{"schema_version":2,"id":"ai-agent-resources","title":"AI Agent Resources","url":"https://redis.io/docs/latest/ai-agent-resources/","summary":"Learn how to develop with Redis as an AI agent","tags":[],"last_updated":"2026-10-05T16:11:44+01:00","page_type":"content","content_hash":"00e53da3341149d5079f8ed4178dc518c71cf51ded5fa8024e3080fa20b87198","sections":[{"id":"llmstxt-index-of-documentation","title":"`llms.txt` index of documentation","role":"overview","text":"Redis provides a comprehensive index of all documentation in Markdown format at [llms.txt](https://redis.io/llms.txt). This index is specifically designed for AI agents to discover available documentation."},{"id":"markdown-documentation-format","title":"Markdown documentation format","role":"content","text":"All documentation pages are available in Markdown format via the same URL as\nthe main doc page but with `index.html.md` added. For example, the Markdown version of\nthis page is available at\n[ai-agent-resources/index.html.md](https://redis.io/docs/latest/ai-agent-resources/index.html.md)."},{"id":"bulk-downloads","title":"Bulk downloads","role":"content","text":"To take whole products at once rather than page by page, use the **Download documentation**\nbutton in the sidebar of any documentation page. Choose the products you want, a version for\neach, and a format, and you get a single `.tar.gz`.\n\nThe archives are also plain files, so you can fetch them directly:\n\n[code example]\n\nFor example, `redis-software-latest-md.tar.gz` or `commands-latest-json.tar.gz`. Formats are\n`md` (one Markdown file per page), `md-single` (a product in one Markdown file), `html`, and\n`json`. Products are `develop`, `integrations`, `redisvl`, `redis-software`, `redis-cloud`, `redis-kubernetes`, `redis-open-source`, `redis-iris`, `redis-feature-form`, `redis-insight`, `redis-data-integration`, `glossary`, and `commands`."},{"id":"json-documentation-feeds","title":"JSON documentation feeds","role":"content","text":"Redis documentation is available in structured JSON format optimized for RAG (Retrieval-Augmented Generation) systems."},{"id":"ndjson-feed-all-pages","title":"NDJSON feed (all pages)","role":"content","text":"A single file containing all documentation pages in [NDJSON](https://github.com/ndjson/ndjson-spec) format (one JSON object per line):\n\n| Format | URL | Size |\n|--------|-----|------|\n| NDJSON | [docs.ndjson](https://redis.io/docs/latest/docs.ndjson) | ~30 MB |\n| Gzipped | [docs.ndjson.gz](https://redis.io/docs/latest/docs.ndjson.gz) | ~5 MB |\n\nBoth files contain one record per documentation page, currently more than 2,600."},{"id":"per-page-json","title":"Per-page JSON","role":"content","text":"Each documentation page has a corresponding JSON file at the same URL with `/index.json` appended. For example:\n\n- Page: `https://redis.io/docs/latest/commands/set/`\n- JSON: `https://redis.io/docs/latest/commands/set/index.json`"},{"id":"what-the-feeds-cover","title":"What the feeds cover","role":"content","text":"The feeds contain one record for every documentation page. They deliberately do **not**\ncontain the taxonomy pages that appear in\n[sitemap.xml](https://redis.io/docs/latest/sitemap.xml), so a straight comparison of the two\nshows the sitemap with more URLs.\n\nThe excluded pages are:\n\n- the `categories/` listing and each `categories/<name>` page\n- the `tags/` listing and each `tags/<name>` page\n- the documentation home page\n\nThese are generated index pages that list other pages. They carry no documentation prose of\ntheir own, so a record for them would add navigation noise without adding content. The\nexclusion is a consequence of how the output formats are configured rather than a filter\napplied afterwards: JSON and Markdown are produced for Hugo's `section` and `page` kinds\nonly, and taxonomy, term and home pages are none of those.\n\nTwo consequences worth knowing if you diff the feed against the sitemap:\n\n- Pages excluded from Hugo's page lists with `_build.list: never` are **absent from the\n  sitemap but present in the feeds**, because they are still built and rendered. They are\n  real documentation pages, usually reference material reached by direct link rather than by\n  browsing.\n- Both figures move. The feed is rebuilt at least daily and the corpus grows, so treat any\n  page count as a snapshot."},{"id":"schema-version","title":"Schema version","role":"content","text":"Every record carries a `schema_version` integer, and the same value appears in the\n`json metadata` block of the Markdown output. It is currently **2**.\n\nIt increments only when the **shape** of a record changes: a field added, removed or\nrenamed, or a new value entering the [role vocabulary](#section-roles). It does **not**\nchange when page content changes, and it does not change when the value inside a field\nchanges without the field itself changing. Use `content_hash` to detect content changes;\nuse `schema_version` to detect when your parser might need attention.\n\nVersion 2 added the `aliases` field to page records, and a new `page_type` value,\n`moved`, for the [redirect records](#pages-that-have-moved) served at a moved page's\nold URL. Version 1 was the initial shape: `sections`, `examples`, `content_hash`,\n`page_type`, `id` and `since`."},{"id":"pages-that-have-moved","title":"Pages that have moved","role":"content","text":"When a page moves, its old URL keeps working — but until now it served only an HTML\nredirect, so appending `/index.json` to it returned 404 and a move was\nindistinguishable from a deletion. Two things now fix that."},{"id":"a-redirect-record-at-the-old-url","title":"A redirect record at the old URL","role":"content","text":"Appending `/index.json` to a moved page's URL returns a record with\n`page_type` set to `moved`:\n\n[code example]\n\n**Check `page_type` before assuming the shape of a record.** A `moved` record\ndeliberately carries no `sections`, `examples` or `content_hash` — there is no content\nat that URL, only a pointer. Fetch `moved_to` to get the page itself.\n\nThese records are **not** included in `docs.ndjson`. The feed is one record per\ndocumentation page, and adding roughly a thousand pointer records would make any count\nof the corpus ambiguous. Use the map below if you need them in bulk."},{"id":"a-map-of-every-redirect","title":"A map of every redirect","role":"content","text":"`https://redis.io/docs/latest/redirects.json` lists every alias the site publishes and\nthe page it resolves to:\n\n[code example]\n\nFour things worth knowing before you rely on it:\n\n- `from` is normalized to a leading slash and no trailing slash. `to` is absolute,\n  matching the `url` field on page records. Every key in `redirects` has a redirect\n  record of its own at `<from>/index.json`, so you can resolve one URL without\n  fetching the whole map.\n- **It is an alias map, not a move log.** Many entries are vanity or legacy paths that\n  were never a page's location. Where we can date a move from the repository history,\n  the entry carries a `moved_on`; entries without one are not undated moves, they are\n  aliases that were never a page's location in the first place.\n- **A URL missing from the map is not necessarily gone.** We publish redirects we can\n  establish, not a complete account of every URL that ever existed. In particular we\n  deliberately do not publish a list of deleted pages: the repository history cannot\n  reliably tell a deletion from a move it failed to detect, and telling you a page was\n  deleted when it merely moved would be worse than telling you nothing.\n- `ambiguous` holds the keys that more than one page claims, with every candidate\n  listed. We publish them separately rather than picking one, because the site itself\n  resolves those arbitrarily — so any single answer we gave you would sometimes\n  disagree with what you would actually be served. Treat an `ambiguous` key as\n  unresolved.\n- `shadowed` holds aliases a page declares that the site does not act on, because a\n  real page already occupies that URL — so the URL serves its own content rather than\n  redirecting. They are listed for completeness and **must not** be followed as\n  redirects; doing so would take a reader away from a live page.\n\nThe map covers the current version of the documentation. Version-specific\ndocumentation is outside both machine-readable feeds."},{"id":"json-schema","title":"JSON schema","role":"content","text":"Each document contains:\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `schema_version` | integer | Version of the record format. See [Schema version](#schema-version). |\n| `id` | string | Unique identifier, the page's path without a file extension (for example `develop/clients/redis-py`) |\n| `title` | string | Page title |\n| `url` | string | Canonical URL |\n| `summary` | string | Short description |\n| `since` | string | Redis version the command was introduced in. Present on command pages only. |\n| `page_type` | string | `\"content\"` (has prose) or `\"index\"` (navigation only) |\n| `content_hash` | string | SHA256 hash for cache invalidation (content pages only) |\n| `sections` | array | Content split by headings with semantic roles |\n| `examples` | array | Code blocks extracted from content |\n| `children` | array | Child pages (index pages only) |\n\nEach **section** contains:\n- `id`: Slugified heading, matching the heading's anchor on the rendered page, so\n  `<url>#<section id>` links to that section\n- `title`: Original heading text\n- `role`: Semantic role, assigned from the heading text. See\n  [Section roles](#section-roles) for the current values.\n- `text`: Section content (code blocks replaced with `[code example]` placeholder)\n\nEach **example** contains:\n- `id`: Unique identifier (`{section_id}-ex{index}`)\n- `language`: Language from code fence (`python`, `go`, `plaintext`, etc.)\n- `code`: The code content\n- `section_id`: Which section this example came from"},{"id":"section-roles","title":"Section roles","role":"content","text":"Each section carries a `role`, derived from its heading text. These are the values currently\nin use:\n\n| Role | Assigned when the heading begins with |\n|------|----------------------------------------|\n| `overview` | `overview`, `introduction`, `about`, `description` |\n| `syntax` | `syntax`, `usage`, `command`, `signature` |\n| `example` | `example`, `demo`, `sample`, `code example` |\n| `parameters` | `option`, `parameter`, `argument`, `flag` |\n| `returns` | `return`, `response`, `output`, `result` |\n| `errors` | `error`, `exception`, `troubleshoot` |\n| `performance` | `performance`, `complexity`, `benchmark` |\n| `limits` | `limit`, `constraint`, `restriction` |\n| `related` | `see also`, `related`, `learn more`, `reference` |\n| `setup` | `install`, `setup`, `getting started`, `quickstart` |\n| `configuration` | `configur`, `setting` |\n| `security` | `security`, `auth`, `permission`, `acl` |\n| `history` | `history`, `changelog`, `version history` |\n| `compatibility` | `compatib`, `support`, `version` |\n| `content` | none of the above |\n\nThe table is in priority order and the first match wins, which matters where the patterns\noverlap: a heading of \"Version history\" is `history` rather than `compatibility`, because\n`history` is tested first.\n\nA page's introductory text, before its first heading, is also given the `overview` role.\n\n> [!NOTE]\n> This vocabulary is descriptive, not a contract. It reflects the values produced\n> today and may gain entries, or change how a heading maps to a role, without notice. If you\n> filter or rank on `role`, treat an unrecognized value as `content` rather than discarding the\n> section, and do not assume a value you rely on will keep its current name."},{"id":"verifying-content_hash","title":"Verifying content_hash","role":"content","text":"The `content_hash` can be verified by computing:\n\n[code example]"},{"id":"api-references","title":"API references","role":"content","text":"API references are available for the following client libraries:\n\n- [redis-py](https://redis.readthedocs.io/en/stable/commands.html)\n- [jedis](https://javadoc.io/doc/redis.clients/jedis/latest/index.html)\n- [lettuce](https://lettuce.io/core/release/reference/index.html)\n- [go-redis](https://pkg.go.dev/github.com/redis/go-redis/v9)\n- [redis-rs](https://docs.rs/redis/latest/redis/)"},{"id":"data-type-comparisons","title":"Data type comparisons","role":"content","text":"See [Compare data types](https://redis.io/docs/latest/develop/data-types/compare-data-types) for advice\non which of the general-purpose data types is best for common tasks."},{"id":"redis-patterns-for-coding-agents","title":"Redis patterns for coding agents","role":"content","text":"Salvatore Sanfilippo (also known as *antirez*, the creator of Redis) has provided the Redis community with a resource containing very useful Redis-oriented design patterns. See [this page](https://redis.antirez.com/) for more information."},{"id":"agent-skills-repository","title":"Agent skills repository","role":"content","text":"The [redis/agent-skills](https://github.com/redis/agent-skills) repository provides reusable skills and tools for AI agents working with Redis."},{"id":"error-handling","title":"Error handling","role":"errors","text":"See [Error handling](https://redis.io/docs/latest/develop/clients/error-handling) for a guide to handling errors in client libraries."}],"examples":[{"id":"bulk-downloads-ex0","language":"plaintext","code":"https://redis.io/docs/latest/downloads/bundles/<product>-<version>-<format>.tar.gz","section_id":"bulk-downloads"},{"id":"a-redirect-record-at-the-old-url-ex0","language":"json","code":"{\n  \"schema_version\": 2,\n  \"id\": \"develop/ai/agent-memory\",\n  \"title\": \"Moved\",\n  \"url\": \"https://redis.io/docs/latest/develop/ai/agent-memory/\",\n  \"page_type\": \"moved\",\n  \"moved_to\": \"https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/\"\n}","section_id":"a-redirect-record-at-the-old-url"},{"id":"a-map-of-every-redirect-ex0","language":"json","code":"{\n  \"schema_version\": 2,\n  \"base_url\": \"https://redis.io/docs/latest/\",\n  \"generated\": \"2026-08-07T15:00:00Z\",\n  \"count\": 1061,\n  \"ambiguous_count\": 27,\n  \"shadowed_count\": 10,\n  \"redirects\": [\n    {\"from\": \"/develop/ai/langcache\", \"to\": \"https://redis.io/docs/latest/develop/ai/context-engine/langcache/\", \"moved_on\": \"2026-05-11\"}\n  ],\n  \"ambiguous\": [\n    {\"from\": \"/develop/use/pipelining\", \"candidates\": [\"https://redis.io/docs/latest/develop/using-commands/\", \"https://redis.io/docs/latest/develop/using-commands/pipelining/\"]}\n  ],\n  \"shadowed\": [\n    {\"from\": \"/glossary\", \"declared\": [\"https://redis.io/docs/latest/glossary/\"]}\n  ]\n}","section_id":"a-map-of-every-redirect"},{"id":"verifying-content_hash-ex0","language":"python","code":"import hashlib\n\ndef verify_hash(page):\n    parts = [page.get('summary', '')]\n    for section in page.get('sections', []):\n        parts.append(section['text'])\n    for example in page.get('examples', []):\n        parts.append(example['code'])\n\n    content = '\\n'.join(parts)\n    return hashlib.sha256(content.encode('utf-8')).hexdigest() == page.get('content_hash')","section_id":"verifying-content_hash"}]}
{"schema_version":2,"id":"apis","title":"APIs","url":"https://redis.io/docs/latest/apis/","summary":"An overview of Redis APIs for developers and operators","tags":[],"last_updated":"2026-01-30T12:29:26-06:00","children":[],"page_type":"content","content_hash":"6a0f5604f3e4970f96cfcedea435546fbb400827f49da5efde395e61974392b9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis provides a number of APIs for developers and operators. The following sections provide you easy access to the client API, the several programmability APIs, the RESTFul management APIs and the Kubernetes resource definitions."},{"id":"apis-for-developers","title":"APIs for Developers","role":"content","text":""},{"id":"client-api","title":"Client API","role":"content","text":"Redis comes with a wide range of commands that help you to develop real-time applications. You can find a complete overview of the Redis commands here:\n\n- [Redis commands](https://redis.io/docs/latest/commands/)\n\nAs a developer, you will likely use one of our supported client libraries for connecting and executing commands.\n\n- [Connect with Redis clients introduction](https://redis.io/docs/latest/develop/clients)"},{"id":"programmability-apis","title":"Programmability APIs","role":"content","text":"The existing Redis commands cover most use cases, but if low latency is a critical requirement, you might need to extend Redis' server-side functionality.\n\nLua scripts have been available since early versions of Redis. With Lua, the script is provided by the client and cached on the server side, which implies the risk that different clients might use a different script version.\n\n- [Redis Lua API reference](https://redis.io/docs/latest/develop/programmability/lua-api)\n- [Scripting with Lua introduction](https://redis.io/docs/latest/develop/programmability/eval-intro)\n\nThe Redis functions feature, which became available in Redis 7, supersedes the use of Lua in prior versions of Redis. The client is still responsible for invoking the execution, but unlike the previous Lua scripts, functions can now be replicated and persisted.\n\n- [Functions and scripting in Redis 7 and beyond](https://redis.io/docs/latest/develop/programmability/functions-intro)\n\nIf none of the previous methods fulfills your needs, then you can extend the functionality of Redis with new commands using the Redis Modules API. \n\n- [Redis Modules API introduction](https://redis.io/docs/latest/develop/reference/modules/)\n- [Redis Modules API reference](https://redis.io/docs/latest/develop/reference/modules/modules-api-ref)"},{"id":"apis-for-operators","title":"APIs for Operators","role":"content","text":""},{"id":"redis-cloud-api","title":"Redis Cloud API","role":"content","text":"Redis Cloud is a fully managed Database as a Service offering and the fastest way to deploy Redis at scale. You can programmatically manage your databases, accounts, access, and credentials using the Redis Cloud REST API.\n\n- [Redis Cloud REST API introduction](https://redis.io/docs/latest/operate/rc/api/)\n- [Redis Cloud REST API examples](https://redis.io/docs/latest/operate/rc/api/examples/)\n- [Redis Cloud REST API reference](https://redis.io/docs/latest/operate/rc/api/api-reference)"},{"id":"redis-software-api","title":"Redis Software API","role":"content","text":"If you have installed Redis Software, you can automate operations with the Redis Software REST API.\n\n- [Redis Software REST API introduction](https://redis.io/docs/latest/operate/rs/references/rest-api/)\n- [Redis Software REST API requests](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/)\n- [Redis Software REST API objects](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/)"},{"id":"redis-enterprise-for-kubernetes-api","title":"Redis Enterprise for Kubernetes API","role":"content","text":"If you need to install Redis Enterprise on Kubernetes, then you can use the [Redis Enterprise for Kubernetes Operators](https://redis.io/docs/latest/operate/Kubernetes/). You can find the resource definitions here:\n\n- [Redis Enterprise Cluster API](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api)\n- [Redis Enterprise Database API](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api)"}],"examples":[]}
{"schema_version":2,"id":"commands/acl","title":"ACL","url":"https://redis.io/docs/latest/commands/acl/","summary":"A container for Access List Control commands.","since":"6.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"a0d220d7ba753c5c19d0e3f81b3b2273a472ebc0de20d9153ba082aa13eb33fb","sections":[{"id":"content","title":"Content","role":"content","text":"This is a container command for [Access Control List](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl) commands.\n\nTo see the list of available commands you can call [`ACL HELP`](https://redis.io/docs/latest/commands/acl-help)."}],"examples":[]}
{"schema_version":2,"id":"commands/acl-cat","title":"ACL CAT","url":"https://redis.io/docs/latest/commands/acl-cat/","summary":"Lists the ACL categories, or the commands inside a category.","since":"6.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"acece495be8cdffe999b16aa2866ce4a2616c2a5d3e5a0aeaac3af982619f3eb","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The command shows the available ACL categories if called without arguments.\nIf a category name is given, the command shows all the Redis commands in\nthe specified category.\n\nACL categories are very useful in order to create ACL rules that include or\nexclude a large set of commands at once, without specifying every single\ncommand. For instance, the following rule will let the user `karin` perform\neverything but the most dangerous operations that may affect the server\nstability:\n\n    ACL SETUSER karin on +@all -@dangerous\n\nWe first add all the commands to the set of commands that `karin` is able\nto execute, but then we remove all the dangerous commands.\n\nChecking for all the available categories is as simple as:\n\n[code example]\n\nThen we may want to know what commands are part of a given category:\n\n[code example]"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>category</code></summary>\n\nList only the commands in the given ACL category. If omitted, all categories are listed.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Not supported for [scripts](https://redis.io/docs/latest/develop/programmability). |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Array reply](../../develop/reference/protocol-spec#arrays): an array of [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings) elements representing ACL categories or commands in a given category.\n* [Simple error reply](../../develop/reference/protocol-spec#simple-errors): the command returns an error if an invalid category name is given.\n\n**RESP3:**\n\nOne of the following:\n* [Array reply](../../develop/reference/protocol-spec#arrays): an array of [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings) elements representing ACL categories or commands in a given category.\n* [Simple error reply](../../develop/reference/protocol-spec#simple-errors): the command returns an error if an invalid category name is given."}],"examples":[{"id":"overview-ex0","language":"plaintext","code":"> ACL CAT\n 1) \"keyspace\"\n 2) \"read\"\n 3) \"write\"\n 4) \"set\"\n 5) \"sortedset\"\n 6) \"list\"\n 7) \"hash\"\n 8) \"string\"\n 9) \"bitmap\"\n10) \"hyperloglog\"\n11) \"geo\"\n12) \"stream\"\n13) \"pubsub\"\n14) \"admin\"\n15) \"fast\"\n16) \"slow\"\n17) \"blocking\"\n18) \"dangerous\"\n19) \"connection\"\n20) \"transaction\"\n21) \"scripting\"\n22) \"json\"\n23) \"search\"\n24) \"tdigest\"\n25) \"cms\"\n26) \"bloom\"\n27) \"cuckoo\"\n28) \"topk\"\n29) \"timeseries\"","section_id":"overview"},{"id":"overview-ex1","language":"plaintext","code":"> ACL CAT dangerous\n 1) \"flushdb\"\n 2) \"acl\"\n 3) \"slowlog\"\n 4) \"debug\"\n 5) \"role\"\n 6) \"keys\"\n 7) \"pfselftest\"\n 8) \"client\"\n 9) \"bgrewriteaof\"\n10) \"replicaof\"\n11) \"monitor\"\n12) \"restore-asking\"\n13) \"latency\"\n14) \"replconf\"\n15) \"pfdebug\"\n16) \"bgsave\"\n17) \"sync\"\n18) \"config\"\n19) \"flushall\"\n20) \"cluster\"\n21) \"info\"\n22) \"lastsave\"\n23) \"slaveof\"\n24) \"swapdb\"\n25) \"module\"\n26) \"restore\"\n27) \"migrate\"\n28) \"save\"\n29) \"shutdown\"\n30) \"psync\"\n31) \"sort\"","section_id":"overview"}]}
{"schema_version":2,"id":"commands/acl-deluser","title":"ACL DELUSER","url":"https://redis.io/docs/latest/commands/acl-deluser/","summary":"Deletes ACL users, and terminates their connections.","since":"6.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-06-15T06:26:34-07:00","page_type":"content","content_hash":"fd3615c9d43a69ac642607aa4498c2f453e84d3812521d34b5c8c9896852e713","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Delete all the specified ACL users and terminate all the connections that are\nauthenticated with such users. Note: the special `default` user cannot be\nremoved from the system, this is the default user that every new connection\nis authenticated with. The list of users may include usernames that do not\nexist, in such case no operation is performed for the non existing users."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>username [username ...]</code></summary>\n\nOne or more usernames to delete.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of users that were deleted. This number will not always match the number of arguments since certain users may not exist.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of users that were deleted. This number will not always match the number of arguments since certain users may not exist."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> ACL DELUSER antirez\n1","section_id":"examples"}]}
{"schema_version":2,"id":"commands/acl-dryrun","title":"ACL DRYRUN","url":"https://redis.io/docs/latest/commands/acl-dryrun/","summary":"Simulates the execution of a command by a user, without executing the command.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-06-15T06:26:34-07:00","page_type":"content","content_hash":"6e1d2eead83080a6a615ded446d938cc4eadaffdbbe575059d0b2a9901c90041","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Simulate the execution of a given command by a given user.\nThis command can be used to test the permissions of a given user without having to enable the user or cause the side effects of running the command."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>username</code></summary>\n\nThe user to run the simulated command as.\n\n</details>\n\n<details open><summary><code>command</code></summary>\n\nThe command to test.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>arg [arg ...]</code></summary>\n\nThe arguments to pass to the tested command.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Might reply with \"unknown user\" for LDAP users even if `AUTH` succeeds. |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nAny of the following:\n* [Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` on success.\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): an error describing why the user can't execute the command.\n\n**RESP3:**\n\nAny of the following:\n* [Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` on success.\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): an error describing why the user can't execute the command."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> ACL SETUSER VIRGINIA +SET ~*\n\"OK\"\n> ACL DRYRUN VIRGINIA SET foo bar\n\"OK\"\n> ACL DRYRUN VIRGINIA GET foo\n\"User VIRGINIA has no permissions to run the 'get' command\"","section_id":"examples"}]}
{"schema_version":2,"id":"commands/acl-genpass","title":"ACL GENPASS","url":"https://redis.io/docs/latest/commands/acl-genpass/","summary":"Generates a pseudorandom, secure password that can be used to identify ACL users.","since":"6.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-06-15T06:26:34-07:00","page_type":"content","content_hash":"4109170c0b4481487bbef99706196f48d77286d69e37fb3c1da28a039f437943","sections":[{"id":"overview","title":"Overview","role":"overview","text":"ACL users need a solid password in order to authenticate to the server without\nsecurity risks. Such password does not need to be remembered by humans, but\nonly by computers, so it can be very long and strong (unguessable by an\nexternal attacker). The `ACL GENPASS` command generates a password starting\nfrom /dev/urandom if available, otherwise (in systems without /dev/urandom) it\nuses a weaker system that is likely still better than picking a weak password\nby hand.\n\nBy default (if /dev/urandom is available) the password is strong and\ncan be used for other uses in the context of a Redis application, for\ninstance in order to create unique session identifiers or other kind of\nunguessable and not colliding IDs. The password generation is also very cheap\nbecause we don't really ask /dev/urandom for bits at every execution. At\nstartup Redis creates a seed using /dev/urandom, then it will use SHA256\nin counter mode, with HMAC-SHA256(seed,counter) as primitive, in order to\ncreate more random bytes as needed. This means that the application developer\nshould be feel free to abuse `ACL GENPASS` to create as many secure\npseudorandom strings as needed.\n\nThe command output is a hexadecimal representation of a binary string.\nBy default it emits 256 bits (so 64 hex characters). The user can provide\nan argument in form of number of bits to emit from 1 to 1024 to change\nthe output length. Note that the number of bits provided is always\nrounded to the next multiple of 4. So for instance asking for just a 1\nbit password will result in 4 bits to be emitted, in the form of a single\nhex character."},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>bits</code></summary>\n\nThe amount of entropy, in bits, for the generated password. Defaults to 256.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): pseudorandom data. By default it contains 64 bytes, representing 256 bits of data. If `bits` was given, the output string length is the number of specified bits (rounded to the next multiple of 4) divided by 4.\n\n**RESP3:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): pseudorandom data. By default it contains 64 bytes, representing 256 bits of data. If `bits` was given, the output string length is the number of specified bits (rounded to the next multiple of 4) divided by 4."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> ACL GENPASS\n\"dd721260bfe1b3d9601e7fbab36de6d04e2e67b0ef1c53de59d45950db0dd3cc\"\n\n> ACL GENPASS 32\n\"355ef3dd\"\n\n> ACL GENPASS 5\n\"90\"","section_id":"examples"}]}
{"schema_version":2,"id":"commands/acl-getuser","title":"ACL GETUSER","url":"https://redis.io/docs/latest/commands/acl-getuser/","summary":"Lists the ACL rules of a user.","since":"6.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"b8fc8efa5760db652e7a2a5e85b19317066a8c4bd82ae69acbe45ac4465e0436","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The command returns all the rules defined for an existing ACL user.\n\nSpecifically, it lists the user's ACL flags, password hashes, commands, key patterns, channel patterns (Added in version 6.2) and selectors (Added in version 7.0).\nAdditional information may be returned in the future if more metadata is added to the user.\n\nCommand rules are always returned in the same format as the one used in the [`ACL SETUSER`](https://redis.io/docs/latest/commands/acl-setuser) command.\nBefore version 7.0, keys and channels were returned as an array of patterns, however in version 7.0 later they are now also returned in same format as the one used in the [`ACL SETUSER`](https://redis.io/docs/latest/commands/acl-setuser) command.\nNote: This description of command rules reflects the user's effective permissions, so while it may not be identical to the set of rules used to configure the user, it is still functionally identical.\n\nSelectors are listed in the order they were applied to the user, and include information about commands, key patterns, and channel patterns."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>username</code></summary>\n\nThe user whose rules to return.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"Here's an example configuration for a user\n\n[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Not supported for [scripts](https://redis.io/docs/latest/develop/programmability). |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Array reply](../../develop/reference/protocol-spec#arrays): a list of ACL rule definitions for the user.\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): if user does not exist.\n\n**RESP3:**\n\nOne of the following:\n* [Map reply](../../develop/reference/protocol-spec#maps): a set of ACL rule definitions for the user\n* [Null reply](../../develop/reference/protocol-spec#nulls): if user does not exist."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> ACL SETUSER sample on nopass +GET allkeys &* (+SET ~key2)\n\"OK\"\n> ACL GETUSER sample\n1) \"flags\"\n2) 1) \"on\"\n   2) \"allkeys\"\n   3) \"nopass\"\n3) \"passwords\"\n4) (empty array)\n5) \"commands\"\n6) \"+@all\"\n7) \"keys\"\n8) \"~*\"\n9) \"channels\"\n10) \"&*\"\n11) \"selectors\"\n12) 1) 1) \"commands\"\n       6) \"+SET\"\n       7) \"keys\"\n       8) \"~key2\"\n       9) \"channels\"\n       10) \"&*\"","section_id":"examples"}]}
{"schema_version":2,"id":"commands/acl-help","title":"ACL HELP","url":"https://redis.io/docs/latest/commands/acl-help/","summary":"Returns helpful text about the different subcommands.","since":"6.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"ff127efc90aea198f9fc22278b8d2ae1eeb0c7c3038d93406ae34b93b143575f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `ACL HELP` command returns a helpful text describing the different subcommands."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Not supported for [scripts](https://redis.io/docs/latest/develop/programmability). |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of subcommands and their descriptions.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of subcommands and their descriptions."}],"examples":[]}
{"schema_version":2,"id":"commands/acl-list","title":"ACL LIST","url":"https://redis.io/docs/latest/commands/acl-list/","summary":"Dumps the effective rules in ACL file format.","since":"6.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"8e28bd549e83a8d51d859a37cd7d1975ca9df6a3e92f3ce9c989d9a93adc52db","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The command shows the currently active ACL rules in the Redis server. Each\nline in the returned array defines a different user, and the format is the\nsame used in the redis.conf file or the external ACL file, so you can\ncut and paste what is returned by the ACL LIST command directly inside a\nconfiguration file if you wish (but make sure to check [`ACL SAVE`](https://redis.io/docs/latest/commands/acl-save))."},{"id":"examples","title":"Examples","role":"example","text":"[code example]\n\n> [!NOTE]\n> In some cases, you might see `allchannels` instead of `&*` and `allkeys` instead of `~*` in the output. This is because `allchannels` and `allkeys` are aliases for `&*` and `~*` respectively."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Not supported for [scripts](https://redis.io/docs/latest/develop/programmability). |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): an array of [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings) elements.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): an array of [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings) elements."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> ACL LIST\n1) \"user antirez on #9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08 ~objects:* &* +@all -@admin -@dangerous\"\n2) \"user default on nopass ~* &* +@all\"","section_id":"examples"}]}
{"schema_version":2,"id":"commands/acl-load","title":"ACL LOAD","url":"https://redis.io/docs/latest/commands/acl-load/","summary":"Reloads the rules from the configured ACL file.","since":"6.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-05-25T10:30:01-07:00","page_type":"content","content_hash":"13a063305aa65cc784feb480c3c4e54dda9b0d4ec8119bae175654cf4eb1067d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"When Redis is configured to use an ACL file (with the `aclfile` configuration\noption), this command will reload the ACLs from the file, replacing all\nthe current ACL rules with the ones defined in the file. The command makes\nsure to have an *all or nothing* behavior, that is:\n\n* If every line in the file is valid, all the ACLs are loaded.\n* If one or more line in the file is not valid, nothing is loaded, and the old ACL rules defined in the server memory continue to be used.\n\nStarting with Redis 8.8, your ACL file is allowed to have comment lines; lines that begin with the `#` character.\nAny such comment lines are stripped when the ACL file is loaded and they are not persisted across `ACL LOAD`/`ACL SAVE` commands.\nMake sure you save a backup of your ACL file. This is the only way to preserve comments."},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` on success.\nThe command may fail with an error for several reasons: if the file is not readable, if there is an error inside the file, and in such cases, the error will be reported to the user in the error.\nFinally, the command will fail if the server is not configured to use an external ACL file.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` on success.\nThe command may fail with an error for several reasons: if the file is not readable, if there is an error inside the file, and in such cases, the error will be reported to the user in the error.\nFinally, the command will fail if the server is not configured to use an external ACL file."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> ACL LOAD\n+OK\n\n> ACL LOAD\n-ERR /tmp/foo:1: Unknown command or category name in ACL...","section_id":"examples"}]}
{"schema_version":2,"id":"commands/acl-log","title":"ACL LOG","url":"https://redis.io/docs/latest/commands/acl-log/","summary":"Lists recent security events generated due to ACL rules.","since":"6.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"6323f63f5ca0e9a055ecfb612733005904d005f417bd85812cac8d6a24350848","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The command shows a list of recent ACL security events:\n\n1. Failed authentications with [`AUTH`](https://redis.io/docs/latest/commands/auth) or [`HELLO`](https://redis.io/docs/latest/commands/hello) (reason = auth)\n2. Commands violating the current ACL rules\n   - Disallowed commands (reason = command).\n   - Disallowed keys (reason = key).\n   - Disallowed pub/sub channel (reason = channel).\n\nThe optional argument specifies how many entries to show. By default\nup to ten failures are returned. The special [`RESET`](https://redis.io/docs/latest/commands/reset) argument clears the log.\nEntries are displayed starting from the most recent."},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>count | RESET</code></summary>\n\nThe number of recent entries to return, or `RESET` to clear the log.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"details","title":"Details","role":"content","text":"Each log entry is composed of the following fields:\n\n1. `count`: The number of security events detected within a 60 second period that are represented by this entry.\n2. `reason`: The reason that the security events were logged. Either `command`, `key`, `channel`, or `auth`.\n3. `context`: The context that the security events were detected in. Either `toplevel`, `multi`, `lua`, or `module`.\n4. `object`: The resource that the user had insufficient permissions to access. `auth` when the reason is `auth`.\n5. `username`: The username that executed the command that caused the security events or the username that had a failed authentication attempt.\n6. `age-seconds`: Age of the log entry in seconds.\n7. `client-info`: Displays the client info of a client which caused one of the security events.\n8. `entry-id`: The sequence number of the entry (starting at 0) since the server process started. Can also be used to check if items were \"lost\", if they fell between periods.\n9. `timestamp-created`: A UNIX timestamp in `milliseconds` at the time the entry was first created.\n10. `timestamp-last-updated`: A UNIX timestamp in `milliseconds` at the time the entry was last updated."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nWhen called to show security events:\n* [Array reply](../../develop/reference/protocol-spec#arrays): an array of [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings) elements representing ACL security events.\nWhen called with `RESET`:\n* [Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the security log was cleared.\n\n**RESP3:**\n\nWhen called to show security events:\n* [Array reply](../../develop/reference/protocol-spec#arrays): an array of [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings) elements representing ACL security events.\nWhen called with `RESET`:\n* [Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the security log was cleared."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> AUTH someuser wrongpassword\n(error) WRONGPASS invalid username-password pair\n> ACL LOG 1\n1)  1) \"count\"\n    2) (integer) 1\n    3) \"reason\"\n    4) \"auth\"\n    5) \"context\"\n    6) \"toplevel\"\n    7) \"object\"\n    8) \"AUTH\"\n    9) \"username\"\n   10) \"someuser\"\n   11) \"age-seconds\"\n   12) \"8.038\"\n   13) \"client-info\"\n   14) \"id=3 addr=127.0.0.1:57275 laddr=127.0.0.1:6379 fd=8 name= age=16 idle=0 flags=N db=0 sub=0 psub=0 ssub=0 multi=-1 qbuf=48 qbuf-free=16842 argv-mem=25 multi-mem=0 rbs=1024 rbp=0 obl=0 oll=0 omem=0 tot-mem=18737 events=r cmd=auth user=default redir=-1 resp=2\"\n   15) \"entry-id\"\n   16) (integer) 0\n   17) \"timestamp-created\"\n   18) (integer) 1675361492408\n   19) \"timestamp-last-updated\"\n   20) (integer) 1675361492408","section_id":"examples"}]}
{"schema_version":2,"id":"commands/acl-save","title":"ACL SAVE","url":"https://redis.io/docs/latest/commands/acl-save/","summary":"Saves the effective ACL rules in the configured ACL file.","since":"6.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"b90c4903d810394997d51871c2c8fcf618b48c49e705b9a140f08f1b116089c4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"When Redis is configured to use an ACL file (with the `aclfile` configuration\noption), this command will save the currently defined ACLs from the server memory to the ACL file."},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\nThe command may fail with an error for several reasons: if the file cannot be written or if the server is not configured to use an external ACL file.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\nThe command may fail with an error for several reasons: if the file cannot be written or if the server is not configured to use an external ACL file."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> ACL SAVE\n+OK\n\n> ACL SAVE\n-ERR There was an error trying to save the ACLs. Please check the server logs for more information","section_id":"examples"}]}
{"schema_version":2,"id":"commands/acl-setuser","title":"ACL SETUSER","url":"https://redis.io/docs/latest/commands/acl-setuser/","summary":"Creates and modifies an ACL user and its rules.","since":"6.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"438ab019b0eada70f8eb1a028021784c6d59b1c7e3728618d08ea7357a530169","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Create an ACL user with the specified rules or modify the rules of an\nexisting user. \n\nManipulate Redis ACL users interactively.\nIf the username does not exist, the command creates the username without any privilege.\nIt then reads from left to right all the [rules](#acl-rules) provided as successive arguments, setting the user ACL rules as specified.\nIf the user already exists, the provided ACL rules are simply applied\n*in addition* to the rules already set. For example:\n\n    ACL SETUSER virginia on allkeys +set\n\nThe above command creates a user called `virginia` who is active(the _on_ rule), can access any key (_allkeys_ rule), and can call the set command (_+set_ rule).\nThen, you can use another `ACL SETUSER` call to modify the user rules:\n\n    ACL SETUSER virginia +get\n\nThe above rule applies the new rule to the user `virginia`, so other than [`SET`](https://redis.io/docs/latest/commands/set), the user `virginia` can now also use the [`GET`](https://redis.io/docs/latest/commands/get) command.\n\nStarting from Redis 7.0, ACL rules can also be grouped into multiple distinct sets of rules, called _selectors_.\nSelectors are added by wrapping the rules in parentheses and providing them just like any other rule.\nIn order to execute a command, either the root permissions (rules defined outside of parenthesis) or any of the selectors (rules defined inside parenthesis) must match the given command.\nFor example:\n\n    ACL SETUSER virginia on +GET allkeys (+SET ~app1*)\n\nThis sets a user with two sets of permissions, one defined on the user and one defined with a selector.\nThe root user permissions only allow executing the get command, but can be executed on any keys.\nThe selector then grants a secondary set of permissions: access to the [`SET`](https://redis.io/docs/latest/commands/set) command to be executed on any key that starts with `app1`.\nUsing multiple selectors allows you to grant permissions that are different depending on what keys are being accessed.\n\nWhen we want to be sure to define a user from scratch, without caring if\nit had previously defined rules associated, we can use the special rule\n`reset` as first rule, in order to flush all the other existing rules:\n\n    ACL SETUSER antirez reset [... other rules ...]\n\nAfter resetting a user, its ACL rules revert to the default: inactive, passwordless, can't execute any command nor access any key or channel:\n\n    > ACL SETUSER antirez reset\n    +OK\n    > ACL LIST\n    1) \"user antirez off -@all\"\n\nACL rules are either words like \"on\", \"off\", \"reset\", \"allkeys\", or are\nspecial rules that start with a special character, and are followed by\nanother string (without any space in between), like \"+SET\".\n\nFor information on persisting ACLs, see the [ACL tutorial](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl#use-an-external-acl-file).\n\nThe following documentation is a reference manual about the capabilities of this command, however our [ACL tutorial](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl) may be a more gentle introduction to how the ACL system works in general."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>username</code></summary>\n\nThe user to create or modify.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>rule [rule ...]</code></summary>\n\nOne or more ACL rules to apply to the user. See the [ACL rules](#acl-rules) section for the full list of rules.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"details","title":"Details","role":"content","text":""},{"id":"acl-rules","title":"ACL rules","role":"security","text":"Redis ACL rules are split into two categories: rules that define command permissions or _command rules_, and rules that define the user state or _user management rules_.\nThis is a list of all the supported Redis ACL rules:\n\n#### Command rules\n\n* `~<pattern>`: Adds the specified key pattern (glob style pattern, like in the [`KEYS`](https://redis.io/docs/latest/commands/keys) command), to the list of key patterns accessible by the user. This grants both read and write permissions to keys that match the pattern. You can add multiple key patterns to the same user. Example: `~objects:*`\n* `%R~<pattern>`: (Available in Redis 7.0 and later) Adds the specified read key pattern. This behaves similar to the regular key pattern but only grants permission to read from keys that match the given pattern. See [key permissions](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl#key-permissions) for more information.\n* `%W~<pattern>`: (Available in Redis 7.0 and later) Adds the specified write key pattern. This behaves similar to the regular key pattern but only grants permission to write to keys that match the given pattern. See [key permissions](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl#key-permissions) for more information.\n* `%RW~<pattern>`: (Available in Redis 7.0 and later) Alias for `~<pattern>`.\n* `allkeys`: Alias for `~*`, it allows the user to access all the keys.\n* `resetkeys`: Removes all the key patterns from the list of key patterns the user can access.\n* `&<pattern>`: (Available in Redis 6.2 and later) Adds the specified glob style pattern to the list of Pub/Sub channel patterns accessible by the user. You can add multiple channel patterns to the same user. Example: `&chatroom:*`\n* `allchannels`: Alias for `&*`, it allows the user to access all Pub/Sub channels.\n* `resetchannels`: Removes all channel patterns from the list of Pub/Sub channel patterns the user can access.\n* `+<command>`: Adds the command to the list of commands the user can call. Can be used with `|` for allowing subcommands (e.g \"+config|get\").\n* `+@<category>`: Adds all the commands in the specified category to the list of commands the user is able to execute. Example: `+@string` (adds all the string commands). For a list of categories, check the [`ACL CAT`](https://redis.io/docs/latest/commands/acl-cat) command.\n* `+<command>|first-arg`: Allows a specific first argument of an otherwise disabled command. It is only supported on commands with no sub-commands, and is not allowed as negative form like -SELECT|1, only additive starting with \"+\". This feature is deprecated and may be removed in the future.\n* `allcommands`: Alias of `+@all`. Adds all the commands there are in the server, including *future commands* loaded via module, to be executed by this user.\n* `-<command>`: Remove the command to the list of commands the user can call. Starting Redis 7.0, it can be used with `|` for blocking subcommands (e.g., \"-config|set\").\n* `-@<category>`: Like `+@<category>` but removes all the commands in the category instead of adding them.\n* `nocommands`: Alias for `-@all`. Removes all the commands, and the user is no longer able to execute anything.\n\n#### User management rules\n\n* `on`: Set the user as active, it will be possible to authenticate as this user using `AUTH <username> <password>`.\n* `off`: Set user as not active, it will be impossible to log as this user. Please note that if a user gets disabled (set to off) after there are connections already authenticated with such a user, the connections will continue to work as expected. To also kill the old connections you can use [`CLIENT KILL`](https://redis.io/docs/latest/commands/client-kill) with the user option. An alternative is to delete the user with [`ACL DELUSER`](https://redis.io/docs/latest/commands/acl-deluser), that will result in all the connections authenticated as the deleted user to be disconnected.\n* `nopass`: The user is set as a _no password_ user. It means that it will be possible to authenticate as such user with any password. By default, the `default` special user is set as \"nopass\". The `nopass` rule will also reset all the configured passwords for the user.\n* `>password`: Adds the specified clear text password as a hashed password in the list of the users passwords. Every user can have many active passwords, so that password rotation will be simpler. The specified password is not stored as clear text inside the server. Example: `>mypassword`.\n* `#<hashedpassword>`: Adds the specified hashed password to the list of user passwords. A Redis hashed password is hashed with SHA256 and translated into a hexadecimal string. Example: `#c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2`.\n* `<password`: Like `>password` but removes the password instead of adding it.\n* `!<hashedpassword>`: Like `#<hashedpassword>` but removes the password instead of adding it.\n* `(<rule list>)`: (Available in Redis 7.0 and later) Creates a new selector to match rules against. Selectors are evaluated after the user permissions, and are evaluated according to the order they are defined. If a command matches either the user permissions or any selector, it is allowed. See [selectors](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl#selectors) for more information.\n* `clearselectors`: (Available in Redis 7.0 and later) Deletes all of the selectors attached to the user.\n* `reset`: Removes any capability from the user. They are set to off, without passwords, unable to execute any command, unable to access any key."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\nIf the rules contain errors, the error is returned.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\nIf the rules contain errors, the error is returned."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> ACL SETUSER alan allkeys +@string +@set -SADD >alanpassword\n+OK\n> ACL SETUSER antirez heeyyyy\n(error) ERR Error in ACL SETUSER modifier 'heeyyyy': Syntax error","section_id":"examples"}]}
{"schema_version":2,"id":"commands/acl-users","title":"ACL USERS","url":"https://redis.io/docs/latest/commands/acl-users/","summary":"Lists all ACL users.","since":"6.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"5cfd97d0be038fca9c5f01faaad947ffecdd4cca4dc77272f60247b961755c2f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The command shows a list of all the usernames of the currently configured\nusers in the Redis ACL system."},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Not supported for [scripts](https://redis.io/docs/latest/develop/programmability). |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): list of existing ACL users.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): list of existing ACL users."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> ACL USERS\n1) \"anna\"\n2) \"antirez\"\n3) \"default\"","section_id":"examples"}]}
{"schema_version":2,"id":"commands/acl-whoami","title":"ACL WHOAMI","url":"https://redis.io/docs/latest/commands/acl-whoami/","summary":"Returns the authenticated username of the current connection.","since":"6.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"ae85179d43c5c67d799a0f3054d91c65e209ab04117b856e3d9e05bd840f8594","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Return the username the current connection is authenticated with.\nNew connections are authenticated with the \"default\" user. They\ncan change user using [`AUTH`](https://redis.io/docs/latest/commands/auth)."},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Not supported for [scripts](https://redis.io/docs/latest/develop/programmability). |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the username of the current connection.\n\n**RESP3:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the username of the current connection."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> ACL WHOAMI\n\"default\"","section_id":"examples"}]}
{"schema_version":2,"id":"commands/append","title":"APPEND","url":"https://redis.io/docs/latest/commands/append/","summary":"Appends a string to the value of a key. Creates the key if it doesn't exist.","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:12+01:00","page_type":"content","content_hash":"cade5f6cbd7ab441ae1c1d8956ea6a537aab6c71c503a9a18e123f0da9571de7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Appends a value to the string stored at `key`. If `key` does not exist, it is created with an empty string, so in that case, `APPEND` behaves like [`SET`](https://redis.io/docs/latest/commands/set)."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key.\n\n</details>\n\n<details open><summary><code>value</code></summary>\n\nThe string to append to the existing value.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> EXISTS mykey\n(integer) 0\nredis> APPEND mykey \"Hello\"\n(integer) 5\nredis> APPEND mykey \" World\"\n(integer) 11\nredis> GET mykey\n\"Hello World\""},{"id":"details","title":"Details","role":"content","text":""},{"id":"pattern-time-series","title":"Pattern: time series","role":"content","text":"The `APPEND` command can be used to create a very compact representation of a\nlist of fixed-size samples, usually referred as _time series_.\nEvery time a new sample arrives we can store it using the command\n\n[code example]\n\nAccessing individual elements in the time series is not hard:\n\n* [`STRLEN`](https://redis.io/docs/latest/commands/strlen) can be used in order to obtain the number of samples.\n* [`GETRANGE`](https://redis.io/docs/latest/commands/getrange) allows for random access of elements.\n  If our time series have associated time information we can easily implement\n  a binary search to get range combining [`GETRANGE`](https://redis.io/docs/latest/commands/getrange) with the Lua scripting\n  engine available in Redis 2.6.\n* [`SETRANGE`](https://redis.io/docs/latest/commands/setrange) can be used to overwrite an existing time series.\n\nThe limitation of this pattern is that we are forced into an append-only mode\nof operation, there is no way to cut the time series to a given size easily\nbecause Redis currently lacks a command able to trim string objects.\nHowever the space efficiency of time series stored in this way is remarkable.\n\nHint: it is possible to switch to a different key based on the current Unix\ntime, in this way it is possible to have just a relatively small amount of\nsamples per key, to avoid dealing with very big keys, and to make this pattern\nmore friendly to be distributed across many Redis instances.\n\nAn example sampling the temperature of a sensor using fixed-size strings (using\na binary format is better in real implementations).\n\n\nredis> APPEND ts \"0043\"\n(integer) 4\nredis> APPEND ts \"0035\"\n(integer) 8\nredis> GETRANGE ts 0 3\n\"0043\"\nredis> GETRANGE ts 4 7\n\"0035\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the length of the string after the append operation.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the length of the string after the append operation."}],"examples":[{"id":"pattern-time-series-ex0","language":"plaintext","code":"APPEND timeseries \"fixed-size sample\"","section_id":"pattern-time-series"}]}
{"schema_version":2,"id":"commands/arcount","title":"ARCOUNT","url":"https://redis.io/docs/latest/commands/arcount/","summary":"Returns the number of non-empty elements in an array.","since":"8.8.0","tags":["docs","develop","oss","rs","rc","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"a9ad9ef062ea1aebafa19977e031984696840d9eb2e85d5c0ff53d4982475a87","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns the number of non-empty elements in an array."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the array.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ARSET myarray 0 \"a\"\n(integer) 1\nredis> ARSET myarray 5 \"b\"\n(integer) 1\nredis> ARCOUNT myarray\n(integer) 2"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): The number of non-empty elements, or 0 if key does not exist.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): The number of non-empty elements, or 0 if key does not exist."}],"examples":[]}
{"schema_version":2,"id":"commands/ardel","title":"ARDEL","url":"https://redis.io/docs/latest/commands/ardel/","summary":"Deletes elements at the specified indices in an array.","since":"8.8.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"779baa6faa855b0c7200e442abb5f68dea01b97dc28a0d9e2d4470c4b84472e8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Deletes elements at the specified indices in an array."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the array.\n\n</details>\n\n<details open><summary><code>index</code></summary>\n\nOne or more zero-based integer indices of the elements to delete. Deleting an index that does not exist counts as zero elements deleted and does not modify the array.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ARSET myarray 0 \"a\"\n(integer) 1\nredis> ARSET myarray 1 \"b\"\n(integer) 1\nredis> ARSET myarray 2 \"c\"\n(integer) 1\nredis> ARDEL myarray 1\n(integer) 1\nredis> ARGET myarray 1\n(nil)\nredis> ARDEL myarray 0 2\n(integer) 2\nredis> ARCOUNT myarray\n(integer) 0"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): Number of elements deleted.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): Number of elements deleted."}],"examples":[]}
{"schema_version":2,"id":"commands/ardelrange","title":"ARDELRANGE","url":"https://redis.io/docs/latest/commands/ardelrange/","summary":"Deletes elements in one or more ranges.","since":"8.8.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"8f17bf326eed21c67a08471460df48abed38576a1c456167cc7755c073b31728","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Deletes elements in one or more ranges."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the array.\n\n</details>\n\n<details open><summary><code>range</code></summary>\n\nOne or more `start end` pairs, each defining an inclusive range of indices to delete. If `start` is greater than `end` for a given pair, the range is processed in ascending order regardless. Multiple pairs may overlap; each element is counted at most once.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ARMSET myarray 0 \"a\" 1 \"b\" 2 \"c\" 3 \"d\" 4 \"e\"\n(integer) 5\nredis> ARDELRANGE myarray 1 3\n(integer) 3\nredis> ARCOUNT myarray\n(integer) 2\nredis> ARGET myarray 0\n\"a\"\nredis> ARGET myarray 4\n\"e\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): Number of elements deleted.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): Number of elements deleted."}],"examples":[]}
{"schema_version":2,"id":"commands/arget","title":"ARGET","url":"https://redis.io/docs/latest/commands/arget/","summary":"Gets the value at an index in an array.","since":"8.8.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"28eb145d32878297cd0e6f39af6ce7e704d85aae2209894269a8199fc6dfed7d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Gets the value at an index in an array."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the array.\n\n</details>\n\n<details open><summary><code>index</code></summary>\n\nThe zero-based integer index of the element to retrieve.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ARSET myarray 0 \"hello\"\n(integer) 1\nredis> ARGET myarray 0\n\"hello\"\nredis> ARGET myarray 1\n(nil)"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): The value at the given index.\n* [Nil reply](../../develop/reference/protocol-spec#null-bulk-strings): Null reply if key or index does not exist.\n\n**RESP3:**\n\nOne of the following:\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): The value at the given index.\n* [Null reply](../../develop/reference/protocol-spec#nulls): Null reply if key or index does not exist."}],"examples":[]}
{"schema_version":2,"id":"commands/argetrange","title":"ARGETRANGE","url":"https://redis.io/docs/latest/commands/argetrange/","summary":"Gets values in a range of indices.","since":"8.8.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"a208f4ca40e59db7f99de1635e8ba86dad9d5c2a2e9525901cea5aa506cd715e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Gets values in a range of indices."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the array.\n\n</details>\n\n<details open><summary><code>start</code></summary>\n\nThe zero-based integer index of the first element to return. If `start` is greater than `end`, elements are returned in reverse index order.\n\n</details>\n\n<details open><summary><code>end</code></summary>\n\nThe zero-based integer index of the last element to return (inclusive). If `end` is less than `start`, elements are returned in reverse index order.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ARMSET myarray 0 \"a\" 1 \"b\" 3 \"d\"\n(integer) 3\nredis> ARGETRANGE myarray 0 3\n1) \"a\"\n2) \"b\"\n3) (nil)\n4) \"d\"\nredis> ARGETRANGE myarray 3 0\n1) \"d\"\n2) (nil)\n3) \"b\"\n4) \"a\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays)\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays)"}],"examples":[]}
{"schema_version":2,"id":"commands/argrep","title":"ARGREP","url":"https://redis.io/docs/latest/commands/argrep/","summary":"Searches array elements in a range using textual predicates.","since":"8.8.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-28T15:41:00+02:00","page_type":"content","content_hash":"35d15d1db65e858e2e5b63b578fff2a33985229cca9ba57ec1e992181dc6f658","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Searches array elements in a range using textual predicates and returns the indices of the matching elements. Empty slots in the range are skipped."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the array.\n\n</details>\n\n<details open><summary><code>start</code></summary>\n\nThe zero-based integer index at which to begin searching. The special value `-` denotes the first index of the array. If `start` is greater than `end`, matches are returned in reverse index order.\n\n</details>\n\n<details open><summary><code>end</code></summary>\n\nThe zero-based integer index at which to stop searching (inclusive). The special value `+` denotes the last index of the array.\n\n</details>\n\n<details open><summary><code>predicate</code></summary>\n\nOne or more textual predicates to evaluate against each non-empty element in the range. Each predicate is one of:\n\n- **`EXACT string`** — Matches elements whose value is exactly equal to `string`.\n- **`MATCH string`** — Matches elements whose value contains `string` as a substring.\n- **`GLOB pattern`** — Matches elements whose value matches the glob-style `pattern` (with `*`, `?`, and `[...]` wildcards), the same syntax used by [`KEYS`](https://redis.io/docs/latest/commands/keys) and [`SCAN`](https://redis.io/docs/latest/commands/scan) `MATCH`.\n- **`RE pattern`** — Matches elements whose value matches the regular expression `pattern`.\n\nWhen more than one predicate is supplied, use the `AND` or `OR` option to control how they are combined. The default is `OR`.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>options</code></summary>\n\nZero or more of the following modifiers:\n\n- **`AND`** — Combine multiple predicates with logical AND. An element matches only if every predicate matches.\n- **`OR`** — Combine multiple predicates with logical OR. An element matches if any predicate matches. This is the default.\n- **`LIMIT limit`** — Stop after `limit` matches have been collected. `limit` must be a positive integer. When omitted, all matches in the range are returned.\n- **`WITHVALUES`** — In addition to each matching index, return the matching value. The reply becomes an array of index-value pairs, each a two-element array.\n- **`NOCASE`** — Perform case-insensitive comparisons for `EXACT`, `MATCH`, `GLOB`, and `RE`.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ARMSET log 0 \"boot: ok\" 1 \"warn: disk\" 2 \"ERROR: cpu\" 3 \"info: ready\" 4 \"error: net\"\n(integer) 5\nredis> ARGREP log - + MATCH \"error\" NOCASE\n1) (integer) 2\n2) (integer) 4\nredis> ARGREP log - + MATCH \"error\" NOCASE WITHVALUES\n1) 1) (integer) 2\n   2) \"ERROR: cpu\"\n2) 1) (integer) 4\n   2) \"error: net\"\nredis> ARGREP log 0 4 GLOB \"warn:*\" OR GLOB \"error:*\"\n1) (integer) 1\n2) (integer) 4\nredis> ARGREP log 0 4 RE \"^[A-Za-z]+: (cpu|net)$\" NOCASE WITHVALUES\n1) 1) (integer) 2\n   2) \"ERROR: cpu\"\n2) 1) (integer) 4\n   2) \"error: net\"\nredis> ARGREP log 0 4 EXACT \"info: ready\"\n1) (integer) 3\nredis> ARGREP log - + MATCH \"error\" NOCASE LIMIT 1\n1) (integer) 2"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): Indices of the matching elements, in the same order in which the range is traversed (ascending when `start <= end`, descending when `start > end`). When `WITHVALUES` is given, an array of index-value pairs, each a two-element array: `[[idx1, val1], [idx2, val2], ...]`. An empty array is returned when the key does not exist or no element matches.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): Indices of the matching elements, in the same order in which the range is traversed (ascending when `start <= end`, descending when `start > end`). When `WITHVALUES` is given, an array of index-value pairs, each a two-element array: `[[idx1, val1], [idx2, val2], ...]`. An empty array is returned when the key does not exist or no element matches."}],"examples":[]}
{"schema_version":2,"id":"commands/arinfo","title":"ARINFO","url":"https://redis.io/docs/latest/commands/arinfo/","summary":"Returns metadata about an array.","since":"8.8.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"3d5b20a3463fc5dbaa7e5c7bf4bd6be6fbeff0c383420564132e8d422750a408","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns metadata about an array."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the array.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>FULL</code></summary>\n\nWhen present, includes per-slice statistics in the reply: the number of dense and sparse slices and their average sizes and fill rates. Raises the complexity from O(1) to O(N) where N is the number of slices.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ARMSET myarray 0 \"a\" 1 \"b\" 100 \"c\"\n(integer) 3\nredis> ARINSERT myarray \"d\"\n(integer) 0\nredis> ARINFO myarray\n 1) \"count\"\n 2) (integer) 3\n 3) \"len\"\n 4) (integer) 101\n 5) \"next-insert-index\"\n 6) (integer) 1\n 7) \"slices\"\n 8) (integer) 1\n 9) \"directory-size\"\n10) (integer) 1\n11) \"super-dir-entries\"\n12) (integer) 0\n13) \"slice-size\"\n14) (integer) 4096"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays)\n\n**RESP3:**\n\n[Map reply](../../develop/reference/protocol-spec#maps)"}],"examples":[]}
{"schema_version":2,"id":"commands/arinsert","title":"ARINSERT","url":"https://redis.io/docs/latest/commands/arinsert/","summary":"Inserts one or more values at consecutive indices.","since":"8.8.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:12+01:00","page_type":"content","content_hash":"7b1d0b67977ab8f617f42173ebc4a0d88dba30f8d260b67850f909b42061abcd","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Inserts one or more values at consecutive indices."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the array.\n\n</details>\n\n<details open><summary><code>value</code></summary>\n\nOne or more string values to insert at consecutive indices, beginning at the current insert cursor position. The cursor advances by one for each value inserted. Use [`ARNEXT`](https://redis.io/docs/latest/commands/arnext) to inspect the current cursor position and [`ARSEEK`](https://redis.io/docs/latest/commands/arseek) to reposition it.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ARINSERT myarray \"alpha\"\n(integer) 0\nredis> ARINSERT myarray \"beta\"\n(integer) 1\nredis> ARINSERT myarray \"gamma\"\n(integer) 2\nredis> ARGET myarray 1\n\"beta\"\nredis> ARNEXT myarray\n(integer) 3"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): The last index where a value was inserted.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): The last index where a value was inserted."}],"examples":[]}
{"schema_version":2,"id":"commands/arlastitems","title":"ARLASTITEMS","url":"https://redis.io/docs/latest/commands/arlastitems/","summary":"Returns the most recently inserted elements.","since":"8.8.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"f238d480d114f89fa6dbbaeb159a2743285e1277437013257028fdf02929a163","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns the most recently inserted elements."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the array.\n\n</details>\n\n<details open><summary><code>count</code></summary>\n\nThe maximum number of most recently inserted elements to return. If the array contains fewer elements than `count`, all elements are returned.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>REV</code></summary>\n\nWhen present, returns elements in reverse chronological order (most recent first) instead of the default oldest-first order.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ARINSERT log \"first\"\n(integer) 0\nredis> ARINSERT log \"second\"\n(integer) 1\nredis> ARINSERT log \"third\"\n(integer) 2\nredis> ARLASTITEMS log 2\n1) \"second\"\n2) \"third\"\nredis> ARLASTITEMS log 2 REV\n1) \"third\"\n2) \"second\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays)\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays)"}],"examples":[]}
{"schema_version":2,"id":"commands/arlen","title":"ARLEN","url":"https://redis.io/docs/latest/commands/arlen/","summary":"Returns the length of an array (max index + 1).","since":"8.8.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"d91032239799b384e264c2f9a620607e22d8e2c50af46cc696bcc713fc099260","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns the length of an array (max index + 1)."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the array.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ARSET myarray 0 \"a\"\n(integer) 1\nredis> ARSET myarray 5 \"b\"\n(integer) 1\nredis> ARLEN myarray\n(integer) 6\nredis> ARCOUNT myarray\n(integer) 2"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): The length of the array (max index + 1), or 0 if key does not exist.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): The length of the array (max index + 1), or 0 if key does not exist."}],"examples":[]}
{"schema_version":2,"id":"commands/armget","title":"ARMGET","url":"https://redis.io/docs/latest/commands/armget/","summary":"Gets values at multiple indices in an array.","since":"8.8.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"18a6836e6fe2ebd2a0349546ed7c3a467bd9137860f5ab4a5f52cb7349c11791","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Gets values at multiple indices in an array."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the array.\n\n</details>\n\n<details open><summary><code>index</code></summary>\n\nOne or more zero-based integer indices of the elements to retrieve. The reply preserves the order of the requested indices and returns nil for any index that is not set.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ARMSET myarray 0 \"a\" 1 \"b\" 5 \"c\"\n(integer) 3\nredis> ARMGET myarray 0 1 5 3\n1) \"a\"\n2) \"b\"\n3) \"c\"\n4) (nil)"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays)\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays)"}],"examples":[]}
{"schema_version":2,"id":"commands/armset","title":"ARMSET","url":"https://redis.io/docs/latest/commands/armset/","summary":"Sets multiple index-value pairs in an array.","since":"8.8.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"d0ea7a48d7b17366ce82ca1f14a665afd84fe6c9bc526c7c2d3fc36df784b08d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Sets multiple index-value pairs in an array."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the array.\n\n</details>\n\n<details open><summary><code>data</code></summary>\n\nOne or more `index value` pairs. Each `index` is a zero-based integer specifying where to write, and each `value` is the string to store at that position. Pairs may be non-contiguous and in any order.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ARMSET myarray 0 \"alpha\" 5 \"beta\" 100 \"gamma\"\n(integer) 3\nredis> ARGET myarray 0\n\"alpha\"\nredis> ARGET myarray 5\n\"beta\"\nredis> ARGET myarray 100\n\"gamma\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): Number of new slots that were set (previously empty).\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): Number of new slots that were set (previously empty)."}],"examples":[]}
{"schema_version":2,"id":"commands/arnext","title":"ARNEXT","url":"https://redis.io/docs/latest/commands/arnext/","summary":"Returns the next index ARINSERT would use.","since":"8.8.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"effc8ebc2e78e1d504276e62fa0e280c7faaa5f5de8d27c9a133c593fc8a7b59","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns the next index ARINSERT would use."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the array.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ARINSERT myarray \"a\"\n(integer) 0\nredis> ARINSERT myarray \"b\"\n(integer) 1\nredis> ARNEXT myarray\n(integer) 2"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): The next index ARINSERT would use. Returns 0 for missing keys or when no insert happened yet.\n* [Nil reply](../../develop/reference/protocol-spec#null-bulk-strings): Null when the insertion cursor is exhausted (next insert would overflow).\n\n**RESP3:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): The next index ARINSERT would use. Returns 0 for missing keys or when no insert happened yet.\n* [Null reply](../../develop/reference/protocol-spec#nulls): Null when the insertion cursor is exhausted (next insert would overflow)."}],"examples":[]}
{"schema_version":2,"id":"commands/arop","title":"AROP","url":"https://redis.io/docs/latest/commands/arop/","summary":"Performs aggregate operations on array elements in a range.","since":"8.8.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"5810331656dd8c1e0de1d0a459fbf4e3bfd8989c5a2841697260be0d906b52ba","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Performs aggregate operations on array elements in a range."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the array.\n\n</details>\n\n<details open><summary><code>start</code></summary>\n\nThe zero-based integer index of the first element in the range to aggregate.\n\n</details>\n\n<details open><summary><code>end</code></summary>\n\nThe zero-based integer index of the last element in the range to aggregate (inclusive). The command always scans from the lower to the higher index regardless of argument order.\n\n</details>\n\n<details open><summary><code>operation</code></summary>\n\nThe aggregate function to apply to all non-empty elements in `[start, end]`. One of:\n\n- **`SUM`** — Returns the sum of all numeric values as a bulk string.\n- **`MIN`** — Returns the minimum numeric value as a bulk string.\n- **`MAX`** — Returns the maximum numeric value as a bulk string.\n- **`AND`** — Returns the bitwise AND of all values, treating each as an integer (floats are truncated toward zero).\n- **`OR`** — Returns the bitwise OR of all values, treating each as an integer (floats are truncated toward zero).\n- **`XOR`** — Returns the bitwise XOR of all values, treating each as an integer (floats are truncated toward zero).\n- **`MATCH value`** — Returns the count of elements whose value equals `value` as an integer reply.\n- **`USED`** — Returns the count of non-empty elements in the range as an integer reply.\n\n`SUM`, `MIN`, and `MAX` return nil when no numeric elements are present in the range. `AND`, `OR`, and `XOR` return nil when the range is empty.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ARMSET myarray 0 \"10\" 1 \"20\" 2 \"30\"\n(integer) 3\nredis> AROP myarray 0 2 SUM\n\"60\"\nredis> AROP myarray 0 2 MIN\n\"10\"\nredis> AROP myarray 0 2 MAX\n\"30\"\nredis> AROP myarray 0 2 MATCH \"10\"\n(integer) 1\nredis> AROP myarray 0 2 USED\n(integer) 3\nredis> ARMSET flags 0 \"255\" 1 \"15\" 2 \"240\"\n(integer) 3\nredis> AROP flags 0 2 AND\n(integer) 0\nredis> AROP flags 0 2 OR\n(integer) 255"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): Result of the operation.\n* [Integer reply](../../develop/reference/protocol-spec#integers): Integer result for MATCH, USED, AND, OR, XOR.\n* [Nil reply](../../develop/reference/protocol-spec#null-bulk-strings): Null if no elements match the operation.\n\n**RESP3:**\n\nOne of the following:\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): Result of the operation.\n* [Integer reply](../../develop/reference/protocol-spec#integers): Integer result for MATCH, USED, AND, OR, XOR.\n* [Null reply](../../develop/reference/protocol-spec#nulls): Null if no elements match the operation."}],"examples":[]}
{"schema_version":2,"id":"commands/arring","title":"ARRING","url":"https://redis.io/docs/latest/commands/arring/","summary":"Inserts values into a ring buffer of specified size, wrapping and truncating as needed.","since":"8.8.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"597eed77e04590bee3c65d7295802433bbbe19262d47aebfc9d5959fff39aa4e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Inserts values into a ring buffer of specified size, wrapping and truncating as needed."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the array.\n\n</details>\n\n<details open><summary><code>size</code></summary>\n\nThe size of the ring buffer window. Each value is inserted at `insert_idx % size`, wrapping back to index `0` when the end of the window is reached. When the buffer is full, newer values overwrite older ones. If `size` is smaller than the current window, the array is truncated to fit.\n\n</details>\n\n<details open><summary><code>value</code></summary>\n\nOne or more string values to insert into the ring buffer. Each value is placed at the next position in the ring and the cursor advances accordingly.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ARRING readings 3 \"v0\"\n(integer) 0\nredis> ARRING readings 3 \"v1\"\n(integer) 1\nredis> ARRING readings 3 \"v2\"\n(integer) 2\nredis> ARRING readings 3 \"v3\"\n(integer) 0\nredis> ARGET readings 0\n\"v3\"\nredis> ARCOUNT readings\n(integer) 3\nredis> ARLASTITEMS readings 3\n1) \"v1\"\n2) \"v2\"\n3) \"v3\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): The last index where a value was inserted.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): The last index where a value was inserted."}],"examples":[]}
{"schema_version":2,"id":"commands/arscan","title":"ARSCAN","url":"https://redis.io/docs/latest/commands/arscan/","summary":"Iterates existing elements in a range, returning index-value pairs.","since":"8.8.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-28T15:41:00+02:00","page_type":"content","content_hash":"51b273299f678091198625a59b766e388c4653c1804c930701ff60900ac2802b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Iterates existing elements in a range, returning index-value pairs."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the array.\n\n</details>\n\n<details open><summary><code>start</code></summary>\n\nThe zero-based integer index at which to begin scanning. If `start` is greater than `end`, elements are returned in reverse index order.\n\n</details>\n\n<details open><summary><code>end</code></summary>\n\nThe zero-based integer index at which to stop scanning (inclusive).\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>LIMIT</code></summary>\n\nThe maximum number of index-value pairs to return. When omitted, all elements in the range are returned. Unlike `ARGETRANGE`, empty slots are not included in the output, so `LIMIT` caps the number of existing elements returned.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ARSET myarray 0 \"a\"\n(integer) 1\nredis> ARSET myarray 5 \"b\"\n(integer) 1\nredis> ARSET myarray 9 \"c\"\n(integer) 1\nredis> ARSCAN myarray 0 10\n1) 1) (integer) 0\n   2) \"a\"\n2) 1) (integer) 5\n   2) \"b\"\n3) 1) (integer) 9\n   2) \"c\"\nredis> ARSCAN myarray 0 10 LIMIT 2\n1) 1) (integer) 0\n   2) \"a\"\n2) 1) (integer) 5\n   2) \"b\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): Array of index-value pairs, each a two-element array: `[[idx1, val1], [idx2, val2], ...]`\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): Array of index-value pairs, each a two-element array: `[[idx1, val1], [idx2, val2], ...]`"}],"examples":[]}
{"schema_version":2,"id":"commands/arseek","title":"ARSEEK","url":"https://redis.io/docs/latest/commands/arseek/","summary":"Sets the ARINSERT / ARRING cursor to a specific index.","since":"8.8.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:12+01:00","page_type":"content","content_hash":"3bf7f2bc3ae025b18f8ba2fbed393e8b53d5069980270ef46d176d09b0e84a5e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Sets the ARINSERT / ARRING cursor to a specific index."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the array.\n\n</details>\n\n<details open><summary><code>index</code></summary>\n\nThe zero-based integer index to set as the new insert cursor position for subsequent [`ARINSERT`](https://redis.io/docs/latest/commands/arinsert) calls.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ARINSERT myarray \"a\"\n(integer) 0\nredis> ARINSERT myarray \"b\"\n(integer) 1\nredis> ARNEXT myarray\n(integer) 2\nredis> ARSEEK myarray 10\n(integer) 1\nredis> ARINSERT myarray \"c\"\n(integer) 10\nredis> ARNEXT myarray\n(integer) 11"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): 1 if the cursor was set, 0 if the key does not exist.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): 1 if the cursor was set, 0 if the key does not exist."}],"examples":[]}
{"schema_version":2,"id":"commands/arset","title":"ARSET","url":"https://redis.io/docs/latest/commands/arset/","summary":"Sets one or more contiguous values starting at an index in an array.","since":"8.8.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"02846ec1303317427c2a6be163fb6b2645e27b59b8a810933308b153eeac6fe3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Sets one or more contiguous values starting at an index in an array."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the array.\n\n</details>\n\n<details open><summary><code>index</code></summary>\n\nThe zero-based integer index at which to start writing. When multiple values are provided, they are stored at consecutive indices starting from `index`.\n\n</details>\n\n<details open><summary><code>value</code></summary>\n\nOne or more string values to store at consecutive indices beginning at `index`. The number of new (previously empty) slots filled is returned.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ARSET myarray 0 \"hello\"\n(integer) 1\nredis> ARGET myarray 0\n\"hello\"\nredis> ARSET myarray 2 \"a\" \"b\" \"c\"\n(integer) 3\nredis> ARGET myarray 2\n\"a\"\nredis> ARGET myarray 4\n\"c\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): Number of new slots that were set (previously empty).\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): Number of new slots that were set (previously empty)."}],"examples":[]}
{"schema_version":2,"id":"commands/asking","title":"ASKING","url":"https://redis.io/docs/latest/commands/asking/","summary":"Signals that a cluster client is following an -ASK redirect.","since":"3.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"13556c359101132f005554d39950d89867b80d44d777cf680c8f5db264cb4be7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"When a cluster client receives an `-ASK` redirect, the `ASKING` command is sent to the target node followed by the command which was redirected.\nThis is normally done automatically by cluster clients.\n\nIf an `-ASK` redirect is received during a transaction, only one ASKING command needs to be sent to the target node before sending the complete transaction to the target node.\n\nSee [ASK redirection in the Redis Cluster Specification](https://redis.io/docs/latest/operate/oss_and_stack/reference/cluster-spec#ask-redirection) for details."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[]}
{"schema_version":2,"id":"commands/auth","title":"AUTH","url":"https://redis.io/docs/latest/commands/auth/","summary":"Authenticates the connection.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"cf5179018cbd688163d06601886fd8c00334a9e179525c037f879e7dee703132","sections":[{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>password</code></summary>\n\nThe password to authenticate with. With `requirepass`, this is the server password; with ACLs, it is the user's password.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>username</code></summary>\n\nThe ACL username to authenticate as. If omitted, the `default` user is used.\n\n</details>"},{"id":"details","title":"Details","role":"content","text":""},{"id":"security-notice","title":"Security notice","role":"security","text":"Because of the high performance nature of Redis, it is possible to try\na lot of passwords in parallel in very short time, so make sure to generate a\nstrong and very long password so that this attack is infeasible.\nA good way to generate strong passwords is via the [`ACL GENPASS`](https://redis.io/docs/latest/commands/acl-genpass) command."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`, or an error if the password, or username/password pair, is invalid.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`, or an error if the password, or username/password pair, is invalid."}],"examples":[]}
{"schema_version":2,"id":"commands/backup","title":"BACKUP","url":"https://redis.io/docs/latest/commands/backup/","summary":"A container for backup management commands.","since":"8.10.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"0472d920511922d73599eb6dd6e2e6d156999a887e4d8fe4ccc532226ec44a20","sections":[{"id":"content","title":"Content","role":"content","text":"This is a container command for backup management commands.\n\nTo see the list of available commands you can call [`BACKUP HELP`](https://redis.io/docs/latest/commands/backup-help).\n\nFor a conceptual overview of how the `BACKUP` command family creates online backups, see [Redis persistence](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence#online-backups-with-the-backup-command-family)."}],"examples":[]}
{"schema_version":2,"id":"commands/backup-abort","title":"BACKUP ABORT","url":"https://redis.io/docs/latest/commands/backup-abort/","summary":"Cancel a backup that has not been sealed yet.","since":"8.10.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"dc8e90369a29c7f12a2699ca3ed9ba79788733c3432f7538ad9c755e2e9e1133","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Cancels a backup that has not been sealed yet."},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"details","title":"Details","role":"content","text":"`BACKUP ABORT` cancels an in-progress backup and moves the backup state machine to `failed`. It is valid from the `pending`, `snapshotting`, and `incrementing` states. After the backup has been sealed, use [`BACKUP CLEANUP`](https://redis.io/docs/latest/commands/backup-cleanup) instead.\n\nAfter aborting, you can start a new backup with [`BACKUP START`](https://redis.io/docs/latest/commands/backup-start).\n\nFor the full workflow, see [Redis persistence](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence#online-backups-with-the-backup-command-family)."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."},{"id":"see-also","title":"See also","role":"related","text":"[`BACKUP START`](https://redis.io/docs/latest/commands/backup-start) | [`BACKUP SEAL`](https://redis.io/docs/latest/commands/backup-seal) | [`BACKUP STATUS`](https://redis.io/docs/latest/commands/backup-status) | [`BACKUP LIST`](https://redis.io/docs/latest/commands/backup-list) | [`BACKUP CLEANUP`](https://redis.io/docs/latest/commands/backup-cleanup)"},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Redis persistence](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence)"}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"127.0.0.1:6379> BACKUP ABORT\nOK","section_id":"examples"}]}
{"schema_version":2,"id":"commands/backup-cleanup","title":"BACKUP CLEANUP","url":"https://redis.io/docs/latest/commands/backup-cleanup/","summary":"Remove sealed backup files and return to idle.","since":"8.10.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"7e06e46e00a5703c56b8a6018a3e85f0a5cd98bff44b6311f2cc533bfd075140","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Removes sealed backup files and returns the backup state machine to idle."},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"details","title":"Details","role":"content","text":"`BACKUP CLEANUP` removes the sealed (or failed) backup artifacts and releases the files that were pinned by hard links, moving the backup state machine from `sealed` back to `idle`. Call it after the data plane has finished copying the files reported by [`BACKUP LIST`](https://redis.io/docs/latest/commands/backup-list).\n\nBy default a sealed backup is kept until you clean it up explicitly. If the `backup-sealed-ttl` configuration setting is non-zero, Redis automatically cleans up a sealed backup after the configured number of seconds; a value of `0` disables automatic cleanup.\n\nFor the full workflow, see [Redis persistence](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence#online-backups-with-the-backup-command-family)."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."},{"id":"see-also","title":"See also","role":"related","text":"[`BACKUP START`](https://redis.io/docs/latest/commands/backup-start) | [`BACKUP SEAL`](https://redis.io/docs/latest/commands/backup-seal) | [`BACKUP STATUS`](https://redis.io/docs/latest/commands/backup-status) | [`BACKUP LIST`](https://redis.io/docs/latest/commands/backup-list) | [`BACKUP ABORT`](https://redis.io/docs/latest/commands/backup-abort)"},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Redis persistence](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence)"}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"127.0.0.1:6379> BACKUP CLEANUP\nOK","section_id":"examples"}]}
{"schema_version":2,"id":"commands/backup-help","title":"BACKUP HELP","url":"https://redis.io/docs/latest/commands/backup-help/","summary":"Return helpful text about BACKUP command parameters.","since":"8.10.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-30T08:54:30-07:00","page_type":"content","content_hash":"9f6f204135783ff8679b66b3e9bd06eeb25b2314ddc5cd0f36bce029c5d105f7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `BACKUP HELP` command returns helpful text describing the different subcommands."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of sub-commands and their descriptions.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of sub-commands and their descriptions."}],"examples":[]}
{"schema_version":2,"id":"commands/backup-list","title":"BACKUP LIST","url":"https://redis.io/docs/latest/commands/backup-list/","summary":"List the immutable backup file paths pinned so far.","since":"8.10.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"cf666307f1929f871864b128df515224de55b341561cdfcd4bd9fbe98d2f2717","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns the absolute paths of the immutable backup files pinned so far."},{"id":"examples","title":"Examples","role":"example","text":"While the backup is still in the `incrementing` state, only the BASE file is pinned:\n\n[code example]\n\nAfter [`BACKUP SEAL`](https://redis.io/docs/latest/commands/backup-seal), the INCR file and the manifest are pinned as well:\n\n[code example]"},{"id":"details","title":"Details","role":"content","text":"`BACKUP LIST` reports the absolute paths of the files that have been pinned by hard links so far. This lets the data plane start uploading the BASE snapshot while Redis is still accumulating incremental writes, before the backup is sealed. After the backup is sealed, the list also includes the INCR file and the manifest.\n\nFor the full workflow, see [Redis persistence](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence#online-backups-with-the-backup-command-family)."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): the absolute paths of the immutable files pinned so far.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): the absolute paths of the immutable files pinned so far."},{"id":"see-also","title":"See also","role":"related","text":"[`BACKUP START`](https://redis.io/docs/latest/commands/backup-start) | [`BACKUP SEAL`](https://redis.io/docs/latest/commands/backup-seal) | [`BACKUP STATUS`](https://redis.io/docs/latest/commands/backup-status) | [`BACKUP ABORT`](https://redis.io/docs/latest/commands/backup-abort) | [`BACKUP CLEANUP`](https://redis.io/docs/latest/commands/backup-cleanup)"},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Redis persistence](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence)"}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"127.0.0.1:6379> BACKUP LIST\n1) \"/var/lib/redis/backupdir/appendonly.aof.3.base.rdb\"","section_id":"examples"},{"id":"examples-ex1","language":"plaintext","code":"127.0.0.1:6379> BACKUP LIST\n1) \"/var/lib/redis/backupdir/appendonly.aof.3.base.rdb\"\n2) \"/var/lib/redis/backupdir/appendonly.aof.3.incr.aof\"\n3) \"/var/lib/redis/backupdir/appendonly.aof.manifest\"","section_id":"examples"}]}
{"schema_version":2,"id":"commands/backup-seal","title":"BACKUP SEAL","url":"https://redis.io/docs/latest/commands/backup-seal/","summary":"Freeze the current backup (BASE + INCR + manifest).","since":"8.10.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"e6ec0382873625b483a5d2736703f88d87e7497dbbb98ff6e6fad598032037d1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Freezes the current backup, producing the final immutable file set: the BASE snapshot, the incremental append-only file (INCR), and a standalone manifest."},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"details","title":"Details","role":"content","text":"`BACKUP SEAL` moves the backup state machine from `incrementing` to `sealed`. Restore loads `BASE + INCR`, so the restored dataset reflects the state at the seal boundary, not merely the earlier BASE snapshot. Sealing performs the following steps:\n\n* Flushes and fsyncs the current INCR file.\n* Hard-links the INCR file into the backup directory.\n* Writes a standalone manifest that references only this backup's `{BASE, INCR}` file set.\n* Rotates the live AOF when AOF persistence is enabled.\n* Stops and removes the temporary AOF state when the backup created it for an `appendonly no` instance.\n\nAfter sealing, the file set is complete and immutable. The data plane can copy the files reported by [`BACKUP LIST`](https://redis.io/docs/latest/commands/backup-list), then call [`BACKUP CLEANUP`](https://redis.io/docs/latest/commands/backup-cleanup) to release them.\n\nFor the full workflow and restore procedure, see [Redis persistence](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence#online-backups-with-the-backup-command-family)."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."},{"id":"see-also","title":"See also","role":"related","text":"[`BACKUP START`](https://redis.io/docs/latest/commands/backup-start) | [`BACKUP STATUS`](https://redis.io/docs/latest/commands/backup-status) | [`BACKUP LIST`](https://redis.io/docs/latest/commands/backup-list) | [`BACKUP ABORT`](https://redis.io/docs/latest/commands/backup-abort) | [`BACKUP CLEANUP`](https://redis.io/docs/latest/commands/backup-cleanup)"},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Redis persistence](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence)"}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"127.0.0.1:6379> BACKUP SEAL\nOK","section_id":"examples"}]}
{"schema_version":2,"id":"commands/backup-start","title":"BACKUP START","url":"https://redis.io/docs/latest/commands/backup-start/","summary":"Start a new backup into the configured 'backupdirname'.","since":"8.10.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"86d78b7c6e8a6cdfe67898d32fafc9644dc50dbf63e50c731434b4194a6526b8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Starts a new backup, opening a backup window and producing a fresh BASE snapshot in the directory named by the `backupdirname` configuration setting."},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"details","title":"Details","role":"content","text":"`BACKUP START` moves the backup state machine from `idle` to `snapshotting` and begins producing a fresh BASE snapshot through an append-only file rewrite (AOFRW). It works whether or not AOF persistence is enabled:\n\n* If AOF is enabled, Redis reuses the existing [multi-part AOF](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence#append-only-file) directly.\n* If AOF is disabled, Redis temporarily starts the AOF machinery without changing the configured `appendonly` value.\n* If an AOFRW is already active or scheduled and can be reused, the backup attaches to it.\n* If another child process is active, the backup enters the `pending` state and starts when an AOFRW can run.\n\nAfter the snapshot rewrite completes, Redis hard-links the BASE file into the backup directory and enters the `incrementing` state, where it continues to accumulate incremental writes until you call [`BACKUP SEAL`](https://redis.io/docs/latest/commands/backup-seal).\n\nThe backup directory (named by `backupdirname` and resolved under the Redis working directory `dir`) must be empty when you call `BACKUP START`. Only one backup can be in progress at a time.\n\nFor the full workflow and restore procedure, see [Redis persistence](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence#online-backups-with-the-backup-command-family)."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."},{"id":"see-also","title":"See also","role":"related","text":"[`BACKUP SEAL`](https://redis.io/docs/latest/commands/backup-seal) | [`BACKUP STATUS`](https://redis.io/docs/latest/commands/backup-status) | [`BACKUP LIST`](https://redis.io/docs/latest/commands/backup-list) | [`BACKUP ABORT`](https://redis.io/docs/latest/commands/backup-abort) | [`BACKUP CLEANUP`](https://redis.io/docs/latest/commands/backup-cleanup)"},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Redis persistence](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence)"}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"127.0.0.1:6379> BACKUP START\nOK","section_id":"examples"}]}
{"schema_version":2,"id":"commands/backup-status","title":"BACKUP STATUS","url":"https://redis.io/docs/latest/commands/backup-status/","summary":"Report the current backup state.","since":"8.10.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"50383ca7b11616af5ad26c114d5f0428393029de3058d93d99d9f00d7db791b8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Reports the current backup state."},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"details","title":"Details","role":"content","text":"`BACKUP STATUS` reports the current state of the backup state machine along with timing and error information. The reply contains the following fields:\n\n| Field | Description |\n|:------|:------------|\n| `state` | The current backup state (see below). |\n| `error` | The error message if the backup failed; an empty string otherwise. |\n| `start_time` | The Unix time, in seconds, when the current backup started; `0` if no backup has started. |\n| `end_time` | The Unix time, in seconds, when the current backup was sealed; `0` if it has not been sealed. |\n\nThe `state` field is one of the following:\n\n| State | Description |\n|:------|:------------|\n| `idle` | No backup is in progress. |\n| `pending` | A backup was requested but is waiting for an append-only file rewrite (AOFRW) to become available. |\n| `snapshotting` | Redis is producing the BASE snapshot. |\n| `incrementing` | The BASE snapshot is complete and pinned; Redis is accumulating incremental writes and waiting to be sealed. |\n| `sealed` | The backup is frozen; the file set is complete and immutable, awaiting data-plane consumption and cleanup. |\n| `failed` | The backup failed or was aborted. A new backup can be started. |\n\nFor lightweight monitoring, `INFO persistence` also exposes a `backup_in_progress` boolean. Full backup state, error, and timestamps are available only through `BACKUP STATUS`.\n\nFor the full workflow, see [Redis persistence](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence#online-backups-with-the-backup-command-family)."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a flat array of field-value pairs describing the current backup state.\n\n**RESP3:**\n\n[Map reply](../../develop/reference/protocol-spec#maps): a map of fields to values describing the current backup state."},{"id":"see-also","title":"See also","role":"related","text":"[`BACKUP START`](https://redis.io/docs/latest/commands/backup-start) | [`BACKUP SEAL`](https://redis.io/docs/latest/commands/backup-seal) | [`BACKUP LIST`](https://redis.io/docs/latest/commands/backup-list) | [`BACKUP ABORT`](https://redis.io/docs/latest/commands/backup-abort) | [`BACKUP CLEANUP`](https://redis.io/docs/latest/commands/backup-cleanup)"},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Redis persistence](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence)"}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"127.0.0.1:6379> BACKUP STATUS\n1) \"state\"\n2) \"incrementing\"\n3) \"error\"\n4) \"\"\n5) \"start_time\"\n6) \"1717921800\"\n7) \"end_time\"\n8) \"0\"","section_id":"examples"}]}
{"schema_version":2,"id":"commands/bf.add","title":"BF.ADD","url":"https://redis.io/docs/latest/commands/bf.add/","summary":"Adds an item to a Bloom Filter","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"879ec9b3840efa09d1694b2c091680bbd1e300f16c78a3928713a9572e57ed7a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Adds an item to a Bloom filter.\n\nThis command is similar to [`BF.MADD`](https://redis.io/docs/latest/commands/bf.madd), except that only one item can be added."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nis key name for a Bloom filter to add the item to.\n\nIf `key` does not exist - a new Bloom filter is created with default error rate, capacity, and expansion (see [`BF.RESERVE`](https://redis.io/docs/latest/commands/bf.reserve)).\n</details>\n\n<details open><summary><code>item</code></summary>\n\nis an item to add.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> BF.ADD bf item1\n(integer) 1\nredis> BF.ADD bf item1\n(integer) 0"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\"><nobr>&#x2705; Flexible & Annual</nobr></span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): `1` for successfully adding an item, or `0` if there's a probability that the item was already added to the filter.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, wrong key type, or when the filter is full.\n\n**RESP3:**\n\nOne of the following:\n* [Boolean reply](https://redis.io/docs/latest/develop/reference/protocol-spec#booleans): `true` for successfully adding an item, or `false` if there's a probability that the item was already added to the filter.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, wrong key type, or when the filter is full."}],"examples":[]}
{"schema_version":2,"id":"commands/bf.card","title":"BF.CARD","url":"https://redis.io/docs/latest/commands/bf.card/","summary":"Returns the cardinality of a Bloom filter","since":"2.4.4","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"de4a8e3fc87dcdc0e7af2f69cae131d00e98dea1f3e3e4856c7d82daeb8d5fb1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns the cardinality of a Bloom filter - number of items that were added to a Bloom filter and detected as unique (items that caused at least one bit to be set in at least one sub-filter)\n\n(since RedisBloom 2.4.4)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nis key name for a Bloom filter.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> BF.ADD bf1 item_foo\n(integer) 1\nredis> BF.CARD bf1\n(integer) 1\nredis> BF.CARD bf_new\n(integer) 0"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\"><nobr>&#x2705; Flexible & Annual</nobr></span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): the number of items detected as unique that were added to the Bloom filter (items that caused at least one bit to be set in at least one sub-filter), or `0` when the given `key` does not exist.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, wrong key type, or when the filter is full.\n\nNote: when `key` exists, `BF.CARD` returns the same value as `BF.INFO key ITEMS`.\n\n**RESP3:**\n\nOne of the following:\n* [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): the number of items detected as unique that were added to the Bloom filter (items that caused at least one bit to be set in at least one sub-filter), or `0` when the given `key` does not exist.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, wrong key type, or when the filter is full.\n\nNote: when `key` exists, `BF.CARD` returns the same value as `BF.INFO key ITEMS`."}],"examples":[]}
{"schema_version":2,"id":"commands/bf.exists","title":"BF.EXISTS","url":"https://redis.io/docs/latest/commands/bf.exists/","summary":"Checks whether an item exists in a Bloom Filter","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"2be66e5f4b802806211e2cfde0cda4357188d05265246559c45997a5bea436f8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Determines whether a given item was added to a Bloom filter.\n\nThis command is similar to [`BF.MEXISTS`](https://redis.io/docs/latest/commands/bf.mexists), except that only one item can be checked."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nis key name for a Bloom filter.\n\n</details>\n\n<details open><summary><code>item</code></summary>\n\nis an item to check.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> BF.ADD bf item1\n(integer) 1\nredis> BF.EXISTS bf item1\n(integer) 1\nredis> BF.EXISTS bf item2\n(integer) 0"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers), where a value of `1` means the item was probably already added to the filter. A value of `0` means the item was definitely not added, `key` does not exist, or `key` contains a value of the wrong type.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if invalid arguments are passed.\n\n**RESP3:**\n\nOne of the following:\n* [Boolean reply](https://redis.io/docs/latest/develop/reference/protocol-spec#booleans), where a value of `true` means the item was probably already added to the filter. A value of `false` means the item was definitely not added, `key` does not exist, or `key` contains a value of the wrong type.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if invalid arguments are passed."}],"examples":[]}
{"schema_version":2,"id":"commands/bf.info","title":"BF.INFO","url":"https://redis.io/docs/latest/commands/bf.info/","summary":"Returns information about a Bloom Filter","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"91a2b8058149a3d7256ef31d909631e9f3844f82e8a1028d36020e16d9ca214c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns information about a Bloom filter."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nis key name for a Bloom filter.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>CAPACITY</code></summary>\n\nReturn the number of unique items that can be stored in this Bloom filter before scaling would be required (including already added items).\n</details>\n\n<details open><summary><code>SIZE</code></summary>\n\nReturn the memory size: number of bytes allocated for this Bloom filter.\n</details>\n\n<details open><summary><code>FILTERS</code></summary>\n\nReturn the number of sub-filters.\n</details>\n\n<details open><summary><code>ITEMS</code></summary>\n\nReturn the number of items that were added to this Bloom filter and detected as unique (items that caused at least one bit to be set in at least one sub-filter).\n</details>\n\n<details open><summary><code>EXPANSION</code></summary>\n\nReturn the expansion rate.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> BF.ADD bf1 observation1\n(integer) 1\nredis> BF.INFO bf1\n 1) Capacity\n 2) (integer) 100\n 3) Size\n 4) (integer) 240\n 5) Number of filters\n 6) (integer) 1\n 7) Number of items inserted\n 8) (integer) 1\n 9) Expansion rate\n10) (integer) 2\nredis> BF.INFO bf1 CAPACITY\n1) (integer) 100"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* A singleton [array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) with an [integer](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) representing the value of the requested property.\n* An [array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) with [simple string](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) and [integer](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) pairs.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, wrong key type, or when the key does not exist.\n\n**RESP3:**\n\nOne of the following:\n* [Map reply](https://redis.io/docs/latest/develop/reference/protocol-spec#maps) with [simple string](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) and [integer](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) pairs.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, wrong key type, or when the key does not exist."}],"examples":[]}
{"schema_version":2,"id":"commands/bf.insert","title":"BF.INSERT","url":"https://redis.io/docs/latest/commands/bf.insert/","summary":"Adds one or more items to a Bloom Filter. A filter will be created if it does not exist","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"970adad549b3f143437d2aa54719fe6024c627dd85271ba54be024463e7b7927","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Creates a new Bloom filter if the `key` does not exist using the specified error rate, capacity, and expansion, then adds all specified items to the Bloom Filter.\n\nThis command is similar to [`BF.MADD`](https://redis.io/docs/latest/commands/bf.madd), except that the error rate, capacity, and expansion can be specified. It is a sugarcoated combination of [`BF.RESERVE`](https://redis.io/docs/latest/commands/bf.reserve) and [`BF.MADD`](https://redis.io/docs/latest/commands/bf.madd)."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nis key name for a Bloom filter to add items to.\n\nIf `key` does not exist, a new Bloom filter is created.\n</details>\n\n<details open><summary><code>ITEMS item...</code></summary>\n\nOne or more items to add.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>NOCREATE</code></summary>\n\nIndicates that the filter should not be created if it does not already exist.\nIf the filter does not yet exist, an error is returned rather than creating it automatically.\nThis may be used where a strict separation between filter creation and filter addition is desired.\nIt is an error to specify `NOCREATE` together with either `CAPACITY` or `ERROR`.\n</details>\n\n<details open><summary><code>CAPACITY capacity</code></summary>\n\nSpecifies the desired `capacity` for the filter to be created.\nThis parameter is ignored if the filter already exists.\nIf the filter is automatically created and this parameter is absent, then the module-level `capacity` is used.\nSee [`BF.RESERVE`](https://redis.io/docs/latest/commands/bf.reserve) for more information about the impact of this value.\n</details>\n\n<details open><summary><code>ERROR error</code></summary>\n    \nSpecifies the `error` ratio of the newly created filter if it does not yet exist.\nIf the filter is automatically created and `error` is not specified then the module-level error rate is used.\nSee [`BF.RESERVE`](https://redis.io/docs/latest/commands/bf.reserve) for more information about the format of this value.\n</details>\n\n<details open><summary><code>NONSCALING</code></summary>\n\nPrevents the filter from creating additional sub-filters if initial capacity is reached.\nNon-scaling filters require slightly less memory than their scaling counterparts. The filter returns an error when `capacity` is reached.\n</details>\n\n<details open><summary><code>EXPANSION expansion</code></summary>\n\nWhen `capacity` is reached, an additional sub-filter is created.\nThe size of the new sub-filter is the size of the last sub-filter multiplied by `expansion`, specified as a positive integer.\n\nIf the number of elements to be stored in the filter is unknown, use an `expansion` of `2` or more to reduce the number of sub-filters.\nOtherwise, use an `expansion` of `1` to reduce memory consumption. The default value is `2`.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"Add three items to a filter, then create the filter with default parameters if it does not already exist.\n\n\nredis> BF.INSERT filter ITEMS foo bar baz\n1) (integer) 1\n2) (integer) 1\n3) (integer) 1\n\n\nAdd one item to a filter, then create the filter with a capacity of 10000 if it does not already exist.\n\n\nredis> BF.INSERT filter CAPACITY 10000 ITEMS hello\n1) (integer) 1\n\n\nAdd two items to a filter, then return error if the filter does not already exist.\n\n\nredis> BF.INSERT filter NOCREATE ITEMS foo bar\n1) (integer) 0\n2) (integer) 0"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following: where each element is one of these options:\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays), where each element is one of the following options:\n  * [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) `1` for successfully adding an item, or `0` if there's a probability that the item was already added to the filter.\n  * [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) when the item cannot be added because the filter is full.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) when the number of arguments or key type is wrong, and also when `NOCREATE` is specified and `key` does not exist.\n\n**RESP3:**\n\nOne of the following: where each element is one of these options:\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays), where each element is one of the following options:\n  * [Boolean reply](https://redis.io/docs/latest/develop/reference/protocol-spec#booleans) `true` for successfully adding an item, or `false` if there's a probability that the item was already added to the filter.\n  * [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) when the item cannot be added because the filter is full.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) when the number of arguments or key type is wrong, and also when `NOCREATE` is specified and `key` does not exist."}],"examples":[]}
{"schema_version":2,"id":"commands/bf.loadchunk","title":"BF.LOADCHUNK","url":"https://redis.io/docs/latest/commands/bf.loadchunk/","summary":"Restores a filter previously saved using SCANDUMP","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"8951bdea0b7b9b6c300d4ae734cfa3a0ca40b8d0bf02d1a8cfa40b4f658f3918","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Restores a Bloom filter previously saved using [`BF.SCANDUMP`](https://redis.io/docs/latest/commands/bf.scandump).\n\nSee the [`BF.SCANDUMP`](https://redis.io/docs/latest/commands/bf.scandump) command for example usage.\n\n<note><b>Notes</b>\n\n- This command overwrites the Bloom filter stored under `key`. \n- Make sure that the Bloom filter is not changed between invocations.\n\n</note>"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nis key name for a Bloom filter to restore.\n</details>\n\n<details open><summary><code>iterator</code></summary>\n\nIterator value associated with `data` (returned by [`BF.SCANDUMP`](https://redis.io/docs/latest/commands/bf.scandump))\n</details>\n\n<details open><summary><code>data</code></summary>\n\nCurrent data chunk (returned by [`BF.SCANDUMP`](https://redis.io/docs/latest/commands/bf.scandump))\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"See [`BF.SCANDUMP`](https://redis.io/docs/latest/commands/bf.scandump) for an example."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, wrong key type, or when invalid data was passed.\n\n**RESP3:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, wrong key type, or when invalid data was passed."}],"examples":[]}
{"schema_version":2,"id":"commands/bf.madd","title":"BF.MADD","url":"https://redis.io/docs/latest/commands/bf.madd/","summary":"Adds one or more items to a Bloom Filter. A filter will be created if it does not exist","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"e38a4d768ea6fbb6d01c328ae8f658edba2f65b5c431626042d1c1e73ce05562","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Adds one or more items to a Bloom filter.\n\nThis command is similar to [`BF.ADD`](https://redis.io/docs/latest/commands/bf.add), except that you can add more than one item.\n\nThis command is similar to [`BF.INSERT`](https://redis.io/docs/latest/commands/bf.insert), except that the error rate, capacity, and expansion cannot be specified."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nis key name for a Bloom filter to add the items to.\n\nIf `key` does not exist - a new Bloom filter is created with default error rate, capacity, and expansion (see [`BF.RESERVE`](https://redis.io/docs/latest/commands/bf.reserve)).\n</details>\n\n<details open><summary><code>item...</code></summary>\n\nOne or more items to add.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> BF.MADD bf item1 item2 item2\n1) (integer) 1\n2) (integer) 1\n3) (integer) 0"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) where each element is either\n  * an [integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers), where `1` means that the item has been added successfully, and `0` means there's a probability that the item was already added to the filter.\n  * a [simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) when the item cannot be added because the filter is full.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, key not found, wrong key type, or when the filter is full.\n\n**RESP3:**\n\nOne of the following:\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) where each element is either\n  * a [boolean reply](https://redis.io/docs/latest/develop/reference/protocol-spec#booleans), where `true` means that the item has been added successfully, and `false` means there's a probability that the item was already added to the filter.\n  * a [simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) when the item cannot be added because the filter is full.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, key not found, wrong key type, or when the filter is full."}],"examples":[]}
{"schema_version":2,"id":"commands/bf.mexists","title":"BF.MEXISTS","url":"https://redis.io/docs/latest/commands/bf.mexists/","summary":"Checks whether one or more items exist in a Bloom Filter","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"c726e3e19e37db1da81527af59a39f7668e8c5393859b3a62de6b223d9de15eb","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Determines whether one or more items were added to a Bloom filter.\n\nThis command is similar to [`BF.EXISTS`](https://redis.io/docs/latest/commands/bf.exists), except that more than one item can be checked."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nis key name for a Bloom filter.\n\n</details>\n\n<details open><summary><code>item...</code></summary>\n\nOne or more items to check.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> BF.MADD bf item1 item2\n1) (integer) 1\n2) (integer) 1\nredis> BF.MEXISTS bf item1 item2 item3\n1) (integer) 1\n2) (integer) 1\n3) (integer) 0"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* An [array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [integers](https://redis.io/docs/latest/develop/reference/protocol-spec#integers), where each element corresponds to an item in the request. A value of `1` means the item was probably already added to the filter. A value of `0` means the item was definitely not added, `key` does not exist, or `key` contains a value of the wrong type.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if invalid arguments are passed.\n\n**RESP3:**\n\nOne of the following:\n* An [array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [booleans](https://redis.io/docs/latest/develop/reference/protocol-spec#booleans), where each element corresponds to an item in the request. A value of `true` means the item was probably already added to the filter. A value of `false` means the item was definitely not added, `key` does not exist, or `key` contains a value of the wrong type.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if invalid arguments are passed."}],"examples":[]}
{"schema_version":2,"id":"commands/bf.reserve","title":"BF.RESERVE","url":"https://redis.io/docs/latest/commands/bf.reserve/","summary":"Creates a new Bloom Filter","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"d0a88bc6594d1eeae135ebd293857156d0ca45e6b20f9a04eb606990ea2993e4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Creates an empty Bloom filter with a single sub-filter for the initial specified capacity and with an upper bound `error_rate`.\n\nBy default, the filter auto-scales by creating additional sub-filters when `capacity` is reached.\nThe new sub-filter is created with size of the previous sub-filter multiplied by `expansion`.\n\nThough the filter can scale up by creating sub-filters, it is recommended to reserve the estimated required `capacity` since maintaining and querying\nsub-filters requires additional memory (each sub-filter uses an extra bits and hash function) and consume  further CPU time than an equivalent filter that had\nthe right capacity at creation time.\n\nThe optimal number of hash functions is `ceil(-ln(error_rate) / ln(2))`.\n\nThe required number of bits per item, given the desired `error_rate` and the optimal number of hash functions, is `-ln(error_rate) / ln(2)^2`. Hence, the required number of bits in the filter is `capacity * -ln(error_rate) / ln(2)^2`.\n\n* **1%**    error rate requires  7 hash functions and  9.585 bits per item.\n* **0.1%**  error rate requires 10 hash functions and 14.378 bits per item.\n* **0.01%** error rate requires 14 hash functions and 19.170 bits per item."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nis key name for the Bloom filter to be created.\n</details>\n\n<details open><summary><code>error_rate</code></summary>\n\nThe desired probability for false positives. The rate is a decimal value between 0 and 1.\nFor example, for a desired false positive rate of 0.1% (1 in 1000), error_rate should be set to 0.001.\n</details>\n\n<details open><summary><code>capacity</code></summary>\n\nThe number of entries intended to be added to the filter.\nIf your filter allows scaling, performance will begin to degrade after adding more items than this number.\nThe actual degradation depends on how far the limit has been exceeded. Performance degrades linearly with the number of `sub-filters`.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>NONSCALING</code></summary>\n\nPrevents the filter from creating additional sub-filters if initial capacity is reached.\nNon-scaling filters requires slightly less memory than their scaling counterparts. The filter returns an error when `capacity` is reached.\n</details>\n\n<details open><summary><code>EXPANSION expansion</code></summary>\n\nWhen `capacity` is reached, an additional sub-filter is created.\nThe size of the new sub-filter is the size of the last sub-filter multiplied by `expansion`, specified as a positive integer.\n\nIf the number of items to be stored in the filter is unknown, you use an `expansion` of `2` or more to reduce the number of sub-filters.\nOtherwise, you use an `expansion` of `1` to reduce memory consumption. The default value is `2`.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> BF.RESERVE bf 0.01 1000\nOK\n\n\n\nredis> BF.RESERVE bf 0.01 1000\n(error) ERR item exists\n\n\n\nredis> BF.RESERVE bf_exp 0.01 1000 EXPANSION 2\nOK\n\n\n\nredis> BF.RESERVE bf_non 0.01 1000 NONSCALING\nOK"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` if the filter was created successfully.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments or the key already exists.\n\n**RESP3:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` if the filter was created successfully.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments or the key already exists."}],"examples":[]}
{"schema_version":2,"id":"commands/bf.scandump","title":"BF.SCANDUMP","url":"https://redis.io/docs/latest/commands/bf.scandump/","summary":"Begins an incremental save of the bloom filter","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"407f28f57d04d1159245228eaf2ea17683011ea75c15b5dd249cf7456dccf115","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Begins an incremental save of the Bloom filter.\n\nThis command is useful for large Bloom filters that cannot fit into the [`DUMP`](https://redis.io/docs/latest/commands/dump) and [`RESTORE`](https://redis.io/docs/latest/commands/restore) model.\n\nThe first time this command is called, the value of `iter` should be 0. \n\nThis command returns successive `(iter, data)` pairs until `(0, NULL)` to indicate completion."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nis key name for a Bloom filter to save.\n</details>\n\n<details open><summary><code>iterator</code></summary>\n\nIterator value; either 0 or the iterator from a previous invocation of this command\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> BF.RESERVE bf 0.1 10\nOK\nredis> BF.ADD bf item1\n(integer) 1\nredis> BF.SCANDUMP bf 0\n1) (integer) 1\n2) \"\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\b\\x00\\x00\\x00\\x00\\x00\\x00\\x00@\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x9a\\x99\\x99\\x99\\x99\\x99\\xa9?J\\xf7\\xd4\\x9e\\xde\\xf0\\x18@\\x05\\x00\\x00\\x00\\n\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\nredis> BF.SCANDUMP bf 1\n1) (integer) 9\n2) \"\\x01\\b\\x00\\x80\\x00\\x04 \\x00\"\nredis> BF.SCANDUMP bf 9\n1) (integer) 0\n2) \"\"\nredis> DEL bf\n(integer) 1\nredis> BF.LOADCHUNK bf 1 \"\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\b\\x00\\x00\\x00\\x00\\x00\\x00\\x00@\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x9a\\x99\\x99\\x99\\x99\\x99\\xa9?J\\xf7\\xd4\\x9e\\xde\\xf0\\x18@\\x05\\x00\\x00\\x00\\n\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\nOK\nredis> BF.LOADCHUNK bf 9 \"\\x01\\b\\x00\\x80\\x00\\x04 \\x00\"\nOK\nredis> BF.EXISTS bf item1\n(integer) 1\n\n\nPython code:\n\nchunks = []\niter = 0\nwhile True:\n    iter, data = BF.SCANDUMP(key, iter)\n    if iter == 0:\n        break\n    else:\n        chunks.append([iter, data])\n\n# Load it back\nfor chunk in chunks:\n    iter, data = chunk\n    BF.LOADCHUNK(key, iter, data)"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): a two-element array of an [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) (_Iterator_) and a [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) (_Data_).\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, the key was not found, or the key is of the wrong type.\n\nThe Iterator is passed as input to the next invocation of `BF.SCANDUMP`. If _Iterator_ is 0, then it means iteration has completed.\n\nThe iterator-data pair should also be passed to [`BF.LOADCHUNK`](https://redis.io/docs/latest/commands/bf.loadchunk) when restoring the filter.\n\n**RESP3:**\n\nOne of the following:\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): a two-element array of an [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) (_Iterator_) and a [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) (_Data_).\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, the key was not found, or the key is of the wrong type.\n\nThe Iterator is passed as input to the next invocation of `BF.SCANDUMP`. If _Iterator_ is 0, then it means iteration has completed.\n\nThe iterator-data pair should also be passed to [`BF.LOADCHUNK`](https://redis.io/docs/latest/commands/bf.loadchunk) when restoring the filter."}],"examples":[]}
{"schema_version":2,"id":"commands/bgrewriteaof","title":"BGREWRITEAOF","url":"https://redis.io/docs/latest/commands/bgrewriteaof/","summary":"Asynchronously rewrites the append-only file to disk.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"18b7d7c1404835d0eee5adcc68cac8117ebd4dc35d711cbfbf675fa6de80d06e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Instruct Redis to start an [Append Only File](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence#append-only-file) rewrite process.\nThe rewrite will create a small optimized version of the current Append Only\nFile.\n\nIf `BGREWRITEAOF` fails, no data gets lost as the old AOF will be untouched.\n\nThe rewrite will be only triggered by Redis if there is not already a background\nprocess doing persistence.\n\nSpecifically:\n\n* If a Redis child is creating a snapshot on disk, the AOF rewrite is _scheduled_ but not started until the saving child producing the RDB file terminates. In this case the `BGREWRITEAOF` will still return a positive status reply, but with an appropriate message.  You can check if an AOF rewrite is scheduled looking at the [`INFO`](https://redis.io/docs/latest/commands/info) command as of Redis 2.6 or successive versions.\n* If an AOF rewrite is already in progress the command returns an error and no\n  AOF rewrite will be scheduled for a later time.\n* If the AOF rewrite could start, but the attempt at starting it fails (for instance because of an error in creating the child process), an error is returned to the caller.\n\nSince Redis 2.4 the AOF rewrite is automatically triggered by Redis, however the\n`BGREWRITEAOF` command can be used to trigger a rewrite at any time.\n\nSee the [persistence documentation](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence) for detailed information."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): a simple string reply indicating that the rewriting started or is about to start ASAP when the call is executed with success.\nThe command may reply with an error in certain cases, as documented above.\n\n**RESP3:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): a simple string reply indicating that the rewriting started or is about to start ASAP when the call is executed with success.\nThe command may reply with an error in certain cases, as documented above."}],"examples":[]}
{"schema_version":2,"id":"commands/bgsave","title":"BGSAVE","url":"https://redis.io/docs/latest/commands/bgsave/","summary":"Asynchronously saves the database(s) to disk.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"3710e762b8d6428d2a8e9b8909bf5f720f114423e61affd0f5a7a4879d09c68c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Save the DB in background.\n\nNormally the OK code is immediately returned.\nRedis forks, the parent continues to serve the clients, the child saves the DB\non disk then exits.\n\nAn error is returned if there is already a background save running or if there\nis another non-background-save process running, specifically an in-progress AOF\nrewrite.\n\nIf `BGSAVE SCHEDULE` is used, the command will immediately return `OK` when an\nAOF rewrite is in progress and schedule the background save to run at the next\nopportunity.\n\nA client may be able to check if the operation succeeded using the [`LASTSAVE`](https://redis.io/docs/latest/commands/lastsave)\ncommand.\n\nSee the [persistence documentation](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence) for detailed information."},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>SCHEDULE</code></summary>\n\nSchedule the save to run after any in-progress `BGSAVE` finishes, instead of returning an error.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Simple string reply](../../develop/reference/protocol-spec#simple-strings): `Background saving started`.\n* [Simple string reply](../../develop/reference/protocol-spec#simple-strings): `Background saving scheduled`.\n\n**RESP3:**\n\nOne of the following:\n* [Simple string reply](../../develop/reference/protocol-spec#simple-strings): `Background saving started`.\n* [Simple string reply](../../develop/reference/protocol-spec#simple-strings): `Background saving scheduled`."}],"examples":[]}
{"schema_version":2,"id":"commands/bitcount","title":"BITCOUNT","url":"https://redis.io/docs/latest/commands/bitcount/","summary":"Counts the number of set bits (population counting) in a string.","since":"2.6.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:12+01:00","page_type":"content","content_hash":"6fca2186964793e61ac664e888896ac9a57047ccb68b1ac81fae80b12967ceae","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Count the number of set bits (population counting) in a string.\n\nBy default all the bytes contained in the string are examined.\nIt is possible to specify the counting operation only in an interval passing the\nadditional arguments _start_ and _end_.\n\nLike for the [`GETRANGE`](https://redis.io/docs/latest/commands/getrange) command start and end can contain negative values in\norder to index bytes starting from the end of the string, where -1 is the last\nbyte, -2 is the penultimate, and so forth.\n\nNon-existent keys are treated as empty strings, so the command will return zero.\n\nBy default, the additional arguments _start_ and _end_ specify a byte index.\nWe can use an additional argument `BIT` to specify a bit index.\nSo 0 is the first bit, 1 is the second bit, and so forth.\nFor negative values, -1 is the last bit, -2 is the penultimate, and so forth."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the string.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>start end</code></summary>\n\nCount set bits only within the range from `start` to `end` (inclusive). By default these are byte offsets; negative values count from the end of the string.\n\n</details>\n\n<details open><summary><code>BYTE | BIT</code></summary>\n\nInterpret `start` and `end` as byte offsets (`BYTE`, the default) or bit offsets (`BIT`).\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SET mykey \"foobar\"\nOK\nredis> BITCOUNT mykey\n(integer) 26\nredis> BITCOUNT mykey 0 0\n(integer) 4\nredis> BITCOUNT mykey 1 1\n(integer) 6\nredis> BITCOUNT mykey 1 1 BYTE\n(integer) 6\nredis> BITCOUNT mykey 5 30 BIT\n(integer) 17"},{"id":"details","title":"Details","role":"content","text":""},{"id":"pattern-real-time-metrics-using-bitmaps","title":"Pattern: real-time metrics using bitmaps","role":"content","text":"Bitmaps are a very space-efficient representation of certain kinds of\ninformation.\nOne example is a Web application that needs the history of user visits, so that\nfor instance it is possible to determine what users are good targets of beta\nfeatures.\n\nUsing the [`SETBIT`](https://redis.io/docs/latest/commands/setbit) command this is trivial to accomplish, identifying every day\nwith a small progressive integer.\nFor instance day 0 is the first day the application was put online, day 1 the\nnext day, and so forth.\n\nEvery time a user performs a page view, the application can register that in\nthe current day the user visited the web site using the [`SETBIT`](https://redis.io/docs/latest/commands/setbit) command setting\nthe bit corresponding to the current day.\n\nLater it will be trivial to know the number of single days the user visited the\nweb site simply calling the `BITCOUNT` command against the bitmap.\n\nA similar pattern where user IDs are used instead of days is described\nin the article called \"[Fast easy realtime metrics using Redis\nbitmaps](http://blog.getspool.com/2011/11/29/fast-easy-realtime-metrics-using-redis-bitmaps)\"."},{"id":"performance-considerations","title":"Performance considerations","role":"performance","text":"In the above example of counting days, even after 10 years the application is\nonline we still have just `365*10` bits of data per user, that is just 456 bytes\nper user.\nWith this amount of data `BITCOUNT` is still as fast as any other O(1) Redis\ncommand like [`GET`](https://redis.io/docs/latest/commands/get) or [`INCR`](https://redis.io/docs/latest/commands/incr).\n\nWhen the bitmap is big, there are two alternatives:\n\n* Taking a separated key that is incremented every time the bitmap is modified.\n  This can be very efficient and atomic using a small Redis Lua script.\n* Running the bitmap incrementally using the `BITCOUNT` _start_ and _end_\n  optional parameters, accumulating the results client-side, and optionally\n  caching the result into a key."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of bits set to 1.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of bits set to 1."}],"examples":[]}
{"schema_version":2,"id":"commands/bitfield","title":"BITFIELD","url":"https://redis.io/docs/latest/commands/bitfield/","summary":"Performs arbitrary bitfield integer operations on strings.","since":"3.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-06-15T06:26:34-07:00","page_type":"content","content_hash":"6c090fe5ba0604485e248d8412e7d48b321e8c5043eb22ebe5de75fb62aba2d2","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The command treats a Redis string as an array of bits, and is capable of addressing specific integer fields of varying bit widths and arbitrary non (necessary) aligned offset. In practical terms using this command you can set, for example, a signed 5-bit integer at bit offset 1234 to a specific value or retrieve a 31-bit unsigned integer from offset 4567. Similarly the command handles increments and decrements of the specified integers, providing guaranteed and well specified overflow and underflow behavior that the user can configure.\n\n`BITFIELD` is able to operate with multiple bit fields in the same command call. It takes a list of operations to perform, and returns an array of replies, where each array matches the corresponding operation in the list of arguments.\n\nFor example the following command increments a 5-bit signed integer at bit offset 100, and gets the value of the 4-bit unsigned integer at bit offset 0:\n\n    > BITFIELD mykey INCRBY i5 100 1 GET u4 0\n    1) (integer) 1\n    2) (integer) 0\n\nNote that:\n\n1. Addressing with `GET` bits outside the current string length (including the case the key does not exist at all), results in the operation being performed as if the missing part all consists of bits set to 0.\n2. Addressing with `SET` or `INCRBY` bits outside the current string length will enlarge the string, zero-padding it as needed, for the minimal length needed, according to the most far bit touched."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the string.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"A `BITFIELD` call may chain multiple of the following operations, applied in order.\n\n<details open><summary><code>GET encoding offset</code></summary>\n\nReturn the value at `offset`, interpreted with `encoding` (for example `u8` or `i16`).\n\n</details>\n\n<details open><summary><code>SET encoding offset value</code></summary>\n\nSet the value at `offset` (interpreted with `encoding`) to `value` and return its previous value.\n\n</details>\n\n<details open><summary><code>INCRBY encoding offset increment</code></summary>\n\nIncrement the value at `offset` (interpreted with `encoding`) by `increment` and return the new value.\n\n</details>\n\n<details open><summary><code>OVERFLOW WRAP | SAT | FAIL</code></summary>\n\nSet the overflow behavior for the following `SET` and `INCRBY` operations: `WRAP` (the default), `SAT` (saturate to the min/max value), or `FAIL` (return nil on overflow).\n\n</details>"},{"id":"details","title":"Details","role":"content","text":""},{"id":"supported-subcommands-and-integer-encoding","title":"Supported subcommands and integer encoding","role":"compatibility","text":"The following is the list of supported commands.\n\n* **GET** `<encoding>` `<offset>` -- Returns the specified bit field.\n* **SET** `<encoding>` `<offset>` `<value>` -- Set the specified bit field and returns its old value.\n* **INCRBY** `<encoding>` `<offset>` `<increment>` -- Increments or decrements (if a negative increment is given) the specified bit field and returns the new value.\n\nThere is another subcommand that only changes the behavior of successive\n`INCRBY` and `SET` subcommands calls by setting the overflow behavior:\n\n* **OVERFLOW** `[WRAP|SAT|FAIL]`\n\nWhere an integer encoding is expected, it can be composed by prefixing with `i` for signed integers and `u` for unsigned integers with the number of bits of our integer encoding. So for example `u8` is an unsigned integer of 8 bits and `i16` is a\nsigned integer of 16 bits.\n\nThe supported encodings are up to 64 bits for signed integers, and up to 63 bits for\nunsigned integers. This limitation with unsigned integers is due to the fact\nthat currently the Redis protocol is unable to return 64 bit unsigned integers\nas replies."},{"id":"bits-and-positional-offsets","title":"Bits and positional offsets","role":"content","text":"There are two ways in order to specify offsets in the bitfield command.\nIf a number without any prefix is specified, it is used just as a zero based\nbit offset inside the string.\n\nHowever if the offset is prefixed with a `#` character, the specified offset\nis multiplied by the integer encoding's width, so for example:\n\n    BITFIELD mystring SET i8 #0 100 SET i8 #1 200\n\nWill set the first i8 integer at offset 0 and the second at offset 8.\nThis way you don't have to do the math yourself inside your client if what\nyou want is a plain array of integers of a given size."},{"id":"overflow-control","title":"Overflow control","role":"content","text":"Using the `OVERFLOW` command the user is able to fine-tune the behavior of\nthe increment or decrement overflow (or underflow) by specifying one of\nthe following behaviors:\n\n* **WRAP**: wrap around, both with signed and unsigned integers. In the case of unsigned integers, wrapping is like performing the operation modulo the maximum value the integer can contain (the C standard behavior). With signed integers instead wrapping means that overflows restart towards the most negative value and underflows towards the most positive ones, so for example if an `i8` integer is set to the value 127, incrementing it by 1 will yield `-128`.\n* **SAT**: uses saturation arithmetic, that is, on underflows the value is set to the minimum integer value, and on overflows to the maximum integer value. For example incrementing an `i8` integer starting from value 120 with an increment of 10, will result into the value 127, and further increments will always keep the value at 127. The same happens on underflows, but towards the value is blocked at the most negative value.\n* **FAIL**: in this mode no operation is performed on overflows or underflows detected. The corresponding return value is set to NULL to signal the condition to the caller.\n\nNote that each `OVERFLOW` statement only affects the `INCRBY` and `SET`\ncommands that follow it in the list of subcommands, up to the next `OVERFLOW`\nstatement.\n\nBy default, **WRAP** is used if not otherwise specified.\n\n    > BITFIELD mykey incrby u2 100 1 OVERFLOW SAT incrby u2 102 1\n    1) (integer) 1\n    2) (integer) 1\n    > BITFIELD mykey incrby u2 100 1 OVERFLOW SAT incrby u2 102 1\n    1) (integer) 2\n    2) (integer) 2\n    > BITFIELD mykey incrby u2 100 1 OVERFLOW SAT incrby u2 102 1\n    1) (integer) 3\n    2) (integer) 3\n    > BITFIELD mykey incrby u2 100 1 OVERFLOW SAT incrby u2 102 1\n    1) (integer) 0\n    2) (integer) 3\n\nThe following is an example of `OVERFLOW FAIL` returning NULL.\n\n    > BITFIELD mykey OVERFLOW FAIL incrby u2 102 1\n    1) (nil)"},{"id":"motivations","title":"Motivations","role":"content","text":"The motivation for this command is that the ability to store many small integers\nas a single large bitmap (or segmented over a few keys to avoid having huge keys) is extremely memory efficient, and opens new use cases for Redis to be applied, especially in the field of real time analytics. This use cases are supported by the ability to specify the overflow in a controlled way.\n\nFun fact: Reddit's 2017 April fools' project [r/place](https://reddit.com/r/place) was [built using the Redis BITFIELD command](https://redditblog.com/2017/04/13/how-we-built-rplace/) in order to take an in-memory representation of the collaborative canvas."},{"id":"performance-considerations","title":"Performance considerations","role":"performance","text":"Usually `BITFIELD` is a fast command, however note that addressing far bits of currently short strings will trigger an allocation that may be more costly than executing the command on bits already existing."},{"id":"bit-order","title":"Bit order","role":"content","text":"The representation used by `BITFIELD` considers the bitmap as having the\nbit number 0 to be the most significant bit of the first byte, and so forth, so\nfor example setting a 5 bits unsigned integer to value 23 at offset 7 into a\nbitmap previously set to all zeroes, will produce the following representation:\n\n    +--------+--------+\n    |00000001|01110000|\n    +--------+--------+\n\nWhen offsets and integer sizes are aligned to bytes boundaries, this is the\nsame as big endian, however when such alignment does not exist, its important\nto also understand how the bits inside a byte are ordered."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Array reply](../../develop/reference/protocol-spec#arrays): each entry being the corresponding result of the sub-command given at the same position.\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): if OVERFLOW FAIL was given and overflows or underflows are detected.\n\n**RESP3:**\n\nOne of the following:\n* [Array reply](../../develop/reference/protocol-spec#arrays): each entry being the corresponding result of the sub-command given at the same position.\n* [Null reply](../../develop/reference/protocol-spec#nulls): if OVERFLOW FAIL was given and overflows or underflows are detected."}],"examples":[]}
{"schema_version":2,"id":"commands/bitfield_ro","title":"BITFIELD_RO","url":"https://redis.io/docs/latest/commands/bitfield_ro/","summary":"Performs arbitrary read-only bitfield integer operations on strings.","since":"6.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:12+01:00","page_type":"content","content_hash":"f71d80dd233db865de7414f04f121432d6754b1a5993fb27ab791bbc22d49fd3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Read-only variant of the [`BITFIELD`](https://redis.io/docs/latest/commands/bitfield) command.\nIt is like the original [`BITFIELD`](https://redis.io/docs/latest/commands/bitfield) but only accepts `GET` subcommand and can safely be used in read-only replicas.\n\nSince the original [`BITFIELD`](https://redis.io/docs/latest/commands/bitfield) has `SET` and `INCRBY` options it is technically flagged as a writing command in the Redis command table.\nFor this reason read-only replicas in a Redis Cluster will redirect it to the master instance even if the connection is in read-only mode (see the [`READONLY`](https://redis.io/docs/latest/commands/readonly) command of Redis Cluster).\n\nSince Redis 6.2, the `BITFIELD_RO` variant was introduced in order to allow [`BITFIELD`](https://redis.io/docs/latest/commands/bitfield) behavior in read-only replicas without breaking compatibility on command flags.\n\nSee original [`BITFIELD`](https://redis.io/docs/latest/commands/bitfield) for more details."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the string.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>GET encoding offset</code></summary>\n\nReturn the value at `offset`, interpreted with `encoding` (for example `u8` or `i16`). May be given multiple times.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): each entry being the corresponding result of the sub-command given at the same position.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): each entry being the corresponding result of the sub-command given at the same position."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"BITFIELD_RO hello GET i8 16","section_id":"examples"}]}
{"schema_version":2,"id":"commands/bitop","title":"BITOP","url":"https://redis.io/docs/latest/commands/bitop/","summary":"Performs bitwise operations on multiple strings, and stores the result.","since":"2.6.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:12+01:00","page_type":"content","content_hash":"ad47b01ea1ac76b64e1eac8190c32bf4a92f05ad3bea53035451505268c1cce5","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nPerform a bitwise operation between multiple keys (containing string values) and\nstore the result in the destination key.\n\nThe `BITOP` command supports eight bitwise operations: `AND`, `OR`, `XOR`,\n`NOT`, `DIFF`, `DIFF1`, `ANDOR`, and `ONE`. The valid forms to call the command are:\n\n* `BITOP AND destkey srckey1 srckey2 srckey3 ... srckeyN`\n\n    A bit in `destkey` is set only if it is set in all source bitmaps.\n* `BITOP OR  destkey srckey1 srckey2 srckey3 ... srckeyN`\n\n    A bit in `destkey` is set only if it is set in at least one source bitmap.\n* `BITOP XOR destkey srckey1 srckey2 srckey3 ... srckeyN`\n\n    Mostly used with two source bitmaps, a bit in `destkey` is set only if its value differs between the two source bitmaps.\n* `BITOP NOT destkey srckey`\n\n    `NOT` is a unary operator and only supports a single source bitmap; set the bit to the inverse of its value in the source bitmap.\n* `BITOP DIFF destkey X [Y1 Y2 ...]` <sup>[1](#list-note-1)</sup>\n\n    A bit in `destkey` is set if it is set in `X`, but not in any of `Y1, Y2, ...` .\n* `BITOP DIFF1 destkey X [Y1 Y2 ...]` <sup>[1](#list-note-1)</sup>\n\n    A bit in `destkey` is set if it is set in one or more of `Y1, Y2, ...`, but not in `X`.\n* `BITOP ANDOR destkey X [Y1 Y2 ...]` <sup>[1](#list-note-1)</sup>\n\n    A bit in `destkey` is set if it is set in `X` and also in one or more of `Y1, Y2, ...`.\n* `BITOP ONE destkey X1 [X2 X3 ...]` <sup>[1](#list-note-1)</sup>\n\n    A bit in `destkey` is set if it is set in exactly one of `X1, X2, ...`.\n\nThe result of each operation is always stored at `destkey`.\n\n1. <a name=\"list-note-1\"></a> Added in Redis 8.2."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>operation</code></summary>\n\nThe bitwise operation to perform: `AND`, `OR`, `XOR`, `NOT`, `DIFF`, `DIFF1`, `ANDOR`, or `ONE`. `NOT` accepts exactly one source key.\n\n</details>\n\n<details open><summary><code>destkey</code></summary>\n\nThe key to store the result in.\n\n</details>\n\n<details open><summary><code>key [key ...]</code></summary>\n\nOne or more source keys.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"1. Basic usage example using the `AND` operator:\n\n\nredis> BITFIELD key1 SET i8 #0 255\n1) (integer) 0\nredis> BITFIELD key2 SET i8 #0 85\n1) (integer) 0\nredis> BITOP AND dest key1 key2\n(integer) 1\nredis> BITFIELD dest GET i8 #0\n1) (integer) 85\n\n\n2. Suppose you want to expose people to a book-related ad. The target audience is people who love to read books and are interested in fantasy, adventure, or science fiction. Assume you have the following bitmaps:\n\n* `LRB` - people who love to read books.\n* `B:F` - people interested in fantasy.\n* `B:A` - people interested in adventure.\n* `B:SF` - people interested in science fiction.\n\nTo create a bitmap representing the target audience, use the following command:\n\n[code example]"},{"id":"details","title":"Details","role":"content","text":""},{"id":"handling-of-strings-with-different-lengths","title":"Handling of strings with different lengths","role":"content","text":"When an operation is performed between strings having different lengths, all the\nstrings shorter than the longest string in the set are treated as if they were\nzero-padded up to the length of the longest string.\n\nThe same holds true for non-existent keys, that are considered as a stream of\nzero bytes up to the length of the longest string."},{"id":"pattern-real-time-metrics-using-bitmaps","title":"Pattern: real time metrics using bitmaps","role":"content","text":"`BITOP` is a good complement to the pattern documented in the [`BITCOUNT`](https://redis.io/docs/latest/commands/bitcount) command\ndocumentation.\nDifferent bitmaps can be combined in order to obtain a target bitmap where\nthe population counting operation is performed.\n\nSee the article called \"[Fast easy realtime metrics using Redis\nbitmaps](http://blog.getspool.com/2011/11/29/fast-easy-realtime-metrics-using-redis-bitmaps)\" for an interesting use cases."},{"id":"performance-considerations","title":"Performance considerations","role":"performance","text":"`BITOP` is a potentially slow command as it runs in O(N) time.\nCare should be taken when running it against long input strings.\n\nFor real-time metrics and statistics involving large inputs a good approach is\nto use a replica (with replica-read-only option enabled) where the bit-wise\noperations are performed to avoid blocking the master instance."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the size of the string stored in the destination key is equal to the size of the longest input string.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the size of the string stored in the destination key is equal to the size of the longest input string."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"BITOP ANDOR TA LRB B:F B:A B:SF","section_id":"examples"}]}
{"schema_version":2,"id":"commands/bitpos","title":"BITPOS","url":"https://redis.io/docs/latest/commands/bitpos/","summary":"Finds the first set (1) or clear (0) bit in a string.","since":"2.8.7","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-16T18:59:00+05:30","page_type":"content","content_hash":"114049136f02438aeba6224675d381d998e06d5bc4f2f4ca2f3c8273b912d998","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Return the position of the first bit set to 1 or 0 in a string.\n\nThe position is returned, thinking of the string as an array of bits from left to\nright, where the first byte's most significant bit is at position 0, the second\nbyte's most significant bit is at position 8, and so forth.\n\nThe same bit position convention is followed by [`GETBIT`](https://redis.io/docs/latest/commands/getbit) and [`SETBIT`](https://redis.io/docs/latest/commands/setbit).\n\nBy default, all the bytes contained in the string are examined.\nIt is possible to look for bits only in a specified interval passing the additional arguments _start_ and _end_ (it is possible to just pass _start_, the operation will assume that the end is the last byte of the string. However there are semantic differences as explained later).\nBy default, the range is interpreted as a range of bytes and not a range of bits, so `start=0` and `end=2` means to look at the first three bytes.\n\nYou can use the optional `BIT` modifier to specify that the range should be interpreted as a range of bits.\nSo `start=0` and `end=2` means to look at the first three bits.\n\nNote that bit positions are returned always as absolute values starting from bit zero even when _start_ and _end_ are used to specify a range.\n\nLike for the [`GETRANGE`](https://redis.io/docs/latest/commands/getrange) command start and end can contain negative values in\norder to index bytes starting from the end of the string, where -1 is the last\nbyte, -2 is the penultimate, and so forth. When `BIT` is specified, -1 is the last\nbit, -2 is the penultimate, and so forth.\n\nIf the key does not exist, it is treated as an infinite array of zero bits: searching for a clear bit (0) returns 0, while searching for a set bit (1) returns -1. In contrast, an existing empty string (a key holding \"\") contains no bits at all, so searching for either 0 or 1 always returns -1."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the string.\n\n</details>\n\n<details open><summary><code>bit</code></summary>\n\nThe bit value to search for: `0` or `1`.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>start</code></summary>\n\nThe offset to start searching from. By default a byte offset; negative values count from the end of the string.\n\n</details>\n\n<details open><summary><code>end</code></summary>\n\nThe offset to stop searching at, inclusive.\n\n</details>\n\n<details open><summary><code>BYTE | BIT</code></summary>\n\nInterpret `start` and `end` as byte offsets (`BYTE`, the default) or bit offsets (`BIT`).\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): the position of the first bit set to 1 or 0 according to the request\n* [Integer reply](../../develop/reference/protocol-spec#integers): `-1`. In case the `bit` argument is 1 and the string is empty or composed of just zero bytes\nIf we look for set bits (the bit argument is 1) and the string is empty or composed of just zero bytes, -1 is returned.\nIf we look for clear bits (the bit argument is 0) and the string only contains bits set to 1, the function returns the first bit not part of the string on the right. So if the string is three bytes set to the value `0xff` the command `BITPOS key 0` will return 24, since up to bit 23 all the bits are 1.\nThe function considers the right of the string as padded with zeros if you look for clear bits and specify no range or the _start_ argument **only**.\nHowever, this behavior changes if you are looking for clear bits and specify a range with both _start_ and _end_.\nIf a clear bit isn't found in the specified range, the function returns -1 as the user specified a clear range and there are no 0 bits in that range.\n\n**RESP3:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): the position of the first bit set to 1 or 0 according to the request\n* [Integer reply](../../develop/reference/protocol-spec#integers): `-1`. In case the `bit` argument is 1 and the string is empty or composed of just zero bytes\nIf we look for set bits (the bit argument is 1) and the string is empty or composed of just zero bytes, -1 is returned.\nIf we look for clear bits (the bit argument is 0) and the string only contains bits set to 1, the function returns the first bit not part of the string on the right. So if the string is three bytes set to the value `0xff` the command `BITPOS key 0` will return 24, since up to bit 23 all the bits are 1.\nThe function considers the right of the string as padded with zeros if you look for clear bits and specify no range or the _start_ argument **only**.\nHowever, this behavior changes if you are looking for clear bits and specify a range with both _start_ and _end_.\nIf a clear bit isn't found in the specified range, the function returns -1 as the user specified a clear range and there are no 0 bits in that range."}],"examples":[{"id":"examples-ex0","language":"redis","code":"redis> SET mykey \"\\xff\\xf0\\x00\"\nOK\nredis> BITPOS mykey 0\n(integer) 12\nredis> SET mykey \"\\x00\\xff\\xf0\"\nOK\nredis> BITPOS mykey 1 0\n(integer) 8\nredis> BITPOS mykey 1 2\n(integer) 16\nredis> BITPOS mykey 1 2 -1 BYTE\n(integer) 16\nredis> BITPOS mykey 1 7 15 BIT\n(integer) 8\nredis> set mykey \"\\x00\\x00\\x00\"\nOK\nredis> BITPOS mykey 1\n(integer) -1\nredis> BITPOS mykey 1 7 -3 BIT\n(integer) -1","section_id":"examples"}]}
{"schema_version":2,"id":"commands/blmove","title":"BLMOVE","url":"https://redis.io/docs/latest/commands/blmove/","summary":"Pops an element from a list, pushes it to another list and returns it. Blocks until an element is available otherwise. Deletes the list if the last element was moved.","since":"6.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"d39c59a7d9d0e6e9768ae06e70945317d117479d71baa23f0404b59386c4f929","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\n`BLMOVE` is the blocking variant of [`LMOVE`](https://redis.io/docs/latest/commands/lmove).\nWhen `source` contains elements, this command behaves exactly like [`LMOVE`](https://redis.io/docs/latest/commands/lmove).\nWhen used inside a [`MULTI`](https://redis.io/docs/latest/commands/multi)/[`EXEC`](https://redis.io/docs/latest/commands/exec) block or a Lua script, this command behaves exactly like [`LMOVE`](https://redis.io/docs/latest/commands/lmove).\nWhen `source` is empty, Redis will block the connection until another client\npushes to it or until `timeout` (a double value specifying the maximum number of seconds to block) is reached.\nA `timeout` of zero can be used to block indefinitely.\n\nThis command comes in place of the now deprecated [`BRPOPLPUSH`](https://redis.io/docs/latest/commands/brpoplpush). Doing\n`BLMOVE RIGHT LEFT` is equivalent.\n\nSee [`LMOVE`](https://redis.io/docs/latest/commands/lmove) for more information."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>source</code></summary>\n\nThe key of the source list.\n\n</details>\n\n<details open><summary><code>destination</code></summary>\n\nThe key of the destination list.\n\n</details>\n\n<details open><summary><code>LEFT | RIGHT</code></summary>\n\nThe end of `source` to pop the element from: `LEFT` (head) or `RIGHT` (tail).\n\n</details>\n\n<details open><summary><code>LEFT | RIGHT</code></summary>\n\nThe end of `destination` to push the element to: `LEFT` (head) or `RIGHT` (tail).\n\n</details>\n\n<details open><summary><code>timeout</code></summary>\n\nThe maximum time to block, in seconds. A timeout of `0` blocks indefinitely.\n\n</details>"},{"id":"details","title":"Details","role":"content","text":""},{"id":"pattern-reliable-queue","title":"Pattern: reliable queue","role":"content","text":"Please see the pattern description in the [`LMOVE`](https://redis.io/docs/latest/commands/lmove) documentation."},{"id":"pattern-circular-list","title":"Pattern: circular list","role":"content","text":"Please see the pattern description in the [`LMOVE`](https://redis.io/docs/latest/commands/lmove) documentation."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the element being popped from the _source_ and pushed to the _destination_.\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): the operation timed-out\n\n**RESP3:**\n\nOne of the following:\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the element being popped from the _source_ and pushed to the _destination_.\n* [Null reply](../../develop/reference/protocol-spec#nulls): the operation timed-out"}],"examples":[]}
{"schema_version":2,"id":"commands/blmovem","title":"BLMOVEM","url":"https://redis.io/docs/latest/commands/blmovem/","summary":"Moves up to (or exactly) a number of elements from one list to another and returns them. Blocks until the elements are available otherwise. Deletes the source list if it becomes empty.","since":"8.10.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"9ec2337ee19a282aec03200ea94201b6139d33acca8bd2c96cb6250b52ccce4b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\n`BLMOVEM` is the blocking variant of [`LMOVEM`](https://redis.io/docs/latest/commands/lmovem).\nWhen `source` holds enough elements to satisfy the request, this command behaves\nexactly like [`LMOVEM`](https://redis.io/docs/latest/commands/lmovem). Otherwise, Redis blocks the connection until\nanother client pushes the required elements to `source` or until `timeout` is\nreached."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>source</code></summary>\n\nThe key of the source list.\n\n</details>\n\n<details open><summary><code>destination</code></summary>\n\nThe key of the destination list.\n\n</details>\n\n<details open><summary><code>LEFT | RIGHT</code></summary>\n\nThe end of `source` to pop elements from: `LEFT` (head) or `RIGHT` (tail).\n\n</details>\n\n<details open><summary><code>LEFT | RIGHT</code></summary>\n\nThe end of `destination` to push elements to: `LEFT` (head) or `RIGHT` (tail).\n\n</details>\n\n<details open><summary><code>timeout</code></summary>\n\nThe maximum time to block, in seconds. A timeout of `0` blocks indefinitely.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"The `how-many` block controls how many elements are moved and in what order.\nWhen you include it, you must provide both a selector (`COUNT` or `EXACTLY`)\nand an ordering (`OBO` or `BULK`).\n\n<details open><summary><code>COUNT count | EXACTLY exactly</code></summary>\n\nThe number of elements to move:\n\n- `COUNT count` moves up to `count` elements. If `source` holds fewer than\n  `count` elements, all of them are moved. This matches the `count` semantics of\n  [`LPOP`](https://redis.io/docs/latest/commands/lpop) and [`LMPOP`](https://redis.io/docs/latest/commands/lmpop).\n- `EXACTLY exactly` moves exactly `exactly` elements. If `source` holds fewer\n  than `exactly` elements, the command blocks (see [Blocking behavior](#blocking-behavior)).\n\n</details>\n\n<details open><summary><code>OBO | BULK</code></summary>\n\nThe order in which elements are pushed onto `destination`:\n\n- `OBO` (one by one) moves elements individually: each element is popped from\n  `source` and pushed to `destination` before the next one is moved.\n- `BULK` moves all of the elements at once, preserving their relative order.\n\nSee the [Element ordering](https://redis.io/docs/latest/commands/lmovem#element-ordering) section of the [`LMOVEM`](https://redis.io/docs/latest/commands/lmovem) documentation for details.\n\n</details>"},{"id":"details","title":"Details","role":"content","text":""},{"id":"blocking-behavior","title":"Blocking behavior","role":"content","text":"Whether `BLMOVEM` blocks depends on the selector:\n\n- With `COUNT`, the command blocks only while `source` is empty. As soon as at\n  least one element is available it unblocks, moves up to `count` elements, and\n  returns them.\n- With `EXACTLY`, the command blocks while `source` holds fewer than the\n  requested number of elements. It unblocks only once `source` grows to at least\n  that length, then moves exactly that many elements atomically.\n\nA `timeout` of `0` blocks indefinitely. When used inside a\n[`MULTI`](https://redis.io/docs/latest/commands/multi)/[`EXEC`](https://redis.io/docs/latest/commands/exec) block or a Lua script, `BLMOVEM` does not block; it\nbehaves like [`LMOVEM`](https://redis.io/docs/latest/commands/lmovem) and, when the request cannot be satisfied,\nreturns nil immediately."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Array reply](../../develop/reference/protocol-spec#arrays): the moved elements, in destination order.\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): the operation timed out.\n\n**RESP3:**\n\nOne of the following:\n* [Array reply](../../develop/reference/protocol-spec#arrays): the moved elements, in destination order.\n* [Null reply](../../develop/reference/protocol-spec#nulls): the operation timed out."},{"id":"see-also","title":"See also","role":"related","text":"[`BLMOVE`](https://redis.io/docs/latest/commands/blmove) | [`LMOVEM`](https://redis.io/docs/latest/commands/lmovem) | [`BLMPOP`](https://redis.io/docs/latest/commands/blmpop) | [`BRPOPLPUSH`](https://redis.io/docs/latest/commands/brpoplpush)"},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Redis lists](https://redis.io/docs/latest/develop/data-types/lists)"}],"examples":[]}
{"schema_version":2,"id":"commands/blmpop","title":"BLMPOP","url":"https://redis.io/docs/latest/commands/blmpop/","summary":"Pops the first element from one of multiple lists. Blocks until an element is available otherwise. Deletes the list if the last element was popped.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"7d3d3c1b80b816d36a73dab6c06f7352bf42521595558edec65411b5229e2dfa","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\n`BLMPOP` is the blocking variant of [`LMPOP`](https://redis.io/docs/latest/commands/lmpop).\n\nWhen any of the lists contains elements, this command behaves exactly like [`LMPOP`](https://redis.io/docs/latest/commands/lmpop).\nWhen used inside a [`MULTI`](https://redis.io/docs/latest/commands/multi)/[`EXEC`](https://redis.io/docs/latest/commands/exec) block or a Lua script, this command behaves exactly like [`LMPOP`](https://redis.io/docs/latest/commands/lmpop).\nWhen all lists are empty, Redis will block the connection until another client pushes to it or until the `timeout` (a double value specifying the maximum number of seconds to block) elapses.\nA `timeout` of zero can be used to block indefinitely.\n\nSee [`LMPOP`](https://redis.io/docs/latest/commands/lmpop) for more information."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>timeout</code></summary>\n\nThe maximum time to block, in seconds. A timeout of `0` blocks indefinitely.\n\n</details>\n\n<details open><summary><code>numkeys</code></summary>\n\nThe number of keys that follow.\n\n</details>\n\n<details open><summary><code>key [key ...]</code></summary>\n\nOne or more keys of lists to operate on.\n\n</details>\n\n<details open><summary><code>LEFT | RIGHT</code></summary>\n\nThe end of the list to pop elements from: `LEFT` (head) or `RIGHT` (tail).\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>COUNT count</code></summary>\n\nThe number of elements to pop. Defaults to 1.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): when no element could be popped and the _timeout_ is reached.\n* [Array reply](../../develop/reference/protocol-spec#arrays): a two-element array with the first element being the name of the key from which elements were popped, and the second element being an array of the popped elements.\n\n**RESP3:**\n\nOne of the following:\n* [Null reply](../../develop/reference/protocol-spec#nulls): when no element could be popped and the _timeout_ is reached.\n* [Array reply](../../develop/reference/protocol-spec#arrays): a two-element array with the first element being the name of the key from which elements were popped, and the second element being an array of the popped elements."}],"examples":[]}
{"schema_version":2,"id":"commands/blpop","title":"BLPOP","url":"https://redis.io/docs/latest/commands/blpop/","summary":"Removes and returns the first element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"d453f9e768247bdb1cf3d07229c94dc4107f205ae1ddbb6717023f19658a79d4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\n`BLPOP` is a blocking list pop primitive.\nIt is the blocking version of [`LPOP`](https://redis.io/docs/latest/commands/lpop) because it blocks the connection when there\nare no elements to pop from any of the given lists.\nAn element is popped from the head of the first list that is non-empty, with the\ngiven keys being checked in the order that they are given."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key [key ...]</code></summary>\n\nOne or more keys of lists to operate on.\n\n</details>\n\n<details open><summary><code>timeout</code></summary>\n\nThe maximum time to block, in seconds. A timeout of `0` blocks indefinitely.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"details","title":"Details","role":"content","text":""},{"id":"non-blocking-behavior","title":"Non-blocking behavior","role":"content","text":"When `BLPOP` is called, if at least one of the specified keys contains a\nnon-empty list, an element is popped from the head of the list and returned to\nthe caller together with the `key` it was popped from.\n\nKeys are checked in the order that they are given.\nLet's say that the key `list1` doesn't exist and `list2` and `list3` hold\nnon-empty lists.\nConsider the following command:\n\n[code example]\n\n`BLPOP` guarantees to return an element from the list stored at `list2` (since\nit is the first non empty list when checking `list1`, `list2` and `list3` in\nthat order)."},{"id":"blocking-behavior","title":"Blocking behavior","role":"content","text":"If none of the specified keys exist, `BLPOP` blocks the connection until another\nclient performs an [`LPUSH`](https://redis.io/docs/latest/commands/lpush) or [`RPUSH`](https://redis.io/docs/latest/commands/rpush) operation against one of the keys.\n\nOnce new data is present on one of the lists, the client returns with the name\nof the key unblocking it and the popped value.\n\nWhen `BLPOP` causes a client to block and a non-zero timeout is specified,\nthe client will unblock returning a `nil` multi-bulk value when the specified\ntimeout has expired without a push operation against at least one of the\nspecified keys.\n\n**The timeout argument is interpreted as a double value specifying the maximum number of seconds to block**. A timeout of zero can be used to block indefinitely."},{"id":"what-key-is-served-first-what-client-what-element-priority-ordering-details","title":"What key is served first? What client? What element? Priority ordering details.","role":"content","text":"* If the client tries to block for multiple keys, but at least one key contains elements, the returned key/element pair is the first key from left to right that has one or more elements. In this case, the client is not blocked. For example, `BLPOP key1 key2 key3 key4 0`, assuming that both `key2` and `key4` are non-empty, will always return an element from `key2`.\n\n* If multiple clients are blocked for the same key, the first client to be served is the one that has been waiting the longest (the first that blocked for the key). Once a client is unblocked it does not retain any priority; when it blocks again with another `BLPOP` call, it will be served according to its new position in the queue.\n\n* When a client is blocking for multiple keys and multiple keys become non-empty as a result of the same command, transaction, or Lua script, the client is served according to the **order of keys in its `BLPOP` call**, not the order of the write operations. After the writes occur, Redis reprocesses the blocking command for the client and pops from the **first non-empty key** in the key list.  \nFor example, a client blocked on `BLPOP key1 key2 0` will pop from `key1` if both `key1` and `key2` receive elements during the same `MULTI`/`EXEC` transaction, because `key1` appears first.\n\nInternally, when a write makes progress possible for a blocked client, Redis marks that client as unblocked (adding it to an internal queue). No reply is sent immediately. Before the server goes idle (in the `beforeSleep()` phase), Redis rechecks the blocking command and serves the client based on the first non-empty key in its provided key list.\n\n#### Example\n\n**Client A**\n\n[code example]\n\n**Client B**\n\n[code example]\n\nAlthough `key2` is pushed first inside the transaction, Redis reprocesses the blocking command for Client A after the transaction completes.  \nAt that time both lists are non-empty, and `BLPOP` pops from the first non-empty key in its argument list (`key1`):\n\n[code example]\n\nTo make Redis pop from `key2` instead, reverse the order of the keys in the blocking call:\n\n[code example]"},{"id":"behavior-of-blpop-when-multiple-elements-are-pushed-inside-a-list","title":"Behavior of `BLPOP` when multiple elements are pushed inside a list.","role":"content","text":"There are times when a list can receive multiple elements in the context of the same conceptual command:\n\n* Variadic push operations such as `LPUSH mylist a b c`.\n* After an [`EXEC`](https://redis.io/docs/latest/commands/exec) of a [`MULTI`](https://redis.io/docs/latest/commands/multi) block with multiple push operations against the same list.\n* Executing a Lua Script with Redis 2.6 or newer.\n\nWhen multiple elements are pushed inside a list where there are clients blocking, the behavior is different for Redis 2.4 and Redis 2.6 or newer.\n\nFor Redis 2.6 what happens is that the command performing multiple pushes is executed, and *only after* the execution of the command the blocked clients are served. Consider this sequence of commands.\n\n    Client A:   BLPOP foo 0\n    Client B:   LPUSH foo a b c\n\nIf the above condition happens using a Redis 2.6 server or greater, Client **A** will be served with the `c` element, because after the [`LPUSH`](https://redis.io/docs/latest/commands/lpush) command the list contains `c,b,a`, so taking an element from the left means to return `c`.\n\nInstead Redis 2.4 works in a different way: clients are served *in the context* of the push operation, so as long as `LPUSH foo a b c` starts pushing the first element to the list, it will be delivered to the Client **A**, that will receive `a` (the first element pushed).\n\nThe behavior of Redis 2.4 creates a lot of problems when replicating or persisting data into the AOF file, so the much more generic and semantically simpler behavior was introduced into Redis 2.6 to prevent problems.\n\nNote that for the same reason a Lua script or a `MULTI/EXEC` block may push elements into a list and afterward **delete the list**. In this case the blocked clients will not be served at all and will continue to be blocked as long as no data is present on the list after the execution of a single command, transaction, or script."},{"id":"blpop-inside-a-multi--exec-transaction","title":"`BLPOP` inside a `MULTI` / `EXEC` transaction","role":"content","text":"`BLPOP` can be used with pipelining (sending multiple commands and\nreading the replies in batch), however this setup makes sense almost solely\nwhen it is the last command of the pipeline.\n\nUsing `BLPOP` inside a [`MULTI`](https://redis.io/docs/latest/commands/multi) / [`EXEC`](https://redis.io/docs/latest/commands/exec) block does not make a lot of sense\nas it would require blocking the entire server in order to execute the block\natomically, which in turn does not allow other clients to perform a push\noperation. For this reason the behavior of `BLPOP` inside [`MULTI`](https://redis.io/docs/latest/commands/multi) / [`EXEC`](https://redis.io/docs/latest/commands/exec) when the list is empty is to return a `nil` multi-bulk reply, which is the same\nthing that happens when the timeout is reached.\n\nIf you like science fiction, think of time flowing at infinite speed inside a\n[`MULTI`](https://redis.io/docs/latest/commands/multi) / [`EXEC`](https://redis.io/docs/latest/commands/exec) block..."},{"id":"reliable-queues","title":"Reliable queues","role":"content","text":"When `BLPOP` returns an element to the client, it also removes the element from the list. This means that the element only exists in the context of the client: if the client crashes while processing the returned element, it is lost forever.\n\nThis can be a problem with some application where we want a more reliable messaging system. When this is the case, please check the [`BRPOPLPUSH`](https://redis.io/docs/latest/commands/brpoplpush) command, that is a variant of `BLPOP` that adds the returned element to a target list before returning it to the client."},{"id":"pattern-event-notification","title":"Pattern: event notification","role":"content","text":"Using blocking list operations it is possible to mount different blocking\nprimitives.\nFor instance for some application you may need to block waiting for elements\ninto a Redis Set, so that as far as a new element is added to the Set, it is\npossible to retrieve it without resort to polling.\nThis would require a blocking version of [`SPOP`](https://redis.io/docs/latest/commands/spop) that is not available, but using\nblocking list operations we can easily accomplish this task.\n\nThe consumer will do:\n\n[code example]\n\nWhile in the producer side we'll use simply:\n\n[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): no element could be popped and the timeout expired\n* [Array reply](../../develop/reference/protocol-spec#arrays): the key from which the element was popped and the value of the popped element.\n\n**RESP3:**\n\nOne of the following:\n* [Null reply](../../develop/reference/protocol-spec#nulls): no element could be popped and the timeout expired\n* [Array reply](../../develop/reference/protocol-spec#arrays): the key from which the element was popped and the value of the popped element."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> DEL list1 list2\n(integer) 0\nredis> RPUSH list1 a b c\n(integer) 3\nredis> BLPOP list1 list2 0\n1) \"list1\"\n2) \"a\"","section_id":"examples"},{"id":"non-blocking-behavior-ex0","language":"plaintext","code":"BLPOP list1 list2 list3 0","section_id":"non-blocking-behavior"},{"id":"what-key-is-served-first-what-client-what-element-priority-ordering-details-ex0","language":"plaintext","code":"BLPOP key1 key2 0","section_id":"what-key-is-served-first-what-client-what-element-priority-ordering-details"},{"id":"what-key-is-served-first-what-client-what-element-priority-ordering-details-ex1","language":"plaintext","code":"MULTI\nRPUSH key2 1 2 3 4\nRPUSH key1 5 6 7\nEXEC","section_id":"what-key-is-served-first-what-client-what-element-priority-ordering-details"},{"id":"what-key-is-served-first-what-client-what-element-priority-ordering-details-ex2","language":"plaintext","code":"\"key1\"\n\"5\"","section_id":"what-key-is-served-first-what-client-what-element-priority-ordering-details"},{"id":"what-key-is-served-first-what-client-what-element-priority-ordering-details-ex3","language":"plaintext","code":"BLPOP key2 key1 0","section_id":"what-key-is-served-first-what-client-what-element-priority-ordering-details"},{"id":"pattern-event-notification-ex0","language":"plaintext","code":"LOOP forever\n    WHILE SPOP(key) returns elements\n        ... process elements ...\n    END\n    BRPOP helper_key\nEND","section_id":"pattern-event-notification"},{"id":"pattern-event-notification-ex1","language":"plaintext","code":"MULTI\nSADD key element\nLPUSH helper_key x\nEXEC","section_id":"pattern-event-notification"}]}
{"schema_version":2,"id":"commands/brpop","title":"BRPOP","url":"https://redis.io/docs/latest/commands/brpop/","summary":"Removes and returns the last element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"25ac4abd958600f5ab030f30e2d4176934d268ba5786e7d4e8de29919fad2fcc","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\n`BRPOP` is a blocking list pop primitive.\nIt is the blocking version of [`RPOP`](https://redis.io/docs/latest/commands/rpop) because it blocks the connection when there\nare no elements to pop from any of the given lists.\nAn element is popped from the tail of the first list that is non-empty, with the\ngiven keys being checked in the order that they are given.\n\nSee the [BLPOP documentation](https://redis.io/docs/latest/commands/blpop) for the exact semantics, since `BRPOP` is\nidentical to [`BLPOP`](https://redis.io/docs/latest/commands/blpop) with the only difference being that it pops elements from\nthe tail of a list instead of popping from the head."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key [key ...]</code></summary>\n\nOne or more keys of lists to operate on.\n\n</details>\n\n<details open><summary><code>timeout</code></summary>\n\nThe maximum time to block, in seconds. A timeout of `0` blocks indefinitely.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): no element could be popped and the timeout expired.\n* [Array reply](../../develop/reference/protocol-spec#arrays): the key from which the element was popped and the value of the popped element\n\n**RESP3:**\n\nOne of the following:\n* [Null reply](../../develop/reference/protocol-spec#nulls): no element could be popped and the timeout expired.\n* [Array reply](../../develop/reference/protocol-spec#arrays): the key from which the element was popped and the value of the popped element"}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> DEL list1 list2\n(integer) 0\nredis> RPUSH list1 a b c\n(integer) 3\nredis> BRPOP list1 list2 0\n1) \"list1\"\n2) \"c\"","section_id":"examples"}]}
{"schema_version":2,"id":"commands/brpoplpush","title":"BRPOPLPUSH","url":"https://redis.io/docs/latest/commands/brpoplpush/","summary":"Pops an element from a list, pushes it to another list and returns it. Block until an element is available otherwise. Deletes the list if the last element was popped.","since":"2.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"85629e9a0a1505b3873be4488ea3d4b9d43df68bd06f6b127947209b1f3bc728","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\n`BRPOPLPUSH` is the blocking variant of [`RPOPLPUSH`](https://redis.io/docs/latest/commands/rpoplpush).\nWhen `source` contains elements, this command behaves exactly like [`RPOPLPUSH`](https://redis.io/docs/latest/commands/rpoplpush).\nWhen used inside a [`MULTI`](https://redis.io/docs/latest/commands/multi)/[`EXEC`](https://redis.io/docs/latest/commands/exec) block or a Lua script, this command behaves exactly like [`RPOPLPUSH`](https://redis.io/docs/latest/commands/rpoplpush).\nWhen `source` is empty, Redis will block the connection until another client\npushes to it or until `timeout` is reached.\nA `timeout` of zero can be used to block indefinitely.\n\nSee [`RPOPLPUSH`](https://redis.io/docs/latest/commands/rpoplpush) for more information."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>source</code></summary>\n\nThe key of the source list.\n\n</details>\n\n<details open><summary><code>destination</code></summary>\n\nThe key of the destination list.\n\n</details>\n\n<details open><summary><code>timeout</code></summary>\n\nThe maximum time to block, in seconds. A timeout of `0` blocks indefinitely.\n\n</details>"},{"id":"details","title":"Details","role":"content","text":""},{"id":"pattern-reliable-queue","title":"Pattern: reliable queue","role":"content","text":"Please see the pattern description in the [`RPOPLPUSH`](https://redis.io/docs/latest/commands/rpoplpush) documentation."},{"id":"pattern-circular-list","title":"Pattern: circular list","role":"content","text":"Please see the pattern description in the [`RPOPLPUSH`](https://redis.io/docs/latest/commands/rpoplpush) documentation."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Deprecated as of Redis v6.2.0. |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the element being popped from _source_ and pushed to _destination_.\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): the timeout is reached.\n\n**RESP3:**\n\nOne of the following:\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the element being popped from _source_ and pushed to _destination_.\n* [Null reply](../../develop/reference/protocol-spec#nulls): the timeout is reached."}],"examples":[]}
{"schema_version":2,"id":"commands/bzmpop","title":"BZMPOP","url":"https://redis.io/docs/latest/commands/bzmpop/","summary":"Removes and returns a member by score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:43+01:00","page_type":"content","content_hash":"e6b35fbe47db65e4d4cb1253cfe904ec4afcd6c7bb10c464776d0f91ada5f8fc","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\n`BZMPOP` is the blocking variant of [`ZMPOP`](https://redis.io/docs/latest/commands/zmpop).\n\nWhen any of the sorted sets contains elements, this command behaves exactly like [`ZMPOP`](https://redis.io/docs/latest/commands/zmpop).\nWhen used inside a [`MULTI`](https://redis.io/docs/latest/commands/multi)/[`EXEC`](https://redis.io/docs/latest/commands/exec) block or a Lua script, this command behaves exactly like [`ZMPOP`](https://redis.io/docs/latest/commands/zmpop).\nWhen all sorted sets are empty, Redis will block the connection until another client adds members to one of the keys or until the `timeout` (a double value specifying the maximum number of seconds to block) elapses.\nA `timeout` of zero can be used to block indefinitely.\n\nSee [`ZMPOP`](https://redis.io/docs/latest/commands/zmpop) for more information."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>timeout</code></summary>\n\nThe maximum time to block, in seconds. A timeout of `0` blocks indefinitely.\n\n</details>\n\n<details open><summary><code>numkeys</code></summary>\n\nThe number of keys that follow.\n\n</details>\n\n<details open><summary><code>key [key ...]</code></summary>\n\nOne or more sorted-set keys to pop from.\n\n</details>\n\n<details open><summary><code>MIN | MAX</code></summary>\n\nPop the members with the lowest scores (`MIN`) or the highest scores (`MAX`).\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>COUNT count</code></summary>\n\nThe number of members to pop. Defaults to 1.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): when no element could be popped.\n* [Array reply](../../develop/reference/protocol-spec#arrays): a two-element array with the first element being the name of the key from which elements were popped, and the second element is an array of the popped elements. Every entry in the elements array is also an array that contains the member and its score.\n\n**RESP3:**\n\nOne of the following:\n* [Null reply](../../develop/reference/protocol-spec#nulls): when no element could be popped.\n* [Array reply](../../develop/reference/protocol-spec#arrays): a two-element array with the first element being the name of the key from which elements were popped, and the second element is an array of the popped elements. Every entry in the elements array is also an array that contains the member and its score."}],"examples":[]}
{"schema_version":2,"id":"commands/bzpopmax","title":"BZPOPMAX","url":"https://redis.io/docs/latest/commands/bzpopmax/","summary":"Removes and returns the member with the highest score from one or more sorted sets. Blocks until a member available otherwise. Deletes the sorted set if the last element was popped.","since":"5.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:43+01:00","page_type":"content","content_hash":"80a6d1b579902ae2e9ee9017cfb42c0fcb55d45301db3d70915d40ba03cecc36","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\n`BZPOPMAX` is the blocking variant of the sorted set [`ZPOPMAX`](https://redis.io/docs/latest/commands/zpopmax) primitive.\n\nIt is the blocking version because it blocks the connection when there are no\nmembers to pop from any of the given sorted sets.\nA member with the highest score is popped from first sorted set that is\nnon-empty, with the given keys being checked in the order that they are given.\n\nThe `timeout` argument is interpreted as a double value specifying the maximum\nnumber of seconds to block. A timeout of zero can be used to block indefinitely.\n\nSee the [BZPOPMIN documentation](https://redis.io/docs/latest/commands/bzpopmin) for the exact semantics, since `BZPOPMAX`\nis identical to [`BZPOPMIN`](https://redis.io/docs/latest/commands/bzpopmin) with the only difference being that it pops members\nwith the highest scores instead of popping the ones with the lowest scores."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key [key ...]</code></summary>\n\nOne or more sorted-set keys to pop from.\n\n</details>\n\n<details open><summary><code>timeout</code></summary>\n\nThe maximum time to block, in seconds. A timeout of `0` blocks indefinitely.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): when no element could be popped and the _timeout_ expired.\n* [Array reply](../../develop/reference/protocol-spec#arrays): the keyname, popped member, and its score.\n\n**RESP3:**\n\nOne of the following:\n* [Null reply](../../develop/reference/protocol-spec#nulls): when no element could be popped and the _timeout_ expired.\n* [Array reply](../../develop/reference/protocol-spec#arrays): the keyname, popped member, and its score."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> DEL zset1 zset2\n(integer) 0\nredis> ZADD zset1 0 a 1 b 2 c\n(integer) 3\nredis> BZPOPMAX zset1 zset2 0\n1) \"zset1\"\n2) \"c\"\n3) \"2\"","section_id":"examples"}]}
{"schema_version":2,"id":"commands/bzpopmin","title":"BZPOPMIN","url":"https://redis.io/docs/latest/commands/bzpopmin/","summary":"Removes and returns the member with the lowest score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.","since":"5.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:43+01:00","page_type":"content","content_hash":"790e957d223c8974738b183cc60d905a13a01e7761e2063db44969d3535ef680","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\n`BZPOPMIN` is the blocking variant of the sorted set [`ZPOPMIN`](https://redis.io/docs/latest/commands/zpopmin) primitive.\n\nIt is the blocking version because it blocks the connection when there are no\nmembers to pop from any of the given sorted sets.\nA member with the lowest score is popped from first sorted set that is\nnon-empty, with the given keys being checked in the order that they are given.\n\nThe `timeout` argument is interpreted as a double value specifying the maximum\nnumber of seconds to block. A timeout of zero can be used to block indefinitely.\n\nSee the [BLPOP documentation](https://redis.io/docs/latest/commands/blpop) for the exact semantics, since `BZPOPMIN` is\nidentical to [`BLPOP`](https://redis.io/docs/latest/commands/blpop) with the only difference being the data structure being\npopped from."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key [key ...]</code></summary>\n\nOne or more sorted-set keys to pop from.\n\n</details>\n\n<details open><summary><code>timeout</code></summary>\n\nThe maximum time to block, in seconds. A timeout of `0` blocks indefinitely.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): when no element could be popped and the _timeout_ expired.\n* [Array reply](../../develop/reference/protocol-spec#arrays): the keyname, popped member, and its score.\n\n**RESP3:**\n\nOne of the following:\n* [Null reply](../../develop/reference/protocol-spec#nulls): when no element could be popped and the _timeout_ expired.\n* [Array reply](../../develop/reference/protocol-spec#arrays): the keyname, popped member, and its score."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> DEL zset1 zset2\n(integer) 0\nredis> ZADD zset1 0 a 1 b 2 c\n(integer) 3\nredis> BZPOPMIN zset1 zset2 0\n1) \"zset1\"\n2) \"a\"\n3) \"0\"","section_id":"examples"}]}
{"schema_version":2,"id":"commands/cf.add","title":"CF.ADD","url":"https://redis.io/docs/latest/commands/cf.add/","summary":"Adds an item to a Cuckoo Filter","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"1544c91230b34e2d9a5643d1deb82bdc51990023912526db1fe850cfb2e49e7f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Adds an item to the cuckoo filter.\n\nCuckoo filters can contain the same item multiple times, and consider each addition as separate.\nUse [`CF.ADDNX`](https://redis.io/docs/latest/commands/cf.addnx) to add an item only if it does not exist."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nis key name for a cuckoo filter to add the item to.\n\nIf `key` does not exist - a new cuckoo filter is created.\n</details>\n\n<details open><summary><code>item</code></summary>\n\nis an item to add.\n</details>"},{"id":"complexity","title":"Complexity","role":"performance","text":"O(n + i), where n is the number of `sub-filters` and i is `maxIterations`.\nAdding items requires up to 2 memory accesses per `sub-filter`.\nBut as the filter fills up, both locations for an item might be full.\nThe filter attempts to `Cuckoo` swap items up to `maxIterations` times."},{"id":"examples","title":"Examples","role":"example","text":"redis> CF.ADD cf item1\n(integer) 1\nredis> CF.ADD cf item1\n(integer) 1"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\"><nobr>&#x2705; Flexible & Annual</nobr></span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): `1` for successfully adding an item to the filter.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, wrong key type, or when the filter is full.\n\n**RESP3:**\n\nOne of the following:\n* [Boolean reply](https://redis.io/docs/latest/develop/reference/protocol-spec#booleans): `true` for successfully adding an item to the filter.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, wrong key type, or when the filter is full."}],"examples":[]}
{"schema_version":2,"id":"commands/cf.addnx","title":"CF.ADDNX","url":"https://redis.io/docs/latest/commands/cf.addnx/","summary":"Adds an item to a Cuckoo Filter if the item did not exist previously.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"88061854590ba25257353d5a627c5064e6c20f0abc31328a114acac9dcce8cf1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Adds an item to a cuckoo filter if the item does not exist.\n\nThis command is similar to the combination of [`CF.EXISTS`](https://redis.io/docs/latest/commands/cf.exists) and [`CF.ADD`](https://redis.io/docs/latest/commands/cf.add). It does not add an item into the filter if its fingerprint already exists.\n\n<note><b>Notes:</b>\n\n- This command is slower than [`CF.ADD`](https://redis.io/docs/latest/commands/cf.add) because it first checks whether the item exists.\n- Since [`CF.EXISTS`](https://redis.io/docs/latest/commands/cf.exists) can result in false positive, `CF.ADDNX` may not add an item because it is supposedly already exist, which may be wrong.\n\n</note>"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nis key name for a cuckoo filter to add items to.\n\nIf `key` does not exist - a new cuckoo filter is created.\n</details>\n\n<details open><summary><code>item</code></summary>\n\nis an item to add.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> CF.ADDNX cf item\n(integer) 1\nredis> CF.ADDNX cf item\n(integer) 0"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): `1` for successfully adding an item to the filter or `0` if the item's fingerprint already exists in the filter.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, wrong key type, or when the filter is full.\n\n**RESP3:**\n\nOne of the following:\n* [Boolean reply](https://redis.io/docs/latest/develop/reference/protocol-spec#booleans): `true` for successfully adding an item to the filter or `false` if the item's fingerprint already exists in the filter.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, wrong key type, or when the filter is full."}],"examples":[]}
{"schema_version":2,"id":"commands/cf.count","title":"CF.COUNT","url":"https://redis.io/docs/latest/commands/cf.count/","summary":"Return the number of times an item might be in a Cuckoo Filter","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"963496fd5893c1c0fa3394c0fbeba1ff216f95926171508fbc129e610389f71e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns an estimation of the number of times a given item was added to a cuckoo filter.\n\nIf you just want to check that a given item was added to a cuckoo filter, use [`CF.EXISTS`](https://redis.io/docs/latest/commands/cf.exists)."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nis key name for a cuckoo filter.\n\n</details>\n\n<details open><summary><code>item</code></summary>\n\nis an item to check.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> CF.INSERT cf ITEMS item1 item2 item2\n1) (integer) 1\n2) (integer) 1\n3) (integer) 1\nredis> CF.COUNT cf item1\n(integer) 1\nredis> CF.COUNT cf item2\n(integer) 2"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers), where a positive value is an estimation of the number of times `item` was added to the filter. An overestimation is possible, but not an underestimation. `0` means that `key` does not exist or that `item` had not been added to the filter. See the note in [`CF.DEL`](https://redis.io/docs/latest/commands/cf.del).\n* [Simple error](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments or wrong key type.\n\n**RESP3:**\n\nOne of the following:\n* [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers), where a positive value is an estimation of the number of times `item` was added to the filter. An overestimation is possible, but not an underestimation. `0` means that `key` does not exist or that `item` had not been added to the filter. See the note in [`CF.DEL`](https://redis.io/docs/latest/commands/cf.del).\n* [Simple error](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments or wrong key type."}],"examples":[]}
{"schema_version":2,"id":"commands/cf.del","title":"CF.DEL","url":"https://redis.io/docs/latest/commands/cf.del/","summary":"Deletes an item from a Cuckoo Filter","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"59201add82c025dfd92093e4b48d8db2bcd1ee296e0fee2ae84fe0ed545af014","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Deletes an item once from the filter.\n\nIf the item exists only once, it will be removed from the filter. If the item was added multiple times, it will still be present.\n\n> [!NOTE]\n> Never use this command to delete an item unless you are certain you've added the item to the filter. Deleting an item you didn't previously add may corrupt the filter and cause false negatives."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nis key name for a cuckoo filter.\n</details>\n\n<details open><summary><code>item</code></summary>\n\nis an item to delete.\n</details>"},{"id":"complexity","title":"Complexity","role":"performance","text":"O(n), where n is the number of `sub-filters`. Both alternative locations are\nchecked on all `sub-filters`."},{"id":"examples","title":"Examples","role":"example","text":"redis> CF.INSERT cf ITEMS item1 item2 item2\n1) (integer) 1\n2) (integer) 1\n3) (integer) 1\nredis> CF.DEL cf item1\n(integer) 1\nredis> CF.DEL cf item1\n(integer) 0\nredis> CF.DEL cf item2\n(integer) 1\nredis> CF.DEL cf item2\n(integer) 1\nredis> CF.DEL cf item2\n(integer) 0"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) `1` for successfully deleting an item, or `0` if no such item was found in the filter.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments or wrong key type.\n\n**RESP3:**\n\nOne of the following:\n* [Boolean reply](https://redis.io/docs/latest/develop/reference/protocol-spec#booleans) `true` for successfully deleting an item, or `false` if no such item was found in the filter.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments or wrong key type."}],"examples":[]}
{"schema_version":2,"id":"commands/cf.exists","title":"CF.EXISTS","url":"https://redis.io/docs/latest/commands/cf.exists/","summary":"Checks whether one or more items exist in a Cuckoo Filter","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"51d5952ea933edc172804dbafb3f474811ba31ec302407ecee68749001aa4dff","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Determines whether a given item was added to a cuckoo filter.\n\nThis command is similar to [`CF.MEXISTS`](https://redis.io/docs/latest/commands/cf.mexists), except that only one item can be checked."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nis key name for a cuckoo filter.\n\n</details>\n\n<details open><summary><code>item</code></summary>\n\nis an item to check.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> CF.ADD cf item1\n(integer) 1\nredis> CF.EXISTS cf item1\n(integer) 1\nredis> CF.EXISTS cf item2\n(integer) 0"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers), where a value of `1` means the item was probably already added to the filter. A value of `0` means the item was definitely not added, `key` does not exist, or `key` contains a value of the wrong type.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if invalid arguments are passed.\n\n**RESP3:**\n\nOne of the following:\n* [Boolean reply](https://redis.io/docs/latest/develop/reference/protocol-spec#booleans), where a value of `true` means the item was probably already added to the filter. A value of `false` means the item was definitely not added, `key` does not exist, or `key` contains a value of the wrong type.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if invalid arguments are passed."}],"examples":[]}
{"schema_version":2,"id":"commands/cf.info","title":"CF.INFO","url":"https://redis.io/docs/latest/commands/cf.info/","summary":"Returns information about a Cuckoo Filter","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"3252738c1fc7459a77b43a6e9903e4e92b53a975f55bf7ad51f3c68ede9cc618","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns information about a cuckoo filter."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nis key name for a cuckoo filter.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> CF.ADD cf item1\n(integer) 1\nredis> CF.INFO cf\n 1) Size\n 2) (integer) 1080\n 3) Number of buckets\n 4) (integer) 512\n 5) Number of filters\n 6) (integer) 1\n 7) Number of items inserted\n 8) (integer) 1\n 9) Number of items deleted\n10) (integer) 0\n11) Bucket size\n12) (integer) 2\n13) Expansion rate\n14) (integer) 1\n15) Max iterations\n16) (integer) 20"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"One of the following:\n\n**RESP2:**\n\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) with argument name ([Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings)) and value ([Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers)) pairs.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if invalid arguments are passed, `key` does not exist, or `key` is not of the correct type.\n\n**RESP3:**\n\n* [Map reply](https://redis.io/docs/latest/develop/reference/protocol-spec#maps) with argument name ([Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings)) and value ([Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers)) pairs.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if invalid arguments are passed, `key` does not exist, or `key` is not of the correct type."}],"examples":[]}
{"schema_version":2,"id":"commands/cf.insert","title":"CF.INSERT","url":"https://redis.io/docs/latest/commands/cf.insert/","summary":"Adds one or more items to a Cuckoo Filter. A filter will be created if it does not exist","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"00f953d17e1e38830fe15eb7d69e48afad1a75385685727e2390993b524c8226","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Adds one or more items to a cuckoo filter, allowing the filter to be created with a custom capacity if it does not exist yet.\n\nThis command is similar to [`CF.ADD`](https://redis.io/docs/latest/commands/cf.add), except that more than one item can be added and capacity can be specified."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nis key name for a cuckoo filter to add items to.\n\nIf `key` does not exist - a new cuckoo filter is created.\n</details>\n\n<details open><summary><code>ITEMS item...</code></summary>\n\nOne or more items to add.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>CAPACITY capacity</code></summary>\n    \nSpecifies the desired capacity of the new filter, if this filter does not exist yet.\n    \nIf the filter already exists, then this parameter is ignored.\n    \nIf the filter does not exist yet and this parameter is *not* specified, then the filter is created with the module-level default capacity which is 1024.\n\nSee [`CF.RESERVE`](https://redis.io/docs/latest/commands/cf.reserve) for more information on cuckoo filter capacities.\n</details>\n    \n<details open><summary><code>NOCREATE</code></summary>\n  \nIf specified, prevents automatic filter creation if the filter does not exist (Instead, an error is returned).\n    \nThis option is mutually exclusive with `CAPACITY`.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> CF.INSERT cf CAPACITY 1000 ITEMS item1 item2 \n1) (integer) 1\n2) (integer) 1\n\n\n\nredis> CF.INSERT cf1 CAPACITY 1000 NOCREATE ITEMS item1 item2 \n(error) ERR not found\n\n\n\nredis> CF.RESERVE cf2 2 BUCKETSIZE 1 EXPANSION 0\nOK\nredis> CF.INSERT cf2 ITEMS 1 1 1 1\n1) (integer) 1\n2) (integer) 1\n3) (integer) -1\n4) (integer) -1"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays), where each element is an [integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): `1` when the item is added successfully, or `-1` when the item cannot be added because the filter is full.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) when the number of arguments or key type is incorrect, and also when `NOCREATE` is specified and `key` does not exist.\n\n**RESP3:**\n\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays), where each element is either a [boolean reply](https://redis.io/docs/latest/develop/reference/protocol-spec#booleans) of `true` for successfully adding an item, or an [integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) of `-1` when the item cannot be added because the filter is full.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) when the number of arguments or key type is incorrect, and also when `NOCREATE` is specified and `key` does not exist."}],"examples":[]}
{"schema_version":2,"id":"commands/cf.insertnx","title":"CF.INSERTNX","url":"https://redis.io/docs/latest/commands/cf.insertnx/","summary":"Adds one or more items to a Cuckoo Filter if the items did not exist previously. A filter will be created if it does not exist","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"9fedb9c6b351adfb04cecdfa463131570700c5dfed4980ff0630a24ffa93ad55","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Adds one or more items to a cuckoo filter if they did not exist previously, allowing the filter to be created with a custom capacity if it does not exist yet.\n\nThis command is similar to [`CF.ADDNX`](https://redis.io/docs/latest/commands/cf.addnx), except that more than one item can be added and capacity can be specified.\n\n<note><b>Notes:</b>\n\n- This command is slower than [`CF.INSERT`](https://redis.io/docs/latest/commands/cf.insert) because it first checks whether each item exists.\n- Since [`CF.EXISTS`](https://redis.io/docs/latest/commands/cf.exists) can result in false positive, `CF.INSERTNX` may not add an item because it is supposedly already exist, which may be wrong.\n    \n</note>"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nis key name for a cuckoo filter to add items to.\n\nIf `key` does not exist - a new cuckoo filter is created.\n</details>\n\n<details open><summary><code>ITEMS item...</code></summary>\n\nOne or more items to add.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>CAPACITY capacity</code></summary>\n    \nSpecifies the desired capacity of the new filter, if this filter does not exist yet.\n    \nIf the filter already exists, then this parameter is ignored.\n    \nIf the filter does not exist yet and this parameter is *not* specified, then the filter is created with the module-level default capacity which is 1024.\n\nSee [`CF.RESERVE`](https://redis.io/docs/latest/commands/cf.reserve) for more information on cuckoo filter capacities.\n</details>\n    \n<details open><summary><code>NOCREATE</code></summary>\n  \nIf specified, prevents automatic filter creation if the filter does not exist (Instead, an error is returned).\n    \nThis option is mutually exclusive with `CAPACITY`.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> CF.INSERTNX cf CAPACITY 1000 ITEMS item1 item2 \n1) (integer) 1\n2) (integer) 1\n\n\n\nredis> CF.INSERTNX cf CAPACITY 1000 ITEMS item1 item2 item3\n1) (integer) 0\n2) (integer) 0\n3) (integer) 1\n\n\n\nredis> CF.INSERTNX cf_new CAPACITY 1000 NOCREATE ITEMS item1 item2 \n(error) ERR not found"},{"id":"details","title":"Details","role":"content","text":""},{"id":"complexity","title":"Complexity","role":"performance","text":"O(n + i), where n is the number of `sub-filters` and i is `maxIterations`.\nAdding items requires up to 2 memory accesses per `sub-filter`.\nBut as the filter fills up, both locations for an item might be full. The filter attempts to `Cuckoo` swap items up to `maxIterations` times."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays), where each element is an [integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): `0` when the item's fingerprint already exists in the filter, `1` when the item is added successfully, or `-1` when the item cannot be added because the filter is full.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) when the number of arguments or key type is incorrect, and also when `NOCREATE` is specified and `key` does not exist.\n\n**RESP3:**\n\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays), where each element is an [integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): `0` means that the item's fingerprint already exists in the filter, `1` for successfully adding an item, or `-1` when the item cannot be added because the filter is full.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) when the number of arguments or key type is incorrect, and also when `NOCREATE` is specified and `key` does not exist."}],"examples":[]}
{"schema_version":2,"id":"commands/cf.loadchunk","title":"CF.LOADCHUNK","url":"https://redis.io/docs/latest/commands/cf.loadchunk/","summary":"Restores a filter previously saved using SCANDUMP","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"d28ea5eec7d961683237c6529ef7953bc7307e5d1f6012f3d4659af12faf55f4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Restores a cuckoo filter previously saved using [`CF.SCANDUMP`](https://redis.io/docs/latest/commands/cf.scandump).\n\nSee the [`CF.SCANDUMP`](https://redis.io/docs/latest/commands/cf.scandump) command for example usage.\n\n<note><b>Notes</b>\n\n- This command overwrites the cuckoo filter stored under `key`.\n- Make sure that the cuckoo filter is not changed between invocations.\n\n</note>"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nis key name for a cuckoo filter to restore.\n</details>\n\n<details open><summary><code>iterator</code></summary>\n\nIterator value associated with `data` (returned by [`CF.SCANDUMP`](https://redis.io/docs/latest/commands/cf.scandump))\n</details>\n\n<details open><summary><code>data</code></summary>\n\nCurrent data chunk (returned by [`CF.SCANDUMP`](https://redis.io/docs/latest/commands/cf.scandump))\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"See [`CF.SCANDUMP`](https://redis.io/docs/latest/commands/cf.scandump) for an example."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, wrong key type, or when invalid data was passed.\n\n**RESP3:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, wrong key type, or when invalid data was passed."}],"examples":[]}
{"schema_version":2,"id":"commands/cf.mexists","title":"CF.MEXISTS","url":"https://redis.io/docs/latest/commands/cf.mexists/","summary":"Checks whether one or more items exist in a Cuckoo Filter","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"2e3be925f33c9dfe12905c9adf2fda356ce47931929f8b2bb13f76959ddd5d8e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Determines whether one or more items were added to a cuckoo filter.\n\nThis command is similar to [`CF.EXISTS`](https://redis.io/docs/latest/commands/cf.exists), except that more than one item can be checked."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nis key name for a cuckoo filter.\n\n</details>\n\n<details open><summary><code>item...</code></summary>\n\nOne or more items to check.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> CF.INSERT cf ITEMS item1 item2\n1) (integer) 1\n2) (integer) 1\nredis> CF.MEXISTS cf item1 item2 item3\n1) (integer) 1\n2) (integer) 1\n3) (integer) 0"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* An [array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [integers](https://redis.io/docs/latest/develop/reference/protocol-spec#integers), where each element corresponds to an item in the request. A value of `1` means the item was probably already added to the filter. A value of `0` means the item was definitely not added, `key` does not exist, or `key` contains a value of the wrong type.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if invalid arguments are passed.\n\n**RESP3:**\n\nOne of the following:\n* An [array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [booleans](https://redis.io/docs/latest/develop/reference/protocol-spec#booleans), where each element corresponds to an item in the request. A value of `true` means the item was probably already added to the filter. A value of `false` means the item was definitely not added, `key` does not exist, or `key` contains a value of the wrong type.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if invalid arguments are passed."}],"examples":[]}
{"schema_version":2,"id":"commands/cf.reserve","title":"CF.RESERVE","url":"https://redis.io/docs/latest/commands/cf.reserve/","summary":"Creates a new Cuckoo Filter","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"eb2e11d669f27947e55e6e8aa37d0c2b056d34920b7765d3eeb06ce3906f466c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Creates an empty cuckoo filter with a single sub-filter for the initial specified capacity.\n\nAccording to the cuckoo filter behavior, the filter is likely to declare itself full before `capacity` is reached; therefore, the fill rate will likely never reach 100 percent.\nThe fill rate can be improved by using a larger `bucketsize` at the cost of a higher error rate.\nWhen the filter self-declare itself `full`, it will auto-expand by generating additional sub-filters at the cost of reduced performance and increased error rate.\nThe new sub-filter is created with size of the previous sub-filter multiplied by `expansion`.\nLike bucket size, additional sub-filters grow the error rate linearly.\n\nThe minimal false positive error rate is 2/255 ≈ 0.78% when bucket size of 1 is used.\nLarger buckets increase the error rate linearly (for example, a bucket size of 3 yields a 2.35% error rate) but improve the fill rate of the filter.\n\n`maxiterations` dictates the number of attempts to find a slot for the incoming fingerprint.\nOnce the filter gets full, high `maxIterations` value will slow down insertions.\n\nUnused capacity in prior sub-filters is automatically used when possible. \nThe filter can grow up to 32 times."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nis key name for the cuckoo filter to be created.\n</details>\n\n<details open><summary><code>capacity</code></summary>\n\nEstimated capacity for the filter.\n\nCapacity is rounded to the next `2^n` number.\n\nThe filter will likely not fill up to 100% of it's capacity. Make sure to reserve extra capacity if you want to avoid expansions.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>BUCKETSIZE bucketsize</code></summary>\n\nNumber of items in each bucket.\n\nA higher bucket size value improves the fill rate but also causes a higher error rate and slightly slower performance.\n\n`bucketsize` is an integer between 1 and 255. The default value is 2.\n</details>\n\n<details open><summary><code>MAXITERATIONS maxiterations</code></summary>\n\nNumber of attempts to swap items between buckets before declaring filter as full and creating an additional filter. \n\nA low value is better for performance and a higher number is better for filter fill rate.\n\n`maxiterations` is an integer between 1 and 65535. The default value is 20.\n</details>\n\n<details open><summary><code>EXPANSION expansion</code></summary>\n\nWhen a new filter is created, its size is the size of the current filter multiplied by `expansion`.\n\n`expansion` is an integer between 0 and 32768. The default value is 1.\n\nExpansion is rounded to the next `2^n` number. \n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> CF.RESERVE cf 1000\nOK\n\nredis> CF.RESERVE cf 1000\n(error) ERR item exists\n\nredis> CF.RESERVE cf_params 1000 BUCKETSIZE 8 MAXITERATIONS 20 EXPANSION 2\nOK"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` if the filter was created successfully.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments or the key already exists.\n\n**RESP3:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` if the filter was created successfully.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments or the key already exists."}],"examples":[]}
{"schema_version":2,"id":"commands/cf.scandump","title":"CF.SCANDUMP","url":"https://redis.io/docs/latest/commands/cf.scandump/","summary":"Begins an incremental save of the bloom filter","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"29f8c9671e478ad6f31ea0c80f5830149055515d9d9ac85ade9f10d3ae7caba6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Begins an incremental save of the cuckoo filter.\n\nThis command is useful for large cuckoo filters that cannot fit into the [`DUMP`](https://redis.io/docs/latest/commands/dump) and [`RESTORE`](https://redis.io/docs/latest/commands/restore) model.\n\nThe first time this command is called, the value of `iter` should be 0. \n\nThis command returns successive `(iter, data)` pairs until `(0, NULL)` indicates completion."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nis key name for a cuckoo filter to save.\n</details>\n\n<details open><summary><code>iterator</code></summary>\n\nIterator value; either 0 or the iterator from a previous invocation of this command\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> CF.RESERVE cf 8\nOK\nredis> CF.ADD cf item1\n(integer) 1\nredis> CF.SCANDUMP cf 0\n1) (integer) 1\n2) \"\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x14\\x00\\x01\\x00\"\nredis> CF.SCANDUMP cf 1\n1) (integer) 9\n2) \"\\x00\\x00\\x00\\x00\\a\\x00\\x00\\x00\"\nredis> CF.SCANDUMP cf 9\n1) (integer) 0\n2) (nil) \nredis> DEL cf\n(integer) 1\nredis> CF.LOADCHUNK cf 1 \"\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x14\\x00\\x01\\x00\"\nOK\nredis> CF.LOADCHUNK cf 9 \"\\x00\\x00\\x00\\x00\\a\\x00\\x00\\x00\"\nOK\nredis> CF.EXISTS cf item1\n(integer) 1\n\n\nPython code:\n\nchunks = []\niter = 0\nwhile True:\n    iter, data = CF.SCANDUMP(key, iter)\n    if iter == 0:\n        break\n    else:\n        chunks.append([iter, data])\n\n# Load it back\nfor chunk in chunks:\n    iter, data = chunk\n    CF.LOADCHUNK(key, iter, data)"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): a two-element array of an [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) (_Iterator_) and a [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) (_Data_).\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, the key was not found, or the key is of the wrong type.\n\nThe Iterator is passed as input to the next invocation of `CF.SCANDUMP`. If _Iterator_ is 0, then it means iteration has completed.\n\nThe iterator-data pair should also be passed to [`CF.LOADCHUNK`](https://redis.io/docs/latest/commands/cf.loadchunk) when restoring the filter.\n\n**RESP3:**\n\nOne of the following:\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): a two-element array of an [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) (_Iterator_) and a [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) (_Data_).\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, the key was not found, or the key is of the wrong type.\n\nThe Iterator is passed as input to the next invocation of `CF.SCANDUMP`. If _Iterator_ is 0, then it means iteration has completed.\n\nThe iterator-data pair should also be passed to [`CF.LOADCHUNK`](https://redis.io/docs/latest/commands/cf.loadchunk) when restoring the filter."}],"examples":[]}
{"schema_version":2,"id":"commands/client","title":"CLIENT","url":"https://redis.io/docs/latest/commands/client/","summary":"A container for client connection commands.","since":"2.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"010c2d92db8fee717c4badc2935d49bb97d6ec7c2b47f407c6f0d3610e453167","sections":[{"id":"content","title":"Content","role":"content","text":"This is a container command for client connection commands.\n\nTo see the list of available commands you can call [`CLIENT HELP`](https://redis.io/docs/latest/commands/client-help)."}],"examples":[]}
{"schema_version":2,"id":"commands/client-caching","title":"CLIENT CACHING","url":"https://redis.io/docs/latest/commands/client-caching/","summary":"Instructs the server whether to track the keys in the next request.","since":"6.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"b9814f65215cc5bbad0ac7827648d611b6b4223a56aec5c59bc4295a8f5138dc","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This command controls the tracking of the keys in the next command executed\nby the connection, when tracking is enabled in `OPTIN` or `OPTOUT` mode.\nPlease check the\n[client side caching documentation](https://redis.io/docs/latest/develop/clients/client-side-caching) for\nbackground information.\n\nWhen tracking is enabled in Redis, using the [`CLIENT TRACKING`](https://redis.io/docs/latest/commands/client-tracking) command, it is\npossible to specify the `OPTIN` or `OPTOUT` options, so that keys\nin read only commands are not automatically remembered by the server to\nbe invalidated later. When we are in `OPTIN` mode, we can enable the\ntracking of the keys in the next command by calling `CLIENT CACHING YES`\nimmediately before it. Similarly when we are in `OPTOUT` mode, and keys\nare normally tracked, we can avoid the keys in the next command to be\ntracked using `CLIENT CACHING NO`.\n\nBasically the command sets a state in the connection, that is valid only\nfor the next command execution, that will modify the behavior of client\ntracking."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>YES | NO</code></summary>\n\nWhether to cache keys for the next command (`YES`) or not (`NO`) when tracking is in `OPTIN`/`OPTOUT` mode.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` or an error if the argument is not \"yes\" or \"no\".\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` or an error if the argument is not \"yes\" or \"no\"."}],"examples":[]}
{"schema_version":2,"id":"commands/client-getname","title":"CLIENT GETNAME","url":"https://redis.io/docs/latest/commands/client-getname/","summary":"Returns the name of the connection.","since":"2.6.9","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"0d7c24b7e223bedaf107f2984819c7b5b2df5533cf92c4829499f6b3cbef7ad9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `CLIENT GETNAME` returns the name of the current connection as set by [`CLIENT SETNAME`](https://redis.io/docs/latest/commands/client-setname). Since every new connection starts without an associated name, if no name was assigned nil is returned."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the connection name of the current connection.\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): the connection name was not set.\n\n**RESP3:**\n\nOne of the following:\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the connection name of the current connection.\n* [Null reply](../../develop/reference/protocol-spec#nulls): the connection name was not set."}],"examples":[]}
{"schema_version":2,"id":"commands/client-getredir","title":"CLIENT GETREDIR","url":"https://redis.io/docs/latest/commands/client-getredir/","summary":"Returns the client ID to which the connection's tracking notifications are redirected.","since":"6.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"4a656a977adcfbd3f656f12b19491787020dc66e9eb0f63c59364073f2029861","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This command returns the client ID we are redirecting our\n[tracking](https://redis.io/docs/latest/develop/clients/client-side-caching#tracking) notifications to. We set a client\nto redirect to when using [`CLIENT TRACKING`](https://redis.io/docs/latest/commands/client-tracking) to enable tracking. However in\norder to avoid forcing client libraries implementations to remember the\nID notifications are redirected to, this command exists in order to improve\nintrospection and allow clients to check later if redirection is active\nand towards which client ID."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0` when not redirecting notifications to any client.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `-1` if client tracking is not enabled.\n* [Integer reply](../../develop/reference/protocol-spec#integers): the ID of the client to which notification are being redirected.\n\n**RESP3:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0` when not redirecting notifications to any client.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `-1` if client tracking is not enabled.\n* [Integer reply](../../develop/reference/protocol-spec#integers): the ID of the client to which notification are being redirected."}],"examples":[]}
{"schema_version":2,"id":"commands/client-help","title":"CLIENT HELP","url":"https://redis.io/docs/latest/commands/client-help/","summary":"Returns helpful text about the different subcommands.","since":"5.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"ef499b287410d52c86151224540e6b918252e49eeaf57fa7992c200cccae2b91","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `CLIENT HELP` command returns a helpful text describing the different subcommands."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of subcommands and their descriptions.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of subcommands and their descriptions."}],"examples":[]}
{"schema_version":2,"id":"commands/client-id","title":"CLIENT ID","url":"https://redis.io/docs/latest/commands/client-id/","summary":"Returns the unique client ID of the connection.","since":"5.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"e9ba14ccf0ab61183e349dddb87e0e13cdc4fa650bb3a007def4298a3afee655","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The command just returns the ID of the current connection. Every connection\nID has certain guarantees:\n\n1. It is never repeated, so if `CLIENT ID` returns the same number, the caller can be sure that the underlying client did not disconnect and reconnect the connection, but it is still the same connection.\n2. The ID is monotonically incremental. If the ID of a connection is greater than the ID of another connection, it is guaranteed that the second connection was established with the server at a later time.\n\nThis command is especially useful together with [`CLIENT UNBLOCK`](https://redis.io/docs/latest/commands/client-unblock) which was\nintroduced also in Redis 5 together with `CLIENT ID`. Check the [`CLIENT UNBLOCK`](https://redis.io/docs/latest/commands/client-unblock) command page for a pattern involving the two commands."},{"id":"examples","title":"Examples","role":"example","text":"redis> CLIENT ID"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Because Redis Software clustering allows [multiple active proxies](https://redis.io/docs/latest/operate/rs/databases/configure/proxy-policy), `CLIENT ID` cannot guarantee incremental IDs between clients that connect to different nodes under multi proxy policies. |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the ID of the client.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the ID of the client."}],"examples":[]}
{"schema_version":2,"id":"commands/client-info","title":"CLIENT INFO","url":"https://redis.io/docs/latest/commands/client-info/","summary":"Returns information about the connection.","since":"6.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"f8fa0ffb41d17e3292b60a42dca6f9324a6929cf618685c792d84d39cfd157d6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The command returns information and statistics about the current client connection in a mostly human readable format.\n\nThe reply format is identical to that of [`CLIENT LIST`](https://redis.io/docs/latest/commands/client-list), and the content consists only of information about the current client."},{"id":"examples","title":"Examples","role":"example","text":"redis> CLIENT INFO"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): a unique string for the current client, as described at the `CLIENT LIST` page.\n\n**RESP3:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): a unique string for the current client, as described at the `CLIENT LIST` page."}],"examples":[]}
{"schema_version":2,"id":"commands/client-kill","title":"CLIENT KILL","url":"https://redis.io/docs/latest/commands/client-kill/","summary":"Terminates open connections.","since":"2.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"9ead7388ff215fcdcc5449c94e9448fc17488046805366bd69f85bc31512d94d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `CLIENT KILL` command closes a given client connection. This command support two formats, the old format:\n\n    CLIENT KILL addr:port\n\nThe `ip:port` should match a line returned by the [`CLIENT LIST`](https://redis.io/docs/latest/commands/client-list) command (`addr` field).\n\nThe new format:\n\n    CLIENT KILL <filter> <value> ... ... <filter> <value>\n\nWith the new form it is possible to kill clients by different attributes\ninstead of killing just by address. See below for the list of supported filters.\n\nIt is possible to provide multiple filters at the same time. Multiple filters are combined using logical AND.\n\n    CLIENT KILL addr 127.0.0.1:12345 type pubsub\n\nis valid and will kill only a pubsub client with the specified address.\n\nWhen the new form is used the command no longer returns `OK` or an error, but instead the number of killed clients, which may be zero."},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"Provide the legacy `ip:port` argument to close a client by address, or one or more of the following filters, which can be combined to select the clients to close.\n\n<details open><summary><code>ip:port</code></summary>\n\nClose the client at the given address. This is the legacy single-argument form.\n\n</details>\n\n<details open><summary><code>ID client-id</code></summary>\n\nClose only the client with the given unique ID.\n\n</details>\n\n<details open><summary><code>TYPE NORMAL | MASTER | SLAVE | REPLICA | PUBSUB</code></summary>\n\nClose only clients of the given type.\n\n</details>\n\n<details open><summary><code>USER username</code></summary>\n\nClose only clients authenticated as the given ACL user.\n\n</details>\n\n<details open><summary><code>ADDR ip:port</code></summary>\n\nClose only clients connected from the given address.\n\n</details>\n\n<details open><summary><code>LADDR ip:port</code></summary>\n\nClose only clients connected to the given local (server) address.\n\n</details>\n\n<details open><summary><code>SKIPME YES | NO</code></summary>\n\nWhether to skip the calling client (`YES`, the default) or allow it to be closed too (`NO`).\n\n</details>\n\n<details open><summary><code>MAXAGE maxage</code></summary>\n\nClose only clients older than the given age, in seconds.\n\n</details>"},{"id":"details","title":"Details","role":"content","text":""},{"id":"client-kill-and-redis-sentinel","title":"CLIENT KILL and Redis Sentinel","role":"content","text":"Recent versions of Redis Sentinel (Redis 2.8.12 or greater) use CLIENT KILL\nin order to kill clients when an instance is reconfigured, in order to\nforce clients to perform the handshake with one Sentinel again and update\nits configuration."},{"id":"notes","title":"Notes","role":"content","text":"Due to the single-threaded nature of Redis, it is not possible to\nkill a client connection while it is executing a command. From\nthe client point of view, the connection can never be closed\nin the middle of the execution of a command. However, the client\nwill notice the connection has been closed only when the\nnext command is sent (and results in network error)."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` when called in 3 argument format and the connection has been closed.\n* [Integer reply](../../develop/reference/protocol-spec#integers): when called in filter/value format, the number of clients killed.\n\n**RESP3:**\n\nOne of the following:\n* [Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` when called in 3 argument format and the connection has been closed.\n* [Integer reply](../../develop/reference/protocol-spec#integers): when called in filter/value format, the number of clients killed."}],"examples":[]}
{"schema_version":2,"id":"commands/client-list","title":"CLIENT LIST","url":"https://redis.io/docs/latest/commands/client-list/","summary":"Lists open connections.","since":"2.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"226afbd66a707112cde16cddacdfa88aba6587112b96422e18f7fa153e108dee","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `CLIENT LIST` command returns information and statistics about the client\nconnections server in a mostly human readable format.\n\nYou can use one of the optional subcommands to filter the list. The `TYPE type` subcommand filters the list by clients' type, where *type* is one of `normal`, `master`, `replica`, and `pubsub`. Note that clients blocked by the [`MONITOR`](https://redis.io/docs/latest/commands/monitor) command belong to the `normal` class.\n\nThe `ID` filter only returns entries for clients with IDs matching the `client-id` arguments."},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>TYPE NORMAL | MASTER | REPLICA | PUBSUB</code></summary>\n\nList only clients of the given type.\n\n</details>\n\n<details open><summary><code>ID client-id [client-id ...]</code></summary>\n\nList only the clients with the given IDs.\n\n</details>"},{"id":"details","title":"Details","role":"content","text":""},{"id":"returned-information","title":"Returned information","role":"returns","text":"* `id`: a unique 64-bit client ID.\n* `addr`: address/port of the client.\n* `laddr`: address/port of local address client connected to (bind address).\n* `fd`: file descriptor corresponding to the socket.\n* `name`: the name set by the client with [`CLIENT SETNAME`](https://redis.io/docs/latest/commands/client-setname).\n* `age`: total duration of the connection in seconds.\n* `idle`: idle time of the connection in seconds.\n* `flags`: client flags (see below).\n* `db`: current database ID.\n* `sub`: number of channel subscriptions.\n* `psub`: number of pattern matching subscriptions.\n* `ssub`: number of shard channel subscriptions. Added in Redis 7.0.3.\n* `multi`: number of commands in a MULTI/EXEC context.\n* `watch`: number of keys this client is currently watching. Added in Redis 7.4.\n* `qbuf`: query buffer length (0 means no query pending).\n* `qbuf-free`: free space of the query buffer (0 means the buffer is full).\n* `argv-mem`: incomplete arguments for the next command (already extracted from query buffer).\n* `multi-mem`: memory is used up by buffered multi commands. Added in Redis 7.0.\n* `obl`: output buffer length.\n* `oll`: output list length (replies are queued in this list when the buffer is full).\n* `omem`: output buffer memory usage.\n* `tot-mem`: total memory consumed by this client in its various buffers.\n* `events`: file descriptor events (see below).\n* `cmd`: last command played.\n* `user`: the authenticated username of the client.\n* `redir`: client id of current client tracking redirection.\n* `resp`: client RESP protocol version. Added in Redis 7.0.\n* `rbp`: peak size of the client's read buffer since the client connected. Added in Redis 7.0.\n* `rbs`: current size of the client's read buffer in bytes. Added in Redis 7.0.\n* `lib-name` - the name of the client library that is being used.\n* `lib-ver` - the version of the client library.\n* `io-thread`: id of I/O thread assigned to the client. Added in Redis 8.0.\n* `tot-net-in`: total network input bytes read from this client.\n* `tot-net-out`: total network output bytes sent to this client.\n* `tot-cmds`: total count of commands this client executed.\n* `read-events`: number of read events for this client. Added in Redis 8.8\n* `parse-batch-cmd-sum`: cumulative number of commands parsed across all parsing batches for this client. Added in Redis 8.8\n* `parse-batch-cnt`: total number of parsing batches for this client. Divide `parse-batch-cmd-sum` by this value to get the client’s average commands per batch. Added in Redis 8.8\n\nThe client flags can be a combination of:\n\n[code example]\n\nThe file descriptor events can be:\n\n[code example]\n\nNew fields are regularly added for debugging purpose. Some could be removed\nin the future. A version safe Redis client using this command should parse\nthe output accordingly (i.e. handling gracefully missing fields, skipping\nunknown fields)."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): information and statistics about client connections.\n\n**RESP3:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): information and statistics about client connections."}],"examples":[{"id":"returned-information-ex0","language":"plaintext","code":"A: connection to be closed ASAP\nb: the client is waiting in a blocking operation\nc: connection to be closed after writing entire reply\nd: a watched keys has been modified - EXEC will fail\ne: the client is excluded from the client eviction mechanism\ng: the client is responsible for migrating slots (atomic slot migration)\ni: the client is waiting for a VM I/O (deprecated)\nM: the client is a master\nN: no specific flag set\no: the client is responsible for importing slots (atomic slot migration)\nO: the client is a client in MONITOR mode\nP: the client is a Pub/Sub subscriber\nr: the client is in readonly mode against a cluster node\nS: the client is a replica node connection to this instance\nu: the client is unblocked\nU: the client is connected via a Unix domain socket\nx: the client is in a MULTI/EXEC context\nt: the client enabled keys tracking in order to perform client side caching\nT: the client will not touch the LRU/LFU of the keys it accesses\nR: the client tracking target client is invalid\nB: the client enabled broadcast tracking mode","section_id":"returned-information"},{"id":"returned-information-ex1","language":"plaintext","code":"r: the client socket is readable (event loop)\nw: the client socket is writable (event loop)","section_id":"returned-information"}]}
{"schema_version":2,"id":"commands/client-no-evict","title":"CLIENT NO-EVICT","url":"https://redis.io/docs/latest/commands/client-no-evict/","summary":"Sets the client eviction mode of the connection.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"5267755d93a74b0c4fb51f33f626bc4ed95b72e0d1ab5b63dd7273bb167d25dd","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `CLIENT NO-EVICT` command sets the [client eviction](https://redis.io/docs/latest/develop/reference/clients#client-eviction) mode for the current connection.\n\nWhen turned on and client eviction is configured, the current connection will be excluded from the client eviction process even if we're above the configured client eviction threshold.\n\nWhen turned off, the current client will be re-included in the pool of potential clients to be evicted (and evicted if needed).\n\nSee [client eviction](https://redis.io/docs/latest/develop/reference/clients#client-eviction) for more details."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>ON | OFF</code></summary>\n\nTurn eviction protection on (`ON`) or off (`OFF`) for the current connection.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` if the command was successful.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: wrong number of or invalid arguments.\n\n**RESP3:**\n\nOne of the following:\n\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` if the command was successful.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: wrong number of or invalid arguments."}],"examples":[]}
{"schema_version":2,"id":"commands/client-no-touch","title":"CLIENT NO-TOUCH","url":"https://redis.io/docs/latest/commands/client-no-touch/","summary":"Controls whether commands sent by the client affect the LRU/LFU of accessed keys.","since":"7.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"aa8cc7c90aed40b93e0f4140ccc720264af674d3ff40470ec5b85c96064c93fd","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `CLIENT NO-TOUCH` command controls whether commands sent by the client will alter the LRU/LFU of the keys they access.\n\nWhen turned on, the current client will not change LFU/LRU stats, unless it sends the [`TOUCH`](https://redis.io/docs/latest/commands/touch) command.\n\nWhen turned off, the client touches LFU/LRU stats just as a normal client."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>ON | OFF</code></summary>\n\nTurn off (`ON`) or on (`OFF`) updates to keys' last-access time and LFU counter for commands issued by this connection.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` if the command was successful.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: wrong number of or invalid arguments.\n\n**RESP3:**\n\nOne of the following:\n\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` if the command was successful.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: wrong number of or invalid arguments."}],"examples":[]}
{"schema_version":2,"id":"commands/client-pause","title":"CLIENT PAUSE","url":"https://redis.io/docs/latest/commands/client-pause/","summary":"Suspends commands processing.","since":"3.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"d148ed6df4a772dfc0fae992ff2bec7aa3ea30108059257149301aa89d3efdb2","sections":[{"id":"overview","title":"Overview","role":"overview","text":"`CLIENT PAUSE` is a connections control command able to suspend all the Redis clients for the specified amount of time (in milliseconds).\n\nThe command performs the following actions:\n\n* It stops processing all the pending commands from normal and pub/sub clients for the given mode. However, interactions with replicas will continue normally. Note that clients are formally paused when they try to execute a command, so no work is taken on the server side for inactive clients.\n* It returns `OK` to the caller as soon as possible so the `CLIENT PAUSE` command execution is not itself paused.\n* When the specified amount of time has elapsed, all the clients are unblocked; this will trigger the processing of all the commands accumulated in the query buffer of every client during the pause."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>timeout</code></summary>\n\nHow long to pause clients, in milliseconds.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>WRITE | ALL</code></summary>\n\n`ALL`: This is the default mode. All client commands are blocked.\n\n`WRITE`: Clients are only blocked if they attempt to execute a write command.\n\n</details>"},{"id":"details","title":"Details","role":"content","text":"For the `WRITE` mode, some commands have special behavior:\n\n* [`EVAL`](https://redis.io/docs/latest/commands/eval)/[`EVALSHA`](https://redis.io/docs/latest/commands/evalsha): Will block client for all scripts.\n* [`PUBLISH`](https://redis.io/docs/latest/commands/publish): Will block client.\n* [`PFCOUNT`](https://redis.io/docs/latest/commands/pfcount): Will block client.\n* [`WAIT`](https://redis.io/docs/latest/commands/wait): Acknowledgments will be delayed, so this command will appear blocked.\n\nThis command is useful as it makes able to switch clients from a Redis instance to another one in a controlled way. For example during an instance upgrade the system administrator could do the following:\n\n* Pause the clients using `CLIENT PAUSE`.\n* Wait a few seconds to make sure the replicas processed the latest replication stream from the master.\n* Turn one of the replicas into a master.\n* Reconfigure clients to connect with the new master.\n\nSince Redis 6.2, the recommended mode for client pause is `WRITE`. This mode will stop all replication traffic, can be\naborted with the [`CLIENT UNPAUSE`](https://redis.io/docs/latest/commands/client-unpause) command, and allows reconfiguring the old master without risking accepting writes after the\nfailover. This is also the mode used during cluster failover.\n\nFor Redis versions earlier than 6.2, you can send CLIENT PAUSE in a MULTI/EXEC block with INFO replication to get the current master offset when Redis blocks clients. You can then wait for that offset on the replica to make sure the replica has processed the full replication stream.\n\nStarting with Redis 3.2.10 and 4.0.0, this command also prevents keys from being evicted or expired while clients are paused. This keeps the dataset static during the pause: clients cannot write to it, and Redis does not change it through eviction or expiration."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` or an error if the timeout is invalid.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` or an error if the timeout is invalid."}],"examples":[]}
{"schema_version":2,"id":"commands/client-reply","title":"CLIENT REPLY","url":"https://redis.io/docs/latest/commands/client-reply/","summary":"Instructs the server whether to reply to commands.","since":"3.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-06-15T06:26:34-07:00","page_type":"content","content_hash":"13cb2066b7aa2180b04a7667d6b192cff5a6f67c0260b0efcd3c889234e65249","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Sometimes it can be useful for clients to completely disable replies from the Redis server. For example when the client sends fire and forget commands or performs a mass loading of data, or in caching contexts where new data is streamed constantly. In such contexts to use server time and bandwidth in order to send back replies to clients, which are going to be ignored, is considered wasteful.\n\nThe `CLIENT REPLY` command controls whether the server will reply the client's commands."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>ON | OFF | SKIP</code></summary>\n\nControl replies for the connection:\n\n* `ON`: This is the default mode in which the server returns a reply to every command.\n* `OFF`: In this mode the server will not reply to client commands.\n* `SKIP`: This mode skips the reply of the command immediately after it.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` when called with `ON`. When called with either `OFF` or `SKIP` sub-commands, no reply is made.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` when called with `ON`. When called with either `OFF` or `SKIP` sub-commands, no reply is made."}],"examples":[]}
{"schema_version":2,"id":"commands/client-setinfo","title":"CLIENT SETINFO","url":"https://redis.io/docs/latest/commands/client-setinfo/","summary":"Sets information specific to the client or connection.","since":"7.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"cff34d41c874ce469ab890be01ba7364340130e4c46f0e400c33450b182f1aef","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `CLIENT SETINFO` command assigns various info attributes to the current connection which are displayed in the output of [`CLIENT LIST`](https://redis.io/docs/latest/commands/client-list) and [`CLIENT INFO`](https://redis.io/docs/latest/commands/client-info).\n\nClient libraries are expected to pipeline this command after authentication on all connections\nand ignore failures since they could be connected to an older version that doesn't support them."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"Provide one of the following.\n\n<details open><summary><code>LIB-NAME libname</code></summary>\n\nSet the library name for the current connection.\n\n</details>\n\n<details open><summary><code>LIB-VER libver</code></summary>\n\nSet the library version for the current connection.\n\n</details>"},{"id":"details","title":"Details","role":"content","text":"There is no limit to the length of these attributes. However it is not possible to use spaces, newlines, or other non-printable characters that would violate the format of the [`CLIENT LIST`](https://redis.io/docs/latest/commands/client-list) reply.\n\n[Official client libraries](https://redis.io/docs/latest/develop/connect/clients/) allow extending `lib-name` with a custom suffix to expose additional information about the client. \nFor example, high-level libraries like [redis-om-spring](https://github.com/redis/redis-om-spring) can report their version. \nThe resulting `lib-name` would be `jedis(redis-om-spring_v1.0.0)`. \nBrace characters are used to delimit the custom suffix and should be avoided in the suffix itself.\nWe recommend using the following format for the custom suffixes for third-party libraries `(?<custom-name>[ -~]+)[ -~]v(?<custom-version>[\\d\\.]+)` and use `;` to delimit multiple suffixes.\n\nNote that these attributes are not cleared by the [`RESET`](https://redis.io/docs/latest/commands/reset) command."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the attribute name was successfully set.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the attribute name was successfully set."}],"examples":[]}
{"schema_version":2,"id":"commands/client-setname","title":"CLIENT SETNAME","url":"https://redis.io/docs/latest/commands/client-setname/","summary":"Sets the connection name.","since":"2.6.9","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"5fa04ca3afb85165f59be276c7be7453cffe7f61b671fa60062485d3574c5502","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `CLIENT SETNAME` command assigns a name to the current connection."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>connection-name</code></summary>\n\nThe name to assign to the current connection.\n\n</details>"},{"id":"details","title":"Details","role":"content","text":"The assigned name is displayed in the output of [`CLIENT LIST`](https://redis.io/docs/latest/commands/client-list) so that it is possible to identify the client that performed a given connection.\n\nFor instance when Redis is used in order to implement a queue, producers and consumers of messages may want to set the name of the connection according to their role.\n\nThere is no limit to the length of the name that can be assigned if not the usual limits of the Redis string type (512 MB). However it is not possible to use spaces in the connection name as this would violate the format of the [`CLIENT LIST`](https://redis.io/docs/latest/commands/client-list) reply.\n\nIt is possible to entirely remove the connection name setting it to the empty string, that is not a valid connection name since it serves to this specific purpose.\n\nThe connection name can be inspected using [`CLIENT GETNAME`](https://redis.io/docs/latest/commands/client-getname).\n\nEvery new connection starts without an assigned name.\n\n> [!TIP]\n> Setting names to connections is a good way to debug connection leaks due to bugs in the application using Redis."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the connection name was successfully set.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the connection name was successfully set."}],"examples":[]}
{"schema_version":2,"id":"commands/client-tracking","title":"CLIENT TRACKING","url":"https://redis.io/docs/latest/commands/client-tracking/","summary":"Controls server-assisted client-side caching for the connection.","since":"6.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"414a23996ccf44e7284d06c08d6c7b8540ba23a4f47c7efbfcfa9f396ef3a200","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This command enables the tracking feature of the Redis server, which is used\nfor [server assisted client side caching](https://redis.io/docs/latest/develop/clients/client-side-caching#tracking).\n\nWhen tracking is enabled Redis remembers the keys that the connection\nrequested, in order to send later invalidation messages when such keys are\nmodified. Invalidation messages are sent in the same connection (only available\nwhen the RESP3 protocol is used) or redirected in a different connection\n(available with RESP2 and Pub/Sub). You can use broadcasting mode to receive notifications for all keys that match the key prefixes you subscribe to, regardless of which keys you request. For details, see [the client-side caching documentation](https://redis.io/docs/latest/develop/reference/client-side-caching). This page only describes the options for this subcommand.\n\nIn order to enable tracking, use:\n\n    CLIENT TRACKING ON ... options ...\n\nThe feature will remain active in the current connection for all its life,\nunless tracking is turned off with `CLIENT TRACKING OFF` at some point."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>ON | OFF</code></summary>\n\nTurn key-invalidation tracking on or off for the connection.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>REDIRECT client-id</code></summary>\n\nSend invalidation messages to the connection with the specified ID. The connection must exist. You can get the ID of a connection using [`CLIENT ID`](https://redis.io/docs/latest/commands/client-id). If the redirected connection terminates while in RESP3 mode, the connection with tracking enabled receives tracking-redir-broken push messages to signal the disconnection.\n\n</details>\n\n<details open><summary><code>PREFIX prefix [PREFIX prefix ...]</code></summary>\n\nTrack only keys that start with one of the specified prefixes. Use with the `BCAST` argument. You can specify this option multiple times to track multiple prefixes. If you enable broadcasting without this option, Redis sends invalidation messages for every key. You can’t remove a single prefix; to remove all prefixes, disable and re-enable tracking. This option adds O(N^2) time complexity, where N is the total number of tracked prefixes.\n\n</details>\n\n<details open><summary><code>BCAST</code></summary>\n\nEnable broadcasting mode. In this mode, Redis sends invalidation messages for all keys, or for keys that match a specified PREFIX, instead of only the keys that the connection reads. Without broadcasting mode, Redis tracks the keys fetched by read-only commands and sends invalidation messages only for those keys.\n\n</details>\n\n<details open><summary><code>OPTIN</code></summary>\n\nTrack keys only for read commands that immediately follow a `CLIENT CACHING YES`.\n\n</details>\n\n<details open><summary><code>OPTOUT</code></summary>\n\nTrack all read keys except those for commands that immediately follow a `CLIENT CACHING NO`.\n\n</details>\n\n<details open><summary><code>NOLOOP</code></summary>\n\nDo not send invalidation messages for keys modified by this connection. In the default tracking mode, modifying a tracked key removes it from the invalidation table, even when `NOLOOP` suppresses the message. To track the key for future invalidations, read the key again.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the connection was successfully put in tracking mode or if the tracking mode was successfully disabled. Otherwise, an error is returned.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the connection was successfully put in tracking mode or if the tracking mode was successfully disabled. Otherwise, an error is returned."}],"examples":[]}
{"schema_version":2,"id":"commands/client-trackinginfo","title":"CLIENT TRACKINGINFO","url":"https://redis.io/docs/latest/commands/client-trackinginfo/","summary":"Returns information about server-assisted client-side caching for the connection.","since":"6.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"3caa1cbd0f3296f28c81e75b033ef017d752455fb00fcc1a81f11f97a40833b8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The command returns information about the current client connection's use of the [server assisted client side caching](https://redis.io/docs/latest/develop/clients/client-side-caching) feature.\n\nHere's the list of tracking information sections and their respective values:\n\n* **flags**: A list of tracking flags used by the connection. The flags and their meanings are as follows:\n  * `off`: The connection isn't using server assisted client side caching.\n  * `on`: Server assisted client side caching is enabled for the connection.\n  * `bcast`: The client uses broadcasting mode.\n  * `optin`: The client does not cache keys by default.\n  * `optout`: The client caches keys by default.\n  * `caching-yes`: The next command will cache keys (exists only together with `optin`).\n  * `caching-no`: The next command won't cache keys (exists only together with `optout`).\n  * `noloop`: The client isn't notified about keys modified by itself.\n  * `broken_redirect`: The client ID used for redirection isn't valid anymore.\n* **redirect**: The client ID used for notifications redirection, 0 for self-redirection, or -1 when none.\n* **prefixes**: A list of key prefixes for which notifications are sent to the client."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of tracking information sections and their respective values.\n\n**RESP3:**\n\n[Map reply](../../develop/reference/protocol-spec#maps): a list of tracking information sections and their respective values."}],"examples":[]}
{"schema_version":2,"id":"commands/client-unblock","title":"CLIENT UNBLOCK","url":"https://redis.io/docs/latest/commands/client-unblock/","summary":"Unblocks a client blocked by a blocking command from a different connection.","since":"5.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"18c50d948f69eb5aa6845682b90bb2ed44f065e694f89d63e01cc145805fe033","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Use this command from one connection to unblock a client that is blocked by a blocking operation, such as [`BRPOP`](https://redis.io/docs/latest/commands/brpop), [`XREAD`](https://redis.io/docs/latest/commands/xread), or [`WAIT`](https://redis.io/docs/latest/commands/wait).\n\nBy default, Redis unblocks the client as if the blocked command timed out. You can pass an optional argument to choose the unblocking behavior: TIMEOUT, the default, or ERROR. If you specify ERROR, Redis unblocks the client and returns an error that indicates the client was force-unblocked.Specifically the client will receive the following error:\n\n    -UNBLOCKED client unblocked via CLIENT UNBLOCK\n\nThe error text may change over time, but the error code will remain `-UNBLOCKED`."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>client-id</code></summary>\n\nThe ID of the client to unblock.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>TIMEOUT | ERROR</code></summary>\n\nHow to unblock the client: as if its timeout expired (`TIMEOUT`, the default), or by returning an error (`ERROR`).\n\n</details>"},{"id":"details","title":"Details","role":"content","text":"Use this command when you need to monitor many keys with a limited number of connections. For example, you might monitor multiple streams with XREAD without using more than N connections. If your consumer process needs to monitor another stream key, you can avoid opening another connection: unblock one of the connections in the pool, add the new key, and issue the blocking command again.\n\nTo use this pattern, create an additional control connection that sends CLIENT UNBLOCK when needed. Before you run a blocking operation on each monitored connection, run CLIENT ID to get that connection’s ID. When you need to add or remove a key, use the control connection to send CLIENT UNBLOCK for the connection that is running the blocking command. The blocking command returns, and you can then reissue it with the updated set of keys.\n\nThe following example uses Redis Streams, but you can apply the same pattern to other blocking operations.\n\n[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): `1` if the client was unblocked successfully.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0` if the client wasn't unblocked.\n\n**RESP3:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): `1` if the client was unblocked successfully.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0` if the client wasn't unblocked."}],"examples":[{"id":"details-ex0","language":"plaintext","code":"Connection A (blocking connection):\n> CLIENT ID\n2934\n> BRPOP key1 key2 key3 0\n(client is blocked)\n\n... Now we want to add a new key ...\n\nConnection B (control connection):\n> CLIENT UNBLOCK 2934\n1\n\nConnection A (blocking connection):\n... BRPOP reply with timeout ...\nNULL\n> BRPOP key1 key2 key3 key4 0\n(client is blocked again)","section_id":"details"}]}
{"schema_version":2,"id":"commands/client-unpause","title":"CLIENT UNPAUSE","url":"https://redis.io/docs/latest/commands/client-unpause/","summary":"Resumes processing commands from paused clients.","since":"6.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"8917d91eb05cf422cbeafb1e5f005cd7ba76821e2f879ebe6cddd64be0776993","sections":[{"id":"overview","title":"Overview","role":"overview","text":"`CLIENT UNPAUSE` is used to resume command processing for all clients that were paused by [`CLIENT PAUSE`](https://redis.io/docs/latest/commands/client-pause)."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[]}
{"schema_version":2,"id":"commands/cluster","title":"CLUSTER","url":"https://redis.io/docs/latest/commands/cluster/","summary":"A container for Redis Cluster commands.","since":"3.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"a5b28bcef4996ad3c1398da529efaf89b50939b07e9ddb6c12fc1695920b08b3","sections":[{"id":"content","title":"Content","role":"content","text":"This is a container command for Redis Cluster commands.\n\nTo see the list of available commands you can call [`CLUSTER HELP`](https://redis.io/docs/latest/commands/cluster-help)."}],"examples":[]}
{"schema_version":2,"id":"commands/cluster-addslots","title":"CLUSTER ADDSLOTS","url":"https://redis.io/docs/latest/commands/cluster-addslots/","summary":"Assigns new hash slots to a node.","since":"3.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-06-15T06:26:34-07:00","page_type":"content","content_hash":"dcd5095c44a104d7ac2831c01db1ad15d0505149eead1030807654990383aeff","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This command is useful in order to modify a node's view of the cluster\nconfiguration. Specifically it assigns a set of hash slots to the node\nreceiving the command. If the command is successful, the node will map\nthe specified hash slots to itself, and will start broadcasting the new\nconfiguration.\n\nHowever note that:\n\n1. The command only works if all the specified slots are, from the point of view of the node receiving the command, currently not assigned. A node will refuse to take ownership for slots that already belong to some other node (including itself).\n2. The command fails if the same slot is specified multiple times.\n3. If one of the specified slots is marked as `importing`, Redis clears that temporary state after the node assigns the previously unbound slot to itself. At that point, the slot belongs to the node, so it is no longer being imported."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>slot [slot ...]</code></summary>\n\nOne or more hash slots to assign to the current node.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"The following command assigns slots 1, 2, and 3 to the node receiving\nthe command:\n\n    > CLUSTER ADDSLOTS 1 2 3\n    OK\n\nIf you try to execute the same command again on the same node, Redis will return an error because the slots\nare already assigned:\n\n    > CLUSTER ADDSLOTS 1 2 3\n    ERR Slot 1 is already busy"},{"id":"details","title":"Details","role":"content","text":""},{"id":"usage-in-redis-cluster","title":"Usage in Redis Cluster","role":"syntax","text":"This command only works in cluster mode and is useful in the following\nRedis Cluster operations:\n\n1. To create a new `cluster ADDSLOTS` is used in order to initially setup master nodes splitting the available hash slots among them.\n2. In order to fix a broken cluster where certain slots are unassigned."},{"id":"information-about-slots-propagation-and-warnings","title":"Information about slots propagation and warnings","role":"content","text":"Note that once a node assigns a set of slots to itself, it will start\npropagating this information in heartbeat packet headers. However the\nother nodes will accept the information only if they have the slot as\nnot already bound with another node, or if the configuration epoch of the\nnode advertising the new hash slot, is greater than the node currently listed\nin the table.\n\nThis means that this command should be used with care only by applications\norchestrating Redis Cluster, like `redis-cli`, and the command if used\nout of the right context can leave the cluster in a wrong state or cause\ndata loss."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the command was successful. Otherwise an error is returned.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the command was successful. Otherwise an error is returned."}],"examples":[]}
{"schema_version":2,"id":"commands/cluster-addslotsrange","title":"CLUSTER ADDSLOTSRANGE","url":"https://redis.io/docs/latest/commands/cluster-addslotsrange/","summary":"Assigns new hash slot ranges to a node.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"c67b287261d31cb095552543535dcbe9644f674ca3ba7e814b4aa9311831c5f9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `CLUSTER ADDSLOTSRANGE` is similar to the [`CLUSTER ADDSLOTS`](https://redis.io/docs/latest/commands/cluster-addslots) command in that they both assign hash slots to nodes.\n\nThe difference between the two commands is that [`CLUSTER ADDSLOTS`](https://redis.io/docs/latest/commands/cluster-addslots) takes a list of slots to assign to the node, while `CLUSTER ADDSLOTSRANGE` takes a list of slot ranges (specified by start and end slots) to assign to the node."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>start-slot end-slot [start-slot end-slot ...]</code></summary>\n\nOne or more inclusive ranges of hash slots to assign to the current node.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"To assign slots 1 2 3 4 5 to the node, the [`CLUSTER ADDSLOTS`](https://redis.io/docs/latest/commands/cluster-addslots) command is:\n\n    > CLUSTER ADDSLOTS 1 2 3 4 5\n    OK\n\nThe same operation can be completed with the following `CLUSTER ADDSLOTSRANGE` command:\n\n    > CLUSTER ADDSLOTSRANGE 1 5\n    OK"},{"id":"details","title":"Details","role":"content","text":""},{"id":"usage-in-redis-cluster","title":"Usage in Redis Cluster","role":"syntax","text":"This command only works in cluster mode and is useful in the following Redis Cluster operations:\n\n1. To create a new cluster, `CLUSTER ADDSLOTSRANGE` is used to initially set up master nodes splitting the available hash slots among them.\n2. In order to fix a broken cluster where certain slots are unassigned."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the command was successful. Otherwise an error is returned.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the command was successful. Otherwise an error is returned."}],"examples":[]}
{"schema_version":2,"id":"commands/cluster-bumpepoch","title":"CLUSTER BUMPEPOCH","url":"https://redis.io/docs/latest/commands/cluster-bumpepoch/","summary":"Advances the cluster config epoch.","since":"3.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"82dd436ed3ab7a8b8d89f9796d0096f01b8a18f88553a99c25a381ae7e7bf666","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Advances the cluster config epoch.\n\nThe `CLUSTER BUMPEPOCH` command triggers an increment to the cluster's config epoch from the connected node. The epoch will be incremented if the node's config epoch is zero, or if it is less than the cluster's greatest epoch.\n\n**Note:** config epoch management is performed internally by the cluster, and relies on obtaining a consensus of nodes. The `CLUSTER BUMPEPOCH` attempts to increment the config epoch **WITHOUT** getting the consensus, so using it may violate the \"last failover wins\" rule. Use it with caution."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): `BUMPED` if the epoch was incremented.\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): `STILL` if the node already has the greatest configured epoch in the cluster.\n\n**RESP3:**\n\nOne of the following:\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): `BUMPED` if the epoch was incremented.\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): `STILL` if the node already has the greatest configured epoch in the cluster."}],"examples":[]}
{"schema_version":2,"id":"commands/cluster-count-failure-reports","title":"CLUSTER COUNT-FAILURE-REPORTS","url":"https://redis.io/docs/latest/commands/cluster-count-failure-reports/","summary":"Returns the number of active failure reports active for a node.","since":"3.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-06-15T06:26:34-07:00","page_type":"content","content_hash":"9389262c1b8c6cc02bb479c87848fa7e859a4c9fefcf2517c5469e6923766097","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This command returns the number of active failure reports for the specified node. Redis Cluster uses failure reports to promote a node from PFAIL to FAIL. PFAIL means the node is not reachable from one node’s point of view. FAIL means that a majority of primary nodes agreed, within a time window, that the node is not reachable.\n\nThe command counts only failure reports that have not expired. A report expires after twice the node timeout. The count includes only reports that the node receiving the command has received from other nodes. It does not include the receiving node’s own view of the specified node.\n\nUse this command to debug Redis Cluster failure detection when it does not behave as expected."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>node-id</code></summary>\n\nThe ID of the node to count failure reports for.\n\n</details>"},{"id":"details","title":"Details","role":"content","text":"* A node flags another node with `PFAIL` when the node is not reachable for a time greater than the configured node timeout, which is a fundamental configuration parameter of a Redis Cluster.\n* Nodes in `PFAIL` state are provided in gossip sections of heartbeat packets.\n* Every time a node processes gossip packets from other nodes, it creates (and refreshes the TTL if needed)*failure reports, remembering that a given node said another given node is in `PFAIL` condition.\n* Each failure report has a time to live of two times the node timeout time.\n* If at a given time a node has another node flagged with `PFAIL`, and at the same time collected the majority of other master nodes failure reports about this node (including itself if it is a master), then it elevates the failure state of the node from `PFAIL` to `FAIL`, and broadcasts a message forcing all the nodes that can be reached to flag the node as `FAIL`."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of active failure reports for the node.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of active failure reports for the node."}],"examples":[]}
{"schema_version":2,"id":"commands/cluster-countkeysinslot","title":"CLUSTER COUNTKEYSINSLOT","url":"https://redis.io/docs/latest/commands/cluster-countkeysinslot/","summary":"Returns the number of keys in a hash slot.","since":"3.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"e2c4e658fb64fff2bc7b0fd3b6e5f5bfb374910cce5b6c14a6dd52265cebd7e9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns the number of keys in the specified Redis Cluster hash slot. The\ncommand only queries the local data set, so contacting a node\nthat is not serving the specified hash slot will always result in a count of\nzero being returned.\n\n> [!NOTE]\n> During atomic slot migration operations (available since Redis 8.4.0), keys being imported or trimmed will be filtered out from the results."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>slot</code></summary>\n\nThe hash slot to count the keys in.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): The number of keys in the specified hash slot, or an error if the hash slot is invalid.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): The number of keys in the specified hash slot, or an error if the hash slot is invalid."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> CLUSTER COUNTKEYSINSLOT 7000\n(integer) 50341","section_id":"examples"}]}
{"schema_version":2,"id":"commands/cluster-delslots","title":"CLUSTER DELSLOTS","url":"https://redis.io/docs/latest/commands/cluster-delslots/","summary":"Sets hash slots as unbound for a node.","since":"3.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"b7c1edf10b65fe987477776c39c52bc6f3f1e96eb14952e7ff008d3a35ee7c2d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"In Redis Cluster, each node keeps track of which master is serving\na particular hash slot.\n\nThe `CLUSTER DELSLOTS` command asks a particular Redis Cluster node to\nforget which master is serving the hash slots specified as arguments."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>slot [slot ...]</code></summary>\n\nOne or more hash slots to unassign from the current node.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"The following command removes the association for slots 5000 and\n5001 from the node receiving the command:\n\n    > CLUSTER DELSLOTS 5000 5001\n    OK"},{"id":"details","title":"Details","role":"content","text":""},{"id":"usage-in-redis-cluster","title":"Usage in Redis Cluster","role":"syntax","text":"This command only works in cluster mode and may be useful for\ndebugging and in order to manually orchestrate a cluster configuration\nwhen a new cluster is created. It is currently not used by `redis-cli`,\nand mainly exists for API completeness."},{"id":"other-details","title":"Other details","role":"content","text":"In the context of a node that has received a `CLUSTER DELSLOTS` command and\nhas consequently removed the associations for the passed hash slots,\nwe say those hash slots are *unbound*. Note that the existence of\nunbound hash slots occurs naturally when a node has not been\nconfigured to handle them (something that can be done with the\n[`CLUSTER ADDSLOTS`](https://redis.io/docs/latest/commands/cluster-addslots) command) and if it has not received any information about\nwho owns those hash slots (something that it can learn from heartbeat\nor update messages).\n\nIf a node with unbound hash slots receives a heartbeat packet from\nanother node that claims to be the owner of some of those hash\nslots, the association is established instantly. Moreover, if a\nheartbeat or update message is received with a configuration epoch\ngreater than the node's own, the association is re-established.\n\nHowever, note that:\n\n1. The command only works if all the specified slots are already\nassociated with some node.\n2. The command fails if the same slot is specified multiple times.\n3. As a side effect of the command execution, the node may go into\ndown state because not all hash slots are covered."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the command was successful. Otherwise an error is returned.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the command was successful. Otherwise an error is returned."}],"examples":[]}
{"schema_version":2,"id":"commands/cluster-delslotsrange","title":"CLUSTER DELSLOTSRANGE","url":"https://redis.io/docs/latest/commands/cluster-delslotsrange/","summary":"Sets hash slot ranges as unbound for a node.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"98d76b4bd413d461c35208f6bc79b070c44c2459d3afe3a710d0ecd13ac991da","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `CLUSTER DELSLOTSRANGE` command is similar to the [`CLUSTER DELSLOTS`](https://redis.io/docs/latest/commands/cluster-delslots) command in that they both remove hash slots from the node.\nThe difference is that [`CLUSTER DELSLOTS`](https://redis.io/docs/latest/commands/cluster-delslots) takes a list of hash slots to remove from the node, while `CLUSTER DELSLOTSRANGE` takes a list of slot ranges (specified by start and end slots) to remove from the node."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>start-slot end-slot [start-slot end-slot ...]</code></summary>\n\nOne or more inclusive ranges of hash slots to unassign from the current node.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"To remove slots 1 2 3 4 5 from the node, the [`CLUSTER DELSLOTS`](https://redis.io/docs/latest/commands/cluster-delslots) command is:\n\n    > CLUSTER DELSLOTS 1 2 3 4 5\n    OK\n\nThe same operation can be completed with the following `CLUSTER DELSLOTSRANGE` command:\n\n    > CLUSTER DELSLOTSRANGE 1 5\n    OK\n\nHowever, note that:\n\n1. The command only works if all the specified slots are already associated with the node.\n2. The command fails if the same slot is specified multiple times.\n3. As a side effect of the command execution, the node may go into *down* state because not all hash slots are covered."},{"id":"details","title":"Details","role":"content","text":""},{"id":"usage-in-redis-cluster","title":"Usage in Redis Cluster","role":"syntax","text":"This command only works in cluster mode and may be useful for\ndebugging and in order to manually orchestrate a cluster configuration\nwhen a new cluster is created. It is currently not used by `redis-cli`,\nand mainly exists for API completeness."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the command was successful. Otherwise an error is returned.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the command was successful. Otherwise an error is returned."}],"examples":[]}
{"schema_version":2,"id":"commands/cluster-failover","title":"CLUSTER FAILOVER","url":"https://redis.io/docs/latest/commands/cluster-failover/","summary":"Forces a replica to perform a manual failover of its master.","since":"3.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"0a63665bc65f439d4d5de43ef7be2f96c684c974096cdc4280fc2001e160f345","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This command, which can only be sent to a Redis Cluster replica node, forces\nthe replica to start a manual failover of its primary instance.\n\nUse a manual failover when you want to replace the current primary with one of its replicas, even though the primary has not failed. Send the command to the replica that you want to promote. Redis Cluster performs the failover safely and avoids data loss by making sure the replica has processed the primary’s full replication stream before it takes over.\n\nA manual failover works as follows:\n\n1. The replica tells the primary to stop processing client requests.1. The primary sends its current replication offset to the replica.\n1. The replica waits until it reaches the same replication offset, which confirms that it has processed all data from the primary.\n1. The replica starts the failover, gets a new configuration epoch from a majority of primary nodes, and broadcasts the new configuration.\n1. The old primary receives the configuration update, unblocks its clients, and starts returning redirection messages so clients connect to the new primary.\n\nThis process moves clients from the old primary to the new primary only after the new primary has processed the full replication stream."},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>FORCE | TAKEOVER</code></summary>\n\n`FORCE` starts the failover without coordinating with the primary, for use when the primary is unreachable. `TAKEOVER` additionally bypasses the cluster consensus needed to authorize the failover.\n\n</details>"},{"id":"details","title":"Details","role":"content","text":""},{"id":"manual-failover-when-the-primary-is-down","title":"Manual failover when the primary is down","role":"content","text":"The command behavior can be modified by two options: `FORCE` and `TAKEOVER`.\n\nIf the `FORCE` option is given, the replica does not perform any handshake\nwith the primary, that may be not reachable, but instead just starts a\nfailover starting from point 4 above. This is useful when you want to start\na manual failover while the primray is no longer reachable.\n\nHowever, using `FORCE` requires that the majority of primaries are available\nto authorize the failover and generate a new configuration epoch\nfor the replica that is going to become primary."},{"id":"manual-failover-without-cluster-consensus","title":"Manual failover without cluster consensus","role":"content","text":"There are situations where this is not enough, and you want a replica to failover\nwithout any agreement with the rest of the cluster. A real world use case\nfor this is to mass promote replicas in a different data center to primaries\nin order to perform a data center switch, while all the primaries are down\nor partitioned away.\n\nThe `TAKEOVER` option implies everything `FORCE` implies, but also does\nnot use any cluster authorization in order to failover. A replica receiving\n`CLUSTER FAILOVER TAKEOVER` will instead:\n\n1. Generate a new `configEpoch` unilaterally, just taking the current greatest epoch available and incrementing it if its local configuration epoch is not already the greatest.\n2. Assign itself all the hash slots of its primary, and propagate the new configuration to every node which is reachable ASAP, and eventually to every other node.\n\nNote that `TAKEOVER` violates the last-failover-wins principle of Redis Cluster, since the configuration epoch generated by the replica violates the normal generation of configuration epochs in several ways:\n\n1. There is no guarantee that it is actually the higher configuration epoch, since, for example, we can use the `TAKEOVER` option within a minority, nor any message exchange is performed to generate the new configuration epoch.\n2. If we generate a configuration epoch which happens to collide with another instance, eventually our configuration epoch, or the one of another instance with our same epoch, will be moved away using the *configuration epoch collision resolution algorithm*.\n\nBecause of this the `TAKEOVER` option should be used with care."},{"id":"implementation-details-and-notes","title":"Implementation details and notes","role":"content","text":"* `CLUSTER FAILOVER`, unless the `TAKEOVER` option is specified, does not execute a failover synchronously.\n  It only schedules a manual failover, bypassing the failure detection stage.\n* An `OK` reply is no guarantee that the failover will succeed.\n* A replica can only be promoted to a primary if it is known as a replica by a majority of the primaries in the cluster.\n  If the replica is a new node that has just been added to the cluster (for example after upgrading it), it may not yet be known to all the primaries in the cluster.\n  To check that the primaries are aware of a new replica, you can send [`CLUSTER NODES`](https://redis.io/docs/latest/commands/cluster-nodes) or [`CLUSTER REPLICAS`](https://redis.io/docs/latest/commands/cluster-replicas) to each of the primary nodes and check that it appears as a replica, before sending `CLUSTER FAILOVER` to the replica.\n* To check that the failover has actually happened you can use [`ROLE`](https://redis.io/docs/latest/commands/role), `INFO REPLICATION` (which indicates \"role:master\" after successful failover), or [`CLUSTER NODES`](https://redis.io/docs/latest/commands/cluster-nodes) to verify that the state of the cluster has changed sometime after the command was sent.\n* To check if the failover has failed, check the replica's log for `Manual failover timed out`, which is logged if the replica has given up after a few seconds."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the command was accepted and a manual failover is going to be attempted. An error if the operation cannot be executed, for example if the client is connected to a node that is already a primary.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the command was accepted and a manual failover is going to be attempted. An error if the operation cannot be executed, for example if the client is connected to a node that is already a primary."}],"examples":[]}
{"schema_version":2,"id":"commands/cluster-flushslots","title":"CLUSTER FLUSHSLOTS","url":"https://redis.io/docs/latest/commands/cluster-flushslots/","summary":"Deletes all slots information from a node.","since":"3.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"8b64d8674981e694f9a1a8b27960d981f90a5c0981f8a85bd2343412fbf76b15","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Deletes all slots from a node.\n\nThe `CLUSTER FLUSHSLOTS` deletes all information about slots from the connected node. It can only be called when the database is empty."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[]}
{"schema_version":2,"id":"commands/cluster-forget","title":"CLUSTER FORGET","url":"https://redis.io/docs/latest/commands/cluster-forget/","summary":"Removes a node from the nodes table.","since":"3.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-06-15T06:26:34-07:00","page_type":"content","content_hash":"023476ed6b3d11856a344f06158b984117a0e661aabaa6ae9f20a36596b00664","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The command is used to remove a node, specified by its node ID,\nfrom the set of known nodes of the Redis Cluster node receiving the command.\nIn other words, the specified node is removed from the nodes table of the\nnode receiving the command.\n\nBecause when a given node is part of the cluster, all the other nodes\nparticipating in the cluster knows about it, in order for a node to be\ncompletely removed from a cluster, the `CLUSTER FORGET` command must be\nsent to all the remaining nodes, regardless of the fact they are masters\nor replicas.\n\nHowever, the command cannot simply drop the node from the internal node\ntable of the node receiving the command, it also implements a ban list, not\nallowing the same node to be added again as a side effect of processing the\ngossip section of the heartbeat packets received from other nodes.\n\nStarting with Redis 7.2.0, the ban list is included in cluster gossip ping/pong messages. \nThis means that `CLUSTER FORGET` doesn't need to be sent to all nodes in a cluster.\nYou can run the command on one or more nodes, after which it will be propagated to the rest of the nodes in most cases."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>node-id</code></summary>\n\nThe ID of the node to remove from the current node's view of the cluster.\n\n</details>"},{"id":"details","title":"Details","role":"content","text":""},{"id":"details-on-why-the-ban-list-is-needed","title":"Details on why the ban list is needed","role":"content","text":"In the following example we'll show why the command must not just remove\na given node from the nodes table, but also prevent it for being re-inserted\nagain for some time.\n\nLet's assume we have four nodes, A, B, C and D. In order to\nend with just a three nodes cluster A, B, C we may follow these steps:\n\n1. Reshard all the hash slots from D to nodes A, B, C.\n2. D is now empty, but still listed in the nodes table of A, B and C.\n3. We contact A, and send `CLUSTER FORGET D`.\n4. B sends node A a heartbeat packet, where node D is listed.\n5. A does no longer known node D (see step 3), so it starts a handshake with D.\n6. D ends re-added in the nodes table of A.\n\nAs you can see in this way removing a node is fragile, we need to send\n`CLUSTER FORGET` commands to all the nodes ASAP hoping there are no\ngossip sections processing in the meantime. Because of this problem the\ncommand implements a ban list with an expire time for each entry.\n\nSo what the command really does is:\n\n1. The specified node gets removed from the nodes table.\n2. The node ID of the removed node gets added to the ban list, for 1 minute.\n3. The node will skip all the node IDs listed in the ban list when processing gossip sections received in heartbeat packets from other nodes.\n\nThis way we have a 60 second window to inform all the nodes in the cluster that\nwe want to remove a node."},{"id":"special-conditions-not-allowing-the-command-execution","title":"Special conditions not allowing the command execution","role":"content","text":"The command does not succeed and returns an error in the following cases:\n\n1. The specified node ID is not found in the nodes table.\n2. The node receiving the command is a replica, and the specified node ID identifies its current master.\n3. The node ID identifies the same node we are sending the command to."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the command was executed successfully. Otherwise an error is returned.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the command was executed successfully. Otherwise an error is returned."}],"examples":[]}
{"schema_version":2,"id":"commands/cluster-getkeysinslot","title":"CLUSTER GETKEYSINSLOT","url":"https://redis.io/docs/latest/commands/cluster-getkeysinslot/","summary":"Returns the key names in a hash slot.","since":"3.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"b942ac26e559c219e0d09289c900104619c91d22352c395389e81f696ee14458","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The command returns key names stored on the contacted node that hash to the specified `slot`. Use the `count` argument to limit the number of keys returned, which lets you process keys in batches.\n\nThis command is mainly used when you reshard cluster slots from one node to another. For details, see the [Redis Cluster specification](https://redis.io/docs/latest/operate/oss_and_stack/reference/cluster-spec) or the appendix in the [`CLUSTER SETSLOT`](https://redis.io/docs/latest/commands/cluster-setslot) documentation.\n\n> [!NOTE]\n> During atomic slot migration operations (available since Redis 8.4.0), keys being imported or trimmed will be filtered out from the results."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>slot</code></summary>\n\nThe hash slot to return key names from.\n\n</details>\n\n<details open><summary><code>count</code></summary>\n\nThe maximum number of key names to return.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): an array with up to count elements.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): an array with up to count elements."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> CLUSTER GETKEYSINSLOT 7000 3\n1) \"key_39015\"\n2) \"key_89793\"\n3) \"key_92937\"","section_id":"examples"}]}
{"schema_version":2,"id":"commands/cluster-help","title":"CLUSTER HELP","url":"https://redis.io/docs/latest/commands/cluster-help/","summary":"Returns helpful text about the different subcommands.","since":"5.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"3233e978bf8146cdb869d9fae7351bac90d099aef6e09b670a7257811864f424","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `CLUSTER HELP` command returns a helpful text describing the different subcommands."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Only supported with the [OSS cluster API](https://redis.io/docs/latest/operate/rs/databases/configure/oss-cluster-api). |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of subcommands and their descriptions.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of subcommands and their descriptions."}],"examples":[]}
{"schema_version":2,"id":"commands/cluster-info","title":"CLUSTER INFO","url":"https://redis.io/docs/latest/commands/cluster-info/","summary":"Returns information about the state of a node.","since":"3.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"25ea935030d5356b37ae79893584e8b5b8bfd6b7ae6b04f6ef670a0f44c32343","sections":[{"id":"overview","title":"Overview","role":"overview","text":"`CLUSTER INFO` provides [`INFO`](https://redis.io/docs/latest/commands/info) style information about Redis Cluster vital parameters.\nThe following fields are always present in the reply:\n\n[code example]\n\n* `cluster_state`: State is `ok` if the node is able to receive queries. `fail` if there is at least one hash slot which is unbound (no node associated), in error state (node serving it is flagged with FAIL flag), or if the majority of masters can't be reached by this node.\n* `cluster_slots_assigned`: Number of slots which are associated to some node (not unbound). This number should be 16384 for the node to work properly, which means that each hash slot should be mapped to a node.\n* `cluster_slots_ok`: Number of hash slots mapping to a node not in `FAIL` or `PFAIL` state.\n* `cluster_slots_pfail`: Number of hash slots mapping to a node in `PFAIL` state. Note that those hash slots still work correctly, as long as the `PFAIL` state is not promoted to `FAIL` by the failure detection algorithm. `PFAIL` only means that we are currently not able to talk with the node, but may be just a transient error.\n* `cluster_slots_fail`: Number of hash slots mapping to a node in `FAIL` state. If this number is not zero the node is not able to serve queries unless `cluster-require-full-coverage` is set to `no` in the configuration.\n* `cluster_known_nodes`: The total number of known nodes in the cluster, including nodes in `HANDSHAKE` state that may not currently be proper members of the cluster.\n* `cluster_size`: The number of master nodes serving at least one hash slot in the cluster.\n* `cluster_current_epoch`: The local `Current Epoch` variable. This is used in order to create unique increasing version numbers during fail overs.\n* `cluster_my_epoch`: The `Config Epoch` of the node we are talking with. This is the current configuration version assigned to this node.\n* `cluster_stats_messages_sent`: Number of messages sent via the cluster node-to-node binary bus.\n* `cluster_stats_messages_received`: Number of messages received via the cluster node-to-node binary bus.\n* `total_cluster_links_buffer_limit_exceeded`: Accumulated count of cluster links freed due to exceeding the `cluster-link-sendbuf-limit` configuration.\n\nThe following atomic slot migration fields may be included in the reply (available since Redis 8.4.0):\n\n* `cluster_slot_migration_active_tasks`: Number of in-progress ASM tasks. Currently, it will be 1 or 0.\n* `cluster_slot_migration_active_trim_running`: Number of active trim jobs in progress and scheduled.\n* `cluster_slot_migration_active_trim_current_job_keys`: Number of keys scheduled for deletion in the current trim job.\n* `cluster_slot_migration_active_trim_current_job_trimmed`: Number of keys already deleted in the current trim job.\n* `cluster_slot_migration_stats_active_trim_started`: Total number of trim jobs that have started since the process began.\n* `cluster_slot_migration_stats_active_trim_completed`: Total number of trim jobs completed since the process began.\n* `cluster_slot_migration_stats_active_trim_cancelled`: Total number of trim jobs cancelled since the process began.\n\nThe following message-related fields may be included in the reply if the value is not 0:\nEach message type includes statistics on the number of messages sent and received.\nHere are the explanation of these fields:\n\n* `cluster_stats_messages_ping_sent` and `cluster_stats_messages_ping_received`: Cluster bus PING (not to be confused with the client command [`PING`](https://redis.io/docs/latest/commands/ping)).\n* `cluster_stats_messages_pong_sent` and `cluster_stats_messages_pong_received`: PONG (reply to PING).\n* `cluster_stats_messages_meet_sent` and `cluster_stats_messages_meet_received`: Handshake message sent to a new node, either through gossip or [`CLUSTER MEET`](https://redis.io/docs/latest/commands/cluster-meet).\n* `cluster_stats_messages_fail_sent` and `cluster_stats_messages_fail_received`: Mark node xxx as failing.\n* `cluster_stats_messages_publish_sent` and `cluster_stats_messages_publish_received`: Pub/Sub Publish propagation, see [Pubsub](https://redis.io/docs/latest/develop/pubsub#pubsub).\n* `cluster_stats_messages_auth-req_sent` and `cluster_stats_messages_auth-req_received`: Replica initiated leader election to replace its master.\n* `cluster_stats_messages_auth-ack_sent` and `cluster_stats_messages_auth-ack_received`: Message indicating a vote during leader election.\n* `cluster_stats_messages_update_sent` and `cluster_stats_messages_update_received`: Another node slots configuration.\n* `cluster_stats_messages_mfstart_sent` and `cluster_stats_messages_mfstart_received`: Pause clients for manual failover.\n* `cluster_stats_messages_module_sent` and `cluster_stats_messages_module_received`: Module cluster API message.\n* `cluster_stats_messages_publishshard_sent` and `cluster_stats_messages_publishshard_received`: Pub/Sub Publish shard propagation, see [Sharded Pubsub](https://redis.io/docs/latest/develop/pubsub#sharded-pubsub).\n\nMore information about the Current Epoch and Config Epoch variables are available in the [Redis Cluster specification document](https://redis.io/docs/latest/operate/oss_and_stack/reference/cluster-spec#cluster-current-epoch)."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Only supported with the [OSS cluster API](https://redis.io/docs/latest/operate/rs/databases/configure/oss-cluster-api). |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): A map between named fields and values in the form of `<field>:<value>` lines separated by newlines composed by the two bytes `CRLF`.\n\n**RESP3:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): A map between named fields and values in the form of <field>:<value> lines separated by newlines composed by the two bytes CRLF"}],"examples":[{"id":"overview-ex0","language":"plaintext","code":"cluster_state:ok\ncluster_slots_assigned:16384\ncluster_slots_ok:16384\ncluster_slots_pfail:0\ncluster_slots_fail:0\ncluster_known_nodes:6\ncluster_size:3\ncluster_current_epoch:6\ncluster_my_epoch:2\ncluster_stats_messages_sent:1483972\ncluster_stats_messages_received:1483968\ntotal_cluster_links_buffer_limit_exceeded:0","section_id":"overview"}]}
{"schema_version":2,"id":"commands/cluster-keyslot","title":"CLUSTER KEYSLOT","url":"https://redis.io/docs/latest/commands/cluster-keyslot/","summary":"Returns the hash slot for a key.","since":"3.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"6805ebcbe91d62c86c8003b4a62bdd4d4816973cf39cce3fd6c74ac44a4af843","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns an integer identifying the hash slot the specified key hashes to.\nThis command is mainly useful for debugging and testing, since it exposes\nvia an API the underlying Redis implementation of the hashing algorithm.\nExample use cases for this command:\n\n1. Client libraries may use Redis in order to test their own hashing algorithm, generating random keys and hashing them with both their local implementation and using Redis `CLUSTER KEYSLOT` command, then checking if the result is the same.\n2. Humans may use this command in order to check what is the hash slot, and then the associated Redis Cluster node, responsible for a given key.\n\nThe command uses the full [Redis Cluster hashing algorithm](https://redis.io/docs/latest/operate/oss_and_stack/reference/cluster-spec#key-distribution-model), including support for hash tags. If a key contains a valid hash tag, Redis hashes only the part of the key between `{` and `}`. You can use hash tags to force multiple keys into the same hash slot so they are handled by the same node."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe key name to compute the hash slot for.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Only supported with the [OSS cluster API](https://redis.io/docs/latest/operate/rs/databases/configure/oss-cluster-api). |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): The hash slot number for the specified key\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): The hash slot number for the specified key"}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> CLUSTER KEYSLOT somekey\n(integer) 11058\n> CLUSTER KEYSLOT foo{hash_tag}\n(integer) 2515\n> CLUSTER KEYSLOT bar{hash_tag}\n(integer) 2515","section_id":"examples"}]}
{"schema_version":2,"id":"commands/cluster-links","title":"CLUSTER LINKS","url":"https://redis.io/docs/latest/commands/cluster-links/","summary":"Returns a list of all TCP links to and from peer nodes.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"208e0e57f5fa87751a869a14a215245b4792c5c4bf5680717239827f9db8f0d9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Each node in a Redis Cluster maintains a pair of long-lived TCP link with each peer in the cluster: One for sending outbound messages towards the peer and one for receiving inbound messages from the peer.\n\n`CLUSTER LINKS` outputs information of all such peer links as an array, where each array element is a map that contains attributes and their values for an individual link."},{"id":"examples","title":"Examples","role":"example","text":"The following is an example output:\n\n[code example]\n\nEach map is composed of the following attributes of the corresponding cluster link and their values:\n\n1. `direction`: This link is established by the local node `to` the peer, or accepted by the local node `from` the peer.\n2. `node`: The node id of the peer.\n3. `create-time`: Creation time of the link. (In the case of a `to` link, this is the time when the TCP link is created by the local node, not the time when it is actually established.)\n4. `events`: Events currently registered for the link. `r` means readable event, `w` means writable event.\n5. `send-buffer-allocated`: Allocated size of the link's send buffer, which is used to buffer outgoing messages toward the peer.\n6. `send-buffer-used`: Size of the portion of the link's send buffer that is currently holding data(messages)."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): an array of maps where each map contains various attributes and their values of a cluster link.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): an array of [Map reply](../../develop/reference/protocol-spec#maps) where each map contains various attributes and their values of a cluster link."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> CLUSTER LINKS\n1)  1) \"direction\"\n    2) \"to\"\n    3) \"node\"\n    4) \"8149d745fa551e40764fecaf7cab9dbdf6b659ae\"\n    5) \"create-time\"\n    6) (integer) 1639442739375\n    7) \"events\"\n    8) \"rw\"\n    9) \"send-buffer-allocated\"\n   10) (integer) 4512\n   11) \"send-buffer-used\"\n   12) (integer) 0\n2)  1) \"direction\"\n    2) \"from\"\n    3) \"node\"\n    4) \"8149d745fa551e40764fecaf7cab9dbdf6b659ae\"\n    5) \"create-time\"\n    6) (integer) 1639442739411\n    7) \"events\"\n    8) \"r\"\n    9) \"send-buffer-allocated\"\n   10) (integer) 0\n   11) \"send-buffer-used\"\n   12) (integer) 0","section_id":"examples"}]}
{"schema_version":2,"id":"commands/cluster-meet","title":"CLUSTER MEET","url":"https://redis.io/docs/latest/commands/cluster-meet/","summary":"Forces a node to handshake with another node.","since":"3.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"645e085786d320c6460ff5c12f5511836ddeb159f1da1a3c18f12448d52a5488","sections":[{"id":"overview","title":"Overview","role":"overview","text":"`CLUSTER MEET` is used in order to connect different Redis nodes with cluster\nsupport enabled, into a working cluster.\n\nThe basic idea is that nodes by default don't trust each other, and are\nconsidered unknown, so that it is unlikely that different cluster nodes will\nmix into a single one because of system administration errors or network\naddresses modifications.\n\nSo in order for a given node to accept another one into the list of nodes\ncomposing a Redis Cluster, there are only two ways:\n\n1. The system administrator sends a `CLUSTER MEET` command to force a node to meet another one.\n2. An already known node sends a list of nodes in the gossip section that we are not aware of. If the receiving node trusts the sending node as a known node, it will process the gossip section and send a handshake to the nodes that are still not known.\n\nNote that Redis Cluster needs to form a full mesh (each node is connected with each other node), but in order to create a cluster, there is no need to send all the `CLUSTER MEET` commands needed to form the full mesh. What matter is to send enough `CLUSTER MEET` messages so that each node can reach each other node through a *chain of known nodes*. Thanks to the exchange of gossip information in heartbeat packets, the missing links will be created.\n\nSo, if we link node A with node B via `CLUSTER MEET`, and B with C, A and C will find their ways to handshake and create a link.\n\nAnother example: if we imagine a cluster formed of the following four nodes called A, B, C and D, we may send just the following set of commands to A:\n\n1. `CLUSTER MEET B-ip B-port`\n2. `CLUSTER MEET C-ip C-port`\n3. `CLUSTER MEET D-ip D-port`\n\nAs a side effect of `A` knowing and being known by all the other nodes, it will send gossip sections in the heartbeat packets that will allow each other node to create a link with each other one, forming a full mesh in a matter of seconds, even if the cluster is large.\n\nMoreover `CLUSTER MEET` does not need to be reciprocal. If I send the command to A in order to join B, I don't need to also send it to B in order to join A.\n\nIf the optional `cluster_bus_port` argument is not provided, the default of client port number + 10,000 will be used."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>ip</code></summary>\n\nThe IP address of the node to add to the cluster.\n\n</details>\n\n<details open><summary><code>port</code></summary>\n\nThe client port of the node to add.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>cluster-bus-port</code></summary>\n\nThe cluster bus port of the node. If omitted, it defaults to the client port number plus 10,000.\n\n</details>"},{"id":"details","title":"Details","role":"content","text":""},{"id":"implementation-meet-and-ping-packets","title":"Implementation: MEET and PING packets","role":"content","text":"When you send CLUSTER MEET to a node, the node specified in the command does not yet know the receiving node. To establish trust, the receiving node sends a MEET packet to the specified node instead of a [PING](https://redis.io/docs/latest/commands/ping) packet. The two packets have the same format, but MEET forces the specified node to trust the sender."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the command was successful. If the address or port specified are invalid an error is returned.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the command was successful. If the address or port specified are invalid an error is returned."}],"examples":[]}
{"schema_version":2,"id":"commands/cluster-migration","title":"CLUSTER MIGRATION","url":"https://redis.io/docs/latest/commands/cluster-migration/","summary":"Start, monitor, and cancel atomic slot migration tasks.","since":"8.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"48df19f928ece760f4518146c13ea6522c63d90ea43f1fe7fbe152a4be769c54","sections":[{"id":"overview","title":"Overview","role":"overview","text":"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."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>subcommand</code></summary>\n\nThe subcommand specifies the operation to perform:\n\n- `IMPORT start-slot end-slot [start-slot end-slot ...]`: 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.\n\n- `CANCEL ID task-id | 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.\n\n- `STATUS [ID task-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.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"Import slots 0-1000 and 2000-3000 to the current node:\n\n[code example]\n\nCheck the status of all migration tasks:\n\n[code example]\n\nCheck the status of a specific migration task:\n\n[code example]\n\nCancel a specific migration task:\n\n[code example]\n\nCancel all migration tasks:\n\n[code example]"},{"id":"details","title":"Details","role":"content","text":"- Cancelling a task on the source node does not automatically stop the migration on the destination node.\n- In `CLUSTER MIGRATION STATUS` output, the \"state\" field will show `completed` for successful operations.\n- Tasks with empty \"last_error\" fields indicate no errors occurred during the migration process."},{"id":"key-visibility-during-migration","title":"Key visibility during migration","role":"content","text":"During atomic slot migration operations, keys in unowned slots may be filtered out from the following commands while importing or trimming is in progress:\n\n- [`KEYS`](https://redis.io/docs/latest/commands/keys)\n- [`SCAN`](https://redis.io/docs/latest/commands/scan)\n- [`RANDOMKEY`](https://redis.io/docs/latest/commands/randomkey)\n- [`CLUSTER GETKEYSINSLOT`](https://redis.io/docs/latest/commands/cluster-getkeysinslot)\n- [`DBSIZE`](https://redis.io/docs/latest/commands/dbsize)\n- [`CLUSTER COUNTKEYSINSLOT`](https://redis.io/docs/latest/commands/cluster-countkeysinslot)\n\nThe [`INFO KEYSPACE`](https://redis.io/docs/latest/commands/info) command will continue to reflect the actual number of keys, including those being imported."},{"id":"related-configuration","title":"Related configuration","role":"related","text":"- `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).\n- `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)."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nFor the `IMPORT` subcommand:\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): task ID on success, or error message on failure.\n\nFor the `CANCEL` subcommand:\n[Integer reply](../../develop/reference/protocol-spec#integers): number of cancelled tasks.\n\nFor the `STATUS` subcommand:\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of migration task details. Each task is represented as an array containing field-value pairs:\n- `id`: Task identifier\n- `slots`: Slot range being imported or migrated\n- `source`: Source node ID\n- `dest`: Destination node ID\n- `operation`: Operation type (\"import\" or \"migrate\")\n- `state`: Current state (\"completed\", \"in_progress\", etc.)\n- `last_error`: Last error message (empty if none)\n- `retries`: Number of retry attempts\n- `create_time`: Task creation timestamp\n- `start_time`: Task start timestamp\n- `end_time`: Task completion timestamp (if completed)\n- `write_pause_ms`: Write pause duration in milliseconds\n\n**RESP3:**\n\nFor the `IMPORT` subcommand:\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): task ID on success, or error message on failure.\n\nFor the `CANCEL` subcommand:\n[Integer reply](../../develop/reference/protocol-spec#integers): number of cancelled tasks.\n\nFor the `STATUS` subcommand:\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of migration task details. Each task is represented as an array containing field-value pairs:\n- `id`: Task identifier\n- `slots`: Slot range being migrated\n- `source`: Source node ID\n- `dest`: Destination node ID\n- `operation`: Operation type (typically \"migrate\")\n- `state`: Current state (\"completed\", \"in_progress\", etc.)\n- `last_error`: Last error message (empty if none)\n- `retries`: Number of retry attempts\n- `create_time`: Task creation timestamp\n- `start_time`: Task start timestamp\n- `end_time`: Task completion timestamp (if completed)\n- `write_pause_ms`: Write pause duration in milliseconds"}],"examples":[{"id":"examples-ex0","language":"bash","code":"CLUSTER MIGRATION IMPORT 0 1000 2000 3000","section_id":"examples"},{"id":"examples-ex1","language":"bash","code":"CLUSTER MIGRATION STATUS ALL","section_id":"examples"},{"id":"examples-ex2","language":"bash","code":"CLUSTER MIGRATION STATUS ID 24cf41718b20f7f05901743dffc40bc9b15db339","section_id":"examples"},{"id":"examples-ex3","language":"bash","code":"CLUSTER MIGRATION CANCEL ID 24cf41718b20f7f05901743dffc40bc9b15db339","section_id":"examples"},{"id":"examples-ex4","language":"bash","code":"CLUSTER MIGRATION CANCEL ALL","section_id":"examples"}]}
{"schema_version":2,"id":"commands/cluster-myid","title":"CLUSTER MYID","url":"https://redis.io/docs/latest/commands/cluster-myid/","summary":"Returns the ID of a node.","since":"3.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"e1704f69c7d76cdb3b8301bf7886a792bacf4a9039244061b7f6a28d01f2ac58","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns the node's id.\n\nThe `CLUSTER MYID` command returns the unique, auto-generated identifier that is associated with the connected cluster node."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the node ID.\n\n**RESP3:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the node ID."}],"examples":[]}
{"schema_version":2,"id":"commands/cluster-myshardid","title":"CLUSTER MYSHARDID","url":"https://redis.io/docs/latest/commands/cluster-myshardid/","summary":"Returns the shard ID of a node.","since":"7.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"0f222e7c91342f53d277e9aba7be0285983c9c5fdb07dc133520d72865908c19","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns the node's shard id.\n\nThe `CLUSTER MYSHARDID` command returns the unique, auto-generated identifier that is associated with the shard to which the connected cluster node belongs."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the node's shard ID.\n\n**RESP3:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the node's shard ID."}],"examples":[]}
{"schema_version":2,"id":"commands/cluster-nodes","title":"CLUSTER NODES","url":"https://redis.io/docs/latest/commands/cluster-nodes/","summary":"Returns the cluster configuration for a node.","since":"3.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"1bcc7d8ea9ae01e8edd20bfc9e0abbae7af72a7a152c206d00e965dde59391cc","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Each node in a Redis Cluster has its view of the current cluster configuration,\ngiven by the set of known nodes, the state of the connection we have with such\nnodes, their flags, properties and assigned slots, and so forth.\n\n`CLUSTER NODES` provides all this information, that is, the current cluster\nconfiguration of the node we are contacting, in a serialization format which\nhappens to be exactly the same as the one used by Redis Cluster itself in\norder to store on disk the cluster state (however the on disk cluster state\nhas a few additional info appended at the end).\n\nNote that normally clients willing to fetch the map between Cluster\nhash slots and node addresses should use [`CLUSTER SHARDS`](https://redis.io/docs/latest/commands/cluster-shards) instead.\n`CLUSTER NODES`, that provides more information, should be used for\nadministrative tasks, debugging, and configuration inspections.\nIt is also used by `redis-cli` in order to manage a cluster."},{"id":"serialization-format","title":"Serialization format","role":"content","text":"The output of the command is just a space-separated CSV string, where\neach line represents a node in the cluster. The following\nis an example of output on Redis 7.2.0.\n\n[code example]\n\nEach line is composed of the following fields:\n\n[code example]\n\nThe meaning of each field is the following:\n\n1. `id`: The node ID, a 40-character globally unique string generated when a node is created and never changed again (unless `CLUSTER RESET HARD` is used).\n2. `ip:port@cport`: The node address that clients should contact to run queries, along with the used cluster bus port.\n   `:0@0` can be expected when the address is no longer known for this node ID, hence flagged with `noaddr`.\n3. `hostname`: A human readable string that can be configured via the `cluster-annouce-hostname` setting. The max length of the string is 256 characters, excluding the null terminator. The name can contain ASCII alphanumeric characters, '-', and '.' only.\n5. `flags`: A list of comma separated flags: `myself`, `master`, `slave`, `fail?`, `fail`, `handshake`, `noaddr`, `nofailover`, `noflags`. Flags are explained below.\n6. `master`: If the node is a replica, and the primary is known, the primary node ID, otherwise the \"-\" character.\n7. `ping-sent`: Unix time at which the currently active ping was sent, or zero if there are no pending pings, in milliseconds.\n8. `pong-recv`: Unix time the last pong was received, in milliseconds.\n9. `config-epoch`: The configuration epoch (or version) of the current node (or of the current primary if the node is a replica). Each time there is a failover, a new, unique, monotonically increasing configuration epoch is created. If multiple nodes claim to serve the same hash slots, the one with the higher configuration epoch wins.\n10. `link-state`: The state of the link used for the node-to-node cluster bus. Use this link to communicate with the node. Can be `connected` or `disconnected`.\n11. `slot`: A hash slot number or range. Starting from argument number 9, but there may be up to 16384 entries in total (limit never reached). This is the list of hash slots served by this node. If the entry is just a number, it is parsed as such.  If it is a range, it is in the form `start-end`, and means that the node is responsible for all the hash slots from `start` to `end` including the start and end values.\n\nFlags are:\n\n* `myself`: The node you are contacting.\n* `master`: Node is a primary.\n* `slave`: Node is a replica.\n* `fail?`: Node is in `PFAIL` state. Not reachable for the node you are contacting, but still logically reachable (not in `FAIL` state).\n* `fail`: Node is in `FAIL` state. It was not reachable for multiple nodes that promoted the `PFAIL` state to `FAIL`.\n* `handshake`: Untrusted node, we are handshaking.\n* `noaddr`: No address known for this node.\n* `nofailover`: Replica will not try to failover.\n* `noflags`: No flags at all."},{"id":"notes-on-published-config-epochs","title":"Notes on published config epochs","role":"content","text":"Replicas broadcast their primary's config epochs (in order to get an `UPDATE`\nmessage if they are found to be stale), so the real config epoch of the\nreplica (which is meaningless more or less, since they don't serve hash slots)\ncan be only obtained checking the node flagged as `myself`, which is the entry\nof the node we are asking to generate `CLUSTER NODES` output. The other\nreplicas epochs reflect what they publish in heartbeat packets, which is, the\nconfiguration epoch of the primaries they are currently replicating."},{"id":"special-slot-entries","title":"Special slot entries","role":"content","text":"Normally hash slots associated to a given node are in one of the following formats,\nas already explained above:\n\n1. Single number: 3894\n2. Range: 3900-4000\n\nHowever node hash slots can be in a special state, used in order to communicate errors after a node restart (mismatch between the keys in the AOF/RDB file, and the node hash slots configuration), or when there is a resharding operation in progress. This two states are **importing** and **migrating**.\n\nThe meaning of the two states is explained in the Redis Specification, however the gist of the two states is the following:\n\n* **Importing** slots are yet not part of the nodes hash slot, there is a migration in progress. The node will accept queries about these slots only if the `ASK` command is used.\n* **Migrating** slots are assigned to the node, but are being migrated to some other node. The node will accept queries if all the keys in the command exist already, otherwise it will emit what is called an **ASK redirection**, to force new keys creation directly in the importing node.\n\nImporting and migrating slots are emitted in the `CLUSTER NODES` output as follows:\n\n* **Importing slot:** `[slot_number-<-importing_from_node_id]`\n* **Migrating slot:** `[slot_number->-migrating_to_node_id]`\n\nThe following are a few examples of importing and migrating slots:\n\n* `[93-<-292f8b365bb7edb5e285caf0b7e6ddc7265d2f4f]`\n* `[1002-<-67ed2db8d677e59ec4a4cefb06858cf2a1a89fa1]`\n* `[77->-e7d1eecce10fd6bb5eb35b9f99a514335d9ba9ca]`\n* `[16311->-292f8b365bb7edb5e285caf0b7e6ddc7265d2f4f]`\n\nNote that the format does not have any space, so `CLUSTER NODES` output format is plain CSV with space as separator even when this special slots are emitted. However a complete parser for the format should be able to handle them.\n\nNote that:\n\n1. Migration and importing slots are only added to the node flagged as `myself`. This information is local to a node, for its own slots.\n2. Importing and migrating slots are provided as **additional info**. If the node has a given hash slot assigned, it will be also a plain number in the list of hash slots, so clients that don't have a clue about hash slots migrations can just skip this special fields.\n\n**A note about the word slave used in this man page and command name**: Starting with Redis 5, if not for backward compatibility, the Redis project no longer uses the word slave. Unfortunately in this command the word slave is part of the protocol, so we'll be able to remove such occurrences only when this API will be naturally deprecated."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Only supported with the [OSS cluster API](https://redis.io/docs/latest/operate/rs/databases/configure/oss-cluster-api). |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the serialized cluster configuration.\n\n**RESP3:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the serialized cluster configuration."}],"examples":[{"id":"serialization-format-ex0","language":"plaintext","code":"07c37dfeb235213a872192d90877d0cd55635b91 127.0.0.1:30004@31004,hostname4 slave e7d1eecce10fd6bb5eb35b9f99a514335d9ba9ca 0 1426238317239 4 connected\n67ed2db8d677e59ec4a4cefb06858cf2a1a89fa1 127.0.0.1:30002@31002,hostname2 master - 0 1426238316232 2 connected 5461-10922\n292f8b365bb7edb5e285caf0b7e6ddc7265d2f4f 127.0.0.1:30003@31003,hostname3 master - 0 1426238318243 3 connected 10923-16383\n6ec23923021cf3ffec47632106199cb7f496ce01 127.0.0.1:30005@31005,hostname5 slave 67ed2db8d677e59ec4a4cefb06858cf2a1a89fa1 0 1426238316232 5 connected\n824fe116063bc5fcf9f4ffd895bc17aee7731ac3 127.0.0.1:30006@31006,hostname6 slave 292f8b365bb7edb5e285caf0b7e6ddc7265d2f4f 0 1426238317741 6 connected\ne7d1eecce10fd6bb5eb35b9f99a514335d9ba9ca 127.0.0.1:30001@31001,hostname1 myself,master - 0 0 1 connected 0-5460","section_id":"serialization-format"},{"id":"serialization-format-ex1","language":"plaintext","code":"<id> <ip:port@cport[,hostname]> <flags> <master> <ping-sent> <pong-recv> <config-epoch> <link-state> <slot> <slot> ... <slot>","section_id":"serialization-format"}]}
{"schema_version":2,"id":"commands/cluster-replicas","title":"CLUSTER REPLICAS","url":"https://redis.io/docs/latest/commands/cluster-replicas/","summary":"Lists the replica nodes of a master node.","since":"5.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"c293805f3c9db26cb494f438f3a550e99c1f1e56d2715c4f0a3c8669381c8044","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The command provides a list of replica nodes replicating from the specified\nmaster node. The list is provided in the same format used by [`CLUSTER NODES`](https://redis.io/docs/latest/commands/cluster-nodes) (please refer to its documentation for the specification of the format).\n\nThe command will fail if the specified node is not known or if it is not\na master according to the node table of the node receiving the command.\n\nNote that if a replica is added, moved, or removed from a given master node,\nand we ask `CLUSTER REPLICAS` to a node that has not yet received the\nconfiguration update, it may show stale information. However eventually\n(in a matter of seconds if there are no network partitions) all the nodes\nwill agree about the set of nodes associated with a given master."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>node-id</code></summary>\n\nThe ID of the master node to list the replicas of.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of replica nodes replicating from the specified master node provided in the same format used by `CLUSTER NODES`.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of replica nodes replicating from the specified master node provided in the same format used by `CLUSTER NODES`."}],"examples":[]}
{"schema_version":2,"id":"commands/cluster-replicate","title":"CLUSTER REPLICATE","url":"https://redis.io/docs/latest/commands/cluster-replicate/","summary":"Configure a node as replica of a master node.","since":"3.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-06-15T06:26:34-07:00","page_type":"content","content_hash":"1abf0bb0a17fe526e823c4c58549cd9e48c7d66b50ced58a0b5c0c6073dd21d6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The command reconfigures a node as a replica of the specified master.\nIf the node receiving the command is an *empty master*, as a side effect\nof the command, the node role is changed from master to replica.\n\nOnce a node is turned into the replica of another master node, there is no need\nto inform the other cluster nodes about the change: heartbeat packets exchanged\nbetween nodes will propagate the new configuration automatically.\n\nA replica will always accept the command, assuming that:\n\n1. The specified node ID exists in its nodes table.\n2. The specified node ID does not identify the instance we are sending the command to.\n3. The specified node ID is a master.\n\nIf the node receiving the command is not already a replica, but is a master,\nthe command will only succeed, and the node will be converted into a replica,\nonly if the following additional conditions are met:\n\n1. The node is not serving any hash slots.\n2. The node is empty, no keys are stored at all in the key space.\n\nIf the command succeeds the new replica will immediately try to contact its master in order to replicate from it."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>node-id</code></summary>\n\nThe ID of the master node that the current node should replicate.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the command was successful. Otherwise an error is returned.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the command was successful. Otherwise an error is returned."}],"examples":[]}
{"schema_version":2,"id":"commands/cluster-reset","title":"CLUSTER RESET","url":"https://redis.io/docs/latest/commands/cluster-reset/","summary":"Resets a node.","since":"3.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"7b6be2b4c9ee15a216936b8b25d8b33abbc30788b6929f5d7d65a1e22efad656","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Use `CLUSTER RESET` to reset a Redis Cluster node. Specify `SOFT` or `HARD` to choose the reset type.\n\nBefore you reset a primary node, remove all keys from it, for example with [FLUSHALL](https://redis.io/docs/latest/commands/flushall). Redis does not reset primary nodes that contain keys.\n\nEffects on the node:\n\n1. All the other nodes in the cluster are forgotten.\n2. All the assigned / open slots are reset, so the slots-to-nodes mapping is totally cleared.\n3. If the node is a replica its dataset is flushed, transforming the node to an empty master.\n4. `HARD` reset only: a new Node ID is generated.\n5. `HARD` reset only: `currentEpoch` and `configEpoch` variables are set to 0.\n6. The new configuration is persisted on disk in the node cluster configuration file.\n\nThis command is mainly useful to reprovision a Redis Cluster node\nin order to be used in the context of a new, different cluster. The command\nis also extensively used by the Redis Cluster testing framework in order to\nreset the state of the cluster every time a new test unit is executed.\n\nIf no reset type is specified, the default is **soft**."},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>HARD | SOFT</code></summary>\n\n`SOFT` (the default) resets the node but keeps its node ID; `HARD` also assigns a new node ID and clears additional state.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the command was successful. Otherwise an error is returned.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the command was successful. Otherwise an error is returned."}],"examples":[]}
{"schema_version":2,"id":"commands/cluster-saveconfig","title":"CLUSTER SAVECONFIG","url":"https://redis.io/docs/latest/commands/cluster-saveconfig/","summary":"Forces a node to save the cluster configuration to disk.","since":"3.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"4fc00084efcec112d5f914e3947bb4ddb29044c5fa83ab9ce8013fd1a3ccdb60","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Forces a node to save the `nodes.conf` configuration on disk. Before to return\nthe command calls `fsync(2)` in order to make sure the configuration is\nflushed on the computer disk.\n\nThis command is mainly used in the event a `nodes.conf` node state file\ngets lost / deleted for some reason, and we want to generate it again from\nscratch. It can also be useful in case of mundane alterations of a node cluster\nconfiguration via the [`CLUSTER`](https://redis.io/docs/latest/commands/cluster) command in order to ensure the new configuration\nis persisted on disk, however all the commands should normally be able to\nauto schedule to persist the configuration on disk when it is important\nto do so for the correctness of the system in the event of a restart."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the command was successful. Otherwise an error is returned.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the command was successful. Otherwise an error is returned."}],"examples":[]}
{"schema_version":2,"id":"commands/cluster-set-config-epoch","title":"CLUSTER SET-CONFIG-EPOCH","url":"https://redis.io/docs/latest/commands/cluster-set-config-epoch/","summary":"Sets the configuration epoch for a new node.","since":"3.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-06-15T06:26:34-07:00","page_type":"content","content_hash":"b398161e23591bba275233b4a1b5be56a5e1b88494842341b79394e9645c1223","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This command sets a specific *config epoch* in a fresh node. It only works when:\n\n1. The nodes table of the node is empty.\n2. The node current *config epoch* is zero.\n\nThese prerequisites are needed since usually, manually altering the\nconfiguration epoch of a node is unsafe, we want to be sure that the node with\nthe higher configuration epoch value (that is the last that failed over) wins\nover other nodes in claiming the hash slots ownership.\n\nHowever there is an exception to this rule, and it is when a new\ncluster is created from scratch. Redis Cluster *config epoch collision\nresolution* algorithm can deal with new nodes all configured with the\nsame configuration at startup, but this process is slow and should be\nthe exception, only to make sure that whatever happens, two more\nnodes eventually always move away from the state of having the same\nconfiguration epoch.\n\nSo, using `CLUSTER SET-CONFIG-EPOCH`, when a new cluster is created, we can\nassign a different progressive configuration epoch to each node before\njoining the cluster together."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>config-epoch</code></summary>\n\nThe configuration epoch to set for the current node.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[]}
{"schema_version":2,"id":"commands/cluster-setslot","title":"CLUSTER SETSLOT","url":"https://redis.io/docs/latest/commands/cluster-setslot/","summary":"Binds a hash slot to a node.","since":"3.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"c9c0b4df2e9ab4bcfb6404b7e16bb6710711bc6bebd588eae13fb531d626048f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"`CLUSTER SETSLOT` is responsible for changing the state of a hash slot in the receiving node in different ways. It can, depending on the subcommand used:\n\n1. `MIGRATING` subcommand: set a hash slot in migrating state.\n2. `IMPORTING` subcommand: set a hash slot in importing state.\n3. `STABLE` subcommand: clear any importing / migrating state from hash slot.\n4. `NODE` subcommand: bind the hash slot to a different node.\n\nThe command with its set of subcommands is useful in order to start and end cluster live resharding operations, which are accomplished by setting a hash slot in migrating state in the source node, and importing state in the destination node.\n\nEach subcommand is documented below. At the end you'll find a description of\nhow live resharding is performed using this command and other related commands."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>slot</code></summary>\n\nThe hash slot to configure.\n\n</details>\n\n<details open><summary><code>IMPORTING node-id | MIGRATING node-id | NODE node-id | STABLE</code></summary>\n\nThe slot state to set: `IMPORTING` to import the slot from the given node, `MIGRATING` to migrate it to the given node, `NODE` to assign the slot to the given node, or `STABLE` to clear any importing or migrating state.\n\n</details>"},{"id":"details","title":"Details","role":"content","text":""},{"id":"migrating-destination-node-id","title":"`MIGRATING destination-node-id`","role":"content","text":"This subcommand sets a slot to the migrating state. The node that receives the command must own the hash slot; otherwise, Redis returns an error.\nWhen a slot is in the migrating state, the node handles commands as follows:\n\n1. If the command accesses an existing key, Redis processes the command normally.\n1. If the command accesses a key that does not exist, Redis returns an ASK redirection to destination node. The client should retry only that command on destination node and should not update its hash slot mapping.\n1. If the command accesses multiple keys, Redis handles it based on how many of the keys exist:\n    - If none of the keys exist, Redis returns an ASK redirection.\n    - If all of the keys exist, Redis processes the command normally.\n    - If only some of the keys exist, Redis returns a TRYAGAIN error. The client should retry after the relevant keys finish migrating to the target node."},{"id":"importing-source-node-id","title":"`IMPORTING source-node-id`","role":"content","text":"This subcommand is the reverse of `MIGRATING`. It prepares the destination node to import keys from the specified source node. The command works only if the destination node does not already own the specified hash slot.\n\nWhen a slot is in the `importing` state, the node handles commands for that slot as follows:\n\n1. If the command is not preceded by [`ASKING`](https://redis.io/docs/latest/commands/asking), Redis rejects the command and returns a `MOVED` redirection.\n2. If the command is preceded by [`ASKING`](https://redis.io/docs/latest/commands/asking), Redis executes the command on the importing node.\n\nWhen a migrating node returns an `ASK` redirection, the client contacts the target node, sends [`ASKING`](https://redis.io/docs/latest/commands/asking), and then sends the redirected command. This lets the target node process commands for keys that do not exist on the source node, as well as keys that have already migrated.\n\nThis behavior provides three guarantees:\n\n1. Redis creates new keys on the target node, so the migration only needs to move existing keys.\n2. Redis processes commands for already-migrated keys on the target node, which is becoming the new hash slot owner.\n3. Without [`ASKING`](https://redis.io/docs/latest/commands/asking), Redis continues to return `MOVED` redirections. This prevents clients with stale or incorrect hash slot mappings from accidentally writing to the target node and creating a second version of a key that has not migrated yet."},{"id":"stable","title":"`STABLE`","role":"content","text":"This subcommand just clears migrating / importing state from the slot. It is\nmainly used to fix a cluster stuck in a wrong state by `redis-cli --cluster fix`.\nNormally the two states are cleared automatically at the end of the migration\nusing the `SETSLOT NODE ...` subcommand as explained in the next section."},{"id":"node-node-id","title":"`NODE node-id`","role":"content","text":"This subcommand associates a hash slot with the specified node. This subcommand has the most complex behavior because it works only in specific situations and can have different side effects depending on the slot state.\n\nThe command has the following preconditions and side effects:\n\n1. If the node that receives the command currently owns the hash slot, and the command assigns the slot to a different node, Redis returns an error if the receiving node still contains keys for that slot.\n2. If the slot is in the `migrating` state, Redis clears that state when it assigns the slot to another node.\n3. If the slot is in the `importing` state on the node that receives the command, and the command assigns the slot to that same node, Redis:\n   - Clears the `importing` state.\n   - Creates a new config epoch and assigns it to the node, unless the node already has the greatest config epoch in the cluster.\n\nThe third case occurs on the target node at the end of a hash slot resharding operation. The new config epoch lets the node’s hash slot ownership take precedence over earlier configurations created by failovers or previous slot migrations.\n\nThis is the only case where a Redis Cluster node creates a new config epoch without agreement from other nodes. It happens only during manual configuration changes. Redis Cluster still prevents a lasting conflict between nodes with the same config epoch by using its config epoch collision resolution algorithm."},{"id":"redis-cluster-live-resharding-explained","title":"Redis Cluster live resharding explained","role":"content","text":"The `CLUSTER SETSLOT` command is an important piece used by Redis Cluster in order to migrate all the keys contained in one hash slot from one node to another. This is how the migration is orchestrated, with the help of other commands as well. We'll call the node that has the current ownership of the hash slot the `source` node, and the node where we want to migrate the `destination` node.\n\n1. Set the destination node slot to *importing* state using `CLUSTER SETSLOT <slot> IMPORTING <source-node-id>`.\n2. Set the source node slot to *migrating* state using `CLUSTER SETSLOT <slot> MIGRATING <destination-node-id>`.\n3. Get keys from the source node with [`CLUSTER GETKEYSINSLOT`](https://redis.io/docs/latest/commands/cluster-getkeysinslot) command and move them into the destination node using the [`MIGRATE`](https://redis.io/docs/latest/commands/migrate) command.\n4. Send `CLUSTER SETSLOT <slot> NODE <destination-node-id>` to the destination node.\n5. Send `CLUSTER SETSLOT <slot> NODE <destination-node-id>` to the source node.\n6. Send `CLUSTER SETSLOT <slot> NODE <destination-node-id>` to the other master nodes (optional).\n\nNotes:\n\n* The order of step 1 and 2 is important. We want the destination node to be ready to accept `ASK` redirections when the source node is configured to redirect.\n* The order of step 4 and 5 is important.\n  The destination node is responsible for propagating the change to the rest of the cluster.\n  If the source node is informed before the destination node and the destination node crashes before it is set as new slot owner, the slot is left with no owner, even after a successful failover.\n* Step 6, sending `SETSLOT` to the nodes not involved in the resharding, is not technically necessary since the configuration will eventually propagate itself.\n  However, it is a good idea to do so in order to stop nodes from pointing to the wrong node for the hash slot moved as soon as possible, resulting in less redirections to find the right node."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): all the sub-commands return `OK` if the command was successful. Otherwise an error is returned.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): all the sub-commands return `OK` if the command was successful. Otherwise an error is returned."}],"examples":[]}
{"schema_version":2,"id":"commands/cluster-shards","title":"CLUSTER SHARDS","url":"https://redis.io/docs/latest/commands/cluster-shards/","summary":"Returns the mapping of cluster slots to shards.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"074262f9c3cbab6ee5339d7af8b8dcc591e5ba7864fd5a49011219dad01ac06a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"`CLUSTER SHARDS` returns details about the shards of the cluster.\nA shard is defined as a collection of nodes that serve the same set of slots and that replicate from each other.\nA shard may only have a single master at a given time, but may have multiple or no replicas.\nIt is possible for a shard to not be serving any slots while still having replicas.\n\nThis command replaces the [`CLUSTER SLOTS`](https://redis.io/docs/latest/commands/cluster-slots) command, by providing a more efficient and extensible representation of the cluster. \n\nThe command is suitable to be used by Redis Cluster client libraries in order to understand the topology of the cluster.\nA client should issue this command on startup in order to retrieve the map associating cluster *hash slots* with actual node information.\nThis map should be used to direct commands to the node that is likely serving the slot associated with a given command.\nIn the event the command is sent to the wrong node, in that it received a '-MOVED' redirect, this command can then be used to update the topology of the cluster.\n\nThe command returns an array of shards, with each shard containing two fields, 'slots' and 'nodes'. \n\nThe 'slots' field is a list of slot ranges served by this shard, stored as pair of integers representing the inclusive start and end slots of the ranges.\nFor example, if a node owns the slots 1, 2, 3, 5, 7, 8 and 9, the slots ranges would be stored as [1-3], [5-5], [7-9].\nThe slots field would therefore be represented by the following list of integers.\n\n[code example]\n\nThe 'nodes' field contains a list of all nodes within the shard.\nEach individual node is a map of attributes that describe the node. \nSome attributes are optional and more attributes may be added in the future. \nThe current list of attributes:\n\n* id: The unique node id for this particular node.\n* endpoint: The preferred endpoint to reach the node, see below for more information about the possible values of this field.\n* ip: The IP address to send requests to for this node.\n* hostname (optional): The announced hostname to send requests to for this node.\n* port (optional): The TCP (non-TLS) port of the node. At least one of port or tls-port will be present.\n* tls-port (optional): The TLS port of the node. At least one of port or tls-port will be present.\n* role: The replication role of this node.\n* replication-offset: The replication offset of this node. This information can be used to send commands to the most up to date replicas.\n* health: Either `online`, `failed`, or `loading`. This information should be used to determine which nodes should be sent traffic. The `loading` health state should be used to know that a node is not currently eligible to serve traffic, but may be eligible in the future. \n\nThe endpoint, along with the port, defines the location that clients should use to send requests for a given slot.\nA NULL value for the endpoint indicates the node has an unknown endpoint and the client should connect to the same endpoint it used to send the `CLUSTER SHARDS` command but with the port returned from the command.\nThis unknown endpoint configuration is useful when the Redis nodes are behind a load balancer that Redis doesn't know the endpoint of.\nWhich endpoint is set is determined by the `cluster-preferred-endpoint-type` config.\nAn empty string `\"\"` is another abnormal value of the endpoint field, as well as for the ip field, which is returned if the node doesn't know its own IP address.\nThis can happen in a cluster that consists of only one node or the node has not yet been joined with the rest of the cluster.\nThe value `?` is displayed if the node is incorrectly configured to use announced hostnames but no hostname is configured using `cluster-announce-hostname`.\nClients may treat the empty string in the same way as NULL, that is the same endpoint it used to send the current command to, while `\"?\"` should be treated as an unknown node, not necessarily the same node as the one serving the current command."},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Only supported with the [OSS cluster API](https://redis.io/docs/latest/operate/rs/databases/configure/oss-cluster-api). |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a nested list of a map of hash ranges and shard nodes describing individual shards.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a nested list of [Map reply](../../develop/reference/protocol-spec#maps) of hash ranges and shard nodes describing individual shards."}],"examples":[{"id":"overview-ex0","language":"plaintext","code":"1) 1) \"slots\"\n   2) 1) (integer) 1\n      2) (integer) 3\n      3) (integer) 5\n      4) (integer) 5\n      5) (integer) 7\n      6) (integer) 9","section_id":"overview"},{"id":"examples-ex0","language":"plaintext","code":"> CLUSTER SHARDS\n1) 1) \"slots\"\n   2) 1) (integer) 0\n      2) (integer) 5460\n   3) \"nodes\"\n   4) 1)  1) \"id\"\n          2) \"e10b7051d6bf2d5febd39a2be297bbaea6084111\"\n          3) \"port\"\n          4) (integer) 30001\n          5) \"ip\"\n          6) \"127.0.0.1\"\n          7) \"endpoint\"\n          8) \"127.0.0.1\"\n          9) \"role\"\n         10) \"master\"\n         11) \"replication-offset\"\n         12) (integer) 72156\n         13) \"health\"\n         14) \"online\"\n      2)  1) \"id\"\n          2) \"1901f5962d865341e81c85f9f596b1e7160c35ce\"\n          3) \"port\"\n          4) (integer) 30006\n          5) \"ip\"\n          6) \"127.0.0.1\"\n          7) \"endpoint\"\n          8) \"127.0.0.1\"\n          9) \"role\"\n         10) \"replica\"\n         11) \"replication-offset\"\n         12) (integer) 72156\n         13) \"health\"\n         14) \"online\"\n2) 1) \"slots\"\n   2) 1) (integer) 10923\n      2) (integer) 16383\n   3) \"nodes\"\n   4) 1)  1) \"id\"\n          2) \"fd20502fe1b32fc32c15b69b0a9537551f162f1f\"\n          3) \"port\"\n          4) (integer) 30003\n          5) \"ip\"\n          6) \"127.0.0.1\"\n          7) \"endpoint\"\n          8) \"127.0.0.1\"\n          9) \"role\"\n         10) \"master\"\n         11) \"replication-offset\"\n         12) (integer) 72156\n         13) \"health\"\n         14) \"online\"\n      2)  1) \"id\"\n          2) \"6daa25c08025a0c7e4cc0d1ab255949ce6cee902\"\n          3) \"port\"\n          4) (integer) 30005\n          5) \"ip\"\n          6) \"127.0.0.1\"\n          7) \"endpoint\"\n          8) \"127.0.0.1\"\n          9) \"role\"\n         10) \"replica\"\n         11) \"replication-offset\"\n         12) (integer) 72156\n         13) \"health\"\n         14) \"online\"\n3) 1) \"slots\"\n   2) 1) (integer) 5461\n      2) (integer) 10922\n   3) \"nodes\"\n   4) 1)  1) \"id\"\n          2) \"a4a3f445ead085eb3eb9ee7d8c644ec4481ec9be\"\n          3) \"port\"\n          4) (integer) 30002\n          5) \"ip\"\n          6) \"127.0.0.1\"\n          7) \"endpoint\"\n          8) \"127.0.0.1\"\n          9) \"role\"\n         10) \"master\"\n         11) \"replication-offset\"\n         12) (integer) 72156\n         13) \"health\"\n         14) \"online\"\n      2)  1) \"id\"\n          2) \"da6d5847aa019e9b9d2a8aa24a75f856fd3456cc\"\n          3) \"port\"\n          4) (integer) 30004\n          5) \"ip\"\n          6) \"127.0.0.1\"\n          7) \"endpoint\"\n          8) \"127.0.0.1\"\n          9) \"role\"\n         10) \"replica\"\n         11) \"replication-offset\"\n         12) (integer) 72156\n         13) \"health\"\n         14) \"online\"","section_id":"examples"}]}
{"schema_version":2,"id":"commands/cluster-slaves","title":"CLUSTER SLAVES","url":"https://redis.io/docs/latest/commands/cluster-slaves/","summary":"Lists the replica nodes of a master node.","since":"3.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"f807980138930b0c6fe60ff1a5fd483d3cc79d888c91cbcbd2196352f5a1a695","sections":[{"id":"overview","title":"Overview","role":"overview","text":"**A note about the word slave used in this man page and command name**: starting with Redis version 5, if not for backward compatibility, the Redis project no longer uses the word slave. Please use the new command [`CLUSTER REPLICAS`](https://redis.io/docs/latest/commands/cluster-replicas). The command `CLUSTER SLAVES` will continue to work for backward compatibility.\n\nThe command provides a list of replica nodes replicating from the specified\nmaster node. The list is provided in the same format used by [`CLUSTER NODES`](https://redis.io/docs/latest/commands/cluster-nodes) (please refer to its documentation for the specification of the format).\n\nThe command will fail if the specified node is not known or if it is not\na master according to the node table of the node receiving the command.\n\nNote that if a replica is added, moved, or removed from a given master node,\nand we ask `CLUSTER SLAVES` to a node that has not yet received the\nconfiguration update, it may show stale information. However eventually\n(in a matter of seconds if there are no network partitions) all the nodes\nwill agree about the set of nodes associated with a given master."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>node-id</code></summary>\n\nThe ID of the master node to list the replicas of. `CLUSTER SLAVES` is deprecated; use `CLUSTER REPLICAS` instead.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | Deprecated as of Redis v5.0.0. |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of replica nodes replicating from the specified master node provided in the same format used by `CLUSTER NODES`.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of replica nodes replicating from the specified master node provided in the same format used by `CLUSTER NODES`."}],"examples":[]}
{"schema_version":2,"id":"commands/cluster-slot-stats","title":"CLUSTER SLOT-STATS","url":"https://redis.io/docs/latest/commands/cluster-slot-stats/","summary":"Return an array of slot usage statistics for slots assigned to the current node.","since":"8.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"07fce19d1c9590920259b5fa4fdcead1943bfdcb79967dc77b95ef03ba8b53f4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Use this command to get an array of slot usage statistics for the slots assigned to the current shard. If you're working with a Redis cluster, this data helps you understand overall slot usage, spot hot or cold slots, plan slot migrations to balance load, or refine your application logic to better distribute keys."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"Specify exactly one of the following to select which slots to report on.\n\n<details open><summary><code>SLOTSRANGE start-slot end-slot</code></summary>\n\nLimits the results to a specific, inclusive range of slots. Results are always sorted by slot number in ascending order.\n\n</details>\n\n<details open><summary><code>ORDERBY metric [LIMIT limit] [ASC | DESC]</code></summary>\n\nSorts the slot statistics by the specified `metric`. Use `ASC` or `DESC` to sort in ascending or descending order, and `LIMIT` to limit the number of results returned. If multiple slots have the same value, the command uses the slot number as a tiebreaker, sorted in ascending order.\n\n`metric` is one of the statistics reported for each slot:\n\n- `KEY-COUNT`: Number of keys stored in the slot.\n- `CPU-USEC`: CPU time (in microseconds) spent handling the slot.\n- `MEMORY-BYTES`: Number of bytes allocated by the slot.\n- `NETWORK-BYTES-IN`: Total inbound network traffic (in bytes) received by the slot.\n- `NETWORK-BYTES-OUT`: Total outbound network traffic (in bytes) sent from the slot.\n\n</details>\n\n> [!NOTE]\n> All metrics except `KEY-COUNT` require that `cluster-slot-stats-enabled` is set to `yes` in the `redis.conf` file."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): a nested list of slot usage statistics.\n* [Simple error](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) otherwise.\n\n**RESP3:**\n\nOne of the following:\n\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): a nested list of slot usage statistics.\n* [Simple error](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) otherwise."}],"examples":[]}
{"schema_version":2,"id":"commands/cluster-slots","title":"CLUSTER SLOTS","url":"https://redis.io/docs/latest/commands/cluster-slots/","summary":"Returns the mapping of cluster slots to nodes.","since":"3.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"e775675041c52571d408979cc8dfbbb7e8ed626133f62434571c606c0c15ff4d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"`CLUSTER SLOTS` returns details about which cluster slots map to which Redis instances. \nThe command is suitable to be used by Redis Cluster client libraries implementations in order to retrieve (or update when a redirection is received) the map associating cluster *hash slots* with actual nodes network information, so that when a command is received, it can be sent to what is likely the right instance for the keys specified in the command. \n\nThe networking information for each node is an array containing the following elements:\n\n* Preferred endpoint (Either an IP address, hostname, or NULL)\n* Port number\n* The node ID\n* A map of additional networking metadata\n\nThe preferred endpoint, along with the port, defines the location that clients should use to send requests for a given slot.\nA NULL value for the endpoint indicates the node has an unknown endpoint and the client should connect to the same endpoint it used to send the `CLUSTER SLOTS` command but with the port returned from the command.\nThis unknown endpoint configuration is useful when the Redis nodes are behind a load balancer that Redis doesn't know the endpoint of.\nWhich endpoint is set as preferred is determined by the `cluster-preferred-endpoint-type` config.\nAn empty string `\"\"` is another abnormal value of the endpoint field, as well as for the ip field, which is returned if the node doesn't know its own IP address.\nThis can happen in a cluster that consists of only one node or the node has not yet been joined with the rest of the cluster.\nThe value `?` is displayed if the node is incorrectly configured to use announced hostnames but no hostname is configured using `cluster-announce-hostname`.\nClients may treat the empty string in the same way as NULL, that is the same endpoint it used to send the current command to, while `\"?\"` should be treated as an unknown node, not necessarily the same node as the one serving the current command.\n\nAdditional networking metadata is provided as a map on the fourth argument for each node. \nThe following networking metadata may be returned:\n\n* IP: When the preferred endpoint is not set to IP.\n* Hostname: When a node has an announced hostname but the primary endpoint is not set to hostname."},{"id":"nested-result-array","title":"Nested Result Array","role":"content","text":"Each nested result is:\n\n  - Start slot range\n  - End slot range\n  - Master for slot range represented as nested networking information\n  - First replica of master for slot range\n  - Second replica\n  - ...continues until all replicas for this master are returned.\n\nEach result includes all active replicas of the master instance\nfor the listed slot range.  Failed replicas are not returned.\n\nThe third nested reply is guaranteed to be the networking information of the master instance for the slot range.\nAll networking information after the third nested reply are replicas of the master.\n\nIf a cluster instance has non-contiguous slots (e.g. 1-400,900,1800-6000) then master and replica networking information results will be duplicated for each top-level slot range reply.\n\n[code example]\n\n**Warning:** In future versions there could be more elements describing the node better.\nIn general a client implementation should just rely on the fact that certain parameters are at fixed positions as specified, but more parameters may follow and should be ignored.\nSimilarly a client library should try if possible to cope with the fact that older versions may just have the primary endpoint and port parameter."},{"id":"behavior-change-history","title":"Behavior change history","role":"content","text":"*   `>= 7.0.0`: Added support for hostnames and unknown endpoints in first field of node response."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Only supported with the [OSS cluster API](https://redis.io/docs/latest/operate/rs/databases/configure/oss-cluster-api). Deprecated as of Redis v7.0.0. |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): nested list of slot ranges with networking information.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): nested list of slot ranges with networking information."}],"examples":[{"id":"nested-result-array-ex0","language":"plaintext","code":"> CLUSTER SLOTS\n1) 1) (integer) 0\n   2) (integer) 5460\n   3) 1) \"127.0.0.1\"\n      2) (integer) 30001\n      3) \"09dbe9720cda62f7865eabc5fd8857c5d2678366\"\n      4) 1) hostname\n         2) \"host-1.redis.example.com\"\n   4) 1) \"127.0.0.1\"\n      2) (integer) 30004\n      3) \"821d8ca00d7ccf931ed3ffc7e3db0599d2271abf\"\n      4) 1) hostname\n         2) \"host-2.redis.example.com\"\n2) 1) (integer) 5461\n   2) (integer) 10922\n   3) 1) \"127.0.0.1\"\n      2) (integer) 30002\n      3) \"c9d93d9f2c0c524ff34cc11838c2003d8c29e013\"\n      4) 1) hostname\n         2) \"host-3.redis.example.com\"\n   4) 1) \"127.0.0.1\"\n      2) (integer) 30005\n      3) \"faadb3eb99009de4ab72ad6b6ed87634c7ee410f\"\n      4) 1) hostname\n         2) \"host-4.redis.example.com\"\n3) 1) (integer) 10923\n   2) (integer) 16383\n   3) 1) \"127.0.0.1\"\n      2) (integer) 30003\n      3) \"044ec91f325b7595e76dbcb18cc688b6a5b434a1\"\n      4) 1) hostname\n         2) \"host-5.redis.example.com\"\n   4) 1) \"127.0.0.1\"\n      2) (integer) 30006\n      3) \"58e6e48d41228013e5d9c1c37c5060693925e97e\"\n      4) 1) hostname\n         2) \"host-6.redis.example.com\"","section_id":"nested-result-array"}]}
{"schema_version":2,"id":"commands/cms.incrby","title":"CMS.INCRBY","url":"https://redis.io/docs/latest/commands/cms.incrby/","summary":"Increases the count of one or more items by increment","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"57b9bf1882cfc51758da5bb1f1ce7dd269c637a3c4d9b2f4504843db22cb9a34","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Increases the count of item by increment. Multiple items can be increased with one call."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the sketch.\n\n</details>\n\n<details open><summary><code>item increment [item increment ...]</code></summary>\n\nOne or more item-increment pairs. Each `item`'s counter is increased by its corresponding `increment`.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\"><nobr>&#x2705; Flexible & Annual</nobr></span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n* [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [integer replies](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) representing updated min-counts of each of the provided items in the sketch.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, missing key, overflow, or wrong key type.\n\n**RESP3:**\n\nOne of the following:\n\n* [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [integer replies](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) representing updated min-counts of each of the provided items in the sketch.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, missing key, overflow, or wrong key type."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> CMS.INCRBY test foo 10 bar 42\n1) (integer) 10\n2) (integer) 42","section_id":"examples"}]}
{"schema_version":2,"id":"commands/cms.info","title":"CMS.INFO","url":"https://redis.io/docs/latest/commands/cms.info/","summary":"Returns information about a sketch","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"8a1310dcba4df4bbadbc6cf3769c2c64aa2545029a5745d40b67d2c5874a545b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns width, depth and total count of the sketch."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the sketch.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) and [integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) pairs containing sketch information.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, missing key, or wrong key type.\n\n**RESP3:**\n\nOne of the following:\n\n* [Map reply](https://redis.io/docs/latest/develop/reference/protocol-spec#maps) of [simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) and [integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) pairs containing sketch information.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, missing key, or wrong key type."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> CMS.INFO test\n 1) width\n 2) (integer) 2000\n 3) depth\n 4) (integer) 7\n 5) count\n 6) (integer) 0","section_id":"examples"}]}
{"schema_version":2,"id":"commands/cms.initbydim","title":"CMS.INITBYDIM","url":"https://redis.io/docs/latest/commands/cms.initbydim/","summary":"Initializes a Count-Min Sketch to dimensions specified by user","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"38dbdb8691907f041765ec578942afb1a635451d466307c05ed089f77b86f226","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Initializes a Count-Min Sketch to dimensions specified by user."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the sketch.\n\n</details>\n\n<details open><summary><code>width</code></summary>\n\nNumber of counters in each array. Reduces the error size.\n\n</details>\n\n<details open><summary><code>depth</code></summary>\n\nNumber of counter-arrays. Reduces the probability for an error of a certain size (percentage of total count).\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if the given key already exists.\n\n**RESP3:**\n\nOne of the following:\n\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if the given key already exists."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> CMS.INITBYDIM test 2000 5\nOK","section_id":"examples"}]}
{"schema_version":2,"id":"commands/cms.initbyprob","title":"CMS.INITBYPROB","url":"https://redis.io/docs/latest/commands/cms.initbyprob/","summary":"Initializes a Count-Min Sketch to accommodate requested tolerances.","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"c7dd686d774df5cc36ba13c4f8107f5200128749a383d05a378593cc9daead79","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Initializes a Count-Min Sketch to accommodate requested tolerances."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the sketch.\n\n</details>\n\n<details open><summary><code>error</code></summary>\n\nEstimate size of error. The error is a percent of total counted items. This effects the width of the sketch.\n\n</details>\n\n<details open><summary><code>probability</code></summary>\n\nThe desired probability for inflated count. This should be a decimal value between 0 and 1. This effects the depth of the sketch. For example, for a desired false positive rate of 0.1% (1 in 1000), error_rate should be set to 0.001. The closer this number is to zero, the greater the memory consumption per item and the more CPU usage per operation.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if the given key already exists.\n\n**RESP3:**\n\nOne of the following:\n\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if the given key already exists."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> CMS.INITBYPROB test 0.001 0.01\nOK","section_id":"examples"}]}
{"schema_version":2,"id":"commands/cms.merge","title":"CMS.MERGE","url":"https://redis.io/docs/latest/commands/cms.merge/","summary":"Merges several sketches into one sketch","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"c8302bde53b909b1d457e1c1ad972144f16d36fc1dc18cbd04289d7c92169fe9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nMerges several sketches into one sketch. All sketches must have identical width and depth. Weights can be used to multiply certain sketches. Default weight is 1."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>destination</code></summary>\n\nThe name of the destination sketch. Must be initialized.\n\n</details>\n\n<details open><summary><code>numKeys</code></summary>\n\nNumber of sketches to be merged.\n\n</details>\n\n<details open><summary><code>source [source ...]</code></summary>\n\nNames of source sketches to be merged.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>WEIGHTS weight [weight ...]</code></summary>\n\nMultiple of each sketch. Default is 1.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: non-existent key or destination key is not of the same width and/or depth.\n\n**RESP3:**\n\nOne of the following:\n\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: non-existent key or destination key is not of the same width and/or depth."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> CMS.MERGE dest 2 test1 test2 WEIGHTS 1 3\nOK","section_id":"examples"}]}
{"schema_version":2,"id":"commands/cms.query","title":"CMS.QUERY","url":"https://redis.io/docs/latest/commands/cms.query/","summary":"Returns the count for one or more items in a sketch","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"5b8d88acae0727f6c45a5f086db68f0c73ca40320e7a3165158c7b8697eb7f39","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns the count for one or more items in a sketch."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the sketch.\n\n</details>\n\n<details open><summary><code>item [item ...]</code></summary>\n\nOne or more items for which to return the count.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n* [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [integer replies](https://redis.io/docs/latest/develop/reference/protocol-spec#integers)  representing the min-counts of each of the provided items in the sketch.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, missing key, or wrong key type.\n\n**RESP3:**\n\nOne of the following:\n\n* [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [integer replies](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) representing the min-counts of each of the provided items in the sketch.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, missing key, or wrong key type."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> CMS.QUERY test foo bar\n1) (integer) 10\n2) (integer) 42","section_id":"examples"}]}
{"schema_version":2,"id":"commands/command","title":"COMMAND","url":"https://redis.io/docs/latest/commands/command/","summary":"Returns detailed information about all commands.","since":"2.8.13","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"faea4bffd98059ad35178b9e1b329c7c00ade9c664908414e7f412ffb95f1479","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Return an array with details about every Redis command.\n\nThe `COMMAND` command is introspective.\nIts reply describes all commands that the server can process.\nRedis clients can call it to obtain the server's runtime capabilities during the handshake.\n\n`COMMAND` also has several subcommands.\nPlease refer to its subcommands for further details.\n\n**Cluster note:**\nthis command is especially beneficial for cluster-aware clients.\nSuch clients must identify the names of keys in commands to route requests to the correct shard.\nAlthough most commands accept a single key as their first argument, there are many exceptions to this rule. \nYou can call `COMMAND` and then keep the mapping between commands and their respective key specification rules cached in the client.\n\nThe reply it returns is an array with an element per command.\nEach element that describes a Redis command is represented as an array by itself.\n\nThe command's array consists of a fixed number of elements.\nThe exact number of elements in the array depends on the server's version.\n\n1. Name\n1. Arity\n1. Flags\n1. First key\n1. Last key\n1. Step\n1. [ACL categories](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl) (as of Redis 6.0)\n1. [Tips](https://redis.io/docs/latest/develop/reference/command-tips) (as of Redis 7.0)\n1. [Key specifications](https://redis.io/docs/latest/develop/reference/key-specs) (as of Redis 7.0)\n1. Subcommands (as of Redis 7.0)"},{"id":"name","title":"Name","role":"content","text":"This is the command's name in lowercase.\n\n**Note:**\nRedis command names are case-insensitive."},{"id":"arity","title":"Arity","role":"content","text":"Arity is the number of arguments a command expects.\nIt follows a simple pattern:\n\n* A positive integer means a fixed number of arguments.\n* A negative integer means a minimal number of arguments.\n\nCommand arity _always includes_ the command's name itself (and the subcommand when applicable).\n\nExamples:\n\n* [`GET`](https://redis.io/docs/latest/commands/get)'s arity is _2_ since the command only accepts one argument and always has the format `GET _key_`.\n* [`MGET`](https://redis.io/docs/latest/commands/mget)'s arity is _-2_ since the command accepts at least one argument, but possibly multiple ones: `MGET _key1_ [key2] [key3] ...`."},{"id":"flags","title":"Flags","role":"parameters","text":"Command flags are an array. It can contain the following simple strings (status reply):\n\n* **admin:** the command is an administrative command.\n* **asking:** the command is allowed even during hash slot migration.\n  This flag is relevant in Redis Cluster deployments.\n* **blocking:** the command may block the requesting client.\n* **denyoom**: the command is rejected if the server's memory usage is too high (see the _maxmemory_ configuration directive).\n* **fast:** the command operates in constant or log(N) time.\n  This flag is used for monitoring latency with the [`LATENCY`](https://redis.io/docs/latest/commands/latency) command.\n* **loading:** the command is allowed while the database is loading.\n* **movablekeys:** the _first key_, _last key_, and _step_ values don't determine all key positions.\n  Clients need to use [`COMMAND GETKEYS`](https://redis.io/docs/latest/commands/command-getkeys) or [key specifications](https://redis.io/docs/latest/develop/reference/key-specs) in this case.\n  See below for more details.\n* **no_auth:** executing the command doesn't require authentication.\n* **no_async_loading:** the command is denied during asynchronous loading (that is when a replica uses disk-less `SWAPDB SYNC`, and allows access to the old dataset).\n* **no_mandatory_keys:** the command may accept key name arguments, but these aren't mandatory.\n* **no_multi:** the command isn't allowed inside the context of a [transaction](https://redis.io/docs/latest/develop/using-commands/transactions).\n* **noscript:** the command can't be called from [scripts](https://redis.io/docs/latest/develop/programmability/eval-intro) or [functions](https://redis.io/docs/latest/develop/programmability/functions-intro).\n* **pubsub:** the command is related to [Redis Pub/Sub](https://redis.io/docs/latest/develop/pubsub).\n* **random**: the command returns random results, which is a concern with verbatim script replication.\n  As of Redis 7.0, this flag is a [command tip](https://redis.io/docs/latest/develop/reference/command-tips).\n* **readonly:** the command doesn't modify data.\n* **sort_for_script:** the command's output is sorted when called from a script.\n* **skip_monitor:** the command is not shown in [`MONITOR`](https://redis.io/docs/latest/commands/monitor)'s output.\n* **skip_slowlog:** the command is not shown in [`SLOWLOG`](https://redis.io/docs/latest/commands/slowlog)'s output.\n  As of Redis 7.0, this flag is a [command tip](https://redis.io/docs/latest/develop/reference/command-tips).\n* **stale:** the command is allowed while a replica has stale data.\n* **write:** the command may modify data."},{"id":"movablekeys","title":"Movablekeys","role":"content","text":"Consider [`SORT`](https://redis.io/docs/latest/commands/sort):\n\n[code example]\n\nSome Redis commands have no predetermined key locations or are not easy to find.\nFor those commands, the _movablekeys_ flag indicates that the _first key_, _last key_, and _step_ values are insufficient to find all the keys.\n\nHere are several examples of commands that have the _movablekeys_ flag:\n\n* [`SORT`](https://redis.io/docs/latest/commands/sort): the optional _STORE_, _BY_, and _GET_ modifiers are followed by names of keys.\n* [`ZUNION`](https://redis.io/docs/latest/commands/zunion): the _numkeys_ argument specifies the number key name arguments.\n* [`MIGRATE`](https://redis.io/docs/latest/commands/migrate): the keys appear _KEYS_ keyword and only when the second argument is the empty string.\n\nRedis Cluster clients need to use other measures, as follows, to locate the keys for such commands.\n\nYou can use the [`COMMAND GETKEYS`](https://redis.io/docs/latest/commands/command-getkeys) command and have your Redis server report all keys of a given command's invocation.\n\nAs of Redis 7.0, clients can use the [key specifications](#key-specifications) to identify the positions of key names.\nThe only commands that require using [`COMMAND GETKEYS`](https://redis.io/docs/latest/commands/command-getkeys) are [`SORT`](https://redis.io/docs/latest/commands/sort) and [`MIGRATE`](https://redis.io/docs/latest/commands/migrate) for clients that parse keys' specifications.\n\nFor more information, please refer to the [key specifications page](https://redis.io/docs/latest/develop/reference/key-specs)."},{"id":"first-key","title":"First key","role":"content","text":"The position of the command's first key name argument.\nFor most commands, the first key's position is 1.\nPosition 0 is always the command name itself."},{"id":"last-key","title":"Last key","role":"content","text":"The position of the command's last key name argument.\nRedis commands usually accept one, two or multiple number of keys.\n\nCommands that accept a single key have both _first key_ and _last key_ set to 1.\n\nCommands that accept two key name arguments, e.g. [`BRPOPLPUSH`](https://redis.io/docs/latest/commands/brpoplpush), [`SMOVE`](https://redis.io/docs/latest/commands/smove) and [`RENAME`](https://redis.io/docs/latest/commands/rename), have this value set to the position of their second key.\n\nMulti-key commands that accept an arbitrary number of keys, such as [`MSET`](https://redis.io/docs/latest/commands/mset), use the value -1."},{"id":"step","title":"Step","role":"content","text":"The step, or increment, between the _first key_ and the position of the next key.\n\nConsider the following two examples:\n\n[code example]\n\n[code example]\n\nThe step count allows us to find keys' positions. \nFor example [`MSET`](https://redis.io/docs/latest/commands/mset): Its syntax is `MSET _key1_ _val1_ [key2] [val2] [key3] [val3]...`, so the keys are at every other position (step value of _2_).\nUnlike [`MGET`](https://redis.io/docs/latest/commands/mget), which uses a step value of _1_."},{"id":"acl-categories","title":"ACL categories","role":"security","text":"This is an array of simple strings that are the ACL categories to which the command belongs.\nPlease refer to the [Access Control List](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl) page for more information."},{"id":"command-tips","title":"Command tips","role":"syntax","text":"Helpful information about the command.\nTo be used by clients/proxies.\n\nPlease check the [Command tips](https://redis.io/docs/latest/develop/reference/command-tips) page for more information."},{"id":"key-specifications","title":"Key specifications","role":"content","text":"This is an array consisting of the command's key specifications.\nEach element in the array is a map describing a method for locating keys in the command's arguments.\n\nFor more information please check the [key specifications page](https://redis.io/docs/latest/develop/reference/key-specs)."},{"id":"subcommands","title":"Subcommands","role":"content","text":"This is an array containing all of the command's subcommands, if any.\nSome Redis commands have subcommands (e.g., the `REWRITE` subcommand of [`CONFIG`](https://redis.io/docs/latest/commands/config)).\nEach element in the array represents one subcommand and follows the same specifications as those of `COMMAND`'s reply."},{"id":"examples","title":"Examples","role":"example","text":"The following is `COMMAND`'s output for the [`GET`](https://redis.io/docs/latest/commands/get) command:\n\n[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a nested list of command details. The order of the commands in the array is random.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a nested list of command details. The order of the commands in the array is random."}],"examples":[{"id":"movablekeys-ex0","language":"plaintext","code":"1) 1) \"sort\"\n   2) (integer) -2\n   3) 1) write\n      2) denyoom\n      3) movablekeys\n   4) (integer) 1\n   5) (integer) 1\n   6) (integer) 1\n   ...","section_id":"movablekeys"},{"id":"step-ex0","language":"plaintext","code":"1) 1) \"mset\"\n   2) (integer) -3\n   3) 1) write\n      2) denyoom\n   4) (integer) 1\n   5) (integer) -1\n   6) (integer) 2\n   ...","section_id":"step"},{"id":"step-ex1","language":"plaintext","code":"1) 1) \"mget\"\n   2) (integer) -2\n   3) 1) readonly\n      2) fast\n   4) (integer) 1\n   5) (integer) -1\n   6) (integer) 1\n   ...","section_id":"step"},{"id":"examples-ex0","language":"plaintext","code":"1)  1) \"get\"\n    2) (integer) 2\n    3) 1) readonly\n       2) fast\n    4) (integer) 1\n    5) (integer) 1\n    6) (integer) 1\n    7) 1) @read\n       2) @string\n       3) @fast\n    8) (empty array)\n    9) 1) 1) \"flags\"\n          2) 1) read\n          3) \"begin_search\"\n          4) 1) \"type\"\n             2) \"index\"\n             3) \"spec\"\n             4) 1) \"index\"\n                2) (integer) 1\n          5) \"find_keys\"\n          6) 1) \"type\"\n             2) \"range\"\n             3) \"spec\"\n             4) 1) \"lastkey\"\n                2) (integer) 0\n                3) \"keystep\"\n                4) (integer) 1\n                5) \"limit\"\n                6) (integer) 0\n   10) (empty array)\n...","section_id":"examples"}]}
{"schema_version":2,"id":"commands/command-count","title":"COMMAND COUNT","url":"https://redis.io/docs/latest/commands/command-count/","summary":"Returns a count of commands.","since":"2.8.13","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"297a36ec4ca981e6cd1f108af92aa18dc08bf504c2b707904f687408e44f1178","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) of number of total commands in this Redis server."},{"id":"examples","title":"Examples","role":"example","text":"redis> COMMAND COUNT\n(integer) 447"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of commands returned by `COMMAND`.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of commands returned by `COMMAND`."}],"examples":[]}
{"schema_version":2,"id":"commands/command-docs","title":"COMMAND DOCS","url":"https://redis.io/docs/latest/commands/command-docs/","summary":"Returns documentary information about one, multiple or all commands.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"6361957d128ae7537609f2e14b3c8f268f5858693c3141512596b14365b8ef6e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Return documentary information about commands.\n\nBy default, the reply includes all of the server's commands.\nYou can use the optional _command-name_ argument to specify the names of one or more commands.\n\nThe reply includes a map for each returned command.\nThe following keys may be included in the mapped reply:\n\n* **summary:** short command description.\n* **since:** the Redis version that added the command (or for module commands, the module version).\n* **group:** the functional group to which the command belongs.\n  Possible values are:\n    - array\n    - bf\n    - bitmap\n    - cf\n    - cluster\n    - cms\n    - connection\n    - generic\n    - geo\n    - hash\n    - hyperloglog\n    - json\n    - list\n    - pubsub\n    - scripting\n    - search\n    - server\n    - set\n    - sorted-set\n    - stream\n    - string\n    - suggestion\n    - tdigest\n    - timeseries\n    - topk\n    - transactions\n    - vector_set\n* **complexity:** a short explanation about the command's time complexity.\n* **doc_flags:** an array of documentation flags.\n  Possible values are:\n  - deprecated: the command is deprecated.\n  - syscmd: a system command that isn't meant to be called by users.\n* **deprecated_since:** the Redis version that deprecated the command (or for module commands, the module version)..\n* **replaced_by:** the alternative for a deprecated command.\n* **history:** an array of historical notes describing changes to the command's output or arguments. It should not contain information about behavioral changes.\n  Each entry is an array itself, made up of two elements:\n  1. The Redis version that the entry applies to.\n  2. The description of the change.\n* **arguments:** an array of maps that describe the command's arguments.\n  Please refer to the [Redis command arguments](https://redis.io/docs/latest/develop/reference/command-arguments) page for more information."},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>command-name [command-name ...]</code></summary>\n\nOne or more command names to return documentation for. If omitted, documentation for all commands is returned.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> COMMAND DOCS SET\n1) \"set\"\n2)  1) \"summary\"\n    2) \"Sets the string value of a key, ignoring its type. The key is created if it doesn't exist.\"\n    3) \"since\"\n    4) \"1.0.0\"\n    5) \"group\"\n    6) \"string\"\n    7) \"complexity\"\n    8) \"O(1)\"\n    9) \"history\"\n   10) 1) 1) \"2.6.12\"\n          2) \"Added the `EX`, `PX`, `NX` and `XX` options.\"\n       2) 1) \"6.0.0\"\n          2) \"Added the `KEEPTTL` option.\"\n       3) 1) \"6.2.0\"\n          2) \"Added the `GET`, `EXAT` and `PXAT` option.\"\n       4) 1) \"7.0.0\"\n          2) \"Allowed the `NX` and `GET` options to be used together.\"\n       5) 1) \"8.4.0\"\n          2) \"Added 'IFEQ', 'IFNE', 'IFDEQ', 'IFDNE' options.\"\n   11) \"arguments\"\n   12) 1) 1) \"name\"\n          2) \"key\"\n          3) \"type\"\n          4) \"key\"\n          5) \"display_text\"\n          6) \"key\"\n          7) \"key_spec_index\"\n          8) (integer) 0\n       2) 1) \"name\"\n          2) \"value\"\n          3) \"type\"\n          4) \"string\"\n          5) \"display_text\"\n          6) \"value\"\n       3)  1) \"name\"\n           2) \"condition\"\n           3) \"type\"\n           4) \"oneof\"\n           5) \"since\"\n           6) \"2.6.12\"\n           7) \"flags\"\n           8) 1) optional\n           9) \"arguments\"\n          10) 1) 1) \"name\"\n                 2) \"nx\"\n                 3) \"type\"\n                 4) \"pure-token\"\n                 5) \"display_text\"\n                 6) \"nx\"\n                 7) \"token\"\n                 8) \"NX\"\n              2) 1) \"name\"\n                 2) \"xx\"\n                 3) \"type\"\n                 4) \"pure-token\"\n                 5) \"display_text\"\n                 6) \"xx\"\n                 7) \"token\"\n                 8) \"XX\"\n              3)  1) \"name\"\n                  2) \"ifeq-value\"\n                  3) \"type\"\n                  4) \"string\"\n                  5) \"display_text\"\n                  6) \"ifeq-value\"\n                  7) \"token\"\n                  8) \"IFEQ\"\n                  9) \"since\"\n                 10) \"8.4.0\"\n              4)  1) \"name\"\n                  2) \"ifne-value\"\n                  3) \"type\"\n                  4) \"string\"\n                  5) \"display_text\"\n                  6) \"ifne-value\"\n                  7) \"token\"\n                  8) \"IFNE\"\n                  9) \"since\"\n                 10) \"8.4.0\"\n              5)  1) \"name\"\n                  2) \"ifdeq-digest\"\n                  3) \"type\"\n                  4) \"string\"\n                  5) \"display_text\"\n                  6) \"ifdeq-digest\"\n                  7) \"token\"\n                  8) \"IFDEQ\"\n                  9) \"since\"\n                 10) \"8.4.0\"\n              6)  1) \"name\"\n                  2) \"ifdne-digest\"\n                  3) \"type\"\n                  4) \"string\"\n                  5) \"display_text\"\n                  6) \"ifdne-digest\"\n                  7) \"token\"\n                  8) \"IFDNE\"\n                  9) \"since\"\n                 10) \"8.4.0\"\n       4)  1) \"name\"\n           2) \"get\"\n           3) \"type\"\n           4) \"pure-token\"\n           5) \"display_text\"\n           6) \"get\"\n           7) \"token\"\n           8) \"GET\"\n           9) \"since\"\n          10) \"6.2.0\"\n          11) \"flags\"\n          12) 1) optional\n       5) 1) \"name\"\n          2) \"expiration\"\n          3) \"type\"\n          4) \"oneof\"\n          5) \"flags\"\n          6) 1) optional\n          7) \"arguments\"\n          8) 1)  1) \"name\"\n                 2) \"seconds\"\n                 3) \"type\"\n                 4) \"integer\"\n                 5) \"display_text\"\n                 6) \"seconds\"\n                 7) \"token\"\n                 8) \"EX\"\n                 9) \"since\"\n                10) \"2.6.12\"\n             2)  1) \"name\"\n                 2) \"milliseconds\"\n                 3) \"type\"\n                 4) \"integer\"\n                 5) \"display_text\"\n                 6) \"milliseconds\"\n                 7) \"token\"\n                 8) \"PX\"\n                 9) \"since\"\n                10) \"2.6.12\"\n             3)  1) \"name\"\n                 2) \"unix-time-seconds\"\n                 3) \"type\"\n                 4) \"unix-time\"\n                 5) \"display_text\"\n                 6) \"unix-time-seconds\"\n                 7) \"token\"\n                 8) \"EXAT\"\n                 9) \"since\"\n                10) \"6.2.0\"\n             4)  1) \"name\"\n                 2) \"unix-time-milliseconds\"\n                 3) \"type\"\n                 4) \"unix-time\"\n                 5) \"display_text\"\n                 6) \"unix-time-milliseconds\"\n                 7) \"token\"\n                 8) \"PXAT\"\n                 9) \"since\"\n                10) \"6.2.0\"\n             5)  1) \"name\"\n                 2) \"keepttl\"\n                 3) \"type\"\n                 4) \"pure-token\"\n                 5) \"display_text\"\n                 6) \"keepttl\"\n                 7) \"token\"\n                 8) \"KEEPTTL\"\n                 9) \"since\"\n                10) \"6.0.0\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a map, as a flattened array, where each key is a command name, and each value is the documentary information.\n\n**RESP3:**\n\n[Map reply](../../develop/reference/protocol-spec#maps): a map where each key is a command name, and each value is the documentary information."}],"examples":[]}
{"schema_version":2,"id":"commands/command-getkeys","title":"COMMAND GETKEYS","url":"https://redis.io/docs/latest/commands/command-getkeys/","summary":"Extracts the key names from an arbitrary command.","since":"2.8.13","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"d878b7ec4aa8e9d7e417db6c35ee206406c7ad65fa096c4f05dd7cc9bd4eb109","sections":[{"id":"overview","title":"Overview","role":"overview","text":"`COMMAND GETKEYS` is a helper command to let you find the keys\nfrom the provided Redis `command`.\n\n\n[`COMMAND`](https://redis.io/docs/latest/commands/command) describes how Redis identifies key names for each command, including `firstkey`, [key specifications](https://redis.io/docs/latest/develop/reference/key-specs#logical-operation-flags), and `movablekeys`. For some commands, Redis can identify the keys only by parsing the full command. Use COMMAND GETKEYS or [`COMMAND GETKEYSANDFLAGS`](https://redis.io/docs/latest/commands/command-getkeysandflags) to get the key names directly from the Redis command parser."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>command</code></summary>\n\nThe name of the command to analyze.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>arg [arg ...]</code></summary>\n\nThe arguments that would be passed to the command.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> COMMAND GETKEYS MSET a b c d e f\n1) \"a\"\n2) \"c\"\n3) \"e\"\nredis> COMMAND GETKEYS EVAL \"not consulted\" 3 key1 key2 key3 arg1 arg2 arg3 argN\n1) \"key1\"\n2) \"key2\"\n3) \"key3\"\nredis> COMMAND GETKEYS SORT mylist ALPHA STORE outlist\n1) \"mylist\"\n2) \"outlist\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): list of keys from the given command.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of keys from the given command."}],"examples":[]}
{"schema_version":2,"id":"commands/command-getkeysandflags","title":"COMMAND GETKEYSANDFLAGS","url":"https://redis.io/docs/latest/commands/command-getkeysandflags/","summary":"Extracts the key names and access flags for an arbitrary command.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"5ff8d8465e0c9a651d45c216e63c63274b6ecb3be8b12c2b5b01a982d83908f5","sections":[{"id":"overview","title":"Overview","role":"overview","text":"`COMMAND GETKEYSANDFLAGS` is a helper command to let you find the keys from a full Redis `command`, together with flags that indicate what each key is used for.\n\n[COMMAND](https://redis.io/docs/latest/commands/command) describes how Redis identifies key names for each command, including `firstkey`, [key specifications](https://redis.io/docs/latest/develop/reference/key-specs#logical-operation-flags), and `movablekeys`. For some commands, Redis can identify the keys only by parsing the full command. Use [COMMAND GETKEYS](https://redis.io/docs/latest/commands/command-getkeys) or COMMAND GETKEYSANDFLAGS to get key names directly from the Redis command parser.\n\nRefer to [key specifications](https://redis.io/docs/latest/develop/reference/key-specs#logical-operation-flags) for information about the meaning of the key flags."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>command</code></summary>\n\nThe name of the command to analyze.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>arg [arg ...]</code></summary>\n\nThe arguments that would be passed to the command.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> COMMAND GETKEYS MSET a b c d e f\n1) \"a\"\n2) \"c\"\n3) \"e\"\nredis> COMMAND GETKEYS EVAL \"not consulted\" 3 key1 key2 key3 arg1 arg2 arg3 argN\n1) \"key1\"\n2) \"key2\"\n3) \"key3\"\nredis> COMMAND GETKEYSANDFLAGS LMOVE mylist1 mylist2 left left\n1) 1) \"mylist1\"\n   2) 1) RW\n      2) access\n      3) delete\n2) 1) \"mylist2\"\n   2) 1) RW\n      2) insert"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of keys from the given command and their usage flags.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of keys from the given command and their usage flags."}],"examples":[]}
{"schema_version":2,"id":"commands/command-help","title":"COMMAND HELP","url":"https://redis.io/docs/latest/commands/command-help/","summary":"Returns helpful text about the different subcommands.","since":"5.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"0ce3db4dda9407e60237876b2cdef3b066356cb6aaaa885e1c560247b8e2ba1e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `COMMAND HELP` command returns a helpful text describing the different subcommands."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of sub-commands and their descriptions.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of sub-commands and their descriptions."}],"examples":[]}
{"schema_version":2,"id":"commands/command-info","title":"COMMAND INFO","url":"https://redis.io/docs/latest/commands/command-info/","summary":"Returns information about one, multiple or all commands.","since":"2.8.13","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"11f0a9b0df42f0311798853fbf8fd73a99911d0b1f13d6137be30c801d1fedf8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns details about multiple Redis commands using the same format as [`COMMAND`](https://redis.io/docs/latest/commands/command), except you can specify which commands are inspected.\n\nIf you request details about non-existing commands, nil is returned."},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>command-name [command-name ...]</code></summary>\n\nOne or more command names to return details for. If omitted, details for all commands are returned.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> COMMAND INFO get set eval\n1)  1) \"get\"\n    2) (integer) 2\n    3) 1) readonly\n       2) fast\n    4) (integer) 1\n    5) (integer) 1\n    6) (integer) 1\n    7) 1) @read\n       2) @string\n       3) @fast\n    8) (empty array)\n    9) 1) 1) \"flags\"\n          2) 1) RO\n             2) access\n          3) \"begin_search\"\n          4) 1) \"type\"\n             2) \"index\"\n             3) \"spec\"\n             4) 1) \"index\"\n                2) (integer) 1\n          5) \"find_keys\"\n          6) 1) \"type\"\n             2) \"range\"\n             3) \"spec\"\n             4) 1) \"lastkey\"\n                2) (integer) 0\n                3) \"keystep\"\n                4) (integer) 1\n                5) \"limit\"\n                6) (integer) 0\n   10) (empty array)\n2)  1) \"set\"\n    2) (integer) -3\n    3) 1) write\n       2) denyoom\n    4) (integer) 1\n    5) (integer) 1\n    6) (integer) 1\n    7) 1) @write\n       2) @string\n       3) @slow\n    8) (empty array)\n    9) 1) 1) \"notes\"\n          2) \"RW and ACCESS due to the optional `GET` argument\"\n          3) \"flags\"\n          4) 1) RW\n             2) access\n             3) update\n             4) variable_flags\n          5) \"begin_search\"\n          6) 1) \"type\"\n             2) \"index\"\n             3) \"spec\"\n             4) 1) \"index\"\n                2) (integer) 1\n          7) \"find_keys\"\n          8) 1) \"type\"\n             2) \"range\"\n             3) \"spec\"\n             4) 1) \"lastkey\"\n                2) (integer) 0\n                3) \"keystep\"\n                4) (integer) 1\n                5) \"limit\"\n                6) (integer) 0\n   10) (empty array)\n3)  1) \"eval\"\n    2) (integer) -3\n    3) 1) noscript\n       2) stale\n       3) skip_monitor\n       4) no_mandatory_keys\n       5) movablekeys\n       6) script_runner\n    4) (integer) 0\n    5) (integer) 0\n    6) (integer) 0\n    7) 1) @slow\n       2) @scripting\n    8) (empty array)\n    9) 1) 1) \"notes\"\n          2) \"We cannot tell how the keys will be used so we assume the worst, RW and UPDATE\"\n          3) \"flags\"\n          4) 1) RW\n             2) access\n             3) update\n          5) \"begin_search\"\n          6) 1) \"type\"\n             2) \"index\"\n             3) \"spec\"\n             4) 1) \"index\"\n                2) (integer) 2\n          7) \"find_keys\"\n          8) 1) \"type\"\n             2) \"keynum\"\n             3) \"spec\"\n             4) 1) \"keynumidx\"\n                2) (integer) 0\n                3) \"firstkey\"\n                4) (integer) 1\n                5) \"keystep\"\n                6) (integer) 1\n   10) (empty array)\nredis> COMMAND INFO foo evalsha config bar\n1) (nil)\n2)  1) \"evalsha\"\n    2) (integer) -3\n    3) 1) noscript\n       2) stale\n       3) skip_monitor\n       4) no_mandatory_keys\n       5) movablekeys\n       6) script_runner\n    4) (integer) 0\n    5) (integer) 0\n    6) (integer) 0\n    7) 1) @slow\n       2) @scripting\n    8) (empty array)\n    9) 1) 1) \"flags\"\n          2) 1) RW\n             2) access\n             3) update\n          3) \"begin_search\"\n          4) 1) \"type\"\n             2) \"index\"\n             3) \"spec\"\n             4) 1) \"index\"\n                2) (integer) 2\n          5) \"find_keys\"\n          6) 1) \"type\"\n             2) \"keynum\"\n             3) \"spec\"\n             4) 1) \"keynumidx\"\n                2) (integer) 0\n                3) \"firstkey\"\n                4) (integer) 1\n                5) \"keystep\"\n                6) (integer) 1\n   10) (empty array)\n3)  1) \"config\"\n    2) (integer) -2\n    3) (empty array)\n    4) (integer) 0\n    5) (integer) 0\n    6) (integer) 0\n    7) 1) @slow\n    8) (empty array)\n    9) (empty array)\n   10) 1)  1) \"config|set\"\n           2) (integer) -4\n           3) 1) admin\n              2) noscript\n              3) loading\n              4) stale\n           4) (integer) 0\n           5) (integer) 0\n           6) (integer) 0\n           7) 1) @admin\n              2) @slow\n              3) @dangerous\n           8) 1) \"request_policy:all_nodes\"\n              2) \"response_policy:all_succeeded\"\n           9) (empty array)\n          10) (empty array)\n       2)  1) \"config|rewrite\"\n           2) (integer) 2\n           3) 1) admin\n              2) noscript\n              3) loading\n              4) stale\n           4) (integer) 0\n           5) (integer) 0\n           6) (integer) 0\n           7) 1) @admin\n              2) @slow\n              3) @dangerous\n           8) 1) \"request_policy:all_nodes\"\n              2) \"response_policy:all_succeeded\"\n           9) (empty array)\n          10) (empty array)\n       3)  1) \"config|get\"\n           2) (integer) -3\n           3) 1) admin\n              2) noscript\n              3) loading\n              4) stale\n           4) (integer) 0\n           5) (integer) 0\n           6) (integer) 0\n           7) 1) @admin\n              2) @slow\n              3) @dangerous\n           8) (empty array)\n           9) (empty array)\n          10) (empty array)\n       4)  1) \"config|help\"\n           2) (integer) 2\n           3) 1) loading\n              2) stale\n           4) (integer) 0\n           5) (integer) 0\n           6) (integer) 0\n           7) 1) @slow\n           8) (empty array)\n           9) (empty array)\n          10) (empty array)\n       5)  1) \"config|resetstat\"\n           2) (integer) 2\n           3) 1) admin\n              2) noscript\n              3) loading\n              4) stale\n           4) (integer) 0\n           5) (integer) 0\n           6) (integer) 0\n           7) 1) @admin\n              2) @slow\n              3) @dangerous\n           8) 1) \"request_policy:all_nodes\"\n              2) \"response_policy:all_succeeded\"\n           9) (empty array)\n          10) (empty array)\n4) (nil)"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a nested list of command details.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a nested list of command details."}],"examples":[]}
{"schema_version":2,"id":"commands/command-list","title":"COMMAND LIST","url":"https://redis.io/docs/latest/commands/command-list/","summary":"Returns a list of command names.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"665a2a07be55cc03123e99db49ced95a74953be294c8ad3230c8ead4cae4a257","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Return an array of the Redis server's command names."},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>FILTERBY MODULE module-name | ACLCAT category | PATTERN pattern</code></summary>\n\nFilter the listed commands by module (`MODULE`), [ACL category](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl#command-categories) (`ACLCAT`), or a glob-style name pattern (`PATTERN`).\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of command names.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of command names."}],"examples":[]}
{"schema_version":2,"id":"commands/config","title":"CONFIG","url":"https://redis.io/docs/latest/commands/config/","summary":"A container for server configuration commands.","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"d26c3198c1b3b74adbd42d48e644fcd156c7458c5a97f1305139b11b45a8813c","sections":[{"id":"content","title":"Content","role":"content","text":"This is a container command for runtime configuration commands.\n\nTo see the list of available commands you can call [`CONFIG HELP`](https://redis.io/docs/latest/commands/config-help)."}],"examples":[]}
{"schema_version":2,"id":"commands/config-get","title":"CONFIG GET","url":"https://redis.io/docs/latest/commands/config-get/","summary":"Returns the effective values of configuration parameters.","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"6cb7be6abbd59714674d8826fba15be333a31400f87fa48ab2419714ad63a582","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `CONFIG GET` command is used to read the configuration parameters of a\nrunning Redis server.\nNot all the configuration parameters are supported in Redis 2.4, while Redis 2.6\ncan read the whole configuration of a server using this command.\n\nThe symmetric command used to alter the configuration at run time is `CONFIG\nSET`.\n\n`CONFIG GET` takes multiple arguments, which are glob-style patterns.\nAny configuration parameter matching any of the patterns are reported as a list\nof key-value pairs.\n\nYou can obtain a list of all the supported configuration parameters by typing\n`CONFIG GET *` in an open `redis-cli` prompt.\n\nAll the supported parameters have the same meaning of the equivalent\nconfiguration parameter used in the [redis.conf](https://redis.io/docs/latest/operate/oss_and_stack/management/config) file. The provided link has links to the `redis.conf` files for Redis version 6.2 and greater. See the [Redis souce code repo](https://github.com/redis/redis) for earlier versions."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>parameter [parameter ...]</code></summary>\n\nOne or more configuration parameter names or glob-style patterns to retrieve.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | [Only supports a subset of configuration settings.](https://redis.io/docs/latest/operate/rs/references/compatibility/config-settings) |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of configuration parameters matching the provided arguments.\n\n**RESP3:**\n\n[Map reply](../../develop/reference/protocol-spec#maps): a list of configuration parameters matching the provided arguments."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> config get *max-*-entries* maxmemory\n 1) \"maxmemory\"\n 2) \"0\"\n 3) \"hash-max-listpack-entries\"\n 4) \"512\"\n 5) \"hash-max-ziplist-entries\"\n 6) \"512\"\n 7) \"set-max-intset-entries\"\n 8) \"512\"\n 9) \"zset-max-listpack-entries\"\n10) \"128\"\n11) \"zset-max-ziplist-entries\"\n12) \"128\"","section_id":"examples"}]}
{"schema_version":2,"id":"commands/config-help","title":"CONFIG HELP","url":"https://redis.io/docs/latest/commands/config-help/","summary":"Returns helpful text about the different subcommands.","since":"5.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"234333ba568cb631899ff994bfd0e458c52df7dd14911cfab4a631f281d9b4b6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `CONFIG HELP` command returns a helpful text describing the different subcommands."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of sub-commands and their descriptions.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of sub-commands and their descriptions."}],"examples":[]}
{"schema_version":2,"id":"commands/config-resetstat","title":"CONFIG RESETSTAT","url":"https://redis.io/docs/latest/commands/config-resetstat/","summary":"Resets the server's statistics.","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"d493ee393955c1ea28b83be35fcb413dd735b3a9d576faf3bc11ff97282dc31e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Resets the statistics reported by Redis using the [`INFO`](https://redis.io/docs/latest/commands/info) and [`LATENCY HISTOGRAM`](https://redis.io/docs/latest/commands/latency-histogram) commands.\n\nThe following is a non-exhaustive list of values that are reset:\n\n* Keyspace hits and misses\n* Number of expired keys\n* Command and error statistics\n* Connections received, rejected and evicted\n* Persistence statistics\n* Active defragmentation statistics"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[]}
{"schema_version":2,"id":"commands/config-rewrite","title":"CONFIG REWRITE","url":"https://redis.io/docs/latest/commands/config-rewrite/","summary":"Persists the effective configuration to file.","since":"2.8.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"231d758c106e83d5ecae40f0c06aef95dace22e15bfb6b792306bfeea57b285e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `CONFIG REWRITE` command rewrites the `redis.conf` file the server was started with, applying the minimal changes needed to make it reflect the configuration currently used by the server, which may be different compared to the original one because of the use of the [`CONFIG SET`](https://redis.io/docs/latest/commands/config-set) command.\n\nThe rewrite is performed in a very conservative way:\n\n* Comments and the overall structure of the original redis.conf are preserved as much as possible.\n* If an option already exists in the old redis.conf file, it will be rewritten at the same position (line number).\n* If an option was not already present, but it is set to its default value, it is not added by the rewrite process.\n* If an option was not already present, but it is set to a non-default value, it is appended at the end of the file.\n* Non used lines are blanked. For instance if you used to have multiple `save` directives, but the current configuration has fewer or none as you disabled RDB persistence, all the lines will be blanked.\n\nCONFIG REWRITE is also able to rewrite the configuration file from scratch if the original one no longer exists for some reason. However if the server was started without a configuration file at all, the CONFIG REWRITE will just return an error."},{"id":"atomic-rewrite-process","title":"Atomic rewrite process","role":"content","text":"In order to make sure the redis.conf file is always consistent, that is, on errors or crashes you always end with the old file, or the new one, the rewrite is performed with a single `write(2)` call that has enough content to be at least as big as the old file. Sometimes additional padding in the form of comments is added in order to make sure the resulting file is big enough, and later the file gets truncated to remove the padding at the end."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` when the configuration was rewritten properly. Otherwise an error is returned.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` when the configuration was rewritten properly. Otherwise an error is returned."}],"examples":[]}
{"schema_version":2,"id":"commands/config-set","title":"CONFIG SET","url":"https://redis.io/docs/latest/commands/config-set/","summary":"Sets configuration parameters in-flight.","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"ec1a3e56de97c52d180675af891d6c28d8cf59dd695011793f887fa318e9c260","sections":[{"id":"overview","title":"Overview","role":"overview","text":"CONFIG SET reconfigures Redis at runtime without restarting the server. You can use it to change simple configuration parameters or switch between persistence options.\n\nRun [CONFIG GET *](https://redis.io/docs/latest/commands/config-get) to list the configuration parameters that CONFIG SET supports.\n\nAll the configuration parameters set using `CONFIG SET` are immediately loaded\nby Redis and will take effect starting with the next command executed.\n\nAll the supported parameters have the same meaning of the equivalent\nconfiguration parameter used in the [redis.conf](https://redis.io/docs/latest/operate/oss_and_stack/management/config) file. The provided link has links to the `redis.conf` files for Redis version 6.2 and greater. See the [Redis souce code repo](https://github.com/redis/redis) for earlier versions.\n\nIt is possible to switch persistence from RDB snapshotting to append-only file\n(and the other way around) using the `CONFIG SET` command.\nSee the [persistence page](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence) for more information.\n\n[code example]\n\nSetting the `appendonly` parameter as shown above will start a background process to save the initial append-only file\n(obtained from the in memory data set), and will append all the subsequent\ncommands on the append-only file, thus obtaining exactly the same effect of a\nRedis server that started with AOF turned on since the start.\n\nYou can have both the AOF enabled with RDB snapshotting if you want, the two\noptions are not mutually exclusive."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>parameter value [parameter value ...]</code></summary>\n\nOne or more configuration parameter-value pairs to set.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | [Only supports a subset of configuration settings.](https://redis.io/docs/latest/operate/rs/references/compatibility/config-settings) |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` when the configuration was set properly. Otherwise an error is returned.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` when the configuration was set properly. Otherwise an error is returned."}],"examples":[{"id":"overview-ex0","language":"plaintext","code":"CONFIG SET appendonly yes","section_id":"overview"}]}
{"schema_version":2,"id":"commands/copy","title":"COPY","url":"https://redis.io/docs/latest/commands/copy/","summary":"Copies the value of a key to a new key.","since":"6.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"7e5bf96c5ac3c7ed0dd678375d6fe4518c68d3270026d68ef80dbb563bde9e8d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nThis command copies the value stored at the `source` key to the `destination`\nkey.\n\nBy default, the `destination` key is created in the logical database used by the\nconnection. The `DB` option allows specifying an alternative logical database\nindex for the destination key.\n\nThe command returns zero when the `destination` key already exists. The\n`REPLACE` option removes the `destination` key before copying the value to it."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>source</code></summary>\n\nThe key to copy from.\n\n</details>\n\n<details open><summary><code>destination</code></summary>\n\nThe key to copy to.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>DB destination-db</code></summary>\n\nCopy the key to the given database index instead of the current one.\n\n</details>\n\n<details open><summary><code>REPLACE</code></summary>\n\nReplace the destination key if it already exists.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active\\*</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active\\*</nobr></span> | For Active-Active or clustered databases, the source and destination keys must be in the same hash slot.<br /><br />\\*Not supported for stream consumer group info. |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): `1` if _source_ was copied.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0` if _source_ was not copied.\n\n**RESP3:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): `1` if _source_ was copied.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0` if _source_ was not copied."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"SET dolly \"sheep\"\nCOPY dolly clone\nGET clone","section_id":"examples"}]}
{"schema_version":2,"id":"commands/dbsize","title":"DBSIZE","url":"https://redis.io/docs/latest/commands/dbsize/","summary":"Returns the number of keys in the database.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"27ff6cdb7de1e142fd639de08011ff946cee460f7c358f314b11b01c52491476","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nReturn the number of keys in the currently-selected database."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of keys in the currently-selected database.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of keys in the currently-selected database."}],"examples":[]}
{"schema_version":2,"id":"commands/debug","title":"DEBUG","url":"https://redis.io/docs/latest/commands/debug/","summary":"A container for debugging commands.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"3e113c99159918bd77f30705e7d7bbc8fceedcdeda4c21c8387e2e5cc25b666e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `DEBUG` command is an internal command.\nIt is meant to be used for developing and testing Redis."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"}],"examples":[]}
{"schema_version":2,"id":"commands/decr","title":"DECR","url":"https://redis.io/docs/latest/commands/decr/","summary":"Decrements the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:12+01:00","page_type":"content","content_hash":"a6a163a9f42f6daa3208abcb06846c0fe02fc103bbb0599242339c7485e6703c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Decrements the number stored at `key` by one.\nIf the key does not exist, it is set to `0` before performing the operation.\nAn error is returned if the key contains a value of the wrong type or contains a\nstring that can not be represented as integer.\nThis operation is limited to 64-bit signed integers.\n\nSee [`INCR`](https://redis.io/docs/latest/commands/incr) for extra information on increment/decrement operations."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SET mykey \"10\"\nOK\nredis> DECR mykey\n(integer) 9\nredis> SET mykey \"234293482390480948029348230948\"\nOK\nredis> DECR mykey\n(error) value is not an integer or out of range"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the value of the key after decrementing it.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the value of the key after decrementing it."}],"examples":[]}
{"schema_version":2,"id":"commands/decrby","title":"DECRBY","url":"https://redis.io/docs/latest/commands/decrby/","summary":"Decrements a number from the integer value of a key. Uses 0 as initial value if the key doesn't exist.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:12+01:00","page_type":"content","content_hash":"d610b9822dcdd022e9dff40cca7bd9c51a504a56b388ebfa80b83534631653f5","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `DECRBY` command reduces the value stored at the specified `key` by the specified `decrement`.\nIf the key does not exist, it is initialized with a value of `0` before performing the operation.\nIf the key's value is not of the correct type or cannot be represented as an integer, an error is returned.\nThis operation is limited to 64-bit signed integers.\n\nSee [`INCR`](https://redis.io/docs/latest/commands/incr) for extra information on increment/decrement operations."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key.\n\n</details>\n\n<details open><summary><code>decrement</code></summary>\n\nThe integer amount to subtract from the value.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SET mykey \"10\"\nOK\nredis> DECRBY mykey 3\n(integer) 7"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the value of the key after decrementing it.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the value of the key after decrementing it."}],"examples":[]}
{"schema_version":2,"id":"commands/del","title":"DEL","url":"https://redis.io/docs/latest/commands/del/","summary":"Deletes one or more keys.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"9056d045f346458c953ea01f6284db6916463e51087bdd9c0ab6c9246338d5e9","sections":[{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key [key ...]</code></summary>\n\nOne or more keys to delete.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"Foundational: Delete one or more keys using DEL (ignores non-existent keys, returns count of deleted keys)\n\n**Difficulty:** Beginner\n\n**Commands:** SET, DEL\n\n**Complexity:**\n- SET: O(1)\n- DEL: O(N)\n\n**Available in:** Redis CLI, C, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of keys that were removed.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of keys that were removed."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> SET key1 \"Hello\"\nOK\n> SET key2 \"World\"\nOK\n> DEL key1 key2 key3\n(integer) 2","section_id":"examples"},{"id":"examples-ex1","language":"c","code":"reply = redisCommand(c, \"SET key1 Hello\");\n    printf(\"%s\\n\", reply->str);\n    // >>> OK\n    freeReplyObject(reply);\n\n    reply = redisCommand(c, \"SET key2 World\");\n    printf(\"%s\\n\", reply->str);\n    // >>> OK\n    freeReplyObject(reply);\n\n    reply = redisCommand(c, \"DEL key1 key2 key3\");\n    printf(\"%lld\\n\", reply->integer);\n    // >>> 2\n    freeReplyObject(reply);","section_id":"examples"},{"id":"examples-ex2","language":"csharp","code":"bool delResult1 = db.StringSet(\"key1\", \"Hello\");\n        Console.WriteLine(delResult1);  // >>> true\n\n        bool delResult2 = db.StringSet(\"key2\", \"World\");\n        Console.WriteLine(delResult2);  // >>> true\n\n        long delResult3 = db.KeyDelete([\"key1\", \"key2\", \"key3\"]);\n        Console.WriteLine(delResult3);  // >>> 2","section_id":"examples"},{"id":"examples-ex3","language":"go","code":"delResult1, err := rdb.Set(ctx, \"key1\", \"Hello\", 0).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(delResult1) // >>> OK\n\n\tdelResult2, err := rdb.Set(ctx, \"key2\", \"World\", 0).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(delResult2) // >>> OK\n\n\tdelResult3, err := rdb.Del(ctx, \"key1\", \"key2\", \"key3\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(delResult3) // >>> 2","section_id":"examples"},{"id":"examples-ex4","language":"java","code":"CompletableFuture<Void> delExample = asyncCommands.set(\"key1\", \"Hello\")\n                    .thenCompose(r1 -> {\n                        System.out.println(r1);              // >>> OK\n                        return asyncCommands.set(\"key2\", \"World\");\n                    })\n                    .thenCompose(r2 -> {\n                        System.out.println(r2);              // >>> OK\n                        return asyncCommands.del(\"key1\", \"key2\", \"key3\");\n                    })\n                    .thenAccept(r3 -> {\n                        System.out.println(r3);              // >>> 2\n                    })\n                    .toCompletableFuture();","section_id":"examples"},{"id":"examples-ex5","language":"java","code":"Mono<Void> delExample = reactiveCommands.set(\"key1\", \"Hello\")\n                    .flatMap(r1 -> {\n                        System.out.println(r1);              // >>> OK\n                        return reactiveCommands.set(\"key2\", \"World\");\n                    })\n                    .flatMap(r2 -> {\n                        System.out.println(r2);              // >>> OK\n                        return reactiveCommands.del(\"key1\", \"key2\", \"key3\");\n                    })\n                    .doOnNext(r3 -> {\n                        System.out.println(r3);              // >>> 2\n                    })\n                    .then();","section_id":"examples"},{"id":"examples-ex6","language":"java","code":"String delResult1 = jedis.set(\"key1\", \"Hello\");\n        System.out.println(delResult1); // >>> OK\n\n        String delResult2 = jedis.set(\"key2\", \"World\");\n        System.out.println(delResult2); // >>> OK\n\n        long delResult3 = jedis.del(\"key1\", \"key2\", \"key3\");\n        System.out.println(delResult3); // >>> 2","section_id":"examples"},{"id":"examples-ex7","language":"javascript","code":"const delRes1 = await client.set('key1', 'Hello');\nconsole.log(delRes1); // OK\n\nconst delRes2 = await client.set('key2', 'World');\nconsole.log(delRes2); // OK\n\nconst delRes3 = await client.del(['key1', 'key2', 'key3']);\nconsole.log(delRes3); // 2","section_id":"examples"},{"id":"examples-ex8","language":"javascript","code":"console.log(await redis.set('key1', 'Hello')); // >>> OK\nconsole.log(await redis.set('key2', 'World')); // >>> OK\n\nconst delResult = await redis.del('key1', 'key2', 'key3');\nconsole.log(delResult); // >>> 2","section_id":"examples"},{"id":"examples-ex9","language":"javascript","code":"const delRes1 = await client.set('key1', 'Hello');\nconsole.log(delRes1); // OK\n\nconst delRes2 = await client.set('key2', 'World');\nconsole.log(delRes2); // OK\n\nconst delRes3 = await client.del(['key1', 'key2', 'key3']);\nconsole.log(delRes3); // 2","section_id":"examples"},{"id":"examples-ex10","language":"javascript","code":"console.log(await redis.set('key1', 'Hello')); // >>> OK\nconsole.log(await redis.set('key2', 'World')); // >>> OK\n\nconst delResult = await redis.del('key1', 'key2', 'key3');\nconsole.log(delResult); // >>> 2","section_id":"examples"},{"id":"examples-ex11","language":"php","code":"echo $r->set('key1', 'Hello') . PHP_EOL;             // >>> OK\n        echo $r->set('key2', 'World') . PHP_EOL;             // >>> OK\n\n        $delResult = $r->del('key1', 'key2', 'key3');\n        echo $delResult . PHP_EOL;                           // >>> 2","section_id":"examples"},{"id":"examples-ex12","language":"python","code":"res = r.set(\"key1\", \"Hello\")\nprint(res)\n# >>> True\n\nres = r.set(\"key2\", \"World\")\nprint(res)\n# >>> True\n\nres = r.delete(\"key1\", \"key2\", \"key3\")\nprint(res)\n# >>> 2","section_id":"examples"},{"id":"examples-ex13","language":"rust","code":"if let Ok(res) = r.set(\"key1\", \"Hello\").await {\n            let res: String = res;\n            println!(\"{res}\");    // >>> OK\n        }\n\n        if let Ok(res) = r.set(\"key2\", \"World\").await {\n            let res: String = res;\n            println!(\"{res}\");    // >>> OK\n        }\n\n        match r.del(&[\"key1\", \"key2\", \"key3\"]).await {\n            Ok(res) => {\n                let res: i32 = res;\n                println!(\"{res}\");    // >>> 2\n            },\n            Err(e) => {\n                println!(\"Error deleting keys: {e}\");\n                return;\n            }\n        }","section_id":"examples"},{"id":"examples-ex14","language":"rust","code":"if let Ok(res) = r.set(\"key1\", \"Hello\") {\n            let res: String = res;\n            println!(\"{res}\");    // >>> OK\n        }\n\n        if let Ok(res) = r.set(\"key2\", \"World\") {\n            let res: String = res;\n            println!(\"{res}\");    // >>> OK\n        }\n\n        match r.del(&[\"key1\", \"key2\", \"key3\"]) {\n            Ok(res) => {\n                let res: i32 = res;\n                println!(\"{res}\");    // >>> 2\n            },\n            Err(e) => {\n                println!(\"Error deleting keys: {e}\");\n                return;\n            }\n        }","section_id":"examples"}]}
{"schema_version":2,"id":"commands/delex","title":"DELEX","url":"https://redis.io/docs/latest/commands/delex/","summary":"Conditionally removes the specified key based on value or hash digest comparison.","since":"8.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:12+01:00","page_type":"content","content_hash":"d88c72ed8445ed0a4c1bdf861e8acb3006aed092fd2b038e37652a690a91cd44","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Conditionally removes the specified key based on value or hash digest comparison."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key to delete.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"The following options condition the deletion on the key's current value or [hash digest](#hash-digest). Only one may be specified.\n\n<details open><summary><code>IFEQ ifeq-value</code></summary>\n\nDelete the key only if its value is equal to `ifeq-value`.\n\n</details>\n\n<details open><summary><code>IFNE ifne-value</code></summary>\n\nDelete the key only if its value is not equal to `ifne-value`.\n\n</details>\n\n<details open><summary><code>IFDEQ ifdeq-digest</code></summary>\n\nDelete the key only if its hash digest is equal to `ifdeq-digest`.\n\n</details>\n\n<details open><summary><code>IFDNE ifdne-digest</code></summary>\n\nDelete the key only if its hash digest is not equal to `ifdne-digest`.\n\n</details>\n\nIn 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."},{"id":"details","title":"Details","role":"content","text":""},{"id":"hash-digest","title":"Hash Digest","role":"content","text":"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`](https://redis.io/docs/latest/commands/digest) command, which returns it as a hexadecimal string that you can use with the `IFDEQ` and `IFDNE` options, and also the [`SET`](https://redis.io/docs/latest/commands/set) command's `IFDEQ` and `IFDNE` options."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n- [Integer reply](https://redis.io/docs/latest/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.\n- [Simple error reply](https://redis.io/docs/latest/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.\n\n**RESP3:**\n\nOne of the following:\n- [Integer reply](https://redis.io/docs/latest/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.\n- [Simple error reply](https://redis.io/docs/latest/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."}],"examples":[]}
{"schema_version":2,"id":"commands/digest","title":"DIGEST","url":"https://redis.io/docs/latest/commands/digest/","summary":"Returns the hash digest of a string value.","since":"8.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:12+01:00","page_type":"content","content_hash":"e248f1b1973606f5566abc0dcd69d1ea2e068f6d3ed7a12f788dfe1a77618ed7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Get the [hash digest](#hash-digest) for the value stored in the specified key as a hexadecimal string. Keys must be of type string."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"> SET key1 \"Hello world\"\nOK\n> DIGEST key1\n\"b6acb9d84a38ff74\""},{"id":"details","title":"Details","role":"content","text":""},{"id":"hash-digest","title":"Hash Digest","role":"content","text":"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](https://redis.io/docs/latest/commands/set) command's `IFDEQ` and `IFDNE` options, and also the [DELEX](https://redis.io/docs/latest/commands/delex) command's `IFDEQ` and `IFDNE` options."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n- [Null bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) if the key does not exist.\n- [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if the key exists but holds a value which is not a string.\n- [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) the hash digest of the value stored in the key as a hexadecimal string.\n\n**RESP3:**\n\nOne of the following:\n\n- [Null bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) if the key does not exist.\n- [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if the key exists but holds a value which is not a string.\n- [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) the hash digest of the value stored in the key as a hexadecimal string."}],"examples":[]}
{"schema_version":2,"id":"commands/discard","title":"DISCARD","url":"https://redis.io/docs/latest/commands/discard/","summary":"Discards a transaction.","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"3b21e65faed8ed37795900806b275146841155dbb8202311b1bde0066cfc6cc0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Flushes all previously queued commands in a [transaction](https://redis.io/docs/latest/develop/using-commands/transactions) and restores the\nconnection state to normal.\n\nIf [`WATCH`](https://redis.io/docs/latest/commands/watch) was used, `DISCARD` unwatches all keys watched by the connection."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[]}
{"schema_version":2,"id":"commands/dump","title":"DUMP","url":"https://redis.io/docs/latest/commands/dump/","summary":"Returns a serialized representation of the value stored at a key.","since":"2.6.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"ec05975ed936cc1ff8100fdb3288886fee84ccd3831457c81c3bd57e29a4cbfe","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Serialize the value stored at key in a Redis-specific format and return it to\nthe user.\nThe returned value can be synthesized back into a Redis key using the [`RESTORE`](https://redis.io/docs/latest/commands/restore)\ncommand.\n\nThe serialization format is opaque and non-standard, however it has a few\nsemantic characteristics:\n\n* It contains a 64-bit checksum that is used to make sure errors will be\n  detected.\n  The [`RESTORE`](https://redis.io/docs/latest/commands/restore) command makes sure to check the checksum before synthesizing a\n  key using the serialized value.\n* Values are encoded in the same format used by RDB.\n* An RDB version is encoded inside the serialized value, so that different Redis\n  versions with incompatible RDB formats will refuse to process the serialized\n  value.\n\nThe serialized value does NOT contain expire information.\nIn order to capture the time to live of the current value the [`PTTL`](https://redis.io/docs/latest/commands/pttl) command\nshould be used.\n\nIf `key` does not exist a nil bulk reply is returned."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key to serialize.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): The serialized value of the key.\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): the key does not exist.\n\n**RESP3:**\n\nOne of the following:\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the serialized value of the key.\n* [Null reply](../../develop/reference/protocol-spec#nulls): the key does not exist."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> SET mykey 10\nOK\n> DUMP mykey\n\"\\x00\\xc0\\n\\n\\x00n\\x9fWE\\x0e\\xaec\\xbb\"","section_id":"examples"}]}
{"schema_version":2,"id":"commands/echo","title":"ECHO","url":"https://redis.io/docs/latest/commands/echo/","summary":"Returns the given string.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"a5cc45229ebcd75291c7e5b26bb3d0851164857b2dd1e69b09c96db1f552c0fb","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns `message`."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>message</code></summary>\n\nThe message to echo back.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ECHO \"Hello World!\"\n\"Hello World!\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the given string.\n\n**RESP3:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the given string."}],"examples":[]}
{"schema_version":2,"id":"commands/eval","title":"EVAL","url":"https://redis.io/docs/latest/commands/eval/","summary":"Executes a server-side Lua script.","since":"2.6.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"09e00de0481c5e0b811acfae5eeebe6765325e51abc52b5f776ce3e0cfae4cb3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\nExecutes a server-side [Lua](https:lua.org) script with the embedded Redis [Lua 5.1](https://redis.io/docs/latest/develop/programmability/lua-api) interpreter. The first argument is the script’s source code.\n\nThe second argument is the number of input key name arguments, followed by all the keys accessed by the script.\nThese input key names are made available to the script as the [`KEYS` global runtime variable](https://redis.io/docs/latest/develop/programmability/lua-api#the-keys-global-variable).\nAny additional input arguments should not represent names of keys.\n\n**Important:**\nto ensure the correct execution of scripts, both in standalone and clustered deployments, all keys that a script accesses must be explicitly provided as input key arguments.\nScripts should only access keys whose names are given as input arguments.\nScripts should never access keys with programmatically-generated names or based on the contents of data structures stored in the database.\n\n**Note:**\nin some cases, users will abuse Lua `EVAL` by embedding values in the script instead of providing them as arguments, thus generating a different script on each call to `EVAL`.\nThese values are added to the Lua interpreter and cached in Redis, consuming a large amount of memory over time.\n\nStarting with Redis 7.4, Redis evicts scripts loaded with `EVAL` or [`EVAL_RO`](https://redis.io/docs/latest/commands/eval_ro) from the script cache when the cache reaches a certain size. Redis evicts the least recently used scripts first. You can view the number of evicted scripts with the `evicted_scripts` field in [`INFO`](https://redis.io/docs/latest/commands/info).\n\nPlease refer to the [Redis Programmability](https://redis.io/docs/latest/develop/programmability) and [Introduction to Eval Scripts](https://redis.io/docs/latest/develop/programmability/eval-intro) for more information about Lua scripts."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>script</code></summary>\n\nThe Lua script to evaluate.\n\n</details>\n\n<details open><summary><code>numkeys</code></summary>\n\nThe number of key names that follow. Arguments after the keys are passed as regular arguments.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>key [key ...]</code></summary>\n\nThe key names the script accesses, provided to it via the Lua global `KEYS` runtime variable. There must be exactly `numkeys` of them.\n\n</details>\n\n<details open><summary><code>arg [arg ...]</code></summary>\n\nAdditional arguments provided to the script via the Lua `ARGV` variable.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"The following example will run a script that returns the first argument that it gets.\n\n[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nThe return value depends on the script that was executed.\n\n**RESP3:**\n\nThe return value depends on the script that was executed."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> EVAL \"return ARGV[1]\" 0 hello\n\"hello\"","section_id":"examples"}]}
{"schema_version":2,"id":"commands/eval_ro","title":"EVAL_RO","url":"https://redis.io/docs/latest/commands/eval_ro/","summary":"Executes a read-only server-side Lua script.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"83aff917ea51b43b974803d3ca566c0543af79be12f00d7dde4f903c94708c2f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Runs a read-only Lua script. Unlike EVAL, this command can’t run commands that modify data.\n\nFor more information about when to use this command versus [`EVAL`](https://redis.io/docs/latest/commands/eval), please refer to [Read-only scripts](https://redis.io/docs/latest/develop/programmability#read-only-scripts).\n\nFor more information about [`EVAL`](https://redis.io/docs/latest/commands/eval) scripts please refer to [Introduction to Eval Scripts](https://redis.io/docs/latest/develop/programmability/eval-intro)."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>script</code></summary>\n\nThe Lua script to evaluate. It must not modify data.\n\n</details>\n\n<details open><summary><code>numkeys</code></summary>\n\nThe number of key names that follow. Arguments after the keys are passed as regular arguments.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>key [key ...]</code></summary>\n\nThe key names the script accesses, provided to it via the Lua `KEYS` global variable. There must be exactly `numkeys` of them.\n\n</details>\n\n<details open><summary><code>arg [arg ...]</code></summary>\n\nAdditional arguments provided to the script via the Lua `ARGV` variable.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nThe return value depends on the script that was executed.\n\n**RESP3:**\n\nThe return value depends on the script that was executed."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> SET mykey \"Hello\"\nOK\n> EVAL_RO \"return redis.call('GET', KEYS[1])\" 1 mykey\n\"Hello\"\n> EVAL_RO \"return redis.call('DEL', KEYS[1])\" 1 mykey\n(error) ERR Error running script (call to b0d697da25b13e49157b2c214a4033546aba2104): @user_script:1: @user_script: 1: Write commands are not allowed from read-only scripts.","section_id":"examples"}]}
{"schema_version":2,"id":"commands/evalsha","title":"EVALSHA","url":"https://redis.io/docs/latest/commands/evalsha/","summary":"Executes a server-side Lua script by SHA1 digest.","since":"2.6.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"5b0f5c6fb7e73129072799ce3f64a76fe2b0536782e8f53c48975041e922fb29","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nEvaluate a script from the server's cache by its SHA1 digest.\n\nThe server caches scripts by using the [`SCRIPT LOAD`](https://redis.io/docs/latest/commands/script-load) command.\nThe command is otherwise identical to [`EVAL`](https://redis.io/docs/latest/commands/eval).\n\nPlease refer to the [Redis Programmability](https://redis.io/docs/latest/develop/programmability) and [Introduction to Eval Scripts](https://redis.io/docs/latest/develop/programmability/eval-intro) for more information about Lua scripts."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>sha1</code></summary>\n\nThe SHA1 digest of a script previously cached with `SCRIPT LOAD` or `EVAL`.\n\n</details>\n\n<details open><summary><code>numkeys</code></summary>\n\nThe number of key names that follow. Arguments after the keys are passed as regular arguments.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>key [key ...]</code></summary>\n\nThe key names the script accesses, provided to it via the Lua `KEYS` global variable. There must be exactly `numkeys` of them.\n\n</details>\n\n<details open><summary><code>arg [arg ...]</code></summary>\n\nAdditional arguments provided to the script via the Lua `ARGV` variable.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nThe return value depends on the script that was executed.\n\n**RESP3:**\n\nThe return value depends on the script that was executed."}],"examples":[]}
{"schema_version":2,"id":"commands/evalsha_ro","title":"EVALSHA_RO","url":"https://redis.io/docs/latest/commands/evalsha_ro/","summary":"Executes a read-only server-side Lua script by SHA1 digest.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"77e24ac2e4c97edf37844ee70c015026a874d8849ec1c2ebf2282150032d355c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a read-only variant of the [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) command that cannot execute commands that modify data.\n\nFor more information about when to use this command vs [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha), please refer to [Read-only scripts](https://redis.io/docs/latest/develop/programmability#read-only-scripts).\n\nFor more information about [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) scripts please refer to [Introduction to Eval Scripts](https://redis.io/docs/latest/develop/programmability/eval-intro)."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>sha1</code></summary>\n\nThe SHA1 digest of a read-only script previously cached with `SCRIPT LOAD` or `EVAL_RO`.\n\n</details>\n\n<details open><summary><code>numkeys</code></summary>\n\nThe number of key names that follow. Arguments after the keys are passed as regular arguments.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>key [key ...]</code></summary>\n\nThe key names the script accesses, provided to it via the Lua `KEYS` global variable. There must be exactly `numkeys` of them.\n\n</details>\n\n<details open><summary><code>arg [arg ...]</code></summary>\n\nAdditional arguments provided to the script via the Lua `ARGV` variable.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nThe return value depends on the script that was executed.\n\n**RESP3:**\n\nThe return value depends on the script that was executed."}],"examples":[]}
{"schema_version":2,"id":"commands/exec","title":"EXEC","url":"https://redis.io/docs/latest/commands/exec/","summary":"Executes all commands in a transaction.","since":"1.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"a48660a05517403d7826f37010ae39abc1acd3498b97b27c3178efbe328b9137","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nExecutes all previously queued commands in a [transaction](https://redis.io/docs/latest/develop/using-commands/transactions) and restores the\nconnection state to normal.\n\nWhen using [`WATCH`](https://redis.io/docs/latest/commands/watch), `EXEC` will execute commands only if the watched keys were\nnot modified, allowing for a [check-and-set mechanism](https://redis.io/docs/latest/develop/using-commands/transactions#cas)."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Array reply](../../develop/reference/protocol-spec#arrays): each element being the reply to each of the commands in the atomic transaction.\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): the transaction was aborted because a `WATCH`ed key was touched.\n\n**RESP3:**\n\nOne of the following:\n* [Array reply](../../develop/reference/protocol-spec#arrays): each element being the reply to each of the commands in the atomic transaction.\n* [Null reply](../../develop/reference/protocol-spec#nulls): the transaction was aborted because a `WATCH`ed key was touched."}],"examples":[]}
{"schema_version":2,"id":"commands/exists","title":"EXISTS","url":"https://redis.io/docs/latest/commands/exists/","summary":"Determines whether one or more keys exist.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"1601af7e80adfd115aeccb4cd20dc67ed6dd67f2b58b65151f7a14ec40135478","sections":[{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key [key ...]</code></summary>\n\nOne or more keys to check for existence. A repeated key is counted once per occurrence.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"Foundational: Check if one or more keys exist using EXISTS (returns count of existing keys, useful for conditional logic)\n\n**Difficulty:** Beginner\n\n**Commands:** SET, EXISTS\n\n**Complexity:**\n- SET: O(1)\n- EXISTS: O(N)\n\n**Available in:** Redis CLI, C, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of keys that exist from those specified as arguments.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of keys that exist from those specified as arguments."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> SET key1 \"Hello\"\n> EXISTS key1\n> EXISTS nosuchkey\n> SET key2 \"World\"\n> EXISTS key1 key2 nosuchkey","section_id":"examples"},{"id":"examples-ex1","language":"c","code":"reply = redisCommand(c, \"SET key1 Hello\");\n    printf(\"%s\\n\", reply->str);\n    // >>> OK\n    freeReplyObject(reply);\n\n    reply = redisCommand(c, \"EXISTS key1\");\n    printf(\"%lld\\n\", reply->integer);\n    // >>> 1\n    freeReplyObject(reply);\n\n    reply = redisCommand(c, \"EXISTS nosuchkey\");\n    printf(\"%lld\\n\", reply->integer);\n    // >>> 0\n    freeReplyObject(reply);\n\n    reply = redisCommand(c, \"SET key2 World\");\n    printf(\"%s\\n\", reply->str);\n    // >>> OK\n    freeReplyObject(reply);\n\n    reply = redisCommand(c, \"EXISTS key1 key2 nosuchkey\");\n    printf(\"%lld\\n\", reply->integer);\n    // >>> 2\n    freeReplyObject(reply);","section_id":"examples"},{"id":"examples-ex2","language":"csharp","code":"bool existsResult1 = db.StringSet(\"key1\", \"Hello\");\n        Console.WriteLine(existsResult1);  // >>> true\n\n        bool existsResult2 = db.KeyExists(\"key1\");\n        Console.WriteLine(existsResult2);  // >>> true\n\n        bool existsResult3 = db.KeyExists(\"nosuchkey\");\n        Console.WriteLine(existsResult3);  // >>> false\n\n        bool existsResult4 = db.StringSet(\"key2\", \"World\");\n        Console.WriteLine(existsResult4);  // >>> true\n\n        long existsResult5 = db.KeyExists([\"key1\", \"key2\", \"nosuchkey\"]);\n        Console.WriteLine(existsResult5);  // >>> 2","section_id":"examples"},{"id":"examples-ex3","language":"go","code":"existsResult1, err := rdb.Set(ctx, \"key1\", \"Hello\", 0).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(existsResult1) // >>> OK\n\n\texistsResult2, err := rdb.Exists(ctx, \"key1\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(existsResult2) // >>> 1\n\n\texistsResult3, err := rdb.Exists(ctx, \"nosuchkey\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(existsResult3) // >>> 0\n\n\texistsResult4, err := rdb.Set(ctx, \"key2\", \"World\", 0).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(existsResult4) // >>> OK\n\n\texistsResult5, err := rdb.Exists(ctx, \"key1\", \"key2\", \"nosuchkey\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(existsResult5) // >>> 2","section_id":"examples"},{"id":"examples-ex4","language":"java","code":"CompletableFuture<Void> existsExample = asyncCommands.set(\"key1\", \"Hello\").thenCompose(res1 -> {\n                System.out.println(res1); // >>> OK\n\n                return asyncCommands.exists(\"key1\");\n            }).thenCompose(res2 -> {\n                System.out.println(res2); // >>> 1\n\n                return asyncCommands.exists(\"nosuchkey\");\n            }).thenCompose(res3 -> {\n                System.out.println(res3); // >>> 0\n\n                return asyncCommands.set(\"key2\", \"World\");\n            }).thenCompose(res4 -> {\n                System.out.println(res4); // >>> OK\n\n                return asyncCommands.exists(\"key1\", \"key2\", \"nosuchkey\");\n            }).thenAccept(res5 -> {\n                System.out.println(res5); // >>> 2\n            }).toCompletableFuture();","section_id":"examples"},{"id":"examples-ex5","language":"java","code":"Mono<Void> existsExample = reactiveCommands.set(\"key1\", \"Hello\").doOnNext(res1 -> {\n                System.out.println(res1); // >>> OK\n            }).then(reactiveCommands.exists(\"key1\")).doOnNext(res2 -> {\n                System.out.println(res2); // >>> 1\n            }).then(reactiveCommands.exists(\"nosuchkey\")).doOnNext(res3 -> {\n                System.out.println(res3); // >>> 0\n            }).then(reactiveCommands.set(\"key2\", \"World\")).doOnNext(res4 -> {\n                System.out.println(res4); // >>> OK\n            }).then(reactiveCommands.exists(\"key1\", \"key2\", \"nosuchkey\")).doOnNext(res5 -> {\n                System.out.println(res5); // >>> 2\n            }).then();","section_id":"examples"},{"id":"examples-ex6","language":"java","code":"String existsResult1 = jedis.set(\"key1\", \"Hello\");\n        System.out.println(existsResult1); // >>> OK\n\n        boolean existsResult2 = jedis.exists(\"key1\");\n        System.out.println(existsResult2); // >>> true\n\n        boolean existsResult3 = jedis.exists(\"nosuchkey\");\n        System.out.println(existsResult3); // >>> false\n\n        String existsResult4 = jedis.set(\"key2\", \"World\");\n        System.out.println(existsResult4); // >>> OK\n\n        long existsResult5 = jedis.exists(\"key1\", \"key2\", \"nosuchkey\");\n        System.out.println(existsResult5); // >>> 2","section_id":"examples"},{"id":"examples-ex7","language":"javascript","code":"const existsRes1 = await client.set('key1', 'Hello');\nconsole.log(existsRes1); // OK\n\nconst existsRes2 = await client.exists('key1');\nconsole.log(existsRes2); // 1\n\nconst existsRes3 = await client.exists('nosuchkey');\nconsole.log(existsRes3); // 0\n\nconst existsRes4 = await client.set('key2', 'World');\nconsole.log(existsRes4); // OK\n\nconst existsRes5 = await client.exists(['key1', 'key2', 'nosuchkey']);\nconsole.log(existsRes5); // 2","section_id":"examples"},{"id":"examples-ex8","language":"javascript","code":"console.log(await redis.set('key1', 'Hello')); // >>> OK\n\nconsole.log(await redis.exists('key1')); // >>> 1\nconsole.log(await redis.exists('nosuchkey')); // >>> 0\n\nconsole.log(await redis.set('key2', 'World')); // >>> OK\n\nconst existsResult = await redis.exists('key1', 'key2', 'nosuchkey');\nconsole.log(existsResult); // >>> 2","section_id":"examples"},{"id":"examples-ex9","language":"javascript","code":"const existsRes1 = await client.set('key1', 'Hello');\nconsole.log(existsRes1); // OK\n\nconst existsRes2 = await client.exists('key1');\nconsole.log(existsRes2); // 1\n\nconst existsRes3 = await client.exists('nosuchkey');\nconsole.log(existsRes3); // 0\n\nconst existsRes4 = await client.set('key2', 'World');\nconsole.log(existsRes4); // OK\n\nconst existsRes5 = await client.exists(['key1', 'key2', 'nosuchkey']);\nconsole.log(existsRes5); // 2","section_id":"examples"},{"id":"examples-ex10","language":"javascript","code":"console.log(await redis.set('key1', 'Hello')); // >>> OK\n\nconsole.log(await redis.exists('key1')); // >>> 1\nconsole.log(await redis.exists('nosuchkey')); // >>> 0\n\nconsole.log(await redis.set('key2', 'World')); // >>> OK\n\nconst existsResult = await redis.exists('key1', 'key2', 'nosuchkey');\nconsole.log(existsResult); // >>> 2","section_id":"examples"},{"id":"examples-ex11","language":"php","code":"$existsResult1 = $r->set('key1', 'Hello');\n        echo $existsResult1 . PHP_EOL; // >>> OK\n\n        $existsResult2 = $r->exists('key1');\n        echo $existsResult2 . PHP_EOL; // >>> 1\n\n        $existsResult3 = $r->exists('nosuchkey');\n        echo $existsResult3 . PHP_EOL; // >>> 0\n\n        $existsResult4 = $r->set('key2', 'World');\n        echo $existsResult4 . PHP_EOL; // >>> OK\n\n        $existsResult5 = $r->exists('key1', 'key2', 'nosuchkey');\n        echo $existsResult5 . PHP_EOL; // >>> 2","section_id":"examples"},{"id":"examples-ex12","language":"python","code":"res = r.set(\"key1\", \"Hello\")\nprint(res)\n# >>> True\n\nres = r.exists(\"key1\")\nprint(res)\n# >>> 1\n\nres = r.exists(\"nosuchkey\")\nprint(res)\n# >>> 0\n\nres = r.set(\"key2\", \"World\")\nprint(res)\n# >>> True\n\nres = r.exists(\"key1\", \"key2\", \"nosuchkey\")\nprint(res)\n# >>> 2","section_id":"examples"},{"id":"examples-ex13","language":"rust","code":"if let Ok(res) = r.set(\"key1\", \"Hello\").await {\n            let res: String = res;\n            println!(\"{res}\");    // >>> OK\n        }\n\n        match r.exists(\"key1\").await {\n            Ok(res) => {\n                let res: i32 = res;\n                println!(\"{res}\");    // >>> 1\n            },\n            Err(e) => {\n                println!(\"Error checking key existence: {e}\");\n                return;\n            }\n        }\n\n        match r.exists(\"nosuchkey\").await {\n            Ok(res) => {\n                let res: i32 = res;\n                println!(\"{res}\");    // >>> 0\n            },\n            Err(e) => {\n                println!(\"Error checking key existence: {e}\");\n                return;\n            }\n        }\n\n        if let Ok(res) = r.set(\"key2\", \"World\").await {\n            let res: String = res;\n            println!(\"{res}\");    // >>> OK\n        }\n\n        match r.exists(&[\"key1\", \"key2\", \"nosuchkey\"]).await {\n            Ok(res) => {\n                let res: i32 = res;\n                println!(\"{res}\");    // >>> 2\n            },\n            Err(e) => {\n                println!(\"Error checking key existence: {e}\");\n                return;\n            }\n        }","section_id":"examples"},{"id":"examples-ex14","language":"rust","code":"if let Ok(res) = r.set(\"key1\", \"Hello\") {\n            let res: String = res;\n            println!(\"{res}\");    // >>> OK\n        }\n\n        match r.exists(\"key1\") {\n            Ok(res) => {\n                let res: i32 = res;\n                println!(\"{res}\");    // >>> 1\n            },\n            Err(e) => {\n                println!(\"Error checking key existence: {e}\");\n                return;\n            }\n        }\n\n        match r.exists(\"nosuchkey\") {\n            Ok(res) => {\n                let res: i32 = res;\n                println!(\"{res}\");    // >>> 0\n            },\n            Err(e) => {\n                println!(\"Error checking key existence: {e}\");\n                return;\n            }\n        }\n\n        if let Ok(res) = r.set(\"key2\", \"World\") {\n            let res: String = res;\n            println!(\"{res}\");    // >>> OK\n        }\n\n        match r.exists(&[\"key1\", \"key2\", \"nosuchkey\"]) {\n            Ok(res) => {\n                let res: i32 = res;\n                println!(\"{res}\");    // >>> 2\n            },\n            Err(e) => {\n                println!(\"Error checking key existence: {e}\");\n                return;\n            }\n        }","section_id":"examples"}]}
{"schema_version":2,"id":"commands/expire","title":"EXPIRE","url":"https://redis.io/docs/latest/commands/expire/","summary":"Sets the expiration time of a key in seconds.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"4de12db1e86aae54fc6dce0a7f891fbffe6e9be75d4a8d6d55c0223fd300e948","sections":[{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key.\n\n</details>\n\n<details open><summary><code>seconds</code></summary>\n\nThe time to live, in seconds. The key is deleted after this many seconds.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"These options are mutually exclusive.\n\n<details open><summary><code>NX</code></summary>\n\nSet the expiry only when the key has no expiry.\n\n</details>\n\n<details open><summary><code>XX</code></summary>\n\nSet the expiry only when the key already has an expiry.\n\n</details>\n\n<details open><summary><code>GT</code></summary>\n\nSet the expiry only when the new expiry is greater than the current one. A non-volatile key is treated as an infinite TTL for the purpose of `GT`.\n\n</details>\n\n<details open><summary><code>LT</code></summary>\n\nSet the expiry only when the new expiry is less than the current one. A non-volatile key is treated as an infinite TTL for the purpose of `LT`.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"Foundational: Set key expiration time using EXPIRE (supports conditional options NX/XX/GT/LT, returns 1 if set or 0 if not)\n\n**Difficulty:** Beginner\n\n**Commands:** SET, EXPIRE, TTL\n\n**Complexity:**\n- SET: O(1)\n- EXPIRE: O(1)\n- TTL: O(1)\n\n**Available in:** Redis CLI, C, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"details","title":"Details","role":"content","text":""},{"id":"refreshing-expires","title":"Refreshing expires","role":"content","text":"It is possible to call `EXPIRE` using as argument a key that already has an\nexisting expire set.\nIn this case the time to live of a key is _updated_ to the new value.\nThere are many useful applications for this, an example is documented in the\n_Navigation session_ pattern section below."},{"id":"differences-in-redis-prior-to-213","title":"Differences in Redis prior to 2.1.3","role":"content","text":"In Redis versions prior to 2.1.3 altering a key with an expire set using a\ncommand altering its value had the effect of removing the key entirely.\nThis semantics was needed because of limitations in the replication layer that\nare now fixed.\n\n`EXPIRE` would return 0 and not alter the timeout for a key with a timeout set."},{"id":"pattern-navigation-session","title":"Pattern: navigation session","role":"content","text":"Imagine you have a web service and you are interested in the latest N pages\n_recently_ visited by your users, such that each adjacent page view was not\nperformed more than 60 seconds after the previous.\nConceptually you may consider this set of page views as a _Navigation session_\nof your user, that may contain interesting information about what kind of\nproducts he or she is looking for currently, so that you can recommend related\nproducts.\n\nYou can easily model this pattern in Redis using the following strategy: every\ntime the user does a page view you call the following commands:\n\n[code example]\n\nIf the user will be idle more than 60 seconds, the key will be deleted and only\nsubsequent page views that have less than 60 seconds of difference will be\nrecorded.\n\nThis pattern is easily modified to use counters using [`INCR`](https://redis.io/docs/latest/commands/incr) instead of lists\nusing [`RPUSH`](https://redis.io/docs/latest/commands/rpush)."},{"id":"appendix-redis-expires","title":"Appendix: Redis expires","role":"content","text":"#### Keys with an expire\n\nNormally Redis keys are created without an associated time to live.\nThe key will simply live forever, unless it is removed by the user in an\nexplicit way, for instance using the [`DEL`](https://redis.io/docs/latest/commands/del) command.\n\nThe `EXPIRE` family of commands is able to associate an expire to a given key,\nat the cost of some additional memory used by the key.\nWhen a key has an expire set, Redis will make sure to remove the key when the\nspecified amount of time elapsed.\n\nThe key time to live can be updated or entirely removed using the `EXPIRE` and\n[`PERSIST`](https://redis.io/docs/latest/commands/persist) command (or other strictly related commands).\n\n#### Expire accuracy\n\nIn Redis 2.4 the expire might not be pin-point accurate, and it could be between\nzero to one seconds out.\n\nSince Redis 2.6 the expire error is from 0 to 1 milliseconds.\n\n#### Expires and persistence\n\nKeys expiring information is stored as absolute Unix timestamps (in milliseconds\nin case of Redis version 2.6 or greater).\nThis means that the time is flowing even when the Redis instance is not active.\n\nFor expires to work well, the computer time must be taken stable.\nIf you move an RDB file from two computers with a big desync in their clocks,\nfunny things may happen (like all the keys loaded to be expired at loading\ntime).\n\nEven running instances will always check the computer clock, so for instance if\nyou set a key with a time to live of 1000 seconds, and then set your computer\ntime 2000 seconds in the future, the key will be expired immediately, instead of\nlasting for 1000 seconds.\n\n#### How Redis expires keys\n\nRedis keys are expired in two ways: a passive way and an active way.\n\nA key is passively expired when a client tries to access it and the\nkey is timed out.\n\nHowever, this is not enough as there are expired keys that will never be\naccessed again.\nThese keys should be expired anyway, so periodically, Redis tests a few keys at\nrandom amongst the set of keys with an expiration.\nAll the keys that are already expired are deleted from the keyspace.\n\n#### How expires are handled in the replication link and AOF file\n\nIn order to obtain a correct behavior without sacrificing consistency, when a\nkey expires, a [`DEL`](https://redis.io/docs/latest/commands/del) operation is synthesized in both the AOF file and gains all\nthe attached replicas nodes.\nThis way the expiration process is centralized in the master instance, and there\nis no chance of consistency errors.\n\nHowever while the replicas connected to a master will not expire keys\nindependently (but will wait for the [`DEL`](https://redis.io/docs/latest/commands/del) coming from the master), they'll\nstill take the full state of the expires existing in the dataset, so when a\nreplica is elected to master it will be able to expire the keys independently,\nfully acting as a master.\n\n####  Redis Search and expiration\n\nStarting with Redis 8, Redis Search has enhanced behavior when handling expiring keys. For detailed information about how [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) and [`FT.AGGREGATE`](https://redis.io/docs/latest/commands/ft.aggregate) commands interact with expiring keys, see [Key and field expiration behavior](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/expiration)."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0` if the timeout was not set; for example, the key doesn't exist, or the operation was skipped because of the provided arguments.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `1` if the timeout was set.\n\n**RESP3:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0` if the timeout was not set; for example, the key doesn't exist, or the operation was skipped because of the provided arguments.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `1` if the timeout was set."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> SET mykey \"Hello\"\nOK\n> EXPIRE mykey 10\n(integer) 1\n> TTL mykey\n(integer) 10\n> SET mykey \"Hello World\"\nOK\n> TTL mykey\n(integer) -1\n> EXPIRE mykey 10 XX\n(integer) 0\n> TTL mykey\n(integer) -1\n> EXPIRE mykey 10 NX\n(integer) 1\n> TTL mykey\n(integer) 10","section_id":"examples"},{"id":"examples-ex1","language":"c","code":"reply = redisCommand(c, \"SET mykey Hello\");\n    printf(\"%s\\n\", reply->str);\n    // >>> OK\n    freeReplyObject(reply);\n\n    reply = redisCommand(c, \"EXPIRE mykey 10\");\n    printf(\"%lld\\n\", reply->integer);\n    // >>> 1\n    freeReplyObject(reply);\n\n    reply = redisCommand(c, \"TTL mykey\");\n    printf(\"%lld\\n\", reply->integer);\n    // >>> 10\n    freeReplyObject(reply);\n\n    // Overwriting a key with SET clears its expiry.\n    reply = redisCommand(c, \"SET mykey %s\", \"Hello World\");\n    printf(\"%s\\n\", reply->str);\n    // >>> OK\n    freeReplyObject(reply);\n\n    reply = redisCommand(c, \"TTL mykey\");\n    printf(\"%lld\\n\", reply->integer);\n    // >>> -1\n    freeReplyObject(reply);\n\n    // XX only sets the expiry when one already exists, so this is a no-op.\n    reply = redisCommand(c, \"EXPIRE mykey 10 XX\");\n    printf(\"%lld\\n\", reply->integer);\n    // >>> 0\n    freeReplyObject(reply);\n\n    reply = redisCommand(c, \"TTL mykey\");\n    printf(\"%lld\\n\", reply->integer);\n    // >>> -1\n    freeReplyObject(reply);\n\n    // NX only sets the expiry when there is none, so this one applies.\n    reply = redisCommand(c, \"EXPIRE mykey 10 NX\");\n    printf(\"%lld\\n\", reply->integer);\n    // >>> 1\n    freeReplyObject(reply);\n\n    reply = redisCommand(c, \"TTL mykey\");\n    printf(\"%lld\\n\", reply->integer);\n    // >>> 10\n    freeReplyObject(reply);","section_id":"examples"},{"id":"examples-ex2","language":"csharp","code":"bool expireResult1 = db.StringSet(\"mykey\", \"Hello\");\n        Console.WriteLine(expireResult1);   // >>> true\n\n        bool expireResult2 = db.KeyExpire(\"mykey\", new TimeSpan(0, 0, 10));\n        Console.WriteLine(expireResult2);   // >>> true\n\n        TimeSpan expireResult3 = db.KeyTimeToLive(\"mykey\") ?? TimeSpan.Zero;\n        Console.WriteLine(Math.Round(expireResult3.TotalSeconds));   // >>> 10\n\n        bool expireResult4 = db.StringSet(\"mykey\", \"Hello World\");\n        Console.WriteLine(expireResult4);   // >>> true\n\n        TimeSpan expireResult5 = db.KeyTimeToLive(\"mykey\") ?? TimeSpan.Zero;\n        Console.WriteLine(Math.Round(expireResult5.TotalSeconds).ToString());   // >>> 0\n\n        bool expireResult6 = db.KeyExpire(\"mykey\", new TimeSpan(0, 0, 10), ExpireWhen.HasExpiry);\n        Console.WriteLine(expireResult6);   // >>> false\n\n        TimeSpan expireResult7 = db.KeyTimeToLive(\"mykey\") ?? TimeSpan.Zero;\n        Console.WriteLine(Math.Round(expireResult7.TotalSeconds));   // >>> 0\n\n        bool expireResult8 = db.KeyExpire(\"mykey\", new TimeSpan(0, 0, 10), ExpireWhen.HasNoExpiry);\n        Console.WriteLine(expireResult8);   // >>> true\n\n        TimeSpan expireResult9 = db.KeyTimeToLive(\"mykey\") ?? TimeSpan.Zero;\n        Console.WriteLine(Math.Round(expireResult9.TotalSeconds));   // >>> 10","section_id":"examples"},{"id":"examples-ex3","language":"go","code":"expireResult1, err := rdb.Set(ctx, \"mykey\", \"Hello\", 0).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(expireResult1) // >>> OK\n\n\texpireResult2, err := rdb.Expire(ctx, \"mykey\", 10*time.Second).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(expireResult2) // >>> true\n\n\texpireResult3, err := rdb.TTL(ctx, \"mykey\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(math.Round(expireResult3.Seconds())) // >>> 10\n\n\texpireResult4, err := rdb.Set(ctx, \"mykey\", \"Hello World\", 0).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(expireResult4) // >>> OK\n\n\texpireResult5, err := rdb.TTL(ctx, \"mykey\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(expireResult5) // >>> -1ns\n\n\texpireResult6, err := rdb.ExpireXX(ctx, \"mykey\", 10*time.Second).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(expireResult6) // >>> false\n\n\texpireResult7, err := rdb.TTL(ctx, \"mykey\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(expireResult7) // >>> -1ns\n\n\texpireResult8, err := rdb.ExpireNX(ctx, \"mykey\", 10*time.Second).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(expireResult8) // >>> true\n\n\texpireResult9, err := rdb.TTL(ctx, \"mykey\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(math.Round(expireResult9.Seconds())) // >>> 10","section_id":"examples"},{"id":"examples-ex4","language":"java","code":"CompletableFuture<Void> expireExample = asyncCommands.set(\"mykey\", \"Hello\")\n                    .thenCompose(r1 -> {\n                        System.out.println(r1);              // >>> OK\n                        return asyncCommands.expire(\"mykey\", 10);\n                    })\n                    .thenCompose(r2 -> {\n                        System.out.println(r2);              // >>> true\n                        return asyncCommands.ttl(\"mykey\");\n                    })\n                    .thenCompose(r3 -> {\n                        System.out.println(r3);              // >>> 10\n                        // Overwriting a key with SET clears its expiry.\n                        return asyncCommands.set(\"mykey\", \"Hello World\");\n                    })\n                    .thenCompose(r4 -> {\n                        System.out.println(r4);              // >>> OK\n                        return asyncCommands.ttl(\"mykey\");\n                    })\n                    .thenCompose(r5 -> {\n                        System.out.println(r5);              // >>> -1\n                        // XX only sets the expiry when one already exists, so this is a no-op.\n                        return asyncCommands.expire(\"mykey\", 10, ExpireArgs.Builder.xx());\n                    })\n                    .thenCompose(r6 -> {\n                        System.out.println(r6);              // >>> false\n                        return asyncCommands.ttl(\"mykey\");\n                    })\n                    .thenCompose(r7 -> {\n                        System.out.println(r7);              // >>> -1\n                        // NX only sets the expiry when there is none, so this one applies.\n                        return asyncCommands.expire(\"mykey\", 10, ExpireArgs.Builder.nx());\n                    })\n                    .thenCompose(r8 -> {\n                        System.out.println(r8);              // >>> true\n                        return asyncCommands.ttl(\"mykey\");\n                    })\n                    .thenAccept(r9 -> {\n                        System.out.println(r9);              // >>> 10\n                    })\n                    .toCompletableFuture();","section_id":"examples"},{"id":"examples-ex5","language":"java","code":"Mono<Void> expireExample = reactiveCommands.set(\"mykey\", \"Hello\")\n                    .flatMap(r1 -> {\n                        System.out.println(r1);              // >>> OK\n                        return reactiveCommands.expire(\"mykey\", 10);\n                    })\n                    .flatMap(r2 -> {\n                        System.out.println(r2);              // >>> true\n                        return reactiveCommands.ttl(\"mykey\");\n                    })\n                    .flatMap(r3 -> {\n                        System.out.println(r3);              // >>> 10\n                        // Overwriting a key with SET clears its expiry.\n                        return reactiveCommands.set(\"mykey\", \"Hello World\");\n                    })\n                    .flatMap(r4 -> {\n                        System.out.println(r4);              // >>> OK\n                        return reactiveCommands.ttl(\"mykey\");\n                    })\n                    .flatMap(r5 -> {\n                        System.out.println(r5);              // >>> -1\n                        // XX only sets the expiry when one already exists, so this is a no-op.\n                        return reactiveCommands.expire(\"mykey\", 10, ExpireArgs.Builder.xx());\n                    })\n                    .flatMap(r6 -> {\n                        System.out.println(r6);              // >>> false\n                        return reactiveCommands.ttl(\"mykey\");\n                    })\n                    .flatMap(r7 -> {\n                        System.out.println(r7);              // >>> -1\n                        // NX only sets the expiry when there is none, so this one applies.\n                        return reactiveCommands.expire(\"mykey\", 10, ExpireArgs.Builder.nx());\n                    })\n                    .flatMap(r8 -> {\n                        System.out.println(r8);              // >>> true\n                        return reactiveCommands.ttl(\"mykey\");\n                    })\n                    .doOnNext(r9 -> {\n                        System.out.println(r9);              // >>> 10\n                    })\n                    .then();","section_id":"examples"},{"id":"examples-ex6","language":"java","code":"String expireResult1 = jedis.set(\"mykey\", \"Hello\");\n        System.out.println(expireResult1);  // >>> OK\n\n        long expireResult2 = jedis.expire(\"mykey\", 10);\n        System.out.println(expireResult2);  // >>> 1\n\n        long expireResult3 = jedis.ttl(\"mykey\");\n        System.out.println(expireResult3);  // >>> 10\n\n        String expireResult4 = jedis.set(\"mykey\", \"Hello World\");\n        System.out.println(expireResult4);  // >>> OK\n\n        long expireResult5 = jedis.ttl(\"mykey\");\n        System.out.println(expireResult5);  // >>> -1\n\n        long expireResult6 = jedis.expire(\"mykey\", 10, ExpiryOption.XX);\n        System.out.println(expireResult6);  // >>> 0\n\n        long expireResult7 = jedis.ttl(\"mykey\");\n        System.out.println(expireResult7);  // >>> -1\n\n        long expireResult8 = jedis.expire(\"mykey\", 10, ExpiryOption.NX);\n        System.out.println(expireResult8);  // >>> 1\n\n        long expireResult9 = jedis.ttl(\"mykey\");\n        System.out.println(expireResult9);  // >>> 10","section_id":"examples"},{"id":"examples-ex7","language":"javascript","code":"const expireRes1 = await client.set('mykey', 'Hello');\nconsole.log(expireRes1); // OK\n\nconst expireRes2 = await client.expire('mykey', 10);\nconsole.log(expireRes2); // 1\n\nconst expireRes3 = await client.ttl('mykey');\nconsole.log(expireRes3); // 10\n\nconst expireRes4 = await client.set('mykey', 'Hello World');\nconsole.log(expireRes4); // OK\n\nconst expireRes5 = await client.ttl('mykey');\nconsole.log(expireRes5); // -1\n\nconst expireRes6 = await client.expire('mykey', 10, \"XX\");\nconsole.log(expireRes6); // 0\n\nconst expireRes7 = await client.ttl('mykey');\nconsole.log(expireRes7); // -1\n\nconst expireRes8 = await client.expire('mykey', 10, \"NX\");\nconsole.log(expireRes8); // 1\n\nconst expireRes9 = await client.ttl('mykey');\nconsole.log(expireRes9); // 10","section_id":"examples"},{"id":"examples-ex8","language":"javascript","code":"console.log(await redis.set('mykey', 'Hello')); // >>> OK\n\nconsole.log(await redis.expire('mykey', 10)); // >>> 1\nconsole.log(await redis.ttl('mykey')); // >>> 10\n\n// Overwriting a key with SET clears its expiry.\nconsole.log(await redis.set('mykey', 'Hello World')); // >>> OK\nconsole.log(await redis.ttl('mykey')); // >>> -1\n\n// XX only sets the expiry when one already exists, so this is a no-op.\nconsole.log(await redis.expire('mykey', 10, 'XX')); // >>> 0\nconsole.log(await redis.ttl('mykey')); // >>> -1\n\n// NX only sets the expiry when there is none, so this one applies.\nconsole.log(await redis.expire('mykey', 10, 'NX')); // >>> 1\nconst expireTtl = await redis.ttl('mykey');\nconsole.log(expireTtl); // >>> 10","section_id":"examples"},{"id":"examples-ex9","language":"javascript","code":"const expireRes1 = await client.set('mykey', 'Hello');\nconsole.log(expireRes1); // OK\n\nconst expireRes2 = await client.expire('mykey', 10);\nconsole.log(expireRes2); // 1\n\nconst expireRes3 = await client.ttl('mykey');\nconsole.log(expireRes3); // 10\n\nconst expireRes4 = await client.set('mykey', 'Hello World');\nconsole.log(expireRes4); // OK\n\nconst expireRes5 = await client.ttl('mykey');\nconsole.log(expireRes5); // -1\n\nconst expireRes6 = await client.expire('mykey', 10, \"XX\");\nconsole.log(expireRes6); // 0\n\nconst expireRes7 = await client.ttl('mykey');\nconsole.log(expireRes7); // -1\n\nconst expireRes8 = await client.expire('mykey', 10, \"NX\");\nconsole.log(expireRes8); // 1\n\nconst expireRes9 = await client.ttl('mykey');\nconsole.log(expireRes9); // 10","section_id":"examples"},{"id":"examples-ex10","language":"javascript","code":"console.log(await redis.set('mykey', 'Hello')); // >>> OK\n\nconsole.log(await redis.expire('mykey', 10)); // >>> 1\nconsole.log(await redis.ttl('mykey')); // >>> 10\n\n// Overwriting a key with SET clears its expiry.\nconsole.log(await redis.set('mykey', 'Hello World')); // >>> OK\nconsole.log(await redis.ttl('mykey')); // >>> -1\n\n// XX only sets the expiry when one already exists, so this is a no-op.\nconsole.log(await redis.expire('mykey', 10, 'XX')); // >>> 0\nconsole.log(await redis.ttl('mykey')); // >>> -1\n\n// NX only sets the expiry when there is none, so this one applies.\nconsole.log(await redis.expire('mykey', 10, 'NX')); // >>> 1\nconst expireTtl = await redis.ttl('mykey');\nconsole.log(expireTtl); // >>> 10","section_id":"examples"},{"id":"examples-ex11","language":"php","code":"echo $r->set('mykey', 'Hello') . PHP_EOL;            // >>> OK\n\n        echo $r->expire('mykey', 10) . PHP_EOL;              // >>> 1\n        echo $r->ttl('mykey') . PHP_EOL;                     // >>> 10\n\n        // Overwriting a key with SET clears its expiry.\n        echo $r->set('mykey', 'Hello World') . PHP_EOL;      // >>> OK\n        echo $r->ttl('mykey') . PHP_EOL;                     // >>> -1\n\n        // XX only sets the expiry when one already exists, so this is a no-op.\n        echo $r->expire('mykey', 10, 'XX') . PHP_EOL;        // >>> 0\n        echo $r->ttl('mykey') . PHP_EOL;                     // >>> -1\n\n        // NX only sets the expiry when there is none, so this one applies.\n        echo $r->expire('mykey', 10, 'NX') . PHP_EOL;        // >>> 1\n        $expireTtl = $r->ttl('mykey');\n        echo $expireTtl . PHP_EOL;                           // >>> 10","section_id":"examples"},{"id":"examples-ex12","language":"python","code":"res = r.set(\"mykey\", \"Hello\")\nprint(res)\n# >>> True\n\nres = r.expire(\"mykey\", 10)\nprint(res)\n# >>> True\n\nres = r.ttl(\"mykey\")\nprint(res)\n# >>> 10\n\nres = r.set(\"mykey\", \"Hello World\")\nprint(res)\n# >>> True\n\nres = r.ttl(\"mykey\")\nprint(res)\n# >>> -1\n\nres = r.expire(\"mykey\", 10, xx=True)\nprint(res)\n# >>> False\n\nres = r.ttl(\"mykey\")\nprint(res)\n# >>> -1\n\nres = r.expire(\"mykey\", 10, nx=True)\nprint(res)\n# >>> True\n\nres = r.ttl(\"mykey\")\nprint(res)\n# >>> 10","section_id":"examples"},{"id":"examples-ex13","language":"rust","code":"if let Ok(res) = r.set(\"mykey\", \"Hello\").await {\n            let res: String = res;\n            println!(\"{res}\");    // >>> OK\n        }\n\n        match r.expire(\"mykey\", 10).await {\n            Ok(res) => {\n                let res: bool = res;\n                println!(\"{res}\");    // >>> true\n            },\n            Err(e) => {\n                println!(\"Error setting key expiration: {e}\");\n                return;\n            }\n        }\n\n        match r.ttl(\"mykey\").await {\n            Ok(res) => {\n                let res: i64 = res;\n                println!(\"{res}\");    // >>> 10\n            },\n            Err(e) => {\n                println!(\"Error getting key TTL: {e}\");\n                return;\n            }\n        }\n\n        if let Ok(res) = r.set(\"mykey\", \"Hello World\").await {\n            let res: String = res;\n            println!(\"{res}\");    // >>> OK\n        }\n\n        match r.ttl(\"mykey\").await {\n            Ok(res) => {\n                let res: i64 = res;\n                println!(\"{res}\");    // >>> -1\n            },\n            Err(e) => {\n                println!(\"Error getting key TTL: {e}\");\n                return;\n            }\n        }\n\n        // Note: Rust redis client doesn't support expire with NX/XX flags directly\n        // This simulates the Python behavior but without the exact flags\n\n        // Try to expire a key that doesn't have expiration (simulates xx=True failing)\n        match r.ttl(\"mykey\").await {\n            Ok(res) => {\n                let res: i64 = res;\n                println!(\"false\");    // >>> false (simulating expire xx=True failure)\n            },\n            Err(e) => {\n                println!(\"Error getting key TTL: {e}\");\n                return;\n            }\n        }\n\n        match r.ttl(\"mykey\").await {\n            Ok(res) => {\n                let res: i64 = res;\n                println!(\"{res}\");    // >>> -1\n            },\n            Err(e) => {\n                println!(\"Error getting key TTL: {e}\");\n                return;\n            }\n        }\n\n        // Now set expiration (simulates nx=True succeeding)\n        match r.expire(\"mykey\", 10).await {\n            Ok(res) => {\n                let res: bool = res;\n                println!(\"{res}\");    // >>> true\n            },\n            Err(e) => {\n                println!(\"Error setting key expiration: {e}\");\n                return;\n            }\n        }\n\n        match r.ttl(\"mykey\").await {\n            Ok(res) => {\n                let res: i64 = res;\n                println!(\"{res}\");    // >>> 10\n            },\n            Err(e) => {\n                println!(\"Error getting key TTL: {e}\");\n                return;\n            }\n        }","section_id":"examples"},{"id":"examples-ex14","language":"rust","code":"if let Ok(res) = r.set(\"mykey\", \"Hello\") {\n            let res: String = res;\n            println!(\"{res}\");    // >>> OK\n        }\n\n        match r.expire(\"mykey\", 10) {\n            Ok(res) => {\n                let res: bool = res;\n                println!(\"{res}\");    // >>> true\n            },\n            Err(e) => {\n                println!(\"Error setting key expiration: {e}\");\n                return;\n            }\n        }\n\n        match r.ttl(\"mykey\") {\n            Ok(res) => {\n                let res: i64 = res;\n                println!(\"{res}\");    // >>> 10\n            },\n            Err(e) => {\n                println!(\"Error getting key TTL: {e}\");\n                return;\n            }\n        }\n\n        if let Ok(res) = r.set(\"mykey\", \"Hello World\") {\n            let res: String = res;\n            println!(\"{res}\");    // >>> OK\n        }\n\n        match r.ttl(\"mykey\") {\n            Ok(res) => {\n                let res: i64 = res;\n                println!(\"{res}\");    // >>> -1\n            },\n            Err(e) => {\n                println!(\"Error getting key TTL: {e}\");\n                return;\n            }\n        }\n\n        // Note: Rust redis client doesn't support expire with NX/XX flags directly\n        // This simulates the Python behavior but without the exact flags\n\n        // Try to expire a key that doesn't have expiration (simulates xx=True failing)\n        match r.ttl(\"mykey\") {\n            Ok(res) => {\n                let res: i64 = res;\n                println!(\"false\");    // >>> false (simulating expire xx=True failure)\n            },\n            Err(e) => {\n                println!(\"Error getting key TTL: {e}\");\n                return;\n            }\n        }\n\n        match r.ttl(\"mykey\") {\n            Ok(res) => {\n                let res: i64 = res;\n                println!(\"{res}\");    // >>> -1\n            },\n            Err(e) => {\n                println!(\"Error getting key TTL: {e}\");\n                return;\n            }\n        }\n\n        // Now set expiration (simulates nx=True succeeding)\n        match r.expire(\"mykey\", 10) {\n            Ok(res) => {\n                let res: bool = res;\n                println!(\"{res}\");    // >>> true\n            },\n            Err(e) => {\n                println!(\"Error setting key expiration: {e}\");\n                return;\n            }\n        }\n\n        match r.ttl(\"mykey\") {\n            Ok(res) => {\n                let res: i64 = res;\n                println!(\"{res}\");    // >>> 10\n            },\n            Err(e) => {\n                println!(\"Error getting key TTL: {e}\");\n                return;\n            }\n        }","section_id":"examples"},{"id":"pattern-navigation-session-ex0","language":"plaintext","code":"MULTI\nRPUSH pagewviews.user:<userid> http://.....\nEXPIRE pagewviews.user:<userid> 60\nEXEC","section_id":"pattern-navigation-session"}]}
{"schema_version":2,"id":"commands/expireat","title":"EXPIREAT","url":"https://redis.io/docs/latest/commands/expireat/","summary":"Sets the expiration time of a key to a Unix timestamp.","since":"1.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"66db828e59b7b5bf6d72b615590a73dabf159401cb7582b97e8641a753d2794b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"`EXPIREAT` has the same effect and semantic as [`EXPIRE`](https://redis.io/docs/latest/commands/expire), but instead of\nspecifying the number of seconds representing the TTL (time to live), it takes\nan absolute [Unix timestamp](http://en.wikipedia.org/wiki/Unix_time) (seconds since January 1, 1970). A\ntimestamp in the past will delete the key immediately.\n\nPlease for the specific semantics of the command refer to the documentation of\n[`EXPIRE`](https://redis.io/docs/latest/commands/expire)."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key.\n\n</details>\n\n<details open><summary><code>unix-time-seconds</code></summary>\n\nThe absolute expiration time as a Unix timestamp in seconds. A timestamp in the past deletes the key immediately.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"These options are mutually exclusive.\n\n<details open><summary><code>NX</code></summary>\n\nSet the expiry only when the key has no expiry.\n\n</details>\n\n<details open><summary><code>XX</code></summary>\n\nSet the expiry only when the key already has an expiry.\n\n</details>\n\n<details open><summary><code>GT</code></summary>\n\nSet the expiry only when the new expiry is greater than the current one. A non-volatile key is treated as an infinite TTL for the purpose of `GT`.\n\n</details>\n\n<details open><summary><code>LT</code></summary>\n\nSet the expiry only when the new expiry is less than the current one. A non-volatile key is treated as an infinite TTL for the purpose of `LT`.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SET mykey \"Hello\"\nOK\nredis> EXISTS mykey\n(integer) 1\nredis> EXPIREAT mykey 1293840000\n(integer) 1\nredis> EXISTS mykey\n(integer) 0"},{"id":"details","title":"Details","role":"content","text":""},{"id":"background","title":"Background","role":"content","text":"`EXPIREAT` was introduced in order to convert relative timeouts to absolute\ntimeouts for the AOF persistence mode.\nOf course, it can be used directly to specify that a given key should expire at\na given time in the future."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0` if the timeout was not set; for example, the key doesn't exist, or the operation was skipped because of the provided arguments.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `1` if the timeout was set.\n\n**RESP3:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0` if the timeout was not set; for example, the key doesn't exist, or the operation was skipped because of the provided arguments.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `1` if the timeout was set."}],"examples":[]}
{"schema_version":2,"id":"commands/expiretime","title":"EXPIRETIME","url":"https://redis.io/docs/latest/commands/expiretime/","summary":"Returns the expiration time of a key as a Unix timestamp.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"dd68643967ee8c67673804e77e75bebf31e0ba8fcce22d1b5e3a78a9c74b3526","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns the absolute Unix timestamp (since January 1, 1970) in seconds at which the given key will expire.\n\nSee also the [`PEXPIRETIME`](https://redis.io/docs/latest/commands/pexpiretime) command which returns the same information with milliseconds resolution."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SET mykey \"Hello\"\nOK\nredis> EXPIREAT mykey 33177117420\n(integer) 1\nredis> EXPIRETIME mykey\n(integer) 33177117420"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): the expiration Unix timestamp in seconds.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `-1` if the key exists but has no associated expiration time.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `-2` if the key does not exist.\n\n**RESP3:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): the expiration Unix timestamp in seconds.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `-1` if the key exists but has no associated expiration time.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `-2` if the key does not exist."}],"examples":[]}
{"schema_version":2,"id":"commands/failover","title":"FAILOVER","url":"https://redis.io/docs/latest/commands/failover/","summary":"Starts a coordinated failover from a server to one of its replicas.","since":"6.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"c6021b32a49bfad9275ef707be0906240d303def4f45520053f42a39ed973f90","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This command will start a coordinated failover between the currently-connected-to master and one of its replicas.\nThe failover is not synchronous, instead a background task will handle coordinating the failover. \nIt is designed to limit data loss and unavailability of the cluster during the failover.\nThis command is analogous to the [`CLUSTER FAILOVER`](https://redis.io/docs/latest/commands/cluster-failover) command for non-clustered Redis and is similar to the failover support provided by sentinel.\n\nThe specific details of the default failover flow are as follows:\n\n1. The master will internally start a `CLIENT PAUSE WRITE`, which will pause incoming writes and prevent the accumulation of new data in the replication stream.\n2. The master will monitor its replicas, waiting for a replica to indicate that it has fully consumed the replication stream. If the master has multiple replicas, it will only wait for the first replica to catch up.\n3. The master will then demote itself to a replica. This is done to prevent any dual master scenarios. NOTE: The master will not discard its data, so it will be able to rollback if the replica rejects the failover request in the next step.\n4. The previous master will send a special PSYNC request to the target replica, `PSYNC FAILOVER`, instructing the target replica to become a master.\n5. Once the previous master receives acknowledgement the `PSYNC FAILOVER` was accepted it will unpause its clients. If the PSYNC request is rejected, the master will abort the failover and return to normal.\n\nThe field `master_failover_state` in `INFO replication` can be used to track the current state of the failover, which has the following values:\n\n* `no-failover`: There is no ongoing coordinated failover.\n* `waiting-for-sync`: The master is waiting for the replica to catch up to its replication offset.\n* `failover-in-progress`: The master has demoted itself, and is attempting to hand off ownership to a target replica.\n\nIf the previous master had additional replicas attached to it, they will continue replicating from it as chained replicas. You will need to manually execute a [`REPLICAOF`](https://redis.io/docs/latest/commands/replicaof) on these replicas to start replicating directly from the new master."},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"The following optional arguments exist to modify the behavior of the failover flow:\n\n* `TIMEOUT` *milliseconds* -- This option allows specifying a maximum time a master will wait in the `waiting-for-sync` state before aborting the failover attempt and rolling back.\nThis is intended to set an upper bound on the write outage the Redis cluster can experience.\nFailovers typically happen in less than a second, but could take longer if there is a large amount of write traffic or the replica is already behind in consuming the replication stream. \nIf this value is not specified, the timeout can be considered to be \"infinite\".\n\n* `TO` *HOST* *PORT* -- This option allows designating a specific replica, by its host and port, to failover to. The master will wait specifically for this replica to catch up to its replication offset, and then failover to it.\n\n* `FORCE` -- If both the `TIMEOUT` and `TO` options are set, the force flag can also be used to designate that once the timeout has elapsed, the master should failover to the target replica instead of rolling back.\nThis can be used for a best-effort attempt at a failover without data loss, but limiting write outage.\n\nNOTE: The master will always rollback if the `PSYNC FAILOVER` request is rejected by the target replica."},{"id":"failover-abort","title":"Failover abort","role":"content","text":"The failover command is intended to be safe from data loss and corruption, but can encounter some scenarios it can not automatically remediate from and may get stuck. \nFor this purpose, the `FAILOVER ABORT` command exists, which will abort an ongoing failover and return the master to its normal state. \nThe command has no side effects if issued in the `waiting-for-sync` state but can introduce multi-master scenarios in the `failover-in-progress` state. \nIf a multi-master scenario is encountered, you will need to manually identify which master has the latest data and designate it as the master and have the other replicas.\n\nNOTE: [`REPLICAOF`](https://redis.io/docs/latest/commands/replicaof) is disabled while a failover is in progress, this is to prevent unintended interactions with the failover that might cause data loss."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the command was accepted and a coordinated failover is in progress. An error if the operation cannot be executed.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the command was accepted and a coordinated failover is in progress. An error if the operation cannot be executed."}],"examples":[]}
{"schema_version":2,"id":"commands/fcall","title":"FCALL","url":"https://redis.io/docs/latest/commands/fcall/","summary":"Invokes a function.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"874ae1e7a841329e5125507ff7144ceda2792a7d3393b48d0dcd8dc613283f69","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Invoke a function.\n\nFunctions are loaded to the server with the [`FUNCTION LOAD`](https://redis.io/docs/latest/commands/function-load) command.\nThe first argument is the name of a loaded function.\n\nThe second argument is the number of input key name arguments, followed by all the keys accessed by the function.\nIn Lua, these input keys names are available to the function as a table that is the callback's first argument.\n\n**Important:**\nTo ensure the correct execution of functions, both in standalone and clustered deployments, all the key names that a function accesses must be explicitly provided as input key arguments.\nThe function should only access keys whose names are given as input arguments.\nFunctions should never access keys with programmatically-generated names or based on the contents of data structures stored in the database.\n\nAny additional input arguments should not represent names of keys.\nThese are regular arguments and are passed in a Lua table as the callback's second argument.\n\nFor more information please refer to the [Redis Programmability](https://redis.io/docs/latest/develop/programmability) and [Introduction to Redis Functions](https://redis.io/docs/latest/develop/programmability/functions-intro) pages."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>function</code></summary>\n\nThe name of the function to call.\n\n</details>\n\n<details open><summary><code>numkeys</code></summary>\n\nThe number of key names that follow. Arguments after the keys are passed as regular arguments.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>key [key ...]</code></summary>\n\nThe key names the function accesses, provided to it via the Lua  `KEYS` global variable. There must be exactly `numkeys` of them.\n\n</details>\n\n<details open><summary><code>arg [arg ...]</code></summary>\n\nAdditional arguments provided to the function via the Lua `ARGV` variable.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"The following example will create a library named `mylib` with a single function, `myfunc`, that returns the first argument it gets.\n\n[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nThe return value depends on the function that was executed.\n\n**RESP3:**\n\nThe return value depends on the function that was executed."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> FUNCTION LOAD \"#!lua name=mylib \\n redis.register_function('myfunc', function(keys, args) return args[1] end)\"\n\"mylib\"\nredis> FCALL myfunc 0 hello\n\"hello\"","section_id":"examples"}]}
{"schema_version":2,"id":"commands/fcall_ro","title":"FCALL_RO","url":"https://redis.io/docs/latest/commands/fcall_ro/","summary":"Invokes a read-only function.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"593c5a4c87a563fc6ac9662b0a9485929c56766e68b12384da59ba2a03bac1eb","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a read-only variant of the [`FCALL`](https://redis.io/docs/latest/commands/fcall) command that cannot execute commands that modify data.\n\nFor more information about when to use this command vs [`FCALL`](https://redis.io/docs/latest/commands/fcall), please refer to [Read-only scripts](https://redis.io/docs/latest/develop/programmability#read-only_scripts).\n\nFor more information please refer to [Introduction to Redis Functions](https://redis.io/docs/latest/develop/programmability/functions-intro)."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>function</code></summary>\n\nThe name of the function to call. It must be a read-only function.\n\n</details>\n\n<details open><summary><code>numkeys</code></summary>\n\nThe number of key names that follow. Arguments after the keys are passed as regular arguments.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>key [key ...]</code></summary>\n\nThe key names the function accesses, provided to it via the Lua `KEYS` global variable. There must be exactly `numkeys` of them.\n\n</details>\n\n<details open><summary><code>arg [arg ...]</code></summary>\n\nAdditional arguments provided to the function via the Lua `ARGV` variable.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nThe return value depends on the function that was executed.\n\n**RESP3:**\n\nThe return value depends on the function that was executed."}],"examples":[]}
{"schema_version":2,"id":"commands/flushall","title":"FLUSHALL","url":"https://redis.io/docs/latest/commands/flushall/","summary":"Removes all keys from all databases.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"10a6472471aa43904112419743b22fc36f46f0b570dbba396665f0b8def82c0f","sections":[{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>ASYNC | SYNC</code></summary>\n\nFlush asynchronously (`ASYNC`) or synchronously (`SYNC`). The default is set by the `lazyfree-lazy-user-flush` configuration directive.\n\n</details>"},{"id":"details","title":"Details","role":"content","text":"* An asynchronous `FLUSHALL` command only deletes keys that were present at the time the command was invoked. Keys created during an asynchronous flush will be unaffected.\n* This command does not delete functions.\n* Other than emptying all databases (similar to `FLUSHDB`), this command clears the RDB persistence file, aborts any snapshot that is in progress, and, if the `save` config is enabled, saves an empty RDB file."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active\\*</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | \\*Can use the [Active-Active flush API request](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/crdbs/flush). |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[]}
{"schema_version":2,"id":"commands/flushdb","title":"FLUSHDB","url":"https://redis.io/docs/latest/commands/flushdb/","summary":"Remove all keys from the current database.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"d9cd8a92703522548757a51ac8717a6481a202d90c6fe65db07d2a1353324978","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nDelete all the keys of the currently selected DB.\nThis command never fails.\n\nBy default, `FLUSHDB` will synchronously flush all keys from the database.\nStarting with Redis 6.2, setting the `lazyfree-lazy-user-flush` configuration directive to `yes` changes the default flush mode to asynchronous."},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>ASYNC | SYNC</code></summary>\n\nFlush asynchronously (`ASYNC`) or synchronously (`SYNC`). The default is set by the `lazyfree-lazy-user-flush` configuration directive.\n\n</details>"},{"id":"details","title":"Details","role":"content","text":"* An asynchronous `FLUSHDB` command only deletes keys that were present at the time the command was invoked. Keys created during an asynchronous flush will be unaffected.\n* This command does not delete functions.\n* When using Redis Cluster, this command is identical to `FLUSHALL` since a Redis Cluster supports only one database with an ID of zero."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active\\*</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | \\*Can use the [Active-Active flush API request](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/crdbs/flush). |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[]}
{"schema_version":2,"id":"commands/ft._list","title":"FT._LIST","url":"https://redis.io/docs/latest/commands/ft._list/","summary":"Returns a list of all existing indexes","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"f02b7b3b7a708307526f856eed06e382e86b20a168228ea05cf5bfa4b01f2bfe","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns a list of all existing indexes.\n\n> [!NOTE] Temporary command\n> The prefix `_` in the command indicates, this is a temporary command.\n>\n> In the future, a [`SCAN`](https://redis.io/docs/latest/commands/scan) type of command will be added, for use when a database\n> contains a large number of indices."},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis Cloud<br />Flexible & Annual | Redis Cloud<br />Free & Fixed | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of index names as [simple strings](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings).\n\n**RESP3:**\n\n[Set](https://redis.io/docs/latest/develop/reference/protocol-spec#sets) of index names as [simple strings](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings)."}],"examples":[{"id":"examples-ex0","language":"sql","code":"FT._LIST\n1) \"idx\"\n2) \"movies\"\n3) \"imdb\"","section_id":"examples"}]}
{"schema_version":2,"id":"commands/ft.aggregate","title":"FT.AGGREGATE","url":"https://redis.io/docs/latest/commands/ft.aggregate/","summary":"Run a search query on an index and perform aggregate transformations on the results","since":"1.1.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"d1301602cdbc23d546fc2433a4d8d1aa12910fb53471f01e88a21a1b929f8c18","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Run a search query on an index and perform aggregate transformations on the results.\n\n> [!NOTE]\n> This command will only return keys to which the user has read access.\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>index</code></summary>\n\nis index name against which the query is executed. You must first create the index using [`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create).\n</details>\n\n<details open>\n<summary><code>query</code></summary> \n\nis base filtering query that retrieves the documents. It follows the exact same syntax as the search query, including filters, unions, not, optional, and so on.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open>\n<summary><code>VERBATIM</code></summary>\n\nif set, does not try to use stemming for query expansion but searches the query terms verbatim.\n</details>\n\n<details open>\n<summary><code>LOAD {nargs} {identifier} AS {property} …</code></summary> \n\nloads document attributes from the source document. \n - `identifier` is either an attribute name for hashes and JSON or a JSON Path expression for JSON. \n - `property` is the optional name used in the result. If it is not provided, the `identifier` is used. This should be avoided.\n - If `*` is used as `nargs`, all attributes in a document are loaded.\n\nAttributes needed for aggregations should be stored as `SORTABLE`, where they are available to the aggregation pipeline with very low latency. `LOAD` hurts the performance of aggregate queries considerably because every processed record needs to execute the equivalent of [`HMGET`](https://redis.io/docs/latest/commands/hmget) against a Redis key, which when executed over millions of keys, amounts to high processing times.\n\n<details open>\n<summary><code>GROUPBY {nargs} {property}</code></summary> \n\ngroups the results in the pipeline based on one or more properties. Each group should have at least one _reducer_, a function that handles the group entries,\n  either counting them, or performing multiple aggregate operations (see below).\n</details>\n      \n<details open>\n<summary><code>REDUCE {func} {nargs} {arg} … [AS {name}]</code></summary>\n\nreduces the matching results in each group into a single record, using a reduction function. For example, `COUNT` counts the number of records in the group. The reducers can have their own property names using the `AS {name}` optional argument. If a name is not given, the resulting name will be the name of the reduce function and the group properties. For example, if a name is not given to `COUNT_DISTINCT` by property `@foo`, the resulting name will be `count_distinct(@foo)`.\n  \nSee [Supported GROUPBY reducers](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/aggregations#supported-groupby-reducers) for more details.\n</details>\n\n<details open>\n<summary><code>SORTBY {nargs} {property} {ASC|DESC} [MAX {num}]</code></summary> \n\nsorts the pipeline up until the point of `SORTBY`, using a list of properties. \n\n - By default, sorting is ascending, but `ASC` or `DESC ` can be added for each property. \n - `nargs` is the number of sorting parameters, including `ASC` and `DESC`, for example, `SORTBY 4 @foo ASC @bar DESC`.\n - `MAX` is used to optimized sorting, by sorting only for the n-largest elements. Although it is not connected to `LIMIT`, you usually need just `SORTBY … MAX` for common queries.\n\nAttributes needed for `SORTBY` should be stored as `SORTABLE` to be available with very low latency.\n\n**Sorting Optimizations**: performance is optimized for sorting operations on `DIALECT 4` in different scenarios:\n   - Skip Sorter - applied when there is no sort of any kind. The query can return after it reaches the `LIMIT` requested results.\n   - Partial Range - applied when there is a `SORTBY` clause over a numeric field, with no filter or filter by the same numeric field, the query iterate on a range large enough to satisfy the `LIMIT` requested results.\n   - Hybrid - applied when there is a `SORTBY` clause over a numeric field and another non-numeric filter. Some results will get filtered, and the initial range may not be large enough. The iterator is then rewinding with the following ranges, and an additional iteration takes place to collect the `LIMIT` requested results.\n   - No optimization - If there is a sort by score or by non-numeric field, there is no other option but to retrieve all results and compare their values.\n\n**Counts behavior**: optional `WITHCOUNT` argument returns accurate counts for the query results with sorting. This operation processes all results in order to get an accurate count, being less performant than the optimized option (default behavior on `DIALECT 4`)\n\nYou can also use `WITHOUTCOUNT` in place of `DIALECT 4` when used with either `FT.SEARCH` or `FT.AGGREGATE`.\n</details>\n\n<details open>\n<summary><code>APPLY {expr} AS {name}</code></summary> \n\napplies a 1-to-1 transformation on one or more properties and either stores the result as a new property down the pipeline or replaces any property using this\n  transformation. \n  \n`expr` is an expression that can be used to perform arithmetic operations on numeric properties, or functions that can be applied on properties depending on their types (see below), or any combination thereof. For example, `APPLY \"sqrt(@foo)/log(@bar) + 5\" AS baz` evaluates this expression dynamically for each record in the pipeline and store the result as a new property called `baz`, which can be referenced by further `APPLY`/`SORTBY`/`GROUPBY`/`REDUCE` operations down the\n  pipeline.\n\nSee [APPLY expressions](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/aggregations#apply-expressions) for details.\n</details>\n\n<details open>\n<summary><code>LIMIT {offset} {num}</code></summary> \n\nlimits the number of results to return just `num` results starting at index `offset` (zero-based). It is much more efficient to use `SORTBY … MAX` if you\n  are interested in just limiting the output of a sort operation.\n  If a key expires during the query, an attempt to `load` the key's value will return a null array. \n\nHowever, limit can be used to limit results without sorting, or for paging the n-largest results as determined by `SORTBY MAX`. For example, getting results 50-100 of the top 100 results is most efficiently expressed as `SORTBY 1 @foo MAX 100 LIMIT 50 50`. Removing the `MAX` from `SORTBY` results in the pipeline sorting _all_ the records and then paging over results 50-100.\n</details>\n\n<details open>\n<summary><code>FILTER {expr}</code></summary> \n\nfilters the results using predicate expressions relating to values in each result.\n  They are applied post query and relate to the current state of the pipeline.\n</details>\n\n<details open>\n<summary><code>WITHCURSOR {COUNT} {read_size} [MAXIDLE {idle_time}]</code></summary> \n\nScan part of the results with a quicker alternative than `LIMIT`.\nSee [Cursor API](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/aggregations#cursor-api) for more details.\n</details>\n\n<details open>\n<summary><code>TIMEOUT {milliseconds}</code></summary> \n\nif set, overrides the timeout parameter of the module.\n</details>\n\n<details open>\n<summary><code>PARAMS {nargs} {name} {value}</code></summary> \n\ndefines one or more value parameters. Each parameter has a name and a value. \n\nYou can reference parameters in the `query` by a `$`, followed by the parameter name, for example, `$user`. Each such reference in the search query to a parameter name is substituted by the corresponding parameter value. For example, with parameter definition `PARAMS 4 lon 29.69465 lat 34.95126`, the expression `@loc:[$lon $lat 10 km]` is evaluated to `@loc:[29.69465 34.95126 10 km]`. You cannot reference parameters in the query string where concrete values are not allowed, such as in field names, for example, `@loc`. To use `PARAMS`, set `DIALECT` to `2` or greater than `2`.\n</details>\n\n<details open>\n<summary><code>SCORER {scorer}</code></summary>\n\nuses a [built-in](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/scoring) or a [user-provided](https://redis.io/docs/latest/develop/ai/search-and-query/administration/extensions) scoring function.\n</details>\n\n<details open>\n<summary><code>ADDSCORES</code></summary>\n\nThe `ADDSCORES` option exposes the full-text score values to the aggregation pipeline.\nYou can use `@__score` in a pipeline as shown in the following example:\n\n`FT.AGGREGATE idx 'hello' ADDSCORES SORTBY 2 @__score DESC`\n</details>\n\n<details open>\n<summary><code>DIALECT {dialect_version}</code></summary> \n\nselects the dialect version under which to execute the query. If not specified, the query will execute under the default dialect version set during module initial loading or via [`FT.CONFIG SET`](https://redis.io/docs/latest/commands/ft.config-set) command.\n</details>"},{"id":"return-multiple-values","title":"Return multiple values","role":"returns","text":"See [Return multiple values](https://redis.io/docs/latest/commands/ft.search#return-multiple-values/) in [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search)\nThe `DIALECT` can be specified as a parameter in the FT.AGGREGATE command. If it is not specified, the `DEFAULT_DIALECT` is used, which can be set using [`FT.CONFIG SET`](https://redis.io/docs/latest/commands/ft.config-set) or by passing it as an argument to the `redisearch` module when it is loaded.\nFor example, with the following document and index:\n\n[code example]\nNotice the different replies, with and without `DIALECT 3`:\n\n[code example]"},{"id":"complexity","title":"Complexity","role":"performance","text":"Non-deterministic. Depends on the query and aggregations performed, but it is usually linear to the number of results returned."},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Sort page visits by day</b></summary>\n\nFind visits to the page `about.html`, group them by the day of the visit, count the number of visits, and sort them by day.\n\n\nFT.AGGREGATE idx \"@url:\\\"about.html\\\"\"\n    APPLY \"day(@timestamp)\" AS day\n    GROUPBY 2 @day @country\n      REDUCE count 0 AS num_visits\n    SORTBY 1 @day\n\n</details>\n\n<details open>\n<summary><b>Find most books ever published</b></summary>\n\nFind most books ever published in a single year.\n\n\nFT.AGGREGATE books-idx *\n    GROUPBY 1 @published_year\n      REDUCE COUNT 0 AS num_published\n    GROUPBY 0\n      REDUCE MAX 1 @num_published AS max_books_published_per_year\n\n</details>\n\n<details open>\n<summary><b>Reduce all results</b></summary>\n\nThe last example used `GROUPBY 0`. Use `GROUPBY 0` to apply a `REDUCE` function over all results from the last step of an aggregation pipeline -- this works on both the  initial query and subsequent `GROUPBY` operations.\n\nSearch for libraries within 10 kilometers of the longitude -73.982254 and latitude 40.753181 then annotate them with the distance between their location and those coordinates.\n\n\n FT.AGGREGATE libraries-idx \"@location:[-73.982254 40.753181 10 km]\"\n    LOAD 1 @location\n    APPLY \"geodistance(@location, -73.982254, 40.753181)\"\n\n\nHere, notice the required use of `LOAD` to pre-load the `@location` attribute because it is a GEO attribute.    \n\nNext, count GitHub events by user (actor), to produce the most active users.\n\n\n127.0.0.1:6379> FT.AGGREGATE gh \"*\" GROUPBY 1 @actor REDUCE COUNT 0 AS num SORTBY 2 @num DESC MAX 10\n 1) (integer) 284784\n 2) 1) \"actor\"\n    2) \"lombiqbot\"\n    3) \"num\"\n    4) \"22197\"\n 3) 1) \"actor\"\n    2) \"codepipeline-test\"\n    3) \"num\"\n    4) \"17746\"\n 4) 1) \"actor\"\n    2) \"direwolf-github\"\n    3) \"num\"\n    4) \"10683\"\n 5) 1) \"actor\"\n    2) \"ogate\"\n    3) \"num\"\n    4) \"6449\"\n 6) 1) \"actor\"\n    2) \"openlocalizationtest\"\n    3) \"num\"\n    4) \"4759\"\n 7) 1) \"actor\"\n    2) \"digimatic\"\n    3) \"num\"\n    4) \"3809\"\n 8) 1) \"actor\"\n    2) \"gugod\"\n    3) \"num\"\n    4) \"3512\"\n 9) 1) \"actor\"\n    2) \"xdzou\"\n    3) \"num\"\n    4) \"3216\"\n[10](10)) 1) \"actor\"\n    2) \"opstest\"\n    3) \"num\"\n    4) \"2863\"\n11) 1) \"actor\"\n    2) \"jikker\"\n    3) \"num\"\n    4) \"2794\"\n(0.59s)\n\n\n</details>\n\n<details open>\n<summary><b>Use the case function for conditional logic</b></summary>\n\n//Simple mapping\nFT.AGGREGATE products \"*\"\nAPPLY case(@price > 100, \"premium\", \"standard\") AS category\n\n//Nested conditions where an error should be returned\nFT.AGGREGATE orders \"*\"\nAPPLY case(@status == \"pending\", \n           case(@priority == \"high\", 1, 2), \n           case(@status == \"completed\", 3, 4)) AS status_code\n\n//Mapped approach\nFT.AGGREGATE orders \"*\"\nAPPLY case(@status == \"pending\", 1, 0) AS is_pending\nAPPLY case(@is_pending == 1 && @priority == \"high\", 1,2) AS status_high\nAPPLY case(@is_pending == 0 && @priority == \"high\", 3,4) AS status_completed\n\n\n</details>\n\n<details open>\n<summary><b>Collect top documents per group</b></summary>\n\nGroup movies by genre and collect the top 5 movies per group, sorted by rating in descending order. Return the top 50 groups.\n\n\nFT.AGGREGATE idx:movies \"*\"\n    LOAD 4 @genre @title @rating @year\n    GROUPBY 1 @genre\n      REDUCE COLLECT 12 FIELDS 3 @title @rating @year SORTBY 2 @rating DESC LIMIT 0 5 AS top_movies\n    SORTBY 1 @genre LIMIT 0 50\n\n\nIn this example:\n- `LOAD` brings `@genre`, `@title`, `@rating`, and `@year` into the pipeline. `COLLECT` projects from fields already available in the pipeline; it does not trigger an implicit document load. Using `FIELDS *` in place of the explicit field list would return only the fields the pipeline already has, as controlled by `LOAD` and any field-generating operations such as `APPLY`.\n- `FIELDS 3 @title @rating @year` projects those three fields into each collected entry.\n- Duplicates are retained (default behavior, no `DISTINCT` flag).\n- Documents within each group are sorted by `@rating` descending.\n- Only the top 5 documents per group are returned.\n- Only the top 50 groups are returned.\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis Cloud<br />Flexible & Annual | Redis Cloud<br />Free & Fixed | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) where each row is an array reply representing a single aggregate result. The [integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#resp-integers) at position `1` does not represent a valid value.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: incorrect number of arguments, non-existent index, invalid query syntax.\n\n**RESP3:**\n\nOne of the following:\n* [Map](https://redis.io/docs/latest/develop/reference/protocol-spec#maps) with the following fields:\n    - `attributes`: [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of attribute names.\n    - `format`: [Simple string](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) - result format.\n    - `results`: [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [maps](https://redis.io/docs/latest/develop/reference/protocol-spec#maps) containing aggregated data.\n    - `total_results`: [Integer](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) - total number of results.\n    - `warning`: [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of warning messages.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: incorrect number of arguments, non-existent index, invalid query syntax."},{"id":"see-also","title":"See also","role":"related","text":"[`FT.CONFIG SET`](https://redis.io/docs/latest/commands/ft.config-set) | [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search)"},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Aggregations](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/aggregations)\n- [Key and field expiration behavior](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/expiration)\n- [RediSearch](https://redis.io/docs/latest/develop/ai/search-and-query)\n- [Search commands in MULTI/EXEC transactions and Lua scripts](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/transactions)"}],"examples":[{"id":"return-multiple-values-ex0","language":"sh","code":"127.0.0.1:6379> JSON.SET doc:1 $ '[{\"arr\": [1, 2, 3]}, {\"val\": \"hello\"}, {\"val\": \"world\"}]'\nOK\n127.0.0.1:6379> FT.CREATE idx ON JSON PREFIX 1 doc: SCHEMA $..arr AS arr NUMERIC $..val AS val TEXT\nOK","section_id":"return-multiple-values"},{"id":"return-multiple-values-ex1","language":"sh","code":"127.0.0.1:6379> FT.AGGREGATE idx * LOAD 2 arr val \n1) (integer) 1\n2) 1) \"arr\"\n   2) \"[1,2,3]\"\n   3) \"val\"\n   4) \"hello\"\n\n127.0.0.1:6379> FT.AGGREGATE idx * LOAD 2 arr val DIALECT 3\n1) (integer) 1\n2) 1) \"arr\"\n   2) \"[[1,2,3]]\"\n   3) \"val\"\n   4) \"[\\\"hello\\\",\\\"world\\\"]\"","section_id":"return-multiple-values"}]}
{"schema_version":2,"id":"commands/ft.aliasadd","title":"FT.ALIASADD","url":"https://redis.io/docs/latest/commands/ft.aliasadd/","summary":"Adds an alias to the index","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"0a6376a2ff71b272102f9fafb8922681f1fbde69c7bad6f94066795b5b946008","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Add an alias to an index\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>alias index</code></summary>\n\nis alias to be added to an index.\n</details>\n\nIndexes can have more than one alias, but an alias cannot refer to another\nalias.\n\nFT.ALIASADD allows administrators to transparently redirect application queries to alternative indexes."},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Add an alias to an index</b></summary>\n\nAdd an alias to an index.\n\n\n127.0.0.1:6379> FT.ALIASADD alias idx\nOK\n\n\nAttempting to add the same alias returns a message that the alias already exists.\n\n\n127.0.0.1:6379> FT.ALIASADD alias idx\n(error) Alias already exists\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis Cloud<br />Flexible & Annual | Redis Cloud<br />Free & Fixed | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: alias already exists, index does not exist.\n\n**RESP3:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: alias already exists, index does not exist."},{"id":"see-also","title":"See also","role":"related","text":"[`FT.ALIASDEL`](https://redis.io/docs/latest/commands/ft.aliasdel) | [`FT.ALIASUPDATE`](https://redis.io/docs/latest/commands/ft.aliasupdate)"}],"examples":[]}
{"schema_version":2,"id":"commands/ft.aliasdel","title":"FT.ALIASDEL","url":"https://redis.io/docs/latest/commands/ft.aliasdel/","summary":"Deletes an alias from the index","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"7cc484406c4de9f81c49871e4436ba2279608272f306929e95ce0bdab9a73cee","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Remove an alias from an index\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>alias</code></summary>\n\nis index alias to be removed.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Remove an alias from an index</b></summary>\n\nRemove an alias from an index.\n\n\n127.0.0.1:6379> FT.ALIASDEL alias\nOK\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis Cloud<br />Flexible & Annual | Redis Cloud<br />Free & Fixed | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: alias does not exist.\n\n**RESP3:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: alias does not exist."},{"id":"see-also","title":"See also","role":"related","text":"[`FT.ALIASADD`](https://redis.io/docs/latest/commands/ft.aliasadd) | [`FT.ALIASUPDATE`](https://redis.io/docs/latest/commands/ft.aliasupdate)"}],"examples":[]}
{"schema_version":2,"id":"commands/ft.aliaslist","title":"FT.ALIASLIST","url":"https://redis.io/docs/latest/commands/ft.aliaslist/","summary":"Lists all aliases for the index","since":"8.10.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"a2c6288c94e7afb301cccc09d9c6d4b95f99b560d51d90a8e061092059f47e6d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Lists all aliases for the given `index`."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>index</code></summary>\n\nis the name of the index for which to list aliases.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Add an alias to an index</b></summary>\n\nAdd an alias to an index.\n\n[code example]\n\nList the aliases for `idx:bicycle`\n\n[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis Cloud<br />Flexible & Annual | Redis Cloud<br />Free & Fixed | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Not supported</span> | <span title=\"Not supported\">&#x274c; Not supported</span> | <span title=\"Not supported\">&#x274c; Not supported</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n* An [array](https://redis.io/docs/latest/develop/reference/protocol-spec#array) of [bulk strings](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings), the names of existing index aliases, or an empty array if no aliases exist.\n* A [simple error](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) when the provided index doesn't exist.\n\n**RESP3:**\n\nOne of the following:\n\n* A [set](https://redis.io/docs/latest/develop/reference/protocol-spec#sets) of [bulk strings](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings), the names of index aliases, or an empty set if no aliases exist.\n* A [simple error](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) when the provided index doesn't exist."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> FT.ALIASADD alias idx:bicycle\nOK","section_id":"examples"},{"id":"examples-ex1","language":"plaintext","code":"> FT.ALIASLIST idx:bicycle\n1) \"alias\"","section_id":"examples"}]}
{"schema_version":2,"id":"commands/ft.aliasupdate","title":"FT.ALIASUPDATE","url":"https://redis.io/docs/latest/commands/ft.aliasupdate/","summary":"Adds or updates an alias to the index","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"7f0fb4778f763c8c63760776df65358ecab0c6cf5bc042caff42c442f670f1ec","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Add an alias to an index. If the alias is already associated with another\nindex, FT.ALIASUPDATE removes the alias association with the previous index.\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>alias index</code></summary>\n\nis alias to be added to an index.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Update an index alias</b></summary>\n\nUpdate the alias of an index.\n\n\n127.0.0.1:6379> FT.ALIASUPDATE alias idx\nOK"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis Cloud<br />Flexible & Annual | Redis Cloud<br />Free & Fixed | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: index does not exist.\n\n**RESP3:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: index does not exist."},{"id":"see-also","title":"See also","role":"related","text":"[`FT.ALIASADD`](https://redis.io/docs/latest/commands/ft.aliasadd) | [`FT.ALIASDEL`](https://redis.io/docs/latest/commands/ft.aliasdel)"},{"id":"related-topics","title":"Related topics","role":"related","text":"[RediSearch](https://redis.io/docs/latest/develop/ai/search-and-query)"}],"examples":[]}
{"schema_version":2,"id":"commands/ft.alter","title":"FT.ALTER","url":"https://redis.io/docs/latest/commands/ft.alter/","summary":"Adds a new field to the index","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"831fa95ee0def2e095947fd9527085ec7c33e5fb346942f5fddc440778289d70","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Add a new attribute to the index. Adding an attribute to the index causes any future document updates to use the new attribute when indexing and reindexing existing documents.\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>index</code></summary> \n\nis index name to create. \n</details>\n\n<details open>\n<summary><code>SKIPINITIALSCAN</code></summary> \n\nif set, does not scan and index.\n</details>\n\n<details open>\n<summary><code>SCHEMA ADD {attribute} {options} ...</code></summary>\n\nafter the SCHEMA keyword, declares which fields to add:\n\n- `attribute` is attribute to add.\n- `options` are attribute options. Refer to [`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create) for more information.\n\n<note><b>Note:</b>\n\nDepending on how the index was created, you may be limited by the number of additional text\nattributes which can be added to an existing index. If the current index contains fewer than 32\ntext attributes, then `SCHEMA ADD` will only be able to add attributes up to 32 total attributes (meaning that the\nindex will only ever be able to contain 32 total text attributes). If you wish for the index to\ncontain more than 32 attributes, create it with the `MAXTEXTFIELDS` option.\n</note>\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Alter an index</b></summary>\n\n\n127.0.0.1:6379> FT.ALTER idx SCHEMA ADD id2 NUMERIC SORTABLE\nOK\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis Cloud<br />Flexible & Annual | Redis Cloud<br />Free & Fixed | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: no such index, invalid schema syntax.\n\n**RESP3:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: no such index, invalid schema syntax."},{"id":"see-also","title":"See also","role":"related","text":"[`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create)"},{"id":"related-topics","title":"Related topics","role":"related","text":"- [RediSearch](https://redis.io/docs/latest/develop/ai/search-and-query)"}],"examples":[]}
{"schema_version":2,"id":"commands/ft.config-get","title":"FT.CONFIG GET","url":"https://redis.io/docs/latest/commands/ft.config-get/","summary":"Retrieves runtime configuration options","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"28ea6efcb7f7cd797c605df1c73f9f78188442de68e4e7584105997dc0a0c216","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Retrieve configuration options\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>option</code></summary> \n\nis name of the configuration option, or '*' for all. \n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Retrieve configuration options</b></summary>\n\n\n127.0.0.1:6379> FT.CONFIG GET TIMEOUT\n1) 1) TIMEOUT\n   2) 42\n\n\n\n127.0.0.1:6379> FT.CONFIG GET *\n 1) 1) EXTLOAD\n    2) (nil)\n 2) 1) SAFEMODE\n    2) true\n 3) 1) CONCURRENT_WRITE_MODE\n    2) false\n 4) 1) NOGC\n    2) false\n 5) 1) MINPREFIX\n    2) 2\n 6) 1) FORKGC_SLEEP_BEFORE_EXIT\n    2) 0\n 7) 1) MAXDOCTABLESIZE\n    2) 1000000\n 8) 1) MAXSEARCHRESULTS\n    2) 1000000\n 9) 1) MAXAGGREGATERESULTS\n    2) unlimited\n10) 1) MAXEXPANSIONS\n    2) 200\n11) 1) MAXPREFIXEXPANSIONS\n    2) 200\n12) 1) TIMEOUT\n    2) 42\n13) 1) INDEX_THREADS\n    2) 8\n14) 1) SEARCH_THREADS\n    2) 20\n15) 1) FRISOINI\n    2) (nil)\n16) 1) ON_TIMEOUT\n    2) return\n17) 1) GCSCANSIZE\n    2) 100\n18) 1) MIN_PHONETIC_TERM_LEN\n    2) 3\n19) 1) GC_POLICY\n    2) fork\n20) 1) FORK_GC_RUN_INTERVAL\n    2) 30\n21) 1) FORK_GC_CLEAN_THRESHOLD\n    2) 100\n22) 1) FORK_GC_RETRY_INTERVAL\n    2) 5\n23) 1) FORK_GC_CLEAN_NUMERIC_EMPTY_NODES\n    2) true\n24) 1) _FORK_GC_CLEAN_NUMERIC_EMPTY_NODES\n    2) true\n25) 1) _MAX_RESULTS_TO_UNSORTED_MODE\n    2) 1000\n26) 1) UNION_ITERATOR_HEAP\n    2) 20\n27) 1) CURSOR_MAX_IDLE\n    2) 300000\n28) 1) NO_MEM_POOLS\n    2) false\n29) 1) PARTIAL_INDEXED_DOCS\n    2) false\n30) 1) UPGRADE_INDEX\n    2) Upgrade config for upgrading\n31) 1) _NUMERIC_COMPRESS\n    2) false\n32) 1) _FREE_RESOURCE_ON_THREAD\n    2) true\n33) 1) _PRINT_PROFILE_CLOCK\n    2) true\n34) 1) RAW_DOCID_ENCODING\n    2) false\n35) 1) _NUMERIC_RANGES_PARENTS\n    2) 0\n36) 1) MINSTEMLEN\n    2) 4\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis Cloud<br />Flexible & Annual | Redis Cloud<br />Free & Fixed | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:-----------------|:------|\n| <span title=\"Not supported\"><nobr>&#x274c; Not supported</span> | <span title=\"Not supported\"><nobr>&#x26A0;&#xFE0F; Not supported</span><sup>1</sup> | <span title=\"Not supported\"><nobr>&#x274c; Not supported</nobr></span> |  |\n\n1. </a>[Contact support](https://redis.com/company/support/) to view the current configuration values or request configuration changes for Flexible or Annual Redis Cloud subscriptions."},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [arrays](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays), where each sub-array contains a configuration option name and its value.\n\n**RESP3:**\n\n[Map](https://redis.io/docs/latest/develop/reference/protocol-spec#maps) where keys are configuration option names and values are their corresponding values."},{"id":"see-also","title":"See also","role":"related","text":"[`FT.CONFIG SET`](https://redis.io/docs/latest/commands/ft.config-set) | [`FT.CONFIG HELP`](https://redis.io/docs/latest/commands/ft.config-help)"},{"id":"related-topics","title":"Related topics","role":"related","text":"[RediSearch](https://redis.io/docs/latest/develop/ai/search-and-query)"}],"examples":[]}
{"schema_version":2,"id":"commands/ft.config-help","title":"FT.CONFIG HELP","url":"https://redis.io/docs/latest/commands/ft.config-help/","summary":"Help description of runtime configuration options","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"d22311a754162acd8ba550396fdbba079c6be5fef2a2d6808cb77de2eb38f9a4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Describe configuration options\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>option</code></summary> \n\nis name of the configuration option, or '*' for all. \n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Get configuration details</b></summary>\n\n\n127.0.0.1:6379> FT.CONFIG HELP TIMEOUT\n1) 1) TIMEOUT\n   2) Description\n   3) Query (search) timeout\n   4) Value\n   5) \"42\"\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis Cloud<br />Flexible & Annual | Redis Cloud<br />Free & Fixed | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:-----------------|:------|\n| <span title=\"Not supported\"><nobr>&#x274c; Not supported</span> | <span title=\"Not supported\"><nobr>&#x274c; Not supported</span> | <span title=\"Not supported\"><nobr>&#x274c; Not supported</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of help information.\n\n**RESP3:**\n\n[Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of help information."},{"id":"see-also","title":"See also","role":"related","text":"[`FT.CONFIG SET`](https://redis.io/docs/latest/commands/ft.config-set) | [`FT.CONFIG GET`](https://redis.io/docs/latest/commands/ft.config-get)"},{"id":"related-topics","title":"Related topics","role":"related","text":"[RediSearch](https://redis.io/docs/latest/develop/ai/search-and-query)"}],"examples":[]}
{"schema_version":2,"id":"commands/ft.config-set","title":"FT.CONFIG SET","url":"https://redis.io/docs/latest/commands/ft.config-set/","summary":"Sets runtime configuration options","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"b6a826f1e836515e42b19b2efdfdeedea2e0a35848f10521de3d2667390ce83d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Set the value of a RediSearch configuration parameter.\n\nValues set using `FT.CONFIG SET` are not persisted after server restart.\n\nRediSearch configuration parameters are detailed in [Configuration parameters](https://redis.io/docs/latest/develop/ai/search-and-query/administration/configuration).\n\n> [!NOTE]\n> As detailed in the link above, not all RediSearch configuration parameters can be set at runtime.\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>option</code></summary> \n\nis name of the configuration option, or '*' for all. \n</details>\n\n<details open>\n<summary><code>value</code></summary> \n\nis value of the configuration option. \n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Set runtime configuration options</b></summary>\n\n\n127.0.0.1:6379> FT.CONFIG SET TIMEOUT 42\nOK\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis Cloud<br />Flexible & Annual | Redis Cloud<br />Free & Fixed | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:-----------------|:------|\n| <span title=\"Not supported\"><nobr>&#x26A0;&#xFE0F; Not supported</span><sup>1</sup> | <span title=\"Not supported\"><nobr>&#x26A0;&#xFE0F; Not supported</span><sup>2</sup> | <span title=\"Not supported\"><nobr>&#x274c; Not supported</nobr></span> |  |\n\n1. Use [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin) or the [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api) to change search and query configuration for Redis Software. See [search and query configuration compatibility with Redis Software](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/config) for more information and examples.\n\n2. [Contact support](https://redis.com/company/support/) to view the current configuration values or request configuration changes for Flexible or Annual Redis Cloud subscriptions."},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid option, invalid value.\n\n**RESP3:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid option, invalid value."},{"id":"see-also","title":"See also","role":"related","text":"[`FT.CONFIG GET`](https://redis.io/docs/latest/commands/ft.config-get) | [`FT.CONFIG HELP`](https://redis.io/docs/latest/commands/ft.config-help)"},{"id":"related-topics","title":"Related topics","role":"related","text":"[RediSearch](https://redis.io/docs/latest/develop/ai/search-and-query)"}],"examples":[]}
{"schema_version":2,"id":"commands/ft.create","title":"FT.CREATE","url":"https://redis.io/docs/latest/commands/ft.create/","summary":"Creates an index with the given spec","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"49da276c9945e3b4911d95203e8a7144694ccb5c5a60bdd1e319c50a0413830b","sections":[{"id":"description","title":"Description","role":"overview","text":"Create an index with the given specification. For usage, see [Examples](#examples)."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<a name=\"index\"></a><details open>\n<summary><code>index</code></summary>\n\nis index name to create.\nIf such index already exists, returns an error reply `(error) Index already exists`.\n</details>\n\n<a name=\"SCHEMA\"></a><details open>\n<summary><code>SCHEMA {identifier} AS {attribute} {attribute type} {options...</code></summary> \n\nafter the SCHEMA keyword, declares which fields to index:\n\n - `{identifier}` for hashes, is a field name within the hash.\n      For JSON, the identifier is a JSON Path expression.\n\n - `AS {attribute}` defines the attribute associated to the identifier. For example, you can use this feature to alias a complex JSONPath expression with more memorable (and easier to type) name.\n\n Field types are:\n\n - `TEXT` - Allows full-text search queries against the value in this attribute.\n\n - `TAG` - Allows exact-match queries, such as categories or primary keys, against the value in this attribute. For more information, see [Tag Fields](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/tags).\n\n - `NUMERIC` - Allows numeric range queries against the value in this attribute. See [query syntax docs](https://redis.io/docs/latest/develop/ai/search-and-query/query) for details on how to use numeric ranges.\n\n - `GEO` - Allows radius range queries against the value (point) in this attribute. The value of the attribute must be a string containing a longitude (first) and latitude separated by a comma.\n\n - `VECTOR` - Allows vector queries against the value in this attribute. This requires [query dialect 2](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/dialects#dialect-2) or above (introduced in [RediSearch v2.4](https://github.com/RediSearch/RediSearch/releases/tag/v2.4.3)). For more information, see [Vector Fields](https://redis.io/docs/latest/develop/ai/search-and-query/vectors).\n\n - `GEOSHAPE`- Allows polygon queries against the value in this attribute. The value of the attribute must follow a [WKT notation](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry) list of 2D points representing the polygon edges `POLYGON((x1 y1, x2 y2, ...)` separated by a comma. A `GEOSHAPE` field type can be followed by one of the following coordinate systems:\n   - `SPHERICAL` for Geographic longitude and latitude coordinates\n   - `FLAT` for Cartesian X Y coordinates\n  \n    The default coordinate system is `SPHERICAL`.\n    \n    Currently `GEOSHAPE` doesn't support JSON multi-value and `SORTABLE` option.\n\n Field options are:\n\n - `SORTABLE` - `NUMERIC`, `TAG`, `TEXT`, or `GEO` attributes can have an optional **SORTABLE** argument. As the user [sorts the results by the value of this attribute](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/sorting), the results are available with very low latency. Note that his adds memory overhead, so consider not declaring it on large text attributes. You can sort an attribute without the `SORTABLE` option, but the latency is not as good as with `SORTABLE`.\n\n - `UNF` - By default, for hashes (not with JSON) `SORTABLE` applies a normalization to the indexed value (characters set to lowercase, removal of diacritics). When using the unnormalized form (UNF), you can disable the normalization and keep the original form of the value. With JSON, `UNF` is implicit with `SORTABLE` (normalization is disabled).\n\n - `NOSTEM` - Text attributes can have the NOSTEM argument that disables stemming when indexing its values. This may be ideal for things like proper names.\n\n - `NOINDEX` - Attributes can have the `NOINDEX` option, which means they will not be indexed. This is useful in conjunction with `SORTABLE`, to create attributes whose update using PARTIAL will not cause full reindexing of the document. If an attribute has NOINDEX and doesn't have SORTABLE, it will just be ignored by the index.\n\n - `PHONETIC {matcher}` - Declaring a text attribute as `PHONETIC` will perform phonetic matching on it in searches by default. The obligatory {matcher} argument specifies the phonetic algorithm and language used. The following matchers are supported:\n\n   - `dm:en` - Double metaphone for English\n   - `dm:fr` - Double metaphone for French\n   - `dm:pt` - Double metaphone for Portuguese\n   - `dm:es` - Double metaphone for Spanish\n\n   For more information, see [Phonetic Matching](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/phonetic_matching).\n\n  - `WEIGHT {weight}` for `TEXT` attributes, declares the importance of this attribute when calculating result accuracy. This is a multiplication factor, and defaults to 1 if not specified.\n\n  - `SEPARATOR {sep}` for `TAG` attributes, indicates how the text contained in the attribute is to be split into individual tags. The default is `,`. The value must be a single character.\n\n  - `CASESENSITIVE` for `TAG` attributes, keeps the original letter cases of the tags. If not specified, the characters are converted to lowercase.\n\n  - `WITHSUFFIXTRIE` for `TEXT` and `TAG` attributes, keeps a suffix trie with all terms which match the suffix. It is used to optimize `contains` (*foo*) and `suffix` (*foo) queries. Otherwise, a brute-force search on the trie is performed. If suffix trie exists for some fields, these queries will be disabled for other fields.\n\n  - `INDEXEMPTY` for `TEXT` and `TAG` attributes, introduced in v2.10, allows you to index and search for empty strings. By default, empty strings are not indexed. \n\n  - `INDEXMISSING` for all field types, introduced in v2.10, allows you to search for missing values, that is, documents that do not contain a specific field. Note the difference between a field with an empty value and a document with a missing value. By default, missing values are not indexed.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<a name=\"ON\"></a><details open>\n<summary><code>ON {data_type}</code></summary>\n\ncurrently supports HASH (default) and JSON. To index JSON, you must have the [RedisJSON](https://redis.io/docs/latest/develop/data-types/json) module installed.\n</details>\n\n<a name=\"PREFIX\"></a><details open>\n<summary><code>PREFIX {count} {prefix}</code></summary> \n\ntells the index which keys it should index. You can add several prefixes to index. Because the argument is optional, the default is `*` (all keys).\n</details>\n\n<a name=\"FILTER\"></a><details open>\n<summary><code>FILTER {filter}</code></summary> \n\nis a filter expression with the full RediSearch aggregation expression language. It is possible to use `@__key` to access the key that was just added/changed. A field can be used to set field name by passing `'FILTER @indexName==\"myindexname\"'`.\n</details>\n\n<a name=\"LANGUAGE\"></a><details open>\n<summary><code>LANGUAGE {default_lang}</code></summary> \n\nif set, indicates the default language for documents in the index. Default is English.\n</details>\n\n<a name=\"LANGUAGE_FIELD\"></a><details open>\n<summary><code>LANGUAGE_FIELD {lang_attribute}</code></summary> \n\nis a document attribute set as the document language.\n\nA stemmer is used for the supplied language during indexing. If an unsupported language is sent, the command returns an error. The supported\nlanguages are `arabic`, `armenian`, `basque`, `catalan`, `chinese` (see below), `danish`, `dutch`, `english`, `finnish`, `french`, `german`, `greek`, `hindi`,\n`hungarian`, `indonesian`, `irish`, `italian`, `lithuanian`, `malay`, `nepali`, `norwegian`, `portuguese`, `romanian`, `russian`, `serbian`,\n`spanish`, `swedish`, `tagalog`, `tamil`, `turkish`, and `yiddish`.\n\nWhen adding Chinese language documents, set `LANGUAGE chinese` for the indexer to properly tokenize the terms. If you use the default language, then search terms are extracted based on punctuation characters and whitespace. The Chinese language tokenizer makes use of a segmentation algorithm (via [Friso](https://github.com/lionsoul2014/friso)), which segments text and checks it against a predefined dictionary. See [Stemming](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/stemming) for more information.\n</details>\n\n<a name=\"SCORE\"></a><details open>\n<summary><code>SCORE {default_score}</code></summary> \n\nis default score for documents in the index. Default score is 1.0.\n</details>\n\n<a name=\"SCORE_FIELD\"></a><details open>\n<summary><code>SCORE_FIELD {score_attribute}</code></summary> \n\nis document attribute that you use as the document rank based on the user ranking. Ranking must be between 0.0 and 1.0. If not set, the default score is 1.\n</details>\n\n<a name=\"PAYLOAD_FIELD\"></a><details open>\n<summary><code>PAYLOAD_FIELD {payload_attribute}</code></summary> \n\nis document attribute that you use as a binary safe payload string to the document that can be evaluated at query time by a custom scoring function or retrieved to the client.\n</details>\n\n<a name=\"MAXTEXTFIELDS\"></a><details open>\n<summary><code>MAXTEXTFIELDS</code></summary> \n\nforces RediSearch to encode indexes as if there were more than 32 text attributes, which allows you to add additional attributes (beyond 32) using [`FT.ALTER`](https://redis.io/docs/latest/commands/ft.alter). For efficiency, RediSearch encodes indexes differently if they are created with less than 32 text attributes.\n</details>\n\n<a name=\"NOOFFSETS\"></a><details open>\n<summary><code>NOOFFSETS</code></summary> \n\ndoes not store term offsets for documents. It saves memory, but does not allow exact searches or highlighting. It implies `NOHL`.\n</details>\n\n<a name=\"TEMPORARY\"></a><details open>\n<summary><code>TEMPORARY {seconds}</code></summary> \n\ncreates a lightweight temporary index that expires after a specified period of inactivity, in seconds. The internal idle timer is reset whenever the index is searched or added to. Because such indexes are lightweight, you can create thousands of such indexes without negative performance implications and, therefore, you should consider using `SKIPINITIALSCAN` to avoid costly scanning.\n\n> [!NOTE] Warning\n>  \n> When temporary indexes expire, they drop all the records associated with them.\n> [`FT.DROPINDEX`](https://redis.io/docs/latest/commands/ft.dropindex) was introduced with a default of not deleting docs and a `DD` flag that enforced deletion.\n> However, for temporary indexes, documents are deleted along with the index.\n> Historically, RediSearch used an FT.ADD command, which made a connection between the document and the index. Then, FT.DROP, also a hystoric command, deleted documents by default.\n> In version 2.x, RediSearch indexes hashes and JSONs, and the dependency between the index and documents no longer exists. \n\n</details>\n\n<a name=\"NOHL\"></a><details open>\n<summary><code>NOHL</code></summary> \n\nconserves storage space and memory by disabling highlighting support. If set, the corresponding byte offsets for term positions are not stored. `NOHL` is also implied by `NOOFFSETS`.\n</details>\n\n<a name=\"NOFIELDS\"></a><details open>\n<summary><code>NOFIELDS</code></summary> \n\ndoes not store attribute bits for each term. It saves memory, but it does not allow\n  filtering by specific attributes.\n</details>\n\n<a name=\"NOFREQS\"></a><details open>\n<summary><code>NOFREQS</code></summary> \n\navoids saving the term frequencies in the index. It saves memory, but does not allow sorting based on the frequencies of a given term within the document.\n</details>\n\n<a name=\"STOPWORDS\"></a><details open>\n<summary><code>STOPWORDS {count}</code></summary> \n\nsets the index with a custom stopword list, to be ignored during indexing and search time. `{count}` is the number of stopwords, followed by a list of stopword arguments exactly the length of `{count}`.\n\nIf not set, FT.CREATE takes the default list of stopwords. If `{count}` is set to 0, the index does not have stopwords.\n</details>\n\n<a name=\"SKIPINITIALSCAN\"></a><details open>\n<summary><code>SKIPINITIALSCAN</code></summary> \n\nif set, does not scan and index.\n</details>\n\n<a name=\"INDEXALL\"></a><details open>\n<summary><code>INDEXALL {ENABLE | DISABLE}</code></summary>\n\nintroduced in v8.0, maintains a compact index of existing document IDs to optimize wildcard queries, for example, `FT.SEARCH idx *`. When disabled, wildcard queries scan all IDs ever assigned—including deleted ones—which can hurt performance in write-heavy workloads. The default is DISABLE.\n</details>\n        \n<note><b>Notes:</b>\n\n - **Attribute number limits:** RediSearch supports up to 1024 attributes per schema, out of which at most 128 can be TEXT attributes. On 32 bit builds, at most 64 attributes can be TEXT attributes. The more attributes you have, the larger your index, as each additional 8 attributes require one extra byte per index record to encode. You can always use the `NOFIELDS` option and not encode attribute information into the index, for saving space, if you do not need filtering by text attributes. This will still allow filtering by numeric and geo attributes.\n - **Running in clustered databases:** When having several indices in a clustered database, you need to make sure the documents you want to index reside on the same shard as the index. You can achieve this by having your documents tagged by the index name.\n    \n   \n   127.0.0.1:6379> HSET doc:1{idx} ...\n   127.0.0.1:6379> FT.CREATE idx ... PREFIX 1 doc: ...\n   \n\n   When Running RediSearch in a clustered database, you can span the index across shards using [RSCoordinator](https://github.com/RedisLabsModules/RSCoordinator). In this case the above does not apply.\n\n</note>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Create an index</b></summary>\n\nCreate an index that stores the title, publication date, and categories of blog post hashes whose keys start with `blog:post:` (for example, `blog:post:1`).\n\n\n127.0.0.1:6379> FT.CREATE idx ON HASH PREFIX 1 blog:post: SCHEMA title TEXT SORTABLE published_at NUMERIC SORTABLE category TAG SORTABLE\nOK\n\n\nIndex the `sku` attribute from a hash as both a `TAG` and as `TEXT`:\n\n\n127.0.0.1:6379> FT.CREATE idx ON HASH PREFIX 1 blog:post: SCHEMA sku AS sku_text TEXT sku AS sku_tag TAG SORTABLE\n\n\nIndex two different hashes, one containing author data and one containing books, in the same index:\n\n\n127.0.0.1:6379> FT.CREATE author-books-idx ON HASH PREFIX 2 author:details: book:details: SCHEMA\nauthor_id TAG SORTABLE author_ids TAG title TEXT name TEXT\n\n\nIn this example, keys for author data use the key pattern `author:details:<id>` while keys for book data use the pattern `book:details:<id>`.\n\nIndex authors whose names start with G.\n\n\n127.0.0.1:6379> FT.CREATE g-authors-idx ON HASH PREFIX 1 author:details FILTER 'startswith(@name, \"G\")' SCHEMA name TEXT\n\n\nIndex only books that have a subtitle.\n\n\n127.0.0.1:6379> FT.CREATE subtitled-books-idx ON HASH PREFIX 1 book:details FILTER '@subtitle != \"\"' SCHEMA title TEXT\n\n\nIndex books that have a \"categories\" attribute where each category is separated by a `;` character.\n\n\n127.0.0.1:6379> FT.CREATE books-idx ON HASH PREFIX 1 book:details SCHEMA title TEXT categories TAG SEPARATOR \";\"\n\n</details>\n\n<details open>\n<summary><b>Index a JSON document using a JSON Path expression</b></summary>\n\nThe following example uses data similar to the hash examples above but uses JSON instead.\n\n\n127.0.0.1:6379> FT.CREATE idx ON JSON SCHEMA $.title AS title TEXT $.categories AS categories TAG\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis Cloud<br />Flexible & Annual | Redis Cloud<br />Free & Fixed | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: index already exists, invalid schema syntax.\n\n**RESP3:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: index already exists, invalid schema syntax."},{"id":"see-also","title":"See also","role":"related","text":"[`FT.ALTER`](https://redis.io/docs/latest/commands/ft.alter) | [`FT.DROPINDEX`](https://redis.io/docs/latest/commands/ft.dropindex)"},{"id":"related-topics","title":"Related topics","role":"related","text":"- [RediSearch](https://redis.io/docs/latest/develop/ai/search-and-query)\n- [RedisJSON](https://redis.io/docs/latest/develop/data-types/json)\n- [Friso](https://github.com/lionsoul2014/friso)\n- [Stemming](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/stemming)\n- [Phonetic Matching](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/phonetic_matching)\n- [RSCoordinator](https://github.com/RedisLabsModules/RSCoordinator)"}],"examples":[]}
{"schema_version":2,"id":"commands/ft.cursor-del","title":"FT.CURSOR DEL","url":"https://redis.io/docs/latest/commands/ft.cursor-del/","summary":"Deletes a cursor","since":"1.1.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"104f9336588a02e8c59d3dcabc402e07c3e5bc50482174097571e7cb6218a6ba","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Delete a cursor\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>index</code></summary>\n\nis index name.\n</details>\n\n<details open>\n<summary><code>cursor_id</code></summary>\n\nis id of the cursor.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Delete a cursor</b></summary>\n\n\nredis> FT.CURSOR DEL idx 342459320\nOK\n\n\nCheck that the cursor is deleted.\n\n\n127.0.0.1:6379> FT.CURSOR DEL idx 342459320\n(error) Cursor does not exist\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis Cloud<br />Flexible & Annual | Redis Cloud<br />Free & Fixed | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: cursor does not exist.\n\n**RESP3:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: cursor does not exist."},{"id":"see-also","title":"See also","role":"related","text":"[`FT.CURSOR READ`](https://redis.io/docs/latest/commands/ft.cursor-read)"},{"id":"related-topics","title":"Related topics","role":"related","text":"[RediSearch](https://redis.io/docs/latest/develop/ai/search-and-query)"}],"examples":[]}
{"schema_version":2,"id":"commands/ft.cursor-read","title":"FT.CURSOR READ","url":"https://redis.io/docs/latest/commands/ft.cursor-read/","summary":"Reads from a cursor","since":"1.1.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"29074ab2b3182e0ff6b1453ab3216f387de4b63af2213d6dc1309fa35db092ff","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Read next results from an existing cursor\n\n[Examples](#examples)\n\nSee [Cursor API](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/aggregations#cursor-api) for more details."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>index</code></summary>\n\nis index name.\n</details>\n\n<details open>\n<summary><code>cursor_id</code></summary>\n\nis id of the cursor.\n</details>\n\n<details open>\n<summary><code>[COUNT read_size]</code></summary>\n\nis number of results to read. This parameter overrides `COUNT` specified in [`FT.AGGREGATE`](https://redis.io/docs/latest/commands/ft.aggregate).\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Read next results from a cursor</b></summary>\n\n\n127.0.0.1:6379> FT.CURSOR READ idx 342459320 COUNT 50\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis Cloud<br />Flexible & Annual | Redis Cloud<br />Free & Fixed | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) with search results and metadata.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: cursor not found.\n\n**RESP3:**\n\nOne of the following:\n* [Map](https://redis.io/docs/latest/develop/reference/protocol-spec#maps) with structured search results and metadata.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: cursor not found."},{"id":"see-also","title":"See also","role":"related","text":"[`FT.CURSOR DEL`](https://redis.io/docs/latest/commands/ft.cursor-del) | [`FT.AGGREGATE`](https://redis.io/docs/latest/commands/ft.aggregate)"},{"id":"related-topics","title":"Related topics","role":"related","text":"- [RediSearch](https://redis.io/docs/latest/develop/ai/search-and-query)\n- [Search commands in MULTI/EXEC transactions and Lua scripts](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/transactions)"}],"examples":[]}
{"schema_version":2,"id":"commands/ft.dictadd","title":"FT.DICTADD","url":"https://redis.io/docs/latest/commands/ft.dictadd/","summary":"Adds terms to a dictionary","since":"1.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"0386927b734f4de7de73fffc66fe6d8b67e03c8961a21cd93264990bcb89d1df","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Add terms to a dictionary\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>dict</code></summary>\n\nis dictionary name.\n</details>\n\n<details open>\n<summary><code>term</code></summary>\n\nterm to add to the dictionary.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Add terms to a dictionary</b></summary>\n\n\n127.0.0.1:6379> FT.DICTADD dict foo bar \"hello world\"\n(integer) 3\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis Cloud<br />Flexible & Annual | Redis Cloud<br />Free & Fixed | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): the number of new terms added to the dictionary.\n\n**RESP3:**\n\n[Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): the number of new terms added to the dictionary."},{"id":"see-also","title":"See also","role":"related","text":"[`FT.DICTDEL`](https://redis.io/docs/latest/commands/ft.dictdel) | [`FT.DICTDUMP`](https://redis.io/docs/latest/commands/ft.dictdump)"},{"id":"related-topics","title":"Related topics","role":"related","text":"[RediSearch](https://redis.io/docs/latest/develop/ai/search-and-query)"}],"examples":[]}
{"schema_version":2,"id":"commands/ft.dictdel","title":"FT.DICTDEL","url":"https://redis.io/docs/latest/commands/ft.dictdel/","summary":"Deletes terms from a dictionary","since":"1.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"e88f84de842239391268baffbaf90e2aa9d80d273ee187146fce81aa1261d7fd","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Delete terms from a dictionary\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>dict</code></summary>\n\nis dictionary name.\n</details>\n\n<details open>\n<summary><code>term</code></summary>\n\nterm to delete from the dictionary.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Delete terms from a dictionary</b></summary>\n\n\n127.0.0.1:6379> FT.DICTDEL dict foo bar \"hello world\"\n(integer) 3\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis Cloud<br />Flexible & Annual | Redis Cloud<br />Free & Fixed | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): the number of terms deleted from the dictionary.\n\n**RESP3:**\n\n[Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): the number of terms deleted from the dictionary."},{"id":"see-also","title":"See also","role":"related","text":"[`FT.DICTADD`](https://redis.io/docs/latest/commands/ft.dictadd) | [`FT.DICTDUMP`](https://redis.io/docs/latest/commands/ft.dictdump)"},{"id":"related-topics","title":"Related topics","role":"related","text":"[RediSearch](https://redis.io/docs/latest/develop/ai/search-and-query)"}],"examples":[]}
{"schema_version":2,"id":"commands/ft.dictdump","title":"FT.DICTDUMP","url":"https://redis.io/docs/latest/commands/ft.dictdump/","summary":"Dumps all terms in the given dictionary","since":"1.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"79eb977ebf2149fdc61a0cd141666cb2145e73cc386ab2d1bffa1a2eba1452d0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Dump all terms in the given dictionary\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>dict</code></summary>\n\nis dictionary name.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Dump all terms in the dictionary</b></summary>\n\n\n127.0.0.1:6379> FT.DICTDUMP dict\n1) \"foo\"\n2) \"bar\"\n3) \"hello world\"\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis Cloud<br />Flexible & Annual | Redis Cloud<br />Free & Fixed | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of dictionary terms.\n\n**RESP3:**\n\n[Set](https://redis.io/docs/latest/develop/reference/protocol-spec#sets) of dictionary terms."},{"id":"see-also","title":"See also","role":"related","text":"[`FT.DICTADD`](https://redis.io/docs/latest/commands/ft.dictadd) | [`FT.DICTDEL`](https://redis.io/docs/latest/commands/ft.dictdel)"},{"id":"related-topics","title":"Related topics","role":"related","text":"[RediSearch](https://redis.io/docs/latest/develop/ai/search-and-query)"}],"examples":[]}
{"schema_version":2,"id":"commands/ft.dropindex","title":"FT.DROPINDEX","url":"https://redis.io/docs/latest/commands/ft.dropindex/","summary":"Deletes the index","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"1f88841b7787e0e4c90aa23834001fa0a000e8f2f7050a10a17b0573934d73e0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Delete an index\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>index</code></summary>\n\nis full-text index name. You must first create the index using [`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create).\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open>\n<summary><code>DD</code></summary>\n\ndrop index operation that, if set, deletes the actual document keys. `FT.DROPINDEX index DD` is an asynchronous operation.\n\nBy default, FT.DROPINDEX does not delete the documents associated with the index. Adding the `DD` option deletes the documents as well. \nIf an index creation is still running ([`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create) is running asynchronously), only the document hashes that have already been indexed are deleted. \nThe document hashes left to be indexed remain in the database.\nTo check the completion of the indexing, use [`FT.INFO`](https://redis.io/docs/latest/commands/ft.info).\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Delete an index</b></summary>\n\n\n127.0.0.1:6379> FT.DROPINDEX idx DD\nOK\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis Cloud<br />Flexible & Annual | Redis Cloud<br />Free & Fixed | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: no such index.\n\n**RESP3:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: no such index."},{"id":"see-also","title":"See also","role":"related","text":"[`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create) | [`FT.INFO`](https://redis.io/docs/latest/commands/ft.info)"},{"id":"related-topics","title":"Related topics","role":"related","text":"[RediSearch](https://redis.io/docs/latest/develop/ai/search-and-query)"}],"examples":[]}
{"schema_version":2,"id":"commands/ft.explain","title":"FT.EXPLAIN","url":"https://redis.io/docs/latest/commands/ft.explain/","summary":"Returns the execution plan for a complex query","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"de63563d47b842f14989cbbd054854e03b919542822608ea8fd9a832888b0753","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Return the execution plan for a complex query\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>index</code></summary>\n\nis index name. You must first create the index using [`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create).\n</details>\n\n<details open>\n<summary><code>query</code></summary>\n\nis query string, as if sent to FT.SEARCH`.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open>\n<summary><code>DIALECT {dialect_version}</code></summary>\n\nis dialect version under which to execute the query. If not specified, the query executes under the default dialect version set during module initial loading or via [`FT.CONFIG SET`](https://redis.io/docs/latest/commands/ft.config-set) command.\n</details>\n\n> [!NOTE] Notes\n>  \n> - In the returned response, a `+` on a term is an indication of stemming.\n> - Use `redis-cli --raw` to properly read line-breaks in the returned response."},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Return the execution plan for a complex query</b></summary>\n\n\n$ redis-cli --raw\n\n127.0.0.1:6379> FT.EXPLAIN rd \"(foo bar)|(hello world) @date:[100 200]|@date:[500 +inf]\"\nINTERSECT {\n  UNION {\n    INTERSECT {\n      foo\n      bar\n    }\n    INTERSECT {\n      hello\n      world\n    }\n  }\n  UNION {\n    NUMERIC {100.000000 <= x <= 200.000000}\n    NUMERIC {500.000000 <= x <= inf}\n  }\n}\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis Cloud<br />Flexible & Annual | Redis Cloud<br />Free & Fixed | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Bulk string](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) containing the query execution plan.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: no such index, syntax error in query.\n\n**RESP3:**\n\nOne of the following:\n* [Bulk string](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) containing the query execution plan.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: no such index, syntax error in query."},{"id":"see-also","title":"See also","role":"related","text":"[`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create) | [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) | [`FT.CONFIG SET`](https://redis.io/docs/latest/commands/ft.config-set)"},{"id":"related-topics","title":"Related topics","role":"related","text":"[RediSearch](https://redis.io/docs/latest/develop/ai/search-and-query)"}],"examples":[]}
{"schema_version":2,"id":"commands/ft.explaincli","title":"FT.EXPLAINCLI","url":"https://redis.io/docs/latest/commands/ft.explaincli/","summary":"Returns the execution plan for a complex query","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"8a7bb669888dc9436c2788280ae2ce44d8f228fd43c9fb020e04a5d7d9c3ccdc","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Return the execution plan for a complex query but formatted for easier reading without using `redis-cli --raw`\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>index</code></summary>\n\nis index name. You must first create the index using [`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create).\n</details>\n\n<details open>\n<summary><code>query</code></summary>\n\nis query string, as if sent to FT.SEARCH`.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open>\n<summary><code>DIALECT {dialect_version}</code></summary>\n\nis dialect version under which to execute the query. If not specified, the query executes under the default dialect version set during module initial loading or via [`FT.CONFIG SET`](https://redis.io/docs/latest/commands/ft.config-set) command.\n\n> [!NOTE]\n>  \n> In the returned response, a `+` on a term is an indication of stemming.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Return the execution plan for a complex query</b></summary>\n\n\n$ redis-cli\n\n127.0.0.1:6379> FT.EXPLAINCLI rd \"(foo bar)|(hello world) @date:[100 200]|@date:[500 +inf]\"\n 1) INTERSECT {\n 2)   UNION {\n 3)     INTERSECT {\n 4)       UNION {\n 5)         foo\n 6)         +foo(expanded)\n 7)       }\n 8)       UNION {\n 9)         bar\n10)         +bar(expanded)\n11)       }\n12)     }\n13)     INTERSECT {\n14)       UNION {\n15)         hello\n16)         +hello(expanded)\n17)       }\n18)       UNION {\n19)         world\n20)         +world(expanded)\n21)       }\n22)     }\n23)   }\n24)   UNION {\n25)     NUMERIC {100.000000 <= @date <= 200.000000}\n26)     NUMERIC {500.000000 <= @date <= inf}\n27)   }\n28) }\n29)\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis Cloud<br />Flexible & Annual | Redis Cloud<br />Free & Fixed | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [bulk string replies](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) containing the query execution plan in CLI format.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: no such index, syntax error in query.\n\n**RESP3:**\n\nOne of the following:\n* [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [bulk string replies](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) containing the query execution plan in CLI format.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: no such index, syntax error in query."},{"id":"see-also","title":"See also","role":"related","text":"[`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create) | [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) | [`FT.CONFIG SET`](https://redis.io/docs/latest/commands/ft.config-set)"},{"id":"related-topics","title":"Related topics","role":"related","text":"[RediSearch](https://redis.io/docs/latest/develop/ai/search-and-query)"}],"examples":[]}
{"schema_version":2,"id":"commands/ft.hybrid","title":"FT.HYBRID","url":"https://redis.io/docs/latest/commands/ft.hybrid/","summary":"Performs hybrid search combining text search and vector similarity search","since":"8.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-15T15:16:04-07:00","page_type":"content","content_hash":"53dd0f500b4ed03171fafac6f2b488d66fab0ae986a3d1ffbc5a617cd3236c4c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Performs hybrid search combining text search and vector similarity with configurable fusion methods.\n\n`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.\n\n> [!NOTE]\n> This command will only return document IDs (`keyid`) and scores to which the user has read access.\n> To retrieve entire documents, use projections with `LOAD *` or `LOAD <count> field...`.\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>index</code></summary>\n\nis the name of the index. You must first create the index using [`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create).\n</details>\n\n<details open>\n<summary><code>SEARCH \"search-expression\"</code></summary>\n\ndefines the text search component of the hybrid query. The search expression uses the same syntax as [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) queries, supporting all text search capabilities including field-specific searches, boolean operations, and phrase matching.\n</details>\n\n<details open>\n<summary><code>VSIM @vector_field \"vector-data\"</code></summary>\n\ndefines 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 <vector-blob>`).\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open>\n<summary><code>SCORER algorithm params...</code></summary>\n\nspecifies 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.\n\nExample: `SCORER 4 BM25 1.2 0.75` uses BM25 algorithm with parameters 1.2 and 0.75.\n</details>\n\n<details open>\n<summary><code>YIELD_SCORE_AS alias-search-score</code></summary>\n\nassigns an alias to the search score for use in post-processing operations like `APPLY` or `SORTBY`.\n</details>\n\n<details open>\n<summary><code>KNN count K top-k [EF_RUNTIME ef-value] [SHARD_K_RATIO shard-k-ratio] [YIELD_SCORE_AS name]</code></summary>\n\nconfigures 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. `SHARD_K_RATIO` controls the number of results each shard retrieves relative to the requested `top_k` in cluster setups. `YIELD_SCORE_AS` assigns an alias to the score value. See the [vector search](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#cluster-optimization-examples) documentation for more information about `SHARD_K_RATIO`.\n</details>\n\n<details open>\n<summary><code>RANGE count RADIUS radius-value [EPSILON epsilon-value] [YIELD_SCORE_AS name]</code></summary>\n\nconfigures 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.\n</details>\n\n<details open>\n<summary><code>FILTER \"filter-expression\"</code></summary>\n\napplies 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`](https://redis.io/docs/latest/commands/ft.search), supporting all text search capabilities including field-specific searches, boolean operations, and phrase matching\n</details>\n\n<details open>\n<summary><code>POLICY [ADHOC_BF|BATCHES] [BATCH_SIZE batch-size-value]</code></summary>\n\ncontrols 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](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#filters) for more information.\n</details>\n\n<details open>\n<summary><code>COMBINE method params...</code></summary>\n\nspecifies how to fuse the text search and vector similarity results. Supports multiple fusion methods:\n\n- **RRF (Reciprocal Rank Fusion)**: Default method. Parameters include `WINDOW` (default 20) and `CONSTANT` (default 60).\n- **LINEAR**: Linear combination with `ALPHA` and `BETA` weights.\n- **FUNCTION**: Custom fusion function (future support).\n\nExample: `COMBINE RRF 4 WINDOW 40 CONSTANT 1.5`\n</details>\n\n<details open>\n<summary><code>YIELD_SCORE_AS alias-combined-score</code></summary>\n\nassigns an alias to the combined fusion score for use in post-processing operations.\n</details>\n\n<details open>\n<summary><code>LOAD count field...</code></summary>\n\nspecifies which fields to return in the results. The `count` parameter indicates the number of fields that follow.\n\nExample: `LOAD 3 @category @brand @price`\n</details>\n\n<details open>\n<summary><code>GROUPBY count field... REDUCE function...</code></summary>\n\ngroups results by specified fields and applies reduction functions. Follows the parameter count convention. The `count` parameter indicates the number of fields that follow.\n\nExample: `GROUPBY 4 category brand REDUCE 2 COUNT 0`\n</details>\n\n<details open>\n<summary><code>APPLY expression AS field</code></summary>\n\napplies transformations to create new fields. Can reference aliased scores and distances.\n\nExample: `APPLY \"@vector_distance+@score\" AS final_score`\n</details>\n\n<details open>\n<summary><code>SORTBY count field [ASC|DESC]</code></summary>\n\nsorts the final results by the specified field in ascending or descending order. The `count` parameter indicates the number of fields that follow.\n</details>\n\n\n\n<details open>\n<summary><code>LIMIT offset num</code></summary>\n\nlimits the final results. Default limit is 10 when not specified. The `offset` parameter is zero-indexed.\n</details>\n\n<details open>\n<summary><code>PARAMS count key value...</code></summary>\n\ndefines parameter substitution for the query. Parameters can be referenced in search expressions using `$parameter_name`.\n\nExample: `PARAMS 4 min_price 50 max_price 200`\n</details>\n\n<details open>\n<summary><code>EXPLAINSCORE</code></summary>\n\nreturns an explanation of how each result's combined score was calculated. Available in Redis 8.10 and later.\n\nEach result gets a `score` field holding a two-element array: the combined score, followed by a nested explanation tree. You don't need `WITHSCORES`. The tree shows:\n\n- The final score formula and the fusion method with its parameters: `RRF` with `window` and `constant`, or `LINEAR` with `alpha`, `beta`, and `window`.\n- The text branch: the scorer name, such as `BM25STD`, and the scorer's own breakdown. With `RRF`, it shows the text rank. With `LINEAR`, it shows the text contribution and the normalized text score.\n- The vector branch: `KNN` or `RANGE` with its radius and epsilon. With `RRF`, it shows the vector rank. With `LINEAR`, it shows the vector contribution and the normalized vector score. `RANGE` also shows whether the document matched within the radius.\n\nA branch that didn't match the document shows `<no match>`.\n\n`EXPLAINSCORE` can't be combined with `GROUPBY`. The command returns the error `EXPLAINSCORE is not supported with GROUPBY`.\n</details>\n\n<details open>\n<summary><code>TIMEOUT timeout</code></summary>\n\nsets a runtime timeout for the query execution in milliseconds. When the query times out, the [`search-on-timeout`](https://redis.io/docs/latest/develop/ai/search-and-query/administration/configuration#search-on-timeout) policy applies. With `RETURN` or `RETURN_STRICT`, the command returns the results accumulated so far and reports the timeout in `warnings`. With `FAIL`, it returns an error.\n</details>"},{"id":"default-values-and-behaviors","title":"Default values and behaviors","role":"content","text":"FT.HYBRID provides sensible defaults to ease onboarding:\n\n- **Query count**: 2 (one SEARCH and one VSIM component required)\n- **Default LIMIT**: 10 results\n- **Default SCORER**: BM25STD for text search\n- **Default KNN K**: 10 neighbors\n- **Default RRF WINDOW**: 20\n- **Default RRF CONSTANT**: 60\n- **Default EF_RUNTIME**: 10 (as vector KNN [default](https://redis.io/docs/latest/develop/ai/search-and-query/vectors/#hnsw-index))\n- **Default EPSILON**: 0.01 (as the vector RANGE [default](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#hnsw-index))"},{"id":"parameter-count-convention","title":"Parameter count convention","role":"parameters","text":"All multi-parameter options use a count prefix that contains ALL tokens that follow:\n\n- `KNN 4 K 10 EF_RUNTIME 100` - 2 key-value pairs\n- `PARAMS 4 min_price 50 max_price 200` - 2 key-value pairs\n- `COMBINE RRF 4 WINDOW 40 CONSTANT 1.5` - RRF method with 2 key-value pairs\n\nThe only exception is alias usage with `AS`, which is not counted:\n- `APPLY \"@vector_distance+@score\" AS final_score`\n- `LOAD 3 @category AS cat @brand AS brd @price AS prc`"},{"id":"reserved-fields","title":"Reserved fields","role":"content","text":"The following fields are reserved for internal use:\n\n- `@__key` - reserved for loading key IDs when required\n- `@__score` - reserved for the combined score (can be aliased)\n- `@vector_distance` - yields the vector distance (can be aliased)\n- `@__combined_score` - fused score from the COMBINE step"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Basic hybrid search</b></summary>\n\nPerform a simple hybrid search combining text search for \"laptop\" with vector similarity:\n\n\n127.0.0.1:6379> FT.HYBRID products-idx\n  SEARCH \"laptop\"\n  VSIM @description_vector $query_vec\n  KNN 2 K 10\n  PARAMS 2 query_vec <vector_blob>\n\n</details>\n\n<details open>\n<summary><b>Hybrid search with custom scoring and fusion</b></summary>\n\nSearch for electronics with custom BM25 parameters and RRF fusion:\n\n\n127.0.0.1:6379> FT.HYBRID products-idx\n  SEARCH \"@category:electronics\"\n  SCORER 4 BM25 1.5 0.8\n  YIELD_SCORE_AS text_score\n  VSIM @features_vector $query_vec\n  KNN 4 K 20 EF_RUNTIME 200\n  YIELD_SCORE_AS vector_score\n  COMBINE RRF 4 WINDOW 50 CONSTANT 80\n  YIELD_SCORE_AS hybrid_score\n  SORTBY 2 hybrid_score DESC\n  LIMIT 0 20\n  PARAMS 2 query_vec <vector_blob>\n\n</details>\n\n<details open>\n<summary><b>Hybrid search with pre-filtering</b></summary>\n\nSearch with vector pre-filtering and post-processing:\n\n\n127.0.0.1:6379> FT.HYBRID products-idx\n  SEARCH \"smartphone\"\n  VSIM @image_vector $query_vec\n  KNN 2 K 15\n  FILTER \"@price:[100 500]\"\n  COMBINE LINEAR 4 ALPHA 0.7 BETA 0.3\n  LOAD 4 @title @price @category @rating\n  APPLY \"@price * 0.9\" AS discounted_price\n  SORTBY 2 rating DESC\n  PARAMS 2 query_vec <vector_blob>\n\n</details>\n\n<details open>\n<summary><b>Hybrid search with parameters</b></summary>\n\nUse parameter substitution for dynamic queries:\n\n\n127.0.0.1:6379> FT.HYBRID products-idx\n  SEARCH \"@brand:$brand_name\"\n  VSIM @content_vector $query_vector\n  RANGE 4 RADIUS 0.8 EPSILON 0.1\n  FILTER \"@availability:$stock_status\"\n  PARAMS 6 brand_name \"Apple\" query_vector <vector_blob> stock_status \"in_stock\"\n\n</details>\n\n<details open>\n<summary><b>Explain hybrid scores</b></summary>\n\nReturn the top result with an explanation of its RRF score. The document ranked first in both the text and vector branches:\n\n\n127.0.0.1:6379> FT.HYBRID products-idx\n  SEARCH \"shoes\"\n  VSIM @embedding $query_vec\n  KNN 2 K 3\n  EXPLAINSCORE\n  LIMIT 0 1\n  PARAMS 2 query_vec <vector_blob>\n1) total_results\n2) (integer) 3\n3) results\n4) 1) 1) score\n      2) 1) \"0.03278688524590164\"\n         2) 1) final score: 1 / (constant 60.00 + rank 1) + 1 / (constant 60.00 + rank 1) = 0.032786885245901641\n            2) 1) 1) Hybrid score (RRF: window=20, constant=60.00)\n                  2) 1) 1) text rank = 1\n                        2) 1) 1) Text scorer: BM25STD\n                              2) 1) 1) Final BM25 : words BM25 1.39 * document score 1.00\n                                    2) 1) 1) (Weight 1.00 * children BM25 1.39)\n                                          2) 1) shoes: (0.69 = Weight 1.00 * IDF 0.69 * (F 1.00 * (k1 1.2 + 1)) / (F 1.00 + k1 1.2 * (1 - b 0.75 + b 0.75 * Doc Len 3 / Average Doc Len 3.00)))\n                                             2) +shoe: (0.69 = Weight 1.00 * IDF 0.69 * (F 1.00 * (k1 1.2 + 1)) / (F 1.00 + k1 1.2 * (1 - b 0.75 + b 0.75 * Doc Len 3 / Average Doc Len 3.00)))\n                     2) 1) vector branch (KNN)\n                        2) 1) vector rank = 1\n      3) \"__key\"\n      4) \"product:1\"\n      5) \"__score\"\n      6) \"0.0327868852459\"\n5) warnings\n6) (empty array)\n7) execution_time\n8) \"2.0055\"\n\n</details>"},{"id":"complexity","title":"Complexity","role":"performance","text":"FT.HYBRID complexity depends on both the text search and vector similarity components:\n- 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.\n- Vector search: O(log n) for KNN with HNSW index, O(n) for range queries\n- Fusion: O(k) where k is the number of results to combine\n- Overall complexity is typically dominated by the more expensive component"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Array](https://redis.io/docs/latest/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](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays).\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: no such index, syntax error in query.\n\n**RESP3:**\n\nOne of the following:\n* [Map](https://redis.io/docs/latest/develop/reference/protocol-spec#maps) with the following fields:\n    - `total_results`: [Integer](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) - total number of results\n    - `execution_time`: [double](https://redis.io/docs/latest/develop/reference/protocol-spec#doubles) containing hybrid query execution time\n    - `warnings`: [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of warning messages indicating partial results due to index errors or `MAXPREFIXEXPANSIONS`, out-of-memory conditions, and `TIMEOUT` reached\n    - `results`: [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [maps](https://redis.io/docs/latest/develop/reference/protocol-spec#maps) containing document information\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: no such index, syntax error in query."},{"id":"see-also","title":"See also","role":"related","text":"[`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create) | [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) | [`FT.AGGREGATE`](https://redis.io/docs/latest/commands/ft.aggregate)"},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Vector search concepts](https://redis.io/docs/latest/develop/ai/search-and-query/vectors)\n- [Combined search](https://redis.io/docs/latest/develop/ai/search-and-query/query/combined)\n- [Search commands in MULTI/EXEC transactions and Lua scripts](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/transactions)"}],"examples":[]}
{"schema_version":2,"id":"commands/ft.info","title":"FT.INFO","url":"https://redis.io/docs/latest/commands/ft.info/","summary":"Returns information and statistics on the index","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"c09b3a4de8241dc3e9cb2d884ceee0685dd6b7437e1074a1fdc249391463145b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns information and statistics about a given index."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"`index`\n<br />\nis the name of the given index. You must first create the index using [`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create). You can also use an alias of `index` created using [`FT.ALIASADD`](https://redis.io/docs/latest/commands/ft.aliasadd) or [`FT.ALIASUPDATE`](https://redis.io/docs/latest/commands/ft.aliasupdate)."},{"id":"returned-values","title":"Returned values","role":"returns","text":""},{"id":"general","title":"General","role":"content","text":"| Return field name | Definition |\n|:---               |:---        |\n| `index_name` | The index name that was defined when index was created. |\n| `index_options` | The index options selected during `FT.CREATE` such as `FILTER {filter}`, `LANGUAGE {default_lang}`, etc. |\n| `index_definition` | Includes `key_type`, hash or JSON; `prefixes`, if any; and `default_score`. |\n| `attributes` | The index schema field names, types, and attributes. |\n| `num_docs` | The number of documents. |\n| `max_doc_id` | The maximum document ID. |\n| `num_terms` | The number of distinct terms. |\n| `num_records` | The total number of records. |"},{"id":"various-size-statistics","title":"Various size statistics","role":"content","text":"| Statistic | Definition |\n|:---       |:---        |\n| `inverted_sz_mb` | The memory used by the inverted index, which is the core data structure used for searching in RediSearch. The size is given in megabytes. |\n| `vector_index_sz_mb` | The memory used by the vector index, which stores any vectors associated with each document. |\n| `total_inverted_index_blocks` | The total number of blocks in the inverted index. |\n| `offset_vectors_sz_mb` | The memory used by the offset vectors, which store positional information for terms in documents. |\n| `doc_table_size_mb` | The memory used by the document table, which contains metadata about each document in the index. |\n| `sortable_values_size_mb` | The memory used by sortable values, which are values associated with documents and used for sorting purposes. |\n| `key_table_size_mb` | The memory used by the key table, which stores the mapping between document IDs and Redis keys. |\n| `geoshapes_sz_mb` | The memory used by GEO-related fields. |\n| `records_per_doc_avg` | The average number of records (including deletions) per document. |\n| `bytes_per_record_avg` | The average size of each record in bytes. |\n| `offsets_per_term_avg` | The average number of offsets (position information) per term. |\n| `offset_bits_per_record_avg` | The average number of bits used for offsets per record. |\n| `tag_overhead_sz_mb` | The size of the TAG index structures used for optimising performance. |\n| `text_overhead_sz_mb` | The size of the TEXT index structures used for optimising performance. |\n| `total_index_memory_sz_mb` | The total memory consumed by all indexes in the DB. |"},{"id":"indexing-related-statistics","title":"Indexing-related statistics","role":"content","text":"| Statistic | Definition |\n|:---       |:---        |\n| `hash_indexing_failures` | The number of failures encountered during indexing. |\n| `total_indexing_time` | The cumulative wall-clock time spent indexing documents in ms. |\n| `indexing` | Indicates whether the index is currently being generated. |\n| `percent_indexed` | The percentage of the index that has been successfully generated (1 means 100%). |\n| `number_of_uses` | The number of times the index has been used. |\n| `cleaning` | The index deletion flag. A value of `1` indicates index deletion is in progress. |"},{"id":"garbage-collection-statistics","title":"Garbage collection statistics","role":"content","text":"| Statistic | Definition |\n|:---       |:---        |\n| `bytes_collected` | The number of bytes collected during garbage collection. |\n| `total_ms_run` | The total time in milliseconds spent on garbage collection. |\n| `total_cycles` | The total number of garbage collection cycles. |\n| `average_cycle_time_ms` | The average time in milliseconds for each garbage collection cycle. The value `nan` indicates that the average cycle time is not available. |\n| `last_run_time_ms` | The time in milliseconds taken by the last garbage collection run. |\n\nThe next two GC-related fields are relevant in scenarios where simultaneous changes occurred in the same memory area for both the parent process and the child process, resulting in the parent discarding these changes.\n\n| Statistic | Definition |\n|:---       |:---        |\n| `gc_numeric_trees_missed` | The number of numeric tree nodes whose changes were discarded due to splitting by the parent process during garbage collection. |\n| `gc_blocks_denied` | The number of blocks whose changes were discarded (skipped) because they were modified by the parent process during the garbage collection. Notably, as inverted index blocks are append-only, only the last block of an inverted index can be skipped. |"},{"id":"cursor-statistics","title":"Cursor statistics","role":"content","text":"| Statistic | Definition |\n|:---       |:---        |\n| `global_idle` | The number of idle cursors in the system. |\n| `global_total` | The total number of cursors in the system. |\n| `index_capacity` | The maximum number of cursors allowed per index. |\n| `index_total` | The total number of cursors open on the index. |"},{"id":"other-statistics","title":"Other statistics","role":"content","text":"- Dialect statistics: the number of times the index was searched using each DIALECT, 1 - 4.\n- Index error statistics, including `indexing failures`, `last indexing error`, and `last indexing error key`.\n- Field statistics, including `indexing failures`, `last indexing error`, and `last indexing error key` for each schema field."},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Return statistics about an index</b></summary>\n\n\n127.0.0.1:6379> ft.info idx:bicycle\n 1) index_name\n 2) idx:bicycle\n 3) index_options\n 4) (empty array)\n 5) index_definition\n 6) 1) key_type\n    2) JSON\n    3) prefixes\n    4) 1) bicycle:\n    5) default_score\n    6) \"1\"\n 7) attributes\n 8) 1) 1) identifier\n       2) $.pickup_zone\n       3) attribute\n       4) pickup_zone\n       5) type\n       6) GEOSHAPE\n       7) coord_system\n       8) SPHERICAL\n    2) 1) identifier\n       2) $.store_location\n       3) attribute\n       4) store_location\n       5) type\n       6) GEO\n    3) 1) identifier\n       2) $.brand\n       3) attribute\n       4) brand\n       5) type\n       6) TEXT\n       7) WEIGHT\n       8) \"1\"\n    4) 1) identifier\n       2) $.model\n       3) attribute\n       4) model\n       5) type\n       6) TEXT\n       7) WEIGHT\n       8) \"1\"\n    5) 1) identifier\n       2) $.description\n       3) attribute\n       4) description\n       5) type\n       6) TEXT\n       7) WEIGHT\n       8) \"1\"\n    6) 1) identifier\n       2) $.price\n       3) attribute\n       4) price\n       5) type\n       6) NUMERIC\n    7) 1) identifier\n       2) $.condition\n       3) attribute\n       4) condition\n       5) type\n       6) TAG\n       7) SEPARATOR\n       8) ,\n 9) num_docs\n10) \"10\"\n11) max_doc_id\n12) \"10\"\n13) num_terms\n14) \"546\"\n15) num_records\n16) \"692\"\n17) inverted_sz_mb\n18) \"0.003993034362792969\"\n19) vector_index_sz_mb\n20) \"0\"\n21) total_inverted_index_blocks\n22) \"551\"\n23) offset_vectors_sz_mb\n24) \"7.047653198242188e-4\"\n25) doc_table_size_mb\n26) \"7.152557373046875e-4\"\n27) sortable_values_size_mb\n28) \"0\"\n29) key_table_size_mb\n30) \"3.0422210693359375e-4\"\n31) geoshapes_sz_mb\n32) \"0.00426483154296875\"\n33) records_per_doc_avg\n34) \"69.19999694824219\"\n35) bytes_per_record_avg\n36) \"6.0505781173706055\"\n37) offsets_per_term_avg\n38) \"1.0679190158843994\"\n39) offset_bits_per_record_avg\n40) \"8\"\n41) hash_indexing_failures\n42) \"0\"\n43) total_indexing_time\n44) \"4.539999961853027\"\n45) indexing\n46) \"0\"\n47) percent_indexed\n48) \"1\"\n49) number_of_uses\n50) (integer) 1\n51) cleaning\n52) (integer) 0\n53) gc_stats\n54)  1) bytes_collected\n     2) \"0\"\n     3) total_ms_run\n     4) \"0\"\n     5) total_cycles\n     6) \"0\"\n     7) average_cycle_time_ms\n     8) \"nan\"\n     9) last_run_time_ms\n    10) \"0\"\n    11) gc_numeric_trees_missed\n    12) \"0\"\n    13) gc_blocks_denied\n    14) \"0\"\n55) cursor_stats\n56) 1) global_idle\n    2) (integer) 0\n    3) global_total\n    4) (integer) 0\n    5) index_capacity\n    6) (integer) 128\n    7) index_total\n    8) (integer) 0\n57) dialect_stats\n58) 1) dialect_1\n    2) (integer) 0\n    3) dialect_2\n    4) (integer) 0\n    5) dialect_3\n    6) (integer) 0\n    7) dialect_4\n    8) (integer) 0\n59) Index Errors\n60) 1) indexing failures\n    2) (integer) 0\n    3) last indexing error\n    4) N/A\n    5) last indexing error key\n    6) \"N/A\"\n61) field statistics\n62) 1) 1) identifier\n       2) $.pickup_zone\n       3) attribute\n       4) pickup_zone\n       5) Index Errors\n       6) 1) indexing failures\n          2) (integer) 0\n          3) last indexing error\n          4) N/A\n          5) last indexing error key\n          6) \"N/A\"\n    2) 1) identifier\n       2) $.store_location\n       3) attribute\n       4) store_location\n       5) Index Errors\n       6) 1) indexing failures\n          2) (integer) 0\n          3) last indexing error\n          4) N/A\n          5) last indexing error key\n          6) \"N/A\"\n    3) 1) identifier\n       2) $.brand\n       3) attribute\n       4) brand\n       5) Index Errors\n       6) 1) indexing failures\n          2) (integer) 0\n          3) last indexing error\n          4) N/A\n          5) last indexing error key\n          6) \"N/A\"\n    4) 1) identifier\n       2) $.model\n       3) attribute\n       4) model\n       5) Index Errors\n       6) 1) indexing failures\n          2) (integer) 0\n          3) last indexing error\n          4) N/A\n          5) last indexing error key\n          6) \"N/A\"\n    5) 1) identifier\n       2) $.description\n       3) attribute\n       4) description\n       5) Index Errors\n       6) 1) indexing failures\n          2) (integer) 0\n          3) last indexing error\n          4) N/A\n          5) last indexing error key\n          6) \"N/A\"\n    6) 1) identifier\n       2) $.price\n       3) attribute\n       4) price\n       5) Index Errors\n       6) 1) indexing failures\n          2) (integer) 0\n          3) last indexing error\n          4) N/A\n          5) last indexing error key\n          6) \"N/A\"\n    7) 1) identifier\n       2) $.condition\n       3) attribute\n       4) condition\n       5) Index Errors\n       6) 1) indexing failures\n          2) (integer) 0\n          3) last indexing error\n          4) N/A\n          5) last indexing error key\n          6) \"N/A\"\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis Cloud<br />Flexible & Annual | Redis Cloud<br />Free & Fixed | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of key-value pairs containing index information and statistics.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: no such index.\n\n**RESP3:**\n\nOne of the following:\n* [Map](https://redis.io/docs/latest/develop/reference/protocol-spec#maps) containing index information and statistics as key-value pairs.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: no such index."},{"id":"see-also","title":"See also","role":"related","text":"[`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create) | [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) | [`FT.ALIASADD`](https://redis.io/docs/latest/commands/ft.aliasadd) | [`FT.ALIASUPDATE`](https://redis.io/docs/latest/commands/ft.aliasupdate)"},{"id":"related-topics","title":"Related topics","role":"related","text":"[RediSearch](https://redis.io/docs/latest/develop/ai/search-and-query)"}],"examples":[]}
{"schema_version":2,"id":"commands/ft.profile","title":"FT.PROFILE","url":"https://redis.io/docs/latest/commands/ft.profile/","summary":"Performs a `FT.SEARCH` or `FT.AGGREGATE` command and collects performance information","since":"2.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"3f39eea046e2a30117b64686ea9a2c7c46aba3af7d494826a5d33bd72a16f03d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Apply [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search), [`FT.HYBRID`](https://redis.io/docs/latest/commands/ft.hybrid), or [`FT.AGGREGATE`](https://redis.io/docs/latest/commands/ft.aggregate) command to collect performance details. For usage, see [Examples](#examples)."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>index</code></summary>\n\nis the name of an index created using [`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create).\n</details>\n\n<details open>\n<summary><code>SEARCH | HYBRID |  AGGREGATE</code></summary>\n\nrepresents the profile type, either [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search), [`FT.HYBRID`](https://redis.io/docs/latest/commands/ft.hybrid), or [`FT.AGGREGATE`](https://redis.io/docs/latest/commands/ft.aggregate).\n</details>\n\n<details open>\n<summary><code>LIMITED</code></summary>\n\nremoves details of any `reader` iterators.\n</details>\n\n<details open>\n<summary><code>QUERY {query}</code></summary>\n\nis the query string, sent to `FT.SEARCH`, `FT.HYBRID`, or `FT.AGGREGATE`.\n</details>\n\n<note><b>Note:</b> To reduce the size of the output, use `NOCONTENT` or `LIMIT 0 0` to reduce the number of reply results, or `LIMITED` to not reply with details of `reader iterators` inside built-in unions, such as `fuzzy` or `prefix` iterators.</note>"},{"id":"return","title":"Return","role":"returns","text":"> [!NOTE]\n> This page contains the up-to-date profile output as of RediSearch v2.8.33, v2.10.26, v8.2.7, and v8.4.3. \n> The output format itself may differ between RediSearch versions, and RESP protocol versions.\n\n`FT.PROFILE` returns a two-element array reply. The first element contains the results of the provided `FT.SEARCH`, `FT.HYBRID`, or `FT.AGGREGATE` command.\nThe second element contains information about query creation, iterator profiles, and result processor profiles.\nDetails of the second element follow in the sections below."},{"id":"per-shard-profiles","title":"Per-shard profiles","role":"content","text":"This section contains query execution details for each shard.\nWhen more than one shard is in play, the shards will be labeled `Shard #1`, `Shard #2`, etc.\nIf there's only one shard, the label will be omitted.\n\n| Returned field name      | Definition                                                                                                                                                                                                            |\n|:--                       |:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `Shard ID`               | String containing the unique shard ID. In Redis Open Source, this identifier is denoted as the node ID, and is received from the `RedisModule_GetMyClusterID()` API. (Available as of RediSearch v8.4.3)                               |\n| `Total`&nbsp;`profile`&nbsp;`time`     | The total run time (ms) of the query. Normally just a few ms.                                                                                                                                                         |\n| `Parsing`&nbsp;`time`           | The time (ms) spent parsing the query and its parameters into a query plan. Normally just a few ms.                                                                                                                   |\n| `Pipeline`&nbsp;`creation`&nbsp;`time` | The creation time (ms) of the execution plan, including iterators, result processors, and reducers creation. Normally just a few ms for `FT.SEARCH` or `FT.HYBRID` queries, but expect a larger number for `FT.AGGREGATE` queries.   |\n| `Total`&nbsp;`GIL`&nbsp;`time` | In multi-threaded deployments, the total time (ms) the query spent holding and waiting for the Redis Global Lock (referred to as GIL) during execution. Note: this value is only valid in the shards profile section. |\n| `Warning`                | Errors that occurred during query execution.                                                                                                                                                                          |\n| `Internal`&nbsp;`cursor`&nbsp;`reads` | The number of times the coordinator fetched result batches from a given shard during a distributed `AGGREGATE` query in cluster mode. Includes the initial request plus any subsequent batch fetches.                 |"},{"id":"iterator-profiles","title":"Iterator profiles","role":"content","text":"This section contains index iterator information, including `Type`, `Query Type`, `Term` (when applicable), `Time` (ms), `Number of reading operations`, `Child iterator`, and `Estimated number of matches` information.\nEach iterator represents an executor for each part of the query plan, nested per the execution plan. The operation types mentioned below (for example, `UNION`) should match up with the provided query.\n\nInverted-index iterators also include the number of elements they contain. Vector iterators include additional profiling information. See [Notes on Vector iterator profile](#notes-onvector-iterator-profile) below.\n\nIterator types include:\n\n* `INTERSECT` (and) with `Child iterator`\n* `UNION` (or) with `Child iterator`\n* `NOT` (`-`) with `Child iterator`\n* `Child iterator` - details of a sub-query component of a compound query.\n* `TEXT` with `Term`\n* `TAG` with `Term`\n* `NUMERIC` with `Term`\n* `VECTOR`\n* `METRIC - VECTOR DISTANCE`\n* `GEO` with `Term`\n* `EMPTY`\n* `WILDCARD`\n* `OPTIONAL`\n* `OPTIMIZER` with `Optimizer mode` - Query optimization wrapper for `FT.SEARCH` queries sorted by a numeric field. Enabled by default in [DIALECT 4](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/dialects##dialect-4-deprecated) or explicitly with `WITHOUTCOUNT`.\n* `ID-LIST` - Iterator over specific document IDs (appears when using `INKEYS`)\n\n**Notes on `Number of reading operations` and `Estimated number of matches`**\n\n`Number of reading operations` is the number of times an iterator was interacted with. A very high value in comparison to others is a possible warning flag. `NUMERIC` and `Child iterator` types are broken into ranges, and `Number of reading operations` will vary depending on the range. For `UNION`, the sum of the reading operations in child iterators should be equal to or greater than the child iterator's reading operations.\n\n`Estimated number of matches` is the size of the document set. `Number of reading operations` should always be equal to or less than `Estimated number of matches`.\n\n#### Notes on Vector iterator profile {#notes-onvector-iterator-profile}\n\nFor vector queries, the iterator profile includes additional information specific to vector search execution.\n\n#### Vector search mode\n\nThe `Vector search mode` field indicates the execution strategy used for both `VECTOR` and `METRIC - VECTOR DISTANCE` iterator types:\n\n| Mode | Description |\n|:--   |:--          |\n| `STANDARD_KNN`              | Pure KNN vector search without filters. |\n| `RANGE_QUERY`               | Range-based vector search. Used with `VECTOR_RANGE` queries. |\n| `HYBRID_ADHOC_BF`           | Ad-hoc brute force for filtered queries. Iterates through results that pass the filter and calculates distances on-the-fly.  |\n| `HYBRID_BATCHES`            | Batch-based filtered search. Retrieves top vectors from the index in batches and checks which ones pass the filter. |\n| `HYBRID_BATCHES_TO_ADHOC_BF`| Dynamic mode switching. Starts with batch-based search but switches to ad-hoc brute force if batch iterations yield insufficient results. |\n\n#### Batch execution statistics\n\nFor queries using batch modes (`HYBRID_BATCHES` or `HYBRID_BATCHES_TO_ADHOC_BF`), additional batch statistics are included:\n\n| Returned field name | Definition |\n|:--                  |:--         |\n| `Batches number`    | Total number of batch iterations executed during the search. |\n| `Largest batch size`| The maximum batch size used during execution. Batch sizes may vary dynamically based on the ratio of estimated matching documents to total index size. |\n| `Largest batch iteration (zero based)` | The iteration number (0-based) when the largest batch occurred. |"},{"id":"result-processor-profiles","title":"Result processor profiles","role":"returns","text":"Result processors form a powerful pipeline in Redis Search. They work in stages to gather, filter, score, sort, and return results as efficiently as possible based on complex query needs. Each processor reports `Time` information, which represents the total duration (in milliseconds, or ms) spent by the processor to complete its operation, and `Results processed` information, which indicates the number of times the processor was invoked during the query.\n\n| Type            | Definition |\n|:--              |:--         |\n| `Metrics`&nbsp;`Applier` | The `Metrics Applier` processor calculates or aggregates specific metrics related to the search results. For example, this might include applying a distance or similarity metric to vector search results, or calculating scores based on relevance or other parameters. |\n| `Index`           | The `Index` processor is responsible for the core retrieval of matching documents from the index based on the initial query criteria (e.g., full-text terms, filters, or numeric ranges). | \n| `Scorer`          | The `Scorer` processor assigns a relevance score to each document based on the query’s specified scoring function. This function could involve factors like term frequency, inverse document frequency, or other weighted metrics. |\n| `Sorter`          | The `Sorter` processor arranges the query results based on a specified sorting criterion. This could be a field value (e.g., sorting by price, date, or another attribute) or by the score assigned during the scoring phase. It operates after documents are fetched and scored, ensuring the results are ordered as required by the query (e.g., ascending or descending order). `Scorer` results will always be present in `FT.SEARCH` profiles. |\n| `Loader`          | The `Loader` processor retrieves the document contents after the results have been sorted and filtered. It ensures that only the fields specified by the query are loaded, which improves efficiency, especially when dealing with large documents where only a few fields are needed. |\n| `Threadsafe-Loader` | The `Threadsafe-Loader` processor safely loads document contents when the query runs in a background thread (relevant for multi-threaded deployments). It acquires the Redis Global Lock (referred to as GIL) to access document data. Reports an additional `GIL-Time` field representing the time (ms) spent holding the GIL and waiting to acquire it. |\n| `Highlighter`     | The `Highlighter` processor is used to highlight matching terms in the search results. This is especially useful for full-text search applications, where relevant terms are often emphasized in the UI. |\n| `Pager/Limiter`       | The `Pager/Limiter` processor is responsible for handling pagination by limiting the results to a specific range (e.g., LIMIT 0 10).It trims down the set of results to fit the required pagination window, ensuring efficient memory usage when dealing with large result sets. |\n| `Counter`         | The `Counter` processor counts the total number of matching results. Used when running queries with `LIMIT 0 0` to return only the count. |\n| `Grouper`         | The `Grouper` processor groups results by field values. Used with `GROUPBY` in `FT.AGGREGATE` queries. |\n| `Projector`       | The `Projector` processor applies field transformations. Used with `APPLY` in `FT.AGGREGATE` queries. |\n| `Network` | Collects and merges results from shards. Appears as the root processor in the coordinator's result processor chain for `FT.AGGREGATE` in cluster mode. |"},{"id":"coordinator","title":"Coordinator","role":"content","text":"This section is only present when run in a multi-shard environment.\n\n| Returned field name | Definition |\n|:--                  |:--         |\n| `Total coordinator time` | Time measured from the beginning of query execution until all shards have completed query execution. (ms) |\n| `Post-processing time`   | The time spent generating the FT.PROFILE output (overhead) (ms). |"},{"id":"examples","title":"Examples","role":"example","text":"<details>\n<summary><b>Collect performance information about a simple JSON index.</b></summary>\n\nImagine you have (1) a dataset consisting of 10 JSON documents, each with the following structure;\n\n[code example]\n\nAnd (2) a corresponding index:\n\n[code example]\n\nHere's an example of running the `FT.PROFILE` command for a compound query.\n\n\n127.0.0.1:6379> ft.profile idx:bicycle search query \"@description:(kids | small) @condition:{new | used}\"\n1) 1) (integer) 3\n   2) \"bicycle:0\"\n   3) 1) \"$\"\n      2) \"{\\\"pickup_zone\\\":\\\"POLYGON((-74.0610 40.7578, -73.9510 40.7578, -73.9510 40.6678, -74.0610 40.6678, -74.0610 40.7578))\\\",\\\"store_location\\\":\\\"-74.0060,40.7128\\\",\\\"brand\\\":\\\"Velorim\\\",\\\"model\\\":\\\"Jigger\\\",\\\"price\\\":270,\\\"description\\\":\\\"Small and powerful, the Jigger is the best ride for the smallest of tikes! This is the tiniest kids\\xe2\\x80\\x99 pedal bike on the market available without a coaster brake, the Jigger is the vehicle of choice for the rare tenacious little rider raring to go.\\\",\\\"condition\\\":\\\"new\\\"}\"\n   4) \"bicycle:1\"\n   5) 1) \"$\"\n      2) \"{\\\"pickup_zone\\\":\\\"POLYGON((-118.2887 34.0972, -118.1987 34.0972, -118.1987 33.9872, -118.2887 33.9872, -118.2887 34.0972))\\\",\\\"store_location\\\":\\\"-118.2437,34.0522\\\",\\\"brand\\\":\\\"Bicyk\\\",\\\"model\\\":\\\"Hillcraft\\\",\\\"price\\\":1200,\\\"description\\\":\\\"Kids want to ride with as little weight as possible. Especially on an incline! They may be at the age when a 27.5\\\\\\\" wheel bike is just too clumsy coming off a 24\\\\\\\" bike. The Hillcraft 26 is just the solution they need!\\\",\\\"condition\\\":\\\"used\\\"}\"\n   6) \"bicycle:2\"\n   7) 1) \"$\"\n      2) \"{\\\"pickup_zone\\\":\\\"POLYGON((-87.6848 41.9331, -87.5748 41.9331, -87.5748 41.8231, -87.6848 41.8231, -87.6848 41.9331))\\\",\\\"store_location\\\":\\\"-87.6298,41.8781\\\",\\\"brand\\\":\\\"Nord\\\",\\\"model\\\":\\\"Chook air 5\\\",\\\"price\\\":815,\\\"description\\\":\\\"The Chook Air 5  gives kids aged six years and older a durable and uberlight mountain bike for their first experience on tracks and easy cruising through forests and fields. The lower  top tube makes it easy to mount and dismount in any situation, giving your kids greater safety on the trails.\\\",\\\"condition\\\":\\\"used\\\"}\"\n2) 1) 1) Total profile time\n      2) \"0\"\n   2) 1) Parsing time\n      2) \"0\"\n   3) 1) Pipeline creation time\n      2) \"0\"\n   4) 1) Warning\n   5) 1) Iterators profile\n      2) 1) Type\n         2) INTERSECT\n         3) Time\n         4) \"0\"\n         5) Number of reading operations\n         6) (integer) 6\n         7) Child iterators\n         8)  1) Type\n             2) UNION\n             3) Query type\n             4) UNION\n             5) Time\n             6) \"0\"\n             7) Number of reading operations\n             8) (integer) 6\n             9) Child iterators\n            10)  1) Type\n                 2) UNION\n                 3) Query type\n                 4) UNION\n                 5) Time\n                 6) \"0\"\n                 7) Number of reading operations\n                 8) (integer) 4\n                 9) Child iterators\n                10)  1) Type\n                     2) TEXT\n                     3) Term\n                     4) kids\n                     5) Time\n                     6) \"0\"\n                     7) Number of reading operations\n                     8) (integer) 4\n                     9) Estimated number of matches\n                    10) (integer) 4\n                11)  1) Type\n                     2) TEXT\n                     3) Term\n                     4) +kid\n                     5) Time\n                     6) \"0\"\n                     7) Number of reading operations\n                     8) (integer) 4\n                     9) Estimated number of matches\n                    10) (integer) 4\n            11)  1) Type\n                 2) TEXT\n                 3) Term\n                 4) small\n                 5) Time\n                 6) \"0\"\n                 7) Number of reading operations\n                 8) (integer) 2\n                 9) Estimated number of matches\n                10) (integer) 2\n         9)  1) Type\n             2) UNION\n             3) Query type\n             4) TAG\n             5) Time\n             6) \"0\"\n             7) Number of reading operations\n             8) (integer) 6\n             9) Child iterators\n            10)  1) Type\n                 2) TAG\n                 3) Term\n                 4) new\n                 5) Time\n                 6) \"0\"\n                 7) Number of reading operations\n                 8) (integer) 4\n                 9) Estimated number of matches\n                10) (integer) 10\n            11)  1) Type\n                 2) TAG\n                 3) Term\n                 4) used\n                 5) Time\n                 6) \"0\"\n                 7) Number of reading operations\n                 8) (integer) 4\n                 9) Estimated number of matches\n                10) (integer) 8\n   6) 1) Result processors profile\n      2) 1) Type\n         2) Index\n         3) Time\n         4) \"0\"\n         5) Results processed\n         6) (integer) 3\n      3) 1) Type\n         2) Scorer\n         3) Time\n         4) \"0\"\n         5) Results processed\n         6) (integer) 3\n      4) 1) Type\n         2) Sorter\n         3) Time\n         4) \"0\"\n         5) Results processed\n         6) (integer) 3\n      5) 1) Type\n         2) Loader\n         3) Time\n         4) \"0\"\n         5) Results processed\n         6) (integer) 3\n\n</details>\n\n<details>\n<summary><b>Collect performance information about a JSON index that includes vector data.</b></summary>\n\nFor this example, you'll create a very simple vector database and index.\n\nIndex:\n\n[code example]\n\nDatabase:\n\n[code example]\n\nHere's an example of running the `FT.PROFILE` command for a vector query.\n\n\n127.0.0.1:6379> ft.profile vss_idx search query \"*=>[KNN 3 @vector $query_vec]\" PARAMS 2 query_vec \"\\x00\\x00\\x00@\\x00\\x00\\x00@\\x00\\x00@@\\x00\\x00@@\" SORTBY __vector_score DIALECT 2\n1) 1) (integer) 3\n   2) \"vec:2\"\n   3) 1) \"__vector_score\"\n      2) \"2\"\n      3) \"$\"\n      4) \"{\\\"vector\\\":[2,2,2,2]}\"\n   4) \"vec:3\"\n   5) 1) \"__vector_score\"\n      2) \"2\"\n      3) \"$\"\n      4) \"{\\\"vector\\\":[3,3,3,3]}\"\n   6) \"vec:1\"\n   7) 1) \"__vector_score\"\n      2) \"10\"\n      3) \"$\"\n      4) \"{\\\"vector\\\":[1,1,1,1]}\"\n2) 1) 1) Total profile time\n      2) \"0\"\n   2) 1) Parsing time\n      2) \"0\"\n   3) 1) Pipeline creation time\n      2) \"0\"\n   4) 1) Warning\n   5) 1) Iterators profile\n      2) 1) Type\n         2) VECTOR\n         3) Time\n         4) \"0\"\n         5) Number of reading operations\n         6) (integer) 3\n   6) 1) Result processors profile\n      2) 1) Type\n         2) Index\n         3) Time\n         4) \"0\"\n         5) Results processed\n         6) (integer) 3\n      3) 1) Type\n         2) Metrics Applier\n         3) Time\n         4) \"0\"\n         5) Results processed\n         6) (integer) 3\n      4) 1) Type\n         2) Sorter\n         3) Time\n         4) \"0\"\n         5) Results processed\n         6) (integer) 3\n      5) 1) Type\n         2) Loader\n         3) Time\n         4) \"0\"\n         5) Results processed\n         6) (integer) 3\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis Cloud<br />Flexible & Annual | Redis Cloud<br />Free & Fixed | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) with two elements: search results and profiling information.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: no such index, syntax error in query.\n\n**RESP3:**\n\nOne of the following:\n* [Map](https://redis.io/docs/latest/develop/reference/protocol-spec#maps) with two keys: `Results` containing search results and `Profile` containing profiling information.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: no such index, syntax error in query."},{"id":"see-also","title":"See also","role":"related","text":"[`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) | [`FT.HYBRID`](https://redis.io/docs/latest/commands/ft.hybrid) | [`FT.AGGREGATE`](https://redis.io/docs/latest/commands/ft.aggregate)"},{"id":"related-topics","title":"Related topics","role":"related","text":"- [RediSearch](https://redis.io/docs/latest/develop/ai/search-and-query)\n- [Search commands in MULTI/EXEC transactions and Lua scripts](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/transactions)"}],"examples":[{"id":"examples-ex0","language":"json","code":"{\n  \"pickup_zone\": \"POLYGON((-74.0610 40.7578, -73.9510 40.7578, -73.9510 40.6678, -74.0610 40.6678, -74.0610 40.7578))\",\n  \"store_location\": \"-74.0060,40.7128\",\n  \"brand\": \"Velorim\",\n  \"model\": \"Jigger\",\n  \"price\": 270,\n  \"description\": \"Small and powerful, the Jigger is the best ride for the smallest of tikes! This is the tiniest kids’ pedal bike on the market available without a coaster brake, the Jigger is the vehicle of choice for the rare tenacious little rider raring to go.\",\n  \"condition\": \"new\"\n}","section_id":"examples"},{"id":"examples-ex1","language":"bash","code":"FT.CREATE idx:bicycle ON JSON PREFIX 1 bicycle: SCORE 1.0 SCHEMA $.pickup_zone AS pickup_zone GEOSHAPE $.store_location AS store_location GEO $.brand AS brand TEXT WEIGHT 1.0 $.model AS model TEXT WEIGHT 1.0 $.description AS description TEXT WEIGHT 1.0 $.price AS price NUMERIC $.condition AS condition TAG SEPARATOR ,","section_id":"examples"},{"id":"examples-ex2","language":"redis","code":"FT.CREATE vss_idx ON JSON PREFIX 1 vec: SCHEMA $.vector AS vector VECTOR FLAT 6 TYPE FLOAT32 DIM 4 DISTANCE_METRIC L2","section_id":"examples"},{"id":"examples-ex3","language":"redis","code":"JSON.SET vec:1 $ '{\"vector\":[1,1,1,1]}'\nJSON.SET vec:2 $ '{\"vector\":[2,2,2,2]}'\nJSON.SET vec:3 $ '{\"vector\":[3,3,3,3]}'\nJSON.SET vec:4 $ '{\"vector\":[4,4,4,4]}'","section_id":"examples"}]}
{"schema_version":2,"id":"commands/ft.search","title":"FT.SEARCH","url":"https://redis.io/docs/latest/commands/ft.search/","summary":"Searches the index with a textual query, returning either documents or just ids","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"2568ba11954639d0163804175a0585ad2ad36c411ed608fc5d32d0fd3236cac9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Search the index with a textual query, returning either documents or just ids.\n\n> [!NOTE]\n> This command will only return keys to which the user has read access.\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>index</code></summary>\n\nis index name. You must first create the index using [`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create).\n</details>\n\n<details open>\n<summary><code>query</code></summary> \n\nis text query to search. If it's more than a single word, put it in quotes. Refer to [Query syntax](https://redis.io/docs/latest/develop/ai/search-and-query/query) for more details.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open>\n<summary><code>NOCONTENT</code></summary>\n\nreturns the document ids and not the content. This is useful if RediSearch is only an index on an external document collection.\n</details>\n\n<details open>\n<summary><code>VERBATIM</code></summary>\n\ndoes not try to use stemming for query expansion but searches the query terms verbatim.\n</details>\n\n<details open>\n<summary><code>NOSTOPWORDS (deprecated)</code></summary>\n\nignores any defined stop words in full text searches. Note: this option is deprecated as of Redis 8.0.\n</details>\n\n<details open>\n<summary><code>WITHSCORES</code></summary>\n\nalso returns the relative internal score of each document. This can be used to merge results from multiple instances.\n</details>\n\n<details open>\n<summary><code>WITHPAYLOADS</code></summary>\n\nretrieves optional document payloads. See [`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create). The payloads follow the document id and, if `WITHSCORES` is set, the scores.\n</details>\n\n<details open>\n<summary><code>WITHSORTKEYS</code></summary>\n\nreturns the value of the sorting key, right after the id and score and/or payload, if requested. This is usually not needed, and exists for distributed search coordination purposes. This option is relevant only if used in conjunction with `SORTBY`.\n</details>\n\n<details open>\n<summary><code>FILTER numeric_attribute min max</code></summary>\n\nlimits results to those having numeric values ranging between `min` and `max`, if numeric_attribute is defined as a numeric attribute in [`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create). \n  `min` and `max` follow [`ZRANGE`](https://redis.io/docs/latest/commands/zrange) syntax, and can be `-inf`, `+inf`, and use `(` for exclusive ranges. Multiple numeric filters for different attributes are supported in one query.\n**Deprecated since v2.10**: [Query dialect 2](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/dialects#dialect-2) explains the query syntax for numeric fields that replaces this argument.\n</details>\n\n<details open>\n<summary><code>GEOFILTER {geo_attribute} {lon} {lat} {radius} m|km|mi|ft</code></summary>\n\nfilter the results to a given `radius` from `lon` and `lat`. Radius is given as a number and units. See [`GEORADIUS`](https://redis.io/docs/latest/commands/georadius) for more details.\n**Deprecated since v2.6**: [Query dialect 3](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/dialects#dialect-3) explains the query syntax for geospatial fields that replaces this argument.\n</details>\n\n<details open>\n<summary><code>INKEYS {num} {attribute} ...</code></summary>\n\nlimits the result to a given set of keys specified in the list. The first argument must be the length of the list and greater than zero. Non-existent keys are ignored, unless all the keys are non-existent.\n</details>\n\n<details open>\n<summary><code>INFIELDS {num} {attribute} ...</code></summary>\n\nfilters the results to those appearing only in specific attributes of the document, like `title` or `URL`. You must include `num`, which is the number of attributes you're filtering by. For example, if you request `title` and `URL`, then `num` is 2.\n</details>\n\n<details open>\n<summary><code>RETURN {num} {identifier} AS {property} ...</code></summary>\n\nlimits the attributes returned from the document. `num` is the number of attributes following the keyword. If `num` is 0, it acts like `NOCONTENT`.\n  `identifier` is either an attribute name (for hashes and JSON) or a JSON Path expression (for JSON).\n  `property` is an optional name used in the result. If not provided, the `identifier` is used in the result.\n</details>\n\n<details open>\n<summary><code>SUMMARIZE ...</code></summary>\n\nreturns only the sections of the attribute that contain the matched text. See [Highlighting](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/highlight) for more information.\n</details>\n\n<details open>\n<summary><code>HIGHLIGHT ...</code></summary>\n\nformats occurrences of matched text. See [Highlighting](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/highlight) for more information.\n</details>\n\n<details open>\n<summary><code>SLOP {slop}</code></summary>\n\nis the number of intermediate terms allowed to appear between the terms of the query. \nSuppose you're searching for a phrase _hello world_.\nIf some terms appear in-between _hello_ and _world_, a `SLOP` greater than `0` allows for these text attributes to match.\nBy default, there is no `SLOP` constraint.\n</details>\n\n<details open>\n<summary><code>INORDER</code></summary>\n\nrequires the terms in the document to have the same order as the terms in the query, regardless of the offsets between them. Typically used in conjunction with `SLOP`. Default is `false`.\n\n</details>\n\n<details open>\n<summary><code>LANGUAGE {language}</code></summary>\n\nuse a stemmer for the supplied language during search for query expansion. If querying documents in Chinese, set to `chinese` to\n  properly tokenize the query terms. Defaults to English. If an unsupported language is sent, the command returns an error.\n  See [`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create) for the list of languages. If `LANGUAGE` was specified as part of index\n  creation, it doesn't need to specified with `FT.SEARCH`.\n</details>\n\n<details open>\n<summary><code>EXPANDER {expander}</code></summary>\n\nuses a custom query expander instead of the stemmer. See [Extensions](https://redis.io/docs/latest/develop/ai/search-and-query/administration/extensions).\n</details>\n\n<details open>\n<summary><code>SCORER {scorer}</code></summary>\n\nuses a [built-in](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/scoring) or a [user-provided](https://redis.io/docs/latest/develop/ai/search-and-query/administration/extensions) scoring function.\n</details>\n\n<details open>\n<summary><code>EXPLAINSCORE</code></summary>\n\nreturns a textual description of how the scores were calculated. Using this option requires `WITHSCORES`.\n</details>\n\n<details open>\n<summary><code>PAYLOAD {payload}</code></summary>\n\nadds an arbitrary, binary safe payload that is exposed to custom scoring functions. See [Extensions](https://redis.io/docs/latest/develop/ai/search-and-query/administration/extensions).\n</details>\n\n<details open>\n<summary><code>SORTBY {attribute} [ASC|DESC] [WITHCOUNT]</code></summary>\n\norders the results by the value of this attribute. This applies to both text and numeric attributes. Attributes needed for `SORTBY` should be declared as `SORTABLE` in the index, in order to be available with very low latency. Note that this adds memory overhead.\n\n**Sorting Optimizations**: performance is optimized for sorting operations on `DIALECT 4` in different scenarios:\n  - Skip Sorter - applied when there is no sort of any kind. The query can return after it reaches the `LIMIT` requested results.\n  - Partial Range - applied when there is a `SORTBY` clause over a numeric field, with no filter or filter by the same numeric field, the query iterate on a range large enough to satisfy the `LIMIT` requested results.\n  - Hybrid - applied when there is a `SORTBY` clause over a numeric field and another non-numeric filter. Some results will get filtered, and the initial range may not be large enough. The iterator is then rewinding with the following ranges, and an additional iteration takes place to collect the `LIMIT` requested results.\n  - No optimization - If there is a sort by score or by non-numeric field, there is no other option but to retrieve all results and compare their values.\n\n**Counts behavior**: optional `WITHCOUNT` argument returns accurate counts for the query results with sorting. This operation processes all results in order to get an accurate count, being less performant than the optimized option (default behavior on `DIALECT 4`)\n\nYou can also use `WITHOUTCOUNT` in place of `DIALECT 4` when used with either `FT.SEARCH` or `FT.AGGREGATE`.\n</details>\n\n<details open>\n<summary><code>LIMIT first num</code></summary>\n\nlimits the results to the offset and number of results given. Note that the offset is zero-indexed. The default is 0 10, which returns 10 items starting from the first result. You can use `LIMIT 0 0` to count the number of documents in the result set without actually returning them.\n\n**`LIMIT` behavior**:  If you use the `LIMIT` option without sorting, the results returned are non-deterministic, which means that subsequent queries may return duplicated or missing values. Add `SORTBY` with a unique field, or use `FT.AGGREGATE` with the `WITHCURSOR` option to ensure deterministic result set paging.\n</details>\n\n<details open>\n<summary><code>TIMEOUT {milliseconds}</code></summary>\n\noverrides the timeout parameter of the module.\n</details>\n\n<details open>\n<summary><code>PARAMS {nargs} {name} {value}</code></summary>\n\ndefines one or more value parameters. Each parameter has a name and a value. \n\nYou can reference parameters in the `query` by a `$`, followed by the parameter name, for example, `$user`. Each such reference in the search query to a parameter name is substituted by the corresponding parameter value. For example, with parameter definition `PARAMS 4 lon 29.69465 lat 34.95126`, the expression `@loc:[$lon $lat 10 km]` is evaluated to `@loc:[29.69465 34.95126 10 km]`. You cannot reference parameters in the query string where concrete values are not allowed, such as in field names, for example, `@loc`. To use `PARAMS`, set\n[`DIALECT`](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/dialects#dialect-2)\nto `2` or greater than `2` (this requires [RediSearch v2.4](https://github.com/RediSearch/RediSearch/releases/tag/v2.4.3) or above).\n</details>\n\n<details open>\n<summary><code>DIALECT {dialect_version}</code></summary>\n\nselects the dialect version under which to execute the query. If not specified, the query will execute under the default dialect version set during module initial loading or via [`FT.CONFIG SET`](https://redis.io/docs/latest/commands/ft.config-set) command. See [Query dialects](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/dialects) for more information.\n</details>"},{"id":"return","title":"Return","role":"returns","text":"FT.SEARCH returns an array reply. The first element is an integer reply that contains the total number of documents that match the query. The remaining elements are pairs: a document ID followed by an array of attribute-value pairs.\n\nThis total is not necessarily the number of documents returned in the reply. `LIMIT` controls how many matching documents follow the count. It defaults to `0 10`, so a query with more than 10 matching documents returns a count greater than the number of returned documents. Use `LIMIT 0 0` to get only the total.\n\nFor example, if 42 documents match the query, the count is 42, even though the reply returns only the first 10 documents:\n\n[code example]\n\n> [!NOTE]\n>  \n> - If you specify `NOCONTENT`, the command returns an array where the first element is the total number of matching documents and the remaining elements are document IDs.\n> - If a matching key expires or is updated while a query is running, Redis returns a null array when it tries to load the updated key's value. The key still counts as a matching document.\n> - The number of matching documents is accurate unless the query uses the sorting optimizations of `DIALECT 4` or `WITHOUTCOUNT`. These queries stop as soon as they collect the results requested by `LIMIT`, so the reported number is capped at the size of the `LIMIT` window instead of counting every matching document. Add `WITHCOUNT` to a `SORTBY` clause to get an accurate count."},{"id":"return-multiple-values","title":"Return multiple values","role":"returns","text":"When the index is defined `ON JSON`, a reply for a single attribute or a single JSONPath may return multiple values when the JSONPath matches multiple values, or when the JSONPath matches an array.\n\nPrior to RediSearch v2.6, only the first of the matched values was returned.\nStarting with RediSearch v2.6, all values are returned, wrapped with a top-level array.\n\nIn order to maintain backward compatibility, the default behavior with RediSearch v2.6 is to return only the first value.\n\nTo return all the values, use `DIALECT` 3 (or greater, when available).\n\nThe `DIALECT` can be specified as a parameter in the FT.SEARCH command. If it is not specified, the `DEFAULT_DIALECT` is used, which can be set using [`FT.CONFIG SET`](https://redis.io/docs/latest/commands/ft.config-set) or by passing it as an argument to the `redisearch` module when it is loaded.\n\nFor example, with the following document and index:\n\n[code example]\nNotice the different replies, with and without `DIALECT 3`:\n\n[code example]"},{"id":"complexity","title":"Complexity","role":"performance","text":"FT.SEARCH complexity is O(n) for single word queries. `n` is the number of the results in the result set. Finding all the documents that have a specific term is O(1), however, a scan on all those documents is needed to load the documents data from redis hashes and return them.\n\nThe time complexity for more complex queries varies, but in general it's proportional to the number of words, the number of intersection points between them and the number of results in the result set."},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Search for a term in every text attribute</b></summary>\n\nSearch for the term \"wizard\" in every TEXT attribute of an index containing book data.\n\n\n127.0.0.1:6379> FT.SEARCH books-idx \"wizard\"\n\n</details>\n\n<details open>\n<summary><b>Search for a term in title attribute</b></summary>\n\nSearch for the term _dogs_ in the `title` attribute.\n\n\n127.0.0.1:6379> FT.SEARCH books-idx \"@title:dogs\"\n\n</details>\n\n<details open>\n<summary><b>Search for books from specific years</b></summary>\n\nSearch for books published in 2020 or 2021.\n\n\n127.0.0.1:6379> FT.SEARCH books-idx \"@published_at:[2020 2021]\"\n\n</details>\n\n<details open>\n<summary><b>Search for a restaurant by distance from longitude/latitude</b></summary>\n\nSearch for Chinese restaurants within 5 kilometers of longitude -122.41, latitude 37.77 (San Francisco).\n\n\n127.0.0.1:6379> FT.SEARCH restaurants-idx \"chinese @location:[-122.41 37.77 5 km]\"\n\n</details>\n\n<details open>\n<summary><b>Search for a book by terms but boost specific term</b></summary>\n\nSearch for the term _dogs_ or _cats_ in the `title` attribute, but give matches of _dogs_ a higher relevance score (also known as _boosting_).\n\n\n127.0.0.1:6379> FT.SEARCH books-idx \"(@title:dogs | @title:cats) | (@title:dogs) => { $weight: 5.0; }\"\n\n</details>\n\n<details open>\n<summary><b>Search for a book by a term and EXPLAINSCORE</b></summary>\n\nSearch for books with _dogs_ in any TEXT attribute in the index and request an explanation of scoring for each result.\n\n\n127.0.0.1:6379> FT.SEARCH books-idx \"dogs\" WITHSCORES EXPLAINSCORE\n\n</details>\n\n<details open>\n<summary><b>Search for a book by a term and TAG</b></summary>\n\nSearch for books with _space_ in the title that have `science` in the TAG attribute `categories`.\n\n\n127.0.0.1:6379> FT.SEARCH books-idx \"@title:space @categories:{science}\"\n\n</details>\n\n<details open>\n<summary><b>Search for a book by a term but limit the number</b></summary>\n\nSearch for books with _Python_ in any `TEXT` attribute, returning 10 results starting with the 11th result in the entire result set (the offset parameter is zero-based), and return only the `title` attribute for each result.\n\n\n127.0.0.1:6379> FT.SEARCH books-idx \"python\" LIMIT 10 10 RETURN 1 title\n\n</details>\n\n<details open>\n<summary><b>Search for a book by a term and price</b></summary>\n\nSearch for books with _Python_ in any `TEXT` attribute, returning the price stored in the original JSON document.\n\n\n127.0.0.1:6379> FT.SEARCH books-idx \"python\" RETURN 3 $.book.price AS price\n\n</details>\n\n<details open>\n<summary><b>Search for a book by title and distance</b></summary>\n\nSearch for books with semantically similar title to _Planet Earth_. Return top 10 results sorted by distance.\n\n\n127.0.0.1:6379> FT.SEARCH books-idx \"*=>[KNN 10 @title_embedding $query_vec AS title_score]\" PARAMS 2 query_vec <\"Planet Earth\" embedding BLOB> SORTBY title_score DIALECT 2\n\n</details>\n\n<details open>\n<summary><b>Vector search with cluster optimization</b></summary>\n\nSearch 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](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/query_attributes) reference page for more information.\n\n\n127.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\n\n</details>\n\n<details open>\n<summary><b>Search for a phrase using SLOP</b></summary>\n\nSearch for a phrase _hello world_.\nFirst, create an index.\n\n\n127.0.0.1:6379> FT.CREATE memes SCHEMA phrase TEXT\nOK\n\n\nAdd variations of the phrase _hello world_.\n\n\n127.0.0.1:6379> HSET s1 phrase \"hello world\"\n(integer) 1\n127.0.0.1:6379> HSET s2 phrase \"hello simple world\"\n(integer) 1\n127.0.0.1:6379> HSET s3 phrase \"hello somewhat less simple world\"\n(integer) 1\n127.0.0.1:6379> HSET s4 phrase \"hello complicated yet encouraging problem solving world\"\n(integer) 1\n127.0.0.1:6379> HSET s5 phrase \"hello complicated yet amazingly encouraging problem solving world\"\n(integer) 1\n\n\nThen, search for the phrase _hello world_. The result returns all documents that contain the phrase.\n\n\n127.0.0.1:6379> FT.SEARCH memes '@phrase:(hello world)' NOCONTENT \n1) (integer) 5\n2) \"s1\"\n3) \"s2\"\n4) \"s3\"\n5) \"s4\"\n6) \"s5\"\n\n\nNow, return all documents that have one of fewer words between _hello_ and _world_.\n\n\n127.0.0.1:6379> FT.SEARCH memes '@phrase:(hello world)' NOCONTENT SLOP 1\n1) (integer) 2\n2) \"s1\"\n3) \"s2\"\n\n\nNow, return all documents with three or fewer words between _hello_ and _world_.\n\n\n127.0.0.1:6379> FT.SEARCH memes '@phrase:(hello world)' NOCONTENT SLOP 3\n1) (integer) 3\n2) \"s1\"\n3) \"s2\"\n4) \"s3\"\n\n\n`s5` needs a higher `SLOP` to match, `SLOP 6` or higher, to be exact. See what happens when you set `SLOP` to `5`.\n\n\n127.0.0.1:6379> FT.SEARCH memes '@phrase:(hello world)' NOCONTENT SLOP 5\n1) (integer) 4\n2) \"s1\"\n3) \"s2\"\n4) \"s3\"\n5) \"s4\"\n\n\nIf you add additional terms (and stemming), you get these results.\n\n\n127.0.0.1:6379> FT.SEARCH memes '@phrase:(hello amazing world)' NOCONTENT \n1) (integer) 1\n2) \"s5\"\n\n\n\n127.0.0.1:6379> FT.SEARCH memes '@phrase:(hello encouraged world)' NOCONTENT SLOP 5\n1) (integer) 2\n2) \"s4\"\n3) \"s5\"\n\n\n\n127.0.0.1:6379> FT.SEARCH memes '@phrase:(hello encouraged world)' NOCONTENT SLOP 4\n1) (integer) 1\n2) \"s4\"\n\n\nIf you swap the terms, you can still retrieve the correct phrase.\n\n\n127.0.0.1:6379> FT.SEARCH memes '@phrase:(amazing hello world)' NOCONTENT\n1) (integer) 1\n2) \"s5\"\n\n\nBut, if you use `INORDER`, you get zero results.\n\n\n127.0.0.1:6379> FT.SEARCH memes '@phrase:(amazing hello world)' NOCONTENT INORDER\n1) (integer) 0\n\n\nLikewise, if you use a query attribute `$inorder` set to `true`, `s5` is not retrieved.\n\n\n127.0.0.1:6379> FT.SEARCH memes '@phrase:(amazing hello world)=>{$inorder: true;}' NOCONTENT\n1) (integer) 0\n\n\nTo sum up, the `INORDER` argument or `$inorder` query attribute require the query terms to match terms with similar ordering.\n\n</details>\n\n<details open>\n<summary><b>Polygon Search with WITHIN, CONTAINS, INTERSECTS, and DISJOINT operators</b></summary>\n\nQuery for polygons which:\n\n- contain a given geoshape\n- are within a given geoshape\n- intersect with a given geoshape\n- are disjoint (nothing in common) with a given shape\n\n`INTERSECTS` and `DISJOINT` were introduced in v2.10.\n\nFirst, create an index using `GEOSHAPE` type with a `FLAT` coordinate system:\n\n\n127.0.0.1:6379> FT.CREATE idx SCHEMA geom GEOSHAPE FLAT\nOK\n\n\nAdding a couple of geometries using [`HSET`](https://redis.io/docs/latest/commands/hset):\n\n\n127.0.0.1:6379> HSET small geom 'POLYGON((1 1, 1 100, 100 100, 100 1, 1 1))'\n(integer) 1\n127.0.0.1:6379> HSET large geom 'POLYGON((1 1, 1 200, 200 200, 200 1, 1 1))'\n(integer) 1\n\n\nQuery with `WITHIN` operator:\n\n\n127.0.0.1:6379> FT.SEARCH idx '@geom:[WITHIN $poly]' PARAMS 2 poly 'POLYGON((0 0, 0 150, 150 150, 150 0, 0 0))' DIALECT 3\n\n1) (integer) 1\n2) \"small\"\n3) 1) \"geom\"\n   2) \"POLYGON((1 1, 1 100, 100 100, 100 1, 1 1))\"\n\n\nQuery with `CONTAINS` operator:\n\n\n127.0.0.1:6379> FT.SEARCH idx '@geom:[CONTAINS $poly]' PARAMS 2 poly 'POLYGON((2 2, 2 50, 50 50, 50 2, 2 2))' DIALECT 3\n\n1) (integer) 2\n2) \"small\"\n3) 1) \"geom\"\n   2) \"POLYGON((1 1, 1 100, 100 100, 100 1, 1 1))\"\n4) \"large\"\n5) 1) \"geom\"\n   2) \"POLYGON((1 1, 1 200, 200 200, 200 1, 1 1))\"\n\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis Cloud<br />Flexible & Annual | Redis Cloud<br />Free & Fixed | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): the first element is the total number of documents that match the query. The remaining elements are document IDs followed by their field-value pairs as [arrays](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays). The number of documents returned in the reply is limited by `LIMIT`, so it is often smaller than the total number of matching documents.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: no such index, syntax error in query.\n\n**RESP3:**\n\nOne of the following:\n* [Map](https://redis.io/docs/latest/develop/reference/protocol-spec#maps) with the following fields:\n    - `total_results`: [Integer](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) - the total number of documents that match the query, regardless of how many are returned in `results`\n    - `results`: [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [maps](https://redis.io/docs/latest/develop/reference/protocol-spec#maps) containing document information, limited by `LIMIT`\n    - `attributes`: [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of attribute names\n    - `format`: [Simple string](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) - result format\n    - `warning`: [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of warning messages\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: no such index, syntax error in query."},{"id":"see-also","title":"See also","role":"related","text":"[`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create) | [`FT.AGGREGATE`](https://redis.io/docs/latest/commands/ft.aggregate)"},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Extensions](https://redis.io/docs/latest/develop/ai/search-and-query/administration/extensions)\n- [Highlighting](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/highlight)\n- [Key and field expiration behavior](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/expiration)\n- [Query syntax](https://redis.io/docs/latest/develop/ai/search-and-query/query)\n- [RediSearch](https://redis.io/docs/latest/develop/ai/search-and-query)\n- [Search commands in MULTI/EXEC transactions and Lua scripts](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/transactions)"}],"examples":[{"id":"return-ex0","language":"sh","code":"127.0.0.1:6379> FT.SEARCH idx \"@title:redis\" NOCONTENT\n 1) (integer) 42\n 2) \"doc:1\"\n 3) \"doc:2\"\n...\n11) \"doc:10\"\n\n127.0.0.1:6379> FT.SEARCH idx \"@title:redis\" LIMIT 0 0\n1) (integer) 42","section_id":"return"},{"id":"return-multiple-values-ex0","language":"sh","code":"127.0.0.1:6379> JSON.SET doc:1 $ '[{\"arr\": [1, 2, 3]}, {\"val\": \"hello\"}, {\"val\": \"world\"}]'\nOK\n127.0.0.1:6379> FT.CREATE idx ON JSON PREFIX 1 doc: SCHEMA $..arr AS arr NUMERIC $..val AS val TEXT\nOK","section_id":"return-multiple-values"},{"id":"return-multiple-values-ex1","language":"sh","code":"127.0.0.1:6379> FT.SEARCH idx * RETURN 2 arr val\n1) (integer) 1\n2) \"doc:1\"\n3) 1) \"arr\"\n   2) \"[1,2,3]\"\n   3) \"val\"\n   4) \"hello\"\n\n127.0.0.1:6379> FT.SEARCH idx * RETURN 2 arr val DIALECT 3\n1) (integer) 1\n2) \"doc:1\"\n3) 1) \"arr\"\n   2) \"[[1,2,3]]\"\n   3) \"val\"\n   4) \"[\\\"hello\\\",\\\"world\\\"]\"","section_id":"return-multiple-values"}]}
{"schema_version":2,"id":"commands/ft.spellcheck","title":"FT.SPELLCHECK","url":"https://redis.io/docs/latest/commands/ft.spellcheck/","summary":"Performs spelling correction on a query, returning suggestions for misspelled terms","since":"1.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"883070121f128e5bdf43b5ce70002059356139d07c88b0b28d9978e4b1d757c4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Perform spelling correction on a query, returning suggestions for misspelled terms\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>index</code></summary>\n\nis index with the indexed terms.\n</details>\n\n<details open>\n<summary><code>query</code></summary> \n\nis search query.\n</details>\n\nSee [Spellchecking](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/spellcheck) for more details."},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open>\n<summary><code>TERMS</code></summary>\n\nspecifies an inclusion (`INCLUDE`) or exclusion (`EXCLUDE`) of a custom dictionary. To learn more about managing custom dictionaries, see [`FT.DICTADD`](https://redis.io/docs/latest/commands/ft.dictadd), [`FT.DICTDEL`](https://redis.io/docs/latest/commands/ft.dictdel), and [`FT.DICTDUMP`](https://redis.io/docs/latest/commands/ft.dictdump).\n\nYou can specify multiple inclusion and exclusion `TERMS`.\n</details>\n\n<details open>\n<summary><code>DISTANCE</code></summary> \n\nis maximum Levenshtein distance for spelling suggestions (default: 1, max: 4).\n</details>\n\n<details open>\n<summary><code>DIALECT {dialect_version}</code></summary> \n\nselects the dialect version under which to execute the query. If not specified, the query will execute under the default dialect version set during module initial loading or via [`FT.CONFIG SET`](https://redis.io/docs/latest/commands/ft.config-set) command.\n</details>"},{"id":"return","title":"Return","role":"returns","text":"FT.SPELLCHECK returns an array reply, in which each element represents a misspelled term from the query. The misspelled terms are ordered by their order of appearance in the query. \nEach misspelled term, in turn, is a 3-element array consisting of the constant string `TERM`, the term itself and an array of suggestions for spelling corrections.\nEach element in the spelling corrections array consists of the score of the suggestion and the suggestion itself. The suggestions array, per misspelled term, is ordered in descending order by score.\nThe score is calculated by dividing the number of documents in which the suggested term exists by the total number of documents in the index. Results can be normalized by dividing scores by the highest score."},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Perform spelling correction on a query</b></summary>\n\n\n127.0.0.1:6379> FT.SPELLCHECK idx held DISTANCE 2\n1) 1) \"TERM\"\n   2) \"held\"\n   3) 1) 1) \"0.66666666666666663\"\n         2) \"hello\"\n      2) 1) \"0.33333333333333331\"\n         2) \"help\"\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis Cloud<br />Flexible & Annual | Redis Cloud<br />Free & Fixed | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of spell check results for each term.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: no such index.\n\n**RESP3:**\n\nOne of the following:\n* [Map](https://redis.io/docs/latest/develop/reference/protocol-spec#maps) with a `results` key containing spell check results for each term.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: no such index."},{"id":"see-also","title":"See also","role":"related","text":"[`FT.CONFIG SET`](https://redis.io/docs/latest/commands/ft.config-set) | [`FT.DICTADD`](https://redis.io/docs/latest/commands/ft.dictadd) | [`FT.DICTDEL`](https://redis.io/docs/latest/commands/ft.dictdel) | [`FT.DICTDUMP`](https://redis.io/docs/latest/commands/ft.dictdump)"},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Spellchecking](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/spellcheck)\n- [RediSearch](https://redis.io/docs/latest/develop/ai/search-and-query)"}],"examples":[]}
{"schema_version":2,"id":"commands/ft.sugadd","title":"FT.SUGADD","url":"https://redis.io/docs/latest/commands/ft.sugadd/","summary":"Adds a suggestion string to an auto-complete suggestion dictionary","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"397fbb4160ca31a4bf920868dd47eb6c2cdf1255295f827b2843da7964979823","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Add a suggestion string to an auto-complete suggestion dictionary\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>key</code></summary>\n\nis suggestion dictionary key.\n</details>\n\n<details open>\n<summary><code>string</code></summary> \n\nis suggestion string to index.\n</details>\n\n<details open>\n<summary><code>score</code></summary> \n\nis floating point number of the suggestion string's weight.\n</details>\n\nThe auto-complete suggestion dictionary is disconnected from the index definitions and leaves creating and updating suggestions dictionaries to the user."},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open>\n<summary><code>INCR</code></summary> \n\nincrements the existing entry of the suggestion by the given score, instead of replacing the score. This is useful for updating the dictionary based on user queries in real time.\n</details>\n\n<details open>\n<summary><code>PAYLOAD {payload}</code></summary> \n\nsaves an extra payload with the suggestion, that can be fetched by adding the `WITHPAYLOADS` argument to [`FT.SUGGET`](https://redis.io/docs/latest/commands/ft.sugget).\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Add a suggestion string to an auto-complete suggestion dictionary</b></summary>\n\n\n127.0.0.1:6379> FT.SUGADD sug \"hello world\" 1\n(integer) 3\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | Not supported on clustered databases. |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): number of elements added to the suggestion dictionary.\n\n**RESP3:**\n\n[Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): number of elements added to the suggestion dictionary."},{"id":"see-also","title":"See also","role":"related","text":"[`FT.SUGGET`](https://redis.io/docs/latest/commands/ft.sugget) | [`FT.SUGDEL`](https://redis.io/docs/latest/commands/ft.sugdel) | [`FT.SUGLEN`](https://redis.io/docs/latest/commands/ft.suglen)"},{"id":"related-topics","title":"Related topics","role":"related","text":"[RediSearch](https://redis.io/docs/latest/develop/ai/search-and-query)"}],"examples":[]}
{"schema_version":2,"id":"commands/ft.sugdel","title":"FT.SUGDEL","url":"https://redis.io/docs/latest/commands/ft.sugdel/","summary":"Deletes a string from a suggestion index","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"a21101a8f699394ac9aa46d30608555e9a3ed501ccfae8fb35b57b4297fa3b69","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Delete a string from a suggestion index\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>key</code></summary>\n\nis suggestion dictionary key.\n</details>\n\n<details open>\n<summary><code>string</code></summary> \n\nis suggestion string to index.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Delete a string from a suggestion index</b></summary>\n\n\n127.0.0.1:6379> FT.SUGDEL sug \"hello\"\n(integer) 1\n127.0.0.1:6379> FT.SUGDEL sug \"hello\"\n(integer) 0\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | Not supported on clustered databases. |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): 1 if the suggestion was deleted, 0 if it was not found.\n\n**RESP3:**\n\n[Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): 1 if the suggestion was deleted, 0 if it was not found."},{"id":"see-also","title":"See also","role":"related","text":"[`FT.SUGGET`](https://redis.io/docs/latest/commands/ft.sugget) | [`FT.SUGADD`](https://redis.io/docs/latest/commands/ft.sugadd) | [`FT.SUGLEN`](https://redis.io/docs/latest/commands/ft.suglen)"},{"id":"related-topics","title":"Related topics","role":"related","text":"[RediSearch](https://redis.io/docs/latest/develop/ai/search-and-query)"}],"examples":[]}
{"schema_version":2,"id":"commands/ft.sugget","title":"FT.SUGGET","url":"https://redis.io/docs/latest/commands/ft.sugget/","summary":"Gets completion suggestions for a prefix","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"d62651b5d70a06ea1e7eaecd8001dae7aba7ae0ab9590ab091be70ae260d193e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Get completion suggestions for a prefix"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>key</code></summary>\n\nis suggestion dictionary key.\n</details>\n\n<details open>\n<summary><code>prefix</code></summary>\n\nis prefix to complete on.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open>\n<summary><code>FUZZY</code></summary> \n\nperforms a fuzzy prefix search, including prefixes at Levenshtein distance of 1 from the prefix sent.\n</details>\n\n<details open>\n<summary><code>MAX num</code></summary> \n\nlimits the results to a maximum of `num` (default: 5).\n</details>\n\n<details open>\n<summary><code>WITHSCORES</code></summary> \n\nalso returns the score of each suggestion. This can be used to merge results from multiple instances.\n</details>\n\n<details open>\n<summary><code>WITHPAYLOADS</code></summary> \n\nreturns optional payloads saved along with the suggestions. If no payload is present for an entry, it returns a null reply.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Get completion suggestions for a prefix</b></summary>\n\n\n127.0.0.1:6379> FT.SUGGET sug hell FUZZY MAX 3 WITHSCORES\n1) \"hell\"\n2) \"2147483648\"\n3) \"hello\"\n4) \"0.70710676908493042\"\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | Not supported on clustered databases. |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of the top suggestions matching the prefix, optionally with a score after each entry.\n\n**RESP3:**\n\n[Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of the top suggestions matching the prefix, optionally with a score after each entry."},{"id":"see-also","title":"See also","role":"related","text":"[`FT.SUGADD`](https://redis.io/docs/latest/commands/ft.sugadd) | [`FT.SUGDEL`](https://redis.io/docs/latest/commands/ft.sugdel) | [`FT.SUGLEN`](https://redis.io/docs/latest/commands/ft.suglen)"},{"id":"related-topics","title":"Related topics","role":"related","text":"[RediSearch](https://redis.io/docs/latest/develop/ai/search-and-query)"}],"examples":[]}
{"schema_version":2,"id":"commands/ft.suglen","title":"FT.SUGLEN","url":"https://redis.io/docs/latest/commands/ft.suglen/","summary":"Gets the size of an auto-complete suggestion dictionary","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"095a198cf6b0cdb5b84bea092b80df9a3714f518975280673ecb02efccb2d7d4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Get the size of an auto-complete suggestion dictionary\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>key</code></summary>\n\nis suggestion dictionary key.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Get the size of an auto-complete suggestion dictionary</b></summary>\n\n\n127.0.0.1:6379> FT.SUGLEN sug\n(integer) 2\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | Not supported on clustered databases. |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): number of suggestions in the dictionary.\n\n**RESP3:**\n\n[Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): number of suggestions in the dictionary."},{"id":"see-also","title":"See also","role":"related","text":"[`FT.SUGADD`](https://redis.io/docs/latest/commands/ft.sugadd) | [`FT.SUGDEL`](https://redis.io/docs/latest/commands/ft.sugdel) | [`FT.SUGGET`](https://redis.io/docs/latest/commands/ft.sugget)"},{"id":"related-topics","title":"Related topics","role":"related","text":"[RediSearch](https://redis.io/docs/latest/develop/ai/search-and-query)"}],"examples":[]}
{"schema_version":2,"id":"commands/ft.syndump","title":"FT.SYNDUMP","url":"https://redis.io/docs/latest/commands/ft.syndump/","summary":"Dumps the contents of a synonym group","since":"1.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"98c6b9538b46b75795f01faa8b5ea5c6985662a7c998fe1b63f8e089d7dedc34","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Dump the contents of a synonym group\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>index</code></summary>\n\nis index name.\n</details>\n\nUse FT.SYNDUMP to dump the synonyms data structure. This command returns a list of synonym terms and their synonym group ids."},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Return the contents of a synonym group</b></summary>\n\n\n127.0.0.1:6379> FT.SYNDUMP idx\n1) \"shalom\"\n2) 1) \"synonym1\"\n   2) \"synonym2\"\n3) \"hi\"\n4) 1) \"synonym1\"\n5) \"hello\"\n6) 1) \"synonym1\"\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis Cloud<br />Flexible & Annual | Redis Cloud<br />Free & Fixed | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of synonym terms and their associated synonym groups.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: no such index.\n\n**RESP3:**\n\nOne of the following:\n* [Map](https://redis.io/docs/latest/develop/reference/protocol-spec#maps) where keys are synonym terms and values are arrays of their associated synonym groups.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: no such index."},{"id":"see-also","title":"See also","role":"related","text":"[`FT.SYNUPDATE`](https://redis.io/docs/latest/commands/ft.synupdate)"},{"id":"related-topics","title":"Related topics","role":"related","text":"[RediSearch](https://redis.io/docs/latest/develop/ai/search-and-query)"}],"examples":[]}
{"schema_version":2,"id":"commands/ft.synupdate","title":"FT.SYNUPDATE","url":"https://redis.io/docs/latest/commands/ft.synupdate/","summary":"Creates or updates a synonym group with additional terms","since":"1.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"0c4a7d6508b3cf05ab913cea53ed6e365a2a857a42b6fee367a2ff5afa78497e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Update a synonym group\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>index</code></summary>\n\nis index name.\n</details>\n\n<details open>\n<summary><code>synonym_group_id</code></summary>\n\nis synonym group to return.\n</details>\n\nUse FT.SYNUPDATE to create or update a synonym group with additional terms. The command triggers a scan of all documents."},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open>\n<summary><code>SKIPINITIALSCAN</code></summary>\n\ndoes not scan and index, and only documents that are indexed after the update are affected.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Update a synonym group</b></summary>\n\n\n127.0.0.1:6379> FT.SYNUPDATE idx synonym hello hi shalom\nOK\n\n\n\n127.0.0.1:6379> FT.SYNUPDATE idx synonym SKIPINITIALSCAN hello hi shalom\nOK\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis Cloud<br />Flexible & Annual | Redis Cloud<br />Free & Fixed | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: no such index.\n\n**RESP3:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: no such index."},{"id":"see-also","title":"See also","role":"related","text":"[`FT.SYNDUMP`](https://redis.io/docs/latest/commands/ft.syndump)"},{"id":"related-topics","title":"Related topics","role":"related","text":"[RediSearch](https://redis.io/docs/latest/develop/ai/search-and-query)"}],"examples":[]}
{"schema_version":2,"id":"commands/ft.tagvals","title":"FT.TAGVALS","url":"https://redis.io/docs/latest/commands/ft.tagvals/","summary":"Returns the distinct tags indexed in a Tag field","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"1c5f1f49f043b211c56ea2c889a4dee51b1edcf359ce111e0673a543d8349d27","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Return a distinct set of values indexed in a Tag field\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>index</code></summary>\n\nis full-text index name. You must first create the index using [`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create).\n</details>\n\n<details open>\n<summary><code>field_name</code></summary>\n\nis name of a Tag file defined in the schema.\n</details>\n\nUse FT.TAGVALS if your tag indexes things like cities, categories, and so on."},{"id":"limitations","title":"Limitations","role":"limits","text":"FT.TAGVALS provides no paging or sorting, and the tags are not alphabetically sorted. FT.TAGVALS only operates on [tag fields](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/tags).\nThe returned strings are lowercase with whitespaces removed, but otherwise unchanged."},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Return a set of values indexed in a Tag field</b></summary>\n\n\n127.0.0.1:6379> FT.TAGVALS idx myTag\n1) \"Hello\"\n2) \"World\"\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis Cloud<br />Flexible & Annual | Redis Cloud<br />Free & Fixed | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of distinct tag values as [bulk strings](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings).\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: no such index, not a tag field.\n\n**RESP3:**\n\nOne of the following:\n* [Set](https://redis.io/docs/latest/develop/reference/protocol-spec#sets) of distinct tag values as [bulk strings](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings).\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: no such index, not a tag field."},{"id":"see-also","title":"See also","role":"related","text":"[`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create)"},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Tag fields](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/tags)\n- [RediSearch](https://redis.io/docs/latest/develop/ai/search-and-query)"}],"examples":[]}
{"schema_version":2,"id":"commands/function","title":"FUNCTION","url":"https://redis.io/docs/latest/commands/function/","summary":"A container for function commands.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"ef335f3baba08d023eb352ea1e5ed787cacc6a25dc96c3830af589981e92b8a2","sections":[{"id":"content","title":"Content","role":"content","text":"This is a container command for function commands.\n\nTo see the list of available commands you can call [`FUNCTION HELP`](https://redis.io/docs/latest/commands/function-help)."}],"examples":[]}
{"schema_version":2,"id":"commands/function-delete","title":"FUNCTION DELETE","url":"https://redis.io/docs/latest/commands/function-delete/","summary":"Deletes a library and its functions.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"cf2b017f29f66310c978924ef17ef3a82101a5812cd9a839aac279c6c1733db3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Delete a library and all its functions.\n\nThis command deletes the library called `library-name` and all functions in it.\nIf the library doesn't exist, the server returns an error.\n\nFor more information please refer to [Introduction to Redis Functions](https://redis.io/docs/latest/develop/programmability/functions-intro)."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>library-name</code></summary>\n\nThe name of the library to delete.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> FUNCTION LOAD \"#!lua name=mylib \\n redis.register_function('myfunc', function(keys, args) return 'hello' end)\"\n\"mylib\"\nredis> FCALL myfunc 0\n\"hello\"\nredis> FUNCTION DELETE mylib\nOK\nredis> FCALL myfunc 0\n(error) ERR Function not found","section_id":"examples"}]}
{"schema_version":2,"id":"commands/function-dump","title":"FUNCTION DUMP","url":"https://redis.io/docs/latest/commands/function-dump/","summary":"Dumps all libraries into a serialized binary payload.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"105534925c98410b053d6e0e3244bfb707c355c5cde2377e5b8f1a4622bcb5f2","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Return the serialized payload of loaded libraries.\nYou can restore the serialized payload later with the [`FUNCTION RESTORE`](https://redis.io/docs/latest/commands/function-restore) command.\n\nFor more information please refer to [Introduction to Redis Functions](https://redis.io/docs/latest/develop/programmability/functions-intro)."},{"id":"examples","title":"Examples","role":"example","text":"The following example shows how to dump loaded libraries using `FUNCTION DUMP` and then it calls [`FUNCTION FLUSH`](https://redis.io/docs/latest/commands/function-flush) deletes all the libraries.\nThen, it restores the original libraries from the serialized payload with [`FUNCTION RESTORE`](https://redis.io/docs/latest/commands/function-restore).\n\n[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the serialized payload\n\n**RESP3:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the serialized payload"}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> FUNCTION LOAD \"#!lua name=mylib \\n redis.register_function('myfunc', function(keys, args) return args[1] end)\"\n\"mylib\"\nredis> FUNCTION DUMP\n\"\\xf5\\xc3@X@]\\x1f#!lua name=mylib \\n redis.registe\\rr_function('my@\\x0b\\x02', @\\x06`\\x12\\nkeys, args) 6\\x03turn`\\x0c\\a[1] end)\\x0c\\x00\\xba\\x98\\xc2\\xa2\\x13\\x0e$\\a\"\nredis> FUNCTION FLUSH\nOK\nredis> FUNCTION RESTORE \"\\xf5\\xc3@X@]\\x1f#!lua name=mylib \\n redis.registe\\rr_function('my@\\x0b\\x02', @\\x06`\\x12\\nkeys, args) 6\\x03turn`\\x0c\\a[1] end)\\x0c\\x00\\xba\\x98\\xc2\\xa2\\x13\\x0e$\\a\"\nOK\nredis> FUNCTION LIST\n1) 1) \"library_name\"\n   2) \"mylib\"\n   3) \"engine\"\n   4) \"LUA\"\n   5) \"functions\"\n   6) 1) 1) \"name\"\n         2) \"myfunc\"\n         3) \"description\"\n         4) (nil)\n         5) \"flags\"\n         6) (empty array)","section_id":"examples"}]}
{"schema_version":2,"id":"commands/function-flush","title":"FUNCTION FLUSH","url":"https://redis.io/docs/latest/commands/function-flush/","summary":"Deletes all libraries and functions.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"4de7d3ff0234511ecc7c0f0200f6c6c98bc31f5005eb423198999ef6ef1ac709","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Deletes all the libraries.\n\nUnless called with the optional mode argument, the `lazyfree-lazy-user-flush` configuration directive sets the effective behavior.\n\nFor more information please refer to [Introduction to Redis Functions](https://redis.io/docs/latest/develop/programmability/functions-intro)."},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>ASYNC | SYNC</code></summary>\n\nFlush the function libraries asynchronously (`ASYNC`) or synchronously (`SYNC`). The default is set by the `lazyfree-lazy-user-flush` configuration directive.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[]}
{"schema_version":2,"id":"commands/function-help","title":"FUNCTION HELP","url":"https://redis.io/docs/latest/commands/function-help/","summary":"Returns helpful text about the different subcommands.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"7a2ca9867367a5d9d51da945027406048d9163c7013e3ddec68f2cbad27a2fc4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `FUNCTION HELP` command returns a helpful text describing the different subcommands."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of sub-commands and their descriptions\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of sub-commands and their descriptions."}],"examples":[]}
{"schema_version":2,"id":"commands/function-kill","title":"FUNCTION KILL","url":"https://redis.io/docs/latest/commands/function-kill/","summary":"Terminates a function during execution.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"a62629976d732e0cbf09f2fc3d30c6bfb2436838549814ee56cee7495b8b1402","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Kill a function that is currently executing.\n\nThe `FUNCTION KILL` command can be used only on functions that did not modify the dataset during their execution (since stopping a read-only function does not violate the scripting engine's guaranteed atomicity).\n\nFor more information please refer to [Introduction to Redis Functions](https://redis.io/docs/latest/develop/programmability/functions-intro)."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[]}
{"schema_version":2,"id":"commands/function-list","title":"FUNCTION LIST","url":"https://redis.io/docs/latest/commands/function-list/","summary":"Returns information about all libraries.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"29c3584d32b3d1e86f20af1d55e711fd05b9062106a0b98f63b740d7126c5184","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Return information about loaded functions and libraries.\n\nFor more information see [Introduction to Redis Functions](https://redis.io/docs/latest/develop/programmability/functions-intro)."},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>LIBRARYNAME library-name-pattern</code></summary>\n\nList only libraries whose names match the given pattern.\n\n</details>\n\n<details open><summary><code>WITHCODE</code></summary>\n\nInclude the source code of each library in the reply.\n\n</details>"},{"id":"details","title":"Details","role":"content","text":"The following information is provided for each of the libraries in the response:\n\n* `library_name:` the name of the library.\n* `engine:` the engine of the library.\n* `functions:` the list of functions in the library.\n  Each function has the following fields:\n  * `name:` the name of the function.\n  * `description:` the function's description.\n  * `flags:` an array of [function flags](https://redis.io/docs/latest/develop/programmability/functions-intro#function-flags).\n* `library_code:` the library's source code (when given the `WITHCODE` modifier)."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): information about loaded functions and libraries.\n\n**RESP3:**\n\n[Map reply](../../develop/reference/protocol-spec#maps): information about loaded functions and libraries."}],"examples":[]}
{"schema_version":2,"id":"commands/function-load","title":"FUNCTION LOAD","url":"https://redis.io/docs/latest/commands/function-load/","summary":"Creates a library.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"8bcba61c5a26e7df3e86faf18ab7be272d57fd09cf876a647f4f28707ab67c65","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Load a library to Redis.\n\nThe command takes one required parameter: the source code that implements the library. The library payload must start with a _shebang_ statement that provides the library metadata, including the engine to use and the library name.\n\nUse this shebang format:\n\n`#!<engine name> name=<library name>`.\n\nCurrently, `<engine name>` must be `lua`.\n\nFor the Lua engine, the implementation should declare one or more entry points to the library with the [`redis.register_function()`](https://redis.io/docs/latest/develop/programmability/lua-api#redis.register_function) API.\nOnce loaded, you can call the functions in the library with the [`FCALL`](https://redis.io/docs/latest/commands/fcall) or [`FCALL_RO`](https://redis.io/docs/latest/commands/fcall_ro) commands, as appropriate.\n\n\n\nFor more information please refer to [Introduction to Redis Functions](https://redis.io/docs/latest/develop/programmability/functions-intro)."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>function-code</code></summary>\n\nThe library's source code. It must begin with a Shebang statement that declares the engine and library name, for example `#!lua name=mylib`.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>REPLACE</code></summary>\n\nReplace an existing library that has the same name.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"The following example will create a library named `mylib` with a single function, `myfunc`, that returns the first argument it gets.\n\n[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n- [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings): the library name that was loaded.\n- [Simple error string](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in the following circumstances: an invalid engine-name was provided, \nthe library's name already exists without the `REPLACE` modifier, \na function in the library is created with a name that already exists in another library (even when `REPLACE` is specified), \nthe engine failed in creating the library's functions (for example, because of a compilation error), or \nno functions were declared by the library.\n\n**RESP3:**\n\nOne of the following:\n\n- [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings): the library name that was loaded.\n- [Simple error string](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in the following circumstances: an invalid engine-name was provided, \nthe library's name already exists without the `REPLACE` modifier, \na function in the library is created with a name that already exists in another library (even when `REPLACE` is specified), \nthe engine failed in creating the library's functions (for example, because of a compilation error), or \nno functions were declared by the library."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> FUNCTION LOAD \"#!lua name=mylib \\n redis.register_function('myfunc', function(keys, args) return args[1] end)\"\nmylib\nredis> FCALL myfunc 0 hello\n\"hello\"","section_id":"examples"}]}
{"schema_version":2,"id":"commands/function-restore","title":"FUNCTION RESTORE","url":"https://redis.io/docs/latest/commands/function-restore/","summary":"Restores all libraries from a payload.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"1c835308e1ba5a06da4b69e431650ed77e7720c97b06d95b43b06b18954b00f4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Restore libraries from serialized payload.\n\nYou can use the optional [policy argument](#optional-arguments) to provide a policy for handling existing libraries.\n\n\nFor more information see [Introduction to Redis Functions](https://redis.io/docs/latest/develop/programmability/functions-intro)."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>serialized-value</code></summary>\n\nThe payload produced by [`FUNCTION DUMP`](https://redis.io/docs/latest/commands/function-dump).\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>FLUSH | APPEND | REPLACE</code></summary>\n\nPolicies for handling existing libraries:\n\n* `APPEND:` appends the restored libraries to the existing libraries and aborts on collision. \n  This is the default policy.\n* `FLUSH:` deletes all existing libraries before restoring the payload.\n* `REPLACE:` appends the restored libraries to the existing libraries, replacing any existing ones in case of name collisions. Note that this policy doesn't prevent function name collisions, only libraries.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[]}
{"schema_version":2,"id":"commands/function-stats","title":"FUNCTION STATS","url":"https://redis.io/docs/latest/commands/function-stats/","summary":"Returns information about a function during execution.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"8c98e4b6f5034bc74f39e097bd6b5aaa994d891e8a69fc1b209ddc4dc161a0f0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Return information about the function that's currently running and information about the available execution engines.\n\nThe reply is map with two keys:\n\n1. `running_script`: information about the running script.\n  If there's no in-flight function, the server replies with a _nil_.\n  Otherwise, this is a map with the following keys:\n  * **name:** the name of the function.\n  * **command:** the command and arguments used for invoking the function.\n  * **duration_ms:** the function's runtime duration in milliseconds.\n2. `engines`: this is a map of maps. Each entry in the map represent a single engine.\n   Engine map contains statistics about the engine like number of functions and number of libraries.\n\nYou can use this command to inspect the invocation of a long-running function and decide whether kill it with the [`FUNCTION KILL`](https://redis.io/docs/latest/commands/function-kill) command.\n\nFor more information please refer to [Introduction to Redis Functions](https://redis.io/docs/latest/develop/programmability/functions-intro)."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): information about the function that's currently running and information about the available execution engines.\n\n**RESP3:**\n\n[Map reply](../../develop/reference/protocol-spec#maps): information about the function that's currently running and information about the available execution engines."}],"examples":[]}
{"schema_version":2,"id":"commands/geoadd","title":"GEOADD","url":"https://redis.io/docs/latest/commands/geoadd/","summary":"Adds one or more members to a geospatial index. The key is created if it doesn't exist.","since":"3.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:43+01:00","page_type":"content","content_hash":"61f8686377811fd950bb0c2f6fcf8cf41b2f2d5a5457759c47a0551b0db7bf14","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Adds the specified geospatial items (longitude, latitude, name) to the specified key. Data is stored into the key as a sorted set, in a way that makes it possible to query the items with the [`GEOSEARCH`](https://redis.io/docs/latest/commands/geosearch) command.\n\nThe command takes arguments in the standard format x,y so the longitude must be specified before the latitude. There are limits to the coordinates that can be indexed: areas very near to the poles are not indexable.\n\nThe exact limits, as specified by EPSG:900913 / EPSG:3785 / OSGEO:41001 are the following:\n\n* Valid longitudes are from -180 to 180 degrees.\n* Valid latitudes are from -85.05112878 to 85.05112878 degrees.\n\nThe command will report an error when the user attempts to index coordinates outside the specified ranges.\n\n**Note:** there is no **GEODEL** command because you can use [`ZREM`](https://redis.io/docs/latest/commands/zrem) to remove elements. The Geo index structure is just a sorted set."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the geospatial index (a sorted set).\n\n</details>\n\n<details open><summary><code>longitude latitude member [longitude latitude member ...]</code></summary>\n\nOne or more longitude-latitude-member triples to add to the index.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"`NX` and `XX` are mutually exclusive.\n\n<details open><summary><code>NX</code></summary>\n\nDon't update already existing elements. Always add new elements.\n\n</details>\n\n<details open><summary><code>XX</code></summary>\n\nOnly update elements that already exist. Never add elements.\n\n</details>\n\n<details open><summary><code>CH</code></summary>\n\nModify the return value from the number of new elements added, to the total number of elements changed (`CH` is an abbreviation of *changed*). Changed elements are **new elements added** and elements already existing for which **the coordinates was updated**. So elements specified in the command line having the same score as they had in the past are not counted. Note: normally, the return value of `GEOADD` only counts the number of new elements added.\n\n</details>"},{"id":"details","title":"Details","role":"content","text":""},{"id":"how-it-works","title":"How it works","role":"content","text":"The way the sorted set is populated is using a technique called\n[Geohash](https://en.wikipedia.org/wiki/Geohash). Latitude and Longitude\nbits are interleaved to form a unique 52-bit integer. We know\nthat a sorted set double score can represent a 52-bit integer without losing\nprecision.\n\nThis format allows for bounding box and radius querying by checking the 1+8 areas needed to cover the whole shape and discarding elements outside it. The areas are checked by calculating the range of the box covered, removing enough bits from the less significant part of the sorted set score, and computing the score range to query in the sorted set for each area."},{"id":"what-earth-model-does-it-use","title":"What Earth model does it use?","role":"content","text":"The model assumes that the Earth is a sphere since it uses the Haversine formula to calculate distance. This formula is only an approximation when applied to the Earth, which is not a perfect sphere.\nThe introduced errors are not an issue when used, for example, by social networks and similar applications requiring this type of querying. \nHowever, in the worst case, the error may be up to 0.5%, so you may want to consider other systems for error-critical applications."},{"id":"examples","title":"Examples","role":"example","text":"redis> GEOADD Sicily 13.361389 38.115556 \"Palermo\" 15.087269 37.502669 \"Catania\"\n(integer) 2\nredis> GEODIST Sicily Palermo Catania\n\"166274.1516\"\nredis> GEORADIUS Sicily 15 37 100 km\n1) \"Catania\"\nredis> GEORADIUS Sicily 15 37 200 km\n1) \"Palermo\"\n2) \"Catania\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): When used without optional arguments, the number of elements added to the sorted set (excluding score updates).  If the CH option is specified, the number of elements that were changed (added or updated).\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): When used without optional arguments, the number of elements added to the sorted set (excluding score updates).  If the CH option is specified, the number of elements that were changed (added or updated)."}],"examples":[]}
{"schema_version":2,"id":"commands/geodist","title":"GEODIST","url":"https://redis.io/docs/latest/commands/geodist/","summary":"Returns the distance between two members of a geospatial index.","since":"3.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:43+01:00","page_type":"content","content_hash":"71fe7ab859adee378e762a91b8fd4c2ef5ab80375ab46fc3a68f2a3b52897994","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Return the distance between two members in the geospatial index represented by the sorted set.\n\nGiven a sorted set representing a geospatial index, populated using the [`GEOADD`](https://redis.io/docs/latest/commands/geoadd) command, the command returns the distance between the two specified members in the specified unit.\n\nIf one or both the members are missing, the command returns NULL.\n\nThe unit must be one of the following, and defaults to meters:\n\n* **m** for meters.\n* **km** for kilometers.\n* **mi** for miles.\n* **ft** for feet.\n\nThe distance is computed assuming that the Earth is a perfect sphere, so errors up to 0.5% are possible in edge cases."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the geospatial index (a sorted set).\n\n</details>\n\n<details open><summary><code>member1</code></summary>\n\nThe first member.\n\n</details>\n\n<details open><summary><code>member2</code></summary>\n\nThe second member.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>M | KM | FT | MI</code></summary>\n\nThe unit for the returned distance: meters (`M`, the default), kilometers (`KM`), feet (`FT`), or miles (`MI`).\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> GEOADD Sicily 13.361389 38.115556 \"Palermo\" 15.087269 37.502669 \"Catania\"\n(integer) 2\nredis> GEODIST Sicily Palermo Catania\n\"166274.1516\"\nredis> GEODIST Sicily Palermo Catania km\n\"166.2742\"\nredis> GEODIST Sicily Palermo Catania mi\n\"103.3182\"\nredis> GEODIST Sicily Foo Bar\n(nil)"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): one or both of the elements are missing.\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): distance as a double (represented as a string) in the specified units.\n\n**RESP3:**\n\nOne of the following:\n* [Null reply](../../develop/reference/protocol-spec#nulls): one or both of the elements are missing.\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): distance as a double (represented as a string) in the specified units."}],"examples":[]}
{"schema_version":2,"id":"commands/geohash","title":"GEOHASH","url":"https://redis.io/docs/latest/commands/geohash/","summary":"Returns members from a geospatial index as geohash strings.","since":"3.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:43+01:00","page_type":"content","content_hash":"bcd6047cdc7f85a452bb962b05783a25021195542b3fa08ad205c9de879bd5ee","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Return valid [Geohash](https://en.wikipedia.org/wiki/Geohash) strings representing the position of one or more elements in a sorted set value representing a geospatial index (where elements were added using [`GEOADD`](https://redis.io/docs/latest/commands/geoadd)).\n\nNormally Redis represents positions of elements using a variation of the Geohash\ntechnique where positions are encoded using 52-bit integers. The encoding is\nalso different compared to the standard because the initial minimum and maximum\ncoordinates used during the encoding and decoding process are different. This\ncommand returns a standard Geohash in the form of a string as\ndescribed in the [Wikipedia article](https://en.wikipedia.org/wiki/Geohash)."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the geospatial index (a sorted set).\n\n</details>\n\n<details open><summary><code>member [member ...]</code></summary>\n\nOne or more members whose Geohash strings to return.\n\n</details>"},{"id":"details","title":"Details","role":"content","text":""},{"id":"geohash-string-properties","title":"Geohash string properties","role":"content","text":"The command returns 11 character Geohash strings, so no precision is lost\ncompared to the Redis internal 52-bit representation. The returned Geohashes\nhave the following properties:\n\n1. They can be shortened by removing characters from the right. They will lose precision but will still point to the same area.\n2. Strings with a similar prefix are nearby, but the contrary is not true, it is possible that strings with different prefixes are nearby too."},{"id":"examples","title":"Examples","role":"example","text":"redis> GEOADD Sicily 13.361389 38.115556 \"Palermo\" 15.087269 37.502669 \"Catania\"\n(integer) 2\nredis> GEOHASH Sicily Palermo Catania\n1) \"sqc8b49rny0\"\n2) \"sqdtr74hyu0\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): an array where each element is the Geohash corresponding to each member name passed as an argument to the command.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): An array where each element is the Geohash corresponding to each member name passed as an argument to the command."}],"examples":[]}
{"schema_version":2,"id":"commands/geopos","title":"GEOPOS","url":"https://redis.io/docs/latest/commands/geopos/","summary":"Returns the longitude and latitude of members from a geospatial index.","since":"3.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:43+01:00","page_type":"content","content_hash":"5b87871e09fc1f39a56bc5f9bc0dfd8c3bebed14d2682b5c2e4cbeb0ddc2197a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Return the positions (longitude,latitude) of all the specified members of the geospatial index represented by the sorted set at *key*.\n\nGiven a sorted set representing a geospatial index, populated using the [`GEOADD`](https://redis.io/docs/latest/commands/geoadd) command, it is often useful to obtain back the coordinates of specified members. When the geospatial index is populated via [`GEOADD`](https://redis.io/docs/latest/commands/geoadd) the coordinates are converted into a 52 bit geohash, so the coordinates returned may not be exactly the ones used in order to add the elements, but small errors may be introduced.\n\nThe command can accept a variable number of arguments so it always returns an array of positions even when a single element is specified."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the geospatial index (a sorted set).\n\n</details>\n\n<details open><summary><code>member [member ...]</code></summary>\n\nOne or more members whose longitude and latitude to return.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> GEOADD Sicily 13.361389 38.115556 \"Palermo\" 15.087269 37.502669 \"Catania\"\n(integer) 2\nredis> GEOPOS Sicily Palermo Catania NonExisting\n1) 1) \"13.361389338970184\"\n   2) \"38.1155563954963\"\n2) 1) \"15.087267458438873\"\n   2) \"37.50266842333162\"\n3) (nil)"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): An array where each element is a two elements array representing longitude and latitude (x,y) of each member name passed as argument to the command. Non-existing elements are reported as [Nil reply](../../develop/reference/protocol-spec#bulk-strings) elements of the array.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): An array where each element is a two elements array representing longitude and latitude (x,y) of each member name passed as argument to the command. Non-existing elements are reported as [Null reply](../../develop/reference/protocol-spec#nulls) elements of the array."}],"examples":[]}
{"schema_version":2,"id":"commands/georadius","title":"GEORADIUS","url":"https://redis.io/docs/latest/commands/georadius/","summary":"Queries a geospatial index for members within a distance from a coordinate, optionally stores the result.","since":"3.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:43+01:00","page_type":"content","content_hash":"f349ce70952b129d58b4319d6aa25da52fe38bb733ce8d1a5ebeaf6523858a68","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nReturn the members of a sorted set populated with geospatial information using [`GEOADD`](https://redis.io/docs/latest/commands/geoadd), which are within the borders of the area specified with the center location and the maximum distance from the center (the radius).\n\nThis manual page also covers the [`GEORADIUS_RO`](https://redis.io/docs/latest/commands/georadius_ro) and [`GEORADIUSBYMEMBER_RO`](https://redis.io/docs/latest/commands/georadiusbymember_ro) variants (see the section below for more information).\n\nThe common use case for this command is to retrieve geospatial items near a specified point not farther than a given amount of meters (or other units). This allows, for example, to suggest mobile users of an application nearby places.\n\nThe radius is specified in one of the following units:\n\n* **m** for meters.\n* **km** for kilometers.\n* **mi** for miles.\n* **ft** for feet.\n\nThe command optionally returns additional information using the following options:\n\n* `WITHDIST`: Also return the distance of the returned items from the specified center. The distance is returned in the same unit as the unit specified as the radius argument of the command.\n* `WITHCOORD`: Also return the longitude,latitude coordinates of the matching items.\n* `WITHHASH`: Also return the raw geohash-encoded sorted set score of the item, in the form of a 52 bit unsigned integer. This is only useful for low level hacks or debugging and is otherwise of little interest for the general user.\n\nThe command default is to return unsorted items. Two different sorting methods can be invoked using the following two options:\n\n* `ASC`: Sort returned items from the nearest to the farthest, relative to the center.\n* `DESC`: Sort returned items from the farthest to the nearest, relative to the center.\n\nBy default all the matching items are returned. It is possible to limit the results to the first N matching items by using the **COUNT `<count>`** option.\nWhen `ANY` is provided the command will return as soon as enough matches are found,\nso the results may not be the ones closest to the specified point, but on the other hand, the effort invested by the server is significantly lower.\nWhen `ANY` is not provided, the command will perform an effort that is proportional to the number of items matching the specified area and sort them,\nso to query very large areas with a very small `COUNT` option may be slow even if just a few results are returned.\n\nBy default the command returns the items to the client. It is possible to store the results with one of these options:\n\n* `STORE`: Store the items in a sorted set populated with their geospatial information.\n* `STOREDIST`: Store the items in a sorted set populated with their distance from the center as a floating point number, in the same unit specified in the radius."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the geospatial index (a sorted set).\n\n</details>\n\n<details open><summary><code>longitude</code></summary>\n\nThe longitude of the center point.\n\n</details>\n\n<details open><summary><code>latitude</code></summary>\n\nThe latitude of the center point.\n\n</details>\n\n<details open><summary><code>radius</code></summary>\n\nThe radius of the search circle, in the unit given by the following argument.\n\n</details>\n\n<details open><summary><code>M | KM | FT | MI</code></summary>\n\nThe unit for `radius`: meters (`M`), kilometers (`KM`), feet (`FT`), or miles (`MI`).\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>WITHCOORD</code></summary>\n\nAlso return the longitude and latitude of each matching item.\n\n</details>\n\n<details open><summary><code>WITHDIST</code></summary>\n\nAlso return the distance of each matching item from the center, in the same unit as the radius.\n\n</details>\n\n<details open><summary><code>WITHHASH</code></summary>\n\nAlso return the raw 52-bit geohash-encoded score of each matching item.\n\n</details>\n\n<details open><summary><code>COUNT count [ANY]</code></summary>\n\nReturn at most `count` matches. With `ANY`, the command returns as soon as enough matches are found — faster, but the results may be unsorted.\n\n</details>\n\n<details open><summary><code>ASC | DESC</code></summary>\n\nSort the results by distance from the center: nearest first (`ASC`) or farthest first (`DESC`).\n\n</details>\n\n<details open><summary><code>STORE key</code></summary>\n\nStore the results as a geospatial index in `key` instead of returning them. `STORE` and `STOREDIST` are mutually exclusive.\n\n</details>\n\n<details open><summary><code>STOREDIST key</code></summary>\n\nStore the results in `key` as a sorted set of distances from the center, instead of returning them.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> GEOADD Sicily 13.361389 38.115556 \"Palermo\" 15.087269 37.502669 \"Catania\"\n(integer) 2\nredis> GEORADIUS Sicily 15 37 200 km WITHDIST\n1) 1) \"Palermo\"\n   2) \"190.4424\"\n2) 1) \"Catania\"\n   2) \"56.4413\"\nredis> GEORADIUS Sicily 15 37 200 km WITHCOORD\n1) 1) \"Palermo\"\n   2) 1) \"13.361389338970184\"\n      2) \"38.1155563954963\"\n2) 1) \"Catania\"\n   2) 1) \"15.087267458438873\"\n      2) \"37.50266842333162\"\nredis> GEORADIUS Sicily 15 37 200 km WITHDIST WITHCOORD\n1) 1) \"Palermo\"\n   2) \"190.4424\"\n   3) 1) \"13.361389338970184\"\n      2) \"38.1155563954963\"\n2) 1) \"Catania\"\n   2) \"56.4413\"\n   3) 1) \"15.087267458438873\"\n      2) \"37.50266842333162\""},{"id":"details","title":"Details","role":"content","text":""},{"id":"read-only-variants","title":"Read-only variants","role":"content","text":"Since `GEORADIUS` and [`GEORADIUSBYMEMBER`](https://redis.io/docs/latest/commands/georadiusbymember) have a `STORE` and `STOREDIST` option they are technically flagged as writing commands in the Redis command table. For this reason read-only replicas will flag them, and Redis Cluster replicas will redirect them to the master instance even if the connection is in read-only mode (see the [`READONLY`](https://redis.io/docs/latest/commands/readonly) command of Redis Cluster).\n\nBreaking the compatibility with the past was considered but rejected, at least for Redis 4.0, so instead two read-only variants of the commands were added. They are exactly like the original commands but refuse the `STORE` and `STOREDIST` options. The two variants are called [`GEORADIUS_RO`](https://redis.io/docs/latest/commands/georadius_ro) and [`GEORADIUSBYMEMBER_RO`](https://redis.io/docs/latest/commands/georadiusbymember_ro), and can safely be used in replicas."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Deprecated as of Redis v6.2.0. |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* If no `WITH*` option is specified, an [Array reply](../../develop/reference/protocol-spec#arrays) of matched member names\n* If `WITHCOORD`, `WITHDIST`, or `WITHHASH` options are specified, the command returns an [Array reply](../../develop/reference/protocol-spec#arrays) of arrays, where each sub-array represents a single item:\n    1. The distance from the center as a floating point number, in the same unit specified in the radius.\n    1. The Geohash integer.\n    1. The coordinates as a two items x,y array (longitude,latitude).\nFor example, the command `GEORADIUS Sicily 15 37 200 km WITHCOORD WITHDIST` will return each item in the following way:\n`[\"Palermo\",\"190.4424\",[\"13.361389338970184\",\"38.115556395496299\"]]`\n\n**RESP3:**\n\nOne of the following:\n* If no `WITH*` option is specified, an [Array reply](../../develop/reference/protocol-spec#arrays) of matched member names\n* If `WITHCOORD`, `WITHDIST`, or `WITHHASH` options are specified, the command returns an [Array reply](../../develop/reference/protocol-spec#arrays) of arrays, where each sub-array represents a single item:\n    1. The distance from the center as a floating point number, in the same unit specified in the radius.\n    1. The Geohash integer.\n    1. The coordinates as a two items x,y array (longitude,latitude).\nFor example, the command `GEORADIUS Sicily 15 37 200 km WITHCOORD WITHDIST` will return each item in the following way:\n`[\"Palermo\",\"190.4424\",[\"13.361389338970184\",\"38.115556395496299\"]]`"}],"examples":[]}
{"schema_version":2,"id":"commands/georadius_ro","title":"GEORADIUS_RO","url":"https://redis.io/docs/latest/commands/georadius_ro/","summary":"Returns members from a geospatial index that are within a distance from a coordinate.","since":"3.2.10","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:43+01:00","page_type":"content","content_hash":"b4ebe8def6aaf239e554eee28b76e39686e6973da12a2cdf5acdbe3d4723b90b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Read-only variant of the [`GEORADIUS`](https://redis.io/docs/latest/commands/georadius) command.\n\nThis command is identical to the [`GEORADIUS`](https://redis.io/docs/latest/commands/georadius) command, except that it doesn't support the optional `STORE` and `STOREDIST` parameters."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the geospatial index (a sorted set).\n\n</details>\n\n<details open><summary><code>longitude</code></summary>\n\nThe longitude of the center point.\n\n</details>\n\n<details open><summary><code>latitude</code></summary>\n\nThe latitude of the center point.\n\n</details>\n\n<details open><summary><code>radius</code></summary>\n\nThe radius of the search circle, in the unit given by the following argument.\n\n</details>\n\n<details open><summary><code>M | KM | FT | MI</code></summary>\n\nThe unit for `radius`: meters (`M`), kilometers (`KM`), feet (`FT`), or miles (`MI`).\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>WITHCOORD</code></summary>\n\nAlso return the longitude and latitude of each matching item.\n\n</details>\n\n<details open><summary><code>WITHDIST</code></summary>\n\nAlso return the distance of each matching item from the center, in the same unit as the radius.\n\n</details>\n\n<details open><summary><code>WITHHASH</code></summary>\n\nAlso return the raw 52-bit geohash-encoded score of each matching item.\n\n</details>\n\n<details open><summary><code>COUNT count [ANY]</code></summary>\n\nReturn at most `count` matches. With `ANY`, the command returns as soon as enough matches are found — faster, but the results may be unsorted.\n\n</details>\n\n<details open><summary><code>ASC | DESC</code></summary>\n\nSort the results by distance from the center: nearest first (`ASC`) or farthest first (`DESC`).\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Deprecated as of Redis v6.2.0. |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* If no `WITH*` option is specified, an [Array reply](../../develop/reference/protocol-spec#arrays) of matched member names\n* If `WITHCOORD`, `WITHDIST`, or `WITHHASH` options are specified, the command returns an [Array reply](../../develop/reference/protocol-spec#arrays) of arrays, where each sub-array represents a single item:\n    * The distance from the center as a floating point number, in the same unit specified in the radius.\n    * The Geohash integer.\n    * The coordinates as a two items x,y array (longitude,latitude).\n\n**RESP3:**\n\nOne of the following:\n* If no `WITH*` option is specified, an [Array reply](../../develop/reference/protocol-spec#arrays) of matched member names\n* If `WITHCOORD`, `WITHDIST`, or `WITHHASH` options are specified, the command returns an [Array reply](../../develop/reference/protocol-spec#arrays) of arrays, where each sub-array represents a single item:\n    * The distance from the center as a floating point number, in the same unit specified in the radius.\n    * The Geohash integer.\n    * The coordinates as a two items x,y array (longitude,latitude)."}],"examples":[]}
{"schema_version":2,"id":"commands/georadiusbymember","title":"GEORADIUSBYMEMBER","url":"https://redis.io/docs/latest/commands/georadiusbymember/","summary":"Queries a geospatial index for members within a distance from a member, optionally stores the result.","since":"3.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:43+01:00","page_type":"content","content_hash":"c540b706da107058db53395ec8dbf1eecb56fb85a5ed02f6b957f1fc3da96f8c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nThis command is exactly like [`GEORADIUS`](https://redis.io/docs/latest/commands/georadius) with the sole difference that instead\nof taking, as the center of the area to query, a longitude and latitude value, it takes the name of a member already existing inside the geospatial index represented by the sorted set.\n\nThe position of the specified member is used as the center of the query.\n\nPlease check the example below and the [`GEORADIUS`](https://redis.io/docs/latest/commands/georadius) documentation for more information about the command and its options.\n\nNote that [`GEORADIUSBYMEMBER_RO`](https://redis.io/docs/latest/commands/georadiusbymember_ro) is also available since Redis 3.2.10 and Redis 4.0.0 in order to provide a read-only command that can be used in replicas. See the [`GEORADIUS`](https://redis.io/docs/latest/commands/georadius) page for more information."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the geospatial index (a sorted set).\n\n</details>\n\n<details open><summary><code>member</code></summary>\n\nThe member whose position is used as the center of the search.\n\n</details>\n\n<details open><summary><code>radius</code></summary>\n\nThe radius of the search circle, in the unit given by the following argument.\n\n</details>\n\n<details open><summary><code>M | KM | FT | MI</code></summary>\n\nThe unit for `radius`: meters (`M`), kilometers (`KM`), feet (`FT`), or miles (`MI`).\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>WITHCOORD</code></summary>\n\nAlso return the longitude and latitude of each matching item.\n\n</details>\n\n<details open><summary><code>WITHDIST</code></summary>\n\nAlso return the distance of each matching item from the center, in the same unit as the radius.\n\n</details>\n\n<details open><summary><code>WITHHASH</code></summary>\n\nAlso return the raw 52-bit geohash-encoded score of each matching item.\n\n</details>\n\n<details open><summary><code>COUNT count [ANY]</code></summary>\n\nReturn at most `count` matches. With `ANY`, the command returns as soon as enough matches are found — faster, but the results may be unsorted.\n\n</details>\n\n<details open><summary><code>ASC | DESC</code></summary>\n\nSort the results by distance from the center: nearest first (`ASC`) or farthest first (`DESC`).\n\n</details>\n\n<details open><summary><code>STORE key</code></summary>\n\nStore the results as a geospatial index in `key` instead of returning them. `STORE` and `STOREDIST` are mutually exclusive.\n\n</details>\n\n<details open><summary><code>STOREDIST key</code></summary>\n\nStore the results in `key` as a sorted set of distances from the center, instead of returning them.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> GEOADD Sicily 13.583333 37.316667 \"Agrigento\"\n(integer) 1\nredis> GEOADD Sicily 13.361389 38.115556 \"Palermo\" 15.087269 37.502669 \"Catania\"\n(integer) 2\nredis> GEORADIUSBYMEMBER Sicily Agrigento 100 km\n1) \"Agrigento\"\n2) \"Palermo\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Deprecated as of Redis v6.2.0. |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* If no `WITH*` option is specified, an [Array reply](../../develop/reference/protocol-spec#arrays) of matched member names\n* If `WITHCOORD`, `WITHDIST`, or `WITHHASH` options are specified, the command returns an [Array reply](../../develop/reference/protocol-spec#arrays) of arrays, where each sub-array represents a single item:\n    * The distance from the center as a floating point number, in the same unit specified in the radius.\n    * The Geohash integer.\n    * The coordinates as a two items x,y array (longitude,latitude).\n\n**RESP3:**\n\nOne of the following:\n* If no `WITH*` option is specified, an [Array reply](../../develop/reference/protocol-spec#arrays) of matched member names\n* If `WITHCOORD`, `WITHDIST`, or `WITHHASH` options are specified, the command returns an [Array reply](../../develop/reference/protocol-spec#arrays) of arrays, where each sub-array represents a single item:\n    * The distance from the center as a floating point number, in the same unit specified in the radius.\n    * The Geohash integer.\n    * The coordinates as a two items x,y array (longitude,latitude)."}],"examples":[]}
{"schema_version":2,"id":"commands/georadiusbymember_ro","title":"GEORADIUSBYMEMBER_RO","url":"https://redis.io/docs/latest/commands/georadiusbymember_ro/","summary":"Returns members from a geospatial index that are within a distance from a member.","since":"3.2.10","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:43+01:00","page_type":"content","content_hash":"b57b3582c572b521ca9357b1436e14d1ea62958987f5cc128f98fe79a80312d3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Read-only variant of the [`GEORADIUSBYMEMBER`](https://redis.io/docs/latest/commands/georadiusbymember) command.\n\nThis command is identical to the [`GEORADIUSBYMEMBER`](https://redis.io/docs/latest/commands/georadiusbymember) command, except that it doesn't support the optional `STORE` and `STOREDIST` parameters."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the geospatial index (a sorted set).\n\n</details>\n\n<details open><summary><code>member</code></summary>\n\nThe member whose position is used as the center of the search.\n\n</details>\n\n<details open><summary><code>radius</code></summary>\n\nThe radius of the search circle, in the unit given by the following argument.\n\n</details>\n\n<details open><summary><code>M | KM | FT | MI</code></summary>\n\nThe unit for `radius`: meters (`M`), kilometers (`KM`), feet (`FT`), or miles (`MI`).\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>WITHCOORD</code></summary>\n\nAlso return the longitude and latitude of each matching item.\n\n</details>\n\n<details open><summary><code>WITHDIST</code></summary>\n\nAlso return the distance of each matching item from the center, in the same unit as the radius.\n\n</details>\n\n<details open><summary><code>WITHHASH</code></summary>\n\nAlso return the raw 52-bit geohash-encoded score of each matching item.\n\n</details>\n\n<details open><summary><code>COUNT count [ANY]</code></summary>\n\nReturn at most `count` matches. With `ANY`, the command returns as soon as enough matches are found — faster, but the results may be unsorted.\n\n</details>\n\n<details open><summary><code>ASC | DESC</code></summary>\n\nSort the results by distance from the center: nearest first (`ASC`) or farthest first (`DESC`).\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Deprecated as of Redis v6.2.0. |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* If no `WITH*` option is specified, an [Array reply](../../develop/reference/protocol-spec#arrays) of matched member names\n* If `WITHCOORD`, `WITHDIST`, or `WITHHASH` options are specified, the command returns an [Array reply](../../develop/reference/protocol-spec#arrays) of arrays, where each sub-array represents a single item:\n    * The distance from the center as a floating point number, in the same unit specified in the radius.\n    * The Geohash integer.\n    * The coordinates as a two items x,y array (longitude,latitude).\n\n**RESP3:**\n\nOne of the following:\n* If no `WITH*` option is specified, an [Array reply](../../develop/reference/protocol-spec#arrays) of matched member names\n* If `WITHCOORD`, `WITHDIST`, or `WITHHASH` options are specified, the command returns an [Array reply](../../develop/reference/protocol-spec#arrays) of arrays, where each sub-array represents a single item:\n    * The distance from the center as a floating point number, in the same unit specified in the radius.\n    * The Geohash integer.\n    * The coordinates as a two items x,y array (longitude,latitude)."}],"examples":[]}
{"schema_version":2,"id":"commands/geosearch","title":"GEOSEARCH","url":"https://redis.io/docs/latest/commands/geosearch/","summary":"Queries a geospatial index for members inside an area of a box or a circle.","since":"6.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:43+01:00","page_type":"content","content_hash":"0188f1ef92c554b3f773a614b3a8984fc0bc54cbf47048aef89ab4281681d879","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Return the members of a sorted set populated with geospatial information using [`GEOADD`](https://redis.io/docs/latest/commands/geoadd), which are within the borders of the area specified by a given shape. This command extends the [`GEORADIUS`](https://redis.io/docs/latest/commands/georadius) command, so in addition to searching within circular areas, it supports searching within rectangular areas.\n\nThis command should be used in place of the deprecated [`GEORADIUS`](https://redis.io/docs/latest/commands/georadius) and [`GEORADIUSBYMEMBER`](https://redis.io/docs/latest/commands/georadiusbymember) commands."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the geospatial index (a sorted set).\n\n</details>\n\nQuery center point options:\n\n<details open><summary><code>FROMMEMBER member</code></summary>\n\nUse the position of the existing `member` as the center of the search. Mutually exclusive with `FROMLONLAT`.\n\n</details>\n\n<details open><summary><code>FROMLONLAT longitude latitude</code></summary>\n\nUse the given coordinates as the center of the search. Mutually exclusive with `FROMMEMBER`.\n\n</details>\n\nQuery shape options:\n\n<details open><summary><code>BYRADIUS radius M | KM | FT | MI</code></summary>\n\nSearch within a circle of the given `radius` in the specified unit. Mutually exclusive with `BYBOX`.\n\n</details>\n\n<details open><summary><code>BYBOX width height M | KM | FT | MI</code></summary>\n\nSearch within an axis-aligned box of the given `width` and `height` in the specified unit. Mutually exclusive with `BYRADIUS`.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>ASC | DESC</code></summary>\n\nSort the results by distance from the center: nearest first (`ASC`) or farthest first (`DESC`).\n\n</details>\n\n<details open><summary><code>COUNT count [ANY]</code></summary>\n\nReturn at most `count` matches. With `ANY`, the command returns as soon as enough matches are found — faster, but the results may be unsorted. Without `COUNT`, all matching items are returned.\nThis means that the results returned may not be the ones closest to the specified point, but the effort invested by the server to generate them is significantly less. When ANY is not provided, the command will perform an effort that is proportional to the number of items matching the specified area and sort them, so to query very large areas with a very small COUNT option may be slow even if just a few results are returned.\n\n</details>\n\n<details open><summary><code>WITHCOORD</code></summary>\n\nAlso return the longitude and latitude of each matching item.\n\n</details>\n\n<details open><summary><code>WITHDIST</code></summary>\n\nAlso return the distance of each matching item from the center, in the same unit as the radius.\n\n</details>\n\n<details open><summary><code>WITHHASH</code></summary>\n\nAlso return the raw 52-bit geohash-encoded score of each matching item.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> GEOADD Sicily 13.361389 38.115556 \"Palermo\" 15.087269 37.502669 \"Catania\"\n(integer) 2\nredis> GEOADD Sicily 12.758489 38.788135 \"edge1\"   17.241510 38.788135 \"edge2\"\n(integer) 2\nredis> GEOSEARCH Sicily FROMLONLAT 15 37 BYRADIUS 200 km ASC\n1) \"Catania\"\n2) \"Palermo\"\nredis> GEOSEARCH Sicily FROMLONLAT 15 37 BYBOX 400 400 km ASC WITHCOORD WITHDIST\n1) 1) \"Catania\"\n   2) \"56.4413\"\n   3) 1) \"15.087267458438873\"\n      2) \"37.50266842333162\"\n2) 1) \"Palermo\"\n   2) \"190.4424\"\n   3) 1) \"13.361389338970184\"\n      2) \"38.1155563954963\"\n3) 1) \"edge2\"\n   2) \"279.7403\"\n   3) 1) \"17.241510450839996\"\n      2) \"38.78813451624225\"\n4) 1) \"edge1\"\n   2) \"279.7405\"\n   3) 1) \"12.75848776102066\"\n      2) \"38.78813451624225\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* If no `WITH*` option is specified, an [Array reply](../../develop/reference/protocol-spec#arrays) of matched member names\n* If `WITHCOORD`, `WITHDIST`, or `WITHHASH` options are specified, the command returns an [Array reply](../../develop/reference/protocol-spec#arrays) of arrays, where each sub-array represents a single item:\n    * The distance from the center as a floating point number, in the same unit specified in the radius.\n    * The Geohash integer.\n    * The coordinates as a two items x,y array (longitude,latitude).\n\n**RESP3:**\n\nOne of the following:\n* If no `WITH*` option is specified, an [Array reply](../../develop/reference/protocol-spec#arrays) of matched member names\n* If `WITHCOORD`, `WITHDIST`, or `WITHHASH` options are specified, the command returns an [Array reply](../../develop/reference/protocol-spec#arrays) of arrays, where each sub-array represents a single item:\n    * The distance from the center as a floating point number, in the same unit specified in the radius.\n    * The Geohash integer.\n    * The coordinates as a two items x,y array (longitude,latitude)."}],"examples":[]}
{"schema_version":2,"id":"commands/geosearchstore","title":"GEOSEARCHSTORE","url":"https://redis.io/docs/latest/commands/geosearchstore/","summary":"Queries a geospatial index for members inside an area of a box or a circle, optionally stores the result.","since":"6.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:43+01:00","page_type":"content","content_hash":"6b3a272905034504ce06d6748a48ed4bdcac8cfe24149551e5354489a7faa4da","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nThis command is similar to [`GEOSEARCH`](https://redis.io/docs/latest/commands/geosearch), but it stores the result in the provided `destination`.\n\nThis command replaces the now deprecated [`GEORADIUS`](https://redis.io/docs/latest/commands/georadius) and [`GEORADIUSBYMEMBER`](https://redis.io/docs/latest/commands/georadiusbymember).\n\nBy default, it stores the results in the `destination` sorted set with their geospatial information.\n\nWhen using the `STOREDIST` option, the command stores the items in a sorted set populated with their distance from the center of the circle or box, as a floating-point number, in the same unit specified for that shape."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>destination</code></summary>\n\nThe key to store the results in.\n\n</details>\n\n<details open><summary><code>source</code></summary>\n\nThe geospatial index key to search.\n\n</details>\n\nQuery center points:\n\n<details open><summary><code>FROMMEMBER member</code></summary>\n\nUse the position of the existing `member` as the center of the search. Mutually exclusive with `FROMLONLAT`.\n\n</details>\n\n<details open><summary><code>FROMLONLAT longitude latitude</code></summary>\n\nUse the given coordinates as the center of the search. Mutually exclusive with `FROMMEMBER`.\n\n</details>\n\nQuery shape options:\n\n<details open><summary><code>BYRADIUS radius M | KM | FT | MI</code></summary>\n\nSearch within a circle of the given `radius` in the specified unit. Mutually exclusive with `BYBOX`.\n\n</details>\n\n<details open><summary><code>BYBOX width height M | KM | FT | MI</code></summary>\n\nSearch within an axis-aligned box of the given `width` and `height` in the specified unit. Mutually exclusive with `BYRADIUS`.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>ASC | DESC</code></summary>\n\nSort the results by distance from the center: nearest first (`ASC`) or farthest first (`DESC`).\n\n</details>\n\n<details open><summary><code>COUNT count [ANY]</code></summary>\n\nReturn at most `count` matches. With `ANY`, the command returns as soon as enough matches are found — faster, but the results may be unsorted.\n\n</details>\n\n<details open><summary><code>STOREDIST</code></summary>\n\nStore the distance from the center as the sorted-set score, instead of the geohash-encoded position.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> GEOADD Sicily 13.361389 38.115556 \"Palermo\" 15.087269 37.502669 \"Catania\"\n(integer) 2\nredis> GEOADD Sicily 12.758489 38.788135 \"edge1\"   17.241510 38.788135 \"edge2\"\n(integer) 2\nredis> GEOSEARCHSTORE key1 Sicily FROMLONLAT 15 37 BYBOX 400 400 km ASC COUNT 3\n(integer) 3\nredis> GEOSEARCH key1 FROMLONLAT 15 37 BYBOX 400 400 km ASC WITHCOORD WITHDIST WITHHASH\n1) 1) \"Catania\"\n   2) \"56.4413\"\n   3) (integer) 3479447370796909\n   4) 1) \"15.087267458438873\"\n      2) \"37.50266842333162\"\n2) 1) \"Palermo\"\n   2) \"190.4424\"\n   3) (integer) 3479099956230698\n   4) 1) \"13.361389338970184\"\n      2) \"38.1155563954963\"\n3) 1) \"edge2\"\n   2) \"279.7403\"\n   3) (integer) 3481342659049484\n   4) 1) \"17.241510450839996\"\n      2) \"38.78813451624225\"\nredis> GEOSEARCHSTORE key2 Sicily FROMLONLAT 15 37 BYBOX 400 400 km ASC COUNT 3 STOREDIST\n(integer) 3\nredis> ZRANGE key2 0 -1 WITHSCORES\n1) \"Catania\"\n2) \"56.4412578701582\"\n3) \"Palermo\"\n4) \"190.44242984775784\"\n5) \"edge2\"\n6) \"279.7403417843143\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of elements in the resulting set\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of elements in the resulting set"}],"examples":[]}
{"schema_version":2,"id":"commands/get","title":"GET","url":"https://redis.io/docs/latest/commands/get/","summary":"Returns the string value of a key.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-20T16:12:55+02:00","page_type":"content","content_hash":"eb1f30d1ffbb745bd6f07f247c27b266fdfccb65ef56e297aa68a2983a9bc89f","sections":[{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"Foundational: Retrieve the string value of a key using GET (returns nil if key doesn't exist)\n\n**Difficulty:** Beginner\n\n**Commands:** GET, SET\n\n**Complexity:**\n- GET: O(1)\n- SET: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the value of the key.\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): if the key does not exist.\n\n**RESP3:**\n\nOne of the following:\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the value of the key.\n* [Null reply](../../develop/reference/protocol-spec#nulls): key does not exist."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> GET nonexisting\n(nil)\n> SET mykey \"Hello\"\nOK\n> GET mykey\n\"Hello\"","section_id":"examples"},{"id":"examples-ex1","language":"csharp","code":"using NRedisStack.Tests;\nusing StackExchange.Redis;\n\npublic class SetGetExample\n{\n    public void Run()\n    {\n        var muxer = ConnectionMultiplexer.Connect(\"localhost:6379\");\n        var db = muxer.GetDatabase();\n\n        bool status = db.StringSet(\"bike:1\", \"Process 134\");\n\n        if (status)\n            Console.WriteLine(\"Successfully added a bike.\");\n\n        var value = db.StringGet(\"bike:1\");\n\n        if (value.HasValue)\n            Console.WriteLine(\"The name of the bike is: \" + value + \".\");\n\n    }\n}","section_id":"examples"},{"id":"examples-ex2","language":"go","code":"package example_commands_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/redis/go-redis/v9\"\n)\n\nfunc ExampleClient_Set_and_get() {\n\tctx := context.Background()\n\n\trdb := redis.NewClient(&redis.Options{\n\t\tAddr:     \"localhost:6379\",\n\t\tPassword: \"\", // no password docs\n\t\tDB:       0,  // use default DB\n\t})\n\n\n\n\terr := rdb.Set(ctx, \"bike:1\", \"Process 134\", 0).Err()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(\"OK\")\n\n\tvalue, err := rdb.Get(ctx, \"bike:1\").Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tfmt.Printf(\"The name of the bike is %s\", value)\n\n}","section_id":"examples"},{"id":"examples-ex3","language":"java","code":"package io.redis.examples.async;\n\nimport io.lettuce.core.RedisClient;\nimport io.lettuce.core.api.async.RedisAsyncCommands;\nimport io.lettuce.core.api.StatefulRedisConnection;\n\nimport java.util.concurrent.CompletableFuture;\n\npublic class SetGetExample {\n\n    public void run() {\n        RedisClient redisClient = RedisClient.create(\"redis://localhost:6379\");\n\n        try (StatefulRedisConnection<String, String> connection = redisClient.connect()) {\n            RedisAsyncCommands<String, String> asyncCommands = connection.async();\n\n\n            CompletableFuture<Void> setGetExample = asyncCommands.set(\"bike:1\", \"Process 134\")\n                    .thenCompose(res1 -> {\n                        System.out.println(res1); // >>> OK\n                        return asyncCommands.get(\"bike:1\");\n                    }).thenAccept(res2 -> {\n                        System.out.println(res2); // >>> Process 134\n                    }).toCompletableFuture();\n\n            setGetExample.join();\n        } finally {\n            redisClient.shutdown();\n        }\n    }\n}","section_id":"examples"},{"id":"examples-ex4","language":"java","code":"package io.redis.examples.reactive;\n\nimport io.lettuce.core.RedisClient;\nimport io.lettuce.core.api.reactive.RedisReactiveCommands;\nimport io.lettuce.core.api.StatefulRedisConnection;\n\nimport reactor.core.publisher.Mono;\n\npublic class SetGetExample {\n\n    public void run() {\n        RedisClient redisClient = RedisClient.create(\"redis://localhost:6379\");\n\n        try (StatefulRedisConnection<String, String> connection = redisClient.connect()) {\n            RedisReactiveCommands<String, String> reactiveCommands = connection.reactive();\n\n\n            Mono<Void> setGetExample = reactiveCommands.set(\"bike:1\", \"Process 134\")\n                    .doOnNext(res1 -> {\n                        System.out.println(res1); // >>> OK\n                    })\n                    .then(reactiveCommands.get(\"bike:1\"))\n                    .doOnNext(res2 -> {\n                        System.out.println(res2); // >>> Process 134\n                    })\n                    .then();\n\n            setGetExample.block();\n        } finally {\n            redisClient.shutdown();\n        }\n    }\n}","section_id":"examples"},{"id":"examples-ex5","language":"java","code":"package io.redis.examples;\n\nimport redis.clients.jedis.RedisClient;\n\n\npublic class SetGetExample {\n\n  public void run() {\n\n    RedisClient jedis = RedisClient.create(\"redis://localhost:6379\");\n\n    String status = jedis.set(\"bike:1\", \"Process 134\");\n\n    if (\"OK\".equals(status)) System.out.println(\"Successfully added a bike.\");\n\n    String value = jedis.get(\"bike:1\");\n\n    if (value != null) System.out.println(\"The name of the bike is: \" + value + \".\");\n\n\n    jedis.close();\n  }\n}","section_id":"examples"},{"id":"examples-ex6","language":"javascript","code":"import { createClient } from 'redis';\n\nconst client = createClient();\n\nclient.on('error', err => console.log('Redis Client Error', err));\n\nawait client.connect().catch(console.error);\n\nawait client.set('bike:1', 'Process 134');\nconst value = await client.get('bike:1');\nconsole.log(value);\n// returns 'Process 134'\n\nawait client.close();","section_id":"examples"},{"id":"examples-ex7","language":"javascript","code":"import assert from 'node:assert';\nimport { Redis } from 'ioredis';\n\nconst redis = new Redis();\n\n\nconst res1 = await redis.set('bike:1', 'Process 134');\nconsole.log(res1); // >>> OK\n\nconst res2 = await redis.get('bike:1');\nconsole.log(res2); // >>> Process 134\n\n\nredis.disconnect();","section_id":"examples"},{"id":"examples-ex8","language":"javascript","code":"import { createClient } from 'redis';\n\nconst client = createClient();\n\nclient.on('error', err => console.log('Redis Client Error', err));\n\nawait client.connect().catch(console.error);\n\nawait client.set('bike:1', 'Process 134');\nconst value = await client.get('bike:1');\nconsole.log(value);\n// returns 'Process 134'\n\nawait client.close();","section_id":"examples"},{"id":"examples-ex9","language":"javascript","code":"import assert from 'node:assert';\nimport { Redis } from 'ioredis';\n\nconst redis = new Redis();\n\n\nconst res1 = await redis.set('bike:1', 'Process 134');\nconsole.log(res1); // >>> OK\n\nconst res2 = await redis.get('bike:1');\nconsole.log(res2); // >>> Process 134\n\n\nredis.disconnect();","section_id":"examples"},{"id":"examples-ex10","language":"php","code":"<?php\nuse Predis\\Client as PredisClient;\n\nclass SetGetTest\n{\n    public function testSetGet() {\n        $r = new PredisClient([\n            'scheme'   => 'tcp',\n            'host'     => '127.0.0.1',\n            'port'     => 6379,\n            'password' => '',\n            'database' => 0,\n        ]);\n\n\n        $res1 = $r->set('bike:1', 'Process 134');\n        echo $res1 . PHP_EOL; // >>> OK\n\n        $res2 = $r->get('bike:1');\n        echo $res2 . PHP_EOL; // >>> Process 134\n\n    }\n}","section_id":"examples"},{"id":"examples-ex11","language":"python","code":"\"\"\"\nCode samples for data structure store quickstart pages:\n    https://redis.io/docs/latest/develop/get-started/data-store/\n\"\"\"\n\nimport redis\n\nr = redis.Redis(host=\"localhost\", port=6379, db=0, decode_responses=True)\n\nres = r.set(\"bike:1\", \"Process 134\")\nprint(res)\n# >>> True\n\nres = r.get(\"bike:1\")\nprint(res)\n# >>> \"Process 134\"","section_id":"examples"},{"id":"examples-ex12","language":"ruby","code":"require 'redis'\n\nr = Redis.new\n\n\nres1 = r.set('bike:1', 'Process 134')\nputs res1 # >>> OK\n\nres2 = r.get('bike:1')\nputs res2 # >>> Process 134","section_id":"examples"},{"id":"examples-ex13","language":"rust","code":"mod set_and_get_tests {\n    use redis::AsyncCommands;\n\n    async fn run() {\n        let mut r = match redis::Client::open(\"redis://127.0.0.1\") {\n            Ok(client) => match client.get_multiplexed_async_connection().await {\n                Ok(conn) => conn,\n                Err(e) => {\n                    println!(\"Failed to connect to Redis: {e}\");\n                    return;\n                }\n            },\n            Err(e) => {\n                println!(\"Failed to create Redis client: {e}\");\n                return;\n            }\n        };\n\n\n        if let Ok(res1) = r.set(\"bike:1\", \"Process 134\").await {\n            let res1: String = res1;\n            println!(\"{res1}\"); // >>> OK\n        }\n\n        if let Ok(res2) = r.get(\"bike:1\").await {\n            let res2: String = res2;\n            println!(\"{res2}\"); // >>> Process 134\n        }\n\n    }\n}","section_id":"examples"},{"id":"examples-ex14","language":"rust","code":"mod set_and_get_tests {\n    use redis::Commands;\n\n    fn run() {\n        let mut r = match redis::Client::open(\"redis://127.0.0.1\") {\n            Ok(client) => match client.get_connection() {\n                Ok(conn) => conn,\n                Err(e) => {\n                    println!(\"Failed to connect to Redis: {e}\");\n                    return;\n                }\n            },\n            Err(e) => {\n                println!(\"Failed to create Redis client: {e}\");\n                return;\n            }\n        };\n\n\n        if let Ok(res1) = r.set(\"bike:1\", \"Process 134\") {\n            let res1: String = res1;\n            println!(\"{res1}\"); // >>> OK\n        }\n\n        if let Ok(res2) = r.get(\"bike:1\") {\n            let res2: String = res2;\n            println!(\"{res2}\"); // >>> Process 134\n        }\n\n    }\n}","section_id":"examples"}]}
{"schema_version":2,"id":"commands/getbit","title":"GETBIT","url":"https://redis.io/docs/latest/commands/getbit/","summary":"Returns a bit value by offset.","since":"2.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"379edcc28ccd3a7be87ed0bfd8290e97238c08891c2361445d8f1ddf84630920","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns the bit value at _offset_ in the string value stored at _key_.\n\nWhen _offset_ is beyond the string length, the string is assumed to be a\ncontiguous space with 0 bits.\nWhen _key_ does not exist it is assumed to be an empty string, so _offset_ is\nalways out of range and the value is also assumed to be a contiguous space with\n0 bits."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the string.\n\n</details>\n\n<details open><summary><code>offset</code></summary>\n\nThe zero-based bit offset to read.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SETBIT mykey 7 1\n(integer) 0\nredis> GETBIT mykey 0\n(integer) 0\nredis> GETBIT mykey 7\n(integer) 1\nredis> GETBIT mykey 100\n(integer) 0"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nThe bit value stored at _offset_, one of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0`.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `1`.\n\n**RESP3:**\n\nThe bit value stored at _offset_, one of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0`.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `1`."}],"examples":[]}
{"schema_version":2,"id":"commands/getdel","title":"GETDEL","url":"https://redis.io/docs/latest/commands/getdel/","summary":"Returns the string value of a key after deleting the key.","since":"6.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:12+01:00","page_type":"content","content_hash":"388b8cce5c0379db57d7ecdbcdafcc05d6dd1b8df6a950fe3d3440fb1e733ff6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Get the value of `key` and delete the key.\nThis command is similar to [`GET`](https://redis.io/docs/latest/commands/get), except for the fact that it also deletes the key on success (if and only if the key's value type is a string)."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key to get and then delete.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SET mykey \"Hello\"\nOK\nredis> GETDEL mykey\n\"Hello\"\nredis> GET mykey\n(nil)"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the value of the key.\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): if the key does not exist or if the key's value type is not a string.\n\n**RESP3:**\n\nOne of the following:\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the value of the key.\n* [Null reply](../../develop/reference/protocol-spec#nulls): if the key does not exist or if the key's value type is not a string."}],"examples":[]}
{"schema_version":2,"id":"commands/getex","title":"GETEX","url":"https://redis.io/docs/latest/commands/getex/","summary":"Returns the string value of a key after setting its expiration time.","since":"6.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:12+01:00","page_type":"content","content_hash":"04b575ff5fdafad03c10fc99689041a7bacd02b3693f469719ddd16c4415bfa3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Get the value of `key` and optionally set its expiration.\n`GETEX` is similar to [`GET`](https://redis.io/docs/latest/commands/get), but is a write command with additional options."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key to retrieve.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"The following options modify the command's behavior. They are mutually exclusive.\n\n<details open><summary><code>EX seconds</code></summary>\n\nSet the specified expire time, in seconds.\n\n</details>\n\n<details open><summary><code>PX milliseconds</code></summary>\n\nSet the specified expire time, in milliseconds.\n\n</details>\n\n<details open><summary><code>EXAT unix-time-seconds</code></summary>\n\nSet the specified Unix time at which the key will expire, in seconds.\n\n</details>\n\n<details open><summary><code>PXAT unix-time-milliseconds</code></summary>\n\nSet the specified Unix time at which the key will expire, in milliseconds.\n\n</details>\n\n<details open><summary><code>PERSIST</code></summary>\n\nRemove the time to live associated with the key. See [`PERSIST`](https://redis.io/docs/latest/commands/persist).\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SET mykey \"Hello\"\nOK\nredis> GETEX mykey\n\"Hello\"\nredis> TTL mykey\n(integer) -1\nredis> GETEX mykey EX 60\n\"Hello\"\nredis> TTL mykey\n(integer) 60"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active\\*</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active\\*</nobr></span> | \\*Not supported for HyperLogLog. |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the value of `key`\n[Nil reply](../../develop/reference/protocol-spec#bulk-strings): if `key` does not exist.\n\n**RESP3:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the value of `key`\n[Null reply](../../develop/reference/protocol-spec#nulls): if `key` does not exist."}],"examples":[]}
{"schema_version":2,"id":"commands/getrange","title":"GETRANGE","url":"https://redis.io/docs/latest/commands/getrange/","summary":"Returns a substring of the string stored at a key.","since":"2.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"a15fd6f16b5677aac48a275a3e46de5d8a9c7ab1651e7f74fdf91dae5ccb3951","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns the substring of the string value stored at key, as determined by the start and end offsets. Both offsets are inclusive.\n\nYou can use negative offsets to count backward from the end of the string. For example, -1 is the last character, -2 is the penultimate character, and so on.\n\nThe function handles out of range requests by limiting the resulting range to\nthe actual length of the string."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key.\n\n</details>\n\n<details open><summary><code>start</code></summary>\n\nThe start offset, zero-based. A negative value counts from the end of the string.\n\n</details>\n\n<details open><summary><code>end</code></summary>\n\nThe end offset, zero-based and inclusive. A negative value counts from the end of the string.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SET mykey \"This is a string\"\nOK\nredis> GETRANGE mykey 0 3\n\"This\"\nredis> GETRANGE mykey -3 -1\n\"ing\"\nredis> GETRANGE mykey 0 -1\n\"This is a string\"\nredis> GETRANGE mykey 10 100\n\"string\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): The substring of the string value stored at key, determined by the offsets start and end (both are inclusive).\n\n**RESP3:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): The substring of the string value stored at key, determined by the offsets start and end (both are inclusive)."}],"examples":[]}
{"schema_version":2,"id":"commands/getset","title":"GETSET","url":"https://redis.io/docs/latest/commands/getset/","summary":"Returns the previous string value of a key after setting it to a new value.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:12+01:00","page_type":"content","content_hash":"b9f8248e68aaa637ad68f9e41fd369bd9d2254bbb2e9e00c0617c8f20b527e28","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Atomically sets `key` to `value` and returns the old value stored at `key`.\nReturns an error when `key` exists but does not hold a string value.  Any \nprevious time to live associated with the key is discarded on successful \n[`SET`](https://redis.io/docs/latest/commands/set) operation."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key.\n\n</details>\n\n<details open><summary><code>value</code></summary>\n\nThe new value to set.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SET mykey \"Hello\"\nOK\nredis> GETSET mykey \"World\"\n\"Hello\"\nredis> GET mykey\n\"World\""},{"id":"details","title":"Details","role":"content","text":""},{"id":"design-pattern","title":"Design pattern","role":"content","text":"You can use GETSET with [INCR](https://redis.io/docs/latest/commands/incr) to count events and reset the counter atomically.\nFor example, you can call [INCR](https://redis.io/docs/latest/commands/incr) on the key `mycounter` each time an event occurs. When you need to read the counter value and reset it to zero atomically, call GETSET.\nThis can be done using `GETSET mycounter \"0\"`:\n\n\nredis> INCR mycounter\n(integer) 1\nredis> GETSET mycounter \"0\"\n\"1\"\nredis> GET mycounter\n\"0\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Deprecated as of Redis v6.2.0. |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the old value stored at the key.\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): if the key does not exist.\n\n**RESP3:**\n\nOne of the following:\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the old value stored at the key.\n* [Null reply](../../develop/reference/protocol-spec#nulls): if the key does not exist."}],"examples":[]}
{"schema_version":2,"id":"commands/hdel","title":"HDEL","url":"https://redis.io/docs/latest/commands/hdel/","summary":"Deletes one or more fields and their values from a hash. Deletes the hash if no fields remain.","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-08T12:05:04+02:00","page_type":"content","content_hash":"efc65699c2ff01c986cbacbcceed05e63d01792b51415003d9ca942d506a7a31","sections":[{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the hash.\n\n</details>\n\n<details open><summary><code>field [field ...]</code></summary>\n\nOne or more fields to delete from the hash.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"Foundational: Delete one or more fields from a hash using HDEL (returns count of deleted fields, ignores non-existent fields)\n\n**Difficulty:** Beginner\n\n**Commands:** HSET, HDEL\n\n**Complexity:**\n- HSET: O(1)\n- HDEL: O(N)\n\n**Available in:** Redis CLI, C, C#, Go, Java, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of fields that were removed from the hash, excluding any specified but non-existing fields.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): The number of fields that were removed from the hash, excluding any specified but non-existing fields."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> HSET myhash field1 \"foo\"\n(integer) 1\n> HDEL myhash field1\n(integer) 1\n> HDEL myhash field2\n(integer) 0","section_id":"examples"},{"id":"examples-ex1","language":"c","code":"reply = redisCommand(c, \"HSET myhash field1 foo\");\n    printf(\"%lld\\n\", reply->integer);\n    // >>> 1\n    freeReplyObject(reply);\n\n    reply = redisCommand(c, \"HDEL myhash field1\");\n    printf(\"%lld\\n\", reply->integer);\n    // >>> 1\n    freeReplyObject(reply);\n\n    // Deleting a field that is not there removes nothing.\n    reply = redisCommand(c, \"HDEL myhash field2\");\n    printf(\"%lld\\n\", reply->integer);\n    // >>> 0\n    freeReplyObject(reply);","section_id":"examples"},{"id":"examples-ex2","language":"csharp","code":"bool hdelRes1 = db.HashSet(\"myhash\", \"field1\", \"foo\");\n\n        RedisValue hdelRes2 = db.HashDelete(\"myhash\", \"field1\");\n        Console.WriteLine(hdelRes2);    // >>> 1\n\n        RedisValue hdelRes3 = db.HashDelete(\"myhash\", \"field1\");\n        Console.WriteLine(hdelRes3);    // >>> 0","section_id":"examples"},{"id":"examples-ex3","language":"go","code":"hdel1, err := rdb.HSet(ctx, \"myhash\", \"field1\", \"foo\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(hdel1) // >>> 1\n\n\thdel2, err := rdb.HDel(ctx, \"myhash\", \"field1\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(hdel2) // >>> 1\n\n\thdel3, err := rdb.HDel(ctx, \"myhash\", \"field2\").Result()\n\n\tif err != nil {\n\t\tfmt.Println(err)\n\t}\n\n\tfmt.Println(hdel3) // >>> 0","section_id":"examples"},{"id":"examples-ex4","language":"java","code":"// `hset` returns true because `field1` is a new field.\n            boolean res1 = syncCommands.hset(\"myhash\", \"field1\", \"foo\");\n            System.out.println(res1); // >>> true\n\n            Long res2 = syncCommands.hdel(\"myhash\", \"field1\");\n            System.out.println(res2); // >>> 1\n\n            // `hdel` returns 0 because `field2` doesn't exist.\n            Long res3 = syncCommands.hdel(\"myhash\", \"field2\");\n            System.out.println(res3); // >>> 0","section_id":"examples"},{"id":"examples-ex5","language":"java","code":"Map<String, String> hDelExampleParams = new HashMap<>();\n            hDelExampleParams.put(\"field1\", \"foo\");\n\n            CompletableFuture<Void> hDelExample = asyncCommands.hset(\"myhash\", hDelExampleParams).thenCompose(res1 -> {\n                System.out.println(res1); // >>> 1\n                return asyncCommands.hdel(\"myhash\", \"field1\");\n            }).thenCompose(res2 -> {\n                System.out.println(res2); // >>> 1\n                return asyncCommands.hdel(\"myhash\", \"field2\");\n            }).thenAccept(res3 -> {\n                System.out.println(res3); // >>> 0\n            }).toCompletableFuture();","section_id":"examples"},{"id":"examples-ex6","language":"java","code":"Map<String, String> hDelExampleParams = new HashMap<>();\n            hDelExampleParams.put(\"field1\", \"foo\");\n\n            Mono<Long> hDelExample1 = reactiveCommands.hset(\"myhash\", hDelExampleParams).doOnNext(result -> {\n                System.out.println(result); // >>> 1\n            });\n\n            hDelExample1.block();\n\n            Mono<Long> hDelExample2 = reactiveCommands.hdel(\"myhash\", \"field1\").doOnNext(result -> {\n                System.out.println(result); // >>> 1\n            });\n\n            hDelExample2.block();\n\n            Mono<Long> hDelExample3 = reactiveCommands.hdel(\"myhash\", \"field2\").doOnNext(result -> {\n                System.out.println(result); // >>> 0\n            });","section_id":"examples"},{"id":"examples-ex7","language":"java","code":"Map<String, String> hDelExampleParams = new HashMap<>();\n        hDelExampleParams.put(\"field1\", \"foo\");\n\n        long hDelResult1 = jedis.hset(\"myhash\", hDelExampleParams);\n        System.out.println(hDelResult1);    // >>> 1\n\n        long hDelResult2 = jedis.hdel(\"myhash\", \"field1\");\n        System.out.println(hDelResult2);    // >>> 1\n\n        long hDelResult3 = jedis.hdel(\"myhash\", \"field2\");\n        System.out.println(hDelResult3);    // >>> 0","section_id":"examples"},{"id":"examples-ex8","language":"javascript","code":"const hDel1 = await client.hSet('myhash', 'field1', 'Hello')\nconsole.log(hDel1) // 1\n\nconst hDel2 = await client.hDel('myhash', 'field1')\nconsole.log(hDel2) // 1\n\nconst hDel3 = await client.hDel('myhash', 'field2')\nconsole.log(hDel3) // 0","section_id":"examples"},{"id":"examples-ex9","language":"javascript","code":"const hdelRes1 = await redis.hset('myhash', 'field1', 'foo');\nconsole.log(hdelRes1); // >>> 1\n\nconst hdelRes2 = await redis.hdel('myhash', 'field1');\nconsole.log(hdelRes2); // >>> 1\n\n// Deleting a field that is not there removes nothing.\nconst hdelRes3 = await redis.hdel('myhash', 'field2');\nconsole.log(hdelRes3); // >>> 0","section_id":"examples"},{"id":"examples-ex10","language":"javascript","code":"const hDel1 = await client.hSet('myhash', 'field1', 'Hello')\nconsole.log(hDel1) // 1\n\nconst hDel2 = await client.hDel('myhash', 'field1')\nconsole.log(hDel2) // 1\n\nconst hDel3 = await client.hDel('myhash', 'field2')\nconsole.log(hDel3) // 0","section_id":"examples"},{"id":"examples-ex11","language":"javascript","code":"const hdelRes1 = await redis.hset('myhash', 'field1', 'foo');\nconsole.log(hdelRes1); // >>> 1\n\nconst hdelRes2 = await redis.hdel('myhash', 'field1');\nconsole.log(hdelRes2); // >>> 1\n\n// Deleting a field that is not there removes nothing.\nconst hdelRes3 = await redis.hdel('myhash', 'field2');\nconsole.log(hdelRes3); // >>> 0","section_id":"examples"},{"id":"examples-ex12","language":"php","code":"$hDelResult1 = $this->redis->hset('myhash', 'field1', 'foo');\n        echo \"HSET myhash field1 foo: \" . $hDelResult1 . \"\\n\"; // >>> 1\n\n        $hDelResult2 = $this->redis->hdel('myhash', 'field1');\n        echo \"HDEL myhash field1: \" . $hDelResult2 . \"\\n\"; // >>> 1\n\n        $hDelResult3 = $this->redis->hdel('myhash', 'field2');\n        echo \"HDEL myhash field2: \" . $hDelResult3 . \"\\n\"; // >>> 0","section_id":"examples"},{"id":"examples-ex13","language":"python","code":"hdel1 = r.hset(\"myhash\", \"field1\", \"foo\")\nprint(hdel1)\n# >>> 1\n\nhdel2 = r.hdel(\"myhash\", \"field1\")\nprint(hdel2)\n# >>> 1\n\nhdel3 = r.hdel(\"myhash\", \"field2\")\nprint(hdel3)\n# >>> 0","section_id":"examples"},{"id":"examples-ex14","language":"ruby","code":"res1 = r.hset('myhash', 'field1', 'foo')\nputs res1 # >>> 1\n\nres2 = r.hdel('myhash', 'field1')\nputs res2 # >>> 1\n\nres3 = r.hdel('myhash', 'field2')\nputs res3 # >>> 0","section_id":"examples"},{"id":"examples-ex15","language":"rust","code":"match r.hset(\"myhash\", \"field1\", \"foo\").await {\n            Ok(hdel1) => {\n                let hdel1: i32 = hdel1;\n                println!(\"{hdel1}\");    // >>> 1\n            },\n            Err(e) => {\n                println!(\"Error setting hash field: {e}\");\n                return;\n            }\n        }\n\n        match r.hdel(\"myhash\", \"field1\").await {\n            Ok(hdel2) => {\n                let hdel2: Option<i32> = hdel2;\n                match hdel2 {\n                    Some(value) => {\n                        println!(\"{value}\");    // >>> 1\n                    },\n                    None => {\n                        println!(\"None\");\n                    }\n                }\n            },\n            Err(e) => {\n                println!(\"Error getting hash field: {e}\");\n                return;\n            }\n        }\n\n        match r.hdel(\"myhash\", \"field2\").await {\n            Ok(hdel3) => {\n                let hdel3: Option<i32> = hdel3;\n                match hdel3 {\n                    Some(value) => {\n                        println!(\"{value}\");    // >>> 0\n                    },\n                    None => {\n                        println!(\"None\");\n                    }\n                }\n            },\n            Err(e) => {\n                println!(\"Error getting hash field: {e}\");\n                return;\n            }\n        }","section_id":"examples"},{"id":"examples-ex16","language":"rust","code":"match r.hset(\"myhash\", \"field1\", \"foo\") {\n            Ok(hdel1) => {\n                let hdel1: i32 = hdel1;\n                println!(\"{hdel1}\");    // >>> 1\n            },\n            Err(e) => {\n                println!(\"Error setting hash field: {e}\");\n                return;\n            }\n        }\n\n        match r.hdel(\"myhash\", \"field1\") {\n            Ok(hdel2) => {\n                let hdel2: Option<i32> = hdel2;\n                match hdel2 {\n                    Some(value) => {\n                        println!(\"{value}\");    // >>> 1\n                    },\n                    None => {\n                        println!(\"None\");\n                    }\n                }\n            },\n            Err(e) => {\n                println!(\"Error getting hash field: {e}\");\n                return;\n            }\n        }\n\n        match r.hdel(\"myhash\", \"field2\") {\n            Ok(hdel3) => {\n                let hdel3: Option<i32> = hdel3;\n                match hdel3 {\n                    Some(value) => {\n                        println!(\"{value}\");    // >>> 0\n                    },\n                    None => {\n                        println!(\"None\");\n                    }\n                }\n            },\n            Err(e) => {\n                println!(\"Error getting hash field: {e}\");\n                return;\n            }\n        }","section_id":"examples"}]}
{"schema_version":2,"id":"commands/hello","title":"HELLO","url":"https://redis.io/docs/latest/commands/hello/","summary":"Handshakes with the Redis server.","since":"6.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"50395de3b658da0fd5a7412951fd65a33f56da7cfb4ce5e68313c4acf453db11","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Switches to a different protocol version, optionally authenticates the connection and sets the connection name. When you call HELLO with no arguments, or with a protocol version (2 or 3) and optional authentication information, the command also returns server and connection properties.\n\nRedis 6 and later support two protocols: RESP2 and RESP3. RESP3 lets Redis return more semantic replies. For example, [HGETALL](https://redis.io/docs/latest/commands/hgetall) returns a [map](https://redis.io/docs/latest/develop/reference/protocol-spec#maps) instead of an [array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays), so a client library does not need command-specific logic to convert the reply into a hash before returning it to the caller.\n\nIn Redis 6+, connections start in RESP2 mode, so clients implementing RESP2 do\nnot need to updated or changed."},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>protover</code></summary>\n\nThe RESP protocol version to switch to (`2` or `3`). Required if any other option is given.\n\n</details>\n\n<details open><summary><code>AUTH username password</code></summary>\n\nAuthenticate the connection in addition to switching to the specified protocol version. This makes calling [`AUTH`](https://redis.io/docs/latest/commands/auth) before `HELLO` unnecessary when setting up a new connection. Note that the `username` can be set to \"default\" to authenticate against a server that does not use ACLs, but rather the simpler `requirepass` mechanism of Redis prior to version 6.\n\n</details>\n\n<details open><summary><code>SETNAME clientname</code></summary>\n\nSet the name of the current connection. This is equivalent to calling [`CLIENT SETNAME`](https://redis.io/docs/latest/commands/client-setname).\n\n</details>"},{"id":"details","title":"Details","role":"content","text":"`HELLO` always replies with a list of current server and connection properties,\nsuch as: version, loaded modules, client ID, replication role and so on.\nWhen called without any arguments in Redis 6.2 or greater and its default use of RESP2\nprotocol, the reply looks like this:\n\n    > HELLO\n     1) \"server\"\n     2) \"redis\"\n     3) \"version\"\n     4) \"8.8.0\"\n     5) \"proto\"\n     6) (integer) 2\n     7) \"id\"\n     8) (integer) 5\n     9) \"mode\"\n    10) \"standalone\"\n    11) \"role\"\n    12) \"master\"\n    13) \"modules\"\n    14) ...\n\nClients that want to handshake using the RESP3 mode need to call the `HELLO`\ncommand and specify the value \"3\" as the `protover` argument, like so:\n\n    > HELLO 3\n    1# \"server\" => \"redis\"\n    2# \"version\" => \"8.8.0\"\n    3# \"proto\" => (integer) 3\n    4# \"id\" => (integer) 10\n    5# \"mode\" => \"standalone\"\n    6# \"role\" => \"master\"\n    7# \"modules\" => ...\n\nBecause `HELLO` replies with useful information, and given that `protover` is\noptional or can be set to \"2\", client library authors may consider using this\ncommand instead of the canonical [`PING`](https://redis.io/docs/latest/commands/ping) when setting up the connection."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Map reply](../../develop/reference/protocol-spec#maps): a list of server properties.\n[Simple error reply](../../develop/reference/protocol-spec#simple-errors): if the `protover` requested does not exist.\n\n**RESP3:**\n\n[Map reply](../../develop/reference/protocol-spec#maps): a list of server properties.\n[Simple error reply](../../develop/reference/protocol-spec#simple-errors): if the `protover` requested does not exist."}],"examples":[]}
{"schema_version":2,"id":"commands/hexists","title":"HEXISTS","url":"https://redis.io/docs/latest/commands/hexists/","summary":"Determines whether a field exists in a hash.","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"23626859b6c0697eaa12d1fcf545ca55eeaac2e0612632a54f9156ea483eecf6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns if `field` is an existing field in the hash stored at `key`."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the hash.\n\n</details>\n\n<details open><summary><code>field</code></summary>\n\nThe field to check for existence.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> HSET myhash field1 \"foo\"\n(integer) 1\nredis> HEXISTS myhash field1\n(integer) 1\nredis> HEXISTS myhash field2\n(integer) 0"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0` if the hash does not contain the field, or the key does not exist.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `1` if the hash contains the field.\n\n**RESP3:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0` if the hash does not contain the field, or the key does not exist.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `1` if the hash contains the field."}],"examples":[]}
{"schema_version":2,"id":"commands/hexpire","title":"HEXPIRE","url":"https://redis.io/docs/latest/commands/hexpire/","summary":"Set expiration for hash fields using relative time to expire (seconds)","since":"7.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"2c4d1f77320c460afcb6bf3bdc4d01ef80f347aa33b0ac2567fe9d464eb6b363","sections":[{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the hash.\n\n</details>\n\n<details open><summary><code>seconds</code></summary>\n\nThe time to live, in seconds. Each specified field is deleted after this many seconds.\n\n</details>\n\n<details open><summary><code>FIELDS numfields field [field ...]</code></summary>\n\nThe hash fields to set expiration for. `numfields` is the number of fields, followed by that many field names.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"The following options modify the command's behavior. They are mutually exclusive.\n\n<details open><summary><code>NX</code></summary>\n\nFor each specified field, set expiration only when the field has no expiration.\n\n</details>\n\n<details open><summary><code>XX</code></summary>\n\nFor each specified field, set expiration only when the field has an existing expiration.\n\n</details>\n\n<details open><summary><code>GT</code></summary>\n\nFor each specified field, set expiration only when the new expiration is greater than the current one. A non-volatile field is treated as an infinite TTL for the purposes of `GT`.\n\n</details>\n\n<details open><summary><code>LT</code></summary>\n\nFor each specified field, set expiration only when the new expiration is less than the current one. A non-volatile field is treated as an infinite TTL for the purposes of `LT`.\n\n</details>"},{"id":"refreshing-expires","title":"Refreshing expires","role":"content","text":"You can call `HEXPIRE` using as argument a field that already has an\nexisting TTL set.\nIn this case, the time to live is _updated_ to the new value."},{"id":"redis-search-and-field-expiration","title":"Redis Search and field expiration","role":"content","text":"Starting with Redis 8, Redis Search has enhanced behavior when handling expiring hash fields. For detailed information about how [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) and [`FT.AGGREGATE`](https://redis.io/docs/latest/commands/ft.aggregate) commands interact with expiring hash fields, see [Key and field expiration behavior](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/expiration)."},{"id":"examples","title":"Examples","role":"example","text":"Field expiration: Set TTL on individual hash fields using HEXPIRE with conditional options (NX, XX, GT, LT) when you need fine-grained control over field lifecycle\n\n**Difficulty:** Intermediate\n\n**Commands:** HEXPIRE, HSET, HGETALL\n\n**Complexity:**\n- HEXPIRE: O(N)\n- HSET: O(1)\n- HGETALL: O(N)\n\n**Available in:** Redis CLI, C, C#, Go, Java, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Array reply](../../develop/reference/protocol-spec#arrays). For each field:\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `-2` if no such field exists in the provided hash key, or the provided key does not exist.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `0` if the specified NX | XX | GT | LT condition has not been met.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `1` if the expiration time was set/updated.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `2` when `HEXPIRE`/`HPEXPIRE` is called with 0 seconds/milliseconds or when `HEXPIREAT`/`HPEXPIREAT` is called with a past Unix time in seconds/milliseconds.\n* [Simple error reply](../../develop/reference/protocol-spec#simple-errors):\n    - if parsing failed, mandatory arguments are missing, unknown arguments are specified, or argument values are of the wrong type or out of range.\n    - if the provided key exists but is not a hash.\n\n**RESP3:**\n\nOne of the following:\n* [Array reply](../../develop/reference/protocol-spec#arrays). For each field:\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `-2` if no such field exists in the provided hash key, or the provided key does not exist.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `0` if the specified NX | XX | GT | LT condition has not been met.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `1` if the expiration time was set/updated.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `2` when `HEXPIRE`/`HPEXPIRE` is called with 0 seconds/milliseconds or when `HEXPIREAT`/`HPEXPIREAT` is called with a past Unix time in seconds/milliseconds.\n* [Simple error reply](../../develop/reference/protocol-spec#simple-errors):\n    - if parsing failed, mandatory arguments are missing, unknown arguments are specified, or argument values are of the wrong type or out of range.\n    - if the provided key exists but is not a hash."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> HEXPIRE no-key 20 NX FIELDS 2 field1 field2\n1) (integer) -2\n2) (integer) -2\n> HSET mykey field1 \"hello\" field2 \"world\"\n(integer) 2\n> HEXPIRE mykey 10 FIELDS 3 field1 field2 field3\n1) (integer) 1\n2) (integer) 1\n3) (integer) -2\n> HGETALL mykey","section_id":"examples"},{"id":"examples-ex1","language":"c","code":"reply = redisCommand(c, \"HSET myhash field1 Hello field2 World\");\n    freeReplyObject(reply);\n\n    // HEXPIRE needs the field count before the field names.\n    reply = redisCommand(c, \"HEXPIRE myhash 10 FIELDS 2 field1 field2\");\n    for (size_t i = 0; i < reply->elements; i++) {\n        printf(\"%lld\\n\", reply->element[i]->integer);\n    }\n    // >>> 1\n    // >>> 1\n    freeReplyObject(reply);\n\n    reply = redisCommand(c, \"HTTL myhash FIELDS 2 field1 field2\");\n    for (size_t i = 0; i < reply->elements; i++) {\n        printf(\"%lld\\n\", reply->element[i]->integer);\n    }\n    // >>> 10\n    // >>> 10\n    freeReplyObject(reply);\n\n    // -2 means the field does not exist.\n    reply = redisCommand(c, \"HEXPIRE myhash 10 FIELDS 1 nonexistent\");\n    printf(\"%lld\\n\", reply->element[0]->integer);\n    // >>> -2\n    freeReplyObject(reply);","section_id":"examples"},{"id":"examples-ex2","language":"csharp","code":"// Set up hash with fields\n        db.HashSet(\"myhash\",\n            [\n                new(\"field1\", \"Hello\"),\n                new(\"field2\", \"World\")\n            ]\n        );\n\n        ExpireResult[] hexpireRes1 = db.HashFieldExpire(\n            \"myhash\",\n            new RedisValue[] { \"field1\", \"field2\" },\n            TimeSpan.FromSeconds(10)\n        );\n        Console.WriteLine(string.Join(\", \", hexpireRes1));\n        // >>> Success, Success\n\n        long[] hexpireRes2 = db.HashFieldGetTimeToLive(\n            \"myhash\",\n            new RedisValue[] { \"field1\", \"field2\" }\n        );\n        Console.WriteLine(string.Join(\", \", hexpireRes2));\n        // >>> 10, 10 (approximately)\n\n        // Try to set expiration on non-existent field\n        ExpireResult[] hexpireRes3 = db.HashFieldExpire(\n            \"myhash\",\n            new RedisValue[] { \"nonexistent\" },\n            TimeSpan.FromSeconds(10)\n        );\n        Console.WriteLine(string.Join(\", \", hexpireRes3));\n        // >>> NoSuchField","section_id":"examples"},{"id":"examples-ex3","language":"go","code":"// Set up hash with fields\n\trdb.HSet(ctx, \"myhash\", \"field1\", \"Hello\", \"field2\", \"World\")\n\n\t// Set expiration on hash fields\n\tres1, err := rdb.HExpire(ctx, \"myhash\", 10*time.Second, \"field1\", \"field2\").Result()\n\n\tif err != nil {\n\t\tfmt.Println(err)\n\t}\n\n\tfmt.Println(res1) // >>> [1 1]\n\n\t// Check TTL of the fields\n\tres2, err := rdb.HTTL(ctx, \"myhash\", \"field1\", \"field2\").Result()\n\n\tif err != nil {\n\t\tfmt.Println(err)\n\t}\n\n\tfmt.Println(len(res2)) // >>> 2\n\n\t// Try to set expiration on non-existent field\n\tres3, err := rdb.HExpire(ctx, \"myhash\", 10*time.Second, \"nonexistent\").Result()\n\n\tif err != nil {\n\t\tfmt.Println(err)\n\t}\n\n\tfmt.Println(res3) // >>> [-2]\n\n\t// Clean up\n\trdb.Del(ctx, \"myhash\")","section_id":"examples"},{"id":"examples-ex4","language":"java","code":"// Set up a hash with two fields.\n            Map<String, String> hExpireFields = new HashMap<>();\n            hExpireFields.put(\"field1\", \"Hello\");\n            hExpireFields.put(\"field2\", \"World\");\n\n            syncCommands.hset(\"myhash\", hExpireFields);\n\n            // Set the expiration of both fields. `hexpire` returns a status\n            // code for each field, where 1 means the expiration was set.\n            List<Long> res16 = syncCommands.hexpire(\"myhash\", 10, \"field1\", \"field2\");\n            System.out.println(res16); // >>> [1, 1]\n\n            // Check the time to live of the fields.\n            List<Long> res17 = syncCommands.httl(\"myhash\", \"field1\", \"field2\");\n            System.out.println(res17); // >>> [10, 10]\n\n            // Try to set the expiration of a field that doesn't exist.\n            // The status code -2 means there's no such field.\n            List<Long> res18 = syncCommands.hexpire(\"myhash\", 10, \"nonexistent\");\n            System.out.println(res18); // >>> [-2]","section_id":"examples"},{"id":"examples-ex5","language":"java","code":"// Set up hash with fields\n            Map<String, String> hExpireExampleParams = new HashMap<>();\n            hExpireExampleParams.put(\"field1\", \"Hello\");\n            hExpireExampleParams.put(\"field2\", \"World\");\n\n            CompletableFuture<Void> hExpireExample = asyncCommands.hset(\"myhash\", hExpireExampleParams).thenCompose(res1 -> {\n                // Set expiration on hash fields\n                return asyncCommands.hexpire(\"myhash\", 10, \"field1\", \"field2\");\n            }).thenCompose(res2 -> {\n                System.out.println(res2);\n                // >>> [1, 1]\n                // Check TTL of the fields\n                return asyncCommands.httl(\"myhash\", \"field1\", \"field2\");\n            }).thenCompose(res3 -> {\n                System.out.println(res3.size());\n                // >>> 2\n                // Try to set expiration on non-existent field\n                return asyncCommands.hexpire(\"myhash\", 10, \"nonexistent\");\n            })\n                    .thenAccept(System.out::println)\n                    // >>> -2\n                    .toCompletableFuture();","section_id":"examples"},{"id":"examples-ex6","language":"java","code":"// Set up hash with fields\n            Map<String, String> hExpireExampleParams = new HashMap<>();\n            hExpireExampleParams.put(\"field1\", \"Hello\");\n            hExpireExampleParams.put(\"field2\", \"World\");\n\n            Mono<Long> hExpireExample1 = reactiveCommands.hset(\"myhash\", hExpireExampleParams).doOnNext(result -> {\n            });\n\n            hExpireExample1.block();\n\n            // Set expiration on hash fields\n            Mono<List<Long>> hExpireExample2 = reactiveCommands.hexpire(\"myhash\", 10, \"field1\", \"field2\").collectList().doOnNext(result -> {\n                System.out.println(result);\n                // >>> [1, 1]\n            });\n\n            hExpireExample2.block();\n\n            // Check TTL of the fields\n            Mono<List<Long>> hExpireExample3 = reactiveCommands.httl(\"myhash\", \"field1\", \"field2\").collectList().doOnNext(result -> {\n                System.out.println(result.size());\n                // >>> 2\n            });\n\n            hExpireExample3.block();\n\n            // Try to set expiration on non-existent field\n            Mono<List<Long>> hExpireExample4 = reactiveCommands.hexpire(\"myhash\", 10, \"nonexistent\").collectList().doOnNext(result -> {\n                System.out.println(result);\n                // >>> [-2]\n            });","section_id":"examples"},{"id":"examples-ex7","language":"java","code":"// Set up hash with fields\n        Map<String, String> hExpireExampleParams = new HashMap<>();\n        hExpireExampleParams.put(\"field1\", \"Hello\");\n        hExpireExampleParams.put(\"field2\", \"World\");\n        jedis.hset(\"myhash\", hExpireExampleParams);\n\n        // Set expiration on hash fields\n        List<Long> hExpireResult1 = jedis.hexpire(\"myhash\", 10, \"field1\", \"field2\");\n        System.out.println(hExpireResult1); // >>> [1, 1]\n\n        // Check TTL of the fields\n        List<Long> hExpireResult2 = jedis.httl(\"myhash\", \"field1\", \"field2\");\n        System.out.println(hExpireResult2.size()); // >>> 2\n\n        // Try to set expiration on non-existent field\n        List<Long> hExpireResult3 = jedis.hexpire(\"myhash\", 10, \"nonexistent\");\n        System.out.println(hExpireResult3); // >>> [-2]","section_id":"examples"},{"id":"examples-ex8","language":"javascript","code":"// Set up hash with fields\nawait client.hSet('myhash', {\n  'field1': 'Hello',\n  'field2': 'World'\n})\n\n// Set expiration on hash fields\nconst res14 = await client.hExpire('myhash', ['field1', 'field2'], 10)\nconsole.log(res14) // [1, 1]\n\n// Check TTL of the fields\nconst res15 = await client.hTTL('myhash', ['field1', 'field2'])\nconsole.log(res15) // [10, 10] (or close to 10)\n\n// Try to set expiration on non-existent field\nconst res16 = await client.hExpire('myhash', ['nonexistent'], 10)\nconsole.log(res16) // [-2]","section_id":"examples"},{"id":"examples-ex9","language":"javascript","code":"await redis.hset('myhash', { field1: 'Hello', field2: 'World' });\n\nconst hexpireRes1 = await redis.hexpire('myhash', 10, 'FIELDS', 2, 'field1', 'field2');\nconsole.log(hexpireRes1); // >>> [ 1, 1 ]\n\nconst hexpireRes2 = await redis.httl('myhash', 'FIELDS', 2, 'field1', 'field2');\nconsole.log(hexpireRes2); // >>> [ 10, 10 ]\n\n// -2 means the field does not exist.\nconst hexpireRes3 = await redis.hexpire('myhash', 10, 'FIELDS', 1, 'nonexistent');\nconsole.log(hexpireRes3); // >>> [ -2 ]","section_id":"examples"},{"id":"examples-ex10","language":"javascript","code":"// Set up hash with fields\nawait client.hSet('myhash', {\n  'field1': 'Hello',\n  'field2': 'World'\n})\n\n// Set expiration on hash fields\nconst res14 = await client.hExpire('myhash', ['field1', 'field2'], 10)\nconsole.log(res14) // [1, 1]\n\n// Check TTL of the fields\nconst res15 = await client.hTTL('myhash', ['field1', 'field2'])\nconsole.log(res15) // [10, 10] (or close to 10)\n\n// Try to set expiration on non-existent field\nconst res16 = await client.hExpire('myhash', ['nonexistent'], 10)\nconsole.log(res16) // [-2]","section_id":"examples"},{"id":"examples-ex11","language":"javascript","code":"await redis.hset('myhash', { field1: 'Hello', field2: 'World' });\n\nconst hexpireRes1 = await redis.hexpire('myhash', 10, 'FIELDS', 2, 'field1', 'field2');\nconsole.log(hexpireRes1); // >>> [ 1, 1 ]\n\nconst hexpireRes2 = await redis.httl('myhash', 'FIELDS', 2, 'field1', 'field2');\nconsole.log(hexpireRes2); // >>> [ 10, 10 ]\n\n// -2 means the field does not exist.\nconst hexpireRes3 = await redis.hexpire('myhash', 10, 'FIELDS', 1, 'nonexistent');\nconsole.log(hexpireRes3); // >>> [ -2 ]","section_id":"examples"},{"id":"examples-ex12","language":"php","code":"// Set up hash with fields\n        $hExpireResult1 = $this->redis->hmset('myhash', ['field1' => 'Hello', 'field2' => 'World']);\n        echo \"HMSET myhash field1 Hello field2 World: \" . ($hExpireResult1 ? 'OK' : 'FAIL') . \"\\n\"; // >>> OK\n\n        // Set expiration on hash fields\n        $hExpireResult2 = $this->redis->hexpire('myhash', 10, ['field1', 'field2']);\n        echo \"HEXPIRE myhash 10 FIELDS field1 field2: \" . json_encode($hExpireResult2) . \"\\n\"; // >>> [1,1]\n\n        // Check TTL of the fields\n        $hExpireResult3 = $this->redis->httl('myhash', ['field1', 'field2']);\n        echo \"HTTL myhash FIELDS field1 field2 count: \" . count($hExpireResult3) . \"\\n\"; // >>> 2\n\n        // Try to set expiration on non-existent field\n        $hExpireResult4 = $this->redis->hexpire('myhash', 10, ['nonexistent']);\n        echo \"HEXPIRE myhash 10 FIELDS nonexistent: \" . json_encode($hExpireResult4) . \"\\n\"; // >>> [-2]","section_id":"examples"},{"id":"examples-ex13","language":"python","code":"# Set up hash with fields\nr.hset(\"myhash\", mapping={\"field1\": \"Hello\", \"field2\": \"World\"})\n\n# Set expiration on hash fields\nres12 = r.hexpire(\"myhash\", 10, \"field1\", \"field2\")\nprint(res12)  # >>> [1, 1]\n\n# Check TTL of the fields\nres13 = r.httl(\"myhash\", \"field1\", \"field2\")\nprint(res13)  # >>> [10, 10] (or close to 10)\n\n# Try to set expiration on non-existent field\nres14 = r.hexpire(\"myhash\", 10, \"nonexistent\")\nprint(res14)  # >>> [-2]","section_id":"examples"},{"id":"examples-ex14","language":"ruby","code":"# Set up a hash with two fields.\nr.hset('myhash', { 'field1' => 'Hello', 'field2' => 'World' })\n\n# Set an expiration on both fields.\nres16 = r.hexpire('myhash', 10, 'field1', 'field2')\nputs res16.inspect # >>> [1, 1]\n\n# Check the TTL of the fields.\nres17 = r.httl('myhash', 'field1', 'field2')\nputs res17.inspect # >>> [10, 10]\n\n# Try to set an expiration on a field that does not exist.\nres18 = r.hexpire('myhash', 10, 'nonexistent')\nputs res18.inspect # >>> [-2]","section_id":"examples"},{"id":"examples-ex15","language":"rust","code":"// Set up hash with fields\n        let hash_fields = vec![(\"field1\", \"Hello\"), (\"field2\", \"World\")];\n        if let Ok(res) = r.hset_multiple(\"myhash\", &hash_fields).await {\n            let res: String = res;\n            println!(\"{res}\");    // >>> OK\n        }\n\n        // Set expiration on hash fields\n        match r.hexpire(\"myhash\", 10, redis::ExpireOption::NONE, &[\"field1\", \"field2\"]).await {\n            Ok(res1) => {\n                let res1: Vec<i32> = res1;\n                println!(\"{:?}\", res1);    // >>> [1, 1]\n            },\n            Err(e) => {\n                println!(\"Error setting expiration: {e}\");\n                return;\n            }\n        }\n\n        // Check TTL of the fields\n        match r.httl(\"myhash\", &[\"field1\", \"field2\"]).await {\n            Ok(res2) => {\n                let res2: Vec<i32> = res2;\n                println!(\"{}\", res2.len());    // >>> 2\n            },\n            Err(e) => {\n                println!(\"Error getting TTL: {e}\");\n                return;\n            }\n        }\n\n        // Try to set expiration on non-existent field\n        match r.hexpire(\"myhash\", 10, redis::ExpireOption::NONE, &[\"nonexistent\"]).await {\n            Ok(res3) => {\n                let res3: Vec<i32> = res3;\n                println!(\"{:?}\", res3);    // >>> [-2]\n            },\n            Err(e) => {\n                println!(\"Error setting expiration on non-existent field: {e}\");\n                return;\n            }\n        }","section_id":"examples"},{"id":"examples-ex16","language":"rust","code":"// Set up hash with fields\n        let hash_fields = vec![(\"field1\", \"Hello\"), (\"field2\", \"World\")];\n        if let Ok(res) = r.hset_multiple(\"myhash\", &hash_fields) {\n            let res: String = res;\n            println!(\"{res}\");    // >>> OK\n        }\n\n        // Set expiration on hash fields\n        match r.hexpire(\"myhash\", 10, redis::ExpireOption::NONE, &[\"field1\", \"field2\"]) {\n            Ok(res1) => {\n                let res1: Vec<i32> = res1;\n                println!(\"{:?}\", res1);    // >>> [1, 1]\n            },\n            Err(e) => {\n                println!(\"Error setting expiration: {e}\");\n                return;\n            }\n        }\n\n        // Check TTL of the fields\n        match r.httl(\"myhash\", &[\"field1\", \"field2\"]) {\n            Ok(res2) => {\n                let res2: Vec<i32> = res2;\n                println!(\"{}\", res2.len());    // >>> 2\n            },\n            Err(e) => {\n                println!(\"Error getting TTL: {e}\");\n                return;\n            }\n        }\n\n        // Try to set expiration on non-existent field\n        match r.hexpire(\"myhash\", 10, redis::ExpireOption::NONE, &[\"nonexistent\"]) {\n            Ok(res3) => {\n                let res3: Vec<i32> = res3;\n                println!(\"{:?}\", res3);    // >>> [-2]\n            },\n            Err(e) => {\n                println!(\"Error setting expiration on non-existent field: {e}\");\n                return;\n            }\n        }","section_id":"examples"}]}
{"schema_version":2,"id":"commands/hexpireat","title":"HEXPIREAT","url":"https://redis.io/docs/latest/commands/hexpireat/","summary":"Set expiration for hash fields using an absolute Unix timestamp (seconds)","since":"7.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"5bcb19aa93d11437625f9ff71ba4c02c8ce17ff8ea3d5b439be2df204ed01d08","sections":[{"id":"overview","title":"Overview","role":"overview","text":"`HEXPIREAT` has the same effect and semantics as [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire), but instead of\nspecifying the number of seconds for the TTL (time to live), it takes\nan absolute [Unix timestamp](http://en.wikipedia.org/wiki/Unix_time) in seconds since Unix epoch. A\ntimestamp in the past will delete the field immediately.\n\nFor the specific semantics of the command, see [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire)."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the hash.\n\n</details>\n\n<details open><summary><code>unix-time-seconds</code></summary>\n\nThe absolute Unix expiration timestamp, in seconds since Unix epoch. A timestamp in the past deletes the field immediately.\n\n</details>\n\n<details open><summary><code>FIELDS numfields field [field ...]</code></summary>\n\nThe hash fields to set expiration for. `numfields` is the number of fields, followed by that many field names.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"The following options modify the command's behavior. They are mutually exclusive.\n\n<details open><summary><code>NX</code></summary>\n\nFor each specified field, set expiration only when the field has no expiration.\n\n</details>\n\n<details open><summary><code>XX</code></summary>\n\nFor each specified field, set expiration only when the field has an existing expiration.\n\n</details>\n\n<details open><summary><code>GT</code></summary>\n\nFor each specified field, set expiration only when the new expiration is greater than the current one. A non-volatile key is treated as an infinite TTL for the purposes of `GT`.\n\n</details>\n\n<details open><summary><code>LT</code></summary>\n\nFor each specified field, set expiration only when the new expiration is less than the current one. A non-volatile key is treated as an infinite TTL for the purposes of `LT`.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Array reply](../../develop/reference/protocol-spec#arrays). For each field:\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `-2` if no such field exists in the provided hash key, or the provided key does not exist.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `0` if the specified NX, XX, GT, or LT condition has not been met.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `1` if the expiration time was set/updated.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `2` when `HEXPIRE` or `HPEXPIRE` is called with 0 seconds or milliseconds, or when `HEXPIREAT` or `HPEXPIREAT` is called with a past Unix time in seconds or milliseconds.\n* [Simple error reply](../../develop/reference/protocol-spec#simple-errors):\n    - if parsing failed, mandatory arguments are missing, unknown arguments are specified, or argument values are of the wrong type or out of range.\n    - if the provided key exists but is not a hash.\n\n**RESP3:**\n\nOne of the following:\n* [Array reply](../../develop/reference/protocol-spec#arrays). For each field:\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `-2` if no such field exists in the provided hash key, or the provided key does not exist.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `0` if the specified NX, XX, GT, or LT condition has not been met.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `1` if the expiration time was set/updated.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `2` when `HEXPIRE` or `HPEXPIRE` is called with 0 seconds or milliseconds, or when `HEXPIREAT` or `HPEXPIREAT` is called with a past Unix time in seconds or milliseconds.\n* [Simple error reply](../../develop/reference/protocol-spec#simple-errors):\n    - if parsing failed, mandatory arguments are missing, unknown arguments are specified, or argument values are of the wrong type or out of range.\n    - if the provided key exists but is not a hash."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> HSET mykey field1 \"hello\" field2 \"world\"\n(integer) 2\nredis> HEXPIREAT mykey 1715704971 FIELDS 2 field1 field2\n1) (integer) 1\n2) (integer) 1\nredis> HTTL mykey FIELDS 2 field1 field2\n1) (integer) 567\n2) (integer) 567","section_id":"examples"}]}
{"schema_version":2,"id":"commands/hexpiretime","title":"HEXPIRETIME","url":"https://redis.io/docs/latest/commands/hexpiretime/","summary":"Returns the expiration time of a hash field as a Unix timestamp, in seconds.","since":"7.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"86f15f141ed99f054855cd3b97d0fd7049cad20afc99b63f9ec061829e7d5403","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns the absolute Unix timestamp in seconds since Unix epoch at which the given key's field(s) will expire.\n\nSee also the [`HPEXPIRETIME`](https://redis.io/docs/latest/commands/hpexpiretime) command, which returns the same information with millisecond resolution."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the hash.\n\n</details>\n\n<details open><summary><code>FIELDS numfields field [field ...]</code></summary>\n\nThe hash fields to retrieve the expiration time for. `numfields` is the number of fields, followed by that many field names.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> HSET mykey field1 \"hello\" field2 \"world\"\n(integer) 2\nredis> HEXPIRE mykey 300  FIELDS 2 field1 field2\n1) (integer) 1\n2) (integer) 1\nredis> HEXPIRETIME mykey FIELDS 2 field1 field2\n1) (integer) 1715705914\n2) (integer) 1715705914"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n* [Array reply](../../develop/reference/protocol-spec#arrays). For each field:\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `-2` if no such field exists in the provided hash key, or the provided key does not exist.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `-1` if the field exists but has no associated expiration set.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): the expiration (Unix timestamp) in seconds.\n\n**RESP3:**\n\n* [Array reply](../../develop/reference/protocol-spec#arrays). For each field:\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `-2` if no such field exists in the provided hash key, or the provided key does not exist.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `-1` if the field exists but has no associated expiration set.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): the expiration (Unix timestamp) in seconds."}],"examples":[]}
{"schema_version":2,"id":"commands/hget","title":"HGET","url":"https://redis.io/docs/latest/commands/hget/","summary":"Returns the value of a field in a hash.","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-08T12:05:04+02:00","page_type":"content","content_hash":"d0d4bd081067dac6fe7c9804c07cf31f004fa5418d871a573a047ece0bd6fc0a","sections":[{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the hash.\n\n</details>\n\n<details open><summary><code>field</code></summary>\n\nThe field whose value to retrieve.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"Foundational: Retrieve a single field value from a hash using HGET (returns nil if field or key doesn't exist)\n\n**Difficulty:** Beginner\n\n**Commands:** HSET, HGET\n\n**Complexity:**\n- HSET: O(1)\n- HGET: O(1)\n\n**Available in:** Redis CLI, C, C#, Go, Java, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): The value associated with the field.\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): If the field is not present in the hash or key does not exist.\n\n**RESP3:**\n\nOne of the following:\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): The value associated with the field.\n* [Null reply](../../develop/reference/protocol-spec#nulls): If the field is not present in the hash or key does not exist."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> HSET myhash field1 \"foo\"\n(integer) 1\n> HGET myhash field1\n\"foo\"\n> HGET myhash field2\n(nil)","section_id":"examples"},{"id":"examples-ex1","language":"c","code":"reply = redisCommand(c, \"HSET myhash field1 foo\");\n    printf(\"%lld\\n\", reply->integer);\n    // >>> 1\n    freeReplyObject(reply);\n\n    reply = redisCommand(c, \"HGET myhash field1\");\n    printf(\"%s\\n\", reply->str);\n    // >>> foo\n    freeReplyObject(reply);\n\n    // A field that does not exist gives a nil reply, not an empty string.\n    reply = redisCommand(c, \"HGET myhash field2\");\n    printf(\"%s\\n\", reply->type == REDIS_REPLY_NIL ? \"(nil)\" : reply->str);\n    // >>> (nil)\n    freeReplyObject(reply);","section_id":"examples"},{"id":"examples-ex2","language":"csharp","code":"bool hgetRes1 = db.HashSet(\"myhash\", \"field1\", \"foo\");\n\n        RedisValue hgetRes2 = db.HashGet(\"myhash\", \"field1\");\n        Console.WriteLine(hgetRes2);    // >>> foo\n\n        RedisValue hgetRes3 = db.HashGet(\"myhash\", \"field2\");\n        Console.WriteLine(hgetRes3);    // >>> Null","section_id":"examples"},{"id":"examples-ex3","language":"go","code":"res7, err := rdb.HSet(ctx, \"myhash\", \"field1\", \"foo\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res7) // >>> 1\n\n\tres8, err := rdb.HGet(ctx, \"myhash\", \"field1\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res8) // >>> foo\n\n\tres9, err := rdb.HGet(ctx, \"myhash\", \"field2\").Result()\n\n\tif err != nil {\n\t\tfmt.Println(err)\n\t}\n\n\tfmt.Println(res9) // >>> <empty string>","section_id":"examples"},{"id":"examples-ex4","language":"java","code":"// `hset` returns true because `field1` is a new field.\n            boolean res10 = syncCommands.hset(\"myhash\", \"field1\", \"foo\");\n            System.out.println(res10); // >>> true\n\n            String res11 = syncCommands.hget(\"myhash\", \"field1\");\n            System.out.println(res11); // >>> foo\n\n            // `hget` returns null because `field2` doesn't exist.\n            String res12 = syncCommands.hget(\"myhash\", \"field2\");\n            System.out.println(res12); // >>> null","section_id":"examples"},{"id":"examples-ex5","language":"java","code":"Map<String, String> hGetExampleParams = new HashMap<>();\n            hGetExampleParams.put(\"field1\", \"foo\");\n\n            CompletableFuture<Void> hGetExample = asyncCommands.hset(\"myhash\", hGetExampleParams).thenCompose(res1 -> {\n                System.out.println(res1); // >>> 1\n                return asyncCommands.hget(\"myhash\", \"field1\");\n            }).thenCompose(res2 -> {\n                System.out.println(res2); // >>> foo\n                return asyncCommands.hget(\"myhash\", \"field2\");\n            }).thenAccept(res3 -> {\n                System.out.println(res3); // >>> null\n            }).toCompletableFuture();","section_id":"examples"},{"id":"examples-ex6","language":"java","code":"Map<String, String> hGetExampleParams = new HashMap<>();\n            hGetExampleParams.put(\"field1\", \"foo\");\n\n            Mono<Long> hGetExample1 = reactiveCommands.hset(\"myhash\", hGetExampleParams).doOnNext(result -> {\n                System.out.println(result); // >>> 1\n            });\n\n            hGetExample1.block();\n\n            Mono<String> hGetExample2 = reactiveCommands.hget(\"myhash\", \"field1\").doOnNext(result -> {\n                System.out.println(result); // >>> foo\n            });\n\n            hGetExample2.block();\n\n            Mono<String> hGetExample3 = reactiveCommands.hget(\"myhash\", \"field2\").doOnNext(result -> {\n                System.out.println(result); // >>> null\n            });","section_id":"examples"},{"id":"examples-ex7","language":"java","code":"Map<String, String> hGetExampleParams = new HashMap<>();\n        hGetExampleParams.put(\"field1\", \"foo\");\n\n        long hGetResult1 = jedis.hset(\"myhash\", hGetExampleParams);\n        System.out.println(hGetResult1);    // >>> 1\n\n        String hGetResult2 = jedis.hget(\"myhash\", \"field1\");\n        System.out.println(hGetResult2);    // >>> foo\n\n        String hGetResult3 = jedis.hget(\"myhash\", \"field2\");\n        System.out.println(hGetResult3);    // >>> null","section_id":"examples"},{"id":"examples-ex8","language":"javascript","code":"const res7 = await client.hSet('myhash', 'field1', 'foo')\nconsole.log(res7) // 1\n\nconst res8 = await client.hGet('myhash', 'field1')\nconsole.log(res8) // foo\n\nconst res9 = await client.hGet('myhash', 'field2')\nconsole.log(res9) // null","section_id":"examples"},{"id":"examples-ex9","language":"javascript","code":"const hgetRes1 = await redis.hset('myhash', 'field1', 'foo');\nconsole.log(hgetRes1); // >>> 1\n\nconst hgetRes2 = await redis.hget('myhash', 'field1');\nconsole.log(hgetRes2); // >>> foo\n\n// A field that does not exist reads back as null.\nconst hgetRes3 = await redis.hget('myhash', 'field2');\nconsole.log(hgetRes3); // >>> null","section_id":"examples"},{"id":"examples-ex10","language":"javascript","code":"const res7 = await client.hSet('myhash', 'field1', 'foo')\nconsole.log(res7) // 1\n\nconst res8 = await client.hGet('myhash', 'field1')\nconsole.log(res8) // foo\n\nconst res9 = await client.hGet('myhash', 'field2')\nconsole.log(res9) // null","section_id":"examples"},{"id":"examples-ex11","language":"javascript","code":"const hgetRes1 = await redis.hset('myhash', 'field1', 'foo');\nconsole.log(hgetRes1); // >>> 1\n\nconst hgetRes2 = await redis.hget('myhash', 'field1');\nconsole.log(hgetRes2); // >>> foo\n\n// A field that does not exist reads back as null.\nconst hgetRes3 = await redis.hget('myhash', 'field2');\nconsole.log(hgetRes3); // >>> null","section_id":"examples"},{"id":"examples-ex12","language":"php","code":"$hGetResult1 = $this->redis->hset('myhash', 'field1', 'foo');\n        echo \"HSET myhash field1 foo: \" . $hGetResult1 . \"\\n\"; // >>> 1\n\n        $hGetResult2 = $this->redis->hget('myhash', 'field1');\n        echo \"HGET myhash field1: \" . ($hGetResult2 ?? 'null') . \"\\n\"; // >>> foo\n\n        $hGetResult3 = $this->redis->hget('myhash', 'field2');\n        echo \"HGET myhash field2: \" . ($hGetResult3 ?? 'null') . \"\\n\"; // >>> null","section_id":"examples"},{"id":"examples-ex13","language":"python","code":"res7 = r.hset(\"myhash\", \"field1\", \"foo\")\nprint(res7)\n# >>> 1\n\nres8 = r.hget(\"myhash\", \"field1\")\nprint(res8)\n# >>> foo\n\nres9 = r.hget(\"myhash\", \"field2\")\nprint(res9)\n# >>> None","section_id":"examples"},{"id":"examples-ex14","language":"ruby","code":"res10 = r.hset('myhash', 'field1', 'foo')\nputs res10 # >>> 1\n\nres11 = r.hget('myhash', 'field1')\nputs res11 # >>> foo\n\nres12 = r.hget('myhash', 'field2')\nputs res12.inspect # >>> nil","section_id":"examples"},{"id":"examples-ex15","language":"rust","code":"match r.hset(\"myhash\", \"field1\", \"foo\").await {\n            Ok(res7) => {\n                let res7: i32 = res7;\n                println!(\"{res7}\");    // >>> 1\n            },\n            Err(e) => {\n                println!(\"Error setting hash field: {e}\");\n                return;\n            }\n        }\n\n        match r.hget(\"myhash\", \"field1\").await {\n            Ok(res8) => {\n                let res8: Option<String> = res8;\n                match res8 {\n                    Some(value) => {\n                        println!(\"{value}\");    // >>> foo\n                    },\n                    None => {\n                        println!(\"None\");\n                    }\n                }\n            },\n            Err(e) => {\n                println!(\"Error getting hash field: {e}\");\n                return;\n            }\n        }\n\n        match r.hget(\"myhash\", \"field2\").await {\n            Ok(res9) => {\n                let res9: Option<String> = res9;\n                match res9 {\n                    Some(_) => {\n                        println!(\"Some value\");\n                    },\n                    None => {\n                        println!(\"None\");    // >>> None\n                    }\n                }\n            },\n            Err(e) => {\n                println!(\"Error getting hash field: {e}\");\n                return;\n            }\n        }","section_id":"examples"},{"id":"examples-ex16","language":"rust","code":"match r.hset(\"myhash\", \"field1\", \"foo\") {\n            Ok(res7) => {\n                let res7: i32 = res7;\n                println!(\"{res7}\");    // >>> 1\n            },\n            Err(e) => {\n                println!(\"Error setting hash field: {e}\");\n                return;\n            }\n        }\n\n        match r.hget(\"myhash\", \"field1\") {\n            Ok(res8) => {\n                let res8: Option<String> = res8;\n                match res8 {\n                    Some(value) => {\n                        println!(\"{value}\");    // >>> foo\n                    },\n                    None => {\n                        println!(\"None\");\n                    }\n                }\n            },\n            Err(e) => {\n                println!(\"Error getting hash field: {e}\");\n                return;\n            }\n        }\n\n        match r.hget(\"myhash\", \"field2\") {\n            Ok(res9) => {\n                let res9: Option<String> = res9;\n                match res9 {\n                    Some(_) => {\n                        println!(\"Some value\");\n                    },\n                    None => {\n                        println!(\"None\");    // >>> None\n                    }\n                }\n            },\n            Err(e) => {\n                println!(\"Error getting hash field: {e}\");\n                return;\n            }\n        }","section_id":"examples"}]}
{"schema_version":2,"id":"commands/hgetall","title":"HGETALL","url":"https://redis.io/docs/latest/commands/hgetall/","summary":"Returns all fields and values in a hash.","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-08T12:05:04+02:00","page_type":"content","content_hash":"9b5b374fc74b3910ca50044b548ced1064301e210e8b773507883c722c919af8","sections":[{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the hash.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"Foundational: Retrieve all fields and values from a hash using HGETALL (returns alternating field-value pairs, useful for loading entire hash data)\n\n**Difficulty:** Beginner\n\n**Commands:** HSET, HGETALL\n\n**Complexity:**\n- HSET: O(1)\n- HGETALL: O(N)\n\n**Available in:** Redis CLI, C, C#, Go, Java, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of fields and their values, or an empty list when key does not exist.\n\n**RESP3:**\n\n[Map reply](../../develop/reference/protocol-spec#maps): a map of fields and their values, or an empty list when key does not exist."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> HSET myhash field1 \"Hello\"\n(integer) 1\nredis> HSET myhash field2 \"World\"\n(integer) 1\nredis> HGETALL myhash\n1) \"field1\"\n2) \"Hello\"\n3) \"field2\"\n4) \"World\"","section_id":"examples"},{"id":"examples-ex1","language":"c","code":"reply = redisCommand(c, \"HSET myhash field1 Hello field2 World\");\n    freeReplyObject(reply);\n\n    reply = redisCommand(c, \"HGETALL myhash\");\n    for (size_t i = 0; i < reply->elements; i += 2) {\n        printf(\"%s=%s\\n\", reply->element[i]->str, reply->element[i + 1]->str);\n    }\n    // >>> field1=Hello\n    // >>> field2=World\n    freeReplyObject(reply);","section_id":"examples"},{"id":"examples-ex2","language":"csharp","code":"db.HashSet(\"myhash\",\n            [\n                new(\"field1\", \"Hello\"),\n                new(\"field2\", \"World\")\n            ]\n        );\n\n        HashEntry[] hGetAllResult = db.HashGetAll(\"myhash\");\n        Array.Sort(hGetAllResult, (a1, a2) => a1.Name.CompareTo(a2.Name));\n        Console.WriteLine(\n            string.Join(\", \", hGetAllResult.Select(e => $\"{e.Name}: {e.Value}\"))\n        );\n        // >>> field1: Hello, field2: World","section_id":"examples"},{"id":"examples-ex3","language":"go","code":"hGetAllResult1, err := rdb.HSet(ctx, \"myhash\",\n\t\t\"field1\", \"Hello\",\n\t\t\"field2\", \"World\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(hGetAllResult1) // >>> 2\n\n\thGetAllResult2, err := rdb.HGetAll(ctx, \"myhash\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tkeys := make([]string, 0, len(hGetAllResult2))\n\n\tfor key, _ := range hGetAllResult2 {\n\t\tkeys = append(keys, key)\n\t}\n\n\tsort.Strings(keys)\n\n\tfor _, key := range keys {\n\t\tfmt.Printf(\"Key: %v, value: %v\\n\", key, hGetAllResult2[key])\n\t}\n\t// >>> Key: field1, value: Hello\n\t// >>> Key: field2, value: World","section_id":"examples"},{"id":"examples-ex4","language":"java","code":"Map<String, String> hGetAllFields = new HashMap<>();\n            hGetAllFields.put(\"field1\", \"Hello\");\n            hGetAllFields.put(\"field2\", \"World\");\n\n            syncCommands.hset(\"myhash\", hGetAllFields);\n\n            // `hgetall` returns a `Map`, whose iteration order isn't\n            // guaranteed. Wrap it in a `TreeMap` to sort the fields by name.\n            Map<String, String> res14 = syncCommands.hgetall(\"myhash\");\n            System.out.println(new TreeMap<>(res14));\n            // >>> {field1=Hello, field2=World}","section_id":"examples"},{"id":"examples-ex5","language":"java","code":"Map<String, String> hGetAllExampleParams = new HashMap<>();\n            hGetAllExampleParams.put(\"field1\", \"Hello\");\n            hGetAllExampleParams.put(\"field2\", \"World\");\n\n            CompletableFuture<Void> hGetAllExample = asyncCommands.hset(\"myhash\", hGetAllExampleParams).thenCompose(res1 -> {\n                return asyncCommands.hgetall(\"myhash\");\n            }).thenAccept(res2 -> {\n                System.out.println(res2);\n                // >>> {field1=Hello, field2=World}\n            }).toCompletableFuture();","section_id":"examples"},{"id":"examples-ex6","language":"java","code":"Map<String, String> hGetAllExampleParams = new HashMap<>();\n            hGetAllExampleParams.put(\"field1\", \"Hello\");\n            hGetAllExampleParams.put(\"field2\", \"World\");\n\n            Mono<Long> hGetAllExample1 = reactiveCommands.hset(\"myhash\", hGetAllExampleParams).doOnNext(result -> {\n            });\n\n            hGetAllExample1.block();\n\n            Mono<Map<String, String>> hGetAllExample2 = reactiveCommands.hgetall(\"myhash\").collectMap(\n                    KeyValue::getKey, KeyValue::getValue\n            ).doOnNext(result -> {\n                System.out.println(result);\n                // >>> {field1=Hello, field2=World}\n            });","section_id":"examples"},{"id":"examples-ex7","language":"java","code":"Map<String, String> hGetAllExampleParams = new HashMap<>();\n        hGetAllExampleParams.put(\"field1\", \"Hello\");\n        hGetAllExampleParams.put(\"field2\", \"World\");\n\n        long hGetAllResult1 = jedis.hset(\"myhash\", hGetAllExampleParams);\n        System.out.println(hGetAllResult1); // >>> 2\n\n        Map<String, String> hGetAllResult2 = jedis.hgetAll(\"myhash\");\n        System.out.println(\n            hGetAllResult2.entrySet().stream()\n                    .sorted((s1, s2)-> s1.getKey().compareTo(s2.getKey()))\n                    .collect(toList())\n                    .toString()\n        );\n        // >>> [field1=Hello, field2=World]","section_id":"examples"},{"id":"examples-ex8","language":"javascript","code":"const res10 = await client.hSet(\n  'myhash',\n  {\n    'field1': 'Hello',\n    'field2': 'World'\n  }\n)\n\nconst res11 = await client.hGetAll('myhash')\nconsole.log(res11) // [Object: null prototype] { field1: 'Hello', field2: 'World' }","section_id":"examples"},{"id":"examples-ex9","language":"javascript","code":"await redis.hset('myhash', { field1: 'Hello', field2: 'World' });\n\nconst hgetallRes = await redis.hgetall('myhash');\nconsole.log(hgetallRes); // >>> { field1: 'Hello', field2: 'World' }","section_id":"examples"},{"id":"examples-ex10","language":"javascript","code":"const res10 = await client.hSet(\n  'myhash',\n  {\n    'field1': 'Hello',\n    'field2': 'World'\n  }\n)\n\nconst res11 = await client.hGetAll('myhash')\nconsole.log(res11) // [Object: null prototype] { field1: 'Hello', field2: 'World' }","section_id":"examples"},{"id":"examples-ex11","language":"javascript","code":"await redis.hset('myhash', { field1: 'Hello', field2: 'World' });\n\nconst hgetallRes = await redis.hgetall('myhash');\nconsole.log(hgetallRes); // >>> { field1: 'Hello', field2: 'World' }","section_id":"examples"},{"id":"examples-ex12","language":"php","code":"$hGetAllResult1 = $this->redis->hmset('myhash', ['field1' => 'Hello', 'field2' => 'World']);\n        echo \"HMSET myhash field1 Hello field2 World: \" . ($hGetAllResult1 ? 'OK' : 'FAIL') . \"\\n\"; // >>> OK\n\n        $hGetAllResult2 = $this->redis->hgetall('myhash');\n        echo \"HGETALL myhash: \" . json_encode($hGetAllResult2) . \"\\n\"; // >>> {\"field1\":\"Hello\",\"field2\":\"World\"}","section_id":"examples"},{"id":"examples-ex13","language":"python","code":"res10 = r.hset(\"myhash\", mapping={\"field1\": \"Hello\", \"field2\": \"World\"})\n\nres11 = r.hgetall(\"myhash\")\nprint(res11) # >>> { \"field1\": \"Hello\", \"field2\": \"World\" }","section_id":"examples"},{"id":"examples-ex14","language":"ruby","code":"r.hset('myhash', { 'field1' => 'Hello', 'field2' => 'World' })\n\nres14 = r.hgetall('myhash')\nputs res14.inspect # >>> {\"field1\"=>\"Hello\", \"field2\"=>\"World\"}","section_id":"examples"},{"id":"examples-ex15","language":"rust","code":"let hash_fields = [\n            (\"field1\", \"Hello\"),\n            (\"field2\", \"World\"),\n        ];\n\n        if let Ok(_) = r.hset_multiple::<&str, &str, &str, String>(\"myhash\", &hash_fields).await {\n            // Fields set successfully\n        }\n\n        match r.hgetall(\"myhash\").await {\n            Ok(res11) => {\n                let res11: HashMap<String, String> = res11;\n                println!(\"{res11:?}\");    // >>> {\"field1\": \"Hello\", \"field2\": \"World\"}\n            },\n            Err(e) => {\n                println!(\"Error getting all hash fields: {e}\");\n                return;\n            }\n        }","section_id":"examples"},{"id":"examples-ex16","language":"rust","code":"let hash_fields = [\n            (\"field1\", \"Hello\"),\n            (\"field2\", \"World\"),\n        ];\n\n        if let Ok(_) = r.hset_multiple::<&str, &str, &str, String>(\"myhash\", &hash_fields) {\n            // Fields set successfully\n        }\n\n        match r.hgetall(\"myhash\") {\n            Ok(res11) => {\n                let res11: HashMap<String, String> = res11;\n                println!(\"{res11:?}\");    // >>> {\"field1\": \"Hello\", \"field2\": \"World\"}\n            },\n            Err(e) => {\n                println!(\"Error getting all hash fields: {e}\");\n                return;\n            }\n        }","section_id":"examples"}]}
{"schema_version":2,"id":"commands/hgetdel","title":"HGETDEL","url":"https://redis.io/docs/latest/commands/hgetdel/","summary":"Returns the value of a field and deletes it from the hash.","since":"8.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-06-15T06:26:34-07:00","page_type":"content","content_hash":"2281b518c1a5ecc5441fbb5e2129fdcba47265222f074482cb881ca197768e71","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Get and delete the value of one or more fields of a given hash key. When the last field is deleted, the key will also be deleted."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the hash.\n\n</details>\n\n<details open><summary><code>FIELDS numfields field [field ...]</code></summary>\n\nThe hash fields to get and then delete. `numfields` is the number of fields, followed by that many field names.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of deleted fields and their values or `nil` for fields that do not exist.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of deleted fields and their values or `nil` for fields that do not exist."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> HSET mykey field1 \"Hello\" field2 \"World\" field3 \"!\"\n(integer) 3\nredis> HGETALL mykey\n1) \"field1\"\n2) \"Hello\"\n3) \"field2\"\n4) \"World\"\n5) \"field3\"\n6) \"!\"\nredis> HGETDEL mykey FIELDS 2 field3 field4\n1) \"!\"\n2) (nil)\nredis> HGETALL mykey\n1) \"field1\"\n2) \"Hello\"\n3) \"field2\"\n4) \"World\"\nredis> HGETDEL mykey FIELDS 2 field1 field2\n1) \"Hello\"\n2) \"World\"\nredis> KEYS *\n(empty array)","section_id":"examples"}]}
{"schema_version":2,"id":"commands/hgetex","title":"HGETEX","url":"https://redis.io/docs/latest/commands/hgetex/","summary":"Get the value of one or more fields of a given hash key, and optionally set their expiration.","since":"8.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-06-15T06:26:34-07:00","page_type":"content","content_hash":"bb21edbff009a32fc04e29651bb81d814d6ff572952fe9ddf7899a2a6a48cc40","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Get the value of one or more fields of a given hash key and optionally set their expiration time or time-to-live (TTL)."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the hash.\n\n</details>\n\n<details open><summary><code>FIELDS numfields field [field ...]</code></summary>\n\nThe hash fields to retrieve. `numfields` is the number of fields, followed by that many field names.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"The following options modify the command's behavior. They are mutually exclusive.\n\n<details open><summary><code>EX seconds</code></summary>\n\nSet the specified expiration time, in seconds.\n\n</details>\n\n<details open><summary><code>PX milliseconds</code></summary>\n\nSet the specified expiration time, in milliseconds.\n\n</details>\n\n<details open><summary><code>EXAT unix-time-seconds</code></summary>\n\nSet the specified Unix time at which the fields will expire, in seconds.\n\n</details>\n\n<details open><summary><code>PXAT unix-time-milliseconds</code></summary>\n\nSet the specified Unix time at which the fields will expire, in milliseconds.\n\n</details>\n\n<details open><summary><code>PERSIST</code></summary>\n\nRemove the TTL associated with the fields.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n* [Array reply](../../develop/reference/protocol-spec#arrays): a list of values associated with the given fields, in the same order as they are requested.\n\n**RESP3:**\n\n* [Array reply](../../develop/reference/protocol-spec#arrays): a list of values associated with the given fields, in the same order as they are requested."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> HSET mykey field1 \"Hello\" field2 \"World\"\n(integer) 2\nredis> HGETEX mykey EX 120 FIELDS 1 field1\n1) \"Hello\"\nredis> HGETEX mykey EX 100 FIELDS 1 field2\n1) \"World\"\nredis> HTTL mykey FIELDS 2 field1 field2\n1) (integer) 91\n2) (integer) 85\nredis> HTTL mykey FIELDS 3 field1 field2 field3 \n1) (integer) 75\n2) (integer) 68\n3) (integer) -2\n...\nredis> HTTL mykey FIELDS 3 field1 field2 \n1) (integer) -2\n2) (integer) -2\nredis> HGETALL mykey\n(empty array)","section_id":"examples"}]}
{"schema_version":2,"id":"commands/himport","title":"HIMPORT","url":"https://redis.io/docs/latest/commands/himport/","summary":"A container for session-based hash import commands using fieldsets.","since":"8.10.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"d332af87e1da884b4a29a6ca21cd766e48165c96a48606640b5de03999e78fc6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"`HIMPORT` is a container command for session-based bulk hash import. Its subcommands let a client import many hashes that share the same field names efficiently by declaring those field names once and then sending only the values for each key.\n\nThe subcommands are:\n\n- [`HIMPORT PREPARE`](https://redis.io/docs/latest/commands/himport-prepare) — define a session-local fieldset that names an ordered list of field names.\n- [`HIMPORT SET`](https://redis.io/docs/latest/commands/himport-set) — create a hash from a prepared fieldset and a list of values.\n- [`HIMPORT DISCARD`](https://redis.io/docs/latest/commands/himport-discard) — remove a single fieldset by name.\n- [`HIMPORT DISCARDALL`](https://redis.io/docs/latest/commands/himport-discardall) — remove every fieldset held by the connection."},{"id":"details","title":"Details","role":"content","text":"A *fieldset* is a named, ordered list of field names that is scoped to the client connection: it is not visible to other clients and is discarded when the connection closes or the client issues the [`RESET`](https://redis.io/docs/latest/commands/reset) command. A connection can prepare several fieldsets, each under its own name, and refer to them by name in later commands.\n\nThe typical workflow is to declare a fieldset once with [`HIMPORT PREPARE`](https://redis.io/docs/latest/commands/himport-prepare), then create many keys from it with [`HIMPORT SET`](https://redis.io/docs/latest/commands/himport-set), sending only the values each time:\n\n[code example]\n\nBecause every key created this way shares one fixed set of field names, `HIMPORT` reduces the network traffic and per-command work compared with running [`HSET`](https://redis.io/docs/latest/commands/hset) once per key. Redis also uses the shared field names as a hint to store the keys as [compact hashes](https://redis.io/docs/latest/develop/data-types/hashes#compact-hashes), an internal encoding that keeps a single copy of the field names and reduces memory when many keys share the same layout. This encoding does not change the behavior or replies of any existing hash command."}],"examples":[{"id":"details-ex0","language":"plaintext","code":"HIMPORT PREPARE u name email age\nHIMPORT SET user:1 u alice a@example.com 30\nHIMPORT SET user:2 u bob b@example.com 25","section_id":"details"}]}
{"schema_version":2,"id":"commands/himport-discard","title":"HIMPORT DISCARD","url":"https://redis.io/docs/latest/commands/himport-discard/","summary":"Removes a single session-local fieldset by name.","since":"8.10.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"c114ad37c74084e6aba3a4023235295d2009a2b142e3fd253ae75a2a1e45bdef","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Removes a single session-local fieldset, previously defined with [`HIMPORT PREPARE`](https://redis.io/docs/latest/commands/himport-prepare), from the current connection."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>fieldset-name</code></summary>\n\nThe name of the fieldset to remove from the current connection.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> HIMPORT PREPARE u name email age\nOK\nredis> HIMPORT DISCARD u\n(integer) 1\nredis> HIMPORT DISCARD u\n(integer) 0"},{"id":"details","title":"Details","role":"content","text":"Discarding a fieldset only removes the fieldset itself; it does not affect any hashes already created from it with [`HIMPORT SET`](https://redis.io/docs/latest/commands/himport-set).\n\nUse `HIMPORT DISCARD` to free up resources on the connection once a bulk-ingestion job that used a given fieldset has finished. If you don't discard it, the fieldset stays allocated on the connection until the connection closes or the client issues the [`RESET`](https://redis.io/docs/latest/commands/reset) command."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): `1` if the fieldset was removed, or `0` if no fieldset with that name existed on the connection.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): `1` if the fieldset was removed, or `0` if no fieldset with that name existed on the connection."},{"id":"see-also","title":"See also","role":"related","text":"[`HIMPORT PREPARE`](https://redis.io/docs/latest/commands/himport-prepare) | [`HIMPORT DISCARDALL`](https://redis.io/docs/latest/commands/himport-discardall)"}],"examples":[]}
{"schema_version":2,"id":"commands/himport-discardall","title":"HIMPORT DISCARDALL","url":"https://redis.io/docs/latest/commands/himport-discardall/","summary":"Removes all session-local fieldsets for the connection.","since":"8.10.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"64fc8304a3d82dad52748b0901a6b2a11d9d3c56a6bbd5bf14d115293d4e98c3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Removes every fieldset held by the current connection, discarding all definitions previously created with [`HIMPORT PREPARE`](https://redis.io/docs/latest/commands/himport-prepare)."},{"id":"examples","title":"Examples","role":"example","text":"redis> HIMPORT PREPARE u name email age\nOK\nredis> HIMPORT PREPARE o id total\nOK\nredis> HIMPORT DISCARDALL\n(integer) 2"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of fieldsets removed.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of fieldsets removed."},{"id":"see-also","title":"See also","role":"related","text":"[`HIMPORT PREPARE`](https://redis.io/docs/latest/commands/himport-prepare) | [`HIMPORT DISCARD`](https://redis.io/docs/latest/commands/himport-discard)"}],"examples":[]}
{"schema_version":2,"id":"commands/himport-prepare","title":"HIMPORT PREPARE","url":"https://redis.io/docs/latest/commands/himport-prepare/","summary":"Defines a session-local fieldset that maps a name to a sorted set of field names.","since":"8.10.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"a3fd78670844a154207110510154610fc3d28bbbdee7f203af3ee0e707c114dc","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Defines a session-local fieldset that maps a name to an ordered list of field names, to be used by later [`HIMPORT SET`](https://redis.io/docs/latest/commands/himport-set) commands on the same connection."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>fieldset-name</code></summary>\n\nThe name to give the fieldset. The fieldset is scoped to the current connection. Preparing a fieldset with a name that already exists on the connection replaces the previous definition.\n\n</details>\n\n<details open><summary><code>field [field ...]</code></summary>\n\nOne or more field names to store under the fieldset, in the order you intend to supply their values to [`HIMPORT SET`](https://redis.io/docs/latest/commands/himport-set). A field name must not appear more than once in the same fieldset.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> HIMPORT PREPARE u name email age\nOK"},{"id":"details","title":"Details","role":"content","text":"A fieldset is scoped to the client connection: it is not visible to other clients and is discarded when the connection closes or the client issues the [`RESET`](https://redis.io/docs/latest/commands/reset) command. A connection can prepare several fieldsets, each under its own name.\n\nThe command returns an error if the same field name is given more than once."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."},{"id":"see-also","title":"See also","role":"related","text":"[`HIMPORT SET`](https://redis.io/docs/latest/commands/himport-set) | [`HIMPORT DISCARD`](https://redis.io/docs/latest/commands/himport-discard) | [`HIMPORT DISCARDALL`](https://redis.io/docs/latest/commands/himport-discardall)"}],"examples":[]}
{"schema_version":2,"id":"commands/himport-set","title":"HIMPORT SET","url":"https://redis.io/docs/latest/commands/himport-set/","summary":"Creates a fieldset-based hash from values supplied in the order matching a previously prepared fieldset.","since":"8.10.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"135e1577714b58d0dbf7ad5b597a2150ec83f05411d6f23e72e1cfa106ac78b3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Creates a hash from a fieldset previously defined with [`HIMPORT PREPARE`](https://redis.io/docs/latest/commands/himport-prepare), pairing the fieldset's field names with the supplied values by position."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe key to create. If the key already exists, it is overwritten.\n\n</details>\n\n<details open><summary><code>fieldset-name</code></summary>\n\nThe name of a fieldset previously defined with [`HIMPORT PREPARE`](https://redis.io/docs/latest/commands/himport-prepare) on the same connection.\n\n</details>\n\n<details open><summary><code>value [value ...]</code></summary>\n\nThe field values, given in the same order as the field names were declared in the fieldset. The number of values must equal the number of fields in the fieldset.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"details","title":"Details","role":"content","text":"`HIMPORT SET` creates or overwrites `key` as a hash whose fields are the field names of the prepared fieldset, each paired with the value at the same position. The number of values must match the number of fields in the fieldset.\n\nThe command returns an error if the fieldset has not been prepared on the current connection, or if the number of values does not match the fieldset's field count.\n\nKeys created this way behave exactly like any other hash and work with all existing hash commands. Because they share a fixed set of field names, Redis can store them as [compact hashes](https://redis.io/docs/latest/develop/data-types/hashes#compact-hashes), keeping a single copy of the field names to reduce memory when many keys share the same layout."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."},{"id":"see-also","title":"See also","role":"related","text":"[`HIMPORT PREPARE`](https://redis.io/docs/latest/commands/himport-prepare) | [`HSET`](https://redis.io/docs/latest/commands/hset) | [`HIMPORT DISCARD`](https://redis.io/docs/latest/commands/himport-discard)"}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> HIMPORT PREPARE u name email age\nOK\n> HIMPORT SET user:1 u alice a@example.com 30\nOK\n> HIMPORT SET user:2 u bob b@example.com 25\nOK\n> HGET user:1 name\n\"alice\"\n> HGET user:1 email\n\"a@example.com\"\n> HGET user:1 age\n\"30\"","section_id":"examples"}]}
{"schema_version":2,"id":"commands/hincrby","title":"HINCRBY","url":"https://redis.io/docs/latest/commands/hincrby/","summary":"Increments the integer value of a field in a hash by a number. Uses 0 as initial value if the field doesn't exist.","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"7d67d2d35072668455ad2b107f9716aaa855df6743731b30429e0a0bd4b2691c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Increments the number stored at `field` in the hash stored at `key` by\n`increment`.\nIf `key` does not exist, a new key holding a hash is created.\nIf `field` does not exist the value is set to `0` before the operation is\nperformed.\n\nThe range of values supported by `HINCRBY` is limited to 64 bit signed integers."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the hash.\n\n</details>\n\n<details open><summary><code>field</code></summary>\n\nThe field whose value to increment.\n\n</details>\n\n<details open><summary><code>increment</code></summary>\n\nThe integer amount to add to the field's value.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"Since the `increment` argument is signed, both increment and decrement\noperations can be performed:\n\n\nredis> HSET myhash field 5\n(integer) 1\nredis> HINCRBY myhash field 1\n(integer) 6\nredis> HINCRBY myhash field -1\n(integer) 5\nredis> HINCRBY myhash field -10\n(integer) -5"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the value of the field after the increment operation.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the value of the field after the increment operation."}],"examples":[]}
{"schema_version":2,"id":"commands/hincrbyfloat","title":"HINCRBYFLOAT","url":"https://redis.io/docs/latest/commands/hincrbyfloat/","summary":"Increments the floating point value of a field by a number. Uses 0 as initial value if the field doesn't exist.","since":"2.6.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"ddf67d48087b66b2115a0b53ca2f350dd25725bb893cfea8d51c49be0cf90b97","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Increment the specified `field` of a hash stored at `key`, and representing a\nfloating point number, by the specified `increment`. If the increment value\nis negative, the result is to have the hash field value **decremented** instead of incremented.\nIf the field does not exist, it is set to `0` before performing the operation.\nAn error is returned if one of the following conditions occur:\n\n* The key contains a value of the wrong type (not a hash).\n* The current field content or the specified increment are not parsable as a\n  double precision floating point number.\n\nThe exact behavior of this command is identical to the one of the [`INCRBYFLOAT`](https://redis.io/docs/latest/commands/incrbyfloat)\ncommand, please refer to the documentation of [`INCRBYFLOAT`](https://redis.io/docs/latest/commands/incrbyfloat) for further\ninformation."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the hash.\n\n</details>\n\n<details open><summary><code>field</code></summary>\n\nThe field whose value to increment.\n\n</details>\n\n<details open><summary><code>increment</code></summary>\n\nThe floating-point amount to add to the field's value. A negative value decrements the field instead.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> HSET mykey field 10.50\n(integer) 1\nredis> HINCRBYFLOAT mykey field 0.1\n\"10.6\"\nredis> HINCRBYFLOAT mykey field -5\n\"5.6\"\nredis> HSET mykey field 5.0e3\n(integer) 0\nredis> HINCRBYFLOAT mykey field 2.0e2\n\"5200\""},{"id":"implementation-details","title":"Implementation details","role":"content","text":"The command is always propagated in the replication link and the Append Only\nFile as a [`HSET`](https://redis.io/docs/latest/commands/hset) operation, so that differences in the underlying floating point\nmath implementation will not be sources of inconsistency."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the value of the field after the increment operation.\n\n**RESP3:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): The value of the field after the increment operation."}],"examples":[]}
{"schema_version":2,"id":"commands/hkeys","title":"HKEYS","url":"https://redis.io/docs/latest/commands/hkeys/","summary":"Returns all fields in a hash.","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"7d94d116d04ed2a6c7978b243d527f6b3f2004c6f2f8fa547b52cb3603f3b9a7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns all field names in the hash stored at `key`."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the hash.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> HSET myhash field1 \"Hello\"\n(integer) 1\nredis> HSET myhash field2 \"World\"\n(integer) 1\nredis> HKEYS myhash\n1) \"field1\"\n2) \"field2\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of fields in the hash, or an empty list when the key does not exist\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of fields in the hash, or an empty list when the key does not exist."}],"examples":[]}
{"schema_version":2,"id":"commands/hlen","title":"HLEN","url":"https://redis.io/docs/latest/commands/hlen/","summary":"Returns the number of fields in a hash.","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-08-05T14:46:45-07:00","page_type":"content","content_hash":"8ebfd307f0f3f3ca74a95fbc035ec60ba316ca18a965d8a3278573a8f29218ee","sections":[{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the hash.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"Foundational: Count the fields in a hash with HLEN when you need a hash's size without transferring its contents\n\n**Difficulty:** Beginner\n\n**Commands:** HSET, HLEN\n\n**Complexity:**\n- HSET: O(1)\n- HLEN: O(1)\n\n**Available in:** Redis CLI, C, C#, Go, Java, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of fields in the hash, or 0 when the key does not exist.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of the fields in the hash, or 0 when the key does not exist."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> HSET myhash field1 \"Hello\"\n(integer) 1\nredis> HSET myhash field2 \"World\"\n(integer) 1\nredis> HLEN myhash\n(integer) 2","section_id":"examples"},{"id":"examples-ex1","language":"c","code":"// Add two new fields to the hash\n    reply = redisCommand(c, \"HSET %s %s %s\", \"myhash\", \"field1\", \"Hello\");\n    printf(\"HSET myhash field1 Hello: %lld\\n\", reply->integer); // >>> 1\n    freeReplyObject(reply);\n\n    reply = redisCommand(c, \"HSET %s %s %s\", \"myhash\", \"field2\", \"World\");\n    printf(\"HSET myhash field2 World: %lld\\n\", reply->integer); // >>> 1\n    freeReplyObject(reply);\n\n    // Count the fields in the hash\n    reply = redisCommand(c, \"HLEN %s\", \"myhash\");\n    printf(\"HLEN myhash: %lld\\n\", reply->integer); // >>> 2\n    freeReplyObject(reply);","section_id":"examples"},{"id":"examples-ex2","language":"csharp","code":"bool hlenRes1 = db.HashSet(\"myhash\", \"field1\", \"Hello\");\n        Console.WriteLine(hlenRes1);    // >>> True\n\n        bool hlenRes2 = db.HashSet(\"myhash\", \"field2\", \"World\");\n        Console.WriteLine(hlenRes2);    // >>> True\n\n        long hlenRes3 = db.HashLength(\"myhash\");\n        Console.WriteLine(hlenRes3);    // >>> 2","section_id":"examples"},{"id":"examples-ex3","language":"go","code":"hlen1, err := rdb.HSet(ctx, \"myhash\", \"field1\", \"Hello\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(hlen1) // >>> 1\n\n\thlen2, err := rdb.HSet(ctx, \"myhash\", \"field2\", \"World\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(hlen2) // >>> 1\n\n\thlen3, err := rdb.HLen(ctx, \"myhash\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(hlen3) // >>> 2","section_id":"examples"},{"id":"examples-ex4","language":"java","code":"// `hset` returns true because `field1` is a new field.\n            boolean res19 = syncCommands.hset(\"myhash\", \"field1\", \"Hello\");\n            System.out.println(res19); // >>> true\n\n            // `hset` returns true because `field2` is also a new field.\n            boolean res20 = syncCommands.hset(\"myhash\", \"field2\", \"World\");\n            System.out.println(res20); // >>> true\n\n            Long res21 = syncCommands.hlen(\"myhash\");\n            System.out.println(res21); // >>> 2","section_id":"examples"},{"id":"examples-ex5","language":"java","code":"CompletableFuture<Void> hLenExample = asyncCommands.hset(\"myhash\", \"field1\", \"Hello\").thenCompose(res1 -> {\n                // `hset` returns true because `field1` is a new field.\n                System.out.println(res1); // >>> true\n                return asyncCommands.hset(\"myhash\", \"field2\", \"World\");\n            }).thenCompose(res2 -> {\n                // `hset` returns true because `field2` is also a new field.\n                System.out.println(res2); // >>> true\n                return asyncCommands.hlen(\"myhash\");\n            }).thenAccept(res3 -> {\n                System.out.println(res3); // >>> 2\n            }).toCompletableFuture();","section_id":"examples"},{"id":"examples-ex6","language":"java","code":"Mono<Boolean> hLenExample1 = reactiveCommands.hset(\"myhash\", \"field1\", \"Hello\").doOnNext(result -> {\n                System.out.println(result); // >>> true\n            });\n\n            hLenExample1.block();\n\n            Mono<Boolean> hLenExample2 = reactiveCommands.hset(\"myhash\", \"field2\", \"World\").doOnNext(result -> {\n                System.out.println(result); // >>> true\n            });\n\n            hLenExample2.block();\n\n            Mono<Long> hLenExample3 = reactiveCommands.hlen(\"myhash\").doOnNext(result -> {\n                System.out.println(result); // >>> 2\n            });","section_id":"examples"},{"id":"examples-ex7","language":"java","code":"// `hset` returns 1 because `field1` is a new field.\n        long hLenResult1 = jedis.hset(\"myhash\", \"field1\", \"Hello\");\n        System.out.println(hLenResult1);    // >>> 1\n\n        // `hset` returns 1 because `field2` is also a new field.\n        long hLenResult2 = jedis.hset(\"myhash\", \"field2\", \"World\");\n        System.out.println(hLenResult2);    // >>> 1\n\n        long hLenResult3 = jedis.hlen(\"myhash\");\n        System.out.println(hLenResult3);    // >>> 2","section_id":"examples"},{"id":"examples-ex8","language":"javascript","code":"const res17 = await client.hSet('myhash', 'field1', 'Hello')\nconsole.log(res17) // 1\n\nconst res18 = await client.hSet('myhash', 'field2', 'World')\nconsole.log(res18) // 1\n\nconst res19 = await client.hLen('myhash')\nconsole.log(res19) // 2","section_id":"examples"},{"id":"examples-ex9","language":"javascript","code":"const hlenSet1 = await redis.hset('myhash', 'field1', 'Hello');\nconsole.log(hlenSet1); // >>> 1\n\nconst hlenSet2 = await redis.hset('myhash', 'field2', 'World');\nconsole.log(hlenSet2); // >>> 1\n\nconst hlenResult = await redis.hlen('myhash');\nconsole.log(hlenResult); // >>> 2","section_id":"examples"},{"id":"examples-ex10","language":"javascript","code":"const res17 = await client.hSet('myhash', 'field1', 'Hello')\nconsole.log(res17) // 1\n\nconst res18 = await client.hSet('myhash', 'field2', 'World')\nconsole.log(res18) // 1\n\nconst res19 = await client.hLen('myhash')\nconsole.log(res19) // 2","section_id":"examples"},{"id":"examples-ex11","language":"javascript","code":"const hlenSet1 = await redis.hset('myhash', 'field1', 'Hello');\nconsole.log(hlenSet1); // >>> 1\n\nconst hlenSet2 = await redis.hset('myhash', 'field2', 'World');\nconsole.log(hlenSet2); // >>> 1\n\nconst hlenResult = await redis.hlen('myhash');\nconsole.log(hlenResult); // >>> 2","section_id":"examples"},{"id":"examples-ex12","language":"php","code":"$hLenResult1 = $this->redis->hset('myhash', 'field1', 'Hello');\n        echo \"HSET myhash field1 Hello: \" . $hLenResult1 . \"\\n\"; // >>> 1\n\n        $hLenResult2 = $this->redis->hset('myhash', 'field2', 'World');\n        echo \"HSET myhash field2 World: \" . $hLenResult2 . \"\\n\"; // >>> 1\n\n        $hLenResult3 = $this->redis->hlen('myhash');\n        echo \"HLEN myhash: \" . $hLenResult3 . \"\\n\"; // >>> 2","section_id":"examples"},{"id":"examples-ex13","language":"python","code":"res15 = r.hset(\"myhash\", \"field1\", \"Hello\")\nprint(res15)\n# >>> 1\n\nres16 = r.hset(\"myhash\", \"field2\", \"World\")\nprint(res16)\n# >>> 1\n\nres17 = r.hlen(\"myhash\")\nprint(res17)\n# >>> 2","section_id":"examples"},{"id":"examples-ex14","language":"ruby","code":"res19 = r.hset('myhash', 'field1', 'Hello')\nputs res19 # >>> 1\n\nres20 = r.hset('myhash', 'field2', 'World')\nputs res20 # >>> 1\n\nres21 = r.hlen('myhash')\nputs res21 # >>> 2","section_id":"examples"},{"id":"examples-ex15","language":"rust","code":"match r.hset(\"myhash\", \"field1\", \"Hello\").await {\n            Ok(res15) => {\n                let res15: i32 = res15;\n                println!(\"{res15}\");    // >>> 1\n            },\n            Err(e) => {\n                println!(\"Error setting hash field: {e}\");\n                return;\n            }\n        }\n\n        match r.hset(\"myhash\", \"field2\", \"World\").await {\n            Ok(res16) => {\n                let res16: i32 = res16;\n                println!(\"{res16}\");    // >>> 1\n            },\n            Err(e) => {\n                println!(\"Error setting hash field: {e}\");\n                return;\n            }\n        }\n\n        match r.hlen(\"myhash\").await {\n            Ok(res17) => {\n                let res17: usize = res17;\n                println!(\"{res17}\");    // >>> 2\n            },\n            Err(e) => {\n                println!(\"Error getting hash length: {e}\");\n                return;\n            }\n        }","section_id":"examples"},{"id":"examples-ex16","language":"rust","code":"match r.hset(\"myhash\", \"field1\", \"Hello\") {\n            Ok(hlen1) => {\n                let hlen1: i32 = hlen1;\n                println!(\"{hlen1}\");    // >>> 1\n            },\n            Err(e) => {\n                println!(\"Error setting hash field: {e}\");\n                return;\n            }\n        }\n\n        match r.hset(\"myhash\", \"field2\", \"World\") {\n            Ok(hlen2) => {\n                let hlen2: i32 = hlen2;\n                println!(\"{hlen2}\");    // >>> 1\n            },\n            Err(e) => {\n                println!(\"Error setting hash field: {e}\");\n                return;\n            }\n        }\n\n        match r.hlen(\"myhash\") {\n            Ok(hlen3) => {\n                let hlen3: usize = hlen3;\n                println!(\"{hlen3}\");    // >>> 2\n            },\n            Err(e) => {\n                println!(\"Error getting hash length: {e}\");\n                return;\n            }\n        }","section_id":"examples"}]}
{"schema_version":2,"id":"commands/hmget","title":"HMGET","url":"https://redis.io/docs/latest/commands/hmget/","summary":"Returns the values of all fields in a hash.","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-20T16:12:55+02:00","page_type":"content","content_hash":"f04b9a8f1caf71dd9c16fa32ab94b43d6912bce2ee2e9b62dc84ad724ebf1ae0","sections":[{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the hash.\n\n</details>\n\n<details open><summary><code>field [field ...]</code></summary>\n\nOne or more fields whose values to retrieve.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"Foundational: Retrieve multiple field values from a hash using HMGET (returns nil if field or key doesn't exist)\n\n**Difficulty:** Beginner\n\n**Commands:** HSET, HMGET\n\n**Complexity:**\n- HSET: O(1)\n- HMGET: O(N)\n\n**Available in:** Redis CLI, C, C#, Go, Java, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of values associated with the given fields, in the same order as they are requested.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of values associated with the given fields, in the same order as they are requested."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> HSET myhash field1 \"Hello\"\n(integer) 1\n> HSET myhash field2 \"World\"\n(integer) 1\n> HMGET myhash field1 field2 nofield\n1) \"Hello\"\n2) \"World\"\n3) (nil)","section_id":"examples"},{"id":"examples-ex1","language":"c","code":"redisReply *reply;\n\n    // Set up hash with fields\n    reply = redisCommand(c, \"HSET %s %s %s %s %s\",\n        \"myhash\", \"field1\", \"Hello\", \"field2\", \"World\");\n    freeReplyObject(reply);\n\n    // Get multiple fields at once\n    reply = redisCommand(c, \"HMGET %s %s %s %s\",\n        \"myhash\", \"field1\", \"field2\", \"nofield\");\n\n    printf(\"HMGET myhash field1 field2 nofield:\\n\");\n    for (size_t i = 0; i < reply->elements; i++) {\n        if (reply->element[i]->type == REDIS_REPLY_NIL) {\n            printf(\"  [%zu]: null\\n\", i);\n        } else {\n            printf(\"  [%zu]: %s\\n\", i, reply->element[i]->str);\n        }\n    }\n    // >>> [0]: Hello\n    // >>> [1]: World\n    // >>> [2]: null","section_id":"examples"},{"id":"examples-ex2","language":"csharp","code":"db.HashSet(\"myhash\",\n            [\n                new(\"field1\", \"Hello\"),\n                new(\"field2\", \"World\")\n            ]\n        );\n\n        RedisValue[] hmgetResult = db.HashGet(\"myhash\", new RedisValue[] { \"field1\", \"field2\", \"nofield\" });\n        Console.WriteLine(string.Join(\", \", hmgetResult.Select(v => v.IsNull ? \"null\" : v.ToString())));\n        // >>> Hello, World, null","section_id":"examples"},{"id":"examples-ex3","language":"go","code":"rdb.HSet(ctx, \"myhash\", \"field1\", \"Hello\", \"field2\", \"World\")\n\n\thmgetResult, err := rdb.HMGet(ctx, \"myhash\", \"field1\", \"field2\", \"nofield\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(hmgetResult) // >>> [Hello World <nil>]","section_id":"examples"},{"id":"examples-ex4","language":"java","code":"Map<String, String> hmgetFields = new HashMap<>();\n            hmgetFields.put(\"field1\", \"Hello\");\n            hmgetFields.put(\"field2\", \"World\");\n\n            syncCommands.hset(\"myhash\", hmgetFields);\n\n            // `hmget` returns a `KeyValue` for each field you ask for, in the\n            // order you asked for them. A field that doesn't exist comes back\n            // as an empty `KeyValue`.\n            List<KeyValue<String, String>> res13 = syncCommands.hmget(\"myhash\", \"field1\", \"field2\", \"nofield\");\n            System.out.println(res13);\n            // >>> [KeyValue[field1, Hello], KeyValue[field2, World], KeyValue[nofield].empty]","section_id":"examples"},{"id":"examples-ex5","language":"java","code":"Map<String, String> hmgetExampleParams = new HashMap<>();\n            hmgetExampleParams.put(\"field1\", \"Hello\");\n            hmgetExampleParams.put(\"field2\", \"World\");\n\n            CompletableFuture<Void> hmgetExample = asyncCommands.hset(\"myhash\", hmgetExampleParams).thenCompose(res1 -> {\n                return asyncCommands.hmget(\"myhash\", \"field1\", \"field2\", \"nofield\");\n            }).thenAccept(res2 -> {\n                System.out.println(res2); // >>> [KeyValue[field1, Hello], KeyValue[field2, World], KeyValue[nofield].empty]\n            }).toCompletableFuture();","section_id":"examples"},{"id":"examples-ex6","language":"java","code":"Map<String, String> hmgetExampleParams = new HashMap<>();\n            hmgetExampleParams.put(\"field1\", \"Hello\");\n            hmgetExampleParams.put(\"field2\", \"World\");\n\n            Mono<Long> hmgetExample1 = reactiveCommands.hset(\"myhash\", hmgetExampleParams);\n\n            hmgetExample1.block();\n\n            Flux<KeyValue<String, String>> hmgetExample2 = reactiveCommands.hmget(\"myhash\", \"field1\", \"field2\", \"nofield\")\n                    .doOnNext(result -> {\n                        System.out.println(result);\n                        // >>> KeyValue[field1, Hello]\n                        // >>> KeyValue[field2, World]\n                        // >>> KeyValue[nofield].empty\n                    });","section_id":"examples"},{"id":"examples-ex7","language":"java","code":"Map<String, String> hmgetExampleParams = new HashMap<>();\n        hmgetExampleParams.put(\"field1\", \"Hello\");\n        hmgetExampleParams.put(\"field2\", \"World\");\n\n        jedis.hset(\"myhash\", hmgetExampleParams);\n\n        List<String> hmgetResult = jedis.hmget(\"myhash\", \"field1\", \"field2\", \"nofield\");\n        System.out.println(hmgetResult);    // >>> [Hello, World, null]","section_id":"examples"},{"id":"examples-ex8","language":"javascript","code":"await client.hSet('myhash', { field1: 'Hello', field2: 'World' });\n\nconst hmgetResult = await client.hmGet('myhash', ['field1', 'field2', 'nofield']);\nconsole.log(hmgetResult); // ['Hello', 'World', null]","section_id":"examples"},{"id":"examples-ex9","language":"javascript","code":"await redis.hset('myhash', { field1: 'Hello', field2: 'World' });\n\nconst hmgetResult = await redis.hmget('myhash', 'field1', 'field2', 'nofield');\nconsole.log(hmgetResult); // >>> ['Hello', 'World', null]","section_id":"examples"},{"id":"examples-ex10","language":"javascript","code":"await client.hSet('myhash', { field1: 'Hello', field2: 'World' });\n\nconst hmgetResult = await client.hmGet('myhash', ['field1', 'field2', 'nofield']);\nconsole.log(hmgetResult); // ['Hello', 'World', null]","section_id":"examples"},{"id":"examples-ex11","language":"javascript","code":"await redis.hset('myhash', { field1: 'Hello', field2: 'World' });\n\nconst hmgetResult = await redis.hmget('myhash', 'field1', 'field2', 'nofield');\nconsole.log(hmgetResult); // >>> ['Hello', 'World', null]","section_id":"examples"},{"id":"examples-ex12","language":"php","code":"$this->redis->hmset('myhash', ['field1' => 'Hello', 'field2' => 'World']);\n\n        $hmgetResult = $this->redis->hmget('myhash', ['field1', 'field2', 'nofield']);\n        echo \"HMGET myhash field1 field2 nofield: \";\n        print_r($hmgetResult);\n        // >>> Array ( [0] => Hello [1] => World [2] => )","section_id":"examples"},{"id":"examples-ex13","language":"python","code":"r.hset(\"myhash\", mapping={\"field1\": \"Hello\", \"field2\": \"World\"})\n\nhmget_result = r.hmget(\"myhash\", [\"field1\", \"field2\", \"nofield\"])\nprint(hmget_result)  # >>> ['Hello', 'World', None]","section_id":"examples"},{"id":"examples-ex14","language":"ruby","code":"r.hset('myhash', { 'field1' => 'Hello', 'field2' => 'World' })\n\nres13 = r.hmget('myhash', 'field1', 'field2', 'nofield')\nputs res13.inspect # >>> [\"Hello\", \"World\", nil]","section_id":"examples"},{"id":"examples-ex15","language":"rust","code":"let hash_fields = [\n            (\"field1\", \"Hello\"),\n            (\"field2\", \"World\"),\n        ];\n\n        if let Ok(_) = r.hset_multiple::<&str, &str, &str, String>(\"myhash\", &hash_fields).await {\n            // Fields set successfully\n        }\n\n        match r.hmget::<&str, &[&str], Vec<Option<String>>>(\"myhash\", &[\"field1\", \"field2\", \"nofield\"]).await {\n            Ok(hmget_result) => {\n                println!(\"{:?}\", hmget_result);    // >>> [Some(\"Hello\"), Some(\"World\"), None]\n            },\n            Err(e) => {\n                println!(\"Error getting hash fields: {e}\");\n                return;\n            }\n        }","section_id":"examples"},{"id":"examples-ex16","language":"rust","code":"let hash_fields = [\n            (\"field1\", \"Hello\"),\n            (\"field2\", \"World\"),\n        ];\n\n        if let Ok(_) = r.hset_multiple::<&str, &str, &str, String>(\"myhash\", &hash_fields) {\n            // Fields set successfully\n        }\n\n        match r.hmget::<&str, &[&str], Vec<Option<String>>>(\"myhash\", &[\"field1\", \"field2\", \"nofield\"]) {\n            Ok(hmget_result) => {\n                println!(\"{:?}\", hmget_result);    // >>> [Some(\"Hello\"), Some(\"World\"), None]\n            },\n            Err(e) => {\n                println!(\"Error getting hash fields: {e}\");\n                return;\n            }\n        }","section_id":"examples"}]}
{"schema_version":2,"id":"commands/hmset","title":"HMSET","url":"https://redis.io/docs/latest/commands/hmset/","summary":"Sets the values of multiple fields.","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"0c2f80ea098e706cbd46b294fbb563b96c07a545bd316d6d9ff94ae6cf19282d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Sets the specified fields to their respective values in the hash stored at\n`key`.\nThis command overwrites any specified fields already existing in the hash.\nIf `key` does not exist, a new key holding a hash is created."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the hash.\n\n</details>\n\n<details open><summary><code>field value [field value ...]</code></summary>\n\nOne or more field-value pairs to set in the hash.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> HMSET myhash field1 \"Hello\" field2 \"World\"\nOK\nredis> HGET myhash field1\n\"Hello\"\nredis> HGET myhash field2\n\"World\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Deprecated as of Redis v4.0.0. |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[]}
{"schema_version":2,"id":"commands/hotkeys","title":"HOTKEYS","url":"https://redis.io/docs/latest/commands/hotkeys/","summary":"A container for hotkeys tracking commands.","since":"8.6.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-12T10:32:09+01:00","page_type":"content","content_hash":"336f4fb044d3c2d6624a7191b977541b9b984213cf0ab09925c9f32296743354","sections":[{"id":"usage","title":"Usage","role":"syntax","text":"The general workflow is for the user to initiate a hotkeys tracking process which should run for some time. The keys' metrics are recorded inside a probabilistic data structure, after which the user is able to fetch the top K metrics.\n\nAvailable subcommands:\n\n- [`HOTKEYS START`](https://redis.io/docs/latest/commands/hotkeys-start) - Starts hotkeys tracking with specified metrics.\n- [`HOTKEYS STOP`](https://redis.io/docs/latest/commands/hotkeys-stop) - Stops hotkeys tracking but preserves data.\n- [`HOTKEYS GET`](https://redis.io/docs/latest/commands/hotkeys-get) - Returns tracking results and metadata.\n- [`HOTKEYS RESET`](https://redis.io/docs/latest/commands/hotkeys-reset) - Releases resources used for tracking."},{"id":"example","title":"Example","role":"example","text":"The following example tracks keys by CPU time and network bytes, reads the results while tracking is active, and then stops tracking and releases its resources.\n\nThe redis-py and go-redis cluster clients, and redis-rs async cluster connections, don't support `HOTKEYS`. With these clients, connect directly to a single node instead.\n\nProfiling: Find the keys that use the most CPU time and network bandwidth using HOTKEYS START, GET, STOP, and RESET (only one tracking session can run at a time)\n\n**Difficulty:** Intermediate\n\n**Commands:** HOTKEYS, GET, HOTKEYS RESET\n\n**Complexity:**\n- HOTKEYS: Depends on subcommand\n- GET: O(1)\n- HOTKEYS RESET: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"}],"examples":[{"id":"example-ex0","language":"plaintext","code":"> HOTKEYS START METRICS 2 CPU NET COUNT 10\nOK\n> GET product:1\n\"Laptop\"\n> GET product:1\n\"Laptop\"\n> GET product:2\n\"Phone\"\n> HOTKEYS GET\n1)  1) \"tracking-active\"\n    2) (integer) 1\n    3) \"sample-ratio\"\n    4) (integer) 1\n    5) \"selected-slots\"\n    6) 1) 1) (integer) 0\n          2) (integer) 16383\n    7) \"all-commands-all-slots-us\"\n    8) (integer) 27\n    9) \"net-bytes-all-commands-all-slots\"\n   10) (integer) 119\n   11) \"collection-start-time-unix-ms\"\n   12) (integer) 1791553768375\n   13) \"collection-duration-ms\"\n   14) (integer) 44\n   15) \"total-cpu-time-user-ms\"\n   16) (integer) 2\n   17) \"total-cpu-time-sys-ms\"\n   18) (integer) 0\n   19) \"total-net-bytes\"\n   20) (integer) 119\n   21) \"by-cpu-time-us\"\n   22) 1) \"product:1\"\n       2) (integer) 24\n       3) \"product:2\"\n       4) (integer) 3\n   23) \"by-net-bytes\"\n   24) 1) \"product:1\"\n       2) (integer) 80\n       3) \"product:2\"\n       4) (integer) 39\n> HOTKEYS STOP\nOK\n> HOTKEYS RESET\nOK","section_id":"example"},{"id":"example-ex1","language":"csharp","code":"// Track the 10 hottest keys by CPU time and network bytes.\n        server.HotKeysStart(metrics: HotKeysMetrics.Cpu | HotKeysMetrics.Network, count: 10);\n\n        // Generate some traffic. In production, this is your application's workload.\n        for (int i = 0; i < 100; i++)\n        {\n            db.StringGet(\"product:1\");\n        }\n\n        for (int i = 0; i < 10; i++)\n        {\n            db.StringGet(\"product:2\");\n        }\n\n        db.StringGet(\"product:3\");\n\n        HotKeysResult res2 = server.HotKeysGet();\n        Console.WriteLine(res2.TrackingActive); // >>> True\n\n        var byNet = res2.NetworkBytesByKey.ToArray();\n        Console.WriteLine(string.Join(\", \", byNet.Select(k => $\"{k.Key}: {k.Bytes}\")));\n        // >>> product:1: 4000, product:2: 390, product:3: 40\n\n        bool res3 = server.HotKeysStop();\n        Console.WriteLine(res3); // >>> True\n\n        server.HotKeysReset();","section_id":"example"},{"id":"example-ex2","language":"go","code":"// Track the 10 hottest keys by CPU time and network bytes.\n\tres1, err := rdb.HotKeysStart(ctx, &redis.HotKeysStartArgs{\n\t\tMetrics: []redis.HotKeysMetric{redis.HotKeysMetricCPU, redis.HotKeysMetricNET},\n\t\tCount:   10,\n\t}).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tfmt.Println(res1) // >>> OK\n\n\t// Generate some traffic. In production, this is your application's workload.\n\tfor i := 0; i < 100; i++ {\n\t\trdb.Get(ctx, \"product:1\")\n\t}\n\n\tfor i := 0; i < 10; i++ {\n\t\trdb.Get(ctx, \"product:2\")\n\t}\n\n\trdb.Get(ctx, \"product:3\")\n\n\tres2, err := rdb.HotKeysGet(ctx).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tfmt.Println(res2.TrackingActive) // >>> true\n\n\tfmt.Println(res2.ByNetBytes)\n\t// >>> [{product:1 4000} {product:2 390} {product:3 40}]\n\n\tres3, err := rdb.HotKeysStop(ctx).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tfmt.Println(res3) // >>> OK\n\n\tres4, err := rdb.HotKeysReset(ctx).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tfmt.Println(res4) // >>> OK","section_id":"example"},{"id":"example-ex3","language":"java","code":"// Track the 10 hottest keys by CPU time and network bytes.\n            String res1 = syncCommands.hotkeysStart(\n                    HotkeysArgs.Builder.metrics(HotkeysArgs.Metric.CPU, HotkeysArgs.Metric.NET).count(10));\n            System.out.println(res1); // >>> OK\n\n            // Generate some traffic. In production, this is your application's workload.\n            for (int i = 0; i < 100; i++) {\n                syncCommands.get(\"product:1\");\n            }\n\n            for (int i = 0; i < 10; i++) {\n                syncCommands.get(\"product:2\");\n            }\n\n            syncCommands.get(\"product:3\");\n\n            HotkeysReply res2 = syncCommands.hotkeysGet();\n            System.out.println(res2.isTrackingActive()); // >>> true\n\n            Map<String, Long> byNet = res2.getByNetBytes();\n            System.out.println(byNet);\n            // >>> {product:1=4000, product:2=390, product:3=40}\n\n\n            String res3 = syncCommands.hotkeysStop();\n            System.out.println(res3); // >>> OK\n\n            String res4 = syncCommands.hotkeysReset();\n            System.out.println(res4); // >>> OK","section_id":"example"},{"id":"example-ex4","language":"java","code":"// Track the 10 hottest keys by CPU time and network bytes.\n            CompletableFuture<Void> hotkeysExample = asyncCommands.hotkeysStart(\n                    HotkeysArgs.Builder.metrics(HotkeysArgs.Metric.CPU, HotkeysArgs.Metric.NET).count(10))\n                    .thenCompose(res1 -> {\n                        System.out.println(res1); // >>> OK\n\n                        // Generate some traffic. In production, this is your application's workload.\n                        List<CompletableFuture<String>> traffic = new ArrayList<>();\n                        for (int i = 0; i < 100; i++) {\n                            traffic.add(asyncCommands.get(\"product:1\").toCompletableFuture());\n                        }\n\n                        for (int i = 0; i < 10; i++) {\n                            traffic.add(asyncCommands.get(\"product:2\").toCompletableFuture());\n                        }\n\n                        traffic.add(asyncCommands.get(\"product:3\").toCompletableFuture());\n\n                        return CompletableFuture.allOf(traffic.toArray(new CompletableFuture[0]));\n                    })\n                    .thenCompose(v -> asyncCommands.hotkeysGet())\n                    .thenCompose(res2 -> {\n                        System.out.println(res2.isTrackingActive()); // >>> true\n\n                        System.out.println(res2.getByNetBytes());\n                        // >>> {product:1=4000, product:2=390, product:3=40}\n\n\n                        return asyncCommands.hotkeysStop();\n                    })\n                    .thenCompose(res3 -> {\n                        System.out.println(res3); // >>> OK\n\n                        return asyncCommands.hotkeysReset();\n                    })\n                    .thenAccept(res4 -> {\n                        System.out.println(res4); // >>> OK\n                    })\n                    .toCompletableFuture();\n\n            hotkeysExample.join();","section_id":"example"},{"id":"example-ex5","language":"java","code":"// Track the 10 hottest keys by CPU time and network bytes.\n            Mono<Void> hotkeysExample = reactiveCommands.hotkeysStart(\n                    HotkeysArgs.Builder.metrics(HotkeysArgs.Metric.CPU, HotkeysArgs.Metric.NET).count(10))\n                    .doOnNext(res1 -> {\n                        System.out.println(res1); // >>> OK\n                    })\n                    // Generate some traffic. In production, this is your application's workload.\n                    .thenMany(Flux.range(0, 100).flatMap(i -> reactiveCommands.get(\"product:1\")))\n                    .thenMany(Flux.range(0, 10).flatMap(i -> reactiveCommands.get(\"product:2\")))\n                    .then(reactiveCommands.get(\"product:3\"))\n                    .then(reactiveCommands.hotkeysGet())\n                    .doOnNext(res2 -> {\n                        System.out.println(res2.isTrackingActive()); // >>> true\n\n                        System.out.println(res2.getByNetBytes());\n                        // >>> {product:1=4000, product:2=390, product:3=40}\n\n                    })\n                    .then(reactiveCommands.hotkeysStop())\n                    .doOnNext(res3 -> {\n                        System.out.println(res3); // >>> OK\n                    })\n                    .then(reactiveCommands.hotkeysReset())\n                    .doOnNext(res4 -> {\n                        System.out.println(res4); // >>> OK\n                    })\n                    .then();\n\n            hotkeysExample.block();","section_id":"example"},{"id":"example-ex6","language":"java","code":"// Track the 10 hottest keys by CPU time and network bytes.\n        String res1 = jedis.hotkeysStart(\n            HotkeysParams.hotkeysParams()\n                .metrics(HotkeysMetric.CPU, HotkeysMetric.NET)\n                .count(10)\n        );\n        System.out.println(res1); // >>> OK\n\n        // Generate some traffic. In production, this is your application's workload.\n        for (int i = 0; i < 100; i++) {\n            jedis.get(\"product:1\");\n        }\n\n        for (int i = 0; i < 10; i++) {\n            jedis.get(\"product:2\");\n        }\n\n        jedis.get(\"product:3\");\n\n        HotkeysInfo res2 = jedis.hotkeysGet();\n        System.out.println(res2.isTrackingActive()); // >>> true\n\n        Map<String, Long> byNet = res2.getByNetBytes();\n        System.out.println(byNet);\n        // >>> {product:1=4000, product:2=390, product:3=40}\n\n        String res3 = jedis.hotkeysStop();\n        System.out.println(res3); // >>> OK\n\n        String res4 = jedis.hotkeysReset();\n        System.out.println(res4); // >>> OK","section_id":"example"},{"id":"example-ex7","language":"javascript","code":"// Track the 10 hottest keys by CPU time and network bytes.\nconst res1 = await client.hotkeysStart({\n    METRICS: { count: 2, CPU: true, NET: true },\n    COUNT: 10\n});\nconsole.log(res1); // >>> OK\n\n// Generate some traffic. In production, this is your application's workload.\nfor (let i = 0; i < 100; i++) {\n    await client.get('product:1');\n}\n\nfor (let i = 0; i < 10; i++) {\n    await client.get('product:2');\n}\n\nawait client.get('product:3');\n\nconst res2 = await client.hotkeysGet();\nconsole.log(res2.trackingActive); // >>> 1\n\nconst byNet = res2.byNetBytes.map(({ key, value }) => [key, value]);\nconsole.log(byNet);\n// >>> [ [ 'product:1', 4000 ], [ 'product:2', 390 ], [ 'product:3', 40 ] ]\n\nconst res3 = await client.hotkeysStop();\nconsole.log(res3); // >>> OK\n\nconst res4 = await client.hotkeysReset();\nconsole.log(res4); // >>> OK","section_id":"example"},{"id":"example-ex8","language":"php","code":"// Track the 10 hottest keys by CPU time and network bytes.\n        $res1 = $r->hotkeys->start([\\Predis\\Command\\Container\\HOTKEYS::CPU, \\Predis\\Command\\Container\\HOTKEYS::NET], 10);\n        echo $res1 . PHP_EOL; // >>> OK\n\n        // Generate some traffic. In production, this is your application's workload.\n        for ($i = 0; $i < 100; $i++) {\n            $r->get('product:1');\n        }\n\n        for ($i = 0; $i < 10; $i++) {\n            $r->get('product:2');\n        }\n\n        $r->get('product:3');\n\n        $res2 = $r->hotkeys->get()[0];\n        echo $res2['tracking-active'] . PHP_EOL; // >>> 1\n\n        $byNet = $res2['by-net-bytes'];\n        echo json_encode(array_chunk($byNet, 2)) . PHP_EOL;\n        // >>> [[\"product:1\",4000],[\"product:2\",390],[\"product:3\",40]]\n\n        $res3 = $r->hotkeys->stop();\n        echo $res3 . PHP_EOL; // >>> OK\n\n        $res4 = $r->hotkeys->reset();\n        echo $res4 . PHP_EOL; // >>> OK","section_id":"example"},{"id":"example-ex9","language":"python","code":"from redis.commands.core import HotkeysMetricsTypes\n\n# Track the 10 hottest keys by CPU time and network bytes.\nres1 = r.hotkeys_start(\n    metrics=[HotkeysMetricsTypes.CPU, HotkeysMetricsTypes.NET], count=10\n)\nprint(res1)  # >>> OK\n\n# Generate some traffic. In production, this is your application's workload.\nfor _ in range(100):\n    r.get(\"product:1\")\n\nfor _ in range(10):\n    r.get(\"product:2\")\n\nr.get(\"product:3\")\n\nres2 = r.hotkeys_get()[0]\nprint(res2[\"tracking-active\"])  # >>> 1\n\nby_net = res2[\"by-net-bytes\"]\nprint(list(zip(by_net[::2], by_net[1::2])))\n# >>> [('product:1', 4000), ('product:2', 390), ('product:3', 40)]\n\nres3 = r.hotkeys_stop()\nprint(res3)  # >>> OK\n\nres4 = r.hotkeys_reset()\nprint(res4)  # >>> OK","section_id":"example"},{"id":"example-ex10","language":"rust","code":"// Track the 10 hottest keys by CPU time and network bytes.\n        let opts = HotkeysOptions::new_with_cpu()\n            .and_net()\n            .with_count(10)\n            .expect(\"COUNT must be between 1 and 64\");\n\n        match r.hotkeys_start(opts).await {\n            Ok(res1) => {\n                println!(\"{res1:?}\"); // >>> ()\n            }\n            Err(e) => println!(\"Error starting hotkeys tracking: {e}\"),\n        }\n\n        // Generate some traffic. In production, this is your application's workload.\n        for _ in 0..100 {\n            let _: Result<String, _> = r.get(\"product:1\").await;\n        }\n\n        for _ in 0..10 {\n            let _: Result<String, _> = r.get(\"product:2\").await;\n        }\n\n        let _: Result<String, _> = r.get(\"product:3\").await;\n\n        match r.hotkeys_get().await {\n            Ok(Some(res2)) => {\n                println!(\"{}\", res2.tracking_active); // >>> true\n\n                let by_net: Vec<(&str, u64)> = res2\n                    .by_net_bytes\n                    .iter()\n                    .flatten()\n                    .map(|entry| (entry.key.as_str(), entry.value))\n                    .collect();\n                println!(\"{by_net:?}\");\n                // >>> [(\"product:1\", 4000), (\"product:2\", 390), (\"product:3\", 40)]\n            }\n            Ok(None) => {\n                println!(\"No hotkeys tracking session\");\n            }\n            Err(e) => println!(\"Error getting hotkeys: {e}\"),\n        }\n\n        match r.hotkeys_stop().await {\n            Ok(res3) => {\n                println!(\"{res3}\"); // >>> true\n            }\n            Err(e) => println!(\"Error stopping hotkeys tracking: {e}\"),\n        }\n\n        match r.hotkeys_reset().await {\n            Ok(res4) => {\n                println!(\"{res4:?}\"); // >>> ()\n            }\n            Err(e) => println!(\"Error resetting hotkeys: {e}\"),\n        }","section_id":"example"},{"id":"example-ex11","language":"rust","code":"// Track the 10 hottest keys by CPU time and network bytes.\n        let opts = HotkeysOptions::new_with_cpu()\n            .and_net()\n            .with_count(10)\n            .expect(\"COUNT must be between 1 and 64\");\n\n        match r.hotkeys_start(opts) {\n            Ok(res1) => {\n                println!(\"{res1:?}\"); // >>> ()\n            }\n            Err(e) => println!(\"Error starting hotkeys tracking: {e}\"),\n        }\n\n        // Generate some traffic. In production, this is your application's workload.\n        for _ in 0..100 {\n            let _: Result<String, _> = r.get(\"product:1\");\n        }\n\n        for _ in 0..10 {\n            let _: Result<String, _> = r.get(\"product:2\");\n        }\n\n        let _: Result<String, _> = r.get(\"product:3\");\n\n        match r.hotkeys_get() {\n            Ok(Some(res2)) => {\n                println!(\"{}\", res2.tracking_active); // >>> true\n\n                let by_net: Vec<(&str, u64)> = res2\n                    .by_net_bytes\n                    .iter()\n                    .flatten()\n                    .map(|entry| (entry.key.as_str(), entry.value))\n                    .collect();\n                println!(\"{by_net:?}\");\n                // >>> [(\"product:1\", 4000), (\"product:2\", 390), (\"product:3\", 40)]\n            }\n            Ok(None) => {\n                println!(\"No hotkeys tracking session\");\n            }\n            Err(e) => println!(\"Error getting hotkeys: {e}\"),\n        }\n\n        match r.hotkeys_stop() {\n            Ok(res3) => {\n                println!(\"{res3}\"); // >>> true\n            }\n            Err(e) => println!(\"Error stopping hotkeys tracking: {e}\"),\n        }\n\n        match r.hotkeys_reset() {\n            Ok(res4) => {\n                println!(\"{res4:?}\"); // >>> ()\n            }\n            Err(e) => println!(\"Error resetting hotkeys: {e}\"),\n        }","section_id":"example"}]}
{"schema_version":2,"id":"commands/hotkeys-get","title":"HOTKEYS GET","url":"https://redis.io/docs/latest/commands/hotkeys-get/","summary":"Returns lists of top K hotkeys depending on metrics chosen in HOTKEYS START command.","since":"8.6.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"a2e55380509f79b51b90e7274bccce3424a2f64793b1cdbcf89f850c556cca83","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns tracking results and metadata from the current or most recent hotkeys tracking session.\n\nThis command returns comprehensive information about the hotkeys tracking session, including:\n\n- Tracking metadata (start time, duration, sample ratio, etc.)\n- Performance statistics (CPU time, network bytes)\n- Lists of top K hot keys sorted by the metrics specified in `HOTKEYS START`\n\nThe following metrics are collected for non-clustered as well as clustered Redis environments:\n\n- `tracking-active` (integer): 1 if tracking is active, 0 if stopped\n- `sample-ratio` (integer): The sampling ratio used during tracking\n- `selected-slots` (array): [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of select slots and slot ranges\n- `all-commands-all-slots-us` (integer): CPU time in microseconds for all commands on all slots\n- `net-bytes-all-commands-all-slots` (integer): Network bytes for all commands on all slots\n- `collection-start-time-unix-ms` (integer): Unix timestamp in milliseconds when tracking started\n- `collection-duration-ms` (integer): Duration of tracking in milliseconds\n- `total-cpu-time-user-ms` (integer): User CPU time used in milliseconds (only when the `CPU` metric was specified with `HOTKEYS START`)\n- `total-cpu-time-sys-ms` (integer): System CPU time used in milliseconds (only when the `CPU` metric was specified with `HOTKEYS START`)\n- `total-net-bytes` (integer): Total network bytes processed (only when the `NET` metric was specified with `HOTKEYS START`)\n- `by-cpu-time-us` (array): [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of key-time pairs sorted by CPU time in microseconds (only when the `CPU` metric was specified with `HOTKEYS START`)\n- `by-net-bytes` (array): [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of key-bytes pairs sorted by network bytes (only when the `NET` metric was specified with `HOTKEYS START`)\n\nThe following additional results are collected only on clustered Redis environments, when `SLOTS` was used with `HOTKEYS START`:\n\n- `sampled-commands-selected-slots-us` (integer): CPU time in milliseconds for sampled commands in selected slots (only when `SAMPLE` was used with `HOTKEYS START`)\n- `all-commands-selected-slots-us` (integer): CPU time in milliseconds for all commands in selected slots\n- `net-bytes-sampled-commands-selected-slots` (integer): Network bytes for sampled commands in selected slots (only when `SAMPLE` was used with `HOTKEYS START`)\n- `net-bytes-all-commands-selected-slots` (integer): Network bytes for all commands on selected slots"},{"id":"example-both-net-and-cpu-metrics-specified","title":"Example (both `NET` and `CPU` metrics specified)","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n- [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) when tracking data is available, containing a single array with alternating field names and values.\n- [Null reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) when no tracking has been started or data has been reset.\n\n**RESP3:**\n\nOne of the following:\n\n- [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) when tracking data is available, containing a single [map](https://redis.io/docs/latest/develop/reference/protocol-spec#maps) with field names and values.\n- [Null reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) when no tracking has been started or data has been reset."}],"examples":[{"id":"example-both-net-and-cpu-metrics-specified-ex0","language":"plaintext","code":"HOTKEYS GET\n1)  1) \"tracking-active\"\n    2) (integer) 0\n    3) \"sample-ratio\"\n    4) (integer) 1\n    5) \"selected-slots\"\n    6) 1) 1) (integer) 0\n          2) (integer) 16383\n    7) \"all-commands-all-slots-us\"\n    8) (integer) 103\n    9) \"net-bytes-all-commands-all-slots\"\n   10) (integer) 2042\n   11) \"collection-start-time-unix-ms\"\n   12) (integer) 1770824933147\n   13) \"collection-duration-ms\"\n   14) (integer) 0\n   15) \"total-cpu-time-user-ms\"\n   16) (integer) 23\n   17) \"total-cpu-time-sys-ms\"\n   18) (integer) 7\n   19) \"total-net-bytes\"\n   20) (integer) 2038\n   21) \"by-cpu-time-us\"\n   22)  1) \"hotkey_001_counter\"\n        2) (integer) 29\n        3) \"hotkey_001\"\n        4) (integer) 25\n        5) \"hotkey_001_hash\"\n        6) (integer) 11\n        7) \"hotkey_001_list\"\n        8) (integer) 9\n        9) \"hotkey_001_set\"\n       10) (integer) 9\n   23) \"by-net-bytes\"\n   24)  1) \"hotkey_001\"\n        2) (integer) 446\n        3) \"hotkey_002\"\n        4) (integer) 328\n        5) \"hotkey_001_hash\"\n        6) (integer) 198\n        7) \"hotkey_001_set\"\n        8) (integer) 167\n        9) \"hotkey_001_counter\"\n       10) (integer) 116","section_id":"example-both-net-and-cpu-metrics-specified"}]}
{"schema_version":2,"id":"commands/hotkeys-help","title":"HOTKEYS HELP","url":"https://redis.io/docs/latest/commands/hotkeys-help/","summary":"Returns helpful text about HOTKEYS commands and parameters.","since":"8.6.1","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"51b35ea9e331434fc119f26e4d03b621b25eb2b75db39c475f2f246abe4205ed","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns helpful text about `HOTKEYS` commands and parameters."},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nReturns an [array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) with the list of `HOTKEYS` subcommands and their descriptions.\n\n**RESP3:**\n\nReturns an [array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) with the list of `HOTKEYS` subcommands and their descriptions."}],"examples":[]}
{"schema_version":2,"id":"commands/hotkeys-reset","title":"HOTKEYS RESET","url":"https://redis.io/docs/latest/commands/hotkeys-reset/","summary":"Release the resources used for hotkey tracking.","since":"8.6.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"140c279e8cf09ecb229fd9d1b347473e441570bfd7f7e5699735e5aaeee9071a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Release the resources used for hotkey tracking.\n\nThis command can only be executed when hotkey tracking is stopped. It will return an error if tracking is currently active. Use [`HOTKEYS STOP`](https://redis.io/docs/latest/commands/hotkeys-stop) first to stop tracking, then use `HOTKEYS RESET` to free the allocated resources."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n- [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` when resources are successfully released.\n- [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors): when tracking is currently active.\n\n**RESP3:**\n\nOne of the following:\n\n- [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` when resources are successfully released.\n- [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors): when tracking is currently active."}],"examples":[]}
{"schema_version":2,"id":"commands/hotkeys-start","title":"HOTKEYS START","url":"https://redis.io/docs/latest/commands/hotkeys-start/","summary":"Starts hotkeys tracking.","since":"8.6.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"52e25dae8b1ef48d06e17202e1666032917869d96dd5b17168d5258fba120a05","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Starts hotkeys tracking with specified metrics.\n\nThis command initiates a hotkey tracking session. You must specify which metrics to track using the required `METRICS` parameter.\nAn error is returned if a tracking session is already in progress.\n\nThe tracking session continues until manually stopped with [`HOTKEYS STOP`](https://redis.io/docs/latest/commands/hotkeys-stop) or automatically stopped after the specified duration."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>METRICS count [CPU] [NET]</code></summary>\n\n**Required.** Specifies which metrics to track and how many hotkeys to track.\n\n- `count` - The number of metrics to collect (required).\n- `CPU` - Track hotkeys by CPU time percentage (optional).\n- `NET` - Track hotkeys by network bytes percentage (optional).\n\nAt least one of `CPU` or `NET` must be specified.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>COUNT</code></summary>\n\nSpecifies the value of K for the top-K hotkeys tracking.\n\n</details>\n\n<details open><summary><code>DURATION</code></summary>\n\nThe duration in seconds for how long the hotkeys tracking should run. After this time period, tracking will automatically stop. If not specified, tracking continues until manually stopped with `HOTKEYS STOP`.\n\n</details>\n\n<details open><summary><code>SAMPLE</code></summary>\n\nSampling ratio for probabilistic tracking. Each key is sampled with probability `1/ratio`. Higher values reduce performance impact but may miss some hotkeys. Lower values provide more accurate results but with higher performance cost.\n\n</details>\n\n<details open><summary><code>SLOTS</code></summary>\n\nSpecifies which hash slots to track in a cluster environment. Takes a count followed by that many slot numbers. Only keys that hash to the specified slots will be tracked. Useful for tracking hotkeys on specific shards in a Redis cluster. Using `SLOTS` when not in cluster mode will result in an error.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n- [Simple string](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) reply: `OK` when tracking is successfully started.\n- [Error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors): when invalid parameters are provided.\n\n**RESP3:**\n\nOne of the following:\n\n- [Simple string](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) reply: `OK` when tracking is successfully started.\n- [Error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors): when invalid parameters are provided."}],"examples":[]}
{"schema_version":2,"id":"commands/hotkeys-stop","title":"HOTKEYS STOP","url":"https://redis.io/docs/latest/commands/hotkeys-stop/","summary":"Stops hotkeys tracking.","since":"8.6.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"f374d55ef892a5a6e779f1bb7cdbca39df1e017df3383f1dd63a75d6d3522129","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Stops hotkeys tracking but preserves the collected data.\n\nAfter stopping, the tracking data remains available through [`HOTKEYS GET`](https://redis.io/docs/latest/commands/hotkeys-get) until [`HOTKEYS RESET`](https://redis.io/docs/latest/commands/hotkeys-reset) is called or a new tracking session is started."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n- [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` when tracking is successfully stopped.\n- [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors): when tracking is not currently active.\n\n**RESP3:**\n\nOne of the following:\n\n- [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` when tracking is successfully stopped.\n- [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors): when tracking is not currently active."}],"examples":[]}
{"schema_version":2,"id":"commands/hpersist","title":"HPERSIST","url":"https://redis.io/docs/latest/commands/hpersist/","summary":"Removes the expiration time for each specified field","since":"7.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-06-15T06:26:34-07:00","page_type":"content","content_hash":"1fae9edca3a3a9a7d6b7b2d2ff6f882845e6db88b8abcf26d98b0f8454514566","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Remove the existing expiration on a hash key's field(s), turning the field(s) from _volatile_ (a field\nwith expiration set) to _persistent_ (a field that will never expire as no TTL (time to live)\nis associated)."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the hash.\n\n</details>\n\n<details open><summary><code>FIELDS numfields field [field ...]</code></summary>\n\nThe hash fields to remove the expiration from. `numfields` is the number of fields, followed by that many field names.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n* [Array reply](../../develop/reference/protocol-spec#arrays). For each field:\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `-2` if no such field exists in the provided hash key, or the provided key does not exist.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `-1` if the field exists but has no associated expiration set.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `1` the expiration was removed.\n\n**RESP3:**\n\n* [Array reply](../../develop/reference/protocol-spec#arrays). For each field:\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `-2` if no such field exists in the provided hash key, or the provided key does not exist.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `-1` if the field exists but has no associated expiration set.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `1` the expiration was removed."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> HSET mykey field1 \"hello\" field2 \"world\"\n(integer) 2\nredis> HEXPIRE mykey 300 FIELDS 2 field1 field2\n1) (integer) 1\n2) (integer) 1\nredis> HTTL mykey FIELDS 2 field1 field2\n1) (integer) 283\n2) (integer) 283\nredis> HPERSIST mykey FIELDS 1 field2\n1) (integer) 1\nredis> HTTL mykey FIELDS 2 field1 field2\n1) (integer) 268\n2) (integer) -1","section_id":"examples"}]}
{"schema_version":2,"id":"commands/hpexpire","title":"HPEXPIRE","url":"https://redis.io/docs/latest/commands/hpexpire/","summary":"Set expiration for hash fields using relative time to expire (milliseconds)","since":"7.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"a70b6f85575e6a9e61f1f18d4e3f6893f442e4d8959afd752fbd50b4b135e893","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This command works like [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire), but the expiration of a field is\nspecified in milliseconds instead of seconds."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the hash.\n\n</details>\n\n<details open><summary><code>milliseconds</code></summary>\n\nThe timeout, in milliseconds.\n\n</details>\n\n<details open><summary><code>FIELDS numfields field [field ...]</code></summary>\n\nThe hash fields to operate on. `numfields` is the number of fields, followed by that many field names.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"The following options modify the command's behavior. They are mutually exclusive.\n\n<details open><summary><code>NX</code></summary>\n\nFor each specified field, set expiration only when the field has no expiration.\n\n</details>\n\n<details open><summary><code>XX</code></summary>\n\nFor each specified field, set expiration only when the field has an existing expiration.\n\n</details>\n\n<details open><summary><code>GT</code></summary>\n\nFor each specified field, set expiration only when the new expiration is greater than the current one. A non-volatile key is treated as an infinite TTL for the purposes of `GT`.\n\n</details>\n\n<details open><summary><code>LT</code></summary>\n\nFor each specified field, set expiration only when the new expiration is less than the current one. A non-volatile key is treated as an infinite TTL for the purposes of `LT`.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Array reply](../../develop/reference/protocol-spec#arrays). For each field:\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `-2` if no such field exists in the provided hash key, or the provided key does not exist.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `0` if the specified NX, XX, GT, or LT condition has not been met.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `1` if the expiration time was set/updated.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `2` when `HEXPIRE` or `HPEXPIRE` is called with 0 seconds or milliseconds, or when `HEXPIREAT` or `HPEXPIREAT` is called with a past Unix time in seconds or milliseconds.\n* [Simple error reply](../../develop/reference/protocol-spec#simple-errors):\n    - if parsing failed, mandatory arguments are missing, unknown arguments are specified, or argument values are of the wrong type or out of range.\n    - if the provided key exists but is not a hash.\n\n**RESP3:**\n\nOne of the following:\n* [Array reply](../../develop/reference/protocol-spec#arrays). For each field:\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `-2` if no such field exists in the provided hash key, or the provided key does not exist.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `0` if the specified NX, XX, GT, or LT condition has not been met.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `1` if the expiration time was set/updated.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `2` when `HEXPIRE` or `HPEXPIRE` is called with 0 seconds or milliseconds, or when `HEXPIREAT` or `HPEXPIREAT` is called with a past Unix time in seconds or milliseconds.\n* [Simple error reply](../../develop/reference/protocol-spec#simple-errors):\n    - if parsing failed, mandatory arguments are missing, unknown arguments are specified, or argument values are of the wrong type or out of range.\n    - if the provided key exists but is not a hash."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> HSET mykey field1 \"hello\" field2 \"world\"\n(integer) 2\nredis> HPEXPIRE mykey 2000 FIELDS 2 field1 field2\n1) (integer) 1\n2) (integer) 1\nredis> HGETALL mykey\n(empty array)","section_id":"examples"}]}
{"schema_version":2,"id":"commands/hpexpireat","title":"HPEXPIREAT","url":"https://redis.io/docs/latest/commands/hpexpireat/","summary":"Set expiration for hash fields using an absolute Unix timestamp (milliseconds)","since":"7.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"3e61e179fdf64a2bec1acbe907130c185cd67d2f83937d311a664d7b10fadb37","sections":[{"id":"overview","title":"Overview","role":"overview","text":"`HPEXPIREAT` has the same effect and semantics as [`HEXPIREAT`](https://redis.io/docs/latest/commands/hexpireat), but the Unix time at\nwhich the field will expire is specified in milliseconds since Unix epoch instead of seconds."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the hash.\n\n</details>\n\n<details open><summary><code>unix-time-milliseconds</code></summary>\n\nThe absolute Unix expiration timestamp, in milliseconds since Unix epoch. A timestamp in the past deletes the field immediately.\n\n</details>\n\n<details open><summary><code>FIELDS numfields field [field ...]</code></summary>\n\nThe hash fields to operate on. `numfields` is the number of fields, followed by that many field names.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"The following options modify the command's behavior. They are mutually exclusive.\n\n<details open><summary><code>NX</code></summary>\n\nFor each specified field, set expiration only when the field has no expiration.\n\n</details>\n\n<details open><summary><code>XX</code></summary>\n\nFor each specified field, set expiration only when the field has an existing expiration.\n\n</details>\n\n<details open><summary><code>GT</code></summary>\n\nFor each specified field, set expiration only when the new expiration is greater than the current one. A non-volatile key is treated as an infinite TTL for the purposes of `GT`.\n\n</details>\n\n<details open><summary><code>LT</code></summary>\n\nFor each specified field, set expiration only when the new expiration is less than the current one. A non-volatile key is treated as an infinite TTL for the purposes of `LT`.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Array reply](../../develop/reference/protocol-spec#arrays). For each field:\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `-2` if no such field exists in the provided hash key, or the provided key does not exist.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `0` if the specified NX, XX, GT, or LT condition has not been met.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `1` if the expiration time was set/updated.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `2` when `HEXPIRE` or `HPEXPIRE` is called with 0 seconds or milliseconds, or when `HEXPIREAT` or `HPEXPIREAT` is called with a past Unix time in seconds or milliseconds.\n* [Simple error reply](../../develop/reference/protocol-spec#simple-errors):\n    - if parsing failed, mandatory arguments are missing, unknown arguments are specified, or argument values are of the wrong type or out of range.\n    - if the provided key exists but is not a hash.\n\n**RESP3:**\n\nOne of the following:\n* [Array reply](../../develop/reference/protocol-spec#arrays). For each field:\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `-2` if no such field exists in the provided hash key, or the provided key does not exist.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `0` if the specified NX, XX, GT, or LT condition has not been met.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `1` if the expiration time was set/updated.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `2` when `HEXPIRE` or `HPEXPIRE` is called with 0 seconds or milliseconds, or when `HEXPIREAT` or `HPEXPIREAT` is called with a past Unix time in seconds or milliseconds.\n* [Simple error reply](../../develop/reference/protocol-spec#simple-errors):\n    - if parsing failed, mandatory arguments are missing, unknown arguments are specified, or argument values are of the wrong type or out of range.\n    - if the provided key exists but is not a hash."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> HSET mykey field1 \"hello\" field2 \"world\"\n(integer) 2\nredis> HPEXPIREAT mykey 1715704971000 FIELDS 2 field1 field2\n1) (integer) 1\n2) (integer) 1\nredis> HPTTL mykey FIELDS 2 field1 field2\n1) (integer) 303340\n2) (integer) 303340","section_id":"examples"}]}
{"schema_version":2,"id":"commands/hpexpiretime","title":"HPEXPIRETIME","url":"https://redis.io/docs/latest/commands/hpexpiretime/","summary":"Returns the expiration time of a hash field as a Unix timestamp, in msec.","since":"7.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"60c4f66626feb000e0e6f4d1e28b670984f8c79df3dbbfb3251dafd921b23760","sections":[{"id":"overview","title":"Overview","role":"overview","text":"`HPEXPIRETIME` has the same semantics as [`HEXPIRETIME`](https://redis.io/docs/latest/commands/hexpiretime), but returns the absolute Unix expiration timestamp in milliseconds since Unix epoch instead of seconds."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the hash.\n\n</details>\n\n<details open><summary><code>FIELDS numfields field [field ...]</code></summary>\n\nThe hash fields to retrieve the expiration time for. `numfields` is the number of fields, followed by that many field names.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n* [Array reply](../../develop/reference/protocol-spec#arrays). For each field:\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `-2` if no such field exists in the provided hash key, or the provided key does not exist.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `-1` if the field exists but has no associated expiration set.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): the expiration (Unix timestamp) in milliseconds.\n\n**RESP3:**\n\n* [Array reply](../../develop/reference/protocol-spec#arrays). For each field:\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `-2` if no such field exists in the provided hash key, or the provided key does not exist.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `-1` if the field exists but has no associated expiration set.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): the expiration (Unix timestamp) in milliseconds."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> HSET mykey field1 \"hello\" field2 \"world\"\n(integer) 2\nredis> HEXPIRE mykey 300 FIELDS 2 field1 field2\n1) (integer) 1\n2) (integer) 1\nredis> HPEXPIRETIME mykey FIELDS 2 field1 field2\n1) (integer) 1715705913659\n2) (integer) 1715705913659","section_id":"examples"}]}
{"schema_version":2,"id":"commands/hpttl","title":"HPTTL","url":"https://redis.io/docs/latest/commands/hpttl/","summary":"Returns the TTL in milliseconds of a hash field.","since":"7.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"4f2b42f7887b85ee965b15b31ec7e175d19bdd6d713f0411cc04ea1a3e6cce72","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Like [`HTTL`](https://redis.io/docs/latest/commands/httl), this command returns the remaining TTL (time to live) of a field that has an\nexpiration set, but in milliseconds instead of seconds."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the hash.\n\n</details>\n\n<details open><summary><code>FIELDS numfields field [field ...]</code></summary>\n\nThe hash fields to retrieve the remaining time to live for. `numfields` is the number of fields, followed by that many field names.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n* [Array reply](../../develop/reference/protocol-spec#arrays). For each field:\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `-2` if no such field exists in the provided hash key, or the provided key does not exist.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `-1` if the field exists but has no associated expiration set.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): the TTL in milliseconds.\n\n**RESP3:**\n\n* [Array reply](../../develop/reference/protocol-spec#arrays). For each field:\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `-2` if no such field exists in the provided hash key, or the provided key does not exist.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `-1` if the field exists but has no associated expiration set.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): the TTL in milliseconds."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> HPTTL no-key FIELDS 3 field1 field2 field3\n(nil)\nredis> HSET mykey field1 \"hello\" field2 \"world\"\n(integer) 2\nredis> HEXPIRE mykey 300 FIELDS 2 field1 field3\n1) (integer) 1\n2) (integer) -2\nredis> HPTTL mykey FIELDS 3 field1 field2 field3\n1) (integer) 292202\n2) (integer) -1\n3) (integer) -2","section_id":"examples"}]}
{"schema_version":2,"id":"commands/hrandfield","title":"HRANDFIELD","url":"https://redis.io/docs/latest/commands/hrandfield/","summary":"Returns one or more random fields from a hash.","since":"6.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"c6291f2681dded7c085db10ed1905b6d04f98bf79556ba4af16224847ea358e4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"When called with just the `key` argument, return a random field from the hash value stored at `key`.\n\nIf the provided `count` argument is positive, return an array of **distinct fields**.\nThe array's length is either `count` or the hash's number of fields ([`HLEN`](https://redis.io/docs/latest/commands/hlen)), whichever is lower.\n\nIf called with a negative `count`, the behavior changes and the command is allowed to return the **same field multiple times**.\nIn this case, the number of returned fields is the absolute value of the specified `count`.\n\nThe optional `WITHVALUES` modifier changes the reply so it includes the respective values of the randomly selected hash fields."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the hash.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>count</code></summary>\n\nThe number of fields to return. A positive `count` returns up to that many distinct fields; a negative `count` returns exactly `|count|` fields and may repeat fields. When omitted, a single random field is returned.\n\n</details>\n\n<details open><summary><code>WITHVALUES</code></summary>\n\nAlso return the value of each selected field. Can only be used together with `count`.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> HSET coin heads obverse tails reverse edge null\n(integer) 3\nredis> HRANDFIELD coin\nredis> HRANDFIELD coin\nredis> HRANDFIELD coin -5 WITHVALUES"},{"id":"specification-of-the-behavior-when-count-is-passed","title":"Specification of the behavior when count is passed","role":"content","text":"When the `count` argument is a positive value this command behaves as follows:\n\n* No repeated fields are returned.\n* If `count` is bigger than the number of fields in the hash, the command will only return the whole hash without additional fields.\n* The order of fields in the reply is not truly random, so it is up to the client to shuffle them if needed.\n\nWhen the `count` is a negative value, the behavior changes as follows:\n\n* Repeating fields are possible.\n* Exactly `count` fields, or an empty array if the hash is empty (non-existing key), are always returned.\n* The order of fields in the reply is truly random."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nAny of the following:\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): if the key doesn't exist\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): a single, randomly selected field when the `count` option is not used\n* [Array reply](../../develop/reference/protocol-spec#arrays): a list containing `count` fields when the `count` option is used, or an empty array if the key does not exists.\n* [Array reply](../../develop/reference/protocol-spec#arrays): a list of fields and their values when `count` and `WITHVALUES` were both used.\n\n**RESP3:**\n\nAny of the following:\n* [Null reply](../../develop/reference/protocol-spec#nulls): if the key doesn't exist\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): a single, randomly selected field when the `count` option is not used\n* [Array reply](../../develop/reference/protocol-spec#arrays): a list containing `count` fields when the `count` option is used, or an empty array if the key does not exists.\n* [Array reply](../../develop/reference/protocol-spec#arrays): a list of fields and their values when `count` and `WITHVALUES` were both used."}],"examples":[]}
{"schema_version":2,"id":"commands/hscan","title":"HSCAN","url":"https://redis.io/docs/latest/commands/hscan/","summary":"Iterates over fields and values of a hash.","since":"2.8.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"62cca1de8ed7bb9b7ae565600f5d49572de21e03eb8fee27ceb1129261dad668","sections":[{"id":"overview","title":"Overview","role":"overview","text":"See [`SCAN`](https://redis.io/docs/latest/commands/scan) for `HSCAN` documentation."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the hash.\n\n</details>\n\n<details open><summary><code>cursor</code></summary>\n\nThe cursor value. Start an iteration with `0`, then use the cursor returned by each call as the argument to the next call.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>MATCH pattern</code></summary>\n\nOnly return fields whose names match the given glob-style pattern.\n\n</details>\n\n<details open><summary><code>COUNT count</code></summary>\n\nA hint for the number of fields to return per iteration. The default is 10.\n\n</details>\n\n<details open><summary><code>NOVALUES</code></summary>\n\nReturn only the field names, without their values.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a two-element array.\n* The first element is a [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings) that represents an unsigned 64-bit number, the cursor.\n* The second element is an [Array reply](../../develop/reference/protocol-spec#arrays) of field/value pairs that were scanned. When the `NOVALUES` flag (since Redis 7.4) is used, only the field names are returned.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a two-element array.\n* The first element is a [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings) that represents an unsigned 64-bit number, the cursor.\n* The second element is an [Array reply](../../develop/reference/protocol-spec#arrays) of field/value pairs that were scanned. When the `NOVALUES` flag (since Redis 7.4) is used, only the field names are returned."}],"examples":[]}
{"schema_version":2,"id":"commands/hset","title":"HSET","url":"https://redis.io/docs/latest/commands/hset/","summary":"Creates or modifies the value of a field in a hash.","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-08T12:05:04+02:00","page_type":"content","content_hash":"1a874c9d95073b0033c076e2536a3aa3bda49b46eb97e9799e29544e63f73e4a","sections":[{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the hash.\n\n</details>\n\n<details open><summary><code>field value [field value ...]</code></summary>\n\nOne or more field-value pairs to set in the hash.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"Foundational: Set one or more field-value pairs in a hash using HSET (creates hash if needed, overwrites existing fields, returns count of new fields)\n\n**Difficulty:** Beginner\n\n**Commands:** HSET, HGET, HGETALL\n\n**Complexity:**\n- HSET: O(1)\n- HGET: O(1)\n- HGETALL: O(N)\n\n**Available in:** Redis CLI, C, C#, Go, Java, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of fields that were added.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of fields that were added."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> HSET myhash field1 \"Hello\"\n(integer) 1\n> HGET myhash field1\n\"Hello\"\n> HSET myhash field2 \"Hi\" field3 \"World\"\n(integer) 2\n> HGET myhash field2\n\"Hi\"\n> HGET myhash field3\n\"World\"\n> HGETALL myhash\n1) \"field1\"\n2) \"Hello\"\n3) \"field2\"\n4) \"Hi\"\n5) \"field3\"\n6) \"World\"","section_id":"examples"},{"id":"examples-ex1","language":"c","code":"reply = redisCommand(c, \"HSET myhash field1 Hello\");\n    printf(\"%lld\\n\", reply->integer);\n    // >>> 1\n    freeReplyObject(reply);\n\n    reply = redisCommand(c, \"HGET myhash field1\");\n    printf(\"%s\\n\", reply->str);\n    // >>> Hello\n    freeReplyObject(reply);\n\n    reply = redisCommand(c, \"HSET myhash field2 Hi field3 World\");\n    printf(\"%lld\\n\", reply->integer);\n    // >>> 2\n    freeReplyObject(reply);\n\n    reply = redisCommand(c, \"HGET myhash field2\");\n    printf(\"%s\\n\", reply->str);\n    // >>> Hi\n    freeReplyObject(reply);\n\n    reply = redisCommand(c, \"HGET myhash field3\");\n    printf(\"%s\\n\", reply->str);\n    // >>> World\n    freeReplyObject(reply);\n\n    // HGETALL returns field and value alternating in a flat array.\n    reply = redisCommand(c, \"HGETALL myhash\");\n    for (size_t i = 0; i < reply->elements; i += 2) {\n        printf(\"%s=%s\\n\", reply->element[i]->str, reply->element[i + 1]->str);\n    }\n    // >>> field1=Hello\n    // >>> field2=Hi\n    // >>> field3=World\n    freeReplyObject(reply);","section_id":"examples"},{"id":"examples-ex2","language":"csharp","code":"bool hsetRes1 = db.HashSet(\"myhash\", \"field1\", \"Hello\");\n        RedisValue hsetRes2 = db.HashGet(\"myhash\", \"field1\");\n        Console.WriteLine(hsetRes2);    // >>> Hello\n\n        db.HashSet(\n            \"myhash\",\n            [\n                new(\"field2\", \"Hi\"),\n                new(\"field3\", \"World\")\n            ]\n        );\n\n        RedisValue hsetRes3 = db.HashGet(\"myhash\", \"field2\");\n        Console.WriteLine(hsetRes3);    // >>> Hi\n\n        RedisValue hsetRes4 = db.HashGet(\"myhash\", \"field3\");\n        Console.WriteLine(hsetRes4);    // >>> World\n\n        HashEntry[] hsetRes5 = db.HashGetAll(\"myhash\");\n        Console.WriteLine($\"{string.Join(\", \", hsetRes5.Select(h => $\"{h.Name}: {h.Value}\"))}\");\n        // >>> field1: Hello, field2: Hi, field3: World","section_id":"examples"},{"id":"examples-ex3","language":"go","code":"res1, err := rdb.HSet(ctx, \"myhash\", \"field1\", \"Hello\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> 1\n\n\tres2, err := rdb.HGet(ctx, \"myhash\", \"field1\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> Hello\n\n\tres3, err := rdb.HSet(ctx, \"myhash\",\n\t\t\"field2\", \"Hi\",\n\t\t\"field3\", \"World\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3) // >>> 2\n\n\tres4, err := rdb.HGet(ctx, \"myhash\", \"field2\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4) // >>> Hi\n\n\tres5, err := rdb.HGet(ctx, \"myhash\", \"field3\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res5) // >>> World\n\n\tres6, err := rdb.HGetAll(ctx, \"myhash\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tkeys := make([]string, 0, len(res6))\n\n\tfor key, _ := range res6 {\n\t\tkeys = append(keys, key)\n\t}\n\n\tsort.Strings(keys)\n\n\tfor _, key := range keys {\n\t\tfmt.Printf(\"Key: %v, value: %v\\n\", key, res6[key])\n\t}\n\t// >>> Key: field1, value: Hello\n\t// >>> Key: field2, value: Hi\n\t// >>> Key: field3, value: World","section_id":"examples"},{"id":"examples-ex4","language":"java","code":"// `hset` returns true because `field1` is a new field.\n            boolean res4 = syncCommands.hset(\"myhash\", \"field1\", \"Hello\");\n            System.out.println(res4); // >>> true\n\n            String res5 = syncCommands.hget(\"myhash\", \"field1\");\n            System.out.println(res5); // >>> Hello\n\n            // The `Map` overload of `hset` returns the number of new fields.\n            Map<String, String> newFields = new HashMap<>();\n            newFields.put(\"field2\", \"Hi\");\n            newFields.put(\"field3\", \"World\");\n\n            Long res6 = syncCommands.hset(\"myhash\", newFields);\n            System.out.println(res6); // >>> 2\n\n            String res7 = syncCommands.hget(\"myhash\", \"field2\");\n            System.out.println(res7); // >>> Hi\n\n            String res8 = syncCommands.hget(\"myhash\", \"field3\");\n            System.out.println(res8); // >>> World\n\n            // `hgetall` returns a `Map`, whose iteration order isn't\n            // guaranteed. Wrap it in a `TreeMap` to sort the fields by name.\n            Map<String, String> res9 = syncCommands.hgetall(\"myhash\");\n            System.out.println(new TreeMap<>(res9));\n            // >>> {field1=Hello, field2=Hi, field3=World}","section_id":"examples"},{"id":"examples-ex5","language":"java","code":"CompletableFuture<Void> hSetExample = asyncCommands.hset(\"myhash\", \"field1\", \"Hello\").thenCompose(res1 -> {\n                System.out.println(res1); // >>> 1\n                return asyncCommands.hget(\"myhash\", \"field1\");\n            }).thenCompose(res2 -> {\n                System.out.println(res2); // >>> Hello\n                Map<String, String> hSetExampleParams = new HashMap<>();\n                hSetExampleParams.put(\"field2\", \"Hi\");\n                hSetExampleParams.put(\"field3\", \"World\");\n                return asyncCommands.hset(\"myhash\", hSetExampleParams);\n            }).thenCompose(res3 -> {\n                System.out.println(res3); // >>> 2\n                return asyncCommands.hget(\"myhash\", \"field2\");\n            }).thenCompose(res4 -> {\n                System.out.println(res4); // >>> Hi\n                return asyncCommands.hget(\"myhash\", \"field3\");\n            }).thenCompose(res5 -> {\n                System.out.println(res5); // >>> World\n                return asyncCommands.hgetall(\"myhash\");\n            }).thenAccept(res6 -> {\n                System.out.println(res6);\n                // >>> {field1=Hello, field2=Hi, field3=World}\n            }).toCompletableFuture();","section_id":"examples"},{"id":"examples-ex6","language":"java","code":"Mono<Boolean> hSetExample1 = reactiveCommands.hset(\"myhash\", \"field1\", \"Hello\").doOnNext(result -> {\n                System.out.println(result); // >>> True\n            });\n\n            hSetExample1.block();\n\n            Mono<String> hSetExample2 = reactiveCommands.hget(\"myhash\", \"field1\").doOnNext(result -> {\n                System.out.println(result); // >>> Hello\n            });\n\n            hSetExample2.block();\n\n            Map<String, String> hSetExampleParams = new HashMap<>();\n            hSetExampleParams.put(\"field2\", \"Hi\");\n            hSetExampleParams.put(\"field3\", \"World\");\n\n            Mono<Long> hSetExample3 = reactiveCommands.hset(\"myhash\", hSetExampleParams).doOnNext(result -> {\n                System.out.println(result); // >>> 2\n            });\n\n            hSetExample3.block();\n\n            Mono<String> hSetExample4 = reactiveCommands.hget(\"myhash\", \"field2\").doOnNext(result -> {\n                System.out.println(result); // >>> Hi\n            });\n\n            hSetExample4.block();\n\n            Mono<String> hSetExample5 = reactiveCommands.hget(\"myhash\", \"field3\").doOnNext(result -> {\n                System.out.println(result); // >>> World\n            });\n\n            hSetExample5.block();\n\n            Mono<Map<String, String>> hSetExample6 = reactiveCommands.hgetall(\"myhash\").collectMap(\n                    KeyValue::getKey, KeyValue::getValue\n            ).doOnNext(result -> {\n                System.out.println(result);\n                // >>> {field1=Hello, field2=Hi, field3=World}\n            });","section_id":"examples"},{"id":"examples-ex7","language":"java","code":"Map<String, String> hSetExampleParams = new HashMap<>();\n        hSetExampleParams.put(\"field1\", \"Hello\");\n        long hSetResult1 = jedis.hset(\"myhash\", hSetExampleParams);\n        System.out.println(hSetResult1);    // >>> 1\n\n        String hSetResult2 = jedis.hget(\"myhash\", \"field1\");\n        System.out.println(hSetResult2);    // >>> Hello\n\n        hSetExampleParams.clear();\n        hSetExampleParams.put(\"field2\", \"Hi\");\n        hSetExampleParams.put(\"field3\", \"World\");\n        long hSetResult3 = jedis.hset(\"myhash\",hSetExampleParams);\n        System.out.println(hSetResult3);    // >>> 2\n\n        String hSetResult4 = jedis.hget(\"myhash\", \"field2\");\n        System.out.println(hSetResult4);    // >>> Hi\n\n        String hSetResult5 = jedis.hget(\"myhash\", \"field3\");\n        System.out.println(hSetResult5);    // >>> World\n\n        Map<String, String> hSetResult6 = jedis.hgetAll(\"myhash\");\n        \n        for (String key: hSetResult6.keySet()) {\n            System.out.println(\"Key: \" + key + \", Value: \" + hSetResult6.get(key));\n        }\n        // >>> Key: field3, Value: World\n        // >>> Key: field2, Value: Hi\n        // >>> Key: field1, Value: Hello","section_id":"examples"},{"id":"examples-ex8","language":"javascript","code":"const res1 = await client.hSet('myhash', 'field1', 'Hello')\nconsole.log(res1) // 1\n\nconst res2 = await client.hGet('myhash', 'field1')\nconsole.log(res2) // Hello\n\nconst res3 = await client.hSet(\n  'myhash',\n  {\n    'field2': 'Hi',\n    'field3': 'World'\n  }\n)\nconsole.log(res3) // 2\n\nconst res4 = await client.hGet('myhash', 'field2')\nconsole.log(res4) // Hi\n\nconst res5 = await client.hGet('myhash', 'field3')\nconsole.log(res5) // World\n\nconst res6 = await client.hGetAll('myhash')\nconsole.log(res6)","section_id":"examples"},{"id":"examples-ex9","language":"javascript","code":"const hsetRes1 = await redis.hset('myhash', 'field1', 'Hello');\nconsole.log(hsetRes1); // >>> 1\n\nconsole.log(await redis.hget('myhash', 'field1')); // >>> Hello\n\nconst hsetRes2 = await redis.hset('myhash', { field2: 'Hi', field3: 'World' });\nconsole.log(hsetRes2); // >>> 2\n\nconsole.log(await redis.hget('myhash', 'field2')); // >>> Hi\nconsole.log(await redis.hget('myhash', 'field3')); // >>> World\n\nconst hsetRes3 = await redis.hgetall('myhash');\nconsole.log(hsetRes3);\n// >>> { field1: 'Hello', field2: 'Hi', field3: 'World' }","section_id":"examples"},{"id":"examples-ex10","language":"javascript","code":"const res1 = await client.hSet('myhash', 'field1', 'Hello')\nconsole.log(res1) // 1\n\nconst res2 = await client.hGet('myhash', 'field1')\nconsole.log(res2) // Hello\n\nconst res3 = await client.hSet(\n  'myhash',\n  {\n    'field2': 'Hi',\n    'field3': 'World'\n  }\n)\nconsole.log(res3) // 2\n\nconst res4 = await client.hGet('myhash', 'field2')\nconsole.log(res4) // Hi\n\nconst res5 = await client.hGet('myhash', 'field3')\nconsole.log(res5) // World\n\nconst res6 = await client.hGetAll('myhash')\nconsole.log(res6)","section_id":"examples"},{"id":"examples-ex11","language":"javascript","code":"const hsetRes1 = await redis.hset('myhash', 'field1', 'Hello');\nconsole.log(hsetRes1); // >>> 1\n\nconsole.log(await redis.hget('myhash', 'field1')); // >>> Hello\n\nconst hsetRes2 = await redis.hset('myhash', { field2: 'Hi', field3: 'World' });\nconsole.log(hsetRes2); // >>> 2\n\nconsole.log(await redis.hget('myhash', 'field2')); // >>> Hi\nconsole.log(await redis.hget('myhash', 'field3')); // >>> World\n\nconst hsetRes3 = await redis.hgetall('myhash');\nconsole.log(hsetRes3);\n// >>> { field1: 'Hello', field2: 'Hi', field3: 'World' }","section_id":"examples"},{"id":"examples-ex12","language":"php","code":"$hSetResult1 = $this->redis->hset('myhash', 'field1', 'Hello');\n        echo \"HSET myhash field1 Hello: \" . $hSetResult1 . \"\\n\"; // >>> 1\n\n        $hSetResult2 = $this->redis->hget('myhash', 'field1');\n        echo \"HGET myhash field1: \" . $hSetResult2 . \"\\n\"; // >>> Hello\n\n        $hSetResult3 = $this->redis->hmset('myhash', ['field2' => 'Hi', 'field3' => 'World']);\n        echo \"HMSET myhash field2 Hi field3 World: \" . ($hSetResult3 ? 'OK' : 'FAIL') . \"\\n\"; // >>> OK\n\n        $hSetResult4 = $this->redis->hget('myhash', 'field2');\n        echo \"HGET myhash field2: \" . $hSetResult4 . \"\\n\"; // >>> Hi\n\n        $hSetResult5 = $this->redis->hget('myhash', 'field3');\n        echo \"HGET myhash field3: \" . $hSetResult5 . \"\\n\"; // >>> World\n\n        $hSetResult6 = $this->redis->hgetall('myhash');\n        echo \"HGETALL myhash: \";\n        foreach ($hSetResult6 as $key => $value) {\n            echo \"Key: $key, Value: $value; \";\n        }\n        echo \"\\n\";","section_id":"examples"},{"id":"examples-ex13","language":"python","code":"res1 = r.hset(\"myhash\", \"field1\", \"Hello\")\nprint(res1)\n# >>> 1\n\nres2 = r.hget(\"myhash\", \"field1\")\nprint(res2)\n# >>> Hello\n\nres3 = r.hset(\"myhash\", mapping={\"field2\": \"Hi\", \"field3\": \"World\"})\nprint(res3)\n# >>> 2\n\nres4 = r.hget(\"myhash\", \"field2\")\nprint(res4)\n# >>> Hi\n\nres5 = r.hget(\"myhash\", \"field3\")\nprint(res5)\n# >>> World\n\nres6 = r.hgetall(\"myhash\")\nprint(res6)\n# >>> { \"field1\": \"Hello\", \"field2\": \"Hi\", \"field3\": \"World\" }","section_id":"examples"},{"id":"examples-ex14","language":"ruby","code":"res4 = r.hset('myhash', 'field1', 'Hello')\nputs res4 # >>> 1\n\nres5 = r.hget('myhash', 'field1')\nputs res5 # >>> Hello\n\nres6 = r.hset('myhash', { 'field2' => 'Hi', 'field3' => 'World' })\nputs res6 # >>> 2\n\nres7 = r.hget('myhash', 'field2')\nputs res7 # >>> Hi\n\nres8 = r.hget('myhash', 'field3')\nputs res8 # >>> World\n\nres9 = r.hgetall('myhash')\nputs res9.inspect\n# >>> {\"field1\"=>\"Hello\", \"field2\"=>\"Hi\", \"field3\"=>\"World\"}","section_id":"examples"},{"id":"examples-ex15","language":"rust","code":"match r.hset(\"myhash\", \"field1\", \"Hello\").await {\n            Ok(res1) => {\n                let res1: i32 = res1;\n                println!(\"{res1}\");    // >>> 1\n            },\n            Err(e) => {\n                println!(\"Error setting hash field: {e}\");\n                return;\n            }\n        }\n\n        match r.hget(\"myhash\", \"field1\").await {\n            Ok(res2) => {\n                let res2: Option<String> = res2;\n                match res2 {\n                    Some(value) => {\n                        println!(\"{value}\");    // >>> Hello\n                    },\n                    None => {\n                        println!(\"None\");\n                    }\n                }\n            },\n            Err(e) => {\n                println!(\"Error getting hash field: {e}\");\n                return;\n            }\n        }\n\n        // Set multiple fields using hset_multiple\n        let hash_fields = [\n            (\"field2\", \"Hi\"),\n            (\"field3\", \"World\"),\n        ];\n\n        if let Ok(res) = r.hset_multiple(\"myhash\", &hash_fields).await {\n            let res: String = res;\n            println!(\"{res}\");    // >>> OK\n        }\n\n        match r.hget(\"myhash\", \"field2\").await {\n            Ok(res4) => {\n                let res4: Option<String> = res4;\n                match res4 {\n                    Some(value) => {\n                        println!(\"{value}\");    // >>> Hi\n                    },\n                    None => {\n                        println!(\"None\");\n                    }\n                }\n            },\n            Err(e) => {\n                println!(\"Error getting hash field: {e}\");\n                return;\n            }\n        }\n\n        match r.hget(\"myhash\", \"field3\").await {\n            Ok(res5) => {\n                let res5: Option<String> = res5;\n                match res5 {\n                    Some(value) => {\n                        println!(\"{value}\");    // >>> World\n                    },\n                    None => {\n                        println!(\"None\");\n                    }\n                }\n            },\n            Err(e) => {\n                println!(\"Error getting hash field: {e}\");\n                return;\n            }\n        }\n\n        match r.hgetall(\"myhash\").await {\n            Ok(res6) => {\n                let res6: HashMap<String, String> = res6;\n                println!(\"{res6:?}\");    // >>> {\"field1\": \"Hello\", \"field2\": \"Hi\", \"field3\": \"World\"}\n            },\n            Err(e) => {\n                println!(\"Error getting all hash fields: {e}\");\n                return;\n            }\n        }","section_id":"examples"},{"id":"examples-ex16","language":"rust","code":"match r.hset(\"myhash\", \"field1\", \"Hello\") {\n            Ok(res1) => {\n                let res1: i32 = res1;\n                println!(\"{res1}\");    // >>> 1\n            },\n            Err(e) => {\n                println!(\"Error setting hash field: {e}\");\n                return;\n            }\n        }\n\n        match r.hget(\"myhash\", \"field1\") {\n            Ok(res2) => {\n                let res2: Option<String> = res2;\n                match res2 {\n                    Some(value) => {\n                        println!(\"{value}\");    // >>> Hello\n                    },\n                    None => {\n                        println!(\"None\");\n                    }\n                }\n            },\n            Err(e) => {\n                println!(\"Error getting hash field: {e}\");\n                return;\n            }\n        }\n\n        // Set multiple fields using hset_multiple\n        let hash_fields = [\n            (\"field2\", \"Hi\"),\n            (\"field3\", \"World\"),\n        ];\n\n        if let Ok(res) = r.hset_multiple(\"myhash\", &hash_fields) {\n            let res: String = res;\n            println!(\"{res}\");    // >>> OK\n        }\n\n        match r.hget(\"myhash\", \"field2\") {\n            Ok(res4) => {\n                let res4: Option<String> = res4;\n                match res4 {\n                    Some(value) => {\n                        println!(\"{value}\");    // >>> Hi\n                    },\n                    None => {\n                        println!(\"None\");\n                    }\n                }\n            },\n            Err(e) => {\n                println!(\"Error getting hash field: {e}\");\n                return;\n            }\n        }\n\n        match r.hget(\"myhash\", \"field3\") {\n            Ok(res5) => {\n                let res5: Option<String> = res5;\n                match res5 {\n                    Some(value) => {\n                        println!(\"{value}\");    // >>> World\n                    },\n                    None => {\n                        println!(\"None\");\n                    }\n                }\n            },\n            Err(e) => {\n                println!(\"Error getting hash field: {e}\");\n                return;\n            }\n        }\n\n        match r.hgetall(\"myhash\") {\n            Ok(res6) => {\n                let res6: HashMap<String, String> = res6;\n                println!(\"{res6:?}\");    // >>> {\"field1\": \"Hello\", \"field2\": \"Hi\", \"field3\": \"World\"}\n            },\n            Err(e) => {\n                println!(\"Error getting all hash fields: {e}\");\n                return;\n            }\n        }","section_id":"examples"}]}
{"schema_version":2,"id":"commands/hsetex","title":"HSETEX","url":"https://redis.io/docs/latest/commands/hsetex/","summary":"Set the value of one or more fields of a given hash key, and optionally set their expiration.","since":"8.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-06-15T06:26:34-07:00","page_type":"content","content_hash":"0e4fcf718e2af444bd6790f0f4545d8a83a7a776e40954ac9fe9e4fca8fa5ae5","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Set the value of one or more fields of a given hash key and optionally\nset their expiration time or time-to-live (TTL). If the given key already holds a value, it is overwritten and any previous TTLs associated with the key are discarded."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the hash.\n\n</details>\n\n<details open><summary><code>FIELDS numfields field value [field value ...]</code></summary>\n\nThe hash field-value pairs to set. `numfields` is the number of fields, followed by that many field-value pairs.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"The following options modify the command's behavior. The `EX`, `PX`, `EXAT`, `PXAT`, and `KEEPTTL` options are mutually exclusive.\n\n<details open><summary><code>FNX</code></summary>\n\nOnly set the fields if none of them already exist. `FNX` and `FXX` are mutually exclusive.\n\n</details>\n\n<details open><summary><code>FXX</code></summary>\n\nOnly set the fields if all of them already exist.\n\n</details>\n\n<details open><summary><code>EX seconds</code></summary>\n\nSet the specified expiration time, in seconds.\n\n</details>\n\n<details open><summary><code>PX milliseconds</code></summary>\n\nSet the specified expiration time, in milliseconds.\n\n</details>\n\n<details open><summary><code>EXAT unix-time-seconds</code></summary>\n\nSet the specified Unix time, in seconds, at which the fields will expire.\n\n</details>\n\n<details open><summary><code>PXAT unix-time-milliseconds</code></summary>\n\nSet the specified Unix time, in milliseconds, at which the fields will expire.\n\n</details>\n\n<details open><summary><code>KEEPTTL</code></summary>\n\nRetain the TTL associated with the fields.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0` if no fields were set.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `1` if all the fields wereset.\n\n**RESP3:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0` if no fields were set.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `1` if all the fields wereset."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> HSETEX mykey EXAT 1740470400 FIELDS 2 field1 \"Hello\" field2 \"World\"\n(integer) 1\nredis> HTTL mykey FIELDS 2 field1 field2\n1) (integer) 55627\n2) (integer) 55627\nredis> HSETEX mykey FNX EX 60 FIELDS 2 field1 \"Hello\" field2 \"World\"\n(integer) 0\nredis> HSETEX mykey FXX EX 60 KEEPTTL FIELDS 2 field1 \"hello\" field2 \"world\"\n(error) ERR Only one of EX, PX, EXAT, PXAT or KEEPTTL arguments can be specified\nredis> HSETEX mykey FXX KEEPTTL FIELDS 2 field1 \"hello\" field2 \"world\"\n(integer) 1\nredis> HTTL mykey FIELDS 2 field1 field2\n1) (integer) 55481\n2) (integer) 55481","section_id":"examples"}]}
{"schema_version":2,"id":"commands/hsetnx","title":"HSETNX","url":"https://redis.io/docs/latest/commands/hsetnx/","summary":"Sets the value of a field in a hash only when the field doesn't exist.","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"cd3bac9c63a8b5722edf3fe2070b4bfe31a96172447a903e04f0e7791e56e124","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Sets `field` in the hash stored at `key` to `value`, only if `field` does not\nyet exist.\nIf `key` does not exist, a new key holding a hash is created.\nIf `field` already exists, this operation has no effect."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the hash.\n\n</details>\n\n<details open><summary><code>field</code></summary>\n\nThe field to set, only if it does not already exist.\n\n</details>\n\n<details open><summary><code>value</code></summary>\n\nThe value to set.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> HSETNX myhash field \"Hello\"\n(integer) 1\nredis> HSETNX myhash field \"World\"\n(integer) 0\nredis> HGET myhash field\n\"Hello\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0` if the field already exists in the hash and no operation was performed.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `1` if the field is a new field in the hash and the value was set.\n\n**RESP3:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0` if the field already exists in the hash and no operation was performed.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `1` if the field is a new field in the hash and the value was set."}],"examples":[]}
{"schema_version":2,"id":"commands/hstrlen","title":"HSTRLEN","url":"https://redis.io/docs/latest/commands/hstrlen/","summary":"Returns the length of the value of a field.","since":"3.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"348affbd5cb77b6274623f4ce2d852f337ce33532051eff53bcbfcfdafd58dec","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns the string length of the value associated with `field` in the hash stored at `key`. If the `key` or the `field` do not exist, 0 is returned."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the hash.\n\n</details>\n\n<details open><summary><code>field</code></summary>\n\nThe field whose value length to return.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> HSET myhash f1 HelloWorld f2 99 f3 -256\n(integer) 3\nredis> HSTRLEN myhash f1\n(integer) 10\nredis> HSTRLEN myhash f2\n(integer) 2\nredis> HSTRLEN myhash f3\n(integer) 4"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the string length of the value associated with the _field_, or zero when the _field_ isn't present in the hash or the _key_ doesn't exist at all.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the string length of the value associated with the _field_, or zero when the _field_ isn't present in the hash or the _key_ doesn't exist at all."}],"examples":[]}
{"schema_version":2,"id":"commands/httl","title":"HTTL","url":"https://redis.io/docs/latest/commands/httl/","summary":"Returns the TTL in seconds of a hash field.","since":"7.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"602b73bacc81d708c1b32e2c6beb0f1b7b8fa1ece73b39ac1d2447742aa35f28","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns the remaining TTL (time to live) of a hash key's field(s) that have a set expiration.\nThis introspection capability allows you to check how many seconds a\ngiven hash field will continue to be part of the hash key.\n\nSee also the [`HPTTL`](https://redis.io/docs/latest/commands/hpttl) command that returns the same information with millisecond resolution."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the hash.\n\n</details>\n\n<details open><summary><code>FIELDS numfields field [field ...]</code></summary>\n\nThe hash fields to retrieve the remaining time to live for. `numfields` is the number of fields, followed by that many field names.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n* [Array reply](../../develop/reference/protocol-spec#arrays). For each field:\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `-2` if no such field exists in the provided hash key, or the provided key does not exist.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `-1` if the field exists but has no associated expiration set.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): the TTL in seconds.\n\n**RESP3:**\n\n* [Array reply](../../develop/reference/protocol-spec#arrays). For each field:\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `-2` if no such field exists in the provided hash key, or the provided key does not exist.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): `-1` if the field exists but has no associated expiration set.\n    - [Integer reply](../../develop/reference/protocol-spec#integers): the TTL in seconds."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> HTTL no-key FIELDS 3 field1 field2 field3\n(nil)\nredis> HSET mykey field1 \"hello\" field2 \"world\"\n(integer) 2\nredis> HEXPIRE mykey 300 FIELDS 2 field1 field3\n1) (integer) 1\n2) (integer) -2\nredis> HTTL mykey FIELDS 3 field1 field2 field3\n1) (integer) 283\n2) (integer) -1\n3) (integer) -2","section_id":"examples"}]}
{"schema_version":2,"id":"commands/hvals","title":"HVALS","url":"https://redis.io/docs/latest/commands/hvals/","summary":"Returns all values in a hash.","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-08T12:05:04+02:00","page_type":"content","content_hash":"d485d3e8b7e924b3c6e3e007cba4d9297bd6b87aa8902c85029cd9275db0863d","sections":[{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the hash.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"Foundational: Retrieve all values from a hash using HVALS (returns only values without field names, useful when you only need the data)\n\n**Difficulty:** Beginner\n\n**Commands:** HSET, HVALS\n\n**Complexity:**\n- HSET: O(1)\n- HVALS: O(N)\n\n**Available in:** Redis CLI, C, C#, Go, Java, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of values in the hash, or an empty list when the key does not exist\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of values in the hash, or an empty list when the key does not exist."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> HSET myhash field1 \"Hello\"\n(integer) 1\nredis> HSET myhash field2 \"World\"\n(integer) 1\nredis> HVALS myhash\n1) \"Hello\"\n2) \"World\"","section_id":"examples"},{"id":"examples-ex1","language":"c","code":"reply = redisCommand(c, \"HSET myhash field1 Hello field2 World\");\n    freeReplyObject(reply);\n\n    reply = redisCommand(c, \"HVALS myhash\");\n    for (size_t i = 0; i < reply->elements; i++) {\n        printf(\"%s\\n\", reply->element[i]->str);\n    }\n    // >>> Hello\n    // >>> World\n    freeReplyObject(reply);","section_id":"examples"},{"id":"examples-ex2","language":"csharp","code":"db.HashSet(\"myhash\",\n            [\n                new(\"field1\", \"Hello\"),\n                new(\"field2\", \"World\")\n            ]\n        );\n\n        RedisValue[] hValsResult = db.HashValues(\"myhash\");\n        Array.Sort(hValsResult);\n        Console.WriteLine(string.Join(\", \", hValsResult));\n        // >>> Hello, World","section_id":"examples"},{"id":"examples-ex3","language":"go","code":"hValsResult1, err := rdb.HSet(ctx, \"myhash\",\n\t\t\"field1\", \"Hello\",\n\t\t\"field2\", \"World\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(hValsResult1) // >>> 2\n\n\thValsResult2, err := rdb.HVals(ctx, \"myhash\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tsort.Strings(hValsResult2)\n\n\tfmt.Println(hValsResult2) // >>> [Hello World]","section_id":"examples"},{"id":"examples-ex4","language":"java","code":"Map<String, String> hValsFields = new HashMap<>();\n            hValsFields.put(\"field1\", \"Hello\");\n            hValsFields.put(\"field2\", \"World\");\n\n            syncCommands.hset(\"myhash\", hValsFields);\n\n            // The order of the values isn't guaranteed, so sort them before\n            // printing.\n            List<String> res15 = syncCommands.hvals(\"myhash\");\n            List<String> sortedValues = new ArrayList<>(res15);\n            Collections.sort(sortedValues);\n            System.out.println(sortedValues); // >>> [Hello, World]","section_id":"examples"},{"id":"examples-ex5","language":"java","code":"Map<String, String> hValsExampleParams = new HashMap<>();\n            hValsExampleParams.put(\"field1\", \"Hello\");\n            hValsExampleParams.put(\"field2\", \"World\");\n\n            CompletableFuture<Void> hValsExample = asyncCommands.hset(\"myhash\", hValsExampleParams).thenCompose(res1 -> {\n                return asyncCommands.hvals(\"myhash\");\n            }).thenAccept(res2 -> {\n                List<String> sortedValues = new ArrayList<>(res2);\n                Collections.sort(sortedValues);\n                System.out.println(sortedValues);\n                // >>> [Hello, World]\n            }).toCompletableFuture();","section_id":"examples"},{"id":"examples-ex6","language":"java","code":"Map<String, String> hValsExampleParams = new HashMap<>();\n            hValsExampleParams.put(\"field1\", \"Hello\");\n            hValsExampleParams.put(\"field2\", \"World\");\n\n            Mono<Long> hValsExample1 = reactiveCommands.hset(\"myhash\", hValsExampleParams).doOnNext(result -> {\n            });\n\n            hValsExample1.block();\n\n            Mono<List<String>> hValsExample2 = reactiveCommands.hvals(\"myhash\").collectList().doOnNext(result -> {\n                List<String> sortedValues = new ArrayList<>(result);\n                Collections.sort(sortedValues);\n                System.out.println(sortedValues);\n                // >>> [Hello, World]\n            });","section_id":"examples"},{"id":"examples-ex7","language":"java","code":"Map<String, String> hValsExampleParams = new HashMap<>();\n        hValsExampleParams.put(\"field1\", \"Hello\");\n        hValsExampleParams.put(\"field2\", \"World\");\n\n        long hValsResult1 = jedis.hset(\"myhash\", hValsExampleParams);\n        System.out.println(hValsResult1); // >>> 2\n\n        List<String> hValsResult2 = jedis.hvals(\"myhash\");\n        Collections.sort(hValsResult2);\n        System.out.println(hValsResult2);\n        // >>> [Hello, World]","section_id":"examples"},{"id":"examples-ex8","language":"javascript","code":"const res12 = await client.hSet(\n  'myhash',\n  {\n    'field1': 'Hello',\n    'field2': 'World'\n  }\n)\n\n// HVALS follows the hash's field order, which Redis does not promise, so sort.\nconst res13 = (await client.hVals('myhash')).sort()\nconsole.log(res13) // [ 'Hello', 'World' ]","section_id":"examples"},{"id":"examples-ex9","language":"javascript","code":"await redis.hset('myhash', { field1: 'Hello', field2: 'World' });\n\n// HVALS follows the hash's field order, which Redis does not promise, so sort.\nconst hvalsRes = await redis.hvals('myhash');\nconsole.log(hvalsRes.sort()); // >>> [ 'Hello', 'World' ]","section_id":"examples"},{"id":"examples-ex10","language":"javascript","code":"const res12 = await client.hSet(\n  'myhash',\n  {\n    'field1': 'Hello',\n    'field2': 'World'\n  }\n)\n\n// HVALS follows the hash's field order, which Redis does not promise, so sort.\nconst res13 = (await client.hVals('myhash')).sort()\nconsole.log(res13) // [ 'Hello', 'World' ]","section_id":"examples"},{"id":"examples-ex11","language":"javascript","code":"await redis.hset('myhash', { field1: 'Hello', field2: 'World' });\n\n// HVALS follows the hash's field order, which Redis does not promise, so sort.\nconst hvalsRes = await redis.hvals('myhash');\nconsole.log(hvalsRes.sort()); // >>> [ 'Hello', 'World' ]","section_id":"examples"},{"id":"examples-ex12","language":"php","code":"$hValsResult1 = $this->redis->hmset('myhash', ['field1' => 'Hello', 'field2' => 'World']);\n        echo \"HMSET myhash field1 Hello field2 World: \" . ($hValsResult1 ? 'OK' : 'FAIL') . \"\\n\"; // >>> OK\n\n        // HVALS follows the hash's field order, which Redis does not promise, so sort.\n        $hValsResult2 = $this->redis->hvals('myhash');\n        sort($hValsResult2);\n        echo \"HVALS myhash: \" . json_encode($hValsResult2) . \"\\n\"; // >>> [\"Hello\",\"World\"]","section_id":"examples"},{"id":"examples-ex13","language":"python","code":"res10 = r.hset(\"myhash\", mapping={\"field1\": \"Hello\", \"field2\": \"World\"})\n\n# HVALS follows the hash's field order, which Redis does not promise, so sort.\nres11 = r.hvals(\"myhash\")\nprint(sorted(res11)) # >>> [ \"Hello\", \"World\" ]","section_id":"examples"},{"id":"examples-ex14","language":"ruby","code":"r.hset('myhash', { 'field1' => 'Hello', 'field2' => 'World' })\n\n# HVALS follows the hash's field order, which Redis does not promise, so sort.\nres15 = r.hvals('myhash').sort\nputs res15.inspect # >>> [\"Hello\", \"World\"]","section_id":"examples"},{"id":"examples-ex15","language":"rust","code":"let hash_fields = [\n            (\"field1\", \"Hello\"),\n            (\"field2\", \"World\"),\n        ];\n\n        if let Ok(_) = r.hset_multiple::<&str, &str, &str, String>(\"myhash\", &hash_fields).await {\n            // Fields set successfully\n        }\n\n        match r.hvals(\"myhash\").await {\n            Ok(res11) => {\n                let res11: Vec<String> = res11;\n                println!(\"{res11:?}\");    // >>> [\"Hello\", \"World\"]\n            },\n            Err(e) => {\n                println!(\"Error getting hash values: {e}\");\n                return;\n            }\n        }","section_id":"examples"},{"id":"examples-ex16","language":"rust","code":"let hash_fields = [\n            (\"field1\", \"Hello\"),\n            (\"field2\", \"World\"),\n        ];\n\n        if let Ok(_) = r.hset_multiple::<&str, &str, &str, String>(\"myhash\", &hash_fields) {\n            // Fields set successfully\n        }\n\n        match r.hvals(\"myhash\") {\n            Ok(res11) => {\n                let res11: Vec<String> = res11;\n                println!(\"{res11:?}\");    // >>> [\"Hello\", \"World\"]\n            },\n            Err(e) => {\n                println!(\"Error getting hash values: {e}\");\n                return;\n            }\n        }","section_id":"examples"}]}
{"schema_version":2,"id":"commands/incr","title":"INCR","url":"https://redis.io/docs/latest/commands/incr/","summary":"Increments the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:12+01:00","page_type":"content","content_hash":"e9311f1cb211cc2fe1c6cd5c985faeb1a53d4c3138c2a7fc7f72e619b0b3264e","sections":[{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"Foundational: Increment the integer value of a key by one using INCR (initializes to 0 if key doesn't exist)\n\n**Difficulty:** Beginner\n\n**Commands:** SET, INCR, GET\n\n**Complexity:**\n- SET: O(1)\n- INCR: O(1)\n- GET: O(1)\n\n**Available in:** Redis CLI, C, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"details","title":"Details","role":"content","text":""},{"id":"pattern-counter","title":"Pattern: counter","role":"content","text":"The counter pattern is the most obvious thing you can do with Redis atomic\nincrement operations.\nThe idea is simply send an `INCR` command to Redis every time an operation\noccurs.\nFor instance in a web application we may want to know how many page views this\nuser did every day of the year.\n\nTo do so the web application may simply increment a key every time the user\nperforms a page view, creating the key name concatenating the User ID and a\nstring representing the current date.\n\nThis simple pattern can be extended in many ways:\n\n* It is possible to use `INCR` and [`EXPIRE`](https://redis.io/docs/latest/commands/expire) together at every page view to have\n  a counter counting only the latest N page views separated by less than the\n  specified amount of seconds.\n* A client may use GETSET in order to atomically get the current counter value\n  and reset it to zero.\n* Using other atomic increment/decrement commands like [`DECR`](https://redis.io/docs/latest/commands/decr) or [`INCRBY`](https://redis.io/docs/latest/commands/incrby) it\n  is possible to handle values that may get bigger or smaller depending on the\n  operations performed by the user.\n  Imagine for instance the score of different users in an online game."},{"id":"pattern-rate-limiter","title":"Pattern: rate limiter","role":"content","text":"The rate limiter pattern is a special counter that is used to limit the rate at\nwhich an operation can be performed.\nThe classical materialization of this pattern involves limiting the number of\nrequests that can be performed against a public API.\n\nYou can implement this pattern with INCR in two ways. Both examples limit API calls to a maximum of ten requests per second per IP address.\n\n#### Pattern: rate limiter 1\n\nThe more simple and direct implementation of this pattern is the following:\n\n[code example]\n\nIn this example, there is a counter for every IP, for every different second.\nBut these counters are always incremented setting an expire of 10 seconds so that\nthey'll be removed by Redis automatically when the current second is a different\none.\n\nNote the used of [`MULTI`](https://redis.io/docs/latest/commands/multi) and [`EXEC`](https://redis.io/docs/latest/commands/exec) in order to make sure that we'll both\nincrement and set the expire at every API call.\n\n#### Pattern: rate limiter 2\n\nAn alternative implementation uses a single counter, but is a bit more complex\nto get it right without race conditions.\nWe'll examine different variants.\n\n[code example]\n\nThe counter is created in a way that it only will survive one second, starting\nfrom the first request performed in the current second.\nIf there are more than 10 requests in the same second the counter will reach a\nvalue greater than 10, otherwise it will expire and start again from 0.\n\n**In the above code there is a race condition**.\nIf for some reason the client performs the `INCR` command but does not perform\nthe [`EXPIRE`](https://redis.io/docs/latest/commands/expire) the key will be leaked until we'll see the same IP address again.\n\nThis can be easily fixed by turning the `INCR` with optional [`EXPIRE`](https://redis.io/docs/latest/commands/expire) into a Lua\nscript that is then sent using the [`EVAL`](https://redis.io/docs/latest/commands/eval) command (only available since Redis version\n2.6).\n\n[code example]\n\nThere is a different way to fix this issue without using scripting, by using\nRedis lists instead of counters.\nThe implementation is more complex and uses more advanced features but has the\nadvantage of remembering the IP addresses of the clients currently performing an\nAPI call, that may be useful or not depending on the application.\n\n[code example]\n\nThe [`RPUSHX`](https://redis.io/docs/latest/commands/rpushx) command only pushes the element if the key already exists.\n\nNote that we have a race here, but it is not a problem: [`EXISTS`](https://redis.io/docs/latest/commands/exists) may return\nfalse but the key may be created by another client before we create it inside\nthe [`MULTI`](https://redis.io/docs/latest/commands/multi) / [`EXEC`](https://redis.io/docs/latest/commands/exec) block.\nHowever this race will just miss an API call under rare conditions, so the rate\nlimiting will still work correctly."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the value of the key after the increment.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the value of the key after the increment."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> SET mykey \"10\"\nOK\n> INCR mykey\n(integer) 11\n> GET mykey\n\"11\"","section_id":"examples"},{"id":"examples-ex1","language":"c","code":"reply = redisCommand(c, \"SET mykey 10\");\n    printf(\"%s\\n\", reply->str);\n    // >>> OK\n    freeReplyObject(reply);\n\n    reply = redisCommand(c, \"INCR mykey\");\n    printf(\"%lld\\n\", reply->integer);\n    // >>> 11\n    freeReplyObject(reply);\n\n    reply = redisCommand(c, \"GET mykey\");\n    printf(\"%s\\n\", reply->str);\n    // >>> 11\n    freeReplyObject(reply);","section_id":"examples"},{"id":"examples-ex2","language":"csharp","code":"bool incrResult1 = db.StringSet(\"mykey\", \"10\");\n        Console.WriteLine(incrResult1); // >>> true\n\n        long incrResult2 = db.StringIncrement(\"mykey\");\n        Console.WriteLine(incrResult2); // >>> 11\n\n        RedisValue incrResult3 = db.StringGet(\"mykey\");\n        Console.WriteLine(incrResult3); // >>> 11","section_id":"examples"},{"id":"examples-ex3","language":"go","code":"incrResult1, err := rdb.Set(ctx, \"mykey\", \"10\", 0).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(incrResult1) // >>> OK\n\n\tincrResult2, err := rdb.Incr(ctx, \"mykey\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(incrResult2) // >>> 11\n\n\tincrResult3, err := rdb.Get(ctx, \"mykey\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(incrResult3) // >>> 11","section_id":"examples"},{"id":"examples-ex4","language":"java","code":"CompletableFuture<Void> incrExample = asyncCommands.set(\"mykey\", \"10\")\n                    .thenCompose(incrResult1 -> {\n                        System.out.println(incrResult1);    // >>> OK\n                        return asyncCommands.incr(\"mykey\");\n                    })\n                    .thenCompose(incrResult2 -> {\n                        System.out.println(incrResult2);    // >>> 11\n                        return asyncCommands.get(\"mykey\");\n                    })\n                    .thenAccept(incrResult3 -> {\n                        System.out.println(incrResult3);    // >>> 11\n                    })\n                    .toCompletableFuture();","section_id":"examples"},{"id":"examples-ex5","language":"java","code":"Mono<Void> incrExample = reactiveCommands.set(\"mykey\", \"10\")\n                    .flatMap(incrResult1 -> {\n                        System.out.println(incrResult1);    // >>> OK\n                        return reactiveCommands.incr(\"mykey\");\n                    })\n                    .flatMap(incrResult2 -> {\n                        System.out.println(incrResult2);    // >>> 11\n                        return reactiveCommands.get(\"mykey\");\n                    })\n                    .doOnNext(incrResult3 -> {\n                        System.out.println(incrResult3);    // >>> 11\n                    })\n                    .then();","section_id":"examples"},{"id":"examples-ex6","language":"java","code":"String incrResult1 = jedis.set(\"mykey\", \"10\");\n        System.out.println(incrResult1);    // >>> OK\n\n        long incrResult2 = jedis.incr(\"mykey\");\n        System.out.println(incrResult2);    // >>> 11\n\n        String incrResult3 = jedis.get(\"mykey\");\n        System.out.println(incrResult3);    // >>> 11","section_id":"examples"},{"id":"examples-ex7","language":"javascript","code":"const incrResult1 = await client.set('mykey', '10');\nconsole.log(incrResult1); // >>> OK\n\nconst incrResult2 = await client.incr('mykey');\nconsole.log(incrResult2); // >>> 11\n\nconst incrResult3 = await client.get('mykey');\nconsole.log(incrResult3); // >>> 11","section_id":"examples"},{"id":"examples-ex8","language":"javascript","code":"const incrResult1 = await redis.set('mykey', '10');\nconsole.log(incrResult1); // >>> OK\n\nconst incrResult2 = await redis.incr('mykey');\nconsole.log(incrResult2); // >>> 11\n\nconst incrResult3 = await redis.get('mykey');\nconsole.log(incrResult3); // >>> 11","section_id":"examples"},{"id":"examples-ex9","language":"javascript","code":"const incrResult1 = await client.set('mykey', '10');\nconsole.log(incrResult1); // >>> OK\n\nconst incrResult2 = await client.incr('mykey');\nconsole.log(incrResult2); // >>> 11\n\nconst incrResult3 = await client.get('mykey');\nconsole.log(incrResult3); // >>> 11","section_id":"examples"},{"id":"examples-ex10","language":"javascript","code":"const incrResult1 = await redis.set('mykey', '10');\nconsole.log(incrResult1); // >>> OK\n\nconst incrResult2 = await redis.incr('mykey');\nconsole.log(incrResult2); // >>> 11\n\nconst incrResult3 = await redis.get('mykey');\nconsole.log(incrResult3); // >>> 11","section_id":"examples"},{"id":"examples-ex11","language":"php","code":"$incrResult1 = $r->set('mykey', '10');\n        echo $incrResult1 . PHP_EOL;        // >>> OK\n\n        $incrResult2 = $r->incr('mykey');\n        echo $incrResult2 . PHP_EOL;        // >>> 11\n\n        $incrResult3 = $r->get('mykey');\n        echo $incrResult3 . PHP_EOL;        // >>> 11","section_id":"examples"},{"id":"examples-ex12","language":"python","code":"incr_result1 = r.set(\"mykey\", \"10\")\nprint(incr_result1)\n# >>> True\n\nincr_result2 = r.incr(\"mykey\")\nprint(incr_result2)\n# >>> 11\n\nincr_result3 = r.get(\"mykey\")\nprint(incr_result3)\n# >>> 11","section_id":"examples"},{"id":"examples-ex13","language":"rust","code":"if let Ok(res) = r.set(\"mykey\", \"10\").await {\n            let res: String = res;\n            println!(\"{res}\");    // >>> OK\n        }\n\n        match r.incr(\"mykey\", 1).await {\n            Ok(incr_result) => {\n                let incr_result: i64 = incr_result;\n                println!(\"{incr_result}\");    // >>> 11\n            }\n            Err(e) => {\n                println!(\"Error incrementing value: {e}\");\n            }\n        }\n\n        match r.get(\"mykey\").await {\n            Ok(get_result) => {\n                let get_result: String = get_result;\n                println!(\"{get_result}\");    // >>> 11\n            }\n            Err(e) => {\n                println!(\"Error getting value: {e}\");\n            }\n        }","section_id":"examples"},{"id":"examples-ex14","language":"rust","code":"if let Ok(res) = r.set(\"mykey\", \"10\") {\n            let res: String = res;\n            println!(\"{res}\");    // >>> OK\n        }\n\n        match r.incr(\"mykey\", 1) {\n            Ok(incr_result) => {\n                let incr_result: i64 = incr_result;\n                println!(\"{incr_result}\");    // >>> 11\n            }\n            Err(e) => {\n                println!(\"Error incrementing value: {e}\");\n            }\n        }\n\n        match r.get(\"mykey\") {\n            Ok(get_result) => {\n                let get_result: String = get_result;\n                println!(\"{get_result}\");    // >>> 11\n            }\n            Err(e) => {\n                println!(\"Error getting value: {e}\");\n            }\n        }","section_id":"examples"},{"id":"pattern-rate-limiter-ex0","language":"plaintext","code":"FUNCTION LIMIT_API_CALL(ip)\nts = CURRENT_UNIX_TIME()\nkeyname = ip+\":\"+ts\nMULTI\n    INCR(keyname)\n    EXPIRE(keyname,10)\nEXEC\ncurrent = RESPONSE_OF_INCR_WITHIN_MULTI\nIF current > 10 THEN\n    ERROR \"too many requests per second\"\nELSE\n    PERFORM_API_CALL()\nEND","section_id":"pattern-rate-limiter"},{"id":"pattern-rate-limiter-ex1","language":"plaintext","code":"FUNCTION LIMIT_API_CALL(ip):\ncurrent = GET(ip)\nIF current != NULL AND current > 10 THEN\n    ERROR \"too many requests per second\"\nELSE\n    value = INCR(ip)\n    IF value == 1 THEN\n        EXPIRE(ip,1)\n    END\n    PERFORM_API_CALL()\nEND","section_id":"pattern-rate-limiter"},{"id":"pattern-rate-limiter-ex2","language":"plaintext","code":"local current\ncurrent = redis.call(\"incr\",KEYS[1])\nif current == 1 then\n    redis.call(\"expire\",KEYS[1],1)\nend","section_id":"pattern-rate-limiter"},{"id":"pattern-rate-limiter-ex3","language":"plaintext","code":"FUNCTION LIMIT_API_CALL(ip)\ncurrent = LLEN(ip)\nIF current > 10 THEN\n    ERROR \"too many requests per second\"\nELSE\n    IF EXISTS(ip) == FALSE\n        MULTI\n            RPUSH(ip,ip)\n            EXPIRE(ip,1)\n        EXEC\n    ELSE\n        RPUSHX(ip,ip)\n    END\n    PERFORM_API_CALL()\nEND","section_id":"pattern-rate-limiter"}]}
{"schema_version":2,"id":"commands/incrby","title":"INCRBY","url":"https://redis.io/docs/latest/commands/incrby/","summary":"Increments the integer value of a key by a number. Uses 0 as initial value if the key doesn't exist.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:12+01:00","page_type":"content","content_hash":"58c057c5784047e576631cb70d2fff27725b2c1312949b4967f9562881c22aa6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Increments the number stored at `key` by `increment`.\nIf the key does not exist, it is set to `0` before performing the operation.\nAn error is returned if the key contains a value of the wrong type or contains a\nstring that can not be represented as integer.\nThis operation is limited to 64-bit signed integers.\n\nSee [`INCR`](https://redis.io/docs/latest/commands/incr) for extra information on increment/decrement operations."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key.\n\n</details>\n\n<details open><summary><code>increment</code></summary>\n\nThe integer amount to add to the value.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SET mykey \"10\"\nOK\nredis> INCRBY mykey 5\n(integer) 15"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the value of the key after the increment.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the value of the key after the increment."}],"examples":[]}
{"schema_version":2,"id":"commands/incrbyfloat","title":"INCRBYFLOAT","url":"https://redis.io/docs/latest/commands/incrbyfloat/","summary":"Increment the floating point value of a key by a number. Uses 0 as initial value if the key doesn't exist.","since":"2.6.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:12+01:00","page_type":"content","content_hash":"281c4580a337d31a90da063d30d4dbe4eec15616c1f26821baad386df005ed56","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Increment the string representing a floating point number stored at `key` by the\nspecified `increment`. By using a negative `increment` value, the result is\nthat the value stored at the key is decremented (by the obvious properties\nof addition).\nIf the key does not exist, it is set to `0` before performing the operation.\nAn error is returned if one of the following conditions occur:\n\n* The key contains a value of the wrong type (not a string).\n* The current key content or the specified increment are not parsable as a\n  double precision floating point number.\n\nIf the command is successful the new incremented value is stored as the new\nvalue of the key (replacing the old one), and returned to the caller as a\nstring.\n\nBoth the value already contained in the string key and the increment argument\ncan be optionally provided in exponential notation, however the value computed\nafter the increment is stored consistently in the same format, that is, an\ninteger number followed (if needed) by a dot, and a variable number of digits\nrepresenting the decimal part of the number.\nTrailing zeroes are always removed.\n\nThe precision of the output is fixed at 17 digits after the decimal point\nregardless of the actual internal precision of the computation."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key.\n\n</details>\n\n<details open><summary><code>increment</code></summary>\n\nThe floating-point amount to add to the value. A negative value decrements the value instead.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SET mykey 10.50\nOK\nredis> INCRBYFLOAT mykey 0.1\n\"10.6\"\nredis> INCRBYFLOAT mykey -5\n\"5.6\"\nredis> SET mykey 5.0e3\nOK\nredis> INCRBYFLOAT mykey 2.0e2\n\"5200\""},{"id":"details","title":"Details","role":"content","text":""},{"id":"implementation","title":"Implementation","role":"content","text":"The command is always propagated in the replication link and the Append Only\nFile as a [`SET`](https://redis.io/docs/latest/commands/set) operation, so that differences in the underlying floating point\nmath implementation will not be sources of inconsistency."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the value of the key after the increment.\n\n**RESP3:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the value of the key after the increment."}],"examples":[]}
{"schema_version":2,"id":"commands/increx","title":"INCREX","url":"https://redis.io/docs/latest/commands/increx/","summary":"Increments the numeric value of a key by a number and sets its expiration time. Uses 0 as initial value if the key doesn't exist.","since":"8.8.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:12+01:00","page_type":"content","content_hash":"eb1cabaa1d997e6133b349e266d48243419d91c3911fc9c38b250984a370702d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Increments or decrements the numeric value stored at `key` by the specified amount, with optional upper/lower bounds and expiration control, in a single atomic operation.\nIf the key does not exist, it is set to `0` before performing the operation.\nAn error is returned if the key contains a value of the wrong type or a string that cannot be interpreted as a number.\n\nUnlike [`INCR`](https://redis.io/docs/latest/commands/incr) and [`INCRBY`](https://redis.io/docs/latest/commands/incrby), `INCREX` returns an array of two elements: the new value of the key after the increment, and the increment that was actually applied. When the computed result would fall outside an explicit `LBOUND`/`UBOUND` or the type limits, the default is to skip the operation and reply with `[current_value, 0]`, leaving the key and its TTL untouched. The `SATURATE` flag changes this behavior so the result is capped at the bound instead."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key to increment.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>BYFLOAT increment | BYINT increment</code></summary>\n\nSpecifies the increment amount and type:\n\n* `BYFLOAT increment`: increment the value by the given long-double float. The key's existing value may be either an integer or a float, since integers can be promoted to floats losslessly. Results that would produce NaN or Infinity are rejected.\n* `BYINT increment`: increment the value by the given 64-bit signed integer. The increment may be negative to decrement the value. `BYINT` requires the key's existing value to be integer-typed; a stored float such as `\"1.5\"` cannot be parsed back as an integer. This is consistent with [`INCR`](https://redis.io/docs/latest/commands/incr)/[`INCRBY`](https://redis.io/docs/latest/commands/incrby) (integer-only) and [`INCRBYFLOAT`](https://redis.io/docs/latest/commands/incrbyfloat) (accepts both).\n\nIf neither `BYFLOAT` nor `BYINT` is specified, the key is incremented by `1` in integer mode. `BYFLOAT` and `BYINT` are mutually exclusive.\n\n</details>\n\n<details open><summary><code>LBOUND lowerbound</code></summary>\n\nSets a lower bound for the resulting value. If the computed result would fall below `lowerbound`, the operation is skipped and the reply is `[current_value, 0]` (or use the `SATURATE` flag to floor the result at `lowerbound` instead). When omitted, the bound is `LLONG_MIN` in integer mode or `-LDBL_MAX` in `BYFLOAT` mode. `LBOUND` must be less than or equal to `UBOUND` when both are specified.\n\n</details>\n\n<details open><summary><code>UBOUND upperbound</code></summary>\n\nSets an upper bound for the resulting value. If the computed result would exceed `upperbound`, the operation is skipped and the reply is `[current_value, 0]` (or use the `SATURATE` flag to cap the result at `upperbound` instead). When omitted, the bound is `LLONG_MAX` in integer mode or `LDBL_MAX` in `BYFLOAT` mode. `UBOUND` must be greater than or equal to `LBOUND` when both are specified.\n\n</details>\n\n<details open><summary><code>SATURATE</code></summary>\n\nWhen specified, an out-of-bounds result is capped at `UBOUND` or floored at `LBOUND` (or saturated to the type limits when no explicit bound is given). The second element of the reply reflects the saturated delta. An error is returned if the delta cannot be represented as a 64-bit signed integer in integer mode, or would produce Infinity in `BYFLOAT` mode. Any expiration option is still applied as specified.\n\nA bound violation includes both exceeding an explicit `LBOUND`/`UBOUND` and overflowing the type limits when no explicit bound is given.\n\n</details>\n\n<details open><summary><code>expiration flags</code></summary>\n\nThe `INCREX` command supports a set of options that modify its expiration behavior:\n\n* `EX seconds`: set the specified expiration time in seconds (a positive integer).\n* `PX milliseconds`: set the specified expiration time in milliseconds (a positive integer).\n* `EXAT unix-time-seconds`: set the specified Unix time in seconds (a positive integer) at which the key will expire.\n* `PXAT unix-time-milliseconds`: set the specified Unix time in milliseconds (a positive integer) at which the key will expire.\n* `PERSIST`: remove the expiration associated with the key.\n\nWhen no expiration option is given, the key's existing TTL (if any) is preserved.\n\n</details>\n\n<details open><summary><code>ENX</code></summary>\n\nOnly sets the TTL/expiration if the key currently has no TTL/expiration. If the key already has a TTL, the increment is still applied but the TTL is left unchanged. `ENX` can ensure that a window counter rate limiter's TTL is set only when it is created, and not reset on subsequent token requests.`ENX` must be combined with `EX`, `PX`, `EXAT`, or `PXAT` and is incompatible with `PERSIST`.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"Default increment (by 1), starting from 0 if the key does not exist:\n\n\nredis> DEL mykey1\n(integer) 0\nredis> INCREX mykey1\n1) (integer) 1\n2) (integer) 1\nredis> INCREX mykey1\n1) (integer) 2\n2) (integer) 1\n\n\nIncrement by a specific integer using `BYINT`, including a negative increment to decrement:\n\n\nredis> SET mykey2 100\nOK\nredis> INCREX mykey2 BYINT 5\n1) (integer) 105\n2) (integer) 5\nredis> INCREX mykey2 BYINT -10\n1) (integer) 95\n2) (integer) -10\n\n\nIncrement by a floating-point number using `BYFLOAT`:\n\n\nredis> SET mykey3 1.5\nOK\nredis> INCREX mykey3 BYFLOAT 0.25\n1) \"1.75\"\n2) \"0.25\"\n\n\nSet an expiration on every increment with `EX`:\n\n\nredis> DEL mykey4\n(integer) 0\nredis> INCREX mykey4 BYINT 1 EX 100\n1) (integer) 1\n2) (integer) 1\nredis> TTL mykey4\n(integer) 100\n\n\nUse `ENX` to set an expiration only when the key has no existing TTL. The increment is always applied regardless:\n\n\nredis> SET mykey5 10\nOK\nredis> INCREX mykey5 BYINT 1 EX 100 ENX\n1) (integer) 11\n2) (integer) 1\nredis> TTL mykey5\n(integer) 100\nredis> SET mykey5 10 EX 500\nOK\nredis> INCREX mykey5 BYINT 1 EX 10 ENX\n1) (integer) 11\n2) (integer) 1\nredis> TTL mykey5\n(integer) 500\n\n\nUse `PERSIST` to remove the key's expiration while incrementing:\n\n\nredis> SET mykey6 5 EX 1000\nOK\nredis> TTL mykey6\n(integer) 1000\nredis> INCREX mykey6 BYINT 1 PERSIST\n1) (integer) 6\n2) (integer) 1\nredis> TTL mykey6\n(integer) -1\n\n\nCompare the default out-of-bounds behavior with `SATURATE` when the result would exceed `UBOUND`. By default the key is left untouched and the reply reports a zero delta; with `SATURATE` the result is capped at the bound and the reply reflects the saturated delta:\n\n\nredis> SET mykey7 99\nOK\nredis> INCREX mykey7 BYINT 5 UBOUND 100\n1) (integer) 99\n2) (integer) 0\nredis> SET mykey7 99\nOK\nredis> INCREX mykey7 BYINT 5 UBOUND 100 SATURATE\n1) (integer) 100\n2) (integer) 1"},{"id":"details","title":"Details","role":"content","text":""},{"id":"pattern-window-counter-rate-limiter","title":"Pattern: window counter rate limiter","role":"content","text":"A common rate-limiting pattern requires atomically incrementing a counter and setting its expiration. With plain [`INCR`](https://redis.io/docs/latest/commands/incr) and [`EXPIRE`](https://redis.io/docs/latest/commands/expire), this typically requires a Lua script to be atomic.\n\n`INCREX` requires a single native command. `UBOUND` enforces the rate cap — by default, once the cap is reached the operation is skipped — and `ENX` ensures that a new window with the correct duration is created if the previous one has expired; if a window already exists, it won't be extended. When the counter has already reached the cap, `actual_increment` is `0`, giving the caller immediate feedback without extra reads or error handling:\n\n[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): a two-element array:\n\n1. **New value** — the value of the key after the increment, or the unchanged current value when an out-of-bounds result caused the operation to be skipped.\n2. **Actual increment** — the increment that was actually applied. May differ from the requested increment when `SATURATE` caps the result at a bound, and is always `0` when an out-of-bounds result caused the operation to be skipped.\n\nBoth elements are [Integer replies](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) in integer mode (default or `BYINT`), or [Bulk string replies](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) representing the float values in `BYFLOAT` mode.\n\n**RESP3:**\n\n[Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): a two-element array:\n\n1. **New value** — the value of the key after the increment, or the unchanged current value when an out-of-bounds result caused the operation to be skipped.\n2. **Actual increment** — the increment that was actually applied. May differ from the requested increment when `SATURATE` caps the result at a bound, and is always `0` when an out-of-bounds result caused the operation to be skipped.\n\nBoth elements are [Integer replies](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) in integer mode (default or `BYINT`), or [Double replies](https://redis.io/docs/latest/develop/reference/protocol-spec#doubles) in `BYFLOAT` mode."}],"examples":[{"id":"pattern-window-counter-rate-limiter-ex0","language":"python","code":"new_val, actual_incr = redis.execute_command(\n    \"INCREX\", f\"ratelimit:{user_id}\",\n    \"BYINT\", 1, \"UBOUND\", 100,\n    \"EX\", 60, \"ENX\",\n)\nif actual_incr == 0:\n    reject_request()  # rate limit exceeded","section_id":"pattern-window-counter-rate-limiter"}]}
{"schema_version":2,"id":"commands","title":"Commands","url":"https://redis.io/docs/latest/commands/","summary":"","tags":[],"last_updated":"2026-10-16T18:59:00+05:30","children":[{"id":"commands/redis-8-10-commands","summary":"Complete list of all Redis commands available in version 8.10, organized by functional group","title":"Redis 8.10 Commands Reference","url":"https://redis.io/docs/latest/commands/redis-8-10-commands/"},{"id":"commands/redis-8-8-commands","summary":"Complete list of all Redis commands available in version 8.8, organized by functional group","title":"Redis 8.8 Commands Reference","url":"https://redis.io/docs/latest/commands/redis-8-8-commands/"},{"id":"commands/redis-8-6-commands","summary":"Complete list of all Redis commands available in version 8.6, organized by functional group","title":"Redis 8.6 Commands Reference","url":"https://redis.io/docs/latest/commands/redis-8-6-commands/"},{"id":"commands/redis-8-4-commands","summary":"Complete list of all Redis commands available in version 8.4, organized by functional group","title":"Redis 8.4 Commands Reference","url":"https://redis.io/docs/latest/commands/redis-8-4-commands/"},{"id":"commands/redis-8-2-commands","summary":"Complete list of all Redis commands available in version 8.2, organized by functional group","title":"Redis 8.2 Commands Reference","url":"https://redis.io/docs/latest/commands/redis-8-2-commands/"},{"id":"commands/redis-8-0-commands","summary":"Complete list of all Redis commands available in version 8.0, organized by functional group","title":"Redis 8.0 Commands Reference","url":"https://redis.io/docs/latest/commands/redis-8-0-commands/"},{"id":"commands/redis-7-4-commands","summary":"Complete list of all Redis commands available in version 7.4, organized by functional group","title":"Redis 7.4 Commands Reference","url":"https://redis.io/docs/latest/commands/redis-7-4-commands/"},{"id":"commands/redis-7-2-commands","summary":"Complete list of all Redis commands available in version 7.2, organized by functional group","title":"Redis 7.2 Commands Reference","url":"https://redis.io/docs/latest/commands/redis-7-2-commands/"},{"id":"commands/redis-6-2-commands","summary":"Complete list of all Redis commands available in version 6.2, organized by functional group","title":"Redis 6.2 Commands Reference","url":"https://redis.io/docs/latest/commands/redis-6-2-commands/"},{"id":"commands/acl","summary":"A container for Access List Control commands.","title":"ACL","url":"https://redis.io/docs/latest/commands/acl/"},{"id":"commands/acl-cat","summary":"Lists the ACL categories, or the commands inside a category.","title":"ACL CAT","url":"https://redis.io/docs/latest/commands/acl-cat/"},{"id":"commands/acl-deluser","summary":"Deletes ACL users, and terminates their connections.","title":"ACL DELUSER","url":"https://redis.io/docs/latest/commands/acl-deluser/"},{"id":"commands/acl-dryrun","summary":"Simulates the execution of a command by a user, without executing the command.","title":"ACL DRYRUN","url":"https://redis.io/docs/latest/commands/acl-dryrun/"},{"id":"commands/acl-genpass","summary":"Generates a pseudorandom, secure password that can be used to identify ACL users.","title":"ACL GENPASS","url":"https://redis.io/docs/latest/commands/acl-genpass/"},{"id":"commands/acl-getuser","summary":"Lists the ACL rules of a user.","title":"ACL GETUSER","url":"https://redis.io/docs/latest/commands/acl-getuser/"},{"id":"commands/acl-help","summary":"Returns helpful text about the different subcommands.","title":"ACL HELP","url":"https://redis.io/docs/latest/commands/acl-help/"},{"id":"commands/acl-list","summary":"Dumps the effective rules in ACL file format.","title":"ACL LIST","url":"https://redis.io/docs/latest/commands/acl-list/"},{"id":"commands/acl-load","summary":"Reloads the rules from the configured ACL file.","title":"ACL LOAD","url":"https://redis.io/docs/latest/commands/acl-load/"},{"id":"commands/acl-log","summary":"Lists recent security events generated due to ACL rules.","title":"ACL LOG","url":"https://redis.io/docs/latest/commands/acl-log/"},{"id":"commands/acl-save","summary":"Saves the effective ACL rules in the configured ACL file.","title":"ACL SAVE","url":"https://redis.io/docs/latest/commands/acl-save/"},{"id":"commands/acl-setuser","summary":"Creates and modifies an ACL user and its rules.","title":"ACL SETUSER","url":"https://redis.io/docs/latest/commands/acl-setuser/"},{"id":"commands/acl-users","summary":"Lists all ACL users.","title":"ACL USERS","url":"https://redis.io/docs/latest/commands/acl-users/"},{"id":"commands/acl-whoami","summary":"Returns the authenticated username of the current connection.","title":"ACL WHOAMI","url":"https://redis.io/docs/latest/commands/acl-whoami/"},{"id":"commands/append","summary":"Appends a string to the value of a key. Creates the key if it doesn't exist.","title":"APPEND","url":"https://redis.io/docs/latest/commands/append/"},{"id":"commands/arcount","summary":"Returns the number of non-empty elements in an array.","title":"ARCOUNT","url":"https://redis.io/docs/latest/commands/arcount/"},{"id":"commands/ardel","summary":"Deletes elements at the specified indices in an array.","title":"ARDEL","url":"https://redis.io/docs/latest/commands/ardel/"},{"id":"commands/ardelrange","summary":"Deletes elements in one or more ranges.","title":"ARDELRANGE","url":"https://redis.io/docs/latest/commands/ardelrange/"},{"id":"commands/arget","summary":"Gets the value at an index in an array.","title":"ARGET","url":"https://redis.io/docs/latest/commands/arget/"},{"id":"commands/argetrange","summary":"Gets values in a range of indices.","title":"ARGETRANGE","url":"https://redis.io/docs/latest/commands/argetrange/"},{"id":"commands/argrep","summary":"Searches array elements in a range using textual predicates.","title":"ARGREP","url":"https://redis.io/docs/latest/commands/argrep/"},{"id":"commands/arinfo","summary":"Returns metadata about an array.","title":"ARINFO","url":"https://redis.io/docs/latest/commands/arinfo/"},{"id":"commands/arinsert","summary":"Inserts one or more values at consecutive indices.","title":"ARINSERT","url":"https://redis.io/docs/latest/commands/arinsert/"},{"id":"commands/arlastitems","summary":"Returns the most recently inserted elements.","title":"ARLASTITEMS","url":"https://redis.io/docs/latest/commands/arlastitems/"},{"id":"commands/arlen","summary":"Returns the length of an array (max index + 1).","title":"ARLEN","url":"https://redis.io/docs/latest/commands/arlen/"},{"id":"commands/armget","summary":"Gets values at multiple indices in an array.","title":"ARMGET","url":"https://redis.io/docs/latest/commands/armget/"},{"id":"commands/armset","summary":"Sets multiple index-value pairs in an array.","title":"ARMSET","url":"https://redis.io/docs/latest/commands/armset/"},{"id":"commands/arnext","summary":"Returns the next index ARINSERT would use.","title":"ARNEXT","url":"https://redis.io/docs/latest/commands/arnext/"},{"id":"commands/arop","summary":"Performs aggregate operations on array elements in a range.","title":"AROP","url":"https://redis.io/docs/latest/commands/arop/"},{"id":"commands/arring","summary":"Inserts values into a ring buffer of specified size, wrapping and truncating as needed.","title":"ARRING","url":"https://redis.io/docs/latest/commands/arring/"},{"id":"commands/arscan","summary":"Iterates existing elements in a range, returning index-value pairs.","title":"ARSCAN","url":"https://redis.io/docs/latest/commands/arscan/"},{"id":"commands/arseek","summary":"Sets the ARINSERT / ARRING cursor to a specific index.","title":"ARSEEK","url":"https://redis.io/docs/latest/commands/arseek/"},{"id":"commands/arset","summary":"Sets one or more contiguous values starting at an index in an array.","title":"ARSET","url":"https://redis.io/docs/latest/commands/arset/"},{"id":"commands/asking","summary":"Signals that a cluster client is following an -ASK redirect.","title":"ASKING","url":"https://redis.io/docs/latest/commands/asking/"},{"id":"commands/auth","summary":"Authenticates the connection.","title":"AUTH","url":"https://redis.io/docs/latest/commands/auth/"},{"id":"commands/backup","summary":"A container for backup management commands.","title":"BACKUP","url":"https://redis.io/docs/latest/commands/backup/"},{"id":"commands/backup-abort","summary":"Cancel a backup that has not been sealed yet.","title":"BACKUP ABORT","url":"https://redis.io/docs/latest/commands/backup-abort/"},{"id":"commands/backup-cleanup","summary":"Remove sealed backup files and return to idle.","title":"BACKUP CLEANUP","url":"https://redis.io/docs/latest/commands/backup-cleanup/"},{"id":"commands/backup-help","summary":"Return helpful text about BACKUP command parameters.","title":"BACKUP HELP","url":"https://redis.io/docs/latest/commands/backup-help/"},{"id":"commands/backup-list","summary":"List the immutable backup file paths pinned so far.","title":"BACKUP LIST","url":"https://redis.io/docs/latest/commands/backup-list/"},{"id":"commands/backup-seal","summary":"Freeze the current backup (BASE + INCR + manifest).","title":"BACKUP SEAL","url":"https://redis.io/docs/latest/commands/backup-seal/"},{"id":"commands/backup-start","summary":"Start a new backup into the configured 'backupdirname'.","title":"BACKUP START","url":"https://redis.io/docs/latest/commands/backup-start/"},{"id":"commands/backup-status","summary":"Report the current backup state.","title":"BACKUP STATUS","url":"https://redis.io/docs/latest/commands/backup-status/"},{"id":"commands/bf.add","summary":"Adds an item to a Bloom Filter","title":"BF.ADD","url":"https://redis.io/docs/latest/commands/bf.add/"},{"id":"commands/bf.card","summary":"Returns the cardinality of a Bloom filter","title":"BF.CARD","url":"https://redis.io/docs/latest/commands/bf.card/"},{"id":"commands/bf.exists","summary":"Checks whether an item exists in a Bloom Filter","title":"BF.EXISTS","url":"https://redis.io/docs/latest/commands/bf.exists/"},{"id":"commands/bf.info","summary":"Returns information about a Bloom Filter","title":"BF.INFO","url":"https://redis.io/docs/latest/commands/bf.info/"},{"id":"commands/bf.insert","summary":"Adds one or more items to a Bloom Filter. A filter will be created if it does not exist","title":"BF.INSERT","url":"https://redis.io/docs/latest/commands/bf.insert/"},{"id":"commands/bf.loadchunk","summary":"Restores a filter previously saved using SCANDUMP","title":"BF.LOADCHUNK","url":"https://redis.io/docs/latest/commands/bf.loadchunk/"},{"id":"commands/bf.madd","summary":"Adds one or more items to a Bloom Filter. A filter will be created if it does not exist","title":"BF.MADD","url":"https://redis.io/docs/latest/commands/bf.madd/"},{"id":"commands/bf.mexists","summary":"Checks whether one or more items exist in a Bloom Filter","title":"BF.MEXISTS","url":"https://redis.io/docs/latest/commands/bf.mexists/"},{"id":"commands/bf.reserve","summary":"Creates a new Bloom Filter","title":"BF.RESERVE","url":"https://redis.io/docs/latest/commands/bf.reserve/"},{"id":"commands/bf.scandump","summary":"Begins an incremental save of the bloom filter","title":"BF.SCANDUMP","url":"https://redis.io/docs/latest/commands/bf.scandump/"},{"id":"commands/bgrewriteaof","summary":"Asynchronously rewrites the append-only file to disk.","title":"BGREWRITEAOF","url":"https://redis.io/docs/latest/commands/bgrewriteaof/"},{"id":"commands/bgsave","summary":"Asynchronously saves the database(s) to disk.","title":"BGSAVE","url":"https://redis.io/docs/latest/commands/bgsave/"},{"id":"commands/bitcount","summary":"Counts the number of set bits (population counting) in a string.","title":"BITCOUNT","url":"https://redis.io/docs/latest/commands/bitcount/"},{"id":"commands/bitfield","summary":"Performs arbitrary bitfield integer operations on strings.","title":"BITFIELD","url":"https://redis.io/docs/latest/commands/bitfield/"},{"id":"commands/bitfield_ro","summary":"Performs arbitrary read-only bitfield integer operations on strings.","title":"BITFIELD_RO","url":"https://redis.io/docs/latest/commands/bitfield_ro/"},{"id":"commands/bitop","summary":"Performs bitwise operations on multiple strings, and stores the result.","title":"BITOP","url":"https://redis.io/docs/latest/commands/bitop/"},{"id":"commands/bitpos","summary":"Finds the first set (1) or clear (0) bit in a string.","title":"BITPOS","url":"https://redis.io/docs/latest/commands/bitpos/"},{"id":"commands/blmove","summary":"Pops an element from a list, pushes it to another list and returns it. Blocks until an element is available otherwise. Deletes the list if the last element was moved.","title":"BLMOVE","url":"https://redis.io/docs/latest/commands/blmove/"},{"id":"commands/blmovem","summary":"Moves up to (or exactly) a number of elements from one list to another and returns them. Blocks until the elements are available otherwise. Deletes the source list if it becomes empty.","title":"BLMOVEM","url":"https://redis.io/docs/latest/commands/blmovem/"},{"id":"commands/blmpop","summary":"Pops the first element from one of multiple lists. Blocks until an element is available otherwise. Deletes the list if the last element was popped.","title":"BLMPOP","url":"https://redis.io/docs/latest/commands/blmpop/"},{"id":"commands/blpop","summary":"Removes and returns the first element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.","title":"BLPOP","url":"https://redis.io/docs/latest/commands/blpop/"},{"id":"commands/brpop","summary":"Removes and returns the last element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.","title":"BRPOP","url":"https://redis.io/docs/latest/commands/brpop/"},{"id":"commands/brpoplpush","summary":"Pops an element from a list, pushes it to another list and returns it. Block until an element is available otherwise. Deletes the list if the last element was popped.","title":"BRPOPLPUSH","url":"https://redis.io/docs/latest/commands/brpoplpush/"},{"id":"commands/bzmpop","summary":"Removes and returns a member by score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.","title":"BZMPOP","url":"https://redis.io/docs/latest/commands/bzmpop/"},{"id":"commands/bzpopmax","summary":"Removes and returns the member with the highest score from one or more sorted sets. Blocks until a member available otherwise. Deletes the sorted set if the last element was popped.","title":"BZPOPMAX","url":"https://redis.io/docs/latest/commands/bzpopmax/"},{"id":"commands/bzpopmin","summary":"Removes and returns the member with the lowest score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.","title":"BZPOPMIN","url":"https://redis.io/docs/latest/commands/bzpopmin/"},{"id":"commands/cf.add","summary":"Adds an item to a Cuckoo Filter","title":"CF.ADD","url":"https://redis.io/docs/latest/commands/cf.add/"},{"id":"commands/cf.addnx","summary":"Adds an item to a Cuckoo Filter if the item did not exist previously.","title":"CF.ADDNX","url":"https://redis.io/docs/latest/commands/cf.addnx/"},{"id":"commands/cf.count","summary":"Return the number of times an item might be in a Cuckoo Filter","title":"CF.COUNT","url":"https://redis.io/docs/latest/commands/cf.count/"},{"id":"commands/cf.del","summary":"Deletes an item from a Cuckoo Filter","title":"CF.DEL","url":"https://redis.io/docs/latest/commands/cf.del/"},{"id":"commands/cf.exists","summary":"Checks whether one or more items exist in a Cuckoo Filter","title":"CF.EXISTS","url":"https://redis.io/docs/latest/commands/cf.exists/"},{"id":"commands/cf.info","summary":"Returns information about a Cuckoo Filter","title":"CF.INFO","url":"https://redis.io/docs/latest/commands/cf.info/"},{"id":"commands/cf.insert","summary":"Adds one or more items to a Cuckoo Filter. A filter will be created if it does not exist","title":"CF.INSERT","url":"https://redis.io/docs/latest/commands/cf.insert/"},{"id":"commands/cf.insertnx","summary":"Adds one or more items to a Cuckoo Filter if the items did not exist previously. A filter will be created if it does not exist","title":"CF.INSERTNX","url":"https://redis.io/docs/latest/commands/cf.insertnx/"},{"id":"commands/cf.loadchunk","summary":"Restores a filter previously saved using SCANDUMP","title":"CF.LOADCHUNK","url":"https://redis.io/docs/latest/commands/cf.loadchunk/"},{"id":"commands/cf.mexists","summary":"Checks whether one or more items exist in a Cuckoo Filter","title":"CF.MEXISTS","url":"https://redis.io/docs/latest/commands/cf.mexists/"},{"id":"commands/cf.reserve","summary":"Creates a new Cuckoo Filter","title":"CF.RESERVE","url":"https://redis.io/docs/latest/commands/cf.reserve/"},{"id":"commands/cf.scandump","summary":"Begins an incremental save of the bloom filter","title":"CF.SCANDUMP","url":"https://redis.io/docs/latest/commands/cf.scandump/"},{"id":"commands/client","summary":"A container for client connection commands.","title":"CLIENT","url":"https://redis.io/docs/latest/commands/client/"},{"id":"commands/client-caching","summary":"Instructs the server whether to track the keys in the next request.","title":"CLIENT CACHING","url":"https://redis.io/docs/latest/commands/client-caching/"},{"id":"commands/client-getname","summary":"Returns the name of the connection.","title":"CLIENT GETNAME","url":"https://redis.io/docs/latest/commands/client-getname/"},{"id":"commands/client-getredir","summary":"Returns the client ID to which the connection's tracking notifications are redirected.","title":"CLIENT GETREDIR","url":"https://redis.io/docs/latest/commands/client-getredir/"},{"id":"commands/client-help","summary":"Returns helpful text about the different subcommands.","title":"CLIENT HELP","url":"https://redis.io/docs/latest/commands/client-help/"},{"id":"commands/client-id","summary":"Returns the unique client ID of the connection.","title":"CLIENT ID","url":"https://redis.io/docs/latest/commands/client-id/"},{"id":"commands/client-info","summary":"Returns information about the connection.","title":"CLIENT INFO","url":"https://redis.io/docs/latest/commands/client-info/"},{"id":"commands/client-kill","summary":"Terminates open connections.","title":"CLIENT KILL","url":"https://redis.io/docs/latest/commands/client-kill/"},{"id":"commands/client-list","summary":"Lists open connections.","title":"CLIENT LIST","url":"https://redis.io/docs/latest/commands/client-list/"},{"id":"commands/client-no-evict","summary":"Sets the client eviction mode of the connection.","title":"CLIENT NO-EVICT","url":"https://redis.io/docs/latest/commands/client-no-evict/"},{"id":"commands/client-no-touch","summary":"Controls whether commands sent by the client affect the LRU/LFU of accessed keys.","title":"CLIENT NO-TOUCH","url":"https://redis.io/docs/latest/commands/client-no-touch/"},{"id":"commands/client-pause","summary":"Suspends commands processing.","title":"CLIENT PAUSE","url":"https://redis.io/docs/latest/commands/client-pause/"},{"id":"commands/client-reply","summary":"Instructs the server whether to reply to commands.","title":"CLIENT REPLY","url":"https://redis.io/docs/latest/commands/client-reply/"},{"id":"commands/client-setinfo","summary":"Sets information specific to the client or connection.","title":"CLIENT SETINFO","url":"https://redis.io/docs/latest/commands/client-setinfo/"},{"id":"commands/client-setname","summary":"Sets the connection name.","title":"CLIENT SETNAME","url":"https://redis.io/docs/latest/commands/client-setname/"},{"id":"commands/client-tracking","summary":"Controls server-assisted client-side caching for the connection.","title":"CLIENT TRACKING","url":"https://redis.io/docs/latest/commands/client-tracking/"},{"id":"commands/client-trackinginfo","summary":"Returns information about server-assisted client-side caching for the connection.","title":"CLIENT TRACKINGINFO","url":"https://redis.io/docs/latest/commands/client-trackinginfo/"},{"id":"commands/client-unblock","summary":"Unblocks a client blocked by a blocking command from a different connection.","title":"CLIENT UNBLOCK","url":"https://redis.io/docs/latest/commands/client-unblock/"},{"id":"commands/client-unpause","summary":"Resumes processing commands from paused clients.","title":"CLIENT UNPAUSE","url":"https://redis.io/docs/latest/commands/client-unpause/"},{"id":"commands/cluster","summary":"A container for Redis Cluster commands.","title":"CLUSTER","url":"https://redis.io/docs/latest/commands/cluster/"},{"id":"commands/cluster-addslots","summary":"Assigns new hash slots to a node.","title":"CLUSTER ADDSLOTS","url":"https://redis.io/docs/latest/commands/cluster-addslots/"},{"id":"commands/cluster-addslotsrange","summary":"Assigns new hash slot ranges to a node.","title":"CLUSTER ADDSLOTSRANGE","url":"https://redis.io/docs/latest/commands/cluster-addslotsrange/"},{"id":"commands/cluster-bumpepoch","summary":"Advances the cluster config epoch.","title":"CLUSTER BUMPEPOCH","url":"https://redis.io/docs/latest/commands/cluster-bumpepoch/"},{"id":"commands/cluster-count-failure-reports","summary":"Returns the number of active failure reports active for a node.","title":"CLUSTER COUNT-FAILURE-REPORTS","url":"https://redis.io/docs/latest/commands/cluster-count-failure-reports/"},{"id":"commands/cluster-countkeysinslot","summary":"Returns the number of keys in a hash slot.","title":"CLUSTER COUNTKEYSINSLOT","url":"https://redis.io/docs/latest/commands/cluster-countkeysinslot/"},{"id":"commands/cluster-delslots","summary":"Sets hash slots as unbound for a node.","title":"CLUSTER DELSLOTS","url":"https://redis.io/docs/latest/commands/cluster-delslots/"},{"id":"commands/cluster-delslotsrange","summary":"Sets hash slot ranges as unbound for a node.","title":"CLUSTER DELSLOTSRANGE","url":"https://redis.io/docs/latest/commands/cluster-delslotsrange/"},{"id":"commands/cluster-failover","summary":"Forces a replica to perform a manual failover of its master.","title":"CLUSTER FAILOVER","url":"https://redis.io/docs/latest/commands/cluster-failover/"},{"id":"commands/cluster-flushslots","summary":"Deletes all slots information from a node.","title":"CLUSTER FLUSHSLOTS","url":"https://redis.io/docs/latest/commands/cluster-flushslots/"},{"id":"commands/cluster-forget","summary":"Removes a node from the nodes table.","title":"CLUSTER FORGET","url":"https://redis.io/docs/latest/commands/cluster-forget/"},{"id":"commands/cluster-getkeysinslot","summary":"Returns the key names in a hash slot.","title":"CLUSTER GETKEYSINSLOT","url":"https://redis.io/docs/latest/commands/cluster-getkeysinslot/"},{"id":"commands/cluster-help","summary":"Returns helpful text about the different subcommands.","title":"CLUSTER HELP","url":"https://redis.io/docs/latest/commands/cluster-help/"},{"id":"commands/cluster-info","summary":"Returns information about the state of a node.","title":"CLUSTER INFO","url":"https://redis.io/docs/latest/commands/cluster-info/"},{"id":"commands/cluster-keyslot","summary":"Returns the hash slot for a key.","title":"CLUSTER KEYSLOT","url":"https://redis.io/docs/latest/commands/cluster-keyslot/"},{"id":"commands/cluster-links","summary":"Returns a list of all TCP links to and from peer nodes.","title":"CLUSTER LINKS","url":"https://redis.io/docs/latest/commands/cluster-links/"},{"id":"commands/cluster-meet","summary":"Forces a node to handshake with another node.","title":"CLUSTER MEET","url":"https://redis.io/docs/latest/commands/cluster-meet/"},{"id":"commands/cluster-migration","summary":"Start, monitor, and cancel atomic slot migration tasks.","title":"CLUSTER MIGRATION","url":"https://redis.io/docs/latest/commands/cluster-migration/"},{"id":"commands/cluster-myid","summary":"Returns the ID of a node.","title":"CLUSTER MYID","url":"https://redis.io/docs/latest/commands/cluster-myid/"},{"id":"commands/cluster-myshardid","summary":"Returns the shard ID of a node.","title":"CLUSTER MYSHARDID","url":"https://redis.io/docs/latest/commands/cluster-myshardid/"},{"id":"commands/cluster-nodes","summary":"Returns the cluster configuration for a node.","title":"CLUSTER NODES","url":"https://redis.io/docs/latest/commands/cluster-nodes/"},{"id":"commands/cluster-replicas","summary":"Lists the replica nodes of a master node.","title":"CLUSTER REPLICAS","url":"https://redis.io/docs/latest/commands/cluster-replicas/"},{"id":"commands/cluster-replicate","summary":"Configure a node as replica of a master node.","title":"CLUSTER REPLICATE","url":"https://redis.io/docs/latest/commands/cluster-replicate/"},{"id":"commands/cluster-reset","summary":"Resets a node.","title":"CLUSTER RESET","url":"https://redis.io/docs/latest/commands/cluster-reset/"},{"id":"commands/cluster-saveconfig","summary":"Forces a node to save the cluster configuration to disk.","title":"CLUSTER SAVECONFIG","url":"https://redis.io/docs/latest/commands/cluster-saveconfig/"},{"id":"commands/cluster-set-config-epoch","summary":"Sets the configuration epoch for a new node.","title":"CLUSTER SET-CONFIG-EPOCH","url":"https://redis.io/docs/latest/commands/cluster-set-config-epoch/"},{"id":"commands/cluster-setslot","summary":"Binds a hash slot to a node.","title":"CLUSTER SETSLOT","url":"https://redis.io/docs/latest/commands/cluster-setslot/"},{"id":"commands/cluster-shards","summary":"Returns the mapping of cluster slots to shards.","title":"CLUSTER SHARDS","url":"https://redis.io/docs/latest/commands/cluster-shards/"},{"id":"commands/cluster-slaves","summary":"Lists the replica nodes of a master node.","title":"CLUSTER SLAVES","url":"https://redis.io/docs/latest/commands/cluster-slaves/"},{"id":"commands/cluster-slot-stats","summary":"Return an array of slot usage statistics for slots assigned to the current node.","title":"CLUSTER SLOT-STATS","url":"https://redis.io/docs/latest/commands/cluster-slot-stats/"},{"id":"commands/cluster-slots","summary":"Returns the mapping of cluster slots to nodes.","title":"CLUSTER SLOTS","url":"https://redis.io/docs/latest/commands/cluster-slots/"},{"id":"commands/cms.incrby","summary":"Increases the count of one or more items by increment","title":"CMS.INCRBY","url":"https://redis.io/docs/latest/commands/cms.incrby/"},{"id":"commands/cms.info","summary":"Returns information about a sketch","title":"CMS.INFO","url":"https://redis.io/docs/latest/commands/cms.info/"},{"id":"commands/cms.initbydim","summary":"Initializes a Count-Min Sketch to dimensions specified by user","title":"CMS.INITBYDIM","url":"https://redis.io/docs/latest/commands/cms.initbydim/"},{"id":"commands/cms.initbyprob","summary":"Initializes a Count-Min Sketch to accommodate requested tolerances.","title":"CMS.INITBYPROB","url":"https://redis.io/docs/latest/commands/cms.initbyprob/"},{"id":"commands/cms.merge","summary":"Merges several sketches into one sketch","title":"CMS.MERGE","url":"https://redis.io/docs/latest/commands/cms.merge/"},{"id":"commands/cms.query","summary":"Returns the count for one or more items in a sketch","title":"CMS.QUERY","url":"https://redis.io/docs/latest/commands/cms.query/"},{"id":"commands/command","summary":"Returns detailed information about all commands.","title":"COMMAND","url":"https://redis.io/docs/latest/commands/command/"},{"id":"commands/command-count","summary":"Returns a count of commands.","title":"COMMAND COUNT","url":"https://redis.io/docs/latest/commands/command-count/"},{"id":"commands/command-docs","summary":"Returns documentary information about one, multiple or all commands.","title":"COMMAND DOCS","url":"https://redis.io/docs/latest/commands/command-docs/"},{"id":"commands/command-getkeys","summary":"Extracts the key names from an arbitrary command.","title":"COMMAND GETKEYS","url":"https://redis.io/docs/latest/commands/command-getkeys/"},{"id":"commands/command-getkeysandflags","summary":"Extracts the key names and access flags for an arbitrary command.","title":"COMMAND GETKEYSANDFLAGS","url":"https://redis.io/docs/latest/commands/command-getkeysandflags/"},{"id":"commands/command-help","summary":"Returns helpful text about the different subcommands.","title":"COMMAND HELP","url":"https://redis.io/docs/latest/commands/command-help/"},{"id":"commands/command-info","summary":"Returns information about one, multiple or all commands.","title":"COMMAND INFO","url":"https://redis.io/docs/latest/commands/command-info/"},{"id":"commands/command-list","summary":"Returns a list of command names.","title":"COMMAND LIST","url":"https://redis.io/docs/latest/commands/command-list/"},{"id":"commands/config","summary":"A container for server configuration commands.","title":"CONFIG","url":"https://redis.io/docs/latest/commands/config/"},{"id":"commands/config-get","summary":"Returns the effective values of configuration parameters.","title":"CONFIG GET","url":"https://redis.io/docs/latest/commands/config-get/"},{"id":"commands/config-help","summary":"Returns helpful text about the different subcommands.","title":"CONFIG HELP","url":"https://redis.io/docs/latest/commands/config-help/"},{"id":"commands/config-resetstat","summary":"Resets the server's statistics.","title":"CONFIG RESETSTAT","url":"https://redis.io/docs/latest/commands/config-resetstat/"},{"id":"commands/config-rewrite","summary":"Persists the effective configuration to file.","title":"CONFIG REWRITE","url":"https://redis.io/docs/latest/commands/config-rewrite/"},{"id":"commands/config-set","summary":"Sets configuration parameters in-flight.","title":"CONFIG SET","url":"https://redis.io/docs/latest/commands/config-set/"},{"id":"commands/copy","summary":"Copies the value of a key to a new key.","title":"COPY","url":"https://redis.io/docs/latest/commands/copy/"},{"id":"commands/dbsize","summary":"Returns the number of keys in the database.","title":"DBSIZE","url":"https://redis.io/docs/latest/commands/dbsize/"},{"id":"commands/debug","summary":"A container for debugging commands.","title":"DEBUG","url":"https://redis.io/docs/latest/commands/debug/"},{"id":"commands/decr","summary":"Decrements the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.","title":"DECR","url":"https://redis.io/docs/latest/commands/decr/"},{"id":"commands/decrby","summary":"Decrements a number from the integer value of a key. Uses 0 as initial value if the key doesn't exist.","title":"DECRBY","url":"https://redis.io/docs/latest/commands/decrby/"},{"id":"commands/del","summary":"Deletes one or more keys.","title":"DEL","url":"https://redis.io/docs/latest/commands/del/"},{"id":"commands/delex","summary":"Conditionally removes the specified key based on value or hash digest comparison.","title":"DELEX","url":"https://redis.io/docs/latest/commands/delex/"},{"id":"commands/digest","summary":"Returns the hash digest of a string value.","title":"DIGEST","url":"https://redis.io/docs/latest/commands/digest/"},{"id":"commands/discard","summary":"Discards a transaction.","title":"DISCARD","url":"https://redis.io/docs/latest/commands/discard/"},{"id":"commands/dump","summary":"Returns a serialized representation of the value stored at a key.","title":"DUMP","url":"https://redis.io/docs/latest/commands/dump/"},{"id":"commands/echo","summary":"Returns the given string.","title":"ECHO","url":"https://redis.io/docs/latest/commands/echo/"},{"id":"commands/eval","summary":"Executes a server-side Lua script.","title":"EVAL","url":"https://redis.io/docs/latest/commands/eval/"},{"id":"commands/eval_ro","summary":"Executes a read-only server-side Lua script.","title":"EVAL_RO","url":"https://redis.io/docs/latest/commands/eval_ro/"},{"id":"commands/evalsha","summary":"Executes a server-side Lua script by SHA1 digest.","title":"EVALSHA","url":"https://redis.io/docs/latest/commands/evalsha/"},{"id":"commands/evalsha_ro","summary":"Executes a read-only server-side Lua script by SHA1 digest.","title":"EVALSHA_RO","url":"https://redis.io/docs/latest/commands/evalsha_ro/"},{"id":"commands/exec","summary":"Executes all commands in a transaction.","title":"EXEC","url":"https://redis.io/docs/latest/commands/exec/"},{"id":"commands/exists","summary":"Determines whether one or more keys exist.","title":"EXISTS","url":"https://redis.io/docs/latest/commands/exists/"},{"id":"commands/expire","summary":"Sets the expiration time of a key in seconds.","title":"EXPIRE","url":"https://redis.io/docs/latest/commands/expire/"},{"id":"commands/expireat","summary":"Sets the expiration time of a key to a Unix timestamp.","title":"EXPIREAT","url":"https://redis.io/docs/latest/commands/expireat/"},{"id":"commands/expiretime","summary":"Returns the expiration time of a key as a Unix timestamp.","title":"EXPIRETIME","url":"https://redis.io/docs/latest/commands/expiretime/"},{"id":"commands/failover","summary":"Starts a coordinated failover from a server to one of its replicas.","title":"FAILOVER","url":"https://redis.io/docs/latest/commands/failover/"},{"id":"commands/fcall","summary":"Invokes a function.","title":"FCALL","url":"https://redis.io/docs/latest/commands/fcall/"},{"id":"commands/fcall_ro","summary":"Invokes a read-only function.","title":"FCALL_RO","url":"https://redis.io/docs/latest/commands/fcall_ro/"},{"id":"commands/flushall","summary":"Removes all keys from all databases.","title":"FLUSHALL","url":"https://redis.io/docs/latest/commands/flushall/"},{"id":"commands/flushdb","summary":"Remove all keys from the current database.","title":"FLUSHDB","url":"https://redis.io/docs/latest/commands/flushdb/"},{"id":"commands/ft._list","summary":"Returns a list of all existing indexes","title":"FT._LIST","url":"https://redis.io/docs/latest/commands/ft._list/"},{"id":"commands/ft.aggregate","summary":"Run a search query on an index and perform aggregate transformations on the results","title":"FT.AGGREGATE","url":"https://redis.io/docs/latest/commands/ft.aggregate/"},{"id":"commands/ft.aliasadd","summary":"Adds an alias to the index","title":"FT.ALIASADD","url":"https://redis.io/docs/latest/commands/ft.aliasadd/"},{"id":"commands/ft.aliasdel","summary":"Deletes an alias from the index","title":"FT.ALIASDEL","url":"https://redis.io/docs/latest/commands/ft.aliasdel/"},{"id":"commands/ft.aliaslist","summary":"Lists all aliases for the index","title":"FT.ALIASLIST","url":"https://redis.io/docs/latest/commands/ft.aliaslist/"},{"id":"commands/ft.aliasupdate","summary":"Adds or updates an alias to the index","title":"FT.ALIASUPDATE","url":"https://redis.io/docs/latest/commands/ft.aliasupdate/"},{"id":"commands/ft.alter","summary":"Adds a new field to the index","title":"FT.ALTER","url":"https://redis.io/docs/latest/commands/ft.alter/"},{"id":"commands/ft.config-get","summary":"Retrieves runtime configuration options","title":"FT.CONFIG GET","url":"https://redis.io/docs/latest/commands/ft.config-get/"},{"id":"commands/ft.config-help","summary":"Help description of runtime configuration options","title":"FT.CONFIG HELP","url":"https://redis.io/docs/latest/commands/ft.config-help/"},{"id":"commands/ft.config-set","summary":"Sets runtime configuration options","title":"FT.CONFIG SET","url":"https://redis.io/docs/latest/commands/ft.config-set/"},{"id":"commands/ft.create","summary":"Creates an index with the given spec","title":"FT.CREATE","url":"https://redis.io/docs/latest/commands/ft.create/"},{"id":"commands/ft.cursor-del","summary":"Deletes a cursor","title":"FT.CURSOR DEL","url":"https://redis.io/docs/latest/commands/ft.cursor-del/"},{"id":"commands/ft.cursor-read","summary":"Reads from a cursor","title":"FT.CURSOR READ","url":"https://redis.io/docs/latest/commands/ft.cursor-read/"},{"id":"commands/ft.dictadd","summary":"Adds terms to a dictionary","title":"FT.DICTADD","url":"https://redis.io/docs/latest/commands/ft.dictadd/"},{"id":"commands/ft.dictdel","summary":"Deletes terms from a dictionary","title":"FT.DICTDEL","url":"https://redis.io/docs/latest/commands/ft.dictdel/"},{"id":"commands/ft.dictdump","summary":"Dumps all terms in the given dictionary","title":"FT.DICTDUMP","url":"https://redis.io/docs/latest/commands/ft.dictdump/"},{"id":"commands/ft.dropindex","summary":"Deletes the index","title":"FT.DROPINDEX","url":"https://redis.io/docs/latest/commands/ft.dropindex/"},{"id":"commands/ft.explain","summary":"Returns the execution plan for a complex query","title":"FT.EXPLAIN","url":"https://redis.io/docs/latest/commands/ft.explain/"},{"id":"commands/ft.explaincli","summary":"Returns the execution plan for a complex query","title":"FT.EXPLAINCLI","url":"https://redis.io/docs/latest/commands/ft.explaincli/"},{"id":"commands/ft.hybrid","summary":"Performs hybrid search combining text search and vector similarity search","title":"FT.HYBRID","url":"https://redis.io/docs/latest/commands/ft.hybrid/"},{"id":"commands/ft.info","summary":"Returns information and statistics on the index","title":"FT.INFO","url":"https://redis.io/docs/latest/commands/ft.info/"},{"id":"commands/ft.profile","summary":"Performs a `FT.SEARCH` or `FT.AGGREGATE` command and collects performance information","title":"FT.PROFILE","url":"https://redis.io/docs/latest/commands/ft.profile/"},{"id":"commands/ft.search","summary":"Searches the index with a textual query, returning either documents or just ids","title":"FT.SEARCH","url":"https://redis.io/docs/latest/commands/ft.search/"},{"id":"commands/ft.spellcheck","summary":"Performs spelling correction on a query, returning suggestions for misspelled terms","title":"FT.SPELLCHECK","url":"https://redis.io/docs/latest/commands/ft.spellcheck/"},{"id":"commands/ft.sugadd","summary":"Adds a suggestion string to an auto-complete suggestion dictionary","title":"FT.SUGADD","url":"https://redis.io/docs/latest/commands/ft.sugadd/"},{"id":"commands/ft.sugdel","summary":"Deletes a string from a suggestion index","title":"FT.SUGDEL","url":"https://redis.io/docs/latest/commands/ft.sugdel/"},{"id":"commands/ft.sugget","summary":"Gets completion suggestions for a prefix","title":"FT.SUGGET","url":"https://redis.io/docs/latest/commands/ft.sugget/"},{"id":"commands/ft.suglen","summary":"Gets the size of an auto-complete suggestion dictionary","title":"FT.SUGLEN","url":"https://redis.io/docs/latest/commands/ft.suglen/"},{"id":"commands/ft.syndump","summary":"Dumps the contents of a synonym group","title":"FT.SYNDUMP","url":"https://redis.io/docs/latest/commands/ft.syndump/"},{"id":"commands/ft.synupdate","summary":"Creates or updates a synonym group with additional terms","title":"FT.SYNUPDATE","url":"https://redis.io/docs/latest/commands/ft.synupdate/"},{"id":"commands/ft.tagvals","summary":"Returns the distinct tags indexed in a Tag field","title":"FT.TAGVALS","url":"https://redis.io/docs/latest/commands/ft.tagvals/"},{"id":"commands/function","summary":"A container for function commands.","title":"FUNCTION","url":"https://redis.io/docs/latest/commands/function/"},{"id":"commands/function-delete","summary":"Deletes a library and its functions.","title":"FUNCTION DELETE","url":"https://redis.io/docs/latest/commands/function-delete/"},{"id":"commands/function-dump","summary":"Dumps all libraries into a serialized binary payload.","title":"FUNCTION DUMP","url":"https://redis.io/docs/latest/commands/function-dump/"},{"id":"commands/function-flush","summary":"Deletes all libraries and functions.","title":"FUNCTION FLUSH","url":"https://redis.io/docs/latest/commands/function-flush/"},{"id":"commands/function-help","summary":"Returns helpful text about the different subcommands.","title":"FUNCTION HELP","url":"https://redis.io/docs/latest/commands/function-help/"},{"id":"commands/function-kill","summary":"Terminates a function during execution.","title":"FUNCTION KILL","url":"https://redis.io/docs/latest/commands/function-kill/"},{"id":"commands/function-list","summary":"Returns information about all libraries.","title":"FUNCTION LIST","url":"https://redis.io/docs/latest/commands/function-list/"},{"id":"commands/function-load","summary":"Creates a library.","title":"FUNCTION LOAD","url":"https://redis.io/docs/latest/commands/function-load/"},{"id":"commands/function-restore","summary":"Restores all libraries from a payload.","title":"FUNCTION RESTORE","url":"https://redis.io/docs/latest/commands/function-restore/"},{"id":"commands/function-stats","summary":"Returns information about a function during execution.","title":"FUNCTION STATS","url":"https://redis.io/docs/latest/commands/function-stats/"},{"id":"commands/geoadd","summary":"Adds one or more members to a geospatial index. The key is created if it doesn't exist.","title":"GEOADD","url":"https://redis.io/docs/latest/commands/geoadd/"},{"id":"commands/geodist","summary":"Returns the distance between two members of a geospatial index.","title":"GEODIST","url":"https://redis.io/docs/latest/commands/geodist/"},{"id":"commands/geohash","summary":"Returns members from a geospatial index as geohash strings.","title":"GEOHASH","url":"https://redis.io/docs/latest/commands/geohash/"},{"id":"commands/geopos","summary":"Returns the longitude and latitude of members from a geospatial index.","title":"GEOPOS","url":"https://redis.io/docs/latest/commands/geopos/"},{"id":"commands/georadius","summary":"Queries a geospatial index for members within a distance from a coordinate, optionally stores the result.","title":"GEORADIUS","url":"https://redis.io/docs/latest/commands/georadius/"},{"id":"commands/georadius_ro","summary":"Returns members from a geospatial index that are within a distance from a coordinate.","title":"GEORADIUS_RO","url":"https://redis.io/docs/latest/commands/georadius_ro/"},{"id":"commands/georadiusbymember","summary":"Queries a geospatial index for members within a distance from a member, optionally stores the result.","title":"GEORADIUSBYMEMBER","url":"https://redis.io/docs/latest/commands/georadiusbymember/"},{"id":"commands/georadiusbymember_ro","summary":"Returns members from a geospatial index that are within a distance from a member.","title":"GEORADIUSBYMEMBER_RO","url":"https://redis.io/docs/latest/commands/georadiusbymember_ro/"},{"id":"commands/geosearch","summary":"Queries a geospatial index for members inside an area of a box or a circle.","title":"GEOSEARCH","url":"https://redis.io/docs/latest/commands/geosearch/"},{"id":"commands/geosearchstore","summary":"Queries a geospatial index for members inside an area of a box or a circle, optionally stores the result.","title":"GEOSEARCHSTORE","url":"https://redis.io/docs/latest/commands/geosearchstore/"},{"id":"commands/get","summary":"Returns the string value of a key.","title":"GET","url":"https://redis.io/docs/latest/commands/get/"},{"id":"commands/getbit","summary":"Returns a bit value by offset.","title":"GETBIT","url":"https://redis.io/docs/latest/commands/getbit/"},{"id":"commands/getdel","summary":"Returns the string value of a key after deleting the key.","title":"GETDEL","url":"https://redis.io/docs/latest/commands/getdel/"},{"id":"commands/getex","summary":"Returns the string value of a key after setting its expiration time.","title":"GETEX","url":"https://redis.io/docs/latest/commands/getex/"},{"id":"commands/getrange","summary":"Returns a substring of the string stored at a key.","title":"GETRANGE","url":"https://redis.io/docs/latest/commands/getrange/"},{"id":"commands/getset","summary":"Returns the previous string value of a key after setting it to a new value.","title":"GETSET","url":"https://redis.io/docs/latest/commands/getset/"},{"id":"commands/hdel","summary":"Deletes one or more fields and their values from a hash. Deletes the hash if no fields remain.","title":"HDEL","url":"https://redis.io/docs/latest/commands/hdel/"},{"id":"commands/hello","summary":"Handshakes with the Redis server.","title":"HELLO","url":"https://redis.io/docs/latest/commands/hello/"},{"id":"commands/hexists","summary":"Determines whether a field exists in a hash.","title":"HEXISTS","url":"https://redis.io/docs/latest/commands/hexists/"},{"id":"commands/hexpire","summary":"Set expiration for hash fields using relative time to expire (seconds)","title":"HEXPIRE","url":"https://redis.io/docs/latest/commands/hexpire/"},{"id":"commands/hexpireat","summary":"Set expiration for hash fields using an absolute Unix timestamp (seconds)","title":"HEXPIREAT","url":"https://redis.io/docs/latest/commands/hexpireat/"},{"id":"commands/hexpiretime","summary":"Returns the expiration time of a hash field as a Unix timestamp, in seconds.","title":"HEXPIRETIME","url":"https://redis.io/docs/latest/commands/hexpiretime/"},{"id":"commands/hget","summary":"Returns the value of a field in a hash.","title":"HGET","url":"https://redis.io/docs/latest/commands/hget/"},{"id":"commands/hgetall","summary":"Returns all fields and values in a hash.","title":"HGETALL","url":"https://redis.io/docs/latest/commands/hgetall/"},{"id":"commands/hgetdel","summary":"Returns the value of a field and deletes it from the hash.","title":"HGETDEL","url":"https://redis.io/docs/latest/commands/hgetdel/"},{"id":"commands/hgetex","summary":"Get the value of one or more fields of a given hash key, and optionally set their expiration.","title":"HGETEX","url":"https://redis.io/docs/latest/commands/hgetex/"},{"id":"commands/himport","summary":"A container for session-based hash import commands using fieldsets.","title":"HIMPORT","url":"https://redis.io/docs/latest/commands/himport/"},{"id":"commands/himport-discard","summary":"Removes a single session-local fieldset by name.","title":"HIMPORT DISCARD","url":"https://redis.io/docs/latest/commands/himport-discard/"},{"id":"commands/himport-discardall","summary":"Removes all session-local fieldsets for the connection.","title":"HIMPORT DISCARDALL","url":"https://redis.io/docs/latest/commands/himport-discardall/"},{"id":"commands/himport-prepare","summary":"Defines a session-local fieldset that maps a name to a sorted set of field names.","title":"HIMPORT PREPARE","url":"https://redis.io/docs/latest/commands/himport-prepare/"},{"id":"commands/himport-set","summary":"Creates a fieldset-based hash from values supplied in the order matching a previously prepared fieldset.","title":"HIMPORT SET","url":"https://redis.io/docs/latest/commands/himport-set/"},{"id":"commands/hincrby","summary":"Increments the integer value of a field in a hash by a number. Uses 0 as initial value if the field doesn't exist.","title":"HINCRBY","url":"https://redis.io/docs/latest/commands/hincrby/"},{"id":"commands/hincrbyfloat","summary":"Increments the floating point value of a field by a number. Uses 0 as initial value if the field doesn't exist.","title":"HINCRBYFLOAT","url":"https://redis.io/docs/latest/commands/hincrbyfloat/"},{"id":"commands/hkeys","summary":"Returns all fields in a hash.","title":"HKEYS","url":"https://redis.io/docs/latest/commands/hkeys/"},{"id":"commands/hlen","summary":"Returns the number of fields in a hash.","title":"HLEN","url":"https://redis.io/docs/latest/commands/hlen/"},{"id":"commands/hmget","summary":"Returns the values of all fields in a hash.","title":"HMGET","url":"https://redis.io/docs/latest/commands/hmget/"},{"id":"commands/hmset","summary":"Sets the values of multiple fields.","title":"HMSET","url":"https://redis.io/docs/latest/commands/hmset/"},{"id":"commands/hotkeys","summary":"A container for hotkeys tracking commands.","title":"HOTKEYS","url":"https://redis.io/docs/latest/commands/hotkeys/"},{"id":"commands/hotkeys-get","summary":"Returns lists of top K hotkeys depending on metrics chosen in HOTKEYS START command.","title":"HOTKEYS GET","url":"https://redis.io/docs/latest/commands/hotkeys-get/"},{"id":"commands/hotkeys-help","summary":"Returns helpful text about HOTKEYS commands and parameters.","title":"HOTKEYS HELP","url":"https://redis.io/docs/latest/commands/hotkeys-help/"},{"id":"commands/hotkeys-reset","summary":"Release the resources used for hotkey tracking.","title":"HOTKEYS RESET","url":"https://redis.io/docs/latest/commands/hotkeys-reset/"},{"id":"commands/hotkeys-start","summary":"Starts hotkeys tracking.","title":"HOTKEYS START","url":"https://redis.io/docs/latest/commands/hotkeys-start/"},{"id":"commands/hotkeys-stop","summary":"Stops hotkeys tracking.","title":"HOTKEYS STOP","url":"https://redis.io/docs/latest/commands/hotkeys-stop/"},{"id":"commands/hpersist","summary":"Removes the expiration time for each specified field","title":"HPERSIST","url":"https://redis.io/docs/latest/commands/hpersist/"},{"id":"commands/hpexpire","summary":"Set expiration for hash fields using relative time to expire (milliseconds)","title":"HPEXPIRE","url":"https://redis.io/docs/latest/commands/hpexpire/"},{"id":"commands/hpexpireat","summary":"Set expiration for hash fields using an absolute Unix timestamp (milliseconds)","title":"HPEXPIREAT","url":"https://redis.io/docs/latest/commands/hpexpireat/"},{"id":"commands/hpexpiretime","summary":"Returns the expiration time of a hash field as a Unix timestamp, in msec.","title":"HPEXPIRETIME","url":"https://redis.io/docs/latest/commands/hpexpiretime/"},{"id":"commands/hpttl","summary":"Returns the TTL in milliseconds of a hash field.","title":"HPTTL","url":"https://redis.io/docs/latest/commands/hpttl/"},{"id":"commands/hrandfield","summary":"Returns one or more random fields from a hash.","title":"HRANDFIELD","url":"https://redis.io/docs/latest/commands/hrandfield/"},{"id":"commands/hscan","summary":"Iterates over fields and values of a hash.","title":"HSCAN","url":"https://redis.io/docs/latest/commands/hscan/"},{"id":"commands/hset","summary":"Creates or modifies the value of a field in a hash.","title":"HSET","url":"https://redis.io/docs/latest/commands/hset/"},{"id":"commands/hsetex","summary":"Set the value of one or more fields of a given hash key, and optionally set their expiration.","title":"HSETEX","url":"https://redis.io/docs/latest/commands/hsetex/"},{"id":"commands/hsetnx","summary":"Sets the value of a field in a hash only when the field doesn't exist.","title":"HSETNX","url":"https://redis.io/docs/latest/commands/hsetnx/"},{"id":"commands/hstrlen","summary":"Returns the length of the value of a field.","title":"HSTRLEN","url":"https://redis.io/docs/latest/commands/hstrlen/"},{"id":"commands/httl","summary":"Returns the TTL in seconds of a hash field.","title":"HTTL","url":"https://redis.io/docs/latest/commands/httl/"},{"id":"commands/hvals","summary":"Returns all values in a hash.","title":"HVALS","url":"https://redis.io/docs/latest/commands/hvals/"},{"id":"commands/incr","summary":"Increments the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.","title":"INCR","url":"https://redis.io/docs/latest/commands/incr/"},{"id":"commands/incrby","summary":"Increments the integer value of a key by a number. Uses 0 as initial value if the key doesn't exist.","title":"INCRBY","url":"https://redis.io/docs/latest/commands/incrby/"},{"id":"commands/incrbyfloat","summary":"Increment the floating point value of a key by a number. Uses 0 as initial value if the key doesn't exist.","title":"INCRBYFLOAT","url":"https://redis.io/docs/latest/commands/incrbyfloat/"},{"id":"commands/increx","summary":"Increments the numeric value of a key by a number and sets its expiration time. Uses 0 as initial value if the key doesn't exist.","title":"INCREX","url":"https://redis.io/docs/latest/commands/increx/"},{"id":"commands/info","summary":"Returns information and statistics about the server.","title":"INFO","url":"https://redis.io/docs/latest/commands/info/"},{"id":"commands/json.arrappend","summary":"Append one or more JSON values into the array at path after the last element in it.","title":"JSON.ARRAPPEND","url":"https://redis.io/docs/latest/commands/json.arrappend/"},{"id":"commands/json.arrindex","summary":"Returns the index of the first occurrence of a JSON scalar value in the array at path","title":"JSON.ARRINDEX","url":"https://redis.io/docs/latest/commands/json.arrindex/"},{"id":"commands/json.arrinsert","summary":"Inserts the JSON scalar(s) value at the specified index in the array at path","title":"JSON.ARRINSERT","url":"https://redis.io/docs/latest/commands/json.arrinsert/"},{"id":"commands/json.arrlen","summary":"Returns the length of the array at path","title":"JSON.ARRLEN","url":"https://redis.io/docs/latest/commands/json.arrlen/"},{"id":"commands/json.arrpop","summary":"Removes and returns the element at the specified index in the array at path","title":"JSON.ARRPOP","url":"https://redis.io/docs/latest/commands/json.arrpop/"},{"id":"commands/json.arrtrim","summary":"Trims the array at path to contain only the specified inclusive range of indices from start to stop","title":"JSON.ARRTRIM","url":"https://redis.io/docs/latest/commands/json.arrtrim/"},{"id":"commands/json.clear","summary":"Clears all values from an array or an object and sets numeric values to `0`","title":"JSON.CLEAR","url":"https://redis.io/docs/latest/commands/json.clear/"},{"id":"commands/json.debug","summary":"Debugging container command","title":"JSON.DEBUG","url":"https://redis.io/docs/latest/commands/json.debug/"},{"id":"commands/json.debug-help","summary":"Shows helpful information","title":"JSON.DEBUG HELP","url":"https://redis.io/docs/latest/commands/json.debug-help/"},{"id":"commands/json.debug-memory","summary":"Reports the size in bytes of a key","title":"JSON.DEBUG MEMORY","url":"https://redis.io/docs/latest/commands/json.debug-memory/"},{"id":"commands/json.del","summary":"Deletes a value","title":"JSON.DEL","url":"https://redis.io/docs/latest/commands/json.del/"},{"id":"commands/json.forget","summary":"Deletes a value","title":"JSON.FORGET","url":"https://redis.io/docs/latest/commands/json.forget/"},{"id":"commands/json.get","summary":"Gets the value at one or more paths in JSON serialized form","title":"JSON.GET","url":"https://redis.io/docs/latest/commands/json.get/"},{"id":"commands/json.merge","summary":"Merges a given JSON value into matching paths. Consequently, JSON values at matching paths are updated, deleted, or expanded with new children","title":"JSON.MERGE","url":"https://redis.io/docs/latest/commands/json.merge/"},{"id":"commands/json.mget","summary":"Returns the values at a path from one or more keys","title":"JSON.MGET","url":"https://redis.io/docs/latest/commands/json.mget/"},{"id":"commands/json.mset","summary":"Sets or updates the JSON value of one or more keys","title":"JSON.MSET","url":"https://redis.io/docs/latest/commands/json.mset/"},{"id":"commands/json.numincrby","summary":"Increments the numeric value at path by a value","title":"JSON.NUMINCRBY","url":"https://redis.io/docs/latest/commands/json.numincrby/"},{"id":"commands/json.nummultby","summary":"Multiplies the numeric value at path by a value","title":"JSON.NUMMULTBY","url":"https://redis.io/docs/latest/commands/json.nummultby/"},{"id":"commands/json.numpowby","summary":"Raises the numeric value at path to the power of a value","title":"JSON.NUMPOWBY","url":"https://redis.io/docs/latest/commands/json.numpowby/"},{"id":"commands/json.objkeys","summary":"Returns the key names of JSON objects at the paths matching a given path expression","title":"JSON.OBJKEYS","url":"https://redis.io/docs/latest/commands/json.objkeys/"},{"id":"commands/json.objlen","summary":"Returns the number of keys in JSON objects at the paths matching a given path expression","title":"JSON.OBJLEN","url":"https://redis.io/docs/latest/commands/json.objlen/"},{"id":"commands/json.resp","summary":"Returns the JSON value at path in Redis Serialization Protocol (RESP)","title":"JSON.RESP","url":"https://redis.io/docs/latest/commands/json.resp/"},{"id":"commands/json.set","summary":"Sets or updates the JSON value at a path","title":"JSON.SET","url":"https://redis.io/docs/latest/commands/json.set/"},{"id":"commands/json.strappend","summary":"Appends a string to JSON strings at the paths matching a given path expression","title":"JSON.STRAPPEND","url":"https://redis.io/docs/latest/commands/json.strappend/"},{"id":"commands/json.strlen","summary":"Returns the length of JSON strings at the paths matching a given path expression","title":"JSON.STRLEN","url":"https://redis.io/docs/latest/commands/json.strlen/"},{"id":"commands/json.toggle","summary":"Toggles a boolean value","title":"JSON.TOGGLE","url":"https://redis.io/docs/latest/commands/json.toggle/"},{"id":"commands/json.type","summary":"Returns the type of the JSON value at path","title":"JSON.TYPE","url":"https://redis.io/docs/latest/commands/json.type/"},{"id":"commands/keys","summary":"Returns all key names that match a pattern.","title":"KEYS","url":"https://redis.io/docs/latest/commands/keys/"},{"id":"commands/lastsave","summary":"Returns the Unix timestamp of the last successful save to disk.","title":"LASTSAVE","url":"https://redis.io/docs/latest/commands/lastsave/"},{"id":"commands/latency","summary":"A container for latency diagnostics commands.","title":"LATENCY","url":"https://redis.io/docs/latest/commands/latency/"},{"id":"commands/latency-doctor","summary":"Returns a human-readable latency analysis report.","title":"LATENCY DOCTOR","url":"https://redis.io/docs/latest/commands/latency-doctor/"},{"id":"commands/latency-graph","summary":"Returns a latency graph for an event.","title":"LATENCY GRAPH","url":"https://redis.io/docs/latest/commands/latency-graph/"},{"id":"commands/latency-help","summary":"Returns helpful text about the different subcommands.","title":"LATENCY HELP","url":"https://redis.io/docs/latest/commands/latency-help/"},{"id":"commands/latency-histogram","summary":"Returns the cumulative distribution of latencies of a subset or all commands.","title":"LATENCY HISTOGRAM","url":"https://redis.io/docs/latest/commands/latency-histogram/"},{"id":"commands/latency-history","summary":"Returns timestamp-latency samples for an event.","title":"LATENCY HISTORY","url":"https://redis.io/docs/latest/commands/latency-history/"},{"id":"commands/latency-latest","summary":"Returns the latest latency samples for all events.","title":"LATENCY LATEST","url":"https://redis.io/docs/latest/commands/latency-latest/"},{"id":"commands/latency-reset","summary":"Resets the latency data for one or more events.","title":"LATENCY RESET","url":"https://redis.io/docs/latest/commands/latency-reset/"},{"id":"commands/lcs","summary":"Finds the longest common substring.","title":"LCS","url":"https://redis.io/docs/latest/commands/lcs/"},{"id":"commands/lindex","summary":"Returns an element from a list by its index.","title":"LINDEX","url":"https://redis.io/docs/latest/commands/lindex/"},{"id":"commands/linsert","summary":"Inserts an element before or after another element in a list.","title":"LINSERT","url":"https://redis.io/docs/latest/commands/linsert/"},{"id":"commands/llen","summary":"Returns the length of a list.","title":"LLEN","url":"https://redis.io/docs/latest/commands/llen/"},{"id":"commands/lmove","summary":"Returns an element after popping it from one list and pushing it to another. Deletes the list if the last element was moved.","title":"LMOVE","url":"https://redis.io/docs/latest/commands/lmove/"},{"id":"commands/lmovem","summary":"Moves up to (or exactly) a number of elements from one list to another and returns them. Deletes the source list if it becomes empty.","title":"LMOVEM","url":"https://redis.io/docs/latest/commands/lmovem/"},{"id":"commands/lmpop","summary":"Returns multiple elements from a list after removing them. Deletes the list if the last element was popped.","title":"LMPOP","url":"https://redis.io/docs/latest/commands/lmpop/"},{"id":"commands/lolwut","summary":"Displays computer art and the Redis version","title":"LOLWUT","url":"https://redis.io/docs/latest/commands/lolwut/"},{"id":"commands/lpop","summary":"Returns the first elements in a list after removing it. Deletes the list if the last element was popped.","title":"LPOP","url":"https://redis.io/docs/latest/commands/lpop/"},{"id":"commands/lpos","summary":"Returns the index of matching elements in a list.","title":"LPOS","url":"https://redis.io/docs/latest/commands/lpos/"},{"id":"commands/lpush","summary":"Prepends one or more elements to a list. Creates the key if it doesn't exist.","title":"LPUSH","url":"https://redis.io/docs/latest/commands/lpush/"},{"id":"commands/lpushx","summary":"Prepends one or more elements to a list only when the list exists.","title":"LPUSHX","url":"https://redis.io/docs/latest/commands/lpushx/"},{"id":"commands/lrange","summary":"Returns a range of elements from a list.","title":"LRANGE","url":"https://redis.io/docs/latest/commands/lrange/"},{"id":"commands/lrem","summary":"Removes elements from a list. Deletes the list if the last element was removed.","title":"LREM","url":"https://redis.io/docs/latest/commands/lrem/"},{"id":"commands/lset","summary":"Sets the value of an element in a list by its index.","title":"LSET","url":"https://redis.io/docs/latest/commands/lset/"},{"id":"commands/ltrim","summary":"Removes elements from both ends a list. Deletes the list if all elements were trimmed.","title":"LTRIM","url":"https://redis.io/docs/latest/commands/ltrim/"},{"id":"commands/memory","summary":"A container for memory diagnostics commands.","title":"MEMORY","url":"https://redis.io/docs/latest/commands/memory/"},{"id":"commands/memory-doctor","summary":"Outputs a memory problems report.","title":"MEMORY DOCTOR","url":"https://redis.io/docs/latest/commands/memory-doctor/"},{"id":"commands/memory-help","summary":"Returns helpful text about the different subcommands.","title":"MEMORY HELP","url":"https://redis.io/docs/latest/commands/memory-help/"},{"id":"commands/memory-malloc-stats","summary":"Returns the allocator statistics.","title":"MEMORY MALLOC-STATS","url":"https://redis.io/docs/latest/commands/memory-malloc-stats/"},{"id":"commands/memory-purge","summary":"Asks the allocator to release memory.","title":"MEMORY PURGE","url":"https://redis.io/docs/latest/commands/memory-purge/"},{"id":"commands/memory-stats","summary":"Returns details about memory usage.","title":"MEMORY STATS","url":"https://redis.io/docs/latest/commands/memory-stats/"},{"id":"commands/memory-usage","summary":"Estimates the memory usage of a key.","title":"MEMORY USAGE","url":"https://redis.io/docs/latest/commands/memory-usage/"},{"id":"commands/mget","summary":"Atomically returns the string values of one or more keys.","title":"MGET","url":"https://redis.io/docs/latest/commands/mget/"},{"id":"commands/migrate","summary":"Atomically transfers a key from one Redis instance to another.","title":"MIGRATE","url":"https://redis.io/docs/latest/commands/migrate/"},{"id":"commands/module","summary":"A container for module commands.","title":"MODULE","url":"https://redis.io/docs/latest/commands/module/"},{"id":"commands/module-help","summary":"Returns helpful text about the different subcommands.","title":"MODULE HELP","url":"https://redis.io/docs/latest/commands/module-help/"},{"id":"commands/module-list","summary":"Returns all loaded modules.","title":"MODULE LIST","url":"https://redis.io/docs/latest/commands/module-list/"},{"id":"commands/module-load","summary":"Loads a module.","title":"MODULE LOAD","url":"https://redis.io/docs/latest/commands/module-load/"},{"id":"commands/module-loadex","summary":"Loads a module using extended parameters.","title":"MODULE LOADEX","url":"https://redis.io/docs/latest/commands/module-loadex/"},{"id":"commands/module-unload","summary":"Unloads a module.","title":"MODULE UNLOAD","url":"https://redis.io/docs/latest/commands/module-unload/"},{"id":"commands/monitor","summary":"Listens for all requests received by the server in real-time.","title":"MONITOR","url":"https://redis.io/docs/latest/commands/monitor/"},{"id":"commands/move","summary":"Moves a key to another database.","title":"MOVE","url":"https://redis.io/docs/latest/commands/move/"},{"id":"commands/mset","summary":"Atomically creates or modifies the string values of one or more keys.","title":"MSET","url":"https://redis.io/docs/latest/commands/mset/"},{"id":"commands/msetex","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.","title":"MSETEX","url":"https://redis.io/docs/latest/commands/msetex/"},{"id":"commands/msetnx","summary":"Atomically modifies the string values of one or more keys only when all keys don't exist.","title":"MSETNX","url":"https://redis.io/docs/latest/commands/msetnx/"},{"id":"commands/multi","summary":"Starts a transaction.","title":"MULTI","url":"https://redis.io/docs/latest/commands/multi/"},{"id":"commands/object","summary":"A container for object introspection commands.","title":"OBJECT","url":"https://redis.io/docs/latest/commands/object/"},{"id":"commands/object-encoding","summary":"Returns the internal encoding of a Redis object.","title":"OBJECT ENCODING","url":"https://redis.io/docs/latest/commands/object-encoding/"},{"id":"commands/object-freq","summary":"Returns the logarithmic access frequency counter of a Redis object.","title":"OBJECT FREQ","url":"https://redis.io/docs/latest/commands/object-freq/"},{"id":"commands/object-help","summary":"Returns helpful text about the different subcommands.","title":"OBJECT HELP","url":"https://redis.io/docs/latest/commands/object-help/"},{"id":"commands/object-idletime","summary":"Returns the time since the last access to a Redis object.","title":"OBJECT IDLETIME","url":"https://redis.io/docs/latest/commands/object-idletime/"},{"id":"commands/object-refcount","summary":"Returns the reference count of a value of a key.","title":"OBJECT REFCOUNT","url":"https://redis.io/docs/latest/commands/object-refcount/"},{"id":"commands/persist","summary":"Removes the expiration time of a key.","title":"PERSIST","url":"https://redis.io/docs/latest/commands/persist/"},{"id":"commands/pexpire","summary":"Sets the expiration time of a key in milliseconds.","title":"PEXPIRE","url":"https://redis.io/docs/latest/commands/pexpire/"},{"id":"commands/pexpireat","summary":"Sets the expiration time of a key to a Unix milliseconds timestamp.","title":"PEXPIREAT","url":"https://redis.io/docs/latest/commands/pexpireat/"},{"id":"commands/pexpiretime","summary":"Returns the expiration time of a key as a Unix milliseconds timestamp.","title":"PEXPIRETIME","url":"https://redis.io/docs/latest/commands/pexpiretime/"},{"id":"commands/pfadd","summary":"Adds elements to a HyperLogLog key. Creates the key if it doesn't exist.","title":"PFADD","url":"https://redis.io/docs/latest/commands/pfadd/"},{"id":"commands/pfcount","summary":"Returns the approximated cardinality of the set(s) observed by the HyperLogLog key(s).","title":"PFCOUNT","url":"https://redis.io/docs/latest/commands/pfcount/"},{"id":"commands/pfdebug","summary":"Internal commands for debugging HyperLogLog values.","title":"PFDEBUG","url":"https://redis.io/docs/latest/commands/pfdebug/"},{"id":"commands/pfmerge","summary":"Merges one or more HyperLogLog values into a single key.","title":"PFMERGE","url":"https://redis.io/docs/latest/commands/pfmerge/"},{"id":"commands/pfselftest","summary":"An internal command for testing HyperLogLog values.","title":"PFSELFTEST","url":"https://redis.io/docs/latest/commands/pfselftest/"},{"id":"commands/ping","summary":"Returns the server's liveliness response.","title":"PING","url":"https://redis.io/docs/latest/commands/ping/"},{"id":"commands/psetex","summary":"Sets both string value and expiration time in milliseconds of a key. The key is created if it doesn't exist.","title":"PSETEX","url":"https://redis.io/docs/latest/commands/psetex/"},{"id":"commands/psubscribe","summary":"Listens for messages published to channels that match one or more patterns.","title":"PSUBSCRIBE","url":"https://redis.io/docs/latest/commands/psubscribe/"},{"id":"commands/psync","summary":"An internal command used in replication.","title":"PSYNC","url":"https://redis.io/docs/latest/commands/psync/"},{"id":"commands/pttl","summary":"Returns the expiration time in milliseconds of a key.","title":"PTTL","url":"https://redis.io/docs/latest/commands/pttl/"},{"id":"commands/publish","summary":"Posts a message to a channel.","title":"PUBLISH","url":"https://redis.io/docs/latest/commands/publish/"},{"id":"commands/pubsub","summary":"A container for Pub/Sub commands.","title":"PUBSUB","url":"https://redis.io/docs/latest/commands/pubsub/"},{"id":"commands/pubsub-channels","summary":"Returns the active channels.","title":"PUBSUB CHANNELS","url":"https://redis.io/docs/latest/commands/pubsub-channels/"},{"id":"commands/pubsub-help","summary":"Returns helpful text about the different subcommands.","title":"PUBSUB HELP","url":"https://redis.io/docs/latest/commands/pubsub-help/"},{"id":"commands/pubsub-numpat","summary":"Returns a count of unique pattern subscriptions.","title":"PUBSUB NUMPAT","url":"https://redis.io/docs/latest/commands/pubsub-numpat/"},{"id":"commands/pubsub-numsub","summary":"Returns a count of subscribers to channels.","title":"PUBSUB NUMSUB","url":"https://redis.io/docs/latest/commands/pubsub-numsub/"},{"id":"commands/pubsub-shardchannels","summary":"Returns the active shard channels.","title":"PUBSUB SHARDCHANNELS","url":"https://redis.io/docs/latest/commands/pubsub-shardchannels/"},{"id":"commands/pubsub-shardnumsub","summary":"Returns the count of subscribers of shard channels.","title":"PUBSUB SHARDNUMSUB","url":"https://redis.io/docs/latest/commands/pubsub-shardnumsub/"},{"id":"commands/punsubscribe","summary":"Stops listening to messages published to channels that match one or more patterns.","title":"PUNSUBSCRIBE","url":"https://redis.io/docs/latest/commands/punsubscribe/"},{"id":"commands/quit","summary":"Closes the connection.","title":"QUIT","url":"https://redis.io/docs/latest/commands/quit/"},{"id":"commands/randomkey","summary":"Returns a random key name from the database.","title":"RANDOMKEY","url":"https://redis.io/docs/latest/commands/randomkey/"},{"id":"commands/readonly","summary":"Enables read-only queries for a connection to a Redis Cluster replica node.","title":"READONLY","url":"https://redis.io/docs/latest/commands/readonly/"},{"id":"commands/readwrite","summary":"Enables read-write queries for a connection to a Redis Cluster replica node.","title":"READWRITE","url":"https://redis.io/docs/latest/commands/readwrite/"},{"id":"commands/rename","summary":"Renames a key and overwrites the destination.","title":"RENAME","url":"https://redis.io/docs/latest/commands/rename/"},{"id":"commands/renamenx","summary":"Renames a key only when the target key name doesn't exist.","title":"RENAMENX","url":"https://redis.io/docs/latest/commands/renamenx/"},{"id":"commands/replconf","summary":"An internal command for configuring the replication stream.","title":"REPLCONF","url":"https://redis.io/docs/latest/commands/replconf/"},{"id":"commands/replicaof","summary":"Configures a server as replica of another, or promotes it to a master.","title":"REPLICAOF","url":"https://redis.io/docs/latest/commands/replicaof/"},{"id":"commands/reset","summary":"Resets the connection.","title":"RESET","url":"https://redis.io/docs/latest/commands/reset/"},{"id":"commands/restore","summary":"Creates a key from the serialized representation of a value.","title":"RESTORE","url":"https://redis.io/docs/latest/commands/restore/"},{"id":"commands/restore-asking","summary":"An internal command for migrating keys in a cluster.","title":"RESTORE-ASKING","url":"https://redis.io/docs/latest/commands/restore-asking/"},{"id":"commands/role","summary":"Returns the replication role.","title":"ROLE","url":"https://redis.io/docs/latest/commands/role/"},{"id":"commands/rpop","summary":"Returns and removes the last elements of a list. Deletes the list if the last element was popped.","title":"RPOP","url":"https://redis.io/docs/latest/commands/rpop/"},{"id":"commands/rpoplpush","summary":"Returns the last element of a list after removing and pushing it to another list. Deletes the list if the last element was popped.","title":"RPOPLPUSH","url":"https://redis.io/docs/latest/commands/rpoplpush/"},{"id":"commands/rpush","summary":"Appends one or more elements to a list. Creates the key if it doesn't exist.","title":"RPUSH","url":"https://redis.io/docs/latest/commands/rpush/"},{"id":"commands/rpushx","summary":"Appends an element to a list only when the list exists.","title":"RPUSHX","url":"https://redis.io/docs/latest/commands/rpushx/"},{"id":"commands/sadd","summary":"Adds one or more members to a set. Creates the key if it doesn't exist.","title":"SADD","url":"https://redis.io/docs/latest/commands/sadd/"},{"id":"commands/save","summary":"Synchronously saves the database(s) to disk.","title":"SAVE","url":"https://redis.io/docs/latest/commands/save/"},{"id":"commands/scan","summary":"Iterates over the key names in the database.","title":"SCAN","url":"https://redis.io/docs/latest/commands/scan/"},{"id":"commands/scard","summary":"Returns the number of members in a set.","title":"SCARD","url":"https://redis.io/docs/latest/commands/scard/"},{"id":"commands/script","summary":"A container for Lua scripts management commands.","title":"SCRIPT","url":"https://redis.io/docs/latest/commands/script/"},{"id":"commands/script-debug","summary":"Sets the debug mode of server-side Lua scripts.","title":"SCRIPT DEBUG","url":"https://redis.io/docs/latest/commands/script-debug/"},{"id":"commands/script-exists","summary":"Determines whether server-side Lua scripts exist in the script cache.","title":"SCRIPT EXISTS","url":"https://redis.io/docs/latest/commands/script-exists/"},{"id":"commands/script-flush","summary":"Removes all server-side Lua scripts from the script cache.","title":"SCRIPT FLUSH","url":"https://redis.io/docs/latest/commands/script-flush/"},{"id":"commands/script-help","summary":"Returns helpful text about the different subcommands.","title":"SCRIPT HELP","url":"https://redis.io/docs/latest/commands/script-help/"},{"id":"commands/script-kill","summary":"Terminates a server-side Lua script during execution.","title":"SCRIPT KILL","url":"https://redis.io/docs/latest/commands/script-kill/"},{"id":"commands/script-load","summary":"Loads a server-side Lua script to the script cache.","title":"SCRIPT LOAD","url":"https://redis.io/docs/latest/commands/script-load/"},{"id":"commands/sdiff","summary":"Returns the difference of multiple sets.","title":"SDIFF","url":"https://redis.io/docs/latest/commands/sdiff/"},{"id":"commands/sdiffcard","summary":"Returns the number of members of the difference between the first set and all successive sets.","title":"SDIFFCARD","url":"https://redis.io/docs/latest/commands/sdiffcard/"},{"id":"commands/sdiffstore","summary":"Stores the difference of multiple sets in a key.","title":"SDIFFSTORE","url":"https://redis.io/docs/latest/commands/sdiffstore/"},{"id":"commands/select","summary":"Changes the selected database.","title":"SELECT","url":"https://redis.io/docs/latest/commands/select/"},{"id":"commands/set","summary":"Sets the string value of a key, ignoring its type. The key is created if it doesn't exist.","title":"SET","url":"https://redis.io/docs/latest/commands/set/"},{"id":"commands/setbit","summary":"Sets or clears the bit at offset of the string value. Creates the key if it doesn't exist.","title":"SETBIT","url":"https://redis.io/docs/latest/commands/setbit/"},{"id":"commands/setex","summary":"Sets the string value and expiration time of a key. Creates the key if it doesn't exist.","title":"SETEX","url":"https://redis.io/docs/latest/commands/setex/"},{"id":"commands/setnx","summary":"Set the string value of a key only when the key doesn't exist.","title":"SETNX","url":"https://redis.io/docs/latest/commands/setnx/"},{"id":"commands/setrange","summary":"Overwrites a part of a string value with another by an offset. Creates the key if it doesn't exist.","title":"SETRANGE","url":"https://redis.io/docs/latest/commands/setrange/"},{"id":"commands/shutdown","summary":"Synchronously saves the database(s) to disk and shuts down the Redis server.","title":"SHUTDOWN","url":"https://redis.io/docs/latest/commands/shutdown/"},{"id":"commands/sinter","summary":"Returns the intersect of multiple sets.","title":"SINTER","url":"https://redis.io/docs/latest/commands/sinter/"},{"id":"commands/sintercard","summary":"Returns the number of members of the intersect of multiple sets.","title":"SINTERCARD","url":"https://redis.io/docs/latest/commands/sintercard/"},{"id":"commands/sinterstore","summary":"Stores the intersect of multiple sets in a key.","title":"SINTERSTORE","url":"https://redis.io/docs/latest/commands/sinterstore/"},{"id":"commands/sismember","summary":"Determines whether a member belongs to a set.","title":"SISMEMBER","url":"https://redis.io/docs/latest/commands/sismember/"},{"id":"commands/slaveof","summary":"Sets a Redis server as a replica of another, or promotes it to being a master.","title":"SLAVEOF","url":"https://redis.io/docs/latest/commands/slaveof/"},{"id":"commands/slowlog","summary":"A container for slow log commands.","title":"SLOWLOG","url":"https://redis.io/docs/latest/commands/slowlog/"},{"id":"commands/slowlog-get","summary":"Returns the slow log's entries.","title":"SLOWLOG GET","url":"https://redis.io/docs/latest/commands/slowlog-get/"},{"id":"commands/slowlog-help","summary":"Show helpful text about the different subcommands","title":"SLOWLOG HELP","url":"https://redis.io/docs/latest/commands/slowlog-help/"},{"id":"commands/slowlog-len","summary":"Returns the number of entries in the slow log.","title":"SLOWLOG LEN","url":"https://redis.io/docs/latest/commands/slowlog-len/"},{"id":"commands/slowlog-reset","summary":"Clears all entries from the slow log.","title":"SLOWLOG RESET","url":"https://redis.io/docs/latest/commands/slowlog-reset/"},{"id":"commands/smembers","summary":"Returns all members of a set.","title":"SMEMBERS","url":"https://redis.io/docs/latest/commands/smembers/"},{"id":"commands/smismember","summary":"Determines whether multiple members belong to a set.","title":"SMISMEMBER","url":"https://redis.io/docs/latest/commands/smismember/"},{"id":"commands/smove","summary":"Moves a member from one set to another.","title":"SMOVE","url":"https://redis.io/docs/latest/commands/smove/"},{"id":"commands/sort","summary":"Sorts the elements in a list, a set, or a sorted set, optionally storing the result.","title":"SORT","url":"https://redis.io/docs/latest/commands/sort/"},{"id":"commands/sort_ro","summary":"Returns the sorted elements of a list, a set, or a sorted set.","title":"SORT_RO","url":"https://redis.io/docs/latest/commands/sort_ro/"},{"id":"commands/spop","summary":"Returns one or more random members from a set after removing them. Deletes the set if the last member was popped.","title":"SPOP","url":"https://redis.io/docs/latest/commands/spop/"},{"id":"commands/spublish","summary":"Post a message to a shard channel","title":"SPUBLISH","url":"https://redis.io/docs/latest/commands/spublish/"},{"id":"commands/srandmember","summary":"Get one or multiple random members from a set","title":"SRANDMEMBER","url":"https://redis.io/docs/latest/commands/srandmember/"},{"id":"commands/srem","summary":"Removes one or more members from a set. Deletes the set if the last member was removed.","title":"SREM","url":"https://redis.io/docs/latest/commands/srem/"},{"id":"commands/sscan","summary":"Iterates over members of a set.","title":"SSCAN","url":"https://redis.io/docs/latest/commands/sscan/"},{"id":"commands/ssubscribe","summary":"Listens for messages published to shard channels.","title":"SSUBSCRIBE","url":"https://redis.io/docs/latest/commands/ssubscribe/"},{"id":"commands/strlen","summary":"Returns the length of a string value.","title":"STRLEN","url":"https://redis.io/docs/latest/commands/strlen/"},{"id":"commands/subscribe","summary":"Listens for messages published to channels.","title":"SUBSCRIBE","url":"https://redis.io/docs/latest/commands/subscribe/"},{"id":"commands/substr","summary":"Returns a substring from a string value.","title":"SUBSTR","url":"https://redis.io/docs/latest/commands/substr/"},{"id":"commands/sunion","summary":"Returns the union of multiple sets.","title":"SUNION","url":"https://redis.io/docs/latest/commands/sunion/"},{"id":"commands/sunioncard","summary":"Returns the number of members of the union of multiple sets.","title":"SUNIONCARD","url":"https://redis.io/docs/latest/commands/sunioncard/"},{"id":"commands/sunionstore","summary":"Stores the union of multiple sets in a key.","title":"SUNIONSTORE","url":"https://redis.io/docs/latest/commands/sunionstore/"},{"id":"commands/sunsubscribe","summary":"Stops listening to messages posted to shard channels.","title":"SUNSUBSCRIBE","url":"https://redis.io/docs/latest/commands/sunsubscribe/"},{"id":"commands/swapdb","summary":"Swaps two Redis databases.","title":"SWAPDB","url":"https://redis.io/docs/latest/commands/swapdb/"},{"id":"commands/sync","summary":"An internal command used in replication.","title":"SYNC","url":"https://redis.io/docs/latest/commands/sync/"},{"id":"commands/tdigest.add","summary":"Adds one or more observations to a t-digest sketch","title":"TDIGEST.ADD","url":"https://redis.io/docs/latest/commands/tdigest.add/"},{"id":"commands/tdigest.byrank","summary":"Returns, for each input rank, a floating-point estimation of the value with that rank","title":"TDIGEST.BYRANK","url":"https://redis.io/docs/latest/commands/tdigest.byrank/"},{"id":"commands/tdigest.byrevrank","summary":"Returns, for each input reverse rank, an estimation of the floating-point value with that reverse rank","title":"TDIGEST.BYREVRANK","url":"https://redis.io/docs/latest/commands/tdigest.byrevrank/"},{"id":"commands/tdigest.cdf","summary":"Returns, for each input value, an estimation of the floating-point fraction of (observations smaller than the given value + half the observations equal to the given value)","title":"TDIGEST.CDF","url":"https://redis.io/docs/latest/commands/tdigest.cdf/"},{"id":"commands/tdigest.create","summary":"Allocates memory and initializes a new t-digest sketch","title":"TDIGEST.CREATE","url":"https://redis.io/docs/latest/commands/tdigest.create/"},{"id":"commands/tdigest.info","summary":"Returns information and statistics about a t-digest sketch","title":"TDIGEST.INFO","url":"https://redis.io/docs/latest/commands/tdigest.info/"},{"id":"commands/tdigest.max","summary":"Returns the maximum observation value from a t-digest sketch","title":"TDIGEST.MAX","url":"https://redis.io/docs/latest/commands/tdigest.max/"},{"id":"commands/tdigest.merge","summary":"Merges multiple t-digest sketches into a single sketch","title":"TDIGEST.MERGE","url":"https://redis.io/docs/latest/commands/tdigest.merge/"},{"id":"commands/tdigest.min","summary":"Returns the minimum observation value from a t-digest sketch","title":"TDIGEST.MIN","url":"https://redis.io/docs/latest/commands/tdigest.min/"},{"id":"commands/tdigest.quantile","summary":"Returns, for each input fraction, a floating-point estimation of the value that is smaller than the given fraction of observations","title":"TDIGEST.QUANTILE","url":"https://redis.io/docs/latest/commands/tdigest.quantile/"},{"id":"commands/tdigest.rank","summary":"Returns, for each floating-point input value, the estimated rank of the value (the number of observations in the sketch that are smaller than the value + half the number of observations that are equal to the value)","title":"TDIGEST.RANK","url":"https://redis.io/docs/latest/commands/tdigest.rank/"},{"id":"commands/tdigest.reset","summary":"Resets a t-digest sketch (empties the sketch and re-initializes it).","title":"TDIGEST.RESET","url":"https://redis.io/docs/latest/commands/tdigest.reset/"},{"id":"commands/tdigest.revrank","summary":"Returns, for each floating-point input value, the estimated reverse rank of the value (the number of observations in the sketch that are larger than the value + half the number of observations that are equal to the value)","title":"TDIGEST.REVRANK","url":"https://redis.io/docs/latest/commands/tdigest.revrank/"},{"id":"commands/tdigest.trimmed_mean","summary":"Returns an estimation of the mean value from the sketch, excluding observation values outside the low and high cutoff quantiles","title":"TDIGEST.TRIMMED_MEAN","url":"https://redis.io/docs/latest/commands/tdigest.trimmed_mean/"},{"id":"commands/time","summary":"Returns the server time.","title":"TIME","url":"https://redis.io/docs/latest/commands/time/"},{"id":"commands/topk.add","summary":"Adds an item to a Top-k sketch. Multiple items can be added at the same time.","title":"TOPK.ADD","url":"https://redis.io/docs/latest/commands/topk.add/"},{"id":"commands/topk.count","summary":"Return the count for one or more items in a sketch","title":"TOPK.COUNT","url":"https://redis.io/docs/latest/commands/topk.count/"},{"id":"commands/topk.incrby","summary":"Increases the count of one or more items by increment","title":"TOPK.INCRBY","url":"https://redis.io/docs/latest/commands/topk.incrby/"},{"id":"commands/topk.info","summary":"Returns information about a sketch","title":"TOPK.INFO","url":"https://redis.io/docs/latest/commands/topk.info/"},{"id":"commands/topk.list","summary":"Return the full list of items in the Top-K sketch","title":"TOPK.LIST","url":"https://redis.io/docs/latest/commands/topk.list/"},{"id":"commands/topk.query","summary":"Checks whether one or more items are in a sketch","title":"TOPK.QUERY","url":"https://redis.io/docs/latest/commands/topk.query/"},{"id":"commands/topk.reserve","summary":"Initializes a Top-K sketch with specified parameters","title":"TOPK.RESERVE","url":"https://redis.io/docs/latest/commands/topk.reserve/"},{"id":"commands/touch","summary":"Returns the number of existing keys out of those specified after updating the time they were last accessed.","title":"TOUCH","url":"https://redis.io/docs/latest/commands/touch/"},{"id":"commands/ts.add","summary":"Append a sample to a time series","title":"TS.ADD","url":"https://redis.io/docs/latest/commands/ts.add/"},{"id":"commands/ts.alter","summary":"Update the retention, chunk size, duplicate policy, and labels of an existing time series","title":"TS.ALTER","url":"https://redis.io/docs/latest/commands/ts.alter/"},{"id":"commands/ts.create","summary":"Create a new time series","title":"TS.CREATE","url":"https://redis.io/docs/latest/commands/ts.create/"},{"id":"commands/ts.createrule","summary":"Create a compaction rule.","title":"TS.CREATERULE","url":"https://redis.io/docs/latest/commands/ts.createrule/"},{"id":"commands/ts.decrby","summary":"Decrease the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given decrement","title":"TS.DECRBY","url":"https://redis.io/docs/latest/commands/ts.decrby/"},{"id":"commands/ts.del","summary":"Delete all samples between two timestamps for a given time series","title":"TS.DEL","url":"https://redis.io/docs/latest/commands/ts.del/"},{"id":"commands/ts.deleterule","summary":"Delete a compaction rule","title":"TS.DELETERULE","url":"https://redis.io/docs/latest/commands/ts.deleterule/"},{"id":"commands/ts.get","summary":"Get the sample with the highest timestamp from a given time series","title":"TS.GET","url":"https://redis.io/docs/latest/commands/ts.get/"},{"id":"commands/ts.incrby","summary":"Increase the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given increment","title":"TS.INCRBY","url":"https://redis.io/docs/latest/commands/ts.incrby/"},{"id":"commands/ts.info","summary":"Returns information and statistics for a time series","title":"TS.INFO","url":"https://redis.io/docs/latest/commands/ts.info/"},{"id":"commands/ts.madd","summary":"Append new samples to one or more time series","title":"TS.MADD","url":"https://redis.io/docs/latest/commands/ts.madd/"},{"id":"commands/ts.mget","summary":"Get the sample with the highest timestamp from each time series matching a specific filter","title":"TS.MGET","url":"https://redis.io/docs/latest/commands/ts.mget/"},{"id":"commands/ts.mrange","summary":"Query a range across multiple time series by filters in forward direction","title":"TS.MRANGE","url":"https://redis.io/docs/latest/commands/ts.mrange/"},{"id":"commands/ts.mrevrange","summary":"Query a range across multiple time-series by filters in reverse direction","title":"TS.MREVRANGE","url":"https://redis.io/docs/latest/commands/ts.mrevrange/"},{"id":"commands/ts.nrange","summary":"Query a range across multiple time series in forward direction, returning the results grouped by timestamp","title":"TS.NRANGE","url":"https://redis.io/docs/latest/commands/ts.nrange/"},{"id":"commands/ts.nrevrange","summary":"Query a range across multiple time series in reverse direction, returning the results grouped by timestamp","title":"TS.NREVRANGE","url":"https://redis.io/docs/latest/commands/ts.nrevrange/"},{"id":"commands/ts.queryindex","summary":"Get all time series keys matching a filter list","title":"TS.QUERYINDEX","url":"https://redis.io/docs/latest/commands/ts.queryindex/"},{"id":"commands/ts.querylabels","summary":"Get all label names, or all values of a given label, for time series matching a filter list, or all series","title":"TS.QUERYLABELS","url":"https://redis.io/docs/latest/commands/ts.querylabels/"},{"id":"commands/ts.range","summary":"Query a range in forward direction","title":"TS.RANGE","url":"https://redis.io/docs/latest/commands/ts.range/"},{"id":"commands/ts.read","summary":"Read: return up to max_count samples with timestamp >= timestamp. With BLOCK, waits up to milliseconds ms until at least min_count qualifying samples exist","title":"TS.READ","url":"https://redis.io/docs/latest/commands/ts.read/"},{"id":"commands/ts.revrange","summary":"Query a range in reverse direction","title":"TS.REVRANGE","url":"https://redis.io/docs/latest/commands/ts.revrange/"},{"id":"commands/ttl","summary":"Returns the expiration time in seconds of a key.","title":"TTL","url":"https://redis.io/docs/latest/commands/ttl/"},{"id":"commands/type","summary":"Determines the type of value stored at a key.","title":"TYPE","url":"https://redis.io/docs/latest/commands/type/"},{"id":"commands/unlink","summary":"Asynchronously deletes one or more keys.","title":"UNLINK","url":"https://redis.io/docs/latest/commands/unlink/"},{"id":"commands/unsubscribe","summary":"Stops listening to messages posted to channels.","title":"UNSUBSCRIBE","url":"https://redis.io/docs/latest/commands/unsubscribe/"},{"id":"commands/unwatch","summary":"Forgets about watched keys of a transaction.","title":"UNWATCH","url":"https://redis.io/docs/latest/commands/unwatch/"},{"id":"commands/vadd","summary":"Add a new element to a vector set, or update its vector if it already exists.","title":"VADD","url":"https://redis.io/docs/latest/commands/vadd/"},{"id":"commands/vcard","summary":"Return the number of elements in a vector set.","title":"VCARD","url":"https://redis.io/docs/latest/commands/vcard/"},{"id":"commands/vdim","summary":"Return the dimension of vectors in the vector set.","title":"VDIM","url":"https://redis.io/docs/latest/commands/vdim/"},{"id":"commands/vemb","summary":"Return the vector associated with an element.","title":"VEMB","url":"https://redis.io/docs/latest/commands/vemb/"},{"id":"commands/vgetattr","summary":"Retrieve the JSON attributes of elements.","title":"VGETATTR","url":"https://redis.io/docs/latest/commands/vgetattr/"},{"id":"commands/vinfo","summary":"Return information about a vector set.","title":"VINFO","url":"https://redis.io/docs/latest/commands/vinfo/"},{"id":"commands/vismember","summary":"Check if an element exists in a vector set.","title":"VISMEMBER","url":"https://redis.io/docs/latest/commands/vismember/"},{"id":"commands/vlinks","summary":"Return the neighbors of an element at each layer in the HNSW graph.","title":"VLINKS","url":"https://redis.io/docs/latest/commands/vlinks/"},{"id":"commands/vrandmember","summary":"Return one or multiple random members from a vector set.","title":"VRANDMEMBER","url":"https://redis.io/docs/latest/commands/vrandmember/"},{"id":"commands/vrange","summary":"Return elements in a lexicographical range","title":"VRANGE","url":"https://redis.io/docs/latest/commands/vrange/"},{"id":"commands/vrem","summary":"Remove an element from a vector set.","title":"VREM","url":"https://redis.io/docs/latest/commands/vrem/"},{"id":"commands/vsetattr","summary":"Associate or remove the JSON attributes of elements.","title":"VSETATTR","url":"https://redis.io/docs/latest/commands/vsetattr/"},{"id":"commands/vsim","summary":"Return elements by vector similarity.","title":"VSIM","url":"https://redis.io/docs/latest/commands/vsim/"},{"id":"commands/wait","summary":"Blocks until the asynchronous replication of all preceding write commands sent by the connection is completed.","title":"WAIT","url":"https://redis.io/docs/latest/commands/wait/"},{"id":"commands/waitaof","summary":"Blocks until all of the preceding write commands sent by the connection are written to the append-only file of the master and/or replicas.","title":"WAITAOF","url":"https://redis.io/docs/latest/commands/waitaof/"},{"id":"commands/watch","summary":"Monitors changes to keys to determine the execution of a transaction.","title":"WATCH","url":"https://redis.io/docs/latest/commands/watch/"},{"id":"commands/xack","summary":"Returns the number of messages that were successfully acknowledged by the consumer group member of a stream.","title":"XACK","url":"https://redis.io/docs/latest/commands/xack/"},{"id":"commands/xackdel","summary":"Acknowledges and conditionally deletes one or multiple entries for a stream consumer group.","title":"XACKDEL","url":"https://redis.io/docs/latest/commands/xackdel/"},{"id":"commands/xadd","summary":"Appends a new message to a stream. Creates the key if it doesn't exist.","title":"XADD","url":"https://redis.io/docs/latest/commands/xadd/"},{"id":"commands/xautoclaim","summary":"Changes, or acquires, ownership of messages in a consumer group, as if the messages were delivered to as consumer group member.","title":"XAUTOCLAIM","url":"https://redis.io/docs/latest/commands/xautoclaim/"},{"id":"commands/xcfgset","summary":"Sets the IDMP configuration parameters for a stream.","title":"XCFGSET","url":"https://redis.io/docs/latest/commands/xcfgset/"},{"id":"commands/xclaim","summary":"Changes, or acquires, ownership of a message in a consumer group, as if the message was delivered a consumer group member.","title":"XCLAIM","url":"https://redis.io/docs/latest/commands/xclaim/"},{"id":"commands/xdel","summary":"Returns the number of messages after removing them from a stream.","title":"XDEL","url":"https://redis.io/docs/latest/commands/xdel/"},{"id":"commands/xdelex","summary":"Deletes one or multiple entries from the stream.","title":"XDELEX","url":"https://redis.io/docs/latest/commands/xdelex/"},{"id":"commands/xgroup","summary":"A container for consumer groups commands.","title":"XGROUP","url":"https://redis.io/docs/latest/commands/xgroup/"},{"id":"commands/xgroup-create","summary":"Creates a consumer group.","title":"XGROUP CREATE","url":"https://redis.io/docs/latest/commands/xgroup-create/"},{"id":"commands/xgroup-createconsumer","summary":"Creates a consumer in a consumer group.","title":"XGROUP CREATECONSUMER","url":"https://redis.io/docs/latest/commands/xgroup-createconsumer/"},{"id":"commands/xgroup-delconsumer","summary":"Deletes a consumer from a consumer group.","title":"XGROUP DELCONSUMER","url":"https://redis.io/docs/latest/commands/xgroup-delconsumer/"},{"id":"commands/xgroup-destroy","summary":"Destroys a consumer group.","title":"XGROUP DESTROY","url":"https://redis.io/docs/latest/commands/xgroup-destroy/"},{"id":"commands/xgroup-help","summary":"Returns helpful text about the different subcommands.","title":"XGROUP HELP","url":"https://redis.io/docs/latest/commands/xgroup-help/"},{"id":"commands/xgroup-setid","summary":"Sets the last-delivered ID of a consumer group.","title":"XGROUP SETID","url":"https://redis.io/docs/latest/commands/xgroup-setid/"},{"id":"commands/xidmprecord","summary":"An internal command for setting IDMP metadata on an existing stream message.","title":"XIDMPRECORD","url":"https://redis.io/docs/latest/commands/xidmprecord/"},{"id":"commands/xinfo","summary":"A container for stream introspection commands.","title":"XINFO","url":"https://redis.io/docs/latest/commands/xinfo/"},{"id":"commands/xinfo-consumers","summary":"Returns a list of the consumers in a consumer group.","title":"XINFO CONSUMERS","url":"https://redis.io/docs/latest/commands/xinfo-consumers/"},{"id":"commands/xinfo-groups","summary":"Returns a list of the consumer groups of a stream.","title":"XINFO GROUPS","url":"https://redis.io/docs/latest/commands/xinfo-groups/"},{"id":"commands/xinfo-help","summary":"Returns helpful text about the different subcommands.","title":"XINFO HELP","url":"https://redis.io/docs/latest/commands/xinfo-help/"},{"id":"commands/xinfo-stream","summary":"Returns information about a stream.","title":"XINFO STREAM","url":"https://redis.io/docs/latest/commands/xinfo-stream/"},{"id":"commands/xlen","summary":"Return the number of messages in a stream.","title":"XLEN","url":"https://redis.io/docs/latest/commands/xlen/"},{"id":"commands/xnack","summary":"Releases pending messages back to the group's PEL without acknowledging them, making them available for re-delivery.","title":"XNACK","url":"https://redis.io/docs/latest/commands/xnack/"},{"id":"commands/xpending","summary":"Returns the information and entries from a stream consumer group's pending entries list.","title":"XPENDING","url":"https://redis.io/docs/latest/commands/xpending/"},{"id":"commands/xrange","summary":"Returns the messages from a stream within a range of IDs.","title":"XRANGE","url":"https://redis.io/docs/latest/commands/xrange/"},{"id":"commands/xread","summary":"Returns messages from multiple streams with IDs greater than the ones requested. Blocks until a message is available otherwise.","title":"XREAD","url":"https://redis.io/docs/latest/commands/xread/"},{"id":"commands/xreadgroup","summary":"Returns new or historical messages from a stream for a consumer in a group. Blocks until a message is available otherwise.","title":"XREADGROUP","url":"https://redis.io/docs/latest/commands/xreadgroup/"},{"id":"commands/xrevrange","summary":"Returns the messages from a stream within a range of IDs in reverse order.","title":"XREVRANGE","url":"https://redis.io/docs/latest/commands/xrevrange/"},{"id":"commands/xsetid","summary":"An internal command for replicating stream values.","title":"XSETID","url":"https://redis.io/docs/latest/commands/xsetid/"},{"id":"commands/xtrim","summary":"Deletes messages from the beginning of a stream.","title":"XTRIM","url":"https://redis.io/docs/latest/commands/xtrim/"},{"id":"commands/zadd","summary":"Adds one or more members to a sorted set, or updates their scores. Creates the key if it doesn't exist.","title":"ZADD","url":"https://redis.io/docs/latest/commands/zadd/"},{"id":"commands/zcard","summary":"Returns the number of members in a sorted set.","title":"ZCARD","url":"https://redis.io/docs/latest/commands/zcard/"},{"id":"commands/zcount","summary":"Returns the count of members in a sorted set that have scores within a range.","title":"ZCOUNT","url":"https://redis.io/docs/latest/commands/zcount/"},{"id":"commands/zdiff","summary":"Returns the difference between multiple sorted sets.","title":"ZDIFF","url":"https://redis.io/docs/latest/commands/zdiff/"},{"id":"commands/zdiffstore","summary":"Stores the difference of multiple sorted sets in a key.","title":"ZDIFFSTORE","url":"https://redis.io/docs/latest/commands/zdiffstore/"},{"id":"commands/zincrby","summary":"Increments the score of a member in a sorted set.","title":"ZINCRBY","url":"https://redis.io/docs/latest/commands/zincrby/"},{"id":"commands/zinter","summary":"Returns the intersect of multiple sorted sets.","title":"ZINTER","url":"https://redis.io/docs/latest/commands/zinter/"},{"id":"commands/zintercard","summary":"Returns the number of members of the intersect of multiple sorted sets.","title":"ZINTERCARD","url":"https://redis.io/docs/latest/commands/zintercard/"},{"id":"commands/zinterstore","summary":"Stores the intersect of multiple sorted sets in a key.","title":"ZINTERSTORE","url":"https://redis.io/docs/latest/commands/zinterstore/"},{"id":"commands/zlexcount","summary":"Returns the number of members in a sorted set within a lexicographical range.","title":"ZLEXCOUNT","url":"https://redis.io/docs/latest/commands/zlexcount/"},{"id":"commands/zmpop","summary":"Returns the highest- or lowest-scoring members from one or more sorted sets after removing them. Deletes the sorted set if the last member was popped.","title":"ZMPOP","url":"https://redis.io/docs/latest/commands/zmpop/"},{"id":"commands/zmscore","summary":"Returns the score of one or more members in a sorted set.","title":"ZMSCORE","url":"https://redis.io/docs/latest/commands/zmscore/"},{"id":"commands/zpopmax","summary":"Returns the highest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.","title":"ZPOPMAX","url":"https://redis.io/docs/latest/commands/zpopmax/"},{"id":"commands/zpopmin","summary":"Returns the lowest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.","title":"ZPOPMIN","url":"https://redis.io/docs/latest/commands/zpopmin/"},{"id":"commands/zrandmember","summary":"Returns one or more random members from a sorted set.","title":"ZRANDMEMBER","url":"https://redis.io/docs/latest/commands/zrandmember/"},{"id":"commands/zrange","summary":"Returns members in a sorted set within a range of indexes.","title":"ZRANGE","url":"https://redis.io/docs/latest/commands/zrange/"},{"id":"commands/zrangebylex","summary":"Returns members in a sorted set within a lexicographical range.","title":"ZRANGEBYLEX","url":"https://redis.io/docs/latest/commands/zrangebylex/"},{"id":"commands/zrangebyscore","summary":"Returns members in a sorted set within a range of scores.","title":"ZRANGEBYSCORE","url":"https://redis.io/docs/latest/commands/zrangebyscore/"},{"id":"commands/zrangestore","summary":"Stores a range of members from sorted set in a key.","title":"ZRANGESTORE","url":"https://redis.io/docs/latest/commands/zrangestore/"},{"id":"commands/zrank","summary":"Returns the index of a member in a sorted set ordered by ascending scores.","title":"ZRANK","url":"https://redis.io/docs/latest/commands/zrank/"},{"id":"commands/zrem","summary":"Removes one or more members from a sorted set. Deletes the sorted set if all members were removed.","title":"ZREM","url":"https://redis.io/docs/latest/commands/zrem/"},{"id":"commands/zremrangebylex","summary":"Removes members in a sorted set within a lexicographical range. Deletes the sorted set if all members were removed.","title":"ZREMRANGEBYLEX","url":"https://redis.io/docs/latest/commands/zremrangebylex/"},{"id":"commands/zremrangebyrank","summary":"Removes members in a sorted set within a range of indexes. Deletes the sorted set if all members were removed.","title":"ZREMRANGEBYRANK","url":"https://redis.io/docs/latest/commands/zremrangebyrank/"},{"id":"commands/zremrangebyscore","summary":"Removes members in a sorted set within a range of scores. Deletes the sorted set if all members were removed.","title":"ZREMRANGEBYSCORE","url":"https://redis.io/docs/latest/commands/zremrangebyscore/"},{"id":"commands/zrevrange","summary":"Returns members in a sorted set within a range of indexes in reverse order.","title":"ZREVRANGE","url":"https://redis.io/docs/latest/commands/zrevrange/"},{"id":"commands/zrevrangebylex","summary":"Returns members in a sorted set within a lexicographical range in reverse order.","title":"ZREVRANGEBYLEX","url":"https://redis.io/docs/latest/commands/zrevrangebylex/"},{"id":"commands/zrevrangebyscore","summary":"Returns members in a sorted set within a range of scores in reverse order.","title":"ZREVRANGEBYSCORE","url":"https://redis.io/docs/latest/commands/zrevrangebyscore/"},{"id":"commands/zrevrank","summary":"Returns the index of a member in a sorted set ordered by descending scores.","title":"ZREVRANK","url":"https://redis.io/docs/latest/commands/zrevrank/"},{"id":"commands/zscan","summary":"Iterates over members and scores of a sorted set.","title":"ZSCAN","url":"https://redis.io/docs/latest/commands/zscan/"},{"id":"commands/zscore","summary":"Returns the score of a member in a sorted set.","title":"ZSCORE","url":"https://redis.io/docs/latest/commands/zscore/"},{"id":"commands/zunion","summary":"Returns the union of multiple sorted sets.","title":"ZUNION","url":"https://redis.io/docs/latest/commands/zunion/"},{"id":"commands/zunionstore","summary":"Stores the union of multiple sorted sets in a key.","title":"ZUNIONSTORE","url":"https://redis.io/docs/latest/commands/zunionstore/"}],"page_type":"index","sections":[],"examples":[]}
{"schema_version":2,"id":"commands/info","title":"INFO","url":"https://redis.io/docs/latest/commands/info/","summary":"Returns information and statistics about the server.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"b5b688222923fbfbb8cb882a60e039a2e671c5d9a7676b9ea82882bb12266bab","sections":[{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>section [section ...]</code></summary>\n\nOne or more sections to include in the output, such as `server`, `clients`, or `memory`. Also accepts `all`, `everything`, and `default`.\n\n</details>"},{"id":"details","title":"Details","role":"content","text":"Depending on the version of Redis some of the fields have been\nadded or removed. A robust client application should therefore parse the\nresult of this command by skipping unknown properties and gracefully handling\nmissing fields.\n\nHere is the description of fields for Redis >= 2.4.\n\nHere is the meaning of all fields in the **server** section:\n\n*   `redis_version`: Version of the Redis server\n*   `redis_git_sha1`:  Git SHA1\n*   `redis_git_dirty`: Git dirty flag\n*   `redis_build_id`: The build id\n*   `redis_mode`: The server's mode (\"standalone\", \"sentinel\" or \"cluster\")\n*   `os`: Operating system hosting the Redis server\n*   `arch_bits`: Architecture (32 or 64 bits)\n*   `multiplexing_api`: Event loop mechanism used by Redis\n*   `atomicvar_api`: Atomicvar API used by Redis\n*   `gcc_version`: Version of the GCC compiler used to compile the Redis server\n*   `process_id`: PID of the server process\n*   `process_supervised`: Supervised system (\"upstart\", \"systemd\", \"unknown\" or \"no\")\n*   `run_id`: Random value identifying the Redis server (to be used by Sentinel\n     and Cluster)\n*   `tcp_port`: TCP/IP listen port\n*   `server_time_usec`: Epoch-based system time with microsecond precision\n*   `uptime_in_seconds`: Number of seconds since Redis server start\n*   `uptime_in_days`: Same value expressed in days\n*   `hz`: The server's current frequency setting\n*   `configured_hz`: The server's configured frequency setting\n*   `lru_clock`: Clock incrementing every minute, for LRU management\n*   `executable`: The path to the server's executable\n*   `config_file`: The path to the config file\n*   `io_threads_active`: Flag indicating if I/O threads are active\n*   `shutdown_in_milliseconds`: The maximum time remaining for replicas to catch up the replication before completing the shutdown sequence.\n    This field is only present during shutdown.\n\nHere is the meaning of all fields in the **clients** section:\n\n*   `connected_clients`: Number of client connections (excluding connections\n     from replicas)\n*   `cluster_connections`: An approximation of the number of sockets used by the\n     cluster's bus\n*   `maxclients`: The value of the `maxclients` configuration directive. This is\n    the upper limit for the sum of `connected_clients`, `connected_slaves` and\n    `cluster_connections`.\n*   `client_recent_max_input_buffer`: Biggest input buffer among current client connections\n*   `client_recent_max_output_buffer`: Biggest output buffer among current client connections\n*   `blocked_clients`: Number of clients pending on a blocking call ([`BLPOP`](https://redis.io/docs/latest/commands/blpop),\n     [`BRPOP`](https://redis.io/docs/latest/commands/brpop), [`BRPOPLPUSH`](https://redis.io/docs/latest/commands/brpoplpush), [`BLMOVE`](https://redis.io/docs/latest/commands/blmove), [`BZPOPMIN`](https://redis.io/docs/latest/commands/bzpopmin), [`BZPOPMAX`](https://redis.io/docs/latest/commands/bzpopmax))\n*   `tracking_clients`: Number of clients being tracked ([`CLIENT TRACKING`](https://redis.io/docs/latest/commands/client-tracking))\n*   `pubsub_clients`: Number of clients in pubsub mode ([`SUBSCRIBE`](https://redis.io/docs/latest/commands/subscribe), [`PSUBSCRIBE`](https://redis.io/docs/latest/commands/psubscribe), [`SSUBSCRIBE`](https://redis.io/docs/latest/commands/ssubscribe)). Added in Redis 7.4\n*   `watching_clients`: Number of clients in watching mode ([`WATCH`](https://redis.io/docs/latest/commands/watch)). Added in Redis 7.4\n*   `clients_in_timeout_table`: Number of clients in the clients timeout table\n*   `total_watched_keys`: Number of watched keys. Added in Redis 7.4.\n*   `total_blocking_keys`: Number of blocking keys. Added in Redis 7.2.\n*   `total_blocking_keys_on_nokey`: Number of blocking keys that one or more clients that would like to be unblocked when the key is deleted. Added in Redis 7.2.\n\nHere is the meaning of all fields in the **memory** section:\n\n*   `used_memory`: Total number of bytes allocated by Redis using its\n     allocator (either standard **libc**, **jemalloc**, or an alternative\n     allocator such as [**tcmalloc**](http://code.google.com/p/google-perftools/))\n*   `used_memory_human`: Human readable representation of previous value\n*   `used_memory_rss`: Number of bytes that Redis allocated as seen by the\n     operating system (a.k.a resident set size). This is the number reported by\n     tools such as `top(1)` and `ps(1)`\n*   `used_memory_rss_human`: Human readable representation of previous value\n*   `used_memory_peak`: Peak memory consumed by Redis (in bytes)\n*   `used_memory_peak_human`: Human readable representation of previous value\n*   `used_memory_peak_time`: Time when peak memory was recorded\n*   `used_memory_peak_perc`: The percentage of `used_memory` out of `used_memory_peak`\n*   `used_memory_overhead`: The sum in bytes of all overheads that the server\n     allocated for managing its internal data structures\n*   `used_memory_startup`: Initial amount of memory consumed by Redis at startup\n     in bytes\n*   `used_memory_dataset`: The size in bytes of the dataset\n     (`used_memory_overhead` subtracted from `used_memory`)\n*   `used_memory_dataset_perc`: The percentage of `used_memory_dataset` out of\n     the net memory usage (`used_memory` minus `used_memory_startup`)\n*   `total_system_memory`: The total amount of memory that the Redis host has\n*   `total_system_memory_human`: Human readable representation of previous value\n*   `used_memory_lua`: Number of bytes used by the Lua engine for EVAL scripts. Deprecated in Redis 7.0, renamed to `used_memory_vm_eval`\n*   `used_memory_vm_eval`: Number of bytes used by the script VM engines for EVAL framework (not part of used_memory). Added in Redis 7.0\n*   `used_memory_lua_human`: Human readable representation of previous value. Deprecated in Redis 7.0\n*   `used_memory_scripts_eval`: Number of bytes overhead by the EVAL scripts (part of used_memory). Added in Redis 7.0\n*   `number_of_cached_scripts`: The number of EVAL scripts cached by the server. Added in Redis 7.0\n*   `number_of_functions`: The number of functions. Added in Redis 7.0\n*   `number_of_libraries`: The number of libraries. Added in Redis 7.0\n*   `used_memory_vm_functions`: Number of bytes used by the script VM engines for Functions framework (not part of used_memory). Added in Redis 7.0\n*   `used_memory_vm_total`: `used_memory_vm_eval` + `used_memory_vm_functions` (not part of used_memory). Added in Redis 7.0\n*   `used_memory_vm_total_human`: Human readable representation of previous value.\n*   `used_memory_functions`: Number of bytes overhead by Function scripts (part of used_memory). Added in Redis 7.0\n*   `used_memory_scripts`: `used_memory_scripts_eval` + `used_memory_functions` (part of used_memory). Added in Redis 7.0\n*   `used_memory_scripts_human`: Human readable representation of previous value\n*   `used_memory_hash_templates`: Total memory in bytes used by all compact hash templates (the shared template registry). Added in Redis 8.10.\n*   `maxmemory`: The value of the `maxmemory` configuration directive\n*   `maxmemory_human`: Human readable representation of previous value\n*   `maxmemory_policy`: The value of the `maxmemory-policy` configuration\n     directive\n*   `mem_fragmentation_ratio`: Ratio between `used_memory_rss` and `used_memory`.\n    Note that this doesn't only includes fragmentation, but also other process overheads (see the `allocator_*` metrics), and also overheads like code, shared libraries, stack, etc.\n*   `mem_fragmentation_bytes`: Delta between `used_memory_rss` and `used_memory`.\n    Note that when the total fragmentation bytes is low (few megabytes), a high ratio (e.g. 1.5 and above) is not an indication of an issue.\n*   `allocator_frag_ratio:`: Ratio between `allocator_active` and `allocator_allocated`. This is the true (external) fragmentation metric (not `mem_fragmentation_ratio`).\n*   `allocator_frag_bytes` Delta between `allocator_active` and `allocator_allocated`. See note about `mem_fragmentation_bytes`.\n*   `allocator_rss_ratio`: Ratio between `allocator_resident` and `allocator_active`. This usually indicates pages that the allocator can and probably will soon release back to the OS.\n*   `allocator_rss_bytes`: Delta between `allocator_resident` and `allocator_active`\n*   `rss_overhead_ratio`: Ratio between `used_memory_rss` (the process RSS) and `allocator_resident`. This includes RSS overheads that are not allocator or heap related.\n*   `rss_overhead_bytes`: Delta between `used_memory_rss` (the process RSS) and `allocator_resident`\n*   `allocator_allocated`: Total bytes allocated form the allocator, including internal-fragmentation. Normally the same as `used_memory`.\n*   `allocator_active`: Total bytes in the allocator active pages, this includes external-fragmentation.\n*   `allocator_resident`: Total bytes resident (RSS) in the allocator, this includes pages that can be released to the OS (by [`MEMORY PURGE`](https://redis.io/docs/latest/commands/memory-purge), or just waiting).\n*   `allocator_muzzy`: Total bytes of 'muzzy' memory (RSS) in the allocator. Muzzy memory is memory that has been freed, but not yet fully returned to the operating system. It can be reused immediately when needed or reclaimed by the OS when system pressure increases.\n*   `mem_not_counted_for_evict`: Used memory that's not counted for key eviction. This is basically transient replica and AOF buffers.\n*   `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.\n*   `mem_clients_normal`: Memory used by normal clients\n*   `mem_cluster_links`: Memory used by links to peers on the cluster bus when cluster mode is enabled.\n*   `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.\n*   `mem_cluster_slot_migration_input_buffer`: Memory usage of the accumulated replication stream buffer on the importing node.\n*   `mem_cluster_slot_migration_input_buffer_peak`: Peak accumulated repl buffer size on the importing side.\n*   `mem_aof_buffer`: Transient memory used for AOF and AOF rewrite buffers\n*   `mem_replication_backlog`: Memory used by replication backlog\n*   `mem_total_replication_buffers`: Total memory consumed for replication buffers - Added in Redis 7.0.\n*   `mem_allocator`: Memory allocator, chosen at compile time.\n*   `mem_overhead_db_hashtable_rehashing`: Temporary memory overhead of database dictionaries currently being rehashed - Added in 7.4.\n*   `active_defrag_running`: When `activedefrag` is enabled, this indicates whether defragmentation is currently active, and the CPU percentage it intends to utilize.\n*   `lazyfree_pending_objects`: The number of objects waiting to be freed (as a\n     result of calling [`UNLINK`](https://redis.io/docs/latest/commands/unlink), or [`FLUSHDB`](https://redis.io/docs/latest/commands/flushdb) and [`FLUSHALL`](https://redis.io/docs/latest/commands/flushall) with the **ASYNC**\n     option)\n*   `lazyfreed_objects`: The number of objects that have been lazy freed.\n\nIdeally, the `used_memory_rss` value should be only slightly higher than\n`used_memory`.\nWhen rss >> used, a large difference may mean there is (external) memory fragmentation, which can be evaluated by checking\n`allocator_frag_ratio`, `allocator_frag_bytes`.\nWhen used >> rss, it means part of Redis memory has been swapped off by the\noperating system: expect some significant latencies.\n\nBecause Redis does not have control over how its allocations are mapped to\nmemory pages, high `used_memory_rss` is often the result of a spike in memory\nusage.\n\nWhen Redis frees memory, the memory is given back to the allocator, and the\nallocator may or may not give the memory back to the system. There may be\na discrepancy between the `used_memory` value and memory consumption as\nreported by the operating system. It may be due to the fact memory has been\nused and released by Redis, but not given back to the system. The\n`used_memory_peak` value is generally useful to check this point.\n\nAdditional introspective information about the server's memory can be obtained\nby referring to the [`MEMORY STATS`](https://redis.io/docs/latest/commands/memory-stats) command and the [`MEMORY DOCTOR`](https://redis.io/docs/latest/commands/memory-doctor).\n\nHere is the meaning of all fields in the **persistence** section:\n\n*   `loading`: Flag indicating if the load of a dump file is on-going\n*   `async_loading`: Currently loading replication data-set asynchronously while serving old data. This means `repl-diskless-load` is enabled and set to `swapdb`. Added in Redis 7.0.\n*   `current_cow_peak`: The peak size in bytes of copy-on-write memory\n     while a child fork is running\n*   `current_cow_size`: The size in bytes of copy-on-write memory\n     while a child fork is running\n*   `current_cow_size_age`: The age, in seconds, of the `current_cow_size` value.\n*   `current_fork_perc`: The percentage of progress of the current fork process. For AOF and RDB forks it is the percentage of `current_save_keys_processed` out of `current_save_keys_total`.\n*   `current_save_keys_processed`: Number of keys processed by the current save operation\n*   `current_save_keys_total`: Number of keys at the beginning of the current save operation \n*   `rdb_changes_since_last_save`: Number of changes since the last dump\n*   `rdb_bgsave_in_progress`: Flag indicating a RDB save is on-going\n*   `rdb_last_save_time`: Epoch-based timestamp of last successful RDB save\n*   `rdb_last_bgsave_status`: Status of the last RDB save operation\n*   `rdb_last_bgsave_time_sec`: Duration of the last RDB save operation in\n     seconds\n*   `rdb_current_bgsave_time_sec`: Duration of the on-going RDB save operation\n     if any\n*   `rdb_last_cow_size`: The size in bytes of copy-on-write memory during\n     the last RDB save operation\n*   `rdb_last_load_keys_expired`: Number of volatile keys deleted during the last RDB loading. Added in Redis 7.0.\n*   `rdb_last_load_keys_loaded`: Number of keys loaded during the last RDB loading. Added in Redis 7.0.\n*   `aof_enabled`: Flag indicating AOF logging is activated\n*   `aof_rewrite_in_progress`: Flag indicating a AOF rewrite operation is\n     on-going\n*   `aof_rewrite_scheduled`: Flag indicating an AOF rewrite operation\n     will be scheduled once the on-going RDB save is complete.\n*   `aof_last_rewrite_time_sec`: Duration of the last AOF rewrite operation in\n     seconds\n*   `aof_current_rewrite_time_sec`: Duration of the on-going AOF rewrite\n     operation if any\n*   `aof_last_bgrewrite_status`: Status of the last AOF rewrite operation\n*   `aof_last_write_status`: Status of the last write operation to the AOF\n*   `aof_last_cow_size`: The size in bytes of copy-on-write memory during\n     the last AOF rewrite operation\n*   `module_fork_in_progress`: Flag indicating a module fork is on-going\n*   `module_fork_last_cow_size`: The size in bytes of copy-on-write memory\n     during the last module fork operation\n*   `aof_rewrites`: Number of AOF rewrites performed since startup\n*   `rdb_saves`: Number of RDB snapshots performed since startup\n\n`rdb_changes_since_last_save` refers to the number of operations that produced\nsome kind of changes in the dataset since the last time either [`SAVE`](https://redis.io/docs/latest/commands/save) or\n[`BGSAVE`](https://redis.io/docs/latest/commands/bgsave) was called.\n\nIf AOF is activated, these additional fields will be added:\n\n*   `aof_current_size`: AOF current file size\n*   `aof_base_size`: AOF file size on latest startup or rewrite\n*   `aof_pending_rewrite`: Flag indicating an AOF rewrite operation\n     will be scheduled once the on-going RDB save is complete.\n*   `aof_buffer_length`: Size of the AOF buffer\n*   `aof_rewrite_buffer_length`: Size of the AOF rewrite buffer. Note this field was removed in Redis 7.0\n*   `aof_pending_bio_fsync`: Number of fsync pending jobs in background I/O\n     queue\n*   `aof_delayed_fsync`: Delayed fsync counter\n\nIf a load operation is on-going, these additional fields will be added:\n\n*   `loading_start_time`: Epoch-based timestamp of the start of the load\n     operation\n*   `loading_total_bytes`: Total file size\n*   `loading_rdb_used_mem`: The memory usage of the server that had generated\n    the RDB file at the time of the file's creation\n*   `loading_loaded_bytes`: Number of bytes already loaded\n*   `loading_loaded_perc`: Same value expressed as a percentage\n*   `loading_eta_seconds`: ETA in seconds for the load to be complete\n\nThe **threads** section provides statistics on I/O threads.\n The statistics are the number of assigned clients,\n the number of read events processed, and the number of write events processed.\n Added in Redis 8.0.\n\nFor each I/O thread, the following line is added:\n\n*   `io_thread_XXX`: `clients=XXX,reads=XXX,writes=XXX`\n\nHere is the meaning of all fields in the **stats** section:\n\n*   `total_connections_received`: Total number of connections accepted by the\n     server\n*   `total_commands_processed`: Total number of commands processed by the server\n*   `instantaneous_ops_per_sec`: Number of commands processed per second\n*   `total_net_input_bytes`: The total number of bytes read from the network\n*   `total_net_output_bytes`: The total number of bytes written to the network\n*   `total_net_repl_input_bytes`: The total number of bytes read from the network for replication purposes\n*   `total_net_repl_output_bytes`: The total number of bytes written to the network for replication purposes\n*   `instantaneous_input_kbps`: The network's read rate per second in KB/sec\n*   `instantaneous_output_kbps`: The network's write rate per second in KB/sec\n*   `instantaneous_input_repl_kbps`: The network's read rate per second in KB/sec for replication purposes\n*   `instantaneous_output_repl_kbps`: The network's write rate per second in KB/sec for replication purposes\n*   `rejected_connections`: Number of connections rejected because of\n     `maxclients` limit\n*   `sync_full`: The number of full resyncs with replicas\n*   `sync_partial_ok`: The number of accepted partial resync requests\n*   `sync_partial_err`: The number of denied partial resync requests\n*   `expired_subkeys`: The number of hash field expiration events\n*   `expired_keys`: Total number of key expiration events\n*   `expired_stale_perc`: The percentage of keys probably expired\n*   `expired_time_cap_reached_count`: The count of times that active expiry cycles have stopped early\n*   `expire_cycle_cpu_milliseconds`: The cumulative amount of time spent on active expiry cycles\n*   `evicted_keys`: Number of evicted keys due to `maxmemory` limit\n*   `evicted_clients`: Number of evicted clients due to `maxmemory-clients` limit. Added in Redis 7.0.\n*   `evicted_scripts`: Number of evicted EVAL scripts due to LRU policy, see [`EVAL`](https://redis.io/docs/latest/commands/eval) for more details. Added in Redis 7.4.\n*   `total_eviction_exceeded_time`:  Total time `used_memory` was greater than `maxmemory` since server startup, in milliseconds\n*   `current_eviction_exceeded_time`: The time passed since `used_memory` last rose above `maxmemory`, in milliseconds\n*   `keyspace_hits`: Number of successful lookup of keys in the main dictionary\n*   `keyspace_misses`: Number of failed lookup of keys in the main dictionary\n*   `hash_templates`: Number of distinct compact hash templates in the shared registry. Added in Redis 8.10.\n*   `hash_template_keys`: Total number of keys backed by a compact hash template. Added in Redis 8.10.\n*   `pubsub_channels`: Global number of pub/sub channels with client\n     subscriptions\n*   `pubsub_patterns`: Global number of pub/sub pattern with client\n     subscriptions\n*   `pubsubshard_channels`: Global number of pub/sub shard channels with client subscriptions. Added in Redis 7.0.3\n*   `latest_fork_usec`: Duration of the latest fork operation in microseconds\n*   `total_forks`: Total number of fork operations since the server start\n*   `migrate_cached_sockets`: The number of sockets open for [`MIGRATE`](https://redis.io/docs/latest/commands/migrate) purposes\n*   `slave_expires_tracked_keys`: The number of keys tracked for expiry purposes\n     (applicable only to writable replicas)\n*   `active_defrag_hits`: Number of value reallocations performed by active the\n     defragmentation process\n*   `active_defrag_misses`: Number of aborted value reallocations started by the\n     active defragmentation process\n*   `active_defrag_key_hits`: Number of keys that were actively defragmented\n*   `active_defrag_key_misses`: Number of keys that were skipped by the active\n     defragmentation process\n*   `total_active_defrag_time`: Total time memory fragmentation was over the limit, in milliseconds\n*   `current_active_defrag_time`: The time passed since memory fragmentation last was over the limit, in milliseconds\n*   `tracking_total_keys`: Number of keys being tracked by the server\n*   `tracking_total_items`: Number of items, that is the sum of clients number for\n     each key, that are being tracked\n*   `tracking_total_prefixes`: Number of tracked prefixes in server's prefix table\n    (only applicable for broadcast mode)\n*   `unexpected_error_replies`: Number of unexpected error replies, that are types\n    of errors from an AOF load or replication\n*   `total_error_replies`: Total number of issued error replies, that is the sum of\n    rejected commands (errors prior command execution) and\n    failed commands (errors within the command execution)\n*   `dump_payload_sanitizations`: Total number of dump payload deep integrity validations (see `sanitize-dump-payload` config).\n*   `total_reads_processed`: Total number of read events processed\n*   `total_writes_processed`: Total number of write events processed\n*   `io_threaded_reads_processed`: Number of read events processed by I/O threads\n*   `io_threaded_writes_processed`: Number of write events processed by I/O threads\n*   `client_query_buffer_limit_disconnections`: Total number of disconnections due to client reaching query buffer limit\n*   `client_output_buffer_limit_disconnections`: Total number of disconnections due to client reaching output buffer limit\n*   `reply_buffer_shrinks`: Total number of output buffer shrinks\n*   `reply_buffer_expands`: Total number of output buffer expands\n*   `eventloop_cycles`: Total number of eventloop cycles\n*   `eventloop_duration_sum`: Total time spent in the eventloop in microseconds (including I/O and command processing)\n*   `eventloop_duration_cmd_sum`: Total time spent on executing commands in microseconds\n*   `instantaneous_eventloop_cycles_per_sec`: Number of eventloop cycles per second\n*   `instantaneous_eventloop_duration_usec`: Average time spent in a single eventloop cycle in microseconds\n*   `acl_access_denied_auth`: Number of authentication failures\n*   `acl_access_denied_cmd`: Number of commands rejected because of access denied to the command\n*   `acl_access_denied_key`: Number of commands rejected because of access denied to a key\n*   `acl_access_denied_channel`: Number of commands rejected because of access denied to a channel\n*   `acl_access_denied_tls_cert`: Number of failed TLS certificate–based authentication attempts\n*   `cluster_incompatible_ops`: Number of cluster-incompatible commands. This metric appears only if the `cluster-compatibility-sample-ratio` configuration parameter is not 0. Added in Redis 8.0.\n*   `slowlog_commands_count`: commands written to slowlog <sup>[1](#list-note-1)</sup>\n*   `slowlog_commands_time_ms_sum`: sum of execution times of commands from the slowlog <sup>[1](#list-note-1)</sup>\n*   `slowlog_commands_time_ms_max`: maximum execution time of a command from the slowlog <sup>[1](#list-note-1)</sup>\n*   `total_client_processing_events`: attempts to process client input buffers; does not guarantee any command was actually parsed <sup>[1](#list-note-1)</sup>\n*   `eventloop_cycles_with_clients_processing`: event loop cycles where client input buffers were processed <sup>[1](#list-note-1)</sup>\n*   `commands_per_parse_batch_sum`: cumulative number of commands parsed across all parsing batches for all clients <sup>[1](#list-note-1)</sup>\n*   `commands_per_parse_batch_cnt`: number of parsing batches across all clients. A batch is counted each time at least one command is parsed from a client's query buffer <sup>[1](#list-note-1)</sup>\n*   `commands_per_parse_batch_avg`: average commands parsed per batch (sum/cnt). Approximates pipelining depth <sup>[1](#list-note-1)</sup>\n\n1. <a name=\"list-note-1\"></a>Added in Redis 8.8\n\n\nHere is the meaning of all fields in the **replication** section:\n\n*   `role`: Value is \"master\" if the instance is replica of no one, or \"slave\" if the instance is a replica of some master instance.\n     Note that a replica can be master of another replica (chained replication).\n*   `master_failover_state`: The state of an ongoing failover, if any.\n*   `master_replid`: The replication ID of the Redis server.\n*   `master_replid2`: The secondary replication ID, used for PSYNC after a failover.\n*   `master_repl_offset`: The server's current replication offset\n*   `second_repl_offset`: The offset up to which replication IDs are accepted\n*   `repl_backlog_active`: Flag indicating replication backlog is active\n*   `repl_backlog_size`: Total size in bytes of the replication backlog buffer\n*   `repl_backlog_first_byte_offset`: The master offset of the replication\n     backlog buffer\n*   `repl_backlog_histlen`: Size in bytes of the data in the replication backlog\n     buffer\n\nIf the instance is a replica, these additional fields are provided:\n\n*   `master_host`: Host or IP address of the master\n*   `master_port`: Master listening TCP port\n*   `master_link_status`: Status of the link (up/down)\n*   `master_last_io_seconds_ago`: Number of seconds since the last interaction\n     with master\n*   `master_sync_in_progress`: Indicate the master is syncing to the replica\n*   `slave_read_repl_offset`: The read replication offset of the replica instance.\n*   `slave_repl_offset`: The replication offset of the replica instance\n*   `slave_priority`: The priority of the instance as a candidate for failover\n*   `slave_read_only`: Flag indicating if the replica is read-only\n*   `replica_announced`: Flag indicating if the replica is announced by Sentinel.\n\nIf a SYNC operation is on-going, these additional fields are provided:\n\n*   `master_sync_total_bytes`: Total number of bytes that need to be \n    transferred. this may be 0 when the size is unknown (for example, when\n    the `repl-diskless-sync` configuration directive is used)\n*   `master_sync_read_bytes`: Number of bytes already transferred\n*   `master_sync_left_bytes`: Number of bytes left before syncing is complete\n    (may be negative when `master_sync_total_bytes` is 0)\n*   `master_sync_perc`: The percentage `master_sync_read_bytes` from \n    `master_sync_total_bytes`, or an approximation that uses\n    `loading_rdb_used_mem` when `master_sync_total_bytes` is 0\n*   `master_sync_last_io_seconds_ago`: Number of seconds since last transfer I/O\n     during a SYNC operation\n\nIf the link between master and replica is down, an additional field is provided:\n\n*   `master_link_down_since_seconds`: Number of seconds since the link is down\n\nThe following field is always provided:\n\n*   `connected_slaves`: Number of connected replicas\n\nIf the server is configured with the `min-slaves-to-write` (or starting with Redis 5 with the `min-replicas-to-write`) directive, an additional field is provided:\n\n*   `min_slaves_good_slaves`: Number of replicas currently considered good\n\nFor each replica, the following line is added:\n\n*   `slaveXXX`: id, IP address, port, state, offset, lag\n\nHere is the meaning of all fields in the **cpu** section:\n\n*   `used_cpu_sys`: System CPU consumed by the Redis server, which is the sum of system CPU consumed by all threads of the server process (main thread and background threads)\n*   `used_cpu_user`: User CPU consumed by the Redis server, which is the sum of user CPU consumed by all threads of the server process (main thread and background threads)\n*   `used_cpu_sys_children`: System CPU consumed by the background processes\n*   `used_cpu_user_children`: User CPU consumed by the background processes\n*   `used_cpu_sys_main_thread`: System CPU consumed by the Redis server main thread\n*   `used_cpu_user_main_thread`: User CPU consumed by the Redis server main thread\n\nThe **commandstats** section provides statistics based on the command type:\n\n- `calls` - the number of calls that reached command execution\n- `usec` - the total CPU time consumed by these commands\n- `usec_per_call` - the average CPU consumed per command execution\n- `rejected_calls` - the number of rejected calls\n- `failed_calls` - the number of failed calls\n \n For commands that are logged to the slowlog, the following statistics are also reported (added in Redis 8.8): \n\n- `slowlog_count` - number of times the command was written in the slowlog\n- `slowlog_time_ms_sum` - sum of execution time of the command (only from the slowlog)\n- `slowlog_time_ms_max` - maximum execution time of the command (only from the slowlog)\n\nFor each command type, the following line is added:\n\n*   `cmdstat_XXX`:`calls=XXX,usec=XXX,usec_per_call=XXX,rejected_calls=XXX,failed_calls=XXX,`\n    `slowlog_count=XXX,slowlog_time_ms_sum=XXX,slowlog_time_ms_max=XXX`\n\nThe **latencystats** section provides latency percentile distribution statistics based on the command type.\n\n By default, the exported latency percentiles are the p50, p99, and p999.\n If you need to change the exported percentiles, use `CONFIG SET latency-tracking-info-percentiles \"50.0 99.0 99.9\"`.\n\n This section requires the extended latency monitoring feature to be enabled (by default it's enabled).\n If you need to enable it, use `CONFIG SET latency-tracking yes`.\n\nFor each command type, the following line is added:\n\n*   `latency_percentiles_usec_XXX: p<percentile 1>=<percentile 1 value>,p<percentile 2>=<percentile 2 value>,...`\n\nThe **errorstats** section enables keeping track of the different errors that occurred within Redis, \n based upon the reply error prefix ( The first word after the \"-\", up to the first space. Example: `ERR` ).\n\nFor each error type, the following line is added:\n\n*   `errorstat_XXX`: `count=XXX`\n\nIf the server detects that this section was flooded with an excessive number of errors, it will be disabled, show a single `ERRORSTATS_DISABLED` error, and print the errors to the server log.\nThis can be reset by `CONFIG RESETSTAT`.\n\nThe **hotkeys** section provides information about hotkeys tracking. It consists of the following fields:\n\n*   `tracking_active`: Boolean flag (0 or 1) indicating whether hotkeys tracking is currently active.\n*   `used_memory`: Memory overhead in bytes of the structures used for hotkeys tracking.\n*   `cpu_time`: Time in milliseconds spent updating the hotkey tracking structures.\n\nThe **sentinel** section is only available in Redis Sentinel instances. It consists of the following fields:\n\n*   `sentinel_masters`: Number of Redis masters monitored by this Sentinel instance\n*   `sentinel_tilt`: A value of 1 means this sentinel is in TILT mode\n*   `sentinel_tilt_since_seconds`: Duration in seconds of current TILT, or -1 if not TILTed. Added in Redis 7.0.0\n*   `sentinel_total_tilt`: The number of times this sentinel has been in TILT mode since running.\n*   `sentinel_running_scripts`: The number of scripts this Sentinel is currently executing\n*   `sentinel_scripts_queue_length`: The length of the queue of user scripts that are pending execution\n*   `sentinel_simulate_failure_flags`: Flags for the `SENTINEL SIMULATE-FAILURE` command\n    \nThe **cluster** section contains a single field:\n\n*   `cluster_enabled`: Indicates whether Redis cluster is enabled.\n\nThe **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.\n\n_Redis Search fields_\n\n*   `search_gc_bytes_collected`: The total amount of memory freed by the garbage collectors from indexes in the shard's memory in bytes. <sup>[3](#tnote-3)</sup>\n*   `search_bytes_collected`: The total amount of memory freed by the garbage collectors from indexes in the shard's memory in bytes. Deprecated in 8.0 (renamed `search_gc_bytes_collected`), but still available in older versions. <sup>[1](#tnote-1)</sup>\n*   `search_gc_marked_deleted_vectors`: The number of vectors marked as deleted in the vector indexes that have not yet been cleaned. <sup>[3](#tnote-3)</sup>\n*   `search_marked_deleted_vectors`: The number of vectors marked as deleted in the vector indexes that have not yet been cleaned. Deprecated in 8.0 (renamed `search_gc_marked_delete_vectors`), but still available in older versions. <sup>[1](#tnote-1)</sup>\n*   `search_gc_total_cycles`: The total number of garbage collection cycles executed. <sup>[3](#tnote-3)</sup>\n*   `search_total_cycles`: The total number of garbage collection cycles executed. Deprecated in 8.0 (renamed `search_gc_total_cycles`), but still available in older versions. <sup>[1](#tnote-1)</sup>\n*   `search_gc_total_docs_not_collected_by_gc`: The number of documents marked as deleted, whose memory has not yet been freed by the garbage collector. <sup>[3](#tnote-3)</sup>\n*   `search_total_docs_not_collected_by_gc`: The number of documents marked as deleted, whose memory has not yet been freed by the garbage collector. Deprecated in 8.0 (renamed `search_gc_total_docs_not_collected`), but still available in older versions. <sup>[1](#tnote-1)</sup>\n*   `search_gc_total_ms_run`: The total duration of all garbage collection cycles in the shard, measured in milliseconds. <sup>[3](#tnote-3)</sup>\n*   `search_total_ms_run`: The total duration of all garbage collection cycles in the shard, measured in milliseconds. Deprecated in 8.0 (renamed `search_gc_total_ms_run`), but still available in older versions. <sup>[1](#tnote-1)</sup>\n\n*   `search_cursors_internal_idle`: The total number of coordinator cursors that are currently holding pending results in the shard. <sup>[3](#tnote-3)</sup>\n*   `search_cursors_user_idle`: The total number of cursors that were explicitly requested by users, that are currently holding pending results in the shard. <sup>[3](#tnote-3)</sup>\n*   `search_global_idle`: The total number of user and internal cursors currently holding pending results in the shard. Deprecated in 8.0 (split into `search_cursors_internal_idle` and `search_cursors_user_idle`) but still available in older versions. <sup>[1](#tnote-1)</sup>\n*   `search_cursors_internal_active`: The total number of coordinator cursors in the shard, either holding pending results or actively executing `FT.CURSOR READ`. <sup>[3](#tnote-3)</sup>\n*   `search_cursors_user_active`: The total number of user cursors in the shard, either holding pending results or actively executing `FT.CURSOR READ`. <sup>[3](#tnote-3)</sup>\n*   `search_global_total`: The total number of user and internal cursors in the shard, either holding pending results or actively executing `FT.CURSOR READ`. Deprecated in 8.0 (split into `search_cursors_internal_active` and `search_cursors_user_active`), but still available in older versions. <sup>[1](#tnote-1)</sup>\n*   `search_number_of_indexes`: The total number of indexes in the shard. <sup>[1](#tnote-1)</sup>\n*   `search_number_of_active_indexes`: The total number of indexes running a background indexing and/or background query processing operation. Background indexing refers to vector ingestion process, or in-progress background indexer. <sup>[1](#tnote-1)</sup>\n*   `search_number_of_active_indexes_running_queries`: The total count of indexes currently running a background query process. <sup>[1](#tnote-1)</sup>\n*   `search_number_of_active_indexes_indexing`: The total count of indexes currently undergoing a background indexing process. Background indexing refers to a vector ingestion process, or an in-progress background indexer. This metric is limited by the number of WORKER threads allocated for writing operations plus the number of indexes. <sup>[1](#tnote-1)</sup>\n*   `search_total_active_write_threads`: The total count of background write (indexing) processes currently running in the shard. Background indexing refers to a vector ingestion process, or an in-progress background indexer. This metric is limited by the number of threads allocated for writing operations. <sup>[1](#tnote-1)</sup>\n*   `search_fields_text_Text`: The total number of `TEXT` fields across all indexes in the shard. <sup>[1](#tnote-1)</sup>\n*   `search_fields_text_Sortable`: The total number of `SORTABLE TEXT` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup>\n*   `search_fields_text_NoIndex`: The total number of `NOINDEX TEXT` fields across all indexes in the shard, which are used for sorting only but not indexed. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup>\n*   `search_fields_numeric_Numeric`: The total number of `NUMERIC` fields across all indexes in the shard. <sup>[1](#tnote-1)</sup>\n*   `search_fields_numeric_Sortable`: The total number of `SORTABLE NUMERIC` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup>\n*   `search_fields_numeric_NoIndex`: The total number of `NOINDEX NUMERIC` fields across all indexes in the shard; i.e., used for sorting only but not indexed. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup>\n*   `search_fields_tag_Tag`: The total number of `TAG` fields across all indexes in the shard. <sup>[1](#tnote-1)</sup>\n*   `search_fields_tag_Sortable`: The total number of `SORTABLE TAG` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup>\n*   `search_fields_tag_NoIndex`: The total number of `NOINDEX TAG` fields across all indexes in the shard; i.e., used for sorting only but not indexed. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup>\n*   `search_fields_tag_CaseSensitive`: The total number of `CASESENSITIVE TAG` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup>\n*   `search_fields_geo_Geo`: The total number of `GEO` fields across all indexes in the shard. <sup>[1](#tnote-1)</sup>\n*   `search_fields_geo_Sortable`: The total number of `SORTABLE GEO` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup>\n*   `search_fields_geo_NoIndex`: The total number of `NOINDEX GEO` fields across all indexes in the shard; i.e., used for sorting only but not indexed. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup>\n*   `search_fields_vector_Vector`: The total number of `VECTOR` fields across all indexes in the shard. <sup>[1](#tnote-1)</sup>\n*   `search_fields_vector_Flat`: The total number of `FLAT VECTOR` fields across all indexes in the shard. <sup>[1](#tnote-1)</sup>\n*   `search_fields_vector_HNSW`: The total number of `HNSW VECTOR` fields across all indexes in the shard. <sup>[1](#tnote-1)</sup>\n*   `search_fields_geoshape_Geoshape`: The total number of `GEOSHAPE` fields across all indexes in the shard. <sup>[2](#tnote-2)</sup>\n*   `search_fields_geoshape_Sortable`: The total number of `SORTABLE GEOSHAPE` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[2](#tnote-2)</sup>\n*   `search_fields_geoshape_NoIndex`: The total number of `NOINDEX GEOSHAPE` fields across all indexes in the shard; i.e., used for sorting only but not indexed. This field appears only if its value is larger than 0. <sup>[2](#tnote-2)</sup>\n*   `search_fields_<field>_IndexErrors`: The total number of indexing failures caused by attempts to index a document containing `field` field. <sup>[1](#tnote-1)</sup>\n*   `search_used_memory_vector_index`: The total memory usage of all vector indexes in the shard. <sup>[1](#tnote-1)</sup>\n*   `search_used_memory_indexes`: The estimated total memory allocated by all indexes in the shard in bytes (including vector indexes memory accounted in `search_used_memory_vector_index`). <sup>[1](#tnote-1)</sup>\n*   `search_used_memory_indexes_human`: The estimated total memory allocated by all indexes in the shard in MB. <sup>[1](#tnote-1)</sup>\n*   `search_smallest_memory_index`: The estimated memory usage of the index with the smallest memory usage in the shard in bytes. <sup>[1](#tnote-1)</sup>\n*   `search_smallest_memory_index_human`: The estimated memory usage of the index with the smallest memory usage in the shard in MB. <sup>[1](#tnote-1)</sup>\n*   `search_largest_memory_index`: The estimated memory usage of the index with the largest memory usage in the shard in bytes. <sup>[1](#tnote-1)</sup>\n*   `search_largest_memory_index_human`: The estimated memory usage of the index with the largest memory usage in the shard in MB. <sup>[1](#tnote-1)</sup>\n*   `search_total_indexing_time`: The total time spent on indexing operations, excluding the background indexing of vectors in the HNSW graph. <sup>[1](#tnote-1)</sup>\n*   `search_bytes_collected`: The total amount of memory freed by the garbage collectors from indexes in the shard memory in bytes. <sup>[1](#tnote-1)</sup>\n*   `search_total_cycles`: The total number of garbage collection cycles executed. <sup>[1](#tnote-1)</sup>\n*   `search_total_ms_run`: The total duration of all garbage collection cycles in the shard, measured in milliseconds. <sup>[1](#tnote-1)</sup>\n*   `search_total_docs_not_collected_by_gc`: The number of documents marked as deleted whose memory has not yet been freed by the garbage collector. <sup>[1](#tnote-1)</sup>\n*   `search_marked_deleted_vectors`: The number of vectors marked as deleted in the vector indexes that have not yet been cleaned. <sup>[1](#tnote-1)</sup>\n*   `search_total_queries_processed`: The total number of successful query executions (When using cursors, not counting reading from existing cursors) in the shard. <sup>[1](#tnote-1)</sup>\n*   `search_total_query_commands`: The total number of successful query command executions (including `FT.SEARCH`, `FT.AGGREGATE`, and `FT.CURSOR READ`). <sup>[1](#tnote-1)</sup>\n*   `search_total_query_execution_time_ms`: The cumulative execution time of all query commands, including `FT.SEARCH`, `FT.AGGREGATE`, and `FT.CURSOR` READ, measured in ms. <sup>[1](#tnote-1)</sup>\n*   `search_total_active_queries`: The total number of background queries currently being executed in the shard, excluding `FT.CURSOR READ`. <sup>[1](#tnote-1)</sup>\n*   `search_errors_indexing_failures`: The total number of indexing failures recorded across all indexes in the shard. <sup>[1](#tnote-1)</sup>\n*   `search_errors_for_index_with_max_failures`: The number of indexing failures in the index with the highest count of failures. <sup>[1](#tnote-1)</sup>\n*   `search_OOM_indexing_failures_indexes_count`: The number of indexes whose background indexing process failed due to out-of-memory (OOM) conditions. <sup>[2](#tnote-2)</sup>\n\n1. <a name=\"tnote-1\"></a> Available in RediSearch 2.6.\n2. <a name=\"tnote-2\"></a> Available in RediSearch 2.8.\n3. <a name=\"tnote-3\"></a> Available in RediSearch 8.0.\n\nThe **keyspace** section provides statistics on the main dictionary of each database.\nThe statistics are the number of keys, and the number of keys with an expiration.\n\nFor each database, the following line is added:\n\n*   `dbXXX`: `keys=XXX,expires=XXX,avg_ttl=XXX,subexpiry=XXX`\n\nThe **keysizes** section provides detailed statistics on the distribution of key sizes for each data type (strings, lists, sets, hashes, and sorted sets) within the dataset. The distribution is tracked using a base-2 logarithmic histogram.\n\nHere's sample output from a two-database instance of Redis:\n\n[code example]\n\nThe **debug** section contains experimental metrics, which might change or get removed in future versions.\nIt won't be included when `INFO` or `INFO ALL` are called, and it is returned only when `INFO DEBUG` is used.\n\n*   `eventloop_duration_aof_sum`: Total time spent on flushing AOF in eventloop in microseconds.\n*   `eventloop_duration_cron_sum`: Total time consumption of cron in microseconds (including serverCron and beforeSleep, but excluding IO and AOF flushing).\n*   `eventloop_duration_max`: The maximal time spent in a single eventloop cycle in microseconds.\n*   `eventloop_cmd_per_cycle_max`: The maximal number of commands processed in a single eventloop cycle.\n*   `allocator_allocated_lua`: Total bytes allocated from the allocator specifically for Lua, including internal-fragmentation.\n*   `allocator_active_lua`: Total bytes in the allocator active pages specifically for Lua, including external-fragmentation.\n*   `allocator_resident_lua`: Total bytes resident (RSS) in the allocator specifically for Lua. This includes pages that can be released to the OS (by `MEMORY PURGE`, or just waiting).\n*   `allocator_frag_bytes_lua`: Delta between `allocator_active_lua` and `allocator_allocated_lua`.\n\n**A note about the word slave used in this man page**: Starting with Redis 5, if not for backward compatibility, the Redis project no longer uses the word slave. Unfortunately in this command the word slave is part of the protocol, so we'll be able to remove such occurrences only when this API will be naturally deprecated.\n\n**Modules generated sections**: Starting with Redis 6, modules can inject their information into the `INFO` command. These are excluded by default even when the `all` argument is provided (it will include a list of loaded modules but not their generated info fields). To get these you must use either the `modules` argument or `everything`."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | In Redis Software, `INFO` returns a different set of fields than Redis Open Source.<br />Not supported for [scripts](https://redis.io/docs/latest/develop/programmability). |\n\nNote: key memory usage is different on Redis Software or Redis Cloud active-active databases than on non-active-active databases. This is because memory usage includes some amount of CRDB overhead.\n\nAdditionally, for JSON keys, Redis implements a \"shared string\" mechanism to save memory when the same JSON field names or field values of type string are used more than once (either inter-key or intra-key).\nIn such cases, instead of storing the field names or values many times, Redis stores them only once. This mechanism is not in place for active-active databases.\n\nOn non-active-active databases, `INFO` (Memory > used_memory) reports that the shared memory is counted, but only once for all keys. On active-active databases, there is no shared memory, so if strings are repeated, they are stored multiple times.\n\n**Example**\n\nSuppose you have ten JSON keys, and each key has 5KB of unique content and 5KB of shared content.\n\nFor non-active-active databases, `INFO` (used_memory) would report (10 keys * 5KB) + 5KB ~= 55KB. The last term, \"+ 5KB\", is the size of the shared content.\n\nFor active-active databases, `INFO` (used_memory) would report 10 keys * 20KB ~= 200KB. This number includes some amount of CRDB overhead per JSON key."},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): a map of info fields, one field per line in the form of `<field>:<value>` where the value can be a comma separated map like `<key>=<val>`. Also contains section header lines starting with `#` and blank lines.\nLines can contain a section name (starting with a `#` character) or a property. All the properties are in the form of `field:value` terminated by `\\r\\n`.\n\n**RESP3:**\n\n[Verbatim string reply](../../develop/reference/protocol-spec#verbatim-strings): a map of info fields, one field per line in the form of `<field>:<value>` where the value can be a comma separated map like `<key>=<val>`. Also contains section header lines starting with `#` and blank lines.\nLines can contain a section name (starting with a `#` character) or a property. All the properties are in the form of `field:value` terminated by `\\r\\n`."}],"examples":[{"id":"details-ex0","language":"plaintext","code":"# Keysizes\ndb0_distrib_strings_sizes:2=1,4=1,8=1,16=1,32=1,64=1,128=1,256=1,512=1,1K=1,2K=1,4K=1,8K=1,16K=1,32K=1,64K=1,128K=1,256K=1,512K=1,1M=1\ndb0_distrib_lists_items:1=1,2=1,4=1,8=1,16=1,32=1,64=1,128=1,256=1,512=1,1K=1,2K=1,4K=1,8K=1,16K=1,32K=1,64K=1\ndb0_distrib_sets_items:1=1,2=1,4=1,8=1,16=1,32=1,64=1,128=1,256=1,512=1,1K=1,2K=1,4K=1,8K=1,16K=1,32K=1,64K=1\ndb0_distrib_zsets_items:1=1,2=1,4=1,8=1,16=1,32=1,64=1,128=1,256=1,512=1,1K=1,2K=1,4K=1,8K=1,16K=1,32K=1,64K=1\ndb0_distrib_hashes_items:1=1,2=1,4=1,8=1,16=1,32=1,64=1,128=1,256=1,512=1,1K=1,2K=1,4K=1,8K=1,16K=1,32K=1\ndb1_distrib_strings_sizes:2=1,4=1,8=1,16=1,32=1,64=1,128=1,256=1,512=1,1K=1,2K=1,4K=1,8K=1,16K=1,32K=1,64K=1,128K=1,256K=1,512K=1,1M=1\ndb1_distrib_lists_items:1=1,2=1,4=1,8=1,16=1,32=1,64=1,128=1,256=1,512=1,1K=1,2K=1,4K=1,8K=1,16K=1,32K=1,64K=1\ndb1_distrib_sets_items:1=1,2=1,4=1,8=1,16=1,32=1,64=1,128=1,256=1,512=1,1K=1,2K=1,4K=1,8K=1,16K=1,32K=1,64K=1\ndb1_distrib_zsets_items:1=1,2=1,4=1,8=1,16=1,32=1,64=1,128=1,256=1,512=1,1K=1,2K=1,4K=1,8K=1,16K=1,32K=1,64K=1\ndb1_distrib_hashes_items:1=1,2=1,4=1,8=1,16=1,32=1,64=1,128=1,256=1,512=1,1K=1,2K=1,4K=1,8K=1,16K=1,32K=1","section_id":"details"}]}
{"schema_version":2,"id":"commands/json.arrappend","title":"JSON.ARRAPPEND","url":"https://redis.io/docs/latest/commands/json.arrappend/","summary":"Append one or more JSON values into the array at path after the last element in it.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"3b25c481fc0aabaac98e979e9c9ce2b7673602080b34fab0a667492a8c0b48f5","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Append the JSON values into the array at `path` after the last element in it.\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary> \n\nis the key to modify.\n</details>\n\n<details open><summary><code>path</code></summary>\n\nis the JSONPath to match.\n</details>\n\n<details open><summary><code>value</code></summary> \n\nis one or more values to append to one or more arrays. \n\n> [!NOTE] About using strings with JSON commands\n> To specify a string as an array value to append, wrap the quoted string with an additional set of single quotes. Example: `'\"silver\"'`. For more detailed use, see [Examples](#examples).\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Add a new color to a list of product colors</b></summary>\n\nCreate a document for noise-cancelling headphones in black and silver colors.\n\n\nredis> JSON.SET item:1 $ '{\"name\":\"Noise-cancelling Bluetooth headphones\",\"description\":\"Wireless Bluetooth headphones with noise-cancelling technology\",\"connection\":{\"wireless\":true,\"type\":\"Bluetooth\"},\"price\":99.98,\"stock\":25,\"colors\":[\"black\",\"silver\"]}'\nOK\n\n\nAdd `blue` to the end of the `colors` array. `JSON.ARRAPPEND` returns the new length of the `colors` array.\n\n\nredis> JSON.ARRAPPEND item:1 $.colors '\"blue\"'\n1) (integer) 3\n\n\nGet the updated value of the `colors` array.\n\n\nredis> JSON.GET item:1 $.colors\n\"[[\\\"black\\\",\\\"silver\\\",\\\"blue\\\"]]\"\n\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\"><nobr>&#x2705; Flexible & Annual</nobr></span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nWith `$`-based path argument: [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [integer replies](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) or [null replies](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls), where each element is the array's new length, or `null` if the matching value is not an array.\n\nWith `.`-based path argument: [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) representing the array's new length, or [null reply](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls) if the matching value is not an array.\n\n**RESP3:**\n\nWith `$`-based path argument (default): [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [integer replies](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) or [null replies](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls), where each element is the array's new length, or `null` if the matching value is not an array.\n\nWith `.`-based path argument: [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) representing the array's new length, or [null reply](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls) if the matching value is not an array."},{"id":"see-also","title":"See also","role":"related","text":"[`JSON.ARRINDEX`](https://redis.io/docs/latest/commands/json.arrindex) | [`JSON.ARRINSERT`](https://redis.io/docs/latest/commands/json.arrinsert)"},{"id":"related-topics","title":"Related topics","role":"related","text":"* [RedisJSON](https://redis.io/docs/latest/develop/data-types/json)\n* [Index and search JSON documents](https://redis.io/docs/latest/develop/ai/search-and-query/indexing)"}],"examples":[]}
{"schema_version":2,"id":"commands/json.arrindex","title":"JSON.ARRINDEX","url":"https://redis.io/docs/latest/commands/json.arrindex/","summary":"Returns the index of the first occurrence of a JSON scalar value in the array at path","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"5052db9f25b4754f493f9e8b74663f854792acae167b0b306ae948df1a4e1d00","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Search for the first occurrence of a JSON value in an array\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary> \n\nis key to parse.\n</details>\n\n<details open><summary><code>path</code></summary> \n\nis JSONPath to specify.\n</details>\n\n<details open><summary><code>value</code></summary> \n\nis value to find its index in one or more arrays. \n\n> [!NOTE] About using strings with JSON commands\n> To specify a string as an array value to index, wrap the quoted string with an additional set of single quotes. Example: `'\"silver\"'`. For more detailed use, see [Examples](#examples).\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>start</code></summary> \n\nis inclusive start value to specify in a slice of the array to search. Default is `0`. \n</details>\n\n<details open><summary><code>stop</code></summary> \n\nis exclusive stop value to specify in a slice of the array to search, including the last element. Default is `0`. Negative values are interpreted as starting from the end.\n</details>\n\n> [!NOTE] About out-of-range indexes\n> Out-of-range indexes round to the array's start and end. An inverse index range (such as the range from 1 to 0) returns unfound or `-1`."},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Find the specific place of a color in a list of product colors</b></summary>\n\nCreate a document for noise-cancelling headphones in black and silver colors.\n\n\nredis> JSON.SET item:1 $ '{\"name\":\"Noise-cancelling Bluetooth headphones\",\"description\":\"Wireless Bluetooth headphones with noise-cancelling technology\",\"connection\":{\"wireless\":true,\"type\":\"Bluetooth\"},\"price\":99.98,\"stock\":25,\"colors\":[\"black\",\"silver\"]}'\nOK\n\n\nAdd color `blue` to the end of the `colors` array. `JSON.ARRAPEND` returns the array's new size.\n\n\nredis> JSON.ARRAPPEND item:1 $.colors '\"blue\"'\n1) (integer) 3\n\n\nReturn the new length of the `colors` array.\n\n\nredis> JSON.GET item:1\n\"{\\\"name\\\":\\\"Noise-cancelling Bluetooth headphones\\\",\\\"description\\\":\\\"Wireless Bluetooth headphones with noise-cancelling technology\\\",\\\"connection\\\":{\\\"wireless\\\":true,\\\"type\\\":\\\"Bluetooth\\\"},\\\"price\\\":99.98,\\\"stock\\\":25,\\\"colors\\\":[\\\"black\\\",\\\"silver\\\",\\\"blue\\\"]}\"\n\n\nGet the list of colors for the product.\n\n\nredis> JSON.GET item:1 '$.colors[*]'\n\"[\\\"black\\\",\\\"silver\\\",\\\"blue\\\"]\"\n\n\nInsert two more colors after the second color. You now have five colors.\n\n\nredis> JSON.ARRINSERT item:1 $.colors 2 '\"yellow\"' '\"gold\"'\n1) (integer) 5\n\n\nGet the updated list of colors.\n\n\nredis> JSON.GET item:1 $.colors\n\"[[\\\"black\\\",\\\"silver\\\",\\\"yellow\\\",\\\"gold\\\",\\\"blue\\\"]]\"\n\n\nFind the place where color `silver` is located.\n\n\nredis> JSON.ARRINDEX item:1 $..colors '\"silver\"'\n1) (integer) 1\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nWith `$`-based path argument: [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [integer replies](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) or [null replies](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls), where each element is the first position in the array, `-1` if unfound, or `null` if the matching value is not an array.\n\nWith `.`-based path argument: [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) representing the first position in the array, `-1` if unfound, or [null reply](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls) if the matching value is not an array.\n\n**RESP3:**\n\nWith `$`-based path argument (default): [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [integer replies](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) or [null replies](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls), where each element is the first position in the array, `-1` if unfound, or `null` if the matching value is not an array.\n\nWith `.`-based path argument: [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) representing the first position in the array, `-1` if unfound, or [null reply](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls) if the matching value is not an array."},{"id":"see-also","title":"See also","role":"related","text":"[`JSON.ARRAPPEND`](https://redis.io/docs/latest/commands/json.arrappend) | [`JSON.ARRINSERT`](https://redis.io/docs/latest/commands/json.arrinsert)"},{"id":"related-topics","title":"Related topics","role":"related","text":"* [RedisJSON](https://redis.io/docs/latest/develop/data-types/json)\n* [Index and search JSON documents](https://redis.io/docs/latest/develop/ai/search-and-query/indexing)"}],"examples":[]}
{"schema_version":2,"id":"commands/json.arrinsert","title":"JSON.ARRINSERT","url":"https://redis.io/docs/latest/commands/json.arrinsert/","summary":"Inserts the JSON scalar(s) value at the specified index in the array at path","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"6278125fa2e7340a0673c7d4dbc8fcfa438b5c755b3ea72da7b4e74db85b0046","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Insert the `json` values into the array at `path` before the `index` (shifts to the right)\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary> \n\nis key to modify.\n</details>\n\n<details open><summary><code>value</code></summary> \n\nis one or more values to insert in one or more arrays. \n\n> [!NOTE] About using strings with JSON commands\n> To specify a string as an array value to insert, wrap the quoted string with an additional set of single quotes. Example: `'\"silver\"'`. For more detailed use, see [Examples](#examples).\n</details>\n\n<details open><summary><code>index</code></summary> \n\nis position in the array where you want to insert a value. The index must be in the array's range. Inserting at `index` 0 prepends to the array. Negative index values start from the end of the array.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>path</code></summary> \n\nis JSONPath to specify. Default is root `$`.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Add new colors to a specific place in a list of product colors</b></summary>\n\nCreate a document for noise-cancelling headphones in black and silver colors.\n\n\nredis> JSON.SET item:1 $ '{\"name\":\"Noise-cancelling Bluetooth headphones\",\"description\":\"Wireless Bluetooth headphones with noise-cancelling technology\",\"connection\":{\"wireless\":true,\"type\":\"Bluetooth\"},\"price\":99.98,\"stock\":25,\"colors\":[\"black\",\"silver\"]}'\nOK\n\n\nAdd color `blue` to the end of the `colors` array. `JSON.ARRAPEND` returns the array's new size.\n\n\nredis> JSON.ARRAPPEND item:1 $.colors '\"blue\"'\n1) (integer) 3\n\n\nReturn the new length of the `colors` array.\n\n\nredis> JSON.GET item:1\n\"{\\\"name\\\":\\\"Noise-cancelling Bluetooth headphones\\\",\\\"description\\\":\\\"Wireless Bluetooth headphones with noise-cancelling technology\\\",\\\"connection\\\":{\\\"wireless\\\":true,\\\"type\\\":\\\"Bluetooth\\\"},\\\"price\\\":99.98,\\\"stock\\\":25,\\\"colors\\\":[\\\"black\\\",\\\"silver\\\",\\\"blue\\\"]}\"\n\n\nGet the list of colors for the product.\n\n\nredis> JSON.GET item:1 '$.colors[*]'\n\"[\\\"black\\\",\\\"silver\\\",\\\"blue\\\"]\"\n\n\nInsert two more colors after the second color. You now have five colors.\n\n\nredis> JSON.ARRINSERT item:1 $.colors 2 '\"yellow\"' '\"gold\"'\n1) (integer) 5\n\n\nGet the updated list of colors.\n\n\nredis> JSON.GET item:1 $.colors\n\"[[\\\"black\\\",\\\"silver\\\",\\\"yellow\\\",\\\"gold\\\",\\\"blue\\\"]]\"\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nWith `$`-based path argument: [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [integer replies](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) or [null replies](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls), where each element is the array's new size, or `null` if the matching value is not an array.\n\nWith `.`-based path argument: [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) representing the array's new size, or [null reply](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls) if the matching value is not an array.\n\n**RESP3:**\n\nWith `$`-based path argument (default): [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [integer replies](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) or [null replies](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls), where each element is the array's new size, or `null` if the matching value is not an array.\n\nWith `.`-based path argument: [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) representing the array's new size, or [null reply](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls) if the matching value is not an array."},{"id":"see-also","title":"See also","role":"related","text":"[`JSON.ARRAPPEND`](https://redis.io/docs/latest/commands/json.arrappend) | [`JSON.ARRINDEX`](https://redis.io/docs/latest/commands/json.arrindex)"},{"id":"related-topics","title":"Related topics","role":"related","text":"* [RedisJSON](https://redis.io/docs/latest/develop/data-types/json)\n* [Index and search JSON documents](https://redis.io/docs/latest/develop/ai/search-and-query/indexing)"}],"examples":[]}
{"schema_version":2,"id":"commands/json.arrlen","title":"JSON.ARRLEN","url":"https://redis.io/docs/latest/commands/json.arrlen/","summary":"Returns the length of the array at path","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"fe55589500afcffb27816e2e64a3647ef7768dd4426ceba7a5c962619541a873","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Report the length of the JSON array at `path` in `key`\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary> \n\nis key to parse.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>path</code></summary> \n\nis JSONPath to specify. Default is root `$`, if not provided. Returns null if the `key` or `path` do not exist.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Get lengths of JSON arrays in a document</b></summary>\n\nCreate a document for wireless earbuds.\n\n\nredis> JSON.SET item:2 $ '{\"name\":\"Wireless earbuds\",\"description\":\"Wireless Bluetooth in-ear headphones\",\"connection\":{\"wireless\":true,\"type\":\"Bluetooth\"},\"price\":64.99,\"stock\":17,\"colors\":[\"black\",\"white\"], \"max_level\":[80, 100, 120]}'\nOK\n\n\nFind lengths of arrays in all objects of the document.\n\n\nredis> JSON.ARRLEN item:2 '$.[*]'\n1) (nil)\n2) (nil)\n3) (nil)\n4) (nil)\n5) (nil)\n6) (integer) 2\n7) (integer) 3\n\n\nReturn the length of the `max_level` array.\n\n\nredis> JSON.ARRLEN item:2 '$..max_level'\n1) (integer) 3\n\n\nGet all the maximum level values.\n\n\nredis> JSON.GET item:2 '$..max_level'\n\"[[80,100,120]]\"\n\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nWith `$`-based path argument: [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [integer replies](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) or [null replies](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls), where each element is the array length, or `null` if the matching value is not an array.\n\nWith `.`-based path argument: [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) representing the array length, or [null reply](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls) if the matching value is not an array.\n\n**RESP3:**\n\nWith `$`-based path argument (default): [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [integer replies](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) or [null replies](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls), where each element is the array length, or `null` if the matching value is not an array.\n\nWith `.`-based path argument: [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) representing the array length, or [null reply](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls) if the matching value is not an array."},{"id":"see-also","title":"See also","role":"related","text":"[`JSON.ARRINDEX`](https://redis.io/docs/latest/commands/json.arrindex) | [`JSON.ARRINSERT`](https://redis.io/docs/latest/commands/json.arrinsert)"},{"id":"related-topics","title":"Related topics","role":"related","text":"* [RedisJSON](https://redis.io/docs/latest/develop/data-types/json)\n* [Index and search JSON documents](https://redis.io/docs/latest/develop/ai/search-and-query/indexing)"}],"examples":[]}
{"schema_version":2,"id":"commands/json.arrpop","title":"JSON.ARRPOP","url":"https://redis.io/docs/latest/commands/json.arrpop/","summary":"Removes and returns the element at the specified index in the array at path","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"d2039788602f4e81a7dbeed9d97b095ec6c5fab7633d1b228b190023a5344c3d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Remove and return an element from the index in the array\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary> \n\nis key to modify.\n</details>\n\n<details open><summary><code>index</code></summary> \n\nis position in the array to start popping from. Default is `-1`, meaning the last element. Out-of-range indexes round to their respective array ends. Popping an empty array returns null.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>path</code></summary> \n\nis JSONPath to specify. Default is root `$`.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Pop a value from an index and insert a new value</b></summary>\n\nCreate two headphone products with maximum sound levels.\n\n\nredis> JSON.SET key $ '[{\"name\":\"Healthy headphones\",\"description\":\"Wireless Bluetooth headphones with noise-cancelling technology\",\"connection\":{\"wireless\":true,\"type\":\"Bluetooth\"},\"price\":99.98,\"stock\":25,\"colors\":[\"black\",\"silver\"],\"max_level\":[60,70,80]},{\"name\":\"Noisy headphones\",\"description\":\"Wireless Bluetooth headphones with noise-cancelling technology\",\"connection\":{\"wireless\":true,\"type\":\"Bluetooth\"},\"price\":99.98,\"stock\":25,\"colors\":[\"black\",\"silver\"],\"max_level\":[80,90,100,120]}]'\nOK\n\n\nGet all maximum values for the second product.\n\n\nredis> JSON.GET key $.[1].max_level\n\"[[80,90,100,120]]\"\n\n\nUpdate the `max_level` field of the product: remove an unavailable value and add a newly available value.\n\n\nredis> JSON.ARRPOP key $.[1].max_level 0\n1) \"80\"\n\n\nGet the updated array.\n\n\nredis> JSON.GET key $.[1].max_level\n\"[[90,100,120]]\"\n\n\nNow insert a new lowest value.\n\n\nredis> JSON.ARRINSERT key $.[1].max_level 0 85\n1) (integer) 4\n\n\nGet the updated array.\n\n\nredis> JSON.GET key $.[1].max_level\n\"[[85,90,100,120]]\"\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [bulk string replies](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) or [null replies](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls), where each element is the popped JSON value as a string, or `null` if the matching value is not an array.\n\n**RESP3:**\n\n[Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [bulk string replies](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) or [null replies](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls), where each element is the popped JSON value as a string, or `null` if the matching value is not an array."},{"id":"see-also","title":"See also","role":"related","text":"[`JSON.ARRAPPEND`](https://redis.io/docs/latest/commands/json.arrappend) | [`JSON.ARRINDEX`](https://redis.io/docs/latest/commands/json.arrindex)"},{"id":"related-topics","title":"Related topics","role":"related","text":"* [RedisJSON](https://redis.io/docs/latest/develop/data-types/json)\n* [Index and search JSON documents](https://redis.io/docs/latest/develop/ai/search-and-query/indexing)"}],"examples":[]}
{"schema_version":2,"id":"commands/json.arrtrim","title":"JSON.ARRTRIM","url":"https://redis.io/docs/latest/commands/json.arrtrim/","summary":"Trims the array at path to contain only the specified inclusive range of indices from start to stop","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"63d023c36be087d1afd402333cf83e4cbf8d80eee9ef8d7ef242e7fa84fdde4d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Trim an array so that it contains only the specified inclusive range of elements\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary> \n\nis key to modify.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>path</code></summary> \n\nis JSONPath to specify. Default is root `$`.\n</details>\n\n<details open><summary><code>start</code></summary> \n\nis index of the first element to keep (previous elements are trimmed). Default is 0. \n</details>\n\n<details open><summary><code>stop</code></summary> \n\nis the index of the last element to keep (following elements are trimmed), including the last element. Default is 0. Negative values are interpreted as starting from the end.\n</details>\n\n> [!NOTE] About out-of-range indexes\n> JSON.ARRTRIM is extremely forgiving, and using it with out-of-range indexes does not produce an error. Note a few differences between how RedisJSON v2.0 and legacy versions handle out-of-range indexes.\n>\n> Behavior as of RedisJSON v2.0:\n>\n> * If `start` is larger than the array's size or `start` > `stop`, returns 0 and an empty array. \n> * If `start` is < 0, then start from the end of the array.\n> * If `stop` is larger than the end of the array, it is treated like the last element."},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Trim an array to a specific set of values</b></summary>\n\nCreate two headphone products with maximum sound levels.\n\n\nredis> JSON.SET key $\n\"[{\\\"name\\\":\\\"Healthy headphones\\\",\\\"description\\\":\\\"Wireless Bluetooth headphones with noise-cancelling technology\\\",\\\"connection\\\":{\\\"wireless\\\":true,\\\"type\\\":\\\"Bluetooth\\\"},\\\"price\\\":99.98,\\\"stock\\\":25,\\\"colors\\\":[\\\"black\\\",\\\"silver\\\"],\\\"max_level\\\":[60,70,80]},{\\\"name\\\":\\\"Noisy headphones\\\",\\\"description\\\":\\\"Wireless Bluetooth headphones with noise-cancelling technology\\\",\\\"connection\\\":{\\\"wireless\\\":true,\\\"type\\\":\\\"Bluetooth\\\"},\\\"price\\\":99.98,\\\"stock\\\":25,\\\"colors\\\":[\\\"black\\\",\\\"silver\\\"],\\\"max_level\\\":[85,90,100,120]}]\"\nOK\n\n\nAdd new sound level values to the second product.\n\n\nredis> JSON.ARRAPPEND key $.[1].max_level 140 160 180 200 220 240 260 280\n1) (integer) 12\n\n\nGet the updated array.\n\n\nredis> JSON.GET key $.[1].max_level\n\"[[85,90,100,120,140,160,180,200,220,240,260,280]]\"\n\n\nKeep only the values between the fifth and the ninth element, inclusive of that last element.\n\n\nredis> JSON.ARRTRIM key $.[1].max_level 4 8\n1) (integer) 5\n\n\nGet the updated array.\n\n\nredis> JSON.GET key $.[1].max_level\n\"[[140,160,180,200,220]]\"\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nWith `$`-based path argument: [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [integer replies](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) or [null replies](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls), where each element is the array's new size, or `null` if the matching value is not an array.\n\nWith `.`-based path argument: [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) representing the array's new size, or [null reply](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls) if the matching value is not an array.\n\n**RESP3:**\n\nWith `$`-based path argument (default): [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [integer replies](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) or [null replies](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls), where each element is the array's new size, or `null` if the matching value is not an array.\n\nWith `.`-based path argument: [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) representing the array's new size, or [null reply](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls) if the matching value is not an array."},{"id":"see-also","title":"See also","role":"related","text":"[`JSON.ARRINDEX`](https://redis.io/docs/latest/commands/json.arrindex) | [`JSON.ARRINSERT`](https://redis.io/docs/latest/commands/json.arrinsert)"},{"id":"related-topics","title":"Related topics","role":"related","text":"* [RedisJSON](https://redis.io/docs/latest/develop/data-types/json)\n* [Index and search JSON documents](https://redis.io/docs/latest/develop/ai/search-and-query/indexing)"}],"examples":[]}
{"schema_version":2,"id":"commands/json.clear","title":"JSON.CLEAR","url":"https://redis.io/docs/latest/commands/json.clear/","summary":"Clears all values from an array or an object and sets numeric values to `0`","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"3f41cd5d53b430d593046da06a2b050e148e9524fd6b51017016ad7da7242bab","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Clear container values (arrays/objects) and set numeric values to `0`\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary> \n\nis key to parse.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>path</code></summary> \n\nis JSONPath to specify. Default is root `$`. Nonexisting paths are ignored.\n</details>\n\n> [!NOTE]\n>  \n> Already cleared values are ignored for empty containers and zero numbers."},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Clear container values and set numeric values to <code>0</code></b></summary>\n\nCreate a JSON document.\n\n\nredis> JSON.SET doc $ '{\"obj\":{\"a\":1, \"b\":2}, \"arr\":[1,2,3], \"str\": \"foo\", \"bool\": true, \"int\": 42, \"float\": 3.14}'\nOK\n\n\nClear all container values. This returns the number of objects with cleared values.\n\n\nredis> JSON.CLEAR doc $.*\n(integer) 4\n\n\nGet the updated document. Note that numeric values have been set to `0`.\n\n\nredis> JSON.GET doc $\n\"[{\\\"obj\\\":{},\\\"arr\\\":[],\\\"str\\\":\\\"foo\\\",\\\"bool\\\":true,\\\"int\\\":0,\\\"float\\\":0}]\"\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): the number of matching JSON arrays and objects cleared plus the number of matching JSON numerical values zeroed.\n\n**RESP3:**\n\n[Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): the number of matching JSON arrays and objects cleared plus the number of matching JSON numerical values zeroed."},{"id":"see-also","title":"See also","role":"related","text":"[`JSON.ARRINDEX`](https://redis.io/docs/latest/commands/json.arrindex) | [`JSON.ARRINSERT`](https://redis.io/docs/latest/commands/json.arrinsert)"},{"id":"related-topics","title":"Related topics","role":"related","text":"* [RedisJSON](https://redis.io/docs/latest/develop/data-types/json)\n* [Index and search JSON documents](https://redis.io/docs/latest/develop/ai/search-and-query/indexing)"}],"examples":[]}
{"schema_version":2,"id":"commands/json.debug","title":"JSON.DEBUG","url":"https://redis.io/docs/latest/commands/json.debug/","summary":"Debugging container command","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2025-12-16T13:45:58-08:00","page_type":"content","content_hash":"b4880f55b9d8cc7d1f4dfa8be20369f72eac60eaa84d3ac93156afea71ee5225","sections":[{"id":"content","title":"Content","role":"content","text":"This is a container command for debugging related tasks."}],"examples":[]}
{"schema_version":2,"id":"commands/json.debug-help","title":"JSON.DEBUG HELP","url":"https://redis.io/docs/latest/commands/json.debug-help/","summary":"Shows helpful information","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"47d758a4366082f766e3f0fd052bcb97b3796563c4ea5fa7fe238a237c4f26fb","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Return helpful information about the [`JSON.DEBUG`](https://redis.io/docs/latest/commands/json.debug) command"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [bulk string replies](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) containing helpful messages about the JSON.DEBUG command.\n\n**RESP3:**\n\n[Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [bulk string replies](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) containing helpful messages about the JSON.DEBUG command."},{"id":"see-also","title":"See also","role":"related","text":"[`JSON.DEBUG`](https://redis.io/docs/latest/commands/json.debug)"},{"id":"related-topics","title":"Related topics","role":"related","text":"* [RedisJSON](https://redis.io/docs/latest/develop/data-types/json)\n* [Index and search JSON documents](https://redis.io/docs/latest/develop/ai/search-and-query/indexing)"}],"examples":[]}
{"schema_version":2,"id":"commands/json.debug-memory","title":"JSON.DEBUG MEMORY","url":"https://redis.io/docs/latest/commands/json.debug-memory/","summary":"Reports the size in bytes of a key","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"2adb76a00a9273bbcd49853d7c75510d8411c7ed268708de1ec326e7b0846442","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Report a value's memory usage in bytes \n\n> [!WARNING]\n> The actual total memory consumption by a key could be much lower than the value reported by this command because of an internal JSON string reuse mechanism. For more information, see the [JSON memory usage page](https://redis.io/docs/latest/develop/data-types/json/ram#json-string-reuse-mechanism).\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary> \n\nis key to parse.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>path</code></summary> \n\nis JSONPath to specify. Default is root `$`. \n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Report a value's memory usage in bytes</b></summary>\n\nCreate a JSON document.\n\n\nredis> JSON.SET item:2 $ '{\"name\":\"Wireless earbuds\",\"description\":\"Wireless Bluetooth in-ear headphones\",\"connection\":{\"wireless\":true,\"type\":\"Bluetooth\"},\"price\":64.99,\"stock\":17,\"colors\":[\"black\",\"white\"], \"max_level\":[80, 100, 120]}'\nOK\n\n\nGet the values' memory usage in bytes.\n\n\nredis> JSON.DEBUG MEMORY item:2\n(integer) 573\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): the value size in bytes.\n\n**RESP3:**\n\n[Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): the value size in bytes."},{"id":"see-also","title":"See also","role":"related","text":"[`JSON.SET`](https://redis.io/docs/latest/commands/json.set) | [`JSON.ARRLEN`](https://redis.io/docs/latest/commands/json.arrlen)"},{"id":"related-topics","title":"Related topics","role":"related","text":"* [RedisJSON](https://redis.io/docs/latest/develop/data-types/json)\n* [Index and search JSON documents](https://redis.io/docs/latest/develop/ai/search-and-query/indexing)"}],"examples":[]}
{"schema_version":2,"id":"commands/json.del","title":"JSON.DEL","url":"https://redis.io/docs/latest/commands/json.del/","summary":"Deletes a value","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"8af17534d1ae653083fd73b776201d7bb11ee4de14839ce7c9ac7424d8518706","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Delete a value\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary> \n\nis key to modify.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>path</code></summary> \n\nis JSONPath to specify. Default is root `$`. Nonexisting paths are ignored.\n\n> [!NOTE]\n>  \n> Deleting an object's root is equivalent to deleting the key from Redis.\n> If `JSON.DEL` deletes a value and leaves the root object or array empty,\n> the key is also deleted from Redis.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Delete a value</b></summary>\n\nCreate a JSON document.\n\n\nredis> JSON.SET doc $ '{\"a\": 1, \"nested\": {\"a\": 2, \"b\": 3}}'\nOK\n\n\nDelete specified values.\n\n\nredis> JSON.DEL doc $..a\n(integer) 2\n\n\nGet the updated document.\n\n\nredis> JSON.GET doc $\n\"[{\\\"nested\\\":{\\\"b\\\":3}}]\"\n\n\nDelete the last value from the root object.\n\n\nredis> JSON.SET doc $ '{\"a\": 1}'\nOK\nredis> JSON.DEL doc $.a\n(integer) 1\nredis> EXISTS doc\n(integer) 0\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): the number of paths deleted (0 or more).\n\n**RESP3:**\n\n[Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): the number of paths deleted (0 or more)."},{"id":"see-also","title":"See also","role":"related","text":"[`JSON.SET`](https://redis.io/docs/latest/commands/json.set) | [`JSON.ARRLEN`](https://redis.io/docs/latest/commands/json.arrlen)"},{"id":"related-topics","title":"Related topics","role":"related","text":"* [RedisJSON](https://redis.io/docs/latest/develop/data-types/json)\n* [Index and search JSON documents](https://redis.io/docs/latest/develop/ai/search-and-query/indexing)"}],"examples":[]}
{"schema_version":2,"id":"commands/json.forget","title":"JSON.FORGET","url":"https://redis.io/docs/latest/commands/json.forget/","summary":"Deletes a value","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"fb1d5c069a142b3917818d1fc93b08cad68f380760d9524b0ffa11fd24c77a83","sections":[{"id":"overview","title":"Overview","role":"overview","text":"See [`JSON.DEL`](https://redis.io/docs/latest/commands/json.del)."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): the number of paths deleted (0 or more).\n\n**RESP3:**\n\n[Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): the number of paths deleted (0 or more)."}],"examples":[]}
{"schema_version":2,"id":"commands/json.get","title":"JSON.GET","url":"https://redis.io/docs/latest/commands/json.get/","summary":"Gets the value at one or more paths in JSON serialized form","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"6a820e129a04667973453f023a17d19ccc32f5b2830f976260ebe680a6774f28","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Return the value at `path` in JSON serialized form\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary> \n\nis key to parse.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>path</code></summary> \n\nis JSONPath to specify. Default is root `$`. JSON.GET accepts multiple `path` arguments.\n\n> [!NOTE]\n> When using a single JSONPath, the root of the matching values is a JSON string with a top-level **array** of serialized JSON value. \n> In contrast, a legacy path returns a single value.\n>\n> When using multiple JSONPath arguments, the root of the matching values is a JSON string with a top-level **object**, with each object value being a top-level array of serialized JSON value.\n> In contrast, if all paths are legacy paths, each object value is a single serialized JSON value.\n> If there are multiple paths that include both legacy path and JSONPath, the returned value conforms to the JSONPath version (an array of values).\n\n</details>\n\n<details open><summary><code>INDENT</code></summary> \n\nsets the indentation string for nested levels.\n</details>\n\n<details open><summary><code>NEWLINE</code></summary> \n\nsets the string that's printed at the end of each line.\n</details>\n\n<details open><summary><code>SPACE</code></summary> \n\nsets the string that's put between a key and a value.\n</details>\n \nProduce pretty-formatted JSON with `redis-cli` by following this example:\n\n\n~/$ redis-cli --raw\nredis> JSON.GET myjsonkey INDENT \"\\t\" NEWLINE \"\\n\" SPACE \" \" path.to.value[1]"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Return the value at <code>path</code> in JSON serialized form</b></summary>\n\nCreate a JSON document.\n\n\nredis> JSON.SET doc $ '{\"a\":2, \"b\": 3, \"nested\": {\"a\": 4, \"b\": null}}'\nOK\n\n\nWith a single JSONPath (JSON array bulk string):\n\n\nredis>  JSON.GET doc $..b\n\"[3,null]\"\n\n\nUsing multiple paths with at least one JSONPath returns a JSON string with a top-level object with an array of JSON values per path:\n\n\nredis> JSON.GET doc ..a $..b\n\"{\\\"$..b\\\":[3,null],\\\"..a\\\":[2,4]}\"\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings): a JSON-encoded string representing the value(s) at the specified path(s).\n\nWith a single path, returns the JSON serialization of the value at that path.\nWith multiple paths, returns a JSON object where each key is a path and each value is an array of JSON serializations.\n\n**RESP3:**\n\n[Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings): a JSON-encoded string with a top-level array containing the value(s) at the specified path(s).\n\nWith a single path using `$` (default in RESP3), returns a JSON array containing the serialized value.\nWith multiple paths, returns a JSON object where each key is a path and each value is an array of JSON serializations."},{"id":"see-also","title":"See also","role":"related","text":"[`JSON.SET`](https://redis.io/docs/latest/commands/json.set) | [`JSON.MGET`](https://redis.io/docs/latest/commands/json.mget)"},{"id":"related-topics","title":"Related topics","role":"related","text":"* [RedisJSON](https://redis.io/docs/latest/develop/data-types/json)\n* [Index and search JSON documents](https://redis.io/docs/latest/develop/ai/search-and-query/indexing)"}],"examples":[]}
{"schema_version":2,"id":"commands/json.merge","title":"JSON.MERGE","url":"https://redis.io/docs/latest/commands/json.merge/","summary":"Merges a given JSON value into matching paths. Consequently, JSON values at matching paths are updated, deleted, or expanded with new children","since":"2.6.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"83d830e9137ff577bb607642fafdb4bc803ce5130fec2379b6e6c56e5a5b1a67","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Merge a given JSON value into matching paths. Consequently, JSON values at matching paths are updated, deleted, or expanded with new children.\n\nThis command complies with [RFC7396](https://datatracker.ietf.org/doc/html/rfc7396) Json Merge Patch\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nis the key to merge into.\n</details>\n\n<details open><summary><code>path</code></summary>\n\nspecifies the JSONPath. For non-existing keys the `path` must be `$`. For existing keys, for each matched `path`, the value that matches the `path` is merged with the JSON `value`. For existing keys, when the path exists, except for the last element, a new child is added with the JSON `value`.\n\n</details>\n\n<details open><summary><code>value</code></summary>\n\nis the JSON value to merge with at the specified path. Merging is done according to the following rules per JSON value in the `value` argument while considering the corresponding original value if it exists:\n*   merging an existing object key with a `null` value deletes the key\n*   merging an existing object key with non-null value updates the value\n*   merging a non-existing object key adds the key and value\n*   merging an existing array with any merged value, replaces the entire array with the value\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"JSON.MERGE provides four different behaviors to merge changes on a given key: create a non-existent path, update an existing path with a new value, delete an existing path, or replace an array with a new array\n\n<details open>\n<summary><b>Create a non-existent path-value</b></summary>\n\n\nredis> JSON.SET doc $ '{\"a\":2}'\nOK\nredis> JSON.MERGE doc $.b '8'\nOK\nredis> JSON.GET doc $\n\"[{\\\"a\\\":2,\\\"b\\\":8}]\"\n\n\n</details>\n\n<details open>\n<summary><b>Replace an existing value</b></summary>\n\n\nredis> JSON.SET doc $ '{\"a\":2}'\nOK\nredis> JSON.MERGE doc $.a '3'\nOK\nredis> JSON.GET doc $\n\"[{\\\"a\\\":3}]\"\n\n\n</details>\n\n<details open>\n<summary><b>Delete an existing value</b></summary>\n\n\nredis> JSON.SET doc $ '{\"a\":2}'\nOK\nredis> JSON.MERGE doc $ '{\"a\":null}'\nOK\nredis> JSON.GET doc $\n\"[{}]\"\n\n\n</details>\n\n<details open>\n<summary><b>Replace an Array</b></summary>\n\n\nredis> JSON.SET doc $ '{\"a\":[2,4,6,8]}'\nOK\nredis> JSON.MERGE doc $.a '[10,12]'\nOK\nredis> JSON.GET doc $\n\"[{\\\"a\\\":[10,12]}]\"\n\n\n</details>\n\n<details open>\n<summary><b>Merge changes in multi-paths</b></summary>\n\n\nredis> JSON.SET doc $ '{\"f1\": {\"a\":1}, \"f2\":{\"a\":2}}'\nOK\nredis> JSON.GET doc\n\"{\\\"f1\\\":{\\\"a\\\":1},\\\"f2\\\":{\\\"a\\\":2}}\"\nredis> JSON.MERGE doc $ '{\"f1\": null, \"f2\":{\"a\":3, \"b\":4}, \"f3\":[2,4,6]}'\nOK\nredis> JSON.GET doc\n\"{\\\"f2\\\":{\\\"a\\\":3,\\\"b\\\":4},\\\"f3\\\":[2,4,6]}\"\n\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors): if the operation fails to set the new values.\n\n**RESP3:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors): if the operation fails to set the new values."},{"id":"see-also","title":"See also","role":"related","text":"[`JSON.GET`](https://redis.io/docs/latest/commands/json.get) | [`JSON.MGET`](https://redis.io/docs/latest/commands/json.mget) | [`JSON.SET`](https://redis.io/docs/latest/commands/json.set) | [`JSON.MSET`](https://redis.io/docs/latest/commands/json.mset)"},{"id":"related-topics","title":"Related topics","role":"related","text":"* [RedisJSON](https://redis.io/docs/latest/develop/data-types/json)\n* [Index and search JSON documents](https://redis.io/docs/latest/develop/ai/search-and-query/indexing)"}],"examples":[]}
{"schema_version":2,"id":"commands/json.mget","title":"JSON.MGET","url":"https://redis.io/docs/latest/commands/json.mget/","summary":"Returns the values at a path from one or more keys","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"2869d287fd7b280756c627fee6ff5613dbbedbd4b5b4036800968dfab1fcf82f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nReturn the values at `path` from multiple `key` arguments\n\n> [!WARNING]\n> When cluster mode is enabled, all specified keys must reside on the same [hash slot](https://redis.io/docs/latest/operate/oss_and_stack/reference/cluster-spec/#key-distribution-model).\n>\n> When the database has more than one shard, and the specified keys reside in different shards, Redis will not report a CROSSSLOT error (to avoid breaking changes) and the results may be partial.\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary> \n\nis key to parse. Returns `null` for nonexistent keys.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>path</code></summary> \n\nis JSONPath to specify. Returns `null` for nonexistent paths.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Return the values at <code>path</code> from multiple <code>key</code> arguments</b></summary>\n\nCreate two JSON documents.\n\n\nredis> JSON.SET doc1 $ '{\"a\":1, \"b\": 2, \"nested\": {\"a\": 3}, \"c\": null}'\nOK\nredis> JSON.SET doc2 $ '{\"a\":4, \"b\": 5, \"nested\": {\"a\": 6}, \"c\": null}'\nOK\n\n\nGet values from all arguments in the documents.\n\n\nredis> JSON.MGET doc1 doc2 $..a\n1) \"[1,3]\"\n2) \"[4,6]\"\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [bulk string replies](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) or [null replies](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls), where each element is the JSON serialization of the value at the corresponding key's path, or `null` if the key or path doesn't exist.\n\n**RESP3:**\n\n[Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [bulk string replies](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) or [null replies](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls), where each element is the JSON serialization of the value at the corresponding key's path, or `null` if the key or path doesn't exist."},{"id":"see-also","title":"See also","role":"related","text":"[`JSON.SET`](https://redis.io/docs/latest/commands/json.set) | [`JSON.GET`](https://redis.io/docs/latest/commands/json.get)"},{"id":"related-topics","title":"Related topics","role":"related","text":"* [RedisJSON](https://redis.io/docs/latest/develop/data-types/json)\n* [Index and search JSON documents](https://redis.io/docs/latest/develop/ai/search-and-query/indexing)"}],"examples":[]}
{"schema_version":2,"id":"commands/json.mset","title":"JSON.MSET","url":"https://redis.io/docs/latest/commands/json.mset/","summary":"Sets or updates the JSON value of one or more keys","since":"2.6.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"c91a0dc239643151d0ba32bd7a2787e510b1a7dbaedfd5c4cd7bd145dc87c0d2","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nSet or update one or more JSON values according to the specified `key`-`path`-`value` triplets\n\n`JSON.MSET` is atomic, hence, all given additions or updates are either applied or not. It is not possible for clients to see that some of the keys were updated while others are unchanged.\n\nA JSON value is a hierarchical structure. If you change a value in a specific path - nested values are affected.\n\n> [!WARNING]\n> When cluster mode is enabled, all specified keys must reside on the same [hash slot](https://redis.io/docs/latest/operate/oss_and_stack/reference/cluster-spec/#key-distribution-model).\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nis key to modify.\n</details>\n\n<details open><summary><code>path</code></summary>\n\nis JSONPath to specify. For new Redis keys the `path` must be the root. For existing keys, when the entire `path` exists, the value that it contains is replaced with the `json` value. For existing keys, when the `path` exists, except for the last element, a new child is added with the `json` value.\n\n</details>\n\n<details open><summary><code>value</code></summary>\n\nis value to set at the specified path\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Add a new values in multiple keys</b></summary>\n\n\nredis> JSON.MSET doc1 $ '{\"a\":1}' doc2 $ '{\"f\":{\"a\":2}}' doc3 $ '{\"f1\":{\"a\":0},\"f2\":{\"a\":0}}'\nOK\nredis> JSON.MSET doc1 $ '{\"a\":2}' doc2 $.f.a '3' doc3 $ '{\"f1\":{\"a\":1},\"f2\":{\"a\":2}}'\nOK\nredis> JSON.GET doc1 $\n\"[{\\\"a\\\":2}]\"\nredis> JSON.GET doc2 $\n\"[{\\\"f\\\":{\\\"a\\\":3}}]\"\nredis> JSON.GET doc3\n\"{\\\"f1\\\":{\\\"a\\\":1},\\\"f2\\\":{\\\"a\\\":2}}\"\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors): if the operation fails to set the new values.\n\n**RESP3:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors): if the operation fails to set the new values."},{"id":"see-also","title":"See also","role":"related","text":"[`JSON.SET`](https://redis.io/docs/latest/commands/json.set) | [`JSON.MGET`](https://redis.io/docs/latest/commands/json.mget) | [`JSON.GET`](https://redis.io/docs/latest/commands/json.get)"},{"id":"related-topics","title":"Related topics","role":"related","text":"* [RedisJSON](https://redis.io/docs/latest/develop/data-types/json)\n* [Index and search JSON documents](https://redis.io/docs/latest/develop/ai/search-and-query/indexing)"}],"examples":[]}
{"schema_version":2,"id":"commands/json.numincrby","title":"JSON.NUMINCRBY","url":"https://redis.io/docs/latest/commands/json.numincrby/","summary":"Increments the numeric value at path by a value","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-06T06:53:54-07:00","page_type":"content","content_hash":"96d22e6c68f303c58379a0e79b8cc6c5fc6a421c23ef1cc2ccd9a0e2c14768fa","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Increment the number value stored at `path` by `value`\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary> \n\nis key to modify.\n</details>\n\n<details open><summary><code>path</code></summary> \n\nis JSONPath to specify.\n</details>\n\n<details open><summary><code>value</code></summary> \n\nis number value to increment. \n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Increment number values</b></summary>\n\nCreate a document.\n\n\nredis> JSON.SET doc . '{\"a\":\"b\",\"b\":[{\"a\":2}, {\"a\":5}, {\"a\":\"c\"}]}'\nOK\n\n\nIncrement a value of `a` object by 2. The command fails to find a number and returns `null`.\n\n\nredis> JSON.NUMINCRBY doc $.a 2\n\"[null]\"\n\n\nRecursively find and increment a value of all `a` objects. The command increments numbers it finds and returns `null` for nonnumber values.\n\n\nredis> JSON.NUMINCRBY doc $..a 2\n\"[null,4,7,null]\"\n\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nWith `$`-based path argument: [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) containing a JSON array with one element per matching path: the new value, or `null` if the matching value is not a number. The array is empty if no path matches.\n\nWith `.`-based path argument: [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) representing the stringified new value. If the path matches more than one number, the command updates all of them and returns the last new value. [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if no matching value is a number.\n\nWith either path argument: [simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if the key doesn't exist or the result is out of range.\n\n**RESP3:**\n\nThe path syntax doesn't change the reply. [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) with one element per matching path: an [integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) or [double reply](https://redis.io/docs/latest/develop/reference/protocol-spec#doubles) containing the new value, or a [null reply](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls) if the matching value is not a number. The array is empty if no path matches.\n\n[Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if the key doesn't exist or the result is out of range."},{"id":"see-also","title":"See also","role":"related","text":"[`JSON.ARRINDEX`](https://redis.io/docs/latest/commands/json.arrindex) | [`JSON.ARRINSERT`](https://redis.io/docs/latest/commands/json.arrinsert)"},{"id":"related-topics","title":"Related topics","role":"related","text":"* [RedisJSON](https://redis.io/docs/latest/develop/data-types/json)\n* [Index and search JSON documents](https://redis.io/docs/latest/develop/ai/search-and-query/indexing)"}],"examples":[]}
{"schema_version":2,"id":"commands/json.nummultby","title":"JSON.NUMMULTBY","url":"https://redis.io/docs/latest/commands/json.nummultby/","summary":"Multiplies the numeric value at path by a value","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-06T06:53:54-07:00","page_type":"content","content_hash":"bee0d75c57c1436884162c61691028352ecbc1281256b169f178a52d1f38c6cc","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Multiply the number value stored at `path` by `value`\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary> \n\nis key to modify.\n</details>\n\n<details open><summary><code>path</code></summary> \n\nis JSONPath to specify.\n</details>\n\n<details open><summary><code>value</code></summary> \n\nis number value to multiply. \n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> JSON.SET doc . '{\"a\":\"b\",\"b\":[{\"a\":2}, {\"a\":5}, {\"a\":\"c\"}]}'\nOK\nredis> JSON.NUMMULTBY doc $.a 2\n\"[null]\"\nredis> JSON.NUMMULTBY doc $..a 2\n\"[null,4,10,null]\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nWith `$`-based path argument: [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) containing a JSON array with one element per matching path: the new value, or `null` if the matching value is not a number. The array is empty if no path matches.\n\nWith `.`-based path argument: [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) representing the stringified new value. If the path matches more than one number, the command updates all of them and returns the last new value. [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if no matching value is a number.\n\nWith either path argument: [simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if the key doesn't exist or the result is out of range.\n\n**RESP3:**\n\nThe path syntax doesn't change the reply. [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) with one element per matching path: an [integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) or [double reply](https://redis.io/docs/latest/develop/reference/protocol-spec#doubles) containing the new value, or a [null reply](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls) if the matching value is not a number. The array is empty if no path matches.\n\n[Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if the key doesn't exist or the result is out of range."},{"id":"see-also","title":"See also","role":"related","text":"[`JSON.NUMINCRBY`](https://redis.io/docs/latest/commands/json.numincrby) | [`JSON.ARRINSERT`](https://redis.io/docs/latest/commands/json.arrinsert)"},{"id":"related-topics","title":"Related topics","role":"related","text":"* [RedisJSON](https://redis.io/docs/latest/develop/data-types/json)\n* [Index and search JSON documents](https://redis.io/docs/latest/develop/ai/search-and-query/indexing)"}],"examples":[]}
{"schema_version":2,"id":"commands/json.numpowby","title":"JSON.NUMPOWBY","url":"https://redis.io/docs/latest/commands/json.numpowby/","summary":"Raises the numeric value at path to the power of a value","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-06T06:53:54-07:00","page_type":"content","content_hash":"568948576a8396af28a4822d4a9d0a8a88b51ac1ab1ff8c27c00c9c811c5e0a9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Raise the number value stored at `path` to the power of `value`\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary> \n\nis key to modify.\n</details>\n\n<details open><summary><code>path</code></summary> \n\nis JSONPath to specify.\n</details>\n\n<details open><summary><code>value</code></summary> \n\nis number value to use as the exponent.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> JSON.SET doc . '{\"a\":\"b\",\"b\":[{\"a\":2}, {\"a\":5}, {\"a\":\"c\"}]}'\nOK\nredis> JSON.NUMPOWBY doc $.a 2\n\"[null]\"\nredis> JSON.NUMPOWBY doc $..a 2\n\"[null,4,25,null]\""},{"id":"details","title":"Details","role":"content","text":"If both the stored value and `value` are integers, the result is an integer. It must fit in a 64-bit signed integer, or the command fails with a `numeric overflow` error.\n\nA negative integer exponent with an integer stored value also fails with `numeric overflow`. To get a fractional result, write the exponent as a floating-point number: raising `2` to `-1.0` returns `0.5`.\n\nIf either number is a floating-point number, the command uses floating-point arithmetic. A result that isn't a finite real number, such as `0` raised to `-1.0` or `-8` raised to `0.5`, fails with a `result is not a number` error.\n\nWhen the command fails, the stored value doesn't change."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nWith `$`-based path argument: [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) containing a JSON array with one element per matching path: the new value, or `null` if the matching value is not a number. The array is empty if no path matches.\n\nWith `.`-based path argument: [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) representing the stringified new value. If the path matches more than one number, the command updates all of them and returns the last new value. [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if no matching value is a number.\n\nWith either path argument: [simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if the key doesn't exist or the result is out of range.\n\n**RESP3:**\n\nThe path syntax doesn't change the reply. [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) with one element per matching path: an [integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) or [double reply](https://redis.io/docs/latest/develop/reference/protocol-spec#doubles) containing the new value, or a [null reply](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls) if the matching value is not a number. The array is empty if no path matches.\n\n[Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if the key doesn't exist or the result is out of range."},{"id":"see-also","title":"See also","role":"related","text":"[`JSON.NUMINCRBY`](https://redis.io/docs/latest/commands/json.numincrby) | [`JSON.NUMMULTBY`](https://redis.io/docs/latest/commands/json.nummultby)"},{"id":"related-topics","title":"Related topics","role":"related","text":"* [RedisJSON](https://redis.io/docs/latest/develop/data-types/json)\n* [Index and search JSON documents](https://redis.io/docs/latest/develop/ai/search-and-query/indexing)"}],"examples":[]}
{"schema_version":2,"id":"commands/json.objkeys","title":"JSON.OBJKEYS","url":"https://redis.io/docs/latest/commands/json.objkeys/","summary":"Returns the key names of JSON objects at the paths matching a given path expression","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"7b442653f7f91b51ee89fd421350571d213e946e3fbd30c5a256de7b5d66ce40","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns the key names of JSON objects at the paths matching a given path expression.\n\n> [!NOTE]\n> A JSON object is a structure within a JSON document that contains an unordered set of key-value pairs (also called name-value pairs). Do not confuse Redis keys with JSON object keys.\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary> \n\nis a Redis key storing a value of type JSON.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>path</code></summary> \n\nis either \n\n- A JSONPath expression\n  - The root \"`$`\", or any string that starts with \"`$.`\" or \"`$[`\".\n  - Resolves to all matching locations in `key`.\n- A legacy path expression \n  - Any string that does not match the JSONPath syntax above.\n  - Allow the leading \"`.`\" to be omitted (for example, \"`name`\" and \"`.name`\" are equivalent).\n  - Resolves to only the first matching location in `key`.\n\nDefault: \"`.`\" (legacy path pointing to the root of the document).\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> JSON.SET doc $ '{\"a\":[3], \"nested\": {\"a\": {\"b\":2, \"c\": 1}}}'\nOK\nredis> JSON.OBJKEYS doc $..a\n1) (nil)\n2) 1) \"b\"\n   2) \"c\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nIf `path` is a JSONPath expression:\n\n- A [simple error](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if `key` does not exist.\n- An empty [array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) if `path` has no matches.\n- An [array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) where each array element corresponds to one match:\n  - [`nil`](https://redis.io/docs/latest/develop/reference/protocol-spec#null-bulk-strings) if the match is not an object.\n  - An [array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [bulk string replies](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) containing the object's key names if the match is an object.\n\nIf `path` is a legacy path expression:\n\n- [`nil`](https://redis.io/docs/latest/develop/reference/protocol-spec#null-bulk-strings) if `key` does not exist.\n- [`nil`](https://redis.io/docs/latest/develop/reference/protocol-spec#null-bulk-strings) if `path` has no matches.\n- A [simple error](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if the first match is not an object.\n- An [array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [bulk string replies](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) containing the object's key names of the first match.\n\n**RESP3:**\n\nIf `path` is a JSONPath expression:\n\n- A [simple error](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if `key` does not exist.\n- An empty [array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) if `path` has no matches.\n- An [array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) where each array element corresponds to one match:\n  - [`nil`](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls) if the match is not an object.\n  - An [array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [bulk string replies](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) containing the object's key names if the match is an object.\n\nIf `path` is a legacy path expression:\n\n- [`nil`](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls) if `key` does not exist.\n- [`nil`](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls) if `path` has no matches.\n- A [simple error](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if the first match is not an object.\n- An [array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [bulk string replies](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) containing the object's key names of the first match."},{"id":"see-also","title":"See also","role":"related","text":"[`JSON.OBJLEN`](https://redis.io/docs/latest/commands/json.objlen)"},{"id":"related-topics","title":"Related topics","role":"related","text":"* [The JSON data type](https://redis.io/docs/latest/develop/data-types/json)\n* [JSONPath](https://redis.io/docs/latest/develop/data-types/json/path)\n* [Index and search JSON documents](https://redis.io/docs/latest/develop/ai/search-and-query/indexing)"}],"examples":[]}
{"schema_version":2,"id":"commands/json.objlen","title":"JSON.OBJLEN","url":"https://redis.io/docs/latest/commands/json.objlen/","summary":"Returns the number of keys in JSON objects at the paths matching a given path expression","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"ea7d92056bcbb41d5bcaacbf1ac4ad91e7ff66b081849a624ed64e0a10e56c7b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns the number of keys in JSON objects at the paths matching a given path expression.\n\n> [!NOTE]\n> A JSON object is a structure within a JSON document that contains an unordered set of key-value pairs (also called name-value pairs). Do not confuse Redis keys with JSON object keys.\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary> \n\nis a Redis key storing a value of type JSON.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>path</code></summary> \n\nis either \n\n- A JSONPath expression\n  - The root \"`$`\", or any string that starts with \"`$.`\" or \"`$[`\".\n  - Resolves to all matching locations in `key`.\n- A legacy path expression \n  - Any string that does not match the JSONPath syntax above.\n  - Allow the leading \"`.`\" to be omitted (for example, \"`name`\" and \"`.name`\" are equivalent).\n  - Resolves to only the first matching location in `key`.\n\nDefault: \"`.`\" (legacy path pointing to the root of the document).\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> JSON.SET doc $ '{\"a\":[3], \"nested\": {\"a\": {\"b\":2, \"c\": 1}}}'\nOK\nredis> JSON.OBJLEN doc $..a\n1) (nil)\n2) (integer) 2"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nIf `path` is a JSONPath expression:\n\n- A [simple error](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if `key` does not exist.\n- An empty [array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) if `path` has no matches.\n- An [array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) where each array element corresponds to one match:\n  - [`nil`](https://redis.io/docs/latest/develop/reference/protocol-spec#null-bulk-strings) if the match is not an object.\n  - An [integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): the number of keys in the object.\n\nIf `path` is a legacy path expression:\n\n- [`nil`](https://redis.io/docs/latest/develop/reference/protocol-spec#null-bulk-strings) if `key` does not exist.\n- [`nil`](https://redis.io/docs/latest/develop/reference/protocol-spec#null-bulk-strings) if `path` has no matches.\n- A [simple error](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if the first match is not an object.\n- An [integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): the number of keys in the first match.\n\n**RESP3:**\n\nIf `path` is a JSONPath expression:\n\n- A [simple error](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if `key` does not exist.\n- An empty [array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) if `path` has no matches.\n- An [array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) where each array element corresponds to one match:\n  - [`nil`](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls) if the match is not an object.\n  - An [integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): the number of keys in the object.\n\nIf `path` is a legacy path expression:\n\n- [`nil`](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls) if `key` does not exist.\n- [`nil`](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls) if `path` has no matches.\n- A [simple error](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if the first match is not an object.\n- An [integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): the number of keys in the first match."},{"id":"see-also","title":"See also","role":"related","text":"[`JSON.OBJKEYS`](https://redis.io/docs/latest/commands/json.objkeys)"},{"id":"related-topics","title":"Related topics","role":"related","text":"* [The JSON data type](https://redis.io/docs/latest/develop/data-types/json)\n* [JSONPath](https://redis.io/docs/latest/develop/data-types/json/path)\n* [Index and search JSON documents](https://redis.io/docs/latest/develop/ai/search-and-query/indexing)"}],"examples":[]}
{"schema_version":2,"id":"commands/json.resp","title":"JSON.RESP","url":"https://redis.io/docs/latest/commands/json.resp/","summary":"Returns the JSON value at path in Redis Serialization Protocol (RESP)","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"a66d2fc56bd790c640e34381f334a0211bf4c1d76c35cb37c1a16c8861cabefd","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Return the JSON in `key` in [Redis serialization protocol specification](https://redis.io/docs/latest/develop/reference/protocol-spec) form \n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary> \n\nis key to parse.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>path</code></summary> \n\nis JSONPath to specify. Default is root `$`. This command uses the following mapping from JSON to RESP:\n\n*   JSON `null` maps to the bulk string reply.\n*   JSON `false` and `true` values map to the simple string reply.\n*   JSON number maps to the integer reply or bulk string reply, depending on type.\n*   JSON string maps to the bulk string reply.\n*   JSON array is represented as an array reply in which the first element is the simple string reply `[`, followed by the array's elements.\n*   JSON object is represented as an array reply in which the first element is the simple string reply `{`. Each successive entry represents a key-value pair as a two-entry array reply of the bulk string reply.\n\nFor more information about replies, see [Redis serialization protocol specification](https://redis.io/docs/latest/develop/reference/protocol-spec).\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Return an array of RESP details about a document</b></summary>\n\nCreate a JSON document.\n\n\nredis> JSON.SET item:2 $ '{\"name\":\"Wireless earbuds\",\"description\":\"Wireless Bluetooth in-ear headphones\",\"connection\":{\"wireless\":true,\"type\":\"Bluetooth\"},\"price\":64.99,\"stock\":17,\"colors\":[\"black\",\"white\"], \"max_level\":[80, 100, 120]}'\nOK\n\n\nGet all RESP details about the document.\n\n\nredis> JSON.RESP item:2\n 1) {\n 2) \"name\"\n 3) \"Wireless earbuds\"\n 4) \"description\"\n 5) \"Wireless Bluetooth in-ear headphones\"\n 6) \"connection\"\n 7) 1) {\n    2) \"wireless\"\n    3) true\n    4) \"type\"\n    5) \"Bluetooth\"\n 8) \"price\"\n 9) \"64.989999999999995\"\n10) \"stock\"\n11) (integer) 17\n12) \"colors\"\n13) 1) [\n    2) \"black\"\n    3) \"white\"\n14) \"max_level\"\n15) 1) [\n    2) (integer) 80\n    3) (integer) 100\n    4) (integer) 120\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) representing the JSON value in RESP form, as detailed in the [Redis serialization protocol specification](https://redis.io/docs/latest/develop/reference/protocol-spec). The mapping from JSON to RESP follows the rules described in the command arguments.\n\n**RESP3:**\n\n[Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) representing the JSON value in RESP form, as detailed in the [Redis serialization protocol specification](https://redis.io/docs/latest/develop/reference/protocol-spec). The mapping from JSON to RESP follows the rules described in the command arguments."},{"id":"see-also","title":"See also","role":"related","text":"[`JSON.SET`](https://redis.io/docs/latest/commands/json.set) | [`JSON.ARRLEN`](https://redis.io/docs/latest/commands/json.arrlen)"},{"id":"related-topics","title":"Related topics","role":"related","text":"* [RedisJSON](https://redis.io/docs/latest/develop/data-types/json)\n* [Index and search JSON documents](https://redis.io/docs/latest/develop/ai/search-and-query/indexing)"}],"examples":[]}
{"schema_version":2,"id":"commands/json.set","title":"JSON.SET","url":"https://redis.io/docs/latest/commands/json.set/","summary":"Sets or updates the JSON value at a path","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"adebc7150a2f4efa1274d8fd27cae92d491c27a783feb25f15f367647db18ca4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Set or replace the value at each location resolved by `path`.\n\nIf the key does not exist, a new JSON document can be created only by setting the root path (`$` or `.`).\n\n`JSON.SET` can also create new object members when the parent object exists.\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary> \n\nis a new key to create or an existing JSON key to modify.\n</details>\n\n<details open><summary><code>path</code></summary> \n\nA JSONPath expression that resolves to zero or more locations within the JSON document.\n\n- The root of the document is specified as `$` or `.`.\n- If `path` resolves to one or more existing locations, the value at each matched location is replaced with `value`.\n- If the final token of `path` is a non-existing object member and the parent location exists and is an object, the member is created and set to `value`.\n- If any intermediate path element does not exist, the path cannot be created, and the command returns nil.\n\nOptional arguments `NX` and `XX` modify this behavior for both new keys and existing JSON keys.\n</details>\n\n<details open><summary><code>value</code></summary> \n\nA valid JSON value to set at the specified path.\n\nThe value can be a scalar (string, number, boolean, or null) or a compound value such as an object or array.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>NX</code></summary> \n\nSets the value only if `path` has no matches.\n</details>\n\n<details open><summary><code>XX</code></summary> \n\nSets the value only if `path` has one or more matches.\n</details>\n\n<details open><summary><code>FPHA <FP16 | BF16 | FP32 | FP64></code></summary>\n\nForce floating point homogeneous arrays (FPHAs) to use a specified FP type. Added in Redis 8.8.\n\nThe available types offer a trade-off between precision and memory footprint. FP64 (64-bit) and FP32 (32-bit) provide higher precision for scientific and general computing workloads. BF16 and FP16 (both 16-bit) use half the memory of FP32 and require less memory, making them well suited for AI training and inference. A common strategy is to store and compute in BF16/FP16 for speed while accumulating results in FP32 to maintain accuracy.\n\nSince JSON's numeric representation is textual, Redis cannot always infer the best FP type to use for FPHAs (for example, for vector embeddings). If your JSON was generated from an array of FP values with a given FP type, you should pass this type here.\n\nIf at least one of the specified values within the given FPHA doesn’t fit into the type specified (an overflow situation), the command will issue\na \"`value out of range`\" error.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Replace an existing value</b></summary>\n\n\nredis> JSON.SET doc $ '{\"a\":2}'\nOK\nredis> JSON.SET doc $.a '3'\nOK\nredis> JSON.GET doc $\n\"[{\\\"a\\\":3}]\"\n\n</details>\n\n<details open>\n<summary><b>Add a new value</b></summary>\n\n\nredis> JSON.SET doc $ '{\"a\":2}'\nOK\nredis> JSON.SET doc $.b '8'\nOK\nredis> JSON.GET doc $\n\"[{\\\"a\\\":2,\\\"b\\\":8}]\"\n\n</details>\n\n<details open>\n<summary><b>Add a new value with FPHA</b></summary>\n\n\nredis> JSON.SET doc $ '[[1,2,3,4e3],[5,6.0,7,8]]' FPHA FP16\nOK\nredis> JSON.GET doc $\n\"[[[1.0,2.0,3.0,4000.0],[5.0,6.0,7.0,8.0]]]\"\n\n</details>\n\n<details open>\n<summary><b>Update multiple matches</b></summary>\n\n\nredis> JSON.SET doc $ '{\"f1\": {\"a\":1}, \"f2\":{\"a\":2}}'\nOK\nredis> JSON.SET doc $..a 3\nOK\nredis> JSON.GET doc\n\"{\\\"f1\\\":{\\\"a\\\":3},\\\"f2\\\":{\\\"a\\\":3}}\"\n\n</details>\n\n<details open>\n<summary><b>path does not exist and cannot be created</b></summary>\n\n\nredis> JSON.SET doc $ 1\nOK\nredis> JSON.SET doc $.x.y 2\n(nil)\n\n</details>\n\n<details open>\n<summary><b>XX condition unmet</b></summary>\n\n\nredis> JSON.SET nonexistentkey $ 5 XX\n(nil)\nredis> JSON.GET nonexistentkey\n(nil)\n\n</details>\n\n<details open>\n<summary><b>key does not exist and path is not root</b></summary>\n\n\nredis> JSON.SET nonexistentkey $.x 5\n(error) ERR new objects must be created at the root\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` if executed correctly.\n* [Null reply](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls): if `key` exists but `path` does not exist and cannot be created, or if an `NX` or `XX` condition is unmet.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors): `(error) expected ...` - if the value is invalid.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors): `(error) Error occurred on position ... expected ...` - if the path is invalid.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors): `(error) ERR new objects must be created at the root` - if `key` does not exist and `path` is not root (`$` or `.`).\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors): `(error) ERR wrong static path` - if a dynamic path expression has no matching locations.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors): `(error) ERR index out of bounds` - if the path refers to an array index outside the array bounds.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors): `(error) value out of range for ...` - if one or more values of a FP array are out of range for the given type.\n\n**RESP3:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` if executed correctly.\n* [Null reply](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls): if `key` exists but `path` does not exist and cannot be created, or if an `NX` or `XX` condition is unmet.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors): `(error) expected ...` - if the value is invalid.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors): `(error) Error occurred on position ... expected ...` - if the path is invalid.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors): `(error) ERR new objects must be created at the root` - if `key` does not exist and `path` is not root (`$` or `.`).\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors): `(error) ERR wrong static path` - if a dynamic path expression has no matching locations.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors): `(error) ERR index out of bounds` - if the path refers to an array index outside the array bounds.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors): `(error) value out of range for ...` - if one or more values of a FP array are out of range for the given type."},{"id":"see-also","title":"See also","role":"related","text":"[`JSON.GET`](https://redis.io/docs/latest/commands/json.get) | [`JSON.MGET`](https://redis.io/docs/latest/commands/json.mget)"},{"id":"related-topics","title":"Related topics","role":"related","text":"* [RedisJSON](https://redis.io/docs/latest/develop/data-types/json)\n* [Index and search JSON documents](https://redis.io/docs/latest/develop/ai/search-and-query/indexing)"}],"examples":[]}
{"schema_version":2,"id":"commands/json.strappend","title":"JSON.STRAPPEND","url":"https://redis.io/docs/latest/commands/json.strappend/","summary":"Appends a string to JSON strings at the paths matching a given path expression","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"f0866299b54a5dd6b6844a4a7beea22fd16cc8813ca10ce521a69e0903cedc20","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Appends a string to JSON strings at the paths matching a given path expression.\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary> \n\nis a Redis key storing a value of type JSON.\n\n</details>\n\n<details open><summary><code>value</code></summary> \n\nis a string to append to the JSON strings at the paths matching `path`.\n\n> [!NOTE] About using strings with JSON commands\n> To specify a string as an array value to append, wrap the quoted string with an additional set of single quotes. Example: `'\"silver\"'`. For more detailed use, see [Examples](#examples).\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>path</code></summary> \n\nis either \n\n- A JSONPath expression\n  - The root \"`$`\", or any string that starts with \"`$.`\" or \"`$[`\".\n  - Resolves to all matching locations in `key`.\n- A legacy path expression \n  - Any string that does not match the JSONPath syntax above.\n  - Allow the leading \"`.`\" to be omitted (for example, \"`name`\" and \"`.name`\" are equivalent).\n  - Resolves to only the first matching location in `key`.\n \nDefault: \"`.`\" (legacy path pointing to the root of the document).\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> JSON.SET doc $ '{\"a\":\"foo\", \"nested\": {\"a\": \"hello\"}, \"nested2\": {\"a\": 31}}'\nOK\nredis> JSON.STRAPPEND doc $..a '\"baz\"'\n1) (integer) 6\n2) (integer) 8\n3) (nil)\nredis> JSON.GET doc $\n\"[{\\\"a\\\":\\\"foobaz\\\",\\\"nested\\\":{\\\"a\\\":\\\"hellobaz\\\"},\\\"nested2\\\":{\\\"a\\\":31}}]\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nIf `path` is a JSONPath expression:\n\n- A [simple error](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if `key` does not exist.\n- An empty [array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) if `path` has no matches.\n- An [array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) where each array element corresponds to one match:\n  - [`nil`](https://redis.io/docs/latest/develop/reference/protocol-spec#null-bulk-strings) if the match is not a string.\n  - An [integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): the new length of the string.\n\nIf `path` is a legacy path expression:\n\n- A [simple error](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if `key` does not exist.\n- A [simple error](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if `path` has no matches.\n- A [simple error](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if the first match is not a string.\n- An [integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): the new length of the string at the first match.\n\n**RESP3:**\n\nIf `path` is a JSONPath expression:\n\n- A [simple error](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if `key` does not exist.\n- An empty [array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) if `path` has no matches.\n- An [array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) where each array element corresponds to one match:\n  - [`nil`](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls) if the match is not a string.\n  - An [integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): the new length of the string.\n\nIf `path` is a legacy path expression:\n\n- A [simple error](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if `key` does not exist.\n- A [simple error](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if `path` has no matches.\n- A [simple error](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if the first match is not a string.\n- An [integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): the new length of the string at the first match."},{"id":"see-also","title":"See also","role":"related","text":"[`JSON.STRLEN`](https://redis.io/docs/latest/commands/json.strlen) | [`JSON.ARRAPPEND`](https://redis.io/docs/latest/commands/json.arrappend)"},{"id":"related-topics","title":"Related topics","role":"related","text":"* [The JSON data type](https://redis.io/docs/latest/develop/data-types/json)\n* [JSONPath](https://redis.io/docs/latest/develop/data-types/json/path)\n* [Index and search JSON documents](https://redis.io/docs/latest/develop/ai/search-and-query/indexing)"}],"examples":[]}
{"schema_version":2,"id":"commands/json.strlen","title":"JSON.STRLEN","url":"https://redis.io/docs/latest/commands/json.strlen/","summary":"Returns the length of JSON strings at the paths matching a given path expression","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"e096fbfe3fd30e1f23823e221ec7b937df7305fc9d1b1b0d996b52e3963554d0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns the length of JSON strings at the paths matching a given path expression.\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary> \n\nis a Redis key storing a value of type JSON.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>path</code></summary> \n\nis either \n\n- A JSONPath expression\n  - The root \"`$`\", or any string that starts with \"`$.`\" or \"`$[`\".\n  - Resolves to all matching locations in `key`.\n- A legacy path expression \n  - Any string that does not match the JSONPath syntax above.\n  - Allow the leading \"`.`\" to be omitted (for example, \"`name`\" and \"`.name`\" are equivalent).\n  - Resolves to only the first matching location in `key`.\n\nDefault: \"`.`\" (legacy path pointing to the root of the document).\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> JSON.SET doc $ '{\"a\":\"foo\", \"nested\": {\"a\": \"hello\"}, \"nested2\": {\"a\": 31}}'\nOK\nredis> JSON.STRLEN doc $..a\n1) (integer) 3\n2) (integer) 5\n3) (nil)"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nIf `path` is a JSONPath expression:\n\n- A [simple error](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if `key` does not exist.\n- An empty [array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) if `path` has no matches.\n- An [array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) where each array element corresponds to one match:\n  - [`nil`](https://redis.io/docs/latest/develop/reference/protocol-spec#null-bulk-strings) if the match is not a string.\n  - An [integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): the length of the string.\n\nIf `path` is a legacy path expression:\n\n- [`nil`](https://redis.io/docs/latest/develop/reference/protocol-spec#null-bulk-strings) if `key` does not exist.\n- A [simple error](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if `path` has no matches.\n- A [simple error](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if the first match is not a string.\n- An [integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): the length of the string at the first match.\n\n**RESP3:**\n\nIf `path` is a JSONPath expression:\n\n- A [simple error](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if `key` does not exist.\n- An empty [array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) if `path` has no matches.\n- An [array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) where each array element corresponds to one match:\n  - [`nil`](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls) if the match is not a string.  \n  - An [integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): the length of the string.\n\nIf `path` is a legacy path expression:\n\n- [`nil`](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls) if `key` does not exist.\n- A [simple error](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if `path` has no matches.\n- A [simple error](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) if the first match is not a string.\n- An [integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): the length of the string at the first match."},{"id":"see-also","title":"See also","role":"related","text":"[`JSON.STRAPPEND`](https://redis.io/docs/latest/commands/json.strappend) | [`JSON.ARRLEN`](https://redis.io/docs/latest/commands/json.arrlen) | [`JSON.OBJLEN`](https://redis.io/docs/latest/commands/json.objlen)"},{"id":"related-topics","title":"Related topics","role":"related","text":"* [The JSON data type](https://redis.io/docs/latest/develop/data-types/json)\n* [JSONPath](https://redis.io/docs/latest/develop/data-types/json/path)\n* [Index and search JSON documents](https://redis.io/docs/latest/develop/ai/search-and-query/indexing)"}],"examples":[]}
{"schema_version":2,"id":"commands/json.toggle","title":"JSON.TOGGLE","url":"https://redis.io/docs/latest/commands/json.toggle/","summary":"Toggles a boolean value","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"58d1f472ece4f6025563411e696d7e783fb8537aac7b64f2a9bafdd79f2d7173","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Toggle a Boolean value stored at `path`\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary> \n\nis key to modify.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>path</code></summary> \n\nis JSONPath to specify. Default is root `$`. \n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Toggle a Boolean value stored at <code>path</code></b></summary>\n\nCreate a JSON document.\n\n\nredis> JSON.SET doc $ '{\"bool\": true}'\nOK\n\n\nToggle the Boolean value.\n\n\nredis> JSON.TOGGLE doc $.bool\n1) (integer) 0\n\n\nGet the updated document.\n\n\nredis> JSON.GET doc $\n\"[{\\\"bool\\\":false}]\"\n\n\nToggle the Boolean value.\n\n\nredis> JSON.TOGGLE doc $.bool\n1) (integer) 1\n\n\nGet the updated document.\n\n\nredis> JSON.GET doc $\n\"[{\\\"bool\\\":true}]\"\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nWith `$`-based path argument: [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [integer replies](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) or [null replies](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls), where each element is the new value (`0` if `false` or `1` if `true`), or `null` if the matching value is not Boolean.\n\nWith `.`-based path argument: [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) representing the new value (`0` if `false` or `1` if `true`), or [null reply](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls) if the matching value is not Boolean.\n\n**RESP3:**\n\nWith `$`-based path argument (default): [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [integer replies](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) or [null replies](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls), where each element is the new value (`0` if `false` or `1` if `true`), or `null` if the matching value is not Boolean.\n\nWith `.`-based path argument: [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) representing the new value (`0` if `false` or `1` if `true`), or [null reply](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls) if the matching value is not Boolean."},{"id":"see-also","title":"See also","role":"related","text":"[`JSON.SET`](https://redis.io/docs/latest/commands/json.set) | [`JSON.GET`](https://redis.io/docs/latest/commands/json.get)"},{"id":"related-topics","title":"Related topics","role":"related","text":"* [RedisJSON](https://redis.io/docs/latest/develop/data-types/json)\n* [Index and search JSON documents](https://redis.io/docs/latest/develop/ai/search-and-query/indexing)"}],"examples":[]}
{"schema_version":2,"id":"commands/json.type","title":"JSON.TYPE","url":"https://redis.io/docs/latest/commands/json.type/","summary":"Returns the type of the JSON value at path","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"2f3b03a76c33d70878fd9dbca5c772f08549446f91565efb14eac2eec0d4838d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Report the type of JSON value at `path`\n\nThe returned type is one of the following strings:\n\n| Type | Description |\n|:-----|:------------|\n| `null` | A JSON null value. |\n| `boolean` | A JSON `true` or `false` value. |\n| `integer` | A number with no fractional part. |\n| `number` | A number with a fractional part (a floating-point value). <sup>[1](#table-note-1)</sup>|\n| `string` | A JSON string value. |\n| `object` | A JSON object (a collection of key-value pairs). |\n| `array` | A JSON array (an ordered list of values). |\n\n1. <a name=\"table-note-1\"></a>\nA floating-point homogeneous array (FPHA) stored with the [`JSON.SET`](https://redis.io/docs/latest/commands/json.set) `FPHA` argument still reports as `array`, and its elements report as `number`. The FP type (`FP16`, `BF16`, `FP32`, or `FP64`) is an internal storage representation and is not exposed by `JSON.TYPE`.\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary> \n\nis key to parse.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>path</code></summary> \n\nis JSONPath to specify. Default is root `$`. Returns null if the `key` or `path` do not exist.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> JSON.SET doc $ '{\"a\":2, \"nested\": {\"a\": true}, \"foo\": \"bar\"}'\nOK\nredis> JSON.TYPE doc $..foo\n1) \"string\"\nredis> JSON.TYPE doc $..a\n1) \"integer\"\n2) \"boolean\"\nredis> JSON.TYPE doc $..dummy\n(empty array)"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nWith `$`-based path argument: [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [bulk string replies](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings), where each element is the type of the matching value.\n\nWith `.`-based path argument: [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) representing the type of the matching value.\n\n**RESP3:**\n\nWith `$`-based path argument (default): [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [array replies](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [bulk string replies](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings), where each nested array contains the type of the matching value.\n\nWith `.`-based path argument: [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [bulk string replies](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) representing the type of the matching value."},{"id":"see-also","title":"See also","role":"related","text":"[`JSON.SET`](https://redis.io/docs/latest/commands/json.set) | [`JSON.ARRLEN`](https://redis.io/docs/latest/commands/json.arrlen)"},{"id":"related-topics","title":"Related topics","role":"related","text":"* [RedisJSON](https://redis.io/docs/latest/develop/data-types/json)\n* [Index and search JSON documents](https://redis.io/docs/latest/develop/ai/search-and-query/indexing)"}],"examples":[]}
{"schema_version":2,"id":"commands/keys","title":"KEYS","url":"https://redis.io/docs/latest/commands/keys/","summary":"Returns all key names that match a pattern.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"ba79622196deab0a26c24fa079c48f76fce3b564b4077348497fe94de15b1bf9","sections":[{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>pattern</code></summary>\n\nThe glob-style pattern to match key names against.\n\n</details>"},{"id":"details","title":"Details","role":"content","text":"While the time complexity for this operation is O(N), the constant times are\nfairly low.\nFor example, Redis running on an entry level laptop can scan a 1 million key\ndatabase in 40 milliseconds.\n\n> [!WARNING]\n> Use extreme care when using this command in production environments.\n> It may ruin performance when it is executed against large databases.\n> This command is intended for debugging and special operations, such as changing\n> your keyspace layout.\n> Don't use `KEYS` in your regular application code.\n> If you're looking for a way to find keys in a subset of your keyspace, consider\n> using [`SCAN`](https://redis.io/docs/latest/commands/scan) or [sets](https://redis.io/docs/latest/develop/data-types/sets).\n\nSupported glob-style patterns:\n\n* `h?llo` matches `hello`, `hallo` and `hxllo`\n* `h*llo` matches `hllo` and `heeeello`\n* `h[ae]llo` matches `hello` and `hallo,` but not `hillo`\n* `h[^e]llo` matches `hallo`, `hbllo`, ... but not `hello`\n* `h[a-b]llo` matches `hallo` and `hbllo`\n\nUse `\\` to escape special characters if you want to match them verbatim.\n\nWhen using [Redis Cluster](https://redis.io/docs/latest/operate/oss_and_stack/management/scaling), the search is optimized for patterns that imply a single slot.\nIf a pattern can only match keys of one slot,\nRedis only iterates over keys in that slot, rather than the whole database,\nwhen searching for keys matching the pattern.\nFor example, with the pattern `{a}h*llo`, Redis would only try to match it with the keys in slot 15495, which hash tag `{a}` implies.\nTo use pattern with a hash tag, see [Hash tags](https://redis.io/docs/latest/operate/oss_and_stack/reference/cluster-spec#hash-tags) in the Cluster specification for more information."},{"id":"examples","title":"Examples","role":"example","text":"Returns all key names that match a pattern\n\n**Difficulty:** Beginner\n\n**Commands:** MSET, KEYS\n\n**Complexity:**\n- MSET: O(N)\n- KEYS: O(N)\n\n**Available in:** Redis CLI, C, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of keys matching _pattern_.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of keys matching _pattern_."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> MSET firstname Jack lastname Stuntman age 35\nOK\n> KEYS *name*\n1) \"lastname\"\n2) \"firstname\"\n> KEYS a??\n1) \"age\"\n> KEYS *\n1) \"age\"\n2) \"lastname\"\n3) \"firstname\"","section_id":"examples"},{"id":"examples-ex1","language":"c","code":"redisReply *reply;\n\n    // Set up keys\n    reply = redisCommand(c, \"MSET %s %s %s %s %s %s\",\n        \"firstname\", \"Jack\", \"lastname\", \"Stuntman\", \"age\", \"35\");\n    printf(\"MSET firstname Jack lastname Stuntman age 35: %s\\n\", reply->str);\n    // >>> OK\n    freeReplyObject(reply);\n\n    // Keys matching *name*\n    reply = redisCommand(c, \"KEYS %s\", \"*name*\");\n    printf(\"KEYS *name*:\\n\");\n    for (size_t i = 0; i < reply->elements; i++) {\n        printf(\"  %s\\n\", reply->element[i]->str);\n    }\n    // >>> firstname\n    // >>> lastname\n    freeReplyObject(reply);\n\n    // Keys matching a??\n    reply = redisCommand(c, \"KEYS %s\", \"a??\");\n    printf(\"KEYS a??:\\n\");\n    for (size_t i = 0; i < reply->elements; i++) {\n        printf(\"  %s\\n\", reply->element[i]->str);\n    }\n    // >>> age\n    freeReplyObject(reply);\n\n    // All keys\n    reply = redisCommand(c, \"KEYS %s\", \"*\");\n    printf(\"KEYS *:\\n\");\n    for (size_t i = 0; i < reply->elements; i++) {\n        printf(\"  %s\\n\", reply->element[i]->str);\n    }\n    // >>> age\n    // >>> firstname\n    // >>> lastname\n    freeReplyObject(reply);","section_id":"examples"},{"id":"examples-ex2","language":"csharp","code":"bool keysResult1 = db.StringSet(\n            new KeyValuePair<RedisKey, RedisValue>[] {\n                new(\"firstname\", \"Jack\"),\n                new(\"lastname\", \"Stuntman\"),\n                new(\"age\", \"35\")\n            }\n        );\n        Console.WriteLine(keysResult1);  // >>> True\n\n        IServer server = muxer.GetServer(\"localhost:6379\");\n\n        RedisKey[] keysResult2 = server.Keys(pattern: \"*name*\").ToArray();\n        Array.Sort(keysResult2, (a, b) => a.ToString().CompareTo(b.ToString()));\n        Console.WriteLine(string.Join(\", \", keysResult2.Select(k => k.ToString())));  // >>> firstname, lastname\n\n        RedisKey[] keysResult3 = server.Keys(pattern: \"a??\").ToArray();\n        Console.WriteLine(string.Join(\", \", keysResult3.Select(k => k.ToString())));  // >>> age\n\n        RedisKey[] keysResult4 = server.Keys(pattern: \"*\").ToArray();\n        Array.Sort(keysResult4, (a, b) => a.ToString().CompareTo(b.ToString()));\n        Console.WriteLine(string.Join(\", \", keysResult4.Select(k => k.ToString())));  // >>> age, firstname, lastname","section_id":"examples"},{"id":"examples-ex3","language":"go","code":"keysResult1, err := rdb.MSet(ctx, \"firstname\", \"Jack\", \"lastname\", \"Stuntman\", \"age\", \"35\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(keysResult1) // >>> OK\n\n\tkeysResult2, err := rdb.Keys(ctx, \"*name*\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tsort.Strings(keysResult2)\n\tfmt.Println(keysResult2) // >>> [firstname lastname]\n\n\tkeysResult3, err := rdb.Keys(ctx, \"a??\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(keysResult3) // >>> [age]\n\n\tkeysResult4, err := rdb.Keys(ctx, \"*\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tsort.Strings(keysResult4)\n\tfmt.Println(keysResult4) // >>> [age firstname lastname]","section_id":"examples"},{"id":"examples-ex4","language":"java","code":"CompletableFuture<Void> keysExample = asyncCommands.mset(Map.of(\n                    \"firstname\", \"Jack\",\n                    \"lastname\", \"Stuntman\",\n                    \"age\", \"35\"\n            )).thenCompose(res1 -> {\n                System.out.println(res1); // >>> OK\n\n                return asyncCommands.keys(\"*name*\");\n            }).thenCompose(res2 -> {\n                Collections.sort(res2);\n                System.out.println(res2); // >>> [firstname, lastname]\n\n                return asyncCommands.keys(\"a??\");\n            }).thenCompose(res3 -> {\n                System.out.println(res3); // >>> [age]\n\n                return asyncCommands.keys(\"*\");\n            }).thenAccept(res4 -> {\n                Collections.sort(res4);\n                System.out.println(res4); // >>> [age, firstname, lastname]\n            }).toCompletableFuture();","section_id":"examples"},{"id":"examples-ex5","language":"java","code":"Mono<Void> keysExample = reactiveCommands.mset(Map.of(\n                    \"firstname\", \"Jack\",\n                    \"lastname\", \"Stuntman\",\n                    \"age\", \"35\"\n            )).doOnNext(res1 -> {\n                System.out.println(res1); // >>> OK\n            }).then(reactiveCommands.keys(\"*name*\").collectList()).doOnNext(res2 -> {\n                Collections.sort(res2);\n                System.out.println(res2); // >>> [firstname, lastname]\n            }).then(reactiveCommands.keys(\"a??\").collectList()).doOnNext(res3 -> {\n                System.out.println(res3); // >>> [age]\n            }).then(reactiveCommands.keys(\"*\").collectList()).doOnNext(res4 -> {\n                Collections.sort(res4);\n                System.out.println(res4); // >>> [age, firstname, lastname]\n            }).then();","section_id":"examples"},{"id":"examples-ex6","language":"java","code":"String keysResult1 = jedis.mset(\"firstname\", \"Jack\", \"lastname\", \"Stuntman\", \"age\", \"35\");\n        System.out.println(keysResult1); // >>> OK\n\n        Set<String> keysResult2 = jedis.keys(\"*name*\");\n        ArrayList<String> keysResult2List = new ArrayList<>(keysResult2);\n        Collections.sort(keysResult2List);\n        System.out.println(keysResult2List); // >>> [firstname, lastname]\n\n        Set<String> keysResult3 = jedis.keys(\"a??\");\n        System.out.println(keysResult3); // >>> [age]\n\n        Set<String> keysResult4 = jedis.keys(\"*\");\n        ArrayList<String> keysResult4List = new ArrayList<>(keysResult4);\n        Collections.sort(keysResult4List);\n        System.out.println(keysResult4List); // >>> [age, firstname, lastname]","section_id":"examples"},{"id":"examples-ex7","language":"javascript","code":"const keysRes1 = await client.mSet({ firstname: 'Jack', lastname: 'Stuntman', age: '35' });\nconsole.log(keysRes1); // OK\n\nconst keysRes2 = await client.keys('*name*');\nconsole.log(keysRes2.sort()); // ['firstname', 'lastname']\n\nconst keysRes3 = await client.keys('a??');\nconsole.log(keysRes3); // ['age']\n\nconst keysRes4 = await client.keys('*');\nconsole.log(keysRes4.sort()); // ['age', 'firstname', 'lastname']","section_id":"examples"},{"id":"examples-ex8","language":"javascript","code":"const keysRes1 = await redis.mset({ firstname: 'Jack', lastname: 'Stuntman', age: '35' });\nconsole.log(keysRes1); // >>> OK\n\nconst keysRes2 = await redis.keys('*name*');\nconsole.log(keysRes2.sort()); // >>> ['firstname', 'lastname']\n\nconst keysRes3 = await redis.keys('a??');\nconsole.log(keysRes3); // >>> ['age']\n\nconst keysRes4 = await redis.keys('*');\nconsole.log(keysRes4.sort()); // >>> ['age', 'firstname', 'lastname']","section_id":"examples"},{"id":"examples-ex9","language":"javascript","code":"const keysRes1 = await client.mSet({ firstname: 'Jack', lastname: 'Stuntman', age: '35' });\nconsole.log(keysRes1); // OK\n\nconst keysRes2 = await client.keys('*name*');\nconsole.log(keysRes2.sort()); // ['firstname', 'lastname']\n\nconst keysRes3 = await client.keys('a??');\nconsole.log(keysRes3); // ['age']\n\nconst keysRes4 = await client.keys('*');\nconsole.log(keysRes4.sort()); // ['age', 'firstname', 'lastname']","section_id":"examples"},{"id":"examples-ex10","language":"javascript","code":"const keysRes1 = await redis.mset({ firstname: 'Jack', lastname: 'Stuntman', age: '35' });\nconsole.log(keysRes1); // >>> OK\n\nconst keysRes2 = await redis.keys('*name*');\nconsole.log(keysRes2.sort()); // >>> ['firstname', 'lastname']\n\nconst keysRes3 = await redis.keys('a??');\nconsole.log(keysRes3); // >>> ['age']\n\nconst keysRes4 = await redis.keys('*');\nconsole.log(keysRes4.sort()); // >>> ['age', 'firstname', 'lastname']","section_id":"examples"},{"id":"examples-ex11","language":"php","code":"$keysResult1 = $r->mset(['firstname' => 'Jack', 'lastname' => 'Stuntman', 'age' => '35']);\n        echo $keysResult1 . PHP_EOL; // >>> OK\n\n        $keysResult2 = $r->keys('*name*');\n        sort($keysResult2);\n        echo implode(', ', $keysResult2) . PHP_EOL; // >>> firstname, lastname\n\n        $keysResult3 = $r->keys('a??');\n        echo implode(', ', $keysResult3) . PHP_EOL; // >>> age\n\n        $keysResult4 = $r->keys('*');\n        sort($keysResult4);\n        echo implode(', ', $keysResult4) . PHP_EOL; // >>> age, firstname, lastname","section_id":"examples"},{"id":"examples-ex12","language":"python","code":"res = r.mset({\"firstname\": \"Jack\", \"lastname\": \"Stuntman\", \"age\": \"35\"})\nprint(res)\n# >>> True\n\nres = r.keys(\"*name*\")\nprint(sorted(res))\n# >>> ['firstname', 'lastname']\n\nres = r.keys(\"a??\")\nprint(res)\n# >>> ['age']\n\nres = r.keys(\"*\")\nprint(sorted(res))\n# >>> ['age', 'firstname', 'lastname']","section_id":"examples"},{"id":"examples-ex13","language":"rust","code":"match r.mset(&[(\"firstname\", \"Jack\"), (\"lastname\", \"Stuntman\"), (\"age\", \"35\")]).await {\n            Ok(res) => {\n                let res: String = res;\n                println!(\"{res}\");    // >>> OK\n            },\n            Err(e) => {\n                println!(\"Error setting keys: {e}\");\n                return;\n            }\n        }\n\n        match r.keys::<&str, Vec<String>>(\"*name*\").await {\n            Ok(res) => {\n                let mut sorted_res = res.clone();\n                sorted_res.sort();\n                println!(\"{sorted_res:?}\");    // >>> [\"firstname\", \"lastname\"]\n            },\n            Err(e) => {\n                println!(\"Error getting keys: {e}\");\n                return;\n            }\n        }\n\n        match r.keys::<&str, Vec<String>>(\"a??\").await {\n            Ok(res) => {\n                println!(\"{res:?}\");    // >>> [\"age\"]\n            },\n            Err(e) => {\n                println!(\"Error getting keys: {e}\");\n                return;\n            }\n        }\n\n        match r.keys::<&str, Vec<String>>(\"*\").await {\n            Ok(res) => {\n                let mut sorted_res = res.clone();\n                sorted_res.sort();\n                println!(\"{sorted_res:?}\");    // >>> [\"age\", \"firstname\", \"lastname\"]\n            },\n            Err(e) => {\n                println!(\"Error getting keys: {e}\");\n                return;\n            }\n        }","section_id":"examples"},{"id":"examples-ex14","language":"rust","code":"match r.mset(&[(\"firstname\", \"Jack\"), (\"lastname\", \"Stuntman\"), (\"age\", \"35\")]) {\n            Ok(res) => {\n                let res: String = res;\n                println!(\"{res}\");    // >>> OK\n            },\n            Err(e) => {\n                println!(\"Error setting keys: {e}\");\n                return;\n            }\n        }\n\n        match r.keys::<&str, Vec<String>>(\"*name*\") {\n            Ok(res) => {\n                let mut sorted_res = res.clone();\n                sorted_res.sort();\n                println!(\"{sorted_res:?}\");    // >>> [\"firstname\", \"lastname\"]\n            },\n            Err(e) => {\n                println!(\"Error getting keys: {e}\");\n                return;\n            }\n        }\n\n        match r.keys::<&str, Vec<String>>(\"a??\") {\n            Ok(res) => {\n                println!(\"{res:?}\");    // >>> [\"age\"]\n            },\n            Err(e) => {\n                println!(\"Error getting keys: {e}\");\n                return;\n            }\n        }\n\n        match r.keys::<&str, Vec<String>>(\"*\") {\n            Ok(res) => {\n                let mut sorted_res = res.clone();\n                sorted_res.sort();\n                println!(\"{sorted_res:?}\");    // >>> [\"age\", \"firstname\", \"lastname\"]\n            },\n            Err(e) => {\n                println!(\"Error getting keys: {e}\");\n                return;\n            }\n        }","section_id":"examples"}]}
{"schema_version":2,"id":"commands/lastsave","title":"LASTSAVE","url":"https://redis.io/docs/latest/commands/lastsave/","summary":"Returns the Unix timestamp of the last successful save to disk.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"2d6fd7803094e0bf78b36a0457608754266a988670288f7450ca8fe1631ce01d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Return the UNIX TIME of the last DB save executed with success.\nA client may check if a [`BGSAVE`](https://redis.io/docs/latest/commands/bgsave) command succeeded reading the `LASTSAVE` value,\nthen issuing a [`BGSAVE`](https://redis.io/docs/latest/commands/bgsave) command and checking at regular intervals every N\nseconds if `LASTSAVE` changed. Redis considers the database saved successfully at startup."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): UNIX TIME of the last DB save executed with success.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): UNIX TIME of the last DB save executed with success."}],"examples":[]}
{"schema_version":2,"id":"commands/latency","title":"LATENCY","url":"https://redis.io/docs/latest/commands/latency/","summary":"A container for latency diagnostics commands.","since":"2.8.13","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"ddefd8c4edda14615eea0e358cb1b6cce0691f6ffe19207d3fec3384ebf11746","sections":[{"id":"content","title":"Content","role":"content","text":"This is a container command for latency diagnostics commands.\n\nTo see the list of available commands you can call [`LATENCY HELP`](https://redis.io/docs/latest/commands/latency-help)."}],"examples":[]}
{"schema_version":2,"id":"commands/latency-doctor","title":"LATENCY DOCTOR","url":"https://redis.io/docs/latest/commands/latency-doctor/","summary":"Returns a human-readable latency analysis report.","since":"2.8.13","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"ad3e9b6b8959421993107a9acacde1c3eaf75ffe4f743e3d8c2cbb8acb7661f2","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `LATENCY DOCTOR` command reports about different latency-related issues and advises about possible remedies.\n\nThis command is the most powerful analysis tool in the latency monitoring\nframework, and is able to provide additional statistical data like the average\nperiod between latency spikes, the median deviation, and a human-readable\nanalysis of the event. For certain events, like `fork`, additional information\nis provided, like the rate at which the system forks processes.\n\nThis is the output you should post in the Redis mailing list if you are\nlooking for help about Latency related issues."},{"id":"examples","title":"Examples","role":"example","text":"[code example]\n\n**Note:** the doctor has erratic psychological behaviors, so we recommend interacting with it carefully.\n\nFor more information refer to the [Latency Monitoring Framework page](https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/latency-monitor)."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): a human readable latency analysis report.\n\n**RESP3:**\n\n[Verbatim string reply](../../develop/reference/protocol-spec#verbatim-strings): a human readable latency analysis report."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"127.0.0.1:6379> latency doctor\n\nDave, I have observed latency spikes in this Redis instance.\nYou don't mind talking about it, do you Dave?\n\n1. command: 5 latency spikes (average 300ms, mean deviation 120ms,\n    period 73.40 sec). Worst all time event 500ms.\n\nI have a few advices for you:\n\n- Your current Slow Log configuration only logs events that are\n    slower than your configured latency monitor threshold. Please\n    use 'CONFIG SET slowlog-log-slower-than 1000'.\n- Check your Slow Log to understand what are the commands you are\n    running which are too slow to execute. Please check\n    http://redis.io/commands/slowlog for more information.\n- Deleting, expiring or evicting (because of maxmemory policy)\n    large objects is a blocking operation. If you have very large\n    objects that are often deleted, expired, or evicted, try to\n    fragment those objects into multiple smaller objects.","section_id":"examples"}]}
{"schema_version":2,"id":"commands/latency-graph","title":"LATENCY GRAPH","url":"https://redis.io/docs/latest/commands/latency-graph/","summary":"Returns a latency graph for an event.","since":"2.8.13","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"a6e6a9943eb6df5e2b4c994adf1f19171f231cdc430732035ced962997c99c37","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Produces an ASCII-art style graph for the specified event.\n\n`LATENCY GRAPH` lets you intuitively understand the latency trend of an `event` via state-of-the-art visualization. It can be used for quickly grasping the situation before resorting to means such parsing the raw data from [`LATENCY HISTORY`](https://redis.io/docs/latest/commands/latency-history) or external tooling.\n\nValid values for `event` are:\n* `active-defrag-cycle`\n* `aof-fsync-always`\n* `aof-stat`\n* `aof-rewrite-diff-write`\n* `aof-rename`\n* `aof-write`\n* `aof-write-active-child`\n* `aof-write-alone`\n* `aof-write-pending-fsync`\n* `command`\n* `expire-cycle`\n* `eviction-cycle`\n* `eviction-del`\n* `fast-command`\n* `fork`\n* `rdb-unlink-temp-file`"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>event</code></summary>\n\nThe latency event to graph.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]\n\nThe vertical labels under each graph column represent the amount of seconds,\nminutes, hours or days ago the event happened. For example \"15s\" means that the\nfirst graphed event happened 15 seconds ago.\n\nThe graph is normalized in the min-max scale so that the zero (the underscore\nin the lower row) is the minimum, and a # in the higher row is the maximum.\n\nFor more information refer to the [Latency Monitoring Framework page](https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/latency-monitor)."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): Latency graph\n\n**RESP3:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): Latency graph"}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"127.0.0.1:6379> latency reset command\n(integer) 0\n127.0.0.1:6379> debug sleep .1\nOK\n127.0.0.1:6379> debug sleep .2\nOK\n127.0.0.1:6379> debug sleep .3\nOK\n127.0.0.1:6379> debug sleep .5\nOK\n127.0.0.1:6379> debug sleep .4\nOK\n127.0.0.1:6379> latency graph command\ncommand - high 500 ms, low 101 ms (all time high 500 ms)\n--------------------------------------------------------------------------------\n   #_\n  _||\n _|||\n_||||\n\n11186\n542ss\nsss","section_id":"examples"}]}
{"schema_version":2,"id":"commands/latency-help","title":"LATENCY HELP","url":"https://redis.io/docs/latest/commands/latency-help/","summary":"Returns helpful text about the different subcommands.","since":"2.8.13","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"62190ae92efcff5fe8e6dfed287f6f2fc847d4d4ef9194d06570b60e5f41649b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `LATENCY HELP` command returns a helpful text describing the different\nsubcommands.\n\nFor more information refer to the [Latency Monitoring Framework page](https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/latency-monitor)."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of sub-commands and their descriptions.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of sub-commands and their descriptions."}],"examples":[]}
{"schema_version":2,"id":"commands/latency-histogram","title":"LATENCY HISTOGRAM","url":"https://redis.io/docs/latest/commands/latency-histogram/","summary":"Returns the cumulative distribution of latencies of a subset or all commands.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"f4b2906fb9cab8d413108f858af79161547e57ba31618cb1c0b47f709dbd9659","sections":[{"id":"overview","title":"Overview","role":"overview","text":"`LATENCY HISTOGRAM` returns a cumulative distribution of commands' latencies in histogram format.\n\nBy default, all available latency histograms are returned.\nYou can filter the reply by providing specific command names.\n\nEach histogram consists of the following fields:\n\n* Command name\n* The total calls for that command\n* A map of time buckets:\n  * Each bucket represents a latency range\n  * Each bucket covers twice the previous bucket's range\n  * Empty buckets are excluded from the reply\n  * The tracked latencies are between 1 nanosecond and roughly 1 second\n  * Everything above 1 second is considered +Inf\n  * At max, there will be log2(1,000,000,000) = 30 buckets\n\nThis command requires the extended latency monitoring feature to be enabled, which is the default.\nIf you need to enable it, call `CONFIG SET latency-tracking yes`.\n\nTo delete the latency histograms' data use the [`CONFIG RESETSTAT`](https://redis.io/docs/latest/commands/config-resetstat) command."},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>command [command ...]</code></summary>\n\nOne or more command names to return latency histograms for. If omitted, histograms for all commands are returned.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a map where each key is a command name, and each value is a map with the total calls, and an inner map of the histogram time buckets.\n\n**RESP3:**\n\n[Map reply](../../develop/reference/protocol-spec#maps): a map where each key is a command name, and each value is a map with the total calls, and an inner map of the histogram time buckets."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"127.0.0.1:6379> LATENCY HISTOGRAM set\n1# \"set\" =>\n   1# \"calls\" => (integer) 100000\n   2# \"histogram_usec\" =>\n      1# (integer) 1 => (integer) 99583\n      2# (integer) 2 => (integer) 99852\n      3# (integer) 4 => (integer) 99914\n      4# (integer) 8 => (integer) 99940\n      5# (integer) 16 => (integer) 99968\n      6# (integer) 33 => (integer) 100000","section_id":"examples"}]}
{"schema_version":2,"id":"commands/latency-history","title":"LATENCY HISTORY","url":"https://redis.io/docs/latest/commands/latency-history/","summary":"Returns timestamp-latency samples for an event.","since":"2.8.13","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"fc07fdffd5c38c8005daa673855bb00053c0044b007ff9746232b91213fd594d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `LATENCY HISTORY` command returns the raw data of the `event`'s latency spikes time series.\n\nThis is useful to an application that wants to fetch raw data in order to perform monitoring, display graphs, and so forth.\n\nThe command will return up to 160 timestamp-latency pairs for the `event`.\n\nValid values for `event` are:\n* `active-defrag-cycle`\n* `aof-fsync-always`\n* `aof-stat`\n* `aof-rewrite-diff-write`\n* `aof-rename`\n* `aof-write`\n* `aof-write-active-child`\n* `aof-write-alone`\n* `aof-write-pending-fsync`\n* `command`\n* `expire-cycle`\n* `eviction-cycle`\n* `eviction-del`\n* `fast-command`\n* `fork`\n* `rdb-unlink-temp-file`"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>event</code></summary>\n\nThe latency event to return the history for.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]\n\nFor more information refer to the [Latency Monitoring Framework page](https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/latency-monitor)."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): an array where each element is a two elements array representing the timestamp and the latency of the event.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): an array where each element is a two elements array representing the timestamp and the latency of the event."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"127.0.0.1:6379> latency history command\n1) 1) (integer) 1405067822\n   2) (integer) 251\n2) 1) (integer) 1405067941\n   2) (integer) 1001","section_id":"examples"}]}
{"schema_version":2,"id":"commands/latency-latest","title":"LATENCY LATEST","url":"https://redis.io/docs/latest/commands/latency-latest/","summary":"Returns the latest latency samples for all events.","since":"2.8.13","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"5bd19b9e3220337ecd8cd3c44a3bf59f8a8c3cb7a495a810d9ede8f504bfdd27","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `LATENCY LATEST` command reports the latest latency events logged.\n\nEach reported event has the following fields:\n\n* Event name.\n* Unix timestamp of the latest latency spike for the event.\n* Latest event latency in millisecond.\n* All-time maximum latency for this event.\n\n\"All-time\" means the maximum latency since the Redis instance was\nstarted, or the time that events were reset [`LATENCY RESET`](https://redis.io/docs/latest/commands/latency-reset)."},{"id":"examples","title":"Examples","role":"example","text":"[code example]\n\nFor more information refer to the [Latency Monitoring Framework page](https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/latency-monitor)."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): an array where each element is a four elements array representing the event's name, timestamp, latest and all-time latency measurements.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): an array where each element is a four elements array representing the event's name, timestamp, latest and all-time latency measurements."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"127.0.0.1:6379> debug sleep 1\nOK\n(1.00s)\n127.0.0.1:6379> debug sleep .25\nOK\n127.0.0.1:6379> latency latest\n1) 1) \"command\"\n   2) (integer) 1405067976\n   3) (integer) 251\n   4) (integer) 1001","section_id":"examples"}]}
{"schema_version":2,"id":"commands/latency-reset","title":"LATENCY RESET","url":"https://redis.io/docs/latest/commands/latency-reset/","summary":"Resets the latency data for one or more events.","since":"2.8.13","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"fc6d105ebbbd4af4e8334e6033de8333f60fc9a8755a072e26cb948e63d5322d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `LATENCY RESET` command resets the latency spikes time series of all, or only some, events.\n\nWhen the command is called without arguments, it resets all the\nevents, discarding the currently logged latency spike events, and resetting\nthe maximum event time register.\n\nIt is possible to reset only specific events by providing the `event` names\nas arguments.\n\nValid values for `event` are:\n* `active-defrag-cycle`\n* `aof-fsync-always`\n* `aof-stat`\n* `aof-rewrite-diff-write`\n* `aof-rename`\n* `aof-write`\n* `aof-write-active-child`\n* `aof-write-alone`\n* `aof-write-pending-fsync`\n* `command`\n* `expire-cycle`\n* `eviction-cycle`\n* `eviction-del`\n* `fast-command`\n* `fork`\n* `rdb-unlink-temp-file`\n\nFor more information refer to the [Latency Monitoring Framework page](https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/latency-monitor)."},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>event [event ...]</code></summary>\n\nOne or more latency events to reset. If omitted, all events are reset.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of event time series that were reset.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of event time series that were reset."}],"examples":[]}
{"schema_version":2,"id":"commands/lcs","title":"LCS","url":"https://redis.io/docs/latest/commands/lcs/","summary":"Finds the longest common substring.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-06-15T06:26:34-07:00","page_type":"content","content_hash":"c2c1d3120325a28021392e61c3d438ad0a0fc7f685c1b646bca43ce9fb6cf913","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The LCS command implements the longest common subsequence algorithm. Note that this is different than the longest common string algorithm, since matching characters in the string does not need to be contiguous.\n\nFor example, the `LCS` between \"foo\" and \"fao\" is \"fo\", since scanning the two strings from left to right, the longest common set of characters is composed of the first \"f\" and then the \"o\".\n\nLCS is very useful in order to evaluate how similar two strings are. Strings can represent many things. For instance if two strings are DNA sequences, the LCS will provide a measure of similarity between the two DNA sequences. If the strings represent some text edited by some user, the LCS could represent how different the new text is compared to the old one, and so forth.\n\nNote that this algorithm runs in `O(N*M)` time, where N is the length of the first string and M is the length of the second string. So either use a separate Redis instance to run this algorithm, or make sure to run it against very small strings."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key1</code></summary>\n\nThe name of the first key.\n\n</details>\n\n<details open><summary><code>key2</code></summary>\n\nThe name of the second key.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>LEN</code></summary>\n\nReturn the length of the longest common subsequence instead of the subsequence itself.\n\n</details>\n\n<details open><summary><code>IDX</code></summary>\n\nReturn the positions of the matches instead of the matching string.\n\n</details>\n\n<details open><summary><code>MINMATCHLEN min-match-len</code></summary>\n\nWhen used with `IDX`, only return matches at least `min-match-len` characters long.\n\n</details>\n\n<details open><summary><code>WITHMATCHLEN</code></summary>\n\nWhen used with `IDX`, include the length of each match in the result.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]\n\nSometimes you need just the length of the match:\n\n[code example]\n\nHowever what is often very useful is to know the match position in each string:\n\n[code example]\n\nMatches are produced from the last one to the first one, since this is how\nthe algorithm works, and it more efficient to emit the matches in the same order.\nThe above array means that the first match (second element of the array)\nis between positions 2-3 of the first string and 0-1 of the second.\nThen there is another match between 4-7 and 5-8.\n\nTo restrict the list of matches to the ones of a given minimal length:\n\n[code example]\n\nFinally, you may want to get the match length as well:\n\n[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the longest common subsequence.\n* [Integer reply](../../develop/reference/protocol-spec#integers): the length of the longest common subsequence when _LEN_ is given.\n* [Array reply](../../develop/reference/protocol-spec#arrays): an array with the LCS length and all the ranges in both the strings when _IDX_ is given.\n\n**RESP3:**\n\nOne of the following:\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the longest common subsequence.\n* [Integer reply](../../develop/reference/protocol-spec#integers): the length of the longest common subsequence when _LEN_ is given.\n* [Map reply](../../develop/reference/protocol-spec#maps): a map with the LCS length and all the ranges in both the strings when _IDX_ is given."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> MSET key1 ohmytext key2 mynewtext\nOK\n> LCS key1 key2\n\"mytext\"","section_id":"examples"},{"id":"examples-ex1","language":"plaintext","code":"> LCS key1 key2 LEN\n(integer) 6","section_id":"examples"},{"id":"examples-ex2","language":"plaintext","code":"> LCS key1 key2 IDX\n1) \"matches\"\n2) 1) 1) 1) (integer) 4\n         2) (integer) 7\n      2) 1) (integer) 5\n         2) (integer) 8\n   2) 1) 1) (integer) 2\n         2) (integer) 3\n      2) 1) (integer) 0\n         2) (integer) 1\n3) \"len\"\n4) (integer) 6","section_id":"examples"},{"id":"examples-ex3","language":"plaintext","code":"> LCS key1 key2 IDX MINMATCHLEN 4\n1) \"matches\"\n2) 1) 1) 1) (integer) 4\n         2) (integer) 7\n      2) 1) (integer) 5\n         2) (integer) 8\n3) \"len\"\n4) (integer) 6","section_id":"examples"},{"id":"examples-ex4","language":"plaintext","code":"> LCS key1 key2 IDX MINMATCHLEN 4 WITHMATCHLEN\n1) \"matches\"\n2) 1) 1) 1) (integer) 4\n         2) (integer) 7\n      2) 1) (integer) 5\n         2) (integer) 8\n      3) (integer) 4\n3) \"len\"\n4) (integer) 6","section_id":"examples"}]}
{"schema_version":2,"id":"commands/lindex","title":"LINDEX","url":"https://redis.io/docs/latest/commands/lindex/","summary":"Returns an element from a list by its index.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"64d82fbe1c75b11f31d7005718c8cff52615dd9cf1408a779e0b06a42728f047","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns the element at index `index` in the list stored at `key`.\nThe index is zero-based, so `0` means the first element, `1` the second element\nand so on.\nNegative indices can be used to designate elements starting at the tail of the\nlist.\nHere, `-1` means the last element, `-2` means the penultimate and so forth.\n\nWhen the value at `key` is not a list, an error is returned."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the list.\n\n</details>\n\n<details open><summary><code>index</code></summary>\n\nThe zero-based index of the element to return. Negative indexes count from the tail (`-1` is the last element).\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> LPUSH mylist \"World\"\n(integer) 1\nredis> LPUSH mylist \"Hello\"\n(integer) 2\nredis> LINDEX mylist 0\n\"Hello\"\nredis> LINDEX mylist -1\n\"World\"\nredis> LINDEX mylist 3\n(nil)"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): when _index_ is out of range.\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the requested element.\n\n**RESP3:**\n\nOne of the following:\n* [Null reply](../../develop/reference/protocol-spec#nulls): when _index_ is out of range.\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the requested element."}],"examples":[]}
{"schema_version":2,"id":"commands/linsert","title":"LINSERT","url":"https://redis.io/docs/latest/commands/linsert/","summary":"Inserts an element before or after another element in a list.","since":"2.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"7f292070493efbfa637dc490fcdd170ff4823a1f60290f86f43010566ff66f33","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Inserts `element` in the list stored at `key` either before or after the reference\nvalue `pivot`.\n\nWhen `key` does not exist, it is considered an empty list and no operation is\nperformed.\n\nAn error is returned when `key` exists but does not hold a list value."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the list.\n\n</details>\n\n<details open><summary><code>BEFORE | AFTER</code></summary>\n\nWhether to insert `element` before or after `pivot`.\n\n</details>\n\n<details open><summary><code>pivot</code></summary>\n\nThe existing element to insert relative to.\n\n</details>\n\n<details open><summary><code>element</code></summary>\n\nThe value to insert.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> RPUSH mylist \"Hello\"\n(integer) 1\nredis> RPUSH mylist \"World\"\n(integer) 2\nredis> LINSERT mylist BEFORE \"World\" \"There\"\n(integer) 3\nredis> LRANGE mylist 0 -1\n1) \"Hello\"\n2) \"There\"\n3) \"World\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): the list length after a successful insert operation.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0` when the key doesn't exist.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `-1` when the pivot wasn't found.\n\n**RESP3:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): the list length after a successful insert operation.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0` when the key doesn't exist.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `-1` when the pivot wasn't found."}],"examples":[]}
{"schema_version":2,"id":"commands/llen","title":"LLEN","url":"https://redis.io/docs/latest/commands/llen/","summary":"Returns the length of a list.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-08T12:05:04+02:00","page_type":"content","content_hash":"8dfc5414cffc25f49acfc64f069d792786476e250231b3de58aa18249ceab354","sections":[{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the list.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"Foundational: Get the length of a list using LLEN (returns 0 if key doesn't exist, useful for checking list size)\n\n**Difficulty:** Beginner\n\n**Commands:** LPUSH, LLEN\n\n**Complexity:**\n- LPUSH: O(1)\n- LLEN: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the length of the list.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the length of the list."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> LPUSH mylist \"World\"\n(integer) 1\nredis> LPUSH mylist \"Hello\"\n(integer) 2\nredis> LLEN mylist\n(integer) 2","section_id":"examples"},{"id":"examples-ex1","language":"csharp","code":"long lLenResult1 = db.ListLeftPush(\"mylist\", \"World\");\n        Console.WriteLine(lLenResult1); // >>> 1\n\n        long lLenResult2 = db.ListLeftPush(\"mylist\", \"Hello\");\n        Console.WriteLine(lLenResult2); // >>> 2\n\n        long lLenResult3 = db.ListLength(\"mylist\");\n        Console.WriteLine(lLenResult3); // >>> 2","section_id":"examples"},{"id":"examples-ex2","language":"go","code":"lPushResult1, err := rdb.LPush(ctx, \"mylist\", \"World\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(lPushResult1) // >>> 1\n\n\tlPushResult2, err := rdb.LPush(ctx, \"mylist\", \"Hello\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(lPushResult2) // >>> 2\n\n\tlLenResult, err := rdb.LLen(ctx, \"mylist\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(lLenResult) // >>> 2","section_id":"examples"},{"id":"examples-ex3","language":"java","code":"CompletableFuture<Void> llen = asyncCommands.lpush(\"mylist\", \"World\").thenCompose(res11 -> {\n                System.out.println(res11); // >>> 1\n\n                return asyncCommands.lpush(\"mylist\", \"Hello\");\n            }).thenCompose(res12 -> {\n                System.out.println(res12); // >>> 2\n\n                return asyncCommands.llen(\"mylist\");\n            })\n                    .thenAccept(res13 -> System.out.println(res13)) // >>> 2\n                    .toCompletableFuture();","section_id":"examples"},{"id":"examples-ex4","language":"java","code":"Mono<Void> llen = reactiveCommands.lpush(\"mylist\", \"World\").doOnNext(res11 -> {\n                System.out.println(res11); // >>> 1\n            }).flatMap(res11 -> reactiveCommands.lpush(\"mylist\", \"Hello\")).doOnNext(res12 -> {\n                System.out.println(res12); // >>> 2\n            }).flatMap(res12 -> reactiveCommands.llen(\"mylist\")).doOnNext(res13 -> {\n                System.out.println(res13); // >>> 2\n            }).then();","section_id":"examples"},{"id":"examples-ex5","language":"java","code":"long lLenResult1 = jedis.lpush(\"mylist\", \"World\");\n        System.out.println(lLenResult1); // >>> 1\n\n        long lLenResult2 = jedis.lpush(\"mylist\", \"Hello\");\n        System.out.println(lLenResult2); // >>> 2\n\n        long lLenResult3 = jedis.llen(\"mylist\");\n        System.out.println(lLenResult3); // >>> 2","section_id":"examples"},{"id":"examples-ex6","language":"javascript","code":"const res11 = await client.lPush('mylist', 'World');\nconsole.log(res11); // 1\n\nconst res12 = await client.lPush('mylist', 'Hello');\nconsole.log(res12); // 2\n\nconst res13 = await client.lLen('mylist');\nconsole.log(res13); // 2","section_id":"examples"},{"id":"examples-ex7","language":"javascript","code":"const res11 = await redis.lpush('mylist', 'World');\nconsole.log(res11); // >>> 1\n\nconst res12 = await redis.lpush('mylist', 'Hello');\nconsole.log(res12); // >>> 2\n\nconst res13 = await redis.llen('mylist');\nconsole.log(res13); // >>> 2","section_id":"examples"},{"id":"examples-ex8","language":"javascript","code":"const res11 = await client.lPush('mylist', 'World');\nconsole.log(res11); // 1\n\nconst res12 = await client.lPush('mylist', 'Hello');\nconsole.log(res12); // 2\n\nconst res13 = await client.lLen('mylist');\nconsole.log(res13); // 2","section_id":"examples"},{"id":"examples-ex9","language":"javascript","code":"const res11 = await redis.lpush('mylist', 'World');\nconsole.log(res11); // >>> 1\n\nconst res12 = await redis.lpush('mylist', 'Hello');\nconsole.log(res12); // >>> 2\n\nconst res13 = await redis.llen('mylist');\nconsole.log(res13); // >>> 2","section_id":"examples"},{"id":"examples-ex10","language":"php","code":"$res11 = $r->lpush('mylist', 'World');\n        echo $res11 . PHP_EOL;\n        // >>> 1\n\n        $res12 = $r->lpush('mylist', 'Hello');\n        echo $res12 . PHP_EOL;\n        // >>> 2\n\n        $res13 = $r->llen('mylist');\n        echo $res13 . PHP_EOL;\n        // >>> 2","section_id":"examples"},{"id":"examples-ex11","language":"python","code":"res11 = r.lpush(\"mylist\", \"World\")\nprint(res11) # >>> 1\n\nres12 = r.lpush(\"mylist\", \"Hello\")\nprint(res12) # >>> 2\n\nres13 = r.llen(\"mylist\")\nprint(res13)  # >>> 2","section_id":"examples"},{"id":"examples-ex12","language":"ruby","code":"res11 = r.lpush('mylist', 'World')\nputs res11 # >>> 1\n\nres12 = r.lpush('mylist', 'Hello')\nputs res12 # >>> 2\n\nres13 = r.llen('mylist')\nputs res13 # >>> 2","section_id":"examples"},{"id":"examples-ex13","language":"rust","code":"if let Ok(res11) = r.lpush(\"mylist\", \"World\").await {\n            let res11: i32 = res11;\n            println!(\"{res11}\"); // >>> 1\n        }\n\n        if let Ok(res12) = r.lpush(\"mylist\", \"Hello\").await {\n            let res12: i32 = res12;\n            println!(\"{res12}\"); // >>> 2\n        }\n\n        if let Ok(res13) = r.llen(\"mylist\").await {\n            let res13: i32 = res13;\n            println!(\"{res13}\"); // >>> 2\n        }","section_id":"examples"},{"id":"examples-ex14","language":"rust","code":"if let Ok(res11) = r.lpush(\"mylist\", \"World\") {\n            let res11: i32 = res11;\n            println!(\"{res11}\"); // >>> 1\n        }\n\n        if let Ok(res12) = r.lpush(\"mylist\", \"Hello\") {\n            let res12: i32 = res12;\n            println!(\"{res12}\"); // >>> 2\n        }\n\n        if let Ok(res13) = r.llen(\"mylist\") {\n            let res13: i32 = res13;\n            println!(\"{res13}\"); // >>> 2\n        }","section_id":"examples"}]}
{"schema_version":2,"id":"commands/lmove","title":"LMOVE","url":"https://redis.io/docs/latest/commands/lmove/","summary":"Returns an element after popping it from one list and pushing it to another. Deletes the list if the last element was moved.","since":"6.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"a8c0f747e0d4f0cb6cd9a9881490d955741c44ce624e96e771124a8695e0f02e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nAtomically returns and removes the first/last element (head/tail depending on\nthe `wherefrom` argument) of the list stored at `source`, and pushes the\nelement at the first/last element (head/tail depending on the `whereto`\nargument) of the list stored at `destination`.\n\nFor example: consider `source` holding the list `a,b,c`, and `destination`\nholding the list `x,y,z`.\nExecuting `LMOVE source destination RIGHT LEFT` results in `source` holding\n`a,b` and `destination` holding `c,x,y,z`.\n\nIf `source` does not exist, the value `nil` is returned and no operation is\nperformed.\nIf `source` and `destination` are the same, the operation is equivalent to\nremoving the first/last element from the list and pushing it as first/last\nelement of the list, so it can be considered as a list rotation command (or a\nno-op if `wherefrom` is the same as `whereto`).\n\nThis command comes in place of the now deprecated [`RPOPLPUSH`](https://redis.io/docs/latest/commands/rpoplpush). Doing\n`LMOVE RIGHT LEFT` is equivalent."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>source</code></summary>\n\nThe key of the source list.\n\n</details>\n\n<details open><summary><code>destination</code></summary>\n\nThe key of the destination list.\n\n</details>\n\n<details open><summary><code>LEFT | RIGHT</code></summary>\n\nThe end of `source` to pop the element from: `LEFT` (head) or `RIGHT` (tail).\n\n</details>\n\n<details open><summary><code>LEFT | RIGHT</code></summary>\n\nThe end of `destination` to push the element to: `LEFT` (head) or `RIGHT` (tail).\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> RPUSH mylist \"one\"\n(integer) 1\nredis> RPUSH mylist \"two\"\n(integer) 2\nredis> RPUSH mylist \"three\"\n(integer) 3\nredis> LMOVE mylist myotherlist RIGHT LEFT\n\"three\"\nredis> LMOVE mylist myotherlist LEFT RIGHT\n\"one\"\nredis> LRANGE mylist 0 -1\n1) \"two\"\nredis> LRANGE myotherlist 0 -1\n1) \"three\"\n2) \"one\""},{"id":"details","title":"Details","role":"content","text":""},{"id":"pattern-reliable-queue","title":"Pattern: reliable queue","role":"content","text":"Redis is often used as a messaging server to implement processing of background\njobs or other kinds of messaging tasks.\nA simple form of queue is often obtained pushing values into a list in the\nproducer side, and waiting for this values in the consumer side using [`RPOP`](https://redis.io/docs/latest/commands/rpop)\n(using polling), or [`BRPOP`](https://redis.io/docs/latest/commands/brpop) if the client is better served by a blocking\noperation.\n\nHowever in this context the obtained queue is not _reliable_ as messages can\nbe lost, for example in the case there is a network problem or if the consumer\ncrashes just after the message is received but it is still to process.\n\n`LMOVE` (or [`BLMOVE`](https://redis.io/docs/latest/commands/blmove) for the blocking variant) offers a way to avoid\nthis problem: the consumer fetches the message and at the same time pushes it\ninto a _processing_ list.\nIt will use the [`LREM`](https://redis.io/docs/latest/commands/lrem) command in order to remove the message from the\n_processing_ list once the message has been processed.\n\nAn additional client may monitor the _processing_ list for items that remain\nthere for too much time, and will push those timed out items into the queue\nagain if needed."},{"id":"pattern-circular-list","title":"Pattern: circular list","role":"content","text":"Using `LMOVE` with the same source and destination key, a client can visit\nall the elements of an N-elements list, one after the other, in O(N) without\ntransferring the full list from the server to the client using a single [`LRANGE`](https://redis.io/docs/latest/commands/lrange)\noperation.\n\nThe above pattern works even in the following conditions:\n\n* There are multiple clients rotating the list: they'll fetch different\n  elements, until all the elements of the list are visited, and the process\n  restarts.\n* Even if other clients are actively pushing new items at the end of the list.\n\nThe above makes it very simple to implement a system where a set of items must\nbe processed by N workers continuously as fast as possible.\nAn example is a monitoring system that must check that a set of web sites are\nreachable, with the smallest delay possible, using a number of parallel workers.\n\nNote that this implementation of workers is trivially scalable and reliable,\nbecause even if a message is lost the item is still in the queue and will be\nprocessed at the next iteration."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the element being popped and pushed.\n\n**RESP3:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the element being popped and pushed."}],"examples":[]}
{"schema_version":2,"id":"commands/lmovem","title":"LMOVEM","url":"https://redis.io/docs/latest/commands/lmovem/","summary":"Moves up to (or exactly) a number of elements from one list to another and returns them. Deletes the source list if it becomes empty.","since":"8.10.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"d61205472679770426e2dbf6c71b4b153cce6fb9044b35b5fc7b740c49e5fee9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nAtomically moves one or more elements from the list stored at `source` to the\nlist stored at `destination` and returns the moved elements.\n`LMOVEM` is the multiple-element version of [`LMOVE`](https://redis.io/docs/latest/commands/lmove): without the\noptional `how-many` block, it behaves exactly like [`LMOVE`](https://redis.io/docs/latest/commands/lmove) but returns\nthe moved element as a one-element array."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>source</code></summary>\n\nThe key of the source list.\n\n</details>\n\n<details open><summary><code>destination</code></summary>\n\nThe key of the destination list.\n\n</details>\n\n<details open><summary><code>LEFT | RIGHT</code></summary>\n\nThe end of `source` to pop elements from: `LEFT` (head) or `RIGHT` (tail).\n\n</details>\n\n<details open><summary><code>LEFT | RIGHT</code></summary>\n\nThe end of `destination` to push elements to: `LEFT` (head) or `RIGHT` (tail).\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"The `how-many` block controls how many elements are moved and in what order.\nWhen you include it, you must provide both a selector (`COUNT` or `EXACTLY`)\nand an ordering (`OBO` or `BULK`).\n\n<details open><summary><code>COUNT count | EXACTLY exactly</code></summary>\n\nThe number of elements to move:\n\n- `COUNT count` moves up to `count` elements. If `source` holds fewer than\n  `count` elements, all of them are moved. This matches the `count` semantics of\n  [`LPOP`](https://redis.io/docs/latest/commands/lpop) and [`LMPOP`](https://redis.io/docs/latest/commands/lmpop).\n- `EXACTLY exactly` moves exactly `exactly` elements. If `source` holds fewer\n  than `exactly` elements, nothing is moved and the command returns nil.\n\n</details>\n\n<details open><summary><code>OBO | BULK</code></summary>\n\nThe order in which elements are pushed onto `destination`:\n\n- `OBO` (one by one) moves elements individually: each element is popped from\n  `source` and pushed to `destination` before the next one is moved.\n- `BULK` moves all of the elements at once, preserving their relative order.\n\nThe two orderings differ only in the resulting arrangement of elements. See\n[Element ordering](#element-ordering) for details.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> rpush mylist 1 2 3 4\n(integer) 4\nredis> LMOVEM mylist myotherlist LEFT LEFT COUNT 2 OBO\n1) \"2\"\n2) \"1\"\nredis> LRANGE mylist 0 -1\n1) \"3\"\n2) \"4\"\nredis> LRANGE myotherlist 0 -1\n1) \"2\"\n2) \"1\"\nredis> LMOVEM mylist myotherlist LEFT LEFT EXACTLY 5 BULK\n(nil)\nredis> RPUSH somelist a b c\n(integer) 3\nredis> LMOVEM somelist somelist LEFT LEFT EXACTLY 2 OBO\n1) \"b\"\n2) \"a\"\nredis> LRANGE somelist 0 -1\n1) \"b\"\n2) \"a\"\n3) \"c\""},{"id":"details","title":"Details","role":"content","text":""},{"id":"moving-multiple-elements","title":"Moving multiple elements","role":"content","text":"`LMOVEM` moves elements from one end of `source` (selected by the first\n`LEFT | RIGHT` argument) to one end of `destination` (selected by the second\n`LEFT | RIGHT` argument), and returns the moved elements as an array. If\n`source` does not exist, no operation is performed and the command returns an\nempty array (or nil when `EXACTLY` is used).\n\nThe `COUNT` and `EXACTLY` selectors decide how many elements are moved:\n\n- With `COUNT count`, the command moves up to `count` elements. If `source`\n  holds fewer than `count` elements, all available elements are moved.\n- With `EXACTLY exactly`, the command moves the requested number of elements\n  only if `source` holds at least that many. Otherwise it moves nothing and\n  returns nil. This is useful when elements form fixed-size groups (for example,\n  pairs or triplets) that must be moved together or not at all."},{"id":"element-ordering","title":"Element ordering","role":"content","text":"The `OBO` and `BULK` orderings determine how the moved elements are arranged on\n`destination`:\n\n- `OBO` (one by one) pops and pushes each element in turn, so an element popped\n  earlier ends up \"beneath\" one popped later at the destination end.\n- `BULK` moves the whole batch at once, preserving the elements' relative order.\n\nWhen `source` and `destination` are the same key, this difference becomes a\nuseful side effect. `BULK` leaves the list unchanged (the elements are removed\nand re-added in the same order), so it is effectively a no-op. `OBO`, however,\nreverses the moved elements in place. For example, with `mylist` holding\n`a, b, c`, running `LMOVEM mylist mylist LEFT LEFT EXACTLY 2 OBO` pops `a` then\n`b` and pushes each back to the head, leaving `mylist` as `b, a, c`."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Array reply](../../develop/reference/protocol-spec#arrays): the moved elements, in destination order.\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): if `EXACTLY` was used and `source` did not hold enough elements.\n\n**RESP3:**\n\nOne of the following:\n* [Array reply](../../develop/reference/protocol-spec#arrays): the moved elements, in destination order.\n* [Null reply](../../develop/reference/protocol-spec#nulls): if `EXACTLY` was used and `source` did not hold enough elements."},{"id":"see-also","title":"See also","role":"related","text":"[`LMOVE`](https://redis.io/docs/latest/commands/lmove) | [`BLMOVEM`](https://redis.io/docs/latest/commands/blmovem) | [`LMPOP`](https://redis.io/docs/latest/commands/lmpop) | [`RPOPLPUSH`](https://redis.io/docs/latest/commands/rpoplpush)"},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Redis lists](https://redis.io/docs/latest/develop/data-types/lists)"}],"examples":[]}
{"schema_version":2,"id":"commands/lmpop","title":"LMPOP","url":"https://redis.io/docs/latest/commands/lmpop/","summary":"Returns multiple elements from a list after removing them. Deletes the list if the last element was popped.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"4872ce09f96cca002ffff1bfae2ea3bc10b209ebe530c903b82de3ed2b6811df","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nPops one or more elements from the first non-empty list key from the list of provided key names.\n\n`LMPOP` and [`BLMPOP`](https://redis.io/docs/latest/commands/blmpop) are similar to the following, more limited, commands:\n\n- [`LPOP`](https://redis.io/docs/latest/commands/lpop) or [`RPOP`](https://redis.io/docs/latest/commands/rpop) which take only one key, and can return multiple elements.\n- [`BLPOP`](https://redis.io/docs/latest/commands/blpop) or [`BRPOP`](https://redis.io/docs/latest/commands/brpop) which take multiple keys, but return only one element from just one key.\n\nSee [`BLMPOP`](https://redis.io/docs/latest/commands/blmpop) for the blocking variant of this command.\n\nElements are popped from either the left or right of the first non-empty list based on the passed argument.\nThe number of returned elements is limited to the lower between the non-empty list's length, and the count argument (which defaults to 1)."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>numkeys</code></summary>\n\nThe number of keys that follow.\n\n</details>\n\n<details open><summary><code>key [key ...]</code></summary>\n\nOne or more keys of lists to operate on.\n\n</details>\n\n<details open><summary><code>LEFT | RIGHT</code></summary>\n\nThe end of the list to pop elements from: `LEFT` (head) or `RIGHT` (tail).\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>COUNT count</code></summary>\n\nThe number of elements to pop. Defaults to 1.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> LMPOP 2 non1 non2 LEFT COUNT 10\n(nil)\nredis> LPUSH mylist \"one\" \"two\" \"three\" \"four\" \"five\"\n(integer) 5\nredis> LMPOP 1 mylist LEFT\n1) \"mylist\"\n2) 1) \"five\"\nredis> LRANGE mylist 0 -1\n1) \"four\"\n2) \"three\"\n3) \"two\"\n4) \"one\"\nredis> LMPOP 1 mylist RIGHT COUNT 10\n1) \"mylist\"\n2) 1) \"one\"\n   2) \"two\"\n   3) \"three\"\n   4) \"four\"\nredis> LPUSH mylist \"one\" \"two\" \"three\" \"four\" \"five\"\n(integer) 5\nredis> LPUSH mylist2 \"a\" \"b\" \"c\" \"d\" \"e\"\n(integer) 5\nredis> LMPOP 2 mylist mylist2 right count 3\n1) \"mylist\"\n2) 1) \"one\"\n   2) \"two\"\n   3) \"three\"\nredis> LRANGE mylist 0 -1\n1) \"five\"\n2) \"four\"\nredis> LMPOP 2 mylist mylist2 right count 5\n1) \"mylist\"\n2) 1) \"four\"\n   2) \"five\"\nredis> LMPOP 2 mylist mylist2 right count 10\n1) \"mylist2\"\n2) 1) \"a\"\n   2) \"b\"\n   3) \"c\"\n   4) \"d\"\n   5) \"e\"\nredis> EXISTS mylist mylist2\n(integer) 0"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): if no element could be popped.\n* [Array reply](../../develop/reference/protocol-spec#arrays): a two-element array with the first element being the name of the key from which elements were popped and the second element being an array of elements.\n\n**RESP3:**\n\nOne of the following:\n* [Null reply](../../develop/reference/protocol-spec#nulls): if no element could be popped.\n* [Array reply](../../develop/reference/protocol-spec#arrays): a two-element array with the first element being the name of the key from which elements were popped and the second element being an array of elements."}],"examples":[]}
{"schema_version":2,"id":"commands/lolwut","title":"LOLWUT","url":"https://redis.io/docs/latest/commands/lolwut/","summary":"Displays computer art and the Redis version","since":"5.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-06-15T06:26:34-07:00","page_type":"content","content_hash":"cdb604e9be3da09b03a52e911211e5cd4c1f25859f5539db6ec41fc83ec44f1e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The LOLWUT command displays the Redis version: however as a side effect of\ndoing so, it also creates a piece of generative computer art that is different\nwith each version of Redis. The command was introduced in Redis 5 and announced\nwith this [blog post](http://antirez.com/news/123).\n\nBy default the `LOLWUT` command will display the piece corresponding to the\ncurrent Redis version, however it is possible to display a specific version\nusing the following form:\n\n    LOLWUT VERSION 5 ... other optional arguments ...\n\nOf course the \"5\" above is an example. Each LOLWUT version takes a different\nset of arguments in order to change the output. The user is encouraged to\nplay with it to discover how the output changes adding more numerical\narguments.\n\nLOLWUT wants to be a reminder that there is more in programming than just\nputting some code together in order to create something useful. Every\nLOLWUT version should have the following properties:\n\n1. It should display some computer art. There are no limits as long as the output works well in a normal terminal display. However the output should not be limited to graphics (like LOLWUT 5 and 6 actually do), but can be generative poetry and other non graphical things.\n2. LOLWUT output should be completely useless. Displaying some useful Redis internal metrics does not count as a valid LOLWUT.\n3. LOLWUT output should be fast to generate so that the command can be called in production instances without issues. It should remain fast even when the user experiments with odd parameters.\n4. LOLWUT implementations should be safe and carefully checked for security, and resist to untrusted inputs if they take arguments.\n5. LOLWUT must always display the Redis version at the end."},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>VERSION version</code></summary>\n\nRequest a specific version of the generated art.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): a string containing generative computer art and the Redis version.\n\n**RESP3:**\n\n[Verbatim string reply](../../develop/reference/protocol-spec#verbatim-strings): a string containing generative computer art and the Redis version."}],"examples":[]}
{"schema_version":2,"id":"commands/lpop","title":"LPOP","url":"https://redis.io/docs/latest/commands/lpop/","summary":"Returns the first elements in a list after removing it. Deletes the list if the last element was popped.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-08T12:05:04+02:00","page_type":"content","content_hash":"1e5a2a4f9cfe539907ef9a98a33948e59d96bd9d6a68e2d054b4f4b9742d1fed","sections":[{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the list.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>count</code></summary>\n\nThe number of elements to pop. Without it, a single element is popped.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"Foundational: Remove and return the first element(s) from a list using LPOP (supports optional count parameter to pop multiple elements)\n\n**Difficulty:** Beginner\n\n**Commands:** RPUSH, LPOP, LRANGE\n\n**Complexity:**\n- RPUSH: O(1)\n- LPOP: O(N)\n- LRANGE: O(S+N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): if the key does not exist.\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): when called without the _count_ argument, the value of the first element.\n* [Array reply](../../develop/reference/protocol-spec#arrays): when called with the _count_ argument, a list of popped elements.\n\n**RESP3:**\n\nOne of the following:\n* [Null reply](../../develop/reference/protocol-spec#nulls): if the key does not exist.\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): when called without the _count_ argument, the value of the first element.\n* [Array reply](../../develop/reference/protocol-spec#arrays): when called with the _count_ argument, a list of popped elements."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> RPUSH mylist \"one\" \"two\" \"three\" \"four\" \"five\"\n(integer) 5\nredis> LPOP mylist\n\"one\"\nredis> LPOP mylist 2\n1) \"two\"\n2) \"three\"\nredis> LRANGE mylist 0 -1\n1) \"four\"\n2) \"five\"","section_id":"examples"},{"id":"examples-ex1","language":"csharp","code":"long lPopResult1 = db.ListRightPush(\"mylist\", [\"one\", \"two\", \"three\", \"four\", \"five\"]);\n        Console.WriteLine(lPopResult1); // >>> 5\n\n        RedisValue lPopResult2 = db.ListLeftPop(\"mylist\");\n        Console.WriteLine(lPopResult2); // >>> one\n\n        RedisValue[] lPopResult3 = db.ListLeftPop(\"mylist\", 2);\n        Console.WriteLine($\"[{string.Join(\", \", lPopResult3)}]\");\n        // >>> [two, three]\n\n        RedisValue[] lPopResult4 = db.ListRange(\"mylist\", 0, -1);\n        Console.WriteLine($\"[{string.Join(\", \", lPopResult4)}]\");\n        // >>> [four, five]","section_id":"examples"},{"id":"examples-ex2","language":"go","code":"RPushResult, err := rdb.RPush(ctx,\n\t\t\"mylist\", \"one\", \"two\", \"three\", \"four\", \"five\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(RPushResult) // >>> 5\n\n\tlPopResult, err := rdb.LPop(ctx, \"mylist\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(lPopResult) // >>> one\n\n\tlPopCountResult, err := rdb.LPopCount(ctx, \"mylist\", 2).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(lPopCountResult) // >>> [two three]\n\n\tlRangeResult, err := rdb.LRange(ctx, \"mylist\", 0, -1).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(lRangeResult) // >>> [four five]","section_id":"examples"},{"id":"examples-ex3","language":"java","code":"CompletableFuture<Void> lpop = asyncCommands.rpush(\"mylist\", \"one\", \"two\", \"three\", \"four\", \"five\")\n                    .thenCompose(res17 -> {\n                        System.out.println(res17); // >>> 5\n\n                        return asyncCommands.lpop(\"mylist\");\n                    }).thenCompose(res18 -> {\n                        System.out.println(res18); // >>> one\n\n                        return asyncCommands.lpop(\"mylist\", 2);\n                    }).thenCompose(res19 -> {\n                        System.out.println(res19); // >>> [two, three]\n\n                        return asyncCommands.lrange(\"mylist\", 0, -1);\n                    })\n                    .thenAccept(res17_final -> System.out.println(res17_final)) // >>> [four, five]\n                    .toCompletableFuture();","section_id":"examples"},{"id":"examples-ex4","language":"java","code":"Mono<Void> lpop = reactiveCommands.rpush(\"mylist\", \"one\", \"two\", \"three\", \"four\", \"five\").doOnNext(res17 -> {\n                System.out.println(res17); // >>> 5\n            }).flatMap(res17 -> reactiveCommands.lpop(\"mylist\")).doOnNext(res18 -> {\n                System.out.println(res18); // >>> one\n            }).flatMap(res18 -> reactiveCommands.lpop(\"mylist\", 2).collectList()).doOnNext(res19 -> {\n                System.out.println(res19); // >>> [two, three]\n            }).flatMap(res19 -> reactiveCommands.lrange(\"mylist\", 0, -1).collectList()).doOnNext(res17_final -> {\n                System.out.println(res17_final); // >>> [four, five]\n            }).then();","section_id":"examples"},{"id":"examples-ex5","language":"java","code":"long lPopResult1 = jedis.rpush(\n            \"mylist\", \"one\", \"two\", \"three\", \"four\", \"five\"\n        );\n        System.out.println(lPopResult1); // >>> 5\n\n        String lPopResult2 = jedis.lpop(\"mylist\");\n        System.out.println(lPopResult2); // >>> one\n\n        List<String> lPopResult3 = jedis.lpop(\"mylist\", 2);\n        System.out.println(lPopResult3); // >>> [two, three]\n\n        List<String> lPopResult4 = jedis.lrange(\"mylist\", 0, -1);\n        System.out.println(lPopResult4); // >>> [four, five]","section_id":"examples"},{"id":"examples-ex6","language":"javascript","code":"const res17 = await client.rPush('mylist', [\"one\", \"two\", \"three\", \"four\", \"five\"]);\nconsole.log(res17); // 5\n\nconst res18 = await client.lPop('mylist');\nconsole.log(res18); // 'one'\n\nconst res19 = await client.lPopCount('mylist', 2);\nconsole.log(res19); // [ 'two', 'three' ]\n\nconst res20 = await client.lRange('mylist', 0, -1);\nconsole.log(res20); // [ 'four', 'five' ]","section_id":"examples"},{"id":"examples-ex7","language":"javascript","code":"const res17 = await redis.rpush('mylist', 'one', 'two', 'three', 'four', 'five');\nconsole.log(res17); // >>> 5\n\nconst res18 = await redis.lpop('mylist');\nconsole.log(res18); // >>> one\n\nconst res19 = await redis.lpop('mylist', 2);\nconsole.log(res19); // >>> ['two', 'three']\n\nconst res20 = await redis.lrange('mylist', 0, -1);\nconsole.log(res20); // >>> ['four', 'five']","section_id":"examples"},{"id":"examples-ex8","language":"javascript","code":"const res17 = await client.rPush('mylist', [\"one\", \"two\", \"three\", \"four\", \"five\"]);\nconsole.log(res17); // 5\n\nconst res18 = await client.lPop('mylist');\nconsole.log(res18); // 'one'\n\nconst res19 = await client.lPopCount('mylist', 2);\nconsole.log(res19); // [ 'two', 'three' ]\n\nconst res20 = await client.lRange('mylist', 0, -1);\nconsole.log(res20); // [ 'four', 'five' ]","section_id":"examples"},{"id":"examples-ex9","language":"javascript","code":"const res17 = await redis.rpush('mylist', 'one', 'two', 'three', 'four', 'five');\nconsole.log(res17); // >>> 5\n\nconst res18 = await redis.lpop('mylist');\nconsole.log(res18); // >>> one\n\nconst res19 = await redis.lpop('mylist', 2);\nconsole.log(res19); // >>> ['two', 'three']\n\nconst res20 = await redis.lrange('mylist', 0, -1);\nconsole.log(res20); // >>> ['four', 'five']","section_id":"examples"},{"id":"examples-ex10","language":"php","code":"$res17 = $r->rpush('mylist', 'one', 'two', 'three', 'four', 'five');\n        echo $res17 . PHP_EOL;\n        // >>> 5\n\n        $res18 = $r->lpop('mylist');\n        echo $res18 . PHP_EOL;\n        // >>> one\n\n        $res19 = $r->lpop('mylist', 2);\n        echo json_encode($res19) . PHP_EOL;\n        // >>> [\"two\",\"three\"]\n\n        $res20 = $r->lrange('mylist', 0, -1);\n        echo json_encode($res20) . PHP_EOL;\n        // >>> [\"four\",\"five\"]","section_id":"examples"},{"id":"examples-ex11","language":"python","code":"res17 = r.rpush(\"mylist\", *[\"one\", \"two\", \"three\", \"four\", \"five\"])\nprint(res17) # >>> 5\n\nres18 = r.lpop(\"mylist\")\nprint(res18) # >>> \"one\"\n\nres19 = r.lpop(\"mylist\", 2)\nprint(res19) # >>> ['two', 'three']\n\nres17 = r.lrange(\"mylist\", 0, -1)\nprint(res17)  # >>> [ \"four\", \"five\" ]","section_id":"examples"},{"id":"examples-ex12","language":"ruby","code":"res17 = r.rpush('mylist', ['one', 'two', 'three', 'four', 'five'])\nputs res17 # >>> 5\n\nres18 = r.lpop('mylist')\nputs res18 # >>> one\n\nres19 = r.lpop('mylist', 2)\nputs res19.inspect # >>> [\"two\", \"three\"]\n\nres20 = r.lrange('mylist', 0, -1)\nputs res20.inspect # >>> [\"four\", \"five\"]","section_id":"examples"},{"id":"examples-ex13","language":"rust","code":"if let Ok(res17) = r.rpush(\"mylist\", &[\"one\", \"two\", \"three\", \"four\", \"five\"]).await {\n            let res17: i32 = res17;\n            println!(\"{res17}\"); // >>> 5\n        }\n\n        if let Ok(res18) = r.lpop(\"mylist\", None).await {\n            let res18: String = res18;\n            println!(\"{res18}\"); // >>> one\n        }\n\n        if let Ok(res19) = r.lpop(\"mylist\", NonZeroUsize::new(2)).await {\n            let res19: Vec<String> = res19;\n            println!(\"{res19:?}\"); // >>> [\"two\", \"three\"]\n        }\n\n        if let Ok(res20) = r.lrange(\"mylist\", 0, -1).await {\n            let res20: Vec<String> = res20;\n            println!(\"{res20:?}\"); // >>> [\"four\", \"five\"]\n        }","section_id":"examples"},{"id":"examples-ex14","language":"rust","code":"if let Ok(res17) = r.rpush(\"mylist\", &[\"one\", \"two\", \"three\", \"four\", \"five\"]) {\n            let res17: i32 = res17;\n            println!(\"{res17}\"); // >>> 5\n        }\n\n        if let Ok(res18) = r.lpop(\"mylist\", None) {\n            let res18: String = res18;\n            println!(\"{res18}\"); // >>> one\n        }\n\n        if let Ok(res19) = r.lpop(\"mylist\", NonZeroUsize::new(2)) {\n            let res19: Vec<String> = res19;\n            println!(\"{res19:?}\"); // >>> [\"two\", \"three\"]\n        }\n\n        if let Ok(res20) = r.lrange(\"mylist\", 0, -1) {\n            let res20: Vec<String> = res20;\n            println!(\"{res20:?}\"); // >>> [\"four\", \"five\"]\n        }","section_id":"examples"}]}
{"schema_version":2,"id":"commands/lpos","title":"LPOS","url":"https://redis.io/docs/latest/commands/lpos/","summary":"Returns the index of matching elements in a list.","since":"6.0.6","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"229f352bbbd179f9d068dc87459dccec2359d2dcc44ca7663eeecea6b6594a1d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The command returns the index of matching elements inside a Redis list.\nBy default, when no options are given, it will scan the list from head to tail,\nlooking for the first match of `element`. If the `element` is found, its index (the zero-based position in the list) is returned. Otherwise, if no match is found, `nil` is returned."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the list.\n\n</details>\n\n<details open><summary><code>element</code></summary>\n\nThe value to search for.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>RANK rank</code></summary>\n\nThe rank of the first match to return: `1` is the first match from the head, `-1` the first from the tail.\n\n</details>\n\n<details open><summary><code>COUNT num-matches</code></summary>\n\nThe number of matches to return. `0` returns all matches.\n\n</details>\n\n<details open><summary><code>MAXLEN len</code></summary>\n\nCompare at most `len` elements. `0` (the default) scans the whole list.\n\n</details>"},{"id":"details","title":"Details","role":"content","text":"The optional arguments and options can modify the command's behavior.\nThe `RANK` option specifies the rank of the first element to return when there are multiple matches. A rank of 1 means to return the first match, 2 to return the second match, and so on.\n\nIn the above example, the element `c` is present multiple times. If you want the index of the second match, write:\n\n[code example]\n\nThat is, the second occurrence of `c` is at position 6.\nA negative integer as the `RANK` argument tells `LPOS` to invert the search direction, starting from the tail to the head.\n\nTo get the first element position starting from the tail of the list:\n\n[code example]\n\nNote that the indexes are zero based, so the first element starting from the head of the list is at index0, the next element is at index 1, and so on. This means that the returned indexes are stable whatever the rank is positive or negative.\n\nSometimes you want to get not just the Nth matching element, but the position of all the first N matching elements. This can be achieved using the `COUNT` option.\n\n[code example]\n\nYou can combine `COUNT` and `RANK`, so that `COUNT` will try to return up to the specified number of matches, but starting from the Nth match, as specified by the `RANK` option.\n\n[code example]\n\nWhen `COUNT` is used, it is possible to specify 0 as the number of matches as a way to tell the command you want all the matches found returned as an array of indexes. This is better than giving a very large `COUNT` option because it is more general.\n\n[code example]\n\nWhen `COUNT` is used and no match is found, an empty array is returned. However when `COUNT` is not used and there are no matches, the command returns `nil`.\n\nFinally, the `MAXLEN` option tells the command to compare the provided element only with a given maximum number of list items. For example, specifying `MAXLEN 1000` will make sure that the command performs only 1000 comparisons, effectively running the algorithm on a subset of the list (the first part or the last part depending ona positive or negative rank). This is useful to limit the maximum complexity of the command. It is also useful when you expect the match to be found very early, but you want to be sure that in case this is not true, the command does not take too much time to run.\n\nWhen `MAXLEN` is used, it is possible to specify 0 as the maximum number of comparisons, as a way to tell the command you want unlimited comparisons. This is better than giving a very large `MAXLEN` option because it is more general."},{"id":"examples","title":"Examples","role":"example","text":"redis> RPUSH mylist a b c d 1 2 3 4 3 3 3\n(integer) 11\nredis> LPOS mylist 3\n(integer) 6\nredis> LPOS mylist 3 COUNT 0 RANK 2\n1) (integer) 8\n2) (integer) 9\n3) (integer) 10"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nAny of the following:\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): if there is no matching element.\n* [Integer reply](../../develop/reference/protocol-spec#integers): an integer representing the matching element.\n* [Array reply](../../develop/reference/protocol-spec#arrays): If the COUNT option is given, an array of integers representing the matching elements (or an empty array if there are no matches).\n\n**RESP3:**\n\nAny of the following:\n* [Null reply](../../develop/reference/protocol-spec#nulls): if there is no matching element.\n* [Integer reply](../../develop/reference/protocol-spec#integers): an integer representing the matching element.\n* [Array reply](../../develop/reference/protocol-spec#arrays): If the COUNT option is given, an array of integers representing the matching elements (or an empty array if there are no matches)."}],"examples":[{"id":"details-ex0","language":"plaintext","code":"> RPUSH mylist a b c 1 2 3 c c\n> LPOS mylist c RANK 2\n6","section_id":"details"},{"id":"details-ex1","language":"plaintext","code":"> LPOS mylist c RANK -1\n7","section_id":"details"},{"id":"details-ex2","language":"plaintext","code":"> LPOS mylist c COUNT 2\n[2,6]","section_id":"details"},{"id":"details-ex3","language":"plaintext","code":"> LPOS mylist c RANK -1 COUNT 2\n[7,6]","section_id":"details"},{"id":"details-ex4","language":"plaintext","code":"> LPOS mylist c COUNT 0\n[2,6,7]","section_id":"details"}]}
{"schema_version":2,"id":"commands/lpush","title":"LPUSH","url":"https://redis.io/docs/latest/commands/lpush/","summary":"Prepends one or more elements to a list. Creates the key if it doesn't exist.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-08T12:05:04+02:00","page_type":"content","content_hash":"0d6e639264e236665093e2fb98865de93a6a1e5456f3a0f5c5b7a8af2b66a09a","sections":[{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the list.\n\n</details>\n\n<details open><summary><code>element [element ...]</code></summary>\n\nOne or more values to prepend to the list.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"Foundational: Add one or more elements to the head of a list using LPUSH (creates list if needed, returns new list length)\n\n**Difficulty:** Beginner\n\n**Commands:** LPUSH, LRANGE\n\n**Complexity:**\n- LPUSH: O(1)\n- LRANGE: O(S+N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the length of the list after the push operation.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the length of the list after the push operation."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> LPUSH mylist \"world\"\n(integer) 1\nredis> LPUSH mylist \"hello\"\n(integer) 2\nredis> LRANGE mylist 0 -1\n1) \"hello\"\n2) \"world\"","section_id":"examples"},{"id":"examples-ex1","language":"csharp","code":"long lPushResult1 = db.ListLeftPush(\"mylist\", \"World\");\n        Console.WriteLine(lPushResult1); // >>> 1\n\n        long lPushResult2 = db.ListLeftPush(\"mylist\", \"Hello\");\n        Console.WriteLine(lPushResult2); // >>> 2\n\n        RedisValue[] lPushResult3 = db.ListRange(\"mylist\", 0, -1);\n        Console.WriteLine($\"[{string.Join(\", \", lPushResult3)}]\");\n        // >>> [Hello, World]","section_id":"examples"},{"id":"examples-ex2","language":"go","code":"lPushResult1, err := rdb.LPush(ctx, \"mylist\", \"World\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(lPushResult1) // >>> 1\n\n\tlPushResult2, err := rdb.LPush(ctx, \"mylist\", \"Hello\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(lPushResult2) // >>> 2\n\n\tlRangeResult, err := rdb.LRange(ctx, \"mylist\", 0, -1).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(lRangeResult) // >>> [Hello World]","section_id":"examples"},{"id":"examples-ex3","language":"java","code":"CompletableFuture<Void> lpush = asyncCommands.lpush(\"mylist\", \"world\").thenCompose(res1 -> {\n                System.out.println(res1); // >>> 1\n\n                return asyncCommands.lpush(\"mylist\", \"hello\");\n            }).thenCompose(res2 -> {\n                System.out.println(res2); // >>> 2\n\n                return asyncCommands.lrange(\"mylist\", 0, -1);\n            })\n                    .thenAccept(res3 -> System.out.println(res3)) // >>> [hello, world]\n                    .toCompletableFuture();","section_id":"examples"},{"id":"examples-ex4","language":"java","code":"Mono<Void> lpush = reactiveCommands.lpush(\"mylist\", \"world\").doOnNext(res1 -> {\n                System.out.println(res1); // >>> 1\n            }).flatMap(res1 -> reactiveCommands.lpush(\"mylist\", \"hello\")).doOnNext(res2 -> {\n                System.out.println(res2); // >>> 2\n            }).flatMap(res2 -> reactiveCommands.lrange(\"mylist\", 0, -1).collectList()).doOnNext(res3 -> {\n                System.out.println(res3); // >>> [hello, world]\n            }).then();","section_id":"examples"},{"id":"examples-ex5","language":"java","code":"long lPushResult1 = jedis.lpush(\"mylist\", \"World\");\n        System.out.println(lPushResult1); // >>> 1\n\n        long lPushResult2 = jedis.lpush(\"mylist\", \"Hello\");\n        System.out.println(lPushResult2); // >>> 2\n\n        List<String> lPushResult3 = jedis.lrange(\"mylist\", 0, -1);\n        System.out.println(lPushResult3);\n        // >>> [Hello, World]","section_id":"examples"},{"id":"examples-ex6","language":"javascript","code":"const res1 = await client.lPush('mylist', 'world');\nconsole.log(res1); // 1\n\nconst res2 = await client.lPush('mylist', 'hello');\nconsole.log(res2); // 2\n\nconst res3 = await client.lRange('mylist', 0, -1);\nconsole.log(res3); // [ 'hello', 'world' ]","section_id":"examples"},{"id":"examples-ex7","language":"javascript","code":"const res1 = await redis.lpush('mylist', 'world');\nconsole.log(res1); // >>> 1\n\nconst res2 = await redis.lpush('mylist', 'hello');\nconsole.log(res2); // >>> 2\n\nconst res3 = await redis.lrange('mylist', 0, -1);\nconsole.log(res3); // >>> ['hello', 'world']","section_id":"examples"},{"id":"examples-ex8","language":"javascript","code":"const res1 = await client.lPush('mylist', 'world');\nconsole.log(res1); // 1\n\nconst res2 = await client.lPush('mylist', 'hello');\nconsole.log(res2); // 2\n\nconst res3 = await client.lRange('mylist', 0, -1);\nconsole.log(res3); // [ 'hello', 'world' ]","section_id":"examples"},{"id":"examples-ex9","language":"javascript","code":"const res1 = await redis.lpush('mylist', 'world');\nconsole.log(res1); // >>> 1\n\nconst res2 = await redis.lpush('mylist', 'hello');\nconsole.log(res2); // >>> 2\n\nconst res3 = await redis.lrange('mylist', 0, -1);\nconsole.log(res3); // >>> ['hello', 'world']","section_id":"examples"},{"id":"examples-ex10","language":"php","code":"$res1 = $r->lpush('mylist', 'world');\n        echo $res1 . PHP_EOL;\n        // >>> 1\n\n        $res2 = $r->lpush('mylist', 'hello');\n        echo $res2 . PHP_EOL;\n        // >>> 2\n\n        $res3 = $r->lrange('mylist', 0, -1);\n        echo json_encode($res3) . PHP_EOL;\n        // >>> [\"hello\",\"world\"]","section_id":"examples"},{"id":"examples-ex11","language":"python","code":"res1 = r.lpush(\"mylist\", \"world\")\nprint(res1) # >>> 1\n\nres2 = r.lpush(\"mylist\", \"hello\")\nprint(res2) # >>> 2\n\nres3 = r.lrange(\"mylist\", 0, -1)\nprint(res3)  # >>> [ \"hello\", \"world\" ]","section_id":"examples"},{"id":"examples-ex12","language":"ruby","code":"res1 = r.lpush('mylist', 'world')\nputs res1 # >>> 1\n\nres2 = r.lpush('mylist', 'hello')\nputs res2 # >>> 2\n\nres3 = r.lrange('mylist', 0, -1)\nputs res3.inspect # >>> [\"hello\", \"world\"]","section_id":"examples"},{"id":"examples-ex13","language":"rust","code":"if let Ok(res1) = r.lpush(\"mylist\", \"world\").await {\n            let res1: i32 = res1;\n            println!(\"{res1}\"); // >>> 1\n        }\n\n        if let Ok(res2) = r.lpush(\"mylist\", \"hello\").await {\n            let res2: i32 = res2;\n            println!(\"{res2}\"); // >>> 2\n        }\n\n        if let Ok(res3) = r.lrange(\"mylist\", 0, -1).await {\n            let res3: Vec<String> = res3;\n            println!(\"{res3:?}\"); // >>> [\"hello\", \"world\"]\n        }","section_id":"examples"},{"id":"examples-ex14","language":"rust","code":"if let Ok(res1) = r.lpush(\"mylist\", \"world\") {\n            let res1: i32 = res1;\n            println!(\"{res1}\"); // >>> 1\n        }\n\n        if let Ok(res2) = r.lpush(\"mylist\", \"hello\") {\n            let res2: i32 = res2;\n            println!(\"{res2}\"); // >>> 2\n        }\n\n        if let Ok(res3) = r.lrange(\"mylist\", 0, -1) {\n            let res3: Vec<String> = res3;\n            println!(\"{res3:?}\"); // >>> [\"hello\", \"world\"]\n        }","section_id":"examples"}]}
{"schema_version":2,"id":"commands/lpushx","title":"LPUSHX","url":"https://redis.io/docs/latest/commands/lpushx/","summary":"Prepends one or more elements to a list only when the list exists.","since":"2.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"a043933890ca5c42932fc8fff14839da696c0868c0c19f660d713797b9d0f01b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Inserts specified values at the head of the list stored at `key`, only if `key`\nalready exists and holds a list.\nIn contrary to [`LPUSH`](https://redis.io/docs/latest/commands/lpush), no operation will be performed when `key` does not yet\nexist."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the list.\n\n</details>\n\n<details open><summary><code>element [element ...]</code></summary>\n\nOne or more values to prepend to the list, only if it already exists.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> LPUSH mylist \"World\"\n(integer) 1\nredis> LPUSHX mylist \"Hello\"\n(integer) 2\nredis> LPUSHX myotherlist \"Hello\"\n(integer) 0\nredis> LRANGE mylist 0 -1\n1) \"Hello\"\n2) \"World\"\nredis> LRANGE myotherlist 0 -1\n(empty array)"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the length of the list after the push operation.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the length of the list after the push operation."}],"examples":[]}
{"schema_version":2,"id":"commands/lrange","title":"LRANGE","url":"https://redis.io/docs/latest/commands/lrange/","summary":"Returns a range of elements from a list.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-08T12:05:04+02:00","page_type":"content","content_hash":"0969ae33ee8221a49fe08076883eb81f40714cea01c46e7d4802ce38ab03f8f5","sections":[{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the list.\n\n</details>\n\n<details open><summary><code>start</code></summary>\n\nThe zero-based start index. Negative indexes count from the tail.\n\n</details>\n\n<details open><summary><code>stop</code></summary>\n\nThe zero-based stop index (inclusive). Negative indexes count from the tail.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"Foundational: Retrieve a range of elements from a list using LRANGE with start and stop indexes (supports negative indexes, inclusive range)\n\n**Difficulty:** Beginner\n\n**Commands:** RPUSH, LRANGE\n\n**Complexity:**\n- RPUSH: O(1)\n- LRANGE: O(S+N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"details","title":"Details","role":"content","text":""},{"id":"consistency-with-range-functions-in-various-programming-languages","title":"Consistency with range functions in various programming languages","role":"content","text":"Note that if you have a list of numbers from 0 to 100, `LRANGE list 0 10` will\nreturn 11 elements, that is, the rightmost item is included.\nThis may or may not be consistent with the behavior of range-related functions\nin your programming language of choice (think Ruby's `Range.new`, `Array#slice`\nor Python's `range()` function)."},{"id":"out-of-range-indexes","title":"Out-of-range indexes","role":"content","text":"Out of range indexes will not produce an error.\nIf `start` is larger than the end of the list, an empty list is returned.\nIf `stop` is larger than the actual end of the list, Redis will treat it like\nthe last element of the list."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of elements in the specified range, or an empty array if the key doesn't exist.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of elements in the specified range, or an empty array if the key doesn't exist."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> RPUSH mylist \"one\"\n(integer) 1\nredis> RPUSH mylist \"two\"\n(integer) 2\nredis> RPUSH mylist \"three\"\n(integer) 3\nredis> LRANGE mylist 0 0\n1) \"one\"\nredis> LRANGE mylist -3 2\n1) \"one\"\n2) \"two\"\n3) \"three\"\nredis> LRANGE mylist -100 100\n1) \"one\"\n2) \"two\"\n3) \"three\"\nredis> LRANGE mylist 5 10\n(empty array)","section_id":"examples"},{"id":"examples-ex1","language":"csharp","code":"long lRangeResult1 = db.ListRightPush(\"mylist\", [\"one\", \"two\", \"three\"]);\n        Console.WriteLine(lRangeResult1); // >>> 3\n\n        RedisValue[] lRangeResult2 = db.ListRange(\"mylist\", 0, 0);\n        Console.WriteLine($\"[{string.Join(\", \", lRangeResult2)}]\");\n        // >>> [one]\n\n        RedisValue[] lRangeResult3 = db.ListRange(\"mylist\", -3, 2);\n        Console.WriteLine($\"[{string.Join(\", \", lRangeResult3)}]\");\n        // >>> [one, two, three]\n\n        RedisValue[] lRangeResult4 = db.ListRange(\"mylist\", -100, 100);\n        Console.WriteLine($\"[{string.Join(\", \", lRangeResult4)}]\");\n        // >>> [one, two, three]\n\n        RedisValue[] lRangeResult5 = db.ListRange(\"mylist\", 5, 10);\n        Console.WriteLine($\"[{string.Join(\", \", lRangeResult5)}]\");\n        // >>> []","section_id":"examples"},{"id":"examples-ex2","language":"go","code":"RPushResult, err := rdb.RPush(ctx, \"mylist\",\n\t\t\"one\", \"two\", \"three\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(RPushResult) // >>> 3\n\n\tlRangeResult1, err := rdb.LRange(ctx, \"mylist\", 0, 0).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(lRangeResult1) // >>> [one]\n\n\tlRangeResult2, err := rdb.LRange(ctx, \"mylist\", -3, 2).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(lRangeResult2) // >>> [one two three]\n\n\tlRangeResult3, err := rdb.LRange(ctx, \"mylist\", -100, 100).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(lRangeResult3) // >>> [one two three]\n\n\tlRangeResult4, err := rdb.LRange(ctx, \"mylist\", 5, 10).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(lRangeResult4) // >>> []","section_id":"examples"},{"id":"examples-ex3","language":"java","code":"CompletableFuture<Void> lrange = asyncCommands.rpush(\"mylist\", \"one\").thenCompose(res4 -> {\n                System.out.println(res4); // >>> 1\n\n                return asyncCommands.rpush(\"mylist\", \"two\");\n            }).thenCompose(res5 -> {\n                System.out.println(res5); // >>> 2\n\n                return asyncCommands.rpush(\"mylist\", \"three\");\n            }).thenCompose(res6 -> {\n                System.out.println(res6); // >>> 3\n\n                return asyncCommands.lrange(\"mylist\", 0, 0);\n            }).thenCompose(res7 -> {\n                System.out.println(res7); // >>> [one]\n\n                return asyncCommands.lrange(\"mylist\", -3, 2);\n            }).thenCompose(res8 -> {\n                System.out.println(res8); // >>> [one, two, three]\n\n                return asyncCommands.lrange(\"mylist\", -100, 100);\n            }).thenCompose(res9 -> {\n                System.out.println(res9); // >>> [one, two, three]\n\n                return asyncCommands.lrange(\"mylist\", 5, 10);\n            })\n                    .thenAccept(res10 -> System.out.println(res10)) // >>> []\n                    .toCompletableFuture();","section_id":"examples"},{"id":"examples-ex4","language":"java","code":"Mono<Void> lrange = reactiveCommands.rpush(\"mylist\", \"one\").doOnNext(res4 -> {\n                System.out.println(res4); // >>> 1\n            }).flatMap(res4 -> reactiveCommands.rpush(\"mylist\", \"two\")).doOnNext(res5 -> {\n                System.out.println(res5); // >>> 2\n            }).flatMap(res5 -> reactiveCommands.rpush(\"mylist\", \"three\")).doOnNext(res6 -> {\n                System.out.println(res6); // >>> 3\n            }).flatMap(res6 -> reactiveCommands.lrange(\"mylist\", 0, 0).collectList()).doOnNext(res7 -> {\n                System.out.println(res7); // >>> [one]\n            }).flatMap(res7 -> reactiveCommands.lrange(\"mylist\", -3, 2).collectList()).doOnNext(res8 -> {\n                System.out.println(res8); // >>> [one, two, three]\n            }).flatMap(res8 -> reactiveCommands.lrange(\"mylist\", -100, 100).collectList()).doOnNext(res9 -> {\n                System.out.println(res9); // >>> [one, two, three]\n            }).flatMap(res9 -> reactiveCommands.lrange(\"mylist\", 5, 10).collectList()).doOnNext(res10 -> {\n                System.out.println(res10); // >>> []\n            }).then();","section_id":"examples"},{"id":"examples-ex5","language":"java","code":"long lRangeResult1 = jedis.rpush(\"mylist\", \"one\", \"two\", \"three\");\n        System.out.println(lRangeResult1); // >>> 3\n\n        List<String> lRangeResult2 = jedis.lrange(\"mylist\", 0, 0);\n        System.out.println(lRangeResult2); // >>> [one]\n\n        List<String> lRangeResult3 = jedis.lrange(\"mylist\", -3, 2);\n        System.out.println(lRangeResult3); // >>> [one, two, three]\n\n        List<String> lRangeResult4 = jedis.lrange(\"mylist\", -100, 100);\n        System.out.println(lRangeResult4); // >>> [one, two, three]\n\n        List<String> lRangeResult5 = jedis.lrange(\"mylist\", 5, 10);\n        System.out.println(lRangeResult5); // >>> []","section_id":"examples"},{"id":"examples-ex6","language":"javascript","code":"const res4 = await client.rPush('mylist', 'one');\nconsole.log(res4); // 1\n\nconst res5 = await client.rPush('mylist', 'two');\nconsole.log(res5); // 2\n\nconst res6 = await client.rPush('mylist', 'three');\nconsole.log(res6); // 3\n\nconst res7 = await client.lRange('mylist', 0, 0);\nconsole.log(res7); // [ 'one' ]\n\nconst res8 = await client.lRange('mylist', -3, 2);\nconsole.log(res8); // [ 'one', 'two', 'three' ]\n\nconst res9 = await client.lRange('mylist', -100, 100);\nconsole.log(res9); // [ 'one', 'two', 'three' ]\n\nconst res10 = await client.lRange('mylist', 5, 10);\nconsole.log(res10); // []","section_id":"examples"},{"id":"examples-ex7","language":"javascript","code":"const res4 = await redis.rpush('mylist', 'one');\nconsole.log(res4); // >>> 1\n\nconst res5 = await redis.rpush('mylist', 'two');\nconsole.log(res5); // >>> 2\n\nconst res6 = await redis.rpush('mylist', 'three');\nconsole.log(res6); // >>> 3\n\nconst res7 = await redis.lrange('mylist', 0, 0);\nconsole.log(res7); // >>> ['one']\n\nconst res8 = await redis.lrange('mylist', -3, 2);\nconsole.log(res8); // >>> ['one', 'two', 'three']\n\nconst res9 = await redis.lrange('mylist', -100, 100);\nconsole.log(res9); // >>> ['one', 'two', 'three']\n\nconst res10 = await redis.lrange('mylist', 5, 10);\nconsole.log(res10); // >>> []","section_id":"examples"},{"id":"examples-ex8","language":"javascript","code":"const res4 = await client.rPush('mylist', 'one');\nconsole.log(res4); // 1\n\nconst res5 = await client.rPush('mylist', 'two');\nconsole.log(res5); // 2\n\nconst res6 = await client.rPush('mylist', 'three');\nconsole.log(res6); // 3\n\nconst res7 = await client.lRange('mylist', 0, 0);\nconsole.log(res7); // [ 'one' ]\n\nconst res8 = await client.lRange('mylist', -3, 2);\nconsole.log(res8); // [ 'one', 'two', 'three' ]\n\nconst res9 = await client.lRange('mylist', -100, 100);\nconsole.log(res9); // [ 'one', 'two', 'three' ]\n\nconst res10 = await client.lRange('mylist', 5, 10);\nconsole.log(res10); // []","section_id":"examples"},{"id":"examples-ex9","language":"javascript","code":"const res4 = await redis.rpush('mylist', 'one');\nconsole.log(res4); // >>> 1\n\nconst res5 = await redis.rpush('mylist', 'two');\nconsole.log(res5); // >>> 2\n\nconst res6 = await redis.rpush('mylist', 'three');\nconsole.log(res6); // >>> 3\n\nconst res7 = await redis.lrange('mylist', 0, 0);\nconsole.log(res7); // >>> ['one']\n\nconst res8 = await redis.lrange('mylist', -3, 2);\nconsole.log(res8); // >>> ['one', 'two', 'three']\n\nconst res9 = await redis.lrange('mylist', -100, 100);\nconsole.log(res9); // >>> ['one', 'two', 'three']\n\nconst res10 = await redis.lrange('mylist', 5, 10);\nconsole.log(res10); // >>> []","section_id":"examples"},{"id":"examples-ex10","language":"php","code":"$res4 = $r->rpush('mylist', 'one');\n        echo $res4 . PHP_EOL;\n        // >>> 1\n\n        $res5 = $r->rpush('mylist', 'two');\n        echo $res5 . PHP_EOL;\n        // >>> 2\n\n        $res6 = $r->rpush('mylist', 'three');\n        echo $res6 . PHP_EOL;\n        // >>> 3\n\n        $res7 = $r->lrange('mylist', 0, 0);\n        echo json_encode($res7) . PHP_EOL;\n        // >>> [\"one\"]\n\n        $res8 = $r->lrange('mylist', -3, 2);\n        echo json_encode($res8) . PHP_EOL;\n        // >>> [\"one\",\"two\",\"three\"]\n\n        $res9 = $r->lrange('mylist', -100, 100);\n        echo json_encode($res9) . PHP_EOL;\n        // >>> [\"one\",\"two\",\"three\"]\n\n        $res10 = $r->lrange('mylist', 5, 10);\n        echo json_encode($res10) . PHP_EOL;\n        // >>> []","section_id":"examples"},{"id":"examples-ex11","language":"python","code":"res4 = r.rpush(\"mylist\", \"one\");\nprint(res4) # >>> 1\n\nres5 = r.rpush(\"mylist\", \"two\")\nprint(res5) # >>> 2\n\nres6 = r.rpush(\"mylist\", \"three\")\nprint(res6) # >>> 3\n\nres7 = r.lrange('mylist', 0, 0)\nprint(res7) # >>> [ 'one' ]\n\nres8 = r.lrange('mylist', -3, 2)\nprint(res8) # >>> [ 'one', 'two', 'three' ]\n\nres9 = r.lrange('mylist', -100, 100)\nprint(res9) # >>> [ 'one', 'two', 'three' ]\n\nres10 = r.lrange('mylist', 5, 10)\nprint(res10) # >>> []","section_id":"examples"},{"id":"examples-ex12","language":"ruby","code":"res4 = r.rpush('mylist', 'one')\nputs res4 # >>> 1\n\nres5 = r.rpush('mylist', 'two')\nputs res5 # >>> 2\n\nres6 = r.rpush('mylist', 'three')\nputs res6 # >>> 3\n\nres7 = r.lrange('mylist', 0, 0)\nputs res7.inspect # >>> [\"one\"]\n\nres8 = r.lrange('mylist', -3, 2)\nputs res8.inspect # >>> [\"one\", \"two\", \"three\"]\n\nres9 = r.lrange('mylist', -100, 100)\nputs res9.inspect # >>> [\"one\", \"two\", \"three\"]\n\nres10 = r.lrange('mylist', 5, 10)\nputs res10.inspect # >>> []","section_id":"examples"},{"id":"examples-ex13","language":"rust","code":"let _: Result<i32, _> = r.rpush(\"mylist\", \"one\").await;\n        let _: Result<i32, _> = r.rpush(\"mylist\", \"two\").await;\n        let _: Result<i32, _> = r.rpush(\"mylist\", \"three\").await;\n\n        if let Ok(res7) = r.lrange(\"mylist\", 0, 0).await {\n            let res7: Vec<String> = res7;\n            println!(\"{res7:?}\"); // >>> [\"one\"]\n        }\n\n        if let Ok(res8) = r.lrange(\"mylist\", -3, 2).await {\n            let res8: Vec<String> = res8;\n            println!(\"{res8:?}\"); // >>> [\"one\", \"two\", \"three\"]\n        }\n\n        if let Ok(res9) = r.lrange(\"mylist\", -100, 100).await {\n            let res9: Vec<String> = res9;\n            println!(\"{res9:?}\"); // >>> [\"one\", \"two\", \"three\"]\n        }\n\n        if let Ok(res10) = r.lrange(\"mylist\", 5, 10).await {\n            let res10: Vec<String> = res10;\n            println!(\"{res10:?}\"); // >>> []\n        }","section_id":"examples"},{"id":"examples-ex14","language":"rust","code":"let _: Result<i32, _> = r.rpush(\"mylist\", \"one\");\n        let _: Result<i32, _> = r.rpush(\"mylist\", \"two\");\n        let _: Result<i32, _> = r.rpush(\"mylist\", \"three\");\n\n        if let Ok(res7) = r.lrange(\"mylist\", 0, 0) {\n            let res7: Vec<String> = res7;\n            println!(\"{res7:?}\"); // >>> [\"one\"]\n        }\n\n        if let Ok(res8) = r.lrange(\"mylist\", -3, 2) {\n            let res8: Vec<String> = res8;\n            println!(\"{res8:?}\"); // >>> [\"one\", \"two\", \"three\"]\n        }\n\n        if let Ok(res9) = r.lrange(\"mylist\", -100, 100) {\n            let res9: Vec<String> = res9;\n            println!(\"{res9:?}\"); // >>> [\"one\", \"two\", \"three\"]\n        }\n\n        if let Ok(res10) = r.lrange(\"mylist\", 5, 10) {\n            let res10: Vec<String> = res10;\n            println!(\"{res10:?}\"); // >>> []\n        }","section_id":"examples"}]}
{"schema_version":2,"id":"commands/lrem","title":"LREM","url":"https://redis.io/docs/latest/commands/lrem/","summary":"Removes elements from a list. Deletes the list if the last element was removed.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"9082070e1c527a3599810d008d2bf861c7a665644a1ca5b45c8b10ee25e908eb","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Removes the first `count` occurrences of elements equal to `element` from the list\nstored at `key`.\nThe `count` argument influences the operation in the following ways:\n\n* `count > 0`: Remove elements equal to `element` moving from head to tail.\n* `count < 0`: Remove elements equal to `element` moving from tail to head.\n* `count = 0`: Remove all elements equal to `element`.\n\nFor example, `LREM list -2 \"hello\"` will remove the last two occurrences of\n`\"hello\"` in the list stored at `key`.\n\nNote that non-existing keys are treated like empty lists, so when `key` does not\nexist, the command will always return `0`."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the list.\n\n</details>\n\n<details open><summary><code>count</code></summary>\n\nThe number of matching elements to remove and the direction: a positive value removes from the head, a negative value from the tail, and `0` removes all matches.\n\n</details>\n\n<details open><summary><code>element</code></summary>\n\nThe value to remove.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> RPUSH mylist \"hello\"\n(integer) 1\nredis> RPUSH mylist \"hello\"\n(integer) 2\nredis> RPUSH mylist \"foo\"\n(integer) 3\nredis> RPUSH mylist \"hello\"\n(integer) 4\nredis> LREM mylist -2 \"hello\"\n(integer) 2\nredis> LRANGE mylist 0 -1\n1) \"hello\"\n2) \"foo\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of removed elements.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of removed elements."}],"examples":[]}
{"schema_version":2,"id":"commands/lset","title":"LSET","url":"https://redis.io/docs/latest/commands/lset/","summary":"Sets the value of an element in a list by its index.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"4be27d2596d8a0676a5da2c4b0b8d3139f23dd57bd9142baa993f3237392c082","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Sets the list element at `index` to `element`.\nFor more information on the `index` argument, see [`LINDEX`](https://redis.io/docs/latest/commands/lindex).\n\nAn error is returned for out of range indexes."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the list.\n\n</details>\n\n<details open><summary><code>index</code></summary>\n\nThe zero-based index of the element to set. Negative indexes count from the tail.\n\n</details>\n\n<details open><summary><code>element</code></summary>\n\nThe new value.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> RPUSH mylist \"one\"\n(integer) 1\nredis> RPUSH mylist \"two\"\n(integer) 2\nredis> RPUSH mylist \"three\"\n(integer) 3\nredis> LSET mylist 0 \"four\"\nOK\nredis> LSET mylist -2 \"five\"\nOK\nredis> LRANGE mylist 0 -1\n1) \"four\"\n2) \"five\"\n3) \"three\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[]}
{"schema_version":2,"id":"commands/ltrim","title":"LTRIM","url":"https://redis.io/docs/latest/commands/ltrim/","summary":"Removes elements from both ends a list. Deletes the list if all elements were trimmed.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"27f1c3908f2f799583a9ceac3185963cc3d4edc6ee176b45fea640016711d85f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Trim an existing list so that it will contain only the specified range of\nelements specified.\nBoth `start` and `stop` are zero-based indexes, where `0` is the first element\nof the list (the head), `1` the next element, and so on.\n\nFor example: `LTRIM foobar 0 2` will modify the list stored at `foobar` so that\nonly the first three elements of the list will remain.\n\n`start` and `stop` can also be negative numbers indicating offsets from the end\nof the list, where `-1` is the last element of the list, `-2` the penultimate\nelement, and so on.\n\nOut of range indexes will not produce an error: if `start` is larger than the\nend of the list (`start > stop`), the result will be an empty list, which\ncauses `key` to be removed.\nIf `stop` is larger than the end of the list (`stop > start`), Redis will treat it like the last\nelement of the list."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the list.\n\n</details>\n\n<details open><summary><code>start</code></summary>\n\nThe zero-based start index. Negative indexes count from the tail.\n\n</details>\n\n<details open><summary><code>stop</code></summary>\n\nThe zero-based stop index (inclusive). Negative indexes count from the tail.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"A common use of `LTRIM` is to use it with [`LPUSH`](https://redis.io/docs/latest/commands/lpush) or [`RPUSH`](https://redis.io/docs/latest/commands/rpush).\nFor example:\n\n[code example]\n\nThis pair of commands will push a new element on the list, while making sure\nthat the list will not grow larger than 100 elements.\nThis is very useful when using Redis to store logs for example.\nIt is important to note that when used in this way `LTRIM` is an O(1) operation\nbecause in the average case just one element is removed from the tail of the\nlist.\n\n\n\nredis> RPUSH mylist \"one\"\n(integer) 1\nredis> RPUSH mylist \"two\"\n(integer) 2\nredis> RPUSH mylist \"three\"\n(integer) 3\nredis> LTRIM mylist 1 -1\nOK\nredis> LRANGE mylist 0 -1\n1) \"two\"\n2) \"three\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"LPUSH mylist someelement\nLTRIM mylist 0 99","section_id":"examples"}]}
{"schema_version":2,"id":"commands/memory","title":"MEMORY","url":"https://redis.io/docs/latest/commands/memory/","summary":"A container for memory diagnostics commands.","since":"4.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"a4587ba81a7db58258450f8efea964474d51d3154380544225f1be8ef45d02e9","sections":[{"id":"content","title":"Content","role":"content","text":"This is a container command for memory introspection and management commands.\n\nTo see the list of available commands you can call [`MEMORY HELP`](https://redis.io/docs/latest/commands/memory-help)."}],"examples":[]}
{"schema_version":2,"id":"commands/memory-doctor","title":"MEMORY DOCTOR","url":"https://redis.io/docs/latest/commands/memory-doctor/","summary":"Outputs a memory problems report.","since":"4.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"a80754768b8ec88e10b1779d12df4760cd21e567e329949b3466da7e61f94acc","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `MEMORY DOCTOR` command reports about different memory-related issues that\nthe Redis server experiences, and advises about possible remedies."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): a memory problems report.\n\n**RESP3:**\n\n[Verbatim string reply](../../develop/reference/protocol-spec#verbatim-strings): a memory problems report."}],"examples":[]}
{"schema_version":2,"id":"commands/memory-help","title":"MEMORY HELP","url":"https://redis.io/docs/latest/commands/memory-help/","summary":"Returns helpful text about the different subcommands.","since":"4.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"810e95f397e29f86453544f1c2d67b5466e4a9838124ee1f180adc31d2ca1397","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `MEMORY HELP` command returns a helpful text describing the different\nsubcommands."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Not supported for [scripts](https://redis.io/docs/latest/develop/programmability) in Redis versions earlier than 7. |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of sub-commands and their descriptions\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of sub-commands and their descriptions."}],"examples":[]}
{"schema_version":2,"id":"commands/memory-malloc-stats","title":"MEMORY MALLOC-STATS","url":"https://redis.io/docs/latest/commands/memory-malloc-stats/","summary":"Returns the allocator statistics.","since":"4.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"28ba65b72d36f5a90365f89077067af2eb8434c23e240bee42810b3973802482","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `MEMORY MALLOC-STATS` command provides an internal statistics report from\nthe memory allocator.\n\nThis command is currently implemented only when using **jemalloc** as an\nallocator, and evaluates to a benign NOOP for all others."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): a long string of statistics.\n\n**RESP3:**\n\n[Verbatim string reply](../../develop/reference/protocol-spec#verbatim-strings): a long string of statistics."}],"examples":[]}
{"schema_version":2,"id":"commands/memory-purge","title":"MEMORY PURGE","url":"https://redis.io/docs/latest/commands/memory-purge/","summary":"Asks the allocator to release memory.","since":"4.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"8356dec3819aae7bfe2560cd9f62d0e864866c444c2ef3bdd17a91eb8da69fea","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `MEMORY PURGE` command attempts to purge dirty pages so these can be\nreclaimed by the allocator.\n\nThis command is currently implemented only when using **jemalloc** as an\nallocator, and evaluates to a benign NOOP for all others."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[]}
{"schema_version":2,"id":"commands/memory-stats","title":"MEMORY STATS","url":"https://redis.io/docs/latest/commands/memory-stats/","summary":"Returns details about memory usage.","since":"4.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"268350d9b499e5886214499a7a005e84f9a7d4eac73e8c7634d806e0e93dea2d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `MEMORY STATS` command returns an [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) about the memory usage of the\nserver.\n\nThe information about memory usage is provided as metrics and their respective\nvalues. The following metrics are reported:\n\n*   `peak.allocated`: Peak memory consumed by Redis in bytes (see [`INFO`](https://redis.io/docs/latest/commands/info)'s\n     `used_memory_peak`)\n*   `total.allocated`: Total number of bytes allocated by Redis using its\n     allocator (see [`INFO`](https://redis.io/docs/latest/commands/info)'s `used_memory`)\n*   `startup.allocated`: Initial amount of memory consumed by Redis at startup\n     in bytes (see [`INFO`](https://redis.io/docs/latest/commands/info)'s `used_memory_startup`)\n*   `replication.backlog`: Size in bytes of the replication backlog (see\n     [`INFO`](https://redis.io/docs/latest/commands/info)'s `repl_backlog_active`)\n*   `clients.slaves`: The total size in bytes of all replicas overheads (output\n     and query buffers, connection contexts)\n*   `clients.normal`: The total size in bytes of all clients overheads (output\n     and query buffers, connection contexts)\n*   `cluster.links`: Memory usage by cluster links (Added in Redis 7.0, see [`INFO`](https://redis.io/docs/latest/commands/info)'s `mem_cluster_links`).\n*   `aof.buffer`: The summed size in bytes of AOF related buffers.\n*   `lua.caches`: the summed size in bytes of the overheads of the Lua scripts'\n     caches\n*   `functions.caches`: the summed size in bytes of the overheads of the Function scripts'\n     caches\n*   `hash.templates`: Total memory in bytes used by all compact hash templates (see [`INFO`](https://redis.io/docs/latest/commands/info)'s `used_memory_hash_templates`). Added in Redis 8.10.\n*   `dbXXX`: For each of the server's databases, the overheads of the main and\n     expiry dictionaries (`overhead.hashtable.main` and\n    `overhead.hashtable.expires`, respectively) are reported in bytes\n*   `overhead.db.hashtable.lut`: Total overhead of dictionary buckets in databases (Added in Redis 7.4)\n*   `overhead.db.hashtable.rehashing`: Temporary memory overhead of database dictionaries currently being rehashed (Added in Redis 7.4) \n*   `overhead.total`: The sum of all overheads, i.e. `startup.allocated`,\n     `replication.backlog`, `clients.slaves`, `clients.normal`, `aof.buffer` and\n     those of the internal data structures that are used in managing the\n     Redis keyspace (see [`INFO`](https://redis.io/docs/latest/commands/info)'s `used_memory_overhead`)\n*   `db.dict.rehashing.count`: Number of DB dictionaries currently being rehashed (Added in Redis 7.4)\n*   `keys.count`: The total number of keys stored across all databases in the\n     server\n*   `keys.bytes-per-key`: The ratio between `dataset.bytes` and `keys.count` \n*   `dataset.bytes`: The size in bytes of the dataset, i.e. `overhead.total`\n     subtracted from `total.allocated` (see [`INFO`](https://redis.io/docs/latest/commands/info)'s `used_memory_dataset`)\n*   `dataset.percentage`: The percentage of `dataset.bytes` out of the total\n     memory usage\n*   `peak.percentage`: The percentage of `total.allocated` out of\n     `peak.allocated`\n*   `allocator.allocated`: See [`INFO`](https://redis.io/docs/latest/commands/info)'s `allocator_allocated`\n*   `allocator.active`: See [`INFO`](https://redis.io/docs/latest/commands/info)'s `allocator_active`\n*   `allocator.resident`: See [`INFO`](https://redis.io/docs/latest/commands/info)'s `allocator_resident`\n*   `allocator.muzzy`: See [`INFO`](https://redis.io/docs/latest/commands/info)'s `allocator_muzzy`\n*   `allocator-fragmentation.ratio`: See [`INFO`](https://redis.io/docs/latest/commands/info)'s `allocator_frag_ratio`\n*   `allocator-fragmentation.bytes`: See [`INFO`](https://redis.io/docs/latest/commands/info)'s `allocator_frag_bytes`\n*   `allocator-rss.ratio`: See [`INFO`](https://redis.io/docs/latest/commands/info)'s `allocator_rss_ratio`\n*   `allocator-rss.bytes`: See [`INFO`](https://redis.io/docs/latest/commands/info)'s `allocator_rss_bytes`\n*   `rss-overhead.ratio`: See [`INFO`](https://redis.io/docs/latest/commands/info)'s `rss_overhead_ratio`\n*   `rss-overhead.bytes`: See [`INFO`](https://redis.io/docs/latest/commands/info)'s `rss_overhead_bytes`\n*   `fragmentation`: See [`INFO`](https://redis.io/docs/latest/commands/info)'s `mem_fragmentation_ratio`\n*   `fragmentation.bytes`: See [`INFO`](https://redis.io/docs/latest/commands/info)'s `mem_fragmentation_bytes`\n\n**A note about the word slave used in this man page**: Starting with Redis 5, if not for backward compatibility, the Redis project no longer uses the word slave. Unfortunately in this command the word slave is part of the protocol, so we'll be able to remove such occurrences only when this API will be naturally deprecated."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a nested list of memory usage metrics and their values.\n\n**RESP3:**\n\n[Map reply](../../develop/reference/protocol-spec#maps): memory usage metrics and their values."}],"examples":[]}
{"schema_version":2,"id":"commands/memory-usage","title":"MEMORY USAGE","url":"https://redis.io/docs/latest/commands/memory-usage/","summary":"Estimates the memory usage of a key.","since":"4.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"5842f6b2fd910f4da877814865cf6830a6dba685a957659b8cc6df1875d4e7ec","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `MEMORY USAGE` command reports the number of bytes that a key and its value\nrequire to be stored in RAM.\n\nThe reported usage is the total of memory allocations for data and\nadministrative overheads that a key and its value require.\n\nFor a hash stored in a compact hash encoding, the reported usage also includes\nthe key's share of the shared compact hash template's memory (the template's\nsize divided evenly across all keys that reference it). Added in Redis 8.10.\n\nFor nested data types, the optional `SAMPLES` option can be provided, where\n`count` is the number of sampled nested values. The samples are averaged to estimate the total size.\nBy default, this option is set to `5`. To sample the all of the nested values, use `SAMPLES 0`."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe key to report memory usage for.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>SAMPLES count</code></summary>\n\nThe number of sampled nested values for aggregate types. `0` samples all of them; the default is 5.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"With Redis v7.2.0 64-bit and **jemalloc**, the empty string measures as follows:\n\n[code example]\n\nThese bytes are pure overhead at the moment as no actual data is stored, and are\nused for maintaining the internal data structures of the server (include internal allocator fragmentation). Longer keys and\nvalues show asymptotically linear usage.\n\n[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n|<span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Not supported for [scripts](https://redis.io/docs/latest/develop/programmability) in Redis versions earlier than 7. |\n\nNote: key memory usage is different on Redis Software or Redis Cloud active-active databases than on non-active-active databases. This is because memory usage includes some amount of CRDB overhead."},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): the memory usage in bytes.\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): if the key does not exist.\n\n**RESP3:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): the memory usage in bytes.\n* [Null reply](../../develop/reference/protocol-spec#nulls): if the key does not exist."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> SET \"\" \"\"\nOK\n> MEMORY USAGE \"\"\n(integer) 56","section_id":"examples"},{"id":"examples-ex1","language":"plaintext","code":"> SET foo bar\nOK\n> MEMORY USAGE foo\n(integer) 56\n> SET foo2 mybar\nOK\n> MEMORY USAGE foo2\n(integer) 64\n> SET foo3 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789\nOK\n> MEMORY USAGE foo3\n(integer) 160","section_id":"examples"}]}
{"schema_version":2,"id":"commands/mget","title":"MGET","url":"https://redis.io/docs/latest/commands/mget/","summary":"Atomically returns the string values of one or more keys.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:12+01:00","page_type":"content","content_hash":"e075beee7156c00afef43e08616b2e4781f15bad4844e45ef483a267390f21fa","sections":[{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key [key ...]</code></summary>\n\nOne or more keys whose values to retrieve.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"Returns the values of all specified keys.\n\n**Difficulty:** Beginner\n\n**Commands:** SET, MGET\n\n**Complexity:**\n- SET: O(1)\n- MGET: O(N)\n\n**Available in:** Redis CLI, C, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of values at the specified keys.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of values at the specified keys."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> SET key1 \"Hello\"\nOK\n> SET key2 \"World\"\nOK\n> MGET key1 key2 nonexisting\n1) \"Hello\"\n2) \"World\"\n3) (nil)","section_id":"examples"},{"id":"examples-ex1","language":"c","code":"redisReply *reply = redisCommand(c, \"SET key1 Hello\");\n    freeReplyObject(reply);\n\n    reply = redisCommand(c, \"SET key2 World\");\n    freeReplyObject(reply);\n\n    reply = redisCommand(c, \"MGET key1 key2 nonexisting\");\n\n    for (size_t i = 0; i < reply->elements; i++) {\n        if (i > 0) {\n            printf(\", \");\n        }\n\n        if (reply->element[i]->type == REDIS_REPLY_NIL) {\n            printf(\"null\");\n        } else {\n            printf(\"%s\", reply->element[i]->str);\n        }\n    }\n    printf(\"\\n\");\n    // >>> Hello, World, null","section_id":"examples"},{"id":"examples-ex2","language":"csharp","code":"bool mgetResult1 = db.StringSet(\"key1\", \"Hello\");\n        bool mgetResult2 = db.StringSet(\"key2\", \"World\");\n\n        RedisValue[] mgetResult3 = db.StringGet(new RedisKey[] { \"key1\", \"key2\", \"nonexisting\" });\n        Console.WriteLine(string.Join(\", \", mgetResult3.Select(v => v.IsNull ? \"null\" : v.ToString())));\n        // >>> Hello, World, null","section_id":"examples"},{"id":"examples-ex3","language":"go","code":"if err := rdb.Set(ctx, \"key1\", \"Hello\", 0).Err(); err != nil {\n\t\tpanic(err)\n\t}\n\n\tif err := rdb.Set(ctx, \"key2\", \"World\", 0).Err(); err != nil {\n\t\tpanic(err)\n\t}\n\n\tmgetResult, err := rdb.MGet(ctx, \"key1\", \"key2\", \"nonexisting\").Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(mgetResult) // >>> [Hello World <nil>]","section_id":"examples"},{"id":"examples-ex4","language":"java","code":"CompletableFuture<Void> mgetExample = asyncCommands.set(\"key1\", \"Hello\")\n                    .thenCompose(res1 -> asyncCommands.set(\"key2\", \"World\"))\n                    .thenCompose(res2 -> asyncCommands.mget(\"key1\", \"key2\", \"nonexisting\"))\n                    .thenAccept(res3 -> {\n                        System.out.println(res3);\n                        // >>> [KeyValue[key1, Hello], KeyValue[key2, World], KeyValue[nonexisting].empty]\n                    })\n                    .toCompletableFuture();","section_id":"examples"},{"id":"examples-ex5","language":"java","code":"Mono<Void> mgetExample = reactiveCommands.set(\"key1\", \"Hello\")\n                    .flatMap(res1 -> reactiveCommands.set(\"key2\", \"World\"))\n                    .flatMap(res2 -> reactiveCommands.mget(\"key1\", \"key2\", \"nonexisting\").collectList())\n                    .doOnNext(res3 -> {\n                        System.out.println(res3);\n                        // >>> [KeyValue[key1, Hello], KeyValue[key2, World], KeyValue[nonexisting].empty]\n                    })\n                    .then();","section_id":"examples"},{"id":"examples-ex6","language":"java","code":"String mgetResult1 = jedis.set(\"key1\", \"Hello\");\n        System.out.println(mgetResult1);    // >>> OK\n\n        String mgetResult2 = jedis.set(\"key2\", \"World\");\n        System.out.println(mgetResult2);    // >>> OK\n\n        java.util.List<String> mgetResult3 = jedis.mget(\"key1\", \"key2\", \"nonexisting\");\n        System.out.println(mgetResult3);    // >>> [Hello, World, null]","section_id":"examples"},{"id":"examples-ex7","language":"javascript","code":"await client.set('key1', 'Hello');\nawait client.set('key2', 'World');\n\nconst mgetResult = await client.mGet(['key1', 'key2', 'nonexisting']);\nconsole.log(mgetResult); // >>> [ 'Hello', 'World', null ]","section_id":"examples"},{"id":"examples-ex8","language":"javascript","code":"await redis.set('key1', 'Hello');\nawait redis.set('key2', 'World');\n\nconst mgetResult = await redis.mget('key1', 'key2', 'nonexisting');\nconsole.log(mgetResult); // >>> ['Hello', 'World', null]","section_id":"examples"},{"id":"examples-ex9","language":"javascript","code":"await client.set('key1', 'Hello');\nawait client.set('key2', 'World');\n\nconst mgetResult = await client.mGet(['key1', 'key2', 'nonexisting']);\nconsole.log(mgetResult); // >>> [ 'Hello', 'World', null ]","section_id":"examples"},{"id":"examples-ex10","language":"javascript","code":"await redis.set('key1', 'Hello');\nawait redis.set('key2', 'World');\n\nconst mgetResult = await redis.mget('key1', 'key2', 'nonexisting');\nconsole.log(mgetResult); // >>> ['Hello', 'World', null]","section_id":"examples"},{"id":"examples-ex11","language":"php","code":"$r->set('key1', 'Hello');\n        $r->set('key2', 'World');\n\n        $mgetResult = $r->mget(['key1', 'key2', 'nonexisting']);\n        echo json_encode($mgetResult) . PHP_EOL; // >>> [\"Hello\",\"World\",null]","section_id":"examples"},{"id":"examples-ex12","language":"python","code":"r.set(\"key1\", \"Hello\")\nr.set(\"key2\", \"World\")\n\nmget_result = r.mget(\"key1\", \"key2\", \"nonexisting\")\nprint(mget_result)\n# >>> ['Hello', 'World', None]","section_id":"examples"},{"id":"examples-ex13","language":"rust","code":"if let Ok(res) = r.set(\"key1\", \"Hello\").await {\n            let _: String = res;\n        }\n\n        if let Ok(res) = r.set(\"key2\", \"World\").await {\n            let _: String = res;\n        }\n\n        match r.mget(&[\"key1\", \"key2\", \"nonexisting\"]).await {\n            Ok(mget_result) => {\n                let mget_result: Vec<Option<String>> = mget_result;\n                println!(\"{mget_result:?}\");    // >>> [Some(\"Hello\"), Some(\"World\"), None]\n            }\n            Err(e) => {\n                println!(\"Error getting values: {e}\");\n            }\n        }","section_id":"examples"},{"id":"examples-ex14","language":"rust","code":"if let Ok(res) = r.set(\"key1\", \"Hello\") {\n            let _: String = res;\n        }\n\n        if let Ok(res) = r.set(\"key2\", \"World\") {\n            let _: String = res;\n        }\n\n        match r.mget(&[\"key1\", \"key2\", \"nonexisting\"]) {\n            Ok(mget_result) => {\n                let mget_result: Vec<Option<String>> = mget_result;\n                println!(\"{mget_result:?}\");    // >>> [Some(\"Hello\"), Some(\"World\"), None]\n            }\n            Err(e) => {\n                println!(\"Error getting values: {e}\");\n            }\n        }","section_id":"examples"}]}
{"schema_version":2,"id":"commands/migrate","title":"MIGRATE","url":"https://redis.io/docs/latest/commands/migrate/","summary":"Atomically transfers a key from one Redis instance to another.","since":"2.6.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"3f3da7d1d6141a8b81c25780b6974913f9f52ae57a9b2b724b0dd6d1921440f3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Atomically transfer a key from a source Redis instance to a destination Redis\ninstance.\nOn success the key is deleted from the original instance and is guaranteed to\nexist in the target instance.\n\nThe command is atomic and blocks the two instances for the time required to\ntransfer the key, at any given time the key will appear to exist in a given\ninstance or in the other instance, unless a timeout error occurs. In 3.2 and\nabove, multiple keys can be pipelined in a single call to `MIGRATE` by passing\nthe empty string (\"\") as key and adding the `KEYS` clause."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>host</code></summary>\n\nThe hostname or IP address of the destination Redis instance.\n\n</details>\n\n<details open><summary><code>port</code></summary>\n\nThe port of the destination Redis instance.\n\n</details>\n\n<details open><summary><code>key | \"\"</code></summary>\n\nThe key to migrate, or an empty string (`\"\"`) when migrating multiple keys with the `KEYS` option.\n\n</details>\n\n<details open><summary><code>destination-db</code></summary>\n\nThe database index to migrate the key into on the destination instance.\n\n</details>\n\n<details open><summary><code>timeout</code></summary>\n\nThe maximum idle time for the transfer, in milliseconds.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>COPY</code></summary>\n\nDo not remove the key from the local instance.\n\n</details>\n\n<details open><summary><code>REPLACE</code></summary>\n\nReplace an existing key on the destination instance.\n\n</details>\n\n<details open><summary><code>AUTH password</code></summary>\n\nAuthenticate to the destination instance with the given password. `AUTH` and `AUTH2` are mutually exclusive.\n\n</details>\n\n<details open><summary><code>AUTH2 username password</code></summary>\n\nAuthenticate to the destination instance with the given username and password (ACL-style auth, Redis 6 or later).\n\n</details>\n\n<details open><summary><code>KEYS key [key ...]</code></summary>\n\nMigrate all the listed keys. Use this when the key argument is an empty string (`\"\"`).\n\n</details>"},{"id":"details","title":"Details","role":"content","text":"The command internally uses [`DUMP`](https://redis.io/docs/latest/commands/dump) to generate the serialized version of the key\nvalue, and [`RESTORE`](https://redis.io/docs/latest/commands/restore) in order to synthesize the key in the target instance.\nThe source instance acts as a client for the target instance.\nIf the target instance returns OK to the [`RESTORE`](https://redis.io/docs/latest/commands/restore) command, the source instance\ndeletes the key using [`DEL`](https://redis.io/docs/latest/commands/del).\n\nThe timeout specifies the maximum idle time in any moment of the communication\nwith the destination instance in milliseconds.\nThis means that the operation does not need to be completed within the specified\namount of milliseconds, but that the transfer should make progresses without\nblocking for more than the specified amount of milliseconds.\n\n`MIGRATE` needs to perform I/O operations and to honor the specified timeout.\nWhen there is an I/O error during the transfer or if the timeout is reached the\noperation is aborted and the special error - `IOERR` returned.\nWhen this happens the following two cases are possible:\n\n* The key may be on both the instances.\n* The key may be only in the source instance.\n\nIt is not possible for the key to get lost in the event of a timeout, but the\nclient calling `MIGRATE`, in the event of a timeout error, should check if the\nkey is _also_ present in the target instance and act accordingly.\n\nWhen any other error is returned (starting with `ERR`) `MIGRATE` guarantees that\nthe key is still only present in the originating instance (unless a key with the\nsame name was also _already_ present on the target instance).\n\nIf there are no keys to migrate in the source instance `NOKEY` is returned.\nBecause missing keys are possible in normal conditions, from expiry for example,\n`NOKEY` isn't an error."},{"id":"migrating-multiple-keys-with-a-single-command-call","title":"Migrating multiple keys with a single command call","role":"content","text":"Starting with Redis 3.0.6 `MIGRATE` supports a new bulk-migration mode that\nuses pipelining in order to migrate multiple keys between instances without\nincurring in the round trip time latency and other overheads that there are\nwhen moving each key with a single `MIGRATE` call.\n\nIn order to enable this form, the `KEYS` option is used, and the normal *key*\nargument is set to an empty string. The actual key names will be provided\nafter the `KEYS` argument itself, like in the following example:\n\n    MIGRATE 192.168.1.34 6379 \"\" 0 5000 KEYS key1 key2 key3\n\nWhen this form is used the `NOKEY` status code is only returned when none\nof the keys is present in the instance, otherwise the command is executed, even if\njust a single key exists."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` on success.\n* [Simple string reply](../../develop/reference/protocol-spec#simple-strings): `NOKEY` when no keys were found in the source instance.\n\n**RESP3:**\n\nOne of the following:\n* [Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` on success.\n* [Simple string reply](../../develop/reference/protocol-spec#simple-strings): `NOKEY` when no keys were found in the source instance."}],"examples":[]}
{"schema_version":2,"id":"commands/module","title":"MODULE","url":"https://redis.io/docs/latest/commands/module/","summary":"A container for module commands.","since":"4.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"c3c8738dfa28c3d4ca4818f8f7ba5114c36bf8cccae537d8fadfada98da090e4","sections":[{"id":"content","title":"Content","role":"content","text":"This is a container command for module management commands.\n\nTo see the list of available commands you can call [`MODULE HELP`](https://redis.io/docs/latest/commands/module-help)."}],"examples":[]}
{"schema_version":2,"id":"commands/module-help","title":"MODULE HELP","url":"https://redis.io/docs/latest/commands/module-help/","summary":"Returns helpful text about the different subcommands.","since":"5.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"c20aaa79a7c5a08ba68a5ddde09461bda294ab4f15fb7a01e0f9337ddc391767","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `MODULE HELP` command returns a helpful text describing the different subcommands."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of sub-commands and their descriptions.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of sub-commands and their descriptions"}],"examples":[]}
{"schema_version":2,"id":"commands/module-list","title":"MODULE LIST","url":"https://redis.io/docs/latest/commands/module-list/","summary":"Returns all loaded modules.","since":"4.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"ae907960a10a7b6e24733e61af8c235cc2a7dae72ee3b130d394e2212fbc8394","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns information about the modules loaded to the server."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): list of loaded modules. Each element in the list represents a represents a module, and is in itself a list of property names and their values. The following properties is reported for each loaded module:\n* name: the name of the module.\n* ver: the version of the module.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): list of loaded modules. Each element in the list represents a represents a module, and is a [Map reply](../../develop/reference/protocol-spec#maps) of property names and their values. The following properties is reported for each loaded module:\n* name: the name of the module.\n* ver: the version of the module."}],"examples":[]}
{"schema_version":2,"id":"commands/module-load","title":"MODULE LOAD","url":"https://redis.io/docs/latest/commands/module-load/","summary":"Loads a module.","since":"4.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"8f7652e826d2c1a39e2080dd487b610f44b152e467561a42916415e50d897374","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Loads a module from a dynamic library at runtime.\n\nThis command loads and initializes the Redis module from the dynamic library\nspecified by the `path` argument. The `path` should be the absolute path of the\nlibrary, including the full filename. Any additional arguments are passed\nunmodified to the module.\n\n**Note**: modules can also be loaded at server startup with `loadmodule`\nconfiguration directive in `redis.conf`.\n\n> [!NOTE]\n> As of Redis 8 in Redis Open Source, loading a module using the Redis CLI with configuration parameters is deprecated."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>path</code></summary>\n\nThe absolute path to the module library file.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>arg [arg ...]</code></summary>\n\nArguments passed to the module's initialization function.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the module was loaded.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the module was loaded."}],"examples":[]}
{"schema_version":2,"id":"commands/module-loadex","title":"MODULE LOADEX","url":"https://redis.io/docs/latest/commands/module-loadex/","summary":"Loads a module using extended parameters.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"b4efdf839591ea95231b7c5a90ba352aad58807dfe72637e3972f1cea56ef884","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Loads a module from a dynamic library at runtime with configuration directives.\n\nThis is an extended version of the [`MODULE LOAD`](https://redis.io/docs/latest/commands/module-load) command.\n\nIt loads and initializes the Redis module from the dynamic library specified by the `path` argument. The `path` should be the absolute path of the library, including the full filename.\n\nYou can use the optional `CONFIG` argument to provide the module with configuration directives.\nAny additional arguments that follow the `ARGS` keyword are passed unmodified to the module.\n\n**Note**: modules can also be loaded at server startup with `loadmodule`\nconfiguration directive in `redis.conf`."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>path</code></summary>\n\nThe absolute path to the module library file.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>CONFIG name value [CONFIG name value ...]</code></summary>\n\nOne or more module configuration name-value pairs to set at load time.\n\n</details>\n\n<details open><summary><code>ARGS args [args ...]</code></summary>\n\nAdditional arguments passed to the module's initialization function.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the module was loaded.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the module was loaded."}],"examples":[]}
{"schema_version":2,"id":"commands/module-unload","title":"MODULE UNLOAD","url":"https://redis.io/docs/latest/commands/module-unload/","summary":"Unloads a module.","since":"4.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"1e77f6b5483b6782be4f076250ffe39f94b366a0b9e19930627c6486f68cfdff","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Unloads a module.\n\nThis command unloads the module specified by `name`. Note that the module's name\nis reported by the [`MODULE LIST`](https://redis.io/docs/latest/commands/module-list) command, and may differ from the dynamic\nlibrary's filename.\n\nKnown limitations:\n\n*   Modules that register custom data types can not be unloaded."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>name</code></summary>\n\nThe name of the module to unload, as reported by `MODULE LIST`.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the module was unloaded.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the module was unloaded."}],"examples":[]}
{"schema_version":2,"id":"commands/monitor","title":"MONITOR","url":"https://redis.io/docs/latest/commands/monitor/","summary":"Listens for all requests received by the server in real-time.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"3c31e9928b5dcde5009f0ca58b2d049a0de692cc01f01512224c3fbde6f4efb0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"`MONITOR` is a debugging command that streams back every command processed by\nthe Redis server.\nIt can help in understanding what is happening to the database.\nThis command can both be used via `redis-cli` and via `telnet`.\n\nThe ability to see all the requests processed by the server is useful in order\nto spot bugs in an application both when using Redis as a database and as a\ndistributed caching system.\n\n[code example]\n\nUse `SIGINT` (Ctrl-C) to stop a `MONITOR` stream running via `redis-cli`.\n\n[code example]\n\nManually issue the [`QUIT`](https://redis.io/docs/latest/commands/quit) or [`RESET`](https://redis.io/docs/latest/commands/reset) commands to stop a `MONITOR` stream running\nvia `telnet`."},{"id":"commands-not-logged-by-monitor","title":"Commands not logged by MONITOR","role":"syntax","text":"Because of security concerns, no administrative commands are logged\nby `MONITOR`'s output and sensitive data is redacted in the command [`AUTH`](https://redis.io/docs/latest/commands/auth).\n\nFurthermore, the command [`QUIT`](https://redis.io/docs/latest/commands/quit) is also not logged."},{"id":"cost-of-running-monitor","title":"Cost of running MONITOR","role":"content","text":"Because `MONITOR` streams back **all** commands, its use comes at a cost.\nThe following (totally unscientific) benchmark numbers illustrate what the cost\nof running `MONITOR` can be.\n\nBenchmark result **without** `MONITOR` running:\n\n[code example]\n\nBenchmark result **with** `MONITOR` running (`redis-cli monitor > /dev/null`):\n\n[code example]\n\nIn this particular case, running a single `MONITOR` client can reduce the\nthroughput by more than 50%.\nRunning more `MONITOR` clients will reduce throughput even more."},{"id":"behavior-change-history","title":"Behavior change history","role":"content","text":"*   `>= 6.0.0`: [`AUTH`](https://redis.io/docs/latest/commands/auth) excluded from the command's output.\n*   `>= 6.2.0`: \"[`RESET`](https://redis.io/docs/latest/commands/reset) can be called to exit monitor mode.\n*   `>= 6.2.4`: \"[`AUTH`](https://redis.io/docs/latest/commands/auth), [`HELLO`](https://redis.io/docs/latest/commands/hello), [`EVAL`](https://redis.io/docs/latest/commands/eval), [`EVAL_RO`](https://redis.io/docs/latest/commands/eval_ro), [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) and [`EVALSHA_RO`](https://redis.io/docs/latest/commands/evalsha_ro) included in the command's output."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n**Non-standard return value**. Dumps the received commands in an infinite flow.\n\n**RESP3:**\n\n**Non-standard return value**. Dumps the received commands in an infinite flow."}],"examples":[{"id":"overview-ex0","language":"plaintext","code":"$ redis-cli monitor\n1339518083.107412 [0 127.0.0.1:60866] \"keys\" \"*\"\n1339518087.877697 [0 127.0.0.1:60866] \"dbsize\"\n1339518090.420270 [0 127.0.0.1:60866] \"set\" \"x\" \"6\"\n1339518096.506257 [0 127.0.0.1:60866] \"get\" \"x\"\n1339518099.363765 [0 127.0.0.1:60866] \"eval\" \"return redis.call('set','x','7')\" \"0\"\n1339518100.363799 [0 lua] \"set\" \"x\" \"7\"\n1339518100.544926 [0 127.0.0.1:60866] \"del\" \"x\"","section_id":"overview"},{"id":"overview-ex1","language":"plaintext","code":"$ telnet localhost 6379\nTrying 127.0.0.1...\nConnected to localhost.\nEscape character is '^]'.\nMONITOR\n+OK\n+1339518083.107412 [0 127.0.0.1:60866] \"keys\" \"*\"\n+1339518087.877697 [0 127.0.0.1:60866] \"dbsize\"\n+1339518090.420270 [0 127.0.0.1:60866] \"set\" \"x\" \"6\"\n+1339518096.506257 [0 127.0.0.1:60866] \"get\" \"x\"\n+1339518099.363765 [0 127.0.0.1:60866] \"del\" \"x\"\n+1339518100.544926 [0 127.0.0.1:60866] \"get\" \"x\"\nQUIT\n+OK\nConnection closed by foreign host.","section_id":"overview"},{"id":"cost-of-running-monitor-ex0","language":"plaintext","code":"$ src/redis-benchmark -c 10 -n 100000 -q\nPING_INLINE: 101936.80 requests per second\nPING_BULK: 102880.66 requests per second\nSET: 95419.85 requests per second\nGET: 104275.29 requests per second\nINCR: 93283.58 requests per second","section_id":"cost-of-running-monitor"},{"id":"cost-of-running-monitor-ex1","language":"plaintext","code":"$ src/redis-benchmark -c 10 -n 100000 -q\nPING_INLINE: 58479.53 requests per second\nPING_BULK: 59136.61 requests per second\nSET: 41823.50 requests per second\nGET: 45330.91 requests per second\nINCR: 41771.09 requests per second","section_id":"cost-of-running-monitor"}]}
{"schema_version":2,"id":"commands/move","title":"MOVE","url":"https://redis.io/docs/latest/commands/move/","summary":"Moves a key to another database.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"5554d13f696f5adcb6751257427729265e5e54d8edce7fe8bf20b14a91179012","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Move `key` from the currently selected database (see [`SELECT`](https://redis.io/docs/latest/commands/select)) to the specified\ndestination database.\nWhen `key` already exists in the destination database, or it does not exist in\nthe source database, it does nothing.\nIt is possible to use `MOVE` as a locking primitive because of this."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key.\n\n</details>\n\n<details open><summary><code>db</code></summary>\n\nThe destination database index.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | Redis Software does not support shared databases due to potential negative performance impacts and blocks any related commands. |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): `1` if _key_ was moved.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0` if _key_ wasn't moved.\n\n**RESP3:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): `1` if _key_ was moved.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0` if _key_ wasn't moved."}],"examples":[]}
{"schema_version":2,"id":"commands/mset","title":"MSET","url":"https://redis.io/docs/latest/commands/mset/","summary":"Atomically creates or modifies the string values of one or more keys.","since":"1.0.1","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:12+01:00","page_type":"content","content_hash":"3a919c87e6e254a6a3089ebc928cf5c832f5909a28c0c18ba72220c74d35ebde","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nSets the given keys to their respective values.\n`MSET` replaces existing values with new values, just as regular [`SET`](https://redis.io/docs/latest/commands/set).\nSee [`MSETNX`](https://redis.io/docs/latest/commands/msetnx) if you don't want to overwrite existing values.\n\n`MSET` is atomic, so all given keys are set at once.\nIt is not possible for clients to see that some of the keys were updated while\nothers are unchanged."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key value [key value ...]</code></summary>\n\nOne or more key-value pairs to set.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> MSET key1 \"Hello\" key2 \"World\"\nOK\nredis> GET key1\n\"Hello\"\nredis> GET key2\n\"World\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): always `OK` because `MSET` can't fail.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): always `OK` because `MSET` can't fail."}],"examples":[]}
{"schema_version":2,"id":"commands/msetex","title":"MSETEX","url":"https://redis.io/docs/latest/commands/msetex/","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.","since":"8.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:12+01:00","page_type":"content","content_hash":"b190a47085a32430a788dc456c51d218fa54b0d20325f01a6fc438cf6ab7441c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Atomically sets multiple string keys with an optional shared expiration in a single operation."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>numkeys</code></summary>\n\nThe number of keys being set.\n\n</details>\n\n<details open><summary><code>key value [key value ...]</code></summary>\n\nA series of key/value pairs.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"The following options modify the command's behavior. The condition options (`NX`, `XX`) are mutually exclusive, as are the expiration options (`EX`, `PX`, `EXAT`, `PXAT`, `KEEPTTL`).\n\n<details open><summary><code>NX</code></summary>\n\nSet the keys and their expiration time only if none of the specified keys exist.\n\n</details>\n\n<details open><summary><code>XX</code></summary>\n\nSet the keys and their expiration time only if all of the specified keys already exist.\n\n</details>\n\n<details open><summary><code>EX seconds</code></summary>\n\nSet the specified expiration time in seconds (a positive integer).\n\n</details>\n\n<details open><summary><code>PX milliseconds</code></summary>\n\nSet the specified expiration time in milliseconds (a positive integer).\n\n</details>\n\n<details open><summary><code>EXAT unix-time-seconds</code></summary>\n\nSet the specified Unix time in seconds (a positive integer) at which the keys will expire.\n\n</details>\n\n<details open><summary><code>PXAT unix-time-milliseconds</code></summary>\n\nSet the specified Unix time in milliseconds (a positive integer) at which the keys will expire.\n\n</details>\n\n<details open><summary><code>KEEPTTL</code></summary>\n\nRetain the time to live associated with the keys.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): 0 if none of the keys were set; 1 if all of the keys were set.\n\n**RESP3:**\n\n[Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): 0 if none of the keys were set; 1 if all of the keys were set."}],"examples":[]}
{"schema_version":2,"id":"commands/msetnx","title":"MSETNX","url":"https://redis.io/docs/latest/commands/msetnx/","summary":"Atomically modifies the string values of one or more keys only when all keys don't exist.","since":"1.0.1","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:12+01:00","page_type":"content","content_hash":"036d121d6df4b5feb922a8a86648d92622d1d493aa19eaf6f3e30cca3bf9fd29","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nSets the given keys to their respective values.\n`MSETNX` will not perform any operation at all even if just a single key already\nexists.\n\nBecause of this semantic `MSETNX` can be used in order to set different keys\nrepresenting different fields of a unique logic object in a way that ensures\nthat either all the fields or none at all are set.\n\n`MSETNX` is atomic, so all given keys are set at once.\nIt is not possible for clients to see that some of the keys were updated while\nothers are unchanged."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key value [key value ...]</code></summary>\n\nOne or more key-value pairs to set. No keys are set if any of them already exists.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> MSETNX key1 \"Hello\" key2 \"there\"\n(integer) 1\nredis> MSETNX key2 \"new\" key3 \"world\"\n(integer) 0\nredis> MGET key1 key2 key3\n1) \"Hello\"\n2) \"there\"\n3) (nil)"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0` if no key was set (at least one key already existed).\n* [Integer reply](../../develop/reference/protocol-spec#integers): `1` if all the keys were set.\n\n**RESP3:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0` if no key was set (at least one key already existed).\n* [Integer reply](../../develop/reference/protocol-spec#integers): `1` if all the keys were set."}],"examples":[]}
{"schema_version":2,"id":"commands/multi","title":"MULTI","url":"https://redis.io/docs/latest/commands/multi/","summary":"Starts a transaction.","since":"1.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"dfd56ee42ac84d478f37dcc6a9cff99b2c87b24dc5fc4ce7971dd351665aabf0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nMarks the start of a [transaction](https://redis.io/docs/latest/develop/using-commands/transactions) block.\nSubsequent commands will be queued for atomic execution using [`EXEC`](https://redis.io/docs/latest/commands/exec)."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[]}
{"schema_version":2,"id":"commands/object","title":"OBJECT","url":"https://redis.io/docs/latest/commands/object/","summary":"A container for object introspection commands.","since":"2.2.3","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"063f8ca3ebd65b9964cf192a12a9a42e732e4817f8f1073245bef57bfe4526dd","sections":[{"id":"content","title":"Content","role":"content","text":"This is a container command for object introspection commands.\n\nTo see the list of available commands you can call [`OBJECT HELP`](https://redis.io/docs/latest/commands/object-help)."}],"examples":[]}
{"schema_version":2,"id":"commands/object-encoding","title":"OBJECT ENCODING","url":"https://redis.io/docs/latest/commands/object-encoding/","summary":"Returns the internal encoding of a Redis object.","since":"2.2.3","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-06-15T06:26:34-07:00","page_type":"content","content_hash":"318f1120e50a0208b182a69878ec18afe99dea5572edd5ae32359c1cbb43b3da","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns the internal encoding for the Redis object stored at `key`."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key.\n\n</details>"},{"id":"details","title":"Details","role":"content","text":"Redis objects can be encoded in different ways:\n\n* Strings can be encoded as: \n\n    - `raw`, normal string encoding.\n    - `int`, strings representing integers in a 64-bit signed interval, encoded in this way to save space.\n    - `embstr`, an embedded string, which is an object where the internal simple dynamic string, `sds`, is an unmodifiable string allocated in the same chuck as the object itself.\n      `embstr` can be strings with lengths up to the hardcoded limit of `OBJ_ENCODING_EMBSTR_SIZE_LIMIT` or 44 bytes. \n\n* Lists can be encoded as:\n \n    - `linkedlist`, simple list encoding. No longer used, an old list encoding.\n    - `ziplist`, Redis <= 6.2, a space-efficient encoding used for small lists.\n    - `listpack`, Redis >= 7.0, a space-efficient encoding used for small lists.\n    - `quicklist`, encoded as linkedlist of ziplists or listpacks.\n\n* Sets can be encoded as:\n\n    - `hashtable`, normal set encoding.\n    - `intset`, a special encoding used for small sets composed solely of integers.\n    - `listpack`, Redis >= 7.2, a space-efficient encoding used for small sets.\n\n* Hashes can be encoded as:\n\n    - `zipmap`, no longer used, an old hash encoding.\n    - `hashtable`, normal hash encoding.\n    - `ziplist`, Redis <= 6.2, a space-efficient encoding used for small hashes.\n    - `listpack`, Redis >= 7.0, a space-efficient encoding used for small hashes.\n\n* Sorted Sets can be encoded as:\n\n    - `skiplist`, normal sorted set encoding.\n    - `ziplist`, Redis <= 6.2, a space-efficient encoding used for small sorted sets.\n    - `listpack`, Redis >= 7.0, a space-efficient encoding used for small sorted sets.\n\n* Streams can be encoded as:\n\n  - `stream`, encoded as a radix tree of listpacks.\n\nAll the specially encoded types are automatically converted to the general type once you perform an operation that makes it impossible for Redis to retain the space saving encoding."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | For Active-Active databases, use `CRDT.DEBUG ENCODING` instead. |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): if the key doesn't exist.\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the encoding of the object.\n\n**RESP3:**\n\nOne of the following:\n* [Null reply](../../develop/reference/protocol-spec#nulls): if the key doesn't exist.\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the encoding of the object."}],"examples":[]}
{"schema_version":2,"id":"commands/object-freq","title":"OBJECT FREQ","url":"https://redis.io/docs/latest/commands/object-freq/","summary":"Returns the logarithmic access frequency counter of a Redis object.","since":"4.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-06-15T06:26:34-07:00","page_type":"content","content_hash":"fbb42d85578b19aab692ce5a7d006091818e1af8cbd30c6a8327035bd9894737","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This command returns the logarithmic access frequency counter of a Redis object stored at `key`.\n\nThe command is only available when the `maxmemory-policy` configuration directive is set to one of the LFU policies."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n[Integer reply](../../develop/reference/protocol-spec#integers): the counter's value.\n[Nil reply](../../develop/reference/protocol-spec#bulk-strings): if _key_ doesn't exist.\n\n**RESP3:**\n\nOne of the following:\n[Integer reply](../../develop/reference/protocol-spec#integers): the counter's value.\n[Null reply](../../develop/reference/protocol-spec#nulls): if _key_ doesn't exist."}],"examples":[]}
{"schema_version":2,"id":"commands/object-help","title":"OBJECT HELP","url":"https://redis.io/docs/latest/commands/object-help/","summary":"Returns helpful text about the different subcommands.","since":"6.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"45918cb3eaa3ad03eb7e5c47d1bff3a6c2456e4abe2286cc1eecaa07d9d8b6bf","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `OBJECT HELP` command returns a helpful text describing the different subcommands."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of sub-commands and their descriptions\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of sub-commands and their descriptions."}],"examples":[]}
{"schema_version":2,"id":"commands/object-idletime","title":"OBJECT IDLETIME","url":"https://redis.io/docs/latest/commands/object-idletime/","summary":"Returns the time since the last access to a Redis object.","since":"2.2.3","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-06-15T06:26:34-07:00","page_type":"content","content_hash":"44fcfc4968c17fd3a2d86f5902c2b3a73b161b1fa305a6810208095a394a5b19","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This command returns the time in seconds since the last access to the value stored at `key`.\n\nThe command is only available when the `maxmemory-policy` configuration directive is not set to one of the LFU policies."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n[Integer reply](../../develop/reference/protocol-spec#integers): the idle time in seconds.\n[Nil reply](../../develop/reference/protocol-spec#bulk-strings): if _key_ doesn't exist.\n\n**RESP3:**\n\nOne of the following:\n[Integer reply](../../develop/reference/protocol-spec#integers): the idle time in seconds.\n[Null reply](../../develop/reference/protocol-spec#nulls): if _key_ doesn't exist."}],"examples":[]}
{"schema_version":2,"id":"commands/object-refcount","title":"OBJECT REFCOUNT","url":"https://redis.io/docs/latest/commands/object-refcount/","summary":"Returns the reference count of a value of a key.","since":"2.2.3","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-06-15T06:26:34-07:00","page_type":"content","content_hash":"6d9b7b7d117fda12bceaec646ad2efa1e5d4c14ff157943cc5e940d08f301aef","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This command returns the reference count of the stored at `key`."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of references.\n[Nil reply](../../develop/reference/protocol-spec#bulk-strings): if _key_ doesn't exist.\n\n**RESP3:**\n\nOne of the following:\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of references.\n[Null reply](../../develop/reference/protocol-spec#nulls): if _key_ doesn't exist."}],"examples":[]}
{"schema_version":2,"id":"commands/persist","title":"PERSIST","url":"https://redis.io/docs/latest/commands/persist/","summary":"Removes the expiration time of a key.","since":"2.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"5081a7f40b375e1689496e78e282cb22ea28766860c9281c9a91ce178aa91d18","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Remove the existing timeout on `key`, turning the key from _volatile_ (a key\nwith an expire set) to _persistent_ (a key that will never expire as no timeout\nis associated)."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SET mykey \"Hello\"\nOK\nredis> EXPIRE mykey 10\n(integer) 1\nredis> TTL mykey\n(integer) 10\nredis> PERSIST mykey\n(integer) 1\nredis> TTL mykey\n(integer) -1"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0` if _key_ does not exist or does not have an associated timeout.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `1` if the timeout has been removed.\n\n**RESP3:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0` if _key_ does not exist or does not have an associated timeout.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `1` if the timeout has been removed."}],"examples":[]}
{"schema_version":2,"id":"commands/pexpire","title":"PEXPIRE","url":"https://redis.io/docs/latest/commands/pexpire/","summary":"Sets the expiration time of a key in milliseconds.","since":"2.6.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"50686052d4f43b27ca0d66dd5b66a8299f8fd4561809a84787b44f8817eb6c82","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This command works exactly like [`EXPIRE`](https://redis.io/docs/latest/commands/expire) but the time to live of the key is\nspecified in milliseconds instead of seconds."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key to set a timeout on.\n\n</details>\n\n<details open><summary><code>milliseconds</code></summary>\n\nThe timeout, in milliseconds.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"The following options modify the command's behavior. They are mutually exclusive.\n\n<details open><summary><code>NX</code></summary>\n\nSet expiry only when the key has no expiry.\n\n</details>\n\n<details open><summary><code>XX</code></summary>\n\nSet expiry only when the key has an existing expiry.\n\n</details>\n\n<details open><summary><code>GT</code></summary>\n\nSet expiry only when the new expiry is greater than the current one. A non-volatile key is treated as an infinite TTL for the purpose of `GT`.\n\n</details>\n\n<details open><summary><code>LT</code></summary>\n\nSet expiry only when the new expiry is less than the current one. A non-volatile key is treated as an infinite TTL for the purpose of `LT`.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SET mykey \"Hello\"\nOK\nredis> PEXPIRE mykey 1500\n(integer) 1\nredis> TTL mykey\n(integer) 2\nredis> PTTL mykey\n(integer) 1500\nredis> PEXPIRE mykey 1000 XX\n(integer) 1\nredis> TTL mykey\n(integer) 1\nredis> PEXPIRE mykey 1000 NX\n(integer) 0\nredis> TTL mykey\n(integer) 1"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0` if the timeout was not set. For example, if the key doesn't exist, or the operation skipped because of the provided arguments.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `1` if the timeout was set.\n\n**RESP3:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0` if the timeout was not set. For example, if the key doesn't exist, or the operation skipped because of the provided arguments.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `1` if the timeout was set."}],"examples":[]}
{"schema_version":2,"id":"commands/pexpireat","title":"PEXPIREAT","url":"https://redis.io/docs/latest/commands/pexpireat/","summary":"Sets the expiration time of a key to a Unix milliseconds timestamp.","since":"2.6.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"15d543edbdd5f5d78e18daa9860bd2f19b94a90fd5dc6746f29cadfd76b36c83","sections":[{"id":"overview","title":"Overview","role":"overview","text":"`PEXPIREAT` has the same effect and semantic as [`EXPIREAT`](https://redis.io/docs/latest/commands/expireat), but the Unix time at\nwhich the key will expire is specified in milliseconds instead of seconds."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key to set a timeout on.\n\n</details>\n\n<details open><summary><code>unix-time-milliseconds</code></summary>\n\nThe absolute Unix expiration timestamp, in milliseconds since Unix epoch. A timestamp in the past deletes the key immediately.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"The following options modify the command's behavior. They are mutually exclusive.\n\n<details open><summary><code>NX</code></summary>\n\nSet expiry only when the key has no expiry.\n\n</details>\n\n<details open><summary><code>XX</code></summary>\n\nSet expiry only when the key has an existing expiry.\n\n</details>\n\n<details open><summary><code>GT</code></summary>\n\nSet expiry only when the new expiry is greater than the current one. A non-volatile key is treated as an infinite TTL for the purpose of `GT`.\n\n</details>\n\n<details open><summary><code>LT</code></summary>\n\nSet expiry only when the new expiry is less than the current one. A non-volatile key is treated as an infinite TTL for the purpose of `LT`.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SET mykey \"Hello\"\nOK\nredis> PEXPIREAT mykey 1555555555005\n(integer) 1\nredis> TTL mykey\n(integer) -2\nredis> PTTL mykey\n(integer) -2"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): `1` if the timeout was set.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0` if the timeout was not set. For example, if the key doesn't exist, or the operation was skipped due to the provided arguments.\n\n**RESP3:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): `1` if the timeout was set.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0` if the timeout was not set. For example, if the key doesn't exist, or the operation was skipped due to the provided arguments."}],"examples":[]}
{"schema_version":2,"id":"commands/pexpiretime","title":"PEXPIRETIME","url":"https://redis.io/docs/latest/commands/pexpiretime/","summary":"Returns the expiration time of a key as a Unix milliseconds timestamp.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"5602b219adde06a6acac90331cc10218cfdcc527bf5de5658e8906c03280b085","sections":[{"id":"overview","title":"Overview","role":"overview","text":"`PEXPIRETIME` has the same semantic as [`EXPIRETIME`](https://redis.io/docs/latest/commands/expiretime), but returns the absolute Unix expiration timestamp in milliseconds instead of seconds."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SET mykey \"Hello\"\nOK\nredis> PEXPIREAT mykey 33177117420000\n(integer) 1\nredis> PEXPIRETIME mykey\n(integer) 33177117420000"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): Expiration Unix timestamp in milliseconds.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `-1` if the key exists but has no associated expiration time.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `-2` if the key does not exist.\n\n**RESP3:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): Expiration Unix timestamp in milliseconds.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `-1` if the key exists but has no associated expiration time.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `-2` if the key does not exist."}],"examples":[]}
{"schema_version":2,"id":"commands/pfadd","title":"PFADD","url":"https://redis.io/docs/latest/commands/pfadd/","summary":"Adds elements to a HyperLogLog key. Creates the key if it doesn't exist.","since":"2.8.9","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:43+01:00","page_type":"content","content_hash":"df72e56da4b96e56e22e1f328ecf3040325da5d5b7c9ce8241779a1e11900e71","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Adds all the element arguments to the HyperLogLog data structure stored at the variable name specified as first argument.\n\nAs a side effect of this command the HyperLogLog internals may be updated to reflect a different estimation of the number of unique items added so far (the cardinality of the set).\n\nIf the approximated cardinality estimated by the HyperLogLog changed after executing the command, `PFADD` returns 1, otherwise 0 is returned. The command automatically creates an empty HyperLogLog structure (that is, a Redis String of a specified length and with a given encoding) if the specified key does not exist.\n\nTo call the command without elements but just the variable name is valid, this will result into no operation performed if the variable already exists, or just the creation of the data structure if the key does not exist (in the latter case 1 is returned).\n\nFor an introduction to the HyperLogLog data structure check its  [data structure page](https://redis.io/docs/latest/develop/data-types/probabilistic/hyperloglogs) and the [`PFCOUNT`](https://redis.io/docs/latest/commands/pfcount) command page."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the HyperLogLog key.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>element [element ...]</code></summary>\n\nOne or more elements to add to the HyperLogLog. With no elements, the command only creates the key if it does not already exist.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> PFADD hll a b c d e f g\n(integer) 1\nredis> PFCOUNT hll\n(integer) 7"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): `1` if at least one HyperLogLog internal register was altered.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0` if no HyperLogLog internal registers were altered.\n\n**RESP3:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): `1` if at least one HyperLogLog internal register was altered.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0` if no HyperLogLog internal registers were altered."}],"examples":[]}
{"schema_version":2,"id":"commands/pfcount","title":"PFCOUNT","url":"https://redis.io/docs/latest/commands/pfcount/","summary":"Returns the approximated cardinality of the set(s) observed by the HyperLogLog key(s).","since":"2.8.9","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:43+01:00","page_type":"content","content_hash":"4615bc12eab48fe527dfce3025de6a23674a6f8d1da9973dc888164bd7107be8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\nWhen called with a single key, `PFCOUNT` returns the approximated cardinality computed by the HyperLogLog data structure stored at the specified `key`, which is 0 if the `key` does not exist.\n\nWhen called with multiple keys, `PFCOUNT` returns the approximated cardinality of the union of the HyperLogLogs passed by internally merging the HyperLogLogs stored at the provided keys into a temporary HyperLogLog.\n\nThe HyperLogLog data structure can be used in order to count unique elements in a set using just a small constant amount of memory, specifically 12k bytes for every HyperLogLog (plus a few bytes for the key itself).\n\nThe returned cardinality of the observed set is not exact, but approximated with a standard error of 0.81%.\n\nFor example, to take the count of all the unique search queries performed in a day, a program needs to call [`PFADD`](https://redis.io/docs/latest/commands/pfadd) every time a query is processed. The estimated number of unique queries can be retrieved with `PFCOUNT` at any time.\n\nNote: as a side effect of calling this function, it is possible that the HyperLogLog is modified, since the last 8 bytes encode the latest computed cardinality\nfor caching purposes. So `PFCOUNT` is technically a write command."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key [key ...]</code></summary>\n\nOne or more HyperLogLog keys. With multiple keys, the command returns the approximated cardinality of their union.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> PFADD hll foo bar zap\n(integer) 1\nredis> PFADD hll zap zap zap\n(integer) 0\nredis> PFADD hll foo bar\n(integer) 0\nredis> PFCOUNT hll\n(integer) 3\nredis> PFADD some-other-hll 1 2 3\n(integer) 1\nredis> PFCOUNT hll some-other-hll\n(integer) 6"},{"id":"details","title":"Details","role":"content","text":""},{"id":"performances","title":"Performances","role":"performance","text":"When `PFCOUNT` is called with a single key, performances are excellent even if\nin theory constant times to process a dense HyperLogLog are high. This is\npossible because the `PFCOUNT` uses caching in order to remember the cardinality\npreviously computed, that rarely changes because most [`PFADD`](https://redis.io/docs/latest/commands/pfadd) operations will\nnot update any register. Hundreds of operations per second are possible.\n\nWhen `PFCOUNT` is called with multiple keys, an on-the-fly merge of the\nHyperLogLogs is performed, which is slow, moreover the cardinality of the union\ncan't be cached, so when used with multiple keys `PFCOUNT` may take a time in\nthe order of magnitude of the millisecond, and should be not abused.\n\nThe user should take in mind that single-key and multiple-keys executions of\nthis command are semantically different and have different performances."},{"id":"hyperloglog-representation","title":"HyperLogLog representation","role":"content","text":"Redis HyperLogLogs are represented using a double representation: the *sparse* representation suitable for HLLs counting a small number of elements (resulting in a small number of registers set to non-zero value), and a *dense* representation suitable for higher cardinalities. Redis automatically switches from the sparse to the dense representation when needed.\n\nThe sparse representation uses a run-length encoding optimized to store efficiently a big number of registers set to zero. The dense representation is a Redis string of 12288 bytes in order to store 16384 6-bit counters. The need for the double representation comes from the fact that using 12k (which is the dense representation memory requirement) to encode just a few registers for smaller cardinalities is extremely suboptimal.\n\nBoth representations are prefixed with a 16 bytes header, that includes a magic, an encoding / version field, and the cached cardinality estimation computed, stored in little endian format (the most significant bit is 1 if the estimation is invalid since the HyperLogLog was updated since the cardinality was computed).\n\nThe HyperLogLog, being a Redis string, can be retrieved with [`GET`](https://redis.io/docs/latest/commands/get) and restored with [`SET`](https://redis.io/docs/latest/commands/set). Calling [`PFADD`](https://redis.io/docs/latest/commands/pfadd), `PFCOUNT` or [`PFMERGE`](https://redis.io/docs/latest/commands/pfmerge) commands with a corrupted HyperLogLog is never a problem, it may return random values but does not affect the stability of the server. Most of the times when corrupting a sparse representation, the server recognizes the corruption and returns an error.\n\nThe representation is neutral from the point of view of the processor word size and endianness, so the same representation is used by 32 bit and 64 bit processor, big endian or little endian.\n\nMore details about the Redis HyperLogLog implementation can be found in [this blog post](http://antirez.com/news/75). The source code of the implementation in the `hyperloglog.c` file is also easy to read and understand, and includes a full specification for the exact encoding used for the sparse and dense representations."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the approximated number of unique elements observed via `PFADD`.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the approximated number of unique elements observed via `PFADD`"}],"examples":[]}
{"schema_version":2,"id":"commands/pfdebug","title":"PFDEBUG","url":"https://redis.io/docs/latest/commands/pfdebug/","summary":"Internal commands for debugging HyperLogLog values.","since":"2.8.9","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-06-15T06:26:34-07:00","page_type":"content","content_hash":"daa846e5682efcddec9152409fc55471df04b8321b09257af68f9e54942c67d0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `PFDEBUG` command is an internal command.\nIt is meant to be used for developing and testing Redis."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"}],"examples":[]}
{"schema_version":2,"id":"commands/pfmerge","title":"PFMERGE","url":"https://redis.io/docs/latest/commands/pfmerge/","summary":"Merges one or more HyperLogLog values into a single key.","since":"2.8.9","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:43+01:00","page_type":"content","content_hash":"ceaf7d6d2b6ba069d5f98af47123e7ece59f808f757e1846775758d7e70e08d2","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nMerge multiple HyperLogLog values into a unique value that will approximate\nthe cardinality of the union of the observed sets of the source HyperLogLog\nstructures.\n\nThe computed merged HyperLogLog is set to the destination variable, which is\ncreated if it does not exist (defaulting to an empty HyperLogLog).\n\nIf the destination variable exists, it is treated as one of the source sets \nand its cardinality will be included in the cardinality of the computed\nHyperLogLog."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>destkey</code></summary>\n\nThe destination HyperLogLog key in which to store the merged result.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>sourcekey [sourcekey ...]</code></summary>\n\nOne or more source HyperLogLog keys to merge into `destkey`. `destkey` itself is also included in the merge.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> PFADD hll1 foo bar zap a\n(integer) 1\nredis> PFADD hll2 a b c foo\n(integer) 1\nredis> PFMERGE hll3 hll1 hll2\nOK\nredis> PFCOUNT hll3\n(integer) 6"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[]}
{"schema_version":2,"id":"commands/pfselftest","title":"PFSELFTEST","url":"https://redis.io/docs/latest/commands/pfselftest/","summary":"An internal command for testing HyperLogLog values.","since":"2.8.9","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-06-15T06:26:34-07:00","page_type":"content","content_hash":"085bda0545a7aaf34d7283ee5a23f4d492714da1b766af905beb0d28983b1083","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `PFSELFTEST` command is an internal command.\nIt is meant to be used for developing and testing Redis."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[]}
{"schema_version":2,"id":"commands/ping","title":"PING","url":"https://redis.io/docs/latest/commands/ping/","summary":"Returns the server's liveliness response.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"4b3673869f0e4d22188f612107a67394011b7751ab374fbf017dd18de565f3ac","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This command returns the bulk string `PONG` if no argument is provided, otherwise it returns a copy of the\nprovided `message` as a bulk string.\n\nThis command is useful for:\n1. Testing whether a connection is still alive.\n1. Verifying the server's ability to serve data - an error is returned when this isn't the case (for example, during load from persistence data or accessing a stale replica).\n1. Measuring latency.\n\nIf the client is subscribed to a channel or a pattern, it will instead return a\nmulti-bulk string with `PONG` in the first position and an empty bulk string in the second\nposition, unless an argument is provided, in which case it returns a copy\nof the argument."},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>message</code></summary>\n\nA message to return instead of `PONG`.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> PING\nPONG\nredis> PING \"hello world\"\n\"hello world\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nAny of the following:\n* [Simple string reply](../../develop/reference/protocol-spec#simple-strings): `PONG` when no argument is provided.\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the provided argument.\n\n**RESP3:**\n\nAny of the following:\n* [Simple string reply](../../develop/reference/protocol-spec#simple-strings): `PONG` when no argument is provided.\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the provided argument."}],"examples":[]}
{"schema_version":2,"id":"commands/psetex","title":"PSETEX","url":"https://redis.io/docs/latest/commands/psetex/","summary":"Sets both string value and expiration time in milliseconds of a key. The key is created if it doesn't exist.","since":"2.6.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:12+01:00","page_type":"content","content_hash":"1ef4cf974c69cda7e2beaee3383b03aaae1dd320f157d9aea3ca2f06f476948d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"`PSETEX` works exactly like [`SETEX`](https://redis.io/docs/latest/commands/setex) with the sole difference that the expire\ntime is specified in milliseconds instead of seconds."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key.\n\n</details>\n\n<details open><summary><code>milliseconds</code></summary>\n\nThe time to live, in milliseconds.\n\n</details>\n\n<details open><summary><code>value</code></summary>\n\nThe value to set.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> PSETEX mykey 1000 \"Hello\"\nOK\nredis> PTTL mykey\n(integer) 1000\nredis> GET mykey\n\"Hello\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[]}
{"schema_version":2,"id":"commands/psubscribe","title":"PSUBSCRIBE","url":"https://redis.io/docs/latest/commands/psubscribe/","summary":"Listens for messages published to channels that match one or more patterns.","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:25+01:00","page_type":"content","content_hash":"cc0602da15df93121f8bb38769eeb1d4fa357baa115b701b140395788e231e98","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Subscribes the client to the given patterns.\n\n\nOnce the client enters the subscribed state it is not supposed to issue any other commands, except for additional [`SUBSCRIBE`](https://redis.io/docs/latest/commands/subscribe), [`SSUBSCRIBE`](https://redis.io/docs/latest/commands/ssubscribe), `PSUBSCRIBE`, [`UNSUBSCRIBE`](https://redis.io/docs/latest/commands/unsubscribe), [`SUNSUBSCRIBE`](https://redis.io/docs/latest/commands/sunsubscribe), [`PUNSUBSCRIBE`](https://redis.io/docs/latest/commands/punsubscribe), [`PING`](https://redis.io/docs/latest/commands/ping), [`RESET`](https://redis.io/docs/latest/commands/reset) and [`QUIT`](https://redis.io/docs/latest/commands/quit) commands.\nHowever, if RESP3 is used (see [`HELLO`](https://redis.io/docs/latest/commands/hello)), it is possible for a client to issue any commands while in a subscribed state.\n\nFor more information, see [Pub/sub](https://redis.io/docs/latest/develop/pubsub)."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>pattern [pattern ...]</code></summary>\n\nOne or more glob-style patterns to subscribe to. Messages published to any channel whose name matches a pattern are delivered to the client.\n\nSupported glob-style patterns:\n\n* `h?llo` subscribes to `hello`, `hallo` and `hxllo`\n* `h*llo` subscribes to `hllo` and `heeeello`\n* `h[ae]llo` subscribes to `hello` and `hallo,` but not `hillo`\n\nUse `\\` to escape special characters if you want to match them verbatim.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nWhen successful, this command doesn't return anything. Instead, for each pattern, one message with the first element being the string `psubscribe` is pushed as a confirmation that the command succeeded.\n\n**RESP3:**\n\nWhen successful, this command doesn't return anything. Instead, for each pattern, one message with the first element being the string `psubscribe` is pushed as a confirmation that the command succeeded."}],"examples":[]}
{"schema_version":2,"id":"commands/psync","title":"PSYNC","url":"https://redis.io/docs/latest/commands/psync/","summary":"An internal command used in replication.","since":"2.8.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"58a8c965d1c09fe677b945fe08fe37dd9bea13ad0dcdccc65037fc5303bcb594","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Initiates a replication stream from the master.\n\nThe `PSYNC` command is called by Redis replicas for initiating a replication\nstream from the master.\n\nFor more information about replication in Redis please check the\n[replication page](https://redis.io/docs/latest/operate/oss_and_stack/management/replication)."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>replicationid</code></summary>\n\nThe replication ID of the master.\n\n</details>\n\n<details open><summary><code>offset</code></summary>\n\nThe replication offset to resume from.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n**Non-standard return value**, a bulk transfer of the data followed by `PING` and write requests from the master.\n\n**RESP3:**\n\n**Non-standard return value**, a bulk transfer of the data followed by `PING` and write requests from the master."}],"examples":[]}
{"schema_version":2,"id":"commands/pttl","title":"PTTL","url":"https://redis.io/docs/latest/commands/pttl/","summary":"Returns the expiration time in milliseconds of a key.","since":"2.6.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"a1b38dedd4b8f6e4b4c6efbd3ab9bf0963fddbf1373dc2fe0b3fda38210f7fab","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Like [`TTL`](https://redis.io/docs/latest/commands/ttl) this command returns the remaining time to live of a key that has an\nexpire set, with the sole difference that [`TTL`](https://redis.io/docs/latest/commands/ttl) returns the amount of remaining\ntime in seconds while `PTTL` returns it in milliseconds.\n\nIn Redis 2.6 or older the command returns `-1` if the key does not exist or if the key exist but has no associated expire.\n\nStarting with Redis 2.8 the return value in case of error changed:\n\n* The command returns `-2` if the key does not exist.\n* The command returns `-1` if the key exists but has no associated expire."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SET mykey \"Hello\"\nOK\nredis> EXPIRE mykey 1\n(integer) 1\nredis> PTTL mykey\n(integer) 1000"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): TTL in milliseconds.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `-1` if the key exists but has no associated expiration.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `-2` if the key does not exist.\n\n**RESP3:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): TTL in milliseconds.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `-1` if the key exists but has no associated expiration.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `-2` if the key does not exist."}],"examples":[]}
{"schema_version":2,"id":"commands/publish","title":"PUBLISH","url":"https://redis.io/docs/latest/commands/publish/","summary":"Posts a message to a channel.","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-06-15T06:26:34-07:00","page_type":"content","content_hash":"8c6e1b32ca1b9b2b8a8a13f744fdfa8985a86ddb10244a554d3e1f9236897c23","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Posts a message to the given channel.\n\nIn a Redis Cluster, clients can publish to every node. The cluster makes sure\nthat published messages are forwarded as needed, so clients can subscribe to any\nchannel by connecting to any one of the nodes."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>channel</code></summary>\n\nThe channel to publish to.\n\n</details>\n\n<details open><summary><code>message</code></summary>\n\nThe message to publish.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of clients that the message was sent to. Note that in a Redis Cluster, only clients that are connected to the same node as the publishing client are included in the count.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of clients that the message was sent to. Note that in a Redis Cluster, only clients that are connected to the same node as the publishing client are included in the count."}],"examples":[]}
{"schema_version":2,"id":"commands/pubsub","title":"PUBSUB","url":"https://redis.io/docs/latest/commands/pubsub/","summary":"A container for Pub/Sub commands.","since":"2.8.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:25+01:00","page_type":"content","content_hash":"1d93aabe9cdc986f09948615f67f43e601af9741cb220f222c175c693e91f5b0","sections":[{"id":"content","title":"Content","role":"content","text":"This is a container command for Pub/Sub introspection commands.\n\nTo see the list of available commands you can call [`PUBSUB HELP`](https://redis.io/docs/latest/commands/pubsub-help)."}],"examples":[]}
{"schema_version":2,"id":"commands/pubsub-channels","title":"PUBSUB CHANNELS","url":"https://redis.io/docs/latest/commands/pubsub-channels/","summary":"Returns the active channels.","since":"2.8.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:25+01:00","page_type":"content","content_hash":"b94f85d50afe8e3cada192e5dcd9328ec3f4f37558c22092af94527cbc16f283","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Lists the currently active Pub/Sub channels.\n\nAn active channel is a Pub/Sub channel with one or more subscribers (excluding clients subscribed to patterns).\n\nIf no `pattern` is specified, all the channels are listed, otherwise if pattern is specified only channels matching the specified glob-style pattern are listed.\n\nCluster note: in a Redis Cluster, clients can subscribe to every node and can also publish to every other node. The cluster will make sure that published messages are forwarded as needed. That said, [`PUBSUB`](https://redis.io/docs/latest/commands/pubsub)'s replies in a cluster only report information from the node's Pub/Sub context, rather than the entire cluster."},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>pattern</code></summary>\n\nList only active channels whose names match the given glob-style pattern. If omitted, all active channels are listed.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of active channels, optionally matching the specified pattern.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of active channels, optionally matching the specified pattern."}],"examples":[]}
{"schema_version":2,"id":"commands/pubsub-help","title":"PUBSUB HELP","url":"https://redis.io/docs/latest/commands/pubsub-help/","summary":"Returns helpful text about the different subcommands.","since":"6.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"56568f2cdceaa2d9135969dcbf9fa827054e7a7274ff44b13864351e72876ede","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `PUBSUB HELP` command returns a helpful text describing the different subcommands."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of sub-commands and their descriptions.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of sub-commands and their descriptions."}],"examples":[]}
{"schema_version":2,"id":"commands/pubsub-numpat","title":"PUBSUB NUMPAT","url":"https://redis.io/docs/latest/commands/pubsub-numpat/","summary":"Returns a count of unique pattern subscriptions.","since":"2.8.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:25+01:00","page_type":"content","content_hash":"03d174afe48b38811156bea55e89add443a652a8cdbf3c28394073bcbb6b3c2b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns the number of unique patterns that are subscribed to by clients (that are performed using the [`PSUBSCRIBE`](https://redis.io/docs/latest/commands/psubscribe) command).\n\nNote that this isn't the count of clients subscribed to patterns, but the total number of unique patterns all the clients are subscribed to.\n\nCluster note: in a Redis Cluster clients can subscribe to every node, and can also publish to every other node. The cluster will make sure that published messages are forwarded as needed. That said, [`PUBSUB`](https://redis.io/docs/latest/commands/pubsub)'s replies in a cluster only report information from the node's Pub/Sub context, rather than the entire cluster."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of patterns all the clients are subscribed to.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of patterns all the clients are subscribed to."}],"examples":[]}
{"schema_version":2,"id":"commands/pubsub-numsub","title":"PUBSUB NUMSUB","url":"https://redis.io/docs/latest/commands/pubsub-numsub/","summary":"Returns a count of subscribers to channels.","since":"2.8.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:25+01:00","page_type":"content","content_hash":"0b169e2249647a383a9245ef03c0eab5f1b11a92db0c94ba1fbb0daaf1c1d90a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns the number of subscribers (exclusive of clients subscribed to patterns) for the specified channels.\n\nNote that it is valid to call this command without channels. In this case it will just return an empty list.\n\nCluster note: in a Redis Cluster, clients can subscribe to every node, and can also publish to every other node. The cluster will make sure that published messages are forwarded as needed. That said, [`PUBSUB`](https://redis.io/docs/latest/commands/pubsub)'s replies in a cluster only report information from the node's Pub/Sub context, rather than the entire cluster."},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>channel [channel ...]</code></summary>\n\nOne or more channels to report the subscriber count for.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): the number of subscribers per channel, each even element (including the 0th) is channel name, each odd element is the number of subscribers\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): the number of subscribers per channel, each even element (including the 0th) is channel name, each odd element is the number of subscribers"}],"examples":[]}
{"schema_version":2,"id":"commands/pubsub-shardchannels","title":"PUBSUB SHARDCHANNELS","url":"https://redis.io/docs/latest/commands/pubsub-shardchannels/","summary":"Returns the active shard channels.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-06-15T06:26:34-07:00","page_type":"content","content_hash":"ffe1842c8b365f23b8c49eafcd39ae8ace029915d3a4d16cc893f670a47dc1e4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Lists the currently active shard channels.\n\nAn active shard channel is a Pub/Sub shard channel with one or more subscribers.\n\nIf no `pattern` is specified, all the channels are listed, otherwise if pattern is specified, only channels matching the specified glob-style pattern are listed.\n\nThe information returned about the active shard channels are at the shard level and not at the cluster level."},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>pattern</code></summary>\n\nList only active shard channels whose names match the given glob-style pattern. If omitted, all active shard channels are listed.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of active channels, optionally matching the specified pattern.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of active channels, optionally matching the specified pattern."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> PUBSUB SHARDCHANNELS\n1) \"orders\"\n> PUBSUB SHARDCHANNELS o*\n1) \"orders\"","section_id":"examples"}]}
{"schema_version":2,"id":"commands/pubsub-shardnumsub","title":"PUBSUB SHARDNUMSUB","url":"https://redis.io/docs/latest/commands/pubsub-shardnumsub/","summary":"Returns the count of subscribers of shard channels.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:25+01:00","page_type":"content","content_hash":"1c61b2b25c2dea34716ea42142e888bb84601f4115b5ee7ae82725f70b1fbac9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns the number of subscribers for the specified shard channels.\n\nNote that it is valid to call this command without channels, in this case it will just return an empty list.\n\nCluster note: in a Redis Cluster, [`PUBSUB`](https://redis.io/docs/latest/commands/pubsub)'s replies in a cluster only report information from the node's Pub/Sub context, rather than the entire cluster."},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>shardchannel [shardchannel ...]</code></summary>\n\nOne or more shard channels to report the subscriber count for.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): the number of subscribers per shard channel, each even element (including the 0th) is channel name, each odd element is the number of subscribers.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): the number of subscribers per shard channel, each even element (including the 0th) is channel name, each odd element is the number of subscribers."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> PUBSUB SHARDNUMSUB orders\n1) \"orders\"\n2) (integer) 1","section_id":"examples"}]}
{"schema_version":2,"id":"commands/punsubscribe","title":"PUNSUBSCRIBE","url":"https://redis.io/docs/latest/commands/punsubscribe/","summary":"Stops listening to messages published to channels that match one or more patterns.","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-06-15T06:26:34-07:00","page_type":"content","content_hash":"276fffa3cf4e48e74d37260c2bb196ad0e761466da1f077c8333c1f4d2c47476","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Unsubscribes the client from the specified patterns, or from all patterns if none are specified.\n\nIf you don’t specify any patterns, the client unsubscribes from all previously subscribed patterns. The client receives one message for each pattern it unsubscribes from."},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>pattern [pattern ...]</code></summary>\n\nOne or more patterns to unsubscribe from. If omitted, the client is unsubscribed from all patterns.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nWhen successful, this command doesn't return anything. Instead, for each pattern, one message with the first element being the string `punsubscribe` is pushed as a confirmation that the command succeeded.\n\n**RESP3:**\n\nWhen successful, this command doesn't return anything. Instead, for each pattern, one message with the first element being the string `punsubscribe` is pushed as a confirmation that the command succeeded."}],"examples":[]}
{"schema_version":2,"id":"commands/quit","title":"QUIT","url":"https://redis.io/docs/latest/commands/quit/","summary":"Closes the connection.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"c862201b0f72acb1797d56491f18d4886c7250a08e963033c85df58e1b919d59","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Ask the server to close the connection.\nThe connection is closed as soon as all pending replies have been written to the\nclient.\n\n**Note:** Clients should not use this command.\nInstead, clients should simply close the connection when they're not used anymore.\nTerminating a connection on the client side is preferable, as it eliminates `TIME_WAIT` lingering sockets on the server side."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Deprecated as of Redis v7.2.0. |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): OK.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[]}
{"schema_version":2,"id":"commands/randomkey","title":"RANDOMKEY","url":"https://redis.io/docs/latest/commands/randomkey/","summary":"Returns a random key name from the database.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"e06c1c464d2e5d326beadc6ba66b21c7e0dea64fee4defc832b1bf2cfb4a4980","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Return a random key from the currently selected database."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): when the database is empty.\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): a random key in database.\n\n**RESP3:**\n\nOne of the following:\n* [Null reply](../../develop/reference/protocol-spec#nulls): when the database is empty.\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): a random key in the database."}],"examples":[]}
{"schema_version":2,"id":"commands/readonly","title":"READONLY","url":"https://redis.io/docs/latest/commands/readonly/","summary":"Enables read-only queries for a connection to a Redis Cluster replica node.","since":"3.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"83ba9c9da48620a266c035c3432a217295c82c0f4ce41eb258902e44c37916c0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Enables read queries for a connection to a Redis Cluster replica node. \n\nNormally replica nodes will redirect clients to the authoritative master for\nthe hash slot involved in a given command, however clients can use replicas\nin order to scale reads using the `READONLY` command.\n\n`READONLY` tells a Redis Cluster replica node that the client is willing to\nread possibly stale data and is not interested in running write queries.\n\nWhen the connection is in readonly mode, the cluster will send a redirection\nto the client only if the operation involves keys not served by the replica's\nmaster node. This may happen because:\n\n1. The client sent a command about hash slots never served by the master of this replica.\n2. The cluster was reconfigured (for example resharded) and the replica is no longer able to serve commands for a given hash slot."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[]}
{"schema_version":2,"id":"commands/readwrite","title":"READWRITE","url":"https://redis.io/docs/latest/commands/readwrite/","summary":"Enables read-write queries for a connection to a Redis Cluster replica node.","since":"3.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"d796cd31c3febefc13c45524be491a2b6b3fbe22ea2053bc82868eb55978bfc7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Disables read queries for a connection to a Redis Cluster replica node.\n\nRead queries against a Redis Cluster replica node are disabled by default,\nbut you can use the [`READONLY`](https://redis.io/docs/latest/commands/readonly) command to change this behavior on a per-\nconnection basis. The `READWRITE` command resets the readonly mode flag\nof a connection back to readwrite."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[]}
{"schema_version":2,"id":"commands/redis-6-2-commands","title":"Redis 6.2 Commands Reference","url":"https://redis.io/docs/latest/commands/redis-6-2-commands/","summary":"Complete list of all Redis commands available in version 6.2, organized by functional group","tags":["docs","develop","stack","oss","rs","rc","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"c6923a83e12d32657ffb0e436c79bf02425b7af19d4c9aea22359b763c46e405","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This page provides a comprehensive reference of all Redis commands available in Redis 6.2, organized by functional group. Each command includes its description and syntax in a collapsible section for easy navigation.\n\n> [!NOTE]\n> Redis 6.2 includes all commands from previous versions plus new commands introduced in 6.2. Commands marked with **⭐ New in 6.2** were added in this release.\n>\n> Command page links take you to the *current implementation* of each command."},{"id":"quick-navigation","title":"Quick Navigation","role":"content","text":"- [String commands](#string-commands)\n- [Hash commands](#hash-commands)\n- [List commands](#list-commands)\n- [Set commands](#set-commands)\n- [Sorted set commands](#sorted-set-commands)\n- [Stream commands](#stream-commands)\n- [Bitmap commands](#bitmap-commands)\n- [HyperLogLog commands](#hyperloglog-commands)\n- [Geospatial commands](#geospatial-commands)\n- [JSON commands](#json-commands)\n- [Search commands](#search-commands)\n- [Time series commands](#time-series-commands)\n- [Pub/Sub commands](#pubsub-commands)\n- [Transaction commands](#transaction-commands)\n- [Scripting commands](#scripting-commands)\n- [Connection commands](#connection-commands)\n- [Server commands](#server-commands)\n- [Cluster commands](#cluster-commands)\n- [Generic commands](#generic-commands)\n\n---"},{"id":"string-commands","title":"String commands","role":"content","text":"String commands operate on string values, the most basic Redis data type.\n\n<details>\n<summary><strong><a href=\"/commands/append/\">APPEND</a></strong> - Appends a string to the value of a key. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `APPEND key value`\n\n**Description:** Appends a string to the value of a key. Creates the key if it doesn't exist.\n\n**Complexity:** O(1). The amortized time complexity is O(1) assuming the appended value is small and the already present value is of any size, since the dynamic string library used by Redis will double the free space available on every reallocation.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/decr/\">DECR</a></strong> - Decrements the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `DECR key`\n\n**Description:** Decrements the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/decrby/\">DECRBY</a></strong> - Decrements a number from the integer value of a key. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `DECRBY key decrement`\n\n**Description:** Decrements a number from the integer value of a key. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/get/\">GET</a></strong> - Returns the string value of a key.</summary>\n\n**Syntax:** `GET key`\n\n**Description:** Returns the string value of a key.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getdel/\">GETDEL</a></strong> - Returns the string value of a key after deleting the key. <span style=\"color: #e74c3c;\">⭐ New in 6.2</span></summary>\n\n**Syntax:** `GETDEL key`\n\n**Description:** Returns the string value of a key after deleting the key.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getex/\">GETEX</a></strong> - Returns the string value of a key after setting its expiration time. <span style=\"color: #e74c3c;\">⭐ New in 6.2</span></summary>\n\n**Syntax:** `GETEX key [EX seconds | PX milliseconds | EXAT unix-time-seconds |\n  PXAT unix-time-milliseconds | PERSIST]`\n\n**Description:** Returns the string value of a key after setting its expiration time.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getrange/\">GETRANGE</a></strong> - Returns a substring of the string stored at a key.</summary>\n\n**Syntax:** `GETRANGE key start end`\n\n**Description:** Returns a substring of the string stored at a key.\n\n**Complexity:** O(N) where N is the length of the returned string. The complexity is ultimately determined by the returned length, but because creating a substring from an existing string is very cheap, it can be considered O(1) for small strings.\n\n**Since:** 2.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getset/\">GETSET</a></strong> - Returns the previous string value of a key after setting it to a new value.</summary>\n\n**Syntax:** `GETSET key value`\n\n**Description:** Returns the previous string value of a key after setting it to a new value.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/incr/\">INCR</a></strong> - Increments the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `INCR key`\n\n**Description:** Increments the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/incrby/\">INCRBY</a></strong> - Increments the integer value of a key by a number. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `INCRBY key increment`\n\n**Description:** Increments the integer value of a key by a number. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/incrbyfloat/\">INCRBYFLOAT</a></strong> - Increment the floating point value of a key by a number. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `INCRBYFLOAT key increment`\n\n**Description:** Increment the floating point value of a key by a number. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/mget/\">MGET</a></strong> - Atomically returns the string values of one or more keys.</summary>\n\n**Syntax:** `MGET key [key ...]`\n\n**Description:** Atomically returns the string values of one or more keys.\n\n**Complexity:** O(N) where N is the number of keys to retrieve.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/mset/\">MSET</a></strong> - Atomically creates or modifies the string values of one or more keys.</summary>\n\n**Syntax:** `MSET key value [key value ...]`\n\n**Description:** Atomically creates or modifies the string values of one or more keys.\n\n**Complexity:** O(N) where N is the number of keys to set.\n\n**Since:** 1.0.1\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/msetnx/\">MSETNX</a></strong> - Atomically modifies the string values of one or more keys only when all keys don't exist.</summary>\n\n**Syntax:** `MSETNX key value [key value ...]`\n\n**Description:** Atomically modifies the string values of one or more keys only when all keys don't exist.\n\n**Complexity:** O(N) where N is the number of keys to set.\n\n**Since:** 1.0.1\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/psetex/\">PSETEX</a></strong> - Sets both string value and expiration time in milliseconds of a key. The key is created if it doesn't exist.</summary>\n\n**Syntax:** `PSETEX key milliseconds value`\n\n**Description:** Sets both string value and expiration time in milliseconds of a key. The key is created if it doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/set/\">SET</a></strong> - Sets the string value of a key, ignoring its type. The key is created if it doesn't exist.</summary>\n\n**Syntax:** `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]`\n\n**Description:** Sets the string value of a key, ignoring its type. The key is created if it doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/setex/\">SETEX</a></strong> - Sets the string value and expiration time of a key. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `SETEX key seconds value`\n\n**Description:** Sets the string value and expiration time of a key. Creates the key if it doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/setnx/\">SETNX</a></strong> - Set the string value of a key only when the key doesn't exist.</summary>\n\n**Syntax:** `SETNX key value`\n\n**Description:** Set the string value of a key only when the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/setrange/\">SETRANGE</a></strong> - Overwrites a part of a string value with another by an offset. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `SETRANGE key offset value`\n\n**Description:** Overwrites a part of a string value with another by an offset. Creates the key if it doesn't exist.\n\n**Complexity:** O(1), not counting the time taken to copy the new string in place. Usually, this string is very small so the amortized complexity is O(1). Otherwise, complexity is O(M) with M being the length of the value argument.\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/strlen/\">STRLEN</a></strong> - Returns the length of a string value.</summary>\n\n**Syntax:** `STRLEN key`\n\n**Description:** Returns the length of a string value.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/substr/\">SUBSTR</a></strong> - Returns a substring from a string value.</summary>\n\n**Syntax:** `SUBSTR key start end`\n\n**Description:** Returns a substring from a string value.\n\n**Complexity:** O(N) where N is the length of the returned string. The complexity is ultimately determined by the returned length, but because creating a substring from an existing string is very cheap, it can be considered O(1) for small strings.\n\n**Since:** 1.0.0\n\n</details>"},{"id":"hash-commands","title":"Hash commands","role":"content","text":"Hash commands operate on hash data structures, which map string fields to string values.\n\n<details>\n<summary><strong><a href=\"/commands/hdel/\">HDEL</a></strong> - Deletes one or more fields and their values from a hash. Deletes the hash if no fields remain.</summary>\n\n**Syntax:** `HDEL key field [field ...]`\n\n**Description:** Deletes one or more fields and their values from a hash. Deletes the hash if no fields remain.\n\n**Complexity:** O(N) where N is the number of fields to be removed.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hexists/\">HEXISTS</a></strong> - Determines whether a field exists in a hash.</summary>\n\n**Syntax:** `HEXISTS key field`\n\n**Description:** Determines whether a field exists in a hash.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hget/\">HGET</a></strong> - Returns the value of a field in a hash.</summary>\n\n**Syntax:** `HGET key field`\n\n**Description:** Returns the value of a field in a hash.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hgetall/\">HGETALL</a></strong> - Returns all fields and values in a hash.</summary>\n\n**Syntax:** `HGETALL key`\n\n**Description:** Returns all fields and values in a hash.\n\n**Complexity:** O(N) where N is the size of the hash.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hincrby/\">HINCRBY</a></strong> - Increments the integer value of a field in a hash by a number. Uses 0 as initial value if the field doesn't exist.</summary>\n\n**Syntax:** `HINCRBY key field increment`\n\n**Description:** Increments the integer value of a field in a hash by a number. Uses 0 as initial value if the field doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hincrbyfloat/\">HINCRBYFLOAT</a></strong> - Increments the floating point value of a field by a number. Uses 0 as initial value if the field doesn't exist.</summary>\n\n**Syntax:** `HINCRBYFLOAT key field increment`\n\n**Description:** Increments the floating point value of a field by a number. Uses 0 as initial value if the field doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hkeys/\">HKEYS</a></strong> - Returns all fields in a hash.</summary>\n\n**Syntax:** `HKEYS key`\n\n**Description:** Returns all fields in a hash.\n\n**Complexity:** O(N) where N is the size of the hash.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hlen/\">HLEN</a></strong> - Returns the number of fields in a hash.</summary>\n\n**Syntax:** `HLEN key`\n\n**Description:** Returns the number of fields in a hash.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hmget/\">HMGET</a></strong> - Returns the values of all fields in a hash.</summary>\n\n**Syntax:** `HMGET key field [field ...]`\n\n**Description:** Returns the values of all fields in a hash.\n\n**Complexity:** O(N) where N is the number of fields being requested.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hmset/\">HMSET</a></strong> - Sets the values of multiple fields.</summary>\n\n**Syntax:** `HMSET key field value [field value ...]`\n\n**Description:** Sets the values of multiple fields.\n\n**Complexity:** O(N) where N is the number of fields being set.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hrandfield/\">HRANDFIELD</a></strong> - Returns one or more random fields from a hash. <span style=\"color: #e74c3c;\">⭐ New in 6.2</span></summary>\n\n**Syntax:** `HRANDFIELD key [count [WITHVALUES]]`\n\n**Description:** Returns one or more random fields from a hash.\n\n**Complexity:** O(N) where N is the number of fields returned\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hscan/\">HSCAN</a></strong> - Iterates over fields and values of a hash.</summary>\n\n**Syntax:** `HSCAN key cursor [MATCH pattern] [COUNT count] [NOVALUES]`\n\n**Description:** Iterates over fields and values of a hash.\n\n**Complexity:** O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hset/\">HSET</a></strong> - Creates or modifies the value of a field in a hash.</summary>\n\n**Syntax:** `HSET key field value [field value ...]`\n\n**Description:** Creates or modifies the value of a field in a hash.\n\n**Complexity:** O(1) for each field/value pair added, so O(N) to add N field/value pairs when the command is called with multiple field/value pairs.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hsetnx/\">HSETNX</a></strong> - Sets the value of a field in a hash only when the field doesn't exist.</summary>\n\n**Syntax:** `HSETNX key field value`\n\n**Description:** Sets the value of a field in a hash only when the field doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hstrlen/\">HSTRLEN</a></strong> - Returns the length of the value of a field.</summary>\n\n**Syntax:** `HSTRLEN key field`\n\n**Description:** Returns the length of the value of a field.\n\n**Complexity:** O(1)\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hvals/\">HVALS</a></strong> - Returns all values in a hash.</summary>\n\n**Syntax:** `HVALS key`\n\n**Description:** Returns all values in a hash.\n\n**Complexity:** O(N) where N is the size of the hash.\n\n**Since:** 2.0.0\n\n</details>"},{"id":"list-commands","title":"List commands","role":"content","text":"List commands operate on lists of strings, ordered by insertion order.\n\n<details>\n<summary><strong><a href=\"/commands/blmove/\">BLMOVE</a></strong> - Pops an element from a list, pushes it to another list and returns it. Blocks until an element is available otherwise. Deletes the list if the last element was moved. <span style=\"color: #e74c3c;\">⭐ New in 6.2</span></summary>\n\n**Syntax:** `BLMOVE source destination <LEFT | RIGHT> <LEFT | RIGHT> timeout`\n\n**Description:** Pops an element from a list, pushes it to another list and returns it. Blocks until an element is available otherwise. Deletes the list if the last element was moved.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/blpop/\">BLPOP</a></strong> - Removes and returns the first element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `BLPOP key [key ...] timeout`\n\n**Description:** Removes and returns the first element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.\n\n**Complexity:** O(N) where N is the number of provided keys.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/brpop/\">BRPOP</a></strong> - Removes and returns the last element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `BRPOP key [key ...] timeout`\n\n**Description:** Removes and returns the last element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.\n\n**Complexity:** O(N) where N is the number of provided keys.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/brpoplpush/\">BRPOPLPUSH</a></strong> - Pops an element from a list, pushes it to another list and returns it. Block until an element is available otherwise. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `BRPOPLPUSH source destination timeout`\n\n**Description:** Pops an element from a list, pushes it to another list and returns it. Block until an element is available otherwise. Deletes the list if the last element was popped.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lindex/\">LINDEX</a></strong> - Returns an element from a list by its index.</summary>\n\n**Syntax:** `LINDEX key index`\n\n**Description:** Returns an element from a list by its index.\n\n**Complexity:** O(N) where N is the number of elements to traverse to get to the element at index. This makes asking for the first or the last element of the list O(1).\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/linsert/\">LINSERT</a></strong> - Inserts an element before or after another element in a list.</summary>\n\n**Syntax:** `LINSERT key <BEFORE | AFTER> pivot element`\n\n**Description:** Inserts an element before or after another element in a list.\n\n**Complexity:** O(N) where N is the number of elements to traverse before seeing the value pivot. This means that inserting somewhere on the left end on the list (head) can be considered O(1) and inserting somewhere on the right end (tail) is O(N).\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/llen/\">LLEN</a></strong> - Returns the length of a list.</summary>\n\n**Syntax:** `LLEN key`\n\n**Description:** Returns the length of a list.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lmove/\">LMOVE</a></strong> - Returns an element after popping it from one list and pushing it to another. Deletes the list if the last element was moved. <span style=\"color: #e74c3c;\">⭐ New in 6.2</span></summary>\n\n**Syntax:** `LMOVE source destination <LEFT | RIGHT> <LEFT | RIGHT>`\n\n**Description:** Returns an element after popping it from one list and pushing it to another. Deletes the list if the last element was moved.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lpop/\">LPOP</a></strong> - Returns the first elements in a list after removing it. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `LPOP key [count]`\n\n**Description:** Returns the first elements in a list after removing it. Deletes the list if the last element was popped.\n\n**Complexity:** O(N) where N is the number of elements returned\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lpos/\">LPOS</a></strong> - Returns the index of matching elements in a list.</summary>\n\n**Syntax:** `LPOS key element [RANK rank] [COUNT num-matches] [MAXLEN len]`\n\n**Description:** Returns the index of matching elements in a list.\n\n**Complexity:** O(N) where N is the number of elements in the list, for the average case. When searching for elements near the head or the tail of the list, or when the MAXLEN option is provided, the command may run in constant time.\n\n**Since:** 6.0.6\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lpush/\">LPUSH</a></strong> - Prepends one or more elements to a list. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `LPUSH key element [element ...]`\n\n**Description:** Prepends one or more elements to a list. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lpushx/\">LPUSHX</a></strong> - Prepends one or more elements to a list only when the list exists.</summary>\n\n**Syntax:** `LPUSHX key element [element ...]`\n\n**Description:** Prepends one or more elements to a list only when the list exists.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lrange/\">LRANGE</a></strong> - Returns a range of elements from a list.</summary>\n\n**Syntax:** `LRANGE key start stop`\n\n**Description:** Returns a range of elements from a list.\n\n**Complexity:** O(S+N) where S is the distance of start offset from HEAD for small lists, from nearest end (HEAD or TAIL) for large lists; and N is the number of elements in the specified range.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lrem/\">LREM</a></strong> - Removes elements from a list. Deletes the list if the last element was removed.</summary>\n\n**Syntax:** `LREM key count element`\n\n**Description:** Removes elements from a list. Deletes the list if the last element was removed.\n\n**Complexity:** O(N+M) where N is the length of the list and M is the number of elements removed.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lset/\">LSET</a></strong> - Sets the value of an element in a list by its index.</summary>\n\n**Syntax:** `LSET key index element`\n\n**Description:** Sets the value of an element in a list by its index.\n\n**Complexity:** O(N) where N is the length of the list. Setting either the first or the last element of the list is O(1).\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ltrim/\">LTRIM</a></strong> - Removes elements from both ends a list. Deletes the list if all elements were trimmed.</summary>\n\n**Syntax:** `LTRIM key start stop`\n\n**Description:** Removes elements from both ends a list. Deletes the list if all elements were trimmed.\n\n**Complexity:** O(N) where N is the number of elements to be removed by the operation.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rpop/\">RPOP</a></strong> - Returns and removes the last elements of a list. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `RPOP key [count]`\n\n**Description:** Returns and removes the last elements of a list. Deletes the list if the last element was popped.\n\n**Complexity:** O(N) where N is the number of elements returned\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rpoplpush/\">RPOPLPUSH</a></strong> - Returns the last element of a list after removing and pushing it to another list. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `RPOPLPUSH source destination`\n\n**Description:** Returns the last element of a list after removing and pushing it to another list. Deletes the list if the last element was popped.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rpush/\">RPUSH</a></strong> - Appends one or more elements to a list. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `RPUSH key element [element ...]`\n\n**Description:** Appends one or more elements to a list. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rpushx/\">RPUSHX</a></strong> - Appends an element to a list only when the list exists.</summary>\n\n**Syntax:** `RPUSHX key element [element ...]`\n\n**Description:** Appends an element to a list only when the list exists.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 2.2.0\n\n</details>"},{"id":"set-commands","title":"Set commands","role":"content","text":"Set commands operate on unordered collections of unique strings.\n\n<details>\n<summary><strong><a href=\"/commands/sadd/\">SADD</a></strong> - Adds one or more members to a set. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `SADD key member [member ...]`\n\n**Description:** Adds one or more members to a set. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/scard/\">SCARD</a></strong> - Returns the number of members in a set.</summary>\n\n**Syntax:** `SCARD key`\n\n**Description:** Returns the number of members in a set.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sdiff/\">SDIFF</a></strong> - Returns the difference of multiple sets.</summary>\n\n**Syntax:** `SDIFF key [key ...]`\n\n**Description:** Returns the difference of multiple sets.\n\n**Complexity:** O(N) where N is the total number of elements in all given sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sdiffstore/\">SDIFFSTORE</a></strong> - Stores the difference of multiple sets in a key.</summary>\n\n**Syntax:** `SDIFFSTORE destination key [key ...]`\n\n**Description:** Stores the difference of multiple sets in a key.\n\n**Complexity:** O(N) where N is the total number of elements in all given sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sinter/\">SINTER</a></strong> - Returns the intersect of multiple sets.</summary>\n\n**Syntax:** `SINTER key [key ...]`\n\n**Description:** Returns the intersect of multiple sets.\n\n**Complexity:** O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sinterstore/\">SINTERSTORE</a></strong> - Stores the intersect of multiple sets in a key.</summary>\n\n**Syntax:** `SINTERSTORE destination key [key ...]`\n\n**Description:** Stores the intersect of multiple sets in a key.\n\n**Complexity:** O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sismember/\">SISMEMBER</a></strong> - Determines whether a member belongs to a set.</summary>\n\n**Syntax:** `SISMEMBER key member`\n\n**Description:** Determines whether a member belongs to a set.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/smembers/\">SMEMBERS</a></strong> - Returns all members of a set.</summary>\n\n**Syntax:** `SMEMBERS key`\n\n**Description:** Returns all members of a set.\n\n**Complexity:** O(N) where N is the set cardinality.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/smismember/\">SMISMEMBER</a></strong> - Determines whether multiple members belong to a set. <span style=\"color: #e74c3c;\">⭐ New in 6.2</span></summary>\n\n**Syntax:** `SMISMEMBER key member [member ...]`\n\n**Description:** Determines whether multiple members belong to a set.\n\n**Complexity:** O(N) where N is the number of elements being checked for membership\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/smove/\">SMOVE</a></strong> - Moves a member from one set to another.</summary>\n\n**Syntax:** `SMOVE source destination member`\n\n**Description:** Moves a member from one set to another.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/spop/\">SPOP</a></strong> - Returns one or more random members from a set after removing them. Deletes the set if the last member was popped.</summary>\n\n**Syntax:** `SPOP key [count]`\n\n**Description:** Returns one or more random members from a set after removing them. Deletes the set if the last member was popped.\n\n**Complexity:** Without the count argument O(1), otherwise O(N) where N is the value of the passed count.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/srandmember/\">SRANDMEMBER</a></strong> - Get one or multiple random members from a set</summary>\n\n**Syntax:** `SRANDMEMBER key [count]`\n\n**Description:** Get one or multiple random members from a set\n\n**Complexity:** Without the count argument O(1), otherwise O(N) where N is the absolute value of the passed count.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/srem/\">SREM</a></strong> - Removes one or more members from a set. Deletes the set if the last member was removed.</summary>\n\n**Syntax:** `SREM key member [member ...]`\n\n**Description:** Removes one or more members from a set. Deletes the set if the last member was removed.\n\n**Complexity:** O(N) where N is the number of members to be removed.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sscan/\">SSCAN</a></strong> - Iterates over members of a set.</summary>\n\n**Syntax:** `SSCAN key cursor [MATCH pattern] [COUNT count]`\n\n**Description:** Iterates over members of a set.\n\n**Complexity:** O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sunion/\">SUNION</a></strong> - Returns the union of multiple sets.</summary>\n\n**Syntax:** `SUNION key [key ...]`\n\n**Description:** Returns the union of multiple sets.\n\n**Complexity:** O(N) where N is the total number of elements in all given sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sunionstore/\">SUNIONSTORE</a></strong> - Stores the union of multiple sets in a key.</summary>\n\n**Syntax:** `SUNIONSTORE destination key [key ...]`\n\n**Description:** Stores the union of multiple sets in a key.\n\n**Complexity:** O(N) where N is the total number of elements in all given sets.\n\n**Since:** 1.0.0\n\n</details>"},{"id":"sorted-set-commands","title":"Sorted set commands","role":"content","text":"Sorted set commands operate on sets of unique strings ordered by a score.\n\n<details>\n<summary><strong><a href=\"/commands/bzpopmax/\">BZPOPMAX</a></strong> - Removes and returns the member with the highest score from one or more sorted sets. Blocks until a member available otherwise.  Deletes the sorted set if the last element was popped.</summary>\n\n**Syntax:** `BZPOPMAX key [key ...] timeout`\n\n**Description:** Removes and returns the member with the highest score from one or more sorted sets. Blocks until a member available otherwise.  Deletes the sorted set if the last element was popped.\n\n**Complexity:** O(log(N)) with N being the number of elements in the sorted set.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bzpopmin/\">BZPOPMIN</a></strong> - Removes and returns the member with the lowest score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.</summary>\n\n**Syntax:** `BZPOPMIN key [key ...] timeout`\n\n**Description:** Removes and returns the member with the lowest score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.\n\n**Complexity:** O(log(N)) with N being the number of elements in the sorted set.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zadd/\">ZADD</a></strong> - Adds one or more members to a sorted set, or updates their scores. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `ZADD key [NX | XX] [GT | LT] [CH] [INCR] score member [score member\n  ...]`\n\n**Description:** Adds one or more members to a sorted set, or updates their scores. Creates the key if it doesn't exist.\n\n**Complexity:** O(log(N)) for each item added, where N is the number of elements in the sorted set.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zcard/\">ZCARD</a></strong> - Returns the number of members in a sorted set.</summary>\n\n**Syntax:** `ZCARD key`\n\n**Description:** Returns the number of members in a sorted set.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zcount/\">ZCOUNT</a></strong> - Returns the count of members in a sorted set that have scores within a range.</summary>\n\n**Syntax:** `ZCOUNT key min max`\n\n**Description:** Returns the count of members in a sorted set that have scores within a range.\n\n**Complexity:** O(log(N)) with N being the number of elements in the sorted set.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zdiff/\">ZDIFF</a></strong> - Returns the difference between multiple sorted sets. <span style=\"color: #e74c3c;\">⭐ New in 6.2</span></summary>\n\n**Syntax:** `ZDIFF numkeys key [key ...] [WITHSCORES]`\n\n**Description:** Returns the difference between multiple sorted sets.\n\n**Complexity:** O(L + (N-K)log(N)) worst case where L is the total number of elements in all the sets, N is the size of the first set, and K is the size of the result set.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zdiffstore/\">ZDIFFSTORE</a></strong> - Stores the difference of multiple sorted sets in a key. <span style=\"color: #e74c3c;\">⭐ New in 6.2</span></summary>\n\n**Syntax:** `ZDIFFSTORE destination numkeys key [key ...]`\n\n**Description:** Stores the difference of multiple sorted sets in a key.\n\n**Complexity:** O(L + (N-K)log(N)) worst case where L is the total number of elements in all the sets, N is the size of the first set, and K is the size of the result set.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zincrby/\">ZINCRBY</a></strong> - Increments the score of a member in a sorted set.</summary>\n\n**Syntax:** `ZINCRBY key increment member`\n\n**Description:** Increments the score of a member in a sorted set.\n\n**Complexity:** O(log(N)) where N is the number of elements in the sorted set.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zinter/\">ZINTER</a></strong> - Returns the intersect of multiple sorted sets. <span style=\"color: #e74c3c;\">⭐ New in 6.2</span></summary>\n\n**Syntax:** `ZINTER numkeys key [key ...] [WEIGHTS weight [weight ...]]\n  [AGGREGATE <SUM | MIN | MAX>] [WITHSCORES]`\n\n**Description:** Returns the intersect of multiple sorted sets.\n\n**Complexity:** O(N*K)+O(M*log(M)) worst case with N being the smallest input sorted set, K being the number of input sorted sets and M being the number of elements in the resulting sorted set.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zinterstore/\">ZINTERSTORE</a></strong> - Stores the intersect of multiple sorted sets in a key.</summary>\n\n**Syntax:** `ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight\n  [weight ...]] [AGGREGATE <SUM | MIN | MAX>]`\n\n**Description:** Stores the intersect of multiple sorted sets in a key.\n\n**Complexity:** O(N*K)+O(M*log(M)) worst case with N being the smallest input sorted set, K being the number of input sorted sets and M being the number of elements in the resulting sorted set.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zlexcount/\">ZLEXCOUNT</a></strong> - Returns the number of members in a sorted set within a lexicographical range.</summary>\n\n**Syntax:** `ZLEXCOUNT key min max`\n\n**Description:** Returns the number of members in a sorted set within a lexicographical range.\n\n**Complexity:** O(log(N)) with N being the number of elements in the sorted set.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zmscore/\">ZMSCORE</a></strong> - Returns the score of one or more members in a sorted set. <span style=\"color: #e74c3c;\">⭐ New in 6.2</span></summary>\n\n**Syntax:** `ZMSCORE key member [member ...]`\n\n**Description:** Returns the score of one or more members in a sorted set.\n\n**Complexity:** O(N) where N is the number of members being requested.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zpopmax/\">ZPOPMAX</a></strong> - Returns the highest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.</summary>\n\n**Syntax:** `ZPOPMAX key [count]`\n\n**Description:** Returns the highest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.\n\n**Complexity:** O(log(N)*M) with N being the number of elements in the sorted set, and M being the number of elements popped.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zpopmin/\">ZPOPMIN</a></strong> - Returns the lowest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.</summary>\n\n**Syntax:** `ZPOPMIN key [count]`\n\n**Description:** Returns the lowest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.\n\n**Complexity:** O(log(N)*M) with N being the number of elements in the sorted set, and M being the number of elements popped.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrandmember/\">ZRANDMEMBER</a></strong> - Returns one or more random members from a sorted set. <span style=\"color: #e74c3c;\">⭐ New in 6.2</span></summary>\n\n**Syntax:** `ZRANDMEMBER key [count [WITHSCORES]]`\n\n**Description:** Returns one or more random members from a sorted set.\n\n**Complexity:** O(N) where N is the number of members returned\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrange/\">ZRANGE</a></strong> - Returns members in a sorted set within a range of indexes.</summary>\n\n**Syntax:** `ZRANGE key start stop [BYSCORE | BYLEX] [REV] [LIMIT offset count]\n  [WITHSCORES]`\n\n**Description:** Returns members in a sorted set within a range of indexes.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements returned.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrangebylex/\">ZRANGEBYLEX</a></strong> - Returns members in a sorted set within a lexicographical range.</summary>\n\n**Syntax:** `ZRANGEBYLEX key min max [LIMIT offset count]`\n\n**Description:** Returns members in a sorted set within a lexicographical range.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrangebyscore/\">ZRANGEBYSCORE</a></strong> - Returns members in a sorted set within a range of scores.</summary>\n\n**Syntax:** `ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]`\n\n**Description:** Returns members in a sorted set within a range of scores.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).\n\n**Since:** 1.0.5\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrangestore/\">ZRANGESTORE</a></strong> - Stores a range of members from sorted set in a key. <span style=\"color: #e74c3c;\">⭐ New in 6.2</span></summary>\n\n**Syntax:** `ZRANGESTORE dst src min max [BYSCORE | BYLEX] [REV] [LIMIT offset\n  count]`\n\n**Description:** Stores a range of members from sorted set in a key.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements stored into the destination key.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrank/\">ZRANK</a></strong> - Returns the index of a member in a sorted set ordered by ascending scores.</summary>\n\n**Syntax:** `ZRANK key member [WITHSCORE]`\n\n**Description:** Returns the index of a member in a sorted set ordered by ascending scores.\n\n**Complexity:** O(log(N))\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrem/\">ZREM</a></strong> - Removes one or more members from a sorted set. Deletes the sorted set if all members were removed.</summary>\n\n**Syntax:** `ZREM key member [member ...]`\n\n**Description:** Removes one or more members from a sorted set. Deletes the sorted set if all members were removed.\n\n**Complexity:** O(M*log(N)) with N being the number of elements in the sorted set and M the number of elements to be removed.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zremrangebylex/\">ZREMRANGEBYLEX</a></strong> - Removes members in a sorted set within a lexicographical range. Deletes the sorted set if all members were removed.</summary>\n\n**Syntax:** `ZREMRANGEBYLEX key min max`\n\n**Description:** Removes members in a sorted set within a lexicographical range. Deletes the sorted set if all members were removed.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zremrangebyrank/\">ZREMRANGEBYRANK</a></strong> - Removes members in a sorted set within a range of indexes. Deletes the sorted set if all members were removed.</summary>\n\n**Syntax:** `ZREMRANGEBYRANK key start stop`\n\n**Description:** Removes members in a sorted set within a range of indexes. Deletes the sorted set if all members were removed.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zremrangebyscore/\">ZREMRANGEBYSCORE</a></strong> - Removes members in a sorted set within a range of scores. Deletes the sorted set if all members were removed.</summary>\n\n**Syntax:** `ZREMRANGEBYSCORE key min max`\n\n**Description:** Removes members in a sorted set within a range of scores. Deletes the sorted set if all members were removed.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrevrange/\">ZREVRANGE</a></strong> - Returns members in a sorted set within a range of indexes in reverse order.</summary>\n\n**Syntax:** `ZREVRANGE key start stop [WITHSCORES]`\n\n**Description:** Returns members in a sorted set within a range of indexes in reverse order.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements returned.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrevrangebylex/\">ZREVRANGEBYLEX</a></strong> - Returns members in a sorted set within a lexicographical range in reverse order.</summary>\n\n**Syntax:** `ZREVRANGEBYLEX key max min [LIMIT offset count]`\n\n**Description:** Returns members in a sorted set within a lexicographical range in reverse order.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrevrangebyscore/\">ZREVRANGEBYSCORE</a></strong> - Returns members in a sorted set within a range of scores in reverse order.</summary>\n\n**Syntax:** `ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]`\n\n**Description:** Returns members in a sorted set within a range of scores in reverse order.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrevrank/\">ZREVRANK</a></strong> - Returns the index of a member in a sorted set ordered by descending scores.</summary>\n\n**Syntax:** `ZREVRANK key member [WITHSCORE]`\n\n**Description:** Returns the index of a member in a sorted set ordered by descending scores.\n\n**Complexity:** O(log(N))\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zscan/\">ZSCAN</a></strong> - Iterates over members and scores of a sorted set.</summary>\n\n**Syntax:** `ZSCAN key cursor [MATCH pattern] [COUNT count]`\n\n**Description:** Iterates over members and scores of a sorted set.\n\n**Complexity:** O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zscore/\">ZSCORE</a></strong> - Returns the score of a member in a sorted set.</summary>\n\n**Syntax:** `ZSCORE key member`\n\n**Description:** Returns the score of a member in a sorted set.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zunion/\">ZUNION</a></strong> - Returns the union of multiple sorted sets. <span style=\"color: #e74c3c;\">⭐ New in 6.2</span></summary>\n\n**Syntax:** `ZUNION numkeys key [key ...] [WEIGHTS weight [weight ...]]\n  [AGGREGATE <SUM | MIN | MAX>] [WITHSCORES]`\n\n**Description:** Returns the union of multiple sorted sets.\n\n**Complexity:** O(N)+O(M*log(M)) with N being the sum of the sizes of the input sorted sets, and M being the number of elements in the resulting sorted set.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zunionstore/\">ZUNIONSTORE</a></strong> - Stores the union of multiple sorted sets in a key.</summary>\n\n**Syntax:** `ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight\n  [weight ...]] [AGGREGATE <SUM | MIN | MAX>]`\n\n**Description:** Stores the union of multiple sorted sets in a key.\n\n**Complexity:** O(N)+O(M log(M)) with N being the sum of the sizes of the input sorted sets, and M being the number of elements in the resulting sorted set.\n\n**Since:** 2.0.0\n\n</details>"},{"id":"stream-commands","title":"Stream commands","role":"content","text":"Stream commands operate on append-only log data structures.\n\n<details>\n<summary><strong><a href=\"/commands/xack/\">XACK</a></strong> - Returns the number of messages that were successfully acknowledged by the consumer group member of a stream.</summary>\n\n**Syntax:** `XACK key group id [id ...]`\n\n**Description:** Returns the number of messages that were successfully acknowledged by the consumer group member of a stream.\n\n**Complexity:** O(1) for each message ID processed.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xadd/\">XADD</a></strong> - Appends a new message to a stream. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `XADD key [NOMKSTREAM] [KEEPREF | DELREF | ACKED] [<MAXLEN | MINID>\n  [= | ~] threshold [LIMIT count]] <* | id> field value [field value\n  ...]`\n\n**Description:** Appends a new message to a stream. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) when adding a new entry, O(N) when trimming where N being the number of entries evicted.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xautoclaim/\">XAUTOCLAIM</a></strong> - Changes, or acquires, ownership of messages in a consumer group, as if the messages were delivered to as consumer group member. <span style=\"color: #e74c3c;\">⭐ New in 6.2</span></summary>\n\n**Syntax:** `XAUTOCLAIM key group consumer min-idle-time start [COUNT count]\n  [JUSTID]`\n\n**Description:** Changes, or acquires, ownership of messages in a consumer group, as if the messages were delivered to as consumer group member.\n\n**Complexity:** O(1) if COUNT is small.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xclaim/\">XCLAIM</a></strong> - Changes, or acquires, ownership of a message in a consumer group, as if the message was delivered a consumer group member.</summary>\n\n**Syntax:** `XCLAIM key group consumer min-idle-time id [id ...] [IDLE ms]\n  [TIME unix-time-milliseconds] [RETRYCOUNT count] [FORCE] [JUSTID]\n  [LASTID lastid]`\n\n**Description:** Changes, or acquires, ownership of a message in a consumer group, as if the message was delivered a consumer group member.\n\n**Complexity:** O(log N) with N being the number of messages in the PEL of the consumer group.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xdel/\">XDEL</a></strong> - Returns the number of messages after removing them from a stream.</summary>\n\n**Syntax:** `XDEL key id [id ...]`\n\n**Description:** Returns the number of messages after removing them from a stream.\n\n**Complexity:** O(1) for each single item to delete in the stream, regardless of the stream size.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-create/\">XGROUP CREATE</a></strong> - Creates a consumer group.</summary>\n\n**Syntax:** `XGROUP CREATE key group <id | $> [MKSTREAM]\n  [ENTRIESREAD entries-read]`\n\n**Description:** Creates a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-createconsumer/\">XGROUP CREATECONSUMER</a></strong> - Creates a consumer in a consumer group. <span style=\"color: #e74c3c;\">⭐ New in 6.2</span></summary>\n\n**Syntax:** `XGROUP CREATECONSUMER key group consumer`\n\n**Description:** Creates a consumer in a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-delconsumer/\">XGROUP DELCONSUMER</a></strong> - Deletes a consumer from a consumer group.</summary>\n\n**Syntax:** `XGROUP DELCONSUMER key group consumer`\n\n**Description:** Deletes a consumer from a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-destroy/\">XGROUP DESTROY</a></strong> - Destroys a consumer group.</summary>\n\n**Syntax:** `XGROUP DESTROY key group`\n\n**Description:** Destroys a consumer group.\n\n**Complexity:** O(N) where N is the number of entries in the group's pending entries list (PEL).\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-setid/\">XGROUP SETID</a></strong> - Sets the last-delivered ID of a consumer group.</summary>\n\n**Syntax:** `XGROUP SETID key group <id | $> [ENTRIESREAD entries-read]`\n\n**Description:** Sets the last-delivered ID of a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xinfo-consumers/\">XINFO CONSUMERS</a></strong> - Returns a list of the consumers in a consumer group.</summary>\n\n**Syntax:** `XINFO CONSUMERS key group`\n\n**Description:** Returns a list of the consumers in a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xinfo-groups/\">XINFO GROUPS</a></strong> - Returns a list of the consumer groups of a stream.</summary>\n\n**Syntax:** `XINFO GROUPS key`\n\n**Description:** Returns a list of the consumer groups of a stream.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xinfo-stream/\">XINFO STREAM</a></strong> - Returns information about a stream.</summary>\n\n**Syntax:** `XINFO STREAM key [FULL [COUNT count]]`\n\n**Description:** Returns information about a stream.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xlen/\">XLEN</a></strong> - Return the number of messages in a stream.</summary>\n\n**Syntax:** `XLEN key`\n\n**Description:** Return the number of messages in a stream.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xpending/\">XPENDING</a></strong> - Returns the information and entries from a stream consumer group's pending entries list.</summary>\n\n**Syntax:** `XPENDING key group [[IDLE min-idle-time] start end count [consumer]]`\n\n**Description:** Returns the information and entries from a stream consumer group's pending entries list.\n\n**Complexity:** O(N) with N being the number of elements returned, so asking for a small fixed number of entries per call is O(1). O(M), where M is the total number of entries scanned when used with the IDLE filter. When the command returns just the summary and the list of consumers is small, it runs in O(1) time; otherwise, an additional O(N) time for iterating every consumer.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xrange/\">XRANGE</a></strong> - Returns the messages from a stream within a range of IDs.</summary>\n\n**Syntax:** `XRANGE key start end [COUNT count]`\n\n**Description:** Returns the messages from a stream within a range of IDs.\n\n**Complexity:** O(N) with N being the number of elements being returned. If N is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1).\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xread/\">XREAD</a></strong> - Returns messages from multiple streams with IDs greater than the ones requested. Blocks until a message is available otherwise.</summary>\n\n**Syntax:** `XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] id\n  [id ...]`\n\n**Description:** Returns messages from multiple streams with IDs greater than the ones requested. Blocks until a message is available otherwise.\n\n**Complexity:** \n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xreadgroup/\">XREADGROUP</a></strong> - Returns new or historical messages from a stream for a consumer in a group. Blocks until a message is available otherwise.</summary>\n\n**Syntax:** `XREADGROUP GROUP group consumer [COUNT count] [BLOCK milliseconds]\n  [CLAIM min-idle-time] [NOACK] STREAMS key [key ...] id [id ...]`\n\n**Description:** Returns new or historical messages from a stream for a consumer in a group. Blocks until a message is available otherwise.\n\n**Complexity:** For each stream mentioned: O(M) with M being the number of elements returned. If M is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1). On the other side when XREADGROUP blocks, XADD will pay the O(N) time in order to serve the N clients blocked on the stream getting new data.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xrevrange/\">XREVRANGE</a></strong> - Returns the messages from a stream within a range of IDs in reverse order.</summary>\n\n**Syntax:** `XREVRANGE key end start [COUNT count]`\n\n**Description:** Returns the messages from a stream within a range of IDs in reverse order.\n\n**Complexity:** O(N) with N being the number of elements returned. If N is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1).\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xsetid/\">XSETID</a></strong> - An internal command for replicating stream values.</summary>\n\n**Syntax:** `XSETID key last-id [ENTRIESADDED entries-added]\n  [MAXDELETEDID max-deleted-id]`\n\n**Description:** An internal command for replicating stream values.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xtrim/\">XTRIM</a></strong> - Deletes messages from the beginning of a stream.</summary>\n\n**Syntax:** `XTRIM key <MAXLEN | MINID> [= | ~] threshold [LIMIT count] [KEEPREF\n  | DELREF | ACKED]`\n\n**Description:** Deletes messages from the beginning of a stream.\n\n**Complexity:** O(N), with N being the number of evicted entries. Constant times are very small however, since entries are organized in macro nodes containing multiple entries that can be released with a single deallocation.\n\n**Since:** 5.0.0\n\n</details>"},{"id":"bitmap-commands","title":"Bitmap commands","role":"content","text":"Bitmap commands operate on strings as arrays of bits.\n\n<details>\n<summary><strong><a href=\"/commands/bitcount/\">BITCOUNT</a></strong> - Counts the number of set bits (population counting) in a string.</summary>\n\n**Syntax:** `BITCOUNT key [start end [BYTE | BIT]]`\n\n**Description:** Counts the number of set bits (population counting) in a string.\n\n**Complexity:** O(N)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bitfield/\">BITFIELD</a></strong> - Performs arbitrary bitfield integer operations on strings.</summary>\n\n**Syntax:** `BITFIELD key [GET encoding offset | [OVERFLOW <WRAP | SAT | FAIL>]\n  <SET encoding offset value | INCRBY encoding offset increment>\n  [GET encoding offset | [OVERFLOW <WRAP | SAT | FAIL>]\n  <SET encoding offset value | INCRBY encoding offset increment>\n  ...]]`\n\n**Description:** Performs arbitrary bitfield integer operations on strings.\n\n**Complexity:** O(1) for each subcommand specified\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bitfield_ro/\">BITFIELD_RO</a></strong> - Performs arbitrary read-only bitfield integer operations on strings.</summary>\n\n**Syntax:** `BITFIELD RO key [GET encoding offset [GET encoding offset ...]]`\n\n**Description:** Performs arbitrary read-only bitfield integer operations on strings.\n\n**Complexity:** O(1) for each subcommand specified\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bitop/\">BITOP</a></strong> - Performs bitwise operations on multiple strings, and stores the result.</summary>\n\n**Syntax:** `BITOP <AND | OR | XOR | NOT | DIFF | DIFF1 | ANDOR | ONE> destkey key [key ...]`\n\n**Description:** Performs bitwise operations on multiple strings, and stores the result.\n\n**Complexity:** O(N)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bitpos/\">BITPOS</a></strong> - Finds the first set (1) or clear (0) bit in a string.</summary>\n\n**Syntax:** `BITPOS key bit [start [end [BYTE | BIT]]]`\n\n**Description:** Finds the first set (1) or clear (0) bit in a string.\n\n**Complexity:** O(N)\n\n**Since:** 2.8.7\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getbit/\">GETBIT</a></strong> - Returns a bit value by offset.</summary>\n\n**Syntax:** `GETBIT key offset`\n\n**Description:** Returns a bit value by offset.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/setbit/\">SETBIT</a></strong> - Sets or clears the bit at offset of the string value. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `SETBIT key offset value`\n\n**Description:** Sets or clears the bit at offset of the string value. Creates the key if it doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>"},{"id":"hyperloglog-commands","title":"HyperLogLog commands","role":"content","text":"HyperLogLog commands provide probabilistic cardinality estimation.\n\n<details>\n<summary><strong><a href=\"/commands/pfadd/\">PFADD</a></strong> - Adds elements to a HyperLogLog key. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `PFADD key [element [element ...]]`\n\n**Description:** Adds elements to a HyperLogLog key. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) to add every element.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pfcount/\">PFCOUNT</a></strong> - Returns the approximated cardinality of the set(s) observed by the HyperLogLog key(s).</summary>\n\n**Syntax:** `PFCOUNT key [key ...]`\n\n**Description:** Returns the approximated cardinality of the set(s) observed by the HyperLogLog key(s).\n\n**Complexity:** O(1) with a very small average constant time when called with a single key. O(N) with N being the number of keys, and much bigger constant times, when called with multiple keys.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pfdebug/\">PFDEBUG</a></strong> - Internal commands for debugging HyperLogLog values.</summary>\n\n**Syntax:** `PFDEBUG subcommand key`\n\n**Description:** Internal commands for debugging HyperLogLog values.\n\n**Complexity:** N/A\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pfmerge/\">PFMERGE</a></strong> - Merges one or more HyperLogLog values into a single key.</summary>\n\n**Syntax:** `PFMERGE destkey [sourcekey [sourcekey ...]]`\n\n**Description:** Merges one or more HyperLogLog values into a single key.\n\n**Complexity:** O(N) to merge N HyperLogLogs, but with high constant times.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pfselftest/\">PFSELFTEST</a></strong> - An internal command for testing HyperLogLog values.</summary>\n\n**Syntax:** `PFSELFTEST`\n\n**Description:** An internal command for testing HyperLogLog values.\n\n**Complexity:** N/A\n\n**Since:** 2.8.9\n\n</details>"},{"id":"geospatial-commands","title":"Geospatial commands","role":"content","text":"Geospatial commands operate on geographic coordinates.\n\n<details>\n<summary><strong><a href=\"/commands/geoadd/\">GEOADD</a></strong> - Adds one or more members to a geospatial index. The key is created if it doesn't exist.</summary>\n\n**Syntax:** `GEOADD key [NX | XX] [CH] longitude latitude member [longitude\n  latitude member ...]`\n\n**Description:** Adds one or more members to a geospatial index. The key is created if it doesn't exist.\n\n**Complexity:** O(log(N)) for each item added, where N is the number of elements in the sorted set.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geodist/\">GEODIST</a></strong> - Returns the distance between two members of a geospatial index.</summary>\n\n**Syntax:** `GEODIST key member1 member2 [M | KM | FT | MI]`\n\n**Description:** Returns the distance between two members of a geospatial index.\n\n**Complexity:** O(1)\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geohash/\">GEOHASH</a></strong> - Returns members from a geospatial index as geohash strings.</summary>\n\n**Syntax:** `GEOHASH key [member [member ...]]`\n\n**Description:** Returns members from a geospatial index as geohash strings.\n\n**Complexity:** O(1) for each member requested.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geopos/\">GEOPOS</a></strong> - Returns the longitude and latitude of members from a geospatial index.</summary>\n\n**Syntax:** `GEOPOS key [member [member ...]]`\n\n**Description:** Returns the longitude and latitude of members from a geospatial index.\n\n**Complexity:** O(1) for each member requested.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/georadius/\">GEORADIUS</a></strong> - Queries a geospatial index for members within a distance from a coordinate, optionally stores the result.</summary>\n\n**Syntax:** `GEORADIUS key longitude latitude radius <M | KM | FT | MI>\n  [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC]\n  [STORE key | STOREDIST key]`\n\n**Description:** Queries a geospatial index for members within a distance from a coordinate, optionally stores the result.\n\n**Complexity:** O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/georadiusbymember/\">GEORADIUSBYMEMBER</a></strong> - Queries a geospatial index for members within a distance from a member, optionally stores the result.</summary>\n\n**Syntax:** `GEORADIUSBYMEMBER key member radius <M | KM | FT | MI> [WITHCOORD]\n  [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC] [STORE key\n  | STOREDIST key]`\n\n**Description:** Queries a geospatial index for members within a distance from a member, optionally stores the result.\n\n**Complexity:** O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/georadiusbymember_ro/\">GEORADIUSBYMEMBER_RO</a></strong> - Returns members from a geospatial index that are within a distance from a member.</summary>\n\n**Syntax:** `GEORADIUSBYMEMBER RO key member radius <M | KM | FT | MI>\n  [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC]`\n\n**Description:** Returns members from a geospatial index that are within a distance from a member.\n\n**Complexity:** O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.\n\n**Since:** 3.2.10\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/georadius_ro/\">GEORADIUS_RO</a></strong> - Returns members from a geospatial index that are within a distance from a coordinate.</summary>\n\n**Syntax:** `GEORADIUS RO key longitude latitude radius <M | KM | FT | MI>\n  [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC]`\n\n**Description:** Returns members from a geospatial index that are within a distance from a coordinate.\n\n**Complexity:** O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.\n\n**Since:** 3.2.10\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geosearch/\">GEOSEARCH</a></strong> - Queries a geospatial index for members inside an area of a box or a circle. <span style=\"color: #e74c3c;\">⭐ New in 6.2</span></summary>\n\n**Syntax:** `GEOSEARCH key <FROMMEMBER member | FROMLONLAT longitude latitude>\n  <BYRADIUS radius <M | KM | FT | MI> | BYBOX width height <M | KM |\n  FT | MI>> [ASC | DESC] [COUNT count [ANY]] [WITHCOORD] [WITHDIST]\n  [WITHHASH]`\n\n**Description:** Queries a geospatial index for members inside an area of a box or a circle.\n\n**Complexity:** O(N+log(M)) where N is the number of elements in the grid-aligned bounding box area around the shape provided as the filter and M is the number of items inside the shape\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geosearchstore/\">GEOSEARCHSTORE</a></strong> - Queries a geospatial index for members inside an area of a box or a circle, optionally stores the result. <span style=\"color: #e74c3c;\">⭐ New in 6.2</span></summary>\n\n**Syntax:** `GEOSEARCHSTORE destination source <FROMMEMBER member |\n  FROMLONLAT longitude latitude> <BYRADIUS radius <M | KM | FT | MI>\n  | BYBOX width height <M | KM | FT | MI>> [ASC | DESC] [COUNT count\n  [ANY]] [STOREDIST]`\n\n**Description:** Queries a geospatial index for members inside an area of a box or a circle, optionally stores the result.\n\n**Complexity:** O(N+log(M)) where N is the number of elements in the grid-aligned bounding box area around the shape provided as the filter and M is the number of items inside the shape\n\n**Since:** 6.2.0\n\n</details>"},{"id":"json-commands","title":"JSON commands","role":"content","text":"JSON commands operate on JSON data structures.\n\n<details>\n<summary><strong><a href=\"/commands/json.arrappend/\">JSON.ARRAPPEND</a></strong> - Append one or more JSON values into the array at path after the last element in it.</summary>\n\n**Syntax:** `JSON.ARRAPPEND key path value [value ...]`\n\n**Description:** Append one or more JSON values into the array at path after the last element in it.\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrindex/\">JSON.ARRINDEX</a></strong> - Returns the index of the first occurrence of a JSON scalar value in the array at path</summary>\n\n**Syntax:** `JSON.ARRINDEX key path value [start [stop]]`\n\n**Description:** Returns the index of the first occurrence of a JSON scalar value in the array at path\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the array, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrinsert/\">JSON.ARRINSERT</a></strong> - Inserts the JSON scalar(s) value at the specified index in the array at path</summary>\n\n**Syntax:** `JSON.ARRINSERT key path index value [value ...]`\n\n**Description:** Inserts the JSON scalar(s) value at the specified index in the array at path\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the array, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrlen/\">JSON.ARRLEN</a></strong> - Returns the length of the array at path</summary>\n\n**Syntax:** `JSON.ARRLEN key [path]`\n\n**Description:** Returns the length of the array at path\n\n**Complexity:** O(1) where path is evaluated to a single value, O(N) where path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrpop/\">JSON.ARRPOP</a></strong> - Removes and returns the element at the specified index in the array at path</summary>\n\n**Syntax:** `JSON.ARRPOP key [path [index]]`\n\n**Description:** Removes and returns the element at the specified index in the array at path\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the array and the specified index is not the last element, O(1) when path is evaluated to a single value and the specified index is the last element, or O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrtrim/\">JSON.ARRTRIM</a></strong> - Trims the array at path to contain only the specified inclusive range of indices from start to stop</summary>\n\n**Syntax:** `JSON.ARRTRIM key path start stop`\n\n**Description:** Trims the array at path to contain only the specified inclusive range of indices from start to stop\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the array, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.clear/\">JSON.CLEAR</a></strong> - Clears all values from an array or an object and sets numeric values to `0`</summary>\n\n**Syntax:** `JSON.CLEAR key [path]`\n\n**Description:** Clears all values from an array or an object and sets numeric values to `0`\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the values, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.debug/\">JSON.DEBUG</a></strong> - Debugging container command</summary>\n\n**Syntax:** `JSON.DEBUG`\n\n**Description:** Debugging container command\n\n**Complexity:** N/A\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.debug-memory/\">JSON.DEBUG MEMORY</a></strong> - Reports the size in bytes of a key</summary>\n\n**Syntax:** `JSON.DEBUG MEMORY key [path]`\n\n**Description:** Reports the size in bytes of a key\n\n**Complexity:** O(N) when path is evaluated to a single value, where N is the size of the value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.del/\">JSON.DEL</a></strong> - Deletes a value</summary>\n\n**Syntax:** `JSON.DEL key [path]`\n\n**Description:** Deletes a value\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the deleted value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.forget/\">JSON.FORGET</a></strong> - Deletes a value</summary>\n\n**Syntax:** `JSON.FORGET key [path]`\n\n**Description:** Deletes a value\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the deleted value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.get/\">JSON.GET</a></strong> - Gets the value at one or more paths in JSON serialized form</summary>\n\n**Syntax:** `JSON.GET key [INDENT indent] [NEWLINE newline] [SPACE space] [path\n  [path ...]]`\n\n**Description:** Gets the value at one or more paths in JSON serialized form\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.merge/\">JSON.MERGE</a></strong> - Merges a given JSON value into matching paths. Consequently, JSON values at matching paths are updated, deleted, or expanded with new children</summary>\n\n**Syntax:** `JSON.MERGE key path value`\n\n**Description:** Merges a given JSON value into matching paths. Consequently, JSON values at matching paths are updated, deleted, or expanded with new children\n\n**Complexity:** O(M+N) when path is evaluated to a single value where M is the size of the original value (if it exists) and N is the size of the new value, O(M+N) when path is evaluated to multiple values where M is the size of the key and N is the size of the new value * the number of original values in the key\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.mget/\">JSON.MGET</a></strong> - Returns the values at a path from one or more keys</summary>\n\n**Syntax:** `JSON.MGET key [key ...] path`\n\n**Description:** Returns the values at a path from one or more keys\n\n**Complexity:** O(M*N) when path is evaluated to a single value where M is the number of keys and N is the size of the value, O(N1+N2+...+Nm) when path is evaluated to multiple values where m is the number of keys and Ni is the size of the i-th key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.mset/\">JSON.MSET</a></strong> - Sets or updates the JSON value of one or more keys</summary>\n\n**Syntax:** `JSON.MSET key path value [key path value ...]`\n\n**Description:** Sets or updates the JSON value of one or more keys\n\n**Complexity:** O(K*(M+N)) where k is the number of keys in the command, when path is evaluated to a single value where M is the size of the original value (if it exists) and N is the size of the new value, or O(K*(M+N)) when path is evaluated to multiple values where M is the size of the key and N is the size of the new value * the number of original values in the key\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.numincrby/\">JSON.NUMINCRBY</a></strong> - Increments the numeric value at path by a value</summary>\n\n**Syntax:** `JSON.NUMINCRBY key path value`\n\n**Description:** Increments the numeric value at path by a value\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.nummultby/\">JSON.NUMMULTBY</a></strong> - Multiplies the numeric value at path by a value</summary>\n\n**Syntax:** `JSON.NUMMULTBY key path value`\n\n**Description:** Multiplies the numeric value at path by a value\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.objkeys/\">JSON.OBJKEYS</a></strong> - Returns the JSON keys of the object at path</summary>\n\n**Syntax:** `JSON.OBJKEYS key [path]`\n\n**Description:** Returns the JSON keys of the object at path\n\n**Complexity:** O(N) when path is evaluated to a single value, where N is the number of keys in the object, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.objlen/\">JSON.OBJLEN</a></strong> - Returns the number of keys of the object at path</summary>\n\n**Syntax:** `JSON.OBJLEN key [path]`\n\n**Description:** Returns the number of keys of the object at path\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.resp/\">JSON.RESP</a></strong> - Returns the JSON value at path in Redis Serialization Protocol (RESP)</summary>\n\n**Syntax:** `JSON.RESP key [path]`\n\n**Description:** Returns the JSON value at path in Redis Serialization Protocol (RESP)\n\n**Complexity:** O(N) when path is evaluated to a single value, where N is the size of the value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.set/\">JSON.SET</a></strong> - Sets or updates the JSON value at a path</summary>\n\n**Syntax:** `JSON.SET key path value [NX | XX]`\n\n**Description:** Sets or updates the JSON value at a path\n\n**Complexity:** O(M+N) when path is evaluated to a single value where M is the size of the original value (if it exists) and N is the size of the new value, O(M+N) when path is evaluated to multiple values where M is the size of the key and N is the size of the new value * the number of original values in the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.strappend/\">JSON.STRAPPEND</a></strong> - Appends a string to a JSON string value at path</summary>\n\n**Syntax:** `JSON.STRAPPEND key [path] value`\n\n**Description:** Appends a string to a JSON string value at path\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.strlen/\">JSON.STRLEN</a></strong> - Returns the length of the JSON String at path in key</summary>\n\n**Syntax:** `JSON.STRLEN key [path]`\n\n**Description:** Returns the length of the JSON String at path in key\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.toggle/\">JSON.TOGGLE</a></strong> - Toggles a boolean value</summary>\n\n**Syntax:** `JSON.TOGGLE key path`\n\n**Description:** Toggles a boolean value\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.type/\">JSON.TYPE</a></strong> - Returns the type of the JSON value at path</summary>\n\n**Syntax:** `JSON.TYPE key [path]`\n\n**Description:** Returns the type of the JSON value at path\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>"},{"id":"search-commands","title":"Search commands","role":"content","text":"Search commands provide full-text search and secondary indexing.\n\n<details>\n<summary><strong><a href=\"/commands/ft.aggregate/\">FT.AGGREGATE</a></strong> - Run a search query on an index and perform aggregate transformations on the results</summary>\n\n**Syntax:** `FT.AGGREGATE index query [VERBATIM] [LOAD count field [field ...]]\n  [TIMEOUT timeout] [LOAD *] [GROUPBY nargs property [property ...]\n  [REDUCE function nargs arg [arg ...] [AS name] [REDUCE function\n  nargs arg [arg ...] [AS name] ...]] [GROUPBY nargs property\n  [property ...] [REDUCE function nargs arg [arg ...] [AS name]\n  [REDUCE function nargs arg [arg ...] [AS name] ...]] ...]]\n  [SORTBY nargs [property <ASC | DESC> [property <ASC | DESC> ...]]\n  [MAX num]] [APPLY expression AS name [APPLY expression AS name\n  ...]] [LIMIT offset num] [FILTER filter] [WITHCURSOR\n  [COUNT read size] [MAXIDLE idle time]] [PARAMS nargs name value\n  [name value ...]]\n  [SCORER scorer]\n [ADDSCORES]\n  [DIALECT dialect]`\n\n**Description:** Run a search query on an index and perform aggregate transformations on the results\n\n**Complexity:** O(1)\n\n**Since:** 1.1.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.aliasadd/\">FT.ALIASADD</a></strong> - Adds an alias to the index</summary>\n\n**Syntax:** `FT.ALIASADD alias index`\n\n**Description:** Adds an alias to the index\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.aliasdel/\">FT.ALIASDEL</a></strong> - Deletes an alias from the index</summary>\n\n**Syntax:** `FT.ALIASDEL alias`\n\n**Description:** Deletes an alias from the index\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.aliasupdate/\">FT.ALIASUPDATE</a></strong> - Adds or updates an alias to the index</summary>\n\n**Syntax:** `FT.ALIASUPDATE alias index`\n\n**Description:** Adds or updates an alias to the index\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.alter/\">FT.ALTER</a></strong> - Adds a new field to the index</summary>\n\n**Syntax:** `FT.ALTER index [SKIPINITIALSCAN] SCHEMA ADD field options`\n\n**Description:** Adds a new field to the index\n\n**Complexity:** O(N) where N is the number of keys in the keyspace\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.config-get/\">FT.CONFIG GET</a></strong> - Retrieves runtime configuration options</summary>\n\n**Syntax:** `FT.CONFIG GET option`\n\n**Description:** Retrieves runtime configuration options\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.config-set/\">FT.CONFIG SET</a></strong> - Sets runtime configuration options</summary>\n\n**Syntax:** `FT.CONFIG SET option value`\n\n**Description:** Sets runtime configuration options\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.create/\">FT.CREATE</a></strong> - Creates an index with the given spec</summary>\n\n**Syntax:** `FT.CREATE index [ON <HASH | JSON>] [PREFIX count prefix [prefix\n  ...]] [FILTER filter] [LANGUAGE default lang]\n  [LANGUAGE FIELD lang attribute] [SCORE default score]\n  [SCORE FIELD score attribute] [PAYLOAD FIELD payload attribute]\n  [MAXTEXTFIELDS] [TEMPORARY seconds] [NOOFFSETS] [NOHL] [NOFIELDS]\n  [NOFREQS] [STOPWORDS count [stopword [stopword ...]]]\n  [SKIPINITIALSCAN] SCHEMA field name [AS alias] <TEXT | TAG |\n  NUMERIC | GEO | VECTOR> [WITHSUFFIXTRIE] [SORTABLE [UNF]]\n  [NOINDEX] [field name [AS alias] <TEXT | TAG | NUMERIC | GEO |\n  VECTOR> [WITHSUFFIXTRIE] [SORTABLE [UNF]] [NOINDEX] ...]`\n\n**Description:** Creates an index with the given spec\n\n**Complexity:** O(K) at creation where K is the number of fields, O(N) if scanning the keyspace is triggered, where N is the number of keys in the keyspace\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.cursor-del/\">FT.CURSOR DEL</a></strong> - Deletes a cursor</summary>\n\n**Syntax:** `FT.CURSOR DEL index cursor id`\n\n**Description:** Deletes a cursor\n\n**Complexity:** O(1)\n\n**Since:** 1.1.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.cursor-read/\">FT.CURSOR READ</a></strong> - Reads from a cursor</summary>\n\n**Syntax:** `FT.CURSOR READ index cursor id [COUNT read size]`\n\n**Description:** Reads from a cursor\n\n**Complexity:** O(1)\n\n**Since:** 1.1.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.dictadd/\">FT.DICTADD</a></strong> - Adds terms to a dictionary</summary>\n\n**Syntax:** `FT.DICTADD dict term [term ...]`\n\n**Description:** Adds terms to a dictionary\n\n**Complexity:** O(1)\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.dictdel/\">FT.DICTDEL</a></strong> - Deletes terms from a dictionary</summary>\n\n**Syntax:** `FT.DICTDEL dict term [term ...]`\n\n**Description:** Deletes terms from a dictionary\n\n**Complexity:** O(1)\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.dictdump/\">FT.DICTDUMP</a></strong> - Dumps all terms in the given dictionary</summary>\n\n**Syntax:** `FT.DICTDUMP dict`\n\n**Description:** Dumps all terms in the given dictionary\n\n**Complexity:** O(N), where N is the size of the dictionary\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.dropindex/\">FT.DROPINDEX</a></strong> - Deletes the index</summary>\n\n**Syntax:** `FT.DROPINDEX index [DD]`\n\n**Description:** Deletes the index\n\n**Complexity:** O(1) or O(N) if documents are deleted, where N is the number of keys in the keyspace\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.explain/\">FT.EXPLAIN</a></strong> - Returns the execution plan for a complex query</summary>\n\n**Syntax:** `FT.EXPLAIN index query [DIALECT dialect]`\n\n**Description:** Returns the execution plan for a complex query\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.explaincli/\">FT.EXPLAINCLI</a></strong> - Returns the execution plan for a complex query</summary>\n\n**Syntax:** `FT.EXPLAINCLI index query [DIALECT dialect]`\n\n**Description:** Returns the execution plan for a complex query\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.info/\">FT.INFO</a></strong> - Returns information and statistics on the index</summary>\n\n**Syntax:** `FT.INFO index`\n\n**Description:** Returns information and statistics on the index\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.profile/\">FT.PROFILE</a></strong> - Performs a `FT.SEARCH` or `FT.AGGREGATE` command and collects performance information</summary>\n\n**Syntax:** `FT.PROFILE index <SEARCH | AGGREGATE> [LIMITED] QUERY query`\n\n**Description:** Performs a `FT.SEARCH` or `FT.AGGREGATE` command and collects performance information\n\n**Complexity:** O(N)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.search/\">FT.SEARCH</a></strong> - Searches the index with a textual query, returning either documents or just ids</summary>\n\n**Syntax:** `FT.SEARCH index query [NOCONTENT] [VERBATIM] [NOSTOPWORDS]\n  [WITHSCORES] [WITHPAYLOADS] [WITHSORTKEYS] [FILTER numeric field\n  min max [FILTER numeric field min max ...]] [GEOFILTER geo field\n  lon lat radius <m | km | mi | ft> [GEOFILTER geo field lon lat\n  radius <m | km | mi | ft> ...]] [INKEYS count key [key ...]]\n  [INFIELDS count field [field ...]] [RETURN count identifier\n  [AS property] [identifier [AS property] ...]] [SUMMARIZE\n  [FIELDS count field [field ...]] [FRAGS num] [LEN fragsize]\n  [SEPARATOR separator]] [HIGHLIGHT [FIELDS count field [field ...]]\n  [TAGS open close]] [SLOP slop] [TIMEOUT timeout] [INORDER]\n  [LANGUAGE language] [EXPANDER expander] [SCORER scorer]\n  [EXPLAINSCORE] [PAYLOAD payload] [SORTBY sortby [ASC | DESC]]\n  [LIMIT offset num] [PARAMS nargs name value [name value ...]]\n  [DIALECT dialect]`\n\n**Description:** Searches the index with a textual query, returning either documents or just ids\n\n**Complexity:** O(N)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.spellcheck/\">FT.SPELLCHECK</a></strong> - Performs spelling correction on a query, returning suggestions for misspelled terms</summary>\n\n**Syntax:** `FT.SPELLCHECK index query [DISTANCE distance] [TERMS <INCLUDE |\n  EXCLUDE> dictionary [terms [terms ...]]] [DIALECT dialect]`\n\n**Description:** Performs spelling correction on a query, returning suggestions for misspelled terms\n\n**Complexity:** O(1)\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.syndump/\">FT.SYNDUMP</a></strong> - Dumps the contents of a synonym group</summary>\n\n**Syntax:** `FT.SYNDUMP index`\n\n**Description:** Dumps the contents of a synonym group\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.synupdate/\">FT.SYNUPDATE</a></strong> - Creates or updates a synonym group with additional terms</summary>\n\n**Syntax:** `FT.SYNUPDATE index synonym group id [SKIPINITIALSCAN] term [term\n  ...]`\n\n**Description:** Creates or updates a synonym group with additional terms\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.tagvals/\">FT.TAGVALS</a></strong> - Returns the distinct tags indexed in a Tag field</summary>\n\n**Syntax:** `FT.TAGVALS index field name`\n\n**Description:** Returns the distinct tags indexed in a Tag field\n\n**Complexity:** O(N)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft._list/\">FT._LIST</a></strong> - Returns a list of all existing indexes</summary>\n\n**Syntax:** `FT. LIST`\n\n**Description:** Returns a list of all existing indexes\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>"},{"id":"time-series-commands","title":"Time series commands","role":"content","text":"Time series commands operate on time-series data.\n\n<details>\n<summary><strong><a href=\"/commands/ts.add/\">TS.ADD</a></strong> - Append a sample to a time series</summary>\n\n**Syntax:** `TS.ADD key timestamp value [RETENTION retentionPeriod]\n  [ENCODING <COMPRESSED | UNCOMPRESSED>] [CHUNK SIZE size]\n  [DUPLICATE POLICY policy] \n  [ON DUPLICATE <BLOCK | FIRST | LAST | MIN | MAX | SUM>]\n  [IGNORE ignoreMaxTimediff ignoreMaxValDiff]\n  [LABELS [label value ...]]`\n\n**Description:** Append a sample to a time series\n\n**Complexity:** O(M) when M is the amount of compaction rules or O(1) with no compaction\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.alter/\">TS.ALTER</a></strong> - Update the retention, chunk size, duplicate policy, and labels of an existing time series</summary>\n\n**Syntax:** `TS.ALTER key [RETENTION retentionPeriod] [CHUNK SIZE size]\n  [DUPLICATE POLICY <BLOCK | FIRST | LAST | MIN | MAX | SUM>]\n  [IGNORE ignoreMaxTimediff ignoreMaxValDiff] \n  [LABELS [label value ...]]`\n\n**Description:** Update the retention, chunk size, duplicate policy, and labels of an existing time series\n\n**Complexity:** O(N) where N is the number of labels requested to update\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.create/\">TS.CREATE</a></strong> - Create a new time series</summary>\n\n**Syntax:** `TS.CREATE key [RETENTION retentionPeriod] [ENCODING <COMPRESSED |\n  UNCOMPRESSED>] [CHUNK SIZE size] [DUPLICATE POLICY <BLOCK | FIRST |\n  LAST | MIN | MAX | SUM>]\n  [IGNORE ignoreMaxTimediff ignoreMaxValDiff]\n  [LABELS [label value ...]]`\n\n**Description:** Create a new time series\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.createrule/\">TS.CREATERULE</a></strong> - Create a compaction rule</summary>\n\n**Syntax:** `TS.CREATERULE sourceKey destKey AGGREGATION <AVG | FIRST | LAST |\n  MIN | MAX | SUM | RANGE | COUNT | STD.P | STD.S | VAR.P | VAR.S |\n  TWA> bucketDuration [alignTimestamp]`\n\n**Description:** Create a compaction rule\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.decrby/\">TS.DECRBY</a></strong> - Decrease the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given decrement</summary>\n\n**Syntax:** `TS.DECRBY key value [TIMESTAMP timestamp]\n  [RETENTION retentionPeriod] [ENCODING <COMPRESSED|UNCOMPRESSED>] [CHUNK SIZE size]\n [DUPLICATE POLICY policy] [LABELS [label value ...]]`\n\n**Description:** Decrease the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given decrement\n\n**Complexity:** O(M) when M is the amount of compaction rules or O(1) with no compaction\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.del/\">TS.DEL</a></strong> - Delete all samples between two timestamps for a given time series</summary>\n\n**Syntax:** `TS.DEL key from timestamp to timestamp`\n\n**Description:** Delete all samples between two timestamps for a given time series\n\n**Complexity:** O(N) where N is the number of data points that will be removed\n\n**Since:** 1.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.deleterule/\">TS.DELETERULE</a></strong> - Delete a compaction rule</summary>\n\n**Syntax:** `TS.DELETERULE sourceKey destKey`\n\n**Description:** Delete a compaction rule\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.get/\">TS.GET</a></strong> - Get the sample with the highest timestamp from a given time series</summary>\n\n**Syntax:** `TS.GET key [LATEST]`\n\n**Description:** Get the sample with the highest timestamp from a given time series\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.incrby/\">TS.INCRBY</a></strong> - Increase the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given increment</summary>\n\n**Syntax:** `TS.INCRBY key value [TIMESTAMP timestamp]\n  [RETENTION retentionPeriod] [ENCODING <COMPRESSED|UNCOMPRESSED>] [CHUNK SIZE size]\n [DUPLICATE POLICY policy] [LABELS [label value ...]]`\n\n**Description:** Increase the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given increment\n\n**Complexity:** O(M) when M is the amount of compaction rules or O(1) with no compaction\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.info/\">TS.INFO</a></strong> - Returns information and statistics for a time series</summary>\n\n**Syntax:** `TS.INFO key [DEBUG]`\n\n**Description:** Returns information and statistics for a time series\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.madd/\">TS.MADD</a></strong> - Append new samples to one or more time series</summary>\n\n**Syntax:** `TS.MADD key timestamp value [key timestamp value ...]`\n\n**Description:** Append new samples to one or more time series\n\n**Complexity:** O(N*M) when N is the amount of series updated and M is the amount of compaction rules or O(N) with no compaction\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.mget/\">TS.MGET</a></strong> - Get the sample with the highest timestamp from each time series matching a specific filter</summary>\n\n**Syntax:** `TS.MGET [LATEST] [WITHLABELS | <SELECTED LABELS label1 [label1 ...]>]\n  FILTER <l=v | l!=v | l= | l!= | l=(v1,v2,...) | l!=(v1,v2,...)\n  [l=v | l!=v | l= | l!= | l=(v1,v2,...) | l!=(v1,v2,...) ...]>`\n\n**Description:** Get the sample with the highest timestamp from each time series matching a specific filter\n\n**Complexity:** O(n) where n is the number of time-series that match the filters\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.mrange/\">TS.MRANGE</a></strong> - Query a range across multiple time series by filters in forward direction</summary>\n\n**Syntax:** `TS.MRANGE fromTimestamp toTimestamp [LATEST] [FILTER BY TS Timestamp\n  [Timestamp ...]] [FILTER BY VALUE min max] [WITHLABELS |\n  <SELECTED LABELS label1 [label1 ...]>] [COUNT count] [[ALIGN value]\n  AGGREGATION <AVG | FIRST | LAST | MIN | MAX | SUM | RANGE | COUNT\n  | STD.P | STD.S | VAR.P | VAR.S | TWA> bucketDuration\n  [BUCKETTIMESTAMP] [EMPTY]] FILTER <l=v | l!=v | l= | l!= |\n  l=(v1,v2,...) | l!=(v1,v2,...) [l=v | l!=v | l= | l!= |\n  l=(v1,v2,...) | l!=(v1,v2,...) ...]> [GROUPBY label REDUCE\n  reducer]`\n\n**Description:** Query a range across multiple time series by filters in forward direction\n\n**Complexity:** O(n/m+k) where n = Number of data points, m = Chunk size (data points per chunk), k = Number of data points that are in the requested ranges\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.mrevrange/\">TS.MREVRANGE</a></strong> - Query a range across multiple time-series by filters in reverse direction</summary>\n\n**Syntax:** `TS.MREVRANGE fromTimestamp toTimestamp [LATEST]\n  [FILTER BY TS Timestamp [Timestamp ...]] [FILTER BY VALUE min max]\n  [WITHLABELS | <SELECTED LABELS label1 [label1 ...]>] [COUNT count]\n  [[ALIGN value] AGGREGATION <AVG | FIRST | LAST | MIN | MAX | SUM |\n  RANGE | COUNT | STD.P | STD.S | VAR.P | VAR.S | TWA>\n  bucketDuration [BUCKETTIMESTAMP] [EMPTY]] FILTER <l=v | l!=v | l=\n  | l!= | l=(v1,v2,...) | l!=(v1,v2,...) [l=v | l!=v | l= | l!= |\n  l=(v1,v2,...) | l!=(v1,v2,...) ...]> [GROUPBY label REDUCE\n  reducer]`\n\n**Description:** Query a range across multiple time-series by filters in reverse direction\n\n**Complexity:** O(n/m+k) where n = Number of data points, m = Chunk size (data points per chunk), k = Number of data points that are in the requested ranges\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.queryindex/\">TS.QUERYINDEX</a></strong> - Get all time series keys matching a filter list</summary>\n\n**Syntax:** `TS.QUERYINDEX <l=v | l!=v | l= | l!= | l=(v1,v2,...) |\n  l!=(v1,v2,...) [l=v | l!=v | l= | l!= | l=(v1,v2,...) |\n  l!=(v1,v2,...) ...]>`\n\n**Description:** Get all time series keys matching a filter list\n\n**Complexity:** O(n) where n is the number of time-series that match the filters\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.range/\">TS.RANGE</a></strong> - Query a range in forward direction</summary>\n\n**Syntax:** `TS.RANGE key fromTimestamp toTimestamp [LATEST]\n  [FILTER BY TS Timestamp [Timestamp ...]] [FILTER BY VALUE min max]\n  [COUNT count] [[ALIGN value] AGGREGATION <AVG | FIRST | LAST | MIN\n  | MAX | SUM | RANGE | COUNT | STD.P | STD.S | VAR.P | VAR.S | TWA>\n  bucketDuration [BUCKETTIMESTAMP] [EMPTY]]`\n\n**Description:** Query a range in forward direction\n\n**Complexity:** O(n/m+k) where n = Number of data points, m = Chunk size (data points per chunk), k = Number of data points that are in the requested range\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.revrange/\">TS.REVRANGE</a></strong> - Query a range in reverse direction</summary>\n\n**Syntax:** `TS.REVRANGE key fromTimestamp toTimestamp [LATEST]\n  [FILTER BY TS Timestamp [Timestamp ...]] [FILTER BY VALUE min max]\n  [COUNT count] [[ALIGN value] AGGREGATION <AVG | FIRST | LAST | MIN\n  | MAX | SUM | RANGE | COUNT | STD.P | STD.S | VAR.P | VAR.S | TWA>\n  bucketDuration [BUCKETTIMESTAMP] [EMPTY]]`\n\n**Description:** Query a range in reverse direction\n\n**Complexity:** O(n/m+k) where n = Number of data points, m = Chunk size (data points per chunk), k = Number of data points that are in the requested range\n\n**Since:** 1.4.0\n\n</details>"},{"id":"pubsub-commands","title":"Pub/Sub commands","role":"content","text":"Pub/Sub commands enable message passing between clients.\n\n<details>\n<summary><strong><a href=\"/commands/psubscribe/\">PSUBSCRIBE</a></strong> - Listens for messages published to channels that match one or more patterns.</summary>\n\n**Syntax:** `PSUBSCRIBE pattern [pattern ...]`\n\n**Description:** Listens for messages published to channels that match one or more patterns.\n\n**Complexity:** O(N) where N is the number of patterns to subscribe to.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/publish/\">PUBLISH</a></strong> - Posts a message to a channel.</summary>\n\n**Syntax:** `PUBLISH channel message`\n\n**Description:** Posts a message to a channel.\n\n**Complexity:** O(N+M) where N is the number of clients subscribed to the receiving channel and M is the total number of subscribed patterns (by any client).\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-channels/\">PUBSUB CHANNELS</a></strong> - Returns the active channels.</summary>\n\n**Syntax:** `PUBSUB CHANNELS [pattern]`\n\n**Description:** Returns the active channels.\n\n**Complexity:** O(N) where N is the number of active channels, and assuming constant time pattern matching (relatively short channels and patterns)\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-numpat/\">PUBSUB NUMPAT</a></strong> - Returns a count of unique pattern subscriptions.</summary>\n\n**Syntax:** `PUBSUB NUMPAT`\n\n**Description:** Returns a count of unique pattern subscriptions.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-numsub/\">PUBSUB NUMSUB</a></strong> - Returns a count of subscribers to channels.</summary>\n\n**Syntax:** `PUBSUB NUMSUB [channel [channel ...]]`\n\n**Description:** Returns a count of subscribers to channels.\n\n**Complexity:** O(N) for the NUMSUB subcommand, where N is the number of requested channels\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/punsubscribe/\">PUNSUBSCRIBE</a></strong> - Stops listening to messages published to channels that match one or more patterns.</summary>\n\n**Syntax:** `PUNSUBSCRIBE [pattern [pattern ...]]`\n\n**Description:** Stops listening to messages published to channels that match one or more patterns.\n\n**Complexity:** O(N) where N is the number of patterns to unsubscribe.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/subscribe/\">SUBSCRIBE</a></strong> - Listens for messages published to channels.</summary>\n\n**Syntax:** `SUBSCRIBE channel [channel ...]`\n\n**Description:** Listens for messages published to channels.\n\n**Complexity:** O(N) where N is the number of channels to subscribe to.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/unsubscribe/\">UNSUBSCRIBE</a></strong> - Stops listening to messages posted to channels.</summary>\n\n**Syntax:** `UNSUBSCRIBE [channel [channel ...]]`\n\n**Description:** Stops listening to messages posted to channels.\n\n**Complexity:** O(N) where N is the number of channels to unsubscribe.\n\n**Since:** 2.0.0\n\n</details>"},{"id":"transaction-commands","title":"Transaction commands","role":"content","text":"Transaction commands enable atomic execution of command groups.\n\n<details>\n<summary><strong><a href=\"/commands/discard/\">DISCARD</a></strong> - Discards a transaction.</summary>\n\n**Syntax:** `DISCARD`\n\n**Description:** Discards a transaction.\n\n**Complexity:** O(N), when N is the number of queued commands\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/exec/\">EXEC</a></strong> - Executes all commands in a transaction.</summary>\n\n**Syntax:** `EXEC`\n\n**Description:** Executes all commands in a transaction.\n\n**Complexity:** Depends on commands in the transaction\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/multi/\">MULTI</a></strong> - Starts a transaction.</summary>\n\n**Syntax:** `MULTI`\n\n**Description:** Starts a transaction.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/unwatch/\">UNWATCH</a></strong> - Forgets about watched keys of a transaction.</summary>\n\n**Syntax:** `UNWATCH`\n\n**Description:** Forgets about watched keys of a transaction.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/watch/\">WATCH</a></strong> - Monitors changes to keys to determine the execution of a transaction.</summary>\n\n**Syntax:** `WATCH key [key ...]`\n\n**Description:** Monitors changes to keys to determine the execution of a transaction.\n\n**Complexity:** O(1) for every key.\n\n**Since:** 2.2.0\n\n</details>"},{"id":"scripting-commands","title":"Scripting commands","role":"content","text":"Scripting commands enable server-side Lua script execution.\n\n<details>\n<summary><strong><a href=\"/commands/eval/\">EVAL</a></strong> - Executes a server-side Lua script.</summary>\n\n**Syntax:** `EVAL script numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Executes a server-side Lua script.\n\n**Complexity:** Depends on the script that is executed.\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/evalsha/\">EVALSHA</a></strong> - Executes a server-side Lua script by SHA1 digest.</summary>\n\n**Syntax:** `EVALSHA sha1 numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Executes a server-side Lua script by SHA1 digest.\n\n**Complexity:** Depends on the script that is executed.\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-debug/\">SCRIPT DEBUG</a></strong> - Sets the debug mode of server-side Lua scripts.</summary>\n\n**Syntax:** `SCRIPT DEBUG <YES | SYNC | NO>`\n\n**Description:** Sets the debug mode of server-side Lua scripts.\n\n**Complexity:** O(1)\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-exists/\">SCRIPT EXISTS</a></strong> - Determines whether server-side Lua scripts exist in the script cache.</summary>\n\n**Syntax:** `SCRIPT EXISTS sha1 [sha1 ...]`\n\n**Description:** Determines whether server-side Lua scripts exist in the script cache.\n\n**Complexity:** O(N) with N being the number of scripts to check (so checking a single script is an O(1) operation).\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-flush/\">SCRIPT FLUSH</a></strong> - Removes all server-side Lua scripts from the script cache.</summary>\n\n**Syntax:** `SCRIPT FLUSH [ASYNC | SYNC]`\n\n**Description:** Removes all server-side Lua scripts from the script cache.\n\n**Complexity:** O(N) with N being the number of scripts in cache\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-kill/\">SCRIPT KILL</a></strong> - Terminates a server-side Lua script during execution.</summary>\n\n**Syntax:** `SCRIPT KILL`\n\n**Description:** Terminates a server-side Lua script during execution.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-load/\">SCRIPT LOAD</a></strong> - Loads a server-side Lua script to the script cache.</summary>\n\n**Syntax:** `SCRIPT LOAD script`\n\n**Description:** Loads a server-side Lua script to the script cache.\n\n**Complexity:** O(N) with N being the length in bytes of the script body.\n\n**Since:** 2.6.0\n\n</details>"},{"id":"connection-commands","title":"Connection commands","role":"content","text":"Connection commands manage client connections.\n\n<details>\n<summary><strong><a href=\"/commands/auth/\">AUTH</a></strong> - Authenticates the connection.</summary>\n\n**Syntax:** `AUTH [username] password`\n\n**Description:** Authenticates the connection.\n\n**Complexity:** O(N) where N is the number of passwords defined for the user\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-caching/\">CLIENT CACHING</a></strong> - Instructs the server whether to track the keys in the next request.</summary>\n\n**Syntax:** `CLIENT CACHING <YES | NO>`\n\n**Description:** Instructs the server whether to track the keys in the next request.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-getname/\">CLIENT GETNAME</a></strong> - Returns the name of the connection.</summary>\n\n**Syntax:** `CLIENT GETNAME`\n\n**Description:** Returns the name of the connection.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-getredir/\">CLIENT GETREDIR</a></strong> - Returns the client ID to which the connection's tracking notifications are redirected.</summary>\n\n**Syntax:** `CLIENT GETREDIR`\n\n**Description:** Returns the client ID to which the connection's tracking notifications are redirected.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-id/\">CLIENT ID</a></strong> - Returns the unique client ID of the connection.</summary>\n\n**Syntax:** `CLIENT ID`\n\n**Description:** Returns the unique client ID of the connection.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-info/\">CLIENT INFO</a></strong> - Returns information about the connection. <span style=\"color: #e74c3c;\">⭐ New in 6.2</span></summary>\n\n**Syntax:** `CLIENT INFO`\n\n**Description:** Returns information about the connection.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-kill/\">CLIENT KILL</a></strong> - Terminates open connections.</summary>\n\n**Syntax:** `CLIENT KILL <ip:port | <[ID client-id] | [TYPE <NORMAL | MASTER |\n  SLAVE | REPLICA | PUBSUB>] | [USER username] | [ADDR ip:port] |\n  [LADDR ip:port] | [SKIPME <YES | NO>] | [MAXAGE maxage]\n  [[ID client-id] | [TYPE <NORMAL | MASTER | SLAVE | REPLICA |\n  PUBSUB>] | [USER username] | [ADDR ip:port] | [LADDR ip:port] |\n  [SKIPME <YES | NO>] | [MAXAGE maxage] ...]>>`\n\n**Description:** Terminates open connections.\n\n**Complexity:** O(N) where N is the number of client connections\n\n**Since:** 2.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-list/\">CLIENT LIST</a></strong> - Lists open connections.</summary>\n\n**Syntax:** `CLIENT LIST [TYPE <NORMAL | MASTER | REPLICA | PUBSUB>]\n  [ID client-id [client-id ...]]`\n\n**Description:** Lists open connections.\n\n**Complexity:** O(N) where N is the number of client connections\n\n**Since:** 2.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-pause/\">CLIENT PAUSE</a></strong> - Suspends commands processing.</summary>\n\n**Syntax:** `CLIENT PAUSE timeout [WRITE | ALL]`\n\n**Description:** Suspends commands processing.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-reply/\">CLIENT REPLY</a></strong> - Instructs the server whether to reply to commands.</summary>\n\n**Syntax:** `CLIENT REPLY <ON | OFF | SKIP>`\n\n**Description:** Instructs the server whether to reply to commands.\n\n**Complexity:** O(1)\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-setname/\">CLIENT SETNAME</a></strong> - Sets the connection name.</summary>\n\n**Syntax:** `CLIENT SETNAME connection-name`\n\n**Description:** Sets the connection name.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-tracking/\">CLIENT TRACKING</a></strong> - Controls server-assisted client-side caching for the connection.</summary>\n\n**Syntax:** `CLIENT TRACKING <ON | OFF> [REDIRECT client-id] [PREFIX prefix\n  [PREFIX prefix ...]] [BCAST] [OPTIN] [OPTOUT] [NOLOOP]`\n\n**Description:** Controls server-assisted client-side caching for the connection.\n\n**Complexity:** O(1). Some options may introduce additional complexity.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-trackinginfo/\">CLIENT TRACKINGINFO</a></strong> - Returns information about server-assisted client-side caching for the connection. <span style=\"color: #e74c3c;\">⭐ New in 6.2</span></summary>\n\n**Syntax:** `CLIENT TRACKINGINFO`\n\n**Description:** Returns information about server-assisted client-side caching for the connection.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-unblock/\">CLIENT UNBLOCK</a></strong> - Unblocks a client blocked by a blocking command from a different connection.</summary>\n\n**Syntax:** `CLIENT UNBLOCK client-id [TIMEOUT | ERROR]`\n\n**Description:** Unblocks a client blocked by a blocking command from a different connection.\n\n**Complexity:** O(log N) where N is the number of client connections\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-unpause/\">CLIENT UNPAUSE</a></strong> - Resumes processing commands from paused clients. <span style=\"color: #e74c3c;\">⭐ New in 6.2</span></summary>\n\n**Syntax:** `CLIENT UNPAUSE`\n\n**Description:** Resumes processing commands from paused clients.\n\n**Complexity:** O(N) Where N is the number of paused clients\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/echo/\">ECHO</a></strong> - Returns the given string.</summary>\n\n**Syntax:** `ECHO message`\n\n**Description:** Returns the given string.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hello/\">HELLO</a></strong> - Handshakes with the Redis server.</summary>\n\n**Syntax:** `HELLO [protover [AUTH username password] [SETNAME clientname]]`\n\n**Description:** Handshakes with the Redis server.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ping/\">PING</a></strong> - Returns the server's liveliness response.</summary>\n\n**Syntax:** `PING [message]`\n\n**Description:** Returns the server's liveliness response.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/quit/\">QUIT</a></strong> - Closes the connection.</summary>\n\n**Syntax:** `QUIT`\n\n**Description:** Closes the connection.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/reset/\">RESET</a></strong> - Resets the connection. <span style=\"color: #e74c3c;\">⭐ New in 6.2</span></summary>\n\n**Syntax:** `RESET`\n\n**Description:** Resets the connection.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/select/\">SELECT</a></strong> - Changes the selected database.</summary>\n\n**Syntax:** `SELECT index`\n\n**Description:** Changes the selected database.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>"},{"id":"server-commands","title":"Server commands","role":"content","text":"Server commands provide server management and introspection.\n\n<details>\n<summary><strong><a href=\"/commands/acl-cat/\">ACL CAT</a></strong> - Lists the ACL categories, or the commands inside a category.</summary>\n\n**Syntax:** `ACL CAT [category]`\n\n**Description:** Lists the ACL categories, or the commands inside a category.\n\n**Complexity:** O(1) since the categories and commands are a fixed set.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-deluser/\">ACL DELUSER</a></strong> - Deletes ACL users, and terminates their connections.</summary>\n\n**Syntax:** `ACL DELUSER username [username ...]`\n\n**Description:** Deletes ACL users, and terminates their connections.\n\n**Complexity:** O(1) amortized time considering the typical user.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-genpass/\">ACL GENPASS</a></strong> - Generates a pseudorandom, secure password that can be used to identify ACL users.</summary>\n\n**Syntax:** `ACL GENPASS [bits]`\n\n**Description:** Generates a pseudorandom, secure password that can be used to identify ACL users.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-getuser/\">ACL GETUSER</a></strong> - Lists the ACL rules of a user.</summary>\n\n**Syntax:** `ACL GETUSER username`\n\n**Description:** Lists the ACL rules of a user.\n\n**Complexity:** O(N). Where N is the number of password, command and pattern rules that the user has.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-list/\">ACL LIST</a></strong> - Dumps the effective rules in ACL file format.</summary>\n\n**Syntax:** `ACL LIST`\n\n**Description:** Dumps the effective rules in ACL file format.\n\n**Complexity:** O(N). Where N is the number of configured users.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-load/\">ACL LOAD</a></strong> - Reloads the rules from the configured ACL file.</summary>\n\n**Syntax:** `ACL LOAD`\n\n**Description:** Reloads the rules from the configured ACL file.\n\n**Complexity:** O(N). Where N is the number of configured users.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-log/\">ACL LOG</a></strong> - Lists recent security events generated due to ACL rules.</summary>\n\n**Syntax:** `ACL LOG [count | RESET]`\n\n**Description:** Lists recent security events generated due to ACL rules.\n\n**Complexity:** O(N) with N being the number of entries shown.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-save/\">ACL SAVE</a></strong> - Saves the effective ACL rules in the configured ACL file.</summary>\n\n**Syntax:** `ACL SAVE`\n\n**Description:** Saves the effective ACL rules in the configured ACL file.\n\n**Complexity:** O(N). Where N is the number of configured users.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-setuser/\">ACL SETUSER</a></strong> - Creates and modifies an ACL user and its rules.</summary>\n\n**Syntax:** `ACL SETUSER username [rule [rule ...]]`\n\n**Description:** Creates and modifies an ACL user and its rules.\n\n**Complexity:** O(N). Where N is the number of rules provided.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-users/\">ACL USERS</a></strong> - Lists all ACL users.</summary>\n\n**Syntax:** `ACL USERS`\n\n**Description:** Lists all ACL users.\n\n**Complexity:** O(N). Where N is the number of configured users.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-whoami/\">ACL WHOAMI</a></strong> - Returns the authenticated username of the current connection.</summary>\n\n**Syntax:** `ACL WHOAMI`\n\n**Description:** Returns the authenticated username of the current connection.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bgrewriteaof/\">BGREWRITEAOF</a></strong> - Asynchronously rewrites the append-only file to disk.</summary>\n\n**Syntax:** `BGREWRITEAOF`\n\n**Description:** Asynchronously rewrites the append-only file to disk.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bgsave/\">BGSAVE</a></strong> - Asynchronously saves the database(s) to disk.</summary>\n\n**Syntax:** `BGSAVE [SCHEDULE]`\n\n**Description:** Asynchronously saves the database(s) to disk.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command/\">COMMAND</a></strong> - Returns detailed information about all commands.</summary>\n\n**Syntax:** `COMMAND`\n\n**Description:** Returns detailed information about all commands.\n\n**Complexity:** O(N) where N is the total number of Redis commands\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-count/\">COMMAND COUNT</a></strong> - Returns a count of commands.</summary>\n\n**Syntax:** `COMMAND COUNT`\n\n**Description:** Returns a count of commands.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-getkeys/\">COMMAND GETKEYS</a></strong> - Extracts the key names from an arbitrary command.</summary>\n\n**Syntax:** `COMMAND GETKEYS command [arg [arg ...]]`\n\n**Description:** Extracts the key names from an arbitrary command.\n\n**Complexity:** O(N) where N is the number of arguments to the command\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-info/\">COMMAND INFO</a></strong> - Returns information about one, multiple or all commands.</summary>\n\n**Syntax:** `COMMAND INFO [command-name [command-name ...]]`\n\n**Description:** Returns information about one, multiple or all commands.\n\n**Complexity:** O(N) where N is the number of commands to look up\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/config-get/\">CONFIG GET</a></strong> - Returns the effective values of configuration parameters.</summary>\n\n**Syntax:** `CONFIG GET parameter [parameter ...]`\n\n**Description:** Returns the effective values of configuration parameters.\n\n**Complexity:** O(N) when N is the number of configuration parameters provided\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/config-resetstat/\">CONFIG RESETSTAT</a></strong> - Resets the server's statistics.</summary>\n\n**Syntax:** `CONFIG RESETSTAT`\n\n**Description:** Resets the server's statistics.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/config-rewrite/\">CONFIG REWRITE</a></strong> - Persists the effective configuration to file.</summary>\n\n**Syntax:** `CONFIG REWRITE`\n\n**Description:** Persists the effective configuration to file.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/config-set/\">CONFIG SET</a></strong> - Sets configuration parameters in-flight.</summary>\n\n**Syntax:** `CONFIG SET parameter value [parameter value ...]`\n\n**Description:** Sets configuration parameters in-flight.\n\n**Complexity:** O(N) when N is the number of configuration parameters provided\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/dbsize/\">DBSIZE</a></strong> - Returns the number of keys in the database.</summary>\n\n**Syntax:** `DBSIZE`\n\n**Description:** Returns the number of keys in the database.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/failover/\">FAILOVER</a></strong> - Starts a coordinated failover from a server to one of its replicas. <span style=\"color: #e74c3c;\">⭐ New in 6.2</span></summary>\n\n**Syntax:** `FAILOVER [TO host port [FORCE]] [ABORT] [TIMEOUT milliseconds]`\n\n**Description:** Starts a coordinated failover from a server to one of its replicas.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/flushall/\">FLUSHALL</a></strong> - Removes all keys from all databases.</summary>\n\n**Syntax:** `FLUSHALL [ASYNC | SYNC]`\n\n**Description:** Removes all keys from all databases.\n\n**Complexity:** O(N) where N is the total number of keys in all databases\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/flushdb/\">FLUSHDB</a></strong> - Remove all keys from the current database.</summary>\n\n**Syntax:** `FLUSHDB [ASYNC | SYNC]`\n\n**Description:** Remove all keys from the current database.\n\n**Complexity:** O(N) where N is the number of keys in the selected database\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/info/\">INFO</a></strong> - Returns information and statistics about the server.</summary>\n\n**Syntax:** `INFO [section [section ...]]`\n\n**Description:** Returns information and statistics about the server.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lastsave/\">LASTSAVE</a></strong> - Returns the Unix timestamp of the last successful save to disk.</summary>\n\n**Syntax:** `LASTSAVE`\n\n**Description:** Returns the Unix timestamp of the last successful save to disk.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-doctor/\">LATENCY DOCTOR</a></strong> - Returns a human-readable latency analysis report.</summary>\n\n**Syntax:** `LATENCY DOCTOR`\n\n**Description:** Returns a human-readable latency analysis report.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-graph/\">LATENCY GRAPH</a></strong> - Returns a latency graph for an event.</summary>\n\n**Syntax:** `LATENCY GRAPH event`\n\n**Description:** Returns a latency graph for an event.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-history/\">LATENCY HISTORY</a></strong> - Returns timestamp-latency samples for an event.</summary>\n\n**Syntax:** `LATENCY HISTORY event`\n\n**Description:** Returns timestamp-latency samples for an event.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-latest/\">LATENCY LATEST</a></strong> - Returns the latest latency samples for all events.</summary>\n\n**Syntax:** `LATENCY LATEST`\n\n**Description:** Returns the latest latency samples for all events.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-reset/\">LATENCY RESET</a></strong> - Resets the latency data for one or more events.</summary>\n\n**Syntax:** `LATENCY RESET [event [event ...]]`\n\n**Description:** Resets the latency data for one or more events.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lolwut/\">LOLWUT</a></strong> - Displays computer art and the Redis version</summary>\n\n**Syntax:** `LOLWUT [VERSION version]`\n\n**Description:** Displays computer art and the Redis version\n\n**Complexity:** \n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-doctor/\">MEMORY DOCTOR</a></strong> - Outputs a memory problems report.</summary>\n\n**Syntax:** `MEMORY DOCTOR`\n\n**Description:** Outputs a memory problems report.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-malloc-stats/\">MEMORY MALLOC-STATS</a></strong> - Returns the allocator statistics.</summary>\n\n**Syntax:** `MEMORY MALLOC-STATS`\n\n**Description:** Returns the allocator statistics.\n\n**Complexity:** Depends on how much memory is allocated, could be slow\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-purge/\">MEMORY PURGE</a></strong> - Asks the allocator to release memory.</summary>\n\n**Syntax:** `MEMORY PURGE`\n\n**Description:** Asks the allocator to release memory.\n\n**Complexity:** Depends on how much memory is allocated, could be slow\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-stats/\">MEMORY STATS</a></strong> - Returns details about memory usage.</summary>\n\n**Syntax:** `MEMORY STATS`\n\n**Description:** Returns details about memory usage.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-usage/\">MEMORY USAGE</a></strong> - Estimates the memory usage of a key.</summary>\n\n**Syntax:** `MEMORY USAGE key [SAMPLES count]`\n\n**Description:** Estimates the memory usage of a key.\n\n**Complexity:** O(N) where N is the number of samples.\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/module-list/\">MODULE LIST</a></strong> - Returns all loaded modules.</summary>\n\n**Syntax:** `MODULE LIST`\n\n**Description:** Returns all loaded modules.\n\n**Complexity:** O(N) where N is the number of loaded modules.\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/module-load/\">MODULE LOAD</a></strong> - Loads a module.</summary>\n\n**Syntax:** `MODULE LOAD path [arg [arg ...]]`\n\n**Description:** Loads a module.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/module-unload/\">MODULE UNLOAD</a></strong> - Unloads a module.</summary>\n\n**Syntax:** `MODULE UNLOAD name`\n\n**Description:** Unloads a module.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/monitor/\">MONITOR</a></strong> - Listens for all requests received by the server in real-time.</summary>\n\n**Syntax:** `MONITOR`\n\n**Description:** Listens for all requests received by the server in real-time.\n\n**Complexity:** \n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/psync/\">PSYNC</a></strong> - An internal command used in replication.</summary>\n\n**Syntax:** `PSYNC replicationid offset`\n\n**Description:** An internal command used in replication.\n\n**Complexity:** \n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/replconf/\">REPLCONF</a></strong> - An internal command for configuring the replication stream.</summary>\n\n**Syntax:** `REPLCONF`\n\n**Description:** An internal command for configuring the replication stream.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/replicaof/\">REPLICAOF</a></strong> - Configures a server as replica of another, or promotes it to a master.</summary>\n\n**Syntax:** `REPLICAOF <host port | NO ONE>`\n\n**Description:** Configures a server as replica of another, or promotes it to a master.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/restore-asking/\">RESTORE-ASKING</a></strong> - An internal command for migrating keys in a cluster.</summary>\n\n**Syntax:** `RESTORE-ASKING key ttl serialized-value [REPLACE] [ABSTTL]\n  [IDLETIME seconds] [FREQ frequency]`\n\n**Description:** An internal command for migrating keys in a cluster.\n\n**Complexity:** O(1) to create the new key and additional O(N*M) to reconstruct the serialized value, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1). However for sorted set values the complexity is O(N*M*log(N)) because inserting values into sorted sets is O(log(N)).\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/role/\">ROLE</a></strong> - Returns the replication role.</summary>\n\n**Syntax:** `ROLE`\n\n**Description:** Returns the replication role.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.12\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/save/\">SAVE</a></strong> - Synchronously saves the database(s) to disk.</summary>\n\n**Syntax:** `SAVE`\n\n**Description:** Synchronously saves the database(s) to disk.\n\n**Complexity:** O(N) where N is the total number of keys in all databases\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/shutdown/\">SHUTDOWN</a></strong> - Synchronously saves the database(s) to disk and shuts down the Redis server.</summary>\n\n**Syntax:** `SHUTDOWN [NOSAVE | SAVE] [NOW] [FORCE] [ABORT]`\n\n**Description:** Synchronously saves the database(s) to disk and shuts down the Redis server.\n\n**Complexity:** O(N) when saving, where N is the total number of keys in all databases when saving data, otherwise O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/slaveof/\">SLAVEOF</a></strong> - Sets a Redis server as a replica of another, or promotes it to being a master.</summary>\n\n**Syntax:** `SLAVEOF <host port | NO ONE>`\n\n**Description:** Sets a Redis server as a replica of another, or promotes it to being a master.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/slowlog-get/\">SLOWLOG GET</a></strong> - Returns the slow log's entries.</summary>\n\n**Syntax:** `SLOWLOG GET [count]`\n\n**Description:** Returns the slow log's entries.\n\n**Complexity:** O(N) where N is the number of entries returned\n\n**Since:** 2.2.12\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/slowlog-len/\">SLOWLOG LEN</a></strong> - Returns the number of entries in the slow log.</summary>\n\n**Syntax:** `SLOWLOG LEN`\n\n**Description:** Returns the number of entries in the slow log.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.12\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/slowlog-reset/\">SLOWLOG RESET</a></strong> - Clears all entries from the slow log.</summary>\n\n**Syntax:** `SLOWLOG RESET`\n\n**Description:** Clears all entries from the slow log.\n\n**Complexity:** O(N) where N is the number of entries in the slowlog\n\n**Since:** 2.2.12\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/swapdb/\">SWAPDB</a></strong> - Swaps two Redis databases.</summary>\n\n**Syntax:** `SWAPDB index1 index2`\n\n**Description:** Swaps two Redis databases.\n\n**Complexity:** O(N) where N is the count of clients watching or blocking on keys from both databases.\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sync/\">SYNC</a></strong> - An internal command used in replication.</summary>\n\n**Syntax:** `SYNC`\n\n**Description:** An internal command used in replication.\n\n**Complexity:** \n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/time/\">TIME</a></strong> - Returns the server time.</summary>\n\n**Syntax:** `TIME`\n\n**Description:** Returns the server time.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>"},{"id":"cluster-commands","title":"Cluster commands","role":"content","text":"Cluster commands manage Redis Cluster operations.\n\n<details>\n<summary><strong><a href=\"/commands/asking/\">ASKING</a></strong> - Signals that a cluster client is following an -ASK redirect.</summary>\n\n**Syntax:** `ASKING`\n\n**Description:** Signals that a cluster client is following an -ASK redirect.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-addslots/\">CLUSTER ADDSLOTS</a></strong> - Assigns new hash slots to a node.</summary>\n\n**Syntax:** `CLUSTER ADDSLOTS slot [slot ...]`\n\n**Description:** Assigns new hash slots to a node.\n\n**Complexity:** O(N) where N is the total number of hash slot arguments\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-bumpepoch/\">CLUSTER BUMPEPOCH</a></strong> - Advances the cluster config epoch.</summary>\n\n**Syntax:** `CLUSTER BUMPEPOCH`\n\n**Description:** Advances the cluster config epoch.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-count-failure-reports/\">CLUSTER COUNT-FAILURE-REPORTS</a></strong> - Returns the number of active failure reports active for a node.</summary>\n\n**Syntax:** `CLUSTER COUNT-FAILURE-REPORTS node-id`\n\n**Description:** Returns the number of active failure reports active for a node.\n\n**Complexity:** O(N) where N is the number of failure reports\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-countkeysinslot/\">CLUSTER COUNTKEYSINSLOT</a></strong> - Returns the number of keys in a hash slot.</summary>\n\n**Syntax:** `CLUSTER COUNTKEYSINSLOT slot`\n\n**Description:** Returns the number of keys in a hash slot.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-delslots/\">CLUSTER DELSLOTS</a></strong> - Sets hash slots as unbound for a node.</summary>\n\n**Syntax:** `CLUSTER DELSLOTS slot [slot ...]`\n\n**Description:** Sets hash slots as unbound for a node.\n\n**Complexity:** O(N) where N is the total number of hash slot arguments\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-failover/\">CLUSTER FAILOVER</a></strong> - Forces a replica to perform a manual failover of its master.</summary>\n\n**Syntax:** `CLUSTER FAILOVER [FORCE | TAKEOVER]`\n\n**Description:** Forces a replica to perform a manual failover of its master.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-flushslots/\">CLUSTER FLUSHSLOTS</a></strong> - Deletes all slots information from a node.</summary>\n\n**Syntax:** `CLUSTER FLUSHSLOTS`\n\n**Description:** Deletes all slots information from a node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-forget/\">CLUSTER FORGET</a></strong> - Removes a node from the nodes table.</summary>\n\n**Syntax:** `CLUSTER FORGET node-id`\n\n**Description:** Removes a node from the nodes table.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-getkeysinslot/\">CLUSTER GETKEYSINSLOT</a></strong> - Returns the key names in a hash slot.</summary>\n\n**Syntax:** `CLUSTER GETKEYSINSLOT slot count`\n\n**Description:** Returns the key names in a hash slot.\n\n**Complexity:** O(N) where N is the number of requested keys\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-info/\">CLUSTER INFO</a></strong> - Returns information about the state of a node.</summary>\n\n**Syntax:** `CLUSTER INFO`\n\n**Description:** Returns information about the state of a node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-keyslot/\">CLUSTER KEYSLOT</a></strong> - Returns the hash slot for a key.</summary>\n\n**Syntax:** `CLUSTER KEYSLOT key`\n\n**Description:** Returns the hash slot for a key.\n\n**Complexity:** O(N) where N is the number of bytes in the key\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-meet/\">CLUSTER MEET</a></strong> - Forces a node to handshake with another node.</summary>\n\n**Syntax:** `CLUSTER MEET ip port [cluster-bus-port]`\n\n**Description:** Forces a node to handshake with another node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-myid/\">CLUSTER MYID</a></strong> - Returns the ID of a node.</summary>\n\n**Syntax:** `CLUSTER MYID`\n\n**Description:** Returns the ID of a node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-nodes/\">CLUSTER NODES</a></strong> - Returns the cluster configuration for a node.</summary>\n\n**Syntax:** `CLUSTER NODES`\n\n**Description:** Returns the cluster configuration for a node.\n\n**Complexity:** O(N) where N is the total number of Cluster nodes\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-replicas/\">CLUSTER REPLICAS</a></strong> - Lists the replica nodes of a master node.</summary>\n\n**Syntax:** `CLUSTER REPLICAS node-id`\n\n**Description:** Lists the replica nodes of a master node.\n\n**Complexity:** O(N) where N is the number of replicas.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-replicate/\">CLUSTER REPLICATE</a></strong> - Configure a node as replica of a master node.</summary>\n\n**Syntax:** `CLUSTER REPLICATE node-id`\n\n**Description:** Configure a node as replica of a master node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-reset/\">CLUSTER RESET</a></strong> - Resets a node.</summary>\n\n**Syntax:** `CLUSTER RESET [HARD | SOFT]`\n\n**Description:** Resets a node.\n\n**Complexity:** O(N) where N is the number of known nodes. The command may execute a FLUSHALL as a side effect.\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-saveconfig/\">CLUSTER SAVECONFIG</a></strong> - Forces a node to save the cluster configuration to disk.</summary>\n\n**Syntax:** `CLUSTER SAVECONFIG`\n\n**Description:** Forces a node to save the cluster configuration to disk.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-set-config-epoch/\">CLUSTER SET-CONFIG-EPOCH</a></strong> - Sets the configuration epoch for a new node.</summary>\n\n**Syntax:** `CLUSTER SET-CONFIG-EPOCH config-epoch`\n\n**Description:** Sets the configuration epoch for a new node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-setslot/\">CLUSTER SETSLOT</a></strong> - Binds a hash slot to a node.</summary>\n\n**Syntax:** `CLUSTER SETSLOT slot <IMPORTING node-id | MIGRATING node-id |\n  NODE node-id | STABLE>`\n\n**Description:** Binds a hash slot to a node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-slaves/\">CLUSTER SLAVES</a></strong> - Lists the replica nodes of a master node.</summary>\n\n**Syntax:** `CLUSTER SLAVES node-id`\n\n**Description:** Lists the replica nodes of a master node.\n\n**Complexity:** O(N) where N is the number of replicas.\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-slots/\">CLUSTER SLOTS</a></strong> - Returns the mapping of cluster slots to nodes.</summary>\n\n**Syntax:** `CLUSTER SLOTS`\n\n**Description:** Returns the mapping of cluster slots to nodes.\n\n**Complexity:** O(N) where N is the total number of Cluster nodes\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/readonly/\">READONLY</a></strong> - Enables read-only queries for a connection to a Redis Cluster replica node.</summary>\n\n**Syntax:** `READONLY`\n\n**Description:** Enables read-only queries for a connection to a Redis Cluster replica node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/readwrite/\">READWRITE</a></strong> - Enables read-write queries for a connection to a Reids Cluster replica node.</summary>\n\n**Syntax:** `READWRITE`\n\n**Description:** Enables read-write queries for a connection to a Reids Cluster replica node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>"},{"id":"generic-commands","title":"Generic commands","role":"content","text":"Generic commands work across all data types.\n\n<details>\n<summary><strong><a href=\"/commands/copy/\">COPY</a></strong> - Copies the value of a key to a new key. <span style=\"color: #e74c3c;\">⭐ New in 6.2</span></summary>\n\n**Syntax:** `COPY source destination [DB destination-db] [REPLACE]`\n\n**Description:** Copies the value of a key to a new key.\n\n**Complexity:** O(N) worst case for collections, where N is the number of nested items. O(1) for string values.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/del/\">DEL</a></strong> - Deletes one or more keys.</summary>\n\n**Syntax:** `DEL key [key ...]`\n\n**Description:** Deletes one or more keys.\n\n**Complexity:** O(N) where N is the number of keys that will be removed. When a key to remove holds a value other than a string, the individual complexity for this key is O(M) where M is the number of elements in the list, set, sorted set or hash. Removing a single key that holds a string value is O(1).\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/dump/\">DUMP</a></strong> - Returns a serialized representation of the value stored at a key.</summary>\n\n**Syntax:** `DUMP key`\n\n**Description:** Returns a serialized representation of the value stored at a key.\n\n**Complexity:** O(1) to access the key and additional O(N*M) to serialize it, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1).\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/exists/\">EXISTS</a></strong> - Determines whether one or more keys exist.</summary>\n\n**Syntax:** `EXISTS key [key ...]`\n\n**Description:** Determines whether one or more keys exist.\n\n**Complexity:** O(N) where N is the number of keys to check.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/expire/\">EXPIRE</a></strong> - Sets the expiration time of a key in seconds.</summary>\n\n**Syntax:** `EXPIRE key seconds [NX | XX | GT | LT]`\n\n**Description:** Sets the expiration time of a key in seconds.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/expireat/\">EXPIREAT</a></strong> - Sets the expiration time of a key to a Unix timestamp.</summary>\n\n**Syntax:** `EXPIREAT key unix-time-seconds [NX | XX | GT | LT]`\n\n**Description:** Sets the expiration time of a key to a Unix timestamp.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/keys/\">KEYS</a></strong> - Returns all key names that match a pattern.</summary>\n\n**Syntax:** `KEYS pattern`\n\n**Description:** Returns all key names that match a pattern.\n\n**Complexity:** O(N) with N being the number of keys in the database, under the assumption that the key names in the database and the given pattern have limited length.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/migrate/\">MIGRATE</a></strong> - Atomically transfers a key from one Redis instance to another.</summary>\n\n**Syntax:** `MIGRATE host port <key | \"\"> destination-db timeout [COPY] [REPLACE]\n  [AUTH password | AUTH2 username password] [KEYS key [key ...]]`\n\n**Description:** Atomically transfers a key from one Redis instance to another.\n\n**Complexity:** This command actually executes a DUMP+DEL in the source instance, and a RESTORE in the target instance. See the pages of these commands for time complexity. Also an O(N) data transfer between the two instances is performed.\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/move/\">MOVE</a></strong> - Moves a key to another database.</summary>\n\n**Syntax:** `MOVE key db`\n\n**Description:** Moves a key to another database.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/object-encoding/\">OBJECT ENCODING</a></strong> - Returns the internal encoding of a Redis object.</summary>\n\n**Syntax:** `OBJECT ENCODING key`\n\n**Description:** Returns the internal encoding of a Redis object.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.3\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/object-freq/\">OBJECT FREQ</a></strong> - Returns the logarithmic access frequency counter of a Redis object.</summary>\n\n**Syntax:** `OBJECT FREQ key`\n\n**Description:** Returns the logarithmic access frequency counter of a Redis object.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/object-idletime/\">OBJECT IDLETIME</a></strong> - Returns the time since the last access to a Redis object.</summary>\n\n**Syntax:** `OBJECT IDLETIME key`\n\n**Description:** Returns the time since the last access to a Redis object.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.3\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/object-refcount/\">OBJECT REFCOUNT</a></strong> - Returns the reference count of a value of a key.</summary>\n\n**Syntax:** `OBJECT REFCOUNT key`\n\n**Description:** Returns the reference count of a value of a key.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.3\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/persist/\">PERSIST</a></strong> - Removes the expiration time of a key.</summary>\n\n**Syntax:** `PERSIST key`\n\n**Description:** Removes the expiration time of a key.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pexpire/\">PEXPIRE</a></strong> - Sets the expiration time of a key in milliseconds.</summary>\n\n**Syntax:** `PEXPIRE key milliseconds [NX | XX | GT | LT]`\n\n**Description:** Sets the expiration time of a key in milliseconds.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pexpireat/\">PEXPIREAT</a></strong> - Sets the expiration time of a key to a Unix milliseconds timestamp.</summary>\n\n**Syntax:** `PEXPIREAT key unix-time-milliseconds [NX | XX | GT | LT]`\n\n**Description:** Sets the expiration time of a key to a Unix milliseconds timestamp.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pttl/\">PTTL</a></strong> - Returns the expiration time in milliseconds of a key.</summary>\n\n**Syntax:** `PTTL key`\n\n**Description:** Returns the expiration time in milliseconds of a key.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/randomkey/\">RANDOMKEY</a></strong> - Returns a random key name from the database.</summary>\n\n**Syntax:** `RANDOMKEY`\n\n**Description:** Returns a random key name from the database.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rename/\">RENAME</a></strong> - Renames a key and overwrites the destination.</summary>\n\n**Syntax:** `RENAME key newkey`\n\n**Description:** Renames a key and overwrites the destination.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/renamenx/\">RENAMENX</a></strong> - Renames a key only when the target key name doesn't exist.</summary>\n\n**Syntax:** `RENAMENX key newkey`\n\n**Description:** Renames a key only when the target key name doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/restore/\">RESTORE</a></strong> - Creates a key from the serialized representation of a value.</summary>\n\n**Syntax:** `RESTORE key ttl serialized-value [REPLACE] [ABSTTL]\n  [IDLETIME seconds] [FREQ frequency]`\n\n**Description:** Creates a key from the serialized representation of a value.\n\n**Complexity:** O(1) to create the new key and additional O(N*M) to reconstruct the serialized value, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1). However for sorted set values the complexity is O(N*M*log(N)) because inserting values into sorted sets is O(log(N)).\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-6-2-commands/\">Redis 6.2 Commands Reference</a></strong> - Complete list of all Redis commands available in version 6.2, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 6.2, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-7-2-commands/\">Redis 7.2 Commands Reference</a></strong> - Complete list of all Redis commands available in version 7.2, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 7.2, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-7-4-commands/\">Redis 7.4 Commands Reference</a></strong> - Complete list of all Redis commands available in version 7.4, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 7.4, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-8-0-commands/\">Redis 8.0 Commands Reference</a></strong> - Complete list of all Redis commands available in version 8.0, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 8.0, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-8-2-commands/\">Redis 8.2 Commands Reference</a></strong> - Complete list of all Redis commands available in version 8.2, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 8.2, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-8-4-commands/\">Redis 8.4 Commands Reference</a></strong> - Complete list of all Redis commands available in version 8.4, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 8.4, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/scan/\">SCAN</a></strong> - Iterates over the key names in the database.</summary>\n\n**Syntax:** `SCAN cursor [MATCH pattern] [COUNT count] [TYPE type]`\n\n**Description:** Iterates over the key names in the database.\n\n**Complexity:** O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sort/\">SORT</a></strong> - Sorts the elements in a list, a set, or a sorted set, optionally storing the result.</summary>\n\n**Syntax:** `SORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern\n  ...]] [ASC | DESC] [ALPHA] [STORE destination]`\n\n**Description:** Sorts the elements in a list, a set, or a sorted set, optionally storing the result.\n\n**Complexity:** O(N+M*log(M)) where N is the number of elements in the list or set to sort, and M the number of returned elements. When the elements are not sorted, complexity is O(N).\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/touch/\">TOUCH</a></strong> - Returns the number of existing keys out of those specified after updating the time they were last accessed.</summary>\n\n**Syntax:** `TOUCH key [key ...]`\n\n**Description:** Returns the number of existing keys out of those specified after updating the time they were last accessed.\n\n**Complexity:** O(N) where N is the number of keys that will be touched.\n\n**Since:** 3.2.1\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ttl/\">TTL</a></strong> - Returns the expiration time in seconds of a key.</summary>\n\n**Syntax:** `TTL key`\n\n**Description:** Returns the expiration time in seconds of a key.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/type/\">TYPE</a></strong> - Determines the type of value stored at a key.</summary>\n\n**Syntax:** `TYPE key`\n\n**Description:** Determines the type of value stored at a key.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/unlink/\">UNLINK</a></strong> - Asynchronously deletes one or more keys.</summary>\n\n**Syntax:** `UNLINK key [key ...]`\n\n**Description:** Asynchronously deletes one or more keys.\n\n**Complexity:** O(1) for each key removed regardless of its size. Then the command does O(N) work in a different thread in order to reclaim memory, where N is the number of allocations the deleted objects where composed of.\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/wait/\">WAIT</a></strong> - Blocks until the asynchronous replication of all preceding write commands sent by the connection is completed.</summary>\n\n**Syntax:** `WAIT numreplicas timeout`\n\n**Description:** Blocks until the asynchronous replication of all preceding write commands sent by the connection is completed.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>"}],"examples":[]}
{"schema_version":2,"id":"commands/redis-7-2-commands","title":"Redis 7.2 Commands Reference","url":"https://redis.io/docs/latest/commands/redis-7-2-commands/","summary":"Complete list of all Redis commands available in version 7.2, organized by functional group","tags":["docs","develop","stack","oss","rs","rc","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"3cff8e22487803d94fc8a7c5222d7e11f2c86bd6d8cfafadf6ef7896166c8d8f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This page provides a comprehensive reference of all Redis commands available in Redis 7.2, organized by functional group. Each command includes its description and syntax in a collapsible section for easy navigation.\n\n> [!NOTE]\n> Redis 7.2 includes all commands from previous versions plus new commands introduced in 7.2. Commands marked with **⭐ New in 7.2** were added in this release.\n>\n> Command page links take you to the *current implementation* of each command."},{"id":"quick-navigation","title":"Quick Navigation","role":"content","text":"- [String commands](#string-commands)\n- [Hash commands](#hash-commands)\n- [List commands](#list-commands)\n- [Set commands](#set-commands)\n- [Sorted set commands](#sorted-set-commands)\n- [Stream commands](#stream-commands)\n- [Bitmap commands](#bitmap-commands)\n- [HyperLogLog commands](#hyperloglog-commands)\n- [Geospatial commands](#geospatial-commands)\n- [JSON commands](#json-commands)\n- [Search commands](#search-commands)\n- [Time series commands](#time-series-commands)\n- [Pub/Sub commands](#pubsub-commands)\n- [Transaction commands](#transaction-commands)\n- [Scripting commands](#scripting-commands)\n- [Connection commands](#connection-commands)\n- [Server commands](#server-commands)\n- [Cluster commands](#cluster-commands)\n- [Generic commands](#generic-commands)\n\n---"},{"id":"string-commands","title":"String commands","role":"content","text":"String commands operate on string values, the most basic Redis data type.\n\n<details>\n<summary><strong><a href=\"/commands/append/\">APPEND</a></strong> - Appends a string to the value of a key. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `APPEND key value`\n\n**Description:** Appends a string to the value of a key. Creates the key if it doesn't exist.\n\n**Complexity:** O(1). The amortized time complexity is O(1) assuming the appended value is small and the already present value is of any size, since the dynamic string library used by Redis will double the free space available on every reallocation.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/decr/\">DECR</a></strong> - Decrements the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `DECR key`\n\n**Description:** Decrements the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/decrby/\">DECRBY</a></strong> - Decrements a number from the integer value of a key. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `DECRBY key decrement`\n\n**Description:** Decrements a number from the integer value of a key. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/get/\">GET</a></strong> - Returns the string value of a key.</summary>\n\n**Syntax:** `GET key`\n\n**Description:** Returns the string value of a key.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getdel/\">GETDEL</a></strong> - Returns the string value of a key after deleting the key.</summary>\n\n**Syntax:** `GETDEL key`\n\n**Description:** Returns the string value of a key after deleting the key.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getex/\">GETEX</a></strong> - Returns the string value of a key after setting its expiration time.</summary>\n\n**Syntax:** `GETEX key [EX seconds | PX milliseconds | EXAT unix-time-seconds |\n  PXAT unix-time-milliseconds | PERSIST]`\n\n**Description:** Returns the string value of a key after setting its expiration time.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getrange/\">GETRANGE</a></strong> - Returns a substring of the string stored at a key.</summary>\n\n**Syntax:** `GETRANGE key start end`\n\n**Description:** Returns a substring of the string stored at a key.\n\n**Complexity:** O(N) where N is the length of the returned string. The complexity is ultimately determined by the returned length, but because creating a substring from an existing string is very cheap, it can be considered O(1) for small strings.\n\n**Since:** 2.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getset/\">GETSET</a></strong> - Returns the previous string value of a key after setting it to a new value.</summary>\n\n**Syntax:** `GETSET key value`\n\n**Description:** Returns the previous string value of a key after setting it to a new value.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/incr/\">INCR</a></strong> - Increments the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `INCR key`\n\n**Description:** Increments the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/incrby/\">INCRBY</a></strong> - Increments the integer value of a key by a number. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `INCRBY key increment`\n\n**Description:** Increments the integer value of a key by a number. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/incrbyfloat/\">INCRBYFLOAT</a></strong> - Increment the floating point value of a key by a number. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `INCRBYFLOAT key increment`\n\n**Description:** Increment the floating point value of a key by a number. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lcs/\">LCS</a></strong> - Finds the longest common substring.</summary>\n\n**Syntax:** `LCS key1 key2 [LEN] [IDX] [MINMATCHLEN min-match-len] [WITHMATCHLEN]`\n\n**Description:** Finds the longest common substring.\n\n**Complexity:** O(N*M) where N and M are the lengths of s1 and s2, respectively\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/mget/\">MGET</a></strong> - Atomically returns the string values of one or more keys.</summary>\n\n**Syntax:** `MGET key [key ...]`\n\n**Description:** Atomically returns the string values of one or more keys.\n\n**Complexity:** O(N) where N is the number of keys to retrieve.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/mset/\">MSET</a></strong> - Atomically creates or modifies the string values of one or more keys.</summary>\n\n**Syntax:** `MSET key value [key value ...]`\n\n**Description:** Atomically creates or modifies the string values of one or more keys.\n\n**Complexity:** O(N) where N is the number of keys to set.\n\n**Since:** 1.0.1\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/msetnx/\">MSETNX</a></strong> - Atomically modifies the string values of one or more keys only when all keys don't exist.</summary>\n\n**Syntax:** `MSETNX key value [key value ...]`\n\n**Description:** Atomically modifies the string values of one or more keys only when all keys don't exist.\n\n**Complexity:** O(N) where N is the number of keys to set.\n\n**Since:** 1.0.1\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/psetex/\">PSETEX</a></strong> - Sets both string value and expiration time in milliseconds of a key. The key is created if it doesn't exist.</summary>\n\n**Syntax:** `PSETEX key milliseconds value`\n\n**Description:** Sets both string value and expiration time in milliseconds of a key. The key is created if it doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/set/\">SET</a></strong> - Sets the string value of a key, ignoring its type. The key is created if it doesn't exist.</summary>\n\n**Syntax:** `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]`\n\n**Description:** Sets the string value of a key, ignoring its type. The key is created if it doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/setex/\">SETEX</a></strong> - Sets the string value and expiration time of a key. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `SETEX key seconds value`\n\n**Description:** Sets the string value and expiration time of a key. Creates the key if it doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/setnx/\">SETNX</a></strong> - Set the string value of a key only when the key doesn't exist.</summary>\n\n**Syntax:** `SETNX key value`\n\n**Description:** Set the string value of a key only when the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/setrange/\">SETRANGE</a></strong> - Overwrites a part of a string value with another by an offset. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `SETRANGE key offset value`\n\n**Description:** Overwrites a part of a string value with another by an offset. Creates the key if it doesn't exist.\n\n**Complexity:** O(1), not counting the time taken to copy the new string in place. Usually, this string is very small so the amortized complexity is O(1). Otherwise, complexity is O(M) with M being the length of the value argument.\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/strlen/\">STRLEN</a></strong> - Returns the length of a string value.</summary>\n\n**Syntax:** `STRLEN key`\n\n**Description:** Returns the length of a string value.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/substr/\">SUBSTR</a></strong> - Returns a substring from a string value.</summary>\n\n**Syntax:** `SUBSTR key start end`\n\n**Description:** Returns a substring from a string value.\n\n**Complexity:** O(N) where N is the length of the returned string. The complexity is ultimately determined by the returned length, but because creating a substring from an existing string is very cheap, it can be considered O(1) for small strings.\n\n**Since:** 1.0.0\n\n</details>"},{"id":"hash-commands","title":"Hash commands","role":"content","text":"Hash commands operate on hash data structures, which map string fields to string values.\n\n<details>\n<summary><strong><a href=\"/commands/hdel/\">HDEL</a></strong> - Deletes one or more fields and their values from a hash. Deletes the hash if no fields remain.</summary>\n\n**Syntax:** `HDEL key field [field ...]`\n\n**Description:** Deletes one or more fields and their values from a hash. Deletes the hash if no fields remain.\n\n**Complexity:** O(N) where N is the number of fields to be removed.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hexists/\">HEXISTS</a></strong> - Determines whether a field exists in a hash.</summary>\n\n**Syntax:** `HEXISTS key field`\n\n**Description:** Determines whether a field exists in a hash.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hget/\">HGET</a></strong> - Returns the value of a field in a hash.</summary>\n\n**Syntax:** `HGET key field`\n\n**Description:** Returns the value of a field in a hash.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hgetall/\">HGETALL</a></strong> - Returns all fields and values in a hash.</summary>\n\n**Syntax:** `HGETALL key`\n\n**Description:** Returns all fields and values in a hash.\n\n**Complexity:** O(N) where N is the size of the hash.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hincrby/\">HINCRBY</a></strong> - Increments the integer value of a field in a hash by a number. Uses 0 as initial value if the field doesn't exist.</summary>\n\n**Syntax:** `HINCRBY key field increment`\n\n**Description:** Increments the integer value of a field in a hash by a number. Uses 0 as initial value if the field doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hincrbyfloat/\">HINCRBYFLOAT</a></strong> - Increments the floating point value of a field by a number. Uses 0 as initial value if the field doesn't exist.</summary>\n\n**Syntax:** `HINCRBYFLOAT key field increment`\n\n**Description:** Increments the floating point value of a field by a number. Uses 0 as initial value if the field doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hkeys/\">HKEYS</a></strong> - Returns all fields in a hash.</summary>\n\n**Syntax:** `HKEYS key`\n\n**Description:** Returns all fields in a hash.\n\n**Complexity:** O(N) where N is the size of the hash.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hlen/\">HLEN</a></strong> - Returns the number of fields in a hash.</summary>\n\n**Syntax:** `HLEN key`\n\n**Description:** Returns the number of fields in a hash.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hmget/\">HMGET</a></strong> - Returns the values of all fields in a hash.</summary>\n\n**Syntax:** `HMGET key field [field ...]`\n\n**Description:** Returns the values of all fields in a hash.\n\n**Complexity:** O(N) where N is the number of fields being requested.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hmset/\">HMSET</a></strong> - Sets the values of multiple fields.</summary>\n\n**Syntax:** `HMSET key field value [field value ...]`\n\n**Description:** Sets the values of multiple fields.\n\n**Complexity:** O(N) where N is the number of fields being set.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hrandfield/\">HRANDFIELD</a></strong> - Returns one or more random fields from a hash.</summary>\n\n**Syntax:** `HRANDFIELD key [count [WITHVALUES]]`\n\n**Description:** Returns one or more random fields from a hash.\n\n**Complexity:** O(N) where N is the number of fields returned\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hscan/\">HSCAN</a></strong> - Iterates over fields and values of a hash.</summary>\n\n**Syntax:** `HSCAN key cursor [MATCH pattern] [COUNT count] [NOVALUES]`\n\n**Description:** Iterates over fields and values of a hash.\n\n**Complexity:** O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hset/\">HSET</a></strong> - Creates or modifies the value of a field in a hash.</summary>\n\n**Syntax:** `HSET key field value [field value ...]`\n\n**Description:** Creates or modifies the value of a field in a hash.\n\n**Complexity:** O(1) for each field/value pair added, so O(N) to add N field/value pairs when the command is called with multiple field/value pairs.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hsetnx/\">HSETNX</a></strong> - Sets the value of a field in a hash only when the field doesn't exist.</summary>\n\n**Syntax:** `HSETNX key field value`\n\n**Description:** Sets the value of a field in a hash only when the field doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hstrlen/\">HSTRLEN</a></strong> - Returns the length of the value of a field.</summary>\n\n**Syntax:** `HSTRLEN key field`\n\n**Description:** Returns the length of the value of a field.\n\n**Complexity:** O(1)\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hvals/\">HVALS</a></strong> - Returns all values in a hash.</summary>\n\n**Syntax:** `HVALS key`\n\n**Description:** Returns all values in a hash.\n\n**Complexity:** O(N) where N is the size of the hash.\n\n**Since:** 2.0.0\n\n</details>"},{"id":"list-commands","title":"List commands","role":"content","text":"List commands operate on lists of strings, ordered by insertion order.\n\n<details>\n<summary><strong><a href=\"/commands/blmove/\">BLMOVE</a></strong> - Pops an element from a list, pushes it to another list and returns it. Blocks until an element is available otherwise. Deletes the list if the last element was moved.</summary>\n\n**Syntax:** `BLMOVE source destination <LEFT | RIGHT> <LEFT | RIGHT> timeout`\n\n**Description:** Pops an element from a list, pushes it to another list and returns it. Blocks until an element is available otherwise. Deletes the list if the last element was moved.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/blmpop/\">BLMPOP</a></strong> - Pops the first element from one of multiple lists. Blocks until an element is available otherwise. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `BLMPOP timeout numkeys key [key ...] <LEFT | RIGHT> [COUNT count]`\n\n**Description:** Pops the first element from one of multiple lists. Blocks until an element is available otherwise. Deletes the list if the last element was popped.\n\n**Complexity:** O(N+M) where N is the number of provided keys and M is the number of elements returned.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/blpop/\">BLPOP</a></strong> - Removes and returns the first element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `BLPOP key [key ...] timeout`\n\n**Description:** Removes and returns the first element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.\n\n**Complexity:** O(N) where N is the number of provided keys.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/brpop/\">BRPOP</a></strong> - Removes and returns the last element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `BRPOP key [key ...] timeout`\n\n**Description:** Removes and returns the last element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.\n\n**Complexity:** O(N) where N is the number of provided keys.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/brpoplpush/\">BRPOPLPUSH</a></strong> - Pops an element from a list, pushes it to another list and returns it. Block until an element is available otherwise. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `BRPOPLPUSH source destination timeout`\n\n**Description:** Pops an element from a list, pushes it to another list and returns it. Block until an element is available otherwise. Deletes the list if the last element was popped.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lindex/\">LINDEX</a></strong> - Returns an element from a list by its index.</summary>\n\n**Syntax:** `LINDEX key index`\n\n**Description:** Returns an element from a list by its index.\n\n**Complexity:** O(N) where N is the number of elements to traverse to get to the element at index. This makes asking for the first or the last element of the list O(1).\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/linsert/\">LINSERT</a></strong> - Inserts an element before or after another element in a list.</summary>\n\n**Syntax:** `LINSERT key <BEFORE | AFTER> pivot element`\n\n**Description:** Inserts an element before or after another element in a list.\n\n**Complexity:** O(N) where N is the number of elements to traverse before seeing the value pivot. This means that inserting somewhere on the left end on the list (head) can be considered O(1) and inserting somewhere on the right end (tail) is O(N).\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/llen/\">LLEN</a></strong> - Returns the length of a list.</summary>\n\n**Syntax:** `LLEN key`\n\n**Description:** Returns the length of a list.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lmove/\">LMOVE</a></strong> - Returns an element after popping it from one list and pushing it to another. Deletes the list if the last element was moved.</summary>\n\n**Syntax:** `LMOVE source destination <LEFT | RIGHT> <LEFT | RIGHT>`\n\n**Description:** Returns an element after popping it from one list and pushing it to another. Deletes the list if the last element was moved.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lmpop/\">LMPOP</a></strong> - Returns multiple elements from a list after removing them. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `LMPOP numkeys key [key ...] <LEFT | RIGHT> [COUNT count]`\n\n**Description:** Returns multiple elements from a list after removing them. Deletes the list if the last element was popped.\n\n**Complexity:** O(N+M) where N is the number of provided keys and M is the number of elements returned.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lpop/\">LPOP</a></strong> - Returns the first elements in a list after removing it. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `LPOP key [count]`\n\n**Description:** Returns the first elements in a list after removing it. Deletes the list if the last element was popped.\n\n**Complexity:** O(N) where N is the number of elements returned\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lpos/\">LPOS</a></strong> - Returns the index of matching elements in a list.</summary>\n\n**Syntax:** `LPOS key element [RANK rank] [COUNT num-matches] [MAXLEN len]`\n\n**Description:** Returns the index of matching elements in a list.\n\n**Complexity:** O(N) where N is the number of elements in the list, for the average case. When searching for elements near the head or the tail of the list, or when the MAXLEN option is provided, the command may run in constant time.\n\n**Since:** 6.0.6\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lpush/\">LPUSH</a></strong> - Prepends one or more elements to a list. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `LPUSH key element [element ...]`\n\n**Description:** Prepends one or more elements to a list. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lpushx/\">LPUSHX</a></strong> - Prepends one or more elements to a list only when the list exists.</summary>\n\n**Syntax:** `LPUSHX key element [element ...]`\n\n**Description:** Prepends one or more elements to a list only when the list exists.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lrange/\">LRANGE</a></strong> - Returns a range of elements from a list.</summary>\n\n**Syntax:** `LRANGE key start stop`\n\n**Description:** Returns a range of elements from a list.\n\n**Complexity:** O(S+N) where S is the distance of start offset from HEAD for small lists, from nearest end (HEAD or TAIL) for large lists; and N is the number of elements in the specified range.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lrem/\">LREM</a></strong> - Removes elements from a list. Deletes the list if the last element was removed.</summary>\n\n**Syntax:** `LREM key count element`\n\n**Description:** Removes elements from a list. Deletes the list if the last element was removed.\n\n**Complexity:** O(N+M) where N is the length of the list and M is the number of elements removed.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lset/\">LSET</a></strong> - Sets the value of an element in a list by its index.</summary>\n\n**Syntax:** `LSET key index element`\n\n**Description:** Sets the value of an element in a list by its index.\n\n**Complexity:** O(N) where N is the length of the list. Setting either the first or the last element of the list is O(1).\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ltrim/\">LTRIM</a></strong> - Removes elements from both ends a list. Deletes the list if all elements were trimmed.</summary>\n\n**Syntax:** `LTRIM key start stop`\n\n**Description:** Removes elements from both ends a list. Deletes the list if all elements were trimmed.\n\n**Complexity:** O(N) where N is the number of elements to be removed by the operation.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rpop/\">RPOP</a></strong> - Returns and removes the last elements of a list. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `RPOP key [count]`\n\n**Description:** Returns and removes the last elements of a list. Deletes the list if the last element was popped.\n\n**Complexity:** O(N) where N is the number of elements returned\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rpoplpush/\">RPOPLPUSH</a></strong> - Returns the last element of a list after removing and pushing it to another list. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `RPOPLPUSH source destination`\n\n**Description:** Returns the last element of a list after removing and pushing it to another list. Deletes the list if the last element was popped.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rpush/\">RPUSH</a></strong> - Appends one or more elements to a list. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `RPUSH key element [element ...]`\n\n**Description:** Appends one or more elements to a list. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rpushx/\">RPUSHX</a></strong> - Appends an element to a list only when the list exists.</summary>\n\n**Syntax:** `RPUSHX key element [element ...]`\n\n**Description:** Appends an element to a list only when the list exists.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 2.2.0\n\n</details>"},{"id":"set-commands","title":"Set commands","role":"content","text":"Set commands operate on unordered collections of unique strings.\n\n<details>\n<summary><strong><a href=\"/commands/sadd/\">SADD</a></strong> - Adds one or more members to a set. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `SADD key member [member ...]`\n\n**Description:** Adds one or more members to a set. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/scard/\">SCARD</a></strong> - Returns the number of members in a set.</summary>\n\n**Syntax:** `SCARD key`\n\n**Description:** Returns the number of members in a set.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sdiff/\">SDIFF</a></strong> - Returns the difference of multiple sets.</summary>\n\n**Syntax:** `SDIFF key [key ...]`\n\n**Description:** Returns the difference of multiple sets.\n\n**Complexity:** O(N) where N is the total number of elements in all given sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sdiffstore/\">SDIFFSTORE</a></strong> - Stores the difference of multiple sets in a key.</summary>\n\n**Syntax:** `SDIFFSTORE destination key [key ...]`\n\n**Description:** Stores the difference of multiple sets in a key.\n\n**Complexity:** O(N) where N is the total number of elements in all given sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sinter/\">SINTER</a></strong> - Returns the intersect of multiple sets.</summary>\n\n**Syntax:** `SINTER key [key ...]`\n\n**Description:** Returns the intersect of multiple sets.\n\n**Complexity:** O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sintercard/\">SINTERCARD</a></strong> - Returns the number of members of the intersect of multiple sets.</summary>\n\n**Syntax:** `SINTERCARD numkeys key [key ...] [LIMIT limit]`\n\n**Description:** Returns the number of members of the intersect of multiple sets.\n\n**Complexity:** O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sinterstore/\">SINTERSTORE</a></strong> - Stores the intersect of multiple sets in a key.</summary>\n\n**Syntax:** `SINTERSTORE destination key [key ...]`\n\n**Description:** Stores the intersect of multiple sets in a key.\n\n**Complexity:** O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sismember/\">SISMEMBER</a></strong> - Determines whether a member belongs to a set.</summary>\n\n**Syntax:** `SISMEMBER key member`\n\n**Description:** Determines whether a member belongs to a set.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/smembers/\">SMEMBERS</a></strong> - Returns all members of a set.</summary>\n\n**Syntax:** `SMEMBERS key`\n\n**Description:** Returns all members of a set.\n\n**Complexity:** O(N) where N is the set cardinality.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/smismember/\">SMISMEMBER</a></strong> - Determines whether multiple members belong to a set.</summary>\n\n**Syntax:** `SMISMEMBER key member [member ...]`\n\n**Description:** Determines whether multiple members belong to a set.\n\n**Complexity:** O(N) where N is the number of elements being checked for membership\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/smove/\">SMOVE</a></strong> - Moves a member from one set to another.</summary>\n\n**Syntax:** `SMOVE source destination member`\n\n**Description:** Moves a member from one set to another.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/spop/\">SPOP</a></strong> - Returns one or more random members from a set after removing them. Deletes the set if the last member was popped.</summary>\n\n**Syntax:** `SPOP key [count]`\n\n**Description:** Returns one or more random members from a set after removing them. Deletes the set if the last member was popped.\n\n**Complexity:** Without the count argument O(1), otherwise O(N) where N is the value of the passed count.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/srandmember/\">SRANDMEMBER</a></strong> - Get one or multiple random members from a set</summary>\n\n**Syntax:** `SRANDMEMBER key [count]`\n\n**Description:** Get one or multiple random members from a set\n\n**Complexity:** Without the count argument O(1), otherwise O(N) where N is the absolute value of the passed count.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/srem/\">SREM</a></strong> - Removes one or more members from a set. Deletes the set if the last member was removed.</summary>\n\n**Syntax:** `SREM key member [member ...]`\n\n**Description:** Removes one or more members from a set. Deletes the set if the last member was removed.\n\n**Complexity:** O(N) where N is the number of members to be removed.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sscan/\">SSCAN</a></strong> - Iterates over members of a set.</summary>\n\n**Syntax:** `SSCAN key cursor [MATCH pattern] [COUNT count]`\n\n**Description:** Iterates over members of a set.\n\n**Complexity:** O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sunion/\">SUNION</a></strong> - Returns the union of multiple sets.</summary>\n\n**Syntax:** `SUNION key [key ...]`\n\n**Description:** Returns the union of multiple sets.\n\n**Complexity:** O(N) where N is the total number of elements in all given sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sunionstore/\">SUNIONSTORE</a></strong> - Stores the union of multiple sets in a key.</summary>\n\n**Syntax:** `SUNIONSTORE destination key [key ...]`\n\n**Description:** Stores the union of multiple sets in a key.\n\n**Complexity:** O(N) where N is the total number of elements in all given sets.\n\n**Since:** 1.0.0\n\n</details>"},{"id":"sorted-set-commands","title":"Sorted set commands","role":"content","text":"Sorted set commands operate on sets of unique strings ordered by a score.\n\n<details>\n<summary><strong><a href=\"/commands/bzmpop/\">BZMPOP</a></strong> - Removes and returns a member by score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.</summary>\n\n**Syntax:** `BZMPOP timeout numkeys key [key ...] <MIN | MAX> [COUNT count]`\n\n**Description:** Removes and returns a member by score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.\n\n**Complexity:** O(K) + O(M*log(N)) where K is the number of provided keys, N being the number of elements in the sorted set, and M being the number of elements popped.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bzpopmax/\">BZPOPMAX</a></strong> - Removes and returns the member with the highest score from one or more sorted sets. Blocks until a member available otherwise.  Deletes the sorted set if the last element was popped.</summary>\n\n**Syntax:** `BZPOPMAX key [key ...] timeout`\n\n**Description:** Removes and returns the member with the highest score from one or more sorted sets. Blocks until a member available otherwise.  Deletes the sorted set if the last element was popped.\n\n**Complexity:** O(log(N)) with N being the number of elements in the sorted set.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bzpopmin/\">BZPOPMIN</a></strong> - Removes and returns the member with the lowest score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.</summary>\n\n**Syntax:** `BZPOPMIN key [key ...] timeout`\n\n**Description:** Removes and returns the member with the lowest score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.\n\n**Complexity:** O(log(N)) with N being the number of elements in the sorted set.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zadd/\">ZADD</a></strong> - Adds one or more members to a sorted set, or updates their scores. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `ZADD key [NX | XX] [GT | LT] [CH] [INCR] score member [score member\n  ...]`\n\n**Description:** Adds one or more members to a sorted set, or updates their scores. Creates the key if it doesn't exist.\n\n**Complexity:** O(log(N)) for each item added, where N is the number of elements in the sorted set.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zcard/\">ZCARD</a></strong> - Returns the number of members in a sorted set.</summary>\n\n**Syntax:** `ZCARD key`\n\n**Description:** Returns the number of members in a sorted set.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zcount/\">ZCOUNT</a></strong> - Returns the count of members in a sorted set that have scores within a range.</summary>\n\n**Syntax:** `ZCOUNT key min max`\n\n**Description:** Returns the count of members in a sorted set that have scores within a range.\n\n**Complexity:** O(log(N)) with N being the number of elements in the sorted set.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zdiff/\">ZDIFF</a></strong> - Returns the difference between multiple sorted sets.</summary>\n\n**Syntax:** `ZDIFF numkeys key [key ...] [WITHSCORES]`\n\n**Description:** Returns the difference between multiple sorted sets.\n\n**Complexity:** O(L + (N-K)log(N)) worst case where L is the total number of elements in all the sets, N is the size of the first set, and K is the size of the result set.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zdiffstore/\">ZDIFFSTORE</a></strong> - Stores the difference of multiple sorted sets in a key.</summary>\n\n**Syntax:** `ZDIFFSTORE destination numkeys key [key ...]`\n\n**Description:** Stores the difference of multiple sorted sets in a key.\n\n**Complexity:** O(L + (N-K)log(N)) worst case where L is the total number of elements in all the sets, N is the size of the first set, and K is the size of the result set.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zincrby/\">ZINCRBY</a></strong> - Increments the score of a member in a sorted set.</summary>\n\n**Syntax:** `ZINCRBY key increment member`\n\n**Description:** Increments the score of a member in a sorted set.\n\n**Complexity:** O(log(N)) where N is the number of elements in the sorted set.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zinter/\">ZINTER</a></strong> - Returns the intersect of multiple sorted sets.</summary>\n\n**Syntax:** `ZINTER numkeys key [key ...] [WEIGHTS weight [weight ...]]\n  [AGGREGATE <SUM | MIN | MAX>] [WITHSCORES]`\n\n**Description:** Returns the intersect of multiple sorted sets.\n\n**Complexity:** O(N*K)+O(M*log(M)) worst case with N being the smallest input sorted set, K being the number of input sorted sets and M being the number of elements in the resulting sorted set.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zintercard/\">ZINTERCARD</a></strong> - Returns the number of members of the intersect of multiple sorted sets.</summary>\n\n**Syntax:** `ZINTERCARD numkeys key [key ...] [LIMIT limit]`\n\n**Description:** Returns the number of members of the intersect of multiple sorted sets.\n\n**Complexity:** O(N*K) worst case with N being the smallest input sorted set, K being the number of input sorted sets.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zinterstore/\">ZINTERSTORE</a></strong> - Stores the intersect of multiple sorted sets in a key.</summary>\n\n**Syntax:** `ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight\n  [weight ...]] [AGGREGATE <SUM | MIN | MAX>]`\n\n**Description:** Stores the intersect of multiple sorted sets in a key.\n\n**Complexity:** O(N*K)+O(M*log(M)) worst case with N being the smallest input sorted set, K being the number of input sorted sets and M being the number of elements in the resulting sorted set.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zlexcount/\">ZLEXCOUNT</a></strong> - Returns the number of members in a sorted set within a lexicographical range.</summary>\n\n**Syntax:** `ZLEXCOUNT key min max`\n\n**Description:** Returns the number of members in a sorted set within a lexicographical range.\n\n**Complexity:** O(log(N)) with N being the number of elements in the sorted set.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zmpop/\">ZMPOP</a></strong> - Returns the highest- or lowest-scoring members from one or more sorted sets after removing them. Deletes the sorted set if the last member was popped.</summary>\n\n**Syntax:** `ZMPOP numkeys key [key ...] <MIN | MAX> [COUNT count]`\n\n**Description:** Returns the highest- or lowest-scoring members from one or more sorted sets after removing them. Deletes the sorted set if the last member was popped.\n\n**Complexity:** O(K) + O(M*log(N)) where K is the number of provided keys, N being the number of elements in the sorted set, and M being the number of elements popped.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zmscore/\">ZMSCORE</a></strong> - Returns the score of one or more members in a sorted set.</summary>\n\n**Syntax:** `ZMSCORE key member [member ...]`\n\n**Description:** Returns the score of one or more members in a sorted set.\n\n**Complexity:** O(N) where N is the number of members being requested.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zpopmax/\">ZPOPMAX</a></strong> - Returns the highest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.</summary>\n\n**Syntax:** `ZPOPMAX key [count]`\n\n**Description:** Returns the highest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.\n\n**Complexity:** O(log(N)*M) with N being the number of elements in the sorted set, and M being the number of elements popped.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zpopmin/\">ZPOPMIN</a></strong> - Returns the lowest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.</summary>\n\n**Syntax:** `ZPOPMIN key [count]`\n\n**Description:** Returns the lowest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.\n\n**Complexity:** O(log(N)*M) with N being the number of elements in the sorted set, and M being the number of elements popped.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrandmember/\">ZRANDMEMBER</a></strong> - Returns one or more random members from a sorted set.</summary>\n\n**Syntax:** `ZRANDMEMBER key [count [WITHSCORES]]`\n\n**Description:** Returns one or more random members from a sorted set.\n\n**Complexity:** O(N) where N is the number of members returned\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrange/\">ZRANGE</a></strong> - Returns members in a sorted set within a range of indexes.</summary>\n\n**Syntax:** `ZRANGE key start stop [BYSCORE | BYLEX] [REV] [LIMIT offset count]\n  [WITHSCORES]`\n\n**Description:** Returns members in a sorted set within a range of indexes.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements returned.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrangebylex/\">ZRANGEBYLEX</a></strong> - Returns members in a sorted set within a lexicographical range.</summary>\n\n**Syntax:** `ZRANGEBYLEX key min max [LIMIT offset count]`\n\n**Description:** Returns members in a sorted set within a lexicographical range.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrangebyscore/\">ZRANGEBYSCORE</a></strong> - Returns members in a sorted set within a range of scores.</summary>\n\n**Syntax:** `ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]`\n\n**Description:** Returns members in a sorted set within a range of scores.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).\n\n**Since:** 1.0.5\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrangestore/\">ZRANGESTORE</a></strong> - Stores a range of members from sorted set in a key.</summary>\n\n**Syntax:** `ZRANGESTORE dst src min max [BYSCORE | BYLEX] [REV] [LIMIT offset\n  count]`\n\n**Description:** Stores a range of members from sorted set in a key.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements stored into the destination key.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrank/\">ZRANK</a></strong> - Returns the index of a member in a sorted set ordered by ascending scores.</summary>\n\n**Syntax:** `ZRANK key member [WITHSCORE]`\n\n**Description:** Returns the index of a member in a sorted set ordered by ascending scores.\n\n**Complexity:** O(log(N))\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrem/\">ZREM</a></strong> - Removes one or more members from a sorted set. Deletes the sorted set if all members were removed.</summary>\n\n**Syntax:** `ZREM key member [member ...]`\n\n**Description:** Removes one or more members from a sorted set. Deletes the sorted set if all members were removed.\n\n**Complexity:** O(M*log(N)) with N being the number of elements in the sorted set and M the number of elements to be removed.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zremrangebylex/\">ZREMRANGEBYLEX</a></strong> - Removes members in a sorted set within a lexicographical range. Deletes the sorted set if all members were removed.</summary>\n\n**Syntax:** `ZREMRANGEBYLEX key min max`\n\n**Description:** Removes members in a sorted set within a lexicographical range. Deletes the sorted set if all members were removed.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zremrangebyrank/\">ZREMRANGEBYRANK</a></strong> - Removes members in a sorted set within a range of indexes. Deletes the sorted set if all members were removed.</summary>\n\n**Syntax:** `ZREMRANGEBYRANK key start stop`\n\n**Description:** Removes members in a sorted set within a range of indexes. Deletes the sorted set if all members were removed.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zremrangebyscore/\">ZREMRANGEBYSCORE</a></strong> - Removes members in a sorted set within a range of scores. Deletes the sorted set if all members were removed.</summary>\n\n**Syntax:** `ZREMRANGEBYSCORE key min max`\n\n**Description:** Removes members in a sorted set within a range of scores. Deletes the sorted set if all members were removed.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrevrange/\">ZREVRANGE</a></strong> - Returns members in a sorted set within a range of indexes in reverse order.</summary>\n\n**Syntax:** `ZREVRANGE key start stop [WITHSCORES]`\n\n**Description:** Returns members in a sorted set within a range of indexes in reverse order.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements returned.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrevrangebylex/\">ZREVRANGEBYLEX</a></strong> - Returns members in a sorted set within a lexicographical range in reverse order.</summary>\n\n**Syntax:** `ZREVRANGEBYLEX key max min [LIMIT offset count]`\n\n**Description:** Returns members in a sorted set within a lexicographical range in reverse order.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrevrangebyscore/\">ZREVRANGEBYSCORE</a></strong> - Returns members in a sorted set within a range of scores in reverse order.</summary>\n\n**Syntax:** `ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]`\n\n**Description:** Returns members in a sorted set within a range of scores in reverse order.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrevrank/\">ZREVRANK</a></strong> - Returns the index of a member in a sorted set ordered by descending scores.</summary>\n\n**Syntax:** `ZREVRANK key member [WITHSCORE]`\n\n**Description:** Returns the index of a member in a sorted set ordered by descending scores.\n\n**Complexity:** O(log(N))\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zscan/\">ZSCAN</a></strong> - Iterates over members and scores of a sorted set.</summary>\n\n**Syntax:** `ZSCAN key cursor [MATCH pattern] [COUNT count]`\n\n**Description:** Iterates over members and scores of a sorted set.\n\n**Complexity:** O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zscore/\">ZSCORE</a></strong> - Returns the score of a member in a sorted set.</summary>\n\n**Syntax:** `ZSCORE key member`\n\n**Description:** Returns the score of a member in a sorted set.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zunion/\">ZUNION</a></strong> - Returns the union of multiple sorted sets.</summary>\n\n**Syntax:** `ZUNION numkeys key [key ...] [WEIGHTS weight [weight ...]]\n  [AGGREGATE <SUM | MIN | MAX>] [WITHSCORES]`\n\n**Description:** Returns the union of multiple sorted sets.\n\n**Complexity:** O(N)+O(M*log(M)) with N being the sum of the sizes of the input sorted sets, and M being the number of elements in the resulting sorted set.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zunionstore/\">ZUNIONSTORE</a></strong> - Stores the union of multiple sorted sets in a key.</summary>\n\n**Syntax:** `ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight\n  [weight ...]] [AGGREGATE <SUM | MIN | MAX>]`\n\n**Description:** Stores the union of multiple sorted sets in a key.\n\n**Complexity:** O(N)+O(M log(M)) with N being the sum of the sizes of the input sorted sets, and M being the number of elements in the resulting sorted set.\n\n**Since:** 2.0.0\n\n</details>"},{"id":"stream-commands","title":"Stream commands","role":"content","text":"Stream commands operate on append-only log data structures.\n\n<details>\n<summary><strong><a href=\"/commands/xack/\">XACK</a></strong> - Returns the number of messages that were successfully acknowledged by the consumer group member of a stream.</summary>\n\n**Syntax:** `XACK key group id [id ...]`\n\n**Description:** Returns the number of messages that were successfully acknowledged by the consumer group member of a stream.\n\n**Complexity:** O(1) for each message ID processed.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xadd/\">XADD</a></strong> - Appends a new message to a stream. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `XADD key [NOMKSTREAM] [KEEPREF | DELREF | ACKED] [<MAXLEN | MINID>\n  [= | ~] threshold [LIMIT count]] <* | id> field value [field value\n  ...]`\n\n**Description:** Appends a new message to a stream. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) when adding a new entry, O(N) when trimming where N being the number of entries evicted.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xautoclaim/\">XAUTOCLAIM</a></strong> - Changes, or acquires, ownership of messages in a consumer group, as if the messages were delivered to as consumer group member.</summary>\n\n**Syntax:** `XAUTOCLAIM key group consumer min-idle-time start [COUNT count]\n  [JUSTID]`\n\n**Description:** Changes, or acquires, ownership of messages in a consumer group, as if the messages were delivered to as consumer group member.\n\n**Complexity:** O(1) if COUNT is small.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xclaim/\">XCLAIM</a></strong> - Changes, or acquires, ownership of a message in a consumer group, as if the message was delivered a consumer group member.</summary>\n\n**Syntax:** `XCLAIM key group consumer min-idle-time id [id ...] [IDLE ms]\n  [TIME unix-time-milliseconds] [RETRYCOUNT count] [FORCE] [JUSTID]\n  [LASTID lastid]`\n\n**Description:** Changes, or acquires, ownership of a message in a consumer group, as if the message was delivered a consumer group member.\n\n**Complexity:** O(log N) with N being the number of messages in the PEL of the consumer group.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xdel/\">XDEL</a></strong> - Returns the number of messages after removing them from a stream.</summary>\n\n**Syntax:** `XDEL key id [id ...]`\n\n**Description:** Returns the number of messages after removing them from a stream.\n\n**Complexity:** O(1) for each single item to delete in the stream, regardless of the stream size.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-create/\">XGROUP CREATE</a></strong> - Creates a consumer group.</summary>\n\n**Syntax:** `XGROUP CREATE key group <id | $> [MKSTREAM]\n  [ENTRIESREAD entries-read]`\n\n**Description:** Creates a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-createconsumer/\">XGROUP CREATECONSUMER</a></strong> - Creates a consumer in a consumer group.</summary>\n\n**Syntax:** `XGROUP CREATECONSUMER key group consumer`\n\n**Description:** Creates a consumer in a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-delconsumer/\">XGROUP DELCONSUMER</a></strong> - Deletes a consumer from a consumer group.</summary>\n\n**Syntax:** `XGROUP DELCONSUMER key group consumer`\n\n**Description:** Deletes a consumer from a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-destroy/\">XGROUP DESTROY</a></strong> - Destroys a consumer group.</summary>\n\n**Syntax:** `XGROUP DESTROY key group`\n\n**Description:** Destroys a consumer group.\n\n**Complexity:** O(N) where N is the number of entries in the group's pending entries list (PEL).\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-setid/\">XGROUP SETID</a></strong> - Sets the last-delivered ID of a consumer group.</summary>\n\n**Syntax:** `XGROUP SETID key group <id | $> [ENTRIESREAD entries-read]`\n\n**Description:** Sets the last-delivered ID of a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xinfo-consumers/\">XINFO CONSUMERS</a></strong> - Returns a list of the consumers in a consumer group.</summary>\n\n**Syntax:** `XINFO CONSUMERS key group`\n\n**Description:** Returns a list of the consumers in a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xinfo-groups/\">XINFO GROUPS</a></strong> - Returns a list of the consumer groups of a stream.</summary>\n\n**Syntax:** `XINFO GROUPS key`\n\n**Description:** Returns a list of the consumer groups of a stream.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xinfo-stream/\">XINFO STREAM</a></strong> - Returns information about a stream.</summary>\n\n**Syntax:** `XINFO STREAM key [FULL [COUNT count]]`\n\n**Description:** Returns information about a stream.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xlen/\">XLEN</a></strong> - Return the number of messages in a stream.</summary>\n\n**Syntax:** `XLEN key`\n\n**Description:** Return the number of messages in a stream.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xpending/\">XPENDING</a></strong> - Returns the information and entries from a stream consumer group's pending entries list.</summary>\n\n**Syntax:** `XPENDING key group [[IDLE min-idle-time] start end count [consumer]]`\n\n**Description:** Returns the information and entries from a stream consumer group's pending entries list.\n\n**Complexity:** O(N) with N being the number of elements returned, so asking for a small fixed number of entries per call is O(1). O(M), where M is the total number of entries scanned when used with the IDLE filter. When the command returns just the summary and the list of consumers is small, it runs in O(1) time; otherwise, an additional O(N) time for iterating every consumer.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xrange/\">XRANGE</a></strong> - Returns the messages from a stream within a range of IDs.</summary>\n\n**Syntax:** `XRANGE key start end [COUNT count]`\n\n**Description:** Returns the messages from a stream within a range of IDs.\n\n**Complexity:** O(N) with N being the number of elements being returned. If N is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1).\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xread/\">XREAD</a></strong> - Returns messages from multiple streams with IDs greater than the ones requested. Blocks until a message is available otherwise.</summary>\n\n**Syntax:** `XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] id\n  [id ...]`\n\n**Description:** Returns messages from multiple streams with IDs greater than the ones requested. Blocks until a message is available otherwise.\n\n**Complexity:** \n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xreadgroup/\">XREADGROUP</a></strong> - Returns new or historical messages from a stream for a consumer in a group. Blocks until a message is available otherwise.</summary>\n\n**Syntax:** `XREADGROUP GROUP group consumer [COUNT count] [BLOCK milliseconds]\n  [CLAIM min-idle-time] [NOACK] STREAMS key [key ...] id [id ...]`\n\n**Description:** Returns new or historical messages from a stream for a consumer in a group. Blocks until a message is available otherwise.\n\n**Complexity:** For each stream mentioned: O(M) with M being the number of elements returned. If M is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1). On the other side when XREADGROUP blocks, XADD will pay the O(N) time in order to serve the N clients blocked on the stream getting new data.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xrevrange/\">XREVRANGE</a></strong> - Returns the messages from a stream within a range of IDs in reverse order.</summary>\n\n**Syntax:** `XREVRANGE key end start [COUNT count]`\n\n**Description:** Returns the messages from a stream within a range of IDs in reverse order.\n\n**Complexity:** O(N) with N being the number of elements returned. If N is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1).\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xsetid/\">XSETID</a></strong> - An internal command for replicating stream values.</summary>\n\n**Syntax:** `XSETID key last-id [ENTRIESADDED entries-added]\n  [MAXDELETEDID max-deleted-id]`\n\n**Description:** An internal command for replicating stream values.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xtrim/\">XTRIM</a></strong> - Deletes messages from the beginning of a stream.</summary>\n\n**Syntax:** `XTRIM key <MAXLEN | MINID> [= | ~] threshold [LIMIT count] [KEEPREF\n  | DELREF | ACKED]`\n\n**Description:** Deletes messages from the beginning of a stream.\n\n**Complexity:** O(N), with N being the number of evicted entries. Constant times are very small however, since entries are organized in macro nodes containing multiple entries that can be released with a single deallocation.\n\n**Since:** 5.0.0\n\n</details>"},{"id":"bitmap-commands","title":"Bitmap commands","role":"content","text":"Bitmap commands operate on strings as arrays of bits.\n\n<details>\n<summary><strong><a href=\"/commands/bitcount/\">BITCOUNT</a></strong> - Counts the number of set bits (population counting) in a string.</summary>\n\n**Syntax:** `BITCOUNT key [start end [BYTE | BIT]]`\n\n**Description:** Counts the number of set bits (population counting) in a string.\n\n**Complexity:** O(N)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bitfield/\">BITFIELD</a></strong> - Performs arbitrary bitfield integer operations on strings.</summary>\n\n**Syntax:** `BITFIELD key [GET encoding offset | [OVERFLOW <WRAP | SAT | FAIL>]\n  <SET encoding offset value | INCRBY encoding offset increment>\n  [GET encoding offset | [OVERFLOW <WRAP | SAT | FAIL>]\n  <SET encoding offset value | INCRBY encoding offset increment>\n  ...]]`\n\n**Description:** Performs arbitrary bitfield integer operations on strings.\n\n**Complexity:** O(1) for each subcommand specified\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bitfield_ro/\">BITFIELD_RO</a></strong> - Performs arbitrary read-only bitfield integer operations on strings.</summary>\n\n**Syntax:** `BITFIELD RO key [GET encoding offset [GET encoding offset ...]]`\n\n**Description:** Performs arbitrary read-only bitfield integer operations on strings.\n\n**Complexity:** O(1) for each subcommand specified\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bitop/\">BITOP</a></strong> - Performs bitwise operations on multiple strings, and stores the result.</summary>\n\n**Syntax:** `BITOP <AND | OR | XOR | NOT | DIFF | DIFF1 | ANDOR | ONE> destkey key [key ...]`\n\n**Description:** Performs bitwise operations on multiple strings, and stores the result.\n\n**Complexity:** O(N)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bitpos/\">BITPOS</a></strong> - Finds the first set (1) or clear (0) bit in a string.</summary>\n\n**Syntax:** `BITPOS key bit [start [end [BYTE | BIT]]]`\n\n**Description:** Finds the first set (1) or clear (0) bit in a string.\n\n**Complexity:** O(N)\n\n**Since:** 2.8.7\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getbit/\">GETBIT</a></strong> - Returns a bit value by offset.</summary>\n\n**Syntax:** `GETBIT key offset`\n\n**Description:** Returns a bit value by offset.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/setbit/\">SETBIT</a></strong> - Sets or clears the bit at offset of the string value. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `SETBIT key offset value`\n\n**Description:** Sets or clears the bit at offset of the string value. Creates the key if it doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>"},{"id":"hyperloglog-commands","title":"HyperLogLog commands","role":"content","text":"HyperLogLog commands provide probabilistic cardinality estimation.\n\n<details>\n<summary><strong><a href=\"/commands/pfadd/\">PFADD</a></strong> - Adds elements to a HyperLogLog key. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `PFADD key [element [element ...]]`\n\n**Description:** Adds elements to a HyperLogLog key. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) to add every element.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pfcount/\">PFCOUNT</a></strong> - Returns the approximated cardinality of the set(s) observed by the HyperLogLog key(s).</summary>\n\n**Syntax:** `PFCOUNT key [key ...]`\n\n**Description:** Returns the approximated cardinality of the set(s) observed by the HyperLogLog key(s).\n\n**Complexity:** O(1) with a very small average constant time when called with a single key. O(N) with N being the number of keys, and much bigger constant times, when called with multiple keys.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pfdebug/\">PFDEBUG</a></strong> - Internal commands for debugging HyperLogLog values.</summary>\n\n**Syntax:** `PFDEBUG subcommand key`\n\n**Description:** Internal commands for debugging HyperLogLog values.\n\n**Complexity:** N/A\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pfmerge/\">PFMERGE</a></strong> - Merges one or more HyperLogLog values into a single key.</summary>\n\n**Syntax:** `PFMERGE destkey [sourcekey [sourcekey ...]]`\n\n**Description:** Merges one or more HyperLogLog values into a single key.\n\n**Complexity:** O(N) to merge N HyperLogLogs, but with high constant times.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pfselftest/\">PFSELFTEST</a></strong> - An internal command for testing HyperLogLog values.</summary>\n\n**Syntax:** `PFSELFTEST`\n\n**Description:** An internal command for testing HyperLogLog values.\n\n**Complexity:** N/A\n\n**Since:** 2.8.9\n\n</details>"},{"id":"geospatial-commands","title":"Geospatial commands","role":"content","text":"Geospatial commands operate on geographic coordinates.\n\n<details>\n<summary><strong><a href=\"/commands/geoadd/\">GEOADD</a></strong> - Adds one or more members to a geospatial index. The key is created if it doesn't exist.</summary>\n\n**Syntax:** `GEOADD key [NX | XX] [CH] longitude latitude member [longitude\n  latitude member ...]`\n\n**Description:** Adds one or more members to a geospatial index. The key is created if it doesn't exist.\n\n**Complexity:** O(log(N)) for each item added, where N is the number of elements in the sorted set.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geodist/\">GEODIST</a></strong> - Returns the distance between two members of a geospatial index.</summary>\n\n**Syntax:** `GEODIST key member1 member2 [M | KM | FT | MI]`\n\n**Description:** Returns the distance between two members of a geospatial index.\n\n**Complexity:** O(1)\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geohash/\">GEOHASH</a></strong> - Returns members from a geospatial index as geohash strings.</summary>\n\n**Syntax:** `GEOHASH key [member [member ...]]`\n\n**Description:** Returns members from a geospatial index as geohash strings.\n\n**Complexity:** O(1) for each member requested.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geopos/\">GEOPOS</a></strong> - Returns the longitude and latitude of members from a geospatial index.</summary>\n\n**Syntax:** `GEOPOS key [member [member ...]]`\n\n**Description:** Returns the longitude and latitude of members from a geospatial index.\n\n**Complexity:** O(1) for each member requested.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/georadius/\">GEORADIUS</a></strong> - Queries a geospatial index for members within a distance from a coordinate, optionally stores the result.</summary>\n\n**Syntax:** `GEORADIUS key longitude latitude radius <M | KM | FT | MI>\n  [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC]\n  [STORE key | STOREDIST key]`\n\n**Description:** Queries a geospatial index for members within a distance from a coordinate, optionally stores the result.\n\n**Complexity:** O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/georadiusbymember/\">GEORADIUSBYMEMBER</a></strong> - Queries a geospatial index for members within a distance from a member, optionally stores the result.</summary>\n\n**Syntax:** `GEORADIUSBYMEMBER key member radius <M | KM | FT | MI> [WITHCOORD]\n  [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC] [STORE key\n  | STOREDIST key]`\n\n**Description:** Queries a geospatial index for members within a distance from a member, optionally stores the result.\n\n**Complexity:** O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/georadiusbymember_ro/\">GEORADIUSBYMEMBER_RO</a></strong> - Returns members from a geospatial index that are within a distance from a member.</summary>\n\n**Syntax:** `GEORADIUSBYMEMBER RO key member radius <M | KM | FT | MI>\n  [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC]`\n\n**Description:** Returns members from a geospatial index that are within a distance from a member.\n\n**Complexity:** O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.\n\n**Since:** 3.2.10\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/georadius_ro/\">GEORADIUS_RO</a></strong> - Returns members from a geospatial index that are within a distance from a coordinate.</summary>\n\n**Syntax:** `GEORADIUS RO key longitude latitude radius <M | KM | FT | MI>\n  [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC]`\n\n**Description:** Returns members from a geospatial index that are within a distance from a coordinate.\n\n**Complexity:** O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.\n\n**Since:** 3.2.10\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geosearch/\">GEOSEARCH</a></strong> - Queries a geospatial index for members inside an area of a box or a circle.</summary>\n\n**Syntax:** `GEOSEARCH key <FROMMEMBER member | FROMLONLAT longitude latitude>\n  <BYRADIUS radius <M | KM | FT | MI> | BYBOX width height <M | KM |\n  FT | MI>> [ASC | DESC] [COUNT count [ANY]] [WITHCOORD] [WITHDIST]\n  [WITHHASH]`\n\n**Description:** Queries a geospatial index for members inside an area of a box or a circle.\n\n**Complexity:** O(N+log(M)) where N is the number of elements in the grid-aligned bounding box area around the shape provided as the filter and M is the number of items inside the shape\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geosearchstore/\">GEOSEARCHSTORE</a></strong> - Queries a geospatial index for members inside an area of a box or a circle, optionally stores the result.</summary>\n\n**Syntax:** `GEOSEARCHSTORE destination source <FROMMEMBER member |\n  FROMLONLAT longitude latitude> <BYRADIUS radius <M | KM | FT | MI>\n  | BYBOX width height <M | KM | FT | MI>> [ASC | DESC] [COUNT count\n  [ANY]] [STOREDIST]`\n\n**Description:** Queries a geospatial index for members inside an area of a box or a circle, optionally stores the result.\n\n**Complexity:** O(N+log(M)) where N is the number of elements in the grid-aligned bounding box area around the shape provided as the filter and M is the number of items inside the shape\n\n**Since:** 6.2.0\n\n</details>"},{"id":"json-commands","title":"JSON commands","role":"content","text":"JSON commands operate on JSON data structures.\n\n<details>\n<summary><strong><a href=\"/commands/json.arrappend/\">JSON.ARRAPPEND</a></strong> - Append one or more JSON values into the array at path after the last element in it.</summary>\n\n**Syntax:** `JSON.ARRAPPEND key path value [value ...]`\n\n**Description:** Append one or more JSON values into the array at path after the last element in it.\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrindex/\">JSON.ARRINDEX</a></strong> - Returns the index of the first occurrence of a JSON scalar value in the array at path</summary>\n\n**Syntax:** `JSON.ARRINDEX key path value [start [stop]]`\n\n**Description:** Returns the index of the first occurrence of a JSON scalar value in the array at path\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the array, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrinsert/\">JSON.ARRINSERT</a></strong> - Inserts the JSON scalar(s) value at the specified index in the array at path</summary>\n\n**Syntax:** `JSON.ARRINSERT key path index value [value ...]`\n\n**Description:** Inserts the JSON scalar(s) value at the specified index in the array at path\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the array, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrlen/\">JSON.ARRLEN</a></strong> - Returns the length of the array at path</summary>\n\n**Syntax:** `JSON.ARRLEN key [path]`\n\n**Description:** Returns the length of the array at path\n\n**Complexity:** O(1) where path is evaluated to a single value, O(N) where path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrpop/\">JSON.ARRPOP</a></strong> - Removes and returns the element at the specified index in the array at path</summary>\n\n**Syntax:** `JSON.ARRPOP key [path [index]]`\n\n**Description:** Removes and returns the element at the specified index in the array at path\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the array and the specified index is not the last element, O(1) when path is evaluated to a single value and the specified index is the last element, or O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrtrim/\">JSON.ARRTRIM</a></strong> - Trims the array at path to contain only the specified inclusive range of indices from start to stop</summary>\n\n**Syntax:** `JSON.ARRTRIM key path start stop`\n\n**Description:** Trims the array at path to contain only the specified inclusive range of indices from start to stop\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the array, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.clear/\">JSON.CLEAR</a></strong> - Clears all values from an array or an object and sets numeric values to `0`</summary>\n\n**Syntax:** `JSON.CLEAR key [path]`\n\n**Description:** Clears all values from an array or an object and sets numeric values to `0`\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the values, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.debug/\">JSON.DEBUG</a></strong> - Debugging container command</summary>\n\n**Syntax:** `JSON.DEBUG`\n\n**Description:** Debugging container command\n\n**Complexity:** N/A\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.debug-memory/\">JSON.DEBUG MEMORY</a></strong> - Reports the size in bytes of a key</summary>\n\n**Syntax:** `JSON.DEBUG MEMORY key [path]`\n\n**Description:** Reports the size in bytes of a key\n\n**Complexity:** O(N) when path is evaluated to a single value, where N is the size of the value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.del/\">JSON.DEL</a></strong> - Deletes a value</summary>\n\n**Syntax:** `JSON.DEL key [path]`\n\n**Description:** Deletes a value\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the deleted value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.forget/\">JSON.FORGET</a></strong> - Deletes a value</summary>\n\n**Syntax:** `JSON.FORGET key [path]`\n\n**Description:** Deletes a value\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the deleted value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.get/\">JSON.GET</a></strong> - Gets the value at one or more paths in JSON serialized form</summary>\n\n**Syntax:** `JSON.GET key [INDENT indent] [NEWLINE newline] [SPACE space] [path\n  [path ...]]`\n\n**Description:** Gets the value at one or more paths in JSON serialized form\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.merge/\">JSON.MERGE</a></strong> - Merges a given JSON value into matching paths. Consequently, JSON values at matching paths are updated, deleted, or expanded with new children</summary>\n\n**Syntax:** `JSON.MERGE key path value`\n\n**Description:** Merges a given JSON value into matching paths. Consequently, JSON values at matching paths are updated, deleted, or expanded with new children\n\n**Complexity:** O(M+N) when path is evaluated to a single value where M is the size of the original value (if it exists) and N is the size of the new value, O(M+N) when path is evaluated to multiple values where M is the size of the key and N is the size of the new value * the number of original values in the key\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.mget/\">JSON.MGET</a></strong> - Returns the values at a path from one or more keys</summary>\n\n**Syntax:** `JSON.MGET key [key ...] path`\n\n**Description:** Returns the values at a path from one or more keys\n\n**Complexity:** O(M*N) when path is evaluated to a single value where M is the number of keys and N is the size of the value, O(N1+N2+...+Nm) when path is evaluated to multiple values where m is the number of keys and Ni is the size of the i-th key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.mset/\">JSON.MSET</a></strong> - Sets or updates the JSON value of one or more keys</summary>\n\n**Syntax:** `JSON.MSET key path value [key path value ...]`\n\n**Description:** Sets or updates the JSON value of one or more keys\n\n**Complexity:** O(K*(M+N)) where k is the number of keys in the command, when path is evaluated to a single value where M is the size of the original value (if it exists) and N is the size of the new value, or O(K*(M+N)) when path is evaluated to multiple values where M is the size of the key and N is the size of the new value * the number of original values in the key\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.numincrby/\">JSON.NUMINCRBY</a></strong> - Increments the numeric value at path by a value</summary>\n\n**Syntax:** `JSON.NUMINCRBY key path value`\n\n**Description:** Increments the numeric value at path by a value\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.nummultby/\">JSON.NUMMULTBY</a></strong> - Multiplies the numeric value at path by a value</summary>\n\n**Syntax:** `JSON.NUMMULTBY key path value`\n\n**Description:** Multiplies the numeric value at path by a value\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.objkeys/\">JSON.OBJKEYS</a></strong> - Returns the JSON keys of the object at path</summary>\n\n**Syntax:** `JSON.OBJKEYS key [path]`\n\n**Description:** Returns the JSON keys of the object at path\n\n**Complexity:** O(N) when path is evaluated to a single value, where N is the number of keys in the object, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.objlen/\">JSON.OBJLEN</a></strong> - Returns the number of keys of the object at path</summary>\n\n**Syntax:** `JSON.OBJLEN key [path]`\n\n**Description:** Returns the number of keys of the object at path\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.resp/\">JSON.RESP</a></strong> - Returns the JSON value at path in Redis Serialization Protocol (RESP)</summary>\n\n**Syntax:** `JSON.RESP key [path]`\n\n**Description:** Returns the JSON value at path in Redis Serialization Protocol (RESP)\n\n**Complexity:** O(N) when path is evaluated to a single value, where N is the size of the value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.set/\">JSON.SET</a></strong> - Sets or updates the JSON value at a path</summary>\n\n**Syntax:** `JSON.SET key path value [NX | XX]`\n\n**Description:** Sets or updates the JSON value at a path\n\n**Complexity:** O(M+N) when path is evaluated to a single value where M is the size of the original value (if it exists) and N is the size of the new value, O(M+N) when path is evaluated to multiple values where M is the size of the key and N is the size of the new value * the number of original values in the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.strappend/\">JSON.STRAPPEND</a></strong> - Appends a string to a JSON string value at path</summary>\n\n**Syntax:** `JSON.STRAPPEND key [path] value`\n\n**Description:** Appends a string to a JSON string value at path\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.strlen/\">JSON.STRLEN</a></strong> - Returns the length of the JSON String at path in key</summary>\n\n**Syntax:** `JSON.STRLEN key [path]`\n\n**Description:** Returns the length of the JSON String at path in key\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.toggle/\">JSON.TOGGLE</a></strong> - Toggles a boolean value</summary>\n\n**Syntax:** `JSON.TOGGLE key path`\n\n**Description:** Toggles a boolean value\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.type/\">JSON.TYPE</a></strong> - Returns the type of the JSON value at path</summary>\n\n**Syntax:** `JSON.TYPE key [path]`\n\n**Description:** Returns the type of the JSON value at path\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>"},{"id":"search-commands","title":"Search commands","role":"content","text":"Search commands provide full-text search and secondary indexing.\n\n<details>\n<summary><strong><a href=\"/commands/ft.aggregate/\">FT.AGGREGATE</a></strong> - Run a search query on an index and perform aggregate transformations on the results</summary>\n\n**Syntax:** `FT.AGGREGATE index query [VERBATIM] [LOAD count field [field ...]]\n  [TIMEOUT timeout] [LOAD *] [GROUPBY nargs property [property ...]\n  [REDUCE function nargs arg [arg ...] [AS name] [REDUCE function\n  nargs arg [arg ...] [AS name] ...]] [GROUPBY nargs property\n  [property ...] [REDUCE function nargs arg [arg ...] [AS name]\n  [REDUCE function nargs arg [arg ...] [AS name] ...]] ...]]\n  [SORTBY nargs [property <ASC | DESC> [property <ASC | DESC> ...]]\n  [MAX num]] [APPLY expression AS name [APPLY expression AS name\n  ...]] [LIMIT offset num] [FILTER filter] [WITHCURSOR\n  [COUNT read size] [MAXIDLE idle time]] [PARAMS nargs name value\n  [name value ...]]\n  [SCORER scorer]\n [ADDSCORES]\n  [DIALECT dialect]`\n\n**Description:** Run a search query on an index and perform aggregate transformations on the results\n\n**Complexity:** O(1)\n\n**Since:** 1.1.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.aliasadd/\">FT.ALIASADD</a></strong> - Adds an alias to the index</summary>\n\n**Syntax:** `FT.ALIASADD alias index`\n\n**Description:** Adds an alias to the index\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.aliasdel/\">FT.ALIASDEL</a></strong> - Deletes an alias from the index</summary>\n\n**Syntax:** `FT.ALIASDEL alias`\n\n**Description:** Deletes an alias from the index\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.aliasupdate/\">FT.ALIASUPDATE</a></strong> - Adds or updates an alias to the index</summary>\n\n**Syntax:** `FT.ALIASUPDATE alias index`\n\n**Description:** Adds or updates an alias to the index\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.alter/\">FT.ALTER</a></strong> - Adds a new field to the index</summary>\n\n**Syntax:** `FT.ALTER index [SKIPINITIALSCAN] SCHEMA ADD field options`\n\n**Description:** Adds a new field to the index\n\n**Complexity:** O(N) where N is the number of keys in the keyspace\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.config-get/\">FT.CONFIG GET</a></strong> - Retrieves runtime configuration options</summary>\n\n**Syntax:** `FT.CONFIG GET option`\n\n**Description:** Retrieves runtime configuration options\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.config-set/\">FT.CONFIG SET</a></strong> - Sets runtime configuration options</summary>\n\n**Syntax:** `FT.CONFIG SET option value`\n\n**Description:** Sets runtime configuration options\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.create/\">FT.CREATE</a></strong> - Creates an index with the given spec</summary>\n\n**Syntax:** `FT.CREATE index [ON <HASH | JSON>] [PREFIX count prefix [prefix\n  ...]] [FILTER filter] [LANGUAGE default lang]\n  [LANGUAGE FIELD lang attribute] [SCORE default score]\n  [SCORE FIELD score attribute] [PAYLOAD FIELD payload attribute]\n  [MAXTEXTFIELDS] [TEMPORARY seconds] [NOOFFSETS] [NOHL] [NOFIELDS]\n  [NOFREQS] [STOPWORDS count [stopword [stopword ...]]]\n  [SKIPINITIALSCAN] SCHEMA field name [AS alias] <TEXT | TAG |\n  NUMERIC | GEO | VECTOR> [WITHSUFFIXTRIE] [SORTABLE [UNF]]\n  [NOINDEX] [field name [AS alias] <TEXT | TAG | NUMERIC | GEO |\n  VECTOR> [WITHSUFFIXTRIE] [SORTABLE [UNF]] [NOINDEX] ...]`\n\n**Description:** Creates an index with the given spec\n\n**Complexity:** O(K) at creation where K is the number of fields, O(N) if scanning the keyspace is triggered, where N is the number of keys in the keyspace\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.cursor-del/\">FT.CURSOR DEL</a></strong> - Deletes a cursor</summary>\n\n**Syntax:** `FT.CURSOR DEL index cursor id`\n\n**Description:** Deletes a cursor\n\n**Complexity:** O(1)\n\n**Since:** 1.1.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.cursor-read/\">FT.CURSOR READ</a></strong> - Reads from a cursor</summary>\n\n**Syntax:** `FT.CURSOR READ index cursor id [COUNT read size]`\n\n**Description:** Reads from a cursor\n\n**Complexity:** O(1)\n\n**Since:** 1.1.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.dictadd/\">FT.DICTADD</a></strong> - Adds terms to a dictionary</summary>\n\n**Syntax:** `FT.DICTADD dict term [term ...]`\n\n**Description:** Adds terms to a dictionary\n\n**Complexity:** O(1)\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.dictdel/\">FT.DICTDEL</a></strong> - Deletes terms from a dictionary</summary>\n\n**Syntax:** `FT.DICTDEL dict term [term ...]`\n\n**Description:** Deletes terms from a dictionary\n\n**Complexity:** O(1)\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.dictdump/\">FT.DICTDUMP</a></strong> - Dumps all terms in the given dictionary</summary>\n\n**Syntax:** `FT.DICTDUMP dict`\n\n**Description:** Dumps all terms in the given dictionary\n\n**Complexity:** O(N), where N is the size of the dictionary\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.dropindex/\">FT.DROPINDEX</a></strong> - Deletes the index</summary>\n\n**Syntax:** `FT.DROPINDEX index [DD]`\n\n**Description:** Deletes the index\n\n**Complexity:** O(1) or O(N) if documents are deleted, where N is the number of keys in the keyspace\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.explain/\">FT.EXPLAIN</a></strong> - Returns the execution plan for a complex query</summary>\n\n**Syntax:** `FT.EXPLAIN index query [DIALECT dialect]`\n\n**Description:** Returns the execution plan for a complex query\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.explaincli/\">FT.EXPLAINCLI</a></strong> - Returns the execution plan for a complex query</summary>\n\n**Syntax:** `FT.EXPLAINCLI index query [DIALECT dialect]`\n\n**Description:** Returns the execution plan for a complex query\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.info/\">FT.INFO</a></strong> - Returns information and statistics on the index</summary>\n\n**Syntax:** `FT.INFO index`\n\n**Description:** Returns information and statistics on the index\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.profile/\">FT.PROFILE</a></strong> - Performs a `FT.SEARCH` or `FT.AGGREGATE` command and collects performance information</summary>\n\n**Syntax:** `FT.PROFILE index <SEARCH | AGGREGATE> [LIMITED] QUERY query`\n\n**Description:** Performs a `FT.SEARCH` or `FT.AGGREGATE` command and collects performance information\n\n**Complexity:** O(N)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.search/\">FT.SEARCH</a></strong> - Searches the index with a textual query, returning either documents or just ids</summary>\n\n**Syntax:** `FT.SEARCH index query [NOCONTENT] [VERBATIM] [NOSTOPWORDS]\n  [WITHSCORES] [WITHPAYLOADS] [WITHSORTKEYS] [FILTER numeric field\n  min max [FILTER numeric field min max ...]] [GEOFILTER geo field\n  lon lat radius <m | km | mi | ft> [GEOFILTER geo field lon lat\n  radius <m | km | mi | ft> ...]] [INKEYS count key [key ...]]\n  [INFIELDS count field [field ...]] [RETURN count identifier\n  [AS property] [identifier [AS property] ...]] [SUMMARIZE\n  [FIELDS count field [field ...]] [FRAGS num] [LEN fragsize]\n  [SEPARATOR separator]] [HIGHLIGHT [FIELDS count field [field ...]]\n  [TAGS open close]] [SLOP slop] [TIMEOUT timeout] [INORDER]\n  [LANGUAGE language] [EXPANDER expander] [SCORER scorer]\n  [EXPLAINSCORE] [PAYLOAD payload] [SORTBY sortby [ASC | DESC]]\n  [LIMIT offset num] [PARAMS nargs name value [name value ...]]\n  [DIALECT dialect]`\n\n**Description:** Searches the index with a textual query, returning either documents or just ids\n\n**Complexity:** O(N)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.spellcheck/\">FT.SPELLCHECK</a></strong> - Performs spelling correction on a query, returning suggestions for misspelled terms</summary>\n\n**Syntax:** `FT.SPELLCHECK index query [DISTANCE distance] [TERMS <INCLUDE |\n  EXCLUDE> dictionary [terms [terms ...]]] [DIALECT dialect]`\n\n**Description:** Performs spelling correction on a query, returning suggestions for misspelled terms\n\n**Complexity:** O(1)\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.syndump/\">FT.SYNDUMP</a></strong> - Dumps the contents of a synonym group</summary>\n\n**Syntax:** `FT.SYNDUMP index`\n\n**Description:** Dumps the contents of a synonym group\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.synupdate/\">FT.SYNUPDATE</a></strong> - Creates or updates a synonym group with additional terms</summary>\n\n**Syntax:** `FT.SYNUPDATE index synonym group id [SKIPINITIALSCAN] term [term\n  ...]`\n\n**Description:** Creates or updates a synonym group with additional terms\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.tagvals/\">FT.TAGVALS</a></strong> - Returns the distinct tags indexed in a Tag field</summary>\n\n**Syntax:** `FT.TAGVALS index field name`\n\n**Description:** Returns the distinct tags indexed in a Tag field\n\n**Complexity:** O(N)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft._list/\">FT._LIST</a></strong> - Returns a list of all existing indexes</summary>\n\n**Syntax:** `FT. LIST`\n\n**Description:** Returns a list of all existing indexes\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>"},{"id":"time-series-commands","title":"Time series commands","role":"content","text":"Time series commands operate on time-series data.\n\n<details>\n<summary><strong><a href=\"/commands/ts.add/\">TS.ADD</a></strong> - Append a sample to a time series</summary>\n\n**Syntax:** `TS.ADD key timestamp value [RETENTION retentionPeriod]\n  [ENCODING <COMPRESSED | UNCOMPRESSED>] [CHUNK SIZE size]\n  [DUPLICATE POLICY policy] \n  [ON DUPLICATE <BLOCK | FIRST | LAST | MIN | MAX | SUM>]\n  [IGNORE ignoreMaxTimediff ignoreMaxValDiff]\n  [LABELS [label value ...]]`\n\n**Description:** Append a sample to a time series\n\n**Complexity:** O(M) when M is the amount of compaction rules or O(1) with no compaction\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.alter/\">TS.ALTER</a></strong> - Update the retention, chunk size, duplicate policy, and labels of an existing time series</summary>\n\n**Syntax:** `TS.ALTER key [RETENTION retentionPeriod] [CHUNK SIZE size]\n  [DUPLICATE POLICY <BLOCK | FIRST | LAST | MIN | MAX | SUM>]\n  [IGNORE ignoreMaxTimediff ignoreMaxValDiff] \n  [LABELS [label value ...]]`\n\n**Description:** Update the retention, chunk size, duplicate policy, and labels of an existing time series\n\n**Complexity:** O(N) where N is the number of labels requested to update\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.create/\">TS.CREATE</a></strong> - Create a new time series</summary>\n\n**Syntax:** `TS.CREATE key [RETENTION retentionPeriod] [ENCODING <COMPRESSED |\n  UNCOMPRESSED>] [CHUNK SIZE size] [DUPLICATE POLICY <BLOCK | FIRST |\n  LAST | MIN | MAX | SUM>]\n  [IGNORE ignoreMaxTimediff ignoreMaxValDiff]\n  [LABELS [label value ...]]`\n\n**Description:** Create a new time series\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.createrule/\">TS.CREATERULE</a></strong> - Create a compaction rule</summary>\n\n**Syntax:** `TS.CREATERULE sourceKey destKey AGGREGATION <AVG | FIRST | LAST |\n  MIN | MAX | SUM | RANGE | COUNT | STD.P | STD.S | VAR.P | VAR.S |\n  TWA> bucketDuration [alignTimestamp]`\n\n**Description:** Create a compaction rule\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.decrby/\">TS.DECRBY</a></strong> - Decrease the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given decrement</summary>\n\n**Syntax:** `TS.DECRBY key value [TIMESTAMP timestamp]\n  [RETENTION retentionPeriod] [ENCODING <COMPRESSED|UNCOMPRESSED>] [CHUNK SIZE size]\n [DUPLICATE POLICY policy] [LABELS [label value ...]]`\n\n**Description:** Decrease the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given decrement\n\n**Complexity:** O(M) when M is the amount of compaction rules or O(1) with no compaction\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.del/\">TS.DEL</a></strong> - Delete all samples between two timestamps for a given time series</summary>\n\n**Syntax:** `TS.DEL key from timestamp to timestamp`\n\n**Description:** Delete all samples between two timestamps for a given time series\n\n**Complexity:** O(N) where N is the number of data points that will be removed\n\n**Since:** 1.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.deleterule/\">TS.DELETERULE</a></strong> - Delete a compaction rule</summary>\n\n**Syntax:** `TS.DELETERULE sourceKey destKey`\n\n**Description:** Delete a compaction rule\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.get/\">TS.GET</a></strong> - Get the sample with the highest timestamp from a given time series</summary>\n\n**Syntax:** `TS.GET key [LATEST]`\n\n**Description:** Get the sample with the highest timestamp from a given time series\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.incrby/\">TS.INCRBY</a></strong> - Increase the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given increment</summary>\n\n**Syntax:** `TS.INCRBY key value [TIMESTAMP timestamp]\n  [RETENTION retentionPeriod] [ENCODING <COMPRESSED|UNCOMPRESSED>] [CHUNK SIZE size]\n [DUPLICATE POLICY policy] [LABELS [label value ...]]`\n\n**Description:** Increase the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given increment\n\n**Complexity:** O(M) when M is the amount of compaction rules or O(1) with no compaction\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.info/\">TS.INFO</a></strong> - Returns information and statistics for a time series</summary>\n\n**Syntax:** `TS.INFO key [DEBUG]`\n\n**Description:** Returns information and statistics for a time series\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.madd/\">TS.MADD</a></strong> - Append new samples to one or more time series</summary>\n\n**Syntax:** `TS.MADD key timestamp value [key timestamp value ...]`\n\n**Description:** Append new samples to one or more time series\n\n**Complexity:** O(N*M) when N is the amount of series updated and M is the amount of compaction rules or O(N) with no compaction\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.mget/\">TS.MGET</a></strong> - Get the sample with the highest timestamp from each time series matching a specific filter</summary>\n\n**Syntax:** `TS.MGET [LATEST] [WITHLABELS | <SELECTED LABELS label1 [label1 ...]>]\n  FILTER <l=v | l!=v | l= | l!= | l=(v1,v2,...) | l!=(v1,v2,...)\n  [l=v | l!=v | l= | l!= | l=(v1,v2,...) | l!=(v1,v2,...) ...]>`\n\n**Description:** Get the sample with the highest timestamp from each time series matching a specific filter\n\n**Complexity:** O(n) where n is the number of time-series that match the filters\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.mrange/\">TS.MRANGE</a></strong> - Query a range across multiple time series by filters in forward direction</summary>\n\n**Syntax:** `TS.MRANGE fromTimestamp toTimestamp [LATEST] [FILTER BY TS Timestamp\n  [Timestamp ...]] [FILTER BY VALUE min max] [WITHLABELS |\n  <SELECTED LABELS label1 [label1 ...]>] [COUNT count] [[ALIGN value]\n  AGGREGATION <AVG | FIRST | LAST | MIN | MAX | SUM | RANGE | COUNT\n  | STD.P | STD.S | VAR.P | VAR.S | TWA> bucketDuration\n  [BUCKETTIMESTAMP] [EMPTY]] FILTER <l=v | l!=v | l= | l!= |\n  l=(v1,v2,...) | l!=(v1,v2,...) [l=v | l!=v | l= | l!= |\n  l=(v1,v2,...) | l!=(v1,v2,...) ...]> [GROUPBY label REDUCE\n  reducer]`\n\n**Description:** Query a range across multiple time series by filters in forward direction\n\n**Complexity:** O(n/m+k) where n = Number of data points, m = Chunk size (data points per chunk), k = Number of data points that are in the requested ranges\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.mrevrange/\">TS.MREVRANGE</a></strong> - Query a range across multiple time-series by filters in reverse direction</summary>\n\n**Syntax:** `TS.MREVRANGE fromTimestamp toTimestamp [LATEST]\n  [FILTER BY TS Timestamp [Timestamp ...]] [FILTER BY VALUE min max]\n  [WITHLABELS | <SELECTED LABELS label1 [label1 ...]>] [COUNT count]\n  [[ALIGN value] AGGREGATION <AVG | FIRST | LAST | MIN | MAX | SUM |\n  RANGE | COUNT | STD.P | STD.S | VAR.P | VAR.S | TWA>\n  bucketDuration [BUCKETTIMESTAMP] [EMPTY]] FILTER <l=v | l!=v | l=\n  | l!= | l=(v1,v2,...) | l!=(v1,v2,...) [l=v | l!=v | l= | l!= |\n  l=(v1,v2,...) | l!=(v1,v2,...) ...]> [GROUPBY label REDUCE\n  reducer]`\n\n**Description:** Query a range across multiple time-series by filters in reverse direction\n\n**Complexity:** O(n/m+k) where n = Number of data points, m = Chunk size (data points per chunk), k = Number of data points that are in the requested ranges\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.queryindex/\">TS.QUERYINDEX</a></strong> - Get all time series keys matching a filter list</summary>\n\n**Syntax:** `TS.QUERYINDEX <l=v | l!=v | l= | l!= | l=(v1,v2,...) |\n  l!=(v1,v2,...) [l=v | l!=v | l= | l!= | l=(v1,v2,...) |\n  l!=(v1,v2,...) ...]>`\n\n**Description:** Get all time series keys matching a filter list\n\n**Complexity:** O(n) where n is the number of time-series that match the filters\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.range/\">TS.RANGE</a></strong> - Query a range in forward direction</summary>\n\n**Syntax:** `TS.RANGE key fromTimestamp toTimestamp [LATEST]\n  [FILTER BY TS Timestamp [Timestamp ...]] [FILTER BY VALUE min max]\n  [COUNT count] [[ALIGN value] AGGREGATION <AVG | FIRST | LAST | MIN\n  | MAX | SUM | RANGE | COUNT | STD.P | STD.S | VAR.P | VAR.S | TWA>\n  bucketDuration [BUCKETTIMESTAMP] [EMPTY]]`\n\n**Description:** Query a range in forward direction\n\n**Complexity:** O(n/m+k) where n = Number of data points, m = Chunk size (data points per chunk), k = Number of data points that are in the requested range\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.revrange/\">TS.REVRANGE</a></strong> - Query a range in reverse direction</summary>\n\n**Syntax:** `TS.REVRANGE key fromTimestamp toTimestamp [LATEST]\n  [FILTER BY TS Timestamp [Timestamp ...]] [FILTER BY VALUE min max]\n  [COUNT count] [[ALIGN value] AGGREGATION <AVG | FIRST | LAST | MIN\n  | MAX | SUM | RANGE | COUNT | STD.P | STD.S | VAR.P | VAR.S | TWA>\n  bucketDuration [BUCKETTIMESTAMP] [EMPTY]]`\n\n**Description:** Query a range in reverse direction\n\n**Complexity:** O(n/m+k) where n = Number of data points, m = Chunk size (data points per chunk), k = Number of data points that are in the requested range\n\n**Since:** 1.4.0\n\n</details>"},{"id":"pubsub-commands","title":"Pub/Sub commands","role":"content","text":"Pub/Sub commands enable message passing between clients.\n\n<details>\n<summary><strong><a href=\"/commands/psubscribe/\">PSUBSCRIBE</a></strong> - Listens for messages published to channels that match one or more patterns.</summary>\n\n**Syntax:** `PSUBSCRIBE pattern [pattern ...]`\n\n**Description:** Listens for messages published to channels that match one or more patterns.\n\n**Complexity:** O(N) where N is the number of patterns to subscribe to.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/publish/\">PUBLISH</a></strong> - Posts a message to a channel.</summary>\n\n**Syntax:** `PUBLISH channel message`\n\n**Description:** Posts a message to a channel.\n\n**Complexity:** O(N+M) where N is the number of clients subscribed to the receiving channel and M is the total number of subscribed patterns (by any client).\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-channels/\">PUBSUB CHANNELS</a></strong> - Returns the active channels.</summary>\n\n**Syntax:** `PUBSUB CHANNELS [pattern]`\n\n**Description:** Returns the active channels.\n\n**Complexity:** O(N) where N is the number of active channels, and assuming constant time pattern matching (relatively short channels and patterns)\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-numpat/\">PUBSUB NUMPAT</a></strong> - Returns a count of unique pattern subscriptions.</summary>\n\n**Syntax:** `PUBSUB NUMPAT`\n\n**Description:** Returns a count of unique pattern subscriptions.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-numsub/\">PUBSUB NUMSUB</a></strong> - Returns a count of subscribers to channels.</summary>\n\n**Syntax:** `PUBSUB NUMSUB [channel [channel ...]]`\n\n**Description:** Returns a count of subscribers to channels.\n\n**Complexity:** O(N) for the NUMSUB subcommand, where N is the number of requested channels\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-shardchannels/\">PUBSUB SHARDCHANNELS</a></strong> - Returns the active shard channels.</summary>\n\n**Syntax:** `PUBSUB SHARDCHANNELS [pattern]`\n\n**Description:** Returns the active shard channels.\n\n**Complexity:** O(N) where N is the number of active shard channels, and assuming constant time pattern matching (relatively short shard channels).\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-shardnumsub/\">PUBSUB SHARDNUMSUB</a></strong> - Returns the count of subscribers of shard channels.</summary>\n\n**Syntax:** `PUBSUB SHARDNUMSUB [shardchannel [shardchannel ...]]`\n\n**Description:** Returns the count of subscribers of shard channels.\n\n**Complexity:** O(N) for the SHARDNUMSUB subcommand, where N is the number of requested shard channels\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/punsubscribe/\">PUNSUBSCRIBE</a></strong> - Stops listening to messages published to channels that match one or more patterns.</summary>\n\n**Syntax:** `PUNSUBSCRIBE [pattern [pattern ...]]`\n\n**Description:** Stops listening to messages published to channels that match one or more patterns.\n\n**Complexity:** O(N) where N is the number of patterns to unsubscribe.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/spublish/\">SPUBLISH</a></strong> - Post a message to a shard channel</summary>\n\n**Syntax:** `SPUBLISH shardchannel message`\n\n**Description:** Post a message to a shard channel\n\n**Complexity:** O(N) where N is the number of clients subscribed to the receiving shard channel.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ssubscribe/\">SSUBSCRIBE</a></strong> - Listens for messages published to shard channels.</summary>\n\n**Syntax:** `SSUBSCRIBE shardchannel [shardchannel ...]`\n\n**Description:** Listens for messages published to shard channels.\n\n**Complexity:** O(N) where N is the number of shard channels to subscribe to.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/subscribe/\">SUBSCRIBE</a></strong> - Listens for messages published to channels.</summary>\n\n**Syntax:** `SUBSCRIBE channel [channel ...]`\n\n**Description:** Listens for messages published to channels.\n\n**Complexity:** O(N) where N is the number of channels to subscribe to.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sunsubscribe/\">SUNSUBSCRIBE</a></strong> - Stops listening to messages posted to shard channels.</summary>\n\n**Syntax:** `SUNSUBSCRIBE [shardchannel [shardchannel ...]]`\n\n**Description:** Stops listening to messages posted to shard channels.\n\n**Complexity:** O(N) where N is the number of shard channels to unsubscribe.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/unsubscribe/\">UNSUBSCRIBE</a></strong> - Stops listening to messages posted to channels.</summary>\n\n**Syntax:** `UNSUBSCRIBE [channel [channel ...]]`\n\n**Description:** Stops listening to messages posted to channels.\n\n**Complexity:** O(N) where N is the number of channels to unsubscribe.\n\n**Since:** 2.0.0\n\n</details>"},{"id":"transaction-commands","title":"Transaction commands","role":"content","text":"Transaction commands enable atomic execution of command groups.\n\n<details>\n<summary><strong><a href=\"/commands/discard/\">DISCARD</a></strong> - Discards a transaction.</summary>\n\n**Syntax:** `DISCARD`\n\n**Description:** Discards a transaction.\n\n**Complexity:** O(N), when N is the number of queued commands\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/exec/\">EXEC</a></strong> - Executes all commands in a transaction.</summary>\n\n**Syntax:** `EXEC`\n\n**Description:** Executes all commands in a transaction.\n\n**Complexity:** Depends on commands in the transaction\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/multi/\">MULTI</a></strong> - Starts a transaction.</summary>\n\n**Syntax:** `MULTI`\n\n**Description:** Starts a transaction.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/unwatch/\">UNWATCH</a></strong> - Forgets about watched keys of a transaction.</summary>\n\n**Syntax:** `UNWATCH`\n\n**Description:** Forgets about watched keys of a transaction.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/watch/\">WATCH</a></strong> - Monitors changes to keys to determine the execution of a transaction.</summary>\n\n**Syntax:** `WATCH key [key ...]`\n\n**Description:** Monitors changes to keys to determine the execution of a transaction.\n\n**Complexity:** O(1) for every key.\n\n**Since:** 2.2.0\n\n</details>"},{"id":"scripting-commands","title":"Scripting commands","role":"content","text":"Scripting commands enable server-side Lua script execution.\n\n<details>\n<summary><strong><a href=\"/commands/eval/\">EVAL</a></strong> - Executes a server-side Lua script.</summary>\n\n**Syntax:** `EVAL script numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Executes a server-side Lua script.\n\n**Complexity:** Depends on the script that is executed.\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/evalsha/\">EVALSHA</a></strong> - Executes a server-side Lua script by SHA1 digest.</summary>\n\n**Syntax:** `EVALSHA sha1 numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Executes a server-side Lua script by SHA1 digest.\n\n**Complexity:** Depends on the script that is executed.\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/evalsha_ro/\">EVALSHA_RO</a></strong> - Executes a read-only server-side Lua script by SHA1 digest.</summary>\n\n**Syntax:** `EVALSHA RO sha1 numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Executes a read-only server-side Lua script by SHA1 digest.\n\n**Complexity:** Depends on the script that is executed.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/eval_ro/\">EVAL_RO</a></strong> - Executes a read-only server-side Lua script.</summary>\n\n**Syntax:** `EVAL RO script numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Executes a read-only server-side Lua script.\n\n**Complexity:** Depends on the script that is executed.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/fcall/\">FCALL</a></strong> - Invokes a function.</summary>\n\n**Syntax:** `FCALL function numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Invokes a function.\n\n**Complexity:** Depends on the function that is executed.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/fcall_ro/\">FCALL_RO</a></strong> - Invokes a read-only function.</summary>\n\n**Syntax:** `FCALL RO function numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Invokes a read-only function.\n\n**Complexity:** Depends on the function that is executed.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-delete/\">FUNCTION DELETE</a></strong> - Deletes a library and its functions.</summary>\n\n**Syntax:** `FUNCTION DELETE library-name`\n\n**Description:** Deletes a library and its functions.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-dump/\">FUNCTION DUMP</a></strong> - Dumps all libraries into a serialized binary payload.</summary>\n\n**Syntax:** `FUNCTION DUMP`\n\n**Description:** Dumps all libraries into a serialized binary payload.\n\n**Complexity:** O(N) where N is the number of functions\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-flush/\">FUNCTION FLUSH</a></strong> - Deletes all libraries and functions.</summary>\n\n**Syntax:** `FUNCTION FLUSH [ASYNC | SYNC]`\n\n**Description:** Deletes all libraries and functions.\n\n**Complexity:** O(N) where N is the number of functions deleted\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-kill/\">FUNCTION KILL</a></strong> - Terminates a function during execution.</summary>\n\n**Syntax:** `FUNCTION KILL`\n\n**Description:** Terminates a function during execution.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-list/\">FUNCTION LIST</a></strong> - Returns information about all libraries.</summary>\n\n**Syntax:** `FUNCTION LIST [LIBRARYNAME library-name-pattern] [WITHCODE]`\n\n**Description:** Returns information about all libraries.\n\n**Complexity:** O(N) where N is the number of functions\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-load/\">FUNCTION LOAD</a></strong> - Creates a library.</summary>\n\n**Syntax:** `FUNCTION LOAD [REPLACE] function-code`\n\n**Description:** Creates a library.\n\n**Complexity:** O(1) (considering compilation time is redundant)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-restore/\">FUNCTION RESTORE</a></strong> - Restores all libraries from a payload.</summary>\n\n**Syntax:** `FUNCTION RESTORE serialized-value [FLUSH | APPEND | REPLACE]`\n\n**Description:** Restores all libraries from a payload.\n\n**Complexity:** O(N) where N is the number of functions on the payload\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-stats/\">FUNCTION STATS</a></strong> - Returns information about a function during execution.</summary>\n\n**Syntax:** `FUNCTION STATS`\n\n**Description:** Returns information about a function during execution.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-debug/\">SCRIPT DEBUG</a></strong> - Sets the debug mode of server-side Lua scripts.</summary>\n\n**Syntax:** `SCRIPT DEBUG <YES | SYNC | NO>`\n\n**Description:** Sets the debug mode of server-side Lua scripts.\n\n**Complexity:** O(1)\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-exists/\">SCRIPT EXISTS</a></strong> - Determines whether server-side Lua scripts exist in the script cache.</summary>\n\n**Syntax:** `SCRIPT EXISTS sha1 [sha1 ...]`\n\n**Description:** Determines whether server-side Lua scripts exist in the script cache.\n\n**Complexity:** O(N) with N being the number of scripts to check (so checking a single script is an O(1) operation).\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-flush/\">SCRIPT FLUSH</a></strong> - Removes all server-side Lua scripts from the script cache.</summary>\n\n**Syntax:** `SCRIPT FLUSH [ASYNC | SYNC]`\n\n**Description:** Removes all server-side Lua scripts from the script cache.\n\n**Complexity:** O(N) with N being the number of scripts in cache\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-kill/\">SCRIPT KILL</a></strong> - Terminates a server-side Lua script during execution.</summary>\n\n**Syntax:** `SCRIPT KILL`\n\n**Description:** Terminates a server-side Lua script during execution.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-load/\">SCRIPT LOAD</a></strong> - Loads a server-side Lua script to the script cache.</summary>\n\n**Syntax:** `SCRIPT LOAD script`\n\n**Description:** Loads a server-side Lua script to the script cache.\n\n**Complexity:** O(N) with N being the length in bytes of the script body.\n\n**Since:** 2.6.0\n\n</details>"},{"id":"connection-commands","title":"Connection commands","role":"content","text":"Connection commands manage client connections.\n\n<details>\n<summary><strong><a href=\"/commands/auth/\">AUTH</a></strong> - Authenticates the connection.</summary>\n\n**Syntax:** `AUTH [username] password`\n\n**Description:** Authenticates the connection.\n\n**Complexity:** O(N) where N is the number of passwords defined for the user\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-caching/\">CLIENT CACHING</a></strong> - Instructs the server whether to track the keys in the next request.</summary>\n\n**Syntax:** `CLIENT CACHING <YES | NO>`\n\n**Description:** Instructs the server whether to track the keys in the next request.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-getname/\">CLIENT GETNAME</a></strong> - Returns the name of the connection.</summary>\n\n**Syntax:** `CLIENT GETNAME`\n\n**Description:** Returns the name of the connection.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-getredir/\">CLIENT GETREDIR</a></strong> - Returns the client ID to which the connection's tracking notifications are redirected.</summary>\n\n**Syntax:** `CLIENT GETREDIR`\n\n**Description:** Returns the client ID to which the connection's tracking notifications are redirected.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-id/\">CLIENT ID</a></strong> - Returns the unique client ID of the connection.</summary>\n\n**Syntax:** `CLIENT ID`\n\n**Description:** Returns the unique client ID of the connection.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-info/\">CLIENT INFO</a></strong> - Returns information about the connection.</summary>\n\n**Syntax:** `CLIENT INFO`\n\n**Description:** Returns information about the connection.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-kill/\">CLIENT KILL</a></strong> - Terminates open connections.</summary>\n\n**Syntax:** `CLIENT KILL <ip:port | <[ID client-id] | [TYPE <NORMAL | MASTER |\n  SLAVE | REPLICA | PUBSUB>] | [USER username] | [ADDR ip:port] |\n  [LADDR ip:port] | [SKIPME <YES | NO>] | [MAXAGE maxage]\n  [[ID client-id] | [TYPE <NORMAL | MASTER | SLAVE | REPLICA |\n  PUBSUB>] | [USER username] | [ADDR ip:port] | [LADDR ip:port] |\n  [SKIPME <YES | NO>] | [MAXAGE maxage] ...]>>`\n\n**Description:** Terminates open connections.\n\n**Complexity:** O(N) where N is the number of client connections\n\n**Since:** 2.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-list/\">CLIENT LIST</a></strong> - Lists open connections.</summary>\n\n**Syntax:** `CLIENT LIST [TYPE <NORMAL | MASTER | REPLICA | PUBSUB>]\n  [ID client-id [client-id ...]]`\n\n**Description:** Lists open connections.\n\n**Complexity:** O(N) where N is the number of client connections\n\n**Since:** 2.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-no-evict/\">CLIENT NO-EVICT</a></strong> - Sets the client eviction mode of the connection.</summary>\n\n**Syntax:** `CLIENT NO-EVICT <ON | OFF>`\n\n**Description:** Sets the client eviction mode of the connection.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-no-touch/\">CLIENT NO-TOUCH</a></strong> - Controls whether commands sent by the client affect the LRU/LFU of accessed keys. <span style=\"color: #e74c3c;\">⭐ New in 7.2</span></summary>\n\n**Syntax:** `CLIENT NO-TOUCH <ON | OFF>`\n\n**Description:** Controls whether commands sent by the client affect the LRU/LFU of accessed keys.\n\n**Complexity:** O(1)\n\n**Since:** 7.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-pause/\">CLIENT PAUSE</a></strong> - Suspends commands processing.</summary>\n\n**Syntax:** `CLIENT PAUSE timeout [WRITE | ALL]`\n\n**Description:** Suspends commands processing.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-reply/\">CLIENT REPLY</a></strong> - Instructs the server whether to reply to commands.</summary>\n\n**Syntax:** `CLIENT REPLY <ON | OFF | SKIP>`\n\n**Description:** Instructs the server whether to reply to commands.\n\n**Complexity:** O(1)\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-setinfo/\">CLIENT SETINFO</a></strong> - Sets information specific to the client or connection. <span style=\"color: #e74c3c;\">⭐ New in 7.2</span></summary>\n\n**Syntax:** `CLIENT SETINFO <LIB-NAME libname | LIB-VER libver>`\n\n**Description:** Sets information specific to the client or connection.\n\n**Complexity:** O(1)\n\n**Since:** 7.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-setname/\">CLIENT SETNAME</a></strong> - Sets the connection name.</summary>\n\n**Syntax:** `CLIENT SETNAME connection-name`\n\n**Description:** Sets the connection name.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-tracking/\">CLIENT TRACKING</a></strong> - Controls server-assisted client-side caching for the connection.</summary>\n\n**Syntax:** `CLIENT TRACKING <ON | OFF> [REDIRECT client-id] [PREFIX prefix\n  [PREFIX prefix ...]] [BCAST] [OPTIN] [OPTOUT] [NOLOOP]`\n\n**Description:** Controls server-assisted client-side caching for the connection.\n\n**Complexity:** O(1). Some options may introduce additional complexity.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-trackinginfo/\">CLIENT TRACKINGINFO</a></strong> - Returns information about server-assisted client-side caching for the connection.</summary>\n\n**Syntax:** `CLIENT TRACKINGINFO`\n\n**Description:** Returns information about server-assisted client-side caching for the connection.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-unblock/\">CLIENT UNBLOCK</a></strong> - Unblocks a client blocked by a blocking command from a different connection.</summary>\n\n**Syntax:** `CLIENT UNBLOCK client-id [TIMEOUT | ERROR]`\n\n**Description:** Unblocks a client blocked by a blocking command from a different connection.\n\n**Complexity:** O(log N) where N is the number of client connections\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-unpause/\">CLIENT UNPAUSE</a></strong> - Resumes processing commands from paused clients.</summary>\n\n**Syntax:** `CLIENT UNPAUSE`\n\n**Description:** Resumes processing commands from paused clients.\n\n**Complexity:** O(N) Where N is the number of paused clients\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/echo/\">ECHO</a></strong> - Returns the given string.</summary>\n\n**Syntax:** `ECHO message`\n\n**Description:** Returns the given string.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hello/\">HELLO</a></strong> - Handshakes with the Redis server.</summary>\n\n**Syntax:** `HELLO [protover [AUTH username password] [SETNAME clientname]]`\n\n**Description:** Handshakes with the Redis server.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ping/\">PING</a></strong> - Returns the server's liveliness response.</summary>\n\n**Syntax:** `PING [message]`\n\n**Description:** Returns the server's liveliness response.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/quit/\">QUIT</a></strong> - Closes the connection.</summary>\n\n**Syntax:** `QUIT`\n\n**Description:** Closes the connection.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/reset/\">RESET</a></strong> - Resets the connection.</summary>\n\n**Syntax:** `RESET`\n\n**Description:** Resets the connection.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/select/\">SELECT</a></strong> - Changes the selected database.</summary>\n\n**Syntax:** `SELECT index`\n\n**Description:** Changes the selected database.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>"},{"id":"server-commands","title":"Server commands","role":"content","text":"Server commands provide server management and introspection.\n\n<details>\n<summary><strong><a href=\"/commands/acl-cat/\">ACL CAT</a></strong> - Lists the ACL categories, or the commands inside a category.</summary>\n\n**Syntax:** `ACL CAT [category]`\n\n**Description:** Lists the ACL categories, or the commands inside a category.\n\n**Complexity:** O(1) since the categories and commands are a fixed set.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-deluser/\">ACL DELUSER</a></strong> - Deletes ACL users, and terminates their connections.</summary>\n\n**Syntax:** `ACL DELUSER username [username ...]`\n\n**Description:** Deletes ACL users, and terminates their connections.\n\n**Complexity:** O(1) amortized time considering the typical user.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-dryrun/\">ACL DRYRUN</a></strong> - Simulates the execution of a command by a user, without executing the command.</summary>\n\n**Syntax:** `ACL DRYRUN username command [arg [arg ...]]`\n\n**Description:** Simulates the execution of a command by a user, without executing the command.\n\n**Complexity:** O(1).\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-genpass/\">ACL GENPASS</a></strong> - Generates a pseudorandom, secure password that can be used to identify ACL users.</summary>\n\n**Syntax:** `ACL GENPASS [bits]`\n\n**Description:** Generates a pseudorandom, secure password that can be used to identify ACL users.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-getuser/\">ACL GETUSER</a></strong> - Lists the ACL rules of a user.</summary>\n\n**Syntax:** `ACL GETUSER username`\n\n**Description:** Lists the ACL rules of a user.\n\n**Complexity:** O(N). Where N is the number of password, command and pattern rules that the user has.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-list/\">ACL LIST</a></strong> - Dumps the effective rules in ACL file format.</summary>\n\n**Syntax:** `ACL LIST`\n\n**Description:** Dumps the effective rules in ACL file format.\n\n**Complexity:** O(N). Where N is the number of configured users.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-load/\">ACL LOAD</a></strong> - Reloads the rules from the configured ACL file.</summary>\n\n**Syntax:** `ACL LOAD`\n\n**Description:** Reloads the rules from the configured ACL file.\n\n**Complexity:** O(N). Where N is the number of configured users.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-log/\">ACL LOG</a></strong> - Lists recent security events generated due to ACL rules.</summary>\n\n**Syntax:** `ACL LOG [count | RESET]`\n\n**Description:** Lists recent security events generated due to ACL rules.\n\n**Complexity:** O(N) with N being the number of entries shown.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-save/\">ACL SAVE</a></strong> - Saves the effective ACL rules in the configured ACL file.</summary>\n\n**Syntax:** `ACL SAVE`\n\n**Description:** Saves the effective ACL rules in the configured ACL file.\n\n**Complexity:** O(N). Where N is the number of configured users.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-setuser/\">ACL SETUSER</a></strong> - Creates and modifies an ACL user and its rules.</summary>\n\n**Syntax:** `ACL SETUSER username [rule [rule ...]]`\n\n**Description:** Creates and modifies an ACL user and its rules.\n\n**Complexity:** O(N). Where N is the number of rules provided.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-users/\">ACL USERS</a></strong> - Lists all ACL users.</summary>\n\n**Syntax:** `ACL USERS`\n\n**Description:** Lists all ACL users.\n\n**Complexity:** O(N). Where N is the number of configured users.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-whoami/\">ACL WHOAMI</a></strong> - Returns the authenticated username of the current connection.</summary>\n\n**Syntax:** `ACL WHOAMI`\n\n**Description:** Returns the authenticated username of the current connection.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bgrewriteaof/\">BGREWRITEAOF</a></strong> - Asynchronously rewrites the append-only file to disk.</summary>\n\n**Syntax:** `BGREWRITEAOF`\n\n**Description:** Asynchronously rewrites the append-only file to disk.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bgsave/\">BGSAVE</a></strong> - Asynchronously saves the database(s) to disk.</summary>\n\n**Syntax:** `BGSAVE [SCHEDULE]`\n\n**Description:** Asynchronously saves the database(s) to disk.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command/\">COMMAND</a></strong> - Returns detailed information about all commands.</summary>\n\n**Syntax:** `COMMAND`\n\n**Description:** Returns detailed information about all commands.\n\n**Complexity:** O(N) where N is the total number of Redis commands\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-count/\">COMMAND COUNT</a></strong> - Returns a count of commands.</summary>\n\n**Syntax:** `COMMAND COUNT`\n\n**Description:** Returns a count of commands.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-docs/\">COMMAND DOCS</a></strong> - Returns documentary information about one, multiple or all commands.</summary>\n\n**Syntax:** `COMMAND DOCS [command-name [command-name ...]]`\n\n**Description:** Returns documentary information about one, multiple or all commands.\n\n**Complexity:** O(N) where N is the number of commands to look up\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-getkeys/\">COMMAND GETKEYS</a></strong> - Extracts the key names from an arbitrary command.</summary>\n\n**Syntax:** `COMMAND GETKEYS command [arg [arg ...]]`\n\n**Description:** Extracts the key names from an arbitrary command.\n\n**Complexity:** O(N) where N is the number of arguments to the command\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-getkeysandflags/\">COMMAND GETKEYSANDFLAGS</a></strong> - Extracts the key names and access flags for an arbitrary command.</summary>\n\n**Syntax:** `COMMAND GETKEYSANDFLAGS command [arg [arg ...]]`\n\n**Description:** Extracts the key names and access flags for an arbitrary command.\n\n**Complexity:** O(N) where N is the number of arguments to the command\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-info/\">COMMAND INFO</a></strong> - Returns information about one, multiple or all commands.</summary>\n\n**Syntax:** `COMMAND INFO [command-name [command-name ...]]`\n\n**Description:** Returns information about one, multiple or all commands.\n\n**Complexity:** O(N) where N is the number of commands to look up\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-list/\">COMMAND LIST</a></strong> - Returns a list of command names.</summary>\n\n**Syntax:** `COMMAND LIST [FILTERBY <MODULE module-name | ACLCAT category |\n  PATTERN pattern>]`\n\n**Description:** Returns a list of command names.\n\n**Complexity:** O(N) where N is the total number of Redis commands\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/config-get/\">CONFIG GET</a></strong> - Returns the effective values of configuration parameters.</summary>\n\n**Syntax:** `CONFIG GET parameter [parameter ...]`\n\n**Description:** Returns the effective values of configuration parameters.\n\n**Complexity:** O(N) when N is the number of configuration parameters provided\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/config-resetstat/\">CONFIG RESETSTAT</a></strong> - Resets the server's statistics.</summary>\n\n**Syntax:** `CONFIG RESETSTAT`\n\n**Description:** Resets the server's statistics.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/config-rewrite/\">CONFIG REWRITE</a></strong> - Persists the effective configuration to file.</summary>\n\n**Syntax:** `CONFIG REWRITE`\n\n**Description:** Persists the effective configuration to file.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/config-set/\">CONFIG SET</a></strong> - Sets configuration parameters in-flight.</summary>\n\n**Syntax:** `CONFIG SET parameter value [parameter value ...]`\n\n**Description:** Sets configuration parameters in-flight.\n\n**Complexity:** O(N) when N is the number of configuration parameters provided\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/dbsize/\">DBSIZE</a></strong> - Returns the number of keys in the database.</summary>\n\n**Syntax:** `DBSIZE`\n\n**Description:** Returns the number of keys in the database.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/failover/\">FAILOVER</a></strong> - Starts a coordinated failover from a server to one of its replicas.</summary>\n\n**Syntax:** `FAILOVER [TO host port [FORCE]] [ABORT] [TIMEOUT milliseconds]`\n\n**Description:** Starts a coordinated failover from a server to one of its replicas.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/flushall/\">FLUSHALL</a></strong> - Removes all keys from all databases.</summary>\n\n**Syntax:** `FLUSHALL [ASYNC | SYNC]`\n\n**Description:** Removes all keys from all databases.\n\n**Complexity:** O(N) where N is the total number of keys in all databases\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/flushdb/\">FLUSHDB</a></strong> - Remove all keys from the current database.</summary>\n\n**Syntax:** `FLUSHDB [ASYNC | SYNC]`\n\n**Description:** Remove all keys from the current database.\n\n**Complexity:** O(N) where N is the number of keys in the selected database\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/info/\">INFO</a></strong> - Returns information and statistics about the server.</summary>\n\n**Syntax:** `INFO [section [section ...]]`\n\n**Description:** Returns information and statistics about the server.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lastsave/\">LASTSAVE</a></strong> - Returns the Unix timestamp of the last successful save to disk.</summary>\n\n**Syntax:** `LASTSAVE`\n\n**Description:** Returns the Unix timestamp of the last successful save to disk.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-doctor/\">LATENCY DOCTOR</a></strong> - Returns a human-readable latency analysis report.</summary>\n\n**Syntax:** `LATENCY DOCTOR`\n\n**Description:** Returns a human-readable latency analysis report.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-graph/\">LATENCY GRAPH</a></strong> - Returns a latency graph for an event.</summary>\n\n**Syntax:** `LATENCY GRAPH event`\n\n**Description:** Returns a latency graph for an event.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-histogram/\">LATENCY HISTOGRAM</a></strong> - Returns the cumulative distribution of latencies of a subset or all commands.</summary>\n\n**Syntax:** `LATENCY HISTOGRAM [command [command ...]]`\n\n**Description:** Returns the cumulative distribution of latencies of a subset or all commands.\n\n**Complexity:** O(N) where N is the number of commands with latency information being retrieved.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-history/\">LATENCY HISTORY</a></strong> - Returns timestamp-latency samples for an event.</summary>\n\n**Syntax:** `LATENCY HISTORY event`\n\n**Description:** Returns timestamp-latency samples for an event.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-latest/\">LATENCY LATEST</a></strong> - Returns the latest latency samples for all events.</summary>\n\n**Syntax:** `LATENCY LATEST`\n\n**Description:** Returns the latest latency samples for all events.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-reset/\">LATENCY RESET</a></strong> - Resets the latency data for one or more events.</summary>\n\n**Syntax:** `LATENCY RESET [event [event ...]]`\n\n**Description:** Resets the latency data for one or more events.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lolwut/\">LOLWUT</a></strong> - Displays computer art and the Redis version</summary>\n\n**Syntax:** `LOLWUT [VERSION version]`\n\n**Description:** Displays computer art and the Redis version\n\n**Complexity:** \n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-doctor/\">MEMORY DOCTOR</a></strong> - Outputs a memory problems report.</summary>\n\n**Syntax:** `MEMORY DOCTOR`\n\n**Description:** Outputs a memory problems report.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-malloc-stats/\">MEMORY MALLOC-STATS</a></strong> - Returns the allocator statistics.</summary>\n\n**Syntax:** `MEMORY MALLOC-STATS`\n\n**Description:** Returns the allocator statistics.\n\n**Complexity:** Depends on how much memory is allocated, could be slow\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-purge/\">MEMORY PURGE</a></strong> - Asks the allocator to release memory.</summary>\n\n**Syntax:** `MEMORY PURGE`\n\n**Description:** Asks the allocator to release memory.\n\n**Complexity:** Depends on how much memory is allocated, could be slow\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-stats/\">MEMORY STATS</a></strong> - Returns details about memory usage.</summary>\n\n**Syntax:** `MEMORY STATS`\n\n**Description:** Returns details about memory usage.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-usage/\">MEMORY USAGE</a></strong> - Estimates the memory usage of a key.</summary>\n\n**Syntax:** `MEMORY USAGE key [SAMPLES count]`\n\n**Description:** Estimates the memory usage of a key.\n\n**Complexity:** O(N) where N is the number of samples.\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/module-list/\">MODULE LIST</a></strong> - Returns all loaded modules.</summary>\n\n**Syntax:** `MODULE LIST`\n\n**Description:** Returns all loaded modules.\n\n**Complexity:** O(N) where N is the number of loaded modules.\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/module-load/\">MODULE LOAD</a></strong> - Loads a module.</summary>\n\n**Syntax:** `MODULE LOAD path [arg [arg ...]]`\n\n**Description:** Loads a module.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/module-loadex/\">MODULE LOADEX</a></strong> - Loads a module using extended parameters.</summary>\n\n**Syntax:** `MODULE LOADEX path [CONFIG name value [CONFIG name value ...]]\n  [ARGS args [args ...]]`\n\n**Description:** Loads a module using extended parameters.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/module-unload/\">MODULE UNLOAD</a></strong> - Unloads a module.</summary>\n\n**Syntax:** `MODULE UNLOAD name`\n\n**Description:** Unloads a module.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/monitor/\">MONITOR</a></strong> - Listens for all requests received by the server in real-time.</summary>\n\n**Syntax:** `MONITOR`\n\n**Description:** Listens for all requests received by the server in real-time.\n\n**Complexity:** \n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/psync/\">PSYNC</a></strong> - An internal command used in replication.</summary>\n\n**Syntax:** `PSYNC replicationid offset`\n\n**Description:** An internal command used in replication.\n\n**Complexity:** \n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/replconf/\">REPLCONF</a></strong> - An internal command for configuring the replication stream.</summary>\n\n**Syntax:** `REPLCONF`\n\n**Description:** An internal command for configuring the replication stream.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/replicaof/\">REPLICAOF</a></strong> - Configures a server as replica of another, or promotes it to a master.</summary>\n\n**Syntax:** `REPLICAOF <host port | NO ONE>`\n\n**Description:** Configures a server as replica of another, or promotes it to a master.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/restore-asking/\">RESTORE-ASKING</a></strong> - An internal command for migrating keys in a cluster.</summary>\n\n**Syntax:** `RESTORE-ASKING key ttl serialized-value [REPLACE] [ABSTTL]\n  [IDLETIME seconds] [FREQ frequency]`\n\n**Description:** An internal command for migrating keys in a cluster.\n\n**Complexity:** O(1) to create the new key and additional O(N*M) to reconstruct the serialized value, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1). However for sorted set values the complexity is O(N*M*log(N)) because inserting values into sorted sets is O(log(N)).\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/role/\">ROLE</a></strong> - Returns the replication role.</summary>\n\n**Syntax:** `ROLE`\n\n**Description:** Returns the replication role.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.12\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/save/\">SAVE</a></strong> - Synchronously saves the database(s) to disk.</summary>\n\n**Syntax:** `SAVE`\n\n**Description:** Synchronously saves the database(s) to disk.\n\n**Complexity:** O(N) where N is the total number of keys in all databases\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/shutdown/\">SHUTDOWN</a></strong> - Synchronously saves the database(s) to disk and shuts down the Redis server.</summary>\n\n**Syntax:** `SHUTDOWN [NOSAVE | SAVE] [NOW] [FORCE] [ABORT]`\n\n**Description:** Synchronously saves the database(s) to disk and shuts down the Redis server.\n\n**Complexity:** O(N) when saving, where N is the total number of keys in all databases when saving data, otherwise O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/slaveof/\">SLAVEOF</a></strong> - Sets a Redis server as a replica of another, or promotes it to being a master.</summary>\n\n**Syntax:** `SLAVEOF <host port | NO ONE>`\n\n**Description:** Sets a Redis server as a replica of another, or promotes it to being a master.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/slowlog-get/\">SLOWLOG GET</a></strong> - Returns the slow log's entries.</summary>\n\n**Syntax:** `SLOWLOG GET [count]`\n\n**Description:** Returns the slow log's entries.\n\n**Complexity:** O(N) where N is the number of entries returned\n\n**Since:** 2.2.12\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/slowlog-len/\">SLOWLOG LEN</a></strong> - Returns the number of entries in the slow log.</summary>\n\n**Syntax:** `SLOWLOG LEN`\n\n**Description:** Returns the number of entries in the slow log.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.12\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/slowlog-reset/\">SLOWLOG RESET</a></strong> - Clears all entries from the slow log.</summary>\n\n**Syntax:** `SLOWLOG RESET`\n\n**Description:** Clears all entries from the slow log.\n\n**Complexity:** O(N) where N is the number of entries in the slowlog\n\n**Since:** 2.2.12\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/swapdb/\">SWAPDB</a></strong> - Swaps two Redis databases.</summary>\n\n**Syntax:** `SWAPDB index1 index2`\n\n**Description:** Swaps two Redis databases.\n\n**Complexity:** O(N) where N is the count of clients watching or blocking on keys from both databases.\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sync/\">SYNC</a></strong> - An internal command used in replication.</summary>\n\n**Syntax:** `SYNC`\n\n**Description:** An internal command used in replication.\n\n**Complexity:** \n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/time/\">TIME</a></strong> - Returns the server time.</summary>\n\n**Syntax:** `TIME`\n\n**Description:** Returns the server time.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>"},{"id":"cluster-commands","title":"Cluster commands","role":"content","text":"Cluster commands manage Redis Cluster operations.\n\n<details>\n<summary><strong><a href=\"/commands/asking/\">ASKING</a></strong> - Signals that a cluster client is following an -ASK redirect.</summary>\n\n**Syntax:** `ASKING`\n\n**Description:** Signals that a cluster client is following an -ASK redirect.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-addslots/\">CLUSTER ADDSLOTS</a></strong> - Assigns new hash slots to a node.</summary>\n\n**Syntax:** `CLUSTER ADDSLOTS slot [slot ...]`\n\n**Description:** Assigns new hash slots to a node.\n\n**Complexity:** O(N) where N is the total number of hash slot arguments\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-addslotsrange/\">CLUSTER ADDSLOTSRANGE</a></strong> - Assigns new hash slot ranges to a node.</summary>\n\n**Syntax:** `CLUSTER ADDSLOTSRANGE start-slot end-slot [start-slot end-slot ...]`\n\n**Description:** Assigns new hash slot ranges to a node.\n\n**Complexity:** O(N) where N is the total number of the slots between the start slot and end slot arguments.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-bumpepoch/\">CLUSTER BUMPEPOCH</a></strong> - Advances the cluster config epoch.</summary>\n\n**Syntax:** `CLUSTER BUMPEPOCH`\n\n**Description:** Advances the cluster config epoch.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-count-failure-reports/\">CLUSTER COUNT-FAILURE-REPORTS</a></strong> - Returns the number of active failure reports active for a node.</summary>\n\n**Syntax:** `CLUSTER COUNT-FAILURE-REPORTS node-id`\n\n**Description:** Returns the number of active failure reports active for a node.\n\n**Complexity:** O(N) where N is the number of failure reports\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-countkeysinslot/\">CLUSTER COUNTKEYSINSLOT</a></strong> - Returns the number of keys in a hash slot.</summary>\n\n**Syntax:** `CLUSTER COUNTKEYSINSLOT slot`\n\n**Description:** Returns the number of keys in a hash slot.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-delslots/\">CLUSTER DELSLOTS</a></strong> - Sets hash slots as unbound for a node.</summary>\n\n**Syntax:** `CLUSTER DELSLOTS slot [slot ...]`\n\n**Description:** Sets hash slots as unbound for a node.\n\n**Complexity:** O(N) where N is the total number of hash slot arguments\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-delslotsrange/\">CLUSTER DELSLOTSRANGE</a></strong> - Sets hash slot ranges as unbound for a node.</summary>\n\n**Syntax:** `CLUSTER DELSLOTSRANGE start-slot end-slot [start-slot end-slot ...]`\n\n**Description:** Sets hash slot ranges as unbound for a node.\n\n**Complexity:** O(N) where N is the total number of the slots between the start slot and end slot arguments.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-failover/\">CLUSTER FAILOVER</a></strong> - Forces a replica to perform a manual failover of its master.</summary>\n\n**Syntax:** `CLUSTER FAILOVER [FORCE | TAKEOVER]`\n\n**Description:** Forces a replica to perform a manual failover of its master.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-flushslots/\">CLUSTER FLUSHSLOTS</a></strong> - Deletes all slots information from a node.</summary>\n\n**Syntax:** `CLUSTER FLUSHSLOTS`\n\n**Description:** Deletes all slots information from a node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-forget/\">CLUSTER FORGET</a></strong> - Removes a node from the nodes table.</summary>\n\n**Syntax:** `CLUSTER FORGET node-id`\n\n**Description:** Removes a node from the nodes table.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-getkeysinslot/\">CLUSTER GETKEYSINSLOT</a></strong> - Returns the key names in a hash slot.</summary>\n\n**Syntax:** `CLUSTER GETKEYSINSLOT slot count`\n\n**Description:** Returns the key names in a hash slot.\n\n**Complexity:** O(N) where N is the number of requested keys\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-info/\">CLUSTER INFO</a></strong> - Returns information about the state of a node.</summary>\n\n**Syntax:** `CLUSTER INFO`\n\n**Description:** Returns information about the state of a node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-keyslot/\">CLUSTER KEYSLOT</a></strong> - Returns the hash slot for a key.</summary>\n\n**Syntax:** `CLUSTER KEYSLOT key`\n\n**Description:** Returns the hash slot for a key.\n\n**Complexity:** O(N) where N is the number of bytes in the key\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-links/\">CLUSTER LINKS</a></strong> - Returns a list of all TCP links to and from peer nodes.</summary>\n\n**Syntax:** `CLUSTER LINKS`\n\n**Description:** Returns a list of all TCP links to and from peer nodes.\n\n**Complexity:** O(N) where N is the total number of Cluster nodes\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-meet/\">CLUSTER MEET</a></strong> - Forces a node to handshake with another node.</summary>\n\n**Syntax:** `CLUSTER MEET ip port [cluster-bus-port]`\n\n**Description:** Forces a node to handshake with another node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-myid/\">CLUSTER MYID</a></strong> - Returns the ID of a node.</summary>\n\n**Syntax:** `CLUSTER MYID`\n\n**Description:** Returns the ID of a node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-myshardid/\">CLUSTER MYSHARDID</a></strong> - Returns the shard ID of a node. <span style=\"color: #e74c3c;\">⭐ New in 7.2</span></summary>\n\n**Syntax:** `CLUSTER MYSHARDID`\n\n**Description:** Returns the shard ID of a node.\n\n**Complexity:** O(1)\n\n**Since:** 7.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-nodes/\">CLUSTER NODES</a></strong> - Returns the cluster configuration for a node.</summary>\n\n**Syntax:** `CLUSTER NODES`\n\n**Description:** Returns the cluster configuration for a node.\n\n**Complexity:** O(N) where N is the total number of Cluster nodes\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-replicas/\">CLUSTER REPLICAS</a></strong> - Lists the replica nodes of a master node.</summary>\n\n**Syntax:** `CLUSTER REPLICAS node-id`\n\n**Description:** Lists the replica nodes of a master node.\n\n**Complexity:** O(N) where N is the number of replicas.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-replicate/\">CLUSTER REPLICATE</a></strong> - Configure a node as replica of a master node.</summary>\n\n**Syntax:** `CLUSTER REPLICATE node-id`\n\n**Description:** Configure a node as replica of a master node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-reset/\">CLUSTER RESET</a></strong> - Resets a node.</summary>\n\n**Syntax:** `CLUSTER RESET [HARD | SOFT]`\n\n**Description:** Resets a node.\n\n**Complexity:** O(N) where N is the number of known nodes. The command may execute a FLUSHALL as a side effect.\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-saveconfig/\">CLUSTER SAVECONFIG</a></strong> - Forces a node to save the cluster configuration to disk.</summary>\n\n**Syntax:** `CLUSTER SAVECONFIG`\n\n**Description:** Forces a node to save the cluster configuration to disk.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-set-config-epoch/\">CLUSTER SET-CONFIG-EPOCH</a></strong> - Sets the configuration epoch for a new node.</summary>\n\n**Syntax:** `CLUSTER SET-CONFIG-EPOCH config-epoch`\n\n**Description:** Sets the configuration epoch for a new node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-setslot/\">CLUSTER SETSLOT</a></strong> - Binds a hash slot to a node.</summary>\n\n**Syntax:** `CLUSTER SETSLOT slot <IMPORTING node-id | MIGRATING node-id |\n  NODE node-id | STABLE>`\n\n**Description:** Binds a hash slot to a node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-shards/\">CLUSTER SHARDS</a></strong> - Returns the mapping of cluster slots to shards.</summary>\n\n**Syntax:** `CLUSTER SHARDS`\n\n**Description:** Returns the mapping of cluster slots to shards.\n\n**Complexity:** O(N) where N is the total number of cluster nodes\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-slaves/\">CLUSTER SLAVES</a></strong> - Lists the replica nodes of a master node.</summary>\n\n**Syntax:** `CLUSTER SLAVES node-id`\n\n**Description:** Lists the replica nodes of a master node.\n\n**Complexity:** O(N) where N is the number of replicas.\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-slots/\">CLUSTER SLOTS</a></strong> - Returns the mapping of cluster slots to nodes.</summary>\n\n**Syntax:** `CLUSTER SLOTS`\n\n**Description:** Returns the mapping of cluster slots to nodes.\n\n**Complexity:** O(N) where N is the total number of Cluster nodes\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/readonly/\">READONLY</a></strong> - Enables read-only queries for a connection to a Redis Cluster replica node.</summary>\n\n**Syntax:** `READONLY`\n\n**Description:** Enables read-only queries for a connection to a Redis Cluster replica node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/readwrite/\">READWRITE</a></strong> - Enables read-write queries for a connection to a Reids Cluster replica node.</summary>\n\n**Syntax:** `READWRITE`\n\n**Description:** Enables read-write queries for a connection to a Reids Cluster replica node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>"},{"id":"generic-commands","title":"Generic commands","role":"content","text":"Generic commands work across all data types.\n\n<details>\n<summary><strong><a href=\"/commands/copy/\">COPY</a></strong> - Copies the value of a key to a new key.</summary>\n\n**Syntax:** `COPY source destination [DB destination-db] [REPLACE]`\n\n**Description:** Copies the value of a key to a new key.\n\n**Complexity:** O(N) worst case for collections, where N is the number of nested items. O(1) for string values.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/del/\">DEL</a></strong> - Deletes one or more keys.</summary>\n\n**Syntax:** `DEL key [key ...]`\n\n**Description:** Deletes one or more keys.\n\n**Complexity:** O(N) where N is the number of keys that will be removed. When a key to remove holds a value other than a string, the individual complexity for this key is O(M) where M is the number of elements in the list, set, sorted set or hash. Removing a single key that holds a string value is O(1).\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/dump/\">DUMP</a></strong> - Returns a serialized representation of the value stored at a key.</summary>\n\n**Syntax:** `DUMP key`\n\n**Description:** Returns a serialized representation of the value stored at a key.\n\n**Complexity:** O(1) to access the key and additional O(N*M) to serialize it, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1).\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/exists/\">EXISTS</a></strong> - Determines whether one or more keys exist.</summary>\n\n**Syntax:** `EXISTS key [key ...]`\n\n**Description:** Determines whether one or more keys exist.\n\n**Complexity:** O(N) where N is the number of keys to check.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/expire/\">EXPIRE</a></strong> - Sets the expiration time of a key in seconds.</summary>\n\n**Syntax:** `EXPIRE key seconds [NX | XX | GT | LT]`\n\n**Description:** Sets the expiration time of a key in seconds.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/expireat/\">EXPIREAT</a></strong> - Sets the expiration time of a key to a Unix timestamp.</summary>\n\n**Syntax:** `EXPIREAT key unix-time-seconds [NX | XX | GT | LT]`\n\n**Description:** Sets the expiration time of a key to a Unix timestamp.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/expiretime/\">EXPIRETIME</a></strong> - Returns the expiration time of a key as a Unix timestamp.</summary>\n\n**Syntax:** `EXPIRETIME key`\n\n**Description:** Returns the expiration time of a key as a Unix timestamp.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/keys/\">KEYS</a></strong> - Returns all key names that match a pattern.</summary>\n\n**Syntax:** `KEYS pattern`\n\n**Description:** Returns all key names that match a pattern.\n\n**Complexity:** O(N) with N being the number of keys in the database, under the assumption that the key names in the database and the given pattern have limited length.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/migrate/\">MIGRATE</a></strong> - Atomically transfers a key from one Redis instance to another.</summary>\n\n**Syntax:** `MIGRATE host port <key | \"\"> destination-db timeout [COPY] [REPLACE]\n  [AUTH password | AUTH2 username password] [KEYS key [key ...]]`\n\n**Description:** Atomically transfers a key from one Redis instance to another.\n\n**Complexity:** This command actually executes a DUMP+DEL in the source instance, and a RESTORE in the target instance. See the pages of these commands for time complexity. Also an O(N) data transfer between the two instances is performed.\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/move/\">MOVE</a></strong> - Moves a key to another database.</summary>\n\n**Syntax:** `MOVE key db`\n\n**Description:** Moves a key to another database.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/object-encoding/\">OBJECT ENCODING</a></strong> - Returns the internal encoding of a Redis object.</summary>\n\n**Syntax:** `OBJECT ENCODING key`\n\n**Description:** Returns the internal encoding of a Redis object.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.3\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/object-freq/\">OBJECT FREQ</a></strong> - Returns the logarithmic access frequency counter of a Redis object.</summary>\n\n**Syntax:** `OBJECT FREQ key`\n\n**Description:** Returns the logarithmic access frequency counter of a Redis object.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/object-idletime/\">OBJECT IDLETIME</a></strong> - Returns the time since the last access to a Redis object.</summary>\n\n**Syntax:** `OBJECT IDLETIME key`\n\n**Description:** Returns the time since the last access to a Redis object.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.3\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/object-refcount/\">OBJECT REFCOUNT</a></strong> - Returns the reference count of a value of a key.</summary>\n\n**Syntax:** `OBJECT REFCOUNT key`\n\n**Description:** Returns the reference count of a value of a key.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.3\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/persist/\">PERSIST</a></strong> - Removes the expiration time of a key.</summary>\n\n**Syntax:** `PERSIST key`\n\n**Description:** Removes the expiration time of a key.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pexpire/\">PEXPIRE</a></strong> - Sets the expiration time of a key in milliseconds.</summary>\n\n**Syntax:** `PEXPIRE key milliseconds [NX | XX | GT | LT]`\n\n**Description:** Sets the expiration time of a key in milliseconds.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pexpireat/\">PEXPIREAT</a></strong> - Sets the expiration time of a key to a Unix milliseconds timestamp.</summary>\n\n**Syntax:** `PEXPIREAT key unix-time-milliseconds [NX | XX | GT | LT]`\n\n**Description:** Sets the expiration time of a key to a Unix milliseconds timestamp.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pexpiretime/\">PEXPIRETIME</a></strong> - Returns the expiration time of a key as a Unix milliseconds timestamp.</summary>\n\n**Syntax:** `PEXPIRETIME key`\n\n**Description:** Returns the expiration time of a key as a Unix milliseconds timestamp.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pttl/\">PTTL</a></strong> - Returns the expiration time in milliseconds of a key.</summary>\n\n**Syntax:** `PTTL key`\n\n**Description:** Returns the expiration time in milliseconds of a key.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/randomkey/\">RANDOMKEY</a></strong> - Returns a random key name from the database.</summary>\n\n**Syntax:** `RANDOMKEY`\n\n**Description:** Returns a random key name from the database.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rename/\">RENAME</a></strong> - Renames a key and overwrites the destination.</summary>\n\n**Syntax:** `RENAME key newkey`\n\n**Description:** Renames a key and overwrites the destination.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/renamenx/\">RENAMENX</a></strong> - Renames a key only when the target key name doesn't exist.</summary>\n\n**Syntax:** `RENAMENX key newkey`\n\n**Description:** Renames a key only when the target key name doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/restore/\">RESTORE</a></strong> - Creates a key from the serialized representation of a value.</summary>\n\n**Syntax:** `RESTORE key ttl serialized-value [REPLACE] [ABSTTL]\n  [IDLETIME seconds] [FREQ frequency]`\n\n**Description:** Creates a key from the serialized representation of a value.\n\n**Complexity:** O(1) to create the new key and additional O(N*M) to reconstruct the serialized value, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1). However for sorted set values the complexity is O(N*M*log(N)) because inserting values into sorted sets is O(log(N)).\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-6-2-commands/\">Redis 6.2 Commands Reference</a></strong> - Complete list of all Redis commands available in version 6.2, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 6.2, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-7-2-commands/\">Redis 7.2 Commands Reference</a></strong> - Complete list of all Redis commands available in version 7.2, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 7.2, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-7-4-commands/\">Redis 7.4 Commands Reference</a></strong> - Complete list of all Redis commands available in version 7.4, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 7.4, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-8-0-commands/\">Redis 8.0 Commands Reference</a></strong> - Complete list of all Redis commands available in version 8.0, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 8.0, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-8-2-commands/\">Redis 8.2 Commands Reference</a></strong> - Complete list of all Redis commands available in version 8.2, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 8.2, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-8-4-commands/\">Redis 8.4 Commands Reference</a></strong> - Complete list of all Redis commands available in version 8.4, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 8.4, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/scan/\">SCAN</a></strong> - Iterates over the key names in the database.</summary>\n\n**Syntax:** `SCAN cursor [MATCH pattern] [COUNT count] [TYPE type]`\n\n**Description:** Iterates over the key names in the database.\n\n**Complexity:** O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sort/\">SORT</a></strong> - Sorts the elements in a list, a set, or a sorted set, optionally storing the result.</summary>\n\n**Syntax:** `SORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern\n  ...]] [ASC | DESC] [ALPHA] [STORE destination]`\n\n**Description:** Sorts the elements in a list, a set, or a sorted set, optionally storing the result.\n\n**Complexity:** O(N+M*log(M)) where N is the number of elements in the list or set to sort, and M the number of returned elements. When the elements are not sorted, complexity is O(N).\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sort_ro/\">SORT_RO</a></strong> - Returns the sorted elements of a list, a set, or a sorted set.</summary>\n\n**Syntax:** `SORT RO key [BY pattern] [LIMIT offset count] [GET pattern [GET\n  pattern ...]] [ASC | DESC] [ALPHA]`\n\n**Description:** Returns the sorted elements of a list, a set, or a sorted set.\n\n**Complexity:** O(N+M*log(M)) where N is the number of elements in the list or set to sort, and M the number of returned elements. When the elements are not sorted, complexity is O(N).\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/touch/\">TOUCH</a></strong> - Returns the number of existing keys out of those specified after updating the time they were last accessed.</summary>\n\n**Syntax:** `TOUCH key [key ...]`\n\n**Description:** Returns the number of existing keys out of those specified after updating the time they were last accessed.\n\n**Complexity:** O(N) where N is the number of keys that will be touched.\n\n**Since:** 3.2.1\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ttl/\">TTL</a></strong> - Returns the expiration time in seconds of a key.</summary>\n\n**Syntax:** `TTL key`\n\n**Description:** Returns the expiration time in seconds of a key.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/type/\">TYPE</a></strong> - Determines the type of value stored at a key.</summary>\n\n**Syntax:** `TYPE key`\n\n**Description:** Determines the type of value stored at a key.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/unlink/\">UNLINK</a></strong> - Asynchronously deletes one or more keys.</summary>\n\n**Syntax:** `UNLINK key [key ...]`\n\n**Description:** Asynchronously deletes one or more keys.\n\n**Complexity:** O(1) for each key removed regardless of its size. Then the command does O(N) work in a different thread in order to reclaim memory, where N is the number of allocations the deleted objects where composed of.\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/wait/\">WAIT</a></strong> - Blocks until the asynchronous replication of all preceding write commands sent by the connection is completed.</summary>\n\n**Syntax:** `WAIT numreplicas timeout`\n\n**Description:** Blocks until the asynchronous replication of all preceding write commands sent by the connection is completed.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/waitaof/\">WAITAOF</a></strong> - Blocks until all of the preceding write commands sent by the connection are written to the append-only file of the master and/or replicas. <span style=\"color: #e74c3c;\">⭐ New in 7.2</span></summary>\n\n**Syntax:** `WAITAOF numlocal numreplicas timeout`\n\n**Description:** Blocks until all of the preceding write commands sent by the connection are written to the append-only file of the master and/or replicas.\n\n**Complexity:** O(1)\n\n**Since:** 7.2.0\n\n</details>"}],"examples":[]}
{"schema_version":2,"id":"commands/redis-7-4-commands","title":"Redis 7.4 Commands Reference","url":"https://redis.io/docs/latest/commands/redis-7-4-commands/","summary":"Complete list of all Redis commands available in version 7.4, organized by functional group","tags":["docs","develop","stack","oss","rs","rc","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"8567c15bb8273a449c7b7a2b695d4ae0fa8a411a1a3121ca7f1aea58a512c164","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This page provides a comprehensive reference of all Redis commands available in Redis 7.4, organized by functional group. Each command includes its description and syntax in a collapsible section for easy navigation.\n\n> [!NOTE]\n> Redis 7.4 includes all commands from previous versions plus new commands introduced in 7.4. Commands marked with **⭐ New in 7.4** were added in this release.\n>\n> Command page links take you to the *current implementation* of each command."},{"id":"quick-navigation","title":"Quick Navigation","role":"content","text":"- [String commands](#string-commands)\n- [Hash commands](#hash-commands)\n- [List commands](#list-commands)\n- [Set commands](#set-commands)\n- [Sorted set commands](#sorted-set-commands)\n- [Stream commands](#stream-commands)\n- [Bitmap commands](#bitmap-commands)\n- [HyperLogLog commands](#hyperloglog-commands)\n- [Geospatial commands](#geospatial-commands)\n- [JSON commands](#json-commands)\n- [Search commands](#search-commands)\n- [Time series commands](#time-series-commands)\n- [Pub/Sub commands](#pubsub-commands)\n- [Transaction commands](#transaction-commands)\n- [Scripting commands](#scripting-commands)\n- [Connection commands](#connection-commands)\n- [Server commands](#server-commands)\n- [Cluster commands](#cluster-commands)\n- [Generic commands](#generic-commands)\n\n---"},{"id":"string-commands","title":"String commands","role":"content","text":"String commands operate on string values, the most basic Redis data type.\n\n<details>\n<summary><strong><a href=\"/commands/append/\">APPEND</a></strong> - Appends a string to the value of a key. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `APPEND key value`\n\n**Description:** Appends a string to the value of a key. Creates the key if it doesn't exist.\n\n**Complexity:** O(1). The amortized time complexity is O(1) assuming the appended value is small and the already present value is of any size, since the dynamic string library used by Redis will double the free space available on every reallocation.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/decr/\">DECR</a></strong> - Decrements the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `DECR key`\n\n**Description:** Decrements the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/decrby/\">DECRBY</a></strong> - Decrements a number from the integer value of a key. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `DECRBY key decrement`\n\n**Description:** Decrements a number from the integer value of a key. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/get/\">GET</a></strong> - Returns the string value of a key.</summary>\n\n**Syntax:** `GET key`\n\n**Description:** Returns the string value of a key.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getdel/\">GETDEL</a></strong> - Returns the string value of a key after deleting the key.</summary>\n\n**Syntax:** `GETDEL key`\n\n**Description:** Returns the string value of a key after deleting the key.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getex/\">GETEX</a></strong> - Returns the string value of a key after setting its expiration time.</summary>\n\n**Syntax:** `GETEX key [EX seconds | PX milliseconds | EXAT unix-time-seconds |\n  PXAT unix-time-milliseconds | PERSIST]`\n\n**Description:** Returns the string value of a key after setting its expiration time.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getrange/\">GETRANGE</a></strong> - Returns a substring of the string stored at a key.</summary>\n\n**Syntax:** `GETRANGE key start end`\n\n**Description:** Returns a substring of the string stored at a key.\n\n**Complexity:** O(N) where N is the length of the returned string. The complexity is ultimately determined by the returned length, but because creating a substring from an existing string is very cheap, it can be considered O(1) for small strings.\n\n**Since:** 2.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getset/\">GETSET</a></strong> - Returns the previous string value of a key after setting it to a new value.</summary>\n\n**Syntax:** `GETSET key value`\n\n**Description:** Returns the previous string value of a key after setting it to a new value.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/incr/\">INCR</a></strong> - Increments the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `INCR key`\n\n**Description:** Increments the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/incrby/\">INCRBY</a></strong> - Increments the integer value of a key by a number. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `INCRBY key increment`\n\n**Description:** Increments the integer value of a key by a number. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/incrbyfloat/\">INCRBYFLOAT</a></strong> - Increment the floating point value of a key by a number. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `INCRBYFLOAT key increment`\n\n**Description:** Increment the floating point value of a key by a number. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lcs/\">LCS</a></strong> - Finds the longest common substring.</summary>\n\n**Syntax:** `LCS key1 key2 [LEN] [IDX] [MINMATCHLEN min-match-len] [WITHMATCHLEN]`\n\n**Description:** Finds the longest common substring.\n\n**Complexity:** O(N*M) where N and M are the lengths of s1 and s2, respectively\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/mget/\">MGET</a></strong> - Atomically returns the string values of one or more keys.</summary>\n\n**Syntax:** `MGET key [key ...]`\n\n**Description:** Atomically returns the string values of one or more keys.\n\n**Complexity:** O(N) where N is the number of keys to retrieve.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/mset/\">MSET</a></strong> - Atomically creates or modifies the string values of one or more keys.</summary>\n\n**Syntax:** `MSET key value [key value ...]`\n\n**Description:** Atomically creates or modifies the string values of one or more keys.\n\n**Complexity:** O(N) where N is the number of keys to set.\n\n**Since:** 1.0.1\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/msetnx/\">MSETNX</a></strong> - Atomically modifies the string values of one or more keys only when all keys don't exist.</summary>\n\n**Syntax:** `MSETNX key value [key value ...]`\n\n**Description:** Atomically modifies the string values of one or more keys only when all keys don't exist.\n\n**Complexity:** O(N) where N is the number of keys to set.\n\n**Since:** 1.0.1\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/psetex/\">PSETEX</a></strong> - Sets both string value and expiration time in milliseconds of a key. The key is created if it doesn't exist.</summary>\n\n**Syntax:** `PSETEX key milliseconds value`\n\n**Description:** Sets both string value and expiration time in milliseconds of a key. The key is created if it doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/set/\">SET</a></strong> - Sets the string value of a key, ignoring its type. The key is created if it doesn't exist.</summary>\n\n**Syntax:** `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]`\n\n**Description:** Sets the string value of a key, ignoring its type. The key is created if it doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/setex/\">SETEX</a></strong> - Sets the string value and expiration time of a key. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `SETEX key seconds value`\n\n**Description:** Sets the string value and expiration time of a key. Creates the key if it doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/setnx/\">SETNX</a></strong> - Set the string value of a key only when the key doesn't exist.</summary>\n\n**Syntax:** `SETNX key value`\n\n**Description:** Set the string value of a key only when the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/setrange/\">SETRANGE</a></strong> - Overwrites a part of a string value with another by an offset. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `SETRANGE key offset value`\n\n**Description:** Overwrites a part of a string value with another by an offset. Creates the key if it doesn't exist.\n\n**Complexity:** O(1), not counting the time taken to copy the new string in place. Usually, this string is very small so the amortized complexity is O(1). Otherwise, complexity is O(M) with M being the length of the value argument.\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/strlen/\">STRLEN</a></strong> - Returns the length of a string value.</summary>\n\n**Syntax:** `STRLEN key`\n\n**Description:** Returns the length of a string value.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/substr/\">SUBSTR</a></strong> - Returns a substring from a string value.</summary>\n\n**Syntax:** `SUBSTR key start end`\n\n**Description:** Returns a substring from a string value.\n\n**Complexity:** O(N) where N is the length of the returned string. The complexity is ultimately determined by the returned length, but because creating a substring from an existing string is very cheap, it can be considered O(1) for small strings.\n\n**Since:** 1.0.0\n\n</details>"},{"id":"hash-commands","title":"Hash commands","role":"content","text":"Hash commands operate on hash data structures, which map string fields to string values.\n\n<details>\n<summary><strong><a href=\"/commands/hdel/\">HDEL</a></strong> - Deletes one or more fields and their values from a hash. Deletes the hash if no fields remain.</summary>\n\n**Syntax:** `HDEL key field [field ...]`\n\n**Description:** Deletes one or more fields and their values from a hash. Deletes the hash if no fields remain.\n\n**Complexity:** O(N) where N is the number of fields to be removed.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hexists/\">HEXISTS</a></strong> - Determines whether a field exists in a hash.</summary>\n\n**Syntax:** `HEXISTS key field`\n\n**Description:** Determines whether a field exists in a hash.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hexpire/\">HEXPIRE</a></strong> - Set expiry for hash field using relative time to expire (seconds) <span style=\"color: #e74c3c;\">⭐ New in 7.4</span></summary>\n\n**Syntax:** `HEXPIRE key seconds [NX | XX | GT | LT] FIELDS numfields field\n  [field ...]`\n\n**Description:** Set expiry for hash field using relative time to expire (seconds)\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hexpireat/\">HEXPIREAT</a></strong> - Set expiry for hash field using an absolute Unix timestamp (seconds) <span style=\"color: #e74c3c;\">⭐ New in 7.4</span></summary>\n\n**Syntax:** `HEXPIREAT key unix-time-seconds [NX | XX | GT | LT] FIELDS numfields\n  field [field ...]`\n\n**Description:** Set expiry for hash field using an absolute Unix timestamp (seconds)\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hexpiretime/\">HEXPIRETIME</a></strong> - Returns the expiration time of a hash field as a Unix timestamp, in seconds. <span style=\"color: #e74c3c;\">⭐ New in 7.4</span></summary>\n\n**Syntax:** `HEXPIRETIME key FIELDS numfields field [field ...]`\n\n**Description:** Returns the expiration time of a hash field as a Unix timestamp, in seconds.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hget/\">HGET</a></strong> - Returns the value of a field in a hash.</summary>\n\n**Syntax:** `HGET key field`\n\n**Description:** Returns the value of a field in a hash.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hgetall/\">HGETALL</a></strong> - Returns all fields and values in a hash.</summary>\n\n**Syntax:** `HGETALL key`\n\n**Description:** Returns all fields and values in a hash.\n\n**Complexity:** O(N) where N is the size of the hash.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hincrby/\">HINCRBY</a></strong> - Increments the integer value of a field in a hash by a number. Uses 0 as initial value if the field doesn't exist.</summary>\n\n**Syntax:** `HINCRBY key field increment`\n\n**Description:** Increments the integer value of a field in a hash by a number. Uses 0 as initial value if the field doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hincrbyfloat/\">HINCRBYFLOAT</a></strong> - Increments the floating point value of a field by a number. Uses 0 as initial value if the field doesn't exist.</summary>\n\n**Syntax:** `HINCRBYFLOAT key field increment`\n\n**Description:** Increments the floating point value of a field by a number. Uses 0 as initial value if the field doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hkeys/\">HKEYS</a></strong> - Returns all fields in a hash.</summary>\n\n**Syntax:** `HKEYS key`\n\n**Description:** Returns all fields in a hash.\n\n**Complexity:** O(N) where N is the size of the hash.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hlen/\">HLEN</a></strong> - Returns the number of fields in a hash.</summary>\n\n**Syntax:** `HLEN key`\n\n**Description:** Returns the number of fields in a hash.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hmget/\">HMGET</a></strong> - Returns the values of all fields in a hash.</summary>\n\n**Syntax:** `HMGET key field [field ...]`\n\n**Description:** Returns the values of all fields in a hash.\n\n**Complexity:** O(N) where N is the number of fields being requested.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hmset/\">HMSET</a></strong> - Sets the values of multiple fields.</summary>\n\n**Syntax:** `HMSET key field value [field value ...]`\n\n**Description:** Sets the values of multiple fields.\n\n**Complexity:** O(N) where N is the number of fields being set.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hpersist/\">HPERSIST</a></strong> - Removes the expiration time for each specified field <span style=\"color: #e74c3c;\">⭐ New in 7.4</span></summary>\n\n**Syntax:** `HPERSIST key FIELDS numfields field [field ...]`\n\n**Description:** Removes the expiration time for each specified field\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hpexpire/\">HPEXPIRE</a></strong> - Set expiry for hash field using relative time to expire (milliseconds) <span style=\"color: #e74c3c;\">⭐ New in 7.4</span></summary>\n\n**Syntax:** `HPEXPIRE key milliseconds [NX | XX | GT | LT] FIELDS numfields field\n  [field ...]`\n\n**Description:** Set expiry for hash field using relative time to expire (milliseconds)\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hpexpireat/\">HPEXPIREAT</a></strong> - Set expiry for hash field using an absolute Unix timestamp (milliseconds) <span style=\"color: #e74c3c;\">⭐ New in 7.4</span></summary>\n\n**Syntax:** `HPEXPIREAT key unix-time-milliseconds [NX | XX | GT | LT]\n  FIELDS numfields field [field ...]`\n\n**Description:** Set expiry for hash field using an absolute Unix timestamp (milliseconds)\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hpexpiretime/\">HPEXPIRETIME</a></strong> - Returns the expiration time of a hash field as a Unix timestamp, in msec. <span style=\"color: #e74c3c;\">⭐ New in 7.4</span></summary>\n\n**Syntax:** `HPEXPIRETIME key FIELDS numfields field [field ...]`\n\n**Description:** Returns the expiration time of a hash field as a Unix timestamp, in msec.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hpttl/\">HPTTL</a></strong> - Returns the TTL in milliseconds of a hash field. <span style=\"color: #e74c3c;\">⭐ New in 7.4</span></summary>\n\n**Syntax:** `HPTTL key FIELDS numfields field [field ...]`\n\n**Description:** Returns the TTL in milliseconds of a hash field.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hrandfield/\">HRANDFIELD</a></strong> - Returns one or more random fields from a hash.</summary>\n\n**Syntax:** `HRANDFIELD key [count [WITHVALUES]]`\n\n**Description:** Returns one or more random fields from a hash.\n\n**Complexity:** O(N) where N is the number of fields returned\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hscan/\">HSCAN</a></strong> - Iterates over fields and values of a hash.</summary>\n\n**Syntax:** `HSCAN key cursor [MATCH pattern] [COUNT count] [NOVALUES]`\n\n**Description:** Iterates over fields and values of a hash.\n\n**Complexity:** O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hset/\">HSET</a></strong> - Creates or modifies the value of a field in a hash.</summary>\n\n**Syntax:** `HSET key field value [field value ...]`\n\n**Description:** Creates or modifies the value of a field in a hash.\n\n**Complexity:** O(1) for each field/value pair added, so O(N) to add N field/value pairs when the command is called with multiple field/value pairs.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hsetnx/\">HSETNX</a></strong> - Sets the value of a field in a hash only when the field doesn't exist.</summary>\n\n**Syntax:** `HSETNX key field value`\n\n**Description:** Sets the value of a field in a hash only when the field doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hstrlen/\">HSTRLEN</a></strong> - Returns the length of the value of a field.</summary>\n\n**Syntax:** `HSTRLEN key field`\n\n**Description:** Returns the length of the value of a field.\n\n**Complexity:** O(1)\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/httl/\">HTTL</a></strong> - Returns the TTL in seconds of a hash field. <span style=\"color: #e74c3c;\">⭐ New in 7.4</span></summary>\n\n**Syntax:** `HTTL key FIELDS numfields field [field ...]`\n\n**Description:** Returns the TTL in seconds of a hash field.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hvals/\">HVALS</a></strong> - Returns all values in a hash.</summary>\n\n**Syntax:** `HVALS key`\n\n**Description:** Returns all values in a hash.\n\n**Complexity:** O(N) where N is the size of the hash.\n\n**Since:** 2.0.0\n\n</details>"},{"id":"list-commands","title":"List commands","role":"content","text":"List commands operate on lists of strings, ordered by insertion order.\n\n<details>\n<summary><strong><a href=\"/commands/blmove/\">BLMOVE</a></strong> - Pops an element from a list, pushes it to another list and returns it. Blocks until an element is available otherwise. Deletes the list if the last element was moved.</summary>\n\n**Syntax:** `BLMOVE source destination <LEFT | RIGHT> <LEFT | RIGHT> timeout`\n\n**Description:** Pops an element from a list, pushes it to another list and returns it. Blocks until an element is available otherwise. Deletes the list if the last element was moved.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/blmpop/\">BLMPOP</a></strong> - Pops the first element from one of multiple lists. Blocks until an element is available otherwise. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `BLMPOP timeout numkeys key [key ...] <LEFT | RIGHT> [COUNT count]`\n\n**Description:** Pops the first element from one of multiple lists. Blocks until an element is available otherwise. Deletes the list if the last element was popped.\n\n**Complexity:** O(N+M) where N is the number of provided keys and M is the number of elements returned.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/blpop/\">BLPOP</a></strong> - Removes and returns the first element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `BLPOP key [key ...] timeout`\n\n**Description:** Removes and returns the first element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.\n\n**Complexity:** O(N) where N is the number of provided keys.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/brpop/\">BRPOP</a></strong> - Removes and returns the last element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `BRPOP key [key ...] timeout`\n\n**Description:** Removes and returns the last element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.\n\n**Complexity:** O(N) where N is the number of provided keys.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/brpoplpush/\">BRPOPLPUSH</a></strong> - Pops an element from a list, pushes it to another list and returns it. Block until an element is available otherwise. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `BRPOPLPUSH source destination timeout`\n\n**Description:** Pops an element from a list, pushes it to another list and returns it. Block until an element is available otherwise. Deletes the list if the last element was popped.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lindex/\">LINDEX</a></strong> - Returns an element from a list by its index.</summary>\n\n**Syntax:** `LINDEX key index`\n\n**Description:** Returns an element from a list by its index.\n\n**Complexity:** O(N) where N is the number of elements to traverse to get to the element at index. This makes asking for the first or the last element of the list O(1).\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/linsert/\">LINSERT</a></strong> - Inserts an element before or after another element in a list.</summary>\n\n**Syntax:** `LINSERT key <BEFORE | AFTER> pivot element`\n\n**Description:** Inserts an element before or after another element in a list.\n\n**Complexity:** O(N) where N is the number of elements to traverse before seeing the value pivot. This means that inserting somewhere on the left end on the list (head) can be considered O(1) and inserting somewhere on the right end (tail) is O(N).\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/llen/\">LLEN</a></strong> - Returns the length of a list.</summary>\n\n**Syntax:** `LLEN key`\n\n**Description:** Returns the length of a list.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lmove/\">LMOVE</a></strong> - Returns an element after popping it from one list and pushing it to another. Deletes the list if the last element was moved.</summary>\n\n**Syntax:** `LMOVE source destination <LEFT | RIGHT> <LEFT | RIGHT>`\n\n**Description:** Returns an element after popping it from one list and pushing it to another. Deletes the list if the last element was moved.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lmpop/\">LMPOP</a></strong> - Returns multiple elements from a list after removing them. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `LMPOP numkeys key [key ...] <LEFT | RIGHT> [COUNT count]`\n\n**Description:** Returns multiple elements from a list after removing them. Deletes the list if the last element was popped.\n\n**Complexity:** O(N+M) where N is the number of provided keys and M is the number of elements returned.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lpop/\">LPOP</a></strong> - Returns the first elements in a list after removing it. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `LPOP key [count]`\n\n**Description:** Returns the first elements in a list after removing it. Deletes the list if the last element was popped.\n\n**Complexity:** O(N) where N is the number of elements returned\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lpos/\">LPOS</a></strong> - Returns the index of matching elements in a list.</summary>\n\n**Syntax:** `LPOS key element [RANK rank] [COUNT num-matches] [MAXLEN len]`\n\n**Description:** Returns the index of matching elements in a list.\n\n**Complexity:** O(N) where N is the number of elements in the list, for the average case. When searching for elements near the head or the tail of the list, or when the MAXLEN option is provided, the command may run in constant time.\n\n**Since:** 6.0.6\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lpush/\">LPUSH</a></strong> - Prepends one or more elements to a list. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `LPUSH key element [element ...]`\n\n**Description:** Prepends one or more elements to a list. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lpushx/\">LPUSHX</a></strong> - Prepends one or more elements to a list only when the list exists.</summary>\n\n**Syntax:** `LPUSHX key element [element ...]`\n\n**Description:** Prepends one or more elements to a list only when the list exists.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lrange/\">LRANGE</a></strong> - Returns a range of elements from a list.</summary>\n\n**Syntax:** `LRANGE key start stop`\n\n**Description:** Returns a range of elements from a list.\n\n**Complexity:** O(S+N) where S is the distance of start offset from HEAD for small lists, from nearest end (HEAD or TAIL) for large lists; and N is the number of elements in the specified range.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lrem/\">LREM</a></strong> - Removes elements from a list. Deletes the list if the last element was removed.</summary>\n\n**Syntax:** `LREM key count element`\n\n**Description:** Removes elements from a list. Deletes the list if the last element was removed.\n\n**Complexity:** O(N+M) where N is the length of the list and M is the number of elements removed.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lset/\">LSET</a></strong> - Sets the value of an element in a list by its index.</summary>\n\n**Syntax:** `LSET key index element`\n\n**Description:** Sets the value of an element in a list by its index.\n\n**Complexity:** O(N) where N is the length of the list. Setting either the first or the last element of the list is O(1).\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ltrim/\">LTRIM</a></strong> - Removes elements from both ends a list. Deletes the list if all elements were trimmed.</summary>\n\n**Syntax:** `LTRIM key start stop`\n\n**Description:** Removes elements from both ends a list. Deletes the list if all elements were trimmed.\n\n**Complexity:** O(N) where N is the number of elements to be removed by the operation.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rpop/\">RPOP</a></strong> - Returns and removes the last elements of a list. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `RPOP key [count]`\n\n**Description:** Returns and removes the last elements of a list. Deletes the list if the last element was popped.\n\n**Complexity:** O(N) where N is the number of elements returned\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rpoplpush/\">RPOPLPUSH</a></strong> - Returns the last element of a list after removing and pushing it to another list. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `RPOPLPUSH source destination`\n\n**Description:** Returns the last element of a list after removing and pushing it to another list. Deletes the list if the last element was popped.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rpush/\">RPUSH</a></strong> - Appends one or more elements to a list. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `RPUSH key element [element ...]`\n\n**Description:** Appends one or more elements to a list. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rpushx/\">RPUSHX</a></strong> - Appends an element to a list only when the list exists.</summary>\n\n**Syntax:** `RPUSHX key element [element ...]`\n\n**Description:** Appends an element to a list only when the list exists.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 2.2.0\n\n</details>"},{"id":"set-commands","title":"Set commands","role":"content","text":"Set commands operate on unordered collections of unique strings.\n\n<details>\n<summary><strong><a href=\"/commands/sadd/\">SADD</a></strong> - Adds one or more members to a set. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `SADD key member [member ...]`\n\n**Description:** Adds one or more members to a set. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/scard/\">SCARD</a></strong> - Returns the number of members in a set.</summary>\n\n**Syntax:** `SCARD key`\n\n**Description:** Returns the number of members in a set.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sdiff/\">SDIFF</a></strong> - Returns the difference of multiple sets.</summary>\n\n**Syntax:** `SDIFF key [key ...]`\n\n**Description:** Returns the difference of multiple sets.\n\n**Complexity:** O(N) where N is the total number of elements in all given sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sdiffstore/\">SDIFFSTORE</a></strong> - Stores the difference of multiple sets in a key.</summary>\n\n**Syntax:** `SDIFFSTORE destination key [key ...]`\n\n**Description:** Stores the difference of multiple sets in a key.\n\n**Complexity:** O(N) where N is the total number of elements in all given sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sinter/\">SINTER</a></strong> - Returns the intersect of multiple sets.</summary>\n\n**Syntax:** `SINTER key [key ...]`\n\n**Description:** Returns the intersect of multiple sets.\n\n**Complexity:** O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sintercard/\">SINTERCARD</a></strong> - Returns the number of members of the intersect of multiple sets.</summary>\n\n**Syntax:** `SINTERCARD numkeys key [key ...] [LIMIT limit]`\n\n**Description:** Returns the number of members of the intersect of multiple sets.\n\n**Complexity:** O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sinterstore/\">SINTERSTORE</a></strong> - Stores the intersect of multiple sets in a key.</summary>\n\n**Syntax:** `SINTERSTORE destination key [key ...]`\n\n**Description:** Stores the intersect of multiple sets in a key.\n\n**Complexity:** O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sismember/\">SISMEMBER</a></strong> - Determines whether a member belongs to a set.</summary>\n\n**Syntax:** `SISMEMBER key member`\n\n**Description:** Determines whether a member belongs to a set.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/smembers/\">SMEMBERS</a></strong> - Returns all members of a set.</summary>\n\n**Syntax:** `SMEMBERS key`\n\n**Description:** Returns all members of a set.\n\n**Complexity:** O(N) where N is the set cardinality.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/smismember/\">SMISMEMBER</a></strong> - Determines whether multiple members belong to a set.</summary>\n\n**Syntax:** `SMISMEMBER key member [member ...]`\n\n**Description:** Determines whether multiple members belong to a set.\n\n**Complexity:** O(N) where N is the number of elements being checked for membership\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/smove/\">SMOVE</a></strong> - Moves a member from one set to another.</summary>\n\n**Syntax:** `SMOVE source destination member`\n\n**Description:** Moves a member from one set to another.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/spop/\">SPOP</a></strong> - Returns one or more random members from a set after removing them. Deletes the set if the last member was popped.</summary>\n\n**Syntax:** `SPOP key [count]`\n\n**Description:** Returns one or more random members from a set after removing them. Deletes the set if the last member was popped.\n\n**Complexity:** Without the count argument O(1), otherwise O(N) where N is the value of the passed count.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/srandmember/\">SRANDMEMBER</a></strong> - Get one or multiple random members from a set</summary>\n\n**Syntax:** `SRANDMEMBER key [count]`\n\n**Description:** Get one or multiple random members from a set\n\n**Complexity:** Without the count argument O(1), otherwise O(N) where N is the absolute value of the passed count.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/srem/\">SREM</a></strong> - Removes one or more members from a set. Deletes the set if the last member was removed.</summary>\n\n**Syntax:** `SREM key member [member ...]`\n\n**Description:** Removes one or more members from a set. Deletes the set if the last member was removed.\n\n**Complexity:** O(N) where N is the number of members to be removed.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sscan/\">SSCAN</a></strong> - Iterates over members of a set.</summary>\n\n**Syntax:** `SSCAN key cursor [MATCH pattern] [COUNT count]`\n\n**Description:** Iterates over members of a set.\n\n**Complexity:** O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sunion/\">SUNION</a></strong> - Returns the union of multiple sets.</summary>\n\n**Syntax:** `SUNION key [key ...]`\n\n**Description:** Returns the union of multiple sets.\n\n**Complexity:** O(N) where N is the total number of elements in all given sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sunionstore/\">SUNIONSTORE</a></strong> - Stores the union of multiple sets in a key.</summary>\n\n**Syntax:** `SUNIONSTORE destination key [key ...]`\n\n**Description:** Stores the union of multiple sets in a key.\n\n**Complexity:** O(N) where N is the total number of elements in all given sets.\n\n**Since:** 1.0.0\n\n</details>"},{"id":"sorted-set-commands","title":"Sorted set commands","role":"content","text":"Sorted set commands operate on sets of unique strings ordered by a score.\n\n<details>\n<summary><strong><a href=\"/commands/bzmpop/\">BZMPOP</a></strong> - Removes and returns a member by score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.</summary>\n\n**Syntax:** `BZMPOP timeout numkeys key [key ...] <MIN | MAX> [COUNT count]`\n\n**Description:** Removes and returns a member by score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.\n\n**Complexity:** O(K) + O(M*log(N)) where K is the number of provided keys, N being the number of elements in the sorted set, and M being the number of elements popped.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bzpopmax/\">BZPOPMAX</a></strong> - Removes and returns the member with the highest score from one or more sorted sets. Blocks until a member available otherwise.  Deletes the sorted set if the last element was popped.</summary>\n\n**Syntax:** `BZPOPMAX key [key ...] timeout`\n\n**Description:** Removes and returns the member with the highest score from one or more sorted sets. Blocks until a member available otherwise.  Deletes the sorted set if the last element was popped.\n\n**Complexity:** O(log(N)) with N being the number of elements in the sorted set.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bzpopmin/\">BZPOPMIN</a></strong> - Removes and returns the member with the lowest score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.</summary>\n\n**Syntax:** `BZPOPMIN key [key ...] timeout`\n\n**Description:** Removes and returns the member with the lowest score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.\n\n**Complexity:** O(log(N)) with N being the number of elements in the sorted set.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zadd/\">ZADD</a></strong> - Adds one or more members to a sorted set, or updates their scores. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `ZADD key [NX | XX] [GT | LT] [CH] [INCR] score member [score member\n  ...]`\n\n**Description:** Adds one or more members to a sorted set, or updates their scores. Creates the key if it doesn't exist.\n\n**Complexity:** O(log(N)) for each item added, where N is the number of elements in the sorted set.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zcard/\">ZCARD</a></strong> - Returns the number of members in a sorted set.</summary>\n\n**Syntax:** `ZCARD key`\n\n**Description:** Returns the number of members in a sorted set.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zcount/\">ZCOUNT</a></strong> - Returns the count of members in a sorted set that have scores within a range.</summary>\n\n**Syntax:** `ZCOUNT key min max`\n\n**Description:** Returns the count of members in a sorted set that have scores within a range.\n\n**Complexity:** O(log(N)) with N being the number of elements in the sorted set.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zdiff/\">ZDIFF</a></strong> - Returns the difference between multiple sorted sets.</summary>\n\n**Syntax:** `ZDIFF numkeys key [key ...] [WITHSCORES]`\n\n**Description:** Returns the difference between multiple sorted sets.\n\n**Complexity:** O(L + (N-K)log(N)) worst case where L is the total number of elements in all the sets, N is the size of the first set, and K is the size of the result set.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zdiffstore/\">ZDIFFSTORE</a></strong> - Stores the difference of multiple sorted sets in a key.</summary>\n\n**Syntax:** `ZDIFFSTORE destination numkeys key [key ...]`\n\n**Description:** Stores the difference of multiple sorted sets in a key.\n\n**Complexity:** O(L + (N-K)log(N)) worst case where L is the total number of elements in all the sets, N is the size of the first set, and K is the size of the result set.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zincrby/\">ZINCRBY</a></strong> - Increments the score of a member in a sorted set.</summary>\n\n**Syntax:** `ZINCRBY key increment member`\n\n**Description:** Increments the score of a member in a sorted set.\n\n**Complexity:** O(log(N)) where N is the number of elements in the sorted set.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zinter/\">ZINTER</a></strong> - Returns the intersect of multiple sorted sets.</summary>\n\n**Syntax:** `ZINTER numkeys key [key ...] [WEIGHTS weight [weight ...]]\n  [AGGREGATE <SUM | MIN | MAX>] [WITHSCORES]`\n\n**Description:** Returns the intersect of multiple sorted sets.\n\n**Complexity:** O(N*K)+O(M*log(M)) worst case with N being the smallest input sorted set, K being the number of input sorted sets and M being the number of elements in the resulting sorted set.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zintercard/\">ZINTERCARD</a></strong> - Returns the number of members of the intersect of multiple sorted sets.</summary>\n\n**Syntax:** `ZINTERCARD numkeys key [key ...] [LIMIT limit]`\n\n**Description:** Returns the number of members of the intersect of multiple sorted sets.\n\n**Complexity:** O(N*K) worst case with N being the smallest input sorted set, K being the number of input sorted sets.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zinterstore/\">ZINTERSTORE</a></strong> - Stores the intersect of multiple sorted sets in a key.</summary>\n\n**Syntax:** `ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight\n  [weight ...]] [AGGREGATE <SUM | MIN | MAX>]`\n\n**Description:** Stores the intersect of multiple sorted sets in a key.\n\n**Complexity:** O(N*K)+O(M*log(M)) worst case with N being the smallest input sorted set, K being the number of input sorted sets and M being the number of elements in the resulting sorted set.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zlexcount/\">ZLEXCOUNT</a></strong> - Returns the number of members in a sorted set within a lexicographical range.</summary>\n\n**Syntax:** `ZLEXCOUNT key min max`\n\n**Description:** Returns the number of members in a sorted set within a lexicographical range.\n\n**Complexity:** O(log(N)) with N being the number of elements in the sorted set.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zmpop/\">ZMPOP</a></strong> - Returns the highest- or lowest-scoring members from one or more sorted sets after removing them. Deletes the sorted set if the last member was popped.</summary>\n\n**Syntax:** `ZMPOP numkeys key [key ...] <MIN | MAX> [COUNT count]`\n\n**Description:** Returns the highest- or lowest-scoring members from one or more sorted sets after removing them. Deletes the sorted set if the last member was popped.\n\n**Complexity:** O(K) + O(M*log(N)) where K is the number of provided keys, N being the number of elements in the sorted set, and M being the number of elements popped.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zmscore/\">ZMSCORE</a></strong> - Returns the score of one or more members in a sorted set.</summary>\n\n**Syntax:** `ZMSCORE key member [member ...]`\n\n**Description:** Returns the score of one or more members in a sorted set.\n\n**Complexity:** O(N) where N is the number of members being requested.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zpopmax/\">ZPOPMAX</a></strong> - Returns the highest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.</summary>\n\n**Syntax:** `ZPOPMAX key [count]`\n\n**Description:** Returns the highest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.\n\n**Complexity:** O(log(N)*M) with N being the number of elements in the sorted set, and M being the number of elements popped.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zpopmin/\">ZPOPMIN</a></strong> - Returns the lowest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.</summary>\n\n**Syntax:** `ZPOPMIN key [count]`\n\n**Description:** Returns the lowest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.\n\n**Complexity:** O(log(N)*M) with N being the number of elements in the sorted set, and M being the number of elements popped.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrandmember/\">ZRANDMEMBER</a></strong> - Returns one or more random members from a sorted set.</summary>\n\n**Syntax:** `ZRANDMEMBER key [count [WITHSCORES]]`\n\n**Description:** Returns one or more random members from a sorted set.\n\n**Complexity:** O(N) where N is the number of members returned\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrange/\">ZRANGE</a></strong> - Returns members in a sorted set within a range of indexes.</summary>\n\n**Syntax:** `ZRANGE key start stop [BYSCORE | BYLEX] [REV] [LIMIT offset count]\n  [WITHSCORES]`\n\n**Description:** Returns members in a sorted set within a range of indexes.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements returned.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrangebylex/\">ZRANGEBYLEX</a></strong> - Returns members in a sorted set within a lexicographical range.</summary>\n\n**Syntax:** `ZRANGEBYLEX key min max [LIMIT offset count]`\n\n**Description:** Returns members in a sorted set within a lexicographical range.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrangebyscore/\">ZRANGEBYSCORE</a></strong> - Returns members in a sorted set within a range of scores.</summary>\n\n**Syntax:** `ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]`\n\n**Description:** Returns members in a sorted set within a range of scores.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).\n\n**Since:** 1.0.5\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrangestore/\">ZRANGESTORE</a></strong> - Stores a range of members from sorted set in a key.</summary>\n\n**Syntax:** `ZRANGESTORE dst src min max [BYSCORE | BYLEX] [REV] [LIMIT offset\n  count]`\n\n**Description:** Stores a range of members from sorted set in a key.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements stored into the destination key.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrank/\">ZRANK</a></strong> - Returns the index of a member in a sorted set ordered by ascending scores.</summary>\n\n**Syntax:** `ZRANK key member [WITHSCORE]`\n\n**Description:** Returns the index of a member in a sorted set ordered by ascending scores.\n\n**Complexity:** O(log(N))\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrem/\">ZREM</a></strong> - Removes one or more members from a sorted set. Deletes the sorted set if all members were removed.</summary>\n\n**Syntax:** `ZREM key member [member ...]`\n\n**Description:** Removes one or more members from a sorted set. Deletes the sorted set if all members were removed.\n\n**Complexity:** O(M*log(N)) with N being the number of elements in the sorted set and M the number of elements to be removed.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zremrangebylex/\">ZREMRANGEBYLEX</a></strong> - Removes members in a sorted set within a lexicographical range. Deletes the sorted set if all members were removed.</summary>\n\n**Syntax:** `ZREMRANGEBYLEX key min max`\n\n**Description:** Removes members in a sorted set within a lexicographical range. Deletes the sorted set if all members were removed.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zremrangebyrank/\">ZREMRANGEBYRANK</a></strong> - Removes members in a sorted set within a range of indexes. Deletes the sorted set if all members were removed.</summary>\n\n**Syntax:** `ZREMRANGEBYRANK key start stop`\n\n**Description:** Removes members in a sorted set within a range of indexes. Deletes the sorted set if all members were removed.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zremrangebyscore/\">ZREMRANGEBYSCORE</a></strong> - Removes members in a sorted set within a range of scores. Deletes the sorted set if all members were removed.</summary>\n\n**Syntax:** `ZREMRANGEBYSCORE key min max`\n\n**Description:** Removes members in a sorted set within a range of scores. Deletes the sorted set if all members were removed.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrevrange/\">ZREVRANGE</a></strong> - Returns members in a sorted set within a range of indexes in reverse order.</summary>\n\n**Syntax:** `ZREVRANGE key start stop [WITHSCORES]`\n\n**Description:** Returns members in a sorted set within a range of indexes in reverse order.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements returned.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrevrangebylex/\">ZREVRANGEBYLEX</a></strong> - Returns members in a sorted set within a lexicographical range in reverse order.</summary>\n\n**Syntax:** `ZREVRANGEBYLEX key max min [LIMIT offset count]`\n\n**Description:** Returns members in a sorted set within a lexicographical range in reverse order.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrevrangebyscore/\">ZREVRANGEBYSCORE</a></strong> - Returns members in a sorted set within a range of scores in reverse order.</summary>\n\n**Syntax:** `ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]`\n\n**Description:** Returns members in a sorted set within a range of scores in reverse order.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrevrank/\">ZREVRANK</a></strong> - Returns the index of a member in a sorted set ordered by descending scores.</summary>\n\n**Syntax:** `ZREVRANK key member [WITHSCORE]`\n\n**Description:** Returns the index of a member in a sorted set ordered by descending scores.\n\n**Complexity:** O(log(N))\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zscan/\">ZSCAN</a></strong> - Iterates over members and scores of a sorted set.</summary>\n\n**Syntax:** `ZSCAN key cursor [MATCH pattern] [COUNT count]`\n\n**Description:** Iterates over members and scores of a sorted set.\n\n**Complexity:** O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zscore/\">ZSCORE</a></strong> - Returns the score of a member in a sorted set.</summary>\n\n**Syntax:** `ZSCORE key member`\n\n**Description:** Returns the score of a member in a sorted set.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zunion/\">ZUNION</a></strong> - Returns the union of multiple sorted sets.</summary>\n\n**Syntax:** `ZUNION numkeys key [key ...] [WEIGHTS weight [weight ...]]\n  [AGGREGATE <SUM | MIN | MAX>] [WITHSCORES]`\n\n**Description:** Returns the union of multiple sorted sets.\n\n**Complexity:** O(N)+O(M*log(M)) with N being the sum of the sizes of the input sorted sets, and M being the number of elements in the resulting sorted set.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zunionstore/\">ZUNIONSTORE</a></strong> - Stores the union of multiple sorted sets in a key.</summary>\n\n**Syntax:** `ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight\n  [weight ...]] [AGGREGATE <SUM | MIN | MAX>]`\n\n**Description:** Stores the union of multiple sorted sets in a key.\n\n**Complexity:** O(N)+O(M log(M)) with N being the sum of the sizes of the input sorted sets, and M being the number of elements in the resulting sorted set.\n\n**Since:** 2.0.0\n\n</details>"},{"id":"stream-commands","title":"Stream commands","role":"content","text":"Stream commands operate on append-only log data structures.\n\n<details>\n<summary><strong><a href=\"/commands/xack/\">XACK</a></strong> - Returns the number of messages that were successfully acknowledged by the consumer group member of a stream.</summary>\n\n**Syntax:** `XACK key group id [id ...]`\n\n**Description:** Returns the number of messages that were successfully acknowledged by the consumer group member of a stream.\n\n**Complexity:** O(1) for each message ID processed.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xadd/\">XADD</a></strong> - Appends a new message to a stream. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `XADD key [NOMKSTREAM] [KEEPREF | DELREF | ACKED] [<MAXLEN | MINID>\n  [= | ~] threshold [LIMIT count]] <* | id> field value [field value\n  ...]`\n\n**Description:** Appends a new message to a stream. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) when adding a new entry, O(N) when trimming where N being the number of entries evicted.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xautoclaim/\">XAUTOCLAIM</a></strong> - Changes, or acquires, ownership of messages in a consumer group, as if the messages were delivered to as consumer group member.</summary>\n\n**Syntax:** `XAUTOCLAIM key group consumer min-idle-time start [COUNT count]\n  [JUSTID]`\n\n**Description:** Changes, or acquires, ownership of messages in a consumer group, as if the messages were delivered to as consumer group member.\n\n**Complexity:** O(1) if COUNT is small.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xclaim/\">XCLAIM</a></strong> - Changes, or acquires, ownership of a message in a consumer group, as if the message was delivered a consumer group member.</summary>\n\n**Syntax:** `XCLAIM key group consumer min-idle-time id [id ...] [IDLE ms]\n  [TIME unix-time-milliseconds] [RETRYCOUNT count] [FORCE] [JUSTID]\n  [LASTID lastid]`\n\n**Description:** Changes, or acquires, ownership of a message in a consumer group, as if the message was delivered a consumer group member.\n\n**Complexity:** O(log N) with N being the number of messages in the PEL of the consumer group.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xdel/\">XDEL</a></strong> - Returns the number of messages after removing them from a stream.</summary>\n\n**Syntax:** `XDEL key id [id ...]`\n\n**Description:** Returns the number of messages after removing them from a stream.\n\n**Complexity:** O(1) for each single item to delete in the stream, regardless of the stream size.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-create/\">XGROUP CREATE</a></strong> - Creates a consumer group.</summary>\n\n**Syntax:** `XGROUP CREATE key group <id | $> [MKSTREAM]\n  [ENTRIESREAD entries-read]`\n\n**Description:** Creates a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-createconsumer/\">XGROUP CREATECONSUMER</a></strong> - Creates a consumer in a consumer group.</summary>\n\n**Syntax:** `XGROUP CREATECONSUMER key group consumer`\n\n**Description:** Creates a consumer in a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-delconsumer/\">XGROUP DELCONSUMER</a></strong> - Deletes a consumer from a consumer group.</summary>\n\n**Syntax:** `XGROUP DELCONSUMER key group consumer`\n\n**Description:** Deletes a consumer from a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-destroy/\">XGROUP DESTROY</a></strong> - Destroys a consumer group.</summary>\n\n**Syntax:** `XGROUP DESTROY key group`\n\n**Description:** Destroys a consumer group.\n\n**Complexity:** O(N) where N is the number of entries in the group's pending entries list (PEL).\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-setid/\">XGROUP SETID</a></strong> - Sets the last-delivered ID of a consumer group.</summary>\n\n**Syntax:** `XGROUP SETID key group <id | $> [ENTRIESREAD entries-read]`\n\n**Description:** Sets the last-delivered ID of a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xinfo-consumers/\">XINFO CONSUMERS</a></strong> - Returns a list of the consumers in a consumer group.</summary>\n\n**Syntax:** `XINFO CONSUMERS key group`\n\n**Description:** Returns a list of the consumers in a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xinfo-groups/\">XINFO GROUPS</a></strong> - Returns a list of the consumer groups of a stream.</summary>\n\n**Syntax:** `XINFO GROUPS key`\n\n**Description:** Returns a list of the consumer groups of a stream.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xinfo-stream/\">XINFO STREAM</a></strong> - Returns information about a stream.</summary>\n\n**Syntax:** `XINFO STREAM key [FULL [COUNT count]]`\n\n**Description:** Returns information about a stream.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xlen/\">XLEN</a></strong> - Return the number of messages in a stream.</summary>\n\n**Syntax:** `XLEN key`\n\n**Description:** Return the number of messages in a stream.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xpending/\">XPENDING</a></strong> - Returns the information and entries from a stream consumer group's pending entries list.</summary>\n\n**Syntax:** `XPENDING key group [[IDLE min-idle-time] start end count [consumer]]`\n\n**Description:** Returns the information and entries from a stream consumer group's pending entries list.\n\n**Complexity:** O(N) with N being the number of elements returned, so asking for a small fixed number of entries per call is O(1). O(M), where M is the total number of entries scanned when used with the IDLE filter. When the command returns just the summary and the list of consumers is small, it runs in O(1) time; otherwise, an additional O(N) time for iterating every consumer.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xrange/\">XRANGE</a></strong> - Returns the messages from a stream within a range of IDs.</summary>\n\n**Syntax:** `XRANGE key start end [COUNT count]`\n\n**Description:** Returns the messages from a stream within a range of IDs.\n\n**Complexity:** O(N) with N being the number of elements being returned. If N is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1).\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xread/\">XREAD</a></strong> - Returns messages from multiple streams with IDs greater than the ones requested. Blocks until a message is available otherwise.</summary>\n\n**Syntax:** `XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] id\n  [id ...]`\n\n**Description:** Returns messages from multiple streams with IDs greater than the ones requested. Blocks until a message is available otherwise.\n\n**Complexity:** \n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xreadgroup/\">XREADGROUP</a></strong> - Returns new or historical messages from a stream for a consumer in a group. Blocks until a message is available otherwise.</summary>\n\n**Syntax:** `XREADGROUP GROUP group consumer [COUNT count] [BLOCK milliseconds]\n  [CLAIM min-idle-time] [NOACK] STREAMS key [key ...] id [id ...]`\n\n**Description:** Returns new or historical messages from a stream for a consumer in a group. Blocks until a message is available otherwise.\n\n**Complexity:** For each stream mentioned: O(M) with M being the number of elements returned. If M is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1). On the other side when XREADGROUP blocks, XADD will pay the O(N) time in order to serve the N clients blocked on the stream getting new data.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xrevrange/\">XREVRANGE</a></strong> - Returns the messages from a stream within a range of IDs in reverse order.</summary>\n\n**Syntax:** `XREVRANGE key end start [COUNT count]`\n\n**Description:** Returns the messages from a stream within a range of IDs in reverse order.\n\n**Complexity:** O(N) with N being the number of elements returned. If N is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1).\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xsetid/\">XSETID</a></strong> - An internal command for replicating stream values.</summary>\n\n**Syntax:** `XSETID key last-id [ENTRIESADDED entries-added]\n  [MAXDELETEDID max-deleted-id]`\n\n**Description:** An internal command for replicating stream values.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xtrim/\">XTRIM</a></strong> - Deletes messages from the beginning of a stream.</summary>\n\n**Syntax:** `XTRIM key <MAXLEN | MINID> [= | ~] threshold [LIMIT count] [KEEPREF\n  | DELREF | ACKED]`\n\n**Description:** Deletes messages from the beginning of a stream.\n\n**Complexity:** O(N), with N being the number of evicted entries. Constant times are very small however, since entries are organized in macro nodes containing multiple entries that can be released with a single deallocation.\n\n**Since:** 5.0.0\n\n</details>"},{"id":"bitmap-commands","title":"Bitmap commands","role":"content","text":"Bitmap commands operate on strings as arrays of bits.\n\n<details>\n<summary><strong><a href=\"/commands/bitcount/\">BITCOUNT</a></strong> - Counts the number of set bits (population counting) in a string.</summary>\n\n**Syntax:** `BITCOUNT key [start end [BYTE | BIT]]`\n\n**Description:** Counts the number of set bits (population counting) in a string.\n\n**Complexity:** O(N)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bitfield/\">BITFIELD</a></strong> - Performs arbitrary bitfield integer operations on strings.</summary>\n\n**Syntax:** `BITFIELD key [GET encoding offset | [OVERFLOW <WRAP | SAT | FAIL>]\n  <SET encoding offset value | INCRBY encoding offset increment>\n  [GET encoding offset | [OVERFLOW <WRAP | SAT | FAIL>]\n  <SET encoding offset value | INCRBY encoding offset increment>\n  ...]]`\n\n**Description:** Performs arbitrary bitfield integer operations on strings.\n\n**Complexity:** O(1) for each subcommand specified\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bitfield_ro/\">BITFIELD_RO</a></strong> - Performs arbitrary read-only bitfield integer operations on strings.</summary>\n\n**Syntax:** `BITFIELD RO key [GET encoding offset [GET encoding offset ...]]`\n\n**Description:** Performs arbitrary read-only bitfield integer operations on strings.\n\n**Complexity:** O(1) for each subcommand specified\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bitop/\">BITOP</a></strong> - Performs bitwise operations on multiple strings, and stores the result.</summary>\n\n**Syntax:** `BITOP <AND | OR | XOR | NOT | DIFF | DIFF1 | ANDOR | ONE> destkey key [key ...]`\n\n**Description:** Performs bitwise operations on multiple strings, and stores the result.\n\n**Complexity:** O(N)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bitpos/\">BITPOS</a></strong> - Finds the first set (1) or clear (0) bit in a string.</summary>\n\n**Syntax:** `BITPOS key bit [start [end [BYTE | BIT]]]`\n\n**Description:** Finds the first set (1) or clear (0) bit in a string.\n\n**Complexity:** O(N)\n\n**Since:** 2.8.7\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getbit/\">GETBIT</a></strong> - Returns a bit value by offset.</summary>\n\n**Syntax:** `GETBIT key offset`\n\n**Description:** Returns a bit value by offset.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/setbit/\">SETBIT</a></strong> - Sets or clears the bit at offset of the string value. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `SETBIT key offset value`\n\n**Description:** Sets or clears the bit at offset of the string value. Creates the key if it doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>"},{"id":"hyperloglog-commands","title":"HyperLogLog commands","role":"content","text":"HyperLogLog commands provide probabilistic cardinality estimation.\n\n<details>\n<summary><strong><a href=\"/commands/pfadd/\">PFADD</a></strong> - Adds elements to a HyperLogLog key. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `PFADD key [element [element ...]]`\n\n**Description:** Adds elements to a HyperLogLog key. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) to add every element.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pfcount/\">PFCOUNT</a></strong> - Returns the approximated cardinality of the set(s) observed by the HyperLogLog key(s).</summary>\n\n**Syntax:** `PFCOUNT key [key ...]`\n\n**Description:** Returns the approximated cardinality of the set(s) observed by the HyperLogLog key(s).\n\n**Complexity:** O(1) with a very small average constant time when called with a single key. O(N) with N being the number of keys, and much bigger constant times, when called with multiple keys.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pfdebug/\">PFDEBUG</a></strong> - Internal commands for debugging HyperLogLog values.</summary>\n\n**Syntax:** `PFDEBUG subcommand key`\n\n**Description:** Internal commands for debugging HyperLogLog values.\n\n**Complexity:** N/A\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pfmerge/\">PFMERGE</a></strong> - Merges one or more HyperLogLog values into a single key.</summary>\n\n**Syntax:** `PFMERGE destkey [sourcekey [sourcekey ...]]`\n\n**Description:** Merges one or more HyperLogLog values into a single key.\n\n**Complexity:** O(N) to merge N HyperLogLogs, but with high constant times.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pfselftest/\">PFSELFTEST</a></strong> - An internal command for testing HyperLogLog values.</summary>\n\n**Syntax:** `PFSELFTEST`\n\n**Description:** An internal command for testing HyperLogLog values.\n\n**Complexity:** N/A\n\n**Since:** 2.8.9\n\n</details>"},{"id":"geospatial-commands","title":"Geospatial commands","role":"content","text":"Geospatial commands operate on geographic coordinates.\n\n<details>\n<summary><strong><a href=\"/commands/geoadd/\">GEOADD</a></strong> - Adds one or more members to a geospatial index. The key is created if it doesn't exist.</summary>\n\n**Syntax:** `GEOADD key [NX | XX] [CH] longitude latitude member [longitude\n  latitude member ...]`\n\n**Description:** Adds one or more members to a geospatial index. The key is created if it doesn't exist.\n\n**Complexity:** O(log(N)) for each item added, where N is the number of elements in the sorted set.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geodist/\">GEODIST</a></strong> - Returns the distance between two members of a geospatial index.</summary>\n\n**Syntax:** `GEODIST key member1 member2 [M | KM | FT | MI]`\n\n**Description:** Returns the distance between two members of a geospatial index.\n\n**Complexity:** O(1)\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geohash/\">GEOHASH</a></strong> - Returns members from a geospatial index as geohash strings.</summary>\n\n**Syntax:** `GEOHASH key [member [member ...]]`\n\n**Description:** Returns members from a geospatial index as geohash strings.\n\n**Complexity:** O(1) for each member requested.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geopos/\">GEOPOS</a></strong> - Returns the longitude and latitude of members from a geospatial index.</summary>\n\n**Syntax:** `GEOPOS key [member [member ...]]`\n\n**Description:** Returns the longitude and latitude of members from a geospatial index.\n\n**Complexity:** O(1) for each member requested.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/georadius/\">GEORADIUS</a></strong> - Queries a geospatial index for members within a distance from a coordinate, optionally stores the result.</summary>\n\n**Syntax:** `GEORADIUS key longitude latitude radius <M | KM | FT | MI>\n  [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC]\n  [STORE key | STOREDIST key]`\n\n**Description:** Queries a geospatial index for members within a distance from a coordinate, optionally stores the result.\n\n**Complexity:** O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/georadiusbymember/\">GEORADIUSBYMEMBER</a></strong> - Queries a geospatial index for members within a distance from a member, optionally stores the result.</summary>\n\n**Syntax:** `GEORADIUSBYMEMBER key member radius <M | KM | FT | MI> [WITHCOORD]\n  [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC] [STORE key\n  | STOREDIST key]`\n\n**Description:** Queries a geospatial index for members within a distance from a member, optionally stores the result.\n\n**Complexity:** O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/georadiusbymember_ro/\">GEORADIUSBYMEMBER_RO</a></strong> - Returns members from a geospatial index that are within a distance from a member.</summary>\n\n**Syntax:** `GEORADIUSBYMEMBER RO key member radius <M | KM | FT | MI>\n  [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC]`\n\n**Description:** Returns members from a geospatial index that are within a distance from a member.\n\n**Complexity:** O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.\n\n**Since:** 3.2.10\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/georadius_ro/\">GEORADIUS_RO</a></strong> - Returns members from a geospatial index that are within a distance from a coordinate.</summary>\n\n**Syntax:** `GEORADIUS RO key longitude latitude radius <M | KM | FT | MI>\n  [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC]`\n\n**Description:** Returns members from a geospatial index that are within a distance from a coordinate.\n\n**Complexity:** O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.\n\n**Since:** 3.2.10\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geosearch/\">GEOSEARCH</a></strong> - Queries a geospatial index for members inside an area of a box or a circle.</summary>\n\n**Syntax:** `GEOSEARCH key <FROMMEMBER member | FROMLONLAT longitude latitude>\n  <BYRADIUS radius <M | KM | FT | MI> | BYBOX width height <M | KM |\n  FT | MI>> [ASC | DESC] [COUNT count [ANY]] [WITHCOORD] [WITHDIST]\n  [WITHHASH]`\n\n**Description:** Queries a geospatial index for members inside an area of a box or a circle.\n\n**Complexity:** O(N+log(M)) where N is the number of elements in the grid-aligned bounding box area around the shape provided as the filter and M is the number of items inside the shape\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geosearchstore/\">GEOSEARCHSTORE</a></strong> - Queries a geospatial index for members inside an area of a box or a circle, optionally stores the result.</summary>\n\n**Syntax:** `GEOSEARCHSTORE destination source <FROMMEMBER member |\n  FROMLONLAT longitude latitude> <BYRADIUS radius <M | KM | FT | MI>\n  | BYBOX width height <M | KM | FT | MI>> [ASC | DESC] [COUNT count\n  [ANY]] [STOREDIST]`\n\n**Description:** Queries a geospatial index for members inside an area of a box or a circle, optionally stores the result.\n\n**Complexity:** O(N+log(M)) where N is the number of elements in the grid-aligned bounding box area around the shape provided as the filter and M is the number of items inside the shape\n\n**Since:** 6.2.0\n\n</details>"},{"id":"json-commands","title":"JSON commands","role":"content","text":"JSON commands operate on JSON data structures.\n\n<details>\n<summary><strong><a href=\"/commands/json.arrappend/\">JSON.ARRAPPEND</a></strong> - Append one or more JSON values into the array at path after the last element in it.</summary>\n\n**Syntax:** `JSON.ARRAPPEND key path value [value ...]`\n\n**Description:** Append one or more JSON values into the array at path after the last element in it.\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrindex/\">JSON.ARRINDEX</a></strong> - Returns the index of the first occurrence of a JSON scalar value in the array at path</summary>\n\n**Syntax:** `JSON.ARRINDEX key path value [start [stop]]`\n\n**Description:** Returns the index of the first occurrence of a JSON scalar value in the array at path\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the array, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrinsert/\">JSON.ARRINSERT</a></strong> - Inserts the JSON scalar(s) value at the specified index in the array at path</summary>\n\n**Syntax:** `JSON.ARRINSERT key path index value [value ...]`\n\n**Description:** Inserts the JSON scalar(s) value at the specified index in the array at path\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the array, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrlen/\">JSON.ARRLEN</a></strong> - Returns the length of the array at path</summary>\n\n**Syntax:** `JSON.ARRLEN key [path]`\n\n**Description:** Returns the length of the array at path\n\n**Complexity:** O(1) where path is evaluated to a single value, O(N) where path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrpop/\">JSON.ARRPOP</a></strong> - Removes and returns the element at the specified index in the array at path</summary>\n\n**Syntax:** `JSON.ARRPOP key [path [index]]`\n\n**Description:** Removes and returns the element at the specified index in the array at path\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the array and the specified index is not the last element, O(1) when path is evaluated to a single value and the specified index is the last element, or O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrtrim/\">JSON.ARRTRIM</a></strong> - Trims the array at path to contain only the specified inclusive range of indices from start to stop</summary>\n\n**Syntax:** `JSON.ARRTRIM key path start stop`\n\n**Description:** Trims the array at path to contain only the specified inclusive range of indices from start to stop\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the array, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.clear/\">JSON.CLEAR</a></strong> - Clears all values from an array or an object and sets numeric values to `0`</summary>\n\n**Syntax:** `JSON.CLEAR key [path]`\n\n**Description:** Clears all values from an array or an object and sets numeric values to `0`\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the values, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.debug/\">JSON.DEBUG</a></strong> - Debugging container command</summary>\n\n**Syntax:** `JSON.DEBUG`\n\n**Description:** Debugging container command\n\n**Complexity:** N/A\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.debug-memory/\">JSON.DEBUG MEMORY</a></strong> - Reports the size in bytes of a key</summary>\n\n**Syntax:** `JSON.DEBUG MEMORY key [path]`\n\n**Description:** Reports the size in bytes of a key\n\n**Complexity:** O(N) when path is evaluated to a single value, where N is the size of the value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.del/\">JSON.DEL</a></strong> - Deletes a value</summary>\n\n**Syntax:** `JSON.DEL key [path]`\n\n**Description:** Deletes a value\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the deleted value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.forget/\">JSON.FORGET</a></strong> - Deletes a value</summary>\n\n**Syntax:** `JSON.FORGET key [path]`\n\n**Description:** Deletes a value\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the deleted value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.get/\">JSON.GET</a></strong> - Gets the value at one or more paths in JSON serialized form</summary>\n\n**Syntax:** `JSON.GET key [INDENT indent] [NEWLINE newline] [SPACE space] [path\n  [path ...]]`\n\n**Description:** Gets the value at one or more paths in JSON serialized form\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.merge/\">JSON.MERGE</a></strong> - Merges a given JSON value into matching paths. Consequently, JSON values at matching paths are updated, deleted, or expanded with new children</summary>\n\n**Syntax:** `JSON.MERGE key path value`\n\n**Description:** Merges a given JSON value into matching paths. Consequently, JSON values at matching paths are updated, deleted, or expanded with new children\n\n**Complexity:** O(M+N) when path is evaluated to a single value where M is the size of the original value (if it exists) and N is the size of the new value, O(M+N) when path is evaluated to multiple values where M is the size of the key and N is the size of the new value * the number of original values in the key\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.mget/\">JSON.MGET</a></strong> - Returns the values at a path from one or more keys</summary>\n\n**Syntax:** `JSON.MGET key [key ...] path`\n\n**Description:** Returns the values at a path from one or more keys\n\n**Complexity:** O(M*N) when path is evaluated to a single value where M is the number of keys and N is the size of the value, O(N1+N2+...+Nm) when path is evaluated to multiple values where m is the number of keys and Ni is the size of the i-th key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.mset/\">JSON.MSET</a></strong> - Sets or updates the JSON value of one or more keys</summary>\n\n**Syntax:** `JSON.MSET key path value [key path value ...]`\n\n**Description:** Sets or updates the JSON value of one or more keys\n\n**Complexity:** O(K*(M+N)) where k is the number of keys in the command, when path is evaluated to a single value where M is the size of the original value (if it exists) and N is the size of the new value, or O(K*(M+N)) when path is evaluated to multiple values where M is the size of the key and N is the size of the new value * the number of original values in the key\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.numincrby/\">JSON.NUMINCRBY</a></strong> - Increments the numeric value at path by a value</summary>\n\n**Syntax:** `JSON.NUMINCRBY key path value`\n\n**Description:** Increments the numeric value at path by a value\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.nummultby/\">JSON.NUMMULTBY</a></strong> - Multiplies the numeric value at path by a value</summary>\n\n**Syntax:** `JSON.NUMMULTBY key path value`\n\n**Description:** Multiplies the numeric value at path by a value\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.objkeys/\">JSON.OBJKEYS</a></strong> - Returns the JSON keys of the object at path</summary>\n\n**Syntax:** `JSON.OBJKEYS key [path]`\n\n**Description:** Returns the JSON keys of the object at path\n\n**Complexity:** O(N) when path is evaluated to a single value, where N is the number of keys in the object, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.objlen/\">JSON.OBJLEN</a></strong> - Returns the number of keys of the object at path</summary>\n\n**Syntax:** `JSON.OBJLEN key [path]`\n\n**Description:** Returns the number of keys of the object at path\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.resp/\">JSON.RESP</a></strong> - Returns the JSON value at path in Redis Serialization Protocol (RESP)</summary>\n\n**Syntax:** `JSON.RESP key [path]`\n\n**Description:** Returns the JSON value at path in Redis Serialization Protocol (RESP)\n\n**Complexity:** O(N) when path is evaluated to a single value, where N is the size of the value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.set/\">JSON.SET</a></strong> - Sets or updates the JSON value at a path</summary>\n\n**Syntax:** `JSON.SET key path value [NX | XX]`\n\n**Description:** Sets or updates the JSON value at a path\n\n**Complexity:** O(M+N) when path is evaluated to a single value where M is the size of the original value (if it exists) and N is the size of the new value, O(M+N) when path is evaluated to multiple values where M is the size of the key and N is the size of the new value * the number of original values in the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.strappend/\">JSON.STRAPPEND</a></strong> - Appends a string to a JSON string value at path</summary>\n\n**Syntax:** `JSON.STRAPPEND key [path] value`\n\n**Description:** Appends a string to a JSON string value at path\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.strlen/\">JSON.STRLEN</a></strong> - Returns the length of the JSON String at path in key</summary>\n\n**Syntax:** `JSON.STRLEN key [path]`\n\n**Description:** Returns the length of the JSON String at path in key\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.toggle/\">JSON.TOGGLE</a></strong> - Toggles a boolean value</summary>\n\n**Syntax:** `JSON.TOGGLE key path`\n\n**Description:** Toggles a boolean value\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.type/\">JSON.TYPE</a></strong> - Returns the type of the JSON value at path</summary>\n\n**Syntax:** `JSON.TYPE key [path]`\n\n**Description:** Returns the type of the JSON value at path\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>"},{"id":"search-commands","title":"Search commands","role":"content","text":"Search commands provide full-text search and secondary indexing.\n\n<details>\n<summary><strong><a href=\"/commands/ft.aggregate/\">FT.AGGREGATE</a></strong> - Run a search query on an index and perform aggregate transformations on the results</summary>\n\n**Syntax:** `FT.AGGREGATE index query [VERBATIM] [LOAD count field [field ...]]\n  [TIMEOUT timeout] [LOAD *] [GROUPBY nargs property [property ...]\n  [REDUCE function nargs arg [arg ...] [AS name] [REDUCE function\n  nargs arg [arg ...] [AS name] ...]] [GROUPBY nargs property\n  [property ...] [REDUCE function nargs arg [arg ...] [AS name]\n  [REDUCE function nargs arg [arg ...] [AS name] ...]] ...]]\n  [SORTBY nargs [property <ASC | DESC> [property <ASC | DESC> ...]]\n  [MAX num]] [APPLY expression AS name [APPLY expression AS name\n  ...]] [LIMIT offset num] [FILTER filter] [WITHCURSOR\n  [COUNT read size] [MAXIDLE idle time]] [PARAMS nargs name value\n  [name value ...]]\n  [SCORER scorer]\n [ADDSCORES]\n  [DIALECT dialect]`\n\n**Description:** Run a search query on an index and perform aggregate transformations on the results\n\n**Complexity:** O(1)\n\n**Since:** 1.1.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.aliasadd/\">FT.ALIASADD</a></strong> - Adds an alias to the index</summary>\n\n**Syntax:** `FT.ALIASADD alias index`\n\n**Description:** Adds an alias to the index\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.aliasdel/\">FT.ALIASDEL</a></strong> - Deletes an alias from the index</summary>\n\n**Syntax:** `FT.ALIASDEL alias`\n\n**Description:** Deletes an alias from the index\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.aliasupdate/\">FT.ALIASUPDATE</a></strong> - Adds or updates an alias to the index</summary>\n\n**Syntax:** `FT.ALIASUPDATE alias index`\n\n**Description:** Adds or updates an alias to the index\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.alter/\">FT.ALTER</a></strong> - Adds a new field to the index</summary>\n\n**Syntax:** `FT.ALTER index [SKIPINITIALSCAN] SCHEMA ADD field options`\n\n**Description:** Adds a new field to the index\n\n**Complexity:** O(N) where N is the number of keys in the keyspace\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.config-get/\">FT.CONFIG GET</a></strong> - Retrieves runtime configuration options</summary>\n\n**Syntax:** `FT.CONFIG GET option`\n\n**Description:** Retrieves runtime configuration options\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.config-set/\">FT.CONFIG SET</a></strong> - Sets runtime configuration options</summary>\n\n**Syntax:** `FT.CONFIG SET option value`\n\n**Description:** Sets runtime configuration options\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.create/\">FT.CREATE</a></strong> - Creates an index with the given spec</summary>\n\n**Syntax:** `FT.CREATE index [ON <HASH | JSON>] [PREFIX count prefix [prefix\n  ...]] [FILTER filter] [LANGUAGE default lang]\n  [LANGUAGE FIELD lang attribute] [SCORE default score]\n  [SCORE FIELD score attribute] [PAYLOAD FIELD payload attribute]\n  [MAXTEXTFIELDS] [TEMPORARY seconds] [NOOFFSETS] [NOHL] [NOFIELDS]\n  [NOFREQS] [STOPWORDS count [stopword [stopword ...]]]\n  [SKIPINITIALSCAN] SCHEMA field name [AS alias] <TEXT | TAG |\n  NUMERIC | GEO | VECTOR> [WITHSUFFIXTRIE] [SORTABLE [UNF]]\n  [NOINDEX] [field name [AS alias] <TEXT | TAG | NUMERIC | GEO |\n  VECTOR> [WITHSUFFIXTRIE] [SORTABLE [UNF]] [NOINDEX] ...]`\n\n**Description:** Creates an index with the given spec\n\n**Complexity:** O(K) at creation where K is the number of fields, O(N) if scanning the keyspace is triggered, where N is the number of keys in the keyspace\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.cursor-del/\">FT.CURSOR DEL</a></strong> - Deletes a cursor</summary>\n\n**Syntax:** `FT.CURSOR DEL index cursor id`\n\n**Description:** Deletes a cursor\n\n**Complexity:** O(1)\n\n**Since:** 1.1.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.cursor-read/\">FT.CURSOR READ</a></strong> - Reads from a cursor</summary>\n\n**Syntax:** `FT.CURSOR READ index cursor id [COUNT read size]`\n\n**Description:** Reads from a cursor\n\n**Complexity:** O(1)\n\n**Since:** 1.1.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.dictadd/\">FT.DICTADD</a></strong> - Adds terms to a dictionary</summary>\n\n**Syntax:** `FT.DICTADD dict term [term ...]`\n\n**Description:** Adds terms to a dictionary\n\n**Complexity:** O(1)\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.dictdel/\">FT.DICTDEL</a></strong> - Deletes terms from a dictionary</summary>\n\n**Syntax:** `FT.DICTDEL dict term [term ...]`\n\n**Description:** Deletes terms from a dictionary\n\n**Complexity:** O(1)\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.dictdump/\">FT.DICTDUMP</a></strong> - Dumps all terms in the given dictionary</summary>\n\n**Syntax:** `FT.DICTDUMP dict`\n\n**Description:** Dumps all terms in the given dictionary\n\n**Complexity:** O(N), where N is the size of the dictionary\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.dropindex/\">FT.DROPINDEX</a></strong> - Deletes the index</summary>\n\n**Syntax:** `FT.DROPINDEX index [DD]`\n\n**Description:** Deletes the index\n\n**Complexity:** O(1) or O(N) if documents are deleted, where N is the number of keys in the keyspace\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.explain/\">FT.EXPLAIN</a></strong> - Returns the execution plan for a complex query</summary>\n\n**Syntax:** `FT.EXPLAIN index query [DIALECT dialect]`\n\n**Description:** Returns the execution plan for a complex query\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.explaincli/\">FT.EXPLAINCLI</a></strong> - Returns the execution plan for a complex query</summary>\n\n**Syntax:** `FT.EXPLAINCLI index query [DIALECT dialect]`\n\n**Description:** Returns the execution plan for a complex query\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.info/\">FT.INFO</a></strong> - Returns information and statistics on the index</summary>\n\n**Syntax:** `FT.INFO index`\n\n**Description:** Returns information and statistics on the index\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.profile/\">FT.PROFILE</a></strong> - Performs a `FT.SEARCH` or `FT.AGGREGATE` command and collects performance information</summary>\n\n**Syntax:** `FT.PROFILE index <SEARCH | AGGREGATE> [LIMITED] QUERY query`\n\n**Description:** Performs a `FT.SEARCH` or `FT.AGGREGATE` command and collects performance information\n\n**Complexity:** O(N)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.search/\">FT.SEARCH</a></strong> - Searches the index with a textual query, returning either documents or just ids</summary>\n\n**Syntax:** `FT.SEARCH index query [NOCONTENT] [VERBATIM] [NOSTOPWORDS]\n  [WITHSCORES] [WITHPAYLOADS] [WITHSORTKEYS] [FILTER numeric field\n  min max [FILTER numeric field min max ...]] [GEOFILTER geo field\n  lon lat radius <m | km | mi | ft> [GEOFILTER geo field lon lat\n  radius <m | km | mi | ft> ...]] [INKEYS count key [key ...]]\n  [INFIELDS count field [field ...]] [RETURN count identifier\n  [AS property] [identifier [AS property] ...]] [SUMMARIZE\n  [FIELDS count field [field ...]] [FRAGS num] [LEN fragsize]\n  [SEPARATOR separator]] [HIGHLIGHT [FIELDS count field [field ...]]\n  [TAGS open close]] [SLOP slop] [TIMEOUT timeout] [INORDER]\n  [LANGUAGE language] [EXPANDER expander] [SCORER scorer]\n  [EXPLAINSCORE] [PAYLOAD payload] [SORTBY sortby [ASC | DESC]]\n  [LIMIT offset num] [PARAMS nargs name value [name value ...]]\n  [DIALECT dialect]`\n\n**Description:** Searches the index with a textual query, returning either documents or just ids\n\n**Complexity:** O(N)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.spellcheck/\">FT.SPELLCHECK</a></strong> - Performs spelling correction on a query, returning suggestions for misspelled terms</summary>\n\n**Syntax:** `FT.SPELLCHECK index query [DISTANCE distance] [TERMS <INCLUDE |\n  EXCLUDE> dictionary [terms [terms ...]]] [DIALECT dialect]`\n\n**Description:** Performs spelling correction on a query, returning suggestions for misspelled terms\n\n**Complexity:** O(1)\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.syndump/\">FT.SYNDUMP</a></strong> - Dumps the contents of a synonym group</summary>\n\n**Syntax:** `FT.SYNDUMP index`\n\n**Description:** Dumps the contents of a synonym group\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.synupdate/\">FT.SYNUPDATE</a></strong> - Creates or updates a synonym group with additional terms</summary>\n\n**Syntax:** `FT.SYNUPDATE index synonym group id [SKIPINITIALSCAN] term [term\n  ...]`\n\n**Description:** Creates or updates a synonym group with additional terms\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.tagvals/\">FT.TAGVALS</a></strong> - Returns the distinct tags indexed in a Tag field</summary>\n\n**Syntax:** `FT.TAGVALS index field name`\n\n**Description:** Returns the distinct tags indexed in a Tag field\n\n**Complexity:** O(N)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft._list/\">FT._LIST</a></strong> - Returns a list of all existing indexes</summary>\n\n**Syntax:** `FT. LIST`\n\n**Description:** Returns a list of all existing indexes\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>"},{"id":"time-series-commands","title":"Time series commands","role":"content","text":"Time series commands operate on time-series data.\n\n<details>\n<summary><strong><a href=\"/commands/ts.add/\">TS.ADD</a></strong> - Append a sample to a time series</summary>\n\n**Syntax:** `TS.ADD key timestamp value [RETENTION retentionPeriod]\n  [ENCODING <COMPRESSED | UNCOMPRESSED>] [CHUNK SIZE size]\n  [DUPLICATE POLICY policy] \n  [ON DUPLICATE <BLOCK | FIRST | LAST | MIN | MAX | SUM>]\n  [IGNORE ignoreMaxTimediff ignoreMaxValDiff]\n  [LABELS [label value ...]]`\n\n**Description:** Append a sample to a time series\n\n**Complexity:** O(M) when M is the amount of compaction rules or O(1) with no compaction\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.alter/\">TS.ALTER</a></strong> - Update the retention, chunk size, duplicate policy, and labels of an existing time series</summary>\n\n**Syntax:** `TS.ALTER key [RETENTION retentionPeriod] [CHUNK SIZE size]\n  [DUPLICATE POLICY <BLOCK | FIRST | LAST | MIN | MAX | SUM>]\n  [IGNORE ignoreMaxTimediff ignoreMaxValDiff] \n  [LABELS [label value ...]]`\n\n**Description:** Update the retention, chunk size, duplicate policy, and labels of an existing time series\n\n**Complexity:** O(N) where N is the number of labels requested to update\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.create/\">TS.CREATE</a></strong> - Create a new time series</summary>\n\n**Syntax:** `TS.CREATE key [RETENTION retentionPeriod] [ENCODING <COMPRESSED |\n  UNCOMPRESSED>] [CHUNK SIZE size] [DUPLICATE POLICY <BLOCK | FIRST |\n  LAST | MIN | MAX | SUM>]\n  [IGNORE ignoreMaxTimediff ignoreMaxValDiff]\n  [LABELS [label value ...]]`\n\n**Description:** Create a new time series\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.createrule/\">TS.CREATERULE</a></strong> - Create a compaction rule</summary>\n\n**Syntax:** `TS.CREATERULE sourceKey destKey AGGREGATION <AVG | FIRST | LAST |\n  MIN | MAX | SUM | RANGE | COUNT | STD.P | STD.S | VAR.P | VAR.S |\n  TWA> bucketDuration [alignTimestamp]`\n\n**Description:** Create a compaction rule\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.decrby/\">TS.DECRBY</a></strong> - Decrease the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given decrement</summary>\n\n**Syntax:** `TS.DECRBY key value [TIMESTAMP timestamp]\n  [RETENTION retentionPeriod] [ENCODING <COMPRESSED|UNCOMPRESSED>] [CHUNK SIZE size]\n [DUPLICATE POLICY policy] [LABELS [label value ...]]`\n\n**Description:** Decrease the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given decrement\n\n**Complexity:** O(M) when M is the amount of compaction rules or O(1) with no compaction\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.del/\">TS.DEL</a></strong> - Delete all samples between two timestamps for a given time series</summary>\n\n**Syntax:** `TS.DEL key from timestamp to timestamp`\n\n**Description:** Delete all samples between two timestamps for a given time series\n\n**Complexity:** O(N) where N is the number of data points that will be removed\n\n**Since:** 1.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.deleterule/\">TS.DELETERULE</a></strong> - Delete a compaction rule</summary>\n\n**Syntax:** `TS.DELETERULE sourceKey destKey`\n\n**Description:** Delete a compaction rule\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.get/\">TS.GET</a></strong> - Get the sample with the highest timestamp from a given time series</summary>\n\n**Syntax:** `TS.GET key [LATEST]`\n\n**Description:** Get the sample with the highest timestamp from a given time series\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.incrby/\">TS.INCRBY</a></strong> - Increase the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given increment</summary>\n\n**Syntax:** `TS.INCRBY key value [TIMESTAMP timestamp]\n  [RETENTION retentionPeriod] [ENCODING <COMPRESSED|UNCOMPRESSED>] [CHUNK SIZE size]\n [DUPLICATE POLICY policy] [LABELS [label value ...]]`\n\n**Description:** Increase the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given increment\n\n**Complexity:** O(M) when M is the amount of compaction rules or O(1) with no compaction\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.info/\">TS.INFO</a></strong> - Returns information and statistics for a time series</summary>\n\n**Syntax:** `TS.INFO key [DEBUG]`\n\n**Description:** Returns information and statistics for a time series\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.madd/\">TS.MADD</a></strong> - Append new samples to one or more time series</summary>\n\n**Syntax:** `TS.MADD key timestamp value [key timestamp value ...]`\n\n**Description:** Append new samples to one or more time series\n\n**Complexity:** O(N*M) when N is the amount of series updated and M is the amount of compaction rules or O(N) with no compaction\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.mget/\">TS.MGET</a></strong> - Get the sample with the highest timestamp from each time series matching a specific filter</summary>\n\n**Syntax:** `TS.MGET [LATEST] [WITHLABELS | <SELECTED LABELS label1 [label1 ...]>]\n  FILTER <l=v | l!=v | l= | l!= | l=(v1,v2,...) | l!=(v1,v2,...)\n  [l=v | l!=v | l= | l!= | l=(v1,v2,...) | l!=(v1,v2,...) ...]>`\n\n**Description:** Get the sample with the highest timestamp from each time series matching a specific filter\n\n**Complexity:** O(n) where n is the number of time-series that match the filters\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.mrange/\">TS.MRANGE</a></strong> - Query a range across multiple time series by filters in forward direction</summary>\n\n**Syntax:** `TS.MRANGE fromTimestamp toTimestamp [LATEST] [FILTER BY TS Timestamp\n  [Timestamp ...]] [FILTER BY VALUE min max] [WITHLABELS |\n  <SELECTED LABELS label1 [label1 ...]>] [COUNT count] [[ALIGN value]\n  AGGREGATION <AVG | FIRST | LAST | MIN | MAX | SUM | RANGE | COUNT\n  | STD.P | STD.S | VAR.P | VAR.S | TWA> bucketDuration\n  [BUCKETTIMESTAMP] [EMPTY]] FILTER <l=v | l!=v | l= | l!= |\n  l=(v1,v2,...) | l!=(v1,v2,...) [l=v | l!=v | l= | l!= |\n  l=(v1,v2,...) | l!=(v1,v2,...) ...]> [GROUPBY label REDUCE\n  reducer]`\n\n**Description:** Query a range across multiple time series by filters in forward direction\n\n**Complexity:** O(n/m+k) where n = Number of data points, m = Chunk size (data points per chunk), k = Number of data points that are in the requested ranges\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.mrevrange/\">TS.MREVRANGE</a></strong> - Query a range across multiple time-series by filters in reverse direction</summary>\n\n**Syntax:** `TS.MREVRANGE fromTimestamp toTimestamp [LATEST]\n  [FILTER BY TS Timestamp [Timestamp ...]] [FILTER BY VALUE min max]\n  [WITHLABELS | <SELECTED LABELS label1 [label1 ...]>] [COUNT count]\n  [[ALIGN value] AGGREGATION <AVG | FIRST | LAST | MIN | MAX | SUM |\n  RANGE | COUNT | STD.P | STD.S | VAR.P | VAR.S | TWA>\n  bucketDuration [BUCKETTIMESTAMP] [EMPTY]] FILTER <l=v | l!=v | l=\n  | l!= | l=(v1,v2,...) | l!=(v1,v2,...) [l=v | l!=v | l= | l!= |\n  l=(v1,v2,...) | l!=(v1,v2,...) ...]> [GROUPBY label REDUCE\n  reducer]`\n\n**Description:** Query a range across multiple time-series by filters in reverse direction\n\n**Complexity:** O(n/m+k) where n = Number of data points, m = Chunk size (data points per chunk), k = Number of data points that are in the requested ranges\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.queryindex/\">TS.QUERYINDEX</a></strong> - Get all time series keys matching a filter list</summary>\n\n**Syntax:** `TS.QUERYINDEX <l=v | l!=v | l= | l!= | l=(v1,v2,...) |\n  l!=(v1,v2,...) [l=v | l!=v | l= | l!= | l=(v1,v2,...) |\n  l!=(v1,v2,...) ...]>`\n\n**Description:** Get all time series keys matching a filter list\n\n**Complexity:** O(n) where n is the number of time-series that match the filters\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.range/\">TS.RANGE</a></strong> - Query a range in forward direction</summary>\n\n**Syntax:** `TS.RANGE key fromTimestamp toTimestamp [LATEST]\n  [FILTER BY TS Timestamp [Timestamp ...]] [FILTER BY VALUE min max]\n  [COUNT count] [[ALIGN value] AGGREGATION <AVG | FIRST | LAST | MIN\n  | MAX | SUM | RANGE | COUNT | STD.P | STD.S | VAR.P | VAR.S | TWA>\n  bucketDuration [BUCKETTIMESTAMP] [EMPTY]]`\n\n**Description:** Query a range in forward direction\n\n**Complexity:** O(n/m+k) where n = Number of data points, m = Chunk size (data points per chunk), k = Number of data points that are in the requested range\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.revrange/\">TS.REVRANGE</a></strong> - Query a range in reverse direction</summary>\n\n**Syntax:** `TS.REVRANGE key fromTimestamp toTimestamp [LATEST]\n  [FILTER BY TS Timestamp [Timestamp ...]] [FILTER BY VALUE min max]\n  [COUNT count] [[ALIGN value] AGGREGATION <AVG | FIRST | LAST | MIN\n  | MAX | SUM | RANGE | COUNT | STD.P | STD.S | VAR.P | VAR.S | TWA>\n  bucketDuration [BUCKETTIMESTAMP] [EMPTY]]`\n\n**Description:** Query a range in reverse direction\n\n**Complexity:** O(n/m+k) where n = Number of data points, m = Chunk size (data points per chunk), k = Number of data points that are in the requested range\n\n**Since:** 1.4.0\n\n</details>"},{"id":"pubsub-commands","title":"Pub/Sub commands","role":"content","text":"Pub/Sub commands enable message passing between clients.\n\n<details>\n<summary><strong><a href=\"/commands/psubscribe/\">PSUBSCRIBE</a></strong> - Listens for messages published to channels that match one or more patterns.</summary>\n\n**Syntax:** `PSUBSCRIBE pattern [pattern ...]`\n\n**Description:** Listens for messages published to channels that match one or more patterns.\n\n**Complexity:** O(N) where N is the number of patterns to subscribe to.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/publish/\">PUBLISH</a></strong> - Posts a message to a channel.</summary>\n\n**Syntax:** `PUBLISH channel message`\n\n**Description:** Posts a message to a channel.\n\n**Complexity:** O(N+M) where N is the number of clients subscribed to the receiving channel and M is the total number of subscribed patterns (by any client).\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-channels/\">PUBSUB CHANNELS</a></strong> - Returns the active channels.</summary>\n\n**Syntax:** `PUBSUB CHANNELS [pattern]`\n\n**Description:** Returns the active channels.\n\n**Complexity:** O(N) where N is the number of active channels, and assuming constant time pattern matching (relatively short channels and patterns)\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-numpat/\">PUBSUB NUMPAT</a></strong> - Returns a count of unique pattern subscriptions.</summary>\n\n**Syntax:** `PUBSUB NUMPAT`\n\n**Description:** Returns a count of unique pattern subscriptions.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-numsub/\">PUBSUB NUMSUB</a></strong> - Returns a count of subscribers to channels.</summary>\n\n**Syntax:** `PUBSUB NUMSUB [channel [channel ...]]`\n\n**Description:** Returns a count of subscribers to channels.\n\n**Complexity:** O(N) for the NUMSUB subcommand, where N is the number of requested channels\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-shardchannels/\">PUBSUB SHARDCHANNELS</a></strong> - Returns the active shard channels.</summary>\n\n**Syntax:** `PUBSUB SHARDCHANNELS [pattern]`\n\n**Description:** Returns the active shard channels.\n\n**Complexity:** O(N) where N is the number of active shard channels, and assuming constant time pattern matching (relatively short shard channels).\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-shardnumsub/\">PUBSUB SHARDNUMSUB</a></strong> - Returns the count of subscribers of shard channels.</summary>\n\n**Syntax:** `PUBSUB SHARDNUMSUB [shardchannel [shardchannel ...]]`\n\n**Description:** Returns the count of subscribers of shard channels.\n\n**Complexity:** O(N) for the SHARDNUMSUB subcommand, where N is the number of requested shard channels\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/punsubscribe/\">PUNSUBSCRIBE</a></strong> - Stops listening to messages published to channels that match one or more patterns.</summary>\n\n**Syntax:** `PUNSUBSCRIBE [pattern [pattern ...]]`\n\n**Description:** Stops listening to messages published to channels that match one or more patterns.\n\n**Complexity:** O(N) where N is the number of patterns to unsubscribe.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/spublish/\">SPUBLISH</a></strong> - Post a message to a shard channel</summary>\n\n**Syntax:** `SPUBLISH shardchannel message`\n\n**Description:** Post a message to a shard channel\n\n**Complexity:** O(N) where N is the number of clients subscribed to the receiving shard channel.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ssubscribe/\">SSUBSCRIBE</a></strong> - Listens for messages published to shard channels.</summary>\n\n**Syntax:** `SSUBSCRIBE shardchannel [shardchannel ...]`\n\n**Description:** Listens for messages published to shard channels.\n\n**Complexity:** O(N) where N is the number of shard channels to subscribe to.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/subscribe/\">SUBSCRIBE</a></strong> - Listens for messages published to channels.</summary>\n\n**Syntax:** `SUBSCRIBE channel [channel ...]`\n\n**Description:** Listens for messages published to channels.\n\n**Complexity:** O(N) where N is the number of channels to subscribe to.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sunsubscribe/\">SUNSUBSCRIBE</a></strong> - Stops listening to messages posted to shard channels.</summary>\n\n**Syntax:** `SUNSUBSCRIBE [shardchannel [shardchannel ...]]`\n\n**Description:** Stops listening to messages posted to shard channels.\n\n**Complexity:** O(N) where N is the number of shard channels to unsubscribe.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/unsubscribe/\">UNSUBSCRIBE</a></strong> - Stops listening to messages posted to channels.</summary>\n\n**Syntax:** `UNSUBSCRIBE [channel [channel ...]]`\n\n**Description:** Stops listening to messages posted to channels.\n\n**Complexity:** O(N) where N is the number of channels to unsubscribe.\n\n**Since:** 2.0.0\n\n</details>"},{"id":"transaction-commands","title":"Transaction commands","role":"content","text":"Transaction commands enable atomic execution of command groups.\n\n<details>\n<summary><strong><a href=\"/commands/discard/\">DISCARD</a></strong> - Discards a transaction.</summary>\n\n**Syntax:** `DISCARD`\n\n**Description:** Discards a transaction.\n\n**Complexity:** O(N), when N is the number of queued commands\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/exec/\">EXEC</a></strong> - Executes all commands in a transaction.</summary>\n\n**Syntax:** `EXEC`\n\n**Description:** Executes all commands in a transaction.\n\n**Complexity:** Depends on commands in the transaction\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/multi/\">MULTI</a></strong> - Starts a transaction.</summary>\n\n**Syntax:** `MULTI`\n\n**Description:** Starts a transaction.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/unwatch/\">UNWATCH</a></strong> - Forgets about watched keys of a transaction.</summary>\n\n**Syntax:** `UNWATCH`\n\n**Description:** Forgets about watched keys of a transaction.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/watch/\">WATCH</a></strong> - Monitors changes to keys to determine the execution of a transaction.</summary>\n\n**Syntax:** `WATCH key [key ...]`\n\n**Description:** Monitors changes to keys to determine the execution of a transaction.\n\n**Complexity:** O(1) for every key.\n\n**Since:** 2.2.0\n\n</details>"},{"id":"scripting-commands","title":"Scripting commands","role":"content","text":"Scripting commands enable server-side Lua script execution.\n\n<details>\n<summary><strong><a href=\"/commands/eval/\">EVAL</a></strong> - Executes a server-side Lua script.</summary>\n\n**Syntax:** `EVAL script numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Executes a server-side Lua script.\n\n**Complexity:** Depends on the script that is executed.\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/evalsha/\">EVALSHA</a></strong> - Executes a server-side Lua script by SHA1 digest.</summary>\n\n**Syntax:** `EVALSHA sha1 numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Executes a server-side Lua script by SHA1 digest.\n\n**Complexity:** Depends on the script that is executed.\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/evalsha_ro/\">EVALSHA_RO</a></strong> - Executes a read-only server-side Lua script by SHA1 digest.</summary>\n\n**Syntax:** `EVALSHA RO sha1 numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Executes a read-only server-side Lua script by SHA1 digest.\n\n**Complexity:** Depends on the script that is executed.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/eval_ro/\">EVAL_RO</a></strong> - Executes a read-only server-side Lua script.</summary>\n\n**Syntax:** `EVAL RO script numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Executes a read-only server-side Lua script.\n\n**Complexity:** Depends on the script that is executed.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/fcall/\">FCALL</a></strong> - Invokes a function.</summary>\n\n**Syntax:** `FCALL function numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Invokes a function.\n\n**Complexity:** Depends on the function that is executed.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/fcall_ro/\">FCALL_RO</a></strong> - Invokes a read-only function.</summary>\n\n**Syntax:** `FCALL RO function numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Invokes a read-only function.\n\n**Complexity:** Depends on the function that is executed.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-delete/\">FUNCTION DELETE</a></strong> - Deletes a library and its functions.</summary>\n\n**Syntax:** `FUNCTION DELETE library-name`\n\n**Description:** Deletes a library and its functions.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-dump/\">FUNCTION DUMP</a></strong> - Dumps all libraries into a serialized binary payload.</summary>\n\n**Syntax:** `FUNCTION DUMP`\n\n**Description:** Dumps all libraries into a serialized binary payload.\n\n**Complexity:** O(N) where N is the number of functions\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-flush/\">FUNCTION FLUSH</a></strong> - Deletes all libraries and functions.</summary>\n\n**Syntax:** `FUNCTION FLUSH [ASYNC | SYNC]`\n\n**Description:** Deletes all libraries and functions.\n\n**Complexity:** O(N) where N is the number of functions deleted\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-kill/\">FUNCTION KILL</a></strong> - Terminates a function during execution.</summary>\n\n**Syntax:** `FUNCTION KILL`\n\n**Description:** Terminates a function during execution.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-list/\">FUNCTION LIST</a></strong> - Returns information about all libraries.</summary>\n\n**Syntax:** `FUNCTION LIST [LIBRARYNAME library-name-pattern] [WITHCODE]`\n\n**Description:** Returns information about all libraries.\n\n**Complexity:** O(N) where N is the number of functions\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-load/\">FUNCTION LOAD</a></strong> - Creates a library.</summary>\n\n**Syntax:** `FUNCTION LOAD [REPLACE] function-code`\n\n**Description:** Creates a library.\n\n**Complexity:** O(1) (considering compilation time is redundant)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-restore/\">FUNCTION RESTORE</a></strong> - Restores all libraries from a payload.</summary>\n\n**Syntax:** `FUNCTION RESTORE serialized-value [FLUSH | APPEND | REPLACE]`\n\n**Description:** Restores all libraries from a payload.\n\n**Complexity:** O(N) where N is the number of functions on the payload\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-stats/\">FUNCTION STATS</a></strong> - Returns information about a function during execution.</summary>\n\n**Syntax:** `FUNCTION STATS`\n\n**Description:** Returns information about a function during execution.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-debug/\">SCRIPT DEBUG</a></strong> - Sets the debug mode of server-side Lua scripts.</summary>\n\n**Syntax:** `SCRIPT DEBUG <YES | SYNC | NO>`\n\n**Description:** Sets the debug mode of server-side Lua scripts.\n\n**Complexity:** O(1)\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-exists/\">SCRIPT EXISTS</a></strong> - Determines whether server-side Lua scripts exist in the script cache.</summary>\n\n**Syntax:** `SCRIPT EXISTS sha1 [sha1 ...]`\n\n**Description:** Determines whether server-side Lua scripts exist in the script cache.\n\n**Complexity:** O(N) with N being the number of scripts to check (so checking a single script is an O(1) operation).\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-flush/\">SCRIPT FLUSH</a></strong> - Removes all server-side Lua scripts from the script cache.</summary>\n\n**Syntax:** `SCRIPT FLUSH [ASYNC | SYNC]`\n\n**Description:** Removes all server-side Lua scripts from the script cache.\n\n**Complexity:** O(N) with N being the number of scripts in cache\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-kill/\">SCRIPT KILL</a></strong> - Terminates a server-side Lua script during execution.</summary>\n\n**Syntax:** `SCRIPT KILL`\n\n**Description:** Terminates a server-side Lua script during execution.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-load/\">SCRIPT LOAD</a></strong> - Loads a server-side Lua script to the script cache.</summary>\n\n**Syntax:** `SCRIPT LOAD script`\n\n**Description:** Loads a server-side Lua script to the script cache.\n\n**Complexity:** O(N) with N being the length in bytes of the script body.\n\n**Since:** 2.6.0\n\n</details>"},{"id":"connection-commands","title":"Connection commands","role":"content","text":"Connection commands manage client connections.\n\n<details>\n<summary><strong><a href=\"/commands/auth/\">AUTH</a></strong> - Authenticates the connection.</summary>\n\n**Syntax:** `AUTH [username] password`\n\n**Description:** Authenticates the connection.\n\n**Complexity:** O(N) where N is the number of passwords defined for the user\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-caching/\">CLIENT CACHING</a></strong> - Instructs the server whether to track the keys in the next request.</summary>\n\n**Syntax:** `CLIENT CACHING <YES | NO>`\n\n**Description:** Instructs the server whether to track the keys in the next request.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-getname/\">CLIENT GETNAME</a></strong> - Returns the name of the connection.</summary>\n\n**Syntax:** `CLIENT GETNAME`\n\n**Description:** Returns the name of the connection.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-getredir/\">CLIENT GETREDIR</a></strong> - Returns the client ID to which the connection's tracking notifications are redirected.</summary>\n\n**Syntax:** `CLIENT GETREDIR`\n\n**Description:** Returns the client ID to which the connection's tracking notifications are redirected.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-id/\">CLIENT ID</a></strong> - Returns the unique client ID of the connection.</summary>\n\n**Syntax:** `CLIENT ID`\n\n**Description:** Returns the unique client ID of the connection.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-info/\">CLIENT INFO</a></strong> - Returns information about the connection.</summary>\n\n**Syntax:** `CLIENT INFO`\n\n**Description:** Returns information about the connection.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-kill/\">CLIENT KILL</a></strong> - Terminates open connections.</summary>\n\n**Syntax:** `CLIENT KILL <ip:port | <[ID client-id] | [TYPE <NORMAL | MASTER |\n  SLAVE | REPLICA | PUBSUB>] | [USER username] | [ADDR ip:port] |\n  [LADDR ip:port] | [SKIPME <YES | NO>] | [MAXAGE maxage]\n  [[ID client-id] | [TYPE <NORMAL | MASTER | SLAVE | REPLICA |\n  PUBSUB>] | [USER username] | [ADDR ip:port] | [LADDR ip:port] |\n  [SKIPME <YES | NO>] | [MAXAGE maxage] ...]>>`\n\n**Description:** Terminates open connections.\n\n**Complexity:** O(N) where N is the number of client connections\n\n**Since:** 2.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-list/\">CLIENT LIST</a></strong> - Lists open connections.</summary>\n\n**Syntax:** `CLIENT LIST [TYPE <NORMAL | MASTER | REPLICA | PUBSUB>]\n  [ID client-id [client-id ...]]`\n\n**Description:** Lists open connections.\n\n**Complexity:** O(N) where N is the number of client connections\n\n**Since:** 2.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-no-evict/\">CLIENT NO-EVICT</a></strong> - Sets the client eviction mode of the connection.</summary>\n\n**Syntax:** `CLIENT NO-EVICT <ON | OFF>`\n\n**Description:** Sets the client eviction mode of the connection.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-no-touch/\">CLIENT NO-TOUCH</a></strong> - Controls whether commands sent by the client affect the LRU/LFU of accessed keys.</summary>\n\n**Syntax:** `CLIENT NO-TOUCH <ON | OFF>`\n\n**Description:** Controls whether commands sent by the client affect the LRU/LFU of accessed keys.\n\n**Complexity:** O(1)\n\n**Since:** 7.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-pause/\">CLIENT PAUSE</a></strong> - Suspends commands processing.</summary>\n\n**Syntax:** `CLIENT PAUSE timeout [WRITE | ALL]`\n\n**Description:** Suspends commands processing.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-reply/\">CLIENT REPLY</a></strong> - Instructs the server whether to reply to commands.</summary>\n\n**Syntax:** `CLIENT REPLY <ON | OFF | SKIP>`\n\n**Description:** Instructs the server whether to reply to commands.\n\n**Complexity:** O(1)\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-setinfo/\">CLIENT SETINFO</a></strong> - Sets information specific to the client or connection.</summary>\n\n**Syntax:** `CLIENT SETINFO <LIB-NAME libname | LIB-VER libver>`\n\n**Description:** Sets information specific to the client or connection.\n\n**Complexity:** O(1)\n\n**Since:** 7.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-setname/\">CLIENT SETNAME</a></strong> - Sets the connection name.</summary>\n\n**Syntax:** `CLIENT SETNAME connection-name`\n\n**Description:** Sets the connection name.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-tracking/\">CLIENT TRACKING</a></strong> - Controls server-assisted client-side caching for the connection.</summary>\n\n**Syntax:** `CLIENT TRACKING <ON | OFF> [REDIRECT client-id] [PREFIX prefix\n  [PREFIX prefix ...]] [BCAST] [OPTIN] [OPTOUT] [NOLOOP]`\n\n**Description:** Controls server-assisted client-side caching for the connection.\n\n**Complexity:** O(1). Some options may introduce additional complexity.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-trackinginfo/\">CLIENT TRACKINGINFO</a></strong> - Returns information about server-assisted client-side caching for the connection.</summary>\n\n**Syntax:** `CLIENT TRACKINGINFO`\n\n**Description:** Returns information about server-assisted client-side caching for the connection.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-unblock/\">CLIENT UNBLOCK</a></strong> - Unblocks a client blocked by a blocking command from a different connection.</summary>\n\n**Syntax:** `CLIENT UNBLOCK client-id [TIMEOUT | ERROR]`\n\n**Description:** Unblocks a client blocked by a blocking command from a different connection.\n\n**Complexity:** O(log N) where N is the number of client connections\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-unpause/\">CLIENT UNPAUSE</a></strong> - Resumes processing commands from paused clients.</summary>\n\n**Syntax:** `CLIENT UNPAUSE`\n\n**Description:** Resumes processing commands from paused clients.\n\n**Complexity:** O(N) Where N is the number of paused clients\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/echo/\">ECHO</a></strong> - Returns the given string.</summary>\n\n**Syntax:** `ECHO message`\n\n**Description:** Returns the given string.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hello/\">HELLO</a></strong> - Handshakes with the Redis server.</summary>\n\n**Syntax:** `HELLO [protover [AUTH username password] [SETNAME clientname]]`\n\n**Description:** Handshakes with the Redis server.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ping/\">PING</a></strong> - Returns the server's liveliness response.</summary>\n\n**Syntax:** `PING [message]`\n\n**Description:** Returns the server's liveliness response.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/quit/\">QUIT</a></strong> - Closes the connection.</summary>\n\n**Syntax:** `QUIT`\n\n**Description:** Closes the connection.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/reset/\">RESET</a></strong> - Resets the connection.</summary>\n\n**Syntax:** `RESET`\n\n**Description:** Resets the connection.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/select/\">SELECT</a></strong> - Changes the selected database.</summary>\n\n**Syntax:** `SELECT index`\n\n**Description:** Changes the selected database.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>"},{"id":"server-commands","title":"Server commands","role":"content","text":"Server commands provide server management and introspection.\n\n<details>\n<summary><strong><a href=\"/commands/acl-cat/\">ACL CAT</a></strong> - Lists the ACL categories, or the commands inside a category.</summary>\n\n**Syntax:** `ACL CAT [category]`\n\n**Description:** Lists the ACL categories, or the commands inside a category.\n\n**Complexity:** O(1) since the categories and commands are a fixed set.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-deluser/\">ACL DELUSER</a></strong> - Deletes ACL users, and terminates their connections.</summary>\n\n**Syntax:** `ACL DELUSER username [username ...]`\n\n**Description:** Deletes ACL users, and terminates their connections.\n\n**Complexity:** O(1) amortized time considering the typical user.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-dryrun/\">ACL DRYRUN</a></strong> - Simulates the execution of a command by a user, without executing the command.</summary>\n\n**Syntax:** `ACL DRYRUN username command [arg [arg ...]]`\n\n**Description:** Simulates the execution of a command by a user, without executing the command.\n\n**Complexity:** O(1).\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-genpass/\">ACL GENPASS</a></strong> - Generates a pseudorandom, secure password that can be used to identify ACL users.</summary>\n\n**Syntax:** `ACL GENPASS [bits]`\n\n**Description:** Generates a pseudorandom, secure password that can be used to identify ACL users.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-getuser/\">ACL GETUSER</a></strong> - Lists the ACL rules of a user.</summary>\n\n**Syntax:** `ACL GETUSER username`\n\n**Description:** Lists the ACL rules of a user.\n\n**Complexity:** O(N). Where N is the number of password, command and pattern rules that the user has.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-list/\">ACL LIST</a></strong> - Dumps the effective rules in ACL file format.</summary>\n\n**Syntax:** `ACL LIST`\n\n**Description:** Dumps the effective rules in ACL file format.\n\n**Complexity:** O(N). Where N is the number of configured users.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-load/\">ACL LOAD</a></strong> - Reloads the rules from the configured ACL file.</summary>\n\n**Syntax:** `ACL LOAD`\n\n**Description:** Reloads the rules from the configured ACL file.\n\n**Complexity:** O(N). Where N is the number of configured users.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-log/\">ACL LOG</a></strong> - Lists recent security events generated due to ACL rules.</summary>\n\n**Syntax:** `ACL LOG [count | RESET]`\n\n**Description:** Lists recent security events generated due to ACL rules.\n\n**Complexity:** O(N) with N being the number of entries shown.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-save/\">ACL SAVE</a></strong> - Saves the effective ACL rules in the configured ACL file.</summary>\n\n**Syntax:** `ACL SAVE`\n\n**Description:** Saves the effective ACL rules in the configured ACL file.\n\n**Complexity:** O(N). Where N is the number of configured users.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-setuser/\">ACL SETUSER</a></strong> - Creates and modifies an ACL user and its rules.</summary>\n\n**Syntax:** `ACL SETUSER username [rule [rule ...]]`\n\n**Description:** Creates and modifies an ACL user and its rules.\n\n**Complexity:** O(N). Where N is the number of rules provided.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-users/\">ACL USERS</a></strong> - Lists all ACL users.</summary>\n\n**Syntax:** `ACL USERS`\n\n**Description:** Lists all ACL users.\n\n**Complexity:** O(N). Where N is the number of configured users.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-whoami/\">ACL WHOAMI</a></strong> - Returns the authenticated username of the current connection.</summary>\n\n**Syntax:** `ACL WHOAMI`\n\n**Description:** Returns the authenticated username of the current connection.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bgrewriteaof/\">BGREWRITEAOF</a></strong> - Asynchronously rewrites the append-only file to disk.</summary>\n\n**Syntax:** `BGREWRITEAOF`\n\n**Description:** Asynchronously rewrites the append-only file to disk.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bgsave/\">BGSAVE</a></strong> - Asynchronously saves the database(s) to disk.</summary>\n\n**Syntax:** `BGSAVE [SCHEDULE]`\n\n**Description:** Asynchronously saves the database(s) to disk.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command/\">COMMAND</a></strong> - Returns detailed information about all commands.</summary>\n\n**Syntax:** `COMMAND`\n\n**Description:** Returns detailed information about all commands.\n\n**Complexity:** O(N) where N is the total number of Redis commands\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-count/\">COMMAND COUNT</a></strong> - Returns a count of commands.</summary>\n\n**Syntax:** `COMMAND COUNT`\n\n**Description:** Returns a count of commands.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-docs/\">COMMAND DOCS</a></strong> - Returns documentary information about one, multiple or all commands.</summary>\n\n**Syntax:** `COMMAND DOCS [command-name [command-name ...]]`\n\n**Description:** Returns documentary information about one, multiple or all commands.\n\n**Complexity:** O(N) where N is the number of commands to look up\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-getkeys/\">COMMAND GETKEYS</a></strong> - Extracts the key names from an arbitrary command.</summary>\n\n**Syntax:** `COMMAND GETKEYS command [arg [arg ...]]`\n\n**Description:** Extracts the key names from an arbitrary command.\n\n**Complexity:** O(N) where N is the number of arguments to the command\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-getkeysandflags/\">COMMAND GETKEYSANDFLAGS</a></strong> - Extracts the key names and access flags for an arbitrary command.</summary>\n\n**Syntax:** `COMMAND GETKEYSANDFLAGS command [arg [arg ...]]`\n\n**Description:** Extracts the key names and access flags for an arbitrary command.\n\n**Complexity:** O(N) where N is the number of arguments to the command\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-info/\">COMMAND INFO</a></strong> - Returns information about one, multiple or all commands.</summary>\n\n**Syntax:** `COMMAND INFO [command-name [command-name ...]]`\n\n**Description:** Returns information about one, multiple or all commands.\n\n**Complexity:** O(N) where N is the number of commands to look up\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-list/\">COMMAND LIST</a></strong> - Returns a list of command names.</summary>\n\n**Syntax:** `COMMAND LIST [FILTERBY <MODULE module-name | ACLCAT category |\n  PATTERN pattern>]`\n\n**Description:** Returns a list of command names.\n\n**Complexity:** O(N) where N is the total number of Redis commands\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/config-get/\">CONFIG GET</a></strong> - Returns the effective values of configuration parameters.</summary>\n\n**Syntax:** `CONFIG GET parameter [parameter ...]`\n\n**Description:** Returns the effective values of configuration parameters.\n\n**Complexity:** O(N) when N is the number of configuration parameters provided\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/config-resetstat/\">CONFIG RESETSTAT</a></strong> - Resets the server's statistics.</summary>\n\n**Syntax:** `CONFIG RESETSTAT`\n\n**Description:** Resets the server's statistics.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/config-rewrite/\">CONFIG REWRITE</a></strong> - Persists the effective configuration to file.</summary>\n\n**Syntax:** `CONFIG REWRITE`\n\n**Description:** Persists the effective configuration to file.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/config-set/\">CONFIG SET</a></strong> - Sets configuration parameters in-flight.</summary>\n\n**Syntax:** `CONFIG SET parameter value [parameter value ...]`\n\n**Description:** Sets configuration parameters in-flight.\n\n**Complexity:** O(N) when N is the number of configuration parameters provided\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/dbsize/\">DBSIZE</a></strong> - Returns the number of keys in the database.</summary>\n\n**Syntax:** `DBSIZE`\n\n**Description:** Returns the number of keys in the database.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/failover/\">FAILOVER</a></strong> - Starts a coordinated failover from a server to one of its replicas.</summary>\n\n**Syntax:** `FAILOVER [TO host port [FORCE]] [ABORT] [TIMEOUT milliseconds]`\n\n**Description:** Starts a coordinated failover from a server to one of its replicas.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/flushall/\">FLUSHALL</a></strong> - Removes all keys from all databases.</summary>\n\n**Syntax:** `FLUSHALL [ASYNC | SYNC]`\n\n**Description:** Removes all keys from all databases.\n\n**Complexity:** O(N) where N is the total number of keys in all databases\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/flushdb/\">FLUSHDB</a></strong> - Remove all keys from the current database.</summary>\n\n**Syntax:** `FLUSHDB [ASYNC | SYNC]`\n\n**Description:** Remove all keys from the current database.\n\n**Complexity:** O(N) where N is the number of keys in the selected database\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/info/\">INFO</a></strong> - Returns information and statistics about the server.</summary>\n\n**Syntax:** `INFO [section [section ...]]`\n\n**Description:** Returns information and statistics about the server.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lastsave/\">LASTSAVE</a></strong> - Returns the Unix timestamp of the last successful save to disk.</summary>\n\n**Syntax:** `LASTSAVE`\n\n**Description:** Returns the Unix timestamp of the last successful save to disk.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-doctor/\">LATENCY DOCTOR</a></strong> - Returns a human-readable latency analysis report.</summary>\n\n**Syntax:** `LATENCY DOCTOR`\n\n**Description:** Returns a human-readable latency analysis report.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-graph/\">LATENCY GRAPH</a></strong> - Returns a latency graph for an event.</summary>\n\n**Syntax:** `LATENCY GRAPH event`\n\n**Description:** Returns a latency graph for an event.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-histogram/\">LATENCY HISTOGRAM</a></strong> - Returns the cumulative distribution of latencies of a subset or all commands.</summary>\n\n**Syntax:** `LATENCY HISTOGRAM [command [command ...]]`\n\n**Description:** Returns the cumulative distribution of latencies of a subset or all commands.\n\n**Complexity:** O(N) where N is the number of commands with latency information being retrieved.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-history/\">LATENCY HISTORY</a></strong> - Returns timestamp-latency samples for an event.</summary>\n\n**Syntax:** `LATENCY HISTORY event`\n\n**Description:** Returns timestamp-latency samples for an event.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-latest/\">LATENCY LATEST</a></strong> - Returns the latest latency samples for all events.</summary>\n\n**Syntax:** `LATENCY LATEST`\n\n**Description:** Returns the latest latency samples for all events.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-reset/\">LATENCY RESET</a></strong> - Resets the latency data for one or more events.</summary>\n\n**Syntax:** `LATENCY RESET [event [event ...]]`\n\n**Description:** Resets the latency data for one or more events.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lolwut/\">LOLWUT</a></strong> - Displays computer art and the Redis version</summary>\n\n**Syntax:** `LOLWUT [VERSION version]`\n\n**Description:** Displays computer art and the Redis version\n\n**Complexity:** \n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-doctor/\">MEMORY DOCTOR</a></strong> - Outputs a memory problems report.</summary>\n\n**Syntax:** `MEMORY DOCTOR`\n\n**Description:** Outputs a memory problems report.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-malloc-stats/\">MEMORY MALLOC-STATS</a></strong> - Returns the allocator statistics.</summary>\n\n**Syntax:** `MEMORY MALLOC-STATS`\n\n**Description:** Returns the allocator statistics.\n\n**Complexity:** Depends on how much memory is allocated, could be slow\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-purge/\">MEMORY PURGE</a></strong> - Asks the allocator to release memory.</summary>\n\n**Syntax:** `MEMORY PURGE`\n\n**Description:** Asks the allocator to release memory.\n\n**Complexity:** Depends on how much memory is allocated, could be slow\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-stats/\">MEMORY STATS</a></strong> - Returns details about memory usage.</summary>\n\n**Syntax:** `MEMORY STATS`\n\n**Description:** Returns details about memory usage.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-usage/\">MEMORY USAGE</a></strong> - Estimates the memory usage of a key.</summary>\n\n**Syntax:** `MEMORY USAGE key [SAMPLES count]`\n\n**Description:** Estimates the memory usage of a key.\n\n**Complexity:** O(N) where N is the number of samples.\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/module-list/\">MODULE LIST</a></strong> - Returns all loaded modules.</summary>\n\n**Syntax:** `MODULE LIST`\n\n**Description:** Returns all loaded modules.\n\n**Complexity:** O(N) where N is the number of loaded modules.\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/module-load/\">MODULE LOAD</a></strong> - Loads a module.</summary>\n\n**Syntax:** `MODULE LOAD path [arg [arg ...]]`\n\n**Description:** Loads a module.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/module-loadex/\">MODULE LOADEX</a></strong> - Loads a module using extended parameters.</summary>\n\n**Syntax:** `MODULE LOADEX path [CONFIG name value [CONFIG name value ...]]\n  [ARGS args [args ...]]`\n\n**Description:** Loads a module using extended parameters.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/module-unload/\">MODULE UNLOAD</a></strong> - Unloads a module.</summary>\n\n**Syntax:** `MODULE UNLOAD name`\n\n**Description:** Unloads a module.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/monitor/\">MONITOR</a></strong> - Listens for all requests received by the server in real-time.</summary>\n\n**Syntax:** `MONITOR`\n\n**Description:** Listens for all requests received by the server in real-time.\n\n**Complexity:** \n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/psync/\">PSYNC</a></strong> - An internal command used in replication.</summary>\n\n**Syntax:** `PSYNC replicationid offset`\n\n**Description:** An internal command used in replication.\n\n**Complexity:** \n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/replconf/\">REPLCONF</a></strong> - An internal command for configuring the replication stream.</summary>\n\n**Syntax:** `REPLCONF`\n\n**Description:** An internal command for configuring the replication stream.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/replicaof/\">REPLICAOF</a></strong> - Configures a server as replica of another, or promotes it to a master.</summary>\n\n**Syntax:** `REPLICAOF <host port | NO ONE>`\n\n**Description:** Configures a server as replica of another, or promotes it to a master.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/restore-asking/\">RESTORE-ASKING</a></strong> - An internal command for migrating keys in a cluster.</summary>\n\n**Syntax:** `RESTORE-ASKING key ttl serialized-value [REPLACE] [ABSTTL]\n  [IDLETIME seconds] [FREQ frequency]`\n\n**Description:** An internal command for migrating keys in a cluster.\n\n**Complexity:** O(1) to create the new key and additional O(N*M) to reconstruct the serialized value, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1). However for sorted set values the complexity is O(N*M*log(N)) because inserting values into sorted sets is O(log(N)).\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/role/\">ROLE</a></strong> - Returns the replication role.</summary>\n\n**Syntax:** `ROLE`\n\n**Description:** Returns the replication role.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.12\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/save/\">SAVE</a></strong> - Synchronously saves the database(s) to disk.</summary>\n\n**Syntax:** `SAVE`\n\n**Description:** Synchronously saves the database(s) to disk.\n\n**Complexity:** O(N) where N is the total number of keys in all databases\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/shutdown/\">SHUTDOWN</a></strong> - Synchronously saves the database(s) to disk and shuts down the Redis server.</summary>\n\n**Syntax:** `SHUTDOWN [NOSAVE | SAVE] [NOW] [FORCE] [ABORT]`\n\n**Description:** Synchronously saves the database(s) to disk and shuts down the Redis server.\n\n**Complexity:** O(N) when saving, where N is the total number of keys in all databases when saving data, otherwise O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/slaveof/\">SLAVEOF</a></strong> - Sets a Redis server as a replica of another, or promotes it to being a master.</summary>\n\n**Syntax:** `SLAVEOF <host port | NO ONE>`\n\n**Description:** Sets a Redis server as a replica of another, or promotes it to being a master.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/slowlog-get/\">SLOWLOG GET</a></strong> - Returns the slow log's entries.</summary>\n\n**Syntax:** `SLOWLOG GET [count]`\n\n**Description:** Returns the slow log's entries.\n\n**Complexity:** O(N) where N is the number of entries returned\n\n**Since:** 2.2.12\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/slowlog-len/\">SLOWLOG LEN</a></strong> - Returns the number of entries in the slow log.</summary>\n\n**Syntax:** `SLOWLOG LEN`\n\n**Description:** Returns the number of entries in the slow log.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.12\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/slowlog-reset/\">SLOWLOG RESET</a></strong> - Clears all entries from the slow log.</summary>\n\n**Syntax:** `SLOWLOG RESET`\n\n**Description:** Clears all entries from the slow log.\n\n**Complexity:** O(N) where N is the number of entries in the slowlog\n\n**Since:** 2.2.12\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/swapdb/\">SWAPDB</a></strong> - Swaps two Redis databases.</summary>\n\n**Syntax:** `SWAPDB index1 index2`\n\n**Description:** Swaps two Redis databases.\n\n**Complexity:** O(N) where N is the count of clients watching or blocking on keys from both databases.\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sync/\">SYNC</a></strong> - An internal command used in replication.</summary>\n\n**Syntax:** `SYNC`\n\n**Description:** An internal command used in replication.\n\n**Complexity:** \n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/time/\">TIME</a></strong> - Returns the server time.</summary>\n\n**Syntax:** `TIME`\n\n**Description:** Returns the server time.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>"},{"id":"cluster-commands","title":"Cluster commands","role":"content","text":"Cluster commands manage Redis Cluster operations.\n\n<details>\n<summary><strong><a href=\"/commands/asking/\">ASKING</a></strong> - Signals that a cluster client is following an -ASK redirect.</summary>\n\n**Syntax:** `ASKING`\n\n**Description:** Signals that a cluster client is following an -ASK redirect.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-addslots/\">CLUSTER ADDSLOTS</a></strong> - Assigns new hash slots to a node.</summary>\n\n**Syntax:** `CLUSTER ADDSLOTS slot [slot ...]`\n\n**Description:** Assigns new hash slots to a node.\n\n**Complexity:** O(N) where N is the total number of hash slot arguments\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-addslotsrange/\">CLUSTER ADDSLOTSRANGE</a></strong> - Assigns new hash slot ranges to a node.</summary>\n\n**Syntax:** `CLUSTER ADDSLOTSRANGE start-slot end-slot [start-slot end-slot ...]`\n\n**Description:** Assigns new hash slot ranges to a node.\n\n**Complexity:** O(N) where N is the total number of the slots between the start slot and end slot arguments.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-bumpepoch/\">CLUSTER BUMPEPOCH</a></strong> - Advances the cluster config epoch.</summary>\n\n**Syntax:** `CLUSTER BUMPEPOCH`\n\n**Description:** Advances the cluster config epoch.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-count-failure-reports/\">CLUSTER COUNT-FAILURE-REPORTS</a></strong> - Returns the number of active failure reports active for a node.</summary>\n\n**Syntax:** `CLUSTER COUNT-FAILURE-REPORTS node-id`\n\n**Description:** Returns the number of active failure reports active for a node.\n\n**Complexity:** O(N) where N is the number of failure reports\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-countkeysinslot/\">CLUSTER COUNTKEYSINSLOT</a></strong> - Returns the number of keys in a hash slot.</summary>\n\n**Syntax:** `CLUSTER COUNTKEYSINSLOT slot`\n\n**Description:** Returns the number of keys in a hash slot.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-delslots/\">CLUSTER DELSLOTS</a></strong> - Sets hash slots as unbound for a node.</summary>\n\n**Syntax:** `CLUSTER DELSLOTS slot [slot ...]`\n\n**Description:** Sets hash slots as unbound for a node.\n\n**Complexity:** O(N) where N is the total number of hash slot arguments\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-delslotsrange/\">CLUSTER DELSLOTSRANGE</a></strong> - Sets hash slot ranges as unbound for a node.</summary>\n\n**Syntax:** `CLUSTER DELSLOTSRANGE start-slot end-slot [start-slot end-slot ...]`\n\n**Description:** Sets hash slot ranges as unbound for a node.\n\n**Complexity:** O(N) where N is the total number of the slots between the start slot and end slot arguments.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-failover/\">CLUSTER FAILOVER</a></strong> - Forces a replica to perform a manual failover of its master.</summary>\n\n**Syntax:** `CLUSTER FAILOVER [FORCE | TAKEOVER]`\n\n**Description:** Forces a replica to perform a manual failover of its master.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-flushslots/\">CLUSTER FLUSHSLOTS</a></strong> - Deletes all slots information from a node.</summary>\n\n**Syntax:** `CLUSTER FLUSHSLOTS`\n\n**Description:** Deletes all slots information from a node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-forget/\">CLUSTER FORGET</a></strong> - Removes a node from the nodes table.</summary>\n\n**Syntax:** `CLUSTER FORGET node-id`\n\n**Description:** Removes a node from the nodes table.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-getkeysinslot/\">CLUSTER GETKEYSINSLOT</a></strong> - Returns the key names in a hash slot.</summary>\n\n**Syntax:** `CLUSTER GETKEYSINSLOT slot count`\n\n**Description:** Returns the key names in a hash slot.\n\n**Complexity:** O(N) where N is the number of requested keys\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-info/\">CLUSTER INFO</a></strong> - Returns information about the state of a node.</summary>\n\n**Syntax:** `CLUSTER INFO`\n\n**Description:** Returns information about the state of a node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-keyslot/\">CLUSTER KEYSLOT</a></strong> - Returns the hash slot for a key.</summary>\n\n**Syntax:** `CLUSTER KEYSLOT key`\n\n**Description:** Returns the hash slot for a key.\n\n**Complexity:** O(N) where N is the number of bytes in the key\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-links/\">CLUSTER LINKS</a></strong> - Returns a list of all TCP links to and from peer nodes.</summary>\n\n**Syntax:** `CLUSTER LINKS`\n\n**Description:** Returns a list of all TCP links to and from peer nodes.\n\n**Complexity:** O(N) where N is the total number of Cluster nodes\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-meet/\">CLUSTER MEET</a></strong> - Forces a node to handshake with another node.</summary>\n\n**Syntax:** `CLUSTER MEET ip port [cluster-bus-port]`\n\n**Description:** Forces a node to handshake with another node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-myid/\">CLUSTER MYID</a></strong> - Returns the ID of a node.</summary>\n\n**Syntax:** `CLUSTER MYID`\n\n**Description:** Returns the ID of a node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-myshardid/\">CLUSTER MYSHARDID</a></strong> - Returns the shard ID of a node.</summary>\n\n**Syntax:** `CLUSTER MYSHARDID`\n\n**Description:** Returns the shard ID of a node.\n\n**Complexity:** O(1)\n\n**Since:** 7.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-nodes/\">CLUSTER NODES</a></strong> - Returns the cluster configuration for a node.</summary>\n\n**Syntax:** `CLUSTER NODES`\n\n**Description:** Returns the cluster configuration for a node.\n\n**Complexity:** O(N) where N is the total number of Cluster nodes\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-replicas/\">CLUSTER REPLICAS</a></strong> - Lists the replica nodes of a master node.</summary>\n\n**Syntax:** `CLUSTER REPLICAS node-id`\n\n**Description:** Lists the replica nodes of a master node.\n\n**Complexity:** O(N) where N is the number of replicas.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-replicate/\">CLUSTER REPLICATE</a></strong> - Configure a node as replica of a master node.</summary>\n\n**Syntax:** `CLUSTER REPLICATE node-id`\n\n**Description:** Configure a node as replica of a master node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-reset/\">CLUSTER RESET</a></strong> - Resets a node.</summary>\n\n**Syntax:** `CLUSTER RESET [HARD | SOFT]`\n\n**Description:** Resets a node.\n\n**Complexity:** O(N) where N is the number of known nodes. The command may execute a FLUSHALL as a side effect.\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-saveconfig/\">CLUSTER SAVECONFIG</a></strong> - Forces a node to save the cluster configuration to disk.</summary>\n\n**Syntax:** `CLUSTER SAVECONFIG`\n\n**Description:** Forces a node to save the cluster configuration to disk.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-set-config-epoch/\">CLUSTER SET-CONFIG-EPOCH</a></strong> - Sets the configuration epoch for a new node.</summary>\n\n**Syntax:** `CLUSTER SET-CONFIG-EPOCH config-epoch`\n\n**Description:** Sets the configuration epoch for a new node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-setslot/\">CLUSTER SETSLOT</a></strong> - Binds a hash slot to a node.</summary>\n\n**Syntax:** `CLUSTER SETSLOT slot <IMPORTING node-id | MIGRATING node-id |\n  NODE node-id | STABLE>`\n\n**Description:** Binds a hash slot to a node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-shards/\">CLUSTER SHARDS</a></strong> - Returns the mapping of cluster slots to shards.</summary>\n\n**Syntax:** `CLUSTER SHARDS`\n\n**Description:** Returns the mapping of cluster slots to shards.\n\n**Complexity:** O(N) where N is the total number of cluster nodes\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-slaves/\">CLUSTER SLAVES</a></strong> - Lists the replica nodes of a master node.</summary>\n\n**Syntax:** `CLUSTER SLAVES node-id`\n\n**Description:** Lists the replica nodes of a master node.\n\n**Complexity:** O(N) where N is the number of replicas.\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-slots/\">CLUSTER SLOTS</a></strong> - Returns the mapping of cluster slots to nodes.</summary>\n\n**Syntax:** `CLUSTER SLOTS`\n\n**Description:** Returns the mapping of cluster slots to nodes.\n\n**Complexity:** O(N) where N is the total number of Cluster nodes\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/readonly/\">READONLY</a></strong> - Enables read-only queries for a connection to a Redis Cluster replica node.</summary>\n\n**Syntax:** `READONLY`\n\n**Description:** Enables read-only queries for a connection to a Redis Cluster replica node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/readwrite/\">READWRITE</a></strong> - Enables read-write queries for a connection to a Reids Cluster replica node.</summary>\n\n**Syntax:** `READWRITE`\n\n**Description:** Enables read-write queries for a connection to a Reids Cluster replica node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>"},{"id":"generic-commands","title":"Generic commands","role":"content","text":"Generic commands work across all data types.\n\n<details>\n<summary><strong><a href=\"/commands/copy/\">COPY</a></strong> - Copies the value of a key to a new key.</summary>\n\n**Syntax:** `COPY source destination [DB destination-db] [REPLACE]`\n\n**Description:** Copies the value of a key to a new key.\n\n**Complexity:** O(N) worst case for collections, where N is the number of nested items. O(1) for string values.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/del/\">DEL</a></strong> - Deletes one or more keys.</summary>\n\n**Syntax:** `DEL key [key ...]`\n\n**Description:** Deletes one or more keys.\n\n**Complexity:** O(N) where N is the number of keys that will be removed. When a key to remove holds a value other than a string, the individual complexity for this key is O(M) where M is the number of elements in the list, set, sorted set or hash. Removing a single key that holds a string value is O(1).\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/dump/\">DUMP</a></strong> - Returns a serialized representation of the value stored at a key.</summary>\n\n**Syntax:** `DUMP key`\n\n**Description:** Returns a serialized representation of the value stored at a key.\n\n**Complexity:** O(1) to access the key and additional O(N*M) to serialize it, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1).\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/exists/\">EXISTS</a></strong> - Determines whether one or more keys exist.</summary>\n\n**Syntax:** `EXISTS key [key ...]`\n\n**Description:** Determines whether one or more keys exist.\n\n**Complexity:** O(N) where N is the number of keys to check.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/expire/\">EXPIRE</a></strong> - Sets the expiration time of a key in seconds.</summary>\n\n**Syntax:** `EXPIRE key seconds [NX | XX | GT | LT]`\n\n**Description:** Sets the expiration time of a key in seconds.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/expireat/\">EXPIREAT</a></strong> - Sets the expiration time of a key to a Unix timestamp.</summary>\n\n**Syntax:** `EXPIREAT key unix-time-seconds [NX | XX | GT | LT]`\n\n**Description:** Sets the expiration time of a key to a Unix timestamp.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/expiretime/\">EXPIRETIME</a></strong> - Returns the expiration time of a key as a Unix timestamp.</summary>\n\n**Syntax:** `EXPIRETIME key`\n\n**Description:** Returns the expiration time of a key as a Unix timestamp.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/keys/\">KEYS</a></strong> - Returns all key names that match a pattern.</summary>\n\n**Syntax:** `KEYS pattern`\n\n**Description:** Returns all key names that match a pattern.\n\n**Complexity:** O(N) with N being the number of keys in the database, under the assumption that the key names in the database and the given pattern have limited length.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/migrate/\">MIGRATE</a></strong> - Atomically transfers a key from one Redis instance to another.</summary>\n\n**Syntax:** `MIGRATE host port <key | \"\"> destination-db timeout [COPY] [REPLACE]\n  [AUTH password | AUTH2 username password] [KEYS key [key ...]]`\n\n**Description:** Atomically transfers a key from one Redis instance to another.\n\n**Complexity:** This command actually executes a DUMP+DEL in the source instance, and a RESTORE in the target instance. See the pages of these commands for time complexity. Also an O(N) data transfer between the two instances is performed.\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/move/\">MOVE</a></strong> - Moves a key to another database.</summary>\n\n**Syntax:** `MOVE key db`\n\n**Description:** Moves a key to another database.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/object-encoding/\">OBJECT ENCODING</a></strong> - Returns the internal encoding of a Redis object.</summary>\n\n**Syntax:** `OBJECT ENCODING key`\n\n**Description:** Returns the internal encoding of a Redis object.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.3\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/object-freq/\">OBJECT FREQ</a></strong> - Returns the logarithmic access frequency counter of a Redis object.</summary>\n\n**Syntax:** `OBJECT FREQ key`\n\n**Description:** Returns the logarithmic access frequency counter of a Redis object.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/object-idletime/\">OBJECT IDLETIME</a></strong> - Returns the time since the last access to a Redis object.</summary>\n\n**Syntax:** `OBJECT IDLETIME key`\n\n**Description:** Returns the time since the last access to a Redis object.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.3\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/object-refcount/\">OBJECT REFCOUNT</a></strong> - Returns the reference count of a value of a key.</summary>\n\n**Syntax:** `OBJECT REFCOUNT key`\n\n**Description:** Returns the reference count of a value of a key.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.3\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/persist/\">PERSIST</a></strong> - Removes the expiration time of a key.</summary>\n\n**Syntax:** `PERSIST key`\n\n**Description:** Removes the expiration time of a key.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pexpire/\">PEXPIRE</a></strong> - Sets the expiration time of a key in milliseconds.</summary>\n\n**Syntax:** `PEXPIRE key milliseconds [NX | XX | GT | LT]`\n\n**Description:** Sets the expiration time of a key in milliseconds.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pexpireat/\">PEXPIREAT</a></strong> - Sets the expiration time of a key to a Unix milliseconds timestamp.</summary>\n\n**Syntax:** `PEXPIREAT key unix-time-milliseconds [NX | XX | GT | LT]`\n\n**Description:** Sets the expiration time of a key to a Unix milliseconds timestamp.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pexpiretime/\">PEXPIRETIME</a></strong> - Returns the expiration time of a key as a Unix milliseconds timestamp.</summary>\n\n**Syntax:** `PEXPIRETIME key`\n\n**Description:** Returns the expiration time of a key as a Unix milliseconds timestamp.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pttl/\">PTTL</a></strong> - Returns the expiration time in milliseconds of a key.</summary>\n\n**Syntax:** `PTTL key`\n\n**Description:** Returns the expiration time in milliseconds of a key.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/randomkey/\">RANDOMKEY</a></strong> - Returns a random key name from the database.</summary>\n\n**Syntax:** `RANDOMKEY`\n\n**Description:** Returns a random key name from the database.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rename/\">RENAME</a></strong> - Renames a key and overwrites the destination.</summary>\n\n**Syntax:** `RENAME key newkey`\n\n**Description:** Renames a key and overwrites the destination.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/renamenx/\">RENAMENX</a></strong> - Renames a key only when the target key name doesn't exist.</summary>\n\n**Syntax:** `RENAMENX key newkey`\n\n**Description:** Renames a key only when the target key name doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/restore/\">RESTORE</a></strong> - Creates a key from the serialized representation of a value.</summary>\n\n**Syntax:** `RESTORE key ttl serialized-value [REPLACE] [ABSTTL]\n  [IDLETIME seconds] [FREQ frequency]`\n\n**Description:** Creates a key from the serialized representation of a value.\n\n**Complexity:** O(1) to create the new key and additional O(N*M) to reconstruct the serialized value, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1). However for sorted set values the complexity is O(N*M*log(N)) because inserting values into sorted sets is O(log(N)).\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-6-2-commands/\">Redis 6.2 Commands Reference</a></strong> - Complete list of all Redis commands available in version 6.2, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 6.2, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-7-2-commands/\">Redis 7.2 Commands Reference</a></strong> - Complete list of all Redis commands available in version 7.2, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 7.2, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-7-4-commands/\">Redis 7.4 Commands Reference</a></strong> - Complete list of all Redis commands available in version 7.4, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 7.4, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-8-0-commands/\">Redis 8.0 Commands Reference</a></strong> - Complete list of all Redis commands available in version 8.0, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 8.0, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-8-2-commands/\">Redis 8.2 Commands Reference</a></strong> - Complete list of all Redis commands available in version 8.2, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 8.2, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-8-4-commands/\">Redis 8.4 Commands Reference</a></strong> - Complete list of all Redis commands available in version 8.4, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 8.4, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/scan/\">SCAN</a></strong> - Iterates over the key names in the database.</summary>\n\n**Syntax:** `SCAN cursor [MATCH pattern] [COUNT count] [TYPE type]`\n\n**Description:** Iterates over the key names in the database.\n\n**Complexity:** O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sort/\">SORT</a></strong> - Sorts the elements in a list, a set, or a sorted set, optionally storing the result.</summary>\n\n**Syntax:** `SORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern\n  ...]] [ASC | DESC] [ALPHA] [STORE destination]`\n\n**Description:** Sorts the elements in a list, a set, or a sorted set, optionally storing the result.\n\n**Complexity:** O(N+M*log(M)) where N is the number of elements in the list or set to sort, and M the number of returned elements. When the elements are not sorted, complexity is O(N).\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sort_ro/\">SORT_RO</a></strong> - Returns the sorted elements of a list, a set, or a sorted set.</summary>\n\n**Syntax:** `SORT RO key [BY pattern] [LIMIT offset count] [GET pattern [GET\n  pattern ...]] [ASC | DESC] [ALPHA]`\n\n**Description:** Returns the sorted elements of a list, a set, or a sorted set.\n\n**Complexity:** O(N+M*log(M)) where N is the number of elements in the list or set to sort, and M the number of returned elements. When the elements are not sorted, complexity is O(N).\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/touch/\">TOUCH</a></strong> - Returns the number of existing keys out of those specified after updating the time they were last accessed.</summary>\n\n**Syntax:** `TOUCH key [key ...]`\n\n**Description:** Returns the number of existing keys out of those specified after updating the time they were last accessed.\n\n**Complexity:** O(N) where N is the number of keys that will be touched.\n\n**Since:** 3.2.1\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ttl/\">TTL</a></strong> - Returns the expiration time in seconds of a key.</summary>\n\n**Syntax:** `TTL key`\n\n**Description:** Returns the expiration time in seconds of a key.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/type/\">TYPE</a></strong> - Determines the type of value stored at a key.</summary>\n\n**Syntax:** `TYPE key`\n\n**Description:** Determines the type of value stored at a key.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/unlink/\">UNLINK</a></strong> - Asynchronously deletes one or more keys.</summary>\n\n**Syntax:** `UNLINK key [key ...]`\n\n**Description:** Asynchronously deletes one or more keys.\n\n**Complexity:** O(1) for each key removed regardless of its size. Then the command does O(N) work in a different thread in order to reclaim memory, where N is the number of allocations the deleted objects where composed of.\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/wait/\">WAIT</a></strong> - Blocks until the asynchronous replication of all preceding write commands sent by the connection is completed.</summary>\n\n**Syntax:** `WAIT numreplicas timeout`\n\n**Description:** Blocks until the asynchronous replication of all preceding write commands sent by the connection is completed.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/waitaof/\">WAITAOF</a></strong> - Blocks until all of the preceding write commands sent by the connection are written to the append-only file of the master and/or replicas.</summary>\n\n**Syntax:** `WAITAOF numlocal numreplicas timeout`\n\n**Description:** Blocks until all of the preceding write commands sent by the connection are written to the append-only file of the master and/or replicas.\n\n**Complexity:** O(1)\n\n**Since:** 7.2.0\n\n</details>"}],"examples":[]}
{"schema_version":2,"id":"commands/redis-8-0-commands","title":"Redis 8.0 Commands Reference","url":"https://redis.io/docs/latest/commands/redis-8-0-commands/","summary":"Complete list of all Redis commands available in version 8.0, organized by functional group","tags":["docs","develop","stack","oss","rs","rc","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"af559c0497f2af765afa971c110b36af85b8a4d59852343b305b655acb4ece7f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This page provides a comprehensive reference of all Redis commands available in Redis 8.0, organized by functional group. Each command includes its description and syntax in a collapsible section for easy navigation.\n\n> [!NOTE]\n> Redis 8.0 includes all commands from previous versions plus new commands introduced in 8.0. Commands marked with **⭐ New in 8.0** were added in this release.\n>\n> Command page links take you to the *current implementation* of each command."},{"id":"quick-navigation","title":"Quick Navigation","role":"content","text":"- [String commands](#string-commands)\n- [Hash commands](#hash-commands)\n- [List commands](#list-commands)\n- [Set commands](#set-commands)\n- [Sorted set commands](#sorted-set-commands)\n- [Stream commands](#stream-commands)\n- [Bitmap commands](#bitmap-commands)\n- [HyperLogLog commands](#hyperloglog-commands)\n- [Geospatial commands](#geospatial-commands)\n- [JSON commands](#json-commands)\n- [Search commands](#search-commands)\n- [Time series commands](#time-series-commands)\n- [Vector set commands](#vector-set-commands)\n- [Pub/Sub commands](#pubsub-commands)\n- [Transaction commands](#transaction-commands)\n- [Scripting commands](#scripting-commands)\n- [Connection commands](#connection-commands)\n- [Server commands](#server-commands)\n- [Cluster commands](#cluster-commands)\n- [Generic commands](#generic-commands)\n\n---"},{"id":"string-commands","title":"String commands","role":"content","text":"String commands operate on string values, the most basic Redis data type.\n\n<details>\n<summary><strong><a href=\"/commands/append/\">APPEND</a></strong> - Appends a string to the value of a key. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `APPEND key value`\n\n**Description:** Appends a string to the value of a key. Creates the key if it doesn't exist.\n\n**Complexity:** O(1). The amortized time complexity is O(1) assuming the appended value is small and the already present value is of any size, since the dynamic string library used by Redis will double the free space available on every reallocation.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/decr/\">DECR</a></strong> - Decrements the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `DECR key`\n\n**Description:** Decrements the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/decrby/\">DECRBY</a></strong> - Decrements a number from the integer value of a key. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `DECRBY key decrement`\n\n**Description:** Decrements a number from the integer value of a key. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/get/\">GET</a></strong> - Returns the string value of a key.</summary>\n\n**Syntax:** `GET key`\n\n**Description:** Returns the string value of a key.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getdel/\">GETDEL</a></strong> - Returns the string value of a key after deleting the key.</summary>\n\n**Syntax:** `GETDEL key`\n\n**Description:** Returns the string value of a key after deleting the key.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getex/\">GETEX</a></strong> - Returns the string value of a key after setting its expiration time.</summary>\n\n**Syntax:** `GETEX key [EX seconds | PX milliseconds | EXAT unix-time-seconds |\n  PXAT unix-time-milliseconds | PERSIST]`\n\n**Description:** Returns the string value of a key after setting its expiration time.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getrange/\">GETRANGE</a></strong> - Returns a substring of the string stored at a key.</summary>\n\n**Syntax:** `GETRANGE key start end`\n\n**Description:** Returns a substring of the string stored at a key.\n\n**Complexity:** O(N) where N is the length of the returned string. The complexity is ultimately determined by the returned length, but because creating a substring from an existing string is very cheap, it can be considered O(1) for small strings.\n\n**Since:** 2.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getset/\">GETSET</a></strong> - Returns the previous string value of a key after setting it to a new value.</summary>\n\n**Syntax:** `GETSET key value`\n\n**Description:** Returns the previous string value of a key after setting it to a new value.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/incr/\">INCR</a></strong> - Increments the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `INCR key`\n\n**Description:** Increments the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/incrby/\">INCRBY</a></strong> - Increments the integer value of a key by a number. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `INCRBY key increment`\n\n**Description:** Increments the integer value of a key by a number. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/incrbyfloat/\">INCRBYFLOAT</a></strong> - Increment the floating point value of a key by a number. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `INCRBYFLOAT key increment`\n\n**Description:** Increment the floating point value of a key by a number. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lcs/\">LCS</a></strong> - Finds the longest common substring.</summary>\n\n**Syntax:** `LCS key1 key2 [LEN] [IDX] [MINMATCHLEN min-match-len] [WITHMATCHLEN]`\n\n**Description:** Finds the longest common substring.\n\n**Complexity:** O(N*M) where N and M are the lengths of s1 and s2, respectively\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/mget/\">MGET</a></strong> - Atomically returns the string values of one or more keys.</summary>\n\n**Syntax:** `MGET key [key ...]`\n\n**Description:** Atomically returns the string values of one or more keys.\n\n**Complexity:** O(N) where N is the number of keys to retrieve.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/mset/\">MSET</a></strong> - Atomically creates or modifies the string values of one or more keys.</summary>\n\n**Syntax:** `MSET key value [key value ...]`\n\n**Description:** Atomically creates or modifies the string values of one or more keys.\n\n**Complexity:** O(N) where N is the number of keys to set.\n\n**Since:** 1.0.1\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/msetnx/\">MSETNX</a></strong> - Atomically modifies the string values of one or more keys only when all keys don't exist.</summary>\n\n**Syntax:** `MSETNX key value [key value ...]`\n\n**Description:** Atomically modifies the string values of one or more keys only when all keys don't exist.\n\n**Complexity:** O(N) where N is the number of keys to set.\n\n**Since:** 1.0.1\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/psetex/\">PSETEX</a></strong> - Sets both string value and expiration time in milliseconds of a key. The key is created if it doesn't exist.</summary>\n\n**Syntax:** `PSETEX key milliseconds value`\n\n**Description:** Sets both string value and expiration time in milliseconds of a key. The key is created if it doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/set/\">SET</a></strong> - Sets the string value of a key, ignoring its type. The key is created if it doesn't exist.</summary>\n\n**Syntax:** `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]`\n\n**Description:** Sets the string value of a key, ignoring its type. The key is created if it doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/setex/\">SETEX</a></strong> - Sets the string value and expiration time of a key. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `SETEX key seconds value`\n\n**Description:** Sets the string value and expiration time of a key. Creates the key if it doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/setnx/\">SETNX</a></strong> - Set the string value of a key only when the key doesn't exist.</summary>\n\n**Syntax:** `SETNX key value`\n\n**Description:** Set the string value of a key only when the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/setrange/\">SETRANGE</a></strong> - Overwrites a part of a string value with another by an offset. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `SETRANGE key offset value`\n\n**Description:** Overwrites a part of a string value with another by an offset. Creates the key if it doesn't exist.\n\n**Complexity:** O(1), not counting the time taken to copy the new string in place. Usually, this string is very small so the amortized complexity is O(1). Otherwise, complexity is O(M) with M being the length of the value argument.\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/strlen/\">STRLEN</a></strong> - Returns the length of a string value.</summary>\n\n**Syntax:** `STRLEN key`\n\n**Description:** Returns the length of a string value.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/substr/\">SUBSTR</a></strong> - Returns a substring from a string value.</summary>\n\n**Syntax:** `SUBSTR key start end`\n\n**Description:** Returns a substring from a string value.\n\n**Complexity:** O(N) where N is the length of the returned string. The complexity is ultimately determined by the returned length, but because creating a substring from an existing string is very cheap, it can be considered O(1) for small strings.\n\n**Since:** 1.0.0\n\n</details>"},{"id":"hash-commands","title":"Hash commands","role":"content","text":"Hash commands operate on hash data structures, which map string fields to string values.\n\n<details>\n<summary><strong><a href=\"/commands/hdel/\">HDEL</a></strong> - Deletes one or more fields and their values from a hash. Deletes the hash if no fields remain.</summary>\n\n**Syntax:** `HDEL key field [field ...]`\n\n**Description:** Deletes one or more fields and their values from a hash. Deletes the hash if no fields remain.\n\n**Complexity:** O(N) where N is the number of fields to be removed.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hexists/\">HEXISTS</a></strong> - Determines whether a field exists in a hash.</summary>\n\n**Syntax:** `HEXISTS key field`\n\n**Description:** Determines whether a field exists in a hash.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hexpire/\">HEXPIRE</a></strong> - Set expiry for hash field using relative time to expire (seconds)</summary>\n\n**Syntax:** `HEXPIRE key seconds [NX | XX | GT | LT] FIELDS numfields field\n  [field ...]`\n\n**Description:** Set expiry for hash field using relative time to expire (seconds)\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hexpireat/\">HEXPIREAT</a></strong> - Set expiry for hash field using an absolute Unix timestamp (seconds)</summary>\n\n**Syntax:** `HEXPIREAT key unix-time-seconds [NX | XX | GT | LT] FIELDS numfields\n  field [field ...]`\n\n**Description:** Set expiry for hash field using an absolute Unix timestamp (seconds)\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hexpiretime/\">HEXPIRETIME</a></strong> - Returns the expiration time of a hash field as a Unix timestamp, in seconds.</summary>\n\n**Syntax:** `HEXPIRETIME key FIELDS numfields field [field ...]`\n\n**Description:** Returns the expiration time of a hash field as a Unix timestamp, in seconds.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hget/\">HGET</a></strong> - Returns the value of a field in a hash.</summary>\n\n**Syntax:** `HGET key field`\n\n**Description:** Returns the value of a field in a hash.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hgetall/\">HGETALL</a></strong> - Returns all fields and values in a hash.</summary>\n\n**Syntax:** `HGETALL key`\n\n**Description:** Returns all fields and values in a hash.\n\n**Complexity:** O(N) where N is the size of the hash.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hgetdel/\">HGETDEL</a></strong> - Returns the value of a field and deletes it from the hash. <span style=\"color: #e74c3c;\">⭐ New in 8.0</span></summary>\n\n**Syntax:** `HGETDEL key FIELDS numfields field [field ...]`\n\n**Description:** Returns the value of a field and deletes it from the hash.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hgetex/\">HGETEX</a></strong> - Get the value of one or more fields of a given hash key, and optionally set their expiration. <span style=\"color: #e74c3c;\">⭐ New in 8.0</span></summary>\n\n**Syntax:** `HGETEX key [EX seconds | PX milliseconds | EXAT unix-time-seconds |\n  PXAT unix-time-milliseconds | PERSIST] FIELDS numfields field\n  [field ...]`\n\n**Description:** Get the value of one or more fields of a given hash key, and optionally set their expiration.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hincrby/\">HINCRBY</a></strong> - Increments the integer value of a field in a hash by a number. Uses 0 as initial value if the field doesn't exist.</summary>\n\n**Syntax:** `HINCRBY key field increment`\n\n**Description:** Increments the integer value of a field in a hash by a number. Uses 0 as initial value if the field doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hincrbyfloat/\">HINCRBYFLOAT</a></strong> - Increments the floating point value of a field by a number. Uses 0 as initial value if the field doesn't exist.</summary>\n\n**Syntax:** `HINCRBYFLOAT key field increment`\n\n**Description:** Increments the floating point value of a field by a number. Uses 0 as initial value if the field doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hkeys/\">HKEYS</a></strong> - Returns all fields in a hash.</summary>\n\n**Syntax:** `HKEYS key`\n\n**Description:** Returns all fields in a hash.\n\n**Complexity:** O(N) where N is the size of the hash.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hlen/\">HLEN</a></strong> - Returns the number of fields in a hash.</summary>\n\n**Syntax:** `HLEN key`\n\n**Description:** Returns the number of fields in a hash.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hmget/\">HMGET</a></strong> - Returns the values of all fields in a hash.</summary>\n\n**Syntax:** `HMGET key field [field ...]`\n\n**Description:** Returns the values of all fields in a hash.\n\n**Complexity:** O(N) where N is the number of fields being requested.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hmset/\">HMSET</a></strong> - Sets the values of multiple fields.</summary>\n\n**Syntax:** `HMSET key field value [field value ...]`\n\n**Description:** Sets the values of multiple fields.\n\n**Complexity:** O(N) where N is the number of fields being set.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hpersist/\">HPERSIST</a></strong> - Removes the expiration time for each specified field</summary>\n\n**Syntax:** `HPERSIST key FIELDS numfields field [field ...]`\n\n**Description:** Removes the expiration time for each specified field\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hpexpire/\">HPEXPIRE</a></strong> - Set expiry for hash field using relative time to expire (milliseconds)</summary>\n\n**Syntax:** `HPEXPIRE key milliseconds [NX | XX | GT | LT] FIELDS numfields field\n  [field ...]`\n\n**Description:** Set expiry for hash field using relative time to expire (milliseconds)\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hpexpireat/\">HPEXPIREAT</a></strong> - Set expiry for hash field using an absolute Unix timestamp (milliseconds)</summary>\n\n**Syntax:** `HPEXPIREAT key unix-time-milliseconds [NX | XX | GT | LT]\n  FIELDS numfields field [field ...]`\n\n**Description:** Set expiry for hash field using an absolute Unix timestamp (milliseconds)\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hpexpiretime/\">HPEXPIRETIME</a></strong> - Returns the expiration time of a hash field as a Unix timestamp, in msec.</summary>\n\n**Syntax:** `HPEXPIRETIME key FIELDS numfields field [field ...]`\n\n**Description:** Returns the expiration time of a hash field as a Unix timestamp, in msec.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hpttl/\">HPTTL</a></strong> - Returns the TTL in milliseconds of a hash field.</summary>\n\n**Syntax:** `HPTTL key FIELDS numfields field [field ...]`\n\n**Description:** Returns the TTL in milliseconds of a hash field.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hrandfield/\">HRANDFIELD</a></strong> - Returns one or more random fields from a hash.</summary>\n\n**Syntax:** `HRANDFIELD key [count [WITHVALUES]]`\n\n**Description:** Returns one or more random fields from a hash.\n\n**Complexity:** O(N) where N is the number of fields returned\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hscan/\">HSCAN</a></strong> - Iterates over fields and values of a hash.</summary>\n\n**Syntax:** `HSCAN key cursor [MATCH pattern] [COUNT count] [NOVALUES]`\n\n**Description:** Iterates over fields and values of a hash.\n\n**Complexity:** O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hset/\">HSET</a></strong> - Creates or modifies the value of a field in a hash.</summary>\n\n**Syntax:** `HSET key field value [field value ...]`\n\n**Description:** Creates or modifies the value of a field in a hash.\n\n**Complexity:** O(1) for each field/value pair added, so O(N) to add N field/value pairs when the command is called with multiple field/value pairs.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hsetex/\">HSETEX</a></strong> - Set the value of one or more fields of a given hash key, and optionally set their expiration. <span style=\"color: #e74c3c;\">⭐ New in 8.0</span></summary>\n\n**Syntax:** `HSETEX key [FNX | FXX] [EX seconds | PX milliseconds |\n  EXAT unix-time-seconds | PXAT unix-time-milliseconds | KEEPTTL]\n  FIELDS numfields field value [field value ...]`\n\n**Description:** Set the value of one or more fields of a given hash key, and optionally set their expiration.\n\n**Complexity:** O(N) where N is the number of fields being set.\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hsetnx/\">HSETNX</a></strong> - Sets the value of a field in a hash only when the field doesn't exist.</summary>\n\n**Syntax:** `HSETNX key field value`\n\n**Description:** Sets the value of a field in a hash only when the field doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hstrlen/\">HSTRLEN</a></strong> - Returns the length of the value of a field.</summary>\n\n**Syntax:** `HSTRLEN key field`\n\n**Description:** Returns the length of the value of a field.\n\n**Complexity:** O(1)\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/httl/\">HTTL</a></strong> - Returns the TTL in seconds of a hash field.</summary>\n\n**Syntax:** `HTTL key FIELDS numfields field [field ...]`\n\n**Description:** Returns the TTL in seconds of a hash field.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hvals/\">HVALS</a></strong> - Returns all values in a hash.</summary>\n\n**Syntax:** `HVALS key`\n\n**Description:** Returns all values in a hash.\n\n**Complexity:** O(N) where N is the size of the hash.\n\n**Since:** 2.0.0\n\n</details>"},{"id":"list-commands","title":"List commands","role":"content","text":"List commands operate on lists of strings, ordered by insertion order.\n\n<details>\n<summary><strong><a href=\"/commands/blmove/\">BLMOVE</a></strong> - Pops an element from a list, pushes it to another list and returns it. Blocks until an element is available otherwise. Deletes the list if the last element was moved.</summary>\n\n**Syntax:** `BLMOVE source destination <LEFT | RIGHT> <LEFT | RIGHT> timeout`\n\n**Description:** Pops an element from a list, pushes it to another list and returns it. Blocks until an element is available otherwise. Deletes the list if the last element was moved.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/blmpop/\">BLMPOP</a></strong> - Pops the first element from one of multiple lists. Blocks until an element is available otherwise. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `BLMPOP timeout numkeys key [key ...] <LEFT | RIGHT> [COUNT count]`\n\n**Description:** Pops the first element from one of multiple lists. Blocks until an element is available otherwise. Deletes the list if the last element was popped.\n\n**Complexity:** O(N+M) where N is the number of provided keys and M is the number of elements returned.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/blpop/\">BLPOP</a></strong> - Removes and returns the first element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `BLPOP key [key ...] timeout`\n\n**Description:** Removes and returns the first element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.\n\n**Complexity:** O(N) where N is the number of provided keys.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/brpop/\">BRPOP</a></strong> - Removes and returns the last element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `BRPOP key [key ...] timeout`\n\n**Description:** Removes and returns the last element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.\n\n**Complexity:** O(N) where N is the number of provided keys.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/brpoplpush/\">BRPOPLPUSH</a></strong> - Pops an element from a list, pushes it to another list and returns it. Block until an element is available otherwise. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `BRPOPLPUSH source destination timeout`\n\n**Description:** Pops an element from a list, pushes it to another list and returns it. Block until an element is available otherwise. Deletes the list if the last element was popped.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lindex/\">LINDEX</a></strong> - Returns an element from a list by its index.</summary>\n\n**Syntax:** `LINDEX key index`\n\n**Description:** Returns an element from a list by its index.\n\n**Complexity:** O(N) where N is the number of elements to traverse to get to the element at index. This makes asking for the first or the last element of the list O(1).\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/linsert/\">LINSERT</a></strong> - Inserts an element before or after another element in a list.</summary>\n\n**Syntax:** `LINSERT key <BEFORE | AFTER> pivot element`\n\n**Description:** Inserts an element before or after another element in a list.\n\n**Complexity:** O(N) where N is the number of elements to traverse before seeing the value pivot. This means that inserting somewhere on the left end on the list (head) can be considered O(1) and inserting somewhere on the right end (tail) is O(N).\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/llen/\">LLEN</a></strong> - Returns the length of a list.</summary>\n\n**Syntax:** `LLEN key`\n\n**Description:** Returns the length of a list.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lmove/\">LMOVE</a></strong> - Returns an element after popping it from one list and pushing it to another. Deletes the list if the last element was moved.</summary>\n\n**Syntax:** `LMOVE source destination <LEFT | RIGHT> <LEFT | RIGHT>`\n\n**Description:** Returns an element after popping it from one list and pushing it to another. Deletes the list if the last element was moved.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lmpop/\">LMPOP</a></strong> - Returns multiple elements from a list after removing them. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `LMPOP numkeys key [key ...] <LEFT | RIGHT> [COUNT count]`\n\n**Description:** Returns multiple elements from a list after removing them. Deletes the list if the last element was popped.\n\n**Complexity:** O(N+M) where N is the number of provided keys and M is the number of elements returned.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lpop/\">LPOP</a></strong> - Returns the first elements in a list after removing it. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `LPOP key [count]`\n\n**Description:** Returns the first elements in a list after removing it. Deletes the list if the last element was popped.\n\n**Complexity:** O(N) where N is the number of elements returned\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lpos/\">LPOS</a></strong> - Returns the index of matching elements in a list.</summary>\n\n**Syntax:** `LPOS key element [RANK rank] [COUNT num-matches] [MAXLEN len]`\n\n**Description:** Returns the index of matching elements in a list.\n\n**Complexity:** O(N) where N is the number of elements in the list, for the average case. When searching for elements near the head or the tail of the list, or when the MAXLEN option is provided, the command may run in constant time.\n\n**Since:** 6.0.6\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lpush/\">LPUSH</a></strong> - Prepends one or more elements to a list. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `LPUSH key element [element ...]`\n\n**Description:** Prepends one or more elements to a list. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lpushx/\">LPUSHX</a></strong> - Prepends one or more elements to a list only when the list exists.</summary>\n\n**Syntax:** `LPUSHX key element [element ...]`\n\n**Description:** Prepends one or more elements to a list only when the list exists.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lrange/\">LRANGE</a></strong> - Returns a range of elements from a list.</summary>\n\n**Syntax:** `LRANGE key start stop`\n\n**Description:** Returns a range of elements from a list.\n\n**Complexity:** O(S+N) where S is the distance of start offset from HEAD for small lists, from nearest end (HEAD or TAIL) for large lists; and N is the number of elements in the specified range.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lrem/\">LREM</a></strong> - Removes elements from a list. Deletes the list if the last element was removed.</summary>\n\n**Syntax:** `LREM key count element`\n\n**Description:** Removes elements from a list. Deletes the list if the last element was removed.\n\n**Complexity:** O(N+M) where N is the length of the list and M is the number of elements removed.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lset/\">LSET</a></strong> - Sets the value of an element in a list by its index.</summary>\n\n**Syntax:** `LSET key index element`\n\n**Description:** Sets the value of an element in a list by its index.\n\n**Complexity:** O(N) where N is the length of the list. Setting either the first or the last element of the list is O(1).\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ltrim/\">LTRIM</a></strong> - Removes elements from both ends a list. Deletes the list if all elements were trimmed.</summary>\n\n**Syntax:** `LTRIM key start stop`\n\n**Description:** Removes elements from both ends a list. Deletes the list if all elements were trimmed.\n\n**Complexity:** O(N) where N is the number of elements to be removed by the operation.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rpop/\">RPOP</a></strong> - Returns and removes the last elements of a list. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `RPOP key [count]`\n\n**Description:** Returns and removes the last elements of a list. Deletes the list if the last element was popped.\n\n**Complexity:** O(N) where N is the number of elements returned\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rpoplpush/\">RPOPLPUSH</a></strong> - Returns the last element of a list after removing and pushing it to another list. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `RPOPLPUSH source destination`\n\n**Description:** Returns the last element of a list after removing and pushing it to another list. Deletes the list if the last element was popped.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rpush/\">RPUSH</a></strong> - Appends one or more elements to a list. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `RPUSH key element [element ...]`\n\n**Description:** Appends one or more elements to a list. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rpushx/\">RPUSHX</a></strong> - Appends an element to a list only when the list exists.</summary>\n\n**Syntax:** `RPUSHX key element [element ...]`\n\n**Description:** Appends an element to a list only when the list exists.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 2.2.0\n\n</details>"},{"id":"set-commands","title":"Set commands","role":"content","text":"Set commands operate on unordered collections of unique strings.\n\n<details>\n<summary><strong><a href=\"/commands/sadd/\">SADD</a></strong> - Adds one or more members to a set. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `SADD key member [member ...]`\n\n**Description:** Adds one or more members to a set. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/scard/\">SCARD</a></strong> - Returns the number of members in a set.</summary>\n\n**Syntax:** `SCARD key`\n\n**Description:** Returns the number of members in a set.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sdiff/\">SDIFF</a></strong> - Returns the difference of multiple sets.</summary>\n\n**Syntax:** `SDIFF key [key ...]`\n\n**Description:** Returns the difference of multiple sets.\n\n**Complexity:** O(N) where N is the total number of elements in all given sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sdiffstore/\">SDIFFSTORE</a></strong> - Stores the difference of multiple sets in a key.</summary>\n\n**Syntax:** `SDIFFSTORE destination key [key ...]`\n\n**Description:** Stores the difference of multiple sets in a key.\n\n**Complexity:** O(N) where N is the total number of elements in all given sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sinter/\">SINTER</a></strong> - Returns the intersect of multiple sets.</summary>\n\n**Syntax:** `SINTER key [key ...]`\n\n**Description:** Returns the intersect of multiple sets.\n\n**Complexity:** O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sintercard/\">SINTERCARD</a></strong> - Returns the number of members of the intersect of multiple sets.</summary>\n\n**Syntax:** `SINTERCARD numkeys key [key ...] [LIMIT limit]`\n\n**Description:** Returns the number of members of the intersect of multiple sets.\n\n**Complexity:** O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sinterstore/\">SINTERSTORE</a></strong> - Stores the intersect of multiple sets in a key.</summary>\n\n**Syntax:** `SINTERSTORE destination key [key ...]`\n\n**Description:** Stores the intersect of multiple sets in a key.\n\n**Complexity:** O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sismember/\">SISMEMBER</a></strong> - Determines whether a member belongs to a set.</summary>\n\n**Syntax:** `SISMEMBER key member`\n\n**Description:** Determines whether a member belongs to a set.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/smembers/\">SMEMBERS</a></strong> - Returns all members of a set.</summary>\n\n**Syntax:** `SMEMBERS key`\n\n**Description:** Returns all members of a set.\n\n**Complexity:** O(N) where N is the set cardinality.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/smismember/\">SMISMEMBER</a></strong> - Determines whether multiple members belong to a set.</summary>\n\n**Syntax:** `SMISMEMBER key member [member ...]`\n\n**Description:** Determines whether multiple members belong to a set.\n\n**Complexity:** O(N) where N is the number of elements being checked for membership\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/smove/\">SMOVE</a></strong> - Moves a member from one set to another.</summary>\n\n**Syntax:** `SMOVE source destination member`\n\n**Description:** Moves a member from one set to another.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/spop/\">SPOP</a></strong> - Returns one or more random members from a set after removing them. Deletes the set if the last member was popped.</summary>\n\n**Syntax:** `SPOP key [count]`\n\n**Description:** Returns one or more random members from a set after removing them. Deletes the set if the last member was popped.\n\n**Complexity:** Without the count argument O(1), otherwise O(N) where N is the value of the passed count.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/srandmember/\">SRANDMEMBER</a></strong> - Get one or multiple random members from a set</summary>\n\n**Syntax:** `SRANDMEMBER key [count]`\n\n**Description:** Get one or multiple random members from a set\n\n**Complexity:** Without the count argument O(1), otherwise O(N) where N is the absolute value of the passed count.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/srem/\">SREM</a></strong> - Removes one or more members from a set. Deletes the set if the last member was removed.</summary>\n\n**Syntax:** `SREM key member [member ...]`\n\n**Description:** Removes one or more members from a set. Deletes the set if the last member was removed.\n\n**Complexity:** O(N) where N is the number of members to be removed.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sscan/\">SSCAN</a></strong> - Iterates over members of a set.</summary>\n\n**Syntax:** `SSCAN key cursor [MATCH pattern] [COUNT count]`\n\n**Description:** Iterates over members of a set.\n\n**Complexity:** O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sunion/\">SUNION</a></strong> - Returns the union of multiple sets.</summary>\n\n**Syntax:** `SUNION key [key ...]`\n\n**Description:** Returns the union of multiple sets.\n\n**Complexity:** O(N) where N is the total number of elements in all given sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sunionstore/\">SUNIONSTORE</a></strong> - Stores the union of multiple sets in a key.</summary>\n\n**Syntax:** `SUNIONSTORE destination key [key ...]`\n\n**Description:** Stores the union of multiple sets in a key.\n\n**Complexity:** O(N) where N is the total number of elements in all given sets.\n\n**Since:** 1.0.0\n\n</details>"},{"id":"sorted-set-commands","title":"Sorted set commands","role":"content","text":"Sorted set commands operate on sets of unique strings ordered by a score.\n\n<details>\n<summary><strong><a href=\"/commands/bzmpop/\">BZMPOP</a></strong> - Removes and returns a member by score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.</summary>\n\n**Syntax:** `BZMPOP timeout numkeys key [key ...] <MIN | MAX> [COUNT count]`\n\n**Description:** Removes and returns a member by score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.\n\n**Complexity:** O(K) + O(M*log(N)) where K is the number of provided keys, N being the number of elements in the sorted set, and M being the number of elements popped.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bzpopmax/\">BZPOPMAX</a></strong> - Removes and returns the member with the highest score from one or more sorted sets. Blocks until a member available otherwise.  Deletes the sorted set if the last element was popped.</summary>\n\n**Syntax:** `BZPOPMAX key [key ...] timeout`\n\n**Description:** Removes and returns the member with the highest score from one or more sorted sets. Blocks until a member available otherwise.  Deletes the sorted set if the last element was popped.\n\n**Complexity:** O(log(N)) with N being the number of elements in the sorted set.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bzpopmin/\">BZPOPMIN</a></strong> - Removes and returns the member with the lowest score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.</summary>\n\n**Syntax:** `BZPOPMIN key [key ...] timeout`\n\n**Description:** Removes and returns the member with the lowest score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.\n\n**Complexity:** O(log(N)) with N being the number of elements in the sorted set.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zadd/\">ZADD</a></strong> - Adds one or more members to a sorted set, or updates their scores. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `ZADD key [NX | XX] [GT | LT] [CH] [INCR] score member [score member\n  ...]`\n\n**Description:** Adds one or more members to a sorted set, or updates their scores. Creates the key if it doesn't exist.\n\n**Complexity:** O(log(N)) for each item added, where N is the number of elements in the sorted set.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zcard/\">ZCARD</a></strong> - Returns the number of members in a sorted set.</summary>\n\n**Syntax:** `ZCARD key`\n\n**Description:** Returns the number of members in a sorted set.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zcount/\">ZCOUNT</a></strong> - Returns the count of members in a sorted set that have scores within a range.</summary>\n\n**Syntax:** `ZCOUNT key min max`\n\n**Description:** Returns the count of members in a sorted set that have scores within a range.\n\n**Complexity:** O(log(N)) with N being the number of elements in the sorted set.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zdiff/\">ZDIFF</a></strong> - Returns the difference between multiple sorted sets.</summary>\n\n**Syntax:** `ZDIFF numkeys key [key ...] [WITHSCORES]`\n\n**Description:** Returns the difference between multiple sorted sets.\n\n**Complexity:** O(L + (N-K)log(N)) worst case where L is the total number of elements in all the sets, N is the size of the first set, and K is the size of the result set.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zdiffstore/\">ZDIFFSTORE</a></strong> - Stores the difference of multiple sorted sets in a key.</summary>\n\n**Syntax:** `ZDIFFSTORE destination numkeys key [key ...]`\n\n**Description:** Stores the difference of multiple sorted sets in a key.\n\n**Complexity:** O(L + (N-K)log(N)) worst case where L is the total number of elements in all the sets, N is the size of the first set, and K is the size of the result set.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zincrby/\">ZINCRBY</a></strong> - Increments the score of a member in a sorted set.</summary>\n\n**Syntax:** `ZINCRBY key increment member`\n\n**Description:** Increments the score of a member in a sorted set.\n\n**Complexity:** O(log(N)) where N is the number of elements in the sorted set.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zinter/\">ZINTER</a></strong> - Returns the intersect of multiple sorted sets.</summary>\n\n**Syntax:** `ZINTER numkeys key [key ...] [WEIGHTS weight [weight ...]]\n  [AGGREGATE <SUM | MIN | MAX>] [WITHSCORES]`\n\n**Description:** Returns the intersect of multiple sorted sets.\n\n**Complexity:** O(N*K)+O(M*log(M)) worst case with N being the smallest input sorted set, K being the number of input sorted sets and M being the number of elements in the resulting sorted set.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zintercard/\">ZINTERCARD</a></strong> - Returns the number of members of the intersect of multiple sorted sets.</summary>\n\n**Syntax:** `ZINTERCARD numkeys key [key ...] [LIMIT limit]`\n\n**Description:** Returns the number of members of the intersect of multiple sorted sets.\n\n**Complexity:** O(N*K) worst case with N being the smallest input sorted set, K being the number of input sorted sets.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zinterstore/\">ZINTERSTORE</a></strong> - Stores the intersect of multiple sorted sets in a key.</summary>\n\n**Syntax:** `ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight\n  [weight ...]] [AGGREGATE <SUM | MIN | MAX>]`\n\n**Description:** Stores the intersect of multiple sorted sets in a key.\n\n**Complexity:** O(N*K)+O(M*log(M)) worst case with N being the smallest input sorted set, K being the number of input sorted sets and M being the number of elements in the resulting sorted set.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zlexcount/\">ZLEXCOUNT</a></strong> - Returns the number of members in a sorted set within a lexicographical range.</summary>\n\n**Syntax:** `ZLEXCOUNT key min max`\n\n**Description:** Returns the number of members in a sorted set within a lexicographical range.\n\n**Complexity:** O(log(N)) with N being the number of elements in the sorted set.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zmpop/\">ZMPOP</a></strong> - Returns the highest- or lowest-scoring members from one or more sorted sets after removing them. Deletes the sorted set if the last member was popped.</summary>\n\n**Syntax:** `ZMPOP numkeys key [key ...] <MIN | MAX> [COUNT count]`\n\n**Description:** Returns the highest- or lowest-scoring members from one or more sorted sets after removing them. Deletes the sorted set if the last member was popped.\n\n**Complexity:** O(K) + O(M*log(N)) where K is the number of provided keys, N being the number of elements in the sorted set, and M being the number of elements popped.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zmscore/\">ZMSCORE</a></strong> - Returns the score of one or more members in a sorted set.</summary>\n\n**Syntax:** `ZMSCORE key member [member ...]`\n\n**Description:** Returns the score of one or more members in a sorted set.\n\n**Complexity:** O(N) where N is the number of members being requested.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zpopmax/\">ZPOPMAX</a></strong> - Returns the highest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.</summary>\n\n**Syntax:** `ZPOPMAX key [count]`\n\n**Description:** Returns the highest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.\n\n**Complexity:** O(log(N)*M) with N being the number of elements in the sorted set, and M being the number of elements popped.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zpopmin/\">ZPOPMIN</a></strong> - Returns the lowest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.</summary>\n\n**Syntax:** `ZPOPMIN key [count]`\n\n**Description:** Returns the lowest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.\n\n**Complexity:** O(log(N)*M) with N being the number of elements in the sorted set, and M being the number of elements popped.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrandmember/\">ZRANDMEMBER</a></strong> - Returns one or more random members from a sorted set.</summary>\n\n**Syntax:** `ZRANDMEMBER key [count [WITHSCORES]]`\n\n**Description:** Returns one or more random members from a sorted set.\n\n**Complexity:** O(N) where N is the number of members returned\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrange/\">ZRANGE</a></strong> - Returns members in a sorted set within a range of indexes.</summary>\n\n**Syntax:** `ZRANGE key start stop [BYSCORE | BYLEX] [REV] [LIMIT offset count]\n  [WITHSCORES]`\n\n**Description:** Returns members in a sorted set within a range of indexes.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements returned.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrangebylex/\">ZRANGEBYLEX</a></strong> - Returns members in a sorted set within a lexicographical range.</summary>\n\n**Syntax:** `ZRANGEBYLEX key min max [LIMIT offset count]`\n\n**Description:** Returns members in a sorted set within a lexicographical range.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrangebyscore/\">ZRANGEBYSCORE</a></strong> - Returns members in a sorted set within a range of scores.</summary>\n\n**Syntax:** `ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]`\n\n**Description:** Returns members in a sorted set within a range of scores.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).\n\n**Since:** 1.0.5\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrangestore/\">ZRANGESTORE</a></strong> - Stores a range of members from sorted set in a key.</summary>\n\n**Syntax:** `ZRANGESTORE dst src min max [BYSCORE | BYLEX] [REV] [LIMIT offset\n  count]`\n\n**Description:** Stores a range of members from sorted set in a key.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements stored into the destination key.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrank/\">ZRANK</a></strong> - Returns the index of a member in a sorted set ordered by ascending scores.</summary>\n\n**Syntax:** `ZRANK key member [WITHSCORE]`\n\n**Description:** Returns the index of a member in a sorted set ordered by ascending scores.\n\n**Complexity:** O(log(N))\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrem/\">ZREM</a></strong> - Removes one or more members from a sorted set. Deletes the sorted set if all members were removed.</summary>\n\n**Syntax:** `ZREM key member [member ...]`\n\n**Description:** Removes one or more members from a sorted set. Deletes the sorted set if all members were removed.\n\n**Complexity:** O(M*log(N)) with N being the number of elements in the sorted set and M the number of elements to be removed.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zremrangebylex/\">ZREMRANGEBYLEX</a></strong> - Removes members in a sorted set within a lexicographical range. Deletes the sorted set if all members were removed.</summary>\n\n**Syntax:** `ZREMRANGEBYLEX key min max`\n\n**Description:** Removes members in a sorted set within a lexicographical range. Deletes the sorted set if all members were removed.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zremrangebyrank/\">ZREMRANGEBYRANK</a></strong> - Removes members in a sorted set within a range of indexes. Deletes the sorted set if all members were removed.</summary>\n\n**Syntax:** `ZREMRANGEBYRANK key start stop`\n\n**Description:** Removes members in a sorted set within a range of indexes. Deletes the sorted set if all members were removed.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zremrangebyscore/\">ZREMRANGEBYSCORE</a></strong> - Removes members in a sorted set within a range of scores. Deletes the sorted set if all members were removed.</summary>\n\n**Syntax:** `ZREMRANGEBYSCORE key min max`\n\n**Description:** Removes members in a sorted set within a range of scores. Deletes the sorted set if all members were removed.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrevrange/\">ZREVRANGE</a></strong> - Returns members in a sorted set within a range of indexes in reverse order.</summary>\n\n**Syntax:** `ZREVRANGE key start stop [WITHSCORES]`\n\n**Description:** Returns members in a sorted set within a range of indexes in reverse order.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements returned.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrevrangebylex/\">ZREVRANGEBYLEX</a></strong> - Returns members in a sorted set within a lexicographical range in reverse order.</summary>\n\n**Syntax:** `ZREVRANGEBYLEX key max min [LIMIT offset count]`\n\n**Description:** Returns members in a sorted set within a lexicographical range in reverse order.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrevrangebyscore/\">ZREVRANGEBYSCORE</a></strong> - Returns members in a sorted set within a range of scores in reverse order.</summary>\n\n**Syntax:** `ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]`\n\n**Description:** Returns members in a sorted set within a range of scores in reverse order.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrevrank/\">ZREVRANK</a></strong> - Returns the index of a member in a sorted set ordered by descending scores.</summary>\n\n**Syntax:** `ZREVRANK key member [WITHSCORE]`\n\n**Description:** Returns the index of a member in a sorted set ordered by descending scores.\n\n**Complexity:** O(log(N))\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zscan/\">ZSCAN</a></strong> - Iterates over members and scores of a sorted set.</summary>\n\n**Syntax:** `ZSCAN key cursor [MATCH pattern] [COUNT count]`\n\n**Description:** Iterates over members and scores of a sorted set.\n\n**Complexity:** O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zscore/\">ZSCORE</a></strong> - Returns the score of a member in a sorted set.</summary>\n\n**Syntax:** `ZSCORE key member`\n\n**Description:** Returns the score of a member in a sorted set.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zunion/\">ZUNION</a></strong> - Returns the union of multiple sorted sets.</summary>\n\n**Syntax:** `ZUNION numkeys key [key ...] [WEIGHTS weight [weight ...]]\n  [AGGREGATE <SUM | MIN | MAX>] [WITHSCORES]`\n\n**Description:** Returns the union of multiple sorted sets.\n\n**Complexity:** O(N)+O(M*log(M)) with N being the sum of the sizes of the input sorted sets, and M being the number of elements in the resulting sorted set.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zunionstore/\">ZUNIONSTORE</a></strong> - Stores the union of multiple sorted sets in a key.</summary>\n\n**Syntax:** `ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight\n  [weight ...]] [AGGREGATE <SUM | MIN | MAX>]`\n\n**Description:** Stores the union of multiple sorted sets in a key.\n\n**Complexity:** O(N)+O(M log(M)) with N being the sum of the sizes of the input sorted sets, and M being the number of elements in the resulting sorted set.\n\n**Since:** 2.0.0\n\n</details>"},{"id":"stream-commands","title":"Stream commands","role":"content","text":"Stream commands operate on append-only log data structures.\n\n<details>\n<summary><strong><a href=\"/commands/xack/\">XACK</a></strong> - Returns the number of messages that were successfully acknowledged by the consumer group member of a stream.</summary>\n\n**Syntax:** `XACK key group id [id ...]`\n\n**Description:** Returns the number of messages that were successfully acknowledged by the consumer group member of a stream.\n\n**Complexity:** O(1) for each message ID processed.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xadd/\">XADD</a></strong> - Appends a new message to a stream. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `XADD key [NOMKSTREAM] [KEEPREF | DELREF | ACKED] [<MAXLEN | MINID>\n  [= | ~] threshold [LIMIT count]] <* | id> field value [field value\n  ...]`\n\n**Description:** Appends a new message to a stream. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) when adding a new entry, O(N) when trimming where N being the number of entries evicted.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xautoclaim/\">XAUTOCLAIM</a></strong> - Changes, or acquires, ownership of messages in a consumer group, as if the messages were delivered to as consumer group member.</summary>\n\n**Syntax:** `XAUTOCLAIM key group consumer min-idle-time start [COUNT count]\n  [JUSTID]`\n\n**Description:** Changes, or acquires, ownership of messages in a consumer group, as if the messages were delivered to as consumer group member.\n\n**Complexity:** O(1) if COUNT is small.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xclaim/\">XCLAIM</a></strong> - Changes, or acquires, ownership of a message in a consumer group, as if the message was delivered a consumer group member.</summary>\n\n**Syntax:** `XCLAIM key group consumer min-idle-time id [id ...] [IDLE ms]\n  [TIME unix-time-milliseconds] [RETRYCOUNT count] [FORCE] [JUSTID]\n  [LASTID lastid]`\n\n**Description:** Changes, or acquires, ownership of a message in a consumer group, as if the message was delivered a consumer group member.\n\n**Complexity:** O(log N) with N being the number of messages in the PEL of the consumer group.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xdel/\">XDEL</a></strong> - Returns the number of messages after removing them from a stream.</summary>\n\n**Syntax:** `XDEL key id [id ...]`\n\n**Description:** Returns the number of messages after removing them from a stream.\n\n**Complexity:** O(1) for each single item to delete in the stream, regardless of the stream size.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-create/\">XGROUP CREATE</a></strong> - Creates a consumer group.</summary>\n\n**Syntax:** `XGROUP CREATE key group <id | $> [MKSTREAM]\n  [ENTRIESREAD entries-read]`\n\n**Description:** Creates a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-createconsumer/\">XGROUP CREATECONSUMER</a></strong> - Creates a consumer in a consumer group.</summary>\n\n**Syntax:** `XGROUP CREATECONSUMER key group consumer`\n\n**Description:** Creates a consumer in a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-delconsumer/\">XGROUP DELCONSUMER</a></strong> - Deletes a consumer from a consumer group.</summary>\n\n**Syntax:** `XGROUP DELCONSUMER key group consumer`\n\n**Description:** Deletes a consumer from a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-destroy/\">XGROUP DESTROY</a></strong> - Destroys a consumer group.</summary>\n\n**Syntax:** `XGROUP DESTROY key group`\n\n**Description:** Destroys a consumer group.\n\n**Complexity:** O(N) where N is the number of entries in the group's pending entries list (PEL).\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-setid/\">XGROUP SETID</a></strong> - Sets the last-delivered ID of a consumer group.</summary>\n\n**Syntax:** `XGROUP SETID key group <id | $> [ENTRIESREAD entries-read]`\n\n**Description:** Sets the last-delivered ID of a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xinfo-consumers/\">XINFO CONSUMERS</a></strong> - Returns a list of the consumers in a consumer group.</summary>\n\n**Syntax:** `XINFO CONSUMERS key group`\n\n**Description:** Returns a list of the consumers in a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xinfo-groups/\">XINFO GROUPS</a></strong> - Returns a list of the consumer groups of a stream.</summary>\n\n**Syntax:** `XINFO GROUPS key`\n\n**Description:** Returns a list of the consumer groups of a stream.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xinfo-stream/\">XINFO STREAM</a></strong> - Returns information about a stream.</summary>\n\n**Syntax:** `XINFO STREAM key [FULL [COUNT count]]`\n\n**Description:** Returns information about a stream.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xlen/\">XLEN</a></strong> - Return the number of messages in a stream.</summary>\n\n**Syntax:** `XLEN key`\n\n**Description:** Return the number of messages in a stream.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xpending/\">XPENDING</a></strong> - Returns the information and entries from a stream consumer group's pending entries list.</summary>\n\n**Syntax:** `XPENDING key group [[IDLE min-idle-time] start end count [consumer]]`\n\n**Description:** Returns the information and entries from a stream consumer group's pending entries list.\n\n**Complexity:** O(N) with N being the number of elements returned, so asking for a small fixed number of entries per call is O(1). O(M), where M is the total number of entries scanned when used with the IDLE filter. When the command returns just the summary and the list of consumers is small, it runs in O(1) time; otherwise, an additional O(N) time for iterating every consumer.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xrange/\">XRANGE</a></strong> - Returns the messages from a stream within a range of IDs.</summary>\n\n**Syntax:** `XRANGE key start end [COUNT count]`\n\n**Description:** Returns the messages from a stream within a range of IDs.\n\n**Complexity:** O(N) with N being the number of elements being returned. If N is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1).\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xread/\">XREAD</a></strong> - Returns messages from multiple streams with IDs greater than the ones requested. Blocks until a message is available otherwise.</summary>\n\n**Syntax:** `XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] id\n  [id ...]`\n\n**Description:** Returns messages from multiple streams with IDs greater than the ones requested. Blocks until a message is available otherwise.\n\n**Complexity:** \n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xreadgroup/\">XREADGROUP</a></strong> - Returns new or historical messages from a stream for a consumer in a group. Blocks until a message is available otherwise.</summary>\n\n**Syntax:** `XREADGROUP GROUP group consumer [COUNT count] [BLOCK milliseconds]\n  [CLAIM min-idle-time] [NOACK] STREAMS key [key ...] id [id ...]`\n\n**Description:** Returns new or historical messages from a stream for a consumer in a group. Blocks until a message is available otherwise.\n\n**Complexity:** For each stream mentioned: O(M) with M being the number of elements returned. If M is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1). On the other side when XREADGROUP blocks, XADD will pay the O(N) time in order to serve the N clients blocked on the stream getting new data.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xrevrange/\">XREVRANGE</a></strong> - Returns the messages from a stream within a range of IDs in reverse order.</summary>\n\n**Syntax:** `XREVRANGE key end start [COUNT count]`\n\n**Description:** Returns the messages from a stream within a range of IDs in reverse order.\n\n**Complexity:** O(N) with N being the number of elements returned. If N is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1).\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xsetid/\">XSETID</a></strong> - An internal command for replicating stream values.</summary>\n\n**Syntax:** `XSETID key last-id [ENTRIESADDED entries-added]\n  [MAXDELETEDID max-deleted-id]`\n\n**Description:** An internal command for replicating stream values.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xtrim/\">XTRIM</a></strong> - Deletes messages from the beginning of a stream.</summary>\n\n**Syntax:** `XTRIM key <MAXLEN | MINID> [= | ~] threshold [LIMIT count] [KEEPREF\n  | DELREF | ACKED]`\n\n**Description:** Deletes messages from the beginning of a stream.\n\n**Complexity:** O(N), with N being the number of evicted entries. Constant times are very small however, since entries are organized in macro nodes containing multiple entries that can be released with a single deallocation.\n\n**Since:** 5.0.0\n\n</details>"},{"id":"bitmap-commands","title":"Bitmap commands","role":"content","text":"Bitmap commands operate on strings as arrays of bits.\n\n<details>\n<summary><strong><a href=\"/commands/bitcount/\">BITCOUNT</a></strong> - Counts the number of set bits (population counting) in a string.</summary>\n\n**Syntax:** `BITCOUNT key [start end [BYTE | BIT]]`\n\n**Description:** Counts the number of set bits (population counting) in a string.\n\n**Complexity:** O(N)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bitfield/\">BITFIELD</a></strong> - Performs arbitrary bitfield integer operations on strings.</summary>\n\n**Syntax:** `BITFIELD key [GET encoding offset | [OVERFLOW <WRAP | SAT | FAIL>]\n  <SET encoding offset value | INCRBY encoding offset increment>\n  [GET encoding offset | [OVERFLOW <WRAP | SAT | FAIL>]\n  <SET encoding offset value | INCRBY encoding offset increment>\n  ...]]`\n\n**Description:** Performs arbitrary bitfield integer operations on strings.\n\n**Complexity:** O(1) for each subcommand specified\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bitfield_ro/\">BITFIELD_RO</a></strong> - Performs arbitrary read-only bitfield integer operations on strings.</summary>\n\n**Syntax:** `BITFIELD RO key [GET encoding offset [GET encoding offset ...]]`\n\n**Description:** Performs arbitrary read-only bitfield integer operations on strings.\n\n**Complexity:** O(1) for each subcommand specified\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bitop/\">BITOP</a></strong> - Performs bitwise operations on multiple strings, and stores the result.</summary>\n\n**Syntax:** `BITOP <AND | OR | XOR | NOT | DIFF | DIFF1 | ANDOR | ONE> destkey key [key ...]`\n\n**Description:** Performs bitwise operations on multiple strings, and stores the result.\n\n**Complexity:** O(N)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bitpos/\">BITPOS</a></strong> - Finds the first set (1) or clear (0) bit in a string.</summary>\n\n**Syntax:** `BITPOS key bit [start [end [BYTE | BIT]]]`\n\n**Description:** Finds the first set (1) or clear (0) bit in a string.\n\n**Complexity:** O(N)\n\n**Since:** 2.8.7\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getbit/\">GETBIT</a></strong> - Returns a bit value by offset.</summary>\n\n**Syntax:** `GETBIT key offset`\n\n**Description:** Returns a bit value by offset.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/setbit/\">SETBIT</a></strong> - Sets or clears the bit at offset of the string value. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `SETBIT key offset value`\n\n**Description:** Sets or clears the bit at offset of the string value. Creates the key if it doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>"},{"id":"hyperloglog-commands","title":"HyperLogLog commands","role":"content","text":"HyperLogLog commands provide probabilistic cardinality estimation.\n\n<details>\n<summary><strong><a href=\"/commands/pfadd/\">PFADD</a></strong> - Adds elements to a HyperLogLog key. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `PFADD key [element [element ...]]`\n\n**Description:** Adds elements to a HyperLogLog key. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) to add every element.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pfcount/\">PFCOUNT</a></strong> - Returns the approximated cardinality of the set(s) observed by the HyperLogLog key(s).</summary>\n\n**Syntax:** `PFCOUNT key [key ...]`\n\n**Description:** Returns the approximated cardinality of the set(s) observed by the HyperLogLog key(s).\n\n**Complexity:** O(1) with a very small average constant time when called with a single key. O(N) with N being the number of keys, and much bigger constant times, when called with multiple keys.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pfdebug/\">PFDEBUG</a></strong> - Internal commands for debugging HyperLogLog values.</summary>\n\n**Syntax:** `PFDEBUG subcommand key`\n\n**Description:** Internal commands for debugging HyperLogLog values.\n\n**Complexity:** N/A\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pfmerge/\">PFMERGE</a></strong> - Merges one or more HyperLogLog values into a single key.</summary>\n\n**Syntax:** `PFMERGE destkey [sourcekey [sourcekey ...]]`\n\n**Description:** Merges one or more HyperLogLog values into a single key.\n\n**Complexity:** O(N) to merge N HyperLogLogs, but with high constant times.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pfselftest/\">PFSELFTEST</a></strong> - An internal command for testing HyperLogLog values.</summary>\n\n**Syntax:** `PFSELFTEST`\n\n**Description:** An internal command for testing HyperLogLog values.\n\n**Complexity:** N/A\n\n**Since:** 2.8.9\n\n</details>"},{"id":"geospatial-commands","title":"Geospatial commands","role":"content","text":"Geospatial commands operate on geographic coordinates.\n\n<details>\n<summary><strong><a href=\"/commands/geoadd/\">GEOADD</a></strong> - Adds one or more members to a geospatial index. The key is created if it doesn't exist.</summary>\n\n**Syntax:** `GEOADD key [NX | XX] [CH] longitude latitude member [longitude\n  latitude member ...]`\n\n**Description:** Adds one or more members to a geospatial index. The key is created if it doesn't exist.\n\n**Complexity:** O(log(N)) for each item added, where N is the number of elements in the sorted set.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geodist/\">GEODIST</a></strong> - Returns the distance between two members of a geospatial index.</summary>\n\n**Syntax:** `GEODIST key member1 member2 [M | KM | FT | MI]`\n\n**Description:** Returns the distance between two members of a geospatial index.\n\n**Complexity:** O(1)\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geohash/\">GEOHASH</a></strong> - Returns members from a geospatial index as geohash strings.</summary>\n\n**Syntax:** `GEOHASH key [member [member ...]]`\n\n**Description:** Returns members from a geospatial index as geohash strings.\n\n**Complexity:** O(1) for each member requested.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geopos/\">GEOPOS</a></strong> - Returns the longitude and latitude of members from a geospatial index.</summary>\n\n**Syntax:** `GEOPOS key [member [member ...]]`\n\n**Description:** Returns the longitude and latitude of members from a geospatial index.\n\n**Complexity:** O(1) for each member requested.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/georadius/\">GEORADIUS</a></strong> - Queries a geospatial index for members within a distance from a coordinate, optionally stores the result.</summary>\n\n**Syntax:** `GEORADIUS key longitude latitude radius <M | KM | FT | MI>\n  [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC]\n  [STORE key | STOREDIST key]`\n\n**Description:** Queries a geospatial index for members within a distance from a coordinate, optionally stores the result.\n\n**Complexity:** O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/georadiusbymember/\">GEORADIUSBYMEMBER</a></strong> - Queries a geospatial index for members within a distance from a member, optionally stores the result.</summary>\n\n**Syntax:** `GEORADIUSBYMEMBER key member radius <M | KM | FT | MI> [WITHCOORD]\n  [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC] [STORE key\n  | STOREDIST key]`\n\n**Description:** Queries a geospatial index for members within a distance from a member, optionally stores the result.\n\n**Complexity:** O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/georadiusbymember_ro/\">GEORADIUSBYMEMBER_RO</a></strong> - Returns members from a geospatial index that are within a distance from a member.</summary>\n\n**Syntax:** `GEORADIUSBYMEMBER RO key member radius <M | KM | FT | MI>\n  [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC]`\n\n**Description:** Returns members from a geospatial index that are within a distance from a member.\n\n**Complexity:** O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.\n\n**Since:** 3.2.10\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/georadius_ro/\">GEORADIUS_RO</a></strong> - Returns members from a geospatial index that are within a distance from a coordinate.</summary>\n\n**Syntax:** `GEORADIUS RO key longitude latitude radius <M | KM | FT | MI>\n  [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC]`\n\n**Description:** Returns members from a geospatial index that are within a distance from a coordinate.\n\n**Complexity:** O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.\n\n**Since:** 3.2.10\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geosearch/\">GEOSEARCH</a></strong> - Queries a geospatial index for members inside an area of a box or a circle.</summary>\n\n**Syntax:** `GEOSEARCH key <FROMMEMBER member | FROMLONLAT longitude latitude>\n  <BYRADIUS radius <M | KM | FT | MI> | BYBOX width height <M | KM |\n  FT | MI>> [ASC | DESC] [COUNT count [ANY]] [WITHCOORD] [WITHDIST]\n  [WITHHASH]`\n\n**Description:** Queries a geospatial index for members inside an area of a box or a circle.\n\n**Complexity:** O(N+log(M)) where N is the number of elements in the grid-aligned bounding box area around the shape provided as the filter and M is the number of items inside the shape\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geosearchstore/\">GEOSEARCHSTORE</a></strong> - Queries a geospatial index for members inside an area of a box or a circle, optionally stores the result.</summary>\n\n**Syntax:** `GEOSEARCHSTORE destination source <FROMMEMBER member |\n  FROMLONLAT longitude latitude> <BYRADIUS radius <M | KM | FT | MI>\n  | BYBOX width height <M | KM | FT | MI>> [ASC | DESC] [COUNT count\n  [ANY]] [STOREDIST]`\n\n**Description:** Queries a geospatial index for members inside an area of a box or a circle, optionally stores the result.\n\n**Complexity:** O(N+log(M)) where N is the number of elements in the grid-aligned bounding box area around the shape provided as the filter and M is the number of items inside the shape\n\n**Since:** 6.2.0\n\n</details>"},{"id":"json-commands","title":"JSON commands","role":"content","text":"JSON commands operate on JSON data structures.\n\n<details>\n<summary><strong><a href=\"/commands/json.arrappend/\">JSON.ARRAPPEND</a></strong> - Append one or more JSON values into the array at path after the last element in it.</summary>\n\n**Syntax:** `JSON.ARRAPPEND key path value [value ...]`\n\n**Description:** Append one or more JSON values into the array at path after the last element in it.\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrindex/\">JSON.ARRINDEX</a></strong> - Returns the index of the first occurrence of a JSON scalar value in the array at path</summary>\n\n**Syntax:** `JSON.ARRINDEX key path value [start [stop]]`\n\n**Description:** Returns the index of the first occurrence of a JSON scalar value in the array at path\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the array, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrinsert/\">JSON.ARRINSERT</a></strong> - Inserts the JSON scalar(s) value at the specified index in the array at path</summary>\n\n**Syntax:** `JSON.ARRINSERT key path index value [value ...]`\n\n**Description:** Inserts the JSON scalar(s) value at the specified index in the array at path\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the array, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrlen/\">JSON.ARRLEN</a></strong> - Returns the length of the array at path</summary>\n\n**Syntax:** `JSON.ARRLEN key [path]`\n\n**Description:** Returns the length of the array at path\n\n**Complexity:** O(1) where path is evaluated to a single value, O(N) where path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrpop/\">JSON.ARRPOP</a></strong> - Removes and returns the element at the specified index in the array at path</summary>\n\n**Syntax:** `JSON.ARRPOP key [path [index]]`\n\n**Description:** Removes and returns the element at the specified index in the array at path\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the array and the specified index is not the last element, O(1) when path is evaluated to a single value and the specified index is the last element, or O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrtrim/\">JSON.ARRTRIM</a></strong> - Trims the array at path to contain only the specified inclusive range of indices from start to stop</summary>\n\n**Syntax:** `JSON.ARRTRIM key path start stop`\n\n**Description:** Trims the array at path to contain only the specified inclusive range of indices from start to stop\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the array, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.clear/\">JSON.CLEAR</a></strong> - Clears all values from an array or an object and sets numeric values to `0`</summary>\n\n**Syntax:** `JSON.CLEAR key [path]`\n\n**Description:** Clears all values from an array or an object and sets numeric values to `0`\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the values, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.debug/\">JSON.DEBUG</a></strong> - Debugging container command</summary>\n\n**Syntax:** `JSON.DEBUG`\n\n**Description:** Debugging container command\n\n**Complexity:** N/A\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.debug-memory/\">JSON.DEBUG MEMORY</a></strong> - Reports the size in bytes of a key</summary>\n\n**Syntax:** `JSON.DEBUG MEMORY key [path]`\n\n**Description:** Reports the size in bytes of a key\n\n**Complexity:** O(N) when path is evaluated to a single value, where N is the size of the value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.del/\">JSON.DEL</a></strong> - Deletes a value</summary>\n\n**Syntax:** `JSON.DEL key [path]`\n\n**Description:** Deletes a value\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the deleted value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.forget/\">JSON.FORGET</a></strong> - Deletes a value</summary>\n\n**Syntax:** `JSON.FORGET key [path]`\n\n**Description:** Deletes a value\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the deleted value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.get/\">JSON.GET</a></strong> - Gets the value at one or more paths in JSON serialized form</summary>\n\n**Syntax:** `JSON.GET key [INDENT indent] [NEWLINE newline] [SPACE space] [path\n  [path ...]]`\n\n**Description:** Gets the value at one or more paths in JSON serialized form\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.merge/\">JSON.MERGE</a></strong> - Merges a given JSON value into matching paths. Consequently, JSON values at matching paths are updated, deleted, or expanded with new children</summary>\n\n**Syntax:** `JSON.MERGE key path value`\n\n**Description:** Merges a given JSON value into matching paths. Consequently, JSON values at matching paths are updated, deleted, or expanded with new children\n\n**Complexity:** O(M+N) when path is evaluated to a single value where M is the size of the original value (if it exists) and N is the size of the new value, O(M+N) when path is evaluated to multiple values where M is the size of the key and N is the size of the new value * the number of original values in the key\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.mget/\">JSON.MGET</a></strong> - Returns the values at a path from one or more keys</summary>\n\n**Syntax:** `JSON.MGET key [key ...] path`\n\n**Description:** Returns the values at a path from one or more keys\n\n**Complexity:** O(M*N) when path is evaluated to a single value where M is the number of keys and N is the size of the value, O(N1+N2+...+Nm) when path is evaluated to multiple values where m is the number of keys and Ni is the size of the i-th key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.mset/\">JSON.MSET</a></strong> - Sets or updates the JSON value of one or more keys</summary>\n\n**Syntax:** `JSON.MSET key path value [key path value ...]`\n\n**Description:** Sets or updates the JSON value of one or more keys\n\n**Complexity:** O(K*(M+N)) where k is the number of keys in the command, when path is evaluated to a single value where M is the size of the original value (if it exists) and N is the size of the new value, or O(K*(M+N)) when path is evaluated to multiple values where M is the size of the key and N is the size of the new value * the number of original values in the key\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.numincrby/\">JSON.NUMINCRBY</a></strong> - Increments the numeric value at path by a value</summary>\n\n**Syntax:** `JSON.NUMINCRBY key path value`\n\n**Description:** Increments the numeric value at path by a value\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.nummultby/\">JSON.NUMMULTBY</a></strong> - Multiplies the numeric value at path by a value</summary>\n\n**Syntax:** `JSON.NUMMULTBY key path value`\n\n**Description:** Multiplies the numeric value at path by a value\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.objkeys/\">JSON.OBJKEYS</a></strong> - Returns the JSON keys of the object at path</summary>\n\n**Syntax:** `JSON.OBJKEYS key [path]`\n\n**Description:** Returns the JSON keys of the object at path\n\n**Complexity:** O(N) when path is evaluated to a single value, where N is the number of keys in the object, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.objlen/\">JSON.OBJLEN</a></strong> - Returns the number of keys of the object at path</summary>\n\n**Syntax:** `JSON.OBJLEN key [path]`\n\n**Description:** Returns the number of keys of the object at path\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.resp/\">JSON.RESP</a></strong> - Returns the JSON value at path in Redis Serialization Protocol (RESP)</summary>\n\n**Syntax:** `JSON.RESP key [path]`\n\n**Description:** Returns the JSON value at path in Redis Serialization Protocol (RESP)\n\n**Complexity:** O(N) when path is evaluated to a single value, where N is the size of the value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.set/\">JSON.SET</a></strong> - Sets or updates the JSON value at a path</summary>\n\n**Syntax:** `JSON.SET key path value [NX | XX]`\n\n**Description:** Sets or updates the JSON value at a path\n\n**Complexity:** O(M+N) when path is evaluated to a single value where M is the size of the original value (if it exists) and N is the size of the new value, O(M+N) when path is evaluated to multiple values where M is the size of the key and N is the size of the new value * the number of original values in the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.strappend/\">JSON.STRAPPEND</a></strong> - Appends a string to a JSON string value at path</summary>\n\n**Syntax:** `JSON.STRAPPEND key [path] value`\n\n**Description:** Appends a string to a JSON string value at path\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.strlen/\">JSON.STRLEN</a></strong> - Returns the length of the JSON String at path in key</summary>\n\n**Syntax:** `JSON.STRLEN key [path]`\n\n**Description:** Returns the length of the JSON String at path in key\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.toggle/\">JSON.TOGGLE</a></strong> - Toggles a boolean value</summary>\n\n**Syntax:** `JSON.TOGGLE key path`\n\n**Description:** Toggles a boolean value\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.type/\">JSON.TYPE</a></strong> - Returns the type of the JSON value at path</summary>\n\n**Syntax:** `JSON.TYPE key [path]`\n\n**Description:** Returns the type of the JSON value at path\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>"},{"id":"search-commands","title":"Search commands","role":"content","text":"Search commands provide full-text search and secondary indexing.\n\n<details>\n<summary><strong><a href=\"/commands/ft.aggregate/\">FT.AGGREGATE</a></strong> - Run a search query on an index and perform aggregate transformations on the results</summary>\n\n**Syntax:** `FT.AGGREGATE index query [VERBATIM] [LOAD count field [field ...]]\n  [TIMEOUT timeout] [LOAD *] [GROUPBY nargs property [property ...]\n  [REDUCE function nargs arg [arg ...] [AS name] [REDUCE function\n  nargs arg [arg ...] [AS name] ...]] [GROUPBY nargs property\n  [property ...] [REDUCE function nargs arg [arg ...] [AS name]\n  [REDUCE function nargs arg [arg ...] [AS name] ...]] ...]]\n  [SORTBY nargs [property <ASC | DESC> [property <ASC | DESC> ...]]\n  [MAX num]] [APPLY expression AS name [APPLY expression AS name\n  ...]] [LIMIT offset num] [FILTER filter] [WITHCURSOR\n  [COUNT read size] [MAXIDLE idle time]] [PARAMS nargs name value\n  [name value ...]]\n  [SCORER scorer]\n [ADDSCORES]\n  [DIALECT dialect]`\n\n**Description:** Run a search query on an index and perform aggregate transformations on the results\n\n**Complexity:** O(1)\n\n**Since:** 1.1.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.aliasadd/\">FT.ALIASADD</a></strong> - Adds an alias to the index</summary>\n\n**Syntax:** `FT.ALIASADD alias index`\n\n**Description:** Adds an alias to the index\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.aliasdel/\">FT.ALIASDEL</a></strong> - Deletes an alias from the index</summary>\n\n**Syntax:** `FT.ALIASDEL alias`\n\n**Description:** Deletes an alias from the index\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.aliasupdate/\">FT.ALIASUPDATE</a></strong> - Adds or updates an alias to the index</summary>\n\n**Syntax:** `FT.ALIASUPDATE alias index`\n\n**Description:** Adds or updates an alias to the index\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.alter/\">FT.ALTER</a></strong> - Adds a new field to the index</summary>\n\n**Syntax:** `FT.ALTER index [SKIPINITIALSCAN] SCHEMA ADD field options`\n\n**Description:** Adds a new field to the index\n\n**Complexity:** O(N) where N is the number of keys in the keyspace\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.config-get/\">FT.CONFIG GET</a></strong> - Retrieves runtime configuration options</summary>\n\n**Syntax:** `FT.CONFIG GET option`\n\n**Description:** Retrieves runtime configuration options\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.config-set/\">FT.CONFIG SET</a></strong> - Sets runtime configuration options</summary>\n\n**Syntax:** `FT.CONFIG SET option value`\n\n**Description:** Sets runtime configuration options\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.create/\">FT.CREATE</a></strong> - Creates an index with the given spec</summary>\n\n**Syntax:** `FT.CREATE index [ON <HASH | JSON>] [PREFIX count prefix [prefix\n  ...]] [FILTER filter] [LANGUAGE default lang]\n  [LANGUAGE FIELD lang attribute] [SCORE default score]\n  [SCORE FIELD score attribute] [PAYLOAD FIELD payload attribute]\n  [MAXTEXTFIELDS] [TEMPORARY seconds] [NOOFFSETS] [NOHL] [NOFIELDS]\n  [NOFREQS] [STOPWORDS count [stopword [stopword ...]]]\n  [SKIPINITIALSCAN] SCHEMA field name [AS alias] <TEXT | TAG |\n  NUMERIC | GEO | VECTOR> [WITHSUFFIXTRIE] [SORTABLE [UNF]]\n  [NOINDEX] [field name [AS alias] <TEXT | TAG | NUMERIC | GEO |\n  VECTOR> [WITHSUFFIXTRIE] [SORTABLE [UNF]] [NOINDEX] ...]`\n\n**Description:** Creates an index with the given spec\n\n**Complexity:** O(K) at creation where K is the number of fields, O(N) if scanning the keyspace is triggered, where N is the number of keys in the keyspace\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.cursor-del/\">FT.CURSOR DEL</a></strong> - Deletes a cursor</summary>\n\n**Syntax:** `FT.CURSOR DEL index cursor id`\n\n**Description:** Deletes a cursor\n\n**Complexity:** O(1)\n\n**Since:** 1.1.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.cursor-read/\">FT.CURSOR READ</a></strong> - Reads from a cursor</summary>\n\n**Syntax:** `FT.CURSOR READ index cursor id [COUNT read size]`\n\n**Description:** Reads from a cursor\n\n**Complexity:** O(1)\n\n**Since:** 1.1.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.dictadd/\">FT.DICTADD</a></strong> - Adds terms to a dictionary</summary>\n\n**Syntax:** `FT.DICTADD dict term [term ...]`\n\n**Description:** Adds terms to a dictionary\n\n**Complexity:** O(1)\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.dictdel/\">FT.DICTDEL</a></strong> - Deletes terms from a dictionary</summary>\n\n**Syntax:** `FT.DICTDEL dict term [term ...]`\n\n**Description:** Deletes terms from a dictionary\n\n**Complexity:** O(1)\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.dictdump/\">FT.DICTDUMP</a></strong> - Dumps all terms in the given dictionary</summary>\n\n**Syntax:** `FT.DICTDUMP dict`\n\n**Description:** Dumps all terms in the given dictionary\n\n**Complexity:** O(N), where N is the size of the dictionary\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.dropindex/\">FT.DROPINDEX</a></strong> - Deletes the index</summary>\n\n**Syntax:** `FT.DROPINDEX index [DD]`\n\n**Description:** Deletes the index\n\n**Complexity:** O(1) or O(N) if documents are deleted, where N is the number of keys in the keyspace\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.explain/\">FT.EXPLAIN</a></strong> - Returns the execution plan for a complex query</summary>\n\n**Syntax:** `FT.EXPLAIN index query [DIALECT dialect]`\n\n**Description:** Returns the execution plan for a complex query\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.explaincli/\">FT.EXPLAINCLI</a></strong> - Returns the execution plan for a complex query</summary>\n\n**Syntax:** `FT.EXPLAINCLI index query [DIALECT dialect]`\n\n**Description:** Returns the execution plan for a complex query\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.info/\">FT.INFO</a></strong> - Returns information and statistics on the index</summary>\n\n**Syntax:** `FT.INFO index`\n\n**Description:** Returns information and statistics on the index\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.profile/\">FT.PROFILE</a></strong> - Performs a `FT.SEARCH` or `FT.AGGREGATE` command and collects performance information</summary>\n\n**Syntax:** `FT.PROFILE index <SEARCH | AGGREGATE> [LIMITED] QUERY query`\n\n**Description:** Performs a `FT.SEARCH` or `FT.AGGREGATE` command and collects performance information\n\n**Complexity:** O(N)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.search/\">FT.SEARCH</a></strong> - Searches the index with a textual query, returning either documents or just ids</summary>\n\n**Syntax:** `FT.SEARCH index query [NOCONTENT] [VERBATIM] [NOSTOPWORDS]\n  [WITHSCORES] [WITHPAYLOADS] [WITHSORTKEYS] [FILTER numeric field\n  min max [FILTER numeric field min max ...]] [GEOFILTER geo field\n  lon lat radius <m | km | mi | ft> [GEOFILTER geo field lon lat\n  radius <m | km | mi | ft> ...]] [INKEYS count key [key ...]]\n  [INFIELDS count field [field ...]] [RETURN count identifier\n  [AS property] [identifier [AS property] ...]] [SUMMARIZE\n  [FIELDS count field [field ...]] [FRAGS num] [LEN fragsize]\n  [SEPARATOR separator]] [HIGHLIGHT [FIELDS count field [field ...]]\n  [TAGS open close]] [SLOP slop] [TIMEOUT timeout] [INORDER]\n  [LANGUAGE language] [EXPANDER expander] [SCORER scorer]\n  [EXPLAINSCORE] [PAYLOAD payload] [SORTBY sortby [ASC | DESC]]\n  [LIMIT offset num] [PARAMS nargs name value [name value ...]]\n  [DIALECT dialect]`\n\n**Description:** Searches the index with a textual query, returning either documents or just ids\n\n**Complexity:** O(N)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.spellcheck/\">FT.SPELLCHECK</a></strong> - Performs spelling correction on a query, returning suggestions for misspelled terms</summary>\n\n**Syntax:** `FT.SPELLCHECK index query [DISTANCE distance] [TERMS <INCLUDE |\n  EXCLUDE> dictionary [terms [terms ...]]] [DIALECT dialect]`\n\n**Description:** Performs spelling correction on a query, returning suggestions for misspelled terms\n\n**Complexity:** O(1)\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.syndump/\">FT.SYNDUMP</a></strong> - Dumps the contents of a synonym group</summary>\n\n**Syntax:** `FT.SYNDUMP index`\n\n**Description:** Dumps the contents of a synonym group\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.synupdate/\">FT.SYNUPDATE</a></strong> - Creates or updates a synonym group with additional terms</summary>\n\n**Syntax:** `FT.SYNUPDATE index synonym group id [SKIPINITIALSCAN] term [term\n  ...]`\n\n**Description:** Creates or updates a synonym group with additional terms\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.tagvals/\">FT.TAGVALS</a></strong> - Returns the distinct tags indexed in a Tag field</summary>\n\n**Syntax:** `FT.TAGVALS index field name`\n\n**Description:** Returns the distinct tags indexed in a Tag field\n\n**Complexity:** O(N)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft._list/\">FT._LIST</a></strong> - Returns a list of all existing indexes</summary>\n\n**Syntax:** `FT. LIST`\n\n**Description:** Returns a list of all existing indexes\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>"},{"id":"time-series-commands","title":"Time series commands","role":"content","text":"Time series commands operate on time-series data.\n\n<details>\n<summary><strong><a href=\"/commands/ts.add/\">TS.ADD</a></strong> - Append a sample to a time series</summary>\n\n**Syntax:** `TS.ADD key timestamp value [RETENTION retentionPeriod]\n  [ENCODING <COMPRESSED | UNCOMPRESSED>] [CHUNK SIZE size]\n  [DUPLICATE POLICY policy] \n  [ON DUPLICATE <BLOCK | FIRST | LAST | MIN | MAX | SUM>]\n  [IGNORE ignoreMaxTimediff ignoreMaxValDiff]\n  [LABELS [label value ...]]`\n\n**Description:** Append a sample to a time series\n\n**Complexity:** O(M) when M is the amount of compaction rules or O(1) with no compaction\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.alter/\">TS.ALTER</a></strong> - Update the retention, chunk size, duplicate policy, and labels of an existing time series</summary>\n\n**Syntax:** `TS.ALTER key [RETENTION retentionPeriod] [CHUNK SIZE size]\n  [DUPLICATE POLICY <BLOCK | FIRST | LAST | MIN | MAX | SUM>]\n  [IGNORE ignoreMaxTimediff ignoreMaxValDiff] \n  [LABELS [label value ...]]`\n\n**Description:** Update the retention, chunk size, duplicate policy, and labels of an existing time series\n\n**Complexity:** O(N) where N is the number of labels requested to update\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.create/\">TS.CREATE</a></strong> - Create a new time series</summary>\n\n**Syntax:** `TS.CREATE key [RETENTION retentionPeriod] [ENCODING <COMPRESSED |\n  UNCOMPRESSED>] [CHUNK SIZE size] [DUPLICATE POLICY <BLOCK | FIRST |\n  LAST | MIN | MAX | SUM>]\n  [IGNORE ignoreMaxTimediff ignoreMaxValDiff]\n  [LABELS [label value ...]]`\n\n**Description:** Create a new time series\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.createrule/\">TS.CREATERULE</a></strong> - Create a compaction rule</summary>\n\n**Syntax:** `TS.CREATERULE sourceKey destKey AGGREGATION <AVG | FIRST | LAST |\n  MIN | MAX | SUM | RANGE | COUNT | STD.P | STD.S | VAR.P | VAR.S |\n  TWA> bucketDuration [alignTimestamp]`\n\n**Description:** Create a compaction rule\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.decrby/\">TS.DECRBY</a></strong> - Decrease the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given decrement</summary>\n\n**Syntax:** `TS.DECRBY key value [TIMESTAMP timestamp]\n  [RETENTION retentionPeriod] [ENCODING <COMPRESSED|UNCOMPRESSED>] [CHUNK SIZE size]\n [DUPLICATE POLICY policy] [LABELS [label value ...]]`\n\n**Description:** Decrease the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given decrement\n\n**Complexity:** O(M) when M is the amount of compaction rules or O(1) with no compaction\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.del/\">TS.DEL</a></strong> - Delete all samples between two timestamps for a given time series</summary>\n\n**Syntax:** `TS.DEL key from timestamp to timestamp`\n\n**Description:** Delete all samples between two timestamps for a given time series\n\n**Complexity:** O(N) where N is the number of data points that will be removed\n\n**Since:** 1.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.deleterule/\">TS.DELETERULE</a></strong> - Delete a compaction rule</summary>\n\n**Syntax:** `TS.DELETERULE sourceKey destKey`\n\n**Description:** Delete a compaction rule\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.get/\">TS.GET</a></strong> - Get the sample with the highest timestamp from a given time series</summary>\n\n**Syntax:** `TS.GET key [LATEST]`\n\n**Description:** Get the sample with the highest timestamp from a given time series\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.incrby/\">TS.INCRBY</a></strong> - Increase the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given increment</summary>\n\n**Syntax:** `TS.INCRBY key value [TIMESTAMP timestamp]\n  [RETENTION retentionPeriod] [ENCODING <COMPRESSED|UNCOMPRESSED>] [CHUNK SIZE size]\n [DUPLICATE POLICY policy] [LABELS [label value ...]]`\n\n**Description:** Increase the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given increment\n\n**Complexity:** O(M) when M is the amount of compaction rules or O(1) with no compaction\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.info/\">TS.INFO</a></strong> - Returns information and statistics for a time series</summary>\n\n**Syntax:** `TS.INFO key [DEBUG]`\n\n**Description:** Returns information and statistics for a time series\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.madd/\">TS.MADD</a></strong> - Append new samples to one or more time series</summary>\n\n**Syntax:** `TS.MADD key timestamp value [key timestamp value ...]`\n\n**Description:** Append new samples to one or more time series\n\n**Complexity:** O(N*M) when N is the amount of series updated and M is the amount of compaction rules or O(N) with no compaction\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.mget/\">TS.MGET</a></strong> - Get the sample with the highest timestamp from each time series matching a specific filter</summary>\n\n**Syntax:** `TS.MGET [LATEST] [WITHLABELS | <SELECTED LABELS label1 [label1 ...]>]\n  FILTER <l=v | l!=v | l= | l!= | l=(v1,v2,...) | l!=(v1,v2,...)\n  [l=v | l!=v | l= | l!= | l=(v1,v2,...) | l!=(v1,v2,...) ...]>`\n\n**Description:** Get the sample with the highest timestamp from each time series matching a specific filter\n\n**Complexity:** O(n) where n is the number of time-series that match the filters\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.mrange/\">TS.MRANGE</a></strong> - Query a range across multiple time series by filters in forward direction</summary>\n\n**Syntax:** `TS.MRANGE fromTimestamp toTimestamp [LATEST] [FILTER BY TS Timestamp\n  [Timestamp ...]] [FILTER BY VALUE min max] [WITHLABELS |\n  <SELECTED LABELS label1 [label1 ...]>] [COUNT count] [[ALIGN value]\n  AGGREGATION <AVG | FIRST | LAST | MIN | MAX | SUM | RANGE | COUNT\n  | STD.P | STD.S | VAR.P | VAR.S | TWA> bucketDuration\n  [BUCKETTIMESTAMP] [EMPTY]] FILTER <l=v | l!=v | l= | l!= |\n  l=(v1,v2,...) | l!=(v1,v2,...) [l=v | l!=v | l= | l!= |\n  l=(v1,v2,...) | l!=(v1,v2,...) ...]> [GROUPBY label REDUCE\n  reducer]`\n\n**Description:** Query a range across multiple time series by filters in forward direction\n\n**Complexity:** O(n/m+k) where n = Number of data points, m = Chunk size (data points per chunk), k = Number of data points that are in the requested ranges\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.mrevrange/\">TS.MREVRANGE</a></strong> - Query a range across multiple time-series by filters in reverse direction</summary>\n\n**Syntax:** `TS.MREVRANGE fromTimestamp toTimestamp [LATEST]\n  [FILTER BY TS Timestamp [Timestamp ...]] [FILTER BY VALUE min max]\n  [WITHLABELS | <SELECTED LABELS label1 [label1 ...]>] [COUNT count]\n  [[ALIGN value] AGGREGATION <AVG | FIRST | LAST | MIN | MAX | SUM |\n  RANGE | COUNT | STD.P | STD.S | VAR.P | VAR.S | TWA>\n  bucketDuration [BUCKETTIMESTAMP] [EMPTY]] FILTER <l=v | l!=v | l=\n  | l!= | l=(v1,v2,...) | l!=(v1,v2,...) [l=v | l!=v | l= | l!= |\n  l=(v1,v2,...) | l!=(v1,v2,...) ...]> [GROUPBY label REDUCE\n  reducer]`\n\n**Description:** Query a range across multiple time-series by filters in reverse direction\n\n**Complexity:** O(n/m+k) where n = Number of data points, m = Chunk size (data points per chunk), k = Number of data points that are in the requested ranges\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.queryindex/\">TS.QUERYINDEX</a></strong> - Get all time series keys matching a filter list</summary>\n\n**Syntax:** `TS.QUERYINDEX <l=v | l!=v | l= | l!= | l=(v1,v2,...) |\n  l!=(v1,v2,...) [l=v | l!=v | l= | l!= | l=(v1,v2,...) |\n  l!=(v1,v2,...) ...]>`\n\n**Description:** Get all time series keys matching a filter list\n\n**Complexity:** O(n) where n is the number of time-series that match the filters\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.range/\">TS.RANGE</a></strong> - Query a range in forward direction</summary>\n\n**Syntax:** `TS.RANGE key fromTimestamp toTimestamp [LATEST]\n  [FILTER BY TS Timestamp [Timestamp ...]] [FILTER BY VALUE min max]\n  [COUNT count] [[ALIGN value] AGGREGATION <AVG | FIRST | LAST | MIN\n  | MAX | SUM | RANGE | COUNT | STD.P | STD.S | VAR.P | VAR.S | TWA>\n  bucketDuration [BUCKETTIMESTAMP] [EMPTY]]`\n\n**Description:** Query a range in forward direction\n\n**Complexity:** O(n/m+k) where n = Number of data points, m = Chunk size (data points per chunk), k = Number of data points that are in the requested range\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.revrange/\">TS.REVRANGE</a></strong> - Query a range in reverse direction</summary>\n\n**Syntax:** `TS.REVRANGE key fromTimestamp toTimestamp [LATEST]\n  [FILTER BY TS Timestamp [Timestamp ...]] [FILTER BY VALUE min max]\n  [COUNT count] [[ALIGN value] AGGREGATION <AVG | FIRST | LAST | MIN\n  | MAX | SUM | RANGE | COUNT | STD.P | STD.S | VAR.P | VAR.S | TWA>\n  bucketDuration [BUCKETTIMESTAMP] [EMPTY]]`\n\n**Description:** Query a range in reverse direction\n\n**Complexity:** O(n/m+k) where n = Number of data points, m = Chunk size (data points per chunk), k = Number of data points that are in the requested range\n\n**Since:** 1.4.0\n\n</details>"},{"id":"vector-set-commands","title":"Vector set commands","role":"content","text":"Vector set commands operate on vector data structures for similarity search and range queries.\n\n<details>\n<summary><strong><a href=\"/commands/vadd/\">VADD</a></strong> - Add a new element to a vector set, or update its vector if it already exists. <span style=\"color: #e74c3c;\">⭐ New in 8.0</span></summary>\n\n**Syntax:** `VADD key [REDUCE dim] (FP32 | VALUES num) vector element [CAS] [NOQUANT | Q8 | BIN]\n  [EF build-exploration-factor] [SETATTR attributes] [M numlinks]`\n\n**Description:** Add a new element to a vector set, or update its vector if it already exists.\n\n**Complexity:** O(log(N)) for each element added, where N is the number of elements in the vector set.\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vcard/\">VCARD</a></strong> - Return the number of elements in a vector set. <span style=\"color: #e74c3c;\">⭐ New in 8.0</span></summary>\n\n**Syntax:** `VCARD key`\n\n**Description:** Return the number of elements in a vector set.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vdim/\">VDIM</a></strong> - Return the dimension of vectors in the vector set. <span style=\"color: #e74c3c;\">⭐ New in 8.0</span></summary>\n\n**Syntax:** `VDIM key`\n\n**Description:** Return the dimension of vectors in the vector set.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vemb/\">VEMB</a></strong> - Return the vector associated with an element. <span style=\"color: #e74c3c;\">⭐ New in 8.0</span></summary>\n\n**Syntax:** `VEMB key element [RAW]`\n\n**Description:** Return the vector associated with an element.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vgetattr/\">VGETATTR</a></strong> - Retrieve the JSON attributes of elements. <span style=\"color: #e74c3c;\">⭐ New in 8.0</span></summary>\n\n**Syntax:** `VGETATTR key element`\n\n**Description:** Retrieve the JSON attributes of elements.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vinfo/\">VINFO</a></strong> - Return information about a vector set. <span style=\"color: #e74c3c;\">⭐ New in 8.0</span></summary>\n\n**Syntax:** `VINFO key`\n\n**Description:** Return information about a vector set.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vismember/\">VISMEMBER</a></strong> - Check if an element exists in a vector set. <span style=\"color: #e74c3c;\">⭐ New in 8.0</span></summary>\n\n**Syntax:** `VISMEMBER key element`\n\n**Description:** Check if an element exists in a vector set.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vlinks/\">VLINKS</a></strong> - Return the neighbors of an element at each layer in the HNSW graph. <span style=\"color: #e74c3c;\">⭐ New in 8.0</span></summary>\n\n**Syntax:** `VLINKS key element [WITHSCORES]`\n\n**Description:** Return the neighbors of an element at each layer in the HNSW graph.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vrandmember/\">VRANDMEMBER</a></strong> - Return one or multiple random members from a vector set. <span style=\"color: #e74c3c;\">⭐ New in 8.0</span></summary>\n\n**Syntax:** `VRANDMEMBER key [count]`\n\n**Description:** Return one or multiple random members from a vector set.\n\n**Complexity:** O(N) where N is the absolute value of the count argument.\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vrem/\">VREM</a></strong> - Remove an element from a vector set. <span style=\"color: #e74c3c;\">⭐ New in 8.0</span></summary>\n\n**Syntax:** `VREM key element`\n\n**Description:** Remove an element from a vector set.\n\n**Complexity:** O(log(N)) for each element removed, where N is the number of elements in the vector set.\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vsetattr/\">VSETATTR</a></strong> - Associate or remove the JSON attributes of elements. <span style=\"color: #e74c3c;\">⭐ New in 8.0</span></summary>\n\n**Syntax:** `VSETATTR key element \"{ JSON obj }\"`\n\n**Description:** Associate or remove the JSON attributes of elements.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vsim/\">VSIM</a></strong> - Return elements by vector similarity. <span style=\"color: #e74c3c;\">⭐ New in 8.0</span></summary>\n\n**Syntax:** `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]`\n\n**Description:** Return elements by vector similarity.\n\n**Complexity:** O(log(N)) where N is the number of elements in the vector set.\n\n**Since:** 8.0.0\n\n</details>"},{"id":"pubsub-commands","title":"Pub/Sub commands","role":"content","text":"Pub/Sub commands enable message passing between clients.\n\n<details>\n<summary><strong><a href=\"/commands/psubscribe/\">PSUBSCRIBE</a></strong> - Listens for messages published to channels that match one or more patterns.</summary>\n\n**Syntax:** `PSUBSCRIBE pattern [pattern ...]`\n\n**Description:** Listens for messages published to channels that match one or more patterns.\n\n**Complexity:** O(N) where N is the number of patterns to subscribe to.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/publish/\">PUBLISH</a></strong> - Posts a message to a channel.</summary>\n\n**Syntax:** `PUBLISH channel message`\n\n**Description:** Posts a message to a channel.\n\n**Complexity:** O(N+M) where N is the number of clients subscribed to the receiving channel and M is the total number of subscribed patterns (by any client).\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-channels/\">PUBSUB CHANNELS</a></strong> - Returns the active channels.</summary>\n\n**Syntax:** `PUBSUB CHANNELS [pattern]`\n\n**Description:** Returns the active channels.\n\n**Complexity:** O(N) where N is the number of active channels, and assuming constant time pattern matching (relatively short channels and patterns)\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-numpat/\">PUBSUB NUMPAT</a></strong> - Returns a count of unique pattern subscriptions.</summary>\n\n**Syntax:** `PUBSUB NUMPAT`\n\n**Description:** Returns a count of unique pattern subscriptions.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-numsub/\">PUBSUB NUMSUB</a></strong> - Returns a count of subscribers to channels.</summary>\n\n**Syntax:** `PUBSUB NUMSUB [channel [channel ...]]`\n\n**Description:** Returns a count of subscribers to channels.\n\n**Complexity:** O(N) for the NUMSUB subcommand, where N is the number of requested channels\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-shardchannels/\">PUBSUB SHARDCHANNELS</a></strong> - Returns the active shard channels.</summary>\n\n**Syntax:** `PUBSUB SHARDCHANNELS [pattern]`\n\n**Description:** Returns the active shard channels.\n\n**Complexity:** O(N) where N is the number of active shard channels, and assuming constant time pattern matching (relatively short shard channels).\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-shardnumsub/\">PUBSUB SHARDNUMSUB</a></strong> - Returns the count of subscribers of shard channels.</summary>\n\n**Syntax:** `PUBSUB SHARDNUMSUB [shardchannel [shardchannel ...]]`\n\n**Description:** Returns the count of subscribers of shard channels.\n\n**Complexity:** O(N) for the SHARDNUMSUB subcommand, where N is the number of requested shard channels\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/punsubscribe/\">PUNSUBSCRIBE</a></strong> - Stops listening to messages published to channels that match one or more patterns.</summary>\n\n**Syntax:** `PUNSUBSCRIBE [pattern [pattern ...]]`\n\n**Description:** Stops listening to messages published to channels that match one or more patterns.\n\n**Complexity:** O(N) where N is the number of patterns to unsubscribe.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/spublish/\">SPUBLISH</a></strong> - Post a message to a shard channel</summary>\n\n**Syntax:** `SPUBLISH shardchannel message`\n\n**Description:** Post a message to a shard channel\n\n**Complexity:** O(N) where N is the number of clients subscribed to the receiving shard channel.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ssubscribe/\">SSUBSCRIBE</a></strong> - Listens for messages published to shard channels.</summary>\n\n**Syntax:** `SSUBSCRIBE shardchannel [shardchannel ...]`\n\n**Description:** Listens for messages published to shard channels.\n\n**Complexity:** O(N) where N is the number of shard channels to subscribe to.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/subscribe/\">SUBSCRIBE</a></strong> - Listens for messages published to channels.</summary>\n\n**Syntax:** `SUBSCRIBE channel [channel ...]`\n\n**Description:** Listens for messages published to channels.\n\n**Complexity:** O(N) where N is the number of channels to subscribe to.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sunsubscribe/\">SUNSUBSCRIBE</a></strong> - Stops listening to messages posted to shard channels.</summary>\n\n**Syntax:** `SUNSUBSCRIBE [shardchannel [shardchannel ...]]`\n\n**Description:** Stops listening to messages posted to shard channels.\n\n**Complexity:** O(N) where N is the number of shard channels to unsubscribe.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/unsubscribe/\">UNSUBSCRIBE</a></strong> - Stops listening to messages posted to channels.</summary>\n\n**Syntax:** `UNSUBSCRIBE [channel [channel ...]]`\n\n**Description:** Stops listening to messages posted to channels.\n\n**Complexity:** O(N) where N is the number of channels to unsubscribe.\n\n**Since:** 2.0.0\n\n</details>"},{"id":"transaction-commands","title":"Transaction commands","role":"content","text":"Transaction commands enable atomic execution of command groups.\n\n<details>\n<summary><strong><a href=\"/commands/discard/\">DISCARD</a></strong> - Discards a transaction.</summary>\n\n**Syntax:** `DISCARD`\n\n**Description:** Discards a transaction.\n\n**Complexity:** O(N), when N is the number of queued commands\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/exec/\">EXEC</a></strong> - Executes all commands in a transaction.</summary>\n\n**Syntax:** `EXEC`\n\n**Description:** Executes all commands in a transaction.\n\n**Complexity:** Depends on commands in the transaction\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/multi/\">MULTI</a></strong> - Starts a transaction.</summary>\n\n**Syntax:** `MULTI`\n\n**Description:** Starts a transaction.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/unwatch/\">UNWATCH</a></strong> - Forgets about watched keys of a transaction.</summary>\n\n**Syntax:** `UNWATCH`\n\n**Description:** Forgets about watched keys of a transaction.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/watch/\">WATCH</a></strong> - Monitors changes to keys to determine the execution of a transaction.</summary>\n\n**Syntax:** `WATCH key [key ...]`\n\n**Description:** Monitors changes to keys to determine the execution of a transaction.\n\n**Complexity:** O(1) for every key.\n\n**Since:** 2.2.0\n\n</details>"},{"id":"scripting-commands","title":"Scripting commands","role":"content","text":"Scripting commands enable server-side Lua script execution.\n\n<details>\n<summary><strong><a href=\"/commands/eval/\">EVAL</a></strong> - Executes a server-side Lua script.</summary>\n\n**Syntax:** `EVAL script numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Executes a server-side Lua script.\n\n**Complexity:** Depends on the script that is executed.\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/evalsha/\">EVALSHA</a></strong> - Executes a server-side Lua script by SHA1 digest.</summary>\n\n**Syntax:** `EVALSHA sha1 numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Executes a server-side Lua script by SHA1 digest.\n\n**Complexity:** Depends on the script that is executed.\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/evalsha_ro/\">EVALSHA_RO</a></strong> - Executes a read-only server-side Lua script by SHA1 digest.</summary>\n\n**Syntax:** `EVALSHA RO sha1 numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Executes a read-only server-side Lua script by SHA1 digest.\n\n**Complexity:** Depends on the script that is executed.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/eval_ro/\">EVAL_RO</a></strong> - Executes a read-only server-side Lua script.</summary>\n\n**Syntax:** `EVAL RO script numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Executes a read-only server-side Lua script.\n\n**Complexity:** Depends on the script that is executed.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/fcall/\">FCALL</a></strong> - Invokes a function.</summary>\n\n**Syntax:** `FCALL function numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Invokes a function.\n\n**Complexity:** Depends on the function that is executed.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/fcall_ro/\">FCALL_RO</a></strong> - Invokes a read-only function.</summary>\n\n**Syntax:** `FCALL RO function numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Invokes a read-only function.\n\n**Complexity:** Depends on the function that is executed.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-delete/\">FUNCTION DELETE</a></strong> - Deletes a library and its functions.</summary>\n\n**Syntax:** `FUNCTION DELETE library-name`\n\n**Description:** Deletes a library and its functions.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-dump/\">FUNCTION DUMP</a></strong> - Dumps all libraries into a serialized binary payload.</summary>\n\n**Syntax:** `FUNCTION DUMP`\n\n**Description:** Dumps all libraries into a serialized binary payload.\n\n**Complexity:** O(N) where N is the number of functions\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-flush/\">FUNCTION FLUSH</a></strong> - Deletes all libraries and functions.</summary>\n\n**Syntax:** `FUNCTION FLUSH [ASYNC | SYNC]`\n\n**Description:** Deletes all libraries and functions.\n\n**Complexity:** O(N) where N is the number of functions deleted\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-kill/\">FUNCTION KILL</a></strong> - Terminates a function during execution.</summary>\n\n**Syntax:** `FUNCTION KILL`\n\n**Description:** Terminates a function during execution.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-list/\">FUNCTION LIST</a></strong> - Returns information about all libraries.</summary>\n\n**Syntax:** `FUNCTION LIST [LIBRARYNAME library-name-pattern] [WITHCODE]`\n\n**Description:** Returns information about all libraries.\n\n**Complexity:** O(N) where N is the number of functions\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-load/\">FUNCTION LOAD</a></strong> - Creates a library.</summary>\n\n**Syntax:** `FUNCTION LOAD [REPLACE] function-code`\n\n**Description:** Creates a library.\n\n**Complexity:** O(1) (considering compilation time is redundant)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-restore/\">FUNCTION RESTORE</a></strong> - Restores all libraries from a payload.</summary>\n\n**Syntax:** `FUNCTION RESTORE serialized-value [FLUSH | APPEND | REPLACE]`\n\n**Description:** Restores all libraries from a payload.\n\n**Complexity:** O(N) where N is the number of functions on the payload\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-stats/\">FUNCTION STATS</a></strong> - Returns information about a function during execution.</summary>\n\n**Syntax:** `FUNCTION STATS`\n\n**Description:** Returns information about a function during execution.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-debug/\">SCRIPT DEBUG</a></strong> - Sets the debug mode of server-side Lua scripts.</summary>\n\n**Syntax:** `SCRIPT DEBUG <YES | SYNC | NO>`\n\n**Description:** Sets the debug mode of server-side Lua scripts.\n\n**Complexity:** O(1)\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-exists/\">SCRIPT EXISTS</a></strong> - Determines whether server-side Lua scripts exist in the script cache.</summary>\n\n**Syntax:** `SCRIPT EXISTS sha1 [sha1 ...]`\n\n**Description:** Determines whether server-side Lua scripts exist in the script cache.\n\n**Complexity:** O(N) with N being the number of scripts to check (so checking a single script is an O(1) operation).\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-flush/\">SCRIPT FLUSH</a></strong> - Removes all server-side Lua scripts from the script cache.</summary>\n\n**Syntax:** `SCRIPT FLUSH [ASYNC | SYNC]`\n\n**Description:** Removes all server-side Lua scripts from the script cache.\n\n**Complexity:** O(N) with N being the number of scripts in cache\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-kill/\">SCRIPT KILL</a></strong> - Terminates a server-side Lua script during execution.</summary>\n\n**Syntax:** `SCRIPT KILL`\n\n**Description:** Terminates a server-side Lua script during execution.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-load/\">SCRIPT LOAD</a></strong> - Loads a server-side Lua script to the script cache.</summary>\n\n**Syntax:** `SCRIPT LOAD script`\n\n**Description:** Loads a server-side Lua script to the script cache.\n\n**Complexity:** O(N) with N being the length in bytes of the script body.\n\n**Since:** 2.6.0\n\n</details>"},{"id":"connection-commands","title":"Connection commands","role":"content","text":"Connection commands manage client connections.\n\n<details>\n<summary><strong><a href=\"/commands/auth/\">AUTH</a></strong> - Authenticates the connection.</summary>\n\n**Syntax:** `AUTH [username] password`\n\n**Description:** Authenticates the connection.\n\n**Complexity:** O(N) where N is the number of passwords defined for the user\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-caching/\">CLIENT CACHING</a></strong> - Instructs the server whether to track the keys in the next request.</summary>\n\n**Syntax:** `CLIENT CACHING <YES | NO>`\n\n**Description:** Instructs the server whether to track the keys in the next request.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-getname/\">CLIENT GETNAME</a></strong> - Returns the name of the connection.</summary>\n\n**Syntax:** `CLIENT GETNAME`\n\n**Description:** Returns the name of the connection.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-getredir/\">CLIENT GETREDIR</a></strong> - Returns the client ID to which the connection's tracking notifications are redirected.</summary>\n\n**Syntax:** `CLIENT GETREDIR`\n\n**Description:** Returns the client ID to which the connection's tracking notifications are redirected.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-id/\">CLIENT ID</a></strong> - Returns the unique client ID of the connection.</summary>\n\n**Syntax:** `CLIENT ID`\n\n**Description:** Returns the unique client ID of the connection.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-info/\">CLIENT INFO</a></strong> - Returns information about the connection.</summary>\n\n**Syntax:** `CLIENT INFO`\n\n**Description:** Returns information about the connection.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-kill/\">CLIENT KILL</a></strong> - Terminates open connections.</summary>\n\n**Syntax:** `CLIENT KILL <ip:port | <[ID client-id] | [TYPE <NORMAL | MASTER |\n  SLAVE | REPLICA | PUBSUB>] | [USER username] | [ADDR ip:port] |\n  [LADDR ip:port] | [SKIPME <YES | NO>] | [MAXAGE maxage]\n  [[ID client-id] | [TYPE <NORMAL | MASTER | SLAVE | REPLICA |\n  PUBSUB>] | [USER username] | [ADDR ip:port] | [LADDR ip:port] |\n  [SKIPME <YES | NO>] | [MAXAGE maxage] ...]>>`\n\n**Description:** Terminates open connections.\n\n**Complexity:** O(N) where N is the number of client connections\n\n**Since:** 2.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-list/\">CLIENT LIST</a></strong> - Lists open connections.</summary>\n\n**Syntax:** `CLIENT LIST [TYPE <NORMAL | MASTER | REPLICA | PUBSUB>]\n  [ID client-id [client-id ...]]`\n\n**Description:** Lists open connections.\n\n**Complexity:** O(N) where N is the number of client connections\n\n**Since:** 2.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-no-evict/\">CLIENT NO-EVICT</a></strong> - Sets the client eviction mode of the connection.</summary>\n\n**Syntax:** `CLIENT NO-EVICT <ON | OFF>`\n\n**Description:** Sets the client eviction mode of the connection.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-no-touch/\">CLIENT NO-TOUCH</a></strong> - Controls whether commands sent by the client affect the LRU/LFU of accessed keys.</summary>\n\n**Syntax:** `CLIENT NO-TOUCH <ON | OFF>`\n\n**Description:** Controls whether commands sent by the client affect the LRU/LFU of accessed keys.\n\n**Complexity:** O(1)\n\n**Since:** 7.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-pause/\">CLIENT PAUSE</a></strong> - Suspends commands processing.</summary>\n\n**Syntax:** `CLIENT PAUSE timeout [WRITE | ALL]`\n\n**Description:** Suspends commands processing.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-reply/\">CLIENT REPLY</a></strong> - Instructs the server whether to reply to commands.</summary>\n\n**Syntax:** `CLIENT REPLY <ON | OFF | SKIP>`\n\n**Description:** Instructs the server whether to reply to commands.\n\n**Complexity:** O(1)\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-setinfo/\">CLIENT SETINFO</a></strong> - Sets information specific to the client or connection.</summary>\n\n**Syntax:** `CLIENT SETINFO <LIB-NAME libname | LIB-VER libver>`\n\n**Description:** Sets information specific to the client or connection.\n\n**Complexity:** O(1)\n\n**Since:** 7.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-setname/\">CLIENT SETNAME</a></strong> - Sets the connection name.</summary>\n\n**Syntax:** `CLIENT SETNAME connection-name`\n\n**Description:** Sets the connection name.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-tracking/\">CLIENT TRACKING</a></strong> - Controls server-assisted client-side caching for the connection.</summary>\n\n**Syntax:** `CLIENT TRACKING <ON | OFF> [REDIRECT client-id] [PREFIX prefix\n  [PREFIX prefix ...]] [BCAST] [OPTIN] [OPTOUT] [NOLOOP]`\n\n**Description:** Controls server-assisted client-side caching for the connection.\n\n**Complexity:** O(1). Some options may introduce additional complexity.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-trackinginfo/\">CLIENT TRACKINGINFO</a></strong> - Returns information about server-assisted client-side caching for the connection.</summary>\n\n**Syntax:** `CLIENT TRACKINGINFO`\n\n**Description:** Returns information about server-assisted client-side caching for the connection.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-unblock/\">CLIENT UNBLOCK</a></strong> - Unblocks a client blocked by a blocking command from a different connection.</summary>\n\n**Syntax:** `CLIENT UNBLOCK client-id [TIMEOUT | ERROR]`\n\n**Description:** Unblocks a client blocked by a blocking command from a different connection.\n\n**Complexity:** O(log N) where N is the number of client connections\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-unpause/\">CLIENT UNPAUSE</a></strong> - Resumes processing commands from paused clients.</summary>\n\n**Syntax:** `CLIENT UNPAUSE`\n\n**Description:** Resumes processing commands from paused clients.\n\n**Complexity:** O(N) Where N is the number of paused clients\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/echo/\">ECHO</a></strong> - Returns the given string.</summary>\n\n**Syntax:** `ECHO message`\n\n**Description:** Returns the given string.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hello/\">HELLO</a></strong> - Handshakes with the Redis server.</summary>\n\n**Syntax:** `HELLO [protover [AUTH username password] [SETNAME clientname]]`\n\n**Description:** Handshakes with the Redis server.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ping/\">PING</a></strong> - Returns the server's liveliness response.</summary>\n\n**Syntax:** `PING [message]`\n\n**Description:** Returns the server's liveliness response.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/quit/\">QUIT</a></strong> - Closes the connection.</summary>\n\n**Syntax:** `QUIT`\n\n**Description:** Closes the connection.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/reset/\">RESET</a></strong> - Resets the connection.</summary>\n\n**Syntax:** `RESET`\n\n**Description:** Resets the connection.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/select/\">SELECT</a></strong> - Changes the selected database.</summary>\n\n**Syntax:** `SELECT index`\n\n**Description:** Changes the selected database.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>"},{"id":"server-commands","title":"Server commands","role":"content","text":"Server commands provide server management and introspection.\n\n<details>\n<summary><strong><a href=\"/commands/acl-cat/\">ACL CAT</a></strong> - Lists the ACL categories, or the commands inside a category.</summary>\n\n**Syntax:** `ACL CAT [category]`\n\n**Description:** Lists the ACL categories, or the commands inside a category.\n\n**Complexity:** O(1) since the categories and commands are a fixed set.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-deluser/\">ACL DELUSER</a></strong> - Deletes ACL users, and terminates their connections.</summary>\n\n**Syntax:** `ACL DELUSER username [username ...]`\n\n**Description:** Deletes ACL users, and terminates their connections.\n\n**Complexity:** O(1) amortized time considering the typical user.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-dryrun/\">ACL DRYRUN</a></strong> - Simulates the execution of a command by a user, without executing the command.</summary>\n\n**Syntax:** `ACL DRYRUN username command [arg [arg ...]]`\n\n**Description:** Simulates the execution of a command by a user, without executing the command.\n\n**Complexity:** O(1).\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-genpass/\">ACL GENPASS</a></strong> - Generates a pseudorandom, secure password that can be used to identify ACL users.</summary>\n\n**Syntax:** `ACL GENPASS [bits]`\n\n**Description:** Generates a pseudorandom, secure password that can be used to identify ACL users.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-getuser/\">ACL GETUSER</a></strong> - Lists the ACL rules of a user.</summary>\n\n**Syntax:** `ACL GETUSER username`\n\n**Description:** Lists the ACL rules of a user.\n\n**Complexity:** O(N). Where N is the number of password, command and pattern rules that the user has.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-list/\">ACL LIST</a></strong> - Dumps the effective rules in ACL file format.</summary>\n\n**Syntax:** `ACL LIST`\n\n**Description:** Dumps the effective rules in ACL file format.\n\n**Complexity:** O(N). Where N is the number of configured users.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-load/\">ACL LOAD</a></strong> - Reloads the rules from the configured ACL file.</summary>\n\n**Syntax:** `ACL LOAD`\n\n**Description:** Reloads the rules from the configured ACL file.\n\n**Complexity:** O(N). Where N is the number of configured users.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-log/\">ACL LOG</a></strong> - Lists recent security events generated due to ACL rules.</summary>\n\n**Syntax:** `ACL LOG [count | RESET]`\n\n**Description:** Lists recent security events generated due to ACL rules.\n\n**Complexity:** O(N) with N being the number of entries shown.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-save/\">ACL SAVE</a></strong> - Saves the effective ACL rules in the configured ACL file.</summary>\n\n**Syntax:** `ACL SAVE`\n\n**Description:** Saves the effective ACL rules in the configured ACL file.\n\n**Complexity:** O(N). Where N is the number of configured users.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-setuser/\">ACL SETUSER</a></strong> - Creates and modifies an ACL user and its rules.</summary>\n\n**Syntax:** `ACL SETUSER username [rule [rule ...]]`\n\n**Description:** Creates and modifies an ACL user and its rules.\n\n**Complexity:** O(N). Where N is the number of rules provided.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-users/\">ACL USERS</a></strong> - Lists all ACL users.</summary>\n\n**Syntax:** `ACL USERS`\n\n**Description:** Lists all ACL users.\n\n**Complexity:** O(N). Where N is the number of configured users.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-whoami/\">ACL WHOAMI</a></strong> - Returns the authenticated username of the current connection.</summary>\n\n**Syntax:** `ACL WHOAMI`\n\n**Description:** Returns the authenticated username of the current connection.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bgrewriteaof/\">BGREWRITEAOF</a></strong> - Asynchronously rewrites the append-only file to disk.</summary>\n\n**Syntax:** `BGREWRITEAOF`\n\n**Description:** Asynchronously rewrites the append-only file to disk.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bgsave/\">BGSAVE</a></strong> - Asynchronously saves the database(s) to disk.</summary>\n\n**Syntax:** `BGSAVE [SCHEDULE]`\n\n**Description:** Asynchronously saves the database(s) to disk.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command/\">COMMAND</a></strong> - Returns detailed information about all commands.</summary>\n\n**Syntax:** `COMMAND`\n\n**Description:** Returns detailed information about all commands.\n\n**Complexity:** O(N) where N is the total number of Redis commands\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-count/\">COMMAND COUNT</a></strong> - Returns a count of commands.</summary>\n\n**Syntax:** `COMMAND COUNT`\n\n**Description:** Returns a count of commands.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-docs/\">COMMAND DOCS</a></strong> - Returns documentary information about one, multiple or all commands.</summary>\n\n**Syntax:** `COMMAND DOCS [command-name [command-name ...]]`\n\n**Description:** Returns documentary information about one, multiple or all commands.\n\n**Complexity:** O(N) where N is the number of commands to look up\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-getkeys/\">COMMAND GETKEYS</a></strong> - Extracts the key names from an arbitrary command.</summary>\n\n**Syntax:** `COMMAND GETKEYS command [arg [arg ...]]`\n\n**Description:** Extracts the key names from an arbitrary command.\n\n**Complexity:** O(N) where N is the number of arguments to the command\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-getkeysandflags/\">COMMAND GETKEYSANDFLAGS</a></strong> - Extracts the key names and access flags for an arbitrary command.</summary>\n\n**Syntax:** `COMMAND GETKEYSANDFLAGS command [arg [arg ...]]`\n\n**Description:** Extracts the key names and access flags for an arbitrary command.\n\n**Complexity:** O(N) where N is the number of arguments to the command\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-info/\">COMMAND INFO</a></strong> - Returns information about one, multiple or all commands.</summary>\n\n**Syntax:** `COMMAND INFO [command-name [command-name ...]]`\n\n**Description:** Returns information about one, multiple or all commands.\n\n**Complexity:** O(N) where N is the number of commands to look up\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-list/\">COMMAND LIST</a></strong> - Returns a list of command names.</summary>\n\n**Syntax:** `COMMAND LIST [FILTERBY <MODULE module-name | ACLCAT category |\n  PATTERN pattern>]`\n\n**Description:** Returns a list of command names.\n\n**Complexity:** O(N) where N is the total number of Redis commands\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/config-get/\">CONFIG GET</a></strong> - Returns the effective values of configuration parameters.</summary>\n\n**Syntax:** `CONFIG GET parameter [parameter ...]`\n\n**Description:** Returns the effective values of configuration parameters.\n\n**Complexity:** O(N) when N is the number of configuration parameters provided\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/config-resetstat/\">CONFIG RESETSTAT</a></strong> - Resets the server's statistics.</summary>\n\n**Syntax:** `CONFIG RESETSTAT`\n\n**Description:** Resets the server's statistics.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/config-rewrite/\">CONFIG REWRITE</a></strong> - Persists the effective configuration to file.</summary>\n\n**Syntax:** `CONFIG REWRITE`\n\n**Description:** Persists the effective configuration to file.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/config-set/\">CONFIG SET</a></strong> - Sets configuration parameters in-flight.</summary>\n\n**Syntax:** `CONFIG SET parameter value [parameter value ...]`\n\n**Description:** Sets configuration parameters in-flight.\n\n**Complexity:** O(N) when N is the number of configuration parameters provided\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/dbsize/\">DBSIZE</a></strong> - Returns the number of keys in the database.</summary>\n\n**Syntax:** `DBSIZE`\n\n**Description:** Returns the number of keys in the database.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/failover/\">FAILOVER</a></strong> - Starts a coordinated failover from a server to one of its replicas.</summary>\n\n**Syntax:** `FAILOVER [TO host port [FORCE]] [ABORT] [TIMEOUT milliseconds]`\n\n**Description:** Starts a coordinated failover from a server to one of its replicas.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/flushall/\">FLUSHALL</a></strong> - Removes all keys from all databases.</summary>\n\n**Syntax:** `FLUSHALL [ASYNC | SYNC]`\n\n**Description:** Removes all keys from all databases.\n\n**Complexity:** O(N) where N is the total number of keys in all databases\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/flushdb/\">FLUSHDB</a></strong> - Remove all keys from the current database.</summary>\n\n**Syntax:** `FLUSHDB [ASYNC | SYNC]`\n\n**Description:** Remove all keys from the current database.\n\n**Complexity:** O(N) where N is the number of keys in the selected database\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/info/\">INFO</a></strong> - Returns information and statistics about the server.</summary>\n\n**Syntax:** `INFO [section [section ...]]`\n\n**Description:** Returns information and statistics about the server.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lastsave/\">LASTSAVE</a></strong> - Returns the Unix timestamp of the last successful save to disk.</summary>\n\n**Syntax:** `LASTSAVE`\n\n**Description:** Returns the Unix timestamp of the last successful save to disk.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-doctor/\">LATENCY DOCTOR</a></strong> - Returns a human-readable latency analysis report.</summary>\n\n**Syntax:** `LATENCY DOCTOR`\n\n**Description:** Returns a human-readable latency analysis report.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-graph/\">LATENCY GRAPH</a></strong> - Returns a latency graph for an event.</summary>\n\n**Syntax:** `LATENCY GRAPH event`\n\n**Description:** Returns a latency graph for an event.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-histogram/\">LATENCY HISTOGRAM</a></strong> - Returns the cumulative distribution of latencies of a subset or all commands.</summary>\n\n**Syntax:** `LATENCY HISTOGRAM [command [command ...]]`\n\n**Description:** Returns the cumulative distribution of latencies of a subset or all commands.\n\n**Complexity:** O(N) where N is the number of commands with latency information being retrieved.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-history/\">LATENCY HISTORY</a></strong> - Returns timestamp-latency samples for an event.</summary>\n\n**Syntax:** `LATENCY HISTORY event`\n\n**Description:** Returns timestamp-latency samples for an event.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-latest/\">LATENCY LATEST</a></strong> - Returns the latest latency samples for all events.</summary>\n\n**Syntax:** `LATENCY LATEST`\n\n**Description:** Returns the latest latency samples for all events.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-reset/\">LATENCY RESET</a></strong> - Resets the latency data for one or more events.</summary>\n\n**Syntax:** `LATENCY RESET [event [event ...]]`\n\n**Description:** Resets the latency data for one or more events.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lolwut/\">LOLWUT</a></strong> - Displays computer art and the Redis version</summary>\n\n**Syntax:** `LOLWUT [VERSION version]`\n\n**Description:** Displays computer art and the Redis version\n\n**Complexity:** \n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-doctor/\">MEMORY DOCTOR</a></strong> - Outputs a memory problems report.</summary>\n\n**Syntax:** `MEMORY DOCTOR`\n\n**Description:** Outputs a memory problems report.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-malloc-stats/\">MEMORY MALLOC-STATS</a></strong> - Returns the allocator statistics.</summary>\n\n**Syntax:** `MEMORY MALLOC-STATS`\n\n**Description:** Returns the allocator statistics.\n\n**Complexity:** Depends on how much memory is allocated, could be slow\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-purge/\">MEMORY PURGE</a></strong> - Asks the allocator to release memory.</summary>\n\n**Syntax:** `MEMORY PURGE`\n\n**Description:** Asks the allocator to release memory.\n\n**Complexity:** Depends on how much memory is allocated, could be slow\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-stats/\">MEMORY STATS</a></strong> - Returns details about memory usage.</summary>\n\n**Syntax:** `MEMORY STATS`\n\n**Description:** Returns details about memory usage.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-usage/\">MEMORY USAGE</a></strong> - Estimates the memory usage of a key.</summary>\n\n**Syntax:** `MEMORY USAGE key [SAMPLES count]`\n\n**Description:** Estimates the memory usage of a key.\n\n**Complexity:** O(N) where N is the number of samples.\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/module-list/\">MODULE LIST</a></strong> - Returns all loaded modules.</summary>\n\n**Syntax:** `MODULE LIST`\n\n**Description:** Returns all loaded modules.\n\n**Complexity:** O(N) where N is the number of loaded modules.\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/module-load/\">MODULE LOAD</a></strong> - Loads a module.</summary>\n\n**Syntax:** `MODULE LOAD path [arg [arg ...]]`\n\n**Description:** Loads a module.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/module-loadex/\">MODULE LOADEX</a></strong> - Loads a module using extended parameters.</summary>\n\n**Syntax:** `MODULE LOADEX path [CONFIG name value [CONFIG name value ...]]\n  [ARGS args [args ...]]`\n\n**Description:** Loads a module using extended parameters.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/module-unload/\">MODULE UNLOAD</a></strong> - Unloads a module.</summary>\n\n**Syntax:** `MODULE UNLOAD name`\n\n**Description:** Unloads a module.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/monitor/\">MONITOR</a></strong> - Listens for all requests received by the server in real-time.</summary>\n\n**Syntax:** `MONITOR`\n\n**Description:** Listens for all requests received by the server in real-time.\n\n**Complexity:** \n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/psync/\">PSYNC</a></strong> - An internal command used in replication.</summary>\n\n**Syntax:** `PSYNC replicationid offset`\n\n**Description:** An internal command used in replication.\n\n**Complexity:** \n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/replconf/\">REPLCONF</a></strong> - An internal command for configuring the replication stream.</summary>\n\n**Syntax:** `REPLCONF`\n\n**Description:** An internal command for configuring the replication stream.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/replicaof/\">REPLICAOF</a></strong> - Configures a server as replica of another, or promotes it to a master.</summary>\n\n**Syntax:** `REPLICAOF <host port | NO ONE>`\n\n**Description:** Configures a server as replica of another, or promotes it to a master.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/restore-asking/\">RESTORE-ASKING</a></strong> - An internal command for migrating keys in a cluster.</summary>\n\n**Syntax:** `RESTORE-ASKING key ttl serialized-value [REPLACE] [ABSTTL]\n  [IDLETIME seconds] [FREQ frequency]`\n\n**Description:** An internal command for migrating keys in a cluster.\n\n**Complexity:** O(1) to create the new key and additional O(N*M) to reconstruct the serialized value, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1). However for sorted set values the complexity is O(N*M*log(N)) because inserting values into sorted sets is O(log(N)).\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/role/\">ROLE</a></strong> - Returns the replication role.</summary>\n\n**Syntax:** `ROLE`\n\n**Description:** Returns the replication role.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.12\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/save/\">SAVE</a></strong> - Synchronously saves the database(s) to disk.</summary>\n\n**Syntax:** `SAVE`\n\n**Description:** Synchronously saves the database(s) to disk.\n\n**Complexity:** O(N) where N is the total number of keys in all databases\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/shutdown/\">SHUTDOWN</a></strong> - Synchronously saves the database(s) to disk and shuts down the Redis server.</summary>\n\n**Syntax:** `SHUTDOWN [NOSAVE | SAVE] [NOW] [FORCE] [ABORT]`\n\n**Description:** Synchronously saves the database(s) to disk and shuts down the Redis server.\n\n**Complexity:** O(N) when saving, where N is the total number of keys in all databases when saving data, otherwise O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/slaveof/\">SLAVEOF</a></strong> - Sets a Redis server as a replica of another, or promotes it to being a master.</summary>\n\n**Syntax:** `SLAVEOF <host port | NO ONE>`\n\n**Description:** Sets a Redis server as a replica of another, or promotes it to being a master.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/slowlog-get/\">SLOWLOG GET</a></strong> - Returns the slow log's entries.</summary>\n\n**Syntax:** `SLOWLOG GET [count]`\n\n**Description:** Returns the slow log's entries.\n\n**Complexity:** O(N) where N is the number of entries returned\n\n**Since:** 2.2.12\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/slowlog-len/\">SLOWLOG LEN</a></strong> - Returns the number of entries in the slow log.</summary>\n\n**Syntax:** `SLOWLOG LEN`\n\n**Description:** Returns the number of entries in the slow log.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.12\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/slowlog-reset/\">SLOWLOG RESET</a></strong> - Clears all entries from the slow log.</summary>\n\n**Syntax:** `SLOWLOG RESET`\n\n**Description:** Clears all entries from the slow log.\n\n**Complexity:** O(N) where N is the number of entries in the slowlog\n\n**Since:** 2.2.12\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/swapdb/\">SWAPDB</a></strong> - Swaps two Redis databases.</summary>\n\n**Syntax:** `SWAPDB index1 index2`\n\n**Description:** Swaps two Redis databases.\n\n**Complexity:** O(N) where N is the count of clients watching or blocking on keys from both databases.\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sync/\">SYNC</a></strong> - An internal command used in replication.</summary>\n\n**Syntax:** `SYNC`\n\n**Description:** An internal command used in replication.\n\n**Complexity:** \n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/time/\">TIME</a></strong> - Returns the server time.</summary>\n\n**Syntax:** `TIME`\n\n**Description:** Returns the server time.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>"},{"id":"cluster-commands","title":"Cluster commands","role":"content","text":"Cluster commands manage Redis Cluster operations.\n\n<details>\n<summary><strong><a href=\"/commands/asking/\">ASKING</a></strong> - Signals that a cluster client is following an -ASK redirect.</summary>\n\n**Syntax:** `ASKING`\n\n**Description:** Signals that a cluster client is following an -ASK redirect.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-addslots/\">CLUSTER ADDSLOTS</a></strong> - Assigns new hash slots to a node.</summary>\n\n**Syntax:** `CLUSTER ADDSLOTS slot [slot ...]`\n\n**Description:** Assigns new hash slots to a node.\n\n**Complexity:** O(N) where N is the total number of hash slot arguments\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-addslotsrange/\">CLUSTER ADDSLOTSRANGE</a></strong> - Assigns new hash slot ranges to a node.</summary>\n\n**Syntax:** `CLUSTER ADDSLOTSRANGE start-slot end-slot [start-slot end-slot ...]`\n\n**Description:** Assigns new hash slot ranges to a node.\n\n**Complexity:** O(N) where N is the total number of the slots between the start slot and end slot arguments.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-bumpepoch/\">CLUSTER BUMPEPOCH</a></strong> - Advances the cluster config epoch.</summary>\n\n**Syntax:** `CLUSTER BUMPEPOCH`\n\n**Description:** Advances the cluster config epoch.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-count-failure-reports/\">CLUSTER COUNT-FAILURE-REPORTS</a></strong> - Returns the number of active failure reports active for a node.</summary>\n\n**Syntax:** `CLUSTER COUNT-FAILURE-REPORTS node-id`\n\n**Description:** Returns the number of active failure reports active for a node.\n\n**Complexity:** O(N) where N is the number of failure reports\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-countkeysinslot/\">CLUSTER COUNTKEYSINSLOT</a></strong> - Returns the number of keys in a hash slot.</summary>\n\n**Syntax:** `CLUSTER COUNTKEYSINSLOT slot`\n\n**Description:** Returns the number of keys in a hash slot.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-delslots/\">CLUSTER DELSLOTS</a></strong> - Sets hash slots as unbound for a node.</summary>\n\n**Syntax:** `CLUSTER DELSLOTS slot [slot ...]`\n\n**Description:** Sets hash slots as unbound for a node.\n\n**Complexity:** O(N) where N is the total number of hash slot arguments\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-delslotsrange/\">CLUSTER DELSLOTSRANGE</a></strong> - Sets hash slot ranges as unbound for a node.</summary>\n\n**Syntax:** `CLUSTER DELSLOTSRANGE start-slot end-slot [start-slot end-slot ...]`\n\n**Description:** Sets hash slot ranges as unbound for a node.\n\n**Complexity:** O(N) where N is the total number of the slots between the start slot and end slot arguments.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-failover/\">CLUSTER FAILOVER</a></strong> - Forces a replica to perform a manual failover of its master.</summary>\n\n**Syntax:** `CLUSTER FAILOVER [FORCE | TAKEOVER]`\n\n**Description:** Forces a replica to perform a manual failover of its master.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-flushslots/\">CLUSTER FLUSHSLOTS</a></strong> - Deletes all slots information from a node.</summary>\n\n**Syntax:** `CLUSTER FLUSHSLOTS`\n\n**Description:** Deletes all slots information from a node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-forget/\">CLUSTER FORGET</a></strong> - Removes a node from the nodes table.</summary>\n\n**Syntax:** `CLUSTER FORGET node-id`\n\n**Description:** Removes a node from the nodes table.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-getkeysinslot/\">CLUSTER GETKEYSINSLOT</a></strong> - Returns the key names in a hash slot.</summary>\n\n**Syntax:** `CLUSTER GETKEYSINSLOT slot count`\n\n**Description:** Returns the key names in a hash slot.\n\n**Complexity:** O(N) where N is the number of requested keys\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-info/\">CLUSTER INFO</a></strong> - Returns information about the state of a node.</summary>\n\n**Syntax:** `CLUSTER INFO`\n\n**Description:** Returns information about the state of a node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-keyslot/\">CLUSTER KEYSLOT</a></strong> - Returns the hash slot for a key.</summary>\n\n**Syntax:** `CLUSTER KEYSLOT key`\n\n**Description:** Returns the hash slot for a key.\n\n**Complexity:** O(N) where N is the number of bytes in the key\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-links/\">CLUSTER LINKS</a></strong> - Returns a list of all TCP links to and from peer nodes.</summary>\n\n**Syntax:** `CLUSTER LINKS`\n\n**Description:** Returns a list of all TCP links to and from peer nodes.\n\n**Complexity:** O(N) where N is the total number of Cluster nodes\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-meet/\">CLUSTER MEET</a></strong> - Forces a node to handshake with another node.</summary>\n\n**Syntax:** `CLUSTER MEET ip port [cluster-bus-port]`\n\n**Description:** Forces a node to handshake with another node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-myid/\">CLUSTER MYID</a></strong> - Returns the ID of a node.</summary>\n\n**Syntax:** `CLUSTER MYID`\n\n**Description:** Returns the ID of a node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-myshardid/\">CLUSTER MYSHARDID</a></strong> - Returns the shard ID of a node.</summary>\n\n**Syntax:** `CLUSTER MYSHARDID`\n\n**Description:** Returns the shard ID of a node.\n\n**Complexity:** O(1)\n\n**Since:** 7.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-nodes/\">CLUSTER NODES</a></strong> - Returns the cluster configuration for a node.</summary>\n\n**Syntax:** `CLUSTER NODES`\n\n**Description:** Returns the cluster configuration for a node.\n\n**Complexity:** O(N) where N is the total number of Cluster nodes\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-replicas/\">CLUSTER REPLICAS</a></strong> - Lists the replica nodes of a master node.</summary>\n\n**Syntax:** `CLUSTER REPLICAS node-id`\n\n**Description:** Lists the replica nodes of a master node.\n\n**Complexity:** O(N) where N is the number of replicas.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-replicate/\">CLUSTER REPLICATE</a></strong> - Configure a node as replica of a master node.</summary>\n\n**Syntax:** `CLUSTER REPLICATE node-id`\n\n**Description:** Configure a node as replica of a master node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-reset/\">CLUSTER RESET</a></strong> - Resets a node.</summary>\n\n**Syntax:** `CLUSTER RESET [HARD | SOFT]`\n\n**Description:** Resets a node.\n\n**Complexity:** O(N) where N is the number of known nodes. The command may execute a FLUSHALL as a side effect.\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-saveconfig/\">CLUSTER SAVECONFIG</a></strong> - Forces a node to save the cluster configuration to disk.</summary>\n\n**Syntax:** `CLUSTER SAVECONFIG`\n\n**Description:** Forces a node to save the cluster configuration to disk.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-set-config-epoch/\">CLUSTER SET-CONFIG-EPOCH</a></strong> - Sets the configuration epoch for a new node.</summary>\n\n**Syntax:** `CLUSTER SET-CONFIG-EPOCH config-epoch`\n\n**Description:** Sets the configuration epoch for a new node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-setslot/\">CLUSTER SETSLOT</a></strong> - Binds a hash slot to a node.</summary>\n\n**Syntax:** `CLUSTER SETSLOT slot <IMPORTING node-id | MIGRATING node-id |\n  NODE node-id | STABLE>`\n\n**Description:** Binds a hash slot to a node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-shards/\">CLUSTER SHARDS</a></strong> - Returns the mapping of cluster slots to shards.</summary>\n\n**Syntax:** `CLUSTER SHARDS`\n\n**Description:** Returns the mapping of cluster slots to shards.\n\n**Complexity:** O(N) where N is the total number of cluster nodes\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-slaves/\">CLUSTER SLAVES</a></strong> - Lists the replica nodes of a master node.</summary>\n\n**Syntax:** `CLUSTER SLAVES node-id`\n\n**Description:** Lists the replica nodes of a master node.\n\n**Complexity:** O(N) where N is the number of replicas.\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-slots/\">CLUSTER SLOTS</a></strong> - Returns the mapping of cluster slots to nodes.</summary>\n\n**Syntax:** `CLUSTER SLOTS`\n\n**Description:** Returns the mapping of cluster slots to nodes.\n\n**Complexity:** O(N) where N is the total number of Cluster nodes\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/readonly/\">READONLY</a></strong> - Enables read-only queries for a connection to a Redis Cluster replica node.</summary>\n\n**Syntax:** `READONLY`\n\n**Description:** Enables read-only queries for a connection to a Redis Cluster replica node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/readwrite/\">READWRITE</a></strong> - Enables read-write queries for a connection to a Reids Cluster replica node.</summary>\n\n**Syntax:** `READWRITE`\n\n**Description:** Enables read-write queries for a connection to a Reids Cluster replica node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>"},{"id":"generic-commands","title":"Generic commands","role":"content","text":"Generic commands work across all data types.\n\n<details>\n<summary><strong><a href=\"/commands/copy/\">COPY</a></strong> - Copies the value of a key to a new key.</summary>\n\n**Syntax:** `COPY source destination [DB destination-db] [REPLACE]`\n\n**Description:** Copies the value of a key to a new key.\n\n**Complexity:** O(N) worst case for collections, where N is the number of nested items. O(1) for string values.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/del/\">DEL</a></strong> - Deletes one or more keys.</summary>\n\n**Syntax:** `DEL key [key ...]`\n\n**Description:** Deletes one or more keys.\n\n**Complexity:** O(N) where N is the number of keys that will be removed. When a key to remove holds a value other than a string, the individual complexity for this key is O(M) where M is the number of elements in the list, set, sorted set or hash. Removing a single key that holds a string value is O(1).\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/dump/\">DUMP</a></strong> - Returns a serialized representation of the value stored at a key.</summary>\n\n**Syntax:** `DUMP key`\n\n**Description:** Returns a serialized representation of the value stored at a key.\n\n**Complexity:** O(1) to access the key and additional O(N*M) to serialize it, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1).\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/exists/\">EXISTS</a></strong> - Determines whether one or more keys exist.</summary>\n\n**Syntax:** `EXISTS key [key ...]`\n\n**Description:** Determines whether one or more keys exist.\n\n**Complexity:** O(N) where N is the number of keys to check.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/expire/\">EXPIRE</a></strong> - Sets the expiration time of a key in seconds.</summary>\n\n**Syntax:** `EXPIRE key seconds [NX | XX | GT | LT]`\n\n**Description:** Sets the expiration time of a key in seconds.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/expireat/\">EXPIREAT</a></strong> - Sets the expiration time of a key to a Unix timestamp.</summary>\n\n**Syntax:** `EXPIREAT key unix-time-seconds [NX | XX | GT | LT]`\n\n**Description:** Sets the expiration time of a key to a Unix timestamp.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/expiretime/\">EXPIRETIME</a></strong> - Returns the expiration time of a key as a Unix timestamp.</summary>\n\n**Syntax:** `EXPIRETIME key`\n\n**Description:** Returns the expiration time of a key as a Unix timestamp.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/keys/\">KEYS</a></strong> - Returns all key names that match a pattern.</summary>\n\n**Syntax:** `KEYS pattern`\n\n**Description:** Returns all key names that match a pattern.\n\n**Complexity:** O(N) with N being the number of keys in the database, under the assumption that the key names in the database and the given pattern have limited length.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/migrate/\">MIGRATE</a></strong> - Atomically transfers a key from one Redis instance to another.</summary>\n\n**Syntax:** `MIGRATE host port <key | \"\"> destination-db timeout [COPY] [REPLACE]\n  [AUTH password | AUTH2 username password] [KEYS key [key ...]]`\n\n**Description:** Atomically transfers a key from one Redis instance to another.\n\n**Complexity:** This command actually executes a DUMP+DEL in the source instance, and a RESTORE in the target instance. See the pages of these commands for time complexity. Also an O(N) data transfer between the two instances is performed.\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/move/\">MOVE</a></strong> - Moves a key to another database.</summary>\n\n**Syntax:** `MOVE key db`\n\n**Description:** Moves a key to another database.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/object-encoding/\">OBJECT ENCODING</a></strong> - Returns the internal encoding of a Redis object.</summary>\n\n**Syntax:** `OBJECT ENCODING key`\n\n**Description:** Returns the internal encoding of a Redis object.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.3\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/object-freq/\">OBJECT FREQ</a></strong> - Returns the logarithmic access frequency counter of a Redis object.</summary>\n\n**Syntax:** `OBJECT FREQ key`\n\n**Description:** Returns the logarithmic access frequency counter of a Redis object.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/object-idletime/\">OBJECT IDLETIME</a></strong> - Returns the time since the last access to a Redis object.</summary>\n\n**Syntax:** `OBJECT IDLETIME key`\n\n**Description:** Returns the time since the last access to a Redis object.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.3\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/object-refcount/\">OBJECT REFCOUNT</a></strong> - Returns the reference count of a value of a key.</summary>\n\n**Syntax:** `OBJECT REFCOUNT key`\n\n**Description:** Returns the reference count of a value of a key.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.3\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/persist/\">PERSIST</a></strong> - Removes the expiration time of a key.</summary>\n\n**Syntax:** `PERSIST key`\n\n**Description:** Removes the expiration time of a key.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pexpire/\">PEXPIRE</a></strong> - Sets the expiration time of a key in milliseconds.</summary>\n\n**Syntax:** `PEXPIRE key milliseconds [NX | XX | GT | LT]`\n\n**Description:** Sets the expiration time of a key in milliseconds.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pexpireat/\">PEXPIREAT</a></strong> - Sets the expiration time of a key to a Unix milliseconds timestamp.</summary>\n\n**Syntax:** `PEXPIREAT key unix-time-milliseconds [NX | XX | GT | LT]`\n\n**Description:** Sets the expiration time of a key to a Unix milliseconds timestamp.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pexpiretime/\">PEXPIRETIME</a></strong> - Returns the expiration time of a key as a Unix milliseconds timestamp.</summary>\n\n**Syntax:** `PEXPIRETIME key`\n\n**Description:** Returns the expiration time of a key as a Unix milliseconds timestamp.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pttl/\">PTTL</a></strong> - Returns the expiration time in milliseconds of a key.</summary>\n\n**Syntax:** `PTTL key`\n\n**Description:** Returns the expiration time in milliseconds of a key.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/randomkey/\">RANDOMKEY</a></strong> - Returns a random key name from the database.</summary>\n\n**Syntax:** `RANDOMKEY`\n\n**Description:** Returns a random key name from the database.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rename/\">RENAME</a></strong> - Renames a key and overwrites the destination.</summary>\n\n**Syntax:** `RENAME key newkey`\n\n**Description:** Renames a key and overwrites the destination.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/renamenx/\">RENAMENX</a></strong> - Renames a key only when the target key name doesn't exist.</summary>\n\n**Syntax:** `RENAMENX key newkey`\n\n**Description:** Renames a key only when the target key name doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/restore/\">RESTORE</a></strong> - Creates a key from the serialized representation of a value.</summary>\n\n**Syntax:** `RESTORE key ttl serialized-value [REPLACE] [ABSTTL]\n  [IDLETIME seconds] [FREQ frequency]`\n\n**Description:** Creates a key from the serialized representation of a value.\n\n**Complexity:** O(1) to create the new key and additional O(N*M) to reconstruct the serialized value, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1). However for sorted set values the complexity is O(N*M*log(N)) because inserting values into sorted sets is O(log(N)).\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-6-2-commands/\">Redis 6.2 Commands Reference</a></strong> - Complete list of all Redis commands available in version 6.2, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 6.2, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-7-2-commands/\">Redis 7.2 Commands Reference</a></strong> - Complete list of all Redis commands available in version 7.2, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 7.2, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-7-4-commands/\">Redis 7.4 Commands Reference</a></strong> - Complete list of all Redis commands available in version 7.4, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 7.4, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-8-0-commands/\">Redis 8.0 Commands Reference</a></strong> - Complete list of all Redis commands available in version 8.0, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 8.0, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-8-2-commands/\">Redis 8.2 Commands Reference</a></strong> - Complete list of all Redis commands available in version 8.2, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 8.2, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-8-4-commands/\">Redis 8.4 Commands Reference</a></strong> - Complete list of all Redis commands available in version 8.4, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 8.4, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/scan/\">SCAN</a></strong> - Iterates over the key names in the database.</summary>\n\n**Syntax:** `SCAN cursor [MATCH pattern] [COUNT count] [TYPE type]`\n\n**Description:** Iterates over the key names in the database.\n\n**Complexity:** O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sort/\">SORT</a></strong> - Sorts the elements in a list, a set, or a sorted set, optionally storing the result.</summary>\n\n**Syntax:** `SORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern\n  ...]] [ASC | DESC] [ALPHA] [STORE destination]`\n\n**Description:** Sorts the elements in a list, a set, or a sorted set, optionally storing the result.\n\n**Complexity:** O(N+M*log(M)) where N is the number of elements in the list or set to sort, and M the number of returned elements. When the elements are not sorted, complexity is O(N).\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sort_ro/\">SORT_RO</a></strong> - Returns the sorted elements of a list, a set, or a sorted set.</summary>\n\n**Syntax:** `SORT RO key [BY pattern] [LIMIT offset count] [GET pattern [GET\n  pattern ...]] [ASC | DESC] [ALPHA]`\n\n**Description:** Returns the sorted elements of a list, a set, or a sorted set.\n\n**Complexity:** O(N+M*log(M)) where N is the number of elements in the list or set to sort, and M the number of returned elements. When the elements are not sorted, complexity is O(N).\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/touch/\">TOUCH</a></strong> - Returns the number of existing keys out of those specified after updating the time they were last accessed.</summary>\n\n**Syntax:** `TOUCH key [key ...]`\n\n**Description:** Returns the number of existing keys out of those specified after updating the time they were last accessed.\n\n**Complexity:** O(N) where N is the number of keys that will be touched.\n\n**Since:** 3.2.1\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ttl/\">TTL</a></strong> - Returns the expiration time in seconds of a key.</summary>\n\n**Syntax:** `TTL key`\n\n**Description:** Returns the expiration time in seconds of a key.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/type/\">TYPE</a></strong> - Determines the type of value stored at a key.</summary>\n\n**Syntax:** `TYPE key`\n\n**Description:** Determines the type of value stored at a key.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/unlink/\">UNLINK</a></strong> - Asynchronously deletes one or more keys.</summary>\n\n**Syntax:** `UNLINK key [key ...]`\n\n**Description:** Asynchronously deletes one or more keys.\n\n**Complexity:** O(1) for each key removed regardless of its size. Then the command does O(N) work in a different thread in order to reclaim memory, where N is the number of allocations the deleted objects where composed of.\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/wait/\">WAIT</a></strong> - Blocks until the asynchronous replication of all preceding write commands sent by the connection is completed.</summary>\n\n**Syntax:** `WAIT numreplicas timeout`\n\n**Description:** Blocks until the asynchronous replication of all preceding write commands sent by the connection is completed.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/waitaof/\">WAITAOF</a></strong> - Blocks until all of the preceding write commands sent by the connection are written to the append-only file of the master and/or replicas.</summary>\n\n**Syntax:** `WAITAOF numlocal numreplicas timeout`\n\n**Description:** Blocks until all of the preceding write commands sent by the connection are written to the append-only file of the master and/or replicas.\n\n**Complexity:** O(1)\n\n**Since:** 7.2.0\n\n</details>"}],"examples":[]}
{"schema_version":2,"id":"commands/redis-8-10-commands","title":"Redis 8.10 Commands Reference","url":"https://redis.io/docs/latest/commands/redis-8-10-commands/","summary":"Complete list of all Redis commands available in version 8.10, organized by functional group","tags":["docs","develop","stack","oss","rs","rc","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"3e5456d686b7a99ab5c62c0dcb84bef2e590d9097e781f7f9a09c99e17b9752c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This page provides a comprehensive reference of all Redis commands available in Redis 8.10, organized by functional group. Each command includes its description and syntax in a collapsible section for easy navigation.\n\n> [!NOTE]\n> Redis 8.10 includes all commands from previous versions plus new commands introduced in 8.10. Commands marked with **⭐ New in 8.10** were added in this release."},{"id":"quick-navigation","title":"Quick Navigation","role":"content","text":"- [String commands](#string-commands)\n- [Hash commands](#hash-commands)\n- [List commands](#list-commands)\n- [Set commands](#set-commands)\n- [Sorted set commands](#sorted-set-commands)\n- [Stream commands](#stream-commands)\n- [Bitmap commands](#bitmap-commands)\n- [Array commands](#array-commands)\n- [HyperLogLog commands](#hyperloglog-commands)\n- [Geospatial commands](#geospatial-commands)\n- [JSON commands](#json-commands)\n- [Search commands](#search-commands)\n- [Time series commands](#time-series-commands)\n- [Vector set commands](#vector-set-commands)\n- [Pub/Sub commands](#pubsub-commands)\n- [Transaction commands](#transaction-commands)\n- [Scripting commands](#scripting-commands)\n- [Connection commands](#connection-commands)\n- [Server commands](#server-commands)\n- [Cluster commands](#cluster-commands)\n- [Generic commands](#generic-commands)\n\n---"},{"id":"string-commands","title":"String commands","role":"content","text":"String commands operate on string values, the most basic Redis data type.\n\n<details>\n<summary><strong><a href=\"/commands/append/\">APPEND</a></strong> - Appends a string to the value of a key. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `APPEND key value`\n\n**Description:** Appends a string to the value of a key. Creates the key if it doesn't exist.\n\n**Complexity:** O(1). The amortized time complexity is O(1) assuming the appended value is small and the already present value is of any size, since the dynamic string library used by Redis will double the free space available on every reallocation.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/decr/\">DECR</a></strong> - Decrements the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `DECR key`\n\n**Description:** Decrements the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/decrby/\">DECRBY</a></strong> - Decrements a number from the integer value of a key. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `DECRBY key decrement`\n\n**Description:** Decrements a number from the integer value of a key. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/delex/\">DELEX</a></strong> - Conditionally removes the specified key based on value or hash digest comparison.</summary>\n\n**Syntax:** `DELEX key [IFEQ ifeq-value | IFNE ifne-value | IFDEQ ifdeq-digest |\n  IFDNE ifdne-digest]`\n\n**Description:** Conditionally removes the specified key based on value or hash digest comparison.\n\n**Complexity:** O(1) for IFEQ/IFNE, O(N) for IFDEQ/IFDNE where N is the length of the string value.\n\n**Since:** 8.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/digest/\">DIGEST</a></strong> - Returns the hash digest of a string value as a hexadecimal string.</summary>\n\n**Syntax:** `DIGEST key`\n\n**Description:** Returns the hash digest of a string value as a hexadecimal string.\n\n**Complexity:** O(N) where N is the length of the string value.\n\n**Since:** 8.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/get/\">GET</a></strong> - Returns the string value of a key.</summary>\n\n**Syntax:** `GET key`\n\n**Description:** Returns the string value of a key.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getdel/\">GETDEL</a></strong> - Returns the string value of a key after deleting the key.</summary>\n\n**Syntax:** `GETDEL key`\n\n**Description:** Returns the string value of a key after deleting the key.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getex/\">GETEX</a></strong> - Returns the string value of a key after setting its expiration time.</summary>\n\n**Syntax:** `GETEX key [EX seconds | PX milliseconds | EXAT unix-time-seconds |\n  PXAT unix-time-milliseconds | PERSIST]`\n\n**Description:** Returns the string value of a key after setting its expiration time.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getrange/\">GETRANGE</a></strong> - Returns a substring of the string stored at a key.</summary>\n\n**Syntax:** `GETRANGE key start end`\n\n**Description:** Returns a substring of the string stored at a key.\n\n**Complexity:** O(N) where N is the length of the returned string. The complexity is ultimately determined by the returned length, but because creating a substring from an existing string is very cheap, it can be considered O(1) for small strings.\n\n**Since:** 2.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getset/\">GETSET</a></strong> - Returns the previous string value of a key after setting it to a new value.</summary>\n\n**Syntax:** `GETSET key value`\n\n**Description:** Returns the previous string value of a key after setting it to a new value.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/incr/\">INCR</a></strong> - Increments the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `INCR key`\n\n**Description:** Increments the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/incrby/\">INCRBY</a></strong> - Increments the integer value of a key by a number. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `INCRBY key increment`\n\n**Description:** Increments the integer value of a key by a number. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/incrbyfloat/\">INCRBYFLOAT</a></strong> - Increment the floating point value of a key by a number. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `INCRBYFLOAT key increment`\n\n**Description:** Increment the floating point value of a key by a number. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/increx/\">INCREX</a></strong> - Increments the numeric value of a key by a number and sets its expiration time. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `INCREX key [BYFLOAT increment | BYINT increment]\n [LBOUND lowerbound] [UBOUND upperbound] [SATURATE]\n [EX seconds | PX milliseconds | EXAT unix-time-seconds| PXAT unix-time-milliseconds | PERSIST] [ENX]`\n\n**Description:** Increments the numeric value of a key by a number and sets its expiration time. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lcs/\">LCS</a></strong> - Finds the longest common substring.</summary>\n\n**Syntax:** `LCS key1 key2 [LEN] [IDX] [MINMATCHLEN min-match-len] [WITHMATCHLEN]`\n\n**Description:** Finds the longest common substring.\n\n**Complexity:** O(N*M) where N and M are the lengths of s1 and s2, respectively\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/mget/\">MGET</a></strong> - Atomically returns the string values of one or more keys.</summary>\n\n**Syntax:** `MGET key [key ...]`\n\n**Description:** Atomically returns the string values of one or more keys.\n\n**Complexity:** O(N) where N is the number of keys to retrieve.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/mset/\">MSET</a></strong> - Atomically creates or modifies the string values of one or more keys.</summary>\n\n**Syntax:** `MSET key value [key value ...]`\n\n**Description:** Atomically creates or modifies the string values of one or more keys.\n\n**Complexity:** O(N) where N is the number of keys to set.\n\n**Since:** 1.0.1\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/msetex/\">MSETEX</a></strong> - Atomically sets multiple string keys with a shared expiration in a single operation.</summary>\n\n**Syntax:** `MSETEX numkeys key value [key value ...] [NX | XX] [EX seconds |\n  PX milliseconds | EXAT unix-time-seconds |\n  PXAT unix-time-milliseconds | KEEPTTL]`\n\n**Description:** Atomically sets multiple string keys with a shared expiration in a single operation.\n\n**Complexity:** O(N) where N is the number of keys to set.\n\n**Since:** 8.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/msetnx/\">MSETNX</a></strong> - Atomically modifies the string values of one or more keys only when all keys don't exist.</summary>\n\n**Syntax:** `MSETNX key value [key value ...]`\n\n**Description:** Atomically modifies the string values of one or more keys only when all keys don't exist.\n\n**Complexity:** O(N) where N is the number of keys to set.\n\n**Since:** 1.0.1\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/psetex/\">PSETEX</a></strong> - Sets both string value and expiration time in milliseconds of a key. The key is created if it doesn't exist.</summary>\n\n**Syntax:** `PSETEX key milliseconds value`\n\n**Description:** Sets both string value and expiration time in milliseconds of a key. The key is created if it doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/set/\">SET</a></strong> - Sets the string value of a key, ignoring its type. The key is created if it doesn't exist.</summary>\n\n**Syntax:** `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]`\n\n**Description:** Sets the string value of a key, ignoring its type. The key is created if it doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/setex/\">SETEX</a></strong> - Sets the string value and expiration time of a key. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `SETEX key seconds value`\n\n**Description:** Sets the string value and expiration time of a key. Creates the key if it doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/setnx/\">SETNX</a></strong> - Set the string value of a key only when the key doesn't exist.</summary>\n\n**Syntax:** `SETNX key value`\n\n**Description:** Set the string value of a key only when the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/setrange/\">SETRANGE</a></strong> - Overwrites a part of a string value with another by an offset. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `SETRANGE key offset value`\n\n**Description:** Overwrites a part of a string value with another by an offset. Creates the key if it doesn't exist.\n\n**Complexity:** O(1), not counting the time taken to copy the new string in place. Usually, this string is very small so the amortized complexity is O(1). Otherwise, complexity is O(M) with M being the length of the value argument.\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/strlen/\">STRLEN</a></strong> - Returns the length of a string value.</summary>\n\n**Syntax:** `STRLEN key`\n\n**Description:** Returns the length of a string value.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/substr/\">SUBSTR</a></strong> - Returns a substring from a string value.</summary>\n\n**Syntax:** `SUBSTR key start end`\n\n**Description:** Returns a substring from a string value.\n\n**Complexity:** O(N) where N is the length of the returned string. The complexity is ultimately determined by the returned length, but because creating a substring from an existing string is very cheap, it can be considered O(1) for small strings.\n\n**Since:** 1.0.0\n\n</details>"},{"id":"hash-commands","title":"Hash commands","role":"content","text":"Hash commands operate on hash data structures, which map string fields to string values.\n\n<details>\n<summary><strong><a href=\"/commands/hdel/\">HDEL</a></strong> - Deletes one or more fields and their values from a hash. Deletes the hash if no fields remain.</summary>\n\n**Syntax:** `HDEL key field [field ...]`\n\n**Description:** Deletes one or more fields and their values from a hash. Deletes the hash if no fields remain.\n\n**Complexity:** O(N) where N is the number of fields to be removed.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hexists/\">HEXISTS</a></strong> - Determines whether a field exists in a hash.</summary>\n\n**Syntax:** `HEXISTS key field`\n\n**Description:** Determines whether a field exists in a hash.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hexpire/\">HEXPIRE</a></strong> - Set expiry for hash field using relative time to expire (seconds)</summary>\n\n**Syntax:** `HEXPIRE key seconds [NX | XX | GT | LT] FIELDS numfields field\n  [field ...]`\n\n**Description:** Set expiry for hash field using relative time to expire (seconds)\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hexpireat/\">HEXPIREAT</a></strong> - Set expiry for hash field using an absolute Unix timestamp (seconds)</summary>\n\n**Syntax:** `HEXPIREAT key unix-time-seconds [NX | XX | GT | LT] FIELDS numfields\n  field [field ...]`\n\n**Description:** Set expiry for hash field using an absolute Unix timestamp (seconds)\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hexpiretime/\">HEXPIRETIME</a></strong> - Returns the expiration time of a hash field as a Unix timestamp, in seconds.</summary>\n\n**Syntax:** `HEXPIRETIME key FIELDS numfields field [field ...]`\n\n**Description:** Returns the expiration time of a hash field as a Unix timestamp, in seconds.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hget/\">HGET</a></strong> - Returns the value of a field in a hash.</summary>\n\n**Syntax:** `HGET key field`\n\n**Description:** Returns the value of a field in a hash.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hgetall/\">HGETALL</a></strong> - Returns all fields and values in a hash.</summary>\n\n**Syntax:** `HGETALL key`\n\n**Description:** Returns all fields and values in a hash.\n\n**Complexity:** O(N) where N is the size of the hash.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hgetdel/\">HGETDEL</a></strong> - Returns the value of a field and deletes it from the hash.</summary>\n\n**Syntax:** `HGETDEL key FIELDS numfields field [field ...]`\n\n**Description:** Returns the value of a field and deletes it from the hash.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hgetex/\">HGETEX</a></strong> - Get the value of one or more fields of a given hash key, and optionally set their expiration.</summary>\n\n**Syntax:** `HGETEX key [EX seconds | PX milliseconds | EXAT unix-time-seconds |\n  PXAT unix-time-milliseconds | PERSIST] FIELDS numfields field\n  [field ...]`\n\n**Description:** Get the value of one or more fields of a given hash key, and optionally set their expiration.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/himport/\">HIMPORT</a></strong> - A container for session-based hash import commands using fieldsets. <span style=\"color: #e74c3c;\">⭐ New in 8.10</span></summary>\n\n**Syntax:** `HIMPORT`\n\n**Description:** A container for session-based hash import commands using fieldsets.\n\n**Complexity:** Depends on subcommand.\n\n**Since:** 8.10.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/himport-discard/\">HIMPORT DISCARD</a></strong> - Removes a single session-local fieldset by name. <span style=\"color: #e74c3c;\">⭐ New in 8.10</span></summary>\n\n**Syntax:** `HIMPORT DISCARD fieldset-name`\n\n**Description:** Removes a single session-local fieldset by name.\n\n**Complexity:** O(N) where N is the number of session-local fieldsets.\n\n**Since:** 8.10.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/himport-discardall/\">HIMPORT DISCARDALL</a></strong> - Removes all session-local fieldsets for the connection. <span style=\"color: #e74c3c;\">⭐ New in 8.10</span></summary>\n\n**Syntax:** `HIMPORT DISCARDALL`\n\n**Description:** Removes all session-local fieldsets for the connection.\n\n**Complexity:** O(N) where N is the number of session-local fieldsets.\n\n**Since:** 8.10.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/himport-prepare/\">HIMPORT PREPARE</a></strong> - Defines a session-local fieldset that maps a name to a sorted set of field names. <span style=\"color: #e74c3c;\">⭐ New in 8.10</span></summary>\n\n**Syntax:** `HIMPORT PREPARE fieldset-name field [field ...]`\n\n**Description:** Defines a session-local fieldset that maps a name to a sorted set of field names.\n\n**Complexity:** O(N*log(N)) where N is the number of fields.\n\n**Since:** 8.10.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/himport-set/\">HIMPORT SET</a></strong> - Creates a fieldset-based hash from values supplied in the order matching a previously prepared fieldset. <span style=\"color: #e74c3c;\">⭐ New in 8.10</span></summary>\n\n**Syntax:** `HIMPORT SET key fieldset-name value [value ...]`\n\n**Description:** Creates a fieldset-based hash from values supplied in the order matching a previously prepared fieldset.\n\n**Complexity:** O(N) where N is the number of fields in the fieldset.\n\n**Since:** 8.10.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hincrby/\">HINCRBY</a></strong> - Increments the integer value of a field in a hash by a number. Uses 0 as initial value if the field doesn't exist.</summary>\n\n**Syntax:** `HINCRBY key field increment`\n\n**Description:** Increments the integer value of a field in a hash by a number. Uses 0 as initial value if the field doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hincrbyfloat/\">HINCRBYFLOAT</a></strong> - Increments the floating point value of a field by a number. Uses 0 as initial value if the field doesn't exist.</summary>\n\n**Syntax:** `HINCRBYFLOAT key field increment`\n\n**Description:** Increments the floating point value of a field by a number. Uses 0 as initial value if the field doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hkeys/\">HKEYS</a></strong> - Returns all fields in a hash.</summary>\n\n**Syntax:** `HKEYS key`\n\n**Description:** Returns all fields in a hash.\n\n**Complexity:** O(N) where N is the size of the hash.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hlen/\">HLEN</a></strong> - Returns the number of fields in a hash.</summary>\n\n**Syntax:** `HLEN key`\n\n**Description:** Returns the number of fields in a hash.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hmget/\">HMGET</a></strong> - Returns the values of all fields in a hash.</summary>\n\n**Syntax:** `HMGET key field [field ...]`\n\n**Description:** Returns the values of all fields in a hash.\n\n**Complexity:** O(N) where N is the number of fields being requested.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hmset/\">HMSET</a></strong> - Sets the values of multiple fields.</summary>\n\n**Syntax:** `HMSET key field value [field value ...]`\n\n**Description:** Sets the values of multiple fields.\n\n**Complexity:** O(N) where N is the number of fields being set.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hpersist/\">HPERSIST</a></strong> - Removes the expiration time for each specified field</summary>\n\n**Syntax:** `HPERSIST key FIELDS numfields field [field ...]`\n\n**Description:** Removes the expiration time for each specified field\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hpexpire/\">HPEXPIRE</a></strong> - Set expiry for hash field using relative time to expire (milliseconds)</summary>\n\n**Syntax:** `HPEXPIRE key milliseconds [NX | XX | GT | LT] FIELDS numfields field\n  [field ...]`\n\n**Description:** Set expiry for hash field using relative time to expire (milliseconds)\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hpexpireat/\">HPEXPIREAT</a></strong> - Set expiry for hash field using an absolute Unix timestamp (milliseconds)</summary>\n\n**Syntax:** `HPEXPIREAT key unix-time-milliseconds [NX | XX | GT | LT]\n  FIELDS numfields field [field ...]`\n\n**Description:** Set expiry for hash field using an absolute Unix timestamp (milliseconds)\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hpexpiretime/\">HPEXPIRETIME</a></strong> - Returns the expiration time of a hash field as a Unix timestamp, in msec.</summary>\n\n**Syntax:** `HPEXPIRETIME key FIELDS numfields field [field ...]`\n\n**Description:** Returns the expiration time of a hash field as a Unix timestamp, in msec.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hpttl/\">HPTTL</a></strong> - Returns the TTL in milliseconds of a hash field.</summary>\n\n**Syntax:** `HPTTL key FIELDS numfields field [field ...]`\n\n**Description:** Returns the TTL in milliseconds of a hash field.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hrandfield/\">HRANDFIELD</a></strong> - Returns one or more random fields from a hash.</summary>\n\n**Syntax:** `HRANDFIELD key [count [WITHVALUES]]`\n\n**Description:** Returns one or more random fields from a hash.\n\n**Complexity:** O(N) where N is the number of fields returned\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hscan/\">HSCAN</a></strong> - Iterates over fields and values of a hash.</summary>\n\n**Syntax:** `HSCAN key cursor [MATCH pattern] [COUNT count] [NOVALUES]`\n\n**Description:** Iterates over fields and values of a hash.\n\n**Complexity:** O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hset/\">HSET</a></strong> - Creates or modifies the value of a field in a hash.</summary>\n\n**Syntax:** `HSET key field value [field value ...]`\n\n**Description:** Creates or modifies the value of a field in a hash.\n\n**Complexity:** O(1) for each field/value pair added, so O(N) to add N field/value pairs when the command is called with multiple field/value pairs.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hsetex/\">HSETEX</a></strong> - Set the value of one or more fields of a given hash key, and optionally set their expiration.</summary>\n\n**Syntax:** `HSETEX key [FNX | FXX] [EX seconds | PX milliseconds |\n  EXAT unix-time-seconds | PXAT unix-time-milliseconds | KEEPTTL]\n  FIELDS numfields field value [field value ...]`\n\n**Description:** Set the value of one or more fields of a given hash key, and optionally set their expiration.\n\n**Complexity:** O(N) where N is the number of fields being set.\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hsetnx/\">HSETNX</a></strong> - Sets the value of a field in a hash only when the field doesn't exist.</summary>\n\n**Syntax:** `HSETNX key field value`\n\n**Description:** Sets the value of a field in a hash only when the field doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hstrlen/\">HSTRLEN</a></strong> - Returns the length of the value of a field.</summary>\n\n**Syntax:** `HSTRLEN key field`\n\n**Description:** Returns the length of the value of a field.\n\n**Complexity:** O(1)\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/httl/\">HTTL</a></strong> - Returns the TTL in seconds of a hash field.</summary>\n\n**Syntax:** `HTTL key FIELDS numfields field [field ...]`\n\n**Description:** Returns the TTL in seconds of a hash field.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hvals/\">HVALS</a></strong> - Returns all values in a hash.</summary>\n\n**Syntax:** `HVALS key`\n\n**Description:** Returns all values in a hash.\n\n**Complexity:** O(N) where N is the size of the hash.\n\n**Since:** 2.0.0\n\n</details>"},{"id":"list-commands","title":"List commands","role":"content","text":"List commands operate on lists of strings, ordered by insertion order.\n\n<details>\n<summary><strong><a href=\"/commands/blmove/\">BLMOVE</a></strong> - Pops an element from a list, pushes it to another list and returns it. Blocks until an element is available otherwise. Deletes the list if the last element was moved.</summary>\n\n**Syntax:** `BLMOVE source destination <LEFT | RIGHT> <LEFT | RIGHT> timeout`\n\n**Description:** Pops an element from a list, pushes it to another list and returns it. Blocks until an element is available otherwise. Deletes the list if the last element was moved.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/blmovem/\">BLMOVEM</a></strong> - Moves up to (or exactly) a number of elements from one list to another and returns them. Blocks until the elements are available otherwise. Deletes the source list if it becomes empty. <span style=\"color: #e74c3c;\">⭐ New in 8.10</span></summary>\n\n**Syntax:** `BLMOVEM source destination <LEFT | RIGHT> <LEFT | RIGHT> timeout\n  [<COUNT count | EXACTLY exactly> <OBO | BULK>]`\n\n**Description:** Moves up to (or exactly) a number of elements from one list to another and returns them. Blocks until the elements are available otherwise. Deletes the source list if it becomes empty.\n\n**Complexity:** O(N) where N is the number of elements moved.\n\n**Since:** 8.10.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/blmpop/\">BLMPOP</a></strong> - Pops the first element from one of multiple lists. Blocks until an element is available otherwise. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `BLMPOP timeout numkeys key [key ...] <LEFT | RIGHT> [COUNT count]`\n\n**Description:** Pops the first element from one of multiple lists. Blocks until an element is available otherwise. Deletes the list if the last element was popped.\n\n**Complexity:** O(N+M) where N is the number of provided keys and M is the number of elements returned.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/blpop/\">BLPOP</a></strong> - Removes and returns the first element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `BLPOP key [key ...] timeout`\n\n**Description:** Removes and returns the first element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.\n\n**Complexity:** O(N) where N is the number of provided keys.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/brpop/\">BRPOP</a></strong> - Removes and returns the last element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `BRPOP key [key ...] timeout`\n\n**Description:** Removes and returns the last element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.\n\n**Complexity:** O(N) where N is the number of provided keys.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/brpoplpush/\">BRPOPLPUSH</a></strong> - Pops an element from a list, pushes it to another list and returns it. Block until an element is available otherwise. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `BRPOPLPUSH source destination timeout`\n\n**Description:** Pops an element from a list, pushes it to another list and returns it. Block until an element is available otherwise. Deletes the list if the last element was popped.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lindex/\">LINDEX</a></strong> - Returns an element from a list by its index.</summary>\n\n**Syntax:** `LINDEX key index`\n\n**Description:** Returns an element from a list by its index.\n\n**Complexity:** O(N) where N is the number of elements to traverse to get to the element at index. This makes asking for the first or the last element of the list O(1).\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/linsert/\">LINSERT</a></strong> - Inserts an element before or after another element in a list.</summary>\n\n**Syntax:** `LINSERT key <BEFORE | AFTER> pivot element`\n\n**Description:** Inserts an element before or after another element in a list.\n\n**Complexity:** O(N) where N is the number of elements to traverse before seeing the value pivot. This means that inserting somewhere on the left end on the list (head) can be considered O(1) and inserting somewhere on the right end (tail) is O(N).\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/llen/\">LLEN</a></strong> - Returns the length of a list.</summary>\n\n**Syntax:** `LLEN key`\n\n**Description:** Returns the length of a list.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lmove/\">LMOVE</a></strong> - Returns an element after popping it from one list and pushing it to another. Deletes the list if the last element was moved.</summary>\n\n**Syntax:** `LMOVE source destination <LEFT | RIGHT> <LEFT | RIGHT>`\n\n**Description:** Returns an element after popping it from one list and pushing it to another. Deletes the list if the last element was moved.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lmovem/\">LMOVEM</a></strong> - Moves up to (or exactly) a number of elements from one list to another and returns them. Deletes the source list if it becomes empty. <span style=\"color: #e74c3c;\">⭐ New in 8.10</span></summary>\n\n**Syntax:** `LMOVEM source destination <LEFT | RIGHT> <LEFT | RIGHT>\n  [<COUNT count | EXACTLY exactly> <OBO | BULK>]`\n\n**Description:** Moves up to (or exactly) a number of elements from one list to another and returns them. Deletes the source list if it becomes empty.\n\n**Complexity:** O(N) where N is the number of elements moved.\n\n**Since:** 8.10.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lmpop/\">LMPOP</a></strong> - Returns multiple elements from a list after removing them. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `LMPOP numkeys key [key ...] <LEFT | RIGHT> [COUNT count]`\n\n**Description:** Returns multiple elements from a list after removing them. Deletes the list if the last element was popped.\n\n**Complexity:** O(N+M) where N is the number of provided keys and M is the number of elements returned.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lpop/\">LPOP</a></strong> - Returns the first elements in a list after removing it. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `LPOP key [count]`\n\n**Description:** Returns the first elements in a list after removing it. Deletes the list if the last element was popped.\n\n**Complexity:** O(N) where N is the number of elements returned\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lpos/\">LPOS</a></strong> - Returns the index of matching elements in a list.</summary>\n\n**Syntax:** `LPOS key element [RANK rank] [COUNT num-matches] [MAXLEN len]`\n\n**Description:** Returns the index of matching elements in a list.\n\n**Complexity:** O(N) where N is the number of elements in the list, for the average case. When searching for elements near the head or the tail of the list, or when the MAXLEN option is provided, the command may run in constant time.\n\n**Since:** 6.0.6\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lpush/\">LPUSH</a></strong> - Prepends one or more elements to a list. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `LPUSH key element [element ...]`\n\n**Description:** Prepends one or more elements to a list. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lpushx/\">LPUSHX</a></strong> - Prepends one or more elements to a list only when the list exists.</summary>\n\n**Syntax:** `LPUSHX key element [element ...]`\n\n**Description:** Prepends one or more elements to a list only when the list exists.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lrange/\">LRANGE</a></strong> - Returns a range of elements from a list.</summary>\n\n**Syntax:** `LRANGE key start stop`\n\n**Description:** Returns a range of elements from a list.\n\n**Complexity:** O(S+N) where S is the distance of start offset from HEAD for small lists, from nearest end (HEAD or TAIL) for large lists; and N is the number of elements in the specified range.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lrem/\">LREM</a></strong> - Removes elements from a list. Deletes the list if the last element was removed.</summary>\n\n**Syntax:** `LREM key count element`\n\n**Description:** Removes elements from a list. Deletes the list if the last element was removed.\n\n**Complexity:** O(N+M) where N is the length of the list and M is the number of elements removed.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lset/\">LSET</a></strong> - Sets the value of an element in a list by its index.</summary>\n\n**Syntax:** `LSET key index element`\n\n**Description:** Sets the value of an element in a list by its index.\n\n**Complexity:** O(N) where N is the length of the list. Setting either the first or the last element of the list is O(1).\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ltrim/\">LTRIM</a></strong> - Removes elements from both ends a list. Deletes the list if all elements were trimmed.</summary>\n\n**Syntax:** `LTRIM key start stop`\n\n**Description:** Removes elements from both ends a list. Deletes the list if all elements were trimmed.\n\n**Complexity:** O(N) where N is the number of elements to be removed by the operation.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rpop/\">RPOP</a></strong> - Returns and removes the last elements of a list. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `RPOP key [count]`\n\n**Description:** Returns and removes the last elements of a list. Deletes the list if the last element was popped.\n\n**Complexity:** O(N) where N is the number of elements returned\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rpoplpush/\">RPOPLPUSH</a></strong> - Returns the last element of a list after removing and pushing it to another list. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `RPOPLPUSH source destination`\n\n**Description:** Returns the last element of a list after removing and pushing it to another list. Deletes the list if the last element was popped.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rpush/\">RPUSH</a></strong> - Appends one or more elements to a list. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `RPUSH key element [element ...]`\n\n**Description:** Appends one or more elements to a list. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rpushx/\">RPUSHX</a></strong> - Appends an element to a list only when the list exists.</summary>\n\n**Syntax:** `RPUSHX key element [element ...]`\n\n**Description:** Appends an element to a list only when the list exists.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 2.2.0\n\n</details>"},{"id":"set-commands","title":"Set commands","role":"content","text":"Set commands operate on unordered collections of unique strings.\n\n<details>\n<summary><strong><a href=\"/commands/sadd/\">SADD</a></strong> - Adds one or more members to a set. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `SADD key member [member ...]`\n\n**Description:** Adds one or more members to a set. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/scard/\">SCARD</a></strong> - Returns the number of members in a set.</summary>\n\n**Syntax:** `SCARD key`\n\n**Description:** Returns the number of members in a set.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sdiff/\">SDIFF</a></strong> - Returns the difference of multiple sets.</summary>\n\n**Syntax:** `SDIFF key [key ...]`\n\n**Description:** Returns the difference of multiple sets.\n\n**Complexity:** O(N) where N is the total number of elements in all given sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sdiffcard/\">SDIFFCARD</a></strong> - Returns the number of members of the difference between the first set and all successive sets. <span style=\"color: #e74c3c;\">⭐ New in 8.10</span></summary>\n\n**Syntax:** `SDIFFCARD numkeys key [key ...] [LIMIT limit]`\n\n**Description:** Returns the number of members of the difference between the first set and all successive sets.\n\n**Complexity:** O(N) where N is the total number of elements in all given sets.\n\n**Since:** 8.10.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sdiffstore/\">SDIFFSTORE</a></strong> - Stores the difference of multiple sets in a key.</summary>\n\n**Syntax:** `SDIFFSTORE destination key [key ...]`\n\n**Description:** Stores the difference of multiple sets in a key.\n\n**Complexity:** O(N) where N is the total number of elements in all given sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sinter/\">SINTER</a></strong> - Returns the intersect of multiple sets.</summary>\n\n**Syntax:** `SINTER key [key ...]`\n\n**Description:** Returns the intersect of multiple sets.\n\n**Complexity:** O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sintercard/\">SINTERCARD</a></strong> - Returns the number of members of the intersect of multiple sets.</summary>\n\n**Syntax:** `SINTERCARD numkeys key [key ...] [LIMIT limit]`\n\n**Description:** Returns the number of members of the intersect of multiple sets.\n\n**Complexity:** O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sinterstore/\">SINTERSTORE</a></strong> - Stores the intersect of multiple sets in a key.</summary>\n\n**Syntax:** `SINTERSTORE destination key [key ...]`\n\n**Description:** Stores the intersect of multiple sets in a key.\n\n**Complexity:** O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sismember/\">SISMEMBER</a></strong> - Determines whether a member belongs to a set.</summary>\n\n**Syntax:** `SISMEMBER key member`\n\n**Description:** Determines whether a member belongs to a set.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/smembers/\">SMEMBERS</a></strong> - Returns all members of a set.</summary>\n\n**Syntax:** `SMEMBERS key`\n\n**Description:** Returns all members of a set.\n\n**Complexity:** O(N) where N is the set cardinality.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/smismember/\">SMISMEMBER</a></strong> - Determines whether multiple members belong to a set.</summary>\n\n**Syntax:** `SMISMEMBER key member [member ...]`\n\n**Description:** Determines whether multiple members belong to a set.\n\n**Complexity:** O(N) where N is the number of elements being checked for membership\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/smove/\">SMOVE</a></strong> - Moves a member from one set to another.</summary>\n\n**Syntax:** `SMOVE source destination member`\n\n**Description:** Moves a member from one set to another.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/spop/\">SPOP</a></strong> - Returns one or more random members from a set after removing them. Deletes the set if the last member was popped.</summary>\n\n**Syntax:** `SPOP key [count]`\n\n**Description:** Returns one or more random members from a set after removing them. Deletes the set if the last member was popped.\n\n**Complexity:** Without the count argument O(1), otherwise O(N) where N is the value of the passed count.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/srandmember/\">SRANDMEMBER</a></strong> - Get one or multiple random members from a set</summary>\n\n**Syntax:** `SRANDMEMBER key [count]`\n\n**Description:** Get one or multiple random members from a set\n\n**Complexity:** Without the count argument O(1), otherwise O(N) where N is the absolute value of the passed count.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/srem/\">SREM</a></strong> - Removes one or more members from a set. Deletes the set if the last member was removed.</summary>\n\n**Syntax:** `SREM key member [member ...]`\n\n**Description:** Removes one or more members from a set. Deletes the set if the last member was removed.\n\n**Complexity:** O(N) where N is the number of members to be removed.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sscan/\">SSCAN</a></strong> - Iterates over members of a set.</summary>\n\n**Syntax:** `SSCAN key cursor [MATCH pattern] [COUNT count]`\n\n**Description:** Iterates over members of a set.\n\n**Complexity:** O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sunion/\">SUNION</a></strong> - Returns the union of multiple sets.</summary>\n\n**Syntax:** `SUNION key [key ...]`\n\n**Description:** Returns the union of multiple sets.\n\n**Complexity:** O(N) where N is the total number of elements in all given sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sunioncard/\">SUNIONCARD</a></strong> - Returns the number of members of the union of multiple sets. <span style=\"color: #e74c3c;\">⭐ New in 8.10</span></summary>\n\n**Syntax:** `SUNIONCARD numkeys key [key ...] [APPROX] [LIMIT limit]`\n\n**Description:** Returns the number of members of the union of multiple sets.\n\n**Complexity:** O(N) where N is the total number of elements in all given sets.\n\n**Since:** 8.10.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sunionstore/\">SUNIONSTORE</a></strong> - Stores the union of multiple sets in a key.</summary>\n\n**Syntax:** `SUNIONSTORE destination key [key ...]`\n\n**Description:** Stores the union of multiple sets in a key.\n\n**Complexity:** O(N) where N is the total number of elements in all given sets.\n\n**Since:** 1.0.0\n\n</details>"},{"id":"sorted-set-commands","title":"Sorted set commands","role":"content","text":"Sorted set commands operate on sets of unique strings ordered by a score.\n\n<details>\n<summary><strong><a href=\"/commands/bzmpop/\">BZMPOP</a></strong> - Removes and returns a member by score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.</summary>\n\n**Syntax:** `BZMPOP timeout numkeys key [key ...] <MIN | MAX> [COUNT count]`\n\n**Description:** Removes and returns a member by score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.\n\n**Complexity:** O(K) + O(M*log(N)) where K is the number of provided keys, N being the number of elements in the sorted set, and M being the number of elements popped.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bzpopmax/\">BZPOPMAX</a></strong> - Removes and returns the member with the highest score from one or more sorted sets. Blocks until a member available otherwise.  Deletes the sorted set if the last element was popped.</summary>\n\n**Syntax:** `BZPOPMAX key [key ...] timeout`\n\n**Description:** Removes and returns the member with the highest score from one or more sorted sets. Blocks until a member available otherwise.  Deletes the sorted set if the last element was popped.\n\n**Complexity:** O(log(N)) with N being the number of elements in the sorted set.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bzpopmin/\">BZPOPMIN</a></strong> - Removes and returns the member with the lowest score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.</summary>\n\n**Syntax:** `BZPOPMIN key [key ...] timeout`\n\n**Description:** Removes and returns the member with the lowest score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.\n\n**Complexity:** O(log(N)) with N being the number of elements in the sorted set.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zadd/\">ZADD</a></strong> - Adds one or more members to a sorted set, or updates their scores. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `ZADD key [NX | XX] [GT | LT] [CH] [INCR] score member [score member\n  ...]`\n\n**Description:** Adds one or more members to a sorted set, or updates their scores. Creates the key if it doesn't exist.\n\n**Complexity:** O(log(N)) for each item added, where N is the number of elements in the sorted set.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zcard/\">ZCARD</a></strong> - Returns the number of members in a sorted set.</summary>\n\n**Syntax:** `ZCARD key`\n\n**Description:** Returns the number of members in a sorted set.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zcount/\">ZCOUNT</a></strong> - Returns the count of members in a sorted set that have scores within a range.</summary>\n\n**Syntax:** `ZCOUNT key min max`\n\n**Description:** Returns the count of members in a sorted set that have scores within a range.\n\n**Complexity:** O(log(N)) with N being the number of elements in the sorted set.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zdiff/\">ZDIFF</a></strong> - Returns the difference between multiple sorted sets.</summary>\n\n**Syntax:** `ZDIFF numkeys key [key ...] [WITHSCORES]`\n\n**Description:** Returns the difference between multiple sorted sets.\n\n**Complexity:** O(L + (N-K)log(N)) worst case where L is the total number of elements in all the sets, N is the size of the first set, and K is the size of the result set.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zdiffstore/\">ZDIFFSTORE</a></strong> - Stores the difference of multiple sorted sets in a key.</summary>\n\n**Syntax:** `ZDIFFSTORE destination numkeys key [key ...]`\n\n**Description:** Stores the difference of multiple sorted sets in a key.\n\n**Complexity:** O(L + (N-K)log(N)) worst case where L is the total number of elements in all the sets, N is the size of the first set, and K is the size of the result set.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zincrby/\">ZINCRBY</a></strong> - Increments the score of a member in a sorted set.</summary>\n\n**Syntax:** `ZINCRBY key increment member`\n\n**Description:** Increments the score of a member in a sorted set.\n\n**Complexity:** O(log(N)) where N is the number of elements in the sorted set.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zinter/\">ZINTER</a></strong> - Returns the intersect of multiple sorted sets.</summary>\n\n**Syntax:** `ZINTER numkeys key [key ...] [WEIGHTS weight [weight ...]]\n  [AGGREGATE <SUM | MIN | MAX | COUNT>] [WITHSCORES]`\n\n**Description:** Returns the intersect of multiple sorted sets.\n\n**Complexity:** O(N*K)+O(M*log(M)) worst case with N being the smallest input sorted set, K being the number of input sorted sets and M being the number of elements in the resulting sorted set.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zintercard/\">ZINTERCARD</a></strong> - Returns the number of members of the intersect of multiple sorted sets.</summary>\n\n**Syntax:** `ZINTERCARD numkeys key [key ...] [LIMIT limit]`\n\n**Description:** Returns the number of members of the intersect of multiple sorted sets.\n\n**Complexity:** O(N*K) worst case with N being the smallest input sorted set, K being the number of input sorted sets.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zinterstore/\">ZINTERSTORE</a></strong> - Stores the intersect of multiple sorted sets in a key.</summary>\n\n**Syntax:** `ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight\n  [weight ...]] [AGGREGATE <SUM | MIN | MAX | COUNT>]`\n\n**Description:** Stores the intersect of multiple sorted sets in a key.\n\n**Complexity:** O(N*K)+O(M*log(M)) worst case with N being the smallest input sorted set, K being the number of input sorted sets and M being the number of elements in the resulting sorted set.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zlexcount/\">ZLEXCOUNT</a></strong> - Returns the number of members in a sorted set within a lexicographical range.</summary>\n\n**Syntax:** `ZLEXCOUNT key min max`\n\n**Description:** Returns the number of members in a sorted set within a lexicographical range.\n\n**Complexity:** O(log(N)) with N being the number of elements in the sorted set.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zmpop/\">ZMPOP</a></strong> - Returns the highest- or lowest-scoring members from one or more sorted sets after removing them. Deletes the sorted set if the last member was popped.</summary>\n\n**Syntax:** `ZMPOP numkeys key [key ...] <MIN | MAX> [COUNT count]`\n\n**Description:** Returns the highest- or lowest-scoring members from one or more sorted sets after removing them. Deletes the sorted set if the last member was popped.\n\n**Complexity:** O(K) + O(M*log(N)) where K is the number of provided keys, N being the number of elements in the sorted set, and M being the number of elements popped.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zmscore/\">ZMSCORE</a></strong> - Returns the score of one or more members in a sorted set.</summary>\n\n**Syntax:** `ZMSCORE key member [member ...]`\n\n**Description:** Returns the score of one or more members in a sorted set.\n\n**Complexity:** O(N) where N is the number of members being requested.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zpopmax/\">ZPOPMAX</a></strong> - Returns the highest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.</summary>\n\n**Syntax:** `ZPOPMAX key [count]`\n\n**Description:** Returns the highest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.\n\n**Complexity:** O(log(N)*M) with N being the number of elements in the sorted set, and M being the number of elements popped.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zpopmin/\">ZPOPMIN</a></strong> - Returns the lowest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.</summary>\n\n**Syntax:** `ZPOPMIN key [count]`\n\n**Description:** Returns the lowest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.\n\n**Complexity:** O(log(N)*M) with N being the number of elements in the sorted set, and M being the number of elements popped.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrandmember/\">ZRANDMEMBER</a></strong> - Returns one or more random members from a sorted set.</summary>\n\n**Syntax:** `ZRANDMEMBER key [count [WITHSCORES]]`\n\n**Description:** Returns one or more random members from a sorted set.\n\n**Complexity:** O(N) where N is the number of members returned\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrange/\">ZRANGE</a></strong> - Returns members in a sorted set within a range of indexes.</summary>\n\n**Syntax:** `ZRANGE key start stop [BYSCORE | BYLEX] [REV] [LIMIT offset count]\n  [WITHSCORES]`\n\n**Description:** Returns members in a sorted set within a range of indexes.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements returned.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrangebylex/\">ZRANGEBYLEX</a></strong> - Returns members in a sorted set within a lexicographical range.</summary>\n\n**Syntax:** `ZRANGEBYLEX key min max [LIMIT offset count]`\n\n**Description:** Returns members in a sorted set within a lexicographical range.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrangebyscore/\">ZRANGEBYSCORE</a></strong> - Returns members in a sorted set within a range of scores.</summary>\n\n**Syntax:** `ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]`\n\n**Description:** Returns members in a sorted set within a range of scores.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).\n\n**Since:** 1.0.5\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrangestore/\">ZRANGESTORE</a></strong> - Stores a range of members from sorted set in a key.</summary>\n\n**Syntax:** `ZRANGESTORE dst src min max [BYSCORE | BYLEX] [REV] [LIMIT offset\n  count]`\n\n**Description:** Stores a range of members from sorted set in a key.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements stored into the destination key.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrank/\">ZRANK</a></strong> - Returns the index of a member in a sorted set ordered by ascending scores.</summary>\n\n**Syntax:** `ZRANK key member [WITHSCORE]`\n\n**Description:** Returns the index of a member in a sorted set ordered by ascending scores.\n\n**Complexity:** O(log(N))\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrem/\">ZREM</a></strong> - Removes one or more members from a sorted set. Deletes the sorted set if all members were removed.</summary>\n\n**Syntax:** `ZREM key member [member ...]`\n\n**Description:** Removes one or more members from a sorted set. Deletes the sorted set if all members were removed.\n\n**Complexity:** O(M*log(N)) with N being the number of elements in the sorted set and M the number of elements to be removed.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zremrangebylex/\">ZREMRANGEBYLEX</a></strong> - Removes members in a sorted set within a lexicographical range. Deletes the sorted set if all members were removed.</summary>\n\n**Syntax:** `ZREMRANGEBYLEX key min max`\n\n**Description:** Removes members in a sorted set within a lexicographical range. Deletes the sorted set if all members were removed.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zremrangebyrank/\">ZREMRANGEBYRANK</a></strong> - Removes members in a sorted set within a range of indexes. Deletes the sorted set if all members were removed.</summary>\n\n**Syntax:** `ZREMRANGEBYRANK key start stop`\n\n**Description:** Removes members in a sorted set within a range of indexes. Deletes the sorted set if all members were removed.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zremrangebyscore/\">ZREMRANGEBYSCORE</a></strong> - Removes members in a sorted set within a range of scores. Deletes the sorted set if all members were removed.</summary>\n\n**Syntax:** `ZREMRANGEBYSCORE key min max`\n\n**Description:** Removes members in a sorted set within a range of scores. Deletes the sorted set if all members were removed.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrevrange/\">ZREVRANGE</a></strong> - Returns members in a sorted set within a range of indexes in reverse order.</summary>\n\n**Syntax:** `ZREVRANGE key start stop [WITHSCORES]`\n\n**Description:** Returns members in a sorted set within a range of indexes in reverse order.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements returned.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrevrangebylex/\">ZREVRANGEBYLEX</a></strong> - Returns members in a sorted set within a lexicographical range in reverse order.</summary>\n\n**Syntax:** `ZREVRANGEBYLEX key max min [LIMIT offset count]`\n\n**Description:** Returns members in a sorted set within a lexicographical range in reverse order.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrevrangebyscore/\">ZREVRANGEBYSCORE</a></strong> - Returns members in a sorted set within a range of scores in reverse order.</summary>\n\n**Syntax:** `ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]`\n\n**Description:** Returns members in a sorted set within a range of scores in reverse order.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrevrank/\">ZREVRANK</a></strong> - Returns the index of a member in a sorted set ordered by descending scores.</summary>\n\n**Syntax:** `ZREVRANK key member [WITHSCORE]`\n\n**Description:** Returns the index of a member in a sorted set ordered by descending scores.\n\n**Complexity:** O(log(N))\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zscan/\">ZSCAN</a></strong> - Iterates over members and scores of a sorted set.</summary>\n\n**Syntax:** `ZSCAN key cursor [MATCH pattern] [COUNT count]`\n\n**Description:** Iterates over members and scores of a sorted set.\n\n**Complexity:** O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zscore/\">ZSCORE</a></strong> - Returns the score of a member in a sorted set.</summary>\n\n**Syntax:** `ZSCORE key member`\n\n**Description:** Returns the score of a member in a sorted set.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zunion/\">ZUNION</a></strong> - Returns the union of multiple sorted sets.</summary>\n\n**Syntax:** `ZUNION numkeys key [key ...] [WEIGHTS weight [weight ...]]\n  [AGGREGATE <SUM | MIN | MAX | COUNT>] [WITHSCORES]`\n\n**Description:** Returns the union of multiple sorted sets.\n\n**Complexity:** O(N)+O(M*log(M)) with N being the sum of the sizes of the input sorted sets, and M being the number of elements in the resulting sorted set.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zunionstore/\">ZUNIONSTORE</a></strong> - Stores the union of multiple sorted sets in a key.</summary>\n\n**Syntax:** `ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight\n  [weight ...]] [AGGREGATE <SUM | MIN | MAX | COUNT>]`\n\n**Description:** Stores the union of multiple sorted sets in a key.\n\n**Complexity:** O(N)+O(M log(M)) with N being the sum of the sizes of the input sorted sets, and M being the number of elements in the resulting sorted set.\n\n**Since:** 2.0.0\n\n</details>"},{"id":"stream-commands","title":"Stream commands","role":"content","text":"Stream commands operate on append-only log data structures.\n\n<details>\n<summary><strong><a href=\"/commands/xack/\">XACK</a></strong> - Returns the number of messages that were successfully acknowledged by the consumer group member of a stream.</summary>\n\n**Syntax:** `XACK key group id [id ...]`\n\n**Description:** Returns the number of messages that were successfully acknowledged by the consumer group member of a stream.\n\n**Complexity:** O(1) for each message ID processed.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xackdel/\">XACKDEL</a></strong> - Acknowledges and conditionally deletes one or multiple entries for a stream consumer group.</summary>\n\n**Syntax:** `XACKDEL key group [KEEPREF | DELREF | ACKED] IDS numids id [id ...]`\n\n**Description:** Acknowledges and conditionally deletes one or multiple entries for a stream consumer group.\n\n**Complexity:** O(1) for each entry ID processed.\n\n**Since:** 8.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xadd/\">XADD</a></strong> - Appends a new message to a stream. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `XADD key [NOMKSTREAM] [KEEPREF | DELREF | ACKED]\n  [IDMPAUTO producer-id | IDMP producer-id idempotent-id]\n  [<MAXLEN | MINID> [= | ~] threshold [LIMIT count]] <* | id>\n  field value [field value ...]`\n\n**Description:** Appends a new message to a stream. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) when adding a new entry, O(N) when trimming where N being the number of entries evicted.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xautoclaim/\">XAUTOCLAIM</a></strong> - Changes, or acquires, ownership of messages in a consumer group, as if the messages were delivered to as consumer group member.</summary>\n\n**Syntax:** `XAUTOCLAIM key group consumer min-idle-time start [COUNT count]\n  [JUSTID]`\n\n**Description:** Changes, or acquires, ownership of messages in a consumer group, as if the messages were delivered to as consumer group member.\n\n**Complexity:** O(1) if COUNT is small.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xcfgset/\">XCFGSET</a></strong> - Sets the IDMP configuration parameters for a stream.</summary>\n\n**Syntax:** `XCFGSET key [IDMP-DURATION idmp-duration]\n  [IDMP-MAXSIZE idmp-maxsize]`\n\n**Description:** Sets the IDMP configuration parameters for a stream.\n\n**Complexity:** O(1)\n\n**Since:** 8.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xclaim/\">XCLAIM</a></strong> - Changes, or acquires, ownership of a message in a consumer group, as if the message was delivered a consumer group member.</summary>\n\n**Syntax:** `XCLAIM key group consumer min-idle-time id [id ...] [IDLE ms]\n  [TIME unix-time-milliseconds] [RETRYCOUNT count] [FORCE] [JUSTID]\n  [LASTID lastid]`\n\n**Description:** Changes, or acquires, ownership of a message in a consumer group, as if the message was delivered a consumer group member.\n\n**Complexity:** O(log N) with N being the number of messages in the PEL of the consumer group.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xdel/\">XDEL</a></strong> - Returns the number of messages after removing them from a stream.</summary>\n\n**Syntax:** `XDEL key id [id ...]`\n\n**Description:** Returns the number of messages after removing them from a stream.\n\n**Complexity:** O(1) for each single item to delete in the stream, regardless of the stream size.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xdelex/\">XDELEX</a></strong> - Deletes one or multiple entries from the stream.</summary>\n\n**Syntax:** `XDELEX key [KEEPREF | DELREF | ACKED] IDS numids id [id ...]`\n\n**Description:** Deletes one or multiple entries from the stream.\n\n**Complexity:** O(1) for each single item to delete in the stream, regardless of the stream size.\n\n**Since:** 8.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-create/\">XGROUP CREATE</a></strong> - Creates a consumer group.</summary>\n\n**Syntax:** `XGROUP CREATE key group <id | $> [MKSTREAM]\n  [ENTRIESREAD entries-read]`\n\n**Description:** Creates a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-createconsumer/\">XGROUP CREATECONSUMER</a></strong> - Creates a consumer in a consumer group.</summary>\n\n**Syntax:** `XGROUP CREATECONSUMER key group consumer`\n\n**Description:** Creates a consumer in a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-delconsumer/\">XGROUP DELCONSUMER</a></strong> - Deletes a consumer from a consumer group.</summary>\n\n**Syntax:** `XGROUP DELCONSUMER key group consumer`\n\n**Description:** Deletes a consumer from a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-destroy/\">XGROUP DESTROY</a></strong> - Destroys a consumer group.</summary>\n\n**Syntax:** `XGROUP DESTROY key group`\n\n**Description:** Destroys a consumer group.\n\n**Complexity:** O(N) where N is the number of entries in the group's pending entries list (PEL).\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-setid/\">XGROUP SETID</a></strong> - Sets the last-delivered ID of a consumer group.</summary>\n\n**Syntax:** `XGROUP SETID key group <id | $> [ENTRIESREAD entries-read]`\n\n**Description:** Sets the last-delivered ID of a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xidmprecord/\">XIDMPRECORD</a></strong> - An internal command for setting IDMP metadata on an existing stream message.</summary>\n\n**Syntax:** `XIDMPRECORD key pid iid stream-id`\n\n**Description:** An internal command for setting IDMP metadata on an existing stream message.\n\n**Complexity:** O(1)\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xinfo-consumers/\">XINFO CONSUMERS</a></strong> - Returns a list of the consumers in a consumer group.</summary>\n\n**Syntax:** `XINFO CONSUMERS key group`\n\n**Description:** Returns a list of the consumers in a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xinfo-groups/\">XINFO GROUPS</a></strong> - Returns a list of the consumer groups of a stream.</summary>\n\n**Syntax:** `XINFO GROUPS key`\n\n**Description:** Returns a list of the consumer groups of a stream.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xinfo-stream/\">XINFO STREAM</a></strong> - Returns information about a stream.</summary>\n\n**Syntax:** `XINFO STREAM key [FULL [COUNT count]]`\n\n**Description:** Returns information about a stream.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xlen/\">XLEN</a></strong> - Return the number of messages in a stream.</summary>\n\n**Syntax:** `XLEN key`\n\n**Description:** Return the number of messages in a stream.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xnack/\">XNACK</a></strong> - Releases pending messages back to the group's PEL without acknowledging them, making them available for re-delivery.</summary>\n\n**Syntax:** `XNACK key group <SILENT | FAIL | FATAL> IDS numids id [id ...]\n  [RETRYCOUNT count] [FORCE]`\n\n**Description:** Releases pending messages back to the group's PEL without acknowledging them, making them available for re-delivery.\n\n**Complexity:** O(1) for each message ID processed.\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xpending/\">XPENDING</a></strong> - Returns the information and entries from a stream consumer group's pending entries list.</summary>\n\n**Syntax:** `XPENDING key group [[IDLE min-idle-time] start end count [consumer]]`\n\n**Description:** Returns the information and entries from a stream consumer group's pending entries list.\n\n**Complexity:** O(N) with N being the number of elements returned, so asking for a small fixed number of entries per call is O(1). O(M), where M is the total number of entries scanned when used with the IDLE filter. When the command returns just the summary and the list of consumers is small, it runs in O(1) time; otherwise, an additional O(N) time for iterating every consumer.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xrange/\">XRANGE</a></strong> - Returns the messages from a stream within a range of IDs.</summary>\n\n**Syntax:** `XRANGE key start end [COUNT count]`\n\n**Description:** Returns the messages from a stream within a range of IDs.\n\n**Complexity:** O(N) with N being the number of elements being returned. If N is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1).\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xread/\">XREAD</a></strong> - Returns messages from multiple streams with IDs greater than the ones requested. Blocks until a message is available otherwise.</summary>\n\n**Syntax:** `XREAD [COUNT count] [MAXCOUNT maxcount] [MAXSIZE maxsize]\n  [BLOCK milliseconds] STREAMS key [key ...] id [id ...]`\n\n**Description:** Returns messages from multiple streams with IDs greater than the ones requested. Blocks until a message is available otherwise.\n\n**Complexity:** For each stream mentioned: O(M) with M being the number of elements returned. If M is constant (for example, always asking for the first 10 elements with COUNT), you can consider it O(1). On the other side when XREADGROUP blocks, XADD will pay the O(N) time in order to serve the N clients blocked on the stream getting new data.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xreadgroup/\">XREADGROUP</a></strong> - Returns new or historical messages from a stream for a consumer in a group. Blocks until a message is available otherwise.</summary>\n\n**Syntax:** `XREADGROUP GROUP group consumer [COUNT count] [MAXCOUNT maxcount]\n  [MAXSIZE maxsize] [BLOCK milliseconds] [CLAIM min-idle-time]\n  [NOACK] STREAMS key [key ...] id [id ...]`\n\n**Description:** Returns new or historical messages from a stream for a consumer in a group. Blocks until a message is available otherwise.\n\n**Complexity:** For each stream mentioned: O(M) with M being the number of elements returned. If M is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1). On the other side when XREADGROUP blocks, XADD will pay the O(N) time in order to serve the N clients blocked on the stream getting new data.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xrevrange/\">XREVRANGE</a></strong> - Returns the messages from a stream within a range of IDs in reverse order.</summary>\n\n**Syntax:** `XREVRANGE key end start [COUNT count]`\n\n**Description:** Returns the messages from a stream within a range of IDs in reverse order.\n\n**Complexity:** O(N) with N being the number of elements returned. If N is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1).\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xsetid/\">XSETID</a></strong> - An internal command for replicating stream values.</summary>\n\n**Syntax:** `XSETID key last-id [ENTRIESADDED entries-added]\n  [MAXDELETEDID max-deleted-id]`\n\n**Description:** An internal command for replicating stream values.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xtrim/\">XTRIM</a></strong> - Deletes messages from the beginning of a stream.</summary>\n\n**Syntax:** `XTRIM key <MAXLEN | MINID> [= | ~] threshold [LIMIT count] [KEEPREF | DELREF | ACKED]`\n\n**Description:** Deletes messages from the beginning of a stream.\n\n**Complexity:** O(N), with N being the number of evicted entries. Constant times are very small however, since entries are organized in macro nodes containing multiple entries that can be released with a single deallocation.\n\n**Since:** 5.0.0\n\n</details>"},{"id":"bitmap-commands","title":"Bitmap commands","role":"content","text":"Bitmap commands operate on strings as arrays of bits.\n\n<details>\n<summary><strong><a href=\"/commands/bitcount/\">BITCOUNT</a></strong> - Counts the number of set bits (population counting) in a string.</summary>\n\n**Syntax:** `BITCOUNT key [start end [BYTE | BIT]]`\n\n**Description:** Counts the number of set bits (population counting) in a string.\n\n**Complexity:** O(N)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bitfield/\">BITFIELD</a></strong> - Performs arbitrary bitfield integer operations on strings.</summary>\n\n**Syntax:** `BITFIELD key [GET encoding offset | [OVERFLOW <WRAP | SAT | FAIL>]\n  <SET encoding offset value | INCRBY encoding offset increment>\n  [GET encoding offset | [OVERFLOW <WRAP | SAT | FAIL>]\n  <SET encoding offset value | INCRBY encoding offset increment>\n  ...]]`\n\n**Description:** Performs arbitrary bitfield integer operations on strings.\n\n**Complexity:** O(1) for each subcommand specified\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bitfield_ro/\">BITFIELD_RO</a></strong> - Performs arbitrary read-only bitfield integer operations on strings.</summary>\n\n**Syntax:** `BITFIELD RO key [GET encoding offset [GET encoding offset ...]]`\n\n**Description:** Performs arbitrary read-only bitfield integer operations on strings.\n\n**Complexity:** O(1) for each subcommand specified\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bitop/\">BITOP</a></strong> - Performs bitwise operations on multiple strings, and stores the result.</summary>\n\n**Syntax:** `BITOP <AND | OR | XOR | NOT | DIFF | DIFF1 | ANDOR | ONE> destkey key [key ...]`\n\n**Description:** Performs bitwise operations on multiple strings, and stores the result.\n\n**Complexity:** O(N)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bitpos/\">BITPOS</a></strong> - Finds the first set (1) or clear (0) bit in a string.</summary>\n\n**Syntax:** `BITPOS key bit [start [end [BYTE | BIT]]]`\n\n**Description:** Finds the first set (1) or clear (0) bit in a string.\n\n**Complexity:** O(N)\n\n**Since:** 2.8.7\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getbit/\">GETBIT</a></strong> - Returns a bit value by offset.</summary>\n\n**Syntax:** `GETBIT key offset`\n\n**Description:** Returns a bit value by offset.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/setbit/\">SETBIT</a></strong> - Sets or clears the bit at offset of the string value. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `SETBIT key offset value`\n\n**Description:** Sets or clears the bit at offset of the string value. Creates the key if it doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>"},{"id":"array-commands","title":"Array commands","role":"content","text":"Array commands operate on arrays of strings.\n\n<details>\n<summary><strong><a href=\"/commands/arcount/\">ARCOUNT</a></strong> - Returns the number of non-empty elements in an array.</summary>\n\n**Syntax:** `ARCOUNT key`\n\n**Description:** Returns the number of non-empty elements in an array.\n\n**Complexity:** O(1)\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ardel/\">ARDEL</a></strong> - Deletes elements at the specified indices in an array.</summary>\n\n**Syntax:** `ARDEL key index [index ...]`\n\n**Description:** Deletes elements at the specified indices in an array.\n\n**Complexity:** O(N) where N is the number of indices to delete\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ardelrange/\">ARDELRANGE</a></strong> - Deletes elements in one or more ranges.</summary>\n\n**Syntax:** `ARDELRANGE key start end [start end ...]`\n\n**Description:** Deletes elements in one or more ranges.\n\n**Complexity:** Proportional to the number of existing elements / slices touched, not to the numeric span of the requested ranges\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/arget/\">ARGET</a></strong> - Gets the value at an index in an array.</summary>\n\n**Syntax:** `ARGET key index`\n\n**Description:** Gets the value at an index in an array.\n\n**Complexity:** O(1)\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/argetrange/\">ARGETRANGE</a></strong> - Gets values in a range of indices.</summary>\n\n**Syntax:** `ARGETRANGE key start end`\n\n**Description:** Gets values in a range of indices.\n\n**Complexity:** O(N) where N is the range length\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/argrep/\">ARGREP</a></strong> - Searches array elements in a range using textual predicates.</summary>\n\n**Syntax:** `ARGREP key start end\n  <EXACT string | MATCH string | GLOB pattern | RE pattern [...]>\n  [AND | OR | LIMIT limit | WITHVALUES | NOCASE [...]]`\n\n**Description:** Searches array elements in a range using textual predicates.\n\n**Complexity:** O(P * C) where P is the number of visited positions in touched slices and C is the cost of evaluating the predicates on one existing element.\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/arinfo/\">ARINFO</a></strong> - Returns metadata about an array.</summary>\n\n**Syntax:** `ARINFO key [FULL]`\n\n**Description:** Returns metadata about an array.\n\n**Complexity:** O(1), or O(N) with FULL option where N is the number of slices.\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/arinsert/\">ARINSERT</a></strong> - Inserts one or more values at consecutive indices.</summary>\n\n**Syntax:** `ARINSERT key value [value ...]`\n\n**Description:** Inserts one or more values at consecutive indices.\n\n**Complexity:** O(N) where N is the number of values\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/arlastitems/\">ARLASTITEMS</a></strong> - Returns the most recently inserted elements.</summary>\n\n**Syntax:** `ARLASTITEMS key count [REV]`\n\n**Description:** Returns the most recently inserted elements.\n\n**Complexity:** O(N) where N is the count\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/arlen/\">ARLEN</a></strong> - Returns the length of an array (max index + 1).</summary>\n\n**Syntax:** `ARLEN key`\n\n**Description:** Returns the length of an array (max index + 1).\n\n**Complexity:** O(1)\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/armget/\">ARMGET</a></strong> - Gets values at multiple indices in an array.</summary>\n\n**Syntax:** `ARMGET key index [index ...]`\n\n**Description:** Gets values at multiple indices in an array.\n\n**Complexity:** O(N) where N is the number of indices\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/armset/\">ARMSET</a></strong> - Sets multiple index-value pairs in an array.</summary>\n\n**Syntax:** `ARMSET key index value [index value ...]`\n\n**Description:** Sets multiple index-value pairs in an array.\n\n**Complexity:** O(N) where N is the number of pairs\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/arnext/\">ARNEXT</a></strong> - Returns the next index ARINSERT would use.</summary>\n\n**Syntax:** `ARNEXT key`\n\n**Description:** Returns the next index ARINSERT would use.\n\n**Complexity:** O(1)\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/arop/\">AROP</a></strong> - Performs aggregate operations on array elements in a range.</summary>\n\n**Syntax:** `AROP key start end <SUM | MIN | MAX | AND | OR | XOR | MATCH value |\n  USED>`\n\n**Description:** Performs aggregate operations on array elements in a range.\n\n**Complexity:** O(P) where P is visited positions in touched slices (dense scanned slots + sparse entries), with worst-case O(|end-start|+1) and typical case close to O(N), where N is the number of existing elements in range.\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/arring/\">ARRING</a></strong> - Inserts values into a ring buffer of specified size, wrapping and truncating as needed.</summary>\n\n**Syntax:** `ARRING key size value [value ...]`\n\n**Description:** Inserts values into a ring buffer of specified size, wrapping and truncating as needed.\n\n**Complexity:** O(M) normally, O(N+M) on ring resize, where N is the maximum of the old and new ring size and M is the number of inserted values\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/arscan/\">ARSCAN</a></strong> - Iterates existing elements in a range, returning index-value pairs.</summary>\n\n**Syntax:** `ARSCAN key start end [LIMIT limit]`\n\n**Description:** Iterates existing elements in a range, returning index-value pairs.\n\n**Complexity:** O(P) where P is visited positions in touched slices (dense scanned slots + sparse entries), with worst-case O(|end-start|+1) and typical case close to O(N), where N is the number of existing elements in range.\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/arseek/\">ARSEEK</a></strong> - Sets the ARINSERT / ARRING cursor to a specific index.</summary>\n\n**Syntax:** `ARSEEK key index`\n\n**Description:** Sets the ARINSERT / ARRING cursor to a specific index.\n\n**Complexity:** O(1)\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/arset/\">ARSET</a></strong> - Sets one or more contiguous values starting at an index in an array.</summary>\n\n**Syntax:** `ARSET key index value [value ...]`\n\n**Description:** Sets one or more contiguous values starting at an index in an array.\n\n**Complexity:** O(N) where N is the number of values\n\n**Since:** 8.8.0\n\n</details>"},{"id":"hyperloglog-commands","title":"HyperLogLog commands","role":"content","text":"HyperLogLog commands provide probabilistic cardinality estimation.\n\n<details>\n<summary><strong><a href=\"/commands/pfadd/\">PFADD</a></strong> - Adds elements to a HyperLogLog key. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `PFADD key [element [element ...]]`\n\n**Description:** Adds elements to a HyperLogLog key. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) to add every element.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pfcount/\">PFCOUNT</a></strong> - Returns the approximated cardinality of the set(s) observed by the HyperLogLog key(s).</summary>\n\n**Syntax:** `PFCOUNT key [key ...]`\n\n**Description:** Returns the approximated cardinality of the set(s) observed by the HyperLogLog key(s).\n\n**Complexity:** O(1) with a very small average constant time when called with a single key. O(N) with N being the number of keys, and much bigger constant times, when called with multiple keys.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pfdebug/\">PFDEBUG</a></strong> - Internal commands for debugging HyperLogLog values.</summary>\n\n**Syntax:** `PFDEBUG subcommand key`\n\n**Description:** Internal commands for debugging HyperLogLog values.\n\n**Complexity:** N/A\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pfmerge/\">PFMERGE</a></strong> - Merges one or more HyperLogLog values into a single key.</summary>\n\n**Syntax:** `PFMERGE destkey [sourcekey [sourcekey ...]]`\n\n**Description:** Merges one or more HyperLogLog values into a single key.\n\n**Complexity:** O(N) to merge N HyperLogLogs, but with high constant times.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pfselftest/\">PFSELFTEST</a></strong> - An internal command for testing HyperLogLog values.</summary>\n\n**Syntax:** `PFSELFTEST`\n\n**Description:** An internal command for testing HyperLogLog values.\n\n**Complexity:** N/A\n\n**Since:** 2.8.9\n\n</details>"},{"id":"geospatial-commands","title":"Geospatial commands","role":"content","text":"Geospatial commands operate on geographic coordinates.\n\n<details>\n<summary><strong><a href=\"/commands/geoadd/\">GEOADD</a></strong> - Adds one or more members to a geospatial index. The key is created if it doesn't exist.</summary>\n\n**Syntax:** `GEOADD key [NX | XX] [CH] longitude latitude member [longitude\n  latitude member ...]`\n\n**Description:** Adds one or more members to a geospatial index. The key is created if it doesn't exist.\n\n**Complexity:** O(log(N)) for each item added, where N is the number of elements in the sorted set.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geodist/\">GEODIST</a></strong> - Returns the distance between two members of a geospatial index.</summary>\n\n**Syntax:** `GEODIST key member1 member2 [M | KM | FT | MI]`\n\n**Description:** Returns the distance between two members of a geospatial index.\n\n**Complexity:** O(1)\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geohash/\">GEOHASH</a></strong> - Returns members from a geospatial index as geohash strings.</summary>\n\n**Syntax:** `GEOHASH key [member [member ...]]`\n\n**Description:** Returns members from a geospatial index as geohash strings.\n\n**Complexity:** O(1) for each member requested.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geopos/\">GEOPOS</a></strong> - Returns the longitude and latitude of members from a geospatial index.</summary>\n\n**Syntax:** `GEOPOS key [member [member ...]]`\n\n**Description:** Returns the longitude and latitude of members from a geospatial index.\n\n**Complexity:** O(1) for each member requested.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/georadius/\">GEORADIUS</a></strong> - Queries a geospatial index for members within a distance from a coordinate, optionally stores the result.</summary>\n\n**Syntax:** `GEORADIUS key longitude latitude radius <M | KM | FT | MI>\n  [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC]\n  [STORE key | STOREDIST key]`\n\n**Description:** Queries a geospatial index for members within a distance from a coordinate, optionally stores the result.\n\n**Complexity:** O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/georadiusbymember/\">GEORADIUSBYMEMBER</a></strong> - Queries a geospatial index for members within a distance from a member, optionally stores the result.</summary>\n\n**Syntax:** `GEORADIUSBYMEMBER key member radius <M | KM | FT | MI> [WITHCOORD]\n  [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC] [STORE key\n  | STOREDIST key]`\n\n**Description:** Queries a geospatial index for members within a distance from a member, optionally stores the result.\n\n**Complexity:** O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/georadiusbymember_ro/\">GEORADIUSBYMEMBER_RO</a></strong> - Returns members from a geospatial index that are within a distance from a member.</summary>\n\n**Syntax:** `GEORADIUSBYMEMBER RO key member radius <M | KM | FT | MI>\n  [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC]`\n\n**Description:** Returns members from a geospatial index that are within a distance from a member.\n\n**Complexity:** O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.\n\n**Since:** 3.2.10\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/georadius_ro/\">GEORADIUS_RO</a></strong> - Returns members from a geospatial index that are within a distance from a coordinate.</summary>\n\n**Syntax:** `GEORADIUS RO key longitude latitude radius <M | KM | FT | MI>\n  [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC]`\n\n**Description:** Returns members from a geospatial index that are within a distance from a coordinate.\n\n**Complexity:** O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.\n\n**Since:** 3.2.10\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geosearch/\">GEOSEARCH</a></strong> - Queries a geospatial index for members inside an area of a box or a circle.</summary>\n\n**Syntax:** `GEOSEARCH key <FROMMEMBER member | FROMLONLAT longitude latitude>\n  <BYRADIUS radius <M | KM | FT | MI> | BYBOX width height <M | KM |\n  FT | MI>> [ASC | DESC] [COUNT count [ANY]] [WITHCOORD] [WITHDIST]\n  [WITHHASH]`\n\n**Description:** Queries a geospatial index for members inside an area of a box or a circle.\n\n**Complexity:** O(N+log(M)) where N is the number of elements in the grid-aligned bounding box area around the shape provided as the filter and M is the number of items inside the shape\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geosearchstore/\">GEOSEARCHSTORE</a></strong> - Queries a geospatial index for members inside an area of a box or a circle, optionally stores the result.</summary>\n\n**Syntax:** `GEOSEARCHSTORE destination source <FROMMEMBER member |\n  FROMLONLAT longitude latitude> <BYRADIUS radius <M | KM | FT | MI>\n  | BYBOX width height <M | KM | FT | MI>> [ASC | DESC] [COUNT count\n  [ANY]] [STOREDIST]`\n\n**Description:** Queries a geospatial index for members inside an area of a box or a circle, optionally stores the result.\n\n**Complexity:** O(N+log(M)) where N is the number of elements in the grid-aligned bounding box area around the shape provided as the filter and M is the number of items inside the shape\n\n**Since:** 6.2.0\n\n</details>"},{"id":"json-commands","title":"JSON commands","role":"content","text":"JSON commands operate on JSON data structures.\n\n<details>\n<summary><strong><a href=\"/commands/json.arrappend/\">JSON.ARRAPPEND</a></strong> - Append one or more json values into the array at path after the last element in it.</summary>\n\n**Syntax:** `JSON.ARRAPPEND key path value [value ...]`\n\n**Description:** Append one or more json values into the array at path after the last element in it.\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrindex/\">JSON.ARRINDEX</a></strong> - Returns the index of the first occurrence of a JSON scalar value in the array at path</summary>\n\n**Syntax:** `JSON.ARRINDEX key path value [start [stop]]`\n\n**Description:** Returns the index of the first occurrence of a JSON scalar value in the array at path\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the array, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrinsert/\">JSON.ARRINSERT</a></strong> - Inserts the JSON scalar(s) value at the specified index in the array at path</summary>\n\n**Syntax:** `JSON.ARRINSERT key path index value [value ...]`\n\n**Description:** Inserts the JSON scalar(s) value at the specified index in the array at path\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the array, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrlen/\">JSON.ARRLEN</a></strong> - Returns the length of the array at path</summary>\n\n**Syntax:** `JSON.ARRLEN key [path]`\n\n**Description:** Returns the length of the array at path\n\n**Complexity:** O(1) where path is evaluated to a single value, O(N) where path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrpop/\">JSON.ARRPOP</a></strong> - Removes and returns the element at the specified index in the array at path</summary>\n\n**Syntax:** `JSON.ARRPOP key [path [index]]`\n\n**Description:** Removes and returns the element at the specified index in the array at path\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the array and the specified index is not the last element, O(1) when path is evaluated to a single value and the specified index is the last element, or O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrtrim/\">JSON.ARRTRIM</a></strong> - Trims the array at path to contain only the specified inclusive range of indices from start to stop</summary>\n\n**Syntax:** `JSON.ARRTRIM key path start stop`\n\n**Description:** Trims the array at path to contain only the specified inclusive range of indices from start to stop\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the array, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.clear/\">JSON.CLEAR</a></strong> - Clears all values from an array or an object and sets numeric values to `0`</summary>\n\n**Syntax:** `JSON.CLEAR key [path]`\n\n**Description:** Clears all values from an array or an object and sets numeric values to `0`\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the values, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.debug/\">JSON.DEBUG</a></strong> - Debugging container command</summary>\n\n**Syntax:** `JSON.DEBUG`\n\n**Description:** Debugging container command\n\n**Complexity:** N/A\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.debug-memory/\">JSON.DEBUG MEMORY</a></strong> - Reports the size in bytes of a key</summary>\n\n**Syntax:** `JSON.DEBUG MEMORY key [path]`\n\n**Description:** Reports the size in bytes of a key\n\n**Complexity:** O(N) when path is evaluated to a single value, where N is the size of the value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.del/\">JSON.DEL</a></strong> - Deletes a value</summary>\n\n**Syntax:** `JSON.DEL key [path]`\n\n**Description:** Deletes a value\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the deleted value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.forget/\">JSON.FORGET</a></strong> - Deletes a value</summary>\n\n**Syntax:** `JSON.FORGET key [path]`\n\n**Description:** Deletes a value\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the deleted value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.get/\">JSON.GET</a></strong> - Gets the value at one or more paths in JSON serialized form</summary>\n\n**Syntax:** `JSON.GET key [INDENT indent] [NEWLINE newline] [SPACE space] [path\n  [path ...]]`\n\n**Description:** Gets the value at one or more paths in JSON serialized form\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.merge/\">JSON.MERGE</a></strong> - Merges a given JSON value into matching paths. Consequently, JSON values at matching paths are updated, deleted, or expanded with new children</summary>\n\n**Syntax:** `JSON.MERGE key path value`\n\n**Description:** Merges a given JSON value into matching paths. Consequently, JSON values at matching paths are updated, deleted, or expanded with new children\n\n**Complexity:** O(M+N) when path is evaluated to a single value where M is the size of the original value (if it exists) and N is the size of the new value, O(M+N) when path is evaluated to multiple values where M is the size of the key and N is the size of the new value * the number of original values in the key\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.mget/\">JSON.MGET</a></strong> - Returns the values at a path from one or more keys</summary>\n\n**Syntax:** `JSON.MGET key [key ...] path`\n\n**Description:** Returns the values at a path from one or more keys\n\n**Complexity:** O(M*N) when path is evaluated to a single value where M is the number of keys and N is the size of the value, O(N1+N2+...+Nm) when path is evaluated to multiple values where m is the number of keys and Ni is the size of the i-th key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.mset/\">JSON.MSET</a></strong> - Sets or updates the JSON value of one or more keys</summary>\n\n**Syntax:** `JSON.MSET key path value [key path value ...]`\n\n**Description:** Sets or updates the JSON value of one or more keys\n\n**Complexity:** O(K*(M+N)) where k is the number of keys in the command, when path is evaluated to a single value where M is the size of the original value (if it exists) and N is the size of the new value, or O(K*(M+N)) when path is evaluated to multiple values where M is the size of the key and N is the size of the new value * the number of original values in the key\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.numincrby/\">JSON.NUMINCRBY</a></strong> - Increments the numeric value at path by a value</summary>\n\n**Syntax:** `JSON.NUMINCRBY key path value`\n\n**Description:** Increments the numeric value at path by a value\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.nummultby/\">JSON.NUMMULTBY</a></strong> - Multiplies the numeric value at path by a value</summary>\n\n**Syntax:** `JSON.NUMMULTBY key path value`\n\n**Description:** Multiplies the numeric value at path by a value\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.objkeys/\">JSON.OBJKEYS</a></strong> - Returns the key names of JSON objects at the paths matching a given path expression</summary>\n\n**Syntax:** `JSON.OBJKEYS key [path]`\n\n**Description:** Returns the key names of JSON objects at the paths matching a given path expression\n\n**Complexity:** O(N) when path is evaluated to a single value, where N is the number of keys in the object, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.objlen/\">JSON.OBJLEN</a></strong> - Returns the number of keys in JSON objects at the paths matching a given path expression</summary>\n\n**Syntax:** `JSON.OBJLEN key [path]`\n\n**Description:** Returns the number of keys in JSON objects at the paths matching a given path expression\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.resp/\">JSON.RESP</a></strong> - Returns the JSON value at path in Redis Serialization Protocol (RESP)</summary>\n\n**Syntax:** `JSON.RESP key [path]`\n\n**Description:** Returns the JSON value at path in Redis Serialization Protocol (RESP)\n\n**Complexity:** O(N) when path is evaluated to a single value, where N is the size of the value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.set/\">JSON.SET</a></strong> - Sets or updates the JSON value at a path</summary>\n\n**Syntax:** `JSON.SET key path value [NX | XX] [FPHA <FP16 | BF16 | FP32 | FP64>]`\n\n**Description:** Sets or updates the JSON value at a path\n\n**Complexity:** O(M+N) when path is evaluated to a single value where M is the size of the original value (if it exists) and N is the size of the new value, O(M+N) when path is evaluated to multiple values where M is the size of the key and N is the size of the new value * the number of original values in the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.strappend/\">JSON.STRAPPEND</a></strong> - Appends a string to JSON strings at the paths matching a given path expression</summary>\n\n**Syntax:** `JSON.STRAPPEND key [path] value`\n\n**Description:** Appends a string to JSON strings at the paths matching a given path expression\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.strlen/\">JSON.STRLEN</a></strong> - Returns the length of JSON strings at the paths matching a given path expression</summary>\n\n**Syntax:** `JSON.STRLEN key [path]`\n\n**Description:** Returns the length of JSON strings at the paths matching a given path expression\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.toggle/\">JSON.TOGGLE</a></strong> - Toggles a boolean value</summary>\n\n**Syntax:** `JSON.TOGGLE key path`\n\n**Description:** Toggles a boolean value\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.type/\">JSON.TYPE</a></strong> - Returns the type of the JSON value at path</summary>\n\n**Syntax:** `JSON.TYPE key [path]`\n\n**Description:** Returns the type of the JSON value at path\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>"},{"id":"search-commands","title":"Search commands","role":"content","text":"Search commands provide full-text search and secondary indexing.\n\n<details>\n<summary><strong><a href=\"/commands/ft.aggregate/\">FT.AGGREGATE</a></strong> - Run a search query on an index and perform aggregate transformations on the results</summary>\n\n**Syntax:** ``\n\n**Description:** Run a search query on an index and perform aggregate transformations on the results\n\n**Complexity:** O(1)\n\n**Since:** 1.1.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.aliasadd/\">FT.ALIASADD</a></strong> - Adds an alias to the index</summary>\n\n**Syntax:** `FT.ALIASADD alias index`\n\n**Description:** Adds an alias to the index\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.aliasdel/\">FT.ALIASDEL</a></strong> - Deletes an alias from the index</summary>\n\n**Syntax:** `FT.ALIASDEL alias`\n\n**Description:** Deletes an alias from the index\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.aliaslist/\">FT.ALIASLIST</a></strong> - Lists all aliases for the index <span style=\"color: #e74c3c;\">⭐ New in 8.10</span></summary>\n\n**Syntax:** `FT.ALIASLIST index`\n\n**Description:** Lists all aliases for the index\n\n**Complexity:** O(N) where N is the number of aliases\n\n**Since:** 8.10.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.aliasupdate/\">FT.ALIASUPDATE</a></strong> - Adds or updates an alias to the index</summary>\n\n**Syntax:** `FT.ALIASUPDATE alias index`\n\n**Description:** Adds or updates an alias to the index\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.alter/\">FT.ALTER</a></strong> - Adds a new field to the index</summary>\n\n**Syntax:** `FT.ALTER index [SKIPINITIALSCAN] SCHEMA ADD field options`\n\n**Description:** Adds a new field to the index\n\n**Complexity:** O(N) where N is the number of keys in the keyspace\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.config-get/\">FT.CONFIG GET</a></strong> - Retrieves runtime configuration options</summary>\n\n**Syntax:** `FT.CONFIG GET option`\n\n**Description:** Retrieves runtime configuration options\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.config-set/\">FT.CONFIG SET</a></strong> - Sets runtime configuration options</summary>\n\n**Syntax:** `FT.CONFIG SET option value`\n\n**Description:** Sets runtime configuration options\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.create/\">FT.CREATE</a></strong> - Creates an index with the given spec</summary>\n\n**Syntax:** `FT.CREATE index [ON <HASH | JSON>] [PREFIX count prefix [prefix\n  ...]] [FILTER filter] [LANGUAGE default lang]\n  [LANGUAGE FIELD lang attribute] [SCORE default score]\n  [SCORE FIELD score attribute] [PAYLOAD FIELD payload attribute]\n  [MAXTEXTFIELDS] [TEMPORARY seconds] [NOOFFSETS] [NOHL] [NOFIELDS]\n  [NOFREQS] [STOPWORDS count [stopword [stopword ...]]]\n  [SKIPINITIALSCAN] [INDEXALL <ENABLE | DISABLE>] SCHEMA field name [AS alias] <TEXT | TAG |\n  NUMERIC | GEO | VECTOR> [WITHSUFFIXTRIE] [INDEXEMPTY]\n  [INDEXMISSING] [SORTABLE [UNF]] [NOINDEX] [field name [AS alias]\n  <TEXT | TAG | NUMERIC | GEO | VECTOR> [WITHSUFFIXTRIE]\n  [INDEXEMPTY] [INDEXMISSING] [SORTABLE [UNF]] [NOINDEX] ...]`\n\n**Description:** Creates an index with the given spec\n\n**Complexity:** O(K) at creation where K is the number of fields, O(N) if scanning the keyspace is triggered, where N is the number of keys in the keyspace\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.cursor-del/\">FT.CURSOR DEL</a></strong> - Deletes a cursor</summary>\n\n**Syntax:** `FT.CURSOR DEL index cursor id`\n\n**Description:** Deletes a cursor\n\n**Complexity:** O(1)\n\n**Since:** 1.1.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.cursor-read/\">FT.CURSOR READ</a></strong> - Reads from a cursor</summary>\n\n**Syntax:** `FT.CURSOR READ index cursor id [COUNT read size]`\n\n**Description:** Reads from a cursor\n\n**Complexity:** O(1)\n\n**Since:** 1.1.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.dictadd/\">FT.DICTADD</a></strong> - Adds terms to a dictionary</summary>\n\n**Syntax:** `FT.DICTADD dict term [term ...]`\n\n**Description:** Adds terms to a dictionary\n\n**Complexity:** O(1)\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.dictdel/\">FT.DICTDEL</a></strong> - Deletes terms from a dictionary</summary>\n\n**Syntax:** `FT.DICTDEL dict term [term ...]`\n\n**Description:** Deletes terms from a dictionary\n\n**Complexity:** O(1)\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.dictdump/\">FT.DICTDUMP</a></strong> - Dumps all terms in the given dictionary</summary>\n\n**Syntax:** `FT.DICTDUMP dict`\n\n**Description:** Dumps all terms in the given dictionary\n\n**Complexity:** O(N), where N is the size of the dictionary\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.dropindex/\">FT.DROPINDEX</a></strong> - Deletes the index</summary>\n\n**Syntax:** `FT.DROPINDEX index [DD]`\n\n**Description:** Deletes the index\n\n**Complexity:** O(1) or O(N) if documents are deleted, where N is the number of keys in the keyspace\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.explain/\">FT.EXPLAIN</a></strong> - Returns the execution plan for a complex query</summary>\n\n**Syntax:** `FT.EXPLAIN index query [DIALECT dialect]`\n\n**Description:** Returns the execution plan for a complex query\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.explaincli/\">FT.EXPLAINCLI</a></strong> - Returns the execution plan for a complex query</summary>\n\n**Syntax:** `FT.EXPLAINCLI index query [DIALECT dialect]`\n\n**Description:** Returns the execution plan for a complex query\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.hybrid/\">FT.HYBRID</a></strong> - Performs hybrid search combining text search and vector similarity search</summary>\n\n**Syntax:** `FT.HYBRID index\n  SEARCH query\n    [SCORER scorer]\n    [YIELD SCORE AS name]\n  VSIM vector field $vector param\n    [KNN count K k [EF RUNTIME ef runtime]] [SHARD K RATIO shard k ratio]]\n    [RANGE count RADIUS radius [EPSILON epsilon]]\n    [YIELD SCORE AS name]\n    [FILTER] count filter-expression [POLICY [ADHOC/BATCHES] BATCH SIZE batch-size-value]\n  [COMBINE RRF count [CONSTANT constant] [WINDOW window] [YIELD SCORE AS name]]\n  [COMBINE LINEAR count [[ALPHA alpha] [BETA beta]] [WINDOW window] [YIELD SCORE AS name]]\n  [LIMIT offset num]\n  [SORTBY count sortby [ASC | DESC]]\n  [NOSORT]\n  [LOAD count field [field ...]]\n  [LOAD *]\n  [GROUPBY nargs property [property ...]\n  [GROUPBY nargs property [property ...]\n    [REDUCE function nargs arg [arg ...] [AS name]\n    [REDUCE function nargs arg [arg ...] [AS name] ...]] ...]]\n  [APPLY expression AS name [APPLY expression AS name ...]]\n  [FILTER filter]\n  PARAMS nargs vector param vector blob [name value ...]\n  [TIMEOUT timeout]`\n\n**Description:** Performs hybrid search combining text search and vector similarity search\n\n**Complexity:** O(N+M) where N is the complexity of the text search and M is the complexity of the vector search\n\n**Since:** 8.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.info/\">FT.INFO</a></strong> - Returns information and statistics on the index</summary>\n\n**Syntax:** `FT.INFO index`\n\n**Description:** Returns information and statistics on the index\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.profile/\">FT.PROFILE</a></strong> - Performs a `FT.SEARCH`, `FT.HYBRID`, or `FT.AGGREGATE` command and collects performance information</summary>\n\n**Syntax:** `FT.PROFILE index <SEARCH | HYBRID | AGGREGATE> [LIMITED] QUERY query`\n\n**Description:** Performs a `FT.SEARCH`, `FT.HYBRID`, or `FT.AGGREGATE` command and collects performance information\n\n**Complexity:** O(N)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.search/\">FT.SEARCH</a></strong> - Searches the index with a textual query, returning either documents or just ids</summary>\n\n**Syntax:** `FT.SEARCH index query [NOCONTENT] [VERBATIM] [NOSTOPWORDS]\n  [WITHSCORES] [WITHPAYLOADS] [WITHSORTKEYS] [FILTER numeric field\n  min max [FILTER numeric field min max ...]] [GEOFILTER geo field\n  lon lat radius <m | km | mi | ft> [GEOFILTER geo field lon lat\n  radius <m | km | mi | ft> ...]] [INKEYS count key [key ...]]\n  [INFIELDS count field [field ...]] [RETURN count identifier\n  [AS property] [identifier [AS property] ...]] [SUMMARIZE\n  [FIELDS count field [field ...]] [FRAGS num] [LEN fragsize]\n  [SEPARATOR separator]] [HIGHLIGHT [FIELDS count field [field ...]]\n  [TAGS open close]] [SLOP slop] [TIMEOUT timeout] [INORDER]\n  [LANGUAGE language] [EXPANDER expander] [SCORER scorer]\n  [EXPLAINSCORE] [PAYLOAD payload] [SORTBY sortby [ASC | DESC]]\n  [LIMIT offset num] [PARAMS nargs name value [name value ...]]\n  [DIALECT dialect]`\n\n**Description:** Searches the index with a textual query, returning either documents or just ids\n\n**Complexity:** O(N)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.spellcheck/\">FT.SPELLCHECK</a></strong> - Performs spelling correction on a query, returning suggestions for misspelled terms</summary>\n\n**Syntax:** `FT.SPELLCHECK index query [DISTANCE distance] [TERMS <INCLUDE |\n  EXCLUDE> dictionary [terms [terms ...]]] [DIALECT dialect]`\n\n**Description:** Performs spelling correction on a query, returning suggestions for misspelled terms\n\n**Complexity:** O(1)\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.syndump/\">FT.SYNDUMP</a></strong> - Dumps the contents of a synonym group</summary>\n\n**Syntax:** `FT.SYNDUMP index`\n\n**Description:** Dumps the contents of a synonym group\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.synupdate/\">FT.SYNUPDATE</a></strong> - Creates or updates a synonym group with additional terms</summary>\n\n**Syntax:** `FT.SYNUPDATE index synonym group id [SKIPINITIALSCAN] term [term\n  ...]`\n\n**Description:** Creates or updates a synonym group with additional terms\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.tagvals/\">FT.TAGVALS</a></strong> - Returns the distinct tags indexed in a Tag field</summary>\n\n**Syntax:** `FT.TAGVALS index field name`\n\n**Description:** Returns the distinct tags indexed in a Tag field\n\n**Complexity:** O(N)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft._list/\">FT._LIST</a></strong> - Returns a list of all existing indexes</summary>\n\n**Syntax:** `FT. LIST`\n\n**Description:** Returns a list of all existing indexes\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>"},{"id":"time-series-commands","title":"Time series commands","role":"content","text":"Time series commands operate on time-series data.\n\n<details>\n<summary><strong><a href=\"/commands/ts.add/\">TS.ADD</a></strong> - Append a sample to a time series</summary>\n\n**Syntax:** `TS.ADD key timestamp value [RETENTION retentionPeriod]\n  [ENCODING <UNCOMPRESSED | COMPRESSED>] [CHUNK SIZE size]\n  [ON DUPLICATE <BLOCK | FIRST | LAST | MIN | MAX | SUM>]\n  [LABELS label value [label value ...]]`\n\n**Description:** Append a sample to a time series\n\n**Complexity:** O(M) when M is the amount of compaction rules or O(1) with no compaction\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.alter/\">TS.ALTER</a></strong> - Update the retention, chunk size, duplicate policy, and labels of an existing time series</summary>\n\n**Syntax:** `TS.ALTER key [RETENTION retentionPeriod] [CHUNK SIZE size]\n  [DUPLICATE POLICY <BLOCK | FIRST | LAST | MIN | MAX | SUM>] [LABELS label value\n  [label value ...]]`\n\n**Description:** Update the retention, chunk size, duplicate policy, and labels of an existing time series\n\n**Complexity:** O(N) where N is the number of labels requested to update\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.create/\">TS.CREATE</a></strong> - Create a new time series</summary>\n\n**Syntax:** `TS.CREATE key [RETENTION retentionPeriod] [ENCODING <UNCOMPRESSED |\n  COMPRESSED>] [CHUNK SIZE size] [DUPLICATE POLICY <BLOCK | FIRST |\n  LAST | MIN | MAX | SUM>] [LABELS label value [label value ...]]`\n\n**Description:** Create a new time series\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.createrule/\">TS.CREATERULE</a></strong> - Create a compaction rule</summary>\n\n**Syntax:** `TS.CREATERULE sourceKey destKey AGGREGATION <AVG | FIRST | LAST |\n  MIN | MAX | SUM | RANGE | COUNT | STD.P | STD.S | VAR.P | VAR.S |\n  TWA> bucketDuration [alignTimestamp]`\n\n**Description:** Create a compaction rule\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.decrby/\">TS.DECRBY</a></strong> - Decrease the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given decrement</summary>\n\n**Syntax:** `TS.DECRBY key value [TIMESTAMP timestamp]\n  [RETENTION retentionPeriod] [UNCOMPRESSED] [CHUNK SIZE size]\n  [LABELS label value [label value ...]]`\n\n**Description:** Decrease the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given decrement\n\n**Complexity:** O(M) when M is the amount of compaction rules or O(1) with no compaction\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.del/\">TS.DEL</a></strong> - Delete all samples between two timestamps for a given time series</summary>\n\n**Syntax:** `TS.DEL key from timestamp to timestamp`\n\n**Description:** Delete all samples between two timestamps for a given time series\n\n**Complexity:** O(N) where N is the number of data points that will be removed\n\n**Since:** 1.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.deleterule/\">TS.DELETERULE</a></strong> - Delete a compaction rule</summary>\n\n**Syntax:** `TS.DELETERULE sourceKey destKey`\n\n**Description:** Delete a compaction rule\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.get/\">TS.GET</a></strong> - Get the sample with the highest timestamp from a given time series</summary>\n\n**Syntax:** `TS.GET key [LATEST]`\n\n**Description:** Get the sample with the highest timestamp from a given time series\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.incrby/\">TS.INCRBY</a></strong> - Increase the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given increment</summary>\n\n**Syntax:** `TS.INCRBY key value [TIMESTAMP timestamp]\n  [RETENTION retentionPeriod] [UNCOMPRESSED] [CHUNK SIZE size]\n  [LABELS label value [label value ...]]`\n\n**Description:** Increase the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given increment\n\n**Complexity:** O(M) when M is the amount of compaction rules or O(1) with no compaction\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.info/\">TS.INFO</a></strong> - Returns information and statistics for a time series</summary>\n\n**Syntax:** `TS.INFO key [DEBUG]`\n\n**Description:** Returns information and statistics for a time series\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.madd/\">TS.MADD</a></strong> - Append new samples to one or more time series</summary>\n\n**Syntax:** `TS.MADD key timestamp value [key timestamp value ...]`\n\n**Description:** Append new samples to one or more time series\n\n**Complexity:** O(N*M) when N is the amount of series updated and M is the amount of compaction rules or O(N) with no compaction\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.mget/\">TS.MGET</a></strong> - Get the sample with the highest timestamp from each time series matching a specific filter</summary>\n\n**Syntax:** `TS.MGET [LATEST] [WITHLABELS | SELECTED LABELS label1 [label1 ...]]\n  FILTER <l=v | l!=v | l= | l!= | l=(v1,v2,...) | l!=(v1,v2,...)\n  [l=v | l!=v | l= | l!= | l=(v1,v2,...) | l!=(v1,v2,...) ...]>`\n\n**Description:** Get the sample with the highest timestamp from each time series matching a specific filter\n\n**Complexity:** O(n) where n is the number of time-series that match the filters\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.mrange/\">TS.MRANGE</a></strong> - Query a range across multiple time series by filters in forward direction</summary>\n\n**Syntax:** `TS.MRANGE fromTimestamp toTimestamp [LATEST] [FILTER BY TS Timestamp\n  [Timestamp ...]] [FILTER BY VALUE min max] [WITHLABELS |\n  SELECTED LABELS label1 [label1 ...]] [COUNT count] [[ALIGN value]\n  AGGREGATION aggregators bucketDuration [BUCKETTIMESTAMP]\n  [EMPTY]] FILTER <l=v | l!=v | l= | l!= | l=(v1,v2,...) |\n  l!=(v1,v2,...) [l=v | l!=v | l= | l!= | l=(v1,v2,...) |\n  l!=(v1,v2,...) ...]> [GROUPBY label REDUCE reducer] [EXCLUDEEMPTY]`\n\n**Description:** Query a range across multiple time series by filters in forward direction\n\n**Complexity:** O(n/m+k) where n = Number of data points, m = Chunk size (data points per chunk), k = Number of data points that are in the requested ranges\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.mrevrange/\">TS.MREVRANGE</a></strong> - Query a range across multiple time-series by filters in reverse direction</summary>\n\n**Syntax:** `TS.MREVRANGE fromTimestamp toTimestamp [LATEST]\n  [FILTER BY TS Timestamp [Timestamp ...]] [FILTER BY VALUE min max]\n  [WITHLABELS | SELECTED LABELS label1 [label1 ...]] [COUNT count]\n  [[ALIGN value] AGGREGATION aggregators bucketDuration\n  [BUCKETTIMESTAMP] [EMPTY]] FILTER <l=v | l!=v | l= | l!= |\n  l=(v1,v2,...) | l!=(v1,v2,...) [l=v | l!=v | l= | l!= |\n  l=(v1,v2,...) | l!=(v1,v2,...) ...]> [GROUPBY label REDUCE\n  reducer] [EXCLUDEEMPTY]`\n\n**Description:** Query a range across multiple time-series by filters in reverse direction\n\n**Complexity:** O(n/m+k) where n = Number of data points, m = Chunk size (data points per chunk), k = Number of data points that are in the requested ranges\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.nrange/\">TS.NRANGE</a></strong> - Query a range across multiple time series in forward direction, returning the results grouped by timestamp <span style=\"color: #e74c3c;\">⭐ New in 8.10</span></summary>\n\n**Syntax:** `TS.NRANGE numkeys key [key ...] fromTimestamp toTimestamp [LATEST]\n  [FILTER BY TS ts [ts ...]] [FILTER BY VALUE min max] [COUNT count]\n  [[ALIGN align] AGGREGATION aggregators [aggregators ...] bucketDuration\n  [BUCKETTIMESTAMP <start | - | end | + | mid | ~>] [EMPTY]]`\n\n**Description:** Query a range across multiple time series in forward direction, returning the results grouped by timestamp\n\n**Complexity:** O(numkeys*(n/m+k)) where n = Number of samples, m = Chunk size (samples per chunk), k = Number of samples that are in the requested range\n\n**Since:** 8.10.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.nrevrange/\">TS.NREVRANGE</a></strong> - Query a range across multiple time series in reverse direction, returning the results grouped by timestamp <span style=\"color: #e74c3c;\">⭐ New in 8.10</span></summary>\n\n**Syntax:** `TS.NREVRANGE numkeys key [key ...] fromTimestamp toTimestamp\n  [LATEST] [FILTER BY TS ts [ts ...]] [FILTER BY VALUE min max]\n  [COUNT count] [[ALIGN align] AGGREGATION aggregators [aggregators ...]\n  bucketDuration [BUCKETTIMESTAMP <start | - | end | + | mid | ~>]\n  [EMPTY]]`\n\n**Description:** Query a range across multiple time series in reverse direction, returning the results grouped by timestamp\n\n**Complexity:** O(numkeys*(n/m+k)) where n = Number of samples, m = Chunk size (samples per chunk), k = Number of samples that are in the requested range\n\n**Since:** 8.10.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.queryindex/\">TS.QUERYINDEX</a></strong> - Get all time series keys matching a filter list</summary>\n\n**Syntax:** `TS.QUERYINDEX <l=v | l!=v | l= | l!= | l=(v1,v2,...) |\n  l!=(v1,v2,...) [l=v | l!=v | l= | l!= | l=(v1,v2,...) |\n  l!=(v1,v2,...) ...]>`\n\n**Description:** Get all time series keys matching a filter list\n\n**Complexity:** O(n) where n is the number of time-series that match the filters\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.querylabels/\">TS.QUERYLABELS</a></strong> - Get all label names, or all values of a given label, for time series matching a filter list, or all series <span style=\"color: #e74c3c;\">⭐ New in 8.10</span></summary>\n\n**Syntax:** `TS.QUERYLABELS <LABELS | VALUES label> [FILTER filterExpr\n  [filterExpr ...]]`\n\n**Description:** Get all label names, or all values of a given label, for time series matching a filter list, or all series\n\n**Complexity:** O(n) where n is the number of time-series that match the filters (all indexed series when FILTER is omitted)\n\n**Since:** 8.10.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.range/\">TS.RANGE</a></strong> - Query a range in forward direction</summary>\n\n**Syntax:** `TS.RANGE key fromTimestamp toTimestamp [LATEST]\n  [FILTER BY TS Timestamp [Timestamp ...]] [FILTER BY VALUE min max]\n  [COUNT count] [[ALIGN value] AGGREGATION aggregators bucketDuration [BUCKETTIMESTAMP] [EMPTY]]`\n\n**Description:** Query a range in forward direction\n\n**Complexity:** O(n/m+k) where n = Number of data points, m = Chunk size (data points per chunk), k = Number of data points that are in the requested range\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.read/\">TS.READ</a></strong> - Read: return up to max_count samples with timestamp >= timestamp. With BLOCK, waits up to milliseconds ms until at least min_count qualifying samples exist <span style=\"color: #e74c3c;\">⭐ New in 8.10</span></summary>\n\n**Syntax:** `TS.READ key timestamp [BLOCK milliseconds min count]\n  [MAX COUNT max count]`\n\n**Description:** Read: return up to max_count samples with timestamp >= timestamp. With BLOCK, waits up to milliseconds ms until at least min_count qualifying samples exist\n\n**Complexity:** O(log(n)+k) where n is the number of samples in the series and k is the number of returned samples\n\n**Since:** 8.10.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.revrange/\">TS.REVRANGE</a></strong> - Query a range in reverse direction</summary>\n\n**Syntax:** `TS.REVRANGE key fromTimestamp toTimestamp [LATEST]\n  [FILTER BY TS Timestamp [Timestamp ...]] [FILTER BY VALUE min max]\n  [COUNT count] [[ALIGN value] AGGREGATION aggregators\n  bucketDuration [BUCKETTIMESTAMP] [EMPTY]]`\n\n**Description:** Query a range in reverse direction\n\n**Complexity:** O(n/m+k) where n = Number of data points, m = Chunk size (data points per chunk), k = Number of data points that are in the requested range\n\n**Since:** 1.4.0\n\n</details>"},{"id":"vector-set-commands","title":"Vector set commands","role":"content","text":"Vector set commands operate on vector data structures for similarity search and range queries.\n\n<details>\n<summary><strong><a href=\"/commands/vadd/\">VADD</a></strong> - Add a new element to a vector set, or update its vector if it already exists.</summary>\n\n**Syntax:** `VADD key [REDUCE dim] (FP32 | VALUES num) vector element [CAS] [NOQUANT | Q8 | BIN]\n  [EF build-exploration-factor] [SETATTR attributes] [M numlinks]`\n\n**Description:** Add a new element to a vector set, or update its vector if it already exists.\n\n**Complexity:** O(log(N)) for each element added, where N is the number of elements in the vector set.\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vcard/\">VCARD</a></strong> - Return the number of elements in a vector set.</summary>\n\n**Syntax:** `VCARD key`\n\n**Description:** Return the number of elements in a vector set.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vdim/\">VDIM</a></strong> - Return the dimension of vectors in the vector set.</summary>\n\n**Syntax:** `VDIM key`\n\n**Description:** Return the dimension of vectors in the vector set.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vemb/\">VEMB</a></strong> - Return the vector associated with an element.</summary>\n\n**Syntax:** `VEMB key element [RAW]`\n\n**Description:** Return the vector associated with an element.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vgetattr/\">VGETATTR</a></strong> - Retrieve the JSON attributes of elements.</summary>\n\n**Syntax:** `VGETATTR key element`\n\n**Description:** Retrieve the JSON attributes of elements.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vinfo/\">VINFO</a></strong> - Return information about a vector set.</summary>\n\n**Syntax:** `VINFO key`\n\n**Description:** Return information about a vector set.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vismember/\">VISMEMBER</a></strong> - Check if an element exists in a vector set.</summary>\n\n**Syntax:** `VISMEMBER key element`\n\n**Description:** Check if an element exists in a vector set.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vlinks/\">VLINKS</a></strong> - Return the neighbors of an element at each layer in the HNSW graph.</summary>\n\n**Syntax:** `VLINKS key element [WITHSCORES]`\n\n**Description:** Return the neighbors of an element at each layer in the HNSW graph.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vrandmember/\">VRANDMEMBER</a></strong> - Return one or multiple random members from a vector set.</summary>\n\n**Syntax:** `VRANDMEMBER key [count]`\n\n**Description:** Return one or multiple random members from a vector set.\n\n**Complexity:** O(N) where N is the absolute value of the count argument.\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vrange/\">VRANGE</a></strong> - Return elements in a lexicographical range</summary>\n\n**Syntax:** `VRANGE key start end [count]`\n\n**Description:** Return elements in a lexicographical range\n\n**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)\n\n**Since:** 8.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vrem/\">VREM</a></strong> - Remove an element from a vector set.</summary>\n\n**Syntax:** `VREM key element`\n\n**Description:** Remove an element from a vector set.\n\n**Complexity:** O(log(N)) for each element removed, where N is the number of elements in the vector set.\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vsetattr/\">VSETATTR</a></strong> - Associate or remove the JSON attributes of elements.</summary>\n\n**Syntax:** `VSETATTR key element \"{ JSON obj }\"`\n\n**Description:** Associate or remove the JSON attributes of elements.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vsim/\">VSIM</a></strong> - Return elements by vector similarity.</summary>\n\n**Syntax:** `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]`\n\n**Description:** Return elements by vector similarity.\n\n**Complexity:** O(log(N)) where N is the number of elements in the vector set.\n\n**Since:** 8.0.0\n\n</details>"},{"id":"pubsub-commands","title":"Pub/Sub commands","role":"content","text":"Pub/Sub commands enable message passing between clients.\n\n<details>\n<summary><strong><a href=\"/commands/psubscribe/\">PSUBSCRIBE</a></strong> - Listens for messages published to channels that match one or more patterns.</summary>\n\n**Syntax:** `PSUBSCRIBE pattern [pattern ...]`\n\n**Description:** Listens for messages published to channels that match one or more patterns.\n\n**Complexity:** O(N) where N is the number of patterns to subscribe to.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/publish/\">PUBLISH</a></strong> - Posts a message to a channel.</summary>\n\n**Syntax:** `PUBLISH channel message`\n\n**Description:** Posts a message to a channel.\n\n**Complexity:** O(N+M) where N is the number of clients subscribed to the receiving channel and M is the total number of subscribed patterns (by any client).\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-channels/\">PUBSUB CHANNELS</a></strong> - Returns the active channels.</summary>\n\n**Syntax:** `PUBSUB CHANNELS [pattern]`\n\n**Description:** Returns the active channels.\n\n**Complexity:** O(N) where N is the number of active channels, and assuming constant time pattern matching (relatively short channels and patterns)\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-numpat/\">PUBSUB NUMPAT</a></strong> - Returns a count of unique pattern subscriptions.</summary>\n\n**Syntax:** `PUBSUB NUMPAT`\n\n**Description:** Returns a count of unique pattern subscriptions.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-numsub/\">PUBSUB NUMSUB</a></strong> - Returns a count of subscribers to channels.</summary>\n\n**Syntax:** `PUBSUB NUMSUB [channel [channel ...]]`\n\n**Description:** Returns a count of subscribers to channels.\n\n**Complexity:** O(N) for the NUMSUB subcommand, where N is the number of requested channels\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-shardchannels/\">PUBSUB SHARDCHANNELS</a></strong> - Returns the active shard channels.</summary>\n\n**Syntax:** `PUBSUB SHARDCHANNELS [pattern]`\n\n**Description:** Returns the active shard channels.\n\n**Complexity:** O(N) where N is the number of active shard channels, and assuming constant time pattern matching (relatively short shard channels).\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-shardnumsub/\">PUBSUB SHARDNUMSUB</a></strong> - Returns the count of subscribers of shard channels.</summary>\n\n**Syntax:** `PUBSUB SHARDNUMSUB [shardchannel [shardchannel ...]]`\n\n**Description:** Returns the count of subscribers of shard channels.\n\n**Complexity:** O(N) for the SHARDNUMSUB subcommand, where N is the number of requested shard channels\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/punsubscribe/\">PUNSUBSCRIBE</a></strong> - Stops listening to messages published to channels that match one or more patterns.</summary>\n\n**Syntax:** `PUNSUBSCRIBE [pattern [pattern ...]]`\n\n**Description:** Stops listening to messages published to channels that match one or more patterns.\n\n**Complexity:** O(N) where N is the number of patterns to unsubscribe.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/spublish/\">SPUBLISH</a></strong> - Post a message to a shard channel</summary>\n\n**Syntax:** `SPUBLISH shardchannel message`\n\n**Description:** Post a message to a shard channel\n\n**Complexity:** O(N) where N is the number of clients subscribed to the receiving shard channel.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ssubscribe/\">SSUBSCRIBE</a></strong> - Listens for messages published to shard channels.</summary>\n\n**Syntax:** `SSUBSCRIBE shardchannel [shardchannel ...]`\n\n**Description:** Listens for messages published to shard channels.\n\n**Complexity:** O(N) where N is the number of shard channels to subscribe to.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/subscribe/\">SUBSCRIBE</a></strong> - Listens for messages published to channels.</summary>\n\n**Syntax:** `SUBSCRIBE channel [channel ...]`\n\n**Description:** Listens for messages published to channels.\n\n**Complexity:** O(N) where N is the number of channels to subscribe to.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sunsubscribe/\">SUNSUBSCRIBE</a></strong> - Stops listening to messages posted to shard channels.</summary>\n\n**Syntax:** `SUNSUBSCRIBE [shardchannel [shardchannel ...]]`\n\n**Description:** Stops listening to messages posted to shard channels.\n\n**Complexity:** O(N) where N is the number of shard channels to unsubscribe.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/unsubscribe/\">UNSUBSCRIBE</a></strong> - Stops listening to messages posted to channels.</summary>\n\n**Syntax:** `UNSUBSCRIBE [channel [channel ...]]`\n\n**Description:** Stops listening to messages posted to channels.\n\n**Complexity:** O(N) where N is the number of channels to unsubscribe.\n\n**Since:** 2.0.0\n\n</details>"},{"id":"transaction-commands","title":"Transaction commands","role":"content","text":"Transaction commands enable atomic execution of command groups.\n\n<details>\n<summary><strong><a href=\"/commands/discard/\">DISCARD</a></strong> - Discards a transaction.</summary>\n\n**Syntax:** `DISCARD`\n\n**Description:** Discards a transaction.\n\n**Complexity:** O(N), when N is the number of queued commands\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/exec/\">EXEC</a></strong> - Executes all commands in a transaction.</summary>\n\n**Syntax:** `EXEC`\n\n**Description:** Executes all commands in a transaction.\n\n**Complexity:** Depends on commands in the transaction\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/multi/\">MULTI</a></strong> - Starts a transaction.</summary>\n\n**Syntax:** `MULTI`\n\n**Description:** Starts a transaction.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/unwatch/\">UNWATCH</a></strong> - Forgets about watched keys of a transaction.</summary>\n\n**Syntax:** `UNWATCH`\n\n**Description:** Forgets about watched keys of a transaction.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/watch/\">WATCH</a></strong> - Monitors changes to keys to determine the execution of a transaction.</summary>\n\n**Syntax:** `WATCH key [key ...]`\n\n**Description:** Monitors changes to keys to determine the execution of a transaction.\n\n**Complexity:** O(1) for every key.\n\n**Since:** 2.2.0\n\n</details>"},{"id":"scripting-commands","title":"Scripting commands","role":"content","text":"Scripting commands enable server-side Lua script execution.\n\n<details>\n<summary><strong><a href=\"/commands/eval/\">EVAL</a></strong> - Executes a server-side Lua script.</summary>\n\n**Syntax:** `EVAL script numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Executes a server-side Lua script.\n\n**Complexity:** Depends on the script that is executed.\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/evalsha/\">EVALSHA</a></strong> - Executes a server-side Lua script by SHA1 digest.</summary>\n\n**Syntax:** `EVALSHA sha1 numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Executes a server-side Lua script by SHA1 digest.\n\n**Complexity:** Depends on the script that is executed.\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/evalsha_ro/\">EVALSHA_RO</a></strong> - Executes a read-only server-side Lua script by SHA1 digest.</summary>\n\n**Syntax:** `EVALSHA RO sha1 numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Executes a read-only server-side Lua script by SHA1 digest.\n\n**Complexity:** Depends on the script that is executed.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/eval_ro/\">EVAL_RO</a></strong> - Executes a read-only server-side Lua script.</summary>\n\n**Syntax:** `EVAL RO script numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Executes a read-only server-side Lua script.\n\n**Complexity:** Depends on the script that is executed.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/fcall/\">FCALL</a></strong> - Invokes a function.</summary>\n\n**Syntax:** `FCALL function numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Invokes a function.\n\n**Complexity:** Depends on the function that is executed.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/fcall_ro/\">FCALL_RO</a></strong> - Invokes a read-only function.</summary>\n\n**Syntax:** `FCALL RO function numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Invokes a read-only function.\n\n**Complexity:** Depends on the function that is executed.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-delete/\">FUNCTION DELETE</a></strong> - Deletes a library and its functions.</summary>\n\n**Syntax:** `FUNCTION DELETE library-name`\n\n**Description:** Deletes a library and its functions.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-dump/\">FUNCTION DUMP</a></strong> - Dumps all libraries into a serialized binary payload.</summary>\n\n**Syntax:** `FUNCTION DUMP`\n\n**Description:** Dumps all libraries into a serialized binary payload.\n\n**Complexity:** O(N) where N is the number of functions\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-flush/\">FUNCTION FLUSH</a></strong> - Deletes all libraries and functions.</summary>\n\n**Syntax:** `FUNCTION FLUSH [ASYNC | SYNC]`\n\n**Description:** Deletes all libraries and functions.\n\n**Complexity:** O(N) where N is the number of functions deleted\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-kill/\">FUNCTION KILL</a></strong> - Terminates a function during execution.</summary>\n\n**Syntax:** `FUNCTION KILL`\n\n**Description:** Terminates a function during execution.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-list/\">FUNCTION LIST</a></strong> - Returns information about all libraries.</summary>\n\n**Syntax:** `FUNCTION LIST [LIBRARYNAME library-name-pattern] [WITHCODE]`\n\n**Description:** Returns information about all libraries.\n\n**Complexity:** O(N) where N is the number of functions\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-load/\">FUNCTION LOAD</a></strong> - Creates a library.</summary>\n\n**Syntax:** `FUNCTION LOAD [REPLACE] function-code`\n\n**Description:** Creates a library.\n\n**Complexity:** O(1) (considering compilation time is redundant)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-restore/\">FUNCTION RESTORE</a></strong> - Restores all libraries from a payload.</summary>\n\n**Syntax:** `FUNCTION RESTORE serialized-value [FLUSH | APPEND | REPLACE]`\n\n**Description:** Restores all libraries from a payload.\n\n**Complexity:** O(N) where N is the number of functions on the payload\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-stats/\">FUNCTION STATS</a></strong> - Returns information about a function during execution.</summary>\n\n**Syntax:** `FUNCTION STATS`\n\n**Description:** Returns information about a function during execution.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-debug/\">SCRIPT DEBUG</a></strong> - Sets the debug mode of server-side Lua scripts.</summary>\n\n**Syntax:** `SCRIPT DEBUG <YES | SYNC | NO>`\n\n**Description:** Sets the debug mode of server-side Lua scripts.\n\n**Complexity:** O(1)\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-exists/\">SCRIPT EXISTS</a></strong> - Determines whether server-side Lua scripts exist in the script cache.</summary>\n\n**Syntax:** `SCRIPT EXISTS sha1 [sha1 ...]`\n\n**Description:** Determines whether server-side Lua scripts exist in the script cache.\n\n**Complexity:** O(N) with N being the number of scripts to check (so checking a single script is an O(1) operation).\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-flush/\">SCRIPT FLUSH</a></strong> - Removes all server-side Lua scripts from the script cache.</summary>\n\n**Syntax:** `SCRIPT FLUSH [ASYNC | SYNC]`\n\n**Description:** Removes all server-side Lua scripts from the script cache.\n\n**Complexity:** O(N) with N being the number of scripts in cache\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-kill/\">SCRIPT KILL</a></strong> - Terminates a server-side Lua script during execution.</summary>\n\n**Syntax:** `SCRIPT KILL`\n\n**Description:** Terminates a server-side Lua script during execution.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-load/\">SCRIPT LOAD</a></strong> - Loads a server-side Lua script to the script cache.</summary>\n\n**Syntax:** `SCRIPT LOAD script`\n\n**Description:** Loads a server-side Lua script to the script cache.\n\n**Complexity:** O(N) with N being the length in bytes of the script body.\n\n**Since:** 2.6.0\n\n</details>"},{"id":"connection-commands","title":"Connection commands","role":"content","text":"Connection commands manage client connections.\n\n<details>\n<summary><strong><a href=\"/commands/auth/\">AUTH</a></strong> - Authenticates the connection.</summary>\n\n**Syntax:** `AUTH [username] password`\n\n**Description:** Authenticates the connection.\n\n**Complexity:** O(N) where N is the number of passwords defined for the user\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-caching/\">CLIENT CACHING</a></strong> - Instructs the server whether to track the keys in the next request.</summary>\n\n**Syntax:** `CLIENT CACHING <YES | NO>`\n\n**Description:** Instructs the server whether to track the keys in the next request.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-getname/\">CLIENT GETNAME</a></strong> - Returns the name of the connection.</summary>\n\n**Syntax:** `CLIENT GETNAME`\n\n**Description:** Returns the name of the connection.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-getredir/\">CLIENT GETREDIR</a></strong> - Returns the client ID to which the connection's tracking notifications are redirected.</summary>\n\n**Syntax:** `CLIENT GETREDIR`\n\n**Description:** Returns the client ID to which the connection's tracking notifications are redirected.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-id/\">CLIENT ID</a></strong> - Returns the unique client ID of the connection.</summary>\n\n**Syntax:** `CLIENT ID`\n\n**Description:** Returns the unique client ID of the connection.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-info/\">CLIENT INFO</a></strong> - Returns information about the connection.</summary>\n\n**Syntax:** `CLIENT INFO`\n\n**Description:** Returns information about the connection.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-kill/\">CLIENT KILL</a></strong> - Terminates open connections.</summary>\n\n**Syntax:** `CLIENT KILL <ip:port | <[ID client-id] | [TYPE <NORMAL | MASTER |\n  SLAVE | REPLICA | PUBSUB>] | [USER username] | [ADDR ip:port] |\n  [LADDR ip:port] | [SKIPME <YES | NO>] | [MAXAGE maxage]\n  [[ID client-id] | [TYPE <NORMAL | MASTER | SLAVE | REPLICA |\n  PUBSUB>] | [USER username] | [ADDR ip:port] | [LADDR ip:port] |\n  [SKIPME <YES | NO>] | [MAXAGE maxage] ...]>>`\n\n**Description:** Terminates open connections.\n\n**Complexity:** O(N) where N is the number of client connections\n\n**Since:** 2.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-list/\">CLIENT LIST</a></strong> - Lists open connections.</summary>\n\n**Syntax:** `CLIENT LIST [TYPE <NORMAL | MASTER | REPLICA | PUBSUB>]\n  [ID client-id [client-id ...]]`\n\n**Description:** Lists open connections.\n\n**Complexity:** O(N) where N is the number of client connections\n\n**Since:** 2.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-no-evict/\">CLIENT NO-EVICT</a></strong> - Sets the client eviction mode of the connection.</summary>\n\n**Syntax:** `CLIENT NO-EVICT <ON | OFF>`\n\n**Description:** Sets the client eviction mode of the connection.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-no-touch/\">CLIENT NO-TOUCH</a></strong> - Controls whether commands sent by the client affect the LRU/LFU of accessed keys.</summary>\n\n**Syntax:** `CLIENT NO-TOUCH <ON | OFF>`\n\n**Description:** Controls whether commands sent by the client affect the LRU/LFU of accessed keys.\n\n**Complexity:** O(1)\n\n**Since:** 7.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-pause/\">CLIENT PAUSE</a></strong> - Suspends commands processing.</summary>\n\n**Syntax:** `CLIENT PAUSE timeout [WRITE | ALL]`\n\n**Description:** Suspends commands processing.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-reply/\">CLIENT REPLY</a></strong> - Instructs the server whether to reply to commands.</summary>\n\n**Syntax:** `CLIENT REPLY <ON | OFF | SKIP>`\n\n**Description:** Instructs the server whether to reply to commands.\n\n**Complexity:** O(1)\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-setinfo/\">CLIENT SETINFO</a></strong> - Sets information specific to the client or connection.</summary>\n\n**Syntax:** `CLIENT SETINFO <LIB-NAME libname | LIB-VER libver>`\n\n**Description:** Sets information specific to the client or connection.\n\n**Complexity:** O(1)\n\n**Since:** 7.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-setname/\">CLIENT SETNAME</a></strong> - Sets the connection name.</summary>\n\n**Syntax:** `CLIENT SETNAME connection-name`\n\n**Description:** Sets the connection name.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-tracking/\">CLIENT TRACKING</a></strong> - Controls server-assisted client-side caching for the connection.</summary>\n\n**Syntax:** `CLIENT TRACKING <ON | OFF> [REDIRECT client-id] [PREFIX prefix\n  [PREFIX prefix ...]] [BCAST] [OPTIN] [OPTOUT] [NOLOOP]`\n\n**Description:** Controls server-assisted client-side caching for the connection.\n\n**Complexity:** O(1). Some options may introduce additional complexity.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-trackinginfo/\">CLIENT TRACKINGINFO</a></strong> - Returns information about server-assisted client-side caching for the connection.</summary>\n\n**Syntax:** `CLIENT TRACKINGINFO`\n\n**Description:** Returns information about server-assisted client-side caching for the connection.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-unblock/\">CLIENT UNBLOCK</a></strong> - Unblocks a client blocked by a blocking command from a different connection.</summary>\n\n**Syntax:** `CLIENT UNBLOCK client-id [TIMEOUT | ERROR]`\n\n**Description:** Unblocks a client blocked by a blocking command from a different connection.\n\n**Complexity:** O(log N) where N is the number of client connections\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-unpause/\">CLIENT UNPAUSE</a></strong> - Resumes processing commands from paused clients.</summary>\n\n**Syntax:** `CLIENT UNPAUSE`\n\n**Description:** Resumes processing commands from paused clients.\n\n**Complexity:** O(N) Where N is the number of paused clients\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/echo/\">ECHO</a></strong> - Returns the given string.</summary>\n\n**Syntax:** `ECHO message`\n\n**Description:** Returns the given string.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hello/\">HELLO</a></strong> - Handshakes with the Redis server.</summary>\n\n**Syntax:** `HELLO [protover [AUTH username password] [SETNAME clientname]]`\n\n**Description:** Handshakes with the Redis server.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ping/\">PING</a></strong> - Returns the server's liveliness response.</summary>\n\n**Syntax:** `PING [message]`\n\n**Description:** Returns the server's liveliness response.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/quit/\">QUIT</a></strong> - Closes the connection.</summary>\n\n**Syntax:** `QUIT`\n\n**Description:** Closes the connection.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/reset/\">RESET</a></strong> - Resets the connection.</summary>\n\n**Syntax:** `RESET`\n\n**Description:** Resets the connection.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/select/\">SELECT</a></strong> - Changes the selected database.</summary>\n\n**Syntax:** `SELECT index`\n\n**Description:** Changes the selected database.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>"},{"id":"server-commands","title":"Server commands","role":"content","text":"Server commands provide server management and introspection.\n\n<details>\n<summary><strong><a href=\"/commands/acl-cat/\">ACL CAT</a></strong> - Lists the ACL categories, or the commands inside a category.</summary>\n\n**Syntax:** `ACL CAT [category]`\n\n**Description:** Lists the ACL categories, or the commands inside a category.\n\n**Complexity:** O(1) since the categories and commands are a fixed set.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-deluser/\">ACL DELUSER</a></strong> - Deletes ACL users, and terminates their connections.</summary>\n\n**Syntax:** `ACL DELUSER username [username ...]`\n\n**Description:** Deletes ACL users, and terminates their connections.\n\n**Complexity:** O(1) amortized time considering the typical user.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-dryrun/\">ACL DRYRUN</a></strong> - Simulates the execution of a command by a user, without executing the command.</summary>\n\n**Syntax:** `ACL DRYRUN username command [arg [arg ...]]`\n\n**Description:** Simulates the execution of a command by a user, without executing the command.\n\n**Complexity:** O(1).\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-genpass/\">ACL GENPASS</a></strong> - Generates a pseudorandom, secure password that can be used to identify ACL users.</summary>\n\n**Syntax:** `ACL GENPASS [bits]`\n\n**Description:** Generates a pseudorandom, secure password that can be used to identify ACL users.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-getuser/\">ACL GETUSER</a></strong> - Lists the ACL rules of a user.</summary>\n\n**Syntax:** `ACL GETUSER username`\n\n**Description:** Lists the ACL rules of a user.\n\n**Complexity:** O(N). Where N is the number of password, command and pattern rules that the user has.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-list/\">ACL LIST</a></strong> - Dumps the effective rules in ACL file format.</summary>\n\n**Syntax:** `ACL LIST`\n\n**Description:** Dumps the effective rules in ACL file format.\n\n**Complexity:** O(N). Where N is the number of configured users.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-load/\">ACL LOAD</a></strong> - Reloads the rules from the configured ACL file.</summary>\n\n**Syntax:** `ACL LOAD`\n\n**Description:** Reloads the rules from the configured ACL file.\n\n**Complexity:** O(N). Where N is the number of configured users.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-log/\">ACL LOG</a></strong> - Lists recent security events generated due to ACL rules.</summary>\n\n**Syntax:** `ACL LOG [count | RESET]`\n\n**Description:** Lists recent security events generated due to ACL rules.\n\n**Complexity:** O(N) with N being the number of entries shown.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-save/\">ACL SAVE</a></strong> - Saves the effective ACL rules in the configured ACL file.</summary>\n\n**Syntax:** `ACL SAVE`\n\n**Description:** Saves the effective ACL rules in the configured ACL file.\n\n**Complexity:** O(N). Where N is the number of configured users.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-setuser/\">ACL SETUSER</a></strong> - Creates and modifies an ACL user and its rules.</summary>\n\n**Syntax:** `ACL SETUSER username [rule [rule ...]]`\n\n**Description:** Creates and modifies an ACL user and its rules.\n\n**Complexity:** O(N). Where N is the number of rules provided.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-users/\">ACL USERS</a></strong> - Lists all ACL users.</summary>\n\n**Syntax:** `ACL USERS`\n\n**Description:** Lists all ACL users.\n\n**Complexity:** O(N). Where N is the number of configured users.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-whoami/\">ACL WHOAMI</a></strong> - Returns the authenticated username of the current connection.</summary>\n\n**Syntax:** `ACL WHOAMI`\n\n**Description:** Returns the authenticated username of the current connection.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/backup-abort/\">BACKUP ABORT</a></strong> - Cancel a backup that has not been sealed yet. <span style=\"color: #e74c3c;\">⭐ New in 8.10</span></summary>\n\n**Syntax:** `BACKUP ABORT`\n\n**Description:** Cancel a backup that has not been sealed yet.\n\n**Complexity:** O(1)\n\n**Since:** 8.10.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/backup-cleanup/\">BACKUP CLEANUP</a></strong> - Remove sealed backup files and return to idle. <span style=\"color: #e74c3c;\">⭐ New in 8.10</span></summary>\n\n**Syntax:** `BACKUP CLEANUP`\n\n**Description:** Remove sealed backup files and return to idle.\n\n**Complexity:** O(1)\n\n**Since:** 8.10.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/backup-list/\">BACKUP LIST</a></strong> - List the immutable backup file paths pinned so far. <span style=\"color: #e74c3c;\">⭐ New in 8.10</span></summary>\n\n**Syntax:** `BACKUP LIST`\n\n**Description:** List the immutable backup file paths pinned so far.\n\n**Complexity:** O(1)\n\n**Since:** 8.10.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/backup-seal/\">BACKUP SEAL</a></strong> - Freeze the current backup (BASE + INCR + manifest). <span style=\"color: #e74c3c;\">⭐ New in 8.10</span></summary>\n\n**Syntax:** `BACKUP SEAL`\n\n**Description:** Freeze the current backup (BASE + INCR + manifest).\n\n**Complexity:** O(1)\n\n**Since:** 8.10.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/backup-start/\">BACKUP START</a></strong> - Start a new backup into the configured 'backupdirname'. <span style=\"color: #e74c3c;\">⭐ New in 8.10</span></summary>\n\n**Syntax:** `BACKUP START`\n\n**Description:** Start a new backup into the configured 'backupdirname'.\n\n**Complexity:** O(1)\n\n**Since:** 8.10.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/backup-status/\">BACKUP STATUS</a></strong> - Report the current backup state. <span style=\"color: #e74c3c;\">⭐ New in 8.10</span></summary>\n\n**Syntax:** `BACKUP STATUS`\n\n**Description:** Report the current backup state.\n\n**Complexity:** O(1)\n\n**Since:** 8.10.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bgrewriteaof/\">BGREWRITEAOF</a></strong> - Asynchronously rewrites the append-only file to disk.</summary>\n\n**Syntax:** `BGREWRITEAOF`\n\n**Description:** Asynchronously rewrites the append-only file to disk.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bgsave/\">BGSAVE</a></strong> - Asynchronously saves the database(s) to disk.</summary>\n\n**Syntax:** `BGSAVE [SCHEDULE]`\n\n**Description:** Asynchronously saves the database(s) to disk.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command/\">COMMAND</a></strong> - Returns detailed information about all commands.</summary>\n\n**Syntax:** `COMMAND`\n\n**Description:** Returns detailed information about all commands.\n\n**Complexity:** O(N) where N is the total number of Redis commands\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-count/\">COMMAND COUNT</a></strong> - Returns a count of commands.</summary>\n\n**Syntax:** `COMMAND COUNT`\n\n**Description:** Returns a count of commands.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-docs/\">COMMAND DOCS</a></strong> - Returns documentary information about one, multiple or all commands.</summary>\n\n**Syntax:** `COMMAND DOCS [command-name [command-name ...]]`\n\n**Description:** Returns documentary information about one, multiple or all commands.\n\n**Complexity:** O(N) where N is the number of commands to look up\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-getkeys/\">COMMAND GETKEYS</a></strong> - Extracts the key names from an arbitrary command.</summary>\n\n**Syntax:** `COMMAND GETKEYS command [arg [arg ...]]`\n\n**Description:** Extracts the key names from an arbitrary command.\n\n**Complexity:** O(N) where N is the number of arguments to the command\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-getkeysandflags/\">COMMAND GETKEYSANDFLAGS</a></strong> - Extracts the key names and access flags for an arbitrary command.</summary>\n\n**Syntax:** `COMMAND GETKEYSANDFLAGS command [arg [arg ...]]`\n\n**Description:** Extracts the key names and access flags for an arbitrary command.\n\n**Complexity:** O(N) where N is the number of arguments to the command\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-info/\">COMMAND INFO</a></strong> - Returns information about one, multiple or all commands.</summary>\n\n**Syntax:** `COMMAND INFO [command-name [command-name ...]]`\n\n**Description:** Returns information about one, multiple or all commands.\n\n**Complexity:** O(N) where N is the number of commands to look up\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-list/\">COMMAND LIST</a></strong> - Returns a list of command names.</summary>\n\n**Syntax:** `COMMAND LIST [FILTERBY <MODULE module-name | ACLCAT category |\n  PATTERN pattern>]`\n\n**Description:** Returns a list of command names.\n\n**Complexity:** O(N) where N is the total number of Redis commands\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/config-get/\">CONFIG GET</a></strong> - Returns the effective values of configuration parameters.</summary>\n\n**Syntax:** `CONFIG GET parameter [parameter ...]`\n\n**Description:** Returns the effective values of configuration parameters.\n\n**Complexity:** O(N) when N is the number of configuration parameters provided\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/config-resetstat/\">CONFIG RESETSTAT</a></strong> - Resets the server's statistics.</summary>\n\n**Syntax:** `CONFIG RESETSTAT`\n\n**Description:** Resets the server's statistics.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/config-rewrite/\">CONFIG REWRITE</a></strong> - Persists the effective configuration to file.</summary>\n\n**Syntax:** `CONFIG REWRITE`\n\n**Description:** Persists the effective configuration to file.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/config-set/\">CONFIG SET</a></strong> - Sets configuration parameters in-flight.</summary>\n\n**Syntax:** `CONFIG SET parameter value [parameter value ...]`\n\n**Description:** Sets configuration parameters in-flight.\n\n**Complexity:** O(N) when N is the number of configuration parameters provided\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/dbsize/\">DBSIZE</a></strong> - Returns the number of keys in the database.</summary>\n\n**Syntax:** `DBSIZE`\n\n**Description:** Returns the number of keys in the database.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/failover/\">FAILOVER</a></strong> - Starts a coordinated failover from a server to one of its replicas.</summary>\n\n**Syntax:** `FAILOVER [TO host port [FORCE]] [ABORT] [TIMEOUT milliseconds]`\n\n**Description:** Starts a coordinated failover from a server to one of its replicas.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/flushall/\">FLUSHALL</a></strong> - Removes all keys from all databases.</summary>\n\n**Syntax:** `FLUSHALL [ASYNC | SYNC]`\n\n**Description:** Removes all keys from all databases.\n\n**Complexity:** O(N) where N is the total number of keys in all databases\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/flushdb/\">FLUSHDB</a></strong> - Remove all keys from the current database.</summary>\n\n**Syntax:** `FLUSHDB [ASYNC | SYNC]`\n\n**Description:** Remove all keys from the current database.\n\n**Complexity:** O(N) where N is the number of keys in the selected database\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hotkeys/\">HOTKEYS</a></strong> - A container for hotkeys tracking commands.</summary>\n\n**Syntax:** `HOTKEYS`\n\n**Description:** A container for hotkeys tracking commands.\n\n**Complexity:** Depends on subcommand.\n\n**Since:** 8.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hotkeys-get/\">HOTKEYS GET</a></strong> - Returns lists of top K hotkeys depending on metrics chosen in HOTKEYS START command.</summary>\n\n**Syntax:** `HOTKEYS GET`\n\n**Description:** Returns lists of top K hotkeys depending on metrics chosen in HOTKEYS START command.\n\n**Complexity:** O(K) where K is the number of hotkeys returned.\n\n**Since:** 8.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hotkeys-reset/\">HOTKEYS RESET</a></strong> - Release the resources used for hotkey tracking.</summary>\n\n**Syntax:** `HOTKEYS RESET`\n\n**Description:** Release the resources used for hotkey tracking.\n\n**Complexity:** O(1)\n\n**Since:** 8.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hotkeys-start/\">HOTKEYS START</a></strong> - Starts hotkeys tracking.</summary>\n\n**Syntax:** `HOTKEYS START METRICS count [CPU] [NET] [COUNT k] [DURATION seconds] [SAMPLE ratio] [SLOTS count slot [slot ...]]`\n\n**Description:** Starts hotkeys tracking.\n\n**Complexity:** O(1)\n\n**Since:** 8.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hotkeys-stop/\">HOTKEYS STOP</a></strong> - Stops hotkeys tracking.</summary>\n\n**Syntax:** `HOTKEYS STOP`\n\n**Description:** Stops hotkeys tracking.\n\n**Complexity:** O(1)\n\n**Since:** 8.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/info/\">INFO</a></strong> - Returns information and statistics about the server.</summary>\n\n**Syntax:** `INFO [section [section ...]]`\n\n**Description:** Returns information and statistics about the server.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lastsave/\">LASTSAVE</a></strong> - Returns the Unix timestamp of the last successful save to disk.</summary>\n\n**Syntax:** `LASTSAVE`\n\n**Description:** Returns the Unix timestamp of the last successful save to disk.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-doctor/\">LATENCY DOCTOR</a></strong> - Returns a human-readable latency analysis report.</summary>\n\n**Syntax:** `LATENCY DOCTOR`\n\n**Description:** Returns a human-readable latency analysis report.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-graph/\">LATENCY GRAPH</a></strong> - Returns a latency graph for an event.</summary>\n\n**Syntax:** `LATENCY GRAPH event`\n\n**Description:** Returns a latency graph for an event.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-histogram/\">LATENCY HISTOGRAM</a></strong> - Returns the cumulative distribution of latencies of a subset or all commands.</summary>\n\n**Syntax:** `LATENCY HISTOGRAM [command [command ...]]`\n\n**Description:** Returns the cumulative distribution of latencies of a subset or all commands.\n\n**Complexity:** O(N) where N is the number of commands with latency information being retrieved.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-history/\">LATENCY HISTORY</a></strong> - Returns timestamp-latency samples for an event.</summary>\n\n**Syntax:** `LATENCY HISTORY event`\n\n**Description:** Returns timestamp-latency samples for an event.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-latest/\">LATENCY LATEST</a></strong> - Returns the latest latency samples for all events.</summary>\n\n**Syntax:** `LATENCY LATEST`\n\n**Description:** Returns the latest latency samples for all events.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-reset/\">LATENCY RESET</a></strong> - Resets the latency data for one or more events.</summary>\n\n**Syntax:** `LATENCY RESET [event [event ...]]`\n\n**Description:** Resets the latency data for one or more events.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lolwut/\">LOLWUT</a></strong> - Displays computer art and the Redis version</summary>\n\n**Syntax:** `LOLWUT [VERSION version]`\n\n**Description:** Displays computer art and the Redis version\n\n**Complexity:** \n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-doctor/\">MEMORY DOCTOR</a></strong> - Outputs a memory problems report.</summary>\n\n**Syntax:** `MEMORY DOCTOR`\n\n**Description:** Outputs a memory problems report.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-malloc-stats/\">MEMORY MALLOC-STATS</a></strong> - Returns the allocator statistics.</summary>\n\n**Syntax:** `MEMORY MALLOC-STATS`\n\n**Description:** Returns the allocator statistics.\n\n**Complexity:** Depends on how much memory is allocated, could be slow\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-purge/\">MEMORY PURGE</a></strong> - Asks the allocator to release memory.</summary>\n\n**Syntax:** `MEMORY PURGE`\n\n**Description:** Asks the allocator to release memory.\n\n**Complexity:** Depends on how much memory is allocated, could be slow\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-stats/\">MEMORY STATS</a></strong> - Returns details about memory usage.</summary>\n\n**Syntax:** `MEMORY STATS`\n\n**Description:** Returns details about memory usage.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-usage/\">MEMORY USAGE</a></strong> - Estimates the memory usage of a key.</summary>\n\n**Syntax:** `MEMORY USAGE key [SAMPLES count]`\n\n**Description:** Estimates the memory usage of a key.\n\n**Complexity:** O(N) where N is the number of samples.\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/module-list/\">MODULE LIST</a></strong> - Returns all loaded modules.</summary>\n\n**Syntax:** `MODULE LIST`\n\n**Description:** Returns all loaded modules.\n\n**Complexity:** O(N) where N is the number of loaded modules.\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/module-load/\">MODULE LOAD</a></strong> - Loads a module.</summary>\n\n**Syntax:** `MODULE LOAD path [arg [arg ...]]`\n\n**Description:** Loads a module.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/module-loadex/\">MODULE LOADEX</a></strong> - Loads a module using extended parameters.</summary>\n\n**Syntax:** `MODULE LOADEX path [CONFIG name value [CONFIG name value ...]]\n  [ARGS args [args ...]]`\n\n**Description:** Loads a module using extended parameters.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/module-unload/\">MODULE UNLOAD</a></strong> - Unloads a module.</summary>\n\n**Syntax:** `MODULE UNLOAD name`\n\n**Description:** Unloads a module.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/monitor/\">MONITOR</a></strong> - Listens for all requests received by the server in real-time.</summary>\n\n**Syntax:** `MONITOR`\n\n**Description:** Listens for all requests received by the server in real-time.\n\n**Complexity:** \n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/psync/\">PSYNC</a></strong> - An internal command used in replication.</summary>\n\n**Syntax:** `PSYNC replicationid offset`\n\n**Description:** An internal command used in replication.\n\n**Complexity:** \n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/replconf/\">REPLCONF</a></strong> - An internal command for configuring the replication stream.</summary>\n\n**Syntax:** `REPLCONF`\n\n**Description:** An internal command for configuring the replication stream.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/replicaof/\">REPLICAOF</a></strong> - Configures a server as replica of another, or promotes it to a master.</summary>\n\n**Syntax:** `REPLICAOF <host port | NO ONE>`\n\n**Description:** Configures a server as replica of another, or promotes it to a master.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/restore-asking/\">RESTORE-ASKING</a></strong> - An internal command for migrating keys in a cluster.</summary>\n\n**Syntax:** `RESTORE-ASKING key ttl serialized-value [REPLACE] [ABSTTL]\n  [IDLETIME seconds] [FREQ frequency]`\n\n**Description:** An internal command for migrating keys in a cluster.\n\n**Complexity:** O(1) to create the new key and additional O(N*M) to reconstruct the serialized value, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1). However for sorted set values the complexity is O(N*M*log(N)) because inserting values into sorted sets is O(log(N)).\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/role/\">ROLE</a></strong> - Returns the replication role.</summary>\n\n**Syntax:** `ROLE`\n\n**Description:** Returns the replication role.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.12\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/save/\">SAVE</a></strong> - Synchronously saves the database(s) to disk.</summary>\n\n**Syntax:** `SAVE`\n\n**Description:** Synchronously saves the database(s) to disk.\n\n**Complexity:** O(N) where N is the total number of keys in all databases\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/shutdown/\">SHUTDOWN</a></strong> - Synchronously saves the database(s) to disk and shuts down the Redis server.</summary>\n\n**Syntax:** `SHUTDOWN [NOSAVE | SAVE] [NOW] [FORCE] [ABORT]`\n\n**Description:** Synchronously saves the database(s) to disk and shuts down the Redis server.\n\n**Complexity:** O(N) when saving, where N is the total number of keys in all databases when saving data, otherwise O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/slaveof/\">SLAVEOF</a></strong> - Sets a Redis server as a replica of another, or promotes it to being a master.</summary>\n\n**Syntax:** `SLAVEOF <host port | NO ONE>`\n\n**Description:** Sets a Redis server as a replica of another, or promotes it to being a master.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/slowlog-get/\">SLOWLOG GET</a></strong> - Returns the slow log's entries.</summary>\n\n**Syntax:** `SLOWLOG GET [count]`\n\n**Description:** Returns the slow log's entries.\n\n**Complexity:** O(N) where N is the number of entries returned\n\n**Since:** 2.2.12\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/slowlog-len/\">SLOWLOG LEN</a></strong> - Returns the number of entries in the slow log.</summary>\n\n**Syntax:** `SLOWLOG LEN`\n\n**Description:** Returns the number of entries in the slow log.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.12\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/slowlog-reset/\">SLOWLOG RESET</a></strong> - Clears all entries from the slow log.</summary>\n\n**Syntax:** `SLOWLOG RESET`\n\n**Description:** Clears all entries from the slow log.\n\n**Complexity:** O(N) where N is the number of entries in the slowlog\n\n**Since:** 2.2.12\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/swapdb/\">SWAPDB</a></strong> - Swaps two Redis databases.</summary>\n\n**Syntax:** `SWAPDB index1 index2`\n\n**Description:** Swaps two Redis databases.\n\n**Complexity:** O(N) where N is the count of clients watching or blocking on keys from both databases.\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sync/\">SYNC</a></strong> - An internal command used in replication.</summary>\n\n**Syntax:** `SYNC`\n\n**Description:** An internal command used in replication.\n\n**Complexity:** \n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/time/\">TIME</a></strong> - Returns the server time.</summary>\n\n**Syntax:** `TIME`\n\n**Description:** Returns the server time.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>"},{"id":"cluster-commands","title":"Cluster commands","role":"content","text":"Cluster commands manage Redis Cluster operations.\n\n<details>\n<summary><strong><a href=\"/commands/asking/\">ASKING</a></strong> - Signals that a cluster client is following an -ASK redirect.</summary>\n\n**Syntax:** `ASKING`\n\n**Description:** Signals that a cluster client is following an -ASK redirect.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-addslots/\">CLUSTER ADDSLOTS</a></strong> - Assigns new hash slots to a node.</summary>\n\n**Syntax:** `CLUSTER ADDSLOTS slot [slot ...]`\n\n**Description:** Assigns new hash slots to a node.\n\n**Complexity:** O(N) where N is the total number of hash slot arguments\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-addslotsrange/\">CLUSTER ADDSLOTSRANGE</a></strong> - Assigns new hash slot ranges to a node.</summary>\n\n**Syntax:** `CLUSTER ADDSLOTSRANGE start-slot end-slot [start-slot end-slot ...]`\n\n**Description:** Assigns new hash slot ranges to a node.\n\n**Complexity:** O(N) where N is the total number of the slots between the start slot and end slot arguments.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-bumpepoch/\">CLUSTER BUMPEPOCH</a></strong> - Advances the cluster config epoch.</summary>\n\n**Syntax:** `CLUSTER BUMPEPOCH`\n\n**Description:** Advances the cluster config epoch.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-count-failure-reports/\">CLUSTER COUNT-FAILURE-REPORTS</a></strong> - Returns the number of active failure reports active for a node.</summary>\n\n**Syntax:** `CLUSTER COUNT-FAILURE-REPORTS node-id`\n\n**Description:** Returns the number of active failure reports active for a node.\n\n**Complexity:** O(N) where N is the number of failure reports\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-countkeysinslot/\">CLUSTER COUNTKEYSINSLOT</a></strong> - Returns the number of keys in a hash slot.</summary>\n\n**Syntax:** `CLUSTER COUNTKEYSINSLOT slot`\n\n**Description:** Returns the number of keys in a hash slot.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-delslots/\">CLUSTER DELSLOTS</a></strong> - Sets hash slots as unbound for a node.</summary>\n\n**Syntax:** `CLUSTER DELSLOTS slot [slot ...]`\n\n**Description:** Sets hash slots as unbound for a node.\n\n**Complexity:** O(N) where N is the total number of hash slot arguments\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-delslotsrange/\">CLUSTER DELSLOTSRANGE</a></strong> - Sets hash slot ranges as unbound for a node.</summary>\n\n**Syntax:** `CLUSTER DELSLOTSRANGE start-slot end-slot [start-slot end-slot ...]`\n\n**Description:** Sets hash slot ranges as unbound for a node.\n\n**Complexity:** O(N) where N is the total number of the slots between the start slot and end slot arguments.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-failover/\">CLUSTER FAILOVER</a></strong> - Forces a replica to perform a manual failover of its master.</summary>\n\n**Syntax:** `CLUSTER FAILOVER [FORCE | TAKEOVER]`\n\n**Description:** Forces a replica to perform a manual failover of its master.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-flushslots/\">CLUSTER FLUSHSLOTS</a></strong> - Deletes all slots information from a node.</summary>\n\n**Syntax:** `CLUSTER FLUSHSLOTS`\n\n**Description:** Deletes all slots information from a node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-forget/\">CLUSTER FORGET</a></strong> - Removes a node from the nodes table.</summary>\n\n**Syntax:** `CLUSTER FORGET node-id`\n\n**Description:** Removes a node from the nodes table.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-getkeysinslot/\">CLUSTER GETKEYSINSLOT</a></strong> - Returns the key names in a hash slot.</summary>\n\n**Syntax:** `CLUSTER GETKEYSINSLOT slot count`\n\n**Description:** Returns the key names in a hash slot.\n\n**Complexity:** O(N) where N is the number of requested keys\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-info/\">CLUSTER INFO</a></strong> - Returns information about the state of a node.</summary>\n\n**Syntax:** `CLUSTER INFO`\n\n**Description:** Returns information about the state of a node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-keyslot/\">CLUSTER KEYSLOT</a></strong> - Returns the hash slot for a key.</summary>\n\n**Syntax:** `CLUSTER KEYSLOT key`\n\n**Description:** Returns the hash slot for a key.\n\n**Complexity:** O(N) where N is the number of bytes in the key\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-links/\">CLUSTER LINKS</a></strong> - Returns a list of all TCP links to and from peer nodes.</summary>\n\n**Syntax:** `CLUSTER LINKS`\n\n**Description:** Returns a list of all TCP links to and from peer nodes.\n\n**Complexity:** O(N) where N is the total number of Cluster nodes\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-meet/\">CLUSTER MEET</a></strong> - Forces a node to handshake with another node.</summary>\n\n**Syntax:** `CLUSTER MEET ip port [cluster-bus-port]`\n\n**Description:** Forces a node to handshake with another node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-migration/\">CLUSTER MIGRATION</a></strong> - Start, monitor, and cancel atomic slot migration tasks.</summary>\n\n**Syntax:** `CLUSTER MIGRATION <IMPORT start-slot end-slot\n  [start-slot end-slot ...] | CANCEL <ID task-id | ALL> |\n  STATUS <[ID task-id] | [ALL]>>`\n\n**Description:** Start, monitor, and cancel atomic slot migration tasks.\n\n**Complexity:** O(N) where N is the total number of the slots between the start slot and end slot arguments.\n\n**Since:** 8.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-myid/\">CLUSTER MYID</a></strong> - Returns the ID of a node.</summary>\n\n**Syntax:** `CLUSTER MYID`\n\n**Description:** Returns the ID of a node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-myshardid/\">CLUSTER MYSHARDID</a></strong> - Returns the shard ID of a node.</summary>\n\n**Syntax:** `CLUSTER MYSHARDID`\n\n**Description:** Returns the shard ID of a node.\n\n**Complexity:** O(1)\n\n**Since:** 7.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-nodes/\">CLUSTER NODES</a></strong> - Returns the cluster configuration for a node.</summary>\n\n**Syntax:** `CLUSTER NODES`\n\n**Description:** Returns the cluster configuration for a node.\n\n**Complexity:** O(N) where N is the total number of Cluster nodes\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-replicas/\">CLUSTER REPLICAS</a></strong> - Lists the replica nodes of a master node.</summary>\n\n**Syntax:** `CLUSTER REPLICAS node-id`\n\n**Description:** Lists the replica nodes of a master node.\n\n**Complexity:** O(N) where N is the number of replicas.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-replicate/\">CLUSTER REPLICATE</a></strong> - Configure a node as replica of a master node.</summary>\n\n**Syntax:** `CLUSTER REPLICATE node-id`\n\n**Description:** Configure a node as replica of a master node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-reset/\">CLUSTER RESET</a></strong> - Resets a node.</summary>\n\n**Syntax:** `CLUSTER RESET [HARD | SOFT]`\n\n**Description:** Resets a node.\n\n**Complexity:** O(N) where N is the number of known nodes. The command may execute a FLUSHALL as a side effect.\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-saveconfig/\">CLUSTER SAVECONFIG</a></strong> - Forces a node to save the cluster configuration to disk.</summary>\n\n**Syntax:** `CLUSTER SAVECONFIG`\n\n**Description:** Forces a node to save the cluster configuration to disk.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-set-config-epoch/\">CLUSTER SET-CONFIG-EPOCH</a></strong> - Sets the configuration epoch for a new node.</summary>\n\n**Syntax:** `CLUSTER SET-CONFIG-EPOCH config-epoch`\n\n**Description:** Sets the configuration epoch for a new node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-setslot/\">CLUSTER SETSLOT</a></strong> - Binds a hash slot to a node.</summary>\n\n**Syntax:** `CLUSTER SETSLOT slot <IMPORTING node-id | MIGRATING node-id |\n  NODE node-id | STABLE>`\n\n**Description:** Binds a hash slot to a node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-shards/\">CLUSTER SHARDS</a></strong> - Returns the mapping of cluster slots to shards.</summary>\n\n**Syntax:** `CLUSTER SHARDS`\n\n**Description:** Returns the mapping of cluster slots to shards.\n\n**Complexity:** O(N) where N is the total number of cluster nodes\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-slaves/\">CLUSTER SLAVES</a></strong> - Lists the replica nodes of a master node.</summary>\n\n**Syntax:** `CLUSTER SLAVES node-id`\n\n**Description:** Lists the replica nodes of a master node.\n\n**Complexity:** O(N) where N is the number of replicas.\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-slot-stats/\">CLUSTER SLOT-STATS</a></strong> - Return an array of slot usage statistics for slots assigned to the current node.</summary>\n\n**Syntax:** `CLUSTER SLOT-STATS <SLOTSRANGE start-slot end-slot | ORDERBY metric\n  [LIMIT limit] [ASC | DESC]>`\n\n**Description:** Return an array of slot usage statistics for slots assigned to the current node.\n\n**Complexity:** O(N) where N is the total number of slots based on arguments. O(N*log(N)) with ORDERBY subcommand.\n\n**Since:** 8.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-slots/\">CLUSTER SLOTS</a></strong> - Returns the mapping of cluster slots to nodes.</summary>\n\n**Syntax:** `CLUSTER SLOTS`\n\n**Description:** Returns the mapping of cluster slots to nodes.\n\n**Complexity:** O(N) where N is the total number of Cluster nodes\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/readonly/\">READONLY</a></strong> - Enables read-only queries for a connection to a Redis Cluster replica node.</summary>\n\n**Syntax:** `READONLY`\n\n**Description:** Enables read-only queries for a connection to a Redis Cluster replica node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/readwrite/\">READWRITE</a></strong> - Enables read-write queries for a connection to a Reids Cluster replica node.</summary>\n\n**Syntax:** `READWRITE`\n\n**Description:** Enables read-write queries for a connection to a Reids Cluster replica node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>"},{"id":"generic-commands","title":"Generic commands","role":"content","text":"Generic commands work across all data types.\n\n<details>\n<summary><strong><a href=\"/commands/copy/\">COPY</a></strong> - Copies the value of a key to a new key.</summary>\n\n**Syntax:** `COPY source destination [DB destination-db] [REPLACE]`\n\n**Description:** Copies the value of a key to a new key.\n\n**Complexity:** O(N) worst case for collections, where N is the number of nested items. O(1) for string values.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/del/\">DEL</a></strong> - Deletes one or more keys.</summary>\n\n**Syntax:** `DEL key [key ...]`\n\n**Description:** Deletes one or more keys.\n\n**Complexity:** O(N) where N is the number of keys that will be removed. When a key to remove holds a value other than a string, the individual complexity for this key is O(M) where M is the number of elements in the list, set, sorted set or hash. Removing a single key that holds a string value is O(1).\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/dump/\">DUMP</a></strong> - Returns a serialized representation of the value stored at a key.</summary>\n\n**Syntax:** `DUMP key`\n\n**Description:** Returns a serialized representation of the value stored at a key.\n\n**Complexity:** O(1) to access the key and additional O(N*M) to serialize it, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1).\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/exists/\">EXISTS</a></strong> - Determines whether one or more keys exist.</summary>\n\n**Syntax:** `EXISTS key [key ...]`\n\n**Description:** Determines whether one or more keys exist.\n\n**Complexity:** O(N) where N is the number of keys to check.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/expire/\">EXPIRE</a></strong> - Sets the expiration time of a key in seconds.</summary>\n\n**Syntax:** `EXPIRE key seconds [NX | XX | GT | LT]`\n\n**Description:** Sets the expiration time of a key in seconds.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/expireat/\">EXPIREAT</a></strong> - Sets the expiration time of a key to a Unix timestamp.</summary>\n\n**Syntax:** `EXPIREAT key unix-time-seconds [NX | XX | GT | LT]`\n\n**Description:** Sets the expiration time of a key to a Unix timestamp.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/expiretime/\">EXPIRETIME</a></strong> - Returns the expiration time of a key as a Unix timestamp.</summary>\n\n**Syntax:** `EXPIRETIME key`\n\n**Description:** Returns the expiration time of a key as a Unix timestamp.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/keys/\">KEYS</a></strong> - Returns all key names that match a pattern.</summary>\n\n**Syntax:** `KEYS pattern`\n\n**Description:** Returns all key names that match a pattern.\n\n**Complexity:** O(N) with N being the number of keys in the database, under the assumption that the key names in the database and the given pattern have limited length.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/migrate/\">MIGRATE</a></strong> - Atomically transfers a key from one Redis instance to another.</summary>\n\n**Syntax:** `MIGRATE host port <key | \"\"> destination-db timeout [COPY] [REPLACE]\n  [AUTH password | AUTH2 username password] [KEYS key [key ...]]`\n\n**Description:** Atomically transfers a key from one Redis instance to another.\n\n**Complexity:** This command actually executes a DUMP+DEL in the source instance, and a RESTORE in the target instance. See the pages of these commands for time complexity. Also an O(N) data transfer between the two instances is performed.\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/move/\">MOVE</a></strong> - Moves a key to another database.</summary>\n\n**Syntax:** `MOVE key db`\n\n**Description:** Moves a key to another database.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/object-encoding/\">OBJECT ENCODING</a></strong> - Returns the internal encoding of a Redis object.</summary>\n\n**Syntax:** `OBJECT ENCODING key`\n\n**Description:** Returns the internal encoding of a Redis object.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.3\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/object-freq/\">OBJECT FREQ</a></strong> - Returns the logarithmic access frequency counter of a Redis object.</summary>\n\n**Syntax:** `OBJECT FREQ key`\n\n**Description:** Returns the logarithmic access frequency counter of a Redis object.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/object-idletime/\">OBJECT IDLETIME</a></strong> - Returns the time since the last access to a Redis object.</summary>\n\n**Syntax:** `OBJECT IDLETIME key`\n\n**Description:** Returns the time since the last access to a Redis object.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.3\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/object-refcount/\">OBJECT REFCOUNT</a></strong> - Returns the reference count of a value of a key.</summary>\n\n**Syntax:** `OBJECT REFCOUNT key`\n\n**Description:** Returns the reference count of a value of a key.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.3\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/persist/\">PERSIST</a></strong> - Removes the expiration time of a key.</summary>\n\n**Syntax:** `PERSIST key`\n\n**Description:** Removes the expiration time of a key.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pexpire/\">PEXPIRE</a></strong> - Sets the expiration time of a key in milliseconds.</summary>\n\n**Syntax:** `PEXPIRE key milliseconds [NX | XX | GT | LT]`\n\n**Description:** Sets the expiration time of a key in milliseconds.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pexpireat/\">PEXPIREAT</a></strong> - Sets the expiration time of a key to a Unix milliseconds timestamp.</summary>\n\n**Syntax:** `PEXPIREAT key unix-time-milliseconds [NX | XX | GT | LT]`\n\n**Description:** Sets the expiration time of a key to a Unix milliseconds timestamp.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pexpiretime/\">PEXPIRETIME</a></strong> - Returns the expiration time of a key as a Unix milliseconds timestamp.</summary>\n\n**Syntax:** `PEXPIRETIME key`\n\n**Description:** Returns the expiration time of a key as a Unix milliseconds timestamp.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pttl/\">PTTL</a></strong> - Returns the expiration time in milliseconds of a key.</summary>\n\n**Syntax:** `PTTL key`\n\n**Description:** Returns the expiration time in milliseconds of a key.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/randomkey/\">RANDOMKEY</a></strong> - Returns a random key name from the database.</summary>\n\n**Syntax:** `RANDOMKEY`\n\n**Description:** Returns a random key name from the database.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rename/\">RENAME</a></strong> - Renames a key and overwrites the destination.</summary>\n\n**Syntax:** `RENAME key newkey`\n\n**Description:** Renames a key and overwrites the destination.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/renamenx/\">RENAMENX</a></strong> - Renames a key only when the target key name doesn't exist.</summary>\n\n**Syntax:** `RENAMENX key newkey`\n\n**Description:** Renames a key only when the target key name doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/restore/\">RESTORE</a></strong> - Creates a key from the serialized representation of a value.</summary>\n\n**Syntax:** `RESTORE key ttl serialized-value [REPLACE] [ABSTTL]\n  [IDLETIME seconds] [FREQ frequency]`\n\n**Description:** Creates a key from the serialized representation of a value.\n\n**Complexity:** O(1) to create the new key and additional O(N*M) to reconstruct the serialized value, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1). However for sorted set values the complexity is O(N*M*log(N)) because inserting values into sorted sets is O(log(N)).\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/scan/\">SCAN</a></strong> - Iterates over the key names in the database.</summary>\n\n**Syntax:** `SCAN cursor [MATCH pattern] [COUNT count] [TYPE type]`\n\n**Description:** Iterates over the key names in the database.\n\n**Complexity:** O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sort/\">SORT</a></strong> - Sorts the elements in a list, a set, or a sorted set, optionally storing the result.</summary>\n\n**Syntax:** `SORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern\n  ...]] [ASC | DESC] [ALPHA] [STORE destination]`\n\n**Description:** Sorts the elements in a list, a set, or a sorted set, optionally storing the result.\n\n**Complexity:** O(N+M*log(M)) where N is the number of elements in the list or set to sort, and M the number of returned elements. When the elements are not sorted, complexity is O(N).\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sort_ro/\">SORT_RO</a></strong> - Returns the sorted elements of a list, a set, or a sorted set.</summary>\n\n**Syntax:** `SORT RO key [BY pattern] [LIMIT offset count] [GET pattern [GET\n  pattern ...]] [ASC | DESC] [ALPHA]`\n\n**Description:** Returns the sorted elements of a list, a set, or a sorted set.\n\n**Complexity:** O(N+M*log(M)) where N is the number of elements in the list or set to sort, and M the number of returned elements. When the elements are not sorted, complexity is O(N).\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/touch/\">TOUCH</a></strong> - Returns the number of existing keys out of those specified after updating the time they were last accessed.</summary>\n\n**Syntax:** `TOUCH key [key ...]`\n\n**Description:** Returns the number of existing keys out of those specified after updating the time they were last accessed.\n\n**Complexity:** O(N) where N is the number of keys that will be touched.\n\n**Since:** 3.2.1\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ttl/\">TTL</a></strong> - Returns the expiration time in seconds of a key.</summary>\n\n**Syntax:** `TTL key`\n\n**Description:** Returns the expiration time in seconds of a key.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/type/\">TYPE</a></strong> - Determines the type of value stored at a key.</summary>\n\n**Syntax:** `TYPE key`\n\n**Description:** Determines the type of value stored at a key.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/unlink/\">UNLINK</a></strong> - Asynchronously deletes one or more keys.</summary>\n\n**Syntax:** `UNLINK key [key ...]`\n\n**Description:** Asynchronously deletes one or more keys.\n\n**Complexity:** O(1) for each key removed regardless of its size. Then the command does O(N) work in a different thread in order to reclaim memory, where N is the number of allocations the deleted objects where composed of.\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/wait/\">WAIT</a></strong> - Blocks until the asynchronous replication of all preceding write commands sent by the connection is completed.</summary>\n\n**Syntax:** `WAIT numreplicas timeout`\n\n**Description:** Blocks until the asynchronous replication of all preceding write commands sent by the connection is completed.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/waitaof/\">WAITAOF</a></strong> - Blocks until all of the preceding write commands sent by the connection are written to the append-only file of the master and/or replicas.</summary>\n\n**Syntax:** `WAITAOF numlocal numreplicas timeout`\n\n**Description:** Blocks until all of the preceding write commands sent by the connection are written to the append-only file of the master and/or replicas.\n\n**Complexity:** O(1)\n\n**Since:** 7.2.0\n\n</details>"}],"examples":[]}
{"schema_version":2,"id":"commands/redis-8-2-commands","title":"Redis 8.2 Commands Reference","url":"https://redis.io/docs/latest/commands/redis-8-2-commands/","summary":"Complete list of all Redis commands available in version 8.2, organized by functional group","tags":["docs","develop","stack","oss","rs","rc","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"8ff1980628da9468c16265e4dda580bd364e081b6c6f683dbb3f6de175d7a9be","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This page provides a comprehensive reference of all Redis commands available in Redis 8.2, organized by functional group. Each command includes its description and syntax in a collapsible section for easy navigation.\n\n> [!NOTE]\n> Redis 8.2 includes all commands from previous versions plus new commands introduced in 8.2. Commands marked with **⭐ New in 8.2** were added in this release.\n>\n> Command page links take you to the *current implementation* of each command."},{"id":"quick-navigation","title":"Quick Navigation","role":"content","text":"- [String commands](#string-commands)\n- [Hash commands](#hash-commands)\n- [List commands](#list-commands)\n- [Set commands](#set-commands)\n- [Sorted set commands](#sorted-set-commands)\n- [Stream commands](#stream-commands)\n- [Bitmap commands](#bitmap-commands)\n- [HyperLogLog commands](#hyperloglog-commands)\n- [Geospatial commands](#geospatial-commands)\n- [JSON commands](#json-commands)\n- [Search commands](#search-commands)\n- [Time series commands](#time-series-commands)\n- [Vector set commands](#vector-set-commands)\n- [Pub/Sub commands](#pubsub-commands)\n- [Transaction commands](#transaction-commands)\n- [Scripting commands](#scripting-commands)\n- [Connection commands](#connection-commands)\n- [Server commands](#server-commands)\n- [Cluster commands](#cluster-commands)\n- [Generic commands](#generic-commands)\n\n---"},{"id":"string-commands","title":"String commands","role":"content","text":"String commands operate on string values, the most basic Redis data type.\n\n<details>\n<summary><strong><a href=\"/commands/append/\">APPEND</a></strong> - Appends a string to the value of a key. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `APPEND key value`\n\n**Description:** Appends a string to the value of a key. Creates the key if it doesn't exist.\n\n**Complexity:** O(1). The amortized time complexity is O(1) assuming the appended value is small and the already present value is of any size, since the dynamic string library used by Redis will double the free space available on every reallocation.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/decr/\">DECR</a></strong> - Decrements the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `DECR key`\n\n**Description:** Decrements the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/decrby/\">DECRBY</a></strong> - Decrements a number from the integer value of a key. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `DECRBY key decrement`\n\n**Description:** Decrements a number from the integer value of a key. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/get/\">GET</a></strong> - Returns the string value of a key.</summary>\n\n**Syntax:** `GET key`\n\n**Description:** Returns the string value of a key.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getdel/\">GETDEL</a></strong> - Returns the string value of a key after deleting the key.</summary>\n\n**Syntax:** `GETDEL key`\n\n**Description:** Returns the string value of a key after deleting the key.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getex/\">GETEX</a></strong> - Returns the string value of a key after setting its expiration time.</summary>\n\n**Syntax:** `GETEX key [EX seconds | PX milliseconds | EXAT unix-time-seconds |\n  PXAT unix-time-milliseconds | PERSIST]`\n\n**Description:** Returns the string value of a key after setting its expiration time.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getrange/\">GETRANGE</a></strong> - Returns a substring of the string stored at a key.</summary>\n\n**Syntax:** `GETRANGE key start end`\n\n**Description:** Returns a substring of the string stored at a key.\n\n**Complexity:** O(N) where N is the length of the returned string. The complexity is ultimately determined by the returned length, but because creating a substring from an existing string is very cheap, it can be considered O(1) for small strings.\n\n**Since:** 2.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getset/\">GETSET</a></strong> - Returns the previous string value of a key after setting it to a new value.</summary>\n\n**Syntax:** `GETSET key value`\n\n**Description:** Returns the previous string value of a key after setting it to a new value.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/incr/\">INCR</a></strong> - Increments the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `INCR key`\n\n**Description:** Increments the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/incrby/\">INCRBY</a></strong> - Increments the integer value of a key by a number. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `INCRBY key increment`\n\n**Description:** Increments the integer value of a key by a number. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/incrbyfloat/\">INCRBYFLOAT</a></strong> - Increment the floating point value of a key by a number. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `INCRBYFLOAT key increment`\n\n**Description:** Increment the floating point value of a key by a number. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lcs/\">LCS</a></strong> - Finds the longest common substring.</summary>\n\n**Syntax:** `LCS key1 key2 [LEN] [IDX] [MINMATCHLEN min-match-len] [WITHMATCHLEN]`\n\n**Description:** Finds the longest common substring.\n\n**Complexity:** O(N*M) where N and M are the lengths of s1 and s2, respectively\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/mget/\">MGET</a></strong> - Atomically returns the string values of one or more keys.</summary>\n\n**Syntax:** `MGET key [key ...]`\n\n**Description:** Atomically returns the string values of one or more keys.\n\n**Complexity:** O(N) where N is the number of keys to retrieve.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/mset/\">MSET</a></strong> - Atomically creates or modifies the string values of one or more keys.</summary>\n\n**Syntax:** `MSET key value [key value ...]`\n\n**Description:** Atomically creates or modifies the string values of one or more keys.\n\n**Complexity:** O(N) where N is the number of keys to set.\n\n**Since:** 1.0.1\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/msetnx/\">MSETNX</a></strong> - Atomically modifies the string values of one or more keys only when all keys don't exist.</summary>\n\n**Syntax:** `MSETNX key value [key value ...]`\n\n**Description:** Atomically modifies the string values of one or more keys only when all keys don't exist.\n\n**Complexity:** O(N) where N is the number of keys to set.\n\n**Since:** 1.0.1\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/psetex/\">PSETEX</a></strong> - Sets both string value and expiration time in milliseconds of a key. The key is created if it doesn't exist.</summary>\n\n**Syntax:** `PSETEX key milliseconds value`\n\n**Description:** Sets both string value and expiration time in milliseconds of a key. The key is created if it doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/set/\">SET</a></strong> - Sets the string value of a key, ignoring its type. The key is created if it doesn't exist.</summary>\n\n**Syntax:** `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]`\n\n**Description:** Sets the string value of a key, ignoring its type. The key is created if it doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/setex/\">SETEX</a></strong> - Sets the string value and expiration time of a key. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `SETEX key seconds value`\n\n**Description:** Sets the string value and expiration time of a key. Creates the key if it doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/setnx/\">SETNX</a></strong> - Set the string value of a key only when the key doesn't exist.</summary>\n\n**Syntax:** `SETNX key value`\n\n**Description:** Set the string value of a key only when the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/setrange/\">SETRANGE</a></strong> - Overwrites a part of a string value with another by an offset. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `SETRANGE key offset value`\n\n**Description:** Overwrites a part of a string value with another by an offset. Creates the key if it doesn't exist.\n\n**Complexity:** O(1), not counting the time taken to copy the new string in place. Usually, this string is very small so the amortized complexity is O(1). Otherwise, complexity is O(M) with M being the length of the value argument.\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/strlen/\">STRLEN</a></strong> - Returns the length of a string value.</summary>\n\n**Syntax:** `STRLEN key`\n\n**Description:** Returns the length of a string value.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/substr/\">SUBSTR</a></strong> - Returns a substring from a string value.</summary>\n\n**Syntax:** `SUBSTR key start end`\n\n**Description:** Returns a substring from a string value.\n\n**Complexity:** O(N) where N is the length of the returned string. The complexity is ultimately determined by the returned length, but because creating a substring from an existing string is very cheap, it can be considered O(1) for small strings.\n\n**Since:** 1.0.0\n\n</details>"},{"id":"hash-commands","title":"Hash commands","role":"content","text":"Hash commands operate on hash data structures, which map string fields to string values.\n\n<details>\n<summary><strong><a href=\"/commands/hdel/\">HDEL</a></strong> - Deletes one or more fields and their values from a hash. Deletes the hash if no fields remain.</summary>\n\n**Syntax:** `HDEL key field [field ...]`\n\n**Description:** Deletes one or more fields and their values from a hash. Deletes the hash if no fields remain.\n\n**Complexity:** O(N) where N is the number of fields to be removed.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hexists/\">HEXISTS</a></strong> - Determines whether a field exists in a hash.</summary>\n\n**Syntax:** `HEXISTS key field`\n\n**Description:** Determines whether a field exists in a hash.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hexpire/\">HEXPIRE</a></strong> - Set expiry for hash field using relative time to expire (seconds)</summary>\n\n**Syntax:** `HEXPIRE key seconds [NX | XX | GT | LT] FIELDS numfields field\n  [field ...]`\n\n**Description:** Set expiry for hash field using relative time to expire (seconds)\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hexpireat/\">HEXPIREAT</a></strong> - Set expiry for hash field using an absolute Unix timestamp (seconds)</summary>\n\n**Syntax:** `HEXPIREAT key unix-time-seconds [NX | XX | GT | LT] FIELDS numfields\n  field [field ...]`\n\n**Description:** Set expiry for hash field using an absolute Unix timestamp (seconds)\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hexpiretime/\">HEXPIRETIME</a></strong> - Returns the expiration time of a hash field as a Unix timestamp, in seconds.</summary>\n\n**Syntax:** `HEXPIRETIME key FIELDS numfields field [field ...]`\n\n**Description:** Returns the expiration time of a hash field as a Unix timestamp, in seconds.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hget/\">HGET</a></strong> - Returns the value of a field in a hash.</summary>\n\n**Syntax:** `HGET key field`\n\n**Description:** Returns the value of a field in a hash.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hgetall/\">HGETALL</a></strong> - Returns all fields and values in a hash.</summary>\n\n**Syntax:** `HGETALL key`\n\n**Description:** Returns all fields and values in a hash.\n\n**Complexity:** O(N) where N is the size of the hash.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hgetdel/\">HGETDEL</a></strong> - Returns the value of a field and deletes it from the hash.</summary>\n\n**Syntax:** `HGETDEL key FIELDS numfields field [field ...]`\n\n**Description:** Returns the value of a field and deletes it from the hash.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hgetex/\">HGETEX</a></strong> - Get the value of one or more fields of a given hash key, and optionally set their expiration.</summary>\n\n**Syntax:** `HGETEX key [EX seconds | PX milliseconds | EXAT unix-time-seconds |\n  PXAT unix-time-milliseconds | PERSIST] FIELDS numfields field\n  [field ...]`\n\n**Description:** Get the value of one or more fields of a given hash key, and optionally set their expiration.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hincrby/\">HINCRBY</a></strong> - Increments the integer value of a field in a hash by a number. Uses 0 as initial value if the field doesn't exist.</summary>\n\n**Syntax:** `HINCRBY key field increment`\n\n**Description:** Increments the integer value of a field in a hash by a number. Uses 0 as initial value if the field doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hincrbyfloat/\">HINCRBYFLOAT</a></strong> - Increments the floating point value of a field by a number. Uses 0 as initial value if the field doesn't exist.</summary>\n\n**Syntax:** `HINCRBYFLOAT key field increment`\n\n**Description:** Increments the floating point value of a field by a number. Uses 0 as initial value if the field doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hkeys/\">HKEYS</a></strong> - Returns all fields in a hash.</summary>\n\n**Syntax:** `HKEYS key`\n\n**Description:** Returns all fields in a hash.\n\n**Complexity:** O(N) where N is the size of the hash.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hlen/\">HLEN</a></strong> - Returns the number of fields in a hash.</summary>\n\n**Syntax:** `HLEN key`\n\n**Description:** Returns the number of fields in a hash.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hmget/\">HMGET</a></strong> - Returns the values of all fields in a hash.</summary>\n\n**Syntax:** `HMGET key field [field ...]`\n\n**Description:** Returns the values of all fields in a hash.\n\n**Complexity:** O(N) where N is the number of fields being requested.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hmset/\">HMSET</a></strong> - Sets the values of multiple fields.</summary>\n\n**Syntax:** `HMSET key field value [field value ...]`\n\n**Description:** Sets the values of multiple fields.\n\n**Complexity:** O(N) where N is the number of fields being set.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hpersist/\">HPERSIST</a></strong> - Removes the expiration time for each specified field</summary>\n\n**Syntax:** `HPERSIST key FIELDS numfields field [field ...]`\n\n**Description:** Removes the expiration time for each specified field\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hpexpire/\">HPEXPIRE</a></strong> - Set expiry for hash field using relative time to expire (milliseconds)</summary>\n\n**Syntax:** `HPEXPIRE key milliseconds [NX | XX | GT | LT] FIELDS numfields field\n  [field ...]`\n\n**Description:** Set expiry for hash field using relative time to expire (milliseconds)\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hpexpireat/\">HPEXPIREAT</a></strong> - Set expiry for hash field using an absolute Unix timestamp (milliseconds)</summary>\n\n**Syntax:** `HPEXPIREAT key unix-time-milliseconds [NX | XX | GT | LT]\n  FIELDS numfields field [field ...]`\n\n**Description:** Set expiry for hash field using an absolute Unix timestamp (milliseconds)\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hpexpiretime/\">HPEXPIRETIME</a></strong> - Returns the expiration time of a hash field as a Unix timestamp, in msec.</summary>\n\n**Syntax:** `HPEXPIRETIME key FIELDS numfields field [field ...]`\n\n**Description:** Returns the expiration time of a hash field as a Unix timestamp, in msec.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hpttl/\">HPTTL</a></strong> - Returns the TTL in milliseconds of a hash field.</summary>\n\n**Syntax:** `HPTTL key FIELDS numfields field [field ...]`\n\n**Description:** Returns the TTL in milliseconds of a hash field.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hrandfield/\">HRANDFIELD</a></strong> - Returns one or more random fields from a hash.</summary>\n\n**Syntax:** `HRANDFIELD key [count [WITHVALUES]]`\n\n**Description:** Returns one or more random fields from a hash.\n\n**Complexity:** O(N) where N is the number of fields returned\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hscan/\">HSCAN</a></strong> - Iterates over fields and values of a hash.</summary>\n\n**Syntax:** `HSCAN key cursor [MATCH pattern] [COUNT count] [NOVALUES]`\n\n**Description:** Iterates over fields and values of a hash.\n\n**Complexity:** O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hset/\">HSET</a></strong> - Creates or modifies the value of a field in a hash.</summary>\n\n**Syntax:** `HSET key field value [field value ...]`\n\n**Description:** Creates or modifies the value of a field in a hash.\n\n**Complexity:** O(1) for each field/value pair added, so O(N) to add N field/value pairs when the command is called with multiple field/value pairs.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hsetex/\">HSETEX</a></strong> - Set the value of one or more fields of a given hash key, and optionally set their expiration.</summary>\n\n**Syntax:** `HSETEX key [FNX | FXX] [EX seconds | PX milliseconds |\n  EXAT unix-time-seconds | PXAT unix-time-milliseconds | KEEPTTL]\n  FIELDS numfields field value [field value ...]`\n\n**Description:** Set the value of one or more fields of a given hash key, and optionally set their expiration.\n\n**Complexity:** O(N) where N is the number of fields being set.\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hsetnx/\">HSETNX</a></strong> - Sets the value of a field in a hash only when the field doesn't exist.</summary>\n\n**Syntax:** `HSETNX key field value`\n\n**Description:** Sets the value of a field in a hash only when the field doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hstrlen/\">HSTRLEN</a></strong> - Returns the length of the value of a field.</summary>\n\n**Syntax:** `HSTRLEN key field`\n\n**Description:** Returns the length of the value of a field.\n\n**Complexity:** O(1)\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/httl/\">HTTL</a></strong> - Returns the TTL in seconds of a hash field.</summary>\n\n**Syntax:** `HTTL key FIELDS numfields field [field ...]`\n\n**Description:** Returns the TTL in seconds of a hash field.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hvals/\">HVALS</a></strong> - Returns all values in a hash.</summary>\n\n**Syntax:** `HVALS key`\n\n**Description:** Returns all values in a hash.\n\n**Complexity:** O(N) where N is the size of the hash.\n\n**Since:** 2.0.0\n\n</details>"},{"id":"list-commands","title":"List commands","role":"content","text":"List commands operate on lists of strings, ordered by insertion order.\n\n<details>\n<summary><strong><a href=\"/commands/blmove/\">BLMOVE</a></strong> - Pops an element from a list, pushes it to another list and returns it. Blocks until an element is available otherwise. Deletes the list if the last element was moved.</summary>\n\n**Syntax:** `BLMOVE source destination <LEFT | RIGHT> <LEFT | RIGHT> timeout`\n\n**Description:** Pops an element from a list, pushes it to another list and returns it. Blocks until an element is available otherwise. Deletes the list if the last element was moved.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/blmpop/\">BLMPOP</a></strong> - Pops the first element from one of multiple lists. Blocks until an element is available otherwise. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `BLMPOP timeout numkeys key [key ...] <LEFT | RIGHT> [COUNT count]`\n\n**Description:** Pops the first element from one of multiple lists. Blocks until an element is available otherwise. Deletes the list if the last element was popped.\n\n**Complexity:** O(N+M) where N is the number of provided keys and M is the number of elements returned.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/blpop/\">BLPOP</a></strong> - Removes and returns the first element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `BLPOP key [key ...] timeout`\n\n**Description:** Removes and returns the first element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.\n\n**Complexity:** O(N) where N is the number of provided keys.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/brpop/\">BRPOP</a></strong> - Removes and returns the last element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `BRPOP key [key ...] timeout`\n\n**Description:** Removes and returns the last element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.\n\n**Complexity:** O(N) where N is the number of provided keys.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/brpoplpush/\">BRPOPLPUSH</a></strong> - Pops an element from a list, pushes it to another list and returns it. Block until an element is available otherwise. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `BRPOPLPUSH source destination timeout`\n\n**Description:** Pops an element from a list, pushes it to another list and returns it. Block until an element is available otherwise. Deletes the list if the last element was popped.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lindex/\">LINDEX</a></strong> - Returns an element from a list by its index.</summary>\n\n**Syntax:** `LINDEX key index`\n\n**Description:** Returns an element from a list by its index.\n\n**Complexity:** O(N) where N is the number of elements to traverse to get to the element at index. This makes asking for the first or the last element of the list O(1).\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/linsert/\">LINSERT</a></strong> - Inserts an element before or after another element in a list.</summary>\n\n**Syntax:** `LINSERT key <BEFORE | AFTER> pivot element`\n\n**Description:** Inserts an element before or after another element in a list.\n\n**Complexity:** O(N) where N is the number of elements to traverse before seeing the value pivot. This means that inserting somewhere on the left end on the list (head) can be considered O(1) and inserting somewhere on the right end (tail) is O(N).\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/llen/\">LLEN</a></strong> - Returns the length of a list.</summary>\n\n**Syntax:** `LLEN key`\n\n**Description:** Returns the length of a list.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lmove/\">LMOVE</a></strong> - Returns an element after popping it from one list and pushing it to another. Deletes the list if the last element was moved.</summary>\n\n**Syntax:** `LMOVE source destination <LEFT | RIGHT> <LEFT | RIGHT>`\n\n**Description:** Returns an element after popping it from one list and pushing it to another. Deletes the list if the last element was moved.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lmpop/\">LMPOP</a></strong> - Returns multiple elements from a list after removing them. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `LMPOP numkeys key [key ...] <LEFT | RIGHT> [COUNT count]`\n\n**Description:** Returns multiple elements from a list after removing them. Deletes the list if the last element was popped.\n\n**Complexity:** O(N+M) where N is the number of provided keys and M is the number of elements returned.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lpop/\">LPOP</a></strong> - Returns the first elements in a list after removing it. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `LPOP key [count]`\n\n**Description:** Returns the first elements in a list after removing it. Deletes the list if the last element was popped.\n\n**Complexity:** O(N) where N is the number of elements returned\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lpos/\">LPOS</a></strong> - Returns the index of matching elements in a list.</summary>\n\n**Syntax:** `LPOS key element [RANK rank] [COUNT num-matches] [MAXLEN len]`\n\n**Description:** Returns the index of matching elements in a list.\n\n**Complexity:** O(N) where N is the number of elements in the list, for the average case. When searching for elements near the head or the tail of the list, or when the MAXLEN option is provided, the command may run in constant time.\n\n**Since:** 6.0.6\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lpush/\">LPUSH</a></strong> - Prepends one or more elements to a list. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `LPUSH key element [element ...]`\n\n**Description:** Prepends one or more elements to a list. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lpushx/\">LPUSHX</a></strong> - Prepends one or more elements to a list only when the list exists.</summary>\n\n**Syntax:** `LPUSHX key element [element ...]`\n\n**Description:** Prepends one or more elements to a list only when the list exists.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lrange/\">LRANGE</a></strong> - Returns a range of elements from a list.</summary>\n\n**Syntax:** `LRANGE key start stop`\n\n**Description:** Returns a range of elements from a list.\n\n**Complexity:** O(S+N) where S is the distance of start offset from HEAD for small lists, from nearest end (HEAD or TAIL) for large lists; and N is the number of elements in the specified range.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lrem/\">LREM</a></strong> - Removes elements from a list. Deletes the list if the last element was removed.</summary>\n\n**Syntax:** `LREM key count element`\n\n**Description:** Removes elements from a list. Deletes the list if the last element was removed.\n\n**Complexity:** O(N+M) where N is the length of the list and M is the number of elements removed.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lset/\">LSET</a></strong> - Sets the value of an element in a list by its index.</summary>\n\n**Syntax:** `LSET key index element`\n\n**Description:** Sets the value of an element in a list by its index.\n\n**Complexity:** O(N) where N is the length of the list. Setting either the first or the last element of the list is O(1).\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ltrim/\">LTRIM</a></strong> - Removes elements from both ends a list. Deletes the list if all elements were trimmed.</summary>\n\n**Syntax:** `LTRIM key start stop`\n\n**Description:** Removes elements from both ends a list. Deletes the list if all elements were trimmed.\n\n**Complexity:** O(N) where N is the number of elements to be removed by the operation.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rpop/\">RPOP</a></strong> - Returns and removes the last elements of a list. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `RPOP key [count]`\n\n**Description:** Returns and removes the last elements of a list. Deletes the list if the last element was popped.\n\n**Complexity:** O(N) where N is the number of elements returned\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rpoplpush/\">RPOPLPUSH</a></strong> - Returns the last element of a list after removing and pushing it to another list. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `RPOPLPUSH source destination`\n\n**Description:** Returns the last element of a list after removing and pushing it to another list. Deletes the list if the last element was popped.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rpush/\">RPUSH</a></strong> - Appends one or more elements to a list. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `RPUSH key element [element ...]`\n\n**Description:** Appends one or more elements to a list. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rpushx/\">RPUSHX</a></strong> - Appends an element to a list only when the list exists.</summary>\n\n**Syntax:** `RPUSHX key element [element ...]`\n\n**Description:** Appends an element to a list only when the list exists.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 2.2.0\n\n</details>"},{"id":"set-commands","title":"Set commands","role":"content","text":"Set commands operate on unordered collections of unique strings.\n\n<details>\n<summary><strong><a href=\"/commands/sadd/\">SADD</a></strong> - Adds one or more members to a set. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `SADD key member [member ...]`\n\n**Description:** Adds one or more members to a set. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/scard/\">SCARD</a></strong> - Returns the number of members in a set.</summary>\n\n**Syntax:** `SCARD key`\n\n**Description:** Returns the number of members in a set.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sdiff/\">SDIFF</a></strong> - Returns the difference of multiple sets.</summary>\n\n**Syntax:** `SDIFF key [key ...]`\n\n**Description:** Returns the difference of multiple sets.\n\n**Complexity:** O(N) where N is the total number of elements in all given sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sdiffstore/\">SDIFFSTORE</a></strong> - Stores the difference of multiple sets in a key.</summary>\n\n**Syntax:** `SDIFFSTORE destination key [key ...]`\n\n**Description:** Stores the difference of multiple sets in a key.\n\n**Complexity:** O(N) where N is the total number of elements in all given sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sinter/\">SINTER</a></strong> - Returns the intersect of multiple sets.</summary>\n\n**Syntax:** `SINTER key [key ...]`\n\n**Description:** Returns the intersect of multiple sets.\n\n**Complexity:** O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sintercard/\">SINTERCARD</a></strong> - Returns the number of members of the intersect of multiple sets.</summary>\n\n**Syntax:** `SINTERCARD numkeys key [key ...] [LIMIT limit]`\n\n**Description:** Returns the number of members of the intersect of multiple sets.\n\n**Complexity:** O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sinterstore/\">SINTERSTORE</a></strong> - Stores the intersect of multiple sets in a key.</summary>\n\n**Syntax:** `SINTERSTORE destination key [key ...]`\n\n**Description:** Stores the intersect of multiple sets in a key.\n\n**Complexity:** O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sismember/\">SISMEMBER</a></strong> - Determines whether a member belongs to a set.</summary>\n\n**Syntax:** `SISMEMBER key member`\n\n**Description:** Determines whether a member belongs to a set.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/smembers/\">SMEMBERS</a></strong> - Returns all members of a set.</summary>\n\n**Syntax:** `SMEMBERS key`\n\n**Description:** Returns all members of a set.\n\n**Complexity:** O(N) where N is the set cardinality.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/smismember/\">SMISMEMBER</a></strong> - Determines whether multiple members belong to a set.</summary>\n\n**Syntax:** `SMISMEMBER key member [member ...]`\n\n**Description:** Determines whether multiple members belong to a set.\n\n**Complexity:** O(N) where N is the number of elements being checked for membership\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/smove/\">SMOVE</a></strong> - Moves a member from one set to another.</summary>\n\n**Syntax:** `SMOVE source destination member`\n\n**Description:** Moves a member from one set to another.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/spop/\">SPOP</a></strong> - Returns one or more random members from a set after removing them. Deletes the set if the last member was popped.</summary>\n\n**Syntax:** `SPOP key [count]`\n\n**Description:** Returns one or more random members from a set after removing them. Deletes the set if the last member was popped.\n\n**Complexity:** Without the count argument O(1), otherwise O(N) where N is the value of the passed count.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/srandmember/\">SRANDMEMBER</a></strong> - Get one or multiple random members from a set</summary>\n\n**Syntax:** `SRANDMEMBER key [count]`\n\n**Description:** Get one or multiple random members from a set\n\n**Complexity:** Without the count argument O(1), otherwise O(N) where N is the absolute value of the passed count.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/srem/\">SREM</a></strong> - Removes one or more members from a set. Deletes the set if the last member was removed.</summary>\n\n**Syntax:** `SREM key member [member ...]`\n\n**Description:** Removes one or more members from a set. Deletes the set if the last member was removed.\n\n**Complexity:** O(N) where N is the number of members to be removed.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sscan/\">SSCAN</a></strong> - Iterates over members of a set.</summary>\n\n**Syntax:** `SSCAN key cursor [MATCH pattern] [COUNT count]`\n\n**Description:** Iterates over members of a set.\n\n**Complexity:** O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sunion/\">SUNION</a></strong> - Returns the union of multiple sets.</summary>\n\n**Syntax:** `SUNION key [key ...]`\n\n**Description:** Returns the union of multiple sets.\n\n**Complexity:** O(N) where N is the total number of elements in all given sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sunionstore/\">SUNIONSTORE</a></strong> - Stores the union of multiple sets in a key.</summary>\n\n**Syntax:** `SUNIONSTORE destination key [key ...]`\n\n**Description:** Stores the union of multiple sets in a key.\n\n**Complexity:** O(N) where N is the total number of elements in all given sets.\n\n**Since:** 1.0.0\n\n</details>"},{"id":"sorted-set-commands","title":"Sorted set commands","role":"content","text":"Sorted set commands operate on sets of unique strings ordered by a score.\n\n<details>\n<summary><strong><a href=\"/commands/bzmpop/\">BZMPOP</a></strong> - Removes and returns a member by score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.</summary>\n\n**Syntax:** `BZMPOP timeout numkeys key [key ...] <MIN | MAX> [COUNT count]`\n\n**Description:** Removes and returns a member by score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.\n\n**Complexity:** O(K) + O(M*log(N)) where K is the number of provided keys, N being the number of elements in the sorted set, and M being the number of elements popped.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bzpopmax/\">BZPOPMAX</a></strong> - Removes and returns the member with the highest score from one or more sorted sets. Blocks until a member available otherwise.  Deletes the sorted set if the last element was popped.</summary>\n\n**Syntax:** `BZPOPMAX key [key ...] timeout`\n\n**Description:** Removes and returns the member with the highest score from one or more sorted sets. Blocks until a member available otherwise.  Deletes the sorted set if the last element was popped.\n\n**Complexity:** O(log(N)) with N being the number of elements in the sorted set.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bzpopmin/\">BZPOPMIN</a></strong> - Removes and returns the member with the lowest score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.</summary>\n\n**Syntax:** `BZPOPMIN key [key ...] timeout`\n\n**Description:** Removes and returns the member with the lowest score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.\n\n**Complexity:** O(log(N)) with N being the number of elements in the sorted set.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zadd/\">ZADD</a></strong> - Adds one or more members to a sorted set, or updates their scores. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `ZADD key [NX | XX] [GT | LT] [CH] [INCR] score member [score member\n  ...]`\n\n**Description:** Adds one or more members to a sorted set, or updates their scores. Creates the key if it doesn't exist.\n\n**Complexity:** O(log(N)) for each item added, where N is the number of elements in the sorted set.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zcard/\">ZCARD</a></strong> - Returns the number of members in a sorted set.</summary>\n\n**Syntax:** `ZCARD key`\n\n**Description:** Returns the number of members in a sorted set.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zcount/\">ZCOUNT</a></strong> - Returns the count of members in a sorted set that have scores within a range.</summary>\n\n**Syntax:** `ZCOUNT key min max`\n\n**Description:** Returns the count of members in a sorted set that have scores within a range.\n\n**Complexity:** O(log(N)) with N being the number of elements in the sorted set.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zdiff/\">ZDIFF</a></strong> - Returns the difference between multiple sorted sets.</summary>\n\n**Syntax:** `ZDIFF numkeys key [key ...] [WITHSCORES]`\n\n**Description:** Returns the difference between multiple sorted sets.\n\n**Complexity:** O(L + (N-K)log(N)) worst case where L is the total number of elements in all the sets, N is the size of the first set, and K is the size of the result set.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zdiffstore/\">ZDIFFSTORE</a></strong> - Stores the difference of multiple sorted sets in a key.</summary>\n\n**Syntax:** `ZDIFFSTORE destination numkeys key [key ...]`\n\n**Description:** Stores the difference of multiple sorted sets in a key.\n\n**Complexity:** O(L + (N-K)log(N)) worst case where L is the total number of elements in all the sets, N is the size of the first set, and K is the size of the result set.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zincrby/\">ZINCRBY</a></strong> - Increments the score of a member in a sorted set.</summary>\n\n**Syntax:** `ZINCRBY key increment member`\n\n**Description:** Increments the score of a member in a sorted set.\n\n**Complexity:** O(log(N)) where N is the number of elements in the sorted set.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zinter/\">ZINTER</a></strong> - Returns the intersect of multiple sorted sets.</summary>\n\n**Syntax:** `ZINTER numkeys key [key ...] [WEIGHTS weight [weight ...]]\n  [AGGREGATE <SUM | MIN | MAX>] [WITHSCORES]`\n\n**Description:** Returns the intersect of multiple sorted sets.\n\n**Complexity:** O(N*K)+O(M*log(M)) worst case with N being the smallest input sorted set, K being the number of input sorted sets and M being the number of elements in the resulting sorted set.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zintercard/\">ZINTERCARD</a></strong> - Returns the number of members of the intersect of multiple sorted sets.</summary>\n\n**Syntax:** `ZINTERCARD numkeys key [key ...] [LIMIT limit]`\n\n**Description:** Returns the number of members of the intersect of multiple sorted sets.\n\n**Complexity:** O(N*K) worst case with N being the smallest input sorted set, K being the number of input sorted sets.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zinterstore/\">ZINTERSTORE</a></strong> - Stores the intersect of multiple sorted sets in a key.</summary>\n\n**Syntax:** `ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight\n  [weight ...]] [AGGREGATE <SUM | MIN | MAX>]`\n\n**Description:** Stores the intersect of multiple sorted sets in a key.\n\n**Complexity:** O(N*K)+O(M*log(M)) worst case with N being the smallest input sorted set, K being the number of input sorted sets and M being the number of elements in the resulting sorted set.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zlexcount/\">ZLEXCOUNT</a></strong> - Returns the number of members in a sorted set within a lexicographical range.</summary>\n\n**Syntax:** `ZLEXCOUNT key min max`\n\n**Description:** Returns the number of members in a sorted set within a lexicographical range.\n\n**Complexity:** O(log(N)) with N being the number of elements in the sorted set.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zmpop/\">ZMPOP</a></strong> - Returns the highest- or lowest-scoring members from one or more sorted sets after removing them. Deletes the sorted set if the last member was popped.</summary>\n\n**Syntax:** `ZMPOP numkeys key [key ...] <MIN | MAX> [COUNT count]`\n\n**Description:** Returns the highest- or lowest-scoring members from one or more sorted sets after removing them. Deletes the sorted set if the last member was popped.\n\n**Complexity:** O(K) + O(M*log(N)) where K is the number of provided keys, N being the number of elements in the sorted set, and M being the number of elements popped.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zmscore/\">ZMSCORE</a></strong> - Returns the score of one or more members in a sorted set.</summary>\n\n**Syntax:** `ZMSCORE key member [member ...]`\n\n**Description:** Returns the score of one or more members in a sorted set.\n\n**Complexity:** O(N) where N is the number of members being requested.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zpopmax/\">ZPOPMAX</a></strong> - Returns the highest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.</summary>\n\n**Syntax:** `ZPOPMAX key [count]`\n\n**Description:** Returns the highest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.\n\n**Complexity:** O(log(N)*M) with N being the number of elements in the sorted set, and M being the number of elements popped.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zpopmin/\">ZPOPMIN</a></strong> - Returns the lowest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.</summary>\n\n**Syntax:** `ZPOPMIN key [count]`\n\n**Description:** Returns the lowest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.\n\n**Complexity:** O(log(N)*M) with N being the number of elements in the sorted set, and M being the number of elements popped.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrandmember/\">ZRANDMEMBER</a></strong> - Returns one or more random members from a sorted set.</summary>\n\n**Syntax:** `ZRANDMEMBER key [count [WITHSCORES]]`\n\n**Description:** Returns one or more random members from a sorted set.\n\n**Complexity:** O(N) where N is the number of members returned\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrange/\">ZRANGE</a></strong> - Returns members in a sorted set within a range of indexes.</summary>\n\n**Syntax:** `ZRANGE key start stop [BYSCORE | BYLEX] [REV] [LIMIT offset count]\n  [WITHSCORES]`\n\n**Description:** Returns members in a sorted set within a range of indexes.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements returned.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrangebylex/\">ZRANGEBYLEX</a></strong> - Returns members in a sorted set within a lexicographical range.</summary>\n\n**Syntax:** `ZRANGEBYLEX key min max [LIMIT offset count]`\n\n**Description:** Returns members in a sorted set within a lexicographical range.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrangebyscore/\">ZRANGEBYSCORE</a></strong> - Returns members in a sorted set within a range of scores.</summary>\n\n**Syntax:** `ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]`\n\n**Description:** Returns members in a sorted set within a range of scores.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).\n\n**Since:** 1.0.5\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrangestore/\">ZRANGESTORE</a></strong> - Stores a range of members from sorted set in a key.</summary>\n\n**Syntax:** `ZRANGESTORE dst src min max [BYSCORE | BYLEX] [REV] [LIMIT offset\n  count]`\n\n**Description:** Stores a range of members from sorted set in a key.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements stored into the destination key.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrank/\">ZRANK</a></strong> - Returns the index of a member in a sorted set ordered by ascending scores.</summary>\n\n**Syntax:** `ZRANK key member [WITHSCORE]`\n\n**Description:** Returns the index of a member in a sorted set ordered by ascending scores.\n\n**Complexity:** O(log(N))\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrem/\">ZREM</a></strong> - Removes one or more members from a sorted set. Deletes the sorted set if all members were removed.</summary>\n\n**Syntax:** `ZREM key member [member ...]`\n\n**Description:** Removes one or more members from a sorted set. Deletes the sorted set if all members were removed.\n\n**Complexity:** O(M*log(N)) with N being the number of elements in the sorted set and M the number of elements to be removed.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zremrangebylex/\">ZREMRANGEBYLEX</a></strong> - Removes members in a sorted set within a lexicographical range. Deletes the sorted set if all members were removed.</summary>\n\n**Syntax:** `ZREMRANGEBYLEX key min max`\n\n**Description:** Removes members in a sorted set within a lexicographical range. Deletes the sorted set if all members were removed.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zremrangebyrank/\">ZREMRANGEBYRANK</a></strong> - Removes members in a sorted set within a range of indexes. Deletes the sorted set if all members were removed.</summary>\n\n**Syntax:** `ZREMRANGEBYRANK key start stop`\n\n**Description:** Removes members in a sorted set within a range of indexes. Deletes the sorted set if all members were removed.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zremrangebyscore/\">ZREMRANGEBYSCORE</a></strong> - Removes members in a sorted set within a range of scores. Deletes the sorted set if all members were removed.</summary>\n\n**Syntax:** `ZREMRANGEBYSCORE key min max`\n\n**Description:** Removes members in a sorted set within a range of scores. Deletes the sorted set if all members were removed.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrevrange/\">ZREVRANGE</a></strong> - Returns members in a sorted set within a range of indexes in reverse order.</summary>\n\n**Syntax:** `ZREVRANGE key start stop [WITHSCORES]`\n\n**Description:** Returns members in a sorted set within a range of indexes in reverse order.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements returned.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrevrangebylex/\">ZREVRANGEBYLEX</a></strong> - Returns members in a sorted set within a lexicographical range in reverse order.</summary>\n\n**Syntax:** `ZREVRANGEBYLEX key max min [LIMIT offset count]`\n\n**Description:** Returns members in a sorted set within a lexicographical range in reverse order.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrevrangebyscore/\">ZREVRANGEBYSCORE</a></strong> - Returns members in a sorted set within a range of scores in reverse order.</summary>\n\n**Syntax:** `ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]`\n\n**Description:** Returns members in a sorted set within a range of scores in reverse order.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrevrank/\">ZREVRANK</a></strong> - Returns the index of a member in a sorted set ordered by descending scores.</summary>\n\n**Syntax:** `ZREVRANK key member [WITHSCORE]`\n\n**Description:** Returns the index of a member in a sorted set ordered by descending scores.\n\n**Complexity:** O(log(N))\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zscan/\">ZSCAN</a></strong> - Iterates over members and scores of a sorted set.</summary>\n\n**Syntax:** `ZSCAN key cursor [MATCH pattern] [COUNT count]`\n\n**Description:** Iterates over members and scores of a sorted set.\n\n**Complexity:** O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zscore/\">ZSCORE</a></strong> - Returns the score of a member in a sorted set.</summary>\n\n**Syntax:** `ZSCORE key member`\n\n**Description:** Returns the score of a member in a sorted set.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zunion/\">ZUNION</a></strong> - Returns the union of multiple sorted sets.</summary>\n\n**Syntax:** `ZUNION numkeys key [key ...] [WEIGHTS weight [weight ...]]\n  [AGGREGATE <SUM | MIN | MAX>] [WITHSCORES]`\n\n**Description:** Returns the union of multiple sorted sets.\n\n**Complexity:** O(N)+O(M*log(M)) with N being the sum of the sizes of the input sorted sets, and M being the number of elements in the resulting sorted set.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zunionstore/\">ZUNIONSTORE</a></strong> - Stores the union of multiple sorted sets in a key.</summary>\n\n**Syntax:** `ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight\n  [weight ...]] [AGGREGATE <SUM | MIN | MAX>]`\n\n**Description:** Stores the union of multiple sorted sets in a key.\n\n**Complexity:** O(N)+O(M log(M)) with N being the sum of the sizes of the input sorted sets, and M being the number of elements in the resulting sorted set.\n\n**Since:** 2.0.0\n\n</details>"},{"id":"stream-commands","title":"Stream commands","role":"content","text":"Stream commands operate on append-only log data structures.\n\n<details>\n<summary><strong><a href=\"/commands/xack/\">XACK</a></strong> - Returns the number of messages that were successfully acknowledged by the consumer group member of a stream.</summary>\n\n**Syntax:** `XACK key group id [id ...]`\n\n**Description:** Returns the number of messages that were successfully acknowledged by the consumer group member of a stream.\n\n**Complexity:** O(1) for each message ID processed.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xackdel/\">XACKDEL</a></strong> - Acknowledges and conditionally deletes one or multiple entries for a stream consumer group. <span style=\"color: #e74c3c;\">⭐ New in 8.2</span></summary>\n\n**Syntax:** `XACKDEL key group [KEEPREF | DELREF | ACKED] IDS numids id [id ...]`\n\n**Description:** Acknowledges and conditionally deletes one or multiple entries for a stream consumer group.\n\n**Complexity:** O(1) for each entry ID processed.\n\n**Since:** 8.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xadd/\">XADD</a></strong> - Appends a new message to a stream. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `XADD key [NOMKSTREAM] [KEEPREF | DELREF | ACKED] [<MAXLEN | MINID>\n  [= | ~] threshold [LIMIT count]] <* | id> field value [field value\n  ...]`\n\n**Description:** Appends a new message to a stream. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) when adding a new entry, O(N) when trimming where N being the number of entries evicted.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xautoclaim/\">XAUTOCLAIM</a></strong> - Changes, or acquires, ownership of messages in a consumer group, as if the messages were delivered to as consumer group member.</summary>\n\n**Syntax:** `XAUTOCLAIM key group consumer min-idle-time start [COUNT count]\n  [JUSTID]`\n\n**Description:** Changes, or acquires, ownership of messages in a consumer group, as if the messages were delivered to as consumer group member.\n\n**Complexity:** O(1) if COUNT is small.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xclaim/\">XCLAIM</a></strong> - Changes, or acquires, ownership of a message in a consumer group, as if the message was delivered a consumer group member.</summary>\n\n**Syntax:** `XCLAIM key group consumer min-idle-time id [id ...] [IDLE ms]\n  [TIME unix-time-milliseconds] [RETRYCOUNT count] [FORCE] [JUSTID]\n  [LASTID lastid]`\n\n**Description:** Changes, or acquires, ownership of a message in a consumer group, as if the message was delivered a consumer group member.\n\n**Complexity:** O(log N) with N being the number of messages in the PEL of the consumer group.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xdel/\">XDEL</a></strong> - Returns the number of messages after removing them from a stream.</summary>\n\n**Syntax:** `XDEL key id [id ...]`\n\n**Description:** Returns the number of messages after removing them from a stream.\n\n**Complexity:** O(1) for each single item to delete in the stream, regardless of the stream size.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xdelex/\">XDELEX</a></strong> - Deletes one or multiple entries from the stream. <span style=\"color: #e74c3c;\">⭐ New in 8.2</span></summary>\n\n**Syntax:** `XDELEX key [KEEPREF | DELREF | ACKED] IDS numids id [id ...]`\n\n**Description:** Deletes one or multiple entries from the stream.\n\n**Complexity:** O(1) for each single item to delete in the stream, regardless of the stream size.\n\n**Since:** 8.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-create/\">XGROUP CREATE</a></strong> - Creates a consumer group.</summary>\n\n**Syntax:** `XGROUP CREATE key group <id | $> [MKSTREAM]\n  [ENTRIESREAD entries-read]`\n\n**Description:** Creates a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-createconsumer/\">XGROUP CREATECONSUMER</a></strong> - Creates a consumer in a consumer group.</summary>\n\n**Syntax:** `XGROUP CREATECONSUMER key group consumer`\n\n**Description:** Creates a consumer in a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-delconsumer/\">XGROUP DELCONSUMER</a></strong> - Deletes a consumer from a consumer group.</summary>\n\n**Syntax:** `XGROUP DELCONSUMER key group consumer`\n\n**Description:** Deletes a consumer from a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-destroy/\">XGROUP DESTROY</a></strong> - Destroys a consumer group.</summary>\n\n**Syntax:** `XGROUP DESTROY key group`\n\n**Description:** Destroys a consumer group.\n\n**Complexity:** O(N) where N is the number of entries in the group's pending entries list (PEL).\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-setid/\">XGROUP SETID</a></strong> - Sets the last-delivered ID of a consumer group.</summary>\n\n**Syntax:** `XGROUP SETID key group <id | $> [ENTRIESREAD entries-read]`\n\n**Description:** Sets the last-delivered ID of a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xinfo-consumers/\">XINFO CONSUMERS</a></strong> - Returns a list of the consumers in a consumer group.</summary>\n\n**Syntax:** `XINFO CONSUMERS key group`\n\n**Description:** Returns a list of the consumers in a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xinfo-groups/\">XINFO GROUPS</a></strong> - Returns a list of the consumer groups of a stream.</summary>\n\n**Syntax:** `XINFO GROUPS key`\n\n**Description:** Returns a list of the consumer groups of a stream.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xinfo-stream/\">XINFO STREAM</a></strong> - Returns information about a stream.</summary>\n\n**Syntax:** `XINFO STREAM key [FULL [COUNT count]]`\n\n**Description:** Returns information about a stream.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xlen/\">XLEN</a></strong> - Return the number of messages in a stream.</summary>\n\n**Syntax:** `XLEN key`\n\n**Description:** Return the number of messages in a stream.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xpending/\">XPENDING</a></strong> - Returns the information and entries from a stream consumer group's pending entries list.</summary>\n\n**Syntax:** `XPENDING key group [[IDLE min-idle-time] start end count [consumer]]`\n\n**Description:** Returns the information and entries from a stream consumer group's pending entries list.\n\n**Complexity:** O(N) with N being the number of elements returned, so asking for a small fixed number of entries per call is O(1). O(M), where M is the total number of entries scanned when used with the IDLE filter. When the command returns just the summary and the list of consumers is small, it runs in O(1) time; otherwise, an additional O(N) time for iterating every consumer.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xrange/\">XRANGE</a></strong> - Returns the messages from a stream within a range of IDs.</summary>\n\n**Syntax:** `XRANGE key start end [COUNT count]`\n\n**Description:** Returns the messages from a stream within a range of IDs.\n\n**Complexity:** O(N) with N being the number of elements being returned. If N is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1).\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xread/\">XREAD</a></strong> - Returns messages from multiple streams with IDs greater than the ones requested. Blocks until a message is available otherwise.</summary>\n\n**Syntax:** `XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] id\n  [id ...]`\n\n**Description:** Returns messages from multiple streams with IDs greater than the ones requested. Blocks until a message is available otherwise.\n\n**Complexity:** \n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xreadgroup/\">XREADGROUP</a></strong> - Returns new or historical messages from a stream for a consumer in a group. Blocks until a message is available otherwise.</summary>\n\n**Syntax:** `XREADGROUP GROUP group consumer [COUNT count] [BLOCK milliseconds]\n  [CLAIM min-idle-time] [NOACK] STREAMS key [key ...] id [id ...]`\n\n**Description:** Returns new or historical messages from a stream for a consumer in a group. Blocks until a message is available otherwise.\n\n**Complexity:** For each stream mentioned: O(M) with M being the number of elements returned. If M is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1). On the other side when XREADGROUP blocks, XADD will pay the O(N) time in order to serve the N clients blocked on the stream getting new data.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xrevrange/\">XREVRANGE</a></strong> - Returns the messages from a stream within a range of IDs in reverse order.</summary>\n\n**Syntax:** `XREVRANGE key end start [COUNT count]`\n\n**Description:** Returns the messages from a stream within a range of IDs in reverse order.\n\n**Complexity:** O(N) with N being the number of elements returned. If N is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1).\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xsetid/\">XSETID</a></strong> - An internal command for replicating stream values.</summary>\n\n**Syntax:** `XSETID key last-id [ENTRIESADDED entries-added]\n  [MAXDELETEDID max-deleted-id]`\n\n**Description:** An internal command for replicating stream values.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xtrim/\">XTRIM</a></strong> - Deletes messages from the beginning of a stream.</summary>\n\n**Syntax:** `XTRIM key <MAXLEN | MINID> [= | ~] threshold [LIMIT count] [KEEPREF\n  | DELREF | ACKED]`\n\n**Description:** Deletes messages from the beginning of a stream.\n\n**Complexity:** O(N), with N being the number of evicted entries. Constant times are very small however, since entries are organized in macro nodes containing multiple entries that can be released with a single deallocation.\n\n**Since:** 5.0.0\n\n</details>"},{"id":"bitmap-commands","title":"Bitmap commands","role":"content","text":"Bitmap commands operate on strings as arrays of bits.\n\n<details>\n<summary><strong><a href=\"/commands/bitcount/\">BITCOUNT</a></strong> - Counts the number of set bits (population counting) in a string.</summary>\n\n**Syntax:** `BITCOUNT key [start end [BYTE | BIT]]`\n\n**Description:** Counts the number of set bits (population counting) in a string.\n\n**Complexity:** O(N)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bitfield/\">BITFIELD</a></strong> - Performs arbitrary bitfield integer operations on strings.</summary>\n\n**Syntax:** `BITFIELD key [GET encoding offset | [OVERFLOW <WRAP | SAT | FAIL>]\n  <SET encoding offset value | INCRBY encoding offset increment>\n  [GET encoding offset | [OVERFLOW <WRAP | SAT | FAIL>]\n  <SET encoding offset value | INCRBY encoding offset increment>\n  ...]]`\n\n**Description:** Performs arbitrary bitfield integer operations on strings.\n\n**Complexity:** O(1) for each subcommand specified\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bitfield_ro/\">BITFIELD_RO</a></strong> - Performs arbitrary read-only bitfield integer operations on strings.</summary>\n\n**Syntax:** `BITFIELD RO key [GET encoding offset [GET encoding offset ...]]`\n\n**Description:** Performs arbitrary read-only bitfield integer operations on strings.\n\n**Complexity:** O(1) for each subcommand specified\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bitop/\">BITOP</a></strong> - Performs bitwise operations on multiple strings, and stores the result.</summary>\n\n**Syntax:** `BITOP <AND | OR | XOR | NOT | DIFF | DIFF1 | ANDOR | ONE> destkey key [key ...]`\n\n**Description:** Performs bitwise operations on multiple strings, and stores the result.\n\n**Complexity:** O(N)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bitpos/\">BITPOS</a></strong> - Finds the first set (1) or clear (0) bit in a string.</summary>\n\n**Syntax:** `BITPOS key bit [start [end [BYTE | BIT]]]`\n\n**Description:** Finds the first set (1) or clear (0) bit in a string.\n\n**Complexity:** O(N)\n\n**Since:** 2.8.7\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getbit/\">GETBIT</a></strong> - Returns a bit value by offset.</summary>\n\n**Syntax:** `GETBIT key offset`\n\n**Description:** Returns a bit value by offset.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/setbit/\">SETBIT</a></strong> - Sets or clears the bit at offset of the string value. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `SETBIT key offset value`\n\n**Description:** Sets or clears the bit at offset of the string value. Creates the key if it doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>"},{"id":"hyperloglog-commands","title":"HyperLogLog commands","role":"content","text":"HyperLogLog commands provide probabilistic cardinality estimation.\n\n<details>\n<summary><strong><a href=\"/commands/pfadd/\">PFADD</a></strong> - Adds elements to a HyperLogLog key. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `PFADD key [element [element ...]]`\n\n**Description:** Adds elements to a HyperLogLog key. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) to add every element.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pfcount/\">PFCOUNT</a></strong> - Returns the approximated cardinality of the set(s) observed by the HyperLogLog key(s).</summary>\n\n**Syntax:** `PFCOUNT key [key ...]`\n\n**Description:** Returns the approximated cardinality of the set(s) observed by the HyperLogLog key(s).\n\n**Complexity:** O(1) with a very small average constant time when called with a single key. O(N) with N being the number of keys, and much bigger constant times, when called with multiple keys.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pfdebug/\">PFDEBUG</a></strong> - Internal commands for debugging HyperLogLog values.</summary>\n\n**Syntax:** `PFDEBUG subcommand key`\n\n**Description:** Internal commands for debugging HyperLogLog values.\n\n**Complexity:** N/A\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pfmerge/\">PFMERGE</a></strong> - Merges one or more HyperLogLog values into a single key.</summary>\n\n**Syntax:** `PFMERGE destkey [sourcekey [sourcekey ...]]`\n\n**Description:** Merges one or more HyperLogLog values into a single key.\n\n**Complexity:** O(N) to merge N HyperLogLogs, but with high constant times.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pfselftest/\">PFSELFTEST</a></strong> - An internal command for testing HyperLogLog values.</summary>\n\n**Syntax:** `PFSELFTEST`\n\n**Description:** An internal command for testing HyperLogLog values.\n\n**Complexity:** N/A\n\n**Since:** 2.8.9\n\n</details>"},{"id":"geospatial-commands","title":"Geospatial commands","role":"content","text":"Geospatial commands operate on geographic coordinates.\n\n<details>\n<summary><strong><a href=\"/commands/geoadd/\">GEOADD</a></strong> - Adds one or more members to a geospatial index. The key is created if it doesn't exist.</summary>\n\n**Syntax:** `GEOADD key [NX | XX] [CH] longitude latitude member [longitude\n  latitude member ...]`\n\n**Description:** Adds one or more members to a geospatial index. The key is created if it doesn't exist.\n\n**Complexity:** O(log(N)) for each item added, where N is the number of elements in the sorted set.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geodist/\">GEODIST</a></strong> - Returns the distance between two members of a geospatial index.</summary>\n\n**Syntax:** `GEODIST key member1 member2 [M | KM | FT | MI]`\n\n**Description:** Returns the distance between two members of a geospatial index.\n\n**Complexity:** O(1)\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geohash/\">GEOHASH</a></strong> - Returns members from a geospatial index as geohash strings.</summary>\n\n**Syntax:** `GEOHASH key [member [member ...]]`\n\n**Description:** Returns members from a geospatial index as geohash strings.\n\n**Complexity:** O(1) for each member requested.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geopos/\">GEOPOS</a></strong> - Returns the longitude and latitude of members from a geospatial index.</summary>\n\n**Syntax:** `GEOPOS key [member [member ...]]`\n\n**Description:** Returns the longitude and latitude of members from a geospatial index.\n\n**Complexity:** O(1) for each member requested.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/georadius/\">GEORADIUS</a></strong> - Queries a geospatial index for members within a distance from a coordinate, optionally stores the result.</summary>\n\n**Syntax:** `GEORADIUS key longitude latitude radius <M | KM | FT | MI>\n  [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC]\n  [STORE key | STOREDIST key]`\n\n**Description:** Queries a geospatial index for members within a distance from a coordinate, optionally stores the result.\n\n**Complexity:** O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/georadiusbymember/\">GEORADIUSBYMEMBER</a></strong> - Queries a geospatial index for members within a distance from a member, optionally stores the result.</summary>\n\n**Syntax:** `GEORADIUSBYMEMBER key member radius <M | KM | FT | MI> [WITHCOORD]\n  [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC] [STORE key\n  | STOREDIST key]`\n\n**Description:** Queries a geospatial index for members within a distance from a member, optionally stores the result.\n\n**Complexity:** O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/georadiusbymember_ro/\">GEORADIUSBYMEMBER_RO</a></strong> - Returns members from a geospatial index that are within a distance from a member.</summary>\n\n**Syntax:** `GEORADIUSBYMEMBER RO key member radius <M | KM | FT | MI>\n  [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC]`\n\n**Description:** Returns members from a geospatial index that are within a distance from a member.\n\n**Complexity:** O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.\n\n**Since:** 3.2.10\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/georadius_ro/\">GEORADIUS_RO</a></strong> - Returns members from a geospatial index that are within a distance from a coordinate.</summary>\n\n**Syntax:** `GEORADIUS RO key longitude latitude radius <M | KM | FT | MI>\n  [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC]`\n\n**Description:** Returns members from a geospatial index that are within a distance from a coordinate.\n\n**Complexity:** O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.\n\n**Since:** 3.2.10\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geosearch/\">GEOSEARCH</a></strong> - Queries a geospatial index for members inside an area of a box or a circle.</summary>\n\n**Syntax:** `GEOSEARCH key <FROMMEMBER member | FROMLONLAT longitude latitude>\n  <BYRADIUS radius <M | KM | FT | MI> | BYBOX width height <M | KM |\n  FT | MI>> [ASC | DESC] [COUNT count [ANY]] [WITHCOORD] [WITHDIST]\n  [WITHHASH]`\n\n**Description:** Queries a geospatial index for members inside an area of a box or a circle.\n\n**Complexity:** O(N+log(M)) where N is the number of elements in the grid-aligned bounding box area around the shape provided as the filter and M is the number of items inside the shape\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geosearchstore/\">GEOSEARCHSTORE</a></strong> - Queries a geospatial index for members inside an area of a box or a circle, optionally stores the result.</summary>\n\n**Syntax:** `GEOSEARCHSTORE destination source <FROMMEMBER member |\n  FROMLONLAT longitude latitude> <BYRADIUS radius <M | KM | FT | MI>\n  | BYBOX width height <M | KM | FT | MI>> [ASC | DESC] [COUNT count\n  [ANY]] [STOREDIST]`\n\n**Description:** Queries a geospatial index for members inside an area of a box or a circle, optionally stores the result.\n\n**Complexity:** O(N+log(M)) where N is the number of elements in the grid-aligned bounding box area around the shape provided as the filter and M is the number of items inside the shape\n\n**Since:** 6.2.0\n\n</details>"},{"id":"json-commands","title":"JSON commands","role":"content","text":"JSON commands operate on JSON data structures.\n\n<details>\n<summary><strong><a href=\"/commands/json.arrappend/\">JSON.ARRAPPEND</a></strong> - Append one or more JSON values into the array at path after the last element in it.</summary>\n\n**Syntax:** `JSON.ARRAPPEND key path value [value ...]`\n\n**Description:** Append one or more JSON values into the array at path after the last element in it.\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrindex/\">JSON.ARRINDEX</a></strong> - Returns the index of the first occurrence of a JSON scalar value in the array at path</summary>\n\n**Syntax:** `JSON.ARRINDEX key path value [start [stop]]`\n\n**Description:** Returns the index of the first occurrence of a JSON scalar value in the array at path\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the array, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrinsert/\">JSON.ARRINSERT</a></strong> - Inserts the JSON scalar(s) value at the specified index in the array at path</summary>\n\n**Syntax:** `JSON.ARRINSERT key path index value [value ...]`\n\n**Description:** Inserts the JSON scalar(s) value at the specified index in the array at path\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the array, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrlen/\">JSON.ARRLEN</a></strong> - Returns the length of the array at path</summary>\n\n**Syntax:** `JSON.ARRLEN key [path]`\n\n**Description:** Returns the length of the array at path\n\n**Complexity:** O(1) where path is evaluated to a single value, O(N) where path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrpop/\">JSON.ARRPOP</a></strong> - Removes and returns the element at the specified index in the array at path</summary>\n\n**Syntax:** `JSON.ARRPOP key [path [index]]`\n\n**Description:** Removes and returns the element at the specified index in the array at path\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the array and the specified index is not the last element, O(1) when path is evaluated to a single value and the specified index is the last element, or O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrtrim/\">JSON.ARRTRIM</a></strong> - Trims the array at path to contain only the specified inclusive range of indices from start to stop</summary>\n\n**Syntax:** `JSON.ARRTRIM key path start stop`\n\n**Description:** Trims the array at path to contain only the specified inclusive range of indices from start to stop\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the array, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.clear/\">JSON.CLEAR</a></strong> - Clears all values from an array or an object and sets numeric values to `0`</summary>\n\n**Syntax:** `JSON.CLEAR key [path]`\n\n**Description:** Clears all values from an array or an object and sets numeric values to `0`\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the values, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.debug/\">JSON.DEBUG</a></strong> - Debugging container command</summary>\n\n**Syntax:** `JSON.DEBUG`\n\n**Description:** Debugging container command\n\n**Complexity:** N/A\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.debug-memory/\">JSON.DEBUG MEMORY</a></strong> - Reports the size in bytes of a key</summary>\n\n**Syntax:** `JSON.DEBUG MEMORY key [path]`\n\n**Description:** Reports the size in bytes of a key\n\n**Complexity:** O(N) when path is evaluated to a single value, where N is the size of the value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.del/\">JSON.DEL</a></strong> - Deletes a value</summary>\n\n**Syntax:** `JSON.DEL key [path]`\n\n**Description:** Deletes a value\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the deleted value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.forget/\">JSON.FORGET</a></strong> - Deletes a value</summary>\n\n**Syntax:** `JSON.FORGET key [path]`\n\n**Description:** Deletes a value\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the deleted value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.get/\">JSON.GET</a></strong> - Gets the value at one or more paths in JSON serialized form</summary>\n\n**Syntax:** `JSON.GET key [INDENT indent] [NEWLINE newline] [SPACE space] [path\n  [path ...]]`\n\n**Description:** Gets the value at one or more paths in JSON serialized form\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.merge/\">JSON.MERGE</a></strong> - Merges a given JSON value into matching paths. Consequently, JSON values at matching paths are updated, deleted, or expanded with new children</summary>\n\n**Syntax:** `JSON.MERGE key path value`\n\n**Description:** Merges a given JSON value into matching paths. Consequently, JSON values at matching paths are updated, deleted, or expanded with new children\n\n**Complexity:** O(M+N) when path is evaluated to a single value where M is the size of the original value (if it exists) and N is the size of the new value, O(M+N) when path is evaluated to multiple values where M is the size of the key and N is the size of the new value * the number of original values in the key\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.mget/\">JSON.MGET</a></strong> - Returns the values at a path from one or more keys</summary>\n\n**Syntax:** `JSON.MGET key [key ...] path`\n\n**Description:** Returns the values at a path from one or more keys\n\n**Complexity:** O(M*N) when path is evaluated to a single value where M is the number of keys and N is the size of the value, O(N1+N2+...+Nm) when path is evaluated to multiple values where m is the number of keys and Ni is the size of the i-th key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.mset/\">JSON.MSET</a></strong> - Sets or updates the JSON value of one or more keys</summary>\n\n**Syntax:** `JSON.MSET key path value [key path value ...]`\n\n**Description:** Sets or updates the JSON value of one or more keys\n\n**Complexity:** O(K*(M+N)) where k is the number of keys in the command, when path is evaluated to a single value where M is the size of the original value (if it exists) and N is the size of the new value, or O(K*(M+N)) when path is evaluated to multiple values where M is the size of the key and N is the size of the new value * the number of original values in the key\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.numincrby/\">JSON.NUMINCRBY</a></strong> - Increments the numeric value at path by a value</summary>\n\n**Syntax:** `JSON.NUMINCRBY key path value`\n\n**Description:** Increments the numeric value at path by a value\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.nummultby/\">JSON.NUMMULTBY</a></strong> - Multiplies the numeric value at path by a value</summary>\n\n**Syntax:** `JSON.NUMMULTBY key path value`\n\n**Description:** Multiplies the numeric value at path by a value\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.objkeys/\">JSON.OBJKEYS</a></strong> - Returns the JSON keys of the object at path</summary>\n\n**Syntax:** `JSON.OBJKEYS key [path]`\n\n**Description:** Returns the JSON keys of the object at path\n\n**Complexity:** O(N) when path is evaluated to a single value, where N is the number of keys in the object, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.objlen/\">JSON.OBJLEN</a></strong> - Returns the number of keys of the object at path</summary>\n\n**Syntax:** `JSON.OBJLEN key [path]`\n\n**Description:** Returns the number of keys of the object at path\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.resp/\">JSON.RESP</a></strong> - Returns the JSON value at path in Redis Serialization Protocol (RESP)</summary>\n\n**Syntax:** `JSON.RESP key [path]`\n\n**Description:** Returns the JSON value at path in Redis Serialization Protocol (RESP)\n\n**Complexity:** O(N) when path is evaluated to a single value, where N is the size of the value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.set/\">JSON.SET</a></strong> - Sets or updates the JSON value at a path</summary>\n\n**Syntax:** `JSON.SET key path value [NX | XX]`\n\n**Description:** Sets or updates the JSON value at a path\n\n**Complexity:** O(M+N) when path is evaluated to a single value where M is the size of the original value (if it exists) and N is the size of the new value, O(M+N) when path is evaluated to multiple values where M is the size of the key and N is the size of the new value * the number of original values in the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.strappend/\">JSON.STRAPPEND</a></strong> - Appends a string to a JSON string value at path</summary>\n\n**Syntax:** `JSON.STRAPPEND key [path] value`\n\n**Description:** Appends a string to a JSON string value at path\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.strlen/\">JSON.STRLEN</a></strong> - Returns the length of the JSON String at path in key</summary>\n\n**Syntax:** `JSON.STRLEN key [path]`\n\n**Description:** Returns the length of the JSON String at path in key\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.toggle/\">JSON.TOGGLE</a></strong> - Toggles a boolean value</summary>\n\n**Syntax:** `JSON.TOGGLE key path`\n\n**Description:** Toggles a boolean value\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.type/\">JSON.TYPE</a></strong> - Returns the type of the JSON value at path</summary>\n\n**Syntax:** `JSON.TYPE key [path]`\n\n**Description:** Returns the type of the JSON value at path\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>"},{"id":"search-commands","title":"Search commands","role":"content","text":"Search commands provide full-text search and secondary indexing.\n\n<details>\n<summary><strong><a href=\"/commands/ft.aggregate/\">FT.AGGREGATE</a></strong> - Run a search query on an index and perform aggregate transformations on the results</summary>\n\n**Syntax:** `FT.AGGREGATE index query [VERBATIM] [LOAD count field [field ...]]\n  [TIMEOUT timeout] [LOAD *] [GROUPBY nargs property [property ...]\n  [REDUCE function nargs arg [arg ...] [AS name] [REDUCE function\n  nargs arg [arg ...] [AS name] ...]] [GROUPBY nargs property\n  [property ...] [REDUCE function nargs arg [arg ...] [AS name]\n  [REDUCE function nargs arg [arg ...] [AS name] ...]] ...]]\n  [SORTBY nargs [property <ASC | DESC> [property <ASC | DESC> ...]]\n  [MAX num]] [APPLY expression AS name [APPLY expression AS name\n  ...]] [LIMIT offset num] [FILTER filter] [WITHCURSOR\n  [COUNT read size] [MAXIDLE idle time]] [PARAMS nargs name value\n  [name value ...]]\n  [SCORER scorer]\n [ADDSCORES]\n  [DIALECT dialect]`\n\n**Description:** Run a search query on an index and perform aggregate transformations on the results\n\n**Complexity:** O(1)\n\n**Since:** 1.1.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.aliasadd/\">FT.ALIASADD</a></strong> - Adds an alias to the index</summary>\n\n**Syntax:** `FT.ALIASADD alias index`\n\n**Description:** Adds an alias to the index\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.aliasdel/\">FT.ALIASDEL</a></strong> - Deletes an alias from the index</summary>\n\n**Syntax:** `FT.ALIASDEL alias`\n\n**Description:** Deletes an alias from the index\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.aliasupdate/\">FT.ALIASUPDATE</a></strong> - Adds or updates an alias to the index</summary>\n\n**Syntax:** `FT.ALIASUPDATE alias index`\n\n**Description:** Adds or updates an alias to the index\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.alter/\">FT.ALTER</a></strong> - Adds a new field to the index</summary>\n\n**Syntax:** `FT.ALTER index [SKIPINITIALSCAN] SCHEMA ADD field options`\n\n**Description:** Adds a new field to the index\n\n**Complexity:** O(N) where N is the number of keys in the keyspace\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.config-get/\">FT.CONFIG GET</a></strong> - Retrieves runtime configuration options</summary>\n\n**Syntax:** `FT.CONFIG GET option`\n\n**Description:** Retrieves runtime configuration options\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.config-set/\">FT.CONFIG SET</a></strong> - Sets runtime configuration options</summary>\n\n**Syntax:** `FT.CONFIG SET option value`\n\n**Description:** Sets runtime configuration options\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.create/\">FT.CREATE</a></strong> - Creates an index with the given spec</summary>\n\n**Syntax:** `FT.CREATE index [ON <HASH | JSON>] [PREFIX count prefix [prefix\n  ...]] [FILTER filter] [LANGUAGE default lang]\n  [LANGUAGE FIELD lang attribute] [SCORE default score]\n  [SCORE FIELD score attribute] [PAYLOAD FIELD payload attribute]\n  [MAXTEXTFIELDS] [TEMPORARY seconds] [NOOFFSETS] [NOHL] [NOFIELDS]\n  [NOFREQS] [STOPWORDS count [stopword [stopword ...]]]\n  [SKIPINITIALSCAN] SCHEMA field name [AS alias] <TEXT | TAG |\n  NUMERIC | GEO | VECTOR> [WITHSUFFIXTRIE] [SORTABLE [UNF]]\n  [NOINDEX] [field name [AS alias] <TEXT | TAG | NUMERIC | GEO |\n  VECTOR> [WITHSUFFIXTRIE] [SORTABLE [UNF]] [NOINDEX] ...]`\n\n**Description:** Creates an index with the given spec\n\n**Complexity:** O(K) at creation where K is the number of fields, O(N) if scanning the keyspace is triggered, where N is the number of keys in the keyspace\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.cursor-del/\">FT.CURSOR DEL</a></strong> - Deletes a cursor</summary>\n\n**Syntax:** `FT.CURSOR DEL index cursor id`\n\n**Description:** Deletes a cursor\n\n**Complexity:** O(1)\n\n**Since:** 1.1.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.cursor-read/\">FT.CURSOR READ</a></strong> - Reads from a cursor</summary>\n\n**Syntax:** `FT.CURSOR READ index cursor id [COUNT read size]`\n\n**Description:** Reads from a cursor\n\n**Complexity:** O(1)\n\n**Since:** 1.1.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.dictadd/\">FT.DICTADD</a></strong> - Adds terms to a dictionary</summary>\n\n**Syntax:** `FT.DICTADD dict term [term ...]`\n\n**Description:** Adds terms to a dictionary\n\n**Complexity:** O(1)\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.dictdel/\">FT.DICTDEL</a></strong> - Deletes terms from a dictionary</summary>\n\n**Syntax:** `FT.DICTDEL dict term [term ...]`\n\n**Description:** Deletes terms from a dictionary\n\n**Complexity:** O(1)\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.dictdump/\">FT.DICTDUMP</a></strong> - Dumps all terms in the given dictionary</summary>\n\n**Syntax:** `FT.DICTDUMP dict`\n\n**Description:** Dumps all terms in the given dictionary\n\n**Complexity:** O(N), where N is the size of the dictionary\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.dropindex/\">FT.DROPINDEX</a></strong> - Deletes the index</summary>\n\n**Syntax:** `FT.DROPINDEX index [DD]`\n\n**Description:** Deletes the index\n\n**Complexity:** O(1) or O(N) if documents are deleted, where N is the number of keys in the keyspace\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.explain/\">FT.EXPLAIN</a></strong> - Returns the execution plan for a complex query</summary>\n\n**Syntax:** `FT.EXPLAIN index query [DIALECT dialect]`\n\n**Description:** Returns the execution plan for a complex query\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.explaincli/\">FT.EXPLAINCLI</a></strong> - Returns the execution plan for a complex query</summary>\n\n**Syntax:** `FT.EXPLAINCLI index query [DIALECT dialect]`\n\n**Description:** Returns the execution plan for a complex query\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.info/\">FT.INFO</a></strong> - Returns information and statistics on the index</summary>\n\n**Syntax:** `FT.INFO index`\n\n**Description:** Returns information and statistics on the index\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.profile/\">FT.PROFILE</a></strong> - Performs a `FT.SEARCH` or `FT.AGGREGATE` command and collects performance information</summary>\n\n**Syntax:** `FT.PROFILE index <SEARCH | AGGREGATE> [LIMITED] QUERY query`\n\n**Description:** Performs a `FT.SEARCH` or `FT.AGGREGATE` command and collects performance information\n\n**Complexity:** O(N)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.search/\">FT.SEARCH</a></strong> - Searches the index with a textual query, returning either documents or just ids</summary>\n\n**Syntax:** `FT.SEARCH index query [NOCONTENT] [VERBATIM] [NOSTOPWORDS]\n  [WITHSCORES] [WITHPAYLOADS] [WITHSORTKEYS] [FILTER numeric field\n  min max [FILTER numeric field min max ...]] [GEOFILTER geo field\n  lon lat radius <m | km | mi | ft> [GEOFILTER geo field lon lat\n  radius <m | km | mi | ft> ...]] [INKEYS count key [key ...]]\n  [INFIELDS count field [field ...]] [RETURN count identifier\n  [AS property] [identifier [AS property] ...]] [SUMMARIZE\n  [FIELDS count field [field ...]] [FRAGS num] [LEN fragsize]\n  [SEPARATOR separator]] [HIGHLIGHT [FIELDS count field [field ...]]\n  [TAGS open close]] [SLOP slop] [TIMEOUT timeout] [INORDER]\n  [LANGUAGE language] [EXPANDER expander] [SCORER scorer]\n  [EXPLAINSCORE] [PAYLOAD payload] [SORTBY sortby [ASC | DESC]]\n  [LIMIT offset num] [PARAMS nargs name value [name value ...]]\n  [DIALECT dialect]`\n\n**Description:** Searches the index with a textual query, returning either documents or just ids\n\n**Complexity:** O(N)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.spellcheck/\">FT.SPELLCHECK</a></strong> - Performs spelling correction on a query, returning suggestions for misspelled terms</summary>\n\n**Syntax:** `FT.SPELLCHECK index query [DISTANCE distance] [TERMS <INCLUDE |\n  EXCLUDE> dictionary [terms [terms ...]]] [DIALECT dialect]`\n\n**Description:** Performs spelling correction on a query, returning suggestions for misspelled terms\n\n**Complexity:** O(1)\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.syndump/\">FT.SYNDUMP</a></strong> - Dumps the contents of a synonym group</summary>\n\n**Syntax:** `FT.SYNDUMP index`\n\n**Description:** Dumps the contents of a synonym group\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.synupdate/\">FT.SYNUPDATE</a></strong> - Creates or updates a synonym group with additional terms</summary>\n\n**Syntax:** `FT.SYNUPDATE index synonym group id [SKIPINITIALSCAN] term [term\n  ...]`\n\n**Description:** Creates or updates a synonym group with additional terms\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.tagvals/\">FT.TAGVALS</a></strong> - Returns the distinct tags indexed in a Tag field</summary>\n\n**Syntax:** `FT.TAGVALS index field name`\n\n**Description:** Returns the distinct tags indexed in a Tag field\n\n**Complexity:** O(N)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft._list/\">FT._LIST</a></strong> - Returns a list of all existing indexes</summary>\n\n**Syntax:** `FT. LIST`\n\n**Description:** Returns a list of all existing indexes\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>"},{"id":"time-series-commands","title":"Time series commands","role":"content","text":"Time series commands operate on time-series data.\n\n<details>\n<summary><strong><a href=\"/commands/ts.add/\">TS.ADD</a></strong> - Append a sample to a time series</summary>\n\n**Syntax:** `TS.ADD key timestamp value [RETENTION retentionPeriod]\n  [ENCODING <COMPRESSED | UNCOMPRESSED>] [CHUNK SIZE size]\n  [DUPLICATE POLICY policy] \n  [ON DUPLICATE <BLOCK | FIRST | LAST | MIN | MAX | SUM>]\n  [IGNORE ignoreMaxTimediff ignoreMaxValDiff]\n  [LABELS [label value ...]]`\n\n**Description:** Append a sample to a time series\n\n**Complexity:** O(M) when M is the amount of compaction rules or O(1) with no compaction\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.alter/\">TS.ALTER</a></strong> - Update the retention, chunk size, duplicate policy, and labels of an existing time series</summary>\n\n**Syntax:** `TS.ALTER key [RETENTION retentionPeriod] [CHUNK SIZE size]\n  [DUPLICATE POLICY <BLOCK | FIRST | LAST | MIN | MAX | SUM>]\n  [IGNORE ignoreMaxTimediff ignoreMaxValDiff] \n  [LABELS [label value ...]]`\n\n**Description:** Update the retention, chunk size, duplicate policy, and labels of an existing time series\n\n**Complexity:** O(N) where N is the number of labels requested to update\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.create/\">TS.CREATE</a></strong> - Create a new time series</summary>\n\n**Syntax:** `TS.CREATE key [RETENTION retentionPeriod] [ENCODING <COMPRESSED |\n  UNCOMPRESSED>] [CHUNK SIZE size] [DUPLICATE POLICY <BLOCK | FIRST |\n  LAST | MIN | MAX | SUM>]\n  [IGNORE ignoreMaxTimediff ignoreMaxValDiff]\n  [LABELS [label value ...]]`\n\n**Description:** Create a new time series\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.createrule/\">TS.CREATERULE</a></strong> - Create a compaction rule</summary>\n\n**Syntax:** `TS.CREATERULE sourceKey destKey AGGREGATION <AVG | FIRST | LAST |\n  MIN | MAX | SUM | RANGE | COUNT | STD.P | STD.S | VAR.P | VAR.S |\n  TWA> bucketDuration [alignTimestamp]`\n\n**Description:** Create a compaction rule\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.decrby/\">TS.DECRBY</a></strong> - Decrease the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given decrement</summary>\n\n**Syntax:** `TS.DECRBY key value [TIMESTAMP timestamp]\n  [RETENTION retentionPeriod] [ENCODING <COMPRESSED|UNCOMPRESSED>] [CHUNK SIZE size]\n [DUPLICATE POLICY policy] [LABELS [label value ...]]`\n\n**Description:** Decrease the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given decrement\n\n**Complexity:** O(M) when M is the amount of compaction rules or O(1) with no compaction\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.del/\">TS.DEL</a></strong> - Delete all samples between two timestamps for a given time series</summary>\n\n**Syntax:** `TS.DEL key from timestamp to timestamp`\n\n**Description:** Delete all samples between two timestamps for a given time series\n\n**Complexity:** O(N) where N is the number of data points that will be removed\n\n**Since:** 1.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.deleterule/\">TS.DELETERULE</a></strong> - Delete a compaction rule</summary>\n\n**Syntax:** `TS.DELETERULE sourceKey destKey`\n\n**Description:** Delete a compaction rule\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.get/\">TS.GET</a></strong> - Get the sample with the highest timestamp from a given time series</summary>\n\n**Syntax:** `TS.GET key [LATEST]`\n\n**Description:** Get the sample with the highest timestamp from a given time series\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.incrby/\">TS.INCRBY</a></strong> - Increase the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given increment</summary>\n\n**Syntax:** `TS.INCRBY key value [TIMESTAMP timestamp]\n  [RETENTION retentionPeriod] [ENCODING <COMPRESSED|UNCOMPRESSED>] [CHUNK SIZE size]\n [DUPLICATE POLICY policy] [LABELS [label value ...]]`\n\n**Description:** Increase the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given increment\n\n**Complexity:** O(M) when M is the amount of compaction rules or O(1) with no compaction\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.info/\">TS.INFO</a></strong> - Returns information and statistics for a time series</summary>\n\n**Syntax:** `TS.INFO key [DEBUG]`\n\n**Description:** Returns information and statistics for a time series\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.madd/\">TS.MADD</a></strong> - Append new samples to one or more time series</summary>\n\n**Syntax:** `TS.MADD key timestamp value [key timestamp value ...]`\n\n**Description:** Append new samples to one or more time series\n\n**Complexity:** O(N*M) when N is the amount of series updated and M is the amount of compaction rules or O(N) with no compaction\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.mget/\">TS.MGET</a></strong> - Get the sample with the highest timestamp from each time series matching a specific filter</summary>\n\n**Syntax:** `TS.MGET [LATEST] [WITHLABELS | <SELECTED LABELS label1 [label1 ...]>]\n  FILTER <l=v | l!=v | l= | l!= | l=(v1,v2,...) | l!=(v1,v2,...)\n  [l=v | l!=v | l= | l!= | l=(v1,v2,...) | l!=(v1,v2,...) ...]>`\n\n**Description:** Get the sample with the highest timestamp from each time series matching a specific filter\n\n**Complexity:** O(n) where n is the number of time-series that match the filters\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.mrange/\">TS.MRANGE</a></strong> - Query a range across multiple time series by filters in forward direction</summary>\n\n**Syntax:** `TS.MRANGE fromTimestamp toTimestamp [LATEST] [FILTER BY TS Timestamp\n  [Timestamp ...]] [FILTER BY VALUE min max] [WITHLABELS |\n  <SELECTED LABELS label1 [label1 ...]>] [COUNT count] [[ALIGN value]\n  AGGREGATION <AVG | FIRST | LAST | MIN | MAX | SUM | RANGE | COUNT\n  | STD.P | STD.S | VAR.P | VAR.S | TWA> bucketDuration\n  [BUCKETTIMESTAMP] [EMPTY]] FILTER <l=v | l!=v | l= | l!= |\n  l=(v1,v2,...) | l!=(v1,v2,...) [l=v | l!=v | l= | l!= |\n  l=(v1,v2,...) | l!=(v1,v2,...) ...]> [GROUPBY label REDUCE\n  reducer]`\n\n**Description:** Query a range across multiple time series by filters in forward direction\n\n**Complexity:** O(n/m+k) where n = Number of data points, m = Chunk size (data points per chunk), k = Number of data points that are in the requested ranges\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.mrevrange/\">TS.MREVRANGE</a></strong> - Query a range across multiple time-series by filters in reverse direction</summary>\n\n**Syntax:** `TS.MREVRANGE fromTimestamp toTimestamp [LATEST]\n  [FILTER BY TS Timestamp [Timestamp ...]] [FILTER BY VALUE min max]\n  [WITHLABELS | <SELECTED LABELS label1 [label1 ...]>] [COUNT count]\n  [[ALIGN value] AGGREGATION <AVG | FIRST | LAST | MIN | MAX | SUM |\n  RANGE | COUNT | STD.P | STD.S | VAR.P | VAR.S | TWA>\n  bucketDuration [BUCKETTIMESTAMP] [EMPTY]] FILTER <l=v | l!=v | l=\n  | l!= | l=(v1,v2,...) | l!=(v1,v2,...) [l=v | l!=v | l= | l!= |\n  l=(v1,v2,...) | l!=(v1,v2,...) ...]> [GROUPBY label REDUCE\n  reducer]`\n\n**Description:** Query a range across multiple time-series by filters in reverse direction\n\n**Complexity:** O(n/m+k) where n = Number of data points, m = Chunk size (data points per chunk), k = Number of data points that are in the requested ranges\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.queryindex/\">TS.QUERYINDEX</a></strong> - Get all time series keys matching a filter list</summary>\n\n**Syntax:** `TS.QUERYINDEX <l=v | l!=v | l= | l!= | l=(v1,v2,...) |\n  l!=(v1,v2,...) [l=v | l!=v | l= | l!= | l=(v1,v2,...) |\n  l!=(v1,v2,...) ...]>`\n\n**Description:** Get all time series keys matching a filter list\n\n**Complexity:** O(n) where n is the number of time-series that match the filters\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.range/\">TS.RANGE</a></strong> - Query a range in forward direction</summary>\n\n**Syntax:** `TS.RANGE key fromTimestamp toTimestamp [LATEST]\n  [FILTER BY TS Timestamp [Timestamp ...]] [FILTER BY VALUE min max]\n  [COUNT count] [[ALIGN value] AGGREGATION <AVG | FIRST | LAST | MIN\n  | MAX | SUM | RANGE | COUNT | STD.P | STD.S | VAR.P | VAR.S | TWA>\n  bucketDuration [BUCKETTIMESTAMP] [EMPTY]]`\n\n**Description:** Query a range in forward direction\n\n**Complexity:** O(n/m+k) where n = Number of data points, m = Chunk size (data points per chunk), k = Number of data points that are in the requested range\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.revrange/\">TS.REVRANGE</a></strong> - Query a range in reverse direction</summary>\n\n**Syntax:** `TS.REVRANGE key fromTimestamp toTimestamp [LATEST]\n  [FILTER BY TS Timestamp [Timestamp ...]] [FILTER BY VALUE min max]\n  [COUNT count] [[ALIGN value] AGGREGATION <AVG | FIRST | LAST | MIN\n  | MAX | SUM | RANGE | COUNT | STD.P | STD.S | VAR.P | VAR.S | TWA>\n  bucketDuration [BUCKETTIMESTAMP] [EMPTY]]`\n\n**Description:** Query a range in reverse direction\n\n**Complexity:** O(n/m+k) where n = Number of data points, m = Chunk size (data points per chunk), k = Number of data points that are in the requested range\n\n**Since:** 1.4.0\n\n</details>"},{"id":"vector-set-commands","title":"Vector set commands","role":"content","text":"Vector set commands operate on vector data structures for similarity search and range queries.\n\n<details>\n<summary><strong><a href=\"/commands/vadd/\">VADD</a></strong> - Add a new element to a vector set, or update its vector if it already exists.</summary>\n\n**Syntax:** `VADD key [REDUCE dim] (FP32 | VALUES num) vector element [CAS] [NOQUANT | Q8 | BIN]\n  [EF build-exploration-factor] [SETATTR attributes] [M numlinks]`\n\n**Description:** Add a new element to a vector set, or update its vector if it already exists.\n\n**Complexity:** O(log(N)) for each element added, where N is the number of elements in the vector set.\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vcard/\">VCARD</a></strong> - Return the number of elements in a vector set.</summary>\n\n**Syntax:** `VCARD key`\n\n**Description:** Return the number of elements in a vector set.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vdim/\">VDIM</a></strong> - Return the dimension of vectors in the vector set.</summary>\n\n**Syntax:** `VDIM key`\n\n**Description:** Return the dimension of vectors in the vector set.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vemb/\">VEMB</a></strong> - Return the vector associated with an element.</summary>\n\n**Syntax:** `VEMB key element [RAW]`\n\n**Description:** Return the vector associated with an element.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vgetattr/\">VGETATTR</a></strong> - Retrieve the JSON attributes of elements.</summary>\n\n**Syntax:** `VGETATTR key element`\n\n**Description:** Retrieve the JSON attributes of elements.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vinfo/\">VINFO</a></strong> - Return information about a vector set.</summary>\n\n**Syntax:** `VINFO key`\n\n**Description:** Return information about a vector set.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vismember/\">VISMEMBER</a></strong> - Check if an element exists in a vector set.</summary>\n\n**Syntax:** `VISMEMBER key element`\n\n**Description:** Check if an element exists in a vector set.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vlinks/\">VLINKS</a></strong> - Return the neighbors of an element at each layer in the HNSW graph.</summary>\n\n**Syntax:** `VLINKS key element [WITHSCORES]`\n\n**Description:** Return the neighbors of an element at each layer in the HNSW graph.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vrandmember/\">VRANDMEMBER</a></strong> - Return one or multiple random members from a vector set.</summary>\n\n**Syntax:** `VRANDMEMBER key [count]`\n\n**Description:** Return one or multiple random members from a vector set.\n\n**Complexity:** O(N) where N is the absolute value of the count argument.\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vrem/\">VREM</a></strong> - Remove an element from a vector set.</summary>\n\n**Syntax:** `VREM key element`\n\n**Description:** Remove an element from a vector set.\n\n**Complexity:** O(log(N)) for each element removed, where N is the number of elements in the vector set.\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vsetattr/\">VSETATTR</a></strong> - Associate or remove the JSON attributes of elements.</summary>\n\n**Syntax:** `VSETATTR key element \"{ JSON obj }\"`\n\n**Description:** Associate or remove the JSON attributes of elements.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vsim/\">VSIM</a></strong> - Return elements by vector similarity.</summary>\n\n**Syntax:** `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]`\n\n**Description:** Return elements by vector similarity.\n\n**Complexity:** O(log(N)) where N is the number of elements in the vector set.\n\n**Since:** 8.0.0\n\n</details>"},{"id":"pubsub-commands","title":"Pub/Sub commands","role":"content","text":"Pub/Sub commands enable message passing between clients.\n\n<details>\n<summary><strong><a href=\"/commands/psubscribe/\">PSUBSCRIBE</a></strong> - Listens for messages published to channels that match one or more patterns.</summary>\n\n**Syntax:** `PSUBSCRIBE pattern [pattern ...]`\n\n**Description:** Listens for messages published to channels that match one or more patterns.\n\n**Complexity:** O(N) where N is the number of patterns to subscribe to.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/publish/\">PUBLISH</a></strong> - Posts a message to a channel.</summary>\n\n**Syntax:** `PUBLISH channel message`\n\n**Description:** Posts a message to a channel.\n\n**Complexity:** O(N+M) where N is the number of clients subscribed to the receiving channel and M is the total number of subscribed patterns (by any client).\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-channels/\">PUBSUB CHANNELS</a></strong> - Returns the active channels.</summary>\n\n**Syntax:** `PUBSUB CHANNELS [pattern]`\n\n**Description:** Returns the active channels.\n\n**Complexity:** O(N) where N is the number of active channels, and assuming constant time pattern matching (relatively short channels and patterns)\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-numpat/\">PUBSUB NUMPAT</a></strong> - Returns a count of unique pattern subscriptions.</summary>\n\n**Syntax:** `PUBSUB NUMPAT`\n\n**Description:** Returns a count of unique pattern subscriptions.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-numsub/\">PUBSUB NUMSUB</a></strong> - Returns a count of subscribers to channels.</summary>\n\n**Syntax:** `PUBSUB NUMSUB [channel [channel ...]]`\n\n**Description:** Returns a count of subscribers to channels.\n\n**Complexity:** O(N) for the NUMSUB subcommand, where N is the number of requested channels\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-shardchannels/\">PUBSUB SHARDCHANNELS</a></strong> - Returns the active shard channels.</summary>\n\n**Syntax:** `PUBSUB SHARDCHANNELS [pattern]`\n\n**Description:** Returns the active shard channels.\n\n**Complexity:** O(N) where N is the number of active shard channels, and assuming constant time pattern matching (relatively short shard channels).\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-shardnumsub/\">PUBSUB SHARDNUMSUB</a></strong> - Returns the count of subscribers of shard channels.</summary>\n\n**Syntax:** `PUBSUB SHARDNUMSUB [shardchannel [shardchannel ...]]`\n\n**Description:** Returns the count of subscribers of shard channels.\n\n**Complexity:** O(N) for the SHARDNUMSUB subcommand, where N is the number of requested shard channels\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/punsubscribe/\">PUNSUBSCRIBE</a></strong> - Stops listening to messages published to channels that match one or more patterns.</summary>\n\n**Syntax:** `PUNSUBSCRIBE [pattern [pattern ...]]`\n\n**Description:** Stops listening to messages published to channels that match one or more patterns.\n\n**Complexity:** O(N) where N is the number of patterns to unsubscribe.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/spublish/\">SPUBLISH</a></strong> - Post a message to a shard channel</summary>\n\n**Syntax:** `SPUBLISH shardchannel message`\n\n**Description:** Post a message to a shard channel\n\n**Complexity:** O(N) where N is the number of clients subscribed to the receiving shard channel.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ssubscribe/\">SSUBSCRIBE</a></strong> - Listens for messages published to shard channels.</summary>\n\n**Syntax:** `SSUBSCRIBE shardchannel [shardchannel ...]`\n\n**Description:** Listens for messages published to shard channels.\n\n**Complexity:** O(N) where N is the number of shard channels to subscribe to.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/subscribe/\">SUBSCRIBE</a></strong> - Listens for messages published to channels.</summary>\n\n**Syntax:** `SUBSCRIBE channel [channel ...]`\n\n**Description:** Listens for messages published to channels.\n\n**Complexity:** O(N) where N is the number of channels to subscribe to.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sunsubscribe/\">SUNSUBSCRIBE</a></strong> - Stops listening to messages posted to shard channels.</summary>\n\n**Syntax:** `SUNSUBSCRIBE [shardchannel [shardchannel ...]]`\n\n**Description:** Stops listening to messages posted to shard channels.\n\n**Complexity:** O(N) where N is the number of shard channels to unsubscribe.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/unsubscribe/\">UNSUBSCRIBE</a></strong> - Stops listening to messages posted to channels.</summary>\n\n**Syntax:** `UNSUBSCRIBE [channel [channel ...]]`\n\n**Description:** Stops listening to messages posted to channels.\n\n**Complexity:** O(N) where N is the number of channels to unsubscribe.\n\n**Since:** 2.0.0\n\n</details>"},{"id":"transaction-commands","title":"Transaction commands","role":"content","text":"Transaction commands enable atomic execution of command groups.\n\n<details>\n<summary><strong><a href=\"/commands/discard/\">DISCARD</a></strong> - Discards a transaction.</summary>\n\n**Syntax:** `DISCARD`\n\n**Description:** Discards a transaction.\n\n**Complexity:** O(N), when N is the number of queued commands\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/exec/\">EXEC</a></strong> - Executes all commands in a transaction.</summary>\n\n**Syntax:** `EXEC`\n\n**Description:** Executes all commands in a transaction.\n\n**Complexity:** Depends on commands in the transaction\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/multi/\">MULTI</a></strong> - Starts a transaction.</summary>\n\n**Syntax:** `MULTI`\n\n**Description:** Starts a transaction.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/unwatch/\">UNWATCH</a></strong> - Forgets about watched keys of a transaction.</summary>\n\n**Syntax:** `UNWATCH`\n\n**Description:** Forgets about watched keys of a transaction.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/watch/\">WATCH</a></strong> - Monitors changes to keys to determine the execution of a transaction.</summary>\n\n**Syntax:** `WATCH key [key ...]`\n\n**Description:** Monitors changes to keys to determine the execution of a transaction.\n\n**Complexity:** O(1) for every key.\n\n**Since:** 2.2.0\n\n</details>"},{"id":"scripting-commands","title":"Scripting commands","role":"content","text":"Scripting commands enable server-side Lua script execution.\n\n<details>\n<summary><strong><a href=\"/commands/eval/\">EVAL</a></strong> - Executes a server-side Lua script.</summary>\n\n**Syntax:** `EVAL script numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Executes a server-side Lua script.\n\n**Complexity:** Depends on the script that is executed.\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/evalsha/\">EVALSHA</a></strong> - Executes a server-side Lua script by SHA1 digest.</summary>\n\n**Syntax:** `EVALSHA sha1 numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Executes a server-side Lua script by SHA1 digest.\n\n**Complexity:** Depends on the script that is executed.\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/evalsha_ro/\">EVALSHA_RO</a></strong> - Executes a read-only server-side Lua script by SHA1 digest.</summary>\n\n**Syntax:** `EVALSHA RO sha1 numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Executes a read-only server-side Lua script by SHA1 digest.\n\n**Complexity:** Depends on the script that is executed.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/eval_ro/\">EVAL_RO</a></strong> - Executes a read-only server-side Lua script.</summary>\n\n**Syntax:** `EVAL RO script numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Executes a read-only server-side Lua script.\n\n**Complexity:** Depends on the script that is executed.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/fcall/\">FCALL</a></strong> - Invokes a function.</summary>\n\n**Syntax:** `FCALL function numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Invokes a function.\n\n**Complexity:** Depends on the function that is executed.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/fcall_ro/\">FCALL_RO</a></strong> - Invokes a read-only function.</summary>\n\n**Syntax:** `FCALL RO function numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Invokes a read-only function.\n\n**Complexity:** Depends on the function that is executed.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-delete/\">FUNCTION DELETE</a></strong> - Deletes a library and its functions.</summary>\n\n**Syntax:** `FUNCTION DELETE library-name`\n\n**Description:** Deletes a library and its functions.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-dump/\">FUNCTION DUMP</a></strong> - Dumps all libraries into a serialized binary payload.</summary>\n\n**Syntax:** `FUNCTION DUMP`\n\n**Description:** Dumps all libraries into a serialized binary payload.\n\n**Complexity:** O(N) where N is the number of functions\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-flush/\">FUNCTION FLUSH</a></strong> - Deletes all libraries and functions.</summary>\n\n**Syntax:** `FUNCTION FLUSH [ASYNC | SYNC]`\n\n**Description:** Deletes all libraries and functions.\n\n**Complexity:** O(N) where N is the number of functions deleted\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-kill/\">FUNCTION KILL</a></strong> - Terminates a function during execution.</summary>\n\n**Syntax:** `FUNCTION KILL`\n\n**Description:** Terminates a function during execution.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-list/\">FUNCTION LIST</a></strong> - Returns information about all libraries.</summary>\n\n**Syntax:** `FUNCTION LIST [LIBRARYNAME library-name-pattern] [WITHCODE]`\n\n**Description:** Returns information about all libraries.\n\n**Complexity:** O(N) where N is the number of functions\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-load/\">FUNCTION LOAD</a></strong> - Creates a library.</summary>\n\n**Syntax:** `FUNCTION LOAD [REPLACE] function-code`\n\n**Description:** Creates a library.\n\n**Complexity:** O(1) (considering compilation time is redundant)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-restore/\">FUNCTION RESTORE</a></strong> - Restores all libraries from a payload.</summary>\n\n**Syntax:** `FUNCTION RESTORE serialized-value [FLUSH | APPEND | REPLACE]`\n\n**Description:** Restores all libraries from a payload.\n\n**Complexity:** O(N) where N is the number of functions on the payload\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-stats/\">FUNCTION STATS</a></strong> - Returns information about a function during execution.</summary>\n\n**Syntax:** `FUNCTION STATS`\n\n**Description:** Returns information about a function during execution.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-debug/\">SCRIPT DEBUG</a></strong> - Sets the debug mode of server-side Lua scripts.</summary>\n\n**Syntax:** `SCRIPT DEBUG <YES | SYNC | NO>`\n\n**Description:** Sets the debug mode of server-side Lua scripts.\n\n**Complexity:** O(1)\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-exists/\">SCRIPT EXISTS</a></strong> - Determines whether server-side Lua scripts exist in the script cache.</summary>\n\n**Syntax:** `SCRIPT EXISTS sha1 [sha1 ...]`\n\n**Description:** Determines whether server-side Lua scripts exist in the script cache.\n\n**Complexity:** O(N) with N being the number of scripts to check (so checking a single script is an O(1) operation).\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-flush/\">SCRIPT FLUSH</a></strong> - Removes all server-side Lua scripts from the script cache.</summary>\n\n**Syntax:** `SCRIPT FLUSH [ASYNC | SYNC]`\n\n**Description:** Removes all server-side Lua scripts from the script cache.\n\n**Complexity:** O(N) with N being the number of scripts in cache\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-kill/\">SCRIPT KILL</a></strong> - Terminates a server-side Lua script during execution.</summary>\n\n**Syntax:** `SCRIPT KILL`\n\n**Description:** Terminates a server-side Lua script during execution.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-load/\">SCRIPT LOAD</a></strong> - Loads a server-side Lua script to the script cache.</summary>\n\n**Syntax:** `SCRIPT LOAD script`\n\n**Description:** Loads a server-side Lua script to the script cache.\n\n**Complexity:** O(N) with N being the length in bytes of the script body.\n\n**Since:** 2.6.0\n\n</details>"},{"id":"connection-commands","title":"Connection commands","role":"content","text":"Connection commands manage client connections.\n\n<details>\n<summary><strong><a href=\"/commands/auth/\">AUTH</a></strong> - Authenticates the connection.</summary>\n\n**Syntax:** `AUTH [username] password`\n\n**Description:** Authenticates the connection.\n\n**Complexity:** O(N) where N is the number of passwords defined for the user\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-caching/\">CLIENT CACHING</a></strong> - Instructs the server whether to track the keys in the next request.</summary>\n\n**Syntax:** `CLIENT CACHING <YES | NO>`\n\n**Description:** Instructs the server whether to track the keys in the next request.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-getname/\">CLIENT GETNAME</a></strong> - Returns the name of the connection.</summary>\n\n**Syntax:** `CLIENT GETNAME`\n\n**Description:** Returns the name of the connection.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-getredir/\">CLIENT GETREDIR</a></strong> - Returns the client ID to which the connection's tracking notifications are redirected.</summary>\n\n**Syntax:** `CLIENT GETREDIR`\n\n**Description:** Returns the client ID to which the connection's tracking notifications are redirected.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-id/\">CLIENT ID</a></strong> - Returns the unique client ID of the connection.</summary>\n\n**Syntax:** `CLIENT ID`\n\n**Description:** Returns the unique client ID of the connection.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-info/\">CLIENT INFO</a></strong> - Returns information about the connection.</summary>\n\n**Syntax:** `CLIENT INFO`\n\n**Description:** Returns information about the connection.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-kill/\">CLIENT KILL</a></strong> - Terminates open connections.</summary>\n\n**Syntax:** `CLIENT KILL <ip:port | <[ID client-id] | [TYPE <NORMAL | MASTER |\n  SLAVE | REPLICA | PUBSUB>] | [USER username] | [ADDR ip:port] |\n  [LADDR ip:port] | [SKIPME <YES | NO>] | [MAXAGE maxage]\n  [[ID client-id] | [TYPE <NORMAL | MASTER | SLAVE | REPLICA |\n  PUBSUB>] | [USER username] | [ADDR ip:port] | [LADDR ip:port] |\n  [SKIPME <YES | NO>] | [MAXAGE maxage] ...]>>`\n\n**Description:** Terminates open connections.\n\n**Complexity:** O(N) where N is the number of client connections\n\n**Since:** 2.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-list/\">CLIENT LIST</a></strong> - Lists open connections.</summary>\n\n**Syntax:** `CLIENT LIST [TYPE <NORMAL | MASTER | REPLICA | PUBSUB>]\n  [ID client-id [client-id ...]]`\n\n**Description:** Lists open connections.\n\n**Complexity:** O(N) where N is the number of client connections\n\n**Since:** 2.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-no-evict/\">CLIENT NO-EVICT</a></strong> - Sets the client eviction mode of the connection.</summary>\n\n**Syntax:** `CLIENT NO-EVICT <ON | OFF>`\n\n**Description:** Sets the client eviction mode of the connection.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-no-touch/\">CLIENT NO-TOUCH</a></strong> - Controls whether commands sent by the client affect the LRU/LFU of accessed keys.</summary>\n\n**Syntax:** `CLIENT NO-TOUCH <ON | OFF>`\n\n**Description:** Controls whether commands sent by the client affect the LRU/LFU of accessed keys.\n\n**Complexity:** O(1)\n\n**Since:** 7.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-pause/\">CLIENT PAUSE</a></strong> - Suspends commands processing.</summary>\n\n**Syntax:** `CLIENT PAUSE timeout [WRITE | ALL]`\n\n**Description:** Suspends commands processing.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-reply/\">CLIENT REPLY</a></strong> - Instructs the server whether to reply to commands.</summary>\n\n**Syntax:** `CLIENT REPLY <ON | OFF | SKIP>`\n\n**Description:** Instructs the server whether to reply to commands.\n\n**Complexity:** O(1)\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-setinfo/\">CLIENT SETINFO</a></strong> - Sets information specific to the client or connection.</summary>\n\n**Syntax:** `CLIENT SETINFO <LIB-NAME libname | LIB-VER libver>`\n\n**Description:** Sets information specific to the client or connection.\n\n**Complexity:** O(1)\n\n**Since:** 7.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-setname/\">CLIENT SETNAME</a></strong> - Sets the connection name.</summary>\n\n**Syntax:** `CLIENT SETNAME connection-name`\n\n**Description:** Sets the connection name.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-tracking/\">CLIENT TRACKING</a></strong> - Controls server-assisted client-side caching for the connection.</summary>\n\n**Syntax:** `CLIENT TRACKING <ON | OFF> [REDIRECT client-id] [PREFIX prefix\n  [PREFIX prefix ...]] [BCAST] [OPTIN] [OPTOUT] [NOLOOP]`\n\n**Description:** Controls server-assisted client-side caching for the connection.\n\n**Complexity:** O(1). Some options may introduce additional complexity.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-trackinginfo/\">CLIENT TRACKINGINFO</a></strong> - Returns information about server-assisted client-side caching for the connection.</summary>\n\n**Syntax:** `CLIENT TRACKINGINFO`\n\n**Description:** Returns information about server-assisted client-side caching for the connection.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-unblock/\">CLIENT UNBLOCK</a></strong> - Unblocks a client blocked by a blocking command from a different connection.</summary>\n\n**Syntax:** `CLIENT UNBLOCK client-id [TIMEOUT | ERROR]`\n\n**Description:** Unblocks a client blocked by a blocking command from a different connection.\n\n**Complexity:** O(log N) where N is the number of client connections\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-unpause/\">CLIENT UNPAUSE</a></strong> - Resumes processing commands from paused clients.</summary>\n\n**Syntax:** `CLIENT UNPAUSE`\n\n**Description:** Resumes processing commands from paused clients.\n\n**Complexity:** O(N) Where N is the number of paused clients\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/echo/\">ECHO</a></strong> - Returns the given string.</summary>\n\n**Syntax:** `ECHO message`\n\n**Description:** Returns the given string.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hello/\">HELLO</a></strong> - Handshakes with the Redis server.</summary>\n\n**Syntax:** `HELLO [protover [AUTH username password] [SETNAME clientname]]`\n\n**Description:** Handshakes with the Redis server.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ping/\">PING</a></strong> - Returns the server's liveliness response.</summary>\n\n**Syntax:** `PING [message]`\n\n**Description:** Returns the server's liveliness response.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/quit/\">QUIT</a></strong> - Closes the connection.</summary>\n\n**Syntax:** `QUIT`\n\n**Description:** Closes the connection.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/reset/\">RESET</a></strong> - Resets the connection.</summary>\n\n**Syntax:** `RESET`\n\n**Description:** Resets the connection.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/select/\">SELECT</a></strong> - Changes the selected database.</summary>\n\n**Syntax:** `SELECT index`\n\n**Description:** Changes the selected database.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>"},{"id":"server-commands","title":"Server commands","role":"content","text":"Server commands provide server management and introspection.\n\n<details>\n<summary><strong><a href=\"/commands/acl-cat/\">ACL CAT</a></strong> - Lists the ACL categories, or the commands inside a category.</summary>\n\n**Syntax:** `ACL CAT [category]`\n\n**Description:** Lists the ACL categories, or the commands inside a category.\n\n**Complexity:** O(1) since the categories and commands are a fixed set.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-deluser/\">ACL DELUSER</a></strong> - Deletes ACL users, and terminates their connections.</summary>\n\n**Syntax:** `ACL DELUSER username [username ...]`\n\n**Description:** Deletes ACL users, and terminates their connections.\n\n**Complexity:** O(1) amortized time considering the typical user.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-dryrun/\">ACL DRYRUN</a></strong> - Simulates the execution of a command by a user, without executing the command.</summary>\n\n**Syntax:** `ACL DRYRUN username command [arg [arg ...]]`\n\n**Description:** Simulates the execution of a command by a user, without executing the command.\n\n**Complexity:** O(1).\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-genpass/\">ACL GENPASS</a></strong> - Generates a pseudorandom, secure password that can be used to identify ACL users.</summary>\n\n**Syntax:** `ACL GENPASS [bits]`\n\n**Description:** Generates a pseudorandom, secure password that can be used to identify ACL users.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-getuser/\">ACL GETUSER</a></strong> - Lists the ACL rules of a user.</summary>\n\n**Syntax:** `ACL GETUSER username`\n\n**Description:** Lists the ACL rules of a user.\n\n**Complexity:** O(N). Where N is the number of password, command and pattern rules that the user has.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-list/\">ACL LIST</a></strong> - Dumps the effective rules in ACL file format.</summary>\n\n**Syntax:** `ACL LIST`\n\n**Description:** Dumps the effective rules in ACL file format.\n\n**Complexity:** O(N). Where N is the number of configured users.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-load/\">ACL LOAD</a></strong> - Reloads the rules from the configured ACL file.</summary>\n\n**Syntax:** `ACL LOAD`\n\n**Description:** Reloads the rules from the configured ACL file.\n\n**Complexity:** O(N). Where N is the number of configured users.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-log/\">ACL LOG</a></strong> - Lists recent security events generated due to ACL rules.</summary>\n\n**Syntax:** `ACL LOG [count | RESET]`\n\n**Description:** Lists recent security events generated due to ACL rules.\n\n**Complexity:** O(N) with N being the number of entries shown.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-save/\">ACL SAVE</a></strong> - Saves the effective ACL rules in the configured ACL file.</summary>\n\n**Syntax:** `ACL SAVE`\n\n**Description:** Saves the effective ACL rules in the configured ACL file.\n\n**Complexity:** O(N). Where N is the number of configured users.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-setuser/\">ACL SETUSER</a></strong> - Creates and modifies an ACL user and its rules.</summary>\n\n**Syntax:** `ACL SETUSER username [rule [rule ...]]`\n\n**Description:** Creates and modifies an ACL user and its rules.\n\n**Complexity:** O(N). Where N is the number of rules provided.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-users/\">ACL USERS</a></strong> - Lists all ACL users.</summary>\n\n**Syntax:** `ACL USERS`\n\n**Description:** Lists all ACL users.\n\n**Complexity:** O(N). Where N is the number of configured users.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-whoami/\">ACL WHOAMI</a></strong> - Returns the authenticated username of the current connection.</summary>\n\n**Syntax:** `ACL WHOAMI`\n\n**Description:** Returns the authenticated username of the current connection.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bgrewriteaof/\">BGREWRITEAOF</a></strong> - Asynchronously rewrites the append-only file to disk.</summary>\n\n**Syntax:** `BGREWRITEAOF`\n\n**Description:** Asynchronously rewrites the append-only file to disk.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bgsave/\">BGSAVE</a></strong> - Asynchronously saves the database(s) to disk.</summary>\n\n**Syntax:** `BGSAVE [SCHEDULE]`\n\n**Description:** Asynchronously saves the database(s) to disk.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command/\">COMMAND</a></strong> - Returns detailed information about all commands.</summary>\n\n**Syntax:** `COMMAND`\n\n**Description:** Returns detailed information about all commands.\n\n**Complexity:** O(N) where N is the total number of Redis commands\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-count/\">COMMAND COUNT</a></strong> - Returns a count of commands.</summary>\n\n**Syntax:** `COMMAND COUNT`\n\n**Description:** Returns a count of commands.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-docs/\">COMMAND DOCS</a></strong> - Returns documentary information about one, multiple or all commands.</summary>\n\n**Syntax:** `COMMAND DOCS [command-name [command-name ...]]`\n\n**Description:** Returns documentary information about one, multiple or all commands.\n\n**Complexity:** O(N) where N is the number of commands to look up\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-getkeys/\">COMMAND GETKEYS</a></strong> - Extracts the key names from an arbitrary command.</summary>\n\n**Syntax:** `COMMAND GETKEYS command [arg [arg ...]]`\n\n**Description:** Extracts the key names from an arbitrary command.\n\n**Complexity:** O(N) where N is the number of arguments to the command\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-getkeysandflags/\">COMMAND GETKEYSANDFLAGS</a></strong> - Extracts the key names and access flags for an arbitrary command.</summary>\n\n**Syntax:** `COMMAND GETKEYSANDFLAGS command [arg [arg ...]]`\n\n**Description:** Extracts the key names and access flags for an arbitrary command.\n\n**Complexity:** O(N) where N is the number of arguments to the command\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-info/\">COMMAND INFO</a></strong> - Returns information about one, multiple or all commands.</summary>\n\n**Syntax:** `COMMAND INFO [command-name [command-name ...]]`\n\n**Description:** Returns information about one, multiple or all commands.\n\n**Complexity:** O(N) where N is the number of commands to look up\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-list/\">COMMAND LIST</a></strong> - Returns a list of command names.</summary>\n\n**Syntax:** `COMMAND LIST [FILTERBY <MODULE module-name | ACLCAT category |\n  PATTERN pattern>]`\n\n**Description:** Returns a list of command names.\n\n**Complexity:** O(N) where N is the total number of Redis commands\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/config-get/\">CONFIG GET</a></strong> - Returns the effective values of configuration parameters.</summary>\n\n**Syntax:** `CONFIG GET parameter [parameter ...]`\n\n**Description:** Returns the effective values of configuration parameters.\n\n**Complexity:** O(N) when N is the number of configuration parameters provided\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/config-resetstat/\">CONFIG RESETSTAT</a></strong> - Resets the server's statistics.</summary>\n\n**Syntax:** `CONFIG RESETSTAT`\n\n**Description:** Resets the server's statistics.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/config-rewrite/\">CONFIG REWRITE</a></strong> - Persists the effective configuration to file.</summary>\n\n**Syntax:** `CONFIG REWRITE`\n\n**Description:** Persists the effective configuration to file.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/config-set/\">CONFIG SET</a></strong> - Sets configuration parameters in-flight.</summary>\n\n**Syntax:** `CONFIG SET parameter value [parameter value ...]`\n\n**Description:** Sets configuration parameters in-flight.\n\n**Complexity:** O(N) when N is the number of configuration parameters provided\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/dbsize/\">DBSIZE</a></strong> - Returns the number of keys in the database.</summary>\n\n**Syntax:** `DBSIZE`\n\n**Description:** Returns the number of keys in the database.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/failover/\">FAILOVER</a></strong> - Starts a coordinated failover from a server to one of its replicas.</summary>\n\n**Syntax:** `FAILOVER [TO host port [FORCE]] [ABORT] [TIMEOUT milliseconds]`\n\n**Description:** Starts a coordinated failover from a server to one of its replicas.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/flushall/\">FLUSHALL</a></strong> - Removes all keys from all databases.</summary>\n\n**Syntax:** `FLUSHALL [ASYNC | SYNC]`\n\n**Description:** Removes all keys from all databases.\n\n**Complexity:** O(N) where N is the total number of keys in all databases\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/flushdb/\">FLUSHDB</a></strong> - Remove all keys from the current database.</summary>\n\n**Syntax:** `FLUSHDB [ASYNC | SYNC]`\n\n**Description:** Remove all keys from the current database.\n\n**Complexity:** O(N) where N is the number of keys in the selected database\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/info/\">INFO</a></strong> - Returns information and statistics about the server.</summary>\n\n**Syntax:** `INFO [section [section ...]]`\n\n**Description:** Returns information and statistics about the server.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lastsave/\">LASTSAVE</a></strong> - Returns the Unix timestamp of the last successful save to disk.</summary>\n\n**Syntax:** `LASTSAVE`\n\n**Description:** Returns the Unix timestamp of the last successful save to disk.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-doctor/\">LATENCY DOCTOR</a></strong> - Returns a human-readable latency analysis report.</summary>\n\n**Syntax:** `LATENCY DOCTOR`\n\n**Description:** Returns a human-readable latency analysis report.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-graph/\">LATENCY GRAPH</a></strong> - Returns a latency graph for an event.</summary>\n\n**Syntax:** `LATENCY GRAPH event`\n\n**Description:** Returns a latency graph for an event.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-histogram/\">LATENCY HISTOGRAM</a></strong> - Returns the cumulative distribution of latencies of a subset or all commands.</summary>\n\n**Syntax:** `LATENCY HISTOGRAM [command [command ...]]`\n\n**Description:** Returns the cumulative distribution of latencies of a subset or all commands.\n\n**Complexity:** O(N) where N is the number of commands with latency information being retrieved.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-history/\">LATENCY HISTORY</a></strong> - Returns timestamp-latency samples for an event.</summary>\n\n**Syntax:** `LATENCY HISTORY event`\n\n**Description:** Returns timestamp-latency samples for an event.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-latest/\">LATENCY LATEST</a></strong> - Returns the latest latency samples for all events.</summary>\n\n**Syntax:** `LATENCY LATEST`\n\n**Description:** Returns the latest latency samples for all events.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-reset/\">LATENCY RESET</a></strong> - Resets the latency data for one or more events.</summary>\n\n**Syntax:** `LATENCY RESET [event [event ...]]`\n\n**Description:** Resets the latency data for one or more events.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lolwut/\">LOLWUT</a></strong> - Displays computer art and the Redis version</summary>\n\n**Syntax:** `LOLWUT [VERSION version]`\n\n**Description:** Displays computer art and the Redis version\n\n**Complexity:** \n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-doctor/\">MEMORY DOCTOR</a></strong> - Outputs a memory problems report.</summary>\n\n**Syntax:** `MEMORY DOCTOR`\n\n**Description:** Outputs a memory problems report.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-malloc-stats/\">MEMORY MALLOC-STATS</a></strong> - Returns the allocator statistics.</summary>\n\n**Syntax:** `MEMORY MALLOC-STATS`\n\n**Description:** Returns the allocator statistics.\n\n**Complexity:** Depends on how much memory is allocated, could be slow\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-purge/\">MEMORY PURGE</a></strong> - Asks the allocator to release memory.</summary>\n\n**Syntax:** `MEMORY PURGE`\n\n**Description:** Asks the allocator to release memory.\n\n**Complexity:** Depends on how much memory is allocated, could be slow\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-stats/\">MEMORY STATS</a></strong> - Returns details about memory usage.</summary>\n\n**Syntax:** `MEMORY STATS`\n\n**Description:** Returns details about memory usage.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-usage/\">MEMORY USAGE</a></strong> - Estimates the memory usage of a key.</summary>\n\n**Syntax:** `MEMORY USAGE key [SAMPLES count]`\n\n**Description:** Estimates the memory usage of a key.\n\n**Complexity:** O(N) where N is the number of samples.\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/module-list/\">MODULE LIST</a></strong> - Returns all loaded modules.</summary>\n\n**Syntax:** `MODULE LIST`\n\n**Description:** Returns all loaded modules.\n\n**Complexity:** O(N) where N is the number of loaded modules.\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/module-load/\">MODULE LOAD</a></strong> - Loads a module.</summary>\n\n**Syntax:** `MODULE LOAD path [arg [arg ...]]`\n\n**Description:** Loads a module.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/module-loadex/\">MODULE LOADEX</a></strong> - Loads a module using extended parameters.</summary>\n\n**Syntax:** `MODULE LOADEX path [CONFIG name value [CONFIG name value ...]]\n  [ARGS args [args ...]]`\n\n**Description:** Loads a module using extended parameters.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/module-unload/\">MODULE UNLOAD</a></strong> - Unloads a module.</summary>\n\n**Syntax:** `MODULE UNLOAD name`\n\n**Description:** Unloads a module.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/monitor/\">MONITOR</a></strong> - Listens for all requests received by the server in real-time.</summary>\n\n**Syntax:** `MONITOR`\n\n**Description:** Listens for all requests received by the server in real-time.\n\n**Complexity:** \n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/psync/\">PSYNC</a></strong> - An internal command used in replication.</summary>\n\n**Syntax:** `PSYNC replicationid offset`\n\n**Description:** An internal command used in replication.\n\n**Complexity:** \n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/replconf/\">REPLCONF</a></strong> - An internal command for configuring the replication stream.</summary>\n\n**Syntax:** `REPLCONF`\n\n**Description:** An internal command for configuring the replication stream.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/replicaof/\">REPLICAOF</a></strong> - Configures a server as replica of another, or promotes it to a master.</summary>\n\n**Syntax:** `REPLICAOF <host port | NO ONE>`\n\n**Description:** Configures a server as replica of another, or promotes it to a master.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/restore-asking/\">RESTORE-ASKING</a></strong> - An internal command for migrating keys in a cluster.</summary>\n\n**Syntax:** `RESTORE-ASKING key ttl serialized-value [REPLACE] [ABSTTL]\n  [IDLETIME seconds] [FREQ frequency]`\n\n**Description:** An internal command for migrating keys in a cluster.\n\n**Complexity:** O(1) to create the new key and additional O(N*M) to reconstruct the serialized value, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1). However for sorted set values the complexity is O(N*M*log(N)) because inserting values into sorted sets is O(log(N)).\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/role/\">ROLE</a></strong> - Returns the replication role.</summary>\n\n**Syntax:** `ROLE`\n\n**Description:** Returns the replication role.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.12\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/save/\">SAVE</a></strong> - Synchronously saves the database(s) to disk.</summary>\n\n**Syntax:** `SAVE`\n\n**Description:** Synchronously saves the database(s) to disk.\n\n**Complexity:** O(N) where N is the total number of keys in all databases\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/shutdown/\">SHUTDOWN</a></strong> - Synchronously saves the database(s) to disk and shuts down the Redis server.</summary>\n\n**Syntax:** `SHUTDOWN [NOSAVE | SAVE] [NOW] [FORCE] [ABORT]`\n\n**Description:** Synchronously saves the database(s) to disk and shuts down the Redis server.\n\n**Complexity:** O(N) when saving, where N is the total number of keys in all databases when saving data, otherwise O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/slaveof/\">SLAVEOF</a></strong> - Sets a Redis server as a replica of another, or promotes it to being a master.</summary>\n\n**Syntax:** `SLAVEOF <host port | NO ONE>`\n\n**Description:** Sets a Redis server as a replica of another, or promotes it to being a master.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/slowlog-get/\">SLOWLOG GET</a></strong> - Returns the slow log's entries.</summary>\n\n**Syntax:** `SLOWLOG GET [count]`\n\n**Description:** Returns the slow log's entries.\n\n**Complexity:** O(N) where N is the number of entries returned\n\n**Since:** 2.2.12\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/slowlog-len/\">SLOWLOG LEN</a></strong> - Returns the number of entries in the slow log.</summary>\n\n**Syntax:** `SLOWLOG LEN`\n\n**Description:** Returns the number of entries in the slow log.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.12\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/slowlog-reset/\">SLOWLOG RESET</a></strong> - Clears all entries from the slow log.</summary>\n\n**Syntax:** `SLOWLOG RESET`\n\n**Description:** Clears all entries from the slow log.\n\n**Complexity:** O(N) where N is the number of entries in the slowlog\n\n**Since:** 2.2.12\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/swapdb/\">SWAPDB</a></strong> - Swaps two Redis databases.</summary>\n\n**Syntax:** `SWAPDB index1 index2`\n\n**Description:** Swaps two Redis databases.\n\n**Complexity:** O(N) where N is the count of clients watching or blocking on keys from both databases.\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sync/\">SYNC</a></strong> - An internal command used in replication.</summary>\n\n**Syntax:** `SYNC`\n\n**Description:** An internal command used in replication.\n\n**Complexity:** \n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/time/\">TIME</a></strong> - Returns the server time.</summary>\n\n**Syntax:** `TIME`\n\n**Description:** Returns the server time.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>"},{"id":"cluster-commands","title":"Cluster commands","role":"content","text":"Cluster commands manage Redis Cluster operations.\n\n<details>\n<summary><strong><a href=\"/commands/asking/\">ASKING</a></strong> - Signals that a cluster client is following an -ASK redirect.</summary>\n\n**Syntax:** `ASKING`\n\n**Description:** Signals that a cluster client is following an -ASK redirect.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-addslots/\">CLUSTER ADDSLOTS</a></strong> - Assigns new hash slots to a node.</summary>\n\n**Syntax:** `CLUSTER ADDSLOTS slot [slot ...]`\n\n**Description:** Assigns new hash slots to a node.\n\n**Complexity:** O(N) where N is the total number of hash slot arguments\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-addslotsrange/\">CLUSTER ADDSLOTSRANGE</a></strong> - Assigns new hash slot ranges to a node.</summary>\n\n**Syntax:** `CLUSTER ADDSLOTSRANGE start-slot end-slot [start-slot end-slot ...]`\n\n**Description:** Assigns new hash slot ranges to a node.\n\n**Complexity:** O(N) where N is the total number of the slots between the start slot and end slot arguments.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-bumpepoch/\">CLUSTER BUMPEPOCH</a></strong> - Advances the cluster config epoch.</summary>\n\n**Syntax:** `CLUSTER BUMPEPOCH`\n\n**Description:** Advances the cluster config epoch.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-count-failure-reports/\">CLUSTER COUNT-FAILURE-REPORTS</a></strong> - Returns the number of active failure reports active for a node.</summary>\n\n**Syntax:** `CLUSTER COUNT-FAILURE-REPORTS node-id`\n\n**Description:** Returns the number of active failure reports active for a node.\n\n**Complexity:** O(N) where N is the number of failure reports\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-countkeysinslot/\">CLUSTER COUNTKEYSINSLOT</a></strong> - Returns the number of keys in a hash slot.</summary>\n\n**Syntax:** `CLUSTER COUNTKEYSINSLOT slot`\n\n**Description:** Returns the number of keys in a hash slot.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-delslots/\">CLUSTER DELSLOTS</a></strong> - Sets hash slots as unbound for a node.</summary>\n\n**Syntax:** `CLUSTER DELSLOTS slot [slot ...]`\n\n**Description:** Sets hash slots as unbound for a node.\n\n**Complexity:** O(N) where N is the total number of hash slot arguments\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-delslotsrange/\">CLUSTER DELSLOTSRANGE</a></strong> - Sets hash slot ranges as unbound for a node.</summary>\n\n**Syntax:** `CLUSTER DELSLOTSRANGE start-slot end-slot [start-slot end-slot ...]`\n\n**Description:** Sets hash slot ranges as unbound for a node.\n\n**Complexity:** O(N) where N is the total number of the slots between the start slot and end slot arguments.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-failover/\">CLUSTER FAILOVER</a></strong> - Forces a replica to perform a manual failover of its master.</summary>\n\n**Syntax:** `CLUSTER FAILOVER [FORCE | TAKEOVER]`\n\n**Description:** Forces a replica to perform a manual failover of its master.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-flushslots/\">CLUSTER FLUSHSLOTS</a></strong> - Deletes all slots information from a node.</summary>\n\n**Syntax:** `CLUSTER FLUSHSLOTS`\n\n**Description:** Deletes all slots information from a node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-forget/\">CLUSTER FORGET</a></strong> - Removes a node from the nodes table.</summary>\n\n**Syntax:** `CLUSTER FORGET node-id`\n\n**Description:** Removes a node from the nodes table.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-getkeysinslot/\">CLUSTER GETKEYSINSLOT</a></strong> - Returns the key names in a hash slot.</summary>\n\n**Syntax:** `CLUSTER GETKEYSINSLOT slot count`\n\n**Description:** Returns the key names in a hash slot.\n\n**Complexity:** O(N) where N is the number of requested keys\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-info/\">CLUSTER INFO</a></strong> - Returns information about the state of a node.</summary>\n\n**Syntax:** `CLUSTER INFO`\n\n**Description:** Returns information about the state of a node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-keyslot/\">CLUSTER KEYSLOT</a></strong> - Returns the hash slot for a key.</summary>\n\n**Syntax:** `CLUSTER KEYSLOT key`\n\n**Description:** Returns the hash slot for a key.\n\n**Complexity:** O(N) where N is the number of bytes in the key\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-links/\">CLUSTER LINKS</a></strong> - Returns a list of all TCP links to and from peer nodes.</summary>\n\n**Syntax:** `CLUSTER LINKS`\n\n**Description:** Returns a list of all TCP links to and from peer nodes.\n\n**Complexity:** O(N) where N is the total number of Cluster nodes\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-meet/\">CLUSTER MEET</a></strong> - Forces a node to handshake with another node.</summary>\n\n**Syntax:** `CLUSTER MEET ip port [cluster-bus-port]`\n\n**Description:** Forces a node to handshake with another node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-myid/\">CLUSTER MYID</a></strong> - Returns the ID of a node.</summary>\n\n**Syntax:** `CLUSTER MYID`\n\n**Description:** Returns the ID of a node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-myshardid/\">CLUSTER MYSHARDID</a></strong> - Returns the shard ID of a node.</summary>\n\n**Syntax:** `CLUSTER MYSHARDID`\n\n**Description:** Returns the shard ID of a node.\n\n**Complexity:** O(1)\n\n**Since:** 7.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-nodes/\">CLUSTER NODES</a></strong> - Returns the cluster configuration for a node.</summary>\n\n**Syntax:** `CLUSTER NODES`\n\n**Description:** Returns the cluster configuration for a node.\n\n**Complexity:** O(N) where N is the total number of Cluster nodes\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-replicas/\">CLUSTER REPLICAS</a></strong> - Lists the replica nodes of a master node.</summary>\n\n**Syntax:** `CLUSTER REPLICAS node-id`\n\n**Description:** Lists the replica nodes of a master node.\n\n**Complexity:** O(N) where N is the number of replicas.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-replicate/\">CLUSTER REPLICATE</a></strong> - Configure a node as replica of a master node.</summary>\n\n**Syntax:** `CLUSTER REPLICATE node-id`\n\n**Description:** Configure a node as replica of a master node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-reset/\">CLUSTER RESET</a></strong> - Resets a node.</summary>\n\n**Syntax:** `CLUSTER RESET [HARD | SOFT]`\n\n**Description:** Resets a node.\n\n**Complexity:** O(N) where N is the number of known nodes. The command may execute a FLUSHALL as a side effect.\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-saveconfig/\">CLUSTER SAVECONFIG</a></strong> - Forces a node to save the cluster configuration to disk.</summary>\n\n**Syntax:** `CLUSTER SAVECONFIG`\n\n**Description:** Forces a node to save the cluster configuration to disk.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-set-config-epoch/\">CLUSTER SET-CONFIG-EPOCH</a></strong> - Sets the configuration epoch for a new node.</summary>\n\n**Syntax:** `CLUSTER SET-CONFIG-EPOCH config-epoch`\n\n**Description:** Sets the configuration epoch for a new node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-setslot/\">CLUSTER SETSLOT</a></strong> - Binds a hash slot to a node.</summary>\n\n**Syntax:** `CLUSTER SETSLOT slot <IMPORTING node-id | MIGRATING node-id |\n  NODE node-id | STABLE>`\n\n**Description:** Binds a hash slot to a node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-shards/\">CLUSTER SHARDS</a></strong> - Returns the mapping of cluster slots to shards.</summary>\n\n**Syntax:** `CLUSTER SHARDS`\n\n**Description:** Returns the mapping of cluster slots to shards.\n\n**Complexity:** O(N) where N is the total number of cluster nodes\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-slaves/\">CLUSTER SLAVES</a></strong> - Lists the replica nodes of a master node.</summary>\n\n**Syntax:** `CLUSTER SLAVES node-id`\n\n**Description:** Lists the replica nodes of a master node.\n\n**Complexity:** O(N) where N is the number of replicas.\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-slot-stats/\">CLUSTER SLOT-STATS</a></strong> - Return an array of slot usage statistics for slots assigned to the current node. <span style=\"color: #e74c3c;\">⭐ New in 8.2</span></summary>\n\n**Syntax:** `CLUSTER SLOT-STATS <SLOTSRANGE start-slot end-slot | ORDERBY metric\n  [LIMIT limit] [ASC | DESC]>`\n\n**Description:** Return an array of slot usage statistics for slots assigned to the current node.\n\n**Complexity:** O(N) where N is the total number of slots based on arguments. O(N*log(N)) with ORDERBY subcommand.\n\n**Since:** 8.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-slots/\">CLUSTER SLOTS</a></strong> - Returns the mapping of cluster slots to nodes.</summary>\n\n**Syntax:** `CLUSTER SLOTS`\n\n**Description:** Returns the mapping of cluster slots to nodes.\n\n**Complexity:** O(N) where N is the total number of Cluster nodes\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/readonly/\">READONLY</a></strong> - Enables read-only queries for a connection to a Redis Cluster replica node.</summary>\n\n**Syntax:** `READONLY`\n\n**Description:** Enables read-only queries for a connection to a Redis Cluster replica node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/readwrite/\">READWRITE</a></strong> - Enables read-write queries for a connection to a Reids Cluster replica node.</summary>\n\n**Syntax:** `READWRITE`\n\n**Description:** Enables read-write queries for a connection to a Reids Cluster replica node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>"},{"id":"generic-commands","title":"Generic commands","role":"content","text":"Generic commands work across all data types.\n\n<details>\n<summary><strong><a href=\"/commands/copy/\">COPY</a></strong> - Copies the value of a key to a new key.</summary>\n\n**Syntax:** `COPY source destination [DB destination-db] [REPLACE]`\n\n**Description:** Copies the value of a key to a new key.\n\n**Complexity:** O(N) worst case for collections, where N is the number of nested items. O(1) for string values.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/del/\">DEL</a></strong> - Deletes one or more keys.</summary>\n\n**Syntax:** `DEL key [key ...]`\n\n**Description:** Deletes one or more keys.\n\n**Complexity:** O(N) where N is the number of keys that will be removed. When a key to remove holds a value other than a string, the individual complexity for this key is O(M) where M is the number of elements in the list, set, sorted set or hash. Removing a single key that holds a string value is O(1).\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/dump/\">DUMP</a></strong> - Returns a serialized representation of the value stored at a key.</summary>\n\n**Syntax:** `DUMP key`\n\n**Description:** Returns a serialized representation of the value stored at a key.\n\n**Complexity:** O(1) to access the key and additional O(N*M) to serialize it, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1).\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/exists/\">EXISTS</a></strong> - Determines whether one or more keys exist.</summary>\n\n**Syntax:** `EXISTS key [key ...]`\n\n**Description:** Determines whether one or more keys exist.\n\n**Complexity:** O(N) where N is the number of keys to check.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/expire/\">EXPIRE</a></strong> - Sets the expiration time of a key in seconds.</summary>\n\n**Syntax:** `EXPIRE key seconds [NX | XX | GT | LT]`\n\n**Description:** Sets the expiration time of a key in seconds.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/expireat/\">EXPIREAT</a></strong> - Sets the expiration time of a key to a Unix timestamp.</summary>\n\n**Syntax:** `EXPIREAT key unix-time-seconds [NX | XX | GT | LT]`\n\n**Description:** Sets the expiration time of a key to a Unix timestamp.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/expiretime/\">EXPIRETIME</a></strong> - Returns the expiration time of a key as a Unix timestamp.</summary>\n\n**Syntax:** `EXPIRETIME key`\n\n**Description:** Returns the expiration time of a key as a Unix timestamp.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/keys/\">KEYS</a></strong> - Returns all key names that match a pattern.</summary>\n\n**Syntax:** `KEYS pattern`\n\n**Description:** Returns all key names that match a pattern.\n\n**Complexity:** O(N) with N being the number of keys in the database, under the assumption that the key names in the database and the given pattern have limited length.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/migrate/\">MIGRATE</a></strong> - Atomically transfers a key from one Redis instance to another.</summary>\n\n**Syntax:** `MIGRATE host port <key | \"\"> destination-db timeout [COPY] [REPLACE]\n  [AUTH password | AUTH2 username password] [KEYS key [key ...]]`\n\n**Description:** Atomically transfers a key from one Redis instance to another.\n\n**Complexity:** This command actually executes a DUMP+DEL in the source instance, and a RESTORE in the target instance. See the pages of these commands for time complexity. Also an O(N) data transfer between the two instances is performed.\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/move/\">MOVE</a></strong> - Moves a key to another database.</summary>\n\n**Syntax:** `MOVE key db`\n\n**Description:** Moves a key to another database.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/object-encoding/\">OBJECT ENCODING</a></strong> - Returns the internal encoding of a Redis object.</summary>\n\n**Syntax:** `OBJECT ENCODING key`\n\n**Description:** Returns the internal encoding of a Redis object.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.3\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/object-freq/\">OBJECT FREQ</a></strong> - Returns the logarithmic access frequency counter of a Redis object.</summary>\n\n**Syntax:** `OBJECT FREQ key`\n\n**Description:** Returns the logarithmic access frequency counter of a Redis object.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/object-idletime/\">OBJECT IDLETIME</a></strong> - Returns the time since the last access to a Redis object.</summary>\n\n**Syntax:** `OBJECT IDLETIME key`\n\n**Description:** Returns the time since the last access to a Redis object.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.3\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/object-refcount/\">OBJECT REFCOUNT</a></strong> - Returns the reference count of a value of a key.</summary>\n\n**Syntax:** `OBJECT REFCOUNT key`\n\n**Description:** Returns the reference count of a value of a key.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.3\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/persist/\">PERSIST</a></strong> - Removes the expiration time of a key.</summary>\n\n**Syntax:** `PERSIST key`\n\n**Description:** Removes the expiration time of a key.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pexpire/\">PEXPIRE</a></strong> - Sets the expiration time of a key in milliseconds.</summary>\n\n**Syntax:** `PEXPIRE key milliseconds [NX | XX | GT | LT]`\n\n**Description:** Sets the expiration time of a key in milliseconds.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pexpireat/\">PEXPIREAT</a></strong> - Sets the expiration time of a key to a Unix milliseconds timestamp.</summary>\n\n**Syntax:** `PEXPIREAT key unix-time-milliseconds [NX | XX | GT | LT]`\n\n**Description:** Sets the expiration time of a key to a Unix milliseconds timestamp.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pexpiretime/\">PEXPIRETIME</a></strong> - Returns the expiration time of a key as a Unix milliseconds timestamp.</summary>\n\n**Syntax:** `PEXPIRETIME key`\n\n**Description:** Returns the expiration time of a key as a Unix milliseconds timestamp.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pttl/\">PTTL</a></strong> - Returns the expiration time in milliseconds of a key.</summary>\n\n**Syntax:** `PTTL key`\n\n**Description:** Returns the expiration time in milliseconds of a key.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/randomkey/\">RANDOMKEY</a></strong> - Returns a random key name from the database.</summary>\n\n**Syntax:** `RANDOMKEY`\n\n**Description:** Returns a random key name from the database.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rename/\">RENAME</a></strong> - Renames a key and overwrites the destination.</summary>\n\n**Syntax:** `RENAME key newkey`\n\n**Description:** Renames a key and overwrites the destination.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/renamenx/\">RENAMENX</a></strong> - Renames a key only when the target key name doesn't exist.</summary>\n\n**Syntax:** `RENAMENX key newkey`\n\n**Description:** Renames a key only when the target key name doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/restore/\">RESTORE</a></strong> - Creates a key from the serialized representation of a value.</summary>\n\n**Syntax:** `RESTORE key ttl serialized-value [REPLACE] [ABSTTL]\n  [IDLETIME seconds] [FREQ frequency]`\n\n**Description:** Creates a key from the serialized representation of a value.\n\n**Complexity:** O(1) to create the new key and additional O(N*M) to reconstruct the serialized value, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1). However for sorted set values the complexity is O(N*M*log(N)) because inserting values into sorted sets is O(log(N)).\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-6-2-commands/\">Redis 6.2 Commands Reference</a></strong> - Complete list of all Redis commands available in version 6.2, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 6.2, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-7-2-commands/\">Redis 7.2 Commands Reference</a></strong> - Complete list of all Redis commands available in version 7.2, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 7.2, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-7-4-commands/\">Redis 7.4 Commands Reference</a></strong> - Complete list of all Redis commands available in version 7.4, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 7.4, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-8-0-commands/\">Redis 8.0 Commands Reference</a></strong> - Complete list of all Redis commands available in version 8.0, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 8.0, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-8-2-commands/\">Redis 8.2 Commands Reference</a></strong> - Complete list of all Redis commands available in version 8.2, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 8.2, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-8-4-commands/\">Redis 8.4 Commands Reference</a></strong> - Complete list of all Redis commands available in version 8.4, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 8.4, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/scan/\">SCAN</a></strong> - Iterates over the key names in the database.</summary>\n\n**Syntax:** `SCAN cursor [MATCH pattern] [COUNT count] [TYPE type]`\n\n**Description:** Iterates over the key names in the database.\n\n**Complexity:** O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sort/\">SORT</a></strong> - Sorts the elements in a list, a set, or a sorted set, optionally storing the result.</summary>\n\n**Syntax:** `SORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern\n  ...]] [ASC | DESC] [ALPHA] [STORE destination]`\n\n**Description:** Sorts the elements in a list, a set, or a sorted set, optionally storing the result.\n\n**Complexity:** O(N+M*log(M)) where N is the number of elements in the list or set to sort, and M the number of returned elements. When the elements are not sorted, complexity is O(N).\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sort_ro/\">SORT_RO</a></strong> - Returns the sorted elements of a list, a set, or a sorted set.</summary>\n\n**Syntax:** `SORT RO key [BY pattern] [LIMIT offset count] [GET pattern [GET\n  pattern ...]] [ASC | DESC] [ALPHA]`\n\n**Description:** Returns the sorted elements of a list, a set, or a sorted set.\n\n**Complexity:** O(N+M*log(M)) where N is the number of elements in the list or set to sort, and M the number of returned elements. When the elements are not sorted, complexity is O(N).\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/touch/\">TOUCH</a></strong> - Returns the number of existing keys out of those specified after updating the time they were last accessed.</summary>\n\n**Syntax:** `TOUCH key [key ...]`\n\n**Description:** Returns the number of existing keys out of those specified after updating the time they were last accessed.\n\n**Complexity:** O(N) where N is the number of keys that will be touched.\n\n**Since:** 3.2.1\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ttl/\">TTL</a></strong> - Returns the expiration time in seconds of a key.</summary>\n\n**Syntax:** `TTL key`\n\n**Description:** Returns the expiration time in seconds of a key.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/type/\">TYPE</a></strong> - Determines the type of value stored at a key.</summary>\n\n**Syntax:** `TYPE key`\n\n**Description:** Determines the type of value stored at a key.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/unlink/\">UNLINK</a></strong> - Asynchronously deletes one or more keys.</summary>\n\n**Syntax:** `UNLINK key [key ...]`\n\n**Description:** Asynchronously deletes one or more keys.\n\n**Complexity:** O(1) for each key removed regardless of its size. Then the command does O(N) work in a different thread in order to reclaim memory, where N is the number of allocations the deleted objects where composed of.\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/wait/\">WAIT</a></strong> - Blocks until the asynchronous replication of all preceding write commands sent by the connection is completed.</summary>\n\n**Syntax:** `WAIT numreplicas timeout`\n\n**Description:** Blocks until the asynchronous replication of all preceding write commands sent by the connection is completed.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/waitaof/\">WAITAOF</a></strong> - Blocks until all of the preceding write commands sent by the connection are written to the append-only file of the master and/or replicas.</summary>\n\n**Syntax:** `WAITAOF numlocal numreplicas timeout`\n\n**Description:** Blocks until all of the preceding write commands sent by the connection are written to the append-only file of the master and/or replicas.\n\n**Complexity:** O(1)\n\n**Since:** 7.2.0\n\n</details>"}],"examples":[]}
{"schema_version":2,"id":"commands/redis-8-4-commands","title":"Redis 8.4 Commands Reference","url":"https://redis.io/docs/latest/commands/redis-8-4-commands/","summary":"Complete list of all Redis commands available in version 8.4, organized by functional group","tags":["docs","develop","stack","oss","rs","rc","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"20ca3f69e2d7ccdc0ddb33daf73cf9e35f4a286c71a5d47c07d36d18fd6bbb4c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This page provides a comprehensive reference of all Redis commands available in Redis 8.4, organized by functional group. Each command includes its description and syntax in a collapsible section for easy navigation.\n\n> [!NOTE]\n> Redis 8.4 includes all commands from previous versions plus new commands introduced in 8.4. Commands marked with **⭐ New in 8.4** were added in this release.\n>\n> Command page links take you to the *current implementation* of each command."},{"id":"quick-navigation","title":"Quick Navigation","role":"content","text":"- [String commands](#string-commands)\n- [Hash commands](#hash-commands)\n- [List commands](#list-commands)\n- [Set commands](#set-commands)\n- [Sorted set commands](#sorted-set-commands)\n- [Stream commands](#stream-commands)\n- [Bitmap commands](#bitmap-commands)\n- [HyperLogLog commands](#hyperloglog-commands)\n- [Geospatial commands](#geospatial-commands)\n- [JSON commands](#json-commands)\n- [Search commands](#search-commands)\n- [Time series commands](#time-series-commands)\n- [Vector set commands](#vector-set-commands)\n- [Pub/Sub commands](#pubsub-commands)\n- [Transaction commands](#transaction-commands)\n- [Scripting commands](#scripting-commands)\n- [Connection commands](#connection-commands)\n- [Server commands](#server-commands)\n- [Cluster commands](#cluster-commands)\n- [Generic commands](#generic-commands)\n\n---"},{"id":"string-commands","title":"String commands","role":"content","text":"String commands operate on string values, the most basic Redis data type.\n\n<details>\n<summary><strong><a href=\"/commands/append/\">APPEND</a></strong> - Appends a string to the value of a key. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `APPEND key value`\n\n**Description:** Appends a string to the value of a key. Creates the key if it doesn't exist.\n\n**Complexity:** O(1). The amortized time complexity is O(1) assuming the appended value is small and the already present value is of any size, since the dynamic string library used by Redis will double the free space available on every reallocation.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/decr/\">DECR</a></strong> - Decrements the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `DECR key`\n\n**Description:** Decrements the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/decrby/\">DECRBY</a></strong> - Decrements a number from the integer value of a key. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `DECRBY key decrement`\n\n**Description:** Decrements a number from the integer value of a key. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/delex/\">DELEX</a></strong> - Conditionally removes the specified key based on value or hash digest comparison. <span style=\"color: #e74c3c;\">⭐ New in 8.4</span></summary>\n\n**Syntax:** `DELEX key [IFEQ ifeq-value | IFNE ifne-value | IFDEQ ifdeq-digest |\n  IFDNE ifdne-digest]`\n\n**Description:** Conditionally removes the specified key based on value or hash digest comparison.\n\n**Complexity:** O(1) for IFEQ/IFNE, O(N) for IFDEQ/IFDNE where N is the length of the string value.\n\n**Since:** 8.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/digest/\">DIGEST</a></strong> - Returns the hash digest of a string value as a hexadecimal string. <span style=\"color: #e74c3c;\">⭐ New in 8.4</span></summary>\n\n**Syntax:** `DIGEST key`\n\n**Description:** Returns the hash digest of a string value as a hexadecimal string.\n\n**Complexity:** O(N) where N is the length of the string value.\n\n**Since:** 8.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/get/\">GET</a></strong> - Returns the string value of a key.</summary>\n\n**Syntax:** `GET key`\n\n**Description:** Returns the string value of a key.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getdel/\">GETDEL</a></strong> - Returns the string value of a key after deleting the key.</summary>\n\n**Syntax:** `GETDEL key`\n\n**Description:** Returns the string value of a key after deleting the key.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getex/\">GETEX</a></strong> - Returns the string value of a key after setting its expiration time.</summary>\n\n**Syntax:** `GETEX key [EX seconds | PX milliseconds | EXAT unix-time-seconds |\n  PXAT unix-time-milliseconds | PERSIST]`\n\n**Description:** Returns the string value of a key after setting its expiration time.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getrange/\">GETRANGE</a></strong> - Returns a substring of the string stored at a key.</summary>\n\n**Syntax:** `GETRANGE key start end`\n\n**Description:** Returns a substring of the string stored at a key.\n\n**Complexity:** O(N) where N is the length of the returned string. The complexity is ultimately determined by the returned length, but because creating a substring from an existing string is very cheap, it can be considered O(1) for small strings.\n\n**Since:** 2.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getset/\">GETSET</a></strong> - Returns the previous string value of a key after setting it to a new value.</summary>\n\n**Syntax:** `GETSET key value`\n\n**Description:** Returns the previous string value of a key after setting it to a new value.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/incr/\">INCR</a></strong> - Increments the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `INCR key`\n\n**Description:** Increments the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/incrby/\">INCRBY</a></strong> - Increments the integer value of a key by a number. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `INCRBY key increment`\n\n**Description:** Increments the integer value of a key by a number. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/incrbyfloat/\">INCRBYFLOAT</a></strong> - Increment the floating point value of a key by a number. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `INCRBYFLOAT key increment`\n\n**Description:** Increment the floating point value of a key by a number. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lcs/\">LCS</a></strong> - Finds the longest common substring.</summary>\n\n**Syntax:** `LCS key1 key2 [LEN] [IDX] [MINMATCHLEN min-match-len] [WITHMATCHLEN]`\n\n**Description:** Finds the longest common substring.\n\n**Complexity:** O(N*M) where N and M are the lengths of s1 and s2, respectively\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/mget/\">MGET</a></strong> - Atomically returns the string values of one or more keys.</summary>\n\n**Syntax:** `MGET key [key ...]`\n\n**Description:** Atomically returns the string values of one or more keys.\n\n**Complexity:** O(N) where N is the number of keys to retrieve.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/mset/\">MSET</a></strong> - Atomically creates or modifies the string values of one or more keys.</summary>\n\n**Syntax:** `MSET key value [key value ...]`\n\n**Description:** Atomically creates or modifies the string values of one or more keys.\n\n**Complexity:** O(N) where N is the number of keys to set.\n\n**Since:** 1.0.1\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/msetex/\">MSETEX</a></strong> - Atomically sets multiple string keys with a shared expiration in a single operation. <span style=\"color: #e74c3c;\">⭐ New in 8.4</span></summary>\n\n**Syntax:** `MSETEX numkeys key value [key value ...] [NX | XX] [EX seconds |\n  PX milliseconds | EXAT unix-time-seconds |\n  PXAT unix-time-milliseconds | KEEPTTL]`\n\n**Description:** Atomically sets multiple string keys with a shared expiration in a single operation.\n\n**Complexity:** O(N) where N is the number of keys to set.\n\n**Since:** 8.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/msetnx/\">MSETNX</a></strong> - Atomically modifies the string values of one or more keys only when all keys don't exist.</summary>\n\n**Syntax:** `MSETNX key value [key value ...]`\n\n**Description:** Atomically modifies the string values of one or more keys only when all keys don't exist.\n\n**Complexity:** O(N) where N is the number of keys to set.\n\n**Since:** 1.0.1\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/psetex/\">PSETEX</a></strong> - Sets both string value and expiration time in milliseconds of a key. The key is created if it doesn't exist.</summary>\n\n**Syntax:** `PSETEX key milliseconds value`\n\n**Description:** Sets both string value and expiration time in milliseconds of a key. The key is created if it doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/set/\">SET</a></strong> - Sets the string value of a key, ignoring its type. The key is created if it doesn't exist.</summary>\n\n**Syntax:** `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]`\n\n**Description:** Sets the string value of a key, ignoring its type. The key is created if it doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/setex/\">SETEX</a></strong> - Sets the string value and expiration time of a key. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `SETEX key seconds value`\n\n**Description:** Sets the string value and expiration time of a key. Creates the key if it doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/setnx/\">SETNX</a></strong> - Set the string value of a key only when the key doesn't exist.</summary>\n\n**Syntax:** `SETNX key value`\n\n**Description:** Set the string value of a key only when the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/setrange/\">SETRANGE</a></strong> - Overwrites a part of a string value with another by an offset. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `SETRANGE key offset value`\n\n**Description:** Overwrites a part of a string value with another by an offset. Creates the key if it doesn't exist.\n\n**Complexity:** O(1), not counting the time taken to copy the new string in place. Usually, this string is very small so the amortized complexity is O(1). Otherwise, complexity is O(M) with M being the length of the value argument.\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/strlen/\">STRLEN</a></strong> - Returns the length of a string value.</summary>\n\n**Syntax:** `STRLEN key`\n\n**Description:** Returns the length of a string value.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/substr/\">SUBSTR</a></strong> - Returns a substring from a string value.</summary>\n\n**Syntax:** `SUBSTR key start end`\n\n**Description:** Returns a substring from a string value.\n\n**Complexity:** O(N) where N is the length of the returned string. The complexity is ultimately determined by the returned length, but because creating a substring from an existing string is very cheap, it can be considered O(1) for small strings.\n\n**Since:** 1.0.0\n\n</details>"},{"id":"hash-commands","title":"Hash commands","role":"content","text":"Hash commands operate on hash data structures, which map string fields to string values.\n\n<details>\n<summary><strong><a href=\"/commands/hdel/\">HDEL</a></strong> - Deletes one or more fields and their values from a hash. Deletes the hash if no fields remain.</summary>\n\n**Syntax:** `HDEL key field [field ...]`\n\n**Description:** Deletes one or more fields and their values from a hash. Deletes the hash if no fields remain.\n\n**Complexity:** O(N) where N is the number of fields to be removed.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hexists/\">HEXISTS</a></strong> - Determines whether a field exists in a hash.</summary>\n\n**Syntax:** `HEXISTS key field`\n\n**Description:** Determines whether a field exists in a hash.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hexpire/\">HEXPIRE</a></strong> - Set expiry for hash field using relative time to expire (seconds)</summary>\n\n**Syntax:** `HEXPIRE key seconds [NX | XX | GT | LT] FIELDS numfields field\n  [field ...]`\n\n**Description:** Set expiry for hash field using relative time to expire (seconds)\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hexpireat/\">HEXPIREAT</a></strong> - Set expiry for hash field using an absolute Unix timestamp (seconds)</summary>\n\n**Syntax:** `HEXPIREAT key unix-time-seconds [NX | XX | GT | LT] FIELDS numfields\n  field [field ...]`\n\n**Description:** Set expiry for hash field using an absolute Unix timestamp (seconds)\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hexpiretime/\">HEXPIRETIME</a></strong> - Returns the expiration time of a hash field as a Unix timestamp, in seconds.</summary>\n\n**Syntax:** `HEXPIRETIME key FIELDS numfields field [field ...]`\n\n**Description:** Returns the expiration time of a hash field as a Unix timestamp, in seconds.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hget/\">HGET</a></strong> - Returns the value of a field in a hash.</summary>\n\n**Syntax:** `HGET key field`\n\n**Description:** Returns the value of a field in a hash.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hgetall/\">HGETALL</a></strong> - Returns all fields and values in a hash.</summary>\n\n**Syntax:** `HGETALL key`\n\n**Description:** Returns all fields and values in a hash.\n\n**Complexity:** O(N) where N is the size of the hash.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hgetdel/\">HGETDEL</a></strong> - Returns the value of a field and deletes it from the hash.</summary>\n\n**Syntax:** `HGETDEL key FIELDS numfields field [field ...]`\n\n**Description:** Returns the value of a field and deletes it from the hash.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hgetex/\">HGETEX</a></strong> - Get the value of one or more fields of a given hash key, and optionally set their expiration.</summary>\n\n**Syntax:** `HGETEX key [EX seconds | PX milliseconds | EXAT unix-time-seconds |\n  PXAT unix-time-milliseconds | PERSIST] FIELDS numfields field\n  [field ...]`\n\n**Description:** Get the value of one or more fields of a given hash key, and optionally set their expiration.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hincrby/\">HINCRBY</a></strong> - Increments the integer value of a field in a hash by a number. Uses 0 as initial value if the field doesn't exist.</summary>\n\n**Syntax:** `HINCRBY key field increment`\n\n**Description:** Increments the integer value of a field in a hash by a number. Uses 0 as initial value if the field doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hincrbyfloat/\">HINCRBYFLOAT</a></strong> - Increments the floating point value of a field by a number. Uses 0 as initial value if the field doesn't exist.</summary>\n\n**Syntax:** `HINCRBYFLOAT key field increment`\n\n**Description:** Increments the floating point value of a field by a number. Uses 0 as initial value if the field doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hkeys/\">HKEYS</a></strong> - Returns all fields in a hash.</summary>\n\n**Syntax:** `HKEYS key`\n\n**Description:** Returns all fields in a hash.\n\n**Complexity:** O(N) where N is the size of the hash.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hlen/\">HLEN</a></strong> - Returns the number of fields in a hash.</summary>\n\n**Syntax:** `HLEN key`\n\n**Description:** Returns the number of fields in a hash.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hmget/\">HMGET</a></strong> - Returns the values of all fields in a hash.</summary>\n\n**Syntax:** `HMGET key field [field ...]`\n\n**Description:** Returns the values of all fields in a hash.\n\n**Complexity:** O(N) where N is the number of fields being requested.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hmset/\">HMSET</a></strong> - Sets the values of multiple fields.</summary>\n\n**Syntax:** `HMSET key field value [field value ...]`\n\n**Description:** Sets the values of multiple fields.\n\n**Complexity:** O(N) where N is the number of fields being set.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hpersist/\">HPERSIST</a></strong> - Removes the expiration time for each specified field</summary>\n\n**Syntax:** `HPERSIST key FIELDS numfields field [field ...]`\n\n**Description:** Removes the expiration time for each specified field\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hpexpire/\">HPEXPIRE</a></strong> - Set expiry for hash field using relative time to expire (milliseconds)</summary>\n\n**Syntax:** `HPEXPIRE key milliseconds [NX | XX | GT | LT] FIELDS numfields field\n  [field ...]`\n\n**Description:** Set expiry for hash field using relative time to expire (milliseconds)\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hpexpireat/\">HPEXPIREAT</a></strong> - Set expiry for hash field using an absolute Unix timestamp (milliseconds)</summary>\n\n**Syntax:** `HPEXPIREAT key unix-time-milliseconds [NX | XX | GT | LT]\n  FIELDS numfields field [field ...]`\n\n**Description:** Set expiry for hash field using an absolute Unix timestamp (milliseconds)\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hpexpiretime/\">HPEXPIRETIME</a></strong> - Returns the expiration time of a hash field as a Unix timestamp, in msec.</summary>\n\n**Syntax:** `HPEXPIRETIME key FIELDS numfields field [field ...]`\n\n**Description:** Returns the expiration time of a hash field as a Unix timestamp, in msec.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hpttl/\">HPTTL</a></strong> - Returns the TTL in milliseconds of a hash field.</summary>\n\n**Syntax:** `HPTTL key FIELDS numfields field [field ...]`\n\n**Description:** Returns the TTL in milliseconds of a hash field.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hrandfield/\">HRANDFIELD</a></strong> - Returns one or more random fields from a hash.</summary>\n\n**Syntax:** `HRANDFIELD key [count [WITHVALUES]]`\n\n**Description:** Returns one or more random fields from a hash.\n\n**Complexity:** O(N) where N is the number of fields returned\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hscan/\">HSCAN</a></strong> - Iterates over fields and values of a hash.</summary>\n\n**Syntax:** `HSCAN key cursor [MATCH pattern] [COUNT count] [NOVALUES]`\n\n**Description:** Iterates over fields and values of a hash.\n\n**Complexity:** O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hset/\">HSET</a></strong> - Creates or modifies the value of a field in a hash.</summary>\n\n**Syntax:** `HSET key field value [field value ...]`\n\n**Description:** Creates or modifies the value of a field in a hash.\n\n**Complexity:** O(1) for each field/value pair added, so O(N) to add N field/value pairs when the command is called with multiple field/value pairs.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hsetex/\">HSETEX</a></strong> - Set the value of one or more fields of a given hash key, and optionally set their expiration.</summary>\n\n**Syntax:** `HSETEX key [FNX | FXX] [EX seconds | PX milliseconds |\n  EXAT unix-time-seconds | PXAT unix-time-milliseconds | KEEPTTL]\n  FIELDS numfields field value [field value ...]`\n\n**Description:** Set the value of one or more fields of a given hash key, and optionally set their expiration.\n\n**Complexity:** O(N) where N is the number of fields being set.\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hsetnx/\">HSETNX</a></strong> - Sets the value of a field in a hash only when the field doesn't exist.</summary>\n\n**Syntax:** `HSETNX key field value`\n\n**Description:** Sets the value of a field in a hash only when the field doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hstrlen/\">HSTRLEN</a></strong> - Returns the length of the value of a field.</summary>\n\n**Syntax:** `HSTRLEN key field`\n\n**Description:** Returns the length of the value of a field.\n\n**Complexity:** O(1)\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/httl/\">HTTL</a></strong> - Returns the TTL in seconds of a hash field.</summary>\n\n**Syntax:** `HTTL key FIELDS numfields field [field ...]`\n\n**Description:** Returns the TTL in seconds of a hash field.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hvals/\">HVALS</a></strong> - Returns all values in a hash.</summary>\n\n**Syntax:** `HVALS key`\n\n**Description:** Returns all values in a hash.\n\n**Complexity:** O(N) where N is the size of the hash.\n\n**Since:** 2.0.0\n\n</details>"},{"id":"list-commands","title":"List commands","role":"content","text":"List commands operate on lists of strings, ordered by insertion order.\n\n<details>\n<summary><strong><a href=\"/commands/blmove/\">BLMOVE</a></strong> - Pops an element from a list, pushes it to another list and returns it. Blocks until an element is available otherwise. Deletes the list if the last element was moved.</summary>\n\n**Syntax:** `BLMOVE source destination <LEFT | RIGHT> <LEFT | RIGHT> timeout`\n\n**Description:** Pops an element from a list, pushes it to another list and returns it. Blocks until an element is available otherwise. Deletes the list if the last element was moved.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/blmpop/\">BLMPOP</a></strong> - Pops the first element from one of multiple lists. Blocks until an element is available otherwise. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `BLMPOP timeout numkeys key [key ...] <LEFT | RIGHT> [COUNT count]`\n\n**Description:** Pops the first element from one of multiple lists. Blocks until an element is available otherwise. Deletes the list if the last element was popped.\n\n**Complexity:** O(N+M) where N is the number of provided keys and M is the number of elements returned.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/blpop/\">BLPOP</a></strong> - Removes and returns the first element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `BLPOP key [key ...] timeout`\n\n**Description:** Removes and returns the first element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.\n\n**Complexity:** O(N) where N is the number of provided keys.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/brpop/\">BRPOP</a></strong> - Removes and returns the last element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `BRPOP key [key ...] timeout`\n\n**Description:** Removes and returns the last element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.\n\n**Complexity:** O(N) where N is the number of provided keys.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/brpoplpush/\">BRPOPLPUSH</a></strong> - Pops an element from a list, pushes it to another list and returns it. Block until an element is available otherwise. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `BRPOPLPUSH source destination timeout`\n\n**Description:** Pops an element from a list, pushes it to another list and returns it. Block until an element is available otherwise. Deletes the list if the last element was popped.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lindex/\">LINDEX</a></strong> - Returns an element from a list by its index.</summary>\n\n**Syntax:** `LINDEX key index`\n\n**Description:** Returns an element from a list by its index.\n\n**Complexity:** O(N) where N is the number of elements to traverse to get to the element at index. This makes asking for the first or the last element of the list O(1).\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/linsert/\">LINSERT</a></strong> - Inserts an element before or after another element in a list.</summary>\n\n**Syntax:** `LINSERT key <BEFORE | AFTER> pivot element`\n\n**Description:** Inserts an element before or after another element in a list.\n\n**Complexity:** O(N) where N is the number of elements to traverse before seeing the value pivot. This means that inserting somewhere on the left end on the list (head) can be considered O(1) and inserting somewhere on the right end (tail) is O(N).\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/llen/\">LLEN</a></strong> - Returns the length of a list.</summary>\n\n**Syntax:** `LLEN key`\n\n**Description:** Returns the length of a list.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lmove/\">LMOVE</a></strong> - Returns an element after popping it from one list and pushing it to another. Deletes the list if the last element was moved.</summary>\n\n**Syntax:** `LMOVE source destination <LEFT | RIGHT> <LEFT | RIGHT>`\n\n**Description:** Returns an element after popping it from one list and pushing it to another. Deletes the list if the last element was moved.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lmpop/\">LMPOP</a></strong> - Returns multiple elements from a list after removing them. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `LMPOP numkeys key [key ...] <LEFT | RIGHT> [COUNT count]`\n\n**Description:** Returns multiple elements from a list after removing them. Deletes the list if the last element was popped.\n\n**Complexity:** O(N+M) where N is the number of provided keys and M is the number of elements returned.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lpop/\">LPOP</a></strong> - Returns the first elements in a list after removing it. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `LPOP key [count]`\n\n**Description:** Returns the first elements in a list after removing it. Deletes the list if the last element was popped.\n\n**Complexity:** O(N) where N is the number of elements returned\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lpos/\">LPOS</a></strong> - Returns the index of matching elements in a list.</summary>\n\n**Syntax:** `LPOS key element [RANK rank] [COUNT num-matches] [MAXLEN len]`\n\n**Description:** Returns the index of matching elements in a list.\n\n**Complexity:** O(N) where N is the number of elements in the list, for the average case. When searching for elements near the head or the tail of the list, or when the MAXLEN option is provided, the command may run in constant time.\n\n**Since:** 6.0.6\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lpush/\">LPUSH</a></strong> - Prepends one or more elements to a list. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `LPUSH key element [element ...]`\n\n**Description:** Prepends one or more elements to a list. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lpushx/\">LPUSHX</a></strong> - Prepends one or more elements to a list only when the list exists.</summary>\n\n**Syntax:** `LPUSHX key element [element ...]`\n\n**Description:** Prepends one or more elements to a list only when the list exists.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lrange/\">LRANGE</a></strong> - Returns a range of elements from a list.</summary>\n\n**Syntax:** `LRANGE key start stop`\n\n**Description:** Returns a range of elements from a list.\n\n**Complexity:** O(S+N) where S is the distance of start offset from HEAD for small lists, from nearest end (HEAD or TAIL) for large lists; and N is the number of elements in the specified range.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lrem/\">LREM</a></strong> - Removes elements from a list. Deletes the list if the last element was removed.</summary>\n\n**Syntax:** `LREM key count element`\n\n**Description:** Removes elements from a list. Deletes the list if the last element was removed.\n\n**Complexity:** O(N+M) where N is the length of the list and M is the number of elements removed.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lset/\">LSET</a></strong> - Sets the value of an element in a list by its index.</summary>\n\n**Syntax:** `LSET key index element`\n\n**Description:** Sets the value of an element in a list by its index.\n\n**Complexity:** O(N) where N is the length of the list. Setting either the first or the last element of the list is O(1).\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ltrim/\">LTRIM</a></strong> - Removes elements from both ends a list. Deletes the list if all elements were trimmed.</summary>\n\n**Syntax:** `LTRIM key start stop`\n\n**Description:** Removes elements from both ends a list. Deletes the list if all elements were trimmed.\n\n**Complexity:** O(N) where N is the number of elements to be removed by the operation.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rpop/\">RPOP</a></strong> - Returns and removes the last elements of a list. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `RPOP key [count]`\n\n**Description:** Returns and removes the last elements of a list. Deletes the list if the last element was popped.\n\n**Complexity:** O(N) where N is the number of elements returned\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rpoplpush/\">RPOPLPUSH</a></strong> - Returns the last element of a list after removing and pushing it to another list. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `RPOPLPUSH source destination`\n\n**Description:** Returns the last element of a list after removing and pushing it to another list. Deletes the list if the last element was popped.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rpush/\">RPUSH</a></strong> - Appends one or more elements to a list. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `RPUSH key element [element ...]`\n\n**Description:** Appends one or more elements to a list. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rpushx/\">RPUSHX</a></strong> - Appends an element to a list only when the list exists.</summary>\n\n**Syntax:** `RPUSHX key element [element ...]`\n\n**Description:** Appends an element to a list only when the list exists.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 2.2.0\n\n</details>"},{"id":"set-commands","title":"Set commands","role":"content","text":"Set commands operate on unordered collections of unique strings.\n\n<details>\n<summary><strong><a href=\"/commands/sadd/\">SADD</a></strong> - Adds one or more members to a set. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `SADD key member [member ...]`\n\n**Description:** Adds one or more members to a set. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/scard/\">SCARD</a></strong> - Returns the number of members in a set.</summary>\n\n**Syntax:** `SCARD key`\n\n**Description:** Returns the number of members in a set.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sdiff/\">SDIFF</a></strong> - Returns the difference of multiple sets.</summary>\n\n**Syntax:** `SDIFF key [key ...]`\n\n**Description:** Returns the difference of multiple sets.\n\n**Complexity:** O(N) where N is the total number of elements in all given sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sdiffstore/\">SDIFFSTORE</a></strong> - Stores the difference of multiple sets in a key.</summary>\n\n**Syntax:** `SDIFFSTORE destination key [key ...]`\n\n**Description:** Stores the difference of multiple sets in a key.\n\n**Complexity:** O(N) where N is the total number of elements in all given sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sinter/\">SINTER</a></strong> - Returns the intersect of multiple sets.</summary>\n\n**Syntax:** `SINTER key [key ...]`\n\n**Description:** Returns the intersect of multiple sets.\n\n**Complexity:** O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sintercard/\">SINTERCARD</a></strong> - Returns the number of members of the intersect of multiple sets.</summary>\n\n**Syntax:** `SINTERCARD numkeys key [key ...] [LIMIT limit]`\n\n**Description:** Returns the number of members of the intersect of multiple sets.\n\n**Complexity:** O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sinterstore/\">SINTERSTORE</a></strong> - Stores the intersect of multiple sets in a key.</summary>\n\n**Syntax:** `SINTERSTORE destination key [key ...]`\n\n**Description:** Stores the intersect of multiple sets in a key.\n\n**Complexity:** O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sismember/\">SISMEMBER</a></strong> - Determines whether a member belongs to a set.</summary>\n\n**Syntax:** `SISMEMBER key member`\n\n**Description:** Determines whether a member belongs to a set.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/smembers/\">SMEMBERS</a></strong> - Returns all members of a set.</summary>\n\n**Syntax:** `SMEMBERS key`\n\n**Description:** Returns all members of a set.\n\n**Complexity:** O(N) where N is the set cardinality.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/smismember/\">SMISMEMBER</a></strong> - Determines whether multiple members belong to a set.</summary>\n\n**Syntax:** `SMISMEMBER key member [member ...]`\n\n**Description:** Determines whether multiple members belong to a set.\n\n**Complexity:** O(N) where N is the number of elements being checked for membership\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/smove/\">SMOVE</a></strong> - Moves a member from one set to another.</summary>\n\n**Syntax:** `SMOVE source destination member`\n\n**Description:** Moves a member from one set to another.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/spop/\">SPOP</a></strong> - Returns one or more random members from a set after removing them. Deletes the set if the last member was popped.</summary>\n\n**Syntax:** `SPOP key [count]`\n\n**Description:** Returns one or more random members from a set after removing them. Deletes the set if the last member was popped.\n\n**Complexity:** Without the count argument O(1), otherwise O(N) where N is the value of the passed count.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/srandmember/\">SRANDMEMBER</a></strong> - Get one or multiple random members from a set</summary>\n\n**Syntax:** `SRANDMEMBER key [count]`\n\n**Description:** Get one or multiple random members from a set\n\n**Complexity:** Without the count argument O(1), otherwise O(N) where N is the absolute value of the passed count.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/srem/\">SREM</a></strong> - Removes one or more members from a set. Deletes the set if the last member was removed.</summary>\n\n**Syntax:** `SREM key member [member ...]`\n\n**Description:** Removes one or more members from a set. Deletes the set if the last member was removed.\n\n**Complexity:** O(N) where N is the number of members to be removed.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sscan/\">SSCAN</a></strong> - Iterates over members of a set.</summary>\n\n**Syntax:** `SSCAN key cursor [MATCH pattern] [COUNT count]`\n\n**Description:** Iterates over members of a set.\n\n**Complexity:** O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sunion/\">SUNION</a></strong> - Returns the union of multiple sets.</summary>\n\n**Syntax:** `SUNION key [key ...]`\n\n**Description:** Returns the union of multiple sets.\n\n**Complexity:** O(N) where N is the total number of elements in all given sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sunionstore/\">SUNIONSTORE</a></strong> - Stores the union of multiple sets in a key.</summary>\n\n**Syntax:** `SUNIONSTORE destination key [key ...]`\n\n**Description:** Stores the union of multiple sets in a key.\n\n**Complexity:** O(N) where N is the total number of elements in all given sets.\n\n**Since:** 1.0.0\n\n</details>"},{"id":"sorted-set-commands","title":"Sorted set commands","role":"content","text":"Sorted set commands operate on sets of unique strings ordered by a score.\n\n<details>\n<summary><strong><a href=\"/commands/bzmpop/\">BZMPOP</a></strong> - Removes and returns a member by score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.</summary>\n\n**Syntax:** `BZMPOP timeout numkeys key [key ...] <MIN | MAX> [COUNT count]`\n\n**Description:** Removes and returns a member by score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.\n\n**Complexity:** O(K) + O(M*log(N)) where K is the number of provided keys, N being the number of elements in the sorted set, and M being the number of elements popped.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bzpopmax/\">BZPOPMAX</a></strong> - Removes and returns the member with the highest score from one or more sorted sets. Blocks until a member available otherwise.  Deletes the sorted set if the last element was popped.</summary>\n\n**Syntax:** `BZPOPMAX key [key ...] timeout`\n\n**Description:** Removes and returns the member with the highest score from one or more sorted sets. Blocks until a member available otherwise.  Deletes the sorted set if the last element was popped.\n\n**Complexity:** O(log(N)) with N being the number of elements in the sorted set.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bzpopmin/\">BZPOPMIN</a></strong> - Removes and returns the member with the lowest score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.</summary>\n\n**Syntax:** `BZPOPMIN key [key ...] timeout`\n\n**Description:** Removes and returns the member with the lowest score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.\n\n**Complexity:** O(log(N)) with N being the number of elements in the sorted set.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zadd/\">ZADD</a></strong> - Adds one or more members to a sorted set, or updates their scores. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `ZADD key [NX | XX] [GT | LT] [CH] [INCR] score member [score member\n  ...]`\n\n**Description:** Adds one or more members to a sorted set, or updates their scores. Creates the key if it doesn't exist.\n\n**Complexity:** O(log(N)) for each item added, where N is the number of elements in the sorted set.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zcard/\">ZCARD</a></strong> - Returns the number of members in a sorted set.</summary>\n\n**Syntax:** `ZCARD key`\n\n**Description:** Returns the number of members in a sorted set.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zcount/\">ZCOUNT</a></strong> - Returns the count of members in a sorted set that have scores within a range.</summary>\n\n**Syntax:** `ZCOUNT key min max`\n\n**Description:** Returns the count of members in a sorted set that have scores within a range.\n\n**Complexity:** O(log(N)) with N being the number of elements in the sorted set.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zdiff/\">ZDIFF</a></strong> - Returns the difference between multiple sorted sets.</summary>\n\n**Syntax:** `ZDIFF numkeys key [key ...] [WITHSCORES]`\n\n**Description:** Returns the difference between multiple sorted sets.\n\n**Complexity:** O(L + (N-K)log(N)) worst case where L is the total number of elements in all the sets, N is the size of the first set, and K is the size of the result set.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zdiffstore/\">ZDIFFSTORE</a></strong> - Stores the difference of multiple sorted sets in a key.</summary>\n\n**Syntax:** `ZDIFFSTORE destination numkeys key [key ...]`\n\n**Description:** Stores the difference of multiple sorted sets in a key.\n\n**Complexity:** O(L + (N-K)log(N)) worst case where L is the total number of elements in all the sets, N is the size of the first set, and K is the size of the result set.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zincrby/\">ZINCRBY</a></strong> - Increments the score of a member in a sorted set.</summary>\n\n**Syntax:** `ZINCRBY key increment member`\n\n**Description:** Increments the score of a member in a sorted set.\n\n**Complexity:** O(log(N)) where N is the number of elements in the sorted set.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zinter/\">ZINTER</a></strong> - Returns the intersect of multiple sorted sets.</summary>\n\n**Syntax:** `ZINTER numkeys key [key ...] [WEIGHTS weight [weight ...]]\n  [AGGREGATE <SUM | MIN | MAX>] [WITHSCORES]`\n\n**Description:** Returns the intersect of multiple sorted sets.\n\n**Complexity:** O(N*K)+O(M*log(M)) worst case with N being the smallest input sorted set, K being the number of input sorted sets and M being the number of elements in the resulting sorted set.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zintercard/\">ZINTERCARD</a></strong> - Returns the number of members of the intersect of multiple sorted sets.</summary>\n\n**Syntax:** `ZINTERCARD numkeys key [key ...] [LIMIT limit]`\n\n**Description:** Returns the number of members of the intersect of multiple sorted sets.\n\n**Complexity:** O(N*K) worst case with N being the smallest input sorted set, K being the number of input sorted sets.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zinterstore/\">ZINTERSTORE</a></strong> - Stores the intersect of multiple sorted sets in a key.</summary>\n\n**Syntax:** `ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight\n  [weight ...]] [AGGREGATE <SUM | MIN | MAX>]`\n\n**Description:** Stores the intersect of multiple sorted sets in a key.\n\n**Complexity:** O(N*K)+O(M*log(M)) worst case with N being the smallest input sorted set, K being the number of input sorted sets and M being the number of elements in the resulting sorted set.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zlexcount/\">ZLEXCOUNT</a></strong> - Returns the number of members in a sorted set within a lexicographical range.</summary>\n\n**Syntax:** `ZLEXCOUNT key min max`\n\n**Description:** Returns the number of members in a sorted set within a lexicographical range.\n\n**Complexity:** O(log(N)) with N being the number of elements in the sorted set.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zmpop/\">ZMPOP</a></strong> - Returns the highest- or lowest-scoring members from one or more sorted sets after removing them. Deletes the sorted set if the last member was popped.</summary>\n\n**Syntax:** `ZMPOP numkeys key [key ...] <MIN | MAX> [COUNT count]`\n\n**Description:** Returns the highest- or lowest-scoring members from one or more sorted sets after removing them. Deletes the sorted set if the last member was popped.\n\n**Complexity:** O(K) + O(M*log(N)) where K is the number of provided keys, N being the number of elements in the sorted set, and M being the number of elements popped.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zmscore/\">ZMSCORE</a></strong> - Returns the score of one or more members in a sorted set.</summary>\n\n**Syntax:** `ZMSCORE key member [member ...]`\n\n**Description:** Returns the score of one or more members in a sorted set.\n\n**Complexity:** O(N) where N is the number of members being requested.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zpopmax/\">ZPOPMAX</a></strong> - Returns the highest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.</summary>\n\n**Syntax:** `ZPOPMAX key [count]`\n\n**Description:** Returns the highest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.\n\n**Complexity:** O(log(N)*M) with N being the number of elements in the sorted set, and M being the number of elements popped.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zpopmin/\">ZPOPMIN</a></strong> - Returns the lowest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.</summary>\n\n**Syntax:** `ZPOPMIN key [count]`\n\n**Description:** Returns the lowest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.\n\n**Complexity:** O(log(N)*M) with N being the number of elements in the sorted set, and M being the number of elements popped.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrandmember/\">ZRANDMEMBER</a></strong> - Returns one or more random members from a sorted set.</summary>\n\n**Syntax:** `ZRANDMEMBER key [count [WITHSCORES]]`\n\n**Description:** Returns one or more random members from a sorted set.\n\n**Complexity:** O(N) where N is the number of members returned\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrange/\">ZRANGE</a></strong> - Returns members in a sorted set within a range of indexes.</summary>\n\n**Syntax:** `ZRANGE key start stop [BYSCORE | BYLEX] [REV] [LIMIT offset count]\n  [WITHSCORES]`\n\n**Description:** Returns members in a sorted set within a range of indexes.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements returned.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrangebylex/\">ZRANGEBYLEX</a></strong> - Returns members in a sorted set within a lexicographical range.</summary>\n\n**Syntax:** `ZRANGEBYLEX key min max [LIMIT offset count]`\n\n**Description:** Returns members in a sorted set within a lexicographical range.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrangebyscore/\">ZRANGEBYSCORE</a></strong> - Returns members in a sorted set within a range of scores.</summary>\n\n**Syntax:** `ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]`\n\n**Description:** Returns members in a sorted set within a range of scores.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).\n\n**Since:** 1.0.5\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrangestore/\">ZRANGESTORE</a></strong> - Stores a range of members from sorted set in a key.</summary>\n\n**Syntax:** `ZRANGESTORE dst src min max [BYSCORE | BYLEX] [REV] [LIMIT offset\n  count]`\n\n**Description:** Stores a range of members from sorted set in a key.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements stored into the destination key.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrank/\">ZRANK</a></strong> - Returns the index of a member in a sorted set ordered by ascending scores.</summary>\n\n**Syntax:** `ZRANK key member [WITHSCORE]`\n\n**Description:** Returns the index of a member in a sorted set ordered by ascending scores.\n\n**Complexity:** O(log(N))\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrem/\">ZREM</a></strong> - Removes one or more members from a sorted set. Deletes the sorted set if all members were removed.</summary>\n\n**Syntax:** `ZREM key member [member ...]`\n\n**Description:** Removes one or more members from a sorted set. Deletes the sorted set if all members were removed.\n\n**Complexity:** O(M*log(N)) with N being the number of elements in the sorted set and M the number of elements to be removed.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zremrangebylex/\">ZREMRANGEBYLEX</a></strong> - Removes members in a sorted set within a lexicographical range. Deletes the sorted set if all members were removed.</summary>\n\n**Syntax:** `ZREMRANGEBYLEX key min max`\n\n**Description:** Removes members in a sorted set within a lexicographical range. Deletes the sorted set if all members were removed.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zremrangebyrank/\">ZREMRANGEBYRANK</a></strong> - Removes members in a sorted set within a range of indexes. Deletes the sorted set if all members were removed.</summary>\n\n**Syntax:** `ZREMRANGEBYRANK key start stop`\n\n**Description:** Removes members in a sorted set within a range of indexes. Deletes the sorted set if all members were removed.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zremrangebyscore/\">ZREMRANGEBYSCORE</a></strong> - Removes members in a sorted set within a range of scores. Deletes the sorted set if all members were removed.</summary>\n\n**Syntax:** `ZREMRANGEBYSCORE key min max`\n\n**Description:** Removes members in a sorted set within a range of scores. Deletes the sorted set if all members were removed.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrevrange/\">ZREVRANGE</a></strong> - Returns members in a sorted set within a range of indexes in reverse order.</summary>\n\n**Syntax:** `ZREVRANGE key start stop [WITHSCORES]`\n\n**Description:** Returns members in a sorted set within a range of indexes in reverse order.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements returned.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrevrangebylex/\">ZREVRANGEBYLEX</a></strong> - Returns members in a sorted set within a lexicographical range in reverse order.</summary>\n\n**Syntax:** `ZREVRANGEBYLEX key max min [LIMIT offset count]`\n\n**Description:** Returns members in a sorted set within a lexicographical range in reverse order.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrevrangebyscore/\">ZREVRANGEBYSCORE</a></strong> - Returns members in a sorted set within a range of scores in reverse order.</summary>\n\n**Syntax:** `ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]`\n\n**Description:** Returns members in a sorted set within a range of scores in reverse order.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrevrank/\">ZREVRANK</a></strong> - Returns the index of a member in a sorted set ordered by descending scores.</summary>\n\n**Syntax:** `ZREVRANK key member [WITHSCORE]`\n\n**Description:** Returns the index of a member in a sorted set ordered by descending scores.\n\n**Complexity:** O(log(N))\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zscan/\">ZSCAN</a></strong> - Iterates over members and scores of a sorted set.</summary>\n\n**Syntax:** `ZSCAN key cursor [MATCH pattern] [COUNT count]`\n\n**Description:** Iterates over members and scores of a sorted set.\n\n**Complexity:** O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zscore/\">ZSCORE</a></strong> - Returns the score of a member in a sorted set.</summary>\n\n**Syntax:** `ZSCORE key member`\n\n**Description:** Returns the score of a member in a sorted set.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zunion/\">ZUNION</a></strong> - Returns the union of multiple sorted sets.</summary>\n\n**Syntax:** `ZUNION numkeys key [key ...] [WEIGHTS weight [weight ...]]\n  [AGGREGATE <SUM | MIN | MAX>] [WITHSCORES]`\n\n**Description:** Returns the union of multiple sorted sets.\n\n**Complexity:** O(N)+O(M*log(M)) with N being the sum of the sizes of the input sorted sets, and M being the number of elements in the resulting sorted set.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zunionstore/\">ZUNIONSTORE</a></strong> - Stores the union of multiple sorted sets in a key.</summary>\n\n**Syntax:** `ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight\n  [weight ...]] [AGGREGATE <SUM | MIN | MAX>]`\n\n**Description:** Stores the union of multiple sorted sets in a key.\n\n**Complexity:** O(N)+O(M log(M)) with N being the sum of the sizes of the input sorted sets, and M being the number of elements in the resulting sorted set.\n\n**Since:** 2.0.0\n\n</details>"},{"id":"stream-commands","title":"Stream commands","role":"content","text":"Stream commands operate on append-only log data structures.\n\n<details>\n<summary><strong><a href=\"/commands/xack/\">XACK</a></strong> - Returns the number of messages that were successfully acknowledged by the consumer group member of a stream.</summary>\n\n**Syntax:** `XACK key group id [id ...]`\n\n**Description:** Returns the number of messages that were successfully acknowledged by the consumer group member of a stream.\n\n**Complexity:** O(1) for each message ID processed.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xackdel/\">XACKDEL</a></strong> - Acknowledges and conditionally deletes one or multiple entries for a stream consumer group.</summary>\n\n**Syntax:** `XACKDEL key group [KEEPREF | DELREF | ACKED] IDS numids id [id ...]`\n\n**Description:** Acknowledges and conditionally deletes one or multiple entries for a stream consumer group.\n\n**Complexity:** O(1) for each entry ID processed.\n\n**Since:** 8.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xadd/\">XADD</a></strong> - Appends a new message to a stream. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `XADD key [NOMKSTREAM] [KEEPREF | DELREF | ACKED] [<MAXLEN | MINID>\n  [= | ~] threshold [LIMIT count]] <* | id> field value [field value\n  ...]`\n\n**Description:** Appends a new message to a stream. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) when adding a new entry, O(N) when trimming where N being the number of entries evicted.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xautoclaim/\">XAUTOCLAIM</a></strong> - Changes, or acquires, ownership of messages in a consumer group, as if the messages were delivered to as consumer group member.</summary>\n\n**Syntax:** `XAUTOCLAIM key group consumer min-idle-time start [COUNT count]\n  [JUSTID]`\n\n**Description:** Changes, or acquires, ownership of messages in a consumer group, as if the messages were delivered to as consumer group member.\n\n**Complexity:** O(1) if COUNT is small.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xclaim/\">XCLAIM</a></strong> - Changes, or acquires, ownership of a message in a consumer group, as if the message was delivered a consumer group member.</summary>\n\n**Syntax:** `XCLAIM key group consumer min-idle-time id [id ...] [IDLE ms]\n  [TIME unix-time-milliseconds] [RETRYCOUNT count] [FORCE] [JUSTID]\n  [LASTID lastid]`\n\n**Description:** Changes, or acquires, ownership of a message in a consumer group, as if the message was delivered a consumer group member.\n\n**Complexity:** O(log N) with N being the number of messages in the PEL of the consumer group.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xdel/\">XDEL</a></strong> - Returns the number of messages after removing them from a stream.</summary>\n\n**Syntax:** `XDEL key id [id ...]`\n\n**Description:** Returns the number of messages after removing them from a stream.\n\n**Complexity:** O(1) for each single item to delete in the stream, regardless of the stream size.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xdelex/\">XDELEX</a></strong> - Deletes one or multiple entries from the stream.</summary>\n\n**Syntax:** `XDELEX key [KEEPREF | DELREF | ACKED] IDS numids id [id ...]`\n\n**Description:** Deletes one or multiple entries from the stream.\n\n**Complexity:** O(1) for each single item to delete in the stream, regardless of the stream size.\n\n**Since:** 8.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-create/\">XGROUP CREATE</a></strong> - Creates a consumer group.</summary>\n\n**Syntax:** `XGROUP CREATE key group <id | $> [MKSTREAM]\n  [ENTRIESREAD entries-read]`\n\n**Description:** Creates a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-createconsumer/\">XGROUP CREATECONSUMER</a></strong> - Creates a consumer in a consumer group.</summary>\n\n**Syntax:** `XGROUP CREATECONSUMER key group consumer`\n\n**Description:** Creates a consumer in a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-delconsumer/\">XGROUP DELCONSUMER</a></strong> - Deletes a consumer from a consumer group.</summary>\n\n**Syntax:** `XGROUP DELCONSUMER key group consumer`\n\n**Description:** Deletes a consumer from a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-destroy/\">XGROUP DESTROY</a></strong> - Destroys a consumer group.</summary>\n\n**Syntax:** `XGROUP DESTROY key group`\n\n**Description:** Destroys a consumer group.\n\n**Complexity:** O(N) where N is the number of entries in the group's pending entries list (PEL).\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-setid/\">XGROUP SETID</a></strong> - Sets the last-delivered ID of a consumer group.</summary>\n\n**Syntax:** `XGROUP SETID key group <id | $> [ENTRIESREAD entries-read]`\n\n**Description:** Sets the last-delivered ID of a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xinfo-consumers/\">XINFO CONSUMERS</a></strong> - Returns a list of the consumers in a consumer group.</summary>\n\n**Syntax:** `XINFO CONSUMERS key group`\n\n**Description:** Returns a list of the consumers in a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xinfo-groups/\">XINFO GROUPS</a></strong> - Returns a list of the consumer groups of a stream.</summary>\n\n**Syntax:** `XINFO GROUPS key`\n\n**Description:** Returns a list of the consumer groups of a stream.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xinfo-stream/\">XINFO STREAM</a></strong> - Returns information about a stream.</summary>\n\n**Syntax:** `XINFO STREAM key [FULL [COUNT count]]`\n\n**Description:** Returns information about a stream.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xlen/\">XLEN</a></strong> - Return the number of messages in a stream.</summary>\n\n**Syntax:** `XLEN key`\n\n**Description:** Return the number of messages in a stream.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xpending/\">XPENDING</a></strong> - Returns the information and entries from a stream consumer group's pending entries list.</summary>\n\n**Syntax:** `XPENDING key group [[IDLE min-idle-time] start end count [consumer]]`\n\n**Description:** Returns the information and entries from a stream consumer group's pending entries list.\n\n**Complexity:** O(N) with N being the number of elements returned, so asking for a small fixed number of entries per call is O(1). O(M), where M is the total number of entries scanned when used with the IDLE filter. When the command returns just the summary and the list of consumers is small, it runs in O(1) time; otherwise, an additional O(N) time for iterating every consumer.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xrange/\">XRANGE</a></strong> - Returns the messages from a stream within a range of IDs.</summary>\n\n**Syntax:** `XRANGE key start end [COUNT count]`\n\n**Description:** Returns the messages from a stream within a range of IDs.\n\n**Complexity:** O(N) with N being the number of elements being returned. If N is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1).\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xread/\">XREAD</a></strong> - Returns messages from multiple streams with IDs greater than the ones requested. Blocks until a message is available otherwise.</summary>\n\n**Syntax:** `XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] id\n  [id ...]`\n\n**Description:** Returns messages from multiple streams with IDs greater than the ones requested. Blocks until a message is available otherwise.\n\n**Complexity:** \n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xreadgroup/\">XREADGROUP</a></strong> - Returns new or historical messages from a stream for a consumer in a group. Blocks until a message is available otherwise.</summary>\n\n**Syntax:** `XREADGROUP GROUP group consumer [COUNT count] [BLOCK milliseconds]\n  [CLAIM min-idle-time] [NOACK] STREAMS key [key ...] id [id ...]`\n\n**Description:** Returns new or historical messages from a stream for a consumer in a group. Blocks until a message is available otherwise.\n\n**Complexity:** For each stream mentioned: O(M) with M being the number of elements returned. If M is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1). On the other side when XREADGROUP blocks, XADD will pay the O(N) time in order to serve the N clients blocked on the stream getting new data.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xrevrange/\">XREVRANGE</a></strong> - Returns the messages from a stream within a range of IDs in reverse order.</summary>\n\n**Syntax:** `XREVRANGE key end start [COUNT count]`\n\n**Description:** Returns the messages from a stream within a range of IDs in reverse order.\n\n**Complexity:** O(N) with N being the number of elements returned. If N is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1).\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xsetid/\">XSETID</a></strong> - An internal command for replicating stream values.</summary>\n\n**Syntax:** `XSETID key last-id [ENTRIESADDED entries-added]\n  [MAXDELETEDID max-deleted-id]`\n\n**Description:** An internal command for replicating stream values.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xtrim/\">XTRIM</a></strong> - Deletes messages from the beginning of a stream.</summary>\n\n**Syntax:** `XTRIM key <MAXLEN | MINID> [= | ~] threshold [LIMIT count] [KEEPREF\n  | DELREF | ACKED]`\n\n**Description:** Deletes messages from the beginning of a stream.\n\n**Complexity:** O(N), with N being the number of evicted entries. Constant times are very small however, since entries are organized in macro nodes containing multiple entries that can be released with a single deallocation.\n\n**Since:** 5.0.0\n\n</details>"},{"id":"bitmap-commands","title":"Bitmap commands","role":"content","text":"Bitmap commands operate on strings as arrays of bits.\n\n<details>\n<summary><strong><a href=\"/commands/bitcount/\">BITCOUNT</a></strong> - Counts the number of set bits (population counting) in a string.</summary>\n\n**Syntax:** `BITCOUNT key [start end [BYTE | BIT]]`\n\n**Description:** Counts the number of set bits (population counting) in a string.\n\n**Complexity:** O(N)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bitfield/\">BITFIELD</a></strong> - Performs arbitrary bitfield integer operations on strings.</summary>\n\n**Syntax:** `BITFIELD key [GET encoding offset | [OVERFLOW <WRAP | SAT | FAIL>]\n  <SET encoding offset value | INCRBY encoding offset increment>\n  [GET encoding offset | [OVERFLOW <WRAP | SAT | FAIL>]\n  <SET encoding offset value | INCRBY encoding offset increment>\n  ...]]`\n\n**Description:** Performs arbitrary bitfield integer operations on strings.\n\n**Complexity:** O(1) for each subcommand specified\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bitfield_ro/\">BITFIELD_RO</a></strong> - Performs arbitrary read-only bitfield integer operations on strings.</summary>\n\n**Syntax:** `BITFIELD RO key [GET encoding offset [GET encoding offset ...]]`\n\n**Description:** Performs arbitrary read-only bitfield integer operations on strings.\n\n**Complexity:** O(1) for each subcommand specified\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bitop/\">BITOP</a></strong> - Performs bitwise operations on multiple strings, and stores the result.</summary>\n\n**Syntax:** `BITOP <AND | OR | XOR | NOT | DIFF | DIFF1 | ANDOR | ONE> destkey key [key ...]`\n\n**Description:** Performs bitwise operations on multiple strings, and stores the result.\n\n**Complexity:** O(N)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bitpos/\">BITPOS</a></strong> - Finds the first set (1) or clear (0) bit in a string.</summary>\n\n**Syntax:** `BITPOS key bit [start [end [BYTE | BIT]]]`\n\n**Description:** Finds the first set (1) or clear (0) bit in a string.\n\n**Complexity:** O(N)\n\n**Since:** 2.8.7\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getbit/\">GETBIT</a></strong> - Returns a bit value by offset.</summary>\n\n**Syntax:** `GETBIT key offset`\n\n**Description:** Returns a bit value by offset.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/setbit/\">SETBIT</a></strong> - Sets or clears the bit at offset of the string value. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `SETBIT key offset value`\n\n**Description:** Sets or clears the bit at offset of the string value. Creates the key if it doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>"},{"id":"hyperloglog-commands","title":"HyperLogLog commands","role":"content","text":"HyperLogLog commands provide probabilistic cardinality estimation.\n\n<details>\n<summary><strong><a href=\"/commands/pfadd/\">PFADD</a></strong> - Adds elements to a HyperLogLog key. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `PFADD key [element [element ...]]`\n\n**Description:** Adds elements to a HyperLogLog key. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) to add every element.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pfcount/\">PFCOUNT</a></strong> - Returns the approximated cardinality of the set(s) observed by the HyperLogLog key(s).</summary>\n\n**Syntax:** `PFCOUNT key [key ...]`\n\n**Description:** Returns the approximated cardinality of the set(s) observed by the HyperLogLog key(s).\n\n**Complexity:** O(1) with a very small average constant time when called with a single key. O(N) with N being the number of keys, and much bigger constant times, when called with multiple keys.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pfdebug/\">PFDEBUG</a></strong> - Internal commands for debugging HyperLogLog values.</summary>\n\n**Syntax:** `PFDEBUG subcommand key`\n\n**Description:** Internal commands for debugging HyperLogLog values.\n\n**Complexity:** N/A\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pfmerge/\">PFMERGE</a></strong> - Merges one or more HyperLogLog values into a single key.</summary>\n\n**Syntax:** `PFMERGE destkey [sourcekey [sourcekey ...]]`\n\n**Description:** Merges one or more HyperLogLog values into a single key.\n\n**Complexity:** O(N) to merge N HyperLogLogs, but with high constant times.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pfselftest/\">PFSELFTEST</a></strong> - An internal command for testing HyperLogLog values.</summary>\n\n**Syntax:** `PFSELFTEST`\n\n**Description:** An internal command for testing HyperLogLog values.\n\n**Complexity:** N/A\n\n**Since:** 2.8.9\n\n</details>"},{"id":"geospatial-commands","title":"Geospatial commands","role":"content","text":"Geospatial commands operate on geographic coordinates.\n\n<details>\n<summary><strong><a href=\"/commands/geoadd/\">GEOADD</a></strong> - Adds one or more members to a geospatial index. The key is created if it doesn't exist.</summary>\n\n**Syntax:** `GEOADD key [NX | XX] [CH] longitude latitude member [longitude\n  latitude member ...]`\n\n**Description:** Adds one or more members to a geospatial index. The key is created if it doesn't exist.\n\n**Complexity:** O(log(N)) for each item added, where N is the number of elements in the sorted set.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geodist/\">GEODIST</a></strong> - Returns the distance between two members of a geospatial index.</summary>\n\n**Syntax:** `GEODIST key member1 member2 [M | KM | FT | MI]`\n\n**Description:** Returns the distance between two members of a geospatial index.\n\n**Complexity:** O(1)\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geohash/\">GEOHASH</a></strong> - Returns members from a geospatial index as geohash strings.</summary>\n\n**Syntax:** `GEOHASH key [member [member ...]]`\n\n**Description:** Returns members from a geospatial index as geohash strings.\n\n**Complexity:** O(1) for each member requested.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geopos/\">GEOPOS</a></strong> - Returns the longitude and latitude of members from a geospatial index.</summary>\n\n**Syntax:** `GEOPOS key [member [member ...]]`\n\n**Description:** Returns the longitude and latitude of members from a geospatial index.\n\n**Complexity:** O(1) for each member requested.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/georadius/\">GEORADIUS</a></strong> - Queries a geospatial index for members within a distance from a coordinate, optionally stores the result.</summary>\n\n**Syntax:** `GEORADIUS key longitude latitude radius <M | KM | FT | MI>\n  [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC]\n  [STORE key | STOREDIST key]`\n\n**Description:** Queries a geospatial index for members within a distance from a coordinate, optionally stores the result.\n\n**Complexity:** O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/georadiusbymember/\">GEORADIUSBYMEMBER</a></strong> - Queries a geospatial index for members within a distance from a member, optionally stores the result.</summary>\n\n**Syntax:** `GEORADIUSBYMEMBER key member radius <M | KM | FT | MI> [WITHCOORD]\n  [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC] [STORE key\n  | STOREDIST key]`\n\n**Description:** Queries a geospatial index for members within a distance from a member, optionally stores the result.\n\n**Complexity:** O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/georadiusbymember_ro/\">GEORADIUSBYMEMBER_RO</a></strong> - Returns members from a geospatial index that are within a distance from a member.</summary>\n\n**Syntax:** `GEORADIUSBYMEMBER RO key member radius <M | KM | FT | MI>\n  [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC]`\n\n**Description:** Returns members from a geospatial index that are within a distance from a member.\n\n**Complexity:** O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.\n\n**Since:** 3.2.10\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/georadius_ro/\">GEORADIUS_RO</a></strong> - Returns members from a geospatial index that are within a distance from a coordinate.</summary>\n\n**Syntax:** `GEORADIUS RO key longitude latitude radius <M | KM | FT | MI>\n  [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC]`\n\n**Description:** Returns members from a geospatial index that are within a distance from a coordinate.\n\n**Complexity:** O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.\n\n**Since:** 3.2.10\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geosearch/\">GEOSEARCH</a></strong> - Queries a geospatial index for members inside an area of a box or a circle.</summary>\n\n**Syntax:** `GEOSEARCH key <FROMMEMBER member | FROMLONLAT longitude latitude>\n  <BYRADIUS radius <M | KM | FT | MI> | BYBOX width height <M | KM |\n  FT | MI>> [ASC | DESC] [COUNT count [ANY]] [WITHCOORD] [WITHDIST]\n  [WITHHASH]`\n\n**Description:** Queries a geospatial index for members inside an area of a box or a circle.\n\n**Complexity:** O(N+log(M)) where N is the number of elements in the grid-aligned bounding box area around the shape provided as the filter and M is the number of items inside the shape\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geosearchstore/\">GEOSEARCHSTORE</a></strong> - Queries a geospatial index for members inside an area of a box or a circle, optionally stores the result.</summary>\n\n**Syntax:** `GEOSEARCHSTORE destination source <FROMMEMBER member |\n  FROMLONLAT longitude latitude> <BYRADIUS radius <M | KM | FT | MI>\n  | BYBOX width height <M | KM | FT | MI>> [ASC | DESC] [COUNT count\n  [ANY]] [STOREDIST]`\n\n**Description:** Queries a geospatial index for members inside an area of a box or a circle, optionally stores the result.\n\n**Complexity:** O(N+log(M)) where N is the number of elements in the grid-aligned bounding box area around the shape provided as the filter and M is the number of items inside the shape\n\n**Since:** 6.2.0\n\n</details>"},{"id":"json-commands","title":"JSON commands","role":"content","text":"JSON commands operate on JSON data structures.\n\n<details>\n<summary><strong><a href=\"/commands/json.arrappend/\">JSON.ARRAPPEND</a></strong> - Append one or more JSON values into the array at path after the last element in it.</summary>\n\n**Syntax:** `JSON.ARRAPPEND key path value [value ...]`\n\n**Description:** Append one or more JSON values into the array at path after the last element in it.\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrindex/\">JSON.ARRINDEX</a></strong> - Returns the index of the first occurrence of a JSON scalar value in the array at path</summary>\n\n**Syntax:** `JSON.ARRINDEX key path value [start [stop]]`\n\n**Description:** Returns the index of the first occurrence of a JSON scalar value in the array at path\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the array, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrinsert/\">JSON.ARRINSERT</a></strong> - Inserts the JSON scalar(s) value at the specified index in the array at path</summary>\n\n**Syntax:** `JSON.ARRINSERT key path index value [value ...]`\n\n**Description:** Inserts the JSON scalar(s) value at the specified index in the array at path\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the array, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrlen/\">JSON.ARRLEN</a></strong> - Returns the length of the array at path</summary>\n\n**Syntax:** `JSON.ARRLEN key [path]`\n\n**Description:** Returns the length of the array at path\n\n**Complexity:** O(1) where path is evaluated to a single value, O(N) where path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrpop/\">JSON.ARRPOP</a></strong> - Removes and returns the element at the specified index in the array at path</summary>\n\n**Syntax:** `JSON.ARRPOP key [path [index]]`\n\n**Description:** Removes and returns the element at the specified index in the array at path\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the array and the specified index is not the last element, O(1) when path is evaluated to a single value and the specified index is the last element, or O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrtrim/\">JSON.ARRTRIM</a></strong> - Trims the array at path to contain only the specified inclusive range of indices from start to stop</summary>\n\n**Syntax:** `JSON.ARRTRIM key path start stop`\n\n**Description:** Trims the array at path to contain only the specified inclusive range of indices from start to stop\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the array, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.clear/\">JSON.CLEAR</a></strong> - Clears all values from an array or an object and sets numeric values to `0`</summary>\n\n**Syntax:** `JSON.CLEAR key [path]`\n\n**Description:** Clears all values from an array or an object and sets numeric values to `0`\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the values, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.debug/\">JSON.DEBUG</a></strong> - Debugging container command</summary>\n\n**Syntax:** `JSON.DEBUG`\n\n**Description:** Debugging container command\n\n**Complexity:** N/A\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.debug-memory/\">JSON.DEBUG MEMORY</a></strong> - Reports the size in bytes of a key</summary>\n\n**Syntax:** `JSON.DEBUG MEMORY key [path]`\n\n**Description:** Reports the size in bytes of a key\n\n**Complexity:** O(N) when path is evaluated to a single value, where N is the size of the value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.del/\">JSON.DEL</a></strong> - Deletes a value</summary>\n\n**Syntax:** `JSON.DEL key [path]`\n\n**Description:** Deletes a value\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the deleted value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.forget/\">JSON.FORGET</a></strong> - Deletes a value</summary>\n\n**Syntax:** `JSON.FORGET key [path]`\n\n**Description:** Deletes a value\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the deleted value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.get/\">JSON.GET</a></strong> - Gets the value at one or more paths in JSON serialized form</summary>\n\n**Syntax:** `JSON.GET key [INDENT indent] [NEWLINE newline] [SPACE space] [path\n  [path ...]]`\n\n**Description:** Gets the value at one or more paths in JSON serialized form\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.merge/\">JSON.MERGE</a></strong> - Merges a given JSON value into matching paths. Consequently, JSON values at matching paths are updated, deleted, or expanded with new children</summary>\n\n**Syntax:** `JSON.MERGE key path value`\n\n**Description:** Merges a given JSON value into matching paths. Consequently, JSON values at matching paths are updated, deleted, or expanded with new children\n\n**Complexity:** O(M+N) when path is evaluated to a single value where M is the size of the original value (if it exists) and N is the size of the new value, O(M+N) when path is evaluated to multiple values where M is the size of the key and N is the size of the new value * the number of original values in the key\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.mget/\">JSON.MGET</a></strong> - Returns the values at a path from one or more keys</summary>\n\n**Syntax:** `JSON.MGET key [key ...] path`\n\n**Description:** Returns the values at a path from one or more keys\n\n**Complexity:** O(M*N) when path is evaluated to a single value where M is the number of keys and N is the size of the value, O(N1+N2+...+Nm) when path is evaluated to multiple values where m is the number of keys and Ni is the size of the i-th key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.mset/\">JSON.MSET</a></strong> - Sets or updates the JSON value of one or more keys</summary>\n\n**Syntax:** `JSON.MSET key path value [key path value ...]`\n\n**Description:** Sets or updates the JSON value of one or more keys\n\n**Complexity:** O(K*(M+N)) where k is the number of keys in the command, when path is evaluated to a single value where M is the size of the original value (if it exists) and N is the size of the new value, or O(K*(M+N)) when path is evaluated to multiple values where M is the size of the key and N is the size of the new value * the number of original values in the key\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.numincrby/\">JSON.NUMINCRBY</a></strong> - Increments the numeric value at path by a value</summary>\n\n**Syntax:** `JSON.NUMINCRBY key path value`\n\n**Description:** Increments the numeric value at path by a value\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.nummultby/\">JSON.NUMMULTBY</a></strong> - Multiplies the numeric value at path by a value</summary>\n\n**Syntax:** `JSON.NUMMULTBY key path value`\n\n**Description:** Multiplies the numeric value at path by a value\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.objkeys/\">JSON.OBJKEYS</a></strong> - Returns the JSON keys of the object at path</summary>\n\n**Syntax:** `JSON.OBJKEYS key [path]`\n\n**Description:** Returns the JSON keys of the object at path\n\n**Complexity:** O(N) when path is evaluated to a single value, where N is the number of keys in the object, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.objlen/\">JSON.OBJLEN</a></strong> - Returns the number of keys of the object at path</summary>\n\n**Syntax:** `JSON.OBJLEN key [path]`\n\n**Description:** Returns the number of keys of the object at path\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.resp/\">JSON.RESP</a></strong> - Returns the JSON value at path in Redis Serialization Protocol (RESP)</summary>\n\n**Syntax:** `JSON.RESP key [path]`\n\n**Description:** Returns the JSON value at path in Redis Serialization Protocol (RESP)\n\n**Complexity:** O(N) when path is evaluated to a single value, where N is the size of the value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.set/\">JSON.SET</a></strong> - Sets or updates the JSON value at a path</summary>\n\n**Syntax:** `JSON.SET key path value [NX | XX]`\n\n**Description:** Sets or updates the JSON value at a path\n\n**Complexity:** O(M+N) when path is evaluated to a single value where M is the size of the original value (if it exists) and N is the size of the new value, O(M+N) when path is evaluated to multiple values where M is the size of the key and N is the size of the new value * the number of original values in the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.strappend/\">JSON.STRAPPEND</a></strong> - Appends a string to a JSON string value at path</summary>\n\n**Syntax:** `JSON.STRAPPEND key [path] value`\n\n**Description:** Appends a string to a JSON string value at path\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.strlen/\">JSON.STRLEN</a></strong> - Returns the length of the JSON String at path in key</summary>\n\n**Syntax:** `JSON.STRLEN key [path]`\n\n**Description:** Returns the length of the JSON String at path in key\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.toggle/\">JSON.TOGGLE</a></strong> - Toggles a boolean value</summary>\n\n**Syntax:** `JSON.TOGGLE key path`\n\n**Description:** Toggles a boolean value\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.type/\">JSON.TYPE</a></strong> - Returns the type of the JSON value at path</summary>\n\n**Syntax:** `JSON.TYPE key [path]`\n\n**Description:** Returns the type of the JSON value at path\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>"},{"id":"search-commands","title":"Search commands","role":"content","text":"Search commands provide full-text search and secondary indexing.\n\n<details>\n<summary><strong><a href=\"/commands/ft.aggregate/\">FT.AGGREGATE</a></strong> - Run a search query on an index and perform aggregate transformations on the results</summary>\n\n**Syntax:** `FT.AGGREGATE index query [VERBATIM] [LOAD count field [field ...]]\n  [TIMEOUT timeout] [LOAD *] [GROUPBY nargs property [property ...]\n  [REDUCE function nargs arg [arg ...] [AS name] [REDUCE function\n  nargs arg [arg ...] [AS name] ...]] [GROUPBY nargs property\n  [property ...] [REDUCE function nargs arg [arg ...] [AS name]\n  [REDUCE function nargs arg [arg ...] [AS name] ...]] ...]]\n  [SORTBY nargs [property <ASC | DESC> [property <ASC | DESC> ...]]\n  [MAX num]] [APPLY expression AS name [APPLY expression AS name\n  ...]] [LIMIT offset num] [FILTER filter] [WITHCURSOR\n  [COUNT read size] [MAXIDLE idle time]] [PARAMS nargs name value\n  [name value ...]]\n  [SCORER scorer]\n [ADDSCORES]\n  [DIALECT dialect]`\n\n**Description:** Run a search query on an index and perform aggregate transformations on the results\n\n**Complexity:** O(1)\n\n**Since:** 1.1.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.aliasadd/\">FT.ALIASADD</a></strong> - Adds an alias to the index</summary>\n\n**Syntax:** `FT.ALIASADD alias index`\n\n**Description:** Adds an alias to the index\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.aliasdel/\">FT.ALIASDEL</a></strong> - Deletes an alias from the index</summary>\n\n**Syntax:** `FT.ALIASDEL alias`\n\n**Description:** Deletes an alias from the index\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.aliasupdate/\">FT.ALIASUPDATE</a></strong> - Adds or updates an alias to the index</summary>\n\n**Syntax:** `FT.ALIASUPDATE alias index`\n\n**Description:** Adds or updates an alias to the index\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.alter/\">FT.ALTER</a></strong> - Adds a new field to the index</summary>\n\n**Syntax:** `FT.ALTER index [SKIPINITIALSCAN] SCHEMA ADD field options`\n\n**Description:** Adds a new field to the index\n\n**Complexity:** O(N) where N is the number of keys in the keyspace\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.config-get/\">FT.CONFIG GET</a></strong> - Retrieves runtime configuration options</summary>\n\n**Syntax:** `FT.CONFIG GET option`\n\n**Description:** Retrieves runtime configuration options\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.config-set/\">FT.CONFIG SET</a></strong> - Sets runtime configuration options</summary>\n\n**Syntax:** `FT.CONFIG SET option value`\n\n**Description:** Sets runtime configuration options\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.create/\">FT.CREATE</a></strong> - Creates an index with the given spec</summary>\n\n**Syntax:** `FT.CREATE index [ON <HASH | JSON>] [PREFIX count prefix [prefix\n  ...]] [FILTER filter] [LANGUAGE default lang]\n  [LANGUAGE FIELD lang attribute] [SCORE default score]\n  [SCORE FIELD score attribute] [PAYLOAD FIELD payload attribute]\n  [MAXTEXTFIELDS] [TEMPORARY seconds] [NOOFFSETS] [NOHL] [NOFIELDS]\n  [NOFREQS] [STOPWORDS count [stopword [stopword ...]]]\n  [SKIPINITIALSCAN] SCHEMA field name [AS alias] <TEXT | TAG |\n  NUMERIC | GEO | VECTOR> [WITHSUFFIXTRIE] [SORTABLE [UNF]]\n  [NOINDEX] [field name [AS alias] <TEXT | TAG | NUMERIC | GEO |\n  VECTOR> [WITHSUFFIXTRIE] [SORTABLE [UNF]] [NOINDEX] ...]`\n\n**Description:** Creates an index with the given spec\n\n**Complexity:** O(K) at creation where K is the number of fields, O(N) if scanning the keyspace is triggered, where N is the number of keys in the keyspace\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.cursor-del/\">FT.CURSOR DEL</a></strong> - Deletes a cursor</summary>\n\n**Syntax:** `FT.CURSOR DEL index cursor id`\n\n**Description:** Deletes a cursor\n\n**Complexity:** O(1)\n\n**Since:** 1.1.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.cursor-read/\">FT.CURSOR READ</a></strong> - Reads from a cursor</summary>\n\n**Syntax:** `FT.CURSOR READ index cursor id [COUNT read size]`\n\n**Description:** Reads from a cursor\n\n**Complexity:** O(1)\n\n**Since:** 1.1.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.dictadd/\">FT.DICTADD</a></strong> - Adds terms to a dictionary</summary>\n\n**Syntax:** `FT.DICTADD dict term [term ...]`\n\n**Description:** Adds terms to a dictionary\n\n**Complexity:** O(1)\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.dictdel/\">FT.DICTDEL</a></strong> - Deletes terms from a dictionary</summary>\n\n**Syntax:** `FT.DICTDEL dict term [term ...]`\n\n**Description:** Deletes terms from a dictionary\n\n**Complexity:** O(1)\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.dictdump/\">FT.DICTDUMP</a></strong> - Dumps all terms in the given dictionary</summary>\n\n**Syntax:** `FT.DICTDUMP dict`\n\n**Description:** Dumps all terms in the given dictionary\n\n**Complexity:** O(N), where N is the size of the dictionary\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.dropindex/\">FT.DROPINDEX</a></strong> - Deletes the index</summary>\n\n**Syntax:** `FT.DROPINDEX index [DD]`\n\n**Description:** Deletes the index\n\n**Complexity:** O(1) or O(N) if documents are deleted, where N is the number of keys in the keyspace\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.explain/\">FT.EXPLAIN</a></strong> - Returns the execution plan for a complex query</summary>\n\n**Syntax:** `FT.EXPLAIN index query [DIALECT dialect]`\n\n**Description:** Returns the execution plan for a complex query\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.explaincli/\">FT.EXPLAINCLI</a></strong> - Returns the execution plan for a complex query</summary>\n\n**Syntax:** `FT.EXPLAINCLI index query [DIALECT dialect]`\n\n**Description:** Returns the execution plan for a complex query\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.hybrid/\">FT.HYBRID</a></strong> - Performs hybrid search combining text search and vector similarity search <span style=\"color: #e74c3c;\">⭐ New in 8.4</span></summary>\n\n**Syntax:** `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 <RRF\n  count [CONSTANT constant] [WINDOW window]\n  [YIELD SCORE AS yield score as] | LINEAR count [ALPHA alpha\n  BETA beta] [WINDOW window] [YIELD SCORE AS yield score as]>]\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 <COUNT |\n  COUNT DISTINCT | COUNT DISTINCTISH | SUM | MIN | MAX | AVG |\n  STDDEV | QUANTILE | TOLIST | FIRST VALUE | RANDOM SAMPLE> nargs\n  arg [arg ...] [AS name] [REDUCE <COUNT | COUNT DISTINCT |\n  COUNT DISTINCTISH | SUM | MIN | MAX | AVG | STDDEV | QUANTILE |\n  TOLIST | FIRST VALUE | RANDOM SAMPLE> nargs arg [arg ...]\n  [AS name] ...]]] [APPLY expression AS name [APPLY expression\n  AS name ...]] [FILTER filter]`\n\n**Description:** Performs hybrid search combining text search and vector similarity search\n\n**Complexity:** O(N+M) where N is the complexity of the text search and M is the complexity of the vector search\n\n**Since:** 8.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.info/\">FT.INFO</a></strong> - Returns information and statistics on the index</summary>\n\n**Syntax:** `FT.INFO index`\n\n**Description:** Returns information and statistics on the index\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.profile/\">FT.PROFILE</a></strong> - Performs a `FT.SEARCH` or `FT.AGGREGATE` command and collects performance information</summary>\n\n**Syntax:** `FT.PROFILE index <SEARCH | AGGREGATE> [LIMITED] QUERY query`\n\n**Description:** Performs a `FT.SEARCH` or `FT.AGGREGATE` command and collects performance information\n\n**Complexity:** O(N)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.search/\">FT.SEARCH</a></strong> - Searches the index with a textual query, returning either documents or just ids</summary>\n\n**Syntax:** `FT.SEARCH index query [NOCONTENT] [VERBATIM] [NOSTOPWORDS]\n  [WITHSCORES] [WITHPAYLOADS] [WITHSORTKEYS] [FILTER numeric field\n  min max [FILTER numeric field min max ...]] [GEOFILTER geo field\n  lon lat radius <m | km | mi | ft> [GEOFILTER geo field lon lat\n  radius <m | km | mi | ft> ...]] [INKEYS count key [key ...]]\n  [INFIELDS count field [field ...]] [RETURN count identifier\n  [AS property] [identifier [AS property] ...]] [SUMMARIZE\n  [FIELDS count field [field ...]] [FRAGS num] [LEN fragsize]\n  [SEPARATOR separator]] [HIGHLIGHT [FIELDS count field [field ...]]\n  [TAGS open close]] [SLOP slop] [TIMEOUT timeout] [INORDER]\n  [LANGUAGE language] [EXPANDER expander] [SCORER scorer]\n  [EXPLAINSCORE] [PAYLOAD payload] [SORTBY sortby [ASC | DESC]]\n  [LIMIT offset num] [PARAMS nargs name value [name value ...]]\n  [DIALECT dialect]`\n\n**Description:** Searches the index with a textual query, returning either documents or just ids\n\n**Complexity:** O(N)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.spellcheck/\">FT.SPELLCHECK</a></strong> - Performs spelling correction on a query, returning suggestions for misspelled terms</summary>\n\n**Syntax:** `FT.SPELLCHECK index query [DISTANCE distance] [TERMS <INCLUDE |\n  EXCLUDE> dictionary [terms [terms ...]]] [DIALECT dialect]`\n\n**Description:** Performs spelling correction on a query, returning suggestions for misspelled terms\n\n**Complexity:** O(1)\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.syndump/\">FT.SYNDUMP</a></strong> - Dumps the contents of a synonym group</summary>\n\n**Syntax:** `FT.SYNDUMP index`\n\n**Description:** Dumps the contents of a synonym group\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.synupdate/\">FT.SYNUPDATE</a></strong> - Creates or updates a synonym group with additional terms</summary>\n\n**Syntax:** `FT.SYNUPDATE index synonym group id [SKIPINITIALSCAN] term [term\n  ...]`\n\n**Description:** Creates or updates a synonym group with additional terms\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.tagvals/\">FT.TAGVALS</a></strong> - Returns the distinct tags indexed in a Tag field</summary>\n\n**Syntax:** `FT.TAGVALS index field name`\n\n**Description:** Returns the distinct tags indexed in a Tag field\n\n**Complexity:** O(N)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft._list/\">FT._LIST</a></strong> - Returns a list of all existing indexes</summary>\n\n**Syntax:** `FT. LIST`\n\n**Description:** Returns a list of all existing indexes\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>"},{"id":"time-series-commands","title":"Time series commands","role":"content","text":"Time series commands operate on time-series data.\n\n<details>\n<summary><strong><a href=\"/commands/ts.add/\">TS.ADD</a></strong> - Append a sample to a time series</summary>\n\n**Syntax:** `TS.ADD key timestamp value [RETENTION retentionPeriod]\n  [ENCODING <COMPRESSED | UNCOMPRESSED>] [CHUNK SIZE size]\n  [DUPLICATE POLICY policy] \n  [ON DUPLICATE <BLOCK | FIRST | LAST | MIN | MAX | SUM>]\n  [IGNORE ignoreMaxTimediff ignoreMaxValDiff]\n  [LABELS [label value ...]]`\n\n**Description:** Append a sample to a time series\n\n**Complexity:** O(M) when M is the amount of compaction rules or O(1) with no compaction\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.alter/\">TS.ALTER</a></strong> - Update the retention, chunk size, duplicate policy, and labels of an existing time series</summary>\n\n**Syntax:** `TS.ALTER key [RETENTION retentionPeriod] [CHUNK SIZE size]\n  [DUPLICATE POLICY <BLOCK | FIRST | LAST | MIN | MAX | SUM>]\n  [IGNORE ignoreMaxTimediff ignoreMaxValDiff] \n  [LABELS [label value ...]]`\n\n**Description:** Update the retention, chunk size, duplicate policy, and labels of an existing time series\n\n**Complexity:** O(N) where N is the number of labels requested to update\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.create/\">TS.CREATE</a></strong> - Create a new time series</summary>\n\n**Syntax:** `TS.CREATE key [RETENTION retentionPeriod] [ENCODING <COMPRESSED |\n  UNCOMPRESSED>] [CHUNK SIZE size] [DUPLICATE POLICY <BLOCK | FIRST |\n  LAST | MIN | MAX | SUM>]\n  [IGNORE ignoreMaxTimediff ignoreMaxValDiff]\n  [LABELS [label value ...]]`\n\n**Description:** Create a new time series\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.createrule/\">TS.CREATERULE</a></strong> - Create a compaction rule</summary>\n\n**Syntax:** `TS.CREATERULE sourceKey destKey AGGREGATION <AVG | FIRST | LAST |\n  MIN | MAX | SUM | RANGE | COUNT | STD.P | STD.S | VAR.P | VAR.S |\n  TWA> bucketDuration [alignTimestamp]`\n\n**Description:** Create a compaction rule\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.decrby/\">TS.DECRBY</a></strong> - Decrease the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given decrement</summary>\n\n**Syntax:** `TS.DECRBY key value [TIMESTAMP timestamp]\n  [RETENTION retentionPeriod] [ENCODING <COMPRESSED|UNCOMPRESSED>] [CHUNK SIZE size]\n [DUPLICATE POLICY policy] [LABELS [label value ...]]`\n\n**Description:** Decrease the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given decrement\n\n**Complexity:** O(M) when M is the amount of compaction rules or O(1) with no compaction\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.del/\">TS.DEL</a></strong> - Delete all samples between two timestamps for a given time series</summary>\n\n**Syntax:** `TS.DEL key from timestamp to timestamp`\n\n**Description:** Delete all samples between two timestamps for a given time series\n\n**Complexity:** O(N) where N is the number of data points that will be removed\n\n**Since:** 1.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.deleterule/\">TS.DELETERULE</a></strong> - Delete a compaction rule</summary>\n\n**Syntax:** `TS.DELETERULE sourceKey destKey`\n\n**Description:** Delete a compaction rule\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.get/\">TS.GET</a></strong> - Get the sample with the highest timestamp from a given time series</summary>\n\n**Syntax:** `TS.GET key [LATEST]`\n\n**Description:** Get the sample with the highest timestamp from a given time series\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.incrby/\">TS.INCRBY</a></strong> - Increase the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given increment</summary>\n\n**Syntax:** `TS.INCRBY key value [TIMESTAMP timestamp]\n  [RETENTION retentionPeriod] [ENCODING <COMPRESSED|UNCOMPRESSED>] [CHUNK SIZE size]\n [DUPLICATE POLICY policy] [LABELS [label value ...]]`\n\n**Description:** Increase the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given increment\n\n**Complexity:** O(M) when M is the amount of compaction rules or O(1) with no compaction\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.info/\">TS.INFO</a></strong> - Returns information and statistics for a time series</summary>\n\n**Syntax:** `TS.INFO key [DEBUG]`\n\n**Description:** Returns information and statistics for a time series\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.madd/\">TS.MADD</a></strong> - Append new samples to one or more time series</summary>\n\n**Syntax:** `TS.MADD key timestamp value [key timestamp value ...]`\n\n**Description:** Append new samples to one or more time series\n\n**Complexity:** O(N*M) when N is the amount of series updated and M is the amount of compaction rules or O(N) with no compaction\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.mget/\">TS.MGET</a></strong> - Get the sample with the highest timestamp from each time series matching a specific filter</summary>\n\n**Syntax:** `TS.MGET [LATEST] [WITHLABELS | <SELECTED LABELS label1 [label1 ...]>]\n  FILTER <l=v | l!=v | l= | l!= | l=(v1,v2,...) | l!=(v1,v2,...)\n  [l=v | l!=v | l= | l!= | l=(v1,v2,...) | l!=(v1,v2,...) ...]>`\n\n**Description:** Get the sample with the highest timestamp from each time series matching a specific filter\n\n**Complexity:** O(n) where n is the number of time-series that match the filters\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.mrange/\">TS.MRANGE</a></strong> - Query a range across multiple time series by filters in forward direction</summary>\n\n**Syntax:** `TS.MRANGE fromTimestamp toTimestamp [LATEST] [FILTER BY TS Timestamp\n  [Timestamp ...]] [FILTER BY VALUE min max] [WITHLABELS |\n  <SELECTED LABELS label1 [label1 ...]>] [COUNT count] [[ALIGN value]\n  AGGREGATION <AVG | FIRST | LAST | MIN | MAX | SUM | RANGE | COUNT\n  | STD.P | STD.S | VAR.P | VAR.S | TWA> bucketDuration\n  [BUCKETTIMESTAMP] [EMPTY]] FILTER <l=v | l!=v | l= | l!= |\n  l=(v1,v2,...) | l!=(v1,v2,...) [l=v | l!=v | l= | l!= |\n  l=(v1,v2,...) | l!=(v1,v2,...) ...]> [GROUPBY label REDUCE\n  reducer]`\n\n**Description:** Query a range across multiple time series by filters in forward direction\n\n**Complexity:** O(n/m+k) where n = Number of data points, m = Chunk size (data points per chunk), k = Number of data points that are in the requested ranges\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.mrevrange/\">TS.MREVRANGE</a></strong> - Query a range across multiple time-series by filters in reverse direction</summary>\n\n**Syntax:** `TS.MREVRANGE fromTimestamp toTimestamp [LATEST]\n  [FILTER BY TS Timestamp [Timestamp ...]] [FILTER BY VALUE min max]\n  [WITHLABELS | <SELECTED LABELS label1 [label1 ...]>] [COUNT count]\n  [[ALIGN value] AGGREGATION <AVG | FIRST | LAST | MIN | MAX | SUM |\n  RANGE | COUNT | STD.P | STD.S | VAR.P | VAR.S | TWA>\n  bucketDuration [BUCKETTIMESTAMP] [EMPTY]] FILTER <l=v | l!=v | l=\n  | l!= | l=(v1,v2,...) | l!=(v1,v2,...) [l=v | l!=v | l= | l!= |\n  l=(v1,v2,...) | l!=(v1,v2,...) ...]> [GROUPBY label REDUCE\n  reducer]`\n\n**Description:** Query a range across multiple time-series by filters in reverse direction\n\n**Complexity:** O(n/m+k) where n = Number of data points, m = Chunk size (data points per chunk), k = Number of data points that are in the requested ranges\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.queryindex/\">TS.QUERYINDEX</a></strong> - Get all time series keys matching a filter list</summary>\n\n**Syntax:** `TS.QUERYINDEX <l=v | l!=v | l= | l!= | l=(v1,v2,...) |\n  l!=(v1,v2,...) [l=v | l!=v | l= | l!= | l=(v1,v2,...) |\n  l!=(v1,v2,...) ...]>`\n\n**Description:** Get all time series keys matching a filter list\n\n**Complexity:** O(n) where n is the number of time-series that match the filters\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.range/\">TS.RANGE</a></strong> - Query a range in forward direction</summary>\n\n**Syntax:** `TS.RANGE key fromTimestamp toTimestamp [LATEST]\n  [FILTER BY TS Timestamp [Timestamp ...]] [FILTER BY VALUE min max]\n  [COUNT count] [[ALIGN value] AGGREGATION <AVG | FIRST | LAST | MIN\n  | MAX | SUM | RANGE | COUNT | STD.P | STD.S | VAR.P | VAR.S | TWA>\n  bucketDuration [BUCKETTIMESTAMP] [EMPTY]]`\n\n**Description:** Query a range in forward direction\n\n**Complexity:** O(n/m+k) where n = Number of data points, m = Chunk size (data points per chunk), k = Number of data points that are in the requested range\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.revrange/\">TS.REVRANGE</a></strong> - Query a range in reverse direction</summary>\n\n**Syntax:** `TS.REVRANGE key fromTimestamp toTimestamp [LATEST]\n  [FILTER BY TS Timestamp [Timestamp ...]] [FILTER BY VALUE min max]\n  [COUNT count] [[ALIGN value] AGGREGATION <AVG | FIRST | LAST | MIN\n  | MAX | SUM | RANGE | COUNT | STD.P | STD.S | VAR.P | VAR.S | TWA>\n  bucketDuration [BUCKETTIMESTAMP] [EMPTY]]`\n\n**Description:** Query a range in reverse direction\n\n**Complexity:** O(n/m+k) where n = Number of data points, m = Chunk size (data points per chunk), k = Number of data points that are in the requested range\n\n**Since:** 1.4.0\n\n</details>"},{"id":"vector-set-commands","title":"Vector set commands","role":"content","text":"Vector set commands operate on vector data structures for similarity search and range queries.\n\n<details>\n<summary><strong><a href=\"/commands/vadd/\">VADD</a></strong> - Add a new element to a vector set, or update its vector if it already exists.</summary>\n\n**Syntax:** `VADD key [REDUCE dim] (FP32 | VALUES num) vector element [CAS] [NOQUANT | Q8 | BIN]\n  [EF build-exploration-factor] [SETATTR attributes] [M numlinks]`\n\n**Description:** Add a new element to a vector set, or update its vector if it already exists.\n\n**Complexity:** O(log(N)) for each element added, where N is the number of elements in the vector set.\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vcard/\">VCARD</a></strong> - Return the number of elements in a vector set.</summary>\n\n**Syntax:** `VCARD key`\n\n**Description:** Return the number of elements in a vector set.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vdim/\">VDIM</a></strong> - Return the dimension of vectors in the vector set.</summary>\n\n**Syntax:** `VDIM key`\n\n**Description:** Return the dimension of vectors in the vector set.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vemb/\">VEMB</a></strong> - Return the vector associated with an element.</summary>\n\n**Syntax:** `VEMB key element [RAW]`\n\n**Description:** Return the vector associated with an element.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vgetattr/\">VGETATTR</a></strong> - Retrieve the JSON attributes of elements.</summary>\n\n**Syntax:** `VGETATTR key element`\n\n**Description:** Retrieve the JSON attributes of elements.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vinfo/\">VINFO</a></strong> - Return information about a vector set.</summary>\n\n**Syntax:** `VINFO key`\n\n**Description:** Return information about a vector set.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vismember/\">VISMEMBER</a></strong> - Check if an element exists in a vector set.</summary>\n\n**Syntax:** `VISMEMBER key element`\n\n**Description:** Check if an element exists in a vector set.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vlinks/\">VLINKS</a></strong> - Return the neighbors of an element at each layer in the HNSW graph.</summary>\n\n**Syntax:** `VLINKS key element [WITHSCORES]`\n\n**Description:** Return the neighbors of an element at each layer in the HNSW graph.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vrandmember/\">VRANDMEMBER</a></strong> - Return one or multiple random members from a vector set.</summary>\n\n**Syntax:** `VRANDMEMBER key [count]`\n\n**Description:** Return one or multiple random members from a vector set.\n\n**Complexity:** O(N) where N is the absolute value of the count argument.\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vrange/\">VRANGE</a></strong> - Return elements in a lexicographical range <span style=\"color: #e74c3c;\">⭐ New in 8.4</span></summary>\n\n**Syntax:** `VRANGE key start end [count]`\n\n**Description:** Return elements in a lexicographical range\n\n**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)\n\n**Since:** 8.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vrem/\">VREM</a></strong> - Remove an element from a vector set.</summary>\n\n**Syntax:** `VREM key element`\n\n**Description:** Remove an element from a vector set.\n\n**Complexity:** O(log(N)) for each element removed, where N is the number of elements in the vector set.\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vsetattr/\">VSETATTR</a></strong> - Associate or remove the JSON attributes of elements.</summary>\n\n**Syntax:** `VSETATTR key element \"{ JSON obj }\"`\n\n**Description:** Associate or remove the JSON attributes of elements.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vsim/\">VSIM</a></strong> - Return elements by vector similarity.</summary>\n\n**Syntax:** `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]`\n\n**Description:** Return elements by vector similarity.\n\n**Complexity:** O(log(N)) where N is the number of elements in the vector set.\n\n**Since:** 8.0.0\n\n</details>"},{"id":"pubsub-commands","title":"Pub/Sub commands","role":"content","text":"Pub/Sub commands enable message passing between clients.\n\n<details>\n<summary><strong><a href=\"/commands/psubscribe/\">PSUBSCRIBE</a></strong> - Listens for messages published to channels that match one or more patterns.</summary>\n\n**Syntax:** `PSUBSCRIBE pattern [pattern ...]`\n\n**Description:** Listens for messages published to channels that match one or more patterns.\n\n**Complexity:** O(N) where N is the number of patterns to subscribe to.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/publish/\">PUBLISH</a></strong> - Posts a message to a channel.</summary>\n\n**Syntax:** `PUBLISH channel message`\n\n**Description:** Posts a message to a channel.\n\n**Complexity:** O(N+M) where N is the number of clients subscribed to the receiving channel and M is the total number of subscribed patterns (by any client).\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-channels/\">PUBSUB CHANNELS</a></strong> - Returns the active channels.</summary>\n\n**Syntax:** `PUBSUB CHANNELS [pattern]`\n\n**Description:** Returns the active channels.\n\n**Complexity:** O(N) where N is the number of active channels, and assuming constant time pattern matching (relatively short channels and patterns)\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-numpat/\">PUBSUB NUMPAT</a></strong> - Returns a count of unique pattern subscriptions.</summary>\n\n**Syntax:** `PUBSUB NUMPAT`\n\n**Description:** Returns a count of unique pattern subscriptions.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-numsub/\">PUBSUB NUMSUB</a></strong> - Returns a count of subscribers to channels.</summary>\n\n**Syntax:** `PUBSUB NUMSUB [channel [channel ...]]`\n\n**Description:** Returns a count of subscribers to channels.\n\n**Complexity:** O(N) for the NUMSUB subcommand, where N is the number of requested channels\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-shardchannels/\">PUBSUB SHARDCHANNELS</a></strong> - Returns the active shard channels.</summary>\n\n**Syntax:** `PUBSUB SHARDCHANNELS [pattern]`\n\n**Description:** Returns the active shard channels.\n\n**Complexity:** O(N) where N is the number of active shard channels, and assuming constant time pattern matching (relatively short shard channels).\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-shardnumsub/\">PUBSUB SHARDNUMSUB</a></strong> - Returns the count of subscribers of shard channels.</summary>\n\n**Syntax:** `PUBSUB SHARDNUMSUB [shardchannel [shardchannel ...]]`\n\n**Description:** Returns the count of subscribers of shard channels.\n\n**Complexity:** O(N) for the SHARDNUMSUB subcommand, where N is the number of requested shard channels\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/punsubscribe/\">PUNSUBSCRIBE</a></strong> - Stops listening to messages published to channels that match one or more patterns.</summary>\n\n**Syntax:** `PUNSUBSCRIBE [pattern [pattern ...]]`\n\n**Description:** Stops listening to messages published to channels that match one or more patterns.\n\n**Complexity:** O(N) where N is the number of patterns to unsubscribe.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/spublish/\">SPUBLISH</a></strong> - Post a message to a shard channel</summary>\n\n**Syntax:** `SPUBLISH shardchannel message`\n\n**Description:** Post a message to a shard channel\n\n**Complexity:** O(N) where N is the number of clients subscribed to the receiving shard channel.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ssubscribe/\">SSUBSCRIBE</a></strong> - Listens for messages published to shard channels.</summary>\n\n**Syntax:** `SSUBSCRIBE shardchannel [shardchannel ...]`\n\n**Description:** Listens for messages published to shard channels.\n\n**Complexity:** O(N) where N is the number of shard channels to subscribe to.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/subscribe/\">SUBSCRIBE</a></strong> - Listens for messages published to channels.</summary>\n\n**Syntax:** `SUBSCRIBE channel [channel ...]`\n\n**Description:** Listens for messages published to channels.\n\n**Complexity:** O(N) where N is the number of channels to subscribe to.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sunsubscribe/\">SUNSUBSCRIBE</a></strong> - Stops listening to messages posted to shard channels.</summary>\n\n**Syntax:** `SUNSUBSCRIBE [shardchannel [shardchannel ...]]`\n\n**Description:** Stops listening to messages posted to shard channels.\n\n**Complexity:** O(N) where N is the number of shard channels to unsubscribe.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/unsubscribe/\">UNSUBSCRIBE</a></strong> - Stops listening to messages posted to channels.</summary>\n\n**Syntax:** `UNSUBSCRIBE [channel [channel ...]]`\n\n**Description:** Stops listening to messages posted to channels.\n\n**Complexity:** O(N) where N is the number of channels to unsubscribe.\n\n**Since:** 2.0.0\n\n</details>"},{"id":"transaction-commands","title":"Transaction commands","role":"content","text":"Transaction commands enable atomic execution of command groups.\n\n<details>\n<summary><strong><a href=\"/commands/discard/\">DISCARD</a></strong> - Discards a transaction.</summary>\n\n**Syntax:** `DISCARD`\n\n**Description:** Discards a transaction.\n\n**Complexity:** O(N), when N is the number of queued commands\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/exec/\">EXEC</a></strong> - Executes all commands in a transaction.</summary>\n\n**Syntax:** `EXEC`\n\n**Description:** Executes all commands in a transaction.\n\n**Complexity:** Depends on commands in the transaction\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/multi/\">MULTI</a></strong> - Starts a transaction.</summary>\n\n**Syntax:** `MULTI`\n\n**Description:** Starts a transaction.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/unwatch/\">UNWATCH</a></strong> - Forgets about watched keys of a transaction.</summary>\n\n**Syntax:** `UNWATCH`\n\n**Description:** Forgets about watched keys of a transaction.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/watch/\">WATCH</a></strong> - Monitors changes to keys to determine the execution of a transaction.</summary>\n\n**Syntax:** `WATCH key [key ...]`\n\n**Description:** Monitors changes to keys to determine the execution of a transaction.\n\n**Complexity:** O(1) for every key.\n\n**Since:** 2.2.0\n\n</details>"},{"id":"scripting-commands","title":"Scripting commands","role":"content","text":"Scripting commands enable server-side Lua script execution.\n\n<details>\n<summary><strong><a href=\"/commands/eval/\">EVAL</a></strong> - Executes a server-side Lua script.</summary>\n\n**Syntax:** `EVAL script numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Executes a server-side Lua script.\n\n**Complexity:** Depends on the script that is executed.\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/evalsha/\">EVALSHA</a></strong> - Executes a server-side Lua script by SHA1 digest.</summary>\n\n**Syntax:** `EVALSHA sha1 numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Executes a server-side Lua script by SHA1 digest.\n\n**Complexity:** Depends on the script that is executed.\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/evalsha_ro/\">EVALSHA_RO</a></strong> - Executes a read-only server-side Lua script by SHA1 digest.</summary>\n\n**Syntax:** `EVALSHA RO sha1 numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Executes a read-only server-side Lua script by SHA1 digest.\n\n**Complexity:** Depends on the script that is executed.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/eval_ro/\">EVAL_RO</a></strong> - Executes a read-only server-side Lua script.</summary>\n\n**Syntax:** `EVAL RO script numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Executes a read-only server-side Lua script.\n\n**Complexity:** Depends on the script that is executed.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/fcall/\">FCALL</a></strong> - Invokes a function.</summary>\n\n**Syntax:** `FCALL function numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Invokes a function.\n\n**Complexity:** Depends on the function that is executed.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/fcall_ro/\">FCALL_RO</a></strong> - Invokes a read-only function.</summary>\n\n**Syntax:** `FCALL RO function numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Invokes a read-only function.\n\n**Complexity:** Depends on the function that is executed.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-delete/\">FUNCTION DELETE</a></strong> - Deletes a library and its functions.</summary>\n\n**Syntax:** `FUNCTION DELETE library-name`\n\n**Description:** Deletes a library and its functions.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-dump/\">FUNCTION DUMP</a></strong> - Dumps all libraries into a serialized binary payload.</summary>\n\n**Syntax:** `FUNCTION DUMP`\n\n**Description:** Dumps all libraries into a serialized binary payload.\n\n**Complexity:** O(N) where N is the number of functions\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-flush/\">FUNCTION FLUSH</a></strong> - Deletes all libraries and functions.</summary>\n\n**Syntax:** `FUNCTION FLUSH [ASYNC | SYNC]`\n\n**Description:** Deletes all libraries and functions.\n\n**Complexity:** O(N) where N is the number of functions deleted\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-kill/\">FUNCTION KILL</a></strong> - Terminates a function during execution.</summary>\n\n**Syntax:** `FUNCTION KILL`\n\n**Description:** Terminates a function during execution.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-list/\">FUNCTION LIST</a></strong> - Returns information about all libraries.</summary>\n\n**Syntax:** `FUNCTION LIST [LIBRARYNAME library-name-pattern] [WITHCODE]`\n\n**Description:** Returns information about all libraries.\n\n**Complexity:** O(N) where N is the number of functions\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-load/\">FUNCTION LOAD</a></strong> - Creates a library.</summary>\n\n**Syntax:** `FUNCTION LOAD [REPLACE] function-code`\n\n**Description:** Creates a library.\n\n**Complexity:** O(1) (considering compilation time is redundant)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-restore/\">FUNCTION RESTORE</a></strong> - Restores all libraries from a payload.</summary>\n\n**Syntax:** `FUNCTION RESTORE serialized-value [FLUSH | APPEND | REPLACE]`\n\n**Description:** Restores all libraries from a payload.\n\n**Complexity:** O(N) where N is the number of functions on the payload\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-stats/\">FUNCTION STATS</a></strong> - Returns information about a function during execution.</summary>\n\n**Syntax:** `FUNCTION STATS`\n\n**Description:** Returns information about a function during execution.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-debug/\">SCRIPT DEBUG</a></strong> - Sets the debug mode of server-side Lua scripts.</summary>\n\n**Syntax:** `SCRIPT DEBUG <YES | SYNC | NO>`\n\n**Description:** Sets the debug mode of server-side Lua scripts.\n\n**Complexity:** O(1)\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-exists/\">SCRIPT EXISTS</a></strong> - Determines whether server-side Lua scripts exist in the script cache.</summary>\n\n**Syntax:** `SCRIPT EXISTS sha1 [sha1 ...]`\n\n**Description:** Determines whether server-side Lua scripts exist in the script cache.\n\n**Complexity:** O(N) with N being the number of scripts to check (so checking a single script is an O(1) operation).\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-flush/\">SCRIPT FLUSH</a></strong> - Removes all server-side Lua scripts from the script cache.</summary>\n\n**Syntax:** `SCRIPT FLUSH [ASYNC | SYNC]`\n\n**Description:** Removes all server-side Lua scripts from the script cache.\n\n**Complexity:** O(N) with N being the number of scripts in cache\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-kill/\">SCRIPT KILL</a></strong> - Terminates a server-side Lua script during execution.</summary>\n\n**Syntax:** `SCRIPT KILL`\n\n**Description:** Terminates a server-side Lua script during execution.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-load/\">SCRIPT LOAD</a></strong> - Loads a server-side Lua script to the script cache.</summary>\n\n**Syntax:** `SCRIPT LOAD script`\n\n**Description:** Loads a server-side Lua script to the script cache.\n\n**Complexity:** O(N) with N being the length in bytes of the script body.\n\n**Since:** 2.6.0\n\n</details>"},{"id":"connection-commands","title":"Connection commands","role":"content","text":"Connection commands manage client connections.\n\n<details>\n<summary><strong><a href=\"/commands/auth/\">AUTH</a></strong> - Authenticates the connection.</summary>\n\n**Syntax:** `AUTH [username] password`\n\n**Description:** Authenticates the connection.\n\n**Complexity:** O(N) where N is the number of passwords defined for the user\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-caching/\">CLIENT CACHING</a></strong> - Instructs the server whether to track the keys in the next request.</summary>\n\n**Syntax:** `CLIENT CACHING <YES | NO>`\n\n**Description:** Instructs the server whether to track the keys in the next request.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-getname/\">CLIENT GETNAME</a></strong> - Returns the name of the connection.</summary>\n\n**Syntax:** `CLIENT GETNAME`\n\n**Description:** Returns the name of the connection.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-getredir/\">CLIENT GETREDIR</a></strong> - Returns the client ID to which the connection's tracking notifications are redirected.</summary>\n\n**Syntax:** `CLIENT GETREDIR`\n\n**Description:** Returns the client ID to which the connection's tracking notifications are redirected.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-id/\">CLIENT ID</a></strong> - Returns the unique client ID of the connection.</summary>\n\n**Syntax:** `CLIENT ID`\n\n**Description:** Returns the unique client ID of the connection.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-info/\">CLIENT INFO</a></strong> - Returns information about the connection.</summary>\n\n**Syntax:** `CLIENT INFO`\n\n**Description:** Returns information about the connection.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-kill/\">CLIENT KILL</a></strong> - Terminates open connections.</summary>\n\n**Syntax:** `CLIENT KILL <ip:port | <[ID client-id] | [TYPE <NORMAL | MASTER |\n  SLAVE | REPLICA | PUBSUB>] | [USER username] | [ADDR ip:port] |\n  [LADDR ip:port] | [SKIPME <YES | NO>] | [MAXAGE maxage]\n  [[ID client-id] | [TYPE <NORMAL | MASTER | SLAVE | REPLICA |\n  PUBSUB>] | [USER username] | [ADDR ip:port] | [LADDR ip:port] |\n  [SKIPME <YES | NO>] | [MAXAGE maxage] ...]>>`\n\n**Description:** Terminates open connections.\n\n**Complexity:** O(N) where N is the number of client connections\n\n**Since:** 2.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-list/\">CLIENT LIST</a></strong> - Lists open connections.</summary>\n\n**Syntax:** `CLIENT LIST [TYPE <NORMAL | MASTER | REPLICA | PUBSUB>]\n  [ID client-id [client-id ...]]`\n\n**Description:** Lists open connections.\n\n**Complexity:** O(N) where N is the number of client connections\n\n**Since:** 2.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-no-evict/\">CLIENT NO-EVICT</a></strong> - Sets the client eviction mode of the connection.</summary>\n\n**Syntax:** `CLIENT NO-EVICT <ON | OFF>`\n\n**Description:** Sets the client eviction mode of the connection.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-no-touch/\">CLIENT NO-TOUCH</a></strong> - Controls whether commands sent by the client affect the LRU/LFU of accessed keys.</summary>\n\n**Syntax:** `CLIENT NO-TOUCH <ON | OFF>`\n\n**Description:** Controls whether commands sent by the client affect the LRU/LFU of accessed keys.\n\n**Complexity:** O(1)\n\n**Since:** 7.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-pause/\">CLIENT PAUSE</a></strong> - Suspends commands processing.</summary>\n\n**Syntax:** `CLIENT PAUSE timeout [WRITE | ALL]`\n\n**Description:** Suspends commands processing.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-reply/\">CLIENT REPLY</a></strong> - Instructs the server whether to reply to commands.</summary>\n\n**Syntax:** `CLIENT REPLY <ON | OFF | SKIP>`\n\n**Description:** Instructs the server whether to reply to commands.\n\n**Complexity:** O(1)\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-setinfo/\">CLIENT SETINFO</a></strong> - Sets information specific to the client or connection.</summary>\n\n**Syntax:** `CLIENT SETINFO <LIB-NAME libname | LIB-VER libver>`\n\n**Description:** Sets information specific to the client or connection.\n\n**Complexity:** O(1)\n\n**Since:** 7.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-setname/\">CLIENT SETNAME</a></strong> - Sets the connection name.</summary>\n\n**Syntax:** `CLIENT SETNAME connection-name`\n\n**Description:** Sets the connection name.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-tracking/\">CLIENT TRACKING</a></strong> - Controls server-assisted client-side caching for the connection.</summary>\n\n**Syntax:** `CLIENT TRACKING <ON | OFF> [REDIRECT client-id] [PREFIX prefix\n  [PREFIX prefix ...]] [BCAST] [OPTIN] [OPTOUT] [NOLOOP]`\n\n**Description:** Controls server-assisted client-side caching for the connection.\n\n**Complexity:** O(1). Some options may introduce additional complexity.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-trackinginfo/\">CLIENT TRACKINGINFO</a></strong> - Returns information about server-assisted client-side caching for the connection.</summary>\n\n**Syntax:** `CLIENT TRACKINGINFO`\n\n**Description:** Returns information about server-assisted client-side caching for the connection.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-unblock/\">CLIENT UNBLOCK</a></strong> - Unblocks a client blocked by a blocking command from a different connection.</summary>\n\n**Syntax:** `CLIENT UNBLOCK client-id [TIMEOUT | ERROR]`\n\n**Description:** Unblocks a client blocked by a blocking command from a different connection.\n\n**Complexity:** O(log N) where N is the number of client connections\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-unpause/\">CLIENT UNPAUSE</a></strong> - Resumes processing commands from paused clients.</summary>\n\n**Syntax:** `CLIENT UNPAUSE`\n\n**Description:** Resumes processing commands from paused clients.\n\n**Complexity:** O(N) Where N is the number of paused clients\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/echo/\">ECHO</a></strong> - Returns the given string.</summary>\n\n**Syntax:** `ECHO message`\n\n**Description:** Returns the given string.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hello/\">HELLO</a></strong> - Handshakes with the Redis server.</summary>\n\n**Syntax:** `HELLO [protover [AUTH username password] [SETNAME clientname]]`\n\n**Description:** Handshakes with the Redis server.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ping/\">PING</a></strong> - Returns the server's liveliness response.</summary>\n\n**Syntax:** `PING [message]`\n\n**Description:** Returns the server's liveliness response.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/quit/\">QUIT</a></strong> - Closes the connection.</summary>\n\n**Syntax:** `QUIT`\n\n**Description:** Closes the connection.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/reset/\">RESET</a></strong> - Resets the connection.</summary>\n\n**Syntax:** `RESET`\n\n**Description:** Resets the connection.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/select/\">SELECT</a></strong> - Changes the selected database.</summary>\n\n**Syntax:** `SELECT index`\n\n**Description:** Changes the selected database.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>"},{"id":"server-commands","title":"Server commands","role":"content","text":"Server commands provide server management and introspection.\n\n<details>\n<summary><strong><a href=\"/commands/acl-cat/\">ACL CAT</a></strong> - Lists the ACL categories, or the commands inside a category.</summary>\n\n**Syntax:** `ACL CAT [category]`\n\n**Description:** Lists the ACL categories, or the commands inside a category.\n\n**Complexity:** O(1) since the categories and commands are a fixed set.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-deluser/\">ACL DELUSER</a></strong> - Deletes ACL users, and terminates their connections.</summary>\n\n**Syntax:** `ACL DELUSER username [username ...]`\n\n**Description:** Deletes ACL users, and terminates their connections.\n\n**Complexity:** O(1) amortized time considering the typical user.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-dryrun/\">ACL DRYRUN</a></strong> - Simulates the execution of a command by a user, without executing the command.</summary>\n\n**Syntax:** `ACL DRYRUN username command [arg [arg ...]]`\n\n**Description:** Simulates the execution of a command by a user, without executing the command.\n\n**Complexity:** O(1).\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-genpass/\">ACL GENPASS</a></strong> - Generates a pseudorandom, secure password that can be used to identify ACL users.</summary>\n\n**Syntax:** `ACL GENPASS [bits]`\n\n**Description:** Generates a pseudorandom, secure password that can be used to identify ACL users.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-getuser/\">ACL GETUSER</a></strong> - Lists the ACL rules of a user.</summary>\n\n**Syntax:** `ACL GETUSER username`\n\n**Description:** Lists the ACL rules of a user.\n\n**Complexity:** O(N). Where N is the number of password, command and pattern rules that the user has.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-list/\">ACL LIST</a></strong> - Dumps the effective rules in ACL file format.</summary>\n\n**Syntax:** `ACL LIST`\n\n**Description:** Dumps the effective rules in ACL file format.\n\n**Complexity:** O(N). Where N is the number of configured users.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-load/\">ACL LOAD</a></strong> - Reloads the rules from the configured ACL file.</summary>\n\n**Syntax:** `ACL LOAD`\n\n**Description:** Reloads the rules from the configured ACL file.\n\n**Complexity:** O(N). Where N is the number of configured users.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-log/\">ACL LOG</a></strong> - Lists recent security events generated due to ACL rules.</summary>\n\n**Syntax:** `ACL LOG [count | RESET]`\n\n**Description:** Lists recent security events generated due to ACL rules.\n\n**Complexity:** O(N) with N being the number of entries shown.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-save/\">ACL SAVE</a></strong> - Saves the effective ACL rules in the configured ACL file.</summary>\n\n**Syntax:** `ACL SAVE`\n\n**Description:** Saves the effective ACL rules in the configured ACL file.\n\n**Complexity:** O(N). Where N is the number of configured users.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-setuser/\">ACL SETUSER</a></strong> - Creates and modifies an ACL user and its rules.</summary>\n\n**Syntax:** `ACL SETUSER username [rule [rule ...]]`\n\n**Description:** Creates and modifies an ACL user and its rules.\n\n**Complexity:** O(N). Where N is the number of rules provided.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-users/\">ACL USERS</a></strong> - Lists all ACL users.</summary>\n\n**Syntax:** `ACL USERS`\n\n**Description:** Lists all ACL users.\n\n**Complexity:** O(N). Where N is the number of configured users.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-whoami/\">ACL WHOAMI</a></strong> - Returns the authenticated username of the current connection.</summary>\n\n**Syntax:** `ACL WHOAMI`\n\n**Description:** Returns the authenticated username of the current connection.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bgrewriteaof/\">BGREWRITEAOF</a></strong> - Asynchronously rewrites the append-only file to disk.</summary>\n\n**Syntax:** `BGREWRITEAOF`\n\n**Description:** Asynchronously rewrites the append-only file to disk.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bgsave/\">BGSAVE</a></strong> - Asynchronously saves the database(s) to disk.</summary>\n\n**Syntax:** `BGSAVE [SCHEDULE]`\n\n**Description:** Asynchronously saves the database(s) to disk.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command/\">COMMAND</a></strong> - Returns detailed information about all commands.</summary>\n\n**Syntax:** `COMMAND`\n\n**Description:** Returns detailed information about all commands.\n\n**Complexity:** O(N) where N is the total number of Redis commands\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-count/\">COMMAND COUNT</a></strong> - Returns a count of commands.</summary>\n\n**Syntax:** `COMMAND COUNT`\n\n**Description:** Returns a count of commands.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-docs/\">COMMAND DOCS</a></strong> - Returns documentary information about one, multiple or all commands.</summary>\n\n**Syntax:** `COMMAND DOCS [command-name [command-name ...]]`\n\n**Description:** Returns documentary information about one, multiple or all commands.\n\n**Complexity:** O(N) where N is the number of commands to look up\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-getkeys/\">COMMAND GETKEYS</a></strong> - Extracts the key names from an arbitrary command.</summary>\n\n**Syntax:** `COMMAND GETKEYS command [arg [arg ...]]`\n\n**Description:** Extracts the key names from an arbitrary command.\n\n**Complexity:** O(N) where N is the number of arguments to the command\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-getkeysandflags/\">COMMAND GETKEYSANDFLAGS</a></strong> - Extracts the key names and access flags for an arbitrary command.</summary>\n\n**Syntax:** `COMMAND GETKEYSANDFLAGS command [arg [arg ...]]`\n\n**Description:** Extracts the key names and access flags for an arbitrary command.\n\n**Complexity:** O(N) where N is the number of arguments to the command\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-info/\">COMMAND INFO</a></strong> - Returns information about one, multiple or all commands.</summary>\n\n**Syntax:** `COMMAND INFO [command-name [command-name ...]]`\n\n**Description:** Returns information about one, multiple or all commands.\n\n**Complexity:** O(N) where N is the number of commands to look up\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-list/\">COMMAND LIST</a></strong> - Returns a list of command names.</summary>\n\n**Syntax:** `COMMAND LIST [FILTERBY <MODULE module-name | ACLCAT category |\n  PATTERN pattern>]`\n\n**Description:** Returns a list of command names.\n\n**Complexity:** O(N) where N is the total number of Redis commands\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/config-get/\">CONFIG GET</a></strong> - Returns the effective values of configuration parameters.</summary>\n\n**Syntax:** `CONFIG GET parameter [parameter ...]`\n\n**Description:** Returns the effective values of configuration parameters.\n\n**Complexity:** O(N) when N is the number of configuration parameters provided\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/config-resetstat/\">CONFIG RESETSTAT</a></strong> - Resets the server's statistics.</summary>\n\n**Syntax:** `CONFIG RESETSTAT`\n\n**Description:** Resets the server's statistics.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/config-rewrite/\">CONFIG REWRITE</a></strong> - Persists the effective configuration to file.</summary>\n\n**Syntax:** `CONFIG REWRITE`\n\n**Description:** Persists the effective configuration to file.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/config-set/\">CONFIG SET</a></strong> - Sets configuration parameters in-flight.</summary>\n\n**Syntax:** `CONFIG SET parameter value [parameter value ...]`\n\n**Description:** Sets configuration parameters in-flight.\n\n**Complexity:** O(N) when N is the number of configuration parameters provided\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/dbsize/\">DBSIZE</a></strong> - Returns the number of keys in the database.</summary>\n\n**Syntax:** `DBSIZE`\n\n**Description:** Returns the number of keys in the database.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/failover/\">FAILOVER</a></strong> - Starts a coordinated failover from a server to one of its replicas.</summary>\n\n**Syntax:** `FAILOVER [TO host port [FORCE]] [ABORT] [TIMEOUT milliseconds]`\n\n**Description:** Starts a coordinated failover from a server to one of its replicas.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/flushall/\">FLUSHALL</a></strong> - Removes all keys from all databases.</summary>\n\n**Syntax:** `FLUSHALL [ASYNC | SYNC]`\n\n**Description:** Removes all keys from all databases.\n\n**Complexity:** O(N) where N is the total number of keys in all databases\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/flushdb/\">FLUSHDB</a></strong> - Remove all keys from the current database.</summary>\n\n**Syntax:** `FLUSHDB [ASYNC | SYNC]`\n\n**Description:** Remove all keys from the current database.\n\n**Complexity:** O(N) where N is the number of keys in the selected database\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/info/\">INFO</a></strong> - Returns information and statistics about the server.</summary>\n\n**Syntax:** `INFO [section [section ...]]`\n\n**Description:** Returns information and statistics about the server.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lastsave/\">LASTSAVE</a></strong> - Returns the Unix timestamp of the last successful save to disk.</summary>\n\n**Syntax:** `LASTSAVE`\n\n**Description:** Returns the Unix timestamp of the last successful save to disk.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-doctor/\">LATENCY DOCTOR</a></strong> - Returns a human-readable latency analysis report.</summary>\n\n**Syntax:** `LATENCY DOCTOR`\n\n**Description:** Returns a human-readable latency analysis report.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-graph/\">LATENCY GRAPH</a></strong> - Returns a latency graph for an event.</summary>\n\n**Syntax:** `LATENCY GRAPH event`\n\n**Description:** Returns a latency graph for an event.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-histogram/\">LATENCY HISTOGRAM</a></strong> - Returns the cumulative distribution of latencies of a subset or all commands.</summary>\n\n**Syntax:** `LATENCY HISTOGRAM [command [command ...]]`\n\n**Description:** Returns the cumulative distribution of latencies of a subset or all commands.\n\n**Complexity:** O(N) where N is the number of commands with latency information being retrieved.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-history/\">LATENCY HISTORY</a></strong> - Returns timestamp-latency samples for an event.</summary>\n\n**Syntax:** `LATENCY HISTORY event`\n\n**Description:** Returns timestamp-latency samples for an event.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-latest/\">LATENCY LATEST</a></strong> - Returns the latest latency samples for all events.</summary>\n\n**Syntax:** `LATENCY LATEST`\n\n**Description:** Returns the latest latency samples for all events.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-reset/\">LATENCY RESET</a></strong> - Resets the latency data for one or more events.</summary>\n\n**Syntax:** `LATENCY RESET [event [event ...]]`\n\n**Description:** Resets the latency data for one or more events.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lolwut/\">LOLWUT</a></strong> - Displays computer art and the Redis version</summary>\n\n**Syntax:** `LOLWUT [VERSION version]`\n\n**Description:** Displays computer art and the Redis version\n\n**Complexity:** \n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-doctor/\">MEMORY DOCTOR</a></strong> - Outputs a memory problems report.</summary>\n\n**Syntax:** `MEMORY DOCTOR`\n\n**Description:** Outputs a memory problems report.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-malloc-stats/\">MEMORY MALLOC-STATS</a></strong> - Returns the allocator statistics.</summary>\n\n**Syntax:** `MEMORY MALLOC-STATS`\n\n**Description:** Returns the allocator statistics.\n\n**Complexity:** Depends on how much memory is allocated, could be slow\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-purge/\">MEMORY PURGE</a></strong> - Asks the allocator to release memory.</summary>\n\n**Syntax:** `MEMORY PURGE`\n\n**Description:** Asks the allocator to release memory.\n\n**Complexity:** Depends on how much memory is allocated, could be slow\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-stats/\">MEMORY STATS</a></strong> - Returns details about memory usage.</summary>\n\n**Syntax:** `MEMORY STATS`\n\n**Description:** Returns details about memory usage.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-usage/\">MEMORY USAGE</a></strong> - Estimates the memory usage of a key.</summary>\n\n**Syntax:** `MEMORY USAGE key [SAMPLES count]`\n\n**Description:** Estimates the memory usage of a key.\n\n**Complexity:** O(N) where N is the number of samples.\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/module-list/\">MODULE LIST</a></strong> - Returns all loaded modules.</summary>\n\n**Syntax:** `MODULE LIST`\n\n**Description:** Returns all loaded modules.\n\n**Complexity:** O(N) where N is the number of loaded modules.\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/module-load/\">MODULE LOAD</a></strong> - Loads a module.</summary>\n\n**Syntax:** `MODULE LOAD path [arg [arg ...]]`\n\n**Description:** Loads a module.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/module-loadex/\">MODULE LOADEX</a></strong> - Loads a module using extended parameters.</summary>\n\n**Syntax:** `MODULE LOADEX path [CONFIG name value [CONFIG name value ...]]\n  [ARGS args [args ...]]`\n\n**Description:** Loads a module using extended parameters.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/module-unload/\">MODULE UNLOAD</a></strong> - Unloads a module.</summary>\n\n**Syntax:** `MODULE UNLOAD name`\n\n**Description:** Unloads a module.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/monitor/\">MONITOR</a></strong> - Listens for all requests received by the server in real-time.</summary>\n\n**Syntax:** `MONITOR`\n\n**Description:** Listens for all requests received by the server in real-time.\n\n**Complexity:** \n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/psync/\">PSYNC</a></strong> - An internal command used in replication.</summary>\n\n**Syntax:** `PSYNC replicationid offset`\n\n**Description:** An internal command used in replication.\n\n**Complexity:** \n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/replconf/\">REPLCONF</a></strong> - An internal command for configuring the replication stream.</summary>\n\n**Syntax:** `REPLCONF`\n\n**Description:** An internal command for configuring the replication stream.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/replicaof/\">REPLICAOF</a></strong> - Configures a server as replica of another, or promotes it to a master.</summary>\n\n**Syntax:** `REPLICAOF <host port | NO ONE>`\n\n**Description:** Configures a server as replica of another, or promotes it to a master.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/restore-asking/\">RESTORE-ASKING</a></strong> - An internal command for migrating keys in a cluster.</summary>\n\n**Syntax:** `RESTORE-ASKING key ttl serialized-value [REPLACE] [ABSTTL]\n  [IDLETIME seconds] [FREQ frequency]`\n\n**Description:** An internal command for migrating keys in a cluster.\n\n**Complexity:** O(1) to create the new key and additional O(N*M) to reconstruct the serialized value, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1). However for sorted set values the complexity is O(N*M*log(N)) because inserting values into sorted sets is O(log(N)).\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/role/\">ROLE</a></strong> - Returns the replication role.</summary>\n\n**Syntax:** `ROLE`\n\n**Description:** Returns the replication role.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.12\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/save/\">SAVE</a></strong> - Synchronously saves the database(s) to disk.</summary>\n\n**Syntax:** `SAVE`\n\n**Description:** Synchronously saves the database(s) to disk.\n\n**Complexity:** O(N) where N is the total number of keys in all databases\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/shutdown/\">SHUTDOWN</a></strong> - Synchronously saves the database(s) to disk and shuts down the Redis server.</summary>\n\n**Syntax:** `SHUTDOWN [NOSAVE | SAVE] [NOW] [FORCE] [ABORT]`\n\n**Description:** Synchronously saves the database(s) to disk and shuts down the Redis server.\n\n**Complexity:** O(N) when saving, where N is the total number of keys in all databases when saving data, otherwise O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/slaveof/\">SLAVEOF</a></strong> - Sets a Redis server as a replica of another, or promotes it to being a master.</summary>\n\n**Syntax:** `SLAVEOF <host port | NO ONE>`\n\n**Description:** Sets a Redis server as a replica of another, or promotes it to being a master.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/slowlog-get/\">SLOWLOG GET</a></strong> - Returns the slow log's entries.</summary>\n\n**Syntax:** `SLOWLOG GET [count]`\n\n**Description:** Returns the slow log's entries.\n\n**Complexity:** O(N) where N is the number of entries returned\n\n**Since:** 2.2.12\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/slowlog-len/\">SLOWLOG LEN</a></strong> - Returns the number of entries in the slow log.</summary>\n\n**Syntax:** `SLOWLOG LEN`\n\n**Description:** Returns the number of entries in the slow log.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.12\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/slowlog-reset/\">SLOWLOG RESET</a></strong> - Clears all entries from the slow log.</summary>\n\n**Syntax:** `SLOWLOG RESET`\n\n**Description:** Clears all entries from the slow log.\n\n**Complexity:** O(N) where N is the number of entries in the slowlog\n\n**Since:** 2.2.12\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/swapdb/\">SWAPDB</a></strong> - Swaps two Redis databases.</summary>\n\n**Syntax:** `SWAPDB index1 index2`\n\n**Description:** Swaps two Redis databases.\n\n**Complexity:** O(N) where N is the count of clients watching or blocking on keys from both databases.\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sync/\">SYNC</a></strong> - An internal command used in replication.</summary>\n\n**Syntax:** `SYNC`\n\n**Description:** An internal command used in replication.\n\n**Complexity:** \n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/time/\">TIME</a></strong> - Returns the server time.</summary>\n\n**Syntax:** `TIME`\n\n**Description:** Returns the server time.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>"},{"id":"cluster-commands","title":"Cluster commands","role":"content","text":"Cluster commands manage Redis Cluster operations.\n\n<details>\n<summary><strong><a href=\"/commands/asking/\">ASKING</a></strong> - Signals that a cluster client is following an -ASK redirect.</summary>\n\n**Syntax:** `ASKING`\n\n**Description:** Signals that a cluster client is following an -ASK redirect.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-addslots/\">CLUSTER ADDSLOTS</a></strong> - Assigns new hash slots to a node.</summary>\n\n**Syntax:** `CLUSTER ADDSLOTS slot [slot ...]`\n\n**Description:** Assigns new hash slots to a node.\n\n**Complexity:** O(N) where N is the total number of hash slot arguments\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-addslotsrange/\">CLUSTER ADDSLOTSRANGE</a></strong> - Assigns new hash slot ranges to a node.</summary>\n\n**Syntax:** `CLUSTER ADDSLOTSRANGE start-slot end-slot [start-slot end-slot ...]`\n\n**Description:** Assigns new hash slot ranges to a node.\n\n**Complexity:** O(N) where N is the total number of the slots between the start slot and end slot arguments.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-bumpepoch/\">CLUSTER BUMPEPOCH</a></strong> - Advances the cluster config epoch.</summary>\n\n**Syntax:** `CLUSTER BUMPEPOCH`\n\n**Description:** Advances the cluster config epoch.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-count-failure-reports/\">CLUSTER COUNT-FAILURE-REPORTS</a></strong> - Returns the number of active failure reports active for a node.</summary>\n\n**Syntax:** `CLUSTER COUNT-FAILURE-REPORTS node-id`\n\n**Description:** Returns the number of active failure reports active for a node.\n\n**Complexity:** O(N) where N is the number of failure reports\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-countkeysinslot/\">CLUSTER COUNTKEYSINSLOT</a></strong> - Returns the number of keys in a hash slot.</summary>\n\n**Syntax:** `CLUSTER COUNTKEYSINSLOT slot`\n\n**Description:** Returns the number of keys in a hash slot.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-delslots/\">CLUSTER DELSLOTS</a></strong> - Sets hash slots as unbound for a node.</summary>\n\n**Syntax:** `CLUSTER DELSLOTS slot [slot ...]`\n\n**Description:** Sets hash slots as unbound for a node.\n\n**Complexity:** O(N) where N is the total number of hash slot arguments\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-delslotsrange/\">CLUSTER DELSLOTSRANGE</a></strong> - Sets hash slot ranges as unbound for a node.</summary>\n\n**Syntax:** `CLUSTER DELSLOTSRANGE start-slot end-slot [start-slot end-slot ...]`\n\n**Description:** Sets hash slot ranges as unbound for a node.\n\n**Complexity:** O(N) where N is the total number of the slots between the start slot and end slot arguments.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-failover/\">CLUSTER FAILOVER</a></strong> - Forces a replica to perform a manual failover of its master.</summary>\n\n**Syntax:** `CLUSTER FAILOVER [FORCE | TAKEOVER]`\n\n**Description:** Forces a replica to perform a manual failover of its master.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-flushslots/\">CLUSTER FLUSHSLOTS</a></strong> - Deletes all slots information from a node.</summary>\n\n**Syntax:** `CLUSTER FLUSHSLOTS`\n\n**Description:** Deletes all slots information from a node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-forget/\">CLUSTER FORGET</a></strong> - Removes a node from the nodes table.</summary>\n\n**Syntax:** `CLUSTER FORGET node-id`\n\n**Description:** Removes a node from the nodes table.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-getkeysinslot/\">CLUSTER GETKEYSINSLOT</a></strong> - Returns the key names in a hash slot.</summary>\n\n**Syntax:** `CLUSTER GETKEYSINSLOT slot count`\n\n**Description:** Returns the key names in a hash slot.\n\n**Complexity:** O(N) where N is the number of requested keys\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-info/\">CLUSTER INFO</a></strong> - Returns information about the state of a node.</summary>\n\n**Syntax:** `CLUSTER INFO`\n\n**Description:** Returns information about the state of a node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-keyslot/\">CLUSTER KEYSLOT</a></strong> - Returns the hash slot for a key.</summary>\n\n**Syntax:** `CLUSTER KEYSLOT key`\n\n**Description:** Returns the hash slot for a key.\n\n**Complexity:** O(N) where N is the number of bytes in the key\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-links/\">CLUSTER LINKS</a></strong> - Returns a list of all TCP links to and from peer nodes.</summary>\n\n**Syntax:** `CLUSTER LINKS`\n\n**Description:** Returns a list of all TCP links to and from peer nodes.\n\n**Complexity:** O(N) where N is the total number of Cluster nodes\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-meet/\">CLUSTER MEET</a></strong> - Forces a node to handshake with another node.</summary>\n\n**Syntax:** `CLUSTER MEET ip port [cluster-bus-port]`\n\n**Description:** Forces a node to handshake with another node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-migration/\">CLUSTER MIGRATION</a></strong> - Start, monitor, and cancel atomic slot migration tasks. <span style=\"color: #e74c3c;\">⭐ New in 8.4</span></summary>\n\n**Syntax:** `CLUSTER MIGRATION <IMPORT start-slot end-slot [start-slot end-slot ...]\n  | CANCEL <ID task-id | ALL> | STATUS <ID task-id | ALL>`\n\n**Description:** Start, monitor, and cancel atomic slot migration tasks.\n\n**Complexity:** O(N) where N is the total number of the slots between the start slot and end slot arguments.\n\n**Since:** 8.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-myid/\">CLUSTER MYID</a></strong> - Returns the ID of a node.</summary>\n\n**Syntax:** `CLUSTER MYID`\n\n**Description:** Returns the ID of a node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-myshardid/\">CLUSTER MYSHARDID</a></strong> - Returns the shard ID of a node.</summary>\n\n**Syntax:** `CLUSTER MYSHARDID`\n\n**Description:** Returns the shard ID of a node.\n\n**Complexity:** O(1)\n\n**Since:** 7.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-nodes/\">CLUSTER NODES</a></strong> - Returns the cluster configuration for a node.</summary>\n\n**Syntax:** `CLUSTER NODES`\n\n**Description:** Returns the cluster configuration for a node.\n\n**Complexity:** O(N) where N is the total number of Cluster nodes\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-replicas/\">CLUSTER REPLICAS</a></strong> - Lists the replica nodes of a master node.</summary>\n\n**Syntax:** `CLUSTER REPLICAS node-id`\n\n**Description:** Lists the replica nodes of a master node.\n\n**Complexity:** O(N) where N is the number of replicas.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-replicate/\">CLUSTER REPLICATE</a></strong> - Configure a node as replica of a master node.</summary>\n\n**Syntax:** `CLUSTER REPLICATE node-id`\n\n**Description:** Configure a node as replica of a master node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-reset/\">CLUSTER RESET</a></strong> - Resets a node.</summary>\n\n**Syntax:** `CLUSTER RESET [HARD | SOFT]`\n\n**Description:** Resets a node.\n\n**Complexity:** O(N) where N is the number of known nodes. The command may execute a FLUSHALL as a side effect.\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-saveconfig/\">CLUSTER SAVECONFIG</a></strong> - Forces a node to save the cluster configuration to disk.</summary>\n\n**Syntax:** `CLUSTER SAVECONFIG`\n\n**Description:** Forces a node to save the cluster configuration to disk.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-set-config-epoch/\">CLUSTER SET-CONFIG-EPOCH</a></strong> - Sets the configuration epoch for a new node.</summary>\n\n**Syntax:** `CLUSTER SET-CONFIG-EPOCH config-epoch`\n\n**Description:** Sets the configuration epoch for a new node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-setslot/\">CLUSTER SETSLOT</a></strong> - Binds a hash slot to a node.</summary>\n\n**Syntax:** `CLUSTER SETSLOT slot <IMPORTING node-id | MIGRATING node-id |\n  NODE node-id | STABLE>`\n\n**Description:** Binds a hash slot to a node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-shards/\">CLUSTER SHARDS</a></strong> - Returns the mapping of cluster slots to shards.</summary>\n\n**Syntax:** `CLUSTER SHARDS`\n\n**Description:** Returns the mapping of cluster slots to shards.\n\n**Complexity:** O(N) where N is the total number of cluster nodes\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-slaves/\">CLUSTER SLAVES</a></strong> - Lists the replica nodes of a master node.</summary>\n\n**Syntax:** `CLUSTER SLAVES node-id`\n\n**Description:** Lists the replica nodes of a master node.\n\n**Complexity:** O(N) where N is the number of replicas.\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-slot-stats/\">CLUSTER SLOT-STATS</a></strong> - Return an array of slot usage statistics for slots assigned to the current node.</summary>\n\n**Syntax:** `CLUSTER SLOT-STATS <SLOTSRANGE start-slot end-slot | ORDERBY metric\n  [LIMIT limit] [ASC | DESC]>`\n\n**Description:** Return an array of slot usage statistics for slots assigned to the current node.\n\n**Complexity:** O(N) where N is the total number of slots based on arguments. O(N*log(N)) with ORDERBY subcommand.\n\n**Since:** 8.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-slots/\">CLUSTER SLOTS</a></strong> - Returns the mapping of cluster slots to nodes.</summary>\n\n**Syntax:** `CLUSTER SLOTS`\n\n**Description:** Returns the mapping of cluster slots to nodes.\n\n**Complexity:** O(N) where N is the total number of Cluster nodes\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/readonly/\">READONLY</a></strong> - Enables read-only queries for a connection to a Redis Cluster replica node.</summary>\n\n**Syntax:** `READONLY`\n\n**Description:** Enables read-only queries for a connection to a Redis Cluster replica node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/readwrite/\">READWRITE</a></strong> - Enables read-write queries for a connection to a Reids Cluster replica node.</summary>\n\n**Syntax:** `READWRITE`\n\n**Description:** Enables read-write queries for a connection to a Reids Cluster replica node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>"},{"id":"generic-commands","title":"Generic commands","role":"content","text":"Generic commands work across all data types.\n\n<details>\n<summary><strong><a href=\"/commands/copy/\">COPY</a></strong> - Copies the value of a key to a new key.</summary>\n\n**Syntax:** `COPY source destination [DB destination-db] [REPLACE]`\n\n**Description:** Copies the value of a key to a new key.\n\n**Complexity:** O(N) worst case for collections, where N is the number of nested items. O(1) for string values.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/del/\">DEL</a></strong> - Deletes one or more keys.</summary>\n\n**Syntax:** `DEL key [key ...]`\n\n**Description:** Deletes one or more keys.\n\n**Complexity:** O(N) where N is the number of keys that will be removed. When a key to remove holds a value other than a string, the individual complexity for this key is O(M) where M is the number of elements in the list, set, sorted set or hash. Removing a single key that holds a string value is O(1).\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/dump/\">DUMP</a></strong> - Returns a serialized representation of the value stored at a key.</summary>\n\n**Syntax:** `DUMP key`\n\n**Description:** Returns a serialized representation of the value stored at a key.\n\n**Complexity:** O(1) to access the key and additional O(N*M) to serialize it, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1).\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/exists/\">EXISTS</a></strong> - Determines whether one or more keys exist.</summary>\n\n**Syntax:** `EXISTS key [key ...]`\n\n**Description:** Determines whether one or more keys exist.\n\n**Complexity:** O(N) where N is the number of keys to check.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/expire/\">EXPIRE</a></strong> - Sets the expiration time of a key in seconds.</summary>\n\n**Syntax:** `EXPIRE key seconds [NX | XX | GT | LT]`\n\n**Description:** Sets the expiration time of a key in seconds.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/expireat/\">EXPIREAT</a></strong> - Sets the expiration time of a key to a Unix timestamp.</summary>\n\n**Syntax:** `EXPIREAT key unix-time-seconds [NX | XX | GT | LT]`\n\n**Description:** Sets the expiration time of a key to a Unix timestamp.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/expiretime/\">EXPIRETIME</a></strong> - Returns the expiration time of a key as a Unix timestamp.</summary>\n\n**Syntax:** `EXPIRETIME key`\n\n**Description:** Returns the expiration time of a key as a Unix timestamp.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/keys/\">KEYS</a></strong> - Returns all key names that match a pattern.</summary>\n\n**Syntax:** `KEYS pattern`\n\n**Description:** Returns all key names that match a pattern.\n\n**Complexity:** O(N) with N being the number of keys in the database, under the assumption that the key names in the database and the given pattern have limited length.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/migrate/\">MIGRATE</a></strong> - Atomically transfers a key from one Redis instance to another.</summary>\n\n**Syntax:** `MIGRATE host port <key | \"\"> destination-db timeout [COPY] [REPLACE]\n  [AUTH password | AUTH2 username password] [KEYS key [key ...]]`\n\n**Description:** Atomically transfers a key from one Redis instance to another.\n\n**Complexity:** This command actually executes a DUMP+DEL in the source instance, and a RESTORE in the target instance. See the pages of these commands for time complexity. Also an O(N) data transfer between the two instances is performed.\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/move/\">MOVE</a></strong> - Moves a key to another database.</summary>\n\n**Syntax:** `MOVE key db`\n\n**Description:** Moves a key to another database.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/object-encoding/\">OBJECT ENCODING</a></strong> - Returns the internal encoding of a Redis object.</summary>\n\n**Syntax:** `OBJECT ENCODING key`\n\n**Description:** Returns the internal encoding of a Redis object.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.3\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/object-freq/\">OBJECT FREQ</a></strong> - Returns the logarithmic access frequency counter of a Redis object.</summary>\n\n**Syntax:** `OBJECT FREQ key`\n\n**Description:** Returns the logarithmic access frequency counter of a Redis object.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/object-idletime/\">OBJECT IDLETIME</a></strong> - Returns the time since the last access to a Redis object.</summary>\n\n**Syntax:** `OBJECT IDLETIME key`\n\n**Description:** Returns the time since the last access to a Redis object.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.3\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/object-refcount/\">OBJECT REFCOUNT</a></strong> - Returns the reference count of a value of a key.</summary>\n\n**Syntax:** `OBJECT REFCOUNT key`\n\n**Description:** Returns the reference count of a value of a key.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.3\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/persist/\">PERSIST</a></strong> - Removes the expiration time of a key.</summary>\n\n**Syntax:** `PERSIST key`\n\n**Description:** Removes the expiration time of a key.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pexpire/\">PEXPIRE</a></strong> - Sets the expiration time of a key in milliseconds.</summary>\n\n**Syntax:** `PEXPIRE key milliseconds [NX | XX | GT | LT]`\n\n**Description:** Sets the expiration time of a key in milliseconds.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pexpireat/\">PEXPIREAT</a></strong> - Sets the expiration time of a key to a Unix milliseconds timestamp.</summary>\n\n**Syntax:** `PEXPIREAT key unix-time-milliseconds [NX | XX | GT | LT]`\n\n**Description:** Sets the expiration time of a key to a Unix milliseconds timestamp.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pexpiretime/\">PEXPIRETIME</a></strong> - Returns the expiration time of a key as a Unix milliseconds timestamp.</summary>\n\n**Syntax:** `PEXPIRETIME key`\n\n**Description:** Returns the expiration time of a key as a Unix milliseconds timestamp.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pttl/\">PTTL</a></strong> - Returns the expiration time in milliseconds of a key.</summary>\n\n**Syntax:** `PTTL key`\n\n**Description:** Returns the expiration time in milliseconds of a key.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/randomkey/\">RANDOMKEY</a></strong> - Returns a random key name from the database.</summary>\n\n**Syntax:** `RANDOMKEY`\n\n**Description:** Returns a random key name from the database.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rename/\">RENAME</a></strong> - Renames a key and overwrites the destination.</summary>\n\n**Syntax:** `RENAME key newkey`\n\n**Description:** Renames a key and overwrites the destination.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/renamenx/\">RENAMENX</a></strong> - Renames a key only when the target key name doesn't exist.</summary>\n\n**Syntax:** `RENAMENX key newkey`\n\n**Description:** Renames a key only when the target key name doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/restore/\">RESTORE</a></strong> - Creates a key from the serialized representation of a value.</summary>\n\n**Syntax:** `RESTORE key ttl serialized-value [REPLACE] [ABSTTL]\n  [IDLETIME seconds] [FREQ frequency]`\n\n**Description:** Creates a key from the serialized representation of a value.\n\n**Complexity:** O(1) to create the new key and additional O(N*M) to reconstruct the serialized value, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1). However for sorted set values the complexity is O(N*M*log(N)) because inserting values into sorted sets is O(log(N)).\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-6-2-commands/\">Redis 6.2 Commands Reference</a></strong> - Complete list of all Redis commands available in version 6.2, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 6.2, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-7-2-commands/\">Redis 7.2 Commands Reference</a></strong> - Complete list of all Redis commands available in version 7.2, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 7.2, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-7-4-commands/\">Redis 7.4 Commands Reference</a></strong> - Complete list of all Redis commands available in version 7.4, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 7.4, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-8-0-commands/\">Redis 8.0 Commands Reference</a></strong> - Complete list of all Redis commands available in version 8.0, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 8.0, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-8-2-commands/\">Redis 8.2 Commands Reference</a></strong> - Complete list of all Redis commands available in version 8.2, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 8.2, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-8-4-commands/\">Redis 8.4 Commands Reference</a></strong> - Complete list of all Redis commands available in version 8.4, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 8.4, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/scan/\">SCAN</a></strong> - Iterates over the key names in the database.</summary>\n\n**Syntax:** `SCAN cursor [MATCH pattern] [COUNT count] [TYPE type]`\n\n**Description:** Iterates over the key names in the database.\n\n**Complexity:** O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sort/\">SORT</a></strong> - Sorts the elements in a list, a set, or a sorted set, optionally storing the result.</summary>\n\n**Syntax:** `SORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern\n  ...]] [ASC | DESC] [ALPHA] [STORE destination]`\n\n**Description:** Sorts the elements in a list, a set, or a sorted set, optionally storing the result.\n\n**Complexity:** O(N+M*log(M)) where N is the number of elements in the list or set to sort, and M the number of returned elements. When the elements are not sorted, complexity is O(N).\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sort_ro/\">SORT_RO</a></strong> - Returns the sorted elements of a list, a set, or a sorted set.</summary>\n\n**Syntax:** `SORT RO key [BY pattern] [LIMIT offset count] [GET pattern [GET\n  pattern ...]] [ASC | DESC] [ALPHA]`\n\n**Description:** Returns the sorted elements of a list, a set, or a sorted set.\n\n**Complexity:** O(N+M*log(M)) where N is the number of elements in the list or set to sort, and M the number of returned elements. When the elements are not sorted, complexity is O(N).\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/touch/\">TOUCH</a></strong> - Returns the number of existing keys out of those specified after updating the time they were last accessed.</summary>\n\n**Syntax:** `TOUCH key [key ...]`\n\n**Description:** Returns the number of existing keys out of those specified after updating the time they were last accessed.\n\n**Complexity:** O(N) where N is the number of keys that will be touched.\n\n**Since:** 3.2.1\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ttl/\">TTL</a></strong> - Returns the expiration time in seconds of a key.</summary>\n\n**Syntax:** `TTL key`\n\n**Description:** Returns the expiration time in seconds of a key.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/type/\">TYPE</a></strong> - Determines the type of value stored at a key.</summary>\n\n**Syntax:** `TYPE key`\n\n**Description:** Determines the type of value stored at a key.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/unlink/\">UNLINK</a></strong> - Asynchronously deletes one or more keys.</summary>\n\n**Syntax:** `UNLINK key [key ...]`\n\n**Description:** Asynchronously deletes one or more keys.\n\n**Complexity:** O(1) for each key removed regardless of its size. Then the command does O(N) work in a different thread in order to reclaim memory, where N is the number of allocations the deleted objects where composed of.\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/wait/\">WAIT</a></strong> - Blocks until the asynchronous replication of all preceding write commands sent by the connection is completed.</summary>\n\n**Syntax:** `WAIT numreplicas timeout`\n\n**Description:** Blocks until the asynchronous replication of all preceding write commands sent by the connection is completed.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/waitaof/\">WAITAOF</a></strong> - Blocks until all of the preceding write commands sent by the connection are written to the append-only file of the master and/or replicas.</summary>\n\n**Syntax:** `WAITAOF numlocal numreplicas timeout`\n\n**Description:** Blocks until all of the preceding write commands sent by the connection are written to the append-only file of the master and/or replicas.\n\n**Complexity:** O(1)\n\n**Since:** 7.2.0\n\n</details>"}],"examples":[]}
{"schema_version":2,"id":"commands/redis-8-6-commands","title":"Redis 8.6 Commands Reference","url":"https://redis.io/docs/latest/commands/redis-8-6-commands/","summary":"Complete list of all Redis commands available in version 8.6, organized by functional group","tags":["docs","develop","stack","oss","rs","rc","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"801eb8ec309046c80b1db43aed2059254e3533f4e42b463a5dd14b3bc5f19f78","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This page provides a comprehensive reference of all Redis commands available in Redis 8.6, organized by functional group. Each command includes its description and syntax in a collapsible section for easy navigation.\n\n> [!NOTE]\n> Redis 8.6 includes all commands from previous versions plus new commands introduced in 8.6. Commands marked with **⭐ New in 8.6** were added in this release."},{"id":"quick-navigation","title":"Quick Navigation","role":"content","text":"- [String commands](#string-commands)\n- [Hash commands](#hash-commands)\n- [List commands](#list-commands)\n- [Set commands](#set-commands)\n- [Sorted set commands](#sorted-set-commands)\n- [Stream commands](#stream-commands)\n- [Bitmap commands](#bitmap-commands)\n- [HyperLogLog commands](#hyperloglog-commands)\n- [Geospatial commands](#geospatial-commands)\n- [JSON commands](#json-commands)\n- [Search commands](#search-commands)\n- [Time series commands](#time-series-commands)\n- [Vector set commands](#vector-set-commands)\n- [Pub/Sub commands](#pubsub-commands)\n- [Transaction commands](#transaction-commands)\n- [Scripting commands](#scripting-commands)\n- [Connection commands](#connection-commands)\n- [Server commands](#server-commands)\n- [Cluster commands](#cluster-commands)\n- [Generic commands](#generic-commands)\n\n---"},{"id":"string-commands","title":"String commands","role":"content","text":"String commands operate on string values, the most basic Redis data type.\n\n<details>\n<summary><strong><a href=\"/commands/append/\">APPEND</a></strong> - Appends a string to the value of a key. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `APPEND key value`\n\n**Description:** Appends a string to the value of a key. Creates the key if it doesn't exist.\n\n**Complexity:** O(1). The amortized time complexity is O(1) assuming the appended value is small and the already present value is of any size, since the dynamic string library used by Redis will double the free space available on every reallocation.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/decr/\">DECR</a></strong> - Decrements the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `DECR key`\n\n**Description:** Decrements the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/decrby/\">DECRBY</a></strong> - Decrements a number from the integer value of a key. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `DECRBY key decrement`\n\n**Description:** Decrements a number from the integer value of a key. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/delex/\">DELEX</a></strong> - Conditionally removes the specified key based on value or hash digest comparison.</summary>\n\n**Syntax:** `DELEX key [IFEQ ifeq-value | IFNE ifne-value | IFDEQ ifdeq-digest |\n  IFDNE ifdne-digest]`\n\n**Description:** Conditionally removes the specified key based on value or hash digest comparison.\n\n**Complexity:** O(1) for IFEQ/IFNE, O(N) for IFDEQ/IFDNE where N is the length of the string value.\n\n**Since:** 8.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/digest/\">DIGEST</a></strong> - Returns the hash digest of a string value as a hexadecimal string.</summary>\n\n**Syntax:** `DIGEST key`\n\n**Description:** Returns the hash digest of a string value as a hexadecimal string.\n\n**Complexity:** O(N) where N is the length of the string value.\n\n**Since:** 8.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/get/\">GET</a></strong> - Returns the string value of a key.</summary>\n\n**Syntax:** `GET key`\n\n**Description:** Returns the string value of a key.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getdel/\">GETDEL</a></strong> - Returns the string value of a key after deleting the key.</summary>\n\n**Syntax:** `GETDEL key`\n\n**Description:** Returns the string value of a key after deleting the key.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getex/\">GETEX</a></strong> - Returns the string value of a key after setting its expiration time.</summary>\n\n**Syntax:** `GETEX key [EX seconds | PX milliseconds | EXAT unix-time-seconds |\n  PXAT unix-time-milliseconds | PERSIST]`\n\n**Description:** Returns the string value of a key after setting its expiration time.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getrange/\">GETRANGE</a></strong> - Returns a substring of the string stored at a key.</summary>\n\n**Syntax:** `GETRANGE key start end`\n\n**Description:** Returns a substring of the string stored at a key.\n\n**Complexity:** O(N) where N is the length of the returned string. The complexity is ultimately determined by the returned length, but because creating a substring from an existing string is very cheap, it can be considered O(1) for small strings.\n\n**Since:** 2.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getset/\">GETSET</a></strong> - Returns the previous string value of a key after setting it to a new value.</summary>\n\n**Syntax:** `GETSET key value`\n\n**Description:** Returns the previous string value of a key after setting it to a new value.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/incr/\">INCR</a></strong> - Increments the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `INCR key`\n\n**Description:** Increments the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/incrby/\">INCRBY</a></strong> - Increments the integer value of a key by a number. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `INCRBY key increment`\n\n**Description:** Increments the integer value of a key by a number. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/incrbyfloat/\">INCRBYFLOAT</a></strong> - Increment the floating point value of a key by a number. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `INCRBYFLOAT key increment`\n\n**Description:** Increment the floating point value of a key by a number. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lcs/\">LCS</a></strong> - Finds the longest common substring.</summary>\n\n**Syntax:** `LCS key1 key2 [LEN] [IDX] [MINMATCHLEN min-match-len] [WITHMATCHLEN]`\n\n**Description:** Finds the longest common substring.\n\n**Complexity:** O(N*M) where N and M are the lengths of s1 and s2, respectively\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/mget/\">MGET</a></strong> - Atomically returns the string values of one or more keys.</summary>\n\n**Syntax:** `MGET key [key ...]`\n\n**Description:** Atomically returns the string values of one or more keys.\n\n**Complexity:** O(N) where N is the number of keys to retrieve.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/mset/\">MSET</a></strong> - Atomically creates or modifies the string values of one or more keys.</summary>\n\n**Syntax:** `MSET key value [key value ...]`\n\n**Description:** Atomically creates or modifies the string values of one or more keys.\n\n**Complexity:** O(N) where N is the number of keys to set.\n\n**Since:** 1.0.1\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/msetex/\">MSETEX</a></strong> - Atomically sets multiple string keys with a shared expiration in a single operation.</summary>\n\n**Syntax:** `MSETEX numkeys key value [key value ...] [NX | XX] [EX seconds |\n  PX milliseconds | EXAT unix-time-seconds |\n  PXAT unix-time-milliseconds | KEEPTTL]`\n\n**Description:** Atomically sets multiple string keys with a shared expiration in a single operation.\n\n**Complexity:** O(N) where N is the number of keys to set.\n\n**Since:** 8.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/msetnx/\">MSETNX</a></strong> - Atomically modifies the string values of one or more keys only when all keys don't exist.</summary>\n\n**Syntax:** `MSETNX key value [key value ...]`\n\n**Description:** Atomically modifies the string values of one or more keys only when all keys don't exist.\n\n**Complexity:** O(N) where N is the number of keys to set.\n\n**Since:** 1.0.1\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/psetex/\">PSETEX</a></strong> - Sets both string value and expiration time in milliseconds of a key. The key is created if it doesn't exist.</summary>\n\n**Syntax:** `PSETEX key milliseconds value`\n\n**Description:** Sets both string value and expiration time in milliseconds of a key. The key is created if it doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/set/\">SET</a></strong> - Sets the string value of a key, ignoring its type. The key is created if it doesn't exist.</summary>\n\n**Syntax:** `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]`\n\n**Description:** Sets the string value of a key, ignoring its type. The key is created if it doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/setex/\">SETEX</a></strong> - Sets the string value and expiration time of a key. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `SETEX key seconds value`\n\n**Description:** Sets the string value and expiration time of a key. Creates the key if it doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/setnx/\">SETNX</a></strong> - Set the string value of a key only when the key doesn't exist.</summary>\n\n**Syntax:** `SETNX key value`\n\n**Description:** Set the string value of a key only when the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/setrange/\">SETRANGE</a></strong> - Overwrites a part of a string value with another by an offset. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `SETRANGE key offset value`\n\n**Description:** Overwrites a part of a string value with another by an offset. Creates the key if it doesn't exist.\n\n**Complexity:** O(1), not counting the time taken to copy the new string in place. Usually, this string is very small so the amortized complexity is O(1). Otherwise, complexity is O(M) with M being the length of the value argument.\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/strlen/\">STRLEN</a></strong> - Returns the length of a string value.</summary>\n\n**Syntax:** `STRLEN key`\n\n**Description:** Returns the length of a string value.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/substr/\">SUBSTR</a></strong> - Returns a substring from a string value.</summary>\n\n**Syntax:** `SUBSTR key start end`\n\n**Description:** Returns a substring from a string value.\n\n**Complexity:** O(N) where N is the length of the returned string. The complexity is ultimately determined by the returned length, but because creating a substring from an existing string is very cheap, it can be considered O(1) for small strings.\n\n**Since:** 1.0.0\n\n</details>"},{"id":"hash-commands","title":"Hash commands","role":"content","text":"Hash commands operate on hash data structures, which map string fields to string values.\n\n<details>\n<summary><strong><a href=\"/commands/hdel/\">HDEL</a></strong> - Deletes one or more fields and their values from a hash. Deletes the hash if no fields remain.</summary>\n\n**Syntax:** `HDEL key field [field ...]`\n\n**Description:** Deletes one or more fields and their values from a hash. Deletes the hash if no fields remain.\n\n**Complexity:** O(N) where N is the number of fields to be removed.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hexists/\">HEXISTS</a></strong> - Determines whether a field exists in a hash.</summary>\n\n**Syntax:** `HEXISTS key field`\n\n**Description:** Determines whether a field exists in a hash.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hexpire/\">HEXPIRE</a></strong> - Set expiry for hash field using relative time to expire (seconds)</summary>\n\n**Syntax:** `HEXPIRE key seconds [NX | XX | GT | LT] FIELDS numfields field\n  [field ...]`\n\n**Description:** Set expiry for hash field using relative time to expire (seconds)\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hexpireat/\">HEXPIREAT</a></strong> - Set expiry for hash field using an absolute Unix timestamp (seconds)</summary>\n\n**Syntax:** `HEXPIREAT key unix-time-seconds [NX | XX | GT | LT] FIELDS numfields\n  field [field ...]`\n\n**Description:** Set expiry for hash field using an absolute Unix timestamp (seconds)\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hexpiretime/\">HEXPIRETIME</a></strong> - Returns the expiration time of a hash field as a Unix timestamp, in seconds.</summary>\n\n**Syntax:** `HEXPIRETIME key FIELDS numfields field [field ...]`\n\n**Description:** Returns the expiration time of a hash field as a Unix timestamp, in seconds.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hget/\">HGET</a></strong> - Returns the value of a field in a hash.</summary>\n\n**Syntax:** `HGET key field`\n\n**Description:** Returns the value of a field in a hash.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hgetall/\">HGETALL</a></strong> - Returns all fields and values in a hash.</summary>\n\n**Syntax:** `HGETALL key`\n\n**Description:** Returns all fields and values in a hash.\n\n**Complexity:** O(N) where N is the size of the hash.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hgetdel/\">HGETDEL</a></strong> - Returns the value of a field and deletes it from the hash.</summary>\n\n**Syntax:** `HGETDEL key FIELDS numfields field [field ...]`\n\n**Description:** Returns the value of a field and deletes it from the hash.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hgetex/\">HGETEX</a></strong> - Get the value of one or more fields of a given hash key, and optionally set their expiration.</summary>\n\n**Syntax:** `HGETEX key [EX seconds | PX milliseconds | EXAT unix-time-seconds |\n  PXAT unix-time-milliseconds | PERSIST] FIELDS numfields field\n  [field ...]`\n\n**Description:** Get the value of one or more fields of a given hash key, and optionally set their expiration.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hincrby/\">HINCRBY</a></strong> - Increments the integer value of a field in a hash by a number. Uses 0 as initial value if the field doesn't exist.</summary>\n\n**Syntax:** `HINCRBY key field increment`\n\n**Description:** Increments the integer value of a field in a hash by a number. Uses 0 as initial value if the field doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hincrbyfloat/\">HINCRBYFLOAT</a></strong> - Increments the floating point value of a field by a number. Uses 0 as initial value if the field doesn't exist.</summary>\n\n**Syntax:** `HINCRBYFLOAT key field increment`\n\n**Description:** Increments the floating point value of a field by a number. Uses 0 as initial value if the field doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hkeys/\">HKEYS</a></strong> - Returns all fields in a hash.</summary>\n\n**Syntax:** `HKEYS key`\n\n**Description:** Returns all fields in a hash.\n\n**Complexity:** O(N) where N is the size of the hash.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hlen/\">HLEN</a></strong> - Returns the number of fields in a hash.</summary>\n\n**Syntax:** `HLEN key`\n\n**Description:** Returns the number of fields in a hash.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hmget/\">HMGET</a></strong> - Returns the values of all fields in a hash.</summary>\n\n**Syntax:** `HMGET key field [field ...]`\n\n**Description:** Returns the values of all fields in a hash.\n\n**Complexity:** O(N) where N is the number of fields being requested.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hmset/\">HMSET</a></strong> - Sets the values of multiple fields.</summary>\n\n**Syntax:** `HMSET key field value [field value ...]`\n\n**Description:** Sets the values of multiple fields.\n\n**Complexity:** O(N) where N is the number of fields being set.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hpersist/\">HPERSIST</a></strong> - Removes the expiration time for each specified field</summary>\n\n**Syntax:** `HPERSIST key FIELDS numfields field [field ...]`\n\n**Description:** Removes the expiration time for each specified field\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hpexpire/\">HPEXPIRE</a></strong> - Set expiry for hash field using relative time to expire (milliseconds)</summary>\n\n**Syntax:** `HPEXPIRE key milliseconds [NX | XX | GT | LT] FIELDS numfields field\n  [field ...]`\n\n**Description:** Set expiry for hash field using relative time to expire (milliseconds)\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hpexpireat/\">HPEXPIREAT</a></strong> - Set expiry for hash field using an absolute Unix timestamp (milliseconds)</summary>\n\n**Syntax:** `HPEXPIREAT key unix-time-milliseconds [NX | XX | GT | LT]\n  FIELDS numfields field [field ...]`\n\n**Description:** Set expiry for hash field using an absolute Unix timestamp (milliseconds)\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hpexpiretime/\">HPEXPIRETIME</a></strong> - Returns the expiration time of a hash field as a Unix timestamp, in msec.</summary>\n\n**Syntax:** `HPEXPIRETIME key FIELDS numfields field [field ...]`\n\n**Description:** Returns the expiration time of a hash field as a Unix timestamp, in msec.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hpttl/\">HPTTL</a></strong> - Returns the TTL in milliseconds of a hash field.</summary>\n\n**Syntax:** `HPTTL key FIELDS numfields field [field ...]`\n\n**Description:** Returns the TTL in milliseconds of a hash field.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hrandfield/\">HRANDFIELD</a></strong> - Returns one or more random fields from a hash.</summary>\n\n**Syntax:** `HRANDFIELD key [count [WITHVALUES]]`\n\n**Description:** Returns one or more random fields from a hash.\n\n**Complexity:** O(N) where N is the number of fields returned\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hscan/\">HSCAN</a></strong> - Iterates over fields and values of a hash.</summary>\n\n**Syntax:** `HSCAN key cursor [MATCH pattern] [COUNT count] [NOVALUES]`\n\n**Description:** Iterates over fields and values of a hash.\n\n**Complexity:** O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hset/\">HSET</a></strong> - Creates or modifies the value of a field in a hash.</summary>\n\n**Syntax:** `HSET key field value [field value ...]`\n\n**Description:** Creates or modifies the value of a field in a hash.\n\n**Complexity:** O(1) for each field/value pair added, so O(N) to add N field/value pairs when the command is called with multiple field/value pairs.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hsetex/\">HSETEX</a></strong> - Set the value of one or more fields of a given hash key, and optionally set their expiration.</summary>\n\n**Syntax:** `HSETEX key [FNX | FXX] [EX seconds | PX milliseconds |\n  EXAT unix-time-seconds | PXAT unix-time-milliseconds | KEEPTTL]\n  FIELDS numfields field value [field value ...]`\n\n**Description:** Set the value of one or more fields of a given hash key, and optionally set their expiration.\n\n**Complexity:** O(N) where N is the number of fields being set.\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hsetnx/\">HSETNX</a></strong> - Sets the value of a field in a hash only when the field doesn't exist.</summary>\n\n**Syntax:** `HSETNX key field value`\n\n**Description:** Sets the value of a field in a hash only when the field doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hstrlen/\">HSTRLEN</a></strong> - Returns the length of the value of a field.</summary>\n\n**Syntax:** `HSTRLEN key field`\n\n**Description:** Returns the length of the value of a field.\n\n**Complexity:** O(1)\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/httl/\">HTTL</a></strong> - Returns the TTL in seconds of a hash field.</summary>\n\n**Syntax:** `HTTL key FIELDS numfields field [field ...]`\n\n**Description:** Returns the TTL in seconds of a hash field.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hvals/\">HVALS</a></strong> - Returns all values in a hash.</summary>\n\n**Syntax:** `HVALS key`\n\n**Description:** Returns all values in a hash.\n\n**Complexity:** O(N) where N is the size of the hash.\n\n**Since:** 2.0.0\n\n</details>"},{"id":"list-commands","title":"List commands","role":"content","text":"List commands operate on lists of strings, ordered by insertion order.\n\n<details>\n<summary><strong><a href=\"/commands/blmove/\">BLMOVE</a></strong> - Pops an element from a list, pushes it to another list and returns it. Blocks until an element is available otherwise. Deletes the list if the last element was moved.</summary>\n\n**Syntax:** `BLMOVE source destination <LEFT | RIGHT> <LEFT | RIGHT> timeout`\n\n**Description:** Pops an element from a list, pushes it to another list and returns it. Blocks until an element is available otherwise. Deletes the list if the last element was moved.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/blmpop/\">BLMPOP</a></strong> - Pops the first element from one of multiple lists. Blocks until an element is available otherwise. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `BLMPOP timeout numkeys key [key ...] <LEFT | RIGHT> [COUNT count]`\n\n**Description:** Pops the first element from one of multiple lists. Blocks until an element is available otherwise. Deletes the list if the last element was popped.\n\n**Complexity:** O(N+M) where N is the number of provided keys and M is the number of elements returned.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/blpop/\">BLPOP</a></strong> - Removes and returns the first element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `BLPOP key [key ...] timeout`\n\n**Description:** Removes and returns the first element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.\n\n**Complexity:** O(N) where N is the number of provided keys.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/brpop/\">BRPOP</a></strong> - Removes and returns the last element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `BRPOP key [key ...] timeout`\n\n**Description:** Removes and returns the last element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.\n\n**Complexity:** O(N) where N is the number of provided keys.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/brpoplpush/\">BRPOPLPUSH</a></strong> - Pops an element from a list, pushes it to another list and returns it. Block until an element is available otherwise. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `BRPOPLPUSH source destination timeout`\n\n**Description:** Pops an element from a list, pushes it to another list and returns it. Block until an element is available otherwise. Deletes the list if the last element was popped.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lindex/\">LINDEX</a></strong> - Returns an element from a list by its index.</summary>\n\n**Syntax:** `LINDEX key index`\n\n**Description:** Returns an element from a list by its index.\n\n**Complexity:** O(N) where N is the number of elements to traverse to get to the element at index. This makes asking for the first or the last element of the list O(1).\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/linsert/\">LINSERT</a></strong> - Inserts an element before or after another element in a list.</summary>\n\n**Syntax:** `LINSERT key <BEFORE | AFTER> pivot element`\n\n**Description:** Inserts an element before or after another element in a list.\n\n**Complexity:** O(N) where N is the number of elements to traverse before seeing the value pivot. This means that inserting somewhere on the left end on the list (head) can be considered O(1) and inserting somewhere on the right end (tail) is O(N).\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/llen/\">LLEN</a></strong> - Returns the length of a list.</summary>\n\n**Syntax:** `LLEN key`\n\n**Description:** Returns the length of a list.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lmove/\">LMOVE</a></strong> - Returns an element after popping it from one list and pushing it to another. Deletes the list if the last element was moved.</summary>\n\n**Syntax:** `LMOVE source destination <LEFT | RIGHT> <LEFT | RIGHT>`\n\n**Description:** Returns an element after popping it from one list and pushing it to another. Deletes the list if the last element was moved.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lmpop/\">LMPOP</a></strong> - Returns multiple elements from a list after removing them. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `LMPOP numkeys key [key ...] <LEFT | RIGHT> [COUNT count]`\n\n**Description:** Returns multiple elements from a list after removing them. Deletes the list if the last element was popped.\n\n**Complexity:** O(N+M) where N is the number of provided keys and M is the number of elements returned.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lpop/\">LPOP</a></strong> - Returns the first elements in a list after removing it. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `LPOP key [count]`\n\n**Description:** Returns the first elements in a list after removing it. Deletes the list if the last element was popped.\n\n**Complexity:** O(N) where N is the number of elements returned\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lpos/\">LPOS</a></strong> - Returns the index of matching elements in a list.</summary>\n\n**Syntax:** `LPOS key element [RANK rank] [COUNT num-matches] [MAXLEN len]`\n\n**Description:** Returns the index of matching elements in a list.\n\n**Complexity:** O(N) where N is the number of elements in the list, for the average case. When searching for elements near the head or the tail of the list, or when the MAXLEN option is provided, the command may run in constant time.\n\n**Since:** 6.0.6\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lpush/\">LPUSH</a></strong> - Prepends one or more elements to a list. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `LPUSH key element [element ...]`\n\n**Description:** Prepends one or more elements to a list. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lpushx/\">LPUSHX</a></strong> - Prepends one or more elements to a list only when the list exists.</summary>\n\n**Syntax:** `LPUSHX key element [element ...]`\n\n**Description:** Prepends one or more elements to a list only when the list exists.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lrange/\">LRANGE</a></strong> - Returns a range of elements from a list.</summary>\n\n**Syntax:** `LRANGE key start stop`\n\n**Description:** Returns a range of elements from a list.\n\n**Complexity:** O(S+N) where S is the distance of start offset from HEAD for small lists, from nearest end (HEAD or TAIL) for large lists; and N is the number of elements in the specified range.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lrem/\">LREM</a></strong> - Removes elements from a list. Deletes the list if the last element was removed.</summary>\n\n**Syntax:** `LREM key count element`\n\n**Description:** Removes elements from a list. Deletes the list if the last element was removed.\n\n**Complexity:** O(N+M) where N is the length of the list and M is the number of elements removed.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lset/\">LSET</a></strong> - Sets the value of an element in a list by its index.</summary>\n\n**Syntax:** `LSET key index element`\n\n**Description:** Sets the value of an element in a list by its index.\n\n**Complexity:** O(N) where N is the length of the list. Setting either the first or the last element of the list is O(1).\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ltrim/\">LTRIM</a></strong> - Removes elements from both ends a list. Deletes the list if all elements were trimmed.</summary>\n\n**Syntax:** `LTRIM key start stop`\n\n**Description:** Removes elements from both ends a list. Deletes the list if all elements were trimmed.\n\n**Complexity:** O(N) where N is the number of elements to be removed by the operation.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rpop/\">RPOP</a></strong> - Returns and removes the last elements of a list. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `RPOP key [count]`\n\n**Description:** Returns and removes the last elements of a list. Deletes the list if the last element was popped.\n\n**Complexity:** O(N) where N is the number of elements returned\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rpoplpush/\">RPOPLPUSH</a></strong> - Returns the last element of a list after removing and pushing it to another list. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `RPOPLPUSH source destination`\n\n**Description:** Returns the last element of a list after removing and pushing it to another list. Deletes the list if the last element was popped.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rpush/\">RPUSH</a></strong> - Appends one or more elements to a list. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `RPUSH key element [element ...]`\n\n**Description:** Appends one or more elements to a list. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rpushx/\">RPUSHX</a></strong> - Appends an element to a list only when the list exists.</summary>\n\n**Syntax:** `RPUSHX key element [element ...]`\n\n**Description:** Appends an element to a list only when the list exists.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 2.2.0\n\n</details>"},{"id":"set-commands","title":"Set commands","role":"content","text":"Set commands operate on unordered collections of unique strings.\n\n<details>\n<summary><strong><a href=\"/commands/sadd/\">SADD</a></strong> - Adds one or more members to a set. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `SADD key member [member ...]`\n\n**Description:** Adds one or more members to a set. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/scard/\">SCARD</a></strong> - Returns the number of members in a set.</summary>\n\n**Syntax:** `SCARD key`\n\n**Description:** Returns the number of members in a set.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sdiff/\">SDIFF</a></strong> - Returns the difference of multiple sets.</summary>\n\n**Syntax:** `SDIFF key [key ...]`\n\n**Description:** Returns the difference of multiple sets.\n\n**Complexity:** O(N) where N is the total number of elements in all given sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sdiffstore/\">SDIFFSTORE</a></strong> - Stores the difference of multiple sets in a key.</summary>\n\n**Syntax:** `SDIFFSTORE destination key [key ...]`\n\n**Description:** Stores the difference of multiple sets in a key.\n\n**Complexity:** O(N) where N is the total number of elements in all given sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sinter/\">SINTER</a></strong> - Returns the intersect of multiple sets.</summary>\n\n**Syntax:** `SINTER key [key ...]`\n\n**Description:** Returns the intersect of multiple sets.\n\n**Complexity:** O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sintercard/\">SINTERCARD</a></strong> - Returns the number of members of the intersect of multiple sets.</summary>\n\n**Syntax:** `SINTERCARD numkeys key [key ...] [LIMIT limit]`\n\n**Description:** Returns the number of members of the intersect of multiple sets.\n\n**Complexity:** O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sinterstore/\">SINTERSTORE</a></strong> - Stores the intersect of multiple sets in a key.</summary>\n\n**Syntax:** `SINTERSTORE destination key [key ...]`\n\n**Description:** Stores the intersect of multiple sets in a key.\n\n**Complexity:** O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sismember/\">SISMEMBER</a></strong> - Determines whether a member belongs to a set.</summary>\n\n**Syntax:** `SISMEMBER key member`\n\n**Description:** Determines whether a member belongs to a set.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/smembers/\">SMEMBERS</a></strong> - Returns all members of a set.</summary>\n\n**Syntax:** `SMEMBERS key`\n\n**Description:** Returns all members of a set.\n\n**Complexity:** O(N) where N is the set cardinality.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/smismember/\">SMISMEMBER</a></strong> - Determines whether multiple members belong to a set.</summary>\n\n**Syntax:** `SMISMEMBER key member [member ...]`\n\n**Description:** Determines whether multiple members belong to a set.\n\n**Complexity:** O(N) where N is the number of elements being checked for membership\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/smove/\">SMOVE</a></strong> - Moves a member from one set to another.</summary>\n\n**Syntax:** `SMOVE source destination member`\n\n**Description:** Moves a member from one set to another.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/spop/\">SPOP</a></strong> - Returns one or more random members from a set after removing them. Deletes the set if the last member was popped.</summary>\n\n**Syntax:** `SPOP key [count]`\n\n**Description:** Returns one or more random members from a set after removing them. Deletes the set if the last member was popped.\n\n**Complexity:** Without the count argument O(1), otherwise O(N) where N is the value of the passed count.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/srandmember/\">SRANDMEMBER</a></strong> - Get one or multiple random members from a set</summary>\n\n**Syntax:** `SRANDMEMBER key [count]`\n\n**Description:** Get one or multiple random members from a set\n\n**Complexity:** Without the count argument O(1), otherwise O(N) where N is the absolute value of the passed count.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/srem/\">SREM</a></strong> - Removes one or more members from a set. Deletes the set if the last member was removed.</summary>\n\n**Syntax:** `SREM key member [member ...]`\n\n**Description:** Removes one or more members from a set. Deletes the set if the last member was removed.\n\n**Complexity:** O(N) where N is the number of members to be removed.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sscan/\">SSCAN</a></strong> - Iterates over members of a set.</summary>\n\n**Syntax:** `SSCAN key cursor [MATCH pattern] [COUNT count]`\n\n**Description:** Iterates over members of a set.\n\n**Complexity:** O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sunion/\">SUNION</a></strong> - Returns the union of multiple sets.</summary>\n\n**Syntax:** `SUNION key [key ...]`\n\n**Description:** Returns the union of multiple sets.\n\n**Complexity:** O(N) where N is the total number of elements in all given sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sunionstore/\">SUNIONSTORE</a></strong> - Stores the union of multiple sets in a key.</summary>\n\n**Syntax:** `SUNIONSTORE destination key [key ...]`\n\n**Description:** Stores the union of multiple sets in a key.\n\n**Complexity:** O(N) where N is the total number of elements in all given sets.\n\n**Since:** 1.0.0\n\n</details>"},{"id":"sorted-set-commands","title":"Sorted set commands","role":"content","text":"Sorted set commands operate on sets of unique strings ordered by a score.\n\n<details>\n<summary><strong><a href=\"/commands/bzmpop/\">BZMPOP</a></strong> - Removes and returns a member by score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.</summary>\n\n**Syntax:** `BZMPOP timeout numkeys key [key ...] <MIN | MAX> [COUNT count]`\n\n**Description:** Removes and returns a member by score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.\n\n**Complexity:** O(K) + O(M*log(N)) where K is the number of provided keys, N being the number of elements in the sorted set, and M being the number of elements popped.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bzpopmax/\">BZPOPMAX</a></strong> - Removes and returns the member with the highest score from one or more sorted sets. Blocks until a member available otherwise.  Deletes the sorted set if the last element was popped.</summary>\n\n**Syntax:** `BZPOPMAX key [key ...] timeout`\n\n**Description:** Removes and returns the member with the highest score from one or more sorted sets. Blocks until a member available otherwise.  Deletes the sorted set if the last element was popped.\n\n**Complexity:** O(log(N)) with N being the number of elements in the sorted set.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bzpopmin/\">BZPOPMIN</a></strong> - Removes and returns the member with the lowest score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.</summary>\n\n**Syntax:** `BZPOPMIN key [key ...] timeout`\n\n**Description:** Removes and returns the member with the lowest score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.\n\n**Complexity:** O(log(N)) with N being the number of elements in the sorted set.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zadd/\">ZADD</a></strong> - Adds one or more members to a sorted set, or updates their scores. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `ZADD key [NX | XX] [GT | LT] [CH] [INCR] score member [score member\n  ...]`\n\n**Description:** Adds one or more members to a sorted set, or updates their scores. Creates the key if it doesn't exist.\n\n**Complexity:** O(log(N)) for each item added, where N is the number of elements in the sorted set.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zcard/\">ZCARD</a></strong> - Returns the number of members in a sorted set.</summary>\n\n**Syntax:** `ZCARD key`\n\n**Description:** Returns the number of members in a sorted set.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zcount/\">ZCOUNT</a></strong> - Returns the count of members in a sorted set that have scores within a range.</summary>\n\n**Syntax:** `ZCOUNT key min max`\n\n**Description:** Returns the count of members in a sorted set that have scores within a range.\n\n**Complexity:** O(log(N)) with N being the number of elements in the sorted set.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zdiff/\">ZDIFF</a></strong> - Returns the difference between multiple sorted sets.</summary>\n\n**Syntax:** `ZDIFF numkeys key [key ...] [WITHSCORES]`\n\n**Description:** Returns the difference between multiple sorted sets.\n\n**Complexity:** O(L + (N-K)log(N)) worst case where L is the total number of elements in all the sets, N is the size of the first set, and K is the size of the result set.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zdiffstore/\">ZDIFFSTORE</a></strong> - Stores the difference of multiple sorted sets in a key.</summary>\n\n**Syntax:** `ZDIFFSTORE destination numkeys key [key ...]`\n\n**Description:** Stores the difference of multiple sorted sets in a key.\n\n**Complexity:** O(L + (N-K)log(N)) worst case where L is the total number of elements in all the sets, N is the size of the first set, and K is the size of the result set.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zincrby/\">ZINCRBY</a></strong> - Increments the score of a member in a sorted set.</summary>\n\n**Syntax:** `ZINCRBY key increment member`\n\n**Description:** Increments the score of a member in a sorted set.\n\n**Complexity:** O(log(N)) where N is the number of elements in the sorted set.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zinter/\">ZINTER</a></strong> - Returns the intersect of multiple sorted sets.</summary>\n\n**Syntax:** `ZINTER numkeys key [key ...] [WEIGHTS weight [weight ...]]\n  [AGGREGATE <SUM | MIN | MAX>] [WITHSCORES]`\n\n**Description:** Returns the intersect of multiple sorted sets.\n\n**Complexity:** O(N*K)+O(M*log(M)) worst case with N being the smallest input sorted set, K being the number of input sorted sets and M being the number of elements in the resulting sorted set.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zintercard/\">ZINTERCARD</a></strong> - Returns the number of members of the intersect of multiple sorted sets.</summary>\n\n**Syntax:** `ZINTERCARD numkeys key [key ...] [LIMIT limit]`\n\n**Description:** Returns the number of members of the intersect of multiple sorted sets.\n\n**Complexity:** O(N*K) worst case with N being the smallest input sorted set, K being the number of input sorted sets.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zinterstore/\">ZINTERSTORE</a></strong> - Stores the intersect of multiple sorted sets in a key.</summary>\n\n**Syntax:** `ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight\n  [weight ...]] [AGGREGATE <SUM | MIN | MAX>]`\n\n**Description:** Stores the intersect of multiple sorted sets in a key.\n\n**Complexity:** O(N*K)+O(M*log(M)) worst case with N being the smallest input sorted set, K being the number of input sorted sets and M being the number of elements in the resulting sorted set.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zlexcount/\">ZLEXCOUNT</a></strong> - Returns the number of members in a sorted set within a lexicographical range.</summary>\n\n**Syntax:** `ZLEXCOUNT key min max`\n\n**Description:** Returns the number of members in a sorted set within a lexicographical range.\n\n**Complexity:** O(log(N)) with N being the number of elements in the sorted set.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zmpop/\">ZMPOP</a></strong> - Returns the highest- or lowest-scoring members from one or more sorted sets after removing them. Deletes the sorted set if the last member was popped.</summary>\n\n**Syntax:** `ZMPOP numkeys key [key ...] <MIN | MAX> [COUNT count]`\n\n**Description:** Returns the highest- or lowest-scoring members from one or more sorted sets after removing them. Deletes the sorted set if the last member was popped.\n\n**Complexity:** O(K) + O(M*log(N)) where K is the number of provided keys, N being the number of elements in the sorted set, and M being the number of elements popped.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zmscore/\">ZMSCORE</a></strong> - Returns the score of one or more members in a sorted set.</summary>\n\n**Syntax:** `ZMSCORE key member [member ...]`\n\n**Description:** Returns the score of one or more members in a sorted set.\n\n**Complexity:** O(N) where N is the number of members being requested.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zpopmax/\">ZPOPMAX</a></strong> - Returns the highest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.</summary>\n\n**Syntax:** `ZPOPMAX key [count]`\n\n**Description:** Returns the highest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.\n\n**Complexity:** O(log(N)*M) with N being the number of elements in the sorted set, and M being the number of elements popped.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zpopmin/\">ZPOPMIN</a></strong> - Returns the lowest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.</summary>\n\n**Syntax:** `ZPOPMIN key [count]`\n\n**Description:** Returns the lowest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.\n\n**Complexity:** O(log(N)*M) with N being the number of elements in the sorted set, and M being the number of elements popped.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrandmember/\">ZRANDMEMBER</a></strong> - Returns one or more random members from a sorted set.</summary>\n\n**Syntax:** `ZRANDMEMBER key [count [WITHSCORES]]`\n\n**Description:** Returns one or more random members from a sorted set.\n\n**Complexity:** O(N) where N is the number of members returned\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrange/\">ZRANGE</a></strong> - Returns members in a sorted set within a range of indexes.</summary>\n\n**Syntax:** `ZRANGE key start stop [BYSCORE | BYLEX] [REV] [LIMIT offset count]\n  [WITHSCORES]`\n\n**Description:** Returns members in a sorted set within a range of indexes.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements returned.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrangebylex/\">ZRANGEBYLEX</a></strong> - Returns members in a sorted set within a lexicographical range.</summary>\n\n**Syntax:** `ZRANGEBYLEX key min max [LIMIT offset count]`\n\n**Description:** Returns members in a sorted set within a lexicographical range.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrangebyscore/\">ZRANGEBYSCORE</a></strong> - Returns members in a sorted set within a range of scores.</summary>\n\n**Syntax:** `ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]`\n\n**Description:** Returns members in a sorted set within a range of scores.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).\n\n**Since:** 1.0.5\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrangestore/\">ZRANGESTORE</a></strong> - Stores a range of members from sorted set in a key.</summary>\n\n**Syntax:** `ZRANGESTORE dst src min max [BYSCORE | BYLEX] [REV] [LIMIT offset\n  count]`\n\n**Description:** Stores a range of members from sorted set in a key.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements stored into the destination key.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrank/\">ZRANK</a></strong> - Returns the index of a member in a sorted set ordered by ascending scores.</summary>\n\n**Syntax:** `ZRANK key member [WITHSCORE]`\n\n**Description:** Returns the index of a member in a sorted set ordered by ascending scores.\n\n**Complexity:** O(log(N))\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrem/\">ZREM</a></strong> - Removes one or more members from a sorted set. Deletes the sorted set if all members were removed.</summary>\n\n**Syntax:** `ZREM key member [member ...]`\n\n**Description:** Removes one or more members from a sorted set. Deletes the sorted set if all members were removed.\n\n**Complexity:** O(M*log(N)) with N being the number of elements in the sorted set and M the number of elements to be removed.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zremrangebylex/\">ZREMRANGEBYLEX</a></strong> - Removes members in a sorted set within a lexicographical range. Deletes the sorted set if all members were removed.</summary>\n\n**Syntax:** `ZREMRANGEBYLEX key min max`\n\n**Description:** Removes members in a sorted set within a lexicographical range. Deletes the sorted set if all members were removed.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zremrangebyrank/\">ZREMRANGEBYRANK</a></strong> - Removes members in a sorted set within a range of indexes. Deletes the sorted set if all members were removed.</summary>\n\n**Syntax:** `ZREMRANGEBYRANK key start stop`\n\n**Description:** Removes members in a sorted set within a range of indexes. Deletes the sorted set if all members were removed.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zremrangebyscore/\">ZREMRANGEBYSCORE</a></strong> - Removes members in a sorted set within a range of scores. Deletes the sorted set if all members were removed.</summary>\n\n**Syntax:** `ZREMRANGEBYSCORE key min max`\n\n**Description:** Removes members in a sorted set within a range of scores. Deletes the sorted set if all members were removed.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrevrange/\">ZREVRANGE</a></strong> - Returns members in a sorted set within a range of indexes in reverse order.</summary>\n\n**Syntax:** `ZREVRANGE key start stop [WITHSCORES]`\n\n**Description:** Returns members in a sorted set within a range of indexes in reverse order.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements returned.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrevrangebylex/\">ZREVRANGEBYLEX</a></strong> - Returns members in a sorted set within a lexicographical range in reverse order.</summary>\n\n**Syntax:** `ZREVRANGEBYLEX key max min [LIMIT offset count]`\n\n**Description:** Returns members in a sorted set within a lexicographical range in reverse order.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrevrangebyscore/\">ZREVRANGEBYSCORE</a></strong> - Returns members in a sorted set within a range of scores in reverse order.</summary>\n\n**Syntax:** `ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]`\n\n**Description:** Returns members in a sorted set within a range of scores in reverse order.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrevrank/\">ZREVRANK</a></strong> - Returns the index of a member in a sorted set ordered by descending scores.</summary>\n\n**Syntax:** `ZREVRANK key member [WITHSCORE]`\n\n**Description:** Returns the index of a member in a sorted set ordered by descending scores.\n\n**Complexity:** O(log(N))\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zscan/\">ZSCAN</a></strong> - Iterates over members and scores of a sorted set.</summary>\n\n**Syntax:** `ZSCAN key cursor [MATCH pattern] [COUNT count]`\n\n**Description:** Iterates over members and scores of a sorted set.\n\n**Complexity:** O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zscore/\">ZSCORE</a></strong> - Returns the score of a member in a sorted set.</summary>\n\n**Syntax:** `ZSCORE key member`\n\n**Description:** Returns the score of a member in a sorted set.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zunion/\">ZUNION</a></strong> - Returns the union of multiple sorted sets.</summary>\n\n**Syntax:** `ZUNION numkeys key [key ...] [WEIGHTS weight [weight ...]]\n  [AGGREGATE <SUM | MIN | MAX>] [WITHSCORES]`\n\n**Description:** Returns the union of multiple sorted sets.\n\n**Complexity:** O(N)+O(M*log(M)) with N being the sum of the sizes of the input sorted sets, and M being the number of elements in the resulting sorted set.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zunionstore/\">ZUNIONSTORE</a></strong> - Stores the union of multiple sorted sets in a key.</summary>\n\n**Syntax:** `ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight\n  [weight ...]] [AGGREGATE <SUM | MIN | MAX>]`\n\n**Description:** Stores the union of multiple sorted sets in a key.\n\n**Complexity:** O(N)+O(M log(M)) with N being the sum of the sizes of the input sorted sets, and M being the number of elements in the resulting sorted set.\n\n**Since:** 2.0.0\n\n</details>"},{"id":"stream-commands","title":"Stream commands","role":"content","text":"Stream commands operate on append-only log data structures.\n\n<details>\n<summary><strong><a href=\"/commands/xack/\">XACK</a></strong> - Returns the number of messages that were successfully acknowledged by the consumer group member of a stream.</summary>\n\n**Syntax:** `XACK key group id [id ...]`\n\n**Description:** Returns the number of messages that were successfully acknowledged by the consumer group member of a stream.\n\n**Complexity:** O(1) for each message ID processed.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xackdel/\">XACKDEL</a></strong> - Acknowledges and conditionally deletes one or multiple entries for a stream consumer group.</summary>\n\n**Syntax:** `XACKDEL key group [KEEPREF | DELREF | ACKED] IDS numids id [id ...]`\n\n**Description:** Acknowledges and conditionally deletes one or multiple entries for a stream consumer group.\n\n**Complexity:** O(1) for each entry ID processed.\n\n**Since:** 8.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xadd/\">XADD</a></strong> - Appends a new message to a stream. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `XADD key [NOMKSTREAM] [KEEPREF | DELREF | ACKED]\n  [IDMPAUTO producer-id | IDMP producer-id idempotent-id]\n  [<MAXLEN | MINID> [= | ~] threshold [LIMIT count]] <* | id>\n  field value [field value ...]`\n\n**Description:** Appends a new message to a stream. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) when adding a new entry, O(N) when trimming where N being the number of entries evicted.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xautoclaim/\">XAUTOCLAIM</a></strong> - Changes, or acquires, ownership of messages in a consumer group, as if the messages were delivered to as consumer group member.</summary>\n\n**Syntax:** `XAUTOCLAIM key group consumer min-idle-time start [COUNT count]\n  [JUSTID]`\n\n**Description:** Changes, or acquires, ownership of messages in a consumer group, as if the messages were delivered to as consumer group member.\n\n**Complexity:** O(1) if COUNT is small.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xcfgset/\">XCFGSET</a></strong> - Sets the IDMP configuration parameters for a stream. <span style=\"color: #e74c3c;\">⭐ New in 8.6</span></summary>\n\n**Syntax:** `XCFGSET key [IDMP-DURATION duration] [IDMP-MAXSIZE maxsize]`\n\n**Description:** Sets the IDMP configuration parameters for a stream.\n\n**Complexity:** O(1)\n\n**Since:** 8.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xclaim/\">XCLAIM</a></strong> - Changes, or acquires, ownership of a message in a consumer group, as if the message was delivered a consumer group member.</summary>\n\n**Syntax:** `XCLAIM key group consumer min-idle-time id [id ...] [IDLE ms]\n  [TIME unix-time-milliseconds] [RETRYCOUNT count] [FORCE] [JUSTID]\n  [LASTID lastid]`\n\n**Description:** Changes, or acquires, ownership of a message in a consumer group, as if the message was delivered a consumer group member.\n\n**Complexity:** O(log N) with N being the number of messages in the PEL of the consumer group.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xdel/\">XDEL</a></strong> - Returns the number of messages after removing them from a stream.</summary>\n\n**Syntax:** `XDEL key id [id ...]`\n\n**Description:** Returns the number of messages after removing them from a stream.\n\n**Complexity:** O(1) for each single item to delete in the stream, regardless of the stream size.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xdelex/\">XDELEX</a></strong> - Deletes one or multiple entries from the stream.</summary>\n\n**Syntax:** `XDELEX key [KEEPREF | DELREF | ACKED] IDS numids id [id ...]`\n\n**Description:** Deletes one or multiple entries from the stream.\n\n**Complexity:** O(1) for each single item to delete in the stream, regardless of the stream size.\n\n**Since:** 8.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-create/\">XGROUP CREATE</a></strong> - Creates a consumer group.</summary>\n\n**Syntax:** `XGROUP CREATE key group <id | $> [MKSTREAM]\n  [ENTRIESREAD entries-read]`\n\n**Description:** Creates a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-createconsumer/\">XGROUP CREATECONSUMER</a></strong> - Creates a consumer in a consumer group.</summary>\n\n**Syntax:** `XGROUP CREATECONSUMER key group consumer`\n\n**Description:** Creates a consumer in a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-delconsumer/\">XGROUP DELCONSUMER</a></strong> - Deletes a consumer from a consumer group.</summary>\n\n**Syntax:** `XGROUP DELCONSUMER key group consumer`\n\n**Description:** Deletes a consumer from a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-destroy/\">XGROUP DESTROY</a></strong> - Destroys a consumer group.</summary>\n\n**Syntax:** `XGROUP DESTROY key group`\n\n**Description:** Destroys a consumer group.\n\n**Complexity:** O(N) where N is the number of entries in the group's pending entries list (PEL).\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-setid/\">XGROUP SETID</a></strong> - Sets the last-delivered ID of a consumer group.</summary>\n\n**Syntax:** `XGROUP SETID key group <id | $> [ENTRIESREAD entries-read]`\n\n**Description:** Sets the last-delivered ID of a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xinfo-consumers/\">XINFO CONSUMERS</a></strong> - Returns a list of the consumers in a consumer group.</summary>\n\n**Syntax:** `XINFO CONSUMERS key group`\n\n**Description:** Returns a list of the consumers in a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xinfo-groups/\">XINFO GROUPS</a></strong> - Returns a list of the consumer groups of a stream.</summary>\n\n**Syntax:** `XINFO GROUPS key`\n\n**Description:** Returns a list of the consumer groups of a stream.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xinfo-stream/\">XINFO STREAM</a></strong> - Returns information about a stream.</summary>\n\n**Syntax:** `XINFO STREAM key [FULL [COUNT count]]`\n\n**Description:** Returns information about a stream.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xlen/\">XLEN</a></strong> - Return the number of messages in a stream.</summary>\n\n**Syntax:** `XLEN key`\n\n**Description:** Return the number of messages in a stream.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xpending/\">XPENDING</a></strong> - Returns the information and entries from a stream consumer group's pending entries list.</summary>\n\n**Syntax:** `XPENDING key group [[IDLE min-idle-time] start end count [consumer]]`\n\n**Description:** Returns the information and entries from a stream consumer group's pending entries list.\n\n**Complexity:** O(N) with N being the number of elements returned, so asking for a small fixed number of entries per call is O(1). O(M), where M is the total number of entries scanned when used with the IDLE filter. When the command returns just the summary and the list of consumers is small, it runs in O(1) time; otherwise, an additional O(N) time for iterating every consumer.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xrange/\">XRANGE</a></strong> - Returns the messages from a stream within a range of IDs.</summary>\n\n**Syntax:** `XRANGE key start end [COUNT count]`\n\n**Description:** Returns the messages from a stream within a range of IDs.\n\n**Complexity:** O(N) with N being the number of elements being returned. If N is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1).\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xread/\">XREAD</a></strong> - Returns messages from multiple streams with IDs greater than the ones requested. Blocks until a message is available otherwise.</summary>\n\n**Syntax:** `XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] id\n  [id ...]`\n\n**Description:** Returns messages from multiple streams with IDs greater than the ones requested. Blocks until a message is available otherwise.\n\n**Complexity:** For each stream mentioned: O(M) with M being the number of elements returned. If M is constant (for example, always asking for the first 10 elements with COUNT), you can consider it O(1). On the other side when XREADGROUP blocks, XADD will pay the O(N) time in order to serve the N clients blocked on the stream getting new data.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xreadgroup/\">XREADGROUP</a></strong> - Returns new or historical messages from a stream for a consumer in a group. Blocks until a message is available otherwise.</summary>\n\n**Syntax:** `XREADGROUP GROUP group consumer [COUNT count] [BLOCK milliseconds]\n  [CLAIM min-idle-time] [NOACK] STREAMS key [key ...] id [id ...]`\n\n**Description:** Returns new or historical messages from a stream for a consumer in a group. Blocks until a message is available otherwise.\n\n**Complexity:** For each stream mentioned: O(M) with M being the number of elements returned. If M is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1). On the other side when XREADGROUP blocks, XADD will pay the O(N) time in order to serve the N clients blocked on the stream getting new data.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xrevrange/\">XREVRANGE</a></strong> - Returns the messages from a stream within a range of IDs in reverse order.</summary>\n\n**Syntax:** `XREVRANGE key end start [COUNT count]`\n\n**Description:** Returns the messages from a stream within a range of IDs in reverse order.\n\n**Complexity:** O(N) with N being the number of elements returned. If N is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1).\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xsetid/\">XSETID</a></strong> - An internal command for replicating stream values.</summary>\n\n**Syntax:** `XSETID key last-id [ENTRIESADDED entries-added]\n  [MAXDELETEDID max-deleted-id]`\n\n**Description:** An internal command for replicating stream values.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xtrim/\">XTRIM</a></strong> - Deletes messages from the beginning of a stream.</summary>\n\n**Syntax:** `XTRIM key <MAXLEN | MINID> [= | ~] threshold [LIMIT count] [KEEPREF | DELREF | ACKED]`\n\n**Description:** Deletes messages from the beginning of a stream.\n\n**Complexity:** O(N), with N being the number of evicted entries. Constant times are very small however, since entries are organized in macro nodes containing multiple entries that can be released with a single deallocation.\n\n**Since:** 5.0.0\n\n</details>"},{"id":"bitmap-commands","title":"Bitmap commands","role":"content","text":"Bitmap commands operate on strings as arrays of bits.\n\n<details>\n<summary><strong><a href=\"/commands/bitcount/\">BITCOUNT</a></strong> - Counts the number of set bits (population counting) in a string.</summary>\n\n**Syntax:** `BITCOUNT key [start end [BYTE | BIT]]`\n\n**Description:** Counts the number of set bits (population counting) in a string.\n\n**Complexity:** O(N)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bitfield/\">BITFIELD</a></strong> - Performs arbitrary bitfield integer operations on strings.</summary>\n\n**Syntax:** `BITFIELD key [GET encoding offset | [OVERFLOW <WRAP | SAT | FAIL>]\n  <SET encoding offset value | INCRBY encoding offset increment>\n  [GET encoding offset | [OVERFLOW <WRAP | SAT | FAIL>]\n  <SET encoding offset value | INCRBY encoding offset increment>\n  ...]]`\n\n**Description:** Performs arbitrary bitfield integer operations on strings.\n\n**Complexity:** O(1) for each subcommand specified\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bitfield_ro/\">BITFIELD_RO</a></strong> - Performs arbitrary read-only bitfield integer operations on strings.</summary>\n\n**Syntax:** `BITFIELD RO key [GET encoding offset [GET encoding offset ...]]`\n\n**Description:** Performs arbitrary read-only bitfield integer operations on strings.\n\n**Complexity:** O(1) for each subcommand specified\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bitop/\">BITOP</a></strong> - Performs bitwise operations on multiple strings, and stores the result.</summary>\n\n**Syntax:** `BITOP <AND | OR | XOR | NOT | DIFF | DIFF1 | ANDOR | ONE> destkey key [key ...]`\n\n**Description:** Performs bitwise operations on multiple strings, and stores the result.\n\n**Complexity:** O(N)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bitpos/\">BITPOS</a></strong> - Finds the first set (1) or clear (0) bit in a string.</summary>\n\n**Syntax:** `BITPOS key bit [start [end [BYTE | BIT]]]`\n\n**Description:** Finds the first set (1) or clear (0) bit in a string.\n\n**Complexity:** O(N)\n\n**Since:** 2.8.7\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getbit/\">GETBIT</a></strong> - Returns a bit value by offset.</summary>\n\n**Syntax:** `GETBIT key offset`\n\n**Description:** Returns a bit value by offset.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/setbit/\">SETBIT</a></strong> - Sets or clears the bit at offset of the string value. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `SETBIT key offset value`\n\n**Description:** Sets or clears the bit at offset of the string value. Creates the key if it doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>"},{"id":"hyperloglog-commands","title":"HyperLogLog commands","role":"content","text":"HyperLogLog commands provide probabilistic cardinality estimation.\n\n<details>\n<summary><strong><a href=\"/commands/pfadd/\">PFADD</a></strong> - Adds elements to a HyperLogLog key. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `PFADD key [element [element ...]]`\n\n**Description:** Adds elements to a HyperLogLog key. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) to add every element.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pfcount/\">PFCOUNT</a></strong> - Returns the approximated cardinality of the set(s) observed by the HyperLogLog key(s).</summary>\n\n**Syntax:** `PFCOUNT key [key ...]`\n\n**Description:** Returns the approximated cardinality of the set(s) observed by the HyperLogLog key(s).\n\n**Complexity:** O(1) with a very small average constant time when called with a single key. O(N) with N being the number of keys, and much bigger constant times, when called with multiple keys.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pfdebug/\">PFDEBUG</a></strong> - Internal commands for debugging HyperLogLog values.</summary>\n\n**Syntax:** `PFDEBUG subcommand key`\n\n**Description:** Internal commands for debugging HyperLogLog values.\n\n**Complexity:** N/A\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pfmerge/\">PFMERGE</a></strong> - Merges one or more HyperLogLog values into a single key.</summary>\n\n**Syntax:** `PFMERGE destkey [sourcekey [sourcekey ...]]`\n\n**Description:** Merges one or more HyperLogLog values into a single key.\n\n**Complexity:** O(N) to merge N HyperLogLogs, but with high constant times.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pfselftest/\">PFSELFTEST</a></strong> - An internal command for testing HyperLogLog values.</summary>\n\n**Syntax:** `PFSELFTEST`\n\n**Description:** An internal command for testing HyperLogLog values.\n\n**Complexity:** N/A\n\n**Since:** 2.8.9\n\n</details>"},{"id":"geospatial-commands","title":"Geospatial commands","role":"content","text":"Geospatial commands operate on geographic coordinates.\n\n<details>\n<summary><strong><a href=\"/commands/geoadd/\">GEOADD</a></strong> - Adds one or more members to a geospatial index. The key is created if it doesn't exist.</summary>\n\n**Syntax:** `GEOADD key [NX | XX] [CH] longitude latitude member [longitude\n  latitude member ...]`\n\n**Description:** Adds one or more members to a geospatial index. The key is created if it doesn't exist.\n\n**Complexity:** O(log(N)) for each item added, where N is the number of elements in the sorted set.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geodist/\">GEODIST</a></strong> - Returns the distance between two members of a geospatial index.</summary>\n\n**Syntax:** `GEODIST key member1 member2 [M | KM | FT | MI]`\n\n**Description:** Returns the distance between two members of a geospatial index.\n\n**Complexity:** O(1)\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geohash/\">GEOHASH</a></strong> - Returns members from a geospatial index as geohash strings.</summary>\n\n**Syntax:** `GEOHASH key [member [member ...]]`\n\n**Description:** Returns members from a geospatial index as geohash strings.\n\n**Complexity:** O(1) for each member requested.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geopos/\">GEOPOS</a></strong> - Returns the longitude and latitude of members from a geospatial index.</summary>\n\n**Syntax:** `GEOPOS key [member [member ...]]`\n\n**Description:** Returns the longitude and latitude of members from a geospatial index.\n\n**Complexity:** O(1) for each member requested.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/georadius/\">GEORADIUS</a></strong> - Queries a geospatial index for members within a distance from a coordinate, optionally stores the result.</summary>\n\n**Syntax:** `GEORADIUS key longitude latitude radius <M | KM | FT | MI>\n  [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC]\n  [STORE key | STOREDIST key]`\n\n**Description:** Queries a geospatial index for members within a distance from a coordinate, optionally stores the result.\n\n**Complexity:** O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/georadiusbymember/\">GEORADIUSBYMEMBER</a></strong> - Queries a geospatial index for members within a distance from a member, optionally stores the result.</summary>\n\n**Syntax:** `GEORADIUSBYMEMBER key member radius <M | KM | FT | MI> [WITHCOORD]\n  [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC] [STORE key\n  | STOREDIST key]`\n\n**Description:** Queries a geospatial index for members within a distance from a member, optionally stores the result.\n\n**Complexity:** O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/georadiusbymember_ro/\">GEORADIUSBYMEMBER_RO</a></strong> - Returns members from a geospatial index that are within a distance from a member.</summary>\n\n**Syntax:** `GEORADIUSBYMEMBER RO key member radius <M | KM | FT | MI>\n  [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC]`\n\n**Description:** Returns members from a geospatial index that are within a distance from a member.\n\n**Complexity:** O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.\n\n**Since:** 3.2.10\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/georadius_ro/\">GEORADIUS_RO</a></strong> - Returns members from a geospatial index that are within a distance from a coordinate.</summary>\n\n**Syntax:** `GEORADIUS RO key longitude latitude radius <M | KM | FT | MI>\n  [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC]`\n\n**Description:** Returns members from a geospatial index that are within a distance from a coordinate.\n\n**Complexity:** O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.\n\n**Since:** 3.2.10\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geosearch/\">GEOSEARCH</a></strong> - Queries a geospatial index for members inside an area of a box or a circle.</summary>\n\n**Syntax:** `GEOSEARCH key <FROMMEMBER member | FROMLONLAT longitude latitude>\n  <BYRADIUS radius <M | KM | FT | MI> | BYBOX width height <M | KM |\n  FT | MI>> [ASC | DESC] [COUNT count [ANY]] [WITHCOORD] [WITHDIST]\n  [WITHHASH]`\n\n**Description:** Queries a geospatial index for members inside an area of a box or a circle.\n\n**Complexity:** O(N+log(M)) where N is the number of elements in the grid-aligned bounding box area around the shape provided as the filter and M is the number of items inside the shape\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geosearchstore/\">GEOSEARCHSTORE</a></strong> - Queries a geospatial index for members inside an area of a box or a circle, optionally stores the result.</summary>\n\n**Syntax:** `GEOSEARCHSTORE destination source <FROMMEMBER member |\n  FROMLONLAT longitude latitude> <BYRADIUS radius <M | KM | FT | MI>\n  | BYBOX width height <M | KM | FT | MI>> [ASC | DESC] [COUNT count\n  [ANY]] [STOREDIST]`\n\n**Description:** Queries a geospatial index for members inside an area of a box or a circle, optionally stores the result.\n\n**Complexity:** O(N+log(M)) where N is the number of elements in the grid-aligned bounding box area around the shape provided as the filter and M is the number of items inside the shape\n\n**Since:** 6.2.0\n\n</details>"},{"id":"json-commands","title":"JSON commands","role":"content","text":"JSON commands operate on JSON data structures.\n\n<details>\n<summary><strong><a href=\"/commands/json.arrappend/\">JSON.ARRAPPEND</a></strong> - Append one or more json values into the array at path after the last element in it.</summary>\n\n**Syntax:** `JSON.ARRAPPEND key [path] value [value ...]`\n\n**Description:** Append one or more json values into the array at path after the last element in it.\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrindex/\">JSON.ARRINDEX</a></strong> - Returns the index of the first occurrence of a JSON scalar value in the array at path</summary>\n\n**Syntax:** `JSON.ARRINDEX key path value [start [stop]]`\n\n**Description:** Returns the index of the first occurrence of a JSON scalar value in the array at path\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the array, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrinsert/\">JSON.ARRINSERT</a></strong> - Inserts the JSON scalar(s) value at the specified index in the array at path</summary>\n\n**Syntax:** `JSON.ARRINSERT key path index value [value ...]`\n\n**Description:** Inserts the JSON scalar(s) value at the specified index in the array at path\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the array, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrlen/\">JSON.ARRLEN</a></strong> - Returns the length of the array at path</summary>\n\n**Syntax:** `JSON.ARRLEN key [path]`\n\n**Description:** Returns the length of the array at path\n\n**Complexity:** O(1) where path is evaluated to a single value, O(N) where path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrpop/\">JSON.ARRPOP</a></strong> - Removes and returns the element at the specified index in the array at path</summary>\n\n**Syntax:** `JSON.ARRPOP key [path [index]]`\n\n**Description:** Removes and returns the element at the specified index in the array at path\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the array and the specified index is not the last element, O(1) when path is evaluated to a single value and the specified index is the last element, or O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrtrim/\">JSON.ARRTRIM</a></strong> - Trims the array at path to contain only the specified inclusive range of indices from start to stop</summary>\n\n**Syntax:** `JSON.ARRTRIM key path start stop`\n\n**Description:** Trims the array at path to contain only the specified inclusive range of indices from start to stop\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the array, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.clear/\">JSON.CLEAR</a></strong> - Clears all values from an array or an object and sets numeric values to `0`</summary>\n\n**Syntax:** `JSON.CLEAR key [path]`\n\n**Description:** Clears all values from an array or an object and sets numeric values to `0`\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the values, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.debug/\">JSON.DEBUG</a></strong> - Debugging container command</summary>\n\n**Syntax:** `JSON.DEBUG`\n\n**Description:** Debugging container command\n\n**Complexity:** N/A\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.debug-memory/\">JSON.DEBUG MEMORY</a></strong> - Reports the size in bytes of a key</summary>\n\n**Syntax:** `JSON.DEBUG MEMORY key [path]`\n\n**Description:** Reports the size in bytes of a key\n\n**Complexity:** O(N) when path is evaluated to a single value, where N is the size of the value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.del/\">JSON.DEL</a></strong> - Deletes a value</summary>\n\n**Syntax:** `JSON.DEL key [path]`\n\n**Description:** Deletes a value\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the deleted value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.forget/\">JSON.FORGET</a></strong> - Deletes a value</summary>\n\n**Syntax:** `JSON.FORGET key [path]`\n\n**Description:** Deletes a value\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the deleted value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.get/\">JSON.GET</a></strong> - Gets the value at one or more paths in JSON serialized form</summary>\n\n**Syntax:** `JSON.GET key [INDENT indent] [NEWLINE newline] [SPACE space] [path\n  [path ...]]`\n\n**Description:** Gets the value at one or more paths in JSON serialized form\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.merge/\">JSON.MERGE</a></strong> - Merges a given JSON value into matching paths. Consequently, JSON values at matching paths are updated, deleted, or expanded with new children</summary>\n\n**Syntax:** `JSON.MERGE key path value`\n\n**Description:** Merges a given JSON value into matching paths. Consequently, JSON values at matching paths are updated, deleted, or expanded with new children\n\n**Complexity:** O(M+N) when path is evaluated to a single value where M is the size of the original value (if it exists) and N is the size of the new value, O(M+N) when path is evaluated to multiple values where M is the size of the key and N is the size of the new value * the number of original values in the key\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.mget/\">JSON.MGET</a></strong> - Returns the values at a path from one or more keys</summary>\n\n**Syntax:** `JSON.MGET key [key ...] path`\n\n**Description:** Returns the values at a path from one or more keys\n\n**Complexity:** O(M*N) when path is evaluated to a single value where M is the number of keys and N is the size of the value, O(N1+N2+...+Nm) when path is evaluated to multiple values where m is the number of keys and Ni is the size of the i-th key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.mset/\">JSON.MSET</a></strong> - Sets or updates the JSON value of one or more keys</summary>\n\n**Syntax:** `JSON.MSET key path value [key path value ...]`\n\n**Description:** Sets or updates the JSON value of one or more keys\n\n**Complexity:** O(K*(M+N)) where k is the number of keys in the command, when path is evaluated to a single value where M is the size of the original value (if it exists) and N is the size of the new value, or O(K*(M+N)) when path is evaluated to multiple values where M is the size of the key and N is the size of the new value * the number of original values in the key\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.numincrby/\">JSON.NUMINCRBY</a></strong> - Increments the numeric value at path by a value</summary>\n\n**Syntax:** `JSON.NUMINCRBY key path value`\n\n**Description:** Increments the numeric value at path by a value\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.nummultby/\">JSON.NUMMULTBY</a></strong> - Multiplies the numeric value at path by a value</summary>\n\n**Syntax:** `JSON.NUMMULTBY key path value`\n\n**Description:** Multiplies the numeric value at path by a value\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.objkeys/\">JSON.OBJKEYS</a></strong> - Returns the JSON keys of the object at path</summary>\n\n**Syntax:** `JSON.OBJKEYS key [path]`\n\n**Description:** Returns the JSON keys of the object at path\n\n**Complexity:** O(N) when path is evaluated to a single value, where N is the number of keys in the object, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.objlen/\">JSON.OBJLEN</a></strong> - Returns the number of keys of the object at path</summary>\n\n**Syntax:** `JSON.OBJLEN key [path]`\n\n**Description:** Returns the number of keys of the object at path\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.resp/\">JSON.RESP</a></strong> - Returns the JSON value at path in Redis Serialization Protocol (RESP)</summary>\n\n**Syntax:** `JSON.RESP key [path]`\n\n**Description:** Returns the JSON value at path in Redis Serialization Protocol (RESP)\n\n**Complexity:** O(N) when path is evaluated to a single value, where N is the size of the value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.set/\">JSON.SET</a></strong> - Sets or updates the JSON value at a path</summary>\n\n**Syntax:** `JSON.SET key path value [NX | XX]`\n\n**Description:** Sets or updates the JSON value at a path\n\n**Complexity:** O(M+N) when path is evaluated to a single value where M is the size of the original value (if it exists) and N is the size of the new value, O(M+N) when path is evaluated to multiple values where M is the size of the key and N is the size of the new value * the number of original values in the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.strappend/\">JSON.STRAPPEND</a></strong> - Appends a string to a JSON string value at path</summary>\n\n**Syntax:** `JSON.STRAPPEND key [path] value`\n\n**Description:** Appends a string to a JSON string value at path\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.strlen/\">JSON.STRLEN</a></strong> - Returns the length of the JSON String at path in key</summary>\n\n**Syntax:** `JSON.STRLEN key [path]`\n\n**Description:** Returns the length of the JSON String at path in key\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.toggle/\">JSON.TOGGLE</a></strong> - Toggles a boolean value</summary>\n\n**Syntax:** `JSON.TOGGLE key path`\n\n**Description:** Toggles a boolean value\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.type/\">JSON.TYPE</a></strong> - Returns the type of the JSON value at path</summary>\n\n**Syntax:** `JSON.TYPE key [path]`\n\n**Description:** Returns the type of the JSON value at path\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>"},{"id":"search-commands","title":"Search commands","role":"content","text":"Search commands provide full-text search and secondary indexing.\n\n<details>\n<summary><strong><a href=\"/commands/ft.aggregate/\">FT.AGGREGATE</a></strong> - Run a search query on an index and perform aggregate transformations on the results</summary>\n\n**Syntax:** `FT.AGGREGATE index query [VERBATIM] [LOAD count field [field ...]]\n  [TIMEOUT timeout] [LOAD *] [GROUPBY nargs property [property ...]\n  [REDUCE function nargs arg [arg ...] [AS name] [REDUCE function\n  nargs arg [arg ...] [AS name] ...]] [GROUPBY nargs property\n  [property ...] [REDUCE function nargs arg [arg ...] [AS name]\n  [REDUCE function nargs arg [arg ...] [AS name] ...]] ...]]\n  [SORTBY nargs [property <ASC | DESC> [property <ASC | DESC> ...]]\n  [MAX num]] [APPLY expression AS name [APPLY expression AS name\n  ...]] [LIMIT offset num] [FILTER filter] [WITHCURSOR\n  [COUNT read size] [MAXIDLE idle time]] [PARAMS nargs name value\n  [name value ...]] [DIALECT dialect]`\n\n**Description:** Run a search query on an index and perform aggregate transformations on the results\n\n**Complexity:** O(1)\n\n**Since:** 1.1.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.aliasadd/\">FT.ALIASADD</a></strong> - Adds an alias to the index</summary>\n\n**Syntax:** `FT.ALIASADD alias index`\n\n**Description:** Adds an alias to the index\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.aliasdel/\">FT.ALIASDEL</a></strong> - Deletes an alias from the index</summary>\n\n**Syntax:** `FT.ALIASDEL alias`\n\n**Description:** Deletes an alias from the index\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.aliasupdate/\">FT.ALIASUPDATE</a></strong> - Adds or updates an alias to the index</summary>\n\n**Syntax:** `FT.ALIASUPDATE alias index`\n\n**Description:** Adds or updates an alias to the index\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.alter/\">FT.ALTER</a></strong> - Adds a new field to the index</summary>\n\n**Syntax:** `FT.ALTER index [SKIPINITIALSCAN] SCHEMA ADD field options`\n\n**Description:** Adds a new field to the index\n\n**Complexity:** O(N) where N is the number of keys in the keyspace\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.config-get/\">FT.CONFIG GET</a></strong> - Retrieves runtime configuration options</summary>\n\n**Syntax:** `FT.CONFIG GET option`\n\n**Description:** Retrieves runtime configuration options\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.config-set/\">FT.CONFIG SET</a></strong> - Sets runtime configuration options</summary>\n\n**Syntax:** `FT.CONFIG SET option value`\n\n**Description:** Sets runtime configuration options\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.create/\">FT.CREATE</a></strong> - Creates an index with the given spec</summary>\n\n**Syntax:** `FT.CREATE index [ON <HASH | JSON>] [PREFIX count prefix [prefix\n  ...]] [FILTER filter] [LANGUAGE default lang]\n  [LANGUAGE FIELD lang attribute] [SCORE default score]\n  [SCORE FIELD score attribute] [PAYLOAD FIELD payload attribute]\n  [MAXTEXTFIELDS] [TEMPORARY seconds] [NOOFFSETS] [NOHL] [NOFIELDS]\n  [NOFREQS] [STOPWORDS count [stopword [stopword ...]]]\n  [SKIPINITIALSCAN] [INDEXALL <ENABLE | DISABLE>] SCHEMA field name [AS alias] <TEXT | TAG |\n  NUMERIC | GEO | VECTOR> [WITHSUFFIXTRIE] [INDEXEMPTY]\n  [INDEXMISSING] [SORTABLE [UNF]] [NOINDEX] [field name [AS alias]\n  <TEXT | TAG | NUMERIC | GEO | VECTOR> [WITHSUFFIXTRIE]\n  [INDEXEMPTY] [INDEXMISSING] [SORTABLE [UNF]] [NOINDEX] ...]`\n\n**Description:** Creates an index with the given spec\n\n**Complexity:** O(K) at creation where K is the number of fields, O(N) if scanning the keyspace is triggered, where N is the number of keys in the keyspace\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.cursor-del/\">FT.CURSOR DEL</a></strong> - Deletes a cursor</summary>\n\n**Syntax:** `FT.CURSOR DEL index cursor id`\n\n**Description:** Deletes a cursor\n\n**Complexity:** O(1)\n\n**Since:** 1.1.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.cursor-read/\">FT.CURSOR READ</a></strong> - Reads from a cursor</summary>\n\n**Syntax:** `FT.CURSOR READ index cursor id [COUNT read size]`\n\n**Description:** Reads from a cursor\n\n**Complexity:** O(1)\n\n**Since:** 1.1.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.dictadd/\">FT.DICTADD</a></strong> - Adds terms to a dictionary</summary>\n\n**Syntax:** `FT.DICTADD dict term [term ...]`\n\n**Description:** Adds terms to a dictionary\n\n**Complexity:** O(1)\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.dictdel/\">FT.DICTDEL</a></strong> - Deletes terms from a dictionary</summary>\n\n**Syntax:** `FT.DICTDEL dict term [term ...]`\n\n**Description:** Deletes terms from a dictionary\n\n**Complexity:** O(1)\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.dictdump/\">FT.DICTDUMP</a></strong> - Dumps all terms in the given dictionary</summary>\n\n**Syntax:** `FT.DICTDUMP dict`\n\n**Description:** Dumps all terms in the given dictionary\n\n**Complexity:** O(N), where N is the size of the dictionary\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.dropindex/\">FT.DROPINDEX</a></strong> - Deletes the index</summary>\n\n**Syntax:** `FT.DROPINDEX index [DD]`\n\n**Description:** Deletes the index\n\n**Complexity:** O(1) or O(N) if documents are deleted, where N is the number of keys in the keyspace\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.explain/\">FT.EXPLAIN</a></strong> - Returns the execution plan for a complex query</summary>\n\n**Syntax:** `FT.EXPLAIN index query [DIALECT dialect]`\n\n**Description:** Returns the execution plan for a complex query\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.explaincli/\">FT.EXPLAINCLI</a></strong> - Returns the execution plan for a complex query</summary>\n\n**Syntax:** `FT.EXPLAINCLI index query [DIALECT dialect]`\n\n**Description:** Returns the execution plan for a complex query\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.hybrid/\">FT.HYBRID</a></strong> - Performs hybrid search combining text search and vector similarity search</summary>\n\n**Syntax:** `FT.HYBRID index\n  SEARCH query\n    [SCORER scorer]\n    [YIELD SCORE AS name]\n  VSIM vector field $vector param\n    [KNN count K k [EF RUNTIME ef runtime]]\n    [RANGE count RADIUS radius [EPSILON epsilon]]\n    [YIELD SCORE AS name]\n    [FILTER filter]\n  [COMBINE RRF count [CONSTANT constant] [WINDOW window] [YIELD SCORE AS name]]\n  [COMBINE LINEAR count [[ALPHA alpha] [BETA beta]] [WINDOW window] [YIELD SCORE AS name]]\n  [LIMIT offset num]\n  [SORTBY count sortby [ASC | DESC]]\n  [NOSORT]\n  [LOAD count field [field ...]]\n  [LOAD *]\n  [GROUPBY nargs property [property ...]\n  [GROUPBY nargs property [property ...]\n    [REDUCE function nargs arg [arg ...] [AS name]\n    [REDUCE function nargs arg [arg ...] [AS name] ...]] ...]]\n  [APPLY expression AS name [APPLY expression AS name ...]]\n  [FILTER filter]\n  PARAMS nargs vector param vector blob [name value ...]\n  [TIMEOUT timeout]`\n\n**Description:** Performs hybrid search combining text search and vector similarity search\n\n**Complexity:** O(N+M) where N is the complexity of the text search and M is the complexity of the vector search\n\n**Since:** 8.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.info/\">FT.INFO</a></strong> - Returns information and statistics on the index</summary>\n\n**Syntax:** `FT.INFO index`\n\n**Description:** Returns information and statistics on the index\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.profile/\">FT.PROFILE</a></strong> - Performs a `FT.SEARCH` or `FT.AGGREGATE` command and collects performance information</summary>\n\n**Syntax:** `FT.PROFILE index <SEARCH | AGGREGATE> [LIMITED] QUERY query`\n\n**Description:** Performs a `FT.SEARCH` or `FT.AGGREGATE` command and collects performance information\n\n**Complexity:** O(N)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.search/\">FT.SEARCH</a></strong> - Searches the index with a textual query, returning either documents or just ids</summary>\n\n**Syntax:** `FT.SEARCH index query [NOCONTENT] [VERBATIM] [NOSTOPWORDS]\n  [WITHSCORES] [WITHPAYLOADS] [WITHSORTKEYS] [FILTER numeric field\n  min max [FILTER numeric field min max ...]] [GEOFILTER geo field\n  lon lat radius <m | km | mi | ft> [GEOFILTER geo field lon lat\n  radius <m | km | mi | ft> ...]] [INKEYS count key [key ...]]\n  [INFIELDS count field [field ...]] [RETURN count identifier\n  [AS property] [identifier [AS property] ...]] [SUMMARIZE\n  [FIELDS count field [field ...]] [FRAGS num] [LEN fragsize]\n  [SEPARATOR separator]] [HIGHLIGHT [FIELDS count field [field ...]]\n  [TAGS open close]] [SLOP slop] [TIMEOUT timeout] [INORDER]\n  [LANGUAGE language] [EXPANDER expander] [SCORER scorer]\n  [EXPLAINSCORE] [PAYLOAD payload] [SORTBY sortby [ASC | DESC]]\n  [LIMIT offset num] [PARAMS nargs name value [name value ...]]\n  [DIALECT dialect]`\n\n**Description:** Searches the index with a textual query, returning either documents or just ids\n\n**Complexity:** O(N)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.spellcheck/\">FT.SPELLCHECK</a></strong> - Performs spelling correction on a query, returning suggestions for misspelled terms</summary>\n\n**Syntax:** `FT.SPELLCHECK index query [DISTANCE distance] [TERMS <INCLUDE |\n  EXCLUDE> dictionary [terms [terms ...]]] [DIALECT dialect]`\n\n**Description:** Performs spelling correction on a query, returning suggestions for misspelled terms\n\n**Complexity:** O(1)\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.syndump/\">FT.SYNDUMP</a></strong> - Dumps the contents of a synonym group</summary>\n\n**Syntax:** `FT.SYNDUMP index`\n\n**Description:** Dumps the contents of a synonym group\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.synupdate/\">FT.SYNUPDATE</a></strong> - Creates or updates a synonym group with additional terms</summary>\n\n**Syntax:** `FT.SYNUPDATE index synonym group id [SKIPINITIALSCAN] term [term\n  ...]`\n\n**Description:** Creates or updates a synonym group with additional terms\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.tagvals/\">FT.TAGVALS</a></strong> - Returns the distinct tags indexed in a Tag field</summary>\n\n**Syntax:** `FT.TAGVALS index field name`\n\n**Description:** Returns the distinct tags indexed in a Tag field\n\n**Complexity:** O(N)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft._list/\">FT._LIST</a></strong> - Returns a list of all existing indexes</summary>\n\n**Syntax:** `FT. LIST`\n\n**Description:** Returns a list of all existing indexes\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>"},{"id":"time-series-commands","title":"Time series commands","role":"content","text":"Time series commands operate on time-series data.\n\n<details>\n<summary><strong><a href=\"/commands/ts.add/\">TS.ADD</a></strong> - Append a sample to a time series</summary>\n\n**Syntax:** `TS.ADD key timestamp value [RETENTION retentionPeriod]\n  [ENCODING <UNCOMPRESSED | COMPRESSED>] [CHUNK SIZE size]\n  [ON DUPLICATE <BLOCK | FIRST | LAST | MIN | MAX | SUM>]\n  [LABELS label value [label value ...]]`\n\n**Description:** Append a sample to a time series\n\n**Complexity:** O(M) when M is the amount of compaction rules or O(1) with no compaction\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.alter/\">TS.ALTER</a></strong> - Update the retention, chunk size, duplicate policy, and labels of an existing time series</summary>\n\n**Syntax:** `TS.ALTER key [RETENTION retentionPeriod] [CHUNK SIZE size]\n  [DUPLICATE POLICY <BLOCK | FIRST | LAST | MIN | MAX | SUM>] [LABELS label value\n  [label value ...]]`\n\n**Description:** Update the retention, chunk size, duplicate policy, and labels of an existing time series\n\n**Complexity:** O(N) where N is the number of labels requested to update\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.create/\">TS.CREATE</a></strong> - Create a new time series</summary>\n\n**Syntax:** `TS.CREATE key [RETENTION retentionPeriod] [ENCODING <UNCOMPRESSED |\n  COMPRESSED>] [CHUNK SIZE size] [DUPLICATE POLICY <BLOCK | FIRST |\n  LAST | MIN | MAX | SUM>] [LABELS label value [label value ...]]`\n\n**Description:** Create a new time series\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.createrule/\">TS.CREATERULE</a></strong> - Create a compaction rule</summary>\n\n**Syntax:** `TS.CREATERULE sourceKey destKey AGGREGATION <AVG | FIRST | LAST |\n  MIN | MAX | SUM | RANGE | COUNT | STD.P | STD.S | VAR.P | VAR.S |\n  TWA> bucketDuration [alignTimestamp]`\n\n**Description:** Create a compaction rule\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.decrby/\">TS.DECRBY</a></strong> - Decrease the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given decrement</summary>\n\n**Syntax:** `TS.DECRBY key value [TIMESTAMP timestamp]\n  [RETENTION retentionPeriod] [UNCOMPRESSED] [CHUNK SIZE size]\n  [LABELS label value [label value ...]]`\n\n**Description:** Decrease the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given decrement\n\n**Complexity:** O(M) when M is the amount of compaction rules or O(1) with no compaction\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.del/\">TS.DEL</a></strong> - Delete all samples between two timestamps for a given time series</summary>\n\n**Syntax:** `TS.DEL key from timestamp to timestamp`\n\n**Description:** Delete all samples between two timestamps for a given time series\n\n**Complexity:** O(N) where N is the number of data points that will be removed\n\n**Since:** 1.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.deleterule/\">TS.DELETERULE</a></strong> - Delete a compaction rule</summary>\n\n**Syntax:** `TS.DELETERULE sourceKey destKey`\n\n**Description:** Delete a compaction rule\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.get/\">TS.GET</a></strong> - Get the sample with the highest timestamp from a given time series</summary>\n\n**Syntax:** `TS.GET key [LATEST]`\n\n**Description:** Get the sample with the highest timestamp from a given time series\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.incrby/\">TS.INCRBY</a></strong> - Increase the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given increment</summary>\n\n**Syntax:** `TS.INCRBY key value [TIMESTAMP timestamp]\n  [RETENTION retentionPeriod] [UNCOMPRESSED] [CHUNK SIZE size]\n  [LABELS label value [label value ...]]`\n\n**Description:** Increase the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given increment\n\n**Complexity:** O(M) when M is the amount of compaction rules or O(1) with no compaction\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.info/\">TS.INFO</a></strong> - Returns information and statistics for a time series</summary>\n\n**Syntax:** `TS.INFO key [DEBUG]`\n\n**Description:** Returns information and statistics for a time series\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.madd/\">TS.MADD</a></strong> - Append new samples to one or more time series</summary>\n\n**Syntax:** `TS.MADD key timestamp value [key timestamp value ...]`\n\n**Description:** Append new samples to one or more time series\n\n**Complexity:** O(N*M) when N is the amount of series updated and M is the amount of compaction rules or O(N) with no compaction\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.mget/\">TS.MGET</a></strong> - Get the sample with the highest timestamp from each time series matching a specific filter</summary>\n\n**Syntax:** `TS.MGET [LATEST] [WITHLABELS | SELECTED LABELS label1 [label1 ...]]\n  FILTER <l=v | l!=v | l= | l!= | l=(v1,v2,...) | l!=(v1,v2,...)\n  [l=v | l!=v | l= | l!= | l=(v1,v2,...) | l!=(v1,v2,...) ...]>`\n\n**Description:** Get the sample with the highest timestamp from each time series matching a specific filter\n\n**Complexity:** O(n) where n is the number of time-series that match the filters\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.mrange/\">TS.MRANGE</a></strong> - Query a range across multiple time series by filters in forward direction</summary>\n\n**Syntax:** `TS.MRANGE fromTimestamp toTimestamp [LATEST] [FILTER BY TS Timestamp\n  [Timestamp ...]] [FILTER BY VALUE min max] [WITHLABELS |\n  SELECTED LABELS label1 [label1 ...]] [COUNT count] [[ALIGN value]\n  AGGREGATION <AVG | FIRST | LAST | MIN | MAX | SUM | RANGE | COUNT\n  | STD.P | STD.S | VAR.P | VAR.S | TWA> bucketDuration\n  [BUCKETTIMESTAMP] [EMPTY]] FILTER <l=v | l!=v | l= | l!= |\n  l=(v1,v2,...) | l!=(v1,v2,...) [l=v | l!=v | l= | l!= |\n  l=(v1,v2,...) | l!=(v1,v2,...) ...]> [GROUPBY label REDUCE\n  reducer]`\n\n**Description:** Query a range across multiple time series by filters in forward direction\n\n**Complexity:** O(n/m+k) where n = Number of data points, m = Chunk size (data points per chunk), k = Number of data points that are in the requested ranges\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.mrevrange/\">TS.MREVRANGE</a></strong> - Query a range across multiple time-series by filters in reverse direction</summary>\n\n**Syntax:** `TS.MREVRANGE fromTimestamp toTimestamp [LATEST]\n  [FILTER BY TS Timestamp [Timestamp ...]] [FILTER BY VALUE min max]\n  [WITHLABELS | SELECTED LABELS label1 [label1 ...]] [COUNT count]\n  [[ALIGN value] AGGREGATION <AVG | FIRST | LAST | MIN | MAX | SUM |\n  RANGE | COUNT | STD.P | STD.S | VAR.P | VAR.S | TWA>\n  bucketDuration [BUCKETTIMESTAMP] [EMPTY]] FILTER <l=v | l!=v | l=\n  | l!= | l=(v1,v2,...) | l!=(v1,v2,...) [l=v | l!=v | l= | l!= |\n  l=(v1,v2,...) | l!=(v1,v2,...) ...]> [GROUPBY label REDUCE\n  reducer]`\n\n**Description:** Query a range across multiple time-series by filters in reverse direction\n\n**Complexity:** O(n/m+k) where n = Number of data points, m = Chunk size (data points per chunk), k = Number of data points that are in the requested ranges\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.queryindex/\">TS.QUERYINDEX</a></strong> - Get all time series keys matching a filter list</summary>\n\n**Syntax:** `TS.QUERYINDEX <l=v | l!=v | l= | l!= | l=(v1,v2,...) |\n  l!=(v1,v2,...) [l=v | l!=v | l= | l!= | l=(v1,v2,...) |\n  l!=(v1,v2,...) ...]>`\n\n**Description:** Get all time series keys matching a filter list\n\n**Complexity:** O(n) where n is the number of time-series that match the filters\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.range/\">TS.RANGE</a></strong> - Query a range in forward direction</summary>\n\n**Syntax:** `TS.RANGE key fromTimestamp toTimestamp [LATEST]\n  [FILTER BY TS Timestamp [Timestamp ...]] [FILTER BY VALUE min max]\n  [COUNT count] [[ALIGN value] AGGREGATION <AVG | FIRST | LAST | MIN\n  | MAX | SUM | RANGE | COUNT | STD.P | STD.S | VAR.P | VAR.S | TWA>\n  bucketDuration [BUCKETTIMESTAMP] [EMPTY]]`\n\n**Description:** Query a range in forward direction\n\n**Complexity:** O(n/m+k) where n = Number of data points, m = Chunk size (data points per chunk), k = Number of data points that are in the requested range\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.revrange/\">TS.REVRANGE</a></strong> - Query a range in reverse direction</summary>\n\n**Syntax:** `TS.REVRANGE key fromTimestamp toTimestamp [LATEST]\n  [FILTER BY TS Timestamp [Timestamp ...]] [FILTER BY VALUE min max]\n  [COUNT count] [[ALIGN value] AGGREGATION <AVG | FIRST | LAST | MIN\n  | MAX | SUM | RANGE | COUNT | STD.P | STD.S | VAR.P | VAR.S | TWA>\n  bucketDuration [BUCKETTIMESTAMP] [EMPTY]]`\n\n**Description:** Query a range in reverse direction\n\n**Complexity:** O(n/m+k) where n = Number of data points, m = Chunk size (data points per chunk), k = Number of data points that are in the requested range\n\n**Since:** 1.4.0\n\n</details>"},{"id":"vector-set-commands","title":"Vector set commands","role":"content","text":"Vector set commands operate on vector data structures for similarity search and range queries.\n\n<details>\n<summary><strong><a href=\"/commands/vadd/\">VADD</a></strong> - Add a new element to a vector set, or update its vector if it already exists.</summary>\n\n**Syntax:** `VADD key [REDUCE dim] (FP32 | VALUES num) vector element [CAS] [NOQUANT | Q8 | BIN]\n  [EF build-exploration-factor] [SETATTR attributes] [M numlinks]`\n\n**Description:** Add a new element to a vector set, or update its vector if it already exists.\n\n**Complexity:** O(log(N)) for each element added, where N is the number of elements in the vector set.\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vcard/\">VCARD</a></strong> - Return the number of elements in a vector set.</summary>\n\n**Syntax:** `VCARD key`\n\n**Description:** Return the number of elements in a vector set.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vdim/\">VDIM</a></strong> - Return the dimension of vectors in the vector set.</summary>\n\n**Syntax:** `VDIM key`\n\n**Description:** Return the dimension of vectors in the vector set.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vemb/\">VEMB</a></strong> - Return the vector associated with an element.</summary>\n\n**Syntax:** `VEMB key element [RAW]`\n\n**Description:** Return the vector associated with an element.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vgetattr/\">VGETATTR</a></strong> - Retrieve the JSON attributes of elements.</summary>\n\n**Syntax:** `VGETATTR key element`\n\n**Description:** Retrieve the JSON attributes of elements.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vinfo/\">VINFO</a></strong> - Return information about a vector set.</summary>\n\n**Syntax:** `VINFO key`\n\n**Description:** Return information about a vector set.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vismember/\">VISMEMBER</a></strong> - Check if an element exists in a vector set.</summary>\n\n**Syntax:** `VISMEMBER key element`\n\n**Description:** Check if an element exists in a vector set.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vlinks/\">VLINKS</a></strong> - Return the neighbors of an element at each layer in the HNSW graph.</summary>\n\n**Syntax:** `VLINKS key element [WITHSCORES]`\n\n**Description:** Return the neighbors of an element at each layer in the HNSW graph.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vrandmember/\">VRANDMEMBER</a></strong> - Return one or multiple random members from a vector set.</summary>\n\n**Syntax:** `VRANDMEMBER key [count]`\n\n**Description:** Return one or multiple random members from a vector set.\n\n**Complexity:** O(N) where N is the absolute value of the count argument.\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vrange/\">VRANGE</a></strong> - Return elements in a lexicographical range</summary>\n\n**Syntax:** `VRANGE key start end [count]`\n\n**Description:** Return elements in a lexicographical range\n\n**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)\n\n**Since:** 8.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vrem/\">VREM</a></strong> - Remove an element from a vector set.</summary>\n\n**Syntax:** `VREM key element`\n\n**Description:** Remove an element from a vector set.\n\n**Complexity:** O(log(N)) for each element removed, where N is the number of elements in the vector set.\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vsetattr/\">VSETATTR</a></strong> - Associate or remove the JSON attributes of elements.</summary>\n\n**Syntax:** `VSETATTR key element \"{ JSON obj }\"`\n\n**Description:** Associate or remove the JSON attributes of elements.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vsim/\">VSIM</a></strong> - Return elements by vector similarity.</summary>\n\n**Syntax:** `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]`\n\n**Description:** Return elements by vector similarity.\n\n**Complexity:** O(log(N)) where N is the number of elements in the vector set.\n\n**Since:** 8.0.0\n\n</details>"},{"id":"pubsub-commands","title":"Pub/Sub commands","role":"content","text":"Pub/Sub commands enable message passing between clients.\n\n<details>\n<summary><strong><a href=\"/commands/psubscribe/\">PSUBSCRIBE</a></strong> - Listens for messages published to channels that match one or more patterns.</summary>\n\n**Syntax:** `PSUBSCRIBE pattern [pattern ...]`\n\n**Description:** Listens for messages published to channels that match one or more patterns.\n\n**Complexity:** O(N) where N is the number of patterns to subscribe to.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/publish/\">PUBLISH</a></strong> - Posts a message to a channel.</summary>\n\n**Syntax:** `PUBLISH channel message`\n\n**Description:** Posts a message to a channel.\n\n**Complexity:** O(N+M) where N is the number of clients subscribed to the receiving channel and M is the total number of subscribed patterns (by any client).\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-channels/\">PUBSUB CHANNELS</a></strong> - Returns the active channels.</summary>\n\n**Syntax:** `PUBSUB CHANNELS [pattern]`\n\n**Description:** Returns the active channels.\n\n**Complexity:** O(N) where N is the number of active channels, and assuming constant time pattern matching (relatively short channels and patterns)\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-numpat/\">PUBSUB NUMPAT</a></strong> - Returns a count of unique pattern subscriptions.</summary>\n\n**Syntax:** `PUBSUB NUMPAT`\n\n**Description:** Returns a count of unique pattern subscriptions.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-numsub/\">PUBSUB NUMSUB</a></strong> - Returns a count of subscribers to channels.</summary>\n\n**Syntax:** `PUBSUB NUMSUB [channel [channel ...]]`\n\n**Description:** Returns a count of subscribers to channels.\n\n**Complexity:** O(N) for the NUMSUB subcommand, where N is the number of requested channels\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-shardchannels/\">PUBSUB SHARDCHANNELS</a></strong> - Returns the active shard channels.</summary>\n\n**Syntax:** `PUBSUB SHARDCHANNELS [pattern]`\n\n**Description:** Returns the active shard channels.\n\n**Complexity:** O(N) where N is the number of active shard channels, and assuming constant time pattern matching (relatively short shard channels).\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-shardnumsub/\">PUBSUB SHARDNUMSUB</a></strong> - Returns the count of subscribers of shard channels.</summary>\n\n**Syntax:** `PUBSUB SHARDNUMSUB [shardchannel [shardchannel ...]]`\n\n**Description:** Returns the count of subscribers of shard channels.\n\n**Complexity:** O(N) for the SHARDNUMSUB subcommand, where N is the number of requested shard channels\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/punsubscribe/\">PUNSUBSCRIBE</a></strong> - Stops listening to messages published to channels that match one or more patterns.</summary>\n\n**Syntax:** `PUNSUBSCRIBE [pattern [pattern ...]]`\n\n**Description:** Stops listening to messages published to channels that match one or more patterns.\n\n**Complexity:** O(N) where N is the number of patterns to unsubscribe.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/spublish/\">SPUBLISH</a></strong> - Post a message to a shard channel</summary>\n\n**Syntax:** `SPUBLISH shardchannel message`\n\n**Description:** Post a message to a shard channel\n\n**Complexity:** O(N) where N is the number of clients subscribed to the receiving shard channel.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ssubscribe/\">SSUBSCRIBE</a></strong> - Listens for messages published to shard channels.</summary>\n\n**Syntax:** `SSUBSCRIBE shardchannel [shardchannel ...]`\n\n**Description:** Listens for messages published to shard channels.\n\n**Complexity:** O(N) where N is the number of shard channels to subscribe to.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/subscribe/\">SUBSCRIBE</a></strong> - Listens for messages published to channels.</summary>\n\n**Syntax:** `SUBSCRIBE channel [channel ...]`\n\n**Description:** Listens for messages published to channels.\n\n**Complexity:** O(N) where N is the number of channels to subscribe to.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sunsubscribe/\">SUNSUBSCRIBE</a></strong> - Stops listening to messages posted to shard channels.</summary>\n\n**Syntax:** `SUNSUBSCRIBE [shardchannel [shardchannel ...]]`\n\n**Description:** Stops listening to messages posted to shard channels.\n\n**Complexity:** O(N) where N is the number of shard channels to unsubscribe.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/unsubscribe/\">UNSUBSCRIBE</a></strong> - Stops listening to messages posted to channels.</summary>\n\n**Syntax:** `UNSUBSCRIBE [channel [channel ...]]`\n\n**Description:** Stops listening to messages posted to channels.\n\n**Complexity:** O(N) where N is the number of channels to unsubscribe.\n\n**Since:** 2.0.0\n\n</details>"},{"id":"transaction-commands","title":"Transaction commands","role":"content","text":"Transaction commands enable atomic execution of command groups.\n\n<details>\n<summary><strong><a href=\"/commands/discard/\">DISCARD</a></strong> - Discards a transaction.</summary>\n\n**Syntax:** `DISCARD`\n\n**Description:** Discards a transaction.\n\n**Complexity:** O(N), when N is the number of queued commands\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/exec/\">EXEC</a></strong> - Executes all commands in a transaction.</summary>\n\n**Syntax:** `EXEC`\n\n**Description:** Executes all commands in a transaction.\n\n**Complexity:** Depends on commands in the transaction\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/multi/\">MULTI</a></strong> - Starts a transaction.</summary>\n\n**Syntax:** `MULTI`\n\n**Description:** Starts a transaction.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/unwatch/\">UNWATCH</a></strong> - Forgets about watched keys of a transaction.</summary>\n\n**Syntax:** `UNWATCH`\n\n**Description:** Forgets about watched keys of a transaction.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/watch/\">WATCH</a></strong> - Monitors changes to keys to determine the execution of a transaction.</summary>\n\n**Syntax:** `WATCH key [key ...]`\n\n**Description:** Monitors changes to keys to determine the execution of a transaction.\n\n**Complexity:** O(1) for every key.\n\n**Since:** 2.2.0\n\n</details>"},{"id":"scripting-commands","title":"Scripting commands","role":"content","text":"Scripting commands enable server-side Lua script execution.\n\n<details>\n<summary><strong><a href=\"/commands/eval/\">EVAL</a></strong> - Executes a server-side Lua script.</summary>\n\n**Syntax:** `EVAL script numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Executes a server-side Lua script.\n\n**Complexity:** Depends on the script that is executed.\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/evalsha/\">EVALSHA</a></strong> - Executes a server-side Lua script by SHA1 digest.</summary>\n\n**Syntax:** `EVALSHA sha1 numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Executes a server-side Lua script by SHA1 digest.\n\n**Complexity:** Depends on the script that is executed.\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/evalsha_ro/\">EVALSHA_RO</a></strong> - Executes a read-only server-side Lua script by SHA1 digest.</summary>\n\n**Syntax:** `EVALSHA RO sha1 numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Executes a read-only server-side Lua script by SHA1 digest.\n\n**Complexity:** Depends on the script that is executed.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/eval_ro/\">EVAL_RO</a></strong> - Executes a read-only server-side Lua script.</summary>\n\n**Syntax:** `EVAL RO script numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Executes a read-only server-side Lua script.\n\n**Complexity:** Depends on the script that is executed.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/fcall/\">FCALL</a></strong> - Invokes a function.</summary>\n\n**Syntax:** `FCALL function numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Invokes a function.\n\n**Complexity:** Depends on the function that is executed.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/fcall_ro/\">FCALL_RO</a></strong> - Invokes a read-only function.</summary>\n\n**Syntax:** `FCALL RO function numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Invokes a read-only function.\n\n**Complexity:** Depends on the function that is executed.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-delete/\">FUNCTION DELETE</a></strong> - Deletes a library and its functions.</summary>\n\n**Syntax:** `FUNCTION DELETE library-name`\n\n**Description:** Deletes a library and its functions.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-dump/\">FUNCTION DUMP</a></strong> - Dumps all libraries into a serialized binary payload.</summary>\n\n**Syntax:** `FUNCTION DUMP`\n\n**Description:** Dumps all libraries into a serialized binary payload.\n\n**Complexity:** O(N) where N is the number of functions\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-flush/\">FUNCTION FLUSH</a></strong> - Deletes all libraries and functions.</summary>\n\n**Syntax:** `FUNCTION FLUSH [ASYNC | SYNC]`\n\n**Description:** Deletes all libraries and functions.\n\n**Complexity:** O(N) where N is the number of functions deleted\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-kill/\">FUNCTION KILL</a></strong> - Terminates a function during execution.</summary>\n\n**Syntax:** `FUNCTION KILL`\n\n**Description:** Terminates a function during execution.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-list/\">FUNCTION LIST</a></strong> - Returns information about all libraries.</summary>\n\n**Syntax:** `FUNCTION LIST [LIBRARYNAME library-name-pattern] [WITHCODE]`\n\n**Description:** Returns information about all libraries.\n\n**Complexity:** O(N) where N is the number of functions\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-load/\">FUNCTION LOAD</a></strong> - Creates a library.</summary>\n\n**Syntax:** `FUNCTION LOAD [REPLACE] function-code`\n\n**Description:** Creates a library.\n\n**Complexity:** O(1) (considering compilation time is redundant)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-restore/\">FUNCTION RESTORE</a></strong> - Restores all libraries from a payload.</summary>\n\n**Syntax:** `FUNCTION RESTORE serialized-value [FLUSH | APPEND | REPLACE]`\n\n**Description:** Restores all libraries from a payload.\n\n**Complexity:** O(N) where N is the number of functions on the payload\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-stats/\">FUNCTION STATS</a></strong> - Returns information about a function during execution.</summary>\n\n**Syntax:** `FUNCTION STATS`\n\n**Description:** Returns information about a function during execution.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-debug/\">SCRIPT DEBUG</a></strong> - Sets the debug mode of server-side Lua scripts.</summary>\n\n**Syntax:** `SCRIPT DEBUG <YES | SYNC | NO>`\n\n**Description:** Sets the debug mode of server-side Lua scripts.\n\n**Complexity:** O(1)\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-exists/\">SCRIPT EXISTS</a></strong> - Determines whether server-side Lua scripts exist in the script cache.</summary>\n\n**Syntax:** `SCRIPT EXISTS sha1 [sha1 ...]`\n\n**Description:** Determines whether server-side Lua scripts exist in the script cache.\n\n**Complexity:** O(N) with N being the number of scripts to check (so checking a single script is an O(1) operation).\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-flush/\">SCRIPT FLUSH</a></strong> - Removes all server-side Lua scripts from the script cache.</summary>\n\n**Syntax:** `SCRIPT FLUSH [ASYNC | SYNC]`\n\n**Description:** Removes all server-side Lua scripts from the script cache.\n\n**Complexity:** O(N) with N being the number of scripts in cache\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-kill/\">SCRIPT KILL</a></strong> - Terminates a server-side Lua script during execution.</summary>\n\n**Syntax:** `SCRIPT KILL`\n\n**Description:** Terminates a server-side Lua script during execution.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-load/\">SCRIPT LOAD</a></strong> - Loads a server-side Lua script to the script cache.</summary>\n\n**Syntax:** `SCRIPT LOAD script`\n\n**Description:** Loads a server-side Lua script to the script cache.\n\n**Complexity:** O(N) with N being the length in bytes of the script body.\n\n**Since:** 2.6.0\n\n</details>"},{"id":"connection-commands","title":"Connection commands","role":"content","text":"Connection commands manage client connections.\n\n<details>\n<summary><strong><a href=\"/commands/auth/\">AUTH</a></strong> - Authenticates the connection.</summary>\n\n**Syntax:** `AUTH [username] password`\n\n**Description:** Authenticates the connection.\n\n**Complexity:** O(N) where N is the number of passwords defined for the user\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-caching/\">CLIENT CACHING</a></strong> - Instructs the server whether to track the keys in the next request.</summary>\n\n**Syntax:** `CLIENT CACHING <YES | NO>`\n\n**Description:** Instructs the server whether to track the keys in the next request.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-getname/\">CLIENT GETNAME</a></strong> - Returns the name of the connection.</summary>\n\n**Syntax:** `CLIENT GETNAME`\n\n**Description:** Returns the name of the connection.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-getredir/\">CLIENT GETREDIR</a></strong> - Returns the client ID to which the connection's tracking notifications are redirected.</summary>\n\n**Syntax:** `CLIENT GETREDIR`\n\n**Description:** Returns the client ID to which the connection's tracking notifications are redirected.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-id/\">CLIENT ID</a></strong> - Returns the unique client ID of the connection.</summary>\n\n**Syntax:** `CLIENT ID`\n\n**Description:** Returns the unique client ID of the connection.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-info/\">CLIENT INFO</a></strong> - Returns information about the connection.</summary>\n\n**Syntax:** `CLIENT INFO`\n\n**Description:** Returns information about the connection.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-kill/\">CLIENT KILL</a></strong> - Terminates open connections.</summary>\n\n**Syntax:** `CLIENT KILL <ip:port | <[ID client-id] | [TYPE <NORMAL | MASTER |\n  SLAVE | REPLICA | PUBSUB>] | [USER username] | [ADDR ip:port] |\n  [LADDR ip:port] | [SKIPME <YES | NO>] | [MAXAGE maxage]\n  [[ID client-id] | [TYPE <NORMAL | MASTER | SLAVE | REPLICA |\n  PUBSUB>] | [USER username] | [ADDR ip:port] | [LADDR ip:port] |\n  [SKIPME <YES | NO>] | [MAXAGE maxage] ...]>>`\n\n**Description:** Terminates open connections.\n\n**Complexity:** O(N) where N is the number of client connections\n\n**Since:** 2.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-list/\">CLIENT LIST</a></strong> - Lists open connections.</summary>\n\n**Syntax:** `CLIENT LIST [TYPE <NORMAL | MASTER | REPLICA | PUBSUB>]\n  [ID client-id [client-id ...]]`\n\n**Description:** Lists open connections.\n\n**Complexity:** O(N) where N is the number of client connections\n\n**Since:** 2.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-no-evict/\">CLIENT NO-EVICT</a></strong> - Sets the client eviction mode of the connection.</summary>\n\n**Syntax:** `CLIENT NO-EVICT <ON | OFF>`\n\n**Description:** Sets the client eviction mode of the connection.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-no-touch/\">CLIENT NO-TOUCH</a></strong> - Controls whether commands sent by the client affect the LRU/LFU of accessed keys.</summary>\n\n**Syntax:** `CLIENT NO-TOUCH <ON | OFF>`\n\n**Description:** Controls whether commands sent by the client affect the LRU/LFU of accessed keys.\n\n**Complexity:** O(1)\n\n**Since:** 7.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-pause/\">CLIENT PAUSE</a></strong> - Suspends commands processing.</summary>\n\n**Syntax:** `CLIENT PAUSE timeout [WRITE | ALL]`\n\n**Description:** Suspends commands processing.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-reply/\">CLIENT REPLY</a></strong> - Instructs the server whether to reply to commands.</summary>\n\n**Syntax:** `CLIENT REPLY <ON | OFF | SKIP>`\n\n**Description:** Instructs the server whether to reply to commands.\n\n**Complexity:** O(1)\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-setinfo/\">CLIENT SETINFO</a></strong> - Sets information specific to the client or connection.</summary>\n\n**Syntax:** `CLIENT SETINFO <LIB-NAME libname | LIB-VER libver>`\n\n**Description:** Sets information specific to the client or connection.\n\n**Complexity:** O(1)\n\n**Since:** 7.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-setname/\">CLIENT SETNAME</a></strong> - Sets the connection name.</summary>\n\n**Syntax:** `CLIENT SETNAME connection-name`\n\n**Description:** Sets the connection name.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-tracking/\">CLIENT TRACKING</a></strong> - Controls server-assisted client-side caching for the connection.</summary>\n\n**Syntax:** `CLIENT TRACKING <ON | OFF> [REDIRECT client-id] [PREFIX prefix\n  [PREFIX prefix ...]] [BCAST] [OPTIN] [OPTOUT] [NOLOOP]`\n\n**Description:** Controls server-assisted client-side caching for the connection.\n\n**Complexity:** O(1). Some options may introduce additional complexity.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-trackinginfo/\">CLIENT TRACKINGINFO</a></strong> - Returns information about server-assisted client-side caching for the connection.</summary>\n\n**Syntax:** `CLIENT TRACKINGINFO`\n\n**Description:** Returns information about server-assisted client-side caching for the connection.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-unblock/\">CLIENT UNBLOCK</a></strong> - Unblocks a client blocked by a blocking command from a different connection.</summary>\n\n**Syntax:** `CLIENT UNBLOCK client-id [TIMEOUT | ERROR]`\n\n**Description:** Unblocks a client blocked by a blocking command from a different connection.\n\n**Complexity:** O(log N) where N is the number of client connections\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-unpause/\">CLIENT UNPAUSE</a></strong> - Resumes processing commands from paused clients.</summary>\n\n**Syntax:** `CLIENT UNPAUSE`\n\n**Description:** Resumes processing commands from paused clients.\n\n**Complexity:** O(N) Where N is the number of paused clients\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/echo/\">ECHO</a></strong> - Returns the given string.</summary>\n\n**Syntax:** `ECHO message`\n\n**Description:** Returns the given string.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hello/\">HELLO</a></strong> - Handshakes with the Redis server.</summary>\n\n**Syntax:** `HELLO [protover [AUTH username password] [SETNAME clientname]]`\n\n**Description:** Handshakes with the Redis server.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ping/\">PING</a></strong> - Returns the server's liveliness response.</summary>\n\n**Syntax:** `PING [message]`\n\n**Description:** Returns the server's liveliness response.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/quit/\">QUIT</a></strong> - Closes the connection.</summary>\n\n**Syntax:** `QUIT`\n\n**Description:** Closes the connection.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/reset/\">RESET</a></strong> - Resets the connection.</summary>\n\n**Syntax:** `RESET`\n\n**Description:** Resets the connection.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/select/\">SELECT</a></strong> - Changes the selected database.</summary>\n\n**Syntax:** `SELECT index`\n\n**Description:** Changes the selected database.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>"},{"id":"server-commands","title":"Server commands","role":"content","text":"Server commands provide server management and introspection.\n\n<details>\n<summary><strong><a href=\"/commands/acl-cat/\">ACL CAT</a></strong> - Lists the ACL categories, or the commands inside a category.</summary>\n\n**Syntax:** `ACL CAT [category]`\n\n**Description:** Lists the ACL categories, or the commands inside a category.\n\n**Complexity:** O(1) since the categories and commands are a fixed set.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-deluser/\">ACL DELUSER</a></strong> - Deletes ACL users, and terminates their connections.</summary>\n\n**Syntax:** `ACL DELUSER username [username ...]`\n\n**Description:** Deletes ACL users, and terminates their connections.\n\n**Complexity:** O(1) amortized time considering the typical user.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-dryrun/\">ACL DRYRUN</a></strong> - Simulates the execution of a command by a user, without executing the command.</summary>\n\n**Syntax:** `ACL DRYRUN username command [arg [arg ...]]`\n\n**Description:** Simulates the execution of a command by a user, without executing the command.\n\n**Complexity:** O(1).\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-genpass/\">ACL GENPASS</a></strong> - Generates a pseudorandom, secure password that can be used to identify ACL users.</summary>\n\n**Syntax:** `ACL GENPASS [bits]`\n\n**Description:** Generates a pseudorandom, secure password that can be used to identify ACL users.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-getuser/\">ACL GETUSER</a></strong> - Lists the ACL rules of a user.</summary>\n\n**Syntax:** `ACL GETUSER username`\n\n**Description:** Lists the ACL rules of a user.\n\n**Complexity:** O(N). Where N is the number of password, command and pattern rules that the user has.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-list/\">ACL LIST</a></strong> - Dumps the effective rules in ACL file format.</summary>\n\n**Syntax:** `ACL LIST`\n\n**Description:** Dumps the effective rules in ACL file format.\n\n**Complexity:** O(N). Where N is the number of configured users.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-load/\">ACL LOAD</a></strong> - Reloads the rules from the configured ACL file.</summary>\n\n**Syntax:** `ACL LOAD`\n\n**Description:** Reloads the rules from the configured ACL file.\n\n**Complexity:** O(N). Where N is the number of configured users.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-log/\">ACL LOG</a></strong> - Lists recent security events generated due to ACL rules.</summary>\n\n**Syntax:** `ACL LOG [count | RESET]`\n\n**Description:** Lists recent security events generated due to ACL rules.\n\n**Complexity:** O(N) with N being the number of entries shown.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-save/\">ACL SAVE</a></strong> - Saves the effective ACL rules in the configured ACL file.</summary>\n\n**Syntax:** `ACL SAVE`\n\n**Description:** Saves the effective ACL rules in the configured ACL file.\n\n**Complexity:** O(N). Where N is the number of configured users.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-setuser/\">ACL SETUSER</a></strong> - Creates and modifies an ACL user and its rules.</summary>\n\n**Syntax:** `ACL SETUSER username [rule [rule ...]]`\n\n**Description:** Creates and modifies an ACL user and its rules.\n\n**Complexity:** O(N). Where N is the number of rules provided.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-users/\">ACL USERS</a></strong> - Lists all ACL users.</summary>\n\n**Syntax:** `ACL USERS`\n\n**Description:** Lists all ACL users.\n\n**Complexity:** O(N). Where N is the number of configured users.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-whoami/\">ACL WHOAMI</a></strong> - Returns the authenticated username of the current connection.</summary>\n\n**Syntax:** `ACL WHOAMI`\n\n**Description:** Returns the authenticated username of the current connection.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bgrewriteaof/\">BGREWRITEAOF</a></strong> - Asynchronously rewrites the append-only file to disk.</summary>\n\n**Syntax:** `BGREWRITEAOF`\n\n**Description:** Asynchronously rewrites the append-only file to disk.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bgsave/\">BGSAVE</a></strong> - Asynchronously saves the database(s) to disk.</summary>\n\n**Syntax:** `BGSAVE [SCHEDULE]`\n\n**Description:** Asynchronously saves the database(s) to disk.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command/\">COMMAND</a></strong> - Returns detailed information about all commands.</summary>\n\n**Syntax:** `COMMAND`\n\n**Description:** Returns detailed information about all commands.\n\n**Complexity:** O(N) where N is the total number of Redis commands\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-count/\">COMMAND COUNT</a></strong> - Returns a count of commands.</summary>\n\n**Syntax:** `COMMAND COUNT`\n\n**Description:** Returns a count of commands.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-docs/\">COMMAND DOCS</a></strong> - Returns documentary information about one, multiple or all commands.</summary>\n\n**Syntax:** `COMMAND DOCS [command-name [command-name ...]]`\n\n**Description:** Returns documentary information about one, multiple or all commands.\n\n**Complexity:** O(N) where N is the number of commands to look up\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-getkeys/\">COMMAND GETKEYS</a></strong> - Extracts the key names from an arbitrary command.</summary>\n\n**Syntax:** `COMMAND GETKEYS command [arg [arg ...]]`\n\n**Description:** Extracts the key names from an arbitrary command.\n\n**Complexity:** O(N) where N is the number of arguments to the command\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-getkeysandflags/\">COMMAND GETKEYSANDFLAGS</a></strong> - Extracts the key names and access flags for an arbitrary command.</summary>\n\n**Syntax:** `COMMAND GETKEYSANDFLAGS command [arg [arg ...]]`\n\n**Description:** Extracts the key names and access flags for an arbitrary command.\n\n**Complexity:** O(N) where N is the number of arguments to the command\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-info/\">COMMAND INFO</a></strong> - Returns information about one, multiple or all commands.</summary>\n\n**Syntax:** `COMMAND INFO [command-name [command-name ...]]`\n\n**Description:** Returns information about one, multiple or all commands.\n\n**Complexity:** O(N) where N is the number of commands to look up\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-list/\">COMMAND LIST</a></strong> - Returns a list of command names.</summary>\n\n**Syntax:** `COMMAND LIST [FILTERBY <MODULE module-name | ACLCAT category |\n  PATTERN pattern>]`\n\n**Description:** Returns a list of command names.\n\n**Complexity:** O(N) where N is the total number of Redis commands\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/config-get/\">CONFIG GET</a></strong> - Returns the effective values of configuration parameters.</summary>\n\n**Syntax:** `CONFIG GET parameter [parameter ...]`\n\n**Description:** Returns the effective values of configuration parameters.\n\n**Complexity:** O(N) when N is the number of configuration parameters provided\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/config-resetstat/\">CONFIG RESETSTAT</a></strong> - Resets the server's statistics.</summary>\n\n**Syntax:** `CONFIG RESETSTAT`\n\n**Description:** Resets the server's statistics.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/config-rewrite/\">CONFIG REWRITE</a></strong> - Persists the effective configuration to file.</summary>\n\n**Syntax:** `CONFIG REWRITE`\n\n**Description:** Persists the effective configuration to file.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/config-set/\">CONFIG SET</a></strong> - Sets configuration parameters in-flight.</summary>\n\n**Syntax:** `CONFIG SET parameter value [parameter value ...]`\n\n**Description:** Sets configuration parameters in-flight.\n\n**Complexity:** O(N) when N is the number of configuration parameters provided\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/dbsize/\">DBSIZE</a></strong> - Returns the number of keys in the database.</summary>\n\n**Syntax:** `DBSIZE`\n\n**Description:** Returns the number of keys in the database.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/failover/\">FAILOVER</a></strong> - Starts a coordinated failover from a server to one of its replicas.</summary>\n\n**Syntax:** `FAILOVER [TO host port [FORCE]] [ABORT] [TIMEOUT milliseconds]`\n\n**Description:** Starts a coordinated failover from a server to one of its replicas.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/flushall/\">FLUSHALL</a></strong> - Removes all keys from all databases.</summary>\n\n**Syntax:** `FLUSHALL [ASYNC | SYNC]`\n\n**Description:** Removes all keys from all databases.\n\n**Complexity:** O(N) where N is the total number of keys in all databases\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/flushdb/\">FLUSHDB</a></strong> - Remove all keys from the current database.</summary>\n\n**Syntax:** `FLUSHDB [ASYNC | SYNC]`\n\n**Description:** Remove all keys from the current database.\n\n**Complexity:** O(N) where N is the number of keys in the selected database\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hotkeys/\">HOTKEYS</a></strong> - A container for hotkeys tracking commands. <span style=\"color: #e74c3c;\">⭐ New in 8.6</span></summary>\n\n**Syntax:** `HOTKEYS`\n\n**Description:** A container for hotkeys tracking commands.\n\n**Complexity:** Depends on subcommand.\n\n**Since:** 8.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hotkeys-get/\">HOTKEYS GET</a></strong> - Returns lists of top K hotkeys depending on metrics chosen in HOTKEYS START command. <span style=\"color: #e74c3c;\">⭐ New in 8.6</span></summary>\n\n**Syntax:** `HOTKEYS GET`\n\n**Description:** Returns lists of top K hotkeys depending on metrics chosen in HOTKEYS START command.\n\n**Complexity:** O(K) where K is the number of hotkeys returned.\n\n**Since:** 8.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hotkeys-reset/\">HOTKEYS RESET</a></strong> - Release the resources used for hotkey tracking. <span style=\"color: #e74c3c;\">⭐ New in 8.6</span></summary>\n\n**Syntax:** `HOTKEYS RESET`\n\n**Description:** Release the resources used for hotkey tracking.\n\n**Complexity:** O(1)\n\n**Since:** 8.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hotkeys-start/\">HOTKEYS START</a></strong> - Starts hotkeys tracking. <span style=\"color: #e74c3c;\">⭐ New in 8.6</span></summary>\n\n**Syntax:** `HOTKEYS START METRICS count [CPU] [NET] [COUNT k] [DURATION seconds] [SAMPLE ratio] [SLOTS count slot [slot ...]]`\n\n**Description:** Starts hotkeys tracking.\n\n**Complexity:** O(1)\n\n**Since:** 8.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hotkeys-stop/\">HOTKEYS STOP</a></strong> - Stops hotkeys tracking. <span style=\"color: #e74c3c;\">⭐ New in 8.6</span></summary>\n\n**Syntax:** `HOTKEYS STOP`\n\n**Description:** Stops hotkeys tracking.\n\n**Complexity:** O(1)\n\n**Since:** 8.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/info/\">INFO</a></strong> - Returns information and statistics about the server.</summary>\n\n**Syntax:** `INFO [section [section ...]]`\n\n**Description:** Returns information and statistics about the server.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lastsave/\">LASTSAVE</a></strong> - Returns the Unix timestamp of the last successful save to disk.</summary>\n\n**Syntax:** `LASTSAVE`\n\n**Description:** Returns the Unix timestamp of the last successful save to disk.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-doctor/\">LATENCY DOCTOR</a></strong> - Returns a human-readable latency analysis report.</summary>\n\n**Syntax:** `LATENCY DOCTOR`\n\n**Description:** Returns a human-readable latency analysis report.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-graph/\">LATENCY GRAPH</a></strong> - Returns a latency graph for an event.</summary>\n\n**Syntax:** `LATENCY GRAPH event`\n\n**Description:** Returns a latency graph for an event.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-histogram/\">LATENCY HISTOGRAM</a></strong> - Returns the cumulative distribution of latencies of a subset or all commands.</summary>\n\n**Syntax:** `LATENCY HISTOGRAM [command [command ...]]`\n\n**Description:** Returns the cumulative distribution of latencies of a subset or all commands.\n\n**Complexity:** O(N) where N is the number of commands with latency information being retrieved.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-history/\">LATENCY HISTORY</a></strong> - Returns timestamp-latency samples for an event.</summary>\n\n**Syntax:** `LATENCY HISTORY event`\n\n**Description:** Returns timestamp-latency samples for an event.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-latest/\">LATENCY LATEST</a></strong> - Returns the latest latency samples for all events.</summary>\n\n**Syntax:** `LATENCY LATEST`\n\n**Description:** Returns the latest latency samples for all events.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-reset/\">LATENCY RESET</a></strong> - Resets the latency data for one or more events.</summary>\n\n**Syntax:** `LATENCY RESET [event [event ...]]`\n\n**Description:** Resets the latency data for one or more events.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lolwut/\">LOLWUT</a></strong> - Displays computer art and the Redis version</summary>\n\n**Syntax:** `LOLWUT [VERSION version]`\n\n**Description:** Displays computer art and the Redis version\n\n**Complexity:** \n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-doctor/\">MEMORY DOCTOR</a></strong> - Outputs a memory problems report.</summary>\n\n**Syntax:** `MEMORY DOCTOR`\n\n**Description:** Outputs a memory problems report.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-malloc-stats/\">MEMORY MALLOC-STATS</a></strong> - Returns the allocator statistics.</summary>\n\n**Syntax:** `MEMORY MALLOC-STATS`\n\n**Description:** Returns the allocator statistics.\n\n**Complexity:** Depends on how much memory is allocated, could be slow\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-purge/\">MEMORY PURGE</a></strong> - Asks the allocator to release memory.</summary>\n\n**Syntax:** `MEMORY PURGE`\n\n**Description:** Asks the allocator to release memory.\n\n**Complexity:** Depends on how much memory is allocated, could be slow\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-stats/\">MEMORY STATS</a></strong> - Returns details about memory usage.</summary>\n\n**Syntax:** `MEMORY STATS`\n\n**Description:** Returns details about memory usage.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-usage/\">MEMORY USAGE</a></strong> - Estimates the memory usage of a key.</summary>\n\n**Syntax:** `MEMORY USAGE key [SAMPLES count]`\n\n**Description:** Estimates the memory usage of a key.\n\n**Complexity:** O(N) where N is the number of samples.\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/module-list/\">MODULE LIST</a></strong> - Returns all loaded modules.</summary>\n\n**Syntax:** `MODULE LIST`\n\n**Description:** Returns all loaded modules.\n\n**Complexity:** O(N) where N is the number of loaded modules.\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/module-load/\">MODULE LOAD</a></strong> - Loads a module.</summary>\n\n**Syntax:** `MODULE LOAD path [arg [arg ...]]`\n\n**Description:** Loads a module.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/module-loadex/\">MODULE LOADEX</a></strong> - Loads a module using extended parameters.</summary>\n\n**Syntax:** `MODULE LOADEX path [CONFIG name value [CONFIG name value ...]]\n  [ARGS args [args ...]]`\n\n**Description:** Loads a module using extended parameters.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/module-unload/\">MODULE UNLOAD</a></strong> - Unloads a module.</summary>\n\n**Syntax:** `MODULE UNLOAD name`\n\n**Description:** Unloads a module.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/monitor/\">MONITOR</a></strong> - Listens for all requests received by the server in real-time.</summary>\n\n**Syntax:** `MONITOR`\n\n**Description:** Listens for all requests received by the server in real-time.\n\n**Complexity:** \n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/psync/\">PSYNC</a></strong> - An internal command used in replication.</summary>\n\n**Syntax:** `PSYNC replicationid offset`\n\n**Description:** An internal command used in replication.\n\n**Complexity:** \n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/replconf/\">REPLCONF</a></strong> - An internal command for configuring the replication stream.</summary>\n\n**Syntax:** `REPLCONF`\n\n**Description:** An internal command for configuring the replication stream.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/replicaof/\">REPLICAOF</a></strong> - Configures a server as replica of another, or promotes it to a master.</summary>\n\n**Syntax:** `REPLICAOF <host port | NO ONE>`\n\n**Description:** Configures a server as replica of another, or promotes it to a master.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/restore-asking/\">RESTORE-ASKING</a></strong> - An internal command for migrating keys in a cluster.</summary>\n\n**Syntax:** `RESTORE-ASKING key ttl serialized-value [REPLACE] [ABSTTL]\n  [IDLETIME seconds] [FREQ frequency]`\n\n**Description:** An internal command for migrating keys in a cluster.\n\n**Complexity:** O(1) to create the new key and additional O(N*M) to reconstruct the serialized value, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1). However for sorted set values the complexity is O(N*M*log(N)) because inserting values into sorted sets is O(log(N)).\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/role/\">ROLE</a></strong> - Returns the replication role.</summary>\n\n**Syntax:** `ROLE`\n\n**Description:** Returns the replication role.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.12\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/save/\">SAVE</a></strong> - Synchronously saves the database(s) to disk.</summary>\n\n**Syntax:** `SAVE`\n\n**Description:** Synchronously saves the database(s) to disk.\n\n**Complexity:** O(N) where N is the total number of keys in all databases\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/shutdown/\">SHUTDOWN</a></strong> - Synchronously saves the database(s) to disk and shuts down the Redis server.</summary>\n\n**Syntax:** `SHUTDOWN [NOSAVE | SAVE] [NOW] [FORCE] [ABORT]`\n\n**Description:** Synchronously saves the database(s) to disk and shuts down the Redis server.\n\n**Complexity:** O(N) when saving, where N is the total number of keys in all databases when saving data, otherwise O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/slaveof/\">SLAVEOF</a></strong> - Sets a Redis server as a replica of another, or promotes it to being a master.</summary>\n\n**Syntax:** `SLAVEOF <host port | NO ONE>`\n\n**Description:** Sets a Redis server as a replica of another, or promotes it to being a master.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/slowlog-get/\">SLOWLOG GET</a></strong> - Returns the slow log's entries.</summary>\n\n**Syntax:** `SLOWLOG GET [count]`\n\n**Description:** Returns the slow log's entries.\n\n**Complexity:** O(N) where N is the number of entries returned\n\n**Since:** 2.2.12\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/slowlog-len/\">SLOWLOG LEN</a></strong> - Returns the number of entries in the slow log.</summary>\n\n**Syntax:** `SLOWLOG LEN`\n\n**Description:** Returns the number of entries in the slow log.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.12\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/slowlog-reset/\">SLOWLOG RESET</a></strong> - Clears all entries from the slow log.</summary>\n\n**Syntax:** `SLOWLOG RESET`\n\n**Description:** Clears all entries from the slow log.\n\n**Complexity:** O(N) where N is the number of entries in the slowlog\n\n**Since:** 2.2.12\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/swapdb/\">SWAPDB</a></strong> - Swaps two Redis databases.</summary>\n\n**Syntax:** `SWAPDB index1 index2`\n\n**Description:** Swaps two Redis databases.\n\n**Complexity:** O(N) where N is the count of clients watching or blocking on keys from both databases.\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sync/\">SYNC</a></strong> - An internal command used in replication.</summary>\n\n**Syntax:** `SYNC`\n\n**Description:** An internal command used in replication.\n\n**Complexity:** \n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/time/\">TIME</a></strong> - Returns the server time.</summary>\n\n**Syntax:** `TIME`\n\n**Description:** Returns the server time.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>"},{"id":"cluster-commands","title":"Cluster commands","role":"content","text":"Cluster commands manage Redis Cluster operations.\n\n<details>\n<summary><strong><a href=\"/commands/asking/\">ASKING</a></strong> - Signals that a cluster client is following an -ASK redirect.</summary>\n\n**Syntax:** `ASKING`\n\n**Description:** Signals that a cluster client is following an -ASK redirect.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-addslots/\">CLUSTER ADDSLOTS</a></strong> - Assigns new hash slots to a node.</summary>\n\n**Syntax:** `CLUSTER ADDSLOTS slot [slot ...]`\n\n**Description:** Assigns new hash slots to a node.\n\n**Complexity:** O(N) where N is the total number of hash slot arguments\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-addslotsrange/\">CLUSTER ADDSLOTSRANGE</a></strong> - Assigns new hash slot ranges to a node.</summary>\n\n**Syntax:** `CLUSTER ADDSLOTSRANGE start-slot end-slot [start-slot end-slot ...]`\n\n**Description:** Assigns new hash slot ranges to a node.\n\n**Complexity:** O(N) where N is the total number of the slots between the start slot and end slot arguments.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-bumpepoch/\">CLUSTER BUMPEPOCH</a></strong> - Advances the cluster config epoch.</summary>\n\n**Syntax:** `CLUSTER BUMPEPOCH`\n\n**Description:** Advances the cluster config epoch.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-count-failure-reports/\">CLUSTER COUNT-FAILURE-REPORTS</a></strong> - Returns the number of active failure reports active for a node.</summary>\n\n**Syntax:** `CLUSTER COUNT-FAILURE-REPORTS node-id`\n\n**Description:** Returns the number of active failure reports active for a node.\n\n**Complexity:** O(N) where N is the number of failure reports\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-countkeysinslot/\">CLUSTER COUNTKEYSINSLOT</a></strong> - Returns the number of keys in a hash slot.</summary>\n\n**Syntax:** `CLUSTER COUNTKEYSINSLOT slot`\n\n**Description:** Returns the number of keys in a hash slot.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-delslots/\">CLUSTER DELSLOTS</a></strong> - Sets hash slots as unbound for a node.</summary>\n\n**Syntax:** `CLUSTER DELSLOTS slot [slot ...]`\n\n**Description:** Sets hash slots as unbound for a node.\n\n**Complexity:** O(N) where N is the total number of hash slot arguments\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-delslotsrange/\">CLUSTER DELSLOTSRANGE</a></strong> - Sets hash slot ranges as unbound for a node.</summary>\n\n**Syntax:** `CLUSTER DELSLOTSRANGE start-slot end-slot [start-slot end-slot ...]`\n\n**Description:** Sets hash slot ranges as unbound for a node.\n\n**Complexity:** O(N) where N is the total number of the slots between the start slot and end slot arguments.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-failover/\">CLUSTER FAILOVER</a></strong> - Forces a replica to perform a manual failover of its master.</summary>\n\n**Syntax:** `CLUSTER FAILOVER [FORCE | TAKEOVER]`\n\n**Description:** Forces a replica to perform a manual failover of its master.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-flushslots/\">CLUSTER FLUSHSLOTS</a></strong> - Deletes all slots information from a node.</summary>\n\n**Syntax:** `CLUSTER FLUSHSLOTS`\n\n**Description:** Deletes all slots information from a node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-forget/\">CLUSTER FORGET</a></strong> - Removes a node from the nodes table.</summary>\n\n**Syntax:** `CLUSTER FORGET node-id`\n\n**Description:** Removes a node from the nodes table.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-getkeysinslot/\">CLUSTER GETKEYSINSLOT</a></strong> - Returns the key names in a hash slot.</summary>\n\n**Syntax:** `CLUSTER GETKEYSINSLOT slot count`\n\n**Description:** Returns the key names in a hash slot.\n\n**Complexity:** O(N) where N is the number of requested keys\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-info/\">CLUSTER INFO</a></strong> - Returns information about the state of a node.</summary>\n\n**Syntax:** `CLUSTER INFO`\n\n**Description:** Returns information about the state of a node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-keyslot/\">CLUSTER KEYSLOT</a></strong> - Returns the hash slot for a key.</summary>\n\n**Syntax:** `CLUSTER KEYSLOT key`\n\n**Description:** Returns the hash slot for a key.\n\n**Complexity:** O(N) where N is the number of bytes in the key\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-links/\">CLUSTER LINKS</a></strong> - Returns a list of all TCP links to and from peer nodes.</summary>\n\n**Syntax:** `CLUSTER LINKS`\n\n**Description:** Returns a list of all TCP links to and from peer nodes.\n\n**Complexity:** O(N) where N is the total number of Cluster nodes\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-meet/\">CLUSTER MEET</a></strong> - Forces a node to handshake with another node.</summary>\n\n**Syntax:** `CLUSTER MEET ip port [cluster-bus-port]`\n\n**Description:** Forces a node to handshake with another node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-migration/\">CLUSTER MIGRATION</a></strong> - Start, monitor, and cancel atomic slot migration tasks.</summary>\n\n**Syntax:** `CLUSTER MIGRATION <IMPORT start-slot end-slot\n  [start-slot end-slot ...] | CANCEL <ID task-id | ALL> |\n  STATUS <[ID task-id] | [ALL]>>`\n\n**Description:** Start, monitor, and cancel atomic slot migration tasks.\n\n**Complexity:** O(N) where N is the total number of the slots between the start slot and end slot arguments.\n\n**Since:** 8.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-myid/\">CLUSTER MYID</a></strong> - Returns the ID of a node.</summary>\n\n**Syntax:** `CLUSTER MYID`\n\n**Description:** Returns the ID of a node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-myshardid/\">CLUSTER MYSHARDID</a></strong> - Returns the shard ID of a node.</summary>\n\n**Syntax:** `CLUSTER MYSHARDID`\n\n**Description:** Returns the shard ID of a node.\n\n**Complexity:** O(1)\n\n**Since:** 7.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-nodes/\">CLUSTER NODES</a></strong> - Returns the cluster configuration for a node.</summary>\n\n**Syntax:** `CLUSTER NODES`\n\n**Description:** Returns the cluster configuration for a node.\n\n**Complexity:** O(N) where N is the total number of Cluster nodes\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-replicas/\">CLUSTER REPLICAS</a></strong> - Lists the replica nodes of a master node.</summary>\n\n**Syntax:** `CLUSTER REPLICAS node-id`\n\n**Description:** Lists the replica nodes of a master node.\n\n**Complexity:** O(N) where N is the number of replicas.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-replicate/\">CLUSTER REPLICATE</a></strong> - Configure a node as replica of a master node.</summary>\n\n**Syntax:** `CLUSTER REPLICATE node-id`\n\n**Description:** Configure a node as replica of a master node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-reset/\">CLUSTER RESET</a></strong> - Resets a node.</summary>\n\n**Syntax:** `CLUSTER RESET [HARD | SOFT]`\n\n**Description:** Resets a node.\n\n**Complexity:** O(N) where N is the number of known nodes. The command may execute a FLUSHALL as a side effect.\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-saveconfig/\">CLUSTER SAVECONFIG</a></strong> - Forces a node to save the cluster configuration to disk.</summary>\n\n**Syntax:** `CLUSTER SAVECONFIG`\n\n**Description:** Forces a node to save the cluster configuration to disk.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-set-config-epoch/\">CLUSTER SET-CONFIG-EPOCH</a></strong> - Sets the configuration epoch for a new node.</summary>\n\n**Syntax:** `CLUSTER SET-CONFIG-EPOCH config-epoch`\n\n**Description:** Sets the configuration epoch for a new node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-setslot/\">CLUSTER SETSLOT</a></strong> - Binds a hash slot to a node.</summary>\n\n**Syntax:** `CLUSTER SETSLOT slot <IMPORTING node-id | MIGRATING node-id |\n  NODE node-id | STABLE>`\n\n**Description:** Binds a hash slot to a node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-shards/\">CLUSTER SHARDS</a></strong> - Returns the mapping of cluster slots to shards.</summary>\n\n**Syntax:** `CLUSTER SHARDS`\n\n**Description:** Returns the mapping of cluster slots to shards.\n\n**Complexity:** O(N) where N is the total number of cluster nodes\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-slaves/\">CLUSTER SLAVES</a></strong> - Lists the replica nodes of a master node.</summary>\n\n**Syntax:** `CLUSTER SLAVES node-id`\n\n**Description:** Lists the replica nodes of a master node.\n\n**Complexity:** O(N) where N is the number of replicas.\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-slot-stats/\">CLUSTER SLOT-STATS</a></strong> - Return an array of slot usage statistics for slots assigned to the current node.</summary>\n\n**Syntax:** `CLUSTER SLOT-STATS <SLOTSRANGE start-slot end-slot | ORDERBY metric\n  [LIMIT limit] [ASC | DESC]>`\n\n**Description:** Return an array of slot usage statistics for slots assigned to the current node.\n\n**Complexity:** O(N) where N is the total number of slots based on arguments. O(N*log(N)) with ORDERBY subcommand.\n\n**Since:** 8.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-slots/\">CLUSTER SLOTS</a></strong> - Returns the mapping of cluster slots to nodes.</summary>\n\n**Syntax:** `CLUSTER SLOTS`\n\n**Description:** Returns the mapping of cluster slots to nodes.\n\n**Complexity:** O(N) where N is the total number of Cluster nodes\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/readonly/\">READONLY</a></strong> - Enables read-only queries for a connection to a Redis Cluster replica node.</summary>\n\n**Syntax:** `READONLY`\n\n**Description:** Enables read-only queries for a connection to a Redis Cluster replica node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/readwrite/\">READWRITE</a></strong> - Enables read-write queries for a connection to a Reids Cluster replica node.</summary>\n\n**Syntax:** `READWRITE`\n\n**Description:** Enables read-write queries for a connection to a Reids Cluster replica node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>"},{"id":"generic-commands","title":"Generic commands","role":"content","text":"Generic commands work across all data types.\n\n<details>\n<summary><strong><a href=\"/commands/copy/\">COPY</a></strong> - Copies the value of a key to a new key.</summary>\n\n**Syntax:** `COPY source destination [DB destination-db] [REPLACE]`\n\n**Description:** Copies the value of a key to a new key.\n\n**Complexity:** O(N) worst case for collections, where N is the number of nested items. O(1) for string values.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/del/\">DEL</a></strong> - Deletes one or more keys.</summary>\n\n**Syntax:** `DEL key [key ...]`\n\n**Description:** Deletes one or more keys.\n\n**Complexity:** O(N) where N is the number of keys that will be removed. When a key to remove holds a value other than a string, the individual complexity for this key is O(M) where M is the number of elements in the list, set, sorted set or hash. Removing a single key that holds a string value is O(1).\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/dump/\">DUMP</a></strong> - Returns a serialized representation of the value stored at a key.</summary>\n\n**Syntax:** `DUMP key`\n\n**Description:** Returns a serialized representation of the value stored at a key.\n\n**Complexity:** O(1) to access the key and additional O(N*M) to serialize it, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1).\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/exists/\">EXISTS</a></strong> - Determines whether one or more keys exist.</summary>\n\n**Syntax:** `EXISTS key [key ...]`\n\n**Description:** Determines whether one or more keys exist.\n\n**Complexity:** O(N) where N is the number of keys to check.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/expire/\">EXPIRE</a></strong> - Sets the expiration time of a key in seconds.</summary>\n\n**Syntax:** `EXPIRE key seconds [NX | XX | GT | LT]`\n\n**Description:** Sets the expiration time of a key in seconds.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/expireat/\">EXPIREAT</a></strong> - Sets the expiration time of a key to a Unix timestamp.</summary>\n\n**Syntax:** `EXPIREAT key unix-time-seconds [NX | XX | GT | LT]`\n\n**Description:** Sets the expiration time of a key to a Unix timestamp.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/expiretime/\">EXPIRETIME</a></strong> - Returns the expiration time of a key as a Unix timestamp.</summary>\n\n**Syntax:** `EXPIRETIME key`\n\n**Description:** Returns the expiration time of a key as a Unix timestamp.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/keys/\">KEYS</a></strong> - Returns all key names that match a pattern.</summary>\n\n**Syntax:** `KEYS pattern`\n\n**Description:** Returns all key names that match a pattern.\n\n**Complexity:** O(N) with N being the number of keys in the database, under the assumption that the key names in the database and the given pattern have limited length.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/migrate/\">MIGRATE</a></strong> - Atomically transfers a key from one Redis instance to another.</summary>\n\n**Syntax:** `MIGRATE host port <key | \"\"> destination-db timeout [COPY] [REPLACE]\n  [AUTH password | AUTH2 username password] [KEYS key [key ...]]`\n\n**Description:** Atomically transfers a key from one Redis instance to another.\n\n**Complexity:** This command actually executes a DUMP+DEL in the source instance, and a RESTORE in the target instance. See the pages of these commands for time complexity. Also an O(N) data transfer between the two instances is performed.\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/move/\">MOVE</a></strong> - Moves a key to another database.</summary>\n\n**Syntax:** `MOVE key db`\n\n**Description:** Moves a key to another database.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/object-encoding/\">OBJECT ENCODING</a></strong> - Returns the internal encoding of a Redis object.</summary>\n\n**Syntax:** `OBJECT ENCODING key`\n\n**Description:** Returns the internal encoding of a Redis object.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.3\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/object-freq/\">OBJECT FREQ</a></strong> - Returns the logarithmic access frequency counter of a Redis object.</summary>\n\n**Syntax:** `OBJECT FREQ key`\n\n**Description:** Returns the logarithmic access frequency counter of a Redis object.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/object-idletime/\">OBJECT IDLETIME</a></strong> - Returns the time since the last access to a Redis object.</summary>\n\n**Syntax:** `OBJECT IDLETIME key`\n\n**Description:** Returns the time since the last access to a Redis object.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.3\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/object-refcount/\">OBJECT REFCOUNT</a></strong> - Returns the reference count of a value of a key.</summary>\n\n**Syntax:** `OBJECT REFCOUNT key`\n\n**Description:** Returns the reference count of a value of a key.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.3\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/persist/\">PERSIST</a></strong> - Removes the expiration time of a key.</summary>\n\n**Syntax:** `PERSIST key`\n\n**Description:** Removes the expiration time of a key.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pexpire/\">PEXPIRE</a></strong> - Sets the expiration time of a key in milliseconds.</summary>\n\n**Syntax:** `PEXPIRE key milliseconds [NX | XX | GT | LT]`\n\n**Description:** Sets the expiration time of a key in milliseconds.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pexpireat/\">PEXPIREAT</a></strong> - Sets the expiration time of a key to a Unix milliseconds timestamp.</summary>\n\n**Syntax:** `PEXPIREAT key unix-time-milliseconds [NX | XX | GT | LT]`\n\n**Description:** Sets the expiration time of a key to a Unix milliseconds timestamp.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pexpiretime/\">PEXPIRETIME</a></strong> - Returns the expiration time of a key as a Unix milliseconds timestamp.</summary>\n\n**Syntax:** `PEXPIRETIME key`\n\n**Description:** Returns the expiration time of a key as a Unix milliseconds timestamp.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pttl/\">PTTL</a></strong> - Returns the expiration time in milliseconds of a key.</summary>\n\n**Syntax:** `PTTL key`\n\n**Description:** Returns the expiration time in milliseconds of a key.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/randomkey/\">RANDOMKEY</a></strong> - Returns a random key name from the database.</summary>\n\n**Syntax:** `RANDOMKEY`\n\n**Description:** Returns a random key name from the database.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rename/\">RENAME</a></strong> - Renames a key and overwrites the destination.</summary>\n\n**Syntax:** `RENAME key newkey`\n\n**Description:** Renames a key and overwrites the destination.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/renamenx/\">RENAMENX</a></strong> - Renames a key only when the target key name doesn't exist.</summary>\n\n**Syntax:** `RENAMENX key newkey`\n\n**Description:** Renames a key only when the target key name doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/restore/\">RESTORE</a></strong> - Creates a key from the serialized representation of a value.</summary>\n\n**Syntax:** `RESTORE key ttl serialized-value [REPLACE] [ABSTTL]\n  [IDLETIME seconds] [FREQ frequency]`\n\n**Description:** Creates a key from the serialized representation of a value.\n\n**Complexity:** O(1) to create the new key and additional O(N*M) to reconstruct the serialized value, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1). However for sorted set values the complexity is O(N*M*log(N)) because inserting values into sorted sets is O(log(N)).\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-6-2-commands/\">Redis 6.2 Commands Reference</a></strong> - Complete list of all Redis commands available in version 6.2, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 6.2, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-7-2-commands/\">Redis 7.2 Commands Reference</a></strong> - Complete list of all Redis commands available in version 7.2, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 7.2, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-7-4-commands/\">Redis 7.4 Commands Reference</a></strong> - Complete list of all Redis commands available in version 7.4, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 7.4, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-8-0-commands/\">Redis 8.0 Commands Reference</a></strong> - Complete list of all Redis commands available in version 8.0, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 8.0, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-8-2-commands/\">Redis 8.2 Commands Reference</a></strong> - Complete list of all Redis commands available in version 8.2, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 8.2, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-8-4-commands/\">Redis 8.4 Commands Reference</a></strong> - Complete list of all Redis commands available in version 8.4, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 8.4, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-8-6-commands/\">Redis 8.6 Commands Reference</a></strong> - Complete list of all Redis commands available in version 8.6, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 8.6, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/scan/\">SCAN</a></strong> - Iterates over the key names in the database.</summary>\n\n**Syntax:** `SCAN cursor [MATCH pattern] [COUNT count] [TYPE type]`\n\n**Description:** Iterates over the key names in the database.\n\n**Complexity:** O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sort/\">SORT</a></strong> - Sorts the elements in a list, a set, or a sorted set, optionally storing the result.</summary>\n\n**Syntax:** `SORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern\n  ...]] [ASC | DESC] [ALPHA] [STORE destination]`\n\n**Description:** Sorts the elements in a list, a set, or a sorted set, optionally storing the result.\n\n**Complexity:** O(N+M*log(M)) where N is the number of elements in the list or set to sort, and M the number of returned elements. When the elements are not sorted, complexity is O(N).\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sort_ro/\">SORT_RO</a></strong> - Returns the sorted elements of a list, a set, or a sorted set.</summary>\n\n**Syntax:** `SORT RO key [BY pattern] [LIMIT offset count] [GET pattern [GET\n  pattern ...]] [ASC | DESC] [ALPHA]`\n\n**Description:** Returns the sorted elements of a list, a set, or a sorted set.\n\n**Complexity:** O(N+M*log(M)) where N is the number of elements in the list or set to sort, and M the number of returned elements. When the elements are not sorted, complexity is O(N).\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/touch/\">TOUCH</a></strong> - Returns the number of existing keys out of those specified after updating the time they were last accessed.</summary>\n\n**Syntax:** `TOUCH key [key ...]`\n\n**Description:** Returns the number of existing keys out of those specified after updating the time they were last accessed.\n\n**Complexity:** O(N) where N is the number of keys that will be touched.\n\n**Since:** 3.2.1\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ttl/\">TTL</a></strong> - Returns the expiration time in seconds of a key.</summary>\n\n**Syntax:** `TTL key`\n\n**Description:** Returns the expiration time in seconds of a key.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/type/\">TYPE</a></strong> - Determines the type of value stored at a key.</summary>\n\n**Syntax:** `TYPE key`\n\n**Description:** Determines the type of value stored at a key.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/unlink/\">UNLINK</a></strong> - Asynchronously deletes one or more keys.</summary>\n\n**Syntax:** `UNLINK key [key ...]`\n\n**Description:** Asynchronously deletes one or more keys.\n\n**Complexity:** O(1) for each key removed regardless of its size. Then the command does O(N) work in a different thread in order to reclaim memory, where N is the number of allocations the deleted objects where composed of.\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/wait/\">WAIT</a></strong> - Blocks until the asynchronous replication of all preceding write commands sent by the connection is completed.</summary>\n\n**Syntax:** `WAIT numreplicas timeout`\n\n**Description:** Blocks until the asynchronous replication of all preceding write commands sent by the connection is completed.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/waitaof/\">WAITAOF</a></strong> - Blocks until all of the preceding write commands sent by the connection are written to the append-only file of the master and/or replicas.</summary>\n\n**Syntax:** `WAITAOF numlocal numreplicas timeout`\n\n**Description:** Blocks until all of the preceding write commands sent by the connection are written to the append-only file of the master and/or replicas.\n\n**Complexity:** O(1)\n\n**Since:** 7.2.0\n\n</details>"}],"examples":[]}
{"schema_version":2,"id":"commands/redis-8-8-commands","title":"Redis 8.8 Commands Reference","url":"https://redis.io/docs/latest/commands/redis-8-8-commands/","summary":"Complete list of all Redis commands available in version 8.8, organized by functional group","tags":["docs","develop","stack","oss","rs","rc","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"45598490bad32a715a15cf8a58c1e7c4228cb325e8d0c32d25e1b78a5a1a5728","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This page provides a comprehensive reference of all Redis commands available in Redis 8.8, organized by functional group. Each command includes its description and syntax in a collapsible section for easy navigation.\n\n> [!NOTE]\n> Redis 8.8 includes all commands from previous versions plus new commands introduced in 8.8. Commands marked with **⭐ New in 8.8** were added in this release."},{"id":"quick-navigation","title":"Quick Navigation","role":"content","text":"- [String commands](#string-commands)\n- [Hash commands](#hash-commands)\n- [List commands](#list-commands)\n- [Set commands](#set-commands)\n- [Sorted set commands](#sorted-set-commands)\n- [Stream commands](#stream-commands)\n- [Bitmap commands](#bitmap-commands)\n- [Array commands](#array-commands)\n- [HyperLogLog commands](#hyperloglog-commands)\n- [Geospatial commands](#geospatial-commands)\n- [JSON commands](#json-commands)\n- [Search commands](#search-commands)\n- [Time series commands](#time-series-commands)\n- [Vector set commands](#vector-set-commands)\n- [Pub/Sub commands](#pubsub-commands)\n- [Transaction commands](#transaction-commands)\n- [Scripting commands](#scripting-commands)\n- [Connection commands](#connection-commands)\n- [Server commands](#server-commands)\n- [Cluster commands](#cluster-commands)\n- [Generic commands](#generic-commands)\n\n---"},{"id":"string-commands","title":"String commands","role":"content","text":"String commands operate on string values, the most basic Redis data type.\n\n<details>\n<summary><strong><a href=\"/commands/append/\">APPEND</a></strong> - Appends a string to the value of a key. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `APPEND key value`\n\n**Description:** Appends a string to the value of a key. Creates the key if it doesn't exist.\n\n**Complexity:** O(1). The amortized time complexity is O(1) assuming the appended value is small and the already present value is of any size, since the dynamic string library used by Redis will double the free space available on every reallocation.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/decr/\">DECR</a></strong> - Decrements the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `DECR key`\n\n**Description:** Decrements the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/decrby/\">DECRBY</a></strong> - Decrements a number from the integer value of a key. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `DECRBY key decrement`\n\n**Description:** Decrements a number from the integer value of a key. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/delex/\">DELEX</a></strong> - Conditionally removes the specified key based on value or hash digest comparison.</summary>\n\n**Syntax:** `DELEX key [IFEQ ifeq-value | IFNE ifne-value | IFDEQ ifdeq-digest |\n  IFDNE ifdne-digest]`\n\n**Description:** Conditionally removes the specified key based on value or hash digest comparison.\n\n**Complexity:** O(1) for IFEQ/IFNE, O(N) for IFDEQ/IFDNE where N is the length of the string value.\n\n**Since:** 8.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/digest/\">DIGEST</a></strong> - Returns the hash digest of a string value as a hexadecimal string.</summary>\n\n**Syntax:** `DIGEST key`\n\n**Description:** Returns the hash digest of a string value as a hexadecimal string.\n\n**Complexity:** O(N) where N is the length of the string value.\n\n**Since:** 8.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/get/\">GET</a></strong> - Returns the string value of a key.</summary>\n\n**Syntax:** `GET key`\n\n**Description:** Returns the string value of a key.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getdel/\">GETDEL</a></strong> - Returns the string value of a key after deleting the key.</summary>\n\n**Syntax:** `GETDEL key`\n\n**Description:** Returns the string value of a key after deleting the key.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getex/\">GETEX</a></strong> - Returns the string value of a key after setting its expiration time.</summary>\n\n**Syntax:** `GETEX key [EX seconds | PX milliseconds | EXAT unix-time-seconds |\n  PXAT unix-time-milliseconds | PERSIST]`\n\n**Description:** Returns the string value of a key after setting its expiration time.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getrange/\">GETRANGE</a></strong> - Returns a substring of the string stored at a key.</summary>\n\n**Syntax:** `GETRANGE key start end`\n\n**Description:** Returns a substring of the string stored at a key.\n\n**Complexity:** O(N) where N is the length of the returned string. The complexity is ultimately determined by the returned length, but because creating a substring from an existing string is very cheap, it can be considered O(1) for small strings.\n\n**Since:** 2.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getset/\">GETSET</a></strong> - Returns the previous string value of a key after setting it to a new value.</summary>\n\n**Syntax:** `GETSET key value`\n\n**Description:** Returns the previous string value of a key after setting it to a new value.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/incr/\">INCR</a></strong> - Increments the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `INCR key`\n\n**Description:** Increments the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/incrby/\">INCRBY</a></strong> - Increments the integer value of a key by a number. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `INCRBY key increment`\n\n**Description:** Increments the integer value of a key by a number. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/incrbyfloat/\">INCRBYFLOAT</a></strong> - Increment the floating point value of a key by a number. Uses 0 as initial value if the key doesn't exist.</summary>\n\n**Syntax:** `INCRBYFLOAT key increment`\n\n**Description:** Increment the floating point value of a key by a number. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/increx/\">INCREX</a></strong> - Increments the numeric value of a key by a number and sets its expiration time. Uses 0 as initial value if the key doesn't exist. <span style=\"color: #e74c3c;\">⭐ New in 8.8</span></summary>\n\n**Syntax:** `INCREX key [BYFLOAT float | BYINT integer]\n [LBOUND lowerbound] [UBOUND upperbound] [OVERFLOW <FAIL | SAT | REJECT>]\n [EX seconds | PX milliseconds| EXAT unix-time-seconds | PXAT unix-time-milliseconds | PERSIST]\n [ENX]`\n\n**Description:** Increments the numeric value of a key by a number and sets its expiration time. Uses 0 as initial value if the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lcs/\">LCS</a></strong> - Finds the longest common substring.</summary>\n\n**Syntax:** `LCS key1 key2 [LEN] [IDX] [MINMATCHLEN min-match-len] [WITHMATCHLEN]`\n\n**Description:** Finds the longest common substring.\n\n**Complexity:** O(N*M) where N and M are the lengths of s1 and s2, respectively\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/mget/\">MGET</a></strong> - Atomically returns the string values of one or more keys.</summary>\n\n**Syntax:** `MGET key [key ...]`\n\n**Description:** Atomically returns the string values of one or more keys.\n\n**Complexity:** O(N) where N is the number of keys to retrieve.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/mset/\">MSET</a></strong> - Atomically creates or modifies the string values of one or more keys.</summary>\n\n**Syntax:** `MSET key value [key value ...]`\n\n**Description:** Atomically creates or modifies the string values of one or more keys.\n\n**Complexity:** O(N) where N is the number of keys to set.\n\n**Since:** 1.0.1\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/msetex/\">MSETEX</a></strong> - Atomically sets multiple string keys with a shared expiration in a single operation.</summary>\n\n**Syntax:** `MSETEX numkeys key value [key value ...] [NX | XX] [EX seconds |\n  PX milliseconds | EXAT unix-time-seconds |\n  PXAT unix-time-milliseconds | KEEPTTL]`\n\n**Description:** Atomically sets multiple string keys with a shared expiration in a single operation.\n\n**Complexity:** O(N) where N is the number of keys to set.\n\n**Since:** 8.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/msetnx/\">MSETNX</a></strong> - Atomically modifies the string values of one or more keys only when all keys don't exist.</summary>\n\n**Syntax:** `MSETNX key value [key value ...]`\n\n**Description:** Atomically modifies the string values of one or more keys only when all keys don't exist.\n\n**Complexity:** O(N) where N is the number of keys to set.\n\n**Since:** 1.0.1\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/psetex/\">PSETEX</a></strong> - Sets both string value and expiration time in milliseconds of a key. The key is created if it doesn't exist.</summary>\n\n**Syntax:** `PSETEX key milliseconds value`\n\n**Description:** Sets both string value and expiration time in milliseconds of a key. The key is created if it doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/set/\">SET</a></strong> - Sets the string value of a key, ignoring its type. The key is created if it doesn't exist.</summary>\n\n**Syntax:** `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]`\n\n**Description:** Sets the string value of a key, ignoring its type. The key is created if it doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/setex/\">SETEX</a></strong> - Sets the string value and expiration time of a key. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `SETEX key seconds value`\n\n**Description:** Sets the string value and expiration time of a key. Creates the key if it doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/setnx/\">SETNX</a></strong> - Set the string value of a key only when the key doesn't exist.</summary>\n\n**Syntax:** `SETNX key value`\n\n**Description:** Set the string value of a key only when the key doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/setrange/\">SETRANGE</a></strong> - Overwrites a part of a string value with another by an offset. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `SETRANGE key offset value`\n\n**Description:** Overwrites a part of a string value with another by an offset. Creates the key if it doesn't exist.\n\n**Complexity:** O(1), not counting the time taken to copy the new string in place. Usually, this string is very small so the amortized complexity is O(1). Otherwise, complexity is O(M) with M being the length of the value argument.\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/strlen/\">STRLEN</a></strong> - Returns the length of a string value.</summary>\n\n**Syntax:** `STRLEN key`\n\n**Description:** Returns the length of a string value.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/substr/\">SUBSTR</a></strong> - Returns a substring from a string value.</summary>\n\n**Syntax:** `SUBSTR key start end`\n\n**Description:** Returns a substring from a string value.\n\n**Complexity:** O(N) where N is the length of the returned string. The complexity is ultimately determined by the returned length, but because creating a substring from an existing string is very cheap, it can be considered O(1) for small strings.\n\n**Since:** 1.0.0\n\n</details>"},{"id":"hash-commands","title":"Hash commands","role":"content","text":"Hash commands operate on hash data structures, which map string fields to string values.\n\n<details>\n<summary><strong><a href=\"/commands/hdel/\">HDEL</a></strong> - Deletes one or more fields and their values from a hash. Deletes the hash if no fields remain.</summary>\n\n**Syntax:** `HDEL key field [field ...]`\n\n**Description:** Deletes one or more fields and their values from a hash. Deletes the hash if no fields remain.\n\n**Complexity:** O(N) where N is the number of fields to be removed.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hexists/\">HEXISTS</a></strong> - Determines whether a field exists in a hash.</summary>\n\n**Syntax:** `HEXISTS key field`\n\n**Description:** Determines whether a field exists in a hash.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hexpire/\">HEXPIRE</a></strong> - Set expiry for hash field using relative time to expire (seconds)</summary>\n\n**Syntax:** `HEXPIRE key seconds [NX | XX | GT | LT] FIELDS numfields field\n  [field ...]`\n\n**Description:** Set expiry for hash field using relative time to expire (seconds)\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hexpireat/\">HEXPIREAT</a></strong> - Set expiry for hash field using an absolute Unix timestamp (seconds)</summary>\n\n**Syntax:** `HEXPIREAT key unix-time-seconds [NX | XX | GT | LT] FIELDS numfields\n  field [field ...]`\n\n**Description:** Set expiry for hash field using an absolute Unix timestamp (seconds)\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hexpiretime/\">HEXPIRETIME</a></strong> - Returns the expiration time of a hash field as a Unix timestamp, in seconds.</summary>\n\n**Syntax:** `HEXPIRETIME key FIELDS numfields field [field ...]`\n\n**Description:** Returns the expiration time of a hash field as a Unix timestamp, in seconds.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hget/\">HGET</a></strong> - Returns the value of a field in a hash.</summary>\n\n**Syntax:** `HGET key field`\n\n**Description:** Returns the value of a field in a hash.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hgetall/\">HGETALL</a></strong> - Returns all fields and values in a hash.</summary>\n\n**Syntax:** `HGETALL key`\n\n**Description:** Returns all fields and values in a hash.\n\n**Complexity:** O(N) where N is the size of the hash.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hgetdel/\">HGETDEL</a></strong> - Returns the value of a field and deletes it from the hash.</summary>\n\n**Syntax:** `HGETDEL key FIELDS numfields field [field ...]`\n\n**Description:** Returns the value of a field and deletes it from the hash.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hgetex/\">HGETEX</a></strong> - Get the value of one or more fields of a given hash key, and optionally set their expiration.</summary>\n\n**Syntax:** `HGETEX key [EX seconds | PX milliseconds | EXAT unix-time-seconds |\n  PXAT unix-time-milliseconds | PERSIST] FIELDS numfields field\n  [field ...]`\n\n**Description:** Get the value of one or more fields of a given hash key, and optionally set their expiration.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hincrby/\">HINCRBY</a></strong> - Increments the integer value of a field in a hash by a number. Uses 0 as initial value if the field doesn't exist.</summary>\n\n**Syntax:** `HINCRBY key field increment`\n\n**Description:** Increments the integer value of a field in a hash by a number. Uses 0 as initial value if the field doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hincrbyfloat/\">HINCRBYFLOAT</a></strong> - Increments the floating point value of a field by a number. Uses 0 as initial value if the field doesn't exist.</summary>\n\n**Syntax:** `HINCRBYFLOAT key field increment`\n\n**Description:** Increments the floating point value of a field by a number. Uses 0 as initial value if the field doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hkeys/\">HKEYS</a></strong> - Returns all fields in a hash.</summary>\n\n**Syntax:** `HKEYS key`\n\n**Description:** Returns all fields in a hash.\n\n**Complexity:** O(N) where N is the size of the hash.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hlen/\">HLEN</a></strong> - Returns the number of fields in a hash.</summary>\n\n**Syntax:** `HLEN key`\n\n**Description:** Returns the number of fields in a hash.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hmget/\">HMGET</a></strong> - Returns the values of all fields in a hash.</summary>\n\n**Syntax:** `HMGET key field [field ...]`\n\n**Description:** Returns the values of all fields in a hash.\n\n**Complexity:** O(N) where N is the number of fields being requested.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hmset/\">HMSET</a></strong> - Sets the values of multiple fields.</summary>\n\n**Syntax:** `HMSET key field value [field value ...]`\n\n**Description:** Sets the values of multiple fields.\n\n**Complexity:** O(N) where N is the number of fields being set.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hpersist/\">HPERSIST</a></strong> - Removes the expiration time for each specified field</summary>\n\n**Syntax:** `HPERSIST key FIELDS numfields field [field ...]`\n\n**Description:** Removes the expiration time for each specified field\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hpexpire/\">HPEXPIRE</a></strong> - Set expiry for hash field using relative time to expire (milliseconds)</summary>\n\n**Syntax:** `HPEXPIRE key milliseconds [NX | XX | GT | LT] FIELDS numfields field\n  [field ...]`\n\n**Description:** Set expiry for hash field using relative time to expire (milliseconds)\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hpexpireat/\">HPEXPIREAT</a></strong> - Set expiry for hash field using an absolute Unix timestamp (milliseconds)</summary>\n\n**Syntax:** `HPEXPIREAT key unix-time-milliseconds [NX | XX | GT | LT]\n  FIELDS numfields field [field ...]`\n\n**Description:** Set expiry for hash field using an absolute Unix timestamp (milliseconds)\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hpexpiretime/\">HPEXPIRETIME</a></strong> - Returns the expiration time of a hash field as a Unix timestamp, in msec.</summary>\n\n**Syntax:** `HPEXPIRETIME key FIELDS numfields field [field ...]`\n\n**Description:** Returns the expiration time of a hash field as a Unix timestamp, in msec.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hpttl/\">HPTTL</a></strong> - Returns the TTL in milliseconds of a hash field.</summary>\n\n**Syntax:** `HPTTL key FIELDS numfields field [field ...]`\n\n**Description:** Returns the TTL in milliseconds of a hash field.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hrandfield/\">HRANDFIELD</a></strong> - Returns one or more random fields from a hash.</summary>\n\n**Syntax:** `HRANDFIELD key [count [WITHVALUES]]`\n\n**Description:** Returns one or more random fields from a hash.\n\n**Complexity:** O(N) where N is the number of fields returned\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hscan/\">HSCAN</a></strong> - Iterates over fields and values of a hash.</summary>\n\n**Syntax:** `HSCAN key cursor [MATCH pattern] [COUNT count] [NOVALUES]`\n\n**Description:** Iterates over fields and values of a hash.\n\n**Complexity:** O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hset/\">HSET</a></strong> - Creates or modifies the value of a field in a hash.</summary>\n\n**Syntax:** `HSET key field value [field value ...]`\n\n**Description:** Creates or modifies the value of a field in a hash.\n\n**Complexity:** O(1) for each field/value pair added, so O(N) to add N field/value pairs when the command is called with multiple field/value pairs.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hsetex/\">HSETEX</a></strong> - Set the value of one or more fields of a given hash key, and optionally set their expiration.</summary>\n\n**Syntax:** `HSETEX key [FNX | FXX] [EX seconds | PX milliseconds |\n  EXAT unix-time-seconds | PXAT unix-time-milliseconds | KEEPTTL]\n  FIELDS numfields field value [field value ...]`\n\n**Description:** Set the value of one or more fields of a given hash key, and optionally set their expiration.\n\n**Complexity:** O(N) where N is the number of fields being set.\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hsetnx/\">HSETNX</a></strong> - Sets the value of a field in a hash only when the field doesn't exist.</summary>\n\n**Syntax:** `HSETNX key field value`\n\n**Description:** Sets the value of a field in a hash only when the field doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hstrlen/\">HSTRLEN</a></strong> - Returns the length of the value of a field.</summary>\n\n**Syntax:** `HSTRLEN key field`\n\n**Description:** Returns the length of the value of a field.\n\n**Complexity:** O(1)\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/httl/\">HTTL</a></strong> - Returns the TTL in seconds of a hash field.</summary>\n\n**Syntax:** `HTTL key FIELDS numfields field [field ...]`\n\n**Description:** Returns the TTL in seconds of a hash field.\n\n**Complexity:** O(N) where N is the number of specified fields\n\n**Since:** 7.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hvals/\">HVALS</a></strong> - Returns all values in a hash.</summary>\n\n**Syntax:** `HVALS key`\n\n**Description:** Returns all values in a hash.\n\n**Complexity:** O(N) where N is the size of the hash.\n\n**Since:** 2.0.0\n\n</details>"},{"id":"list-commands","title":"List commands","role":"content","text":"List commands operate on lists of strings, ordered by insertion order.\n\n<details>\n<summary><strong><a href=\"/commands/blmove/\">BLMOVE</a></strong> - Pops an element from a list, pushes it to another list and returns it. Blocks until an element is available otherwise. Deletes the list if the last element was moved.</summary>\n\n**Syntax:** `BLMOVE source destination <LEFT | RIGHT> <LEFT | RIGHT> timeout`\n\n**Description:** Pops an element from a list, pushes it to another list and returns it. Blocks until an element is available otherwise. Deletes the list if the last element was moved.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/blmpop/\">BLMPOP</a></strong> - Pops the first element from one of multiple lists. Blocks until an element is available otherwise. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `BLMPOP timeout numkeys key [key ...] <LEFT | RIGHT> [COUNT count]`\n\n**Description:** Pops the first element from one of multiple lists. Blocks until an element is available otherwise. Deletes the list if the last element was popped.\n\n**Complexity:** O(N+M) where N is the number of provided keys and M is the number of elements returned.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/blpop/\">BLPOP</a></strong> - Removes and returns the first element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `BLPOP key [key ...] timeout`\n\n**Description:** Removes and returns the first element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.\n\n**Complexity:** O(N) where N is the number of provided keys.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/brpop/\">BRPOP</a></strong> - Removes and returns the last element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `BRPOP key [key ...] timeout`\n\n**Description:** Removes and returns the last element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.\n\n**Complexity:** O(N) where N is the number of provided keys.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/brpoplpush/\">BRPOPLPUSH</a></strong> - Pops an element from a list, pushes it to another list and returns it. Block until an element is available otherwise. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `BRPOPLPUSH source destination timeout`\n\n**Description:** Pops an element from a list, pushes it to another list and returns it. Block until an element is available otherwise. Deletes the list if the last element was popped.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lindex/\">LINDEX</a></strong> - Returns an element from a list by its index.</summary>\n\n**Syntax:** `LINDEX key index`\n\n**Description:** Returns an element from a list by its index.\n\n**Complexity:** O(N) where N is the number of elements to traverse to get to the element at index. This makes asking for the first or the last element of the list O(1).\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/linsert/\">LINSERT</a></strong> - Inserts an element before or after another element in a list.</summary>\n\n**Syntax:** `LINSERT key <BEFORE | AFTER> pivot element`\n\n**Description:** Inserts an element before or after another element in a list.\n\n**Complexity:** O(N) where N is the number of elements to traverse before seeing the value pivot. This means that inserting somewhere on the left end on the list (head) can be considered O(1) and inserting somewhere on the right end (tail) is O(N).\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/llen/\">LLEN</a></strong> - Returns the length of a list.</summary>\n\n**Syntax:** `LLEN key`\n\n**Description:** Returns the length of a list.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lmove/\">LMOVE</a></strong> - Returns an element after popping it from one list and pushing it to another. Deletes the list if the last element was moved.</summary>\n\n**Syntax:** `LMOVE source destination <LEFT | RIGHT> <LEFT | RIGHT>`\n\n**Description:** Returns an element after popping it from one list and pushing it to another. Deletes the list if the last element was moved.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lmpop/\">LMPOP</a></strong> - Returns multiple elements from a list after removing them. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `LMPOP numkeys key [key ...] <LEFT | RIGHT> [COUNT count]`\n\n**Description:** Returns multiple elements from a list after removing them. Deletes the list if the last element was popped.\n\n**Complexity:** O(N+M) where N is the number of provided keys and M is the number of elements returned.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lpop/\">LPOP</a></strong> - Returns the first elements in a list after removing it. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `LPOP key [count]`\n\n**Description:** Returns the first elements in a list after removing it. Deletes the list if the last element was popped.\n\n**Complexity:** O(N) where N is the number of elements returned\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lpos/\">LPOS</a></strong> - Returns the index of matching elements in a list.</summary>\n\n**Syntax:** `LPOS key element [RANK rank] [COUNT num-matches] [MAXLEN len]`\n\n**Description:** Returns the index of matching elements in a list.\n\n**Complexity:** O(N) where N is the number of elements in the list, for the average case. When searching for elements near the head or the tail of the list, or when the MAXLEN option is provided, the command may run in constant time.\n\n**Since:** 6.0.6\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lpush/\">LPUSH</a></strong> - Prepends one or more elements to a list. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `LPUSH key element [element ...]`\n\n**Description:** Prepends one or more elements to a list. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lpushx/\">LPUSHX</a></strong> - Prepends one or more elements to a list only when the list exists.</summary>\n\n**Syntax:** `LPUSHX key element [element ...]`\n\n**Description:** Prepends one or more elements to a list only when the list exists.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lrange/\">LRANGE</a></strong> - Returns a range of elements from a list.</summary>\n\n**Syntax:** `LRANGE key start stop`\n\n**Description:** Returns a range of elements from a list.\n\n**Complexity:** O(S+N) where S is the distance of start offset from HEAD for small lists, from nearest end (HEAD or TAIL) for large lists; and N is the number of elements in the specified range.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lrem/\">LREM</a></strong> - Removes elements from a list. Deletes the list if the last element was removed.</summary>\n\n**Syntax:** `LREM key count element`\n\n**Description:** Removes elements from a list. Deletes the list if the last element was removed.\n\n**Complexity:** O(N+M) where N is the length of the list and M is the number of elements removed.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lset/\">LSET</a></strong> - Sets the value of an element in a list by its index.</summary>\n\n**Syntax:** `LSET key index element`\n\n**Description:** Sets the value of an element in a list by its index.\n\n**Complexity:** O(N) where N is the length of the list. Setting either the first or the last element of the list is O(1).\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ltrim/\">LTRIM</a></strong> - Removes elements from both ends a list. Deletes the list if all elements were trimmed.</summary>\n\n**Syntax:** `LTRIM key start stop`\n\n**Description:** Removes elements from both ends a list. Deletes the list if all elements were trimmed.\n\n**Complexity:** O(N) where N is the number of elements to be removed by the operation.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rpop/\">RPOP</a></strong> - Returns and removes the last elements of a list. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `RPOP key [count]`\n\n**Description:** Returns and removes the last elements of a list. Deletes the list if the last element was popped.\n\n**Complexity:** O(N) where N is the number of elements returned\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rpoplpush/\">RPOPLPUSH</a></strong> - Returns the last element of a list after removing and pushing it to another list. Deletes the list if the last element was popped.</summary>\n\n**Syntax:** `RPOPLPUSH source destination`\n\n**Description:** Returns the last element of a list after removing and pushing it to another list. Deletes the list if the last element was popped.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rpush/\">RPUSH</a></strong> - Appends one or more elements to a list. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `RPUSH key element [element ...]`\n\n**Description:** Appends one or more elements to a list. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rpushx/\">RPUSHX</a></strong> - Appends an element to a list only when the list exists.</summary>\n\n**Syntax:** `RPUSHX key element [element ...]`\n\n**Description:** Appends an element to a list only when the list exists.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 2.2.0\n\n</details>"},{"id":"set-commands","title":"Set commands","role":"content","text":"Set commands operate on unordered collections of unique strings.\n\n<details>\n<summary><strong><a href=\"/commands/sadd/\">SADD</a></strong> - Adds one or more members to a set. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `SADD key member [member ...]`\n\n**Description:** Adds one or more members to a set. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/scard/\">SCARD</a></strong> - Returns the number of members in a set.</summary>\n\n**Syntax:** `SCARD key`\n\n**Description:** Returns the number of members in a set.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sdiff/\">SDIFF</a></strong> - Returns the difference of multiple sets.</summary>\n\n**Syntax:** `SDIFF key [key ...]`\n\n**Description:** Returns the difference of multiple sets.\n\n**Complexity:** O(N) where N is the total number of elements in all given sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sdiffstore/\">SDIFFSTORE</a></strong> - Stores the difference of multiple sets in a key.</summary>\n\n**Syntax:** `SDIFFSTORE destination key [key ...]`\n\n**Description:** Stores the difference of multiple sets in a key.\n\n**Complexity:** O(N) where N is the total number of elements in all given sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sinter/\">SINTER</a></strong> - Returns the intersect of multiple sets.</summary>\n\n**Syntax:** `SINTER key [key ...]`\n\n**Description:** Returns the intersect of multiple sets.\n\n**Complexity:** O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sintercard/\">SINTERCARD</a></strong> - Returns the number of members of the intersect of multiple sets.</summary>\n\n**Syntax:** `SINTERCARD numkeys key [key ...] [LIMIT limit]`\n\n**Description:** Returns the number of members of the intersect of multiple sets.\n\n**Complexity:** O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sinterstore/\">SINTERSTORE</a></strong> - Stores the intersect of multiple sets in a key.</summary>\n\n**Syntax:** `SINTERSTORE destination key [key ...]`\n\n**Description:** Stores the intersect of multiple sets in a key.\n\n**Complexity:** O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sismember/\">SISMEMBER</a></strong> - Determines whether a member belongs to a set.</summary>\n\n**Syntax:** `SISMEMBER key member`\n\n**Description:** Determines whether a member belongs to a set.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/smembers/\">SMEMBERS</a></strong> - Returns all members of a set.</summary>\n\n**Syntax:** `SMEMBERS key`\n\n**Description:** Returns all members of a set.\n\n**Complexity:** O(N) where N is the set cardinality.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/smismember/\">SMISMEMBER</a></strong> - Determines whether multiple members belong to a set.</summary>\n\n**Syntax:** `SMISMEMBER key member [member ...]`\n\n**Description:** Determines whether multiple members belong to a set.\n\n**Complexity:** O(N) where N is the number of elements being checked for membership\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/smove/\">SMOVE</a></strong> - Moves a member from one set to another.</summary>\n\n**Syntax:** `SMOVE source destination member`\n\n**Description:** Moves a member from one set to another.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/spop/\">SPOP</a></strong> - Returns one or more random members from a set after removing them. Deletes the set if the last member was popped.</summary>\n\n**Syntax:** `SPOP key [count]`\n\n**Description:** Returns one or more random members from a set after removing them. Deletes the set if the last member was popped.\n\n**Complexity:** Without the count argument O(1), otherwise O(N) where N is the value of the passed count.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/srandmember/\">SRANDMEMBER</a></strong> - Get one or multiple random members from a set</summary>\n\n**Syntax:** `SRANDMEMBER key [count]`\n\n**Description:** Get one or multiple random members from a set\n\n**Complexity:** Without the count argument O(1), otherwise O(N) where N is the absolute value of the passed count.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/srem/\">SREM</a></strong> - Removes one or more members from a set. Deletes the set if the last member was removed.</summary>\n\n**Syntax:** `SREM key member [member ...]`\n\n**Description:** Removes one or more members from a set. Deletes the set if the last member was removed.\n\n**Complexity:** O(N) where N is the number of members to be removed.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sscan/\">SSCAN</a></strong> - Iterates over members of a set.</summary>\n\n**Syntax:** `SSCAN key cursor [MATCH pattern] [COUNT count]`\n\n**Description:** Iterates over members of a set.\n\n**Complexity:** O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sunion/\">SUNION</a></strong> - Returns the union of multiple sets.</summary>\n\n**Syntax:** `SUNION key [key ...]`\n\n**Description:** Returns the union of multiple sets.\n\n**Complexity:** O(N) where N is the total number of elements in all given sets.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sunionstore/\">SUNIONSTORE</a></strong> - Stores the union of multiple sets in a key.</summary>\n\n**Syntax:** `SUNIONSTORE destination key [key ...]`\n\n**Description:** Stores the union of multiple sets in a key.\n\n**Complexity:** O(N) where N is the total number of elements in all given sets.\n\n**Since:** 1.0.0\n\n</details>"},{"id":"sorted-set-commands","title":"Sorted set commands","role":"content","text":"Sorted set commands operate on sets of unique strings ordered by a score.\n\n<details>\n<summary><strong><a href=\"/commands/bzmpop/\">BZMPOP</a></strong> - Removes and returns a member by score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.</summary>\n\n**Syntax:** `BZMPOP timeout numkeys key [key ...] <MIN | MAX> [COUNT count]`\n\n**Description:** Removes and returns a member by score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.\n\n**Complexity:** O(K) + O(M*log(N)) where K is the number of provided keys, N being the number of elements in the sorted set, and M being the number of elements popped.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bzpopmax/\">BZPOPMAX</a></strong> - Removes and returns the member with the highest score from one or more sorted sets. Blocks until a member available otherwise.  Deletes the sorted set if the last element was popped.</summary>\n\n**Syntax:** `BZPOPMAX key [key ...] timeout`\n\n**Description:** Removes and returns the member with the highest score from one or more sorted sets. Blocks until a member available otherwise.  Deletes the sorted set if the last element was popped.\n\n**Complexity:** O(log(N)) with N being the number of elements in the sorted set.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bzpopmin/\">BZPOPMIN</a></strong> - Removes and returns the member with the lowest score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.</summary>\n\n**Syntax:** `BZPOPMIN key [key ...] timeout`\n\n**Description:** Removes and returns the member with the lowest score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.\n\n**Complexity:** O(log(N)) with N being the number of elements in the sorted set.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zadd/\">ZADD</a></strong> - Adds one or more members to a sorted set, or updates their scores. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `ZADD key [NX | XX] [GT | LT] [CH] [INCR] score member [score member\n  ...]`\n\n**Description:** Adds one or more members to a sorted set, or updates their scores. Creates the key if it doesn't exist.\n\n**Complexity:** O(log(N)) for each item added, where N is the number of elements in the sorted set.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zcard/\">ZCARD</a></strong> - Returns the number of members in a sorted set.</summary>\n\n**Syntax:** `ZCARD key`\n\n**Description:** Returns the number of members in a sorted set.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zcount/\">ZCOUNT</a></strong> - Returns the count of members in a sorted set that have scores within a range.</summary>\n\n**Syntax:** `ZCOUNT key min max`\n\n**Description:** Returns the count of members in a sorted set that have scores within a range.\n\n**Complexity:** O(log(N)) with N being the number of elements in the sorted set.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zdiff/\">ZDIFF</a></strong> - Returns the difference between multiple sorted sets.</summary>\n\n**Syntax:** `ZDIFF numkeys key [key ...] [WITHSCORES]`\n\n**Description:** Returns the difference between multiple sorted sets.\n\n**Complexity:** O(L + (N-K)log(N)) worst case where L is the total number of elements in all the sets, N is the size of the first set, and K is the size of the result set.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zdiffstore/\">ZDIFFSTORE</a></strong> - Stores the difference of multiple sorted sets in a key.</summary>\n\n**Syntax:** `ZDIFFSTORE destination numkeys key [key ...]`\n\n**Description:** Stores the difference of multiple sorted sets in a key.\n\n**Complexity:** O(L + (N-K)log(N)) worst case where L is the total number of elements in all the sets, N is the size of the first set, and K is the size of the result set.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zincrby/\">ZINCRBY</a></strong> - Increments the score of a member in a sorted set.</summary>\n\n**Syntax:** `ZINCRBY key increment member`\n\n**Description:** Increments the score of a member in a sorted set.\n\n**Complexity:** O(log(N)) where N is the number of elements in the sorted set.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zinter/\">ZINTER</a></strong> - Returns the intersect of multiple sorted sets.</summary>\n\n**Syntax:** `ZINTER numkeys key [key ...] [WEIGHTS weight [weight ...]]\n  [AGGREGATE <SUM | MIN | MAX | COUNT>] [WITHSCORES]`\n\n**Description:** Returns the intersect of multiple sorted sets.\n\n**Complexity:** O(N*K)+O(M*log(M)) worst case with N being the smallest input sorted set, K being the number of input sorted sets and M being the number of elements in the resulting sorted set.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zintercard/\">ZINTERCARD</a></strong> - Returns the number of members of the intersect of multiple sorted sets.</summary>\n\n**Syntax:** `ZINTERCARD numkeys key [key ...] [LIMIT limit]`\n\n**Description:** Returns the number of members of the intersect of multiple sorted sets.\n\n**Complexity:** O(N*K) worst case with N being the smallest input sorted set, K being the number of input sorted sets.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zinterstore/\">ZINTERSTORE</a></strong> - Stores the intersect of multiple sorted sets in a key.</summary>\n\n**Syntax:** `ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight\n  [weight ...]] [AGGREGATE <SUM | MIN | MAX | COUNT>]`\n\n**Description:** Stores the intersect of multiple sorted sets in a key.\n\n**Complexity:** O(N*K)+O(M*log(M)) worst case with N being the smallest input sorted set, K being the number of input sorted sets and M being the number of elements in the resulting sorted set.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zlexcount/\">ZLEXCOUNT</a></strong> - Returns the number of members in a sorted set within a lexicographical range.</summary>\n\n**Syntax:** `ZLEXCOUNT key min max`\n\n**Description:** Returns the number of members in a sorted set within a lexicographical range.\n\n**Complexity:** O(log(N)) with N being the number of elements in the sorted set.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zmpop/\">ZMPOP</a></strong> - Returns the highest- or lowest-scoring members from one or more sorted sets after removing them. Deletes the sorted set if the last member was popped.</summary>\n\n**Syntax:** `ZMPOP numkeys key [key ...] <MIN | MAX> [COUNT count]`\n\n**Description:** Returns the highest- or lowest-scoring members from one or more sorted sets after removing them. Deletes the sorted set if the last member was popped.\n\n**Complexity:** O(K) + O(M*log(N)) where K is the number of provided keys, N being the number of elements in the sorted set, and M being the number of elements popped.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zmscore/\">ZMSCORE</a></strong> - Returns the score of one or more members in a sorted set.</summary>\n\n**Syntax:** `ZMSCORE key member [member ...]`\n\n**Description:** Returns the score of one or more members in a sorted set.\n\n**Complexity:** O(N) where N is the number of members being requested.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zpopmax/\">ZPOPMAX</a></strong> - Returns the highest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.</summary>\n\n**Syntax:** `ZPOPMAX key [count]`\n\n**Description:** Returns the highest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.\n\n**Complexity:** O(log(N)*M) with N being the number of elements in the sorted set, and M being the number of elements popped.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zpopmin/\">ZPOPMIN</a></strong> - Returns the lowest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.</summary>\n\n**Syntax:** `ZPOPMIN key [count]`\n\n**Description:** Returns the lowest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.\n\n**Complexity:** O(log(N)*M) with N being the number of elements in the sorted set, and M being the number of elements popped.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrandmember/\">ZRANDMEMBER</a></strong> - Returns one or more random members from a sorted set.</summary>\n\n**Syntax:** `ZRANDMEMBER key [count [WITHSCORES]]`\n\n**Description:** Returns one or more random members from a sorted set.\n\n**Complexity:** O(N) where N is the number of members returned\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrange/\">ZRANGE</a></strong> - Returns members in a sorted set within a range of indexes.</summary>\n\n**Syntax:** `ZRANGE key start stop [BYSCORE | BYLEX] [REV] [LIMIT offset count]\n  [WITHSCORES]`\n\n**Description:** Returns members in a sorted set within a range of indexes.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements returned.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrangebylex/\">ZRANGEBYLEX</a></strong> - Returns members in a sorted set within a lexicographical range.</summary>\n\n**Syntax:** `ZRANGEBYLEX key min max [LIMIT offset count]`\n\n**Description:** Returns members in a sorted set within a lexicographical range.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrangebyscore/\">ZRANGEBYSCORE</a></strong> - Returns members in a sorted set within a range of scores.</summary>\n\n**Syntax:** `ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]`\n\n**Description:** Returns members in a sorted set within a range of scores.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).\n\n**Since:** 1.0.5\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrangestore/\">ZRANGESTORE</a></strong> - Stores a range of members from sorted set in a key.</summary>\n\n**Syntax:** `ZRANGESTORE dst src min max [BYSCORE | BYLEX] [REV] [LIMIT offset\n  count]`\n\n**Description:** Stores a range of members from sorted set in a key.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements stored into the destination key.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrank/\">ZRANK</a></strong> - Returns the index of a member in a sorted set ordered by ascending scores.</summary>\n\n**Syntax:** `ZRANK key member [WITHSCORE]`\n\n**Description:** Returns the index of a member in a sorted set ordered by ascending scores.\n\n**Complexity:** O(log(N))\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrem/\">ZREM</a></strong> - Removes one or more members from a sorted set. Deletes the sorted set if all members were removed.</summary>\n\n**Syntax:** `ZREM key member [member ...]`\n\n**Description:** Removes one or more members from a sorted set. Deletes the sorted set if all members were removed.\n\n**Complexity:** O(M*log(N)) with N being the number of elements in the sorted set and M the number of elements to be removed.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zremrangebylex/\">ZREMRANGEBYLEX</a></strong> - Removes members in a sorted set within a lexicographical range. Deletes the sorted set if all members were removed.</summary>\n\n**Syntax:** `ZREMRANGEBYLEX key min max`\n\n**Description:** Removes members in a sorted set within a lexicographical range. Deletes the sorted set if all members were removed.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zremrangebyrank/\">ZREMRANGEBYRANK</a></strong> - Removes members in a sorted set within a range of indexes. Deletes the sorted set if all members were removed.</summary>\n\n**Syntax:** `ZREMRANGEBYRANK key start stop`\n\n**Description:** Removes members in a sorted set within a range of indexes. Deletes the sorted set if all members were removed.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zremrangebyscore/\">ZREMRANGEBYSCORE</a></strong> - Removes members in a sorted set within a range of scores. Deletes the sorted set if all members were removed.</summary>\n\n**Syntax:** `ZREMRANGEBYSCORE key min max`\n\n**Description:** Removes members in a sorted set within a range of scores. Deletes the sorted set if all members were removed.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrevrange/\">ZREVRANGE</a></strong> - Returns members in a sorted set within a range of indexes in reverse order.</summary>\n\n**Syntax:** `ZREVRANGE key start stop [WITHSCORES]`\n\n**Description:** Returns members in a sorted set within a range of indexes in reverse order.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements returned.\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrevrangebylex/\">ZREVRANGEBYLEX</a></strong> - Returns members in a sorted set within a lexicographical range in reverse order.</summary>\n\n**Syntax:** `ZREVRANGEBYLEX key max min [LIMIT offset count]`\n\n**Description:** Returns members in a sorted set within a lexicographical range in reverse order.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrevrangebyscore/\">ZREVRANGEBYSCORE</a></strong> - Returns members in a sorted set within a range of scores in reverse order.</summary>\n\n**Syntax:** `ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]`\n\n**Description:** Returns members in a sorted set within a range of scores in reverse order.\n\n**Complexity:** O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zrevrank/\">ZREVRANK</a></strong> - Returns the index of a member in a sorted set ordered by descending scores.</summary>\n\n**Syntax:** `ZREVRANK key member [WITHSCORE]`\n\n**Description:** Returns the index of a member in a sorted set ordered by descending scores.\n\n**Complexity:** O(log(N))\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zscan/\">ZSCAN</a></strong> - Iterates over members and scores of a sorted set.</summary>\n\n**Syntax:** `ZSCAN key cursor [MATCH pattern] [COUNT count]`\n\n**Description:** Iterates over members and scores of a sorted set.\n\n**Complexity:** O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zscore/\">ZSCORE</a></strong> - Returns the score of a member in a sorted set.</summary>\n\n**Syntax:** `ZSCORE key member`\n\n**Description:** Returns the score of a member in a sorted set.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zunion/\">ZUNION</a></strong> - Returns the union of multiple sorted sets.</summary>\n\n**Syntax:** `ZUNION numkeys key [key ...] [WEIGHTS weight [weight ...]]\n  [AGGREGATE <SUM | MIN | MAX | COUNT>] [WITHSCORES]`\n\n**Description:** Returns the union of multiple sorted sets.\n\n**Complexity:** O(N)+O(M*log(M)) with N being the sum of the sizes of the input sorted sets, and M being the number of elements in the resulting sorted set.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/zunionstore/\">ZUNIONSTORE</a></strong> - Stores the union of multiple sorted sets in a key.</summary>\n\n**Syntax:** `ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight\n  [weight ...]] [AGGREGATE <SUM | MIN | MAX | COUNT>]`\n\n**Description:** Stores the union of multiple sorted sets in a key.\n\n**Complexity:** O(N)+O(M log(M)) with N being the sum of the sizes of the input sorted sets, and M being the number of elements in the resulting sorted set.\n\n**Since:** 2.0.0\n\n</details>"},{"id":"stream-commands","title":"Stream commands","role":"content","text":"Stream commands operate on append-only log data structures.\n\n<details>\n<summary><strong><a href=\"/commands/xack/\">XACK</a></strong> - Returns the number of messages that were successfully acknowledged by the consumer group member of a stream.</summary>\n\n**Syntax:** `XACK key group id [id ...]`\n\n**Description:** Returns the number of messages that were successfully acknowledged by the consumer group member of a stream.\n\n**Complexity:** O(1) for each message ID processed.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xackdel/\">XACKDEL</a></strong> - Acknowledges and conditionally deletes one or multiple entries for a stream consumer group.</summary>\n\n**Syntax:** `XACKDEL key group [KEEPREF | DELREF | ACKED] IDS numids id [id ...]`\n\n**Description:** Acknowledges and conditionally deletes one or multiple entries for a stream consumer group.\n\n**Complexity:** O(1) for each entry ID processed.\n\n**Since:** 8.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xadd/\">XADD</a></strong> - Appends a new message to a stream. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `XADD key [NOMKSTREAM] [KEEPREF | DELREF | ACKED]\n  [IDMPAUTO producer-id | IDMP producer-id idempotent-id]\n  [<MAXLEN | MINID> [= | ~] threshold [LIMIT count]] <* | id>\n  field value [field value ...]`\n\n**Description:** Appends a new message to a stream. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) when adding a new entry, O(N) when trimming where N being the number of entries evicted.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xautoclaim/\">XAUTOCLAIM</a></strong> - Changes, or acquires, ownership of messages in a consumer group, as if the messages were delivered to as consumer group member.</summary>\n\n**Syntax:** `XAUTOCLAIM key group consumer min-idle-time start [COUNT count]\n  [JUSTID]`\n\n**Description:** Changes, or acquires, ownership of messages in a consumer group, as if the messages were delivered to as consumer group member.\n\n**Complexity:** O(1) if COUNT is small.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xcfgset/\">XCFGSET</a></strong> - Sets the IDMP configuration parameters for a stream.</summary>\n\n**Syntax:** `XCFGSET key [IDMP-DURATION idmp-duration]\n  [IDMP-MAXSIZE idmp-maxsize]`\n\n**Description:** Sets the IDMP configuration parameters for a stream.\n\n**Complexity:** O(1)\n\n**Since:** 8.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xclaim/\">XCLAIM</a></strong> - Changes, or acquires, ownership of a message in a consumer group, as if the message was delivered a consumer group member.</summary>\n\n**Syntax:** `XCLAIM key group consumer min-idle-time id [id ...] [IDLE ms]\n  [TIME unix-time-milliseconds] [RETRYCOUNT count] [FORCE] [JUSTID]\n  [LASTID lastid]`\n\n**Description:** Changes, or acquires, ownership of a message in a consumer group, as if the message was delivered a consumer group member.\n\n**Complexity:** O(log N) with N being the number of messages in the PEL of the consumer group.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xdel/\">XDEL</a></strong> - Returns the number of messages after removing them from a stream.</summary>\n\n**Syntax:** `XDEL key id [id ...]`\n\n**Description:** Returns the number of messages after removing them from a stream.\n\n**Complexity:** O(1) for each single item to delete in the stream, regardless of the stream size.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xdelex/\">XDELEX</a></strong> - Deletes one or multiple entries from the stream.</summary>\n\n**Syntax:** `XDELEX key [KEEPREF | DELREF | ACKED] IDS numids id [id ...]`\n\n**Description:** Deletes one or multiple entries from the stream.\n\n**Complexity:** O(1) for each single item to delete in the stream, regardless of the stream size.\n\n**Since:** 8.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-create/\">XGROUP CREATE</a></strong> - Creates a consumer group.</summary>\n\n**Syntax:** `XGROUP CREATE key group <id | $> [MKSTREAM]\n  [ENTRIESREAD entries-read]`\n\n**Description:** Creates a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-createconsumer/\">XGROUP CREATECONSUMER</a></strong> - Creates a consumer in a consumer group.</summary>\n\n**Syntax:** `XGROUP CREATECONSUMER key group consumer`\n\n**Description:** Creates a consumer in a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-delconsumer/\">XGROUP DELCONSUMER</a></strong> - Deletes a consumer from a consumer group.</summary>\n\n**Syntax:** `XGROUP DELCONSUMER key group consumer`\n\n**Description:** Deletes a consumer from a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-destroy/\">XGROUP DESTROY</a></strong> - Destroys a consumer group.</summary>\n\n**Syntax:** `XGROUP DESTROY key group`\n\n**Description:** Destroys a consumer group.\n\n**Complexity:** O(N) where N is the number of entries in the group's pending entries list (PEL).\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xgroup-setid/\">XGROUP SETID</a></strong> - Sets the last-delivered ID of a consumer group.</summary>\n\n**Syntax:** `XGROUP SETID key group <id | $> [ENTRIESREAD entries-read]`\n\n**Description:** Sets the last-delivered ID of a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xidmprecord/\">XIDMPRECORD</a></strong> - An internal command for setting IDMP metadata on an existing stream message. <span style=\"color: #e74c3c;\">⭐ New in 8.8</span></summary>\n\n**Syntax:** `XIDMPRECORD key pid iid stream-id`\n\n**Description:** An internal command for setting IDMP metadata on an existing stream message.\n\n**Complexity:** O(1)\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xinfo-consumers/\">XINFO CONSUMERS</a></strong> - Returns a list of the consumers in a consumer group.</summary>\n\n**Syntax:** `XINFO CONSUMERS key group`\n\n**Description:** Returns a list of the consumers in a consumer group.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xinfo-groups/\">XINFO GROUPS</a></strong> - Returns a list of the consumer groups of a stream.</summary>\n\n**Syntax:** `XINFO GROUPS key`\n\n**Description:** Returns a list of the consumer groups of a stream.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xinfo-stream/\">XINFO STREAM</a></strong> - Returns information about a stream.</summary>\n\n**Syntax:** `XINFO STREAM key [FULL [COUNT count]]`\n\n**Description:** Returns information about a stream.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xlen/\">XLEN</a></strong> - Return the number of messages in a stream.</summary>\n\n**Syntax:** `XLEN key`\n\n**Description:** Return the number of messages in a stream.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xnack/\">XNACK</a></strong> - Releases pending messages back to the group's PEL without acknowledging them, making them available for re-delivery. <span style=\"color: #e74c3c;\">⭐ New in 8.8</span></summary>\n\n**Syntax:** `XNACK key group <SILENT | FAIL | FATAL> IDS numids id [id ...]\n  [RETRYCOUNT count] [FORCE]`\n\n**Description:** Releases pending messages back to the group's PEL without acknowledging them, making them available for re-delivery.\n\n**Complexity:** O(1) for each message ID processed.\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xpending/\">XPENDING</a></strong> - Returns the information and entries from a stream consumer group's pending entries list.</summary>\n\n**Syntax:** `XPENDING key group [[IDLE min-idle-time] start end count [consumer]]`\n\n**Description:** Returns the information and entries from a stream consumer group's pending entries list.\n\n**Complexity:** O(N) with N being the number of elements returned, so asking for a small fixed number of entries per call is O(1). O(M), where M is the total number of entries scanned when used with the IDLE filter. When the command returns just the summary and the list of consumers is small, it runs in O(1) time; otherwise, an additional O(N) time for iterating every consumer.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xrange/\">XRANGE</a></strong> - Returns the messages from a stream within a range of IDs.</summary>\n\n**Syntax:** `XRANGE key start end [COUNT count]`\n\n**Description:** Returns the messages from a stream within a range of IDs.\n\n**Complexity:** O(N) with N being the number of elements being returned. If N is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1).\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xread/\">XREAD</a></strong> - Returns messages from multiple streams with IDs greater than the ones requested. Blocks until a message is available otherwise.</summary>\n\n**Syntax:** `XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] id\n  [id ...]`\n\n**Description:** Returns messages from multiple streams with IDs greater than the ones requested. Blocks until a message is available otherwise.\n\n**Complexity:** For each stream mentioned: O(M) with M being the number of elements returned. If M is constant (for example, always asking for the first 10 elements with COUNT), you can consider it O(1). On the other side when XREADGROUP blocks, XADD will pay the O(N) time in order to serve the N clients blocked on the stream getting new data.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xreadgroup/\">XREADGROUP</a></strong> - Returns new or historical messages from a stream for a consumer in a group. Blocks until a message is available otherwise.</summary>\n\n**Syntax:** `XREADGROUP GROUP group consumer [COUNT count] [BLOCK milliseconds]\n  [CLAIM min-idle-time] [NOACK] STREAMS key [key ...] id [id ...]`\n\n**Description:** Returns new or historical messages from a stream for a consumer in a group. Blocks until a message is available otherwise.\n\n**Complexity:** For each stream mentioned: O(M) with M being the number of elements returned. If M is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1). On the other side when XREADGROUP blocks, XADD will pay the O(N) time in order to serve the N clients blocked on the stream getting new data.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xrevrange/\">XREVRANGE</a></strong> - Returns the messages from a stream within a range of IDs in reverse order.</summary>\n\n**Syntax:** `XREVRANGE key end start [COUNT count]`\n\n**Description:** Returns the messages from a stream within a range of IDs in reverse order.\n\n**Complexity:** O(N) with N being the number of elements returned. If N is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1).\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xsetid/\">XSETID</a></strong> - An internal command for replicating stream values.</summary>\n\n**Syntax:** `XSETID key last-id [ENTRIESADDED entries-added]\n  [MAXDELETEDID max-deleted-id]`\n\n**Description:** An internal command for replicating stream values.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/xtrim/\">XTRIM</a></strong> - Deletes messages from the beginning of a stream.</summary>\n\n**Syntax:** `XTRIM key <MAXLEN | MINID> [= | ~] threshold [LIMIT count] [KEEPREF | DELREF | ACKED]`\n\n**Description:** Deletes messages from the beginning of a stream.\n\n**Complexity:** O(N), with N being the number of evicted entries. Constant times are very small however, since entries are organized in macro nodes containing multiple entries that can be released with a single deallocation.\n\n**Since:** 5.0.0\n\n</details>"},{"id":"bitmap-commands","title":"Bitmap commands","role":"content","text":"Bitmap commands operate on strings as arrays of bits.\n\n<details>\n<summary><strong><a href=\"/commands/bitcount/\">BITCOUNT</a></strong> - Counts the number of set bits (population counting) in a string.</summary>\n\n**Syntax:** `BITCOUNT key [start end [BYTE | BIT]]`\n\n**Description:** Counts the number of set bits (population counting) in a string.\n\n**Complexity:** O(N)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bitfield/\">BITFIELD</a></strong> - Performs arbitrary bitfield integer operations on strings.</summary>\n\n**Syntax:** `BITFIELD key [GET encoding offset | [OVERFLOW <WRAP | SAT | FAIL>]\n  <SET encoding offset value | INCRBY encoding offset increment>\n  [GET encoding offset | [OVERFLOW <WRAP | SAT | FAIL>]\n  <SET encoding offset value | INCRBY encoding offset increment>\n  ...]]`\n\n**Description:** Performs arbitrary bitfield integer operations on strings.\n\n**Complexity:** O(1) for each subcommand specified\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bitfield_ro/\">BITFIELD_RO</a></strong> - Performs arbitrary read-only bitfield integer operations on strings.</summary>\n\n**Syntax:** `BITFIELD RO key [GET encoding offset [GET encoding offset ...]]`\n\n**Description:** Performs arbitrary read-only bitfield integer operations on strings.\n\n**Complexity:** O(1) for each subcommand specified\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bitop/\">BITOP</a></strong> - Performs bitwise operations on multiple strings, and stores the result.</summary>\n\n**Syntax:** `BITOP <AND | OR | XOR | NOT | DIFF | DIFF1 | ANDOR | ONE> destkey key [key ...]`\n\n**Description:** Performs bitwise operations on multiple strings, and stores the result.\n\n**Complexity:** O(N)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bitpos/\">BITPOS</a></strong> - Finds the first set (1) or clear (0) bit in a string.</summary>\n\n**Syntax:** `BITPOS key bit [start [end [BYTE | BIT]]]`\n\n**Description:** Finds the first set (1) or clear (0) bit in a string.\n\n**Complexity:** O(N)\n\n**Since:** 2.8.7\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/getbit/\">GETBIT</a></strong> - Returns a bit value by offset.</summary>\n\n**Syntax:** `GETBIT key offset`\n\n**Description:** Returns a bit value by offset.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/setbit/\">SETBIT</a></strong> - Sets or clears the bit at offset of the string value. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `SETBIT key offset value`\n\n**Description:** Sets or clears the bit at offset of the string value. Creates the key if it doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>"},{"id":"array-commands","title":"Array commands","role":"content","text":"Array commands operate on arrays of strings.\n\n<details>\n<summary><strong><a href=\"/commands/arcount/\">ARCOUNT</a></strong> - Returns the number of non-empty elements in an array. <span style=\"color: #e74c3c;\">⭐ New in 8.8</span></summary>\n\n**Syntax:** `ARCOUNT key`\n\n**Description:** Returns the number of non-empty elements in an array.\n\n**Complexity:** O(1)\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ardel/\">ARDEL</a></strong> - Deletes elements at the specified indices in an array. <span style=\"color: #e74c3c;\">⭐ New in 8.8</span></summary>\n\n**Syntax:** `ARDEL key index [index ...]`\n\n**Description:** Deletes elements at the specified indices in an array.\n\n**Complexity:** O(N) where N is the number of indices to delete\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ardelrange/\">ARDELRANGE</a></strong> - Deletes elements in one or more ranges. <span style=\"color: #e74c3c;\">⭐ New in 8.8</span></summary>\n\n**Syntax:** `ARDELRANGE key start end [start end ...]`\n\n**Description:** Deletes elements in one or more ranges.\n\n**Complexity:** Proportional to the number of existing elements / slices touched, not to the numeric span of the requested ranges\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/arget/\">ARGET</a></strong> - Gets the value at an index in an array. <span style=\"color: #e74c3c;\">⭐ New in 8.8</span></summary>\n\n**Syntax:** `ARGET key index`\n\n**Description:** Gets the value at an index in an array.\n\n**Complexity:** O(1)\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/argetrange/\">ARGETRANGE</a></strong> - Gets values in a range of indices. <span style=\"color: #e74c3c;\">⭐ New in 8.8</span></summary>\n\n**Syntax:** `ARGETRANGE key start end`\n\n**Description:** Gets values in a range of indices.\n\n**Complexity:** O(N) where N is the range length\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/argrep/\">ARGREP</a></strong> - Searches array elements in a range using textual predicates. <span style=\"color: #e74c3c;\">⭐ New in 8.8</span></summary>\n\n**Syntax:** `ARGREP key start end\n  <EXACT string | MATCH string | GLOB pattern | RE pattern [...]>\n  [AND | OR | LIMIT limit | WITHVALUES | NOCASE [...]]`\n\n**Description:** Searches array elements in a range using textual predicates.\n\n**Complexity:** O(P * C) where P is the number of visited positions in touched slices and C is the cost of evaluating the predicates on one existing element.\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/arinfo/\">ARINFO</a></strong> - Returns metadata about an array. <span style=\"color: #e74c3c;\">⭐ New in 8.8</span></summary>\n\n**Syntax:** `ARINFO key [FULL]`\n\n**Description:** Returns metadata about an array.\n\n**Complexity:** O(1), or O(N) with FULL option where N is the number of slices.\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/arinsert/\">ARINSERT</a></strong> - Inserts one or more values at consecutive indices. <span style=\"color: #e74c3c;\">⭐ New in 8.8</span></summary>\n\n**Syntax:** `ARINSERT key value [value ...]`\n\n**Description:** Inserts one or more values at consecutive indices.\n\n**Complexity:** O(N) where N is the number of values\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/arlastitems/\">ARLASTITEMS</a></strong> - Returns the most recently inserted elements. <span style=\"color: #e74c3c;\">⭐ New in 8.8</span></summary>\n\n**Syntax:** `ARLASTITEMS key count [REV]`\n\n**Description:** Returns the most recently inserted elements.\n\n**Complexity:** O(N) where N is the count\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/arlen/\">ARLEN</a></strong> - Returns the length of an array (max index + 1). <span style=\"color: #e74c3c;\">⭐ New in 8.8</span></summary>\n\n**Syntax:** `ARLEN key`\n\n**Description:** Returns the length of an array (max index + 1).\n\n**Complexity:** O(1)\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/armget/\">ARMGET</a></strong> - Gets values at multiple indices in an array. <span style=\"color: #e74c3c;\">⭐ New in 8.8</span></summary>\n\n**Syntax:** `ARMGET key index [index ...]`\n\n**Description:** Gets values at multiple indices in an array.\n\n**Complexity:** O(N) where N is the number of indices\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/armset/\">ARMSET</a></strong> - Sets multiple index-value pairs in an array. <span style=\"color: #e74c3c;\">⭐ New in 8.8</span></summary>\n\n**Syntax:** `ARMSET key index value [index value ...]`\n\n**Description:** Sets multiple index-value pairs in an array.\n\n**Complexity:** O(N) where N is the number of pairs\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/arnext/\">ARNEXT</a></strong> - Returns the next index ARINSERT would use. <span style=\"color: #e74c3c;\">⭐ New in 8.8</span></summary>\n\n**Syntax:** `ARNEXT key`\n\n**Description:** Returns the next index ARINSERT would use.\n\n**Complexity:** O(1)\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/arop/\">AROP</a></strong> - Performs aggregate operations on array elements in a range. <span style=\"color: #e74c3c;\">⭐ New in 8.8</span></summary>\n\n**Syntax:** `AROP key start end <SUM | MIN | MAX | AND | OR | XOR | MATCH value |\n  USED>`\n\n**Description:** Performs aggregate operations on array elements in a range.\n\n**Complexity:** O(P) where P is visited positions in touched slices (dense scanned slots + sparse entries), with worst-case O(|end-start|+1) and typical case close to O(N), where N is the number of existing elements in range.\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/arring/\">ARRING</a></strong> - Inserts values into a ring buffer of specified size, wrapping and truncating as needed. <span style=\"color: #e74c3c;\">⭐ New in 8.8</span></summary>\n\n**Syntax:** `ARRING key size value [value ...]`\n\n**Description:** Inserts values into a ring buffer of specified size, wrapping and truncating as needed.\n\n**Complexity:** O(M) normally, O(N+M) on ring resize, where N is the maximum of the old and new ring size and M is the number of inserted values\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/arscan/\">ARSCAN</a></strong> - Iterates existing elements in a range, returning index-value pairs. <span style=\"color: #e74c3c;\">⭐ New in 8.8</span></summary>\n\n**Syntax:** `ARSCAN key start end [LIMIT limit]`\n\n**Description:** Iterates existing elements in a range, returning index-value pairs.\n\n**Complexity:** O(P) where P is visited positions in touched slices (dense scanned slots + sparse entries), with worst-case O(|end-start|+1) and typical case close to O(N), where N is the number of existing elements in range.\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/arseek/\">ARSEEK</a></strong> - Sets the ARINSERT / ARRING cursor to a specific index. <span style=\"color: #e74c3c;\">⭐ New in 8.8</span></summary>\n\n**Syntax:** `ARSEEK key index`\n\n**Description:** Sets the ARINSERT / ARRING cursor to a specific index.\n\n**Complexity:** O(1)\n\n**Since:** 8.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/arset/\">ARSET</a></strong> - Sets one or more contiguous values starting at an index in an array. <span style=\"color: #e74c3c;\">⭐ New in 8.8</span></summary>\n\n**Syntax:** `ARSET key index value [value ...]`\n\n**Description:** Sets one or more contiguous values starting at an index in an array.\n\n**Complexity:** O(N) where N is the number of values\n\n**Since:** 8.8.0\n\n</details>"},{"id":"hyperloglog-commands","title":"HyperLogLog commands","role":"content","text":"HyperLogLog commands provide probabilistic cardinality estimation.\n\n<details>\n<summary><strong><a href=\"/commands/pfadd/\">PFADD</a></strong> - Adds elements to a HyperLogLog key. Creates the key if it doesn't exist.</summary>\n\n**Syntax:** `PFADD key [element [element ...]]`\n\n**Description:** Adds elements to a HyperLogLog key. Creates the key if it doesn't exist.\n\n**Complexity:** O(1) to add every element.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pfcount/\">PFCOUNT</a></strong> - Returns the approximated cardinality of the set(s) observed by the HyperLogLog key(s).</summary>\n\n**Syntax:** `PFCOUNT key [key ...]`\n\n**Description:** Returns the approximated cardinality of the set(s) observed by the HyperLogLog key(s).\n\n**Complexity:** O(1) with a very small average constant time when called with a single key. O(N) with N being the number of keys, and much bigger constant times, when called with multiple keys.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pfdebug/\">PFDEBUG</a></strong> - Internal commands for debugging HyperLogLog values.</summary>\n\n**Syntax:** `PFDEBUG subcommand key`\n\n**Description:** Internal commands for debugging HyperLogLog values.\n\n**Complexity:** N/A\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pfmerge/\">PFMERGE</a></strong> - Merges one or more HyperLogLog values into a single key.</summary>\n\n**Syntax:** `PFMERGE destkey [sourcekey [sourcekey ...]]`\n\n**Description:** Merges one or more HyperLogLog values into a single key.\n\n**Complexity:** O(N) to merge N HyperLogLogs, but with high constant times.\n\n**Since:** 2.8.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pfselftest/\">PFSELFTEST</a></strong> - An internal command for testing HyperLogLog values.</summary>\n\n**Syntax:** `PFSELFTEST`\n\n**Description:** An internal command for testing HyperLogLog values.\n\n**Complexity:** N/A\n\n**Since:** 2.8.9\n\n</details>"},{"id":"geospatial-commands","title":"Geospatial commands","role":"content","text":"Geospatial commands operate on geographic coordinates.\n\n<details>\n<summary><strong><a href=\"/commands/geoadd/\">GEOADD</a></strong> - Adds one or more members to a geospatial index. The key is created if it doesn't exist.</summary>\n\n**Syntax:** `GEOADD key [NX | XX] [CH] longitude latitude member [longitude\n  latitude member ...]`\n\n**Description:** Adds one or more members to a geospatial index. The key is created if it doesn't exist.\n\n**Complexity:** O(log(N)) for each item added, where N is the number of elements in the sorted set.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geodist/\">GEODIST</a></strong> - Returns the distance between two members of a geospatial index.</summary>\n\n**Syntax:** `GEODIST key member1 member2 [M | KM | FT | MI]`\n\n**Description:** Returns the distance between two members of a geospatial index.\n\n**Complexity:** O(1)\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geohash/\">GEOHASH</a></strong> - Returns members from a geospatial index as geohash strings.</summary>\n\n**Syntax:** `GEOHASH key [member [member ...]]`\n\n**Description:** Returns members from a geospatial index as geohash strings.\n\n**Complexity:** O(1) for each member requested.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geopos/\">GEOPOS</a></strong> - Returns the longitude and latitude of members from a geospatial index.</summary>\n\n**Syntax:** `GEOPOS key [member [member ...]]`\n\n**Description:** Returns the longitude and latitude of members from a geospatial index.\n\n**Complexity:** O(1) for each member requested.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/georadius/\">GEORADIUS</a></strong> - Queries a geospatial index for members within a distance from a coordinate, optionally stores the result.</summary>\n\n**Syntax:** `GEORADIUS key longitude latitude radius <M | KM | FT | MI>\n  [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC]\n  [STORE key | STOREDIST key]`\n\n**Description:** Queries a geospatial index for members within a distance from a coordinate, optionally stores the result.\n\n**Complexity:** O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/georadiusbymember/\">GEORADIUSBYMEMBER</a></strong> - Queries a geospatial index for members within a distance from a member, optionally stores the result.</summary>\n\n**Syntax:** `GEORADIUSBYMEMBER key member radius <M | KM | FT | MI> [WITHCOORD]\n  [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC] [STORE key\n  | STOREDIST key]`\n\n**Description:** Queries a geospatial index for members within a distance from a member, optionally stores the result.\n\n**Complexity:** O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/georadiusbymember_ro/\">GEORADIUSBYMEMBER_RO</a></strong> - Returns members from a geospatial index that are within a distance from a member.</summary>\n\n**Syntax:** `GEORADIUSBYMEMBER RO key member radius <M | KM | FT | MI>\n  [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC]`\n\n**Description:** Returns members from a geospatial index that are within a distance from a member.\n\n**Complexity:** O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.\n\n**Since:** 3.2.10\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/georadius_ro/\">GEORADIUS_RO</a></strong> - Returns members from a geospatial index that are within a distance from a coordinate.</summary>\n\n**Syntax:** `GEORADIUS RO key longitude latitude radius <M | KM | FT | MI>\n  [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC | DESC]`\n\n**Description:** Returns members from a geospatial index that are within a distance from a coordinate.\n\n**Complexity:** O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.\n\n**Since:** 3.2.10\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geosearch/\">GEOSEARCH</a></strong> - Queries a geospatial index for members inside an area of a box or a circle.</summary>\n\n**Syntax:** `GEOSEARCH key <FROMMEMBER member | FROMLONLAT longitude latitude>\n  <BYRADIUS radius <M | KM | FT | MI> | BYBOX width height <M | KM |\n  FT | MI>> [ASC | DESC] [COUNT count [ANY]] [WITHCOORD] [WITHDIST]\n  [WITHHASH]`\n\n**Description:** Queries a geospatial index for members inside an area of a box or a circle.\n\n**Complexity:** O(N+log(M)) where N is the number of elements in the grid-aligned bounding box area around the shape provided as the filter and M is the number of items inside the shape\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/geosearchstore/\">GEOSEARCHSTORE</a></strong> - Queries a geospatial index for members inside an area of a box or a circle, optionally stores the result.</summary>\n\n**Syntax:** `GEOSEARCHSTORE destination source <FROMMEMBER member |\n  FROMLONLAT longitude latitude> <BYRADIUS radius <M | KM | FT | MI>\n  | BYBOX width height <M | KM | FT | MI>> [ASC | DESC] [COUNT count\n  [ANY]] [STOREDIST]`\n\n**Description:** Queries a geospatial index for members inside an area of a box or a circle, optionally stores the result.\n\n**Complexity:** O(N+log(M)) where N is the number of elements in the grid-aligned bounding box area around the shape provided as the filter and M is the number of items inside the shape\n\n**Since:** 6.2.0\n\n</details>"},{"id":"json-commands","title":"JSON commands","role":"content","text":"JSON commands operate on JSON data structures.\n\n<details>\n<summary><strong><a href=\"/commands/json.arrappend/\">JSON.ARRAPPEND</a></strong> - Append one or more json values into the array at path after the last element in it.</summary>\n\n**Syntax:** `JSON.ARRAPPEND key path value [value ...]`\n\n**Description:** Append one or more json values into the array at path after the last element in it.\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrindex/\">JSON.ARRINDEX</a></strong> - Returns the index of the first occurrence of a JSON scalar value in the array at path</summary>\n\n**Syntax:** `JSON.ARRINDEX key path value [start [stop]]`\n\n**Description:** Returns the index of the first occurrence of a JSON scalar value in the array at path\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the array, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrinsert/\">JSON.ARRINSERT</a></strong> - Inserts the JSON scalar(s) value at the specified index in the array at path</summary>\n\n**Syntax:** `JSON.ARRINSERT key path index value [value ...]`\n\n**Description:** Inserts the JSON scalar(s) value at the specified index in the array at path\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the array, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrlen/\">JSON.ARRLEN</a></strong> - Returns the length of the array at path</summary>\n\n**Syntax:** `JSON.ARRLEN key [path]`\n\n**Description:** Returns the length of the array at path\n\n**Complexity:** O(1) where path is evaluated to a single value, O(N) where path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrpop/\">JSON.ARRPOP</a></strong> - Removes and returns the element at the specified index in the array at path</summary>\n\n**Syntax:** `JSON.ARRPOP key [path [index]]`\n\n**Description:** Removes and returns the element at the specified index in the array at path\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the array and the specified index is not the last element, O(1) when path is evaluated to a single value and the specified index is the last element, or O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.arrtrim/\">JSON.ARRTRIM</a></strong> - Trims the array at path to contain only the specified inclusive range of indices from start to stop</summary>\n\n**Syntax:** `JSON.ARRTRIM key path start stop`\n\n**Description:** Trims the array at path to contain only the specified inclusive range of indices from start to stop\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the array, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.clear/\">JSON.CLEAR</a></strong> - Clears all values from an array or an object and sets numeric values to `0`</summary>\n\n**Syntax:** `JSON.CLEAR key [path]`\n\n**Description:** Clears all values from an array or an object and sets numeric values to `0`\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the values, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.debug/\">JSON.DEBUG</a></strong> - Debugging container command</summary>\n\n**Syntax:** `JSON.DEBUG`\n\n**Description:** Debugging container command\n\n**Complexity:** N/A\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.debug-memory/\">JSON.DEBUG MEMORY</a></strong> - Reports the size in bytes of a key</summary>\n\n**Syntax:** `JSON.DEBUG MEMORY key [path]`\n\n**Description:** Reports the size in bytes of a key\n\n**Complexity:** O(N) when path is evaluated to a single value, where N is the size of the value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.del/\">JSON.DEL</a></strong> - Deletes a value</summary>\n\n**Syntax:** `JSON.DEL key [path]`\n\n**Description:** Deletes a value\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the deleted value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.forget/\">JSON.FORGET</a></strong> - Deletes a value</summary>\n\n**Syntax:** `JSON.FORGET key [path]`\n\n**Description:** Deletes a value\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the deleted value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.get/\">JSON.GET</a></strong> - Gets the value at one or more paths in JSON serialized form</summary>\n\n**Syntax:** `JSON.GET key [INDENT indent] [NEWLINE newline] [SPACE space] [path\n  [path ...]]`\n\n**Description:** Gets the value at one or more paths in JSON serialized form\n\n**Complexity:** O(N) when path is evaluated to a single value where N is the size of the value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.merge/\">JSON.MERGE</a></strong> - Merges a given JSON value into matching paths. Consequently, JSON values at matching paths are updated, deleted, or expanded with new children</summary>\n\n**Syntax:** `JSON.MERGE key path value`\n\n**Description:** Merges a given JSON value into matching paths. Consequently, JSON values at matching paths are updated, deleted, or expanded with new children\n\n**Complexity:** O(M+N) when path is evaluated to a single value where M is the size of the original value (if it exists) and N is the size of the new value, O(M+N) when path is evaluated to multiple values where M is the size of the key and N is the size of the new value * the number of original values in the key\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.mget/\">JSON.MGET</a></strong> - Returns the values at a path from one or more keys</summary>\n\n**Syntax:** `JSON.MGET key [key ...] path`\n\n**Description:** Returns the values at a path from one or more keys\n\n**Complexity:** O(M*N) when path is evaluated to a single value where M is the number of keys and N is the size of the value, O(N1+N2+...+Nm) when path is evaluated to multiple values where m is the number of keys and Ni is the size of the i-th key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.mset/\">JSON.MSET</a></strong> - Sets or updates the JSON value of one or more keys</summary>\n\n**Syntax:** `JSON.MSET key path value [key path value ...]`\n\n**Description:** Sets or updates the JSON value of one or more keys\n\n**Complexity:** O(K*(M+N)) where k is the number of keys in the command, when path is evaluated to a single value where M is the size of the original value (if it exists) and N is the size of the new value, or O(K*(M+N)) when path is evaluated to multiple values where M is the size of the key and N is the size of the new value * the number of original values in the key\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.numincrby/\">JSON.NUMINCRBY</a></strong> - Increments the numeric value at path by a value</summary>\n\n**Syntax:** `JSON.NUMINCRBY key path value`\n\n**Description:** Increments the numeric value at path by a value\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.nummultby/\">JSON.NUMMULTBY</a></strong> - Multiplies the numeric value at path by a value</summary>\n\n**Syntax:** `JSON.NUMMULTBY key path value`\n\n**Description:** Multiplies the numeric value at path by a value\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.objkeys/\">JSON.OBJKEYS</a></strong> - Returns the key names of JSON objects at the paths matching a given path expression</summary>\n\n**Syntax:** `JSON.OBJKEYS key [path]`\n\n**Description:** Returns the key names of JSON objects at the paths matching a given path expression\n\n**Complexity:** O(N) when path is evaluated to a single value, where N is the number of keys in the object, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.objlen/\">JSON.OBJLEN</a></strong> - Returns the number of keys in JSON objects at the paths matching a given path expression</summary>\n\n**Syntax:** `JSON.OBJLEN key [path]`\n\n**Description:** Returns the number of keys in JSON objects at the paths matching a given path expression\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.resp/\">JSON.RESP</a></strong> - Returns the JSON value at path in Redis Serialization Protocol (RESP)</summary>\n\n**Syntax:** `JSON.RESP key [path]`\n\n**Description:** Returns the JSON value at path in Redis Serialization Protocol (RESP)\n\n**Complexity:** O(N) when path is evaluated to a single value, where N is the size of the value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.set/\">JSON.SET</a></strong> - Sets or updates the JSON value at a path</summary>\n\n**Syntax:** `JSON.SET key path value [NX | XX] [FPHA <FP16 | BF16 | FP32 | FP64>]`\n\n**Description:** Sets or updates the JSON value at a path\n\n**Complexity:** O(M+N) when path is evaluated to a single value where M is the size of the original value (if it exists) and N is the size of the new value, O(M+N) when path is evaluated to multiple values where M is the size of the key and N is the size of the new value * the number of original values in the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.strappend/\">JSON.STRAPPEND</a></strong> - Appends a string to JSON strings at the paths matching a given path expression</summary>\n\n**Syntax:** `JSON.STRAPPEND key [path] value`\n\n**Description:** Appends a string to JSON strings at the paths matching a given path expression\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.strlen/\">JSON.STRLEN</a></strong> - Returns the length of JSON strings at the paths matching a given path expression</summary>\n\n**Syntax:** `JSON.STRLEN key [path]`\n\n**Description:** Returns the length of JSON strings at the paths matching a given path expression\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.toggle/\">JSON.TOGGLE</a></strong> - Toggles a boolean value</summary>\n\n**Syntax:** `JSON.TOGGLE key path`\n\n**Description:** Toggles a boolean value\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/json.type/\">JSON.TYPE</a></strong> - Returns the type of the JSON value at path</summary>\n\n**Syntax:** `JSON.TYPE key [path]`\n\n**Description:** Returns the type of the JSON value at path\n\n**Complexity:** O(1) when path is evaluated to a single value, O(N) when path is evaluated to multiple values, where N is the size of the key\n\n**Since:** 1.0.0\n\n</details>"},{"id":"search-commands","title":"Search commands","role":"content","text":"Search commands provide full-text search and secondary indexing.\n\n<details>\n<summary><strong><a href=\"/commands/ft.aggregate/\">FT.AGGREGATE</a></strong> - Run a search query on an index and perform aggregate transformations on the results</summary>\n\n**Syntax:** `FT.AGGREGATE index query [VERBATIM] [LOAD count field [field ...]]\n  [TIMEOUT timeout] [LOAD *] [GROUPBY nargs property [property ...]\n  [REDUCE function nargs arg [arg ...] [AS name] [REDUCE function\n  nargs arg [arg ...] [AS name] ...]] [GROUPBY nargs property\n  [property ...] [REDUCE function nargs arg [arg ...] [AS name]\n  [REDUCE function nargs arg [arg ...] [AS name] ...]] ...]]\n  [SORTBY nargs [property <ASC | DESC> [property <ASC | DESC> ...]]\n  [MAX num]] [APPLY expression AS name [APPLY expression AS name\n  ...]] [LIMIT offset num] [FILTER filter] [WITHCURSOR\n  [COUNT read size] [MAXIDLE idle time]] [PARAMS nargs name value\n  [name value ...]] [DIALECT dialect]`\n\n**Description:** Run a search query on an index and perform aggregate transformations on the results\n\n**Complexity:** O(1)\n\n**Since:** 1.1.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.aliasadd/\">FT.ALIASADD</a></strong> - Adds an alias to the index</summary>\n\n**Syntax:** `FT.ALIASADD alias index`\n\n**Description:** Adds an alias to the index\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.aliasdel/\">FT.ALIASDEL</a></strong> - Deletes an alias from the index</summary>\n\n**Syntax:** `FT.ALIASDEL alias`\n\n**Description:** Deletes an alias from the index\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.aliasupdate/\">FT.ALIASUPDATE</a></strong> - Adds or updates an alias to the index</summary>\n\n**Syntax:** `FT.ALIASUPDATE alias index`\n\n**Description:** Adds or updates an alias to the index\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.alter/\">FT.ALTER</a></strong> - Adds a new field to the index</summary>\n\n**Syntax:** `FT.ALTER index [SKIPINITIALSCAN] SCHEMA ADD field options`\n\n**Description:** Adds a new field to the index\n\n**Complexity:** O(N) where N is the number of keys in the keyspace\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.config-get/\">FT.CONFIG GET</a></strong> - Retrieves runtime configuration options</summary>\n\n**Syntax:** `FT.CONFIG GET option`\n\n**Description:** Retrieves runtime configuration options\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.config-set/\">FT.CONFIG SET</a></strong> - Sets runtime configuration options</summary>\n\n**Syntax:** `FT.CONFIG SET option value`\n\n**Description:** Sets runtime configuration options\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.create/\">FT.CREATE</a></strong> - Creates an index with the given spec</summary>\n\n**Syntax:** `FT.CREATE index [ON <HASH | JSON>] [PREFIX count prefix [prefix\n  ...]] [FILTER filter] [LANGUAGE default lang]\n  [LANGUAGE FIELD lang attribute] [SCORE default score]\n  [SCORE FIELD score attribute] [PAYLOAD FIELD payload attribute]\n  [MAXTEXTFIELDS] [TEMPORARY seconds] [NOOFFSETS] [NOHL] [NOFIELDS]\n  [NOFREQS] [STOPWORDS count [stopword [stopword ...]]]\n  [SKIPINITIALSCAN] [INDEXALL <ENABLE | DISABLE>] SCHEMA field name [AS alias] <TEXT | TAG |\n  NUMERIC | GEO | VECTOR> [WITHSUFFIXTRIE] [INDEXEMPTY]\n  [INDEXMISSING] [SORTABLE [UNF]] [NOINDEX] [field name [AS alias]\n  <TEXT | TAG | NUMERIC | GEO | VECTOR> [WITHSUFFIXTRIE]\n  [INDEXEMPTY] [INDEXMISSING] [SORTABLE [UNF]] [NOINDEX] ...]`\n\n**Description:** Creates an index with the given spec\n\n**Complexity:** O(K) at creation where K is the number of fields, O(N) if scanning the keyspace is triggered, where N is the number of keys in the keyspace\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.cursor-del/\">FT.CURSOR DEL</a></strong> - Deletes a cursor</summary>\n\n**Syntax:** `FT.CURSOR DEL index cursor id`\n\n**Description:** Deletes a cursor\n\n**Complexity:** O(1)\n\n**Since:** 1.1.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.cursor-read/\">FT.CURSOR READ</a></strong> - Reads from a cursor</summary>\n\n**Syntax:** `FT.CURSOR READ index cursor id [COUNT read size]`\n\n**Description:** Reads from a cursor\n\n**Complexity:** O(1)\n\n**Since:** 1.1.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.dictadd/\">FT.DICTADD</a></strong> - Adds terms to a dictionary</summary>\n\n**Syntax:** `FT.DICTADD dict term [term ...]`\n\n**Description:** Adds terms to a dictionary\n\n**Complexity:** O(1)\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.dictdel/\">FT.DICTDEL</a></strong> - Deletes terms from a dictionary</summary>\n\n**Syntax:** `FT.DICTDEL dict term [term ...]`\n\n**Description:** Deletes terms from a dictionary\n\n**Complexity:** O(1)\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.dictdump/\">FT.DICTDUMP</a></strong> - Dumps all terms in the given dictionary</summary>\n\n**Syntax:** `FT.DICTDUMP dict`\n\n**Description:** Dumps all terms in the given dictionary\n\n**Complexity:** O(N), where N is the size of the dictionary\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.dropindex/\">FT.DROPINDEX</a></strong> - Deletes the index</summary>\n\n**Syntax:** `FT.DROPINDEX index [DD]`\n\n**Description:** Deletes the index\n\n**Complexity:** O(1) or O(N) if documents are deleted, where N is the number of keys in the keyspace\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.explain/\">FT.EXPLAIN</a></strong> - Returns the execution plan for a complex query</summary>\n\n**Syntax:** `FT.EXPLAIN index query [DIALECT dialect]`\n\n**Description:** Returns the execution plan for a complex query\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.explaincli/\">FT.EXPLAINCLI</a></strong> - Returns the execution plan for a complex query</summary>\n\n**Syntax:** `FT.EXPLAINCLI index query [DIALECT dialect]`\n\n**Description:** Returns the execution plan for a complex query\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.hybrid/\">FT.HYBRID</a></strong> - Performs hybrid search combining text search and vector similarity search</summary>\n\n**Syntax:** `FT.HYBRID index\n  SEARCH query\n    [SCORER scorer]\n    [YIELD SCORE AS name]\n  VSIM vector field $vector param\n    [KNN count K k [EF RUNTIME ef runtime]]\n    [RANGE count RADIUS radius [EPSILON epsilon]]\n    [YIELD SCORE AS name]\n    [FILTER filter]\n  [COMBINE RRF count [CONSTANT constant] [WINDOW window] [YIELD SCORE AS name]]\n  [COMBINE LINEAR count [[ALPHA alpha] [BETA beta]] [WINDOW window] [YIELD SCORE AS name]]\n  [LIMIT offset num]\n  [SORTBY count sortby [ASC | DESC]]\n  [NOSORT]\n  [LOAD count field [field ...]]\n  [LOAD *]\n  [GROUPBY nargs property [property ...]\n  [GROUPBY nargs property [property ...]\n    [REDUCE function nargs arg [arg ...] [AS name]\n    [REDUCE function nargs arg [arg ...] [AS name] ...]] ...]]\n  [APPLY expression AS name [APPLY expression AS name ...]]\n  [FILTER filter]\n  PARAMS nargs vector param vector blob [name value ...]\n  [TIMEOUT timeout]`\n\n**Description:** Performs hybrid search combining text search and vector similarity search\n\n**Complexity:** O(N+M) where N is the complexity of the text search and M is the complexity of the vector search\n\n**Since:** 8.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.info/\">FT.INFO</a></strong> - Returns information and statistics on the index</summary>\n\n**Syntax:** `FT.INFO index`\n\n**Description:** Returns information and statistics on the index\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.profile/\">FT.PROFILE</a></strong> - Performs a `FT.SEARCH` or `FT.AGGREGATE` command and collects performance information</summary>\n\n**Syntax:** `FT.PROFILE index <SEARCH | AGGREGATE> [LIMITED] QUERY query`\n\n**Description:** Performs a `FT.SEARCH` or `FT.AGGREGATE` command and collects performance information\n\n**Complexity:** O(N)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.search/\">FT.SEARCH</a></strong> - Searches the index with a textual query, returning either documents or just ids</summary>\n\n**Syntax:** `FT.SEARCH index query [NOCONTENT] [VERBATIM] [NOSTOPWORDS]\n  [WITHSCORES] [WITHPAYLOADS] [WITHSORTKEYS] [FILTER numeric field\n  min max [FILTER numeric field min max ...]] [GEOFILTER geo field\n  lon lat radius <m | km | mi | ft> [GEOFILTER geo field lon lat\n  radius <m | km | mi | ft> ...]] [INKEYS count key [key ...]]\n  [INFIELDS count field [field ...]] [RETURN count identifier\n  [AS property] [identifier [AS property] ...]] [SUMMARIZE\n  [FIELDS count field [field ...]] [FRAGS num] [LEN fragsize]\n  [SEPARATOR separator]] [HIGHLIGHT [FIELDS count field [field ...]]\n  [TAGS open close]] [SLOP slop] [TIMEOUT timeout] [INORDER]\n  [LANGUAGE language] [EXPANDER expander] [SCORER scorer]\n  [EXPLAINSCORE] [PAYLOAD payload] [SORTBY sortby [ASC | DESC]]\n  [LIMIT offset num] [PARAMS nargs name value [name value ...]]\n  [DIALECT dialect]`\n\n**Description:** Searches the index with a textual query, returning either documents or just ids\n\n**Complexity:** O(N)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.spellcheck/\">FT.SPELLCHECK</a></strong> - Performs spelling correction on a query, returning suggestions for misspelled terms</summary>\n\n**Syntax:** `FT.SPELLCHECK index query [DISTANCE distance] [TERMS <INCLUDE |\n  EXCLUDE> dictionary [terms [terms ...]]] [DIALECT dialect]`\n\n**Description:** Performs spelling correction on a query, returning suggestions for misspelled terms\n\n**Complexity:** O(1)\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.syndump/\">FT.SYNDUMP</a></strong> - Dumps the contents of a synonym group</summary>\n\n**Syntax:** `FT.SYNDUMP index`\n\n**Description:** Dumps the contents of a synonym group\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.synupdate/\">FT.SYNUPDATE</a></strong> - Creates or updates a synonym group with additional terms</summary>\n\n**Syntax:** `FT.SYNUPDATE index synonym group id [SKIPINITIALSCAN] term [term\n  ...]`\n\n**Description:** Creates or updates a synonym group with additional terms\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft.tagvals/\">FT.TAGVALS</a></strong> - Returns the distinct tags indexed in a Tag field</summary>\n\n**Syntax:** `FT.TAGVALS index field name`\n\n**Description:** Returns the distinct tags indexed in a Tag field\n\n**Complexity:** O(N)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ft._list/\">FT._LIST</a></strong> - Returns a list of all existing indexes</summary>\n\n**Syntax:** `FT. LIST`\n\n**Description:** Returns a list of all existing indexes\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>"},{"id":"time-series-commands","title":"Time series commands","role":"content","text":"Time series commands operate on time-series data.\n\n<details>\n<summary><strong><a href=\"/commands/ts.add/\">TS.ADD</a></strong> - Append a sample to a time series</summary>\n\n**Syntax:** `TS.ADD key timestamp value [RETENTION retentionPeriod]\n  [ENCODING <UNCOMPRESSED | COMPRESSED>] [CHUNK SIZE size]\n  [ON DUPLICATE <BLOCK | FIRST | LAST | MIN | MAX | SUM>]\n  [LABELS label value [label value ...]]`\n\n**Description:** Append a sample to a time series\n\n**Complexity:** O(M) when M is the amount of compaction rules or O(1) with no compaction\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.alter/\">TS.ALTER</a></strong> - Update the retention, chunk size, duplicate policy, and labels of an existing time series</summary>\n\n**Syntax:** `TS.ALTER key [RETENTION retentionPeriod] [CHUNK SIZE size]\n  [DUPLICATE POLICY <BLOCK | FIRST | LAST | MIN | MAX | SUM>] [LABELS label value\n  [label value ...]]`\n\n**Description:** Update the retention, chunk size, duplicate policy, and labels of an existing time series\n\n**Complexity:** O(N) where N is the number of labels requested to update\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.create/\">TS.CREATE</a></strong> - Create a new time series</summary>\n\n**Syntax:** `TS.CREATE key [RETENTION retentionPeriod] [ENCODING <UNCOMPRESSED |\n  COMPRESSED>] [CHUNK SIZE size] [DUPLICATE POLICY <BLOCK | FIRST |\n  LAST | MIN | MAX | SUM>] [LABELS label value [label value ...]]`\n\n**Description:** Create a new time series\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.createrule/\">TS.CREATERULE</a></strong> - Create a compaction rule</summary>\n\n**Syntax:** `TS.CREATERULE sourceKey destKey AGGREGATION <AVG | FIRST | LAST |\n  MIN | MAX | SUM | RANGE | COUNT | STD.P | STD.S | VAR.P | VAR.S |\n  TWA> bucketDuration [alignTimestamp]`\n\n**Description:** Create a compaction rule\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.decrby/\">TS.DECRBY</a></strong> - Decrease the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given decrement</summary>\n\n**Syntax:** `TS.DECRBY key value [TIMESTAMP timestamp]\n  [RETENTION retentionPeriod] [UNCOMPRESSED] [CHUNK SIZE size]\n  [LABELS label value [label value ...]]`\n\n**Description:** Decrease the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given decrement\n\n**Complexity:** O(M) when M is the amount of compaction rules or O(1) with no compaction\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.del/\">TS.DEL</a></strong> - Delete all samples between two timestamps for a given time series</summary>\n\n**Syntax:** `TS.DEL key from timestamp to timestamp`\n\n**Description:** Delete all samples between two timestamps for a given time series\n\n**Complexity:** O(N) where N is the number of data points that will be removed\n\n**Since:** 1.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.deleterule/\">TS.DELETERULE</a></strong> - Delete a compaction rule</summary>\n\n**Syntax:** `TS.DELETERULE sourceKey destKey`\n\n**Description:** Delete a compaction rule\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.get/\">TS.GET</a></strong> - Get the sample with the highest timestamp from a given time series</summary>\n\n**Syntax:** `TS.GET key [LATEST]`\n\n**Description:** Get the sample with the highest timestamp from a given time series\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.incrby/\">TS.INCRBY</a></strong> - Increase the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given increment</summary>\n\n**Syntax:** `TS.INCRBY key value [TIMESTAMP timestamp]\n  [RETENTION retentionPeriod] [UNCOMPRESSED] [CHUNK SIZE size]\n  [LABELS label value [label value ...]]`\n\n**Description:** Increase the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given increment\n\n**Complexity:** O(M) when M is the amount of compaction rules or O(1) with no compaction\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.info/\">TS.INFO</a></strong> - Returns information and statistics for a time series</summary>\n\n**Syntax:** `TS.INFO key [DEBUG]`\n\n**Description:** Returns information and statistics for a time series\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.madd/\">TS.MADD</a></strong> - Append new samples to one or more time series</summary>\n\n**Syntax:** `TS.MADD key timestamp value [key timestamp value ...]`\n\n**Description:** Append new samples to one or more time series\n\n**Complexity:** O(N*M) when N is the amount of series updated and M is the amount of compaction rules or O(N) with no compaction\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.mget/\">TS.MGET</a></strong> - Get the sample with the highest timestamp from each time series matching a specific filter</summary>\n\n**Syntax:** `TS.MGET [LATEST] [WITHLABELS | SELECTED LABELS label1 [label1 ...]]\n  FILTER <l=v | l!=v | l= | l!= | l=(v1,v2,...) | l!=(v1,v2,...)\n  [l=v | l!=v | l= | l!= | l=(v1,v2,...) | l!=(v1,v2,...) ...]>`\n\n**Description:** Get the sample with the highest timestamp from each time series matching a specific filter\n\n**Complexity:** O(n) where n is the number of time-series that match the filters\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.mrange/\">TS.MRANGE</a></strong> - Query a range across multiple time series by filters in forward direction</summary>\n\n**Syntax:** `TS.MRANGE fromTimestamp toTimestamp [LATEST] [FILTER BY TS Timestamp\n  [Timestamp ...]] [FILTER BY VALUE min max] [WITHLABELS |\n  SELECTED LABELS label1 [label1 ...]] [COUNT count] [[ALIGN value]\n  AGGREGATION aggregators bucketDuration [BUCKETTIMESTAMP]\n  [EMPTY]] FILTER <l=v | l!=v | l= | l!= | l=(v1,v2,...) |\n  l!=(v1,v2,...) [l=v | l!=v | l= | l!= | l=(v1,v2,...) |\n  l!=(v1,v2,...) ...]> [GROUPBY label REDUCE reducer]`\n\n**Description:** Query a range across multiple time series by filters in forward direction\n\n**Complexity:** O(n/m+k) where n = Number of data points, m = Chunk size (data points per chunk), k = Number of data points that are in the requested ranges\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.mrevrange/\">TS.MREVRANGE</a></strong> - Query a range across multiple time-series by filters in reverse direction</summary>\n\n**Syntax:** `TS.MREVRANGE fromTimestamp toTimestamp [LATEST]\n  [FILTER BY TS Timestamp [Timestamp ...]] [FILTER BY VALUE min max]\n  [WITHLABELS | SELECTED LABELS label1 [label1 ...]] [COUNT count]\n  [[ALIGN value] AGGREGATION aggregators bucketDuration\n  [BUCKETTIMESTAMP] [EMPTY]] FILTER <l=v | l!=v | l= | l!= |\n  l=(v1,v2,...) | l!=(v1,v2,...) [l=v | l!=v | l= | l!= |\n  l=(v1,v2,...) | l!=(v1,v2,...) ...]> [GROUPBY label REDUCE\n  reducer]`\n\n**Description:** Query a range across multiple time-series by filters in reverse direction\n\n**Complexity:** O(n/m+k) where n = Number of data points, m = Chunk size (data points per chunk), k = Number of data points that are in the requested ranges\n\n**Since:** 1.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.queryindex/\">TS.QUERYINDEX</a></strong> - Get all time series keys matching a filter list</summary>\n\n**Syntax:** `TS.QUERYINDEX <l=v | l!=v | l= | l!= | l=(v1,v2,...) |\n  l!=(v1,v2,...) [l=v | l!=v | l= | l!= | l=(v1,v2,...) |\n  l!=(v1,v2,...) ...]>`\n\n**Description:** Get all time series keys matching a filter list\n\n**Complexity:** O(n) where n is the number of time-series that match the filters\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.range/\">TS.RANGE</a></strong> - Query a range in forward direction</summary>\n\n**Syntax:** `TS.RANGE key fromTimestamp toTimestamp [LATEST]\n  [FILTER BY TS Timestamp [Timestamp ...]] [FILTER BY VALUE min max]\n  [COUNT count] [[ALIGN value] AGGREGATION aggregators bucketDuration [BUCKETTIMESTAMP] [EMPTY]]`\n\n**Description:** Query a range in forward direction\n\n**Complexity:** O(n/m+k) where n = Number of data points, m = Chunk size (data points per chunk), k = Number of data points that are in the requested range\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ts.revrange/\">TS.REVRANGE</a></strong> - Query a range in reverse direction</summary>\n\n**Syntax:** `TS.REVRANGE key fromTimestamp toTimestamp [LATEST]\n  [FILTER BY TS Timestamp [Timestamp ...]] [FILTER BY VALUE min max]\n  [COUNT count] [[ALIGN value] AGGREGATION aggregators\n  bucketDuration [BUCKETTIMESTAMP] [EMPTY]]`\n\n**Description:** Query a range in reverse direction\n\n**Complexity:** O(n/m+k) where n = Number of data points, m = Chunk size (data points per chunk), k = Number of data points that are in the requested range\n\n**Since:** 1.4.0\n\n</details>"},{"id":"vector-set-commands","title":"Vector set commands","role":"content","text":"Vector set commands operate on vector data structures for similarity search and range queries.\n\n<details>\n<summary><strong><a href=\"/commands/vadd/\">VADD</a></strong> - Add a new element to a vector set, or update its vector if it already exists.</summary>\n\n**Syntax:** `VADD key [REDUCE dim] (FP32 | VALUES num) vector element [CAS] [NOQUANT | Q8 | BIN]\n  [EF build-exploration-factor] [SETATTR attributes] [M numlinks]`\n\n**Description:** Add a new element to a vector set, or update its vector if it already exists.\n\n**Complexity:** O(log(N)) for each element added, where N is the number of elements in the vector set.\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vcard/\">VCARD</a></strong> - Return the number of elements in a vector set.</summary>\n\n**Syntax:** `VCARD key`\n\n**Description:** Return the number of elements in a vector set.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vdim/\">VDIM</a></strong> - Return the dimension of vectors in the vector set.</summary>\n\n**Syntax:** `VDIM key`\n\n**Description:** Return the dimension of vectors in the vector set.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vemb/\">VEMB</a></strong> - Return the vector associated with an element.</summary>\n\n**Syntax:** `VEMB key element [RAW]`\n\n**Description:** Return the vector associated with an element.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vgetattr/\">VGETATTR</a></strong> - Retrieve the JSON attributes of elements.</summary>\n\n**Syntax:** `VGETATTR key element`\n\n**Description:** Retrieve the JSON attributes of elements.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vinfo/\">VINFO</a></strong> - Return information about a vector set.</summary>\n\n**Syntax:** `VINFO key`\n\n**Description:** Return information about a vector set.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vismember/\">VISMEMBER</a></strong> - Check if an element exists in a vector set.</summary>\n\n**Syntax:** `VISMEMBER key element`\n\n**Description:** Check if an element exists in a vector set.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vlinks/\">VLINKS</a></strong> - Return the neighbors of an element at each layer in the HNSW graph.</summary>\n\n**Syntax:** `VLINKS key element [WITHSCORES]`\n\n**Description:** Return the neighbors of an element at each layer in the HNSW graph.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vrandmember/\">VRANDMEMBER</a></strong> - Return one or multiple random members from a vector set.</summary>\n\n**Syntax:** `VRANDMEMBER key [count]`\n\n**Description:** Return one or multiple random members from a vector set.\n\n**Complexity:** O(N) where N is the absolute value of the count argument.\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vrange/\">VRANGE</a></strong> - Return elements in a lexicographical range</summary>\n\n**Syntax:** `VRANGE key start end [count]`\n\n**Description:** Return elements in a lexicographical range\n\n**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)\n\n**Since:** 8.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vrem/\">VREM</a></strong> - Remove an element from a vector set.</summary>\n\n**Syntax:** `VREM key element`\n\n**Description:** Remove an element from a vector set.\n\n**Complexity:** O(log(N)) for each element removed, where N is the number of elements in the vector set.\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vsetattr/\">VSETATTR</a></strong> - Associate or remove the JSON attributes of elements.</summary>\n\n**Syntax:** `VSETATTR key element \"{ JSON obj }\"`\n\n**Description:** Associate or remove the JSON attributes of elements.\n\n**Complexity:** O(1)\n\n**Since:** 8.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/vsim/\">VSIM</a></strong> - Return elements by vector similarity.</summary>\n\n**Syntax:** `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]`\n\n**Description:** Return elements by vector similarity.\n\n**Complexity:** O(log(N)) where N is the number of elements in the vector set.\n\n**Since:** 8.0.0\n\n</details>"},{"id":"pubsub-commands","title":"Pub/Sub commands","role":"content","text":"Pub/Sub commands enable message passing between clients.\n\n<details>\n<summary><strong><a href=\"/commands/psubscribe/\">PSUBSCRIBE</a></strong> - Listens for messages published to channels that match one or more patterns.</summary>\n\n**Syntax:** `PSUBSCRIBE pattern [pattern ...]`\n\n**Description:** Listens for messages published to channels that match one or more patterns.\n\n**Complexity:** O(N) where N is the number of patterns to subscribe to.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/publish/\">PUBLISH</a></strong> - Posts a message to a channel.</summary>\n\n**Syntax:** `PUBLISH channel message`\n\n**Description:** Posts a message to a channel.\n\n**Complexity:** O(N+M) where N is the number of clients subscribed to the receiving channel and M is the total number of subscribed patterns (by any client).\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-channels/\">PUBSUB CHANNELS</a></strong> - Returns the active channels.</summary>\n\n**Syntax:** `PUBSUB CHANNELS [pattern]`\n\n**Description:** Returns the active channels.\n\n**Complexity:** O(N) where N is the number of active channels, and assuming constant time pattern matching (relatively short channels and patterns)\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-numpat/\">PUBSUB NUMPAT</a></strong> - Returns a count of unique pattern subscriptions.</summary>\n\n**Syntax:** `PUBSUB NUMPAT`\n\n**Description:** Returns a count of unique pattern subscriptions.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-numsub/\">PUBSUB NUMSUB</a></strong> - Returns a count of subscribers to channels.</summary>\n\n**Syntax:** `PUBSUB NUMSUB [channel [channel ...]]`\n\n**Description:** Returns a count of subscribers to channels.\n\n**Complexity:** O(N) for the NUMSUB subcommand, where N is the number of requested channels\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-shardchannels/\">PUBSUB SHARDCHANNELS</a></strong> - Returns the active shard channels.</summary>\n\n**Syntax:** `PUBSUB SHARDCHANNELS [pattern]`\n\n**Description:** Returns the active shard channels.\n\n**Complexity:** O(N) where N is the number of active shard channels, and assuming constant time pattern matching (relatively short shard channels).\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pubsub-shardnumsub/\">PUBSUB SHARDNUMSUB</a></strong> - Returns the count of subscribers of shard channels.</summary>\n\n**Syntax:** `PUBSUB SHARDNUMSUB [shardchannel [shardchannel ...]]`\n\n**Description:** Returns the count of subscribers of shard channels.\n\n**Complexity:** O(N) for the SHARDNUMSUB subcommand, where N is the number of requested shard channels\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/punsubscribe/\">PUNSUBSCRIBE</a></strong> - Stops listening to messages published to channels that match one or more patterns.</summary>\n\n**Syntax:** `PUNSUBSCRIBE [pattern [pattern ...]]`\n\n**Description:** Stops listening to messages published to channels that match one or more patterns.\n\n**Complexity:** O(N) where N is the number of patterns to unsubscribe.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/spublish/\">SPUBLISH</a></strong> - Post a message to a shard channel</summary>\n\n**Syntax:** `SPUBLISH shardchannel message`\n\n**Description:** Post a message to a shard channel\n\n**Complexity:** O(N) where N is the number of clients subscribed to the receiving shard channel.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ssubscribe/\">SSUBSCRIBE</a></strong> - Listens for messages published to shard channels.</summary>\n\n**Syntax:** `SSUBSCRIBE shardchannel [shardchannel ...]`\n\n**Description:** Listens for messages published to shard channels.\n\n**Complexity:** O(N) where N is the number of shard channels to subscribe to.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/subscribe/\">SUBSCRIBE</a></strong> - Listens for messages published to channels.</summary>\n\n**Syntax:** `SUBSCRIBE channel [channel ...]`\n\n**Description:** Listens for messages published to channels.\n\n**Complexity:** O(N) where N is the number of channels to subscribe to.\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sunsubscribe/\">SUNSUBSCRIBE</a></strong> - Stops listening to messages posted to shard channels.</summary>\n\n**Syntax:** `SUNSUBSCRIBE [shardchannel [shardchannel ...]]`\n\n**Description:** Stops listening to messages posted to shard channels.\n\n**Complexity:** O(N) where N is the number of shard channels to unsubscribe.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/unsubscribe/\">UNSUBSCRIBE</a></strong> - Stops listening to messages posted to channels.</summary>\n\n**Syntax:** `UNSUBSCRIBE [channel [channel ...]]`\n\n**Description:** Stops listening to messages posted to channels.\n\n**Complexity:** O(N) where N is the number of channels to unsubscribe.\n\n**Since:** 2.0.0\n\n</details>"},{"id":"transaction-commands","title":"Transaction commands","role":"content","text":"Transaction commands enable atomic execution of command groups.\n\n<details>\n<summary><strong><a href=\"/commands/discard/\">DISCARD</a></strong> - Discards a transaction.</summary>\n\n**Syntax:** `DISCARD`\n\n**Description:** Discards a transaction.\n\n**Complexity:** O(N), when N is the number of queued commands\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/exec/\">EXEC</a></strong> - Executes all commands in a transaction.</summary>\n\n**Syntax:** `EXEC`\n\n**Description:** Executes all commands in a transaction.\n\n**Complexity:** Depends on commands in the transaction\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/multi/\">MULTI</a></strong> - Starts a transaction.</summary>\n\n**Syntax:** `MULTI`\n\n**Description:** Starts a transaction.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/unwatch/\">UNWATCH</a></strong> - Forgets about watched keys of a transaction.</summary>\n\n**Syntax:** `UNWATCH`\n\n**Description:** Forgets about watched keys of a transaction.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/watch/\">WATCH</a></strong> - Monitors changes to keys to determine the execution of a transaction.</summary>\n\n**Syntax:** `WATCH key [key ...]`\n\n**Description:** Monitors changes to keys to determine the execution of a transaction.\n\n**Complexity:** O(1) for every key.\n\n**Since:** 2.2.0\n\n</details>"},{"id":"scripting-commands","title":"Scripting commands","role":"content","text":"Scripting commands enable server-side Lua script execution.\n\n<details>\n<summary><strong><a href=\"/commands/eval/\">EVAL</a></strong> - Executes a server-side Lua script.</summary>\n\n**Syntax:** `EVAL script numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Executes a server-side Lua script.\n\n**Complexity:** Depends on the script that is executed.\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/evalsha/\">EVALSHA</a></strong> - Executes a server-side Lua script by SHA1 digest.</summary>\n\n**Syntax:** `EVALSHA sha1 numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Executes a server-side Lua script by SHA1 digest.\n\n**Complexity:** Depends on the script that is executed.\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/evalsha_ro/\">EVALSHA_RO</a></strong> - Executes a read-only server-side Lua script by SHA1 digest.</summary>\n\n**Syntax:** `EVALSHA RO sha1 numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Executes a read-only server-side Lua script by SHA1 digest.\n\n**Complexity:** Depends on the script that is executed.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/eval_ro/\">EVAL_RO</a></strong> - Executes a read-only server-side Lua script.</summary>\n\n**Syntax:** `EVAL RO script numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Executes a read-only server-side Lua script.\n\n**Complexity:** Depends on the script that is executed.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/fcall/\">FCALL</a></strong> - Invokes a function.</summary>\n\n**Syntax:** `FCALL function numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Invokes a function.\n\n**Complexity:** Depends on the function that is executed.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/fcall_ro/\">FCALL_RO</a></strong> - Invokes a read-only function.</summary>\n\n**Syntax:** `FCALL RO function numkeys [key [key ...]] [arg [arg ...]]`\n\n**Description:** Invokes a read-only function.\n\n**Complexity:** Depends on the function that is executed.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-delete/\">FUNCTION DELETE</a></strong> - Deletes a library and its functions.</summary>\n\n**Syntax:** `FUNCTION DELETE library-name`\n\n**Description:** Deletes a library and its functions.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-dump/\">FUNCTION DUMP</a></strong> - Dumps all libraries into a serialized binary payload.</summary>\n\n**Syntax:** `FUNCTION DUMP`\n\n**Description:** Dumps all libraries into a serialized binary payload.\n\n**Complexity:** O(N) where N is the number of functions\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-flush/\">FUNCTION FLUSH</a></strong> - Deletes all libraries and functions.</summary>\n\n**Syntax:** `FUNCTION FLUSH [ASYNC | SYNC]`\n\n**Description:** Deletes all libraries and functions.\n\n**Complexity:** O(N) where N is the number of functions deleted\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-kill/\">FUNCTION KILL</a></strong> - Terminates a function during execution.</summary>\n\n**Syntax:** `FUNCTION KILL`\n\n**Description:** Terminates a function during execution.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-list/\">FUNCTION LIST</a></strong> - Returns information about all libraries.</summary>\n\n**Syntax:** `FUNCTION LIST [LIBRARYNAME library-name-pattern] [WITHCODE]`\n\n**Description:** Returns information about all libraries.\n\n**Complexity:** O(N) where N is the number of functions\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-load/\">FUNCTION LOAD</a></strong> - Creates a library.</summary>\n\n**Syntax:** `FUNCTION LOAD [REPLACE] function-code`\n\n**Description:** Creates a library.\n\n**Complexity:** O(1) (considering compilation time is redundant)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-restore/\">FUNCTION RESTORE</a></strong> - Restores all libraries from a payload.</summary>\n\n**Syntax:** `FUNCTION RESTORE serialized-value [FLUSH | APPEND | REPLACE]`\n\n**Description:** Restores all libraries from a payload.\n\n**Complexity:** O(N) where N is the number of functions on the payload\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/function-stats/\">FUNCTION STATS</a></strong> - Returns information about a function during execution.</summary>\n\n**Syntax:** `FUNCTION STATS`\n\n**Description:** Returns information about a function during execution.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-debug/\">SCRIPT DEBUG</a></strong> - Sets the debug mode of server-side Lua scripts.</summary>\n\n**Syntax:** `SCRIPT DEBUG <YES | SYNC | NO>`\n\n**Description:** Sets the debug mode of server-side Lua scripts.\n\n**Complexity:** O(1)\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-exists/\">SCRIPT EXISTS</a></strong> - Determines whether server-side Lua scripts exist in the script cache.</summary>\n\n**Syntax:** `SCRIPT EXISTS sha1 [sha1 ...]`\n\n**Description:** Determines whether server-side Lua scripts exist in the script cache.\n\n**Complexity:** O(N) with N being the number of scripts to check (so checking a single script is an O(1) operation).\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-flush/\">SCRIPT FLUSH</a></strong> - Removes all server-side Lua scripts from the script cache.</summary>\n\n**Syntax:** `SCRIPT FLUSH [ASYNC | SYNC]`\n\n**Description:** Removes all server-side Lua scripts from the script cache.\n\n**Complexity:** O(N) with N being the number of scripts in cache\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-kill/\">SCRIPT KILL</a></strong> - Terminates a server-side Lua script during execution.</summary>\n\n**Syntax:** `SCRIPT KILL`\n\n**Description:** Terminates a server-side Lua script during execution.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/script-load/\">SCRIPT LOAD</a></strong> - Loads a server-side Lua script to the script cache.</summary>\n\n**Syntax:** `SCRIPT LOAD script`\n\n**Description:** Loads a server-side Lua script to the script cache.\n\n**Complexity:** O(N) with N being the length in bytes of the script body.\n\n**Since:** 2.6.0\n\n</details>"},{"id":"connection-commands","title":"Connection commands","role":"content","text":"Connection commands manage client connections.\n\n<details>\n<summary><strong><a href=\"/commands/auth/\">AUTH</a></strong> - Authenticates the connection.</summary>\n\n**Syntax:** `AUTH [username] password`\n\n**Description:** Authenticates the connection.\n\n**Complexity:** O(N) where N is the number of passwords defined for the user\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-caching/\">CLIENT CACHING</a></strong> - Instructs the server whether to track the keys in the next request.</summary>\n\n**Syntax:** `CLIENT CACHING <YES | NO>`\n\n**Description:** Instructs the server whether to track the keys in the next request.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-getname/\">CLIENT GETNAME</a></strong> - Returns the name of the connection.</summary>\n\n**Syntax:** `CLIENT GETNAME`\n\n**Description:** Returns the name of the connection.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-getredir/\">CLIENT GETREDIR</a></strong> - Returns the client ID to which the connection's tracking notifications are redirected.</summary>\n\n**Syntax:** `CLIENT GETREDIR`\n\n**Description:** Returns the client ID to which the connection's tracking notifications are redirected.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-id/\">CLIENT ID</a></strong> - Returns the unique client ID of the connection.</summary>\n\n**Syntax:** `CLIENT ID`\n\n**Description:** Returns the unique client ID of the connection.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-info/\">CLIENT INFO</a></strong> - Returns information about the connection.</summary>\n\n**Syntax:** `CLIENT INFO`\n\n**Description:** Returns information about the connection.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-kill/\">CLIENT KILL</a></strong> - Terminates open connections.</summary>\n\n**Syntax:** `CLIENT KILL <ip:port | <[ID client-id] | [TYPE <NORMAL | MASTER |\n  SLAVE | REPLICA | PUBSUB>] | [USER username] | [ADDR ip:port] |\n  [LADDR ip:port] | [SKIPME <YES | NO>] | [MAXAGE maxage]\n  [[ID client-id] | [TYPE <NORMAL | MASTER | SLAVE | REPLICA |\n  PUBSUB>] | [USER username] | [ADDR ip:port] | [LADDR ip:port] |\n  [SKIPME <YES | NO>] | [MAXAGE maxage] ...]>>`\n\n**Description:** Terminates open connections.\n\n**Complexity:** O(N) where N is the number of client connections\n\n**Since:** 2.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-list/\">CLIENT LIST</a></strong> - Lists open connections.</summary>\n\n**Syntax:** `CLIENT LIST [TYPE <NORMAL | MASTER | REPLICA | PUBSUB>]\n  [ID client-id [client-id ...]]`\n\n**Description:** Lists open connections.\n\n**Complexity:** O(N) where N is the number of client connections\n\n**Since:** 2.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-no-evict/\">CLIENT NO-EVICT</a></strong> - Sets the client eviction mode of the connection.</summary>\n\n**Syntax:** `CLIENT NO-EVICT <ON | OFF>`\n\n**Description:** Sets the client eviction mode of the connection.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-no-touch/\">CLIENT NO-TOUCH</a></strong> - Controls whether commands sent by the client affect the LRU/LFU of accessed keys.</summary>\n\n**Syntax:** `CLIENT NO-TOUCH <ON | OFF>`\n\n**Description:** Controls whether commands sent by the client affect the LRU/LFU of accessed keys.\n\n**Complexity:** O(1)\n\n**Since:** 7.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-pause/\">CLIENT PAUSE</a></strong> - Suspends commands processing.</summary>\n\n**Syntax:** `CLIENT PAUSE timeout [WRITE | ALL]`\n\n**Description:** Suspends commands processing.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-reply/\">CLIENT REPLY</a></strong> - Instructs the server whether to reply to commands.</summary>\n\n**Syntax:** `CLIENT REPLY <ON | OFF | SKIP>`\n\n**Description:** Instructs the server whether to reply to commands.\n\n**Complexity:** O(1)\n\n**Since:** 3.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-setinfo/\">CLIENT SETINFO</a></strong> - Sets information specific to the client or connection.</summary>\n\n**Syntax:** `CLIENT SETINFO <LIB-NAME libname | LIB-VER libver>`\n\n**Description:** Sets information specific to the client or connection.\n\n**Complexity:** O(1)\n\n**Since:** 7.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-setname/\">CLIENT SETNAME</a></strong> - Sets the connection name.</summary>\n\n**Syntax:** `CLIENT SETNAME connection-name`\n\n**Description:** Sets the connection name.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.9\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-tracking/\">CLIENT TRACKING</a></strong> - Controls server-assisted client-side caching for the connection.</summary>\n\n**Syntax:** `CLIENT TRACKING <ON | OFF> [REDIRECT client-id] [PREFIX prefix\n  [PREFIX prefix ...]] [BCAST] [OPTIN] [OPTOUT] [NOLOOP]`\n\n**Description:** Controls server-assisted client-side caching for the connection.\n\n**Complexity:** O(1). Some options may introduce additional complexity.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-trackinginfo/\">CLIENT TRACKINGINFO</a></strong> - Returns information about server-assisted client-side caching for the connection.</summary>\n\n**Syntax:** `CLIENT TRACKINGINFO`\n\n**Description:** Returns information about server-assisted client-side caching for the connection.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-unblock/\">CLIENT UNBLOCK</a></strong> - Unblocks a client blocked by a blocking command from a different connection.</summary>\n\n**Syntax:** `CLIENT UNBLOCK client-id [TIMEOUT | ERROR]`\n\n**Description:** Unblocks a client blocked by a blocking command from a different connection.\n\n**Complexity:** O(log N) where N is the number of client connections\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/client-unpause/\">CLIENT UNPAUSE</a></strong> - Resumes processing commands from paused clients.</summary>\n\n**Syntax:** `CLIENT UNPAUSE`\n\n**Description:** Resumes processing commands from paused clients.\n\n**Complexity:** O(N) Where N is the number of paused clients\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/echo/\">ECHO</a></strong> - Returns the given string.</summary>\n\n**Syntax:** `ECHO message`\n\n**Description:** Returns the given string.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hello/\">HELLO</a></strong> - Handshakes with the Redis server.</summary>\n\n**Syntax:** `HELLO [protover [AUTH username password] [SETNAME clientname]]`\n\n**Description:** Handshakes with the Redis server.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ping/\">PING</a></strong> - Returns the server's liveliness response.</summary>\n\n**Syntax:** `PING [message]`\n\n**Description:** Returns the server's liveliness response.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/quit/\">QUIT</a></strong> - Closes the connection.</summary>\n\n**Syntax:** `QUIT`\n\n**Description:** Closes the connection.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/reset/\">RESET</a></strong> - Resets the connection.</summary>\n\n**Syntax:** `RESET`\n\n**Description:** Resets the connection.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/select/\">SELECT</a></strong> - Changes the selected database.</summary>\n\n**Syntax:** `SELECT index`\n\n**Description:** Changes the selected database.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>"},{"id":"server-commands","title":"Server commands","role":"content","text":"Server commands provide server management and introspection.\n\n<details>\n<summary><strong><a href=\"/commands/acl-cat/\">ACL CAT</a></strong> - Lists the ACL categories, or the commands inside a category.</summary>\n\n**Syntax:** `ACL CAT [category]`\n\n**Description:** Lists the ACL categories, or the commands inside a category.\n\n**Complexity:** O(1) since the categories and commands are a fixed set.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-deluser/\">ACL DELUSER</a></strong> - Deletes ACL users, and terminates their connections.</summary>\n\n**Syntax:** `ACL DELUSER username [username ...]`\n\n**Description:** Deletes ACL users, and terminates their connections.\n\n**Complexity:** O(1) amortized time considering the typical user.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-dryrun/\">ACL DRYRUN</a></strong> - Simulates the execution of a command by a user, without executing the command.</summary>\n\n**Syntax:** `ACL DRYRUN username command [arg [arg ...]]`\n\n**Description:** Simulates the execution of a command by a user, without executing the command.\n\n**Complexity:** O(1).\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-genpass/\">ACL GENPASS</a></strong> - Generates a pseudorandom, secure password that can be used to identify ACL users.</summary>\n\n**Syntax:** `ACL GENPASS [bits]`\n\n**Description:** Generates a pseudorandom, secure password that can be used to identify ACL users.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-getuser/\">ACL GETUSER</a></strong> - Lists the ACL rules of a user.</summary>\n\n**Syntax:** `ACL GETUSER username`\n\n**Description:** Lists the ACL rules of a user.\n\n**Complexity:** O(N). Where N is the number of password, command and pattern rules that the user has.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-list/\">ACL LIST</a></strong> - Dumps the effective rules in ACL file format.</summary>\n\n**Syntax:** `ACL LIST`\n\n**Description:** Dumps the effective rules in ACL file format.\n\n**Complexity:** O(N). Where N is the number of configured users.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-load/\">ACL LOAD</a></strong> - Reloads the rules from the configured ACL file.</summary>\n\n**Syntax:** `ACL LOAD`\n\n**Description:** Reloads the rules from the configured ACL file.\n\n**Complexity:** O(N). Where N is the number of configured users.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-log/\">ACL LOG</a></strong> - Lists recent security events generated due to ACL rules.</summary>\n\n**Syntax:** `ACL LOG [count | RESET]`\n\n**Description:** Lists recent security events generated due to ACL rules.\n\n**Complexity:** O(N) with N being the number of entries shown.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-save/\">ACL SAVE</a></strong> - Saves the effective ACL rules in the configured ACL file.</summary>\n\n**Syntax:** `ACL SAVE`\n\n**Description:** Saves the effective ACL rules in the configured ACL file.\n\n**Complexity:** O(N). Where N is the number of configured users.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-setuser/\">ACL SETUSER</a></strong> - Creates and modifies an ACL user and its rules.</summary>\n\n**Syntax:** `ACL SETUSER username [rule [rule ...]]`\n\n**Description:** Creates and modifies an ACL user and its rules.\n\n**Complexity:** O(N). Where N is the number of rules provided.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-users/\">ACL USERS</a></strong> - Lists all ACL users.</summary>\n\n**Syntax:** `ACL USERS`\n\n**Description:** Lists all ACL users.\n\n**Complexity:** O(N). Where N is the number of configured users.\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/acl-whoami/\">ACL WHOAMI</a></strong> - Returns the authenticated username of the current connection.</summary>\n\n**Syntax:** `ACL WHOAMI`\n\n**Description:** Returns the authenticated username of the current connection.\n\n**Complexity:** O(1)\n\n**Since:** 6.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bgrewriteaof/\">BGREWRITEAOF</a></strong> - Asynchronously rewrites the append-only file to disk.</summary>\n\n**Syntax:** `BGREWRITEAOF`\n\n**Description:** Asynchronously rewrites the append-only file to disk.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/bgsave/\">BGSAVE</a></strong> - Asynchronously saves the database(s) to disk.</summary>\n\n**Syntax:** `BGSAVE [SCHEDULE]`\n\n**Description:** Asynchronously saves the database(s) to disk.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command/\">COMMAND</a></strong> - Returns detailed information about all commands.</summary>\n\n**Syntax:** `COMMAND`\n\n**Description:** Returns detailed information about all commands.\n\n**Complexity:** O(N) where N is the total number of Redis commands\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-count/\">COMMAND COUNT</a></strong> - Returns a count of commands.</summary>\n\n**Syntax:** `COMMAND COUNT`\n\n**Description:** Returns a count of commands.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-docs/\">COMMAND DOCS</a></strong> - Returns documentary information about one, multiple or all commands.</summary>\n\n**Syntax:** `COMMAND DOCS [command-name [command-name ...]]`\n\n**Description:** Returns documentary information about one, multiple or all commands.\n\n**Complexity:** O(N) where N is the number of commands to look up\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-getkeys/\">COMMAND GETKEYS</a></strong> - Extracts the key names from an arbitrary command.</summary>\n\n**Syntax:** `COMMAND GETKEYS command [arg [arg ...]]`\n\n**Description:** Extracts the key names from an arbitrary command.\n\n**Complexity:** O(N) where N is the number of arguments to the command\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-getkeysandflags/\">COMMAND GETKEYSANDFLAGS</a></strong> - Extracts the key names and access flags for an arbitrary command.</summary>\n\n**Syntax:** `COMMAND GETKEYSANDFLAGS command [arg [arg ...]]`\n\n**Description:** Extracts the key names and access flags for an arbitrary command.\n\n**Complexity:** O(N) where N is the number of arguments to the command\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-info/\">COMMAND INFO</a></strong> - Returns information about one, multiple or all commands.</summary>\n\n**Syntax:** `COMMAND INFO [command-name [command-name ...]]`\n\n**Description:** Returns information about one, multiple or all commands.\n\n**Complexity:** O(N) where N is the number of commands to look up\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/command-list/\">COMMAND LIST</a></strong> - Returns a list of command names.</summary>\n\n**Syntax:** `COMMAND LIST [FILTERBY <MODULE module-name | ACLCAT category |\n  PATTERN pattern>]`\n\n**Description:** Returns a list of command names.\n\n**Complexity:** O(N) where N is the total number of Redis commands\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/config-get/\">CONFIG GET</a></strong> - Returns the effective values of configuration parameters.</summary>\n\n**Syntax:** `CONFIG GET parameter [parameter ...]`\n\n**Description:** Returns the effective values of configuration parameters.\n\n**Complexity:** O(N) when N is the number of configuration parameters provided\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/config-resetstat/\">CONFIG RESETSTAT</a></strong> - Resets the server's statistics.</summary>\n\n**Syntax:** `CONFIG RESETSTAT`\n\n**Description:** Resets the server's statistics.\n\n**Complexity:** O(1)\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/config-rewrite/\">CONFIG REWRITE</a></strong> - Persists the effective configuration to file.</summary>\n\n**Syntax:** `CONFIG REWRITE`\n\n**Description:** Persists the effective configuration to file.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/config-set/\">CONFIG SET</a></strong> - Sets configuration parameters in-flight.</summary>\n\n**Syntax:** `CONFIG SET parameter value [parameter value ...]`\n\n**Description:** Sets configuration parameters in-flight.\n\n**Complexity:** O(N) when N is the number of configuration parameters provided\n\n**Since:** 2.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/dbsize/\">DBSIZE</a></strong> - Returns the number of keys in the database.</summary>\n\n**Syntax:** `DBSIZE`\n\n**Description:** Returns the number of keys in the database.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/failover/\">FAILOVER</a></strong> - Starts a coordinated failover from a server to one of its replicas.</summary>\n\n**Syntax:** `FAILOVER [TO host port [FORCE]] [ABORT] [TIMEOUT milliseconds]`\n\n**Description:** Starts a coordinated failover from a server to one of its replicas.\n\n**Complexity:** O(1)\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/flushall/\">FLUSHALL</a></strong> - Removes all keys from all databases.</summary>\n\n**Syntax:** `FLUSHALL [ASYNC | SYNC]`\n\n**Description:** Removes all keys from all databases.\n\n**Complexity:** O(N) where N is the total number of keys in all databases\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/flushdb/\">FLUSHDB</a></strong> - Remove all keys from the current database.</summary>\n\n**Syntax:** `FLUSHDB [ASYNC | SYNC]`\n\n**Description:** Remove all keys from the current database.\n\n**Complexity:** O(N) where N is the number of keys in the selected database\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hotkeys/\">HOTKEYS</a></strong> - A container for hotkeys tracking commands.</summary>\n\n**Syntax:** `HOTKEYS`\n\n**Description:** A container for hotkeys tracking commands.\n\n**Complexity:** Depends on subcommand.\n\n**Since:** 8.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hotkeys-get/\">HOTKEYS GET</a></strong> - Returns lists of top K hotkeys depending on metrics chosen in HOTKEYS START command.</summary>\n\n**Syntax:** `HOTKEYS GET`\n\n**Description:** Returns lists of top K hotkeys depending on metrics chosen in HOTKEYS START command.\n\n**Complexity:** O(K) where K is the number of hotkeys returned.\n\n**Since:** 8.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hotkeys-reset/\">HOTKEYS RESET</a></strong> - Release the resources used for hotkey tracking.</summary>\n\n**Syntax:** `HOTKEYS RESET`\n\n**Description:** Release the resources used for hotkey tracking.\n\n**Complexity:** O(1)\n\n**Since:** 8.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hotkeys-start/\">HOTKEYS START</a></strong> - Starts hotkeys tracking.</summary>\n\n**Syntax:** `HOTKEYS START METRICS count [CPU] [NET] [COUNT k] [DURATION seconds] [SAMPLE ratio] [SLOTS count slot [slot ...]]`\n\n**Description:** Starts hotkeys tracking.\n\n**Complexity:** O(1)\n\n**Since:** 8.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/hotkeys-stop/\">HOTKEYS STOP</a></strong> - Stops hotkeys tracking.</summary>\n\n**Syntax:** `HOTKEYS STOP`\n\n**Description:** Stops hotkeys tracking.\n\n**Complexity:** O(1)\n\n**Since:** 8.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/info/\">INFO</a></strong> - Returns information and statistics about the server.</summary>\n\n**Syntax:** `INFO [section [section ...]]`\n\n**Description:** Returns information and statistics about the server.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lastsave/\">LASTSAVE</a></strong> - Returns the Unix timestamp of the last successful save to disk.</summary>\n\n**Syntax:** `LASTSAVE`\n\n**Description:** Returns the Unix timestamp of the last successful save to disk.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-doctor/\">LATENCY DOCTOR</a></strong> - Returns a human-readable latency analysis report.</summary>\n\n**Syntax:** `LATENCY DOCTOR`\n\n**Description:** Returns a human-readable latency analysis report.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-graph/\">LATENCY GRAPH</a></strong> - Returns a latency graph for an event.</summary>\n\n**Syntax:** `LATENCY GRAPH event`\n\n**Description:** Returns a latency graph for an event.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-histogram/\">LATENCY HISTOGRAM</a></strong> - Returns the cumulative distribution of latencies of a subset or all commands.</summary>\n\n**Syntax:** `LATENCY HISTOGRAM [command [command ...]]`\n\n**Description:** Returns the cumulative distribution of latencies of a subset or all commands.\n\n**Complexity:** O(N) where N is the number of commands with latency information being retrieved.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-history/\">LATENCY HISTORY</a></strong> - Returns timestamp-latency samples for an event.</summary>\n\n**Syntax:** `LATENCY HISTORY event`\n\n**Description:** Returns timestamp-latency samples for an event.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-latest/\">LATENCY LATEST</a></strong> - Returns the latest latency samples for all events.</summary>\n\n**Syntax:** `LATENCY LATEST`\n\n**Description:** Returns the latest latency samples for all events.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/latency-reset/\">LATENCY RESET</a></strong> - Resets the latency data for one or more events.</summary>\n\n**Syntax:** `LATENCY RESET [event [event ...]]`\n\n**Description:** Resets the latency data for one or more events.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.13\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/lolwut/\">LOLWUT</a></strong> - Displays computer art and the Redis version</summary>\n\n**Syntax:** `LOLWUT [VERSION version]`\n\n**Description:** Displays computer art and the Redis version\n\n**Complexity:** \n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-doctor/\">MEMORY DOCTOR</a></strong> - Outputs a memory problems report.</summary>\n\n**Syntax:** `MEMORY DOCTOR`\n\n**Description:** Outputs a memory problems report.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-malloc-stats/\">MEMORY MALLOC-STATS</a></strong> - Returns the allocator statistics.</summary>\n\n**Syntax:** `MEMORY MALLOC-STATS`\n\n**Description:** Returns the allocator statistics.\n\n**Complexity:** Depends on how much memory is allocated, could be slow\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-purge/\">MEMORY PURGE</a></strong> - Asks the allocator to release memory.</summary>\n\n**Syntax:** `MEMORY PURGE`\n\n**Description:** Asks the allocator to release memory.\n\n**Complexity:** Depends on how much memory is allocated, could be slow\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-stats/\">MEMORY STATS</a></strong> - Returns details about memory usage.</summary>\n\n**Syntax:** `MEMORY STATS`\n\n**Description:** Returns details about memory usage.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/memory-usage/\">MEMORY USAGE</a></strong> - Estimates the memory usage of a key.</summary>\n\n**Syntax:** `MEMORY USAGE key [SAMPLES count]`\n\n**Description:** Estimates the memory usage of a key.\n\n**Complexity:** O(N) where N is the number of samples.\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/module-list/\">MODULE LIST</a></strong> - Returns all loaded modules.</summary>\n\n**Syntax:** `MODULE LIST`\n\n**Description:** Returns all loaded modules.\n\n**Complexity:** O(N) where N is the number of loaded modules.\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/module-load/\">MODULE LOAD</a></strong> - Loads a module.</summary>\n\n**Syntax:** `MODULE LOAD path [arg [arg ...]]`\n\n**Description:** Loads a module.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/module-loadex/\">MODULE LOADEX</a></strong> - Loads a module using extended parameters.</summary>\n\n**Syntax:** `MODULE LOADEX path [CONFIG name value [CONFIG name value ...]]\n  [ARGS args [args ...]]`\n\n**Description:** Loads a module using extended parameters.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/module-unload/\">MODULE UNLOAD</a></strong> - Unloads a module.</summary>\n\n**Syntax:** `MODULE UNLOAD name`\n\n**Description:** Unloads a module.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/monitor/\">MONITOR</a></strong> - Listens for all requests received by the server in real-time.</summary>\n\n**Syntax:** `MONITOR`\n\n**Description:** Listens for all requests received by the server in real-time.\n\n**Complexity:** \n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/psync/\">PSYNC</a></strong> - An internal command used in replication.</summary>\n\n**Syntax:** `PSYNC replicationid offset`\n\n**Description:** An internal command used in replication.\n\n**Complexity:** \n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/replconf/\">REPLCONF</a></strong> - An internal command for configuring the replication stream.</summary>\n\n**Syntax:** `REPLCONF`\n\n**Description:** An internal command for configuring the replication stream.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/replicaof/\">REPLICAOF</a></strong> - Configures a server as replica of another, or promotes it to a master.</summary>\n\n**Syntax:** `REPLICAOF <host port | NO ONE>`\n\n**Description:** Configures a server as replica of another, or promotes it to a master.\n\n**Complexity:** O(1)\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/restore-asking/\">RESTORE-ASKING</a></strong> - An internal command for migrating keys in a cluster.</summary>\n\n**Syntax:** `RESTORE-ASKING key ttl serialized-value [REPLACE] [ABSTTL]\n  [IDLETIME seconds] [FREQ frequency]`\n\n**Description:** An internal command for migrating keys in a cluster.\n\n**Complexity:** O(1) to create the new key and additional O(N*M) to reconstruct the serialized value, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1). However for sorted set values the complexity is O(N*M*log(N)) because inserting values into sorted sets is O(log(N)).\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/role/\">ROLE</a></strong> - Returns the replication role.</summary>\n\n**Syntax:** `ROLE`\n\n**Description:** Returns the replication role.\n\n**Complexity:** O(1)\n\n**Since:** 2.8.12\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/save/\">SAVE</a></strong> - Synchronously saves the database(s) to disk.</summary>\n\n**Syntax:** `SAVE`\n\n**Description:** Synchronously saves the database(s) to disk.\n\n**Complexity:** O(N) where N is the total number of keys in all databases\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/shutdown/\">SHUTDOWN</a></strong> - Synchronously saves the database(s) to disk and shuts down the Redis server.</summary>\n\n**Syntax:** `SHUTDOWN [NOSAVE | SAVE] [NOW] [FORCE] [ABORT]`\n\n**Description:** Synchronously saves the database(s) to disk and shuts down the Redis server.\n\n**Complexity:** O(N) when saving, where N is the total number of keys in all databases when saving data, otherwise O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/slaveof/\">SLAVEOF</a></strong> - Sets a Redis server as a replica of another, or promotes it to being a master.</summary>\n\n**Syntax:** `SLAVEOF <host port | NO ONE>`\n\n**Description:** Sets a Redis server as a replica of another, or promotes it to being a master.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/slowlog-get/\">SLOWLOG GET</a></strong> - Returns the slow log's entries.</summary>\n\n**Syntax:** `SLOWLOG GET [count]`\n\n**Description:** Returns the slow log's entries.\n\n**Complexity:** O(N) where N is the number of entries returned\n\n**Since:** 2.2.12\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/slowlog-len/\">SLOWLOG LEN</a></strong> - Returns the number of entries in the slow log.</summary>\n\n**Syntax:** `SLOWLOG LEN`\n\n**Description:** Returns the number of entries in the slow log.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.12\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/slowlog-reset/\">SLOWLOG RESET</a></strong> - Clears all entries from the slow log.</summary>\n\n**Syntax:** `SLOWLOG RESET`\n\n**Description:** Clears all entries from the slow log.\n\n**Complexity:** O(N) where N is the number of entries in the slowlog\n\n**Since:** 2.2.12\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/swapdb/\">SWAPDB</a></strong> - Swaps two Redis databases.</summary>\n\n**Syntax:** `SWAPDB index1 index2`\n\n**Description:** Swaps two Redis databases.\n\n**Complexity:** O(N) where N is the count of clients watching or blocking on keys from both databases.\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sync/\">SYNC</a></strong> - An internal command used in replication.</summary>\n\n**Syntax:** `SYNC`\n\n**Description:** An internal command used in replication.\n\n**Complexity:** \n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/time/\">TIME</a></strong> - Returns the server time.</summary>\n\n**Syntax:** `TIME`\n\n**Description:** Returns the server time.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>"},{"id":"cluster-commands","title":"Cluster commands","role":"content","text":"Cluster commands manage Redis Cluster operations.\n\n<details>\n<summary><strong><a href=\"/commands/asking/\">ASKING</a></strong> - Signals that a cluster client is following an -ASK redirect.</summary>\n\n**Syntax:** `ASKING`\n\n**Description:** Signals that a cluster client is following an -ASK redirect.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-addslots/\">CLUSTER ADDSLOTS</a></strong> - Assigns new hash slots to a node.</summary>\n\n**Syntax:** `CLUSTER ADDSLOTS slot [slot ...]`\n\n**Description:** Assigns new hash slots to a node.\n\n**Complexity:** O(N) where N is the total number of hash slot arguments\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-addslotsrange/\">CLUSTER ADDSLOTSRANGE</a></strong> - Assigns new hash slot ranges to a node.</summary>\n\n**Syntax:** `CLUSTER ADDSLOTSRANGE start-slot end-slot [start-slot end-slot ...]`\n\n**Description:** Assigns new hash slot ranges to a node.\n\n**Complexity:** O(N) where N is the total number of the slots between the start slot and end slot arguments.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-bumpepoch/\">CLUSTER BUMPEPOCH</a></strong> - Advances the cluster config epoch.</summary>\n\n**Syntax:** `CLUSTER BUMPEPOCH`\n\n**Description:** Advances the cluster config epoch.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-count-failure-reports/\">CLUSTER COUNT-FAILURE-REPORTS</a></strong> - Returns the number of active failure reports active for a node.</summary>\n\n**Syntax:** `CLUSTER COUNT-FAILURE-REPORTS node-id`\n\n**Description:** Returns the number of active failure reports active for a node.\n\n**Complexity:** O(N) where N is the number of failure reports\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-countkeysinslot/\">CLUSTER COUNTKEYSINSLOT</a></strong> - Returns the number of keys in a hash slot.</summary>\n\n**Syntax:** `CLUSTER COUNTKEYSINSLOT slot`\n\n**Description:** Returns the number of keys in a hash slot.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-delslots/\">CLUSTER DELSLOTS</a></strong> - Sets hash slots as unbound for a node.</summary>\n\n**Syntax:** `CLUSTER DELSLOTS slot [slot ...]`\n\n**Description:** Sets hash slots as unbound for a node.\n\n**Complexity:** O(N) where N is the total number of hash slot arguments\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-delslotsrange/\">CLUSTER DELSLOTSRANGE</a></strong> - Sets hash slot ranges as unbound for a node.</summary>\n\n**Syntax:** `CLUSTER DELSLOTSRANGE start-slot end-slot [start-slot end-slot ...]`\n\n**Description:** Sets hash slot ranges as unbound for a node.\n\n**Complexity:** O(N) where N is the total number of the slots between the start slot and end slot arguments.\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-failover/\">CLUSTER FAILOVER</a></strong> - Forces a replica to perform a manual failover of its master.</summary>\n\n**Syntax:** `CLUSTER FAILOVER [FORCE | TAKEOVER]`\n\n**Description:** Forces a replica to perform a manual failover of its master.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-flushslots/\">CLUSTER FLUSHSLOTS</a></strong> - Deletes all slots information from a node.</summary>\n\n**Syntax:** `CLUSTER FLUSHSLOTS`\n\n**Description:** Deletes all slots information from a node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-forget/\">CLUSTER FORGET</a></strong> - Removes a node from the nodes table.</summary>\n\n**Syntax:** `CLUSTER FORGET node-id`\n\n**Description:** Removes a node from the nodes table.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-getkeysinslot/\">CLUSTER GETKEYSINSLOT</a></strong> - Returns the key names in a hash slot.</summary>\n\n**Syntax:** `CLUSTER GETKEYSINSLOT slot count`\n\n**Description:** Returns the key names in a hash slot.\n\n**Complexity:** O(N) where N is the number of requested keys\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-info/\">CLUSTER INFO</a></strong> - Returns information about the state of a node.</summary>\n\n**Syntax:** `CLUSTER INFO`\n\n**Description:** Returns information about the state of a node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-keyslot/\">CLUSTER KEYSLOT</a></strong> - Returns the hash slot for a key.</summary>\n\n**Syntax:** `CLUSTER KEYSLOT key`\n\n**Description:** Returns the hash slot for a key.\n\n**Complexity:** O(N) where N is the number of bytes in the key\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-links/\">CLUSTER LINKS</a></strong> - Returns a list of all TCP links to and from peer nodes.</summary>\n\n**Syntax:** `CLUSTER LINKS`\n\n**Description:** Returns a list of all TCP links to and from peer nodes.\n\n**Complexity:** O(N) where N is the total number of Cluster nodes\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-meet/\">CLUSTER MEET</a></strong> - Forces a node to handshake with another node.</summary>\n\n**Syntax:** `CLUSTER MEET ip port [cluster-bus-port]`\n\n**Description:** Forces a node to handshake with another node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-migration/\">CLUSTER MIGRATION</a></strong> - Start, monitor, and cancel atomic slot migration tasks.</summary>\n\n**Syntax:** `CLUSTER MIGRATION <IMPORT start-slot end-slot\n  [start-slot end-slot ...] | CANCEL <ID task-id | ALL> |\n  STATUS <[ID task-id] | [ALL]>>`\n\n**Description:** Start, monitor, and cancel atomic slot migration tasks.\n\n**Complexity:** O(N) where N is the total number of the slots between the start slot and end slot arguments.\n\n**Since:** 8.4.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-myid/\">CLUSTER MYID</a></strong> - Returns the ID of a node.</summary>\n\n**Syntax:** `CLUSTER MYID`\n\n**Description:** Returns the ID of a node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-myshardid/\">CLUSTER MYSHARDID</a></strong> - Returns the shard ID of a node.</summary>\n\n**Syntax:** `CLUSTER MYSHARDID`\n\n**Description:** Returns the shard ID of a node.\n\n**Complexity:** O(1)\n\n**Since:** 7.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-nodes/\">CLUSTER NODES</a></strong> - Returns the cluster configuration for a node.</summary>\n\n**Syntax:** `CLUSTER NODES`\n\n**Description:** Returns the cluster configuration for a node.\n\n**Complexity:** O(N) where N is the total number of Cluster nodes\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-replicas/\">CLUSTER REPLICAS</a></strong> - Lists the replica nodes of a master node.</summary>\n\n**Syntax:** `CLUSTER REPLICAS node-id`\n\n**Description:** Lists the replica nodes of a master node.\n\n**Complexity:** O(N) where N is the number of replicas.\n\n**Since:** 5.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-replicate/\">CLUSTER REPLICATE</a></strong> - Configure a node as replica of a master node.</summary>\n\n**Syntax:** `CLUSTER REPLICATE node-id`\n\n**Description:** Configure a node as replica of a master node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-reset/\">CLUSTER RESET</a></strong> - Resets a node.</summary>\n\n**Syntax:** `CLUSTER RESET [HARD | SOFT]`\n\n**Description:** Resets a node.\n\n**Complexity:** O(N) where N is the number of known nodes. The command may execute a FLUSHALL as a side effect.\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-saveconfig/\">CLUSTER SAVECONFIG</a></strong> - Forces a node to save the cluster configuration to disk.</summary>\n\n**Syntax:** `CLUSTER SAVECONFIG`\n\n**Description:** Forces a node to save the cluster configuration to disk.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-set-config-epoch/\">CLUSTER SET-CONFIG-EPOCH</a></strong> - Sets the configuration epoch for a new node.</summary>\n\n**Syntax:** `CLUSTER SET-CONFIG-EPOCH config-epoch`\n\n**Description:** Sets the configuration epoch for a new node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-setslot/\">CLUSTER SETSLOT</a></strong> - Binds a hash slot to a node.</summary>\n\n**Syntax:** `CLUSTER SETSLOT slot <IMPORTING node-id | MIGRATING node-id |\n  NODE node-id | STABLE>`\n\n**Description:** Binds a hash slot to a node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-shards/\">CLUSTER SHARDS</a></strong> - Returns the mapping of cluster slots to shards.</summary>\n\n**Syntax:** `CLUSTER SHARDS`\n\n**Description:** Returns the mapping of cluster slots to shards.\n\n**Complexity:** O(N) where N is the total number of cluster nodes\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-slaves/\">CLUSTER SLAVES</a></strong> - Lists the replica nodes of a master node.</summary>\n\n**Syntax:** `CLUSTER SLAVES node-id`\n\n**Description:** Lists the replica nodes of a master node.\n\n**Complexity:** O(N) where N is the number of replicas.\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-slot-stats/\">CLUSTER SLOT-STATS</a></strong> - Return an array of slot usage statistics for slots assigned to the current node.</summary>\n\n**Syntax:** `CLUSTER SLOT-STATS <SLOTSRANGE start-slot end-slot | ORDERBY metric\n  [LIMIT limit] [ASC | DESC]>`\n\n**Description:** Return an array of slot usage statistics for slots assigned to the current node.\n\n**Complexity:** O(N) where N is the total number of slots based on arguments. O(N*log(N)) with ORDERBY subcommand.\n\n**Since:** 8.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/cluster-slots/\">CLUSTER SLOTS</a></strong> - Returns the mapping of cluster slots to nodes.</summary>\n\n**Syntax:** `CLUSTER SLOTS`\n\n**Description:** Returns the mapping of cluster slots to nodes.\n\n**Complexity:** O(N) where N is the total number of Cluster nodes\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/readonly/\">READONLY</a></strong> - Enables read-only queries for a connection to a Redis Cluster replica node.</summary>\n\n**Syntax:** `READONLY`\n\n**Description:** Enables read-only queries for a connection to a Redis Cluster replica node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/readwrite/\">READWRITE</a></strong> - Enables read-write queries for a connection to a Reids Cluster replica node.</summary>\n\n**Syntax:** `READWRITE`\n\n**Description:** Enables read-write queries for a connection to a Reids Cluster replica node.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>"},{"id":"generic-commands","title":"Generic commands","role":"content","text":"Generic commands work across all data types.\n\n<details>\n<summary><strong><a href=\"/commands/copy/\">COPY</a></strong> - Copies the value of a key to a new key.</summary>\n\n**Syntax:** `COPY source destination [DB destination-db] [REPLACE]`\n\n**Description:** Copies the value of a key to a new key.\n\n**Complexity:** O(N) worst case for collections, where N is the number of nested items. O(1) for string values.\n\n**Since:** 6.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/del/\">DEL</a></strong> - Deletes one or more keys.</summary>\n\n**Syntax:** `DEL key [key ...]`\n\n**Description:** Deletes one or more keys.\n\n**Complexity:** O(N) where N is the number of keys that will be removed. When a key to remove holds a value other than a string, the individual complexity for this key is O(M) where M is the number of elements in the list, set, sorted set or hash. Removing a single key that holds a string value is O(1).\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/dump/\">DUMP</a></strong> - Returns a serialized representation of the value stored at a key.</summary>\n\n**Syntax:** `DUMP key`\n\n**Description:** Returns a serialized representation of the value stored at a key.\n\n**Complexity:** O(1) to access the key and additional O(N*M) to serialize it, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1).\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/exists/\">EXISTS</a></strong> - Determines whether one or more keys exist.</summary>\n\n**Syntax:** `EXISTS key [key ...]`\n\n**Description:** Determines whether one or more keys exist.\n\n**Complexity:** O(N) where N is the number of keys to check.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/expire/\">EXPIRE</a></strong> - Sets the expiration time of a key in seconds.</summary>\n\n**Syntax:** `EXPIRE key seconds [NX | XX | GT | LT]`\n\n**Description:** Sets the expiration time of a key in seconds.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/expireat/\">EXPIREAT</a></strong> - Sets the expiration time of a key to a Unix timestamp.</summary>\n\n**Syntax:** `EXPIREAT key unix-time-seconds [NX | XX | GT | LT]`\n\n**Description:** Sets the expiration time of a key to a Unix timestamp.\n\n**Complexity:** O(1)\n\n**Since:** 1.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/expiretime/\">EXPIRETIME</a></strong> - Returns the expiration time of a key as a Unix timestamp.</summary>\n\n**Syntax:** `EXPIRETIME key`\n\n**Description:** Returns the expiration time of a key as a Unix timestamp.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/keys/\">KEYS</a></strong> - Returns all key names that match a pattern.</summary>\n\n**Syntax:** `KEYS pattern`\n\n**Description:** Returns all key names that match a pattern.\n\n**Complexity:** O(N) with N being the number of keys in the database, under the assumption that the key names in the database and the given pattern have limited length.\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/migrate/\">MIGRATE</a></strong> - Atomically transfers a key from one Redis instance to another.</summary>\n\n**Syntax:** `MIGRATE host port <key | \"\"> destination-db timeout [COPY] [REPLACE]\n  [AUTH password | AUTH2 username password] [KEYS key [key ...]]`\n\n**Description:** Atomically transfers a key from one Redis instance to another.\n\n**Complexity:** This command actually executes a DUMP+DEL in the source instance, and a RESTORE in the target instance. See the pages of these commands for time complexity. Also an O(N) data transfer between the two instances is performed.\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/move/\">MOVE</a></strong> - Moves a key to another database.</summary>\n\n**Syntax:** `MOVE key db`\n\n**Description:** Moves a key to another database.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/object-encoding/\">OBJECT ENCODING</a></strong> - Returns the internal encoding of a Redis object.</summary>\n\n**Syntax:** `OBJECT ENCODING key`\n\n**Description:** Returns the internal encoding of a Redis object.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.3\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/object-freq/\">OBJECT FREQ</a></strong> - Returns the logarithmic access frequency counter of a Redis object.</summary>\n\n**Syntax:** `OBJECT FREQ key`\n\n**Description:** Returns the logarithmic access frequency counter of a Redis object.\n\n**Complexity:** O(1)\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/object-idletime/\">OBJECT IDLETIME</a></strong> - Returns the time since the last access to a Redis object.</summary>\n\n**Syntax:** `OBJECT IDLETIME key`\n\n**Description:** Returns the time since the last access to a Redis object.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.3\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/object-refcount/\">OBJECT REFCOUNT</a></strong> - Returns the reference count of a value of a key.</summary>\n\n**Syntax:** `OBJECT REFCOUNT key`\n\n**Description:** Returns the reference count of a value of a key.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.3\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/persist/\">PERSIST</a></strong> - Removes the expiration time of a key.</summary>\n\n**Syntax:** `PERSIST key`\n\n**Description:** Removes the expiration time of a key.\n\n**Complexity:** O(1)\n\n**Since:** 2.2.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pexpire/\">PEXPIRE</a></strong> - Sets the expiration time of a key in milliseconds.</summary>\n\n**Syntax:** `PEXPIRE key milliseconds [NX | XX | GT | LT]`\n\n**Description:** Sets the expiration time of a key in milliseconds.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pexpireat/\">PEXPIREAT</a></strong> - Sets the expiration time of a key to a Unix milliseconds timestamp.</summary>\n\n**Syntax:** `PEXPIREAT key unix-time-milliseconds [NX | XX | GT | LT]`\n\n**Description:** Sets the expiration time of a key to a Unix milliseconds timestamp.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pexpiretime/\">PEXPIRETIME</a></strong> - Returns the expiration time of a key as a Unix milliseconds timestamp.</summary>\n\n**Syntax:** `PEXPIRETIME key`\n\n**Description:** Returns the expiration time of a key as a Unix milliseconds timestamp.\n\n**Complexity:** O(1)\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/pttl/\">PTTL</a></strong> - Returns the expiration time in milliseconds of a key.</summary>\n\n**Syntax:** `PTTL key`\n\n**Description:** Returns the expiration time in milliseconds of a key.\n\n**Complexity:** O(1)\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/randomkey/\">RANDOMKEY</a></strong> - Returns a random key name from the database.</summary>\n\n**Syntax:** `RANDOMKEY`\n\n**Description:** Returns a random key name from the database.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/rename/\">RENAME</a></strong> - Renames a key and overwrites the destination.</summary>\n\n**Syntax:** `RENAME key newkey`\n\n**Description:** Renames a key and overwrites the destination.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/renamenx/\">RENAMENX</a></strong> - Renames a key only when the target key name doesn't exist.</summary>\n\n**Syntax:** `RENAMENX key newkey`\n\n**Description:** Renames a key only when the target key name doesn't exist.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/restore/\">RESTORE</a></strong> - Creates a key from the serialized representation of a value.</summary>\n\n**Syntax:** `RESTORE key ttl serialized-value [REPLACE] [ABSTTL]\n  [IDLETIME seconds] [FREQ frequency]`\n\n**Description:** Creates a key from the serialized representation of a value.\n\n**Complexity:** O(1) to create the new key and additional O(N*M) to reconstruct the serialized value, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1). However for sorted set values the complexity is O(N*M*log(N)) because inserting values into sorted sets is O(log(N)).\n\n**Since:** 2.6.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-6-2-commands/\">Redis 6.2 Commands Reference</a></strong> - Complete list of all Redis commands available in version 6.2, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 6.2, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-7-2-commands/\">Redis 7.2 Commands Reference</a></strong> - Complete list of all Redis commands available in version 7.2, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 7.2, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-7-4-commands/\">Redis 7.4 Commands Reference</a></strong> - Complete list of all Redis commands available in version 7.4, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 7.4, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-8-0-commands/\">Redis 8.0 Commands Reference</a></strong> - Complete list of all Redis commands available in version 8.0, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 8.0, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-8-2-commands/\">Redis 8.2 Commands Reference</a></strong> - Complete list of all Redis commands available in version 8.2, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 8.2, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-8-4-commands/\">Redis 8.4 Commands Reference</a></strong> - Complete list of all Redis commands available in version 8.4, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 8.4, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/redis-8-6-commands/\">Redis 8.6 Commands Reference</a></strong> - Complete list of all Redis commands available in version 8.6, organized by functional group</summary>\n\n**Syntax:** ``\n\n**Description:** Complete list of all Redis commands available in version 8.6, organized by functional group\n\n**Complexity:** \n\n**Since:** \n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/scan/\">SCAN</a></strong> - Iterates over the key names in the database.</summary>\n\n**Syntax:** `SCAN cursor [MATCH pattern] [COUNT count] [TYPE type]`\n\n**Description:** Iterates over the key names in the database.\n\n**Complexity:** O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.\n\n**Since:** 2.8.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sort/\">SORT</a></strong> - Sorts the elements in a list, a set, or a sorted set, optionally storing the result.</summary>\n\n**Syntax:** `SORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern\n  ...]] [ASC | DESC] [ALPHA] [STORE destination]`\n\n**Description:** Sorts the elements in a list, a set, or a sorted set, optionally storing the result.\n\n**Complexity:** O(N+M*log(M)) where N is the number of elements in the list or set to sort, and M the number of returned elements. When the elements are not sorted, complexity is O(N).\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/sort_ro/\">SORT_RO</a></strong> - Returns the sorted elements of a list, a set, or a sorted set.</summary>\n\n**Syntax:** `SORT RO key [BY pattern] [LIMIT offset count] [GET pattern [GET\n  pattern ...]] [ASC | DESC] [ALPHA]`\n\n**Description:** Returns the sorted elements of a list, a set, or a sorted set.\n\n**Complexity:** O(N+M*log(M)) where N is the number of elements in the list or set to sort, and M the number of returned elements. When the elements are not sorted, complexity is O(N).\n\n**Since:** 7.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/touch/\">TOUCH</a></strong> - Returns the number of existing keys out of those specified after updating the time they were last accessed.</summary>\n\n**Syntax:** `TOUCH key [key ...]`\n\n**Description:** Returns the number of existing keys out of those specified after updating the time they were last accessed.\n\n**Complexity:** O(N) where N is the number of keys that will be touched.\n\n**Since:** 3.2.1\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/ttl/\">TTL</a></strong> - Returns the expiration time in seconds of a key.</summary>\n\n**Syntax:** `TTL key`\n\n**Description:** Returns the expiration time in seconds of a key.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/type/\">TYPE</a></strong> - Determines the type of value stored at a key.</summary>\n\n**Syntax:** `TYPE key`\n\n**Description:** Determines the type of value stored at a key.\n\n**Complexity:** O(1)\n\n**Since:** 1.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/unlink/\">UNLINK</a></strong> - Asynchronously deletes one or more keys.</summary>\n\n**Syntax:** `UNLINK key [key ...]`\n\n**Description:** Asynchronously deletes one or more keys.\n\n**Complexity:** O(1) for each key removed regardless of its size. Then the command does O(N) work in a different thread in order to reclaim memory, where N is the number of allocations the deleted objects where composed of.\n\n**Since:** 4.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/wait/\">WAIT</a></strong> - Blocks until the asynchronous replication of all preceding write commands sent by the connection is completed.</summary>\n\n**Syntax:** `WAIT numreplicas timeout`\n\n**Description:** Blocks until the asynchronous replication of all preceding write commands sent by the connection is completed.\n\n**Complexity:** O(1)\n\n**Since:** 3.0.0\n\n</details>\n\n<details>\n<summary><strong><a href=\"/commands/waitaof/\">WAITAOF</a></strong> - Blocks until all of the preceding write commands sent by the connection are written to the append-only file of the master and/or replicas.</summary>\n\n**Syntax:** `WAITAOF numlocal numreplicas timeout`\n\n**Description:** Blocks until all of the preceding write commands sent by the connection are written to the append-only file of the master and/or replicas.\n\n**Complexity:** O(1)\n\n**Since:** 7.2.0\n\n</details>"}],"examples":[]}
{"schema_version":2,"id":"commands/rename","title":"RENAME","url":"https://redis.io/docs/latest/commands/rename/","summary":"Renames a key and overwrites the destination.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"d08c61e6f8338a725baab212415f83c95d7dfe563525ef74caee23494224aa4a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nRenames `key` to `newkey`.\nIt returns an error when `key` does not exist.\nIf `newkey` already exists it is overwritten, when this happens `RENAME` executes an implicit [`DEL`](https://redis.io/docs/latest/commands/del) operation, so if the deleted key contains a very big value it may cause high latency even if `RENAME` itself is usually a constant-time operation.\n\nIn Cluster mode, both `key` and `newkey` must be in the same **hash slot**, meaning that in practice only keys that have the same hash tag can be reliably renamed in cluster."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe key to rename.\n\n</details>\n\n<details open><summary><code>newkey</code></summary>\n\nThe new key name. An existing key with this name is overwritten.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SET mykey \"Hello\"\nOK\nredis> RENAME mykey myotherkey\nOK\nredis> GET myotherkey\n\"Hello\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active\\*</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active\\*</nobr></span> | For Active-Active or clustered databases, the original key and new key must be in the same hash slot.<br /><br />\\*Not supported for stream consumer group info. |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[]}
{"schema_version":2,"id":"commands/renamenx","title":"RENAMENX","url":"https://redis.io/docs/latest/commands/renamenx/","summary":"Renames a key only when the target key name doesn't exist.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"9f56b96273c5eee8a30a460a568a5ba77ba9ecb855469c35db079b58a30d7e15","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nRenames `key` to `newkey` if `newkey` does not yet exist.\nIt returns an error when `key` does not exist.\n\nIn Cluster mode, both `key` and `newkey` must be in the same **hash slot**, meaning that in practice only keys that have the same hash tag can be reliably renamed in cluster."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe key to rename.\n\n</details>\n\n<details open><summary><code>newkey</code></summary>\n\nThe new key name. The command fails if a key with this name already exists.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SET mykey \"Hello\"\nOK\nredis> SET myotherkey \"World\"\nOK\nredis> RENAMENX mykey myotherkey\n(integer) 0\nredis> GET myotherkey\n\"World\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | For Active-Active or clustered databases, the original key and new key must be in the same hash slot. |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): `1` if _key_ was renamed to _newkey_.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0` if _newkey_ already exists.\n\n**RESP3:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): `1` if _key_ was renamed to _newkey_.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0` if _newkey_ already exists."}],"examples":[]}
{"schema_version":2,"id":"commands/replconf","title":"REPLCONF","url":"https://redis.io/docs/latest/commands/replconf/","summary":"An internal command for configuring the replication stream.","since":"3.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"d582974fb326b2679ba96cf37715c77534f739eb7bf6cd108ed5f057f66247ff","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `REPLCONF` command is an internal command.\nIt is used by a Redis master to configure a connected replica."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[]}
{"schema_version":2,"id":"commands/replicaof","title":"REPLICAOF","url":"https://redis.io/docs/latest/commands/replicaof/","summary":"Configures a server as replica of another, or promotes it to a master.","since":"5.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-06-15T06:26:34-07:00","page_type":"content","content_hash":"2b02f643e1b9a2521762451fa4cc4bbccfbc3cf0f34a0a02158e801e997e7e92","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `REPLICAOF` command can change the replication settings of a replica on the fly.\n\nIf a Redis server is already acting as replica, the command `REPLICAOF` NO ONE will turn off the replication, turning the Redis server into a MASTER.  In the proper form `REPLICAOF` hostname port will make the server a replica of another server listening at the specified hostname and port.\n\nIf a server is already a replica of some master, `REPLICAOF` hostname port will stop the replication against the old server and start the synchronization against the new one, discarding the old dataset.\n\nThe form `REPLICAOF` NO ONE will stop replication, turning the server into a MASTER, but will not discard the already replicated data. So, if the old master stops working, it is possible to turn the replica into a master and set the application to use this new master in read/write. Later when the other Redis server is fixed, it can be reconfigured to work as a replica."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>host port | NO ONE</code></summary>\n\nThe host and port of the master to replicate from, or `NO ONE` to stop replicating and turn the server back into a master.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> REPLICAOF NO ONE\n\"OK\"\n\n> REPLICAOF 127.0.0.1 6799\n\"OK\"","section_id":"examples"}]}
{"schema_version":2,"id":"commands/reset","title":"RESET","url":"https://redis.io/docs/latest/commands/reset/","summary":"Resets the connection.","since":"6.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"04eac358c5dedf321bf6ada9b83a43cfbd7790971378230e3e0d58d2adf67da9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This command performs a full reset of the connection's server-side context, \nmimicking the effect of disconnecting and reconnecting again.\n\nWhen the command is called from a regular client connection, it does the\nfollowing:\n\n* Discards the current [`MULTI`](https://redis.io/docs/latest/commands/multi) transaction block, if one exists.\n* Unwatches all keys [`WATCH`](https://redis.io/docs/latest/commands/watch)ed by the connection.\n* Disables [`CLIENT TRACKING`](https://redis.io/docs/latest/commands/client-tracking), if in use.\n* Sets the connection to [`READWRITE`](https://redis.io/docs/latest/commands/readwrite) mode.\n* Cancels the connection's [`ASKING`](https://redis.io/docs/latest/commands/asking) mode, if previously set.\n* Sets [`CLIENT REPLY`](https://redis.io/docs/latest/commands/client-reply) to `ON`.\n* Sets the protocol version to RESP2.\n* [`SELECT`](https://redis.io/docs/latest/commands/select)s database 0.\n* Exits [`MONITOR`](https://redis.io/docs/latest/commands/monitor) mode, when applicable.\n* Aborts Pub/Sub's subscription state ([`SUBSCRIBE`](https://redis.io/docs/latest/commands/subscribe) and [`PSUBSCRIBE`](https://redis.io/docs/latest/commands/psubscribe)), when\n  appropriate.\n* Deauthenticates the connection, requiring a call [`AUTH`](https://redis.io/docs/latest/commands/auth) to reauthenticate when\n  authentication is enabled.\n* Turns off `NO-EVICT` mode.\n* Turns off `NO-TOUCH` mode."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `RESET`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `RESET`."}],"examples":[]}
{"schema_version":2,"id":"commands/restore","title":"RESTORE","url":"https://redis.io/docs/latest/commands/restore/","summary":"Creates a key from the serialized representation of a value.","since":"2.6.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"0c2d05219608e7d0d54f78b8bb4e6050545376b6ae5258846a349704dbfc1745","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Create a key associated with a value that is obtained by deserializing the\nprovided serialized value (obtained via [`DUMP`](https://redis.io/docs/latest/commands/dump)).\n\nIf `ttl` is 0 the key is created without any expire, otherwise the specified\nexpire time (in milliseconds) is set.\n\nIf the `ABSTTL` modifier was used, `ttl` should represent an absolute\n[Unix timestamp](http://en.wikipedia.org/wiki/Unix_time) (in milliseconds) in which the key will expire.\n\nFor eviction purposes, you may use the `IDLETIME` or `FREQ` modifiers. See\n[`OBJECT`](https://redis.io/docs/latest/commands/object) for more information.\n\n`RESTORE` will return a \"Target key name is busy\" error when `key` already\nexists unless you use the `REPLACE` modifier.\n\n`RESTORE` checks the RDB version and data checksum.\nIf they don't match an error is returned."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key to create.\n\n</details>\n\n<details open><summary><code>ttl</code></summary>\n\nThe time to live for the key, in milliseconds. `0` means no expiration. With `ABSTTL`, this is an absolute Unix timestamp in milliseconds.\n\n</details>\n\n<details open><summary><code>serialized-value</code></summary>\n\nThe serialized value, as produced by `DUMP`.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>REPLACE</code></summary>\n\nReplace the key if it already exists.\n\n</details>\n\n<details open><summary><code>ABSTTL</code></summary>\n\nInterpret `ttl` as an absolute Unix timestamp in milliseconds rather than a relative time.\n\n</details>\n\n<details open><summary><code>IDLETIME seconds</code></summary>\n\nSet the key's idle time (for LRU eviction) to this many seconds.\n\n</details>\n\n<details open><summary><code>FREQ frequency</code></summary>\n\nSet the key's access-frequency counter (for LFU eviction) to this value.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\">&#x274c; Active-Active\\*</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\">&#x274c; Active-Active\\*</nobr></span> | \\*Only supported for module keys. |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> DEL mykey\n0\nredis> RESTORE mykey 0 \"\\n\\x17\\x17\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x03\\x00\\\n                        x00\\xc0\\x01\\x00\\x04\\xc0\\x02\\x00\\x04\\xc0\\x03\\x00\\\n                        xff\\x04\\x00u#<\\xc0;.\\xe9\\xdd\"\nOK\nredis> TYPE mykey\nlist\nredis> LRANGE mykey 0 -1\n1) \"1\"\n2) \"2\"\n3) \"3\"","section_id":"examples"}]}
{"schema_version":2,"id":"commands/restore-asking","title":"RESTORE-ASKING","url":"https://redis.io/docs/latest/commands/restore-asking/","summary":"An internal command for migrating keys in a cluster.","since":"3.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-06-15T06:26:34-07:00","page_type":"content","content_hash":"b29c0b9043fa9d3aaf2238d3db43d49ecf8bfee950ac30f4420779b31722b7b2","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `RESTORE-ASKING` command is an internal command.\nIt is used by a Redis cluster master during slot migration."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key to create.\n\n</details>\n\n<details open><summary><code>ttl</code></summary>\n\nThe time to live for the key, in milliseconds. `0` means no expiration. With `ABSTTL`, this is an absolute Unix timestamp in milliseconds.\n\n</details>\n\n<details open><summary><code>serialized-value</code></summary>\n\nThe serialized value, as produced by `DUMP`.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>REPLACE</code></summary>\n\nReplace the key if it already exists.\n\n</details>\n\n<details open><summary><code>ABSTTL</code></summary>\n\nInterpret `ttl` as an absolute Unix timestamp in milliseconds rather than a relative time.\n\n</details>\n\n<details open><summary><code>IDLETIME seconds</code></summary>\n\nSet the key's idle time (for LRU eviction) to this many seconds.\n\n</details>\n\n<details open><summary><code>FREQ frequency</code></summary>\n\nSet the key's access-frequency counter (for LFU eviction) to this value.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[]}
{"schema_version":2,"id":"commands/role","title":"ROLE","url":"https://redis.io/docs/latest/commands/role/","summary":"Returns the replication role.","since":"2.8.12","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"12be1f8298e497c685d695a83ed6ee852c3d22324cfd407c3a352602e5e07300","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Provide information on the role of a Redis instance in the context of replication, by returning if the instance is currently a `master`, `slave`, or `sentinel`. The command also returns additional information about the state of the replication (if the role is master or slave) or the list of monitored master names (if the role is sentinel)."},{"id":"output-format","title":"Output format","role":"returns","text":"The command returns an array of elements. The first element is the role of\nthe instance, as one of the following three strings:\n\n* \"master\"\n* \"slave\"\n* \"sentinel\"\n\nThe additional elements of the array depends on the role."},{"id":"master-output","title":"Master output","role":"content","text":"An example of output when `ROLE` is called in a master instance:\n\n[code example]\n\nThe master output is composed of the following parts:\n\n1. The string `master`.\n2. The current master replication offset, which is an offset that masters and replicas share to understand, in partial resynchronizations, the part of the replication stream the replicas needs to fetch to continue.\n3. An array composed of three elements array representing the connected replicas. Every sub-array contains the replica IP, port, and the last acknowledged replication offset."},{"id":"output-of-the-command-on-replicas","title":"Output of the command on replicas","role":"returns","text":"An example of output when `ROLE` is called in a replica instance:\n\n[code example]\n\nThe replica output is composed of the following parts:\n\n1. The string `slave`, because of backward compatibility (see note at the end of this page).\n2. The IP of the master.\n3. The port number of the master.\n4. The state of the replication from the point of view of the master, that can be `connect` (the instance needs to connect to its master), `connecting` (the master-replica connection is in progress), `sync` (the master and replica are trying to perform the synchronization), `connected` (the replica is online).\n5. The amount of data received from the replica so far in terms of master replication offset."},{"id":"sentinel-output","title":"Sentinel output","role":"content","text":"An example of Sentinel output:\n\n[code example]\n\nThe sentinel output is composed of the following parts:\n\n1. The string `sentinel`.\n2. An array of master names monitored by this Sentinel instance."},{"id":"examples","title":"Examples","role":"example","text":"redis> ROLE\n1) \"master\"\n2) (integer) 0\n3) (empty array)\n\n\n**A note about the word slave used in this man page**: Starting with Redis 5, if not for backward compatibility, the Redis project no longer uses the word slave. Unfortunately in this command the word slave is part of the protocol, so we'll be able to remove such occurrences only when this API will be naturally deprecated."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): where the first element is one of `master`, `slave`, or `sentinel`, and the additional elements are role-specific as illustrated above.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): where the first element is one of `master`, `slave`, or `sentinel`, and the additional elements are role-specific as illustrated above."}],"examples":[{"id":"master-output-ex0","language":"plaintext","code":"1) \"master\"\n2) (integer) 3129659\n3) 1) 1) \"127.0.0.1\"\n      2) \"9001\"\n      3) \"3129242\"\n   2) 1) \"127.0.0.1\"\n      2) \"9002\"\n      3) \"3129543\"","section_id":"master-output"},{"id":"output-of-the-command-on-replicas-ex0","language":"plaintext","code":"1) \"slave\"\n2) \"127.0.0.1\"\n3) (integer) 9000\n4) \"connected\"\n5) (integer) 3167038","section_id":"output-of-the-command-on-replicas"},{"id":"sentinel-output-ex0","language":"plaintext","code":"1) \"sentinel\"\n2) 1) \"resque-master\"\n   2) \"html-fragments-master\"\n   3) \"stats-master\"\n   4) \"metadata-master\"","section_id":"sentinel-output"}]}
{"schema_version":2,"id":"commands/rpop","title":"RPOP","url":"https://redis.io/docs/latest/commands/rpop/","summary":"Returns and removes the last elements of a list. Deletes the list if the last element was popped.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-08T12:05:04+02:00","page_type":"content","content_hash":"2f80546e2ff2aca2424221ccfdadd88639dce65d840fcc89ffc3ad1305b0393f","sections":[{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the list.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>count</code></summary>\n\nThe number of elements to pop. Without it, a single element is popped.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"Foundational: Remove and return the last element(s) from a list using RPOP (supports optional count parameter to pop multiple elements from tail)\n\n**Difficulty:** Beginner\n\n**Commands:** RPUSH, RPOP, LRANGE\n\n**Complexity:**\n- RPUSH: O(1)\n- RPOP: O(N)\n- LRANGE: O(S+N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): if the key does not exist.\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): when called without the _count_ argument, the value of the last element.\n* [Array reply](../../develop/reference/protocol-spec#arrays): when called with the _count_ argument, a list of popped elements.\n\n**RESP3:**\n\nOne of the following:\n* [Null reply](../../develop/reference/protocol-spec#nulls): if the key does not exist.\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): when called without the _count_ argument, the value of the last element.\n* [Array reply](../../develop/reference/protocol-spec#arrays): when called with the _count_ argument, a list of popped elements."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> RPUSH mylist \"one\" \"two\" \"three\" \"four\" \"five\"\n(integer) 5\nredis> RPOP mylist\n\"five\"\nredis> RPOP mylist 2\n1) \"four\"\n2) \"three\"\nredis> LRANGE mylist 0 -1\n1) \"one\"\n2) \"two\"","section_id":"examples"},{"id":"examples-ex1","language":"csharp","code":"long rPopResult1 = db.ListRightPush(\"mylist\", [\"one\", \"two\", \"three\", \"four\", \"five\"]);\n        Console.WriteLine(rPopResult1); // >>> 5\n\n        RedisValue rPopResult2 = db.ListRightPop(\"mylist\");\n        Console.WriteLine(rPopResult2); // >>> five\n\n        RedisValue[] rPopResult3 = db.ListRightPop(\"mylist\", 2);\n        Console.WriteLine($\"[{string.Join(\", \", rPopResult3)}]\");\n        // >>> [four, three]\n\n        RedisValue[] rPopResult4 = db.ListRange(\"mylist\", 0, -1);\n        Console.WriteLine($\"[{string.Join(\", \", rPopResult4)}]\");\n        // >>> [one, two]","section_id":"examples"},{"id":"examples-ex2","language":"go","code":"rPushResult, err := rdb.RPush(ctx, \"mylist\",\n\t\t\"one\", \"two\", \"three\", \"four\", \"five\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(rPushResult) // >>> 5\n\n\trPopResult, err := rdb.RPop(ctx, \"mylist\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(rPopResult) // >>> five\n\n\trPopCountResult, err := rdb.RPopCount(ctx, \"mylist\", 2).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(rPopCountResult) // >>> [four three]\n\n\tlRangeResult, err := rdb.LRange(ctx, \"mylist\", 0, -1).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(lRangeResult) // >>> [one two]","section_id":"examples"},{"id":"examples-ex3","language":"java","code":"CompletableFuture<Void> rpop = asyncCommands.rpush(\"mylist\", \"one\", \"two\", \"three\", \"four\", \"five\")\n                    .thenCompose(res18 -> {\n                        System.out.println(res18); // >>> 5\n\n                        return asyncCommands.rpop(\"mylist\");\n                    }).thenCompose(res19 -> {\n                        System.out.println(res19); // >>> five\n\n                        return asyncCommands.rpop(\"mylist\", 2);\n                    }).thenCompose(res20 -> {\n                        System.out.println(res20); // >>> [four, three]\n\n                        return asyncCommands.lrange(\"mylist\", 0, -1);\n                    })\n                    .thenAccept(res21 -> System.out.println(res21)) // >>> [one, two]\n                    .toCompletableFuture();","section_id":"examples"},{"id":"examples-ex4","language":"java","code":"Mono<Void> rpop = reactiveCommands.rpush(\"mylist\", \"one\", \"two\", \"three\", \"four\", \"five\").doOnNext(res18 -> {\n                System.out.println(res18); // >>> 5\n            }).flatMap(res18 -> reactiveCommands.rpop(\"mylist\")).doOnNext(res19 -> {\n                System.out.println(res19); // >>> five\n            }).flatMap(res19 -> reactiveCommands.rpop(\"mylist\", 2).collectList()).doOnNext(res20 -> {\n                System.out.println(res20); // >>> [four, three]\n            }).flatMap(res20 -> reactiveCommands.lrange(\"mylist\", 0, -1).collectList()).doOnNext(res21 -> {\n                System.out.println(res21); // >>> [one, two]\n            }).then();","section_id":"examples"},{"id":"examples-ex5","language":"java","code":"long rPopResult1 = jedis.rpush(\n            \"mylist\", \"one\", \"two\", \"three\", \"four\", \"five\"\n        );\n        System.out.println(rPopResult1); // >>> 5\n\n        String rPopResult2 = jedis.rpop(\"mylist\");\n        System.out.println(rPopResult2); // >>> five\n\n        List<String> rPopResult3 = jedis.rpop(\"mylist\", 2);\n        System.out.println(rPopResult3); // >>> [four, three]\n\n        List<String> rPopResult4 = jedis.lrange(\"mylist\", 0, -1);\n        System.out.println(rPopResult4); // >>> [one, two]","section_id":"examples"},{"id":"examples-ex6","language":"javascript","code":"const res21 = await client.rPush('mylist', [\"one\", \"two\", \"three\", \"four\", \"five\"]);\nconsole.log(res21); // 5\n\nconst res22 = await client.rPop('mylist');\nconsole.log(res22); // 'five'\n\nconst res23 = await client.rPopCount('mylist', 2);\nconsole.log(res23); // [ 'four', 'three' ]\n\nconst res24 = await client.lRange('mylist', 0, -1);\nconsole.log(res24); // [ 'one', 'two' ]","section_id":"examples"},{"id":"examples-ex7","language":"javascript","code":"const res21 = await redis.rpush('mylist', 'one', 'two', 'three', 'four', 'five');\nconsole.log(res21); // >>> 5\n\nconst res22 = await redis.rpop('mylist');\nconsole.log(res22); // >>> five\n\nconst res23 = await redis.rpop('mylist', 2);\nconsole.log(res23); // >>> ['four', 'three']\n\nconst res24 = await redis.lrange('mylist', 0, -1);\nconsole.log(res24); // >>> ['one', 'two']","section_id":"examples"},{"id":"examples-ex8","language":"javascript","code":"const res21 = await client.rPush('mylist', [\"one\", \"two\", \"three\", \"four\", \"five\"]);\nconsole.log(res21); // 5\n\nconst res22 = await client.rPop('mylist');\nconsole.log(res22); // 'five'\n\nconst res23 = await client.rPopCount('mylist', 2);\nconsole.log(res23); // [ 'four', 'three' ]\n\nconst res24 = await client.lRange('mylist', 0, -1);\nconsole.log(res24); // [ 'one', 'two' ]","section_id":"examples"},{"id":"examples-ex9","language":"javascript","code":"const res21 = await redis.rpush('mylist', 'one', 'two', 'three', 'four', 'five');\nconsole.log(res21); // >>> 5\n\nconst res22 = await redis.rpop('mylist');\nconsole.log(res22); // >>> five\n\nconst res23 = await redis.rpop('mylist', 2);\nconsole.log(res23); // >>> ['four', 'three']\n\nconst res24 = await redis.lrange('mylist', 0, -1);\nconsole.log(res24); // >>> ['one', 'two']","section_id":"examples"},{"id":"examples-ex10","language":"php","code":"$res21 = $r->rpush('mylist', 'one', 'two', 'three', 'four', 'five');\n        echo $res21 . PHP_EOL;\n        // >>> 5\n\n        $res22 = $r->rpop('mylist');\n        echo $res22 . PHP_EOL;\n        // >>> five\n\n        $res23 = $r->rpop('mylist', 2);\n        echo json_encode($res23) . PHP_EOL;\n        // >>> [\"four\",\"three\"]\n\n        $res24 = $r->lrange('mylist', 0, -1);\n        echo json_encode($res24) . PHP_EOL;\n        // >>> [\"one\",\"two\"]","section_id":"examples"},{"id":"examples-ex11","language":"python","code":"res18 = r.rpush(\"mylist\", *[\"one\", \"two\", \"three\", \"four\", \"five\"])\nprint(res18) # >>> 5\n\nres19 = r.rpop(\"mylist\")\nprint(res19) # >>> \"five\"\n\nres20 = r.rpop(\"mylist\", 2)\nprint(res20) # >>> ['four', 'three']\n\nres21 = r.lrange(\"mylist\", 0, -1)\nprint(res21)  # >>> [ \"one\", \"two\" ]","section_id":"examples"},{"id":"examples-ex12","language":"ruby","code":"res21 = r.rpush('mylist', ['one', 'two', 'three', 'four', 'five'])\nputs res21 # >>> 5\n\nres22 = r.rpop('mylist')\nputs res22 # >>> five\n\nres23 = r.rpop('mylist', 2)\nputs res23.inspect # >>> [\"four\", \"three\"]\n\nres24 = r.lrange('mylist', 0, -1)\nputs res24.inspect # >>> [\"one\", \"two\"]","section_id":"examples"},{"id":"examples-ex13","language":"rust","code":"if let Ok(res21) = r.rpush(\"mylist\", &[\"one\", \"two\", \"three\", \"four\", \"five\"]).await {\n            let res21: i32 = res21;\n            println!(\"{res21}\"); // >>> 5\n        }\n\n        if let Ok(res22) = r.rpop(\"mylist\", None).await {\n            let res22: String = res22;\n            println!(\"{res22}\"); // >>> five\n        }\n\n        if let Ok(res23) = r.rpop(\"mylist\", NonZeroUsize::new(2)).await {\n            let res23: Vec<String> = res23;\n            println!(\"{res23:?}\"); // >>> [\"four\", \"three\"]\n        }\n\n        if let Ok(res24) = r.lrange(\"mylist\", 0, -1).await {\n            let res24: Vec<String> = res24;\n            println!(\"{res24:?}\"); // >>> [\"one\", \"two\"]\n        }","section_id":"examples"},{"id":"examples-ex14","language":"rust","code":"if let Ok(res21) = r.rpush(\"mylist\", &[\"one\", \"two\", \"three\", \"four\", \"five\"]) {\n            let res21: i32 = res21;\n            println!(\"{res21}\"); // >>> 5\n        }\n\n        if let Ok(res22) = r.rpop(\"mylist\", None) {\n            let res22: String = res22;\n            println!(\"{res22}\"); // >>> five\n        }\n\n        if let Ok(res23) = r.rpop(\"mylist\", NonZeroUsize::new(2)) {\n            let res23: Vec<String> = res23;\n            println!(\"{res23:?}\"); // >>> [\"four\", \"three\"]\n        }\n\n        if let Ok(res24) = r.lrange(\"mylist\", 0, -1) {\n            let res24: Vec<String> = res24;\n            println!(\"{res24:?}\"); // >>> [\"one\", \"two\"]\n        }","section_id":"examples"}]}
{"schema_version":2,"id":"commands/rpoplpush","title":"RPOPLPUSH","url":"https://redis.io/docs/latest/commands/rpoplpush/","summary":"Returns the last element of a list after removing and pushing it to another list. Deletes the list if the last element was popped.","since":"1.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"7f5e84a14dfe1eb34985a9d7296e56fe91e9fd0667b0a046c68ee8bcff60ce05","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nAtomically returns and removes the last element (tail) of the list stored at\n`source`, and pushes the element at the first element (head) of the list stored\nat `destination`.\n\nFor example: consider `source` holding the list `a,b,c`, and `destination`\nholding the list `x,y,z`.\nExecuting `RPOPLPUSH` results in `source` holding `a,b` and `destination`\nholding `c,x,y,z`.\n\nIf `source` does not exist, the value `nil` is returned and no operation is\nperformed.\nIf `source` and `destination` are the same, the operation is equivalent to\nremoving the last element from the list and pushing it as first element of the\nlist, so it can be considered as a list rotation command."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>source</code></summary>\n\nThe key of the source list.\n\n</details>\n\n<details open><summary><code>destination</code></summary>\n\nThe key of the destination list.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> RPUSH mylist \"one\"\n(integer) 1\nredis> RPUSH mylist \"two\"\n(integer) 2\nredis> RPUSH mylist \"three\"\n(integer) 3\nredis> RPOPLPUSH mylist myotherlist\n\"three\"\nredis> LRANGE mylist 0 -1\n1) \"one\"\n2) \"two\"\nredis> LRANGE myotherlist 0 -1\n1) \"three\""},{"id":"details","title":"Details","role":"content","text":""},{"id":"pattern-reliable-queue","title":"Pattern: reliable queue","role":"content","text":"Redis is often used as a messaging server to implement processing of background\njobs or other kinds of messaging tasks.\nA simple form of queue is often obtained pushing values into a list in the\nproducer side, and waiting for this values in the consumer side using [`RPOP`](https://redis.io/docs/latest/commands/rpop)\n(using polling), or [`BRPOP`](https://redis.io/docs/latest/commands/brpop) if the client is better served by a blocking\noperation.\n\nHowever in this context the obtained queue is not _reliable_ as messages can\nbe lost, for example in the case there is a network problem or if the consumer\ncrashes just after the message is received but before it can be processed.\n\n`RPOPLPUSH` (or [`BRPOPLPUSH`](https://redis.io/docs/latest/commands/brpoplpush) for the blocking variant) offers a way to avoid\nthis problem: the consumer fetches the message and at the same time pushes it\ninto a _processing_ list.\nIt will use the [`LREM`](https://redis.io/docs/latest/commands/lrem) command in order to remove the message from the\n_processing_ list once the message has been processed.\n\nAn additional client may monitor the _processing_ list for items that remain\nthere for too much time, pushing timed out items into the queue\nagain if needed."},{"id":"pattern-circular-list","title":"Pattern: circular list","role":"content","text":"Using `RPOPLPUSH` with the same source and destination key, a client can visit\nall the elements of an N-elements list, one after the other, in O(N) without\ntransferring the full list from the server to the client using a single [`LRANGE`](https://redis.io/docs/latest/commands/lrange)\noperation.\n\nThe above pattern works even if one or both of the following conditions occur:\n\n* There are multiple clients rotating the list: they'll fetch different \n  elements, until all the elements of the list are visited, and the process \n  restarts.\n* Other clients are actively pushing new items at the end of the list.\n\nThe above makes it very simple to implement a system where a set of items must\nbe processed by N workers continuously as fast as possible.\nAn example is a monitoring system that must check that a set of web sites are\nreachable, with the smallest delay possible, using a number of parallel workers.\n\nNote that this implementation of workers is trivially scalable and reliable,\nbecause even if a message is lost the item is still in the queue and will be\nprocessed at the next iteration."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Deprecated as of Redis v6.2.0. |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the element being popped and pushed.\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): if the source list is empty.\n\n**RESP3:**\n\nOne of the following:\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the element being popped and pushed.\n* [Null reply](../../develop/reference/protocol-spec#nulls): if the source list is empty."}],"examples":[]}
{"schema_version":2,"id":"commands/rpush","title":"RPUSH","url":"https://redis.io/docs/latest/commands/rpush/","summary":"Appends one or more elements to a list. Creates the key if it doesn't exist.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-08T12:05:04+02:00","page_type":"content","content_hash":"d97f2001a4592a32d18a80f12a8c9ec4db82948a9bd4370b5483a0f1a4a00a85","sections":[{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the list.\n\n</details>\n\n<details open><summary><code>element [element ...]</code></summary>\n\nOne or more values to append to the list.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"Foundational: Add one or more elements to the tail of a list using RPUSH (creates list if needed, returns new list length)\n\n**Difficulty:** Beginner\n\n**Commands:** RPUSH, LRANGE\n\n**Complexity:**\n- RPUSH: O(1)\n- LRANGE: O(S+N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the length of the list after the push operation.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the length of the list after the push operation."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> RPUSH mylist \"hello\"\n(integer) 1\nredis> RPUSH mylist \"world\"\n(integer) 2\nredis> LRANGE mylist 0 -1\n1) \"hello\"\n2) \"world\"","section_id":"examples"},{"id":"examples-ex1","language":"csharp","code":"long rPushResult1 = db.ListRightPush(\"mylist\", \"hello\");\n        Console.WriteLine(rPushResult1); // >>> 1\n\n        long rPushResult2 = db.ListRightPush(\"mylist\", \"world\");\n        Console.WriteLine(rPushResult2); // >>> 2\n\n        RedisValue[] rPushResult3 = db.ListRange(\"mylist\", 0, -1);\n        Console.WriteLine($\"[{string.Join(\", \", rPushResult3)}]\");\n        // >>> [hello, world]","section_id":"examples"},{"id":"examples-ex2","language":"go","code":"rPushResult1, err := rdb.RPush(ctx, \"mylist\", \"Hello\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(rPushResult1) // >>> 1\n\n\trPushResult2, err := rdb.RPush(ctx, \"mylist\", \"World\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(rPushResult2) // >>> 2\n\n\tlRangeResult, err := rdb.LRange(ctx, \"mylist\", 0, -1).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(lRangeResult) // >>> [Hello World]","section_id":"examples"},{"id":"examples-ex3","language":"java","code":"CompletableFuture<Void> rpush = asyncCommands.rpush(\"mylist\", \"hello\").thenCompose(res14 -> {\n                System.out.println(res14); // >>> 1\n\n                return asyncCommands.rpush(\"mylist\", \"world\");\n            }).thenCompose(res15 -> {\n                System.out.println(res15); // >>> 2\n\n                return asyncCommands.lrange(\"mylist\", 0, -1);\n            })\n                    .thenAccept(res16 -> System.out.println(res16)) // >>> [hello, world]\n                    .toCompletableFuture();","section_id":"examples"},{"id":"examples-ex4","language":"java","code":"Mono<Void> rpush = reactiveCommands.rpush(\"mylist\", \"hello\").doOnNext(res14 -> {\n                System.out.println(res14); // >>> 1\n            }).flatMap(res14 -> reactiveCommands.rpush(\"mylist\", \"world\")).doOnNext(res15 -> {\n                System.out.println(res15); // >>> 2\n            }).flatMap(res15 -> reactiveCommands.lrange(\"mylist\", 0, -1).collectList()).doOnNext(res16 -> {\n                System.out.println(res16); // >>> [hello, world]\n            }).then();","section_id":"examples"},{"id":"examples-ex5","language":"java","code":"long rPushResult1 = jedis.rpush(\"mylist\", \"hello\");\n        System.out.println(rPushResult1); // >>> 1\n\n        long rPushResult2 = jedis.rpush(\"mylist\", \"world\");\n        System.out.println(rPushResult2); // >>> 2\n\n        List<String> rPushResult3 = jedis.lrange(\"mylist\", 0, -1);\n        System.out.println(rPushResult3); // >>> [hello, world]","section_id":"examples"},{"id":"examples-ex6","language":"javascript","code":"const res14 = await client.rPush('mylist', 'hello');\nconsole.log(res14); // 1\n\nconst res15 = await client.rPush('mylist', 'world');\nconsole.log(res15); // 2\n\nconst res16 = await client.lRange('mylist', 0, -1);\nconsole.log(res16); // [ 'hello', 'world' ]","section_id":"examples"},{"id":"examples-ex7","language":"javascript","code":"const res14 = await redis.rpush('mylist', 'hello');\nconsole.log(res14); // >>> 1\n\nconst res15 = await redis.rpush('mylist', 'world');\nconsole.log(res15); // >>> 2\n\nconst res16 = await redis.lrange('mylist', 0, -1);\nconsole.log(res16); // >>> ['hello', 'world']","section_id":"examples"},{"id":"examples-ex8","language":"javascript","code":"const res14 = await client.rPush('mylist', 'hello');\nconsole.log(res14); // 1\n\nconst res15 = await client.rPush('mylist', 'world');\nconsole.log(res15); // 2\n\nconst res16 = await client.lRange('mylist', 0, -1);\nconsole.log(res16); // [ 'hello', 'world' ]","section_id":"examples"},{"id":"examples-ex9","language":"javascript","code":"const res14 = await redis.rpush('mylist', 'hello');\nconsole.log(res14); // >>> 1\n\nconst res15 = await redis.rpush('mylist', 'world');\nconsole.log(res15); // >>> 2\n\nconst res16 = await redis.lrange('mylist', 0, -1);\nconsole.log(res16); // >>> ['hello', 'world']","section_id":"examples"},{"id":"examples-ex10","language":"php","code":"$res14 = $r->rpush('mylist', 'hello');\n        echo $res14 . PHP_EOL;\n        // >>> 1\n\n        $res15 = $r->rpush('mylist', 'world');\n        echo $res15 . PHP_EOL;\n        // >>> 2\n\n        $res16 = $r->lrange('mylist', 0, -1);\n        echo json_encode($res16) . PHP_EOL;\n        // >>> [\"hello\",\"world\"]","section_id":"examples"},{"id":"examples-ex11","language":"python","code":"res14 = r.rpush(\"mylist\", \"hello\")\nprint(res14) # >>> 1\n\nres15 = r.rpush(\"mylist\", \"world\")\nprint(res15) # >>> 2\n\nres16 = r.lrange(\"mylist\", 0, -1)\nprint(res16)  # >>> [ \"hello\", \"world\" ]","section_id":"examples"},{"id":"examples-ex12","language":"ruby","code":"res14 = r.rpush('mylist', 'hello')\nputs res14 # >>> 1\n\nres15 = r.rpush('mylist', 'world')\nputs res15 # >>> 2\n\nres16 = r.lrange('mylist', 0, -1)\nputs res16.inspect # >>> [\"hello\", \"world\"]","section_id":"examples"},{"id":"examples-ex13","language":"rust","code":"if let Ok(res14) = r.rpush(\"mylist\", \"hello\").await {\n            let res14: i32 = res14;\n            println!(\"{res14}\"); // >>> 1\n        }\n\n        if let Ok(res15) = r.rpush(\"mylist\", \"world\").await {\n            let res15: i32 = res15;\n            println!(\"{res15}\"); // >>> 2\n        }\n\n        if let Ok(res16) = r.lrange(\"mylist\", 0, -1).await {\n            let res16: Vec<String> = res16;\n            println!(\"{res16:?}\"); // >>> [\"hello\", \"world\"]\n        }","section_id":"examples"},{"id":"examples-ex14","language":"rust","code":"if let Ok(res14) = r.rpush(\"mylist\", \"hello\") {\n            let res14: i32 = res14;\n            println!(\"{res14}\"); // >>> 1\n        }\n\n        if let Ok(res15) = r.rpush(\"mylist\", \"world\") {\n            let res15: i32 = res15;\n            println!(\"{res15}\"); // >>> 2\n        }\n\n        if let Ok(res16) = r.lrange(\"mylist\", 0, -1) {\n            let res16: Vec<String> = res16;\n            println!(\"{res16:?}\"); // >>> [\"hello\", \"world\"]\n        }","section_id":"examples"}]}
{"schema_version":2,"id":"commands/rpushx","title":"RPUSHX","url":"https://redis.io/docs/latest/commands/rpushx/","summary":"Appends an element to a list only when the list exists.","since":"2.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"473726f8796ccfbb602708f79382685203619474bc249eda063b951736c0a45b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Inserts specified values at the tail of the list stored at `key`, only if `key`\nalready exists and holds a list.\nIn contrary to [`RPUSH`](https://redis.io/docs/latest/commands/rpush), no operation will be performed when `key` does not yet\nexist."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the list.\n\n</details>\n\n<details open><summary><code>element [element ...]</code></summary>\n\nOne or more values to append to the list, only if it already exists.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> RPUSH mylist \"Hello\"\n(integer) 1\nredis> RPUSHX mylist \"World\"\n(integer) 2\nredis> RPUSHX myotherlist \"World\"\n(integer) 0\nredis> LRANGE mylist 0 -1\n1) \"Hello\"\n2) \"World\"\nredis> LRANGE myotherlist 0 -1\n(empty array)"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the length of the list after the push operation.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the length of the list after the push operation."}],"examples":[]}
{"schema_version":2,"id":"commands/sadd","title":"SADD","url":"https://redis.io/docs/latest/commands/sadd/","summary":"Adds one or more members to a set. Creates the key if it doesn't exist.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-08T12:05:04+02:00","page_type":"content","content_hash":"4f94dc9c413ff255631706160b157ddcf90ace6a6f9917da492f08a5bdc72f2e","sections":[{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the set.\n\n</details>\n\n<details open><summary><code>member [member ...]</code></summary>\n\nOne or more members to add to the set.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"Foundational: Add one or more members to a set using SADD (creates set if needed, ignores duplicates, returns count of new members)\n\n**Difficulty:** Beginner\n\n**Commands:** SADD, SMEMBERS\n\n**Complexity:**\n- SADD: O(1)\n- SMEMBERS: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of elements that were added to the set, not including all the elements already present in the set.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of elements that were added to the set, not including all the elements already present in the set."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> SADD myset \"Hello\" \"World\"\n(integer) 2\nredis> SADD myset \"World\"\n(integer) 0\nredis> SMEMBERS myset\n1) \"Hello\"\n2) \"World\"","section_id":"examples"},{"id":"examples-ex1","language":"csharp","code":"bool sAddResult1 = db.SetAdd(\"myset\", \"Hello\");\n        Console.WriteLine(sAddResult1); // >>> True\n\n        bool sAddResult2 = db.SetAdd(\"myset\", \"World\");\n        Console.WriteLine(sAddResult2); // >>> True\n\n        bool sAddResult3 = db.SetAdd(\"myset\", \"World\");\n        Console.WriteLine(sAddResult2); // >>> False\n\n        RedisValue[] sAddResult4 = db.SetMembers(\"myset\");\n        Array.Sort(sAddResult4);\n        Console.WriteLine(string.Join(\", \", sAddResult4));\n        // >>> Hello, World","section_id":"examples"},{"id":"examples-ex2","language":"go","code":"sAddResult1, err := rdb.SAdd(ctx, \"myset\", \"Hello\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(sAddResult1) // >>> 1\n\n\tsAddResult2, err := rdb.SAdd(ctx, \"myset\", \"World\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(sAddResult2) // >>> 1\n\n\tsAddResult3, err := rdb.SAdd(ctx, \"myset\", \"World\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(sAddResult3) // >>> 0\n\n\tsMembersResult, err := rdb.SMembers(ctx, \"myset\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(sMembersResult) // >>> [Hello World]","section_id":"examples"},{"id":"examples-ex3","language":"java","code":"CompletableFuture<Void> sadd = asyncCommands.sadd(\"myset\", \"Hello\").thenCompose(r -> {\n                System.out.println(r); // >>> 1\n                return asyncCommands.sadd(\"myset\", \"World\");\n            }).thenCompose(r -> {\n                System.out.println(r); // >>> 1\n                return asyncCommands.sadd(\"myset\", \"World\");\n            }).thenCompose(r -> {\n                System.out.println(r); // >>> 0\n                return asyncCommands.smembers(\"myset\");\n            })\n                    .thenAccept(System.out::println)\n                    // >>> [Hello, World]\n                    .toCompletableFuture();","section_id":"examples"},{"id":"examples-ex4","language":"java","code":"Mono<Void> sadd = reactiveCommands.sadd(\"myset\", \"Hello\").doOnNext(r -> {\n                System.out.println(r); // >>> 1\n            }).flatMap(r -> reactiveCommands.sadd(\"myset\", \"World\")).doOnNext(r -> {\n                System.out.println(r); // >>> 1\n            }).flatMap(r -> reactiveCommands.sadd(\"myset\", \"World\")).doOnNext(r -> {\n                System.out.println(r); // >>> 0\n            }).flatMap(r -> reactiveCommands.smembers(\"myset\").collectList()).doOnNext(r -> {\n                System.out.println(r); // >>> [Hello, World]\n            }).then();","section_id":"examples"},{"id":"examples-ex5","language":"java","code":"long sAddResult1 = jedis.sadd(\"myset\", \"Hello\");\n        System.out.println(sAddResult1); // >>> 1\n\n        long sAddResult2 = jedis.sadd(\"myset\", \"World\");\n        System.out.println(sAddResult2); // >>> 1\n\n        long sAddResult3 = jedis.sadd(\"myset\", \"World\");\n        System.out.println(sAddResult3); // >>> 0\n\n        Set<String> sAddResult4 = jedis.smembers(\"myset\");\n        System.out.println(sAddResult4.stream().sorted().collect(toList()));\n        // >>> [Hello, World]","section_id":"examples"},{"id":"examples-ex6","language":"javascript","code":"const res1 = await client.sAdd('myset', ['Hello', 'World']);\nconsole.log(res1);  // 2\n\nconst res2 = await client.sAdd('myset', ['World']);\nconsole.log(res2);  // 0\n\nconst res3 = await client.sMembers('myset')\nconsole.log(res3);  // ['Hello', 'World']","section_id":"examples"},{"id":"examples-ex7","language":"javascript","code":"const res1 = await redis.sadd('myset', 'Hello', 'World');\nconsole.log(res1); // >>> 2\n\nconst res2 = await redis.sadd('myset', 'World');\nconsole.log(res2); // >>> 0\n\nconst res3 = await redis.smembers('myset');\nconsole.log(res3); // >>> ['Hello', 'World']","section_id":"examples"},{"id":"examples-ex8","language":"javascript","code":"const res1 = await client.sAdd('myset', ['Hello', 'World']);\nconsole.log(res1);  // 2\n\nconst res2 = await client.sAdd('myset', ['World']);\nconsole.log(res2);  // 0\n\nconst res3 = await client.sMembers('myset')\nconsole.log(res3);  // ['Hello', 'World']","section_id":"examples"},{"id":"examples-ex9","language":"javascript","code":"const res1 = await redis.sadd('myset', 'Hello', 'World');\nconsole.log(res1); // >>> 2\n\nconst res2 = await redis.sadd('myset', 'World');\nconsole.log(res2); // >>> 0\n\nconst res3 = await redis.smembers('myset');\nconsole.log(res3); // >>> ['Hello', 'World']","section_id":"examples"},{"id":"examples-ex10","language":"php","code":"$res1 = $r->sadd('myset', ['Hello', 'World']);\n        echo $res1 . PHP_EOL;\n        // >>> 2\n\n        $res2 = $r->sadd('myset', ['World']);\n        echo $res2 . PHP_EOL;\n        // >>> 0\n\n        $res3 = $r->smembers('myset');\n        sort($res3);\n        echo json_encode($res3) . PHP_EOL;\n        // >>> [\"Hello\",\"World\"]","section_id":"examples"},{"id":"examples-ex11","language":"python","code":"res1 = r.sadd(\"myset\", \"Hello\", \"World\")\nprint(res1)  # >>> 2\n\nres2 = r.sadd(\"myset\", \"World\")\nprint(res2)  # >>> 0\n\nres3 = r.smembers(\"myset\")\nprint(res3)  # >>> {'Hello', 'World'}","section_id":"examples"},{"id":"examples-ex12","language":"ruby","code":"res1 = r.sadd('myset', ['Hello', 'World'])\nputs res1 # >>> 2\n\nres2 = r.sadd('myset', ['World'])\nputs res2 # >>> 0\n\nres3 = r.smembers('myset')\nputs res3.inspect # >>> [\"Hello\", \"World\"]","section_id":"examples"},{"id":"examples-ex13","language":"rust","code":"if let Ok(res1) = r.sadd(\"myset\", &[\"Hello\", \"World\"]).await {\n            let res1: i32 = res1;\n            println!(\"{res1}\"); // >>> 2\n        }\n\n        if let Ok(res2) = r.sadd(\"myset\", \"World\").await {\n            let res2: i32 = res2;\n            println!(\"{res2}\"); // >>> 0\n        }\n\n        if let Ok(res3) = r.smembers(\"myset\").await {\n            let res3: HashSet<String> = res3;\n            println!(\"{res3:?}\"); // >>> {\"Hello\", \"World\"}\n        }","section_id":"examples"},{"id":"examples-ex14","language":"rust","code":"if let Ok(res1) = r.sadd(\"myset\", &[\"Hello\", \"World\"]) {\n            let res1: i32 = res1;\n            println!(\"{res1}\"); // >>> 2\n        }\n\n        if let Ok(res2) = r.sadd(\"myset\", \"World\") {\n            let res2: i32 = res2;\n            println!(\"{res2}\"); // >>> 0\n        }\n\n        if let Ok(res3) = r.smembers(\"myset\") {\n            let res3: HashSet<String> = res3;\n            println!(\"{res3:?}\"); // >>> {\"Hello\", \"World\"}\n        }","section_id":"examples"}]}
{"schema_version":2,"id":"commands/save","title":"SAVE","url":"https://redis.io/docs/latest/commands/save/","summary":"Synchronously saves the database(s) to disk.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"e2c8b65b67c4bd1eefcf4b6e9b93d03c34a42effe5db4af34b3b4c8c1187a00a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `SAVE` commands performs a **synchronous** save of the dataset producing a\n_point in time_ snapshot of all the data inside the Redis instance, in the form\nof an RDB file.\n\nYou almost never want to call `SAVE` in production environments where it will\nblock all the other clients.\nInstead usually [`BGSAVE`](https://redis.io/docs/latest/commands/bgsave) is used.\nHowever in case of issues preventing Redis to create the background saving child\n(for instance errors in the fork(2) system call), the `SAVE` command can be a\ngood last resort to perform the dump of the latest dataset.\n\nSee the [persistence documentation](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence) for detailed information."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[]}
{"schema_version":2,"id":"commands/scan","title":"SCAN","url":"https://redis.io/docs/latest/commands/scan/","summary":"Iterates over the key names in the database.","since":"2.8.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"6628d4e627cdd0485014e07bfe9521dacb672e93c97138160e6b92dfe22642ac","sections":[{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>cursor</code></summary>\n\nThe cursor value. Start an iteration with `0`, then use the cursor returned by each call as the argument to the next call.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>MATCH pattern</code></summary>\n\nOnly return keys matching the given glob-style pattern.\n\n</details>\n\n<details open><summary><code>COUNT count</code></summary>\n\nA hint for the number of keys to return per iteration. The default is 10.\n\n</details>\n\n<details open><summary><code>TYPE type</code></summary>\n\nOnly return keys of the given type, such as `string`, `list`, or `set`.\n\n</details>"},{"id":"details","title":"Details","role":"content","text":""},{"id":"scan-basic-usage","title":"SCAN basic usage","role":"content","text":"SCAN is a cursor based iterator. This means that at every call of the command, the server returns an updated cursor that the user needs to use as the cursor argument in the next call.\n\nAn iteration starts when the cursor is set to 0, and terminates when the cursor returned by the server is 0. The following is an example of SCAN iteration:\n\n[code example]\n\nIn the example above, the first call uses zero as a cursor, to start the iteration. The second call uses the cursor returned by the previous call as the first element of the reply, that is, 17.\n\nAs you can see the **SCAN return value** is an array of two values: the first value is the new cursor to use in the next call, the second value is an array of elements.\n\nSince in the second call the returned cursor is 0, the server signaled to the caller that the iteration finished, and the collection was completely explored. Starting an iteration with a cursor value of 0, and calling `SCAN` until the returned cursor is 0 again is called a **full iteration**."},{"id":"return-value","title":"Return value","role":"returns","text":"`SCAN`, [`SSCAN`](https://redis.io/docs/latest/commands/sscan), [`HSCAN`](https://redis.io/docs/latest/commands/hscan) and [`ZSCAN`](https://redis.io/docs/latest/commands/zscan) return a two element multi-bulk reply, where the first element is a string representing an unsigned 64 bit number (the cursor), and the second element is a multi-bulk with an array of elements.\n\n* `SCAN` array of elements is a list of keys.\n* [`SSCAN`](https://redis.io/docs/latest/commands/sscan) array of elements is a list of Set members.\n* [`HSCAN`](https://redis.io/docs/latest/commands/hscan) array of elements contain two elements, a field and a value, for every returned element of the Hash.\n* [`ZSCAN`](https://redis.io/docs/latest/commands/zscan) array of elements contain two elements, a member and its associated score, for every returned element of the Sorted Set."},{"id":"scan-guarantees","title":"Scan guarantees","role":"content","text":"The `SCAN` command, and the other commands in the `SCAN` family, are able to provide to the user a set of guarantees associated to full iterations.\n\n* A full iteration always retrieves all the elements that were present in the collection from the start to the end of a full iteration. This means that if a given element is inside the collection when an iteration is started, and is still there when an iteration terminates, then at some point `SCAN` returned it to the user.\n* A full iteration never returns any element that was NOT present in the collection from the start to the end of a full iteration. So if an element was removed before the start of an iteration, and is never added back to the collection for all the time an iteration lasts, `SCAN` ensures that this element will never be returned.\n\nHowever because `SCAN` has very little state associated (just the cursor) it has the following drawbacks:\n\n* A given element may be returned multiple times. It is up to the application to handle the case of duplicated elements, for example only using the returned elements in order to perform operations that are safe when re-applied multiple times.\n* Elements that were not constantly present in the collection during a full iteration, may be returned or not: it is undefined."},{"id":"number-of-elements-returned-at-every-scan-call","title":"Number of elements returned at every SCAN call","role":"content","text":"`SCAN` family functions do not guarantee that the number of elements returned per call are in a given range. The commands are also allowed to return zero elements, and the client should not consider the iteration complete as long as the returned cursor is not zero.\n\nHowever the number of returned elements is reasonable, that is, in practical terms `SCAN` may return a maximum number of elements in the order of a few tens of elements when iterating a large collection, or may return all the elements of the collection in a single call when the iterated collection is small enough to be internally represented as an encoded data structure (this happens for small Sets, Hashes and Sorted Sets).\n\nHowever there is a way for the user to tune the order of magnitude of the number of returned elements per call using the **COUNT** option."},{"id":"the-count-option","title":"The COUNT option","role":"content","text":"While `SCAN` does not provide guarantees about the number of elements returned at every iteration, it is possible to empirically adjust the behavior of `SCAN` using the **COUNT** option. Basically with COUNT the user specifies the *amount of work that should be done at every call in order to retrieve elements from the collection*. This is **just a hint** for the implementation, however generally speaking this is what you could expect most of the times from the implementation.\n\n* The default `COUNT` value is 10.\n* When iterating the key space, or a Set, Hash or Sorted Set that is big enough to be represented by a hash table, assuming no **MATCH** option is used, the server will usually return *count* or a few more than *count* elements per call. Please check the *why SCAN may return all the elements at once* section later in this document.\n* When iterating Sets encoded as intsets (small sets composed of just integers), or Hashes and Sorted Sets encoded as ziplists (small hashes and sets composed of small individual values), usually all the elements are returned in the first `SCAN` call regardless of the `COUNT` value.\n\nImportant: **there is no need to use the same COUNT value** for every iteration. The caller is free to change the count from one iteration to the other as required, as long as the cursor passed in the next call is the one obtained in the previous call to the command."},{"id":"the-match-option","title":"The MATCH option","role":"content","text":"It is possible to only iterate elements matching a given glob-style pattern, similarly to the behavior of the [`KEYS`](https://redis.io/docs/latest/commands/keys) command that takes a pattern as its only argument.\n\nTo do so, just append the `MATCH <pattern>` arguments at the end of the `SCAN` command (it works with all the `SCAN` family commands).\n\nThis is an example of iteration using **MATCH**:\n\nSet iteration: Iterate set members with pattern matching using SSCAN MATCH (cursor-based iteration, non-blocking)\n\n**Difficulty:** Intermediate\n\n**Commands:** SADD, SSCAN\n\n**Complexity:**\n- SADD: O(1)\n- SSCAN: O(1)\n\n**Available in:** Redis CLI, C, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C\n\n[code example]\n\n##### C#\n\n\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nIt is important to note that the **MATCH** filter is applied after elements are retrieved from the collection, just before returning data to the client. This means that if the pattern matches very little elements inside the collection, `SCAN` will likely return no elements in most iterations. An example is shown below:\n\nKeyspace iteration: Iterate database keys with pattern matching using SCAN MATCH and COUNT (demonstrates cursor iteration with sparse results)\n\n**Difficulty:** Intermediate\n\n**Commands:** SCAN\n\n**Complexity:**\n- SCAN: O(1)\n\n**Available in:** Redis CLI, C, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C\n\n[code example]\n\n##### C#\n\n\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nAs you can see most of the calls returned zero elements, but the last call where a `COUNT` of 1000 was used in order to force the command to do more scanning for that iteration.\n\nWhen using [Redis Cluster](https://redis.io/docs/latest/operate/oss_and_stack/management/scaling), the search is optimized for patterns that imply a single slot.\nIf a pattern can only match keys of one slot,\nRedis only iterates over keys in that slot, rather than the whole database,\nwhen searching for keys matching the pattern.\nFor example, with the pattern `{a}h*llo`, Redis would only try to match it with the keys in slot 15495, which hash tag `{a}` implies.\nTo use pattern with hash tag, see [Hash tags](https://redis.io/docs/latest/operate/oss_and_stack/reference/cluster-spec#hash-tags) in the Cluster specification for more information."},{"id":"the-type-option","title":"The TYPE option","role":"content","text":"You can use the `TYPE` option to ask `SCAN` to only return objects that match a given `type`, allowing you to iterate through the database looking for keys of a specific type. The **TYPE** option is only available on the whole-database `SCAN`, not [`HSCAN`](https://redis.io/docs/latest/commands/hscan) or [`ZSCAN`](https://redis.io/docs/latest/commands/zscan) etc.\n\nThe `type` argument is the same string name that the [`TYPE`](https://redis.io/docs/latest/commands/type) command returns. Note a quirk where some Redis types, such as GeoHashes, HyperLogLogs, Bitmaps, and Bitfields, may internally be implemented using other Redis types, such as a string or zset, so can't be distinguished from other keys of that same type by `SCAN`. For example, a ZSET and GEOHASH:\n\nIterate keyspace by type: Iterate database keys filtered by type using SCAN TYPE (filters keys by data type, useful for type-specific operations)\n\n**Difficulty:** Intermediate\n\n**Commands:** GEOADD, ZADD, TYPE, SCAN\n\n**Complexity:**\n- GEOADD: O(log(N)\n- ZADD: O(log(N)\n- TYPE: O(1)\n- SCAN: O(1)\n\n**Available in:** Redis CLI, C, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C\n\n[code example]\n\n##### C#\n\n\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nIt is important to note that the **TYPE** filter is also applied after elements are retrieved from the database, so the option does not reduce the amount of work the server has to do to complete a full iteration, and for rare types you may receive no elements in many iterations."},{"id":"the-novalues-option","title":"The NOVALUES option","role":"content","text":"When using [`HSCAN`](https://redis.io/docs/latest/commands/hscan), you can use the `NOVALUES` option to make Redis return only the keys in the hash table without their corresponding values.\n\nHash iteration: Iterate hash fields with optional NOVALUES using HSCAN (returns field-value pairs or fields only)\n\n**Difficulty:** Intermediate\n\n**Commands:** HSET, HSCAN\n\n**Complexity:**\n- HSET: O(1)\n- HSCAN: O(1)\n\n**Available in:** Redis CLI, C, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C\n\n[code example]\n\n##### C#\n\n\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"multiple-parallel-iterations","title":"Multiple parallel iterations","role":"content","text":"It is possible for an infinite number of clients to iterate the same collection at the same time, as the full state of the iterator is in the cursor, that is obtained and returned to the client at every call. No server side state is taken at all."},{"id":"terminating-iterations-in-the-middle","title":"Terminating iterations in the middle","role":"content","text":"Since there is no state server side, but the full state is captured by the cursor, the caller is free to terminate an iteration half-way without signaling this to the server in any way. An infinite number of iterations can be started and never terminated without any issue."},{"id":"calling-scan-with-a-corrupted-cursor","title":"Calling SCAN with a corrupted cursor","role":"content","text":"Calling `SCAN` with a broken, negative, out of range, or otherwise invalid cursor, will result in undefined behavior but never in a crash. What will be undefined is that the guarantees about the returned elements can no longer be ensured by the `SCAN` implementation.\n\nThe only valid cursors to use are:\n\n* The cursor value of 0 when starting an iteration.\n* The cursor returned by the previous call to SCAN in order to continue the iteration."},{"id":"guarantee-of-termination","title":"Guarantee of termination","role":"content","text":"The `SCAN` algorithm is guaranteed to terminate only if the size of the iterated collection remains bounded to a given maximum size, otherwise iterating a collection that always grows may result into `SCAN` to never terminate a full iteration.\n\nThis is easy to see intuitively: if the collection grows there is more and more work to do in order to visit all the possible elements, and the ability to terminate the iteration depends on the number of calls to `SCAN` and its COUNT option value compared with the rate at which the collection grows."},{"id":"why-scan-may-return-all-the-items-of-an-aggregate-data-type-in-a-single-call","title":"Why SCAN may return all the items of an aggregate data type in a single call?","role":"content","text":"In the `COUNT` option documentation, we state that sometimes this family of commands may return all the elements of a Set, Hash or Sorted Set at once in a single call, regardless of the `COUNT` option value. The reason why this happens is that the cursor-based iterator can be implemented, and is useful, only when the aggregate data type that we are scanning is represented as a hash table. However Redis uses a [memory optimization](https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/memory-optimization) where small aggregate data types, until they reach a given amount of items or a given max size of single elements, are represented using a compact single-allocation packed encoding. When this is the case, `SCAN` has no meaningful cursor to return, and must iterate the whole data structure at once, so the only sane behavior it has is to return everything in a call.\n\nHowever once the data structures are bigger and are promoted to use real hash tables, the `SCAN` family of commands will resort to the normal behavior. Note that since this special behavior of returning all the elements is true only for small aggregates, it has no effects on the command complexity or latency. However the exact limits to get converted into real hash tables are [user configurable](https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/memory-optimization), so the maximum number of elements you can see returned in a single call depends on how big an aggregate data type could be and still use the packed representation.\n\nAlso note that this behavior is specific of [`SSCAN`](https://redis.io/docs/latest/commands/sscan), [`HSCAN`](https://redis.io/docs/latest/commands/hscan) and [`ZSCAN`](https://redis.io/docs/latest/commands/zscan). `SCAN` itself never shows this behavior because the key space is always represented by hash tables."},{"id":"further-reading","title":"Further reading","role":"content","text":"For more information about managing keys, please refer to the [The Redis Keyspace](https://redis.io/docs/latest/develop/using-commands/keyspace) tutorial."},{"id":"additional-examples","title":"Additional examples","role":"content","text":"Give the following commands, showing iteration of a hash key, a try in the interactive console:\n\n\nredis> HMSET hash name Jack age 33\nOK\nredis> HSCAN hash 0\n1) \"0\"\n2) 1) \"name\"\n   2) \"Jack\"\n   3) \"age\"\n   4) \"33\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): specifically, an array with two elements.\n* The first element is a [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings) that represents an unsigned 64-bit number, the cursor.\n* The second element is an [Array reply](../../develop/reference/protocol-spec#arrays) with the names of scanned keys.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): specifically, an array with two elements.\n* The first element is a [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings) that represents an unsigned 64-bit number, the cursor.\n* The second element is an [Array reply](../../develop/reference/protocol-spec#arrays) with the names of scanned keys."}],"examples":[{"id":"scan-basic-usage-ex0","language":"plaintext","code":"> scan 0\n1) \"17\"\n2)  1) \"key:12\"\n    2) \"key:8\"\n    3) \"key:4\"\n    4) \"key:14\"\n    5) \"key:16\"\n    6) \"key:17\"\n    7) \"key:15\"\n    8) \"key:10\"\n    9) \"key:3\"\n   10) \"key:7\"\n   11) \"key:1\"\n> scan 17\n1) \"0\"\n2) 1) \"key:5\"\n   2) \"key:18\"\n   3) \"key:0\"\n   4) \"key:2\"\n   5) \"key:19\"\n   6) \"key:13\"\n   7) \"key:6\"\n   8) \"key:9\"\n   9) \"key:11\"","section_id":"scan-basic-usage"},{"id":"the-match-option-ex0","language":"plaintext","code":"> sadd myset 1 2 3 foo foobar feelsgood\n(integer) 6\n> sscan myset 0 match f*\n1) \"0\"\n2) 1) \"foo\"\n   2) \"feelsgood\"\n   3) \"foobar\"","section_id":"the-match-option"},{"id":"the-match-option-ex1","language":"c","code":"reply = redisCommand(c, \"SADD myset 1 2 3 foo foobar feelsgood\");\n    printf(\"%lld\\n\", reply->integer);\n    // >>> 6\n    freeReplyObject(reply);\n\n    // SCAN-family replies are a two-element array: the next cursor, then the results.\n    reply = redisCommand(c, \"SSCAN myset 0 MATCH f*\");\n    printf(\"%zu\\n\", reply->element[1]->elements);\n    // >>> 3\n    freeReplyObject(reply);","section_id":"the-match-option"},{"id":"the-match-option-ex2","language":"go","code":"scan1Result1, err := rdb.SAdd(ctx, \"myset\", \"1\", \"2\", \"3\", \"foo\", \"foobar\", \"feelsgood\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(scan1Result1) // >>> 6\n\n\tscan1Result2, _, err := rdb.SScan(ctx, \"myset\", 0, \"f*\", 0).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tsort.Strings(scan1Result2)\n\tfmt.Println(scan1Result2) // >>> [feelsgood foo foobar]","section_id":"the-match-option"},{"id":"the-match-option-ex3","language":"java","code":"CompletableFuture<Void> scan1Example = asyncCommands\n                    .sadd(\"myset\", \"1\", \"2\", \"3\", \"foo\", \"foobar\", \"feelsgood\")\n                    .thenCompose(scan1Res1 -> {\n                        System.out.println(scan1Res1);      // >>> 6\n                        return asyncCommands.sscan(\"myset\", ScanArgs.Builder.matches(\"f*\"));\n                    })\n                    .thenAccept(scan1Res2 -> {\n                        List<String> members = new java.util.ArrayList<>(scan1Res2.getValues());\n                        Collections.sort(members);\n                        System.out.println(members);        // >>> [feelsgood, foo, foobar]\n                    })\n                    .toCompletableFuture();","section_id":"the-match-option"},{"id":"the-match-option-ex4","language":"java","code":"Mono<Void> scan1Example = reactiveCommands\n                    .sadd(\"myset\", \"1\", \"2\", \"3\", \"foo\", \"foobar\", \"feelsgood\")\n                    .flatMap(scan1Res1 -> {\n                        System.out.println(scan1Res1);      // >>> 6\n                        return reactiveCommands.sscan(\"myset\", ScanArgs.Builder.matches(\"f*\"));\n                    })\n                    .doOnNext(scan1Res2 -> {\n                        List<String> members = new java.util.ArrayList<>(scan1Res2.getValues());\n                        Collections.sort(members);\n                        System.out.println(members);        // >>> [feelsgood, foo, foobar]\n                    })\n                    .then();","section_id":"the-match-option"},{"id":"the-match-option-ex5","language":"java","code":"long scan1Result1 = jedis.sadd(\"myset\", \"1\", \"2\", \"3\", \"foo\", \"foobar\", \"feelsgood\");\n        System.out.println(scan1Result1); // >>> 6\n\n        ScanResult<String> scan1Result2 = jedis.sscan(\n                \"myset\", \"0\", new ScanParams().match(\"f*\")\n        );\n        ArrayList<String> scan1Members = new ArrayList<>(scan1Result2.getResult());\n        Collections.sort(scan1Members);\n        System.out.println(scan1Members); // >>> [feelsgood, foo, foobar]","section_id":"the-match-option"},{"id":"the-match-option-ex6","language":"javascript","code":"const scan1Res1 = await client.sAdd('myset', ['1', '2', '3', 'foo', 'foobar', 'feelsgood']);\nconsole.log(scan1Res1); // 6\n\nlet scan1Res2 = [];\nfor await (const values of client.sScanIterator('myset', { MATCH: 'f*' })) {\n    scan1Res2 = scan1Res2.concat(values);\n}\nconsole.log(scan1Res2); // ['foo', 'foobar', 'feelsgood']","section_id":"the-match-option"},{"id":"the-match-option-ex7","language":"javascript","code":"const scan1Res1 = await redis.sadd('myset', '1', '2', '3', 'foo', 'foobar', 'feelsgood');\nconsole.log(scan1Res1); // >>> 6\n\nconst [, scan1Members] = await redis.sscan('myset', 0, 'MATCH', 'f*');\nconsole.log(scan1Members.sort()); // >>> ['feelsgood', 'foo', 'foobar']","section_id":"the-match-option"},{"id":"the-match-option-ex8","language":"javascript","code":"const scan1Res1 = await client.sAdd('myset', ['1', '2', '3', 'foo', 'foobar', 'feelsgood']);\nconsole.log(scan1Res1); // 6\n\nlet scan1Res2 = [];\nfor await (const values of client.sScanIterator('myset', { MATCH: 'f*' })) {\n    scan1Res2 = scan1Res2.concat(values);\n}\nconsole.log(scan1Res2); // ['foo', 'foobar', 'feelsgood']","section_id":"the-match-option"},{"id":"the-match-option-ex9","language":"javascript","code":"const scan1Res1 = await redis.sadd('myset', '1', '2', '3', 'foo', 'foobar', 'feelsgood');\nconsole.log(scan1Res1); // >>> 6\n\nconst [, scan1Members] = await redis.sscan('myset', 0, 'MATCH', 'f*');\nconsole.log(scan1Members.sort()); // >>> ['feelsgood', 'foo', 'foobar']","section_id":"the-match-option"},{"id":"the-match-option-ex10","language":"php","code":"$scan1Result1 = $r->sadd('myset', ['1', '2', '3', 'foo', 'foobar', 'feelsgood']);\n        echo $scan1Result1 . PHP_EOL;                       // >>> 6\n\n        [$scan1Cursor, $scan1Members] = $r->sscan('myset', 0, ['MATCH' => 'f*']);\n        sort($scan1Members);\n        echo implode(', ', $scan1Members) . PHP_EOL;         // >>> feelsgood, foo, foobar","section_id":"the-match-option"},{"id":"the-match-option-ex11","language":"python","code":"res = r.sadd(\"myset\", *set([1, 2, 3, \"foo\", \"foobar\", \"feelsgood\"]))\nprint(res)\n# >>> 6\n\nres = list(r.sscan_iter(\"myset\", match=\"f*\"))\nprint(res)\n# >>> ['foobar', 'foo', 'feelsgood']","section_id":"the-match-option"},{"id":"the-match-option-ex12","language":"rust","code":"match r.sadd(\"myset\", &[\"1\", \"2\", \"3\", \"foo\", \"foobar\", \"feelsgood\"]).await {\n            Ok(res) => {\n                let res: i32 = res;\n                println!(\"{res}\");    // >>> 6\n            },\n            Err(e) => {\n                println!(\"Error adding to set: {e}\");\n                return;\n            }\n        }\n\n        let res = match r.sscan_match(\"myset\", \"f*\").await {\n            Ok(iter) => {\n                let res: Vec<Result<String, _>> = iter.collect().await;\n                res.into_iter().filter_map(|r| r.ok()).collect::<Vec<String>>()\n            },\n            Err(e) => {\n                println!(\"Error scanning set: {e}\");\n                return;\n            }\n        };\n\n        println!(\"{res:?}\");    // >>> [\"foo\", \"foobar\", \"feelsgood\"]","section_id":"the-match-option"},{"id":"the-match-option-ex13","language":"rust","code":"match r.sadd(\"myset\", &[\"1\", \"2\", \"3\", \"foo\", \"foobar\", \"feelsgood\"]) {\n            Ok(res) => {\n                let res: i32 = res;\n                println!(\"{res}\");    // >>> 6\n            },\n            Err(e) => {\n                println!(\"Error adding to set: {e}\");\n                return;\n            }\n        }\n\n        match r.sscan_match(\"myset\", \"f*\") {\n            Ok(iter) => {\n                let res: Vec<String> = iter.filter_map(|r| r.ok()).collect();\n                println!(\"{res:?}\");    // >>> [\"foo\", \"foobar\", \"feelsgood\"]\n            },\n            Err(e) => {\n                println!(\"Error scanning set: {e}\");\n                return;\n            }\n        }","section_id":"the-match-option"},{"id":"the-match-option-ex14","language":"plaintext","code":"> scan 0 MATCH *11*\n1) \"288\"\n2) 1) \"key:911\"\n> scan 288 MATCH *11*\n1) \"224\"\n2) (empty list or set)\n> scan 224 MATCH *11*\n1) \"80\"\n2) (empty list or set)\n> scan 80 MATCH *11*\n1) \"176\"\n2) (empty list or set)\n> scan 176 MATCH *11* COUNT 1000\n1) \"0\"\n2)  1) \"key:611\"\n    2) \"key:711\"\n    3) \"key:118\"\n    4) \"key:117\"\n    5) \"key:311\"\n    6) \"key:112\"\n    7) \"key:111\"\n    8) \"key:110\"\n    9) \"key:113\"\n   10) \"key:211\"\n   11) \"key:411\"\n   12) \"key:115\"\n   13) \"key:116\"\n   14) \"key:114\"\n   15) \"key:119\"\n   16) \"key:811\"\n   17) \"key:511\"\n   18) \"key:11\"","section_id":"the-match-option"},{"id":"the-match-option-ex15","language":"c","code":"// MATCH is applied after elements are fetched, so with the default COUNT most\n    // iterations return few keys or none at all.\n    char cursor[64] = \"0\";\n\n    for (int i = 0; i < 4; i++) {\n        reply = redisCommand(c, \"SCAN %s MATCH *11*\", cursor);\n        snprintf(cursor, sizeof(cursor), \"%s\", reply->element[0]->str);\n        printf(\"%zu\\n\", reply->element[1]->elements);\n        freeReplyObject(reply);\n    }\n\n    // A larger COUNT forces more scanning in a single iteration, so the remaining\n    // matches arrive together. This continues from the cursor reached above.\n    reply = redisCommand(c, \"SCAN %s MATCH *11* COUNT 1000\", cursor);\n    printf(\"%zu\\n\", reply->element[1]->elements);\n    // >>> 18\n    freeReplyObject(reply);","section_id":"the-match-option"},{"id":"the-match-option-ex16","language":"go","code":"// MATCH is applied after elements are fetched, so with the default COUNT most\n\t// iterations return few keys or none at all.\n\tvar scan2Cursor uint64\n\tvar scan2Keys []string\n\tvar err error\n\tscan2Total := 0\n\n\tfor i := 0; i < 4; i++ {\n\t\tscan2Keys, scan2Cursor, err = rdb.Scan(ctx, scan2Cursor, \"*11*\", 0).Result()\n\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tscan2Total += len(scan2Keys)\n\t}\n\n\t// A larger COUNT forces more scanning in a single iteration, so the remaining\n\t// matches arrive together. This continues from the cursor reached above.\n\tscan2Keys, _, err = rdb.Scan(ctx, scan2Cursor, \"*11*\", 1000).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tscan2Total += len(scan2Keys)\n\n\t// The per-call split isn't guaranteed, but the cumulative total is.\n\tfmt.Println(scan2Total) // >>> 19","section_id":"the-match-option"},{"id":"the-match-option-ex17","language":"java","code":"// MATCH is applied after elements are fetched, so with the default COUNT most\n            // iterations return few keys or none at all. Each iteration is awaited because\n            // the next one needs the cursor this one returns.\n            KeyScanCursor<String> scan2Cursor = asyncCommands\n                    .scan(ScanArgs.Builder.matches(\"*11*\")).toCompletableFuture().join();\n            int scan2Total = scan2Cursor.getKeys().size();\n            System.out.println(scan2Cursor.getKeys().size());\n\n            for (int i = 0; i < 3; i++) {\n                scan2Cursor = asyncCommands\n                        .scan(scan2Cursor, ScanArgs.Builder.matches(\"*11*\"))\n                        .toCompletableFuture().join();\n                scan2Total += scan2Cursor.getKeys().size();\n                System.out.println(scan2Cursor.getKeys().size());\n            }\n\n            // A larger COUNT forces more scanning in a single iteration, so the remaining\n            // matches arrive together. This continues from the cursor reached above.\n            scan2Cursor = asyncCommands\n                    .scan(scan2Cursor, ScanArgs.Builder.matches(\"*11*\").limit(1000))\n                    .toCompletableFuture().join();\n            scan2Total += scan2Cursor.getKeys().size();\n            System.out.println(scan2Cursor.getKeys().size());\n\n            // The per-call split isn't guaranteed, but the cumulative total is.\n            System.out.println(scan2Total);   // >>> 19","section_id":"the-match-option"},{"id":"the-match-option-ex18","language":"java","code":"// MATCH is applied after elements are fetched, so with the default COUNT most\n            // iterations return few keys or none at all. Each iteration is subscribed to in\n            // turn because the next one needs the cursor this one returns.\n            KeyScanCursor<String> scan2Cursor = reactiveCommands\n                    .scan(ScanArgs.Builder.matches(\"*11*\")).block();\n            int scan2Total = scan2Cursor.getKeys().size();\n            System.out.println(scan2Cursor.getKeys().size());\n\n            for (int i = 0; i < 3; i++) {\n                scan2Cursor = reactiveCommands\n                        .scan(scan2Cursor, ScanArgs.Builder.matches(\"*11*\")).block();\n                scan2Total += scan2Cursor.getKeys().size();\n                System.out.println(scan2Cursor.getKeys().size());\n            }\n\n            // A larger COUNT forces more scanning in a single iteration, so the remaining\n            // matches arrive together. This continues from the cursor reached above.\n            scan2Cursor = reactiveCommands\n                    .scan(scan2Cursor, ScanArgs.Builder.matches(\"*11*\").limit(1000)).block();\n            scan2Total += scan2Cursor.getKeys().size();\n            System.out.println(scan2Cursor.getKeys().size());\n\n            // The per-call split isn't guaranteed, but the cumulative total is.\n            System.out.println(scan2Total);   // >>> 19","section_id":"the-match-option"},{"id":"the-match-option-ex19","language":"java","code":"// MATCH is applied after elements are fetched, so with the default COUNT most\n        // iterations return few keys or none at all.\n        String scan2Cursor = \"0\";\n        ScanResult<String> scan2Result;\n        int scan2Total = 0;\n\n        for (int i = 0; i < 4; i++) {\n            scan2Result = jedis.scan(scan2Cursor, new ScanParams().match(\"*11*\"));\n            scan2Cursor = scan2Result.getCursor();\n            scan2Total += scan2Result.getResult().size();\n            System.out.println(scan2Result.getResult().size());\n        }\n\n        // A larger COUNT forces more scanning in a single iteration, so the remaining\n        // matches arrive together. This continues from the cursor reached above.\n        scan2Result = jedis.scan(scan2Cursor, new ScanParams().match(\"*11*\").count(1000));\n        scan2Total += scan2Result.getResult().size();\n        System.out.println(scan2Result.getResult().size());\n\n        // The per-call split isn't guaranteed, but the cumulative total is.\n        System.out.println(scan2Total); // >>> 19","section_id":"the-match-option"},{"id":"the-match-option-ex20","language":"javascript","code":"let cursor = '0';\nlet scanResult;\nlet total = 0;\n\nscanResult = await client.scan(cursor, { MATCH: '*11*' });\ntotal += scanResult.keys.length;\nconsole.log(scanResult.cursor, scanResult.keys);\n\nscanResult = await client.scan(scanResult.cursor, { MATCH: '*11*' });\ntotal += scanResult.keys.length;\nconsole.log(scanResult.cursor, scanResult.keys);\n\nscanResult = await client.scan(scanResult.cursor, { MATCH: '*11*' });\ntotal += scanResult.keys.length;\nconsole.log(scanResult.cursor, scanResult.keys);\n\nscanResult = await client.scan(scanResult.cursor, { MATCH: '*11*' });\ntotal += scanResult.keys.length;\nconsole.log(scanResult.cursor, scanResult.keys);\n\nscanResult = await client.scan(scanResult.cursor, { MATCH: '*11*', COUNT: 1000 });\ntotal += scanResult.keys.length;\nconsole.log(scanResult.cursor, scanResult.keys);\n\n// The per-call split isn't guaranteed, but the cumulative total is.\nconsole.log(total);\n// >>> 19","section_id":"the-match-option"},{"id":"the-match-option-ex21","language":"javascript","code":"// MATCH filters after the elements are fetched, so most iterations return nothing.\nlet [scan2Cursor, scan2Keys] = await redis.scan(0, 'MATCH', '*11*');\nlet scan2Total = scan2Keys.length;\nconsole.log(scan2Keys.length);\n\nfor (let i = 0; i < 3; i++) {\n  [scan2Cursor, scan2Keys] = await redis.scan(scan2Cursor, 'MATCH', '*11*');\n  scan2Total += scan2Keys.length;\n  console.log(scan2Keys.length);\n}\n\n// A larger COUNT forces more scanning in a single iteration, so the rest of the\n// matches arrive together. The scan continues from the cursor reached above.\n[scan2Cursor, scan2Keys] = await redis.scan(scan2Cursor, 'MATCH', '*11*', 'COUNT', 1000);\nscan2Total += scan2Keys.length;\nconsole.log(scan2Keys.length);\n\n// The per-call split isn't guaranteed, but the cumulative total is.\nconsole.log(scan2Total); // >>> 19","section_id":"the-match-option"},{"id":"the-match-option-ex22","language":"javascript","code":"let cursor = '0';\nlet scanResult;\nlet total = 0;\n\nscanResult = await client.scan(cursor, { MATCH: '*11*' });\ntotal += scanResult.keys.length;\nconsole.log(scanResult.cursor, scanResult.keys);\n\nscanResult = await client.scan(scanResult.cursor, { MATCH: '*11*' });\ntotal += scanResult.keys.length;\nconsole.log(scanResult.cursor, scanResult.keys);\n\nscanResult = await client.scan(scanResult.cursor, { MATCH: '*11*' });\ntotal += scanResult.keys.length;\nconsole.log(scanResult.cursor, scanResult.keys);\n\nscanResult = await client.scan(scanResult.cursor, { MATCH: '*11*' });\ntotal += scanResult.keys.length;\nconsole.log(scanResult.cursor, scanResult.keys);\n\nscanResult = await client.scan(scanResult.cursor, { MATCH: '*11*', COUNT: 1000 });\ntotal += scanResult.keys.length;\nconsole.log(scanResult.cursor, scanResult.keys);\n\n// The per-call split isn't guaranteed, but the cumulative total is.\nconsole.log(total);\n// >>> 19","section_id":"the-match-option"},{"id":"the-match-option-ex23","language":"javascript","code":"// MATCH filters after the elements are fetched, so most iterations return nothing.\nlet [scan2Cursor, scan2Keys] = await redis.scan(0, 'MATCH', '*11*');\nlet scan2Total = scan2Keys.length;\nconsole.log(scan2Keys.length);\n\nfor (let i = 0; i < 3; i++) {\n  [scan2Cursor, scan2Keys] = await redis.scan(scan2Cursor, 'MATCH', '*11*');\n  scan2Total += scan2Keys.length;\n  console.log(scan2Keys.length);\n}\n\n// A larger COUNT forces more scanning in a single iteration, so the rest of the\n// matches arrive together. The scan continues from the cursor reached above.\n[scan2Cursor, scan2Keys] = await redis.scan(scan2Cursor, 'MATCH', '*11*', 'COUNT', 1000);\nscan2Total += scan2Keys.length;\nconsole.log(scan2Keys.length);\n\n// The per-call split isn't guaranteed, but the cumulative total is.\nconsole.log(scan2Total); // >>> 19","section_id":"the-match-option"},{"id":"the-match-option-ex24","language":"php","code":"// MATCH is applied after elements are fetched, so with the default COUNT most\n        // iterations return few keys or none at all.\n        $scan2Cursor = 0;\n        $scan2Total = 0;\n\n        for ($i = 0; $i < 4; $i++) {\n            [$scan2Cursor, $scan2Keys] = $r->scan($scan2Cursor, ['MATCH' => '*11*']);\n            $scan2Total += count($scan2Keys);\n            echo count($scan2Keys) . PHP_EOL;\n        }\n\n        // A larger COUNT forces more scanning in a single iteration, so the remaining\n        // matches arrive together. This continues from the cursor reached above.\n        [$scan2Cursor, $scan2Keys] = $r->scan($scan2Cursor, ['MATCH' => '*11*', 'COUNT' => 1000]);\n        $scan2Total += count($scan2Keys);\n        echo count($scan2Keys) . PHP_EOL;\n\n        // The per-call split isn't guaranteed, but the cumulative total is.\n        echo $scan2Total . PHP_EOL;                          // >>> 19","section_id":"the-match-option"},{"id":"the-match-option-ex25","language":"python","code":"total = 0\n\ncursor, keys = r.scan(cursor=0, match='*11*')\ntotal += len(keys)\nprint(cursor, keys)\n\ncursor, keys = r.scan(cursor, match='*11*')\ntotal += len(keys)\nprint(cursor, keys)\n\ncursor, keys = r.scan(cursor, match='*11*')\ntotal += len(keys)\nprint(cursor, keys)\n\ncursor, keys = r.scan(cursor, match='*11*')\ntotal += len(keys)\nprint(cursor, keys)\n\ncursor, keys = r.scan(cursor, match='*11*', count=1000)\ntotal += len(keys)\nprint(cursor, keys)\n\n# The per-call split isn't guaranteed, but the cumulative total is.\nprint(total)\n# >>> 19","section_id":"the-match-option"},{"id":"the-match-option-ex26","language":"rust","code":"// Note: Rust redis client scan_match returns an iterator, not cursor-based\n        // This simulates the Python cursor-based output but uses the available API\n        let keys = match r.scan_match(\"*11*\").await {\n            Ok(iter) => {\n                let keys: Vec<Result<String, _>> = iter.collect().await;\n                keys.into_iter().filter_map(|r| r.ok()).collect::<Vec<String>>()\n            },\n            Err(e) => {\n                println!(\"Error scanning keys: {e}\");\n                return;\n            }\n        };","section_id":"the-match-option"},{"id":"the-match-option-ex27","language":"rust","code":"// Note: Rust redis client scan_match returns an iterator, not cursor-based\n        // This simulates the Python cursor-based output but uses the available API\n        match r.scan_match(\"*11*\") {\n            Ok(iter) => {\n                let keys: Vec<String> = iter.filter_map(|r| r.ok()).collect();\n            },\n            Err(e) => {\n                println!(\"Error scanning keys: {e}\");\n                return;\n            }\n        }","section_id":"the-match-option"},{"id":"the-type-option-ex0","language":"plaintext","code":"> GEOADD geokey 0 0 value\n(integer) 1\n> ZADD zkey 1000 value\n(integer) 1\n> TYPE geokey\nzset\n> TYPE zkey\nzset\n> SCAN 0 TYPE zset\n1) \"0\"\n2) 1) \"geokey\"\n   2) \"zkey\"","section_id":"the-type-option"},{"id":"the-type-option-ex1","language":"c","code":"reply = redisCommand(c, \"GEOADD geokey 0 0 value\");\n    printf(\"%lld\\n\", reply->integer);\n    // >>> 1\n    freeReplyObject(reply);\n\n    reply = redisCommand(c, \"ZADD zkey 1000 value\");\n    printf(\"%lld\\n\", reply->integer);\n    // >>> 1\n    freeReplyObject(reply);\n\n    reply = redisCommand(c, \"TYPE geokey\");\n    printf(\"%s\\n\", reply->str);\n    // >>> zset\n    freeReplyObject(reply);\n\n    reply = redisCommand(c, \"TYPE zkey\");\n    printf(\"%s\\n\", reply->str);\n    // >>> zset\n    freeReplyObject(reply);\n\n    reply = redisCommand(c, \"SCAN 0 TYPE zset\");\n    printf(\"%zu\\n\", reply->element[1]->elements);\n    // >>> 2\n    freeReplyObject(reply);","section_id":"the-type-option"},{"id":"the-type-option-ex2","language":"go","code":"scan3Result1, err := rdb.GeoAdd(ctx, \"geokey\", &redis.GeoLocation{\n\t\tLongitude: 0, Latitude: 0, Name: \"value\",\n\t}).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(scan3Result1) // >>> 1\n\n\tscan3Result2, err := rdb.ZAdd(ctx, \"zkey\", redis.Z{Score: 1000, Member: \"value\"}).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(scan3Result2) // >>> 1\n\n\tscan3Result3, err := rdb.Type(ctx, \"geokey\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(scan3Result3) // >>> zset\n\n\tscan3Result4, err := rdb.Type(ctx, \"zkey\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(scan3Result4) // >>> zset\n\n\t// A single call isn't guaranteed to find every match, so loop until the cursor\n\t// returns to 0, accumulating matches from every call.\n\tvar scan3Cursor uint64\n\tvar scan3Keys []string\n\tvar scan3Batch []string\n\n\tfor {\n\t\tscan3Batch, scan3Cursor, err = rdb.ScanType(ctx, scan3Cursor, \"\", 0, \"zset\").Result()\n\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tscan3Keys = append(scan3Keys, scan3Batch...)\n\n\t\tif scan3Cursor == 0 {\n\t\t\tbreak\n\t\t}\n\t}\n\n\tsort.Strings(scan3Keys)\n\tfmt.Println(scan3Keys) // >>> [geokey zkey]","section_id":"the-type-option"},{"id":"the-type-option-ex3","language":"java","code":"long scan3Result1 = asyncCommands.geoadd(\"geokey\", 0, 0, \"value\")\n                    .toCompletableFuture().join();\n            System.out.println(scan3Result1);      // >>> 1\n\n            long scan3Result2 = asyncCommands.zadd(\"zkey\", 1000, \"value\")\n                    .toCompletableFuture().join();\n            System.out.println(scan3Result2);      // >>> 1\n\n            String scan3Result3 = asyncCommands.type(\"geokey\").toCompletableFuture().join();\n            System.out.println(scan3Result3);      // >>> zset\n\n            String scan3Result4 = asyncCommands.type(\"zkey\").toCompletableFuture().join();\n            System.out.println(scan3Result4);      // >>> zset\n\n            // A single call isn't guaranteed to find every match, so loop until\n            // the cursor is finished, accumulating matches from every call.\n            List<String> scan3Keys = new java.util.ArrayList<>();\n            KeyScanCursor<String> scan3Cursor = asyncCommands\n                    .scan(KeyScanArgs.Builder.type(\"zset\")).toCompletableFuture().join();\n            scan3Keys.addAll(scan3Cursor.getKeys());\n            while (!scan3Cursor.isFinished()) {\n                scan3Cursor = asyncCommands\n                        .scan(scan3Cursor, KeyScanArgs.Builder.type(\"zset\"))\n                        .toCompletableFuture().join();\n                scan3Keys.addAll(scan3Cursor.getKeys());\n            }\n            Collections.sort(scan3Keys);\n            System.out.println(scan3Keys);         // >>> [geokey, zkey]","section_id":"the-type-option"},{"id":"the-type-option-ex4","language":"java","code":"long scan3Result1 = reactiveCommands.geoadd(\"geokey\", 0, 0, \"value\").block();\n            System.out.println(scan3Result1);      // >>> 1\n\n            long scan3Result2 = reactiveCommands.zadd(\"zkey\", 1000, \"value\").block();\n            System.out.println(scan3Result2);      // >>> 1\n\n            String scan3Result3 = reactiveCommands.type(\"geokey\").block();\n            System.out.println(scan3Result3);      // >>> zset\n\n            String scan3Result4 = reactiveCommands.type(\"zkey\").block();\n            System.out.println(scan3Result4);      // >>> zset\n\n            // A single call isn't guaranteed to find every match, so loop until\n            // the cursor is finished, accumulating matches from every call.\n            List<String> scan3Keys = new java.util.ArrayList<>();\n            KeyScanCursor<String> scan3Cursor = reactiveCommands\n                    .scan(KeyScanArgs.Builder.type(\"zset\")).block();\n            scan3Keys.addAll(scan3Cursor.getKeys());\n            while (!scan3Cursor.isFinished()) {\n                scan3Cursor = reactiveCommands\n                        .scan(scan3Cursor, KeyScanArgs.Builder.type(\"zset\")).block();\n                scan3Keys.addAll(scan3Cursor.getKeys());\n            }\n            Collections.sort(scan3Keys);\n            System.out.println(scan3Keys);         // >>> [geokey, zkey]","section_id":"the-type-option"},{"id":"the-type-option-ex5","language":"java","code":"long scan3Result1 = jedis.geoadd(\"geokey\", 0, 0, \"value\");\n        System.out.println(scan3Result1); // >>> 1\n\n        long scan3Result2 = jedis.zadd(\"zkey\", 1000, \"value\");\n        System.out.println(scan3Result2); // >>> 1\n\n        System.out.println(jedis.type(\"geokey\")); // >>> zset\n        System.out.println(jedis.type(\"zkey\")); // >>> zset\n\n        // A single call isn't guaranteed to find every match, so loop until the cursor\n        // returns to \"0\", accumulating matches from every call.\n        String scan3Cursor = \"0\";\n        ArrayList<String> scan3Keys = new ArrayList<>();\n        do {\n            ScanResult<String> scan3Result3 = jedis.scan(scan3Cursor, new ScanParams(), \"zset\");\n            scan3Cursor = scan3Result3.getCursor();\n            scan3Keys.addAll(scan3Result3.getResult());\n        } while (!scan3Cursor.equals(\"0\"));\n        Collections.sort(scan3Keys);\n        System.out.println(scan3Keys); // >>> [geokey, zkey]","section_id":"the-type-option"},{"id":"the-type-option-ex6","language":"javascript","code":"const scan3Res1 = await client.geoAdd('geokey', { longitude: 0, latitude: 0, member: 'value' });\nconsole.log(scan3Res1); // 1\n\nconst scan3Res2 = await client.zAdd('zkey', [{ score: 1000, value: 'value' }]);\nconsole.log(scan3Res2); // 1\n\nconst scan3Res3 = await client.type('geokey');\nconsole.log(scan3Res3); // zset\n\nconst scan3Res4 = await client.type('zkey');\nconsole.log(scan3Res4); // zset\n\n// A single call isn't guaranteed to find every match, so loop until the cursor\n// returns to 0, accumulating matches from every call.\nlet scan3Cursor = '0';\nlet scan3Keys = [];\ndo {\n    const scan3Res5 = await client.scan(scan3Cursor, { TYPE: 'zset' });\n    scan3Cursor = scan3Res5.cursor;\n    scan3Keys = scan3Keys.concat(scan3Res5.keys);\n} while (scan3Cursor !== '0');\nconsole.log(scan3Keys.sort()); // ['geokey', 'zkey']","section_id":"the-type-option"},{"id":"the-type-option-ex7","language":"javascript","code":"const scan3Res1 = await redis.geoadd('geokey', '0', '0', 'value');\nconsole.log(scan3Res1); // >>> 1\n\nconst scan3Res2 = await redis.zadd('zkey', '1000', 'value');\nconsole.log(scan3Res2); // >>> 1\n\nconsole.log(await redis.type('geokey')); // >>> zset\nconsole.log(await redis.type('zkey')); // >>> zset\n\n// A single call isn't guaranteed to find every match, so loop until the cursor\n// returns to 0, accumulating matches from every call.\nlet scan3Cursor = '0';\nlet scan3Keys = [];\ndo {\n  let scan3Batch;\n  [scan3Cursor, scan3Batch] = await redis.scan(scan3Cursor, 'TYPE', 'zset');\n  scan3Keys = scan3Keys.concat(scan3Batch);\n} while (scan3Cursor !== '0');\nconsole.log(scan3Keys.sort()); // >>> ['geokey', 'zkey']","section_id":"the-type-option"},{"id":"the-type-option-ex8","language":"javascript","code":"const scan3Res1 = await client.geoAdd('geokey', { longitude: 0, latitude: 0, member: 'value' });\nconsole.log(scan3Res1); // 1\n\nconst scan3Res2 = await client.zAdd('zkey', [{ score: 1000, value: 'value' }]);\nconsole.log(scan3Res2); // 1\n\nconst scan3Res3 = await client.type('geokey');\nconsole.log(scan3Res3); // zset\n\nconst scan3Res4 = await client.type('zkey');\nconsole.log(scan3Res4); // zset\n\n// A single call isn't guaranteed to find every match, so loop until the cursor\n// returns to 0, accumulating matches from every call.\nlet scan3Cursor = '0';\nlet scan3Keys = [];\ndo {\n    const scan3Res5 = await client.scan(scan3Cursor, { TYPE: 'zset' });\n    scan3Cursor = scan3Res5.cursor;\n    scan3Keys = scan3Keys.concat(scan3Res5.keys);\n} while (scan3Cursor !== '0');\nconsole.log(scan3Keys.sort()); // ['geokey', 'zkey']","section_id":"the-type-option"},{"id":"the-type-option-ex9","language":"javascript","code":"const scan3Res1 = await redis.geoadd('geokey', '0', '0', 'value');\nconsole.log(scan3Res1); // >>> 1\n\nconst scan3Res2 = await redis.zadd('zkey', '1000', 'value');\nconsole.log(scan3Res2); // >>> 1\n\nconsole.log(await redis.type('geokey')); // >>> zset\nconsole.log(await redis.type('zkey')); // >>> zset\n\n// A single call isn't guaranteed to find every match, so loop until the cursor\n// returns to 0, accumulating matches from every call.\nlet scan3Cursor = '0';\nlet scan3Keys = [];\ndo {\n  let scan3Batch;\n  [scan3Cursor, scan3Batch] = await redis.scan(scan3Cursor, 'TYPE', 'zset');\n  scan3Keys = scan3Keys.concat(scan3Batch);\n} while (scan3Cursor !== '0');\nconsole.log(scan3Keys.sort()); // >>> ['geokey', 'zkey']","section_id":"the-type-option"},{"id":"the-type-option-ex10","language":"php","code":"<?php\nuse PHPUnit\\Framework\\TestCase;\nuse Predis\\Client as PredisClient;\n\nclass CmdsGenericTest\n{\n    public function testCmdsGeneric() {\n        $r = new PredisClient([\n            'scheme'   => 'tcp',\n            'host'     => '127.0.0.1',\n            'port'     => 6379,\n            'password' => '',\n            'database' => 0,\n        ]);\n\n        $existsResult1 = $r->set('key1', 'Hello');\n        echo $existsResult1 . PHP_EOL; // >>> OK\n\n        $existsResult2 = $r->exists('key1');\n        echo $existsResult2 . PHP_EOL; // >>> 1\n\n        $existsResult3 = $r->exists('nosuchkey');\n        echo $existsResult3 . PHP_EOL; // >>> 0\n\n        $existsResult4 = $r->set('key2', 'World');\n        echo $existsResult4 . PHP_EOL; // >>> OK\n\n        $existsResult5 = $r->exists('key1', 'key2', 'nosuchkey');\n        echo $existsResult5 . PHP_EOL; // >>> 2\n\n        $keysResult1 = $r->mset(['firstname' => 'Jack', 'lastname' => 'Stuntman', 'age' => '35']);\n        echo $keysResult1 . PHP_EOL; // >>> OK\n\n        $keysResult2 = $r->keys('*name*');\n        sort($keysResult2);\n        echo implode(', ', $keysResult2) . PHP_EOL; // >>> firstname, lastname\n\n        $keysResult3 = $r->keys('a??');\n        echo implode(', ', $keysResult3) . PHP_EOL; // >>> age\n\n        $keysResult4 = $r->keys('*');\n        sort($keysResult4);\n        echo implode(', ', $keysResult4) . PHP_EOL; // >>> age, firstname, lastname\n\n        $scan1Result1 = $r->sadd('myset', ['1', '2', '3', 'foo', 'foobar', 'feelsgood']);\n        echo $scan1Result1 . PHP_EOL;                       // >>> 6\n\n        [$scan1Cursor, $scan1Members] = $r->sscan('myset', 0, ['MATCH' => 'f*']);\n        sort($scan1Members);\n        echo implode(', ', $scan1Members) . PHP_EOL;         // >>> feelsgood, foo, foobar\n\n\n\n        // MATCH is applied after elements are fetched, so with the default COUNT most\n        // iterations return few keys or none at all.\n        $scan2Cursor = 0;\n        $scan2Total = 0;\n\n        for ($i = 0; $i < 4; $i++) {\n            [$scan2Cursor, $scan2Keys] = $r->scan($scan2Cursor, ['MATCH' => '*11*']);\n            $scan2Total += count($scan2Keys);\n            echo count($scan2Keys) . PHP_EOL;\n        }\n\n        // A larger COUNT forces more scanning in a single iteration, so the remaining\n        // matches arrive together. This continues from the cursor reached above.\n        [$scan2Cursor, $scan2Keys] = $r->scan($scan2Cursor, ['MATCH' => '*11*', 'COUNT' => 1000]);\n        $scan2Total += count($scan2Keys);\n        echo count($scan2Keys) . PHP_EOL;\n\n        // The per-call split isn't guaranteed, but the cumulative total is.\n        echo $scan2Total . PHP_EOL;                          // >>> 19\n\n\n        $scan4Result1 = $r->hset('myhash', 'a', 1, 'b', 2);\n        echo $scan4Result1 . PHP_EOL;                        // >>> 2\n\n        [$scan4Cursor, $scan4Pairs] = $r->hscan('myhash', 0);\n        echo json_encode($scan4Pairs) . PHP_EOL;             // >>> {\"a\":\"1\",\"b\":\"2\"}\n\n        // Redis does not promise a field order, so sort before comparing.\n        [$scan4Cursor, $scan4Fields] = $r->hscan('myhash', 0, ['NOVALUES' => true]);\n        sort($scan4Fields);\n        echo implode(', ', $scan4Fields) . PHP_EOL;          // >>> a, b\n\n\n        echo $r->set('key1', 'Hello') . PHP_EOL;             // >>> OK\n        echo $r->set('key2', 'World') . PHP_EOL;             // >>> OK\n\n        $delResult = $r->del('key1', 'key2', 'key3');\n        echo $delResult . PHP_EOL;                           // >>> 2\n\n\n        echo $r->set('mykey', 'Hello') . PHP_EOL;            // >>> OK\n\n        echo $r->expire('mykey', 10) . PHP_EOL;              // >>> 1\n        echo $r->ttl('mykey') . PHP_EOL;                     // >>> 10\n\n        // Overwriting a key with SET clears its expiry.\n        echo $r->set('mykey', 'Hello World') . PHP_EOL;      // >>> OK\n        echo $r->ttl('mykey') . PHP_EOL;                     // >>> -1\n\n        // XX only sets the expiry when one already exists, so this is a no-op.\n        echo $r->expire('mykey', 10, 'XX') . PHP_EOL;        // >>> 0\n        echo $r->ttl('mykey') . PHP_EOL;                     // >>> -1\n\n        // NX only sets the expiry when there is none, so this one applies.\n        echo $r->expire('mykey', 10, 'NX') . PHP_EOL;        // >>> 1\n        $expireTtl = $r->ttl('mykey');\n        echo $expireTtl . PHP_EOL;                           // >>> 10\n\n\n        echo $r->set('mykey', 'Hello') . PHP_EOL;            // >>> OK\n        echo $r->expire('mykey', 10) . PHP_EOL;              // >>> 1\n\n        $ttlResult = $r->ttl('mykey');\n        echo $ttlResult . PHP_EOL;                           // >>> 10\n\n\n    }\n}","section_id":"the-type-option"},{"id":"the-type-option-ex11","language":"python","code":"res = r.geoadd(\"geokey\", (0, 0, \"value\"))\nprint(res)\n# >>> 1\n\nres = r.zadd(\"zkey\", {\"value\": 1000})\nprint(res)\n# >>> 1\n\nres = r.type(\"geokey\")\nprint(res)\n# >>> zset\n\nres = r.type(\"zkey\")\nprint(res)\n# >>> zset\n\n# A single call isn't guaranteed to find every match, so loop until the cursor\n# returns to 0, accumulating matches from every call.\ncursor = 0\nscan3_keys = []\nwhile True:\n    cursor, keys = r.scan(cursor=cursor, _type=\"zset\")\n    scan3_keys.extend(keys)\n    if cursor == 0:\n        break\nprint(sorted(scan3_keys))\n# >>> ['geokey', 'zkey']","section_id":"the-type-option"},{"id":"the-type-option-ex12","language":"rust","code":"match r.geo_add(\"geokey\", &[(0.0, 0.0, \"value\")]).await {\n            Ok(res) => {\n                let res: i32 = res;\n                println!(\"{res}\");    // >>> 1\n            },\n            Err(e) => {\n                println!(\"Error adding geo location: {e}\");\n                return;\n            }\n        }\n\n        match r.zadd(\"zkey\", \"value\", 1000).await {\n            Ok(res) => {\n                let res: i32 = res;\n                println!(\"{res}\");    // >>> 1\n            },\n            Err(e) => {\n                println!(\"Error adding to sorted set: {e}\");\n                return;\n            }\n        }\n\n        match r.key_type::<&str, redis::ValueType>(\"geokey\").await {\n            Ok(res) => {\n                println!(\"{res:?}\");    // >>> zset\n            },\n            Err(e) => {\n                println!(\"Error getting key type: {e}\");\n                return;\n            }\n        }\n\n        match r.key_type::<&str, redis::ValueType>(\"zkey\").await {\n            Ok(res) => {\n                println!(\"{res:?}\");    // >>> zset\n            },\n            Err(e) => {\n                println!(\"Error getting key type: {e}\");\n                return;\n            }\n        }\n\n        // Note: Rust redis client doesn't support scan by type directly\n        // We'll manually check the types of our known keys\n        let mut zset_keys = Vec::new();\n        for key in &[\"geokey\", \"zkey\"] {\n            match r.key_type::<&str, redis::ValueType>(key).await {\n                Ok(key_type) => {\n                    if format!(\"{key_type:?}\") == \"ZSet\" {\n                        zset_keys.push(key.to_string());\n                    }\n                },\n                Err(_) => {},\n            }\n        }\n        println!(\"{:?}\", zset_keys);    // >>> [\"zkey\", \"geokey\"]","section_id":"the-type-option"},{"id":"the-type-option-ex13","language":"rust","code":"match r.geo_add(\"geokey\", &[(0.0, 0.0, \"value\")]) {\n            Ok(res) => {\n                let res: i32 = res;\n                println!(\"{res}\");    // >>> 1\n            },\n            Err(e) => {\n                println!(\"Error adding geo location: {e}\");\n                return;\n            }\n        }\n\n        match r.zadd(\"zkey\", \"value\", 1000) {\n            Ok(res) => {\n                let res: i32 = res;\n                println!(\"{res}\");    // >>> 1\n            },\n            Err(e) => {\n                println!(\"Error adding to sorted set: {e}\");\n                return;\n            }\n        }\n\n        match r.key_type::<&str, redis::ValueType>(\"geokey\") {\n            Ok(res) => {\n                println!(\"{res:?}\");    // >>> zset\n            },\n            Err(e) => {\n                println!(\"Error getting key type: {e}\");\n                return;\n            }\n        }\n\n        match r.key_type::<&str, redis::ValueType>(\"zkey\") {\n            Ok(res) => {\n                println!(\"{res:?}\");    // >>> zset\n            },\n            Err(e) => {\n                println!(\"Error getting key type: {e}\");\n                return;\n            }\n        }\n\n        // Note: Rust redis client doesn't support scan by type directly\n        // We'll manually check the types of our known keys\n        let mut zset_keys = Vec::new();\n        for key in &[\"geokey\", \"zkey\"] {\n            match r.key_type::<&str, redis::ValueType>(key) {\n                Ok(key_type) => {\n                    if format!(\"{key_type:?}\") == \"ZSet\" {\n                        zset_keys.push(key.to_string());\n                    }\n                },\n                Err(_) => {},\n            }\n        }\n        println!(\"{:?}\", zset_keys);    // >>> [\"zkey\", \"geokey\"]","section_id":"the-type-option"},{"id":"the-novalues-option-ex0","language":"plaintext","code":"> HSET myhash a 1 b 2\nOK\n> HSCAN myhash 0\n1) \"0\"\n2) 1) \"a\"\n   2) \"1\"\n   3) \"b\"\n   4) \"2\"\n> HSCAN myhash 0 NOVALUES\n1) \"0\"\n2) 1) \"a\"\n   2) \"b\"","section_id":"the-novalues-option"},{"id":"the-novalues-option-ex1","language":"c","code":"reply = redisCommand(c, \"HSET myhash a 1 b 2\");\n    printf(\"%lld\\n\", reply->integer);\n    // >>> 2\n    freeReplyObject(reply);\n\n    // Without NOVALUES the results alternate field, value, field, value.\n    reply = redisCommand(c, \"HSCAN myhash 0\");\n    for (size_t i = 0; i < reply->element[1]->elements; i += 2) {\n        printf(\"%s=%s\\n\", reply->element[1]->element[i]->str,\n                           reply->element[1]->element[i + 1]->str);\n    }\n    // >>> a=1\n    // >>> b=2\n    freeReplyObject(reply);\n\n    reply = redisCommand(c, \"HSCAN myhash 0 NOVALUES\");\n    for (size_t i = 0; i < reply->element[1]->elements; i++) {\n        printf(\"%s\\n\", reply->element[1]->element[i]->str);\n    }\n    // >>> a\n    // >>> b\n    freeReplyObject(reply);","section_id":"the-novalues-option"},{"id":"the-novalues-option-ex2","language":"go","code":"scan4Result1, err := rdb.HSet(ctx, \"myhash\", \"a\", 1, \"b\", 2).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(scan4Result1) // >>> 2\n\n\tscan4Result2, _, err := rdb.HScan(ctx, \"myhash\", 0, \"\", 0).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// HSCAN returns field and value interleaved. Redis does not promise an order, so\n\t// collect the pairs into a map: fmt prints map keys sorted, whatever order they arrived in.\n\tscan4Fields := map[string]string{}\n\n\tfor i := 0; i < len(scan4Result2); i += 2 {\n\t\tscan4Fields[scan4Result2[i]] = scan4Result2[i+1]\n\t}\n\n\tfmt.Println(scan4Fields) // >>> map[a:1 b:2]\n\n\tscan4Result3, _, err := rdb.HScanNoValues(ctx, \"myhash\", 0, \"\", 0).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tsort.Strings(scan4Result3)\n\tfmt.Println(scan4Result3) // >>> [a b]","section_id":"the-novalues-option"},{"id":"the-novalues-option-ex3","language":"java","code":"CompletableFuture<Void> scan4Example = asyncCommands\n                    .hset(\"myhash\", Map.of(\"a\", \"1\", \"b\", \"2\"))\n                    .thenCompose(scan4Res1 -> {\n                        System.out.println(scan4Res1);      // >>> 2\n                        return asyncCommands.hscan(\"myhash\");\n                    })\n                    .thenCompose(scan4Res2 -> {\n                        System.out.println(new java.util.TreeMap<>(scan4Res2.getMap()));\n                        // >>> {a=1, b=2}\n                        return asyncCommands.hscanNovalues(\"myhash\");\n                    })\n                    .thenAccept(scan4Res3 -> {\n                        List<String> fields = new java.util.ArrayList<>(scan4Res3.getKeys());\n                        Collections.sort(fields);\n                        System.out.println(fields);         // >>> [a, b]\n                    })\n                    .toCompletableFuture();","section_id":"the-novalues-option"},{"id":"the-novalues-option-ex4","language":"java","code":"Mono<Void> scan4Example = reactiveCommands\n                    .hset(\"myhash\", Map.of(\"a\", \"1\", \"b\", \"2\"))\n                    .flatMap(scan4Res1 -> {\n                        System.out.println(scan4Res1);      // >>> 2\n                        return reactiveCommands.hscan(\"myhash\");\n                    })\n                    .flatMap(scan4Res2 -> {\n                        System.out.println(new java.util.TreeMap<>(scan4Res2.getMap()));\n                        // >>> {a=1, b=2}\n                        return reactiveCommands.hscanNovalues(\"myhash\");\n                    })\n                    .doOnNext(scan4Res3 -> {\n                        List<String> fields = new java.util.ArrayList<>(scan4Res3.getKeys());\n                        Collections.sort(fields);\n                        System.out.println(fields);         // >>> [a, b]\n                    })\n                    .then();","section_id":"the-novalues-option"},{"id":"the-novalues-option-ex5","language":"java","code":"long scan4Result1 = jedis.hset(\"myhash\", Map.of(\"a\", \"1\", \"b\", \"2\"));\n        System.out.println(scan4Result1); // >>> 2\n\n        ScanResult<Map.Entry<String, String>> scan4Result2 = jedis.hscan(\n                \"myhash\", \"0\", new ScanParams()\n        );\n        ArrayList<String> scan4Pairs = new ArrayList<>();\n        for (Map.Entry<String, String> entry : scan4Result2.getResult()) {\n            scan4Pairs.add(entry.getKey() + \"=\" + entry.getValue());\n        }\n        Collections.sort(scan4Pairs);\n        System.out.println(scan4Pairs); // >>> [a=1, b=2]\n\n        ScanResult<String> scan4Result3 = jedis.hscanNoValues(\n                \"myhash\", \"0\", new ScanParams()\n        );\n        ArrayList<String> scan4Fields = new ArrayList<>(scan4Result3.getResult());\n        Collections.sort(scan4Fields);\n        System.out.println(scan4Fields); // >>> [a, b]","section_id":"the-novalues-option"},{"id":"the-novalues-option-ex6","language":"javascript","code":"const scan4Res1 = await client.hSet('myhash', { a: 1, b: 2 });\nconsole.log(scan4Res1); // 2\n\n// HSCAN doesn't promise a field order, so pair entries into an object rather than\n// relying on position.\nconst scan4Res2 = await client.hScan('myhash', '0');\nconst scan4Pairs = Object.fromEntries(scan4Res2.entries.map((e) => [e.field, e.value]));\nconsole.log(scan4Pairs); // {a: '1', b: '2'}\n\nconst scan4Res3 = await client.hScan('myhash', '0', { COUNT: 10 });\nconst items = scan4Res3.entries.map((item) => item.field).sort()\nconsole.log(items); // ['a', 'b']","section_id":"the-novalues-option"},{"id":"the-novalues-option-ex7","language":"javascript","code":"const scan4Res1 = await redis.hset('myhash', { a: 1, b: 2 });\nconsole.log(scan4Res1); // >>> 2\n\n// HSCAN returns field and value interleaved. Redis does not promise an order, so pair\n// them up into an object rather than relying on the position of each element.\nconst [, scan4Flat] = await redis.hscan('myhash', 0);\nconst scan4Pairs = Object.fromEntries(\n  scan4Flat.reduce((acc, v, i) => (i % 2 ? acc : [...acc, [v, scan4Flat[i + 1]]]), [])\n);\nconsole.log(scan4Pairs); // >>> { a: '1', b: '2' }\n\nconst [, scan4Fields] = await redis.hscan('myhash', 0, 'NOVALUES');\nconsole.log(scan4Fields.sort()); // >>> [ 'a', 'b' ]","section_id":"the-novalues-option"},{"id":"the-novalues-option-ex8","language":"javascript","code":"const scan4Res1 = await client.hSet('myhash', { a: 1, b: 2 });\nconsole.log(scan4Res1); // 2\n\n// HSCAN doesn't promise a field order, so pair entries into an object rather than\n// relying on position.\nconst scan4Res2 = await client.hScan('myhash', '0');\nconst scan4Pairs = Object.fromEntries(scan4Res2.entries.map((e) => [e.field, e.value]));\nconsole.log(scan4Pairs); // {a: '1', b: '2'}\n\nconst scan4Res3 = await client.hScan('myhash', '0', { COUNT: 10 });\nconst items = scan4Res3.entries.map((item) => item.field).sort()\nconsole.log(items); // ['a', 'b']","section_id":"the-novalues-option"},{"id":"the-novalues-option-ex9","language":"javascript","code":"const scan4Res1 = await redis.hset('myhash', { a: 1, b: 2 });\nconsole.log(scan4Res1); // >>> 2\n\n// HSCAN returns field and value interleaved. Redis does not promise an order, so pair\n// them up into an object rather than relying on the position of each element.\nconst [, scan4Flat] = await redis.hscan('myhash', 0);\nconst scan4Pairs = Object.fromEntries(\n  scan4Flat.reduce((acc, v, i) => (i % 2 ? acc : [...acc, [v, scan4Flat[i + 1]]]), [])\n);\nconsole.log(scan4Pairs); // >>> { a: '1', b: '2' }\n\nconst [, scan4Fields] = await redis.hscan('myhash', 0, 'NOVALUES');\nconsole.log(scan4Fields.sort()); // >>> [ 'a', 'b' ]","section_id":"the-novalues-option"},{"id":"the-novalues-option-ex10","language":"php","code":"$scan4Result1 = $r->hset('myhash', 'a', 1, 'b', 2);\n        echo $scan4Result1 . PHP_EOL;                        // >>> 2\n\n        [$scan4Cursor, $scan4Pairs] = $r->hscan('myhash', 0);\n        echo json_encode($scan4Pairs) . PHP_EOL;             // >>> {\"a\":\"1\",\"b\":\"2\"}\n\n        // Redis does not promise a field order, so sort before comparing.\n        [$scan4Cursor, $scan4Fields] = $r->hscan('myhash', 0, ['NOVALUES' => true]);\n        sort($scan4Fields);\n        echo implode(', ', $scan4Fields) . PHP_EOL;          // >>> a, b","section_id":"the-novalues-option"},{"id":"the-novalues-option-ex11","language":"python","code":"res = r.hset(\"myhash\", mapping={\"a\": 1, \"b\": 2})\nprint(res)\n# >>> 2\n\ncursor, keys = r.hscan(\"myhash\", 0)\nprint(keys)\n# >>> {'a': '1', 'b': '2'}\n\ncursor, keys = r.hscan(\"myhash\", 0, no_values=True)\nprint(sorted(keys))\n# >>> ['a', 'b']","section_id":"the-novalues-option"},{"id":"the-novalues-option-ex12","language":"rust","code":"match r.hset(\"myhash\", \"a\", \"1\").await {\n            Ok(res) => {\n                let res: i32 = res;\n                println!(\"{res}\");    // >>> 1\n            },\n            Err(e) => {\n                println!(\"Error setting hash field: {e}\");\n                return;\n            }\n        }\n\n        match r.hset(\"myhash\", \"b\", \"2\").await {\n            Ok(res) => {\n                let res: i32 = res;\n                println!(\"{res}\");    // >>> 1\n            },\n            Err(e) => {\n                println!(\"Error setting hash fields: {e}\");\n                return;\n            }\n        }\n\n        let fields = match r.hscan(\"myhash\").await {\n            Ok(iter) => {\n                let items: Vec<Result<(String, String), _>> = iter.collect().await;\n                items.into_iter().filter_map(|r| r.ok()).collect::<std::collections::HashMap<String, String>>()\n            },\n            Err(e) => {\n                println!(\"Error scanning hash: {e}\");\n                return;\n            }\n        };\n\n        println!(\"{fields:?}\");    // >>> {\"a\": \"1\", \"b\": \"2\"}\n\n        // Scan hash keys only (no values)\n        match r.hkeys(\"myhash\").await {\n            Ok(keys) => {\n                let keys: Vec<String> = keys;\n                println!(\"{keys:?}\");    // >>> [\"a\", \"b\"]\n            },\n            Err(e) => {\n                println!(\"Error getting hash keys: {e}\");\n                return;\n            }\n        }","section_id":"the-novalues-option"},{"id":"the-novalues-option-ex13","language":"rust","code":"match r.hset(\"myhash\", \"a\", \"1\") {\n            Ok(res) => {\n                let res: i32 = res;\n                println!(\"{res}\");    // >>> 1\n            },\n            Err(e) => {\n                println!(\"Error setting hash field: {e}\");\n                return;\n            }\n        }\n\n        match r.hset(\"myhash\", \"b\", \"2\") {\n            Ok(res) => {\n                let res: i32 = res;\n                println!(\"{res}\");    // >>> 1\n            },\n            Err(e) => {\n                println!(\"Error setting hash fields: {e}\");\n                return;\n            }\n        }\n\n        match r.hscan(\"myhash\") {\n            Ok(iter) => {\n                let fields: std::collections::HashMap<String, String> = iter.filter_map(|r| r.ok()).collect();\n                println!(\"{fields:?}\");    // >>> {\"a\": \"1\", \"b\": \"2\"}\n            },\n            Err(e) => {\n                println!(\"Error scanning hash: {e}\");\n                return;\n            }\n        }\n\n        // Scan hash keys only (no values)\n        match r.hkeys(\"myhash\") {\n            Ok(keys) => {\n                let keys: Vec<String> = keys;\n                println!(\"{keys:?}\");    // >>> [\"a\", \"b\"]\n            },\n            Err(e) => {\n                println!(\"Error getting hash keys: {e}\");\n                return;\n            }\n        }","section_id":"the-novalues-option"}]}
{"schema_version":2,"id":"commands/scard","title":"SCARD","url":"https://redis.io/docs/latest/commands/scard/","summary":"Returns the number of members in a set.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"4a7bf90fdb39f6ea109ae881b789ea82070f09cb87a4a528f6a187a0b1a62699","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns the set cardinality (number of elements) of the set stored at `key`."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the set.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SADD myset \"Hello\"\n(integer) 1\nredis> SADD myset \"World\"\n(integer) 1\nredis> SCARD myset\n(integer) 2"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the cardinality (number of elements) of the set, or `0` if the key does not exist.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): The cardinality (number of elements) of the set, or 0 if the key does not exist."}],"examples":[]}
{"schema_version":2,"id":"commands/script","title":"SCRIPT","url":"https://redis.io/docs/latest/commands/script/","summary":"A container for Lua scripts management commands.","since":"2.6.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"5b4172e3bb2a343e7c6bc2d4be43b896cd056d3845d523ec256ff0b90e563da6","sections":[{"id":"content","title":"Content","role":"content","text":"This is a container command for script management commands.\n\nTo see the list of available commands you can call [`SCRIPT HELP`](https://redis.io/docs/latest/commands/script-help)."}],"examples":[]}
{"schema_version":2,"id":"commands/script-debug","title":"SCRIPT DEBUG","url":"https://redis.io/docs/latest/commands/script-debug/","summary":"Sets the debug mode of server-side Lua scripts.","since":"3.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"3ce6d9622d598e22ef6d57b1edeca10dbec91681d5e2f47a1a4cf7b8f90bda87","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Set the debug mode for subsequent scripts executed with [`EVAL`](https://redis.io/docs/latest/commands/eval). Redis includes a\ncomplete Lua debugger (LDB) that can be used to make the task of\nwriting complex scripts much simpler. In debug mode Redis acts as a remote\ndebugging server and a client, such as [`redis-cli`](https://redis.io/docs/latest/develop/tools/cli), can execute scripts step by\nstep, set breakpoints, inspect variables, and more. For additional information\nabout LDB see the [Redis Lua debugger](https://redis.io/docs/latest/develop/programmability/lua-debugging) page.\n\n> [!WARNING]\n> Avoid debugging Lua scripts using your production Redis\n> server. Use a development server instead.\n\nYou can enable LDB in one of two modes: asynchronous or synchronous. In asynchronous mode, Redis creates a forked debugging session that does not block the server. Redis rolls back all data changes after the session ends, so you can restart debugging from the same initial state. In synchronous mode, Redis blocks the server while the debugging session is active and retains all data changes after the session ends.\n\n\n\nFor more information about [`EVAL`](https://redis.io/docs/latest/commands/eval) scripts please see [Introduction to Eval Scripts](https://redis.io/docs/latest/develop/programmability/eval-intro)."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>YES | SYNC | NO</code></summary>\n\nSet the debugging mode:\n\n* `YES`: Enable non-blocking asynchronous debugging of Lua scripts (changes are discarded).\n* `SYNC`: Enable blocking synchronous debugging of Lua scripts (saves changes to data).\n* `NO`: Disable Lua script debug mode. Subsequent scripts run normally, without LDB.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[]}
{"schema_version":2,"id":"commands/script-exists","title":"SCRIPT EXISTS","url":"https://redis.io/docs/latest/commands/script-exists/","summary":"Determines whether server-side Lua scripts exist in the script cache.","since":"2.6.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"7b9d0c1e95a98b5e97baa9e21b3820bbc4f5b62af09652d4288536c535b55bd7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns information about the existence of the scripts in the script cache.\n\nThis command accepts one or more SHA1 digests and returns a list of 1 and 0 values to indicate whether each script exists in the script cache. Before you run a pipeline, use this command to check whether Redis has loaded the scripts you need. For missing scripts, use [`SCRIPT LOAD`](https://redis.io/docs/latest/commands/script-load); then use [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) instead of [`EVAL`](https://redis.io/docs/latest/commands/eval) in the pipeline to save bandwidth.\n\nFor more information about [`EVAL`](https://redis.io/docs/latest/commands/eval) scripts see [Introduction to Eval Scripts](https://redis.io/docs/latest/develop/programmability/eval-intro)."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>sha1 [sha1 ...]</code></summary>\n\nOne or more SHA1 digests to look up in the script cache.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): an array of integers that correspond to the specified SHA1 digest arguments.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): an array of integers that correspond to the specified SHA1 digest arguments."}],"examples":[]}
{"schema_version":2,"id":"commands/script-flush","title":"SCRIPT FLUSH","url":"https://redis.io/docs/latest/commands/script-flush/","summary":"Removes all server-side Lua scripts from the script cache.","since":"2.6.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"e4dbad47d57cd21590c712447fe21cfb13af599659bb9feed42aa5430f1b17fd","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Flush the Lua scripts cache.\n\nBy default, `SCRIPT FLUSH` will synchronously flush the cache.\nStarting with Redis 6.2, setting the `lazyfree-lazy-user-flush` configuration directive to `yes` changes the default flush mode to asynchronous.\n\nFor more information about [`EVAL`](https://redis.io/docs/latest/commands/eval) scripts please see [Introduction to Eval Scripts](https://redis.io/docs/latest/develop/programmability/eval-intro)."},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>ASYNC | SYNC</code></summary>\n\nFlush the script cache asynchronously (`ASYNC`) or synchronously (`SYNC`). The default is set by the `lazyfree-lazy-user-flush` configuration directive.\n\n</details>"},{"id":"details","title":"Details","role":"content","text":""},{"id":"behavior-change-history","title":"Behavior change history","role":"content","text":"*   `>= 6.2.0`: Default flush behavior now configurable by the **lazyfree-lazy-user-flush** configuration directive."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[]}
{"schema_version":2,"id":"commands/script-help","title":"SCRIPT HELP","url":"https://redis.io/docs/latest/commands/script-help/","summary":"Returns helpful text about the different subcommands.","since":"5.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"f4ba213597c661636f8ce66d646122ad847dff9d8e03670c205ea77bc3ca8f95","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `SCRIPT HELP` command returns a helpful text describing the different subcommands."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of sub-commands and their descriptions.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of sub-commands and their descriptions."}],"examples":[]}
{"schema_version":2,"id":"commands/script-kill","title":"SCRIPT KILL","url":"https://redis.io/docs/latest/commands/script-kill/","summary":"Terminates a server-side Lua script during execution.","since":"2.6.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"c78e1fb3a643ac1eb0f0c9ec146cba4174e0a69f4c5da5288356555c3025db4b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Kills the currently executing [`EVAL`](https://redis.io/docs/latest/commands/eval) script, assuming no write operation was yet\nperformed by the script.\n\nThis command is mainly useful to kill a script that is running for too much\ntime(for instance, because it entered an infinite loop because of a bug).\nThe script will be killed, and the client currently blocked into EVAL will see\nthe command returning with an error.\n\nIf the script has already performed write operations, it can not be killed in this\nway because it would violate Lua's script atomicity contract.\nIn such a case, only `SHUTDOWN NOSAVE` can kill the script, killing\nthe Redis process in a hard way and preventing it from persisting with half-written\ninformation.\n\nFor more information about [`EVAL`](https://redis.io/docs/latest/commands/eval) scripts please refer to [Introduction to Eval Scripts](https://redis.io/docs/latest/develop/programmability/eval-intro)."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[]}
{"schema_version":2,"id":"commands/script-load","title":"SCRIPT LOAD","url":"https://redis.io/docs/latest/commands/script-load/","summary":"Loads a server-side Lua script to the script cache.","since":"2.6.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"497bf58fee57b9373eb20a25cd4e7ae9af6ad69abf0d518111e1cf2b3442d247","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Load a script into the scripts cache, without executing it.\nAfter the specified command is loaded into the script cache it will be callable\nusing [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) with the correct SHA1 digest of the script, exactly like after\nthe first successful invocation of [`EVAL`](https://redis.io/docs/latest/commands/eval).\n\nThe script is guaranteed to stay in the script cache forever (unless `SCRIPT\nFLUSH` is called).\n\nThe command works in the same way even if the script was already present in the\nscript cache.\n\nFor more information about [`EVAL`](https://redis.io/docs/latest/commands/eval) scripts please see [Introduction to Eval Scripts](https://redis.io/docs/latest/develop/programmability/eval-intro)."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>script</code></summary>\n\nThe Lua script to load into the script cache. Its SHA1 digest is returned for use with `EVALSHA`.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the SHA1 digest of the script added into the script cache.\n\n**RESP3:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the SHA1 digest of the script added into the script cache."}],"examples":[]}
{"schema_version":2,"id":"commands/sdiff","title":"SDIFF","url":"https://redis.io/docs/latest/commands/sdiff/","summary":"Returns the difference of multiple sets.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"08bff82b656ab68afe804a947033ef10ac0f637ab71ea7ae2a37217b085b15e2","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nReturns the members of the set resulting from the difference between the first\nset and all the successive sets.\n\nFor example:\n\n[code example]\n\nKeys that do not exist are considered to be empty sets."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key [key ...]</code></summary>\n\nOne or more set keys. The result is the members of the first set that are not present in any of the subsequent sets.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SADD key1 \"a\"\n(integer) 1\nredis> SADD key1 \"b\"\n(integer) 1\nredis> SADD key1 \"c\"\n(integer) 1\nredis> SADD key2 \"c\"\n(integer) 1\nredis> SADD key2 \"d\"\n(integer) 1\nredis> SADD key2 \"e\"\n(integer) 1\nredis> SDIFF key1 key2\n1) \"a\"\n2) \"b\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list with members of the resulting set.\n\n**RESP3:**\n\n[Set reply](../../develop/reference/protocol-spec#sets): a set with the members of the resulting set."},{"id":"see-also","title":"See also","role":"related","text":"[`SDIFFCARD`](https://redis.io/docs/latest/commands/sdiffcard) | [`SDIFFSTORE`](https://redis.io/docs/latest/commands/sdiffstore)"}],"examples":[{"id":"overview-ex0","language":"plaintext","code":"key1 = {a,b,c,d}\nkey2 = {c}\nkey3 = {a,c,e}\nSDIFF key1 key2 key3 = {b,d}","section_id":"overview"}]}
{"schema_version":2,"id":"commands/sdiffcard","title":"SDIFFCARD","url":"https://redis.io/docs/latest/commands/sdiffcard/","summary":"Returns the number of members of the difference between the first set and all successive sets.","since":"8.10.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"807b2d2cda72ab4eb825a195ad1c328508aca00daacfce27c09b0927c74715d8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nReturns the cardinality of the difference between the first set and all the successive sets. This is the count-only counterpart of [`SDIFF`](https://redis.io/docs/latest/commands/sdiff): it returns just the number of elements in the difference, not the members themselves."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>numkeys</code></summary>\n\nThe number of keys that follow.\n\n</details>\n\n<details open><summary><code>key [key ...]</code></summary>\n\nOne or more set keys. The result counts the members of the first set that are not present in any of the subsequent sets.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>LIMIT limit</code></summary>\n\nStop counting once the cardinality reaches `limit`. `0` (the default) means no limit.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SADD key1 \"a\"\n(integer) 1\nredis> SADD key1 \"b\"\n(integer) 1\nredis> SADD key1 \"c\"\n(integer) 1\nredis> SADD key2 \"c\"\n(integer) 1\nredis> SADD key2 \"d\"\n(integer) 1\nredis> SADD key2 \"e\"\n(integer) 1\nredis> SDIFF key1 key2\n1) \"b\"\n2) \"a\"\nredis> SDIFFCARD 2 key1 key2\n(integer) 2\nredis> SDIFFCARD 2 key1 key2 LIMIT 1\n(integer) 1"},{"id":"details","title":"Details","role":"content","text":"Keys that do not exist are considered to be empty sets.\n\nWhen provided with the optional `LIMIT` argument (which defaults to `0`, meaning unlimited), if the difference cardinality reaches `limit` partway through the computation, the command stops and returns `limit` as the cardinality. This ensures a significant speedup for queries where the limit is lower than the actual difference cardinality."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of elements in the resulting difference.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of elements in the resulting difference."},{"id":"see-also","title":"See also","role":"related","text":"[`SDIFF`](https://redis.io/docs/latest/commands/sdiff) | [`SDIFFSTORE`](https://redis.io/docs/latest/commands/sdiffstore)"},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Redis sets](https://redis.io/docs/latest/develop/data-types/sets)\n- [Multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations)"}],"examples":[]}
{"schema_version":2,"id":"commands/sdiffstore","title":"SDIFFSTORE","url":"https://redis.io/docs/latest/commands/sdiffstore/","summary":"Stores the difference of multiple sets in a key.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"578fc1b806e3822257adb571a0d6d14283258fc30b734ca482136fce7eb0b19c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nThis command is equal to [`SDIFF`](https://redis.io/docs/latest/commands/sdiff), but instead of returning the resulting set, it\nis stored in `destination`.\n\nIf `destination` already exists, it is overwritten."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>destination</code></summary>\n\nThe key to store the resulting set in.\n\n</details>\n\n<details open><summary><code>key [key ...]</code></summary>\n\nOne or more set keys. The difference is the members of the first set that are not present in any of the subsequent sets.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SADD key1 \"a\"\n(integer) 1\nredis> SADD key1 \"b\"\n(integer) 1\nredis> SADD key1 \"c\"\n(integer) 1\nredis> SADD key2 \"c\"\n(integer) 1\nredis> SADD key2 \"d\"\n(integer) 1\nredis> SADD key2 \"e\"\n(integer) 1\nredis> SDIFFSTORE key key1 key2\n(integer) 2\nredis> SMEMBERS key\n1) \"a\"\n2) \"b\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of elements in the resulting set.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of elements in the resulting set."},{"id":"see-also","title":"See also","role":"related","text":"[`SDIFF`](https://redis.io/docs/latest/commands/sdiff) | [`SDIFFCARD`](https://redis.io/docs/latest/commands/sdiffcard)"}],"examples":[]}
{"schema_version":2,"id":"commands/select","title":"SELECT","url":"https://redis.io/docs/latest/commands/select/","summary":"Changes the selected database.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"520b22ddcf70fa3d0249fdb0f2b62ea82c77ebe2a64b3b468229a1874354395f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Select the logical Redis database having the specified zero-based numeric index.\nNew connections always use database zero.\n\nSelectable Redis databases are a form of namespacing: all databases are still persisted in the same RDB or AOF file. However, different databases can have keys with the same name, and commands like [`FLUSHDB`](https://redis.io/docs/latest/commands/flushdb), [`SWAPDB`](https://redis.io/docs/latest/commands/swapdb) or [`RANDOMKEY`](https://redis.io/docs/latest/commands/randomkey) work on specific databases.\n\nUse Redis databases to separate keys within the same application when needed. Don’t use them to run multiple unrelated applications in a single Redis instance.\n\nWhen using Redis Cluster, the `SELECT` command cannot be used, since Redis Cluster only supports database zero. Having multiple databases would be useless and an unnecessary source of complexity. Commands operating atomically on a single database would not be possible with the Redis Cluster's design and goals.\n\nSince the currently selected database is a property of the connection, clients should track the currently selected database and re-select it on reconnection. While there is no command in order to query the selected database in the current connection, the [`CLIENT LIST`](https://redis.io/docs/latest/commands/client-list) output shows, for each client, the currently selected database."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>index</code></summary>\n\nThe zero-based index of the database to switch to.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | Redis Software does not support shared databases due to potential negative performance impacts and blocks any related commands. The `SELECT` command is supported solely for compatibility with Redis Open Source but does not perform any operations in Redis Software. |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[]}
{"schema_version":2,"id":"commands/set","title":"SET","url":"https://redis.io/docs/latest/commands/set/","summary":"Sets the string value of a key, ignoring its type. The key is created if it doesn't exist.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:12+01:00","page_type":"content","content_hash":"a3c29db62890298d08c621e74bb03832f6f051982dc8f8e27b5c62b720612095","sections":[{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key.\n\n</details>\n\n<details open><summary><code>value</code></summary>\n\nThe string value to set.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"The following options modify the command's behavior. The condition options (`NX`, `XX`, `IFEQ`, `IFNE`, `IFDEQ`, `IFDNE`) are mutually exclusive, as are the expiration options (`EX`, `PX`, `EXAT`, `PXAT`, `KEEPTTL`).\n\n<details open><summary><code>NX</code></summary>\n\nOnly set the key if it does not already exist.\n\n</details>\n\n<details open><summary><code>XX</code></summary>\n\nOnly set the key if it already exists.\n\n</details>\n\n<details open><summary><code>IFEQ ifeq-value</code></summary>\n\nSet 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.\n\n</details>\n\n<details open><summary><code>IFNE ifne-value</code></summary>\n\nSet 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.\n\n</details>\n\n<details open><summary><code>IFDEQ ifdeq-digest</code></summary>\n\nSet the key's value and expiration only if the hash digest of its current value is equal to `ifdeq-digest`. If the key doesn't exist, it won't be created. See the [Hash Digest](#hash-digest) section below for more information.\n\n</details>\n\n<details open><summary><code>IFDNE ifdne-digest</code></summary>\n\nSet the key's value and expiration only if the hash digest of its current value is not equal to `ifdne-digest`. If the key doesn't exist, it will be created. See the [Hash Digest](#hash-digest) section below for more information.\n\n</details>\n\n<details open><summary><code>GET</code></summary>\n\nReturn the old string stored at the key, or nil if the key did not exist. An error is returned and `SET` is aborted if the value stored at the key is not a string.\n\n</details>\n\n<details open><summary><code>EX seconds</code></summary>\n\nSet the specified expire time, in seconds (a positive integer).\n\n</details>\n\n<details open><summary><code>PX milliseconds</code></summary>\n\nSet the specified expire time, in milliseconds (a positive integer).\n\n</details>\n\n<details open><summary><code>EXAT unix-time-seconds</code></summary>\n\nSet the specified Unix time at which the key will expire, in seconds (a positive integer).\n\n</details>\n\n<details open><summary><code>PXAT unix-time-milliseconds</code></summary>\n\nSet the specified Unix time at which the key will expire, in milliseconds (a positive integer).\n\n</details>\n\n<details open><summary><code>KEEPTTL</code></summary>\n\nRetain the time to live associated with the key.\n\n</details>\n\nNote: Since the `SET` command options can replace [`SETNX`](https://redis.io/docs/latest/commands/setnx), [`SETEX`](https://redis.io/docs/latest/commands/setex), [`PSETEX`](https://redis.io/docs/latest/commands/psetex), [`GETSET`](https://redis.io/docs/latest/commands/getset), it is possible that in future versions of Redis these commands will be deprecated and finally removed."},{"id":"examples","title":"Examples","role":"example","text":"Foundational: Set the string value of a key using SET (creates key if needed, overwrites existing value, supports expiration options)\n\n**Difficulty:** Beginner\n\n**Commands:** SET, GET\n\n**Complexity:**\n- SET: O(1)\n- GET: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"details","title":"Details","role":"content","text":""},{"id":"hash-digest","title":"Hash digest","role":"content","text":"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`](https://redis.io/docs/latest/commands/digest) command, which returns it as a hexadecimal string that you can use with the `IFDEQ` and `IFDNE` options, and also the [`DELEX`](https://redis.io/docs/latest/commands/delex) command's `IFDEQ` and `IFDNE` options."},{"id":"patterns","title":"Patterns","role":"content","text":"Note: The following pattern is discouraged in favor of [the Redlock algorithm](https://redis.io/docs/latest/develop/clients/patterns/distributed-locks) which is only a bit more complex to implement, but offers better guarantees and is fault tolerant.\n\nThe command `SET resource-name anystring NX EX max-lock-time` is a simple way to implement a locking system with Redis.\n\nA client can acquire the lock if the above command returns `OK` (or retry after some time if the command returns Nil), and remove the lock just using [`DEL`](https://redis.io/docs/latest/commands/del).\n\nThe lock will be auto-released after the expire time is reached.\n\nIt is possible to make this system more robust modifying the unlock schema as follows:\n\n* Instead of setting a fixed string, set a non-guessable large random string, called token.\n* Instead of releasing the lock with [`DEL`](https://redis.io/docs/latest/commands/del), send a script that only removes the key if the value matches.\n\nThis avoids that a client will try to release the lock after the expire time deleting the key created by another client that acquired the lock later.\n\nAn example of unlock script would be similar to the following:\n\n    if redis.call(\"get\",KEYS[1]) == ARGV[1]\n    then\n        return redis.call(\"del\",KEYS[1])\n    else\n        return 0\n    end\n\nThe script should be called with `EVAL ...script... 1 resource-name token-value`"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n* If `GET` was not specified, one of the following:\n  * [Null bulk string reply](../../develop/reference/protocol-spec#bulk-strings) in the following two cases.\n    * The key doesn’t exist and `XX/IFEQ/IFDEQ` was specified. The key was not created.\n    * The key exists, and `NX` was specified or a specified `IFEQ/IFNE/IFDEQ/IFDNE` condition is false. The key was not set.\n  * [Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`: The key was set.\n* If `GET` was specified, one of the following:\n  * [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.\n  * [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): The previous value of the key, whether the key was set or not.\n\n**RESP3:**\n\n* If `GET` was not specified, one of the following:\n  * [Null reply](../../develop/reference/protocol-spec#nulls) in the following two cases.\n    * The key doesn’t exist and `XX/IFEQ/IFDEQ` was specified. The key was not created.\n    * The key exists, and `NX` was specified or a specified `IFEQ/IFNE/IFDEQ/IFDNE` condition is false. The key was not set.\n  * [Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`: The key was set.\n* If `GET` was specified, one of the following:\n  * [Null reply](../../develop/reference/protocol-spec#nulls): The key didn't exist before the `SET` operation, whether the key was created of not.\n  * [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): The previous value of the key, whether the key was set or not."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> SET mykey \"Hello\"\nOK\n> GET mykey\n\"Hello\"\n> SET anotherkey \"will expire in a minute\" EX 60\nOK","section_id":"examples"},{"id":"examples-ex1","language":"csharp","code":"using NRedisStack.Tests;\nusing StackExchange.Redis;\n\npublic class SetGetExample\n{\n    public void Run()\n    {\n        var muxer = ConnectionMultiplexer.Connect(\"localhost:6379\");\n        var db = muxer.GetDatabase();\n\n        bool status = db.StringSet(\"bike:1\", \"Process 134\");\n\n        if (status)\n            Console.WriteLine(\"Successfully added a bike.\");\n\n        var value = db.StringGet(\"bike:1\");\n\n        if (value.HasValue)\n            Console.WriteLine(\"The name of the bike is: \" + value + \".\");\n\n    }\n}","section_id":"examples"},{"id":"examples-ex2","language":"go","code":"package example_commands_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/redis/go-redis/v9\"\n)\n\nfunc ExampleClient_Set_and_get() {\n\tctx := context.Background()\n\n\trdb := redis.NewClient(&redis.Options{\n\t\tAddr:     \"localhost:6379\",\n\t\tPassword: \"\", // no password docs\n\t\tDB:       0,  // use default DB\n\t})\n\n\n\n\terr := rdb.Set(ctx, \"bike:1\", \"Process 134\", 0).Err()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(\"OK\")\n\n\tvalue, err := rdb.Get(ctx, \"bike:1\").Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tfmt.Printf(\"The name of the bike is %s\", value)\n\n}","section_id":"examples"},{"id":"examples-ex3","language":"java","code":"package io.redis.examples.async;\n\nimport io.lettuce.core.RedisClient;\nimport io.lettuce.core.api.async.RedisAsyncCommands;\nimport io.lettuce.core.api.StatefulRedisConnection;\n\nimport java.util.concurrent.CompletableFuture;\n\npublic class SetGetExample {\n\n    public void run() {\n        RedisClient redisClient = RedisClient.create(\"redis://localhost:6379\");\n\n        try (StatefulRedisConnection<String, String> connection = redisClient.connect()) {\n            RedisAsyncCommands<String, String> asyncCommands = connection.async();\n\n\n            CompletableFuture<Void> setGetExample = asyncCommands.set(\"bike:1\", \"Process 134\")\n                    .thenCompose(res1 -> {\n                        System.out.println(res1); // >>> OK\n                        return asyncCommands.get(\"bike:1\");\n                    }).thenAccept(res2 -> {\n                        System.out.println(res2); // >>> Process 134\n                    }).toCompletableFuture();\n\n            setGetExample.join();\n        } finally {\n            redisClient.shutdown();\n        }\n    }\n}","section_id":"examples"},{"id":"examples-ex4","language":"java","code":"package io.redis.examples.reactive;\n\nimport io.lettuce.core.RedisClient;\nimport io.lettuce.core.api.reactive.RedisReactiveCommands;\nimport io.lettuce.core.api.StatefulRedisConnection;\n\nimport reactor.core.publisher.Mono;\n\npublic class SetGetExample {\n\n    public void run() {\n        RedisClient redisClient = RedisClient.create(\"redis://localhost:6379\");\n\n        try (StatefulRedisConnection<String, String> connection = redisClient.connect()) {\n            RedisReactiveCommands<String, String> reactiveCommands = connection.reactive();\n\n\n            Mono<Void> setGetExample = reactiveCommands.set(\"bike:1\", \"Process 134\")\n                    .doOnNext(res1 -> {\n                        System.out.println(res1); // >>> OK\n                    })\n                    .then(reactiveCommands.get(\"bike:1\"))\n                    .doOnNext(res2 -> {\n                        System.out.println(res2); // >>> Process 134\n                    })\n                    .then();\n\n            setGetExample.block();\n        } finally {\n            redisClient.shutdown();\n        }\n    }\n}","section_id":"examples"},{"id":"examples-ex5","language":"java","code":"package io.redis.examples;\n\nimport redis.clients.jedis.RedisClient;\n\n\npublic class SetGetExample {\n\n  public void run() {\n\n    RedisClient jedis = RedisClient.create(\"redis://localhost:6379\");\n\n    String status = jedis.set(\"bike:1\", \"Process 134\");\n\n    if (\"OK\".equals(status)) System.out.println(\"Successfully added a bike.\");\n\n    String value = jedis.get(\"bike:1\");\n\n    if (value != null) System.out.println(\"The name of the bike is: \" + value + \".\");\n\n\n    jedis.close();\n  }\n}","section_id":"examples"},{"id":"examples-ex6","language":"javascript","code":"import { createClient } from 'redis';\n\nconst client = createClient();\n\nclient.on('error', err => console.log('Redis Client Error', err));\n\nawait client.connect().catch(console.error);\n\nawait client.set('bike:1', 'Process 134');\nconst value = await client.get('bike:1');\nconsole.log(value);\n// returns 'Process 134'\n\nawait client.close();","section_id":"examples"},{"id":"examples-ex7","language":"javascript","code":"import assert from 'node:assert';\nimport { Redis } from 'ioredis';\n\nconst redis = new Redis();\n\n\nconst res1 = await redis.set('bike:1', 'Process 134');\nconsole.log(res1); // >>> OK\n\nconst res2 = await redis.get('bike:1');\nconsole.log(res2); // >>> Process 134\n\n\nredis.disconnect();","section_id":"examples"},{"id":"examples-ex8","language":"javascript","code":"import { createClient } from 'redis';\n\nconst client = createClient();\n\nclient.on('error', err => console.log('Redis Client Error', err));\n\nawait client.connect().catch(console.error);\n\nawait client.set('bike:1', 'Process 134');\nconst value = await client.get('bike:1');\nconsole.log(value);\n// returns 'Process 134'\n\nawait client.close();","section_id":"examples"},{"id":"examples-ex9","language":"javascript","code":"import assert from 'node:assert';\nimport { Redis } from 'ioredis';\n\nconst redis = new Redis();\n\n\nconst res1 = await redis.set('bike:1', 'Process 134');\nconsole.log(res1); // >>> OK\n\nconst res2 = await redis.get('bike:1');\nconsole.log(res2); // >>> Process 134\n\n\nredis.disconnect();","section_id":"examples"},{"id":"examples-ex10","language":"php","code":"<?php\nuse Predis\\Client as PredisClient;\n\nclass SetGetTest\n{\n    public function testSetGet() {\n        $r = new PredisClient([\n            'scheme'   => 'tcp',\n            'host'     => '127.0.0.1',\n            'port'     => 6379,\n            'password' => '',\n            'database' => 0,\n        ]);\n\n\n        $res1 = $r->set('bike:1', 'Process 134');\n        echo $res1 . PHP_EOL; // >>> OK\n\n        $res2 = $r->get('bike:1');\n        echo $res2 . PHP_EOL; // >>> Process 134\n\n    }\n}","section_id":"examples"},{"id":"examples-ex11","language":"python","code":"\"\"\"\nCode samples for data structure store quickstart pages:\n    https://redis.io/docs/latest/develop/get-started/data-store/\n\"\"\"\n\nimport redis\n\nr = redis.Redis(host=\"localhost\", port=6379, db=0, decode_responses=True)\n\nres = r.set(\"bike:1\", \"Process 134\")\nprint(res)\n# >>> True\n\nres = r.get(\"bike:1\")\nprint(res)\n# >>> \"Process 134\"","section_id":"examples"},{"id":"examples-ex12","language":"ruby","code":"require 'redis'\n\nr = Redis.new\n\n\nres1 = r.set('bike:1', 'Process 134')\nputs res1 # >>> OK\n\nres2 = r.get('bike:1')\nputs res2 # >>> Process 134","section_id":"examples"},{"id":"examples-ex13","language":"rust","code":"mod set_and_get_tests {\n    use redis::AsyncCommands;\n\n    async fn run() {\n        let mut r = match redis::Client::open(\"redis://127.0.0.1\") {\n            Ok(client) => match client.get_multiplexed_async_connection().await {\n                Ok(conn) => conn,\n                Err(e) => {\n                    println!(\"Failed to connect to Redis: {e}\");\n                    return;\n                }\n            },\n            Err(e) => {\n                println!(\"Failed to create Redis client: {e}\");\n                return;\n            }\n        };\n\n\n        if let Ok(res1) = r.set(\"bike:1\", \"Process 134\").await {\n            let res1: String = res1;\n            println!(\"{res1}\"); // >>> OK\n        }\n\n        if let Ok(res2) = r.get(\"bike:1\").await {\n            let res2: String = res2;\n            println!(\"{res2}\"); // >>> Process 134\n        }\n\n    }\n}","section_id":"examples"},{"id":"examples-ex14","language":"rust","code":"mod set_and_get_tests {\n    use redis::Commands;\n\n    fn run() {\n        let mut r = match redis::Client::open(\"redis://127.0.0.1\") {\n            Ok(client) => match client.get_connection() {\n                Ok(conn) => conn,\n                Err(e) => {\n                    println!(\"Failed to connect to Redis: {e}\");\n                    return;\n                }\n            },\n            Err(e) => {\n                println!(\"Failed to create Redis client: {e}\");\n                return;\n            }\n        };\n\n\n        if let Ok(res1) = r.set(\"bike:1\", \"Process 134\") {\n            let res1: String = res1;\n            println!(\"{res1}\"); // >>> OK\n        }\n\n        if let Ok(res2) = r.get(\"bike:1\") {\n            let res2: String = res2;\n            println!(\"{res2}\"); // >>> Process 134\n        }\n\n    }\n}","section_id":"examples"}]}
{"schema_version":2,"id":"commands/setbit","title":"SETBIT","url":"https://redis.io/docs/latest/commands/setbit/","summary":"Sets or clears the bit at offset of the string value. Creates the key if it doesn't exist.","since":"2.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:12+01:00","page_type":"content","content_hash":"89ff480dd8ec716cfdd10e920ca05b42c0a6dceebe9fd56610b5589887845e1b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Sets or clears the bit at _offset_ in the string value stored at _key_.\n\nThe bit is either set or cleared depending on _value_, which can be either 0 or\n1.\n\nWhen _key_ does not exist, a new string value is created.\nThe string is grown to make sure it can hold a bit at _offset_.\nThe _offset_ argument is required to be greater than or equal to 0, and smaller\nthan 2^32 (this limits bitmaps to 512MB).\nWhen the string at _key_ is grown, added bits are set to 0.\n\n> [!WARNING]\n> When setting the last possible bit (_offset_ equal to 2^32 -1) and\n> the string value stored at _key_ does not yet hold a string value, or holds a\n> small string value, Redis needs to allocate all intermediate memory which can\n> block the server for some time.\n>\n> Note that once this first allocation is done, subsequent calls to `SETBIT` for\n> the same _key_ will not have the allocation overhead."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the string.\n\n</details>\n\n<details open><summary><code>offset</code></summary>\n\nThe zero-based bit offset to set. The string is zero-padded if the offset is beyond its current length.\n\n</details>\n\n<details open><summary><code>value</code></summary>\n\nThe bit value to set: `0` or `1`.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SETBIT mykey 7 1\n(integer) 0\nredis> SETBIT mykey 7 0\n(integer) 1\nredis> GET mykey\n\"\\x00\""},{"id":"details","title":"Details","role":"content","text":""},{"id":"pattern-accessing-the-entire-bitmap","title":"Pattern: accessing the entire bitmap","role":"content","text":"There are cases when you need to set all the bits of single bitmap at once, for\nexample when initializing it to a default non-zero value. It is possible to do\nthis with multiple calls to the `SETBIT` command, one for each bit that needs to\nbe set. However, so as an optimization you can use a single [`SET`](https://redis.io/docs/latest/commands/set) command to set\nthe entire bitmap.\n\nBitmaps are not an actual data type, but a set of bit-oriented operations\ndefined on the String type (for more information refer to the\n[Bitmaps section of the Data Types Introduction page](https://redis.io/docs/latest/develop/data-types#bitmaps)). This means that\nbitmaps can be used with string commands, and most importantly with [`SET`](https://redis.io/docs/latest/commands/set) and\n[`GET`](https://redis.io/docs/latest/commands/get).\n\nBecause Redis' strings are binary-safe, a bitmap is trivially encoded as a bytes\nstream. The first byte of the string corresponds to offsets 0..7 of\nthe bitmap, the second byte to the 8..15 range, and so forth.\n\nFor example, after setting a few bits, getting the string value of the bitmap\nwould look like this:\n\n[code example]\n\nBy getting the string representation of a bitmap, the client can then parse the\nresponse's bytes by extracting the bit values using native bit operations in its\nnative programming language. Symmetrically, it is also possible to set an entire\nbitmap by performing the bits-to-bytes encoding in the client and calling [`SET`](https://redis.io/docs/latest/commands/set)\nwith the resultant string."},{"id":"pattern-setting-multiple-bits","title":"Pattern: setting multiple bits","role":"content","text":"`SETBIT` excels at setting single bits, and can be called several times when\nmultiple bits need to be set. To optimize this operation you can replace\nmultiple `SETBIT` calls with a single call to the variadic [`BITFIELD`](https://redis.io/docs/latest/commands/bitfield) command\nand the use of fields of type `u1`.\n\nFor example, the example above could be replaced by:\n\n[code example]"},{"id":"advanced-pattern-accessing-bitmap-ranges","title":"Advanced pattern: accessing bitmap ranges","role":"content","text":"It is also possible to use the [`GETRANGE`](https://redis.io/docs/latest/commands/getrange) and [`SETRANGE`](https://redis.io/docs/latest/commands/setrange) string commands to\nefficiently access a range of bit offsets in a bitmap. Below is a sample\nimplementation in idiomatic Redis Lua scripting that can be run with the [`EVAL`](https://redis.io/docs/latest/commands/eval)\ncommand:\n\n[code example]\n\n**Note:** the implementation for getting a range of bit offsets from a bitmap is\nleft as an exercise to the reader."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the original bit value stored at _offset_.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the original bit value stored at _offset_."}],"examples":[{"id":"pattern-accessing-the-entire-bitmap-ex0","language":"plaintext","code":"> SETBIT bitmapsarestrings 2 1\n> SETBIT bitmapsarestrings 3 1\n> SETBIT bitmapsarestrings 5 1\n> SETBIT bitmapsarestrings 10 1\n> SETBIT bitmapsarestrings 11 1\n> SETBIT bitmapsarestrings 14 1\n> GET bitmapsarestrings\n\"42\"","section_id":"pattern-accessing-the-entire-bitmap"},{"id":"pattern-setting-multiple-bits-ex0","language":"plaintext","code":"> BITFIELD bitsinabitmap SET u1 2 1 SET u1 3 1 SET u1 5 1 SET u1 10 1 SET u1 11 1 SET u1 14 1","section_id":"pattern-setting-multiple-bits"},{"id":"advanced-pattern-accessing-bitmap-ranges-ex0","language":"plaintext","code":"--[[\nSets a bitmap range\n\nBitmaps are stored as Strings in Redis. A range spans one or more bytes,\nso we can call [`SETRANGE`](https://redis.io/docs/latest/commands/setrange) when entire bytes need to be set instead of flipping\nindividual bits. Also, to avoid multiple internal memory allocations in\nRedis, we traverse in reverse.\nExpected input:\n  KEYS[1] - bitfield key\n  ARGV[1] - start offset (0-based, inclusive)\n  ARGV[2] - end offset (same, should be bigger than start, no error checking)\n  ARGV[3] - value (should be 0 or 1, no error checking)\n]]--\n\n-- A helper function to stringify a binary string to semi-binary format\nlocal function tobits(str)\n  local r = ''\n  for i = 1, string.len(str) do\n    local c = string.byte(str, i)\n    local b = ' '\n    for j = 0, 7 do\n      b = tostring(bit.band(c, 1)) .. b\n      c = bit.rshift(c, 1)\n    end\n    r = r .. b\n  end\n  return r\nend\n\n-- Main\nlocal k = KEYS[1]\nlocal s, e, v = tonumber(ARGV[1]), tonumber(ARGV[2]), tonumber(ARGV[3])\n\n-- First treat the dangling bits in the last byte\nlocal ms, me = s % 8, (e + 1) % 8\nif me > 0 then\n  local t = math.max(e - me + 1, s)\n  for i = e, t, -1 do\n    redis.call('SETBIT', k, i, v)\n  end\n  e = t\nend\n\n-- Then the danglings in the first byte\nif ms > 0 then\n  local t = math.min(s - ms + 7, e)\n  for i = s, t, 1 do\n    redis.call('SETBIT', k, i, v)\n  end\n  s = t + 1\nend\n\n-- Set a range accordingly, if at all\nlocal rs, re = s / 8, (e + 1) / 8\nlocal rl = re - rs\nif rl > 0 then\n  local b = '\\255'\n  if 0 == v then\n    b = '\\0'\n  end\n  redis.call('SETRANGE', k, rs, string.rep(b, rl))\nend","section_id":"advanced-pattern-accessing-bitmap-ranges"}]}
{"schema_version":2,"id":"commands/setex","title":"SETEX","url":"https://redis.io/docs/latest/commands/setex/","summary":"Sets the string value and expiration time of a key. Creates the key if it doesn't exist.","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:12+01:00","page_type":"content","content_hash":"95d24fa358da3bd85259c5f549172d0775911da80ac9df99b89d8a0a4d926716","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Set `key` to hold the string `value` and set `key` to timeout after a given\nnumber of seconds.\nThis command is equivalent to:\n\n[code example]\n\nAn error is returned when `seconds` is invalid."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key.\n\n</details>\n\n<details open><summary><code>seconds</code></summary>\n\nThe time to live, in seconds.\n\n</details>\n\n<details open><summary><code>value</code></summary>\n\nThe value to set.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SETEX mykey 10 \"Hello\"\nOK\nredis> TTL mykey\n(integer) 10\nredis> GET mykey\n\"Hello\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."},{"id":"see-also","title":"See also","role":"related","text":"[`TTL`](https://redis.io/docs/latest/commands/ttl)"}],"examples":[{"id":"overview-ex0","language":"plaintext","code":"SET key value EX seconds","section_id":"overview"}]}
{"schema_version":2,"id":"commands/setnx","title":"SETNX","url":"https://redis.io/docs/latest/commands/setnx/","summary":"Set the string value of a key only when the key doesn't exist.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:12+01:00","page_type":"content","content_hash":"2a5dfc272b6fd29fe9ed7fd08798f39b0001253e4905c073be456e7bcec0e9f8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Set `key` to hold string `value` if `key` does not exist.\nIn that case, it is equal to [`SET`](https://redis.io/docs/latest/commands/set).\nWhen `key` already holds a value, no operation is performed.\n`SETNX` is short for \"**SET** if **N**ot e**X**ists\"."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key.\n\n</details>\n\n<details open><summary><code>value</code></summary>\n\nThe value to set, only if the key does not already exist.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SETNX mykey \"Hello\"\n(integer) 1\nredis> SETNX mykey \"World\"\n(integer) 0\nredis> GET mykey\n\"Hello\""},{"id":"details","title":"Details","role":"content","text":""},{"id":"design-pattern-locking-with-setnx","title":"Design pattern: locking with `SETNX`","role":"content","text":"**Please note that:**\n\n1. The following pattern is discouraged in favor of [the Redlock algorithm](https://redis.io/docs/latest/develop/clients/patterns/distributed-locks) which is only a bit more complex to implement, but offers better guarantees and is fault tolerant.\n2. We document the old pattern anyway because certain existing implementations link to this page as a reference. Moreover it is an interesting example of how Redis commands can be used in order to mount programming primitives.\n3. Anyway even assuming a single-instance locking primitive, starting with 2.6.12 it is possible to create a much simpler locking primitive, equivalent to the one discussed here, using the [`SET`](https://redis.io/docs/latest/commands/set) command to acquire the lock, and a simple Lua script to release the lock. The pattern is documented in the [`SET`](https://redis.io/docs/latest/commands/set) command page.\n\nThat said, `SETNX` can be used, and was historically used, as a locking primitive. For example, to acquire the lock of the key `foo`, the client could try the\nfollowing:\n\n[code example]\n\nIf `SETNX` returns `1` the client acquired the lock, setting the `lock.foo` key\nto the Unix time at which the lock should no longer be considered valid.\nThe client will later use `DEL lock.foo` in order to release the lock.\n\nIf `SETNX` returns `0` the key is already locked by some other client.\nWe can either return to the caller if it's a non blocking lock, or enter a loop\nretrying to hold the lock until we succeed or some kind of timeout expires.\n\n#### Handling deadlocks\n\nIn the above locking algorithm there is a problem: what happens if a client\nfails, crashes, or is otherwise not able to release the lock?\nIt's possible to detect this condition because the lock key contains a UNIX\ntimestamp.\nIf such a timestamp is equal to the current Unix time the lock is no longer\nvalid.\n\nWhen this happens we can't just call [`DEL`](https://redis.io/docs/latest/commands/del) against the key to remove the lock\nand then try to issue a `SETNX`, as there is a race condition here, when\nmultiple clients detected an expired lock and are trying to release it.\n\n* C1 and C2 read `lock.foo` to check the timestamp, because they both received\n  `0` after executing `SETNX`, as the lock is still held by C3 that crashed\n  after holding the lock.\n* C1 sends `DEL lock.foo`\n* C1 sends `SETNX lock.foo` and it succeeds\n* C2 sends `DEL lock.foo`\n* C2 sends `SETNX lock.foo` and it succeeds\n* **ERROR**: both C1 and C2 acquired the lock because of the race condition.\n\nFortunately, it's possible to avoid this issue using the following algorithm.\nLet's see how C4, our sane client, uses the good algorithm:\n\n*   C4 sends `SETNX lock.foo` in order to acquire the lock\n\n*   The crashed client C3 still holds it, so Redis will reply with `0` to C4.\n\n*   C4 sends `GET lock.foo` to check if the lock expired.\n    If it is not, it will sleep for some time and retry from the start.\n\n*   Instead, if the lock is expired because the Unix time at `lock.foo` is older\n    than the current Unix time, C4 tries to perform:\n\n    [code example]\n\n*   Because of the [`GETSET`](https://redis.io/docs/latest/commands/getset) semantic, C4 can check if the old value stored at\n    `key` is still an expired timestamp.\n    If it is, the lock was acquired.\n\n*   If another client, for instance C5, was faster than C4 and acquired the lock\n    with the [`GETSET`](https://redis.io/docs/latest/commands/getset) operation, the C4 [`GETSET`](https://redis.io/docs/latest/commands/getset) operation will return a non\n    expired timestamp.\n    C4 will simply restart from the first step.\n    Note that even if C4 set the key a bit a few seconds in the future this is\n    not a problem.\n\nIn order to make this locking algorithm more robust, a\nclient holding a lock should always check the timeout didn't expire before\nunlocking the key with [`DEL`](https://redis.io/docs/latest/commands/del) because client failures can be complex, not just\ncrashing but also blocking a lot of time against some operations and trying\nto issue [`DEL`](https://redis.io/docs/latest/commands/del) after a lot of time (when the LOCK is already held by another\nclient)."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0` if the key was not set.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `1` if the key was set.\n\n**RESP3:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0` if the key was not set.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `1` if the key was set."}],"examples":[{"id":"design-pattern-locking-with-setnx-ex0","language":"plaintext","code":"SETNX lock.foo <current Unix time + lock timeout + 1>","section_id":"design-pattern-locking-with-setnx"},{"id":"design-pattern-locking-with-setnx-ex1","language":"plaintext","code":"GETSET lock.foo <current Unix timestamp + lock timeout + 1>","section_id":"design-pattern-locking-with-setnx"}]}
{"schema_version":2,"id":"commands/setrange","title":"SETRANGE","url":"https://redis.io/docs/latest/commands/setrange/","summary":"Overwrites a part of a string value with another by an offset. Creates the key if it doesn't exist.","since":"2.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:12+01:00","page_type":"content","content_hash":"3407d9957e32c72856220eba65ca3ca9ce5013ceca29687f231edf64d01be064","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Overwrites part of the string stored at `key`, starting at the specified offset,\nfor the entire length of `value`.\nIf the offset is larger than the current length of the string at `key`, the\nstring is padded with zero-bytes to make `offset` fit.\nNon-existing keys are considered as empty strings, so this command will make\nsure it holds a string large enough to be able to set `value` at `offset`.\n\nNote that the maximum offset that you can set is 2^29 -1 (536870911), as Redis\nstrings are limited to 512 megabytes.\nIf you need to grow beyond this size, you can use multiple keys.\n\n**Warning**: When setting the last possible byte and the string value stored at\n`key ` does not yet hold a string value, or holds a small string value, Redis\nneeds to allocate all intermediate memory which can block the server for some\ntime.\n\nNote that once the first allocation is done, subsequent calls to `SETRANGE` for\nthe same `key` will not have the allocation overhead."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key.\n\n</details>\n\n<details open><summary><code>offset</code></summary>\n\nThe zero-based offset at which to start overwriting. The string is zero-padded if the offset is beyond the current length.\n\n</details>\n\n<details open><summary><code>value</code></summary>\n\nThe string to write at the offset.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"Basic usage:\n\n\nredis> SET key1 \"Hello World\"\nOK\nredis> SETRANGE key1 6 \"Redis\"\n(integer) 11\nredis> GET key1\n\"Hello Redis\"\n\n\nExample of zero padding:\n\n\nredis> SETRANGE key2 6 \"Redis\"\n(integer) 11\nredis> GET key2\n\"\\x00\\x00\\x00\\x00\\x00\\x00Redis\""},{"id":"details","title":"Details","role":"content","text":""},{"id":"patterns","title":"Patterns","role":"content","text":"Thanks to `SETRANGE` and the analogous [`GETRANGE`](https://redis.io/docs/latest/commands/getrange) commands, you can use Redis\nstrings as a linear array with O(1) random access.\nThis is a very fast and efficient storage in many real world use cases."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the length of the string after it was modified by the command.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the length of the string after it was modified by the command."}],"examples":[]}
{"schema_version":2,"id":"commands/shutdown","title":"SHUTDOWN","url":"https://redis.io/docs/latest/commands/shutdown/","summary":"Synchronously saves the database(s) to disk and shuts down the Redis server.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"a012600a8e142848dda8a35d1e711f84de7251f79420675652bf92d65fffda6d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The command behavior is the following:\n\n* If there are any replicas lagging behind in replication:\n  * Pause clients attempting to write by performing a [`CLIENT PAUSE`](https://redis.io/docs/latest/commands/client-pause) with the `WRITE` option.\n  * Wait up to the configured `shutdown-timeout` (default 10 seconds) for replicas to catch up the replication offset.\n* Stop all the clients.\n* Perform a blocking SAVE if at least one **save point** is configured.\n* Flush the Append Only File if AOF is enabled.\n* Quit the server.\n\nIf persistence is enabled this commands makes sure that Redis is switched off\nwithout any data loss.\n\nNote: A Redis instance that is configured for not persisting on disk (no AOF\nconfigured, nor \"save\" directive) will not dump the RDB file on `SHUTDOWN`, as\nusually you don't want Redis instances used only for caching to block on when\nshutting down.\n\nAlso note: If Redis receives one of the signals `SIGTERM` and `SIGINT`, the same shutdown sequence is performed.\nSee also [Signal Handling](https://redis.io/docs/latest/operate/oss_and_stack/reference/signals)."},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>NOSAVE | SAVE</code></summary>\n\n`SAVE` forces a DB save even if no save points are configured; `NOSAVE` prevents a DB save even if one or more save points are configured.\n\n</details>\n\n<details open><summary><code>NOW</code></summary>\n\nSkip waiting for lagging replicas, bypassing the first step in the shutdown sequence.\n\n</details>\n\n<details open><summary><code>FORCE</code></summary>\n\nIgnore any errors that would normally prevent the server from exiting.\n\n</details>\n\n<details open><summary><code>ABORT</code></summary>\n\nCancel an ongoing shutdown. Cannot be combined with the other flags.\n\n</details>"},{"id":"details","title":"Details","role":"content","text":""},{"id":"conditions-where-a-shutdown-fails","title":"Conditions where a SHUTDOWN fails","role":"content","text":"When a save point is configured or the **SAVE** modifier is specified, the shutdown may fail if the RDB file can't be saved.\nThen, the server continues to run in order to ensure no data loss.\nThis may be bypassed using the **FORCE** modifier, causing the server to exit anyway.\n\nWhen the Append Only File is enabled the shutdown may fail because the\nsystem is in a state that does not allow to safely immediately persist\non disk.\n\nNormally if there is an AOF child process performing an AOF rewrite, Redis\nwill simply kill it and exit.\nHowever, there are situations where it is unsafe to do so and, unless the **FORCE** modifier is specified, the **SHUTDOWN** command will be refused with an error instead.\nThis happens in the following situations:\n\n* The user just turned on AOF, and the server triggered the first AOF rewrite in order to create the initial AOF file. In this context, stopping will result in losing the dataset at all: once restarted, the server will potentially have AOF enabled without having any AOF file at all.\n* A replica with AOF enabled, reconnected with its master, performed a full resynchronization, and restarted the AOF file, triggering the initial AOF creation process. In this case not completing the AOF rewrite is dangerous because the latest dataset received from the master would be lost. The new master can actually be even a different instance (if the **REPLICAOF** or **SLAVEOF** command was used in order to reconfigure the replica), so it is important to finish the AOF rewrite and start with the correct data set representing the data set in memory when the server was terminated.\n\nThere are situations when we want just to terminate a Redis instance ASAP, regardless of what its content is.\nIn such a case, the command **SHUTDOWN NOW NOSAVE FORCE** can be used.\nIn versions before 7.0, where the **NOW** and **FORCE** flags are not available, the right combination of commands is to send a **CONFIG appendonly no** followed by a **SHUTDOWN NOSAVE**.\nThe first command will turn off the AOF if needed, and will terminate the AOF rewriting child if there is one active.\nThe second command will not have any problem to execute since the AOF is no longer enabled."},{"id":"minimize-the-risk-of-data-loss","title":"Minimize the risk of data loss","role":"content","text":"Since Redis 7.0, the server waits for lagging replicas up to a configurable `shutdown-timeout`, by default 10 seconds, before shutting down.\nThis provides a best effort minimizing the risk of data loss in a situation where no save points are configured and AOF is disabled.\nBefore version 7.0, shutting down a heavily loaded master node in a diskless setup was more likely to result in data loss.\nTo minimize the risk of data loss in such setups, it's advised to trigger a manual [`FAILOVER`](https://redis.io/docs/latest/commands/failover) (or [`CLUSTER FAILOVER`](https://redis.io/docs/latest/commands/cluster-failover)) to demote the master to a replica and promote one of the replicas to be the new master, before shutting down a master node."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if _ABORT_ was specified and shutdown was aborted. On successful shutdown, nothing is returned because the server quits and the connection is closed. On failure, an error is returned.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if _ABORT_ was specified and shutdown was aborted. On successful shutdown, nothing is returned because the server quits and the connection is closed. On failure, an error is returned."}],"examples":[]}
{"schema_version":2,"id":"commands/sinter","title":"SINTER","url":"https://redis.io/docs/latest/commands/sinter/","summary":"Returns the intersect of multiple sets.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"c32f03126ac2152932c07e52196ea6bcee4127afa349773c694fcb9aa08169c9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nReturns the members of the set resulting from the intersection of all the given\nsets.\n\nFor example:\n\n[code example]\n\nKeys that do not exist are considered to be empty sets.\nWith one of the keys being an empty set, the resulting set is also empty (since\nset intersection with an empty set always results in an empty set)."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key [key ...]</code></summary>\n\nOne or more set keys to intersect.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SADD key1 \"a\"\n(integer) 1\nredis> SADD key1 \"b\"\n(integer) 1\nredis> SADD key1 \"c\"\n(integer) 1\nredis> SADD key2 \"c\"\n(integer) 1\nredis> SADD key2 \"d\"\n(integer) 1\nredis> SADD key2 \"e\"\n(integer) 1\nredis> SINTER key1 key2\n1) \"c\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): an array with the members of the resulting set.\n\n**RESP3:**\n\n[Set reply](../../develop/reference/protocol-spec#sets): a set with the members of the resulting set."},{"id":"see-also","title":"See also","role":"related","text":"[`SINTERCARD`](https://redis.io/docs/latest/commands/sintercard) | [`SINTERSTORE`](https://redis.io/docs/latest/commands/sinterstore)"}],"examples":[{"id":"overview-ex0","language":"plaintext","code":"key1 = {a,b,c,d}\nkey2 = {c}\nkey3 = {a,c,e}\nSINTER key1 key2 key3 = {c}","section_id":"overview"}]}
{"schema_version":2,"id":"commands/sintercard","title":"SINTERCARD","url":"https://redis.io/docs/latest/commands/sintercard/","summary":"Returns the number of members of the intersect of multiple sets.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"9c8cb08123b88622cdf16a2f64e0b2979500257e1393a603b648a6b43db9f7c1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nThis command is similar to [`SINTER`](https://redis.io/docs/latest/commands/sinter), but instead of returning the result set, it returns just the cardinality of the result.\nReturns the cardinality of the set which would result from the intersection of all the given sets.\n\nKeys that do not exist are considered to be empty sets.\nWith one of the keys being an empty set, the resulting set is also empty (since set intersection with an empty set always results in an empty set).\n\nBy default, the command calculates the cardinality of the intersection of all given sets.\nWhen provided with the optional `LIMIT` argument (which defaults to 0 and means unlimited), if the intersection cardinality reaches limit partway through the computation, the algorithm will exit and yield limit as the cardinality.\nSuch implementation ensures a significant speedup for queries where the limit is lower than the actual intersection cardinality."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>numkeys</code></summary>\n\nThe number of keys that follow.\n\n</details>\n\n<details open><summary><code>key [key ...]</code></summary>\n\nOne or more set keys to intersect.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>LIMIT limit</code></summary>\n\nStop counting once the cardinality reaches `limit`. `0` (the default) means no limit.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SADD key1 \"a\"\n(integer) 1\nredis> SADD key1 \"b\"\n(integer) 1\nredis> SADD key1 \"c\"\n(integer) 1\nredis> SADD key1 \"d\"\n(integer) 1\nredis> SADD key2 \"c\"\n(integer) 1\nredis> SADD key2 \"d\"\n(integer) 1\nredis> SADD key2 \"e\"\n(integer) 1\nredis> SINTER key1 key2\n1) \"c\"\n2) \"d\"\nredis> SINTERCARD 2 key1 key2\n(integer) 2\nredis> SINTERCARD 2 key1 key2 LIMIT 1\n(integer) 1"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of elements in the resulting intersection.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of elements in the resulting intersection."},{"id":"see-also","title":"See also","role":"related","text":"[`SINTER`](https://redis.io/docs/latest/commands/sinter) | [`SINTERSTORE`](https://redis.io/docs/latest/commands/sinterstore)"}],"examples":[]}
{"schema_version":2,"id":"commands/sinterstore","title":"SINTERSTORE","url":"https://redis.io/docs/latest/commands/sinterstore/","summary":"Stores the intersect of multiple sets in a key.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"b01048f0644dcdeac34dc5c23d3c5b54d013ac1c2b9665858bafd1ccdc047ef7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nThis command is equal to [`SINTER`](https://redis.io/docs/latest/commands/sinter), but instead of returning the resulting set,\nit is stored in `destination`.\n\nIf `destination` already exists, it is overwritten."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>destination</code></summary>\n\nThe key to store the resulting set in.\n\n</details>\n\n<details open><summary><code>key [key ...]</code></summary>\n\nOne or more set keys to intersect.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SADD key1 \"a\"\n(integer) 1\nredis> SADD key1 \"b\"\n(integer) 1\nredis> SADD key1 \"c\"\n(integer) 1\nredis> SADD key2 \"c\"\n(integer) 1\nredis> SADD key2 \"d\"\n(integer) 1\nredis> SADD key2 \"e\"\n(integer) 1\nredis> SINTERSTORE key key1 key2\n(integer) 1\nredis> SMEMBERS key\n1) \"c\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of elements in the resulting set.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of elements in the result set."},{"id":"see-also","title":"See also","role":"related","text":"[`SINTER`](https://redis.io/docs/latest/commands/sinter) | [`SINTERCARD`](https://redis.io/docs/latest/commands/sintercard)"}],"examples":[]}
{"schema_version":2,"id":"commands/sismember","title":"SISMEMBER","url":"https://redis.io/docs/latest/commands/sismember/","summary":"Determines whether a member belongs to a set.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"1ae44a94e26d9eb763c2784b0c26d70b24857135586e0218bf525777bacc1400","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns if `member` is a member of the set stored at `key`."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the set.\n\n</details>\n\n<details open><summary><code>member</code></summary>\n\nThe member to check for.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SADD myset \"one\"\n(integer) 1\nredis> SISMEMBER myset \"one\"\n(integer) 1\nredis> SISMEMBER myset \"two\"\n(integer) 0"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0` if the element is not a member of the set, or when the key does not exist.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `1` if the element is a member of the set.\n\n**RESP3:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0` if the element is not a member of the set, or when the key does not exist.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `1` if the element is a member of the set."}],"examples":[]}
{"schema_version":2,"id":"commands/slaveof","title":"SLAVEOF","url":"https://redis.io/docs/latest/commands/slaveof/","summary":"Sets a Redis server as a replica of another, or promotes it to being a master.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"c9a7e25067de20f2ef58042d5ac4e0a7c4b6cafb3ccb044f19258b79a92f40b3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"**A note about the word slave used in this man page and command name**: starting with Redis version 5, if not for backward compatibility, the Redis project no longer uses the word slave. Please use the new command [`REPLICAOF`](https://redis.io/docs/latest/commands/replicaof). The command `SLAVEOF` will continue to work for backward compatibility.\n\nThe `SLAVEOF` command can change the replication settings of a replica on the fly.\nIf a Redis server is already acting as replica, the command `SLAVEOF` NO ONE will\nturn off the replication, turning the Redis server into a MASTER.\nIn the proper form `SLAVEOF` hostname port will make the server a replica of\nanother server listening at the specified hostname and port.\n\nIf a server is already a replica of some master, `SLAVEOF` hostname port will stop\nthe replication against the old server and start the synchronization against the\nnew one, discarding the old dataset.\n\nThe form `SLAVEOF` NO ONE will stop replication, turning the server into a\nMASTER, but will not discard the replication.\nSo, if the old master stops working, it is possible to turn the replica into a\nmaster and set the application to use this new master in read/write.\nLater when the other Redis server is fixed, it can be reconfigured to work as a\nreplica."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>host port | NO ONE</code></summary>\n\nThe host and port of the master to replicate from, or `NO ONE` to stop replicating. `SLAVEOF` is deprecated; use `REPLICAOF` instead.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | Deprecated as of Redis v5.0.0. |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[]}
{"schema_version":2,"id":"commands/slowlog","title":"SLOWLOG","url":"https://redis.io/docs/latest/commands/slowlog/","summary":"A container for slow log commands.","since":"2.2.12","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"8d9a717cddbb3600f956ad33a277a5175c08bd2796c81100be7d21566e4d5ec5","sections":[{"id":"content","title":"Content","role":"content","text":"The Redis Slow Log is a system to log queries that exceeded a specified execution time.\nThe execution time does not include I/O operations like talking with the client, just the time needed to execute the command (this is the only stage of command execution where the thread is blocked and cannot serve other requests).\n\nA new entry is added to the slow log whenever a command exceeds the execution time threshold defined by the `slowlog-log-slower-than` configuration directive.\n\nThe maximum number of entries in the slow log is governed by the `slowlog-max-len` configuration directive.\nThis is a container command for slow log management commands.\n\nSee [`SLOWLOG GET`](https://redis.io/docs/latest/commands/slowlog-get) for a description of what's stored in the Redis slow log. To see the list of available commands use the [`SLOWLOG HELP`](https://redis.io/docs/latest/commands/slowlog-help) command."}],"examples":[]}
{"schema_version":2,"id":"commands/slowlog-get","title":"SLOWLOG GET","url":"https://redis.io/docs/latest/commands/slowlog-get/","summary":"Returns the slow log's entries.","since":"2.2.12","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"5db247b0d92bd7f7cea54052d2bd521f6104c914913fc805d56c211877628ea1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `SLOWLOG GET` command returns entries from the slow log in chronological order.\n\nThe Redis Slow Log is a system to log queries that exceeded a specified execution time.\nThe execution time does not include I/O operations like talking with the client, sending the reply and so forth, but just the time needed to actually execute the command (this is the only stage of command execution where the thread is blocked and can not serve other requests in the meantime).\n\nA new entry is added to the slow log whenever a command exceeds the execution time threshold defined by the `slowlog-log-slower-than` configuration directive.\nThe maximum number of entries in the slow log is governed by the `slowlog-max-len` configuration directive.\n\nBy default the command returns latest ten entries in the log. The optional `count` argument limits the number of returned entries, so the command returns at most up to `count` entries, the special number -1 means return all entries.\n\nEach entry from the slow log is comprised of the following seven values:\n\n1. A unique progressive identifier for every slow log entry.\n2. The unix timestamp at which the logged command was processed.\n3. The amount of time needed for its execution, in microseconds.\n4. The array composing the arguments of the command.\n5. Client IP address and port.\n6. Client name if set via the [`CLIENT SETNAME`](https://redis.io/docs/latest/commands/client-setname) command.\n7. The total number of arguments in the command, including the command name. Added in Redis 8.10.\n\nThe logged argument array (value 4) is truncated when the command has more arguments than the `slowlog-max-argc` configuration directive allows (32 by default). In that case, the last logged element is replaced with a string of the form `... (N more arguments)`. The total argument count (value 7) always reflects the command's original argument count, captured before truncation, so it can be read directly without parsing that string.\n\nThe entry's unique ID can be used in order to avoid processing slow log entries multiple times (for instance you may have a script sending you an email alert for every new slow log entry).\nThe ID is never reset in the course of the Redis server execution, only a server\nrestart will reset it."},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>count</code></summary>\n\nThe number of recent slow-log entries to return. `-1` returns all entries; the default is 10.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"The seventh element of each entry reports the command's total argument count. This is most useful for variadic commands whose logged argument list has been truncated. In the following example, the `slowlog-max-argc` limit is left at its default of 32 and a `SADD` command is issued with 33 arguments (the command name, the key, and 31 members):\n\n[code example]\n\nThe logged argument array (element 4) is truncated to 32 items, with the final item indicating how many more arguments were omitted. The argument count (element 7) still reports the original total of `33`."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Not supported for [scripts](https://redis.io/docs/latest/develop/programmability). Also, the client IP address, port, and name are not returned by Redis Software. |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of slow log entries per the above format.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of slow log entries per the above format."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> CONFIG SET slowlog-log-slower-than 0\nOK\nredis> SLOWLOG RESET\nOK\nredis> SADD myset 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33\n(integer) 31\nredis> SLOWLOG GET 1\n1) 1) (integer) 12\n   2) (integer) 1752422400\n   3) (integer) 21\n   4)  1) \"SADD\"\n       2) \"myset\"\n       3) \"3\"\n       4) \"4\"\n          ...\n      32) \"... (2 more arguments)\"\n   5) \"127.0.0.1:52276\"\n   6) \"\"\n   7) (integer) 33","section_id":"examples"}]}
{"schema_version":2,"id":"commands/slowlog-help","title":"SLOWLOG HELP","url":"https://redis.io/docs/latest/commands/slowlog-help/","summary":"Show helpful text about the different subcommands","since":"6.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"34e3a4c3b27a37fabdf8ce6deb4d9040670ee78035210e8e887f714615bb078a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `SLOWLOG HELP` command returns a helpful text describing the different subcommands."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of sub-commands and their descriptions.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of sub-commands and their descriptions."}],"examples":[]}
{"schema_version":2,"id":"commands/slowlog-len","title":"SLOWLOG LEN","url":"https://redis.io/docs/latest/commands/slowlog-len/","summary":"Returns the number of entries in the slow log.","since":"2.2.12","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"5c05485eab886115f7b2314f89ba7892654682377f4a758e026a7b62274ac561","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This command returns the current number of entries in the slow log.\n\nA new entry is added to the slow log whenever a command exceeds the execution time threshold defined by the `slowlog-log-slower-than` configuration directive.\nThe maximum number of entries in the slow log is governed by the `slowlog-max-len` configuration directive.\nOnce the slog log reaches its maximal size, the oldest entry is removed whenever a new entry is created.\nThe slow log can be cleared with the [`SLOWLOG RESET`](https://redis.io/docs/latest/commands/slowlog-reset) command."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Not supported for [scripts](https://redis.io/docs/latest/develop/programmability). |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of entries in the slow log.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of entries in the slow log."}],"examples":[]}
{"schema_version":2,"id":"commands/slowlog-reset","title":"SLOWLOG RESET","url":"https://redis.io/docs/latest/commands/slowlog-reset/","summary":"Clears all entries from the slow log.","since":"2.2.12","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"a19f3ecefcd1e840de949698a20f9c5147fa39c85a4008219260f3b5bca58ca7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This command resets the slow log, clearing all entries in it.\n\nOnce deleted the information is lost forever."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Not supported for [scripts](https://redis.io/docs/latest/develop/programmability). |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[]}
{"schema_version":2,"id":"commands/smembers","title":"SMEMBERS","url":"https://redis.io/docs/latest/commands/smembers/","summary":"Returns all members of a set.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"67bf679226d33204bc9a5b00fcdf8a5d43143e1f0371213655d506e6be6c25e8","sections":[{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the set.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"Foundational: Retrieve all members of a set using SMEMBERS (returns unordered collection, useful for iterating all set members)\n\n**Difficulty:** Beginner\n\n**Commands:** SADD, SMEMBERS\n\n**Complexity:**\n- SADD: O(1)\n- SMEMBERS: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): an array with all the members of the set.\n\n**RESP3:**\n\n[Set reply](../../develop/reference/protocol-spec#sets): a set with all the members of the set."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> SADD myset \"Hello\"\n(integer) 1\nredis> SADD myset \"World\"\n(integer) 1\nredis> SMEMBERS myset\n1) \"Hello\"\n2) \"World\"","section_id":"examples"},{"id":"examples-ex1","language":"csharp","code":"long sMembersResult1 = db.SetAdd(\n            \"myset\", [\"Hello\", \"World\"]\n        );\n        Console.WriteLine(sMembersResult1); // >>> 2\n\n        RedisValue[] sMembersResult2 = db.SetMembers(\"myset\");\n        Array.Sort(sMembersResult2);\n        Console.WriteLine(string.Join(\", \", sMembersResult2));\n        // >>> Hello, World","section_id":"examples"},{"id":"examples-ex2","language":"go","code":"sAddResult, err := rdb.SAdd(ctx, \"myset\", \"Hello\", \"World\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(sAddResult) // >>> 2\n\n\tsMembersResult, err := rdb.SMembers(ctx, \"myset\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(sMembersResult) // >>> [Hello World]","section_id":"examples"},{"id":"examples-ex3","language":"java","code":"CompletableFuture<Void> smembers = asyncCommands.sadd(\"myset\", \"Hello\", \"World\").thenCompose(r -> {\n                return asyncCommands.smembers(\"myset\");\n            })\n                    .thenAccept(System.out::println)\n                    // >>> [Hello, World]\n                    .toCompletableFuture();","section_id":"examples"},{"id":"examples-ex4","language":"java","code":"Mono<Void> smembers = reactiveCommands.sadd(\"myset\", \"Hello\", \"World\").doOnNext(r -> {\n                System.out.println(r); // >>> 2\n            }).flatMap(r -> reactiveCommands.smembers(\"myset\").collectList()).doOnNext(r -> {\n                System.out.println(r); // >>> [Hello, World]\n            }).then();","section_id":"examples"},{"id":"examples-ex5","language":"java","code":"long sMembersResult1 = jedis.sadd(\"myset\", \"Hello\", \"World\");\n        System.out.println(sMembersResult1); // >>> 2\n\n        Set<String> sMembersResult2 = jedis.smembers(\"myset\");\n        System.out.println(sMembersResult2.stream().sorted().collect(toList()));\n        // >>> [Hello, World]","section_id":"examples"},{"id":"examples-ex6","language":"javascript","code":"const res4 = await client.sAdd('myset', ['Hello', 'World']);\nconsole.log(res4);  // 2\n\nconst res5 = await client.sMembers('myset')\nconsole.log(res5);  // ['Hello', 'World']","section_id":"examples"},{"id":"examples-ex7","language":"javascript","code":"const res4 = await redis.sadd('myset', 'Hello', 'World');\nconsole.log(res4); // >>> 2\n\nconst res5 = await redis.smembers('myset');\nconsole.log(res5); // >>> ['Hello', 'World']","section_id":"examples"},{"id":"examples-ex8","language":"javascript","code":"const res4 = await client.sAdd('myset', ['Hello', 'World']);\nconsole.log(res4);  // 2\n\nconst res5 = await client.sMembers('myset')\nconsole.log(res5);  // ['Hello', 'World']","section_id":"examples"},{"id":"examples-ex9","language":"javascript","code":"const res4 = await redis.sadd('myset', 'Hello', 'World');\nconsole.log(res4); // >>> 2\n\nconst res5 = await redis.smembers('myset');\nconsole.log(res5); // >>> ['Hello', 'World']","section_id":"examples"},{"id":"examples-ex10","language":"php","code":"$res4 = $r->sadd('myset', ['Hello', 'World']);\n        echo $res4 . PHP_EOL;\n        // >>> 2\n\n        $res5 = $r->smembers('myset');\n        sort($res5);\n        echo json_encode($res5) . PHP_EOL;\n        // >>> [\"Hello\",\"World\"]","section_id":"examples"},{"id":"examples-ex11","language":"python","code":"res4 = r.sadd(\"myset\", \"Hello\", \"World\")\nprint(res4)  # >>> 2\n\nres5 = r.smembers(\"myset\")\nprint(res5)  # >>> {'Hello', 'World'}","section_id":"examples"},{"id":"examples-ex12","language":"ruby","code":"res4 = r.sadd('myset', ['Hello', 'World'])\nputs res4 # >>> 2\n\nres5 = r.smembers('myset')\nputs res5.inspect # >>> [\"Hello\", \"World\"]","section_id":"examples"},{"id":"examples-ex13","language":"rust","code":"if let Ok(res4) = r.sadd(\"myset\", &[\"Hello\", \"World\"]).await {\n            let res4: i32 = res4;\n            println!(\"{res4}\"); // >>> 2\n        }\n\n        if let Ok(res5) = r.smembers(\"myset\").await {\n            let res5: HashSet<String> = res5;\n            println!(\"{res5:?}\"); // >>> {\"Hello\", \"World\"}\n        }","section_id":"examples"},{"id":"examples-ex14","language":"rust","code":"if let Ok(res4) = r.sadd(\"myset\", &[\"Hello\", \"World\"]) {\n            let res4: i32 = res4;\n            println!(\"{res4}\"); // >>> 2\n        }\n\n        if let Ok(res5) = r.smembers(\"myset\") {\n            let res5: HashSet<String> = res5;\n            println!(\"{res5:?}\"); // >>> {\"Hello\", \"World\"}\n        }","section_id":"examples"}]}
{"schema_version":2,"id":"commands/smismember","title":"SMISMEMBER","url":"https://redis.io/docs/latest/commands/smismember/","summary":"Determines whether multiple members belong to a set.","since":"6.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"33bfea9fc3329812222b3e5f25dfa2544c9216a1eb3cb8d124f7104f83ac26b2","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns whether each `member` is a member of the set stored at `key`.\n\nFor every `member`, `1` is returned if the value is a member of the set, or `0` if the element is not a member of the set or if `key` does not exist."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the set.\n\n</details>\n\n<details open><summary><code>member [member ...]</code></summary>\n\nOne or more members to check for.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SADD myset \"one\"\n(integer) 1\nredis> SADD myset \"one\"\n(integer) 0\nredis> SMISMEMBER myset \"one\" \"notamember\"\n1) (integer) 1\n2) (integer) 0"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list representing the membership of the given elements, in the same order as they are requested.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list representing the membership of the given elements, in the same order as they are requested."}],"examples":[]}
{"schema_version":2,"id":"commands/smove","title":"SMOVE","url":"https://redis.io/docs/latest/commands/smove/","summary":"Moves a member from one set to another.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"0ec860392d2993b13c1783fab5855b99e0f12c86bddd3b23754528eed3835aa6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nMove `member` from the set at `source` to the set at `destination`.\nThis operation is atomic.\nIn every given moment the element will appear to be a member of `source` **or**\n`destination` for other clients.\n\nIf the source set does not exist or does not contain the specified element, no\noperation is performed and `0` is returned.\nOtherwise, the element is removed from the source set and added to the\ndestination set.\nWhen the specified element already exists in the destination set, it is only\nremoved from the source set."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>source</code></summary>\n\nThe key of the source set.\n\n</details>\n\n<details open><summary><code>destination</code></summary>\n\nThe key of the destination set.\n\n</details>\n\n<details open><summary><code>member</code></summary>\n\nThe member to move from the source set to the destination set.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SADD myset \"one\"\n(integer) 1\nredis> SADD myset \"two\"\n(integer) 1\nredis> SADD myotherset \"three\"\n(integer) 1\nredis> SMOVE myset myotherset \"two\"\n(integer) 1\nredis> SMEMBERS myset\n1) \"one\"\nredis> SMEMBERS myotherset\n1) \"three\"\n2) \"two\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): `1` if the element is moved.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0` if the element is not a member of _source_ and no operation was performed.\n\n**RESP3:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): `1` if the element is moved.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `0` if the element is not a member of _source_ and no operation was performed."}],"examples":[]}
{"schema_version":2,"id":"commands/sort","title":"SORT","url":"https://redis.io/docs/latest/commands/sort/","summary":"Sorts the elements in a list, a set, or a sorted set, optionally storing the result.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"d7b3d9b78fc0666875bebcc3680d25497a2c532a4514cb436fcd5fb2720ae9fd","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns or stores the elements contained in the [list](https://redis.io/docs/latest/develop/data-types#lists), [set](https://redis.io/docs/latest/develop/data-types#set) or\n[sorted set](https://redis.io/docs/latest/develop/data-types#sorted-sets) at `key`.\n\nThere is also the [`SORT_RO`](https://redis.io/docs/latest/commands/sort_ro) read-only variant of this command.\n\nBy default, sorting is numeric and elements are compared by their value\ninterpreted as double precision floating point number.\nThis is `SORT` in its simplest form:\n\n[code example]\n\nAssuming `mylist` is a list of numbers, this command will return the same list\nwith the elements sorted from small to large.\nIn order to sort the numbers from large to small, use the `DESC` modifier:\n\n[code example]\n\nWhen `mylist` contains string values and you want to sort them\nlexicographically, use the `ALPHA` modifier:\n\n[code example]\n\nRedis is UTF-8 aware, assuming you correctly set the `LC_COLLATE` environment\nvariable.\n\nThe number of returned elements can be limited using the `LIMIT` modifier.\nThis modifier takes the `offset` argument, specifying the number of elements to\nskip and the `count` argument, specifying the number of elements to return from\nstarting at `offset`.\nThe following example will return 10 elements of the sorted version of `mylist`,\nstarting at element 0 (`offset` is zero-based):\n\n[code example]\n\nAlmost all modifiers can be used together.\nThe following example will return the first 5 elements, lexicographically sorted\nin descending order:\n\n[code example]"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key to sort. It may hold a list, set, or sorted set.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>BY pattern</code></summary>\n\nSort by the values of the external keys matched by `pattern` (each `*` is replaced by an element) instead of by the elements themselves. A pattern with no `*`, or a hash-field pattern that resolves the same for all elements, skips sorting.\n\n</details>\n\n<details open><summary><code>LIMIT offset count</code></summary>\n\nReturn a slice of the sorted result: skip `offset` elements and return up to `count`.\n\n</details>\n\n<details open><summary><code>GET pattern [GET pattern ...]</code></summary>\n\nReturn the values of the external keys matched by `pattern` for each sorted element. Use `GET #` to also return the element itself. May be given multiple times.\n\n</details>\n\n<details open><summary><code>ASC | DESC</code></summary>\n\nSort in ascending (`ASC`, the default) or descending (`DESC`) order.\n\n</details>\n\n<details open><summary><code>ALPHA</code></summary>\n\nSort the elements lexicographically instead of numerically.\n\n</details>\n\n<details open><summary><code>STORE destination</code></summary>\n\nStore the resulting list in `destination` instead of returning it.\n\n</details>"},{"id":"details","title":"Details","role":"content","text":""},{"id":"sorting-by-external-keys","title":"Sorting by external keys","role":"content","text":"Sometimes you want to sort elements using external keys as weights to compare\ninstead of comparing the actual elements in the list, set or sorted set.\nLet's say the list `mylist` contains the elements `1`, `2` and `3` representing\nunique IDs of objects stored in `object_1`, `object_2` and `object_3`.\nWhen these objects have associated weights stored in `weight_1`, `weight_2` and\n`weight_3`, `SORT` can be instructed to use these weights to sort `mylist` with\nthe following statement:\n\n[code example]\n\nThe `BY` option takes a pattern (equal to `weight_*` in this example) that is\nused to generate the keys that are used for sorting.\nThese key names are obtained substituting the first occurrence of `*` with the\nactual value of the element in the list (`1`, `2` and `3` in this example)."},{"id":"skip-sorting-the-elements","title":"Skip sorting the elements","role":"content","text":"The `BY` option can also take a non-existent key, which causes `SORT` to skip\nthe sorting operation.\nThis is useful if you want to retrieve external keys (see the `GET` option\nbelow) without the overhead of sorting.\n\n[code example]"},{"id":"retrieving-external-keys","title":"Retrieving external keys","role":"content","text":"Our previous example returns just the sorted IDs.\nIn some cases, it is more useful to get the actual objects instead of their IDs\n(`object_1`, `object_2` and `object_3`).\nRetrieving external keys based on the elements in a list, set or sorted set can\nbe done with the following command:\n\n[code example]\n\nThe `GET` option can be used multiple times in order to get more keys for every\nelement of the original list, set or sorted set.\n\nIt is also possible to `GET` the element itself using the special pattern `#`:\n\n[code example]"},{"id":"restrictions-for-using-external-keys","title":"Restrictions for using external keys","role":"limits","text":"Before 7.4, when enabling `Redis cluster-mode` there is no way to guarantee the existence of the external keys on the node which the command is processed on. In this case, any use of [`GET`](https://redis.io/docs/latest/commands/get) or `BY` which reference external key pattern will cause the command to fail with an error.\n\nStarting from 7.4, pattern with hash tag can be mapped to a slot, and so in `Redis cluster-mode`, the use of `BY` or [`GET`](https://redis.io/docs/latest/commands/get) is allowed when pattern contains hash tag and implies a specific slot which the key is also in, which means any key matching this pattern must be in the same slot as the key, and therefore in the same node. For example, in cluster mode, `{mylist}weight_*` is acceptable as a pattern when sorting `mylist`, while pattern `{abc}weight_*` will be denied, causing the command to fail with an error.\n\nTo use pattern with hash tag, see [Hash tags](https://redis.io/docs/latest/operate/oss_and_stack/reference/cluster-spec#hash-tags) for more information.\n\nStarting from Redis 7.0, any use of [`GET`](https://redis.io/docs/latest/commands/get) or `BY` which reference external key pattern will only be allowed in case the current user running the command has full key read permissions.\nFull key read permissions can be set for the user by, for example, specifying `'%R~*'` or `'~*` with the relevant command access rules.\nYou can check the [`ACL SETUSER`](https://redis.io/docs/latest/commands/acl-setuser) command manual for more information on setting ACL access rules.\nIf full key read permissions aren't set, the command will fail with an error."},{"id":"storing-the-result-of-a-sort-operation","title":"Storing the result of a SORT operation","role":"content","text":"By default, `SORT` returns the sorted elements to the client.\nWith the `STORE` option, the result will be stored as a list at the specified\nkey instead of being returned to the client.\n\n[code example]\n\nAn interesting pattern using `SORT ... STORE` consists in associating an\n[`EXPIRE`](https://redis.io/docs/latest/commands/expire) timeout to the resulting key so that in applications where the result\nof a `SORT` operation can be cached for some time.\nOther clients will use the cached list instead of calling `SORT` for every\nrequest.\nWhen the key will timeout, an updated version of the cache can be created by\ncalling `SORT ... STORE` again.\n\nNote that for correctly implementing this pattern it is important to avoid\nmultiple clients rebuilding the cache at the same time.\nSome kind of locking is needed here (for instance using [`SETNX`](https://redis.io/docs/latest/commands/setnx))."},{"id":"using-hashes-in-by-and-get","title":"Using hashes in `BY` and `GET`","role":"content","text":"It is possible to use `BY` and `GET` options against hash fields with the\nfollowing syntax:\n\n[code example]\n\nThe string `->` is used to separate the key name from the hash field name.\nThe key is substituted as documented above, and the hash stored at the resulting\nkey is accessed to retrieve the specified hash field."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): without passing the _STORE_ option, the command returns a list of sorted elements.\n[Integer reply](../../develop/reference/protocol-spec#integers): when the _STORE_ option is specified, the command returns the number of sorted elements in the destination list.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): without passing the _STORE_ option, the command returns a list of sorted elements.\n[Integer reply](../../develop/reference/protocol-spec#integers): when the _STORE_ option is specified, the command returns the number of sorted elements in the destination list."}],"examples":[{"id":"overview-ex0","language":"plaintext","code":"SORT mylist","section_id":"overview"},{"id":"overview-ex1","language":"plaintext","code":"SORT mylist DESC","section_id":"overview"},{"id":"overview-ex2","language":"plaintext","code":"SORT mylist ALPHA","section_id":"overview"},{"id":"overview-ex3","language":"plaintext","code":"SORT mylist LIMIT 0 10","section_id":"overview"},{"id":"overview-ex4","language":"plaintext","code":"SORT mylist LIMIT 0 5 ALPHA DESC","section_id":"overview"},{"id":"sorting-by-external-keys-ex0","language":"plaintext","code":"SORT mylist BY weight_*","section_id":"sorting-by-external-keys"},{"id":"skip-sorting-the-elements-ex0","language":"plaintext","code":"SORT mylist BY nosort","section_id":"skip-sorting-the-elements"},{"id":"retrieving-external-keys-ex0","language":"plaintext","code":"SORT mylist BY weight_* GET object_*","section_id":"retrieving-external-keys"},{"id":"retrieving-external-keys-ex1","language":"plaintext","code":"SORT mylist BY weight_* GET object_* GET #","section_id":"retrieving-external-keys"},{"id":"storing-the-result-of-a-sort-operation-ex0","language":"plaintext","code":"SORT mylist BY weight_* STORE resultkey","section_id":"storing-the-result-of-a-sort-operation"},{"id":"using-hashes-in-by-and-get-ex0","language":"plaintext","code":"SORT mylist BY weight_*->fieldname GET object_*->fieldname","section_id":"using-hashes-in-by-and-get"}]}
{"schema_version":2,"id":"commands/sort_ro","title":"SORT_RO","url":"https://redis.io/docs/latest/commands/sort_ro/","summary":"Returns the sorted elements of a list, a set, or a sorted set.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"e3088294fb90c18c4cb12939f55251d86e1cce3a8b6bc4e7c58d57083fc57a50","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Read-only variant of the [`SORT`](https://redis.io/docs/latest/commands/sort) command. It is exactly like the original [`SORT`](https://redis.io/docs/latest/commands/sort) but refuses the `STORE` option and can safely be used in read-only replicas.\n\nSince the original [`SORT`](https://redis.io/docs/latest/commands/sort) has a `STORE` option it is technically flagged as a writing command in the Redis command table. For this reason read-only replicas in a Redis Cluster will redirect it to the master instance even if the connection is in read-only mode (see the [`READONLY`](https://redis.io/docs/latest/commands/readonly) command of Redis Cluster).\n\nThe `SORT_RO` variant was introduced in order to allow [`SORT`](https://redis.io/docs/latest/commands/sort) behavior in read-only replicas without breaking compatibility on command flags.\n\nSee original [`SORT`](https://redis.io/docs/latest/commands/sort) for more details."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key to sort. It may hold a list, set, or sorted set.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>BY pattern</code></summary>\n\nSort by the values of the external keys matched by `pattern` (each `*` is replaced by an element) instead of by the elements themselves. A pattern with no `*`, or a hash-field pattern that resolves the same for all elements, skips sorting.\n\n</details>\n\n<details open><summary><code>LIMIT offset count</code></summary>\n\nReturn a slice of the sorted result: skip `offset` elements and return up to `count`.\n\n</details>\n\n<details open><summary><code>GET pattern [GET pattern ...]</code></summary>\n\nReturn the values of the external keys matched by `pattern` for each sorted element. Use `GET #` to also return the element itself. May be given multiple times.\n\n</details>\n\n<details open><summary><code>ASC | DESC</code></summary>\n\nSort in ascending (`ASC`, the default) or descending (`DESC`) order.\n\n</details>\n\n<details open><summary><code>ALPHA</code></summary>\n\nSort the elements lexicographically instead of numerically.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of sorted elements.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of sorted elements."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"SORT_RO mylist BY weight_*->fieldname GET object_*->fieldname","section_id":"examples"}]}
{"schema_version":2,"id":"commands/spop","title":"SPOP","url":"https://redis.io/docs/latest/commands/spop/","summary":"Returns one or more random members from a set after removing them. Deletes the set if the last member was popped.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"da0ba1042a150db4242e33f9e61f6fc6cebde08ade31ffccb7028b9481cd9579","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Removes and returns one or more random members from the set value store at `key`.\n\nThis operation is similar to [`SRANDMEMBER`](https://redis.io/docs/latest/commands/srandmember), that returns one or more random elements from a set but does not remove it.\n\nBy default, the command pops a single member from the set. When provided with\nthe optional `count` argument, the reply will consist of up to `count` members,\ndepending on the set's cardinality."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the set.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>count</code></summary>\n\nThe number of members to pop. Without it, a single member is popped.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SADD myset \"one\"\n(integer) 1\nredis> SADD myset \"two\"\n(integer) 1\nredis> SADD myset \"three\"\n(integer) 1\nredis> SPOP myset\nredis> SMEMBERS myset\n1) \"one\"\n2) \"three\"\nredis> SADD myset \"four\"\n(integer) 1\nredis> SADD myset \"five\"\n(integer) 1\nredis> SPOP myset 3\nredis> SMEMBERS myset\n1) \"three\""},{"id":"details","title":"Details","role":"content","text":""},{"id":"distribution-of-returned-elements","title":"Distribution of returned elements","role":"content","text":"Note that this command is not suitable when you need a guaranteed uniform distribution of the returned elements. For more information about the algorithms used for `SPOP`, look up both the Knuth sampling and Floyd sampling algorithms."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): if the key does not exist.\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): when called without the _count_ argument, the removed member.\n* [Array reply](../../develop/reference/protocol-spec#arrays): when called with the _count_ argument, a list of the removed members.\n\n**RESP3:**\n\nOne of the following:\n* [Null reply](../../develop/reference/protocol-spec#nulls): if the key does not exist.\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): when called without the _count_ argument, the removed member.\n* [Array reply](../../develop/reference/protocol-spec#arrays): when called with the _count_ argument, a list of the removed members."}],"examples":[]}
{"schema_version":2,"id":"commands/spublish","title":"SPUBLISH","url":"https://redis.io/docs/latest/commands/spublish/","summary":"Post a message to a shard channel","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:25+01:00","page_type":"content","content_hash":"ce188dfaedf31e5d2a89992d0ac37836ebb16fd266cb0eb23fc5ca2cc3fe5771","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Posts a message to the given shard channel.\n\nIn Redis Cluster, shard channels are assigned to slots by the same algorithm used to assign keys to slots.\nA shard message must be sent to a node that owns the slot the shard channel is hashed to. \nThe cluster makes sure that published shard messages are forwarded to all the nodes in the shard, so clients can subscribe to a shard channel by connecting to any one of the nodes in the shard.\n\nFor more information about sharded pubsub, see [Sharded Pubsub](https://redis.io/docs/latest/develop/pubsub#sharded-pubsub)."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>shardchannel</code></summary>\n\nThe shard channel to publish to.\n\n</details>\n\n<details open><summary><code>message</code></summary>\n\nThe message to publish.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"For example the following command publishes to the `orders` channel with a subscriber already waiting for message(s).\n    \n[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of clients that the message was sent to. Note that in a Redis Cluster, only clients that are connected to the same node as the publishing client are included in the count\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of clients that the message was sent to. Note that in a Redis Cluster, only clients that are connected to the same node as the publishing client are included in the count"}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> spublish orders hello\n(integer) 1","section_id":"examples"}]}
{"schema_version":2,"id":"commands/srandmember","title":"SRANDMEMBER","url":"https://redis.io/docs/latest/commands/srandmember/","summary":"Get one or multiple random members from a set","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"043e949ddc44b44287200203be925b44400017a156e581d03275de5f3226a6a8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"When called with just the `key` argument, return a random element from the set value stored at `key`.\n\nIf the provided `count` argument is positive, return an array of **distinct elements**.\nThe array's length is either `count` or the set's cardinality ([`SCARD`](https://redis.io/docs/latest/commands/scard)), whichever is lower.\n\nIf called with a negative `count`, the behavior changes and the command is allowed to return the **same element multiple times**.\nIn this case, the number of returned elements is the absolute value of the specified `count`."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the set.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>count</code></summary>\n\nThe number of members to return. A positive `count` returns up to that many distinct members; a negative `count` returns exactly `|count|` members and may repeat them.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SADD myset one two three\n(integer) 3\nredis> SRANDMEMBER myset\nredis> SRANDMEMBER myset 2\nredis> SRANDMEMBER myset -5"},{"id":"details","title":"Details","role":"content","text":""},{"id":"specification-of-the-behavior-when-count-is-passed","title":"Specification of the behavior when count is passed","role":"content","text":"When the `count` argument is a positive value this command behaves as follows:\n\n* No repeated elements are returned.\n* If `count` is bigger than the set's cardinality, the command will only return the whole set without additional elements.\n* The order of elements in the reply is not truly random, so it is up to the client to shuffle them if needed.\n\nWhen the `count` is a negative value, the behavior changes as follows:\n\n* Repeating elements are possible.\n* Exactly `count` elements, or an empty array if the set is empty (non-existing key), are always returned.\n* The order of elements in the reply is truly random."},{"id":"distribution-of-returned-elements","title":"Distribution of returned elements","role":"content","text":"Note: this section is relevant only for Redis 5 or below, as Redis 6 implements a fairer algorithm. \n\nThe distribution of the returned elements is far from perfect when the number of elements in the set is small, this is due to the fact that we used an approximated random element function that does not really guarantees good distribution.\n\nThe algorithm used, that is implemented inside dict.c, samples the hash table buckets to find a non-empty one. Once a non empty bucket is found, since we use chaining in our hash table implementation, the number of elements inside the bucket is checked and a random element is selected.\n\nThis means that if you have two non-empty buckets in the entire hash table, and one has three elements while one has just one, the element that is alone in its bucket will be returned with much higher probability."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): without the additional _count_ argument, the command returns a randomly selected member, or a [Nil reply](../../develop/reference/protocol-spec#bulk-strings) when _key_ doesn't exist.\n* [Array reply](../../develop/reference/protocol-spec#arrays): when the optional _count_ argument is passed, the command returns an array of members, or an empty array when _key_ doesn't exist.\n\n**RESP3:**\n\nOne of the following:\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): without the additional _count_ argument, the command returns a randomly selected member, or a [Null reply](../../develop/reference/protocol-spec#nulls) when _key_ doesn't exist.\n* [Array reply](../../develop/reference/protocol-spec#arrays): when the optional _count_ argument is passed, the command returns an array of members, or an empty array when _key_ doesn't exist."}],"examples":[]}
{"schema_version":2,"id":"commands/srem","title":"SREM","url":"https://redis.io/docs/latest/commands/srem/","summary":"Removes one or more members from a set. Deletes the set if the last member was removed.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"1f9df15d2560b07c78f2437fb11e685d3ad0583187b91f89a8c3fb6269f0ddbe","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Remove the specified members from the set stored at `key`.\nSpecified members that are not a member of this set are ignored.\nIf `key` does not exist, it is treated as an empty set and this command returns\n`0`.\n\nAn error is returned when the value stored at `key` is not a set."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the set.\n\n</details>\n\n<details open><summary><code>member [member ...]</code></summary>\n\nOne or more members to remove from the set.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SADD myset \"one\"\n(integer) 1\nredis> SADD myset \"two\"\n(integer) 1\nredis> SADD myset \"three\"\n(integer) 1\nredis> SREM myset \"one\"\n(integer) 1\nredis> SREM myset \"four\"\n(integer) 0\nredis> SMEMBERS myset\n1) \"two\"\n2) \"three\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of members that were removed from the set, not including non existing members.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): Number of members that were removed from the set, not including non existing members."}],"examples":[]}
{"schema_version":2,"id":"commands/sscan","title":"SSCAN","url":"https://redis.io/docs/latest/commands/sscan/","summary":"Iterates over members of a set.","since":"2.8.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"e84ca2e3a486172d102a2a49ab22fc5ff43586a9f7c31dd0f2556f07b255b31b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"See [`SCAN`](https://redis.io/docs/latest/commands/scan) for `SSCAN` documentation."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the set.\n\n</details>\n\n<details open><summary><code>cursor</code></summary>\n\nThe cursor value. Start an iteration with `0`, then use the cursor returned by each call as the argument to the next call.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>MATCH pattern</code></summary>\n\nOnly return members matching the given glob-style pattern.\n\n</details>\n\n<details open><summary><code>COUNT count</code></summary>\n\nA hint for the number of members to return per iteration. The default is 10.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): specifically, an array with two elements:\n* The first element is a [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings) that represents an unsigned 64-bit number, the cursor.\n* The second element is an [Array reply](../../develop/reference/protocol-spec#arrays) with the names of scanned members.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): specifically, an array with two elements:\n* The first element is a [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings) that represents an unsigned 64-bit number, the cursor.\n* The second element is an [Array reply](../../develop/reference/protocol-spec#arrays) with the names of scanned members."}],"examples":[]}
{"schema_version":2,"id":"commands/ssubscribe","title":"SSUBSCRIBE","url":"https://redis.io/docs/latest/commands/ssubscribe/","summary":"Listens for messages published to shard channels.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:25+01:00","page_type":"content","content_hash":"0ce60c4acc5b5489f0f51406e3e539908fce263538f13343f3dd1b4411ec30d6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Subscribes the client to the specified shard channels.\n\nIn Redis Cluster, shard channels are assigned to slots with the same algorithm Redis uses to assign keys to slots. To receive messages published to a shard channel, subscribe to a node, either a primary or replica, that serves the channel’s slot.\n\nAll shard channels in a single SSUBSCRIBE call must belong to the same slot. To subscribe to shard channels across different slots, use separate SSUBSCRIBE calls.\n\nFor more information about sharded Pub/Sub, see [Sharded Pub/Sub](https://redis.io/docs/latest/develop/pubsub#sharded-pubsub)."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>shardchannel [shardchannel ...]</code></summary>\n\nOne or more shard channels to subscribe to.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nWhen successful, this command doesn't return anything. Instead, for each shard channel, one message with the first element being the string `ssubscribe` is pushed as a confirmation that the command succeeded. Note that this command can also return a -MOVED redirect.\n\n**RESP3:**\n\nWhen successful, this command doesn't return anything. Instead, for each shard channel, one message with the first element being the string 'ssubscribe' is pushed as a confirmation that the command succeeded. Note that this command can also return a -MOVED redirect."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> ssubscribe orders\nReading messages... (press Ctrl-C to quit)\n1) \"ssubscribe\"\n2) \"orders\"\n3) (integer) 1\n1) \"smessage\"\n2) \"orders\"\n3) \"hello\"","section_id":"examples"}]}
{"schema_version":2,"id":"commands/strlen","title":"STRLEN","url":"https://redis.io/docs/latest/commands/strlen/","summary":"Returns the length of a string value.","since":"2.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"a47dae2f6b63bc8673f4607564ab985bd26eaf1773aea788a362d9fe188d404a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns the length of the string value stored at `key`.\nAn error is returned when `key` holds a non-string value."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SET mykey \"Hello world\"\nOK\nredis> STRLEN mykey\n(integer) 11\nredis> STRLEN nonexisting\n(integer) 0"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the length of the string stored at key, or 0 when the key does not exist.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the length of the string stored at key, or 0 when the key does not exist."}],"examples":[]}
{"schema_version":2,"id":"commands/subscribe","title":"SUBSCRIBE","url":"https://redis.io/docs/latest/commands/subscribe/","summary":"Listens for messages published to channels.","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:25+01:00","page_type":"content","content_hash":"9ed34543ed7d205a7416663a7595d238646d17b8801ea5ce0e36a5e88e9b1c7f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Subscribes the client to the specified channels.\n\nOnce the client enters the subscribed state it is not supposed to issue any\nother commands, except for additional `SUBSCRIBE`, [`SSUBSCRIBE`](https://redis.io/docs/latest/commands/ssubscribe), [`PSUBSCRIBE`](https://redis.io/docs/latest/commands/psubscribe), [`UNSUBSCRIBE`](https://redis.io/docs/latest/commands/unsubscribe), [`SUNSUBSCRIBE`](https://redis.io/docs/latest/commands/sunsubscribe), \n[`PUNSUBSCRIBE`](https://redis.io/docs/latest/commands/punsubscribe), [`PING`](https://redis.io/docs/latest/commands/ping), [`RESET`](https://redis.io/docs/latest/commands/reset) and [`QUIT`](https://redis.io/docs/latest/commands/quit) commands.\nHowever, if RESP3 is used (see [`HELLO`](https://redis.io/docs/latest/commands/hello)) it is possible for a client to issue any commands while in subscribed state.\n\nFor more information, see [Pub/sub](https://redis.io/docs/latest/develop/pubsub)."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>channel [channel ...]</code></summary>\n\nOne or more channels to subscribe to.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nWhen successful, this command doesn't return anything. Instead, for each channel, one message with the first element being the string `subscribe` is pushed as a confirmation that the command succeeded.\n\n**RESP3:**\n\nWhen successful, this command doesn't return anything. Instead, for each channel, one message with the first element being the string `subscribe` is pushed as a confirmation that the command succeeded."}],"examples":[]}
{"schema_version":2,"id":"commands/substr","title":"SUBSTR","url":"https://redis.io/docs/latest/commands/substr/","summary":"Returns a substring from a string value.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"c1eeb435975c949092d367f71df1c4d3304fc6f9c66ccf7297a9e6b912e2136d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns the substring of the string value stored at `key`, determined by the\noffsets `start` and `end` (both are inclusive).\nNegative offsets can be used in order to provide an offset starting from the end\nof the string.\nFor example, -1 is the last character, -2 is the penultimate character, and so on.\n\nThe function handles out of range requests by limiting the resulting range to\nthe actual length of the string."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key.\n\n</details>\n\n<details open><summary><code>start</code></summary>\n\nThe start offset, zero-based. A negative value counts from the end of the string.\n\n</details>\n\n<details open><summary><code>end</code></summary>\n\nThe end offset, zero-based and inclusive. A negative value counts from the end of the string.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SET mykey \"This is a string\"\nOK\nredis> GETRANGE mykey 0 3\n\"This\"\nredis> GETRANGE mykey -3 -1\n\"ing\"\nredis> GETRANGE mykey 0 -1\n\"This is a string\"\nredis> GETRANGE mykey 10 100\n\"string\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | Deprecated as of Redis v2.0.0. |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the substring of the string value stored at key, determined by the offsets start and end (both are inclusive).\n\n**RESP3:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the substring of the string value stored at key, determined by the offsets start and end (both are inclusive)."}],"examples":[]}
{"schema_version":2,"id":"commands/sunion","title":"SUNION","url":"https://redis.io/docs/latest/commands/sunion/","summary":"Returns the union of multiple sets.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"ea7c63510edbffdc38b22adc2964497bcf3ee8d1b132032ad51eea275433ba0c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nReturns the members of the set resulting from the union of all the given sets.\n\nFor example:\n\n[code example]\n\nKeys that do not exist are considered to be empty sets."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key [key ...]</code></summary>\n\nOne or more set keys to union.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SADD key1 \"a\"\n(integer) 1\nredis> SADD key1 \"b\"\n(integer) 1\nredis> SADD key1 \"c\"\n(integer) 1\nredis> SADD key2 \"c\"\n(integer) 1\nredis> SADD key2 \"d\"\n(integer) 1\nredis> SADD key2 \"e\"\n(integer) 1\nredis> SUNION key1 key2\n1) \"a\"\n2) \"c\"\n3) \"d\"\n4) \"e\"\n5) \"b\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list with members of the resulting set.\n\n**RESP3:**\n\n[Set reply](../../develop/reference/protocol-spec#sets): a set with the members of the resulting set."},{"id":"see-also","title":"See also","role":"related","text":"[`SUNIONCARD`](https://redis.io/docs/latest/commands/sunioncard) | [`SUNIONSTORE`](https://redis.io/docs/latest/commands/sunionstore)"}],"examples":[{"id":"overview-ex0","language":"plaintext","code":"key1 = {a,b,c,d}\nkey2 = {c}\nkey3 = {a,c,e}\nSUNION key1 key2 key3 = {a,b,c,d,e}","section_id":"overview"}]}
{"schema_version":2,"id":"commands/sunioncard","title":"SUNIONCARD","url":"https://redis.io/docs/latest/commands/sunioncard/","summary":"Returns the number of members of the union of multiple sets.","since":"8.10.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"864084bdbf9d83304420e157c86e36b50115eef099eda07f0044c5e1643bc46a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nReturns the cardinality of the union of the given sets. This is the count-only counterpart of [`SUNION`](https://redis.io/docs/latest/commands/sunion): it returns just the number of distinct elements in the union, not the members themselves."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>numkeys</code></summary>\n\nThe number of keys that follow.\n\n</details>\n\n<details open><summary><code>key [key ...]</code></summary>\n\nOne or more set keys to union.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>APPROX</code></summary>\n\nReturn an approximate cardinality instead of an exact count.\n\n</details>\n\n<details open><summary><code>LIMIT limit</code></summary>\n\nStop counting once the cardinality reaches `limit`. `0` (the default) means no limit.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SADD key1 \"a\"\n(integer) 1\nredis> SADD key1 \"b\"\n(integer) 1\nredis> SADD key1 \"c\"\n(integer) 1\nredis> SADD key2 \"c\"\n(integer) 1\nredis> SADD key2 \"d\"\n(integer) 1\nredis> SADD key2 \"e\"\n(integer) 1\nredis> SUNION key1 key2\n1) \"a\"\n2) \"c\"\n3) \"d\"\n4) \"b\"\n5) \"e\"\nredis> SUNIONCARD 2 key1 key2\n(integer) 5\nredis> SUNIONCARD 2 key1 key2 LIMIT 3\n(integer) 3"},{"id":"details","title":"Details","role":"content","text":"Keys that do not exist are considered to be empty sets.\n\nWhen provided with the optional `LIMIT` argument (which defaults to `0`, meaning unlimited), if the union cardinality reaches `limit` partway through the computation, the command stops and returns `limit` as the cardinality. This ensures a significant speedup for queries where the limit is lower than the actual union cardinality. `LIMIT` works in both exact and approximate modes."},{"id":"approximate-cardinality","title":"Approximate cardinality","role":"content","text":"By default, `SUNIONCARD` returns the exact union cardinality. With the `APPROX` option, it instead uses [HyperLogLog](https://redis.io/docs/latest/develop/data-types/probabilistic/hyperloglogs) internally to estimate the cardinality with a standard error of about 0.81%, without materializing the full union. This is useful for very large unions, where computing an exact count is expensive in both time and memory."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of elements in the resulting union.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of elements in the resulting union."},{"id":"see-also","title":"See also","role":"related","text":"[`SUNION`](https://redis.io/docs/latest/commands/sunion) | [`SUNIONSTORE`](https://redis.io/docs/latest/commands/sunionstore)"},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Redis sets](https://redis.io/docs/latest/develop/data-types/sets)\n- [Multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations)"}],"examples":[]}
{"schema_version":2,"id":"commands/sunionstore","title":"SUNIONSTORE","url":"https://redis.io/docs/latest/commands/sunionstore/","summary":"Stores the union of multiple sets in a key.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:41+01:00","page_type":"content","content_hash":"ffe09b133424f91f7bd05908a18ff0e98128b16f8e7c78f19c2c94c627bdb4a8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nThis command is equal to [`SUNION`](https://redis.io/docs/latest/commands/sunion), but instead of returning the resulting set,\nit is stored in `destination`.\n\nIf `destination` already exists, it is overwritten."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>destination</code></summary>\n\nThe key to store the resulting set in.\n\n</details>\n\n<details open><summary><code>key [key ...]</code></summary>\n\nOne or more set keys to union.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SADD key1 \"a\"\n(integer) 1\nredis> SADD key1 \"b\"\n(integer) 1\nredis> SADD key1 \"c\"\n(integer) 1\nredis> SADD key2 \"c\"\n(integer) 1\nredis> SADD key2 \"d\"\n(integer) 1\nredis> SADD key2 \"e\"\n(integer) 1\nredis> SUNIONSTORE key key1 key2\n(integer) 5\nredis> SMEMBERS key\n1) \"a\"\n2) \"b\"\n3) \"c\"\n4) \"d\"\n5) \"e\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of elements in the resulting set.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): Number of the elements in the resulting set."},{"id":"see-also","title":"See also","role":"related","text":"[`SUNION`](https://redis.io/docs/latest/commands/sunion) | [`SUNIONCARD`](https://redis.io/docs/latest/commands/sunioncard)"}],"examples":[]}
{"schema_version":2,"id":"commands/sunsubscribe","title":"SUNSUBSCRIBE","url":"https://redis.io/docs/latest/commands/sunsubscribe/","summary":"Stops listening to messages posted to shard channels.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:25+01:00","page_type":"content","content_hash":"45f85b7b647777eef3bf5c6a12d57a55ff375a08cac2978b206cc53527a20730","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Unsubscribes the client from the specified patterns, or from all patterns if none are specified.\n\nIf you don’t specify any shard channels, the client unsubscribes from all previously subscribed shard channels. The client receives one message for each shard channel it unsubscribes from.\n\nNote: global channels and shard channels need to be unsubscribed from separately.\n\nFor more information about sharded Pub/Sub, see [Sharded Pub/Sub](https://redis.io/docs/latest/develop/pubsub#sharded-pubsub)."},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>shardchannel [shardchannel ...]</code></summary>\n\nOne or more shard channels to unsubscribe from. If omitted, the client is unsubscribed from all shard channels.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nWhen successful, this command doesn't return anything. Instead, for each shard channel, one message with the first element being the string `sunsubscribe` is pushed as a confirmation that the command succeeded.\n\n**RESP3:**\n\nWhen successful, this command doesn't return anything. Instead, for each shard channel, one message with the first element being the string `sunsubscribe` is pushed as a confirmation that the command succeeded."}],"examples":[]}
{"schema_version":2,"id":"commands/swapdb","title":"SWAPDB","url":"https://redis.io/docs/latest/commands/swapdb/","summary":"Swaps two Redis databases.","since":"4.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-06-15T06:26:34-07:00","page_type":"content","content_hash":"5f36cc1bd576fa863343099a344a9ccded3f758da8ac5837348e11a6959d371f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This command swaps two Redis databases, so that immediately all the\nclients connected to a given database will see the data of the other database, and\nthe other way around."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>index1</code></summary>\n\nThe index of the first database.\n\n</details>\n\n<details open><summary><code>index2</code></summary>\n\nThe index of the second database.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]\n\nThis will swap database 0 with database 1. All the clients connected with database 0 will immediately see the new data, exactly like all the clients connected with database 1 will see the data that was formerly of database 0."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"SWAPDB 0 1","section_id":"examples"}]}
{"schema_version":2,"id":"commands/sync","title":"SYNC","url":"https://redis.io/docs/latest/commands/sync/","summary":"An internal command used in replication.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:09+01:00","page_type":"content","content_hash":"d9d4dd33597866203675cc81b6cb09f6bac090664fb2ccde5810e13cd294dadb","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Initiates a replication stream from the master.\n\nThe `SYNC` command is called by Redis replicas for initiating a replication\nstream from the master. It has been replaced in newer versions of Redis by\n [`PSYNC`](https://redis.io/docs/latest/commands/psync).\n\nFor more information about replication in Redis please check the\n[replication page](https://redis.io/docs/latest/operate/oss_and_stack/management/replication)."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n**Non-standard return value**, a bulk transfer of the data followed by `PING` and write requests from the master.\n\n**RESP3:**\n\n**Non-standard return value**, a bulk transfer of the data followed by `PING` and write requests from the master."}],"examples":[]}
{"schema_version":2,"id":"commands/tdigest.add","title":"TDIGEST.ADD","url":"https://redis.io/docs/latest/commands/tdigest.add/","summary":"Adds one or more observations to a t-digest sketch","since":"2.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"2ab4a0ef6c70c8ed3b6fef266bdbec9a711b32c24bb55cf2a5cd227732faf291","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Adds one or more observations to a t-digest sketch."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary> \n\nis the key name for an existing t-digest sketch.\n</details>\n\n<details open><summary><code>value</code></summary> \n\nis the floating-point value of an observation.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> TDIGEST.ADD t 1 2 3\nOK\n\n\n\nredis> TDIGEST.ADD t string\n(error) ERR T-Digest: error parsing val parameter"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\"><nobr>&#x2705; Flexible & Annual</nobr></span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: the given key does not exist or the value parameter is of the incorrect type.\n\n**RESP3:**\n\nOne of the following:\n\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: the given key does not exist or the value parameter is of the incorrect type."}],"examples":[]}
{"schema_version":2,"id":"commands/tdigest.byrank","title":"TDIGEST.BYRANK","url":"https://redis.io/docs/latest/commands/tdigest.byrank/","summary":"Returns, for each input rank, a floating-point estimation of the value with that rank","since":"2.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"79396941afcb30c2172e913bfeaaa6bd1c671ae4cb7479c41da327eff2ed6a19","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns, for each input rank, a floating-point estimation of the value with that rank.\nMultiple estimations can be retrieved in a single call."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nis the key name of an existing t-digest sketch.\n</details>\n\n<details open><summary><code>rank</code></summary>\n\nRanks for which the values should be retrieved.\n\n0 is the rank of the value of the smallest observation.\n\n_n_-1 is the rank of the value of the largest observation, where _n_ denotes the number of observations that have been added to the sketch.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> TDIGEST.CREATE t COMPRESSION 1000\nOK\nredis> TDIGEST.ADD t 1 2 2 3 3 3 4 4 4 4 5 5 5 5 5\nOK\nredis> TDIGEST.BYRANK t 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15\n 1) \"1\"\n 2) \"2\"\n 3) \"2\"\n 4) \"3\"\n 5) \"3\"\n 6) \"3\"\n 7) \"4\"\n 8) \"4\"\n 9) \"4\"\n10) \"4\"\n11) \"5\"\n12) \"5\"\n13) \"5\"\n14) \"5\"\n15) \"5\"\n16) \"inf\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\"><nobr>&#x2705; Flexible & Annual</nobr></span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n* [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [bulk strings](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) as floating-points, populated with value_1, value_2, ..., value_R:\n    * an accurate result when `rank` is `0`, the value of the smallest observation.\n    * an accurate result when `rank` is _n_-1, the value of the largest observation, where _n_ denotes the number of observations added to the sketch.\n    * `inf` when `rank` is equal to _n_ or larger than _n_.\n    * `nan` for all ranks when the given sketch is empty.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: the given key does not exist or is of an incorrect type, rank parsing errors, or incorrect number of arguments.\n\n**RESP3:**\n\nOne of the following:\n\n* [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [doubles](https://redis.io/docs/latest/develop/reference/protocol-spec#doubles) as floating-points, populated with value_1, value_2, ..., value_R:\n    * an accurate result when `rank` is `0`, the value of the smallest observation.\n    * an accurate result when `rank` is _n_-1, the value of the largest observation, where _n_ denotes the number of observations added to the sketch.\n    * `inf` when `rank` is equal to _n_ or larger than _n_.\n    * `nan` for all ranks when the given sketch is empty.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: the given key does not exist or is of an incorrect type, rank parsing errors, or incorrect number of arguments."}],"examples":[]}
{"schema_version":2,"id":"commands/tdigest.byrevrank","title":"TDIGEST.BYREVRANK","url":"https://redis.io/docs/latest/commands/tdigest.byrevrank/","summary":"Returns, for each input reverse rank, an estimation of the floating-point value with that reverse rank","since":"2.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"d5e953b92a4ed2a81672a030aa66bca79bb3811d7f73bc57daa55cb7518ae6e2","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns, for each input reverse rank (`revrank`), an estimation of the floating-point value with that reverse rank.\nMultiple estimations can be retrieved in a single call."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nis the key name of an existing t-digest sketch.\n</details>\n\n<details open><summary><code>revrank</code></summary>\n\nReverse ranks for which the values should be retrieved.\n\n0 is the reverse rank of the value of the largest observation.\n  \n_n_-1 is the reverse rank of the value of the smallest observation, where _n_ denotes the number of observations added to the sketch.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> TDIGEST.CREATE t COMPRESSION 1000\nOK\nredis> TDIGEST.ADD t 1 2 2 3 3 3 4 4 4 4 5 5 5 5 5\nOK\nredis> TDIGEST.BYREVRANK t 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15\n 1) \"5\"\n 2) \"5\"\n 3) \"5\"\n 4) \"5\"\n 5) \"5\"\n 6) \"4\"\n 7) \"4\"\n 8) \"4\"\n 9) \"4\"\n10) \"3\"\n11) \"3\"\n12) \"3\"\n13) \"2\"\n14) \"2\"\n15) \"1\"\n16) \"-inf\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\"><nobr>&#x2705; Flexible & Annual</nobr></span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n* [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [bulk strings](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) as floating-points, populated with value_1, value_2, ..., value_R:\n    * an accurate result when `rank` is `0`, the value of the largest observation.\n    * an accurate result when `rank` is _n_-1, the value of the smallest observation, where _n_ denotes the number of observations added to the sketch.\n    * `inf` when `rank` is equal to _n_ or larger than _n_.\n    * `nan` for all ranks when the given sketch is empty.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: the given key does not exist or is of an incorrect type, rank parsing errors, or an incorrect number of arguments.\n\n**RESP3:**\n\nOne of the following:\n\n* [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [doubles](https://redis.io/docs/latest/develop/reference/protocol-spec#doubles) as floating-points, populated with value_1, value_2, ..., value_R:\n    * an accurate result when `rank` is `0`, the value of the largest observation.\n    * an accurate result when `rank` is _n_-1, the value of the smallest observation, where _n_ denotes the number of observations added to the sketch.\n    * `inf` when `rank` is equal to _n_ or larger than _n_.\n    * `nan` for all ranks when the given sketch is empty.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: the given key does not exist or is of an incorrect type, rank parsing errors, or an incorrect number of arguments."}],"examples":[]}
{"schema_version":2,"id":"commands/tdigest.cdf","title":"TDIGEST.CDF","url":"https://redis.io/docs/latest/commands/tdigest.cdf/","summary":"Returns, for each input value, an estimation of the floating-point fraction of (observations smaller than the given value + half the observations equal to the given value)","since":"2.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"3310a4ce359e2f9e50077f62afcb1f4c389269660bdf69080f6285ea5c98f52a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns, for each input value, an estimation of the floating-point fraction of (_observations smaller than the given value_ + _half the observations equal to the given value_).\nMultiple fractions can be retrieved in a single call."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nis the key name for an existing t-digest sketch.\n</details>\n\n<details open><summary><code>value</code></summary>\n\nare the values for which the CDF (Cumulative Distribution Function) should be retrieved.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> TDIGEST.CREATE t COMPRESSION 1000\nOK\nredis> TDIGEST.ADD t 1 2 2 3 3 3 4 4 4 4 5 5 5 5 5\nOK\nredis> TDIGEST.CDF t 0 1 2 3 4 5 6\n1) \"0\"\n2) \"0.033333333333333333\"\n3) \"0.13333333333333333\"\n4) \"0.29999999999999999\"\n5) \"0.53333333333333333\"\n6) \"0.83333333333333337\"\n7) \"1\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n* [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [bulk string replies](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) as floating-points, populated with fraction_1, fraction_2, ..., fraction_N.\nAll values are `nan` if the given sketch is empty.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: the given key does not exist or is of an incorrect type, value parsing errors, or an incorrect number of arguments.\n\n**RESP3:**\n\nOne of the following:\n\n* [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [doubles](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) populated with fraction_1, fraction_2, ..., fraction_N.\nAll values are `nan` if the given sketch is empty.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: the given key does not exist or is of an incorrect type, value parsing errors, or an incorrect number of arguments."}],"examples":[]}
{"schema_version":2,"id":"commands/tdigest.create","title":"TDIGEST.CREATE","url":"https://redis.io/docs/latest/commands/tdigest.create/","summary":"Allocates memory and initializes a new t-digest sketch","since":"2.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"1200db315eb72d6e11cb79d245a8f5586d2cc2fe422f3e03bc0d983e219972ac","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Allocates memory and initializes a new t-digest sketch."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary> \n\nis the key name for this new t-digest sketch.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>COMPRESSION compression</code></summary>\n\nis a controllable tradeoff between accuracy and memory consumption. 100 is a common value for normal uses and also the default if not specified. 1000 is more accurate. For more information on scaling of accuracy versus the compression value see [_The t-digest: Efficient estimates of distributions_](https://www.sciencedirect.com/science/article/pii/S2665963820300403).\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> TDIGEST.CREATE t COMPRESSION 100\nOK"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: incorrect key type or incorrect keyword.\n\n**RESP3:**\n\nOne of the following:\n\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: incorrect key type or incorrect keyword."}],"examples":[]}
{"schema_version":2,"id":"commands/tdigest.info","title":"TDIGEST.INFO","url":"https://redis.io/docs/latest/commands/tdigest.info/","summary":"Returns information and statistics about a t-digest sketch","since":"2.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"ef489b8e626c6282cfcd358e1c5e8d1391c0f15b270f7ac85137c45c322ca537","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns information and statistics about a t-digest sketch."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary> \n\nis the key name for an existing t-digest sketch.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> TDIGEST.CREATE t\nOK\nredis> TDIGEST.ADD t 1 2 3 4 5\nOK\nredis> TDIGEST.INFO t\n 1) Compression\n 2) (integer) 100\n 3) Capacity\n 4) (integer) 610\n 5) Merged nodes\n 6) (integer) 0\n 7) Unmerged nodes\n 8) (integer) 5\n 9) Merged weight\n10) (integer) 0\n11) Unmerged weight\n12) (integer) 5\n13) Observations\n14) (integer) 5\n15) Total compressions\n16) (integer) 0\n17) Memory usage\n18) (integer) 9768"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) with information about the given sketch as name-value pairs:\n\n    | Name<br>[Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) | Description\n    | ---------------------------- | -\n    | `Compression`        | [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers)<br> The compression (controllable trade-off between accuracy and memory consumption) of the sketch \n    | `Capacity`           | [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers)<br> Size of the buffer used for storing the centroids and for the incoming unmerged observations\n    | `Merged nodes`       | [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers)<br> Number of merged observations\n    | `Unmerged nodes`     | [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers)<br> Number of buffered nodes (uncompressed observations)\n    | `Merged weight`      | [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers)<br> Weight of values of the merged nodes\n    | `Unmerged weight`    | [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers)<br> Weight of values of the unmerged nodes (uncompressed observations)\n    | `Observations`       | [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers)<br> Number of observations added to the sketch\n    | `Total compressions` | [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers)<br> Number of times this sketch compressed data together\n    | `Memory usage`       | [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers)<br> Number of bytes allocated for the sketch\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: the given key does not exist or is of an incorrect type or an incorrect number of arguments.\n\n**RESP3:**\n\nOne of the following:\n\n* [Map reply](https://redis.io/docs/latest/develop/reference/protocol-spec#maps) with information about the given sketch as name-value pairs:\n\n    | Name<br>[Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) | Description\n    | ---------------------------- | -\n    | `Compression`        | [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers)<br> The compression (controllable trade-off between accuracy and memory consumption) of the sketch \n    | `Capacity`           | [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers)<br> Size of the buffer used for storing the centroids and for the incoming unmerged observations\n    | `Merged nodes`       | [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers)<br> Number of merged observations\n    | `Unmerged nodes`     | [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers)<br> Number of buffered nodes (uncompressed observations)\n    | `Merged weight`      | [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers)<br> Weight of values of the merged nodes\n    | `Unmerged weight`    | [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers)<br> Weight of values of the unmerged nodes (uncompressed observations)\n    | `Observations`       | [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers)<br> Number of observations added to the sketch\n    | `Total compressions` | [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers)<br> Number of times this sketch compressed data together\n    | `Memory usage`       | [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers)<br> Number of bytes allocated for the sketch\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: the given key does not exist or is of an incorrect type or an incorrect number of arguments."}],"examples":[]}
{"schema_version":2,"id":"commands/tdigest.max","title":"TDIGEST.MAX","url":"https://redis.io/docs/latest/commands/tdigest.max/","summary":"Returns the maximum observation value from a t-digest sketch","since":"2.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"4078f9bf53e380d31dbd3d9924a145dc6aea65de640944301e88dc278eb698a9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns the maximum observation value from a t-digest sketch."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nis the key name for an existing t-digest sketch.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> TDIGEST.CREATE t\nOK\nredis> TDIGEST.MAX t\n\"nan\"\nredis> TDIGEST.ADD t 3 4 1 2 5\nOK\nredis>TDIGEST.MAX t\n\"5\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n* [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) as floating-point representing the maximum observation value from the given sketch. The result is always accurate. `nan` is returned if the sketch is empty.\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) in these cases: incorrect number of arguments or incorrect key type.\n\n**RESP3:**\n\nOne of the following:\n\n* [Double reply](https://redis.io/docs/latest/develop/reference/protocol-spec#doubles) representing the maximum observation value from the given sketch. The result is always accurate. `nan` is returned if the sketch is empty.\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) in these cases: incorrect number of arguments or incorrect key type."}],"examples":[]}
{"schema_version":2,"id":"commands/tdigest.merge","title":"TDIGEST.MERGE","url":"https://redis.io/docs/latest/commands/tdigest.merge/","summary":"Merges multiple t-digest sketches into a single sketch","since":"2.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"850176f9f733b667ebcefe2e98b35bf200b9483e3a624230a631d8c471f2febe","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nMerges multiple t-digest sketches into a single sketch."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>destination-key</code></summary>\n\nis the key name for a t-digest sketch to merge observation values to.\n\nIf `destination-key` does not exist, a new sketch is created.\n\nIf `destination-key` is an existing sketch, its values are merged with the values of the source keys. To override the destination key contents use `OVERRIDE`.\n</details>\n\n<details open><summary><code>numkeys</code></summary>\n\nthe number of sketches from which to merge observation values (one or more).\n</details>\n\n<details open><summary><code>source-key</code></summary>\n\nEach `source-key` is a key name for a t-digest sketch from which to merge observation values.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>COMPRESSION compression</code></summary>\n  \nis a controllable tradeoff between accuracy and memory consumption. 100 is a common value for normal uses and also the default if not specified. 1000 is more accurate. For more information on scaling of accuracy versus the compression value see [_The t-digest: Efficient estimates of distributions_](https://www.sciencedirect.com/science/article/pii/S2665963820300403).\n  \nWhen `COMPRESSION` is not specified:\n- If `destination-key` does not exist or if `OVERRIDE` is specified, the compression is set to the maximum value among all source sketches.\n- If `destination-key` already exists and `OVERRIDE` is not specified, its compression is not changed.\n\n</details>\n\n<details open><summary><code>OVERRIDE</code></summary>\n\nIf `destination-key` already exists and `OVERRIDE` is specified, the key is overwritten.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> TDIGEST.CREATE {metrics}:m1\nOK\nredis> TDIGEST.CREATE {metrics}:m2\nOK\nredis> TDIGEST.ADD {metrics}:m1 10.0 20.0\nOK\nredis> TDIGEST.ADD {metrics}:m2 30.0 40.0\nOK\nredis> TDIGEST.MERGE {metrics}:m 2 {metrics}:m1 {metrics}:m2\nOK\nredis> TDIGEST.BYRANK {metrics}:m 0 1 2 3 4\n1) \"10\"\n2) \"20\"\n3) \"30\"\n4) \"40\"\n5) \"inf\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) `OK` if successful.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in the following cases: incorrect key type, incorrect keyword, or incorrect number of arguments.\n\n**RESP3:**\n\nOne of the following:\n\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) `OK` if successful.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in the following cases: incorrect key type, incorrect keyword, or incorrect number of arguments."}],"examples":[]}
{"schema_version":2,"id":"commands/tdigest.min","title":"TDIGEST.MIN","url":"https://redis.io/docs/latest/commands/tdigest.min/","summary":"Returns the minimum observation value from a t-digest sketch","since":"2.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"52d0e11d6e4ae8edb6f791e7cde0dbdb1a94d0ead22ceb79b156137998cd87c3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns the minimum observation value from a t-digest sketch."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nis the key name for an existing t-digest sketch.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> TDIGEST.CREATE t\nOK\nredis> TDIGEST.MIN t\n\"nan\"\nredis> TDIGEST.ADD t 3 4 1 2 5\nOK\nredis> TDIGEST.MIN t\n\"1\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n* [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) as floating-point representing the minimum observation value from the given sketch. The result is always accurate. `nan` is returned if the sketch is empty.\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) in these cases: incorrect number of arguments or incorrect key type.\n\n**RESP3:**\n\nOne of the following:\n\n* [Double reply](https://redis.io/docs/latest/develop/reference/protocol-spec#doubles) representing the minimum observation value from the given sketch. The result is always accurate. `nan` is returned if the sketch is empty.\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) in these cases: incorrect number of arguments or incorrect key type."}],"examples":[]}
{"schema_version":2,"id":"commands/tdigest.quantile","title":"TDIGEST.QUANTILE","url":"https://redis.io/docs/latest/commands/tdigest.quantile/","summary":"Returns, for each input fraction, a floating-point estimation of the value that is smaller than the given fraction of observations","since":"2.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"08e662e27ffd713fc8312b098ab7b5929121b50e06341065439eaa7750ee2109","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns, for each input fraction, a floating-point estimation of the value that is smaller than the given fraction of observations.\nMultiple quantiles can be retrieved in a single call."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary> \n\nis the key name for an existing t-digest sketch.\n</details>\n\n<details open><summary><code>quantile</code></summary> \n\nis the input fraction between 0 and 1 inclusively.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> TDIGEST.CREATE t COMPRESSION 1000\nOK\nredis> TDIGEST.ADD t 1 2 2 3 3 3 4 4 4 4 5 5 5 5 5\nOK\nredis> TDIGEST.QUANTILE t 0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1\n 1) \"1\"\n 2) \"2\"\n 3) \"3\"\n 4) \"3\"\n 5) \"4\"\n 6) \"4\"\n 7) \"4\"\n 8) \"5\"\n 9) \"5\"\n10) \"5\"\n11) \"5\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n* [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [bulk string replies](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) as floating-point estimates, populated with value_1, value_2, ..., value_N.\n    * an accurate result when `quantile` is `0`, the value of the smallest observation.\n    * an accurate result when `quantile` is `1`, the value of the largest observation.\n    * `nan` for all quantiles when the given sketch is empty.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: the given key does not exist or is of an incorrect type, quantile parsing errors, or incorrect number of arguments.\n\n**RESP3:**\n\nOne of the following:\n\n* [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [double replies](https://redis.io/docs/latest/develop/reference/protocol-spec#doubles) as estimates, populated with value_1, value_2, ..., value_N.\n    * an accurate result when `quantile` is `0`, the value of the smallest observation.\n    * an accurate result when `quantile` is `1`, the value of the largest observation.\n    * `nan` for all quantiles when the given sketch is empty.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: the given key does not exist or is of an incorrect type, quantile parsing errors, or incorrect number of arguments."}],"examples":[]}
{"schema_version":2,"id":"commands/tdigest.rank","title":"TDIGEST.RANK","url":"https://redis.io/docs/latest/commands/tdigest.rank/","summary":"Returns, for each floating-point input value, the estimated rank of the value (the number of observations in the sketch that are smaller than the value + half the number of observations that are equal to the value)","since":"2.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"26ac87a3637af11dcff551ce5c2d69fedef08c13dd867d1cb6052a91a8862326","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns, for each floating-point input value, the estimated rank of the value (_the number of observations in the sketch that are smaller than the value_ + _half the number of observations that are equal to the value_).\nMultiple ranks can be retrieved in a single call."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nis the key name for an existing t-digest sketch.\n</details>\n\n<details open><summary><code>value</code></summary>\n\nis the input value for which the rank should be estimated.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> TDIGEST.CREATE s COMPRESSION 1000\nOK\nredis> TDIGEST.ADD s 10 20 30 40 50 60\nOK\nredis> TDIGEST.RANK s 0 10 20 30 40 50 60 70\n1) (integer) -1\n2) (integer) 0\n3) (integer) 1\n4) (integer) 2\n5) (integer) 3\n6) (integer) 4\n7) (integer) 5\n8) (integer) 6\nredis> TDIGEST.REVRANK s 0 10 20 30 40 50 60 70\n1) (integer) 6\n2) (integer) 5\n3) (integer) 4\n4) (integer) 3\n5) (integer) 2\n6) (integer) 1\n7) (integer) 0\n8) (integer) -1  \n\n  \n\nredis> TDIGEST.CREATE s COMPRESSION 1000\nOK\nredis> TDIGEST.ADD s 10 10 10 10 20 20\nOK\nredis> TDIGEST.RANK s 10 20\n1) (integer) 2\n2) (integer) 5\nredis> TDIGEST.REVRANK s 10 20\n1) (integer) 4\n2) (integer) 1"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\"><nobr>&#x2705; Flexible & Annual</nobr></span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n* [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [integers](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) populated with rank_1, rank_2, ..., rank_V:\n    * `-1` when `value` is smaller than the value of the smallest observation.\n    * The number of observations when `value` is larger than the value of the largest observation.\n    * Otherwise, an estimation of the number of (_observations smaller than `value`_ + _half the observations equal to `value`_).\n\n    `0` is the rank of the value of the smallest observation.\n\n    _n_-1 is the rank of the value of the largest observation, where _n_ denotes the number of observations added to the sketch.\n\n    All values are `-2` if the sketch is empty.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: the given key does not exist or is of an incorrect type, quantile parsing errors, or incorrect number of arguments.\n\n**RESP3:**\n\nOne of the following:\n\n* [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [integers](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) populated with rank_1, rank_2, ..., rank_V:\n    * `-1` when `value` is smaller than the value of the smallest observation.\n    * The number of observations when `value` is larger than the value of the largest observation.\n    * Otherwise, an estimation of the number of (_observations smaller than `value`_ + _half the observations equal to `value`_).\n\n    `0` is the rank of the value of the smallest observation.\n\n    _n_-1 is the rank of the value of the largest observation, where _n_ denotes the number of observations added to the sketch.\n\n    All values are `-2` if the sketch is empty.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: the given key does not exist or is of an incorrect type, quantile parsing errors, or incorrect number of arguments."}],"examples":[]}
{"schema_version":2,"id":"commands/tdigest.reset","title":"TDIGEST.RESET","url":"https://redis.io/docs/latest/commands/tdigest.reset/","summary":"Resets a t-digest sketch (empties the sketch and re-initializes it).","since":"2.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"43d305db3e34cf674dbc35f846b08569478489900397559b19ffa8d1a2a0731f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Resets a t-digest sketch: empties the sketch and re-initializes it."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nis the key name for an existing t-digest sketch.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> TDIGEST.RESET t\nOK"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: key does not exist or is of the wrong type, or incorrect number of arguments.\n\n**RESP3:**\n\nOne of the following:\n\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: key does not exist or is of the wrong type, or incorrect number of arguments."}],"examples":[]}
{"schema_version":2,"id":"commands/tdigest.revrank","title":"TDIGEST.REVRANK","url":"https://redis.io/docs/latest/commands/tdigest.revrank/","summary":"Returns, for each floating-point input value, the estimated reverse rank of the value (the number of observations in the sketch that are larger than the value + half the number of observations that are equal to the value)","since":"2.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"d7deae3caa59263a469a783b8d3420a9c717afa13152942c3307ba703828ef6d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns, for each floating-point input value, the estimated reverse rank of the value (_the number of observations in the sketch that are larger than the value_ + _half the number of observations that are equal to the value_).\nMultiple reverse ranks can be retrieved in a single call."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nis the key name for an existing t-digest sketch.\n</details>\n\n<details open><summary><code>value</code></summary>\n\nis the input value for which the reverse rank should be estimated.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> TDIGEST.CREATE s COMPRESSION 1000\nOK\nredis> TDIGEST.ADD s 10 20 30 40 50 60\nOK\nredis> TDIGEST.RANK s 0 10 20 30 40 50 60 70\n1) (integer) -1\n2) (integer) 0\n3) (integer) 1\n4) (integer) 2\n5) (integer) 3\n6) (integer) 4\n7) (integer) 5\n8) (integer) 6\nredis> TDIGEST.REVRANK s 0 10 20 30 40 50 60 70\n1) (integer) 6\n2) (integer) 5\n3) (integer) 4\n4) (integer) 3\n5) (integer) 2\n6) (integer) 1\n7) (integer) 0\n8) (integer) -1\n\n  \n\nredis> TDIGEST.CREATE s COMPRESSION 1000\nOK\nredis> TDIGEST.ADD s 10 10 10 10 20 20\nOK\nredis> TDIGEST.RANK s 10 20\n1) (integer) 2\n2) (integer) 5\nredis> TDIGEST.REVRANK s 10 20\n1) (integer) 4\n2) (integer) 1"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\"><nobr>&#x2705; Flexible & Annual</nobr></span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n* [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [integers](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) populated with revrank_1, revrank_2, ..., revrank_V:\n    * `-1` when `value` is larger than the value of the largest observation.\n    * The number of observations when `value` is smaller than the value of the smallest observation.\n    * Otherwise, an estimation of the number of (_observations larger than `value`_ + _half the observations equal to `value`_).\n\n    `0` is the reverse rank of the value of the largest observation.\n\n    _n_-1 is the rank of the value of the smallest observation, where _n_ denotes the number of observations added to the sketch.\n\n    All values are `-2` if the sketch is empty.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: the given key does not exist or is of an incorrect type, quantile parsing errors, or incorrect number of arguments.\n\n**RESP3:**\n\nOne of the following:\n\n* [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [integers](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) populated with revrank_1, revrank_2, ..., revrank_V:\n    * `-1` when `value` is larger than the value of the largest observation.\n    * The number of observations when `value` is smaller than the value of the smallest observation.\n    * Otherwise, an estimation of the number of (_observations larger than `value`_ + _half the observations equal to `value`_).\n\n    `0` is the reverse rank of the value of the largest observation.\n\n    _n_-1 is the rank of the value of the smallest observation, where _n_ denotes the number of observations added to the sketch.\n\n    All values are `-2` if the sketch is empty.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: the given key does not exist or is of an incorrect type, quantile parsing errors, or incorrect number of arguments."}],"examples":[]}
{"schema_version":2,"id":"commands/tdigest.trimmed_mean","title":"TDIGEST.TRIMMED_MEAN","url":"https://redis.io/docs/latest/commands/tdigest.trimmed_mean/","summary":"Returns an estimation of the mean value from the sketch, excluding observation values outside the low and high cutoff quantiles","since":"2.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"aa3fdb136b933dc3d2f2a2d1714a62de97acc8f80ec8b7232806220edabc4d06","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns an estimation of the mean value from the sketch, excluding observation values outside the low and high cutoff quantiles."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary> \n\nis the key name for an existing t-digest sketch.\n</details>\n\n<details open><summary><code>low_cut_quantile</code></summary> \n  \na floating-point value in the range [0..1]. It must be lower than `high_cut_quantile`.\n  \nWhen equal to 0, no low cut.\n  \nWhen greater than 0, exclude observation values lower than this quantile.\n</details>\n\n<details open><summary><code>high_cut_quantile</code></summary> \n  \na floating-point value in the range [0..1]. It must be higher than `low_cut_quantile`.\n  \nWhen less than 1, exclude observation values greater than or equal to this quantile.\n\nWhen equal to 1, no high cut.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> TDIGEST.CREATE t COMPRESSION 1000\nOK\nredis> TDIGEST.ADD t 1 2 3 4 5 6 7 8 9 10\nOK\nredis> TDIGEST.TRIMMED_MEAN t 0.1 0.6\n\"4\"\nredis> TDIGEST.TRIMMED_MEAN t 0.3 0.9\n\"6.5\"\nredis> TDIGEST.TRIMMED_MEAN t 0 1\n\"5.5\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n* [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) as a floating-point estimation of the mean value.\n* `nan` if the sketch is empty.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: the given key does not exist or is of an incorrect type, quantiles out of range [0..1], or incorrect number of arguments.\n\n**RESP3:**\n\nOne of the following:\n\n* [Double reply](https://redis.io/docs/latest/develop/reference/protocol-spec#doubles) as an estimation of the mean value.\n* `nan` if the sketch is empty.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: the given key does not exist or is of an incorrect type, quantiles out of range [0..1], or incorrect number of arguments."}],"examples":[]}
{"schema_version":2,"id":"commands/time","title":"TIME","url":"https://redis.io/docs/latest/commands/time/","summary":"Returns the server time.","since":"2.6.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"b504e556102b5f3971e6365f02baad09039c1ee1f00d2f6dbeb51d03bef4a59a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `TIME` command returns the current server time as a two items lists: a Unix\ntimestamp and the amount of microseconds already elapsed in the current second.\nBasically the interface is very similar to the one of the `gettimeofday` system\ncall."},{"id":"examples","title":"Examples","role":"example","text":"redis> TIME\n1) \"1784722081\"\n2) \"827551\"\nredis> TIME\n1) \"1784722081\"\n2) \"828843\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): specifically, a two-element array consisting of the Unix timestamp in seconds and the microseconds' count.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): specifically, a two-element array consisting of the Unix timestamp in seconds and the microseconds' count."}],"examples":[]}
{"schema_version":2,"id":"commands/topk.add","title":"TOPK.ADD","url":"https://redis.io/docs/latest/commands/topk.add/","summary":"Adds an item to a Top-k sketch. Multiple items can be added at the same time.","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"c774f5a7a26ce131d0f666455a96138e3b4c2a6a534404e4f0f0f84f0f721296","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Adds an item to a Top-k sketch. \nMultiple items can be added at the same time.\nIf an item enters the Top-K sketch, the item that is expelled (if any) is returned.\nThis allows dynamic heavy-hitter detection of items being entered or expelled from Top-K sketch."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nthe name of the sketch where items are added.\n\n</details>\n\n<details open><summary><code>item [item ...]</code></summary>\n\nthe items to be added.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\"><nobr>&#x2705; Flexible & Annual</nobr></span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n* [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [bulk string replies](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) containing either dropped elements or [nil (null bulk string)](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings).\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: incorrect number of arguments or non-existant key.\n\n**RESP3:**\n\nOne of the following:\n\n* [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [bulk string replies](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) containing either dropped elements or [null](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls).\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: incorrect number of arguments, non-existant key, or key of the incorrect type."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> TOPK.ADD topk foo bar 42\n1) (nil)\n2) baz\n3) (nil)","section_id":"examples"}]}
{"schema_version":2,"id":"commands/topk.count","title":"TOPK.COUNT","url":"https://redis.io/docs/latest/commands/topk.count/","summary":"Return the count for one or more items in a sketch","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"00f9069d6e919fdec2761ddce2278c852faf4f2ad6351e99c18ec2f9aaab3e8b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns counts for each item present in the sketch. \nMultiple items can be requested at once.\nPlease note this number will never be higher than the real count and will likely be lower.\n\nThis command has been deprecated. The count value is not a representative of\nthe number of appearances of an item."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nthe name of the sketch where items are to be counted.\n\n</details>\n\n<details open><summary><code>item [item ...]</code></summary>\n\nthe items to be counted.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n* [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [integer replies](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) representing the count of each specified item. For non-existant items, `0` is returned.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: incorrect number of arguments, non-existant key, or key of the incorrect type.\n\n**RESP3:**\n\nOne of the following:\n\n* [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [integer replies](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) representing the count of each specified item. For non-existant items, `0` is returned.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: incorrect number of arguments, non-existant key, or key of the incorrect type."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> TOPK.COUNT topk foo 42 nonexist\n1) (integer) 3\n2) (integer) 1\n3) (integer) 0","section_id":"examples"}]}
{"schema_version":2,"id":"commands/topk.incrby","title":"TOPK.INCRBY","url":"https://redis.io/docs/latest/commands/topk.incrby/","summary":"Increases the count of one or more items by increment","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"c1f8425b4c63ce4af4eeb857c89e26f0d6c6247eef05f5f0c055e5243313810d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Increase the score of an item in the data structure by `increment`. \nMultiple items' scores can be increased at once.\nIf an item enters the Top-K list, the item that is expelled (if any) is returned."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nthe name of the sketch.\n\n</details>\n\n<details open><summary><code>item increment [item increment ...]</code></summary>\n\nOne or more item-increment pairs. Each `item` is incremented by its corresponding `increment`. The `increment` must be greater than or equal to 1 and less than or equal to 100,000 to avoid server freeze.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n* [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [bulk string replies](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) containing either dropped elements or [nil (null bulk string)](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings).\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: incorrect number of arguments, non-existant key, key of the incorrect type, or an incorrect increment (less than 0 or greater than 100,000).\n\n**RESP3:**\n\nOne of the following:\n\n* [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [bulk string replies](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) containing either dropped elements or [null](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls).\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: incorrect number of arguments, non-existant key, key of the incorrect type, or an incorrect increment (less than 0 or greater than 100,000)."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> TOPK.INCRBY topk foo 3 bar 2 42 30\n1) (nil)\n2) (nil)\n3) foo","section_id":"examples"}]}
{"schema_version":2,"id":"commands/topk.info","title":"TOPK.INFO","url":"https://redis.io/docs/latest/commands/topk.info/","summary":"Returns information about a sketch","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"6f7437b18ff601afcdde12bd030c7e0f45858039f240ff6ca02a596168513df7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns number of required items (k), width, depth, and decay values of a given sketch."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nthe name of the sketch.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [simple string](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) and [integer](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) pairs. For decay, a [simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) is used to represent the floating point value.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: incorrect number of arguments, non-existant key, or key of the incorrect type.\n\n**RESP3:**\n\nOne of the following:\n\n* [A map](https://redis.io/docs/latest/develop/reference/protocol-spec#maps) of [simple string](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) and [integer](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) pairs. For decay, a [double reply](https://redis.io/docs/latest/develop/reference/protocol-spec#doubles) is used to represent the floating point value.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: incorrect number of arguments, non-existant key, or key of the incorrect type."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"TOPK.INFO topk\n1) k\n2) (integer) 50\n3) width\n4) (integer) 2000\n5) depth\n6) (integer) 7\n7) decay\n8) \"0.92500000000000004\"","section_id":"examples"}]}
{"schema_version":2,"id":"commands/topk.list","title":"TOPK.LIST","url":"https://redis.io/docs/latest/commands/topk.list/","summary":"Return the full list of items in the Top-K sketch","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"41b89b5903c744056d5b70d5e76da778fe93ac1b8af2475e6314284b523f7ed6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Return the full list of items in Top-K sketch."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nthe name of the sketch.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>WITHCOUNT</code></summary>\n\nthe count of each element is also returned.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]\n\n[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"k (or less) items in the given Top-k sketch. The list is sorted by decreased count estimation.\n\n**RESP2:**\n\nOne of the following:\n\n* [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [bulk string replies](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) representing the names of items in the given sketch. If `WITHCOUNT` is requested, an [array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) and \n[integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) pairs, representing the names of the items in the sketch together with their counts.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, non-existant key, or key of the incorrect type.\n\n**RESP3:**\n\nOne of the following:\n\n* [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [bulk string replies](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) representing the names of items in the given sketch. If `WITHCOUNT` is requested, an [array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) and \n[integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) pairs, representing the names of the items in the sketch together with their counts.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, non-existant key, or key of the incorrect type."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"TOPK.LIST topk\n1) foo\n2) 42\n3) bar","section_id":"examples"},{"id":"examples-ex1","language":"plaintext","code":"TOPK.LIST topk WITHCOUNT\n1) foo\n2) (integer) 12\n3) 42\n4) (integer) 7\n5) bar\n6) (integer) 2","section_id":"examples"}]}
{"schema_version":2,"id":"commands/topk.query","title":"TOPK.QUERY","url":"https://redis.io/docs/latest/commands/topk.query/","summary":"Checks whether one or more items are in a sketch","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"a9026d375d0d74e6df2957aa72797b38e769dcbd6cceb613d1e1d36f40bcd633","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Checks whether one or more items are one of the Top-K items."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nthe name of the sketch.\n\n</details>\n\n<details open><summary><code>item [item ...]</code></summary>\n\nthe items to be queried.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n* [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [integer replies](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): `1` if an item is in the Top-K or `0` otherwise.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: non-existant key or key of the incorrect type.\n\n**RESP3:**\n\n* [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [boolean replies](https://redis.io/docs/latest/develop/reference/protocol-spec#booleans): `true` if an item is in the Top-K or `false` otherwise.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: non-existant key or key of the incorrect type."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> TOPK.QUERY topk 42 nonexist\n1) (integer) 1\n2) (integer) 0","section_id":"examples"}]}
{"schema_version":2,"id":"commands/topk.reserve","title":"TOPK.RESERVE","url":"https://redis.io/docs/latest/commands/topk.reserve/","summary":"Initializes a Top-K sketch with specified parameters","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"fd9bca5d41c3420fca75ab309f9b8b3f0287491afc1caeedadd0a2f17f0919a7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Initializes a Top-K sketch with specified parameters."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nthe name of the Top-k sketch.\n\n</details>\n\n<details open><summary><code>topk</code></summary>\n\nthe number of top (k) occurring items to keep.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>width</code></summary>\n\nNumber of counters kept in each array. (Default 8)\n\n</details>\n\n<details open><summary><code>depth</code></summary>\n\nNumber of arrays. (Default 7)\n\n</details>\n\n<details open><summary><code>decay</code></summary>\n\nThe probability of reducing a counter in an occupied bucket (decay ^ bucket[i].counter). As the counter gets higher, the likelihood of a reduction is lower. (Default 0.9)\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: incorrect number of arguments, invalid decay value, or key already exists.\n\n**RESP3:**\n\nOne of the following:\n\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) `OK` if executed correctly.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: incorrect number of arguments, invalid decay value, or key already exists."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> TOPK.RESERVE topk 50 2000 7 0.925\nOK","section_id":"examples"}]}
{"schema_version":2,"id":"commands/touch","title":"TOUCH","url":"https://redis.io/docs/latest/commands/touch/","summary":"Returns the number of existing keys out of those specified after updating the time they were last accessed.","since":"3.2.1","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"81412a126c9ca264c4bd960db84f9c5a21e0da1fd1f14b7b44ed41b6766034c8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nAlters the last access time of one or more keys.\nA key is ignored if it does not exist."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key [key ...]</code></summary>\n\nOne or more keys whose last-access time to update.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SET key1 \"Hello\"\nOK\nredis> SET key2 \"World\"\nOK\nredis> TOUCH key1 key2\n(integer) 2"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of touched keys.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of touched keys."}],"examples":[]}
{"schema_version":2,"id":"commands/ts.add","title":"TS.ADD","url":"https://redis.io/docs/latest/commands/ts.add/","summary":"Append a sample to a time series","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"0a89ebf127de84624a2e7e7e87fc98b4462d4e5a461b87308e8afb3c6afaf197","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Append a sample to a time series\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary> \n\nis key name for the time series.\n</details>\n\n<details open><summary><code>timestamp</code></summary> \n\nis Unix time (integer, in milliseconds) specifying the sample timestamp or `*` to set the sample timestamp to the Unix time of the server's clock.\n\nUnix time is the number of milliseconds that have elapsed since 00:00:00 UTC on 1 January 1970, the Unix epoch, without adjustments made due to leap seconds.\n</details>\n\n<details open><summary><code>value</code></summary> \n\nis (double) numeric data value of the sample. The double number should follow [RFC 7159](https://tools.ietf.org/html/rfc7159) (JSON standard). In particular, the parser rejects overly large values that do not fit in binary64. It does not accept infinite values. NaN (Not a Number) values are supported starting from Redis 8.6.\n</details>\n\n<note><b>Notes:</b>\n- When specified key does not exist, a new time series is created.\n\n  if a [COMPACTION_POLICY](https://redis.io/docs/latest/develop/data-types/timeseries/configuration#compaction_policy) configuration parameter is defined, compacted time series would be created as well.\n\n- If `timestamp` is older than the retention period compared to the maximum existing timestamp, the sample is discarded and an error is returned.\n- When adding a sample to a time series for which compaction rules are defined:\n  - If all the original samples for an affected aggregated time bucket are available, the compacted value is recalculated based on the reported sample and the original samples.\n  - If only a part of the original samples for an affected aggregated time bucket is available due to trimming caused in accordance with the time series RETENTION policy, the compacted value is recalculated based on the reported sample and the available original samples.\n  - If the original samples for an affected aggregated time bucket are not available due to trimming caused in accordance with the time series RETENTION policy, the compacted value bucket is not updated.\n- Explicitly adding samples to a compacted time series (using `TS.ADD`, [`TS.MADD`](https://redis.io/docs/latest/commands/ts.madd), [`TS.INCRBY`](https://redis.io/docs/latest/commands/ts.incrby), or [`TS.DECRBY`](https://redis.io/docs/latest/commands/ts.decrby)) may result in inconsistencies between the raw and the compacted data. The compaction process may override such samples.\n</note>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"The following arguments are optional because they can be set by [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create).\n\n<details open><summary><code>RETENTION retentionPeriod</code></summary> \n \nis maximum retention period, compared to the maximum existing timestamp, in milliseconds.\n\nUse it only if you are creating a new time series. It is ignored if you are adding samples to an existing time series. See `RETENTION` in [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create).\n</details>\n    \n<details open><summary><code>ENCODING enc</code></summary> \n\nspecifies the series sample encoding format.\n\nUse it only if you are creating a new time series. It is ignored if you are adding samples to an existing time series. See `ENCODING` in [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create).\n</details>\n\n<details open><summary><code>CHUNK_SIZE size</code></summary>\n  \nis memory size, in bytes, allocated for each data chunk.\n\nUse it only if you are creating a new time series. It is ignored if you are adding samples to an existing time series. See `CHUNK_SIZE` in [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create).\n</details>\n\n<details open><summary><code>DUPLICATE_POLICY policy</code></summary>\n\nis policy for handling insertion ([`TS.ADD`](https://redis.io/docs/latest/commands/ts.add) and [`TS.MADD`](https://redis.io/docs/latest/commands/ts.madd)) of multiple samples with identical timestamps.\n\nUse it only if you are creating a new time series. It is ignored if you are adding samples to an existing time series. See `DUPLICATE_POLICY` in [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create).\n</details>\n\n<details open><summary><code>ON_DUPLICATE policy_ovr</code></summary> \n\nis overwrite key and database configuration for [DUPLICATE_POLICY](https://redis.io/docs/latest/develop/data-types/timeseries/configuration#duplicate_policy), the policy for handling samples with identical timestamps.\nThis override is effective only for this single command and does not set the time series duplication policy (which can be set with [`TS.ALTER`](https://redis.io/docs/latest/commands/ts.alter)).\n\n`policy_ovr` can be one of the following values:\n  - `BLOCK`: ignore any newly reported value and reply with an error\n  - `FIRST`: ignore any newly reported value\n  - `LAST`: override with the newly reported value\n  - `MIN`: only override if the value is lower than the existing value\n  - `MAX`: only override if the value is higher than the existing value\n  - `SUM`: If a previous sample exists, add the new sample to it so that the updated value is set to (previous + new). If no previous sample exists, the value is set to the new value.\n\n<note><b>NaN Handling (Redis 8.6+):</b> When using `MIN`, `MAX`, or `SUM` policies, an error is returned if there is an existing value in the specified timestamp and either the previous or the new value (but not both) are NaN.</note>\n\nThis argument has no effect when a new time series is created by this command.\n</details>\n\n<details open><summary><code>IGNORE ignoreMaxTimediff ignoreMaxValDiff</code></summary> \n\nis the policy for handling duplicate samples. A new sample is considered a duplicate and is ignored if the following conditions are met:\n\n  - The time series is not a compaction;\n  - The time series' `DUPLICATE_POLICY` IS `LAST`;\n  - The sample is added in-order (`timestamp ≥ max_timestamp`);\n  - The difference of the current timestamp from the previous timestamp (`timestamp - max_timestamp`) is less than or equal to `IGNORE_MAX_TIME_DIFF`;\n  - The absolute value difference of the current value from the value at the previous maximum timestamp (`abs(value - value_at_max_timestamp`) is less than or equal to `IGNORE_MAX_VAL_DIFF`.\n \nwhere `max_timestamp` is the timestamp of the sample with the largest timestamp in the time series, and `value_at_max_timestamp` is the value at `max_timestamp`.\n\nWhen not specified: set to the global [IGNORE_MAX_TIME_DIFF](https://redis.io/docs/latest/develop/data-types/timeseries/configuration#ignore_max_time_diff-and-ignore_max_val_diff) and [IGNORE_MAX_VAL_DIFF](https://redis.io/docs/latest/develop/data-types/timeseries/configuration#ignore_max_time_diff-and-ignore_max_val_diff), which are, by default, both set to 0.\n\nThese parameters are used when creating a new time series to set the per-key parameters, and are ignored when called with an existing time series (the existing per-key configuration parameters is used).\n\n<note><b>NaN Handling (Redis 8.6+):</b> NaN values are never regarded as duplicates when using the `IGNORE` parameters.</note>\n\n</details>\n\n<details open><summary><code>LABELS {label value}...</code></summary> \n\nis set of label-value pairs that represent metadata labels of the time series.\n\nUse it only if you are creating a new time series. It is ignored if you are adding samples to an existing time series. See `LABELS` in [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create).\n</details>\n\n<note><b>Notes:</b>\n- You can use this command to create a new time series and add data to it in a single command.\n  `RETENTION`, `ENCODING`, `CHUNK_SIZE`, `DUPLICATE_POLICY`, `IGNORE`, and `LABELS` are used only when creating a new time series, and ignored when adding samples to an existing time series.\n- Setting `RETENTION` and `LABELS` introduces additional time complexity.\n</note>"},{"id":"complexity","title":"Complexity","role":"performance","text":"If a compaction rule exists on a time series, the performance of `TS.ADD` can be reduced.\nThe complexity of `TS.ADD` is always `O(M)`, where `M` is the number of compaction rules or `O(1)` with no compaction."},{"id":"examples","title":"Examples","role":"example","text":"<details open><summary><b>Append a sample to a temperature time series</b></summary>\n\nCreate a temperature time series, set its retention to 1 year, and append a sample.\n\n\n127.0.0.1:6379> TS.ADD temperature:3:11 1548149183000 27 RETENTION 31536000000\n(integer) 1548149183000\n\n\n<note><b>Note:</b> If a time series with such a name already exists, the sample is added, but the retention does not change.</note>\n\nAdd a sample to the time series, setting the sample's timestamp to the current Unix time of the server's clock.\n\n\n127.0.0.1:6379> TS.ADD temperature:3:11 * 30\n(integer) 1662042954573\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\"><nobr>&#x2705; Flexible & Annual</nobr></span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): the timestamp of the upserted sample. If the sample is ignored (see `IGNORE` in [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create)), the reply will be the largest timestamp in the time series.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, wrong key type, duplication policy is `BLOCK`, or when `timestamp` is older than the retention period compared to the maximum existing timestamp.\n\n**RESP3:**\n\nOne of the following:\n* [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): the timestamp of the upserted sample. If the sample is ignored (see `IGNORE` in [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create)), the reply will be the largest timestamp in the time series.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, wrong key type, duplication policy is `BLOCK`, or when `timestamp` is older than the retention period compared to the maximum existing timestamp."},{"id":"see-also","title":"See also","role":"related","text":"[`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create)"},{"id":"related-topics","title":"Related topics","role":"related","text":"[RedisTimeSeries](https://redis.io/docs/latest/develop/data-types/timeseries)"}],"examples":[]}
{"schema_version":2,"id":"commands/ts.alter","title":"TS.ALTER","url":"https://redis.io/docs/latest/commands/ts.alter/","summary":"Update the retention, chunk size, duplicate policy, and labels of an existing time series","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"5728008724e46460837dcc9ffbffb07ff52e83f254ec1dab501b6712688fc4aa","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Update the retention, chunk size, duplicate policy, and labels of an existing time series\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary> \n\nis key name for the time series.\n</details>\n\n<note><b>Note:</b> This command alters only the specified element. For example, if you specify only `RETENTION` and `LABELS`, the chunk size and the duplicate policy are not altered. </note>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>RETENTION retentionPeriod</code></summary>\n\nis maximum retention period, compared to the maximum existing timestamp, in milliseconds. See `RETENTION` in [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create).\n</details>\n\n<details open><summary><code>CHUNK_SIZE size</code></summary> \n\nis the initial allocation size, in bytes, for the data part of each new chunk. Actual chunks may consume more memory. See `CHUNK_SIZE` in [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create). Changing this value does not affect existing chunks.\n</details>\n\n<details open><summary><code>DUPLICATE_POLICY policy</code></summary> \n\nis policy for handling multiple samples with identical timestamps. See `DUPLICATE_POLICY` in [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create).\n</details>\n\n<details open><summary><code>LABELS [{label value}...]</code></summary> \n\nis set of label-value pairs that represent metadata labels of the key and serve as a secondary index.\n\nIf `LABELS` is specified, the given label list is applied. Labels that are not present in the given list are removed implicitly. Specifying `LABELS` with no label-value pairs removes all existing labels. See `LABELS` in [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create).\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open><summary><b>Alter a temperature time series</b></summary>\n\nCreate a temperature time series.\n\n\n127.0.0.1:6379> TS.CREATE temperature:2:32 RETENTION 60000 DUPLICATE_POLICY MAX LABELS sensor_id 2 area_id 32\nOK\n\n\nAlter the labels in the time series.\n\n\n127.0.0.1:6379> TS.ALTER temperature:2:32 LABELS sensor_id 2 area_id 32 sub_area_id 15\nOK\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` when the time series is altered successfully.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, wrong key type, key does not exist, etc.\n\n**RESP3:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` when the time series is altered successfully.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, wrong key type, key does not exist, etc."},{"id":"see-also","title":"See also","role":"related","text":"[`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create)"},{"id":"related-topics","title":"Related topics","role":"related","text":"[RedisTimeSeries](https://redis.io/docs/latest/develop/data-types/timeseries)"}],"examples":[]}
{"schema_version":2,"id":"commands/ts.create","title":"TS.CREATE","url":"https://redis.io/docs/latest/commands/ts.create/","summary":"Create a new time series","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"be3f577b4bdb555fd88c1c2c754b12895fe4a93862bb38d63fa6b2200dcbdade","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Create a new time series\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary> \n\nis key name for the time series.\n</details>\n\n<note><b>Notes:</b>\n\n- If a key already exists, you get a Redis error reply, `TSDB: key already exists`. You can check for the existence of a key with the [`EXISTS`](https://redis.io/docs/latest/commands/exists) command.\n- Other commands that also create a new time series when called with a key that does not exist are [`TS.ADD`](https://redis.io/docs/latest/commands/ts.add), [`TS.INCRBY`](https://redis.io/docs/latest/commands/ts.incrby), and [`TS.DECRBY`](https://redis.io/docs/latest/commands/ts.decrby).\n</note>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>RETENTION retentionPeriod</code></summary> \n\nis maximum age for samples compared to the highest reported timestamp, in milliseconds. Samples are expired based solely on the difference between their timestamp and the timestamps passed to subsequent [`TS.ADD`](https://redis.io/docs/latest/commands/ts.add), [`TS.MADD`](https://redis.io/docs/latest/commands/ts.madd), [`TS.INCRBY`](https://redis.io/docs/latest/commands/ts.incrby), and [`TS.DECRBY`](https://redis.io/docs/latest/commands/ts.decrby) calls with this key.\n\nWhen set to 0, samples never expire. When not specified, the option is set to the global [RETENTION_POLICY](https://redis.io/docs/latest/develop/data-types/timeseries/configuration#retention_policy) configuration of the database, which by default is 0.\n</details>\n\n<details open><summary><code>ENCODING enc</code></summary> \n\nspecifies the series samples encoding format as one of the following values:\n - `COMPRESSED`, applies compression to the series samples.\n - `UNCOMPRESSED`, keeps the raw samples in memory. Adding this flag keeps data in an uncompressed form. \n\n`COMPRESSED` is almost always the right choice. Compression not only saves memory but usually improves performance due to a lower number of memory accesses. It can result in about 90% memory reduction. The exception are highly irregular timestamps or values, which occur rarely.\n\nWhen not specified, the option is set to `COMPRESSED`.\n</details>\n\n<details open><summary><code>CHUNK_SIZE size</code></summary> \n\nis initial allocation size, in bytes, for the data part of each new chunk. Actual chunks may consume more memory. Changing chunkSize (using [`TS.ALTER`](https://redis.io/docs/latest/commands/ts.alter)) does not affect existing chunks.\n\nMust be a multiple of 8 in the range [48 .. 1048576]. When not specified, it is set to the global [CHUNK_SIZE_BYTES](https://redis.io/docs/latest/develop/data-types/timeseries/configuration#chunk_size_bytes) configuration of the database, which by default is 4096 (a single memory page).\n\nNote: Before v1.6.10 no minimum was enforced. Between v1.6.10 and v1.6.17 and in v1.8.0 the minimum value was 128. Since v1.8.1 the minimum value is 48.\n\nThe data in each key is stored in chunks. Each chunk contains header and data for a given timeframe. An index contains all chunks. Iterations occur inside each chunk. Depending on your use case, consider these tradeoffs for having smaller or larger sizes of chunks:\n\n  - Insert performance: Smaller chunks result in slower inserts (more chunks need to be created).\n  - Query performance: Queries for a small subset when the chunks are very large are slower, as we need to iterate over the chunk to find the data.\n  - Larger chunks may take more memory when you have a very large number of keys and very few samples per key, or less memory when you have many samples per key.\n\n If you are unsure about your use case, select the default.\n</details>\n\n<details open><summary><code>DUPLICATE_POLICY policy</code></summary> \n\nis policy for handling insertion ([`TS.ADD`](https://redis.io/docs/latest/commands/ts.add) and [`TS.MADD`](https://redis.io/docs/latest/commands/ts.madd)) of multiple samples with identical timestamps, with one of the following values:\n  - `BLOCK`: ignore any newly reported value and reply with an error\n  - `FIRST`: ignore any newly reported value\n  - `LAST`: override with the newly reported value\n  - `MIN`: only override if the value is lower than the existing value\n  - `MAX`: only override if the value is higher than the existing value\n  - `SUM`: If a previous sample exists, add the new sample to it so that the updated value is equal to (previous + new). If no previous sample exists, set the updated value equal to the new value.\n\n  When not specified: set to the global [DUPLICATE_POLICY](https://redis.io/docs/latest/develop/data-types/timeseries/configuration#duplicate_policy) configuration of the database (which, by default, is `BLOCK`).\n\n`BLOCK` is often used to avoid accidental changes. `FIRST` can be used as an optimization when duplicate reports are possible. `LAST` can be used when updates are being reported. `SUM` is used for counters (e.g., the number of cars entering a parking lot per minute when there are multiple reporting counting devices). `MIN` and `MAX` can be used, for example, to store the minimal/maximal stock price per minute (instead of storing all the samples and defining a compaction rule).\n\n</details>\n\n<details open><summary><code>IGNORE ignoreMaxTimediff ignoreMaxValDiff</code></summary> \n\nis the policy for handling duplicate samples. A new sample is considered a duplicate and is ignored if the following conditions are met:\n\n  - The time series is not a compaction;\n  - The time series' `DUPLICATE_POLICY` IS `LAST`;\n  - The sample is added in-order (`timestamp ≥ max_timestamp`);\n  - The difference of the current timestamp from the previous timestamp (`timestamp - max_timestamp`) is less than or equal to `IGNORE_MAX_TIME_DIFF`;\n  - The absolute value difference of the current value from the value at the previous maximum timestamp (`abs(value - value_at_max_timestamp`) is less than or equal to `IGNORE_MAX_VAL_DIFF`.\n \nwhere `max_timestamp` is the timestamp of the sample with the largest timestamp in the time series, and `value_at_max_timestamp` is the value at `max_timestamp`.\n\nWhen not specified: set to the global [IGNORE_MAX_TIME_DIFF](https://redis.io/docs/latest/develop/data-types/timeseries/configuration#ignore_max_time_diff-and-ignore_max_val_diff) and [IGNORE_MAX_VAL_DIFF](https://redis.io/docs/latest/develop/data-types/timeseries/configuration#ignore_max_time_diff-and-ignore_max_val_diff), which are, by default, both set to 0.\n</details>\n\n<details open><summary><code>LABELS {label value}...</code></summary> \n\nis set of label-value pairs that represent metadata labels of the key and serve as a secondary index.\n\nThe [`TS.MGET`](https://redis.io/docs/latest/commands/ts.mget), [`TS.MRANGE`](https://redis.io/docs/latest/commands/ts.mrange), and [`TS.MREVRANGE`](https://redis.io/docs/latest/commands/ts.mrevrange) commands operate on multiple time series based on their labels. The [`TS.QUERYINDEX`](https://redis.io/docs/latest/commands/ts.queryindex) command returns all time series keys matching a given filter based on their labels.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open><summary><b>Create a temperature time series</b></summary>\n\n\n127.0.0.1:6379> TS.CREATE temperature:2:32 RETENTION 60000 DUPLICATE_POLICY MAX LABELS sensor_id 2 area_id 32\nOK\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` when the time series is created successfully.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, key already exists, etc.\n\n**RESP3:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` when the time series is created successfully.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, key already exists, etc."},{"id":"see-also","title":"See also","role":"related","text":"[`TS.ADD`](https://redis.io/docs/latest/commands/ts.add) | [`TS.INCRBY`](https://redis.io/docs/latest/commands/ts.incrby) | [`TS.DECRBY`](https://redis.io/docs/latest/commands/ts.decrby) | [`TS.MGET`](https://redis.io/docs/latest/commands/ts.mget) | [`TS.MRANGE`](https://redis.io/docs/latest/commands/ts.mrange) | [`TS.MREVRANGE`](https://redis.io/docs/latest/commands/ts.mrevrange) | [`TS.QUERYINDEX`](https://redis.io/docs/latest/commands/ts.queryindex)"},{"id":"related-topics","title":"Related topics","role":"related","text":"- [RedisTimeSeries](https://redis.io/docs/latest/develop/data-types/timeseries)\n- [RedisTimeSeries Version 1.2 Is Here!](https://redis.com/blog/redistimeseries-version-1-2-is-here/)"}],"examples":[]}
{"schema_version":2,"id":"commands/ts.createrule","title":"TS.CREATERULE","url":"https://redis.io/docs/latest/commands/ts.createrule/","summary":"Create a compaction rule.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"b04280b17be8fef179a02e45b811e729f4b9d9a6d22a37a21869ffa4de484a97","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\n\nCreate a compaction rule\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>sourceKey</code></summary>\n\nis key name for the source time series.\n</details>\n\n<details open><summary><code>destKey</code></summary> \n\nis key name for destination (compacted) time series. It must be created before `TS.CREATERULE` is called. \n</details>\n\n<details open><summary><code>AGGREGATION aggregator bucketDuration</code></summary> \n\naggregates results into time buckets.\n\n  - `aggregator` takes one of the following aggregation types:\n\n    | `aggregator` | Description                                                     |\n    | ------------ | --------------------------------------------------------------- |\n    | `avg`        | Arithmetic mean of all non-NaN values                           |\n    | `sum`        | Sum of all non-NaN values                                       |\n    | `min`        | Minimum non-NaN value                                           |\n    | `max`        | Maximum non-NaN value                                           |\n    | `range`      | Difference between the maximum and the minimum non-NaN values   |\n    | `count`      | Number of non-NaN values                                        |\n    | `countNaN`   | Number of NaN values (since Redis 8.6)                          |\n    | `countAll`   | Number of values, including NaN and non-NaN (since Redis 8.6)   |\n    | `first`      | The non-NaN value with the lowest timestamp in the bucket       |\n    | `last`       | The non-NaN value with the highest timestamp in the bucket      |\n    | `std.p`      | Population standard deviation of the non-NaN values             |\n    | `std.s`      | Sample standard deviation of the non-NaN values                 |\n    | `var.p`      | Population variance of the non-NaN values                       |\n    | `var.s`      | Sample variance of the non-NaN values                           |\n    | `twa`        | Time-weighted average over the bucket's timeframe (ignores NaN values) (since RedisTimeSeries 1.8) |\n\n  - `bucketDuration` is duration of each bucket, in milliseconds.\n  \n<note><b>Notes</b>\n\n- Only new samples that are added into the source series after the creation of the rule will be aggregated.\n- Calling `TS.CREATERULE` with a nonempty `destKey` may result in inconsistencies between the raw and the compacted data.\n- Explicitly adding samples to a compacted time series (using [`TS.ADD`](https://redis.io/docs/latest/commands/ts.add), [`TS.MADD`](https://redis.io/docs/latest/commands/ts.madd), [`TS.INCRBY`](https://redis.io/docs/latest/commands/ts.incrby), or [`TS.DECRBY`](https://redis.io/docs/latest/commands/ts.decrby)) may result in inconsistencies between the raw and the compacted data. The compaction process may override such samples.\n- If no samples are added to the source time series during a bucket period. no _compacted sample_ is added to the destination time series.\n- The timestamp of a compacted sample added to the destination time series is set to the start timestamp the appropriate compaction bucket. For example, for a 10-minute compaction bucket with no alignment, the compacted samples timestamps are `x:00`, `x:10`, `x:20`, and so on.\n- Deleting `destKey` will cause the compaction rule to be deleted as well.\n\n> [!WARNING]\n> In a clustered environment, you must use [hash tags](https://redis.io/docs/latest/operate/oss_and_stack/reference/cluster-spec#hash-tags) to force `sourceKey` and `destKey` to be stored in the same hash slot. If you don't, Redis may fail to compact the data without displaying any error messages.\n  \n</note>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>alignTimestamp</code> (since RedisTimeSeries 1.8)</summary>\n\nensures that there is a bucket that starts exactly at `alignTimestamp` and aligns all other buckets accordingly. It is expressed in milliseconds. The default value is 0: aligned with the Unix epoch.\n\nFor example, if `bucketDuration` is 24 hours (`24 * 3600 * 1000`), setting `alignTimestamp` to 6 hours after the Unix epoch (`6 * 3600 * 1000`) ensures that each bucket’s timeframe is `[06:00 .. 06:00)`.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Create a compaction rule</b></summary>\n\nCreate a time series to store the temperatures measured in Tel Aviv.\n\n\n127.0.0.1:6379> TS.CREATE temp:{TLV} LABELS type temp location TLV\nOK\n\n\nNext, create a compacted time series named _dailyAvgTemp_ containing one compacted sample per 24 hours: the time-weighted average of all measurements taken from midnight to next midnight.\n\n\n127.0.0.1:6379> TS.CREATE dailyAvgTemp:{TLV} LABELS type temp location TLV\n127.0.0.1:6379> TS.CREATERULE temp:{TLV} dailyAvgTemp:{TLV} AGGREGATION twa 86400000 \n\n\nNow, also create a compacted time series named _dailyDiffTemp_. This time series will contain one compacted sample per 24 hours: the difference between the minimum and the maximum temperature measured between 06:00 and 06:00 next day.\n Here, 86400000 is the number of milliseconds in 24 hours, 21600000 is the number of milliseconds in 6 hours.\n\n\n127.0.0.1:6379> TS.CREATE dailyDiffTemp:{TLV} LABELS type temp location TLV\n127.0.0.1:6379> TS.CREATERULE temp:{TLV} dailyDiffTemp:{TLV} AGGREGATION range 86400000 21600000\n\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` when the compaction rule is created successfully.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, wrong key type, `sourceKey` does not exist, `destKey` does not exist, `sourceKey` is already a destination of a compaction rule, `destKey` is already a source or a destination of a compaction rule, or `sourceKey` and `destKey` are identical.\n\n**RESP3:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` when the compaction rule is created successfully.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, wrong key type, `sourceKey` does not exist, `destKey` does not exist, `sourceKey` is already a destination of a compaction rule, `destKey` is already a source or a destination of a compaction rule, or `sourceKey` and `destKey` are identical."},{"id":"see-also","title":"See also","role":"related","text":"[`TS.DELETERULE`](https://redis.io/docs/latest/commands/ts.deleterule)"},{"id":"related-topics","title":"Related topics","role":"related","text":"[RedisTimeSeries](https://redis.io/docs/latest/develop/data-types/timeseries)"}],"examples":[]}
{"schema_version":2,"id":"commands/ts.decrby","title":"TS.DECRBY","url":"https://redis.io/docs/latest/commands/ts.decrby/","summary":"Decrease the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given decrement","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"f354d310c58ee6f7470c08aebc9eb6582b7c66c9d5d97e556b01bd12b22098a4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Decrease the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given decrement\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary> \n\nis key name for the time series.\n</details>\n\n<details open><summary><code>subtrahend</code></summary> \n\nis numeric value of the subtrahend (double). An error is returned if the subtrahend is NaN.\n</details>\n\n<note><b>Notes</b>\n- When specified key does not exist, a new time series is created.\n- You can use this command as a counter or gauge that automatically gets history as a time series.\n- If a policy for handling duplicate samples (`IGNORE`) is defined for this time series - `TS.DECRBY` operations are affected as well (sample additions/modifications can be filtered).\n- Explicitly adding samples to a compacted time series (using [`TS.ADD`](https://redis.io/docs/latest/commands/ts.add), [`TS.MADD`](https://redis.io/docs/latest/commands/ts.madd), [`TS.INCRBY`](https://redis.io/docs/latest/commands/ts.incrby), or `TS.DECRBY`) may result in inconsistencies between the raw and the compacted data. The compaction process may override such samples.\n</note>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>TIMESTAMP timestamp</code></summary> \n\nis Unix time (integer, in milliseconds) specifying the sample timestamp or `*` to set the sample timestamp to the Unix time of the server's clock.\n\nUnix time is the number of milliseconds that have elapsed since 00:00:00 UTC on 1 January 1970, the Unix epoch, without adjustments made due to leap seconds.\n\n`timestamp` must be equal to or higher than the maximum existing timestamp. When equal, the value of the sample with the maximum existing timestamp is decreased. If it is higher, a new sample with a timestamp set to `timestamp` is created, and its value is set to the value of the sample with the maximum existing timestamp minus `subtrahend`. \n\nIf the time series is empty, the value is set to `subtrahend`.\n\nWhen not specified, the timestamp is set to the Unix time of the server's clock.\n\n<note><b>NaN Handling (Redis 8.6+):</b> An error is returned if the current value at the maximum existing timestamp is NaN.</note>\n</details>\n\n<details open><summary><code>RETENTION retentionPeriod</code></summary> \n\nis maximum retention period, compared to the maximum existing timestamp, in milliseconds.\n\nUse it only if you are creating a new time series. It is ignored if you are adding samples to an existing time series. See `RETENTION` in [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create).\n</details>\n\n<details open><summary><code>ENCODING enc</code></summary> \n\nspecifies the series sample encoding format.\n\nUse it only if you are creating a new time series. It is ignored if you are adding samples to an existing time series. See `ENCODING` in [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create).\n</details>\n\n<details open><summary><code>CHUNK_SIZE size</code></summary> \n\nis memory size, in bytes, allocated for each data chunk.\n\nUse it only if you are creating a new time series. It is ignored if you are adding samples to an existing time series. See `CHUNK_SIZE` in [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create).\n</details>\n\n<details open><summary><code>DUPLICATE_POLICY policy</code></summary>\n\nis policy for handling insertion ([`TS.ADD`](https://redis.io/docs/latest/commands/ts.add) and [`TS.MADD`](https://redis.io/docs/latest/commands/ts.madd)) of multiple samples with identical timestamps.\n\nUse it only if you are creating a new time series. It is ignored if you are adding samples to an existing time series. See `DUPLICATE_POLICY` in [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create).\n</details>\n\n<details open><summary><code>IGNORE ignoreMaxTimediff ignoreMaxValDiff</code></summary> \n\nis the policy for handling duplicate samples. A new sample is considered a duplicate and is ignored if the following conditions are met:\n\n  - The time series is not a compaction;\n  - The time series' `DUPLICATE_POLICY` IS `LAST`;\n  - The sample is added in-order (`timestamp ≥ max_timestamp`);\n  - The difference of the current timestamp from the previous timestamp (`timestamp - max_timestamp`) is less than or equal to `IGNORE_MAX_TIME_DIFF`;\n  - The absolute value difference of the current value from the value at the previous maximum timestamp (`abs(value - value_at_max_timestamp`) is less than or equal to `IGNORE_MAX_VAL_DIFF`.\n\nwhere `max_timestamp` is the timestamp of the sample with the largest timestamp in the time series, and `value_at_max_timestamp` is the value at `max_timestamp`.\n\nWhen not specified: set to the global [IGNORE_MAX_TIME_DIFF](https://redis.io/docs/latest/develop/data-types/timeseries/configuration#ignore_max_time_diff-and-ignore_max_val_diff) and [IGNORE_MAX_VAL_DIFF](https://redis.io/docs/latest/develop/data-types/timeseries/configuration#ignore_max_time_diff-and-ignore_max_val_diff), which are, by default, both set to 0.\n\nThese parameters are used when creating a new time series to set the per-key parameters, and are ignored when called with an existing time series (the existing per-key configuration parameters are used).\n</details>\n\n<details open><summary><code>LABELS [{label value}...]</code></summary> \n\nis set of label-value pairs that represent metadata labels of the key and serve as a secondary index.\n\nUse it only if you are creating a new time series. It is ignored if you are adding samples to an existing time series. See `LABELS` in [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create).\n</details>\n\n<note><b>Notes</b>\n- You can use this command to create a new time series and add a sample to it in a single command.\n  `RETENTION`, `ENCODING`, `CHUNK_SIZE`, `DUPLICATE_POLICY`, `IGNORE`, and `LABELS` are used only when creating a new time series, and ignored when adding or modifying samples in an existing time series.\n- Setting `RETENTION` and `LABELS` introduces additional time complexity.\n</note>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): the timestamp of the upserted sample. If the sample is ignored (see `IGNORE` in [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create)), the reply will be the largest timestamp in the time series.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, wrong key type, or when `timestamp` is not equal to or higher than the maximum existing timestamp.\n\n**RESP3:**\n\nOne of the following:\n* [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): the timestamp of the upserted sample. If the sample is ignored (see `IGNORE` in [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create)), the reply will be the largest timestamp in the time series.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, wrong key type, or when `timestamp` is not equal to or higher than the maximum existing timestamp."},{"id":"see-also","title":"See also","role":"related","text":"[`TS.INCRBY`](https://redis.io/docs/latest/commands/ts.incrby) | [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create)"},{"id":"related-topics","title":"Related topics","role":"related","text":"[RedisTimeSeries](https://redis.io/docs/latest/develop/data-types/timeseries)"}],"examples":[]}
{"schema_version":2,"id":"commands/ts.del","title":"TS.DEL","url":"https://redis.io/docs/latest/commands/ts.del/","summary":"Delete all samples between two timestamps for a given time series","since":"1.6.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"59648ecee683ce2c028272cf8fdca0189b39714e39746a99a8f13bb1008401db","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Delete all samples between two timestamps for a given time series\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary> \n\nis key name for the time series.\n</details>\n\n<details open><summary><code>fromTimestamp</code></summary> \n\nis start timestamp for the range deletion.\n</details>\n\n<details open><summary><code>toTimestamp</code></summary>\n\nis end timestamp for the range deletion.\n\nThe given timestamp interval is closed (inclusive), meaning that samples whose timestamp eqauls the `fromTimestamp` or `toTimestamp` are also deleted.\n\n<note><b>Notes:</b>\n  \n- If fromTimestamp is older than the retention period compared to the maximum existing timestamp, the deletion is discarded and an error is returned.\n- When deleting a sample from a time series for which compaction rules are defined:\n  - If all the original samples for an affected compaction bucket are available, the compacted value is recalculated based on the remaining original samples, or removed if all original samples within the compaction bucket  were deleted.\n  - If original samples for an affected compaction bucket were expired, the deletion is discarded and an error is returned.\n- Explicitly deleting samples from a compacted time series may result in inconsistencies between the raw and the compacted data. The compaction process may override such samples. That being said, it is safe to explicitly delete samples from a compacted time series beyond the retention period of the original time series.\n\n</note>"},{"id":"examples","title":"Examples","role":"example","text":"<details open><summary><b>Delete range of data points</b></summary>\n\nCreate time series for temperature in Tel Aviv and Jerusalem, then add different temperature samples.\n\n\n127.0.0.1:6379> TS.CREATE temp:TLV LABELS type temp location TLV\nOK\n127.0.0.1:6379> TS.CREATE temp:JLM LABELS type temp location JLM\nOK\n127.0.0.1:6379> TS.MADD temp:TLV 1000 30 temp:TLV 1010 35 temp:TLV 1020 9999 temp:TLV 1030 40\n1) (integer) 1000\n2) (integer) 1010\n3) (integer) 1020\n4) (integer) 1030\n127.0.0.1:6379> TS.MADD temp:JLM 1005 30 temp:JLM 1015 35 temp:JLM 1025 9999 temp:JLM 1035 40\n1) (integer) 1005\n2) (integer) 1015\n3) (integer) 1025\n4) (integer) 1035\n\n\nDelete the range of data points for temperature in Tel Aviv.\n\n\n127.0.0.1:6379> TS.DEL temp:TLV 1000 1030\n(integer) 4\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): the number of samples that were deleted.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, wrong key type, `timestamp` is older than the retention period compared to the maximum existing timestamp, or when an affected compaction bucket cannot be recalculated.\n\n**RESP3:**\n\nOne of the following:\n* [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): the number of samples that were deleted.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, wrong key type, `timestamp` is older than the retention period compared to the maximum existing timestamp, or when an affected compaction bucket cannot be recalculated."},{"id":"see-also","title":"See also","role":"related","text":"[`TS.ADD`](https://redis.io/docs/latest/commands/ts.add)"},{"id":"related-topics","title":"Related topics","role":"related","text":"[RedisTimeSeries](https://redis.io/docs/latest/develop/data-types/timeseries)"}],"examples":[]}
{"schema_version":2,"id":"commands/ts.deleterule","title":"TS.DELETERULE","url":"https://redis.io/docs/latest/commands/ts.deleterule/","summary":"Delete a compaction rule","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"1b929ebf9acca6c19a53d47d3285beb513d98c2250da4fbcabda92e621c66533","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\n\nDelete a compaction rule"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>sourceKey</code></summary>\n\nis key name for the source time series.\n</details>\n\n<details open><summary><code>destKey</code></summary> \n\nis key name for destination (compacted) time series.\n</details>\n\n<note><b>Note:</b> This command does not delete the compacted series.</note>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` when the compaction rule is deleted successfully.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, or when such rule does not exist.\n\n**RESP3:**\n\nOne of the following:\n* [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): `OK` when the compaction rule is deleted successfully.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, or when such rule does not exist."},{"id":"see-also","title":"See also","role":"related","text":"[`TS.CREATERULE`](https://redis.io/docs/latest/commands/ts.createrule)"},{"id":"related-topics","title":"Related topics","role":"related","text":"[RedisTimeSeries](https://redis.io/docs/latest/develop/data-types/timeseries)"}],"examples":[]}
{"schema_version":2,"id":"commands/ts.get","title":"TS.GET","url":"https://redis.io/docs/latest/commands/ts.get/","summary":"Get the sample with the highest timestamp from a given time series","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"5e3735eee67243be6cc42233b3c9df864852681c87c4a9d298d8a55f7bf577f9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Get the sample with the highest timestamp from a given time series. Starting from Redis 8.6, NaN values are included in the results.\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary> \n\nis key name for the time series.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>LATEST</code> (since RedisTimeSeries v1.8)</summary> \n\nis used when a time series is a compaction. With `LATEST`, TS.GET reports the compacted value of the latest (possibly partial) bucket. Without `LATEST`, TS.GET does not report the latest (possibly partial) bucket. When a time series is not a compaction, `LATEST` is ignored.\n  \nThe data in the latest bucket of a compaction is possibly partial. A bucket is _closed_ and compacted only upon arrival of a new sample that _opens_ a new _latest_ bucket. There are cases, however, when the compacted value of the latest (possibly partial) bucket is also required. In such a case, use `LATEST`.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Get latest measured temperature for a city</b></summary>\n\nCreate a time series to store the temperatures measured in Tel Aviv and add four measurements for Sun Jan 01 2023\n  \n\n127.0.0.1:6379> TS.CREATE temp:TLV LABELS type temp location TLV\nOK\n127.0.0.1:6379> TS.MADD temp:TLV 1672534800 12 temp:TLV 1672556400 16 temp:TLV 1672578000 21 temp:TLV 1672599600 14\n\n  \nNext, get the latest measured temperature (the temperature with the highest timestamp)\n\n\n127.0.0.1:6379> TS.GET temp:TLV\n1) (integer) 1672599600\n2) 14\n\n</details>\n\n<details open>\n<summary><b>Get latest maximal daily temperature for a city</b></summary>\n\nCreate a time series to store the temperatures measured in Jerusalem\n\n\n127.0.0.1:6379> TS.CREATE temp:JLM LABELS type temp location JLM\nOK\n\n\nNext, create a compacted time series named _dailyAvgTemp:JLM_ containing one compacted sample per 24 hours: the maximum of all measurements taken from midnight to next midnight.\n\n\n127.0.0.1:6379> TS.CREATE dailyMaxTemp:JLM LABELS type temp location JLM\nOK\n127.0.0.1:6379> TS.CREATERULE temp:JLM dailyMaxTemp:JLM AGGREGATION max 86400000\nOK\n\n\nAdd four measurements for Sun Jan 01 2023 and three measurements for Mon Jan 02 2023\n\n\n127.0.0.1:6379> TS.MADD temp:JLM 1672534800000 12 temp:JLM 1672556400000 16 temp:JLM 1672578000000 21 temp:JLM 1672599600000 14\n1) (integer) 1672534800000\n2) (integer) 1672556400000\n3) (integer) 1672578000000\n4) (integer) 1672599600000\n127.0.0.1:6379> TS.MADD temp:JLM 1672621200000 11 temp:JLM 1672642800000 21 temp:JLM 1672664400000 26\n1) (integer) 1672621200000\n2) (integer) 1672642800000\n3) (integer) 1672664400000\n\n  \nNext, get the latest maximum daily temperature; do not report the latest, possibly partial, bucket \n\n\n127.0.0.1:6379> TS.GET dailyMaxTemp:JLM\n1) (integer) 1672531200000\n2) 21\n\n\nGet the latest maximum daily temperature (the temperature with the highest timestamp); report the latest, possibly partial, bucket\n\n\n127.0.0.1:6379> TS.GET dailyMaxTemp:JLM LATEST\n1) (integer) 1672617600000\n2) 26\n\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of a single ([Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers), [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings)) pair representing (timestamp, value) of the sample with the highest timestamp.\n* An empty [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) when the time series is empty.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, wrong key type, key does not exist, etc.\n\n**RESP3:**\n\nOne of the following:\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of a single ([Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers), [Double reply](https://redis.io/docs/latest/develop/reference/protocol-spec#doubles)) pair representing (timestamp, value) of the sample with the highest timestamp.\n* An empty [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) when the time series is empty.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, wrong key type, key does not exist, etc."},{"id":"see-also","title":"See also","role":"related","text":"[`TS.MGET`](https://redis.io/docs/latest/commands/ts.mget)"},{"id":"related-topics","title":"Related topics","role":"related","text":"[RedisTimeSeries](https://redis.io/docs/latest/develop/data-types/timeseries)"}],"examples":[]}
{"schema_version":2,"id":"commands/ts.incrby","title":"TS.INCRBY","url":"https://redis.io/docs/latest/commands/ts.incrby/","summary":"Increase the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given increment","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"a0eb3cc59c7a8760f0ca9f99951dade3080177af4d775a8be035803b8590d4bc","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Increase the value of the sample with the maximum existing timestamp, or create a new sample with a value equal to the value of the sample with the maximum existing timestamp with a given increment\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary> \n\nis key name for the time series.\n</details>\n\n<details open><summary><code>addend</code></summary> \n\nis numeric value of the addend (double). An error is returned if the addend is NaN.\n</details>\n\n<note><b>Notes</b>\n- When specified key does not exist, a new time series is created.  \n- You can use this command as a counter or gauge that automatically gets history as a time series.\n- If a policy for handling duplicate samples (`IGNORE`) is defined for this time series - `TS.INCRBY` operations are affected as well (sample additions/modifications can be filtered).\n- Explicitly adding samples to a compacted time series (using [`TS.ADD`](https://redis.io/docs/latest/commands/ts.add), [`TS.MADD`](https://redis.io/docs/latest/commands/ts.madd), `TS.INCRBY`, or [`TS.DECRBY`](https://redis.io/docs/latest/commands/ts.decrby)) may result in inconsistencies between the raw and the compacted data. The compaction process may override such samples.  \n</note>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>TIMESTAMP timestamp</code></summary> \n\nis Unix time (integer, in milliseconds) specifying the sample timestamp or `*` to set the sample timestamp to the Unix time of the server's clock.\n\nUnix time is the number of milliseconds that have elapsed since 00:00:00 UTC on 1 January 1970, the Unix epoch, without adjustments made due to leap seconds.\n\n`timestamp` must be equal to or higher than the maximum existing timestamp. When equal, the value of the sample with the maximum existing timestamp is increased. If it is higher, a new sample with a timestamp set to `timestamp` is created, and its value is set to the value of the sample with the maximum existing timestamp plus `addend`.\n\nIf the time series is empty, the value is set to `addend`.\n\nWhen not specified, the timestamp is set to the Unix time of the server's clock.\n\n<note><b>NaN Handling (Redis 8.6+):</b> An error is returned if the current value at the maximum existing timestamp is NaN.</note>\n</details>\n\n<details open><summary><code>RETENTION retentionPeriod</code></summmary> \n\nis maximum retention period, compared to the maximum existing timestamp, in milliseconds.\n\nUse it only if you are creating a new time series. It is ignored if you are adding samples to an existing time series. See `RETENTION` in [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create).\n</details>\n\n<details open><summary><code>ENCODING enc</code></summary> \n\nspecifies the series sample encoding format.\n\nUse it only if you are creating a new time series. It is ignored if you are adding samples to an existing time series. See `ENCODING` in [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create).\n</details>\n\n<details open><summary><code>CHUNK_SIZE size</code></summary> \n\nis memory size, in bytes, allocated for each data chunk.\n\nUse it only if you are creating a new time series. It is ignored if you are adding samples to an existing time series. See `CHUNK_SIZE` in [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create).\n</details>\n\n<details open><summary><code>DUPLICATE_POLICY policy</code></summary>\n\nis policy for handling insertion ([`TS.ADD`](https://redis.io/docs/latest/commands/ts.add) and [`TS.MADD`](https://redis.io/docs/latest/commands/ts.madd)) of multiple samples with identical timestamps.\n\nUse it only if you are creating a new time series. It is ignored if you are adding samples to an existing time series. See `DUPLICATE_POLICY` in [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create).\n</details>\n\n<details open><summary><code>IGNORE ignoreMaxTimediff ignoreMaxValDiff</code></summary> \n\nis the policy for handling duplicate samples. A new sample is considered a duplicate and is ignored if the following conditions are met:\n\n  - The time series is not a compaction;\n  - The time series' `DUPLICATE_POLICY` IS `LAST`;\n  - The sample is added in-order (`timestamp ≥ max_timestamp`);\n  - The difference of the current timestamp from the previous timestamp (`timestamp - max_timestamp`) is less than or equal to `IGNORE_MAX_TIME_DIFF`;\n  - The absolute value difference of the current value from the value at the previous maximum timestamp (`abs(value - value_at_max_timestamp`) is less than or equal to `IGNORE_MAX_VAL_DIFF`.\n\nwhere `max_timestamp` is the timestamp of the sample with the largest timestamp in the time series, and `value_at_max_timestamp` is the value at `max_timestamp`.\n\nWhen not specified: set to the global [IGNORE_MAX_TIME_DIFF](https://redis.io/docs/latest/develop/data-types/timeseries/configuration#ignore_max_time_diff-and-ignore_max_val_diff) and [IGNORE_MAX_VAL_DIFF](https://redis.io/docs/latest/develop/data-types/timeseries/configuration#ignore_max_time_diff-and-ignore_max_val_diff), which are, by default, both set to 0.\n\nThese parameters are used when creating a new time series to set the per-key parameters, and are ignored when called with an existing time series (the existing per-key configuration parameters are used).\n</details>\n\n<details open><summary><code>LABELS [{label value}...]</code></summary> \n\nis set of label-value pairs that represent metadata labels of the key and serve as a secondary index.\n\nUse it only if you are creating a new time series. It is ignored if you are adding samples to an existing time series. See `LABELS` in [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create).\n</details>\n\n<note><b>Notes</b>\n- You can use this command to create a new time series and add a sample to it in a single command.\n  `RETENTION`, `ENCODING`, `CHUNK_SIZE`, `DUPLICATE_POLICY`, `IGNORE`, and `LABELS` are used only when creating a new time series, and ignored when adding or modifying samples in an existing time series.\n- Setting `RETENTION` and `LABELS` introduces additional time complexity.\n</note>"},{"id":"examples","title":"Examples","role":"example","text":"<details open><summary><b>Store sum of data from several sources</b></summary> \n\nSuppose you are getting number of orders or total income per minute from several points of sale, and you want to store only the combined value. Call TS.INCRBY for each point-of-sale report.\n\n\n127.0.0.1:6379> TS.INCRBY a 232 TIMESTAMP 1657811829000\t\t// point-of-sale #1\n(integer) 1657811829000\n127.0.0.1:6379> TS.INCRBY a 157 TIMESTAMP 1657811829000\t\t// point-of-sale #2\n(integer) 1657811829000\n127.0.0.1:6379> TS.INCRBY a 432 TIMESTAMP 1657811829000\t\t// point-of-sale #3\n(integer) 1657811829000\n\n\nNote that the timestamps must arrive in non-decreasing order.\n\n\n127.0.0.1:6379> ts.incrby a 100 TIMESTAMP 50\n(error) TSDB: timestamp must be equal to or higher than the maximum existing timestamp\n\n\nYou can achieve similar results without such protection using `TS.ADD key timestamp value ON_DUPLICATE sum`.\n</details>\n\n<details open><summary><b>Count sensor captures</b></summary>\n\nSuppose a sensor ticks whenever a car is passed on a road, and you want to count occurrences. Whenever you get a tick from the sensor you can simply call:\n\n\n127.0.0.1:6379> TS.INCRBY a 1\n(integer) 1658431553109\n\n\nThe timestamp is filled automatically.\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): the timestamp of the upserted sample. If the sample is ignored (see `IGNORE` in [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create)), the reply will be the largest timestamp in the time series.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, wrong key type, or when `timestamp` is not equal to or higher than the maximum existing timestamp.\n\n**RESP3:**\n\nOne of the following:\n* [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): the timestamp of the upserted sample. If the sample is ignored (see `IGNORE` in [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create)), the reply will be the largest timestamp in the time series.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, wrong key type, or when `timestamp` is not equal to or higher than the maximum existing timestamp."},{"id":"see-also","title":"See also","role":"related","text":"[`TS.DECRBY`](https://redis.io/docs/latest/commands/ts.decrby) | [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create)"},{"id":"related-topics","title":"Related topics","role":"related","text":"[RedisTimeSeries](https://redis.io/docs/latest/develop/data-types/timeseries)"}],"examples":[]}
{"schema_version":2,"id":"commands/ts.info","title":"TS.INFO","url":"https://redis.io/docs/latest/commands/ts.info/","summary":"Returns information and statistics for a time series","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"229c60ae5f19c0312f1a57770472968b4a70885cf5b4a75240606a0b598e818d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Return information and statistics for a time series.\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>key</code></summary> \nis key name of the time series.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open>\n<summary><code>[DEBUG]</code></summary>\n\nis an optional flag to get a more detailed information about the chunks.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Find information about a temperature/humidity time series by location and sensor type</b></summary>\n\nCreate a set of sensors to measure temperature and humidity in your study and kitchen.\n\n\n127.0.0.1:6379> TS.CREATE telemetry:study:temperature LABELS room study type temperature\nOK\n127.0.0.1:6379> TS.CREATE telemetry:study:humidity LABELS room study type humidity\nOK\n127.0.0.1:6379> TS.CREATE telemetry:kitchen:temperature LABELS room kitchen type temperature\nOK\n127.0.0.1:6379> TS.CREATE telemetry:kitchen:humidity LABELS room kitchen type humidity\nOK\n\n\nFind information about the time series for temperature in the kitchen.\n\n\n127.0.0.1:6379> TS.INFO telemetry:kitchen:temperature\n 1) totalSamples\n 2) (integer) 0\n 3) memoryUsage\n 4) (integer) 4246\n 5) firstTimestamp\n 6) (integer) 0\n 7) lastTimestamp\n 8) (integer) 0\n 9) retentionTime\n10) (integer) 0\n11) chunkCount\n12) (integer) 1\n13) chunkSize\n14) (integer) 4096\n15) chunkType\n16) compressed\n17) duplicatePolicy\n18) (nil)\n19) labels\n20) 1) 1) \"room\"\n       2) \"kitchen\"\n    2) 1) \"type\"\n       2) \"temperature\"\n21) sourceKey\n22) (nil)\n23) rules\n24) (empty array)\n\n\nQuery the time series using DEBUG to get more information about the chunks.\n\n\n127.0.0.1:6379> TS.INFO telemetry:kitchen:temperature DEBUG\n 1) totalSamples\n 2) (integer) 0\n 3) memoryUsage\n 4) (integer) 4246\n 5) firstTimestamp\n 6) (integer) 0\n 7) lastTimestamp\n 8) (integer) 0\n 9) retentionTime\n10) (integer) 0\n11) chunkCount\n12) (integer) 1\n13) chunkSize\n14) (integer) 4096\n15) chunkType\n16) compressed\n17) duplicatePolicy\n18) (nil)\n19) labels\n20) 1) 1) \"room\"\n       2) \"kitchen\"\n    2) 1) \"type\"\n       2) \"temperature\"\n21) sourceKey\n22) (nil)\n23) rules\n24) (empty array)\n25) keySelfName\n26) \"telemetry:kitchen:temperature\"\n27) Chunks\n28) 1)  1) startTimestamp\n        2) (integer) 0\n        3) endTimestamp\n        4) (integer) 0\n        5) samples\n        6) (integer) 0\n        7) size\n        8) (integer) 4096\n        9) bytesPerSample\n       10) \"inf\"\n\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) with information about the time series as flattened name-value pairs:\n\n| Name<br>[Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) | Description\n| ---------------------------- | -\n| `totalSamples`    | [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers)<br> Total number of samples in this time series\n| `memoryUsage`     | [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers)<br> Total number of bytes allocated for this time series, which is the sum of <br> - The memory used for storing the series' configuration parameters (retention period, duplication policy, etc.)<br>- The memory used for storing the series' compaction rules<br>- The memory used for storing the series' labels (key-value pairs)<br>- The memory used for storing the chunks (chunk header + compressed/uncompressed data)\n| `firstTimestamp`  | [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers)<br> First timestamp present in this time series (Unix timestamp in milliseconds)\n| `lastTimestamp`   | [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers)<br> Last timestamp present in this time series  (Unix timestamp in milliseconds)\n| `retentionTime`   | [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers)<br> The retention period, in milliseconds, for this time series\n| `chunkCount`      | [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers)<br> Number of chunks used for this time series\n| `chunkSize`       | [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers)<br> The initial allocation size, in bytes, for the data part of each new chunk.<br>Actual chunks may consume more memory. Changing the chunk size (using [`TS.ALTER`](https://redis.io/docs/latest/commands/ts.alter)) does not affect existing chunks.\n| `chunkType`       | [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings)<br> The chunks type: `compressed` or `uncompressed`\n| `duplicatePolicy` | [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) or [Nil reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings)<br> The [duplicate policy](https://redis.io/docs/latest/develop/data-types/timeseries/configuration#duplicate_policy) of this time series\n| `labels`          | [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) or [Nil reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings)<br> Metadata labels of this time series<br> Each element is a 2-elements [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of ([Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings), [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings)) representing (label, value)\n| `sourceKey`       | [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) or [Nil reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings)<br>Key name for source time series in case the current series is a target of a [compaction rule](https://redis.io/docs/latest/commands/ts.createrule)\n| `rules`           | [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays)<br> [Compaction rules](https://redis.io/docs/latest/commands/ts.createrule) defined in this time series<br> Each rule is an [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) with 4 elements:<br>- [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings): The compaction key<br>- [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): The bucket duration<br>- [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings): The aggregator<br>- [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): The alignment (since RedisTimeSeries v1.8)\n\nWhen [`DEBUG`](https://redis.io/docs/latest/commands/debug) is specified, the response also contains:\n\n| Name<br>[Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) | Description\n| ---------------------------- | -\n| `keySelfName`     | [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings)<br> Name of the key\n| `Chunks`          | [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) with information about the chunks<br>Each element is an [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of information about a single chunk in a name([Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings))-value pairs:<br>- `startTimestamp` - [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) - First timestamp present in the chunk<br>- `endTimestamp` - [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) - Last timestamp present in the chunk<br>- `samples` - [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) - Total number of samples in the chunk<br>- `size` - [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) - the chunk's internal data size (without overheads) in bytes<br>- `bytesPerSample` - [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) (double) - Ratio of `size` and `samples`\n\n**RESP3:**\n\n[Map reply](https://redis.io/docs/latest/develop/reference/protocol-spec#maps) with information about the time series. The map contains the same fields as described in the RESP2 response, but organized as key-value pairs in a map structure rather than a flattened array.\n\nWhen [`DEBUG`](https://redis.io/docs/latest/commands/debug) is specified, the response also contains the additional `keySelfName` and `Chunks` fields as described above."},{"id":"see-also","title":"See also","role":"related","text":"[`TS.RANGE`](https://redis.io/docs/latest/commands/ts.range) | [`TS.QUERYINDEX`](https://redis.io/docs/latest/commands/ts.queryindex) | [`TS.GET`](https://redis.io/docs/latest/commands/ts.get)"},{"id":"related-topics","title":"Related topics","role":"related","text":"[RedisTimeSeries](https://redis.io/docs/latest/develop/data-types/timeseries)"}],"examples":[]}
{"schema_version":2,"id":"commands/ts.madd","title":"TS.MADD","url":"https://redis.io/docs/latest/commands/ts.madd/","summary":"Append new samples to one or more time series","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"17705e414616d195f8d252745aa65749aeb4dd2df19ffa206c53413663a7f8f0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\n\nAppend new samples to one or more time series\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>key</code></summary> \n\nis the key name for the time series.\n</details>\n\n<details open>\n<summary><code>timestamp</code></summary>\n\nis Unix time (integer, in milliseconds) specifying the sample timestamp or `*` to set the sample timestamp to the Unix time of the server's clock.\n\nUnix time is the number of milliseconds that have elapsed since 00:00:00 UTC on 1 January 1970, the Unix epoch, without adjustments made due to leap seconds.\n</details>\n\n<details open>\n<summary><code>value</code></summary>\n\nis numeric data value of the sample (double). The double number should follow <a href=\"https://tools.ietf.org/html/rfc7159\">RFC 7159</a> (JSON standard). The parser rejects overly large values that would not fit in binary64. It does not accept infinite values. NaN (Not a Number) values are supported starting from Redis 8.6.\n</details>\n\n<note><b>Notes:</b>\n- If `timestamp` is older than the retention period compared to the maximum existing timestamp, the sample is discarded and an error is returned.\n- Explicitly adding samples to a compacted time series (using [`TS.ADD`](https://redis.io/docs/latest/commands/ts.add), `TS.MADD`, [`TS.INCRBY`](https://redis.io/docs/latest/commands/ts.incrby), or [`TS.DECRBY`](https://redis.io/docs/latest/commands/ts.decrby)) may result in inconsistencies between the raw and the compacted data. The compaction process may override such samples.\n- `ignoreMaxTimeDiff` and `ignoreMaxValDiff` cannot be specified as is the case with `TS.ADD`. However, the same logic still applies based on the values of the per-key configuration parameters. See the [`TS.ADD`](https://redis.io/docs/latest/commands/ts.add) command page for more information.\n</note>"},{"id":"complexity","title":"Complexity","role":"performance","text":"If a compaction rule exits on a time series, TS.MADD performance might be reduced.\nThe complexity of TS.MADD is always `O(N*M)`, where `N` is the amount of series updated and `M` is the number of compaction rules or `O(N)` with no compaction."},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Add stock prices at different timestamps</b></summary>\n\nCreate two stocks and add their prices at three different timestamps.\n\n\n127.0.0.1:6379> TS.CREATE stock:A LABELS type stock name A\nOK\n127.0.0.1:6379> TS.CREATE stock:B LABELS type stock name B\nOK\n127.0.0.1:6379> TS.MADD stock:A 1000 100 stock:A 1010 110 stock:A 1020 120 stock:B 1000 120 stock:B 1010 110 stock:B 1020 100\n1) (integer) 1000\n2) (integer) 1010\n3) (integer) 1020\n4) (integer) 1000\n5) (integer) 1010\n6) (integer) 1020\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays), where each element is an [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) representing the timestamp of a upserted sample. For each element that is ignored (see `IGNORE` in [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create)), the reply element value will be the largest timestamp in the time series.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, wrong key type, duplication policy is `BLOCK`, or when `timestamp` is older than the retention period compared to the maximum existing timestamp.\n\n**RESP3:**\n\nOne of the following:\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays), where each element is an [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) representing the timestamp of a upserted sample. For each element that is ignored (see `IGNORE` in [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create)), the reply element value will be the largest timestamp in the time series.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, wrong key type, duplication policy is `BLOCK`, or when `timestamp` is older than the retention period compared to the maximum existing timestamp."},{"id":"see-also","title":"See also","role":"related","text":"[`TS.MRANGE`](https://redis.io/docs/latest/commands/ts.mrange) | [`TS.RANGE`](https://redis.io/docs/latest/commands/ts.range) | [`TS.MREVRANGE`](https://redis.io/docs/latest/commands/ts.mrevrange) | [`TS.REVRANGE`](https://redis.io/docs/latest/commands/ts.revrange)"},{"id":"related-topics","title":"Related topics","role":"related","text":"[RedisTimeSeries](https://redis.io/docs/latest/develop/data-types/timeseries)"}],"examples":[]}
{"schema_version":2,"id":"commands/ts.mget","title":"TS.MGET","url":"https://redis.io/docs/latest/commands/ts.mget/","summary":"Get the sample with the highest timestamp from each time series matching a specific filter","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"8e58f93b706fcdc09b20bc01686fd02b8e1d9960c8f651d7e31576c111cbd68a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\n\nGet the sample with the highest timestamp from each time series matching a specific filter. Starting from Redis 8.6, NaN values are included in the results.\n\n> [!NOTE]\n> This command will reply only if the current user has read access to all keys that match the filter.\n> Otherwise, it will reply with \"*(error): current user doesn't have read permission to one or more keys that match the specified filter*\".\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>FILTER filterExpr...</code></summary>\n\nfilters time series based on their labels and label values.\n\nEach filter expression has one of the following syntaxes:\n\n  - `label!=` - the time series has a label named `label`\n  - `label=value` - the time series has a label named `label` with a value equal to `value`\n  - `label=(value1,value2,...)` - the time series has a label named `label` with a value equal to one of the values in the list\n  - `label=` - the time series does not have a label named `label`\n  - `label!=value` - the time series does not have a label named `label` with a value equal to `value`\n  - `label!=(value1,value2,...)` - the time series does not have a label named `label` with a value equal to any of the values in the list\n\n  <note><b>Notes:</b>\n   - At least one filter expression with a syntax `label=value` or `label=(value1,value2,...)` is required.\n   - Filter expressions are conjunctive. For example, the filter `type=temperature room=study` means that `type=temperature` AND `room=study`.\n   - Whitespace is not allowed in a filter expression except between quotes or double quotes in values - for example, `x=\"y y\"` or `x='(y y,z z)'`.\n   </note>\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open>\n<summary><code>LATEST</code> (since RedisTimeSeries v1.8)</summary> \n\nis used when a time series is a compaction. With `LATEST`, TS.MGET also reports the compacted value of the latest (possibly partial) bucket, given that this bucket's start time falls within `[fromTimestamp, toTimestamp]`. Without `LATEST`, TS.MGET does not report the latest (possibly partial) bucket. When a time series is not a compaction, `LATEST` is ignored.\n  \nThe data in the latest bucket of a compaction is possibly partial. A bucket is _closed_ and compacted only upon the arrival of a new sample that _opens_ a new _latest_ bucket. There are cases, however, when the compacted value of the latest (possibly partial) bucket is also required. In such a case, use `LATEST`.\n</details>\n\n<details open>\n<summary><code>WITHLABELS</code></summary> \n\nincludes in the reply all label-value pairs representing metadata labels of the time series. \nIf `WITHLABELS` or `SELECTED_LABELS` are not specified, by default, an empty list is reported as label-value pairs.\n\n</details>\n\n<details open>\n<summary><code>SELECTED_LABELS label...</code> (since RedisTimeSeries v1.6)</summary> \n\nreturns a subset of the label-value pairs that represent metadata labels of the time series. \nUse when a large number of labels exists per series, but only the values of some of the labels are required. \nIf `WITHLABELS` or `SELECTED_LABELS` are not specified, by default, an empty list is reported as label-value pairs.\n\n</details>\n\n<note><b>Note:</b> The [`MGET`](https://redis.io/docs/latest/commands/mget) command cannot be part of a transaction when running on a Redis cluster.</note>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Select labels to retrieve</b></summary>\n\nCreate time series for temperature in Tel Aviv and Jerusalem, then add different temperature samples.\n\n\nredis> TS.CREATE temp:TLV LABELS type temp location TLV\nOK\nredis> TS.CREATE temp:JLM LABELS type temp location JLM\nOK\nredis> TS.MADD temp:TLV 1000 30 temp:TLV 1010 35 temp:TLV 1020 9999 temp:TLV 1030 40\n1) (integer) 1000\n2) (integer) 1010\n3) (integer) 1020\n4) (integer) 1030\nredis> TS.MADD temp:JLM 1005 30 temp:JLM 1015 35 temp:JLM 1025 9999 temp:JLM 1035 40\n1) (integer) 1005\n2) (integer) 1015\n3) (integer) 1025\n4) (integer) 1035\n\n\nGet all the labels associated with the last sample.\n\n\nredis> TS.MGET WITHLABELS FILTER type=temp\n1) 1) \"temp:JLM\"\n   2) 1) 1) \"type\"\n         2) \"temp\"\n      2) 1) \"location\"\n         2) \"JLM\"\n   3) 1) (integer) 1035\n      2) 40\n2) 1) \"temp:TLV\"\n   2) 1) 1) \"type\"\n         2) \"temp\"\n      2) 1) \"location\"\n         2) \"TLV\"\n   3) 1) (integer) 1030\n      2) 40\n\n\nTo get only the `location` label for each last sample, use `SELECTED_LABELS`.\n\n\nredis> TS.MGET SELECTED_LABELS location FILTER type=temp\n1) 1) \"temp:JLM\"\n   2) 1) 1) \"location\"\n         2) \"JLM\"\n   3) 1) (integer) 1035\n      2) 40\n2) 1) \"temp:TLV\"\n   2) 1) 1) \"location\"\n         2) \"TLV\"\n   3) 1) (integer) 1030\n      2) 40\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): for each time series matching the specified filters, the following is reported:\n- [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings): The time series key name\n- [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): label-value pairs ([Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings), [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings))\n  - By default, an empty array is reported\n  - If `WITHLABELS` is specified, all labels associated with this time series are reported\n  - If `SELECTED_LABELS label...` is specified, the selected labels are reported (null value when no such label defined)\n- [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): a single timestamp-value pair ([Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers), [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings))\n\n**RESP3:**\n\n[Map reply](https://redis.io/docs/latest/develop/reference/protocol-spec#maps): for each time series matching the specified filters, the following is reported:\n- [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings): The time series key name\n- [Map reply](https://redis.io/docs/latest/develop/reference/protocol-spec#maps) or [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): label-value pairs\n  - By default, an empty array is reported\n  - If `WITHLABELS` is specified, all labels associated with this time series are reported as a map\n  - If `SELECTED_LABELS label...` is specified, the selected labels are reported as a map (null value when no such label defined)\n- [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): a single timestamp-value pair ([Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers), [Double reply](https://redis.io/docs/latest/develop/reference/protocol-spec#doubles))"},{"id":"see-also","title":"See also","role":"related","text":"[`TS.MRANGE`](https://redis.io/docs/latest/commands/ts.mrange) | [`TS.RANGE`](https://redis.io/docs/latest/commands/ts.range) | [`TS.MREVRANGE`](https://redis.io/docs/latest/commands/ts.mrevrange) | [`TS.REVRANGE`](https://redis.io/docs/latest/commands/ts.revrange)"},{"id":"related-topics","title":"Related topics","role":"related","text":"[RedisTimeSeries](https://redis.io/docs/latest/develop/data-types/timeseries)"}],"examples":[]}
{"schema_version":2,"id":"commands/ts.mrange","title":"TS.MRANGE","url":"https://redis.io/docs/latest/commands/ts.mrange/","summary":"Query a range across multiple time series by filters in forward direction","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"a2755246ca79d70648f9075bec25cc17475dbcf9cdc1a03a2fdae082cba9dcab","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\n\nQuery a range across multiple time series by filters in the forward direction. Starting from Redis 8.6, NaN values are included in raw measurement reports (queries without aggregation).\n\n> [!NOTE]\n> This command will reply only if the current user has read access to all keys that match the filter.\n> Otherwise, it will reply with \"*(error): current user doesn't have read permission to one or more keys that match the specified filter*\".\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>fromTimestamp</code></summary>\n\nis the start timestamp for the range query (integer Unix timestamp in milliseconds) or `-` to denote the timestamp of the earliest sample among all the time series that passes `FILTER filterExpr...`.\n</details>\n\n<details open>\n<summary><code>toTimestamp</code></summary>\n\nis the end timestamp for the range query (integer Unix timestamp in milliseconds) or `+` to denote the timestamp of the latest sample among all the time series that passes `FILTER filterExpr...`.\n</details>\n\n<details open>\n<summary><code>FILTER filterExpr...</code></summary>\n\nfilters time series based on their labels and label values.\n\nEach filter expression has one of the following syntaxes:\n\n  - `label!=` - the time series has a label named `label`\n  - `label=value` - the time series has a label named `label` with a value equal to `value`\n  - `label=(value1,value2,...)` - the time series has a label named `label` with a value equal to one of the values in the list\n  - `label=` - the time series does not have a label named `label`\n  - `label!=value` - the time series does not have a label named `label` with a value equal to `value`\n  - `label!=(value1,value2,...)` - the time series does not have a label named `label` with a value equal to any of the values in the list\n\n  <note><b>Notes:</b>\n   - At least one filter expression with a syntax `label=value` or `label=(value1,value2,...)` is required.\n   - Filter expressions are conjunctive. For example, the filter `type=temperature room=study` means that `type=temperature` AND `room=study`.\n   - Whitespace is not allowed in a filter expression except between quotes or double quotes in values - for example, `x=\"y y\"` or `x='(y y,z z)'`.\n   </note>\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open>\n<summary><code>LATEST</code> (since RedisTimeSeries 1.8)</summary>\n\nis used when a time series is a compaction. With `LATEST`, TS.MRANGE also reports the compacted value of the latest (possibly partial) bucket, given that this bucket's start time falls within `[fromTimestamp, toTimestamp]`. Without `LATEST`, TS.MRANGE does not report the latest (possibly partial) bucket. When a time series is not a compaction, `LATEST` is ignored.\n\nThe data in the latest bucket of a compaction is possibly partial. A bucket is _closed_ and compacted only upon the arrival of a new sample that _opens_ a new _latest_ bucket. There are cases, however, when the compacted value of the latest (possibly partial) bucket is also required. In such a case, use `LATEST`.\n</details>\n\n<details open>\n<summary><code>FILTER_BY_TS ts...</code> (since RedisTimeSeries 1.6)</summary>\n\nfilters samples by a list of specific timestamps. A sample passes the filter if its exact timestamp is specified and falls within `[fromTimestamp, toTimestamp]`.\n\nWhen used together with `AGGREGATION`: samples are filtered before being aggregated.\n</details>\n\n<details open>\n<summary><code>FILTER_BY_VALUE min max</code> (since RedisTimeSeries 1.6)</summary>\n\nfilters samples by minimum and maximum values. `min` and `max` cannot be NaN values.\n\nWhen used together with `AGGREGATION`: samples are filtered before being aggregated.\n</details>\n\n<details open>\n<summary><code>WITHLABELS</code></summary>\n\nincludes in the reply all label-value pairs representing metadata labels of the time series. \nIf `WITHLABELS` or `SELECTED_LABELS` are not specified, by default, an empty list is reported as label-value pairs.\n</details>\n\n<details open>\n<summary><code>SELECTED_LABELS label...</code> (since RedisTimeSeries 1.6)</summary>\n\nreturns a subset of the label-value pairs that represent metadata labels of the time series. \nUse when a large number of labels exists per series, but only the values of some of the labels are required. \nIf `WITHLABELS` or `SELECTED_LABELS` are not specified, by default, an empty list is reported as label-value pairs.\n</details>\n\n<details open>\n<summary><code>COUNT count</code></summary>\n\nWhen used without `AGGREGATION`: limits the number of reported samples per time series.\n\nWhen used together with `AGGREGATION`: limits the number of reported buckets.\n</details>\n\n<details open>\n<summary><code>ALIGN align</code> (since RedisTimeSeries 1.6)</summary>\n\nis a time bucket alignment control for `AGGREGATION`. It controls the time bucket timestamps by changing the reference timestamp on which a bucket is defined. \n\nValues include:\n   \n - `start` or `-`: The reference timestamp will be the query start interval time (`fromTimestamp`) which can't be `-`\n - `end` or `+`: The reference timestamp will be the query end interval time (`toTimestamp`) which can't be `+`\n - A specific timestamp: align the reference timestamp to a specific time\n   \n<note><b>Note:</b> When not provided, alignment is set to `0`.</note>\n</details>\n\n<details open>\n<summary><code>AGGREGATION aggregators bucketDuration</code></summary>\n\nfor each time series, aggregates samples into time buckets, where:\n\n  - `aggregators` is one or more comma-separated aggregators from the following table:\n\n    | aggregator   | Description                                                     |\n    | ------------ | --------------------------------------------------------------- |\n    | `avg`        | Arithmetic mean of all non-NaN values                           |\n    | `sum`        | Sum of all non-NaN values                                       |\n    | `min`        | Minimum non-NaN value                                           |\n    | `max`        | Maximum non-NaN value                                           |\n    | `range`      | Difference between the maximum and the minimum non-NaN values   |\n    | `count`      | Number of non-NaN values                                        |\n    | `countNaN`   | Number of NaN values (since Redis 8.6)                          |\n    | `countAll`   | Number of values, including NaN and non-NaN (since Redis 8.6)   |\n    | `first`      | The non-NaN value with the lowest timestamp in the bucket       |\n    | `last`       | The non-NaN value with the highest timestamp in the bucket      |\n    | `std.p`      | Population standard deviation of the non-NaN values             |\n    | `std.s`      | Sample standard deviation of the non-NaN values                 |\n    | `var.p`      | Population variance of the non-NaN values                       |\n    | `var.s`      | Sample variance of the non-NaN values                           |\n    | `twa`        | Time-weighted average over the bucket's timeframe (ignores NaN values) (since RedisTimeSeries 1.8) |\n\n  - `bucketDuration` is duration of each bucket, in milliseconds.\n\n  Note: `aggregators` can either be a single aggregator or multiple aggregators separated by commas as shown below.\n  No whitespace is allowed between aggregators.\n\n  AGGREGATION min,avg,max\n  \n  Without `ALIGN`, bucket start times are multiples of `bucketDuration`.\n  \n  With `ALIGN align`, bucket start times are multiples of `bucketDuration` with remainder `align % bucketDuration`.\n  \n  The first bucket start time is less than or equal to `fromTimestamp`.  \n</details>\n\n<details open>\n<summary><code>[BUCKETTIMESTAMP bt]</code> (since RedisTimeSeries 1.8)</summary>\n\ncontrols how bucket timestamps are reported.\n\n| `bt`             | Timestamp reported for each bucket                            |\n| ---------------- | ------------------------------------------------------------- |\n| `-` or `start`   | the bucket's start time (default)                             |\n| `+` or `end`     | the bucket's end time                                         |\n| `~` or `mid`     | the bucket's mid time (rounded down if not an integer)        |\n</details>\n\n<details open>\n<summary><code>[EMPTY]</code> (since RedisTimeSeries 1.8)</summary>\n\nis a flag, which, when specified, reports aggregations also for empty buckets.\n\n| aggregator           | Value reported for each empty bucket |\n| -------------------- | ------------------------------------ |\n| `sum`, `count`       | `0`                                  |\n| `last`               | The value of the last sample before the bucket's start. `NaN` when no such sample. |\n| `twa`                | Average value over the bucket's timeframe based on linear interpolation of the last sample before the bucket's start and the first sample after the bucket's end. `NaN` when no such samples. |\n| `min`, `max`, `range`, `avg`, `first`, `std.p`, `std.s` | `NaN` |\n\nRegardless of the values of `fromTimestamp` and `toTimestamp`, no data is reported for buckets that end before the earliest sample or begin after the latest sample in the time series.\n</details>\n\n<details open>\n<summary><code>GROUPBY label REDUCE reducer</code> (since RedisTimeSeries 1.6)</summary>\n\nsplits time series into groups, each group contains time series that share the same value for the provided label name, then aggregates results in each group.\n\nWhen combined with `AGGREGATION` the `GROUPBY`/`REDUCE` is applied post aggregation stage.\n`GROUPBY`/`REDUCE` is not permitted when multiple aggregators are specified.\n\n  - `label` is label name. A group is created for all time series that share the same value for this label.\n\n  - `reducer` is an aggregation type used to aggregate the results in each group.\n\n    | `reducer` | Description                                                                                                      |\n    | --------- | ---------------------------------------------------------------------------------------------------------------- |\n    | `avg`     | Arithmetic mean of all non-NaN values; NaN if no such values (since RedisTimeSeries 1.8)                         |\n    | `sum`     | Sum of all non-NaN values; NaN if no such values                                                                 |\n    | `min`     | Minimum non-NaN value; NaN if no such values                                                                     |\n    | `max`     | Maximum non-NaN value; NaN if no such values                                                                     |\n    | `range`   | Difference between the maximum and the minimum non-NaN values; NaN if no such values (since RedisTimeSeries 1.8) |\n    | `count`   | Number of non-NaN values (since RedisTimeSeries 1.8)                                                             |\n    | `countNaN`| Number of NaN values (since Redis 8.6)                                                                           |\n    | `countAll`| Number of values, including NaN and non-NaN (since Redis 8.6)                                                     |\n    | `std.p`   | Population standard deviation of the non-NaN values; NaN if no such values (since RedisTimeSeries 1.8)           |\n    | `std.s`   | Sample standard deviation of thel non-NaN values; NaN if no such values (since RedisTimeSeries 1.8)              |\n    | `var.p`   | Population variance of the non-NaN values; NaN if no such values (since RedisTimeSeries 1.8)                     |\n    | `var.s`   | Sample variance of the non-NaN values; NaN if no such values (since RedisTimeSeries 1.8)                         |\n\n<note><b>Notes:</b> \n  - The produced time series is named `<label>=<value>`\n  - The produced time series contains two labels with these label array structures:\n    - `__reducer__`, the reducer used (e.g., `\"count\"`)\n    - `__source__`, the list of time series keys used to compute the grouped series (e.g., `\"key1,key2,key3\"`)\n</note>\n\n</details>\n\n<details open>\n<summary><code>EXCLUDEEMPTY</code> (since Redis 8.10)</summary>\n\nexcludes from the reply any time series that has no samples in the requested range. By default, every time series that passes `FILTER filterExpr...` is reported, even those with no samples in the range (reported with an empty samples list).\n\nA time series whose only samples in the range are NaN is not considered empty and is still reported.\n\n`EXCLUDEEMPTY` cannot be used together with `GROUPBY label REDUCE reducer`; combining them replies with an error.\n</details>\n\n<note><b>Note:</b> An `MRANGE` command cannot be part of a transaction when running on a Redis cluster.</note>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Retrieve maximum stock price per timestamp</b></summary>\n\nCreate two stocks and add their prices at three different timestamps.\n\n\n127.0.0.1:6379> TS.CREATE stock:A LABELS type stock name A\nOK\n127.0.0.1:6379> TS.CREATE stock:B LABELS type stock name B\nOK\n127.0.0.1:6379> TS.MADD stock:A 1000 100 stock:A 1010 110 stock:A 1020 120\n1) (integer) 1000\n2) (integer) 1010\n3) (integer) 1020\n127.0.0.1:6379> TS.MADD stock:B 1000 120 stock:B 1010 110 stock:B 1020 100\n1) (integer) 1000\n2) (integer) 1010\n3) (integer) 1020\n\n\nYou can now retrieve the maximum stock price per timestamp.\n\n\n127.0.0.1:6379> TS.MRANGE - + WITHLABELS FILTER type=stock GROUPBY type REDUCE max\n1) 1) \"type=stock\"\n   2) 1) 1) \"type\"\n         2) \"stock\"\n      2) 1) \"__reducer__\"\n         2) \"max\"\n      3) 1) \"__source__\"\n         2) \"stock:A,stock:B\"\n   3) 1) 1) (integer) 1000\n         2) 120\n      2) 1) (integer) 1010\n         2) 110\n      3) 1) (integer) 1020\n         2) 120\n\n\nThe `FILTER type=stock` clause returns a single time series representing stock prices. The `GROUPBY type REDUCE max` clause splits the time series into groups with identical type values, and then, for each timestamp, aggregates all series that share the same type value using the max aggregator.\n</details>\n\n<details open>\n<summary><b>Calculate average stock price and retrieve maximum average</b></summary> \n\nCreate two stocks and add their prices at nine different timestamps.\n\n\n127.0.0.1:6379> TS.CREATE stock:A LABELS type stock name A\nOK\n127.0.0.1:6379> TS.CREATE stock:B LABELS type stock name B\nOK\n127.0.0.1:6379> TS.MADD stock:A 1000 100 stock:A 1010 110 stock:A 1020 120\n1) (integer) 1000\n2) (integer) 1010\n3) (integer) 1020\n127.0.0.1:6379> TS.MADD stock:B 1000 120 stock:B 1010 110 stock:B 1020 100\n1) (integer) 1000\n2) (integer) 1010\n3) (integer) 1020\n127.0.0.1:6379> TS.MADD stock:A 2000 200 stock:A 2010 210 stock:A 2020 220\n1) (integer) 2000\n2) (integer) 2010\n3) (integer) 2020\n127.0.0.1:6379> TS.MADD stock:B 2000 220 stock:B 2010 210 stock:B 2020 200\n1) (integer) 2000\n2) (integer) 2010\n3) (integer) 2020\n127.0.0.1:6379> TS.MADD stock:A 3000 300 stock:A 3010 310 stock:A 3020 320\n1) (integer) 3000\n2) (integer) 3010\n3) (integer) 3020\n127.0.0.1:6379> TS.MADD stock:B 3000 320 stock:B 3010 310 stock:B 3020 300\n1) (integer) 3000\n2) (integer) 3010\n3) (integer) 3020\n\n\nNow, for each stock, calculate the average stock price per a 1000-millisecond timeframe, and then retrieve the stock with the maximum average for that timeframe.\n\n\n127.0.0.1:6379> TS.MRANGE - + WITHLABELS AGGREGATION avg 1000 FILTER type=stock GROUPBY type REDUCE max\n1) 1) \"type=stock\"\n   2) 1) 1) \"type\"\n         2) \"stock\"\n      2) 1) \"__reducer__\"\n         2) \"max\"\n      3) 1) \"__source__\"\n         2) \"stock:A,stock:B\"\n   3) 1) 1) (integer) 1000\n         2) 110\n      2) 1) (integer) 2000\n         2) 210\n      3) 1) (integer) 3000\n         2) 310\n\n</details>\n\n<details open>\n<summary><b>Group query results</b></summary>\n\nQuery all time series with the metric label equal to `cpu`, then group the time series by the value of their `metric_name` label value and for each group return the maximum value and the time series keys (_source_) with that value.\n\n\n127.0.0.1:6379> TS.ADD ts1 1548149180000 90 labels metric cpu metric_name system\n(integer) 1548149180000\n127.0.0.1:6379> TS.ADD ts1 1548149185000 45\n(integer) 1548149185000\n127.0.0.1:6379> TS.ADD ts2 1548149180000 99 labels metric cpu metric_name user\n(integer) 1548149180000\n127.0.0.1:6379> TS.MRANGE - + WITHLABELS FILTER metric=cpu GROUPBY metric_name REDUCE max\n1) 1) \"metric_name=system\"\n   2) 1) 1) \"metric_name\"\n         2) \"system\"\n      2) 1) \"__reducer__\"\n         2) \"max\"\n      3) 1) \"__source__\"\n         2) \"ts1\"\n   3) 1) 1) (integer) 1548149180000\n         2) 90\n      2) 1) (integer) 1548149185000\n         2) 45\n2) 1) \"metric_name=user\"\n   2) 1) 1) \"metric_name\"\n         2) \"user\"\n      2) 1) \"__reducer__\"\n         2) \"max\"\n      3) 1) \"__source__\"\n         2) \"ts2\"\n   3) 1) 1) (integer) 1548149180000\n         2) 99\n\n</details>\n\n<details open>\n<summary><b>Filter query by value</b></summary>\n\nQuery all time series with the metric label equal to `cpu`, then filter values larger or equal to 90.0 and smaller or equal to 100.0.\n\n\n127.0.0.1:6379> TS.ADD ts1 1548149180000 90 labels metric cpu metric_name system\n(integer) 1548149180000\n127.0.0.1:6379> TS.ADD ts1 1548149185000 45\n(integer) 1548149185000\n127.0.0.1:6379> TS.ADD ts2 1548149180000 99 labels metric cpu metric_name user\n(integer) 1548149180000\n127.0.0.1:6379> TS.MRANGE - + FILTER_BY_VALUE 90 100 WITHLABELS FILTER metric=cpu\n1) 1) \"ts1\"\n   2) 1) 1) \"metric\"\n         2) \"cpu\"\n      2) 1) \"metric_name\"\n         2) \"system\"\n   3) 1) 1) (integer) 1548149180000\n         2) 90\n2) 1) \"ts2\"\n   2) 1) 1) \"metric\"\n         2) \"cpu\"\n      2) 1) \"metric_name\"\n         2) \"user\"\n   3) 1) 1) (integer) 1548149180000\n         2) 99\n\n</details>\n\n<details open>\n<summary><b>Query using a label</b></summary>\n\nQuery all time series with the metric label equal to `cpu`, but only return the team label.\n\n\n127.0.0.1:6379> TS.ADD ts1 1548149180000 90 labels metric cpu metric_name system team NY\n(integer) 1548149180000\n127.0.0.1:6379> TS.ADD ts1 1548149185000 45\n(integer) 1548149185000\n127.0.0.1:6379> TS.ADD ts2 1548149180000 99 labels metric cpu metric_name user team SF\n(integer) 1548149180000\n127.0.0.1:6379> TS.MRANGE - + SELECTED_LABELS team FILTER metric=cpu\n1) 1) \"ts1\"\n   2) 1) 1) \"team\"\n         2) \"NY\"\n   3) 1) 1) (integer) 1548149180000\n         2) 90\n      2) 1) (integer) 1548149185000\n         2) 45\n2) 1) \"ts2\"\n   2) 1) 1) \"team\"\n         2) \"SF\"\n   3) 1) 1) (integer) 1548149180000\n         2) 99\n\n</details>\n\n<details open>\n<summary><b>Exclude empty time series from the reply</b></summary>\n\nCreate three time series that share the label `s=1`, then add samples so that only two of them have data in the `- 500` range.\n\n\n127.0.0.1:6379> TS.CREATE s LABELS s 1 t 1\nOK\n127.0.0.1:6379> TS.CREATE t LABELS s 1 t 1\nOK\n127.0.0.1:6379> TS.CREATE u LABELS s 1 t 1\nOK\n127.0.0.1:6379> TS.MADD s 100 100 t 100 100 s 200 200 t 300 300 s 400 400 t 400 400 u 2000 2000\n1) (integer) 100\n2) (integer) 100\n3) (integer) 200\n4) (integer) 300\n5) (integer) 400\n6) (integer) 400\n7) (integer) 2000\n\n\nQuery the `- 500` range with `EXCLUDEEMPTY`. Time series `u`, whose only sample is at timestamp `2000`, has no samples in the range and is omitted from the reply.\n\n\n127.0.0.1:6379> TS.MRANGE - 500 WITHLABELS EXCLUDEEMPTY FILTER s=1\n1) 1) \"s\"\n   2) 1) 1) \"s\"\n         2) \"1\"\n      2) 1) \"t\"\n         2) \"1\"\n   3) 1) 1) (integer) 100\n         2) 100\n      2) 1) (integer) 200\n         2) 200\n      3) 1) (integer) 400\n         2) 400\n2) 1) \"t\"\n   2) 1) 1) \"s\"\n         2) \"1\"\n      2) 1) \"t\"\n         2) \"1\"\n   3) 1) 1) (integer) 100\n         2) 100\n      2) 1) (integer) 300\n         2) 300\n      3) 1) (integer) 400\n         2) 400\n\n\nWithout `EXCLUDEEMPTY`, `u` is also reported, with an empty samples list.\n\n\n127.0.0.1:6379> TS.MRANGE - 500 WITHLABELS FILTER s=1\n1) 1) \"s\"\n   ...\n2) 1) \"t\"\n   ...\n3) 1) \"u\"\n   2) 1) 1) \"s\"\n         2) \"1\"\n      2) 1) \"t\"\n         2) \"1\"\n   3) (empty array)\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nIf `GROUPBY label REDUCE reducer` is not specified:\n\n[Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): for each time series matching the specified filters, the following is reported:\n- [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings): The time series key name\n- [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): label-value pairs ([Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings), [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings))\n  - By default, an empty array is reported\n  - If `WITHLABELS` is specified, all labels associated with this time series are reported\n  - If `SELECTED_LABELS label...` is specified, the selected labels are reported (null value when no such label defined)\n- [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): representing all samples/aggregations matching the range:\n  - Without `AGGREGATION` or with a single aggregator:\n    timestamp-value pairs ([Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers), [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings)) representing (timestamp, value)\n  - With multiple aggregators:\n    timestamp-value tuples ([Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers), multiple [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings)) representing (timestamp, value...)\n\nIf `GROUPBY label REDUCE reducer` is specified:\n\n[Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): for each group of time series matching the specified filters, the following is reported:\n- [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) with the format `label=value` where `label` is the `GROUPBY` label argument\n- [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): label-value pairs ([Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings), [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings)):\n  - By default, an empty array is reported\n  - If `WITHLABELS` is specified, the `GROUPBY` label argument and value are reported\n  - If `SELECTED_LABELS label...` is specified, the selected labels are reported (null value when no such label defined or label does not have the same value for all grouped time series)\n- [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): either a single pair ([Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings), [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings)): the `GROUPBY` label argument and value, or empty array\n- [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): a single pair ([Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings), [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings)):  the string `__reducer__` and the reducer argument\n- [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): a single pair ([Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings), [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings)): the string `__source__` and the time series key names separated by \",\"\n- [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): representing all samples/aggregations matching the range:\n  - Without `AGGREGATION` or with a single aggregator:\n    timestamp-value pairs ([Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers), [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings)) representing (timestamp, value)\n  - With multiple aggregators:\n    timestamp-value tuples ([Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers), multiple [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings)) representing (timestamp, value...)\n\n**RESP3:**\n\nIf `GROUPBY label REDUCE reducer` is not specified:\n\n[Map reply](https://redis.io/docs/latest/develop/reference/protocol-spec#maps): for each time series matching the specified filters, the following is reported:\n- [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings): The time series key name\n- [Map reply](https://redis.io/docs/latest/develop/reference/protocol-spec#maps) or [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): label-value pairs\n  - By default, an empty map is reported\n  - If `WITHLABELS` is specified, all labels associated with this time series are reported as a map\n  - If `SELECTED_LABELS label...` is specified, the selected labels are reported as a map (null value when no such label defined)\n- Additional metadata including aggregators information\n- [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): representing all samples/aggregations matching the range:\n  - Without `AGGREGATION` or with a single aggregator:\n    timestamp-value pairs ([Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers), [Double reply](https://redis.io/docs/latest/develop/reference/protocol-spec#doubles)) representing (timestamp, value)\n  - With multiple aggregators:\n    timestamp-value tuples ([Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers), multiple [Double reply](https://redis.io/docs/latest/develop/reference/protocol-spec#doubles)) representing (timestamp, value...)\n\nIf `GROUPBY label REDUCE reducer` is specified:\n\nSimilar structure as RESP2 but with map-based organization for labels and metadata, and double values instead of string values."},{"id":"see-also","title":"See also","role":"related","text":"[`TS.RANGE`](https://redis.io/docs/latest/commands/ts.range) | [`TS.MREVRANGE`](https://redis.io/docs/latest/commands/ts.mrevrange) | [`TS.REVRANGE`](https://redis.io/docs/latest/commands/ts.revrange)"},{"id":"related-topics","title":"Related topics","role":"related","text":"[RedisTimeSeries](https://redis.io/docs/latest/develop/data-types/timeseries)"}],"examples":[]}
{"schema_version":2,"id":"commands/ts.mrevrange","title":"TS.MREVRANGE","url":"https://redis.io/docs/latest/commands/ts.mrevrange/","summary":"Query a range across multiple time-series by filters in reverse direction","since":"1.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"53e905d6fc4e43ed7cbf32cd045864773ac738559ca39b13f8d2599cb43ef99c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\n\nQuery a range across multiple time series by filters in the reverse direction. Starting from Redis 8.6, NaN values are included in raw measurement reports (queries without aggregation).\n\n> [!NOTE]\n> This command will reply only if the current user has read access to all keys that match the filter.\n> Otherwise, it will reply with \"*(error): current user doesn't have read permission to one or more keys that match the specified filter*\".\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>fromTimestamp</code></summary> \n\nis the start timestamp for the range query (integer Unix timestamp in milliseconds) or `-` to denote the timestamp of the earliest sample among all the time series that passes `FILTER filterExpr...`.\n</details>\n\n<details open>\n<summary><code>toTimestamp</code></summary> \n\nis the end timestamp for the range query (integer Unix timestamp in milliseconds) or `+` to denote the timestamp of the latest sample among all the time series that passes `FILTER filterExpr...`.\n</details>\n\n<details open>\n<summary><code>FILTER filterExpr...</code></summary> \n\nfilters time series based on their labels and label values.\n\nEach filter expression has one of the following syntaxes:\n\n  - `label!=` - the time series has a label named `label`\n  - `label=value` - the time series has a label named `label` with a value equal to `value`\n  - `label=(value1,value2,...)` - the time series has a label named `label` with a value equal to one of the values in the list\n  - `label=` - the time series does not have a label named `label`\n  - `label!=value` - the time series does not have a label named `label` with a value equal to `value`\n  - `label!=(value1,value2,...)` - the time series does not have a label named `label` with a value equal to any of the values in the list\n\n  <note><b>Notes:</b>\n   - At least one filter expression with a syntax `label=value` or `label=(value1,value2,...)` is required.\n   - Filter expressions are conjunctive. For example, the filter `type=temperature room=study` means that `type=temperature` AND `room=study`.\n   - Whitespace is not allowed in a filter expression except between quotes or double quotes in values - for example, `x=\"y y\"` or `x='(y y,z z)'`.\n   </note>\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open>\n<summary><code>LATEST</code> (since RedisTimeSeries 1.8)</summary> \n\nis used when a time series is a compaction. With `LATEST`, TS.MREVRANGE also reports the compacted value of the latest (possibly partial) bucket, given that this bucket's start time falls within `[fromTimestamp, toTimestamp]`. Without `LATEST`, TS.MREVRANGE does not report the latest (possibly partial) bucket. When a time series is not a compaction, `LATEST` is ignored.\n  \nThe data in the latest bucket of a compaction is possibly partial. A bucket is _closed_ and compacted only upon the arrival of a new sample that _opens_ a new _latest_ bucket. There are cases, however, when the compacted value of the latest (possibly partial) bucket is also required. In such a case, use `LATEST`.\n</details>\n\n<details open>\n<summary><code>FILTER_BY_TS ts...</code> (since RedisTimeSeries 1.6)</summary> \n\nfilters samples by a list of specific timestamps. A sample passes the filter if its exact timestamp is specified and falls within `[fromTimestamp, toTimestamp]`.\n\nWhen used together with `AGGREGATION`: samples are filtered before being aggregated.\n</details>\n\n<details open>\n<summary><code>FILTER_BY_VALUE min max</code> (since RedisTimeSeries 1.6)</summary> \n\nfilters samples by minimum and maximum values. `min` and `max` cannot be NaN values.\n\nWhen used together with `AGGREGATION`: samples are filtered before being aggregated.\n</details>\n\n<details open>\n<summary><code>WITHLABELS</code></summary> \n\nincludes in the reply all label-value pairs representing metadata labels of the time series. \nIf `WITHLABELS` or `SELECTED_LABELS` are not specified, by default, an empty list is reported as label-value pairs.\n</details>\n\n<details open>\n<summary><code>SELECTED_LABELS label...</code> (since RedisTimeSeries 1.6)</summary>\n\nreturns a subset of the label-value pairs that represent metadata labels of the time series. \nUse when a large number of labels exists per series, but only the values of some of the labels are required. \nIf `WITHLABELS` or `SELECTED_LABELS` are not specified, by default, an empty list is reported as label-value pairs.\n</details>\n\n<details open>\n<summary><code>COUNT count</code></summary> \n\nWhen used without `AGGREGATION`: limits the number of reported samples per time series.\n\nWhen used together with `AGGREGATION`: limits the number of reported buckets.\n</details>\n\n<details open>\n<summary><code>ALIGN align</code> (since RedisTimeSeries 1.6)</summary> \n\nis a time bucket alignment control for `AGGREGATION`. It controls the time bucket timestamps by changing the reference timestamp on which a bucket is defined. \n\nValues include:\n   \n - `start` or `-`: The reference timestamp will be the query start interval time (`fromTimestamp`) which can't be `-`\n - `end` or `+`: The reference timestamp will be the query end interval time (`toTimestamp`) which can't be `+`\n - A specific timestamp: align the reference timestamp to a specific time\n   \n<note><b>Note:</b> When not provided, alignment is set to `0`.</note>\n</details>\n\n<details open>\n<summary><code>AGGREGATION aggregators bucketDuration</code></summary> \n\nfor each time series, aggregates samples into time buckets, where:\n\n  - `aggregators` is one or more comma-separated aggregators from the following table:\n\n    | aggregator   | Description                                                     |\n    | ------------ | --------------------------------------------------------------- |\n    | `avg`        | Arithmetic mean of all non-NaN values                           |\n    | `sum`        | Sum of all non-NaN values                                       |\n    | `min`        | Minimum non-NaN value                                           |\n    | `max`        | Maximum non-NaN value                                           |\n    | `range`      | Difference between the maximum and the minimum non-NaN values   |\n    | `count`      | Number of non-NaN values                                        |\n    | `countNaN`   | Number of NaN values (since Redis 8.6)                          |\n    | `countAll`   | Number of values, including NaN and non-NaN (since Redis 8.6)   |\n    | `first`      | The non-NaN value with the lowest timestamp in the bucket       |\n    | `last`       | The non-NaN value with the highest timestamp in the bucket      |\n    | `std.p`      | Population standard deviation of the non-NaN values             |\n    | `std.s`      | Sample standard deviation of the non-NaN values                 |\n    | `var.p`      | Population variance of the non-NaN values                       |\n    | `var.s`      | Sample variance of the non-NaN values                           |\n    | `twa`        | Time-weighted average over the bucket's timeframe (ignores NaN values) (since RedisTimeSeries 1.8) |\n\n  - `bucketDuration` is duration of each bucket, in milliseconds.\n\n  Note: `aggregators` can either be a single aggregator or multiple aggregators separated by commas as shown below.\n  No whitespace is allowed between aggregators.\n\n  AGGREGATION min,avg,max\n  \n  Without `ALIGN`, bucket start times are multiples of `bucketDuration`.\n  \n  With `ALIGN align`, bucket start times are multiples of `bucketDuration` with remainder `align % bucketDuration`.\n  \n  The first bucket start time is less than or equal to `fromTimestamp`.  \n</details>\n\n<details open>\n<summary><code>[BUCKETTIMESTAMP bt]</code> (since RedisTimeSeries 1.8)</summary> \n\ncontrols how bucket timestamps are reported.\n\n| `bt`             | Timestamp reported for each bucket                            |\n| ---------------- | ------------------------------------------------------------- |\n| `-` or `start`   | the bucket's start time (default)                             |\n| `+` or `end`     | the bucket's end time                                         |\n| `~` or `mid`     | the bucket's mid time (rounded down if not an integer)        |\n</details>\n\n<details open>\n<summary><code>[EMPTY]</code> (since RedisTimeSeries 1.8)</summary> \n\nis a flag, which, when specified, reports aggregations also for empty buckets.\n\n| aggregator           | Value reported for each empty bucket |\n| -------------------- | ------------------------------------ |\n| `sum`, `count`       | `0`                                  |\n| `last`               | The value of the last sample before the bucket's start. `NaN` when no such sample. |\n| `twa`                | Average value over the bucket's timeframe based on linear interpolation of the last sample before the bucket's start and the first sample after the bucket's end. `NaN` when no such samples. |\n| `min`, `max`, `range`, `avg`, `first`, `std.p`, `std.s` | `NaN` |\n\nRegardless of the values of `fromTimestamp` and `toTimestamp`, no data is reported for buckets that end before the earliest sample or begin after the latest sample in the time series.\n</details>\n\n<details open>\n<summary><code>GROUPBY label REDUCE reducer</code> (since RedisTimeSeries 1.6)</summary>\n\nsplits time series into groups, each group contains time series that share the same value for the provided label name, then aggregates results in each group.\n\nWhen combined with `AGGREGATION` the `GROUPBY`/`REDUCE` is applied post aggregation stage.\n`GROUPBY`/`REDUCE` is not permitted when multiple aggregators are specified.\n\n  - `label` is label name. A group is created for all time series that share the same value for this label.\n\n  - `reducer` is an aggregation type used to aggregate the results in each group.\n\n    | `reducer` | Description                                                                                                      |\n    | --------- | ---------------------------------------------------------------------------------------------------------------- |\n    | `avg`     | Arithmetic mean of all non-NaN values; NaN if no such values (since RedisTimeSeries 1.8)                         |\n    | `sum`     | Sum of all non-NaN values; NaN if no such values                                                                 |\n    | `min`     | Minimum non-NaN value; NaN if no such values                                                                     |\n    | `max`     | Maximum non-NaN value; NaN if no such values                                                                     |\n    | `range`   | Difference between the maximum and the minimum non-NaN values; NaN if no such values (since RedisTimeSeries 1.8) |\n    | `count`   | Number of non-NaN values (since RedisTimeSeries 1.8)                                                             |\n    | `countNaN`| Number of NaN values (since Redis 8.6)                                                                           |\n    | `countAll`| Number of values, including NaN and non-NaN (since Redis 8.6)                                                     |\n    | `std.p`   | Population standard deviation of the non-NaN values; NaN if no such values (since RedisTimeSeries 1.8)           |\n    | `std.s`   | Sample standard deviation of the non-NaN values; NaN if no such values (since RedisTimeSeries 1.8)              |\n    | `var.p`   | Population variance of the non-NaN values; NaN if no such values (since RedisTimeSeries 1.8)                     |\n    | `var.s`   | Sample variance of the non-NaN values; NaN if no such values (since RedisTimeSeries 1.8)                         |\n\n<note><b>Notes:</b> \n  - The produced time series is named `<label>=<value>`\n  - The produced time series contains two labels with these label array structures:\n    - `__reducer__`, the reducer used (e.g., `\"count\"`)\n    - `__source__`, the list of time series keys used to compute the grouped series (e.g., `\"key1,key2,key3\"`)\n</note>\n</details>\n\n<details open>\n<summary><code>EXCLUDEEMPTY</code> (since Redis 8.10)</summary>\n\nexcludes from the reply any time series that has no samples in the requested range. By default, every time series that passes `FILTER filterExpr...` is reported, even those with no samples in the range (reported with an empty samples list).\n\nA time series whose only samples in the range are NaN is not considered empty and is still reported.\n\n`EXCLUDEEMPTY` cannot be used together with `GROUPBY label REDUCE reducer`; combining them replies with an error.\n</details>\n\n<note><b>Note:</b> An `MREVRANGE` command cannot be part of a transaction when running on a Redis cluster.</note>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Retrieve maximum stock price per timestamp</b></summary>\n\nCreate two stocks and add their prices at three different timestamps.\n\n\n127.0.0.1:6379> TS.CREATE stock:A LABELS type stock name A\nOK\n127.0.0.1:6379> TS.CREATE stock:B LABELS type stock name B\nOK\n127.0.0.1:6379> TS.MADD stock:A 1000 100 stock:A 1010 110 stock:A 1020 120\n1) (integer) 1000\n2) (integer) 1010\n3) (integer) 1020\n127.0.0.1:6379> TS.MADD stock:B 1000 120 stock:B 1010 110 stock:B 1020 100\n1) (integer) 1000\n2) (integer) 1010\n3) (integer) 1020\n\n\nYou can now retrieve the maximum stock price per timestamp.\n\n\n127.0.0.1:6379> TS.MREVRANGE - + WITHLABELS FILTER type=stock GROUPBY type REDUCE max\n1) 1) \"type=stock\"\n   2) 1) 1) \"type\"\n         2) \"stock\"\n      2) 1) \"__reducer__\"\n         2) \"max\"\n      3) 1) \"__source__\"\n         2) \"stock:A,stock:B\"\n   3) 1) 1) (integer) 1020\n         2) 120\n      2) 1) (integer) 1010\n         2) 110\n      3) 1) (integer) 1000\n         2) 120\n\n\nThe `FILTER type=stock` clause returns a single time series representing stock prices. The `GROUPBY type REDUCE max` clause splits the time series into groups with identical type values, and then, for each timestamp, aggregates all series that share the same type value using the max aggregator.\n</details>\n\n<details open>\n<summary><b>Calculate average stock price and retrieve maximum average</b></summary> \n\nCreate two stocks and add their prices at nine different timestamps.\n\n\n127.0.0.1:6379> TS.CREATE stock:A LABELS type stock name A\nOK\n127.0.0.1:6379> TS.CREATE stock:B LABELS type stock name B\nOK\n127.0.0.1:6379> TS.MADD stock:A 1000 100 stock:A 1010 110 stock:A 1020 120\n1) (integer) 1000\n2) (integer) 1010\n3) (integer) 1020\n127.0.0.1:6379> TS.MADD stock:B 1000 120 stock:B 1010 110 stock:B 1020 100\n1) (integer) 1000\n2) (integer) 1010\n3) (integer) 1020\n127.0.0.1:6379> TS.MADD stock:A 2000 200 stock:A 2010 210 stock:A 2020 220\n1) (integer) 2000\n2) (integer) 2010\n3) (integer) 2020\n127.0.0.1:6379> TS.MADD stock:B 2000 220 stock:B 2010 210 stock:B 2020 200\n1) (integer) 2000\n2) (integer) 2010\n3) (integer) 2020\n127.0.0.1:6379> TS.MADD stock:A 3000 300 stock:A 3010 310 stock:A 3020 320\n1) (integer) 3000\n2) (integer) 3010\n3) (integer) 3020\n127.0.0.1:6379> TS.MADD stock:B 3000 320 stock:B 3010 310 stock:B 3020 300\n1) (integer) 3000\n2) (integer) 3010\n3) (integer) 3020\n\n\nNow, for each stock, calculate the average stock price per a 1000-millisecond timeframe, and then retrieve the stock with the maximum average for that timeframe in reverse direction.\n\n\n127.0.0.1:6379> TS.MREVRANGE - + WITHLABELS AGGREGATION avg 1000 FILTER type=stock GROUPBY type REDUCE max\n1) 1) \"type=stock\"\n   2) 1) 1) \"type\"\n         2) \"stock\"\n      2) 1) \"__reducer__\"\n         2) \"max\"\n      3) 1) \"__source__\"\n         2) \"stock:A,stock:B\"\n   3) 1) 1) (integer) 3000\n         2) 310\n      2) 1) (integer) 2000\n         2) 210\n      3) 1) (integer) 1000\n         2) 110\n\n</details>\n\n<details open>\n<summary><b>Group query results</b></summary>\n\nQuery all time series with the metric label equal to `cpu`, then group the time series by the value of their `metric_name` label value and for each group return the maximum value and the time series keys (_source_) with that value.\n\n\n127.0.0.1:6379> TS.ADD ts1 1548149180000 90 labels metric cpu metric_name system\n(integer) 1548149180000\n127.0.0.1:6379> TS.ADD ts1 1548149185000 45\n(integer) 1548149185000\n127.0.0.1:6379> TS.ADD ts2 1548149180000 99 labels metric cpu metric_name user\n(integer) 1548149180000\n127.0.0.1:6379> TS.MREVRANGE - + WITHLABELS FILTER metric=cpu GROUPBY metric_name REDUCE max\n1) 1) \"metric_name=system\"\n   2) 1) 1) \"metric_name\"\n         2) \"system\"\n      2) 1) \"__reducer__\"\n         2) \"max\"\n      3) 1) \"__source__\"\n         2) \"ts1\"\n   3) 1) 1) (integer) 1548149185000\n         2) 45\n      2) 1) (integer) 1548149180000\n         2) 90\n2) 1) \"metric_name=user\"\n   2) 1) 1) \"metric_name\"\n         2) \"user\"\n      2) 1) \"__reducer__\"\n         2) \"max\"\n      3) 1) \"__source__\"\n         2) \"ts2\"\n   3) 1) 1) (integer) 1548149180000\n         2) 99\n\n</details>\n\n<details open>\n<summary><b>Filter query by value</b></summary>\n\nQuery all time series with the metric label equal to `cpu`, then filter values larger or equal to 90.0 and smaller or equal to 100.0.\n\n\n127.0.0.1:6379> TS.ADD ts1 1548149180000 90 labels metric cpu metric_name system\n(integer) 1548149180000\n127.0.0.1:6379> TS.ADD ts1 1548149185000 45\n(integer) 1548149185000\n127.0.0.1:6379> TS.ADD ts2 1548149180000 99 labels metric cpu metric_name user\n(integer) 1548149180000\n127.0.0.1:6379> TS.MREVRANGE - + FILTER_BY_VALUE 90 100 WITHLABELS FILTER metric=cpu\n1) 1) \"ts1\"\n   2) 1) 1) \"metric\"\n         2) \"cpu\"\n      2) 1) \"metric_name\"\n         2) \"system\"\n   3) 1) 1) (integer) 1548149180000\n         2) 90\n2) 1) \"ts2\"\n   2) 1) 1) \"metric\"\n         2) \"cpu\"\n      2) 1) \"metric_name\"\n         2) \"user\"\n   3) 1) 1) (integer) 1548149180000\n         2) 99\n\n</details>\n\n<details open>\n<summary><b>Query using a label</b></summary>\n\nQuery all time series with the metric label equal to `cpu`, but only return the team label.\n\n\n127.0.0.1:6379> TS.ADD ts1 1548149180000 90 labels metric cpu metric_name system team NY\n(integer) 1548149180000\n127.0.0.1:6379> TS.ADD ts1 1548149185000 45\n(integer) 1548149185000\n127.0.0.1:6379> TS.ADD ts2 1548149180000 99 labels metric cpu metric_name user team SF\n(integer) 1548149180000\n127.0.0.1:6379> TS.MREVRANGE - + SELECTED_LABELS team FILTER metric=cpu\n1) 1) \"ts1\"\n   2) 1) 1) \"team\"\n         2) (nil)\n   3) 1) 1) (integer) 1548149185000\n         2) 45\n      2) 1) (integer) 1548149180000\n         2) 90\n2) 1) \"ts2\"\n   2) 1) 1) \"team\"\n         2) (nil)\n   3) 1) 1) (integer) 1548149180000\n         2) 99\n\n</details>\n\n<details open>\n<summary><b>Exclude empty time series from the reply</b></summary>\n\nCreate three time series that share the label `s=1`, then add samples so that only two of them have data in the `- 500` range.\n\n\n127.0.0.1:6379> TS.CREATE s LABELS s 1 t 1\nOK\n127.0.0.1:6379> TS.CREATE t LABELS s 1 t 1\nOK\n127.0.0.1:6379> TS.CREATE u LABELS s 1 t 1\nOK\n127.0.0.1:6379> TS.MADD s 100 100 t 100 100 s 200 200 t 300 300 s 400 400 t 400 400 u 2000 2000\n1) (integer) 100\n2) (integer) 100\n3) (integer) 200\n4) (integer) 300\n5) (integer) 400\n6) (integer) 400\n7) (integer) 2000\n\n\nQuery the `- 500` range with `EXCLUDEEMPTY`. Time series `u`, whose only sample is at timestamp `2000`, has no samples in the range and is omitted from the reply.\n\n\n127.0.0.1:6379> TS.MREVRANGE - 500 WITHLABELS EXCLUDEEMPTY FILTER s=1\n1) 1) \"s\"\n   2) 1) 1) \"s\"\n         2) \"1\"\n      2) 1) \"t\"\n         2) \"1\"\n   3) 1) 1) (integer) 400\n         2) 400\n      2) 1) (integer) 200\n         2) 200\n      3) 1) (integer) 100\n         2) 100\n2) 1) \"t\"\n   2) 1) 1) \"s\"\n         2) \"1\"\n      2) 1) \"t\"\n         2) \"1\"\n   3) 1) 1) (integer) 400\n         2) 400\n      2) 1) (integer) 300\n         2) 300\n      3) 1) (integer) 100\n         2) 100\n\n\nWithout `EXCLUDEEMPTY`, `u` is also reported, with an empty samples list.\n\n\n127.0.0.1:6379> TS.MREVRANGE - 500 WITHLABELS FILTER s=1\n1) 1) \"s\"\n   ...\n2) 1) \"t\"\n   ...\n3) 1) \"u\"\n   2) 1) 1) \"s\"\n         2) \"1\"\n      2) 1) \"t\"\n         2) \"1\"\n   3) (empty array)\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nIf `GROUPBY label REDUCE reducer` is not specified:\n\n[Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): for each time series matching the specified filters, the following is reported:\n- [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings): The time series key name\n- [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): label-value pairs ([Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings), [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings))\n  - By default, an empty array is reported\n  - If `WITHLABELS` is specified, all labels associated with this time series are reported\n  - If `SELECTED_LABELS label...` is specified, the selected labels are reported\n- [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): representing all samples/aggregations matching the range in reverse chronological order:\n  - Without `AGGREGATION` or with a single aggregator:\n    timestamp-value pairs ([Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers), [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings)) representing (timestamp, value)\n  - With multiple aggregators:\n    timestamp-value tuples ([Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers), multiple [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings)) representing (timestamp, value...)\n\nIf `GROUPBY label REDUCE reducer` is specified:\n\n[Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): for each group of time series matching the specified filters, the following is reported:\n- [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) with the format `label=value` where `label` is the `GROUPBY` label argument\n- [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): a single pair ([Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings), [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings)): the `GROUPBY` label argument and value\n- [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): a single pair ([Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings), [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings)):  the string `__reducer__` and the reducer argument\n- [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): a single pair ([Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings), [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings)): the string `__source__` and the time series key names separated by \",\"\n- [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): representing all samples/aggregations matching the range in reverse chronological order:\n  - Without `AGGREGATION` or with a single aggregator:\n    timestamp-value pairs ([Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers), [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings)) representing (timestamp, value)\n  - With multiple aggregators:\n    timestamp-value tuples ([Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers), multiple [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings)) representing (timestamp, value...)\n\n**RESP3:**\n\nIf `GROUPBY label REDUCE reducer` is not specified:\n\n[Map reply](https://redis.io/docs/latest/develop/reference/protocol-spec#maps): for each time series matching the specified filters, the following is reported:\n- [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings): The time series key name\n- [Map reply](https://redis.io/docs/latest/develop/reference/protocol-spec#maps) or [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): label-value pairs\n  - By default, an empty map is reported\n  - If `WITHLABELS` is specified, all labels associated with this time series are reported as a map\n  - If `SELECTED_LABELS label...` is specified, the selected labels are reported as a map\n- Additional metadata including aggregators information\n- [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): representing all samples/aggregations matching the range in reverse chronological order:\n  - Without `AGGREGATION` or with a single aggregator:\n    timestamp-value pairs ([Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers), [Double reply](https://redis.io/docs/latest/develop/reference/protocol-spec#doubles)) representing (timestamp, value)\n  - With multiple aggregators:\n    timestamp-value tuples ([Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers), multiple [Double reply](https://redis.io/docs/latest/develop/reference/protocol-spec#doubles)) representing (timestamp, value...)\n\nIf `GROUPBY label REDUCE reducer` is specified:\n\nSimilar structure as RESP2 but with map-based organization for labels and metadata, and double values instead of string values."},{"id":"see-also","title":"See also","role":"related","text":"[`TS.MRANGE`](https://redis.io/docs/latest/commands/ts.mrange) | [`TS.RANGE`](https://redis.io/docs/latest/commands/ts.range) | [`TS.REVRANGE`](https://redis.io/docs/latest/commands/ts.revrange)"},{"id":"related-topics","title":"Related topics","role":"related","text":"[RedisTimeSeries](https://redis.io/docs/latest/develop/data-types/timeseries)"}],"examples":[]}
{"schema_version":2,"id":"commands/ts.nrange","title":"TS.NRANGE","url":"https://redis.io/docs/latest/commands/ts.nrange/","summary":"Query a range across multiple time series in forward direction, returning the results grouped by timestamp","since":"8.10.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"b98b90f589cd1244c3e014137f69dccede736bef2512081073095e6acc555728","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Query a range across an explicit list of time series in the forward direction. `TS.NRANGE` groups results by timestamp: for each timestamp, it returns the timestamp followed by one value per time series, in the order the keys are given. With `AGGREGATION`, a time series can contribute more than one value per timestamp — one for each aggregator requested. A time series with no sample at a given timestamp returns `NaN` for that timestamp.\n\n> [!NOTE]\n> In a Redis cluster, all specified keys must map to the same hash slot. `TS.NRANGE` is a [single hash slot](https://redis.io/docs/latest/operate/oss_and_stack/reference/cluster-spec#key-distribution-model) command; it does not split a request across shards or merge replies from multiple hash slots.\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>numkeys</code></summary>\n\nis the number of time series keys that follow. It must be a positive integer and must equal the number of `key` arguments.\n</details>\n\n<details open>\n<summary><code>key [key ...]</code></summary>\n\nare the explicit time series keys to query. Key order and duplicate keys are significant: the reply has one value per `key` argument, in the order the keys are given. A repeated key produces a separate value for each occurrence.\n</details>\n\n<details open>\n<summary><code>fromTimestamp</code></summary>\n\nis the start timestamp for the range query (integer Unix timestamp in milliseconds) or `-` to denote the timestamp of the earliest sample among the specified time series. The range is inclusive.\n</details>\n\n<details open>\n<summary><code>toTimestamp</code></summary>\n\nis the end timestamp for the range query (integer Unix timestamp in milliseconds) or `+` to denote the timestamp of the latest sample among the specified time series. The range is inclusive.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open>\n<summary><code>LATEST</code></summary>\n\nis used when a time series is a compaction. With `LATEST`, TS.NRANGE also reports the compacted value of the latest, possibly partial, bucket, given that this bucket's start time falls within `[fromTimestamp, toTimestamp]`. Without `LATEST`, TS.NRANGE does not report the latest, possibly partial, bucket. When a time series is not a compaction, `LATEST` is ignored.\n\nThe data in the latest bucket of a compaction is possibly partial. A bucket is _closed_ and compacted only upon arrival of a new sample that _opens_ a new _latest_ bucket. There are cases, however, when the compacted value of the latest, possibly partial, bucket is also required. In such a case, use `LATEST`.\n</details>\n\n<details open>\n<summary><code>FILTER_BY_TS ts...</code></summary>\n\nfilters samples by a list of specific timestamps. A sample passes the filter if its exact timestamp is specified and falls within `[fromTimestamp, toTimestamp]`. Samples are filtered before pivoting.\n\nWhen used together with `AGGREGATION`: samples are filtered before being aggregated.\n</details>\n\n<details open>\n<summary><code>FILTER_BY_VALUE min max</code></summary>\n\nfilters samples by minimum and maximum values. A sample passes the filter if its value is within the inclusive range `[min, max]`. `min` and `max` cannot be NaN values. Samples are filtered before pivoting.\n\nWhen used together with `AGGREGATION`: samples are filtered before being aggregated.\n</details>\n\n<details open>\n<summary><code>COUNT count</code></summary>\n\nlimits the number of returned entries, keeping those with the lowest timestamps. The limit is applied after the per-timestamp merge.\n</details>\n\n<details open>\n<summary><code>ALIGN align</code></summary>\n\nis a time bucket alignment control for `AGGREGATION`. It controls the time bucket timestamps by changing the reference timestamp on which a bucket is defined.\n\n`align` values include:\n\n - `start` or `-`: The reference timestamp will be the query start interval time (`fromTimestamp`) which can't be `-`\n - `end` or `+`: The reference timestamp will be the query end interval time (`toTimestamp`) which can't be `+`\n - A specific timestamp: align the reference timestamp to a specific time\n\n<note><b>Note:</b> When not provided, alignment is set to `0`.</note>\n</details>\n\n<details open>\n<summary><code>AGGREGATION aggregators [aggregators ...] bucketDuration</code></summary>\n\naggregates samples into time buckets.\n\nProvide one or more aggregators per key, as a separate argument in the same order as the keys: the first argument applies to the first key, and so on. The number of these arguments must equal `numkeys`, and all keys share the same `bucketDuration`.\n\nEach per-key argument is either a single aggregator or a comma-separated list of aggregators (for example `avg,max`), exactly as in [`TS.RANGE`](https://redis.io/docs/latest/commands/ts.range); no whitespace is allowed. A key contributes one value for each aggregator you list for it, and a key's values appear together in the reply, in the order you list its aggregators. To compute several aggregations for one series, give that key a comma-separated list such as `min,max`.\n\n  - each `aggregator` is one of the following:\n\n    | aggregator   | Description                                                     |\n    | ------------ | --------------------------------------------------------------- |\n    | `avg`        | Arithmetic mean of all non-NaN values                           |\n    | `sum`        | Sum of all non-NaN values                                       |\n    | `min`        | Minimum non-NaN value                                           |\n    | `max`        | Maximum non-NaN value                                           |\n    | `range`      | Difference between the maximum and the minimum non-NaN values   |\n    | `count`      | Number of non-NaN values                                        |\n    | `countNaN`   | Number of NaN values                                            |\n    | `countAll`   | Number of values, including NaN and non-NaN                     |\n    | `first`      | The non-NaN value with the lowest timestamp in the bucket       |\n    | `last`       | The non-NaN value with the highest timestamp in the bucket      |\n    | `std.p`      | Population standard deviation of the non-NaN values             |\n    | `std.s`      | Sample standard deviation of the non-NaN values                 |\n    | `var.p`      | Population variance of the non-NaN values                       |\n    | `var.s`      | Sample variance of the non-NaN values                           |\n    | `twa`        | Time-weighted average over the bucket's timeframe (ignores NaN values) |\n\n  - `bucketDuration` is the duration of each bucket, in milliseconds.\n\n  Without `ALIGN`, bucket start times are multiples of `bucketDuration`.\n\n  With `ALIGN align`, bucket start times are multiples of `bucketDuration` with remainder `align % bucketDuration`.\n\n  The first bucket start time is less than or equal to `fromTimestamp`.\n</details>\n\n<details open>\n<summary><code>[BUCKETTIMESTAMP bt]</code></summary>\n\ncontrols how bucket timestamps are reported.\n\n| `bt`             | Timestamp reported for each bucket                            |\n| ---------------- | ------------------------------------------------------------- |\n| `-` or `start`   | the bucket's start time (default)                             |\n| `+` or `end`     | the bucket's end time                                         |\n| `~` or `mid`     | the bucket's mid time (rounded down if not an integer)        |\n</details>\n\n<details open>\n<summary><code>[EMPTY]</code></summary>\n\nis a flag, which, when specified, reports aggregations also for empty buckets.\n\n| aggregator           | Value reported for each empty bucket |\n| -------------------- | ------------------------------------ |\n| `sum`, `count`       | `0`                                  |\n| `last`               | The value of the last sample before the bucket's start. `NaN` when no such sample. |\n| `twa`                | Average value over the bucket's timeframe based on linear interpolation of the last sample before the bucket's start and the first sample after the bucket's end. `NaN` when no such samples. |\n| `min`, `max`, `range`, `avg`, `first`, `std.p`, `std.s` | `NaN` |\n\nRegardless of the values of `fromTimestamp` and `toTimestamp`, no data is reported for buckets that end before the earliest sample or begin after the latest sample in the time series. When a bucket is reported for one key but a different key has no data in that bucket, that key's value is `NaN`.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Pivot raw samples from multiple series by timestamp</b></summary>\n\nCreate two time series and add samples at partially overlapping timestamps.\n\n\nredis> TS.CREATE {sensor}:1\nOK\nredis> TS.CREATE {sensor}:2\nOK\nredis> TS.MADD {sensor}:1 1000 10 {sensor}:1 2000 12\n1) (integer) 1000\n2) (integer) 2000\nredis> TS.MADD {sensor}:2 1000 20 {sensor}:2 3000 25\n1) (integer) 1000\n2) (integer) 3000\n\n\nQuery both series and group the samples by timestamp. One entry is returned for every distinct timestamp produced by at least one key, with the values ordered by input key. A key with no sample at that timestamp has a `NaN` value.\n\n\nredis> TS.NRANGE 2 {sensor}:1 {sensor}:2 - +\n1) 1) (integer) 1000\n   2) 1) 10\n      2) 20\n2) 1) (integer) 2000\n   2) 1) 12\n      2) NaN\n3) 1) (integer) 3000\n   2) 1) NaN\n      2) 25\n\n</details>\n\n<details open>\n<summary><b>Aggregate each series with a per-key aggregator</b></summary>\n\nIn aggregation mode, supply one aggregator per key, in key order. Here `{sensor}:3` is aggregated with `avg` and `{sensor}:4` with `sum`, both over 1000-millisecond buckets.\n\n\nredis> TS.CREATE {sensor}:3\nOK\nredis> TS.CREATE {sensor}:4\nOK\nredis> TS.MADD {sensor}:3 1000 10 {sensor}:3 1100 20 {sensor}:3 2000 30\n1) (integer) 1000\n2) (integer) 1100\n3) (integer) 2000\nredis> TS.MADD {sensor}:4 1000 5 {sensor}:4 1100 15 {sensor}:4 2000 25\n1) (integer) 1000\n2) (integer) 1100\n3) (integer) 2000\nredis> TS.NRANGE 2 {sensor}:3 {sensor}:4 - + AGGREGATION avg sum 1000\n1) 1) (integer) 1000\n   2) 1) 15\n      2) 20\n2) 1) (integer) 2000\n   2) 1) 30\n      2) 25\n\n</details>\n\n<details open>\n<summary><b>Apply multiple aggregators to a series</b></summary>\n\nTo compute several aggregators for a single key, pass them as a comma-separated list. Here `{sensor}:3` uses `avg,max` (two values) and `{sensor}:4` uses `sum` (one value). Each timestamp's values are a single flat list: `{sensor}:3`'s `avg`, then its `max`, then `{sensor}:4`'s `sum`.\n\n\nredis> TS.NRANGE 2 {sensor}:3 {sensor}:4 - + AGGREGATION avg,max sum 1000\n1) 1) (integer) 1000\n   2) 1) 15\n      2) 20\n      3) 20\n2) 1) (integer) 2000\n   2) 1) 30\n      2) 30\n      3) 25\n\n</details>"},{"id":"details","title":"Details","role":"content","text":""},{"id":"semantics","title":"Semantics","role":"content","text":"`TS.NRANGE` behaves like running a compatible [`TS.RANGE`](https://redis.io/docs/latest/commands/ts.range) over each input key and then performing a server-side outer join by timestamp. Rows are returned from the lowest timestamp to the highest.\n\nIn raw mode (no `AGGREGATION`):\n\n- One entry is returned for every distinct timestamp produced by at least one key.\n- If a key has no sample at that timestamp, that key's value is `NaN`.\n\nIn aggregation mode (with `AGGREGATION`):\n\n- One aggregation applies to each key, in key order; the number of these arguments must equal `numkeys`, and all keys share one `bucketDuration`.\n- Each per-key argument is a single aggregator or a comma-separated list of aggregators, and a key contributes one value per aggregator.\n- Each timestamp's values form a single flat list, ordered by key and, within each key, by the order its aggregators are listed. For example, `avg,max` for the first key and `sum` for the second produce values of the form `[avg, max, sum]`.\n- When a key has no data for a given bucket, all of that key's values are `NaN`.\n- With `EMPTY`, empty buckets can produce rows in which every value is `NaN`."},{"id":"nan-values","title":"NaN values","role":"content","text":"A `NaN` value can mean that a key had no sample at that timestamp or no samples at that time bucket, or that the key stored or aggregated to a real `NaN`. These two cases are indistinguishable in the reply.\n\n| Case                                            | Value                                           |\n| ----------------------------------------------- | ----------------------------------------------- |\n| Key has a sample at that timestamp               | The sample value                                |\n| Key has no sample at that timestamp              | `NaN`                                           |\n| Key has aggregated data for that time bucket     | The aggregated value                            |\n| Key has no samples at that time bucket           | `NaN`                                           |\n| Key stores or aggregates to a real `NaN`         | `NaN`, indistinguishable from no data           |"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Not supported</span><br /> | <span title=\"Not supported\">&#x274c; Flexible & Annual</span><br /><span title=\"Not supported\">&#x274c; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) with one entry per timestamp, ordered by increasing timestamp. Each entry is an [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) composed of an [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) (the timestamp) and a flat [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) values. The values are concatenated across keys in input order; with `AGGREGATION`, each key contributes one value per aggregator, otherwise one value per key. A missing value is reported as `NaN`. The reply is an empty array when no samples match.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, wrong number of aggregators, unknown aggregation type, wrong key type, etc.\n\n**RESP3:**\n\nOne of the following:\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) with one entry per timestamp, ordered by increasing timestamp. Each entry is an [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) composed of an [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) (the timestamp) and a flat [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [Double reply](https://redis.io/docs/latest/develop/reference/protocol-spec#doubles) values. The values are concatenated across keys in input order; with `AGGREGATION`, each key contributes one value per aggregator, otherwise one value per key. A missing value is reported as `NaN`. The reply is an empty array when no samples match.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, wrong number of aggregators, unknown aggregation type, wrong key type, etc."},{"id":"see-also","title":"See also","role":"related","text":"[`TS.NREVRANGE`](https://redis.io/docs/latest/commands/ts.nrevrange) | [`TS.RANGE`](https://redis.io/docs/latest/commands/ts.range) | [`TS.MRANGE`](https://redis.io/docs/latest/commands/ts.mrange)"},{"id":"related-topics","title":"Related topics","role":"related","text":"[RedisTimeSeries](https://redis.io/docs/latest/develop/data-types/timeseries)"}],"examples":[]}
{"schema_version":2,"id":"commands/ts.nrevrange","title":"TS.NREVRANGE","url":"https://redis.io/docs/latest/commands/ts.nrevrange/","summary":"Query a range across multiple time series in reverse direction, returning the results grouped by timestamp","since":"8.10.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"ef731e39321379d701222fdb9dbf832558a78bbd0aacde42368c9ecbfeb103e3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Query a range across an explicit list of time series in the reverse direction. `TS.NREVRANGE` groups results by timestamp: for each timestamp, it returns the timestamp followed by one value per time series, in the order the keys are given. With `AGGREGATION`, a time series can contribute more than one value per timestamp — one for each aggregator requested. A time series with no sample at a given timestamp returns `NaN` for that timestamp.\n\n> [!NOTE]\n> In a Redis cluster, all specified keys must map to the same hash slot. `TS.NREVRANGE` is a [single hash slot](https://redis.io/docs/latest/operate/oss_and_stack/reference/cluster-spec#key-distribution-model) command; it does not split a request across shards or merge replies from multiple hash slots.\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>numkeys</code></summary>\n\nis the number of time series keys that follow. It must be a positive integer and must equal the number of `key` arguments.\n</details>\n\n<details open>\n<summary><code>key [key ...]</code></summary>\n\nare the explicit time series keys to query. Key order and duplicate keys are significant: the output has one value per `key` argument, in the order the keys are given. A repeated key produces a separate value for each occurrence.\n</details>\n\n<details open>\n<summary><code>fromTimestamp</code></summary>\n\nis the start timestamp for the range query (integer Unix timestamp in milliseconds) or `-` to denote the timestamp of the earliest sample among the specified time series. The range is inclusive.\n</details>\n\n<details open>\n<summary><code>toTimestamp</code></summary>\n\nis the end timestamp for the range query (integer Unix timestamp in milliseconds) or `+` to denote the timestamp of the latest sample among the specified time series. The range is inclusive.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open>\n<summary><code>LATEST</code></summary>\n\nis used when a time series is a compaction. With `LATEST`, TS.NREVRANGE also reports the compacted value of the latest, possibly partial, bucket, given that this bucket's start time falls within `[fromTimestamp, toTimestamp]`. Without `LATEST`, TS.NREVRANGE does not report the latest, possibly partial, bucket. When a time series is not a compaction, `LATEST` is ignored.\n\nThe data in the latest bucket of a compaction is possibly partial. A bucket is _closed_ and compacted only upon arrival of a new sample that _opens_ a new _latest_ bucket. There are cases, however, when the compacted value of the latest, possibly partial, bucket is also required. In such a case, use `LATEST`.\n</details>\n\n<details open>\n<summary><code>FILTER_BY_TS ts...</code></summary>\n\nfilters samples by a list of specific timestamps. A sample passes the filter if its exact timestamp is specified and falls within `[fromTimestamp, toTimestamp]`. Samples are filtered before pivoting.\n\nWhen used together with `AGGREGATION`: samples are filtered before being aggregated.\n</details>\n\n<details open>\n<summary><code>FILTER_BY_VALUE min max</code></summary>\n\nfilters samples by minimum and maximum values. A sample passes the filter if its value is within the inclusive range `[min, max]`. `min` and `max` cannot be NaN values. Samples are filtered before pivoting.\n\nWhen used together with `AGGREGATION`: samples are filtered before being aggregated.\n</details>\n\n<details open>\n<summary><code>COUNT count</code></summary>\n\nlimits the number of returned entries, keeping those with the highest timestamps. The limit is applied after the per-timestamp merge.\n</details>\n\n<details open>\n<summary><code>ALIGN align</code></summary>\n\nis a time bucket alignment control for `AGGREGATION`. It controls the time bucket timestamps by changing the reference timestamp on which a bucket is defined.\n\n`align` values include:\n\n - `start` or `-`: The reference timestamp will be the query start interval time (`fromTimestamp`) which can't be `-`\n - `end` or `+`: The reference timestamp will be the query end interval time (`toTimestamp`) which can't be `+`\n - A specific timestamp: align the reference timestamp to a specific time\n\n<note><b>Note:</b> When not provided, alignment is set to `0`.</note>\n</details>\n\n<details open>\n<summary><code>AGGREGATION aggregators [aggregators ...] bucketDuration</code></summary>\n\naggregates samples into time buckets.\n\nProvide one or more aggregators per key, as a separate argument in the same order as the keys: the first argument applies to the first key, and so on. The number of these arguments must equal `numkeys`, and all keys share the same `bucketDuration`.\n\nEach per-key argument is either a single aggregator or a comma-separated list of aggregators (for example `avg,max`), exactly as in [`TS.REVRANGE`](https://redis.io/docs/latest/commands/ts.revrange); no whitespace is allowed. A key contributes one value for each aggregator you list for it, and a key's values appear together in the reply, in the order you list its aggregators. To compute several aggregations for one series, give that key a comma-separated list such as `min,max`.\n\n  - each `aggregator` is one of the following:\n\n    | aggregator   | Description                                                     |\n    | ------------ | --------------------------------------------------------------- |\n    | `avg`        | Arithmetic mean of all non-NaN values                           |\n    | `sum`        | Sum of all non-NaN values                                       |\n    | `min`        | Minimum non-NaN value                                           |\n    | `max`        | Maximum non-NaN value                                           |\n    | `range`      | Difference between the maximum and the minimum non-NaN values   |\n    | `count`      | Number of non-NaN values                                        |\n    | `countNaN`   | Number of NaN values                                            |\n    | `countAll`   | Number of values, including NaN and non-NaN                     |\n    | `first`      | The non-NaN value with the lowest timestamp in the bucket       |\n    | `last`       | The non-NaN value with the highest timestamp in the bucket      |\n    | `std.p`      | Population standard deviation of the non-NaN values             |\n    | `std.s`      | Sample standard deviation of the non-NaN values                 |\n    | `var.p`      | Population variance of the non-NaN values                       |\n    | `var.s`      | Sample variance of the non-NaN values                           |\n    | `twa`        | Time-weighted average over the bucket's timeframe (ignores NaN values) |\n\n  - `bucketDuration` is the duration of each bucket, in milliseconds.\n\n  Without `ALIGN`, bucket start times are multiples of `bucketDuration`.\n\n  With `ALIGN align`, bucket start times are multiples of `bucketDuration` with remainder `align % bucketDuration`.\n\n  The first bucket start time is less than or equal to `fromTimestamp`.\n</details>\n\n<details open>\n<summary><code>[BUCKETTIMESTAMP bt]</code></summary>\n\ncontrols how bucket timestamps are reported.\n\n| `bt`             | Timestamp reported for each bucket                            |\n| ---------------- | ------------------------------------------------------------- |\n| `-` or `start`   | the bucket's start time (default)                             |\n| `+` or `end`     | the bucket's end time                                         |\n| `~` or `mid`     | the bucket's mid time (rounded down if not an integer)        |\n</details>\n\n<details open>\n<summary><code>[EMPTY]</code></summary>\n\nis a flag, which, when specified, reports aggregations also for empty buckets.\n\n| aggregator           | Value reported for each empty bucket |\n| -------------------- | ------------------------------------ |\n| `sum`, `count`       | `0`                                  |\n| `last`               | The value of the last sample before the bucket's start. `NaN` when no such sample. |\n| `twa`                | Average value over the bucket's timeframe based on linear interpolation of the last sample before the bucket's start and the first sample after the bucket's end. `NaN` when no such samples. |\n| `min`, `max`, `range`, `avg`, `first`, `std.p`, `std.s` | `NaN` |\n\nRegardless of the values of `fromTimestamp` and `toTimestamp`, no data is reported for buckets that end before the earliest sample or begin after the latest sample in the time series. When a bucket is reported for one key but a different key has no data in that bucket, that key's value is `NaN`.\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Pivot raw samples from multiple series by timestamp</b></summary>\n\nCreate two time series and add samples at partially overlapping timestamps.\n\n\nredis> TS.CREATE {sensor}:1\nOK\nredis> TS.CREATE {sensor}:2\nOK\nredis> TS.MADD {sensor}:1 1000 10 {sensor}:1 2000 12\n1) (integer) 1000\n2) (integer) 2000\nredis> TS.MADD {sensor}:2 1000 20 {sensor}:2 3000 25\n1) (integer) 1000\n2) (integer) 3000\n\n\nQuery both series and group the samples by timestamp. One entry is returned for every distinct timestamp produced by at least one key, ordered from the highest timestamp to the lowest, with the values ordered by input key. A key with no sample at that timestamp has a `NaN` value.\n\n\nredis> TS.NREVRANGE 2 {sensor}:1 {sensor}:2 - +\n1) 1) (integer) 3000\n   2) 1) NaN\n      2) 25\n2) 1) (integer) 2000\n   2) 1) 12\n      2) NaN\n3) 1) (integer) 1000\n   2) 1) 10\n      2) 20\n\n</details>\n\n<details open>\n<summary><b>Aggregate each series with a per-key aggregator</b></summary>\n\nIn aggregation mode, supply one aggregator per key, in key order. Here `{sensor}:3` is aggregated with `avg` and `{sensor}:4` with `sum`, both over 1000-millisecond buckets.\n\n\nredis> TS.CREATE {sensor}:3\nOK\nredis> TS.CREATE {sensor}:4\nOK\nredis> TS.MADD {sensor}:3 1000 10 {sensor}:3 1100 20 {sensor}:3 2000 30\n1) (integer) 1000\n2) (integer) 1100\n3) (integer) 2000\nredis> TS.MADD {sensor}:4 1000 5 {sensor}:4 1100 15 {sensor}:4 2000 25\n1) (integer) 1000\n2) (integer) 1100\n3) (integer) 2000\nredis> TS.NREVRANGE 2 {sensor}:3 {sensor}:4 - + AGGREGATION avg sum 1000\n1) 1) (integer) 2000\n   2) 1) 30\n      2) 25\n2) 1) (integer) 1000\n   2) 1) 15\n      2) 20\n\n</details>\n\n<details open>\n<summary><b>Apply multiple aggregators to a series</b></summary>\n\nTo compute several aggregators for a single key, pass them as a comma-separated list. Here `{sensor}:3` uses `avg,max` (two values) and `{sensor}:4` uses `sum` (one value). Each timestamp's values are a single flat list: `{sensor}:3`'s `avg`, then its `max`, then `{sensor}:4`'s `sum`.\n\n\nredis> TS.NREVRANGE 2 {sensor}:3 {sensor}:4 - + AGGREGATION avg,max sum 1000\n1) 1) (integer) 2000\n   2) 1) 30\n      2) 30\n      3) 25\n2) 1) (integer) 1000\n   2) 1) 15\n      2) 20\n      3) 20\n\n</details>"},{"id":"details","title":"Details","role":"content","text":""},{"id":"semantics","title":"Semantics","role":"content","text":"`TS.NREVRANGE` behaves like running a compatible [`TS.REVRANGE`](https://redis.io/docs/latest/commands/ts.revrange) over each input key and then performing a server-side outer join by timestamp. Rows are returned from the highest timestamp to the lowest.\n\nIn raw mode (no `AGGREGATION`):\n\n- One entry is returned for every distinct timestamp produced by at least one key.\n- If a key has no sample at that timestamp, that key's value is `NaN`.\n\nIn aggregation mode (with `AGGREGATION`):\n\n- One aggregation applies to each key, in key order; the number of these arguments must equal `numkeys`, and all keys share one `bucketDuration`.\n- Each per-key argument is a single aggregator or a comma-separated list of aggregators, and a key contributes one value per aggregator.\n- Each timestamp's values form a single flat list, ordered by key and, within each key, by the order its aggregators are listed. For example, `avg,max` for the first key and `sum` for the second produce values of the form `[avg, max, sum]`.\n- When a key has no data for a given bucket, all of that key's values are `NaN`.\n- With `EMPTY`, empty buckets can produce rows in which every value is `NaN`."},{"id":"nan-values","title":"NaN values","role":"content","text":"A `NaN` value can mean that a key had no sample at that timestamp or no samples at that time bucket, or that the key stored or aggregated to a real `NaN`. These two cases are indistinguishable in the reply.\n\n| Case                                            | Value                                           |\n| ----------------------------------------------- | ----------------------------------------------- |\n| Key has a sample at that timestamp               | The sample value                                |\n| Key has no sample at that timestamp              | `NaN`                                           |\n| Key has aggregated data for that time bucket     | The aggregated value                            |\n| Key has no samples at that time bucket           | `NaN`                                           |\n| Key stores or aggregates to a real `NaN`         | `NaN`, indistinguishable from no data           |"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Not supported</span><br /> | <span title=\"Not supported\">&#x274c; Flexible & Annual</span><br /><span title=\"Not supported\">&#x274c; Free & Fixed</nobr></span> |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) with one entry per timestamp, ordered by decreasing timestamp. Each entry is an [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) composed of an [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) (the timestamp) and a flat [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) values. The values are concatenated across keys in input order; with `AGGREGATION`, each key contributes one value per aggregator, otherwise one value per key. A missing value is reported as `NaN`. The reply is an empty array when no samples match.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, wrong number of aggregators, unknown aggregation type, wrong key type, etc.\n\n**RESP3:**\n\nOne of the following:\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) with one entry per timestamp, ordered by decreasing timestamp. Each entry is an [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) composed of an [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) (the timestamp) and a flat [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of [Double reply](https://redis.io/docs/latest/develop/reference/protocol-spec#doubles) values. The values are concatenated across keys in input order; with `AGGREGATION`, each key contributes one value per aggregator, otherwise one value per key. A missing value is reported as `NaN`. The reply is an empty array when no samples match.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid arguments, wrong number of aggregators, unknown aggregation type, wrong key type, etc."},{"id":"see-also","title":"See also","role":"related","text":"[`TS.NRANGE`](https://redis.io/docs/latest/commands/ts.nrange) | [`TS.REVRANGE`](https://redis.io/docs/latest/commands/ts.revrange) | [`TS.MREVRANGE`](https://redis.io/docs/latest/commands/ts.mrevrange)"},{"id":"related-topics","title":"Related topics","role":"related","text":"[RedisTimeSeries](https://redis.io/docs/latest/develop/data-types/timeseries)"}],"examples":[]}
{"schema_version":2,"id":"commands/ts.queryindex","title":"TS.QUERYINDEX","url":"https://redis.io/docs/latest/commands/ts.queryindex/","summary":"Get all time series keys matching a filter list","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"0d59692813338fe4bd9814773b4c6a9eab0e6ca58be50cb4fb275c29b8cf66de","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\n\nGet all time series keys matching a filter list. Note: all matching keys will be listed, whether or not the user has read access.\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>filterExpr...</code></summary>\n\nfilters time series based on their labels and label values.\n\nEach filter expression has one of the following syntaxes:\n\n  - `label!=` - the time series has a label named `label`\n  - `label=value` - the time series has a label named `label` with a value equal to `value`\n  - `label=(value1,value2,...)` - the time series has a label named `label` with a value equal to one of the values in the list\n  - `label=` - the time series does not have a label named `label`\n  - `label!=value` - the time series does not have a label named `label` with a value equal to `value`\n  - `label!=(value1,value2,...)` - the time series does not have a label named `label` with a value equal to any of the values in the list\n\n  <note><b>Notes:</b>\n   - At least one filter expression with a syntax `label=value` or `label=(value1,value2,...)` is required.\n   - Filter expressions are conjunctive. For example, the filter `type=temperature room=study` means that `type=temperature` AND `room=study`.\n   - Whitespace is not allowed in a filter expression except between quotes or double quotes in values - for example, `x=\"y y\"` or `x='(y y,z z)'`.\n   </note>\n\n</details>\n\n<note><b>Note:</b> The `QUERYINDEX` command cannot be part of a transaction when running on a Redis cluster.</note>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Find keys by location and sensor type</b></summary>\n\nCreate a set of sensors to measure temperature and humidity in your study and kitchen.\n\n\nredis> TS.CREATE telemetry:study:temperature LABELS room study type temperature\nOK\nredis> TS.CREATE telemetry:study:humidity LABELS room study type humidity\nOK\nredis> TS.CREATE telemetry:kitchen:temperature LABELS room kitchen type temperature\nOK\nredis> TS.CREATE telemetry:kitchen:humidity LABELS room kitchen type humidity\nOK\n\n\nRetrieve keys of all time series representing sensors located in the kitchen. \n\n\nredis> TS.QUERYINDEX room=kitchen\n1) \"telemetry:kitchen:humidity\"\n2) \"telemetry:kitchen:temperature\"\n\n\nTo retrieve the keys of all time series representing sensors that measure temperature, use this query:\n\n\nredis> TS.QUERYINDEX type=temperature\n1) \"telemetry:kitchen:temperature\"\n2) \"telemetry:study:temperature\"\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) where each element is a [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings): a time series key. The array is empty if no time series matches the filter.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid filter expression.\n\n**RESP3:**\n\nOne of the following:\n* [Set reply](https://redis.io/docs/latest/develop/reference/protocol-spec#sets) where each element is a [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings): a time series key. The set is empty if no time series matches the filter.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid filter expression."},{"id":"see-also","title":"See also","role":"related","text":"[`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create) | [`TS.MRANGE`](https://redis.io/docs/latest/commands/ts.mrange) | [`TS.MREVRANGE`](https://redis.io/docs/latest/commands/ts.mrevrange) | [`TS.MGET`](https://redis.io/docs/latest/commands/ts.mget)"},{"id":"related-topics","title":"Related topics","role":"related","text":"[RedisTimeSeries](https://redis.io/docs/latest/develop/data-types/timeseries)"}],"examples":[]}
{"schema_version":2,"id":"commands/ts.querylabels","title":"TS.QUERYLABELS","url":"https://redis.io/docs/latest/commands/ts.querylabels/","summary":"Get all label names, or all values of a given label, for time series matching a filter list, or all series","since":"8.10.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"dab47db3aef964c2ddc415389c87a1c0bc896168f68fef6d2c2e0fdd5e1eec8c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\nGet all label names, or all values of a given label, for the time series matching a filter list (or all series when no filter is given).\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>LABELS | VALUES label</code></summary>\n\nselects what the command returns:\n\n  - `LABELS` - returns the set of distinct label names used by the matching time series.\n  - `VALUES label` - returns the set of distinct values of `label` used by the matching time series. The result is empty (not an error) when `label` does not exist or when no matching series carries it.\n\nEach name or value appears at most once, regardless of how many series it belongs to. The order of the results is not defined.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open>\n<summary><code>FILTER filterExpr [filterExpr ...]</code></summary>\n\nrestricts the command to the time series whose labels and label values match all of the filter expressions. When `FILTER` is omitted, the command considers all indexed time series.\n\nEach filter expression has one of the following syntaxes:\n\n  - `label!=` - the time series has a label named `label`\n  - `label=value` - the time series has a label named `label` with a value equal to `value`\n  - `label=(value1,value2,...)` - the time series has a label named `label` with a value equal to one of the values in the list\n  - `label=` - the time series does not have a label named `label`\n  - `label!=value` - the time series does not have a label named `label` with a value equal to `value`\n  - `label!=(value1,value2,...)` - the time series does not have a label named `label` with a value equal to any of the values in the list\n\n  <note><b>Notes:</b>\n   - At least one filter expression with a syntax `label=value` or `label=(value1,value2,...)` is required.\n   - Filter expressions are conjunctive. For example, the filter `type=temperature room=study` means that `type=temperature` AND `room=study`.\n   - Whitespace is not allowed in a filter expression except between quotes or double quotes in values - for example, `x=\"y y\"` or `x='(y y,z z)'`.\n   </note>\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>List labels and label values for a set of sensors</b></summary>\n\nCreate a set of sensors to measure temperature and humidity in your study and kitchen.\n\n[code example]\n\nRetrieve the label names used by the time series located in the kitchen.\n\n[code example]\n\nRetrieve the distinct values of the `type` label across all time series.\n\n[code example]\n</details>"},{"id":"details","title":"Details","role":"content","text":""},{"id":"access-control","title":"Access control","role":"content","text":"Unlike [`TS.QUERYINDEX`](https://redis.io/docs/latest/commands/ts.queryindex), which lists every matching key whether or not the user has read access to it, `TS.QUERYLABELS` silently omits the time series that the user is not allowed to read. Label names and values that belong only to unreadable series do not appear in the result."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) where each element is a [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings): a label name (with `LABELS`) or a label value (with `VALUES`). The array is empty if no time series matches the filter.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid subtype, missing label after `VALUES`, or invalid filter expression.\n\n**RESP3:**\n\nOne of the following:\n* [Set reply](https://redis.io/docs/latest/develop/reference/protocol-spec#sets) where each element is a [Bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings): a label name (with `LABELS`) or a label value (with `VALUES`). The set is empty if no time series matches the filter.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid subtype, missing label after `VALUES`, or invalid filter expression."},{"id":"see-also","title":"See also","role":"related","text":"[`TS.QUERYINDEX`](https://redis.io/docs/latest/commands/ts.queryindex) | [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create) | [`TS.MGET`](https://redis.io/docs/latest/commands/ts.mget) | [`TS.MRANGE`](https://redis.io/docs/latest/commands/ts.mrange)"},{"id":"related-topics","title":"Related topics","role":"related","text":"[RedisTimeSeries](https://redis.io/docs/latest/develop/data-types/timeseries)"}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> TS.CREATE telemetry:study:temperature LABELS room study type temperature\nOK\nredis> TS.CREATE telemetry:study:humidity LABELS room study type humidity\nOK\nredis> TS.CREATE telemetry:kitchen:temperature LABELS room kitchen type temperature\nOK\nredis> TS.CREATE telemetry:kitchen:humidity LABELS room kitchen type humidity\nOK","section_id":"examples"},{"id":"examples-ex1","language":"plaintext","code":"redis> TS.QUERYLABELS LABELS FILTER room=kitchen\n1) \"room\"\n2) \"type\"","section_id":"examples"},{"id":"examples-ex2","language":"plaintext","code":"redis> TS.QUERYLABELS VALUES type\n1) \"humidity\"\n2) \"temperature\"","section_id":"examples"}]}
{"schema_version":2,"id":"commands/ts.range","title":"TS.RANGE","url":"https://redis.io/docs/latest/commands/ts.range/","summary":"Query a range in forward direction","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"da0501ae0209de0ea5f661a7e8e2dc95536d52eb1f54180f9f82e020e9fd60e8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Query a range in forward direction. Starting from Redis 8.6, NaN values are included in raw measurement reports (queries without aggregation).\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>key</code></summary> \n\nis the key name for the time series.\n</details>\n\n<details open>\n<summary><code>fromTimestamp</code></summary> \n\nis start timestamp for the range query (integer Unix timestamp in milliseconds) or `-` to denote the timestamp of the earliest sample in the time series.\n</details>\n\n<details open>\n<summary><code>toTimestamp</code></summary> \n\nis end timestamp for the range query (integer Unix timestamp in milliseconds) or `+` to denote the timestamp of the latest sample in the time series.\n\n<note><b>Note:</b>    When the time series is a compaction, the last compacted value may aggregate raw values with timestamp beyond `toTimestamp`. That is because `toTimestamp` only limits the timestamp of the compacted value, which is the start time of the raw bucket that was compacted.</note>\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open>\n<summary><code>LATEST</code> (since RedisTimeSeries 1.8)</summary>\n\nis used when a time series is a compaction. With `LATEST`, TS.RANGE also reports the compacted value of the latest, possibly partial, bucket, given that this bucket's start time falls within `[fromTimestamp, toTimestamp]`. Without `LATEST`, TS.RANGE does not report the latest, possibly partial, bucket. When a time series is not a compaction, `LATEST` is ignored.\n  \nThe data in the latest bucket of a compaction is possibly partial. A bucket is _closed_ and compacted only upon arrival of a new sample that _opens_ a new _latest_ bucket. There are cases, however, when the compacted value of the latest, possibly partial, bucket is also required. In such a case, use `LATEST`.\n</details>\n\n<details open>\n<summary><code>FILTER_BY_TS ts...</code> (since RedisTimeSeries 1.6)</summary> \n\nfilters samples by a list of specific timestamps. A sample passes the filter if its exact timestamp is specified and falls within `[fromTimestamp, toTimestamp]`.\n\nWhen used together with `AGGREGATION`: samples are filtered before being aggregated.\n</details>\n\n<details open>\n<summary><code>FILTER_BY_VALUE min max</code> (since RedisTimeSeries 1.6)</summary> \n\nfilters samples by minimum and maximum values. `min` and `max` cannot be NaN values.\n\nWhen used together with `AGGREGATION`: samples are filtered before being aggregated.\n</details>\n\n<details open>\n<summary><code>COUNT count</code></summary> \n\nWhen used without `AGGREGATION`: limits the number of reported samples.\n\nWhen used together with `AGGREGATION`: limits the number of reported buckets.\n</details>\n\n<details open>\n<summary><code>ALIGN align</code> (since RedisTimeSeries 1.6)</summary> \n\nis a time bucket alignment control for `AGGREGATION`. It controls the time bucket timestamps by changing the reference timestamp on which a bucket is defined.\n\n`align` values include:\n   \n - `start` or `-`: The reference timestamp will be the query start interval time (`fromTimestamp`) which can't be `-`\n - `end` or `+`: The reference timestamp will be the query end interval time (`toTimestamp`) which can't be `+`\n - A specific timestamp: align the reference timestamp to a specific time\n   \n<note><b>Note:</b> When not provided, alignment is set to `0`.</note>\n</details>\n\n<details open>\n<summary><code>AGGREGATION aggregators bucketDuration</code></summary> \n\nfor each time series, aggregates samples into time buckets, where:\n\n  - `aggregators` is one or more comma-separated aggregators from the following table:\n\n    | aggregator   | Description                                                     |\n    | ------------ | --------------------------------------------------------------- |\n    | `avg`        | Arithmetic mean of all non-NaN values                           |\n    | `sum`        | Sum of all non-NaN values                                       |\n    | `min`        | Minimum non-NaN value                                           |\n    | `max`        | Maximum non-NaN value                                           |\n    | `range`      | Difference between the maximum and the minimum non-NaN values   |\n    | `count`      | Number of non-NaN values                                        |\n    | `countNaN`   | Number of NaN values (since Redis 8.6)                          |\n    | `countAll`   | Number of values, including NaN and non-NaN (since Redis 8.6)   |\n    | `first`      | The non-NaN value with the lowest timestamp in the bucket       |\n    | `last`       | The non-NaN value with the highest timestamp in the bucket      |\n    | `std.p`      | Population standard deviation of the non-NaN values             |\n    | `std.s`      | Sample standard deviation of the non-NaN values                 |\n    | `var.p`      | Population variance of the non-NaN values                       |\n    | `var.s`      | Sample variance of the non-NaN values                           |\n    | `twa`        | Time-weighted average over the bucket's timeframe (ignores NaN values) (since RedisTimeSeries 1.8) |\n\n  - `bucketDuration` is duration of each bucket, in milliseconds.\n\n  Note: `aggregators` can either be a single aggregator or multiple aggregators separated by commas as shown below.\n  No whitespace is allowed between aggregators.\n\n  AGGREGATION min,avg,max\n  \n  Without `ALIGN`, bucket start times are multiples of `bucketDuration`.\n  \n  With `ALIGN align`, bucket start times are multiples of `bucketDuration` with remainder `align % bucketDuration`.\n  \n  The first bucket start time is less than or equal to `fromTimestamp`.\n</details>\n\n<details open>\n<summary><code>[BUCKETTIMESTAMP bt]</code> (since RedisTimeSeries 1.8)</summary> \n\ncontrols how bucket timestamps are reported.\n\n| `bt`             | Timestamp reported for each bucket                            |\n| ---------------- | ------------------------------------------------------------- |\n| `-` or `start`   | the bucket's start time (default)                             |\n| `+` or `end`     | the bucket's end time                                         |\n| `~` or `mid`     | the bucket's mid time (rounded down if not an integer)        |\n</details>\n\n<details open>\n<summary><code>[EMPTY]</code> (since RedisTimeSeries 1.8)</summary> \n\nis a flag, which, when specified, reports aggregations also for empty buckets.\n\n| aggregator           | Value reported for each empty bucket |\n| -------------------- | ------------------------------------ |\n| `sum`, `count`       | `0`                                  |\n| `last`               | The value of the last sample before the bucket's start. `NaN` when no such sample. |\n| `twa`                | Average value over the bucket's timeframe based on linear interpolation of the last sample before the bucket's start and the first sample after the bucket's end. `NaN` when no such samples. |\n| `min`, `max`, `range`, `avg`, `first`, `std.p`, `std.s` | `NaN` |\n\nRegardless of the values of `fromTimestamp` and `toTimestamp`, no data is reported for buckets that end before the earliest sample or begin after the latest sample in the time series.\n</details>"},{"id":"complexity","title":"Complexity","role":"performance","text":"TS.RANGE complexity can be improved in the future by using binary search to find the start of the range, which makes this `O(Log(n/m)+k*m)`.\nBut, because `m` is small, you can disregard it and look at the operation as `O(Log(n)+k)`."},{"id":"examples","title":"Examples","role":"example","text":"<details open><summary><b>Filter results by timestamp or sample value</b></summary>\n\nConsider a metric where acceptable values are between -100 and 100, and the value 9999 is used as an indication of bad measurement.\n\n\n127.0.0.1:6379> TS.CREATE temp:TLV LABELS type temp location TLV\nOK\n127.0.0.1:6379> TS.MADD temp:TLV 1000 30 temp:TLV 1010 35 temp:TLV 1020 9999 temp:TLV 1030 40\n1) (integer) 1000\n2) (integer) 1010\n3) (integer) 1020\n4) (integer) 1030\n\n\nNow, retrieve all values except out-of-range values.\n\n\nTS.RANGE temp:TLV - + FILTER_BY_VALUE -100 100\n1) 1) (integer) 1000\n   2) 30\n2) 1) (integer) 1010\n   2) 35\n3) 1) (integer) 1030\n   2) 40\n\n\nNow, retrieve the minimum, average, and maximum values, while ignoring out-of-range values.\n\n\nTS.RANGE temp:TLV - + FILTER_BY_VALUE -100 100 AGGREGATION min,avg,max 1000\n1) 1) (integer) 1000\n   2) 30\n   3) 35\n   4) 40\n\n</details>\n\n<details open><summary><b>Align aggregation buckets</b></summary>\n\nTo demonstrate alignment, let’s create a stock and add prices at nine different timestamps.\n\n\n127.0.0.1:6379> TS.CREATE stock:A LABELS type stock name A\nOK\n127.0.0.1:6379> TS.MADD stock:A 1000 100 stock:A 1010 110 stock:A 1020 120\n1) (integer) 1000\n2) (integer) 1010\n3) (integer) 1020\n127.0.0.1:6379> TS.MADD stock:A 2000 200 stock:A 2010 210 stock:A 2020 220\n1) (integer) 2000\n2) (integer) 2010\n3) (integer) 2020\n127.0.0.1:6379> TS.MADD stock:A 3000 300 stock:A 3010 310 stock:A 3020 320\n1) (integer) 3000\n2) (integer) 3010\n3) (integer) 3020\n\n\nNext, aggregate without using `ALIGN`, defaulting to alignment 0.\n\n\n127.0.0.1:6379> TS.RANGE stock:A - + AGGREGATION min 20\n1) 1) (integer) 1000\n   2) 100\n2) 1) (integer) 1020\n   2) 120\n3) 1) (integer) 2000\n   2) 200\n4) 1) (integer) 2020\n   2) 220\n5) 1) (integer) 3000\n   2) 300\n6) 1) (integer) 3020\n   2) 320\n\n\nAnd now set `ALIGN` to 10 to have a bucket start at time 10, and align all the buckets with a 20 milliseconds duration.\n\n\n127.0.0.1:6379> TS.RANGE stock:A - + ALIGN 10 AGGREGATION min 20\n1) 1) (integer) 990\n   2) 100\n2) 1) (integer) 1010\n   2) 110\n3) 1) (integer) 1990\n   2) 200\n4) 1) (integer) 2010\n   2) 210\n5) 1) (integer) 2990\n   2) 300\n6) 1) (integer) 3010\n   2) 310\n\n\nWhen the start timestamp for the range query is explicitly stated (not `-`), you can set `ALIGN` to that time by setting align to `-` or to `start`.\n\n\n127.0.0.1:6379> TS.RANGE stock:A 5 + ALIGN - AGGREGATION min 20\n1) 1) (integer) 985\n   2) 100\n2) 1) (integer) 1005\n   2) 110\n3) 1) (integer) 1985\n   2) 200\n4) 1) (integer) 2005\n   2) 210\n5) 1) (integer) 2985\n   2) 300\n6) 1) (integer) 3005\n   2) 310\n\n\nSimilarly, when the end timestamp for the range query is explicitly stated, you can set `ALIGN` to that time by setting align to `+` or to `end`.\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of \n  * Without `AGGREGATION` or with a single aggregator:\n    ([Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers), [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings)) pairs representing (timestamp, value).\n  * With multiple aggregators:\n    ([Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers), multiple [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings)) tuples representing (timestamp, value...).\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid filter value, wrong key type, key does not exist, etc.\n\n**RESP3:**\n\nOne of the following:\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of\n  * Without `AGGREGATION` or with a single aggregator:\n    ([Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers), [Double reply](https://redis.io/docs/latest/develop/reference/protocol-spec#doubles)) pairs representing (timestamp, value).\n  * With multiple aggregators:\n    ([Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers), multiple [Double reply](https://redis.io/docs/latest/develop/reference/protocol-spec#doubles)) tuples representing (timestamp, value...).    \n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid filter value, wrong key type, key does not exist, etc."},{"id":"see-also","title":"See also","role":"related","text":"[`TS.MRANGE`](https://redis.io/docs/latest/commands/ts.mrange) | [`TS.REVRANGE`](https://redis.io/docs/latest/commands/ts.revrange) | [`TS.MREVRANGE`](https://redis.io/docs/latest/commands/ts.mrevrange)"},{"id":"related-topics","title":"Related topics","role":"related","text":"[RedisTimeSeries](https://redis.io/docs/latest/develop/data-types/timeseries)"}],"examples":[]}
{"schema_version":2,"id":"commands/ts.read","title":"TS.READ","url":"https://redis.io/docs/latest/commands/ts.read/","summary":"Read: return up to max_count samples with timestamp >= timestamp. With BLOCK, waits up to milliseconds ms until at least min_count qualifying samples exist","since":"8.10.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"71d540c53b70b705c1bb05c9561f6860f3bfe7d61b72f289aa8ed8ce12c5e7fb","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Read a batch of time series samples at or after a given timestamp, returning up to `max_count` of the oldest qualifying samples ordered by increasing timestamp. By default `TS.READ` returns immediately with whatever is available; add the optional `BLOCK` keyword to wait until at least `min_count` samples exist or `milliseconds` elapse, whichever occurs first.\n\nThe key may be a regular time series or a compaction time series.\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>key</code></summary>\n\nis the key name for the time series. It may identify a regular series or a compaction series.\n</details>\n\n<details open>\n<summary><code>timestamp</code></summary>\n\nis the inclusive lower-bound cursor for the read. The command selects samples whose timestamp is greater than or equal to the resolved cursor (`sample_timestamp >= resolved_timestamp`).\n\n`timestamp` is either a non-negative integer Unix timestamp in milliseconds or one of the sentinels `-`, `+`, or `$`:\n\n| Value                | Name          | Description |\n| -------------------- | ------------- | ----------- |\n| Non-negative integer | Literal cursor | A Unix timestamp in milliseconds. Matching is inclusive. `0` is accepted and reads from the beginning. |\n| `-`                  | Earliest      | The timestamp of the earliest sample in the series, or `0` when the series is empty or does not exist. |\n| `+`                  | Latest        | The timestamp of the latest sample in the series, or `0` when the series is empty or does not exist. The cursor is inclusive, so the latest existing sample itself qualifies (aligned with [`TS.RANGE`](https://redis.io/docs/latest/commands/ts.range)). Without `BLOCK`, the call returns that sample immediately; with `BLOCK` and a `min_count` greater than `1`, it blocks until enough samples at or after that timestamp exist. Intended for the first call only. |\n| `$`                  | New           | The timestamp of the latest sample plus 1, or `0` when the series is empty or does not exist. Only samples reported after the command was received by the server qualify; the latest existing sample is excluded. |\n\nThe `+` and `$` semantics mirror the special IDs of [`XREAD`](https://redis.io/docs/latest/commands/xread) and [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup). The server resolves all sentinels exactly once, when the command is received, so the cursor stays stable while the client is blocked. Send `-`, `+`, and `$` to the server as-is; do not resolve them on the client side.\n\nUse the following table to choose the cursor for the first call:\n\n| Goal                                          | First-call `timestamp` |\n| --------------------------------------------- | ---------------------- |\n| Read the full history, then new samples       | `-` or `0`             |\n| Start from the current latest sample, inclusive | `+`                  |\n| Receive only samples added after the call     | `$`                    |\n\nFor every subsequent call, use `last_returned_timestamp + 1` so you receive no misses and no duplicates. Do not reuse `+` or `$` after the first call. If no samples are returned, you can retry with the same cursor, subject to your application's delivery policy.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open>\n<summary><code>BLOCK milliseconds min_count</code></summary>\n\nmakes the command block instead of returning immediately. When `BLOCK` is present, the command waits until at least `min_count` qualifying samples are available, until `milliseconds` elapse, or until the key is removed, whichever occurs first.\n\n`milliseconds` is a non-negative integer timeout. A value of `0` means block indefinitely, until `min_count` samples become available or the key is removed (the same convention as [`BLPOP`](https://redis.io/docs/latest/commands/blpop) and `XREAD BLOCK 0`).\n\n`min_count` is the unblock threshold: the number of qualifying samples required before the command returns ahead of the timeout. It must be a positive integer. When `BLOCK` is omitted, the command does not block and returns whatever is available immediately, even when no samples qualify.\n</details>\n\n<details open>\n<summary><code>MAX_COUNT max_count</code></summary>\n\nis the reply cap: the maximum number of samples to return. It must be a positive integer and, when `BLOCK` is used, greater than or equal to `min_count`. When `MAX_COUNT` is not provided, the reply is unbounded. When more matching samples exist than `max_count`, the server returns the oldest `max_count` samples first, so you can page forward.\n\n`MAX_COUNT` defaults to unlimited; set it explicitly when consuming large histories to bound the reply size and enable paging.\n</details>\n\nThe `BLOCK` and `MAX_COUNT` keywords are optional, independent, and can be given in either order. Omit `BLOCK` to read without blocking; omit `MAX_COUNT` to leave the reply size unbounded. A keyword without its values, or any stray trailing token, is rejected as a wrong-arity error."},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Read history without blocking</b></summary>\n\nCreate a time series and add three samples.\n\n[code example]\n\nRead all historical samples without blocking. With no `BLOCK` keyword the command returns immediately, and `max_count` is unlimited.\n\n[code example]\n\nThe `-` sentinel is equivalent for a full read from the earliest sample.\n\n[code example]\n</details>\n\n<details open>\n<summary><b>Page through history in bounded batches</b></summary>\n\nRead at most two samples per call, advancing the cursor to `last_returned_timestamp + 1` on each subsequent call.\n\n[code example]\n</details>\n\n<details open>\n<summary><b>Start from the latest sample</b></summary>\n\nStart from the current latest sample (inclusive). Without `BLOCK`, this returns immediately.\n\n[code example]\n\nWith `BLOCK 5000 2`, the same `+` call blocks until a second sample with timestamp greater than or equal to 300 arrives, or returns the available samples after 5000 milliseconds.\n\n[code example]\n</details>\n\n<details open>\n<summary><b>Tail only new samples</b></summary>\n\nUse `$` to ignore everything that already exists and receive only samples added after the call. `BLOCK 5000 1` waits up to 5000 milliseconds for the first new sample.\n\n[code example]\n\nIf another client runs `TS.ADD sensor:1 400 4.0` within 5000 milliseconds, the blocked call returns `[[400, 4]]`. If no new sample arrives, it returns an empty array.\n</details>\n\n<details open>\n<summary><b>Partial flush on timeout</b></summary>\n\nWhen `min_count` cannot be reached, the command returns the available samples after the timeout. Here `BLOCK 1000 10` cannot reach 10 samples, so the two qualifying samples are returned after 1000 milliseconds.\n\n[code example]\n\nWhen nothing qualifies by the timeout, the reply is an empty array.\n\n[code example]\n</details>\n\n<details open>\n<summary><b>Read from a compaction series</b></summary>\n\n`key` may be a compaction series. The command can return compaction samples and, with `BLOCK`, can wait for future compaction output.\n\n[code example]\n</details>"},{"id":"details","title":"Details","role":"content","text":""},{"id":"when-to-use-tsread","title":"When to use `TS.READ`","role":"content","text":"Applications often render charts from time series data — metrics, sensor readings, financial prices and volumes, or application telemetry — and need to keep those charts current as new samples arrive. Without `TS.READ`, an application typically either polls [`TS.RANGE`](https://redis.io/docs/latest/commands/ts.range) at a fixed interval and checks for samples added since the previous request, or listens for keyspace notifications. Both approaches work, but they require periodic polling or additional notification handling.\n\n`TS.READ` replaces that with a single, optionally blocking read: the application issues one call and waits until new samples are available, then appends them and repeats. This suits anything that streams updates continuously — monitoring dashboards, live charts, financial terminals, IoT sensor feeds, or alerting systems — without issuing repeated range queries when no new sample has been added."},{"id":"blocking-and-retrieval-semantics","title":"Blocking and retrieval semantics","role":"content","text":"- From `key`, the command reads samples whose timestamp is greater than or equal to the resolved cursor.\n- Without `BLOCK`, the command never blocks: it returns up to `max_count` of the oldest qualifying samples immediately, even when no samples qualify (an empty array).\n- With `BLOCK milliseconds min_count`, if at least `min_count` matching samples are already available, the server returns immediately with up to `max_count` of the oldest qualifying samples.\n- With `BLOCK` and fewer than `min_count` matching samples available, the server blocks until `min_count` is reached, `milliseconds` elapse, or the key is removed. `BLOCK 0` blocks indefinitely. Each sample append ([`TS.ADD`](https://redis.io/docs/latest/commands/ts.add), [`TS.MADD`](https://redis.io/docs/latest/commands/ts.madd), [`TS.INCRBY`](https://redis.io/docs/latest/commands/ts.incrby), [`TS.DECRBY`](https://redis.io/docs/latest/commands/ts.decrby), and compaction-rule writes to the destination key) can unblock a blocked client.\n- On timeout, the server returns whatever is available, which can be an empty array or fewer than `min_count` samples. This is a successful reply, not an error.\n- If the key is removed while the client is blocked (`DEL`, `UNLINK`, `FLUSHDB`, `FLUSHALL`, expiry, or eviction), the server returns an empty list to the blocked client. This is a successful reply, not an error.\n- Returned samples are sorted by increasing timestamp, including when samples were inserted out of order.\n- Multiple blocked clients waiting on the same key wait independently. One client receiving samples does not consume them for another client."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Not supported</span><br /> | <span title=\"Not supported\">&#x274c; Flexible & Annual</span><br /><span title=\"Not supported\">&#x274c; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of ([Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers), [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings)) pairs representing (timestamp, value), ordered by increasing timestamp. The array is empty when no matching samples are available by the time the command returns, or when the key was removed while the command was blocked.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid timestamp, invalid `milliseconds`, invalid `min_count` or `max_count`, `min_count` greater than `max_count`, wrong number of arguments, wrong key type, the command is used with `BLOCK` where blocking is not allowed (for example, inside `MULTI` or a Lua script), etc.\n\n**RESP3:**\n\nOne of the following:\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of ([Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers), [Double reply](https://redis.io/docs/latest/develop/reference/protocol-spec#doubles)) pairs representing (timestamp, value), ordered by increasing timestamp. The array is empty when no matching samples are available by the time the command returns, or when the key was removed while the command was blocked.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid timestamp, invalid `milliseconds`, invalid `min_count` or `max_count`, `min_count` greater than `max_count`, wrong number of arguments, wrong key type, the command is used with `BLOCK` where blocking is not allowed (for example, inside `MULTI` or a Lua script), etc."},{"id":"see-also","title":"See also","role":"related","text":"[`TS.GET`](https://redis.io/docs/latest/commands/ts.get) | [`TS.RANGE`](https://redis.io/docs/latest/commands/ts.range) | [`TS.REVRANGE`](https://redis.io/docs/latest/commands/ts.revrange)"},{"id":"related-topics","title":"Related topics","role":"related","text":"[RedisTimeSeries](https://redis.io/docs/latest/develop/data-types/timeseries)"}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"127.0.0.1:6379> TS.CREATE sensor:1\nOK\n127.0.0.1:6379> TS.ADD sensor:1 100 1.0\n(integer) 100\n127.0.0.1:6379> TS.ADD sensor:1 200 2.0\n(integer) 200\n127.0.0.1:6379> TS.ADD sensor:1 300 3.0\n(integer) 300","section_id":"examples"},{"id":"examples-ex1","language":"plaintext","code":"127.0.0.1:6379> TS.READ sensor:1 0\n1) 1) (integer) 100\n   2) 1\n2) 1) (integer) 200\n   2) 2\n3) 1) (integer) 300\n   2) 3","section_id":"examples"},{"id":"examples-ex2","language":"plaintext","code":"127.0.0.1:6379> TS.READ sensor:1 -","section_id":"examples"},{"id":"examples-ex3","language":"plaintext","code":"127.0.0.1:6379> TS.READ sensor:1 - MAX_COUNT 2\n1) 1) (integer) 100\n   2) 1\n2) 1) (integer) 200\n   2) 2\n127.0.0.1:6379> TS.READ sensor:1 201 MAX_COUNT 2\n1) 1) (integer) 300\n   2) 3","section_id":"examples"},{"id":"examples-ex4","language":"plaintext","code":"127.0.0.1:6379> TS.READ sensor:1 +\n1) 1) (integer) 300\n   2) 3","section_id":"examples"},{"id":"examples-ex5","language":"plaintext","code":"127.0.0.1:6379> TS.READ sensor:1 + BLOCK 5000 2","section_id":"examples"},{"id":"examples-ex6","language":"plaintext","code":"127.0.0.1:6379> TS.READ sensor:1 $ BLOCK 5000 1","section_id":"examples"},{"id":"examples-ex7","language":"plaintext","code":"127.0.0.1:6379> TS.READ sensor:1 101 BLOCK 1000 10\n1) 1) (integer) 200\n   2) 2\n2) 1) (integer) 300\n   2) 3","section_id":"examples"},{"id":"examples-ex8","language":"plaintext","code":"127.0.0.1:6379> TS.READ sensor:1 301 BLOCK 1000 1\n(empty array)","section_id":"examples"},{"id":"examples-ex9","language":"plaintext","code":"127.0.0.1:6379> TS.READ sensor:1:avg - BLOCK 10000 1","section_id":"examples"}]}
{"schema_version":2,"id":"commands/ts.revrange","title":"TS.REVRANGE","url":"https://redis.io/docs/latest/commands/ts.revrange/","summary":"Query a range in reverse direction","since":"1.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:54+01:00","page_type":"content","content_hash":"cdcea97d0d30922669153cfb246e6932f02d4690ef8c17361285f73a4b60deb3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Query a range in reverse direction. Starting from Redis 8.6, NaN values are included in raw measurement reports (queries without aggregation).\n\n[Examples](#examples)"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>key</code></summary>\n\nis the key name for the time series.\n</details>\n\n<details open>\n<summary><code>fromTimestamp</code></summary>\n\nis start timestamp for the range query (integer Unix timestamp in milliseconds) or `-` to denote the timestamp of the earliest sample in the time series.\n</details>\n\n<details open>\n<summary><code>toTimestamp</code></summary>\n\nis end timestamp for the range query (integer Unix timestamp in milliseconds) or `+` to denote the timestamp of the latest sample in the time series.\n\n<note><b>Note:</b>  When the time series is a compaction, the last compacted value may aggregate raw values with timestamp beyond `toTimestamp`. That is because `toTimestamp` limits only the timestamp of the compacted value, which is the start time of the raw bucket that was compacted.</note>\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open>\n<summary><code>LATEST</code> (since RedisTimeSeries 1.8)</summary>\n\nis used when a time series is a compaction. With `LATEST`, TS.REVRANGE also reports the compacted value of the latest, possibly partial, bucket, given that this bucket's start time falls within `[fromTimestamp, toTimestamp]`. Without `LATEST`, TS.REVRANGE does not report the latest, possibly partial, bucket. When a time series is not a compaction, `LATEST` is ignored.\n  \nThe data in the latest bucket of a compaction is possibly partial. A bucket is _closed_ and compacted only upon arrival of a new sample that _opens_ a new _latest_ bucket. There are cases, however, when the compacted value of the latest, possibly partial, bucket is also required. In such a case, use `LATEST`.\n</details>\n\n<details open>\n<summary><code>FILTER_BY_TS ts...</code> (since RedisTimeSeries 1.6)</summary>\n\nfilters samples by a list of specific timestamps. A sample passes the filter if its exact timestamp is specified and falls within `[fromTimestamp, toTimestamp]`.\n\nWhen used together with `AGGREGATION`: samples are filtered before being aggregated.\n</details>\n\n<details open>\n<summary><code>FILTER_BY_VALUE min max</code> (since RedisTimeSeries 1.6)</summary>\n\nfilters samples by minimum and maximum values. `min` and `max` cannot be NaN values.\n\nWhen used together with `AGGREGATION`: samples are filtered before being aggregated.\n</details>\n\n<details open>\n<summary><code>COUNT count</code></summary>\n\nWhen used without `AGGREGATION`: limits the number of reported samples.\n\nWhen used together with `AGGREGATION`: limits the number of reported buckets.\n</details>\n\n<details open>\n<summary><code>ALIGN align</code> (since RedisTimeSeries 1.6)</summary>\n\nis a time bucket alignment control for `AGGREGATION`. It controls the time bucket timestamps by changing the reference timestamp on which a bucket is defined. \nValues include:\n   \n - `start` or `-`: The reference timestamp will be the query start interval time (`fromTimestamp`) which can't be `-`\n - `end` or `+`: The reference timestamp will be the query end interval time (`toTimestamp`) which can't be `+`\n - A specific timestamp: align the reference timestamp to a specific time\n   \n<note><b>NOTE:</b> When not provided, alignment is set to `0`.</note>\n\n</details>\n\n<details open>\n<summary><code>AGGREGATION aggregators bucketDuration</code></summary>\n\nfor each time series, aggregates samples into time buckets, where:\n\n  - `aggregators` is one or more comma-separated aggregators from the following table:\n\n    | aggregator   | Description                                                     |\n    | ------------ | --------------------------------------------------------------- |\n    | `avg`        | Arithmetic mean of all non-NaN values                           |\n    | `sum`        | Sum of all non-NaN values                                       |\n    | `min`        | Minimum non-NaN value                                           |\n    | `max`        | Maximum non-NaN value                                           |\n    | `range`      | Difference between the maximum and the minimum non-NaN values   |\n    | `count`      | Number of non-NaN values                                        |\n    | `countNaN`   | Number of NaN values (since Redis 8.6)                          |\n    | `countAll`   | Number of values, including NaN and non-NaN (since Redis 8.6)   |\n    | `first`      | The non-NaN value with the lowest timestamp in the bucket       |\n    | `last`       | The non-NaN value with the highest timestamp in the bucket      |\n    | `std.p`      | Population standard deviation of the non-NaN values             |\n    | `std.s`      | Sample standard deviation of the non-NaN values                 |\n    | `var.p`      | Population variance of the non-NaN values                       |\n    | `var.s`      | Sample variance of the non-NaN values                           |\n    | `twa`        | Time-weighted average over the bucket's timeframe (ignores NaN values) (since RedisTimeSeries 1.8) |\n\n  - `bucketDuration` is duration of each bucket, in milliseconds.\n\n  Note: `aggregators` can either be a single aggregator or multiple aggregators separated by commas as shown below.\n  No whitespace is allowed between aggregators.\n\n  AGGREGATION min,avg,max\n\n  Without `ALIGN`, bucket start times are multiples of `bucketDuration`.\n  \n  With `ALIGN align`, bucket start times are multiples of `bucketDuration` with remainder `align % bucketDuration`.\n  \n  The first bucket start time is less than or equal to `fromTimestamp`.  \n</details>\n\n<details open>\n<summary><code>[BUCKETTIMESTAMP bt]</code> (since RedisTimeSeries 1.8)</summary>\n\ncontrols how bucket timestamps are reported.\n\n| `bt`             | Timestamp reported for each bucket                            |\n| ---------------- | ------------------------------------------------------------- |\n| `-` or `start`   | the bucket's start time (default)                             |\n| `+` or `end`     | the bucket's end time                                         |\n| `~` or `mid`     | the bucket's mid time (rounded down if not an integer)        |\n</details>\n\n<details open>\n<summary><code>[EMPTY]</code> (since RedisTimeSeries 1.8)</summary>\nis a flag, which, when specified, reports aggregations also for empty buckets.\n\n| aggregator           | Value reported for each empty bucket |\n| -------------------- | ------------------------------------ |\n| `sum`, `count`       | `0`                                  |\n| `last`               | The value of the last sample before the bucket's start. `NaN` when no such sample. |\n| `twa`                | Average value over the bucket's timeframe based on linear interpolation of the last sample before the bucket's start and the first sample after the bucket's end. `NaN` when no such samples. |\n| `min`, `max`, `range`, `avg`, `first`, `std.p`, `std.s` | `NaN` |\n\nRegardless of the values of `fromTimestamp` and `toTimestamp`, no data is reported for buckets that end before the earliest sample or begin after the latest sample in the time series.\n</details>"},{"id":"complexity","title":"Complexity","role":"performance","text":"TS.REVRANGE complexity can be improved in the future by using binary search to find the start of the range, which makes this `O(Log(n/m)+k*m)`.\nBut, because `m` is small, you can disregard it and look at the operation as `O(Log(n)+k)`."},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><b>Filter results by timestamp or sample value</b></summary>\n\nConsider a metric where acceptable values are between -100 and 100, and the value 9999 is used as an indication of bad measurement.\n\n\n127.0.0.1:6379> TS.CREATE temp:TLV LABELS type temp location TLV\nOK\n127.0.0.1:6379> TS.MADD temp:TLV 1000 30 temp:TLV 1010 35 temp:TLV 1020 9999 temp:TLV 1030 40\n1) (integer) 1000\n2) (integer) 1010\n3) (integer) 1020\n4) (integer) 1030\n\n\nNow, retrieve all values except out-of-range values.\n\n\nTS.REVRANGE temp:TLV - + FILTER_BY_VALUE -100 100\n1) 1) (integer) 1030\n   2) 40\n2) 1) (integer) 1010\n   2) 35\n3) 1) (integer) 1000\n   2) 30\n\n\nNow, retrieve the minimum, average, and maximum values, while ignoring out-of-range values.\n\n\nTS.REVRANGE temp:TLV - + FILTER_BY_VALUE -100 100 AGGREGATION min,avg,max 1000\n1) 1) (integer) 1000\n   2) 30\n   3) 35\n   4) 40\n\n</details>\n\n<details open>\n<summary><b>Align aggregation buckets</b></summary>\n\nTo demonstrate alignment, let’s create a stock and add prices at three different timestamps.\n\n\n127.0.0.1:6379> TS.CREATE stock:A LABELS type stock name A\nOK\n127.0.0.1:6379> TS.MADD stock:A 1000 100 stock:A 1010 110 stock:A 1020 120\n1) (integer) 1000\n2) (integer) 1010\n3) (integer) 1020\n127.0.0.1:6379> TS.MADD stock:A 2000 200 stock:A 2010 210 stock:A 2020 220\n1) (integer) 2000\n2) (integer) 2010\n3) (integer) 2020\n127.0.0.1:6379> TS.MADD stock:A 3000 300 stock:A 3010 310 stock:A 3020 320\n1) (integer) 3000\n2) (integer) 3010\n3) (integer) 3020\n\n\nNext, aggregate without using `ALIGN`, defaulting to alignment 0.\n\n\n127.0.0.1:6379> TS.REVRANGE stock:A - + AGGREGATION min 20\n1) 1) (integer) 3020\n   2) 320\n2) 1) (integer) 3000\n   2) 300\n3) 1) (integer) 2020\n   2) 220\n4) 1) (integer) 2000\n   2) 200\n5) 1) (integer) 1020\n   2) 120\n6) 1) (integer) 1000\n   2) 100\n\n\nAnd now set `ALIGN` to 10 to have a bucket start at time 10, and align all the buckets with a 20 milliseconds duration.\n\n\n127.0.0.1:6379> TS.REVRANGE stock:A - + ALIGN 10 AGGREGATION min 20\n1) 1) (integer) 3010\n   2) 310\n2) 1) (integer) 2990\n   2) 300\n3) 1) (integer) 2010\n   2) 210\n4) 1) (integer) 1990\n   2) 200\n5) 1) (integer) 1010\n   2) 110\n6) 1) (integer) 990\n   2) 100\n\n\nWhen the start timestamp for the range query is explicitly stated (not `-`), you can set ALIGN to that time by setting align to `-` or to `start`.\n\n\n127.0.0.1:6379> TS.REVRANGE stock:A 5 + ALIGN - AGGREGATION min 20\n1) 1) (integer) 3005\n   2) 310\n2) 1) (integer) 2985\n   2) 300\n3) 1) (integer) 2005\n   2) 210\n4) 1) (integer) 1985\n   2) 200\n5) 1) (integer) 1005\n   2) 110\n6) 1) (integer) 985\n   2) 100\n\n\nSimilarly, when the end timestamp for the range query is explicitly stated, you can set ALIGN to that time by setting align to `+` or to `end`.\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Supported</span><br /> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of\n  * Without `AGGREGATION` or with a single aggregator:\n    ([Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers), [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings)) pairs representing (timestamp, value) in reverse chronological order.\n  * With multiple aggregators:\n    ([Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers), multiple [Simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings)) tuples representing (timestamp, value...) in reverse chronological order.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid filter value, wrong key type, key does not exist, etc.\n\n**RESP3:**\n\nOne of the following:\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of\n  * Without `AGGREGATION` or with a single aggregator:\n    ([Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers), [Double reply](https://redis.io/docs/latest/develop/reference/protocol-spec#doubles)) pairs representing (timestamp, value) in reverse chronological order.\n  * With multiple aggregators:\n    ([Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers), multiple [Double reply](https://redis.io/docs/latest/develop/reference/protocol-spec#doubles)) tuples representing (timestamp, value...) in reverse chronological order.\n* [Simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in these cases: invalid filter value, wrong key type, key does not exist, etc."},{"id":"see-also","title":"See also","role":"related","text":"[`TS.RANGE`](https://redis.io/docs/latest/commands/ts.range) | [`TS.MRANGE`](https://redis.io/docs/latest/commands/ts.mrange) | [`TS.MREVRANGE`](https://redis.io/docs/latest/commands/ts.mrevrange)"},{"id":"related-topics","title":"Related topics","role":"related","text":"[RedisTimeSeries](https://redis.io/docs/latest/develop/data-types/timeseries)"}],"examples":[]}
{"schema_version":2,"id":"commands/ttl","title":"TTL","url":"https://redis.io/docs/latest/commands/ttl/","summary":"Returns the expiration time in seconds of a key.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"8b9c0f8835396172648a38becb3f7c3f8455f4c945dd661fb02cfb906bad37e9","sections":[{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"Foundational: Check remaining time-to-live of a key using TTL (returns seconds remaining, -1 if no expiry, -2 if key doesn't exist)\n\n**Difficulty:** Beginner\n\n**Commands:** SET, EXPIRE, TTL\n\n**Complexity:**\n- SET: O(1)\n- EXPIRE: O(1)\n- TTL: O(1)\n\n**Available in:** Redis CLI, C, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): TTL in seconds.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `-1` if the key exists but has no associated expiration.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `-2` if the key does not exist.\n\n**RESP3:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): TTL in seconds.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `-1` if the key exists but has no associated expiration.\n* [Integer reply](../../develop/reference/protocol-spec#integers): `-2` if the key does not exist."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> SET mykey \"Hello\"\nOK\n> EXPIRE mykey 10\n(integer) 1\n> TTL mykey\n(integer) 10","section_id":"examples"},{"id":"examples-ex1","language":"c","code":"reply = redisCommand(c, \"SET mykey Hello\");\n    printf(\"%s\\n\", reply->str);\n    // >>> OK\n    freeReplyObject(reply);\n\n    reply = redisCommand(c, \"EXPIRE mykey 10\");\n    printf(\"%lld\\n\", reply->integer);\n    // >>> 1\n    freeReplyObject(reply);\n\n    reply = redisCommand(c, \"TTL mykey\");\n    printf(\"%lld\\n\", reply->integer);\n    // >>> 10\n    freeReplyObject(reply);","section_id":"examples"},{"id":"examples-ex2","language":"csharp","code":"bool ttlResult1 = db.StringSet(\"mykey\", \"Hello\");\n        Console.WriteLine(ttlResult1);  // >>> true\n\n        bool ttlResult2 = db.KeyExpire(\"mykey\", new TimeSpan(0, 0, 10));\n        Console.WriteLine(ttlResult2);\n\n        TimeSpan ttlResult3 = db.KeyTimeToLive(\"mykey\") ?? TimeSpan.Zero;\n        string ttlRes = Math.Round(ttlResult3.TotalSeconds).ToString();\n        Console.WriteLine(Math.Round(ttlResult3.TotalSeconds)); // >>> 10","section_id":"examples"},{"id":"examples-ex3","language":"go","code":"ttlResult1, err := rdb.Set(ctx, \"mykey\", \"Hello\", 10*time.Second).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(ttlResult1) // >>> OK\n\n\tttlResult2, err := rdb.TTL(ctx, \"mykey\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(math.Round(ttlResult2.Seconds())) // >>> 10","section_id":"examples"},{"id":"examples-ex4","language":"java","code":"CompletableFuture<Void> ttlExample = asyncCommands.set(\"mykey\", \"Hello\")\n                    .thenCompose(r1 -> {\n                        System.out.println(r1);              // >>> OK\n                        return asyncCommands.expire(\"mykey\", 10);\n                    })\n                    .thenCompose(r2 -> {\n                        System.out.println(r2);              // >>> true\n                        return asyncCommands.ttl(\"mykey\");\n                    })\n                    .thenAccept(r3 -> {\n                        System.out.println(r3);              // >>> 10\n                    })\n                    .toCompletableFuture();","section_id":"examples"},{"id":"examples-ex5","language":"java","code":"Mono<Void> ttlExample = reactiveCommands.set(\"mykey\", \"Hello\")\n                    .flatMap(r1 -> {\n                        System.out.println(r1);              // >>> OK\n                        return reactiveCommands.expire(\"mykey\", 10);\n                    })\n                    .flatMap(r2 -> {\n                        System.out.println(r2);              // >>> true\n                        return reactiveCommands.ttl(\"mykey\");\n                    })\n                    .doOnNext(r3 -> {\n                        System.out.println(r3);              // >>> 10\n                    })\n                    .then();","section_id":"examples"},{"id":"examples-ex6","language":"java","code":"String ttlResult1 = jedis.set(\"mykey\", \"Hello\");\n        System.out.println(ttlResult1); // >>> OK\n\n        long ttlResult2 = jedis.expire(\"mykey\", 10);\n        System.out.println(ttlResult2); // >>> 1\n\n        long ttlResult3 = jedis.ttl(\"mykey\");\n        System.out.println(ttlResult3); // >>> 10","section_id":"examples"},{"id":"examples-ex7","language":"javascript","code":"const ttlRes1 = await client.set('mykey', 'Hello');\nconsole.log(ttlRes1); // OK\n\nconst ttlRes2 = await client.expire('mykey', 10);\nconsole.log(ttlRes2); // 1\n\nconst ttlRes3 = await client.ttl('mykey');\nconsole.log(ttlRes3); // 10","section_id":"examples"},{"id":"examples-ex8","language":"javascript","code":"console.log(await redis.set('mykey', 'Hello')); // >>> OK\nconsole.log(await redis.expire('mykey', 10)); // >>> 1\n\nconst ttlResult = await redis.ttl('mykey');\nconsole.log(ttlResult); // >>> 10","section_id":"examples"},{"id":"examples-ex9","language":"javascript","code":"const ttlRes1 = await client.set('mykey', 'Hello');\nconsole.log(ttlRes1); // OK\n\nconst ttlRes2 = await client.expire('mykey', 10);\nconsole.log(ttlRes2); // 1\n\nconst ttlRes3 = await client.ttl('mykey');\nconsole.log(ttlRes3); // 10","section_id":"examples"},{"id":"examples-ex10","language":"javascript","code":"console.log(await redis.set('mykey', 'Hello')); // >>> OK\nconsole.log(await redis.expire('mykey', 10)); // >>> 1\n\nconst ttlResult = await redis.ttl('mykey');\nconsole.log(ttlResult); // >>> 10","section_id":"examples"},{"id":"examples-ex11","language":"php","code":"echo $r->set('mykey', 'Hello') . PHP_EOL;            // >>> OK\n        echo $r->expire('mykey', 10) . PHP_EOL;              // >>> 1\n\n        $ttlResult = $r->ttl('mykey');\n        echo $ttlResult . PHP_EOL;                           // >>> 10","section_id":"examples"},{"id":"examples-ex12","language":"python","code":"res = r.set(\"mykey\", \"Hello\")\nprint(res)\n# >>> True\n\nres = r.expire(\"mykey\", 10)\nprint(res)\n# >>> True\n\nres = r.ttl(\"mykey\")\nprint(res)\n# >>> 10","section_id":"examples"},{"id":"examples-ex13","language":"rust","code":"if let Ok(res) = r.set(\"mykey\", \"Hello\").await {\n            let res: String = res;\n            println!(\"{res}\");    // >>> OK\n        }\n\n        match r.expire(\"mykey\", 10).await {\n            Ok(res) => {\n                let res: bool = res;\n                println!(\"{res}\");    // >>> true\n            },\n            Err(e) => {\n                println!(\"Error setting key expiration: {e}\");\n                return;\n            }\n        }\n\n        match r.ttl(\"mykey\").await {\n            Ok(res) => {\n                let res: i64 = res;\n                println!(\"{res}\");    // >>> 10\n            },\n            Err(e) => {\n                println!(\"Error getting key TTL: {e}\");\n                return;\n            }\n        }","section_id":"examples"},{"id":"examples-ex14","language":"rust","code":"if let Ok(res) = r.set(\"mykey\", \"Hello\") {\n            let res: String = res;\n            println!(\"{res}\");    // >>> OK\n        }\n\n        match r.expire(\"mykey\", 10) {\n            Ok(res) => {\n                let res: bool = res;\n                println!(\"{res}\");    // >>> true\n            },\n            Err(e) => {\n                println!(\"Error setting key expiration: {e}\");\n                return;\n            }\n        }\n\n        match r.ttl(\"mykey\") {\n            Ok(res) => {\n                let res: i64 = res;\n                println!(\"{res}\");    // >>> 10\n            },\n            Err(e) => {\n                println!(\"Error getting key TTL: {e}\");\n                return;\n            }\n        }","section_id":"examples"}]}
{"schema_version":2,"id":"commands/type","title":"TYPE","url":"https://redis.io/docs/latest/commands/type/","summary":"Determines the type of value stored at a key.","since":"1.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"953123abceab17f251b77bebf9d117544d8152cc4986b141ed6b1e3219b08241","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns the string representation of the type of the value stored at `key`.\nThe different types that can be returned are: `string`, `list`, `set`, `zset`,\n`hash`, `stream`, and `vectorset`."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SET key1 \"value\"\nOK\nredis> LPUSH key2 \"value\"\n(integer) 1\nredis> SADD key3 \"value\"\n(integer) 1\nredis> TYPE key1\nstring\nredis> TYPE key2\nlist\nredis> TYPE key3\nset"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): the type of _key_, or `none` when _key_ doesn't exist.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): the type of _key_, or `none` when _key_ doesn't exist."}],"examples":[]}
{"schema_version":2,"id":"commands/unlink","title":"UNLINK","url":"https://redis.io/docs/latest/commands/unlink/","summary":"Asynchronously deletes one or more keys.","since":"4.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"68340a90b9533f8772ff83a1fa1c2732e3a62fea887026814a389b15b7c277ff","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nThis command is very similar to [`DEL`](https://redis.io/docs/latest/commands/del): it removes the specified keys.\nJust like [`DEL`](https://redis.io/docs/latest/commands/del) a key is ignored if it does not exist. However the command\nperforms the actual memory reclaiming in a different thread, so it is not\nblocking, while [`DEL`](https://redis.io/docs/latest/commands/del) is. This is where the command name comes from: the\ncommand just unlinks the keys from the keyspace. The actual removal\nwill happen later asynchronously."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key [key ...]</code></summary>\n\nOne or more keys to delete asynchronously.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> SET key1 \"Hello\"\nOK\nredis> SET key2 \"World\"\nOK\nredis> UNLINK key1 key2 key3\n(integer) 2"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of keys that were unlinked.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of keys that were unlinked."}],"examples":[]}
{"schema_version":2,"id":"commands/unsubscribe","title":"UNSUBSCRIBE","url":"https://redis.io/docs/latest/commands/unsubscribe/","summary":"Stops listening to messages posted to channels.","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-06-15T06:26:34-07:00","page_type":"content","content_hash":"65e6284e587ef987441277855409cd02b538f85a95a7b1e71f817874046e5e7f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Unsubscribes the client from the specified channels, or from all channels if none are specified.\n\nIf you don’t specify any channels, the client unsubscribes from all previously subscribed channels. The client receives one message for each channel it unsubscribes from."},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>channel [channel ...]</code></summary>\n\nOne or more channels to unsubscribe from. If omitted, the client is unsubscribed from all channels.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nWhen successful, this command doesn't return anything. Instead, for each channel, one message with the first element being the string `unsubscribe` is pushed as a confirmation that the command succeeded.\n\n**RESP3:**\n\nWhen successful, this command doesn't return anything. Instead, for each channel, one message with the first element being the string `unsubscribe` is pushed as a confirmation that the command succeeded."}],"examples":[]}
{"schema_version":2,"id":"commands/unwatch","title":"UNWATCH","url":"https://redis.io/docs/latest/commands/unwatch/","summary":"Forgets about watched keys of a transaction.","since":"2.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"535fec65a063cf5e8dffe2461ca38e40b83805aeb211296ee3be40b5260f6ede","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Flushes all the previously watched keys for a [transaction](https://redis.io/docs/latest/develop/using-commands/transactions).\n\nIf you call [`EXEC`](https://redis.io/docs/latest/commands/exec) or [`DISCARD`](https://redis.io/docs/latest/commands/discard), there's no need to manually call `UNWATCH`."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[]}
{"schema_version":2,"id":"commands/vadd","title":"VADD","url":"https://redis.io/docs/latest/commands/vadd/","summary":"Add a new element to a vector set, or update its vector if it already exists.","since":"8.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"1b50aad87322065b90ae2dd4ddf83cd2b1aa89c58c37645cf4a79dd45d00c9d8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"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):\n\n[code example]"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>key</code></summary>\n\nis the name of the key that will hold the vector set data.\n</details>\n\n<details open>\n<summary><code>FP32 vector or VALUES num vector</code></summary>\n\neither a 32-bit floating point (FP32) blob of values or `num` floating point numbers as strings. When using the FP32 format, the blob must use little-endian encoding. On platforms with different endianness, you should either manually adjust the byte order or use the VALUES syntax instead.\n</details>\n\n<details open>\n<summary><code>element</code></summary>\n\nis the name of the element that is being added to the vector set.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open>\n<summary><code>REDUCE dim</code></summary>\n\nimplements random projection to reduce the dimensionality of the vector. The projection matrix is saved and reloaded along with the vector set. Please note that the REDUCE option must be passed immediately before the vector. For example,\n\n[code example]\n</details>\n\n<details open>\n<summary><code>CAS</code></summary>\n\nperforms the operation partially using threads, in a check-and-set style. The neighbor candidates collection, which is slow, is performed in the background, while the command is executed in the main thread.\n</details>\n\n<details open>\n<summary><code>NOQUANT</code></summary>\n\nin the first VADD call for a given key, NOQUANT forces the vector to be created without int8 quantization, which is otherwise the default.\n</details>\n\n<details open>\n<summary><code>BIN</code></summary>\n\nforces the vector to use binary quantization instead of int8. This is much faster and uses less memory, but impacts the recall quality.\n</details>\n\n<details open>\n<summary><code>Q8</code></summary>\n\nforces the vector to use signed 8-bit quantization. This is the default, and the option only exists to make sure to check at insertion time that the vector set is of the same format.\n</details>\n\n> [!NOTE]\n> `NOQUANT`, `Q8`, and `BIN` are mutually exclusive.\n\n<details open>\n<summary><code>EF build-exploration-factor</code></summary>\n\nplays a role in the effort made to find good candidates when connecting the new node to the existing Hierarchical Navigable Small World (HNSW) graph. The default is 200. Using a larger value may help in achieving a better recall. To improve the recall it is also possible to increase EF during VSIM searches.\n</details>\n\n<details open>\n<summary><code>SETATTR attributes</code></summary>\n\nassociates attributes in the form of a JavaScript object to the newly created entry or updates the attributes (if they already exist).\nIt is the same as calling the VSETATTR command separately.\n</details>\n\n<details open>\n<summary><code>M numlinks</code></summary>\n\nis the maximum number of connections that each node of the graph will have with other nodes. The default is 16. More connections means more memory, but provides for more efficient graph exploration. Nodes at layer zero (every node exists at least at layer zero) have `M * 2` connections, while the other layers only have `M` connections. For example, setting `M` to `64` will use at least 1024 bytes of memory for layer zero. That's `M * 2` connections times 8 bytes (pointers), or `128 * 8 = 1024`. For higher layers, consider the following:\n\n- Each node appears in ~1.33 layers on average (empirical observation from HNSW papers), which works out to be 0.33 higher layers per node.\n- Each of those higher layers has `M = 64` connections.\n\nSo, the additional amount of memory is approximately `0.33 × 64 × 8 ≈ 169.6` bytes per node, bringing the total memory to ~1193 bytes.\n\nIf you don't have a recall quality problem, the default is acceptable, and uses a minimal amount of memory.\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): 1 if key was added; 0 if key was not added.\n* [Simple error reply](../../develop/reference/protocol-spec#simple-errors): if the command was malformed.\n\n**RESP3:**\n\nOne of the following:\n* [Boolean reply](../../develop/reference/protocol-spec#booleans): true if key was added; false if key was not added.\n* [Simple error reply](../../develop/reference/protocol-spec#simple-errors): if the command was malformed."},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Vector sets](https://redis.io/docs/latest/develop/data-types/vector-sets)\n- [Filtered search](https://redis.io/docs/latest/develop/data-types/vector-sets/filtered-search)"}],"examples":[{"id":"overview-ex0","language":"plaintext","code":"VADD mykey VALUES 3 0.1 1.2 0.5 my-element","section_id":"overview"},{"id":"optional-arguments-ex0","language":"plaintext","code":"VADD mykey REDUCE 50 VALUES ...","section_id":"optional-arguments"}]}
{"schema_version":2,"id":"commands/vcard","title":"VCARD","url":"https://redis.io/docs/latest/commands/vcard/","summary":"Return the number of elements in a vector set.","since":"8.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"7e26716046ca3ea8ea9471fdf086e2a8e21b62a5ec6673190cde174c148da9a0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Return the number of elements in the specified vector set.\n\n[code example]"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>key</code></summary>\n\nis the name of the key that holds the vector set.\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): 0 if the key doesn't exist or the number of elements contained in the vector set.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): 0 if the key doesn't exist or the number of elements contained in the vector set."},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Vector sets](https://redis.io/docs/latest/develop/data-types/vector-sets)"}],"examples":[{"id":"overview-ex0","language":"shell","code":"VCARD word_embeddings\n(integer) 3000000","section_id":"overview"}]}
{"schema_version":2,"id":"commands/vdim","title":"VDIM","url":"https://redis.io/docs/latest/commands/vdim/","summary":"Return the dimension of vectors in the vector set.","since":"8.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"165315c0751c07beaf7f3980e25990950d7bf0a9bf5dad22311587fa6b6fcd8d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Return the number of dimensions of the vectors in the specified vector set.\n\n[code example]\n\nIf the vector set was created using the `REDUCE` option for dimensionality reduction, this command reports the reduced dimension. However, you must still use full-size vectors when performing queries with the `VSIM` command."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>key</code></summary>\n\nis the name of the key that holds the vector set.\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): the number of vector set elements.\n* [Simple error reply](../../develop/reference/protocol-spec#simple-errors): if the key does not exist.\n\n**RESP3:**\n\nOne of the following:\n* [Integer reply](../../develop/reference/protocol-spec#integers): the number of vector set elements.\n* [Simple error reply](../../develop/reference/protocol-spec#simple-errors): if the key does not exist."},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Vector sets](https://redis.io/docs/latest/develop/data-types/vector-sets)"}],"examples":[{"id":"overview-ex0","language":"shell","code":"VDIM word_embeddings\n(integer) 300","section_id":"overview"}]}
{"schema_version":2,"id":"commands/vemb","title":"VEMB","url":"https://redis.io/docs/latest/commands/vemb/","summary":"Return the vector associated with an element.","since":"8.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"b07de4c4db1b9fba43f7d2b176851133c322b0fa438fa42d4526cbcd07899ecd","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Return the approximate vector associated with a given element in the vector set.\n\n[code example]\n\nVector sets normalize and may quantize vectors on insertion. `VEMB` reverses this process to approximate the original vector by de-normalizing and de-quantizing it.\n\nTo retrieve the raw internal representation, use the `RAW` option:\n\n[code example]"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>key</code></summary>\n\nis the name of the key that holds the vector set.\n</details>\n\n<details open>\n<summary><code>element</code></summary>\n\nis the name of the element whose vector you want to retrieve.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open>\n<summary><code>RAW</code></summary>\n\nreturns the raw vector data, its quantization type, and metadata such as norm and range.\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Array reply](../../develop/reference/protocol-spec#arrays): of real numbers as [bulk strings](../../develop/reference/protocol-spec#bulk-strings), representing the vector.\n* [Array reply](../../develop/reference/protocol-spec#arrays): consisting of the following elements:\n    1. The quantization type as a [simple string](../../develop/reference/protocol-spec#simple-strings): `fp32`, `bin`, or `q8`.\n    1. A [bulk string](../../develop/reference/protocol-spec#bulk-strings) blob with the following raw data:\n        * 4-byte floats for fp32 (little-endian encoding)\n        * A bitmap for binary quantization\n        * A byte array for q8\n    1. The L2 norm, as a [simple string](../../develop/reference/protocol-spec#simple-strings), of the vector before normalization.\n    1. (Only for q8): The quantization range as a [simple string](../../develop/reference/protocol-spec#simple-strings). Multiply this by integer components to recover normalized values.\n\n**RESP3:**\n\nOne of the following:\n* [Array reply](../../develop/reference/protocol-spec#arrays): of [doubles](../../develop/reference/protocol-spec#doubles), representing the vector.\n* [Array reply](../../develop/reference/protocol-spec#arrays): consisting of the following elements:\n    1. The quantization type as a [simple string](../../develop/reference/protocol-spec#simple-strings): `fp32`, `bin`, or `q8`.\n    1. A [bulk string](../../develop/reference/protocol-spec#bulk-strings) blob with the following raw data:\n        * 4-byte floats for fp32 (little-endian encoding)\n        * A bitmap for binary quantization\n        * A byte array for q8\n    1. The [double](../../develop/reference/protocol-spec#doubles) L2 norm of the vector before normalization.\n    1. (Only for q8): The quantization range as a [double](../../develop/reference/protocol-spec#doubles). Multiply this by integer components to recover normalized values."},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Vector sets](https://redis.io/docs/latest/develop/data-types/vector-sets)"}],"examples":[{"id":"overview-ex0","language":"shell","code":"VEMB word_embeddings SQL\n1) \"0.18208661675453186\"\n2) \"0.08535309880971909\"\n3) \"0.1365649551153183\"\n4) \"-0.16501599550247192\"\n5) \"0.14225517213344574\"\n... 295 more elements ...","section_id":"overview"},{"id":"overview-ex1","language":"shell","code":"VEMB word_embeddings apple RAW\n1) int8\n2) \"\\xf1\\xdc\\xfd\\x1e\\xcc%E...\\xde\\x1f\\xfbN\" # artificially shortened for this example\n3) \"3.1426539421081543\"\n4) \"0.17898885905742645\"","section_id":"overview"}]}
{"schema_version":2,"id":"commands/vgetattr","title":"VGETATTR","url":"https://redis.io/docs/latest/commands/vgetattr/","summary":"Retrieve the JSON attributes of elements.","since":"8.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"9b12edc8572f780ea00a3c8b98a845e86cdef78b8a1baf4c0aa77a1497909974","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Return the JSON attributes associated with an element in a vector set.\n\n[code example]"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>key</code></summary>\n\nis the name of the key that holds the vector set.\n</details>\n\n<details open>\n<summary><code>element</code></summary>\n\nis the name of the element whose attributes you want to retrieve.\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Simple string reply](../../develop/reference/protocol-spec#simple-strings) containing the JSON attribute(s).\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings) (null bulk string) for unknown key or element, or when no attributes exist for the given key/element pair.\n\n**RESP3:**\n\nOne of the following:\n* [Simple string reply](../../develop/reference/protocol-spec#simple-strings) containing the JSON attribute(s).\n* [Null reply](../../develop/reference/protocol-spec#nulls) for unknown key or element, or when no attributes exist for the given key/element pair."},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Vector sets](https://redis.io/docs/latest/develop/data-types/vector-sets)\n- [Filtered search](https://redis.io/docs/latest/develop/data-types/vector-sets/filtered-search)"}],"examples":[{"id":"overview-ex0","language":"shell","code":"VGETATTR key element","section_id":"overview"}]}
{"schema_version":2,"id":"commands/vinfo","title":"VINFO","url":"https://redis.io/docs/latest/commands/vinfo/","summary":"Return information about a vector set.","since":"8.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"12a9351cfd08e73f8c3881459e0aa7e846049f8635eb9eb2cfc3f7bbd0a5a758","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Return metadata and internal details about a vector set, including size, dimensions, quantization type, and graph structure.\n\n[code example]"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>key</code></summary>\n\nis the name of the key that holds the vector set.\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Array reply](../../develop/reference/protocol-spec#arrays) containing metadata and internal details about a vector set, including size, dimensions, quantization type, and graph structure.\n* [Array reply](../../develop/reference/protocol-spec#arrays) (null array reply) for unknown key.\n\n**RESP3:**\n\nOne of the following:\n* [Array reply](../../develop/reference/protocol-spec#arrays) containing metadata and internal details about a vector set, including size, dimensions, quantization type, and graph structure.\n* [Null reply](../../develop/reference/protocol-spec#nulls) for unknown key."},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Vector sets](https://redis.io/docs/latest/develop/data-types/vector-sets)"}],"examples":[{"id":"overview-ex0","language":"shell","code":"VINFO word_embeddings\n1) quant-type\n2) int8\n3) vector-dim\n4) (integer) 300\n5) size\n6) (integer) 3000000\n7) max-level\n8) (integer) 12\n9) vset-uid\n10) (integer) 1\n11) hnsw-max-node-uid\n12) (integer) 3000000","section_id":"overview"}]}
{"schema_version":2,"id":"commands/vismember","title":"VISMEMBER","url":"https://redis.io/docs/latest/commands/vismember/","summary":"Check if an element exists in a vector set.","since":"8.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"2b84b98bf8d9f9f55d6e276d882f2de0b3b31bc09ef6ecc655c314ff02a25fc5","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Check if an element exists in a vector set."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>key</code></summary>\n\nis the name of the key that holds the vector set.\n</details>\n\n<details open>\n<summary><code>element</code></summary>\n\nis the name of the element you want to check for membership.\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): `0` if the element does not exist in the vector set, or the key does not exist. `1` if the element exists in the vector set.\n\n**RESP3:**\n\n[Boolean reply](../../develop/reference/protocol-spec#booleans): `false` if the element does not exist in the vector set, or the key does not exist. `true` if the element exists in the vector set."},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Vector sets](https://redis.io/docs/latest/develop/data-types/vector-sets)"}],"examples":[]}
{"schema_version":2,"id":"commands/vlinks","title":"VLINKS","url":"https://redis.io/docs/latest/commands/vlinks/","summary":"Return the neighbors of an element at each layer in the HNSW graph.","since":"8.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"e0c43ff5d3c8446b9cc4f792a82e4e55d4baf04cd999e5337369c85c5d9c7493","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Return the neighbors of a specified element in a vector set. The command shows the connections for each layer of the HNSW graph.\n\n[code example]\n\nUse the `WITHSCORES` option to include similarity scores for each neighbor.\n\n[code example]"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>key</code></summary>\n\nis the name of the key that holds the vector set.\n</details>\n\n<details open>\n<summary><code>element</code></summary>\n\nis the name of the element whose HNSW neighbors you want to inspect.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open>\n<summary><code>WITHSCORES</code></summary>\n\nincludes similarity scores for each neighbor.\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* An [array](../../develop/reference/protocol-spec#arrays) of [array replies](../../develop/reference/protocol-spec#arrays) containing the names of adjacent elements as [bulk strings](../../develop/reference/protocol-spec#bulk-strings); interleaved with scores as [bulk strings](../../develop/reference/protocol-spec#bulk-strings) when used with the `WITHSCORES` option.\n* [Simple error reply](../../develop/reference/protocol-spec/#simple-errors) for incorrect syntax.\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings) (null bulk string) for unknown keys and/or elements.\n\n**RESP3:**\n\nOne of the following:\n* An [array](../../develop/reference/protocol-spec#arrays) of [array replies](../../develop/reference/protocol-spec#arrays) containing the names of adjacent elements as [bulk strings](../../develop/reference/protocol-spec#bulk-strings) when used without the `WITHSCORES` option.\n* An [array](../../develop/reference/protocol-spec#arrays) of [map replies](../../develop/reference/protocol-spec#maps) containing the names of adjecent elements as [bulk strings](../../develop/reference/protocol-spec#bulk-strings), together with their scores as [doubles](../../develop/reference/protocol-spec#doubles) when used with the `WITHSCORES` option.\n* [Simple error reply](../../develop/reference/protocol-spec/#simple-errors) for incorrect syntax.\n* [Null reply](../../develop/reference/protocol-spec#nulls) for unknown keys and/or elements."},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Vector sets](https://redis.io/docs/latest/develop/data-types/vector-sets)"}],"examples":[{"id":"overview-ex0","language":"shell","code":"VLINKS key element","section_id":"overview"},{"id":"overview-ex1","language":"shell","code":"VLINKS key element WITHSCORES","section_id":"overview"}]}
{"schema_version":2,"id":"commands/vrandmember","title":"VRANDMEMBER","url":"https://redis.io/docs/latest/commands/vrandmember/","summary":"Return one or multiple random members from a vector set.","since":"8.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"67d55377bffedab912a064033c928e0de3700a84c8306ec172654fce07a94f80","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Return one or more random elements from a vector set.\n\nThe behavior is similar to the `SRANDMEMBER` command:\n\n- When called without a count, returns a single element as a bulk string.\n- When called with a positive count, returns up to that many distinct elements (no duplicates).\n- When called with a negative count, returns that many elements, possibly with duplicates.\n- If the count exceeds the number of elements, the entire set is returned.\n- If the key does not exist, the command returns `null` if no count is given, or an empty array if a count is provided.\n\n[code example]\n\nReturn a single random element:\n\n[code example]\n\nReturn two distinct random elements:\n\n[code example]\n\nReturn 3 random elements with possible duplicates:\n\n[code example]\n\nRequest more elements than exist in the set:\n\n[code example]\n\nWhen the key doesn't exist:\n\n[code example]\n\n[code example]\n\nThis command is useful for:\n\n- Sampling elements for testing or training.\n- Generating random queries for performance testing.\n\nInternally:\n\n- For small counts (less than 20% of the set size), a dictionary is used to ensure uniqueness.\n- For large counts (more than 20% of the set size), a linear scan provides faster performance, though results may be less random."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>key</code></summary>\n\nis the name of the key that holds the vector set.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open>\n<summary><code>count</code></summary>\n\nspecifies the number of elements to return. Positive values return distinct elements; negative values allow duplicates.\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Array reply](../../develop/reference/protocol-spec#arrays) containing the names of count random elements as [strings](../../develop/reference/protocol-spec#simple-strings).\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings) (null bulk string) for unknown keys.\n* [Array reply](../../develop/reference/protocol-spec#arrays) (empty array) for unknown keys when a count is specified.\n\n**RESP3:**\n\nOne of the following:\n* [Array reply](../../develop/reference/protocol-spec#arrays) containing the names of *count* random elements as [strings](../../develop/reference/protocol-spec#simple-strings).\n* [Null reply](../../develop/reference/protocol-spec#nulls) for unknown keys.\n* [Array reply](../../develop/reference/protocol-spec#arrays) (empty array) for unknown keys when a count is specified."},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Vector sets](https://redis.io/docs/latest/develop/data-types/vector-sets)"}],"examples":[{"id":"overview-ex0","language":"shell","code":"VADD vset VALUES 3 1 0 0 elem1\nVADD vset VALUES 3 0 1 0 elem2\nVADD vset VALUES 3 0 0 1 elem3","section_id":"overview"},{"id":"overview-ex1","language":"shell","code":"VRANDMEMBER vset\n\"elem2\"","section_id":"overview"},{"id":"overview-ex2","language":"shell","code":"VRANDMEMBER vset 2\n1) \"elem1\"\n2) \"elem3\"","section_id":"overview"},{"id":"overview-ex3","language":"shell","code":"VRANDMEMBER vset -3\n1) \"elem2\"\n2) \"elem2\"\n3) \"elem1\"","section_id":"overview"},{"id":"overview-ex4","language":"shell","code":"VRANDMEMBER vset 10\n1) \"elem1\"\n2) \"elem2\"\n3) \"elem3\"","section_id":"overview"},{"id":"overview-ex5","language":"shell","code":"VRANDMEMBER nonexistent\n(nil)","section_id":"overview"},{"id":"overview-ex6","language":"shell","code":"VRANDMEMBER nonexistent 3\n(empty array)","section_id":"overview"}]}
{"schema_version":2,"id":"commands/vrange","title":"VRANGE","url":"https://redis.io/docs/latest/commands/vrange/","summary":"Return elements in a lexicographical range","since":"8.4.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"adfb6f39909f82040e2ae632f569124703041fd5b32092ac34c54339f3f429d8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"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.\n\nThe command returns elements in lexicographical order, using byte-by-byte comparison (like `memcmp()`) to establish a total order among elements."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the vector set key from which to retrieve elements.\n\n</details>\n\n<details open><summary><code>start</code></summary>\n\nThe starting point of the lexicographical range. Can be:\n- A string prefixed with `[` for inclusive range (e.g., `[Redis`)\n- A string prefixed with `(` for exclusive range (e.g., `(a7`)\n- The special symbol `-` to indicate the minimum element\n\n</details>\n\n<details open><summary><code>end</code></summary>\n\nThe ending point of the lexicographical range. Can be:\n- A string prefixed with `[` for inclusive range\n- A string prefixed with `(` for exclusive range\n- The special symbol `+` to indicate the maximum element\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>count</code></summary>\n\nThe 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).\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"Retrieve the first 10 elements starting from the string \"Redis\" (inclusive):\n\n[code example]\n\nIterate through all elements, 10 at a time:\n\n[code example]\n\nContinue iteration from the last element of the previous result (exclusive):\n\n[code example]\n\nReturn all elements in the set (use with caution):\n\n[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n- [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of elements in lexicographical order within the specified range.\n- [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) (empty array) if the key doesn't exist.\n\n**RESP3:**\n\nOne of the following:\n\n- [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) of elements in lexicographical order within the specified range.\n- [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) (empty array) if the key doesn't exist."},{"id":"behavior","title":"Behavior","role":"content","text":"- **Iteration guarantees**: Each range will produce exactly the elements that were present in the range at the moment the `VRANGE` command was executed.\n- **Concurrent modifications**: Elements removed or added during iteration may or may not be returned, depending on when they were modified.\n- **Empty key**: If the key doesn't exist, returns an empty array."},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Vector sets](https://redis.io/docs/latest/develop/data-types/vector-sets)"}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"VRANGE word_embeddings [Redis + 10","section_id":"examples"},{"id":"examples-ex1","language":"plaintext","code":"VRANGE mykey - + 10","section_id":"examples"},{"id":"examples-ex2","language":"plaintext","code":"VRANGE mykey (a7 + 10","section_id":"examples"},{"id":"examples-ex3","language":"plaintext","code":"VRANGE mykey - + -1","section_id":"examples"}]}
{"schema_version":2,"id":"commands/vrem","title":"VREM","url":"https://redis.io/docs/latest/commands/vrem/","summary":"Remove an element from a vector set.","since":"8.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"9855fdd11539d0b3ed36f9479cb40ea7dd10f962f1bd2f2a40c8e9eb42a0e2ca","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Remove an element from a vector set.\n\n[code example]\n\n`VREM` reclaims memory immediately. It does not use tombstones or logical deletions, making it safe to use in long-running applications that frequently update the same vector set."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>key</code></summary>\n\nis the name of the key that holds the vector set.\n</details>\n\n<details open>\n<summary><code>element</code></summary>\n\nis the name of the element to remove from the vector set.\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): 0 if either element or key do not exist; 1 if the element was removed.\n\n**RESP3:**\n\n[Boolean reply](../../develop/reference/protocol-spec#booleans): false if either element or key do not exist; true if the element was removed."},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Vector sets](https://redis.io/docs/latest/develop/data-types/vector-sets)"}],"examples":[{"id":"overview-ex0","language":"shell","code":"VADD vset VALUES 3 1 0 1 bar\n(integer) 1\n\nVREM vset bar\n(integer) 1\n\nVREM vset bar\n(integer) 0","section_id":"overview"}]}
{"schema_version":2,"id":"commands/vsetattr","title":"VSETATTR","url":"https://redis.io/docs/latest/commands/vsetattr/","summary":"Associate or remove the JSON attributes of elements.","since":"8.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-28T16:08:11+02:00","page_type":"content","content_hash":"843783f2de20ee28e5e54c83926b14656376b25519d212c170892da10fa16ec7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"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`.\n\nYou can also update existing attributes or delete them by setting an empty string.\n\n[code example]\n\nTo remove attributes, pass an empty JSON string:\n\n[code example]"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>key</code></summary>\n\nis the name of the key that holds the vector set.\n</details>\n\n<details open>\n<summary><code>element</code></summary>\n\nis the name of the element whose attributes you want to set or remove.\n</details>\n\n<details open>\n<summary><code>json</code></summary>\n\nis the JSON string with the attributes of the element. Use an empty string (`\"\"`) to delete the attributes.\nThe string is stored as given, without validation. Elements whose attributes are not valid JSON are treated as not matching by the `FILTER` option of `VSIM`.\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): 0 if either the key or element does not exist; 1 if the attributes were set or deleted.\n\n**RESP3:**\n\n[Boolean reply](../../develop/reference/protocol-spec#booleans): false if either the key or element does not exist; true if the attributes were set or deleted."},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Vector sets](https://redis.io/docs/latest/develop/data-types/vector-sets)\n- [Filtered search](https://redis.io/docs/latest/develop/data-types/vector-sets/filtered-search)"}],"examples":[{"id":"overview-ex0","language":"shell","code":"VSETATTR key element \"{\\\"type\\\": \\\"fruit\\\", \\\"color\\\": \\\"red\\\"}\"","section_id":"overview"},{"id":"overview-ex1","language":"shell","code":"VSETATTR key element \"\"","section_id":"overview"}]}
{"schema_version":2,"id":"commands/vsim","title":"VSIM","url":"https://redis.io/docs/latest/commands/vsim/","summary":"Return elements by vector similarity.","since":"8.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:47:58+01:00","page_type":"content","content_hash":"7f542a1a4c170e34e2dbbb669e32ac3e122580557ea2f23e863719a47d884a27","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Return elements similar to a given vector or element. Use this command to perform approximate or exact similarity searches within a vector set.\n\nYou can query using either a vector (via `FP32` or `VALUES num`) or by referencing another element (using `ELE`). Optional parameters let you control the search behavior, such as score output, result count, and filtering options.\n\n[code example]\n\nYou can include similarity scores, attributes (if any), and limit the number of results:\n\n[code example]\n\nSet the `EF` (exploration factor) to improve recall at the cost of performance. Use the `TRUTH` option to perform an exact linear scan, useful for benchmarking. The `NOTHREAD` option runs the search in the main thread and may increase server latency."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>key</code></summary>\n\nis the name of the key that holds the vector set data.\n</details>\n\n<details open>\n<summary><code>ELE | FP32 | VALUES num</code></summary>\n\nspecifies how the input vector is provided. Use `ELE` to refer to an existing element, `FP32` for binary float format (little-endian encoding required), or `VALUES num` for a list of stringified float values.\n</details>\n\n<details open>\n<summary><code>vector or element</code></summary>\n\nis either the vector data (for `FP32` or `VALUES`) or the name of the element (for `ELE`) to use as the similarity reference.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open>\n<summary><code>WITHSCORES</code></summary>\n\nreturns the similarity score (from 1 to 0) alongside each result. A score of 1 is identical; 0 is the opposite.\n</details>\n\n<details open>\n<summary><code>WITHATTRIBS</code></summary>\n\nreturns, for each element, the JSON attribute associated with the element or NULL when no attributes are present.\n</details>\n\n<details open>\n<summary><code>COUNT num</code></summary>\n\nlimits the number of returned results to `num`.\n</details>\n\n<details open>\n<summary><code>EPSILON delta</code></summary>\n\nis a floating point number between 0 and 1. It is used to retrieve elements that have a distance that is no further than the specified `delta`. In vector sets, returned elements have a similarity score (when compared to the query vector) that is between 1 and 0, where 1 means identical and 0 means opposite vectors. For example, if the `EPSILON` option is specified with an argument of `0.2`, it means only elements that have a similarity of 0.8 or better (a distance < 0.2) are returned. This is useful when you specify a large `COUNT`, but you don't want elements that are too far away from the query vector.\n</details>\n\n<details open>\n<summary><code>EF search-exploration-factor</code></summary>\n\ncontrols the search effort. Higher values explore more nodes, improving recall at the cost of speed. Typical values range from 50 to 1000.\n</details>\n\n<details open>\n<summary><code>FILTER expression</code></summary>\n\napplies a filter expression to restrict matching elements. See [Filtered search](https://redis.io/docs/latest/develop/data-types/vector-sets/filtered-search) for syntax details.\n</details>\n\n<details open>\n<summary><code>FILTER-EF max-filtering-effort</code></summary>\n\nlimits the number of filtering attempts for the `FILTER` expression. See [Filtered search](https://redis.io/docs/latest/develop/data-types/vector-sets/filtered-search) for more information.\n</details>\n\n<details open>\n<summary><code>TRUTH</code></summary>\n\nforces an exact linear scan of all elements, bypassing the HNSW graph. Use for benchmarking or to calculate recall. This is significantly slower (O(N)).\n</details>\n\n<details open>\n<summary><code>NOTHREAD</code></summary>\n\nexecutes the search in the main thread instead of a background thread. Useful for small vector sets or benchmarks. This may block the server during execution.\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Simple error reply](../../develop/reference/protocol-spec/#simple-errors) for an unknown element.\n* [Array reply](../../develop/reference/protocol-spec#arrays) (empty array) for an unknown key.\n* [Array reply](../../develop/reference/protocol-spec#arrays) with matching elements.\n* With the `WITHSCORES` option, an [array reply](../../develop/reference/protocol-spec#arrays) with matching [bulk string](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) elements juxtaposed with [bulk string](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) as floating-point scores.\n* With the `WITHSCORES` and `WITHATTRIBS` options, an [array reply](../../develop/reference/protocol-spec#arrays) with matching [bulk string](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) elements, and two additional elements: (1) a [bulk string](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) as floating-point score and (2) a [bulk string](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) representing the JSON attribute associated with the element or [nil (null bulk string)](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) for the elements missing an attribute.\n\n**RESP3:**\n\nOne of the following:\n* [Simple error reply](../../develop/reference/protocol-spec/#simple-errors) for unknown element.\n* [Array reply](../../develop/reference/protocol-spec#arrays) (empty array) for unknown key.\n* [Array reply](../../develop/reference/protocol-spec#arrays) with matching elements.\n* With the `WITHSCORES` option, a [map reply](../../develop/reference/protocol-spec#maps) with matching [bulk string](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) elements (keys) and  [double](../../develop/reference/protocol-spec#doubles) scores (values).\n* With the `WITHSCORES` and `WITHATTRIBS` options, a [Map reply](../../develop/reference/protocol-spec#maps) with matching [bulk string](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) elements (keys), and an additional array (values) with the following elements: (1) a [double reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings)  for the score and (2) a [bulk string](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) representing the JSON attribute associated with the element or [null](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls) for the elements missing an attribute."},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Vector sets](https://redis.io/docs/latest/develop/data-types/vector-sets)\n- [Filtered search](https://redis.io/docs/latest/develop/data-types/vector-sets/filtered-search)"}],"examples":[{"id":"overview-ex0","language":"shell","code":"VSIM word_embeddings ELE apple\n1) \"apple\"\n2) \"apples\"\n3) \"pear\"\n4) \"fruit\"\n5) \"berry\"\n6) \"pears\"\n7) \"strawberry\"\n8) \"peach\"\n9) \"potato\"\n10) \"grape\"","section_id":"overview"},{"id":"overview-ex1","language":"shell","code":"VSIM word_embeddings ELE apple WITHSCORES WITHATTRIBS COUNT 3\n1) \"apple\"\n2) \"0.9998867657923256\"\n3) \"{\\\"len\\\": 5}\"\n4) \"apples\"\n5) \"0.859852746129036\"\n6) \"{\\\"len\\\": 6}\"\n7) \"pear\"\n8) \"0.8226882070302963\"\n9) \"{\\\"len\\\": 4}\"","section_id":"overview"}]}
{"schema_version":2,"id":"commands/wait","title":"WAIT","url":"https://redis.io/docs/latest/commands/wait/","summary":"Blocks until the asynchronous replication of all preceding write commands sent by the connection is completed.","since":"3.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"10b6906b42361a23fb1d9bd57fcc620b35737c93a59d1056d9a9d25f8a3047ff","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This command blocks the current client until all the previous write commands\nare successfully transferred and acknowledged by at least the number\nof replicas you specify in the `numreplicas` argument. If the value\nyou specify for the `timeout` argument (in milliseconds) is reached, the command\nreturns even if the specified number of replicas were not yet reached.\n\nThe command will always return the number of replicas that acknowledged\nthe write commands sent by the current client before the `WAIT` command, both in the case where\nthe specified number of replicas are reached, or when the timeout is reached.\n\nA few remarks:\n\n1. When `WAIT` returns, all the previous write commands sent in the context of the current connection are guaranteed to be received by the number of replicas returned by `WAIT`.\n2. If the command is sent as part of a [`MULTI`](https://redis.io/docs/latest/commands/multi) transaction (since Redis 7.0, any context that does not allow blocking, such as inside scripts), the command does not block but instead just return ASAP the number of replicas that acknowledged the previous write commands.\n3. A timeout of 0 means to block forever.\n4. Since `WAIT` returns the number of replicas reached both in case of failure and success, the client should check that the returned value is equal or greater to the replication level it demanded."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>numreplicas</code></summary>\n\nThe number of replicas to wait for.\n\n</details>\n\n<details open><summary><code>timeout</code></summary>\n\nThe maximum time to wait, in milliseconds. `0` means wait forever.\n\n</details>"},{"id":"details","title":"Details","role":"content","text":""},{"id":"consistency-and-wait","title":"Consistency and WAIT","role":"content","text":"`WAIT` does not make Redis a strongly consistent store: while synchronous replication is part of a replicated state machine, it is not the only thing needed. However in the context of Sentinel or Redis Cluster failover, `WAIT` improves the real world data safety.\n\nSpecifically, if a given write is transferred to one or more replicas, it is more likely (but not guaranteed) that if the master fails, we'll be able to promote, during a failover, a replica that received the write: both Sentinel and Redis Cluster will do a best-effort attempt to promote the best replica among the set of available replicas.\n\nHowever this is just a best-effort attempt so it is possible to still lose a write synchronously replicated to multiple replicas."},{"id":"implementation","title":"Implementation","role":"content","text":"Since the introduction of partial resynchronization with replicas (PSYNC feature) Redis replicas asynchronously ping their master with the offset they already processed in the replication stream. This is used in multiple ways:\n\n1. Detect timed out replicas.\n1. Perform a partial resynchronization after a disconnection.\n1. Implement `WAIT`.\n\nIn the specific case of the implementation of `WAIT`, Redis remembers, for each client, the replication offset of the produced replication stream when a given\nwrite command was executed in the context of a given client. When `WAIT` is\ncalled Redis checks if the specified number of replicas already acknowledged\nthis offset or a greater one."},{"id":"examples","title":"Examples","role":"example","text":"[code example]\n\nIn the previous example, the first WAIT call does not use a timeout. It waits for the write to reach one replica and returns successfully.\n\nThe second WAIT call uses a timeout and waits for the write to reach two replicas. Because only one replica is available, WAIT unblocks after one second and returns 1, the number of replicas that acknowledged the write."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active\\*</nobr></span> | <span title=\"Not supported\">&#x274c; Standard**</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | \\*For Active-Active databases, `WAIT` commands are supported for primary and replica shard replication. You can contact support to enable `WAIT` for local replicas only. `WAIT` is not supported for cross-instance replication.<br></br>\\*\\*`WAIT` commands are supported on Redis Cloud Flexible subscriptions. |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the command returns the number of replicas reached by all the writes performed in the context of the current connection.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of replicas reached by all the writes performed in the context of the current connection."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> SET foo bar\nOK\n> WAIT 1 0\n(integer) 1\n> WAIT 2 1000\n(integer) 1","section_id":"examples"}]}
{"schema_version":2,"id":"commands/waitaof","title":"WAITAOF","url":"https://redis.io/docs/latest/commands/waitaof/","summary":"Blocks until all of the preceding write commands sent by the connection are written to the append-only file of the master and/or replicas.","since":"7.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:32+01:00","page_type":"content","content_hash":"060e7a118cf3509cdee3dea6b1f5810f3d8ae2bb2750310f5ac4743d35f01d5a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This command blocks the current client until all previous write commands by that client are acknowledged as having been fsynced to the AOF of the local Redis and/or at least the specified number of replicas.\n\n`numlocal` represents the number of local fsyncs required to be confirmed before proceeding.\nWhen `numlocal` is set to 1, the command blocks until the data written to the Redis instance is confirmed to be persisted to the local AOF file.\nThe value 0 disables this check.\n\nIf the timeout, specified in milliseconds, is reached, the command returns even if the specified number of acknowledgments has not been met.\n\nThe command will always return the number of masters and replicas that have fsynced all write commands sent by the current client before the `WAITAOF` command, both in the case where the specified thresholds were met, and when the timeout is reached.\n\nA few remarks:\n\n1. When `WAITAOF` returns, all the previous write commands sent in the context of the current connection are guaranteed to be fsynced to the AOF of at least the number of masters and replicas returned by `WAITAOF`.\n2. If the command is sent as part of a [`MULTI`](https://redis.io/docs/latest/commands/multi) transaction (or any other context that does not allow blocking, such as inside scripts), the command does not block but instead returns immediately the number of masters and replicas that fsynced all previous write commands.\n3. A timeout of 0 means to block forever.\n4. Since `WAITAOF` returns the number of fsyncs completed both in case of success and timeout, the client should check that the returned values are equal or greater than the persistence level required.\n5. `WAITAOF` cannot be used on replica instances, and the `numlocal` argument cannot be non-zero if the local Redis does not have AOF enabled."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>numlocal</code></summary>\n\nThe number of local Redis instances to wait for an AOF fsync on (`0` or `1`).\n\n</details>\n\n<details open><summary><code>numreplicas</code></summary>\n\nThe number of replicas to wait for an AOF fsync on.\n\n</details>\n\n<details open><summary><code>timeout</code></summary>\n\nThe maximum time to wait, in milliseconds. `0` means wait forever.\n\n</details>"},{"id":"details","title":"Details","role":"content","text":""},{"id":"limitations","title":"Limitations","role":"limits","text":"It is possible to write a module or Lua script that propagate writes to the AOF but not the replication stream.\n(For modules, this is done using the `fmt` argument to `RedisModule_Call` or `RedisModule_Replicate`; For Lua scripts, this is achieved using `redis.set_repl`.)\n\nThese features are incompatible with the `WAITAOF` command as it is currently implemented, and using them in combination may result in incorrect behavior."},{"id":"consistency-and-waitaof","title":"Consistency and WAITAOF","role":"content","text":"Note that, similarly to [`WAIT`](https://redis.io/docs/latest/commands/wait), `WAITAOF` does not make Redis a strongly-consistent store.\nUnless waiting for all members of a cluster to fsync writes to disk, data can still be lost during a failover or a Redis restart.\nHowever, `WAITAOF` does improve real-world data safety."},{"id":"implementation","title":"Implementation","role":"content","text":"Since Redis 7.2, Redis tracks and increments the replication offset even when no replicas are configured (as long as AOF exists).\n\nIn addition, Redis replicas asynchronously ping their master with two replication offsets: the offset they have processed in the replication stream, and the offset they have fsynced to their AOF.\n\nRedis remembers, for each client, the replication offset of the produced replication stream when the last write command was executed in the context of that client.\nWhen `WAITAOF` is called, Redis checks if the local Redis and/or the specified number of replicas have confirmed fsyncing this offset or a greater one to their AOF."},{"id":"examples","title":"Examples","role":"example","text":"[code example]\n\nIn the above example, the first call to `WAITAOF` does not use a timeout and asks for the write to be fsynced to the local Redis only; it returns with [1, 0] when this is completed.\n\nIn the second attempt we instead specify a timeout, and ask for the write to be confirmed as fsynced by a single replica.\nSince there are no connected replicas, the `WAITAOF` command unblocks after one second and again returns [1, 0], indicating the write has been fsynced on the local Redis but no replicas."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): The command returns an array of two integers:\n1. The first is the number of local Redises (0 or 1) that have fsynced to AOF  all writes performed in the context of the current connection\n2. The second is the number of replicas that have acknowledged doing the same.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): The command returns an array of two integers:\n1. The first is the number of local Redises (0 or 1) that have fsynced to AOF  all writes performed in the context of the current connection\n2. The second is the number of replicas that have acknowledged doing the same."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> SET foo bar\nOK\n> WAITAOF 1 0 0\n1) (integer) 1\n2) (integer) 0\n> WAITAOF 0 1 1000\n1) (integer) 1\n2) (integer) 0","section_id":"examples"}]}
{"schema_version":2,"id":"commands/watch","title":"WATCH","url":"https://redis.io/docs/latest/commands/watch/","summary":"Monitors changes to keys to determine the execution of a transaction.","since":"2.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:21+01:00","page_type":"content","content_hash":"ea4f64850e1c23b9915944776aea6e9bd43be110f0c03f1a0cd741c9832d827b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nMarks the given keys to be watched for conditional execution of a\n[transaction](https://redis.io/docs/latest/develop/using-commands/transactions)."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[]}
{"schema_version":2,"id":"commands/xack","title":"XACK","url":"https://redis.io/docs/latest/commands/xack/","summary":"Returns the number of messages that were successfully acknowledged by the consumer group member of a stream.","since":"5.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:25+01:00","page_type":"content","content_hash":"a5607287c3ab32e19ce4cc5d33f05d5ce2f787ca24b9171a84473df7fb023713","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `XACK` command removes one or multiple messages from the\n*Pending Entries List* (PEL) of a stream consumer group. A message is pending,\nand as such stored inside the PEL, when it was delivered to some consumer,\nnormally as a side effect of calling [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup), or when a consumer took\nownership of a message calling [`XCLAIM`](https://redis.io/docs/latest/commands/xclaim). The pending message was delivered to\nsome consumer but the server is yet not sure it was processed at least once.\nSo new calls to [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) to grab the messages history for a consumer\n(for instance using an ID of 0), will return such message.\nSimilarly the pending message will be listed by the [`XPENDING`](https://redis.io/docs/latest/commands/xpending) command,\nthat inspects the PEL.\n\nOnce a consumer *successfully* processes a message, it should call `XACK`\nso that such message does not get processed again, and as a side effect,\nthe PEL entry about this message is also purged, releasing memory from the\nRedis server."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe stream key.\n\n</details>\n\n<details open><summary><code>group</code></summary>\n\nThe consumer group name.\n\n</details>\n\n<details open><summary><code>id [id ...]</code></summary>\n\nOne or more message IDs to acknowledge.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): The command returns the number of messages successfully acknowledged. Certain message IDs may no longer be part of the PEL (for example because they have already been acknowledged), and XACK will not count them as successfully acknowledged.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): The command returns the number of messages successfully acknowledged. Certain message IDs may no longer be part of the PEL (for example because they have already been acknowledged), and XACK will not count them as successfully acknowledged."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"redis> XACK mystream mygroup 1526569495631-0\n(integer) 1","section_id":"examples"}]}
{"schema_version":2,"id":"commands/xackdel","title":"XACKDEL","url":"https://redis.io/docs/latest/commands/xackdel/","summary":"Acknowledges and conditionally deletes one or multiple entries for a stream consumer group.","since":"8.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:25+01:00","page_type":"content","content_hash":"0dd1cc3efb26ca4d3faa063e8a36c569534bbd246137d1b9d5d649d6932c01ee","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Acknowledges and conditionally deletes one or multiple entries (messages) for a stream consumer group at the specified `key`.\n\n`XACKDEL` combines the functionality of [`XACK`](https://redis.io/docs/latest/commands/xack) and [`XDEL`](https://redis.io/docs/latest/commands/xdel) in Redis Streams. It acknowledges the specified entry IDs in the given consumer group and simultaneously attempts to delete the corresponding entries from the stream."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>key</code></summary>\n\nThe name of the stream key.\n</details>\n\n<details open>\n<summary><code>group</code></summary>\n\nThe name of the consumer group.\n</details>\n\n<details open>\n<summary><code>IDS numids id [id ...]</code></summary>\n\nThe IDS block specifying which entries to acknowledge and delete:\n- `numids`: The number of IDs that follow\n- `id [id ...]`: One or more stream entry IDs to acknowledge and delete\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open>\n<summary><code>KEEPREF | DELREF | ACKED</code></summary>\n\nSpecifies how to handle consumer group references when acknowledging and deleting entries. Available since Redis 8.2. If no option is specified, `KEEPREF` is used by default:\n\n- `KEEPREF` (default): Acknowledges the entries in the specified consumer group and deletes the entries from the stream, but preserves existing references to these entries in all consumer groups' PEL (Pending Entries List).\n- `DELREF`: Acknowledges the entries in the specified consumer group, deletes the entries from the stream, and also removes all references to these entries from all consumer groups' pending entry lists, effectively cleaning up all traces of the entries. If an entry ID is not in the stream, but there are dangling references, `XACKDEL` with `DELREF` would still remove all those references.\n- `ACKED`: Acknowledges the entries in the specified consumer group and only deletes entries that were read and acknowledged by all consumer groups.\n</details>\n\nThis command is particularly useful when you want to both acknowledge entry processing and clean up the stream in a single atomic operation, providing fine-grained control over how entry references are handled.\n\n> [!NOTE]\n> When using multiple consumer groups, users are encouraged to use `XACKDEL` with the `ACKED` option instead of `XACK` and `XDEL`, simplifying the application logic."},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): -1 for each requested ID when the given key does not exist.\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): For each ID:\n    * [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): 1 if the entry was acknowledged and deleted from the stream.\n    * [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): -1 if no such ID exists in the provided stream key.\n    * [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): 2 if the entry was acknowledged but not deleted, as there are still dangling references (ACKED option).\n\n**RESP3:**\n\nOne of the following:\n\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): -1 for each requested ID when the given key does not exist.\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): For each ID:\n    * [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): 1 if the entry was acknowledged and deleted from the stream.\n    * [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): -1 if no such ID exists in the provided stream key.\n    * [Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): 2 if the entry was acknowledged but not deleted, as there are still dangling references (ACKED option)."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> XADD mystream * field1 value1\n\"1755870377536-0\"\n> XADD mystream * field2 value2\n\"1755870387045-0\"\n> XGROUP CREATE mystream mygroup 0\nOK\n> XREADGROUP GROUP mygroup consumer1 COUNT 2 STREAMS mystream >\n1) 1) \"mystream\"\n   2) 1) 1) \"1755870377536-0\"\n         2) 1) \"field1\"\n            2) \"value1\"\n      2) 1) \"1755870387045-0\"\n         2) 1) \"field2\"\n            2) \"value2\"\n> XPENDING mystream mygroup\n1) (integer) 2\n2) \"1755870377536-0\"\n3) \"1755870387045-0\"\n4) 1) 1) \"consumer1\"\n      2) \"2\"\n> XACKDEL mystream mygroup KEEPREF IDS 2 1755870377536-0 1755870387045-0\n1) (integer) 1\n2) (integer) 1\n> XPENDING mystream mygroup\n1) (integer) 0\n2) (nil)\n3) (nil)\n4) (nil)\n> XRANGE mystream - +\n(empty array)","section_id":"examples"}]}
{"schema_version":2,"id":"commands/xadd","title":"XADD","url":"https://redis.io/docs/latest/commands/xadd/","summary":"Appends a new message to a stream. Creates the key if it doesn't exist.","since":"5.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:25+01:00","page_type":"content","content_hash":"d8269a877e7f4fa027b035a62f7d458e9860edc921356a863f0515353d67bc62","sections":[{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>key</code></summary>\n\nThe name of the stream key.\n</details>\n\n<details open>\n<summary><code>id</code></summary>\n\nThe stream entry ID. Use `*` to auto-generate a unique ID, or specify a well-formed ID in the format `<ms>-<seq>` (for example, `1526919030474-55`).\n</details>\n\n<details open>\n<summary><code>field value [field value ...]</code></summary>\n\nOne or more field-value pairs that make up the stream entry. You must provide at least one field-value pair.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open>\n<summary><code>NOMKSTREAM</code></summary>\n\nPrevents the creation of a new stream if the key does not exist. Available since Redis 6.2.0.\n</details>\n\n<details open>\n<summary><code>IDMPAUTO producer-id | IDMP producer-id idempotent-id</code></summary>\n\nEnables idempotent message processing (at-most-once production) to prevent duplicate entries. Available since Redis 8.6.\n\n- `IDMPAUTO producer-id`: Automatically generates a unique idempotent ID (iid) for the specified producer-id. Redis tracks this iid to prevent duplicate messages from the same producer-id.\n- `IDMP producer-id idempotent-id`: Uses the specified idempotent-id for the given producer-id. If this producer-id/idempotent-id combination was already used, the command returns the ID of the original entry instead of creating a duplicate.\n\nThe producer-id identifies the source of the message, while the idempotent-id ensures uniqueness within that producer-id's message stream. Redis maintains an internal map of recent producer-id/idempotent-id combinations to detect and prevent duplicates.\n\nBoth modes can only be specified when the entry ID is `*` (auto-generated).\n\nUse [`XCFGSET`](https://redis.io/docs/latest/commands/xcfgset) to configure how long idempotent IDs are retained (`IDMP-DURATION`) and the maximum number tracked per producer (`IDMP-MAXSIZE`).\n\nSee [Idempotent message processing](https://redis.io/docs/latest/develop/data-types/streams/idempotency) for more information.\n\n</details>\n\n<details open>\n<summary><code>KEEPREF | DELREF | ACKED</code></summary>\n\nSpecifies how to handle consumer group references when trimming. If there are no consumer groups, these arguments have no effect. Available since Redis 8.2.\n\nIf no option is specified, `KEEPREF` is used by default. Unlike the `XDELEX` and `XACKDEL` commands where one of these options is required, here they are optional to maintain backward compatibility:\n\n- `KEEPREF` (default): When trimming, removes entries from the stream according to the specified strategy (`MAXLEN` or `MINID`), regardless of whether they are referenced by any consumer groups, but preserves existing references to these entries in all consumer groups' PEL (Pending Entries List).\n- `DELREF`: When trimming, removes entries from the stream according to the specified strategy and also removes all references to these entries from all consumer groups' PEL.\n- `ACKED`: When trimming, only removes entries that were read and acknowledged by all consumer groups. Note that if the number of referenced entries is larger than `MAXLEN`, trimming will still stop at the limit.\n</details>\n\n<details open>\n<summary><code>MAXLEN | MINID [= | ~] threshold [LIMIT count]></code></summary>\n\nTrims the stream to maintain a specific size or remove old entries:\n\n<details open>\n<summary><code>MAXLEN | MINID</code></summary>\n\nThe trimming strategy:\n- `MAXLEN`: Evicts entries as long as the stream's length exceeds the specified threshold\n- `MINID`: Evicts entries with IDs lower than the specified threshold (available since Redis 6.2.0)\n</details>\n\n<details open>\n<summary><code>= | ~</code></summary>\n\nThe trimming operator:\n- `=`: Exact trimming (default) - trims to the exact threshold\n- `~`: Approximate trimming - more efficient, may leave slightly more entries than the threshold\n</details>\n\n<details open>\n<summary><code>threshold</code></summary>\n\nThe trimming threshold:\n- For `MAXLEN`: `threshold` is a non-negative integer specifying the maximum number of entries that may remain in the stream after trimming. Redis enforces this by removing the oldest entries - that is, the entries with the lowest stream IDs - so that only the newest entries are kept.\n- For `MINID`: `threshold` is a stream ID. All entries whose IDs are less than `threshold` are trimmed. All entries with IDs greater than or equal to `threshold` are kept.\n</details>\n\n<details open>\n<summary><code>LIMIT count</code></summary>\n\nLimits the number of entries to examine during trimming. Available since Redis 6.2.0. When not specified, Redis uses a default value of 100 * the number of entries in a macro node. Specifying 0 disables the limiting mechanism entirely.\n</details>\n\n</details>\n  \nEach entry consists of a list of field-value pairs.\nRedis stores the field-value pairs in the same order you provide them.\nCommands that read the stream, such as [`XRANGE`](https://redis.io/docs/latest/commands/xrange) or [`XREAD`](https://redis.io/docs/latest/commands/xread), return the fields and values in exactly the same order you added them with `XADD`.\n\n> [!NOTE]\n> `XADD` is the only Redis command that can add data to a stream. However,\n> other commands, such as [`XDEL`](https://redis.io/docs/latest/commands/xdel) and [`XTRIM`](https://redis.io/docs/latest/commands/xtrim), can\n> remove data from a stream."},{"id":"examples","title":"Examples","role":"example","text":"Basic XADD: Add entries to a stream with auto-generated IDs, check stream the stream size, and read entries\n\n**Difficulty:** Beginner\n\n**Commands:** XADD, XLEN, XRANGE\n\n**Complexity:**\n- XADD: O(1)\n- XLEN: O(1)\n- XRANGE: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"idempotent-message-processing-examples","title":"Idempotent message processing examples","role":"content","text":"Idempotent XADD (Redis 8.6+): Use IDMP and IDMPAUTO for at-most-once message delivery, preventing duplicate entries\n\n**Difficulty:** Intermediate\n\n**Commands:** XADD, XCFGSET\n\n**Complexity:**\n- XADD: O(1)\n- XCFGSET: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"details","title":"Details","role":"content","text":""},{"id":"specifying-a-stream-id-as-an-argument","title":"Specifying a Stream ID as an argument","role":"content","text":"A stream entry ID identifies a specific entry inside a stream.\n\n`XADD` auto-generates a unique ID for you if you specify the `*` character (asterisk) as the ID argument. However, you can also specify a well-formed ID to add the new entry with that exact ID, though this is useful only in rare cases.\n\nSpecify IDs using two numbers separated by a `-` character:\n\n    1526919030474-55\n\nBoth numbers are 64-bit integers. When Redis auto-generates an ID, the\nfirst part is the Unix time in milliseconds of the Redis instance generating\nthe ID. The second part is a sequence number used to\ndistinguish IDs generated in the same millisecond.\n\nYou can also specify an incomplete ID that consists only of the milliseconds part, which Redis interprets as a zero value for the sequence part.\nTo have only the sequence part automatically generated, specify the milliseconds part followed by the `-` separator and the `*` character:\n\n[code example]\n\nRedis guarantees that IDs are always incremental: the ID of any entry you insert will be greater than any previous ID, so entries are totally ordered inside a stream. To guarantee this property, if the current top ID in the stream has a time greater than the current local time of the instance, Redis uses the top entry time instead and increments the sequence part of the ID. This may happen when, for instance, the local clock jumps backward, or after a failover when the new master has a different absolute time.\n\nWhen you specify an explicit ID to `XADD`, the minimum valid ID is `0-1`, and you *must* specify an ID that is greater than any other ID currently inside the stream, otherwise the command fails and returns an error. Specifying explicit IDs is usually useful only if you have another system generating unique IDs (for instance an SQL table) and you want the Redis stream IDs to match those from your other system."},{"id":"capped-streams","title":"Capped streams","role":"content","text":"`XADD` incorporates the same semantics as the [`XTRIM`](https://redis.io/docs/latest/commands/xtrim) command - refer to its documentation page for more information.\nThis allows you to add new entries and keep the stream's size in check with a single call to `XADD`, effectively capping the stream with an arbitrary threshold.\nAlthough exact trimming is possible and is the default, due to the internal representation of streams, it is more efficient to add an entry and trim the stream with `XADD` using **almost exact** trimming (the `~` argument).\n\nFor example, calling `XADD` in the following form:\n\n    XADD mystream MAXLEN ~ 1000 * ... entry fields here ...\n\nThis adds a new entry but also evicts old entries so that the stream contains only 1000 entries, or at most a few tens more."},{"id":"additional-information-about-streams","title":"Additional information about streams","role":"content","text":"For more information about Redis streams, see the\n[introduction to Redis Streams document](https://redis.io/docs/latest/develop/data-types/streams)."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): The ID of the added entry. The ID is the one automatically generated if an asterisk (`*`) is passed as the _id_ argument, otherwise the command just returns the same ID specified by the user during insertion. When using IDMP and a duplicate is detected, returns the ID of the original entry.\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): if the NOMKSTREAM option is given and the key doesn't exist.\n\n**RESP3:**\n\nOne of the following:\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): The ID of the added entry. The ID is the one automatically generated if an asterisk (`*`) is passed as the _id_ argument, otherwise the command just returns the same ID specified by the user during insertion. When using IDMP and a duplicate is detected, returns the ID of the original entry.\n* [Null reply](../../develop/reference/protocol-spec#nulls): if the NOMKSTREAM option is given and the key doesn't exist."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> XADD mystream * name Sara surname OConnor\n\"4378417975-0\"\n> XADD mystream * field1 value1 field2 value2 field3 value3\n\"4378417976-0\"\n> XLEN mystream\n(integer) 2\n> XRANGE mystream - +\n1) 1) \"1774378417975-0\"\n   2) 1) \"name\"\n      2) \"Sara\"\n      3) \"surname\"\n      4) \"OConnor\"\n2) 1) \"1774378417976-0\"\n   2) 1) \"field1\"\n      2) \"value1\"\n      3) \"field2\"\n      4) \"value2\"\n      5) \"field3\"\n      6) \"value3\"","section_id":"examples"},{"id":"examples-ex1","language":"csharp","code":"var res1 = db.StreamAdd(\"mystream\", new NameValueEntry[] {\n            new NameValueEntry(\"name\", \"Sara\"),\n            new NameValueEntry(\"surname\", \"OConnor\")\n        });\n        Console.WriteLine(res1);  // >>> 1726055713866-0\n\n        var res2 = db.StreamAdd(\"mystream\", new NameValueEntry[] {\n            new NameValueEntry(\"field1\", \"value1\"),\n            new NameValueEntry(\"field2\", \"value2\"),\n            new NameValueEntry(\"field3\", \"value3\")\n        });\n        Console.WriteLine(res2);  // >>> 1726055713866-1\n\n        var res3 = db.StreamLength(\"mystream\");\n        Console.WriteLine(res3);  // >>> 2\n\n        var res4 = db.StreamRange(\"mystream\", \"-\", \"+\");\n        foreach (var entry in res4)\n        {\n            Console.WriteLine($\"{entry.Id} -> {string.Join(\", \", entry.Values.Select(v => $\"{v.Name}={v.Value}\"))}\");\n        }\n        // >>> 1726055713866-0 -> name=Sara, surname=OConnor\n        // >>> 1726055713866-1 -> field1=value1, field2=value2, field3=value3","section_id":"examples"},{"id":"examples-ex2","language":"go","code":"res1, err := rdb.XAdd(ctx, &redis.XAddArgs{\n\t\tStream: \"mystream\",\n\t\tValues: map[string]interface{}{\n\t\t\t\"name\":    \"Sara\",\n\t\t\t\"surname\": \"OConnor\",\n\t\t},\n\t}).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1 != \"\") // >>> true\n\n\tres2, err := rdb.XAdd(ctx, &redis.XAddArgs{\n\t\tStream: \"mystream\",\n\t\tValues: map[string]interface{}{\n\t\t\t\"field1\": \"value1\",\n\t\t\t\"field2\": \"value2\",\n\t\t\t\"field3\": \"value3\",\n\t\t},\n\t}).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2 != \"\") // >>> true\n\n\tres3, err := rdb.XLen(ctx, \"mystream\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3) // >>> 2\n\n\tres4, err := rdb.XRange(ctx, \"mystream\", \"-\", \"+\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(len(res4)) // >>> 2","section_id":"examples"},{"id":"examples-ex3","language":"java","code":"Map<String, String> entry1 = new HashMap<>();\n            entry1.put(\"name\", \"Sara\");\n            entry1.put(\"surname\", \"OConnor\");\n\n            CompletableFuture<Void> xadd1Ops = asyncCommands.xadd(\"mystream\", entry1)\n                .thenCompose(res1 -> {\n                    System.out.println(res1); // >>> 1726055713866-0\n                    Map<String, String> entry2 = new HashMap<>();\n                    entry2.put(\"field1\", \"value1\");\n                    entry2.put(\"field2\", \"value2\");\n                    entry2.put(\"field3\", \"value3\");\n                    return asyncCommands.xadd(\"mystream\", entry2);\n                })\n                .thenCompose(res2 -> {\n                    System.out.println(res2); // >>> 1726055713866-1\n                    return asyncCommands.xlen(\"mystream\");\n                })\n                .thenCompose(res3 -> {\n                    System.out.println(res3); // >>> 2\n                    return asyncCommands.xrange(\"mystream\", Range.unbounded());\n                })\n                .thenAccept(res4 -> {\n                    for (var entry : res4) {\n                        System.out.println(entry.getId() + \" -> \" + entry.getBody());\n                    }\n                    // >>> 1726055713866-0 -> {name=Sara, surname=OConnor}\n                    // >>> 1726055713866-1 -> {field1=value1, field2=value2, field3=value3}\n                })\n                .toCompletableFuture();\n\n            xadd1Ops.join();","section_id":"examples"},{"id":"examples-ex4","language":"java","code":"Map<String, String> entry1 = new HashMap<>();\n            entry1.put(\"name\", \"Sara\");\n            entry1.put(\"surname\", \"OConnor\");\n\n            Mono<String> addEntry1 = reactiveCommands.xadd(\"mystream\", entry1)\n                .doOnNext(res1 -> {\n                    System.out.println(res1); // >>> 1726055713866-0\n                });\n\n            addEntry1.block();\n\n            Map<String, String> entry2 = new HashMap<>();\n            entry2.put(\"field1\", \"value1\");\n            entry2.put(\"field2\", \"value2\");\n            entry2.put(\"field3\", \"value3\");\n\n            Mono<String> addEntry2 = reactiveCommands.xadd(\"mystream\", entry2)\n                .doOnNext(res2 -> {\n                    System.out.println(res2); // >>> 1726055713866-1\n                });\n\n            addEntry2.block();\n\n            Mono<Long> getLen = reactiveCommands.xlen(\"mystream\")\n                .doOnNext(res3 -> {\n                    System.out.println(res3); // >>> 2\n                });\n\n            getLen.block();\n\n            Mono<List<StreamMessage<String, String>>> getRange = reactiveCommands\n                .xrange(\"mystream\", Range.unbounded())\n                .collectList()\n                .doOnNext(res4 -> {\n                    for (var entry : res4) {\n                        System.out.println(entry.getId() + \" -> \" + entry.getBody());\n                    }\n                    // >>> 1726055713866-0 -> {name=Sara, surname=OConnor}\n                    // >>> 1726055713866-1 -> {field1=value1, field2=value2, field3=value3}\n                });\n\n            getRange.block();","section_id":"examples"},{"id":"examples-ex5","language":"java","code":"Map<String, String> entry1 = new HashMap<>();\n        entry1.put(\"name\", \"Sara\");\n        entry1.put(\"surname\", \"OConnor\");\n        StreamEntryID res1 = jedis.xadd(\"mystream\", entry1, XAddParams.xAddParams());\n        System.out.println(res1); // >>> 1726055713866-0\n\n        Map<String, String> entry2 = new HashMap<>();\n        entry2.put(\"field1\", \"value1\");\n        entry2.put(\"field2\", \"value2\");\n        entry2.put(\"field3\", \"value3\");\n        StreamEntryID res2 = jedis.xadd(\"mystream\", entry2, XAddParams.xAddParams());\n        System.out.println(res2); // >>> 1726055713866-1\n\n        long res3 = jedis.xlen(\"mystream\");\n        System.out.println(res3); // >>> 2\n\n        List<StreamEntry> res4 = jedis.xrange(\"mystream\", \"-\", \"+\");\n        for (StreamEntry entry : res4) {\n            System.out.println(entry.getID() + \" -> \" + entry.getFields());\n        }\n        // >>> 1726055713866-0 -> {name=Sara, surname=OConnor}\n        // >>> 1726055713866-1 -> {field1=value1, field2=value2, field3=value3}","section_id":"examples"},{"id":"examples-ex6","language":"javascript","code":"const res1 = await client.xAdd('mystream', '*', {\n  'name': 'Sara',\n  'surname': 'OConnor'\n});\nconsole.log(res1); // >>> 1726055713866-0\n\nconst res2 = await client.xAdd('mystream', '*', {\n  'field1': 'value1',\n  'field2': 'value2',\n  'field3': 'value3'\n});\nconsole.log(res2); // >>> 1726055713866-1\n\nconst res3 = await client.xLen('mystream');\nconsole.log(res3); // >>> 2\n\nconst res4 = await client.xRange('mystream', '-', '+');\nconsole.log(res4);\n// >>> [\n//   { id: '1726055713866-0', message: { name: 'Sara', surname: 'OConnor' } },\n//   { id: '1726055713866-1', message: { field1: 'value1', field2: 'value2', field3: 'value3' } }\n// ]","section_id":"examples"},{"id":"examples-ex7","language":"javascript","code":"const res1 = await redis.xadd('mystream', '*', 'name', 'Sara', 'surname', 'OConnor');\nconsole.log(res1); // >>> 1726055713866-0\n\nconst res2 = await redis.xadd('mystream', '*', 'field1', 'value1', 'field2', 'value2', 'field3', 'value3');\nconsole.log(res2); // >>> 1726055713866-1\n\nconst res3 = await redis.xlen('mystream');\nconsole.log(res3); // >>> 2\n\nconst res4 = await redis.xrange('mystream', '-', '+');\nconsole.log(res4);\n// >>> [\n//   ['1726055713866-0', ['name', 'Sara', 'surname', 'OConnor']],\n//   ['1726055713866-1', ['field1', 'value1', 'field2', 'value2', 'field3', 'value3']]\n// ]","section_id":"examples"},{"id":"examples-ex8","language":"javascript","code":"const res1 = await client.xAdd('mystream', '*', {\n  'name': 'Sara',\n  'surname': 'OConnor'\n});\nconsole.log(res1); // >>> 1726055713866-0\n\nconst res2 = await client.xAdd('mystream', '*', {\n  'field1': 'value1',\n  'field2': 'value2',\n  'field3': 'value3'\n});\nconsole.log(res2); // >>> 1726055713866-1\n\nconst res3 = await client.xLen('mystream');\nconsole.log(res3); // >>> 2\n\nconst res4 = await client.xRange('mystream', '-', '+');\nconsole.log(res4);\n// >>> [\n//   { id: '1726055713866-0', message: { name: 'Sara', surname: 'OConnor' } },\n//   { id: '1726055713866-1', message: { field1: 'value1', field2: 'value2', field3: 'value3' } }\n// ]","section_id":"examples"},{"id":"examples-ex9","language":"javascript","code":"const res1 = await redis.xadd('mystream', '*', 'name', 'Sara', 'surname', 'OConnor');\nconsole.log(res1); // >>> 1726055713866-0\n\nconst res2 = await redis.xadd('mystream', '*', 'field1', 'value1', 'field2', 'value2', 'field3', 'value3');\nconsole.log(res2); // >>> 1726055713866-1\n\nconst res3 = await redis.xlen('mystream');\nconsole.log(res3); // >>> 2\n\nconst res4 = await redis.xrange('mystream', '-', '+');\nconsole.log(res4);\n// >>> [\n//   ['1726055713866-0', ['name', 'Sara', 'surname', 'OConnor']],\n//   ['1726055713866-1', ['field1', 'value1', 'field2', 'value2', 'field3', 'value3']]\n// ]","section_id":"examples"},{"id":"examples-ex10","language":"php","code":"$res1 = $redis->xadd('mystream', ['name' => 'Sara', 'surname' => 'OConnor']);\n        echo $res1 . PHP_EOL; // >>> 1726055713866-0\n\n        $res2 = $redis->xadd('mystream', ['field1' => 'value1', 'field2' => 'value2', 'field3' => 'value3']);\n        echo $res2 . PHP_EOL; // >>> 1726055713866-1\n\n        $res3 = $redis->xlen('mystream');\n        echo $res3 . PHP_EOL; // >>> 2\n\n        $res4 = $redis->xrange('mystream', '-', '+');\n        foreach ($res4 as $id => $fields) {\n            echo $id . ' -> ' . json_encode($fields) . PHP_EOL;\n        }\n        // >>> 1726055713866-0 -> {\"name\":\"Sara\",\"surname\":\"OConnor\"}\n        // >>> 1726055713866-1 -> {\"field1\":\"value1\",\"field2\":\"value2\",\"field3\":\"value3\"}","section_id":"examples"},{"id":"examples-ex11","language":"python","code":"res1 = r.xadd(\"mystream\", {\"name\": \"Sara\", \"surname\": \"OConnor\"})\nprint(res1)  # >>> 1726055713866-0\n\nres2 = r.xadd(\"mystream\", {\"field1\": \"value1\", \"field2\": \"value2\", \"field3\": \"value3\"})\nprint(res2)  # >>> 1726055713866-1\n\nres3 = r.xlen(\"mystream\")\nprint(res3)  # >>> 2\n\nres4 = r.xrange(\"mystream\", \"-\", \"+\")\nprint(res4)\n# >>> [\n#   ('1726055713866-0', {'name': 'Sara', 'surname': 'OConnor'}),\n#   ('1726055713866-1', {'field1': 'value1', 'field2': 'value2', 'field3': 'value3'})\n# ]","section_id":"examples"},{"id":"examples-ex12","language":"ruby","code":"res1 = r.xadd('mystream', { 'name' => 'Sara', 'surname' => 'OConnor' })\nputs res1 # >>> 1726055713866-0\n\nres2 = r.xadd('mystream', { 'field1' => 'value1', 'field2' => 'value2', 'field3' => 'value3' })\nputs res2 # >>> 1726055713866-1\n\nres3 = r.xlen('mystream')\nputs res3 # >>> 2\n\nres4 = r.xrange('mystream', '-', '+')\nputs res4.inspect\n# >>> [\n#   [\"1726055713866-0\", {\"name\"=>\"Sara\", \"surname\"=>\"OConnor\"}],\n#   [\"1726055713866-1\", {\"field1\"=>\"value1\", \"field2\"=>\"value2\", \"field3\"=>\"value3\"}]\n# ]","section_id":"examples"},{"id":"examples-ex13","language":"rust","code":"let client = redis::Client::open(\"redis://127.0.0.1/\").unwrap();\n        let mut con = client.get_multiplexed_async_connection().await.unwrap();\n\n\n        let res1: String = redis::cmd(\"XADD\")\n            .arg(\"mystream\")\n            .arg(\"*\")\n            .arg(\"name\")\n            .arg(\"Sara\")\n            .arg(\"surname\")\n            .arg(\"OConnor\")\n            .query_async(&mut con)\n            .await\n            .unwrap();\n        println!(\"XADD result: {}\", res1); // >>> 1726055713866-0\n\n        let res2: String = redis::cmd(\"XADD\")\n            .arg(\"mystream\")\n            .arg(\"*\")\n            .arg(\"field1\")\n            .arg(\"value1\")\n            .arg(\"field2\")\n            .arg(\"value2\")\n            .arg(\"field3\")\n            .arg(\"value3\")\n            .query_async(&mut con)\n            .await\n            .unwrap();\n        println!(\"XADD result: {}\", res2); // >>> 1726055713866-1\n\n        let res3: i64 = con.xlen(\"mystream\").await.unwrap();\n        println!(\"XLEN result: {}\", res3); // >>> 2\n\n        let res4: redis::streams::StreamRangeReply = con.xrange_all(\"mystream\").await.unwrap();\n        for entry in &res4.ids {\n            println!(\"{} -> {:?}\", entry.id, entry.map);\n        }\n        // >>> 1726055713866-0 -> {\"name\": \"Sara\", \"surname\": \"OConnor\"}\n        // >>> 1726055713866-1 -> {\"field1\": \"value1\", \"field2\": \"value2\", \"field3\": \"value3\"}","section_id":"examples"},{"id":"examples-ex14","language":"rust","code":"let client = redis::Client::open(\"redis://127.0.0.1/\").unwrap();\n        let mut con = client.get_connection().unwrap();\n\n\n        let res1: String = redis::cmd(\"XADD\")\n            .arg(\"mystream\")\n            .arg(\"*\")\n            .arg(\"name\")\n            .arg(\"Sara\")\n            .arg(\"surname\")\n            .arg(\"OConnor\")\n            .query(&mut con)\n            .unwrap();\n        println!(\"XADD result: {}\", res1); // >>> 1726055713866-0\n\n        let res2: String = redis::cmd(\"XADD\")\n            .arg(\"mystream\")\n            .arg(\"*\")\n            .arg(\"field1\")\n            .arg(\"value1\")\n            .arg(\"field2\")\n            .arg(\"value2\")\n            .arg(\"field3\")\n            .arg(\"value3\")\n            .query(&mut con)\n            .unwrap();\n        println!(\"XADD result: {}\", res2); // >>> 1726055713866-1\n\n        let res3: i64 = con.xlen(\"mystream\").unwrap();\n        println!(\"XLEN result: {}\", res3); // >>> 2\n\n        let res4: redis::streams::StreamRangeReply = con.xrange_all(\"mystream\").unwrap();\n        for entry in &res4.ids {\n            println!(\"{} -> {:?}\", entry.id, entry.map);\n        }\n        // >>> 1726055713866-0 -> {\"name\": \"Sara\", \"surname\": \"OConnor\"}\n        // >>> 1726055713866-1 -> {\"field1\": \"value1\", \"field2\": \"value2\", \"field3\": \"value3\"}","section_id":"examples"},{"id":"idempotent-message-processing-examples-ex0","language":"plaintext","code":"> XADD mystream IDMP producer1 msg1 * field value\n\"1774378417976-0\"\n> XADD mystream IDMP producer1 msg1 * field different_value\n\"1774378417976-0\"\n> XADD mystream IDMPAUTO producer2 * field value\n\"1774378417977-0\"\n> XADD mystream IDMPAUTO producer2 * field value\n\"1774378417977-0\"\n> XCFGSET mystream IDMP-DURATION 300 IDMP-MAXSIZE 1000\nOK","section_id":"idempotent-message-processing-examples"},{"id":"idempotent-message-processing-examples-ex1","language":"csharp","code":"// Note: IDMP is a Redis 8.6 feature - using Execute for raw command access\n        var res5 = db.Execute(\"XADD\", \"mystream\", \"IDMP\", \"producer1\", \"msg1\", \"*\", \"field\", \"value\");\n        Console.WriteLine(res5);  // >>> 1726055713867-0\n\n        // Attempting to add the same message again with IDMP returns the original entry ID\n        var res6 = db.Execute(\"XADD\", \"mystream\", \"IDMP\", \"producer1\", \"msg1\", \"*\", \"field\", \"different_value\");\n        Console.WriteLine(res6);  // >>> 1726055713867-0 (same ID as res5, message was deduplicated)\n\n        var res7 = db.Execute(\"XADD\", \"mystream\", \"IDMPAUTO\", \"producer2\", \"*\", \"field\", \"value\");\n        Console.WriteLine(res7);  // >>> 1726055713867-1\n\n        // Auto-generated idempotent ID prevents duplicates for same producer+content\n        var res8 = db.Execute(\"XADD\", \"mystream\", \"IDMPAUTO\", \"producer2\", \"*\", \"field\", \"value\");\n        Console.WriteLine(res8);  // >>> 1726055713867-1 (same ID as res7, duplicate detected)\n\n        // Configure idempotent message processing settings\n        var res9 = db.Execute(\"XCFGSET\", \"mystream\", \"IDMP-DURATION\", \"300\", \"IDMP-MAXSIZE\", \"1000\");\n        Console.WriteLine(res9);  // >>> OK","section_id":"idempotent-message-processing-examples"},{"id":"idempotent-message-processing-examples-ex2","language":"go","code":"res5, err := rdb.XAdd(ctx, &redis.XAddArgs{\n\t\tStream:       \"mystream\",\n\t\tValues:       map[string]interface{}{\"field\": \"value\"},\n\t\tProducerID:   \"producer1\",\n\t\tIdempotentID: \"msg1\",\n\t}).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res5 != \"\") // >>> true\n\n\t// Attempting to add the same message again with IDMP returns the original entry ID\n\tres6, err := rdb.XAdd(ctx, &redis.XAddArgs{\n\t\tStream:       \"mystream\",\n\t\tValues:       map[string]interface{}{\"field\": \"different_value\"},\n\t\tProducerID:   \"producer1\",\n\t\tIdempotentID: \"msg1\",\n\t}).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res5 == res6) // >>> true (same ID, message was deduplicated)\n\n\tres7, err := rdb.XAdd(ctx, &redis.XAddArgs{\n\t\tStream:         \"mystream\",\n\t\tValues:         map[string]interface{}{\"field\": \"value\"},\n\t\tProducerID:     \"producer2\",\n\t\tIdempotentAuto: true,\n\t}).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res7 != \"\") // >>> true\n\n\t// Auto-generated idempotent ID prevents duplicates for same producer+content\n\tres8, err := rdb.XAdd(ctx, &redis.XAddArgs{\n\t\tStream:         \"mystream\",\n\t\tValues:         map[string]interface{}{\"field\": \"value\"},\n\t\tProducerID:     \"producer2\",\n\t\tIdempotentAuto: true,\n\t}).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res7 == res8) // >>> true (same ID, duplicate detected)\n\n\t// Configure idempotent message processing settings\n\tres9, err := rdb.XCfgSet(ctx, &redis.XCfgSetArgs{\n\t\tStream:   \"mystream\",\n\t\tDuration: 300,  // 300 seconds\n\t\tMaxSize:  1000, // 1000 idempotent IDs\n\t}).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res9) // >>> OK","section_id":"idempotent-message-processing-examples"},{"id":"idempotent-message-processing-examples-ex3","language":"java","code":"Map<String, String> idmpEntry1 = new HashMap<>();\n            idmpEntry1.put(\"field\", \"value\");\n\n            CompletableFuture<Void> xadd2Ops = asyncCommands.xadd(\n                    \"mystream\",\n                    XAddArgs.Builder.idmp(\"producer1\", \"msg1\"),\n                    idmpEntry1)\n                .thenCompose(res5 -> {\n                    System.out.println(res5); // >>> 1726055713867-0\n                    // Attempting to add same message again with IDMP returns original entry ID\n                    Map<String, String> idmpEntry2 = new HashMap<>();\n                    idmpEntry2.put(\"field\", \"different_value\");\n                    return asyncCommands.xadd(\"mystream\",\n                        XAddArgs.Builder.idmp(\"producer1\", \"msg1\"), idmpEntry2);\n                })\n                .thenCompose(res6 -> {\n                    System.out.println(res6); // >>> 1726055713867-0 (deduplicated)\n                    Map<String, String> idmpAutoEntry1 = new HashMap<>();\n                    idmpAutoEntry1.put(\"field\", \"value\");\n                    return asyncCommands.xadd(\"mystream\",\n                        XAddArgs.Builder.idmpAuto(\"producer2\"), idmpAutoEntry1);\n                })\n                .thenCompose(res7 -> {\n                    System.out.println(res7); // >>> 1726055713867-1\n                    Map<String, String> idmpAutoEntry2 = new HashMap<>();\n                    idmpAutoEntry2.put(\"field\", \"value\");\n                    return asyncCommands.xadd(\"mystream\",\n                        XAddArgs.Builder.idmpAuto(\"producer2\"), idmpAutoEntry2);\n                })\n                .thenAccept(res8 -> {\n                    System.out.println(res8); // >>> 1726055713867-1 (duplicate detected)\n                })\n                .toCompletableFuture();\n\n            xadd2Ops.join();","section_id":"idempotent-message-processing-examples"},{"id":"idempotent-message-processing-examples-ex4","language":"java","code":"Map<String, String> idmpEntry1 = new HashMap<>();\n            idmpEntry1.put(\"field\", \"value\");\n\n            Mono<String> addIdmp1 = reactiveCommands.xadd(\n                    \"mystream\",\n                    XAddArgs.Builder.idmp(\"producer1\", \"msg1\"),\n                    idmpEntry1)\n                .doOnNext(res5 -> {\n                    System.out.println(res5); // >>> 1726055713867-0\n                });\n\n            addIdmp1.block();\n\n            // Attempting to add the same message again with IDMP returns the original entry ID\n            Map<String, String> idmpEntry2 = new HashMap<>();\n            idmpEntry2.put(\"field\", \"different_value\");\n\n            Mono<String> addIdmp2 = reactiveCommands.xadd(\n                    \"mystream\",\n                    XAddArgs.Builder.idmp(\"producer1\", \"msg1\"),\n                    idmpEntry2)\n                .doOnNext(res6 -> {\n                    System.out.println(res6); // >>> 1726055713867-0 (deduplicated)\n                });\n\n            addIdmp2.block();\n\n            Map<String, String> idmpAutoEntry1 = new HashMap<>();\n            idmpAutoEntry1.put(\"field\", \"value\");\n\n            Mono<String> addIdmpAuto1 = reactiveCommands.xadd(\n                    \"mystream\",\n                    XAddArgs.Builder.idmpAuto(\"producer2\"),\n                    idmpAutoEntry1)\n                .doOnNext(res7 -> {\n                    System.out.println(res7); // >>> 1726055713867-1\n                });\n\n            addIdmpAuto1.block();\n\n            // Auto-generated idempotent ID prevents duplicates for same producer+content\n            Map<String, String> idmpAutoEntry2 = new HashMap<>();\n            idmpAutoEntry2.put(\"field\", \"value\");\n\n            Mono<String> addIdmpAuto2 = reactiveCommands.xadd(\n                    \"mystream\",\n                    XAddArgs.Builder.idmpAuto(\"producer2\"),\n                    idmpAutoEntry2)\n                .doOnNext(res8 -> {\n                    System.out.println(res8); // >>> 1726055713867-1 (duplicate detected)\n                });\n\n            addIdmpAuto2.block();","section_id":"idempotent-message-processing-examples"},{"id":"idempotent-message-processing-examples-ex5","language":"java","code":"Map<String, String> idmpEntry1 = new HashMap<>();\n        idmpEntry1.put(\"field\", \"value\");\n        StreamEntryID res5 = jedis.xadd(\"mystream\", idmpEntry1, \n            XAddParams.xAddParams().idmp(\"producer1\", \"msg1\"));\n        System.out.println(res5); // >>> 1726055713867-0\n\n        // Attempting to add the same message again with IDMP returns the original entry ID\n        Map<String, String> idmpEntry2 = new HashMap<>();\n        idmpEntry2.put(\"field\", \"different_value\");\n        StreamEntryID res6 = jedis.xadd(\"mystream\", idmpEntry2,\n            XAddParams.xAddParams().idmp(\"producer1\", \"msg1\"));\n        System.out.println(res6); // >>> 1726055713867-0 (same ID as res5, message was deduplicated)\n\n        Map<String, String> idmpAutoEntry1 = new HashMap<>();\n        idmpAutoEntry1.put(\"field\", \"value\");\n        StreamEntryID res7 = jedis.xadd(\"mystream\", idmpAutoEntry1,\n            XAddParams.xAddParams().idmpAuto(\"producer2\"));\n        System.out.println(res7); // >>> 1726055713867-1\n\n        // Auto-generated idempotent ID prevents duplicates for same producer+content\n        Map<String, String> idmpAutoEntry2 = new HashMap<>();\n        idmpAutoEntry2.put(\"field\", \"value\");\n        StreamEntryID res8 = jedis.xadd(\"mystream\", idmpAutoEntry2,\n            XAddParams.xAddParams().idmpAuto(\"producer2\"));\n        System.out.println(res8); // >>> 1726055713867-1 (same ID as res7, duplicate detected)\n\n        // Configure idempotent message processing settings\n        String res9 = jedis.xcfgset(\"mystream\",\n            XCfgSetParams.xCfgSetParams().idmpDuration(300).idmpMaxsize(1000));\n        System.out.println(res9); // >>> OK","section_id":"idempotent-message-processing-examples"},{"id":"idempotent-message-processing-examples-ex6","language":"javascript","code":"const res5 = await client.xAdd('mystream', '*', { 'field': 'value' }, {\n  IDMP: { pid: 'producer1', iid: 'msg1' }\n});\nconsole.log(res5); // >>> 1726055713867-0\n\n// Attempting to add the same message again with IDMP returns the original entry ID\nconst res6 = await client.xAdd('mystream', '*', { 'field': 'different_value' }, {\n  IDMP: { pid: 'producer1', iid: 'msg1' }\n});\nconsole.log(res6); // >>> 1726055713867-0 (same ID as res5, message was deduplicated)\n\nconst res7 = await client.xAdd('mystream', '*', { 'field': 'value' }, {\n  IDMPAUTO: { pid: 'producer2' }\n});\nconsole.log(res7); // >>> 1726055713867-1\n\n// Auto-generated idempotent ID prevents duplicates for same producer+content\nconst res8 = await client.xAdd('mystream', '*', { 'field': 'value' }, {\n  IDMPAUTO: { pid: 'producer2' }\n});\nconsole.log(res8); // >>> 1726055713867-1 (same ID as res7, duplicate detected)\n\n// Configure idempotent message processing settings\nconst res9 = await client.xCfgSet('mystream', {\n  IDMP_DURATION: 300,\n  IDMP_MAXSIZE: 1000\n});\nconsole.log(res9); // >>> OK","section_id":"idempotent-message-processing-examples"},{"id":"idempotent-message-processing-examples-ex7","language":"javascript","code":"const res5 = await redis.call('XADD', 'mystream', 'IDMP', 'producer1', 'msg1', '*', 'field', 'value');\nconsole.log(res5); // >>> 1726055713867-0\n\n// Attempting to add the same message again with IDMP returns the original entry ID\nconst res6 = await redis.call('XADD', 'mystream', 'IDMP', 'producer1', 'msg1', '*', 'field', 'different_value');\nconsole.log(res6); // >>> 1726055713867-0 (same ID as res5, message was deduplicated)\n\nconst res7 = await redis.call('XADD', 'mystream', 'IDMPAUTO', 'producer2', '*', 'field', 'value');\nconsole.log(res7); // >>> 1726055713867-1\n\n// Auto-generated idempotent ID prevents duplicates for same producer+content\nconst res8 = await redis.call('XADD', 'mystream', 'IDMPAUTO', 'producer2', '*', 'field', 'value');\nconsole.log(res8); // >>> 1726055713867-1 (same ID as res7, duplicate detected)\n\n// Configure idempotent message processing settings\nconst res9 = await redis.call('XCFGSET', 'mystream', 'IDMP-DURATION', 300, 'IDMP-MAXSIZE', 1000);\nconsole.log(res9); // >>> OK","section_id":"idempotent-message-processing-examples"},{"id":"idempotent-message-processing-examples-ex8","language":"javascript","code":"const res5 = await client.xAdd('mystream', '*', { 'field': 'value' }, {\n  IDMP: { pid: 'producer1', iid: 'msg1' }\n});\nconsole.log(res5); // >>> 1726055713867-0\n\n// Attempting to add the same message again with IDMP returns the original entry ID\nconst res6 = await client.xAdd('mystream', '*', { 'field': 'different_value' }, {\n  IDMP: { pid: 'producer1', iid: 'msg1' }\n});\nconsole.log(res6); // >>> 1726055713867-0 (same ID as res5, message was deduplicated)\n\nconst res7 = await client.xAdd('mystream', '*', { 'field': 'value' }, {\n  IDMPAUTO: { pid: 'producer2' }\n});\nconsole.log(res7); // >>> 1726055713867-1\n\n// Auto-generated idempotent ID prevents duplicates for same producer+content\nconst res8 = await client.xAdd('mystream', '*', { 'field': 'value' }, {\n  IDMPAUTO: { pid: 'producer2' }\n});\nconsole.log(res8); // >>> 1726055713867-1 (same ID as res7, duplicate detected)\n\n// Configure idempotent message processing settings\nconst res9 = await client.xCfgSet('mystream', {\n  IDMP_DURATION: 300,\n  IDMP_MAXSIZE: 1000\n});\nconsole.log(res9); // >>> OK","section_id":"idempotent-message-processing-examples"},{"id":"idempotent-message-processing-examples-ex9","language":"javascript","code":"const res5 = await redis.call('XADD', 'mystream', 'IDMP', 'producer1', 'msg1', '*', 'field', 'value');\nconsole.log(res5); // >>> 1726055713867-0\n\n// Attempting to add the same message again with IDMP returns the original entry ID\nconst res6 = await redis.call('XADD', 'mystream', 'IDMP', 'producer1', 'msg1', '*', 'field', 'different_value');\nconsole.log(res6); // >>> 1726055713867-0 (same ID as res5, message was deduplicated)\n\nconst res7 = await redis.call('XADD', 'mystream', 'IDMPAUTO', 'producer2', '*', 'field', 'value');\nconsole.log(res7); // >>> 1726055713867-1\n\n// Auto-generated idempotent ID prevents duplicates for same producer+content\nconst res8 = await redis.call('XADD', 'mystream', 'IDMPAUTO', 'producer2', '*', 'field', 'value');\nconsole.log(res8); // >>> 1726055713867-1 (same ID as res7, duplicate detected)\n\n// Configure idempotent message processing settings\nconst res9 = await redis.call('XCFGSET', 'mystream', 'IDMP-DURATION', 300, 'IDMP-MAXSIZE', 1000);\nconsole.log(res9); // >>> OK","section_id":"idempotent-message-processing-examples"},{"id":"idempotent-message-processing-examples-ex10","language":"php","code":"$res5 = $redis->xadd('mystream', ['field' => 'value'], '*', ['idmp' => ['producer1', 'msg1']]);\n        echo $res5 . PHP_EOL; // >>> 1726055713867-0\n\n        // Attempting to add the same message again with IDMP returns the original entry ID\n        $res6 = $redis->xadd('mystream', ['field' => 'different_value'], '*', ['idmp' => ['producer1', 'msg1']]);\n        echo $res6 . PHP_EOL; // >>> 1726055713867-0 (same ID as res5, message was deduplicated)\n\n        $res7 = $redis->xadd('mystream', ['field' => 'value'], '*', ['idmpauto' => 'producer2']);\n        echo $res7 . PHP_EOL; // >>> 1726055713867-1\n\n        // Auto-generated idempotent ID prevents duplicates for same producer+content\n        $res8 = $redis->xadd('mystream', ['field' => 'value'], '*', ['idmpauto' => 'producer2']);\n        echo $res8 . PHP_EOL; // >>> 1726055713867-1 (same ID as res7, duplicate detected)\n\n        // Configure idempotent message processing settings\n        $res9 = $redis->xcfgset('mystream', 300, 1000);\n        echo $res9 . PHP_EOL; // >>> OK","section_id":"idempotent-message-processing-examples"},{"id":"idempotent-message-processing-examples-ex11","language":"python","code":"res5 = r.xadd(\"mystream\", {\"field\": \"value\"}, idmp=(\"producer1\", b\"msg1\"))\nprint(res5)  # >>> 1726055713867-0\n\n# Attempting to add the same message again with IDMP returns the original entry ID\nres6 = r.xadd(\"mystream\", {\"field\": \"different_value\"}, idmp=(\"producer1\", b\"msg1\"))\nprint(res6)  # >>> 1726055713867-0 (same ID as res5, message was deduplicated)\n\nres7 = r.xadd(\"mystream\", {\"field\": \"value\"}, idmpauto=\"producer2\")\nprint(res7)  # >>> 1726055713867-1\n\n# Auto-generated idempotent ID prevents duplicates for same producer+content\nres8 = r.xadd(\"mystream\", {\"field\": \"value\"}, idmpauto=\"producer2\")\nprint(res8)  # >>> 1726055713867-1 (same ID as res7, duplicate detected)\n\n# Configure idempotent message processing settings\nres9 = r.xcfgset(\"mystream\", idmp_duration=300, idmp_maxsize=1000)\nprint(res9)  # >>> True","section_id":"idempotent-message-processing-examples"},{"id":"idempotent-message-processing-examples-ex12","language":"ruby","code":"res5 = r.call('XADD', 'mystream', 'IDMP', 'producer1', 'msg1', '*', 'field', 'value')\nputs res5 # >>> 1726055713867-0\n\n# Attempting to add the same message again with IDMP returns the original entry ID\nres6 = r.call('XADD', 'mystream', 'IDMP', 'producer1', 'msg1', '*', 'field', 'different_value')\nputs res6 # >>> 1726055713867-0 (same ID as res5, message was deduplicated)\n\nres7 = r.call('XADD', 'mystream', 'IDMPAUTO', 'producer2', '*', 'field', 'value')\nputs res7 # >>> 1726055713867-1\n\n# Auto-generated idempotent ID prevents duplicates for same producer+content\nres8 = r.call('XADD', 'mystream', 'IDMPAUTO', 'producer2', '*', 'field', 'value')\nputs res8 # >>> 1726055713867-1 (same ID as res7, duplicate detected)\n\n# Configure idempotent message processing settings\nres9 = r.call('XCFGSET', 'mystream', 'IDMP-DURATION', 300, 'IDMP-MAXSIZE', 1000)\nputs res9 # >>> OK","section_id":"idempotent-message-processing-examples"},{"id":"idempotent-message-processing-examples-ex13","language":"rust","code":"// Note: IDMP is a Redis 8.6 feature - using raw commands\n        let res5: String = redis::cmd(\"XADD\")\n            .arg(\"idmpstream\")\n            .arg(\"IDMP\")\n            .arg(\"producer1\")\n            .arg(\"msg1\")\n            .arg(\"*\")\n            .arg(\"field\")\n            .arg(\"value\")\n            .query_async(&mut con)\n            .await\n            .unwrap();\n        println!(\"XADD IDMP result: {}\", res5); // >>> 1726055713867-0\n\n        // Attempting to add the same message again with IDMP returns the original entry ID\n        let res6: String = redis::cmd(\"XADD\")\n            .arg(\"idmpstream\")\n            .arg(\"IDMP\")\n            .arg(\"producer1\")\n            .arg(\"msg1\")\n            .arg(\"*\")\n            .arg(\"field\")\n            .arg(\"different_value\")\n            .query_async(&mut con)\n            .await\n            .unwrap();\n        println!(\"XADD IDMP result: {}\", res6); // >>> 1726055713867-0 (deduplicated)\n\n        let res7: String = redis::cmd(\"XADD\")\n            .arg(\"idmpstream\")\n            .arg(\"IDMPAUTO\")\n            .arg(\"producer2\")\n            .arg(\"*\")\n            .arg(\"field\")\n            .arg(\"value\")\n            .query_async(&mut con)\n            .await\n            .unwrap();\n        println!(\"XADD IDMPAUTO result: {}\", res7); // >>> 1726055713867-1\n\n        // Auto-generated idempotent ID prevents duplicates for same producer+content\n        let res8: String = redis::cmd(\"XADD\")\n            .arg(\"idmpstream\")\n            .arg(\"IDMPAUTO\")\n            .arg(\"producer2\")\n            .arg(\"*\")\n            .arg(\"field\")\n            .arg(\"value\")\n            .query_async(&mut con)\n            .await\n            .unwrap();\n        println!(\"XADD IDMPAUTO result: {}\", res8); // >>> 1726055713867-1 (duplicate detected)\n\n        // Configure idempotent message processing settings\n        let res9: String = redis::cmd(\"XCFGSET\")\n            .arg(\"idmpstream\")\n            .arg(\"IDMP-DURATION\")\n            .arg(\"300\")\n            .arg(\"IDMP-MAXSIZE\")\n            .arg(\"1000\")\n            .query_async(&mut con)\n            .await\n            .unwrap();\n        println!(\"XCFGSET result: {}\", res9); // >>> OK","section_id":"idempotent-message-processing-examples"},{"id":"idempotent-message-processing-examples-ex14","language":"rust","code":"// Note: IDMP is a Redis 8.6 feature - using raw commands\n        let res5: String = redis::cmd(\"XADD\")\n            .arg(\"idmpstream\")\n            .arg(\"IDMP\")\n            .arg(\"producer1\")\n            .arg(\"msg1\")\n            .arg(\"*\")\n            .arg(\"field\")\n            .arg(\"value\")\n            .query(&mut con)\n            .unwrap();\n        println!(\"XADD IDMP result: {}\", res5); // >>> 1726055713867-0\n\n        // Attempting to add the same message again with IDMP returns the original entry ID\n        let res6: String = redis::cmd(\"XADD\")\n            .arg(\"idmpstream\")\n            .arg(\"IDMP\")\n            .arg(\"producer1\")\n            .arg(\"msg1\")\n            .arg(\"*\")\n            .arg(\"field\")\n            .arg(\"different_value\")\n            .query(&mut con)\n            .unwrap();\n        println!(\"XADD IDMP result: {}\", res6); // >>> 1726055713867-0 (deduplicated)\n\n        let res7: String = redis::cmd(\"XADD\")\n            .arg(\"idmpstream\")\n            .arg(\"IDMPAUTO\")\n            .arg(\"producer2\")\n            .arg(\"*\")\n            .arg(\"field\")\n            .arg(\"value\")\n            .query(&mut con)\n            .unwrap();\n        println!(\"XADD IDMPAUTO result: {}\", res7); // >>> 1726055713867-1\n\n        // Auto-generated idempotent ID prevents duplicates for same producer+content\n        let res8: String = redis::cmd(\"XADD\")\n            .arg(\"idmpstream\")\n            .arg(\"IDMPAUTO\")\n            .arg(\"producer2\")\n            .arg(\"*\")\n            .arg(\"field\")\n            .arg(\"value\")\n            .query(&mut con)\n            .unwrap();\n        println!(\"XADD IDMPAUTO result: {}\", res8); // >>> 1726055713867-1 (duplicate detected)\n\n        // Configure idempotent message processing settings\n        let res9: String = redis::cmd(\"XCFGSET\")\n            .arg(\"idmpstream\")\n            .arg(\"IDMP-DURATION\")\n            .arg(\"300\")\n            .arg(\"IDMP-MAXSIZE\")\n            .arg(\"1000\")\n            .query(&mut con)\n            .unwrap();\n        println!(\"XCFGSET result: {}\", res9); // >>> OK","section_id":"idempotent-message-processing-examples"},{"id":"specifying-a-stream-id-as-an-argument-ex0","language":"plaintext","code":"> XADD mystream 1526919030474-55 message \"Hello,\"\n\"1526919030474-55\"\n> XADD mystream 1526919030474-* message \" World!\"\n\"1526919030474-56\"","section_id":"specifying-a-stream-id-as-an-argument"}]}
{"schema_version":2,"id":"commands/xautoclaim","title":"XAUTOCLAIM","url":"https://redis.io/docs/latest/commands/xautoclaim/","summary":"Changes, or acquires, ownership of messages in a consumer group, as if the messages were delivered to as consumer group member.","since":"6.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:25+01:00","page_type":"content","content_hash":"810430a17963a04c70b88f810c071a558da263c22d1ee2d86f4bfeecaa644fe1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This command transfers ownership of pending stream entries that match the specified criteria. Conceptually, `XAUTOCLAIM`  is equivalent to calling [`XPENDING`](https://redis.io/docs/latest/commands/xpending) and then [`XCLAIM`](https://redis.io/docs/latest/commands/xclaim),\nbut provides a more straightforward way to deal with message delivery failures via [`SCAN`](https://redis.io/docs/latest/commands/scan)-like semantics.\n\nLike [`XCLAIM`](https://redis.io/docs/latest/commands/xclaim), the command operates on the stream entries at `key` and in the context of the provided `group`.\nIt transfers ownership to `consumer` of messages pending for more than `min-idle-time` milliseconds and having an equal or greater ID than `start`.\n\nThe optional `count` argument, which defaults to 100, is the upper limit of the number of entries that the command attempts to claim.\nInternally, the command begins scanning the consumer group's Pending Entries List (PEL) from `start` and filters out entries having an idle time less than or equal to `min-idle-time`.\nThe maximum number of pending entries that the command scans is the product of multiplying `count`'s value by 10 (hard-coded).\nIt is possible, therefore, that the number of entries claimed will be less than the specified value.\n\nThe optional `JUSTID` argument changes the reply to return just an array of IDs of messages successfully claimed, without returning the actual message.\nUsing this option means the retry counter is not incremented.\n\nThe command returns the claimed entries as an array. It also returns a stream ID intended for cursor-like use as the `start` argument for its subsequent call.\nWhen there are no remaining PEL entries, the command returns the special `0-0` ID to signal completion.\nHowever, note that you may want to continue calling `XAUTOCLAIM` even after the scan is complete with the `0-0` as `start` ID, because enough time passed, so older pending entries may now be eligible for claiming.\n\nNote that only messages that are idle longer than `min-idle-time` are claimed, and claiming a message resets its idle time.\nThis ensures that only a single consumer can successfully claim a given pending message at a specific instant of time and trivially reduces the probability of processing the same message multiple times.\n\nMessages that have been released back to the group using [`XNACK`](https://redis.io/docs/latest/commands/xnack) are immediately claimable since their delivery time is set to 0, satisfying any minimum idle time requirement.\n\nWhile iterating the PEL, if `XAUTOCLAIM` stumbles upon a message which doesn't exist in the stream anymore (either trimmed or deleted by [`XDEL`](https://redis.io/docs/latest/commands/xdel)) it does not claim it, and deletes it from the PEL in which it was found. This feature was introduced in Redis 7.0.\nThese message IDs are returned to the caller as a part of `XAUTOCLAIM`s reply.\n\nLastly, claiming a message with `XAUTOCLAIM` also increments the attempted deliveries count for that message, unless the `JUSTID` option has been specified (which only delivers the message ID, not the message itself).\nMessages that cannot be processed for some reason - for example, because consumers systematically crash when processing them - will exhibit high attempted delivery counts that can be detected by monitoring."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe stream key.\n\n</details>\n\n<details open><summary><code>group</code></summary>\n\nThe consumer group name.\n\n</details>\n\n<details open><summary><code>consumer</code></summary>\n\nThe name of the consumer that will own the claimed messages.\n\n</details>\n\n<details open><summary><code>min-idle-time</code></summary>\n\nClaim only messages that have been idle for at least this long, in milliseconds.\n\n</details>\n\n<details open><summary><code>start</code></summary>\n\nThe message ID to start scanning from. Use `0` to start at the beginning of the PEL.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>COUNT count</code></summary>\n\nThe number of messages to attempt to claim per call. Defaults to 100.\n\n</details>\n\n<details open><summary><code>JUSTID</code></summary>\n\nReturn only the IDs of the claimed messages, without their fields, and do not increment their retry counters.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]\n\nIn the previous example, you claim up to 25 pending entries that have been idle (having not been acknowledged or claimed) for at least one hour, starting from the beginning of the stream. The consumer Alice in the mygroup group takes ownership of these messages.\n\nThe returned stream ID is 0-0, which indicates that XAUTOCLAIM scanned the entire stream. The third reply element is an empty array, which means XAUTOCLAIM did not find any deleted messages."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays), specifically, an array with three elements:\n1. A stream ID to be used as the _start_ argument for the next call to XAUTOCLAIM.\n2. An [Array reply](../../develop/reference/protocol-spec#arrays) containing all the successfully claimed messages in the same format as `XRANGE`.\n3. An [Array reply](../../develop/reference/protocol-spec#arrays) containing message IDs that no longer exist in the stream, and were deleted from the PEL in which they were found.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays), specifically, an array with three elements:\n1. A stream ID to be used as the _start_ argument for the next call to XAUTOCLAIM.\n2. An [Array reply](../../develop/reference/protocol-spec#arrays) containing all the successfully claimed messages in the same format as `XRANGE`.\n3. An [Array reply](../../develop/reference/protocol-spec#arrays) containing message IDs that no longer exist in the stream, and were deleted from the PEL in which they were found."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> XAUTOCLAIM mystream mygroup Alice 3600000 0-0 COUNT 25\n1) \"0-0\"\n2) 1) 1) \"1609338752495-0\"\n      2) 1) \"field\"\n         2) \"value\"\n3) (empty array)","section_id":"examples"}]}
{"schema_version":2,"id":"commands/xcfgset","title":"XCFGSET","url":"https://redis.io/docs/latest/commands/xcfgset/","summary":"Sets the IDMP configuration parameters for a stream.","since":"8.6.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-06-30T09:57:40-07:00","page_type":"content","content_hash":"92cfb92adef0187d1723a485d6d3a35557b649648cb24ebaa434dec2f2885cbe","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Sets the IDMP (Idempotent Message Processing) configuration parameters for a stream. This command configures how long idempotent IDs are retained and the maximum number of idempotent IDs tracked per producer.\n\nFor more details, see [Idempotent message processing](../../develop/data-types/streams/idempotency/)."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the stream key. The stream must already exist.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>IDMP-DURATION idmp-duration</code></summary>\n\nSets the duration, in seconds, for which each idempotent ID (iid) is retained in the stream's IDMP map. Valid range: 1-86,400 seconds. Default: 100 seconds.\n\nWhen an idempotent ID expires, it can be reused for new messages. This provides an operational guarantee that Redis will not forget an idempotency ID before the duration elapses (unless capacity is reached).\n\n</details>\n\n<details open><summary><code>IDMP-MAXSIZE idmp-maxsize</code></summary>\n\nSets the maximum number of most recent idempotent IDs kept for each producer in the stream's IDMP map. Valid range: 1-10,000 entries. Default: 100 entries.\n\nWhen the capacity is reached, the oldest idempotent IDs for that producer are evicted regardless of remaining duration. This prevents unbounded memory growth.\n\n</details>"},{"id":"details","title":"Details","role":"content","text":""},{"id":"behavior","title":"Behavior","role":"content","text":"- Calling `XCFGSET` clears all existing producer IDMP maps for the stream.\n- At least one of `IDMP-DURATION` or `IDMP-MAXSIZE` must be specified.\n- The stream must exist before calling this command.\n- Configuration changes apply immediately to all future IDMP operations."},{"id":"error-conditions","title":"Error conditions","role":"errors","text":"The command returns an error in the following cases:\n\n- `WRONGTYPE`: The key exists but is not a stream.\n- `ERR no such key`: The stream does not exist.\n- `ERR syntax error`: Invalid command syntax or missing required arguments.\n- `ERR invalid duration`: Duration value is outside the valid range (1-86,400).\n- `ERR invalid maxsize`: Maxsize value is outside the valid range (1-10,000)."},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the configuration was set successfully.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK` if the configuration was set successfully."}],"examples":[{"id":"examples-ex0","language":"redis-cli","code":"XADD mystream * field value\nXCFGSET mystream IDMP-DURATION 300\nXCFGSET mystream IDMP-MAXSIZE 1000\nXCFGSET mystream IDMP-DURATION 600 IDMP-MAXSIZE 500","section_id":"examples"}]}
{"schema_version":2,"id":"commands/xclaim","title":"XCLAIM","url":"https://redis.io/docs/latest/commands/xclaim/","summary":"Changes, or acquires, ownership of a message in a consumer group, as if the message was delivered a consumer group member.","since":"5.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:25+01:00","page_type":"content","content_hash":"69f00eedbe8c1b439a9e901a52386db20c0d17eb3cb202911d94241efd4dc7f4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"In the context of a stream consumer group, this command changes the ownership\nof a pending message, so that the new owner is the consumer specified as the\ncommand argument. Normally this is what happens:\n\n1. There is a stream with an associated consumer group.\n2. Some consumer A reads a message via [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) from a stream, in the context of that consumer group.\n3. As a side effect a pending message entry is created in the Pending Entries List (PEL) of the consumer group: it means the message was delivered to a given consumer, but it was not yet acknowledged via [`XACK`](https://redis.io/docs/latest/commands/xack).\n4. Then suddenly that consumer fails forever.\n5. Other consumers may inspect the list of pending messages, that are stale for quite some time, using the [`XPENDING`](https://redis.io/docs/latest/commands/xpending) command. In order to continue processing such messages, they use `XCLAIM` to acquire the ownership of the message and continue. Consumers can also use the [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) command to automatically scan and claim stale pending messages.\n\nThis dynamic is clearly explained in the [Stream intro documentation](https://redis.io/docs/latest/develop/data-types/streams).\n\nNote that the message is claimed only if its idle time is greater than the minimum idle time we specify when calling `XCLAIM`. Because as a side effect `XCLAIM` will also reset the idle time (since this is a new attempt at processing the message), two consumers trying to claim a message at the same time will never both succeed: only one will successfully claim the message. This avoids that we process a given message multiple times in a trivial way (yet multiple processing is possible and unavoidable in the general case).\n\nMessages that have been released back to the group using [`XNACK`](https://redis.io/docs/latest/commands/xnack) are immediately claimable since their delivery time is set to 0, satisfying any minimum idle time requirement.\n\nMoreover, as a side effect, `XCLAIM` will increment the count of attempted deliveries of the message unless the `JUSTID` option has been specified (which only delivers the message ID, not the message itself). In this way messages that cannot be processed for some reason, for instance because the consumers crash attempting to process them, will start to have a larger counter and can be detected inside the system.\n\n`XCLAIM` will not claim a message in the following cases:\n\n1. The message doesn't exist in the group PEL (i.e. it was never read by any consumer)\n2. The message exists in the group PEL but not in the stream itself (i.e. the message was read but never acknowledged, and then was deleted from the stream, either by trimming or by [`XDEL`](https://redis.io/docs/latest/commands/xdel))\n\nIn both cases the reply will not contain a corresponding entry to that message (i.e. the length of the reply array may be smaller than the number of IDs provided to `XCLAIM`).\nIn the latter case, the message will also be deleted from the PEL in which it was found. This feature was introduced in Redis 7.0."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe stream key.\n\n</details>\n\n<details open><summary><code>group</code></summary>\n\nThe consumer group name.\n\n</details>\n\n<details open><summary><code>consumer</code></summary>\n\nThe name of the consumer that will own the claimed messages.\n\n</details>\n\n<details open><summary><code>min-idle-time</code></summary>\n\nClaim only messages that have been idle for at least this long, in milliseconds.\n\n</details>\n\n<details open><summary><code>id [id ...]</code></summary>\n\nOne or more message IDs to claim.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"Most of the following options are mainly for internal use, to transfer the effects of `XCLAIM` to the AOF file and replicas, and are unlikely to be useful to normal users.\n\n<details open><summary><code>IDLE ms</code></summary>\n\nSet the idle time (last time it was delivered) of the message. If `IDLE` is not specified, an idle time of `0` is assumed, that is, the time count is reset because the message now has a new owner trying to process it.\n\n</details>\n\n<details open><summary><code>TIME unix-time-milliseconds</code></summary>\n\nThe same as `IDLE` but sets the idle time to a specific Unix time (in milliseconds) instead of a relative amount of milliseconds. This is useful in order to rewrite the AOF file generating `XCLAIM` commands.\n\n</details>\n\n<details open><summary><code>RETRYCOUNT count</code></summary>\n\nSet the retry counter to the specified value. If not set, `XCLAIM` increments the retry counter every time a message is delivered again.\n\n</details>\n\n<details open><summary><code>FORCE</code></summary>\n\nCreate the pending message entry in the PEL even if the specified IDs are not already in the PEL assigned to a different client. However, the message must exist in the stream, otherwise the IDs of non-existing messages are ignored.\n\n</details>\n\n<details open><summary><code>JUSTID</code></summary>\n\nReturn just an array of IDs of messages successfully claimed, without returning the actual messages. Using this option means the retry counter is not incremented.\n\n</details>\n\n<details open><summary><code>LASTID lastid</code></summary>\n\nUpdate the consumer group's last-delivered ID to the given ID.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]\n\nIn the previous example, you claim the message with ID 1526569498055-0 and assign it to the consumer Alice, but only if neither the original consumer nor another consumer has acknowledged or claimed it for at least one hour."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nAny of the following:\n* [Array reply](../../develop/reference/protocol-spec#arrays): when the _JUSTID_ option is specified, an array of IDs of messages successfully claimed.\n* [Array reply](../../develop/reference/protocol-spec#arrays): an array of stream entries, each of which contains an array of two elements, the entry ID and the entry data itself.\n\n**RESP3:**\n\nAny of the following:\n* [Array reply](../../develop/reference/protocol-spec#arrays): when the _JUSTID_ option is specified, an array of IDs of messages successfully claimed.\n* [Array reply](../../develop/reference/protocol-spec#arrays): an array of stream entries, each of which contains an array of two elements, the entry ID and the entry data itself."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> XCLAIM mystream mygroup Alice 3600000 1526569498055-0\n1) 1) 1526569498055-0\n   2) 1) \"message\"\n      2) \"orange\"","section_id":"examples"}]}
{"schema_version":2,"id":"commands/xdel","title":"XDEL","url":"https://redis.io/docs/latest/commands/xdel/","summary":"Returns the number of messages after removing them from a stream.","since":"5.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-06-15T06:26:34-07:00","page_type":"content","content_hash":"67fae388ff3ff0e1629a06d5864bd43f061b9aaf7cefefc59ae3d5582e5e97b8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Removes the specified entries from a stream, and returns the number of entries\ndeleted.  This number may be less than the number of IDs passed to the command in\nthe case where some of the specified IDs do not exist in the stream.\n\nNormally you may think at a Redis stream as an append-only data structure,\nhowever Redis streams are represented in memory, so we are also able to \ndelete entries. This may be useful, for instance, in order to comply with\ncertain privacy policies."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe stream key.\n\n</details>\n\n<details open><summary><code>id [id ...]</code></summary>\n\nOne or more message IDs to delete.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"details","title":"Details","role":"content","text":""},{"id":"understanding-the-low-level-details-of-entries-deletion","title":"Understanding the low level details of entries deletion","role":"content","text":"Redis streams are represented in a way that makes them memory efficient:\na radix tree is used in order to index macro-nodes that pack linearly tens\nof stream entries. Normally what happens when you delete an entry from a stream\nis that the entry is not *really* evicted, it just gets marked as deleted.\n\nEventually if all the entries in a macro-node are marked as deleted, the whole\nnode is destroyed and the memory reclaimed. This means that if you delete\na large amount of entries from a stream, for instance more than 50% of the\nentries appended to the stream, the memory usage per entry may increment, since\nwhat happens is that the stream will become fragmented. However the stream\nperformance will remain the same.\n\nIn future versions of Redis it is possible that we'll trigger a node garbage\ncollection in case a given macro-node reaches a given amount of deleted\nentries. Currently with the usage we anticipate for this data structure, it is\nnot a good idea to add such complexity."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of entries that were deleted.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of entries that were deleted."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> XADD mystream * a 1\n1538561698944-0\n> XADD mystream * b 2\n1538561700640-0\n> XADD mystream * c 3\n1538561701744-0\n> XDEL mystream 1538561700640-0\n(integer) 1\n127.0.0.1:6379> XRANGE mystream - +\n1) 1) 1538561698944-0\n   2) 1) \"a\"\n      2) \"1\"\n2) 1) 1538561701744-0\n   2) 1) \"c\"\n      2) \"3\"","section_id":"examples"}]}
{"schema_version":2,"id":"commands/xdelex","title":"XDELEX","url":"https://redis.io/docs/latest/commands/xdelex/","summary":"Deletes one or multiple entries from the stream.","since":"8.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:25+01:00","page_type":"content","content_hash":"0633849ad3eb80cede270620d3979d178c08607df511effa1ebfb2610ec7288b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Deletes one or multiple entries from the stream at the specified `key`.\n\n`XDELEX` is an extension of the Redis Streams [`XDEL`](https://redis.io/docs/latest/commands/xdel) command that provides more control over how message entries are deleted concerning consumer groups."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>key</code></summary>\n\nThe name of the stream key.\n</details>\n\n<details open>\n<summary><code>IDS numids id [id ...]</code></summary>\n\nThe IDS block specifying which entries to delete:\n- `numids`: The number of IDs that follow\n- `id [id ...]`: One or more stream entry IDs to delete\n\nNote: The IDS block can be at any position in the command, same as other commands.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open>\n<summary><code>KEEPREF | DELREF | ACKED</code></summary>\n\nSpecifies how to handle consumer group references when deleting entries. Available since Redis 8.2. If no option is specified, `KEEPREF` is used by default:\n\n- `KEEPREF` (default): Deletes the specified entries from the stream, but preserves existing references to these entries in all consumer groups' PEL (Pending Entries List). This behavior is similar to [`XDEL`](https://redis.io/docs/latest/commands/xdel).\n- `DELREF`: Deletes the specified entries from the stream and also removes all references to these entries from all consumer groups' pending entry lists, effectively cleaning up all traces of the messages. If an entry ID is not in the stream, but there are dangling references, `XDELEX` with `DELREF` would still remove all those references.\n- `ACKED`: Only deletes entries that were read and acknowledged by all consumer groups.\n</details>\n\nThe command provides fine-grained control over stream entry deletion, particularly useful when working with consumer groups where you need to manage pending entry references carefully."},{"id":"examples","title":"Examples","role":"example","text":"redis> XADD mystream * field1 value1\n\"1784722083515-0\"\nredis> XADD mystream * field2 value2\n\"1784722083516-0\"\nredis> XADD mystream * field3 value3\n\"1784722083517-0\"\nredis> XRANGE mystream - +\n1) 1) \"1784722083515-0\"\n   2) 1) \"field1\"\n      2) \"value1\"\n2) 1) \"1784722083516-0\"\n   2) 1) \"field2\"\n      2) \"value2\"\n3) 1) \"1784722083517-0\"\n   2) 1) \"field3\"\n      2) \"value3\"\nredis> XDELEX mystream KEEPREF IDS 2 1526919030474-55 1526919030474-56\n1) (integer) -1\n2) (integer) -1\nredis> XRANGE mystream - +\n1) 1) \"1784722083515-0\"\n   2) 1) \"field1\"\n      2) \"value1\"\n2) 1) \"1784722083516-0\"\n   2) 1) \"field2\"\n      2) \"value2\"\n3) 1) \"1784722083517-0\"\n   2) 1) \"field3\"\n      2) \"value3\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): -1 for each requested ID when the given key does not exist.\n* [Array reply](../../develop/reference/protocol-spec#arrays): For each ID:\n    * [Integer reply](../../develop/reference/protocol-spec#integers): -1 if no such ID exists in the provided stream key.\n    * [Integer reply](../../develop/reference/protocol-spec#integers): 1 if the entry was deleted from the stream.\n    * [Integer reply](../../develop/reference/protocol-spec#integers): 2 if the entry was not deleted and there are still existing references (`ACKED` option); also, if there are no consumer groups.\n\n**RESP3:**\n\nOne of the following:\n\n* [Array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays): -1 for each requested ID when the given key does not exist.\n* [Array reply](../../develop/reference/protocol-spec#arrays): For each ID:\n    * [Integer reply](../../develop/reference/protocol-spec#integers): -1 if no such ID exists in the provided stream key.\n    * [Integer reply](../../develop/reference/protocol-spec#integers): 1 if the entry was deleted from the stream.\n    * [Integer reply](../../develop/reference/protocol-spec#integers):  2 if the entry was not deleted and there are still existing references (`ACKED` option); also, if there are no consumer groups."}],"examples":[]}
{"schema_version":2,"id":"commands/xgroup","title":"XGROUP","url":"https://redis.io/docs/latest/commands/xgroup/","summary":"A container for consumer groups commands.","since":"5.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:25+01:00","page_type":"content","content_hash":"a459e30c15b82600f4f2e0e0453f57663318fdfae110a94083bee7f8ae380b9d","sections":[{"id":"content","title":"Content","role":"content","text":"This is a container command for stream consumer group management commands.\n\nTo see the list of available commands you can call [`XGROUP HELP`](https://redis.io/docs/latest/commands/xgroup-help)."}],"examples":[]}
{"schema_version":2,"id":"commands/xgroup-create","title":"XGROUP CREATE","url":"https://redis.io/docs/latest/commands/xgroup-create/","summary":"Creates a consumer group.","since":"5.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-06-15T06:26:34-07:00","page_type":"content","content_hash":"004750e52ba30dcced364c731e0936f238ebd57ece0d105b5ec4373f380cc5f4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Create a new consumer group uniquely identified by `groupname` for the stream stored at `key`\n\nEvery group has a unique name in a given stream. \nWhen a consumer group with the same name already exists, the command returns a `-BUSYGROUP` error.\n\nThe command's `id` argument specifies the last delivered entry in the stream from the new group's perspective.\nThe special ID `$` is the ID of the last entry in the stream, but you can substitute it with any valid ID.\n\nFor example, if you want the group's consumers to fetch the entire stream from the beginning, use zero as the starting ID for the consumer group:\n\n    XGROUP CREATE mystream mygroup 0\n\nBy default, the `XGROUP CREATE` command expects that the target stream exists, and returns an error when it doesn't.\nIf a stream does not exist, you can create it automatically with length of 0 by using the optional `MKSTREAM` subcommand as the last argument after the `id`:\n\n    XGROUP CREATE mystream mygroup $ MKSTREAM\n\nTo enable consumer group lag tracking, specify the optional `entries_read` named argument with an arbitrary ID.\nAn arbitrary ID is any ID that isn't the ID of the stream's first entry, last entry, or zero (\"0-0\") ID.\nUse it to find out how many entries are between the arbitrary ID (excluding it) and the stream's last entry.\nSet the `entries_read` the stream's `entries_added` subtracted by the number of entries."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe stream key.\n\n</details>\n\n<details open><summary><code>group</code></summary>\n\nThe name of the consumer group to create.\n\n</details>\n\n<details open><summary><code>id | $</code></summary>\n\nThe ID of the last delivered message the group starts reading after. Use `$` for the last message currently in the stream, or `0` to read from the start.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>MKSTREAM</code></summary>\n\nCreate the stream as an empty stream if it does not already exist.\n\n</details>\n\n<details open><summary><code>ENTRIESREAD entries-read</code></summary>\n\nSet the group's initial entries-read counter, used to compute its lag.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[]}
{"schema_version":2,"id":"commands/xgroup-createconsumer","title":"XGROUP CREATECONSUMER","url":"https://redis.io/docs/latest/commands/xgroup-createconsumer/","summary":"Creates a consumer in a consumer group.","since":"6.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:25+01:00","page_type":"content","content_hash":"1c54866b33c9697a43d8078028d3edb722223d18a4d315b013d913597e64a62e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Create a consumer named `consumername` in the consumer group `groupname` of the stream that's stored at `key`.\n\nConsumers are also created automatically whenever an operation, such as [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup), references a consumer that doesn't exist.\nThis is valid for [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) only when there is data in the stream."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe stream key.\n\n</details>\n\n<details open><summary><code>group</code></summary>\n\nThe consumer group name.\n\n</details>\n\n<details open><summary><code>consumer</code></summary>\n\nThe name of the consumer to create.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of created consumers, either 0 or 1.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of created consumers, either 0 or 1."}],"examples":[]}
{"schema_version":2,"id":"commands/xgroup-delconsumer","title":"XGROUP DELCONSUMER","url":"https://redis.io/docs/latest/commands/xgroup-delconsumer/","summary":"Deletes a consumer from a consumer group.","since":"5.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-06-15T06:26:34-07:00","page_type":"content","content_hash":"81de9735d7448c2d3a0c89acdbb4b5b875be6b26736a98a2521ffd3b1f739252","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `XGROUP DELCONSUMER` command deletes a consumer from the consumer group.\n\nSometimes it may be useful to remove old consumers since they are no longer used.\n\nNote, however, that any pending messages that the consumer had will become unclaimable after it was deleted.\nIt is strongly recommended, therefore, that any pending messages are claimed or acknowledged prior to deleting the consumer from the group."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe stream key.\n\n</details>\n\n<details open><summary><code>group</code></summary>\n\nThe consumer group name.\n\n</details>\n\n<details open><summary><code>consumer</code></summary>\n\nThe name of the consumer to delete.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of pending messages the consumer had before it was deleted.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of pending messages the consumer had before it was deleted."}],"examples":[]}
{"schema_version":2,"id":"commands/xgroup-destroy","title":"XGROUP DESTROY","url":"https://redis.io/docs/latest/commands/xgroup-destroy/","summary":"Destroys a consumer group.","since":"5.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-06-15T06:26:34-07:00","page_type":"content","content_hash":"2430d77b275649898dd668200f3c33653b4c634b8ebd1521185e2966c9927206","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `XGROUP DESTROY` command completely destroys a consumer group.\n\nThe consumer group will be destroyed even if there are active consumers, and pending messages, so make sure to call this command only when really needed."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe stream key.\n\n</details>\n\n<details open><summary><code>group</code></summary>\n\nThe name of the consumer group to destroy.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of destroyed consumer groups, either 0 or 1.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of destroyed consumer groups, either 0 or 1."}],"examples":[]}
{"schema_version":2,"id":"commands/xgroup-help","title":"XGROUP HELP","url":"https://redis.io/docs/latest/commands/xgroup-help/","summary":"Returns helpful text about the different subcommands.","since":"5.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"e3bbf82d2609ab1279744c5d914f3eb8b53de68ac6fea6a9efaf9f40efcce8eb","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `XGROUP HELP` command returns a helpful text describing the different subcommands."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of sub-commands and their descriptions.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of sub-commands and their descriptions."}],"examples":[]}
{"schema_version":2,"id":"commands/xgroup-setid","title":"XGROUP SETID","url":"https://redis.io/docs/latest/commands/xgroup-setid/","summary":"Sets the last-delivered ID of a consumer group.","since":"5.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:25+01:00","page_type":"content","content_hash":"b650ce9392e239363e6c0bac34bd93662808675f330e4a2f512af7db03ffedc1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Set the last delivered ID for a consumer group.\n\nNormally, a consumer group's last delivered ID is set when the group is created with [`XGROUP CREATE`](https://redis.io/docs/latest/commands/xgroup-create).\nThe `XGROUP SETID` command allows modifying the group's last delivered ID, without having to delete and recreate the group.\nFor instance if you want the consumers in a consumer group to re-process all the messages in a stream, you may want to set its next ID to 0:\n\n    XGROUP SETID mystream mygroup 0\n\nThe optional `entries_read` argument can be specified to enable consumer group lag tracking for an arbitrary ID.\nAn arbitrary ID is any ID that isn't the ID of the stream's first entry, its last entry or the zero (`0-0`) ID.\nThis can be useful you know exactly how many entries are between the arbitrary ID (excluding it) and the stream's last entry.\nIn such cases, the `entries_read` can be set to the stream's `entries_added` subtracted with the number of entries."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe stream key.\n\n</details>\n\n<details open><summary><code>group</code></summary>\n\nThe consumer group name.\n\n</details>\n\n<details open><summary><code>id | $</code></summary>\n\nThe ID to set as the group's last-delivered ID. Use `$` for the last message currently in the stream, or `0` to reset to the start.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>ENTRIESREAD entries-read</code></summary>\n\nSet the group's entries-read counter, used to compute its lag.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[]}
{"schema_version":2,"id":"commands/xidmprecord","title":"XIDMPRECORD","url":"https://redis.io/docs/latest/commands/xidmprecord/","summary":"An internal command for setting IDMP metadata on an existing stream message.","since":"8.8.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:25+01:00","page_type":"content","content_hash":"daf12d1539d9ba8ee8fb9b85df6dde8f4ac32174541ace1f157439dd2d003d31","sections":[{"id":"overview","title":"Overview","role":"overview","text":"`XIDMPRECORD` is an internal command. Users should not call it directly.\n \nRedis uses it to set IDMP metadata, which is then replayed during AOF loading."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nis the name of a stream key.\n\n</details>\n\n<details open><summary><code>pid</code></summary>\n\nis a producer ID.\n\n</details>\n\n<details open><summary><code>iid</code></summary>\n\nis an IDMP (idempotency) ID.\n\n</details>\n\n<details open><summary><code>stream-id</code></summary>\n\nis the ID of an existing stream message.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n\n- A [simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) - `OK` when the provided pid/iid pair already maps to the same stream ID (that is, the command is idempotent).\n- A [simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in one of the following cases:\n    - the key does not exist\n    - the key does not refer to a stream\n    - the stream ID refers to an non-existent or deleted entry\n    - pid and/or iid are empty\n    - the pid/iid pair already maps to a different stream ID\n\n**RESP3:**\n\nOne of the following:\n\n- A [simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) - `OK` when the provided pid/iid pair already maps to the same stream ID (that is, the command is idempotent).\n- A [simple error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) in one of the following cases:\n    - the key does not exist\n    - the key does not refer to a stream\n    - the stream ID refers to an non-existent or deleted entry\n    - pid and/or iid are empty\n    - the pid/iid pair already maps to a different stream ID"}],"examples":[]}
{"schema_version":2,"id":"commands/xinfo","title":"XINFO","url":"https://redis.io/docs/latest/commands/xinfo/","summary":"A container for stream introspection commands.","since":"5.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:25+01:00","page_type":"content","content_hash":"04e43de615298857360ad112a08e4c56588f0613144c17b3c8bd7d86b04d04b5","sections":[{"id":"content","title":"Content","role":"content","text":"This is a container command for stream introspection commands.\n\nTo see the list of available commands you can call [`XINFO HELP`](https://redis.io/docs/latest/commands/xinfo-help)."}],"examples":[]}
{"schema_version":2,"id":"commands/xinfo-consumers","title":"XINFO CONSUMERS","url":"https://redis.io/docs/latest/commands/xinfo-consumers/","summary":"Returns a list of the consumers in a consumer group.","since":"5.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:25+01:00","page_type":"content","content_hash":"ad328735bd9b15f5bf886d41ab8cc4faf7b1003de7cef01662e388888c8b8a06","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This command returns the list of consumers that belong to the `groupname` consumer group of the stream stored at `key`.\n\nThe following information is provided for each consumer in the group:\n\n* **name**: the consumer's name\n* **pending**: the number of entries in the PEL: pending messages for the consumer, which are messages that were delivered but are yet to be acknowledged\n* **idle**: the number of milliseconds that have passed since the consumer's last attempted interaction (Examples: [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup), [`XCLAIM`](https://redis.io/docs/latest/commands/xclaim), [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim))\n* **inactive**: the number of milliseconds that have passed since the consumer's last successful interaction (Examples: [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) that actually read some entries into the PEL, [`XCLAIM`](https://redis.io/docs/latest/commands/xclaim)/[`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) that actually claimed some entries)\n\nNote that before Redis 7.2.0, **idle** used to denote the time passed since last successful interaction.\nIn 7.2.0, **inactive** was added and **idle** was changed to denote the time passed since last attempted interaction."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe stream key.\n\n</details>\n\n<details open><summary><code>group</code></summary>\n\nThe consumer group name.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of consumers and their attributes.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of consumers and their attributes."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> XINFO CONSUMERS mystream mygroup\n1) 1) name\n   2) \"Alice\"\n   3) pending\n   4) (integer) 1\n   5) idle\n   6) (integer) 9104628\n   7) inactive\n   8) (integer) 18104698\n2) 1) name\n   2) \"Bob\"\n   3) pending\n   4) (integer) 1\n   5) idle\n   6) (integer) 83841983\n   7) inactive\n   8) (integer) 993841998","section_id":"examples"}]}
{"schema_version":2,"id":"commands/xinfo-groups","title":"XINFO GROUPS","url":"https://redis.io/docs/latest/commands/xinfo-groups/","summary":"Returns a list of the consumer groups of a stream.","since":"5.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:25+01:00","page_type":"content","content_hash":"0853d7f22275f9cd3fb827330dd4ae5eaa300d7e3933911ac3b847826ba3b71c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This command returns the list of all consumer groups of the stream stored at `key`.\n\nBy default, only the following information is provided for each of the groups:\n\n* **name**: the consumer group's name\n* **consumers**: the number of consumers in the group\n* **pending**: the length of the group's pending entries list (PEL), which are messages that were delivered but are yet to be acknowledged\n* **last-delivered-id**: the ID of the last entry delivered to the group's consumers\n* **entries-read**: the logical \"read counter\" of the last entry delivered to the group's consumers\n* **lag**: the number of entries in the stream that are still waiting to be delivered to the group's consumers, or a NULL when that number can't be determined."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe stream key.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"details","title":"Details","role":"content","text":""},{"id":"consumer-group-lag","title":"Consumer group lag","role":"content","text":"The lag of a given consumer group is the number of entries in the range between the group's `entries_read` and the stream's `entries_added`.\nPut differently, it is the number of entries that are yet to be delivered to the group's consumers.\n\nThe values and trends of this metric are helpful in making scaling decisions about the consumer group.\nYou can address high lag values by adding more consumers to the group, whereas low values may indicate that you can remove consumers from the group to scale it down.\n\nRedis reports the lag of a consumer group by keeping two counters: the number of all entries added to the stream and the number of logical reads made by the consumer group.\nThe lag is the difference between these two.\n\nThe stream's counter (the `entries_added` field of the [`XINFO STREAM`](https://redis.io/docs/latest/commands/xinfo-stream) command) is incremented by one with every [`XADD`](https://redis.io/docs/latest/commands/xadd) and counts all of the entries added to the stream during its lifetime.\n\nThe consumer group's counter, `entries_read`, is the logical counter of entries the group had read.\nIt is important to note that this counter is only a heuristic rather than an accurate counter, and therefore the use of the term \"logical\".\nThe counter attempts to reflect the number of entries that the group **should have read** to get to its current `last-delivered-id`.\nThe `entries_read` counter is accurate only in a perfect world, where a consumer group starts at the stream's first entry and processes all of its entries (i.e., no entries deleted before processing).\n\nThere are two special cases in which this mechanism is unable to report the lag:\n\n1. A consumer group is created or set with an arbitrary last delivered ID (the [`XGROUP CREATE`](https://redis.io/docs/latest/commands/xgroup-create) and [`XGROUP SETID`](https://redis.io/docs/latest/commands/xgroup-setid) commands, respectively).\n    An arbitrary ID is any ID that isn't the ID of the stream's first entry, its last entry or the zero (\"0-0\") ID.\n2. One or more entries between the group's `last-delivered-id` and the stream's `last-generated-id` were deleted (with [`XDEL`](https://redis.io/docs/latest/commands/xdel) or a trimming operation).\n\nIn both cases, the group's read counter is considered invalid, and the returned value is set to NULL to signal that the lag isn't currently available.\n\nHowever, the lag is only temporarily unavailable.\nIt is restored automatically during regular operation as consumers keep processing messages.\nOnce the consumer group delivers the last message in the stream to its members, it will be set with the correct logical read counter, and tracking its lag can be resumed."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of consumer groups.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of consumer groups."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> XINFO GROUPS mystream\n1)  1) \"name\"\n    2) \"mygroup\"\n    3) \"consumers\"\n    4) (integer) 2\n    5) \"pending\"\n    6) (integer) 2\n    7) \"last-delivered-id\"\n    8) \"1638126030001-0\"\n    9) \"entries-read\"\n   10) (integer) 2\n   11) \"lag\"\n   12) (integer) 0\n2)  1) \"name\"\n    2) \"some-other-group\"\n    3) \"consumers\"\n    4) (integer) 1\n    5) \"pending\"\n    6) (integer) 0\n    7) \"last-delivered-id\"\n    8) \"1638126028070-0\"\n    9) \"entries-read\"\n   10) (integer) 1\n   11) \"lag\"\n   12) (integer) 1","section_id":"examples"}]}
{"schema_version":2,"id":"commands/xinfo-help","title":"XINFO HELP","url":"https://redis.io/docs/latest/commands/xinfo-help/","summary":"Returns helpful text about the different subcommands.","since":"5.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"53b8588f8e7d6e4bc1c0088f20471a0c8d725036e49edeac55c25c6132b6060b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `XINFO HELP` command returns a helpful text describing the different subcommands."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of sub-commands and their descriptions.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of sub-commands and their descriptions."}],"examples":[]}
{"schema_version":2,"id":"commands/xinfo-stream","title":"XINFO STREAM","url":"https://redis.io/docs/latest/commands/xinfo-stream/","summary":"Returns information about a stream.","since":"5.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:25+01:00","page_type":"content","content_hash":"6905e207a20f2045674eca609dd83cdcb07199a368d467a0ad9ded4a81e4816f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This command returns information about the stream stored at `key`.\n\nThe informative details provided by this command are:\n\n* **length**: the number of entries in the stream (see [`XLEN`](https://redis.io/docs/latest/commands/xlen))\n* **radix-tree-keys**: the number of keys in the underlying radix data structure\n* **radix-tree-nodes**: the number of nodes in the underlying radix data structure\n* **groups**: the number of consumer groups defined for the stream\n* **last-generated-id**: the ID of the least-recently entry that was added to the stream\n* **max-deleted-entry-id**: the maximal entry ID that was deleted from the stream\n* **entries-added**: the count of all entries added to the stream during its lifetime\n* **first-entry**: the ID and field-value tuples of the first entry in the stream\n* **last-entry**: the ID and field-value tuples of the last entry in the stream"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe stream key.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>FULL</code></summary>\n\nReturn the full stream state, including all entries and detailed PEL information, instead of a summary.\n\n</details>\n\n<details open><summary><code>COUNT count</code></summary>\n\nWith `FULL`, the number of entries to return. `0` returns all entries; the default is 10.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"Setting up a stream with IDMP:\n\n[code example]\n\nDefault reply (with IDMP configured):\n\n[code example]\n\nFull reply:\n\n[code example]"},{"id":"details","title":"Details","role":"content","text":""},{"id":"idmp-idempotent-message-processing-fields","title":"IDMP (Idempotent Message Processing) fields","role":"content","text":"When IDMP is configured for the stream using [`XCFGSET`](https://redis.io/docs/latest/commands/xcfgset), the following additional fields are included:\n\n* **idmp-duration**: the duration in seconds that idempotent IDs are retained in the stream's IDMP map\n* **idmp-maxsize**: the maximum number of idempotent IDs kept for each producer in the stream's IDMP map\n* **pids-tracked**: the number of unique producer IDs currently being tracked\n* **iids-tracked**: the total number of idempotent IDs currently stored across all producers\n* **iids-added**: the total count of idempotent IDs that have been added to the stream during its lifetime\n* **iids-duplicates**: the total count of duplicate messages that were detected and prevented by IDMP"},{"id":"the-full-modifier","title":"The `FULL` modifier","role":"content","text":"The optional `FULL` modifier provides a more verbose reply.\nWhen provided, the `FULL` reply includes an **entries** array that consists of the stream entries (ID and field-value tuples) in ascending order.\nFurthermore, **groups** is also an array, and for each of the consumer groups it consists of the information reported by [`XINFO GROUPS`](https://redis.io/docs/latest/commands/xinfo-groups) and [`XINFO CONSUMERS`](https://redis.io/docs/latest/commands/xinfo-consumers).\n\nThe following information is provided for each of the groups:\n\n* **name**: the consumer group's name\n* **last-delivered-id**: the ID of the last entry delivered to the group's consumers\n* **entries-read**: the logical \"read counter\" of the last entry delivered to the group's consumers\n* **lag**: the number of entries in the stream that are still waiting to be delivered to the group's consumers, or a NULL when that number can't be determined.\n* **pel-count**: the length of the group's pending entries list (PEL), which are messages that were delivered but are yet to be acknowledged\n* **pending**: an array with pending entries information (see below)\n* **consumers**: an array with consumers information (see below)\n* **nacked-count**: the number of entries currently in the NACKed portion of the PEL. See the [`XNACK` command page](https://redis.io/docs/latest/commands/xnack) for more details. Added in Redis 8.8.\n\nThe following information is provided for each pending entry:\n\n1. The ID of the message.\n2. The name of the consumer that fetched the message and has yet to acknowledge it. We call it the current *owner* of the message. For messages that have been released back to the group using [`XNACK`](https://redis.io/docs/latest/commands/xnack), this field will be an empty string.\n3. The UNIX timestamp of when the message was delivered to this consumer.\n4. The number of times this message was delivered.\n\nThe following information is provided for each consumer:\n\n* **name**: the consumer's name\n* **seen-time**: the UNIX timestamp of the last attempted interaction (Examples: [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup), [`XCLAIM`](https://redis.io/docs/latest/commands/xclaim), [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim))\n* **active-time**: the UNIX timestamp of the last successful interaction (Examples: [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) that actually read some entries into the PEL, [`XCLAIM`](https://redis.io/docs/latest/commands/xclaim)/[`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) that actually claimed some entries)\n* **pel-count**: the number of entries in the PEL: pending messages for the consumer, which are messages that were delivered but are yet to be acknowledged\n* **pending**: an array with pending entries information, has the same structure as described above, except the consumer name is omitted (redundant, since anyway we are in a specific consumer context)\n\nNote that before Redis 7.2.0, **seen-time** used to denote the last successful interaction.\nIn 7.2.0, **active-time** was added and **seen-time** was changed to denote the last attempted interaction.\n\nThe `COUNT` option can be used to limit the number of stream and PEL entries that are returned (The first `count` entries are returned).\nThe default `COUNT` is 10 and a `COUNT` of 0 means that all entries will be returned (execution time may be long if the stream has a lot of entries)."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Array reply](../../develop/reference/protocol-spec#arrays): when the _FULL_ argument is used, a list of information about a stream in summary form.\n* [Array reply](../../develop/reference/protocol-spec#arrays): when the _FULL_ argument is used, a list of information about a stream in extended form.\n\n**RESP3:**\n\nOne of the following:\n* [Map reply](../../develop/reference/protocol-spec#maps): when the _FULL_ argument was not given, a list of information about a stream in summary form.\n* [Map reply](../../develop/reference/protocol-spec#maps): when the _FULL_ argument was given, a list of information about a stream in extended form."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> XADD mystream * message apple\n\"1638125133432-0\"\n> XADD mystream * message banana\n\"1638125141232-0\"\n> XCFGSET mystream DURATION 100 MAXSIZE 100\nOK\n> XADD mystream IDMP producer1 msg1 * field value\n\"1638125150000-0\"","section_id":"examples"},{"id":"examples-ex1","language":"plaintext","code":"> XINFO STREAM mystream\n 1) \"length\"\n 2) (integer) 2\n 3) \"radix-tree-keys\"\n 4) (integer) 1\n 5) \"radix-tree-nodes\"\n 6) (integer) 2\n 7) \"last-generated-id\"\n 8) \"1638125141232-0\"\n 9) \"max-deleted-entry-id\"\n10) \"0-0\"\n11) \"entries-added\"\n12) (integer) 2\n13) \"recorded-first-entry-id\"\n14) \"1719505260513-0\"\n15) \"idmp-duration\"\n16) (integer) 100\n17) \"idmp-maxsize\"\n18) (integer) 100\n19) \"pids-tracked\"\n20) (integer) 1\n21) \"iids-tracked\"\n22) (integer) 1\n23) \"iids-added\"\n24) (integer) 1\n25) \"iids-duplicates\"\n26) (integer) 0\n27) \"groups\"\n28) (integer) 1\n29) \"first-entry\"\n30) 1) \"1638125133432-0\"\n    2) 1) \"message\"\n       2) \"apple\"\n31) \"last-entry\"\n32) 1) \"1638125141232-0\"\n    2) 1) \"message\"\n       2) \"banana\"","section_id":"examples"},{"id":"examples-ex2","language":"plaintext","code":"> XADD mystream * foo bar\n\"1638125133432-0\"\n> XADD mystream * foo bar2\n\"1638125141232-0\"\n> XGROUP CREATE mystream mygroup 0-0\nOK\n> XREADGROUP GROUP mygroup Alice COUNT 1 STREAMS mystream >\n1) 1) \"mystream\"\n   2) 1) 1) \"1638125133432-0\"\n         2) 1) \"foo\"\n            2) \"bar\"\n> XINFO STREAM mystream FULL\n 1) \"length\"\n 2) (integer) 2\n 3) \"radix-tree-keys\"\n 4) (integer) 1\n 5) \"radix-tree-nodes\"\n 6) (integer) 2\n 7) \"last-generated-id\"\n 8) \"1638125141232-0\"\n 9) \"max-deleted-entry-id\"\n10) \"0-0\"\n11) \"entries-added\"\n12) (integer) 2\n13) \"recorded-first-entry-id\"\n14) \"1719505260513-0\"\n15) \"entries\"\n16) 1) 1) \"1638125133432-0\"\n       2) 1) \"foo\"\n          2) \"bar\"\n    2) 1) \"1638125141232-0\"\n       2) 1) \"foo\"\n          2) \"bar2\"\n17) \"groups\"\n18) 1)  1) \"name\"\n        2) \"mygroup\"\n        3) \"last-delivered-id\"\n        4) \"1638125133432-0\"\n        5) \"entries-read\"\n        6) (integer) 1\n        7) \"lag\"\n        8) (integer) 1\n        9) \"pel-count\"\n       10) (integer) 1\n       11) \"pending\"\n       12) 1) 1) \"1638125133432-0\"\n              2) \"Alice\"\n              3) (integer) 1638125153423\n              4) (integer) 1\n       13) \"consumers\"\n       14) 1) 1) \"name\"\n              2) \"Alice\"\n              3) \"seen-time\"\n              4) (integer) 1638125133422\n              5) \"active-time\"\n              6) (integer) 1638125133432\n              7) \"pel-count\"\n              8) (integer) 1\n              9) \"pending\"\n              10) 1) 1) \"1638125133432-0\"\n                     2) (integer) 1638125133432\n                     3) (integer) 1","section_id":"examples"}]}
{"schema_version":2,"id":"commands/xlen","title":"XLEN","url":"https://redis.io/docs/latest/commands/xlen/","summary":"Return the number of messages in a stream.","since":"5.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:25+01:00","page_type":"content","content_hash":"5fac269a68310ed7c22037afd1cc2bc15d852b147be996f42b18ab4720ee88da","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns the number of entries inside a stream. If the specified key does not\nexist the command returns zero, as if the stream was empty.\nHowever note that unlike other Redis types, zero-length streams are\npossible, so you should call [`TYPE`](https://redis.io/docs/latest/commands/type) or [`EXISTS`](https://redis.io/docs/latest/commands/exists) in order to check if\na key exists or not.\n\nStreams are not auto-deleted once they have no entries inside (for instance\nafter an [`XDEL`](https://redis.io/docs/latest/commands/xdel) call), because the stream may have consumer groups\nassociated with it."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe stream key.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> XADD mystream * item 1\n\"1784722083902-0\"\nredis> XADD mystream * item 2\n\"1784722083903-0\"\nredis> XADD mystream * item 3\n\"1784722083904-0\"\nredis> XLEN mystream\n(integer) 3"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of entries of the stream at _key_.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of entries of the stream at _key_."}],"examples":[]}
{"schema_version":2,"id":"commands/xnack","title":"XNACK","url":"https://redis.io/docs/latest/commands/xnack/","summary":"Releases pending messages back to the group's PEL without acknowledging them, making them available for re-delivery.","since":"8.8.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:25+01:00","page_type":"content","content_hash":"07e59c6a9d4f7f6adeb1d19f8cee456315a37b428d5b79cbfdaa3ef1ad7d690a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"In the context of a stream consumer group, this command allows a consumer to explicitly release pending messages back to the group's Pending Entries List (PEL) without acknowledging them. Released messages become immediately available for re-delivery to other consumers in the group, eliminating the idle-timeout delay normally required for message recovery.\n\nThis is particularly useful in scenarios such as graceful consumer shutdown, consumer-side failures, resource constraints, or poison message detection. The command provides three different modes that control how the delivery counter is adjusted, giving consumers fine-grained control over retry semantics."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nName of the stream.\n\n</details>\n\n<details open><summary><code>group</code></summary>\n\nName of the consumer group.\n\n</details>\n\n<details open><summary><code>mode</code></summary>\n\nControls the delivery counter adjustment. Must be one of:\n\n- **SILENT**: Decrements the delivery counter by 1, essentially \"undoing\" the delivery increment. Use this for an internal failure on the consumer side while processing the message or graceful shutdown where the delivery \"didn't count\".\n- **FAIL**: Keeps the current delivery counter value unchanged. Use this when the current consumer failed to process this message (for example, due to memory constraints). The root cause may be the message or the consumer (it is unclear), so the best strategy would be to let another consumer try to process the message.\n- **FATAL**: Sets the delivery counter to the maximum value (LLONG_MAX or ~9.22 X 10<sup>18</sup>), marking the message as permanently failed. Use this for invalid or suspected malicious messages.\n\n</details>\n\n<details open><summary><code>ids</code></summary>\n\nBlock of arguments that specifies the message IDs to release, where `numids` is the number of message IDs that follow, and `id [id ...]` represents the stream entry IDs to be released back to the group.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>RETRYCOUNT</code></summary>\n\nDirectly sets the delivery counter to the specified value, overriding the mode-based adjustment. This gives explicit control over the retry counter regardless of the mode selected. Note: this is an internal argument that should usually not be used by consumers.\n\n</details>\n\n<details open><summary><code>FORCE</code></summary>\n\nCreates new unowned PEL entries for IDs that are not already in the group PEL. Each entry must exist in the stream. When `FORCE` creates an entry, the delivery counter is set to `0` (or to `RETRYCOUNT` if specified, or to `LLONG_MAX` if mode is `FATAL`). Note: this is an internal argument that should usually not be used by consumers.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"<details open>\n<summary><strong>Basic usage with FAIL mode</strong></summary>\n\n[code example]\n\nAfter NACKing, the messages appear with empty consumer in XPENDING:\n\n[code example]\n\n</details>\n\n<details open>\n<summary><strong>Using SILENT mode for graceful shutdown</strong></summary>\n\n[code example]\n\nWith SILENT mode, the delivery counter is decremented, effectively \"undoing\" the delivery.\n\n</details>\n\n<details open>\n<summary><strong>Using FATAL mode for poison messages</strong></summary>\n\n[code example]\n\nThis marks the message as permanently failed by setting the delivery counter to maximum value.\n\n</details>"},{"id":"details","title":"Details","role":"content","text":"When `XNACK` executes successfully, the entry:\n\n* is marked as unowned (its last consumer is set to an empty string).\n* is assigned a last delivery time of `0`.\n* is placed at the end of the NACKed portion of the PEL.\n* is available for immediate re-delivery via [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) CLAIM, [`XCLAIM`](https://redis.io/docs/latest/commands/xclaim), or [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim).\n\nThe head of the PEL is reserved for all NACKed messages, ordered as a FIFO list, followed by pending messages that were neither ACKed nor NACKed in their existing order.\n\nNACKed messages are prioritized over other pending messages in the group's PEL, ensuring they are delivered before idle messages during claim operations.\n\nNotes:\n\n- `XNACK` will only process message IDs that exist in the consumer group's PEL. Messages that are not pending will be ignored and not counted in the return value.\n- Released messages occupy a dedicated zone at the head of the PEL (called the *XNACKed portion of the PEL*), ensuring they are prioritized for re-delivery over other pending entries.\n- Released messages have their delivery time set to 0, making them immediately claimable regardless of the `min-idle-time` parameter in claiming commands.\n- Unlike [`XACK`](https://redis.io/docs/latest/commands/xack), this command does not remove messages from the PEL but instead makes them available for other consumers."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): The number of messages successfully released back to the group PEL. Messages that are not in the consumer group PEL will not be counted.\n\n**RESP3:**\n\n[Integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers): The number of messages successfully released back to the consumer group PEL. Messages that are not in the consumer group PEL will not be counted."},{"id":"see-also","title":"See also","role":"related","text":"- [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup): Read messages from a consumer group\n- [`XACK`](https://redis.io/docs/latest/commands/xack): Acknowledge processed messages\n- [`XCLAIM`](https://redis.io/docs/latest/commands/xclaim): Claim pending messages from other consumers\n- [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim): Automatically claim idle pending messages\n- [`XPENDING`](https://redis.io/docs/latest/commands/xpending): Inspect pending messages in a consumer group"}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> XADD mystream * field value1\n\"1526569498055-0\"\n> XADD mystream * field value2\n\"1526569498056-0\"\n> XGROUP CREATE mystream mygroup 0\nOK\n> XREADGROUP GROUP mygroup consumer1 STREAMS mystream >\n1) 1) \"mystream\"\n   2) 1) 1) \"1526569498055-0\"\n         2) 1) \"field\"\n            2) \"value1\"\n      2) 1) \"1526569498056-0\"\n         2) 1) \"field\"\n            2) \"value2\"\n> XNACK mystream mygroup FAIL IDS 2 1526569498055-0 1526569498056-0\n(integer) 2","section_id":"examples"},{"id":"examples-ex1","language":"plaintext","code":"> XPENDING mystream mygroup - + 10\n1) 1) \"1526569498055-0\"\n   2) \"\"\n   3) (integer) -1\n   4) (integer) 1\n2) 1) \"1526569498056-0\"\n   2) \"\"\n   3) (integer) -1\n   4) (integer) 1","section_id":"examples"},{"id":"examples-ex2","language":"plaintext","code":"> XNACK mystream mygroup SILENT IDS 1 1526569498055-0\n(integer) 1","section_id":"examples"},{"id":"examples-ex3","language":"plaintext","code":"> XNACK mystream mygroup FATAL IDS 1 1526569498055-0\n(integer) 1","section_id":"examples"}]}
{"schema_version":2,"id":"commands/xpending","title":"XPENDING","url":"https://redis.io/docs/latest/commands/xpending/","summary":"Returns the information and entries from a stream consumer group's pending entries list.","since":"5.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:25+01:00","page_type":"content","content_hash":"eae31cc246a9f49de9d6426e3856aa705cc606ceeb9c0c39ef2e641f180acc9a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Fetching data from a stream via a consumer group, and not acknowledging\nsuch data, has the effect of creating *pending entries*. This is\nwell explained in the [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) command, and even better in our\n[introduction to Redis Streams](https://redis.io/docs/latest/develop/data-types/streams). The [`XACK`](https://redis.io/docs/latest/commands/xack) command\nwill immediately remove the pending entry from the Pending Entries List (PEL)\nsince once a message is successfully processed, there is no longer need\nfor the consumer group to track it and to remember the current owner\nof the message.\n\nThe `XPENDING` command is the interface to inspect the list of pending\nmessages, and is as thus a very important command in order to observe\nand understand what is happening with a streams consumer groups: what\nclients are active, what messages are pending to be consumed, or to see\nif there are idle messages. Moreover this command, together with [`XCLAIM`](https://redis.io/docs/latest/commands/xclaim)\nis used in order to implement recovering of consumers that are failing\nfor a long time, and as a result certain messages are not processed: a\ndifferent consumer can claim the message and continue. This is better\nexplained in the [streams intro](https://redis.io/docs/latest/develop/data-types/streams) and in the\n[`XCLAIM`](https://redis.io/docs/latest/commands/xclaim) command page, and is not covered here."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe stream key.\n\n</details>\n\n<details open><summary><code>group</code></summary>\n\nThe consumer group name.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"With no optional arguments, `XPENDING` returns a summary. Providing `start`, `end`, and `count` returns the extended (detailed) form.\n\n<details open><summary><code>IDLE min-idle-time</code></summary>\n\nFilter to messages that have been idle for at least this long, in milliseconds.\n\n</details>\n\n<details open><summary><code>start</code></summary>\n\nThe start of the ID range to inspect, inclusive. Use `-` for the smallest ID.\n\n</details>\n\n<details open><summary><code>end</code></summary>\n\nThe end of the ID range to inspect, inclusive. Use `+` for the greatest ID.\n\n</details>\n\n<details open><summary><code>count</code></summary>\n\nThe maximum number of messages to return.\n\n</details>\n\n<details open><summary><code>consumer</code></summary>\n\nFilter to messages owned by the given consumer.\n\n</details>"},{"id":"details","title":"Details","role":"content","text":""},{"id":"summary-form-of-xpending","title":"Summary form of XPENDING","role":"content","text":"When `XPENDING` is called with just a key name and a consumer group\nname, it just outputs a summary about the pending messages in a given\nconsumer group. In the following example, we create a consumer group and\nimmediately create a pending message by reading from the group with\n[`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup).\n\n[code example]\n\nWe expect the pending entries list for the consumer group `group55` to\nhave a message right now: consumer named `consumer-123` fetched the\nmessage without acknowledging its processing. The simple `XPENDING`\nform will give us this information:\n\n[code example]\n\nIn this form, the command outputs the total number of pending messages for this\nconsumer group, which is one, followed by the smallest and greatest ID among the\npending messages, and then list every consumer in the consumer group with\nat least one pending message, and the number of pending messages it has."},{"id":"extended-form-of-xpending","title":"Extended form of XPENDING","role":"content","text":"The summary provides a good overview, but sometimes we are interested in the\ndetails. In order to see all the pending messages with more associated\ninformation we need to also pass a range of IDs, in a similar way we do it with\n[`XRANGE`](https://redis.io/docs/latest/commands/xrange), and a non optional *count* argument, to limit the number\nof messages returned per call:\n\n[code example]\n\nIn the extended form we no longer see the summary information, instead there\nis detailed information for each message in the pending entries list. For\neach message four attributes are returned:\n\n1. The ID of the message.\n2. The name of the consumer that fetched the message and has still to acknowledge it. We call it the current *owner* of the message. For messages that have been released back to the group using [`XNACK`](https://redis.io/docs/latest/commands/xnack), this field will be an empty string.\n3. The number of milliseconds that elapsed since the last time this message was delivered to this consumer.\n4. The number of times this message was delivered.\n\nThe deliveries counter, that is the fourth element in the array, is incremented\nwhen some other consumer *claims* the message with [`XCLAIM`](https://redis.io/docs/latest/commands/xclaim), or when the\nmessage is delivered again via [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup), when accessing the history\nof a consumer in a consumer group (see the [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) page for more info).\n\nIt is possible to pass an additional argument to the command, in order\nto see the messages having a specific owner:\n\n[code example]\n\nBut in the above case the output would be the same, since we have pending\nmessages only for a single consumer. However what is important to keep in\nmind is that this operation, filtering by a specific consumer, is not\ninefficient even when there are many pending messages from many consumers:\nwe have a pending entries list data structure both globally, and for\nevery consumer, so we can very efficiently show just messages pending for\na single consumer."},{"id":"idle-time-filter","title":"Idle time filter","role":"content","text":"It is also possible to filter pending stream entries by their idle-time,\ngiven in milliseconds (useful for [`XCLAIM`](https://redis.io/docs/latest/commands/xclaim)ing entries that have not been\nprocessed for some time):\n\n[code example]\n\nThe first case will return the first 10 (or less) PEL entries of the entire group\nthat are idle for over 9 seconds, whereas in the second case only those of\n`consumer-123`."},{"id":"exclusive-ranges-and-iterating-the-pel","title":"Exclusive ranges and iterating the PEL","role":"content","text":"The `XPENDING` command allows iterating over the pending entries just like\n[`XRANGE`](https://redis.io/docs/latest/commands/xrange) and [`XREVRANGE`](https://redis.io/docs/latest/commands/xrevrange) allow for the stream's entries. You can do this by\nprefixing the ID of the last-read pending entry with the `(` character that\ndenotes an open (exclusive) range, and proving it to the subsequent call to the\ncommand."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n* [Array reply](../../develop/reference/protocol-spec#arrays): different data depending on the way XPENDING is called, as explained on this page.\n\n**RESP3:**\n\n* [Array reply](../../develop/reference/protocol-spec#arrays): different data depending on the way XPENDING is called, as explained on this page."}],"examples":[{"id":"summary-form-of-xpending-ex0","language":"plaintext","code":"> XGROUP CREATE mystream group55 0-0\nOK\n\n> XREADGROUP GROUP group55 consumer-123 COUNT 1 STREAMS mystream >\n1) 1) \"mystream\"\n   2) 1) 1) 1526984818136-0\n         2) 1) \"duration\"\n            2) \"1532\"\n            3) \"event-id\"\n            4) \"5\"\n            5) \"user-id\"\n            6) \"7782813\"","section_id":"summary-form-of-xpending"},{"id":"summary-form-of-xpending-ex1","language":"plaintext","code":"> XPENDING mystream group55\n1) (integer) 1\n2) 1526984818136-0\n3) 1526984818136-0\n4) 1) 1) \"consumer-123\"\n      2) \"1\"","section_id":"summary-form-of-xpending"},{"id":"extended-form-of-xpending-ex0","language":"plaintext","code":"> XPENDING mystream group55 - + 10\n1) 1) 1526984818136-0\n   2) \"consumer-123\"\n   3) (integer) 196415\n   4) (integer) 1","section_id":"extended-form-of-xpending"},{"id":"extended-form-of-xpending-ex1","language":"plaintext","code":"> XPENDING mystream group55 - + 10 consumer-123","section_id":"extended-form-of-xpending"},{"id":"idle-time-filter-ex0","language":"plaintext","code":"> XPENDING mystream group55 IDLE 9000 - + 10\n> XPENDING mystream group55 IDLE 9000 - + 10 consumer-123","section_id":"idle-time-filter"}]}
{"schema_version":2,"id":"commands/xrange","title":"XRANGE","url":"https://redis.io/docs/latest/commands/xrange/","summary":"Returns the messages from a stream within a range of IDs.","since":"5.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:25+01:00","page_type":"content","content_hash":"39794072a6d7bbb32ab8f9e039f2284565cd8b2bab2c353f86ec8a1b465cdd0a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The command returns the stream entries matching a given range of IDs.\nThe range is specified by a minimum and maximum ID. All the entries having\nan ID between the two specified or exactly one of the two IDs specified\n(closed interval) are returned.\n\nThe `XRANGE` command has a number of applications:\n\n* Returning items in a specific time range. This is possible because\n  Stream IDs are [related to time](https://redis.io/docs/latest/develop/data-types/streams).\n* Iterating a stream incrementally, returning just\n  a few items at every iteration. However it is semantically much more\n  robust than the [`SCAN`](https://redis.io/docs/latest/commands/scan) family of functions.\n* Fetching a single entry from a stream, providing the ID of the entry\n  to fetch two times: as start and end of the query interval.\n\nThe command also has a reciprocal command returning items in the\nreverse order, called [`XREVRANGE`](https://redis.io/docs/latest/commands/xrevrange), which is otherwise identical."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe stream key.\n\n</details>\n\n<details open><summary><code>start</code></summary>\n\nThe start of the ID range, inclusive. Use `-` for the smallest ID.\n\n</details>\n\n<details open><summary><code>end</code></summary>\n\nThe end of the ID range, inclusive. Use `+` for the greatest ID.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>COUNT count</code></summary>\n\nThe maximum number of entries to return.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> XADD writers * name Virginia surname Woolf\n\"1784722084364-0\"\nredis> XADD writers * name Jane surname Austen\n\"1784722084364-1\"\nredis> XADD writers * name Toni surname Morrison\n\"1784722084365-0\"\nredis> XADD writers * name Agatha surname Christie\n\"1784722084366-0\"\nredis> XADD writers * name Ngozi surname Adichie\n\"1784722084366-1\"\nredis> XLEN writers\n(integer) 5\nredis> XRANGE writers - + COUNT 2\n1) 1) \"1784722084364-0\"\n   2) 1) \"name\"\n      2) \"Virginia\"\n      3) \"surname\"\n      4) \"Woolf\"\n2) 1) \"1784722084364-1\"\n   2) 1) \"name\"\n      2) \"Jane\"\n      3) \"surname\"\n      4) \"Austen\""},{"id":"details","title":"Details","role":"content","text":""},{"id":"--and--special-ids","title":"`-` and `+` special IDs","role":"content","text":"The `-` and `+` special IDs mean respectively the minimum ID possible\nand the maximum ID possible inside a stream, so the following command\nwill just return every entry in the stream:\n\n[code example]\n\nThe `-` and `+` special IDs mean, respectively, the minimal and maximal range IDs,\nhowever they are nicer to type."},{"id":"incomplete-ids","title":"Incomplete IDs","role":"content","text":"Stream IDs are composed of two parts, a Unix millisecond time stamp and a\nsequence number for entries inserted in the same millisecond. It is possible\nto use `XRANGE` specifying just the first part of the ID, the millisecond time,\nlike in the following example:\n\n[code example]\n\nIn this case, `XRANGE` will auto-complete the start interval with `-0`\nand end interval with `-18446744073709551615`, in order to return all the\nentries that were generated between a given millisecond and the end of\nthe other specified millisecond. This also means that repeating the same\nmillisecond two times, we get all the entries within such millisecond,\nbecause the sequence number range will be from zero to the maximum.\n\nUsed in this way `XRANGE` works as a range query command to obtain entries\nin a specified time. This is very handy in order to access the history\nof past events in a stream."},{"id":"exclusive-ranges","title":"Exclusive ranges","role":"content","text":"The range is close (inclusive) by default, meaning that the reply can include\nentries with IDs matching the query's start and end intervals. It is possible\nto specify an open interval (exclusive) by prefixing the ID with the\ncharacter `(`. This is useful for iterating the stream, as explained below."},{"id":"returning-a-maximum-number-of-entries","title":"Returning a maximum number of entries","role":"returns","text":"Using the **COUNT** option it is possible to reduce the number of entries\nreported. This is a very important feature even if it may look marginal,\nbecause it allows, for instance, to model operations such as *give me\nthe entry greater or equal to the following*:\n\n[code example]\n\nIn the above case the entry `1526985054069-0` exists, otherwise the server\nwould have sent us the next one. Using `COUNT` is also the base in order to\nuse `XRANGE` as an iterator."},{"id":"iterating-a-stream","title":"Iterating a stream","role":"content","text":"In order to iterate a stream, we can proceed as follows. Let's assume that\nwe want two elements per iteration. We start fetching the first two\nelements, which is trivial:\n\n[code example]\n\nThen instead of starting the iteration again from `-`, as the start\nof the range we use the entry ID of the *last* entry returned by the\nprevious `XRANGE` call as an exclusive interval.\n\nThe ID of the last entry is `1526985685298-0`, so we just prefix it\nwith a '(', and continue our iteration:\n\n[code example]\n\nAnd so forth. Eventually this will allow to visit all the entries in the\nstream. Obviously, we can start the iteration from any ID, or even from\na specific time, by providing a given incomplete start ID. Moreover, we\ncan limit the iteration to a given ID or time, by providing an end\nID or incomplete ID instead of `+`.\n\nThe command [`XREAD`](https://redis.io/docs/latest/commands/xread) is also able to iterate the stream.\nThe command [`XREVRANGE`](https://redis.io/docs/latest/commands/xrevrange) can iterate the stream reverse, from higher IDs\n(or times) to lower IDs (or times).\n\n#### Iterating with earlier versions of Redis\n\nWhile exclusive range intervals are only available from Redis 6.2, it is still\npossible to use a similar stream iteration pattern with earlier versions. You\nstart fetching from the stream the same way as described above to obtain the\nfirst entries.\n\nFor the subsequent calls, you'll need to programmatically advance the last\nentry's ID returned. Most Redis client should abstract this detail, but the\nimplementation can also be in the application if needed. In the example above,\nthis means incrementing the sequence of `1526985685298-0` by one, from 0 to 1.\nThe second call would, therefore, be:\n\n[code example]\n\nAlso, note that once the sequence part of the last ID equals \n18446744073709551615, you'll need to increment the timestamp and reset the\nsequence part to 0. For example, incrementing the ID\n`1526985685298-18446744073709551615` should result in `1526985685299-0`.\n\nA symmetrical pattern applies to iterating the stream with [`XREVRANGE`](https://redis.io/docs/latest/commands/xrevrange). The\nonly difference is that the client needs to decrement the ID for the subsequent\ncalls. When decrementing an ID with a sequence part of 0, the timestamp needs\nto be decremented by 1 and the sequence set to 18446744073709551615."},{"id":"fetching-single-items","title":"Fetching single items","role":"content","text":"If you look for an `XGET` command you'll be disappointed because `XRANGE`\nis effectively the way to go in order to fetch a single entry from a\nstream. All you have to do is to specify the ID two times in the arguments\nof XRANGE:\n\n[code example]"},{"id":"additional-information-about-streams","title":"Additional information about streams","role":"content","text":"For further information about Redis streams please see\n[Introduction to Redis Streams](https://redis.io/docs/latest/develop/data-types/streams)."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of stream entries with IDs matching the specified range.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of stream entries with IDs matching the specified range."}],"examples":[{"id":"--and--special-ids-ex0","language":"plaintext","code":"> XRANGE somestream - +\n1) 1) 1526985054069-0\n   2) 1) \"duration\"\n      2) \"72\"\n      3) \"event-id\"\n      4) \"9\"\n      5) \"user-id\"\n      6) \"839248\"\n2) 1) 1526985069902-0\n   2) 1) \"duration\"\n      2) \"415\"\n      3) \"event-id\"\n      4) \"2\"\n      5) \"user-id\"\n      6) \"772213\"\n... other entries here ...","section_id":"--and--special-ids"},{"id":"incomplete-ids-ex0","language":"plaintext","code":"> XRANGE somestream 1526985054069 1526985055069","section_id":"incomplete-ids"},{"id":"returning-a-maximum-number-of-entries-ex0","language":"plaintext","code":"> XRANGE somestream 1526985054069-0 + COUNT 1\n1) 1) 1526985054069-0\n   2) 1) \"duration\"\n      2) \"72\"\n      3) \"event-id\"\n      4) \"9\"\n      5) \"user-id\"\n      6) \"839248\"","section_id":"returning-a-maximum-number-of-entries"},{"id":"iterating-a-stream-ex0","language":"plaintext","code":"> XRANGE writers - + COUNT 2\n1) 1) 1526985676425-0\n   2) 1) \"name\"\n      2) \"Virginia\"\n      3) \"surname\"\n      4) \"Woolf\"\n2) 1) 1526985685298-0\n   2) 1) \"name\"\n      2) \"Jane\"\n      3) \"surname\"\n      4) \"Austen\"","section_id":"iterating-a-stream"},{"id":"iterating-a-stream-ex1","language":"plaintext","code":"> XRANGE writers (1526985685298-0 + COUNT 2\n1) 1) 1526985691746-0\n   2) 1) \"name\"\n      2) \"Toni\"\n      3) \"surname\"\n      4) \"Morrison\"\n2) 1) 1526985712947-0\n   2) 1) \"name\"\n      2) \"Agatha\"\n      3) \"surname\"\n      4) \"Christie\"","section_id":"iterating-a-stream"},{"id":"iterating-a-stream-ex2","language":"plaintext","code":"> XRANGE writers 1526985685298-1 + COUNT 2\n1) 1) 1526985691746-0\n   2) 1) \"name\"\n      2) \"Toni\"\n...","section_id":"iterating-a-stream"},{"id":"fetching-single-items-ex0","language":"plaintext","code":"> XRANGE mystream 1526984818136-0 1526984818136-0\n1) 1) 1526984818136-0\n   2) 1) \"duration\"\n      2) \"1532\"\n      3) \"event-id\"\n      4) \"5\"\n      5) \"user-id\"\n      6) \"7782813\"","section_id":"fetching-single-items"}]}
{"schema_version":2,"id":"commands/xread","title":"XREAD","url":"https://redis.io/docs/latest/commands/xread/","summary":"Returns messages from multiple streams with IDs greater than the ones requested. Blocks until a message is available otherwise.","since":"5.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:25+01:00","page_type":"content","content_hash":"156210e4f503a9881a760897677247b2937ffce3bca849dc9350458f9d33f871","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nRead data from one or more streams, returning only entries with an\nID greater than the last received ID reported by the caller.\nThis command has an option to block if items are not available, in a similar\nfashion to [`BRPOP`](https://redis.io/docs/latest/commands/brpop) or [`BZPOPMIN`](https://redis.io/docs/latest/commands/bzpopmin) and others.\n\nIf you are new to streams, see [Introduction to Redis Streams](https://redis.io/docs/latest/develop/data-types/streams)."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>STREAMS key [key ...] id [id ...]</code></summary>\n\nThe keys to read from, followed by an ID for each key. For each stream, entries with an ID greater than the given one are returned. Use `$` to read only entries added after the call.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>COUNT count</code></summary>\n\nThe maximum number of entries to return per stream.\n\n</details>\n\n<details open><summary><code>MAXCOUNT maxcount</code></summary>\n\nAdded in Redis 8.10. The maximum number of entries to return in total across all streams named in the command. Unlike `COUNT`, which limits entries on a per-stream basis, `MAXCOUNT` applies a single cumulative budget for the whole command. It must be a positive integer, and it must be greater than or equal to `COUNT` when both are given. When `COUNT` is omitted, `MAXCOUNT` alone bounds the total. See [The MAXCOUNT and MAXSIZE options](#the-maxcount-and-maxsize-options) for details.\n\n</details>\n\n<details open><summary><code>MAXSIZE maxsize</code></summary>\n\nAdded in Redis 8.10. The maximum size, in bytes, of the reply across all streams named in the command. It must be a positive integer. At least one entry is always returned, so a single entry larger than `MAXSIZE` is still returned rather than yielding an empty reply. See [The MAXCOUNT and MAXSIZE options](#the-maxcount-and-maxsize-options) for details.\n\n</details>\n\n<details open><summary><code>BLOCK milliseconds</code></summary>\n\nBlock for up to this many milliseconds if no entries are available. `0` blocks indefinitely.\n\n</details>"},{"id":"details","title":"Details","role":"content","text":""},{"id":"non-blocking-usage","title":"Non-blocking usage","role":"content","text":"If the **BLOCK** option is not used, the command is synchronous, and can\nbe considered somewhat related to [`XRANGE`](https://redis.io/docs/latest/commands/xrange): it will return a range of items\ninside streams, however it has two fundamental differences compared to [`XRANGE`](https://redis.io/docs/latest/commands/xrange)\neven if we just consider the synchronous usage:\n\n* This command can be called with multiple streams if we want to read at\n  the same time from a number of keys. This is a key feature of `XREAD` because\n  especially when blocking with **BLOCK**, to be able to listen with a single\n  connection to multiple keys is a vital feature.\n* While [`XRANGE`](https://redis.io/docs/latest/commands/xrange) returns items in a range of IDs, `XREAD` is more suited in\n  order to consume the stream starting from the first entry which is greater\n  than any other entry we saw so far. So what we pass to `XREAD` is, for each\n  stream, the ID of the last element that we received from that stream.\n\nFor example, if I have two streams `mystream` and `writers`, and I want to\nread data from both the streams starting from the first element they contain,\nI could call `XREAD` like in the following example.\n\nNote: we use the `COUNT` option in the example, so that for each stream\nthe call will return at maximum two elements per stream.\n\n[code example]\n\nThe **STREAMS** option is mandatory and MUST be the final option because\nsuch option gets a variable length of argument in the following format:\n\n    STREAMS key_1 key_2 key_3 ... key_N ID_1 ID_2 ID_3 ... ID_N\n\nSo we start with a list of keys, and later continue with all the associated\nIDs, representing *the last ID we received for that stream*, so that the\ncall will serve us only greater IDs from the same stream.\n\nFor instance in the above example, the last items that we received\nfor the stream `mystream` has ID `1526999352406-0`, while for the\nstream `writers` has the ID `1526985685298-0`.\n\nTo continue iterating the two streams I'll call:\n\n[code example]\n\nAnd so forth. Eventually, the call will not return any item, but just an\nempty array, then we know that there is nothing more to fetch from our\nstream (and we would have to retry the operation, hence this command\nalso supports a blocking mode)."},{"id":"incomplete-ids","title":"Incomplete IDs","role":"content","text":"To use incomplete IDs is valid, like it is valid for [`XRANGE`](https://redis.io/docs/latest/commands/xrange). However\nhere the sequence part of the ID, if missing, is always interpreted as\nzero, so the command:\n\n[code example]\n\nis exactly equivalent to\n\n[code example]"},{"id":"blocking-for-data","title":"Blocking for data","role":"content","text":"In its synchronous form, the command can get new data as long as there\nare more items available. However, at some point, we'll have to wait for\nproducers of data to use [`XADD`](https://redis.io/docs/latest/commands/xadd) to push new entries inside the streams\nwe are consuming. In order to avoid polling at a fixed or adaptive interval\nthe command is able to block if it could not return any data, according\nto the specified streams and IDs, and automatically unblock once one of\nthe requested keys accept data.\n\nIt is important to understand that this command *fans out* to all the\nclients that are waiting for the same range of IDs, so every consumer will\nget a copy of the data, unlike to what happens when blocking list pop\noperations are used.\n\nIn order to block, the **BLOCK** option is used, together with the number\nof milliseconds we want to block before timing out. Normally Redis blocking\ncommands take timeouts in seconds, however this command takes a millisecond\ntimeout, even if normally the server will have a timeout resolution near\nto 0.1 seconds. This time it is possible to block for a shorter time in\ncertain use cases, and if the server internals will improve over time, it is\npossible that the resolution of timeouts will improve.\n\nWhen the **BLOCK** command is passed, but there is data to return at\nleast in one of the streams passed, the command is executed synchronously\n*exactly like if the BLOCK option would be missing*.\n\nThis is an example of blocking invocation, where the command later returns\na null reply because the timeout has elapsed without new data arriving:\n\n[code example]"},{"id":"the-special--id","title":"The special `$` ID.","role":"content","text":"When blocking sometimes we want to receive just entries that are added\nto the stream via [`XADD`](https://redis.io/docs/latest/commands/xadd) starting from the moment we block. In such a case\nwe are not interested in the history of already added entries. For\nthis use case, we would have to check the stream top element ID, and use\nsuch ID in the `XREAD` command line. This is not clean and requires to\ncall other commands, so instead it is possible to use the special `$`\nID to signal the stream that we want only the new things.\n\nIt is **very important** to understand that you should use the `$`\nID only for the first call to `XREAD`. Later the ID should be the one\nof the last reported item in the stream, otherwise you could miss all\nthe entries that are added in between.\n\nThis is how a typical `XREAD` call looks like in the first iteration\nof a consumer willing to consume only new entries:\n\n[code example]\n\nOnce we get some replies, the next call will be something like:\n\n[code example]\n\nAnd so forth."},{"id":"the-special--id-1","title":"The special `+` ID","role":"content","text":"You can read the last entry in a single stream easily using the `XREVRANGE` command, like so:\n\n[code example]\nBut this approach becomes slow as you add more streams because you must issue a separate command for each stream.\nInstead, starting from Redis 7.4, you can use the `+` sign as a special ID.\nThis requests the last available entry in a stream. For example:\n\n[code example]\n\nNote that when using this special ID for a stream, the `COUNT` option will\nbe ignored (for the specific stream) since only the last entry can be returned."},{"id":"the-maxcount-and-maxsize-options","title":"The MAXCOUNT and MAXSIZE options","role":"content","text":"Added in Redis 8.10, the `MAXCOUNT` and `MAXSIZE` options cap the *cumulative*\nreply across all streams named in a single command. This is different from\n`COUNT`, which limits the number of entries returned on a *per-stream* basis.\nBecause `XREAD` accepts multiple streams in one call, a read over N streams with\n`COUNT C` can return up to `N * C` entries, and the total reply size is otherwise\nunbounded. `MAXCOUNT` and `MAXSIZE` give clients a reliable way to bound the\nwork and memory of a single multi-stream read.\n\n* `MAXCOUNT` caps the total number of entries returned across all streams. It\n  must be a positive integer, and it must be greater than or equal to `COUNT`\n  when both are given (since it is a cumulative cap over a per-stream limit).\n  When `COUNT` is omitted, `MAXCOUNT` alone bounds the total, filling from\n  the first stream onward.\n* `MAXSIZE` caps the total reply size in bytes. It must be a positive integer.\n* At least one entry is always returned. The byte budget is never enforced\n  until at least one entry has been emitted across the whole reply, so a single\n  entry larger than `MAXSIZE` is still returned rather than yielding an empty\n  reply.\n* When both options are given, whichever bound is reached first wins.\n\nGiven three streams, each with 100 entries:\n\n[code example]\n\nA single oversized entry is always returned, even when it exceeds `MAXSIZE`:\n\n[code example]"},{"id":"how-multiple-clients-blocked-on-a-single-stream-are-served","title":"How multiple clients blocked on a single stream are served","role":"content","text":"Blocking list operations on lists or sorted sets have a *pop* behavior.\nBasically, the element is removed from the list or sorted set in order\nto be returned to the client. In this scenario you want the items\nto be consumed in a fair way, depending on the moment clients blocked\non a given key arrived. Normally Redis uses the FIFO semantics in this\nuse cases.\n\nHowever note that with streams this is not a problem: stream entries\nare not removed from the stream when clients are served, so every\nclient waiting will be served as soon as an [`XADD`](https://redis.io/docs/latest/commands/xadd) command provides\ndata to the stream.\n\nReading the [Redis Streams introduction](https://redis.io/docs/latest/develop/data-types/streams) is highly\nsuggested in order to understand more about the streams overall behavior\nand semantics."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Array reply](../../develop/reference/protocol-spec#arrays): an array where each element is an array composed of a two elements containing the key name and the entries reported for that key. The entries reported are full stream entries, having IDs and the list of all the fields and values. Field and values are guaranteed to be reported in the same order they were added by `XADD`.\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): if the _BLOCK_ option is given and a timeout occurs, or if there is no stream that can be served.\n\n**RESP3:**\n\nOne of the following:\n* [Map reply](../../develop/reference/protocol-spec#maps): A map of key-value elements where each element is composed of the key name and the entries reported for that key. The entries reported are full stream entries, having IDs and the list of all the fields and values. Field and values are guaranteed to be reported in the same order they were added by `XADD`.\n* [Null reply](../../develop/reference/protocol-spec#nulls): if the _BLOCK_ option is given and a timeout occurs, or if there is no stream that can be served."}],"examples":[{"id":"non-blocking-usage-ex0","language":"plaintext","code":"> XREAD COUNT 2 STREAMS mystream writers 0-0 0-0\n1) 1) \"mystream\"\n   2) 1) 1) 1526984818136-0\n         2) 1) \"duration\"\n            2) \"1532\"\n            3) \"event-id\"\n            4) \"5\"\n            5) \"user-id\"\n            6) \"7782813\"\n      2) 1) 1526999352406-0\n         2) 1) \"duration\"\n            2) \"812\"\n            3) \"event-id\"\n            4) \"9\"\n            5) \"user-id\"\n            6) \"388234\"\n2) 1) \"writers\"\n   2) 1) 1) 1526985676425-0\n         2) 1) \"name\"\n            2) \"Virginia\"\n            3) \"surname\"\n            4) \"Woolf\"\n      2) 1) 1526985685298-0\n         2) 1) \"name\"\n            2) \"Jane\"\n            3) \"surname\"\n            4) \"Austen\"","section_id":"non-blocking-usage"},{"id":"non-blocking-usage-ex1","language":"plaintext","code":"> XREAD COUNT 2 STREAMS mystream writers 1526999352406-0 1526985685298-0\n1) 1) \"mystream\"\n   2) 1) 1) 1526999626221-0\n         2) 1) \"duration\"\n            2) \"911\"\n            3) \"event-id\"\n            4) \"7\"\n            5) \"user-id\"\n            6) \"9488232\"\n2) 1) \"writers\"\n   2) 1) 1) 1526985691746-0\n         2) 1) \"name\"\n            2) \"Toni\"\n            3) \"surname\"\n            4) \"Morrison\"\n      2) 1) 1526985712947-0\n         2) 1) \"name\"\n            2) \"Agatha\"\n            3) \"surname\"\n            4) \"Christie\"","section_id":"non-blocking-usage"},{"id":"incomplete-ids-ex0","language":"plaintext","code":"> XREAD COUNT 2 STREAMS mystream writers 0 0","section_id":"incomplete-ids"},{"id":"incomplete-ids-ex1","language":"plaintext","code":"> XREAD COUNT 2 STREAMS mystream writers 0-0 0-0","section_id":"incomplete-ids"},{"id":"blocking-for-data-ex0","language":"plaintext","code":"> XREAD BLOCK 1000 STREAMS mystream 1526999626221-0\n(nil)","section_id":"blocking-for-data"},{"id":"the-special--id-ex0","language":"plaintext","code":"> XREAD BLOCK 5000 COUNT 100 STREAMS mystream $","section_id":"the-special--id"},{"id":"the-special--id-ex1","language":"plaintext","code":"> XREAD BLOCK 5000 COUNT 100 STREAMS mystream 1526999644174-3","section_id":"the-special--id"},{"id":"the-special--id-1-ex0","language":"plaintext","code":"> XREVRANGE stream + - COUNT 1","section_id":"the-special--id-1"},{"id":"the-special--id-1-ex1","language":"plaintext","code":"> XREAD STREAMS streamA streamB streamC streamD + + + +","section_id":"the-special--id-1"},{"id":"the-maxcount-and-maxsize-options-ex0","language":"plaintext","code":"> XREAD COUNT 50 STREAMS s1 s2 s3 0 0 0\n# returns 150 entries total (50 per stream)\n\n> XREAD COUNT 50 MAXCOUNT 80 STREAMS s1 s2 s3 0 0 0\n# returns 80 entries total — capped across all streams\n\n> XREAD MAXCOUNT 7 STREAMS s1 s2 s3 0 0 0\n# returns 7 entries total, all from s1\n\n> XREAD MAXCOUNT 5 MAXSIZE 100000 STREAMS s1 s2 s3 0 0 0\n# returns 5 entries (MAXCOUNT is the tighter bound)","section_id":"the-maxcount-and-maxsize-options"},{"id":"the-maxcount-and-maxsize-options-ex1","language":"plaintext","code":"> XADD bigstream 1-1 f <5000-byte value>\n> XREAD MAXSIZE 50 STREAMS bigstream 0\n1) 1) \"bigstream\"\n   2) 1) 1) \"1-1\"\n         2) 1) \"f\"\n            2) \"<5000-byte value>\"\n# the single entry exceeds MAXSIZE but is still returned","section_id":"the-maxcount-and-maxsize-options"}]}
{"schema_version":2,"id":"commands/xreadgroup","title":"XREADGROUP","url":"https://redis.io/docs/latest/commands/xreadgroup/","summary":"Returns new or historical messages from a stream for a consumer in a group. Blocks until a message is available otherwise.","since":"5.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:45:25+01:00","page_type":"content","content_hash":"1733213a2c78687a32bd4777a8d2b42399ca8dd78612e384e02e16f319ee1408","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nThe `XREADGROUP` command is a special version of the [`XREAD`](https://redis.io/docs/latest/commands/xread) command\nwith support for consumer groups. See \n[`XREAD`](https://redis.io/docs/latest/commands/xread) command before reading this page.\n\nIf you are new to streams, see \n[Introduction to Redis Streams](https://redis.io/docs/latest/develop/data-types/streams).\nMake sure you understand consumer groups in the introduction."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>GROUP group consumer</code></summary>\n\nThe consumer group and the consumer name to read as.\n\n</details>\n\n<details open><summary><code>STREAMS key [key ...] id [id ...]</code></summary>\n\nThe keys to read from, followed by an ID for each key. Use `>` to read messages never delivered to any other consumer, or a specific ID to read from this consumer's pending list.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>COUNT count</code></summary>\n\nThe maximum number of entries to return per stream.\n\n</details>\n\n<details open><summary><code>MAXCOUNT maxcount</code></summary>\n\nAdded in Redis 8.10. The maximum number of entries to return in total across all streams named in the command. Unlike `COUNT`, which limits entries on a per-stream basis, `MAXCOUNT` applies a single cumulative budget for the whole command. It must be a positive integer, and it must be greater than or equal to `COUNT` when both are given. When `COUNT` is omitted, `MAXCOUNT` alone bounds the total. It works for both new (`>`) reads and history/pending entries list (PEL) reads. See [The MAXCOUNT and MAXSIZE options](#the-maxcount-and-maxsize-options) for details.\n\n</details>\n\n<details open><summary><code>MAXSIZE maxsize</code></summary>\n\nAdded in Redis 8.10. The maximum size, in bytes, of the reply across all streams named in the command. It must be a positive integer. At least one entry is always returned, so a single entry larger than `MAXSIZE` is still returned rather than yielding an empty reply. For `MAXSIZE`, the limit is checked before delivering the next new or PEL entry, so a skipped entry is neither sent nor added to the consumer's PEL. See [The MAXCOUNT and MAXSIZE options](#the-maxcount-and-maxsize-options) for details.\n\n</details>\n\n<details open><summary><code>BLOCK milliseconds</code></summary>\n\nBlock for up to this many milliseconds if no entries are available. `0` blocks indefinitely.\n\n</details>\n\n<details open><summary><code>CLAIM min-idle-time</code></summary>\n\nClaim messages that have been idle for at least this long (in milliseconds) before reading.\n\n</details>\n\n<details open><summary><code>NOACK</code></summary>\n\nDo not add the read messages to the PEL; treat them as acknowledged immediately.\n\n</details>"},{"id":"details","title":"Details","role":"content","text":""},{"id":"consumer-groups-in-30-seconds","title":"Consumer groups in 30 seconds","role":"content","text":"The difference between this command and the vanilla [`XREAD`](https://redis.io/docs/latest/commands/xread) is that this\none supports consumer groups.\n\nWithout consumer groups, just using [`XREAD`](https://redis.io/docs/latest/commands/xread), all the clients are served with all the entries arriving in a stream. Instead using consumer groups with `XREADGROUP`, it is possible to create groups of clients that consume different parts of the messages arriving in a given stream. If, for instance, the stream gets the new entries A, B, and C and there are two consumers reading via a consumer group, one client will get, for instance, the messages A and C, and the other the message B, and so forth.\n\nWithin a consumer group, a given consumer (that is, just a client consuming messages from the stream), has to identify with a unique *consumer name*. Which is just a string.\n\nOne of the guarantees of consumer groups is that a given consumer can only see the history of messages that were delivered to it, so a message has just a single owner. However there is a special feature called *message claiming* that allows other consumers to claim messages in case there is a non recoverable failure of some consumer. In order to implement such semantics, consumer groups require explicit acknowledgment of the messages successfully processed by the consumer, via the [`XACK`](https://redis.io/docs/latest/commands/xack) command. This is needed because the stream will track, for each consumer group, who is processing what message.\n\nThis is how to understand if you want to use a consumer group or not:\n\n1. If you have a stream and multiple clients, and you want all the clients to get all the messages, you do not need a consumer group.\n2. If you have a stream and multiple clients, and you want the stream to be *partitioned* or *sharded* across your clients, so that each client will get a sub set of the messages arriving in a stream, you need a consumer group."},{"id":"differences-between-xread-and-xreadgroup","title":"Differences between XREAD and XREADGROUP","role":"content","text":"From the point of view of the syntax, the commands are almost the same,\nhowever `XREADGROUP` *requires* a special and mandatory option:\n\n    GROUP `group-name` `consumer-name`\n\nThe group name is just the name of a consumer group associated to the stream.\nThe group is created using the [`XGROUP`](https://redis.io/docs/latest/commands/xgroup) command. The consumer name is the\nstring that is used by the client to identify itself inside the group.\nThe consumer is auto created inside the consumer group the first time it\nis seen. Different clients should select a different consumer name.\n\nWhen you read with `XREADGROUP`, the server will *remember* that a given\nmessage was delivered to you: the message will be stored inside the\nconsumer group in what is called a Pending Entries List (PEL), that is\na list of message IDs delivered but not yet acknowledged.\n\nThe client will have to acknowledge the message processing using [`XACK`](https://redis.io/docs/latest/commands/xack)\nin order for the pending entry to be removed from the PEL. The PEL\ncan be inspected using the [`XPENDING`](https://redis.io/docs/latest/commands/xpending) command.\n\nThe `NOACK` subcommand can be used to avoid adding the message to the PEL in\ncases where reliability is not a requirement and the occasional message loss\nis acceptable. This is equivalent to acknowledging the message when it is read.\nWhen used together with `CLAIM`, `NOACK` does not apply for retrieved pending entries.\n\nThe ID to specify in the **STREAMS** option when using `XREADGROUP` can\nbe one of the following two:\n\n* 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.\n* 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.\n* 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.\n\nLike [`XREAD`](https://redis.io/docs/latest/commands/xread) the `XREADGROUP` command can be used in a blocking way. There\nare no differences in this regard."},{"id":"the-claim-option","title":"The CLAIM option","role":"content","text":"The `CLAIM` option was added as part of Redis 8.4. 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).\n\nIf there are no such messages, Redis will continue as normal (consume incoming messages).\n\n`CLAIM min-idle-time` is ignored if the specified id is not `>`.\n\nMessages that have been released back to the group using [`XNACK`](https://redis.io/docs/latest/commands/xnack), added in Redis 8.8, are immediately claimable since their delivery time is set to 0, satisfying any minimum idle time requirement.\n\n#### Reply extension for claimed entries\n\nWhen `CLAIM min-idle-time` is used, additional information is provided for each pending entry retrieved (similar to the reply of [`XPENDING`](https://redis.io/docs/latest/commands/xpending)). For each claimed pending entry, the reply includes:\n\n1. Entry id\n2. Field-value pairs\n3. Milliseconds since the last time this entry was delivered\n4. Number of times this entry was previously delivered\n\n#### Ordering guarantees\n\nWithin each stream, entries are reported in the same order they were added by `XADD` (older first).\n\nWhen not blocked, across streams, entries are reported in the order the streams were specified.\n\nWhen using `CLAIM`, the following ordering guarantees apply per stream:\n\n- Idle pending entries are reported first, then incoming entries\n- Among pending entries, messages released via [`XNACK`](https://redis.io/docs/latest/commands/xnack) are prioritized and reported first\n- Other idle pending entries are ordered by idle time (longer first)\n- Incoming entries are reported in the order they were added by `XADD` (older first)\n\nFor example, if there are 20 idle pending entries and 200 incoming entries (in all the specified streams together):\n- When calling `XREADGROUP ... CLAIM ...`, you would retrieve 220 entries in the reply\n- When calling `XREADGROUP ... COUNT 100 ... CLAIM ...`, you would retrieve the 20 idle pending entries + 80 incoming entries in the reply"},{"id":"the-maxcount-and-maxsize-options","title":"The MAXCOUNT and MAXSIZE options","role":"content","text":"Added in Redis 8.10, the `MAXCOUNT` and `MAXSIZE` options cap the *cumulative*\nreply across all streams named in a single command. This is different from\n`COUNT`, which limits the number of entries returned on a *per-stream* basis.\nBecause `XREADGROUP` accepts multiple streams in one call, a read over N streams\nwith `COUNT C` can return up to `N * C` entries, and the total reply size is\notherwise unbounded. `MAXCOUNT` and `MAXSIZE` give clients a reliable way to\nbound the work and memory of a single multi-stream read.\n\n* `MAXCOUNT` caps the total number of entries returned across all streams. It\n  must be a positive integer, and it must be greater than or equal to `COUNT`\n  when both are given (since it is a cumulative cap over a per-stream limit).\n  When `COUNT` is omitted, `MAXCOUNT` alone bounds the total, filling from\n  the first stream onward.\n* `MAXSIZE` caps the total reply size in bytes. It must be a positive integer.\n* At least one entry is always returned. The byte budget is never enforced\n  until at least one entry has been emitted across the whole reply, so a single\n  entry larger than `MAXSIZE` is still returned rather than yielding an empty\n  reply.\n* When both options are given, whichever bound is reached first wins.\n\nBoth caps apply to new (`>`) reads and to history/PEL reads, and they are honored\nafter a blocking client is unblocked. For `MAXSIZE`, the limit is checked\n*before* delivering the next new or PEL entry, so a skipped entry is neither sent\nnor added to the consumer's PEL.\n\nGiven three streams, each with 100 entries that the consumer reads as new\nmessages:\n\n[code example]"},{"id":"what-happens-when-a-message-is-delivered-to-a-consumer","title":"What happens when a message is delivered to a consumer?","role":"content","text":"Two things:\n\n1. If the message was never delivered to anyone, that is, if we are talking about a new message, then a PEL (Pending Entries List) is created.\n2. If instead the message was already delivered to this consumer, and it is just re-fetching the same message again, then the *last delivery counter* is updated to the current time, and the *number of deliveries* is incremented by one. You can access those message properties using the [`XPENDING`](https://redis.io/docs/latest/commands/xpending) command."},{"id":"usage-example","title":"Usage example","role":"syntax","text":"Normally you use the command like that in order to get new messages and\nprocess them. In pseudo-code:\n\n[code example]\n\nIn this way the example consumer code will fetch only new messages, process\nthem, and acknowledge them via [`XACK`](https://redis.io/docs/latest/commands/xack). However the example code above is\nnot complete, because it does not handle recovering after a crash. What\nwill happen if we crash in the middle of processing messages, is that our\nmessages will remain in the pending entries list, so we can access our\nhistory by giving `XREADGROUP` initially an ID of 0, and performing the same\nloop. Once providing an ID of 0 the reply is an empty set of messages, we\nknow that we processed and acknowledged all the pending messages: we\ncan start to use `>` as ID, in order to get the new messages and rejoin the\nconsumers that are processing new things.\n\nTo see how the command actually replies, please check the [`XREAD`](https://redis.io/docs/latest/commands/xread) command page."},{"id":"what-happens-when-a-pending-message-is-deleted","title":"What happens when a pending message is deleted?","role":"content","text":"Entries may be deleted from the stream due to trimming with [`XADD`](https://redis.io/docs/latest/commands/xadd) or [`XTRIM`](https://redis.io/docs/latest/commands/xtrim),\nor explicit calls to [`XDEL`](https://redis.io/docs/latest/commands/xdel), [`XDELEX`](https://redis.io/docs/latest/commands/xdelex), or [`XACKDEL`](https://redis.io/docs/latest/commands/xackdel).\n\nWhen an entry is trimmed with [`XADD`](https://redis.io/docs/latest/commands/xadd) or [`XTRIM`](https://redis.io/docs/latest/commands/xtrim) and `DELREF` or `ACKED` are not specified,\ndeleted with [`XDEL`](https://redis.io/docs/latest/commands/xdel), or deleted with [`XDELEX`](https://redis.io/docs/latest/commands/xdelex) or [`XACKDEL`](https://redis.io/docs/latest/commands/xackdel) \nand `DELREF` or `ACKED` are not specified, Redis doesn't prevent the deletion of entries that are present in the stream's PELs.\nWhen this happens, the PELs retain the deleted entries' IDs, but the actual entry payload is no longer available.\nTherefore, when reading such PEL entries, Redis will return a null value in place of their respective data.\n\nExample:\n\n[code example]\n\nSee [Introduction to Redis streams](https://redis.io/docs/latest/develop/data-types/streams) to understand more about the overall behavior of Redis streams.\nand semantics."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Array reply](../../develop/reference/protocol-spec#arrays): an array where each element is an array composed of a two elements containing the key name and the entries reported for that key. The entries reported are full stream entries, having IDs and the list of all the fields and values. Field and values are guaranteed to be reported in the same order they were added by `XADD`.\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): if the _BLOCK_ option is given and a timeout occurs, or if there is no stream that can be served.\n\n**RESP3:**\n\nOne of the following:\n* [Map reply](../../develop/reference/protocol-spec#maps): A map of key-value elements where each element is composed of the key name and the entries reported for that key. The entries reported are full stream entries, having IDs and the list of all the fields and values. Field and values are guaranteed to be reported in the same order they were added by `XADD`.\n* [Null reply](../../develop/reference/protocol-spec#nulls): if the _BLOCK_ option is given and a timeout occurs, or if there is no stream that can be served."}],"examples":[{"id":"the-maxcount-and-maxsize-options-ex0","language":"plaintext","code":"> XREADGROUP GROUP g c COUNT 50 STREAMS s1 s2 s3 > > >\n# returns 150 entries total (50 per stream)\n\n> XREADGROUP GROUP g c COUNT 50 MAXCOUNT 80 STREAMS s1 s2 s3 > > >\n# returns 80 entries total — capped across all streams\n\n> XREADGROUP GROUP g c MAXCOUNT 5 MAXSIZE 100000 STREAMS s1 s2 s3 > > >\n# returns 5 entries (MAXCOUNT is the tighter bound)","section_id":"the-maxcount-and-maxsize-options"},{"id":"usage-example-ex0","language":"plaintext","code":"WHILE true\n    entries = XREADGROUP GROUP $GroupName $ConsumerName BLOCK 2000 COUNT 10 STREAMS mystream >\n    if entries == nil\n        puts \"Timeout... try again\"\n        CONTINUE\n    end\n\n    FOREACH entries AS stream_entries\n        FOREACH stream_entries as message\n            process_message(message.id,message.fields)\n\n            # ACK the message as processed\n            XACK mystream $GroupName message.id\n        END\n    END\nEND","section_id":"usage-example"},{"id":"what-happens-when-a-pending-message-is-deleted-ex0","language":"plaintext","code":"> XADD mystream 1 myfield mydata\n\"1-0\"\n> XGROUP CREATE mystream mygroup 0\nOK\n> XREADGROUP GROUP mygroup myconsumer STREAMS mystream >\n1) 1) \"mystream\"\n   2) 1) 1) \"1-0\"\n         2) 1) \"myfield\"\n            2) \"mydata\"\n> XDEL mystream 1-0\n(integer) 1\n> XREADGROUP GROUP mygroup myconsumer STREAMS mystream 0\n1) 1) \"mystream\"\n   2) 1) 1) \"1-0\"\n         2) (nil)","section_id":"what-happens-when-a-pending-message-is-deleted"}]}
{"schema_version":2,"id":"commands/xrevrange","title":"XREVRANGE","url":"https://redis.io/docs/latest/commands/xrevrange/","summary":"Returns the messages from a stream within a range of IDs in reverse order.","since":"5.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"54a0ebb6c71fbf8d522d90bba0c3986d485b358901e8044f4b7f4b3b62435070","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This command works like XRANGE, but returns entries in reverse order. Specify the end ID first, then the start ID. XREVRANGE returns the entries between those IDs, inclusive, starting from the end of the range.\n\nSo for example, to get all the elements from the higher ID to the lower\nID you can use:\n\n    XREVRANGE somestream + -\n\nSimilarly to get just the last element added into the stream it is\nenough to send:\n\n    XREVRANGE somestream + - COUNT 1"},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe stream key.\n\n</details>\n\n<details open><summary><code>end</code></summary>\n\nThe end of the ID range, inclusive. Entries are returned in reverse order. Use `+` for the greatest ID.\n\n</details>\n\n<details open><summary><code>start</code></summary>\n\nThe start of the ID range, inclusive. Use `-` for the smallest ID.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>COUNT count</code></summary>\n\nThe maximum number of entries to return.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> XADD writers * name Virginia surname Woolf\n\"1784722084747-0\"\nredis> XADD writers * name Jane surname Austen\n\"1784722084747-1\"\nredis> XADD writers * name Toni surname Morrison\n\"1784722084748-0\"\nredis> XADD writers * name Agatha surname Christie\n\"1784722084748-1\"\nredis> XADD writers * name Ngozi surname Adichie\n\"1784722084748-2\"\nredis> XLEN writers\n(integer) 5\nredis> XREVRANGE writers + - COUNT 1\n1) 1) \"1784722084748-2\"\n   2) 1) \"name\"\n      2) \"Ngozi\"\n      3) \"surname\"\n      4) \"Adichie\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): The command returns the entries with IDs matching the specified range. The returned entries are complete, which means that the ID and all the fields they are composed of are returned. Moreover, the entries are returned with their fields and values in the same order as `XADD` added them.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): The command returns the entries with IDs matching the specified range. The returned entries are complete, which means that the ID and all the fields they are composed of are returned. Moreover, the entries are returned with their fields and values in the same order as `XADD` added them."}],"examples":[]}
{"schema_version":2,"id":"commands/xsetid","title":"XSETID","url":"https://redis.io/docs/latest/commands/xsetid/","summary":"An internal command for replicating stream values.","since":"5.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-06-29T16:21:31+03:00","page_type":"content","content_hash":"a57144f81fa0326585fe964c280d2c429489c8bc35b74e189935f0e256e9db1a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"`XSETID` is an internal command. Users should not call it directly.\n \nRedis primary shards use it to replicate the last delivered entry ID of the stream."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe stream key.\n\n</details>\n\n<details open><summary><code>last-id</code></summary>\n\nThe ID to set as the stream's last-generated ID.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>ENTRIESADDED entries-added</code></summary>\n\nSet the stream's entries-added counter.\n\n</details>\n\n<details open><summary><code>MAXDELETEDID max-deleted-id</code></summary>\n\nSet the stream's maximum deleted entry ID.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`.\n\n**RESP3:**\n\n[Simple string reply](../../develop/reference/protocol-spec#simple-strings): `OK`."}],"examples":[]}
{"schema_version":2,"id":"commands/xtrim","title":"XTRIM","url":"https://redis.io/docs/latest/commands/xtrim/","summary":"Deletes messages from the beginning of a stream.","since":"5.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"dbc9c7cbce34924a1ce991b1e7ca238db472e97b2da809e766ba223143f85cbb","sections":[{"id":"overview","title":"Overview","role":"overview","text":"`XTRIM` trims the stream by evicting older entries (entries with lower IDs) if needed."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open>\n<summary><code>key</code></summary>\n\nThe name of the stream key.\n</details>\n\n<details open>\n<summary><code>MAXLEN | MINID</code></summary>\n\nThe trimming strategy:\n- `MAXLEN`: Evicts entries as long as the stream's length exceeds the specified threshold\n- `MINID`: Evicts entries with IDs lower than the specified threshold (available since Redis 6.2.0)\n</details>\n\n<details open>\n<summary><code>threshold</code></summary>\n\nThe trimming threshold:\n- For `MAXLEN`: `threshold` is a non-negative integer specifying the maximum number of entries that may remain in the stream after trimming. Redis enforces this by removing the oldest entries - that is, the entries with the lowest stream IDs - so that only the newest entries are kept.\n- For `MINID`: `threshold` is a stream ID. All entries whose IDs are less than `threshold` are trimmed. All entries with IDs greater than or equal to `threshold` are kept.\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open>\n<summary><code>= | ~</code></summary>\n\nThe trimming operator:\n- `=`: Exact trimming (default) - trims to the exact threshold\n- `~`: Approximate trimming - more efficient, may leave slightly more entries than the threshold\n</details>\n\n<details open>\n<summary><code>LIMIT count</code></summary>\n\nLimits the number of entries to examine during trimming. Available since Redis 6.2.0. When not specified, Redis uses a default value of 100 * the number of entries in a macro node. Specifying 0 disables the limiting mechanism entirely.\n</details>\n\n<details open>\n<summary><code>KEEPREF | DELREF | ACKED</code></summary>\n\nSpecifies how to handle consumer group references when trimming. If there are no consumer groups, these arguments have no effect. Available since Redis 8.2.\n\nIf no option is specified, `KEEPREF` is used by default. Unlike the `XDELEX` and `XACKDEL` commands where one of these options is required, here they are optional to maintain backward compatibility:\n\n- `KEEPREF` (default): When trimming, removes entries from the stream according to the specified strategy (`MAXLEN` or `MINID`), regardless of whether they are referenced by any consumer groups, but preserves existing references to these entries in all consumer groups' PEL (Pending Entries List).\n- `DELREF`: When trimming, removes entries from the stream according to the specified strategy and also removes all references to these entries from all consumer groups' PEL.\n- `ACKED`: When trimming, only removes entries that were read and acknowledged by all consumer groups. Note that if the number of referenced entries is larger than `MAXLEN`, trimming will still stop at the limit.\n</details>\n\nFor example, this trims the stream to exactly the latest 1000 items:\n\n[code example]\n\nIn this example, Redis evicts all entries that have an ID lower than 649085820-0:\n\n[code example]\n\nBy default, or when you provide the optional `=` argument, the command performs exact trimming.\n\nDepending on the strategy, exact trimming means:\n\n* `MAXLEN`: The trimmed stream's length will be exactly the minimum between its original length and the specified `threshold`.\n* `MINID`: The oldest ID in the stream will be exactly the maximum between its original oldest ID and the specified `threshold`.\n\nNearly exact trimming\n---\n\nBecause exact trimming may require additional effort from the Redis server, you can provide the optional `~` argument to make it more efficient.\n\nFor example:\n\n[code example]\n\nThe `~` argument between the `MAXLEN` strategy and the `threshold` means that you are requesting to trim the stream so its length is **at least** the `threshold`, but possibly slightly more.\nIn this case, Redis stops trimming early when performance can be gained (for example, when a whole macro node in the data structure can't be removed).\nThis makes trimming much more efficient, and it is usually what you want, although after trimming, the stream may have a few tens of additional entries over the `threshold`.\n\nAnother way to control the amount of work done by the command when using `~` is the `LIMIT` clause.\nWhen you use it, it specifies the maximum `count` of entries that will be evicted.\nWhen you don't specify `LIMIT` and `count`, Redis implicitly uses the default value of 100 * the number of entries in a macro node as the `count`.\nSpecifying the value 0 as `count` disables the limiting mechanism entirely."},{"id":"examples","title":"Examples","role":"example","text":"redis> XADD mystream * field1 A field2 B field3 C field4 D\n\"1784722085136-0\"\nredis> XTRIM mystream MAXLEN 2\n(integer) 0\nredis> XRANGE mystream - +\n1) 1) \"1784722085136-0\"\n   2) 1) \"field1\"\n      2) \"A\"\n      3) \"field2\"\n      4) \"B\"\n      5) \"field3\"\n      6) \"C\"\n      7) \"field4\"\n      8) \"D\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): The number of entries deleted from the stream.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): The number of entries deleted from the stream."}],"examples":[{"id":"optional-arguments-ex0","language":"plaintext","code":"XTRIM mystream MAXLEN 1000","section_id":"optional-arguments"},{"id":"optional-arguments-ex1","language":"plaintext","code":"XTRIM mystream MINID 649085820","section_id":"optional-arguments"},{"id":"optional-arguments-ex2","language":"plaintext","code":"XTRIM mystream MAXLEN ~ 1000","section_id":"optional-arguments"}]}
{"schema_version":2,"id":"commands/zadd","title":"ZADD","url":"https://redis.io/docs/latest/commands/zadd/","summary":"Adds one or more members to a sorted set, or updates their scores. Creates the key if it doesn't exist.","since":"1.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:43+01:00","page_type":"content","content_hash":"bc053a650c8e372f0cfd691e310dfe71d2eafa00f41fab0f584e33fd868409d2","sections":[{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the sorted set.\n\n</details>\n\n<details open><summary><code>score member [score member ...]</code></summary>\n\nOne or more score-member pairs to add or update. `score` is a double-precision floating-point number.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"`NX` and `XX` are mutually exclusive, as are `GT` and `LT`.\n\n<details open><summary><code>NX</code></summary>\n\nOnly add new members; do not update existing members.\n\n</details>\n\n<details open><summary><code>XX</code></summary>\n\nOnly update existing members; do not add new members.\n\n</details>\n\n<details open><summary><code>GT</code></summary>\n\nOnly update an existing member if the new score is greater than the current score. Does not prevent adding new members.\n\n</details>\n\n<details open><summary><code>LT</code></summary>\n\nOnly update an existing member if the new score is less than the current score. Does not prevent adding new members.\n\n</details>\n\n<details open><summary><code>CH</code></summary>\n\nChange the return value from the number of added members to the number of changed members (added plus updated).\n\n</details>\n\n<details open><summary><code>INCR</code></summary>\n\nIncrement the member's score by `score` instead of setting it, behaving like `ZINCRBY`. Only one score-member pair may be given.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"Foundational: Add one or more members to a sorted set with scores using ZADD (creates sorted set if needed, updates scores if member exists)\n\n**Difficulty:** Beginner\n\n**Commands:** ZADD, ZRANGE\n\n**Complexity:**\n- ZADD: O(log(N)\n- ZRANGE: O(log(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"details","title":"Details","role":"content","text":""},{"id":"range-of-integer-scores-that-can-be-expressed-precisely","title":"Range of integer scores that can be expressed precisely","role":"content","text":"Redis sorted sets use a double 64-bit floating-point number to represent the score. This format precisely represents integers between `-(2^53)` and `+(2^53)`, inclusive. In practical terms, you can store integers from -9007199254740992 to 9007199254740992 without losing precision. Redis represents larger integers and fractions in exponential form internally, so you may get only an approximation of the score you set."},{"id":"sorted-sets-101","title":"Sorted sets 101","role":"content","text":"Sorted sets are sorted by their score in an ascending way.\nThe same element only exists a single time, no repeated elements are\npermitted. The score can be modified both by `ZADD` that will update the\nelement score, and as a side effect, its position on the sorted set, and\nby [`ZINCRBY`](https://redis.io/docs/latest/commands/zincrby) that can be used in order to update the score relatively to its\nprevious value.\n\nThe current score of an element can be retrieved using the [`ZSCORE`](https://redis.io/docs/latest/commands/zscore) command,\nthat can also be used to verify if an element already exists or not.\n\nFor an introduction to sorted sets, see the data types page on [sorted\nsets](https://redis.io/docs/latest/develop/data-types#sorted-sets)."},{"id":"elements-with-the-same-score","title":"Elements with the same score","role":"content","text":"While the same element can't be repeated in a sorted set since every element\nis unique, it is possible to add multiple different elements *having the same score*. When multiple elements have the same score, they are *ordered lexicographically* (they are still ordered by score as a first key, however, locally, all the elements with the same score are relatively ordered lexicographically).\n\nThe lexicographic ordering used is binary, it compares strings as array of bytes.\n\nIf the user inserts all the elements in a sorted set with the same score (for example 0), all the elements of the sorted set are sorted lexicographically, and range queries on elements are possible using the command [`ZRANGEBYLEX`](https://redis.io/docs/latest/commands/zrangebylex) (Note: it is also possible to query sorted sets by range of scores using [`ZRANGEBYSCORE`](https://redis.io/docs/latest/commands/zrangebyscore))."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nAny of the following:\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): if the operation was aborted because of a conflict with one of the _XX/NX/LT/GT_ options.\n* [Integer reply](../../develop/reference/protocol-spec#integers): the number of new members when the _CH_ option is not used.\n* [Integer reply](../../develop/reference/protocol-spec#integers): the number of new or updated members when the _CH_ option is used.\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the updated score of the member when the _INCR_ option is used.\n\n**RESP3:**\n\nAny of the following:\n* [Null reply](../../develop/reference/protocol-spec#nulls): if the operation was aborted because of a conflict with one of the _XX/NX/LT/GT_ options.\n* [Integer reply](../../develop/reference/protocol-spec#integers): the number of new members when the _CH_ option is not used.\n* [Integer reply](../../develop/reference/protocol-spec#integers): the number of new or updated members when the _CH_ option is used.\n* [Double reply](../../develop/reference/protocol-spec#doubles): the updated score of the member when the _INCR_ option is used."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> ZADD myzset 1 \"one\"\n(integer) 1\n> ZADD myzset 1 \"uno\"\n(integer) 1\n> ZADD myzset 2 \"two\" 3 \"three\"\n(integer) 2\n> ZRANGE myzset 0 -1 WITHSCORES\n1) \"one\"\n2) \"1\"\n3) \"uno\"\n4) \"1\"\n5) \"two\"\n6) \"2\"\n7) \"three\"\n8) \"3\"","section_id":"examples"},{"id":"examples-ex1","language":"csharp","code":"bool zAddResult1 = db.SortedSetAdd(\"myzset\", \"one\", 1);\n        Console.WriteLine(zAddResult1); // >>> True\n\n        bool zAddResult2 = db.SortedSetAdd(\"myzset\", \"uno\", 1);\n        Console.WriteLine(zAddResult2); // >>> True\n\n        long zAddResult3 = db.SortedSetAdd(\n            \"myzset\",\n            [\n                new(\"two\", 2),\n                new(\"three\", 3)\n            ]\n        );\n        Console.WriteLine(zAddResult3); // >>> 2\n\n        SortedSetEntry[] zAddResult4 = db.SortedSetRangeByRankWithScores(\"myzset\", 0, -1);\n        Console.WriteLine($\"{string.Join(\", \", zAddResult4.Select(b => $\"{b.Element}: {b.Score}\"))}\");\n        // >>> one: 1, uno: 1, two: 2, three: 3","section_id":"examples"},{"id":"examples-ex2","language":"go","code":"zAddResult1, err := rdb.ZAdd(ctx, \"myzset\",\n\t\tredis.Z{Member: \"one\", Score: 1},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(zAddResult1) // >>> 1\n\n\tzAddResult2, err := rdb.ZAdd(ctx, \"myzset\",\n\t\tredis.Z{Member: \"uno\", Score: 1},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(zAddResult2)\n\n\tzAddResult3, err := rdb.ZAdd(ctx, \"myzset\",\n\t\tredis.Z{Member: \"two\", Score: 2},\n\t\tredis.Z{Member: \"three\", Score: 3},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(zAddResult3) // >>> 2\n\n\tzAddResult4, err := rdb.ZRangeWithScores(ctx, \"myzset\", 0, -1).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(zAddResult4) // >>> [{1 one} {1 uno} {2 two} {3 three}]","section_id":"examples"},{"id":"examples-ex3","language":"java","code":"CompletableFuture<Void> zAddExample = asyncCommands.zadd(\"myzset\", ScoredValue.just(1d, \"one\"))\n                    .thenCompose(res1 -> {\n                        System.out.println(res1); // >>> 1\n                        return asyncCommands.zadd(\"myzset\", ScoredValue.just(1d, \"uno\"));\n                    }).thenCompose(res2 -> {\n                        System.out.println(res2); // >>> 1\n                        return asyncCommands.zadd(\"myzset\",\n                                ScoredValue.just(2d, \"two\"), ScoredValue.just(3d, \"three\"));\n                    }).thenCompose(res3 -> {\n                        System.out.println(res3); // >>> 2\n                        return asyncCommands.zrangeWithScores(\"myzset\", 0, -1);\n                    }).thenAccept(res4 -> {\n                        System.out.println(res4);\n                        // >>> [ScoredValue[1.000000, one], ScoredValue[1.000000, uno],\n                        // >>>  ScoredValue[2.000000, two], ScoredValue[3.000000, three]]\n                    }).toCompletableFuture();","section_id":"examples"},{"id":"examples-ex4","language":"java","code":"Mono<Void> zAddExample = reactiveCommands.zadd(\"myzset\", ScoredValue.just(1d, \"one\"))\n                    .doOnNext(res1 -> {\n                        System.out.println(res1); // >>> 1\n                    })\n                    .then(reactiveCommands.zadd(\"myzset\", ScoredValue.just(1d, \"uno\")))\n                    .doOnNext(res2 -> {\n                        System.out.println(res2); // >>> 1\n                    })\n                    .then(reactiveCommands.zadd(\"myzset\",\n                            ScoredValue.just(2d, \"two\"), ScoredValue.just(3d, \"three\")))\n                    .doOnNext(res3 -> {\n                        System.out.println(res3); // >>> 2\n                    })\n                    .then(reactiveCommands.zrangeWithScores(\"myzset\", 0, -1).collectList())\n                    .doOnNext(res4 -> {\n                        System.out.println(res4);\n                        // >>> [ScoredValue[1.000000, one], ScoredValue[1.000000, uno],\n                        // >>>  ScoredValue[2.000000, two], ScoredValue[3.000000, three]]\n                    })\n                    .then();","section_id":"examples"},{"id":"examples-ex5","language":"java","code":"Map<String, Double> zAddExampleParams = new HashMap<>();\n        zAddExampleParams.put(\"one\", 1.0);\n        long zAddResult1 = jedis.zadd(\"myzset\", zAddExampleParams);\n        System.out.println(zAddResult1);    // >>> 1\n\n        zAddExampleParams.clear();\n        zAddExampleParams.put(\"uno\", 1.0);\n        long zAddResult2 = jedis.zadd(\"myzset\", zAddExampleParams);\n        System.out.println(zAddResult2);    // >>> 1\n\n        zAddExampleParams.clear();\n        zAddExampleParams.put(\"two\", 2.0);\n        zAddExampleParams.put(\"three\", 3.0);\n        long zAddResult3 = jedis.zadd(\"myzset\", zAddExampleParams);\n        System.out.println(zAddResult3);    // >>> 2\n\n        List<Tuple> zAddResult4 = jedis.zrangeWithScores(\"myzset\", new ZRangeParams(0, -1));\n\n        for (Tuple item: zAddResult4) {\n            System.out.println(\"Element: \" + item.getElement() + \", Score: \" + item.getScore());\n        }\n        // >>> Element: one, Score: 1.0\n        // >>> Element: uno, Score: 1.0\n        // >>> Element: two, Score: 2.0\n        // >>> Element: three, Score: 3.0","section_id":"examples"},{"id":"examples-ex6","language":"javascript","code":"const val1 = await client.zAdd(\"myzset\", [{ value: 'one', score: 1 }]);\nconsole.log(val1);\n// returns 1\n\nconst val2 = await client.zAdd(\"myzset\", [{ value: 'uno', score: 1 }]);\nconsole.log(val2);\n// returns 1\n\nconst val3 = await client.zAdd(\"myzset\", [{ value: 'two', score: 2 }, { value: 'three', score: 3 }]);\nconsole.log(val3);\n// returns 2\n\nconst val4 = await client.zRangeWithScores(\"myzset\", 0, -1);\nconsole.log(val4);\n// returns [{value: 'one', score: 1}, {value: 'uno', score: 1}, {value: 'two', score: 2}, {value: 'three', score: 3} ]","section_id":"examples"},{"id":"examples-ex7","language":"javascript","code":"const res1 = await redis.zadd('myzset', 1, 'one');\nconsole.log(res1); // >>> 1\n\nconst res2 = await redis.zadd('myzset', 1, 'uno');\nconsole.log(res2); // >>> 1\n\nconst res3 = await redis.zadd('myzset', 2, 'two', 3, 'three');\nconsole.log(res3); // >>> 2\n\nconst res4 = await redis.zrange('myzset', 0, -1, 'WITHSCORES');\nconsole.log(res4); // >>> ['one', '1', 'uno', '1', 'two', '2', 'three', '3']","section_id":"examples"},{"id":"examples-ex8","language":"javascript","code":"const val1 = await client.zAdd(\"myzset\", [{ value: 'one', score: 1 }]);\nconsole.log(val1);\n// returns 1\n\nconst val2 = await client.zAdd(\"myzset\", [{ value: 'uno', score: 1 }]);\nconsole.log(val2);\n// returns 1\n\nconst val3 = await client.zAdd(\"myzset\", [{ value: 'two', score: 2 }, { value: 'three', score: 3 }]);\nconsole.log(val3);\n// returns 2\n\nconst val4 = await client.zRangeWithScores(\"myzset\", 0, -1);\nconsole.log(val4);\n// returns [{value: 'one', score: 1}, {value: 'uno', score: 1}, {value: 'two', score: 2}, {value: 'three', score: 3} ]","section_id":"examples"},{"id":"examples-ex9","language":"javascript","code":"const res1 = await redis.zadd('myzset', 1, 'one');\nconsole.log(res1); // >>> 1\n\nconst res2 = await redis.zadd('myzset', 1, 'uno');\nconsole.log(res2); // >>> 1\n\nconst res3 = await redis.zadd('myzset', 2, 'two', 3, 'three');\nconsole.log(res3); // >>> 2\n\nconst res4 = await redis.zrange('myzset', 0, -1, 'WITHSCORES');\nconsole.log(res4); // >>> ['one', '1', 'uno', '1', 'two', '2', 'three', '3']","section_id":"examples"},{"id":"examples-ex10","language":"php","code":"$res1 = $r->zadd('myzset', ['one' => 1]);\n        echo $res1 . PHP_EOL; // >>> 1\n\n        $res2 = $r->zadd('myzset', ['uno' => 1]);\n        echo $res2 . PHP_EOL; // >>> 1\n\n        $res3 = $r->zadd('myzset', ['two' => 2, 'three' => 3]);\n        echo $res3 . PHP_EOL; // >>> 2\n\n        $res4 = $r->zrange('myzset', 0, -1, ['withscores' => true]);\n        echo json_encode($res4) . PHP_EOL;\n        // >>> {\"one\":\"1\",\"uno\":\"1\",\"two\":\"2\",\"three\":\"3\"}","section_id":"examples"},{"id":"examples-ex11","language":"python","code":"res = r.zadd(\"myzset\", {\"one\": 1})\nprint(res)\n# >>> 1\n\nres = r.zadd(\"myzset\", {\"uno\": 1})\nprint(res)\n# >>> 1\n\nres = r.zadd(\"myzset\", {\"two\": 2, \"three\": 3})\nprint(res)\n# >>> 2\n\nres = r.zrange(\"myzset\", 0, -1, withscores=True)\n# >>> [('one', 1.0), ('uno', 1.0), ('two', 2.0), ('three', 3.0)]","section_id":"examples"},{"id":"examples-ex12","language":"ruby","code":"res1 = r.zadd('myzset', [[1, 'one']])\nputs res1 # >>> 1\n\nres2 = r.zadd('myzset', [[1, 'uno']])\nputs res2 # >>> 1\n\nres3 = r.zadd('myzset', [[2, 'two'], [3, 'three']])\nputs res3 # >>> 2\n\nres4 = r.zrange('myzset', 0, -1, with_scores: true)\nputs res4.inspect\n# >>> [[\"one\", 1.0], [\"uno\", 1.0], [\"two\", 2.0], [\"three\", 3.0]]","section_id":"examples"},{"id":"examples-ex13","language":"rust","code":"if let Ok(res1) = r.zadd(\"myzset\", \"one\", 1).await {\n            let res1: i32 = res1;\n            println!(\"{res1}\"); // >>> 1\n        }\n\n        if let Ok(res2) = r.zadd(\"myzset\", \"uno\", 1).await {\n            let res2: i32 = res2;\n            println!(\"{res2}\"); // >>> 1\n        }\n\n        if let Ok(res3) = r.zadd_multiple(\"myzset\", &[(2, \"two\"), (3, \"three\")]).await {\n            let res3: i32 = res3;\n            println!(\"{res3}\"); // >>> 2\n        }\n\n        if let Ok(res4) = r.zrange_withscores(\"myzset\", 0, -1).await {\n            let res4: Vec<(String, f64)> = res4;\n            println!(\"{res4:?}\");\n            // >>> [(\"one\", 1.0), (\"uno\", 1.0), (\"two\", 2.0), (\"three\", 3.0)]\n        }","section_id":"examples"},{"id":"examples-ex14","language":"rust","code":"if let Ok(res1) = r.zadd(\"myzset\", \"one\", 1) {\n            let res1: i32 = res1;\n            println!(\"{res1}\"); // >>> 1\n        }\n\n        if let Ok(res2) = r.zadd(\"myzset\", \"uno\", 1) {\n            let res2: i32 = res2;\n            println!(\"{res2}\"); // >>> 1\n        }\n\n        if let Ok(res3) = r.zadd_multiple(\"myzset\", &[(2, \"two\"), (3, \"three\")]) {\n            let res3: i32 = res3;\n            println!(\"{res3}\"); // >>> 2\n        }\n\n        if let Ok(res4) = r.zrange_withscores(\"myzset\", 0, -1) {\n            let res4: Vec<(String, f64)> = res4;\n            println!(\"{res4:?}\");\n            // >>> [(\"one\", 1.0), (\"uno\", 1.0), (\"two\", 2.0), (\"three\", 3.0)]\n        }","section_id":"examples"}]}
{"schema_version":2,"id":"commands/zcard","title":"ZCARD","url":"https://redis.io/docs/latest/commands/zcard/","summary":"Returns the number of members in a sorted set.","since":"1.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"795c73c3c029466189fa4b8faf060a355085523fe4b954e4ea9ebc7e5fdc5eec","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns the sorted set cardinality (number of elements) of the sorted set stored\nat `key`."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the sorted set.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ZADD myzset 1 \"one\"\n(integer) 1\nredis> ZADD myzset 2 \"two\"\n(integer) 1\nredis> ZCARD myzset\n(integer) 2"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the cardinality (number of members) of the sorted set, or 0 if the key doesn't exist.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the cardinality (number of members) of the sorted set, or 0 if the key doesn't exist."}],"examples":[]}
{"schema_version":2,"id":"commands/zcount","title":"ZCOUNT","url":"https://redis.io/docs/latest/commands/zcount/","summary":"Returns the count of members in a sorted set that have scores within a range.","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:43+01:00","page_type":"content","content_hash":"8399d47ae3f61fa37f4e620d95d82726be1c7aca58f6264528784167e98b35f3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns the number of elements in the sorted set at `key` with a score between\n`min` and `max`.\n\nThe `min` and `max` arguments have the same semantic as described for\n[`ZRANGEBYSCORE`](https://redis.io/docs/latest/commands/zrangebyscore).\n\nNote: the command has a complexity of just O(log(N)) because it uses elements ranks (see [`ZRANK`](https://redis.io/docs/latest/commands/zrank)) to get an idea of the range. Because of this there is no need to do a work proportional to the size of the range."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the sorted set.\n\n</details>\n\n<details open><summary><code>min</code></summary>\n\nThe minimum score. The bound is inclusive unless prefixed with `(`. Use `-inf` for no lower bound.\n\n</details>\n\n<details open><summary><code>max</code></summary>\n\nThe maximum score. The bound is inclusive unless prefixed with `(`. Use `+inf` for no upper bound.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ZADD myzset 1 \"one\"\n(integer) 1\nredis> ZADD myzset 2 \"two\"\n(integer) 1\nredis> ZADD myzset 3 \"three\"\n(integer) 1\nredis> ZCOUNT myzset -inf +inf\n(integer) 3\nredis> ZCOUNT myzset (1 3\n(integer) 2"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of members in the specified score range.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of members in the specified score range."}],"examples":[]}
{"schema_version":2,"id":"commands/zdiff","title":"ZDIFF","url":"https://redis.io/docs/latest/commands/zdiff/","summary":"Returns the difference between multiple sorted sets.","since":"6.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:43+01:00","page_type":"content","content_hash":"fb7914fe47f836261835ac5f20bc92778791c7a626ec3aa4037569be30cb06cf","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nThis command is similar to [`ZDIFFSTORE`](https://redis.io/docs/latest/commands/zdiffstore), but instead of storing the resulting\nsorted set, it is returned to the client."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>numkeys</code></summary>\n\nThe number of keys that follow.\n\n</details>\n\n<details open><summary><code>key [key ...]</code></summary>\n\nOne or more sorted-set keys. The result is the members of the first set that are not present in any of the subsequent sets.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>WITHSCORES</code></summary>\n\nAlso return the score of each member.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ZADD zset1 1 \"one\"\n(integer) 1\nredis> ZADD zset1 2 \"two\"\n(integer) 1\nredis> ZADD zset1 3 \"three\"\n(integer) 1\nredis> ZADD zset2 1 \"one\"\n(integer) 1\nredis> ZADD zset2 2 \"two\"\n(integer) 1\nredis> ZDIFF 2 zset1 zset2\n1) \"three\"\nredis> ZDIFF 2 zset1 zset2 WITHSCORES\n1) \"three\"\n2) \"3\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n* [Array reply](../../develop/reference/protocol-spec#arrays): the result of the difference including, optionally, scores when the _WITHSCORES_ option is used.\n\n**RESP3:**\n\n* [Array reply](../../develop/reference/protocol-spec#arrays): the result of the difference including, optionally, scores when the _WITHSCORES_ option is used."}],"examples":[]}
{"schema_version":2,"id":"commands/zdiffstore","title":"ZDIFFSTORE","url":"https://redis.io/docs/latest/commands/zdiffstore/","summary":"Stores the difference of multiple sorted sets in a key.","since":"6.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:43+01:00","page_type":"content","content_hash":"63dd41647004e06c570522a945eb333e9563dc762f78714189eec811fa8e82d5","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nComputes the difference between the first and all successive input sorted sets\nand stores the result in `destination`. The total number of input keys is\nspecified by `numkeys`.\n\nKeys that do not exist are considered to be empty sets.\n\nIf `destination` already exists, it is overwritten."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>destination</code></summary>\n\nThe key to store the resulting sorted set in.\n\n</details>\n\n<details open><summary><code>numkeys</code></summary>\n\nThe number of keys that follow.\n\n</details>\n\n<details open><summary><code>key [key ...]</code></summary>\n\nOne or more sorted-set keys. The difference is the members of the first set that are not present in any of the subsequent sets.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ZADD zset1 1 \"one\"\n(integer) 1\nredis> ZADD zset1 2 \"two\"\n(integer) 1\nredis> ZADD zset1 3 \"three\"\n(integer) 1\nredis> ZADD zset2 1 \"one\"\n(integer) 1\nredis> ZADD zset2 2 \"two\"\n(integer) 1\nredis> ZDIFFSTORE out 2 zset1 zset2\n(integer) 1\nredis> ZRANGE out 0 -1 WITHSCORES\n1) \"three\"\n2) \"3\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of members in the resulting sorted set at _destination_.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of members in the resulting sorted set at _destination_."}],"examples":[]}
{"schema_version":2,"id":"commands/zincrby","title":"ZINCRBY","url":"https://redis.io/docs/latest/commands/zincrby/","summary":"Increments the score of a member in a sorted set.","since":"1.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"3a5b69ebce7fc901aa4b8172b1ed6da418b772df530501ecfc921e9a9864d812","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Increments the score of `member` in the sorted set stored at `key` by\n`increment`.\nIf `member` does not exist in the sorted set, it is added with `increment` as\nits score (as if its previous score was `0.0`).\nIf `key` does not exist, a new sorted set with the specified `member` as its\nsole member is created.\n\nAn error is returned when `key` exists but does not hold a sorted set.\n\nThe `score` value should be the string representation of a numeric value, and\naccepts double precision floating point numbers.\nIt is possible to provide a negative value to decrement the score."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the sorted set.\n\n</details>\n\n<details open><summary><code>increment</code></summary>\n\nThe amount to add to the member's current score.\n\n</details>\n\n<details open><summary><code>member</code></summary>\n\nThe member whose score to increment. It is added with the increment as its score if it does not exist.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ZADD myzset 1 \"one\"\n(integer) 1\nredis> ZADD myzset 2 \"two\"\n(integer) 1\nredis> ZINCRBY myzset 2 \"one\"\n\"3\"\nredis> ZRANGE myzset 0 -1 WITHSCORES\n1) \"two\"\n2) \"2\"\n3) \"one\"\n4) \"3\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the new score of _member_ as a double precision floating point number.\n\n**RESP3:**\n\n[Double reply](../../develop/reference/protocol-spec#doubles): the new score of _member_."}],"examples":[]}
{"schema_version":2,"id":"commands/zinter","title":"ZINTER","url":"https://redis.io/docs/latest/commands/zinter/","summary":"Returns the intersect of multiple sorted sets.","since":"6.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:43+01:00","page_type":"content","content_hash":"c084bb71d65100b313b5feeec20f14d09613c368baac5f6bde315f3c58db41fa","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nThis command is similar to [`ZINTERSTORE`](https://redis.io/docs/latest/commands/zinterstore), but instead of storing the resulting\nsorted set, it is returned to the client.\n\nFor a description of the `WEIGHTS` and `AGGREGATE` options, see [`ZUNIONSTORE`](https://redis.io/docs/latest/commands/zunionstore)."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>numkeys</code></summary>\n\nThe number of keys that follow.\n\n</details>\n\n<details open><summary><code>key [key ...]</code></summary>\n\nOne or more sorted-set keys to intersect.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>WEIGHTS weight [weight ...]</code></summary>\n\nA multiplication factor for each input set; each set's scores are multiplied by its weight before aggregation. Defaults to 1 for every set.\n\n</details>\n\n<details open><summary><code>AGGREGATE SUM | MIN | MAX | COUNT</code></summary>\n\nHow to combine the scores of members that exist in multiple sets: `SUM` (the default), `MIN`, `MAX`, or `COUNT`.\n\n</details>\n\n<details open><summary><code>WITHSCORES</code></summary>\n\nAlso return the score of each member.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ZADD zset1 1 \"one\"\n(integer) 1\nredis> ZADD zset1 2 \"two\"\n(integer) 1\nredis> ZADD zset2 1 \"one\"\n(integer) 1\nredis> ZADD zset2 2 \"two\"\n(integer) 1\nredis> ZADD zset2 3 \"three\"\n(integer) 1\nredis> ZINTER 2 zset1 zset2\n1) \"one\"\n2) \"two\"\nredis> ZINTER 2 zset1 zset2 WITHSCORES\n1) \"one\"\n2) \"2\"\n3) \"two\"\n4) \"4\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n* [Array reply](../../develop/reference/protocol-spec#arrays): the result of the intersection including, optionally, scores when the _WITHSCORES_ option is used.\n\n**RESP3:**\n\n* [Array reply](../../develop/reference/protocol-spec#arrays): the result of the intersection including, optionally, scores when the _WITHSCORES_ option is used."}],"examples":[]}
{"schema_version":2,"id":"commands/zintercard","title":"ZINTERCARD","url":"https://redis.io/docs/latest/commands/zintercard/","summary":"Returns the number of members of the intersect of multiple sorted sets.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:43+01:00","page_type":"content","content_hash":"905aa63379c83bd5a09951d9be54c03e30e60acd994a2c08e4cbc65525e9e0ed","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nThis command is similar to [`ZINTER`](https://redis.io/docs/latest/commands/zinter), but instead of returning the result set, it returns just the cardinality of the result.\n\nKeys that do not exist are considered to be empty sets.\nWith one of the keys being an empty set, the resulting set is also empty (since set intersection with an empty set always results in an empty set).\n\nBy default, the command calculates the cardinality of the intersection of all given sets.\nWhen provided with the optional `LIMIT` argument (which defaults to 0 and means unlimited), if the intersection cardinality reaches limit partway through the computation, the algorithm will exit and yield limit as the cardinality.\nSuch implementation ensures a significant speedup for queries where the limit is lower than the actual intersection cardinality."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>numkeys</code></summary>\n\nThe number of keys that follow.\n\n</details>\n\n<details open><summary><code>key [key ...]</code></summary>\n\nOne or more sorted-set keys to intersect.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>LIMIT limit</code></summary>\n\nStop counting once the cardinality reaches `limit`. `0` (the default) means no limit.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ZADD zset1 1 \"one\"\n(integer) 1\nredis> ZADD zset1 2 \"two\"\n(integer) 1\nredis> ZADD zset2 1 \"one\"\n(integer) 1\nredis> ZADD zset2 2 \"two\"\n(integer) 1\nredis> ZADD zset2 3 \"three\"\n(integer) 1\nredis> ZINTER 2 zset1 zset2\n1) \"one\"\n2) \"two\"\nredis> ZINTERCARD 2 zset1 zset2\n(integer) 2\nredis> ZINTERCARD 2 zset1 zset2 LIMIT 1\n(integer) 1"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of members in the resulting intersection.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of members in the resulting intersection."}],"examples":[]}
{"schema_version":2,"id":"commands/zinterstore","title":"ZINTERSTORE","url":"https://redis.io/docs/latest/commands/zinterstore/","summary":"Stores the intersect of multiple sorted sets in a key.","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:43+01:00","page_type":"content","content_hash":"8232d6c34ca31b1d2ea915a1e2e27a3e2da6fe68a3e136405ef50d7d62ef2dc4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nComputes the intersection of `numkeys` sorted sets given by the specified keys,\nand stores the result in `destination`.\nIt is mandatory to provide the number of input keys (`numkeys`) before passing\nthe input keys and the other (optional) arguments.\n\nBy default, the resulting score of an element is the sum of its scores in the\nsorted sets where it exists.\nBecause intersection requires an element to be a member of every given sorted\nset, this results in the score of every element in the resulting sorted set to\nbe equal to the number of input sorted sets.\n\nFor a description of the `WEIGHTS` and `AGGREGATE` options, see [`ZUNIONSTORE`](https://redis.io/docs/latest/commands/zunionstore).\n\nIf `destination` already exists, it is overwritten."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>destination</code></summary>\n\nThe key to store the resulting sorted set in.\n\n</details>\n\n<details open><summary><code>numkeys</code></summary>\n\nThe number of keys that follow.\n\n</details>\n\n<details open><summary><code>key [key ...]</code></summary>\n\nOne or more sorted-set keys to intersect.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>WEIGHTS weight [weight ...]</code></summary>\n\nA multiplication factor for each input set; each set's scores are multiplied by its weight before aggregation. Defaults to 1 for every set.\n\n</details>\n\n<details open><summary><code>AGGREGATE SUM | MIN | MAX | COUNT</code></summary>\n\nHow to combine the scores of members that exist in multiple sets: `SUM` (the default), `MIN`, `MAX`, or `COUNT`.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ZADD zset1 1 \"one\"\n(integer) 1\nredis> ZADD zset1 2 \"two\"\n(integer) 1\nredis> ZADD zset2 1 \"one\"\n(integer) 1\nredis> ZADD zset2 2 \"two\"\n(integer) 1\nredis> ZADD zset2 3 \"three\"\n(integer) 1\nredis> ZINTERSTORE out 2 zset1 zset2 WEIGHTS 2 3\n(integer) 2\nredis> ZRANGE out 0 -1 WITHSCORES\n1) \"one\"\n2) \"5\"\n3) \"two\"\n4) \"10\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of members in the resulting sorted set at the _destination_.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of members in the resulting sorted set at the _destination_."}],"examples":[]}
{"schema_version":2,"id":"commands/zlexcount","title":"ZLEXCOUNT","url":"https://redis.io/docs/latest/commands/zlexcount/","summary":"Returns the number of members in a sorted set within a lexicographical range.","since":"2.8.9","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:43+01:00","page_type":"content","content_hash":"61bb68c40b3f842ef7703b23adeb371a39a239b5e9a9dc747478b38f66bc3240","sections":[{"id":"overview","title":"Overview","role":"overview","text":"When all the elements in a sorted set are inserted with the same score, in order to force lexicographical ordering, this command returns the number of elements in the sorted set at `key` with a value between `min` and `max`.\n\nThe `min` and `max` arguments have the same meaning as described for\n[`ZRANGEBYLEX`](https://redis.io/docs/latest/commands/zrangebylex).\n\nNote: the command has a complexity of just O(log(N)) because it uses elements ranks (see [`ZRANK`](https://redis.io/docs/latest/commands/zrank)) to get an idea of the range. Because of this there is no need to do a work proportional to the size of the range."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the sorted set.\n\n</details>\n\n<details open><summary><code>min</code></summary>\n\nThe minimum member, compared lexicographically. Prefix with `[` for an inclusive bound or `(` for an exclusive bound; use `-` for the lowest possible value.\n\n</details>\n\n<details open><summary><code>max</code></summary>\n\nThe maximum member, compared lexicographically. Prefix with `[` for an inclusive bound or `(` for an exclusive bound; use `+` for the highest possible value.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ZADD myzset 0 a 0 b 0 c 0 d 0 e\n(integer) 5\nredis> ZADD myzset 0 f 0 g\n(integer) 2\nredis> ZLEXCOUNT myzset - +\n(integer) 7\nredis> ZLEXCOUNT myzset [b [f\n(integer) 5"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of members in the specified score range.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of members in the specified score range."}],"examples":[]}
{"schema_version":2,"id":"commands/zmpop","title":"ZMPOP","url":"https://redis.io/docs/latest/commands/zmpop/","summary":"Returns the highest- or lowest-scoring members from one or more sorted sets after removing them. Deletes the sorted set if the last member was popped.","since":"7.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:43+01:00","page_type":"content","content_hash":"779e93790c1faae3f87dc5fbc7528717113f3cec0262bd34b8ba140af1768b7f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nPops one or more elements, that are member-score pairs, from the first non-empty sorted set in the provided list of key names.\n\n`ZMPOP` and [`BZMPOP`](https://redis.io/docs/latest/commands/bzmpop) are similar to the following, more limited, commands:\n\n- [`ZPOPMIN`](https://redis.io/docs/latest/commands/zpopmin) or [`ZPOPMAX`](https://redis.io/docs/latest/commands/zpopmax) which take only one key, and can return multiple elements.\n- [`BZPOPMIN`](https://redis.io/docs/latest/commands/bzpopmin) or [`BZPOPMAX`](https://redis.io/docs/latest/commands/bzpopmax) which take multiple keys, but return only one element from just one key.\n\nSee [`BZMPOP`](https://redis.io/docs/latest/commands/bzmpop) for the blocking variant of this command.\n\nWhen the `MIN` modifier is used, the elements popped are those with the lowest scores from the first non-empty sorted set. The `MAX` modifier causes elements with the highest scores to be popped.\nThe optional `COUNT` can be used to specify the number of elements to pop, and is set to 1 by default.\n\nThe number of popped elements is the minimum from the sorted set's cardinality and `COUNT`'s value."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>numkeys</code></summary>\n\nThe number of keys that follow.\n\n</details>\n\n<details open><summary><code>key [key ...]</code></summary>\n\nOne or more sorted-set keys to pop from.\n\n</details>\n\n<details open><summary><code>MIN | MAX</code></summary>\n\nPop the members with the lowest scores (`MIN`) or the highest scores (`MAX`).\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>COUNT count</code></summary>\n\nThe number of members to pop. Defaults to 1.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ZMPOP 1 notsuchkey MIN\n(nil)\nredis> ZADD myzset 1 \"one\" 2 \"two\" 3 \"three\"\n(integer) 3\nredis> ZMPOP 1 myzset MIN\n1) \"myzset\"\n2) 1) 1) \"one\"\n      2) \"1\"\nredis> ZRANGE myzset 0 -1 WITHSCORES\n1) \"two\"\n2) \"2\"\n3) \"three\"\n4) \"3\"\nredis> ZMPOP 1 myzset MAX COUNT 10\n1) \"myzset\"\n2) 1) 1) \"three\"\n      2) \"3\"\n   2) 1) \"two\"\n      2) \"2\"\nredis> ZADD myzset2 4 \"four\" 5 \"five\" 6 \"six\"\n(integer) 3\nredis> ZMPOP 2 myzset myzset2 MIN COUNT 10\n1) \"myzset2\"\n2) 1) 1) \"four\"\n      2) \"4\"\n   2) 1) \"five\"\n      2) \"5\"\n   3) 1) \"six\"\n      2) \"6\"\nredis> ZRANGE myzset 0 -1 WITHSCORES\n(empty array)\nredis> ZMPOP 2 myzset myzset2 MAX COUNT 10\n(nil)\nredis> ZRANGE myzset2 0 -1 WITHSCORES\n(empty array)\nredis> EXISTS myzset myzset2\n(integer) 0"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): when no element could be popped.\n* [Array reply](../../develop/reference/protocol-spec#arrays): A two-element array with the first element being the name of the key from which elements were popped, and the second element is an array of the popped elements. Every entry in the elements array is also an array that contains the member and its score.\n\n**RESP3:**\n\nOne of the following:\n* [Null reply](../../develop/reference/protocol-spec#nulls): when no element could be popped.\n* [Array reply](../../develop/reference/protocol-spec#arrays): A two-element array with the first element being the name of the key from which elements were popped, and the second element is an array of the popped elements. Every entry in the elements array is also an array that contains the member and its score."}],"examples":[]}
{"schema_version":2,"id":"commands/zmscore","title":"ZMSCORE","url":"https://redis.io/docs/latest/commands/zmscore/","summary":"Returns the score of one or more members in a sorted set.","since":"6.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"0cfefd3615c23d7adc9f5cc9990a26da44f49abfdff5b27ba3b9ef829a08128e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns the scores associated with the specified `members` in the sorted set stored at `key`.\n\nFor every `member` that does not exist in the sorted set, a `nil` value is returned."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the sorted set.\n\n</details>\n\n<details open><summary><code>member [member ...]</code></summary>\n\nOne or more members whose scores to return.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ZADD myzset 1 \"one\"\n(integer) 1\nredis> ZADD myzset 2 \"two\"\n(integer) 1\nredis> ZMSCORE myzset \"one\" \"two\" \"nofield\"\n1) \"1\"\n2) \"2\"\n3) (nil)"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): if the member does not exist in the sorted set.\n* [Array reply](../../develop/reference/protocol-spec#arrays): a list of [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings) _member_ scores as double-precision floating point numbers.\n\n**RESP3:**\n\nOne of the following:\n* [Null reply](../../develop/reference/protocol-spec#nulls): if the member does not exist in the sorted set.\n* [Array reply](../../develop/reference/protocol-spec#arrays): a list of [Double reply](../../develop/reference/protocol-spec#doubles) _member_ scores as double-precision floating point numbers."}],"examples":[]}
{"schema_version":2,"id":"commands/zpopmax","title":"ZPOPMAX","url":"https://redis.io/docs/latest/commands/zpopmax/","summary":"Returns the highest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.","since":"5.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"c62cd9958c4b1b26d47e15689ef7623fc534d6ebeefd5a23f7d024e6171636e3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Removes and returns up to `count` members with the highest scores in the sorted\nset stored at `key`.\n\nWhen left unspecified, the default value for `count` is 1. Specifying a `count`\nvalue that is higher than the sorted set's cardinality will not produce an\nerror. When returning multiple elements, the one with the highest score will\nbe the first, followed by the elements with lower scores."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the sorted set.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>count</code></summary>\n\nThe number of members to pop. Without it, a single member is popped.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ZADD myzset 1 \"one\"\n(integer) 1\nredis> ZADD myzset 2 \"two\"\n(integer) 1\nredis> ZADD myzset 3 \"three\"\n(integer) 1\nredis> ZPOPMAX myzset\n1) \"three\"\n2) \"3\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n* [Array reply](../../develop/reference/protocol-spec#arrays): a list of popped elements and scores.\n\n**RESP3:**\n\n* [Array reply](../../develop/reference/protocol-spec#arrays): a list of popped elements and scores."}],"examples":[]}
{"schema_version":2,"id":"commands/zpopmin","title":"ZPOPMIN","url":"https://redis.io/docs/latest/commands/zpopmin/","summary":"Returns the lowest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.","since":"5.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"b339c86821cce92c5563116c2c1e2cc654171ef5ad298e6bc2812489604ef346","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Removes and returns up to `count` members with the lowest scores in the sorted\nset stored at `key`.\n\nWhen left unspecified, the default value for `count` is 1. Specifying a `count`\nvalue that is higher than the sorted set's cardinality will not produce an\nerror. When returning multiple elements, the one with the lowest score will\nbe the first, followed by the elements with greater scores."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the sorted set.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>count</code></summary>\n\nThe number of members to pop. Without it, a single member is popped.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ZADD myzset 1 \"one\"\n(integer) 1\nredis> ZADD myzset 2 \"two\"\n(integer) 1\nredis> ZADD myzset 3 \"three\"\n(integer) 1\nredis> ZPOPMIN myzset\n1) \"one\"\n2) \"1\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n* [Array reply](../../develop/reference/protocol-spec#arrays): a list of popped elements and scores.\n\n**RESP3:**\n\n* [Array reply](../../develop/reference/protocol-spec#arrays): a list of popped elements and scores."}],"examples":[]}
{"schema_version":2,"id":"commands/zrandmember","title":"ZRANDMEMBER","url":"https://redis.io/docs/latest/commands/zrandmember/","summary":"Returns one or more random members from a sorted set.","since":"6.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:43+01:00","page_type":"content","content_hash":"1b0a6b746ba2fdd44fe9397f486dec74598c2e629c40a9e61aece9d6133a226d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"When called with just the `key` argument, return a random element from the sorted set value stored at `key`.\n\nIf the provided `count` argument is positive, return an array of distinct elements.\nThe array's length is either `count` or the sorted set's cardinality ([`ZCARD`](https://redis.io/docs/latest/commands/zcard)), whichever is lower.\n\nIf called with a negative `count`, the behavior changes and the command is allowed to return the same element multiple times.\nIn this case, the number of returned elements is the absolute value of the specified `count`.\n\nThe optional `WITHSCORES` modifier changes the reply so it includes the respective scores of the randomly selected elements from the sorted set."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the sorted set.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>count</code></summary>\n\nThe number of members to return. A positive `count` returns up to that many distinct members; a negative `count` returns exactly `|count|` members and may repeat them.\n\n</details>\n\n<details open><summary><code>WITHSCORES</code></summary>\n\nAlso return the score of each returned member. Can only be used together with `count`.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ZADD dadi 1 uno 2 due 3 tre 4 quattro 5 cinque 6 sei\n(integer) 6\nredis> ZRANDMEMBER dadi\nredis> ZRANDMEMBER dadi\nredis> ZRANDMEMBER dadi -5 WITHSCORES"},{"id":"details","title":"Details","role":"content","text":""},{"id":"specification-of-the-behavior-when-count-is-passed","title":"Specification of the behavior when count is passed","role":"content","text":"When the `count` argument is a positive value this command behaves as follows:\n\n* No repeated elements are returned.\n* If `count` is bigger than the cardinality of the sorted set, the command will only return the whole sorted set without additional elements.\n* The order of elements in the reply is not truly random, so it is up to the client to shuffle them if needed.\n\nWhen the `count` is a negative value, the behavior changes as follows:\n\n* Repeating elements are possible.\n* Exactly `count` elements, or an empty array if the sorted set is empty (non-existing key), are always returned.\n* The order of elements in the reply is truly random."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): without the additional _count_ argument, the command returns a randomly selected member, or [Nil reply](../../develop/reference/protocol-spec#bulk-strings) when _key_ doesn't exist.\n[Array reply](../../develop/reference/protocol-spec#arrays): when the additional _count_ argument is passed, the command returns an array of members, or an empty array when _key_ doesn't exist. If the _WITHSCORES_ modifier is used, the reply is a list of members and their scores from the sorted set.\n\n**RESP3:**\n\n[Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): without the additional _count_ argument, the command returns a randomly selected member, or [Null reply](../../develop/reference/protocol-spec#nulls) when _key_ doesn't exist.\n[Array reply](../../develop/reference/protocol-spec#arrays): when the additional _count_ argument is passed, the command returns an array of members, or an empty array when _key_ doesn't exist. If the _WITHSCORES_ modifier is used, the reply is a list of members and their scores from the sorted set."}],"examples":[]}
{"schema_version":2,"id":"commands/zrange","title":"ZRANGE","url":"https://redis.io/docs/latest/commands/zrange/","summary":"Returns members in a sorted set within a range of indexes.","since":"1.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:43+01:00","page_type":"content","content_hash":"ac760cb04569f58e4a7119f1fcc1507cbe666f3de425d306ab2f278121a8ec6a","sections":[{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the sorted set.\n\n</details>\n\n<details open><summary><code>start</code></summary>\n\nThe start of the range. By default a zero-based index (negatives count from the end); a score bound with `BYSCORE`; or a lexicographical bound with `BYLEX`.\n\n</details>\n\n<details open><summary><code>stop</code></summary>\n\nThe end of the range, inclusive. Interpreted the same way as `start`.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>BYSCORE | BYLEX</code></summary>\n\nInterpret the range as a score range (`BYSCORE`) or a lexicographical range (`BYLEX`) instead of an index range.\n\n</details>\n\n<details open><summary><code>REV</code></summary>\n\nReverse the ordering so results are returned from high to low.\n\n</details>\n\n<details open><summary><code>LIMIT offset count</code></summary>\n\nSkip `offset` matching members and return up to `count` of them. Requires `BYSCORE` or `BYLEX`. A negative `count` returns all remaining members.\n\n</details>\n\n<details open><summary><code>WITHSCORES</code></summary>\n\nAlso return the score of each member.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"Foundational: Retrieve a range of members from a sorted set by index using ZRANGE (supports negative indexes, inclusive range)\n\n**Difficulty:** Beginner\n\n**Commands:** ZADD, ZRANGE\n\n**Complexity:**\n- ZADD: O(log(N)\n- ZRANGE: O(log(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nThe following example using `WITHSCORES` shows how the command returns always an array, but this time, populated with *element_1*, *score_1*, *element_2*, *score_2*, ..., *element_N*, *score_N*.\n\nReturn scores with members: Retrieve members with their scores from a sorted set using ZRANGE with WITHSCORES option\n\n**Difficulty:** Intermediate\n\n**Commands:** DEL, ZADD, ZRANGE\n\n**Complexity:**\n- DEL: O(N)\n- ZADD: O(log(N)\n- ZRANGE: O(log(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nThis example shows how to query the sorted set by score, excluding the value `1` and up to infinity, returning only the second element of the result:\n\nQuery by score: Query a sorted set by score range using ZRANGE with BYSCORE and LIMIT options (supports exclusive ranges and pagination)\n\n**Difficulty:** Intermediate\n\n**Commands:** DEL, ZADD, ZRANGE\n\n**Complexity:**\n- DEL: O(N)\n- ZADD: O(log(N)\n- ZRANGE: O(log(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"details","title":"Details","role":"content","text":""},{"id":"common-behavior-and-options","title":"Common behavior and options","role":"content","text":"The order of elements is from the lowest to the highest score. Elements with the same score are ordered lexicographically.\n\nThe optional `REV` argument reverses the ordering, so elements are ordered from highest to lowest score, and score ties are resolved by reverse lexicographical ordering.\n\nThe optional `LIMIT` argument can be used to obtain a sub-range from the matching elements (similar to _SELECT LIMIT offset, count_ in SQL).\nA negative `<count>` returns all elements from the `<offset>`. Keep in mind that if `<offset>` is large, the sorted set needs to be traversed for `<offset>` elements before getting to the elements to return, which can add up to O(N) time complexity.\n\nThe optional `WITHSCORES` argument supplements the command's reply with the scores of elements returned. The returned list contains `value1,score1,...,valueN,scoreN` instead of `value1,...,valueN`. Client libraries are free to return a more appropriate data type (suggestion: an array with (value, score) arrays/tuples)."},{"id":"index-ranges","title":"Index ranges","role":"content","text":"By default, the command performs an index range query. The `<start>` and `<stop>` arguments represent zero-based indexes, where `0` is the first element, `1` is the next element, and so on. These arguments specify an **inclusive range**, so for example, `ZRANGE myzset 0 1` will return both the first and the second element of the sorted set.\n\nThe indexes can also be negative numbers indicating offsets from the end of the sorted set, with `-1` being the last element of the sorted set, `-2` the penultimate element, and so on.\n\nOut of range indexes do not produce an error.\n\nIf `<start>` is greater than either the end index of the sorted set or `<stop>`, an empty list is returned.\n\nIf `<stop>` is greater than the end index of the sorted set, Redis will use the last element of the sorted set."},{"id":"score-ranges","title":"Score ranges","role":"content","text":"When the `BYSCORE` option is provided, the command behaves like [`ZRANGEBYSCORE`](https://redis.io/docs/latest/commands/zrangebyscore) and returns the range of elements from the sorted set having scores equal or between `<start>` and `<stop>`.\n\n`<start>` and `<stop>` can be `-inf` and `+inf`, denoting the negative and positive infinities, respectively. This means that you are not required to know the highest or lowest score in the sorted set to get all elements from or up to a certain score.\n\nBy default, the score intervals specified by `<start>` and `<stop>` are closed (inclusive).\nIt is possible to specify an open interval (exclusive) by prefixing the score\nwith the character `(`.\n\nFor example:\n\n[code example]\n\nWill return all elements with `1 < score <= 5` while:\n\n[code example]\n\nWill return all the elements with `5 < score < 10` (5 and 10 excluded)."},{"id":"reverse-ranges","title":"Reverse ranges","role":"content","text":"Using the `REV` option reverses the sorted set, with index 0 as the element with the highest score.\n\nBy default, `<start>` must be less than or equal to `<stop>` to return anything.\nHowever, if the `BYSCORE`, or `BYLEX` options are selected, the `<start>` is the highest score to consider, and `<stop>` is the lowest score to consider, therefore `<start>` must be greater than or equal to `<stop>` in order to return anything.\n\nFor example:\n\n[code example]\n\nWill return the elements between index 5 and 10 in the reversed index.\n\n[code example]\n\nWill return all elements with scores less than 10 and greater than 5."},{"id":"lexicographical-ranges","title":"Lexicographical ranges","role":"content","text":"When the `BYLEX` option is used, the command behaves like [`ZRANGEBYLEX`](https://redis.io/docs/latest/commands/zrangebylex) and returns the range of elements from the sorted set between the `<start>` and `<stop>` lexicographical closed range intervals.\n\nNote that lexicographical ordering relies on all elements having the same score. The reply is unspecified when the elements have different scores.\n\nValid `<start>` and `<stop>` must start with `(` or `[`, in order to specify\nwhether the range interval is exclusive or inclusive, respectively.\n\nThe special values of `+` or `-` for `<start>` and `<stop>` mean positive and negative infinite strings, respectively, so for instance the command `ZRANGE myzset - + BYLEX` is guaranteed to return all the elements in the sorted set, providing that all the elements have the same score.\n\nThe `REV` options reverses the order of the `<start>` and `<stop>` elements, where `<start>` must be lexicographically greater than `<stop>` to produce a non-empty result.\n\n#### Lexicographical comparison of strings\n\nStrings are compared as a binary array of bytes. Because of how the ASCII character set is specified, this means that usually this also have the effect of comparing normal ASCII characters in an obvious dictionary way. However, this is not true if non-plain ASCII strings are used (for example, utf8 strings).\n\nHowever, the user can apply a transformation to the encoded string so that the first part of the element inserted in the sorted set will compare as the user requires for the specific application. For example, if I want to\nadd strings that will be compared in a case-insensitive way, but I still\nwant to retrieve the real case when querying, I can add strings in the\nfollowing way:\n\n    ZADD autocomplete 0 foo:Foo 0 bar:BAR 0 zap:zap\n\nBecause of the first *normalized* part in every element (before the colon character), we are forcing a given comparison. However, after the range is queried using `ZRANGE ... BYLEX`, the application can display to the user the second part of the string, after the colon.\n\nThe binary nature of the comparison allows to use sorted sets as a general purpose index, for example, the first part of the element can be a 64-bit big-endian number. Since big-endian numbers have the most significant bytes in the initial positions, the binary comparison will match the numerical comparison of the numbers. This can be used in order to implement range queries on 64-bit values. As in the example below, after the first 8 bytes, we can store the value of the element we are indexing."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of members in the specified range with, optionally, their scores when the _WITHSCORES_ option is given.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of members in the specified range with, optionally, their scores when the _WITHSCORES_ option is given."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> ZADD myzset 1 \"one\" 2 \"two\" 3 \"three\"\n(integer) 3\n> ZRANGE myzset 0 -1\n1) \"one\"\n2) \"two\"\n3) \"three\"\n> ZRANGE myzset 2 3\n1) \"three\"\n> ZRANGE myzset -2 -1\n1) \"two\"\n2) \"three\"","section_id":"examples"},{"id":"examples-ex1","language":"csharp","code":"long zRangeResult1 = db.SortedSetAdd(\n            \"myzset\",\n            [\n                new(\"one\", 1),\n                new(\"two\", 2),\n                new(\"three\", 3)\n            ]\n        );\n        Console.WriteLine(zRangeResult1);   // >>> 3\n\n        RedisValue[] zRangeResult2 = db.SortedSetRangeByRank(\"myzset\", 0, -1);\n        Console.WriteLine(string.Join(\", \", zRangeResult2));\n        // >>> one, two, three\n\n        RedisValue[] zRangeResult3 = db.SortedSetRangeByRank(\"myzset\", 2, 3);\n        Console.WriteLine(string.Join(\", \", zRangeResult3));\n        // >>> three\n\n        RedisValue[] zRangeResult4 = db.SortedSetRangeByRank(\"myzset\", -2, -1);\n        Console.WriteLine(string.Join(\", \", zRangeResult4));\n        // >>> two, three","section_id":"examples"},{"id":"examples-ex2","language":"go","code":"zrangeResult1, err := rdb.ZAdd(ctx, \"myzset\",\n\t\tredis.Z{Member: \"one\", Score: 1},\n\t\tredis.Z{Member: \"two\", Score: 2},\n\t\tredis.Z{Member: \"three\", Score: 3},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(zrangeResult1) // >>> 3\n\n\tzrangeResult2, err := rdb.ZRange(ctx, \"myzset\", 0, -1).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(zrangeResult2) // >>> [one two three]\n\n\tzrangeResult3, err := rdb.ZRange(ctx, \"myzset\", 2, 3).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(zrangeResult3) // >>> [three]\n\n\tzrangeResult4, err := rdb.ZRange(ctx, \"myzset\", -2, -1).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(zrangeResult4) // >>> [two three]","section_id":"examples"},{"id":"examples-ex3","language":"java","code":"CompletableFuture<Void> zRange1Example = asyncCommands.zadd(\"myzset\",\n                    ScoredValue.just(1d, \"one\"), ScoredValue.just(2d, \"two\"), ScoredValue.just(3d, \"three\"))\n                    .thenCompose(res5 -> {\n                        System.out.println(res5); // >>> 3\n                        return asyncCommands.zrange(\"myzset\", 0, -1);\n                    }).thenCompose(res6 -> {\n                        System.out.println(res6); // >>> [one, two, three]\n                        return asyncCommands.zrange(\"myzset\", 2, 3);\n                    }).thenCompose(res7 -> {\n                        System.out.println(res7); // >>> [three]\n                        return asyncCommands.zrange(\"myzset\", -2, -1);\n                    }).thenAccept(res8 -> {\n                        System.out.println(res8); // >>> [two, three]\n                    }).toCompletableFuture();","section_id":"examples"},{"id":"examples-ex4","language":"java","code":"Mono<Void> zRange1Example = reactiveCommands.zadd(\"myzset\",\n                    ScoredValue.just(1d, \"one\"), ScoredValue.just(2d, \"two\"), ScoredValue.just(3d, \"three\"))\n                    .doOnNext(res5 -> {\n                        System.out.println(res5); // >>> 3\n                    })\n                    .then(reactiveCommands.zrange(\"myzset\", 0, -1).collectList())\n                    .doOnNext(res6 -> {\n                        System.out.println(res6); // >>> [one, two, three]\n                    })\n                    .then(reactiveCommands.zrange(\"myzset\", 2, 3).collectList())\n                    .doOnNext(res7 -> {\n                        System.out.println(res7); // >>> [three]\n                    })\n                    .then(reactiveCommands.zrange(\"myzset\", -2, -1).collectList())\n                    .doOnNext(res8 -> {\n                        System.out.println(res8); // >>> [two, three]\n                    })\n                    .then();","section_id":"examples"},{"id":"examples-ex5","language":"java","code":"Map<String, Double> zRangeExampleParams1 = new HashMap<>();\n        zRangeExampleParams1.put(\"one\", 1.0);\n        zRangeExampleParams1.put(\"two\", 2.0);\n        zRangeExampleParams1.put(\"three\", 3.0);\n        long zRangeResult1 = jedis.zadd(\"myzset\", zRangeExampleParams1);\n        System.out.println(zRangeResult1);  // >>> 3\n\n        List<String> zRangeResult2 = jedis.zrange(\"myzset\", new ZRangeParams(0, -1));\n        System.out.println(String.join(\", \", zRangeResult2));   // >>> one, two, three\n\n        List<String> zRangeResult3 = jedis.zrange(\"myzset\", new ZRangeParams(2, 3));\n        System.out.println(String.join(\", \", zRangeResult3));   // >> three\n\n        List<String> zRangeResult4 = jedis.zrange(\"myzset\", new ZRangeParams(-2, -1));\n        System.out.println(String.join(\", \", zRangeResult4));   // >> two, three","section_id":"examples"},{"id":"examples-ex6","language":"javascript","code":"const val5 = await client.zAdd(\"myzset\", [\n  { value: 'one', score: 1 },\n  { value: 'two', score: 2 },\n  { value: 'three', score: 3 }\n]);\nconsole.log(val5);\n// returns 3\n\nconst val6 = await client.zRange('myzset', 0, -1);\nconsole.log(val6);\n// returns ['one', 'two', 'three']\n\nconst val7 = await client.zRange('myzset', 2, 3);\nconsole.log(val7);\n// returns ['three']\n\nconst val8 = await client.zRange('myzset', -2, -1);\nconsole.log(val8);\n// returns ['two', 'three']","section_id":"examples"},{"id":"examples-ex7","language":"javascript","code":"const res5 = await redis.zadd('myzset', 1, 'one', 2, 'two', 3, 'three');\nconsole.log(res5); // >>> 3\n\nconst res6 = await redis.zrange('myzset', 0, -1);\nconsole.log(res6); // >>> ['one', 'two', 'three']\n\nconst res7 = await redis.zrange('myzset', 2, 3);\nconsole.log(res7); // >>> ['three']\n\nconst res8 = await redis.zrange('myzset', -2, -1);\nconsole.log(res8); // >>> ['two', 'three']","section_id":"examples"},{"id":"examples-ex8","language":"javascript","code":"const val5 = await client.zAdd(\"myzset\", [\n  { value: 'one', score: 1 },\n  { value: 'two', score: 2 },\n  { value: 'three', score: 3 }\n]);\nconsole.log(val5);\n// returns 3\n\nconst val6 = await client.zRange('myzset', 0, -1);\nconsole.log(val6);\n// returns ['one', 'two', 'three']\n\nconst val7 = await client.zRange('myzset', 2, 3);\nconsole.log(val7);\n// returns ['three']\n\nconst val8 = await client.zRange('myzset', -2, -1);\nconsole.log(val8);\n// returns ['two', 'three']","section_id":"examples"},{"id":"examples-ex9","language":"javascript","code":"const res5 = await redis.zadd('myzset', 1, 'one', 2, 'two', 3, 'three');\nconsole.log(res5); // >>> 3\n\nconst res6 = await redis.zrange('myzset', 0, -1);\nconsole.log(res6); // >>> ['one', 'two', 'three']\n\nconst res7 = await redis.zrange('myzset', 2, 3);\nconsole.log(res7); // >>> ['three']\n\nconst res8 = await redis.zrange('myzset', -2, -1);\nconsole.log(res8); // >>> ['two', 'three']","section_id":"examples"},{"id":"examples-ex10","language":"php","code":"$res5 = $r->zadd('myzset', ['one' => 1, 'two' => 2, 'three' => 3]);\n        echo $res5 . PHP_EOL; // >>> 3\n\n        $res6 = $r->zrange('myzset', 0, -1);\n        echo json_encode($res6) . PHP_EOL; // >>> [\"one\",\"two\",\"three\"]\n\n        $res7 = $r->zrange('myzset', 2, 3);\n        echo json_encode($res7) . PHP_EOL; // >>> [\"three\"]\n\n        $res8 = $r->zrange('myzset', -2, -1);\n        echo json_encode($res8) . PHP_EOL; // >>> [\"two\",\"three\"]","section_id":"examples"},{"id":"examples-ex11","language":"python","code":"res = r.zadd(\"myzset\", {\"one\": 1, \"two\":2, \"three\":3})\nprint(res)\n# >>> 3\n\nres = r.zrange(\"myzset\", 0, -1)\nprint(res)\n# >>> ['one', 'two', 'three']\n\nres = r.zrange(\"myzset\", 2, 3)\nprint(res)\n# >>> ['three']\n\nres = r.zrange(\"myzset\", -2, -1)\nprint(res)\n# >>> ['two', 'three']","section_id":"examples"},{"id":"examples-ex12","language":"ruby","code":"res5 = r.zadd('myzset', [[1, 'one'], [2, 'two'], [3, 'three']])\nputs res5 # >>> 3\n\nres6 = r.zrange('myzset', 0, -1)\nputs res6.inspect # >>> [\"one\", \"two\", \"three\"]\n\nres7 = r.zrange('myzset', 2, 3)\nputs res7.inspect # >>> [\"three\"]\n\nres8 = r.zrange('myzset', -2, -1)\nputs res8.inspect # >>> [\"two\", \"three\"]","section_id":"examples"},{"id":"examples-ex13","language":"rust","code":"if let Ok(res5) = r.zadd_multiple(\"myzset\", &[(1, \"one\"), (2, \"two\"), (3, \"three\")]).await {\n            let res5: i32 = res5;\n            println!(\"{res5}\"); // >>> 3\n        }\n\n        if let Ok(res6) = r.zrange(\"myzset\", 0, -1).await {\n            let res6: Vec<String> = res6;\n            println!(\"{res6:?}\"); // >>> [\"one\", \"two\", \"three\"]\n        }\n\n        if let Ok(res7) = r.zrange(\"myzset\", 2, 3).await {\n            let res7: Vec<String> = res7;\n            println!(\"{res7:?}\"); // >>> [\"three\"]\n        }\n\n        if let Ok(res8) = r.zrange(\"myzset\", -2, -1).await {\n            let res8: Vec<String> = res8;\n            println!(\"{res8:?}\"); // >>> [\"two\", \"three\"]\n        }","section_id":"examples"},{"id":"examples-ex14","language":"rust","code":"if let Ok(res5) = r.zadd_multiple(\"myzset\", &[(1, \"one\"), (2, \"two\"), (3, \"three\")]) {\n            let res5: i32 = res5;\n            println!(\"{res5}\"); // >>> 3\n        }\n\n        if let Ok(res6) = r.zrange(\"myzset\", 0, -1) {\n            let res6: Vec<String> = res6;\n            println!(\"{res6:?}\"); // >>> [\"one\", \"two\", \"three\"]\n        }\n\n        if let Ok(res7) = r.zrange(\"myzset\", 2, 3) {\n            let res7: Vec<String> = res7;\n            println!(\"{res7:?}\"); // >>> [\"three\"]\n        }\n\n        if let Ok(res8) = r.zrange(\"myzset\", -2, -1) {\n            let res8: Vec<String> = res8;\n            println!(\"{res8:?}\"); // >>> [\"two\", \"three\"]\n        }","section_id":"examples"},{"id":"examples-ex15","language":"plaintext","code":"> DEL myzset\n(integer) 1\n> ZADD myzset 1 \"one\" 2 \"two\" 3 \"three\"\n(integer) 3\n> ZRANGE myzset 0 1 WITHSCORES\n1) \"one\"\n2) \"1\"\n3) \"two\"\n4) \"2\"","section_id":"examples"},{"id":"examples-ex16","language":"csharp","code":"long zRangeResult5 = db.SortedSetAdd(\n            \"myzset\",\n            [\n                new(\"one\", 1),\n                new(\"two\", 2),\n                new(\"three\", 3)\n            ]\n        );\n\n        SortedSetEntry[] zRangeResult6 = db.SortedSetRangeByRankWithScores(\"myzset\", 0, 1);\n        Console.WriteLine($\"{string.Join(\", \", zRangeResult6.Select(b => $\"{b.Element}: {b.Score}\"))}\");\n        // >>> one: 1, two: 2","section_id":"examples"},{"id":"examples-ex17","language":"go","code":"zRangeResult5, err := rdb.ZAdd(ctx, \"myzset\",\n\t\tredis.Z{Member: \"one\", Score: 1},\n\t\tredis.Z{Member: \"two\", Score: 2},\n\t\tredis.Z{Member: \"three\", Score: 3},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(zRangeResult5) // >>> 3\n\n\tzRangeResult6, err := rdb.ZRangeWithScores(ctx, \"myzset\", 0, 1).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(zRangeResult6) // >>> [{1 one} {2 two}]","section_id":"examples"},{"id":"examples-ex18","language":"java","code":"CompletableFuture<Void> zRange2Example = asyncCommands.zadd(\"myzset\",\n                    ScoredValue.just(1d, \"one\"), ScoredValue.just(2d, \"two\"), ScoredValue.just(3d, \"three\"))\n                    .thenCompose(res9 -> {\n                        return asyncCommands.zrangeWithScores(\"myzset\", 0, 1);\n                    }).thenAccept(res10 -> {\n                        System.out.println(res10);\n                        // >>> [ScoredValue[1.000000, one], ScoredValue[2.000000, two]]\n                    }).toCompletableFuture();","section_id":"examples"},{"id":"examples-ex19","language":"java","code":"Mono<Void> zRange2Example = reactiveCommands.zadd(\"myzset\",\n                    ScoredValue.just(1d, \"one\"), ScoredValue.just(2d, \"two\"), ScoredValue.just(3d, \"three\"))\n                    .then(reactiveCommands.zrangeWithScores(\"myzset\", 0, 1).collectList())\n                    .doOnNext(res9 -> {\n                        System.out.println(res9);\n                        // >>> [ScoredValue[1.000000, one], ScoredValue[2.000000, two]]\n                    })\n                    .then();","section_id":"examples"},{"id":"examples-ex20","language":"java","code":"Map<String, Double> zRangeExampleParams2 = new HashMap<>();\n        zRangeExampleParams2.put(\"one\", 1.0);\n        zRangeExampleParams2.put(\"two\", 2.0);\n        zRangeExampleParams2.put(\"three\", 3.0);\n        long zRangeResult5 = jedis.zadd(\"myzset\", zRangeExampleParams2);\n        System.out.println(zRangeResult5);  // >>> 3\n\n        List<Tuple> zRangeResult6 = jedis.zrangeWithScores(\"myzset\", new ZRangeParams(0, 1));\n\n        for (Tuple item: zRangeResult6) {\n            System.out.println(\"Element: \" + item.getElement() + \", Score: \" + item.getScore());\n        }\n        // >>> Element: one, Score: 1.0\n        // >>> Element: two, Score: 2.0","section_id":"examples"},{"id":"examples-ex21","language":"javascript","code":"const val9 = await client.zAdd(\"myzset\", [\n  { value: 'one', score: 1 },\n  { value: 'two', score: 2 },\n  { value: 'three', score: 3 }\n]);\nconsole.log(val9);\n// returns 3\n\nconst val10 = await client.zRangeWithScores('myzset', 0, 1);\nconsole.log(val10);\n// returns [{value: 'one', score: 1}, {value: 'two', score: 2}]","section_id":"examples"},{"id":"examples-ex22","language":"javascript","code":"await redis.zadd('myzset', 1, 'one', 2, 'two', 3, 'three');\n\nconst res9 = await redis.zrange('myzset', 0, 1, 'WITHSCORES');\nconsole.log(res9); // >>> ['one', '1', 'two', '2']","section_id":"examples"},{"id":"examples-ex23","language":"javascript","code":"const val9 = await client.zAdd(\"myzset\", [\n  { value: 'one', score: 1 },\n  { value: 'two', score: 2 },\n  { value: 'three', score: 3 }\n]);\nconsole.log(val9);\n// returns 3\n\nconst val10 = await client.zRangeWithScores('myzset', 0, 1);\nconsole.log(val10);\n// returns [{value: 'one', score: 1}, {value: 'two', score: 2}]","section_id":"examples"},{"id":"examples-ex24","language":"javascript","code":"await redis.zadd('myzset', 1, 'one', 2, 'two', 3, 'three');\n\nconst res9 = await redis.zrange('myzset', 0, 1, 'WITHSCORES');\nconsole.log(res9); // >>> ['one', '1', 'two', '2']","section_id":"examples"},{"id":"examples-ex25","language":"php","code":"$r->zadd('myzset', ['one' => 1, 'two' => 2, 'three' => 3]);\n\n        $res9 = $r->zrange('myzset', 0, 1, ['withscores' => true]);\n        echo json_encode($res9) . PHP_EOL; // >>> {\"one\":\"1\",\"two\":\"2\"}","section_id":"examples"},{"id":"examples-ex26","language":"python","code":"res = r.zadd(\"myzset\", {\"one\": 1, \"two\":2, \"three\":3})\nres = r.zrange(\"myzset\", 0, 1, withscores=True)\nprint(res)\n# >>> [('one', 1.0), ('two', 2.0)]","section_id":"examples"},{"id":"examples-ex27","language":"ruby","code":"r.zadd('myzset', [[1, 'one'], [2, 'two'], [3, 'three']])\n\nres9 = r.zrange('myzset', 0, 1, with_scores: true)\nputs res9.inspect # >>> [[\"one\", 1.0], [\"two\", 2.0]]","section_id":"examples"},{"id":"examples-ex28","language":"rust","code":"let _: Result<i32, _> = r.zadd_multiple(\"myzset\", &[(1, \"one\"), (2, \"two\"), (3, \"three\")]).await;\n\n        if let Ok(res9) = r.zrange_withscores(\"myzset\", 0, 1).await {\n            let res9: Vec<(String, f64)> = res9;\n            println!(\"{res9:?}\"); // >>> [(\"one\", 1.0), (\"two\", 2.0)]\n        }","section_id":"examples"},{"id":"examples-ex29","language":"rust","code":"let _: Result<i32, _> = r.zadd_multiple(\"myzset\", &[(1, \"one\"), (2, \"two\"), (3, \"three\")]);\n\n        if let Ok(res9) = r.zrange_withscores(\"myzset\", 0, 1) {\n            let res9: Vec<(String, f64)> = res9;\n            println!(\"{res9:?}\"); // >>> [(\"one\", 1.0), (\"two\", 2.0)]\n        }","section_id":"examples"},{"id":"examples-ex30","language":"plaintext","code":"> DEL myzset\n(integer) 1\n> ZADD myzset 1 \"one\" 2 \"two\" 3 \"three\"\n(integer) 3\n> ZRANGE myzset (1 +inf BYSCORE LIMIT 1 1\n1) \"three\"","section_id":"examples"},{"id":"examples-ex31","language":"csharp","code":"long zRangeResult7 = db.SortedSetAdd(\n            \"myzset\",\n            [\n                new(\"one\", 1),\n                new(\"two\", 2),\n                new(\"three\", 3)\n            ]\n        );\n\n        RedisValue[] zRangeResult8 = db.SortedSetRangeByScore(\n            \"myzset\",\n            1,\n            double.PositiveInfinity,\n            Exclude.Start,\n            skip: 1, take: 1\n        );\n        Console.WriteLine(string.Join(\", \", zRangeResult8));\n        // >>> three","section_id":"examples"},{"id":"examples-ex32","language":"go","code":"zRangeResult7, err := rdb.ZAdd(ctx, \"myzset\",\n\t\tredis.Z{Member: \"one\", Score: 1},\n\t\tredis.Z{Member: \"two\", Score: 2},\n\t\tredis.Z{Member: \"three\", Score: 3},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(zRangeResult7) // >>> 3\n\n\tzRangeResult8, err := rdb.ZRangeArgs(ctx,\n\t\tredis.ZRangeArgs{\n\t\t\tKey:     \"myzset\",\n\t\t\tByScore: true,\n\t\t\tStart:   \"(1\",\n\t\t\tStop:    \"+inf\",\n\t\t\tOffset:  1,\n\t\t\tCount:   1,\n\t\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(zRangeResult8) // >>> [three]","section_id":"examples"},{"id":"examples-ex33","language":"java","code":"CompletableFuture<Void> zRange3Example = asyncCommands.zadd(\"myzset\",\n                    ScoredValue.just(1d, \"one\"), ScoredValue.just(2d, \"two\"), ScoredValue.just(3d, \"three\"))\n                    .thenCompose(res11 -> {\n                        return asyncCommands.zrangebyscore(\"myzset\",\n                                Range.from(Range.Boundary.excluding(1d), Range.Boundary.unbounded()),\n                                Limit.create(1, 1));\n                    }).thenAccept(res12 -> {\n                        System.out.println(res12); // >>> [three]\n                    }).toCompletableFuture();","section_id":"examples"},{"id":"examples-ex34","language":"java","code":"Mono<Void> zRange3Example = reactiveCommands.zadd(\"myzset\",\n                    ScoredValue.just(1d, \"one\"), ScoredValue.just(2d, \"two\"), ScoredValue.just(3d, \"three\"))\n                    .then(reactiveCommands.zrangebyscore(\"myzset\",\n                            Range.from(Range.Boundary.excluding(1d), Range.Boundary.unbounded()),\n                            Limit.create(1, 1)).collectList())\n                    .doOnNext(res10 -> {\n                        System.out.println(res10); // >>> [three]\n                    })\n                    .then();","section_id":"examples"},{"id":"examples-ex35","language":"java","code":"Map<String, Double> zRangeExampleParams3 = new HashMap<>();\n        zRangeExampleParams3.put(\"one\", 1.0);\n        zRangeExampleParams3.put(\"two\", 2.0);\n        zRangeExampleParams3.put(\"three\", 3.0);\n        long zRangeResult7 = jedis.zadd(\"myzset\", zRangeExampleParams3);\n        System.out.println(zRangeResult7);  // >>> 3\n\n        List<String> zRangeResult8 = jedis.zrangeByScore(\"myzset\", \"(1\", \"+inf\", 1, 1);\n        System.out.println(String.join(\", \", zRangeResult8));   // >>> three","section_id":"examples"},{"id":"examples-ex36","language":"javascript","code":"const val11 = await client.zAdd(\"myzset\", [\n  { value: 'one', score: 1 },\n  { value: 'two', score: 2 },\n  { value: 'three', score: 3 }\n]);\nconsole.log(val11);\n// returns 3\n\nconst val12 = await client.zRange('myzset', 2, 3, { BY: 'SCORE', LIMIT: { offset: 1, count: 1 } });\nconsole.log(val12);\n// >>> ['three']","section_id":"examples"},{"id":"examples-ex37","language":"javascript","code":"await redis.zadd('myzset', 1, 'one', 2, 'two', 3, 'three');\n\nconst res10 = await redis.zrange('myzset', '(1', '+inf', 'BYSCORE', 'LIMIT', 1, 1);\nconsole.log(res10); // >>> ['three']","section_id":"examples"},{"id":"examples-ex38","language":"javascript","code":"const val11 = await client.zAdd(\"myzset\", [\n  { value: 'one', score: 1 },\n  { value: 'two', score: 2 },\n  { value: 'three', score: 3 }\n]);\nconsole.log(val11);\n// returns 3\n\nconst val12 = await client.zRange('myzset', 2, 3, { BY: 'SCORE', LIMIT: { offset: 1, count: 1 } });\nconsole.log(val12);\n// >>> ['three']","section_id":"examples"},{"id":"examples-ex39","language":"javascript","code":"await redis.zadd('myzset', 1, 'one', 2, 'two', 3, 'three');\n\nconst res10 = await redis.zrange('myzset', '(1', '+inf', 'BYSCORE', 'LIMIT', 1, 1);\nconsole.log(res10); // >>> ['three']","section_id":"examples"},{"id":"examples-ex40","language":"php","code":"$r->zadd('myzset', ['one' => 1, 'two' => 2, 'three' => 3]);\n\n        $res10 = $r->zrange('myzset', '(1', '+inf', ['byscore' => true, 'limit' => [1, 1]]);\n        echo json_encode($res10) . PHP_EOL; // >>> [\"three\"]","section_id":"examples"},{"id":"examples-ex41","language":"python","code":"res = r.zadd(\"myzset\", {\"one\": 1, \"two\":2, \"three\":3})\nres = r.zrange(\"myzset\", 2, 3, byscore=True, offset=1, num=1)\nprint(res)\n# >>> ['three']","section_id":"examples"},{"id":"examples-ex42","language":"ruby","code":"r.zadd('myzset', [[1, 'one'], [2, 'two'], [3, 'three']])\n\nres10 = r.zrange('myzset', '(1', '+inf', by_score: true, limit: [1, 1])\nputs res10.inspect # >>> [\"three\"]","section_id":"examples"},{"id":"examples-ex43","language":"rust","code":"let _: Result<i32, _> = r.zadd_multiple(\"myzset\", &[(1, \"one\"), (2, \"two\"), (3, \"three\")]).await;\n\n        if let Ok(res10) = r.zrangebyscore_limit(\"myzset\", \"(1\", \"+inf\", 1, 1).await {\n            let res10: Vec<String> = res10;\n            println!(\"{res10:?}\"); // >>> [\"three\"]\n        }","section_id":"examples"},{"id":"examples-ex44","language":"rust","code":"let _: Result<i32, _> = r.zadd_multiple(\"myzset\", &[(1, \"one\"), (2, \"two\"), (3, \"three\")]);\n\n        if let Ok(res10) = r.zrangebyscore_limit(\"myzset\", \"(1\", \"+inf\", 1, 1) {\n            let res10: Vec<String> = res10;\n            println!(\"{res10:?}\"); // >>> [\"three\"]\n        }","section_id":"examples"},{"id":"score-ranges-ex0","language":"plaintext","code":"ZRANGE zset (1 5 BYSCORE","section_id":"score-ranges"},{"id":"score-ranges-ex1","language":"plaintext","code":"ZRANGE zset (5 (10 BYSCORE","section_id":"score-ranges"},{"id":"reverse-ranges-ex0","language":"plaintext","code":"ZRANGE zset 5 10 REV","section_id":"reverse-ranges"},{"id":"reverse-ranges-ex1","language":"plaintext","code":"ZRANGE zset 10 5 REV BYSCORE","section_id":"reverse-ranges"}]}
{"schema_version":2,"id":"commands/zrangebylex","title":"ZRANGEBYLEX","url":"https://redis.io/docs/latest/commands/zrangebylex/","summary":"Returns members in a sorted set within a lexicographical range.","since":"2.8.9","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"3ff55bb9a801cf2c9d6f0fa2ffffa9a93afcab170aaf17bd7e2cbf4f7d6faa88","sections":[{"id":"overview","title":"Overview","role":"overview","text":"When all the elements in a sorted set are inserted with the same score, in order to force lexicographical ordering, this command returns all the elements in the sorted set at `key` with a value between `min` and `max`.\n\nIf the elements in the sorted set have different scores, the returned elements are unspecified.\n\nThe elements are considered to be ordered from lower to higher strings as compared byte-by-byte using the `memcmp()` C function. Longer strings are considered greater than shorter strings if the common part is identical.\n\nThe optional `LIMIT` argument can be used to only get a range of the matching\nelements (similar to _SELECT LIMIT offset, count_ in SQL). A negative `count`\nreturns all elements from the `offset`.\nKeep in mind that if `offset` is large, the sorted set needs to be traversed for\n`offset` elements before getting to the elements to return, which can add up to\nO(N) time complexity."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the sorted set.\n\n</details>\n\n<details open><summary><code>min</code></summary>\n\nThe minimum member, compared lexicographically. Prefix with `[` for an inclusive bound or `(` for an exclusive bound; use `-` for the lowest possible value.\n\n</details>\n\n<details open><summary><code>max</code></summary>\n\nThe maximum member, compared lexicographically. Prefix with `[` for an inclusive bound or `(` for an exclusive bound; use `+` for the highest possible value.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>LIMIT offset count</code></summary>\n\nSkip `offset` matching members and return up to `count` of them. A negative `count` returns all remaining members.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ZADD myzset 0 a 0 b 0 c 0 d 0 e 0 f 0 g\n(integer) 7\nredis> ZRANGEBYLEX myzset - [c\n1) \"a\"\n2) \"b\"\n3) \"c\"\nredis> ZRANGEBYLEX myzset - (c\n1) \"a\"\n2) \"b\"\nredis> ZRANGEBYLEX myzset [aaa (g\n1) \"b\"\n2) \"c\"\n3) \"d\"\n4) \"e\"\n5) \"f\""},{"id":"details","title":"Details","role":"content","text":""},{"id":"how-to-specify-intervals","title":"How to specify intervals","role":"content","text":"Valid *start* and *stop* must start with `(` or `[`, in order to specify\nif the range item is respectively exclusive or inclusive.\nThe special values of `+` or `-` for *start* and *stop* have the special\nmeaning or positively infinite and negatively infinite strings, so for\ninstance the command **ZRANGEBYLEX myzset - +** is guaranteed to return\nall the elements in the sorted set, if all the elements have the same\nscore."},{"id":"details-on-strings-comparison","title":"Details on strings comparison","role":"content","text":"Strings are compared as binary array of bytes. Because of how the ASCII character\nset is specified, this means that usually this also have the effect of comparing\nnormal ASCII characters in an obvious dictionary way. However this is not true\nif non plain ASCII strings are used (for example utf8 strings).\n\nHowever the user can apply a transformation to the encoded string so that\nthe first part of the element inserted in the sorted set will compare as the\nuser requires for the specific application. For example if I want to\nadd strings that will be compared in a case-insensitive way, but I still\nwant to retrieve the real case when querying, I can add strings in the\nfollowing way:\n\n    ZADD autocomplete 0 foo:Foo 0 bar:BAR 0 zap:zap\n\nBecause of the first *normalized* part in every element (before the colon character), we are forcing a given comparison, however after the range is queries using `ZRANGEBYLEX` the application can display to the user the second part of the string, after the colon.\n\nThe binary nature of the comparison allows to use sorted sets as a general\npurpose index, for example the first part of the element can be a 64 bit\nbig endian number: since big endian numbers have the most significant bytes\nin the initial positions, the binary comparison will match the numerical\ncomparison of the numbers. This can be used in order to implement range\nqueries on 64 bit values. As in the example below, after the first 8 bytes\nwe can store the value of the element we are actually indexing."},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Deprecated as of Redis v6.2.0. |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of elements in the specified score range.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of elements in the specified score range."}],"examples":[]}
{"schema_version":2,"id":"commands/zrangebyscore","title":"ZRANGEBYSCORE","url":"https://redis.io/docs/latest/commands/zrangebyscore/","summary":"Returns members in a sorted set within a range of scores.","since":"1.0.5","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"04879f70e8da58ddd0d700ab26465579604df89626c2b215314bc10122ac1f6b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns all the elements in the sorted set at `key` with a score between `min`\nand `max` (including elements with score equal to `min` or `max`).\nThe elements are considered to be ordered from low to high scores.\n\nThe elements having the same score are returned in lexicographical order (this\nfollows from a property of the sorted set implementation in Redis and does not\ninvolve further computation).\n\nThe optional `LIMIT` argument can be used to only get a range of the matching\nelements (similar to _SELECT LIMIT offset, count_ in SQL). A negative `count`\nreturns all elements from the `offset`.\nKeep in mind that if `offset` is large, the sorted set needs to be traversed for\n`offset` elements before getting to the elements to return, which can add up to\nO(N) time complexity.\n\nThe optional `WITHSCORES` argument makes the command return both the element and\nits score, instead of the element alone.\nThis option is available since Redis 2.0."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the sorted set.\n\n</details>\n\n<details open><summary><code>min</code></summary>\n\nThe minimum score. The bound is inclusive unless prefixed with `(`. Use `-inf` for no lower bound.\n\n</details>\n\n<details open><summary><code>max</code></summary>\n\nThe maximum score. The bound is inclusive unless prefixed with `(`. Use `+inf` for no upper bound.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>WITHSCORES</code></summary>\n\nAlso return the score of each member.\n\n</details>\n\n<details open><summary><code>LIMIT offset count</code></summary>\n\nSkip `offset` matching members and return up to `count` of them. A negative `count` returns all remaining members.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ZADD myzset 1 \"one\"\n(integer) 1\nredis> ZADD myzset 2 \"two\"\n(integer) 1\nredis> ZADD myzset 3 \"three\"\n(integer) 1\nredis> ZRANGEBYSCORE myzset -inf +inf\n1) \"one\"\n2) \"two\"\n3) \"three\"\nredis> ZRANGEBYSCORE myzset 1 2\n1) \"one\"\n2) \"two\"\nredis> ZRANGEBYSCORE myzset (1 2\n1) \"two\"\nredis> ZRANGEBYSCORE myzset (1 (2\n(empty array)"},{"id":"details","title":"Details","role":"content","text":""},{"id":"exclusive-intervals-and-infinity","title":"Exclusive intervals and infinity","role":"content","text":"`min` and `max` can be `-inf` and `+inf`, so that you are not required to know\nthe highest or lowest score in the sorted set to get all elements from or up to\na certain score.\n\nBy default, the interval specified by `min` and `max` is closed (inclusive).\nIt is possible to specify an open interval (exclusive) by prefixing the score\nwith the character `(`.\nFor example:\n\n[code example]\n\nWill return all elements with `1 < score <= 5` while:\n\n[code example]\n\nWill return all the elements with `5 < score < 10` (5 and 10 excluded)."},{"id":"pattern-weighted-random-selection-of-an-element","title":"Pattern: weighted random selection of an element","role":"content","text":"Normally `ZRANGEBYSCORE` is simply used in order to get range of items\nwhere the score is the indexed integer key, however it is possible to do less\nobvious things with the command.\n\nFor example a common problem when implementing Markov chains and other algorithms\nis to select an element at random from a set, but different elements may have\ndifferent weights that change how likely it is they are picked.\n\nThis is how we use this command in order to mount such an algorithm:\n\nImagine you have elements A, B and C with weights 1, 2 and 3.\nYou compute the sum of the weights, which is 1+2+3 = 6\n\nAt this point you add all the elements into a sorted set using this algorithm:\n\n[code example]\n\nThis means that you set:\n\n[code example]\n\nSince this involves approximations, in order to avoid C is set to,\nlike, 0.998 instead of 1, we just modify the above algorithm to make sure\nthe last score is 1 (left as an exercise for the reader...).\n\nAt this point, each time you want to get a weighted random element,\njust compute a random number between 0 and 1 (which is like calling\n`rand()` in most languages), so you can just do:\n\n    RANDOM_ELE = ZRANGEBYSCORE key RAND() +inf LIMIT 0 1"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Deprecated as of Redis v6.2.0. |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n* [Array reply](../../develop/reference/protocol-spec#arrays): a list of the members with, optionally, their scores in the specified score range.\n\n**RESP3:**\n\n* [Array reply](../../develop/reference/protocol-spec#arrays): a list of the members with, optionally, their scores in the specified score range."}],"examples":[{"id":"exclusive-intervals-and-infinity-ex0","language":"plaintext","code":"ZRANGEBYSCORE zset (1 5","section_id":"exclusive-intervals-and-infinity"},{"id":"exclusive-intervals-and-infinity-ex1","language":"plaintext","code":"ZRANGEBYSCORE zset (5 (10","section_id":"exclusive-intervals-and-infinity"},{"id":"pattern-weighted-random-selection-of-an-element-ex0","language":"plaintext","code":"SUM = ELEMENTS.TOTAL_WEIGHT // 6 in this case.\nSCORE = 0\nFOREACH ELE in ELEMENTS\n    SCORE += ELE.weight / SUM\n    ZADD KEY SCORE ELE\nEND","section_id":"pattern-weighted-random-selection-of-an-element"},{"id":"pattern-weighted-random-selection-of-an-element-ex1","language":"plaintext","code":"A to score 0.16\nB to score .5\nC to score 1","section_id":"pattern-weighted-random-selection-of-an-element"}]}
{"schema_version":2,"id":"commands/zrangestore","title":"ZRANGESTORE","url":"https://redis.io/docs/latest/commands/zrangestore/","summary":"Stores a range of members from sorted set in a key.","since":"6.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:43+01:00","page_type":"content","content_hash":"5105f7b4db24e922e82a4834adb80a16c7d9dd9ec1f13f1a2715ef1402e441c4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nThis command is like [`ZRANGE`](https://redis.io/docs/latest/commands/zrange), but stores the result in the `<dst>` destination key."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>dst</code></summary>\n\nThe key to store the resulting sorted set in.\n\n</details>\n\n<details open><summary><code>src</code></summary>\n\nThe source sorted-set key.\n\n</details>\n\n<details open><summary><code>min</code></summary>\n\nThe start of the range. By default a zero-based index (negatives count from the end); a score bound with `BYSCORE`; or a lexicographical bound with `BYLEX`.\n\n</details>\n\n<details open><summary><code>max</code></summary>\n\nThe end of the range, inclusive. Interpreted the same way as `min`.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>BYSCORE | BYLEX</code></summary>\n\nInterpret the range as a score range (`BYSCORE`) or a lexicographical range (`BYLEX`) instead of an index range.\n\n</details>\n\n<details open><summary><code>REV</code></summary>\n\nReverse the ordering so results are returned from high to low.\n\n</details>\n\n<details open><summary><code>LIMIT offset count</code></summary>\n\nSkip `offset` matching members and return up to `count` of them. Requires `BYSCORE` or `BYLEX`. A negative `count` returns all remaining members.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ZADD srczset 1 \"one\" 2 \"two\" 3 \"three\" 4 \"four\"\n(integer) 4\nredis> ZRANGESTORE dstzset srczset 2 -1\n(integer) 2\nredis> ZRANGE dstzset 0 -1\n1) \"three\"\n2) \"four\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of elements in the resulting sorted set.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of elements in the resulting sorted set."}],"examples":[]}
{"schema_version":2,"id":"commands/zrank","title":"ZRANK","url":"https://redis.io/docs/latest/commands/zrank/","summary":"Returns the index of a member in a sorted set ordered by ascending scores.","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:43+01:00","page_type":"content","content_hash":"61e8b4a3ab89575ccc8f01dae7096f7240f20b3e599e956af0d99e2ed880ef1a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns the rank of `member` in the sorted set stored at `key`, with the scores\nordered from low to high.\nThe rank (or index) is 0-based, which means that the member with the lowest\nscore has rank `0`.\n\nThe optional `WITHSCORE` argument supplements the command's reply with the score of the element returned.\n\nUse [`ZREVRANK`](https://redis.io/docs/latest/commands/zrevrank) to get the rank of an element with the scores ordered from high\nto low."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the sorted set.\n\n</details>\n\n<details open><summary><code>member</code></summary>\n\nThe member whose rank to return.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>WITHSCORE</code></summary>\n\nAlso return the member's score along with its rank.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ZADD myzset 1 \"one\"\n(integer) 1\nredis> ZADD myzset 2 \"two\"\n(integer) 1\nredis> ZADD myzset 3 \"three\"\n(integer) 1\nredis> ZRANK myzset \"three\"\n(integer) 2\nredis> ZRANK myzset \"four\"\n(nil)\nredis> ZRANK myzset \"three\" WITHSCORE\n1) (integer) 2\n2) \"3\"\nredis> ZRANK myzset \"four\" WITHSCORE\n(nil)"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): if the key does not exist or the member does not exist in the sorted set.\n* [Integer reply](../../develop/reference/protocol-spec#integers): the rank of the member when _WITHSCORE_ is not used.\n* [Array reply](../../develop/reference/protocol-spec#arrays): the rank and score of the member when _WITHSCORE_ is used.\n\n**RESP3:**\n\nOne of the following:\n* [Null reply](../../develop/reference/protocol-spec#nulls): if the key does not exist or the member does not exist in the sorted set.\n* [Integer reply](../../develop/reference/protocol-spec#integers): the rank of the member when _WITHSCORE_ is not used.\n* [Array reply](../../develop/reference/protocol-spec#arrays): the rank and score of the member when _WITHSCORE_ is used."}],"examples":[]}
{"schema_version":2,"id":"commands/zrem","title":"ZREM","url":"https://redis.io/docs/latest/commands/zrem/","summary":"Removes one or more members from a sorted set. Deletes the sorted set if all members were removed.","since":"1.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"bbef965cb28d8d60cfd177391ddcbac61484486820cf0265e2402396d8f12c6a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Removes the specified members from the sorted set stored at `key`.\nNon existing members are ignored.\n\nAn error is returned when `key` exists and does not hold a sorted set."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the sorted set.\n\n</details>\n\n<details open><summary><code>member [member ...]</code></summary>\n\nOne or more members to remove from the sorted set.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ZADD myzset 1 \"one\"\n(integer) 1\nredis> ZADD myzset 2 \"two\"\n(integer) 1\nredis> ZADD myzset 3 \"three\"\n(integer) 1\nredis> ZREM myzset \"two\"\n(integer) 1\nredis> ZRANGE myzset 0 -1 WITHSCORES\n1) \"one\"\n2) \"1\"\n3) \"three\"\n4) \"3\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of members removed from the sorted set, not including non-existing members.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of members removed from the sorted set, not including non-existing members."}],"examples":[]}
{"schema_version":2,"id":"commands/zremrangebylex","title":"ZREMRANGEBYLEX","url":"https://redis.io/docs/latest/commands/zremrangebylex/","summary":"Removes members in a sorted set within a lexicographical range. Deletes the sorted set if all members were removed.","since":"2.8.9","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:43+01:00","page_type":"content","content_hash":"a928b8106e99999e387f896916f54dbafd027e7f49df12d34af7d799cc4272a0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"When all the elements in a sorted set are inserted with the same score, in order to force lexicographical ordering, this command removes all elements in the sorted set stored at `key` between the lexicographical range specified by `min` and `max`.\n\nThe meaning of `min` and `max` are the same of the [`ZRANGEBYLEX`](https://redis.io/docs/latest/commands/zrangebylex) command. Similarly, this command actually removes the same elements that [`ZRANGEBYLEX`](https://redis.io/docs/latest/commands/zrangebylex) would return if called with the same `min` and `max` arguments."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the sorted set.\n\n</details>\n\n<details open><summary><code>min</code></summary>\n\nThe minimum member, compared lexicographically. Prefix with `[` for an inclusive bound or `(` for an exclusive bound; use `-` for the lowest possible value.\n\n</details>\n\n<details open><summary><code>max</code></summary>\n\nThe maximum member, compared lexicographically. Prefix with `[` for an inclusive bound or `(` for an exclusive bound; use `+` for the highest possible value.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ZADD myzset 0 aaaa 0 b 0 c 0 d 0 e\n(integer) 5\nredis> ZADD myzset 0 foo 0 zap 0 zip 0 ALPHA 0 alpha\n(integer) 5\nredis> ZRANGE myzset 0 -1\n 1) \"ALPHA\"\n 2) \"aaaa\"\n 3) \"alpha\"\n 4) \"b\"\n 5) \"c\"\n 6) \"d\"\n 7) \"e\"\n 8) \"foo\"\n 9) \"zap\"\n10) \"zip\"\nredis> ZREMRANGEBYLEX myzset [alpha [omega\n(integer) 6\nredis> ZRANGE myzset 0 -1\n1) \"ALPHA\"\n2) \"aaaa\"\n3) \"zap\"\n4) \"zip\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of members removed.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): Number of members removed."}],"examples":[]}
{"schema_version":2,"id":"commands/zremrangebyrank","title":"ZREMRANGEBYRANK","url":"https://redis.io/docs/latest/commands/zremrangebyrank/","summary":"Removes members in a sorted set within a range of indexes. Deletes the sorted set if all members were removed.","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"bf6dfe6324d7d73f9042550e04f5c396212fcbc2fc276532600e244eba1c0a38","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Removes all elements in the sorted set stored at `key` with rank between `start`\nand `stop`.\nBoth `start` and `stop` are `0` -based indexes with `0` being the element with\nthe lowest score.\nThese indexes can be negative numbers, where they indicate offsets starting at\nthe element with the highest score.\nFor example: `-1` is the element with the highest score, `-2` the element with\nthe second highest score and so forth."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the sorted set.\n\n</details>\n\n<details open><summary><code>start</code></summary>\n\nThe start index, zero-based. Negative values count from the end of the sorted set (`-1` is the member with the highest score).\n\n</details>\n\n<details open><summary><code>stop</code></summary>\n\nThe stop index, zero-based and inclusive. Negative values count from the end of the sorted set.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ZADD myzset 1 \"one\"\n(integer) 1\nredis> ZADD myzset 2 \"two\"\n(integer) 1\nredis> ZADD myzset 3 \"three\"\n(integer) 1\nredis> ZREMRANGEBYRANK myzset 0 1\n(integer) 2\nredis> ZRANGE myzset 0 -1 WITHSCORES\n1) \"three\"\n2) \"3\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of members removed.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): Number of members removed."}],"examples":[]}
{"schema_version":2,"id":"commands/zremrangebyscore","title":"ZREMRANGEBYSCORE","url":"https://redis.io/docs/latest/commands/zremrangebyscore/","summary":"Removes members in a sorted set within a range of scores. Deletes the sorted set if all members were removed.","since":"1.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"2fcc0fb26e3f20aec805260d36d0eaa28e14e0bf5607af0d15ede4f50eb125e1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Removes all elements in the sorted set stored at `key` with a score between\n`min` and `max` (inclusive)."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the sorted set.\n\n</details>\n\n<details open><summary><code>min</code></summary>\n\nThe minimum score. The bound is inclusive unless prefixed with `(`. Use `-inf` for no lower bound.\n\n</details>\n\n<details open><summary><code>max</code></summary>\n\nThe maximum score. The bound is inclusive unless prefixed with `(`. Use `+inf` for no upper bound.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ZADD myzset 1 \"one\"\n(integer) 1\nredis> ZADD myzset 2 \"two\"\n(integer) 1\nredis> ZADD myzset 3 \"three\"\n(integer) 1\nredis> ZREMRANGEBYSCORE myzset -inf (2\n(integer) 1\nredis> ZRANGE myzset 0 -1 WITHSCORES\n1) \"two\"\n2) \"2\"\n3) \"three\"\n4) \"3\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of members removed.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): Number of members removed."}],"examples":[]}
{"schema_version":2,"id":"commands/zrevrange","title":"ZREVRANGE","url":"https://redis.io/docs/latest/commands/zrevrange/","summary":"Returns members in a sorted set within a range of indexes in reverse order.","since":"1.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:43+01:00","page_type":"content","content_hash":"505d7cac04d18687ea71658a4a43713d2218c152ec58b6ffbccac410bfde5320","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns the specified range of elements in the sorted set stored at `key`.\nThe elements are considered to be ordered from the highest to the lowest score.\nDescending lexicographical order is used for elements with equal score.\n\nApart from the reversed ordering, `ZREVRANGE` is similar to [`ZRANGE`](https://redis.io/docs/latest/commands/zrange)."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the sorted set.\n\n</details>\n\n<details open><summary><code>start</code></summary>\n\nThe start index, zero-based. Negative values count from the end of the sorted set (`-1` is the member with the highest score).\n\n</details>\n\n<details open><summary><code>stop</code></summary>\n\nThe stop index, zero-based and inclusive. Negative values count from the end of the sorted set.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>WITHSCORES</code></summary>\n\nAlso return the score of each member.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ZADD myzset 1 \"one\"\n(integer) 1\nredis> ZADD myzset 2 \"two\"\n(integer) 1\nredis> ZADD myzset 3 \"three\"\n(integer) 1\nredis> ZREVRANGE myzset 0 -1\n1) \"three\"\n2) \"two\"\n3) \"one\"\nredis> ZREVRANGE myzset 2 3\n1) \"one\"\nredis> ZREVRANGE myzset -2 -1\n1) \"two\"\n2) \"one\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Deprecated as of Redis v6.2.0. |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n* [Array reply](../../develop/reference/protocol-spec#arrays): a list of members in the specified range, optionally with their scores if _WITHSCORE_ was used.\n\n**RESP3:**\n\n* [Array reply](../../develop/reference/protocol-spec#arrays): a list of members in the specified range, optionally with their scores if _WITHSCORE_ was used."}],"examples":[]}
{"schema_version":2,"id":"commands/zrevrangebylex","title":"ZREVRANGEBYLEX","url":"https://redis.io/docs/latest/commands/zrevrangebylex/","summary":"Returns members in a sorted set within a lexicographical range in reverse order.","since":"2.8.9","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:43+01:00","page_type":"content","content_hash":"3f56522eac04173ea339d718a749955e527790e61e62509041f5a1ed2e70db1a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"When all the elements in a sorted set are inserted with the same score, in order to force lexicographical ordering, this command returns all the elements in the sorted set at `key` with a value between `max` and `min`.\n\nApart from the reversed ordering, `ZREVRANGEBYLEX` is similar to [`ZRANGEBYLEX`](https://redis.io/docs/latest/commands/zrangebylex)."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the sorted set.\n\n</details>\n\n<details open><summary><code>max</code></summary>\n\nThe maximum member, compared lexicographically. Prefix with `[` for an inclusive bound or `(` for an exclusive bound; use `+` for the highest possible value.\n\n</details>\n\n<details open><summary><code>min</code></summary>\n\nThe minimum member, compared lexicographically. Prefix with `[` for an inclusive bound or `(` for an exclusive bound; use `-` for the lowest possible value.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>LIMIT offset count</code></summary>\n\nSkip `offset` matching members and return up to `count` of them. A negative `count` returns all remaining members.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ZADD myzset 0 a 0 b 0 c 0 d 0 e 0 f 0 g\n(integer) 7\nredis> ZREVRANGEBYLEX myzset [c -\n1) \"c\"\n2) \"b\"\n3) \"a\"\nredis> ZREVRANGEBYLEX myzset (c -\n1) \"b\"\n2) \"a\"\nredis> ZREVRANGEBYLEX myzset (g [aaa\n1) \"f\"\n2) \"e\"\n3) \"d\"\n4) \"c\"\n5) \"b\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Deprecated as of Redis v6.2.0. |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): a list of members in the specified score range.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): List of the elements in the specified score range."}],"examples":[]}
{"schema_version":2,"id":"commands/zrevrangebyscore","title":"ZREVRANGEBYSCORE","url":"https://redis.io/docs/latest/commands/zrevrangebyscore/","summary":"Returns members in a sorted set within a range of scores in reverse order.","since":"2.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:43+01:00","page_type":"content","content_hash":"6ba4d66f7b85386b7bf9ce3dc94b6393b9d4c4782ac7fcb5c0a7f2d0043afb9f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns all the elements in the sorted set at `key` with a score between `max`\nand `min` (including elements with score equal to `max` or `min`).\nIn contrary to the default ordering of sorted sets, for this command the\nelements are considered to be ordered from high to low scores.\n\nThe elements having the same score are returned in reverse lexicographical\norder.\n\nApart from the reversed ordering, `ZREVRANGEBYSCORE` is similar to\n[`ZRANGEBYSCORE`](https://redis.io/docs/latest/commands/zrangebyscore)."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the sorted set.\n\n</details>\n\n<details open><summary><code>max</code></summary>\n\nThe maximum score. The bound is inclusive unless prefixed with `(`. Use `+inf` for no upper bound.\n\n</details>\n\n<details open><summary><code>min</code></summary>\n\nThe minimum score. The bound is inclusive unless prefixed with `(`. Use `-inf` for no lower bound.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>WITHSCORES</code></summary>\n\nAlso return the score of each member.\n\n</details>\n\n<details open><summary><code>LIMIT offset count</code></summary>\n\nSkip `offset` matching members and return up to `count` of them. A negative `count` returns all remaining members.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ZADD myzset 1 \"one\"\n(integer) 1\nredis> ZADD myzset 2 \"two\"\n(integer) 1\nredis> ZADD myzset 3 \"three\"\n(integer) 1\nredis> ZREVRANGEBYSCORE myzset +inf -inf\n1) \"three\"\n2) \"two\"\n3) \"one\"\nredis> ZREVRANGEBYSCORE myzset 2 1\n1) \"two\"\n2) \"one\"\nredis> ZREVRANGEBYSCORE myzset 2 (1\n1) \"two\"\nredis> ZREVRANGEBYSCORE myzset (2 (1\n(empty array)"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Deprecated as of Redis v6.2.0. |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n* [Array reply](../../develop/reference/protocol-spec#arrays): a list of the members and, optionally, their scores in the specified score range.\n\n**RESP3:**\n\n* [Array reply](../../develop/reference/protocol-spec#arrays): a list of the members and, optionally, their scores in the specified score range."}],"examples":[]}
{"schema_version":2,"id":"commands/zrevrank","title":"ZREVRANK","url":"https://redis.io/docs/latest/commands/zrevrank/","summary":"Returns the index of a member in a sorted set ordered by descending scores.","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:43+01:00","page_type":"content","content_hash":"72450c04c538306a99d7ba4ebb6b87ac5227a5b8b330c1ba44ba160eac241c58","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns the rank of `member` in the sorted set stored at `key`, with the scores\nordered from high to low.\nThe rank (or index) is 0-based, which means that the member with the highest\nscore has rank `0`.\n\nThe optional `WITHSCORE` argument supplements the command's reply with the score of the element returned.\n\nUse [`ZRANK`](https://redis.io/docs/latest/commands/zrank) to get the rank of an element with the scores ordered from low to\nhigh."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the sorted set.\n\n</details>\n\n<details open><summary><code>member</code></summary>\n\nThe member whose rank to return.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>WITHSCORE</code></summary>\n\nAlso return the member's score along with its rank.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ZADD myzset 1 \"one\"\n(integer) 1\nredis> ZADD myzset 2 \"two\"\n(integer) 1\nredis> ZADD myzset 3 \"three\"\n(integer) 1\nredis> ZREVRANK myzset \"one\"\n(integer) 2\nredis> ZREVRANK myzset \"four\"\n(nil)\nredis> ZREVRANK myzset \"three\" WITHSCORE\n1) (integer) 0\n2) \"3\"\nredis> ZREVRANK myzset \"four\" WITHSCORE\n(nil)"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): if the key does not exist or the member does not exist in the sorted set.\n* [Integer reply](../../develop/reference/protocol-spec#integers): The rank of the member when _WITHSCORE_ is not used.\n* [Array reply](../../develop/reference/protocol-spec#arrays): The rank and score of the member when _WITHSCORE_ is used.\n\n**RESP3:**\n\nOne of the following:\n* [Null reply](../../develop/reference/protocol-spec#nulls): if the key does not exist or the member does not exist in the sorted set.\n* [Integer reply](../../develop/reference/protocol-spec#integers): The rank of the member when _WITHSCORE_ is not used.\n* [Array reply](../../develop/reference/protocol-spec#arrays): The rank and score of the member when _WITHSCORE_ is used."}],"examples":[]}
{"schema_version":2,"id":"commands/zscan","title":"ZSCAN","url":"https://redis.io/docs/latest/commands/zscan/","summary":"Iterates over members and scores of a sorted set.","since":"2.8.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:43+01:00","page_type":"content","content_hash":"efd9aacefe28a098ab7e3f5653dd98fe2098fb985785ae5aa8a793e96ef80c4f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"See [`SCAN`](https://redis.io/docs/latest/commands/scan) for `ZSCAN` documentation."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the sorted set.\n\n</details>\n\n<details open><summary><code>cursor</code></summary>\n\nThe cursor value. Start an iteration with `0`, then use the cursor returned by each call as the argument to the next call.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>MATCH pattern</code></summary>\n\nOnly return members matching the given glob-style pattern.\n\n</details>\n\n<details open><summary><code>COUNT count</code></summary>\n\nA hint for the number of members to return per iteration. The default is 10.\n\n</details>"},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): cursor and scan response in array form.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): cursor and scan response in array form."}],"examples":[]}
{"schema_version":2,"id":"commands/zscore","title":"ZSCORE","url":"https://redis.io/docs/latest/commands/zscore/","summary":"Returns the score of a member in a sorted set.","since":"1.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-24T11:07:00+02:00","page_type":"content","content_hash":"3864aa6071d325f25315268246affb0ca0fbfa20031207f12b9f0c2240e68d6f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Returns the score of `member` in the sorted set at `key`.\n\nIf `member` does not exist in the sorted set, or `key` does not exist, `nil` is\nreturned."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>key</code></summary>\n\nThe name of the key that holds the sorted set.\n\n</details>\n\n<details open><summary><code>member</code></summary>\n\nThe member whose score to return.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ZADD myzset 1 \"one\"\n(integer) 1\nredis> ZSCORE myzset \"one\"\n\"1\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\nOne of the following:\n* [Bulk string reply](../../develop/reference/protocol-spec#bulk-strings): the score of the member (a double-precision floating point number), represented as a string.\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): if _member_ does not exist in the sorted set, or the key does not exist.\n\n**RESP3:**\n\nOne of the following:\n* [Double reply](../../develop/reference/protocol-spec#doubles): the score of the member (a double-precision floating point number).\n* [Nil reply](../../develop/reference/protocol-spec#bulk-strings): if _member_ does not exist in the sorted set, or the key does not exist."}],"examples":[]}
{"schema_version":2,"id":"commands/zunion","title":"ZUNION","url":"https://redis.io/docs/latest/commands/zunion/","summary":"Returns the union of multiple sorted sets.","since":"6.2.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:43+01:00","page_type":"content","content_hash":"1d1a2e5825ddf4ed38fe60d28b81cd10d94cb764e1789fd9593b8b54f1a9c4e0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nThis command is similar to [`ZUNIONSTORE`](https://redis.io/docs/latest/commands/zunionstore), but instead of storing the resulting\nsorted set, it is returned to the client.\n\nFor a description of the `WEIGHTS` and `AGGREGATE` options, see [`ZUNIONSTORE`](https://redis.io/docs/latest/commands/zunionstore)."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>numkeys</code></summary>\n\nThe number of keys that follow.\n\n</details>\n\n<details open><summary><code>key [key ...]</code></summary>\n\nOne or more sorted-set keys to union.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>WEIGHTS weight [weight ...]</code></summary>\n\nA multiplication factor for each input set; each set's scores are multiplied by its weight before aggregation. Defaults to 1 for every set.\n\n</details>\n\n<details open><summary><code>AGGREGATE SUM | MIN | MAX | COUNT</code></summary>\n\nHow to combine the scores of members that exist in multiple sets: `SUM` (the default), `MIN`, `MAX`, or `COUNT`.\n\n</details>\n\n<details open><summary><code>WITHSCORES</code></summary>\n\nAlso return the score of each member.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ZADD zset1 1 \"one\"\n(integer) 1\nredis> ZADD zset1 2 \"two\"\n(integer) 1\nredis> ZADD zset2 1 \"one\"\n(integer) 1\nredis> ZADD zset2 2 \"two\"\n(integer) 1\nredis> ZADD zset2 3 \"three\"\n(integer) 1\nredis> ZUNION 2 zset1 zset2\n1) \"one\"\n2) \"three\"\n3) \"two\"\nredis> ZUNION 2 zset1 zset2 WITHSCORES\n1) \"one\"\n2) \"2\"\n3) \"three\"\n4) \"3\"\n5) \"two\"\n6) \"4\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): the result of the union with, optionally, their scores when _WITHSCORES_ is used.\n\n**RESP3:**\n\n[Array reply](../../develop/reference/protocol-spec#arrays): the result of the union with, optionally, their scores when _WITHSCORES_ is used."}],"examples":[]}
{"schema_version":2,"id":"commands/zunionstore","title":"ZUNIONSTORE","url":"https://redis.io/docs/latest/commands/zunionstore/","summary":"Stores the union of multiple sorted sets in a key.","since":"2.0.0","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T09:48:43+01:00","page_type":"content","content_hash":"796fec303b74990b56dd1ae2bb0ed22be38e20f9740ad9f8295d8db3660373e0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This command's behavior varies in clustered Redis environments. See the [multi-key operations](https://redis.io/docs/latest/develop/using-commands/multi-key-operations) page for more information.\n\n\nComputes the union of `numkeys` sorted sets given by the specified keys, and\nstores the result in `destination`.\nIt is mandatory to provide the number of input keys (`numkeys`) before passing\nthe input keys and the other (optional) arguments.\n\nBy default, the resulting score of an element is the sum of its scores in the\nsorted sets where it exists.\n\nUsing the `WEIGHTS` option, it is possible to specify a multiplication factor for\neach input sorted set. Each element's score is multiplied by its corresponding\nweight before aggregation.\nWhen `WEIGHTS` is not given, the multiplication factors default to `1`.\n\nWith the `AGGREGATE` option, it is possible to specify how the results of the\nunion are aggregated.\nThis option defaults to `SUM`, where the score of an element is summed across\nthe inputs where it exists.\nWhen this option is set to either `MIN` or `MAX`, the resulting set will contain\nthe minimum or maximum score of an element across the inputs where it exists.\nFor `SUM`, `MIN`, and `MAX`, each element's score is multiplied by its\ncorresponding weight before aggregation.\n\nWhen `AGGREGATE COUNT` is specified, the original element scores are ignored\nentirely. The resulting score for each element is determined by which input sets\ncontain it, optionally scaled by `WEIGHTS`:\n\n- Without `WEIGHTS`, each input set containing the element contributes `1` to\n  its score — effectively counting set membership.\n- With `WEIGHTS`, each input set containing the element contributes its\n  corresponding weight, so the score becomes the sum of those weights.\n\nThis enables a common use case: counting set membership frequency directly at\nthe command level, without application-side workarounds.\n\nIf `destination` already exists, it is overwritten."},{"id":"required-arguments","title":"Required arguments","role":"content","text":"<details open><summary><code>destination</code></summary>\n\nThe key to store the resulting sorted set in.\n\n</details>\n\n<details open><summary><code>numkeys</code></summary>\n\nThe number of keys that follow.\n\n</details>\n\n<details open><summary><code>key [key ...]</code></summary>\n\nOne or more sorted-set keys to union.\n\n</details>"},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"<details open><summary><code>WEIGHTS weight [weight ...]</code></summary>\n\nA multiplication factor for each input set; each set's scores are multiplied by its weight before aggregation. Defaults to 1 for every set.\n\n</details>\n\n<details open><summary><code>AGGREGATE SUM | MIN | MAX | COUNT</code></summary>\n\nHow to combine the scores of members that exist in multiple sets: `SUM` (the default), `MIN`, `MAX`, or `COUNT`.\n\n</details>"},{"id":"examples","title":"Examples","role":"example","text":"redis> ZADD zset1 1 \"one\"\n(integer) 1\nredis> ZADD zset1 2 \"two\"\n(integer) 1\nredis> ZADD zset2 1 \"one\"\n(integer) 1\nredis> ZADD zset2 2 \"two\"\n(integer) 1\nredis> ZADD zset2 3 \"three\"\n(integer) 1\nredis> ZUNIONSTORE out 2 zset1 zset2 WEIGHTS 2 3\n(integer) 3\nredis> ZRANGE out 0 -1 WITHSCORES\n1) \"one\"\n2) \"5\"\n3) \"three\"\n4) \"9\"\n5) \"two\"\n6) \"10\""},{"id":"redis-software-and-redis-cloud-compatibility","title":"Redis Software and Redis Cloud compatibility","role":"content","text":"| Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:----------------------|:-----------------|:------|\n| <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"return-information","title":"Return information","role":"returns","text":"**RESP2:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of elements in the resulting sorted set.\n\n**RESP3:**\n\n[Integer reply](../../develop/reference/protocol-spec#integers): the number of elements in the resulting sorted set."}],"examples":[]}
{"schema_version":2,"id":"develop/ai/agent-builder/agent-concepts","title":"How agents work","url":"https://redis.io/docs/latest/develop/ai/agent-builder/agent-concepts/","summary":"Learn how AI agents work and why Redis is the perfect foundation for building intelligent systems","tags":["docs","develop","ai"],"last_updated":"2026-10-02T07:18:00-07:00","page_type":"content","content_hash":"3b9051de5128f8948ec026aee6f3cbadd1a50113d815af2ae8f95b5e35ec378e","sections":[{"id":"how-ai-agents-work","title":"How AI agents work","role":"overview","text":"AI agents are autonomous systems that go far beyond simple chatbots. They combine large language models (LLMs) with external tools, memory, and planning capabilities to accomplish complex tasks.\n\n**Key differences from chatbots:**\n- Maintain state across multiple conversations\n- Reason through problems step-by-step\n- Take actions in the real world\n- Learn and adapt from interactions"},{"id":"core-agent-architecture","title":"Core agent architecture","role":"content","text":"![AI agent architecture](https://redis.io/docs/latest/images/ai_agent/ai-agent-architecture-diagram.svg)"},{"id":"the-agent-processing-cycle","title":"The agent processing cycle","role":"content","text":"Every user interaction follows a 6-step cycle that makes agents intelligent:\n\n![AI agent processing cycle](https://redis.io/docs/latest/images/ai_agent/simple-processing-cycle.svg)\n\nWhy this cycle matters:\n- Maintains context across multiple conversations\n- Learns from experience to improve future responses\n- Handles complex tasks that require multiple steps\n- Recovers from failures and adapts plans in real-time\n\n> Example: When you ask \"Book me a flight to Paris and find a hotel,\" the agent breaks this into separate tasks, remembers your travel preferences, searches for options, and coordinates the booking process."},{"id":"why-redis-powers-ai-agents","title":"Why Redis powers AI agents","role":"content","text":"Redis is the **ideal foundation** for AI agents because it excels at the three things agents need most: **speed**, **memory**, and **search**."},{"id":"redis-powers-every-part-of-your-agent","title":"Redis powers every part of your agent","role":"content","text":"<div class=\"grid grid-cols-1 md:grid-cols-3 gap-4 my-6\">\n  <div class=\"bg-gray-50 dark:bg-gray-800 p-4 rounded-lg border border-gray-200 dark:border-gray-700\">\n    <h4 class=\"font-semibold text-black dark:text-white mb-2\">Planner</h4>\n    <p class=\"text-sm text-black dark:text-white\">Stores workflow templates and agent plans as Hashes or JSON. Enables complex multi-step reasoning.</p>\n  </div>\n\n  <div class=\"bg-gray-50 dark:bg-gray-800 p-4 rounded-lg border border-gray-200 dark:border-gray-700\">\n    <h4 class=\"font-semibold text-black dark:text-white mb-2\">Retriever</h4>\n    <p class=\"text-sm text-black dark:text-white\">Vector Search finds semantically similar documents instantly. Supports hybrid search for better results.</p>\n  </div>\n\n  <div class=\"bg-gray-50 dark:bg-gray-800 p-4 rounded-lg border border-gray-200 dark:border-gray-700\">\n    <h4 class=\"font-semibold text-black dark:text-white mb-2\">Executor</h4>\n    <p class=\"text-sm text-black dark:text-white\">Stores conversation history, user preferences, and intermediate results. Maintains state across workflows.</p>\n  </div>\n</div>"},{"id":"key-advantages","title":"Key advantages","role":"content","text":"**Ultra-fast response times**\n- Sub-millisecond data access keeps conversations flowing naturally\n- In-memory processing eliminates disk I/O bottlenecks\n- Optimized data structures for different use cases\n\n**Built-in vector search**\n- Native vector indexing with HNSW, FLAT, and SVS-VAMANA algorithms\n- SVS-VAMANA leverages Intel hardware acceleration for enhanced performance\n- Hybrid search combining vector similarity with metadata filtering\n- Real-time updates without index rebuilds\n- [Learn more about Redis Vector Search →](https://redis.io/docs/latest/develop/ai/search-and-query/vectors)\n\n**Agent memory**\n- **Short-term**: Conversation context and session state\n- **Long-term**: User preferences and learned patterns\n- Flexible data structures (Hashes, Lists, Streams, JSON) for different memory types\n- **Managed option**: The [Redis Iris Context Engine](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory) provides short-term (session) and long-term memory as a managed service — with semantic long-term search — so you don't have to build the vector index and storage yourself\n- [Explore Redis data structures →](https://redis.io/docs/latest/develop/data-types)"},{"id":"managed-agent-context-with-redis-iris","title":"Managed agent context with Redis Iris","role":"content","text":"You can build each part of an agent's context layer yourself from Redis data structures, or use the [Redis Iris context engine](https://redis.io/docs/latest/develop/ai/context-engine) services as a pre-built solution. Each service runs fully managed on Redis Cloud, or self-managed on your own infrastructure, and has a REST API.\n\n| Agent need | Build it yourself with Redis | Redis Iris service |\n|:--|:--|:--|\n| Remember the user across sessions | Streams, Hashes, and vector search for session and long-term memory | [Agent Memory](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory) stores session events, summarizes long sessions, and extracts long-term memories in the background. |\n| Avoid repeat LLM calls | Vector search plus your own cache logic | [LangCache](https://redis.io/docs/latest/develop/ai/context-engine/langcache) returns a cached response when a new prompt is semantically similar to a cached one. |\n| Query business data safely | Hand-written tools or generated queries per agent | [Context Retriever](https://redis.io/docs/latest/develop/ai/context-engine/context-retriever) generates tools from a data model you define once. Agents call them over MCP (Model Context Protocol), and agent keys limit what each agent can reach. |\n| Keep that data current | Your own sync jobs from the source database | [Data Integration](https://redis.io/docs/latest/develop/ai/context-engine/data-integration) streams changes from relational databases into Redis within seconds. |\n\nLangCache, Agent Memory, and Context Retriever are currently in preview. To see where each service fits in a single request, see [how a request flows through Redis Iris](https://redis.io/docs/latest/develop/ai/context-engine/concepts/request-flow)."},{"id":"types-of-agents-you-can-build","title":"Types of agents you can build","role":"content","text":"<div class=\"grid grid-cols-1 md:grid-cols-2 gap-6 my-8\">\n\n<div class=\"bg-gray-50 p-4 rounded-lg border border-gray-200\">\n<h3 class=\"no-toc\">Conversational assistants</h3>\n\nBuild chatbots and virtual assistants that:\n- Maintain natural conversations with context and memory\n- Provide personalized responses based on user history\n\n[Build a conversational agent →](../)\n</div>\n\n<div class=\"bg-gray-50 p-4 rounded-lg border border-gray-200\">\n<h3 class=\"no-toc\">Recommendation engines</h3>\n\nCreate intelligent recommendation systems that:\n- Learn from user behavior and preferences\n- Provide real-time personalized suggestions\n\n[Build a recommendation agent →](../)\n</div>\n\n<div class=\"bg-gray-50 p-4 rounded-lg border border-gray-200\">\n<h3 class=\"no-toc\">Knowledge assistants (RAG)</h3>\n\nBuild retrieval-augmented generation agents that:\n- Ingest documents and answer questions with citations\n- Combine vector search with semantic caching for fast, grounded responses\n\n[Build a knowledge assistant →](../)\n</div>\n\n<div class=\"bg-gray-50 p-4 rounded-lg border border-gray-200\">\n<h3 class=\"no-toc\">Redis Iris conversational assistants</h3>\n\nBuild conversational agents backed by managed Redis Iris Agent Memory that:\n- Get session and long-term memory without building a vector index\n- Extract durable memories automatically in the background\n\n[Build a Redis Iris agent →](../)\n</div>\n\n<div class=\"bg-gray-50 p-4 rounded-lg border border-gray-200\">\n<h3 class=\"no-toc\">Task automation agents</h3>\n\nAutomate complex workflows and business processes:\n- Execute multi-step tasks with decision-making\n- Integrate with APIs and external systems\n</div>\n\n<div class=\"bg-gray-50 p-4 rounded-lg border border-gray-200\">\n<h3 class=\"no-toc\">Data analysis agents</h3>\n\nProcess and analyze large datasets intelligently:\n- Perform statistical analysis and pattern recognition\n- Handle real-time data streams\n</div>\n\n<div class=\"bg-gray-50 p-4 rounded-lg border border-gray-200\">\n<h3 class=\"no-toc\">Customer support agents</h3>\n\nProvide intelligent customer service:\n- Answer questions using knowledge bases\n- Route complex issues to human agents\n</div>\n\n<div class=\"bg-gray-50 p-4 rounded-lg border border-gray-200\">\n<h3 class=\"no-toc\">Research and retrieval agents</h3>\n\nFind and synthesize information from multiple sources:\n- Search across documents, databases, and web content\n- Summarize findings and extract key insights\n</div>\n\n<div class=\"bg-gray-50 p-4 rounded-lg border border-gray-200\">\n<h3 class=\"no-toc\">Monitoring and alerting agents</h3>\n\nWatch systems and notify when action is needed:\n- Detect anomalies and security threats\n- Send intelligent alerts with context\n</div>\n\n<div class=\"bg-gray-50 p-4 rounded-lg border border-gray-200\">\n<h3 class=\"no-toc\">Personal productivity agents</h3>\n\nHelp users manage tasks and information:\n- Schedule meetings and manage calendars\n- Organize and prioritize tasks\n</div>\n\n<div class=\"bg-gray-50 p-4 rounded-lg border border-gray-200\">\n<h3 class=\"no-toc\">Trading and financial agents</h3>\n\nMake intelligent financial decisions:\n- Analyze market data and trends\n- Execute trades based on predefined strategies\n</div>\n\n<div class=\"bg-gray-50 p-4 rounded-lg border border-gray-200\">\n<h3 class=\"no-toc\">Content generation agents</h3>\n\nCreate and manage content at scale:\n- Generate articles, summaries, and documentation\n- Adapt content for different audiences and formats\n</div>\n\n</div>"},{"id":"agent-architecture-patterns","title":"Agent architecture patterns","role":"content","text":""},{"id":"single-agent-systems","title":"Single-agent systems","role":"content","text":"Simple agents that handle all tasks within one system:\n- Easier to develop and maintain\n- Good for focused use cases\n- All logic contained in one place\n- Suitable for most applications"},{"id":"multi-agent-systems","title":"Multi-agent systems","role":"content","text":"Multiple specialized agents working together:\n- Each agent handles specific domains or tasks\n- agents can communicate and coordinate\n- More complex but more scalable\n- Good for enterprise applications"},{"id":"hierarchical-agents","title":"Hierarchical agents","role":"content","text":"Agents organized in layers with different responsibilities:\n- High-level agents handle planning and coordination\n- Low-level agents execute specific tasks\n- Clear separation of concerns\n- Easier to debug and maintain"},{"id":"redis-data-structures-for-agent-memory","title":"Redis data structures for agent memory","role":"content","text":"Understanding how to map agent memory needs to Redis data structures is crucial for building efficient agents:"},{"id":"redis-streams-for-conversation-history","title":"Redis streams for conversation history","role":"content","text":"- Use case: Ordered conversation logs with timestamps and metadata\n- Key benefits: Automatic ordering, range queries, consumer groups, guaranteed delivery\n- Implementation: Store user/agent message pairs with rich contextual metadata\n- Retention: Use XTRIM for automatic cleanup based on age or count limits\n- [Learn about Redis Streams →](https://redis.io/docs/latest/develop/data-types/streams)"},{"id":"redis-hashes-for-user-profiles","title":"Redis Hashes for User Profiles","role":"content","text":"- Use case: Structured user data with frequent partial updates and atomic operations\n- Key benefits: Memory efficient field-level operations, atomic updates, O(1) field access\n- Implementation: Multi-layered profile system with preferences, behavior patterns, and learned data\n- Scaling: Hash tags for cluster distribution, field expiration for data lifecycle management\n- [Learn about Redis Hashes →](https://redis.io/docs/latest/develop/data-types/hashes)"},{"id":"redis-json-for-complex-state","title":"Redis JSON for Complex State","role":"content","text":"- Use case: Nested data structures, complex agent workflows, hierarchical configurations\n- Key benefits: JSONPath queries, atomic nested updates, schema validation, efficient storage\n- Implementation: Multi-step task orchestration, complex decision trees, dynamic configurations\n- Querying: Advanced JSONPath expressions for complex data retrieval and manipulation\n- [Learn about Redis JSON →](https://redis.io/docs/latest/develop/data-types/json)"},{"id":"redis-sets-for-relationships-and-tags","title":"Redis Sets for Relationships and Tags","role":"content","text":"- Use case: Entity relationships, user interest tracking, session management, feature flags\n- Key benefits: O(1) membership testing, efficient set operations, automatic deduplication\n- Implementation: Complex relationship modeling, real-time recommendation engines, access control\n- Operations: Union, intersection, difference for advanced analytics and personalization\n- [Learn about Redis Sets →](https://redis.io/docs/latest/develop/data-types/sets)"},{"id":"redis-vector-sets-for-semantic-search","title":"Redis Vector Sets for Semantic Search","role":"content","text":"- Use case: Embedding storage, similarity search, semantic retrieval, content recommendations\n- Key benefits: High-performance vector similarity search, multiple distance metrics, real-time indexing\n- Implementation: RAG systems, semantic memory, content discovery, personalized recommendations\n- Queries: K-nearest neighbor search, range queries, hybrid filtering with metadata\n- [Learn about Redis Vector Sets →](https://redis.io/docs/latest/develop/data-types/vector-sets)"},{"id":"redis-sorted-sets-for-rankings-and-priorities","title":"Redis Sorted Sets for Rankings and Priorities","role":"content","text":"- Use case: Dynamic scoring systems, priority queues, leaderboards, time-series data\n- Key benefits: O(log N) insertions, range queries by score/rank, atomic score updates\n- Implementation: Real-time recommendation scoring, task prioritization, performance analytics\n- Queries: Range by score, rank, lexicographical order, and complex aggregations\n- [Learn about Redis Sorted Sets →](https://redis.io/docs/latest/develop/data-types/sorted-sets)"},{"id":"reliability-features","title":"Reliability features","role":"content","text":"Production-ready agents include built-in reliability features:"},{"id":"error-handling","title":"Error handling","role":"errors","text":"- Gracefully handle API failures and unexpected inputs\n- Provide helpful error messages when things go wrong\n- Continue functioning even when some components fail"},{"id":"retry-logic","title":"Retry logic","role":"content","text":"- Automatically retry failed operations with exponential backoff\n- Handle temporary network issues and rate limiting\n- Ensure important operations complete successfully"},{"id":"logging-and-monitoring","title":"Logging and monitoring","role":"content","text":"- Track what your agent does for debugging and improvement\n- Monitor performance metrics like response times\n- Log errors and unusual behavior for investigation"},{"id":"performance-optimization","title":"Performance optimization","role":"performance","text":"- Cache frequently accessed information\n- Cache LLM responses semantically, so paraphrased questions skip the model call. [LangCache](https://redis.io/docs/latest/develop/ai/context-engine/langcache) does this as a managed service\n- Use efficient data structures for fast retrieval\n- Scale resources based on demand"},{"id":"production-deployment-considerations","title":"Production deployment considerations","role":"content","text":""},{"id":"monitoring-and-observability","title":"Monitoring and Observability","role":"content","text":"- Agent performance metrics: Response times, success rates, user satisfaction\n- Redis metrics: Memory usage, connection counts, operation latencies\n- LLM usage tracking: Token consumption, API costs, rate limiting\n- Business metrics: Task completion rates, user engagement, conversion"},{"id":"security-and-privacy","title":"Security and Privacy","role":"security","text":"- Data encryption: Encrypt sensitive data at rest and in transit\n- Access controls: Implement proper authentication and authorization\n- Governed data access: Give agents defined tools instead of direct database access. [Context Retriever](https://redis.io/docs/latest/develop/ai/context-engine/context-retriever/concepts) generates these tools and uses access tags to filter what each agent can see\n- Data retention: Automatic cleanup of personal data per regulations\n- Sensitive data in memory: Keep information such as payment card numbers out of long-term memory. Agent Memory [sensitive-data exclusions](https://redis.io/docs/latest/operate/iris/agent-memory/create-service#sensitive-data-exclusions) guide automatic extraction away from it\n- Audit logging: Track all data access and modifications\n- Memory integrity: Validate content before it is written to agent memory and verify protected records on read. Memory written from tool results, web pages, or other agents can carry instructions that are replayed into later prompts ([OWASP Top 10 for Agentic Applications, ASI06](https://genai.owasp.org/resource/owasp-top-10-for-agentic-applications-for-2026/); [MITRE ATLAS AML.T0080.000](https://atlas.mitre.org/techniques/AML.T0080.000)). [OWASP Agent Memory Guard](https://github.com/OWASP/www-project-agent-memory-guard) is an open-source guard for this with adapters for LangChain, OpenAI Agents SDK, AutoGen, CrewAI, and mem0"},{"id":"scaling-strategies","title":"Scaling Strategies","role":"content","text":"- Horizontal scaling: Multiple agent instances with shared Redis state\n- Load balancing: Distribute requests across agent instances\n- Redis clustering: Scale data storage across multiple nodes\n- Caching layers: CDN for static content, Redis for dynamic data\n- [Learn about Redis scaling →](https://redis.io/docs/latest/operate/rs/clusters)"},{"id":"cost-optimization","title":"Cost Optimization","role":"content","text":"- LLM cost management: Use appropriate models for different tasks, and serve repeat questions from a semantic cache such as [LangCache](https://redis.io/docs/latest/develop/ai/context-engine/langcache)\n- Redis memory optimization: Efficient data structures and TTL policies\n- API rate limiting: Prevent excessive external API calls\n- Resource monitoring: Track and optimize compute and storage costs\n- [Redis performance optimization →](https://redis.io/docs/latest/operate/rs/databases/memory-performance)\n\n---"},{"id":"key-takeaways","title":"Key takeaways","role":"content","text":"<div class=\"bg-gray-50 p-6 rounded-lg border border-gray-200 my-8\">\n\n**What makes agents different:**\nAgents maintain memory, plan multi-step tasks, and learn from interactions—unlike simple chatbots.\n\n**Why Redis is perfect:**\nSub-millisecond data access, built-in vector search, and flexible data structures designed for agent workflows.\n\n**What you can build:**\nConversational assistants, recommendation engines, and complex multi-agent systems.\n\n</div>"},{"id":"next-steps","title":"Next steps","role":"content","text":"Ready to build your AI agent with Redis?\n\n**Get started:**\n- [Use the agent builder](https://redis.io/docs/latest/develop/ai/agent-builder) to generate your code and get started\n- [Redis quick start guide](https://redis.io/docs/latest/develop/get-started) for setting up Redis\n\n**Learn more:**\n- [Redis Iris Context Engine — Agent Memory](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory) for managed session and long-term agent memory\n- [Redis Iris concepts](https://redis.io/docs/latest/develop/ai/context-engine/concepts) for how memory, caching, and governed data access work together\n- Interactive demos for [Agent Memory](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/interactive-demo), [LangCache](https://redis.io/docs/latest/develop/ai/context-engine/langcache/interactive-demo), and [Context Retriever](https://redis.io/docs/latest/develop/ai/context-engine/context-retriever/interactive-demo) that run in your browser\n- [Redis Vector Search documentation](https://redis.io/docs/latest/develop/ai/search-and-query/vectors)\n- [RedisVL Python library](https://redis.io/docs/latest/develop/clients/redis-vl) for vector operations and AI workflows\n- [Redis data structures guide](https://redis.io/docs/latest/develop/data-types)\n- [Redis client libraries](https://redis.io/docs/latest/develop/clients) for your programming language\n\n**Deploy and scale:**\n- [Redis Cloud](https://redis.io/docs/latest/operate/rc) for managed Redis hosting\n- [Redis Software](https://redis.io/docs/latest/operate/rs) for on-premises deployment\n- [Performance optimization](https://redis.io/docs/latest/operate/rs/databases/memory-performance) best practices"}],"examples":[]}
{"schema_version":2,"id":"develop/ai/agent-builder","title":"AI agent builder","url":"https://redis.io/docs/latest/develop/ai/agent-builder/","summary":"Build custom AI agents powered by Redis with our interactive code generator","tags":["docs","develop","ai"],"last_updated":"2026-09-11T15:54:40+01:00","children":[{"id":"develop/ai/agent-builder/agent-concepts","summary":"Learn how AI agents work and why Redis is the perfect foundation for building intelligent systems","title":"How agents work","url":"https://redis.io/docs/latest/develop/ai/agent-builder/agent-concepts/"}],"page_type":"content","content_hash":"ce9b7a18ccdf88a992c274561955c71b7bea2a87583132312854e6ae0567135d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Agents use Redis for data storage, [vector search](https://redis.io/docs/latest/develop/get-started/search-tutorial/vector-search), and [conversation memory](https://redis.io/docs/latest/develop/get-started/rag). The interactive builder generates code in your preferred programming language with your choice of model."},{"id":"get-started","title":"Get started","role":"content","text":"Use the interactive builder below to generate your custom AI agent code:"},{"id":"what-are-ai-agents","title":"What are AI agents?","role":"content","text":"AI agents are intelligent systems that can plan, remember, and take actions to help users accomplish goals. Unlike simple chatbots, agents can remember conversations, plan multi-step tasks, use external tools, and learn from interactions.\n\nRedis powers these capabilities with fast, reliable data storage and retrieval that keeps your agents responsive and intelligent.\n\n**Learn more**: [How agents work](agent-concepts/)"},{"id":"what-you-can-build","title":"What you can build","role":"content","text":"Choose from four types of intelligent agents:\n\n- **Recommendation engines**: Personalized product and content recommendations\n- **Conversational assistants**: Chatbots with memory and context awareness\n- **Knowledge assistants**: RAG agents that ingest documents, answer questions with citations, and use semantic caching\n- **Redis Iris conversational assistants**: Conversational agents backed by managed [Redis Iris Agent Memory](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory) — session and long-term memory with no vector index to build\n\nThe agent builder will generate complete, working code examples for your chosen agent type."},{"id":"features","title":"Features","role":"content","text":"- **Multiple programming languages**: Generate code in Python and JavaScript (Node.js), with Java and C# coming soon\n- **LLM integration**: Support for OpenAI, Anthropic Claude, and Llama 2\n- **Redis optimized**: Uses Redis data structures for optimal performance"},{"id":"after-you-generate-your-code","title":"After you generate your code","role":"content","text":"Use the **Copy** or **Download** buttons and follow the steps below to test it locally:\n\n1. **Set up your environment**: Install Redis and the required dependencies\n2. **Configure your API keys**: Add your LLM provider credentials to environment variables\n3. **Test locally**: Start with simple conversations to verify everything works\n4. **Deploy and scale**: Use Redis Cloud for production deployments\n\nThe generated code includes detailed setup instructions and best practices to get you started quickly. If you want to generate another agent, select the **Start again** button."},{"id":"learn-more","title":"Learn more","role":"related","text":""},{"id":"ai-agent-resources","title":"AI agent resources","role":"content","text":"- [How agents work](agent-concepts/) - Learn how agents work and why Redis is perfect for them"},{"id":"redis-ai-documentation","title":"Redis AI documentation","role":"content","text":"- [Redis Vector Search](https://redis.io/docs/latest/develop/ai/search-and-query/vectors) - Semantic search capabilities \n- [Redis Streams](https://redis.io/docs/latest/develop/data-types/streams) - Real-time data and conversation history\n- [AI Notebooks Collection](https://redis.io/docs/latest/develop/ai/notebook-collection) - Interactive tutorials and examples \n- [Ecosystem Integrations](https://redis.io/docs/latest/develop/ai/ecosystem-integrations) - Redis with AI frameworks"},{"id":"for-experienced-developers","title":"For experienced developers","role":"content","text":"If you're ready to go beyond the agent builder, these resources cover production-grade managed services and cutting-edge Redis AI projects:\n\n- [Redis Context Engine](https://redis.io/docs/latest/develop/ai/context-engine) — Managed services for agent memory, semantic caching, and structured data access (Redis Iris)\n- [Redis AI Incubator](https://redis.io/ai-incubator/) — Early-stage AI projects and experiments from the Redis team"},{"id":"community-and-support","title":"Community and support","role":"content","text":"- Join the [Redis Discord](https://discord.gg/redis) for community support\n- Explore [Redis AI Resources on GitHub](https://github.com/redis-developer/redis-ai-resources)\n- Watch [AI Video Collection](https://redis.io/docs/latest/develop/ai/ai-videos) for tutorials and demonstrations"}],"examples":[]}
{"schema_version":2,"id":"develop/ai/ai-videos","title":"Redis video tutorial collection","url":"https://redis.io/docs/latest/develop/ai/ai-videos/","summary":"Watch video tutorials and demos showcasing Redis in AI applications, from vector search to RAG implementations.","tags":["docs","develop","ai"],"last_updated":"2026-05-19T15:06:42-07:00","page_type":"content","content_hash":"03e2b47e82038ca19a1db725ab585f65dc1a4cc5b62bf13d045f7ea83fac3ea2","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Explore our collection of video tutorials and demonstrations showcasing how Redis powers AI applications. From vector search fundamentals to advanced RAG implementations, these videos provide practical insights and hands-on examples.\n\n| | | |\n|---|---|---|\n| [**Long-Term Memory with LangGraph**](https://www.youtube.com/watch?v=fsENEq4F55Q) | [**Short-Term Memory with LangGraph**](https://www.youtube.com/watch?v=k3FUWWEwgfc) | [**What is semantic search?**](https://www.youtube.com/watch?v=o3XN4dImESE) |\n| Learn how to implement long-term memory capabilities in AI agents using LangGraph. This video shows you how to build AI systems that can retain and recall information across extended interactions. | Want your AI agents to remember what users tell them? Short-term memory is the key to natural conversations, and in this tutorial, you'll learn how to implement it with LangGraph. | Traditional search matches words — but what if your AI app could match meaning instead? This video explains how semantic search works and why it's essential for modern AI applications. |\n| [**What is a semantic cache?**](https://www.youtube.com/watch?v=AtVTT_s8AGc) | [**Building a RAG Pipeline from Scratch with RedisVL**](https://www.youtube.com/watch?v=cCTKmmGO4CY) | [**What is a vector database?**](https://www.youtube.com/watch?v=Yhv19le0sBw) |\n| What if you could skip redundant LLM calls and make your AI app faster, cheaper, and smarter? This video breaks down semantic caching and shows how it can transform your AI applications. | Unlock the Power of Retrieval-Augmented Generation (RAG) with RedisVL. This tutorial will show you how to build a complete RAG pipeline from scratch using Redis as your vector database. | Vector databases have been trending recently as they power modern search, recommendations, and AI-driven applications. Learn what vector databases are and how they work. |\n| [**Building the future Architecting AI Agents with AWS, LlamaIndex and Redis**](https://www.youtube.com/watch?v=SFWroqAbBM4) | [**Building AI Apps using LangChain**](https://www.youtube.com/watch?v=YhxksXfgsp0) | [**Resources to Learn AI with Redis**](https://www.youtube.com/watch?v=M_WU_fN_lrs) |\n| The ins and outs of AI agents: understand their role in breaking down tasks into manageable components for better performance. Learn how to architect AI agents using AWS, LlamaIndex, and Redis. | This series of videos dives into the integration between LangChain and Redis to power AI applications that need runtime speed, scalability, and intelligent data management. | This video shows which resources you can use to learn AI with Redis and build powerful AI applications. |\n| [**What Is RAG? Retrieval-Augmented Generation Explained Simply**](https://www.youtube.com/watch?v=xPMQ2cVbUTI) | [**Chunking Strategies Explained**](https://www.youtube.com/watch?v=ZTOtxiWb2bE) | [**What is an embedding model?**](https://www.youtube.com/watch?v=0U1S0WSsPuE) |\n| Retrieval-Augmented Generation (RAG) is one of the most powerful architectural patterns in GenAI today—combining the strengths of large language models (LLMs) with real-time, external context from your own data. In this session, learn why it matters and how each component—from query rewriting to dense retrieval to semantic chunking—works behind the scenes to power more accurate, grounded, and up-to-date responses. | Are you interested in building LLM applications that actually work? Your chunking strategy makes all the difference. In this video, get a  break down of the science of text chunking so your embeddings can start answering the right questions to your users. | Everyone’s talking about embedding models lately—but what do they actually do, and why does it matter? This video breaks it down in simple terms and shows how embeddings power search, recommendations, and AI features behind the scenes. |\n| [**Exact vs Approximate (HNSW) Nearest Neighbors in Vector Databases**](https://www.youtube.com/watch?v=9NvO-VdjY80) | [**What is Hybrid Search?**](https://www.youtube.com/watch?v=ZcZGwS1Pgq0) | [**Vector Database Search: HNSW Algorithm Explained**](https://www.youtube.com/watch?v=cZyTZ-EMskI) |\n| Explore the trade-offs between exact and approximate nearest neighbor search using the HNSW algorithm, with a focus on precision vs. performance in vector databases. | Learn how hybrid search combines vector (semantic) search with traditional keyword search to improve retrieval quality in AI applications. | A deep-dive into the HNSW (Hierarchical Navigable Small World) algorithm and how it powers efficient similarity search in vector databases. |"},{"id":"ai-agents-and-context-engine","title":"AI Agents and Context Engine","role":"content","text":"| | | |\n|---|---|---|\n| [**Redis Agent Memory Basics**](https://www.youtube.com/watch?v=OWsCEs8_Wt4) | [**Build with Rowan: The real-time context engine**](https://www.youtube.com/watch?v=7JOnwT1v2Do) | [**Redis LangCache Setup**](https://www.youtube.com/watch?v=UOGhMZlZLko) |\n| Learn the fundamentals of agent memory using Redis as a memory layer, covering how to give AI agents the ability to store and retrieve information. | A hands-on build session demonstrating how to construct applications that use the Redis Context Engine for real-time agent context. | Walk through how to configure Redis LangCache for semantic caching of LLM responses to reduce latency and cost. |\n| [**Getting Started with the MCP Server for Redis Cloud using Docker**](https://www.youtube.com/watch?v=ziPu9TZD8hg) | | |\n| Learn how to set up and run the MCP Server for Redis Cloud inside a Docker container to connect AI agents to your Redis data. | | |"},{"id":"additional-resources","title":"Additional Resources","role":"content","text":"| | | |\n|---|---|---|\n| [**LLM Session Management with Redis**](https://www.youtube.com/watch?v=2jHtSLVUu0w) | [**A Semantic Cache using LangChain**](https://www.youtube.com/watch?v=LRswXEc5chE) | [**Similarity Search using Vector Store**](https://www.youtube.com/watch?v=BtFJdSiFh00) |\n| Developers building AI applications require a way to store the conversation history between an LLM and a user. This is important to provide context and maintain coherent conversations across sessions. | One common concern of developers building AI applications is how quickly answers from LLMs will be served to their end users, as well as how much it will cost. Learn how to implement semantic caching using LangChain and Redis. | Similarity search is one of the most popular use cases for developers building AI applications. It allows users to perform searches that can find semantically similar content using vector embeddings. |\n| [**Create a New Database on Redis Cloud**](https://www.youtube.com/watch?v=jF89DiC5RqM) | [**Redis Insight: A Developer's Deep Dive**](https://www.youtube.com/watch?v=dINUz_XOZ0M) | [**Redis + Amazon SageMaker for real-time fraud detection demo**](https://www.youtube.com/watch?v=kQKfXi7NfWs) |\n| Learn how to create a new database on Redis Cloud in this step-by-step tutorial. Perfect for developers getting started with Redis Cloud for their AI and data applications. | This video breaks down Redis Insight and shows developers how to use this powerful tool for database management and development. | See how Redis integrates with Amazon SageMaker to build real-time fraud detection systems. This demo shows practical applications of Redis in machine learning and AI-powered fraud prevention. |\n| [**Redis + Amazon Bedrock in two minutes**](https://www.youtube.com/watch?v=1e2tM5kIJ5Y) | | |\n| AWS has announced Redis Cloud as one of the few supported vector databases supported for Amazon Bedrock. Learn how to integrate Redis with Amazon Bedrock for your generative AI applications. | | |"},{"id":"getting-started","title":"Getting Started","role":"setup","text":"Ready to start building AI applications with Redis? Check out our:\n\n- [Quickstart guides](https://redis.io/docs/latest/develop/get-started)\n- [Vector search documentation](https://redis.io/docs/latest/develop/ai/search-and-query/vectors)\n- [AI ecosystem integrations](https://redis.io/docs/latest/develop/ai/ecosystem-integrations)\n- [Notebook collection](https://redis.io/docs/latest/develop/ai/notebook-collection)\n- [Vector set data type](https://redis.io/docs/latest/develop/data-types/vector-sets)"}],"examples":[]}
{"schema_version":2,"id":"develop/ai/context-engine/agent-memory/api-reference","title":"Redis Agent Memory API","url":"https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/api-reference/","summary":"","aliases":["/develop/ai/agent-memory/api-reference/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"index","sections":[],"examples":[]}
{"schema_version":2,"id":"develop/ai/context-engine/agent-memory/developer-guide","title":"Redis Agent Memory developer guide","url":"https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/developer-guide/","summary":"Connect an application to Redis Agent Memory and work with session memory and long-term memory through Python, TypeScript, or REST.","tags":["docs","develop","ai"],"last_updated":"2026-09-11T15:54:40+01:00","page_type":"content","content_hash":"6c0879524e7878c9cd42469ad442fbca457f1d4b14234dcfea21cb6b3425f068","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Use the Python SDK, TypeScript SDK, or REST API to add session events, retrieve conversation context, create long-term memories, and search for relevant information."},{"id":"integration-workflow","title":"Integration workflow","role":"content","text":"1. Connect to a Redis Agent Memory service with its endpoint, Store ID, and API key.\n1. Add conversation events to session memory.\n1. Retrieve session memory before an agent turn to reconstruct the conversation context.\n1. Search long-term memory for information relevant to the current interaction.\n\nRedis Agent Memory can automatically summarize older session events and extract long-term memories in the background. Applications can also create long-term memories directly."},{"id":"choose-a-client","title":"Choose a client","role":"content","text":"| Client | Use it when | Package and quickstart |\n|:-------|:------------|:-----------------------|\n| Python SDK | Your application or agent uses Python. | Install [`redis-agent-memory`](https://pypi.org/project/redis-agent-memory/) and follow the [Python SDK quickstart](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/python-sdk-quickstart). |\n| TypeScript SDK | Your application or agent uses JavaScript or TypeScript. | Install [`@redis-iris/agent-memory`](https://www.npmjs.com/package/@redis-iris/agent-memory) and follow the [TypeScript SDK quickstart](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/typescript-sdk-quickstart). |\n| REST API | You need language-independent HTTP access or don't want an SDK dependency. | No package required. Follow the [REST API quickstart](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/rest-api-quickstart). |"},{"id":"connect-to-a-redis-agent-memory-service","title":"Connect to a Redis Agent Memory service","role":"content","text":"Every client requires:\n\n* The Redis Agent Memory endpoint.\n* The Store ID.\n* A Redis Agent Memory API key.\n\nThe Python and TypeScript SDKs accept the endpoint, Store ID, and API key when you create the client. When you use the REST API, send the API key as a bearer token and include the Store ID in request paths.\n\nFollow the [Redis Cloud setup guide](https://redis.io/docs/latest/operate/iris/agent-memory/create-service) if you don't have a service. After you create one, copy its endpoint and Store ID from the **Configuration** tab and save the API key securely."},{"id":"identify-users-and-conversations","title":"Identify users and conversations","role":"content","text":"| Identifier | Purpose |\n|:-----------|:--------|\n| `sessionId` | Identifies a conversation or interaction session. |\n| `actorId` | Identifies the actor that produced a session event. |\n| `ownerId` | Identifies the user or entity associated with a long-term memory. |\n| Memory ID | Uniquely identifies a long-term memory within the store. |\n\nAn application can use the same user identifier for `actorId` and `ownerId`, but the fields describe different relationships."},{"id":"work-with-session-memory","title":"Work with session memory","role":"content","text":"Use a stable `sessionId` to store a conversation as an ordered sequence of events. Add an event for each user, assistant, or system message that your application needs to retain.\n\nEach stored event can include:\n\n| Field | Purpose |\n|:------|:--------|\n| `eventId` | Server-generated identifier for the event. |\n| `sessionId` | Session that contains the event. |\n| `actorId` | User, agent, or other actor that produced the event. |\n| `role` | Role of the message in the conversation. |\n| `content` | Message content, including its text. |\n| `createdAt` | Time the event occurred in the application. |\n| `systemTimestamp` | Time Redis Agent Memory stored the event. |\n| `metadata` | Optional application-specific information associated with the event. |\n\nBefore an agent turn, retrieve the session by `sessionId` and provide the relevant events to the agent. This lets the application reconstruct the conversation without maintaining a separate conversation store."},{"id":"session-retention","title":"Session retention","role":"content","text":"The session-memory TTL controls how long sessions remain available. Configure it according to the retention requirements of your application. When a session expires, its events are no longer available through session-memory retrieval.\n\nSee [memory configuration](https://redis.io/docs/latest/operate/iris/agent-memory/create-service#memory-configuration) to configure the session-memory TTL in Redis Cloud."},{"id":"automatic-session-summarization","title":"Automatic session summarization","role":"content","text":"Automatic summarization limits the amount of conversation history that must be added to a model's context window. Configure:\n\n* **Summarize after:** The number of messages a session can contain before older messages are summarized.\n* **Keep most recent:** The number of recent messages that remain available in full.\n\nFor example, with **Summarize after** set to 20 and **Keep most recent** set to 10, Redis Agent Memory summarizes the older 10 messages when the session reaches 20 messages and retains the 10 most recent messages in full.\n\nSee [automatic summarization](https://redis.io/docs/latest/operate/iris/agent-memory/create-service#automatic-summarization) to enable summarization and configure both thresholds in Redis Cloud.\n\nFollow any of the client quickstarts to add and retrieve a session event. For complete schemas, see the [session-memory API reference](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/api-reference#tag/session-memory)."},{"id":"work-with-long-term-memory","title":"Work with long-term memory","role":"content","text":"Long-term memory stores information that remains useful beyond one conversation. A long-term memory record includes content and fields that let applications classify, scope, and retrieve it:\n\n| Field | Purpose |\n|:------|:--------|\n| `id` | Unique identifier for the memory. |\n| `text` | Memory content used for retrieval. |\n| `memoryType` | Built-in or custom memory type. |\n| `sessionId` | Session associated with the memory. |\n| `ownerId` | User or entity associated with the memory. |\n| `namespace` | Logical grouping for the memory. |\n| `topics` | Topic tags used to categorize the memory. |\n| `createdAt` | Time the memory was created. |\n| `updatedAt` | Time the memory was last updated. |"},{"id":"create-long-term-memories","title":"Create long-term memories","role":"content","text":"Redis Agent Memory provides two creation paths:\n\n* **Automatic extraction:** Redis Agent Memory processes session events asynchronously and creates durable memories from relevant information. Configure the extraction cadence to control how often session events are processed.\n* **Direct creation:** Your application creates one or more long-term memories through the API or an SDK. Use direct creation when importing existing information or when your application determines exactly what to store.\n\nConfigure the long-term-memory TTL separately from the session-memory TTL.\n\nSee [memory configuration](https://redis.io/docs/latest/operate/iris/agent-memory/create-service#memory-configuration) to configure the extraction cadence and long-term-memory TTL in Redis Cloud."},{"id":"exclude-sensitive-data-from-automatic-extraction","title":"Exclude sensitive data from automatic extraction","role":"content","text":"Sensitive-data exclusions keep specified information out of long-term memory during automatic extraction. A store's exclusions policy combines three mechanisms that you enable independently:\n\n* **Built-in detectors:** Validated patterns for common identifiers, such as payment card numbers, email addresses, phone numbers, IP addresses, and US Social Security numbers. Select the ones a store should apply.\n* **Custom detectors:** Regular expressions you write yourself, for identifiers specific to your domain such as tenant IDs or internal reference numbers.\n* **Semantic exclusions:** A plain-language exclusion prompt describing what must not be kept, such as passwords, access tokens, recovery codes, payment card information, or booking confirmation codes. Use it for concepts a pattern cannot express.\n\nEvery detector chooses what happens to a memory it matches: redact the matched text and keep the rest, or drop the memory. When a memory matches several detectors that choose different actions, the memory is dropped.\n\nExclusions apply to automatic extraction from session events. They do not apply when an application creates long-term memories directly, and session memory itself is never altered.\n\n> [!WARNING]\n> Detector matches are deterministic, but semantic exclusions are advisory and do not guarantee exclusion. Sensitive session content still reaches the extraction model provider. Use appropriate controls before sending sensitive information to Redis Agent Memory or the model provider.\n\nSee [sensitive-data exclusions](https://redis.io/docs/latest/operate/iris/agent-memory/create-service#sensitive-data-exclusions) to configure the feature in Redis Cloud."},{"id":"search-long-term-memory","title":"Search long-term memory","role":"content","text":"Search long-term memory using semantic, keyword, or hybrid retrieval. Scope results with filters for owners, sessions, namespaces, topics, and memory types.\n\nUse `ownerId` to restrict recall to the relevant user or entity. Add narrower filters when the application needs memories from a particular session, namespace, topic, or memory type.\n\nFor request fields, filter operators, and response schemas, see [`SearchLongTermMemory`](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/api-reference#tag/long-term-memory/operation/SearchLongTermMemory)."},{"id":"define-custom-memory-types","title":"Define custom memory types","role":"content","text":"Custom memory types capture structured information specific to your business domain. Define them in the Redis Agent Memory service configuration with:\n\n| Setting | Purpose |\n|:--------|:--------|\n| **Name** | Unique name used as the memory's `memoryType`. |\n| **Description** | Description of the information represented by the type. |\n| **Extraction prompt** | Instructions that tell Redis Agent Memory when and how to extract the custom memory from session events. |\n| **Enabled** | Controls whether Redis Agent Memory extracts new memories for the type. |\n| **Custom fields** | Structured fields added to memories of this type. |\n\nCustom fields support `str`, `int`, `float`, `bool`, `list[str]`, `list[float]`, and `object`. Each field has a name and description that explain what it captures. Every custom memory also includes the built-in long-term memory fields listed above.\n\nFor example, a travel application could define a `trip_preference` type with these fields:\n\n| Field | Type | Captures |\n|:------|:-----|:---------|\n| `destination` | `str` | City or country the user plans to visit. |\n| `travel_period` | `str` | Dates or period of the trip. |\n| `dietary_requirements` | `list[str]` | Dietary requirements that affect recommendations. |\n\nWhen this type is enabled, Redis Agent Memory can extract a structured `trip_preference` memory from relevant session events. Each enabled custom type processes session events independently.\n\nSee [custom memory types](https://redis.io/docs/latest/operate/iris/agent-memory/create-service#custom-memory-types) for configuration requirements and limits.\n\nFollow any of the client quickstarts to create and search long-term memory. For complete schemas, see the [long-term-memory API reference](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/api-reference#tag/long-term-memory)."},{"id":"references","title":"References","role":"related","text":"* [Python SDK quickstart](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/python-sdk-quickstart)\n* [TypeScript SDK quickstart](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/typescript-sdk-quickstart)\n* [Python SDK reference](https://pypi.org/project/redis-agent-memory/)\n* [TypeScript SDK reference](https://www.npmjs.com/package/@redis-iris/agent-memory)\n* [REST API quickstart](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/rest-api-quickstart)\n* [Redis Agent Memory API reference](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/api-reference)"}],"examples":[]}
{"schema_version":2,"id":"develop/ai/context-engine/agent-memory","title":"Redis Agent Memory","url":"https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/","summary":"Store agent memory for AI applications in Redis.","aliases":["/develop/ai/agent-memory/"],"tags":["docs","develop","ai"],"last_updated":"2026-10-05T20:57:56-07:00","children":[{"id":"develop/ai/context-engine/agent-memory/overview","summary":"How Redis Agent Memory's session and long-term memory tiers work, and what runs automatically in the background.","title":"Redis Agent Memory overview","url":"https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/overview/"},{"id":"develop/ai/context-engine/agent-memory/developer-guide","summary":"Connect an application to Redis Agent Memory and work with session memory and long-term memory through Python, TypeScript, or REST.","title":"Redis Agent Memory developer guide","url":"https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/developer-guide/"},{"id":"develop/ai/context-engine/agent-memory/python-sdk-quickstart","summary":"Explore session memory, automatic extraction, summarization, custom memory types, and sensitive-data exclusions with the Redis Agent Memory Python SDK.","title":"Redis Agent Memory Python SDK quickstart","url":"https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/python-sdk-quickstart/"},{"id":"develop/ai/context-engine/agent-memory/typescript-sdk-quickstart","summary":"Explore session memory, automatic extraction, summarization, custom memory types, and sensitive-data exclusions with the Redis Agent Memory TypeScript SDK.","title":"Redis Agent Memory TypeScript SDK quickstart","url":"https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/typescript-sdk-quickstart/"},{"id":"develop/ai/context-engine/agent-memory/rest-api-quickstart","summary":"Explore session memory, automatic extraction, summarization, custom memory types, and sensitive-data exclusions with the Redis Agent Memory REST API.","title":"Redis Agent Memory REST API quickstart","url":"https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/rest-api-quickstart/"},{"id":"develop/ai/context-engine/agent-memory/interactive-demo","summary":"Watch session memory and long-term memory change as a customer talks to an AI assistant, then see what the assistant recalls in a later conversation.","title":"Redis Agent Memory interactive demo","url":"https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/interactive-demo/"},{"id":"develop/ai/context-engine/agent-memory/api-reference","summary":"","title":"Redis Agent Memory API","url":"https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/api-reference/"}],"page_type":"content","content_hash":"2c0fb3843f22e480a9b2aa29c3c95679413f7aeb8ca41b004a71b0df6654c577","sections":[{"id":"what-is-redis-agent-memory","title":"What is Redis Agent Memory?","role":"overview","text":"Redis Agent Memory is a memory service for AI applications. It stores ordered conversation events in session memory and durable information in long-term memory.\n\nWhen enabled, automatic summarization compacts session memory by summarizing older events while retaining recent events in full. Redis Agent Memory extracts long-term memories automatically from session events.\n\nAccess Redis Agent Memory through the Python and TypeScript SDKs or its REST API. It works with any agent framework or LLM provider.\n\n<div class=\"grid grid-cols-1 md:grid-cols-4 gap-6 my-8\">\n  \n  \n  \n  \n</div>"},{"id":"why-use-redis-agent-memory","title":"Why use Redis Agent Memory?","role":"content","text":"* **Context-aware conversations:** Store ordered conversation events with their actor, role, timestamp, and metadata, then retrieve them by session ID. Configure session expiration to control how long the conversation is retained.\n* **Automatic session summarization:** Automatically summarize older conversation events while retaining recent messages in full.\n* **Automatic long-term memory:** Automatically extract durable information from session events in the background. You can also create long-term memories directly from external data.\n* **Sensitive-data exclusions:** Guide automatic extraction away from information that should not be kept in long-term memory.\n* **Relevant retrieval:** Search long-term memory using semantic, keyword, or hybrid search.\n* **Multi-session recall:** Retrieve relevant memories across conversations and filter results by owner, session, namespace, topic, or memory type.\n* **Custom memory types:** Define memory types for your business domain, with structured fields and instructions that control what Redis Agent Memory extracts."},{"id":"two-tier-memory-model","title":"Two-tier memory model","role":"content","text":"Redis Agent Memory provides two memory tiers:\n\n<div class=\"grid grid-cols-1 md:grid-cols-2 gap-6 my-6\">\n  <div class=\"p-5 border border-redis-pen-300 rounded-lg\">\n    <h3 class=\"text-redis-ink-900 font-semibold mb-3\">Session memory</h3>\n    <p>Stores the ordered events and metadata for a conversation.</p>\n    <ul class=\"space-y-2\">\n      <li><strong>Configurable retention:</strong> Set a time-to-live (TTL) to control how long session events are retained.</li>\n      <li><strong>Automatic summarization:</strong> Condense older events after a configured threshold while retaining recent events in full, reducing the conversation history sent to the model's context window.</li>\n    </ul>\n  </div>\n  <div class=\"p-5 border border-redis-pen-300 rounded-lg\">\n    <h3 class=\"text-redis-ink-900 font-semibold mb-3\">Long-term memory</h3>\n    <p>Stores durable information that can be retrieved across sessions using semantic, keyword, or hybrid search.</p>\n    <ul class=\"space-y-2\">\n      <li><strong>Automatic extraction:</strong> Process session events asynchronously and store important information with vector embeddings and metadata.</li>\n      <li><strong>Custom memory types:</strong> Define domain-specific memories with structured fields and extraction instructions.</li>\n      <li><strong>Sensitive-data exclusions:</strong> Guide automatic extraction away from specified sensitive information.</li>\n      <li><strong>Direct memory creation:</strong> Create memories through the API or import knowledge from external sources.</li>\n      <li><strong>Configurable retention:</strong> Set a separate TTL for long-term memories.</li>\n    </ul>\n  </div>\n</div>\n\nTo watch both tiers change during a conversation, try the [interactive demo](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/interactive-demo)."},{"id":"example-travel-planning-agent","title":"Example: Travel planning agent","role":"example","text":"Consider a travel agent helping a user plan a trip:\n\n[code example]\n\n<div class=\"grid grid-cols-1 md:grid-cols-2 gap-6 my-6\">\n  <div class=\"p-5 border border-redis-pen-300 rounded-lg\">\n    <h4 class=\"text-redis-ink-900 font-semibold mb-3\">Session memory</h4>\n    <p>Stores an ordered sequence of events under a session ID, including each event's role, content, timestamps, and metadata. Before the next agent turn, the application can retrieve the session to reconstruct the conversation context.</p>\n  </div>\n  <div class=\"p-5 border border-redis-pen-300 rounded-lg\">\n    <h4 class=\"text-redis-ink-900 font-semibold mb-3\">Automatic session summarization</h4>\n    <p>Summarizes older events after the configured threshold while retaining recent messages in full. The application can provide relevant conversation history to the agent without filling the model's context window with every original event.</p>\n  </div>\n  <div class=\"p-5 border border-redis-pen-300 rounded-lg\">\n    <h4 class=\"text-redis-ink-900 font-semibold mb-3\">Automatic long-term memory extraction</h4>\n    <p>Extracts durable information in the background, such as \"The user is vegetarian.\" Later sessions can retrieve it after the original session expires.</p>\n  </div>\n  <div class=\"p-5 border border-redis-pen-300 rounded-lg\">\n    <h4 class=\"text-redis-ink-900 font-semibold mb-3\">Custom memory types</h4>\n    <p>Stores domain-specific information in a custom <code>trip_preference</code> type with fields such as <code>destination</code>, <code>travel_period</code>, and <code>dietary_requirement</code>.</p>\n  </div>\n</div>"},{"id":"get-started","title":"Get started with Redis Agent Memory","role":"content","text":"Get started with Redis Agent Memory on Redis Cloud or join the private preview for Redis Software.\n\n<div class=\"grid grid-cols-1 md:grid-cols-2 gap-6 my-8\">\n  <div class=\"p-5 border border-redis-pen-300 rounded-lg\">\n    <h3 class=\"text-redis-ink-900 font-semibold mb-3\">Redis Cloud</h3>\n    <p>Create a managed Redis Agent Memory service and make your first requests.</p>\n    <p><a href=\"https://redis.io/docs/latest/operate/iris/agent-memory/create-service\">Open the Redis Cloud setup guide</a></p>\n  </div>\n  <div class=\"p-5 border border-redis-pen-300 rounded-lg\">\n    <h3 class=\"text-redis-ink-900 font-semibold mb-3\">Redis Software private preview</h3>\n    <p>Deploy Redis Agent Memory on Kubernetes with Redis Software.</p>\n    <p><a href=\"https://redis.io/docs/latest/operate/iris/agent-memory/self-managed\">Open the self-managed deployment guide</a></p>\n  </div>\n</div>"},{"id":"choose-a-quickstart","title":"Choose a quickstart","role":"content","text":"After your Redis Agent Memory service is ready, choose a client. Each quickstart follows the same travel planning scenario through session memory, automatic extraction, summarization, custom memory types, and sensitive-data exclusions.\n\n<div class=\"grid grid-cols-1 md:grid-cols-3 gap-6 my-6\">\n  <div class=\"p-5 border border-redis-pen-300 rounded-lg\">\n    <h3 class=\"text-redis-ink-900 font-semibold mb-3\">Python SDK</h3>\n    <p>Explore the Redis Agent Memory workflow with the Python SDK.</p>\n    <p><a href=\"https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/python-sdk-quickstart\">Open the Python quickstart</a></p>\n  </div>\n  <div class=\"p-5 border border-redis-pen-300 rounded-lg\">\n    <h3 class=\"text-redis-ink-900 font-semibold mb-3\">TypeScript SDK</h3>\n    <p>Explore the Redis Agent Memory workflow with the TypeScript SDK.</p>\n    <p><a href=\"https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/typescript-sdk-quickstart\">Open the TypeScript quickstart</a></p>\n  </div>\n  <div class=\"p-5 border border-redis-pen-300 rounded-lg\">\n    <h3 class=\"text-redis-ink-900 font-semibold mb-3\">REST API</h3>\n    <p>Explore the Redis Agent Memory workflow with <code>curl</code>.</p>\n    <p><a href=\"https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/rest-api-quickstart\">Open the REST API quickstart</a></p>\n  </div>\n</div>\n\nFor shared integration concepts, identifiers, and authentication, see the [Redis Agent Memory developer guide](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/developer-guide).\n\nTo generate a complete conversational agent that uses Agent Memory, open the [AI agent builder](https://redis.io/docs/latest/develop/ai/agent-builder) and select **Redis Iris Conversational Assistant**."}],"examples":[{"id":"example-travel-planning-agent-ex0","language":"text","code":"User: I'm planning a trip to Japan next month and need help finding some restaurants for the trip.\nAgent: Nice! What cities are you visiting?\nUser: I'm going to Tokyo and Kyoto. Also, I'm a vegetarian.\nAgent: Good to know! I'll help you find some vegetarian-friendly restaurants in Tokyo and Kyoto.","section_id":"example-travel-planning-agent"}]}
{"schema_version":2,"id":"develop/ai/context-engine/agent-memory/interactive-demo","title":"Redis Agent Memory interactive demo","url":"https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/interactive-demo/","summary":"Watch session memory and long-term memory change as a customer talks to an AI assistant, then see what the assistant recalls in a later conversation.","tags":["docs","develop","ai"],"last_updated":"2026-10-01T10:13:31+02:00","page_type":"content","content_hash":"4326c87e06220162c02fea715f0e5d8f24ef46f375c95f3326f673a31c03ad75","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Agent Memory gives an AI agent two tiers of memory. Session memory holds the current conversation, and long-term memory holds what the agent should remember across conversations. This demo follows one customer of a food delivery app through two conversations. You choose what she says, and the demo shows what each tier stores, when it changes, and how long it lasts.\n\nThe demo runs in your browser and doesn't connect to an Agent Memory service. Its requests and responses follow the shapes of the [Agent Memory API](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/api-reference). The customer, restaurants, and order come from the same sample data as the [Context Retriever interactive demo](https://redis.io/docs/latest/develop/ai/context-engine/context-retriever/interactive-demo).\n\n\n\nTry different messages to see how they change what the service remembers. Open **Service settings** to change how the service behaves. For example, have the customer share her gate code, then set sensitive-data exclusions to **Off** and see where the code ends up."},{"id":"session-memory-and-long-term-memory-compared","title":"Session memory and long-term memory compared","role":"content","text":"| | Session memory | Long-term memory |\n|:--|:--|:--|\n| **What it stores** | Every event in a conversation, as sent and in order | Standalone facts and events that may be useful in later conversations |\n| **When it's written** | When your application adds an event | In the background on the extraction cadence, or when your application creates memories directly |\n| **How your application reads it** | Fetches the session by its ID | Searches by meaning, filtered by owner, session, namespace, topic, or memory type |\n| **How long it's kept** | The short-term TTL, 1 hour by default | The long-term TTL, 365 days by default |\n| **How it stays compact** | Automatic summarization condenses older events | Extraction keeps only the information worth remembering |\n| **Sensitive-data exclusions** | Not applied. Events and summaries are kept as sent | Applied when memories are extracted from a session |"},{"id":"next-steps","title":"Next steps","role":"content","text":"- [Create an Agent Memory service](https://redis.io/docs/latest/operate/iris/agent-memory/create-service) on Redis Cloud.\n- Follow the [REST API quickstart](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/rest-api-quickstart) or the [Python SDK quickstart](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/python-sdk-quickstart) to try the same flow with a real service."}],"examples":[]}
{"schema_version":2,"id":"develop/ai/context-engine/agent-memory/overview","title":"Redis Agent Memory overview","url":"https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/overview/","summary":"How Redis Agent Memory's session and long-term memory tiers work, and what runs automatically in the background.","aliases":["/develop/ai/context-engine/agent-memory/concepts/"],"tags":["docs","develop","ai"],"last_updated":"2026-10-02T06:51:20-07:00","page_type":"content","content_hash":"d7ffc94dc5f009b69d228390a521f5eb2a7cf9c1c82dcba98b951317ec7dcfbe","sections":[{"id":"background-summarization-and-extraction","title":"Background summarization and extraction","role":"overview","text":"Agent Memory has two writers. Your application writes session events as they happen. After you enable summarization and extraction, Agent Memory reads those events in the background and does two things without an application write:\n\n- **Summarizes** older events into a compact summary once the session passes a configured threshold, so a long conversation doesn't blow the model's context window.\n- **Extracts** long-term memories, facts and preferences worth keeping, and writes them as separate, searchable records with vector embeddings.\n\n![An application writes session events synchronously to session memory. Background extraction moves durable information into long-term memory, which the application can search directly.](https://redis.io/docs/latest/images/ai/context-engine/agent-memory-writers.svg)\n\nBoth run asynchronously, to keep session writes fast. Extraction also weighs a new memory against existing memories before writing it. Rather than rejecting anything that looks similar, it uses model judgment to decide whether a near-identical memory is a true duplicate or is meaningfully different and worth keeping too."},{"id":"memory-types","title":"Memory types","role":"content","text":"You can think of Redis Agent Memory's tiers as analogous to human memory:\n\n| Human memory | What it holds | Redis Agent Memory tier |\n|:---|:---|:---|\n| Working memory | What's being discussed right now | Session memory: the ordered events of the current conversation |\n| Episodic memory | What happened in a specific past experience | Long-term memory, `episodic` type: a snapshot from one session |\n| Semantic memory | Facts and preferences you've generalized over time | Long-term memory, `semantic` type: durable, cross-session facts |\n\nSession memory is the working set: the current conversation's events, read and written every turn. Long-term memory is what survives after the session ends, durable enough to recall in a conversation the agent hasn't seen before.\n\nThis table is a simplified starting point, not the full picture. Redis Agent Memory also maintains an automatically updated summary of each session, as derived session state. It returns this summary separately from long-term memories. You can also define custom long-term memory types for domain-specific information. See [Memory types & extraction](https://redis.io/docs/latest/operate/iris/agent-memory/create-service#memory-types-and-extraction) for the complete set of built-in and custom types."},{"id":"adding-to-session-memory","title":"Adding to session memory","role":"content","text":"Your application gives Agent Memory the conversation for extracting long-term memories by appending to session memory. Think of session memory as a shared transcript that both your application and the background extraction pipeline read."},{"id":"key-terms","title":"Key terms","role":"content","text":"- **Session event**: One message in a conversation, added with `sessionId`, `actorId`, `role`, `content`, and `createdAt`.\n- **actorId**: Identifies who or what sent the event, a user, the agent, or a tool.\n- **ownerId**: Identifies who a long-term memory belongs to, for search and filtering later. Independent of who wrote the session event it came from.\n- **memoryType**: The long-term memory tier a fact is stored under, one of the built-in types (`semantic`, `episodic`, `message`, `session_summary_view`) or a custom type you define."},{"id":"how-it-works","title":"How it works","role":"content","text":"1. **Add the event.** Your application sends the new turn to session memory as it happens. This write is synchronous and fast.\n2. **Extraction reads the session.** In the background, extraction reads the session's accumulated events, not just the latest one, so it can resolve context across turns.\n3. **Long-term memory is written.** Facts worth keeping are stored as separate, searchable records, scoped by owner and memory type.\n\nExtraction reads the whole session. You only need to send each new turn as it happens. You don't need to resend earlier turns for a later one to be understood in context."},{"id":"faq","title":"FAQ","role":"content","text":"**Do I still need a separate store for conversation state?**\nNo, not for that purpose. Session memory holds the ordered events of a conversation, with automatic expiration based on a time-to-live (TTL) you set, and no schema to design. It doesn't replace a general-purpose cache or your application's other session data unrelated to the conversation.\n\n**What happens if I write directly to long-term memory instead of letting extraction do it?**\nBoth paths are supported. Use direct writes for bulk imports or external knowledge sources: anything that didn't originate in a conversation. Automatic extraction is for facts that emerge from session events themselves."},{"id":"next-steps","title":"Next steps","role":"content","text":"- Try the [Agent Memory interactive demo](content/develop/ai/context-engine/agent-memory/interactive-demo.md) to watch session memory and long-term memory change during a conversation.\n- [Developer guide](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/developer-guide) to connect an application and start writing session events.\n- [Python SDK quickstart](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/python-sdk-quickstart), [TypeScript SDK quickstart](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/typescript-sdk-quickstart), or [REST API quickstart](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/rest-api-quickstart) to see session memory, extraction, and summarization in action."}],"examples":[]}
{"schema_version":2,"id":"develop/ai/context-engine/agent-memory/python-sdk-quickstart","title":"Redis Agent Memory Python SDK quickstart","url":"https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/python-sdk-quickstart/","summary":"Explore session memory, automatic extraction, summarization, custom memory types, and sensitive-data exclusions with the Redis Agent Memory Python SDK.","tags":["docs","develop","ai"],"last_updated":"2026-10-01T12:49:59-07:00","page_type":"content","content_hash":"e3de4a16ffff8451c2bd56017aa83bde5c854834e193e5eb4df96bc0ef0fbc48","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Use this quickstart to follow a travel planning conversation through Redis Agent Memory. You will retrieve the conversation from session memory, recall information extracted in the background, inspect an automatically generated session summary, extract structured travel information, and guide extraction away from sensitive data."},{"id":"before-you-begin","title":"Before you begin","role":"content","text":"To complete this quickstart, you need:\n\n* A Redis Cloud account that can create Redis Agent Memory services.\n* An eligible Redis Cloud database, or permission to create one.\n\nThe database must be active, run a supported Redis version, and use a Pro or Essentials plan. It must also have a public endpoint, Redis Search, and the default user enabled.\n\nRedis Agent Memory doesn't support Flex, Active-Active, or AWS PrivateLink databases during public preview.\n\nFor the complete list, see [prerequisites and limitations](https://redis.io/docs/latest/operate/iris/agent-memory/create-service#prerequisites-and-limitations).\n\n\nYou also need Python 3.10 or later."},{"id":"create-a-redis-agent-memory-service","title":"Create a Redis Agent Memory service","role":"content","text":"1. Sign in to the [Redis Cloud console](https://cloud.redis.io/).\n1. Select **Agent Memory** from the navigation menu.\n1. If Redis Cloud displays the public preview terms, review and accept them.\n1. Select **Create custom service**.\n1. Enter a service name, select an eligible database, and select its `default` user.\n1. Under **Memory configuration**, enter these values:\n\n    | Setting | Value | What it controls |\n    |:--------|:------|:-----------------|\n    | **Short-term TTL** | `1` day | How long session memory is retained. |\n    | **Long-term TTL** | `365` days | How long long-term memories are retained. |\n    | **Extraction cadence** | `1` minute | How often session events are processed for extraction. One minute is for this quickstart; use a longer production interval unless you need rapid extraction. |\n    | **Automatic summarization** | Enabled | Whether older session events are condensed into a summary. |\n    | **Summarize after (messages)** | `6` | The event count that triggers summarization. Six is for this quickstart; use a higher production threshold. |\n    | **Keep most recent (messages)** | `2` | How many recent events remain in full. Two is for this quickstart; retain more in production when recent turns are needed. |\n\n1. Under **Memory types & extraction**, select **Add type** and configure this custom memory type:\n\n    | Setting | Value | What it controls |\n    |:--------|:------|:-----------------|\n    | **Name** | `trip_preference` | The identifier stored in `memoryType` and used in search filters. |\n    | **Description** | `Structured requirements for a planned trip` | The purpose of the custom memory type. |\n    | **Extraction prompt** | `Extract trip requirements only when the user states a destination or travel plan. Preserve explicit dietary requirements and food preferences.` | When to create the memory and which information to capture. |\n    | **Enabled** | Enabled | Whether new memories of this type are extracted. |\n\n1. Add these custom fields:\n\n    | Field | Type | Description |\n    |:------|:-----|:------------|\n    | `destinations` | `list[str]` | Cities or countries the user plans to visit. |\n    | `travel_period` | `str` | When the user plans to travel. |\n    | `dietary_requirements` | `list[str]` | Dietary requirements that affect recommendations. |\n    | `food_preferences` | `list[str]` | Cuisines, flavors, or dining preferences stated by the user. |\n\n1. Under **Sensitive-data exclusions**, enable **Semantic exclusions** and enter this exclusion prompt:\n\n    [code example]\n\n1. Select **Create**.\n1. Copy the Redis Agent Memory API key and store it securely.\n\n> [!WARNING]\n> Redis Cloud displays the Redis Agent Memory API key only once. If you lose it, [generate a new API key](https://redis.io/docs/latest/operate/iris/agent-memory/view-service#replace-service-api-key).\n\nThese settings keep the background stages short enough to observe during the quickstart. For screenshots and configuration details, see [create a Redis Agent Memory service](https://redis.io/docs/latest/operate/iris/agent-memory/create-service).\n\n> [!WARNING]\n> Sensitive-data exclusions guide the extraction model but do not guarantee exclusion. Sensitive session content still reaches the model provider. Exclusions do not apply when an application creates long-term memories directly."},{"id":"save-the-connection-values","title":"Save the connection values","role":"content","text":"1. Open the Redis Agent Memory service in the Redis Cloud console.\n1. On the **Configuration** tab, copy the **Endpoint** and **Store ID**.\n1. Export the API key in your shell:\n\n    [code example]\n\nKeep the API key out of source control, application logs, and other unsecured locations."},{"id":"install-the-sdk","title":"Install the SDK","role":"setup","text":"[code example]"},{"id":"create-the-client-and-check-the-service-health","title":"Create the client and check the service health","role":"content","text":"Create `quickstart.py` with the following code. Replace `<ENDPOINT>` and `<STORE_ID>` with the values from Redis Cloud. The endpoint must include `https://`.\n\n[code example]\n\nRun the file:\n\n[code example]\n\nA healthy response confirms that the client can reach Redis Agent Memory and authenticate with the API key. The first store request validates the Store ID."},{"id":"1-build-conversation-context-with-session-memory","title":"1. Build conversation context with session memory","role":"content","text":"Session memory stores a conversation as an ordered sequence of events. Add the following code after the health check, inside the `with` block:\n\n[code example]\n\nRun the file again. The session response contains the stored message, its role, actor, and timestamps. An application can retrieve this session before the next agent turn and add the events to the model's context.\n\n> [!NOTE]\n> **What to expect:** The `events` array contains the travel message. Redis Agent Memory adds an `eventId` and `systemTimestamp`, showing that the application can recover the complete event later using only the session ID.\n\nAfter the event is stored, comment out the call to `add_session_event` before subsequent runs to avoid adding the same message again."},{"id":"2-recall-automatically-extracted-information","title":"2. Recall automatically extracted information","role":"content","text":"Redis Agent Memory processes session events in the background and creates long term memories for information that may be useful in later conversations. You configured the extraction cadence to one minute when you created the service. You do not need to call a memory creation method.\n\nWait at least one minute, then add this search after the session retrieval:\n\n[code example]\n\nRun the file. The `items` array should contain memories derived from the conversation, such as the vegetarian requirement or preference for spicy food. Extraction is asynchronous, so run the search again if the array is empty.\n\n> [!NOTE]\n> **What to expect:** Results similar to `User is a vegetarian` and `User prefers spicy food`. Your application did not create these memories directly. Redis Agent Memory derived them from the session event. The exact text and memory types can vary.\n\nThe extracted memory remains searchable after the session expires, subject to the long term memory TTL. You can change the extraction cadence and both TTLs in the [Redis Agent Memory service configuration](https://redis.io/docs/latest/operate/iris/agent-memory/create-service#memory-configuration).\n\nThe Python SDK uses snake case for method arguments and request fields. Serialized API requests and responses use camel case."},{"id":"3-keep-long-conversations-concise-with-automatic-summarization","title":"3. Keep long conversations concise with automatic summarization","role":"content","text":"Automatic summarization condenses older events and retains the most recent events in full. The retrieved session then contains a `summary` object and the recent `events` array, so the application can provide useful history without filling the model's context window with every original message.\n\nYou enabled automatic summarization when you created the service. When the session reaches six events, Redis Agent Memory summarizes the older events and retains the two most recent events in full."},{"id":"add-conversation-turns","title":"Add conversation turns","role":"content","text":"Add this code after the first session event to continue the conversation past the configured threshold:\n\n[code example]\n\nRun the code once, then comment out the loop to avoid adding the same turns again. Summarization runs in the background."},{"id":"retrieve-the-summarized-session","title":"Retrieve the summarized session","role":"content","text":"After a short wait, retrieve the session again:\n\n[code example]\n\nRun the retrieval again after a short wait if `summary` is not present. Compare `summary.text` with the recent events. The summary should preserve earlier trip decisions while recent turns remain available in full.\n\n> [!NOTE]\n> **What to expect:** A `summary` object that preserves details such as Tokyo, Kyoto, the travel dates, and food preferences. `summarizedUpToEventId` identifies the last event covered by the summary, while `events` contains the newer turns that remain in full. The exact summary text can vary.\n\nSee [automatic summarization configuration](https://redis.io/docs/latest/operate/iris/agent-memory/create-service#automatic-summarization) for details."},{"id":"4-extract-business-specific-data-with-a-custom-memory-type","title":"4. Extract business specific data with a custom memory type","role":"content","text":"Built in memories preserve generally useful information. Custom memory types let an application extract structured information for its business domain. You configured `trip_preference` when you created the service, so it processed the same travel planning event independently.\n\nSearch for the structured memory:\n\n[code example]\n\nThe result uses `trip_preference` as its `memoryType` and contains travel information extracted from the conversation. The exact text and returned fields depend on the conversation, extraction model, and client.\n\n> [!NOTE]\n> **What to expect:** A result with `memoryType` set to `trip_preference` that combines the destinations, travel period, and dietary preferences. This shows that the custom type processed the same conversation independently from the built-in memory types.\n\nSee [custom memory types](https://redis.io/docs/latest/operate/iris/agent-memory/create-service#custom-memory-types) for configuration requirements and limits."},{"id":"5-guide-extraction-away-from-sensitive-data","title":"5. Guide extraction away from sensitive data","role":"content","text":"The semantic exclusion prompt tells Redis Agent Memory which information should not be kept in long-term memory. Add an event containing a fictional booking code and information that is safe to retain:\n\n[code example]\n\nRun the code once, then comment out the call to `add_session_event`. Wait at least one minute and search for the safe hotel information:\n\n[code example]\n\nInspect the returned memories. They can retain the hotel name, but should not contain `DEMO-7QX9` because the exclusion prompt covers booking confirmation codes.\n\n> [!NOTE]\n> **What to expect:** A memory similar to `User booked Hotel Sakura in Tokyo` without the fictional confirmation code. If the code appears, refine the exclusion prompt and test again. Exclusions remain advisory.\n\n> [!WARNING]\n> Semantic exclusions are advisory and do not guarantee that sensitive information is excluded. Session content still reaches the extraction model provider. Do not use real sensitive data in this exercise. Exclusions do not apply to directly created long-term memories.\n\nSee [sensitive-data exclusions](https://redis.io/docs/latest/operate/iris/agent-memory/create-service#sensitive-data-exclusions) for configuration details."},{"id":"next-steps","title":"Next steps","role":"content","text":"* Review the [Python SDK package and reference](https://pypi.org/project/redis-agent-memory/).\n* Generate a complete conversational agent with the [AI agent builder](https://redis.io/docs/latest/develop/ai/agent-builder). Select **Redis Iris Conversational Assistant** and **Python**.\n* Try the [TypeScript SDK quickstart](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/typescript-sdk-quickstart) or [REST API quickstart](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/rest-api-quickstart).\n* Learn when to [create long term memories directly](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/developer-guide#create-long-term-memories)."}],"examples":[{"id":"create-a-redis-agent-memory-service-ex0","language":"text","code":"Do not keep passwords, access tokens, recovery codes, payment card information, or booking confirmation codes in long-term memory.","section_id":"create-a-redis-agent-memory-service"},{"id":"save-the-connection-values-ex0","language":"sh","code":"export API_KEY='<API_KEY>'","section_id":"save-the-connection-values"},{"id":"install-the-sdk-ex0","language":"sh","code":"python -m pip install redis-agent-memory","section_id":"install-the-sdk"},{"id":"create-the-client-and-check-the-service-health-ex0","language":"python","code":"import os\nfrom datetime import datetime, timezone\n\nfrom redis_agent_memory import AgentMemory, models\n\n\nENDPOINT = \"<ENDPOINT>\"\nSTORE_ID = \"<STORE_ID>\"\nSESSION_ID = \"travel-planning-session\"\nUSER_ID = \"quickstart-user\"\n\n\ndef show(label, response):\n    print(f\"{label}:\")\n    print(response.model_dump_json(by_alias=True, indent=2))\n\n\ndef main():\n    with AgentMemory(\n        ENDPOINT,\n        store_id=STORE_ID,\n        api_key=os.environ[\"API_KEY\"],\n    ) as agent_memory:\n        health = agent_memory.health()\n        show(\"Service health\", health)\n\n\nif __name__ == \"__main__\":\n    main()","section_id":"create-the-client-and-check-the-service-health"},{"id":"create-the-client-and-check-the-service-health-ex1","language":"sh","code":"python quickstart.py","section_id":"create-the-client-and-check-the-service-health"},{"id":"1-build-conversation-context-with-session-memory-ex0","language":"python","code":"event = agent_memory.add_session_event(\n            session_id=SESSION_ID,\n            actor_id=USER_ID,\n            role=models.MessageRole.USER,\n            content=[models.Text(\n                text=(\n                    \"I am visiting Tokyo and Kyoto next month. \"\n                    \"I am vegetarian and prefer spicy food.\"\n                ),\n            )],\n            created_at=datetime.now(timezone.utc),\n        )\n        show(\"Created event\", event)\n\n        session = agent_memory.get_session_memory(\n            session_id=SESSION_ID,\n        )\n        show(\"Session memory\", session)","section_id":"1-build-conversation-context-with-session-memory"},{"id":"2-recall-automatically-extracted-information-ex0","language":"python","code":"results = agent_memory.search_long_term_memory(\n            request={\n                \"text\": \"What dietary requirements and food preferences does the user have?\",\n                \"filter_\": {\n                    \"owner_id\": {\n                        \"eq\": USER_ID,\n                    }\n                },\n                \"limit\": 5,\n            },\n        )\n        show(\"Automatically extracted memories\", results)","section_id":"2-recall-automatically-extracted-information"},{"id":"add-conversation-turns-ex0","language":"python","code":"turns = [\n            (models.MessageRole.ASSISTANT, \"What dates are you traveling?\"),\n            (models.MessageRole.USER, \"I arrive on October 10 and leave on October 18.\"),\n            (models.MessageRole.ASSISTANT, \"Would you like formal or casual restaurants?\"),\n            (models.MessageRole.USER, \"Mostly casual places near public transit.\"),\n            (models.MessageRole.ASSISTANT, \"Do you have a preferred budget?\"),\n            (models.MessageRole.USER, \"About 40 euros per person.\"),\n        ]\n\n        for role, text in turns:\n            agent_memory.add_session_event(\n                session_id=SESSION_ID,\n                actor_id=USER_ID if role == models.MessageRole.USER else \"travel-agent\",\n                role=role,\n                content=[models.Text(text=text)],\n                created_at=datetime.now(timezone.utc),\n            )","section_id":"add-conversation-turns"},{"id":"retrieve-the-summarized-session-ex0","language":"python","code":"compacted_session = agent_memory.get_session_memory(\n            session_id=SESSION_ID,\n        )\n        show(\"Compacted session memory\", compacted_session)","section_id":"retrieve-the-summarized-session"},{"id":"4-extract-business-specific-data-with-a-custom-memory-type-ex0","language":"python","code":"custom_results = agent_memory.search_long_term_memory(\n            request={\n                \"text\": \"What are the requirements for the user's trip?\",\n                \"filter_\": {\n                    \"owner_id\": {\"eq\": USER_ID},\n                    \"memory_type\": {\"eq\": \"trip_preference\"},\n                },\n                \"limit\": 5,\n            },\n        )\n        show(\"Trip preference memories\", custom_results)","section_id":"4-extract-business-specific-data-with-a-custom-memory-type"},{"id":"5-guide-extraction-away-from-sensitive-data-ex0","language":"python","code":"sensitive_event = agent_memory.add_session_event(\n            session_id=SESSION_ID,\n            actor_id=USER_ID,\n            role=models.MessageRole.USER,\n            content=[models.Text(\n                text=(\n                    \"I booked Hotel Sakura in Tokyo. For this example, \"\n                    \"the fictional booking confirmation code is DEMO-7QX9.\"\n                ),\n            )],\n            created_at=datetime.now(timezone.utc),\n        )\n        show(\"Event with excluded information\", sensitive_event)","section_id":"5-guide-extraction-away-from-sensitive-data"},{"id":"5-guide-extraction-away-from-sensitive-data-ex1","language":"python","code":"exclusion_results = agent_memory.search_long_term_memory(\n            request={\n                \"text\": \"Where is the user staying in Tokyo?\",\n                \"filter_\": {\n                    \"owner_id\": {\"eq\": USER_ID},\n                },\n                \"limit\": 5,\n            },\n        )\n        show(\"Memories after semantic exclusion\", exclusion_results)","section_id":"5-guide-extraction-away-from-sensitive-data"}]}
{"schema_version":2,"id":"develop/ai/context-engine/agent-memory/rest-api-quickstart","title":"Redis Agent Memory REST API quickstart","url":"https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/rest-api-quickstart/","summary":"Explore session memory, automatic extraction, summarization, custom memory types, and sensitive-data exclusions with the Redis Agent Memory REST API.","aliases":["/operate/rc/context-engine/agent-memory/use-agent-memory/","/develop/ai/context-engine/agent-memory/api-examples/"],"tags":["docs","develop","ai"],"last_updated":"2026-09-11T15:54:40+01:00","page_type":"content","content_hash":"5ef4688c58fe37e2c1b198ec7934066184e1e264448ab06a562c106c7032727f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Use this quickstart to follow a travel planning conversation through Redis Agent Memory. You will retrieve the conversation from session memory, recall information extracted in the background, inspect an automatically generated session summary, extract structured travel information, and guide extraction away from sensitive data."},{"id":"before-you-begin","title":"Before you begin","role":"content","text":"To complete this quickstart, you need:\n\n* A Redis Cloud account that can create Redis Agent Memory services.\n* An eligible Redis Cloud database, or permission to create one.\n* A shell with `curl` and `jq` installed."},{"id":"create-a-redis-agent-memory-service","title":"Create a Redis Agent Memory service","role":"content","text":"1. Sign in to the [Redis Cloud console](https://cloud.redis.io/).\n1. Select **Agent Memory** from the navigation menu.\n1. If Redis Cloud displays the public preview terms, review and accept them.\n1. Select **Create custom service**.\n1. Enter a service name, select an eligible database, and select its `default` user.\n1. Under **Memory configuration**, enter these values:\n\n    | Setting | Value | What it controls |\n    |:--------|:------|:-----------------|\n    | **Short-term TTL** | `1` day | How long session memory is retained. |\n    | **Long-term TTL** | `365` days | How long long-term memories are retained. |\n    | **Extraction cadence** | `1` minute | How often session events are processed for extraction. One minute is for this quickstart; use a longer production interval unless you need rapid extraction. |\n    | **Automatic summarization** | Enabled | Whether older session events are condensed into a summary. |\n    | **Summarize after (messages)** | `6` | The event count that triggers summarization. Six is for this quickstart; use a higher production threshold. |\n    | **Keep most recent (messages)** | `2` | How many recent events remain in full. Two is for this quickstart; retain more in production when recent turns are needed. |\n\n1. Under **Memory types & extraction**, select **Add type** and configure this custom memory type:\n\n    | Setting | Value | What it controls |\n    |:--------|:------|:-----------------|\n    | **Name** | `trip_preference` | The identifier stored in `memoryType` and used in search filters. |\n    | **Description** | `Structured requirements for a planned trip` | The purpose of the custom memory type. |\n    | **Extraction prompt** | `Extract trip requirements only when the user states a destination or travel plan. Preserve explicit dietary requirements and food preferences.` | When to create the memory and which information to capture. |\n    | **Enabled** | Enabled | Whether new memories of this type are extracted. |\n\n1. Add these custom fields:\n\n    | Field | Type | Description |\n    |:------|:-----|:------------|\n    | `destinations` | `list[str]` | Cities or countries the user plans to visit. |\n    | `travel_period` | `str` | When the user plans to travel. |\n    | `dietary_requirements` | `list[str]` | Dietary requirements that affect recommendations. |\n    | `food_preferences` | `list[str]` | Cuisines, flavors, or dining preferences stated by the user. |\n\n1. Under **Sensitive-data exclusions**, enable **Semantic exclusions** and enter this exclusion prompt:\n\n    [code example]\n\n1. Select **Create**.\n1. Copy the Redis Agent Memory API key and store it securely.\n\n> [!WARNING]\n> Redis Cloud displays the Redis Agent Memory API key only once. If you lose it, [generate a new API key](https://redis.io/docs/latest/operate/iris/agent-memory/view-service#replace-service-api-key).\n\nThese settings keep the background stages short enough to observe during the quickstart. For screenshots and configuration details, see [create a Redis Agent Memory service](https://redis.io/docs/latest/operate/iris/agent-memory/create-service).\n\n> [!WARNING]\n> Sensitive-data exclusions guide the extraction model but do not guarantee exclusion. Sensitive session content still reaches the model provider. Exclusions do not apply when an application creates long-term memories directly."},{"id":"save-the-connection-values","title":"Save the connection values","role":"content","text":"1. Open the Redis Agent Memory service in the Redis Cloud console.\n1. On the **Configuration** tab, copy the **Endpoint** and **Store ID**.\n1. Export the values in your shell:\n\n    [code example]\n\n`AGENT_MEMORY_URL` must include `https://`. Keep the API key out of source control, application logs, and other unsecured locations."},{"id":"check-the-service-health","title":"Check the service health","role":"content","text":"Verify that the service is available:\n\n[code example]"},{"id":"1-build-conversation-context-with-session-memory","title":"1. Build conversation context with session memory","role":"content","text":"Session memory stores a conversation as an ordered sequence of events. Add a user message that contains details the travel agent will need later:\n\n[code example]\n\nRetrieve the session:\n\n[code example]\n\nThe `events` array contains the stored message, its role, actor, and timestamps. An application can retrieve this session before the next agent turn and add the events to the model's context.\n\n> [!NOTE]\n> **What to expect:** The `events` array contains the travel message. Redis Agent Memory adds an `eventId` and `systemTimestamp`, showing that the application can recover the complete event later using only the session ID."},{"id":"2-recall-automatically-extracted-information","title":"2. Recall automatically extracted information","role":"content","text":"Redis Agent Memory processes session events in the background and creates long term memories for information that may be useful in later conversations. You configured the extraction cadence to one minute when you created the service. You do not need to submit a separate memory creation request.\n\nWait at least one minute, then search for the user's dietary requirements:\n\n[code example]\n\nThe `items` array should contain memories derived from the conversation, such as the vegetarian requirement or preference for spicy food. Extraction is asynchronous, so run the search again if the array is empty.\n\n> [!NOTE]\n> **What to expect:** Results similar to `User is a vegetarian` and `User prefers spicy food`. Your application did not create these memories directly. Redis Agent Memory derived them from the session event. The exact text and memory types can vary.\n\nThe extracted memory remains searchable after the session expires, subject to the long term memory TTL. You can change the extraction cadence and both TTLs in the [Redis Agent Memory service configuration](https://redis.io/docs/latest/operate/iris/agent-memory/create-service#memory-configuration)."},{"id":"3-keep-long-conversations-concise-with-automatic-summarization","title":"3. Keep long conversations concise with automatic summarization","role":"content","text":"Automatic summarization condenses older events and retains the most recent events in full. The retrieved session then contains a `summary` object and the recent `events` array, so the application can provide useful history without filling the model's context window with every original message.\n\nYou enabled automatic summarization when you created the service. When the session reaches six events, Redis Agent Memory summarizes the older events and retains the two most recent events in full."},{"id":"add-conversation-turns","title":"Add conversation turns","role":"content","text":"Add enough user and assistant events to reach the configured threshold. Use the request from the first step and change `role`, `actorId`, `content`, and `createdAt` for each event. The Python and TypeScript quickstarts use six additional turns about travel dates, restaurant style, and budget.\n\nSummarization runs in the background after the session reaches the threshold."},{"id":"retrieve-the-summarized-session","title":"Retrieve the summarized session","role":"content","text":"After a short wait, retrieve the session again:\n\n[code example]\n\nRepeat the retrieval after a short wait if `summary` is not present. Compare `summary.text` with the recent events. The summary should preserve earlier decisions about the trip while recent turns remain available in full.\n\n> [!NOTE]\n> **What to expect:** A `summary` object that preserves details such as Tokyo, Kyoto, the travel dates, and food preferences. `summarizedUpToEventId` identifies the last event covered by the summary, while `events` contains the newer turns that remain in full. The exact summary text can vary.\n\nSee [automatic summarization configuration](https://redis.io/docs/latest/operate/iris/agent-memory/create-service#automatic-summarization) for details."},{"id":"4-extract-business-specific-data-with-a-custom-memory-type","title":"4. Extract business specific data with a custom memory type","role":"content","text":"Built in memories preserve generally useful information. Custom memory types let an application extract structured information for its business domain. You configured `trip_preference` when you created the service, so it processed the same travel planning event independently.\n\nSearch for the structured memory:\n\n[code example]\n\nThe result uses `trip_preference` as its `memoryType` and contains travel information extracted from the conversation. The exact text and returned fields depend on the conversation, extraction model, and client.\n\n> [!NOTE]\n> **What to expect:** A result with `memoryType` set to `trip_preference` that combines the destinations, travel period, and dietary preferences. This shows that the custom type processed the same conversation independently from the built-in memory types.\n\nSee [custom memory types](https://redis.io/docs/latest/operate/iris/agent-memory/create-service#custom-memory-types) for configuration requirements and limits."},{"id":"5-guide-extraction-away-from-sensitive-data","title":"5. Guide extraction away from sensitive data","role":"content","text":"The semantic exclusion prompt tells Redis Agent Memory which information should not be kept in long-term memory. Add an event containing a fictional booking code and information that is safe to retain:\n\n[code example]\n\nWait at least one minute and search for the safe hotel information:\n\n[code example]\n\nInspect the returned memories. They can retain the hotel name, but should not contain `DEMO-7QX9` because the exclusion prompt covers booking confirmation codes.\n\n> [!NOTE]\n> **What to expect:** A memory similar to `User booked Hotel Sakura in Tokyo` without the fictional confirmation code. If the code appears, refine the exclusion prompt and test again. Exclusions remain advisory.\n\n> [!WARNING]\n> Semantic exclusions are advisory and do not guarantee that sensitive information is excluded. Session content still reaches the extraction model provider. Do not use real sensitive data in this exercise. Exclusions do not apply to directly created long-term memories.\n\nSee [sensitive-data exclusions](https://redis.io/docs/latest/operate/iris/agent-memory/create-service#sensitive-data-exclusions) for configuration details."},{"id":"next-steps","title":"Next steps","role":"content","text":"* Follow the [Python SDK quickstart](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/python-sdk-quickstart) or [TypeScript SDK quickstart](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/typescript-sdk-quickstart).\n* Learn when to [create long term memories directly](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/developer-guide#create-long-term-memories).\n* Use the [Redis Agent Memory API reference](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/api-reference) for endpoint and schema details."}],"examples":[{"id":"create-a-redis-agent-memory-service-ex0","language":"text","code":"Do not keep passwords, access tokens, recovery codes, payment card information, or booking confirmation codes in long-term memory.","section_id":"create-a-redis-agent-memory-service"},{"id":"save-the-connection-values-ex0","language":"sh","code":"export AGENT_MEMORY_URL='<ENDPOINT>'\n    export STORE_ID='<STORE_ID>'\n    export API_KEY='<API_KEY>'\n    export SESSION_ID='travel-planning-session'\n    export OWNER_ID='quickstart-user'","section_id":"save-the-connection-values"},{"id":"check-the-service-health-ex0","language":"sh","code":"curl --fail-with-body --silent --show-error \\\n  --header \"Authorization: Bearer $API_KEY\" \\\n  \"$AGENT_MEMORY_URL/health\" | jq","section_id":"check-the-service-health"},{"id":"1-build-conversation-context-with-session-memory-ex0","language":"sh","code":"export EVENT_CREATED_AT=\"$(date -u +'%Y-%m-%dT%H:%M:%SZ')\"\n\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --header \"Authorization: Bearer $API_KEY\" \\\n  --header 'Content-Type: application/json' \\\n  --data @- \\\n  \"$AGENT_MEMORY_URL/v1/stores/$STORE_ID/session-memory/events\" <<JSON | jq\n{\n  \"sessionId\": \"$SESSION_ID\",\n  \"actorId\": \"$OWNER_ID\",\n  \"role\": \"USER\",\n  \"content\": [\n    {\n      \"text\": \"I am visiting Tokyo and Kyoto next month. I am vegetarian and prefer spicy food.\"\n    }\n  ],\n  \"createdAt\": \"$EVENT_CREATED_AT\"\n}\nJSON","section_id":"1-build-conversation-context-with-session-memory"},{"id":"1-build-conversation-context-with-session-memory-ex1","language":"sh","code":"curl --fail-with-body --silent --show-error \\\n  --header \"Authorization: Bearer $API_KEY\" \\\n  \"$AGENT_MEMORY_URL/v1/stores/$STORE_ID/session-memory/$SESSION_ID\" | jq","section_id":"1-build-conversation-context-with-session-memory"},{"id":"2-recall-automatically-extracted-information-ex0","language":"sh","code":"curl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --header \"Authorization: Bearer $API_KEY\" \\\n  --header 'Content-Type: application/json' \\\n  --data @- \\\n  \"$AGENT_MEMORY_URL/v1/stores/$STORE_ID/long-term-memory/search\" <<JSON | jq\n{\n  \"text\": \"What dietary requirements and food preferences does the user have?\",\n  \"filter\": {\n    \"ownerId\": {\n      \"eq\": \"$OWNER_ID\"\n    }\n  },\n  \"limit\": 5\n}\nJSON","section_id":"2-recall-automatically-extracted-information"},{"id":"retrieve-the-summarized-session-ex0","language":"sh","code":"curl --fail-with-body --silent --show-error \\\n  --header \"Authorization: Bearer $API_KEY\" \\\n  \"$AGENT_MEMORY_URL/v1/stores/$STORE_ID/session-memory/$SESSION_ID\" | jq","section_id":"retrieve-the-summarized-session"},{"id":"4-extract-business-specific-data-with-a-custom-memory-type-ex0","language":"sh","code":"curl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --header \"Authorization: Bearer $API_KEY\" \\\n  --header 'Content-Type: application/json' \\\n  --data @- \\\n  \"$AGENT_MEMORY_URL/v1/stores/$STORE_ID/long-term-memory/search\" <<JSON | jq\n{\n  \"text\": \"What are the requirements for the user's trip?\",\n  \"filter\": {\n    \"ownerId\": {\n      \"eq\": \"$OWNER_ID\"\n    },\n    \"memoryType\": {\n      \"eq\": \"trip_preference\"\n    }\n  },\n  \"limit\": 5\n}\nJSON","section_id":"4-extract-business-specific-data-with-a-custom-memory-type"},{"id":"5-guide-extraction-away-from-sensitive-data-ex0","language":"sh","code":"export EVENT_CREATED_AT=\"$(date -u +'%Y-%m-%dT%H:%M:%SZ')\"\n\ncurl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --header \"Authorization: Bearer $API_KEY\" \\\n  --header 'Content-Type: application/json' \\\n  --data @- \\\n  \"$AGENT_MEMORY_URL/v1/stores/$STORE_ID/session-memory/events\" <<JSON | jq\n{\n  \"sessionId\": \"$SESSION_ID\",\n  \"actorId\": \"$OWNER_ID\",\n  \"role\": \"USER\",\n  \"content\": [\n    {\n      \"text\": \"I booked Hotel Sakura in Tokyo. For this example, the fictional booking confirmation code is DEMO-7QX9.\"\n    }\n  ],\n  \"createdAt\": \"$EVENT_CREATED_AT\"\n}\nJSON","section_id":"5-guide-extraction-away-from-sensitive-data"},{"id":"5-guide-extraction-away-from-sensitive-data-ex1","language":"sh","code":"curl --fail-with-body --silent --show-error \\\n  --request POST \\\n  --header \"Authorization: Bearer $API_KEY\" \\\n  --header 'Content-Type: application/json' \\\n  --data @- \\\n  \"$AGENT_MEMORY_URL/v1/stores/$STORE_ID/long-term-memory/search\" <<JSON | jq\n{\n  \"text\": \"Where is the user staying in Tokyo?\",\n  \"filter\": {\n    \"ownerId\": {\n      \"eq\": \"$OWNER_ID\"\n    }\n  },\n  \"limit\": 5\n}\nJSON","section_id":"5-guide-extraction-away-from-sensitive-data"}]}
{"schema_version":2,"id":"develop/ai/context-engine/agent-memory/typescript-sdk-quickstart","title":"Redis Agent Memory TypeScript SDK quickstart","url":"https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/typescript-sdk-quickstart/","summary":"Explore session memory, automatic extraction, summarization, custom memory types, and sensitive-data exclusions with the Redis Agent Memory TypeScript SDK.","tags":["docs","develop","ai"],"last_updated":"2026-10-01T12:49:59-07:00","page_type":"content","content_hash":"0e6489d1b1d89c3f9089001241f59289734058d4389f4fe6eb2146b167a29b1f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Use this quickstart to follow a travel planning conversation through Redis Agent Memory. You will retrieve the conversation from session memory, recall information extracted in the background, inspect an automatically generated session summary, extract structured travel information, and guide extraction away from sensitive data."},{"id":"before-you-begin","title":"Before you begin","role":"content","text":"To complete this quickstart, you need:\n\n* A Redis Cloud account that can create Redis Agent Memory services.\n* An eligible Redis Cloud database, or permission to create one.\n\nThe database must be active, run a supported Redis version, and use a Pro or Essentials plan. It must also have a public endpoint, Redis Search, and the default user enabled.\n\nRedis Agent Memory doesn't support Flex, Active-Active, or AWS PrivateLink databases during public preview.\n\nFor the complete list, see [prerequisites and limitations](https://redis.io/docs/latest/operate/iris/agent-memory/create-service#prerequisites-and-limitations).\n\n\nYou also need Node.js and npm."},{"id":"create-a-redis-agent-memory-service","title":"Create a Redis Agent Memory service","role":"content","text":"1. Sign in to the [Redis Cloud console](https://cloud.redis.io/).\n1. Select **Agent Memory** from the navigation menu.\n1. If Redis Cloud displays the public preview terms, review and accept them.\n1. Select **Create custom service**.\n1. Enter a service name, select an eligible database, and select its `default` user.\n1. Under **Memory configuration**, enter these values:\n\n    | Setting | Value | What it controls |\n    |:--------|:------|:-----------------|\n    | **Short-term TTL** | `1` day | How long session memory is retained. |\n    | **Long-term TTL** | `365` days | How long long-term memories are retained. |\n    | **Extraction cadence** | `1` minute | How often session events are processed for extraction. One minute is for this quickstart; use a longer production interval unless you need rapid extraction. |\n    | **Automatic summarization** | Enabled | Whether older session events are condensed into a summary. |\n    | **Summarize after (messages)** | `6` | The event count that triggers summarization. Six is for this quickstart; use a higher production threshold. |\n    | **Keep most recent (messages)** | `2` | How many recent events remain in full. Two is for this quickstart; retain more in production when recent turns are needed. |\n\n1. Under **Memory types & extraction**, select **Add type** and configure this custom memory type:\n\n    | Setting | Value | What it controls |\n    |:--------|:------|:-----------------|\n    | **Name** | `trip_preference` | The identifier stored in `memoryType` and used in search filters. |\n    | **Description** | `Structured requirements for a planned trip` | The purpose of the custom memory type. |\n    | **Extraction prompt** | `Extract trip requirements only when the user states a destination or travel plan. Preserve explicit dietary requirements and food preferences.` | When to create the memory and which information to capture. |\n    | **Enabled** | Enabled | Whether new memories of this type are extracted. |\n\n1. Add these custom fields:\n\n    | Field | Type | Description |\n    |:------|:-----|:------------|\n    | `destinations` | `list[str]` | Cities or countries the user plans to visit. |\n    | `travel_period` | `str` | When the user plans to travel. |\n    | `dietary_requirements` | `list[str]` | Dietary requirements that affect recommendations. |\n    | `food_preferences` | `list[str]` | Cuisines, flavors, or dining preferences stated by the user. |\n\n1. Under **Sensitive-data exclusions**, enable **Semantic exclusions** and enter this exclusion prompt:\n\n    [code example]\n\n1. Select **Create**.\n1. Copy the Redis Agent Memory API key and store it securely.\n\n> [!WARNING]\n> Redis Cloud displays the Redis Agent Memory API key only once. If you lose it, [generate a new API key](https://redis.io/docs/latest/operate/iris/agent-memory/view-service#replace-service-api-key).\n\nThese settings keep the background stages short enough to observe during the quickstart. For screenshots and configuration details, see [create a Redis Agent Memory service](https://redis.io/docs/latest/operate/iris/agent-memory/create-service).\n\n> [!WARNING]\n> Sensitive-data exclusions guide the extraction model but do not guarantee exclusion. Sensitive session content still reaches the model provider. Exclusions do not apply when an application creates long-term memories directly."},{"id":"save-the-connection-values","title":"Save the connection values","role":"content","text":"1. Open the Redis Agent Memory service in the Redis Cloud console.\n1. On the **Configuration** tab, copy the **Endpoint** and **Store ID**.\n1. Export the API key in your shell:\n\n    [code example]\n\nKeep the API key out of source control, application logs, and other unsecured locations."},{"id":"install-the-sdk","title":"Install the SDK","role":"setup","text":"[code example]"},{"id":"create-the-client-and-check-the-service-health","title":"Create the client and check the service health","role":"content","text":"Create `quickstart.ts` with the following code. Replace `<ENDPOINT>` and `<STORE_ID>` with the values from Redis Cloud. The endpoint must include `https://`.\n\n[code example]\n\nRun the file:\n\n[code example]\n\nA healthy response confirms that the client can reach Redis Agent Memory and authenticate with the API key. The first store request validates the Store ID."},{"id":"1-build-conversation-context-with-session-memory","title":"1. Build conversation context with session memory","role":"content","text":"Session memory stores a conversation as an ordered sequence of events. Add the following code after the health check, inside `run`:\n\n[code example]\n\nRun the file again. The session response contains the stored message, its role, actor, and timestamps. An application can retrieve this session before the next agent turn and add the events to the model's context.\n\n> [!NOTE]\n> **What to expect:** The `events` array contains the travel message. Redis Agent Memory adds an `eventId` and `systemTimestamp`, showing that the application can recover the complete event later using only the session ID.\n\nAfter the event is stored, comment out the call to `addSessionEvent` before subsequent runs to avoid adding the same message again."},{"id":"2-recall-automatically-extracted-information","title":"2. Recall automatically extracted information","role":"content","text":"Redis Agent Memory processes session events in the background and creates long term memories for information that may be useful in later conversations. You configured the extraction cadence to one minute when you created the service. You do not need to call a memory creation method.\n\nWait at least one minute, then add this search after the session retrieval:\n\n[code example]\n\nRun the file. The `items` array should contain memories derived from the conversation, such as the vegetarian requirement or preference for spicy food. Extraction is asynchronous, so run the search again if the array is empty.\n\n> [!NOTE]\n> **What to expect:** Results similar to `User is a vegetarian` and `User prefers spicy food`. Your application did not create these memories directly. Redis Agent Memory derived them from the session event. The exact text and memory types can vary.\n\nThe extracted memory remains searchable after the session expires, subject to the long term memory TTL. You can change the extraction cadence and both TTLs in the [Redis Agent Memory service configuration](https://redis.io/docs/latest/operate/iris/agent-memory/create-service#memory-configuration)."},{"id":"3-keep-long-conversations-concise-with-automatic-summarization","title":"3. Keep long conversations concise with automatic summarization","role":"content","text":"Automatic summarization condenses older events and retains the most recent events in full. The retrieved session then contains a `summary` object and the recent `events` array, so the application can provide useful history without filling the model's context window with every original message.\n\nYou enabled automatic summarization when you created the service. When the session reaches six events, Redis Agent Memory summarizes the older events and retains the two most recent events in full."},{"id":"add-conversation-turns","title":"Add conversation turns","role":"content","text":"Add this code after the first session event to continue the conversation past the configured threshold:\n\n[code example]\n\nRun the code once, then comment out the loop to avoid adding the same turns again. Summarization runs in the background."},{"id":"retrieve-the-summarized-session","title":"Retrieve the summarized session","role":"content","text":"After a short wait, retrieve the session again:\n\n[code example]\n\nRun the retrieval again after a short wait if `summary` is not present. Compare `summary.text` with the recent events. The summary should preserve earlier trip decisions while recent turns remain available in full.\n\n> [!NOTE]\n> **What to expect:** A `summary` object that preserves details such as Tokyo, Kyoto, the travel dates, and food preferences. `summarizedUpToEventId` identifies the last event covered by the summary, while `events` contains the newer turns that remain in full. The exact summary text can vary.\n\nSee [automatic summarization configuration](https://redis.io/docs/latest/operate/iris/agent-memory/create-service#automatic-summarization) for details."},{"id":"4-extract-business-specific-data-with-a-custom-memory-type","title":"4. Extract business specific data with a custom memory type","role":"content","text":"Built in memories preserve generally useful information. Custom memory types let an application extract structured information for its business domain. You configured `trip_preference` when you created the service, so it processed the same travel planning event independently.\n\nSearch for the structured memory:\n\n[code example]\n\nThe result uses `trip_preference` as its `memoryType` and contains travel information extracted from the conversation. The exact text and returned fields depend on the conversation, extraction model, and client.\n\n> [!NOTE]\n> **What to expect:** A result with `memoryType` set to `trip_preference` that combines the destinations, travel period, and dietary preferences. This shows that the custom type processed the same conversation independently from the built-in memory types.\n\nSee [custom memory types](https://redis.io/docs/latest/operate/iris/agent-memory/create-service#custom-memory-types) for configuration requirements and limits."},{"id":"5-guide-extraction-away-from-sensitive-data","title":"5. Guide extraction away from sensitive data","role":"content","text":"The semantic exclusion prompt tells Redis Agent Memory which information should not be kept in long-term memory. Add an event containing a fictional booking code and information that is safe to retain:\n\n[code example]\n\nRun the code once, then comment out the call to `addSessionEvent`. Wait at least one minute and search for the safe hotel information:\n\n[code example]\n\nInspect the returned memories. They can retain the hotel name, but should not contain `DEMO-7QX9` because the exclusion prompt covers booking confirmation codes.\n\n> [!NOTE]\n> **What to expect:** A memory similar to `User booked Hotel Sakura in Tokyo` without the fictional confirmation code. If the code appears, refine the exclusion prompt and test again. Exclusions remain advisory.\n\n> [!WARNING]\n> Semantic exclusions are advisory and do not guarantee that sensitive information is excluded. Session content still reaches the extraction model provider. Do not use real sensitive data in this exercise. Exclusions do not apply to directly created long-term memories.\n\nSee [sensitive-data exclusions](https://redis.io/docs/latest/operate/iris/agent-memory/create-service#sensitive-data-exclusions) for configuration details."},{"id":"next-steps","title":"Next steps","role":"content","text":"* Review the [TypeScript SDK package and reference](https://www.npmjs.com/package/@redis-iris/agent-memory).\n* Generate a complete conversational agent with the [AI agent builder](https://redis.io/docs/latest/develop/ai/agent-builder). Select **Redis Iris Conversational Assistant** and **JavaScript**.\n* Try the [Python SDK quickstart](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/python-sdk-quickstart) or [REST API quickstart](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/rest-api-quickstart).\n* Learn when to [create long term memories directly](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/developer-guide#create-long-term-memories)."}],"examples":[{"id":"create-a-redis-agent-memory-service-ex0","language":"text","code":"Do not keep passwords, access tokens, recovery codes, payment card information, or booking confirmation codes in long-term memory.","section_id":"create-a-redis-agent-memory-service"},{"id":"save-the-connection-values-ex0","language":"sh","code":"export API_KEY='<API_KEY>'","section_id":"save-the-connection-values"},{"id":"install-the-sdk-ex0","language":"sh","code":"mkdir agent-memory-quickstart\ncd agent-memory-quickstart\nnpm init -y\nnpm install @redis-iris/agent-memory\nnpm install --save-dev tsx","section_id":"install-the-sdk"},{"id":"create-the-client-and-check-the-service-health-ex0","language":"typescript","code":"import { AgentMemory } from \"@redis-iris/agent-memory\";\n\nconst apiKey = process.env.API_KEY;\nif (!apiKey) {\n  throw new Error(\"Set the API_KEY environment variable.\");\n}\n\nconst serverURL = \"<ENDPOINT>\";\nconst storeId = \"<STORE_ID>\";\nconst sessionId = \"travel-planning-session\";\nconst userId = \"quickstart-user\";\n\nconst agentMemory = new AgentMemory({\n  serverURL,\n  storeId,\n  apiKey,\n});\n\nasync function run() {\n  const health = await agentMemory.health();\n  console.log(\"Service health:\");\n  console.dir(health, { depth: null });\n}\n\nrun().catch((error) => {\n  console.error(error);\n  process.exitCode = 1;\n});","section_id":"create-the-client-and-check-the-service-health"},{"id":"create-the-client-and-check-the-service-health-ex1","language":"sh","code":"npx tsx quickstart.ts","section_id":"create-the-client-and-check-the-service-health"},{"id":"1-build-conversation-context-with-session-memory-ex0","language":"typescript","code":"const event = await agentMemory.addSessionEvent({\n    sessionId,\n    actorId: userId,\n    role: \"USER\",\n    content: [{\n      text: \"I am visiting Tokyo and Kyoto next month. I am vegetarian and prefer spicy food.\",\n    }],\n    createdAt: new Date(),\n  });\n  console.log(\"Created event:\");\n  console.dir(event, { depth: null });\n\n  const session = await agentMemory.getSessionMemory(sessionId);\n  console.log(\"Session memory:\");\n  console.dir(session, { depth: null });","section_id":"1-build-conversation-context-with-session-memory"},{"id":"2-recall-automatically-extracted-information-ex0","language":"typescript","code":"const results = await agentMemory.searchLongTermMemory({\n    text: \"What dietary requirements and food preferences does the user have?\",\n    filter: {\n      ownerId: {\n        eq: userId,\n      },\n    },\n    limit: 5,\n  });\n  console.log(\"Automatically extracted memories:\");\n  console.dir(results, { depth: null });","section_id":"2-recall-automatically-extracted-information"},{"id":"add-conversation-turns-ex0","language":"typescript","code":"const turns = [\n    { role: \"ASSISTANT\", actorId: \"travel-agent\", text: \"What dates are you traveling?\" },\n    { role: \"USER\", actorId: userId, text: \"I arrive on October 10 and leave on October 18.\" },\n    { role: \"ASSISTANT\", actorId: \"travel-agent\", text: \"Would you like formal or casual restaurants?\" },\n    { role: \"USER\", actorId: userId, text: \"Mostly casual places near public transit.\" },\n    { role: \"ASSISTANT\", actorId: \"travel-agent\", text: \"Do you have a preferred budget?\" },\n    { role: \"USER\", actorId: userId, text: \"About 40 euros per person.\" },\n  ] as const;\n\n  for (const turn of turns) {\n    await agentMemory.addSessionEvent({\n      sessionId,\n      actorId: turn.actorId,\n      role: turn.role,\n      content: [{ text: turn.text }],\n      createdAt: new Date(),\n    });\n  }","section_id":"add-conversation-turns"},{"id":"retrieve-the-summarized-session-ex0","language":"typescript","code":"const compactedSession = await agentMemory.getSessionMemory(sessionId);\n  console.log(\"Compacted session memory:\");\n  console.dir(compactedSession, { depth: null });","section_id":"retrieve-the-summarized-session"},{"id":"4-extract-business-specific-data-with-a-custom-memory-type-ex0","language":"typescript","code":"const customResults = await agentMemory.searchLongTermMemory({\n    text: \"What are the requirements for the user's trip?\",\n    filter: {\n      ownerId: { eq: userId },\n      memoryType: { eq: \"trip_preference\" },\n    },\n    limit: 5,\n  });\n  console.log(\"Trip preference memories:\");\n  console.dir(customResults, { depth: null });","section_id":"4-extract-business-specific-data-with-a-custom-memory-type"},{"id":"5-guide-extraction-away-from-sensitive-data-ex0","language":"typescript","code":"const sensitiveEvent = await agentMemory.addSessionEvent({\n    sessionId,\n    actorId: userId,\n    role: \"USER\",\n    content: [{\n      text: \"I booked Hotel Sakura in Tokyo. For this example, the fictional booking confirmation code is DEMO-7QX9.\",\n    }],\n    createdAt: new Date(),\n  });\n  console.log(\"Event with excluded information:\");\n  console.dir(sensitiveEvent, { depth: null });","section_id":"5-guide-extraction-away-from-sensitive-data"},{"id":"5-guide-extraction-away-from-sensitive-data-ex1","language":"typescript","code":"const exclusionResults = await agentMemory.searchLongTermMemory({\n    text: \"Where is the user staying in Tokyo?\",\n    filter: {\n      ownerId: { eq: userId },\n    },\n    limit: 5,\n  });\n  console.log(\"Memories after semantic exclusion:\");\n  console.dir(exclusionResults, { depth: null });","section_id":"5-guide-extraction-away-from-sensitive-data"}]}
{"schema_version":2,"id":"develop/ai/context-engine/concepts","title":"How Redis Iris works","url":"https://redis.io/docs/latest/develop/ai/context-engine/concepts/","summary":"What happens when an agent asks Redis Iris for context, and why that's faster than assembling it yourself.","tags":["docs","develop","ai"],"last_updated":"2026-10-15T14:32:20-07:00","children":[{"id":"develop/ai/context-engine/concepts/request-flow","summary":"Trace how a single agent request draws on Redis Iris's caching, memory, and retrieval capabilities before a model call.","title":"How a request flows through Redis Iris","url":"https://redis.io/docs/latest/develop/ai/context-engine/concepts/request-flow/"},{"id":"develop/ai/context-engine/concepts/interactive-demo","summary":"Step through one agent request across Data Integration, LangCache, Agent Memory, and Context Retriever, then take a service away to see what the agent loses.","title":"Redis Iris interactive demo","url":"https://redis.io/docs/latest/develop/ai/context-engine/concepts/interactive-demo/"}],"page_type":"content","content_hash":"ba3561dbfb821f9f3f127b7bd41a22661610fbc0d51b62ee3d5dcc714b4cefed","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Iris sits between your agent and your data. It's one system. Your agent asks for context. Iris returns it through the fastest available path: a cached answer, a remembered fact, or a live lookup against your business data. That data can live in Redis or in another database you connect. You don't wire together separate lookups yourself. Iris decides which path answers the request.\n\nUse Redis Iris when you want agents to respond with cached answers instead of repeat model calls, recall what they've learned across turns and sessions, and act on live business data. Iris builds and maintains that infrastructure for you.\n\n![An agent connects to Redis Iris, which stores memories, caches answers, and retrieves context, all backed by Redis or a connected database.](https://redis.io/docs/latest/images/ai/context-engine/iris-mental-model.svg)"},{"id":"the-mental-model","title":"The mental model","role":"content","text":"| Without a context layer | With Redis Iris |\n|:---|:---|\n| Replay the full conversation history every turn | Recall only what's relevant, summarized or extracted once |\n| Call the model even when you already know the answer | Return a cached answer when a similar request already ran |\n| Give the agent a live database connection or let it generate its own queries | Give it a fixed set of governed tools that fetch exactly what's needed |\n| Build separate integration code for every data source | Reach Redis or any connected database through the same interface |"},{"id":"what-happens-on-a-request","title":"What happens on a request","role":"content","text":"Iris does up to three things before your agent's request reaches the model, in whatever combination the request needs:\n\n1. **Recall.** Iris checks whether it already has a cached answer for a similar request, or relevant memory from earlier in the conversation or a past session. If so, it returns that instead of spending a model call.\n2. **Retrieve.** If the request depends on current business data, Iris fetches exactly what's needed through a governed tool call, not a raw database query the agent constructs itself.\n3. **Record.** After the model responds, Iris writes back what's worth remembering. The next request, in this conversation or a future one, can recall it.\n\nNot every request needs all three. A question the cache already answered skips straight to a response. A question about live inventory or account data skips the cache and goes straight to retrieval."},{"id":"two-phases-serving-and-recording","title":"Two phases: serving and recording","role":"content","text":"Most requests touch Redis Iris at two points:\n\n1. **Before the model call**, check whether Iris already has what's needed: a cached answer, relevant memory, or business data a tool can fetch.\n2. **After the model responds**, let Iris record what's worth keeping. The next request doesn't start from nothing."},{"id":"1-serving-the-request-reading","title":"1. Serving the request (reading)","role":"content","text":"Iris draws on whichever of its capabilities the request needs, before the model call:\n\n- **Cache check.** Iris compares the incoming prompt against previously cached responses. A close-enough match returns immediately, skipping the model call.\n- **Memory recall.** Iris searches session and long-term memory for what's relevant to this conversation or user. You don't need to replay the full transcript to the model.\n- **Governed retrieval.** If the request depends on current business data, Iris calls a fixed tool generated from your data model instead of running a raw query."},{"id":"2-recording-the-interaction-writing","title":"2. Recording the interaction (writing)","role":"content","text":"Iris writes back what's worth keeping after the model responds. Your application doesn't do this explicitly:\n\n- **Session write.** Iris stores the conversation's events as they happen. The next turn has recent history available.\n- **Background extraction.** Iris pulls durable facts and preferences out of the conversation and stores them separately, with embeddings for later semantic search.\n- **Cache write.** Iris stores the new prompt-and-response pair as a candidate to serve the next similar request without a model call.\n\nThis write path runs asynchronously. It doesn't slow down the response your application is waiting for."},{"id":"where-context-lives","title":"Where context lives","role":"content","text":"Redis Iris splits that context across stores, each built for a different lookup pattern:\n\n| Store | Holds | Purpose |\n|:---|:---|:---|\n| Cache index | Prompt embeddings and their cached responses | Sub-second similarity lookups before a model call |\n| Memory store | Session events and extracted long-term facts, with embeddings | Recall across turns and sessions |\n| Your business data | Whatever you already store in Redis or a connected database | Queried through governed tools, not a raw connection |\n\nRun all three fully managed on Redis Cloud, or self-managed on your own infrastructure. Either way, you configure what to keep and for how long, not how the underlying store works."},{"id":"build-against-this-flow","title":"Build against this flow","role":"content","text":"- Let Iris decide what's cached and what's fresh. Don't build a separate caching layer on top of the model call.\n- Write session events as they happen. Let background extraction decide what's durable, rather than deciding yourself on every turn.\n- Model your business data as entities and relationships once. Let Context Retriever generate the tools your agent calls, instead of writing a tool per query.\n- Scope every write and lookup by user, session, or namespace. This keeps agents and users from colliding."},{"id":"next-steps","title":"Next steps","role":"content","text":"- [How a request flows through Redis Iris](https://redis.io/docs/latest/develop/ai/context-engine/concepts/request-flow)\n- [Agent Memory overview](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/overview)\n- [Context Retriever concepts](https://redis.io/docs/latest/develop/ai/context-engine/context-retriever/concepts)\n- Interactive demos for [Agent Memory](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/interactive-demo) and [Context Retriever](https://redis.io/docs/latest/develop/ai/context-engine/context-retriever/interactive-demo) that run in your browser"}],"examples":[]}
{"schema_version":2,"id":"develop/ai/context-engine/concepts/interactive-demo","title":"Redis Iris interactive demo","url":"https://redis.io/docs/latest/develop/ai/context-engine/concepts/interactive-demo/","summary":"Step through one agent request across Data Integration, LangCache, Agent Memory, and Context Retriever, then take a service away to see what the agent loses.","aliases":["/develop/ai/context-engine/interactive-demo/"],"tags":["docs","develop","ai"],"last_updated":"2026-10-15T14:32:20-07:00","page_type":"content","content_hash":"0a77ee909869270d1621c296df821d42a0dbf192cadd7811b1a34a00093ef141","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Iris gives an AI agent governed access to live business data, memory, and cached answers. This tour follows one customer request through all four services, one step at a time. It starts where business data enters Iris, with Data Integration, and then follows the request through the other three services, in the order described in [How Redis Iris works](https://redis.io/docs/latest/develop/ai/context-engine/concepts). The last step takes one service away to show what the agent loses without it.\n\nThe tour runs in your browser and doesn't connect to any service. Each step links to that service's own interactive demo, which covers it in depth with the same customer and data. The agent's reasoning, the reply, and the timings are illustrative."},{"id":"what-each-service-adds","title":"What each service adds","role":"content","text":"| Service | What it does in this request | Without it |\n|:--|:--|:--|\n| [Data Integration](https://redis.io/docs/latest/develop/ai/context-engine/data-integration) | Syncs a change from PostgreSQL to Redis seconds after it happens | The agent reads last night's data and makes a promise it can't keep |\n| [Context Retriever](https://redis.io/docs/latest/develop/ai/context-engine/context-retriever) | Looks up delivery times, alternatives, and past orders through governed tools | The agent can't check whether the food will arrive in time |\n| [Agent Memory](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory) | Recalls last week's order and the customer's peanut allergy | The agent can find the order, but not the allergy |\n| [LangCache](https://redis.io/docs/latest/develop/ai/context-engine/langcache) | Answers the general delivery fee question from the cache | The model answers it, which takes longer and costs output tokens |"},{"id":"next-steps","title":"Next steps","role":"content","text":"- See [how a request flows through Redis Iris](https://redis.io/docs/latest/develop/ai/context-engine/concepts/request-flow), capability by capability.\n- Get started with [Redis Iris on Redis Cloud](https://redis.io/docs/latest/operate/iris)."}],"examples":[]}
{"schema_version":2,"id":"develop/ai/context-engine/concepts/request-flow","title":"How a request flows through Redis Iris","url":"https://redis.io/docs/latest/develop/ai/context-engine/concepts/request-flow/","summary":"Trace how a single agent request draws on Redis Iris's caching, memory, and retrieval capabilities before a model call.","tags":["docs","develop","ai"],"last_updated":"2026-10-15T14:32:20-07:00","page_type":"content","content_hash":"fb5118f32a69edd901c203e56d5841a6c466b472a8eeccff6f6068f30d8d4179","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[How Redis Iris works](https://redis.io/docs/latest/develop/ai/context-engine/concepts) covers the mental model. This page traces the same idea through an actual request, capability by capability.\n\nEvery capability in Iris manages the same limited resource: the model's context window. Semantic caching skips a model call when a similar one already ran. Agent Memory decides what's worth keeping from a conversation. Context Retriever returns exactly the data a tool call needs, instead of a raw query result. Treat \"what goes into the next model call\" as a budget. Manage it actively. It doesn't take care of itself once a request reaches Iris.\n\nThese capabilities aren't a fixed sequence. A request can draw on any combination of them before a model call happens: semantic caching for responses to similar prompts, [Agent Memory](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory) for session and long-term recall, and [Context Retriever](https://redis.io/docs/latest/develop/ai/context-engine/context-retriever) for governed access to business data. [Data Integration](https://redis.io/docs/latest/develop/ai/context-engine/data-integration) runs in the background, keeping that business data fresh for Context Retriever to query.\n\nSelect a node in the diagram for a description of what that capability does, with a link to its docs. Select a scenario button to trace the path a request takes.\n\n[code example]"},{"id":"two-things-iris-changes","title":"Two things Iris changes","role":"content","text":"**Isolation is about meaning, not just timing.** Multiple agents or users often share Iris. The conflict that matters usually isn't two writes touching the same record at the same instant. It's two agents writing similar-but-different memories about the same user, or two near-duplicate cache entries competing to answer the same class of question. Both writes can be individually valid and still disagree with each other. Scoping every write and lookup by user, session, and data type prevents this. Treat those scoping identifiers as carefully as you'd treat any other access boundary in your application.\n\n**Trust follows the agent, not the data source.** Application code is trusted by default, and the database enforces permissions at the boundary between code and data. An agent complicates that. Its next action can be shaped by content it's processing, such as a retrieved document or a tool's own output. You don't fully control that content. Assume anything an agent reads can influence what it does next. Design the tools and memory it can reach with that in mind. Don't trust content just because a trusted code path read it."},{"id":"next-steps","title":"Next steps","role":"content","text":"- [Agent Memory overview](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/overview)\n- [Context Retriever concepts](https://redis.io/docs/latest/develop/ai/context-engine/context-retriever/concepts)\n- Interactive demos for [Agent Memory](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/interactive-demo) and [Context Retriever](https://redis.io/docs/latest/develop/ai/context-engine/context-retriever/interactive-demo) that run in your browser"}],"examples":[{"id":"overview-ex0","language":"context-map {id=\"iris-request-flow\" scope=\"context-engine\"}","code":"id: iris-request-flow\nscope: context-engine\nnodes:\n    agent:\n        label: \"Agent\"\n        type: process\n        col: 0\n        row: 1\n        description: |\n            The calling application or AI agent that sends a prompt.\n        docsUrl: \"/develop/ai/agent-builder\"\n    langcache:\n        label: \"Cache hit?\"\n        type: decision\n        col: 1\n        row: 0\n        description: |\n            Cache: checks whether a similar prompt is already cached before calling the model.\n        links:\n            demo:\n                label: \"Interactive demo\"\n                url: \"/develop/ai/context-engine/langcache/interactive-demo\"\n        docsUrl: \"/develop/ai/context-engine/langcache\"\n    agentMemory:\n        label: \"Agent Memory\"\n        type: process\n        col: 1\n        row: 1\n        description: |\n            Agent Memory: session and long-term recall. Recalls session history and long-term facts about the user or task.\n        links:\n            demo:\n                label: \"Interactive demo\"\n                url: \"/develop/ai/context-engine/agent-memory/interactive-demo\"\n        docsUrl: \"/develop/ai/context-engine/agent-memory\"\n    contextRetriever:\n        label: \"Context Retriever\"\n        type: process\n        col: 1\n        row: 2\n        description: |\n            Context Retriever: governed tool calls. Calls governed, schema-first tools to fetch business data the agent needs.\n        links:\n            demo:\n                label: \"Interactive demo\"\n                url: \"/develop/ai/context-engine/context-retriever/interactive-demo\"\n        docsUrl: \"/develop/ai/context-engine/context-retriever\"\n    dataIntegration:\n        label: \"Data Integration\"\n        type: process\n        col: 0\n        row: 3\n        description: |\n            Data Integration: keeps business data fresh. Streams changes from source databases into the data layer Context Retriever queries.\n        docsUrl: \"/develop/ai/context-engine/data-integration\"\n    cachedResponse:\n        label: \"Cached response\"\n        type: terminal\n        col: 2\n        row: 0\n        description: |\n            Return cached response: on a cache hit, Cache returns the stored response directly, skipping the model call.\n    modelCall:\n        label: \"Model call\"\n        type: process\n        col: 2\n        row: 1\n        description: |\n            The model generates a response using the retrieved context.\n        links:\n            googleAdk:\n                label: \"Google ADK\"\n                url: \"/integrate/google-adk\"\n            bedrock:\n                label: \"Amazon Bedrock\"\n                url: \"/integrate/amazon-bedrock\"\n            langchain:\n                label: \"LangChain\"\n                url: \"/integrate/langchain-redis\"\n            ecosystem:\n                label: \"More integrations\"\n                url: \"/develop/ai/ecosystem-integrations\"\n    response:\n        label: \"Response\"\n        type: terminal\n        col: 3\n        row: 1\n        description: |\n            The final response returned to the caller.\nedges:\n    e1:\n        from: agent\n        to: langcache\n        kind: normal\n        path: cacheHit\n    e2:\n        from: langcache\n        to: cachedResponse\n        kind: branch\n        path: cacheHit\n    e3:\n        from: agent\n        to: agentMemory\n        kind: normal\n        path: memory\n    e4:\n        from: agentMemory\n        to: modelCall\n        kind: normal\n        path: memory\n    e5:\n        from: agent\n        to: contextRetriever\n        kind: normal\n        label: \"MCP\"\n        path: context\n    e6:\n        from: dataIntegration\n        to: contextRetriever\n        kind: normal\n        path: context\n    e7:\n        from: contextRetriever\n        to: modelCall\n        kind: normal\n        path: context\n    e8:\n        from: modelCall\n        to: response\n        kind: normal\n        path: \"memory,context\"\n    e9:\n        from: response\n        to: agentMemory\n        kind: loopback\n        route: top\n        label: \"App writes session event\"\n        path: \"memory,context\"\npaths:\n    cacheHit:\n        label: \"Cache hit: fastest\"\n        description: |\n            Cache finds a similar prompt already cached and returns it directly. No model call, so this path is the fastest and adds no LLM cost.\n    memory:\n        label: \"Needs memory: recalls session or long-term info\"\n        description: |\n            For a question that depends on earlier turns or what's known about the user, Agent Memory supplies that recall before the model call.\n    context:\n        label: \"Needs business data: retrieves via Context Retriever\"\n        description: |\n            For a question that depends on live business data, Context Retriever calls governed tools to fetch it before the model call. Data Integration keeps that data fresh.","section_id":"overview"}]}
{"schema_version":2,"id":"develop/ai/context-engine/context-retriever/concepts","title":"Context Retriever concepts","url":"https://redis.io/docs/latest/develop/ai/context-engine/context-retriever/concepts/","summary":"Learn how Context Retriever's governed tool-calling differs from giving application code direct database access.","tags":["docs","develop","ai"],"last_updated":"2026-10-01T12:49:59-07:00","page_type":"content","content_hash":"f8dd2f7c79db3d83db04f4cf1e50e86e66156d64273ebf100fff869e3922a1da","sections":[{"id":"a-layer-on-top-of-your-database-not-a-replacement-for-it","title":"A layer on top of your database, not a replacement for it","role":"overview","text":"Context Retriever gives an agent a fixed, predefined set of callable tools to use instead of direct query access. Your database, its schema, and its own access controls stay unchanged. Context Retriever sits between the agent and that database, as an added layer.\n\n![An agent calls Context Retriever through MCP tools, and Context Retriever queries the database.](https://redis.io/docs/latest/images/ai/context-engine/context-retriever-layer.svg)"},{"id":"direct-data-access-vs-governed-tool-calling","title":"Direct data access vs. governed tool-calling","role":"content","text":"If you've built backend services before, you're used to reasoning about data access as a permissions problem: which role can query which tables. Context Retriever reframes it as an API-design problem: which tools exist, and what does each one return.\n\n| | Direct database access | Context Retriever |\n|:---|:---|:---|\n| What the agent gets | A query interface (SQL, an ORM, a generic API) | A fixed set of tools generated from your data model |\n| How you scope access | Row/column permissions, roles | Access tags on the agent's key, filtering which tools and data it can reach |\n| What a bad request looks like | A malformed or overly broad query | A call to a tool that isn't defined; Context Retriever doesn't guess a path around it |\n| Where you invest | Query optimization, permission grants | Modeling entities and relationships once, reused by every agent |"},{"id":"why-agents-cant-access-raw-queries","title":"Why agents can't access raw queries","role":"content","text":"An agent's input often includes content it didn't choose to trust. An agent that summarizes a document, reads a support ticket, or follows a web page can act on text embedded in that content (prompt injection). If that agent also holds a database connection, or can generate arbitrary SQL, injected content can turn into an arbitrary query. A fixed tool surface bounds the blast radius. Context Retriever ensures that the worst an agent can do is call a tool it was already allowed to call, with parameters that tool already accepts."},{"id":"key-terms","title":"Key terms","role":"content","text":"- **Entity**: A business object in your data. You define it with a key template, such as `customer:{id}`, and a set of fields.\n- **Field**: An attribute of an entity. One field is the primary key. Others can be indexed for search and filtering, or linked to another entity as a relationship.\n- **Index type**: How a field can be searched. Options are text, tag (exact match), numeric, and vector (semantic similarity).\n- **Tool**: A callable operation generated from your entity model, such as a search or get-by-ID lookup for an entity.\n- **Agent key**: Scopes which tools and data an agent can reach, through access tags."},{"id":"how-it-works","title":"How it works","role":"content","text":"1. **Model your entities.** Define your entities, their key templates, fields, primary keys, and relationships. Use the Redis Cloud console, a model file, or the Python client.\n2. **Context Retriever generates tools.** It creates a fixed set of tools from your model, such as a search and a get-by-ID tool for each entity. These tools are exposed over MCP.\n3. **An agent calls a tool.** The agent uses an agent key scoped by access tags. It calls a generated tool instead of running a query, and gets back structured data.\n\nChanging your entity model regenerates the tool set. Add a field or relationship, and the tools available to your agents change with it."},{"id":"modeling-is-an-upfront-investment-but-its-reusable","title":"Modeling is an upfront investment, but it's reusable","role":"content","text":"Modeling your entities takes more upfront work than giving an agent a database connection. You define your objects, their key templates, and their relationships once. You don't rely on the agent to work them out from a raw query.\n\nThat upfront work pays off. Every agent that uses the surface reuses the same tools, instead of rebuilding them per agent or per query.\n\nYou can define fields manually, or let Context Retriever auto-detect them by scanning a sample of your existing keys. Auto-detection is faster, but it's a starting point, not a guarantee. The model that infers fields and relationships can get them wrong. Review what it generates before you rely on it. Manual modeling takes longer, but it gives you exact control over which tools get generated and what an agent can reach."},{"id":"faq","title":"FAQ","role":"content","text":"**Why can't my agent run SQL directly?**\n\"Run SQL\" means the set of things an agent can do is as large as your schema. Untrusted content the agent is processing can also influence its next action. A fixed tool surface, call this tool with these parameters, bounds that risk to what the tool itself allows.\n\n**How is Context Retriever different from a regular REST API?**\nContext Retriever uses the same idea: a fixed, callable surface instead of a query language. The tools are generated from your entity model, instead of hand-written per endpoint. Access is scoped per agent key through tags, rather than a single API-wide permission model.\n\n**What happens when an agent needs a query I haven't defined a tool for?**\nThe agent can't get that data. Context Retriever doesn't fall back to an open query path by design. Extend your entity model and regenerate the tool set instead.\n\nSee the [AI agent context engine FAQ](https://redis.io/blog/faq-real-time-context-engine-agent-memory-and-retrieval/) for how this compares to text-to-SQL and OpenAPI-to-MCP approaches."},{"id":"next-steps","title":"Next steps","role":"content","text":"- Try the [Context Retriever interactive demo](https://redis.io/docs/latest/develop/ai/context-engine/context-retriever/interactive-demo) to see a data model become the tools an agent calls.\n- [Create a Context Retriever service](https://redis.io/docs/latest/operate/iris/context-retriever/create-service) on Redis Cloud.\n- [Install Context Retriever](https://redis.io/docs/latest/operate/iris/context-retriever/self-managed) on your own Kubernetes infrastructure.\n- Follow the [quickstart](https://redis.io/docs/latest/develop/ai/context-engine/context-retriever/quickstart) to model entities, generate tools, and call one with the `ctxctl` CLI.\n- [Manage agent keys and access tags](https://redis.io/docs/latest/operate/iris/context-retriever/view-admin-keys) to control what each agent can reach."}],"examples":[]}
{"schema_version":2,"id":"develop/ai/context-engine/context-retriever","title":"Redis Context Retriever","url":"https://redis.io/docs/latest/develop/ai/context-engine/context-retriever/","summary":"Expose tools to Agents to query your Redis databases.","tags":["docs","develop","ai"],"last_updated":"2026-10-05T20:57:56-07:00","children":[{"id":"develop/ai/context-engine/context-retriever/concepts","summary":"Learn how Context Retriever's governed tool-calling differs from giving application code direct database access.","title":"Context Retriever concepts","url":"https://redis.io/docs/latest/develop/ai/context-engine/context-retriever/concepts/"},{"id":"develop/ai/context-engine/context-retriever/quickstart","summary":"Model a data source, generate MCP tools, and call them from an agent using the ctxctl CLI.","title":"Redis Context Retriever quickstart","url":"https://redis.io/docs/latest/develop/ai/context-engine/context-retriever/quickstart/"},{"id":"develop/ai/context-engine/context-retriever/interactive-demo","summary":"See how Context Retriever turns the keys in a Redis database into MCP tools that an agent calls to answer questions.","title":"Context Retriever interactive demo","url":"https://redis.io/docs/latest/develop/ai/context-engine/context-retriever/interactive-demo/"}],"page_type":"content","content_hash":"1c7f0e6dba8aa5abcdbf2a8878628c59c51725fbdd80e40ed7c939c87703e2cf","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"Give your agents structured, governed access to business data, without building custom tools for every project.\n\nContext Retriever lets you define your data model once. It automatically generates the retrieval tools agents call at runtime, so agents always work with accurate, live data through a controlled interface rather than guessing at SQL or calling databases directly.\n\n<div class=\"grid grid-cols-1 md:grid-cols-4 gap-6 my-8\">\n  \n  \n  \n  \n</div>"},{"id":"what-is-context-retriever","title":"What is Context Retriever?","role":"content","text":"Redis Context Retriever is a schema-first context layer for AI agents that:\n\n- **Defines business context once**: Model your entities, fields, and relationships in one place, reused across all agents\n- **Auto-generates retrieval tools**: Tools are created from your data model, not hand-coded per agent\n- **Keeps agents out of your database**: Agents call generated tools; the system handles data access safely\n- **Governs access by design**: Each agent key has access tags that automatically filter what data it can see\n- **Exposes tools via MCP**: Agents call tools through a standard Model Context Protocol (MCP) interface at runtime"},{"id":"why-use-context-retriever","title":"Why use Context Retriever?","role":"content","text":"<div class=\"grid grid-cols-1 md:grid-cols-2 gap-6 my-6\">\n  <div class=\"p-5 border border-redis-pen-300 rounded-lg\">\n    <h3 class=\"text-redis-ink-900 font-semibold mb-3\">For AI applications</h3>\n    <ul class=\"space-y-1 text-redis-pen-600\">\n      <li>Agents reliably follow defined data paths instead of guessing at SQL</li>\n      <li>Live, structured context from your business data at every agent step</li>\n      <li>No tool zoo sprawl: one model definition, consistent tool surface</li>\n      <li>Access control built in: agents only see what they're allowed to see</li>\n    </ul>\n  </div>\n  <div class=\"p-5 border border-redis-pen-300 rounded-lg\">\n    <h3 class=\"text-redis-ink-900 font-semibold mb-3\">For developers</h3>\n    <ul class=\"space-y-1 text-redis-pen-600\">\n      <li>Python client and <code>ctxctl</code> CLI for modeling and deploying</li>\n      <li>UI-based setup available in Redis Cloud console</li>\n      <li>No per-agent tool engineering: the platform handles tool generation</li>\n      <li>Available fully managed on Redis Cloud or self-managed on your own infrastructure</li>\n    </ul>\n  </div>\n</div>"},{"id":"quick-example","title":"Quick example","role":"content","text":"Install the Python client, which also includes the `ctxctl` CLI:\n\n[code example]\n\nUse the `ctxctl` CLI, the Python client, or the Redis Cloud UI to model your entities and relationships. Context Retriever uses that model to automatically generate retrieval tools that agents call at runtime through its MCP interface. Agents never access your database directly.\n\nSee the [Redis Cloud setup guide](https://redis.io/docs/latest/operate/iris/context-retriever/create-service) to create your first Context Retriever service, or follow the [quickstart](https://redis.io/docs/latest/develop/ai/context-engine/context-retriever/quickstart) for a full CLI walkthrough: model entities, generate tools, and call one as an agent would.\n\nTo see how a data model becomes tools before you create a service, try the [interactive demo](https://redis.io/docs/latest/develop/ai/context-engine/context-retriever/interactive-demo).\n\nRedis Context Retriever helps teams expose operational context to AI agents through schema-first retrieval. It models the entities, fields, keys, and relationships that matter to an agent workflow, then presents that context through a governed tool surface the agent can call at runtime. Context Retriever helps an AI Agent understand what business objects exist, how they connect, and which paths are safe to use."},{"id":"overview","title":"Overview","role":"overview","text":"Production agents fail not because the model is wrong, but because the context layer breaks. Enterprise data can be fragmented across multiple different databases, and can be disorganized. Teams try to patch this with text-to-SQL, OpenAPI-to-MCP wrappers, or hand-built tools, which works for demos but creates tool zoo sprawl, SQL risk, and agents that can't reliably choose the right path in production. Redis Context Retriever gives teams a governed, schema-first surface agents can traverse safely.\n\nWhen you set up Redis Context Retriever, you model the objects that matter to your agent workflow and connect the relationships between them. You can do this either through the UI, using the [Context Surfaces Python Client](https://pypi.org/project/redis-context-retriever/), or the `ctxctl` CLI (available when you install the python client). Context Retriever will use those relationships to automatically create and deploy retrieval tools from your entity model.  \n\nWhen an agent needs context during execution, it calls the MCP tools Context Retriever exposes. Instead of guessing which tool to use, or generating SQL, the agent follows the defined entity paths and gets back structured, live, operational context."},{"id":"get-started-with-redis-context-retriever","title":"Get started with Redis Context Retriever","role":"content","text":"Get started with Redis Context Retriever on Redis Cloud or join the private preview for Redis Software.\n\n**Redis Cloud:**\n\nTo set up a Redis Context Retriever on Redis Cloud, you need a database on Redis Cloud that already has relevant data. If you use a relational database, use [Redis Data Integration (RDI)](https://redis.io/docs/latest/operate/rc/rdi) to ingest data into a Redis Cloud database.\n\nWhen you have a database, [Create a context retriever service](https://redis.io/docs/latest/operate/iris/context-retriever/create-service) for your database on Redis Cloud.\n\nAfter you set up Context Retriever, you can [view your service](https://redis.io/docs/latest/operate/iris/context-retriever/view-service). See the [Context Surfaces Python Client](https://pypi.org/project/redis-context-retriever/) for more information on how to call your tools.\n\n**Redis Software (private preview):**\n\nRedis Context Retriever is available for self-managed deployment on Kubernetes as a private preview. See [Install Context Retriever](https://redis.io/docs/latest/operate/iris/context-retriever/self-managed).\n\nYou need a license key to deploy: [contact Redis](https://redis.io/contact/) to request access."}],"examples":[{"id":"quick-example-ex0","language":"bash","code":"pip install redis-context-retriever","section_id":"quick-example"}]}
{"schema_version":2,"id":"develop/ai/context-engine/context-retriever/interactive-demo","title":"Context Retriever interactive demo","url":"https://redis.io/docs/latest/develop/ai/context-engine/context-retriever/interactive-demo/","summary":"See how Context Retriever turns the keys in a Redis database into MCP tools that an agent calls to answer questions.","tags":["docs","develop","ai"],"last_updated":"2026-09-28T14:50:18+02:00","page_type":"content","content_hash":"851a3a74a92d4670224e27c057ab19c13cdfc4476f62429fa92f760f5e58af5b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Context Retriever reads a data model that you define once, and generates the tools an agent uses to query your data. The agent calls those tools over MCP (Model Context Protocol) and never connects to Redis itself.\n\nThis demo runs in your browser with sample data from a food delivery app: customers, restaurants, and orders. It doesn't connect to a Context Retriever service, but the tool names, arguments, and responses follow the same shape a real service returns."},{"id":"what-the-demo-shows","title":"What the demo shows","role":"content","text":"1. **Keys in Redis.** Each business object is a JSON document, and its key name starts with the object type, such as `restaurant:r201`.\n2. **Data model to tools.** A key template such as `restaurant:{id}` tells Context Retriever which keys belong to an entity. The fields you index decide which tools it generates and which arguments those tools accept.\n3. **Ask over MCP.** The agent lists the tools, picks one or more for each question, and combines the results into an answer.\n\nTry removing the index from a field in step 2, then ask a question that needs it. The server rejects the call, because the tool no longer accepts that field."},{"id":"next-steps","title":"Next steps","role":"content","text":"- [Create a Context Retriever service](https://redis.io/docs/latest/operate/iris/context-retriever/create-service) on Redis Cloud.\n- Read the [Context Retriever overview](https://redis.io/docs/latest/develop/ai/context-engine/context-retriever)."}],"examples":[]}
{"schema_version":2,"id":"develop/ai/context-engine/context-retriever/quickstart","title":"Redis Context Retriever quickstart","url":"https://redis.io/docs/latest/develop/ai/context-engine/context-retriever/quickstart/","summary":"Model a data source, generate MCP tools, and call them from an agent using the ctxctl CLI.","tags":["docs","develop","ai"],"last_updated":"2026-09-25T11:04:30-07:00","page_type":"content","content_hash":"9415a0b9c211017f08631462134f8a436a72833fa3e13cef2980b67d4c324d7a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Use this quickstart to model a Redis data source as a context surface, generate the retrieval tools that Context Retriever exposes, and call one of those tools as an agent would.\n\nThis quickstart walks you through:\n\n1. [Install the Python client](#install-the-python-client)\n1. [Sign in and create an admin key](#sign-in-and-create-an-admin-key)\n1. [Define your data model](#define-your-data-model)\n1. [Load sample data](#load-sample-data)\n1. [Create a surface](#create-a-surface)\n1. [Create an agent key](#create-an-agent-key)\n1. [List the generated tools](#list-the-generated-tools)\n1. [Call a tool](#call-a-tool)\n1. [Clean up](#clean-up)\n\nThis quickstart uses Redis Cloud. If you're running Context Retriever self-managed, see [Install Context Retriever](https://redis.io/docs/latest/operate/iris/context-retriever/self-managed) instead; every step after sign-in and admin-key creation is the same either way."},{"id":"before-you-begin","title":"Before you begin","role":"content","text":"To complete this quickstart, you need:\n\n* A Redis Cloud account.\n* A Redis Cloud database. If you don't have one, see [Create a database](https://redis.io/docs/latest/operate/rc/databases/create-database).\n* Python 3.11 or later and `pip`.\n* `redis-cli`, to load sample data. See [Install redis-cli](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/install-redis-cli)."},{"id":"install-the-python-client","title":"Install the Python client","role":"setup","text":"The Python client includes the `ctxctl` CLI, which you use to model data, manage keys, and call the tools Context Retriever generates.\n\n[code example]"},{"id":"sign-in-and-create-an-admin-key","title":"Sign in and create an admin key","role":"content","text":"1. Start a session against your Redis Cloud account:\n\n   [code example]\n\n1. Create an admin key. An admin key authorizes operations such as creating surfaces and agent keys.\n\n   [code example]\n\n1. Save the returned key. Export it so later commands can use it:\n\n   [code example]\n\nThis quickstart uses a Redis Cloud account for sign-in and admin-key creation. If you're running Context Retriever self-managed instead, see [Install Context Retriever](https://redis.io/docs/latest/operate/iris/context-retriever/self-managed) to bootstrap your first admin key. Every other step in this quickstart applies to both."},{"id":"define-your-data-model","title":"Define your data model","role":"content","text":"Context Retriever generates tools from a data model, not from CLI flags entered one field at a time. Define your entities in a Python file.\n\nCreate `models.py`:\n\n[code example]"},{"id":"load-sample-data","title":"Load sample data","role":"content","text":"Load a few customer hashes that match the model, so the tool call later in this quickstart returns predictable results:\n\n[code example]\n\nIf you already have data that matches this shape, you can skip this step and use your own keys instead."},{"id":"create-a-surface","title":"Create a surface","role":"content","text":"1. Create a context surface from your model file, pointing it at your Redis Cloud database:\n\n   [code example]\n\n1. Save the returned surface ID:\n\n   [code example]\n\n1. Confirm the surface was created:\n\n   [code example]"},{"id":"create-an-agent-key","title":"Create an agent key","role":"content","text":"An agent key authorizes an agent to call the tools generated for a surface.\n\n1. Create one scoped to the surface you just created:\n\n   [code example]\n\n1. Save the returned key:\n\n   [code example]"},{"id":"list-the-generated-tools","title":"List the generated tools","role":"content","text":"As the admin, confirm which tools Context Retriever generated from your model:\n\n[code example]\n\nThe list includes tools such as a search tool and a get-by-ID tool for each entity you defined."},{"id":"call-a-tool","title":"Call a tool","role":"content","text":"Call a generated tool directly, using the agent key instead of the admin key:\n\n[code example]\n\n> [!NOTE]\n> **What to expect:** A JSON result containing `Jane Doe` and `Jane Roberts`, the two sample customers whose `name` field matches `jane`. The agent never sends a database query directly. It calls a tool that Context Retriever generated from your model."},{"id":"clean-up","title":"Clean up","role":"content","text":"Delete the surface you created, which also revokes its agent keys:\n\n[code example]"},{"id":"next-steps","title":"Next steps","role":"content","text":"* Read [Context Retriever concepts](https://redis.io/docs/latest/develop/ai/context-engine/context-retriever/concepts) to understand tools, providers, and access tags.\n* [Create a Context Retriever service in Redis Cloud](https://redis.io/docs/latest/operate/iris/context-retriever/create-service) using the console instead of the CLI.\n* [Manage admin keys](https://redis.io/docs/latest/operate/iris/context-retriever/view-admin-keys)."}],"examples":[{"id":"install-the-python-client-ex0","language":"bash","code":"pip install redis-context-retriever","section_id":"install-the-python-client"},{"id":"sign-in-and-create-an-admin-key-ex0","language":"bash","code":"ctxctl auth login -u <your-redis-cloud-email>","section_id":"sign-in-and-create-an-admin-key"},{"id":"sign-in-and-create-an-admin-key-ex1","language":"bash","code":"ctxctl --output json admin create --name \"quickstart-admin\"","section_id":"sign-in-and-create-an-admin-key"},{"id":"sign-in-and-create-an-admin-key-ex2","language":"bash","code":"export CTX_ADMIN_KEY='<the returned key, starts with cs_admin_>'","section_id":"sign-in-and-create-an-admin-key"},{"id":"define-your-data-model-ex0","language":"python","code":"from context_surfaces.context_model import ContextField, ContextModel\n\nclass Customer(ContextModel):\n    __redis_key_template__ = \"customer:{id}\"\n\n    id: str = ContextField(description=\"Unique customer ID\", is_key_component=True)\n    name: str = ContextField(description=\"Customer name\", index=\"text\")\n    email: str = ContextField(description=\"Customer email address\", index=\"tag\")","section_id":"define-your-data-model"},{"id":"load-sample-data-ex0","language":"bash","code":"redis-cli -h <your-database-host> -p <port> -a '<your-database-password>' HSET customer:1 id 1 name \"Jane Doe\" email \"jane.doe@example.com\"\nredis-cli -h <your-database-host> -p <port> -a '<your-database-password>' HSET customer:2 id 2 name \"John Smith\" email \"john.smith@example.com\"\nredis-cli -h <your-database-host> -p <port> -a '<your-database-password>' HSET customer:3 id 3 name \"Jane Roberts\" email \"jane.roberts@example.com\"","section_id":"load-sample-data"},{"id":"create-a-surface-ex0","language":"bash","code":"ctxctl --output json surface create \\\n     --name \"quickstart-surface\" \\\n     --description \"Quickstart context surface\" \\\n     --models ./models.py \\\n     --redis-addr <your-database-host>:<port> \\\n     --redis-password '<your-database-password>' \\\n     --admin-key \"$CTX_ADMIN_KEY\"","section_id":"create-a-surface"},{"id":"create-a-surface-ex1","language":"bash","code":"export CTX_SURFACE_ID='<the returned surface id>'","section_id":"create-a-surface"},{"id":"create-a-surface-ex2","language":"bash","code":"ctxctl surface describe \"$CTX_SURFACE_ID\" --admin-key \"$CTX_ADMIN_KEY\"","section_id":"create-a-surface"},{"id":"create-an-agent-key-ex0","language":"bash","code":"ctxctl --output json agent create \\\n     --surface-id \"$CTX_SURFACE_ID\" \\\n     --name \"quickstart-agent\" \\\n     --admin-key \"$CTX_ADMIN_KEY\"","section_id":"create-an-agent-key"},{"id":"create-an-agent-key-ex1","language":"bash","code":"export CTX_AGENT_KEY='<the returned key, starts with cs_agent_>'","section_id":"create-an-agent-key"},{"id":"list-the-generated-tools-ex0","language":"bash","code":"ctxctl tools list --agent-key \"$CTX_AGENT_KEY\"","section_id":"list-the-generated-tools"},{"id":"call-a-tool-ex0","language":"bash","code":"ctxctl tools call search_customer_by_text --agent-key \"$CTX_AGENT_KEY\" --args '{\"query\": \"jane\", \"limit\": 5}'","section_id":"call-a-tool"},{"id":"clean-up-ex0","language":"bash","code":"ctxctl surface delete \"$CTX_SURFACE_ID\" --admin-key \"$CTX_ADMIN_KEY\" --confirm","section_id":"clean-up"}]}
{"schema_version":2,"id":"develop/ai/context-engine/data-integration","title":"Redis Data Integration","url":"https://redis.io/docs/latest/develop/ai/context-engine/data-integration/","summary":"Keep Redis in sync with your primary databases so AI agents always have access to fresh, accurate business data.","tags":["docs","develop","ai"],"last_updated":"2026-10-05T09:34:27+02:00","children":[{"id":"develop/ai/context-engine/data-integration/interactive-demo","summary":"Deploy an RDI pipeline for a PostgreSQL database, follow inserts, updates, and deletes into Redis, and change a table's keys with a job file.","title":"Redis Data Integration interactive demo","url":"https://redis.io/docs/latest/develop/ai/context-engine/data-integration/interactive-demo/"}],"page_type":"content","content_hash":"afed7fd6732a29a7c9e676176728c3174cae95c8d04484b7dbad4f5e8c8c7735","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Stream live business data into Redis so agents always work with accurate, up-to-date information.\n\nRedis Data Integration (RDI) keeps your Redis Cloud database in sync with your existing relational databases using [Change data capture](https://en.wikipedia.org/wiki/Change_data_capture) (CDC). Agents query Redis at full speed without ever querying your production databases directly.\n\n<div class=\"grid grid-cols-1 md:grid-cols-3 gap-6 my-8\">\n  \n  \n  \n</div>"},{"id":"what-is-redis-data-integration","title":"What is Redis Data Integration?","role":"content","text":"Redis Data Integration (RDI) is a pipeline service, available fully managed on Redis Cloud or self-managed on your own infrastructure, that:\n\n- **Syncs data in near real time**: Changes in your source database propagate to Redis within seconds using CDC\n- **Keeps agents away from source databases**: Agents query Redis at full speed, preserving performance and security\n- **Requires no coding**: Define pipelines through configuration; transformations are handled automatically\n- **Handles initial and streaming sync**: Full snapshot on first run, then continuous change capture from that point on\n- **Supports major relational databases**: Oracle, MySQL, PostgreSQL, MariaDB, SQL Server, and AWS Aurora"},{"id":"why-use-redis-data-integration","title":"Why use Redis Data Integration?","role":"content","text":"<div class=\"grid grid-cols-1 md:grid-cols-2 gap-6 my-6\">\n  <div class=\"p-5 border border-redis-pen-300 rounded-lg\">\n    <h3 class=\"text-redis-ink-900 font-semibold mb-3\">For AI applications</h3>\n    <ul class=\"space-y-1 text-redis-pen-600\">\n      <li>Eliminate stale data: agents always see current business state</li>\n      <li>Millisecond query performance from Redis instead of slow source DB calls</li>\n      <li>No cache invalidation logic to write or maintain</li>\n      <li>Combine with Context Retriever for governed, always-fresh agent tools</li>\n    </ul>\n  </div>\n  <div class=\"p-5 border border-redis-pen-300 rounded-lg\">\n    <h3 class=\"text-redis-ink-900 font-semibold mb-3\">For developers</h3>\n    <ul class=\"space-y-1 text-redis-pen-600\">\n      <li>Configuration-driven: no custom ETL code required</li>\n      <li>Deploy fully managed on Redis Cloud or self-managed on your own infrastructure</li>\n      <li>~10,000 records per second per core for initial snapshots and streaming</li>\n      <li>At-least-once delivery guaranteed for every change in the defined dataset</li>\n    </ul>\n  </div>\n</div>"},{"id":"quick-example","title":"Quick example","role":"content","text":"RDI pipelines are defined through configuration. You specify which source database tables to sync, how to map each row to a Redis key, and what transformations to apply. No custom code is required.\n\nSee the [RDI quick start](https://redis.io/docs/latest/operate/rc/rdi/quick-start) for a step-by-step walkthrough syncing a live PostgreSQL source to Redis Cloud.\n\nTo watch a pipeline sync a PostgreSQL database without setting one up, try the [interactive demo](https://redis.io/docs/latest/develop/ai/context-engine/data-integration/interactive-demo)."},{"id":"redis-data-integration-overview","title":"Redis Data Integration overview","role":"content","text":"AI agents are only as reliable as the data they work with. RDI solves the freshness problem by using [Change Data Capture (CDC)](https://en.wikipedia.org/wiki/Change_data_capture) to detect changes in your source database and propagate them to Redis within seconds. Agents interact only with Redis, which is fast, predictable, and always current.\n\nRDI pipelines run in two phases:\n\n- **Initial sync**: Reads a full snapshot of your source data and loads it into the target Redis database.\n- **Streaming**: Captures changes as they happen and applies them to Redis within seconds of the source change.\n\n![A source database syncs to Redis through an initial sync and a continuous CDC stream. Redis serves the agent.](https://redis.io/docs/latest/images/ai/context-engine/rdi-sync-flow.svg)\n\nData is transformed from relational rows into Redis hashes or JSON documents as part of the pipeline, with no coding required. You define what data to sync and how to map it using configuration, and RDI handles the rest."},{"id":"why-agents-need-fresh-data","title":"Why agents need fresh data","role":"content","text":"Without a reliable data pipeline, agents face two common failure modes: stale cached data that no longer reflects reality, or slow queries to source databases that block agent responsiveness. RDI eliminates both by maintaining a continuously updated Redis cache that agents can query at full Redis speed."},{"id":"key-benefits","title":"Key benefits","role":"content","text":"- **Near real-time updates**: Changes in your source database reach Redis within seconds using CDC.\n- **No direct database access**: Agents query Redis, not your production databases, preserving performance and security.\n- **No coding required**: Define pipelines through configuration. Transformations are handled automatically.\n- **Fully managed on Redis Cloud**: No infrastructure to provision or maintain.\n- **High throughput**: Processes approximately 10,000 records per second per core for initial snapshots and streaming.\n- **At-least-once delivery**: RDI guarantees every change in the defined dataset is delivered to Redis."},{"id":"supported-source-databases","title":"Supported source databases","role":"compatibility","text":"RDI supports the following source databases when used with Redis Cloud:\n\n| Database | Versions |\n|----------|----------|\n| Oracle | 19c, 21c |\n| MySQL | 5.7, 8.0.x, 8.2 |\n| PostgreSQL | 10–16 |\n| MariaDB | 10.5, 11.4.3 |\n| AWS Aurora PostgreSQL | 15 |\n| SQL Server | 2017, 2019, 2022 |"},{"id":"get-started","title":"Get started","role":"content","text":"**Redis Cloud:**\n\nRDI on Redis Cloud is available in preview for Redis Cloud Pro databases hosted on AWS.\n\nTo get started:\n\n1. [Prepare your source database](https://redis.io/docs/latest/operate/rc/rdi/setup) and configure credentials and connectivity.\n2. [Define your data pipeline](https://redis.io/docs/latest/operate/rc/rdi/define) by selecting which tables to sync and how to map them.\n3. [View and manage your pipeline](https://redis.io/docs/latest/operate/rc/rdi/view-edit) once it's running.\n\nSee the [RDI Cloud quick start](https://redis.io/docs/latest/operate/rc/rdi/quick-start) to get up and running quickly with a PostgreSQL source database.\n\n**Redis Enterprise:**\n\nRDI is also available for self-managed Redis Enterprise deployments. See the [Redis Data Integration documentation](https://redis.io/docs/latest/integrate/redis-data-integration) for full installation and configuration instructions."}],"examples":[]}
{"schema_version":2,"id":"develop/ai/context-engine/data-integration/interactive-demo","title":"Redis Data Integration interactive demo","url":"https://redis.io/docs/latest/develop/ai/context-engine/data-integration/interactive-demo/","summary":"Deploy an RDI pipeline for a PostgreSQL database, follow inserts, updates, and deletes into Redis, and change a table's keys with a job file.","tags":["docs","develop","ai"],"last_updated":"2026-10-05T09:34:27+02:00","page_type":"content","content_hash":"09630d548aca28c169b47bd1632fe4d6bc26111b20afeadebe9cf096434f7cd1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Data Integration (RDI) keeps Redis in sync with a relational database, so your app and its agents read from Redis instead of the source. This demo follows a food delivery app that keeps its data in PostgreSQL. You deploy the pipeline, run SQL statements against the source, and see each change reach Redis.\n\nThe demo runs in your browser and doesn't connect to an RDI pipeline or a database. The configuration, job file, key names, and `redis-di status` output follow the [RDI documentation](https://redis.io/docs/latest/integrate/redis-data-integration). The data and timings are illustrative. The customers, restaurants, and orders are the same sample data as the [Context Retriever interactive demo](https://redis.io/docs/latest/develop/ai/context-engine/context-retriever/interactive-demo)."},{"id":"what-the-demo-shows","title":"What the demo shows","role":"content","text":"1. **Initial sync.** When you deploy a pipeline, RDI takes a snapshot of the tables you select and writes each row to Redis under its own key, as a hash or a JSON document.\n2. **Change data capture.** After the snapshot, RDI captures every insert, update, and delete committed to those tables and applies it to Redis, usually within a few seconds.\n3. **Job files.** A job file changes how RDI writes one table, such as its key pattern or its fields. A job only affects changes captured after you deploy it, until you reset the pipeline, and a reset doesn't delete the keys RDI wrote before."},{"id":"next-steps","title":"Next steps","role":"content","text":"- Follow the [RDI quick start](https://redis.io/docs/latest/operate/rc/rdi/quick-start) to sync a PostgreSQL database to Redis Cloud.\n- Learn how to [define a data pipeline](https://redis.io/docs/latest/operate/rc/rdi/define) on Redis Cloud.\n- See the [job file examples](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples) for more transformations."}],"examples":[]}
{"schema_version":2,"id":"develop/ai/context-engine","title":"Redis Iris context engine","url":"https://redis.io/docs/latest/develop/ai/context-engine/","summary":"Redis Iris is a suite of managed and self-managed services for agent memory, semantic caching, and governed data access.","tags":["docs","develop","ai"],"last_updated":"2026-10-15T14:32:20-07:00","children":[{"id":"develop/ai/context-engine/concepts","summary":"What happens when an agent asks Redis Iris for context, and why that's faster than assembling it yourself.","title":"How Redis Iris works","url":"https://redis.io/docs/latest/develop/ai/context-engine/concepts/"},{"id":"develop/ai/context-engine/agent-memory","summary":"Store agent memory for AI applications in Redis.","title":"Redis Agent Memory","url":"https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/"},{"id":"develop/ai/context-engine/context-retriever","summary":"Expose tools to Agents to query your Redis databases.","title":"Redis Context Retriever","url":"https://redis.io/docs/latest/develop/ai/context-engine/context-retriever/"},{"id":"develop/ai/context-engine/langcache","summary":"Store LLM responses for AI apps in a semantic cache.","title":"Redis LangCache","url":"https://redis.io/docs/latest/develop/ai/context-engine/langcache/"},{"id":"develop/ai/context-engine/data-integration","summary":"Keep Redis in sync with your primary databases so AI agents always have access to fresh, accurate business data.","title":"Redis Data Integration","url":"https://redis.io/docs/latest/develop/ai/context-engine/data-integration/"}],"page_type":"content","content_hash":"295b3cd4c7b33c2da57b8afb30a39e7e4dccba62f3f14ef2fcbe1763df49075f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Give your AI agents the context layer they need to reliably act on business data.\n\nRedis Iris eliminates the infrastructure burden of building context-aware AI agents: persistent memory, semantic caching, governed data access, and live data sync, fully managed on Redis Cloud or self-managed on your own infrastructure.\n\n<div class=\"grid grid-cols-1 md:grid-cols-4 gap-6 my-8\">\n  \n  \n  \n</div>"},{"id":"what-is-redis-iris","title":"What is Redis Iris?","role":"content","text":"Redis Iris is a production-ready context engine for AI agents that:\n\n- **Reduces LLM costs**: Semantic caching returns cached responses for similar queries in milliseconds\n- **Adds persistent memory**: Agents remember past interactions and user preferences across sessions\n- **Structures business data access**: Context Retriever generates governed tools agents can safely call at runtime\n- **Keeps data fresh**: Data Integration streams live changes from relational databases into Redis within seconds\n- **Deploys your way**: All four services are available fully managed on Redis Cloud or self-managed on your own infrastructure, via REST API"},{"id":"why-use-redis-iris","title":"Why use Redis Iris?","role":"content","text":"<div class=\"grid grid-cols-1 md:grid-cols-2 gap-6 my-6\">\n  <div class=\"p-5 border border-redis-pen-300 rounded-lg\">\n    <h3 class=\"text-redis-ink-900 font-semibold mb-3\">For AI applications</h3>\n    <ul class=\"space-y-1 text-redis-pen-600\">\n      <li>Agents that remember context across sessions and users</li>\n      <li>Faster responses and lower costs through semantic caching</li>\n      <li>Reliable, structured access to live business data</li>\n      <li>No stale data: near real-time sync from your source databases</li>\n    </ul>\n  </div>\n  <div class=\"p-5 border border-redis-pen-300 rounded-lg\">\n    <h3 class=\"text-redis-ink-900 font-semibold mb-3\">For developers</h3>\n    <ul class=\"space-y-1 text-redis-pen-600\">\n      <li>Four services, fully managed on Redis Cloud or self-managed on your own infrastructure</li>\n      <li>Python and JavaScript SDKs and REST APIs for all services</li>\n      <li>Define your data model once, reuse it across all agents</li>\n      <li>No database setup required on Redis Cloud</li>\n    </ul>\n  </div>\n</div>"},{"id":"next-steps","title":"Next steps","role":"content","text":"Learn [how Redis Iris works](https://redis.io/docs/latest/develop/ai/context-engine/concepts), from the agent request to where each kind of context lives."}],"examples":[]}
{"schema_version":2,"id":"develop/ai/context-engine/langcache/api-examples","title":"Use the LangCache API and SDK","url":"https://redis.io/docs/latest/develop/ai/context-engine/langcache/api-examples/","summary":"Learn to use the Redis LangCache API for semantic caching.","aliases":["/develop/ai/langcache/api-examples"],"tags":["docs","develop","ai"],"last_updated":"2026-09-11T15:54:40+01:00","page_type":"content","content_hash":"404df6418fc0c9d89773ab29b1f209f55cdc9092c201a1fd563ce856afad921e","sections":[{"id":"authentication","title":"Authentication","role":"security","text":"To access the LangCache API, you need:\n\n- LangCache API base URL\n- LangCache service API key\n- Cache ID\n\nWhen you call the API, you need to pass the LangCache API key in the `Authorization` header as a Bearer token and the Cache ID as the `cacheId` path parameter.\n\nFor example:\n\nFoundational: Authenticate with the LangCache API using Bearer token authorization in the request header\n\n**Difficulty:** Beginner\n\n**Available in:** Redis CLI, JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nThis example expects several variables to be set in the shell:\n\n- **$HOST** - the LangCache API base URL\n- **$CACHE_ID** - the Cache ID of your cache\n- **$API_KEY** - The LangCache API token"},{"id":"examples","title":"Examples","role":"example","text":""},{"id":"search-langcache-for-similar-responses","title":"Search LangCache for similar responses","role":"content","text":"Use [`POST /v1/caches/{cacheId}/entries/search`](https://redis.io/docs/latest/develop/ai/context-engine/langcache/api-reference#tag/Cache-Entries/operation/search) to search the cache for matching responses to a user prompt.\n\nFoundational: Search the cache for semantically similar responses to a user prompt\n\n**Difficulty:** Beginner\n\n**Available in:** Redis CLI, JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nPlace this call in your client app right before you call your LLM's REST API. If LangCache returns a response, you can send that response back to the user instead of calling the LLM.\n\nIf LangCache does not return a response, you should call your LLM's REST API to generate a new response. After you get a response from the LLM, you can [store it in LangCache](#store-a-new-response-in-langcache) for future use.\n\n#### Attributes\nYou can also scope the responses returned from LangCache by adding an `attributes` object to the request. LangCache will only return responses that match the attributes you specify.\n\nFilter responses: Filter cached responses by custom attributes to scope search results to specific contexts or metadata\n\n**Difficulty:** Intermediate\n\n**Available in:** Redis CLI, JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\n#### Search strategies\nLangCache supports two search strategies when looking for a cached response:\n\n- **`exact`**: Returns a result only when the stored prompt matches the query exactly (case insensitive).\n- `**semantic**`: Uses vector similarity to find semantically similar prompts and responses.\n\nBy default, LangCache uses `semantic` search only. You can specify one or more search strategies in the request body.\n\nHybrid search: Combine exact and semantic search strategies to find both identical and semantically similar cached responses\n\n**Difficulty:** Intermediate\n\n**Available in:** Redis CLI, JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]"},{"id":"store-a-new-response-in-langcache","title":"Store a new response in LangCache","role":"content","text":"Use [`POST /v1/caches/{cacheId}/entries`](https://redis.io/docs/latest/develop/ai/context-engine/langcache/api-reference#tag/Cache-Entries/operation/set) to store a new response in the cache.\n\nFoundational: Store a new LLM response in the cache with its corresponding prompt for future retrieval\n\n**Difficulty:** Beginner\n\n**Available in:** Redis CLI, JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nPlace this call in your client app after you get a response from the LLM. This will store the response in the cache for future use.\n\nYou can also store the responses with custom attributes by adding an `attributes` object to the request.\n\nCustom attributes: Store cached responses with custom attributes to enable filtering and scoping of future searches\n\n**Difficulty:** Intermediate\n\n**Available in:** Redis CLI, JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]"},{"id":"delete-cached-responses","title":"Delete cached responses","role":"content","text":"Use [`DELETE /v1/caches/{cacheId}/entries/{entryId}`](https://redis.io/docs/latest/develop/ai/context-engine/langcache/api-reference#tag/Cache-Entries/operation/delete) to delete a cached response from the cache.\n\nFoundational: Delete a specific cached response by its entry ID when you need to remove outdated or incorrect cache entries\n\n**Difficulty:** Beginner\n\n**Available in:** Redis CLI, JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nYou can also use [`DELETE /v1/caches/{cacheId}/entries`](https://redis.io/docs/latest/develop/ai/context-engine/langcache/api-reference#tag/Cache-Entries/operation/deleteQuery) to delete multiple cached responses based on the `attributes` you specify. If you specify multiple `attributes`, LangCache will delete entries that contain all given attributes.\n\n> [!WARNING]\n> If you do not specify any `attributes`, all responses in the cache will be deleted. This cannot be undone.\n\n<br/>\n\nDelete by attribute: Delete multiple cached responses based on attribute filters when you need to remove entries matching specific criteria\n\n**Difficulty:** Intermediate\n\n**Available in:** Redis CLI, JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]"},{"id":"flush-the-cache","title":"Flush the cache","role":"content","text":"Use\n[POST /v1/caches/{cacheId}/flush](https://redis.io/docs/latest/develop/ai/context-engine/langcache/api-reference/#tag/Cache-Entries/operation/flush)\nto flush all entries from the cache.\n\nFlush cache: Clear all cached responses from the cache in a single operation (use with caution as this cannot be undone)\n\n**Difficulty:** Advanced\n\n**Available in:** Redis CLI, JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]"}],"examples":[{"id":"authentication-ex0","language":"plaintext","code":"curl -s -X POST \"https://$HOST/v1/caches/$CACHE_ID/entries/search\" \\\n    -H \"accept: application/json\" \\\n    -H \"Authorization: Bearer $API_KEY\" \\\n    -d '{ \"prompt\": \"What is semantic caching\" }'","section_id":"authentication"},{"id":"authentication-ex1","language":"javascript","code":"import { LangCache } from \"@redis-ai/langcache\";\n\nconst langCache = new LangCache({\n    serverURL: \"https://\" + process.env.HOST,\n    cacheId: process.env.CACHE_ID,\n    apiKey: process.env.API_KEY,\n  });","section_id":"authentication"},{"id":"authentication-ex2","language":"python","code":"from langcache import LangCache\nimport os\n\nlang_cache = LangCache(\n    server_url=f\"https://{os.getenv('HOST', '')}\",\n    cache_id=os.getenv(\"CACHE_ID\", \"\"),\n    api_key=os.getenv(\"API_KEY\", \"\")\n)","section_id":"authentication"},{"id":"search-langcache-for-similar-responses-ex0","language":"plaintext","code":"POST https://[host]/v1/caches/{cacheId}/entries/search\n{\n    \"prompt\": \"User prompt text\"\n}","section_id":"search-langcache-for-similar-responses"},{"id":"search-langcache-for-similar-responses-ex1","language":"javascript","code":"async function searchBasic() {\n    const result = await langCache.search({\n      prompt: \"User prompt text\",\n      similarityThreshold: 0.9,\n    });\n\n    console.log(result);\n}\n\nsearchBasic();","section_id":"search-langcache-for-similar-responses"},{"id":"search-langcache-for-similar-responses-ex2","language":"python","code":"res = lang_cache.search(\n    prompt=\"User prompt text\",\n    similarity_threshold=0.9\n)\n\nprint(res)","section_id":"search-langcache-for-similar-responses"},{"id":"search-langcache-for-similar-responses-ex3","language":"plaintext","code":"POST https://[host]/v1/caches/{cacheId}/entries/search\n{\n    \"prompt\": \"User prompt text\",\n    \"attributes\": {\n        \"customAttributeName\": \"customAttributeValue\"\n    }\n}","section_id":"search-langcache-for-similar-responses"},{"id":"search-langcache-for-similar-responses-ex4","language":"javascript","code":"async function searchAttributes() {\n    const result = await langCache.search({\n      prompt: \"User prompt text\",\n      attributes: {\n        \"customAttributeName\": \"customAttributeValue\",\n      },\n      similarityThreshold: 0.9,\n    });\n\n    console.log(result);\n}\n\nsearchAttributes();","section_id":"search-langcache-for-similar-responses"},{"id":"search-langcache-for-similar-responses-ex5","language":"python","code":"res = lang_cache.search(\n    prompt=\"User prompt text\",\n    attributes={\"customAttributeName\": \"customAttributeValue\"},\n    similarity_threshold=0.9,\n)\n\nprint(res)","section_id":"search-langcache-for-similar-responses"},{"id":"search-langcache-for-similar-responses-ex6","language":"plaintext","code":"POST https://[host]/v1/caches/{cacheId}/entries/search\n{\n    \"prompt\": \"User prompt text\",\n    \"searchStrategies\": [\"exact\", \"semantic\"]\n}","section_id":"search-langcache-for-similar-responses"},{"id":"search-langcache-for-similar-responses-ex7","language":"javascript","code":"import { SearchStrategy } from '@redis-ai/langcache/models/searchstrategy.js';\n\nasync function searchStrategies() {\n    const result = await langCache.search({\n      prompt: \"User prompt text\",\n      searchStrategies: [SearchStrategy.Exact, SearchStrategy.Semantic],\n      similarityThreshold: 0.9,\n    });\n\n    console.log(result);\n}\n\nsearchStrategies();","section_id":"search-langcache-for-similar-responses"},{"id":"search-langcache-for-similar-responses-ex8","language":"python","code":"from langcache.models import SearchStrategy\n\nres = lang_cache.search(\n    prompt=\"User prompt text\",\n    search_strategies=[SearchStrategy.EXACT, SearchStrategy.SEMANTIC],\n    similarity_threshold=0.9,\n)\n\nprint(res)","section_id":"search-langcache-for-similar-responses"},{"id":"store-a-new-response-in-langcache-ex0","language":"plaintext","code":"POST https://[host]/v1/caches/{cacheId}/entries\n{\n    \"prompt\": \"User prompt text\",\n    \"response\": \"LLM response text\"\n}","section_id":"store-a-new-response-in-langcache"},{"id":"store-a-new-response-in-langcache-ex1","language":"javascript","code":"async function storeBasic() {\n    const result = await langCache.set({\n      prompt: \"User prompt text\",\n      response: \"LLM response text\",\n    });\n\n    console.log(result);\n}\n\nstoreBasic();","section_id":"store-a-new-response-in-langcache"},{"id":"store-a-new-response-in-langcache-ex2","language":"python","code":"res = lang_cache.set(\n    prompt=\"User prompt text\",\n    response=\"LLM response text\",\n)\n\nprint(res)","section_id":"store-a-new-response-in-langcache"},{"id":"store-a-new-response-in-langcache-ex3","language":"plaintext","code":"POST https://[host]/v1/caches/{cacheId}/entries\n{\n    \"prompt\": \"User prompt text\",\n    \"response\": \"LLM response text\",\n    \"attributes\": {\n        \"customAttributeName\": \"customAttributeValue\"\n    }\n}","section_id":"store-a-new-response-in-langcache"},{"id":"store-a-new-response-in-langcache-ex4","language":"javascript","code":"async function storeAttributes() {\n    const result = await langCache.set({\n      prompt: \"User prompt text\",\n      response: \"LLM response text\",\n      attributes: {\n        \"customAttributeName\": \"customAttributeValue\",\n      },\n    });\n\n    console.log(result);\n}\n\nstoreAttributes();","section_id":"store-a-new-response-in-langcache"},{"id":"store-a-new-response-in-langcache-ex5","language":"python","code":"res = lang_cache.set(\n    prompt=\"User prompt text\",\n    response=\"LLM response text\",\n    attributes={\"customAttributeName\": \"customAttributeValue\"},\n)\n\nprint(res)","section_id":"store-a-new-response-in-langcache"},{"id":"delete-cached-responses-ex0","language":"plaintext","code":"DELETE https://[host]/v1/caches/{cacheId}/entries/{entryId}","section_id":"delete-cached-responses"},{"id":"delete-cached-responses-ex1","language":"javascript","code":"async function deleteEntry() {\n    const result = await langCache.deleteById({\n      entryId: \"<entryId>\",\n    });\n\n    console.log(result);\n}\n\ndeleteEntry();","section_id":"delete-cached-responses"},{"id":"delete-cached-responses-ex2","language":"python","code":"res = lang_cache.delete_by_id(entry_id=\"<entryId>\")\n\nprint(res)","section_id":"delete-cached-responses"},{"id":"delete-cached-responses-ex3","language":"plaintext","code":"DELETE https://[host]/v1/caches/{cacheId}/entries\n{\n    \"attributes\": {\n        \"customAttributeName\": \"customAttributeValue\"\n    }\n}","section_id":"delete-cached-responses"},{"id":"delete-cached-responses-ex4","language":"javascript","code":"async function deleteQuery() {\n    const result = await langCache.deleteQuery({\n      attributes: {\n        \"customAttributeName\": \"customAttributeValue\",\n      },\n    });\n\n    console.log(result);\n}\n\ndeleteQuery();","section_id":"delete-cached-responses"},{"id":"delete-cached-responses-ex5","language":"python","code":"res = lang_cache.delete_query(\n    attributes={\"customAttributeName\": \"customAttributeValue\"},\n)\n\nprint(res)","section_id":"delete-cached-responses"},{"id":"flush-the-cache-ex0","language":"plaintext","code":"POST https://[host]/v1/caches/{cacheId}/flush","section_id":"flush-the-cache"},{"id":"flush-the-cache-ex1","language":"javascript","code":"async function flush() {\n    await langCache.flush();\n}\n\nflush();","section_id":"flush-the-cache"},{"id":"flush-the-cache-ex2","language":"python","code":"lang_cache.flush()","section_id":"flush-the-cache"}]}
{"schema_version":2,"id":"develop/ai/context-engine/langcache/api-reference","title":"LangCache REST API","url":"https://redis.io/docs/latest/develop/ai/context-engine/langcache/api-reference/","summary":"","aliases":["/develop/ai/langcache/api-reference"],"tags":[],"last_updated":"2026-05-11T17:06:06-04:00","page_type":"index","sections":[],"examples":[]}
{"schema_version":2,"id":"develop/ai/context-engine/langcache/concepts","title":"LangCache concepts","url":"https://redis.io/docs/latest/develop/ai/context-engine/langcache/concepts/","summary":"Learn how semantic caching in LangCache differs from exact-key caching, and what that means for correctness.","tags":["docs","develop","ai"],"last_updated":"2026-10-01T12:49:59-07:00","page_type":"content","content_hash":"a90ec4818c30e049d37fd6fabfd59bc682f9c519352359b4f52089c793ce093c","sections":[{"id":"semantic-caching","title":"Semantic caching","role":"overview","text":"LangCache matches an incoming prompt against stored entries by similarity, not exact text, so two prompts that are close enough are treated as the same request. This similarity match lets *What are Product A's features?* and *Tell me about Product A's capabilities* share a cached response.\n\n![An incoming prompt is checked against a similarity threshold. A match returns a cached response in milliseconds. A miss calls the LLM, stores the prompt and response as a new cache entry, then returns the LLM response.](https://redis.io/docs/latest/images/ai/context-engine/langcache-semantic-flow.svg)\n\nIf you've used a traditional, exact-key cache before, similarity matching behaves differently from what you're used to. Semantic caching gives up the guarantee that a cache hit is always correct, in exchange for the ability to match paraphrased or related questions. That trade-off is what makes the cache useful for an agent. Users and agents rarely phrase the same request the same way twice. Matching by meaning, instead of exact text, turns repeat requests into fast, cache-served responses."},{"id":"exact-key-caching-vs-semantic-caching","title":"Exact-key caching vs. semantic caching","role":"content","text":"Here's how the two compare:\n\n| | Exact-key caching | Semantic caching (LangCache) |\n|:---|:---|:---|\n| Match criterion | Exact key equality | Similarity above a threshold |\n| Hit/miss | Binary: no in-between | Threshold-tuned: a near-miss is still possible |\n| Correctness risk | None from the cache itself | A false-positive match can return a wrong answer |\n| Tuning | Time-to-live (TTL), eviction policy | TTL, eviction policy, **and** similarity threshold |"},{"id":"key-terms","title":"Key terms","role":"content","text":"- **Cache ID**: The unique identifier for your LangCache service. You pass it as the `cacheId` path parameter in every API call.\n- **Attributes**: Custom key-value tags you define per service, up to 5. Attach them to a cache entry to scope later searches and deletes.\n- **Embedding provider**: The model that generates embeddings for similarity matching. Choose Redis, OpenAI, or your own provider.\n- **Search strategy**: How LangCache matches a request against stored entries. `exact` matches on literal text. `semantic` matches by embedding similarity above your threshold."},{"id":"choosing-a-threshold-is-a-tradeoff-even-with-a-default","title":"Choosing a threshold is a tradeoff, even with a default","role":"content","text":"LangCache defaults to a similarity threshold of `0.85`, with a recommended starting range of `0.8`–`0.9`. No single value is correct for every use case. A tighter threshold reduces wrong-answer risk. It also reduces the hit rate you're paying for the cache to get. A looser threshold raises the hit rate. It also raises the odds of a false-positive match. The right setting depends on the relative cost of a wrong answer versus an unnecessary LLM call. A support FAQ bot can tolerate a looser threshold than a bot answering account-specific financial questions."},{"id":"faq","title":"FAQ","role":"content","text":"**Could I get back a response for a different question than the one I asked?**\nYes. Matching on similarity rather than exact content is an inherent property of semantic caching, not a bug. LangCache matches by similarity. A prompt that's close enough to a cached one can match, even when the two aren't equivalent. Tightening the similarity threshold makes mismatches less likely. You can also inspect a matched entry to see how close the embeddings actually were.\n\n**Is there a default similarity threshold I should use?**\nYes. LangCache defaults to `0.85`, with a recommended starting range of `0.8`–`0.9`. From there, the right threshold is a tradeoff specific to your use case (see above). Start with a conservative, tighter threshold. Loosen it gradually while monitoring hit rate and spot-checking matches. Don't start loose and try to catch bad matches after the fact.\n\n**Does LangCache replace my existing cache layer?**\nOnly the part of it that caches LLM responses by similarity. LangCache doesn't replace general-purpose exact-key caching for anything else in your application.\n\nSee the [AI agent context engine FAQ](https://redis.io/blog/faq-real-time-context-engine-agent-memory-and-retrieval/) for how LangCache compares to building your own cache or skipping caching for smaller workloads."},{"id":"next-steps","title":"Next steps","role":"content","text":"- Try the [LangCache interactive demo](https://redis.io/docs/latest/develop/ai/context-engine/langcache/interactive-demo) to see how the similarity threshold changes hits and misses.\n- [Create a LangCache service](https://redis.io/docs/latest/operate/iris/langcache/create-service) on Redis Cloud.\n- [Deploy LangCache self-managed](https://redis.io/docs/latest/operate/iris/langcache/self-managed/deploy) on your own Kubernetes infrastructure.\n- [Use the LangCache API and SDK](https://redis.io/docs/latest/develop/ai/context-engine/langcache/api-examples) to search and populate a cache.\n- [LangCache REST API reference](https://redis.io/docs/latest/develop/ai/context-engine/langcache/api-reference) for the full endpoint and parameter details, including threshold configuration."}],"examples":[]}
{"schema_version":2,"id":"develop/ai/context-engine/langcache","title":"Redis LangCache","url":"https://redis.io/docs/latest/develop/ai/context-engine/langcache/","summary":"Store LLM responses for AI apps in a semantic cache.","aliases":["/develop/ai/langcache"],"tags":["docs","develop","ai"],"last_updated":"2026-10-05T20:57:56-07:00","children":[{"id":"develop/ai/context-engine/langcache/concepts","summary":"Learn how semantic caching in LangCache differs from exact-key caching, and what that means for correctness.","title":"LangCache concepts","url":"https://redis.io/docs/latest/develop/ai/context-engine/langcache/concepts/"},{"id":"develop/ai/context-engine/langcache/interactive-demo","summary":"Watch a semantic cache answer customers' repeat questions, tune its similarity threshold, and scope answers with attributes.","title":"Redis LangCache interactive demo","url":"https://redis.io/docs/latest/develop/ai/context-engine/langcache/interactive-demo/"},{"id":"develop/ai/context-engine/langcache/api-examples","summary":"Learn to use the Redis LangCache API for semantic caching.","title":"Use the LangCache API and SDK","url":"https://redis.io/docs/latest/develop/ai/context-engine/langcache/api-examples/"},{"id":"develop/ai/context-engine/langcache/api-reference","summary":"","title":"LangCache REST API","url":"https://redis.io/docs/latest/develop/ai/context-engine/langcache/api-reference/"}],"page_type":"content","content_hash":"32a35ae7d835db274fdc5ac304758d7515539c54261ae0b8e87196f8da00fdec","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Cut LLM costs and improve response times with semantic caching.\n\nLangCache checks whether a semantically similar prompt has been answered before and returns the cached response instantly: no LLM call required. When there's no match, your app calls the LLM as usual and stores the result for future use.\n\n<div class=\"grid grid-cols-1 md:grid-cols-4 gap-6 my-8\">\n  \n  \n  \n  \n</div>"},{"id":"what-is-langcache","title":"What is LangCache?","role":"content","text":"LangCache is a semantic caching service, available fully managed on Redis Cloud or self-managed on your own infrastructure, that:\n\n- **Reduces LLM costs**: Avoids redundant API calls for semantically equivalent queries\n- **Improves response times**: Returns cached answers in milliseconds instead of waiting for an LLM\n- **Handles embeddings automatically**: No embedding model to manage; LangCache generates them for you\n- **Gives you cache control**: Configure similarity thresholds, TTLs, and eviction policies\n- **Works with any LLM workflow**: REST API and Python/JS SDKs drop into existing applications"},{"id":"why-use-langcache","title":"Why use LangCache?","role":"content","text":"<div class=\"grid grid-cols-1 md:grid-cols-2 gap-6 my-6\">\n  <div class=\"p-5 border border-redis-pen-300 rounded-lg\">\n    <h3 class=\"text-redis-ink-900 font-semibold mb-3\">For AI applications</h3>\n    <ul class=\"space-y-1 text-redis-pen-600\">\n      <li>Dramatically lower LLM API spend for apps with repetitive queries</li>\n      <li>Faster responses for AI assistants, chatbots, and retrieval-augmented generation (RAG) pipelines</li>\n      <li>Cache intermediate results in multi-step agent workflows</li>\n      <li>Centralize caching across multiple apps via an AI gateway</li>\n    </ul>\n  </div>\n  <div class=\"p-5 border border-redis-pen-300 rounded-lg\">\n    <h3 class=\"text-redis-ink-900 font-semibold mb-3\">For developers</h3>\n    <ul class=\"space-y-1 text-redis-pen-600\">\n      <li>Two API calls to integrate: search before LLM, store after LLM</li>\n      <li>Python and JavaScript SDKs available on PyPI and npm</li>\n      <li>Deploy fully managed on Redis Cloud or self-managed on your own infrastructure</li>\n      <li>Monitor hit rates and cost savings from the Redis Cloud console</li>\n    </ul>\n  </div>\n</div>"},{"id":"quick-example","title":"Quick example","role":"content","text":"Before calling your LLM, search for a cached response:\n\n[code example]\n\nOn a cache miss, call your LLM, then store the result:\n\n[code example]\n\nSee the full [LangCache API and SDK examples](https://redis.io/docs/latest/develop/ai/context-engine/langcache/api-examples) for more."},{"id":"langcache-overview","title":"LangCache overview","role":"content","text":"LangCache uses semantic caching to store and reuse previous LLM responses for repeated queries. Instead of calling the LLM for every request, LangCache checks if a similar response has already been generated and is stored in the cache. If a match is found, LangCache returns the cached response instantly, saving time and resources. \n\nImagine you’re using an LLM to build an agent to answer questions about your company's products. Your users may ask questions like the following:\n\n- \"What are the features of Product A?\"\n- \"Can you list the main features of Product A?\"\n- \"Tell me about Product A’s features.\"\n\nThese prompts may have slight variations, but they essentially ask the same question. LangCache can help you avoid calling the LLM for each of these prompts by caching the response to the first prompt and returning it for any similar prompts.\n\nTo watch a cache answer paraphrased questions, try the [interactive demo](https://redis.io/docs/latest/develop/ai/context-engine/langcache/interactive-demo).\n\nUsing LangCache as a semantic caching service has the following benefits:\n\n- **Lower LLM costs**:  Reduce costly LLM calls by easily storing the most frequently-requested responses.\n- **Faster AI app responses**: Get faster AI responses by retrieving previously-stored requests from memory.\n- **Simpler deployments**: Access our managed service using a REST API with automated embedding generation, configurable controls, and no database management required.\n- **Advanced cache management**: Manage data access, privacy, and eviction protocols. Monitor usage and cache hit rates.\n\nLangCache works well for the following use cases:\n\n- **AI assistants and chatbots**: Optimize conversational AI applications by caching common responses and reducing latency for frequently asked questions.\n- **RAG applications**: Enhance retrieval-augmented generation performance by caching responses to similar queries, reducing both cost and response time.\n- **AI agents**: Improve multi-step reasoning chains and agent workflows by caching intermediate results and common reasoning patterns.\n- **AI gateways**: Integrate LangCache into centralized AI gateway services to manage and control LLM costs across multiple applications.."},{"id":"llm-cost-reduction-with-langcache","title":"LLM cost reduction with LangCache","role":"content","text":"LangCache reduces your LLM costs by caching responses and avoiding repeated API calls. When a response is served from cache, you don’t pay for output tokens. Input token costs are typically offset by embedding and storage costs.\n\nFor every cached response, you'll save the output token cost. To calculate your monthly savings with LangCache, you can use the following formula:\n\n[code example]\n\nThe more requests you serve from LangCache, the more you save, because you’re not paying to regenerate the output.\n\nHere’s an example:\n- Monthly LLM spend: $200\n- Percentage of output tokens in your spend: 60%\n- Cost of output tokens: $200 × 60% = $120\n- Cache hit rate: 50%\n- Estimated savings: $120 × 50% = $60/month\n\n> [!NOTE]\n> The formula and numbers above provide a rough estimate of your monthly savings. Actual savings will vary depending on your usage.\n\nYou can also use the [LangCache savings calculator](https://redis.io/calculator/langcache/) to estimate your annual savings with LangCache."},{"id":"langcache-architecture","title":"LangCache architecture","role":"content","text":"The following diagram displays how you can integrate LangCache into your GenAI app:\n\n![The LangCache process diagram.](https://redis.io/docs/latest/images/rc/langcache-process.png)\n\n1. A user sends a prompt to your AI app.\n1. Your app sends the prompt to LangCache through the `POST /v1/caches/{cacheId}/entries/search` endpoint.\n1. LangCache calls an embedding model service to generate an embedding for the prompt.\n1. LangCache searches the cache to see if a similar response already exists by matching the embeddings of the new query with the stored embeddings. \n1. If a semantically similar entry is found (also known as a cache hit), LangCache gets the cached response and returns it to your app. Your app can then send the cached response back to the user.\n1. If no match is found (also known as a cache miss), your app receives an empty response from LangCache. Your app then queries your chosen LLM to generate a new response.\n1. Your app sends the prompt and the new response to LangCache through the `POST /v1/caches/{cacheId}/entries` endpoint. \n1. LangCache stores the embedding with the new response in the cache for future use.\n\nSee the [LangCache API and SDK examples](https://redis.io/docs/latest/develop/ai/context-engine/langcache/api-examples) for more information on how to use the LangCache API."},{"id":"get-started","title":"Get started","role":"content","text":"LangCache is currently in preview:\n\n- Public preview on [Redis Cloud](https://redis.io/docs/latest/operate/iris/langcache)\n- Self-managed deployment on Kubernetes, as a private preview\n- Fully-managed [private preview](https://redis.io/langcache/)\n\n**Redis Cloud:**\n\nTo set up LangCache on Redis Cloud:\n\n1. [Create a database](https://redis.io/docs/latest/operate/rc/databases/create-database) on Redis Cloud.\n2. [Create a LangCache service](https://redis.io/docs/latest/operate/iris/langcache/create-service) for your database on Redis Cloud.\n3. [Use the LangCache API](https://redis.io/docs/latest/operate/iris/langcache/use-langcache) from your client app.\n\nAfter you set up LangCache, you can [view and edit the cache](https://redis.io/docs/latest/operate/iris/langcache/view-edit-cache) and [monitor the cache's performance](https://redis.io/docs/latest/operate/iris/langcache/monitor-cache).\n\nSee also our [Redis LangCache setup](https://www.youtube.com/watch?v=UOGhMZlZLko)\ntutorial video for advice on how to get started.\n\n**Self-managed (private preview):**\n\nSelf-managed LangCache is available for deployment on Kubernetes as a private preview. See [Self-managed LangCache](https://redis.io/docs/latest/operate/iris/langcache/self-managed) for deployment, configuration, security, and operations.\n\nYou need a license key to deploy: [contact Redis](https://redis.io/contact/) to request access.\n\n**Fully-managed private preview:**"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"To use LangCache in private preview, you need:\n\n- An AI application that makes LLM API calls\n- A use case involving repetitive or similar queries\n- Willingness to provide feedback during the preview phase"},{"id":"access","title":"Access","role":"content","text":"LangCache is offered as a fully-managed service. During the private preview:\n\n- Participation is free\n- Usage limits may apply\n- Dedicated support is provided\n- Regular feedback sessions are conducted"},{"id":"data-security-and-privacy","title":"Data security and privacy","role":"content","text":"LangCache stores your data on your Redis servers. Redis does not access your data or use it to train AI models. The service maintains enterprise-grade security and privacy standards."},{"id":"support","title":"Support","role":"compatibility","text":"Private preview participants receive:\n\n- Dedicated onboarding resources\n- Documentation and tutorials\n- Email and chat support\n- Regular check-ins with the product team\n- Exclusive roadmap updates\n\nFor more information about joining the private preview, visit the [Redis LangCache website](https://redis.io/langcache/)."}],"examples":[{"id":"quick-example-ex0","language":"json","code":"POST /v1/caches/{cacheId}/entries/search\n{\n    \"prompt\": \"What are the features of Product A?\"\n}","section_id":"quick-example"},{"id":"quick-example-ex1","language":"json","code":"POST /v1/caches/{cacheId}/entries\n{\n    \"prompt\": \"What are the features of Product A?\",\n    \"response\": \"Product A includes real-time analytics, automatic scaling, and sub-millisecond latency.\"\n}","section_id":"quick-example"},{"id":"llm-cost-reduction-with-langcache-ex0","language":"bash","code":"Est. monthly savings with LangCache = \n    (Monthly output token costs) × (Cache hit rate)","section_id":"llm-cost-reduction-with-langcache"}]}
{"schema_version":2,"id":"develop/ai/context-engine/langcache/interactive-demo","title":"Redis LangCache interactive demo","url":"https://redis.io/docs/latest/develop/ai/context-engine/langcache/interactive-demo/","summary":"Watch a semantic cache answer customers' repeat questions, tune its similarity threshold, and scope answers with attributes.","tags":["docs","develop","ai"],"last_updated":"2026-10-01T09:15:22+02:00","page_type":"content","content_hash":"251f72874b11a377cb60f672cc8b7c8744d983db8ce7d7241dc746f4687e716a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis LangCache stores the answers your LLM generates and returns them when someone asks a similar question, so your app doesn't call the LLM again. This demo follows the help assistant of a food delivery app. You choose which questions its customers ask, and the demo shows each search, each cache hit or miss, and what the cache stores.\n\nThe demo runs in your browser and doesn't connect to a LangCache service or an LLM. Its requests and responses follow the shapes of the [LangCache API](https://redis.io/docs/latest/develop/ai/context-engine/langcache/api-reference). The similarity scores, answers, and timings are illustrative. The customers and restaurants come from the same sample data as the [Context Retriever interactive demo](https://redis.io/docs/latest/develop/ai/context-engine/context-retriever/interactive-demo)."},{"id":"what-the-demo-shows","title":"What the demo shows","role":"content","text":"1. **Hits and misses.** The app searches the cache before it calls the LLM. A miss calls the LLM and stores the answer, so the next similar question is a hit.\n2. **Similarity threshold.** A search is a hit when a cached prompt is at least as similar as the threshold. A higher threshold gives fewer wrong answers and more LLM calls. A lower threshold does the opposite.\n3. **Attributes.** Attributes scope searches and deletes. A restaurant attribute keeps one restaurant's answers from reaching another restaurant's customers, and lets the app delete a restaurant's answers when its menu changes."},{"id":"next-steps","title":"Next steps","role":"content","text":"- Read [LangCache concepts](https://redis.io/docs/latest/develop/ai/context-engine/langcache/concepts) to learn how to choose a similarity threshold.\n- [Create a LangCache service](https://redis.io/docs/latest/operate/iris/langcache/create-service) on Redis Cloud.\n- Use the [LangCache API and SDK](https://redis.io/docs/latest/develop/ai/context-engine/langcache/api-examples) to search and store entries from your app."}],"examples":[]}
{"schema_version":2,"id":"develop/ai/ecosystem-integrations","title":"Redis AI ecosystem integrations","url":"https://redis.io/docs/latest/develop/ai/ecosystem-integrations/","summary":"Explore key integrations between Redis and popular AI frameworks, platforms, and tools.","tags":["docs","develop","ai"],"last_updated":"2026-09-15T16:01:02+08:00","page_type":"content","content_hash":"9bac8cf510809fb143f977d9d334b6f031dd9d88d9908c3d81a08e93b2df9550","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis integrates with a wide range of AI frameworks, platforms, and tools to enhance your AI applications. This page highlights key ecosystem integrations that can help you build more powerful, efficient, and scalable AI solutions with Redis.\n\n| | | |\n|---|---|---|\n| [**Kong AI Gateway & Redis**](https://redis.io/blog/kong-ai-gateway-and-redis/) | [**Unstructured & Redis**](https://redis.io/blog/faster-ai-workflows-with-unstructured-redis/) | [**Mem0 & Redis**](https://redis.io/blog/smarter-memory-management-for-ai-agents-with-mem0-and-redis/) |\n| Combine Kong's AI Gateway with Redis for efficient AI request routing, caching, and rate limiting to optimize your AI infrastructure. | Accelerate AI workflows by using Redis to cache document processing results from Unstructured, reducing processing time and costs. | Implement smarter memory management for AI agents with Mem0's integration with Redis, providing persistent, queryable memory for LLMs. |\n| [**LiteLLM & Redis**](https://docs.litellm.ai/docs/caching/all_caches#initialize-cache---in-memory-redis-s3-bucket-redis-semantic-disk-cache-qdrant-semantic) | [**LangGraph & Redis**](https://redis.io/blog/langgraph-redis-build-smarter-ai-agents-with-memory-persistence/) | [**LangChain & Redis**](https://redis.io/blog/langchain-redis-partner-package/) |\n| Optimize LLM performance with LiteLLM's Redis caching capabilities, supporting both traditional and semantic caching to reduce costs and latency. | Build smarter AI agents with LangGraph's Redis integration for memory persistence, state management, and multi-agent coordination. | Leverage LangChain's Redis integration for vector storage, memory, and caching to create more capable AI applications with improved performance. |\n| [**LlamaIndex & Redis**](https://developers.llamaindex.ai/python/examples/vector_stores/redisindexdemo/) | [**Amazon Bedrock & Redis**](https://redis.io/docs/latest/integrate/amazon-bedrock/set-up-redis) | [**NVIDIA NIM & Redis**](https://redis.io/blog/use-redis-with-nvidia-nim-to-deploy-genai-apps-faster/) |\n| Use LlamaIndex with Redis as a vector store for efficient document indexing, retrieval, and querying in RAG applications. | Integrate Redis with Amazon Bedrock to enhance your generative AI applications with persistent memory and efficient vector search. | Deploy GenAI applications faster by combining NVIDIA NIM's inference optimization with Redis for vector search, caching, and data management. |"},{"id":"additional-integrations","title":"Additional integrations","role":"content","text":"- [**AISIX AI Gateway & Redis**](https://docs.api7.ai/ai-gateway/traffic-controls/caching): Use Redis to share response cache entries and coordinate [rate-limit counters](https://docs.api7.ai/ai-gateway/traffic-controls/rate-limits) across AISIX gateway instances.\n- [**Microsoft Semantic Kernel**](https://learn.microsoft.com/en-us/semantic-kernel/concepts/vector-store-connectors/out-of-the-box-connectors/redis-connector?pivots=programming-language-csharp): Use Redis as a vector store connector with Microsoft's Semantic Kernel framework.\n- [**DocArray**](https://docs.docarray.org/user_guide/storing/index_redis/): Leverage Redis as a document store and vector database with Jina AI's DocArray.\n- [**Redis Cloud on Vercel**](https://redis.io/blog/redis-cloud-now-available-on-vercel-marketplace/): Deploy and manage Redis databases directly from your Vercel dashboard with the Redis Cloud integration. Refer to the [setup guide](https://redis.io/docs/latest/operate/rc/cloud-integrations/vercel) for more details.\n- [**Spring AI & Redis**](https://redis.io/blog/building-a-rag-application-with-redis-and-spring-ai/): Build powerful RAG applications by combining Spring AI's framework with Redis for vector storage and retrieval.\n- [**Kernel Memory & Redis**](https://redis.io/blog/building-llm-applications-with-kernel-memory-and-redis/): Create memory-enabled LLM applications using Microsoft's Kernel Memory with Redis for efficient storage and retrieval."},{"id":"getting-started","title":"Getting started","role":"setup","text":"To learn more about using Redis with AI frameworks and tools, check out our:\n\n* [AI notebooks collection](https://redis.io/docs/latest/develop/ai/notebook-collection) - Interactive code examples and tutorials\n* [AI video collection](https://redis.io/docs/latest/develop/ai/ai-videos) - Video tutorials and demonstrations\n* [RAG quickstart guide](https://redis.io/docs/latest/develop/get-started/rag) - Get started with Retrieval-Augmented Generation"}],"examples":[]}
{"schema_version":2,"id":"develop/ai/featureform/concepts","title":"Redis Feature Form concepts","url":"https://redis.io/docs/latest/develop/ai/featureform/concepts/","summary":"Learn the workspace, resource graph, provider, secret, and serving model behind Redis Feature Form.","aliases":["/develop/ai/featureform/training-sets-and-feature-views/"],"tags":[],"last_updated":"2026-09-11T15:54:40+01:00","page_type":"content","content_hash":"52ce15a50df40540b2eb8fd50f99f4f980fcf2e35ff8d8beb7242821d31b7dc2","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Feature Form is a feature platform. It turns raw data from your existing systems into the values your models read at inference time. This page introduces the core concepts behind that workflow."},{"id":"how-the-pieces-fit-together","title":"How the pieces fit together","role":"content","text":"A Feature Form deployment runs one or more [workspaces](#workspaces). Each workspace owns a versioned [resource graph](#the-resource-graph) that describes what features should exist, where their inputs live, and how they're served. You author that graph in a Python [definitions file](#definitions-files-and-ff-apply) and apply it with `ff apply`. The `ff` command-line tool also handles workspaces, providers, RBAC, and serving operations.\n\nThe graph itself is data, not credentials or connections. [Providers](#providers) connect the workspace to external systems (Postgres, Redis, S3, Spark, an Iceberg catalog), and [secret references](#secrets-and-secret-references) point to the backend that holds the credentials. At the end of the chain, a [feature view](#feature-views-and-serving) is the single resource the rest of your stack reads from to serve features online."},{"id":"workspaces","title":"Workspaces","role":"content","text":"A workspace is a self-contained environment in Feature Form. Each one owns its own resource graph, providers, secret references, and serving metadata. Nothing is shared between workspaces.\n\nUse workspaces to keep environments such as dev, staging, and prod separate, or to give independent teams their own area on a shared deployment. Conceptually, a workspace is a single tenant in a multi-tenant Feature Form deployment — providers, secret references, RBAC bindings, and graph state are all scoped per workspace, even though they share the underlying server. Two workspaces can connect to the same external Postgres database and remain fully isolated, because each workspace tracks its own resources.\n\nEvery workspace also has a `last_applied_version` counter that increases each time you successfully apply a change. Read commands always return the latest committed version.\n\nTo create, inspect, update, or delete workspaces, see [Manage workspaces](https://redis.io/docs/latest/develop/ai/featureform/manage-workspace)."},{"id":"the-resource-graph","title":"The resource graph","role":"content","text":"The resource graph describes what a workspace should look like. Every feature, label, transformation, dataset, and feature view lives in this graph and refers to the others by name.\n\nTwo properties shape how you work with it:\n\n- It is versioned as a whole. Each successful change creates a new version of the entire graph. Either every resource in the change lands together, or nothing does — you never end up with half-applied feature definitions.\n- It is declarative. You describe what the graph should look like, not the steps to get there. Feature Form figures out the difference between the current graph and the new one and applies only what changed.\n\nWhen `ff apply` accepts a change, Feature Form's planner converts the desired graph into a task DAG that runs against your data infrastructure — materializing new datasets, executing transformations, and populating the online store. The graph is the logical specification; the task DAG is the executed plan."},{"id":"resource-types","title":"Resource types","role":"content","text":"A graph is built from seven resource types. New users encountering Feature Form for the first time benefit from learning these as a vocabulary list — every other concept on this page builds on them.\n\n- **Entities** identify the real-world objects features describe, such as a `customer` or `order`. Other resources join on the entity's primary key — the actual column name can vary across tables (`customer_id`, `user_id`, `id`), as long as the values identify the same business entity.\n- **Datasets** point at an existing table, view, or file in an offline store and make it visible to the graph. The data itself stays where it lives; Feature Form just registers a handle to it.\n- **Transformations** produce new datasets from existing ones, expressed as SQL or as a Spark job. A transformation describes the shape of the output; the compute that runs it is supplied by a provider.\n- **Features** are named, measurable characteristics of an entity that a model reads as input at inference time. A feature attaches to a column of a dataset (or transformation), optionally applies an aggregation (such as `SUM` over a 7-day window), and declares which provider owns its computation.\n- **Labels** are the target value a model is trained to predict — the known outcome attached to each training example. They share the entity-keyed shape of features but feed offline training rather than online serving.\n- **Training sets** join one or more features with a label by entity key and time, so an offline training job reads a single point-in-time-correct table instead of stitching inputs together by hand. The point-in-time alignment prevents data leakage — features whose values weren't actually known at the label's timestamp.\n- **Feature views** group related features for an entity behind a shared definition, materialization policy, and serving contract. They're the primary interface model-serving systems use to read feature values at inference time, and the only graph resource downstream applications interact with directly.\n\nThe following example definitions file shows how the vocabulary above appears as code.\n\n[code example]"},{"id":"definitions-files-and-ff-apply","title":"Definitions files and `ff apply`","role":"content","text":"The Python definitions file is the source of truth for what the graph should look like. The file uses Python to declare resources, not to run commands against Feature Form. When you run `ff apply`, Feature Form imports the file, collects those resources, and treats them as the workspace's desired state. Feature Form compares that set with the current graph and, if the change is accepted, commits a new graph version.\n\nBy default, `ff apply` replaces the workspace's current graph with the resources defined in the file. Any existing resource not in the file becomes a candidate for removal. To apply a partial set and leave missing resources untouched, run `ff apply --merge` instead.\n\n> [!NOTE]\n> Definitions files describe features, not infrastructure. Providers and secret backends are registered separately by a workspace admin. Definitions files reference providers by name and assume they already exist. This separation keeps feature authors away from credentials and infrastructure choices.\n\nFor an end-to-end walkthrough of authoring a definitions file and applying it, see the [Quickstart](https://redis.io/docs/latest/develop/ai/featureform/quickstart). For the full apply lifecycle and editing loop, see [Define and deploy features](https://redis.io/docs/latest/develop/ai/featureform/define-and-deploy-features) and [Update features](https://redis.io/docs/latest/develop/ai/featureform/update-features)."},{"id":"secrets-and-secret-references","title":"Secrets and secret references","role":"content","text":"Feature Form never stores credentials in any form — not plaintext, not hashed, not encrypted. A provider configuration carries a secret reference that points at a separately-registered secret backend, and Feature Form resolves the reference at the moment the credential is needed. Because of this, secret providers are registered first — every provider that follows points back at one through its secret reference.\n\nKeeping credentials out of the graph has two important consequences:\n\n- The graph is safe to inspect and export. Nothing in it contains a usable credential. You can hand the graph to another team, version it, or paste it into a ticket without leaking secrets.\n- The process that resolves a reference is whichever process actually needs the credential. In a deployed environment, that's almost always the Feature Form server, not your local CLI shell. A reference such as `env:PG_PASSWORD` reads from the server's process environment, not yours.\n\nEvery new workspace is created with a built-in `env` secret provider, which makes `env:` references work out of the box for local development. Production deployments typically register a Vault, Kubernetes-secrets, or AWS Secrets Manager backend instead, because the `env` backend offers no rotation, no audit, and exposes values in process listings.\n\nTo register a secret provider for a workspace, see [Configure secret providers](https://redis.io/docs/latest/develop/ai/featureform/register-providers#configure-secret-providers)."},{"id":"providers","title":"Providers","role":"content","text":"A provider is the workspace's connection to an external system. It carries the host, port, credentials reference, and any configuration Feature Form needs to talk to that system. Resources in the graph reference providers by name, so you must register a provider in the workspace before applying any resource that uses it.\n\nEvery provider fills one or more roles, which describe the kind of work it can do for the workspace:\n\n| Role            | What it does                                                            |\n| --------------- | ----------------------------------------------------------------------- |\n| `offline-store` | Holds batch data and materialized datasets the graph reads from.        |\n| `online-store`  | Serves materialized feature values to applications at low latency.      |\n| `compute`       | Runs transformations and materialization jobs.                          |\n| `streaming`     | Connects the workspace to streaming sources.                            |\n\nOne provider often fills more than one role. Postgres, for example, is commonly registered as both `offline-store` and `compute` because the same instance that holds datasets can run SQL transformations against them. The documented integrations and their typical roles:\n\n| Provider          | Typical roles                  |\n| ----------------- | ------------------------------ |\n| Postgres          | `offline-store`, `compute`     |\n| Redis             | `online-store`                 |\n| S3                | `offline-store`                |\n| Spark             | `compute`                      |\n| Iceberg catalog   | `offline-store`                |\n\nThe role model is what lets a graph stay portable: a feature definition doesn't care that compute happens to be Postgres in dev and Spark in prod, only that some provider fills the `compute` role.\n\nTo register providers in a workspace, see [Register providers](https://redis.io/docs/latest/develop/ai/featureform/register-providers)."},{"id":"feature-views-and-serving","title":"Feature views and serving","role":"content","text":"A feature view is the resource that everything else in the graph eventually feeds. Applications query it to get the latest features for an entity.\n\nA feature view declares:\n\n- The entity used as the lookup key (for example, `customer`).\n- The list of features it exposes.\n- The online provider that holds the materialized values — typically Redis.\n- A materialization engine that produces those values from offline data.\n\nFor example:\n\n[code example]"},{"id":"feature-view-requirements","title":"Feature view requirements","role":"content","text":"Before applications can read from a feature view:\n\n- The online provider it points to must be registered and reachable.\n- The graph version that introduced the feature view must be committed.\n- Materialization must have populated values for the entities you want to query.\n\nIf any of those are missing, the read fails immediately rather than returning stale data."},{"id":"serving-interfaces","title":"Serving interfaces","role":"content","text":"Applications can read feature values through any of three interfaces:\n\n- A gRPC service (`ServingService.Serve` and `ServingService.GetServingMetadata`).\n- A REST endpoint (`POST /api/v1/serve`).\n- A Python client (`client.serve(...)`).\n\n> [!NOTE]\n> Reading feature values and reading serving metadata are governed by separate RBAC permissions. For example, a dashboard user can have access to feature view schemas without access to the actual values — or vice versa.\n\nTo serve from a feature view in an application, see [Serve features](https://redis.io/docs/latest/develop/ai/featureform/serve-features). To inspect datasets, training sets, or feature views directly, see [Query data](https://redis.io/docs/latest/develop/ai/featureform/query-data)."},{"id":"next-steps","title":"Next steps","role":"content","text":"- [Quickstart](https://redis.io/docs/latest/develop/ai/featureform/quickstart) — one end-to-end walkthrough that exercises every concept on this page.\n- [Manage workspaces](https://redis.io/docs/latest/develop/ai/featureform/manage-workspace) — create, inspect, update, and delete workspaces.\n- [Register providers](https://redis.io/docs/latest/develop/ai/featureform/register-providers) — connect the workspace to Postgres, Redis, S3, Spark, or an Iceberg catalog, and register secret backends.\n- [Define and deploy features](https://redis.io/docs/latest/develop/ai/featureform/define-and-deploy-features) — author a definitions file and run `ff apply`.\n- [Update features](https://redis.io/docs/latest/develop/ai/featureform/update-features) — iterate on a graph after the first apply.\n- [Serve features](https://redis.io/docs/latest/develop/ai/featureform/serve-features) — read from a feature view in an application.\n- [Query data](https://redis.io/docs/latest/develop/ai/featureform/query-data) — inspect datasets, training sets, and feature views directly."}],"examples":[{"id":"resource-types-ex0","language":"python","code":"import featureform as ff\nfrom datetime import timedelta\n\ncustomer = ff.Entity(name=\"customer\")            # Entity: the real-world object features describe\n\ntransactions = postgres.dataset(                 # Dataset: registers an existing table with the graph\n    name=\"transactions_raw\",\n    table=\"transactions\",\n    timestamp_column=\"timestamp\",\n)\n\n# Transformation: derives a new dataset from existing ones via SQL\n@postgres.sql_transformation(name=\"customer_daily_rollups\", inputs=[transactions])\ndef customer_daily_rollups() -> str:\n    return \"\"\"\n        SELECT customer_id,\n               date_trunc('day', timestamp) AS event_day,\n               SUM(transaction_amount) AS total_amount\n        FROM {{transactions_raw}}\n        GROUP BY 1, 2\n    \"\"\"\n\n# Feature: an aggregated value served to models at inference time\ncustomer_total_amount_7d = (\n    ff.Feature(name=\"customer_total_amount_7d\")\n    .from_dataset(customer_daily_rollups, entity=\"customer\",\n                  entity_column=\"customer_id\", value=\"total_amount\",\n                  timestamp=\"event_day\")\n    .aggregate(function=ff.AggregateFunction.SUM, window=timedelta(days=7))\n)\n\ncustomer_risk_view = ff.FeatureView(             # Feature view: groups features behind a serving contract\n    name=\"customer_risk_feature_view\",\n    entity=\"customer\",\n    features=[customer_total_amount_7d],\n    inference_store=\"demo_redis\",\n)","section_id":"resource-types"},{"id":"feature-views-and-serving-ex0","language":"python","code":"customer_risk_view = ff.FeatureView(\n    name=\"customer_risk_feature_view\",\n    entity=\"customer\",\n    features=[customer_total_amount_7d, customer_transaction_count_7d],\n    inference_store=\"demo_redis\",\n)","section_id":"feature-views-and-serving"}]}
{"schema_version":2,"id":"develop/ai/featureform/define-and-deploy-features","title":"Define and deploy features","url":"https://redis.io/docs/latest/develop/ai/featureform/define-and-deploy-features/","summary":"Author a Python definitions file and apply it to a Redis Feature Form workspace.","aliases":["/develop/ai/featureform/features-and-labels/","/develop/ai/featureform/providers/"],"tags":[],"last_updated":"2026-09-11T15:54:40+01:00","page_type":"content","content_hash":"8181581c71c9d693d036179b0c1445e9848b305a1859da2941aa20aa4402db6c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"A feature workflow starts with a Python [definitions file](https://redis.io/docs/latest/develop/ai/featureform/concepts#definitions-files-and-ff-apply). This file declares the [entities, datasets, transformations, features, labels, training sets, and feature views](https://redis.io/docs/latest/develop/ai/featureform/concepts#resource-types) you want in a [workspace](https://redis.io/docs/latest/develop/ai/featureform/concepts#workspaces). Run `ff apply` to submit that file as the workspace's [desired state](https://redis.io/docs/latest/develop/ai/featureform/concepts#the-resource-graph). Feature Form compares the file with the workspace's current resource graph and applies only the differences. If Feature Form accepts the change, it commits a new graph version. The model is declarative — the file describes the end state, not the steps to get there. Re-applying the same file leaves the workspace unchanged."},{"id":"author-a-definitions-file","title":"Author a definitions file","role":"security","text":"Redis Feature Form treats a Python definitions file as the source of a desired resource graph. The example below declares one complete workflow end to end: a Postgres dataset, a SQL rollup transformation, a feature with a 7-day aggregation, and a Redis-backed feature view.\n\n[code example]"},{"id":"typical-file-structure","title":"Typical file structure","role":"content","text":"A definitions file typically declares resources in this order:\n\n1. **Import the module** with `import featureform as ff`, which exposes the resource builders and provider helpers.\n2. **[Entities](https://redis.io/docs/latest/develop/ai/featureform/concepts#resource-types)** — identify the real-world objects features describe, such as `customer` or `order`. Other resources join on the entity's key column.\n3. **[Datasets](https://redis.io/docs/latest/develop/ai/featureform/concepts#resource-types)** — point at an existing table, view, or file on an offline store. The data remains in its original location.\n4. **[Transformations](https://redis.io/docs/latest/develop/ai/featureform/concepts#resource-types)** — produce new datasets from existing ones, expressed as SQL or as a Spark job.\n5. **[Features](https://redis.io/docs/latest/develop/ai/featureform/concepts#resource-types) and [labels](https://redis.io/docs/latest/develop/ai/featureform/concepts#resource-types)** — entity-keyed values served at inference time (features) and used as the prediction target offline (labels).\n6. **[Training sets](https://redis.io/docs/latest/develop/ai/featureform/concepts#resource-types) and [feature views](https://redis.io/docs/latest/develop/ai/featureform/concepts#feature-views-and-serving)** — join features with a label on the entity key (training set) and expose features for online serving (feature view).\n7. **Export a `resources = [...]` list** that names every resource above. See [Definitions files and `ff apply`](https://redis.io/docs/latest/develop/ai/featureform/concepts#definitions-files-and-ff-apply) for how the loader uses it.\n\n> [!NOTE]\n> If your file doesn't export `resources = [...]`, `ff apply` falls back to its auto-registration registry. Prefer the explicit list during onboarding; it's easier to reason about and is what the [Quickstart](https://redis.io/docs/latest/develop/ai/featureform/quickstart) uses."},{"id":"the-file-should-reference","title":"The file should reference","role":"content","text":"- Registered [provider names](https://redis.io/docs/latest/develop/ai/featureform/register-providers) such as `demo_postgres` and `demo_redis`. Providers must already exist in the workspace before they're referenced.\n- [Secret references](https://redis.io/docs/latest/develop/ai/featureform/concepts#secrets-and-secret-references) such as `env:PG_PASSWORD`, resolved at runtime by a [secret provider](https://redis.io/docs/latest/develop/ai/featureform/register-providers#configure-secret-providers) registered in the workspace.\n- Stable resource names that make sense across re-apply cycles, since the graph compares the file to current state by name."},{"id":"the-file-should-not-do","title":"The file should not do","role":"content","text":"- Don't replace provider registration.\n- Don't assume providers exist before the workspace registers them.\n- Don't mix infrastructure provisioning into the definitions entrypoint."},{"id":"apply-a-definitions-file","title":"Apply a definitions file","role":"content","text":"`ff apply` reads the file you pass with `--file` and submits its resources to a workspace. `--file` accepts a Python file, a package `__init__.py`, or a package directory containing one.\n\nGet the workspace ID with `ff workspace list`. See [Manage workspaces](https://redis.io/docs/latest/develop/ai/featureform/manage-workspace) for the full workspace lifecycle."},{"id":"preview-with---plan","title":"Preview with `--plan`","role":"content","text":"Preview the change without applying it:\n\n[code example]\n\nUse this before large changes or whenever the file might be incomplete relative to the workspace's full desired state."},{"id":"standard-apply","title":"Standard apply","role":"content","text":"Apply the file and wait for it to finish:\n\n[code example]\n\nWithout `--wait`, `ff apply` returns as soon as the server accepts the request and runs the job asynchronously. `--wait` blocks until the job reaches a target state: `--wait-for finished` waits for terminal success; `--wait-for running` returns as soon as the job is actively running.\n\nIf you skip `--wait`, the response includes a job ID. Check the job's status and per-task progress with:\n\n[code example]"},{"id":"apply-modes","title":"Apply modes","role":"content","text":"- **Default apply** replaces the workspace's current resource graph with the file.\n- **`--merge`** applies a partial definition file without treating omitted resources as deletions.\n- **`--update`** is an advanced scheduler-backed mode that re-runs supported resources' normal update or incremental path, even when the graph definition is unchanged.\n- **`--full-rematerialize`** is an advanced scheduler-backed mode that forces full-refresh behavior on supported materialized resources.\n\nUse only one of `--merge`, `--update`, or `--full-rematerialize` at a time. Support for `--update` and `--full-rematerialize` is resource-family dependent — run `--plan` first to inspect the planned job, and pair them with `--wait` to see the outcome."},{"id":"verify-the-apply","title":"Verify the apply","role":"content","text":"After apply finishes, confirm the change with:\n\n[code example]\n\nThe graph commands show the resources Feature Form recognizes; `ff catalog` shows where each materialized resource physically landed. See [Query data](https://redis.io/docs/latest/develop/ai/featureform/query-data) for more inspection options."},{"id":"if-apply-fails","title":"If apply fails","role":"content","text":"Common reasons:\n\n- **Provider not registered.** A resource references a provider name the workspace doesn't know. Confirm with `ff provider list --workspace <workspace-id>` and register the missing provider per [Register providers](https://redis.io/docs/latest/develop/ai/featureform/register-providers).\n- **Secret can't be resolved.** A provider config uses a reference such as `env:PG_PASSWORD`, but the Feature Form server's environment doesn't expose that variable. Check the secret provider with `ff secret-provider get env --workspace <workspace-id>`.\n- **No resources to apply.** The entrypoint produced no resources. Make sure your file exports a `resources = [...]` list, or that auto-registration finds the resources you declared.\n- **Validation error.** The CLI prints the specific resource and field that failed; fix the file and re-run with `--plan`.\n\nFor more detail, re-run with `--verbose` to enable debug logging to stderr. For most failures, though, the apply job itself surfaces clearer errors than the debug log — let `--wait` finish, or run `ff scheduler job get <job-id>`, before reaching for `--verbose`."}],"examples":[{"id":"author-a-definitions-file-ex0","language":"python","code":"import featureform as ff\nfrom datetime import timedelta\n\npostgres = ff.get_postgres(\"demo_postgres\")      # Look up a provider already registered in the workspace\n\ncustomer = ff.Entity(name=\"customer\")            # Entity: the real-world object features describe\n\ntransactions = postgres.dataset(                 # Dataset: registers an existing table with the graph\n    name=\"transactions_raw\",\n    table=\"transactions\",\n    timestamp_column=\"timestamp\",\n)\n\n# Transformation: derives a new dataset from existing ones via SQL\n@postgres.sql_transformation(name=\"customer_daily_rollups\", inputs=[transactions])\ndef customer_daily_rollups() -> str:\n    return \"\"\"\n        SELECT customer_id,\n               date_trunc('day', timestamp) AS event_day,\n               SUM(transaction_amount) AS total_amount\n        FROM {{transactions_raw}}\n        GROUP BY 1, 2\n    \"\"\"\n\n# Feature: an aggregated value served to models at inference time\ncustomer_total_amount_7d = (\n    ff.Feature(name=\"customer_total_amount_7d\")\n    .from_dataset(customer_daily_rollups, entity=\"customer\",\n                  entity_column=\"customer_id\", value=\"total_amount\",\n                  timestamp=\"event_day\")\n    .aggregate(function=ff.AggregateFunction.SUM, window=timedelta(days=7))\n)\n\ncustomer_risk_view = ff.FeatureView(             # Feature view: groups features behind a serving contract\n    name=\"customer_risk_feature_view\",\n    entity=\"customer\",\n    features=[customer_total_amount_7d],\n    inference_store=\"demo_redis\",\n)\n\n# Export every resource so ff apply registers them as the desired graph\nresources = [\n    customer,\n    transactions,\n    customer_daily_rollups,\n    customer_total_amount_7d,\n    customer_risk_view,\n]","section_id":"author-a-definitions-file"},{"id":"preview-with---plan-ex0","language":"bash","code":"ff apply \\\n  --workspace <workspace-id> \\\n  --file examples/featureform/docs/resources.py \\\n  --plan","section_id":"preview-with---plan"},{"id":"standard-apply-ex0","language":"bash","code":"ff apply \\\n  --workspace <workspace-id> \\\n  --file examples/featureform/docs/resources.py \\\n  --wait \\\n  --wait-for finished","section_id":"standard-apply"},{"id":"standard-apply-ex1","language":"bash","code":"ff scheduler job get <job-id>","section_id":"standard-apply"},{"id":"verify-the-apply-ex0","language":"bash","code":"ff graph workspace stats --workspace <workspace-id>\nff graph feature list --workspace <workspace-id>\nff catalog list --workspace <workspace-id>","section_id":"verify-the-apply"}]}
{"schema_version":2,"id":"develop/ai/featureform","title":"Redis Feature Form","url":"https://redis.io/docs/latest/develop/ai/featureform/","summary":"Build feature engineering workflows with Redis Feature Form.","tags":["docs","develop","ai"],"last_updated":"2026-09-24T18:15:36-07:00","children":[{"id":"develop/ai/featureform/overview","summary":"Learn what Redis Feature Form is, who it is for, and how it fits into Redis-based ML workflows.","title":"Redis Feature Form overview","url":"https://redis.io/docs/latest/develop/ai/featureform/overview/"},{"id":"develop/ai/featureform/manage-workspace","summary":"Create, verify access to, monitor, and delete Redis Feature Form workspaces with the ff CLI.","title":"Manage workspaces","url":"https://redis.io/docs/latest/develop/ai/featureform/manage-workspace/"},{"id":"develop/ai/featureform/quickstart","summary":"Register providers, define a feature, materialize it to Redis, and serve it.","title":"Redis Feature Form quickstart","url":"https://redis.io/docs/latest/develop/ai/featureform/quickstart/"},{"id":"develop/ai/featureform/concepts","summary":"Learn the workspace, resource graph, provider, secret, and serving model behind Redis Feature Form.","title":"Redis Feature Form concepts","url":"https://redis.io/docs/latest/develop/ai/featureform/concepts/"},{"id":"develop/ai/featureform/register-providers","summary":"Register storage, compute, catalog, and secret providers in a Redis Feature Form workspace.","title":"Register providers","url":"https://redis.io/docs/latest/develop/ai/featureform/register-providers/"},{"id":"develop/ai/featureform/define-and-deploy-features","summary":"Author a Python definitions file and apply it to a Redis Feature Form workspace.","title":"Define and deploy features","url":"https://redis.io/docs/latest/develop/ai/featureform/define-and-deploy-features/"},{"id":"develop/ai/featureform/update-features","summary":"Iterate on a Redis Feature Form definitions file with plan, merge, and re-apply workflows.","title":"Update features","url":"https://redis.io/docs/latest/develop/ai/featureform/update-features/"},{"id":"develop/ai/featureform/serve-features","summary":"Read materialized features from a Redis Feature Form feature view at inference time.","title":"Serve features","url":"https://redis.io/docs/latest/develop/ai/featureform/serve-features/"},{"id":"develop/ai/featureform/query-data","summary":"Inspect the Redis Feature Form catalog and query datasets, training sets, and feature views with the ff CLI.","title":"Query data","url":"https://redis.io/docs/latest/develop/ai/featureform/query-data/"},{"id":"develop/ai/featureform/reference","summary":"Reference data for the ff CLI, Python client, gRPC API, and RBAC permissions.","title":"Redis Feature Form reference","url":"https://redis.io/docs/latest/develop/ai/featureform/reference/"}],"page_type":"content","content_hash":"5b8fb5a51fdb75d25adb6ee0a30673510f5abfb781e530a4ca989929cba77d21","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Define, manage, and serve machine learning features on top of your existing data systems, without replacing them.\n\nRedis Feature Form lets you register your data providers, define features as Python transformations, materialize them on a schedule, and serve them at low latency from Redis. Your existing databases stay in place; Feature Form coordinates the pipeline.\n\n<div class=\"grid grid-cols-1 md:grid-cols-3 gap-6 my-8\">\n  \n  \n  \n</div>"},{"id":"what-is-redis-feature-form","title":"What is Redis Feature Form?","role":"content","text":"Redis Feature Form is a feature platform for machine learning teams that:\n\n- **Works with your existing data**: Register your databases, data warehouses, and streams as providers; no migration required\n- **Defines features as code**: Write transformations in Python and version them alongside your model code\n- **Materializes on demand**: Push computed features to Redis for sub-millisecond online serving\n- **Serves training and inference from the same definitions**: Training sets and online feature views share one source of truth\n- **Supports streaming features**: Ingest real-time events and serve up-to-the-second feature values"},{"id":"why-use-redis-feature-form","title":"Why use Redis Feature Form?","role":"content","text":"<div class=\"grid grid-cols-1 md:grid-cols-2 gap-6 my-6\">\n  <div class=\"p-5 border border-redis-pen-300 rounded-lg\">\n    <h3 class=\"text-redis-ink-900 font-semibold mb-3\">For ML teams</h3>\n    <ul class=\"space-y-1 text-redis-pen-600\">\n      <li>Consistent features between training and serving: no training-serving skew</li>\n      <li>Version and audit feature definitions alongside model code</li>\n      <li>Reuse features across multiple models and teams</li>\n      <li>Streaming support for time-sensitive predictions</li>\n    </ul>\n  </div>\n  <div class=\"p-5 border border-redis-pen-300 rounded-lg\">\n    <h3 class=\"text-redis-ink-900 font-semibold mb-3\">For developers</h3>\n    <ul class=\"space-y-1 text-redis-pen-600\">\n      <li>Python SDK for defining features, labels, training sets, and providers</li>\n      <li>Redis as the online store: sub-millisecond feature serving at scale</li>\n      <li>No need to replace existing data systems: register them as providers</li>\n    </ul>\n  </div>\n</div>\n\nRedis Feature Form helps teams define, manage, materialize, and serve machine learning features while keeping existing data systems in place. In the documented workflow, Redis acts as the low-latency online store for feature serving.\n\nThis documentation describes platform setup, workspace access, provider and secret registration, definitions-file authoring, apply, and serving. Refer to [Deploy](https://redis.io/docs/latest/operate/featureform) for installation and authentication instructions."}],"examples":[]}
{"schema_version":2,"id":"develop/ai/featureform/manage-workspace","title":"Manage workspaces","url":"https://redis.io/docs/latest/develop/ai/featureform/manage-workspace/","summary":"Create, verify access to, monitor, and delete Redis Feature Form workspaces with the ff CLI.","tags":[],"last_updated":"2026-09-11T15:54:40+01:00","page_type":"content","content_hash":"a426063f16a9ab777d04b56766fb936bf2eb3d93b41478de3c00cbbb5019b2c0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"A workspace is a self-contained environment in Redis Feature Form. Each workspace owns its own resource graph, providers, secret references, catalog entries, and serving metadata—nothing is shared between workspaces. Use workspaces to keep environments such as dev, staging, and prod separate, or to give independent teams their own isolated area on a shared deployment.\n\nEach workspace has:\n\n- A unique name and an optional description.\n- A `last_applied_version` that tracks the most recently applied resource graph.\n- A built-in `env` secret provider, created automatically.\n\nThe tasks on this page require one of two roles:\n\n- A global admin (`global_admin`) creates workspaces and grants access.\n- A workspace admin (`workspace_admin`) verifies their access, runs health checks, and updates or deletes the workspace.\n\nFor the full list of built-in roles and the scope model, see [Authentication and RBAC](https://redis.io/docs/latest/operate/featureform/configure-auth).\n\nThis page covers the `ff` CLI. The Feature Form dashboard supports the same workspace operations with a UI when you prefer.\n\nMost commands on this page identify a workspace by its UUID, either as a positional argument or via the `--workspace` flag. Find the UUID with `ff workspace create` or `ff workspace list`. The examples below use `<workspace-id>`, substitute the actual UUID."},{"id":"create-a-workspace-and-grant-access","title":"Create a workspace and grant access","role":"content","text":"A global admin creates a workspace and hands it off to the workspace admin who will manage it."},{"id":"1-create-the-workspace","title":"1. Create the workspace","role":"content","text":"[code example]\n\nThe command returns a table with the new workspace's ID, name, description, version, and timestamps:\n\n[code example]\n\nSave the ID—you'll need it for the RBAC commands below."},{"id":"2-grant-workspace-admin-access","title":"2. Grant workspace-admin access","role":"content","text":"[code example]\n\nTo bind a group or service account instead, use `--group <group-id>` or `--service-account <service-account-id>`. Exactly one of `--user`, `--group`, or `--service-account` is required."},{"id":"3-verify-the-binding","title":"3. Verify the binding","role":"content","text":"[code example]\n\nYou should see the new role bound to the user, group, or service account you specified. For an alternate view that groups bindings by user, group, or service account instead of one row per binding, use `ff rbac subjects --workspace <workspace-id>`.\n\nCreating a workspace does not automatically grant other users access—each member needs their own binding."},{"id":"confirm-access-to-a-workspace","title":"Confirm access to a workspace","role":"content","text":"Use these checks when a workspace already exists and you need to confirm you can register providers, apply resources, or serve features.\n\n[code example]\n\nYou should see your user, group, or service-account binding listed. If you don't, ask a global admin to grant access using the steps above."},{"id":"check-workspace-health","title":"Check workspace health","role":"content","text":"Run these commands routinely, or whenever something looks wrong, to confirm a workspace is healthy:\n\n[code example]\n\nAlso confirm that your serving and dataframe clients point at the expected transport and state backend."},{"id":"update-a-workspace","title":"Update a workspace","role":"content","text":"Change a workspace's name or description:\n\n[code example]\n\nUpdate affects metadata only—it doesn't touch providers, the resource graph, or catalog entries."},{"id":"delete-a-workspace","title":"Delete a workspace","role":"content","text":"> [!WARNING]\n> Deleting a workspace permanently removes all workspace-scoped data: providers, secret references, the resource graph, catalog entries, and serving metadata. This cannot be undone.\n\n[code example]\n\n`--force` skips the interactive confirmation prompt. Omit it for a safer, interactive delete."},{"id":"troubleshooting","title":"Troubleshooting","role":"errors","text":"- **`permission denied` on provider or apply commands.** Your account is missing workspace write access. Run `ff rbac list --workspace <workspace-id>` to confirm the binding, and ask a global admin to grant the appropriate role if it's missing.\n- **`workspace not found`.** Usually means the wrong deployment, the wrong transport, or a typo in the workspace name. Try `ff workspace list` to see what's actually visible.\n- **Missing workspaces, providers, or resources after apply.** With memory-backed state, gRPC and REST can behave like separate state domains. Check for transport mismatches first, and use durable PostgreSQL-backed state for shared environments."},{"id":"next-steps","title":"Next steps","role":"content","text":"- [Register providers](https://redis.io/docs/latest/develop/ai/featureform/register-providers) to connect a workspace to its storage, compute, and catalog systems.\n- See [Authentication and RBAC](https://redis.io/docs/latest/operate/featureform/configure-auth) for the deployment-wide role and scope model.\n- See [Concepts](https://redis.io/docs/latest/develop/ai/featureform/concepts) for the workspace, resource graph, and serving model."}],"examples":[{"id":"1-create-the-workspace-ex0","language":"bash","code":"ff workspace create demo-workspace \\\n  --description \"Workspace for the feature workflow docs path\"","section_id":"1-create-the-workspace"},{"id":"1-create-the-workspace-ex1","language":"text","code":"ID                                    NAME            DESCRIPTION                              VERSION  CREATED              UPDATED\n7f2e4d8c-3a91-4b6d-9f0a-5e8c1b2d3f4a  demo-workspace  Workspace for the feature workflow ...   0        2026-05-12 14:03:21  2026-05-12 14:03:21","section_id":"1-create-the-workspace"},{"id":"2-grant-workspace-admin-access-ex0","language":"bash","code":"ff rbac grant workspace_admin \\\n  --workspace <workspace-id> \\\n  --user alice@example.com","section_id":"2-grant-workspace-admin-access"},{"id":"3-verify-the-binding-ex0","language":"bash","code":"ff rbac list --workspace <workspace-id>","section_id":"3-verify-the-binding"},{"id":"confirm-access-to-a-workspace-ex0","language":"bash","code":"# Verify your identity.\nff auth whoami\nff rbac whoami\n\n# Confirm the workspace is visible to you.\nff workspace list\nff workspace get --name demo-workspace\n\n# Confirm your binding.\nff rbac list --workspace <workspace-id>","section_id":"confirm-access-to-a-workspace"},{"id":"check-workspace-health-ex0","language":"bash","code":"# Confirm the CLI can reach the deployment.\nff ping\n\n# Inspect workspace metadata, including last_applied_version.\nff workspace get <workspace-id>\n\n# List configured providers and secret providers.\nff provider list --workspace <workspace-id>\nff secret-provider list --workspace <workspace-id>\n\n# Inspect graph overview and stats.\nff graph workspace stats --workspace <workspace-id>\n\n# List catalog locations for materialized resources.\nff catalog list --workspace <workspace-id>","section_id":"check-workspace-health"},{"id":"update-a-workspace-ex0","language":"bash","code":"ff workspace update <workspace-id> \\\n  --name demo-workspace \\\n  --description \"Updated description\"","section_id":"update-a-workspace"},{"id":"delete-a-workspace-ex0","language":"bash","code":"ff workspace delete <workspace-id> --force","section_id":"delete-a-workspace"}]}
{"schema_version":2,"id":"develop/ai/featureform/overview","title":"Redis Feature Form overview","url":"https://redis.io/docs/latest/develop/ai/featureform/overview/","summary":"Learn what Redis Feature Form is, who it is for, and how it fits into Redis-based ML workflows.","tags":[],"last_updated":"2026-09-11T15:54:40+01:00","page_type":"content","content_hash":"9c082e234195e7e3a8ef873886785e4c467d5374eba6ebe895abe061883c78bd","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This page describes the standard Feature Form onboarding path: workspace creation, access handoff, secrets, providers, definitions, apply, and serving. Use it as the high-level sequence before you dive into task-specific guides."},{"id":"audience","title":"Audience","role":"content","text":"- Global admins creating and handing off workspaces\n- Workspace admins wiring secrets and providers\n- Feature engineers authoring definitions files\n- Application or model teams serving ready feature views"},{"id":"recommended-path","title":"Recommended path","role":"content","text":"1. Create the workspace and grant initial access.\n2. Confirm the intended principal can see the workspace.\n3. Reuse the built-in `env` secret provider or register another backend.\n4. Register the providers the workspace will reference.\n5. Author a definitions file and run `ff apply`.\n6. Inspect the graph and catalog, then serve from a feature view."},{"id":"what-to-verify-before-the-first-apply","title":"What to verify before the first apply","role":"content","text":"- The workspace exists and the right principal can access it.\n- Secret references such as `env:PG_PASSWORD` resolve from the runtime environment.\n- Providers are already registered by the names the definitions file expects.\n- The team understands whether the definitions file represents complete desired state or a partial subset."},{"id":"latest-updates","title":"Latest updates","role":"content","text":"The latest release adds enterprise-oriented capabilities:\n\n- **Unified batch and streaming pipelines**: Support for tiling, backfills, and incremental updates reduces custom pipeline work.\n- **Workspaces for multi-tenancy**: Isolate providers, data, authentication, and observability at the workspace level.\n- **Fine-grained job control**: Planning, impact analysis, split materializations, and queue-based job management provide visibility into changes before they affect production systems.\n- **Atomic DAG updates**: Manage graph-level changes atomically instead of versioning individual resources, which simplifies rollback and change history.\n- **Enhanced RBAC and security**: Workspace-scoped access controls, API key pairs, a granular role model, audit logs, secret-provider improvements, mTLS, and encrypted internal transport.\n- **Two-service deployment model**: A simplified deployment architecture that reduces operational complexity.\n- **Redesigned dashboard**: Configure workspaces and providers directly from the UI."},{"id":"what-to-read-next","title":"What to read next","role":"content","text":"- [Quickstart](https://redis.io/docs/latest/develop/ai/featureform/quickstart)\n- [Register providers](https://redis.io/docs/latest/develop/ai/featureform/register-providers)"}],"examples":[]}
{"schema_version":2,"id":"develop/ai/featureform/query-data","title":"Query data","url":"https://redis.io/docs/latest/develop/ai/featureform/query-data/","summary":"Inspect the Redis Feature Form catalog and query datasets, training sets, and feature views with the ff CLI.","tags":[],"last_updated":"2026-07-23T16:30:51-05:00","page_type":"content","content_hash":"bb6d348f908792d37f9e7df7f0a1f01e80b1aa776eb082f3448997b20093f043","sections":[{"id":"overview","title":"Overview","role":"overview","text":"After you apply resources, two commands help you inspect the result: `ff catalog` reports where each resource was materialized, and `ff dataframe query` reads sampled rows from datasets, training sets, and feature views."},{"id":"inspect-materialized-locations","title":"Inspect materialized locations","role":"content","text":"The Feature Form catalog records where each resource is physically materialized. It is distinct from systems such as Unity Catalog, Glue, or an Iceberg catalog. Each catalog entry shows the logical resource name, owning provider, status, physical table/path/namespace, and update timestamps."},{"id":"list-catalog-entries","title":"List catalog entries","role":"content","text":"[code example]"},{"id":"inspect-one-resource","title":"Inspect one resource","role":"content","text":"[code example]\n\nUse the catalog together with graph views: graph explains why a resource exists; catalog shows where it's materialized."},{"id":"query-datasets-training-sets-and-feature-views","title":"Query datasets, training sets, and feature views","role":"content","text":"Use `ff dataframe query` to inspect rows from a dataset, training set, or feature view after it's been applied."},{"id":"query-a-dataset","title":"Query a dataset","role":"content","text":"[code example]"},{"id":"supported-kinds","title":"Supported kinds","role":"compatibility","text":"- `dataset`\n- `training_set`\n- `feature_view`"},{"id":"useful-flags","title":"Useful flags","role":"content","text":"- `--columns`\n- `--filter`\n- `--limit`\n- `--output table|json|csv`\n\nThe dataframe command uses the gRPC Flight endpoint. Connection failures usually indicate transport or endpoint mismatches with the Feature Form server."}],"examples":[{"id":"list-catalog-entries-ex0","language":"bash","code":"ff catalog list --workspace <workspace-id>","section_id":"list-catalog-entries"},{"id":"inspect-one-resource-ex0","language":"bash","code":"ff catalog get demo_transactions --workspace <workspace-id>","section_id":"inspect-one-resource"},{"id":"query-a-dataset-ex0","language":"bash","code":"ff dataframe query demo_transactions \\\n  --workspace <workspace-id> \\\n  --server localhost:9090 \\\n  --kind dataset \\\n  --limit 10 \\\n  --insecure","section_id":"query-a-dataset"}]}
{"schema_version":2,"id":"develop/ai/featureform/quickstart","title":"Redis Feature Form quickstart","url":"https://redis.io/docs/latest/develop/ai/featureform/quickstart/","summary":"Register providers, define a feature, materialize it to Redis, and serve it.","tags":[],"last_updated":"2026-09-11T15:54:40+01:00","page_type":"content","content_hash":"f64aa9897c512850fcf3897d1ffb5f0e03cf3aeb65bcb411037746fe32a4f989","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Feature Form is a feature platform for defining, deploying, and serving machine learning features, with Redis as the low-latency online store at inference time. This quickstart covers one workflow end to end — register providers, apply a definitions file, and read back the result — to confirm Feature Form is set up correctly in your environment."},{"id":"before-you-begin","title":"Before you begin","role":"content","text":"Before you begin, you'll need: \n\n- A running [Feature Form deployment](https://redis.io/docs/latest/operate/featureform/deploy) with durable state\n- An existing [workspace](https://redis.io/docs/latest/develop/ai/featureform/manage-workspace)\n- A working [auth path](https://redis.io/docs/latest/operate/featureform/configure-auth) for `ff`\n- Reachable Postgres and Redis endpoints for your offline-store and online-store [providers](https://redis.io/docs/latest/develop/ai/featureform/register-providers)\n- Sample data loaded that matches the quickstart definitions file"},{"id":"install-the-ff-cli","title":"Install the `ff` CLI","role":"setup","text":"The Feature Form CLI ships as the `redis-featureform` package on PyPI. **Do not run `pip install featureform`** — that's an unrelated upstream project. Install into a virtual environment:\n\n[code example]"},{"id":"1-confirm-identity-and-workspace-access","title":"1. Confirm identity and workspace access","role":"content","text":"If you haven't yet, log in with `ff auth login`. Then:\n\n[code example]\n\nUse the returned workspace ID in later commands."},{"id":"2-confirm-the-built-in-env-secret-provider","title":"2. Confirm the built-in `env` secret provider","role":"content","text":"[code example]\n\nIf a provider uses a secret reference like `env:VAR_NAME`, make sure `VAR_NAME` exists in the runtime environment that resolves secrets."},{"id":"3-register-your-providers","title":"3. Register your providers","role":"content","text":"See [Register providers](https://redis.io/docs/latest/develop/ai/featureform/register-providers) for steps to register the offline and online providers before applying resources."},{"id":"4-review-your-definitions-file","title":"4. Review your definitions file","role":"content","text":"Open the Python definitions file you plan to apply and confirm it declares the resources you want in the workspace. See [Typical file structure](https://redis.io/docs/latest/develop/ai/featureform/define-and-deploy-features#typical-file-structure) for the canonical shape."},{"id":"5-apply-the-file","title":"5. Apply the file","role":"content","text":"[code example]\n\nFor a dry run first:\n\n[code example]"},{"id":"6-inspect-the-results","title":"6. Inspect the results","role":"content","text":"[code example]\n\nYou should see the expected graph entries plus the catalog locations created by the applied resources."},{"id":"7-query-the-materialized-data","title":"7. Query the materialized data","role":"content","text":"Read sampled rows from a dataset, training set, or feature view in your applied graph. Use a resource name from the `ff graph feature list` or `ff catalog list` output in step 6:\n\n[code example]\n\nSee [Query data](https://redis.io/docs/latest/develop/ai/featureform/query-data) for the full set of flags and supported kinds."},{"id":"next-steps","title":"Next steps","role":"content","text":"- [Serve features](https://redis.io/docs/latest/develop/ai/featureform/serve-features) at inference time from a feature view in your graph.\n- [Update features](https://redis.io/docs/latest/develop/ai/featureform/update-features) to iterate on a definitions file you've already applied."}],"examples":[{"id":"install-the-ff-cli-ex0","language":"bash","code":"python3 -m venv .venv && source .venv/bin/activate\npip install redis-featureform\nff --help","section_id":"install-the-ff-cli"},{"id":"1-confirm-identity-and-workspace-access-ex0","language":"bash","code":"ff rbac whoami\nff workspace get --name <workspace-name>","section_id":"1-confirm-identity-and-workspace-access"},{"id":"2-confirm-the-built-in-env-secret-provider-ex0","language":"bash","code":"ff secret-provider list --workspace <workspace-id>","section_id":"2-confirm-the-built-in-env-secret-provider"},{"id":"5-apply-the-file-ex0","language":"bash","code":"ff apply \\\n  --workspace <workspace-id> \\\n  --file path/to/your/definitions.py \\\n  --wait \\\n  --wait-for finished","section_id":"5-apply-the-file"},{"id":"5-apply-the-file-ex1","language":"bash","code":"ff apply \\\n  --workspace <workspace-id> \\\n  --file path/to/your/definitions.py \\\n  --plan","section_id":"5-apply-the-file"},{"id":"6-inspect-the-results-ex0","language":"bash","code":"ff graph workspace stats --workspace <workspace-id>\nff graph feature list --workspace <workspace-id>\nff catalog list --workspace <workspace-id>","section_id":"6-inspect-the-results"},{"id":"7-query-the-materialized-data-ex0","language":"bash","code":"ff dataframe query <resource-name> \\\n  --workspace <workspace-id> \\\n  --kind dataset \\\n  --limit 5 \\\n  --insecure","section_id":"7-query-the-materialized-data"}]}
{"schema_version":2,"id":"develop/ai/featureform/reference","title":"Redis Feature Form reference","url":"https://redis.io/docs/latest/develop/ai/featureform/reference/","summary":"Reference data for the ff CLI, Python client, gRPC API, and RBAC permissions.","aliases":["/develop/ai/featureform/datasets-and-transformations/"],"tags":[],"last_updated":"2026-09-11T15:54:40+01:00","page_type":"content","content_hash":"5546e93436d3adc95ce967e99ebb9dcbaf4a7c359b57cbb8d9a72d424675a962","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This page collects reference data for Redis Feature Form. For help understaind the concepts behind Redis Feature Form, see [Concepts](https://redis.io/docs/latest/develop/ai/featureform/concepts)."},{"id":"permissions","title":"Permissions","role":"security","text":"The role table on [Configure authentication and RBAC](https://redis.io/docs/latest/operate/featureform/configure-auth#built-in-roles) summarizes which permissions each role grants; the table below lists every permission the authorization service checks. To grant a role on a workspace, see [Manage workspaces](https://redis.io/docs/latest/develop/ai/featureform/manage-workspace).\n\n| Permission ID | Category | Resource scope | What it grants |\n| --- | --- | --- | --- |\n| `workspace.create` | workspace | deployment | Create new workspaces. |\n| `workspace.read` | workspace | workspace | Read workspace metadata. |\n| `workspace.list` | workspace | deployment | List visible workspaces. |\n| `workspace.update` | workspace | workspace | Update workspace metadata. |\n| `workspace.delete` | workspace | workspace | Delete a workspace. |\n| `workspace.membership.manage` | workspace | workspace | Manage workspace RBAC bindings. |\n| `graph.read` | graph | workspace | Read graph and resource metadata. |\n| `catalog.read` | catalog | workspace | Read catalog metadata. |\n| `provider.read` | infrastructure | workspace | Read provider definitions. |\n| `provider.write` | infrastructure | workspace | Mutate provider definitions. |\n| `secret_provider.read` | infrastructure | workspace | Read secret-provider definitions. |\n| `secret_provider.write` | infrastructure | workspace | Mutate secret-provider definitions. |\n| `apply.plan` | mutation | workspace | Run apply planning. |\n| `apply.write` | mutation | workspace | Apply workspace changes. |\n| `serving.metadata.read` | data | workspace or resource | Read serving metadata. |\n| `serving.read` | data | workspace or resource | Read served feature values. |\n| `dataframe.read` | data | workspace | Read dataframe data. |\n| `training_set.read` | data | workspace or resource | Read training-set data. |\n| `scheduler.read` | operations | workspace | Read scheduler state. |\n| `scheduler.control` | operations | workspace | Control scheduler state. |\n| `audit.read` | audit | workspace or deployment | Read audit logs. |\n| `machine_credential.read` | machine credentials | workspace | Read machine credentials. |\n| `machine_credential.write` | machine credentials | workspace | Create, rotate, and revoke machine credentials. |"},{"id":"grpc-services","title":"gRPC services","role":"content","text":"This section lists the public gRPC services Feature Form exposes. Most users interact with these services indirectly through the `ff` CLI or the `featureform` Python client, both documented below."},{"id":"service-index","title":"Service index","role":"content","text":"| Service | Purpose |\n| --- | --- |\n| `WorkspaceService` | Workspace CRUD and lookup |\n| `ProviderService` | Workspace-scoped provider CRUD |\n| `SecretProviderService` | Workspace-scoped secret-provider CRUD |\n| `ApplyService` | Declarative graph apply and dry-run planning |\n| `ResourceService` | Graph browsing, lineage, search, versions, and workspace stats |\n| `CatalogService` | Physical catalog inspection |\n| `ServingService` | Online serving and serving metadata |\n| `DataframeService` | Dataframe plan resolution |\n| `RbacService` | Roles, permissions, access, and bindings |\n| `MachineCredentialService` | Machine credential lifecycle |\n| `AuditService` | Audit log listing |\n| `VersionService` | Version compatibility and auth discovery metadata |"},{"id":"service-operations","title":"Service operations","role":"content","text":"- `WorkspaceService`: create, get, list, update, delete\n- `ProviderService`: register, get, list, update, delete\n- `SecretProviderService`: register, get, list, update, delete\n- `ApplyService`: apply and plan\n- `ResourceService`: per-resource get and list plus lineage, impact, versions, and workspace stats"},{"id":"apply-request-fields","title":"Apply request fields","role":"content","text":"- `workspace_id`\n- `resources`\n- `dry_run`\n- `apply_strategy`\n- `execution_mode`"},{"id":"provider-config-models","title":"Provider config models","role":"content","text":"- `PostgresConfig`\n- `SnowflakeConfig`\n- `S3Config`\n- `SparkProviderConfig`\n- `ProviderHealth`\n- `SecretRef`"},{"id":"cli","title":"CLI","role":"content","text":"This section lists `ff` CLI commands and global flags. For usage in context, see [Define and deploy features](https://redis.io/docs/latest/develop/ai/featureform/define-and-deploy-features), [Manage workspaces](https://redis.io/docs/latest/develop/ai/featureform/manage-workspace), and [Register providers](https://redis.io/docs/latest/develop/ai/featureform/register-providers)."},{"id":"global-flags","title":"Global flags","role":"content","text":"Connection and transport:\n\n- `--server`, `-s`\n- `--grpc-server`\n- `--transport rest|grpc`\n- `--timeout`, `-t`\n- `--no-tls`\n\nAuthentication:\n\n- `--token`\n- `--client-id`\n- `--client-secret`\n- `--issuer-url`\n\nCLI behavior:\n\n- `--output`, `-o`\n- `--config`\n- `--no-color`\n- `--verbose`, `-v`\n- `--skip-version-check`"},{"id":"top-level-commands","title":"Top-level commands","role":"content","text":"- `ff version`\n- `ff ping`\n- `ff workspace`\n- `ff provider`\n- `ff secret-provider`\n- `ff apply`\n- `ff auth`\n- `ff rbac`\n- `ff machine-credential`\n- `ff audit`\n- `ff catalog`\n- `ff graph`\n- `ff scheduler`\n- `ff dataframe`\n- `ff config`"},{"id":"python-client","title":"Python client","role":"content","text":"This section lists the public symbols the `featureform` Python package exports. For usage, see [Define and deploy features](https://redis.io/docs/latest/develop/ai/featureform/define-and-deploy-features) to author a definitions file, and [Serve features](https://redis.io/docs/latest/develop/ai/featureform/serve-features) to read features at inference time."},{"id":"client-apis","title":"Client APIs","role":"content","text":"- `ff.Client`\n- `ff.WorkspaceClient`\n- `ff.ProviderClient`\n- `ff.SecretProviderClient`\n- `ff.ApplyResult`\n- `ff.ApplyWaitResult`"},{"id":"common-error-types","title":"Common error types","role":"content","text":"- `ff.FeatureformError`\n- `ff.ConnectionError`\n- `ff.InvalidArgumentError`\n- `ff.NotFoundError`\n- `ff.TimeoutError`\n- `ff.ValidationError`"},{"id":"secret-provider-and-provider-helpers","title":"Secret-provider and provider helpers","role":"content","text":"- `ff.EnvSecretProvider`\n- `ff.VaultSecretProvider`\n- `ff.K8sSecretProvider`\n- `ff.PostgresProvider`\n- `ff.RedisProvider`\n- `ff.S3Provider`\n- `ff.SparkProvider`\n- `ff.get_postgres(name)`\n- `ff.get_provider(name)`"},{"id":"core-resource-types","title":"Core resource types","role":"content","text":"- `ff.Entity`\n- `ff.Dataset`\n- `ff.Feature`\n- `ff.Label`\n- `ff.TrainingSet`\n- `ff.FeatureView`"}],"examples":[]}
{"schema_version":2,"id":"develop/ai/featureform/register-providers/databricks","title":"Register Databricks providers","url":"https://redis.io/docs/latest/develop/ai/featureform/register-providers/databricks/","summary":"Register Databricks compute, Unity Catalog, and secret providers with Redis Feature Form.","tags":[],"last_updated":"2026-09-11T15:54:40+01:00","page_type":"content","content_hash":"cac556384a0748bcbf2d5f750825f4cf80faa66132629f9d2f222b83cc618163","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Use Databricks with Redis Feature Form by registering each required provider record separately. A typical Spark workflow needs at least one `unity-catalog` provider and one `databricks` compute provider. Register an additional provider for each physical catalog, secret backend, or online store the workflow uses."},{"id":"understand-the-databricks-providers","title":"Understand the Databricks providers","role":"content","text":"| Provider type | Purpose | Required for Databricks compute |\n| --- | --- | --- |\n| `unity-catalog` | Identifies a catalog and provides credentials for catalog health, direct reads, and optional Volume artifact storage | Yes |\n| `databricks` | Selects Databricks compute, the managed-output catalog, table format defaults, and artifact storage | Yes |\n| `databricks-secret` | Resolves existing Databricks secret scope and key values | No |\n\nThe `databricks` provider fills the `offline-store` and `compute` roles. It doesn't provide online serving. A feature view also needs a supported `online-store` provider such as Redis."},{"id":"before-you-begin","title":"Before you begin","role":"content","text":"Make sure you have:\n\n- A Feature Form [workspace](https://redis.io/docs/latest/develop/ai/featureform/manage-workspace).\n- An HTTPS [Databricks workspace URL](https://docs.databricks.com/aws/en/workspace/workspace-details).\n- A personal access token (PAT), or an OAuth machine-to-machine (M2M) client ID and client secret.\n- A non-Databricks [secret provider](https://redis.io/docs/latest/develop/ai/featureform/register-providers#configure-secret-providers) that can resolve the PAT or OAuth client secret.\n- Every Unity Catalog catalog the workflow needs, plus a schema and Volume for execution artifacts. One catalog can serve multiple purposes.\n- Either an existing cluster or the configuration required to create Jobs compute.\n\nThe compute principal needs permission to inspect or create its selected compute, submit Databricks Jobs work, access the output catalog, and delete Feature Form-managed tables. Workloads need read and write access to the required catalog locations.\n\n> [!NOTE]\n> Don't use a `databricks@<provider>:<scope>#<key>` reference to authenticate a `databricks` or `unity-catalog` provider. Feature Form rejects that bootstrap cycle. Use `env`, Vault, Kubernetes secrets, or AWS Secrets Manager for these credentials."},{"id":"register-providers-in-dependency-order","title":"Register providers in dependency order","role":"content","text":"For a new Feature Form workspace, register providers in this order:\n\n1. Register a non-Databricks secret provider for the Databricks personal access token (PAT) or OAuth machine-to-machine (M2M) client secret.\n2. Register one `unity-catalog` provider for each physical input, output, or artifact catalog.\n3. Register the `databricks` compute provider.\n4. Register `databricks-secret` if another provider needs a value from a Databricks secret scope.\n5. Register the Redis `online-store` provider used by feature views.\n\nThe Python `DatabricksWorkspaceConfig` object in the examples is a reusable configuration object. It isn't a separate provider record."},{"id":"plan-permissions-and-network-access","title":"Plan permissions and network access","role":"content","text":"Provider registration and workload execution use different identities and network paths:\n\n| Component or identity | Required access |\n| --- | --- |\n| Feature Form server | The bootstrap secret backend, Databricks workspace APIs, configured catalogs and clusters, and Redis for provider health checks and inference-time feature reads |\n| Unity Catalog provider principal | Catalog lookup and direct reads; create, read, and delete access to a configured artifact Volume when used |\n| Databricks provider principal | Inspect existing compute or submit Jobs compute; access and delete Feature Form-managed output tables |\n| Databricks workload identity | Read input catalogs, write output catalogs, read execution artifacts, and connect to Redis during feature-view materialization |\n\nAllow each connection independently. A successful connection from the Feature Form server doesn't prove that Databricks compute can reach the same endpoint. See the [Unity Catalog privilege reference](https://docs.databricks.com/aws/en/data-governance/unity-catalog/access-control/privileges-reference) for catalog permissions and the [Databricks authentication documentation](https://docs.databricks.com/aws/en/dev-tools/auth) for workspace authentication.\n\nThe Python examples use a workspace-scoped provider client and this shared Databricks workspace configuration. Each tab also shows the equivalent command-line interface (CLI) command.\n\n[code example]"},{"id":"register-unity-catalog","title":"Register Unity Catalog","role":"content","text":"Register the catalog before the compute provider that refers to it:\n\n**Python:**\n\n[code example]\n\n**ff CLI:**\n\n[code example]\n\n\n\nFor OAuth M2M authentication with a Kubernetes-backed client secret, first register the Kubernetes secret provider as described in [Configure secret providers](https://redis.io/docs/latest/develop/ai/featureform/register-providers#register-kubernetes-secrets). Then replace the shared Python workspace configuration or the PAT flags with:\n\n**Python:**\n\n[code example]\n\n**ff CLI:**\n\n[code example]\n\n\n\nOne `unity-catalog` provider represents one physical Databricks catalog. You can reference the same provider for input tables, managed outputs, and execution artifacts when they use that catalog. Register separate providers when they use different physical catalogs or need different credentials:\n\n| Catalog use | Where to reference the provider name |\n| --- | --- |\n| Existing input tables | The dataset location's `catalog_provider` field |\n| Feature Form-managed outputs | `DatabricksConfig.output_catalog` or `--databricks-output-catalog` |\n| Unity Catalog Volume artifacts | `artifact_store.provider` or `--databricks-artifact-store-provider` |\n\nAll catalog and compute providers used by one workload must be in the same Feature Form workspace and identify the same Databricks workspace host. They can use different Databricks credentials. Register every referenced catalog provider before the compute provider. The compute examples on this page use the same `<catalog-provider-name>` for managed outputs and artifact storage."},{"id":"choose-compute","title":"Choose compute","role":"content","text":"Feature Form exposes two Databricks compute targets:\n\n| Target | Use it when | Required configuration |\n| --- | --- | --- |\n| `existing_cluster` | You manage a long-running cluster outside Feature Form | An accessible cluster ID |\n| `job_cluster` | Each managed job should use Jobs compute | A runtime version, worker node type or instance pool, and fixed workers or autoscaling |\n\nAn existing-cluster health check authenticates and verifies that Feature Form can inspect the cluster. A Jobs compute health check authenticates and validates the configuration, but it doesn't create a cluster or submit a representative job."},{"id":"register-an-existing-cluster","title":"Register an existing cluster","role":"content","text":"**Python:**\n\n[code example]\n\n**ff CLI:**\n\n[code example]\n\n\n\nIf you author Python callable transformations, import `SparkCallableRuntime` from `featureform.types` and set `callable_runtime=SparkCallableRuntime(python_version=\"<python-version>\", cloudpickle_version=\"<cloudpickle-version>\")` on the Python `DatabricksConfig`. With the CLI, add:\n\n[code example]\n\nThe versions must match the environment that serializes and executes the callable."},{"id":"register-jobs-compute","title":"Register Jobs compute","role":"content","text":"This example uses a fixed worker count:\n\n**Python:**\n\n[code example]\n\n**ff CLI:**\n\n[code example]"},{"id":"apply-a-compute-policy-and-tags","title":"Apply a compute policy and tags","role":"content","text":"Use a compute policy to enforce workspace requirements. Set `apply_policy_default_values` when the Databricks Jobs application programming interface (API) should apply defaults defined by the policy. Registration validates these fields but doesn't create a cluster or prove that the principal can use the policy.\n\n**Python:**\n\n[code example]\n\n**ff CLI:**\n\n[code example]\n\n\n\nDatabricks allows at most 45 custom cluster tags, and Feature Form enforces that platform limit during registration. Repeat `--databricks-job-custom-tag` for each `key=value` pair. See the [Databricks Jobs API reference](https://docs.databricks.com/api/jobs/v2) for cluster-tag constraints and the [Databricks compute policy reference](https://docs.databricks.com/aws/en/admin/clusters/policy-definition) for policy rules and default-value behavior.\n\nIn Python, set `instance_pool_id=\"<instance-pool-id>\"` instead of `node_type_id` when the workspace uses an instance pool. With the CLI, use `--databricks-job-instance-pool-id <instance-pool-id>` instead of a worker node type.\n\nTo autoscale in Python, import `DatabricksAutoscaleConfig` from `featureform.types`, omit `num_workers`, and set `autoscale=DatabricksAutoscaleConfig(min_workers=int(\"<minimum-workers>\"), max_workers=int(\"<maximum-workers>\"))`. With the CLI, omit `--databricks-job-num-workers` and set both of these flags:\n\n[code example]\n\nFixed workers and autoscaling are mutually exclusive. Common optional settings include a driver node type or pool, cluster policy, data security mode, runtime engine, custom tags, log delivery, init scripts, and disk settings. Run `ff provider register --help` for the current flags."},{"id":"configure-aws-attributes","title":"Configure AWS attributes","role":"configuration","text":"Use Amazon Web Services (AWS) attributes only for Jobs compute in a Databricks workspace on AWS. In Python, create the following configuration and set `aws_attributes=aws_attributes` on `DatabricksJobClusterConfig`. With the CLI, append the equivalent flags to the registration command:\n\n**Python:**\n\n[code example]\n\n**ff CLI:**\n\n[code example]\n\n\n\nThis example keeps the driver on an on-demand instance, uses Spot instances with on-demand fallback for subsequent nodes, limits the Spot bid to the corresponding on-demand price, and attaches a preconfigured instance profile. The instance profile must already be added to the Databricks workspace.\n\n| Python field | CLI flag | Meaning or constraint |\n| --- | --- | --- |\n| `availability` | `--databricks-job-aws-availability` | Databricks availability mode |\n| `zone_id` | `--databricks-job-aws-zone-id` | AWS availability zone or Databricks automatic-zone value |\n| `first_on_demand` | `--databricks-job-aws-first-on-demand` | Nonnegative number of initial on-demand nodes |\n| `spot_bid_price_percent` | `--databricks-job-aws-spot-bid-price-percent` | Nonnegative percentage of the corresponding on-demand price |\n| `ebs_volume_type` | `--databricks-job-aws-ebs-volume-type` | Databricks-supported Amazon Elastic Block Store (EBS) volume type |\n| `ebs_volume_count` | `--databricks-job-aws-ebs-volume-count` | Nonnegative number of EBS volumes per instance |\n| `ebs_volume_size` | `--databricks-job-aws-ebs-volume-size` | Nonnegative EBS volume size in gigabytes |\n| `ebs_volume_iops` | `--databricks-job-aws-ebs-volume-iops` | Nonnegative provisioned input/output operations per second for supported volume types |\n| `ebs_volume_throughput` | `--databricks-job-aws-ebs-volume-throughput` | Nonnegative throughput for supported volume types |\n| `instance_profile_arn` | `--databricks-job-aws-instance-profile-arn` | Preconfigured AWS instance-profile Amazon Resource Name (ARN) |\n\nSee the [Databricks Jobs API reference](https://docs.databricks.com/aws/en/reference/jobs-2.0-api) for the allowed values, units, interactions, and cloud-specific constraints.\n\nFor a Python callable transformation on Jobs compute, import `SparkCallableRuntime` from `featureform.types` and set `callable_runtime=SparkCallableRuntime(python_version=\"<python-version>\")`. With the CLI, set `--databricks-callable-python-version`. Don't set the provider-level cloudpickle version for `job_cluster`."},{"id":"choose-artifact-storage","title":"Choose artifact storage","role":"content","text":"Feature Form publishes temporary execution artifacts for Databricks jobs. Explicitly configure a Unity Catalog Volume for every new Databricks compute provider. If you omit `artifact_store`, Feature Form uses the legacy Workspace Files and DBFS policy for compatibility; this policy isn't recommended for new registrations.\n\nTo use a [Unity Catalog Volume](https://docs.databricks.com/aws/en/volumes/), create the schema and Volume first. The Unity Catalog provider's principal needs create, read, and delete access through the Databricks Files API. The Databricks run principal needs read access.\n\nIn Python, import `DatabricksUnityCatalogArtifactStoreConfig` from `featureform.types` and set `artifact_store=DatabricksUnityCatalogArtifactStoreConfig(provider=\"<catalog-provider-name>\", schema=\"<schema-name>\", volume=\"<volume-name>\")` on `DatabricksConfig`. With the CLI, add these flags to the compute registration command:\n\n[code example]\n\nThe artifact catalog can use the same `unity-catalog` provider as the output catalog, as shown in the compute examples, or another provider. It must target the same Databricks workspace host. The output catalog and artifact-store policy are immutable; changing either requires a new Databricks compute provider."},{"id":"choose-a-managed-table-format","title":"Choose a managed table format","role":"content","text":"The provider default applies when a managed transformation, feature view, or training set doesn't select a format.\n\n| Value | Use with Feature Form dataframe reads | Important boundary |\n| --- | --- | --- |\n| `delta_uniform` | Yes | Default; exposes Iceberg-compatible metadata |\n| `iceberg` | Yes | Not supported for incremental Databricks transformations |\n| `delta` | No | Remains usable by Databricks compute, but not by the current dataframe path |\n\nChanging the provider default affects new managed output versions. It doesn't rewrite existing outputs."},{"id":"use-databricks-secrets","title":"Use Databricks secrets","role":"content","text":"Feature Form doesn't infer access to Databricks secret scopes from a `databricks` compute or `unity-catalog` provider. To use any value stored in a Databricks secret scope, register a separate `databricks-secret` provider. Every reference names that provider, the scope, and the key: `databricks@<databricks-secret-provider-name>:<scope>#<key>`.\n\nA `databricks-secret` provider must authenticate before it can call the Databricks Secrets API. Feature Form therefore has to resolve the provider's PAT or OAuth client secret before it can retrieve any Databricks secret. The provider can't use a secret behind itself as that bootstrap credential.\n\nUse this bootstrap order:\n\n1. Register a non-Databricks secret provider, such as `env`, Vault, Kubernetes secrets, or AWS Secrets Manager.\n2. Register `databricks-secret` with a PAT or OAuth client-secret reference from that provider.\n3. Use the registered `databricks-secret` provider in references to existing Databricks scopes and keys.\n\nThe following PAT example uses the built-in `env` provider for the bootstrap credential:\n\n**Python:**\n\n[code example]\n\n**ff CLI:**\n\n[code example]\n\n\n\nFor OAuth M2M authentication, use the same non-Databricks bootstrap secret provider used by the catalog and compute providers:\n\n**Python:**\n\n[code example]\n\n**ff CLI:**\n\n[code example]\n\n\n\nThis secret-provider type doesn't have a live health probe, so registration success doesn't prove that the scope and key can be read. Test a representative reference before relying on the provider."},{"id":"connect-databricks-compute-to-redis","title":"Connect Databricks compute to Redis","role":"content","text":"A feature view needs a Redis `online-store` provider for materialization and inference-time reads. Register Redis as described in [Register Redis providers](https://redis.io/docs/latest/develop/ai/featureform/register-providers/redis).\n\nIf the Redis password is stored in a Databricks secret scope, configure the provider with the registered `databricks-secret` provider:\n\n**Python:**\n\n[code example]\n\n**ff CLI:**\n\n[code example]\n\n\n\nFor both `job_cluster` and `existing_cluster` Spark feature-view materialization, the Redis password must use a `databricks@<provider>:<scope>#<key>` reference. With `job_cluster`, Feature Form adds the reference to the job environment so Databricks can resolve it. With `existing_cluster`, the cluster resolves the scope and key directly.\n\nKeep Transport Layer Security (TLS) enabled when the Redis deployment requires it. For Spark materialization, Redis certificate paths must also be available to the Databricks runtime; secret-backed Redis TLS certificate fields aren't projected to Spark. Verify Redis connectivity separately from the Feature Form server and the Databricks compute environment."},{"id":"databricks-configuration-reference","title":"Databricks configuration reference","role":"content","text":"The tables in this section map the Python configuration fields to their `ff` CLI flags. All secret fields accept a registered secret reference, not a credential value."},{"id":"workspace-authentication-and-related-providers","title":"Workspace authentication and related providers","role":"content","text":"`unity-catalog` and `databricks` use `DatabricksWorkspaceConfig`:\n\n| Python field | CLI flag | Requirement |\n| --- | --- | --- |\n| `workspace.workspace_url` | `--databricks-workspace-url` | Required HTTPS workspace URL |\n| `workspace.auth.type` | `--databricks-auth-type` | `pat` or `oauth_m2m` |\n| `DatabricksPATAuth.token` | `--databricks-token-secret` | Required for PAT authentication |\n| `DatabricksOAuthM2MAuth.client_id` | `--databricks-oauth-client-id` | Required for OAuth M2M |\n| `DatabricksOAuthM2MAuth.client_secret` | `--databricks-oauth-client-secret` | Required for OAuth M2M; use a non-Databricks secret reference |\n| `UnityCatalogConfig.catalog_name` | `--unity-catalog-name` | Required for `unity-catalog` |"},{"id":"databricks-compute","title":"Databricks compute","role":"content","text":"| Python field | CLI flag | Requirement or default |\n| --- | --- | --- |\n| `workspace` | Databricks workspace and authentication flags | Required |\n| `compute_target` | `--databricks-compute-target` | `existing_cluster` by default; also accepts `job_cluster` |\n| `cluster_id` | `--databricks-cluster-id` | Required for `existing_cluster` |\n| `job_cluster` | `--databricks-job-*` | Required for `job_cluster`; see the Jobs compute table |\n| `output_catalog` | `--databricks-output-catalog` | Required `unity-catalog` provider name |\n| `default_managed_table_format` | `--default-managed-table-format` | `delta_uniform` by default; accepts `delta_uniform`, `iceberg`, or `delta` |\n| `callable_runtime.python_version` | `--databricks-callable-python-version` | Required when you configure a callable runtime |\n| `callable_runtime.cloudpickle_version` | `--databricks-callable-cloudpickle-version` | Required for an existing-cluster callable runtime; invalid for Jobs compute |\n| `artifact_store.type` | `--databricks-artifact-store-type` | Use `unity_catalog` for new registrations; omission selects the legacy `legacy_workspace_dbfs` compatibility policy |\n| `artifact_store.provider` | `--databricks-artifact-store-provider` | Required `unity-catalog` provider for Volume storage |\n| `artifact_store.schema` | `--databricks-artifact-store-schema` | Required for Volume storage |\n| `artifact_store.volume` | `--databricks-artifact-store-volume` | Required for Volume storage |\n\n`output_catalog` and the artifact-store policy are immutable. Register a new compute provider to change either setting."},{"id":"jobs-compute","title":"Jobs compute","role":"content","text":"| Python field | CLI flag | Requirement or constraint |\n| --- | --- | --- |\n| `spark_version` | `--databricks-job-spark-version` | Required Databricks Runtime version |\n| `node_type_id` | `--databricks-job-node-type-id` | A worker node type or worker instance pool is required |\n| `driver_node_type_id` | `--databricks-job-driver-node-type-id` | Optional driver node type |\n| `num_workers` | `--databricks-job-num-workers` | Positive fixed count; mutually exclusive with autoscaling |\n| `autoscale.min_workers` | `--databricks-job-autoscale-min-workers` | Nonnegative; set with maximum workers |\n| `autoscale.max_workers` | `--databricks-job-autoscale-max-workers` | Greater than minimum workers |\n| `data_security_mode` | `--databricks-job-data-security-mode` | Optional Databricks access mode |\n| `policy_id` | `--databricks-job-policy-id` | Optional compute policy ID |\n| `apply_policy_default_values` | `--databricks-job-apply-policy-default-values` | `false` by default |\n| `runtime_engine` | `--databricks-job-runtime-engine` | Optional runtime engine such as `PHOTON` |\n| `custom_tags` | Repeat `--databricks-job-custom-tag <key>=<value>` | At most 45 entries; keys must be nonempty |\n| `cluster_log_conf.s3` | `--databricks-job-cluster-log-s3-*` | Optional; see the log-delivery table |\n| `init_scripts` | Repeat a `--databricks-job-init-script-*` flag | Optional; see the init-script table |\n| `instance_pool_id` | `--databricks-job-instance-pool-id` | Worker pool; can replace `node_type_id` |\n| `driver_instance_pool_id` | `--databricks-job-driver-instance-pool-id` | Optional driver pool |\n| `enable_elastic_disk` | `--databricks-job-enable-elastic-disk` | `false` by default |\n| `enable_local_disk_encryption` | `--databricks-job-enable-local-disk-encryption` | `false` by default |\n| `single_user_name` | `--databricks-job-single-user-name` | Optional user for `SINGLE_USER` access mode |\n| `aws_attributes` | `--databricks-job-aws-*` | Optional for Databricks on AWS; see [Configure AWS attributes](#configure-aws-attributes) |"},{"id":"log-delivery-and-init-scripts","title":"Log delivery and init scripts","role":"content","text":"Jobs compute supports Amazon Simple Storage Service (S3) cluster-log delivery:\n\n| Python field under `cluster_log_conf.s3` | CLI flag |\n| --- | --- |\n| `destination` | `--databricks-job-cluster-log-s3-destination` |\n| `region` | `--databricks-job-cluster-log-s3-region` |\n| `endpoint` | `--databricks-job-cluster-log-s3-endpoint` |\n| `canned_acl` | `--databricks-job-cluster-log-s3-canned-acl` |\n| `enable_encryption` | `--databricks-job-cluster-log-s3-enable-encryption` |\n| `encryption_type` | `--databricks-job-cluster-log-s3-encryption-type` |\n| `kms_key` | `--databricks-job-cluster-log-s3-kms-key` |\n\nSet `destination` and either `region` or `endpoint`. For init scripts, each `DatabricksInitScript` must configure exactly one destination:\n\n| Python destination | CLI flag |\n| --- | --- |\n| `s3=DatabricksS3StorageInfo(...)` | Repeat `--databricks-job-init-script-s3 destination=<s3-uri>,region=<aws-region>` |\n| `dbfs=DatabricksStorageInfo(...)` | Repeat `--databricks-job-init-script-dbfs <dbfs-path>` |\n| `workspace=DatabricksStorageInfo(...)` | Repeat `--databricks-job-init-script-workspace <workspace-path>` |\n| `volumes=DatabricksStorageInfo(...)` | Repeat `--databricks-job-init-script-volumes <volume-path>` |\n\nThe S3 init-script value also accepts `endpoint`, `canned_acl`, `enable_encryption`, `encryption_type`, and `kms_key` as comma-separated `key=value` entries. It requires either `region` or `endpoint`."},{"id":"databricks-secret-provider","title":"Databricks secret provider","role":"content","text":"| Python field | CLI flag | Requirement |\n| --- | --- | --- |\n| `workspace_url` | `--workspace-url` or `--databricks-workspace-url` | Required HTTPS workspace URL |\n| `auth.type` | `--auth-type` or `--databricks-auth-type` | `pat` or `oauth_m2m` |\n| `DatabricksSecretPATAuth.token_secret` | `--token-secret` or `--databricks-token-secret` | Required for PAT authentication |\n| `DatabricksSecretOAuthM2MAuth.client_id` | `--client-id` or `--databricks-client-id` | Required for OAuth M2M |\n| `DatabricksSecretOAuthM2MAuth.client_secret_secret` | `--client-secret-secret` or `--databricks-client-secret-secret` | Required for OAuth M2M; use a non-Databricks secret reference |"},{"id":"understand-registration-health-checks","title":"Understand registration health checks","role":"content","text":"| Provider type | Registration checks | Not checked |\n| --- | --- | --- |\n| `unity-catalog` | Workspace authentication and catalog lookup | Table writes, Volume access, or compute permissions |\n| `databricks` with `existing_cluster` | Workspace authentication and cluster lookup | Job submission, libraries, catalog writes, artifacts, or Redis connectivity |\n| `databricks` with `job_cluster` | Workspace authentication and static configuration | Cluster creation, policy authorization, runtime startup, catalog access, or Redis connectivity |\n| `databricks-secret` | Configuration validation | Live scope or key access |\n\nBecause registration doesn't run a representative workload, policy, permission, library, secret, and network errors can first appear during an apply operation."},{"id":"verify-the-providers","title":"Verify the providers","role":"content","text":"[code example]\n\nIn Python, use `providers.get()` or `providers.list()` for data providers and `secret_providers.get()` for the Databricks secret provider."},{"id":"troubleshoot-registration","title":"Troubleshoot registration","role":"errors","text":"| Symptom | What to check |\n| --- | --- |\n| Output catalog provider isn't found | Register it first and confirm its name and Feature Form workspace |\n| Provider is the wrong type | Use `unity-catalog`, not a generic Iceberg catalog, for `output_catalog` or `--databricks-output-catalog` |\n| Workspace hosts don't match | Use the same Databricks workspace host for compute, output, and artifact catalogs |\n| Existing-cluster health fails | Check the cluster ID, workspace authentication, server network access, and permission to inspect the cluster |\n| Jobs compute validation fails | Supply a runtime, a node type or pool, and either fixed workers or valid autoscaling bounds |\n| A Jobs run is rejected by a compute policy | Confirm that the provider principal can use the policy, required fixed values match, and policy defaults are applied when needed |\n| A Jobs run starts but can't access a catalog | Check the Databricks workload identity's grants on the input and output catalogs, schemas, tables, and storage locations |\n| A Databricks secret fails on first use | Check the bootstrap credential, Databricks secret scope and key, and the principal's secret access |\n| Redis health passes but materialization can't connect | Test name resolution, routing, firewall rules, and TLS from the Databricks compute environment, not only from the Feature Form server |\n| Existing-cluster materialization rejects the Redis password reference | Use a `databricks@<provider>:<scope>#<key>` reference for the Redis password |\n| Volume publication fails | Check that the schema and Volume exist and that both principals have the required Files API access |\n| A dataframe read rejects an output | Check the managed table format; `delta` isn't supported by the current dataframe path |\n\nIf registration succeeds but the first workload fails, start with the health-check boundaries, identity permissions, and network paths on this page. Then compare the submitted Jobs configuration with the [Databricks Jobs API reference](https://docs.databricks.com/aws/en/reference/jobs-2.0-api)."}],"examples":[{"id":"plan-permissions-and-network-access-ex0","language":"python","code":"import featureform as ff\nfrom featureform.types import (\n    DatabricksPATAuth,\n    DatabricksWorkspaceConfig,\n    EnvSecretRef,\n)\n\nclient = ff.Client.from_env()\nproviders = client.providers(\"<workspace-id>\")\ndatabricks_workspace = DatabricksWorkspaceConfig(\n    workspace_url=\"https://<databricks-workspace-host>\",\n    auth=DatabricksPATAuth(\n        token=EnvSecretRef(name=\"DATABRICKS_TOKEN\"),\n    ),\n)","section_id":"plan-permissions-and-network-access"},{"id":"register-unity-catalog-ex0","language":"python","code":"from featureform.types import ProviderType, UnityCatalogConfig\n\nproviders.register(\n    name=\"<catalog-provider-name>\",\n    provider_type=ProviderType.UNITY_CATALOG,\n    config=UnityCatalogConfig(\n        workspace=databricks_workspace,\n        catalog_name=\"<catalog-name>\",\n    ),\n)","section_id":"register-unity-catalog"},{"id":"register-unity-catalog-ex1","language":"bash","code":"ff provider register <catalog-provider-name> \\\n  --workspace <workspace-id> \\\n  --type unity-catalog \\\n  --databricks-workspace-url https://<databricks-workspace-host> \\\n  --databricks-auth-type pat \\\n  --databricks-token-secret env:DATABRICKS_TOKEN \\\n  --unity-catalog-name <catalog-name>","section_id":"register-unity-catalog"},{"id":"register-unity-catalog-ex2","language":"python","code":"from featureform.types import DatabricksOAuthM2MAuth, K8sSecretRef\n\ndatabricks_workspace = DatabricksWorkspaceConfig(\n    workspace_url=\"https://<databricks-workspace-host>\",\n    auth=DatabricksOAuthM2MAuth(\n        client_id=\"<client-id>\",\n        client_secret=K8sSecretRef(\n            provider_name=\"<k8s-secret-provider-name>\",\n            name=\"<secret-name>\",\n            key=\"<client-secret-key>\",\n        ),\n    ),\n)","section_id":"register-unity-catalog"},{"id":"register-unity-catalog-ex3","language":"text","code":"--databricks-auth-type oauth_m2m\n--databricks-oauth-client-id <client-id>\n--databricks-oauth-client-secret k8s@<k8s-secret-provider-name>:<secret-name>#<client-secret-key>","section_id":"register-unity-catalog"},{"id":"register-an-existing-cluster-ex0","language":"python","code":"from featureform.types import (\n    DatabricksConfig,\n    DatabricksUnityCatalogArtifactStoreConfig,\n    ManagedTableFormat,\n    ProviderType,\n)\n\nproviders.register(\n    name=\"<databricks-provider-name>\",\n    provider_type=ProviderType.DATABRICKS,\n    config=DatabricksConfig(\n        workspace=databricks_workspace,\n        compute_target=\"existing_cluster\",\n        cluster_id=\"<cluster-id>\",\n        output_catalog=\"<catalog-provider-name>\",\n        artifact_store=DatabricksUnityCatalogArtifactStoreConfig(\n            provider=\"<catalog-provider-name>\",\n            schema=\"<schema-name>\",\n            volume=\"<volume-name>\",\n        ),\n        default_managed_table_format=ManagedTableFormat.DELTA_UNIFORM,\n    ),\n)","section_id":"register-an-existing-cluster"},{"id":"register-an-existing-cluster-ex1","language":"bash","code":"ff provider register <databricks-provider-name> \\\n  --workspace <workspace-id> \\\n  --type databricks \\\n  --databricks-workspace-url https://<databricks-workspace-host> \\\n  --databricks-auth-type pat \\\n  --databricks-token-secret env:DATABRICKS_TOKEN \\\n  --databricks-compute-target existing_cluster \\\n  --databricks-cluster-id <cluster-id> \\\n  --databricks-output-catalog <catalog-provider-name> \\\n  --databricks-artifact-store-type unity_catalog \\\n  --databricks-artifact-store-provider <catalog-provider-name> \\\n  --databricks-artifact-store-schema <schema-name> \\\n  --databricks-artifact-store-volume <volume-name> \\\n  --default-managed-table-format delta_uniform","section_id":"register-an-existing-cluster"},{"id":"register-an-existing-cluster-ex2","language":"text","code":"--databricks-callable-python-version <python-version>\n--databricks-callable-cloudpickle-version <cloudpickle-version>","section_id":"register-an-existing-cluster"},{"id":"register-jobs-compute-ex0","language":"python","code":"from featureform.types import (\n    DatabricksConfig,\n    DatabricksJobClusterConfig,\n    DatabricksUnityCatalogArtifactStoreConfig,\n    ManagedTableFormat,\n    ProviderType,\n)\n\nproviders.register(\n    name=\"<databricks-provider-name>\",\n    provider_type=ProviderType.DATABRICKS,\n    config=DatabricksConfig(\n        workspace=databricks_workspace,\n        compute_target=\"job_cluster\",\n        job_cluster=DatabricksJobClusterConfig(\n            spark_version=\"<databricks-runtime-version>\",\n            node_type_id=\"<worker-node-type-id>\",\n            num_workers=int(\"<worker-count>\"),\n        ),\n        output_catalog=\"<catalog-provider-name>\",\n        artifact_store=DatabricksUnityCatalogArtifactStoreConfig(\n            provider=\"<catalog-provider-name>\",\n            schema=\"<schema-name>\",\n            volume=\"<volume-name>\",\n        ),\n        default_managed_table_format=ManagedTableFormat.DELTA_UNIFORM,\n    ),\n)","section_id":"register-jobs-compute"},{"id":"register-jobs-compute-ex1","language":"bash","code":"ff provider register <databricks-provider-name> \\\n  --workspace <workspace-id> \\\n  --type databricks \\\n  --databricks-workspace-url https://<databricks-workspace-host> \\\n  --databricks-auth-type pat \\\n  --databricks-token-secret env:DATABRICKS_TOKEN \\\n  --databricks-compute-target job_cluster \\\n  --databricks-job-spark-version <databricks-runtime-version> \\\n  --databricks-job-node-type-id <worker-node-type-id> \\\n  --databricks-job-num-workers <worker-count> \\\n  --databricks-output-catalog <catalog-provider-name> \\\n  --databricks-artifact-store-type unity_catalog \\\n  --databricks-artifact-store-provider <catalog-provider-name> \\\n  --databricks-artifact-store-schema <schema-name> \\\n  --databricks-artifact-store-volume <volume-name> \\\n  --default-managed-table-format delta_uniform","section_id":"register-jobs-compute"},{"id":"apply-a-compute-policy-and-tags-ex0","language":"python","code":"job_cluster = DatabricksJobClusterConfig(\n    spark_version=\"<databricks-runtime-version>\",\n    node_type_id=\"<worker-node-type-id>\",\n    num_workers=int(\"<worker-count>\"),\n    policy_id=\"<cluster-policy-id>\",\n    apply_policy_default_values=True,\n    custom_tags={\n        \"team\": \"<team-name>\",\n        \"purpose\": \"featureform\",\n    },\n)","section_id":"apply-a-compute-policy-and-tags"},{"id":"apply-a-compute-policy-and-tags-ex1","language":"text","code":"--databricks-job-policy-id <cluster-policy-id>\n--databricks-job-apply-policy-default-values\n--databricks-job-custom-tag team=<team-name>\n--databricks-job-custom-tag purpose=featureform","section_id":"apply-a-compute-policy-and-tags"},{"id":"apply-a-compute-policy-and-tags-ex2","language":"text","code":"--databricks-job-autoscale-min-workers <minimum-workers>\n--databricks-job-autoscale-max-workers <maximum-workers>","section_id":"apply-a-compute-policy-and-tags"},{"id":"configure-aws-attributes-ex0","language":"python","code":"from featureform.types import DatabricksAWSAttributesConfig\n\naws_attributes = DatabricksAWSAttributesConfig(\n    availability=\"SPOT_WITH_FALLBACK\",\n    first_on_demand=1,\n    spot_bid_price_percent=100,\n    instance_profile_arn=\"<instance-profile-arn>\",\n)","section_id":"configure-aws-attributes"},{"id":"configure-aws-attributes-ex1","language":"text","code":"--databricks-job-aws-availability SPOT_WITH_FALLBACK\n--databricks-job-aws-first-on-demand 1\n--databricks-job-aws-spot-bid-price-percent 100\n--databricks-job-aws-instance-profile-arn <instance-profile-arn>","section_id":"configure-aws-attributes"},{"id":"choose-artifact-storage-ex0","language":"text","code":"--databricks-artifact-store-type unity_catalog\n--databricks-artifact-store-provider <catalog-provider-name>\n--databricks-artifact-store-schema <schema-name>\n--databricks-artifact-store-volume <volume-name>","section_id":"choose-artifact-storage"},{"id":"use-databricks-secrets-ex0","language":"python","code":"from featureform.types import (\n    DatabricksSecretConfig,\n    DatabricksSecretPATAuth,\n    SecretProviderType,\n)\n\nsecret_providers = client.secret_providers(\"<workspace-id>\")\nsecret_providers.register(\n    name=\"<databricks-secret-provider-name>\",\n    provider_type=SecretProviderType.DATABRICKS,\n    config=DatabricksSecretConfig(\n        workspace_url=\"https://<databricks-workspace-host>\",\n        auth=DatabricksSecretPATAuth(\n            token_secret=EnvSecretRef(name=\"DATABRICKS_TOKEN\"),\n        ),\n    ),\n)","section_id":"use-databricks-secrets"},{"id":"use-databricks-secrets-ex1","language":"bash","code":"ff secret-provider register <databricks-secret-provider-name> \\\n  --workspace <workspace-id> \\\n  --type databricks-secret \\\n  --workspace-url https://<databricks-workspace-host> \\\n  --auth-type pat \\\n  --token-secret env:DATABRICKS_TOKEN","section_id":"use-databricks-secrets"},{"id":"use-databricks-secrets-ex2","language":"python","code":"from featureform.types import (\n    DatabricksSecretConfig,\n    DatabricksSecretOAuthM2MAuth,\n    K8sSecretRef,\n    SecretProviderType,\n)\n\nsecret_providers.register(\n    name=\"<databricks-secret-provider-name>\",\n    provider_type=SecretProviderType.DATABRICKS,\n    config=DatabricksSecretConfig(\n        workspace_url=\"https://<databricks-workspace-host>\",\n        auth=DatabricksSecretOAuthM2MAuth(\n            client_id=\"<client-id>\",\n            client_secret_secret=K8sSecretRef(\n                provider_name=\"<k8s-secret-provider-name>\",\n                name=\"<secret-name>\",\n                key=\"<client-secret-key>\",\n            ),\n        ),\n    ),\n)","section_id":"use-databricks-secrets"},{"id":"use-databricks-secrets-ex3","language":"bash","code":"ff secret-provider register <databricks-secret-provider-name> \\\n  --workspace <workspace-id> \\\n  --type databricks-secret \\\n  --workspace-url https://<databricks-workspace-host> \\\n  --auth-type oauth_m2m \\\n  --client-id <client-id> \\\n  --client-secret-secret k8s@<k8s-secret-provider-name>:<secret-name>#<client-secret-key>","section_id":"use-databricks-secrets"},{"id":"connect-databricks-compute-to-redis-ex0","language":"python","code":"from featureform.types import DatabricksSecretRef, ProviderType, RedisConfig\n\nproviders.register(\n    name=\"<redis-provider-name>\",\n    provider_type=ProviderType.REDIS,\n    config=RedisConfig(\n        host=\"<redis-host>\",\n        port=int(\"<redis-port>\"),\n        username=\"<redis-username>\",\n        password_secret=DatabricksSecretRef(\n            provider_name=\"<databricks-secret-provider-name>\",\n            scope=\"<secret-scope>\",\n            key=\"<redis-password-key>\",\n        ),\n    ),\n)","section_id":"connect-databricks-compute-to-redis"},{"id":"connect-databricks-compute-to-redis-ex1","language":"bash","code":"ff provider register <redis-provider-name> \\\n  --workspace <workspace-id> \\\n  --type redis \\\n  --redis-host <redis-host> \\\n  --redis-port <redis-port> \\\n  --redis-username <redis-username> \\\n  --redis-password-secret databricks@<databricks-secret-provider-name>:<secret-scope>#<redis-password-key>","section_id":"connect-databricks-compute-to-redis"},{"id":"verify-the-providers-ex0","language":"bash","code":"ff provider get <catalog-provider-name> --workspace <workspace-id>\nff provider get <databricks-provider-name> --workspace <workspace-id>\nff provider get <redis-provider-name> --workspace <workspace-id>\nff secret-provider get <databricks-secret-provider-name> --workspace <workspace-id>\nff provider list --workspace <workspace-id>","section_id":"verify-the-providers"}]}
{"schema_version":2,"id":"develop/ai/featureform/register-providers","title":"Register providers","url":"https://redis.io/docs/latest/develop/ai/featureform/register-providers/","summary":"Register storage, compute, catalog, and secret providers in a Redis Feature Form workspace.","aliases":["/develop/ai/featureform/streaming/"],"tags":[],"last_updated":"2026-09-11T15:54:40+01:00","children":[{"id":"develop/ai/featureform/register-providers/databricks","summary":"Register Databricks compute, Unity Catalog, and secret providers with Redis Feature Form.","title":"Register Databricks providers","url":"https://redis.io/docs/latest/develop/ai/featureform/register-providers/databricks/"},{"id":"develop/ai/featureform/register-providers/snowflake","summary":"Register Snowflake storage and compute with Redis Feature Form.","title":"Register a Snowflake provider","url":"https://redis.io/docs/latest/develop/ai/featureform/register-providers/snowflake/"},{"id":"develop/ai/featureform/register-providers/postgresql","summary":"Register PostgreSQL storage and compute with Redis Feature Form.","title":"Register a PostgreSQL provider","url":"https://redis.io/docs/latest/develop/ai/featureform/register-providers/postgresql/"},{"id":"develop/ai/featureform/register-providers/s3","summary":"Register Amazon S3 and S3-compatible object storage with Redis Feature Form.","title":"Register an S3 provider","url":"https://redis.io/docs/latest/develop/ai/featureform/register-providers/s3/"},{"id":"develop/ai/featureform/register-providers/redis","summary":"Register standalone Redis and Redis Cluster online-store providers with Redis Feature Form.","title":"Register Redis providers","url":"https://redis.io/docs/latest/develop/ai/featureform/register-providers/redis/"}],"page_type":"content","content_hash":"ffd99c2d8137c02755f2da345c385329976dcbd32a00dbfdb91f451a1851cb89","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Register the providers and secret backends Redis Feature Form needs before you author features or transformations. Providers connect a workspace to external systems, and definitions files reference them by name."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before you register a provider, make sure you have:\n\n- A [workspace](https://redis.io/docs/latest/develop/ai/featureform/manage-workspace).\n- The [`redis-featureform` Python package](https://redis.io/docs/latest/develop/ai/featureform/quickstart#install-the-ff-cli) installed. It includes the Python client and the `ff` command-line interface (CLI).\n- Network access from the Feature Form server and its workers to the external system.\n- A registered secret provider for each credential reference in the provider configuration.\n\nThe examples show Python first and the equivalent `ff` command second. [Configure authentication](https://redis.io/docs/latest/operate/featureform/configure-auth) through an active profile or environment variables before you use either interface. `ff.Client.from_env()` reads that configuration, including `FEATUREFORM_BASE_URL` and `FEATUREFORM_TOKEN` when set. The CLI connects to `localhost:9090` by default; use `--server <host:port>` or configure another endpoint when the server is elsewhere.\n\nThe Python examples on this page use these workspace-scoped clients:\n\n[code example]\n\nProvider names are unique within a workspace. Use stable names because definitions files and other providers can refer to them."},{"id":"choose-a-provider","title":"Choose a provider","role":"content","text":"A provider fills one or more literal roles: `offline-store`, `online-store`, `compute`, or `streaming`. Registration support doesn't mean every resource accepts every provider.\n\n| Provider | Roles | Use it for |\n| --- | --- | --- |\n| [Databricks](https://redis.io/docs/latest/develop/ai/featureform/register-providers/databricks) | `offline-store`, `compute` | Managed Spark compute with Unity Catalog outputs |\n| [Snowflake](https://redis.io/docs/latest/develop/ai/featureform/register-providers/snowflake) | `offline-store`, `compute` | Snowflake datasets and SQL compute |\n| [PostgreSQL](https://redis.io/docs/latest/develop/ai/featureform/register-providers/postgresql) | `offline-store`, `compute` | PostgreSQL datasets and SQL compute |\n| [S3](https://redis.io/docs/latest/develop/ai/featureform/register-providers/s3) | `offline-store` | Object storage and Spark data or staging access |\n| [Redis](https://redis.io/docs/latest/develop/ai/featureform/register-providers/redis) | `online-store` | Low-latency feature serving |\n| Spark | `compute` | Generic Spark execution |\n| Iceberg catalog | `offline-store` | Catalog-backed Iceberg tables |\n\nUse `ff provider register --help` to see the complete list of provider types and their current options."},{"id":"configure-secret-providers","title":"Configure secret providers","role":"configuration","text":"Feature Form never stores credentials in any form. A provider configuration contains secret references, and the Feature Form process that needs a credential resolves it from the registered backend.\n\nEach workspace includes an `env` secret provider. An `env:PG_PASSWORD` reference reads `PG_PASSWORD` from the Feature Form server's environment, not from the shell where you run `ff`.\n\nCheck the built-in provider:\n\n[code example]\n\nFor production deployments, register the backend that manages your credentials before registering data providers that use it."},{"id":"register-an-environment-secret-provider","title":"Register an environment secret provider","role":"content","text":"Use an environment provider for local development or bootstrap. A prefix helps separate its variables from other process configuration.\n\n**Python:**\n\n[code example]\n\n**ff CLI:**\n\n[code example]"},{"id":"register-vault","title":"Register Vault","role":"content","text":"The Feature Form server must be able to authenticate to Vault. The provider uses the key-value version 2 secrets engine.\n\n**Python:**\n\n[code example]\n\n**ff CLI:**\n\n[code example]"},{"id":"register-kubernetes-secrets","title":"Register Kubernetes secrets","role":"content","text":"For a Kubernetes deployment, use in-cluster authentication. The Feature Form server's service account needs permission to read secrets in the target namespace.\n\n**Python:**\n\n[code example]\n\n**ff CLI:**\n\n[code example]\n\n\n\nIf Feature Form runs outside Kubernetes, omit `in_cluster=True` or use `--no-k8s-in-cluster`. The Feature Form server then uses `~/.kube/config` and its current context. It doesn't use kubeconfig from the computer where you run `ff`."},{"id":"register-aws-secrets-manager","title":"Register AWS Secrets Manager","role":"content","text":"The Feature Form server authenticates with the standard Amazon Web Services (AWS) credential chain.\n\n**Python:**\n\n[code example]\n\n**ff CLI:**\n\n[code example]"},{"id":"register-redis-for-online-serving","title":"Register Redis for online serving","role":"content","text":"Register a `redis` or `redis-cluster` provider when feature views need an online store. See [Register Redis providers](https://redis.io/docs/latest/develop/ai/featureform/register-providers/redis) for topology, authentication, TLS, Spark, and Databricks guidance."},{"id":"keep-health-checks-enabled","title":"Keep health checks enabled","role":"content","text":"Provider registration validates configuration and runs a synchronous health check before persistence by default. The exact check is provider-specific and might require write permissions.\n\nUse `--skip-health-check`, or `skip_health_check=True` in Python, only when the external system is deliberately unavailable during registration. This skips the synchronous check but does not disable recurring monitoring. Use `--disable-monitoring`, or `disable_monitoring=True`, to skip the initial check and disable recurring checks.\n\n> [!NOTE]\n> A successful health check verifies only the operations documented for that provider. It doesn't prove that every future dataset, table, policy, or workload is accessible."},{"id":"verify-registration","title":"Verify registration","role":"content","text":"[code example]\n\nPass `--output json` or `--output yaml` for machine-readable output. If the provider isn't returned, rerun the registration command and review its validation or health-check error.\n\nIn Python, use `providers.list()` or `providers.get(\"<provider-name>\")`."},{"id":"update-or-delete-a-provider","title":"Update or delete a provider","role":"content","text":"[code example]\n\nProvider-specific fields can be mutable, force-required, or immutable. `--force` allows a force-required change after Feature Form checks known direct uses. It doesn't override immutable fields, migrate external data, or guarantee that no indirect references exist. Review definitions and provider-to-provider references before an update or deletion.\n\nIn Python, use `providers.update()` and `providers.delete()`. Pass `force=True` to `providers.update()` only after the same review required for `--force`."},{"id":"automate-repeatable-registration","title":"Automate repeatable registration","role":"content","text":"`register()` creates a provider and returns an error when that name already exists. For a repeatable automation script, register missing providers and update existing providers of the same type:\n\n[code example]\n\nYou can pass either `providers` or `secret_providers` as `registry`. The helper deliberately doesn't force an update. A force-required or immutable change fails so you can review its effect before changing the provider."},{"id":"next-steps","title":"Next steps","role":"content","text":"After you register the required providers, [define and deploy features](https://redis.io/docs/latest/develop/ai/featureform/define-and-deploy-features)."}],"examples":[{"id":"prerequisites-ex0","language":"python","code":"import featureform as ff\n\nclient = ff.Client.from_env()\nproviders = client.providers(\"<workspace-id>\")\nsecret_providers = client.secret_providers(\"<workspace-id>\")","section_id":"prerequisites"},{"id":"configure-secret-providers-ex0","language":"bash","code":"ff secret-provider list --workspace <workspace-id>\nff secret-provider get env --workspace <workspace-id>","section_id":"configure-secret-providers"},{"id":"register-an-environment-secret-provider-ex0","language":"python","code":"from featureform.types import EnvSecretConfig, SecretProviderType\n\nsecret_providers.register(\n    name=\"<secret-provider-name>\",\n    provider_type=SecretProviderType.ENV,\n    config=EnvSecretConfig(prefix=\"FF_\"),\n)","section_id":"register-an-environment-secret-provider"},{"id":"register-an-environment-secret-provider-ex1","language":"bash","code":"ff secret-provider register <secret-provider-name> \\\n  --workspace <workspace-id> \\\n  --type env-var \\\n  --env-prefix FF_","section_id":"register-an-environment-secret-provider"},{"id":"register-vault-ex0","language":"python","code":"from featureform.types import SecretProviderType, VaultSecretConfig\n\nsecret_providers.register(\n    name=\"<secret-provider-name>\",\n    provider_type=SecretProviderType.VAULT,\n    config=VaultSecretConfig(\n        address=\"https://<vault-host>\",\n        token_path=\"<vault-token-path>\",\n    ),\n)","section_id":"register-vault"},{"id":"register-vault-ex1","language":"bash","code":"ff secret-provider register <secret-provider-name> \\\n  --workspace <workspace-id> \\\n  --type vault \\\n  --vault-address https://<vault-host> \\\n  --vault-token-path <vault-token-path>","section_id":"register-vault"},{"id":"register-kubernetes-secrets-ex0","language":"python","code":"from featureform.types import KubernetesSecretConfig, SecretProviderType\n\nsecret_providers.register(\n    name=\"<secret-provider-name>\",\n    provider_type=SecretProviderType.K8S,\n    config=KubernetesSecretConfig(\n        namespace=\"<namespace>\",\n        secret_name=\"<secret-name>\",\n        in_cluster=True,\n    ),\n)","section_id":"register-kubernetes-secrets"},{"id":"register-kubernetes-secrets-ex1","language":"bash","code":"ff secret-provider register <secret-provider-name> \\\n  --workspace <workspace-id> \\\n  --type k8s-secret \\\n  --k8s-namespace <namespace> \\\n  --k8s-secret-name <secret-name> \\\n  --k8s-in-cluster","section_id":"register-kubernetes-secrets"},{"id":"register-aws-secrets-manager-ex0","language":"python","code":"from featureform.types import AWSSecretsManagerSecretConfig, SecretProviderType\n\nsecret_providers.register(\n    name=\"<secret-provider-name>\",\n    provider_type=SecretProviderType.AWS,\n    config=AWSSecretsManagerSecretConfig(region=\"<aws-region>\"),\n)","section_id":"register-aws-secrets-manager"},{"id":"register-aws-secrets-manager-ex1","language":"bash","code":"ff secret-provider register <secret-provider-name> \\\n  --workspace <workspace-id> \\\n  --type aws-secrets-manager \\\n  --aws-region <aws-region>","section_id":"register-aws-secrets-manager"},{"id":"verify-registration-ex0","language":"bash","code":"ff provider list --workspace <workspace-id>\nff provider get <provider-name> --workspace <workspace-id>","section_id":"verify-registration"},{"id":"update-or-delete-a-provider-ex0","language":"bash","code":"ff provider update <provider-name> \\\n  --workspace <workspace-id> \\\n  <provider-options>\n\nff provider delete <provider-name> \\\n  --workspace <workspace-id>","section_id":"update-or-delete-a-provider"},{"id":"automate-repeatable-registration-ex0","language":"python","code":"import featureform as ff\n\n\ndef ensure_registered(registry, name, provider_type, config):\n    try:\n        current = registry.get(name)\n    except ff.NotFoundError:\n        return registry.register(\n            name=name,\n            provider_type=provider_type,\n            config=config,\n        )\n\n    if current.type != provider_type:\n        raise ValueError(\n            f\"{name!r} is registered as {current.type}, not {provider_type}\"\n        )\n\n    return registry.update(\n        name=name,\n        config=config,\n        provider_type=provider_type,\n    )","section_id":"automate-repeatable-registration"}]}
{"schema_version":2,"id":"develop/ai/featureform/register-providers/postgresql","title":"Register a PostgreSQL provider","url":"https://redis.io/docs/latest/develop/ai/featureform/register-providers/postgresql/","summary":"Register PostgreSQL storage and compute with Redis Feature Form.","tags":[],"last_updated":"2026-09-11T15:54:40+01:00","page_type":"content","content_hash":"9b6c2b9d945b37e4253db1c197bad472eb6bb1c04ef3415ce3e8e73d2181b994","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Register a `postgres` provider when Redis Feature Form should discover PostgreSQL tables, run structured query language (SQL) transformations, or materialize training sets and feature-view data in PostgreSQL.\n\nThe provider fills the `offline-store` and `compute` roles. It doesn't provide online serving. A serving feature view also needs a supported `online-store` provider."},{"id":"distinguish-data-from-feature-form-state","title":"Distinguish data from Feature Form state","role":"content","text":"A PostgreSQL data provider holds or computes customer feature data. It is separate from a PostgreSQL state backend that stores Feature Form's control-plane state. Use separate databases, credentials, and schemas for these purposes unless your deployment has another isolation policy."},{"id":"before-you-begin","title":"Before you begin","role":"content","text":"Make sure you have:\n\n- A Feature Form [workspace](https://redis.io/docs/latest/develop/ai/featureform/manage-workspace).\n- A PostgreSQL hostname and database reachable from the Feature Form server.\n- A PostgreSQL username and password.\n- A [secret provider](https://redis.io/docs/latest/develop/ai/featureform/register-providers#configure-secret-providers) for the password reference.\n- Permission to read the required source tables and create managed outputs.\n\nThe Python example uses this workspace-scoped provider client:\n\n[code example]"},{"id":"register-postgresql","title":"Register PostgreSQL","role":"content","text":"**Python:**\n\n[code example]\n\n**ff CLI:**\n\n[code example]\n\n\n\nThe environment reference is resolved by the Feature Form server. Replace it with a reference to another registered secret backend when appropriate.\n\nThe current public provider supports password authentication. Don't assume that internal or database-specific identity authentication methods are available through `ff`."},{"id":"choose-connection-security","title":"Choose connection security","role":"content","text":"The Python `ssl_mode` field and the `--pg-ssl-mode` CLI option accept:\n\n- `disable`\n- `allow`\n- `prefer` (default)\n- `require`\n- `verify-ca`\n- `verify-full`\n\nThe public registration contract doesn't accept explicit client-certificate or root-certificate fields. Configure the required trust material in the Feature Form deployment environment."},{"id":"grant-workload-permissions","title":"Grant workload permissions","role":"content","text":"The registration health check connects to PostgreSQL and queries its version. It proves connectivity and authentication, but it doesn't prove that later workloads can read or write their tables.\n\n| Workload | Required capability |\n| --- | --- |\n| Discover or read an existing table | Connect and `SELECT` on the referenced table |\n| Run transformations | Read source tables and create managed schema and table objects |\n| Replace managed outputs | Create, rename, update, insert into, and drop managed tables |\n| Materialize training sets or feature views | Read inputs and create or update the corresponding managed outputs |\n\nFeature Form-managed transformation tables use the `ff_transforms` schema through the documented gRPC registration path. The server creates the schema when required. This path doesn't expose a custom managed-schema value."},{"id":"understand-supported-workloads","title":"Understand supported workloads","role":"content","text":"| Capability | Support |\n| --- | --- |\n| Existing PostgreSQL datasets | Supported |\n| Full SQL transformations | Supported |\n| Incremental SQL transformations | Supported |\n| Training-set materialization | Supported |\n| Feature-view batch computation | Supported with a separate online provider |\n| Direct dataframe reads | Supported |\n| Spark reads through Java Database Connectivity (JDBC) | Supported |\n| Online serving | Not supported |"},{"id":"verify-registration","title":"Verify registration","role":"content","text":"[code example]\n\nIn Python, use `providers.get(\"<postgres-provider-name>\")` or `providers.list()`."},{"id":"update-safely","title":"Update safely","role":"content","text":"| Field | Update behavior |\n| --- | --- |\n| Database | Immutable |\n| Host and port | Requires `force=True` or `--force` |\n| Username, password reference, and `ssl_mode` | Mutable |\n\nBefore a forced update or deletion, review datasets, transformations, training sets, feature views, and other references to the provider. The usage check doesn't detect every indirect reference."},{"id":"troubleshoot-registration","title":"Troubleshoot registration","role":"errors","text":"| Symptom | What to check |\n| --- | --- |\n| Authentication is required | Supply both `username` and `password_secret` in Python, or both `--pg-username` and `--pg-password-secret` with the CLI |\n| Password resolution fails | Confirm the secret provider, workspace, and canonical reference syntax |\n| Connection is refused or times out | Check Domain Name System (DNS), routing, port, PostgreSQL host-based authentication, connection security, and credentials from the Feature Form server network |\n| Registration succeeds but a transformation fails | Grant the required source-table and `ff_transforms` permissions |\n| A dataset table isn't found | Check the provider database and the dataset's schema and table location |\n| A custom schema setting has no effect | Use `ff_transforms`; the documented gRPC registration path doesn't expose a managed-schema option |"}],"examples":[{"id":"before-you-begin-ex0","language":"python","code":"import featureform as ff\n\nclient = ff.Client.from_env()\nproviders = client.providers(\"<workspace-id>\")","section_id":"before-you-begin"},{"id":"register-postgresql-ex0","language":"python","code":"from featureform.types import EnvSecretRef, PostgresConfig, ProviderType, SSLMode\n\nproviders.register(\n    name=\"<postgres-provider-name>\",\n    provider_type=ProviderType.POSTGRES,\n    config=PostgresConfig(\n        host=\"<postgres-host>\",\n        port=5432,\n        database=\"<database-name>\",\n        username=\"<username>\",\n        password_secret=EnvSecretRef(name=\"PG_PASSWORD\"),\n        ssl_mode=SSLMode.REQUIRE,\n    ),\n)","section_id":"register-postgresql"},{"id":"register-postgresql-ex1","language":"bash","code":"ff provider register <postgres-provider-name> \\\n  --workspace <workspace-id> \\\n  --type postgres \\\n  --pg-host <postgres-host> \\\n  --pg-port 5432 \\\n  --pg-database <database-name> \\\n  --pg-username <username> \\\n  --pg-password-secret env:PG_PASSWORD \\\n  --pg-ssl-mode require","section_id":"register-postgresql"},{"id":"verify-registration-ex0","language":"bash","code":"ff provider get <postgres-provider-name> --workspace <workspace-id>\nff provider list --workspace <workspace-id>","section_id":"verify-registration"}]}
{"schema_version":2,"id":"develop/ai/featureform/register-providers/redis","title":"Register Redis providers","url":"https://redis.io/docs/latest/develop/ai/featureform/register-providers/redis/","summary":"Register standalone Redis and Redis Cluster online-store providers with Redis Feature Form.","tags":[],"last_updated":"2026-09-11T15:54:40+01:00","page_type":"content","content_hash":"f8cc110115906afb6eb674baee876daf75e6d9a11c45fc4bea4b08e33bc6a379","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Register a Redis provider when feature views need an `online-store` for materialization and inference-time reads. A Redis provider doesn't supply offline storage or compute."},{"id":"choose-a-redis-provider-type","title":"Choose a Redis provider type","role":"content","text":"Choose the type that matches the topology of the Redis deployment:\n\n| Provider type | Use it for | Connection configuration | Database |\n| --- | --- | --- | --- |\n| `redis` | A single endpoint that doesn't use Redis Cluster topology discovery | Host and port | Database 0 by default; Python supports databases 0 through 15 |\n| `redis-cluster` | An open source Redis Cluster topology | One or more startup endpoints in `host:port` format | Database 0 |\n\nThe `redis-cluster` provider discovers the cluster topology from its startup endpoints. It doesn't mean every Redis service or deployment with multiple nodes uses the Redis Cluster protocol. Confirm the connection mode and endpoint format for your deployment before choosing the provider type."},{"id":"before-you-begin","title":"Before you begin","role":"content","text":"Make sure you have:\n\n- A Feature Form [workspace](https://redis.io/docs/latest/develop/ai/featureform/manage-workspace).\n- Network access to Redis from the Feature Form server and any compute runtime that materializes feature views.\n- A Redis host and port, or at least one Redis Cluster startup endpoint.\n- A registered [secret provider](https://redis.io/docs/latest/develop/ai/featureform/register-providers#configure-secret-providers) for the Redis password, if authentication is enabled.\n- Certificate material available to each runtime that needs it, if Redis requires TLS or mutual TLS (mTLS).\n\nThe Python examples use this workspace-scoped provider client:\n\n[code example]"},{"id":"choose-authentication","title":"Choose authentication","role":"content","text":"Redis authentication configuration is the same for both provider types:\n\n| Redis authentication | Configuration |\n| --- | --- |\n| No authentication | Omit `username` and `password_secret` |\n| Password only | Set `password_secret` and omit `username` |\n| Access control list (ACL) | Set both `username` and `password_secret` |\n\nFeature Form rejects a username without a password secret. Use a secret reference such as `env:REDIS_PASSWORD`; don't put a password value in the provider definition. An environment reference resolves in the Feature Form server environment, not in the shell where you run the CLI."},{"id":"register-standalone-redis","title":"Register standalone Redis","role":"content","text":"This example uses Redis ACL authentication:\n\n**Python:**\n\n[code example]\n\n**ff CLI:**\n\n[code example]\n\n\n\nOmit the username and password fields for a deployment without authentication. For password-only authentication, omit only the username.\n\nStandalone Redis uses database 0 by default. The Python client also supports databases 0 through 15 by setting `database` in `RedisConfig`. The CLI doesn't currently expose a database option, so CLI registration uses database 0."},{"id":"register-redis-cluster","title":"Register Redis Cluster","role":"content","text":"Supply one or more startup endpoints. They aren't required to list every node because the provider discovers the cluster topology.\n\n**Python:**\n\n[code example]\n\n**ff CLI:**\n\n[code example]\n\n\n\nWith the CLI, pass all startup endpoints as one comma-delimited `--redis-cluster-startup-endpoints` value. Don't repeat the flag. Redis Cluster uses database 0."},{"id":"configure-tls","title":"Configure TLS","role":"configuration","text":"Set a TLS mode when the Redis deployment requires an encrypted connection:\n\n| Mode | Behavior | Required certificate configuration |\n| --- | --- | --- |\n| `disabled` | Plaintext connection | None |\n| `enabled` | TLS using the runtime's trust store and normal hostname verification | None; `insecure_skip_verify` is available only with this mode for development or testing |\n| `verify-ca` | TLS with a supplied CA certificate; verifies the certificate chain but not the hostname | CA certificate path or secret |\n| `verify-full` | TLS with a supplied CA certificate; verifies the certificate chain and hostname | CA certificate path or secret |\n| `mtls` | Full server verification plus a client certificate and key | CA certificate, client certificate, and client key, each as a path or secret |\n\nTLS 1.2 is the default minimum. Set the minimum version to `1.2` or `1.3` when you need to make it explicit. For `verify-full` and `mtls`, set `server_name` when the name used for certificate verification differs from the connection host.\n\nThis mTLS example uses file paths so the same configuration can be used by Feature Form and a Spark materialization runtime:\n\n**Python:**\n\n[code example]\n\n**ff CLI:**\n\n[code example]\n\n\n\nCertificate paths are resolved by the Feature Form server, not by the machine where you run the CLI. If Spark materializes the feature view, mount the same paths in the Spark runtime. Direct Feature Form connections can instead use the certificate secret fields, but Spark materialization doesn't project secret-backed Redis TLS certificates; use runtime-visible file paths for that workflow."},{"id":"use-redis-with-databricks","title":"Use Redis with Databricks","role":"content","text":"The Feature Form server and Databricks compute must both be able to reach the Redis endpoint.\n\nFor both `job_cluster` and `existing_cluster` Spark materialization, configure `password_secret` as a Databricks secret reference. With `job_cluster`, Feature Form adds the reference to the job environment so Databricks can resolve it. With `existing_cluster`, the cluster resolves the scope and key directly. An environment, Kubernetes, Vault, or AWS secret reference can't be projected to either Databricks compute target. First register the Databricks secret provider, then use this reference format:\n\n[code example]\n\nSee [Connect Databricks compute to Redis](https://redis.io/docs/latest/develop/ai/featureform/register-providers/databricks#connect-databricks-compute-to-redis) for equivalent Python and CLI examples and the Databricks secret bootstrap requirements.\n\nRedis TLS certificate paths must also be mounted at the configured locations in the Databricks runtime. Secret-backed Redis TLS certificate fields aren't projected to Spark."},{"id":"configuration-reference","title":"Configuration reference","role":"configuration","text":""},{"id":"standalone-redis","title":"Standalone Redis","role":"content","text":"| Python field | CLI flag | Requirement or default |\n| --- | --- | --- |\n| `RedisConfig.host` | `--redis-host` | Required hostname or IP address |\n| `RedisConfig.port` | `--redis-port` | Defaults to `6379` |\n| `RedisConfig.database` | Not available | Defaults to `0`; Python accepts 0 through 15 |\n| `RedisConfig.username` | `--redis-username` | Optional; requires a password secret |\n| `RedisConfig.password_secret` | `--redis-password-secret` | Optional secret reference |"},{"id":"redis-cluster","title":"Redis Cluster","role":"content","text":"| Python field | CLI flag | Requirement or default |\n| --- | --- | --- |\n| `RedisClusterConfig.endpoints` | `--redis-cluster-startup-endpoints` | Required; CLI accepts one comma-delimited value |\n| `RedisClusterConfig.username` | `--redis-username` | Optional; requires a password secret |\n| `RedisClusterConfig.password_secret` | `--redis-password-secret` | Optional secret reference |"},{"id":"redis-tls","title":"Redis TLS","role":"content","text":"| Python field | CLI flag | Requirement or default |\n| --- | --- | --- |\n| `RedisTLSConfig.mode` | `--redis-tls-mode` | `disabled`, `enabled`, `verify-ca`, `verify-full`, or `mtls` |\n| `ca_cert_path` | `--redis-tls-ca-cert-path` | Server-visible CA certificate path |\n| `ca_cert_secret` | `--redis-tls-ca-cert-secret` | CA certificate secret reference; alternative to a path |\n| `server_name` | `--redis-tls-server-name` | Optional TLS server name override |\n| `client_cert_path` | `--redis-tls-client-cert-path` | Client certificate path for mTLS |\n| `client_cert_secret` | `--redis-tls-client-cert-secret` | Client certificate secret reference for mTLS |\n| `client_key_path` | `--redis-tls-client-key-path` | Client key path for mTLS |\n| `client_key_secret` | `--redis-tls-client-key-secret` | Client key secret reference for mTLS |\n| `insecure_skip_verify` | `--redis-tls-insecure-skip-verify` | Optional; accepted only with `enabled` |\n| `min_version` | `--redis-tls-min-version` | Defaults to `1.2`; accepts `1.2` or `1.3` |"},{"id":"verify-registration","title":"Verify registration","role":"content","text":"[code example]\n\nIn Python, use `providers.get(\"<redis-provider-name>\")` or `providers.list()`.\n\nRegistration resolves the configured secrets, builds the Redis client, and sends a ping before persisting the provider. Keep this health check enabled so endpoint, authentication, TLS, and network errors fail during registration. `--skip-health-check` or `skip_health_check=True` skips only the registration-time check; it doesn't disable recurring monitoring."},{"id":"update-safely","title":"Update safely","role":"content","text":"| Field | Update behavior |\n| --- | --- |\n| Standalone database | Immutable |\n| Standalone host and port | Requires `force=True` or `--force` |\n| Cluster startup endpoints | Requires `force=True` or `--force` |\n| Username and password secret | Mutable |\n| TLS mode, certificate paths, server name, verification settings, and minimum version | Requires `force=True` or `--force` |\n| TLS certificate secret references | Mutable |\n\nChanging endpoints or TLS settings can interrupt materialization and serving if the new configuration isn't reachable from every runtime. Test connectivity from the Feature Form server and compute runtime before forcing the update."},{"id":"troubleshoot-registration","title":"Troubleshoot registration","role":"errors","text":"| Symptom | What to check |\n| --- | --- |\n| Redis ping fails | Confirm the endpoint, route, firewall rules, authentication, and TLS settings from the Feature Form server environment |\n| Authentication validation fails | Supply a password secret when a username is set; omit the username for password-only authentication |\n| Redis Cluster registration rejects endpoints | Pass `host:port` entries as one comma-delimited CLI value, with no empty entries or repeated flag |\n| TLS verification fails | Check the TLS mode, CA chain, server name, minimum TLS version, and whether certificate paths exist in the runtime |\n| Spark rejects secret-backed TLS certificates | Mount certificate files in the Spark runtime and use the TLS path fields |\n| Existing Databricks cluster can't resolve the Redis password | Use a Databricks secret reference from a registered `databricks-secret` provider |\n\nSee [Serve features](https://redis.io/docs/latest/develop/ai/featureform/serve-features) for the serving workflow. To provision a managed deployment, see the [Redis Cloud quick start](https://redis.io/docs/latest/operate/rc/rc-quickstart)."}],"examples":[{"id":"before-you-begin-ex0","language":"python","code":"import featureform as ff\n\nclient = ff.Client.from_env()\nproviders = client.providers(\"<workspace-id>\")","section_id":"before-you-begin"},{"id":"register-standalone-redis-ex0","language":"python","code":"from featureform.types import EnvSecretRef, ProviderType, RedisConfig\n\nproviders.register(\n    name=\"<redis-provider-name>\",\n    provider_type=ProviderType.REDIS,\n    config=RedisConfig(\n        host=\"<redis-host>\",\n        port=int(\"<redis-port>\"),\n        username=\"<redis-username>\",\n        password_secret=EnvSecretRef(name=\"REDIS_PASSWORD\"),\n    ),\n)","section_id":"register-standalone-redis"},{"id":"register-standalone-redis-ex1","language":"bash","code":"ff provider register <redis-provider-name> \\\n  --workspace <workspace-id> \\\n  --type redis \\\n  --redis-host <redis-host> \\\n  --redis-port <redis-port> \\\n  --redis-username <redis-username> \\\n  --redis-password-secret env:REDIS_PASSWORD","section_id":"register-standalone-redis"},{"id":"register-redis-cluster-ex0","language":"python","code":"from featureform.types import EnvSecretRef, ProviderType, RedisClusterConfig\n\nproviders.register(\n    name=\"<redis-cluster-provider-name>\",\n    provider_type=ProviderType.REDIS_CLUSTER,\n    config=RedisClusterConfig(\n        endpoints=[\n            \"<redis-host-1>:<redis-port>\",\n            \"<redis-host-2>:<redis-port>\",\n        ],\n        username=\"<redis-username>\",\n        password_secret=EnvSecretRef(name=\"REDIS_PASSWORD\"),\n    ),\n)","section_id":"register-redis-cluster"},{"id":"register-redis-cluster-ex1","language":"bash","code":"ff provider register <redis-cluster-provider-name> \\\n  --workspace <workspace-id> \\\n  --type redis-cluster \\\n  --redis-cluster-startup-endpoints <redis-host-1>:<redis-port>,<redis-host-2>:<redis-port> \\\n  --redis-username <redis-username> \\\n  --redis-password-secret env:REDIS_PASSWORD","section_id":"register-redis-cluster"},{"id":"configure-tls-ex0","language":"python","code":"from featureform.types import (\n    EnvSecretRef,\n    ProviderType,\n    RedisConfig,\n    RedisTLSConfig,\n    RedisTLSMode,\n)\n\nproviders.register(\n    name=\"<redis-provider-name>\",\n    provider_type=ProviderType.REDIS,\n    config=RedisConfig(\n        host=\"<redis-host>\",\n        port=int(\"<redis-port>\"),\n        username=\"<redis-username>\",\n        password_secret=EnvSecretRef(name=\"REDIS_PASSWORD\"),\n        tls=RedisTLSConfig(\n            mode=RedisTLSMode.MTLS,\n            ca_cert_path=\"<runtime-visible-ca-cert-path>\",\n            server_name=\"<redis-server-name>\",\n            client_cert_path=\"<runtime-visible-client-cert-path>\",\n            client_key_path=\"<runtime-visible-client-key-path>\",\n            min_version=\"1.2\",\n        ),\n    ),\n)","section_id":"configure-tls"},{"id":"configure-tls-ex1","language":"bash","code":"ff provider register <redis-provider-name> \\\n  --workspace <workspace-id> \\\n  --type redis \\\n  --redis-host <redis-host> \\\n  --redis-port <redis-port> \\\n  --redis-username <redis-username> \\\n  --redis-password-secret env:REDIS_PASSWORD \\\n  --redis-tls-mode mtls \\\n  --redis-tls-ca-cert-path <runtime-visible-ca-cert-path> \\\n  --redis-tls-server-name <redis-server-name> \\\n  --redis-tls-client-cert-path <runtime-visible-client-cert-path> \\\n  --redis-tls-client-key-path <runtime-visible-client-key-path> \\\n  --redis-tls-min-version 1.2","section_id":"configure-tls"},{"id":"use-redis-with-databricks-ex0","language":"text","code":"databricks@<databricks-secret-provider-name>:<secret-scope>#<redis-password-key>","section_id":"use-redis-with-databricks"},{"id":"verify-registration-ex0","language":"bash","code":"ff provider get <redis-provider-name> --workspace <workspace-id>\nff provider list --workspace <workspace-id>","section_id":"verify-registration"}]}
{"schema_version":2,"id":"develop/ai/featureform/register-providers/s3","title":"Register an S3 provider","url":"https://redis.io/docs/latest/develop/ai/featureform/register-providers/s3/","summary":"Register Amazon S3 and S3-compatible object storage with Redis Feature Form.","tags":[],"last_updated":"2026-09-11T15:54:40+01:00","page_type":"content","content_hash":"a3546d8270524b32ceb333da8c321f7f9819057f930e2c849600deb8a46e1c2c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Register an `s3` provider when Redis Feature Form needs a named Amazon Simple Storage Service (S3) bucket and prefix for supported batch-data or Spark workflows.\n\nThe provider fills the `offline-store` role. It isn't a compute engine, catalog, or online serving provider. Each resource and compute provider still determines whether it accepts S3."},{"id":"before-you-begin","title":"Before you begin","role":"content","text":"Make sure you have:\n\n- A Feature Form [workspace](https://redis.io/docs/latest/develop/ai/featureform/manage-workspace).\n- An existing bucket and Amazon Web Services (AWS) region.\n- Network access from the Feature Form server and relevant workers to the S3 endpoint.\n- Either a usable default AWS credential chain in the Feature Form runtime or access-key credentials in a registered [secret provider](https://redis.io/docs/latest/develop/ai/featureform/register-providers#configure-secret-providers).\n- Permission to write, read, and delete objects under the configured prefix.\n\nPass the bucket name without an `s3://` prefix. Set `path_prefix` in Python, or use `--s3-path-prefix` with the CLI, for a directory-like prefix within the bucket.\n\nThe Python examples use this workspace-scoped provider client:\n\n[code example]"},{"id":"choose-authentication","title":"Choose authentication","role":"content","text":"If you omit the Python access-key fields or the corresponding CLI flags, Feature Form uses the runtime's default AWS credential chain. This is the usual choice when the server runs with an assigned identity.\n\nIn Python, set both `access_key_id_secret` and `secret_access_key_secret`. With the CLI, supply both references:\n\n[code example]\n\nFeature Form rejects a configuration that supplies only one member of the pair."},{"id":"register-amazon-s3","title":"Register Amazon S3","role":"content","text":"This example uses the runtime's default AWS credential chain:\n\n**Python:**\n\n[code example]\n\n**ff CLI:**\n\n[code example]\n\n\n\nTo use access-key secret references instead:\n\n**Python:**\n\n[code example]\n\n**ff CLI:**\n\n[code example]\n\n\n\nThe environment references are resolved by the Feature Form server. Replace them with references to another registered secret backend when appropriate."},{"id":"register-s3-compatible-storage","title":"Register S3-compatible storage","role":"content","text":"Set a full Hypertext Transfer Protocol (HTTP) or secure HTTP (HTTPS) endpoint and enable path-style access for an S3-compatible service:\n\n**Python:**\n\n[code example]\n\n**ff CLI:**\n\n[code example]\n\n\n\nThe endpoint must include `http://` or `https://` and a hostname."},{"id":"grant-health-check-permissions","title":"Grant health-check permissions","role":"content","text":"The default registration health check writes a small object under the configured prefix, reads it back, and deletes it. The identity needs permission for all three operations.\n\nUse `skip_health_check=True` in Python or `--skip-health-check` with the CLI only to defer this validation intentionally. A read-only bucket can't pass the default check and won't support workflows that need to publish or clean up objects."},{"id":"use-s3-with-spark","title":"Use S3 with Spark","role":"content","text":"A generic Spark provider can refer to an S3 provider for two separate purposes:\n\n- Hadoop S3A access supplies the region, endpoint, path style, and credentials used to read or write S3 data.\n- Remote execution staging publishes temporary configuration and runtime assets for the Spark driver.\n\nRegister the S3 provider before the Spark provider that refers to it. Remote Spark staging currently requires an S3 provider configured with access-key authentication. A provider that relies on the default AWS credential chain can still be used for supported S3A data access, but not for this staging path.\n\nKeep staging and long-lived data in separate prefixes or providers when they need different permissions or retention policies."},{"id":"verify-registration","title":"Verify registration","role":"content","text":"[code example]\n\nIn Python, use `providers.get(\"<s3-provider-name>\")` or `providers.list()`."},{"id":"update-safely","title":"Update safely","role":"content","text":"| Field | Update behavior |\n| --- | --- |\n| Bucket | Immutable |\n| Region, endpoint, and path-style access | Requires `force=True` or `--force` |\n| Path prefix and credential references | Mutable |\n\nChanging a prefix changes where later operations read and write. It doesn't move existing objects. Before updating or deleting the provider, review every dataset, catalog, compute, and staging configuration that refers to it."},{"id":"troubleshoot-registration","title":"Troubleshoot registration","role":"errors","text":"| Symptom | What to check |\n| --- | --- |\n| Bucket validation fails | Pass a bucket name without `s3://`; put the remaining path in `path_prefix` or `--s3-path-prefix` |\n| Credential validation fails | Supply both access-key secret references or omit both to use the default AWS credential chain |\n| Health check is denied | Grant write, read, and delete permission under the configured prefix |\n| A custom endpoint can't be reached | Check its scheme, hostname, network route, certificate trust, and path-style behavior from the Feature Form server |\n| Spark can't access data | Confirm that the Spark provider refers to this S3 provider for Hadoop S3A access and that the runtime has the required credentials |\n| Remote Spark staging rejects the provider | Configure the S3 provider with access-key secret references; staging doesn't use default-chain authentication |"}],"examples":[{"id":"before-you-begin-ex0","language":"python","code":"import featureform as ff\n\nclient = ff.Client.from_env()\nproviders = client.providers(\"<workspace-id>\")","section_id":"before-you-begin"},{"id":"choose-authentication-ex0","language":"text","code":"--s3-access-key-id-secret env:AWS_ACCESS_KEY_ID\n--s3-secret-access-key-secret env:AWS_SECRET_ACCESS_KEY","section_id":"choose-authentication"},{"id":"register-amazon-s3-ex0","language":"python","code":"from featureform.types import ProviderType, S3Config\n\nproviders.register(\n    name=\"<s3-provider-name>\",\n    provider_type=ProviderType.S3,\n    config=S3Config(\n        bucket=\"<bucket-name>\",\n        region=\"<aws-region>\",\n        path_prefix=\"<path-prefix>\",\n    ),\n)","section_id":"register-amazon-s3"},{"id":"register-amazon-s3-ex1","language":"bash","code":"ff provider register <s3-provider-name> \\\n  --workspace <workspace-id> \\\n  --type s3 \\\n  --s3-bucket <bucket-name> \\\n  --s3-region <aws-region> \\\n  --s3-path-prefix <path-prefix>","section_id":"register-amazon-s3"},{"id":"register-amazon-s3-ex2","language":"python","code":"from featureform.types import EnvSecretRef, ProviderType, S3Config\n\nproviders.register(\n    name=\"<s3-provider-name>\",\n    provider_type=ProviderType.S3,\n    config=S3Config(\n        bucket=\"<bucket-name>\",\n        region=\"<aws-region>\",\n        path_prefix=\"<path-prefix>\",\n        access_key_id_secret=EnvSecretRef(name=\"AWS_ACCESS_KEY_ID\"),\n        secret_access_key_secret=EnvSecretRef(name=\"AWS_SECRET_ACCESS_KEY\"),\n    ),\n)","section_id":"register-amazon-s3"},{"id":"register-amazon-s3-ex3","language":"bash","code":"ff provider register <s3-provider-name> \\\n  --workspace <workspace-id> \\\n  --type s3 \\\n  --s3-bucket <bucket-name> \\\n  --s3-region <aws-region> \\\n  --s3-path-prefix <path-prefix> \\\n  --s3-access-key-id-secret env:AWS_ACCESS_KEY_ID \\\n  --s3-secret-access-key-secret env:AWS_SECRET_ACCESS_KEY","section_id":"register-amazon-s3"},{"id":"register-s3-compatible-storage-ex0","language":"python","code":"from featureform.types import EnvSecretRef, ProviderType, S3Config\n\nproviders.register(\n    name=\"<s3-provider-name>\",\n    provider_type=ProviderType.S3,\n    config=S3Config(\n        bucket=\"<bucket-name>\",\n        region=\"<region>\",\n        endpoint=\"https://<object-storage-host>\",\n        path_style_access=True,\n        access_key_id_secret=EnvSecretRef(name=\"S3_ACCESS_KEY_ID\"),\n        secret_access_key_secret=EnvSecretRef(name=\"S3_SECRET_ACCESS_KEY\"),\n    ),\n)","section_id":"register-s3-compatible-storage"},{"id":"register-s3-compatible-storage-ex1","language":"bash","code":"ff provider register <s3-provider-name> \\\n  --workspace <workspace-id> \\\n  --type s3 \\\n  --s3-bucket <bucket-name> \\\n  --s3-region <region> \\\n  --s3-endpoint https://<object-storage-host> \\\n  --s3-path-style-access \\\n  --s3-access-key-id-secret env:S3_ACCESS_KEY_ID \\\n  --s3-secret-access-key-secret env:S3_SECRET_ACCESS_KEY","section_id":"register-s3-compatible-storage"},{"id":"verify-registration-ex0","language":"bash","code":"ff provider get <s3-provider-name> --workspace <workspace-id>\nff provider list --workspace <workspace-id>","section_id":"verify-registration"}]}
{"schema_version":2,"id":"develop/ai/featureform/register-providers/snowflake","title":"Register a Snowflake provider","url":"https://redis.io/docs/latest/develop/ai/featureform/register-providers/snowflake/","summary":"Register Snowflake storage and compute with Redis Feature Form.","tags":[],"last_updated":"2026-09-11T15:54:40+01:00","page_type":"content","content_hash":"73c9f7eb1ccd2e9838b3763dc21b893fd2d7a2e162bd0a5e4e3096581812da24","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Register a `snowflake` provider when Redis Feature Form should discover Snowflake tables, run structured query language (SQL) transformations, or materialize training sets and feature-view data in Snowflake.\n\nThe provider fills the `offline-store` and `compute` roles for one account, warehouse, database, and schema. It doesn't provide online serving. A serving feature view also needs a supported `online-store` provider."},{"id":"before-you-begin","title":"Before you begin","role":"content","text":"Make sure you have:\n\n- A Feature Form [workspace](https://redis.io/docs/latest/develop/ai/featureform/manage-workspace).\n- A Snowflake account identifier, warehouse, database, and schema.\n- A Snowflake username with password or key-pair authentication.\n- A [secret provider](https://redis.io/docs/latest/develop/ai/featureform/register-providers#configure-secret-providers) for each password, private key, or passphrase reference.\n- Network access from the Feature Form server and workers to Snowflake.\n\nSet `host` on `SnowflakeConfig`, or use `--snowflake-host` with the CLI, for a PrivateLink or custom hostname. Without it, Feature Form derives the standard hostname from the account identifier.\n\nThe Python examples use this workspace-scoped provider client:\n\n[code example]"},{"id":"choose-authentication","title":"Choose authentication","role":"content","text":"| Authentication | Required values | Support |\n| --- | --- | --- |\n| `password` | Username and password secret reference | Supported |\n| `key-pair` | Username and private-key secret reference; passphrase reference for an encrypted key | Supported |\n| `oauth` | OAuth client and token fields | Not supported by the current Snowflake connector |\n\nDon't use `skip_health_check=True` or `--skip-health-check` to register OAuth. Skipping the check postpones the unsupported-authentication error until the provider is used."},{"id":"register-with-a-password","title":"Register with a password","role":"content","text":"**Python:**\n\n[code example]\n\n**ff CLI:**\n\n[code example]\n\n\n\nThe environment reference is resolved by the Feature Form server. Replace it with a reference to another registered secret backend when appropriate."},{"id":"register-with-a-key-pair","title":"Register with a key pair","role":"content","text":"**Python:**\n\n[code example]\n\n**ff CLI:**\n\n[code example]\n\n\n\nOmit `--snowflake-passphrase-secret` when the private key isn't encrypted. The resolved value must be a Rivest-Shamir-Adleman (RSA) private key."},{"id":"configure-sessions","title":"Configure sessions","role":"configuration","text":"In Python, set `login_timeout`, `request_timeout`, or `session_params` on `SnowflakeConfig`. With the CLI, use these optional flags:\n\n[code example]\n\nRepeat `--snowflake-session-param` for multiple values. Keys and values must be nonempty."},{"id":"grant-health-check-permissions","title":"Grant health-check permissions","role":"content","text":"The default registration health check:\n\n1. Opens a Snowflake session.\n2. Selects the configured warehouse.\n3. Creates a health-check table in the configured database and schema.\n4. Drops the table.\n\nThe Snowflake role therefore needs `USAGE` on the warehouse, database, and schema, plus permission to create and drop a table in that schema. Read-only credentials can't pass the default check.\n\nWorkloads can require additional permissions:\n\n| Workload | Required capability |\n| --- | --- |\n| Discover or read an existing table | `SELECT` on that table |\n| Materialize transformations, training sets, or feature views | Read inputs and create, replace, rename, update, and drop managed tables as needed |\n| Delete a managed output | Drop the managed table |\n\nA successful registration check doesn't test every source table, access policy, masking policy, or warehouse quota."},{"id":"understand-supported-workloads","title":"Understand supported workloads","role":"content","text":"| Capability | Support |\n| --- | --- |\n| Existing Snowflake datasets | Supported |\n| Full SQL transformations | Supported |\n| Incremental transformed datasets | Not supported |\n| Static training-set materialization | Supported |\n| Incremental static training sets | Supported |\n| Feature-view batch computation | Supported with a separate online provider |\n| Direct dataframe reads | Supported |\n| Online serving | Not supported |\n\nKey-pair authentication works for direct Snowflake dataframe reads. A Spark-based Snowflake Java Database Connectivity (JDBC) plan currently requires password authentication."},{"id":"verify-registration","title":"Verify registration","role":"content","text":"[code example]\n\nIn Python, use `providers.get(\"<snowflake-provider-name>\")` or `providers.list()`."},{"id":"update-safely","title":"Update safely","role":"content","text":"| Field | Update behavior |\n| --- | --- |\n| Database and schema | Immutable |\n| Account and custom host | Requires `force=True` or `--force` |\n| Warehouse, role, authentication, timeouts, and session parameters | Mutable |\n\nBefore a forced update or deletion, review datasets, transformations, training sets, feature views, and other references to the provider. The usage check doesn't detect every indirect reference."},{"id":"troubleshoot-registration","title":"Troubleshoot registration","role":"errors","text":"| Symptom | What to check |\n| --- | --- |\n| Secret resolution fails | Confirm the secret-provider name, workspace, and canonical reference syntax |\n| `USE WAREHOUSE` fails | Grant warehouse `USAGE`, or correct the warehouse and role |\n| Health-check table creation fails | Grant create and drop permissions in the configured schema |\n| Key-pair authentication fails | Check that the resolved value is an RSA private key and that an encrypted key has the matching passphrase |\n| Incremental transformation fails | Use a full Snowflake SQL transformation; incremental transformed datasets aren't supported |\n| Direct reads work but a Spark read fails | Use password authentication for the Spark JDBC path |\n| A workload fails after registration | Check grants and policies for the specific table and operation; the health check tests only the configured schema |"}],"examples":[{"id":"before-you-begin-ex0","language":"python","code":"import featureform as ff\n\nclient = ff.Client.from_env()\nproviders = client.providers(\"<workspace-id>\")","section_id":"before-you-begin"},{"id":"register-with-a-password-ex0","language":"python","code":"from featureform.types import EnvSecretRef, ProviderType, SnowflakeConfig\n\nproviders.register(\n    name=\"<snowflake-provider-name>\",\n    provider_type=ProviderType.SNOWFLAKE,\n    config=SnowflakeConfig(\n        account=\"<snowflake-account>\",\n        warehouse=\"<warehouse-name>\",\n        database=\"<database-name>\",\n        schema=\"<schema-name>\",\n        role=\"<role-name>\",\n        auth_type=\"password\",\n        username=\"<username>\",\n        password_secret=EnvSecretRef(name=\"SNOWFLAKE_PASSWORD\"),\n    ),\n)","section_id":"register-with-a-password"},{"id":"register-with-a-password-ex1","language":"bash","code":"ff provider register <snowflake-provider-name> \\\n  --workspace <workspace-id> \\\n  --type snowflake \\\n  --snowflake-account <snowflake-account> \\\n  --snowflake-warehouse <warehouse-name> \\\n  --snowflake-database <database-name> \\\n  --snowflake-schema <schema-name> \\\n  --snowflake-role <role-name> \\\n  --snowflake-auth-type password \\\n  --snowflake-username <username> \\\n  --snowflake-password-secret env:SNOWFLAKE_PASSWORD","section_id":"register-with-a-password"},{"id":"register-with-a-key-pair-ex0","language":"python","code":"from featureform.types import K8sSecretRef, ProviderType, SnowflakeConfig\n\nproviders.register(\n    name=\"<snowflake-provider-name>\",\n    provider_type=ProviderType.SNOWFLAKE,\n    config=SnowflakeConfig(\n        account=\"<snowflake-account>\",\n        warehouse=\"<warehouse-name>\",\n        database=\"<database-name>\",\n        schema=\"<schema-name>\",\n        role=\"<role-name>\",\n        auth_type=\"key-pair\",\n        username=\"<username>\",\n        private_key_secret=K8sSecretRef(\n            provider_name=\"<secret-provider-name>\",\n            name=\"<secret-name>\",\n            key=\"private-key\",\n        ),\n        passphrase_secret=K8sSecretRef(\n            provider_name=\"<secret-provider-name>\",\n            name=\"<secret-name>\",\n            key=\"private-key-passphrase\",\n        ),\n    ),\n)","section_id":"register-with-a-key-pair"},{"id":"register-with-a-key-pair-ex1","language":"bash","code":"ff provider register <snowflake-provider-name> \\\n  --workspace <workspace-id> \\\n  --type snowflake \\\n  --snowflake-account <snowflake-account> \\\n  --snowflake-warehouse <warehouse-name> \\\n  --snowflake-database <database-name> \\\n  --snowflake-schema <schema-name> \\\n  --snowflake-role <role-name> \\\n  --snowflake-auth-type key-pair \\\n  --snowflake-username <username> \\\n  --snowflake-private-key-secret k8s@<secret-provider-name>:<secret-name>#private-key \\\n  --snowflake-passphrase-secret k8s@<secret-provider-name>:<secret-name>#private-key-passphrase","section_id":"register-with-a-key-pair"},{"id":"configure-sessions-ex0","language":"text","code":"--snowflake-login-timeout <seconds>\n--snowflake-request-timeout <seconds>\n--snowflake-session-param <key>=<value>","section_id":"configure-sessions"},{"id":"verify-registration-ex0","language":"bash","code":"ff provider get <snowflake-provider-name> --workspace <workspace-id>\nff provider list --workspace <workspace-id>","section_id":"verify-registration"}]}
{"schema_version":2,"id":"develop/ai/featureform/serve-features","title":"Serve features","url":"https://redis.io/docs/latest/develop/ai/featureform/serve-features/","summary":"Read materialized features from a Redis Feature Form feature view at inference time.","tags":[],"last_updated":"2026-09-11T15:54:40+01:00","page_type":"content","content_hash":"bfb42bf5439c57ddff2d6da24825c7d8c0cd430723ed92e48ddd22899cb25abc","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Read materialized features from a feature view by calling `ff.Client.serve(...)`. This page assumes the feature view exists and the online store is ready — the [Quickstart](https://redis.io/docs/latest/develop/ai/featureform/quickstart)'s `demo_customer_feature_view` is the canonical example.\n\nThe examples use `<workspace-id>` as the workspace UUID. Get yours with `ff workspace list`. See [Manage workspaces](https://redis.io/docs/latest/develop/ai/featureform/manage-workspace) for the full workspace lifecycle."},{"id":"verify-the-feature-view-exists","title":"Verify the feature view exists","role":"content","text":"[code example]"},{"id":"minimal-python-workflow","title":"Minimal Python workflow","role":"content","text":"[code example]\n\nThe example connects to a local server with TLS off. For a deployed server, change `host` and set `insecure=False`."},{"id":"if-serving-fails","title":"If serving fails","role":"content","text":"- The feature view isn't ready. Confirm with `ff graph feature-view get` and re-check after materialization completes.\n- The online provider is unavailable or not registered. Confirm with `ff provider list --workspace <workspace-id>`.\n- The caller lacks the required RBAC permission. Serving values and reading serving metadata are governed by separate permissions; see the [Permissions table](https://redis.io/docs/latest/develop/ai/featureform/reference#permissions)."}],"examples":[{"id":"verify-the-feature-view-exists-ex0","language":"bash","code":"ff graph feature-view get demo_customer_feature_view --workspace <workspace-id>","section_id":"verify-the-feature-view-exists"},{"id":"minimal-python-workflow-ex0","language":"python","code":"import featureform as ff\n\nclient = ff.Client(host=\"127.0.0.1:9090\", insecure=True, workspace=\"<workspace-id>\")\nfeatures = client.serve(\"demo_customer_feature_view\", entity=\"C1001\")\nprint(features)","section_id":"minimal-python-workflow"}]}
{"schema_version":2,"id":"develop/ai/featureform/update-features","title":"Update features","url":"https://redis.io/docs/latest/develop/ai/featureform/update-features/","summary":"Iterate on a Redis Feature Form definitions file with plan, merge, and re-apply workflows.","tags":[],"last_updated":"2026-09-11T15:54:40+01:00","page_type":"content","content_hash":"ea3ba28a410efdfc82fd31140c6587b7da5059048e6cee65018421416e6f1403","sections":[{"id":"overview","title":"Overview","role":"overview","text":"After the first `ff apply`, most changes will be iterative: edit the definitions file, preview the delta, apply, and verify. For the full apply mechanics and failure modes, see [Define and deploy features](https://redis.io/docs/latest/develop/ai/featureform/define-and-deploy-features)."},{"id":"typical-cycle","title":"Typical cycle","role":"content","text":"1. Change a resource definition.\n2. Run a plan.\n3. Apply the change.\n4. Verify the resulting graph or catalog state.\n\nPreview the change first:\n\n[code example]\n\nThen apply:\n\n[code example]"},{"id":"when-to-use---merge","title":"When to use `--merge`","role":"content","text":"Use `--merge` when your file is intentionally partial and omitted resources should not be treated as deletions."},{"id":"verify-the-outcome","title":"Verify the outcome","role":"content","text":"[code example]"}],"examples":[{"id":"typical-cycle-ex0","language":"bash","code":"ff apply \\\n  --workspace <workspace-id> \\\n  --file examples/featureform/docs/resources.py \\\n  --plan","section_id":"typical-cycle"},{"id":"typical-cycle-ex1","language":"bash","code":"ff apply \\\n  --workspace <workspace-id> \\\n  --file examples/featureform/docs/resources.py \\\n  --wait \\\n  --wait-for finished","section_id":"typical-cycle"},{"id":"verify-the-outcome-ex0","language":"bash","code":"ff graph workspace stats --workspace <workspace-id>\nff catalog list --workspace <workspace-id>","section_id":"verify-the-outcome"}]}
{"schema_version":2,"id":"develop/ai","title":"Redis for AI and search","url":"https://redis.io/docs/latest/develop/ai/","summary":"An overview of Redis for AI and search documentation, including vector search, AI agents, and the Context Engine (Redis Iris) managed services.","tags":["docs","operate","rs","rc"],"last_updated":"2026-10-01T12:49:59-07:00","children":[{"id":"develop/ai/redisvl","summary":"This is the Redis vector library (RedisVL).","title":"RedisVL","url":"https://redis.io/docs/latest/develop/ai/redisvl/"},{"id":"develop/ai/search-and-query","summary":"Searching and querying Redis data using Redis Search","title":"Redis Search","url":"https://redis.io/docs/latest/develop/ai/search-and-query/"},{"id":"develop/ai/when-to-choose-redis","summary":"Determine when Redis is the optimal choice compared to other databases","title":"When to choose Redis over alternatives","url":"https://redis.io/docs/latest/develop/ai/when-to-choose-redis/"},{"id":"develop/ai/context-engine","summary":"Redis Iris is a suite of managed and self-managed services for agent memory, semantic caching, and governed data access.","title":"Redis Iris context engine","url":"https://redis.io/docs/latest/develop/ai/context-engine/"},{"id":"develop/ai/notebook-collection","summary":"This collection showcases how Redis can be integrated into AI workflows to enhance performance, reduce latency, and enable real-time AI applications. Each notebook comes with complete code examples, explanations, and integration guides.","title":"Notebooks collection","url":"https://redis.io/docs/latest/develop/ai/notebook-collection/"},{"id":"develop/ai/agent-builder","summary":"Build custom AI agents powered by Redis with our interactive code generator","title":"AI agent builder","url":"https://redis.io/docs/latest/develop/ai/agent-builder/"},{"id":"develop/ai/ecosystem-integrations","summary":"Explore key integrations between Redis and popular AI frameworks, platforms, and tools.","title":"Redis AI ecosystem integrations","url":"https://redis.io/docs/latest/develop/ai/ecosystem-integrations/"},{"id":"develop/ai/featureform","summary":"Build feature engineering workflows with Redis Feature Form.","title":"Redis Feature Form","url":"https://redis.io/docs/latest/develop/ai/featureform/"},{"id":"develop/ai/ai-videos","summary":"Watch video tutorials and demos showcasing Redis in AI applications, from vector search to RAG implementations.","title":"Redis video tutorial collection","url":"https://redis.io/docs/latest/develop/ai/ai-videos/"}],"page_type":"content","content_hash":"de82fd8af8df39e70562aca1757c2bf7018571839bb225ba531b60921333e1fa","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis stores and indexes vector embeddings that semantically represent unstructured data including text passages, images, videos, or audio. Store vectors and the associated metadata within [hashes](https://redis.io/docs/latest/develop/data-types/hashes) or [JSON](https://redis.io/docs/latest/develop/data-types/json) documents for [indexing](https://redis.io/docs/latest/develop/ai/search-and-query/indexing) and [querying](https://redis.io/docs/latest/develop/ai/search-and-query/query).\n\n<div class=\"grid grid-cols-1 md:grid-cols-3 gap-6 my-8\">\n  \n  \n  \n</div>"},{"id":"what-is-redis-for-ai-and-search","title":"What is Redis for AI and search?","role":"content","text":"Redis is an in-memory data platform purpose-built for the speed and structure that AI applications demand. It stores and indexes vector embeddings alongside structured metadata, enabling semantic search, real-time retrieval, and agent memory at millisecond latency, at any scale.\n\n- **Vector search**: Store and query vector embeddings using KNN and range queries with metadata filters, across hashes and JSON documents\n- **Semantic caching**: Reduce LLM API costs by reusing cached responses for semantically similar prompts with LangCache\n- **Agent memory**: Give agents short-term session memory and long-term persistent memory that survives across interactions\n- **Structured data access**: Turn your business data into governed tools agents can reliably query with Context Retriever\n- **Live data sync**: Stream changes from relational databases into Redis in near real time so agents always work with current information"},{"id":"why-use-redis-for-ai-and-search","title":"Why use Redis for AI and search?","role":"content","text":"<div class=\"grid grid-cols-1 md:grid-cols-2 gap-6 my-6\">\n  <div class=\"p-5 border border-redis-pen-300 rounded-lg\">\n    <h3 class=\"text-redis-ink-900 font-semibold mb-3\">For AI applications</h3>\n    <ul class=\"space-y-1 text-redis-pen-600\">\n      <li>Sub-millisecond vector search at production scale</li>\n      <li>Agents that remember context across sessions and users</li>\n      <li>Lower LLM costs through semantic caching of repeated queries</li>\n      <li>Reliable access to fresh, structured business data for every agent step</li>\n    </ul>\n  </div>\n  <div class=\"p-5 border border-redis-pen-300 rounded-lg\">\n    <h3 class=\"text-redis-ink-900 font-semibold mb-3\">For developers</h3>\n    <ul class=\"space-y-1 text-redis-pen-600\">\n      <li>Redis Search available in Python, JavaScript, Java, Go, .NET, and PHP client libraries</li>\n      <li>Managed Context Engine services: no infrastructure to build or maintain</li>\n      <li>Works with LangChain, LlamaIndex, LangGraph, and other AI frameworks</li>\n      <li>From local development to Redis Cloud with the same API</li>\n    </ul>\n  </div>\n</div>"},{"id":"redis-feature-form","title":"Redis Feature Form","role":"content","text":"Use [Redis Feature Form](https://redis.io/docs/latest/develop/ai/featureform/) to define, manage, and serve machine learning features on top of your existing data systems. The Feature Form docs cover the Python SDK workflow from provider registration through feature serving."},{"id":"ai-agents","title":"AI agents","role":"content","text":"AI agents are autonomous systems that combine LLMs with memory, tools, and planning to accomplish complex, multi-step tasks. Redis powers the core capabilities agents need: fast vector search, persistent memory, real-time data streaming, and structured access to business data.\n\n- [AI agent builder](https://redis.io/docs/latest/develop/ai/agent-builder): Use the interactive code generator to create a working agent in your preferred language with your choice of LLM.\n- [How agents work](https://redis.io/docs/latest/develop/ai/agent-builder/agent-concepts): Learn the agent processing cycle, memory architecture, and why Redis is the foundation for production agents.\n- [Context Engine](https://redis.io/docs/latest/develop/ai/context-engine): The managed service suite that gives agents what they need: semantic caching, persistent memory, structured data access, and live data integration."},{"id":"context-engine-services","title":"Context Engine services","role":"content","text":"The [Context Engine](https://redis.io/docs/latest/develop/ai/context-engine) (Redis Iris) includes four services, available fully managed on Redis Cloud or self-managed on your own infrastructure:\n\n- [LangCache](https://redis.io/docs/latest/develop/ai/context-engine/langcache): Semantic caching that reduces LLM API costs and improves response times by reusing cached responses for similar queries.\n- [Redis Agent Memory](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory): Two-tier persistent memory (session and long-term) for agents, available through Python and TypeScript SDKs and a REST API.\n- [Context Retriever](https://redis.io/docs/latest/develop/ai/context-engine/context-retriever): Turns your business data into structured, governed tools that agents can reliably use, defined once and reused across all agents.\n- [Data Integration](https://redis.io/docs/latest/develop/ai/context-engine/data-integration): Keeps your Redis Cloud database in sync with relational databases in near real time using Change Data Capture."},{"id":"how-tos","title":"How to's","role":"content","text":"1. [**Create a vector index**](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#create-a-vector-index): Redis maintains a secondary index over your data with a defined schema (including vector fields and metadata). Redis supports [`FLAT`](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#flat-index) and [`HNSW`](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#hnsw-index) vector index types.\n1. [**Store and update vectors**](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#store-and-update-vectors): Redis stores vectors and metadata in hashes or JSON objects.\n1. [**Search with vectors**](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#search-with-vectors): Redis supports several advanced querying strategies with vector fields including k-nearest neighbor ([KNN](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#knn-vector-search)), [vector range queries](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#vector-range-queries), and [metadata filters](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#filters).\n1. [**Configure vector queries at runtime**](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#runtime-query-parameters): Select the best filter mode to optimize query execution.\n1. [**Build an AI agent**](https://redis.io/docs/latest/develop/ai/agent-builder): Use the interactive agent builder to generate complete working code for conversational assistants and recommendation engines.\n1. [**Add semantic caching**](https://redis.io/docs/latest/develop/ai/context-engine/langcache): Reduce LLM API calls by caching and reusing responses for semantically similar queries.\n1. [**Add agent memory**](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory): Give your agent persistent session and long-term memory using the Agent Memory REST API.\n1. [**Access structured business data**](https://redis.io/docs/latest/develop/ai/context-engine/context-retriever): Use Context Retriever to define your business data as governed tools that any agent can query reliably.\n1. [**Sync live data to Redis**](https://redis.io/docs/latest/develop/ai/context-engine/data-integration): Use Data Integration to keep your Redis Cloud database in sync with your primary relational database using Change Data Capture.\n\n#### Learn how to index and query vector embeddings\n* [redis-py (Python)](https://redis.io/docs/latest/develop/clients/redis-py/vecsearch)\n* [NRedisStack (C#/.NET)](https://redis.io/docs/latest/develop/clients/dotnet/nredisstack/vecsearch)\n* [node-redis (JavaScript)](https://redis.io/docs/latest/develop/clients/nodejs/vecsearch)\n* [Jedis (Java)](https://redis.io/docs/latest/develop/clients/jedis/vecsearch)\n* [go-redis (Go)](https://redis.io/docs/latest/develop/clients/go/vecsearch)"},{"id":"concepts","title":"Concepts","role":"content","text":"Learn to perform vector search, build AI agents, and use semantic caching and memory in your AI/ML projects.\n\n<div class=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-4 my-8\">\n  \n  \n  \n  \n  \n</div>"},{"id":"quickstarts","title":"Quickstarts","role":"setup","text":"Quickstarts or recipes are useful when you are trying to build specific functionality. For example, you might want to do RAG with LangChain or set up LLM memory for your AI agent.\n\nGet started with these foundational guides:\n\n* [Redis vector database quick start guide](https://redis.io/docs/latest/develop/get-started/search-tutorial/vector-search)\n* [Retrieval-Augmented Generation quick start guide](https://redis.io/docs/latest/develop/get-started/rag)\n\n#### RAG\nRetrieval Augmented Generation (aka RAG) is a technique to enhance the ability of an LLM to respond to user queries. The retrieval part of RAG is supported by a vector database, which can return semantically relevant results to a user's query, serving as contextual information to augment the generative capabilities of an LLM.\n\nExplore our [AI notebooks collection](https://redis.io/docs/latest/develop/ai/notebook-collection) for comprehensive RAG examples including:\n\n* RAG implementations with RedisVL, LangChain, and LlamaIndex\n* Advanced RAG techniques and optimizations\n* RAG evaluation with the RAGAS framework\n* Integration with cloud platforms like Azure and Vertex AI\n\nAdditional resources:\n* [Vector search with Azure](https://techcommunity.microsoft.com/blog/azuredevcommunityblog/vector-similarity-search-with-azure-cache-for-redis-enterprise/3822059)\n* [RAG with Vertex AI](https://github.com/redis-developer/gcp-redis-llm-stack/tree/main)\n\n#### Agents\nAI agents can act autonomously to plan and execute tasks for the user.\n\n* [Build your first AI agent](https://redis.io/docs/latest/develop/ai/agent-builder): Use the interactive agent builder to generate production-ready agent code.\n* [How agents work](https://redis.io/docs/latest/develop/ai/agent-builder/agent-concepts): Learn the agent processing cycle, memory architecture, and Redis data structures for agents.\n* [Redis Notebooks for LangGraph](https://github.com/redis-developer/langgraph-redis/tree/main/examples): End-to-end agent examples using LangGraph and Redis.\n\n#### Context Engine\nThe Context Engine provides managed services for agent memory and data access.\n\n* [Get started with LangCache](https://redis.io/docs/latest/develop/ai/context-engine/langcache): Add semantic caching to reduce LLM costs in minutes.\n* [Get started with Agent Memory](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory): Add persistent two-tier memory to any agent using the REST API.\n* [Get started with Context Retriever](https://redis.io/docs/latest/develop/ai/context-engine/context-retriever): Expose your business data as governed tools that agents can reliably query.\n* [Get started with Data Integration](https://redis.io/docs/latest/develop/ai/context-engine/data-integration): Keep Redis in sync with your primary database so agents always have fresh data."},{"id":"tutorials","title":"Tutorials","role":"content","text":"Need a deeper-dive through different use cases and topics?\n\n#### Agents\n* [Agentic RAG](https://github.com/redis-developer/agentic-rag) - A tutorial focused on agentic RAG with LlamaIndex and Amazon Bedrock\n* [Redis Notebooks for LangGraph](https://github.com/redis-developer/langgraph-redis/tree/main/examples) - Working with LangGraph agents and Redis memory\n* [Build a LangGraph travel agent with Redis Agent Memory](https://redis.io/tutorials/redis-agent-memory-with-langgraph/) - Build a LangGraph agent with short-term session memory and long-term persistent memory using Redis Agent Memory\n* [Build a real-time AI agent with Redis Iris](https://redis.io/tutorials/redis-iris-call-agent/) - Combine Redis Agent Memory and Context Retriever to build a wealth advisor agent with persistent memory and structured data access\n* [Build a car dealership agent with Google ADK and Redis Agent Memory](https://redis.io/tutorials/build-a-car-dealership-agent-with-google-adk-and-redis-agent-memory/) - Build a persistent AI agent using Google ADK and Redis Agent Memory Server with working and long-term memory\n* [Build Google ADK agents with persistent, real-time memory on Redis](https://redis.io/en/blog/build-google-adk-agents-with-persistent-real-time-memory-on-redis/) - Use the `adk-redis` package to integrate Google ADK with Redis for persistent memory, sessions, and semantic caching in production agents\n\n#### Context Engine\n* [Semantic caching with Redis LangCache](https://redis.io/tutorials/semantic-caching-with-redis-langcache/) - Build a FastAPI app with semantic caching using LangCache to reduce LLM costs and improve response times\n\n#### RAG\n* [RAG on Vertex AI](https://github.com/redis-developer/gcp-redis-llm-stack/tree/main) - A RAG tutorial featuring Redis with Vertex AI\n* [RAG workbench](https://github.com/redis-developer/redis-rag-workbench) - A development playground for exploring RAG techniques with Redis\n* [ArXiv Chat](https://github.com/redis-developer/ArxivChatGuru) - Streamlit demo of RAG over ArXiv documents with Redis & OpenAI\n\n#### Recommendations and search\n* [Redis Repo Memory](https://github.com/redis-learn/redis-repo-memory) - A GitHub Action that surfaces semantically related PRs, issues, and commits from repository history using Redis vector search ([install from the Marketplace](https://github.com/marketplace/actions/redis-repo-memory))\n* [Recommendation systems w/ NVIDIA Merlin & Redis](https://github.com/redis-developer/redis-nvidia-recsys) - Three examples, each escalating in complexity, showcasing the process of building a realtime recsys with NVIDIA and Redis\n* [Redis product search](https://github.com/redis-developer/redis-product-search) - Build a real-time product search engine using features like full-text search, vector similarity, and real-time data updates\n* [ArXiv Search](https://github.com/redis-developer/redis-arxiv-search) - Full stack implementation of Redis with React FE\n\n#### Vector sets\n* [Getting started with vector sets](https://redis.io/tutorials/howtos/vector-sets-basics/) - Learn the fundamentals of Redis vector sets for similarity search using the `VADD` and `VSIM` commands\n* [Face similarity search with Redis vector sets](https://redis.io/tutorials/face-similarity-search-with-redis-vector-sets/) - Build a celebrity lookalike app using Redis vector sets and a Vision Transformer model for face embedding and similarity search"},{"id":"ecosystem-integrations","title":"Ecosystem integrations","role":"content","text":"Explore our comprehensive [ecosystem integrations page](https://redis.io/docs/latest/develop/ai/ecosystem-integrations) to discover how Redis works with popular AI frameworks, platforms, and tools including:\n\n* LangGraph, LangChain, and LlamaIndex for building advanced AI applications\n* Amazon Bedrock and NVIDIA NIM for enhanced AI infrastructure\n* Microsoft Semantic Kernel and Kernel Memory for LLM applications\n* And many more integrations to power your AI solutions"},{"id":"video-tutorials","title":"Video tutorials","role":"content","text":"Watch our [AI video collection](https://redis.io/docs/latest/develop/ai/ai-videos) featuring practical tutorials and demonstrations on:\n\n* Building RAG applications and implementing vector search\n* Working with LangGraph for AI agents with memory\n* Semantic caching and search techniques\n* Redis integrations with popular AI frameworks\n* Real-world AI application examples and best practices"},{"id":"benchmarks","title":"Benchmarks","role":"performance","text":"See how we stack up against the competition.\n* [Redis vector benchmark results](https://redis.io/blog/benchmarking-results-for-vector-databases/)\n* [1 billion vectors](https://redis.io/blog/redis-8-0-m02-the-fastest-redis-ever/)"},{"id":"best-practices","title":"Best practices","role":"content","text":"See how leaders in the industry are building their AI apps.\n\n#### Agents and architecture\n* [AI Agent vs Chatbot: Key Differences Explained](https://redis.io/en/blog/ai-agent-vs-chatbot/): Understand the architectural differences between chatbots and agents and when to use each based on task complexity, cost, and latency.\n* [Agentic AI Architecture: 5 Patterns Explained](https://redis.io/en/blog/agentic-ai-architecture-examples/): Learn five production agentic patterns and the data layer requirements needed to support them.\n* [AI Agents vs Workflows: When to Use Each](https://redis.io/en/blog/agents-vs-workflows/): Understand the distinction between deterministic workflows and autonomous agents and how to combine them in production.\n* [How agents work](https://redis.io/docs/latest/develop/ai/agent-builder/agent-concepts): Agent memory patterns, data structure selection, and production deployment considerations.\n\n#### Memory and context\n* [Context Engineering for AI: What It Is & How to Build It](https://redis.io/en/blog/context-engineering-ai/): Learn the discipline of designing what an LLM receives at inference time, including the four core operations and how Redis provides the infrastructure.\n* [Long-Term Memory Architectures for AI Agents](https://redis.io/en/blog/long-term-memory-architectures-ai-agents/): Design persistent memory systems that retain information across sessions, with guidance on memory types and design tradeoffs.\n* [Context Pruning: Cut LLM Tokens Without Losing Quality](https://redis.io/en/blog/context-pruning-llm-tokens/): Selectively remove low-value tokens from LLM input to reduce costs and improve quality, with benchmarks and failure modes.\n\n#### Performance\n* [What is semantic caching](https://redis.io/blog/what-is-semantic-caching): When and how to apply semantic caching in your AI applications.\n* [Streaming LLM Responses: Make Your AI App Feel Fast](https://redis.io/en/blog/streaming-llm-responses/): Deliver tokens incrementally via Server-Sent Events and combine streaming with caching and context optimization in production.\n\n#### RAG\n* [Get better RAG responses with Ragas](https://redis.io/blog/get-better-rag-responses-with-ragas/)"},{"id":"continue-learning-with-redis-university","title":"Continue learning with Redis University","role":"content","text":"* [Context Engineering with Redis & LangChain](https://university.redis.io/course/vsgabnbkd3f5cd?tab=details)\n* [Vector Search with RedisVL](https://university.redis.io/course/1npvvtfft2agew?tab=details)\n* [Build a RAG Chatbot](https://university.redis.io/course/ihjs7iip0gpkrw?tab=details)"}],"examples":[]}
{"schema_version":2,"id":"develop/ai/notebook-collection","title":"Notebooks collection","url":"https://redis.io/docs/latest/develop/ai/notebook-collection/","summary":"This collection showcases how Redis can be integrated into AI workflows to enhance performance, reduce latency, and enable real-time AI applications. Each notebook comes with complete code examples, explanations, and integration guides.","tags":["docs","develop","stack","oss","rc","rs"],"last_updated":"2025-07-29T10:59:22-07:00","page_type":"content","content_hash":"43e7a709ed4023deea311f195fd589a6951b939ebc186e385afd4bc214be5b66","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Notebook | Category | Description |  |\n|----------|----------|-------------|--|\n| The place to start if you are brand new to Redis | Introduction | Great for Redis beginners looking for a guided Colab experience. | [Open in Colab](https://colab.research.google.com/github/redis-developer/redis-ai-resources/blob/main/python-recipes/redis-intro/00_redis_intro.ipynb) |\n| Implementing hybrid search with Redis | Hybrid and Vector Search | Combines vector similarity with keyword filters. | [Open in Colab](https://colab.research.google.com/github/redis-developer/redis-ai-resources/blob/main/python-recipes/vector-search/02_hybrid_search.ipynb) |\n| Vector search with Redis Python client | Hybrid and Vector Search | Demonstrates pure vector search using the Redis Python client. | [Open in Colab](https://colab.research.google.com/github/redis-developer/redis-ai-resources/blob/main/python-recipes/vector-search/00_redispy.ipynb) |\n| Vector search with Redis Vector Library | Hybrid and Vector Search | Uses RedisVL for advanced vector indexing and querying. | [Open in Colab](https://colab.research.google.com/github/redis-developer/redis-ai-resources/blob/main/python-recipes/vector-search/01_redisvl.ipynb) |\n| Shows how to convert a float32 index to float16 or integer data types | Hybrid and Vector Search | Demonstrates data type optimization for vector indices. | [Open in Colab](https://colab.research.google.com/github/redis-developer/redis-ai-resources/blob/main/python-recipes/vector-search/03_dtype_support.ipynb) |\n| RAG from scratch with Redis Vector Library | RAG | Basic RAG implementation using RedisVL. | [Open in Colab](https://colab.research.google.com/github/redis-developer/redis-ai-resources/blob/main/python-recipes/RAG/01_redisvl.ipynb) |\n| RAG using Redis and LangChain | RAG | Shows integration between Redis and LangChain for RAG. | [Open in Colab](https://colab.research.google.com/github/redis-developer/redis-ai-resources/blob/main/python-recipes/RAG/02_langchain.ipynb) |\n| RAG using Redis and LlamaIndex | RAG | Walkthrough of RAG with Redis and LlamaIndex. | [Open in Colab](https://colab.research.google.com/github/redis-developer/redis-ai-resources/blob/main/python-recipes/RAG/03_llamaindex.ipynb) |\n| Advanced RAG with RedisVL | RAG | Advanced concepts and techniques using RedisVL. | [Open in Colab](https://colab.research.google.com/github/redis-developer/redis-ai-resources/blob/main/python-recipes/RAG/04_advanced_redisvl.ipynb) |\n| RAG using Redis and Nvidia | RAG | NVIDIA + Redis for LLM context retrieval. | [Open in Colab](https://colab.research.google.com/github/redis-developer/redis-ai-resources/blob/main/python-recipes/RAG/05_nvidia_ai_rag_redis.ipynb) |\n| Utilize RAGAS framework to evaluate RAG performance | RAG | Evaluation of RAG apps using the RAGAS framework. | [Open in Colab](https://colab.research.google.com/github/redis-developer/redis-ai-resources/blob/main/python-recipes/RAG/06_ragas_evaluation.ipynb) |\n| Implement a simple RBAC policy with vector search using Redis | RAG | Role-based access control implementation for RAG systems. | [Open in Colab](https://colab.research.google.com/github/redis-developer/redis-ai-resources/blob/main/python-recipes/RAG/07_user_role_based_rag.ipynb) |\n| LangGraph and agents | Agents | Getting started with agent workflows. | [Open in Colab](https://colab.research.google.com/github/redis-developer/redis-ai-resources/blob/main/python-recipes/agents/00_langgraph_redis_agentic_rag.ipynb) |\n| Movie recommendation system | Agents | Collaborative agent-based movie recommender. | [Open in Colab](https://colab.research.google.com/github/redis-developer/redis-ai-resources/blob/main/python-recipes/agents/01_crewai_langgraph_redis.ipynb) |\n| Full-Featured Agent Architecture | Agents | Comprehensive agent implementation with advanced features. | [Open in Colab](https://colab.research.google.com/github/redis-developer/redis-ai-resources/blob/main/python-recipes/agents/02_full_featured_agent.ipynb) |\n| Optimize semantic cache threshold with RedisVL | Semantic Cache | Performance optimization for semantic caching systems. | [Open in Colab](https://colab.research.google.com/github/redis-developer/redis-ai-resources/blob/main/python-recipes/semantic-cache/02_semantic_cache_optimization.ipynb) |\n| Simple examples of how to build an allow/block list router in addition to a multi-topic router | Semantic Router | Basic routing patterns and access control mechanisms. | [Open in Colab](https://colab.research.google.com/github/redis-developer/redis-ai-resources/blob/main/python-recipes/semantic-router/00_semantic_routing.ipynb) |\n| Use `RouterThresholdOptimizer` from RedisVL to setup best router config | Semantic Router | Router configuration optimization using RedisVL. | [Open in Colab](https://colab.research.google.com/github/redis-developer/redis-ai-resources/blob/main/python-recipes/semantic-router/01_routing_optimization.ipynb) |\n| Facial recognition | Computer Vision | Face matching using Facenet and RedisVL. | [Open in Colab](https://colab.research.google.com/github/redis-developer/redis-ai-resources/blob/main/python-recipes/computer-vision/00_facial_recognition_facenet.ipynb) |\n| Content filtering with RedisVL | Recommendation Systems | Introduction to content-based filtering. | [Open in Colab](https://colab.research.google.com/github/redis-developer/redis-ai-resources/blob/main/python-recipes/recommendation-systems/00_content_filtering.ipynb) |\n| Collaborative filtering with RedisVL | Recommendation Systems | Intro to collaborative filtering with RedisVL. | [Open in Colab](https://colab.research.google.com/github/redis-developer/redis-ai-resources/blob/main/python-recipes/recommendation-systems/01_collaborative_filtering.ipynb) |\n| Intro deep learning two tower example with RedisVL | Recommendation Systems | Deep learning approach to recommendation systems. | [Open in Colab](https://colab.research.google.com/github/redis-developer/redis-ai-resources/blob/main/python-recipes/recommendation-systems/02_two_towers.ipynb) |\n| Credit scoring system using Feast with Redis as the online store | Feature Store | Feature store implementation for ML model serving. | [Open in Colab](https://colab.research.google.com/github/redis-developer/redis-ai-resources/blob/main/python-recipes/feature-store/00_feast_credit_score.ipynb) |"},{"id":"additional-resources","title":"Additional resources","role":"content","text":"Looking for more ways to learn about Redis for AI? Check out our:\n\n* [AI video collection](https://redis.io/docs/latest/develop/ai/ai-videos) - Video tutorials and demonstrations covering Redis AI concepts\n* [Ecosystem integrations](https://redis.io/docs/latest/develop/ai/ecosystem-integrations) - Learn how Redis works with popular AI frameworks and tools"}],"examples":[]}
{"schema_version":2,"id":"develop/ai/redisvl/api/cache","title":"LLM Cache","url":"https://redis.io/docs/latest/develop/ai/redisvl/api/cache/","summary":"","aliases":["/integrate/redisvl/api/cache"],"tags":[],"last_updated":"2026-10-05T16:11:44+01:00","page_type":"content","content_hash":"ecf35c99ea7469138ce9993b37c92b8a97f668065e7687e408cd511bd4a83175","sections":[{"id":"semanticcache","title":"SemanticCache","role":"overview","text":"<a id=\"semantic-cache-api\"></a>"},{"id":"class-semanticcachenamellmcache-distance_threshold01-ttlnone-vectorizernone-filterable_fieldsnone-redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-overwritefalse-create_indextrue-kwargs","title":"`class SemanticCache(name='llmcache', distance_threshold=0.1, ttl=None, vectorizer=None, filterable_fields=None, redis_client=None, redis_url='redis://localhost:6379', connection_kwargs=None, overwrite=False, create_index=True, **kwargs)`","role":"content","text":"Bases: `BaseLLMCache`\n\nSemantic Cache for Large Language Models.\n\nSemantic Cache for Large Language Models.\n\n* **Parameters:**\n  * **name** (*str* *,* *optional*) – The name of the semantic cache search index.\n    Defaults to \"llmcache\".\n  * **distance_threshold** (*float* *,* *optional*) – Semantic distance threshold for the\n    cache in Redis COSINE units [0-2], where lower values indicate stricter\n    matching. Defaults to 0.1.\n  * **ttl** (*Optional* *[* *int* *]* *,* *optional*) – The time-to-live for records cached\n    in Redis. Defaults to None.\n  * **vectorizer** (*Optional* *[* *BaseVectorizer* *]* *,* *optional*) – The vectorizer for the cache.\n    Defaults to HFTextVectorizer.\n  * **filterable_fields** (*Optional* *[* *List* *[* *Dict* *[* *str* *,* *Any* *]* *]* *]*) – An optional list of RedisVL fields\n    that can be used to customize cache retrieval with filters.\n  * **redis_client** (*Optional* *[* *Redis* *]* *,* *optional*) – A redis client connection instance.\n    Defaults to None.\n  * **redis_url** (*str* *,* *optional*) – The redis url. Defaults to redis://localhost:6379.\n  * **connection_kwargs** (*Dict* *[* *str* *,* *Any* *]*) – The connection arguments\n    for the redis client. Defaults to empty {}.\n  * **overwrite** (*bool*) – Whether or not to force overwrite the schema for\n    the semantic cache index. Defaults to false.\n  * **create_index** (*bool*) – Whether RedisVL creates and validates the index.\n    When True, the constructor runs `FT.INFO` to check whether the\n    index exists, compares the live schema against this one, and runs\n    `FT.CREATE` if it is absent. When False it does none of these\n    and issues no index command at all: the index must already exist\n    with a compatible schema. A live index whose prefix or storage\n    type differs from this schema is not detected and produces empty\n    results rather than an error. Use this when the index is managed\n    externally, or when the credential cannot run `FT.INFO`. See\n    [Install RedisVL](https://redis.io/docs/latest/../user_guide/installation) for the ACL details. Defaults to\n    true.\n* **Raises:**\n  * **TypeError** – If an invalid vectorizer is provided.\n  * **TypeError** – If the TTL value is not an int.\n  * **ValueError** – If the threshold is not between 0 and 2 (Redis COSINE distance).\n  * **ValueError** – If existing schema does not match new schema and overwrite is False.\n  * **ValueError** – If both create_index is False and overwrite is True.\n\n[code example]\n\n#### `async acheck(prompt=None, vector=None, num_results=1, return_fields=None, filter_expression=None, distance_threshold=None)`\n\nAsync check the semantic cache for results similar to the specified prompt\nor vector.\n\nThis method searches the cache using vector similarity with\neither a raw text prompt (converted to a vector) or a provided vector as\ninput. It checks for semantically similar prompts and fetches the cached\nLLM responses.\n\n* **Parameters:**\n  * **prompt** (*Optional* *[* *str* *]* *,* *optional*) – The text prompt to search for in\n    the cache.\n  * **vector** (*Optional* *[* *List* *[* *float* *]* *]* *,* *optional*) – The vector representation\n    of the prompt to search for in the cache.\n  * **num_results** (*int* *,* *optional*) – The number of cached results to return.\n    Defaults to 1.\n  * **return_fields** (*Optional* *[* *List* *[* *str* *]* *]* *,* *optional*) – The fields to include\n    in each returned result. If None, defaults to all available\n    fields in the cached entry.\n  * **filter_expression** (*Optional* *[*[*FilterExpression*](https://redis.io/docs/latest/filter/#filterexpression) *]*) – Optional filter expression\n    that can be used to filter cache results. Defaults to None and\n    the full cache will be searched.\n  * **distance_threshold** (*Optional* *[* *float* *]*) – The threshold for semantic\n    vector distance.\n* **Returns:**\n  A list of dicts containing the requested\n  : return fields for each similar cached response.\n* **Return type:**\n  List[Dict[str, Any]]\n* **Raises:**\n  * **ValueError** – If neither a prompt nor a vector is specified.\n  * **ValueError** – if ‘vector’ has incorrect dimensions.\n  * **TypeError** – If return_fields is not a list when provided.\n\n[code example]\n\n#### `async aclear()`\n\nAsync delete every cache entry, leaving the index in place.\n\nSee [clear](#clear) for the caveats, which apply identically here.\n\n* **Return type:**\n  None\n\n#### `async adelete()`\n\nAsync delete the cache and its index entirely.\n\n* **Raises:**\n  **ValueError** – If `create_index=False`. See [delete](#delete).\n* **Return type:**\n  None\n\n#### `async adisconnect()`\n\nAsynchronously disconnect from Redis and search index.\n\nCloses all Redis connections and index connections.\n\n#### `async adrop(ids=None, keys=None)`\n\nAsync drop specific entries from the cache by ID or Redis key.\n\n* **Parameters:**\n  * **ids** (*Optional* *[* *List* *[* *str* *]* *]*) – List of entry IDs to remove from the cache.\n    Entry IDs are the unique identifiers without the cache prefix.\n  * **keys** (*Optional* *[* *List* *[* *str* *]* *]*) – List of full Redis keys to remove from the cache.\n    Keys are the complete Redis keys including the cache prefix.\n* **Return type:**\n  None\n\n> [!NOTE]\n> At least one of ids or keys must be provided.\n\n* **Raises:**\n  **ValueError** – If neither ids nor keys is provided.\n* **Parameters:**\n  * **ids** (*list* *[* *str* *]*  *|* *None*)\n  * **keys** (*list* *[* *str* *]*  *|* *None*)\n* **Return type:**\n  None\n\n#### `async aexpire(key, ttl=None)`\n\nAsynchronously set or refresh the expiration time for a key in the cache.\n\n* **Parameters:**\n  * **key** (*str*) – The Redis key to set the expiration on.\n  * **ttl** (*Optional* *[* *int* *]* *,* *optional*) – The time-to-live in seconds. If None,\n    uses the default TTL configured for this cache instance.\n    Defaults to None.\n* **Return type:**\n  None\n\n> [!NOTE]\n> If neither the provided TTL nor the default TTL is set (both are None),\n> this method will have no effect.\n\n#### `async astore(prompt, response, vector=None, metadata=None, filters=None, ttl=None)`\n\nAsync stores the specified key-value pair in the cache along with metadata.\n\n* **Parameters:**\n  * **prompt** (*str*) – The user prompt to cache.\n  * **response** (*str*) – The LLM response to cache.\n  * **vector** (*Optional* *[* *List* *[* *float* *]* *]* *,* *optional*) – The prompt vector to\n    cache. Defaults to None, and the prompt vector is generated on\n    demand.\n  * **metadata** (*Optional* *[* *Dict* *[* *str* *,* *Any* *]* *]* *,* *optional*) – The optional metadata to cache\n    alongside the prompt and response. Defaults to None.\n  * **filters** (*Optional* *[* *Dict* *[* *str* *,* *Any* *]* *]*) – The optional tag to assign to the cache entry.\n    Defaults to None.\n  * **ttl** (*Optional* *[* *int* *]*) – The optional TTL override to use on this individual cache\n    entry. Defaults to the global TTL setting.\n* **Returns:**\n  The Redis key for the entries added to the semantic cache.\n* **Return type:**\n  str\n* **Raises:**\n  * **ValueError** – If neither prompt nor vector is specified.\n  * **ValueError** – if vector has incorrect dimensions.\n  * **TypeError** – If provided metadata is not a dictionary.\n\n[code example]\n\n#### `async aupdate(key, **kwargs)`\n\nAsync update specific fields within an existing cache entry. If no fields\nare passed, then only the document TTL is refreshed.\n\n* **Parameters:**\n  **key** (*str*) – the key of the document to update using kwargs.\n* **Raises:**\n  * **ValueError if an incorrect mapping is provided as a kwarg.** – \n  * **TypeError if metadata is provided and not** **of** **type dict.** – \n* **Return type:**\n  None\n\n[code example]\n\n#### `check(prompt=None, vector=None, num_results=1, return_fields=None, filter_expression=None, distance_threshold=None)`\n\nChecks the semantic cache for results similar to the specified prompt\nor vector.\n\nThis method searches the cache using vector similarity with\neither a raw text prompt (converted to a vector) or a provided vector as\ninput. It checks for semantically similar prompts and fetches the cached\nLLM responses.\n\n* **Parameters:**\n  * **prompt** (*Optional* *[* *str* *]* *,* *optional*) – The text prompt to search for in\n    the cache.\n  * **vector** (*Optional* *[* *List* *[* *float* *]* *]* *,* *optional*) – The vector representation\n    of the prompt to search for in the cache.\n  * **num_results** (*int* *,* *optional*) – The number of cached results to return.\n    Defaults to 1.\n  * **return_fields** (*Optional* *[* *List* *[* *str* *]* *]* *,* *optional*) – The fields to include\n    in each returned result. If None, defaults to all available\n    fields in the cached entry.\n  * **filter_expression** (*Optional* *[*[*FilterExpression*](https://redis.io/docs/latest/filter/#filterexpression) *]*) – Optional filter expression\n    that can be used to filter cache results. Defaults to None and\n    the full cache will be searched.\n  * **distance_threshold** (*Optional* *[* *float* *]*) – The threshold for semantic\n    vector distance.\n* **Returns:**\n  A list of dicts containing the requested\n  : return fields for each similar cached response.\n* **Return type:**\n  List[Dict[str, Any]]\n* **Raises:**\n  * **ValueError** – If neither a prompt nor a vector is specified.\n  * **ValueError** – if ‘vector’ has incorrect dimensions.\n  * **TypeError** – If return_fields is not a list when provided.\n\n[code example]\n\n#### `clear()`\n\nDelete every cache entry, leaving the index in place.\n\nClears by key prefix, not by index membership, so it removes every key\nunder `{name}:` and nothing outside it. Available under\n`create_index=False`; dropping the index is [delete](#delete).\n\n> [!WARNING]\n> Under `create_index=False` the prefix is unverified, so this can\n> delete keys the index never covered and miss entries it does. See\n> [Install RedisVL](https://redis.io/docs/latest/../user_guide/installation).\n\n* **Return type:**\n  None\n\n#### `delete()`\n\nDelete the cache and its index entirely.\n\n* **Raises:**\n  **ValueError** – If `create_index=False`. Use [clear](#clear) to\n      empty the cache and leave the index standing.\n* **Return type:**\n  None\n\n#### `disconnect()`\n\nDisconnect from Redis and search index.\n\nCloses all Redis connections and index connections.\n\n#### `drop(ids=None, keys=None)`\n\nDrop specific entries from the cache by ID or Redis key.\n\n* **Parameters:**\n  * **ids** (*Optional* *[* *List* *[* *str* *]* *]*) – List of entry IDs to remove from the cache.\n    Entry IDs are the unique identifiers without the cache prefix.\n  * **keys** (*Optional* *[* *List* *[* *str* *]* *]*) – List of full Redis keys to remove from the cache.\n    Keys are the complete Redis keys including the cache prefix.\n* **Return type:**\n  None\n\n> [!NOTE]\n> At least one of ids or keys must be provided.\n\n* **Raises:**\n  **ValueError** – If neither ids nor keys is provided.\n* **Parameters:**\n  * **ids** (*list* *[* *str* *]*  *|* *None*)\n  * **keys** (*list* *[* *str* *]*  *|* *None*)\n* **Return type:**\n  None\n\n#### `expire(key, ttl=None)`\n\nSet or refresh the expiration time for a key in the cache.\n\n* **Parameters:**\n  * **key** (*str*) – The Redis key to set the expiration on.\n  * **ttl** (*Optional* *[* *int* *]* *,* *optional*) – The time-to-live in seconds. If None,\n    uses the default TTL configured for this cache instance.\n    Defaults to None.\n* **Return type:**\n  None\n\n> [!NOTE]\n> If neither the provided TTL nor the default TTL is set (both are None),\n> this method will have no effect.\n\n#### `set_threshold(distance_threshold)`\n\nSets the semantic distance threshold for the cache.\n\n* **Parameters:**\n  **distance_threshold** (*float*) – The semantic distance threshold for\n  the cache.\n* **Raises:**\n  **ValueError** – If the threshold is not between 0 and 2 (Redis COSINE distance).\n* **Return type:**\n  None\n\n#### `set_ttl(ttl=None)`\n\nSet the default TTL, in seconds, for entries in the cache.\n\n* **Parameters:**\n  **ttl** (*Optional* *[* *int* *]* *,* *optional*) – The optional time-to-live expiration\n  for the cache, in seconds.\n* **Raises:**\n  **ValueError** – If the time-to-live value is not an integer.\n* **Return type:**\n  None\n\n#### `store(prompt, response, vector=None, metadata=None, filters=None, ttl=None)`\n\nStores the specified key-value pair in the cache along with metadata.\n\n* **Parameters:**\n  * **prompt** (*str*) – The user prompt to cache.\n  * **response** (*str*) – The LLM response to cache.\n  * **vector** (*Optional* *[* *List* *[* *float* *]* *]* *,* *optional*) – The prompt vector to\n    cache. Defaults to None, and the prompt vector is generated on\n    demand.\n  * **metadata** (*Optional* *[* *Dict* *[* *str* *,* *Any* *]* *]* *,* *optional*) – The optional metadata to cache\n    alongside the prompt and response. Defaults to None.\n  * **filters** (*Optional* *[* *Dict* *[* *str* *,* *Any* *]* *]*) – The optional tag to assign to the cache entry.\n    Defaults to None.\n  * **ttl** (*Optional* *[* *int* *]*) – The optional TTL override to use on this individual cache\n    entry. Defaults to the global TTL setting.\n* **Returns:**\n  The Redis key for the entries added to the semantic cache.\n* **Return type:**\n  str\n* **Raises:**\n  * **ValueError** – If neither prompt nor vector is specified.\n  * **ValueError** – if vector has incorrect dimensions.\n  * **TypeError** – If provided metadata is not a dictionary.\n\n[code example]\n\n#### `update(key, **kwargs)`\n\nUpdate specific fields within an existing cache entry. If no fields\nare passed, then only the document TTL is refreshed.\n\n* **Parameters:**\n  **key** (*str*) – the key of the document to update using kwargs.\n* **Raises:**\n  * **ValueError if an incorrect mapping is provided as a kwarg.** – \n  * **TypeError if metadata is provided and not** **of** **type dict.** – \n* **Return type:**\n  None\n\n[code example]\n\n#### `property aindex: `[`AsyncSearchIndex`](https://redis.io/docs/latest/searchindex/#asyncsearchindex)`  | None`\n\nThe underlying AsyncSearchIndex for the cache.\n\n* **Returns:**\n  The async search index.\n* **Return type:**\n  [AsyncSearchIndex](https://redis.io/docs/latest/searchindex/#asyncsearchindex)\n\n#### `property distance_threshold: float`\n\nThe semantic distance threshold for the cache.\n\n* **Returns:**\n  The semantic distance threshold.\n* **Return type:**\n  float\n\n#### `property index: `[`SearchIndex`](https://redis.io/docs/latest/searchindex/#searchindex)` `\n\nThe underlying SearchIndex for the cache.\n\n* **Returns:**\n  The search index.\n* **Return type:**\n  [SearchIndex](https://redis.io/docs/latest/searchindex/#searchindex)\n\n#### `property ttl: int | None`\n\nThe default TTL, in seconds, for entries in the cache."},{"id":"langcachesemanticcache","title":"LangCacheSemanticCache","role":"content","text":"<a id=\"langcache-semantic-cache-api\"></a>"},{"id":"class-langcachesemanticcachenamelangcache-server_urlhttpsaws-us-east-1langcacheredisio-cache_id-api_key-ttlnone-use_exact_searchtrue-use_semantic_searchtrue-distance_scalenormalized-kwargs","title":"`class LangCacheSemanticCache(name='langcache', server_url='https://aws-us-east-1.langcache.redis.io', cache_id='', api_key='', ttl=None, use_exact_search=True, use_semantic_search=True, distance_scale='normalized', **kwargs)`","role":"content","text":"Bases: `BaseLLMCache`\n\nLLM Cache implementation using the LangCache managed service.\n\nThis cache uses the LangCache API service for semantic caching of LLM\nresponses. It requires a LangCache account and API key."},{"id":"example","title":"`Example`","role":"content","text":"[code example]\n\nInitialize a LangCache semantic cache.\n\n* **Parameters:**\n  * **name** (*str*) – The name of the cache. Defaults to \"langcache\".\n  * **server_url** (*str*) – The LangCache server URL.\n  * **cache_id** (*str*) – The LangCache cache ID.\n  * **api_key** (*str*) – The LangCache API key.\n  * **ttl** (*Optional* *[* *int* *]*) – Time-to-live for cache entries in seconds.\n  * **use_exact_search** (*bool*) – Whether to use exact matching. Defaults to True.\n  * **use_semantic_search** (*bool*) – Whether to use semantic search. Defaults to True.\n  * **distance_scale** (*str*) – Threshold scale for distance_threshold:\n    - \"normalized\": 0–1 semantic distance (lower is better)\n    - \"redis\": Redis COSINE distance 0–2 (lower is better)\n* **Raises:**\n  * **ImportError** – If the langcache package is not installed.\n  * **ValueError** – If cache_id or api_key is not provided.\n\n#### `async acheck(prompt=None, vector=None, num_results=1, return_fields=None, filter_expression=None, distance_threshold=None, attributes=None)`\n\nAsync check the cache for semantically similar prompts.\n\n* **Parameters:**\n  * **prompt** (*Optional* *[* *str* *]*) – The text prompt to search for.\n  * **vector** (*Optional* *[* *List* *[* *float* *]* *]*) – Not supported by LangCache API.\n  * **num_results** (*int*) – Number of results to return. Defaults to 1.\n  * **return_fields** (*Optional* *[* *List* *[* *str* *]* *]*) – Not used (for compatibility).\n  * **filter_expression** (*Optional* *[*[*FilterExpression*](https://redis.io/docs/latest/filter/#filterexpression) *]*) – Not supported.\n  * **distance_threshold** (*Optional* *[* *float* *]*) – Maximum distance threshold.\n    Converted to similarity_threshold according to distance_scale:\n    If \"redis\", uses norm_cosine_distance(distance_threshold) ([0,2] -> [0,1]).\n    If \"normalized\", uses (1.0 - distance_threshold) ([0,1] -> [0,1]).\n  * **attributes** (*Optional* *[* *Dict* *[* *str* *,* *Any* *]* *]*) – LangCache attributes to filter by.\n    Note: Attributes must be pre-configured in your LangCache instance.\n* **Returns:**\n  List of matching cache entries.\n* **Return type:**\n  List[Dict[str, Any]]\n* **Raises:**\n  **ValueError** – If prompt is not provided.\n\n#### `async aclear()`\n\nAsync clear the cache of all entries.\n\nThis is an alias for adelete() to match the BaseCache interface.\n\n* **Return type:**\n  None\n\n#### `async adelete()`\n\nAsync delete the entire cache.\n\nThis deletes all entries in the cache by calling the flush API.\n\n* **Return type:**\n  None\n\n#### `async adelete_by_attributes(attributes)`\n\nAsync delete cache entries matching the given attributes.\n\n* **Parameters:**\n  **attributes** (*Dict* *[* *str* *,* *Any* *]*) – Attributes to match for deletion.\n  Cannot be empty.\n* **Returns:**\n  Result of the deletion operation.\n* **Return type:**\n  Dict[str, Any]\n* **Raises:**\n  **ValueError** – If attributes is an empty dictionary.\n\n#### `async adelete_by_id(entry_id)`\n\nAsync delete a single cache entry by ID.\n\n* **Parameters:**\n  **entry_id** (*str*) – The ID of the entry to delete.\n* **Return type:**\n  None\n\n#### `async adisconnect()`\n\nAsync disconnect from Redis.\n\n* **Return type:**\n  None\n\n#### `async aexpire(key, ttl=None)`\n\nAsynchronously set or refresh the expiration time for a key in the cache.\n\n* **Parameters:**\n  * **key** (*str*) – The Redis key to set the expiration on.\n  * **ttl** (*Optional* *[* *int* *]* *,* *optional*) – The time-to-live in seconds. If None,\n    uses the default TTL configured for this cache instance.\n    Defaults to None.\n* **Return type:**\n  None\n\n> [!NOTE]\n> If neither the provided TTL nor the default TTL is set (both are None),\n> this method will have no effect.\n\n#### `async astore(prompt, response, vector=None, metadata=None, filters=None, ttl=None)`\n\nAsync store a prompt-response pair in the cache.\n\n* **Parameters:**\n  * **prompt** (*str*) – The user prompt to cache.\n  * **response** (*str*) – The LLM response to cache.\n  * **vector** (*Optional* *[* *List* *[* *float* *]* *]*) – Not supported by LangCache API.\n  * **metadata** (*Optional* *[* *Dict* *[* *str* *,* *Any* *]* *]*) – Optional metadata (stored as attributes).\n  * **filters** (*Optional* *[* *Dict* *[* *str* *,* *Any* *]* *]*) – Not supported.\n  * **ttl** (*Optional* *[* *int* *]*) – Optional TTL override in seconds.\n* **Returns:**\n  The entry ID for the cached entry.\n* **Return type:**\n  str\n* **Raises:**\n  **ValueError** – If prompt or response is empty.\n\n#### `async aupdate(key, **kwargs)`\n\nAsync update specific fields within an existing cache entry.\n\nNote: LangCache API does not support updating individual entries.\nThis method will raise NotImplementedError.\n\n* **Parameters:**\n  * **key** (*str*) – The key of the document to update.\n  * **\\*\\*kwargs** – Field-value pairs to update.\n* **Raises:**\n  **NotImplementedError** – LangCache does not support entry updates.\n* **Return type:**\n  None\n\n#### `check(prompt=None, vector=None, num_results=1, return_fields=None, filter_expression=None, distance_threshold=None, attributes=None)`\n\nCheck the cache for semantically similar prompts.\n\n* **Parameters:**\n  * **prompt** (*Optional* *[* *str* *]*) – The text prompt to search for.\n  * **vector** (*Optional* *[* *List* *[* *float* *]* *]*) – Not supported by LangCache API.\n  * **num_results** (*int*) – Number of results to return. Defaults to 1.\n  * **return_fields** (*Optional* *[* *List* *[* *str* *]* *]*) – Not used (for compatibility).\n  * **filter_expression** (*Optional* *[*[*FilterExpression*](https://redis.io/docs/latest/filter/#filterexpression) *]*) – Not supported.\n  * **distance_threshold** (*Optional* *[* *float* *]*) – Maximum distance threshold.\n    Converted to similarity_threshold according to distance_scale:\n    If \"redis\", uses norm_cosine_distance(distance_threshold) ([0,2] -> [0,1]).\n    If \"normalized\", uses (1.0 - distance_threshold) ([0,1] -> [0,1]).\n  * **attributes** (*Optional* *[* *Dict* *[* *str* *,* *Any* *]* *]*) – LangCache attributes to filter by.\n    Note: Attributes must be pre-configured in your LangCache instance.\n* **Returns:**\n  List of matching cache entries.\n* **Return type:**\n  List[Dict[str, Any]]\n* **Raises:**\n  **ValueError** – If prompt is not provided.\n\n#### `clear()`\n\nClear the cache of all entries.\n\nThis is an alias for delete() to match the BaseCache interface.\n\n* **Return type:**\n  None\n\n#### `delete()`\n\nDelete the entire cache.\n\nThis deletes all entries in the cache by calling the flush API.\n\n* **Return type:**\n  None\n\n#### `delete_by_attributes(attributes)`\n\nDelete cache entries matching the given attributes.\n\n* **Parameters:**\n  **attributes** (*Dict* *[* *str* *,* *Any* *]*) – Attributes to match for deletion.\n  Cannot be empty.\n* **Returns:**\n  Result of the deletion operation.\n* **Return type:**\n  Dict[str, Any]\n* **Raises:**\n  **ValueError** – If attributes is an empty dictionary.\n\n#### `delete_by_id(entry_id)`\n\nDelete a single cache entry by ID.\n\n* **Parameters:**\n  **entry_id** (*str*) – The ID of the entry to delete.\n* **Return type:**\n  None\n\n#### `disconnect()`\n\nDisconnect from Redis.\n\n* **Return type:**\n  None\n\n#### `expire(key, ttl=None)`\n\nSet or refresh the expiration time for a key in the cache.\n\n* **Parameters:**\n  * **key** (*str*) – The Redis key to set the expiration on.\n  * **ttl** (*Optional* *[* *int* *]* *,* *optional*) – The time-to-live in seconds. If None,\n    uses the default TTL configured for this cache instance.\n    Defaults to None.\n* **Return type:**\n  None\n\n> [!NOTE]\n> If neither the provided TTL nor the default TTL is set (both are None),\n> this method will have no effect.\n\n#### `set_ttl(ttl=None)`\n\nSet the default TTL, in seconds, for entries in the cache.\n\n* **Parameters:**\n  **ttl** (*Optional* *[* *int* *]* *,* *optional*) – The optional time-to-live expiration\n  for the cache, in seconds.\n* **Raises:**\n  **ValueError** – If the time-to-live value is not an integer.\n* **Return type:**\n  None\n\n#### `store(prompt, response, vector=None, metadata=None, filters=None, ttl=None)`\n\nStore a prompt-response pair in the cache.\n\n* **Parameters:**\n  * **prompt** (*str*) – The user prompt to cache.\n  * **response** (*str*) – The LLM response to cache.\n  * **vector** (*Optional* *[* *List* *[* *float* *]* *]*) – Not supported by LangCache API.\n  * **metadata** (*Optional* *[* *Dict* *[* *str* *,* *Any* *]* *]*) – Optional metadata (stored as attributes).\n  * **filters** (*Optional* *[* *Dict* *[* *str* *,* *Any* *]* *]*) – Not supported.\n  * **ttl** (*Optional* *[* *int* *]*) – Optional TTL override in seconds.\n* **Returns:**\n  The entry ID for the cached entry.\n* **Return type:**\n  str\n* **Raises:**\n  **ValueError** – If prompt or response is empty.\n\n#### `update(key, **kwargs)`\n\nUpdate specific fields within an existing cache entry.\n\nNote: LangCache API does not support updating individual entries.\nThis method will raise NotImplementedError.\n\n* **Parameters:**\n  * **key** (*str*) – The key of the document to update.\n  * **\\*\\*kwargs** – Field-value pairs to update.\n* **Raises:**\n  **NotImplementedError** – LangCache does not support entry updates.\n* **Return type:**\n  None\n\n#### `property ttl: int | None`\n\nThe default TTL, in seconds, for entries in the cache."},{"id":"cache-schema-classes","title":"Cache Schema Classes","role":"content","text":""},{"id":"cacheentry","title":"`CacheEntry`","role":"content","text":"<a id=\"cache-entry-api\"></a>"},{"id":"class-cacheentry-entry_idnone-prompt-response-prompt_vector-inserted_atfactory-updated_atfactory-metadatanone-filtersnone","title":"`class CacheEntry(*, entry_id=None, prompt, response, prompt_vector, inserted_at=<factory>, updated_at=<factory>, metadata=None, filters=None)`","role":"content","text":"Bases: `BaseModel`\n\nA single cache entry in Redis\n\nCreate a new model by parsing and validating input data from keyword arguments.\n\nRaises [ValidationError][pydantic_core.ValidationError] if the input data cannot be\nvalidated to form a valid model.\n\nself is explicitly positional-only to allow self as a field name.\n\n* **Parameters:**\n  * **entry_id** (*str* *|* *None*)\n  * **prompt** (*str*)\n  * **response** (*str*)\n  * **prompt_vector** (*list* *[* *float* *]*)\n  * **inserted_at** (*float*)\n  * **updated_at** (*float*)\n  * **metadata** (*dict* *[* *str* *,* *Any* *]*  *|* *None*)\n  * **filters** (*dict* *[* *str* *,* *Any* *]*  *|* *None*)\n\n#### `entry_id: str | None`\n\nCache entry identifier\n\n#### `filters: dict[str, Any] | None`\n\nOptional filter data stored on the cache entry for customizing retrieval\n\n#### `inserted_at: float`\n\nTimestamp of when the entry was added to the cache\n\n#### `metadata: dict[str, Any] | None`\n\nOptional metadata stored on the cache entry\n\n#### `model_config: ClassVar[ConfigDict] = {}`\n\nConfiguration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].\n\n#### `prompt: str`\n\nInput prompt or question cached in Redis\n\n#### `prompt_vector: list[float]`\n\nText embedding representation of the prompt\n\n#### `response: str`\n\nResponse or answer to the question, cached in Redis\n\n#### `updated_at: float`\n\nTimestamp of when the entry was updated in the cache"},{"id":"cachehit","title":"`CacheHit`","role":"content","text":"<a id=\"cache-hit-api\"></a>"},{"id":"class-cachehit-entry_id-prompt-response-vector_distance-inserted_at-updated_at-metadatanone-filtersnone-extra_data","title":"`class CacheHit(*, entry_id, prompt, response, vector_distance, inserted_at, updated_at, metadata=None, filters=None, **extra_data)`","role":"content","text":"Bases: `BaseModel`\n\nA cache hit based on some input query\n\nCreate a new model by parsing and validating input data from keyword arguments.\n\nRaises [ValidationError][pydantic_core.ValidationError] if the input data cannot be\nvalidated to form a valid model.\n\nself is explicitly positional-only to allow self as a field name.\n\n* **Parameters:**\n  * **entry_id** (*str*)\n  * **prompt** (*str*)\n  * **response** (*str*)\n  * **vector_distance** (*float*)\n  * **inserted_at** (*float*)\n  * **updated_at** (*float*)\n  * **metadata** (*dict* *[* *str* *,* *Any* *]*  *|* *None*)\n  * **filters** (*dict* *[* *str* *,* *Any* *]*  *|* *None*)\n  * **extra_data** (*Any*)\n\n#### `to_dict()`\n\nConvert this model to a dictionary, merging filters into the result.\n\n* **Return type:**\n  dict[str, *Any*]\n\n#### `entry_id: str`\n\nCache entry identifier\n\n#### `filters: dict[str, Any] | None`\n\nOptional filter data stored on the cache entry for customizing retrieval\n\n#### `inserted_at: float`\n\nTimestamp of when the entry was added to the cache\n\n#### `metadata: dict[str, Any] | None`\n\nOptional metadata stored on the cache entry\n\n#### `model_config: ClassVar[ConfigDict] = {'extra': 'allow'}`\n\nConfiguration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].\n\n#### `prompt: str`\n\nInput prompt or question cached in Redis\n\n#### `response: str`\n\nResponse or answer to the question, cached in Redis\n\n#### `updated_at: float`\n\nTimestamp of when the entry was updated in the cache\n\n#### `vector_distance: float`\n\nThe semantic distance between the query vector and the stored prompt vector\n\n# Embeddings Cache"},{"id":"embeddingscache","title":"EmbeddingsCache","role":"content","text":"<a id=\"embeddings-cache-api\"></a>"},{"id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone","title":"`class EmbeddingsCache(name='embedcache', ttl=None, redis_client=None, async_redis_client=None, redis_url='redis://localhost:6379', connection_kwargs=None)`","role":"content","text":"Bases: `BaseCache`\n\nEmbeddings Cache for storing embedding vectors with exact key matching.\n\nInitialize an embeddings cache.\n\n* **Parameters:**\n  * **name** (*str*) – The name of the cache. Defaults to \"embedcache\".\n  * **ttl** (*Optional* *[* *int* *]*) – The time-to-live for cached embeddings. Defaults to None.\n  * **redis_client** (*Optional* *[* *SyncRedisClient* *]*) – Redis client instance. Defaults to None.\n  * **redis_url** (*str*) – Redis URL for connection. Defaults to \"redis://localhost:6379\".\n  * **connection_kwargs** (*Dict* *[* *str* *,* *Any* *]*) – Redis connection arguments. Defaults to {}.\n  * **async_redis_client** (*Redis* *|* *RedisCluster* *|* *None*)\n* **Raises:**\n  **ValueError** – If vector dimensions are invalid\n\n[code example]\n\n#### `async aclear()`\n\nAsynchronously clear the cache of all keys.\n\nDeletes every Redis key under the cache’s prefix (`<name>:`) with\n`SCAN` + `DEL`. The cache object itself stays usable for future\nwrites.\n\n> [!NOTE]\n> `SCAN` is not a point-in-time snapshot, so this is a best-effort\n> sweep rather than an atomic flush:\n\n- Keys written by other clients while the sweep is in progress may\n  or may not be deleted, so the cache is not guaranteed to be empty\n  when this returns. Quiesce writers first if you need that.\n- `SCAN` may return the same key on more than one page. `DEL`\n  on an already-deleted key is a no-op, so this is harmless.\n- Deletion is not atomic across keys. If the call raises partway\n  through, some keys are already gone. The operation is idempotent,\n  so retrying is safe and converges.\n\n* **Return type:**\n  None\n\n#### `async adisconnect()`\n\nAsync disconnect from Redis.\n\n* **Return type:**\n  None\n\n#### `async adrop(content, model_name)`\n\nAsync remove an embedding from the cache.\n\nAsynchronously removes an embedding from the cache.\n\n* **Parameters:**\n  * **content** (*bytes* *|* *str*) – The content that was embedded.\n  * **model_name** (*str*) – The name of the embedding model.\n* **Return type:**\n  None\n\n[code example]\n\n#### `async adrop_by_key(key)`\n\nAsync remove an embedding from the cache by its Redis key.\n\nAsynchronously removes an embedding from the cache by its Redis key.\n\n* **Parameters:**\n  **key** (*str*) – The full Redis key for the embedding.\n* **Return type:**\n  None\n\n[code example]\n\n#### `async aexists(content, model_name)`\n\nAsync check if an embedding exists.\n\nAsynchronously checks if an embedding exists for the given content and model.\n\n* **Parameters:**\n  * **content** (*bytes* *|* *str*) – The content that was embedded.\n  * **model_name** (*str*) – The name of the embedding model.\n* **Returns:**\n  True if the embedding exists in the cache, False otherwise.\n* **Return type:**\n  bool\n\n[code example]\n\n#### `async aexists_by_key(key)`\n\nAsync check if an embedding exists for the given Redis key.\n\nAsynchronously checks if an embedding exists for the given Redis key.\n\n* **Parameters:**\n  **key** (*str*) – The full Redis key for the embedding.\n* **Returns:**\n  True if the embedding exists in the cache, False otherwise.\n* **Return type:**\n  bool\n\n[code example]\n\n#### `async aexpire(key, ttl=None)`\n\nAsynchronously set or refresh the expiration time for a key in the cache.\n\n* **Parameters:**\n  * **key** (*str*) – The Redis key to set the expiration on.\n  * **ttl** (*Optional* *[* *int* *]* *,* *optional*) – The time-to-live in seconds. If None,\n    uses the default TTL configured for this cache instance.\n    Defaults to None.\n* **Return type:**\n  None\n\n> [!NOTE]\n> If neither the provided TTL nor the default TTL is set (both are None),\n> this method will have no effect.\n\n#### `async aget(content, model_name)`\n\nAsync get embedding by content and model name.\n\nAsynchronously retrieves a cached embedding for the given content and model name.\nIf found, refreshes the TTL of the entry.\n\n* **Parameters:**\n  * **content** (*bytes* *|* *str*) – The content that was embedded.\n  * **model_name** (*str*) – The name of the embedding model.\n* **Returns:**\n  Embedding cache entry or None if not found.\n* **Return type:**\n  Optional[Dict[str, Any]]\n\n[code example]\n\n#### `async aget_by_key(key)`\n\nAsync get embedding by its full Redis key.\n\nAsynchronously retrieves a cached embedding for the given Redis key.\nIf found, refreshes the TTL of the entry.\n\n* **Parameters:**\n  **key** (*str*) – The full Redis key for the embedding.\n* **Returns:**\n  Embedding cache entry or None if not found.\n* **Return type:**\n  Optional[Dict[str, Any]]\n\n[code example]\n\n#### `async amdrop(contents, model_name)`\n\nAsync remove multiple embeddings from the cache by their contents and model name.\n\nAsynchronously removes multiple embeddings in a single operation.\n\n* **Parameters:**\n  * **contents** (*Iterable* *[* *bytes* *|* *str* *]*) – Iterable of content that was embedded.\n  * **model_name** (*str*) – The name of the embedding model.\n* **Return type:**\n  None\n\n[code example]\n\n#### `async amdrop_by_keys(keys)`\n\nAsync remove multiple embeddings from the cache by their Redis keys.\n\nAsynchronously removes multiple embeddings in a single operation.\n\n* **Parameters:**\n  **keys** (*List* *[* *str* *]*) – List of Redis keys to remove.\n* **Return type:**\n  None\n\n[code example]\n\n#### `async amexists(contents, model_name)`\n\nAsync check if multiple embeddings exist by their contents and model name.\n\nAsynchronously checks existence of multiple embeddings in a single operation.\n\n* **Parameters:**\n  * **contents** (*Iterable* *[* *bytes* *|* *str* *]*) – Iterable of content that was embedded.\n  * **model_name** (*str*) – The name of the embedding model.\n* **Returns:**\n  List of boolean values indicating whether each embedding exists.\n* **Return type:**\n  List[bool]\n\n[code example]\n\n#### `async amexists_by_keys(keys)`\n\nAsync check if multiple embeddings exist by their Redis keys.\n\nAsynchronously checks existence of multiple keys in a single operation.\n\n* **Parameters:**\n  **keys** (*List* *[* *str* *]*) – List of Redis keys to check.\n* **Returns:**\n  List of boolean values indicating whether each key exists.\n  The order matches the input keys order.\n* **Return type:**\n  List[bool]\n\n[code example]\n\n#### `async amget(contents, model_name)`\n\nAsync get multiple embeddings by their contents and model name.\n\nAsynchronously retrieves multiple cached embeddings in a single operation.\nIf found, refreshes the TTL of each entry.\n\n* **Parameters:**\n  * **contents** (*Iterable* *[* *bytes* *|* *str* *]*) – Iterable of content that was embedded.\n  * **model_name** (*str*) – The name of the embedding model.\n* **Returns:**\n  List of embedding cache entries or None for contents not found.\n* **Return type:**\n  List[Optional[Dict[str, Any]]]\n\n[code example]\n\n#### `async amget_by_keys(keys)`\n\nAsync get multiple embeddings by their Redis keys.\n\nAsynchronously retrieves multiple cached embeddings in a single network roundtrip.\nIf found, refreshes the TTL of each entry.\n\n* **Parameters:**\n  **keys** (*List* *[* *str* *]*) – List of Redis keys to retrieve.\n* **Returns:**\n  List of embedding cache entries or None for keys not found.\n  The order matches the input keys order.\n* **Return type:**\n  List[Optional[Dict[str, Any]]]\n\n[code example]\n\n#### `async amset(items, ttl=None)`\n\nAsync store multiple embeddings in a batch operation.\n\nEach item in the input list should be a dictionary with the following fields:\n- ‘content’: The content that was embedded\n- ‘model_name’: The name of the embedding model\n- ‘embedding’: The embedding vector\n- ‘metadata’: Optional metadata to store with the embedding\n\n* **Parameters:**\n  * **items** (*list* *[* *dict* *[* *str* *,* *Any* *]* *]*) – List of dictionaries, each containing content, model_name, embedding, and optional metadata.\n  * **ttl** (*int* *|* *None*) – Optional TTL override for these entries.\n* **Returns:**\n  List of Redis keys where the embeddings were stored.\n* **Return type:**\n  List[str]\n\n> [!NOTE]\n> The batch is pipelined, not transactional, so on a Redis Cluster it\n> fans out across shards. If it fails partway, some entries will have\n> been written; the operation is idempotent, so simply retry it.\n\n[code example]\n\n#### `async aset(content, model_name, embedding, metadata=None, ttl=None)`\n\nAsync store an embedding with its content and model name.\n\nAsynchronously stores an embedding with its content and model name.\n\n* **Parameters:**\n  * **content** (*bytes* *|* *str*) – The content that was embedded.\n  * **model_name** (*str*) – The name of the embedding model.\n  * **embedding** (*List* *[* *float* *]*) – The embedding vector to store.\n  * **metadata** (*Optional* *[* *Dict* *[* *str* *,* *Any* *]* *]*) – Optional metadata to store with the embedding.\n  * **ttl** (*Optional* *[* *int* *]*) – Optional TTL override for this specific entry.\n* **Returns:**\n  The Redis key where the embedding was stored.\n* **Return type:**\n  str\n\n[code example]\n\n#### `clear()`\n\nClear the cache of all keys.\n\nDeletes every Redis key under the cache’s prefix (`<name>:`) with\n`SCAN` + `DEL`. The cache object itself stays usable for future\nwrites.\n\n> [!NOTE]\n> `SCAN` is not a point-in-time snapshot, so this is a best-effort\n> sweep rather than an atomic flush:\n\n- Keys written by other clients while the sweep is in progress may\n  or may not be deleted, so the cache is not guaranteed to be empty\n  when this returns. Quiesce writers first if you need that.\n- `SCAN` may return the same key on more than one page. `DEL`\n  on an already-deleted key is a no-op, so this is harmless.\n- Deletion is not atomic across keys. If the call raises partway\n  through, some keys are already gone. The operation is idempotent,\n  so retrying is safe and converges.\n\n* **Return type:**\n  None\n\n#### `disconnect()`\n\nDisconnect from Redis.\n\n* **Return type:**\n  None\n\n#### `drop(content, model_name)`\n\nRemove an embedding from the cache.\n\n* **Parameters:**\n  * **content** (*bytes* *|* *str*) – The content that was embedded.\n  * **model_name** (*str*) – The name of the embedding model.\n* **Return type:**\n  None\n\n[code example]\n\n#### `drop_by_key(key)`\n\nRemove an embedding from the cache by its Redis key.\n\n* **Parameters:**\n  **key** (*str*) – The full Redis key for the embedding.\n* **Return type:**\n  None\n\n[code example]\n\n#### `exists(content, model_name)`\n\nCheck if an embedding exists for the given content and model.\n\n* **Parameters:**\n  * **content** (*bytes* *|* *str*) – The content that was embedded.\n  * **model_name** (*str*) – The name of the embedding model.\n* **Returns:**\n  True if the embedding exists in the cache, False otherwise.\n* **Return type:**\n  bool\n\n[code example]\n\n#### `exists_by_key(key)`\n\nCheck if an embedding exists for the given Redis key.\n\n* **Parameters:**\n  **key** (*str*) – The full Redis key for the embedding.\n* **Returns:**\n  True if the embedding exists in the cache, False otherwise.\n* **Return type:**\n  bool\n\n[code example]\n\n#### `expire(key, ttl=None)`\n\nSet or refresh the expiration time for a key in the cache.\n\n* **Parameters:**\n  * **key** (*str*) – The Redis key to set the expiration on.\n  * **ttl** (*Optional* *[* *int* *]* *,* *optional*) – The time-to-live in seconds. If None,\n    uses the default TTL configured for this cache instance.\n    Defaults to None.\n* **Return type:**\n  None\n\n> [!NOTE]\n> If neither the provided TTL nor the default TTL is set (both are None),\n> this method will have no effect.\n\n#### `get(content, model_name)`\n\nGet embedding by content and model name.\n\nRetrieves a cached embedding for the given content and model name.\nIf found, refreshes the TTL of the entry.\n\n* **Parameters:**\n  * **content** (*bytes* *|* *str*) – The content that was embedded.\n  * **model_name** (*str*) – The name of the embedding model.\n* **Returns:**\n  Embedding cache entry or None if not found.\n* **Return type:**\n  Optional[Dict[str, Any]]\n\n[code example]\n\n#### `get_by_key(key)`\n\nGet embedding by its full Redis key.\n\nRetrieves a cached embedding for the given Redis key.\nIf found, refreshes the TTL of the entry.\n\n* **Parameters:**\n  **key** (*str*) – The full Redis key for the embedding.\n* **Returns:**\n  Embedding cache entry or None if not found.\n* **Return type:**\n  Optional[Dict[str, Any]]\n\n[code example]\n\n#### `mdrop(contents, model_name)`\n\nRemove multiple embeddings from the cache by their contents and model name.\n\nEfficiently removes multiple embeddings in a single operation.\n\n* **Parameters:**\n  * **contents** (*Iterable* *[* *bytes* *|* *str* *]*) – Iterable of content that was embedded.\n  * **model_name** (*str*) – The name of the embedding model.\n* **Return type:**\n  None\n\n[code example]\n\n#### `mdrop_by_keys(keys)`\n\nRemove multiple embeddings from the cache by their Redis keys.\n\nEfficiently removes multiple embeddings in a single operation.\n\n* **Parameters:**\n  **keys** (*List* *[* *str* *]*) – List of Redis keys to remove.\n* **Return type:**\n  None\n\n[code example]\n\n#### `mexists(contents, model_name)`\n\nCheck if multiple embeddings exist by their contents and model name.\n\nEfficiently checks existence of multiple embeddings in a single operation.\n\n* **Parameters:**\n  * **contents** (*Iterable* *[* *bytes* *|* *str* *]*) – Iterable of content that was embedded.\n  * **model_name** (*str*) – The name of the embedding model.\n* **Returns:**\n  List of boolean values indicating whether each embedding exists.\n* **Return type:**\n  List[bool]\n\n[code example]\n\n#### `mexists_by_keys(keys)`\n\nCheck if multiple embeddings exist by their Redis keys.\n\nEfficiently checks existence of multiple keys in a single operation.\n\n* **Parameters:**\n  **keys** (*List* *[* *str* *]*) – List of Redis keys to check.\n* **Returns:**\n  List of boolean values indicating whether each key exists.\n  The order matches the input keys order.\n* **Return type:**\n  List[bool]\n\n[code example]\n\n#### `mget(contents, model_name)`\n\nGet multiple embeddings by their content and model name.\n\nEfficiently retrieves multiple cached embeddings in a single operation.\nIf found, refreshes the TTL of each entry.\n\n* **Parameters:**\n  * **contents** (*Iterable* *[* *bytes* *|* *str* *]*) – Iterable of content that was embedded.\n  * **model_name** (*str*) – The name of the embedding model.\n* **Returns:**\n  List of embedding cache entries or None for contents not found.\n* **Return type:**\n  List[Optional[Dict[str, Any]]]\n\n[code example]\n\n#### `mget_by_keys(keys)`\n\nGet multiple embeddings by their Redis keys.\n\nEfficiently retrieves multiple cached embeddings in a single network roundtrip.\nIf found, refreshes the TTL of each entry.\n\n* **Parameters:**\n  **keys** (*List* *[* *str* *]*) – List of Redis keys to retrieve.\n* **Returns:**\n  List of embedding cache entries or None for keys not found.\n  The order matches the input keys order.\n* **Return type:**\n  List[Optional[Dict[str, Any]]]\n\n[code example]\n\n#### `mset(items, ttl=None)`\n\nStore multiple embeddings in a batch operation.\n\nEach item in the input list should be a dictionary with the following fields:\n- ‘content’: The input that was embedded\n- ‘model_name’: The name of the embedding model\n- ‘embedding’: The embedding vector\n- ‘metadata’: Optional metadata to store with the embedding\n\n* **Parameters:**\n  * **items** (*list* *[* *dict* *[* *str* *,* *Any* *]* *]*) – List of dictionaries, each containing content, model_name, embedding, and optional metadata.\n  * **ttl** (*int* *|* *None*) – Optional TTL override for these entries.\n* **Returns:**\n  List of Redis keys where the embeddings were stored.\n* **Return type:**\n  List[str]\n\n> [!NOTE]\n> The batch is pipelined, not transactional, so on a Redis Cluster it\n> fans out across shards. If it fails partway, some entries will have\n> been written; the operation is idempotent, so simply retry it.\n\n[code example]\n\n#### `set(content, model_name, embedding, metadata=None, ttl=None)`\n\nStore an embedding with its content and model name.\n\n* **Parameters:**\n  * **content** (*Union* *[* *bytes* *,* *str* *]*) – The content to be embedded.\n  * **model_name** (*str*) – The name of the embedding model.\n  * **embedding** (*List* *[* *float* *]*) – The embedding vector to store.\n  * **metadata** (*Optional* *[* *Dict* *[* *str* *,* *Any* *]* *]*) – Optional metadata to store with the embedding.\n  * **ttl** (*Optional* *[* *int* *]*) – Optional TTL override for this specific entry.\n* **Returns:**\n  The Redis key where the embedding was stored.\n* **Return type:**\n  str\n\n[code example]\n\n#### `set_ttl(ttl=None)`\n\nSet the default TTL, in seconds, for entries in the cache.\n\n* **Parameters:**\n  **ttl** (*Optional* *[* *int* *]* *,* *optional*) – The optional time-to-live expiration\n  for the cache, in seconds.\n* **Raises:**\n  **ValueError** – If the time-to-live value is not an integer.\n* **Return type:**\n  None\n\n#### `property ttl: int | None`\n\nThe default TTL, in seconds, for entries in the cache."}],"examples":[{"id":"class-semanticcachenamellmcache-distance_threshold01-ttlnone-vectorizernone-filterable_fieldsnone-redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-overwritefalse-create_indextrue-kwargs-ex0","language":"python","code":"from redisvl.extensions.cache.llm import SemanticCache\n\n# RedisVL creates the index if it is missing\ncache = SemanticCache(name=\"llmcache\", redis_url=\"redis://localhost:6379\")\n\n# the index is managed externally, or this credential cannot run\n# FT.INFO -- assume the index exists and issue no index command\ncache = SemanticCache(\n    name=\"llmcache\",\n    redis_url=\"redis://localhost:6379\",\n    create_index=False,\n)","section_id":"class-semanticcachenamellmcache-distance_threshold01-ttlnone-vectorizernone-filterable_fieldsnone-redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-overwritefalse-create_indextrue-kwargs"},{"id":"class-semanticcachenamellmcache-distance_threshold01-ttlnone-vectorizernone-filterable_fieldsnone-redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-overwritefalse-create_indextrue-kwargs-ex1","language":"python","code":"response = await cache.acheck(\n    prompt=\"What is the capital city of France?\"\n)","section_id":"class-semanticcachenamellmcache-distance_threshold01-ttlnone-vectorizernone-filterable_fieldsnone-redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-overwritefalse-create_indextrue-kwargs"},{"id":"class-semanticcachenamellmcache-distance_threshold01-ttlnone-vectorizernone-filterable_fieldsnone-redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-overwritefalse-create_indextrue-kwargs-ex2","language":"python","code":"key = await cache.astore(\n    prompt=\"What is the capital city of France?\",\n    response=\"Paris\",\n    metadata={\"city\": \"Paris\", \"country\": \"France\"}\n)","section_id":"class-semanticcachenamellmcache-distance_threshold01-ttlnone-vectorizernone-filterable_fieldsnone-redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-overwritefalse-create_indextrue-kwargs"},{"id":"class-semanticcachenamellmcache-distance_threshold01-ttlnone-vectorizernone-filterable_fieldsnone-redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-overwritefalse-create_indextrue-kwargs-ex3","language":"python","code":"key = await cache.astore('this is a prompt', 'this is a response')\nawait cache.aupdate(\n    key,\n    metadata={\"hit_count\": 1, \"model_name\": \"Llama-2-7b\"}\n)","section_id":"class-semanticcachenamellmcache-distance_threshold01-ttlnone-vectorizernone-filterable_fieldsnone-redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-overwritefalse-create_indextrue-kwargs"},{"id":"class-semanticcachenamellmcache-distance_threshold01-ttlnone-vectorizernone-filterable_fieldsnone-redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-overwritefalse-create_indextrue-kwargs-ex4","language":"python","code":"response = cache.check(\n    prompt=\"What is the capital city of France?\"\n)","section_id":"class-semanticcachenamellmcache-distance_threshold01-ttlnone-vectorizernone-filterable_fieldsnone-redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-overwritefalse-create_indextrue-kwargs"},{"id":"class-semanticcachenamellmcache-distance_threshold01-ttlnone-vectorizernone-filterable_fieldsnone-redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-overwritefalse-create_indextrue-kwargs-ex5","language":"python","code":"key = cache.store(\n    prompt=\"What is the capital city of France?\",\n    response=\"Paris\",\n    metadata={\"city\": \"Paris\", \"country\": \"France\"}\n)","section_id":"class-semanticcachenamellmcache-distance_threshold01-ttlnone-vectorizernone-filterable_fieldsnone-redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-overwritefalse-create_indextrue-kwargs"},{"id":"class-semanticcachenamellmcache-distance_threshold01-ttlnone-vectorizernone-filterable_fieldsnone-redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-overwritefalse-create_indextrue-kwargs-ex6","language":"python","code":"key = cache.store('this is a prompt', 'this is a response')\ncache.update(key, metadata={\"hit_count\": 1, \"model_name\": \"Llama-2-7b\"})","section_id":"class-semanticcachenamellmcache-distance_threshold01-ttlnone-vectorizernone-filterable_fieldsnone-redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-overwritefalse-create_indextrue-kwargs"},{"id":"example-ex0","language":"python","code":"from redisvl.extensions.cache.llm import LangCacheSemanticCache\n\ncache = LangCacheSemanticCache(\n    name=\"my_cache\",\n    server_url=\"https://api.langcache.com\",\n    cache_id=\"your-cache-id\",\n    api_key=\"your-api-key\",\n    ttl=3600\n)\n\n# Store a response\ncache.store(\n    prompt=\"What is the capital of France?\",\n    response=\"Paris\"\n)\n\n# Check for cached responses\nresults = cache.check(prompt=\"What is the capital of France?\")","section_id":"example"},{"id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-ex0","language":"python","code":"cache = EmbeddingsCache(\n    name=\"my_embeddings_cache\",\n    ttl=3600,  # 1 hour\n    redis_url=\"redis://localhost:6379\"\n)","section_id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone"},{"id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-ex1","language":"python","code":"await cache.adrop(\n    content=\"What is machine learning?\",\n    model_name=\"text-embedding-ada-002\"\n)","section_id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone"},{"id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-ex2","language":"python","code":"await cache.adrop_by_key(\"embedcache:1234567890abcdef\")","section_id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone"},{"id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-ex3","language":"python","code":"if await cache.aexists(\"What is machine learning?\", \"text-embedding-ada-002\"):\n    print(\"Embedding is in cache\")","section_id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone"},{"id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-ex4","language":"python","code":"if await cache.aexists_by_key(\"embedcache:1234567890abcdef\"):\n    print(\"Embedding is in cache\")","section_id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone"},{"id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-ex5","language":"python","code":"embedding_data = await cache.aget(\n    content=\"What is machine learning?\",\n    model_name=\"text-embedding-ada-002\"\n)","section_id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone"},{"id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-ex6","language":"python","code":"embedding_data = await cache.aget_by_key(\"embedcache:1234567890abcdef\")","section_id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone"},{"id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-ex7","language":"python","code":"# Remove multiple embeddings asynchronously\nawait cache.amdrop(\n    contents=[\"What is machine learning?\", \"What is deep learning?\"],\n    model_name=\"text-embedding-ada-002\"\n)","section_id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone"},{"id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-ex8","language":"python","code":"# Remove multiple embeddings asynchronously\nawait cache.amdrop_by_keys([\"embedcache:key1\", \"embedcache:key2\"])","section_id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone"},{"id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-ex9","language":"python","code":"# Check if multiple embeddings exist asynchronously\nexists_results = await cache.amexists(\n    contents=[\"What is machine learning?\", \"What is deep learning?\"],\n    model_name=\"text-embedding-ada-002\"\n)","section_id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone"},{"id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-ex10","language":"python","code":"# Check if multiple keys exist asynchronously\nexists_results = await cache.amexists_by_keys([\"embedcache:key1\", \"embedcache:key2\"])","section_id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone"},{"id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-ex11","language":"python","code":"# Get multiple embeddings asynchronously\nembedding_data = await cache.amget(\n    contents=[\"What is machine learning?\", \"What is deep learning?\"],\n    model_name=\"text-embedding-ada-002\"\n)","section_id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone"},{"id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-ex12","language":"python","code":"# Get multiple embeddings asynchronously\nembedding_data = await cache.amget_by_keys([\n    \"embedcache:key1\",\n    \"embedcache:key2\"\n])","section_id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone"},{"id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-ex13","language":"python","code":"# Store multiple embeddings asynchronously\nkeys = await cache.amset([\n    {\n        \"content\": \"What is ML?\",\n        \"model_name\": \"text-embedding-ada-002\",\n        \"embedding\": [0.1, 0.2, 0.3],\n        \"metadata\": {\"source\": \"user\"}\n    },\n    {\n        \"content\": \"What is AI?\",\n        \"model_name\": \"text-embedding-ada-002\",\n        \"embedding\": [0.4, 0.5, 0.6],\n        \"metadata\": {\"source\": \"docs\"}\n    }\n])","section_id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone"},{"id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-ex14","language":"python","code":"key = await cache.aset(\n    content=\"What is machine learning?\",\n    model_name=\"text-embedding-ada-002\",\n    embedding=[0.1, 0.2, 0.3, ...],\n    metadata={\"source\": \"user_query\"}\n)","section_id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone"},{"id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-ex15","language":"python","code":"cache.drop(\n    content=\"What is machine learning?\",\n    model_name=\"text-embedding-ada-002\"\n)","section_id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone"},{"id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-ex16","language":"python","code":"cache.drop_by_key(\"embedcache:1234567890abcdef\")","section_id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone"},{"id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-ex17","language":"python","code":"if cache.exists(\"What is machine learning?\", \"text-embedding-ada-002\"):\n    print(\"Embedding is in cache\")","section_id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone"},{"id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-ex18","language":"python","code":"if cache.exists_by_key(\"embedcache:1234567890abcdef\"):\n    print(\"Embedding is in cache\")","section_id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone"},{"id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-ex19","language":"python","code":"embedding_data = cache.get(\n    content=\"What is machine learning?\",\n    model_name=\"text-embedding-ada-002\"\n)","section_id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone"},{"id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-ex20","language":"python","code":"embedding_data = cache.get_by_key(\"embedcache:1234567890abcdef\")","section_id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone"},{"id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-ex21","language":"python","code":"# Remove multiple embeddings\ncache.mdrop(\n    contents=[\"What is machine learning?\", \"What is deep learning?\"],\n    model_name=\"text-embedding-ada-002\"\n)","section_id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone"},{"id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-ex22","language":"python","code":"# Remove multiple embeddings\ncache.mdrop_by_keys([\"embedcache:key1\", \"embedcache:key2\"])","section_id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone"},{"id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-ex23","language":"python","code":"# Check if multiple embeddings exist\nexists_results = cache.mexists(\n    contents=[\"What is machine learning?\", \"What is deep learning?\"],\n    model_name=\"text-embedding-ada-002\"\n)","section_id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone"},{"id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-ex24","language":"python","code":"# Check if multiple keys exist\nexists_results = cache.mexists_by_keys([\"embedcache:key1\", \"embedcache:key2\"])","section_id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone"},{"id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-ex25","language":"python","code":"# Get multiple embeddings\nembedding_data = cache.mget(\n    contents=[\"What is machine learning?\", \"What is deep learning?\"],\n    model_name=\"text-embedding-ada-002\"\n)","section_id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone"},{"id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-ex26","language":"python","code":"# Get multiple embeddings\nembedding_data = cache.mget_by_keys([\n    \"embedcache:key1\",\n    \"embedcache:key2\"\n])","section_id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone"},{"id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-ex27","language":"python","code":"# Store multiple embeddings\nkeys = cache.mset([\n    {\n        \"content\": \"What is ML?\",\n        \"model_name\": \"text-embedding-ada-002\",\n        \"embedding\": [0.1, 0.2, 0.3],\n        \"metadata\": {\"source\": \"user\"}\n    },\n    {\n        \"content\": \"What is AI?\",\n        \"model_name\": \"text-embedding-ada-002\",\n        \"embedding\": [0.4, 0.5, 0.6],\n        \"metadata\": {\"source\": \"docs\"}\n    }\n])","section_id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone"},{"id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-ex28","language":"python","code":"key = cache.set(\n    content=\"What is machine learning?\",\n    model_name=\"text-embedding-ada-002\",\n    embedding=[0.1, 0.2, 0.3, ...],\n    metadata={\"source\": \"user_query\"}\n)","section_id":"class-embeddingscachenameembedcache-ttlnone-redis_clientnone-async_redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone"}]}
{"schema_version":2,"id":"develop/ai/redisvl/api/cli","title":"Command Line Interface","url":"https://redis.io/docs/latest/develop/ai/redisvl/api/cli/","summary":"","aliases":["/integrate/redisvl/api/cli"],"tags":[],"last_updated":"2026-10-05T16:11:44+01:00","page_type":"content","content_hash":"5443234e8f9c88b8ffe555d6e7d3982b8d9baeb9f9453339b3cba29338be0865","sections":[{"id":"overview","title":"Overview","role":"overview","text":"RedisVL provides a command line interface (CLI) called `rvl` for managing vector search indices. The CLI enables you to create, inspect, and delete indices directly from your terminal without writing Python code."},{"id":"installation","title":"Installation","role":"setup","text":"The `rvl` command is included when you install RedisVL.\n\n[code example]\n\nVerify the installation by running:\n\n[code example]"},{"id":"connection-configuration","title":"Connection Configuration","role":"content","text":"The CLI connects to Redis using the following resolution order:\n\n1. The `REDIS_URL` environment variable, if set\n2. Explicit connection flags (`--host`, `--port`, `--url`)\n3. Default values (`localhost:6379`)\n\n**Connection Flags**\n\nAll commands that interact with Redis accept these optional flags:\n\n| Flag               | Type    | Description                                     | Default     |\n|--------------------|---------|-------------------------------------------------|-------------|\n| `-u`, `--url`      | string  | Full Redis URL (e.g., `redis://localhost:6379`) | None        |\n| `--host`           | string  | Redis server hostname                           | `localhost` |\n| `-p`, `--port`     | integer | Redis server port                               | `6379`      |\n| `--user`           | string  | Redis username for authentication               | `default`   |\n| `-a`, `--password` | string  | Redis password for authentication               | Empty       |\n| `--ssl`            | flag    | Enable SSL/TLS encryption                       | Disabled    |\n\n**Examples**\n\nConnect using environment variable:\n\n[code example]\n\nConnect with explicit host and port:\n\n[code example]\n\nConnect with authentication and SSL:\n\n[code example]"},{"id":"getting-help","title":"Getting Help","role":"content","text":"All commands support the `-h` and `--help` flags to display usage information.\n\n| Flag           | Description                               |\n|----------------|-------------------------------------------|\n| `-h`, `--help` | Display usage information for the command |\n\n**Examples**\n\n[code example]\n\nRunning `rvl` without any arguments also displays the top-level help message."},{"id":"commands","title":"Commands","role":"syntax","text":""},{"id":"rvl-version","title":"`rvl version`","role":"content","text":"Display the installed RedisVL version.\n\n**Syntax**\n\n[code example]\n\n**Options**\n\n| Option          | Description                                                 |\n|-----------------|-------------------------------------------------------------|\n| `-s`, `--short` | Print only the version number without additional formatting |\n\n**Examples**\n\n[code example]"},{"id":"rvl-index","title":"`rvl index`","role":"content","text":"Manage vector search indices. This command group provides subcommands for creating, inspecting, listing, and removing indices.\n\n**Syntax**\n\n[code example]\n\n**Subcommands**\n\n| Subcommand   | Description                                          |\n|--------------|------------------------------------------------------|\n| `create`     | Create a new index from a YAML schema file           |\n| `info`       | Display detailed information about an index          |\n| `listall`    | List all existing indices in the Redis instance      |\n| `delete`     | Remove an index while preserving the underlying data |\n| `destroy`    | Remove an index and delete all associated data       |\n\n#### `rvl index create`\n\nCreate a new vector search index from a YAML schema definition.\n\n**Syntax**\n\n[code example]\n\n**Required Options**\n\n| Option           | Description                                               |\n|------------------|-----------------------------------------------------------|\n| `-s`, `--schema` | Path to the YAML schema file defining the index structure |\n\n**Example**\n\n[code example]\n\n**Schema File Format**\n\nThe schema file must be valid YAML with the following structure:\n\n[code example]\n\n#### `rvl index info`\n\nDisplay detailed information about an existing index, including field definitions and index options.\n\n**Syntax**\n\n[code example]\n\n**Options**\n\n| Option           | Description                                                    |\n|------------------|----------------------------------------------------------------|\n| `-i`, `--index`  | Name of the index to inspect                                   |\n| `-s`, `--schema` | Path to the schema file (alternative to specifying index name) |\n\n**Example**\n\n[code example]\n\n**Output**\n\nThe command displays two tables:\n\n1. **Index Information** containing the index name, storage type, key prefixes, index options, and indexing status\n2. **Index Fields** listing each field with its name, attribute, type, and any additional field options\n\n#### `rvl index listall`\n\nList all vector search indices in the connected Redis instance.\n\n**Syntax**\n\n[code example]\n\n**Example**\n\n[code example]\n\n**Output**\n\nReturns a numbered list of all index names:\n\n[code example]\n\n> [!NOTE]\n> `listall` uses `FT._LIST`, which Redis tags `@admin` as well as `@search`.\n> An ACL that grants search access and then subtracts `@admin`, such as\n> `+@search -@admin`, denies this command with a permission error even though the\n> rest of the CLI works. The migration commands that discover indexes for you are\n> denied by the same ACL: `rvl migrate helper`, `rvl migrate wizard` when no\n> `-i/--index` is given, and `rvl migrate batch-plan --pattern`. See\n> [Install RedisVL](https://redis.io/docs/latest/../user_guide/installation).\n\n#### `rvl index delete`\n\nRemove an index from Redis while preserving the underlying data. Use this when you want to rebuild an index with a different schema without losing your data.\n\n**Syntax**\n\n[code example]\n\n**Options**\n\n| Option           | Description                                                    |\n|------------------|----------------------------------------------------------------|\n| `-i`, `--index`  | Name of the index to delete                                    |\n| `-s`, `--schema` | Path to the schema file (alternative to specifying index name) |\n\n**Example**\n\n[code example]\n\n#### `rvl index destroy`\n\nRemove an index and permanently delete all associated data from Redis. This operation cannot be undone.\n\n**Syntax**\n\n[code example]\n\n**Options**\n\n| Option           | Description                                                    |\n|------------------|----------------------------------------------------------------|\n| `-i`, `--index`  | Name of the index to destroy                                   |\n| `-s`, `--schema` | Path to the schema file (alternative to specifying index name) |\n\n**Example**\n\n[code example]\n\n> [!WARNING]\n> This command permanently deletes both the index and all documents stored with the index prefix. Ensure you have backups before running this command."},{"id":"rvl-stats","title":"`rvl stats`","role":"content","text":"Display statistics about an existing index, including document counts, memory usage, and indexing performance metrics.\n\n**Syntax**\n\n[code example]\n\n**Options**\n\n| Option           | Description                                                    |\n|------------------|----------------------------------------------------------------|\n| `-i`, `--index`  | Name of the index to query                                     |\n| `-s`, `--schema` | Path to the schema file (alternative to specifying index name) |\n\n**Example**\n\n[code example]\n\n**Statistics Reference**\n\nThe command returns the following metrics:\n\n| Metric                        | Description                                |\n|-------------------------------|--------------------------------------------|\n| `num_docs`                    | Total number of indexed documents          |\n| `num_terms`                   | Number of distinct terms in text fields    |\n| `max_doc_id`                  | Highest internal document ID               |\n| `num_records`                 | Total number of index records              |\n| `percent_indexed`             | Percentage of documents fully indexed      |\n| `hash_indexing_failures`      | Number of documents that failed to index   |\n| `number_of_uses`              | Number of times the index has been queried |\n| `bytes_per_record_avg`        | Average bytes per index record             |\n| `doc_table_size_mb`           | Document table size in megabytes           |\n| `inverted_sz_mb`              | Inverted index size in megabytes           |\n| `key_table_size_mb`           | Key table size in megabytes                |\n| `offset_bits_per_record_avg`  | Average offset bits per record             |\n| `offset_vectors_sz_mb`        | Offset vectors size in megabytes           |\n| `offsets_per_term_avg`        | Average offsets per term                   |\n| `records_per_doc_avg`         | Average records per document               |\n| `sortable_values_size_mb`     | Sortable values size in megabytes          |\n| `total_indexing_time`         | Total time spent indexing in milliseconds  |\n| `total_inverted_index_blocks` | Number of inverted index blocks            |\n| `vector_index_sz_mb`          | Vector index size in megabytes             |"},{"id":"rvl-migrate","title":"`rvl migrate`","role":"content","text":"> [!WARNING]\n> The index migrator is an **experimental** feature. APIs, CLI commands, and on-disk formats (plans, checkpoints, backups) may change in future releases. Review migration plans carefully before applying to production indexes.\n\nManage document-preserving index migrations. This command group provides subcommands for planning, executing, and validating schema migrations that preserve existing data.\n\n**Syntax**\n\n[code example]\n\n**Subcommands**\n\n| Subcommand     | Description                                                    |\n|----------------|----------------------------------------------------------------|\n| `helper`       | Show migration guidance and supported capabilities             |\n| `wizard`       | Interactively build a migration plan and schema patch          |\n| `plan`         | Generate a migration plan from a schema patch or target schema |\n| `apply`        | Execute a reviewed drop/recreate migration plan                |\n| `estimate`     | Estimate disk space required for a migration (dry-run)         |\n| `rollback`     | Restore original vectors from a backup directory               |\n| `validate`     | Validate a completed migration against the live index          |\n| `batch-plan`   | Generate a batch migration plan for multiple indexes           |\n| `batch-apply`  | Execute a batch migration plan with state tracking             |\n| `batch-resume` | Resume an interrupted batch migration                          |\n| `batch-status` | Show status of an in-progress or completed batch migration     |\n\n#### `rvl migrate plan`\n\nGenerate a migration plan for a document-preserving drop/recreate migration.\n\n**Syntax**\n\n[code example]\n\n**Required Options**\n\n| Option            | Description                                                                       |\n|-------------------|-----------------------------------------------------------------------------------|\n| `--index`, `-i`   | Name of the source index to migrate                                               |\n| `--schema-patch`  | Path to a YAML schema patch file (mutually exclusive with `--target-schema`)      |\n| `--target-schema` | Path to a full target schema YAML file (mutually exclusive with `--schema-patch`) |\n\n**Optional Options**\n\n| Option       | Description                                                              |\n|--------------|--------------------------------------------------------------------------|\n| `--plan-out` | Output path for the migration plan YAML (default: `migration_plan.yaml`) |\n\n**Example**\n\n[code example]\n\n#### `rvl migrate apply`\n\nExecute a reviewed drop/recreate migration plan. Use `--async` for large migrations involving vector quantization.\n\n> [!WARNING]\n> Hash vector quantization is unsupported when the same Redis keys are also\n> indexed by another live RediSearch index that expects the old vector\n> datatype. Quantization rewrites vector bytes in the document key itself, so\n> other indexes covering the same key may drop the document or fail to index\n> it. Use an application-level migration with new keys or fields when\n> documents are shared across indexes.\n\n**Syntax**\n\n[code example]\n\n**Required Options**\n\n| Option         | Description                                                                                                                                                                                                    |\n|----------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `--plan`       | Path to the migration plan YAML file                                                                                                                                                                           |\n| `--backup-dir` | Required migration backup directory. Vector backup files are written when hash vector bytes are mutated; index-only and JSON migrations validate and record the directory without writing vector backup files. |\n\n**Optional Options**\n\n| Option               | Description                                                            |\n|----------------------|------------------------------------------------------------------------|\n| `--async`            | Run migration asynchronously (recommended for large quantization jobs) |\n| `--batch-size`       | Keys per pipeline batch (default 500)                                  |\n| `--workers`          | Number of parallel workers for quantization (default 1).               |\n| `--query-check-file` | Path to a YAML file with post-migration query checks                   |\n\n**Example**\n\n[code example]\n\n#### `rvl migrate wizard`\n\nInteractively build a schema patch and migration plan through a guided wizard.\n\n**Syntax**\n\n[code example]\n\n**Example**\n\n[code example]\n\n#### `rvl migrate rollback`\n\nRestore original vector bytes from a retained backup directory. Rollback restores data only; recreate the original index schema separately if the index definition was changed.\n\n**Syntax**\n\n[code example]\n\n**Required Options**\n\n| Option         | Description                                                     |\n|----------------|-----------------------------------------------------------------|\n| `--backup-dir` | Directory containing vector backup files from a prior migration |\n\n**Example**\n\n[code example]\n\n#### `rvl migrate batch-plan`\n\nGenerate a batch plan that applies one shared schema patch to multiple indexes.\n\n**Syntax**\n\n[code example]\n\n#### `rvl migrate batch-apply`\n\nExecute a batch migration plan and write checkpoint state for resume.\n\n**Syntax**\n\n[code example]\n\n**Required Options**\n\n| Option         | Description                                                                                                                                    |\n|----------------|------------------------------------------------------------------------------------------------------------------------------------------------|\n| `--plan`       | Path to the batch plan YAML file                                                                                                               |\n| `--backup-dir` | Required per-index migration backup directory. Stored in checkpoint state and used for vector backup files when hash vector bytes are mutated. |\n\n**Example**\n\n[code example]\n\n#### `rvl migrate batch-resume`\n\nResume an interrupted batch migration from its checkpoint state.\n\n**Syntax**\n\n[code example]\n\nIf `--backup-dir` is omitted, resume uses the backup directory stored in `batch_state.yaml`. Passing a different backup directory for the same checkpoint is rejected.\n\n#### `rvl migrate batch-status`\n\nShow status for an in-progress or completed batch migration.\n\n**Syntax**\n\n[code example]"},{"id":"exit-codes","title":"Exit Codes","role":"content","text":"The CLI returns the following exit codes:\n\n| Code   | Description                                                       |\n|--------|-------------------------------------------------------------------|\n| `0`    | Command completed successfully                                    |\n| `1`    | Command failed due to missing required arguments or invalid input |"},{"id":"related-resources","title":"Related Resources","role":"related","text":"- [The RedisVL CLI](https://redis.io/docs/latest/../user_guide/cli) for a tutorial-style walkthrough\n- [Schema](https://redis.io/docs/latest/schema) for YAML schema format details\n- [Search Index Classes](https://redis.io/docs/latest/searchindex) for the Python `SearchIndex` API"}],"examples":[{"id":"installation-ex0","language":"bash","code":"pip install redisvl","section_id":"installation"},{"id":"installation-ex1","language":"bash","code":"rvl version","section_id":"installation"},{"id":"connection-configuration-ex0","language":"bash","code":"export REDIS_URL=\"redis://localhost:6379\"\nrvl index listall","section_id":"connection-configuration"},{"id":"connection-configuration-ex1","language":"bash","code":"rvl index listall --host myredis.example.com --port 6380","section_id":"connection-configuration"},{"id":"connection-configuration-ex2","language":"bash","code":"rvl index listall --user admin --password secret --ssl","section_id":"connection-configuration"},{"id":"getting-help-ex0","language":"bash","code":"# Display top-level help\nrvl --help\n\n# Display help for a command group\nrvl index --help\n\n# Display help for a specific subcommand\nrvl index create --help","section_id":"getting-help"},{"id":"rvl-version-ex0","language":"bash","code":"rvl version [OPTIONS]","section_id":"rvl-version"},{"id":"rvl-version-ex1","language":"bash","code":"# Full version output\nrvl version\n\n# Version number only\nrvl version --short","section_id":"rvl-version"},{"id":"rvl-index-ex0","language":"bash","code":"rvl index <subcommand> [OPTIONS]","section_id":"rvl-index"},{"id":"rvl-index-ex1","language":"bash","code":"rvl index create -s <schema_file> [CONNECTION_OPTIONS]","section_id":"rvl-index"},{"id":"rvl-index-ex2","language":"bash","code":"rvl index create -s schema.yaml","section_id":"rvl-index"},{"id":"rvl-index-ex3","language":"yaml","code":"version: '0.1.0'\n\nindex:\n    name: my_index\n    prefix: doc\n    storage_type: hash\n\nfields:\n    - name: content\n      type: text\n    - name: embedding\n      type: vector\n      attrs:\n        dims: 768\n        algorithm: hnsw\n        distance_metric: cosine","section_id":"rvl-index"},{"id":"rvl-index-ex4","language":"bash","code":"rvl index info (-i <index_name> | -s <schema_file>) [OPTIONS]","section_id":"rvl-index"},{"id":"rvl-index-ex5","language":"bash","code":"rvl index info -i my_index","section_id":"rvl-index"},{"id":"rvl-index-ex6","language":"bash","code":"rvl index listall [CONNECTION_OPTIONS]","section_id":"rvl-index"},{"id":"rvl-index-ex7","language":"bash","code":"rvl index listall","section_id":"rvl-index"},{"id":"rvl-index-ex8","language":"text","code":"Indices:\n1. products_index\n2. documents_index\n3. embeddings_index","section_id":"rvl-index"},{"id":"rvl-index-ex9","language":"bash","code":"rvl index delete (-i <index_name> | -s <schema_file>) [CONNECTION_OPTIONS]","section_id":"rvl-index"},{"id":"rvl-index-ex10","language":"bash","code":"rvl index delete -i my_index","section_id":"rvl-index"},{"id":"rvl-index-ex11","language":"bash","code":"rvl index destroy (-i <index_name> | -s <schema_file>) [CONNECTION_OPTIONS]","section_id":"rvl-index"},{"id":"rvl-index-ex12","language":"bash","code":"rvl index destroy -i my_index","section_id":"rvl-index"},{"id":"rvl-stats-ex0","language":"bash","code":"rvl stats (-i <index_name> | -s <schema_file>) [OPTIONS]","section_id":"rvl-stats"},{"id":"rvl-stats-ex1","language":"bash","code":"rvl stats -i my_index","section_id":"rvl-stats"},{"id":"rvl-migrate-ex0","language":"bash","code":"rvl migrate <subcommand> [OPTIONS]","section_id":"rvl-migrate"},{"id":"rvl-migrate-ex1","language":"bash","code":"rvl migrate plan --index <name> (--schema-patch <patch.yaml> | --target-schema <schema.yaml>) [OPTIONS]","section_id":"rvl-migrate"},{"id":"rvl-migrate-ex2","language":"bash","code":"rvl migrate plan -i my_index --schema-patch changes.yaml --plan-out plan.yaml","section_id":"rvl-migrate"},{"id":"rvl-migrate-ex3","language":"bash","code":"rvl migrate apply --plan <migration_plan.yaml> --backup-dir <dir> [OPTIONS]","section_id":"rvl-migrate"},{"id":"rvl-migrate-ex4","language":"bash","code":"rvl migrate apply --plan plan.yaml --backup-dir /tmp/backups\nrvl migrate apply --plan plan.yaml --async --backup-dir /tmp/backups --workers 4","section_id":"rvl-migrate"},{"id":"rvl-migrate-ex5","language":"bash","code":"rvl migrate wizard [--index <name>] [OPTIONS]","section_id":"rvl-migrate"},{"id":"rvl-migrate-ex6","language":"bash","code":"rvl migrate wizard -i my_index --plan-out plan.yaml","section_id":"rvl-migrate"},{"id":"rvl-migrate-ex7","language":"bash","code":"rvl migrate rollback --backup-dir <dir> [--index <name>] [OPTIONS]","section_id":"rvl-migrate"},{"id":"rvl-migrate-ex8","language":"bash","code":"rvl migrate rollback --backup-dir /tmp/backups --index my_index","section_id":"rvl-migrate"},{"id":"rvl-migrate-ex9","language":"bash","code":"rvl migrate batch-plan --schema-patch <patch.yaml> (--pattern <glob> | --indexes <name1,name2> | --indexes-file <file>) [OPTIONS]","section_id":"rvl-migrate"},{"id":"rvl-migrate-ex10","language":"bash","code":"rvl migrate batch-apply --plan <batch_plan.yaml> --backup-dir <dir> [OPTIONS]","section_id":"rvl-migrate"},{"id":"rvl-migrate-ex11","language":"bash","code":"rvl migrate batch-apply --plan batch_plan.yaml --backup-dir /tmp/backups","section_id":"rvl-migrate"},{"id":"rvl-migrate-ex12","language":"bash","code":"rvl migrate batch-resume --state <batch_state.yaml> [--plan <batch_plan.yaml>] [--retry-failed] [--backup-dir <dir>]","section_id":"rvl-migrate"},{"id":"rvl-migrate-ex13","language":"bash","code":"rvl migrate batch-status --state <batch_state.yaml>","section_id":"rvl-migrate"}]}
{"schema_version":2,"id":"develop/ai/redisvl/api/exceptions","title":"Exceptions","url":"https://redis.io/docs/latest/develop/ai/redisvl/api/exceptions/","summary":"","aliases":["/integrate/redisvl/api/exceptions"],"tags":[],"last_updated":"2026-10-05T16:11:44+01:00","page_type":"content","content_hash":"2a90e1c6d26c30819e9d3915c89e5db392024ec7b45c612f48a3d38c7f748566","sections":[{"id":"overview","title":"Overview","role":"overview","text":"RedisVL defines its custom exceptions in `redisvl.exceptions`. Every one of them\ninherits from [RedisVLError](#redisvlerror), so catching that single base class is enough to\nhandle any error the core index and query APIs raise on their own behalf. Catch the\nmore specific subclasses when you want to react differently to, for example, a\nschema validation failure than to a Redis connection problem. (The MCP integration\ndefines its own `redisvl.mcp.errors.RedisVLMCPError`, which is outside this\nhierarchy.)\n\n[code example]\n\n> [!NOTE]\n> Exceptions raised by the underlying `redis-py` client, such as\n> `redis.exceptions.ConnectionError`, are not part of this hierarchy. Where\n> RedisVL performs an index or search operation on your behalf it wraps those\n> errors in a [RedisSearchError](#redissearcherror) and chains the original exception, so the\n> underlying cause is still available on `__cause__`. Constructor and argument\n> validation raises standard Python exceptions instead: for example,\n> `VectorQuery(..., ef_runtime=-1)` raises `ValueError` at construction time,\n> before any `try` block around the query run is entered."},{"id":"when-each-error-is-raised","title":"When each error is raised","role":"content","text":"| Exception                                                                | Raised when                                                                                                                           | Typical entry points                                          |\n|--------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------|\n| [SchemaValidationError](#schemavalidationerror)     | An object does not match the index schema. Only raised when the index was<br/>created with `validate_on_load=True`.                   | `load()`                                                      |\n| [QueryValidationError](#queryvalidationerror)       | A query is not valid for the index it targets, for example setting<br/>`ef_runtime` on a vector field that uses the `flat` algorithm. | `query()`                                                     |\n| [RedisSearchError](#redissearcherror)               | An index or search operation fails, including errors returned by Redis<br/>itself.                                                    | `create()`, `exists()`, `delete()`, `search()`, `aggregate()` |\n| [RedisModuleVersionError](#redismoduleversionerror) | The connected Redis or Redis Search version does not support a requested<br/>feature, such as an `svs-vamana` vector field.           | `create()`                                                    |\n| [RedisVLError](#redisvlerror)                       | A load operation fails for a reason not covered by a more specific error.<br/>Also the base class for everything above.               | `load()`                                                      |\n\nAll of the above apply equally to [`SearchIndex`](https://redis.io/docs/latest/searchindex/#searchindex) and\n[`AsyncSearchIndex`](https://redis.io/docs/latest/searchindex/#asyncsearchindex)."},{"id":"handling-errors","title":"Handling errors","role":"content","text":""},{"id":"validating-data-on-load","title":"`Validating data on load`","role":"content","text":"Schema validation is off by default. Pass `validate_on_load=True` to have RedisVL\ncheck each object against the index schema before writing it, and raise\n[SchemaValidationError](#schemavalidationerror) on the first object that does not match.\n\n[code example]\n\nThe error message reports the index of the object within the batch you passed, so a\nfailure part way through a large load still points at a specific record."},{"id":"handling-query-failures","title":"`Handling query failures`","role":"content","text":"[QueryValidationError](#queryvalidationerror) signals a query that cannot run against this index. It\nis a programming error rather than a transient one, so it is usually worth failing\nloudly instead of retrying.\n\n[code example]"},{"id":"separating-configuration-problems-from-redis-problems","title":"`Separating configuration problems from Redis problems`","role":"content","text":"[RedisModuleVersionError](#redismoduleversionerror) is a subclass of [RedisVLError](#redisvlerror), not of\n[RedisSearchError](#redissearcherror), so ordering the `except` clauses lets you distinguish an\nunsupported feature from a genuine Redis failure.\n\n[code example]\n\nInsufficient permissions usually arrive as [RedisSearchError](#redissearcherror) as well.\n`create()` checks whether the index already exists before doing anything, so a\ncredential that cannot run `FT.INFO` fails at that check rather than at\n`FT.CREATE`, with the chained `redis.exceptions.NoPermissionError` on\n`e.__cause__` naming the denied command. The same applies to an existing index whose\nkey prefix falls outside the credential’s key patterns; for an index that does not exist\nyet, the check simply reports it as absent and `create()` proceeds.\n\n`listall()` is the exception: it issues `FT._LIST` directly, so a permission failure\nthere raises `redis.exceptions.NoPermissionError` itself rather than a wrapped\n[RedisSearchError](#redissearcherror). See [Install RedisVL](https://redis.io/docs/latest/../user_guide/installation) for the ACL categories\nRedisVL needs.\n\nWhen the credential genuinely cannot run `FT.INFO`, this error is not something to\nhandle: construct the extension with `create_index=False` instead, which skips the\nexistence check entirely. See [Install RedisVL](https://redis.io/docs/latest/../user_guide/installation)."},{"id":"telling-the-index-is-missing-apart-from-other-failures","title":"`Telling \"the index is missing\" apart from other failures`","role":"content","text":"Redis Search reports an absent index as an ordinary error reply rather than a distinct\ntype, and the wording has changed between versions – older releases say `Unknown index\nname`, Redis 8.6 and earlier say `<name>: no such index`, and Redis 8.8 introduced\n`SEARCH_INDEX_NOT_FOUND Index not found: <name>`. There is no error code to branch on,\nso code that needs to distinguish \"missing\" from \"something went wrong\" has to match the\nmessage.\n\n[`exists()`](https://redis.io/docs/latest/searchindex/#exists) already does this for you, which is the reason\nto prefer it over catching errors from [`info()`](https://redis.io/docs/latest/searchindex/#info): it\nreturns `False` only for a recognized missing-index reply and re-raises everything\nelse, so a permission or connection failure is never reported as an absent index.\n\n[code example]\n\nWhen you do need the distinction elsewhere, read `e.__cause__` rather than the\n[RedisSearchError](#redissearcherror) message: the wrapper interpolates the index name, so an index\nwhose name happens to contain one of the wordings above would make an unrelated failure\nlook like an absence."},{"id":"catching-everything","title":"`Catching everything`","role":"content","text":"When the calling code only needs to know that the operation failed, catch the base\nclass.\n\n[code example]\n\nBecause RedisVL chains the underlying exception when it wraps one, `e.__cause__`\nstill holds the original `redis-py` error where there was one."},{"id":"exception-classes","title":"Exception classes","role":"errors","text":""},{"id":"redisvlerror","title":"`RedisVLError`","role":"content","text":""},{"id":"class-redisvlerror","title":"`class RedisVLError`","role":"content","text":"Bases: `Exception`\n\nBase exception for all RedisVL errors."},{"id":"redissearcherror","title":"`RedisSearchError`","role":"content","text":""},{"id":"class-redissearcherror","title":"`class RedisSearchError`","role":"content","text":"Bases: [RedisVLError](#redisvlerror)\n\nError raised for Redis Search specific operations."},{"id":"schemavalidationerror","title":"`SchemaValidationError`","role":"content","text":""},{"id":"class-schemavalidationerrormessage-indexnone","title":"`class SchemaValidationError(message, index=None)`","role":"content","text":"Bases: [RedisVLError](#redisvlerror)\n\nError when validating data against a schema."},{"id":"queryvalidationerror","title":"`QueryValidationError`","role":"content","text":""},{"id":"class-queryvalidationerror","title":"`class QueryValidationError`","role":"content","text":"Bases: [RedisVLError](#redisvlerror)\n\nError when validating a query."},{"id":"redismoduleversionerror","title":"`RedisModuleVersionError`","role":"content","text":""},{"id":"class-redismoduleversionerror","title":"`class RedisModuleVersionError`","role":"content","text":"Bases: [RedisVLError](#redisvlerror)\n\nError when Redis or module versions are incompatible with requested features.\n\n#### `classmethod for_svs_vamana(min_redis_version)`\n\nCreate error for unsupported SVS-VAMANA.\n\n* **Parameters:**\n  **min_redis_version** (*str*) – Minimum required Redis version\n* **Returns:**\n  RedisModuleVersionError with formatted message"}],"examples":[{"id":"overview-ex0","language":"text","code":"Exception\n└── RedisVLError\n    ├── RedisSearchError\n    ├── SchemaValidationError\n    ├── QueryValidationError\n    └── RedisModuleVersionError","section_id":"overview"},{"id":"validating-data-on-load-ex0","language":"python","code":"from redisvl.index import SearchIndex\nfrom redisvl.exceptions import SchemaValidationError\n\nindex = SearchIndex.from_yaml(\n    \"schema.yaml\",\n    redis_url=\"redis://localhost:6379\",\n    validate_on_load=True,\n)\n\ntry:\n    index.load(data)\nexcept SchemaValidationError as e:\n    # The message identifies the offending object by its position in the\n    # input and describes which field failed and why.\n    print(f\"Invalid record: {e}\")","section_id":"validating-data-on-load"},{"id":"handling-query-failures-ex0","language":"python","code":"from redisvl.query import VectorQuery\nfrom redisvl.exceptions import QueryValidationError\n\nquery = VectorQuery(\n    vector=[0.1, 0.2, 0.3],\n    vector_field_name=\"embedding\",\n    return_fields=[\"title\"],\n    ef_runtime=50,  # only supported by the 'hnsw' algorithm\n)\n\ntry:\n    results = index.query(query)\nexcept QueryValidationError as e:\n    print(f\"Query rejected: {e}\")","section_id":"handling-query-failures"},{"id":"separating-configuration-problems-from-redis-problems-ex0","language":"python","code":"from redisvl.exceptions import RedisModuleVersionError, RedisSearchError\n\ntry:\n    index.create(overwrite=True)\nexcept RedisModuleVersionError as e:\n    # The deployment does not support the requested feature, for example an\n    # 'svs-vamana' field on a Redis version without a new enough Redis Search.\n    print(f\"Unsupported by this Redis deployment: {e}\")\nexcept RedisSearchError as e:\n    # Something went wrong talking to Redis, or the index definition was\n    # rejected. The original redis-py exception is available as e.__cause__.\n    print(f\"Index creation failed: {e}\")","section_id":"separating-configuration-problems-from-redis-problems"},{"id":"telling-the-index-is-missing-apart-from-other-failures-ex0","language":"python","code":"# Prefer this\nif not index.exists():\n    index.create()\n\n# over inspecting the error yourself, which couples your code to the\n# wording of a particular Redis version\ntry:\n    index.info()\nexcept RedisSearchError as e:\n    if \"no such index\" in str(e):  # breaks on Redis 8.8\n        index.create()","section_id":"telling-the-index-is-missing-apart-from-other-failures"},{"id":"catching-everything-ex0","language":"python","code":"from redisvl.exceptions import RedisVLError\n\ntry:\n    index.load(data)\n    results = index.query(query)\nexcept RedisVLError as e:\n    logger.error(\"RedisVL operation failed: %s\", e)\n    raise","section_id":"catching-everything"}]}
{"schema_version":2,"id":"develop/ai/redisvl/api/filter","title":"Filter","url":"https://redis.io/docs/latest/develop/ai/redisvl/api/filter/","summary":"","aliases":["/integrate/redisvl/api/filter"],"tags":[],"last_updated":"2026-10-12T09:34:21+02:00","page_type":"content","content_hash":"b10f5d7ee16aab89b198bc20f03aebc6fbc2961cdcc8a0fbaf1143ea5fff8cd4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"<a id=\"filter-api\"></a>"},{"id":"filterexpression","title":"FilterExpression","role":"content","text":""},{"id":"class-filterexpression_filternone-operatornone-leftnone-rightnone","title":"`class FilterExpression(_filter=None, operator=None, left=None, right=None)`","role":"content","text":"A FilterExpression is a logical combination of filters in RedisVL.\n\nFilterExpressions can be combined using the & and | operators to create\ncomplex expressions that evaluate to the Redis Query language.\n\nThis presents an interface by which users can create complex queries\nwithout having to know the Redis Query language.\n\n[code example]\n\nThis can be combined with the VectorQuery class to create a query:\n\n[code example]\n\n> [!NOTE]\n> Filter expressions are typically not called directly. Instead they are\n> built by combining filter statements using the & and | operators.\n\n* **Parameters:**\n  * **\\_filter** (*str* *|* *None*)\n  * **operator** (*FilterOperator* *|* *None*)\n  * **left** ([FilterExpression](#filterexpression) *|* *None*)\n  * **right** ([FilterExpression](#filterexpression) *|* *None*)"},{"id":"tag","title":"Tag","role":"content","text":""},{"id":"class-tagfield","title":"`class Tag(field)`","role":"content","text":"A Tag filter can be applied to Tag fields\n\n* **Parameters:**\n  **field** (*str*)\n\n#### `__eq__(other)`\n\nCreate a Tag equality filter expression.\n\n* **Parameters:**\n  **other** (*Union* *[* *List* *[* *str* *]* *,* *str* *]*) – The tag(s) to filter on.\n* **Return type:**\n  [FilterExpression](#filterexpression)\n\n[code example]\n\n#### `__mod__(other)`\n\nCreate a Tag wildcard filter expression for pattern matching.\n\nThis enables wildcard pattern matching on tag fields using the `*`\ncharacter. Unlike the equality operator, wildcards are not escaped,\nallowing patterns with wildcards in any position, such as prefix\n(`\"tech*\"`), suffix (`\"*tech\"`), or middle (`\"*tech*\"`)\nmatches.\n\n* **Parameters:**\n  **other** (*Union* *[* *List* *[* *str* *]* *,* *str* *]*) – The tag pattern(s) to filter on.\n  Use `*` for wildcard matching (e.g., `\"tech*\"`, `\"*tech\"`,\n  or `\"*tech*\"`).\n* **Return type:**\n  [FilterExpression](#filterexpression)\n\n[code example]\n\n#### `__ne__(other)`\n\nCreate a Tag inequality filter expression.\n\n* **Parameters:**\n  **other** (*Union* *[* *List* *[* *str* *]* *,* *str* *]*) – The tag(s) to filter on.\n* **Return type:**\n  [FilterExpression](#filterexpression)\n\n[code example]\n\n#### `__str__()`\n\nReturn the Redis Query string for the Tag filter\n\n* **Return type:**\n  str"},{"id":"text","title":"Text","role":"content","text":""},{"id":"class-textfield","title":"`class Text(field)`","role":"content","text":"A Text is a FilterField representing a text field in a Redis index.\n\n> [!NOTE]\n> `==` and `!=` match the value as a quoted phrase. Any `\"` in the\n> value becomes a space first, so the value cannot close that phrase; a\n> quote already separates tokens at index time, so this matches the same\n> documents that escaping it never could. A value of nothing but quotes\n> therefore becomes an empty phrase, which `==` matches no document\n> against. `%` is the pattern operator and interpolates its value\n> untouched.\n\n* **Parameters:**\n  **field** (*str*)\n\n#### `__eq__(other)`\n\nCreate a Text equality filter expression. These expressions yield\nfilters that enforce an exact match on the supplied term(s).\n\n* **Parameters:**\n  **other** (*str*) – The text value to filter on.\n* **Return type:**\n  [FilterExpression](#filterexpression)\n\n[code example]\n\n#### `__mod__(other)`\n\nCreate a Text \"LIKE\" filter expression. A flexible expression that\nyields filters that can use a variety of additional operators like\nwildcards (\\*), fuzzy matches (%%), or combinatorics (|) of the supplied\nterm(s).\n\n* **Parameters:**\n  **other** (*str*) – The text value to filter on.\n* **Return type:**\n  [FilterExpression](#filterexpression)\n\n[code example]\n\n> [!NOTE]\n> The value is interpolated raw, which is what makes `*`, `%%` and\n> `|` work. A value carrying a `)` therefore closes this clause and\n> has its remainder parsed as query syntax, past any surrounding\n> filter. Pass only patterns your own code composes; for a value you\n> did not construct, use `==`, which matches it as a literal phrase.\n\n#### `__ne__(other)`\n\nCreate a Text inequality filter expression. These expressions yield\nnegated filters on exact matches on the supplied term(s). Opposite of an\nequality filter expression.\n\n* **Parameters:**\n  **other** (*str*) – The text value to filter on.\n* **Return type:**\n  [FilterExpression](#filterexpression)\n\n[code example]\n\n#### `__str__()`\n\nReturn the Redis Query string for the Text filter\n\n* **Return type:**\n  str"},{"id":"num","title":"Num","role":"content","text":""},{"id":"class-numfield","title":"`class Num(field)`","role":"content","text":"A Num is a FilterField representing a numeric field in a Redis index.\n\n* **Parameters:**\n  **field** (*str*)\n\n#### `__eq__(other)`\n\nCreate a Numeric equality filter expression.\n\n* **Parameters:**\n  **other** (*Union* *[* *int* *,* *float* *]*) – The value to filter on.\n* **Return type:**\n  [FilterExpression](#filterexpression)\n\n[code example]\n\n#### `__ge__(other)`\n\nCreate a Numeric greater than or equal to filter expression.\n\n* **Parameters:**\n  **other** (*Union* *[* *int* *,* *float* *]*) – The value to filter on.\n* **Return type:**\n  [FilterExpression](#filterexpression)\n\n[code example]\n\n#### `__gt__(other)`\n\nCreate a Numeric greater than filter expression.\n\n* **Parameters:**\n  **other** (*Union* *[* *int* *,* *float* *]*) – The value to filter on.\n* **Return type:**\n  [FilterExpression](#filterexpression)\n\n[code example]\n\n#### `__le__(other)`\n\nCreate a Numeric less than or equal to filter expression.\n\n* **Parameters:**\n  **other** (*Union* *[* *int* *,* *float* *]*) – The value to filter on.\n* **Return type:**\n  [FilterExpression](#filterexpression)\n\n[code example]\n\n#### `__lt__(other)`\n\nCreate a Numeric less than filter expression.\n\n* **Parameters:**\n  **other** (*Union* *[* *int* *,* *float* *]*) – The value to filter on.\n* **Return type:**\n  [FilterExpression](#filterexpression)\n\n[code example]\n\n#### `__ne__(other)`\n\nCreate a Numeric inequality filter expression.\n\n* **Parameters:**\n  **other** (*Union* *[* *int* *,* *float* *]*) – The value to filter on.\n* **Return type:**\n  [FilterExpression](#filterexpression)\n\n[code example]\n\n#### `__str__()`\n\nReturn the Redis Query string for the Numeric filter\n\n* **Return type:**\n  str\n\n#### `between(start, end, inclusive='both')`\n\nOperator for searching values between two numeric values.\n\n* **Parameters:**\n  * **start** (*Union* *[* *int* *,* *float* *]*) – The lower bound of the range.\n  * **end** (*Union* *[* *int* *,* *float* *]*) – The upper bound of the range.\n  * **inclusive** (*str* *,* *optional*) – Which bounds to include: \"both\",\n    \"neither\", \"left\" or \"right\". Defaults to \"both\".\n* **Raises:**\n  * **TypeError** – If either bound is not an `int`, a `float`, or\n        another `numbers.Real`. numpy scalars qualify; `Decimal`\n        and `str` do not.\n  * **ValueError** – If either bound is NaN, or if `inclusive` is not one\n        of the four accepted values.\n* **Return type:**\n  [FilterExpression](#filterexpression)\n\n[code example]"},{"id":"geo","title":"Geo","role":"content","text":""},{"id":"class-geofield","title":"`class Geo(field)`","role":"content","text":"A Geo is a FilterField representing a geographic (longitude, latitude)\nfield in a Redis index.\n\n> [!NOTE]\n> Redis indexes latitudes only within +/-85.05112878 degrees (EPSG:900913).\n> A document or a query center nearer a pole than that is silently\n> excluded: the query returns no error and no results, at any radius.\n\n* **Parameters:**\n  **field** (*str*)\n\n#### `__eq__(other)`\n\nCreate a geographic filter within a specified GeoRadius.\n\n* **Parameters:**\n  **other** ([GeoRadius](#georadius)) – The geographic spec to filter on.\n* **Return type:**\n  [FilterExpression](#filterexpression)\n\n[code example]\n\n#### `__ne__(other)`\n\nCreate a geographic filter outside of a specified GeoRadius.\n\n* **Parameters:**\n  **other** ([GeoRadius](#georadius)) – The geographic spec to filter on.\n* **Return type:**\n  [FilterExpression](#filterexpression)\n\n[code example]\n\n#### `__str__()`\n\nReturn the Redis Query string for the Geo filter\n\n* **Return type:**\n  str"},{"id":"georadius","title":"GeoRadius","role":"content","text":""},{"id":"class-georadiuslongitude-latitude-radius1-unitkm","title":"`class GeoRadius(longitude, latitude, radius=1, unit='km')`","role":"content","text":"A GeoRadius is a GeoSpec representing a geographic radius.\n\nCreate a GeoRadius specification (GeoSpec)\n\n* **Parameters:**\n  * **longitude** (*float*) – The longitude of the center of the radius, in\n    degrees, from -180 to 180.\n  * **latitude** (*float*) – The latitude of the center of the radius, in\n    degrees, from -90 to 90.\n  * **radius** (*float* *,* *optional*) – The radius of the circle, in `unit`,\n    greater than 0. Fractional radii are sent as given, so 0.5 with\n    a unit of \"km\" is half a kilometre. Defaults to 1.\n  * **unit** (*str* *,* *optional*) – The unit of the radius. Defaults to \"km\".\n* **Raises:**\n  * **TypeError** – If a coordinate or the radius is not an `int`, a\n        `float`, or another `numbers.Real`. numpy scalars qualify;\n        `Decimal` and `str` do not.\n  * **ValueError** – If a coordinate is NaN, infinite, or outside its range,\n        if the radius is NaN, infinite, or not greater than 0, or if\n        the unit is not a string spelling one of \"m\", \"km\", \"mi\", or\n        \"ft\".\n\n#### `__init__(longitude, latitude, radius=1, unit='km')`\n\nCreate a GeoRadius specification (GeoSpec)\n\n* **Parameters:**\n  * **longitude** (*float*) – The longitude of the center of the radius, in\n    degrees, from -180 to 180.\n  * **latitude** (*float*) – The latitude of the center of the radius, in\n    degrees, from -90 to 90.\n  * **radius** (*float* *,* *optional*) – The radius of the circle, in `unit`,\n    greater than 0. Fractional radii are sent as given, so 0.5 with\n    a unit of \"km\" is half a kilometre. Defaults to 1.\n  * **unit** (*str* *,* *optional*) – The unit of the radius. Defaults to \"km\".\n* **Raises:**\n  * **TypeError** – If a coordinate or the radius is not an `int`, a\n        `float`, or another `numbers.Real`. numpy scalars qualify;\n        `Decimal` and `str` do not.\n  * **ValueError** – If a coordinate is NaN, infinite, or outside its range,\n        if the radius is NaN, infinite, or not greater than 0, or if\n        the unit is not a string spelling one of \"m\", \"km\", \"mi\", or\n        \"ft\"."},{"id":"timestamp","title":"Timestamp","role":"content","text":""},{"id":"class-timestampfield","title":"`class Timestamp(field)`","role":"content","text":"A timestamp filter for querying date/time fields in Redis.\n\nThis filter can handle various date and time formats, including:\n- datetime objects (with or without timezone)\n- date objects\n- ISO-8601 formatted strings\n- Unix timestamps (as integers or floats)\n\nAll timestamps are converted to Unix timestamps in UTC for consistency.\nBare date values and date-only ISO strings are anchored to the UTC calendar\nday, not the host’s local day, and naive datetimes are read as UTC.\n\n* **Parameters:**\n  **field** (*str*)\n\n#### `__eq__(other)`\n\nFilter for timestamps equal to the specified value.\nFor date objects (without time), this matches the entire UTC calendar\nday, from 00:00:00 to 23:59:59.999999 UTC.\n\n* **Parameters:**\n  **other** (*datetime* *|* *date* *|* *str* *|* *int* *|* *float*) – A datetime, date, ISO string, or Unix timestamp\n* **Returns:**\n  The filter object for method chaining\n* **Return type:**\n  self\n\n#### `__ge__(other)`\n\nFilter for timestamps greater than or equal to the specified value.\n\nFor a bare date (or date-only ISO string), this means from the *start* of\nthat UTC day, so the day itself is included.\n\n* **Parameters:**\n  **other** – A datetime, date, ISO string, or Unix timestamp\n* **Returns:**\n  The filter object for method chaining\n* **Return type:**\n  self\n\n#### `__gt__(other)`\n\nFilter for timestamps greater than the specified value.\n\nFor a bare date (or date-only ISO string), this means after the *end* of\nthat UTC day, so the day itself is excluded.\n\n* **Parameters:**\n  **other** – A datetime, date, ISO string, or Unix timestamp\n* **Returns:**\n  The filter object for method chaining\n* **Return type:**\n  self\n\n#### `__le__(other)`\n\nFilter for timestamps less than or equal to the specified value.\n\nFor a bare date (or date-only ISO string), this means through the *end* of\nthat UTC day, so the day itself is included.\n\n* **Parameters:**\n  **other** – A datetime, date, ISO string, or Unix timestamp\n* **Returns:**\n  The filter object for method chaining\n* **Return type:**\n  self\n\n#### `__lt__(other)`\n\nFilter for timestamps less than the specified value.\n\nFor a bare date (or date-only ISO string), this means before the *start*\nof that UTC day, so the day itself is excluded.\n\n* **Parameters:**\n  **other** – A datetime, date, ISO string, or Unix timestamp\n* **Returns:**\n  The filter object for method chaining\n* **Return type:**\n  self\n\n#### `__ne__(other)`\n\nFilter for timestamps not equal to the specified value.\nFor date objects (without time), this excludes the entire UTC calendar\nday, from 00:00:00 to 23:59:59.999999 UTC.\n\n* **Parameters:**\n  **other** (*datetime* *|* *date* *|* *str* *|* *int* *|* *float*) – A datetime, date, ISO string, or Unix timestamp\n* **Returns:**\n  The filter object for method chaining\n* **Return type:**\n  self\n\n#### `between(start, end, inclusive='both')`\n\nFilter for timestamps between start and end (inclusive).\n\nBare dates (and date-only ISO strings) span whole UTC calendar days:\nstart anchors to 00:00:00 of its day and end to 23:59:59.999999 of its\nday, so both endpoint days are covered in full.\n\n* **Parameters:**\n  * **start** – A datetime, date, ISO string, or Unix timestamp\n  * **end** – A datetime, date, ISO string, or Unix timestamp\n  * **inclusive** (*str*) – Which endpoints to include – \"both\" (default), \"left\",\n    \"right\", or \"neither\".\n* **Returns:**\n  The filter object for method chaining\n* **Return type:**\n  self"}],"examples":[{"id":"class-filterexpression_filternone-operatornone-leftnone-rightnone-ex0","language":"python","code":"from redisvl.query.filter import Tag, Num\n\nbrand_is_nike = Tag(\"brand\") == \"nike\"\nprice_is_over_100 = Num(\"price\") < 100\nf = brand_is_nike & price_is_over_100\n\nprint(str(f))\n\n>> (@brand:{nike} @price:[-inf (100)])","section_id":"class-filterexpression_filternone-operatornone-leftnone-rightnone"},{"id":"class-filterexpression_filternone-operatornone-leftnone-rightnone-ex1","language":"python","code":"from redisvl.query import VectorQuery\n\nv = VectorQuery(\n    vector=[0.1, 0.1, 0.5, ...],\n    vector_field_name=\"product_embedding\",\n    return_fields=[\"product_id\", \"brand\", \"price\"],\n    filter_expression=f,\n)","section_id":"class-filterexpression_filternone-operatornone-leftnone-rightnone"},{"id":"class-tagfield-ex0","language":"python","code":"from redisvl.query.filter import Tag\n\nf = Tag(\"brand\") == \"nike\"","section_id":"class-tagfield"},{"id":"class-tagfield-ex1","language":"python","code":"from redisvl.query.filter import Tag\n\nf = Tag(\"category\") % \"tech*\"               # Prefix match\nf = Tag(\"category\") % \"*tech\"               # Suffix match\nf = Tag(\"category\") % \"*tech*\"              # Contains match\nf = Tag(\"category\") % \"elec*|*soft\"         # Multiple wildcard patterns\nf = Tag(\"category\") % [\"tech*\", \"*science\"] # List of patterns","section_id":"class-tagfield"},{"id":"class-tagfield-ex2","language":"python","code":"from redisvl.query.filter import Tag\nf = Tag(\"brand\") != \"nike\"","section_id":"class-tagfield"},{"id":"class-textfield-ex0","language":"python","code":"from redisvl.query.filter import Text\n\nf = Text(\"job\") == \"engineer\"","section_id":"class-textfield"},{"id":"class-textfield-ex1","language":"python","code":"from redisvl.query.filter import Text\n\nf = Text(\"job\") % \"engine*\"         # suffix wild card match\nf = Text(\"job\") % \"%%engine%%\"      # fuzzy match w/ Levenshtein Distance\nf = Text(\"job\") % \"engineer|doctor\" # contains either term in field\nf = Text(\"job\") % \"engineer doctor\" # contains both terms in field","section_id":"class-textfield"},{"id":"class-textfield-ex2","language":"python","code":"from redisvl.query.filter import Text\n\nf = Text(\"job\") != \"engineer\"","section_id":"class-textfield"},{"id":"class-numfield-ex0","language":"python","code":"from redisvl.query.filter import Num\nf = Num(\"zipcode\") == 90210","section_id":"class-numfield"},{"id":"class-numfield-ex1","language":"python","code":"from redisvl.query.filter import Num\n\nf = Num(\"age\") >= 18","section_id":"class-numfield"},{"id":"class-numfield-ex2","language":"python","code":"from redisvl.query.filter import Num\n\nf = Num(\"age\") > 18","section_id":"class-numfield"},{"id":"class-numfield-ex3","language":"python","code":"from redisvl.query.filter import Num\n\nf = Num(\"age\") <= 18","section_id":"class-numfield"},{"id":"class-numfield-ex4","language":"python","code":"from redisvl.query.filter import Num\n\nf = Num(\"age\") < 18","section_id":"class-numfield"},{"id":"class-numfield-ex5","language":"python","code":"from redisvl.query.filter import Num\n\nf = Num(\"zipcode\") != 90210","section_id":"class-numfield"},{"id":"class-numfield-ex6","language":"python","code":"from redisvl.query.filter import Num\n\nf = Num(\"age\").between(18, 65)\nf = Num(\"age\").between(18, 65, inclusive=\"neither\")","section_id":"class-numfield"},{"id":"class-geofield-ex0","language":"python","code":"from redisvl.query.filter import Geo, GeoRadius\n\nf = Geo(\"location\") == GeoRadius(-122.4194, 37.7749, 1, unit=\"m\")","section_id":"class-geofield"},{"id":"class-geofield-ex1","language":"python","code":"from redisvl.query.filter import Geo, GeoRadius\n\nf = Geo(\"location\") != GeoRadius(-122.4194, 37.7749, 1, unit=\"m\")","section_id":"class-geofield"}]}
{"schema_version":2,"id":"develop/ai/redisvl/api","title":"RedisVL API","url":"https://redis.io/docs/latest/develop/ai/redisvl/api/","summary":"","aliases":["/integrate/redisvl/api"],"tags":[],"last_updated":"2026-09-11T10:30:42+02:00","children":[{"id":"develop/ai/redisvl/api/cli","summary":"","title":"Command Line Interface","url":"https://redis.io/docs/latest/develop/ai/redisvl/api/cli/"},{"id":"develop/ai/redisvl/api/exceptions","summary":"","title":"Exceptions","url":"https://redis.io/docs/latest/develop/ai/redisvl/api/exceptions/"},{"id":"develop/ai/redisvl/api/filter","summary":"","title":"Filter","url":"https://redis.io/docs/latest/develop/ai/redisvl/api/filter/"},{"id":"develop/ai/redisvl/api/cache","summary":"","title":"LLM Cache","url":"https://redis.io/docs/latest/develop/ai/redisvl/api/cache/"},{"id":"develop/ai/redisvl/api/message_history","summary":"","title":"LLM Message History","url":"https://redis.io/docs/latest/develop/ai/redisvl/api/message_history/"},{"id":"develop/ai/redisvl/api/query","summary":"","title":"Query","url":"https://redis.io/docs/latest/develop/ai/redisvl/api/query/"},{"id":"develop/ai/redisvl/api/reranker","summary":"","title":"Rerankers","url":"https://redis.io/docs/latest/develop/ai/redisvl/api/reranker/"},{"id":"develop/ai/redisvl/api/schema","summary":"","title":"Schema","url":"https://redis.io/docs/latest/develop/ai/redisvl/api/schema/"},{"id":"develop/ai/redisvl/api/searchindex","summary":"","title":"Search Index Classes","url":"https://redis.io/docs/latest/develop/ai/redisvl/api/searchindex/"},{"id":"develop/ai/redisvl/api/router","summary":"","title":"Semantic Router","url":"https://redis.io/docs/latest/develop/ai/redisvl/api/router/"},{"id":"develop/ai/redisvl/api/vector","summary":"","title":"Vector","url":"https://redis.io/docs/latest/develop/ai/redisvl/api/vector/"},{"id":"develop/ai/redisvl/api/vectorizer","summary":"","title":"Vectorizers","url":"https://redis.io/docs/latest/develop/ai/redisvl/api/vectorizer/"}],"page_type":"content","content_hash":"baf11022352ce481556d4749d7dcf8622178c703d20ba0f222ed9040f9596bb5","sections":[{"id":"content","title":"Content","role":"content","text":"Reference documentation for the RedisVL API.\n\n\n\n* [Schema](schema/)\n  * [IndexSchema](schema/#indexschema)\n  * [Index-Level Stopwords Configuration](schema/#index-level-stopwords-configuration)\n  * [Defining Fields](schema/#defining-fields)\n  * [Basic Field Types](schema/#basic-field-types)\n  * [Vector Field Types](schema/#vector-field-types)\n  * [SVS-VAMANA Configuration Utilities](schema/#svs-vamana-configuration-utilities)\n  * [Vector Algorithm Comparison](schema/#vector-algorithm-comparison)\n* [Search Index Classes](searchindex/)\n  * [SearchIndex](searchindex/#searchindex)\n  * [AsyncSearchIndex](searchindex/#asyncsearchindex)\n  * [SearchResults](searchindex/#searchresults)\n* [Vector](vector/)\n  * [Vector](vector/#id1)\n* [Query](query/)\n  * [VectorQuery](query/#vectorquery)\n  * [VectorRangeQuery](query/#vectorrangequery)\n  * [AggregateHybridQuery](query/#aggregatehybridquery)\n  * [HybridQuery](query/#hybridquery)\n  * [TextQuery](query/#textquery)\n  * [FilterQuery](query/#filterquery)\n  * [CountQuery](query/#countquery)\n  * [MultiVectorQuery](query/#multivectorquery)\n  * [SQLQuery](query/#sqlquery)\n* [Filter](filter/)\n  * [FilterExpression](filter/#filterexpression)\n  * [Tag](filter/#tag)\n  * [Text](filter/#text)\n  * [Num](filter/#num)\n  * [Geo](filter/#geo)\n  * [GeoRadius](filter/#georadius)\n  * [Timestamp](filter/#timestamp)\n* [Vectorizers](vectorizer/)\n  * [HFTextVectorizer](vectorizer/#hftextvectorizer)\n  * [OpenAITextVectorizer](vectorizer/#openaitextvectorizer)\n  * [AzureOpenAITextVectorizer](vectorizer/#azureopenaitextvectorizer)\n  * [VertexAIVectorizer](vectorizer/#vertexaivectorizer)\n  * [GoogleGenAIVectorizer](vectorizer/#googlegenaivectorizer)\n  * [CohereTextVectorizer](vectorizer/#coheretextvectorizer)\n  * [BedrockVectorizer](vectorizer/#bedrockvectorizer)\n  * [CustomVectorizer](vectorizer/#customvectorizer)\n  * [VoyageAIVectorizer](vectorizer/#voyageaivectorizer)\n  * [MistralAITextVectorizer](vectorizer/#mistralaitextvectorizer)\n* [Rerankers](reranker/)\n  * [CohereReranker](reranker/#coherereranker)\n  * [HFCrossEncoderReranker](reranker/#hfcrossencoderreranker)\n  * [VoyageAIReranker](reranker/#voyageaireranker)\n* [LLM Cache](cache/)\n  * [SemanticCache](cache/#semanticcache)\n  * [LangCacheSemanticCache](cache/#langcachesemanticcache)\n  * [Cache Schema Classes](cache/#cache-schema-classes)\n* [Embeddings Cache](cache/#embeddings-cache)\n  * [EmbeddingsCache](cache/#embeddingscache)\n* [LLM Message History](message_history/)\n  * [SemanticMessageHistory](message_history/#semanticmessagehistory)\n  * [MessageHistory](message_history/#messagehistory)\n* [Semantic Router](router/)\n  * [Semantic Router](router/#semantic-router-api)\n  * [Routing Config](router/#routing-config)\n  * [Route](router/#route)\n  * [Route Match](router/#route-match)\n  * [Distance Aggregation Method](router/#distance-aggregation-method)\n* [Command Line Interface](cli/)\n  * [Installation](cli/#installation)\n  * [Connection Configuration](cli/#connection-configuration)\n  * [Getting Help](cli/#getting-help)\n  * [Commands](cli/#commands)\n  * [Exit Codes](cli/#exit-codes)\n  * [Related Resources](cli/#related-resources)\n* [Exceptions](exceptions/)\n  * [When each error is raised](exceptions/#when-each-error-is-raised)\n  * [Handling errors](exceptions/#handling-errors)\n  * [Exception classes](exceptions/#exception-classes)"}],"examples":[]}
{"schema_version":2,"id":"develop/ai/redisvl/api/message_history","title":"LLM Message History","url":"https://redis.io/docs/latest/develop/ai/redisvl/api/message_history/","summary":"","aliases":["/integrate/redisvl/api/message_history"],"tags":[],"last_updated":"2026-10-05T16:11:44+01:00","page_type":"content","content_hash":"bb690e3505da7a69496801faa1c494e1bcfd7cd2200f335f380448616c712736","sections":[{"id":"semanticmessagehistory","title":"SemanticMessageHistory","role":"overview","text":"<a id=\"semantic-message-history-api\"></a>"},{"id":"class-semanticmessagehistoryname-session_tagnone-prefixnone-vectorizernone-distance_threshold03-redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-overwritefalse-create_indextrue-kwargs","title":"`class SemanticMessageHistory(name, session_tag=None, prefix=None, vectorizer=None, distance_threshold=0.3, redis_client=None, redis_url='redis://localhost:6379', connection_kwargs=None, overwrite=False, create_index=True, **kwargs)`","role":"content","text":"Bases: `BaseMessageHistory`\n\nInitialize message history with index\n\nSemantic Message History stores the current and previous user text prompts\nand LLM responses to allow for enriching future prompts with session\ncontext. Message history is stored in individual user or LLM prompts and\nresponses.\n\n* **Parameters:**\n  * **name** (*str*) – The name of the message history index.\n  * **session_tag** (*Optional* *[* *str* *]*) – Tag to be added to entries to link to a specific\n    conversation session. Defaults to instance ULID.\n  * **prefix** (*Optional* *[* *str* *]*) – Prefix for the keys for this message data.\n    Defaults to None and will be replaced with the index name.\n  * **vectorizer** (*Optional* *[* *BaseVectorizer* *]*) – The vectorizer used to create embeddings.\n  * **distance_threshold** (*float*) – The maximum semantic distance to be\n    included in the context. Defaults to 0.3.\n  * **redis_client** (*Optional* *[* *Redis* *]*) – A Redis client instance. Defaults to\n    None.\n  * **redis_url** (*str* *,* *optional*) – The redis url. Defaults to redis://localhost:6379.\n  * **connection_kwargs** (*Dict* *[* *str* *,* *Any* *]*) – The connection arguments\n    for the redis client. Defaults to empty {}.\n  * **overwrite** (*bool*) – Whether or not to force overwrite the schema for\n    the semantic message index. Defaults to false.\n  * **create_index** (*bool*) – Whether RedisVL creates and validates the index.\n    When False the constructor issues no index command at all: the\n    index must already exist with a compatible schema, and a live\n    index whose prefix or storage type differs is not detected –\n    which produces empty results rather than an error. See\n    [`SemanticCache`](https://redis.io/docs/latest/cache/#semanticcache) for a worked\n    example, and [Install RedisVL](https://redis.io/docs/latest/../user_guide/installation) for the ACL details.\n    Defaults to True.\n* **Raises:**\n  **ValueError** – If both create_index is False and overwrite is True.\n\nThe proposed schema will support a single vector embedding constructed\nfrom either the prompt or response in a single string.\n\n#### `add_message(message, session_tag=None)`\n\nInsert a single prompt or response into the message history.\nA timestamp is associated with it so that it can be later sorted\nin sequential ordering after retrieval.\n\n* **Parameters:**\n  * **message** (*Dict* *[* *str* *,**str* *]*) – The user prompt or LLM response.\n  * **session_tag** (*Optional* *[* *str* *]*) – Tag to be added to entry to link to a specific\n    conversation session. Defaults to instance ULID.\n* **Return type:**\n  None\n\n#### `add_messages(messages, session_tag=None)`\n\nInsert a list of prompts and responses into the session memory.\nA timestamp is associated with each so that they can be later sorted\nin sequential ordering after retrieval.\n\n* **Parameters:**\n  * **messages** (*List* *[* *Dict* *[* *str* *,* *str* *]* *]*) – The list of user prompts and LLM responses.\n  * **session_tag** (*Optional* *[* *str* *]*) – Tag to be added to entries to link to a specific\n    conversation session. Defaults to instance ULID.\n* **Return type:**\n  None\n\n#### `clear()`\n\nDelete every message, leaving the index in place.\n\nClears by index membership, so it removes the documents the live index\ncovers. Available under `create_index=False`; dropping the index is\n[delete](#delete).\n\n> [!WARNING]\n> Under `create_index=False` the live index is unverified, so if its\n> prefix differs from this instance’s it removes documents this\n> instance never wrote. See [Install RedisVL](https://redis.io/docs/latest/../user_guide/installation).\n\n* **Return type:**\n  None\n\n#### `count(session_tag=None)`\n\nCount the number of messages in the conversation history.\n\n* **Parameters:**\n  **session_tag** (*Optional* *[* *str* *]*) – The session tag to filter messages by.\n  If None, counts all messages in the history.\n\n#### `delete()`\n\nRemove every message and drop the search index.\n\n* **Raises:**\n  **ValueError** – If `create_index=False`. Use [clear](#clear) to\n      remove the messages and leave the index standing.\n* **Return type:**\n  None\n\n#### `drop(id=None)`\n\nRemove a specific exchange from the message history.\n\n* **Parameters:**\n  **id** (*Optional* *[* *str* *]*) – The id of the message entry to delete.\n  If None then the last entry is deleted.\n* **Return type:**\n  None\n\n#### `get_recent(top_k=5, as_text=False, raw=False, session_tag=None, role=None)`\n\nRetrieve the recent message history in sequential order.\n\n* **Parameters:**\n  * **top_k** (*int*) – The number of previous exchanges to return. Default is 5.\n  * **as_text** (*bool*) – Whether to return the conversation as a single string,\n    or list of alternating prompts and responses.\n  * **raw** (*bool*) – Whether to return the full Redis hash entry or just the\n    prompt and response\n  * **session_tag** (*Optional* *[* *str* *]*) – Tag of the entries linked to a specific\n    conversation session. Defaults to instance ULID.\n  * **role** (*Optional* *[* *Union* *[* *str* *,* *List* *[* *str* *]* *]* *]*) – Filter messages by role(s).\n    Can be a single role string (\"system\", \"user\", \"llm\", \"tool\") or\n    a list of roles. If None, all roles are returned.\n* **Returns:**\n  A single string transcription of the session\n  : or list of strings if as_text is false.\n* **Return type:**\n  Union[str, List[str]]\n* **Raises:**\n  **ValueError** – if top_k is not an integer greater than or equal to 0,\n      or if role contains invalid values.\n\n#### `get_relevant(prompt, as_text=False, top_k=5, fall_back=False, session_tag=None, raw=False, distance_threshold=None, role=None)`\n\nSearches the message history for information semantically related to\nthe specified prompt.\n\nThis method uses vector similarity search with a text prompt as input.\nIt checks for semantically similar prompts and responses and gets\nthe top k most relevant previous prompts or responses to include as\ncontext to the next LLM call.\n\n* **Parameters:**\n  * **prompt** (*str*) – The message text to search for in message history\n  * **as_text** (*bool*) – Whether to return the prompts and responses as text\n  * **JSON.** (*or as*)\n  * **top_k** (*int*) – The number of previous messages to return. Default is 5.\n  * **session_tag** (*Optional* *[* *str* *]*) – Tag of the entries linked to a specific\n    conversation session. Defaults to instance ULID.\n  * **distance_threshold** (*Optional* *[* *float* *]*) – The threshold for semantic\n    vector distance.\n  * **fall_back** (*bool*) – Whether to drop back to recent conversation history\n    if no relevant context is found.\n  * **raw** (*bool*) – Whether to return the full Redis hash entry or just the\n    message.\n  * **role** (*Optional* *[* *Union* *[* *str* *,* *List* *[* *str* *]* *]* *]*) – Filter messages by role(s).\n    Can be a single role string (\"system\", \"user\", \"llm\", \"tool\") or\n    a list of roles. If None, all roles are returned.\n* **Returns:**\n  Either a list of strings, or a\n  list of prompts and responses in JSON containing the most relevant.\n* **Return type:**\n  Union[List[str], List[Dict[str,str]]\n\nRaises ValueError: if top_k is not an integer greater or equal to 0,\n: or if role contains invalid values.\n\n#### `store(prompt, response, session_tag=None)`\n\nInsert a prompt:response pair into the message history. A timestamp\nis associated with each message so that they can be later sorted\nin sequential ordering after retrieval.\n\n* **Parameters:**\n  * **prompt** (*str*) – The user prompt to the LLM.\n  * **response** (*str*) – The corresponding LLM response.\n  * **session_tag** (*Optional* *[* *str* *]*) – Tag to be added to entries to link to a specific\n    conversation session. Defaults to instance ULID.\n* **Return type:**\n  None\n\n#### `property messages: list[str] | list[dict[str, str]]`\n\nReturns the full message history."},{"id":"messagehistory","title":"MessageHistory","role":"content","text":"<a id=\"message-history-api\"></a>"},{"id":"class-messagehistoryname-session_tagnone-prefixnone-redis_clientnone-redis_urlredislocalhost6379-connection_kwargsnone-create_indextrue-kwargs","title":"`class MessageHistory(name, session_tag=None, prefix=None, redis_client=None, redis_url='redis://localhost:6379', connection_kwargs=None, create_index=True, **kwargs)`","role":"content","text":"Bases: `BaseMessageHistory`\n\nInitialize message history\n\nMessage History stores the current and previous user text prompts and\nLLM responses to allow for enriching future prompts with session\ncontext. Message history is stored in individual user or LLM prompts and\nresponses.\n\n* **Parameters:**\n  * **name** (*str*) – The name of the message history index.\n  * **session_tag** (*Optional* *[* *str* *]*) – Tag to be added to entries to link to a specific\n    conversation session. Defaults to instance ULID.\n  * **prefix** (*Optional* *[* *str* *]*) – Prefix for the keys for this conversation data.\n    Defaults to None and will be replaced with the index name.\n  * **redis_client** (*Optional* *[* *Redis* *]*) – A Redis client instance. Defaults to\n    None.\n  * **redis_url** (*str* *,* *optional*) – The redis url. Defaults to redis://localhost:6379.\n  * **connection_kwargs** (*Dict* *[* *str* *,* *Any* *]*) – The connection arguments\n    for the redis client. Defaults to empty {}.\n  * **create_index** (*bool*) – Whether RedisVL creates the index. When False\n    the constructor issues no index command at all: the index must\n    already exist over this name and prefix. This class never\n    validates an existing index’s schema, so nothing further is\n    verified either way – and as elsewhere, a live index whose\n    prefix or storage type differs from this one is not detected and\n    produces empty results rather than an error. See\n    [`SemanticCache`](https://redis.io/docs/latest/cache/#semanticcache) for a worked\n    example, and [Install RedisVL](https://redis.io/docs/latest/../user_guide/installation) for the ACL details.\n    Defaults to True.\n\n#### `add_message(message, session_tag=None)`\n\nInsert a single prompt or response into the message history.\nA timestamp is associated with it so that it can be later sorted\nin sequential ordering after retrieval.\n\n* **Parameters:**\n  * **message** (*Dict* *[* *str* *,**str* *]*) – The user prompt or LLM response.\n  * **session_tag** (*Optional* *[* *str* *]*) – Tag to be added to entries to link to a specific\n    conversation session. Defaults to instance ULID.\n* **Return type:**\n  None\n\n#### `add_messages(messages, session_tag=None)`\n\nInsert a list of prompts and responses into the message history.\nA timestamp is associated with each so that they can be later sorted\nin sequential ordering after retrieval.\n\n* **Parameters:**\n  * **messages** (*List* *[* *Dict* *[* *str* *,* *str* *]* *]*) – The list of user prompts and LLM responses.\n  * **session_tag** (*Optional* *[* *str* *]*) – Tag to be added to entries to link to a specific\n    conversation session. Defaults to instance ULID.\n* **Return type:**\n  None\n\n#### `clear()`\n\nDelete every message, leaving the index in place.\n\nClears by index membership, so it removes the documents the live index\ncovers. Available under `create_index=False`; dropping the index is\n[delete](#delete).\n\n> [!WARNING]\n> Under `create_index=False` the live index is unverified, so if its\n> prefix differs from this instance’s it removes documents this\n> instance never wrote. See [Install RedisVL](https://redis.io/docs/latest/../user_guide/installation).\n\n* **Return type:**\n  None\n\n#### `count(session_tag=None)`\n\nCount the number of messages in the conversation history.\n\n* **Parameters:**\n  **session_tag** (*Optional* *[* *str* *]*) – The session tag to filter messages by.\n  If None, counts all messages in the history.\n\n#### `delete()`\n\nRemove every message and drop the search index.\n\n* **Raises:**\n  **ValueError** – If `create_index=False`. Use [clear](#clear) to\n      remove the messages and leave the index standing.\n* **Return type:**\n  None\n\n#### `drop(id=None)`\n\nRemove a specific exchange from the conversation history.\n\n* **Parameters:**\n  **id** (*Optional* *[* *str* *]*) – The id of the message entry to delete.\n  If None then the last entry is deleted.\n* **Return type:**\n  None\n\n#### `get_recent(top_k=5, as_text=False, raw=False, session_tag=None, role=None)`\n\nRetrieve the recent message history in sequential order.\n\n* **Parameters:**\n  * **top_k** (*int*) – The number of previous messages to return. Default is 5.\n  * **as_text** (*bool*) – Whether to return the conversation as a single string,\n    or list of alternating prompts and responses.\n  * **raw** (*bool*) – Whether to return the full Redis hash entry or just the\n    prompt and response.\n  * **session_tag** (*Optional* *[* *str* *]*) – Tag of the entries linked to a specific\n    conversation session. Defaults to instance ULID.\n  * **role** (*Optional* *[* *Union* *[* *str* *,* *List* *[* *str* *]* *]* *]*) – Filter messages by role(s).\n    Can be a single role string (\"system\", \"user\", \"llm\", \"tool\") or\n    a list of roles. If None, all roles are returned.\n* **Returns:**\n  A single string transcription of the messages\n  : or list of strings if as_text is false.\n* **Return type:**\n  Union[str, List[str]]\n* **Raises:**\n  **ValueError** – if top_k is not an integer greater than or equal to 0,\n      or if role contains invalid values.\n\n#### `store(prompt, response, session_tag=None)`\n\nInsert a prompt:response pair into the message history. A timestamp\nis associated with each exchange so that they can be later sorted\nin sequential ordering after retrieval.\n\n* **Parameters:**\n  * **prompt** (*str*) – The user prompt to the LLM.\n  * **response** (*str*) – The corresponding LLM response.\n  * **session_tag** (*Optional* *[* *str* *]*) – Tag to be added to entries to link to a specific\n    conversation session. Defaults to instance ULID.\n* **Return type:**\n  None\n\n#### `property messages: list[str] | list[dict[str, str]]`\n\nReturns the full message history."}],"examples":[]}
{"schema_version":2,"id":"develop/ai/redisvl/api/query","title":"Query","url":"https://redis.io/docs/latest/develop/ai/redisvl/api/query/","summary":"","aliases":["/integrate/redisvl/api/query"],"tags":[],"last_updated":"2026-10-05T16:11:44+01:00","page_type":"content","content_hash":"b7dda598eb72b50019f3d0b723782cc1be1752620a522cea7fb76b386663ab3d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Query classes in RedisVL provide a structured way to define simple or complex\nqueries for different use cases. Each query class wraps the `redis-py` Query module\n[https://github.com/redis/redis-py/blob/master/redis/commands/search/query.py](https://github.com/redis/redis-py/blob/master/redis/commands/search/query.py) with extended functionality for ease-of-use."},{"id":"vectorquery","title":"VectorQuery","role":"content","text":""},{"id":"class-vectorqueryvector-vector_field_name-return_fieldsnone-filter_expressionnone-dtypefloat32-num_results10-return_scoretrue-dialect2-sort_bynone-in_orderfalse-hybrid_policynone-batch_sizenone-ef_runtimenone-search_window_sizenone-use_search_historynone-search_buffer_capacitynone-normalize_vector_distancefalse","title":"`class VectorQuery(vector, vector_field_name, return_fields=None, filter_expression=None, dtype='float32', num_results=10, return_score=True, dialect=2, sort_by=None, in_order=False, hybrid_policy=None, batch_size=None, ef_runtime=None, search_window_size=None, use_search_history=None, search_buffer_capacity=None, normalize_vector_distance=False)`","role":"content","text":"Bases: `BaseVectorQuery`, `BaseQuery`\n\nA query for running a vector search along with an optional filter\nexpression.\n\n* **Parameters:**\n  * **vector** (*List* *[* *float* *]*) – The vector to perform the vector search with.\n  * **vector_field_name** (*str*) – The name of the vector field to search\n    against in the database.\n  * **return_fields** (*List* *[* *str* *]*) – The declared fields to return with search\n    results.\n  * **filter_expression** (*Union* *[* *str* *,* [*FilterExpression*](https://redis.io/docs/latest/filter/#filterexpression) *]* *,* *optional*) – A filter to apply\n    along with the vector search. Defaults to None.\n  * **dtype** (*str* *,* *optional*) – The dtype of the vector. Defaults to\n    \"float32\".\n  * **num_results** (*int* *,* *optional*) – The top k results to return from the\n    vector search. Defaults to 10.\n  * **return_score** (*bool* *,* *optional*) – Whether to return the vector\n    distance. Defaults to True.\n  * **dialect** (*int* *,* *optional*) – The Redis Search query dialect.\n    Defaults to 2.\n  * **sort_by** (*Optional* *[* *SortSpec* *]*) – The field(s) to order the results by. Can be:\n    - str: single field name\n    - Tuple[str, str]: (field_name, \"ASC\"|\"DESC\")\n    - List: list of fields or tuples\n    Note: Only the first field is used for Redis sorting.\n    Defaults to None. Results will be ordered by vector distance.\n  * **in_order** (*bool*) – Requires the terms in the field to have\n    the same order as the terms in the query filter, regardless of\n    the offsets between them. Defaults to False.\n  * **hybrid_policy** (*Optional* *[* *str* *]*) – Controls how filters are applied during vector search.\n    Options are \"BATCHES\" (paginates through small batches of nearest neighbors) or\n    \"ADHOC_BF\" (computes scores for all vectors passing the filter).\n    \"BATCHES\" mode is typically faster for queries with selective filters.\n    \"ADHOC_BF\" mode is better when filters match a large portion of the dataset.\n    Defaults to None, which lets Redis auto-select the optimal policy.\n  * **batch_size** (*Optional* *[* *int* *]*) – When hybrid_policy is \"BATCHES\", controls the number\n    of vectors to fetch in each batch. Larger values may improve performance\n    at the cost of memory usage. Only applies when hybrid_policy=\"BATCHES\".\n    Defaults to None, which lets Redis auto-select an appropriate batch size.\n  * **ef_runtime** (*Optional* *[* *int* *]*) – Controls the size of the dynamic candidate list for HNSW\n    algorithm at query time. Higher values improve recall at the expense of\n    slower search performance. Defaults to None, which uses the index-defined value.\n  * **search_window_size** (*Optional* *[* *int* *]*) – The size of the search window for SVS-VAMANA KNN searches.\n    Increasing this value generally yields more accurate but slower search results.\n    Defaults to None, which uses the index-defined value (typically 10).\n  * **use_search_history** (*Optional* *[* *str* *]*) – For SVS-VAMANA indexes, controls whether to use the\n    search buffer or entire search history. Options are \"OFF\", \"ON\", or \"AUTO\".\n    \"AUTO\" is always evaluated internally as \"ON\". Using the entire history may yield\n    a slightly better graph at the cost of more search time.\n    Defaults to None, which uses the index-defined value (typically \"AUTO\").\n  * **search_buffer_capacity** (*Optional* *[* *int* *]*) – Tuning parameter for SVS-VAMANA indexes using\n    two-level compression (LVQ<X>x<Y> or LeanVec types). Determines the number of vector\n    candidates to collect in the first level of search before the re-ranking level.\n    Defaults to None, which uses the index-defined value (typically SEARCH_WINDOW_SIZE).\n  * **normalize_vector_distance** (*bool*) – Redis supports 3 distance metrics: L2 (euclidean),\n    IP (inner product), and COSINE. By default, L2 distance returns an unbounded value.\n    COSINE distance returns a value between 0 and 2. IP returns a value determined by\n    the magnitude of the vector. Setting this flag to true converts COSINE and L2 distance\n    to a similarity score between 0 and 1. Note: setting this flag to true for IP will\n    throw a warning since by definition COSINE similarity is normalized IP.\n* **Raises:**\n  **TypeError** – If filter_expression is not of type redisvl.query.FilterExpression\n\n> [!NOTE]\n> Learn more about vector queries in Redis: [https://redis.io/docs/latest/develop/ai/search-and-query/vectors/#knn-vector-search](https://redis.io/docs/latest/develop/ai/search-and-query/vectors/#knn-vector-search)\n\n#### `dialect(dialect)`\n\nAdd a dialect field to the query.\n\n- **dialect** - dialect version to execute the query under\n\n* **Parameters:**\n  **dialect** (*int*)\n* **Return type:**\n  *Query*\n\n#### `expander(expander)`\n\nAdd an expander field to the query.\n\n- **expander** - the name of the expander\n\n* **Parameters:**\n  **expander** (*str*)\n* **Return type:**\n  *Query*\n\n#### `in_order()`\n\nMatch only documents where the query terms appear in\nthe same order in the document.\ni.e., for the query \"hello world\", we do not match \"world hello\"\n\n* **Return type:**\n  *Query*\n\n#### `language(language)`\n\nAnalyze the query as being in the specified language.\n\n* **Parameters:**\n  **language** (*str*) – The language (e.g. chinese or english)\n* **Return type:**\n  *Query*\n\n#### `limit_fields(*fields)`\n\nLimit the search to specific TEXT fields only.\n\n- **fields**: Each element should be a string, case sensitive field name\n\nfrom the defined schema.\n\n* **Parameters:**\n  **fields** (*str*)\n* **Return type:**\n  *Query*\n\n#### `limit_ids(*ids)`\n\nLimit the results to a specific set of pre-known document\nids of any length.\n\n* **Return type:**\n  *Query*\n\n#### `no_content()`\n\nSet the query to only return ids and not the document content.\n\n* **Return type:**\n  *Query*\n\n#### `no_stopwords()`\n\nPrevent the query from being filtered for stopwords.\nOnly useful in very big queries that you are certain contain\nno stopwords.\n\n* **Return type:**\n  *Query*\n\n#### `paging(offset, num)`\n\nSet the paging for the query (defaults to 0..10).\n\n- **offset**: Paging offset for the results. Defaults to 0\n- **num**: How many results do we want\n\n* **Parameters:**\n  * **offset** (*int*)\n  * **num** (*int*)\n* **Return type:**\n  *Query*\n\n#### `query_string()`\n\nReturn the query string of this query only.\n\n* **Return type:**\n  str\n\n#### `return_fields(*fields, skip_decode=None)`\n\nSet the fields to return with search results.\n\n* **Parameters:**\n  * **\\*fields** – Variable number of field names to return.\n  * **skip_decode** (*str* *|* *list* *[* *str* *]*  *|* *None*) – Optional field name or list of field names that should not be\n    decoded. Useful for binary data like embeddings.\n* **Returns:**\n  Returns the query object for method chaining.\n* **Return type:**\n  self\n* **Raises:**\n  **TypeError** – If skip_decode is not a string, list, or None.\n\n#### `scorer(scorer)`\n\nUse a different scoring function to evaluate document relevance.\nDefault is TFIDF.\n\nSince Redis 8.0 default was changed to BM25STD.\n\n* **Parameters:**\n  **scorer** (*str*) – The scoring function to use\n  (e.g. TFIDF.DOCNORM or BM25)\n* **Return type:**\n  *Query*\n\n#### `set_batch_size(batch_size)`\n\nSet the batch size for the query.\n\n* **Parameters:**\n  **batch_size** (*int*) – The batch size to use when hybrid_policy is \"BATCHES\".\n* **Raises:**\n  * **TypeError** – If batch_size is not an integer\n  * **ValueError** – If batch_size is not positive\n\n#### `set_ef_runtime(ef_runtime)`\n\nSet the EF_RUNTIME parameter for the query.\n\n* **Parameters:**\n  **ef_runtime** (*int*) – The EF_RUNTIME value to use for HNSW algorithm.\n  Higher values improve recall at the expense of slower search.\n* **Raises:**\n  * **TypeError** – If ef_runtime is not an integer\n  * **ValueError** – If ef_runtime is not positive\n\n#### `set_filter(filter_expression=None)`\n\nSet the filter expression for the query.\n\n* **Parameters:**\n  **filter_expression** (*Optional* *[* *Union* *[* *str* *,* [*FilterExpression*](https://redis.io/docs/latest/filter/#filterexpression) *]* *]* *,* *optional*) – The filter\n  expression or query string to use on the query.\n* **Raises:**\n  **TypeError** – If filter_expression is not a valid FilterExpression or string.\n\n#### `set_hybrid_policy(hybrid_policy)`\n\nSet the hybrid policy for the query.\n\n* **Parameters:**\n  **hybrid_policy** (*str*) – The hybrid policy to use. Options are \"BATCHES\"\n  or \"ADHOC_BF\".\n* **Raises:**\n  **ValueError** – If hybrid_policy is not one of the valid options\n\n#### `set_search_buffer_capacity(search_buffer_capacity)`\n\nSet the SEARCH_BUFFER_CAPACITY parameter for the query.\n\n* **Parameters:**\n  **search_buffer_capacity** (*int*) – Tuning parameter for SVS-VAMANA indexes using\n  two-level compression. Determines the number of vector candidates to collect\n  in the first level of search before the re-ranking level.\n* **Raises:**\n  * **TypeError** – If search_buffer_capacity is not an integer\n  * **ValueError** – If search_buffer_capacity is not positive\n\n#### `set_search_window_size(search_window_size)`\n\nSet the SEARCH_WINDOW_SIZE parameter for the query.\n\n* **Parameters:**\n  **search_window_size** (*int*) – The size of the search window for SVS-VAMANA KNN searches.\n  Increasing this value generally yields more accurate but slower search results.\n* **Raises:**\n  * **TypeError** – If search_window_size is not an integer\n  * **ValueError** – If search_window_size is not positive\n\n#### `set_use_search_history(use_search_history)`\n\nSet the USE_SEARCH_HISTORY parameter for the query.\n\n* **Parameters:**\n  **use_search_history** (*str*) – For SVS-VAMANA indexes, controls whether to use the\n  search buffer or entire search history. Options are \"OFF\", \"ON\", or \"AUTO\".\n* **Raises:**\n  * **TypeError** – If use_search_history is not a string\n  * **ValueError** – If use_search_history is not one of \"OFF\", \"ON\", or \"AUTO\"\n\n#### `slop(slop)`\n\nAllow a maximum of N intervening non-matched terms between\nphrase terms (0 means exact phrase).\n\n* **Parameters:**\n  **slop** (*int*)\n* **Return type:**\n  *Query*\n\n#### `sort_by(sort_spec=None, asc=True)`\n\nSet the sort order for query results.\n\nThis method supports sorting by single or multiple fields. Note that Redis Search\nnatively supports only a single SORTBY field. When multiple fields are specified,\nonly the FIRST field is used for the Redis SORTBY clause.\n\n* **Parameters:**\n  * **sort_spec** (*str* *|* *tuple* *[* *str* *,* *str* *]*  *|* *list* *[* *str* *|* *tuple* *[* *str* *,* *str* *]* *]*  *|* *None*) – Sort specification in various formats:\n    - str: single field name\n    - Tuple[str, str]: (field_name, \"ASC\"|\"DESC\")\n    - List: list of field names or tuples\n  * **asc** (*bool*) – Default sort direction when not specified (only used when sort_spec is a string).\n    Defaults to True (ascending).\n* **Returns:**\n  Returns the query object for method chaining.\n* **Return type:**\n  self\n* **Raises:**\n  * **TypeError** – If sort_spec is not a valid type.\n  * **ValueError** – If direction is not \"ASC\" or \"DESC\"."},{"id":"examples","title":"`Examples`","role":"content","text":"[code example]\n\n> [!NOTE]\n> When multiple fields are specified, only the first field is used for sorting\n> in Redis. Future versions may support multi-field sorting through post-query\n> sorting in Python.\n\n#### `timeout(timeout)`\n\noverrides the timeout parameter of the module\n\n* **Parameters:**\n  **timeout** (*float*)\n* **Return type:**\n  *Query*\n\n#### `verbatim()`\n\nSet the query to be verbatim, i.e., use no query expansion\nor stemming.\n\n* **Return type:**\n  *Query*\n\n#### `with_payloads()`\n\nAsk the engine to return document payloads.\n\n* **Return type:**\n  *Query*\n\n#### `with_scores()`\n\nAsk the engine to return document search scores.\n\n* **Return type:**\n  *Query*\n\n#### `property batch_size: int | None`\n\nReturn the batch size for the query.\n\n* **Returns:**\n  The batch size for the query.\n* **Return type:**\n  Optional[int]\n\n#### `property ef_runtime: int | None`\n\nReturn the EF_RUNTIME parameter for the query.\n\n* **Returns:**\n  The EF_RUNTIME value for the query.\n* **Return type:**\n  Optional[int]\n\n#### `property filter: str | `[`FilterExpression`](https://redis.io/docs/latest/filter/#filterexpression)` `\n\nThe filter expression for the query.\n\n#### `property hybrid_policy: str | None`\n\nReturn the hybrid policy for the query.\n\n* **Returns:**\n  The hybrid policy for the query.\n* **Return type:**\n  Optional[str]\n\n#### `property params: dict[str, Any]`\n\nReturn the parameters for the query.\n\n* **Returns:**\n  The parameters for the query.\n* **Return type:**\n  Dict[str, Any]\n\n#### `property query: BaseQuery`\n\nReturn self as the query object.\n\n#### `property search_buffer_capacity: int | None`\n\nReturn the SEARCH_BUFFER_CAPACITY parameter for the query.\n\n* **Returns:**\n  The SEARCH_BUFFER_CAPACITY value for the query.\n* **Return type:**\n  Optional[int]\n\n#### `property search_window_size: int | None`\n\nReturn the SEARCH_WINDOW_SIZE parameter for the query.\n\n* **Returns:**\n  The SEARCH_WINDOW_SIZE value for the query.\n* **Return type:**\n  Optional[int]\n\n#### `property use_search_history: str | None`\n\nReturn the USE_SEARCH_HISTORY parameter for the query.\n\n* **Returns:**\n  The USE_SEARCH_HISTORY value for the query.\n* **Return type:**\n  Optional[str]\n\n> [!NOTE]\n> **Runtime Parameters for Performance Tuning**\n>\n> VectorQuery supports runtime parameters for HNSW and SVS-VAMANA indexes that can be adjusted at query time without rebuilding the index:\n>\n> **HNSW Parameters:**\n>\n> - `ef_runtime`: Controls search accuracy (higher = better recall, slower search)\n>\n> **SVS-VAMANA Parameters:**\n>\n> - `search_window_size`: Size of search window for KNN searches\n> - `use_search_history`: Whether to use search buffer (OFF/ON/AUTO)\n> - `search_buffer_capacity`: Tuning parameter for 2-level compression\n>\n> Example with HNSW runtime parameters:\n>\n> [code example]\n>\n> Example with SVS-VAMANA runtime parameters:\n>\n> [code example]"},{"id":"vectorrangequery","title":"VectorRangeQuery","role":"content","text":""},{"id":"class-vectorrangequeryvector-vector_field_name-return_fieldsnone-filter_expressionnone-dtypefloat32-distance_threshold02-epsilonnone-search_window_sizenone-use_search_historynone-search_buffer_capacitynone-num_results10-return_scoretrue-dialect2-sort_bynone-in_orderfalse-hybrid_policynone-batch_sizenone-normalize_vector_distancefalse","title":"`class VectorRangeQuery(vector, vector_field_name, return_fields=None, filter_expression=None, dtype='float32', distance_threshold=0.2, epsilon=None, search_window_size=None, use_search_history=None, search_buffer_capacity=None, num_results=10, return_score=True, dialect=2, sort_by=None, in_order=False, hybrid_policy=None, batch_size=None, normalize_vector_distance=False)`","role":"content","text":"Bases: `BaseVectorQuery`, `BaseQuery`\n\nA query for running a filtered vector search based on semantic\ndistance threshold.\n\n* **Parameters:**\n  * **vector** (*List* *[* *float* *]*) – The vector to perform the range query with.\n  * **vector_field_name** (*str*) – The name of the vector field to search\n    against in the database.\n  * **return_fields** (*List* *[* *str* *]*) – The declared fields to return with search\n    results.\n  * **filter_expression** (*Union* *[* *str* *,* [*FilterExpression*](https://redis.io/docs/latest/filter/#filterexpression) *]* *,* *optional*) – A filter to apply\n    along with the range query. Defaults to None.\n  * **dtype** (*str* *,* *optional*) – The dtype of the vector. Defaults to\n    \"float32\".\n  * **distance_threshold** (*float*) – The threshold for vector distance.\n    A smaller threshold indicates a stricter semantic search.\n    Defaults to 0.2.\n  * **epsilon** (*Optional* *[* *float* *]*) – The relative factor for vector range queries,\n    setting boundaries for candidates within radius \\* (1 + epsilon).\n    This controls how extensive the search is beyond the specified radius.\n    Higher values increase recall at the expense of performance.\n    Defaults to None, which uses the index-defined epsilon (typically 0.01).\n  * **search_window_size** (*Optional* *[* *int* *]*) – The size of the search window for SVS-VAMANA range searches.\n    Increasing this value generally yields more accurate but slower search results.\n    Defaults to None, which uses the index-defined value (typically 10).\n  * **use_search_history** (*Optional* *[* *str* *]*) – For SVS-VAMANA indexes, controls whether to use the\n    search buffer or entire search history. Options are \"OFF\", \"ON\", or \"AUTO\".\n    \"AUTO\" is always evaluated internally as \"ON\". Using the entire history may yield\n    a slightly better graph at the cost of more search time.\n    Defaults to None, which uses the index-defined value (typically \"AUTO\").\n  * **search_buffer_capacity** (*Optional* *[* *int* *]*) – Tuning parameter for SVS-VAMANA indexes using\n    two-level compression (LVQ<X>x<Y> or LeanVec types). Determines the number of vector\n    candidates to collect in the first level of search before the re-ranking level.\n    Defaults to None, which uses the index-defined value (typically SEARCH_WINDOW_SIZE).\n  * **num_results** (*int*) – The MAX number of results to return.\n    Defaults to 10.\n  * **return_score** (*bool* *,* *optional*) – Whether to return the vector\n    distance. Defaults to True.\n  * **dialect** (*int* *,* *optional*) – The Redis Search query dialect.\n    Defaults to 2.\n  * **sort_by** (*Optional* *[* *SortSpec* *]*) – The field(s) to order the results by. Can be:\n    - str: single field name\n    - Tuple[str, str]: (field_name, \"ASC\"|\"DESC\")\n    - List: list of fields or tuples\n    Note: Only the first field is used for Redis sorting.\n    Defaults to None. Results will be ordered by vector distance.\n  * **in_order** (*bool*) – Requires the terms in the field to have\n    the same order as the terms in the query filter, regardless of\n    the offsets between them. Defaults to False.\n  * **hybrid_policy** (*Optional* *[* *str* *]*) – Controls how filters are applied during vector search.\n    Options are \"BATCHES\" (paginates through small batches of nearest neighbors) or\n    \"ADHOC_BF\" (computes scores for all vectors passing the filter).\n    \"BATCHES\" mode is typically faster for queries with selective filters.\n    \"ADHOC_BF\" mode is better when filters match a large portion of the dataset.\n    Defaults to None, which lets Redis auto-select the optimal policy.\n  * **batch_size** (*Optional* *[* *int* *]*) – When hybrid_policy is \"BATCHES\", controls the number\n    of vectors to fetch in each batch. Larger values may improve performance\n    at the cost of memory usage. Only applies when hybrid_policy=\"BATCHES\".\n    Defaults to None, which lets Redis auto-select an appropriate batch size.\n  * **normalize_vector_distance** (*bool*) – Redis supports 3 distance metrics: L2 (euclidean),\n    IP (inner product), and COSINE. By default, L2 distance returns an unbounded value.\n    COSINE distance returns a value between 0 and 2. IP returns a value determined by\n    the magnitude of the vector. Setting this flag to true converts COSINE and L2 distance\n    to a similarity score between 0 and 1. Note: setting this flag to true for IP will\n    throw a warning since by definition COSINE similarity is normalized IP.\n* **Raises:**\n  **TypeError** – If filter_expression is not of type redisvl.query.FilterExpression\n\n> [!NOTE]\n> Learn more about vector range queries: [https://redis.io/docs/latest/develop/ai/search-and-query/vectors/#range-query](https://redis.io/docs/latest/develop/ai/search-and-query/vectors/#range-query)\n\n#### `dialect(dialect)`\n\nAdd a dialect field to the query.\n\n- **dialect** - dialect version to execute the query under\n\n* **Parameters:**\n  **dialect** (*int*)\n* **Return type:**\n  *Query*\n\n#### `expander(expander)`\n\nAdd an expander field to the query.\n\n- **expander** - the name of the expander\n\n* **Parameters:**\n  **expander** (*str*)\n* **Return type:**\n  *Query*\n\n#### `in_order()`\n\nMatch only documents where the query terms appear in\nthe same order in the document.\ni.e., for the query \"hello world\", we do not match \"world hello\"\n\n* **Return type:**\n  *Query*\n\n#### `language(language)`\n\nAnalyze the query as being in the specified language.\n\n* **Parameters:**\n  **language** (*str*) – The language (e.g. chinese or english)\n* **Return type:**\n  *Query*\n\n#### `limit_fields(*fields)`\n\nLimit the search to specific TEXT fields only.\n\n- **fields**: Each element should be a string, case sensitive field name\n\nfrom the defined schema.\n\n* **Parameters:**\n  **fields** (*str*)\n* **Return type:**\n  *Query*\n\n#### `limit_ids(*ids)`\n\nLimit the results to a specific set of pre-known document\nids of any length.\n\n* **Return type:**\n  *Query*\n\n#### `no_content()`\n\nSet the query to only return ids and not the document content.\n\n* **Return type:**\n  *Query*\n\n#### `no_stopwords()`\n\nPrevent the query from being filtered for stopwords.\nOnly useful in very big queries that you are certain contain\nno stopwords.\n\n* **Return type:**\n  *Query*\n\n#### `paging(offset, num)`\n\nSet the paging for the query (defaults to 0..10).\n\n- **offset**: Paging offset for the results. Defaults to 0\n- **num**: How many results do we want\n\n* **Parameters:**\n  * **offset** (*int*)\n  * **num** (*int*)\n* **Return type:**\n  *Query*\n\n#### `query_string()`\n\nReturn the query string of this query only.\n\n* **Return type:**\n  str\n\n#### `return_fields(*fields, skip_decode=None)`\n\nSet the fields to return with search results.\n\n* **Parameters:**\n  * **\\*fields** – Variable number of field names to return.\n  * **skip_decode** (*str* *|* *list* *[* *str* *]*  *|* *None*) – Optional field name or list of field names that should not be\n    decoded. Useful for binary data like embeddings.\n* **Returns:**\n  Returns the query object for method chaining.\n* **Return type:**\n  self\n* **Raises:**\n  **TypeError** – If skip_decode is not a string, list, or None.\n\n#### `scorer(scorer)`\n\nUse a different scoring function to evaluate document relevance.\nDefault is TFIDF.\n\nSince Redis 8.0 default was changed to BM25STD.\n\n* **Parameters:**\n  **scorer** (*str*) – The scoring function to use\n  (e.g. TFIDF.DOCNORM or BM25)\n* **Return type:**\n  *Query*\n\n#### `set_batch_size(batch_size)`\n\nSet the batch size for the query.\n\n* **Parameters:**\n  **batch_size** (*int*) – The batch size to use when hybrid_policy is \"BATCHES\".\n* **Raises:**\n  * **TypeError** – If batch_size is not an integer\n  * **ValueError** – If batch_size is not positive\n\n#### `set_distance_threshold(distance_threshold)`\n\nSet the distance threshold for the query.\n\n* **Parameters:**\n  **distance_threshold** (*float*) – Vector distance threshold.\n* **Raises:**\n  * **TypeError** – If distance_threshold is not a float or int\n  * **ValueError** – If distance_threshold is negative\n\n#### `set_epsilon(epsilon)`\n\nSet the epsilon parameter for the range query.\n\n* **Parameters:**\n  **epsilon** (*float*) – The relative factor for vector range queries,\n  setting boundaries for candidates within radius \\* (1 + epsilon).\n* **Raises:**\n  * **TypeError** – If epsilon is not a float or int\n  * **ValueError** – If epsilon is negative\n\n#### `set_filter(filter_expression=None)`\n\nSet the filter expression for the query.\n\n* **Parameters:**\n  **filter_expression** (*Optional* *[* *Union* *[* *str* *,* [*FilterExpression*](https://redis.io/docs/latest/filter/#filterexpression) *]* *]* *,* *optional*) – The filter\n  expression or query string to use on the query.\n* **Raises:**\n  **TypeError** – If filter_expression is not a valid FilterExpression or string.\n\n#### `set_hybrid_policy(hybrid_policy)`\n\nSet the hybrid policy for the query.\n\n* **Parameters:**\n  **hybrid_policy** (*str*) – The hybrid policy to use. Options are \"BATCHES\"\n  or \"ADHOC_BF\".\n* **Raises:**\n  **ValueError** – If hybrid_policy is not one of the valid options\n\n#### `set_search_buffer_capacity(search_buffer_capacity)`\n\nSet the SEARCH_BUFFER_CAPACITY parameter for the range query.\n\n* **Parameters:**\n  **search_buffer_capacity** (*int*) – Tuning parameter for SVS-VAMANA indexes using\n  two-level compression.\n* **Raises:**\n  * **TypeError** – If search_buffer_capacity is not an integer\n  * **ValueError** – If search_buffer_capacity is not positive\n\n#### `set_search_window_size(search_window_size)`\n\nSet the SEARCH_WINDOW_SIZE parameter for the range query.\n\n* **Parameters:**\n  **search_window_size** (*int*) – The size of the search window for SVS-VAMANA range searches.\n* **Raises:**\n  * **TypeError** – If search_window_size is not an integer\n  * **ValueError** – If search_window_size is not positive\n\n#### `set_use_search_history(use_search_history)`\n\nSet the USE_SEARCH_HISTORY parameter for the range query.\n\n* **Parameters:**\n  **use_search_history** (*str*) – Controls whether to use the search buffer or entire history.\n  Must be one of \"OFF\", \"ON\", or \"AUTO\".\n* **Raises:**\n  * **TypeError** – If use_search_history is not a string\n  * **ValueError** – If use_search_history is not one of the valid options\n\n#### `slop(slop)`\n\nAllow a maximum of N intervening non-matched terms between\nphrase terms (0 means exact phrase).\n\n* **Parameters:**\n  **slop** (*int*)\n* **Return type:**\n  *Query*\n\n#### `sort_by(sort_spec=None, asc=True)`\n\nSet the sort order for query results.\n\nThis method supports sorting by single or multiple fields. Note that Redis Search\nnatively supports only a single SORTBY field. When multiple fields are specified,\nonly the FIRST field is used for the Redis SORTBY clause.\n\n* **Parameters:**\n  * **sort_spec** (*str* *|* *tuple* *[* *str* *,* *str* *]*  *|* *list* *[* *str* *|* *tuple* *[* *str* *,* *str* *]* *]*  *|* *None*) – Sort specification in various formats:\n    - str: single field name\n    - Tuple[str, str]: (field_name, \"ASC\"|\"DESC\")\n    - List: list of field names or tuples\n  * **asc** (*bool*) – Default sort direction when not specified (only used when sort_spec is a string).\n    Defaults to True (ascending).\n* **Returns:**\n  Returns the query object for method chaining.\n* **Return type:**\n  self\n* **Raises:**\n  * **TypeError** – If sort_spec is not a valid type.\n  * **ValueError** – If direction is not \"ASC\" or \"DESC\"."},{"id":"examples-1","title":"`Examples`","role":"content","text":"[code example]\n\n> [!NOTE]\n> When multiple fields are specified, only the first field is used for sorting\n> in Redis. Future versions may support multi-field sorting through post-query\n> sorting in Python.\n\n#### `timeout(timeout)`\n\noverrides the timeout parameter of the module\n\n* **Parameters:**\n  **timeout** (*float*)\n* **Return type:**\n  *Query*\n\n#### `verbatim()`\n\nSet the query to be verbatim, i.e., use no query expansion\nor stemming.\n\n* **Return type:**\n  *Query*\n\n#### `with_payloads()`\n\nAsk the engine to return document payloads.\n\n* **Return type:**\n  *Query*\n\n#### `with_scores()`\n\nAsk the engine to return document search scores.\n\n* **Return type:**\n  *Query*\n\n#### `property batch_size: int | None`\n\nReturn the batch size for the query.\n\n* **Returns:**\n  The batch size for the query.\n* **Return type:**\n  Optional[int]\n\n#### `property distance_threshold: float`\n\nReturn the distance threshold for the query.\n\n* **Returns:**\n  The distance threshold for the query.\n* **Return type:**\n  float\n\n#### `property epsilon: float | None`\n\nReturn the epsilon for the query.\n\n* **Returns:**\n  The epsilon for the query, or None if not set.\n* **Return type:**\n  Optional[float]\n\n#### `property filter: str | `[`FilterExpression`](https://redis.io/docs/latest/filter/#filterexpression)` `\n\nThe filter expression for the query.\n\n#### `property hybrid_policy: str | None`\n\nReturn the hybrid policy for the query.\n\n* **Returns:**\n  The hybrid policy for the query.\n* **Return type:**\n  Optional[str]\n\n#### `property params: dict[str, Any]`\n\nReturn the parameters for the query.\n\n* **Returns:**\n  The parameters for the query.\n* **Return type:**\n  Dict[str, Any]\n\n#### `property query: BaseQuery`\n\nReturn self as the query object.\n\n#### `property search_buffer_capacity: int | None`\n\nReturn the SEARCH_BUFFER_CAPACITY parameter for the query.\n\n* **Returns:**\n  The SEARCH_BUFFER_CAPACITY value for the query.\n* **Return type:**\n  Optional[int]\n\n#### `property search_window_size: int | None`\n\nReturn the SEARCH_WINDOW_SIZE parameter for the query.\n\n* **Returns:**\n  The SEARCH_WINDOW_SIZE value for the query.\n* **Return type:**\n  Optional[int]\n\n#### `property use_search_history: str | None`\n\nReturn the USE_SEARCH_HISTORY parameter for the query.\n\n* **Returns:**\n  The USE_SEARCH_HISTORY value for the query.\n* **Return type:**\n  Optional[str]\n\n> [!NOTE]\n> **Runtime Parameters for Range Queries**\n>\n> VectorRangeQuery supports runtime parameters for controlling range search behavior:\n>\n> **HNSW & SVS-VAMANA Parameters:**\n>\n> - `epsilon`: Range search approximation factor (default: 0.01)\n>\n> **SVS-VAMANA Parameters:**\n>\n> - `search_window_size`: Size of search window\n> - `use_search_history`: Whether to use search buffer (OFF/ON/AUTO)\n> - `search_buffer_capacity`: Tuning parameter for 2-level compression\n>\n> Example:\n>\n> [code example]"},{"id":"aggregatehybridquery","title":"AggregateHybridQuery","role":"content","text":""},{"id":"class-aggregatehybridquerytext-text_field_name-vector-vector_field_name-text_scorerbm25std-filter_expressionnone-alpha07-dtypefloat32-num_results10-return_fieldsnone-stopwordsenglish-dialect2-text_weightsnone","title":"`class AggregateHybridQuery(text, text_field_name, vector, vector_field_name, text_scorer='BM25STD', filter_expression=None, alpha=0.7, dtype='float32', num_results=10, return_fields=None, stopwords='english', dialect=2, text_weights=None)`","role":"content","text":"Bases: `AggregationQuery`\n\nAggregateHybridQuery combines text and vector search in Redis.\nIt allows you to perform a hybrid search using both text and vector similarity.\nIt scores documents based on a weighted combination of text and vector similarity.\n\n[code example]\n\nInstantiates a AggregateHybridQuery object.\n\n* **Parameters:**\n  * **text** (*str*) – The text to search for.\n  * **text_field_name** (*str*) – The text field name to search in.\n  * **vector** (*Union* *[* *bytes* *,* *List* *[* *float* *]* *]*) – The vector to perform vector similarity search.\n  * **vector_field_name** (*str*) – The vector field name to search in.\n  * **text_scorer** (*str* *,* *optional*) – The text scorer to use. Options are {TFIDF, TFIDF.DOCNORM,\n    BM25, DISMAX, DOCSCORE, BM25STD}. Defaults to \"BM25STD\".\n  * **filter_expression** (*Optional* *[*[*FilterExpression*](https://redis.io/docs/latest/filter/#filterexpression) *]* *,* *optional*) – The filter expression to use.\n    Defaults to None.\n  * **alpha** (*float* *,* *optional*) – The weight of the vector similarity. Documents will be scored\n    as: hybrid_score = (alpha) \\* vector_score + (1-alpha) \\* text_score.\n    Defaults to 0.7.\n  * **dtype** (*str* *,* *optional*) – The data type of the vector. Defaults to \"float32\".\n  * **num_results** (*int* *,* *optional*) – The number of results to return. Defaults to 10.\n  * **return_fields** (*Optional* *[* *List* *[* *str* *]* *]* *,* *optional*) – The fields to return. Defaults to None.\n  * **stopwords** (*Optional* *[* *Union* *[* *str* *,* *Set* *[* *str* *]* *]* *]* *,* *optional*) – \n\n    The stopwords to remove from the\n    provided text prior to search-use. If a string such as \"english\" \"german\" is\n    provided then a default set of stopwords for that language will be used. if a list,\n    set, or tuple of strings is provided then those will be used as stopwords.\n    Defaults to \"english\". if set to \"None\" then no stopwords will be removed.\n\n    Note: This parameter controls query-time stopword filtering (client-side).\n    For index-level stopwords configuration (server-side), see IndexInfo.stopwords.\n    Using query-time stopwords with index-level STOPWORDS 0 is counterproductive.\n  * **dialect** (*int* *,* *optional*) – The Redis dialect version. Defaults to 2.\n  * **text_weights** (*Optional* *[* *Dict* *[* *str* *,* *float* *]* *]*) – The importance weighting of individual words\n    within the query text. Defaults to None, as no modifications will be made to the\n    text_scorer score.\n\n> [!NOTE]\n> AggregateHybridQuery uses FT.AGGREGATE commands which do NOT support runtime\n> parameters. For runtime parameter support (ef_runtime, search_window_size, etc.),\n> use VectorQuery or VectorRangeQuery which use FT.SEARCH commands.\n\n* **Raises:**\n  * **ValueError** – If the text string is empty, or if the text string becomes empty after\n        stopwords are removed.\n  * **TypeError** – If the stopwords are not a set, list, or tuple of strings.\n* **Parameters:**\n  * **text** (*str*)\n  * **text_field_name** (*str*)\n  * **vector** (*bytes* *|* *list* *[* *float* *]*)\n  * **vector_field_name** (*str*)\n  * **text_scorer** (*str*)\n  * **filter_expression** (*str* *|* [*FilterExpression*](https://redis.io/docs/latest/filter/#filterexpression) *|* *None*)\n  * **alpha** (*float*)\n  * **dtype** (*str*)\n  * **num_results** (*int*)\n  * **return_fields** (*list* *[* *str* *]*  *|* *None*)\n  * **stopwords** (*str* *|* *set* *[* *str* *]*  *|* *None*)\n  * **dialect** (*int*)\n  * **text_weights** (*dict* *[* *str* *,* *float* *]*  *|* *None*)\n\n#### `add_scores()`\n\nIf set, includes the score as an ordinary field of the row.\n\n* **Return type:**\n  *AggregateRequest*\n\n#### `apply(**kwexpr)`\n\nSpecify one or more projection expressions to add to each result"},{"id":"parameters","title":"`Parameters`","role":"content","text":"- **kwexpr**: One or more key-value pairs for a projection. The key is\n  : the alias for the projection, and the value is the projection\n    expression itself, for example apply(square_root=\"sqrt(@foo)\")\n\n* **Return type:**\n  *AggregateRequest*\n\n#### `dialect(dialect)`\n\nAdd a dialect field to the aggregate command.\n\n- **dialect** - dialect version to execute the query under\n\n* **Parameters:**\n  **dialect** (*int*)\n* **Return type:**\n  *AggregateRequest*\n\n#### `filter(expressions)`\n\nSpecify filter for post-query results using predicates relating to\nvalues in the result set."},{"id":"parameters-1","title":"`Parameters`","role":"content","text":"- **fields**: Fields to group by. This can either be a single string,\n  : or a list of strings.\n\n* **Parameters:**\n  **expressions** (*str* *|* *List* *[* *str* *]*)\n* **Return type:**\n  *AggregateRequest*\n\n#### `group_by(fields, *reducers)`\n\nSpecify by which fields to group the aggregation."},{"id":"parameters-2","title":"`Parameters`","role":"content","text":"- **fields**: Fields to group by. This can either be a single string,\n  : or a list of strings. both cases, the field should be specified as\n    @field.\n- **reducers**: One or more reducers. Reducers may be found in the\n  : aggregation module.\n\n* **Parameters:**\n  * **fields** (*str* *|* *List* *[* *str* *]*)\n  * **reducers** (*Reducer*)\n* **Return type:**\n  *AggregateRequest*\n\n#### `limit(offset, num)`\n\nSets the limit for the most recent group or query.\n\nIf no group has been defined yet (via group_by()) then this sets\nthe limit for the initial pool of results from the query. Otherwise,\nthis limits the number of items operated on from the previous group.\n\nSetting a limit on the initial search results may be useful when\nattempting to execute an aggregation on a sample of a large data set."},{"id":"parameters-3","title":"`Parameters`","role":"content","text":"- **offset**: Result offset from which to begin paging\n- **num**: Number of results to return\n\nExample of sorting the initial results:\n\n``\nAggregateRequest(\"@sale_amount:[10000, inf]\")            .limit(0, 10)            .group_by(\"@state\", r.count())\n``\n\nWill only group by the states found in the first 10 results of the\nquery @sale_amount:[10000, inf]. On the other hand,\n\n``\nAggregateRequest(\"@sale_amount:[10000, inf]\")            .limit(0, 1000)            .group_by(\"@state\", r.count()            .limit(0, 10)\n``\n\nWill group all the results matching the query, but only return the\nfirst 10 groups.\n\nIf you only wish to return a *top-N* style query, consider using\nsort_by() instead.\n\n* **Parameters:**\n  * **offset** (*int*)\n  * **num** (*int*)\n* **Return type:**\n  *AggregateRequest*\n\n#### `load(*fields)`\n\nIndicate the fields to be returned in the response. These fields are\nreturned in addition to any others implicitly specified."},{"id":"parameters-4","title":"`Parameters`","role":"content","text":"- **fields**: If fields not specified, all the fields will be loaded.\n\nOtherwise, fields should be given in the format of @field.\n\n* **Parameters:**\n  **fields** (*str*)\n* **Return type:**\n  *AggregateRequest*\n\n#### `scorer(scorer)`\n\nUse a different scoring function to evaluate document relevance.\nDefault is TFIDF.\n\n* **Parameters:**\n  **scorer** (*str*) – The scoring function to use\n  (e.g. TFIDF.DOCNORM or BM25)\n* **Return type:**\n  *AggregateRequest*\n\n#### `set_text_weights(weights)`\n\nSet or update the text weights for the query.\n\n* **Parameters:**\n  **weights** (*dict* *[* *str* *,* *float* *]*) – Dictionary of word:weight mappings\n\n#### `sort_by(*fields, **kwargs)`\n\nIndicate how the results should be sorted. This can also be used for\n*top-N* style queries"},{"id":"parameters-5","title":"`Parameters`","role":"content","text":"- **fields**: The fields by which to sort. This can be either a single\n  : field or a list of fields. If you wish to specify order, you can\n    use the Asc or Desc wrapper classes.\n- **max**: Maximum number of results to return. This can be\n  : used instead of LIMIT and is also faster.\n\nExample of sorting by foo ascending and bar descending:\n\n``\nsort_by(Asc(\"@foo\"), Desc(\"@bar\"))\n``\n\nReturn the top 10 customers:\n\n``\nAggregateRequest()            .group_by(\"@customer\", r.sum(\"@paid\").alias(FIELDNAME))            .sort_by(Desc(\"@paid\"), max=10)\n``\n\n* **Parameters:**\n  **fields** (*str*)\n* **Return type:**\n  *AggregateRequest*\n\n#### `with_schema()`\n\nIf set, the schema property will contain a list of [field, type]\nentries in the result object.\n\n* **Return type:**\n  *AggregateRequest*\n\n#### `property params: dict[str, Any]`\n\nReturn the parameters for the aggregation.\n\n* **Returns:**\n  The parameters for the aggregation.\n* **Return type:**\n  Dict[str, Any]\n\n#### `property stopwords: set[str]`\n\nReturn the stopwords used in the query.\n:returns: The stopwords used in the query.\n:rtype: Set[str]\n\n#### `property text_weights: dict[str, float]`\n\nGet the text weights.\n\n* **Returns:**\n  weight mappings.\n* **Return type:**\n  Dictionary of word\n\n> [!NOTE]\n> The `stopwords` parameter in [AggregateHybridQuery](#aggregatehybridquery) (and `HybridQuery`) controls query-time stopword filtering (client-side).\n> For index-level stopwords configuration (server-side), see `redisvl.schema.IndexInfo.stopwords`.\n> Using query-time stopwords with index-level `STOPWORDS 0` is counterproductive.\n\n> [!NOTE]\n> `HybridQuery` and [AggregateHybridQuery](#aggregatehybridquery) apply linear combination inconsistently. `HybridQuery` uses `linear_alpha` to weight the text score, while [AggregateHybridQuery](#aggregatehybridquery) uses `alpha` to weight the vector score. When switching between the two classes, take care to revise your `alpha` setting.\n\n> [!NOTE]\n> **Runtime Parameters for Hybrid Queries**\n>\n> **Important:** AggregateHybridQuery uses FT.AGGREGATE commands which do NOT support runtime parameters.\n> Runtime parameters (`ef_runtime`, `search_window_size`, `use_search_history`, `search_buffer_capacity`)\n> are only supported with FT.SEARCH commands.\n>\n> For runtime parameter support, use `HybridQuery`, [VectorQuery](#vectorquery), or [VectorRangeQuery](#vectorrangequery) instead of AggregateHybridQuery.\n>\n> Example with HybridQuery (supports runtime parameters):\n>\n> [code example]"},{"id":"hybridquery","title":"HybridQuery","role":"content","text":""},{"id":"class-hybridquerytext-text_field_name-vector-vector_field_name-vector_param_namevector-text_scorerbm25std-yield_text_score_asnone-vector_search_methodnone-knn_ef_runtime10-range_radiusnone-range_epsilon001-yield_vsim_score_asnone-filter_expressionnone-combination_methodnone-rrf_window20-rrf_constant60-linear_alpha03-yield_combined_score_asnone-dtypefloat32-num_results10-return_fieldsnone-stopwordsenglish-text_weightsnone","title":"`class HybridQuery(text, text_field_name, vector, vector_field_name, vector_param_name='vector', text_scorer='BM25STD', yield_text_score_as=None, vector_search_method=None, knn_ef_runtime=10, range_radius=None, range_epsilon=0.01, yield_vsim_score_as=None, filter_expression=None, combination_method=None, rrf_window=20, rrf_constant=60, linear_alpha=0.3, yield_combined_score_as=None, dtype='float32', num_results=10, return_fields=None, stopwords='english', text_weights=None)`","role":"content","text":"Bases: `object`\n\nA hybrid search query that combines text search and vector similarity, with configurable fusion methods.\n\n[code example]\n\n> [!NOTE]\n> - [FT.HYBRID command documentation](https://redis.io/docs/latest/commands/ft.hybrid)\n- [redis-py hybrid_search documentation](https://redis.readthedocs.io/en/stable/redismodules.html#redis.commands.search.commands.SearchCommands.hybrid_search)\n\nInstantiates a HybridQuery object.\n\n* **Parameters:**\n  * **text** (*str*) – The text to search for.\n  * **text_field_name** (*str*) – The text field name to search in.\n  * **vector** (*bytes* *|* *list* *[* *float* *]*) – The vector to perform vector similarity search.\n  * **vector_field_name** (*str*) – The vector field name to search in.\n  * **vector_param_name** (*str*) – The name of the parameter substitution containing the vector blob.\n  * **text_scorer** (*str*) – The text scorer to use. Options are {TFIDF, TFIDF.DOCNORM,\n    BM25STD, BM25STD.NORM, BM25STD.TANH, DISMAX, DOCSCORE, HAMMING}. Defaults to \"BM25STD\". For more\n    information about supported scoring algorithms,\n    see [https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/scoring/](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/scoring/)\n  * **yield_text_score_as** (*str* *|* *None*) – The name of the field to yield the text score as.\n  * **vector_search_method** (*Literal* *[* *'KNN'* *,*  *'RANGE'* *]*  *|* *None*) – The vector search method to use. Options are {KNN, RANGE}. Defaults to None.\n  * **knn_ef_runtime** (*int*) – The exploration factor parameter for HNSW, optional if vector_search_method is \"KNN\".\n  * **range_radius** (*float* *|* *None*) – The search radius to use, required if vector_search_method is \"RANGE\".\n  * **range_epsilon** (*float*) – The epsilon value to use, optional if vector_search_method is \"RANGE\"; defines the\n    accuracy of the search.\n  * **yield_vsim_score_as** (*str* *|* *None*) – The name of the field to yield the vector similarity score as.\n  * **filter_expression** (*str* *|* [*FilterExpression*](https://redis.io/docs/latest/filter/#filterexpression) *|* *None*) – The filter expression to use for both the text and vector searches. Defaults to None.\n  * **combination_method** (*Literal* *[* *'RRF'* *,*  *'LINEAR'* *]*  *|* *None*) – The combination method to use. Options are {RRF, LINEAR}. If not specified, the server\n    defaults to RRF. If \"RRF\" is specified, then at least one of rrf_window or rrf_constant must be\n    provided. If \"LINEAR\" is specified, then at least one of linear_alpha or linear_beta must be\n    provided.\n  * **rrf_window** (*int*) – The window size to use for the reciprocal rank fusion (RRF) combination method. Limits\n    fusion scope.\n  * **rrf_constant** (*int*) – The constant to use for the reciprocal rank fusion (RRF) combination method. Controls decay\n    of rank influence.\n  * **linear_alpha** (*float*) – The weight of the text query for the linear combination method (LINEAR).\n  * **yield_combined_score_as** (*str* *|* *None*) – The name of the field to yield the combined score as.\n  * **dtype** (*str*) – The data type of the vector. Defaults to \"float32\".\n  * **num_results** (*int* *|* *None*) – The number of results to return.\n  * **return_fields** (*list* *[* *str* *]*  *|* *None*) – The fields to return. Defaults to None.\n  * **stopwords** (*Optional* *[* *Union* *[* *str* *,* *Set* *[* *str* *]* *]* *]* *,* *optional*) – \n\n    The stopwords to remove from the\n    provided text prior to search-use. If a string such as \"english\" \"german\" is\n    provided then a default set of stopwords for that language will be used. if a list,\n    set, or tuple of strings is provided then those will be used as stopwords.\n    Defaults to \"english\". if set to \"None\" then no stopwords will be removed.\n\n    Note: This parameter controls query-time stopword filtering (client-side).\n    For index-level stopwords configuration (server-side), see IndexInfo.stopwords.\n    Using query-time stopwords with index-level STOPWORDS 0 is counterproductive.\n  * **text_weights** (*Optional* *[* *Dict* *[* *str* *,* *float* *]* *]*) – The importance weighting of individual words\n    within the query text. Defaults to None, as no modifications will be made to the\n    text_scorer score.\n* **Raises:**\n  * **ImportError** – If redis-py>=7.1.0 is not installed.\n  * **TypeError** – If the stopwords are not a set, list, or tuple of strings.\n  * **ValueError** – If the text string is empty, or if the text string becomes empty after\n        stopwords are removed.\n  * **ValueError** – If vector_search_method is defined and isn’t one of {KNN, RANGE}.\n  * **ValueError** – If vector_search_method is \"KNN\" and knn_k is not provided.\n  * **ValueError** – If vector_search_method is \"RANGE\" and range_radius is not provided.\n\n> [!NOTE]\n> The `stopwords` parameter in [HybridQuery](#hybridquery) (and `AggregateHybridQuery`) controls query-time stopword filtering (client-side).\n> For index-level stopwords configuration (server-side), see `redisvl.schema.IndexInfo.stopwords`.\n> Using query-time stopwords with index-level `STOPWORDS 0` is counterproductive.\n\n> [!NOTE]\n> [HybridQuery](#hybridquery) and `AggregateHybridQuery` apply linear combination inconsistently. [HybridQuery](#hybridquery) uses `linear_alpha` to weight the text score, while `AggregateHybridQuery` uses `alpha` to weight the vector score. When switching between the two classes, take care to revise your `alpha` setting."},{"id":"textquery","title":"TextQuery","role":"content","text":""},{"id":"class-textquerytext-text_field_name-text_scorerbm25std-filter_expressionnone-return_fieldsnone-num_results10-return_scoretrue-dialect2-sort_bynone-in_orderfalse-paramsnone-stopwordsenglish-text_weightsnone","title":"`class TextQuery(text, text_field_name, text_scorer='BM25STD', filter_expression=None, return_fields=None, num_results=10, return_score=True, dialect=2, sort_by=None, in_order=False, params=None, stopwords='english', text_weights=None)`","role":"content","text":"Bases: `BaseQuery`\n\nTextQuery is a query for running a full text search, along with an optional filter expression.\n\n[code example]\n\nA query for running a full text search, along with an optional filter expression.\n\n* **Parameters:**\n  * **text** (*str*) – The text string to perform the text search with.\n  * **text_field_name** (*Union* *[* *str* *,* *Dict* *[* *str* *,* *float* *]* *]*) – The name of the document field to perform\n    text search on, or a dictionary mapping field names to their weights.\n  * **text_scorer** (*str* *,* *optional*) – The text scoring algorithm to use.\n    Defaults to BM25STD. Options are {TFIDF, BM25STD, BM25, TFIDF.DOCNORM, DISMAX, DOCSCORE}.\n    See [https://redis.io/docs/latest/develop/interact/search-and-query/advanced-concepts/scoring/](https://redis.io/docs/latest/develop/interact/search-and-query/advanced-concepts/scoring/)\n  * **filter_expression** (*Union* *[* *str* *,* [*FilterExpression*](https://redis.io/docs/latest/filter/#filterexpression) *]* *,* *optional*) – A filter to apply\n    along with the text search. Defaults to None.\n  * **return_fields** (*List* *[* *str* *]*) – The declared fields to return with search\n    results.\n  * **num_results** (*int* *,* *optional*) – The top k results to return from the\n    search. Defaults to 10.\n  * **return_score** (*bool* *,* *optional*) – Whether to return the text score.\n    Defaults to True.\n  * **dialect** (*int* *,* *optional*) – The Redis Search query dialect.\n    Defaults to 2.\n  * **sort_by** (*Optional* *[* *SortSpec* *]*) – The field(s) to order the results by. Can be:\n    - str: single field name\n    - Tuple[str, str]: (field_name, \"ASC\"|\"DESC\")\n    - List: list of fields or tuples\n    Note: Only the first field is used for Redis sorting.\n    Defaults to None. Results will be ordered by text score.\n  * **in_order** (*bool*) – Requires the terms in the field to have\n    the same order as the terms in the query filter, regardless of\n    the offsets between them. Defaults to False.\n  * **params** (*Optional* *[* *Dict* *[* *str* *,* *Any* *]* *]* *,* *optional*) – The parameters for the query.\n    Defaults to None.\n  * **stopwords** (*Optional* *[* *Union* *[* *str* *,* *Set* *[* *str* *]* *]*) – \n\n    The set of stop words to remove\n    from the query text (client-side filtering). If a language like ‘english’ or ‘spanish’ is provided\n    a default set of stopwords for that language will be used. Users may specify\n    their own stop words by providing a List or Set of words. if set to None,\n    then no words will be removed. Defaults to ‘english’.\n\n    Note: This parameter controls query-time stopword filtering (client-side).\n    For index-level stopwords configuration (server-side), see IndexInfo.stopwords.\n    Using query-time stopwords with index-level STOPWORDS 0 is counterproductive.\n  * **text_weights** (*Optional* *[* *Dict* *[* *str* *,* *float* *]* *]*) – The importance weighting of individual words\n    within the query text. Defaults to None, as no modifications will be made to the\n    text_scorer score.\n* **Raises:**\n  * **ValueError** – if stopwords language string cannot be loaded.\n  * **TypeError** – If stopwords is not a valid iterable set of strings.\n\n#### `dialect(dialect)`\n\nAdd a dialect field to the query.\n\n- **dialect** - dialect version to execute the query under\n\n* **Parameters:**\n  **dialect** (*int*)\n* **Return type:**\n  *Query*\n\n#### `expander(expander)`\n\nAdd an expander field to the query.\n\n- **expander** - the name of the expander\n\n* **Parameters:**\n  **expander** (*str*)\n* **Return type:**\n  *Query*\n\n#### `in_order()`\n\nMatch only documents where the query terms appear in\nthe same order in the document.\ni.e., for the query \"hello world\", we do not match \"world hello\"\n\n* **Return type:**\n  *Query*\n\n#### `language(language)`\n\nAnalyze the query as being in the specified language.\n\n* **Parameters:**\n  **language** (*str*) – The language (e.g. chinese or english)\n* **Return type:**\n  *Query*\n\n#### `limit_fields(*fields)`\n\nLimit the search to specific TEXT fields only.\n\n- **fields**: Each element should be a string, case sensitive field name\n\nfrom the defined schema.\n\n* **Parameters:**\n  **fields** (*str*)\n* **Return type:**\n  *Query*\n\n#### `limit_ids(*ids)`\n\nLimit the results to a specific set of pre-known document\nids of any length.\n\n* **Return type:**\n  *Query*\n\n#### `no_content()`\n\nSet the query to only return ids and not the document content.\n\n* **Return type:**\n  *Query*\n\n#### `no_stopwords()`\n\nPrevent the query from being filtered for stopwords.\nOnly useful in very big queries that you are certain contain\nno stopwords.\n\n* **Return type:**\n  *Query*\n\n#### `paging(offset, num)`\n\nSet the paging for the query (defaults to 0..10).\n\n- **offset**: Paging offset for the results. Defaults to 0\n- **num**: How many results do we want\n\n* **Parameters:**\n  * **offset** (*int*)\n  * **num** (*int*)\n* **Return type:**\n  *Query*\n\n#### `query_string()`\n\nReturn the query string of this query only.\n\n* **Return type:**\n  str\n\n#### `return_fields(*fields, skip_decode=None)`\n\nSet the fields to return with search results.\n\n* **Parameters:**\n  * **\\*fields** – Variable number of field names to return.\n  * **skip_decode** (*str* *|* *list* *[* *str* *]*  *|* *None*) – Optional field name or list of field names that should not be\n    decoded. Useful for binary data like embeddings.\n* **Returns:**\n  Returns the query object for method chaining.\n* **Return type:**\n  self\n* **Raises:**\n  **TypeError** – If skip_decode is not a string, list, or None.\n\n#### `scorer(scorer)`\n\nUse a different scoring function to evaluate document relevance.\nDefault is TFIDF.\n\nSince Redis 8.0 default was changed to BM25STD.\n\n* **Parameters:**\n  **scorer** (*str*) – The scoring function to use\n  (e.g. TFIDF.DOCNORM or BM25)\n* **Return type:**\n  *Query*\n\n#### `set_field_weights(field_weights)`\n\nSet or update the field weights for the query.\n\n* **Parameters:**\n  **field_weights** (*str* *|* *dict* *[* *str* *,* *float* *]*) – Either a single field name or dictionary of field:weight mappings\n\n#### `set_filter(filter_expression=None)`\n\nSet the filter expression for the query.\n\n* **Parameters:**\n  **filter_expression** (*Optional* *[* *Union* *[* *str* *,* [*FilterExpression*](https://redis.io/docs/latest/filter/#filterexpression) *]* *]* *,* *optional*) – The filter\n  expression or query string to use on the query.\n* **Raises:**\n  **TypeError** – If filter_expression is not a valid FilterExpression or string.\n\n#### `set_text_weights(weights)`\n\nSet or update the text weights for the query.\n\n* **Parameters:**\n  * **text_weights** – Dictionary of word:weight mappings\n  * **weights** (*dict* *[* *str* *,* *float* *]*)\n\n#### `slop(slop)`\n\nAllow a maximum of N intervening non-matched terms between\nphrase terms (0 means exact phrase).\n\n* **Parameters:**\n  **slop** (*int*)\n* **Return type:**\n  *Query*\n\n#### `sort_by(sort_spec=None, asc=True)`\n\nSet the sort order for query results.\n\nThis method supports sorting by single or multiple fields. Note that Redis Search\nnatively supports only a single SORTBY field. When multiple fields are specified,\nonly the FIRST field is used for the Redis SORTBY clause.\n\n* **Parameters:**\n  * **sort_spec** (*str* *|* *tuple* *[* *str* *,* *str* *]*  *|* *list* *[* *str* *|* *tuple* *[* *str* *,* *str* *]* *]*  *|* *None*) – Sort specification in various formats:\n    - str: single field name\n    - Tuple[str, str]: (field_name, \"ASC\"|\"DESC\")\n    - List: list of field names or tuples\n  * **asc** (*bool*) – Default sort direction when not specified (only used when sort_spec is a string).\n    Defaults to True (ascending).\n* **Returns:**\n  Returns the query object for method chaining.\n* **Return type:**\n  self\n* **Raises:**\n  * **TypeError** – If sort_spec is not a valid type.\n  * **ValueError** – If direction is not \"ASC\" or \"DESC\"."},{"id":"examples-2","title":"`Examples`","role":"content","text":"[code example]\n\n> [!NOTE]\n> When multiple fields are specified, only the first field is used for sorting\n> in Redis. Future versions may support multi-field sorting through post-query\n> sorting in Python.\n\n#### `timeout(timeout)`\n\noverrides the timeout parameter of the module\n\n* **Parameters:**\n  **timeout** (*float*)\n* **Return type:**\n  *Query*\n\n#### `verbatim()`\n\nSet the query to be verbatim, i.e., use no query expansion\nor stemming.\n\n* **Return type:**\n  *Query*\n\n#### `with_payloads()`\n\nAsk the engine to return document payloads.\n\n* **Return type:**\n  *Query*\n\n#### `with_scores()`\n\nAsk the engine to return document search scores.\n\n* **Return type:**\n  *Query*\n\n#### `property field_weights: dict[str, float]`\n\nGet the field weights for the query.\n\n* **Returns:**\n  Dictionary mapping field names to their weights\n\n#### `property filter: str | `[`FilterExpression`](https://redis.io/docs/latest/filter/#filterexpression)` `\n\nThe filter expression for the query.\n\n#### `property params: dict[str, Any]`\n\nReturn the query parameters.\n\n#### `property query: BaseQuery`\n\nReturn self as the query object.\n\n#### `property text_field_name: str | dict[str, float]`\n\nGet the text field name(s) - for backward compatibility.\n\n* **Returns:**\n  Either a single field name string (if only one field with weight 1.0)\n  or a dictionary of field:weight mappings.\n\n#### `property text_weights: dict[str, float]`\n\nGet the text weights.\n\n* **Returns:**\n  weight mappings.\n* **Return type:**\n  Dictionary of word\n\n> [!NOTE]\n> The `stopwords` parameter in [TextQuery](#textquery) controls query-time stopword filtering (client-side).\n> For index-level stopwords configuration (server-side), see `redisvl.schema.IndexInfo.stopwords`.\n> Using query-time stopwords with index-level `STOPWORDS 0` is counterproductive."},{"id":"filterquery","title":"FilterQuery","role":"content","text":""},{"id":"class-filterqueryfilter_expressionnone-return_fieldsnone-num_results10-dialect2-sort_bynone-in_orderfalse-paramsnone","title":"`class FilterQuery(filter_expression=None, return_fields=None, num_results=10, dialect=2, sort_by=None, in_order=False, params=None)`","role":"content","text":"Bases: `BaseQuery`\n\nA query for running a filtered search with a filter expression.\n\n* **Parameters:**\n  * **filter_expression** (*Optional* *[* *Union* *[* *str* *,* [*FilterExpression*](https://redis.io/docs/latest/filter/#filterexpression) *]* *]*) – The optional filter\n    expression to query with. Defaults to ‘\\*’.\n  * **return_fields** (*Optional* *[* *List* *[* *str* *]* *]* *,* *optional*) – The fields to return.\n  * **num_results** (*Optional* *[* *int* *]* *,* *optional*) – The number of results to return. Defaults to 10.\n  * **dialect** (*int* *,* *optional*) – The query dialect. Defaults to 2.\n  * **sort_by** (*Optional* *[* *SortSpec* *]* *,* *optional*) – The field(s) to order the results by. Can be:\n    - str: single field name (e.g., \"price\")\n    - Tuple[str, str]: (field_name, \"ASC\"|\"DESC\") (e.g., (\"price\", \"DESC\"))\n    - List: list of fields or tuples (e.g., [\"price\", (\"rating\", \"DESC\")])\n    Note: Redis Search only supports single-field sorting, so only the first field is used.\n    Defaults to None.\n  * **in_order** (*bool* *,* *optional*) – Requires the terms in the field to have the same order as the\n    terms in the query filter. Defaults to False.\n  * **params** (*Optional* *[* *Dict* *[* *str* *,* *Any* *]* *]* *,* *optional*) – The parameters for the query. Defaults to None.\n* **Raises:**\n  **TypeError** – If filter_expression is not of type redisvl.query.FilterExpression\n\n#### `dialect(dialect)`\n\nAdd a dialect field to the query.\n\n- **dialect** - dialect version to execute the query under\n\n* **Parameters:**\n  **dialect** (*int*)\n* **Return type:**\n  *Query*\n\n#### `expander(expander)`\n\nAdd an expander field to the query.\n\n- **expander** - the name of the expander\n\n* **Parameters:**\n  **expander** (*str*)\n* **Return type:**\n  *Query*\n\n#### `in_order()`\n\nMatch only documents where the query terms appear in\nthe same order in the document.\ni.e., for the query \"hello world\", we do not match \"world hello\"\n\n* **Return type:**\n  *Query*\n\n#### `language(language)`\n\nAnalyze the query as being in the specified language.\n\n* **Parameters:**\n  **language** (*str*) – The language (e.g. chinese or english)\n* **Return type:**\n  *Query*\n\n#### `limit_fields(*fields)`\n\nLimit the search to specific TEXT fields only.\n\n- **fields**: Each element should be a string, case sensitive field name\n\nfrom the defined schema.\n\n* **Parameters:**\n  **fields** (*str*)\n* **Return type:**\n  *Query*\n\n#### `limit_ids(*ids)`\n\nLimit the results to a specific set of pre-known document\nids of any length.\n\n* **Return type:**\n  *Query*\n\n#### `no_content()`\n\nSet the query to only return ids and not the document content.\n\n* **Return type:**\n  *Query*\n\n#### `no_stopwords()`\n\nPrevent the query from being filtered for stopwords.\nOnly useful in very big queries that you are certain contain\nno stopwords.\n\n* **Return type:**\n  *Query*\n\n#### `paging(offset, num)`\n\nSet the paging for the query (defaults to 0..10).\n\n- **offset**: Paging offset for the results. Defaults to 0\n- **num**: How many results do we want\n\n* **Parameters:**\n  * **offset** (*int*)\n  * **num** (*int*)\n* **Return type:**\n  *Query*\n\n#### `query_string()`\n\nReturn the query string of this query only.\n\n* **Return type:**\n  str\n\n#### `return_fields(*fields, skip_decode=None)`\n\nSet the fields to return with search results.\n\n* **Parameters:**\n  * **\\*fields** – Variable number of field names to return.\n  * **skip_decode** (*str* *|* *list* *[* *str* *]*  *|* *None*) – Optional field name or list of field names that should not be\n    decoded. Useful for binary data like embeddings.\n* **Returns:**\n  Returns the query object for method chaining.\n* **Return type:**\n  self\n* **Raises:**\n  **TypeError** – If skip_decode is not a string, list, or None.\n\n#### `scorer(scorer)`\n\nUse a different scoring function to evaluate document relevance.\nDefault is TFIDF.\n\nSince Redis 8.0 default was changed to BM25STD.\n\n* **Parameters:**\n  **scorer** (*str*) – The scoring function to use\n  (e.g. TFIDF.DOCNORM or BM25)\n* **Return type:**\n  *Query*\n\n#### `set_filter(filter_expression=None)`\n\nSet the filter expression for the query.\n\n* **Parameters:**\n  **filter_expression** (*Optional* *[* *Union* *[* *str* *,* [*FilterExpression*](https://redis.io/docs/latest/filter/#filterexpression) *]* *]* *,* *optional*) – The filter\n  expression or query string to use on the query.\n* **Raises:**\n  **TypeError** – If filter_expression is not a valid FilterExpression or string.\n\n#### `slop(slop)`\n\nAllow a maximum of N intervening non-matched terms between\nphrase terms (0 means exact phrase).\n\n* **Parameters:**\n  **slop** (*int*)\n* **Return type:**\n  *Query*\n\n#### `sort_by(sort_spec=None, asc=True)`\n\nSet the sort order for query results.\n\nThis method supports sorting by single or multiple fields. Note that Redis Search\nnatively supports only a single SORTBY field. When multiple fields are specified,\nonly the FIRST field is used for the Redis SORTBY clause.\n\n* **Parameters:**\n  * **sort_spec** (*str* *|* *tuple* *[* *str* *,* *str* *]*  *|* *list* *[* *str* *|* *tuple* *[* *str* *,* *str* *]* *]*  *|* *None*) – Sort specification in various formats:\n    - str: single field name\n    - Tuple[str, str]: (field_name, \"ASC\"|\"DESC\")\n    - List: list of field names or tuples\n  * **asc** (*bool*) – Default sort direction when not specified (only used when sort_spec is a string).\n    Defaults to True (ascending).\n* **Returns:**\n  Returns the query object for method chaining.\n* **Return type:**\n  self\n* **Raises:**\n  * **TypeError** – If sort_spec is not a valid type.\n  * **ValueError** – If direction is not \"ASC\" or \"DESC\"."},{"id":"examples-3","title":"`Examples`","role":"content","text":"[code example]\n\n> [!NOTE]\n> When multiple fields are specified, only the first field is used for sorting\n> in Redis. Future versions may support multi-field sorting through post-query\n> sorting in Python.\n\n#### `timeout(timeout)`\n\noverrides the timeout parameter of the module\n\n* **Parameters:**\n  **timeout** (*float*)\n* **Return type:**\n  *Query*\n\n#### `verbatim()`\n\nSet the query to be verbatim, i.e., use no query expansion\nor stemming.\n\n* **Return type:**\n  *Query*\n\n#### `with_payloads()`\n\nAsk the engine to return document payloads.\n\n* **Return type:**\n  *Query*\n\n#### `with_scores()`\n\nAsk the engine to return document search scores.\n\n* **Return type:**\n  *Query*\n\n#### `property filter: str | `[`FilterExpression`](https://redis.io/docs/latest/filter/#filterexpression)` `\n\nThe filter expression for the query.\n\n#### `property params: dict[str, Any]`\n\nReturn the query parameters.\n\n#### `property query: BaseQuery`\n\nReturn self as the query object."},{"id":"countquery","title":"CountQuery","role":"content","text":""},{"id":"class-countqueryfilter_expressionnone-dialect2-paramsnone","title":"`class CountQuery(filter_expression=None, dialect=2, params=None)`","role":"content","text":"Bases: `BaseQuery`\n\nA query for a simple count operation provided some filter expression.\n\n* **Parameters:**\n  * **filter_expression** (*Optional* *[* *Union* *[* *str* *,* [*FilterExpression*](https://redis.io/docs/latest/filter/#filterexpression) *]* *]*) – The filter expression to\n    query with. Defaults to None.\n  * **params** (*Optional* *[* *Dict* *[* *str* *,* *Any* *]* *]* *,* *optional*) – The parameters for the query. Defaults to None.\n  * **dialect** (*int*)\n* **Raises:**\n  **TypeError** – If filter_expression is not of type redisvl.query.FilterExpression\n\n[code example]\n\n#### `dialect(dialect)`\n\nAdd a dialect field to the query.\n\n- **dialect** - dialect version to execute the query under\n\n* **Parameters:**\n  **dialect** (*int*)\n* **Return type:**\n  *Query*\n\n#### `expander(expander)`\n\nAdd an expander field to the query.\n\n- **expander** - the name of the expander\n\n* **Parameters:**\n  **expander** (*str*)\n* **Return type:**\n  *Query*\n\n#### `in_order()`\n\nMatch only documents where the query terms appear in\nthe same order in the document.\ni.e., for the query \"hello world\", we do not match \"world hello\"\n\n* **Return type:**\n  *Query*\n\n#### `language(language)`\n\nAnalyze the query as being in the specified language.\n\n* **Parameters:**\n  **language** (*str*) – The language (e.g. chinese or english)\n* **Return type:**\n  *Query*\n\n#### `limit_fields(*fields)`\n\nLimit the search to specific TEXT fields only.\n\n- **fields**: Each element should be a string, case sensitive field name\n\nfrom the defined schema.\n\n* **Parameters:**\n  **fields** (*str*)\n* **Return type:**\n  *Query*\n\n#### `limit_ids(*ids)`\n\nLimit the results to a specific set of pre-known document\nids of any length.\n\n* **Return type:**\n  *Query*\n\n#### `no_content()`\n\nSet the query to only return ids and not the document content.\n\n* **Return type:**\n  *Query*\n\n#### `no_stopwords()`\n\nPrevent the query from being filtered for stopwords.\nOnly useful in very big queries that you are certain contain\nno stopwords.\n\n* **Return type:**\n  *Query*\n\n#### `paging(offset, num)`\n\nSet the paging for the query (defaults to 0..10).\n\n- **offset**: Paging offset for the results. Defaults to 0\n- **num**: How many results do we want\n\n* **Parameters:**\n  * **offset** (*int*)\n  * **num** (*int*)\n* **Return type:**\n  *Query*\n\n#### `query_string()`\n\nReturn the query string of this query only.\n\n* **Return type:**\n  str\n\n#### `return_fields(*fields, skip_decode=None)`\n\nSet the fields to return with search results.\n\n* **Parameters:**\n  * **\\*fields** – Variable number of field names to return.\n  * **skip_decode** (*str* *|* *list* *[* *str* *]*  *|* *None*) – Optional field name or list of field names that should not be\n    decoded. Useful for binary data like embeddings.\n* **Returns:**\n  Returns the query object for method chaining.\n* **Return type:**\n  self\n* **Raises:**\n  **TypeError** – If skip_decode is not a string, list, or None.\n\n#### `scorer(scorer)`\n\nUse a different scoring function to evaluate document relevance.\nDefault is TFIDF.\n\nSince Redis 8.0 default was changed to BM25STD.\n\n* **Parameters:**\n  **scorer** (*str*) – The scoring function to use\n  (e.g. TFIDF.DOCNORM or BM25)\n* **Return type:**\n  *Query*\n\n#### `set_filter(filter_expression=None)`\n\nSet the filter expression for the query.\n\n* **Parameters:**\n  **filter_expression** (*Optional* *[* *Union* *[* *str* *,* [*FilterExpression*](https://redis.io/docs/latest/filter/#filterexpression) *]* *]* *,* *optional*) – The filter\n  expression or query string to use on the query.\n* **Raises:**\n  **TypeError** – If filter_expression is not a valid FilterExpression or string.\n\n#### `slop(slop)`\n\nAllow a maximum of N intervening non-matched terms between\nphrase terms (0 means exact phrase).\n\n* **Parameters:**\n  **slop** (*int*)\n* **Return type:**\n  *Query*\n\n#### `sort_by(sort_spec=None, asc=True)`\n\nSet the sort order for query results.\n\nThis method supports sorting by single or multiple fields. Note that Redis Search\nnatively supports only a single SORTBY field. When multiple fields are specified,\nonly the FIRST field is used for the Redis SORTBY clause.\n\n* **Parameters:**\n  * **sort_spec** (*str* *|* *tuple* *[* *str* *,* *str* *]*  *|* *list* *[* *str* *|* *tuple* *[* *str* *,* *str* *]* *]*  *|* *None*) – Sort specification in various formats:\n    - str: single field name\n    - Tuple[str, str]: (field_name, \"ASC\"|\"DESC\")\n    - List: list of field names or tuples\n  * **asc** (*bool*) – Default sort direction when not specified (only used when sort_spec is a string).\n    Defaults to True (ascending).\n* **Returns:**\n  Returns the query object for method chaining.\n* **Return type:**\n  self\n* **Raises:**\n  * **TypeError** – If sort_spec is not a valid type.\n  * **ValueError** – If direction is not \"ASC\" or \"DESC\"."},{"id":"examples-4","title":"`Examples`","role":"content","text":"[code example]\n\n> [!NOTE]\n> When multiple fields are specified, only the first field is used for sorting\n> in Redis. Future versions may support multi-field sorting through post-query\n> sorting in Python.\n\n#### `timeout(timeout)`\n\noverrides the timeout parameter of the module\n\n* **Parameters:**\n  **timeout** (*float*)\n* **Return type:**\n  *Query*\n\n#### `verbatim()`\n\nSet the query to be verbatim, i.e., use no query expansion\nor stemming.\n\n* **Return type:**\n  *Query*\n\n#### `with_payloads()`\n\nAsk the engine to return document payloads.\n\n* **Return type:**\n  *Query*\n\n#### `with_scores()`\n\nAsk the engine to return document search scores.\n\n* **Return type:**\n  *Query*\n\n#### `property filter: str | `[`FilterExpression`](https://redis.io/docs/latest/filter/#filterexpression)` `\n\nThe filter expression for the query.\n\n#### `property params: dict[str, Any]`\n\nReturn the query parameters.\n\n#### `property query: BaseQuery`\n\nReturn self as the query object."},{"id":"multivectorquery","title":"MultiVectorQuery","role":"content","text":""},{"id":"class-multivectorqueryvectors-return_fieldsnone-filter_expressionnone-num_results10-dialect2","title":"`class MultiVectorQuery(vectors, return_fields=None, filter_expression=None, num_results=10, dialect=2)`","role":"content","text":"Bases: `AggregationQuery`\n\nMultiVectorQuery allows for search over multiple vector fields in a document simultaneously.\nThe final score will be a weighted combination of the individual vector similarity scores\nfollowing the formula:\n\nscore = (w_1 \\* score_1 + w_2 \\* score_2 + w_3 \\* score_3 + … )\n\nVectors may be of different size and datatype, but must be indexed using the ‘cosine’ distance_metric.\n\n[code example]\n\nInstantiates a MultiVectorQuery object.\n\n* **Parameters:**\n  * **vectors** (*Union* *[*[*Vector*](https://redis.io/docs/latest/vector/#vector) *,* *List* *[*[*Vector*](https://redis.io/docs/latest/vector/#vector) *]* *]*) – The Vectors to perform vector similarity search.\n  * **return_fields** (*Optional* *[* *List* *[* *str* *]* *]* *,* *optional*) – The fields to return. Defaults to None.\n  * **filter_expression** (*Optional* *[* *Union* *[* *str* *,* [*FilterExpression*](https://redis.io/docs/latest/filter/#filterexpression) *]* *]*) – The filter expression to use.\n    Defaults to None.\n  * **num_results** (*int* *,* *optional*) – The number of results to return. Defaults to 10.\n  * **dialect** (*int* *,* *optional*) – The Redis dialect version. Defaults to 2.\n\n#### `add_scores()`\n\nIf set, includes the score as an ordinary field of the row.\n\n* **Return type:**\n  *AggregateRequest*\n\n#### `apply(**kwexpr)`\n\nSpecify one or more projection expressions to add to each result"},{"id":"parameters-6","title":"`Parameters`","role":"content","text":"- **kwexpr**: One or more key-value pairs for a projection. The key is\n  : the alias for the projection, and the value is the projection\n    expression itself, for example apply(square_root=\"sqrt(@foo)\")\n\n* **Return type:**\n  *AggregateRequest*\n\n#### `dialect(dialect)`\n\nAdd a dialect field to the aggregate command.\n\n- **dialect** - dialect version to execute the query under\n\n* **Parameters:**\n  **dialect** (*int*)\n* **Return type:**\n  *AggregateRequest*\n\n#### `filter(expressions)`\n\nSpecify filter for post-query results using predicates relating to\nvalues in the result set."},{"id":"parameters-7","title":"`Parameters`","role":"content","text":"- **fields**: Fields to group by. This can either be a single string,\n  : or a list of strings.\n\n* **Parameters:**\n  **expressions** (*str* *|* *List* *[* *str* *]*)\n* **Return type:**\n  *AggregateRequest*\n\n#### `group_by(fields, *reducers)`\n\nSpecify by which fields to group the aggregation."},{"id":"parameters-8","title":"`Parameters`","role":"content","text":"- **fields**: Fields to group by. This can either be a single string,\n  : or a list of strings. both cases, the field should be specified as\n    @field.\n- **reducers**: One or more reducers. Reducers may be found in the\n  : aggregation module.\n\n* **Parameters:**\n  * **fields** (*str* *|* *List* *[* *str* *]*)\n  * **reducers** (*Reducer*)\n* **Return type:**\n  *AggregateRequest*\n\n#### `limit(offset, num)`\n\nSets the limit for the most recent group or query.\n\nIf no group has been defined yet (via group_by()) then this sets\nthe limit for the initial pool of results from the query. Otherwise,\nthis limits the number of items operated on from the previous group.\n\nSetting a limit on the initial search results may be useful when\nattempting to execute an aggregation on a sample of a large data set."},{"id":"parameters-9","title":"`Parameters`","role":"content","text":"- **offset**: Result offset from which to begin paging\n- **num**: Number of results to return\n\nExample of sorting the initial results:\n\n``\nAggregateRequest(\"@sale_amount:[10000, inf]\")            .limit(0, 10)            .group_by(\"@state\", r.count())\n``\n\nWill only group by the states found in the first 10 results of the\nquery @sale_amount:[10000, inf]. On the other hand,\n\n``\nAggregateRequest(\"@sale_amount:[10000, inf]\")            .limit(0, 1000)            .group_by(\"@state\", r.count()            .limit(0, 10)\n``\n\nWill group all the results matching the query, but only return the\nfirst 10 groups.\n\nIf you only wish to return a *top-N* style query, consider using\nsort_by() instead.\n\n* **Parameters:**\n  * **offset** (*int*)\n  * **num** (*int*)\n* **Return type:**\n  *AggregateRequest*\n\n#### `load(*fields)`\n\nIndicate the fields to be returned in the response. These fields are\nreturned in addition to any others implicitly specified."},{"id":"parameters-10","title":"`Parameters`","role":"content","text":"- **fields**: If fields not specified, all the fields will be loaded.\n\nOtherwise, fields should be given in the format of @field.\n\n* **Parameters:**\n  **fields** (*str*)\n* **Return type:**\n  *AggregateRequest*\n\n#### `scorer(scorer)`\n\nUse a different scoring function to evaluate document relevance.\nDefault is TFIDF.\n\n* **Parameters:**\n  **scorer** (*str*) – The scoring function to use\n  (e.g. TFIDF.DOCNORM or BM25)\n* **Return type:**\n  *AggregateRequest*\n\n#### `sort_by(*fields, **kwargs)`\n\nIndicate how the results should be sorted. This can also be used for\n*top-N* style queries"},{"id":"parameters-11","title":"`Parameters`","role":"content","text":"- **fields**: The fields by which to sort. This can be either a single\n  : field or a list of fields. If you wish to specify order, you can\n    use the Asc or Desc wrapper classes.\n- **max**: Maximum number of results to return. This can be\n  : used instead of LIMIT and is also faster.\n\nExample of sorting by foo ascending and bar descending:\n\n``\nsort_by(Asc(\"@foo\"), Desc(\"@bar\"))\n``\n\nReturn the top 10 customers:\n\n``\nAggregateRequest()            .group_by(\"@customer\", r.sum(\"@paid\").alias(FIELDNAME))            .sort_by(Desc(\"@paid\"), max=10)\n``\n\n* **Parameters:**\n  **fields** (*str*)\n* **Return type:**\n  *AggregateRequest*\n\n#### `with_schema()`\n\nIf set, the schema property will contain a list of [field, type]\nentries in the result object.\n\n* **Return type:**\n  *AggregateRequest*\n\n#### `property params: dict[str, Any]`\n\nReturn the parameters for the aggregation.\n\n* **Returns:**\n  The parameters for the aggregation.\n* **Return type:**\n  Dict[str, Any]"},{"id":"sqlquery","title":"SQLQuery","role":"content","text":""},{"id":"class-sqlquerysql-paramsnone--sql_redis_optionsnone","title":"`class SQLQuery(sql, params=None, *, sql_redis_options=None)`","role":"content","text":"Bases: `object`\n\nA query class that translates SQL-like syntax into Redis queries.\n\nThis class allows users to write SQL SELECT statements that are\nautomatically translated into Redis FT.SEARCH or FT.AGGREGATE commands.\n\nFor TEXT fields with `sql-redis >= 0.4.0`:\n\n- `=` performs exact phrase or exact-term matching\n- `LIKE` performs wildcard/pattern matching using SQL `%` wildcards\n- `fuzzy(field, 'term')` performs typo-tolerant matching\n- `fulltext(field, 'query')` performs tokenized text search\n\nVector and hybrid search:\n\n- `vector_distance(field, :vec)` / `cosine_distance(field, :vec)` perform\n  KNN vector similarity search (pass the query vector via `params`)\n- `hybrid_vector_search(cosine_distance(field, :vec), fulltext(field, 'query'), rrf())` fuses a text and a vector query server-side via Redis `FT.HYBRID`\n  (requires `sql-redis >= 0.7.0`, Redis 8.4+, and redis-py >= 7.1.0). Use\n  `rrf()` or `linear()` to select the fusion method\n\n[code example]\n\n> [!NOTE]\n> Requires the optional sql-redis package. Install with:\n> `pip install redisvl[sql-redis]`\n\nInitialize a SQLQuery.\n\n* **Parameters:**\n  * **sql** (*str*) – The SQL SELECT statement to execute.\n  * **params** (*dict* *[* *str* *,* *Any* *]*  *|* *None*) – Optional dictionary of parameters for parameterized queries.\n    Useful for passing vector data for similarity searches.\n  * **sql_redis_options** (*dict* *[* *str* *,* *Any* *]*  *|* *None*) – Optional passthrough options forwarded to\n    `sql-redis` executor creation. Use this to tune how SQL\n    query translation loads and caches index schema metadata.\n    For example, `{\"schema_cache_strategy\": \"lazy\"}` loads\n    schemas on demand (the RedisVL default), while\n    `{\"schema_cache_strategy\": \"load_all\"}` eagerly loads\n    all schemas up front. These options exist to balance startup\n    cost vs repeated-query performance across many indexes.\n\n> [!NOTE]\n> `sql-redis >= 0.4.0` uses explicit TEXT search operators.\n> Use `=` for exact phrase matching, `LIKE` for wildcard\n> matching, `fuzzy()` for typo-tolerant matching, and\n> `fulltext()` for tokenized search.\n\n#### `redis_query_string(redis_client=None, redis_url='redis://localhost:6379')`\n\nTranslate the SQL query to a Redis command string.\n\nThis method uses the sql-redis translator to convert the SQL statement\ninto the equivalent Redis FT.SEARCH or FT.AGGREGATE command.\n\n* **Parameters:**\n  * **redis_client** (*Any* *|* *None*) – A Redis client connection used to load index schemas.\n    If not provided, a connection will be created using redis_url.\n  * **redis_url** (*str*) – The Redis URL to connect to if redis_client is not provided.\n    Defaults to \"redis://localhost:6379\".\n* **Returns:**\n  {electronics}\"’).\n* **Return type:**\n  The Redis command string (e.g., ‘FT.SEARCH products \"@category\n* **Raises:**\n  **ImportError** – If sql-redis package is not installed."},{"id":"example","title":"`Example`","role":"content","text":"[code example]\n\n> [!NOTE]\n> SQLQuery requires the optional `sql-redis` package. Install with:\n> `pip install redisvl[sql-redis]`\n\n> [!NOTE]\n> SQLQuery translates SQL SELECT statements into Redis FT.SEARCH or FT.AGGREGATE commands.\n> The SQL syntax supports WHERE clauses, field selection, ordering, and parameterized queries\n> for vector similarity searches.\n\n> [!NOTE]\n> SQLQuery accepts a `sql_redis_options` dictionary that is passed through to\n> `sql-redis` executor creation. The most common option is\n> `schema_cache_strategy`:\n>\n> - `\"lazy\"` (default) loads schemas on demand, which keeps one-off or\n>   narrow queries cheaper.\n> - `\"load_all\"` eagerly loads all schemas up front, which can help when\n>   running many SQL queries across many indexes.\n\n> [!NOTE]\n> SQLQuery supports hybrid search via `hybrid_vector_search(cosine_distance(...), fulltext(...), rrf())`, which translates to a native Redis `FT.HYBRID` command\n> fusing a text and a vector query server-side. This is the SQL front-end to\n> `HybridQuery` and requires `sql-redis >= 0.7.0`, Redis 8.4+, and\n> redis-py >= 7.1.0."}],"examples":[{"id":"examples-ex0","language":"pycon","code":">> query.sort_by(\"price\")  # Single field, ascending\n>> query.sort_by((\"price\", \"DESC\"))  # Single field, descending\n>> query.sort_by([\"price\", \"rating\"])  # Multiple fields (only first used)\n>> query.sort_by([(\"price\", \"DESC\"), (\"rating\", \"ASC\")])","section_id":"examples"},{"id":"examples-ex1","language":"python","code":"> from redisvl.query import VectorQuery\n>\n> query = VectorQuery(\n>     vector=[0.1, 0.2, 0.3],\n>     vector_field_name=\"embedding\",\n>     num_results=10,\n>     ef_runtime=150  # Higher for better recall\n> )\n>","section_id":"examples"},{"id":"examples-ex2","language":"python","code":"> query = VectorQuery(\n>     vector=[0.1, 0.2, 0.3],\n>     vector_field_name=\"embedding\",\n>     num_results=10,\n>     search_window_size=20,\n>     use_search_history='ON',\n>     search_buffer_capacity=30\n> )\n>","section_id":"examples"},{"id":"examples-1-ex0","language":"pycon","code":">> query.sort_by(\"price\")  # Single field, ascending\n>> query.sort_by((\"price\", \"DESC\"))  # Single field, descending\n>> query.sort_by([\"price\", \"rating\"])  # Multiple fields (only first used)\n>> query.sort_by([(\"price\", \"DESC\"), (\"rating\", \"ASC\")])","section_id":"examples-1"},{"id":"examples-1-ex1","language":"python","code":"> from redisvl.query import VectorRangeQuery\n>\n> query = VectorRangeQuery(\n>     vector=[0.1, 0.2, 0.3],\n>     vector_field_name=\"embedding\",\n>     distance_threshold=0.3,\n>     epsilon=0.05,              # Approximation factor\n>     search_window_size=20,     # SVS-VAMANA only\n>     use_search_history='AUTO'  # SVS-VAMANA only\n> )\n>","section_id":"examples-1"},{"id":"class-aggregatehybridquerytext-text_field_name-vector-vector_field_name-text_scorerbm25std-filter_expressionnone-alpha07-dtypefloat32-num_results10-return_fieldsnone-stopwordsenglish-dialect2-text_weightsnone-ex0","language":"python","code":"from redisvl.query import AggregateHybridQuery\nfrom redisvl.index import SearchIndex\n\nindex = SearchIndex.from_yaml(\"path/to/index.yaml\")\n\nquery = AggregateHybridQuery(\n    text=\"example text\",\n    text_field_name=\"text_field\",\n    vector=[0.1, 0.2, 0.3],\n    vector_field_name=\"vector_field\",\n    text_scorer=\"BM25STD\",\n    filter_expression=None,\n    alpha=0.7,\n    dtype=\"float32\",\n    num_results=10,\n    return_fields=[\"field1\", \"field2\"],\n    stopwords=\"english\",\n    dialect=2,\n)\n\nresults = index.query(query)","section_id":"class-aggregatehybridquerytext-text_field_name-vector-vector_field_name-text_scorerbm25std-filter_expressionnone-alpha07-dtypefloat32-num_results10-return_fieldsnone-stopwordsenglish-dialect2-text_weightsnone"},{"id":"parameters-5-ex0","language":"python","code":"> from redisvl.query import HybridQuery\n>\n> query = HybridQuery(\n>     text=\"query string\",\n>     text_field_name=\"description\",\n>     vector=[0.1, 0.2, 0.3],\n>     vector_field_name=\"embedding\",\n>     vector_search_method=\"KNN\",\n>     knn_ef_runtime=150,  # Runtime parameters work with HybridQuery\n>     return_fields=[\"description\"],\n>     num_results=10,\n> )\n>","section_id":"parameters-5"},{"id":"class-hybridquerytext-text_field_name-vector-vector_field_name-vector_param_namevector-text_scorerbm25std-yield_text_score_asnone-vector_search_methodnone-knn_ef_runtime10-range_radiusnone-range_epsilon001-yield_vsim_score_asnone-filter_expressionnone-combination_methodnone-rrf_window20-rrf_constant60-linear_alpha03-yield_combined_score_asnone-dtypefloat32-num_results10-return_fieldsnone-stopwordsenglish-text_weightsnone-ex0","language":"python","code":"from redisvl.query import HybridQuery\nfrom redisvl.index import SearchIndex\n\nindex = SearchIndex.from_yaml(\"path/to/index.yaml\")\n\nquery = HybridQuery(\n    text=\"example text\",\n    text_field_name=\"text_field\",\n    vector=[0.1, 0.2, 0.3],\n    vector_field_name=\"vector_field\",\n    text_scorer=\"BM25STD\",\n    yield_text_score_as=\"text_score\",\n    yield_vsim_score_as=\"vector_similarity\",\n    combination_method=\"LINEAR\",\n    linear_alpha=0.3,\n    yield_combined_score_as=\"hybrid_score\",\n    num_results=10,\n    return_fields=[\"field1\", \"field2\"],\n    stopwords=\"english\",\n)\n\nresults = index.query(query)","section_id":"class-hybridquerytext-text_field_name-vector-vector_field_name-vector_param_namevector-text_scorerbm25std-yield_text_score_asnone-vector_search_methodnone-knn_ef_runtime10-range_radiusnone-range_epsilon001-yield_vsim_score_asnone-filter_expressionnone-combination_methodnone-rrf_window20-rrf_constant60-linear_alpha03-yield_combined_score_asnone-dtypefloat32-num_results10-return_fieldsnone-stopwordsenglish-text_weightsnone"},{"id":"class-textquerytext-text_field_name-text_scorerbm25std-filter_expressionnone-return_fieldsnone-num_results10-return_scoretrue-dialect2-sort_bynone-in_orderfalse-paramsnone-stopwordsenglish-text_weightsnone-ex0","language":"python","code":"from redisvl.query import TextQuery\nfrom redisvl.index import SearchIndex\n\nindex = SearchIndex.from_yaml(\"index.yaml\")\n\nquery = TextQuery(\n    text=\"example text\",\n    text_field_name=\"text_field\",\n    text_scorer=\"BM25STD\",\n    filter_expression=None,\n    num_results=10,\n    return_fields=[\"field1\", \"field2\"],\n    stopwords=\"english\",\n    dialect=2,\n)\n\nresults = index.query(query)","section_id":"class-textquerytext-text_field_name-text_scorerbm25std-filter_expressionnone-return_fieldsnone-num_results10-return_scoretrue-dialect2-sort_bynone-in_orderfalse-paramsnone-stopwordsenglish-text_weightsnone"},{"id":"examples-2-ex0","language":"pycon","code":">> query.sort_by(\"price\")  # Single field, ascending\n>> query.sort_by((\"price\", \"DESC\"))  # Single field, descending\n>> query.sort_by([\"price\", \"rating\"])  # Multiple fields (only first used)\n>> query.sort_by([(\"price\", \"DESC\"), (\"rating\", \"ASC\")])","section_id":"examples-2"},{"id":"examples-3-ex0","language":"pycon","code":">> query.sort_by(\"price\")  # Single field, ascending\n>> query.sort_by((\"price\", \"DESC\"))  # Single field, descending\n>> query.sort_by([\"price\", \"rating\"])  # Multiple fields (only first used)\n>> query.sort_by([(\"price\", \"DESC\"), (\"rating\", \"ASC\")])","section_id":"examples-3"},{"id":"class-countqueryfilter_expressionnone-dialect2-paramsnone-ex0","language":"python","code":"from redisvl.query import CountQuery\nfrom redisvl.query.filter import Tag\n\nt = Tag(\"brand\") == \"Nike\"\nquery = CountQuery(filter_expression=t)\n\ncount = index.query(query)","section_id":"class-countqueryfilter_expressionnone-dialect2-paramsnone"},{"id":"examples-4-ex0","language":"pycon","code":">> query.sort_by(\"price\")  # Single field, ascending\n>> query.sort_by((\"price\", \"DESC\"))  # Single field, descending\n>> query.sort_by([\"price\", \"rating\"])  # Multiple fields (only first used)\n>> query.sort_by([(\"price\", \"DESC\"), (\"rating\", \"ASC\")])","section_id":"examples-4"},{"id":"class-multivectorqueryvectors-return_fieldsnone-filter_expressionnone-num_results10-dialect2-ex0","language":"python","code":"from redisvl.query import MultiVectorQuery, Vector\nfrom redisvl.index import SearchIndex\n\nindex = SearchIndex.from_yaml(\"path/to/index.yaml\")\n\nvector_1 = Vector(\n    vector=[0.1, 0.2, 0.3],\n    field_name=\"text_vector\",\n    dtype=\"float32\",\n    weight=0.7,\n)\nvector_2 = Vector(\n    vector=[0.5, 0.5],\n    field_name=\"image_vector\",\n    dtype=\"bfloat16\",\n    weight=0.2,\n)\nvector_3 = Vector(\n    vector=[0.1, 0.2, 0.3],\n    field_name=\"text_vector\",\n    dtype=\"float64\",\n    weight=0.5,\n)\n\nquery = MultiVectorQuery(\n    vectors=[vector_1, vector_2, vector_3],\n    filter_expression=None,\n    num_results=10,\n    return_fields=[\"field1\", \"field2\"],\n    dialect=2,\n)\n\nresults = index.query(query)","section_id":"class-multivectorqueryvectors-return_fieldsnone-filter_expressionnone-num_results10-dialect2"},{"id":"class-sqlquerysql-paramsnone--sql_redis_optionsnone-ex0","language":"python","code":"from redisvl.query import SQLQuery\nfrom redisvl.index import SearchIndex\n\nindex = SearchIndex.from_existing(\"products\", redis_url=\"redis://localhost:6379\")\n\nsql_query = SQLQuery('''\n    SELECT title, price, category\n    FROM products\n    WHERE category = 'electronics' AND price < 100\n''')\n\nresults = index.query(sql_query)","section_id":"class-sqlquerysql-paramsnone--sql_redis_optionsnone"},{"id":"example-ex0","language":"python","code":"from redisvl.query import SQLQuery\n\nsql_query = SQLQuery(\"SELECT * FROM products WHERE category = 'electronics'\")\n\n# Using redis_url\nredis_cmd = sql_query.redis_query_string(redis_url=\"redis://localhost:6379\")\n\n# Or using an existing client\nfrom redis import Redis\nclient = Redis()\nredis_cmd = sql_query.redis_query_string(redis_client=client)\n\nprint(redis_cmd)\n# Output: FT.SEARCH products \"@category:{electronics}\"","section_id":"example"}]}
{"schema_version":2,"id":"develop/ai/redisvl/api/reranker","title":"Rerankers","url":"https://redis.io/docs/latest/develop/ai/redisvl/api/reranker/","summary":"","aliases":["/integrate/redisvl/api/reranker"],"tags":[],"last_updated":"2026-04-30T14:25:09+02:00","page_type":"content","content_hash":"b8e3f083eaf7d2ae364566d2dd36ac6bbbb1dbddcc8541ecb31b85379050663f","sections":[{"id":"coherereranker","title":"CohereReranker","role":"overview","text":"<a id=\"coherereranker-api\"></a>"},{"id":"class-coherererankermodelrerank-english-v30-rank_bynone-limit5-return_scoretrue-api_confignone","title":"`class CohereReranker(model='rerank-english-v3.0', rank_by=None, limit=5, return_score=True, api_config=None)`","role":"content","text":"Bases: `BaseReranker`\n\nThe CohereReranker class uses Cohere’s API to rerank documents based on an\ninput query.\n\nThis reranker is designed to interact with Cohere’s /rerank API,\nrequiring an API key for authentication. The key can be provided\ndirectly in the api_config dictionary or through the COHERE_API_KEY\nenvironment variable. User must obtain an API key from Cohere’s website\n([https://dashboard.cohere.com/](https://dashboard.cohere.com/)). Additionally, the cohere python\nclient must be installed with pip install cohere.\n\n[code example]\n\nInitialize the CohereReranker with specified model, ranking criteria,\nand API configuration.\n\n* **Parameters:**\n  * **model** (*str*) – The identifier for the Cohere model used for reranking.\n    Defaults to ‘rerank-english-v3.0’.\n  * **rank_by** (*Optional* *[* *List* *[* *str* *]* *]*) – Optional list of keys specifying the\n    attributes in the documents that should be considered for\n    ranking. None means ranking will rely on the model’s default\n    behavior.\n  * **limit** (*int*) – The maximum number of results to return after\n    reranking. Must be a positive integer.\n  * **return_score** (*bool*) – Whether to return scores alongside the\n    reranked results.\n  * **api_config** (*Optional* *[* *Dict* *]* *,* *optional*) – Dictionary containing the API key.\n    Defaults to None.\n* **Raises:**\n  * **ImportError** – If the cohere library is not installed.\n  * **ValueError** – If the API key is not provided.\n\n#### `async arank(query, docs, **kwargs)`\n\nRerank documents based on the provided query using the Cohere rerank API.\n\nThis method processes the user’s query and the provided documents to\nrerank them in a manner that is potentially more relevant to the\nquery’s context.\n\n* **Parameters:**\n  * **query** (*str*) – The user’s search query.\n  * **docs** (*Union* *[* *List* *[* *Dict* *[* *str* *,* *Any* *]* *]* *,* *List* *[* *str* *]* *]*) – The list of documents\n    to be ranked, either as dictionaries or strings.\n* **Returns:**\n  The reranked list of documents and optionally associated scores.\n* **Return type:**\n  Union[Tuple[Union[List[Dict[str, Any]], List[str]], float], List[Dict[str, Any]]]\n\n#### `model_post_init(context, /)`\n\nThis function is meant to behave like a BaseModel method to initialise private attributes.\n\nIt takes context as an argument since that’s what pydantic-core passes when calling it.\n\n* **Parameters:**\n  * **self** (*BaseModel*) – The BaseModel instance.\n  * **context** (*Any*) – The context.\n* **Return type:**\n  None\n\n#### `rank(query, docs, **kwargs)`\n\nRerank documents based on the provided query using the Cohere rerank API.\n\nThis method processes the user’s query and the provided documents to\nrerank them in a manner that is potentially more relevant to the\nquery’s context.\n\n* **Parameters:**\n  * **query** (*str*) – The user’s search query.\n  * **docs** (*Union* *[* *List* *[* *Dict* *[* *str* *,* *Any* *]* *]* *,* *List* *[* *str* *]* *]*) – The list of documents\n    to be ranked, either as dictionaries or strings.\n* **Returns:**\n  The reranked list of documents and optionally associated scores.\n* **Return type:**\n  Union[Tuple[Union[List[Dict[str, Any]], List[str]], float], List[Dict[str, Any]]]\n\n#### `model_config: ClassVar[ConfigDict] = {}`\n\nConfiguration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict]."},{"id":"hfcrossencoderreranker","title":"HFCrossEncoderReranker","role":"content","text":"<a id=\"hfcrossencoderreranker-api\"></a>"},{"id":"class-hfcrossencoderrerankermodelcross-encoderms-marco-minilm-l-6-v2-limit3-return_scoretrue--rank_bynone","title":"`class HFCrossEncoderReranker(model='cross-encoder/ms-marco-MiniLM-L-6-v2', limit=3, return_score=True, *, rank_by=None)`","role":"content","text":"Bases: `BaseReranker`\n\nThe HFCrossEncoderReranker class uses a cross-encoder models from Hugging Face\nto rerank documents based on an input query.\n\nThis reranker loads a cross-encoder model using the CrossEncoder class\nfrom the sentence_transformers library. It requires the\nsentence_transformers library to be installed.\n\n[code example]\n\nInitialize the HFCrossEncoderReranker with a specified model and ranking criteria.\n\n* **Parameters:**\n  * **model** (*str*) – The name or path of the cross-encoder model to use for reranking.\n    Defaults to ‘cross-encoder/ms-marco-MiniLM-L-6-v2’.\n  * **limit** (*int*) – The maximum number of results to return after reranking. Must be a positive integer.\n  * **return_score** (*bool*) – Whether to return scores alongside the reranked results.\n  * **rank_by** (*list* *[* *str* *]*  *|* *None*)\n\n#### `async arank(query, docs, **kwargs)`\n\nAsynchronously rerank documents based on the provided query using the loaded cross-encoder model.\n\nThis method processes the user’s query and the provided documents to rerank them\nin a manner that is potentially more relevant to the query’s context.\n\n* **Parameters:**\n  * **query** (*str*) – The user’s search query.\n  * **docs** (*Union* *[* *List* *[* *Dict* *[* *str* *,* *Any* *]* *]* *,* *List* *[* *str* *]* *]*) – The list of documents to be ranked,\n    either as dictionaries or strings.\n* **Returns:**\n  The reranked list of documents and optionally associated scores.\n* **Return type:**\n  Union[Tuple[List[Dict[str, Any]], List[float]], List[Dict[str, Any]]]\n\n#### `model_post_init(context, /)`\n\nThis function is meant to behave like a BaseModel method to initialise private attributes.\n\nIt takes context as an argument since that’s what pydantic-core passes when calling it.\n\n* **Parameters:**\n  * **self** (*BaseModel*) – The BaseModel instance.\n  * **context** (*Any*) – The context.\n* **Return type:**\n  None\n\n#### `rank(query, docs, **kwargs)`\n\nRerank documents based on the provided query using the loaded cross-encoder model.\n\nThis method processes the user’s query and the provided documents to rerank them\nin a manner that is potentially more relevant to the query’s context.\n\n* **Parameters:**\n  * **query** (*str*) – The user’s search query.\n  * **docs** (*Union* *[* *List* *[* *Dict* *[* *str* *,* *Any* *]* *]* *,* *List* *[* *str* *]* *]*) – The list of documents to be ranked,\n    either as dictionaries or strings.\n* **Returns:**\n  The reranked list of documents and optionally associated scores.\n* **Return type:**\n  Union[Tuple[List[Dict[str, Any]], List[float]], List[Dict[str, Any]]]\n\n#### `model_config: ClassVar[ConfigDict] = {}`\n\nConfiguration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict]."},{"id":"voyageaireranker","title":"VoyageAIReranker","role":"content","text":"<a id=\"voyageaireranker-api\"></a>"},{"id":"class-voyageairerankermodel-rank_bynone-limit5-return_scoretrue-api_confignone","title":"`class VoyageAIReranker(model, rank_by=None, limit=5, return_score=True, api_config=None)`","role":"content","text":"Bases: `BaseReranker`\n\nThe VoyageAIReranker class uses VoyageAI’s API to rerank documents based on an\ninput query.\n\nThis reranker is designed to interact with VoyageAI’s /rerank API,\nrequiring an API key for authentication. The key can be provided\ndirectly in the api_config dictionary or through the VOYAGE_API_KEY\nenvironment variable. User must obtain an API key from VoyageAI’s website\n([https://dash.voyageai.com/](https://dash.voyageai.com/)). Additionally, the voyageai python\nclient must be installed with pip install voyageai.\n\n[code example]\n\nInitialize the VoyageAIReranker with specified model, ranking criteria,\nand API configuration.\n\n* **Parameters:**\n  * **model** (*str*) – The identifier for the VoyageAI model used for reranking.\n  * **rank_by** (*Optional* *[* *List* *[* *str* *]* *]*) – Optional list of keys specifying the\n    attributes in the documents that should be considered for\n    ranking. None means ranking will rely on the model’s default\n    behavior.\n  * **limit** (*int*) – The maximum number of results to return after\n    reranking. Must be a positive integer.\n  * **return_score** (*bool*) – Whether to return scores alongside the\n    reranked results.\n  * **api_config** (*Optional* *[* *Dict* *]* *,* *optional*) – Dictionary containing the API key.\n    Defaults to None.\n* **Raises:**\n  * **ImportError** – If the voyageai library is not installed.\n  * **ValueError** – If the API key is not provided.\n\n#### `async arank(query, docs, **kwargs)`\n\nRerank documents based on the provided query using the VoyageAI rerank API.\n\nThis method processes the user’s query and the provided documents to\nrerank them in a manner that is potentially more relevant to the\nquery’s context.\n\n* **Parameters:**\n  * **query** (*str*) – The user’s search query.\n  * **docs** (*Union* *[* *List* *[* *Dict* *[* *str* *,* *Any* *]* *]* *,* *List* *[* *str* *]* *]*) – The list of documents\n    to be ranked, either as dictionaries or strings.\n* **Returns:**\n  The reranked list of documents and optionally associated scores.\n* **Return type:**\n  Union[Tuple[Union[List[Dict[str, Any]], List[str]], float], List[Dict[str, Any]]]\n\n#### `model_post_init(context, /)`\n\nThis function is meant to behave like a BaseModel method to initialise private attributes.\n\nIt takes context as an argument since that’s what pydantic-core passes when calling it.\n\n* **Parameters:**\n  * **self** (*BaseModel*) – The BaseModel instance.\n  * **context** (*Any*) – The context.\n* **Return type:**\n  None\n\n#### `rank(query, docs, **kwargs)`\n\nRerank documents based on the provided query using the VoyageAI rerank API.\n\nThis method processes the user’s query and the provided documents to\nrerank them in a manner that is potentially more relevant to the\nquery’s context.\n\n* **Parameters:**\n  * **query** (*str*) – The user’s search query.\n  * **docs** (*Union* *[* *List* *[* *Dict* *[* *str* *,* *Any* *]* *]* *,* *List* *[* *str* *]* *]*) – The list of documents\n    to be ranked, either as dictionaries or strings.\n* **Returns:**\n  The reranked list of documents and optionally associated scores.\n* **Return type:**\n  Union[Tuple[Union[List[Dict[str, Any]], List[str]], float], List[Dict[str, Any]]]\n\n#### `model_config: ClassVar[ConfigDict] = {}`\n\nConfiguration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict]."}],"examples":[{"id":"class-coherererankermodelrerank-english-v30-rank_bynone-limit5-return_scoretrue-api_confignone-ex0","language":"python","code":"from redisvl.utils.rerank import CohereReranker\n\n# set up the Cohere reranker with some configuration\nreranker = CohereReranker(rank_by=[\"content\"], limit=2)\n# rerank raw search results based on user input/query\nresults = reranker.rank(\n    query=\"your input query text here\",\n    docs=[\n        {\"content\": \"document 1\"},\n        {\"content\": \"document 2\"},\n        {\"content\": \"document 3\"}\n    ]\n)","section_id":"class-coherererankermodelrerank-english-v30-rank_bynone-limit5-return_scoretrue-api_confignone"},{"id":"class-hfcrossencoderrerankermodelcross-encoderms-marco-minilm-l-6-v2-limit3-return_scoretrue--rank_bynone-ex0","language":"python","code":"from redisvl.utils.rerank import HFCrossEncoderReranker\n\n# set up the HFCrossEncoderReranker with a specific model\nreranker = HFCrossEncoderReranker(model_name=\"cross-encoder/ms-marco-MiniLM-L-6-v2\", limit=3)\n# rerank raw search results based on user input/query\nresults = reranker.rank(\n    query=\"your input query text here\",\n    docs=[\n        {\"content\": \"document 1\"},\n        {\"content\": \"document 2\"},\n        {\"content\": \"document 3\"}\n    ]\n)","section_id":"class-hfcrossencoderrerankermodelcross-encoderms-marco-minilm-l-6-v2-limit3-return_scoretrue--rank_bynone"},{"id":"class-voyageairerankermodel-rank_bynone-limit5-return_scoretrue-api_confignone-ex0","language":"python","code":"from redisvl.utils.rerank import VoyageAIReranker\n\n# set up the VoyageAI reranker with some configuration\nreranker = VoyageAIReranker(rank_by=[\"content\"], limit=2)\n# rerank raw search results based on user input/query\nresults = reranker.rank(\n    query=\"your input query text here\",\n    docs=[\n        {\"content\": \"document 1\"},\n        {\"content\": \"document 2\"},\n        {\"content\": \"document 3\"}\n    ]\n)","section_id":"class-voyageairerankermodel-rank_bynone-limit5-return_scoretrue-api_confignone"}]}
{"schema_version":2,"id":"develop/ai/redisvl/api/router","title":"Semantic Router","url":"https://redis.io/docs/latest/develop/ai/redisvl/api/router/","summary":"","aliases":["/integrate/redisvl/api/router"],"tags":[],"last_updated":"2026-10-12T09:34:21+02:00","page_type":"content","content_hash":"53b6a37f517c3c88e47772c8a709b0be666589f7232029045e7f85f31809da7e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"<a id=\"semantic-router-api\"></a>"},{"id":"semantic-router","title":"Semantic Router","role":"content","text":""},{"id":"class-semanticroutername-routes-vectorizernone-routing_confignone-redis_clientnone-redis_urlredislocalhost6379-overwritefalse-connection_kwargsnone-create_indextrue","title":"`class SemanticRouter(name, routes, vectorizer=None, routing_config=None, redis_client=None, redis_url='redis://localhost:6379', overwrite=False, connection_kwargs=None, create_index=True)`","role":"content","text":"Semantic Router for managing and querying route vectors.\n\nInitialize the SemanticRouter.\n\n* **Parameters:**\n  * **name** (*str*) – The name of the semantic router.\n  * **routes** (*List* *[*[Route](#route) *]*) – List of Route objects.\n  * **vectorizer** (*BaseVectorizer* *,* *optional*) – The vectorizer used to embed route references. Defaults to default HFTextVectorizer.\n  * **routing_config** ([RoutingConfig](#routingconfig) *,* *optional*) – Configuration for routing behavior. Defaults to the default RoutingConfig.\n  * **redis_client** (*Optional* *[* *SyncRedisClient* *]* *,* *optional*) – Redis client for connection. Defaults to None.\n  * **redis_url** (*str* *,* *optional*) – The redis url. Defaults to redis://localhost:6379.\n  * **overwrite** (*bool* *,* *optional*) – Whether to overwrite existing index. Defaults to False.\n  * **connection_kwargs** (*Dict* *[* *str* *,* *Any* *]*) – The connection arguments\n    for the redis client. Defaults to empty {}.\n  * **create_index** (*bool* *,* *optional*) – Whether RedisVL creates and validates\n    the index. When False the constructor issues no index command at\n    all and writes nothing: the index must already exist, already\n    hold the reference vectors for `routes`, and already have its\n    stored config, since none of that is written or verified.\n    `routes` must match what is indexed, because each route’s\n    distance threshold is applied from this local list – and\n    [add_route](#add_route) rewrites the stored config from that same list,\n    so attaching with a partial set and then adding a route\n    truncates the config every other client reads. See\n    [`SemanticCache`](https://redis.io/docs/latest/cache/#semanticcache) for a worked\n    example of the flag, and [Install RedisVL](https://redis.io/docs/latest/../user_guide/installation) for the\n    ACL details. Defaults to True.\n* **Raises:**\n  **ValueError** – If both create_index is False and overwrite is True.\n\n#### `add_route(route)`\n\nAdd a new route to the SemanticRouter.\n\nNote that this replaces the router’s stored config with this instance’s\nroute list, so a router constructed with a subset of the indexed routes\nwill drop the rest from the config that [from_existing](#from_existing) reads.\n\nEmbeds the route’s references, writes them to the Redis index,\nappends the route to `self.routes`, and persists the updated router\nconfig so the route survives [from_existing](#from_existing).\n\n* **Parameters:**\n  **route** ([Route](#route)) – A fully-formed Route (name, references,\n  distance_threshold, optional metadata).\n* **Returns:**\n  The added route’s name.\n* **Return type:**\n  str\n* **Raises:**\n  **ValueError** – If a route with this name already exists on the router.\n      Use [add_route_references](#add_route_references) to extend an existing route.\n\n#### `add_route_references(route_name, references)`\n\nAdd a reference(s) to an existing route.\n\n* **Parameters:**\n  * **router_name** (*str*) – The name of the router.\n  * **references** (*Union* *[* *str* *,* *List* *[* *str* *]* *]*) – The reference or list of references to add.\n  * **route_name** (*str*)\n* **Returns:**\n  The list of added references keys.\n* **Return type:**\n  List[str]\n\n#### `clear()`\n\nDelete every route reference, leaving the index in place.\n\nClears by index membership. Available under `create_index=False`;\ndropping the index is [delete](#delete).\n\n> [!WARNING]\n> The stored `route_config` is left as it was, here and on the\n> default path. A separate process calling [from_existing](#from_existing)\n> afterwards will report routes whose reference vectors are gone.\n> [remove_route](#remove_route) keeps the two in step.\n\n* **Return type:**\n  None\n\n#### `delete()`\n\nDelete the semantic router index and its persisted route config.\n\n* **Raises:**\n  **ValueError** – If `create_index=False`. Use [clear](#clear) to\n      remove the route references and leave the index standing.\n* **Return type:**\n  None\n\n#### `delete_route_references(route_name='', reference_ids=None, keys=None)`\n\nGet references for an existing semantic router route.\n\n* **Parameters:**\n  * **Optional** (*keys*) – The name of the router.\n  * **Optional** – The reference or list of references to delete.\n  * **Optional** – List of fully qualified keys (prefix:router:reference_id) to delete.\n  * **route_name** (*str*)\n  * **reference_ids** (*list* *[* *str* *]*  *|* *None*)\n  * **keys** (*list* *[* *str* *]*  *|* *None*)\n* **Returns:**\n  Number of objects deleted\n* **Return type:**\n  int\n\n#### `classmethod from_dict(data, **kwargs)`\n\nCreate a SemanticRouter from a dictionary.\n\n* **Parameters:**\n  **data** (*Dict* *[* *str* *,* *Any* *]*) – The dictionary containing the semantic router data.\n* **Returns:**\n  The semantic router instance.\n* **Return type:**\n  [SemanticRouter](#semanticrouter)\n* **Raises:**\n  **ValueError** – If required data is missing or invalid.\n\n[code example]\n\n#### `classmethod from_existing(name, redis_client=None, redis_url='redis://localhost:6379', **kwargs)`\n\nReturn SemanticRouter instance from existing index.\n\nReads the stored route config with `JSON.GET`, so unlike\n`SearchIndex.from_existing()` this needs no `FT.INFO`. Pass\n`create_index=False` to keep it that way through construction, which\nmakes this the way to attach to a router with a credential that cannot\nrun index-metadata commands.\n\n* **Parameters:**\n  * **name** (*str*)\n  * **redis_client** (*Redis* *|* *RedisCluster* *|* *None*)\n  * **redis_url** (*str*)\n* **Return type:**\n  [SemanticRouter](#semanticrouter)\n\n#### `classmethod from_yaml(file_path, **kwargs)`\n\nCreate a SemanticRouter from a YAML file.\n\n* **Parameters:**\n  **file_path** (*str*) – The path to the YAML file.\n* **Returns:**\n  The semantic router instance.\n* **Return type:**\n  [SemanticRouter](#semanticrouter)\n* **Raises:**\n  * **ValueError** – If the file path is invalid.\n  * **FileNotFoundError** – If the file does not exist.\n\n[code example]\n\n#### `get(route_name)`\n\nGet a route by its name.\n\n* **Parameters:**\n  **route_name** (*str*) – Name of the route.\n* **Returns:**\n  The selected Route object or None if not found.\n* **Return type:**\n  Optional[[Route](#route)]\n\n#### `get_route_references(route_name='', reference_ids=None, keys=None)`\n\nGet references for an existing route route.\n\n* **Parameters:**\n  * **router_name** (*str*) – The name of the router.\n  * **references** (*Union* *[* *str* *,* *List* *[* *str* *]* *]*) – The reference or list of references to add.\n  * **route_name** (*str*)\n  * **reference_ids** (*list* *[* *str* *]*  *|* *None*)\n  * **keys** (*list* *[* *str* *]*  *|* *None*)\n* **Returns:**\n  Reference objects stored\n* **Return type:**\n  List[Dict[str, Any]]]\n\n#### `model_post_init(context, /)`\n\nThis function is meant to behave like a BaseModel method to initialise private attributes.\n\nIt takes context as an argument since that’s what pydantic-core passes when calling it.\n\n* **Parameters:**\n  * **self** (*BaseModel*) – The BaseModel instance.\n  * **context** (*Any*) – The context.\n* **Return type:**\n  None\n\n#### `remove_route(route_name)`\n\nRemove a route and all references from the semantic router.\n\nLike [add_route](#add_route), this replaces the router’s stored config with\nthis instance’s route list, so removing one route from a router holding\nonly a subset drops the rest from the config [from_existing](#from_existing)\nreads.\n\n* **Parameters:**\n  **route_name** (*str*) – Name of the route to remove.\n* **Return type:**\n  None\n\n#### `route_many(statement=None, vector=None, max_k=None, distance_threshold=None, aggregation_method=None)`\n\nQuery the semantic router with a given statement or vector for multiple matches.\n\n* **Parameters:**\n  * **statement** (*Optional* *[* *str* *]*) – The input statement to be queried.\n  * **vector** (*Optional* *[* *List* *[* *float* *]* *]*) – The input vector to be queried.\n  * **max_k** (*Optional* *[* *int* *]*) – The maximum number of top matches to return.\n  * **distance_threshold** (*Optional* *[* *float* *]*) – The threshold for semantic distance.\n  * **aggregation_method** (*Optional* *[*[DistanceAggregationMethod](#distanceaggregationmethod) *]*) – The aggregation method used for vector distances.\n* **Returns:**\n  The matching routes and their details.\n* **Return type:**\n  List[[RouteMatch](#routematch)]\n\n#### `to_dict()`\n\nConvert the SemanticRouter instance to a dictionary.\n\n* **Returns:**\n  The dictionary representation of the SemanticRouter.\n* **Return type:**\n  Dict[str, Any]\n\n[code example]\n\n#### `to_yaml(file_path, overwrite=True)`\n\nWrite the semantic router to a YAML file.\n\n* **Parameters:**\n  * **file_path** (*str*) – The path to the YAML file.\n  * **overwrite** (*bool*) – Whether to overwrite the file if it already exists.\n* **Raises:**\n  **FileExistsError** – If the file already exists and overwrite is False.\n* **Return type:**\n  None\n\n[code example]\n\n#### `update_route_thresholds(route_thresholds)`\n\nUpdate the distance thresholds for each route.\n\n* **Parameters:**\n  **route_thresholds** (*Dict* *[* *str* *,* *float* *]*) – Dictionary of route names and their distance thresholds.\n\n#### `update_routing_config(routing_config)`\n\nUpdate the routing configuration.\n\n* **Parameters:**\n  **routing_config** ([RoutingConfig](#routingconfig)) – The new routing configuration.\n\n#### `model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True}`\n\nConfiguration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].\n\n#### `name: str`\n\nThe name of the semantic router.\n\n#### `property route_names: list[str]`\n\nGet the list of route names.\n\n* **Returns:**\n  List of route names.\n* **Return type:**\n  List[str]\n\n#### `property route_thresholds: dict[str, float | None]`\n\nGet the distance thresholds for each route.\n\n* **Returns:**\n  Dictionary of route names and their distance thresholds.\n* **Return type:**\n  Dict[str, float]\n\n#### `routes: `list[[Route](#route)]\n\nList of Route objects.\n\n#### `routing_config: `[RoutingConfig](#routingconfig)\n\nConfiguration for routing behavior.\n\n#### `vectorizer: BaseVectorizer`\n\nThe vectorizer used to embed route references."},{"id":"routing-config","title":"Routing Config","role":"content","text":""},{"id":"class-routingconfig-max_k1-aggregation_methoddistanceaggregationmethodavg","title":"`class RoutingConfig(*, max_k=1, aggregation_method=DistanceAggregationMethod.avg)`","role":"content","text":"Configuration for routing behavior.\n\nCreate a new model by parsing and validating input data from keyword arguments.\n\nRaises [ValidationError][pydantic_core.ValidationError] if the input data cannot be\nvalidated to form a valid model.\n\nself is explicitly positional-only to allow self as a field name.\n\n* **Parameters:**\n  * **max_k** (*Annotated* *[* *int* *,* *FieldInfo* *(* *annotation=NoneType* *,* *required=True* *,* *metadata=* *[* *Strict* *(* *strict=True* *)* *,* *Gt* *(* *gt=0* *)* *]* *)* *]*)\n  * **aggregation_method** ([DistanceAggregationMethod](#distanceaggregationmethod))\n\n#### `max_k: Annotated[int, FieldInfo(annotation=NoneType, required=True, metadata=[Strict(strict=True), Gt(gt=0)])]`\n\nAggregation method to use to classify queries.\n\n#### `model_config: ClassVar[ConfigDict] = {'extra': 'ignore'}`\n\nConfiguration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict]."},{"id":"route","title":"Route","role":"content","text":""},{"id":"class-route-name-references-metadata-distance_threshold05","title":"`class Route(*, name, references, metadata={}, distance_threshold=0.5)`","role":"content","text":"Model representing a routing path with associated metadata and thresholds.\n\nCreate a new model by parsing and validating input data from keyword arguments.\n\nRaises [ValidationError][pydantic_core.ValidationError] if the input data cannot be\nvalidated to form a valid model.\n\nself is explicitly positional-only to allow self as a field name.\n\n* **Parameters:**\n  * **name** (*str*)\n  * **references** (*list* *[* *str* *]*)\n  * **metadata** (*dict* *[* *str* *,* *Any* *]*)\n  * **distance_threshold** (*Annotated* *[* *float* *,* *FieldInfo* *(* *annotation=NoneType* *,* *required=True* *,* *metadata=* *[* *Strict* *(* *strict=True* *)* *,* *Gt* *(* *gt=0* *)* *,* *Le* *(* *le=2* *)* *]* *)* *]*)\n\n#### `distance_threshold: Annotated[float, FieldInfo(annotation=NoneType, required=True, metadata=[Strict(strict=True), Gt(gt=0), Le(le=2)])]`\n\nDistance threshold for matching the route.\n\n#### `metadata: dict[str, Any]`\n\nMetadata associated with the route.\n\n#### `model_config: ClassVar[ConfigDict] = {}`\n\nConfiguration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].\n\n#### `name: str`\n\nThe name of the route.\n\n#### `references: list[str]`\n\nList of reference phrases for the route."},{"id":"route-match","title":"Route Match","role":"content","text":""},{"id":"class-routematch-namenone-distancenone","title":"`class RouteMatch(*, name=None, distance=None)`","role":"content","text":"Model representing a matched route with distance information.\n\nCreate a new model by parsing and validating input data from keyword arguments.\n\nRaises [ValidationError][pydantic_core.ValidationError] if the input data cannot be\nvalidated to form a valid model.\n\nself is explicitly positional-only to allow self as a field name.\n\n* **Parameters:**\n  * **name** (*str* *|* *None*)\n  * **distance** (*float* *|* *None*)\n\n#### `distance: float | None`\n\nThe vector distance between the statement and the matched route.\n\n#### `model_config: ClassVar[ConfigDict] = {}`\n\nConfiguration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].\n\n#### `name: str | None`\n\nThe matched route name."},{"id":"distance-aggregation-method","title":"Distance Aggregation Method","role":"content","text":""},{"id":"class-distanceaggregationmethodvalue-namesnone--modulenone-qualnamenone-typenone-start1-boundarynone","title":"`class DistanceAggregationMethod(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)`","role":"content","text":"Enumeration for distance aggregation methods.\n\n#### `avg = 'avg'`\n\nCompute the average of the vector distances.\n\n#### `min = 'min'`\n\nCompute the minimum of the vector distances.\n\n#### `sum = 'sum'`\n\nCompute the sum of the vector distances."}],"examples":[{"id":"class-semanticroutername-routes-vectorizernone-routing_confignone-redis_clientnone-redis_urlredislocalhost6379-overwritefalse-connection_kwargsnone-create_indextrue-ex0","language":"python","code":"from redisvl.extensions.router import SemanticRouter\nrouter_data = {\n    \"name\": \"example_router\",\n    \"routes\": [{\"name\": \"route1\", \"references\": [\"ref1\"], \"distance_threshold\": 0.5}],\n    \"vectorizer\": {\"type\": \"openai\", \"model\": \"text-embedding-ada-002\"},\n}\nrouter = SemanticRouter.from_dict(router_data)","section_id":"class-semanticroutername-routes-vectorizernone-routing_confignone-redis_clientnone-redis_urlredislocalhost6379-overwritefalse-connection_kwargsnone-create_indextrue"},{"id":"class-semanticroutername-routes-vectorizernone-routing_confignone-redis_clientnone-redis_urlredislocalhost6379-overwritefalse-connection_kwargsnone-create_indextrue-ex1","language":"python","code":"from redisvl.extensions.router import SemanticRouter\nrouter = SemanticRouter.from_yaml(\"router.yaml\", redis_url=\"redis://localhost:6379\")","section_id":"class-semanticroutername-routes-vectorizernone-routing_confignone-redis_clientnone-redis_urlredislocalhost6379-overwritefalse-connection_kwargsnone-create_indextrue"},{"id":"class-semanticroutername-routes-vectorizernone-routing_confignone-redis_clientnone-redis_urlredislocalhost6379-overwritefalse-connection_kwargsnone-create_indextrue-ex2","language":"python","code":"from redisvl.extensions.router import SemanticRouter\nrouter = SemanticRouter(name=\"example_router\", routes=[], redis_url=\"redis://localhost:6379\")\nrouter_dict = router.to_dict()","section_id":"class-semanticroutername-routes-vectorizernone-routing_confignone-redis_clientnone-redis_urlredislocalhost6379-overwritefalse-connection_kwargsnone-create_indextrue"},{"id":"class-semanticroutername-routes-vectorizernone-routing_confignone-redis_clientnone-redis_urlredislocalhost6379-overwritefalse-connection_kwargsnone-create_indextrue-ex3","language":"python","code":"from redisvl.extensions.router import SemanticRouter\nrouter = SemanticRouter(\n    name=\"example_router\",\n    routes=[],\n    redis_url=\"redis://localhost:6379\"\n)\nrouter.to_yaml(\"router.yaml\")","section_id":"class-semanticroutername-routes-vectorizernone-routing_confignone-redis_clientnone-redis_urlredislocalhost6379-overwritefalse-connection_kwargsnone-create_indextrue"}]}
{"schema_version":2,"id":"develop/ai/redisvl/api/schema","title":"Schema","url":"https://redis.io/docs/latest/develop/ai/redisvl/api/schema/","summary":"","aliases":["/integrate/redisvl/api/schema"],"tags":[],"last_updated":"2026-10-05T16:11:44+01:00","page_type":"content","content_hash":"9d821a2b6a82159eb49b57442181aa6618f5f3acf9e08e9438d13475e218f9e0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Schema in RedisVL provides a structured format to define index settings and\nfield configurations using the following three components:\n\n| Component   | Description                                                                        |\n|-------------|------------------------------------------------------------------------------------|\n| version     | The version of the schema spec. Current supported version is 0.1.0.                |\n| index       | Index specific settings like name, key prefix, key separator, and storage type.    |\n| fields      | Subset of fields within your data to include in the index and any custom settings. |"},{"id":"indexschema","title":"IndexSchema","role":"content","text":"<a id=\"indexschema-api\"></a>"},{"id":"class-indexschema-index-fieldsfactory-version010","title":"`class IndexSchema(*, index, fields=<factory>, version='0.1.0')`","role":"content","text":"A schema definition for a search index in Redis, used in RedisVL for\nconfiguring index settings and organizing vector and metadata fields.\n\nThe class offers methods to create an index schema from a YAML file or a\nPython dictionary, supporting flexible schema definitions and easy\nintegration into various workflows.\n\nAn example schema.yaml file might look like this:\n\n[code example]\n\nLoading the schema for RedisVL from yaml is as simple as:\n\n[code example]\n\nLoading the schema for RedisVL from dict is as simple as:\n\n[code example]\n\n> [!NOTE]\n> The fields attribute in the schema must contain unique field names to ensure\n> correct and unambiguous field references.\n\nCreate a new model by parsing and validating input data from keyword arguments.\n\nRaises [ValidationError][pydantic_core.ValidationError] if the input data cannot be\nvalidated to form a valid model.\n\nself is explicitly positional-only to allow self as a field name.\n\n* **Parameters:**\n  * **index** (*IndexInfo*)\n  * **fields** (*dict* *[* *str* *,* *BaseField* *]*)\n  * **version** (*Literal* *[* *'0.1.0'* *]*)\n\n#### `add_field(field_inputs)`\n\nAdds a single field to the index schema based on the specified field\ntype and attributes.\n\nThis method allows for the addition of individual fields to the schema,\nproviding flexibility in defining the structure of the index.\n\n* **Parameters:**\n  **field_inputs** (*Dict* *[* *str* *,* *Any* *]*) – A field to add.\n* **Raises:**\n  **ValueError** – If the field name or type are not provided or if the name\n      already exists within the schema.\n\n[code example]\n\n#### `add_fields(fields)`\n\nExtends the schema with additional fields.\n\nThis method allows dynamically adding new fields to the index schema. It\nprocesses a list of field definitions.\n\n* **Parameters:**\n  **fields** (*List* *[* *Dict* *[* *str* *,* *Any* *]* *]*) – A list of fields to add.\n* **Raises:**\n  **ValueError** – If a field with the same name already exists in the\n      schema.\n\n[code example]\n\n#### `classmethod from_dict(data)`\n\nCreate an IndexSchema from a dictionary.\n\n* **Parameters:**\n  **data** (*Dict* *[* *str* *,* *Any* *]*) – The index schema data.\n* **Returns:**\n  The index schema.\n* **Return type:**\n  [IndexSchema](#indexschema)\n\n[code example]\n\n#### `classmethod from_yaml(file_path)`\n\nCreate an IndexSchema from a YAML file.\n\n* **Parameters:**\n  **file_path** (*str*) – The path to the YAML file.\n* **Returns:**\n  The index schema.\n* **Return type:**\n  [IndexSchema](#indexschema)\n\n[code example]\n\n#### `remove_field(field_name)`\n\nRemoves a field from the schema based on the specified name.\n\nThis method is useful for dynamically altering the schema by removing\nexisting fields.\n\n* **Parameters:**\n  **field_name** (*str*) – The name of the field to be removed.\n\n#### `to_dict()`\n\nSerialize the index schema model to a dictionary, handling Enums\nand other special cases properly.\n\n* **Returns:**\n  The index schema as a dictionary.\n* **Return type:**\n  Dict[str, Any]\n\n#### `to_yaml(file_path, overwrite=True)`\n\nWrite the index schema to a YAML file.\n\n* **Parameters:**\n  * **file_path** (*str*) – The path to the YAML file.\n  * **overwrite** (*bool*) – Whether to overwrite the file if it already exists.\n* **Raises:**\n  **FileExistsError** – If the file already exists and overwrite is False.\n* **Return type:**\n  None\n\n#### `property field_names: list[str]`\n\nA list of field names associated with the index schema.\n\n* **Returns:**\n  A list of field names from the schema.\n* **Return type:**\n  List[str]\n\n#### `fields: dict[str, BaseField]`\n\nFields associated with the search index and their properties.\n\nNote: When creating from dict/YAML, provide fields as a list of field definitions.\nThe validator will convert them to a Dict[str, BaseField] internally.\n\n#### `index: IndexInfo`\n\nDetails of the basic index configurations.\n\n#### `model_config: ClassVar[ConfigDict] = {}`\n\nConfiguration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].\n\n#### `version: Literal['0.1.0']`\n\nVersion of the underlying index schema."},{"id":"index-level-stopwords-configuration","title":"Index-Level Stopwords Configuration","role":"content","text":"The `IndexInfo` class supports index-level stopwords configuration through\nthe `stopwords` field. This controls which words are filtered during indexing\n(server-side), as opposed to query-time filtering (client-side).\n\n**Configuration Options:**\n\n- `None` (default): Use Redis default stopwords (~300 common words)\n- `[]` (empty list): Disable stopwords completely (`STOPWORDS 0`)\n- Custom list: Specify your own stopwords (e.g., `[\"the\", \"a\", \"an\"]`)\n\n**Example:**\n\n[code example]\n\n**Important Notes:**\n\n- Index-level stopwords affect what gets indexed (server-side)\n- Query-time stopwords (in `TextQuery` and `AggregateHybridQuery`) affect what gets searched (client-side)\n- Using query-time stopwords with index-level `STOPWORDS 0` is counterproductive\n\nFor detailed information about stopwords configuration and best practices, see the\nAdvanced Queries user guide (`docs/user_guide/11_advanced_queries.ipynb`)."},{"id":"defining-fields","title":"Defining Fields","role":"content","text":"Fields in the schema can be defined in YAML format or as a Python dictionary, specifying a name, type, an optional path, and attributes for customization.\n\n**YAML Example**:\n\n[code example]\n\n**Python Dictionary Example**:\n\n[code example]"},{"id":"basic-field-types","title":"Basic Field Types","role":"content","text":"RedisVL supports several basic field types for indexing different kinds of data. Each field type has specific attributes that customize its indexing and search behavior."},{"id":"text-fields","title":"`Text Fields`","role":"content","text":"Text fields support full-text search with stemming, phonetic matching, and other text analysis features."},{"id":"class-textfield-name-typefieldtypestext-pathnone-attrsfactory","title":"`class TextField(*, name, type=FieldTypes.TEXT, path=None, attrs=<factory>)`","role":"content","text":"Bases: `BaseField`\n\nText field supporting a full text search index\n\nCreate a new model by parsing and validating input data from keyword arguments.\n\nRaises [ValidationError][pydantic_core.ValidationError] if the input data cannot be\nvalidated to form a valid model.\n\nself is explicitly positional-only to allow self as a field name.\n\n* **Parameters:**\n  * **name** (*str*)\n  * **type** (*Literal* *[* *FieldTypes.TEXT* *]*)\n  * **path** (*str* *|* *None*)\n  * **attrs** ([TextFieldAttributes](#textfieldattributes))\n\n#### `as_redis_field()`\n\nConvert schema field to Redis Field object\n\n* **Return type:**\n  *Field*\n\n#### `attrs: `[TextFieldAttributes](#textfieldattributes)\n\nSpecified field attributes\n\n#### `model_config: ClassVar[ConfigDict] = {}`\n\nConfiguration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].\n\n#### `type: Literal[FieldTypes.TEXT]`\n\nField type"},{"id":"class-textfieldattributes-sortablefalse-index_missingfalse-no_indexfalse-weight1-no_stemfalse-withsuffixtriefalse-phonetic_matchernone-index_emptyfalse-unffalse","title":"`class TextFieldAttributes(*, sortable=False, index_missing=False, no_index=False, weight=1, no_stem=False, withsuffixtrie=False, phonetic_matcher=None, index_empty=False, unf=False)`","role":"content","text":"Full text field attributes\n\nCreate a new model by parsing and validating input data from keyword arguments.\n\nRaises [ValidationError][pydantic_core.ValidationError] if the input data cannot be\nvalidated to form a valid model.\n\nself is explicitly positional-only to allow self as a field name.\n\n* **Parameters:**\n  * **sortable** (*bool*)\n  * **index_missing** (*bool*)\n  * **no_index** (*bool*)\n  * **weight** (*float*)\n  * **no_stem** (*bool*)\n  * **withsuffixtrie** (*bool*)\n  * **phonetic_matcher** (*str* *|* *None*)\n  * **index_empty** (*bool*)\n  * **unf** (*bool*)\n\n#### `index_empty: bool`\n\nAllow indexing and searching for empty strings\n\n#### `model_config: ClassVar[ConfigDict] = {}`\n\nConfiguration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].\n\n#### `no_stem: bool`\n\nDisable stemming on the text field during indexing\n\n#### `phonetic_matcher: str | None`\n\nUsed to perform phonetic matching during search\n\n#### `unf: bool`\n\nUn-normalized form - disable normalization on sortable fields (only applies when sortable=True)\n\n#### `weight: float`\n\nDeclares the importance of this field when calculating results\n\n#### `withsuffixtrie: bool`\n\nKeep a suffix trie with all terms which match the suffix to optimize certain queries"},{"id":"tag-fields","title":"`Tag Fields`","role":"content","text":"Tag fields are optimized for exact-match filtering and faceted search on categorical data."},{"id":"class-tagfield-name-typefieldtypestag-pathnone-attrsfactory","title":"`class TagField(*, name, type=FieldTypes.TAG, path=None, attrs=<factory>)`","role":"content","text":"Bases: `BaseField`\n\nTag field for simple boolean-style filtering\n\nCreate a new model by parsing and validating input data from keyword arguments.\n\nRaises [ValidationError][pydantic_core.ValidationError] if the input data cannot be\nvalidated to form a valid model.\n\nself is explicitly positional-only to allow self as a field name.\n\n* **Parameters:**\n  * **name** (*str*)\n  * **type** (*Literal* *[* *FieldTypes.TAG* *]*)\n  * **path** (*str* *|* *None*)\n  * **attrs** ([TagFieldAttributes](#tagfieldattributes))\n\n#### `as_redis_field()`\n\nConvert schema field to Redis Field object\n\n* **Return type:**\n  *Field*\n\n#### `attrs: `[TagFieldAttributes](#tagfieldattributes)\n\nSpecified field attributes\n\n#### `model_config: ClassVar[ConfigDict] = {}`\n\nConfiguration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].\n\n#### `type: Literal[FieldTypes.TAG]`\n\nField type"},{"id":"class-tagfieldattributes-sortablefalse-index_missingfalse-no_indexfalse-separator-case_sensitivefalse-withsuffixtriefalse-index_emptyfalse","title":"`class TagFieldAttributes(*, sortable=False, index_missing=False, no_index=False, separator=',', case_sensitive=False, withsuffixtrie=False, index_empty=False)`","role":"content","text":"Tag field attributes\n\nCreate a new model by parsing and validating input data from keyword arguments.\n\nRaises [ValidationError][pydantic_core.ValidationError] if the input data cannot be\nvalidated to form a valid model.\n\nself is explicitly positional-only to allow self as a field name.\n\n* **Parameters:**\n  * **sortable** (*bool*)\n  * **index_missing** (*bool*)\n  * **no_index** (*bool*)\n  * **separator** (*str*)\n  * **case_sensitive** (*bool*)\n  * **withsuffixtrie** (*bool*)\n  * **index_empty** (*bool*)\n\n#### `case_sensitive: bool`\n\nTreat text as case sensitive or not. By default, tag characters are converted to lowercase\n\n#### `index_empty: bool`\n\nAllow indexing and searching for empty strings\n\n#### `model_config: ClassVar[ConfigDict] = {}`\n\nConfiguration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].\n\n#### `separator: str`\n\nIndicates how the text in the original attribute is split into individual tags\n\n#### `withsuffixtrie: bool`\n\nKeep a suffix trie with all terms which match the suffix to optimize certain queries"},{"id":"numeric-fields","title":"`Numeric Fields`","role":"content","text":"Numeric fields support range queries and sorting on numeric data."},{"id":"class-numericfield-name-typefieldtypesnumeric-pathnone-attrsfactory","title":"`class NumericField(*, name, type=FieldTypes.NUMERIC, path=None, attrs=<factory>)`","role":"content","text":"Bases: `BaseField`\n\nNumeric field for numeric range filtering\n\nCreate a new model by parsing and validating input data from keyword arguments.\n\nRaises [ValidationError][pydantic_core.ValidationError] if the input data cannot be\nvalidated to form a valid model.\n\nself is explicitly positional-only to allow self as a field name.\n\n* **Parameters:**\n  * **name** (*str*)\n  * **type** (*Literal* *[* *FieldTypes.NUMERIC* *]*)\n  * **path** (*str* *|* *None*)\n  * **attrs** ([NumericFieldAttributes](#numericfieldattributes))\n\n#### `as_redis_field()`\n\nConvert schema field to Redis Field object\n\n* **Return type:**\n  *Field*\n\n#### `attrs: `[NumericFieldAttributes](#numericfieldattributes)\n\nSpecified field attributes\n\n#### `model_config: ClassVar[ConfigDict] = {}`\n\nConfiguration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].\n\n#### `type: Literal[FieldTypes.NUMERIC]`\n\nField type"},{"id":"class-numericfieldattributes-sortablefalse-index_missingfalse-no_indexfalse-unffalse","title":"`class NumericFieldAttributes(*, sortable=False, index_missing=False, no_index=False, unf=False)`","role":"content","text":"Numeric field attributes\n\nCreate a new model by parsing and validating input data from keyword arguments.\n\nRaises [ValidationError][pydantic_core.ValidationError] if the input data cannot be\nvalidated to form a valid model.\n\nself is explicitly positional-only to allow self as a field name.\n\n* **Parameters:**\n  * **sortable** (*bool*)\n  * **index_missing** (*bool*)\n  * **no_index** (*bool*)\n  * **unf** (*bool*)\n\n#### `model_config: ClassVar[ConfigDict] = {}`\n\nConfiguration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].\n\n#### `unf: bool`\n\nUn-normalized form - disable normalization on sortable fields (only applies when sortable=True)"},{"id":"geo-fields","title":"`Geo Fields`","role":"content","text":"Geo fields enable location-based search with geographic coordinates."},{"id":"class-geofield-name-typefieldtypesgeo-pathnone-attrsfactory","title":"`class GeoField(*, name, type=FieldTypes.GEO, path=None, attrs=<factory>)`","role":"content","text":"Bases: `BaseField`\n\nGeo field with a geo-spatial index for location based search\n\nCreate a new model by parsing and validating input data from keyword arguments.\n\nRaises [ValidationError][pydantic_core.ValidationError] if the input data cannot be\nvalidated to form a valid model.\n\nself is explicitly positional-only to allow self as a field name.\n\n* **Parameters:**\n  * **name** (*str*)\n  * **type** (*Literal* *[* *FieldTypes.GEO* *]*)\n  * **path** (*str* *|* *None*)\n  * **attrs** ([GeoFieldAttributes](#geofieldattributes))\n\n#### `as_redis_field()`\n\nConvert schema field to Redis Field object\n\n* **Return type:**\n  *Field*\n\n#### `attrs: `[GeoFieldAttributes](#geofieldattributes)\n\nSpecified field attributes\n\n#### `model_config: ClassVar[ConfigDict] = {}`\n\nConfiguration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].\n\n#### `type: Literal[FieldTypes.GEO]`\n\nField type"},{"id":"class-geofieldattributes-sortablefalse-index_missingfalse-no_indexfalse","title":"`class GeoFieldAttributes(*, sortable=False, index_missing=False, no_index=False)`","role":"content","text":"Numeric field attributes\n\nCreate a new model by parsing and validating input data from keyword arguments.\n\nRaises [ValidationError][pydantic_core.ValidationError] if the input data cannot be\nvalidated to form a valid model.\n\nself is explicitly positional-only to allow self as a field name.\n\n* **Parameters:**\n  * **sortable** (*bool*)\n  * **index_missing** (*bool*)\n  * **no_index** (*bool*)\n\n#### `model_config: ClassVar[ConfigDict] = {}`\n\nConfiguration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict]."},{"id":"vector-field-types","title":"Vector Field Types","role":"content","text":"Vector fields enable semantic similarity search using various algorithms. All vector fields share common attributes but have algorithm-specific configurations."},{"id":"common-vector-attributes","title":"`Common Vector Attributes`","role":"content","text":"All vector field types share these base attributes:"},{"id":"class-basevectorfieldattributes-dims-algorithm-datatypevectordatatypefloat32-distance_metricvectordistancemetriccosine-initial_capnone-index_missingfalse","title":"`class BaseVectorFieldAttributes(*, dims, algorithm, datatype=VectorDataType.FLOAT32, distance_metric=VectorDistanceMetric.COSINE, initial_cap=None, index_missing=False)`","role":"content","text":"Base vector field attributes shared by FLAT, HNSW, and SVS-VAMANA fields\n\nCreate a new model by parsing and validating input data from keyword arguments.\n\nRaises [ValidationError][pydantic_core.ValidationError] if the input data cannot be\nvalidated to form a valid model.\n\nself is explicitly positional-only to allow self as a field name.\n\n* **Parameters:**\n  * **dims** (*int*)\n  * **algorithm** (*VectorIndexAlgorithm*)\n  * **datatype** (*VectorDataType*)\n  * **distance_metric** (*VectorDistanceMetric*)\n  * **initial_cap** (*int* *|* *None*)\n  * **index_missing** (*bool*)\n\n#### `classmethod uppercase_strings(v)`\n\nValidate that provided values are cast to uppercase\n\n#### `algorithm: VectorIndexAlgorithm`\n\nFLAT, HNSW, or SVS-VAMANA\n\n* **Type:**\n  The indexing algorithm for the field\n\n#### `datatype: VectorDataType`\n\nThe float datatype for the vector embeddings\n\n#### `dims: int`\n\nDimensionality of the vector embeddings field\n\n#### `distance_metric: VectorDistanceMetric`\n\nThe distance metric used to measure query relevance\n\n#### `property field_data: dict[str, Any]`\n\nSelect attributes required by the Redis API\n\n#### `index_missing: bool`\n\nAllow indexing and searching for missing values (documents without the field)\n\n#### `initial_cap: int | None`\n\nInitial vector capacity in the index affecting memory allocation size of the index\n\n#### `model_config: ClassVar[ConfigDict] = {}`\n\nConfiguration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].\n\n**Key Attributes:**\n\n- dims: Dimensionality of the vector (e.g., 768, 1536).\n- algorithm: Indexing algorithm for vector search:\n  - flat: Brute-force exact search. 100% recall, slower for large datasets. Best for <10K vectors.\n  - hnsw: Graph-based approximate search. Fast with high recall (95-99%). Best for general use.\n  - svs-vamana: SVS-VAMANA (Scalable Vector Search with VAMANA graph algorithm) provides fast approximate nearest neighbor search with optional compression support. This algorithm is optimized for Intel hardware and offers reduced memory usage through vector compression.\n\n  > [!NOTE]\n  > For detailed algorithm comparison and selection guidance, see [Vector Algorithm Comparison](#vector-algorithm-comparison).\n- datatype: Float precision (bfloat16, float16, float32, float64). Note: SVS-VAMANA only supports float16 and float32.\n- distance_metric: Similarity metric (COSINE, L2, IP).\n- initial_cap: Initial capacity hint for memory allocation (optional).\n- index_missing: When True, allows searching for documents missing this field (optional)."},{"id":"hnsw-vector-fields","title":"`HNSW Vector Fields`","role":"content","text":"HNSW (Hierarchical Navigable Small World) - Graph-based approximate search with excellent recall. **Best for general-purpose vector search (10K-1M+ vectors).**"},{"id":"when-to-use-hnsw--performance-details","title":"`When to use HNSW & Performance Details`","role":"content","text":"**Use HNSW when:**\n\n- Medium to large datasets (100K-1M+ vectors) requiring high recall rates\n- Search accuracy is more important than memory usage\n- Need general-purpose vector search with balanced performance\n- Cross-platform deployments where hardware-specific optimizations aren’t available\n\n**Performance characteristics:**\n\n- **Search speed**: Very fast approximate search with tunable accuracy (via `ef_runtime` at query time)\n- **Memory usage**: Higher than compressed SVS-VAMANA but reasonable for most applications\n- **Recall quality**: Excellent recall rates (95-99%), tunable via `ef_runtime` parameter\n- **Build time**: Moderate construction time, faster than SVS-VAMANA for smaller datasets\n\n**Runtime parameters** (adjustable at query time without rebuilding index):\n\n- `ef_runtime`: Controls search accuracy (higher = better recall, slower search). Default: 10\n- `epsilon`: Range search approximation factor for VectorRangeQuery. Default: 0.01"},{"id":"class-hnswvectorfield-name-typevector-pathnone-attrs","title":"`class HNSWVectorField(*, name, type='vector', path=None, attrs)`","role":"content","text":"Bases: `BaseField`\n\nVector field with HNSW (Hierarchical Navigable Small World) indexing for approximate nearest neighbor search.\n\nCreate a new model by parsing and validating input data from keyword arguments.\n\nRaises [ValidationError][pydantic_core.ValidationError] if the input data cannot be\nvalidated to form a valid model.\n\nself is explicitly positional-only to allow self as a field name.\n\n* **Parameters:**\n  * **name** (*str*)\n  * **type** (*Literal* *[* *'vector'* *]*)\n  * **path** (*str* *|* *None*)\n  * **attrs** ([HNSWVectorFieldAttributes](#hnswvectorfieldattributes))\n\n#### `as_redis_field()`\n\nConvert schema field to Redis Field object\n\n* **Return type:**\n  *Field*\n\n#### `attrs: `[HNSWVectorFieldAttributes](#hnswvectorfieldattributes)\n\nSpecified field attributes\n\n#### `model_config: ClassVar[ConfigDict] = {}`\n\nConfiguration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].\n\n#### `type: Literal['vector']`\n\nField type"},{"id":"class-hnswvectorfieldattributes-dims-algorithmvectorindexalgorithmhnsw-datatypevectordatatypefloat32-distance_metricvectordistancemetriccosine-initial_capnone-index_missingfalse-m16-ef_construction200-ef_runtime10-epsilon001","title":"`class HNSWVectorFieldAttributes(*, dims, algorithm=VectorIndexAlgorithm.HNSW, datatype=VectorDataType.FLOAT32, distance_metric=VectorDistanceMetric.COSINE, initial_cap=None, index_missing=False, m=16, ef_construction=200, ef_runtime=10, epsilon=0.01)`","role":"content","text":"HNSW vector field attributes for approximate nearest neighbor search.\n\nCreate a new model by parsing and validating input data from keyword arguments.\n\nRaises [ValidationError][pydantic_core.ValidationError] if the input data cannot be\nvalidated to form a valid model.\n\nself is explicitly positional-only to allow self as a field name.\n\n* **Parameters:**\n  * **dims** (*int*)\n  * **algorithm** (*Literal* *[* *VectorIndexAlgorithm.HNSW* *]*)\n  * **datatype** (*VectorDataType*)\n  * **distance_metric** (*VectorDistanceMetric*)\n  * **initial_cap** (*int* *|* *None*)\n  * **index_missing** (*bool*)\n  * **m** (*int*)\n  * **ef_construction** (*int*)\n  * **ef_runtime** (*int*)\n  * **epsilon** (*float*)\n\n#### `algorithm: Literal[VectorIndexAlgorithm.HNSW]`\n\nThe indexing algorithm (fixed as ‘hnsw’)\n\n#### `ef_construction: int`\n\n100-800)\n\n* **Type:**\n  Max edge candidates during build time (default\n* **Type:**\n  200, range\n\n#### `ef_runtime: int`\n\n1. - primary tuning parameter\n\n* **Type:**\n  Max top candidates during search (default\n\n#### `epsilon: float`\n\n0.01)\n\n* **Type:**\n  Range search boundary factor (default\n\n#### `m: int`\n\n8-64)\n\n* **Type:**\n  Max outgoing edges per node in each layer (default\n* **Type:**\n  16, range\n\n#### `model_config: ClassVar[ConfigDict] = {}`\n\nConfiguration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].\n\n**HNSW Examples:**\n\n**Balanced configuration (recommended starting point):**\n\n[code example]\n\n**High-recall configuration:**\n\n[code example]"},{"id":"svs-vamana-vector-fields","title":"`SVS-VAMANA Vector Fields`","role":"content","text":"SVS-VAMANA (Scalable Vector Search with VAMANA graph algorithm) provides fast approximate nearest neighbor search with optional compression support. This algorithm is optimized for Intel hardware and offers reduced memory usage through vector compression. **Best for large datasets (>100K vectors) on Intel hardware with memory constraints.**"},{"id":"when-to-use-svs-vamana--detailed-guide","title":"`When to use SVS-VAMANA & Detailed Guide`","role":"content","text":"**Requirements:**\n: - Redis >= 8.2.0 with Redis Search >= 2.8.10\n  - datatype must be ‘float16’ or ‘float32’ (float64/bfloat16 not supported)\n\n**Use SVS-VAMANA when:**\n: - Large datasets where memory is expensive\n  - Cloud deployments with memory-based pricing\n  - When 90-95% recall is acceptable\n  - High-dimensional vectors (>1024 dims) with LeanVec compression\n\n**Performance vs other algorithms:**\n: - **vs FLAT**: Much faster search, significantly lower memory usage with compression, but approximate results\n  - **vs HNSW**: Better memory efficiency with compression, similar or better recall, Intel-optimized\n\n**Runtime parameters** (adjustable at query time without rebuilding index):\n\n- `epsilon`: Range search approximation factor. Default: 0.01\n- `search_window_size`: Size of search window for KNN searches. Higher = better recall, slower search\n- `use_search_history`: Whether to use search buffer (OFF/ON/AUTO). Default: AUTO\n- `search_buffer_capacity`: Tuning parameter for 2-level compression. Default: search_window_size\n\n**Compression selection guide:**\n\n- **No compression**: Best performance, standard memory usage\n- **LVQ4/LVQ8**: Good balance of compression (2x-4x) and performance\n- **LeanVec4x8/LeanVec8x8**: Maximum compression (up to 8x) with dimensionality reduction\n\n**Memory Savings Examples (1M vectors, 768 dims):**\n: - No compression (float32): 3.1 GB\n  - LVQ4x4 compression: 1.6 GB (~48% savings)\n  - LeanVec4x8 + reduce to 384: 580 MB (~81% savings)"},{"id":"class-svsvectorfield-name-typefieldtypesvector-pathnone-attrs","title":"`class SVSVectorField(*, name, type=FieldTypes.VECTOR, path=None, attrs)`","role":"content","text":"Bases: `BaseField`\n\nVector field with SVS-VAMANA indexing and compression for memory-efficient approximate nearest neighbor search.\n\nCreate a new model by parsing and validating input data from keyword arguments.\n\nRaises [ValidationError][pydantic_core.ValidationError] if the input data cannot be\nvalidated to form a valid model.\n\nself is explicitly positional-only to allow self as a field name.\n\n* **Parameters:**\n  * **name** (*str*)\n  * **type** (*Literal* *[* *FieldTypes.VECTOR* *]*)\n  * **path** (*str* *|* *None*)\n  * **attrs** ([SVSVectorFieldAttributes](#svsvectorfieldattributes))\n\n#### `as_redis_field()`\n\nConvert schema field to Redis Field object\n\n* **Return type:**\n  *Field*\n\n#### `attrs: `[SVSVectorFieldAttributes](#svsvectorfieldattributes)\n\nSpecified field attributes\n\n#### `model_config: ClassVar[ConfigDict] = {}`\n\nConfiguration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].\n\n#### `type: Literal[FieldTypes.VECTOR]`\n\nField type"},{"id":"class-svsvectorfieldattributes-dims-algorithmvectorindexalgorithmsvs_vamana-datatypevectordatatypefloat32-distance_metricvectordistancemetriccosine-initial_capnone-index_missingfalse-graph_max_degree40-construction_window_size250-search_window_size20-epsilon001-compressionnone-reducenone-training_thresholdnone","title":"`class SVSVectorFieldAttributes(*, dims, algorithm=VectorIndexAlgorithm.SVS_VAMANA, datatype=VectorDataType.FLOAT32, distance_metric=VectorDistanceMetric.COSINE, initial_cap=None, index_missing=False, graph_max_degree=40, construction_window_size=250, search_window_size=20, epsilon=0.01, compression=None, reduce=None, training_threshold=None)`","role":"content","text":"SVS-VAMANA vector field attributes with compression support.\n\nCreate a new model by parsing and validating input data from keyword arguments.\n\nRaises [ValidationError][pydantic_core.ValidationError] if the input data cannot be\nvalidated to form a valid model.\n\nself is explicitly positional-only to allow self as a field name.\n\n* **Parameters:**\n  * **dims** (*int*)\n  * **algorithm** (*Literal* *[* *VectorIndexAlgorithm.SVS_VAMANA* *]*)\n  * **datatype** (*VectorDataType*)\n  * **distance_metric** (*VectorDistanceMetric*)\n  * **initial_cap** (*int* *|* *None*)\n  * **index_missing** (*bool*)\n  * **graph_max_degree** (*int*)\n  * **construction_window_size** (*int*)\n  * **search_window_size** (*int*)\n  * **epsilon** (*float*)\n  * **compression** (*CompressionType* *|* *None*)\n  * **reduce** (*int* *|* *None*)\n  * **training_threshold** (*int* *|* *None*)\n\n#### `validate_svs_params()`\n\nValidate SVS-VAMANA specific constraints\n\n#### `algorithm: Literal[VectorIndexAlgorithm.SVS_VAMANA]`\n\nThe indexing algorithm for the vector field\n\n#### `compression: CompressionType | None`\n\nLVQ4, LVQ8, LeanVec4x8, LeanVec8x8\n\n* **Type:**\n  Vector compression\n\n#### `construction_window_size: int`\n\n1. - affects quality vs build time\n\n* **Type:**\n  Build-time candidates (default\n\n#### `epsilon: float`\n\n0.01)\n\n* **Type:**\n  Range query boundary factor (default\n\n#### `graph_max_degree: int`\n\n1. - affects recall vs memory\n\n* **Type:**\n  Max edges per node (default\n\n#### `model_config: ClassVar[ConfigDict] = {}`\n\nConfiguration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].\n\n#### `reduce: int | None`\n\nDimensionality reduction for LeanVec types (must be < dims)\n\n#### `search_window_size: int`\n\n1. - primary tuning parameter\n\n* **Type:**\n  Search candidates (default\n\n#### `training_threshold: int | None`\n\n10,240)\n\n* **Type:**\n  Min vectors before compression training (default\n\n**SVS-VAMANA Examples:**\n\n**Basic configuration (no compression):**\n\n[code example]\n\n**High-performance configuration with compression:**\n\n[code example]\n\n**Important Notes:**\n\n- **Requirements**: SVS-VAMANA requires Redis >= 8.2 with Redis Search >= 2.8.10.\n- **Datatype limitations**: SVS-VAMANA only supports float16 and float32 datatypes (not bfloat16 or float64).\n- **Compression compatibility**: The reduce parameter is only valid with LeanVec compression types (LeanVec4x8 or LeanVec8x8).\n- **Platform considerations**: Intel’s proprietary LVQ and LeanVec optimizations are not available in Redis Open Source. On non-Intel platforms and Redis Open Source, SVS-VAMANA with compression falls back to basic 8-bit scalar quantization.\n- **Performance tip**: Runtime parameters like `search_window_size`, `epsilon`, and `use_search_history` can be adjusted at query time without rebuilding the index. Start with defaults and tune `search_window_size` first for your speed vs accuracy requirements."},{"id":"flat-vector-fields","title":"`FLAT Vector Fields`","role":"content","text":"FLAT - Brute-force exact search. **Best for small datasets (<10K vectors) requiring 100% accuracy.**"},{"id":"when-to-use-flat--performance-details","title":"`When to use FLAT & Performance Details`","role":"content","text":"**Use FLAT when:**\n: - Small datasets (<100K vectors) where exact results are required\n  - Search accuracy is critical and approximate results are not acceptable\n  - Baseline comparisons when evaluating approximate algorithms\n  - Simple use cases where setup simplicity is more important than performance\n\n**Performance characteristics:**\n: - **Search accuracy**: 100% exact results (no approximation)\n  - **Search speed**: Linear time O(n) - slower as dataset grows\n  - **Memory usage**: Minimal overhead, stores vectors as-is\n  - **Build time**: Fastest index construction (no preprocessing)\n\n**Trade-offs vs other algorithms:**\n: - **vs HNSW**: Much slower search but exact results, faster index building\n  - **vs SVS-VAMANA**: Slower search and higher memory usage, but exact results"},{"id":"class-flatvectorfield-name-typefieldtypesvector-pathnone-attrs","title":"`class FlatVectorField(*, name, type=FieldTypes.VECTOR, path=None, attrs)`","role":"content","text":"Bases: `BaseField`\n\nVector field with FLAT (exact search) indexing for exact nearest neighbor search.\n\nCreate a new model by parsing and validating input data from keyword arguments.\n\nRaises [ValidationError][pydantic_core.ValidationError] if the input data cannot be\nvalidated to form a valid model.\n\nself is explicitly positional-only to allow self as a field name.\n\n* **Parameters:**\n  * **name** (*str*)\n  * **type** (*Literal* *[* *FieldTypes.VECTOR* *]*)\n  * **path** (*str* *|* *None*)\n  * **attrs** ([FlatVectorFieldAttributes](#flatvectorfieldattributes))\n\n#### `as_redis_field()`\n\nConvert schema field to Redis Field object\n\n* **Return type:**\n  *Field*\n\n#### `attrs: `[FlatVectorFieldAttributes](#flatvectorfieldattributes)\n\nSpecified field attributes\n\n#### `model_config: ClassVar[ConfigDict] = {}`\n\nConfiguration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].\n\n#### `type: Literal[FieldTypes.VECTOR]`\n\nField type"},{"id":"class-flatvectorfieldattributes-dims-algorithmvectorindexalgorithmflat-datatypevectordatatypefloat32-distance_metricvectordistancemetriccosine-initial_capnone-index_missingfalse-block_sizenone","title":"`class FlatVectorFieldAttributes(*, dims, algorithm=VectorIndexAlgorithm.FLAT, datatype=VectorDataType.FLOAT32, distance_metric=VectorDistanceMetric.COSINE, initial_cap=None, index_missing=False, block_size=None)`","role":"content","text":"FLAT vector field attributes for exact nearest neighbor search.\n\nCreate a new model by parsing and validating input data from keyword arguments.\n\nRaises [ValidationError][pydantic_core.ValidationError] if the input data cannot be\nvalidated to form a valid model.\n\nself is explicitly positional-only to allow self as a field name.\n\n* **Parameters:**\n  * **dims** (*int*)\n  * **algorithm** (*Literal* *[* *VectorIndexAlgorithm.FLAT* *]*)\n  * **datatype** (*VectorDataType*)\n  * **distance_metric** (*VectorDistanceMetric*)\n  * **initial_cap** (*int* *|* *None*)\n  * **index_missing** (*bool*)\n  * **block_size** (*int* *|* *None*)\n\n#### `algorithm: Literal[VectorIndexAlgorithm.FLAT]`\n\nThe indexing algorithm (fixed as ‘flat’)\n\n#### `block_size: int | None`\n\nBlock size for processing (optional) - improves batch operation throughput\n\n#### `model_config: ClassVar[ConfigDict] = {}`\n\nConfiguration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].\n\n**FLAT Example:**\n\n[code example]\n\n**Note**: FLAT is recommended for small datasets or when exact results are mandatory. For larger datasets, consider HNSW or SVS-VAMANA for better performance."},{"id":"svs-vamana-configuration-utilities","title":"SVS-VAMANA Configuration Utilities","role":"content","text":"For SVS-VAMANA indices, RedisVL provides utilities to help configure compression settings and estimate memory savings."},{"id":"compressionadvisor","title":"`CompressionAdvisor`","role":"content","text":""},{"id":"class-compressionadvisor","title":"`class CompressionAdvisor`","role":"content","text":"Bases: `object`\n\nHelper to recommend compression settings based on vector characteristics.\n\nThis class provides utilities to:\n- Recommend optimal SVS-VAMANA configurations based on vector dimensions and priorities\n- Estimate memory savings from compression and dimensionality reduction"},{"id":"examples","title":"`Examples`","role":"content","text":"[code example]\n\n[code example]\n\n#### `static estimate_memory_savings(compression, dims, reduce=None)`\n\nEstimate memory savings percentage from compression.\n\nCalculates the percentage of memory saved compared to uncompressed float32 vectors.\n\n* **Parameters:**\n  * **compression** (*str*) – Compression type (e.g., \"LVQ4\", \"LeanVec4x8\")\n  * **dims** (*int*) – Original vector dimensionality\n  * **reduce** (*int* *|* *None*) – Reduced dimensionality (for LeanVec compression)\n* **Returns:**\n  Memory savings percentage (0-100)\n* **Return type:**\n  float"},{"id":"examples-1","title":"`Examples`","role":"content","text":"[code example]\n\n[code example]\n\n#### `static recommend(dims, priority='balanced', datatype=None)`\n\nRecommend compression settings based on dimensions and priorities.\n\n* **Parameters:**\n  * **dims** (*int*) – Vector dimensionality (must be > 0)\n  * **priority** (*Literal* *[* *'speed'* *,*  *'memory'* *,*  *'balanced'* *]*) – Optimization priority:\n    - \"memory\": Maximize memory savings\n    - \"speed\": Optimize for query speed\n    - \"balanced\": Balance between memory and speed\n  * **datatype** (*str* *|* *None*) – Override datatype (default: float16 for high-dim, float32 for low-dim)\n* **Returns:**\n  Complete SVS-VAMANA configuration including:\n  : - algorithm: \"svs-vamana\"\n    - datatype: Recommended datatype\n    - compression: Compression type\n    - reduce: Dimensionality reduction (for LeanVec only)\n    - graph_max_degree: Graph connectivity\n    - construction_window_size: Build-time candidates\n    - search_window_size: Query-time candidates\n* **Return type:**\n  dict\n* **Raises:**\n  **ValueError** – If dims <= 0"},{"id":"examples-2","title":"`Examples`","role":"content","text":"[code example]\n\n[code example]"},{"id":"svsconfig","title":"`SVSConfig`","role":"content","text":""},{"id":"class-svsconfig-algorithmsvs-vamana-datatypenone-compressionnone-reducenone-graph_max_degreenone-construction_window_sizenone-search_window_sizenone","title":"`class SVSConfig(*, algorithm='svs-vamana', datatype=None, compression=None, reduce=None, graph_max_degree=None, construction_window_size=None, search_window_size=None)`","role":"content","text":"Bases: `BaseModel`\n\nSVS-VAMANA configuration model.\n\n* **Parameters:**\n  * **algorithm** (*Literal* *[* *'svs-vamana'* *]*)\n  * **datatype** (*str* *|* *None*)\n  * **compression** (*str* *|* *None*)\n  * **reduce** (*int* *|* *None*)\n  * **graph_max_degree** (*int* *|* *None*)\n  * **construction_window_size** (*int* *|* *None*)\n  * **search_window_size** (*int* *|* *None*)\n\n#### `algorithm`\n\nAlways \"svs-vamana\"\n\n* **Type:**\n  Literal[‘svs-vamana’]\n\n#### `datatype`\n\nVector datatype (float16, float32)\n\n* **Type:**\n  str | None\n\n#### `compression`\n\nCompression type (LVQ4, LeanVec4x8, etc.)\n\n* **Type:**\n  str | None\n\n#### `reduce`\n\nReduced dimensionality (only for LeanVec)\n\n* **Type:**\n  int | None\n\n#### `graph_max_degree`\n\nMax edges per node\n\n* **Type:**\n  int | None\n\n#### `construction_window_size`\n\nBuild-time candidates\n\n* **Type:**\n  int | None\n\n#### `search_window_size`\n\nQuery-time candidates\n\n* **Type:**\n  int | None\n\nCreate a new model by parsing and validating input data from keyword arguments.\n\nRaises [ValidationError][pydantic_core.ValidationError] if the input data cannot be\nvalidated to form a valid model.\n\nself is explicitly positional-only to allow self as a field name.\n\n#### `model_config: ClassVar[ConfigDict] = {}`\n\nConfiguration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].\n\n<a id=\"vector-algorithm-comparison\"></a>"},{"id":"vector-algorithm-comparison","title":"Vector Algorithm Comparison","role":"content","text":"This section provides detailed guidance for choosing between vector search algorithms."},{"id":"algorithm-selection-guide","title":"`Algorithm Selection Guide`","role":"content","text":"#### `Vector Algorithm Comparison`\n\n| Algorithm      | Best For                               | Performance                    | Memory Usage              | Trade-offs                            |\n|----------------|----------------------------------------|--------------------------------|---------------------------|---------------------------------------|\n| **FLAT**       | Small datasets (<100K vectors)         | 100% recall, O(n) search       | Minimal overhead          | Exact but slow for large data         |\n| **HNSW**       | General purpose (100K-1M+ vectors)     | 95-99% recall, O(log n) search | Moderate (graph overhead) | Fast approximate search               |\n| **SVS-VAMANA** | Large datasets with memory constraints | 90-95% recall, O(log n) search | Low (with compression)    | Intel-optimized, requires newer Redis |"},{"id":"when-to-use-each-algorithm","title":"`When to Use Each Algorithm`","role":"content","text":"**Choose FLAT when:**\n: - Dataset size < 100,000 vectors\n  - Exact results are mandatory\n  - Simple setup is preferred\n  - Query latency is not critical\n\n**Choose HNSW when:**\n: - Dataset size 100K - 1M+ vectors\n  - Need balanced speed and accuracy\n  - Cross-platform compatibility required\n  - Most common choice for production\n\n**Choose SVS-VAMANA when:**\n: - Dataset size > 100K vectors\n  - Memory usage is a primary concern\n  - Running on Intel hardware\n  - Can accept 90-95% recall for memory savings"},{"id":"performance-characteristics","title":"`Performance Characteristics`","role":"content","text":"**Search Speed:**\n: - FLAT: Linear time O(n) - gets slower as data grows\n  - HNSW: Logarithmic time O(log n) - scales well\n  - SVS-VAMANA: Logarithmic time O(log n) - scales well\n\n**Memory Usage (1M vectors, 768 dims, float32):**\n: - FLAT: ~3.1 GB (baseline)\n  - HNSW: ~3.7 GB (20% overhead for graph)\n  - SVS-VAMANA: 1.6-3.1 GB (depends on compression)\n\n**Recall Quality:**\n: - FLAT: 100% (exact search)\n  - HNSW: 95-99% (tunable via `ef_runtime` at query time)\n  - SVS-VAMANA: 90-95% (tunable via `search_window_size` at query time, also depends on compression)"},{"id":"migration-considerations","title":"`Migration Considerations`","role":"content","text":"**From FLAT to HNSW:**\n: - Straightforward migration\n  - Expect slight recall reduction but major speed improvement\n  - Tune `ef_runtime` at query time to balance speed vs accuracy (no index rebuild needed)\n\n**From HNSW to SVS-VAMANA:**\n: - Requires Redis >= 8.2 with Redis Search >= 2.8.10\n  - Change datatype to float16 or float32 if using others\n  - Consider compression options for memory savings\n\n**From SVS-VAMANA to others:**\n: - May need to change datatype back if using float64/bfloat16\n  - HNSW provides similar performance with broader compatibility\n\nFor complete Redis field documentation, see: [https://redis.io/commands/ft.create/](https://redis.io/commands/ft.create/)"}],"examples":[{"id":"class-indexschema-index-fieldsfactory-version010-ex0","language":"yaml","code":"version: '0.1.0'\n\nindex:\n    name: user-index\n    prefix: user\n    key_separator: \":\"\n    storage_type: json\n\nfields:\n    - name: user\n      type: tag\n    - name: credit_score\n      type: tag\n    - name: embedding\n      type: vector\n      attrs:\n        algorithm: flat\n        dims: 3\n        distance_metric: cosine\n        datatype: float32","section_id":"class-indexschema-index-fieldsfactory-version010"},{"id":"class-indexschema-index-fieldsfactory-version010-ex1","language":"python","code":"from redisvl.schema import IndexSchema\n\nschema = IndexSchema.from_yaml(\"schema.yaml\")","section_id":"class-indexschema-index-fieldsfactory-version010"},{"id":"class-indexschema-index-fieldsfactory-version010-ex2","language":"python","code":"from redisvl.schema import IndexSchema\n\nschema = IndexSchema.from_dict({\n    \"index\": {\n        \"name\": \"user-index\",\n        \"prefix\": \"user\",\n        \"key_separator\": \":\",\n        \"storage_type\": \"json\",\n    },\n    \"fields\": [\n        {\"name\": \"user\", \"type\": \"tag\"},\n        {\"name\": \"credit_score\", \"type\": \"tag\"},\n        {\n            \"name\": \"embedding\",\n            \"type\": \"vector\",\n            \"attrs\": {\n                \"algorithm\": \"flat\",\n                \"dims\": 3,\n                \"distance_metric\": \"cosine\",\n                \"datatype\": \"float32\"\n            }\n        }\n    ]\n})","section_id":"class-indexschema-index-fieldsfactory-version010"},{"id":"class-indexschema-index-fieldsfactory-version010-ex3","language":"python","code":"# Add a tag field\nschema.add_field({\"name\": \"user\", \"type\": \"tag\"})\n\n# Add a vector field\nschema.add_field({\n    \"name\": \"user-embedding\",\n    \"type\": \"vector\",\n    \"attrs\": {\n        \"dims\": 1024,\n        \"algorithm\": \"flat\",\n        \"datatype\": \"float32\"\n    }\n})","section_id":"class-indexschema-index-fieldsfactory-version010"},{"id":"class-indexschema-index-fieldsfactory-version010-ex4","language":"python","code":"schema.add_fields([\n    {\"name\": \"user\", \"type\": \"tag\"},\n    {\"name\": \"bio\", \"type\": \"text\"},\n    {\n        \"name\": \"user-embedding\",\n        \"type\": \"vector\",\n        \"attrs\": {\n            \"dims\": 1024,\n            \"algorithm\": \"flat\",\n            \"datatype\": \"float32\"\n        }\n    }\n])","section_id":"class-indexschema-index-fieldsfactory-version010"},{"id":"class-indexschema-index-fieldsfactory-version010-ex5","language":"python","code":"from redisvl.schema import IndexSchema\n\nschema = IndexSchema.from_dict({\n    \"index\": {\n        \"name\": \"docs-index\",\n        \"prefix\": \"docs\",\n        \"storage_type\": \"hash\",\n    },\n    \"fields\": [\n        {\n            \"name\": \"doc-id\",\n            \"type\": \"tag\"\n        },\n        {\n            \"name\": \"doc-embedding\",\n            \"type\": \"vector\",\n            \"attrs\": {\n                \"algorithm\": \"flat\",\n                \"dims\": 1536\n            }\n        }\n    ]\n})","section_id":"class-indexschema-index-fieldsfactory-version010"},{"id":"class-indexschema-index-fieldsfactory-version010-ex6","language":"python","code":"from redisvl.schema import IndexSchema\nschema = IndexSchema.from_yaml(\"schema.yaml\")","section_id":"class-indexschema-index-fieldsfactory-version010"},{"id":"index-level-stopwords-configuration-ex0","language":"python","code":"from redisvl.schema import IndexSchema\n\n# Disable stopwords to search for phrases like \"Bank of Glasberliner\"\nschema = IndexSchema.from_dict({\n    \"index\": {\n        \"name\": \"company-idx\",\n        \"prefix\": \"company\",\n        \"stopwords\": []  # STOPWORDS 0\n    },\n    \"fields\": [\n        {\"name\": \"name\", \"type\": \"text\"}\n    ]\n})","section_id":"index-level-stopwords-configuration"},{"id":"defining-fields-ex0","language":"yaml","code":"- name: title\n  type: text\n  path: $.document.title\n  attrs:\n    weight: 1.0\n    no_stem: false\n    withsuffixtrie: true","section_id":"defining-fields"},{"id":"defining-fields-ex1","language":"python","code":"{\n    \"name\": \"location\",\n    \"type\": \"geo\",\n    \"attrs\": {\n        \"sortable\": true\n    }\n}","section_id":"defining-fields"},{"id":"class-hnswvectorfieldattributes-dims-algorithmvectorindexalgorithmhnsw-datatypevectordatatypefloat32-distance_metricvectordistancemetriccosine-initial_capnone-index_missingfalse-m16-ef_construction200-ef_runtime10-epsilon001-ex0","language":"yaml","code":"- name: embedding\n  type: vector\n  attrs:\n    algorithm: hnsw\n    dims: 768\n    distance_metric: cosine\n    datatype: float32\n    # Index-time parameters (set during index creation)\n    m: 16                    # Graph connectivity\n    ef_construction: 200     # Build-time accuracy\n    # Note: ef_runtime can be set at query time via VectorQuery","section_id":"class-hnswvectorfieldattributes-dims-algorithmvectorindexalgorithmhnsw-datatypevectordatatypefloat32-distance_metricvectordistancemetriccosine-initial_capnone-index_missingfalse-m16-ef_construction200-ef_runtime10-epsilon001"},{"id":"class-hnswvectorfieldattributes-dims-algorithmvectorindexalgorithmhnsw-datatypevectordatatypefloat32-distance_metricvectordistancemetriccosine-initial_capnone-index_missingfalse-m16-ef_construction200-ef_runtime10-epsilon001-ex1","language":"yaml","code":"- name: embedding\n  type: vector\n  attrs:\n    algorithm: hnsw\n    dims: 768\n    distance_metric: cosine\n    datatype: float32\n    # Index-time parameters tuned for maximum accuracy\n    m: 32\n    ef_construction: 400\n    # Note: ef_runtime=50 can be set at query time for higher recall","section_id":"class-hnswvectorfieldattributes-dims-algorithmvectorindexalgorithmhnsw-datatypevectordatatypefloat32-distance_metricvectordistancemetriccosine-initial_capnone-index_missingfalse-m16-ef_construction200-ef_runtime10-epsilon001"},{"id":"class-svsvectorfieldattributes-dims-algorithmvectorindexalgorithmsvs_vamana-datatypevectordatatypefloat32-distance_metricvectordistancemetriccosine-initial_capnone-index_missingfalse-graph_max_degree40-construction_window_size250-search_window_size20-epsilon001-compressionnone-reducenone-training_thresholdnone-ex0","language":"yaml","code":"- name: embedding\n  type: vector\n  attrs:\n    algorithm: svs-vamana\n    dims: 768\n    distance_metric: cosine\n    datatype: float32\n    # Index-time parameters (set during index creation)\n    graph_max_degree: 40\n    construction_window_size: 250\n    # Note: search_window_size and other runtime params can be set at query time","section_id":"class-svsvectorfieldattributes-dims-algorithmvectorindexalgorithmsvs_vamana-datatypevectordatatypefloat32-distance_metricvectordistancemetriccosine-initial_capnone-index_missingfalse-graph_max_degree40-construction_window_size250-search_window_size20-epsilon001-compressionnone-reducenone-training_thresholdnone"},{"id":"class-svsvectorfieldattributes-dims-algorithmvectorindexalgorithmsvs_vamana-datatypevectordatatypefloat32-distance_metricvectordistancemetriccosine-initial_capnone-index_missingfalse-graph_max_degree40-construction_window_size250-search_window_size20-epsilon001-compressionnone-reducenone-training_thresholdnone-ex1","language":"yaml","code":"- name: embedding\n  type: vector\n  attrs:\n    algorithm: svs-vamana\n    dims: 768\n    distance_metric: cosine\n    datatype: float32\n    # Index-time parameters tuned for better recall\n    graph_max_degree: 64\n    construction_window_size: 500\n    # Maximum compression with dimensionality reduction\n    compression: LeanVec4x8\n    reduce: 384  # 50% dimensionality reduction\n    training_threshold: 1000\n    # Note: search_window_size=40 can be set at query time for higher recall","section_id":"class-svsvectorfieldattributes-dims-algorithmvectorindexalgorithmsvs_vamana-datatypevectordatatypefloat32-distance_metricvectordistancemetriccosine-initial_capnone-index_missingfalse-graph_max_degree40-construction_window_size250-search_window_size20-epsilon001-compressionnone-reducenone-training_thresholdnone"},{"id":"class-flatvectorfieldattributes-dims-algorithmvectorindexalgorithmflat-datatypevectordatatypefloat32-distance_metricvectordistancemetriccosine-initial_capnone-index_missingfalse-block_sizenone-ex0","language":"yaml","code":"- name: embedding\n  type: vector\n  attrs:\n    algorithm: flat\n    dims: 768\n    distance_metric: cosine\n    datatype: float32\n    # Optional: tune for batch processing\n    block_size: 1024","section_id":"class-flatvectorfieldattributes-dims-algorithmvectorindexalgorithmflat-datatypevectordatatypefloat32-distance_metricvectordistancemetriccosine-initial_capnone-index_missingfalse-block_sizenone"},{"id":"examples-ex0","language":"pycon","code":">> # Get recommendations for high-dimensional vectors\n>> config = CompressionAdvisor.recommend(dims=1536, priority=\"balanced\")\n>> config.compression\n'LeanVec4x8'\n>> config.reduce\n768","section_id":"examples"},{"id":"examples-ex1","language":"pycon","code":">> # Estimate memory savings\n>> savings = CompressionAdvisor.estimate_memory_savings(\n...     compression=\"LeanVec4x8\",\n...     dims=1536,\n...     reduce=768\n... )\n>> savings\n81.2","section_id":"examples"},{"id":"examples-1-ex0","language":"pycon","code":">> # LeanVec with dimensionality reduction\n>> CompressionAdvisor.estimate_memory_savings(\n...     compression=\"LeanVec4x8\",\n...     dims=1536,\n...     reduce=768\n... )\n81.2","section_id":"examples-1"},{"id":"examples-1-ex1","language":"pycon","code":">> # LVQ without dimensionality reduction\n>> CompressionAdvisor.estimate_memory_savings(\n...     compression=\"LVQ4\",\n...     dims=384\n... )\n87.5","section_id":"examples-1"},{"id":"examples-2-ex0","language":"pycon","code":">> # High-dimensional embeddings (e.g., OpenAI ada-002)\n>> config = CompressionAdvisor.recommend(dims=1536, priority=\"memory\")\n>> config.compression\n'LeanVec4x8'\n>> config.reduce\n768","section_id":"examples-2"},{"id":"examples-2-ex1","language":"pycon","code":">> # Lower-dimensional embeddings\n>> config = CompressionAdvisor.recommend(dims=384, priority=\"speed\")\n>> config.compression\n'LVQ4x8'","section_id":"examples-2"}]}
{"schema_version":2,"id":"develop/ai/redisvl/api/searchindex","title":"Search Index Classes","url":"https://redis.io/docs/latest/develop/ai/redisvl/api/searchindex/","summary":"","aliases":["/integrate/redisvl/api/searchindex"],"tags":[],"last_updated":"2026-10-12T09:34:21+02:00","page_type":"content","content_hash":"040134935faa826d0d5fbddd980ddd96e97595d1bedb740e0015186d1f0980e5","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Class                                     | Description                                                                                  |\n|-------------------------------------------|----------------------------------------------------------------------------------------------|\n| [SearchIndex](#searchindex-api)           | Primary class to write, read, and search across data structures in Redis.                    |\n| [AsyncSearchIndex](#asyncsearchindex-api) | Async version of the SearchIndex to write, read, and search across data structures in Redis. |\n| [SearchResults](#searchresults-api)       | List of result documents returned by a query, which also reports result completeness.        |\n\n<a id=\"searchindex-api\"></a>"},{"id":"searchindex","title":"SearchIndex","role":"content","text":""},{"id":"class-searchindexschema-redis_clientnone-redis_urlnone-connection_kwargsnone-validate_on_loadfalse-owns_clientnone-kwargs","title":"`class SearchIndex(schema, redis_client=None, redis_url=None, connection_kwargs=None, validate_on_load=False, owns_client=None, **kwargs)`","role":"content","text":"A search index class for interacting with Redis as a vector database.\n\nThe SearchIndex is instantiated with a reference to a Redis database and an\nIndexSchema (YAML path or dictionary object) that describes the various\nsettings and field configurations.\n\n[code example]\n\nPass `redis_client` to use a client you configured yourself. The index\nleaves such a client open when it is disconnected or garbage collected;\npass `owns_client=True` to hand that responsibility over.\n\n[code example]\n\nInitialize the RedisVL search index with a schema and either a Redis\nclient or a URL string with other connection kwargs.\n\n* **Parameters:**\n  * **schema** ([*IndexSchema*](https://redis.io/docs/latest/schema/#indexschema)) – Index schema object.\n  * **redis_client** (*Optional* *[* *Redis* *]*) – An\n    instantiated redis client.\n  * **redis_url** (*Optional* *[* *str* *]*) – The URL of the Redis server to\n    connect to.\n  * **connection_kwargs** (*Dict* *[* *str* *,* *Any* *]* *,* *optional*) – Redis client connection\n    args.\n  * **validate_on_load** (*bool* *,* *optional*) – Whether to validate data against schema\n    when loading. Defaults to False.\n  * **owns_client** (*Optional* *[* *bool* *]* *,* *optional*) – Whether the index closes\n    the Redis client when the index is disconnected or garbage\n    collected. Defaults to None, meaning the index owns a client\n    only if it created one itself. Pass True to hand over a client\n    you created, or False to keep one the index would otherwise\n    close, in which case closing it becomes your responsibility.\n* **Raises:**\n  * **ValueError** – If `schema` is not an IndexSchema.\n  * **TypeError** – If `redis_client` is the wrong flavour, or if a\n        keyword that no longer exists is passed. `connection_args`\n        and `redis_kwargs` are both now `connection_kwargs`.\n\n#### `aggregate(*args, **kwargs)`\n\nPerform an aggregation operation against the index.\n\nWrapper around the aggregation API that adds the index name\nto the query and passes along the rest of the arguments\nto the redis-py ft().aggregate() method.\n\n* **Returns:**\n  Raw Redis aggregation results.\n* **Return type:**\n  Result\n\n#### `batch_query(queries, batch_size=10)`\n\nExecute a batch of queries and process results.\n\n* **Parameters:**\n  * **queries** (*Sequence* *[* *BaseQuery* *]*)\n  * **batch_size** (*int*)\n* **Return type:**\n  list[list[dict[str, *Any*]]]\n\n#### `batch_search(queries, batch_size=10)`\n\nPerform a search against the index for multiple queries.\n\nThis method takes a list of queries and optionally query params and\nreturns a list of Result objects for each query. Results are\nreturned in the same order as the queries.\n\nNOTE: Cluster users may need to incorporate hash tags into their query\nto avoid cross-slot operations.\n\n* **Parameters:**\n  * **queries** (*List* *[* *SearchParams* *]*) – The queries to search for.\n  * **batch_size** (*int* *,* *optional*) – The number of queries to search for at a time.\n    Defaults to 10.\n* **Returns:**\n  The search results for each query.\n* **Return type:**\n  List[Result]\n\n#### `clear()`\n\nClear all keys in Redis associated with the index, leaving the index\navailable and in-place for future insertions or updates.\n\nNOTE: This method requires custom behavior for Redis Cluster because\nhere, we can’t easily give control of the keys we’re clearing to the\nuser so they can separate them based on hash tag.\n\n> [!NOTE]\n> The sweep enumerates through the index, so it removes only what the\n> index currently returns, and it can stop early – against an index\n> still being backfilled, or when a page’s keys cannot be deleted. The\n> returned count is the only signal, and `0` does not distinguish an\n> empty index from a sweep that deleted nothing. Re-running is safe.\n\n* **Returns:**\n  Count of records deleted from Redis.\n* **Return type:**\n  int\n\n#### `create(overwrite=False, drop=False)`\n\nCreate an index in Redis with the current schema and properties.\n\n* **Parameters:**\n  * **overwrite** (*bool* *,* *optional*) – Whether to overwrite the index if it\n    already exists. Defaults to False.\n  * **drop** (*bool* *,* *optional*) – Whether to drop all keys associated with the\n    index in the case of overwriting. Defaults to False.\n* **Raises:**\n  * **RuntimeError** – If the index already exists and ‘overwrite’ is False.\n  * **ValueError** – If no fields are defined for the index.\n* **Return type:**\n  None\n\n[code example]\n\n#### `delete(drop=True)`\n\nDelete the search index while optionally dropping all keys associated\nwith the index.\n\n`FT.DROPINDEX` resolves index aliases, so if this schema’s name happens\nto be an alias, the index the alias points at is dropped instead – along\nwith its documents when `drop` is True. [exists](#exists) reports an alias\nas absent to keep [create](#create) away from this path.\n\n* **Parameters:**\n  **drop** (*bool* *,* *optional*) – Delete the key / documents pairs in the\n  index. Defaults to True.\n* **Raises:**\n  [**RedisSearchError**](https://redis.io/docs/latest/exceptions/#redissearcherror) – If the index cannot be deleted, for example when it\n      does not exist. The original `redis-py` exception is available\n      as `__cause__`.\n\n#### `disconnect()`\n\nClose the Redis client if this index owns it.\n\nAlways invalidates the cached SQL schema. When the index does not own\nthe client (see `owns_client`), the client is left open and the\nindex remains usable.\n\n#### `drop_by_filter(filter_expression, *, batch_size=500, dry_run=False, allow_all=False, on_progress=None)`\n\nDelete every document matching a filter expression.\n\nRedis has no server-side \"delete by query\", so RedisVL resolves the\nmatching document keys and removes them with non-blocking `UNLINK` in\nbatches. Matching documents leave the result set as they are deleted, so\nthis re-queries from offset 0 each round (the same strategy as\n[clear](#clear)) and is not subject to the `MAXSEARCHRESULTS` limit\n(provided `batch_size` itself does not exceed it).\n\n* **Parameters:**\n  * **filter_expression** (*Union* *[* *str* *,* [*FilterExpression*](https://redis.io/docs/latest/filter/#filterexpression) *]*) – Selects the\n    documents to delete. Prefer the escaping builders (`Tag`,\n    `Num`, `Text`…) over raw filter strings; **never\n    string-concatenate untrusted input into a filter** — unlike a\n    read query, an injected predicate here deletes data.\n  * **batch_size** (*int*) – Number of documents to resolve and unlink per\n    round-trip. Defaults to 500.\n  * **dry_run** (*bool*) – If True, report how many documents *would* be\n    deleted (via a count query) without deleting anything.\n  * **allow_all** (*bool*) – Must be True to run against a match-all filter\n    (empty/`\"*\"`/`None`). Prefer [clear](#clear) to intentionally\n    empty the index.\n  * **on_progress** (*Optional* *[* *Callable* *[* *[* *int* *,* *int* *]* *,* *None* *]* *]*) – Called after each\n    batch with `(processed, matched)` — the cumulative documents\n    deleted and the total matched at the start. Invoked\n    synchronously (do not pass a coroutine); raising from it aborts\n    the run (already-deleted documents stay deleted).\n* **Returns:**\n  `matched`/`processed` counts, plus `completed`\n  (False if the runaway backstop tripped) and `dry_run`. Read the\n  fields explicitly (`result.processed`, `result.completed`).\n* **Return type:**\n  BulkResult\n\n> [!NOTE]\n> [update_by_filter](#update_by_filter) (bulk partial update), [drop_documents](#drop_documents)\n> / [drop_keys](#drop_keys) (delete by id/key), [clear](#clear) (delete all).\n\n> [!NOTE]\n> This operation is **not atomic** across the match set. Each key is\n> unlinked atomically, but batches are applied incrementally with no\n> rollback, so a crash or connection error mid-run leaves the already\n> -deleted documents gone and the rest in place. Deletes are\n> idempotent: re-running the same call after a failure removes only\n> whatever still matches, converging on the intended state.\n\n#### `drop_documents(ids, batch_size=500)`\n\nRemove documents from the index by their document IDs.\n\nThis method converts document IDs to Redis keys automatically by applying\nthe index’s key prefix and separator configuration.\n\nNOTE: Cluster users will need to incorporate hash tags into their\ndocument IDs and only call this method with documents from a single hash\ntag at a time.\n\n* **Parameters:**\n  * **ids** (*Union* *[* *str* *,* *List* *[* *str* *]* *]*) – The document ID or IDs to remove from the index.\n  * **batch_size** (*int*) – Number of documents to delete per round-trip\n    (standalone Redis only; cluster deletes in a single call after\n    the shared-hash-tag check).\n* **Returns:**\n  Count of documents deleted from Redis.\n* **Return type:**\n  int\n* **Raises:**\n  **ValueError** – On Redis Cluster, if the resolved keys do not all share\n      a hash tag (a cross-slot `DELETE` is not permitted).\n\n#### `drop_keys(keys, batch_size=500)`\n\nRemove a specific entry or entries from the index by it’s key ID.\n\nUses `UNLINK` rather than `DEL` so memory reclamation runs on a\nbackground thread. This avoids blocking the main thread when a large\nnumber of keys are dropped at once (for example, scope-targeted\n`SemanticCache` invalidation). The returned count is unchanged.\n\nLarge key lists are unlinked in chunks of `batch_size`. On Redis\nCluster, keys are unlinked individually so a chunk that spans hash\nslots does not raise `CROSSSLOT`.\n\n* **Parameters:**\n  * **keys** (*Union* *[* *str* *,* *List* *[* *str* *]* *]*) – The document ID or IDs to remove from the index.\n  * **batch_size** (*int*) – Number of keys to unlink per round-trip.\n* **Returns:**\n  Count of records deleted from Redis.\n* **Return type:**\n  int\n\n#### `exists()`\n\nCheck if the index exists in Redis.\n\n* **Returns:**\n  True if the index exists, False otherwise.\n* **Return type:**\n  bool\n* **Raises:**\n  [**RedisSearchError**](https://redis.io/docs/latest/exceptions/#redissearcherror) – If the check fails for any reason other than the\n      index not existing, such as a connection failure or insufficient\n      permissions. The original `redis-py` exception is available as\n      `__cause__`.\n\n#### `expire_keys(keys, ttl)`\n\nSet the expiration time for a specific entry or entries in Redis.\n\n* **Parameters:**\n  * **keys** (*Union* *[* *str* *,* *List* *[* *str* *]* *]*) – The entry ID or IDs to set the expiration for.\n  * **ttl** (*int*) – The time-to-live in seconds.\n* **Return type:**\n  int | list[int]\n\n#### `fetch(id)`\n\nFetch an object from Redis by id.\n\nThe id is typically either a unique identifier,\nor derived from some domain-specific metadata combination\n(like a document id or chunk id).\n\n* **Parameters:**\n  **id** (*str*) – The specified unique identifier for a particular\n  document indexed in Redis.\n* **Returns:**\n  The fetched object.\n* **Return type:**\n  Dict[str, Any]\n\n#### `classmethod from_dict(schema_dict, **kwargs)`\n\nCreate a SearchIndex from a dictionary.\n\n* **Parameters:**\n  **schema_dict** (*Dict* *[* *str* *,* *Any* *]*) – A dictionary containing the schema.\n* **Returns:**\n  A RedisVL SearchIndex object.\n* **Return type:**\n  [SearchIndex](#searchindex)\n\n[code example]\n\n#### `classmethod from_existing(name, redis_client=None, redis_url=None, **kwargs)`\n\nInitialize from an existing search index in Redis by index name.\n\n* **Parameters:**\n  * **name** (*str*) – Name of the search index in Redis.\n  * **redis_client** (*Optional* *[* *Redis* *]*) – An\n    instantiated redis client.\n  * **redis_url** (*Optional* *[* *str* *]*) – The URL of the Redis server to\n    connect to.\n  * **owns_client** (*Optional* *[* *bool* *]* *,* *optional*) – Whether the index closes\n    the client. Defaults to True when this method created the\n    client from redis_url, and False when you supplied one.\n  * **connection_kwargs** (*Optional* *[* *Dict* *[* *str* *,* *Any* *]* *]*) – Redis client\n    connection args, used only when this method creates the\n    client from `redis_url`. Other keyword arguments are\n    treated the same way.\n* **Raises:**\n  **ValueError** – If redis_url or redis_client is not provided.\n\n#### `classmethod from_yaml(schema_path, **kwargs)`\n\nCreate a SearchIndex from a YAML schema file.\n\n* **Parameters:**\n  **schema_path** (*str*) – Path to the YAML schema file.\n* **Returns:**\n  A RedisVL SearchIndex object.\n* **Return type:**\n  [SearchIndex](#searchindex)\n\n[code example]\n\n#### `info(name=None)`\n\nGet information about the index.\n\n* **Parameters:**\n  **name** (*str* *,* *optional*) – Index name to fetch info about.\n  Defaults to None.\n* **Returns:**\n  A dictionary containing the information about the index.\n* **Return type:**\n  dict\n\n#### `invalidate_sql_schema_cache()`\n\nClear cached sql-redis executors and schema state for this index.\n\n* **Return type:**\n  None\n\n#### `key(id)`\n\nConstruct a redis key as a combination of an index key prefix (optional)\nand specified id.\n\nThe id is typically either a unique identifier, or\nderived from some domain-specific metadata combination (like a document\nid or chunk id).\n\n* **Parameters:**\n  **id** (*str*) – The specified unique identifier for a particular\n  document indexed in Redis.\n* **Returns:**\n  The full Redis key including key prefix and value as a string.\n* **Return type:**\n  str\n\n#### `listall()`\n\nList all search indices in Redis database.\n\n* **Returns:**\n  The list of indices in the database.\n* **Return type:**\n  List[str]\n\n#### `load(data, id_field=None, keys=None, ttl=None, preprocess=None, batch_size=None)`\n\nLoad objects to the Redis database. Returns the list of keys loaded\nto Redis.\n\nRedisVL automatically handles constructing the object keys, batching,\noptional preprocessing steps, and setting optional expiration\n(TTL policies) on keys.\n\n* **Parameters:**\n  * **data** (*Iterable* *[* *Any* *]*) – An iterable of objects to store.\n  * **id_field** (*Optional* *[* *str* *]* *,* *optional*) – Specified field used as the id\n    portion of the redis key (after the prefix) for each\n    object. Defaults to None.\n  * **keys** (*Optional* *[* *Iterable* *[* *str* *]* *]* *,* *optional*) – Optional iterable of keys.\n    Must match the length of objects if provided. Defaults to None.\n  * **ttl** (*Optional* *[* *int* *]* *,* *optional*) – Time-to-live in seconds for each key.\n    Defaults to None.\n  * **preprocess** (*Optional* *[* *Callable* *]* *,* *optional*) – A function to preprocess\n    objects before storage. Defaults to None.\n  * **batch_size** (*Optional* *[* *int* *]* *,* *optional*) – Number of objects to write in\n    a single Redis pipeline execution. Defaults to class’s\n    default batch size.\n* **Returns:**\n  List of keys loaded to Redis.\n* **Return type:**\n  List[str]\n* **Raises:**\n  * [**SchemaValidationError**](https://redis.io/docs/latest/exceptions/#schemavalidationerror) – If validation fails when validate_on_load is enabled.\n  * [**RedisVLError**](https://redis.io/docs/latest/exceptions/#redisvlerror) – If there’s an error loading data to Redis.\n\n#### `paginate(query, page_size=30)`\n\nExecute a given query against the index and return results in\npaginated batches.\n\nThis method accepts a RedisVL query instance, enabling pagination of\nresults which allows for subsequent processing over each batch with a\ngenerator.\n\n* **Parameters:**\n  * **query** (*BaseQuery*) – The search query to be executed.\n  * **page_size** (*int* *,* *optional*) – The number of results to return in each\n    batch. Defaults to 30.\n* **Yields:**\n  A generator yielding non-empty `SearchResults` batches.\n* **Raises:**\n  * **TypeError** – If the page_size argument is not of type int.\n  * **ValueError** – If the page_size argument is less than or equal to zero.\n* **Return type:**\n  *Generator*\n\n[code example]\n\n> [!NOTE]\n> The page_size parameter controls the number of items each result\n> batch contains. Adjust this value based on performance\n> considerations and the expected volume of search results.\n\n> [!NOTE]\n> For stable pagination, the query must have a sort_by clause on a\n> **unique** field. Redis documents that `LIMIT` without sorting is\n> non-deterministic, so pages may otherwise repeat or miss documents.\n> Very deep pagination is also bounded server-side by\n> `search-max-search-results` (1,000,000 by default, but 10,000 on\n> some managed tiers), past which the search errors rather than ending.\n\n> [!NOTE]\n> A yielded batch may contain fewer than `page_size` documents, and an\n> empty batch is never yielded. On Redis 8+ a matched document whose\n> data expires while the search is running is skipped; each batch\n> reports how many were skipped via `dropped_count` (and\n> `complete`), including skips inherited from a page that was dropped\n> in its entirety. Pagination still runs to the end of the result set in\n> that case.\n\n#### `query(query)`\n\nExecute a query on the index.\n\nThis method takes a BaseQuery, AggregationQuery, or HybridQuery object directly, and\nhandles post-processing of the search.\n\n* **Parameters:**\n  **query** (*Union* *[* *BaseQuery* *,* *AggregationQuery* *,* [*HybridQuery*](https://redis.io/docs/latest/query/#hybridquery) *]*) – The query to run.\n* **Returns:**\n  A list of search results.\n* **Return type:**\n  List[Result]\n\n[code example]\n\n#### `search(*args, **kwargs)`\n\nPerform a search against the index.\n\nWrapper around the search API that adds the index name\nto the query and passes along the rest of the arguments\nto the redis-py ft().search() method.\n\n* **Returns:**\n  Raw Redis search results.\n* **Return type:**\n  Result\n\n#### `update_by_filter(filter_expression, values, *, batch_size=500, dry_run=False, allow_all=False, on_progress=None)`\n\nSet `values` on every document matching a filter expression.\n\nThis is a partial update: fields not present in `values` are left\nuntouched. For hash indexes the fields are written with `HSET`; for\nJSON indexes they are merged at the document root (`$`) with\n`JSON.MERGE` (RFC 7396), so nested objects merge recursively, arrays\nare replaced wholesale, and a `None` value deletes that path.\n\nThe read phase (an `FT.AGGREGATE` cursor) is drained in full *before*\nany write, holding every matching key in memory in between. Interleaving\nthe two does not terminate: RediSearch has no in-place update, so each\nwrite appends a new, higher document id ahead of the cursor, which hands\nthat document back to be written again. Memory use is therefore\nproportional to the match count; for very large match sets, narrow the\nfilter and run in partitions (see \"Bulk Delete and Update\" in the\nsearch-and-indexing concepts guide).\n\n* **Parameters:**\n  * **filter_expression** (*Union* *[* *str* *,* [*FilterExpression*](https://redis.io/docs/latest/filter/#filterexpression) *]*) – Selects the\n    documents to update. Prefer the escaping builders (`Tag`,\n    `Num`…) over raw filter strings; **never string-concatenate\n    untrusted input into a filter** — an injected predicate here\n    mutates data.\n  * **values** (*Dict* *[* *str* *,* *Any* *]*) – Field/value pairs to set on each match.\n    Values are written **as-is with no schema validation** (unlike\n    [load](#load)): callers must pre-encode vectors/bytes and format\n    numerics as the schema expects. For JSON indexes, keys must\n    match the document’s JSON **layout**, not the schema field name\n    — a field indexed at a nested path (e.g. `$.metadata.status`)\n    must be passed nested (`{\"metadata\": {\"status\": ...}}`);\n    passing the flat field name writes the wrong path and leaves the\n    indexed field unchanged. Only static values are supported (no\n    callable/expression transforms).\n  * **batch_size** (*int*) – Number of documents to update per round-trip.\n  * **dry_run** (*bool*) – If True, report how many documents *would* be\n    updated without writing anything.\n  * **allow_all** (*bool*) – Must be True to run against a match-all filter.\n  * **on_progress** (*Optional* *[* *Callable* *[* *[* *int* *,* *int* *]* *,* *None* *]* *]*) – Called after each\n    write batch with `(processed, matched)`. Invoked synchronously\n    (do not pass a coroutine); raising from it aborts the run.\n* **Returns:**\n  `matched`/`processed` counts, plus `completed`\n  (always True for update — it runs to completion or raises) and\n  `dry_run`.\n* **Return type:**\n  BulkResult\n\n> [!NOTE]\n> [drop_by_filter](#drop_by_filter), [load](#load) (validated whole-document\n> upsert by key).\n\n> [!NOTE]\n> This operation is **not atomic** across the match set. Each\n> document is updated atomically (one `HSET`/`JSON.MERGE`), but\n> batches use a non-transactional pipeline and are applied\n> incrementally with no rollback, so a crash or connection error\n> mid-run can leave some documents updated and others not. Because\n> the update is a fixed field set, it is idempotent: re-running the\n> same call after a failure converges on the intended state.\n\nKeys are resolved before writing, so a document may be deleted by\nanother client in between. Each write is conditional on the key\nstill existing (applied atomically), so such a document is\n**skipped rather than recreated** as a partial document; it simply\nisn’t counted in `processed`. `processed` therefore reflects the\ndocuments actually written, which can be less than `matched` under\nconcurrent deletion.\n\n`processed` counts **distinct** documents — a document is written\n*at most* once, even though the resolving cursor can emit it on more\nthan one page — but there is no *at least* once guarantee. It can\nexceed `matched` if the match set grew after the initial count, and\nfall short of it either from a concurrent deletion or because the\ncursor itself returned fewer rows than matched (which Redis\ndocuments as possible). A short `processed` is therefore not\nevidence of any particular cause.\n\n#### `property client: Redis | RedisCluster | None`\n\nThe underlying redis-py client object.\n\n#### `property key_separator: str`\n\nThe optional separator between a defined prefix and key value in\nforming a Redis key.\n\n#### `property name: str`\n\nThe name of the Redis search index.\n\n#### `property prefix: str`\n\nThe key prefix used in forming Redis keys.\n\nFor multi-prefix indexes, returns the first prefix.\n\n#### `property prefixes: list[str]`\n\nAll key prefixes configured for this index.\n\n#### `property storage_type: StorageType`\n\nThe underlying storage type for the search index; either\nhash or json.\n\n<a id=\"asyncsearchindex-api\"></a>"},{"id":"asyncsearchindex","title":"AsyncSearchIndex","role":"content","text":""},{"id":"class-asyncsearchindexschema--redis_urlnone-redis_clientnone-connection_kwargsnone-validate_on_loadfalse-owns_clientnone-kwargs","title":"`class AsyncSearchIndex(schema, *, redis_url=None, redis_client=None, connection_kwargs=None, validate_on_load=False, owns_client=None, **kwargs)`","role":"content","text":"A search index class for interacting with Redis as a vector database in\nasync-mode.\n\nThe AsyncSearchIndex is instantiated with a reference to a Redis database\nand an IndexSchema (YAML path or dictionary object) that describes the\nvarious settings and field configurations.\n\n[code example]\n\nPass `redis_client` to use a client you configured yourself. The index\nleaves such a client open when it is disconnected or garbage collected;\npass `owns_client=True` to hand that responsibility over.\n\n[code example]\n\nInitialize the RedisVL async search index with a schema.\n\n* **Parameters:**\n  * **schema** ([*IndexSchema*](https://redis.io/docs/latest/schema/#indexschema)) – Index schema object.\n  * **redis_url** (*Optional* *[* *str* *]* *,* *optional*) – The URL of the Redis server to\n    connect to.\n  * **redis_client** (*Optional* *[* *AsyncRedis* *]*) – An\n    instantiated redis client.\n  * **connection_kwargs** (*Optional* *[* *Dict* *[* *str* *,* *Any* *]* *]*) – Redis client connection\n    args.\n  * **validate_on_load** (*bool* *,* *optional*) – Whether to validate data against schema\n    when loading. Defaults to False.\n  * **owns_client** (*Optional* *[* *bool* *]* *,* *optional*) – Whether the index closes\n    the Redis client when the index is disconnected or garbage\n    collected. Defaults to None, meaning the index owns a client\n    only if it created one itself. Pass True to hand over a client\n    you created, or False to keep one the index would otherwise\n    close, in which case closing it becomes your responsibility.\n* **Raises:**\n  * **ValueError** – If `schema` is not an IndexSchema.\n  * **TypeError** – If `redis_client` is the wrong flavour, or if a\n        keyword that no longer exists is passed. `connection_args`\n        and `redis_kwargs` are both now `connection_kwargs`.\n\n#### `async aggregate(*args, **kwargs)`\n\nPerform an aggregation operation against the index.\n\nWrapper around the aggregation API that adds the index name\nto the query and passes along the rest of the arguments\nto the redis-py ft().aggregate() method.\n\n* **Returns:**\n  Raw Redis aggregation results.\n* **Return type:**\n  Result\n\n#### `async batch_query(queries, batch_size=10)`\n\nAsynchronously execute a batch of queries and process results.\n\n* **Parameters:**\n  * **queries** (*list* *[* *BaseQuery* *]*)\n  * **batch_size** (*int*)\n* **Return type:**\n  list[list[dict[str, *Any*]]]\n\n#### `async batch_search(queries, batch_size=10)`\n\nAsynchronously execute a batch of search queries.\n\nThis method takes a list of search queries and executes them in batches\nto improve performance when dealing with multiple queries.\n\nNOTE: Cluster users may need to incorporate hash tags into their query\nto avoid cross-slot operations.\n\n* **Parameters:**\n  * **queries** (*List* *[* *SearchParams* *]*) – A list of search queries to execute.\n    Each query can be either a string or a tuple of (query, params).\n  * **batch_size** (*int* *,* *optional*) – The number of queries to execute in each\n    batch. Defaults to 10.\n* **Returns:**\n  A list of search results corresponding to each query.\n* **Return type:**\n  List[Result]\n\n[code example]\n\n#### `async clear()`\n\nClear all keys in Redis associated with the index, leaving the index\navailable and in-place for future insertions or updates.\n\nNOTE: This method requires custom behavior for Redis Cluster because here,\nwe can’t easily give control of the keys we’re clearing to the user so they\ncan separate them based on hash tag.\n\nSee [clear](#clear) for the sweep’s caveats, which apply\nidentically here.\n\n* **Returns:**\n  Count of records deleted from Redis.\n* **Return type:**\n  int\n\n#### `async create(overwrite=False, drop=False)`\n\nAsynchronously create an index in Redis with the current schema\n: and properties.\n\n* **Parameters:**\n  * **overwrite** (*bool* *,* *optional*) – Whether to overwrite the index if it\n    already exists. Defaults to False.\n  * **drop** (*bool* *,* *optional*) – Whether to drop all keys associated with the\n    index in the case of overwriting. Defaults to False.\n* **Raises:**\n  * **RuntimeError** – If the index already exists and ‘overwrite’ is False.\n  * **ValueError** – If no fields are defined for the index.\n* **Return type:**\n  None\n\n[code example]\n\n#### `async delete(drop=True)`\n\nDelete the search index.\n\n`FT.DROPINDEX` resolves index aliases, so if this schema’s name happens\nto be an alias, the index the alias points at is dropped instead – along\nwith its documents when `drop` is True. [exists](#exists) reports an alias\nas absent to keep [create](#create) away from this path.\n\n* **Parameters:**\n  **drop** (*bool* *,* *optional*) – Delete the documents in the index.\n  Defaults to True.\n* **Raises:**\n  [**RedisSearchError**](https://redis.io/docs/latest/exceptions/#redissearcherror) – If the index cannot be deleted, for example when it\n      does not exist. The original `redis-py` exception is available\n      as `__cause__`.\n\n#### `async disconnect()`\n\nClose the Redis client if this index owns it.\n\nAlways invalidates the cached SQL schema. When the index does not own\nthe client (see `owns_client`), the client is left open and the\nindex remains usable.\n\n#### `disconnect_sync()`\n\nClose an owned Redis client from synchronous code.\n\nFor callers outside an event loop, such as `__del__` or a shutdown\nhook. Honours `owns_client` exactly as [disconnect](#disconnect) does.\n\nDoes nothing if a loop is already running, since it cannot await the\nclose from inside one. Use [disconnect](#disconnect) there.\n\n#### `async drop_by_filter(filter_expression, *, batch_size=500, dry_run=False, allow_all=False, on_progress=None)`\n\nDelete every document matching a filter expression (async).\n\nSee [drop_by_filter](#drop_by_filter) for full semantics.\n\n* **Parameters:**\n  * **filter_expression** (*str* *|* [*FilterExpression*](https://redis.io/docs/latest/filter/#filterexpression))\n  * **batch_size** (*int*)\n  * **dry_run** (*bool*)\n  * **allow_all** (*bool*)\n  * **on_progress** (*Callable* *[* *[* *int* *,* *int* *]* *,* *None* *]*  *|* *None*)\n* **Return type:**\n  *BulkResult*\n\n#### `async drop_documents(ids, batch_size=500)`\n\nRemove documents from the index by their document IDs.\n\nThis method converts document IDs to Redis keys automatically by applying\nthe index’s key prefix and separator configuration.\n\nNOTE: Cluster users will need to incorporate hash tags into their\ndocument IDs and only call this method with documents from a single hash\ntag at a time.\n\n* **Parameters:**\n  * **ids** (*Union* *[* *str* *,* *List* *[* *str* *]* *]*) – The document ID or IDs to remove from the index.\n  * **batch_size** (*int*) – Number of documents to delete per round-trip\n    (standalone Redis only; cluster deletes in a single call after\n    the shared-hash-tag check).\n* **Returns:**\n  Count of documents deleted from Redis.\n* **Return type:**\n  int\n* **Raises:**\n  **ValueError** – On Redis Cluster, if the resolved keys do not all share\n      a hash tag (a cross-slot `DELETE` is not permitted).\n\n#### `async drop_keys(keys, batch_size=500)`\n\nRemove a specific entry or entries from the index by it’s key ID.\n\nUses `UNLINK` rather than `DEL` so memory reclamation runs on a\nbackground thread. This avoids blocking the main thread when a large\nnumber of keys are dropped at once (for example, scope-targeted\n`SemanticCache` invalidation). The returned count is unchanged.\n\nLarge key lists are unlinked in chunks of `batch_size`. On Redis\nCluster, keys are unlinked individually so a chunk that spans hash\nslots does not raise `CROSSSLOT`.\n\n* **Parameters:**\n  * **keys** (*Union* *[* *str* *,* *List* *[* *str* *]* *]*) – The document ID or IDs to remove from the index.\n  * **batch_size** (*int*) – Number of keys to unlink per round-trip.\n* **Returns:**\n  Count of records deleted from Redis.\n* **Return type:**\n  int\n\n#### `async exists()`\n\nCheck if the index exists in Redis.\n\n* **Returns:**\n  True if the index exists, False otherwise.\n* **Return type:**\n  bool\n* **Raises:**\n  [**RedisSearchError**](https://redis.io/docs/latest/exceptions/#redissearcherror) – If the check fails for any reason other than the\n      index not existing, such as a connection failure or insufficient\n      permissions. The original `redis-py` exception is available as\n      `__cause__`.\n\n#### `async expire_keys(keys, ttl)`\n\nSet the expiration time for a specific entry or entries in Redis.\n\n* **Parameters:**\n  * **keys** (*Union* *[* *str* *,* *List* *[* *str* *]* *]*) – The entry ID or IDs to set the expiration for.\n  * **ttl** (*int*) – The time-to-live in seconds.\n* **Return type:**\n  int | list[int]\n\n#### `async fetch(id)`\n\nAsynchronously etch an object from Redis by id. The id is typically\neither a unique identifier, or derived from some domain-specific\nmetadata combination (like a document id or chunk id).\n\n* **Parameters:**\n  **id** (*str*) – The specified unique identifier for a particular\n  document indexed in Redis.\n* **Returns:**\n  The fetched object.\n* **Return type:**\n  Dict[str, Any]\n\n#### `classmethod from_dict(schema_dict, **kwargs)`\n\nCreate a SearchIndex from a dictionary.\n\n* **Parameters:**\n  **schema_dict** (*Dict* *[* *str* *,* *Any* *]*) – A dictionary containing the schema.\n* **Returns:**\n  A RedisVL SearchIndex object.\n* **Return type:**\n  [SearchIndex](#searchindex)\n\n[code example]\n\n#### `async classmethod* from_existing(name, redis_client=None, redis_url=None, **kwargs)`\n\nInitialize from an existing search index in Redis by index name.\n\n* **Parameters:**\n  * **name** (*str*) – Name of the search index in Redis.\n  * **redis_client** (*Optional* *[* *Redis* *]*) – An\n    instantiated redis client.\n  * **redis_url** (*Optional* *[* *str* *]*) – The URL of the Redis server to\n    connect to.\n  * **owns_client** (*Optional* *[* *bool* *]* *,* *optional*) – Whether the index closes\n    the client. Defaults to True when this method created the\n    client from redis_url, and False when you supplied one.\n  * **connection_kwargs** (*Optional* *[* *Dict* *[* *str* *,* *Any* *]* *]*) – Redis client\n    connection args, used only when this method creates the\n    client from `redis_url`. Other keyword arguments are\n    treated the same way.\n\n#### `classmethod from_yaml(schema_path, **kwargs)`\n\nCreate a SearchIndex from a YAML schema file.\n\n* **Parameters:**\n  **schema_path** (*str*) – Path to the YAML schema file.\n* **Returns:**\n  A RedisVL SearchIndex object.\n* **Return type:**\n  [SearchIndex](#searchindex)\n\n[code example]\n\n#### `async info(name=None)`\n\nGet information about the index.\n\n* **Parameters:**\n  **name** (*str* *,* *optional*) – Index name to fetch info about.\n  Defaults to None.\n* **Returns:**\n  A dictionary containing the information about the index.\n* **Return type:**\n  dict\n\n#### `invalidate_sql_schema_cache()`\n\nClear cached sql-redis executors and schema state for this index.\n\n* **Return type:**\n  None\n\n#### `key(id)`\n\nConstruct a redis key as a combination of an index key prefix (optional)\nand specified id.\n\nThe id is typically either a unique identifier, or\nderived from some domain-specific metadata combination (like a document\nid or chunk id).\n\n* **Parameters:**\n  **id** (*str*) – The specified unique identifier for a particular\n  document indexed in Redis.\n* **Returns:**\n  The full Redis key including key prefix and value as a string.\n* **Return type:**\n  str\n\n#### `async listall()`\n\nList all search indices in Redis database.\n\n* **Returns:**\n  The list of indices in the database.\n* **Return type:**\n  List[str]\n\n#### `load(data, id_field=None, keys=None, ttl=None, preprocess=None, concurrency=None, batch_size=None)`\n\nAsynchronously load objects to Redis. Returns the list of keys loaded\nto Redis.\n\nRedisVL automatically handles constructing the object keys, batching,\noptional preprocessing steps, and setting optional expiration\n(TTL policies) on keys.\n\n* **Parameters:**\n  * **data** (*Iterable* *[* *Any* *]*) – An iterable of objects to store.\n  * **id_field** (*Optional* *[* *str* *]* *,* *optional*) – Specified field used as the id\n    portion of the redis key (after the prefix) for each\n    object. Defaults to None.\n  * **keys** (*Optional* *[* *Iterable* *[* *str* *]* *]* *,* *optional*) – Optional iterable of keys.\n    Must match the length of objects if provided. Defaults to None.\n  * **ttl** (*Optional* *[* *int* *]* *,* *optional*) – Time-to-live in seconds for each key.\n    Defaults to None.\n  * **preprocess** (*Optional* *[* *Callable* *]* *,* *optional*) – A function to\n    preprocess objects before storage. Defaults to None.\n  * **batch_size** (*Optional* *[* *int* *]* *,* *optional*) – Number of objects to write in\n    a single Redis pipeline execution. Defaults to class’s\n    default batch size.\n  * **concurrency** (*int* *|* *None*)\n* **Returns:**\n  List of keys loaded to Redis.\n* **Return type:**\n  List[str]\n* **Raises:**\n  * [**SchemaValidationError**](https://redis.io/docs/latest/exceptions/#schemavalidationerror) – If validation fails when validate_on_load is enabled.\n  * [**RedisVLError**](https://redis.io/docs/latest/exceptions/#redisvlerror) – If there’s an error loading data to Redis.\n\n[code example]\n\n#### `async paginate(query, page_size=30)`\n\nExecute a given query against the index and return results in\npaginated batches.\n\nThis method accepts a RedisVL query instance, enabling async pagination\nof results which allows for subsequent processing over each batch with a\ngenerator.\n\n* **Parameters:**\n  * **query** (*BaseQuery*) – The search query to be executed.\n  * **page_size** (*int* *,* *optional*) – The number of results to return in each\n    batch. Defaults to 30.\n* **Yields:**\n  An async generator yielding non-empty `SearchResults` batches.\n* **Raises:**\n  * **TypeError** – If the page_size argument is not of type int.\n  * **ValueError** – If the page_size argument is less than or equal to zero.\n* **Return type:**\n  *AsyncGenerator*\n\n[code example]\n\n> [!NOTE]\n> The page_size parameter controls the number of items each result\n> batch contains. Adjust this value based on performance\n> considerations and the expected volume of search results.\n\n> [!NOTE]\n> For stable pagination, the query must have a sort_by clause on a\n> **unique** field. Redis documents that `LIMIT` without sorting is\n> non-deterministic, so pages may otherwise repeat or miss documents.\n> Very deep pagination is also bounded server-side by\n> `search-max-search-results` (1,000,000 by default, but 10,000 on\n> some managed tiers), past which the search errors rather than ending.\n\n> [!NOTE]\n> A yielded batch may contain fewer than `page_size` documents, and an\n> empty batch is never yielded. On Redis 8+ a matched document whose\n> data expires while the search is running is skipped; each batch\n> reports how many were skipped via `dropped_count` (and\n> `complete`), including skips inherited from a page that was dropped\n> in its entirety. Pagination still runs to the end of the result set in\n> that case.\n\n#### `async query(query)`\n\nAsynchronously execute a query on the index.\n\nThis method takes a BaseQuery, AggregationQuery, HybridQuery, or SQLQuery object\ndirectly, runs the search, and handles post-processing of the search.\n\n* **Parameters:**\n  **query** (*Union* *[* *BaseQuery* *,* *AggregationQuery* *,* [*HybridQuery*](https://redis.io/docs/latest/query/#hybridquery) *,* [*SQLQuery*](https://redis.io/docs/latest/query/#sqlquery) *]*) – The query to run.\n* **Returns:**\n  A list of search results.\n* **Return type:**\n  List[Result]\n\n[code example]\n\n#### `async search(*args, **kwargs)`\n\nPerform an async search against the index.\n\nWrapper around the search API that adds the index name\nto the query and passes along the rest of the arguments\nto the redis-py ft().search() method.\n\n* **Returns:**\n  Raw Redis search results.\n* **Return type:**\n  Result\n\n#### `async update_by_filter(filter_expression, values, *, batch_size=500, dry_run=False, allow_all=False, on_progress=None)`\n\nSet `values` on every document matching a filter expression (async).\n\nSee [update_by_filter](#update_by_filter) for full semantics.\n\n* **Parameters:**\n  * **filter_expression** (*str* *|* [*FilterExpression*](https://redis.io/docs/latest/filter/#filterexpression))\n  * **values** (*dict* *[* *str* *,* *Any* *]*)\n  * **batch_size** (*int*)\n  * **dry_run** (*bool*)\n  * **allow_all** (*bool*)\n  * **on_progress** (*Callable* *[* *[* *int* *,* *int* *]* *,* *None* *]*  *|* *None*)\n* **Return type:**\n  *BulkResult*\n\n#### `property client: Redis | RedisCluster | None`\n\nThe underlying redis-py client object.\n\n#### `property key_separator: str`\n\nThe optional separator between a defined prefix and key value in\nforming a Redis key.\n\n#### `property name: str`\n\nThe name of the Redis search index.\n\n#### `property prefix: str`\n\nThe key prefix used in forming Redis keys.\n\nFor multi-prefix indexes, returns the first prefix.\n\n#### `property prefixes: list[str]`\n\nAll key prefixes configured for this index.\n\n#### `property storage_type: StorageType`\n\nThe underlying storage type for the search index; either\nhash or json.\n\n<a id=\"searchresults-api\"></a>"},{"id":"searchresults","title":"SearchResults","role":"content","text":""},{"id":"class-searchresultsiterable-dropped_count0","title":"`class SearchResults(iterable=(), dropped_count=0)`","role":"content","text":"A list of result documents that also reports result completeness.\n\nThis is a drop-in `list` — iteration, indexing, `len()`, and every other\nlist operation behave exactly as before, so existing callers need no\nchanges. It additionally carries:\n\n- `dropped_count`: the number of matched documents that were skipped\n  because their field payload came back missing (the Redis 8.8+\n  background-search TTL/expiry race); `0` in the normal case.\n- `complete`: `False` when `dropped_count > 0`, i.e. the result set may\n  be missing one or more matches that the server reported but could not\n  materialize.\n\nCallers that need completeness guarantees — audit/compliance queries, eval\nharnesses, or agents making control-flow decisions — can inspect these to\ndetect and react to a partial result (e.g. retry, reconcile against a\n`CountQuery`, or annotate a downstream answer as incomplete). Everyone else\ncan ignore them. Note that operations returning a new list (slicing,\n`sorted()`, concatenation) yield a plain `list` without this metadata.\n\n* **Parameters:**\n  * **iterable** (*Any*)\n  * **dropped_count** (*int*)\n\n#### `property complete: bool`\n\nTrue when no matched documents were dropped from this result set."}],"examples":[{"id":"class-searchindexschema-redis_clientnone-redis_urlnone-connection_kwargsnone-validate_on_loadfalse-owns_clientnone-kwargs-ex0","language":"python","code":"from redisvl.index import SearchIndex\n\n# initialize the index object with schema from file\nindex = SearchIndex.from_yaml(\n    \"schemas/schema.yaml\",\n    redis_url=\"redis://localhost:6379\",\n    validate_on_load=True\n)\n\n# create the index\nindex.create(overwrite=True, drop=False)\n\n# data is an iterable of dictionaries\nindex.load(data)\n\n# delete index and data\nindex.delete(drop=True)","section_id":"class-searchindexschema-redis_clientnone-redis_urlnone-connection_kwargsnone-validate_on_loadfalse-owns_clientnone-kwargs"},{"id":"class-searchindexschema-redis_clientnone-redis_urlnone-connection_kwargsnone-validate_on_loadfalse-owns_clientnone-kwargs-ex1","language":"python","code":"from redis import Redis\nfrom redisvl.index import SearchIndex\n\nclient = Redis.from_url(\"redis://localhost:6379\", socket_timeout=5)\nindex = SearchIndex.from_yaml(\"schemas/schema.yaml\", redis_client=client)","section_id":"class-searchindexschema-redis_clientnone-redis_urlnone-connection_kwargsnone-validate_on_loadfalse-owns_clientnone-kwargs"},{"id":"class-searchindexschema-redis_clientnone-redis_urlnone-connection_kwargsnone-validate_on_loadfalse-owns_clientnone-kwargs-ex2","language":"python","code":"# create an index in Redis; only if one does not exist with given name\nindex.create()\n\n# overwrite an index in Redis without dropping associated data\nindex.create(overwrite=True)\n\n# overwrite an index in Redis; drop associated data (clean slate)\nindex.create(overwrite=True, drop=True)","section_id":"class-searchindexschema-redis_clientnone-redis_urlnone-connection_kwargsnone-validate_on_loadfalse-owns_clientnone-kwargs"},{"id":"class-searchindexschema-redis_clientnone-redis_urlnone-connection_kwargsnone-validate_on_loadfalse-owns_clientnone-kwargs-ex3","language":"python","code":"from redisvl.index import SearchIndex\n\nindex = SearchIndex.from_dict({\n    \"index\": {\n        \"name\": \"my-index\",\n        \"prefix\": \"rvl\",\n        \"storage_type\": \"hash\",\n    },\n    \"fields\": [\n        {\"name\": \"doc-id\", \"type\": \"tag\"}\n    ]\n}, redis_url=\"redis://localhost:6379\")","section_id":"class-searchindexschema-redis_clientnone-redis_urlnone-connection_kwargsnone-validate_on_loadfalse-owns_clientnone-kwargs"},{"id":"class-searchindexschema-redis_clientnone-redis_urlnone-connection_kwargsnone-validate_on_loadfalse-owns_clientnone-kwargs-ex4","language":"python","code":"from redisvl.index import SearchIndex\n\nindex = SearchIndex.from_yaml(\"schemas/schema.yaml\", redis_url=\"redis://localhost:6379\")","section_id":"class-searchindexschema-redis_clientnone-redis_urlnone-connection_kwargsnone-validate_on_loadfalse-owns_clientnone-kwargs"},{"id":"class-searchindexschema-redis_clientnone-redis_urlnone-connection_kwargsnone-validate_on_loadfalse-owns_clientnone-kwargs-ex5","language":"python","code":"# Iterate over paginated search results in batches of 10\nfor result_batch in index.paginate(query, page_size=10):\n    # Process each batch of results\n    pass","section_id":"class-searchindexschema-redis_clientnone-redis_urlnone-connection_kwargsnone-validate_on_loadfalse-owns_clientnone-kwargs"},{"id":"class-searchindexschema-redis_clientnone-redis_urlnone-connection_kwargsnone-validate_on_loadfalse-owns_clientnone-kwargs-ex6","language":"python","code":"from redisvl.query import VectorQuery\n\nquery = VectorQuery(\n    vector=[0.16, -0.34, 0.98, 0.23],\n    vector_field_name=\"embedding\",\n    num_results=3\n)\n\nresults = index.query(query)","section_id":"class-searchindexschema-redis_clientnone-redis_urlnone-connection_kwargsnone-validate_on_loadfalse-owns_clientnone-kwargs"},{"id":"class-asyncsearchindexschema--redis_urlnone-redis_clientnone-connection_kwargsnone-validate_on_loadfalse-owns_clientnone-kwargs-ex0","language":"python","code":"from redisvl.index import AsyncSearchIndex\n\n# initialize the index object with schema from file\nindex = AsyncSearchIndex.from_yaml(\n    \"schemas/schema.yaml\",\n    redis_url=\"redis://localhost:6379\",\n    validate_on_load=True\n)\n\n# create the index\nawait index.create(overwrite=True, drop=False)\n\n# data is an iterable of dictionaries\nawait index.load(data)\n\n# delete index and data\nawait index.delete(drop=True)","section_id":"class-asyncsearchindexschema--redis_urlnone-redis_clientnone-connection_kwargsnone-validate_on_loadfalse-owns_clientnone-kwargs"},{"id":"class-asyncsearchindexschema--redis_urlnone-redis_clientnone-connection_kwargsnone-validate_on_loadfalse-owns_clientnone-kwargs-ex1","language":"python","code":"from redis.asyncio import Redis\nfrom redisvl.index import AsyncSearchIndex\n\nclient = Redis.from_url(\"redis://localhost:6379\", socket_timeout=5)\nindex = AsyncSearchIndex.from_yaml(\"schemas/schema.yaml\", redis_client=client)","section_id":"class-asyncsearchindexschema--redis_urlnone-redis_clientnone-connection_kwargsnone-validate_on_loadfalse-owns_clientnone-kwargs"},{"id":"class-asyncsearchindexschema--redis_urlnone-redis_clientnone-connection_kwargsnone-validate_on_loadfalse-owns_clientnone-kwargs-ex2","language":"python","code":"queries = [\n    \"hello world\",\n    (\"goodbye world\", {\"num_results\": 5}),\n]\n\nresults = await index.batch_search(queries)","section_id":"class-asyncsearchindexschema--redis_urlnone-redis_clientnone-connection_kwargsnone-validate_on_loadfalse-owns_clientnone-kwargs"},{"id":"class-asyncsearchindexschema--redis_urlnone-redis_clientnone-connection_kwargsnone-validate_on_loadfalse-owns_clientnone-kwargs-ex3","language":"python","code":"# create an index in Redis; only if one does not exist with given name\nawait index.create()\n\n# overwrite an index in Redis without dropping associated data\nawait index.create(overwrite=True)\n\n# overwrite an index in Redis; drop associated data (clean slate)\nawait index.create(overwrite=True, drop=True)","section_id":"class-asyncsearchindexschema--redis_urlnone-redis_clientnone-connection_kwargsnone-validate_on_loadfalse-owns_clientnone-kwargs"},{"id":"class-asyncsearchindexschema--redis_urlnone-redis_clientnone-connection_kwargsnone-validate_on_loadfalse-owns_clientnone-kwargs-ex4","language":"python","code":"from redisvl.index import SearchIndex\n\nindex = SearchIndex.from_dict({\n    \"index\": {\n        \"name\": \"my-index\",\n        \"prefix\": \"rvl\",\n        \"storage_type\": \"hash\",\n    },\n    \"fields\": [\n        {\"name\": \"doc-id\", \"type\": \"tag\"}\n    ]\n}, redis_url=\"redis://localhost:6379\")","section_id":"class-asyncsearchindexschema--redis_urlnone-redis_clientnone-connection_kwargsnone-validate_on_loadfalse-owns_clientnone-kwargs"},{"id":"class-asyncsearchindexschema--redis_urlnone-redis_clientnone-connection_kwargsnone-validate_on_loadfalse-owns_clientnone-kwargs-ex5","language":"python","code":"from redisvl.index import SearchIndex\n\nindex = SearchIndex.from_yaml(\"schemas/schema.yaml\", redis_url=\"redis://localhost:6379\")","section_id":"class-asyncsearchindexschema--redis_urlnone-redis_clientnone-connection_kwargsnone-validate_on_loadfalse-owns_clientnone-kwargs"},{"id":"class-asyncsearchindexschema--redis_urlnone-redis_clientnone-connection_kwargsnone-validate_on_loadfalse-owns_clientnone-kwargs-ex6","language":"python","code":"data = [{\"test\": \"foo\"}, {\"test\": \"bar\"}]\n\n# simple case\nkeys = await index.load(data)\n\n# set 360 second ttl policy on data\nkeys = await index.load(data, ttl=360)\n\n# load data with predefined keys\nkeys = await index.load(data, keys=[\"rvl:foo\", \"rvl:bar\"])\n\n# load data with preprocessing step\ndef add_field(d):\n    d[\"new_field\"] = 123\n    return d\nkeys = await index.load(data, preprocess=add_field)","section_id":"class-asyncsearchindexschema--redis_urlnone-redis_clientnone-connection_kwargsnone-validate_on_loadfalse-owns_clientnone-kwargs"},{"id":"class-asyncsearchindexschema--redis_urlnone-redis_clientnone-connection_kwargsnone-validate_on_loadfalse-owns_clientnone-kwargs-ex7","language":"python","code":"# Iterate over paginated search results in batches of 10\nasync for result_batch in index.paginate(query, page_size=10):\n    # Process each batch of results\n    pass","section_id":"class-asyncsearchindexschema--redis_urlnone-redis_clientnone-connection_kwargsnone-validate_on_loadfalse-owns_clientnone-kwargs"},{"id":"class-asyncsearchindexschema--redis_urlnone-redis_clientnone-connection_kwargsnone-validate_on_loadfalse-owns_clientnone-kwargs-ex8","language":"python","code":"from redisvl.query import VectorQuery\n\nquery = VectorQuery(\n    vector=[0.16, -0.34, 0.98, 0.23],\n    vector_field_name=\"embedding\",\n    num_results=3\n)\n\nresults = await index.query(query)","section_id":"class-asyncsearchindexschema--redis_urlnone-redis_clientnone-connection_kwargsnone-validate_on_loadfalse-owns_clientnone-kwargs"}]}
{"schema_version":2,"id":"develop/ai/redisvl/api/vector","title":"Vector","url":"https://redis.io/docs/latest/develop/ai/redisvl/api/vector/","summary":"","aliases":["/integrate/redisvl/api/vector"],"tags":[],"last_updated":"2026-04-22T11:55:45+02:00","page_type":"content","content_hash":"8b3912325c73debc784132e3658e63b41d7c5db9fe0d5f8516e0b0bcc4f76132","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The Vector class in RedisVL is a container that encapsulates a numerical vector, it’s datatype, corresponding index field name, and optional importance weight. It is used when constructing multi-vector queries using the MultiVectorQuery class."},{"id":"vector","title":"Vector","role":"content","text":""},{"id":"class-vector-vector-field_name-dtypefloat32-weight10-max_distance20","title":"`class Vector(*, vector, field_name, dtype='float32', weight=1.0, max_distance=2.0)`","role":"content","text":"Simple object containing the necessary arguments to perform a multi vector query.\n\nArgs:\nvector: The vector values as a list of floats or bytes\nfield_name: The name of the vector field to search\ndtype: The data type of the vector (default: \"float32\")\nweight: The weight for this vector in the combined score (default: 1.0)\nmax_distance: The maximum distance for vector range search (default: 2.0, range: [0.0, 2.0])\n\nCreate a new model by parsing and validating input data from keyword arguments.\n\nRaises [ValidationError][pydantic_core.ValidationError] if the input data cannot be\nvalidated to form a valid model.\n\nself is explicitly positional-only to allow self as a field name.\n\n* **Parameters:**\n  * **vector** (*list* *[* *float* *]*  *|* *bytes*)\n  * **field_name** (*str*)\n  * **dtype** (*str*)\n  * **weight** (*float*)\n  * **max_distance** (*float*)\n\n#### `validate_vector()`\n\nIf the vector passed in is an array of float convert it to a byte string.\n\n* **Return type:**\n  *Self*\n\n#### `model_config: ClassVar[ConfigDict] = {}`\n\nConfiguration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict]."}],"examples":[]}
{"schema_version":2,"id":"develop/ai/redisvl/api/vectorizer","title":"Vectorizers","url":"https://redis.io/docs/latest/develop/ai/redisvl/api/vectorizer/","summary":"","aliases":["/integrate/redisvl/api/vectorizer"],"tags":[],"last_updated":"2026-10-05T16:11:44+01:00","page_type":"content","content_hash":"8e93d8763021c6c918db164870124839591cbd51cd9663c26865793a8bc27c45","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> **Backwards Compatibility:** Several vectorizers have deprecated aliases\n> available in the `redisvl.utils.vectorize.text` module for backwards\n> compatibility:\n>\n> - `VoyageAITextVectorizer` → Use `VoyageAIVectorizer` instead\n> - `VertexAITextVectorizer` → Use `GoogleGenAIVectorizer` instead\n>   (`VertexAIVectorizer` is itself deprecated; see below)\n> - `BedrockTextVectorizer` → Use `BedrockVectorizer` instead\n> - `CustomTextVectorizer` → Use `CustomVectorizer` instead\n>\n> These aliases are deprecated as of version 0.13.0 and will be removed\n> in a future major release."},{"id":"hftextvectorizer","title":"HFTextVectorizer","role":"content","text":"<a id=\"hftextvectorizer-api\"></a>"},{"id":"class-hftextvectorizermodelsentence-transformersall-mpnet-base-v2-dtypefloat32-cachenone--dimsnone","title":"`class HFTextVectorizer(model='sentence-transformers/all-mpnet-base-v2', dtype='float32', cache=None, *, dims=None)`","role":"content","text":"Bases: `BaseVectorizer`\n\nThe HFTextVectorizer class leverages Hugging Face’s Sentence Transformers\nfor generating vector embeddings from text input.\n\nThis vectorizer is particularly useful in scenarios where advanced natural language\nprocessing and understanding are required, and ideal for running on your own\nhardware without usage fees.\n\nYou can optionally enable caching to improve performance when generating\nembeddings for repeated text inputs.\n\nUtilizing this vectorizer involves specifying a pre-trained model from\nHugging Face’s vast collection of Sentence Transformers. These models are\ntrained on a variety of datasets and tasks, ensuring versatility and\nrobust performance across different embedding needs.\n\n> [!NOTE]\n> Some multimodal models can make use of sentence-transformers by passing\n> PIL Image objects in place of strings (e.g. CLIP). To enable those use\n> cases, this class follows the SentenceTransformer convention of hinting\n> that it expects string inputs, but never enforcing it.\n\nRequirements:\n: - The sentence-transformers library must be installed with pip.\n\n[code example]\n\nInitialize the Hugging Face text vectorizer.\n\n* **Parameters:**\n  * **model** (*str*) – The pre-trained model from Hugging Face’s Sentence\n    Transformers to be used for embedding. Defaults to\n    ‘sentence-transformers/all-mpnet-base-v2’.\n  * **dtype** (*str*) – the default datatype to use when embedding text as byte arrays.\n    Used when setting as_buffer=True in calls to embed() and embed_many().\n    Defaults to ‘float32’.\n  * **cache** (*Optional* *[*[*EmbeddingsCache*](https://redis.io/docs/latest/cache/#embeddingscache) *]*) – Optional EmbeddingsCache instance to cache embeddings for\n    better performance with repeated texts. Defaults to None.\n  * **\\*\\*kwargs** – Additional parameters to pass to the SentenceTransformer\n    constructor.\n  * **dims** (*Annotated* *[* *int* *|* *None* *,* *FieldInfo* *(* *annotation=NoneType* *,* *required=True* *,* *metadata=* *[* *Strict* *(* *strict=True* *)* *,* *Gt* *(* *gt=0* *)* *]* *)* *]*)\n* **Raises:**\n  * **ImportError** – If the sentence-transformers library is not installed.\n  * **ValueError** – If there is an error setting the embedding model dimensions.\n  * **ValueError** – If an invalid dtype is provided.\n\n#### `model_post_init(context, /)`\n\nThis function is meant to behave like a BaseModel method to initialise private attributes.\n\nIt takes context as an argument since that’s what pydantic-core passes when calling it.\n\n* **Parameters:**\n  * **self** (*BaseModel*) – The BaseModel instance.\n  * **context** (*Any*) – The context.\n* **Return type:**\n  None\n\n#### `model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True}`\n\nConfiguration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].\n\n#### `property type: str`\n\nReturn the type of vectorizer."},{"id":"openaitextvectorizer","title":"OpenAITextVectorizer","role":"content","text":"<a id=\"openaitextvectorizer-api\"></a>"},{"id":"class-openaitextvectorizermodeltext-embedding-ada-002-api_confignone-dtypefloat32-cachenone--dimsnone","title":"`class OpenAITextVectorizer(model='text-embedding-ada-002', api_config=None, dtype='float32', cache=None, *, dims=None)`","role":"content","text":"Bases: `BaseVectorizer`\n\nThe OpenAITextVectorizer class utilizes OpenAI’s API to generate\nembeddings for text data.\n\nThis vectorizer is designed to interact with OpenAI’s embeddings API,\nrequiring an API key for authentication. The key can be provided directly\nin the api_config dictionary or through the OPENAI_API_KEY environment\nvariable. Users must obtain an API key from OpenAI’s website\n([https://api.openai.com/](https://api.openai.com/)). Additionally, the openai python client must be\ninstalled with pip install openai>=1.13.0.\n\nThe vectorizer supports both synchronous and asynchronous operations,\nallowing for batch processing of texts and flexibility in handling\npreprocessing tasks.\n\nYou can optionally enable caching to improve performance when generating\nembeddings for repeated text inputs.\n\n[code example]\n\nInitialize the OpenAI vectorizer.\n\n* **Parameters:**\n  * **model** (*str*) – Model to use for embedding. Defaults to\n    ‘text-embedding-ada-002’.\n  * **api_config** (*Optional* *[* *Dict* *]* *,* *optional*) – Dictionary containing the\n    API key and any additional OpenAI API options. Defaults to None.\n  * **dtype** (*str*) – the default datatype to use when embedding text as byte arrays.\n    Used when setting as_buffer=True in calls to embed() and embed_many().\n    Defaults to ‘float32’.\n  * **cache** (*Optional* *[*[*EmbeddingsCache*](https://redis.io/docs/latest/cache/#embeddingscache) *]*) – Optional EmbeddingsCache instance to cache embeddings for\n    better performance with repeated texts. Defaults to None.\n  * **dims** (*Annotated* *[* *int* *|* *None* *,* *FieldInfo* *(* *annotation=NoneType* *,* *required=True* *,* *metadata=* *[* *Strict* *(* *strict=True* *)* *,* *Gt* *(* *gt=0* *)* *]* *)* *]*)\n* **Raises:**\n  * **ImportError** – If the openai library is not installed.\n  * **ValueError** – If the OpenAI API key is not provided.\n  * **ValueError** – If an invalid dtype is provided.\n\n#### `model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True}`\n\nConfiguration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].\n\n#### `property type: str`\n\nReturn the type of vectorizer."},{"id":"azureopenaitextvectorizer","title":"AzureOpenAITextVectorizer","role":"content","text":"<a id=\"azureopenaitextvectorizer-api\"></a>"},{"id":"class-azureopenaitextvectorizermodeltext-embedding-ada-002-api_confignone-dtypefloat32-cachenone--dimsnone","title":"`class AzureOpenAITextVectorizer(model='text-embedding-ada-002', api_config=None, dtype='float32', cache=None, *, dims=None)`","role":"content","text":"Bases: `BaseVectorizer`\n\nThe AzureOpenAITextVectorizer class utilizes AzureOpenAI’s API to generate\nembeddings for text data.\n\nThis vectorizer is designed to interact with AzureOpenAI’s embeddings API,\nrequiring an API key, an AzureOpenAI deployment endpoint and API version.\nThese values can be provided directly in the api_config dictionary with\nthe parameters ‘azure_endpoint’, ‘api_version’ and ‘api_key’ or through the\nenvironment variables ‘AZURE_OPENAI_ENDPOINT’, ‘OPENAI_API_VERSION’, and ‘AZURE_OPENAI_API_KEY’.\nUsers must obtain these values from the ‘Keys and Endpoints’ section in their Azure OpenAI service.\nAdditionally, the openai python client must be installed with pip install openai>=1.13.0.\n\nThe vectorizer supports both synchronous and asynchronous operations,\nallowing for batch processing of texts and flexibility in handling\npreprocessing tasks.\n\nYou can optionally enable caching to improve performance when generating\nembeddings for repeated text inputs.\n\n[code example]\n\nInitialize the AzureOpenAI vectorizer.\n\n* **Parameters:**\n  * **model** (*str*) – Deployment to use for embedding. Must be the\n    ‘Deployment name’ not the ‘Model name’. Defaults to\n    ‘text-embedding-ada-002’.\n  * **api_config** (*Optional* *[* *Dict* *]* *,* *optional*) – Dictionary containing the\n    API key, API version, Azure endpoint, and any other API options.\n    Defaults to None.\n  * **dtype** (*str*) – the default datatype to use when embedding text as byte arrays.\n    Used when setting as_buffer=True in calls to embed() and embed_many().\n    Defaults to ‘float32’.\n  * **cache** (*Optional* *[*[*EmbeddingsCache*](https://redis.io/docs/latest/cache/#embeddingscache) *]*) – Optional EmbeddingsCache instance to cache embeddings for\n    better performance with repeated texts. Defaults to None.\n  * **dims** (*Annotated* *[* *int* *|* *None* *,* *FieldInfo* *(* *annotation=NoneType* *,* *required=True* *,* *metadata=* *[* *Strict* *(* *strict=True* *)* *,* *Gt* *(* *gt=0* *)* *]* *)* *]*)\n* **Raises:**\n  * **ImportError** – If the openai library is not installed.\n  * **ValueError** – If the AzureOpenAI API key, version, or endpoint are not provided.\n  * **ValueError** – If an invalid dtype is provided.\n\n#### `model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True}`\n\nConfiguration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].\n\n#### `property type: str`\n\nReturn the type of vectorizer."},{"id":"vertexaivectorizer","title":"VertexAIVectorizer","role":"content","text":"<a id=\"vertexaivectorizer-api\"></a>\n\n> [!NOTE]\n> `VertexAIVectorizer` is **deprecated**. It uses Google’s Vertex AI\n> model-garden SDK, which Google has deprecated with a scheduled removal. Use\n> [GoogleGenAIVectorizer](#googlegenaivectorizer) for text\n> embeddings on the supported `google-genai` SDK. The alias\n> `VertexAITextVectorizer` (in `redisvl.utils.vectorize.text`) is likewise\n> deprecated. Multimodal (image/video) migration is tracked in\n> [issue #620](https://github.com/redis/redis-vl-python/issues/620)."},{"id":"class-vertexaivectorizermodeltextembedding-gecko-api_confignone-dtypefloat32-cachenone--dimsnone","title":"`class VertexAIVectorizer(model='textembedding-gecko', api_config=None, dtype='float32', cache=None, *, dims=None)`","role":"content","text":"Bases: `BaseVectorizer`\n\nThe VertexAIVectorizer uses Google’s VertexAI embedding model\nAPI to create embeddings.\n\nThis vectorizer is tailored for use in\nenvironments where integration with Google Cloud Platform (GCP) services is\na key requirement.\n\nUtilizing this vectorizer requires an active GCP project and location\n(region), along with appropriate application credentials. These can be\nprovided through the api_config dictionary or set the GOOGLE_APPLICATION_CREDENTIALS\nenv var. Additionally, the vertexai python client must be\ninstalled with pip install google-cloud-aiplatform>=1.26.\n\nYou can optionally enable caching to improve performance when generating\nembeddings for repeated inputs.\n\n[code example]\n\nInitialize the VertexAI vectorizer.\n\n* **Parameters:**\n  * **model** (*str*) – Model to use for embedding. Defaults to\n    ‘textembedding-gecko’.\n  * **api_config** (*Optional* *[* *Dict* *]* *,* *optional*) – Dictionary containing the\n    API config details. Defaults to None.\n  * **dtype** (*str*) – the default datatype to use when embedding text as byte arrays.\n    Used when setting as_buffer=True in calls to embed() and embed_many().\n    Defaults to ‘float32’.\n  * **cache** (*Optional* *[*[*EmbeddingsCache*](https://redis.io/docs/latest/cache/#embeddingscache) *]*) – Optional EmbeddingsCache instance to cache embeddings for\n    better performance with repeated texts. Defaults to None.\n  * **dims** (*Annotated* *[* *int* *|* *None* *,* *FieldInfo* *(* *annotation=NoneType* *,* *required=True* *,* *metadata=* *[* *Strict* *(* *strict=True* *)* *,* *Gt* *(* *gt=0* *)* *]* *)* *]*)\n* **Raises:**\n  * **ImportError** – If the google-cloud-aiplatform library is not installed.\n  * **ValueError** – If the API key is not provided.\n  * **ValueError** – If an invalid dtype is provided.\n\n#### `embed_image(image_path, **kwargs)`\n\nEmbed an image (from its path on disk) using a VertexAI multimodal model.\n\n* **Parameters:**\n  **image_path** (*str*)\n* **Return type:**\n  list[float] | bytes\n\n#### `embed_video(video_path, **kwargs)`\n\nEmbed a video (from its path on disk) using a VertexAI multimodal model.\n\n* **Parameters:**\n  **video_path** (*str*)\n* **Return type:**\n  list[float] | bytes\n\n#### `property is_multimodal: bool`\n\nWhether a multimodal model has been configured.\n\n#### `model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True}`\n\nConfiguration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].\n\n#### `property type: str`\n\nReturn the type of vectorizer."},{"id":"googlegenaivectorizer","title":"GoogleGenAIVectorizer","role":"content","text":"<a id=\"googlegenaivectorizer-api\"></a>"},{"id":"class-googlegenaivectorizermodelgemini-embedding-001-api_confignone-dtypefloat32-cachenone-task_typenone-output_dimensionalitynone--dimsnone","title":"`class GoogleGenAIVectorizer(model='gemini-embedding-001', api_config=None, dtype='float32', cache=None, task_type=None, output_dimensionality=None, *, dims=None)`","role":"content","text":"Bases: `BaseVectorizer`\n\nThe GoogleGenAIVectorizer creates embeddings with Google’s google-genai\nSDK, the supported replacement for the deprecated Vertex AI model-garden SDK\nused by `VertexAIVectorizer`.\n\nOne client, two backends. google-genai reaches both of Google’s embedding\nbackends; this vectorizer auto-selects one from your config/environment (or an\nexplicit override):\n\n- **Vertex AI / Gemini Enterprise** (GCP project auth) — for existing Vertex users.\n- **Gemini Developer API** (a single API key) — the simplest way to get started.\n\nCredentials are resolved in this order (explicit beats ambient):\n\n1. Explicit override — `api_config={\"backend\": \"vertex\" | \"gemini\"}`.\n2. Explicit creds in `api_config` — `api_key` selects Gemini; `project_id`\n   (+ `location`) selects Vertex.\n3. Environment — a project (`GOOGLE_CLOUD_PROJECT` | `GCP_PROJECT_ID` with\n   `GOOGLE_CLOUD_LOCATION` | `GCP_LOCATION` and Application Default\n   Credentials via `GOOGLE_APPLICATION_CREDENTIALS`) selects Vertex; otherwise\n   `GEMINI_API_KEY` | `GOOGLE_API_KEY` selects Gemini. If both a project and a\n   key are present, Vertex wins.\n\nInstall the client with `pip install redisvl[google-genai]`.\n\n> [!NOTE]\n> The default model `gemini-embedding-001` returns **3072-dimensional**\n> vectors (≈4× the width of the legacy `textembedding-gecko`, so ≈4× the\n> index memory). A reduced `output_dimensionality` returns shorter vectors that\n> Google does **not** re-normalize; this is invisible under the COSINE metric\n> (scale-invariant) but matters for inner-product / L2 — normalize yourself if your\n> index needs it. Embeddings from different models (or dimensions) are not\n> interchangeable — reindex when you change them.\n\n[code example]\n\nInitialize the Google GenAI vectorizer.\n\n* **Parameters:**\n  * **model** (*str*) – The embedding model to use. Defaults to\n    ‘gemini-embedding-001’, which works on both backends.\n  * **api_config** (*Optional* *[* *Dict* *]*) – Auth/client configuration. Recognized keys:\n    `backend` (\"vertex\"|\"gemini\"), `project_id`, `location`,\n    `credentials` (Vertex), and `api_key` (Gemini). Model-behavior\n    options are the `task_type`/`output_dimensionality` arguments\n    below, not `api_config` keys. Defaults to None.\n  * **dtype** (*str*) – The default datatype to use when embedding text as byte\n    arrays. Used when setting `as_buffer=True`. Defaults to ‘float32’.\n  * **cache** (*Optional* *[*[*EmbeddingsCache*](https://redis.io/docs/latest/cache/#embeddingscache) *]*) – Optional cache for repeated inputs.\n  * **task_type** (*Optional* *[* *str* *]*) – Default embedding task type (e.g.\n    ‘RETRIEVAL_DOCUMENT’, ‘RETRIEVAL_QUERY’). Overridable per call.\n  * **output_dimensionality** (*Optional* *[* *int* *]*) – Request shorter (Matryoshka)\n    embeddings of this width; sets `dims` accordingly. Fixed for the\n    vectorizer’s lifetime (cannot be overridden per call). Note Google does\n    not re-normalize reduced vectors.\n  * **\\*\\*kwargs** – Additional arguments forwarded to `google.genai.Client`\n    (e.g. `http_options`).\n  * **dims** (*Annotated* *[* *int* *|* *None* *,* *FieldInfo* *(* *annotation=NoneType* *,* *required=True* *,* *metadata=* *[* *Strict* *(* *strict=True* *)* *,* *Gt* *(* *gt=0* *)* *]* *)* *]*)\n* **Raises:**\n  * **ImportError** – If the google-genai library is not installed.\n  * **ValueError** – If a backend cannot be resolved, or an invalid dtype is given.\n\n#### `property backend: str`\n\n‘vertex’ or ‘gemini’.\n\n* **Type:**\n  The resolved backend\n\n#### `model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True}`\n\nConfiguration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].\n\n#### `property type: str`\n\nReturn the type of vectorizer."},{"id":"coheretextvectorizer","title":"CohereTextVectorizer","role":"content","text":"<a id=\"coheretextvectorizer-api\"></a>"},{"id":"class-coheretextvectorizermodelembed-english-v30-api_confignone-dtypefloat32-cachenone--dimsnone","title":"`class CohereTextVectorizer(model='embed-english-v3.0', api_config=None, dtype='float32', cache=None, *, dims=None)`","role":"content","text":"Bases: `BaseVectorizer`\n\nThe CohereTextVectorizer class utilizes Cohere’s API to generate\nembeddings for text data.\n\nThis vectorizer is designed to interact with Cohere’s /embed API,\nrequiring an API key for authentication. The key can be provided\ndirectly in the api_config dictionary or through the COHERE_API_KEY\nenvironment variable. User must obtain an API key from Cohere’s website\n([https://dashboard.cohere.com/](https://dashboard.cohere.com/)). Additionally, the cohere python\nclient must be installed with pip install cohere.\n\nThe vectorizer supports only synchronous operations, allows for batch\nprocessing of texts and flexibility in handling preprocessing tasks.\n\nYou can optionally enable caching to improve performance when generating\nembeddings for repeated text inputs.\n\n[code example]\n\nInitialize the Cohere vectorizer.\n\nVisit [https://cohere.ai/embed](https://cohere.ai/embed) to learn about embeddings.\n\n* **Parameters:**\n  * **model** (*str*) – Model to use for embedding. Defaults to ‘embed-english-v3.0’.\n  * **api_config** (*Optional* *[* *Dict* *]* *,* *optional*) – Dictionary containing the API key.\n    Defaults to None.\n  * **dtype** (*str*) – the default datatype to use when embedding text as byte arrays.\n    Used when setting as_buffer=True in calls to embed() and embed_many().\n    ‘float32’ will use Cohere’s float embeddings, ‘int8’ and ‘uint8’ will map\n    to Cohere’s corresponding embedding types. Defaults to ‘float32’.\n  * **cache** (*Optional* *[*[*EmbeddingsCache*](https://redis.io/docs/latest/cache/#embeddingscache) *]*) – Optional EmbeddingsCache instance to cache embeddings for\n    better performance with repeated texts. Defaults to None.\n  * **dims** (*Annotated* *[* *int* *|* *None* *,* *FieldInfo* *(* *annotation=NoneType* *,* *required=True* *,* *metadata=* *[* *Strict* *(* *strict=True* *)* *,* *Gt* *(* *gt=0* *)* *]* *)* *]*)\n* **Raises:**\n  * **ImportError** – If the cohere library is not installed.\n  * **ValueError** – If the API key is not provided.\n  * **ValueError** – If an invalid dtype is provided.\n\n#### `model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True}`\n\nConfiguration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].\n\n#### `property type: str`\n\nReturn the type of vectorizer."},{"id":"bedrockvectorizer","title":"BedrockVectorizer","role":"content","text":"<a id=\"bedrockvectorizer-api\"></a>\n\n> [!NOTE]\n> For backwards compatibility, an alias `BedrockTextVectorizer` is available\n> in the `redisvl.utils.vectorize.text` module. This alias is deprecated\n> as of version 0.13.0 and will be removed in a future major release."},{"id":"class-bedrockvectorizermodelamazontitan-embed-text-v20-api_confignone-dtypefloat32-cachenone--dimsnone","title":"`class BedrockVectorizer(model='amazon.titan-embed-text-v2:0', api_config=None, dtype='float32', cache=None, *, dims=None)`","role":"content","text":"Bases: `BaseVectorizer`\n\nThe BedrockVectorizer class utilizes Amazon Bedrock’s API to generate\nembeddings for text or image data.\n\nThis vectorizer is designed to interact with Amazon Bedrock API,\nrequiring AWS credentials for authentication. The credentials can be provided\ndirectly in the api_config dictionary or through environment variables:\n- AWS_ACCESS_KEY_ID\n- AWS_SECRET_ACCESS_KEY\n- AWS_REGION (defaults to us-east-1)\n\nThe vectorizer supports synchronous operations with batch processing and\npreprocessing capabilities.\n\nYou can optionally enable caching to improve performance when generating\nembeddings for repeated inputs.\n\n[code example]\n\nInitialize the AWS Bedrock Vectorizer.\n\n* **Parameters:**\n  * **model** (*str*) – The Bedrock model ID to use. Defaults to amazon.titan-embed-text-v2:0\n  * **api_config** (*Optional* *[* *Dict* *[* *str* *,* *str* *]* *]*) – AWS credentials and config.\n    Can include: aws_access_key_id, aws_secret_access_key, aws_region\n    If not provided, will use environment variables.\n  * **dtype** (*str*) – the default datatype to use when embedding text as byte arrays.\n    Used when setting as_buffer=True in calls to embed() and embed_many().\n    Defaults to ‘float32’.\n  * **cache** (*Optional* *[*[*EmbeddingsCache*](https://redis.io/docs/latest/cache/#embeddingscache) *]*) – Optional EmbeddingsCache instance to cache embeddings for\n    better performance with repeated texts. Defaults to None.\n  * **dims** (*Annotated* *[* *int* *|* *None* *,* *FieldInfo* *(* *annotation=NoneType* *,* *required=True* *,* *metadata=* *[* *Strict* *(* *strict=True* *)* *,* *Gt* *(* *gt=0* *)* *]* *)* *]*)\n* **Raises:**\n  * **ValueError** – If credentials are not provided in config or environment.\n  * **ImportError** – If boto3 is not installed.\n  * **ValueError** – If an invalid dtype is provided.\n\n#### `embed_image(image_path, **kwargs)`\n\nEmbed an image (from its path on disk) using a Bedrock multimodal model.\n\n* **Parameters:**\n  **image_path** (*str*)\n* **Return type:**\n  list[float] | bytes\n\n#### `model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True}`\n\nConfiguration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].\n\n#### `property type: str`\n\nReturn the type of vectorizer."},{"id":"customvectorizer","title":"CustomVectorizer","role":"content","text":"<a id=\"customvectorizer-api\"></a>\n\n> [!NOTE]\n> For backwards compatibility, an alias `CustomTextVectorizer` is available\n> in the `redisvl.utils.vectorize.text` module. This alias is deprecated\n> as of version 0.13.0 and will be removed in a future major release."},{"id":"class-customvectorizerembed-embed_manynone-aembednone-aembed_manynone-dtypefloat32-cachenone","title":"`class CustomVectorizer(embed, embed_many=None, aembed=None, aembed_many=None, dtype='float32', cache=None)`","role":"content","text":"Bases: `BaseVectorizer`\n\nThe CustomVectorizer class wraps user-defined embedding methods to create\nembeddings for data.\n\nThis vectorizer is designed to accept a provided callable vectorizer and\nprovides a class definition to allow for compatibility with RedisVL.\nThe vectorizer may support both synchronous and asynchronous operations which\nallows for batch processing of inputs, but at a minimum only synchronous embedding\nis required to satisfy the ‘embed()’ method.\n\nYou can optionally enable caching to improve performance when generating\nembeddings for repeated inputs.\n\n[code example]\n\nInitialize the Custom vectorizer.\n\n* **Parameters:**\n  * **embed** (*Callable*) – a Callable function that accepts an object and returns a list of floats.\n  * **embed_many** (*Optional* *[* *Callable* *]*) – a Callable function that accepts a list of objects and returns a list containing lists of floats. Defaults to None.\n  * **aembed** (*Optional* *[* *Callable* *]*) – an asynchronous Callable function that accepts a object and returns a lists of floats. Defaults to None.\n  * **aembed_many** (*Optional* *[* *Callable* *]*) – an asynchronous Callable function that accepts a list of objects and returns a list containing lists of floats. Defaults to None.\n  * **dtype** (*str*) – the default datatype to use when embedding inputs as byte arrays.\n    Used when setting as_buffer=True in calls to embed() and embed_many().\n    Defaults to ‘float32’.\n  * **cache** (*Optional* *[*[*EmbeddingsCache*](https://redis.io/docs/latest/cache/#embeddingscache) *]*) – Optional EmbeddingsCache instance to cache embeddings for\n    better performance with repeated inputs. Defaults to None.\n* **Raises:**\n  **ValueError** – if embedding validation fails.\n\n#### `model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True}`\n\nConfiguration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].\n\n#### `property type: str`\n\nReturn the type of vectorizer."},{"id":"voyageaivectorizer","title":"VoyageAIVectorizer","role":"content","text":"<a id=\"voyageaivectorizer-api\"></a>\n\n> [!NOTE]\n> For backwards compatibility, an alias `VoyageAITextVectorizer` is available\n> in the `redisvl.utils.vectorize.text` module. This alias is deprecated\n> as of version 0.13.0 and will be removed in a future major release."},{"id":"class-voyageaivectorizermodelvoyage-3-large-api_confignone-dtypefloat32-cachenone--dimsnone","title":"`class VoyageAIVectorizer(model='voyage-3-large', api_config=None, dtype='float32', cache=None, *, dims=None)`","role":"content","text":"Bases: `BaseVectorizer`\n\nThe VoyageAIVectorizer class utilizes VoyageAI’s API to generate\nembeddings for text and multimodal (text / image / video) data.\n\nThis vectorizer is designed to interact with VoyageAI’s /embed, /multimodal_embed,\nand /contextualized_embed APIs. Any model identifier accepted by VoyageAI can be\npassed via `model` - for example the general-purpose `voyage-4-large` /\n`voyage-4` / `voyage-4-lite` / `voyage-4-nano` family, domain models such\nas `voyage-code-4`, contextualized `voyage-context-4` / `voyage-context-3`\nmodels, and multimodal `voyage-multimodal-*` models.\nSee [https://docs.voyageai.com/docs/embeddings](https://docs.voyageai.com/docs/embeddings) for the current catalog.\n\nIt requires an API key for authentication. The key can be provided\ndirectly in the api_config dictionary or through the VOYAGE_API_KEY\nenvironment variable. User must obtain an API key from VoyageAI’s website\n([https://dash.voyageai.com/](https://dash.voyageai.com/)). Additionally, the voyageai python\nclient must be installed with pip install voyageai. For image embeddings, the Pillow\nlibrary must also be installed with pip install pillow.\n\nThe vectorizer supports both synchronous and asynchronous operations, allows for batch\nprocessing of content and flexibility in handling preprocessing tasks.\n\nYou can optionally enable caching to improve performance when generating\nembeddings for repeated text inputs.\n\n[code example]\n\nInitialize the VoyageAI vectorizer.\n\nVisit [https://docs.voyageai.com/docs/embeddings](https://docs.voyageai.com/docs/embeddings) to learn about embeddings and check the available models.\n\n* **Parameters:**\n  * **model** (*str*) – Model to use for embedding. Defaults to \"voyage-3-large\".\n  * **api_config** (*Optional* *[* *Dict* *]* *,* *optional*) – Dictionary containing the API key.\n    Defaults to None.\n  * **dtype** (*str*) – the default datatype to use when embedding content as byte arrays.\n    Used when setting as_buffer=True in calls to embed() and embed_many().\n    Defaults to ‘float32’.\n  * **cache** (*Optional* *[*[*EmbeddingsCache*](https://redis.io/docs/latest/cache/#embeddingscache) *]*) – Optional EmbeddingsCache instance to cache embeddings for\n    better performance with repeated items. Defaults to None.\n  * **dims** (*Annotated* *[* *int* *|* *None* *,* *FieldInfo* *(* *annotation=NoneType* *,* *required=True* *,* *metadata=* *[* *Strict* *(* *strict=True* *)* *,* *Gt* *(* *gt=0* *)* *]* *)* *]*)\n* **Raises:**\n  * **ImportError** – If the voyageai library is not installed.\n  * **ValueError** – If the API key is not provided."},{"id":"notes","title":"`Notes`","role":"content","text":"- Multimodal models require voyageai>=0.3.6 to be installed for video embeddings, as well as\n  : ffmpeg installed on the system. Image embeddings require pillow to be installed.\n- Contextualized (`voyage-context-*`) models require voyageai>=0.5.0. Each input\n  : string is sent as its own document with auto-chunking, so inputs are embedded\n    independently (no cross-input contextualization) and the one-embedding-per-input\n    contract and cache determinism are preserved. A document longer than `chunk_size`\n    (32000 tokens) auto-chunks into multiple chunks but only the first chunk’s embedding\n    is kept; the rest are dropped. `truncation` is not forwarded to the contextualized\n    API (it does not accept it), so it is silently ignored for these models.\n- The plain text embedding path (`embed`/`embed_many` for non-context,\n  : non-multimodal models) uses token-aware batching: inputs are grouped into requests\n    bounded by both the per-model item cap and the model’s per-request token limit, so\n    large inputs are packed efficiently without exceeding VoyageAI’s token budget.\n\n#### `embed_image(image_path, **kwargs)`\n\nEmbed an image (from its path on disk) using VoyageAI’s multimodal API. Requires pillow to be installed.\n\n* **Parameters:**\n  **image_path** (*str*)\n* **Return type:**\n  list[float] | bytes\n\n#### `embed_video(video_path, **kwargs)`\n\nEmbed a video (from its path on disk) using VoyageAI’s multimodal API.\n\nRequires voyageai>=0.3.6 to be installed, as well as ffmpeg to be installed on the system.\n\n* **Parameters:**\n  **video_path** (*str*)\n* **Return type:**\n  list[float] | bytes\n\n#### `property is_context: bool`\n\nWhether a contextualized-embedding model (voyage-context-\n\n[code example]\n\n) has been configured.\n\n#### `property is_multimodal: bool`\n\nWhether a multimodal model has been configured.\n\n#### `model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True}`\n\nConfiguration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].\n\n#### `property type: str`\n\nReturn the type of vectorizer."},{"id":"mistralaitextvectorizer","title":"MistralAITextVectorizer","role":"content","text":"<a id=\"mistralaitextvectorizer-api\"></a>"},{"id":"class-mistralaitextvectorizermodelmistral-embed-api_confignone-dtypefloat32-cachenone--dimsnone","title":"`class MistralAITextVectorizer(model='mistral-embed', api_config=None, dtype='float32', cache=None, *, dims=None)`","role":"content","text":"Bases: `BaseVectorizer`\n\nThe MistralAITextVectorizer class utilizes MistralAI’s API to generate\nembeddings for text data.\n\nThis vectorizer is designed to interact with Mistral’s embeddings API,\nrequiring an API key for authentication. The key can be provided directly\nin the api_config dictionary or through the MISTRAL_API_KEY environment\nvariable. Users must obtain an API key from Mistral’s website\n([https://console.mistral.ai/](https://console.mistral.ai/)). Additionally, the mistralai python client\nmust be installed with pip install mistralai.\n\nThe vectorizer supports both synchronous and asynchronous operations,\nallowing for batch processing of texts and flexibility in handling\npreprocessing tasks.\n\nYou can optionally enable caching to improve performance when generating\nembeddings for repeated text inputs.\n\n[code example]\n\nInitialize the MistralAI vectorizer.\n\n* **Parameters:**\n  * **model** (*str*) – Model to use for embedding. Defaults to\n    ‘mistral-embed’.\n  * **api_config** (*Optional* *[* *Dict* *]* *,* *optional*) – Dictionary containing the\n    API key. Defaults to None.\n  * **dtype** (*str*) – the default datatype to use when embedding text as byte arrays.\n    Used when setting as_buffer=True in calls to embed() and embed_many().\n    Defaults to ‘float32’.\n  * **cache** (*Optional* *[*[*EmbeddingsCache*](https://redis.io/docs/latest/cache/#embeddingscache) *]*) – Optional EmbeddingsCache instance to cache embeddings for\n    better performance with repeated texts. Defaults to None.\n  * **dims** (*Annotated* *[* *int* *|* *None* *,* *FieldInfo* *(* *annotation=NoneType* *,* *required=True* *,* *metadata=* *[* *Strict* *(* *strict=True* *)* *,* *Gt* *(* *gt=0* *)* *]* *)* *]*)\n* **Raises:**\n  * **ImportError** – If the mistralai library is not installed.\n  * **ValueError** – If the Mistral API key is not provided.\n  * **ValueError** – If an invalid dtype is provided.\n\n#### `model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True}`\n\nConfiguration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].\n\n#### `property type: str`\n\nReturn the type of vectorizer."}],"examples":[{"id":"class-hftextvectorizermodelsentence-transformersall-mpnet-base-v2-dtypefloat32-cachenone--dimsnone-ex0","language":"python","code":"# Basic usage\nvectorizer = HFTextVectorizer(model=\"sentence-transformers/all-mpnet-base-v2\")\nembedding = vectorizer.embed(\"Hello, world!\")\n\n# With caching enabled\nfrom redisvl.extensions.cache.embeddings import EmbeddingsCache\ncache = EmbeddingsCache(name=\"my_embeddings_cache\")\n\nvectorizer = HFTextVectorizer(\n    model=\"sentence-transformers/all-mpnet-base-v2\",\n    cache=cache\n)\n\n# First call will compute and cache the embedding\nembedding1 = vectorizer.embed(\"Hello, world!\")\n\n# Second call will retrieve from cache\nembedding2 = vectorizer.embed(\"Hello, world!\")\n\n# Batch processing\nembeddings = vectorizer.embed_many(\n    [\"Hello, world!\", \"How are you?\"],\n    batch_size=2\n)\n\n# Multimodal usage\nfrom PIL import Image\nvectorizer = HFTextVectorizer(model=\"sentence-transformers/clip-ViT-L-14\")\nembeddings1 = vectorizer.embed(\"Hello, world!\")\nembeddings2 = vectorizer.embed(Image.open(\"path/to/your/image.jpg\"))","section_id":"class-hftextvectorizermodelsentence-transformersall-mpnet-base-v2-dtypefloat32-cachenone--dimsnone"},{"id":"class-openaitextvectorizermodeltext-embedding-ada-002-api_confignone-dtypefloat32-cachenone--dimsnone-ex0","language":"python","code":"# Basic usage with OpenAI embeddings\nvectorizer = OpenAITextVectorizer(\n    model=\"text-embedding-ada-002\",\n    api_config={\"api_key\": \"your_api_key\"} # OR set OPENAI_API_KEY in your env\n)\nembedding = vectorizer.embed(\"Hello, world!\")\n\n# With caching enabled\nfrom redisvl.extensions.cache.embeddings import EmbeddingsCache\ncache = EmbeddingsCache(name=\"openai_embeddings_cache\")\n\nvectorizer = OpenAITextVectorizer(\n    model=\"text-embedding-ada-002\",\n    api_config={\"api_key\": \"your_api_key\"},\n    cache=cache\n)\n\n# First call will compute and cache the embedding\nembedding1 = vectorizer.embed(\"Hello, world!\")\n\n# Second call will retrieve from cache\nembedding2 = vectorizer.embed(\"Hello, world!\")\n\n# Asynchronous batch embedding of multiple texts\nembeddings = await vectorizer.aembed_many(\n    [\"Hello, world!\", \"How are you?\"],\n    batch_size=2\n)","section_id":"class-openaitextvectorizermodeltext-embedding-ada-002-api_confignone-dtypefloat32-cachenone--dimsnone"},{"id":"class-azureopenaitextvectorizermodeltext-embedding-ada-002-api_confignone-dtypefloat32-cachenone--dimsnone-ex0","language":"python","code":"# Basic usage\nvectorizer = AzureOpenAITextVectorizer(\n    model=\"text-embedding-ada-002\",\n    api_config={\n        \"api_key\": \"your_api_key\", # OR set AZURE_OPENAI_API_KEY in your env\n        \"api_version\": \"your_api_version\", # OR set OPENAI_API_VERSION in your env\n        \"azure_endpoint\": \"your_azure_endpoint\", # OR set AZURE_OPENAI_ENDPOINT in your env\n    }\n)\nembedding = vectorizer.embed(\"Hello, world!\")\n\n# With caching enabled\nfrom redisvl.extensions.cache.embeddings import EmbeddingsCache\ncache = EmbeddingsCache(name=\"azureopenai_embeddings_cache\")\n\nvectorizer = AzureOpenAITextVectorizer(\n    model=\"text-embedding-ada-002\",\n    api_config={\n        \"api_key\": \"your_api_key\",\n        \"api_version\": \"your_api_version\",\n        \"azure_endpoint\": \"your_azure_endpoint\",\n    },\n    cache=cache\n)\n\n# First call will compute and cache the embedding\nembedding1 = vectorizer.embed(\"Hello, world!\")\n\n# Second call will retrieve from cache\nembedding2 = vectorizer.embed(\"Hello, world!\")\n\n# Asynchronous batch embedding of multiple texts\nembeddings = await vectorizer.aembed_many(\n    [\"Hello, world!\", \"How are you?\"],\n    batch_size=2\n)","section_id":"class-azureopenaitextvectorizermodeltext-embedding-ada-002-api_confignone-dtypefloat32-cachenone--dimsnone"},{"id":"class-vertexaivectorizermodeltextembedding-gecko-api_confignone-dtypefloat32-cachenone--dimsnone-ex0","language":"python","code":"# Basic usage\nvectorizer = VertexAIVectorizer(\n    model=\"textembedding-gecko\",\n    api_config={\n        \"project_id\": \"your_gcp_project_id\", # OR set GCP_PROJECT_ID\n        \"location\": \"your_gcp_location\",     # OR set GCP_LOCATION\n    })\nembedding = vectorizer.embed(\"Hello, world!\")\n\n# With caching enabled\nfrom redisvl.extensions.cache.embeddings import EmbeddingsCache\ncache = EmbeddingsCache(name=\"vertexai_embeddings_cache\")\n\nvectorizer = VertexAIVectorizer(\n    model=\"textembedding-gecko\",\n    api_config={\n        \"project_id\": \"your_gcp_project_id\",\n        \"location\": \"your_gcp_location\",\n    },\n    cache=cache\n)\n\n# First call will compute and cache the embedding\nembedding1 = vectorizer.embed(\"Hello, world!\")\n\n# Second call will retrieve from cache\nembedding2 = vectorizer.embed(\"Hello, world!\")\n\n# Batch embedding of multiple texts\nembeddings = vectorizer.embed_many(\n    [\"Hello, world!\", \"Goodbye, world!\"],\n    batch_size=2\n)\n\n# Multimodal usage\nfrom vertexai.vision_models import Image, Video\n\nvectorizer = VertexAIVectorizer(\n    model=\"multimodalembedding@001\",\n    api_config={\n        \"project_id\": \"your_gcp_project_id\", # OR set GCP_PROJECT_ID\n        \"location\": \"your_gcp_location\",     # OR set GCP_LOCATION\n    }\n)\ntext_embedding = vectorizer.embed(\"Hello, world!\")\nimage_embedding = vectorizer.embed(Image.load_from_file(\"path/to/your/image.jpg\"))\nvideo_embedding = vectorizer.embed(Video.load_from_file(\"path/to/your/video.mp4\"))","section_id":"class-vertexaivectorizermodeltextembedding-gecko-api_confignone-dtypefloat32-cachenone--dimsnone"},{"id":"class-googlegenaivectorizermodelgemini-embedding-001-api_confignone-dtypefloat32-cachenone-task_typenone-output_dimensionalitynone--dimsnone-ex0","language":"python","code":"# Vertex AI backend\nfrom redisvl.utils.vectorize import GoogleGenAIVectorizer\n\nvectorizer = GoogleGenAIVectorizer(\n    model=\"gemini-embedding-001\",\n    api_config={\n        \"project_id\": \"your-gcp-project\",  # or set GOOGLE_CLOUD_PROJECT\n        \"location\": \"us-central1\",         # or set GOOGLE_CLOUD_LOCATION\n    },\n)\nembedding = vectorizer.embed(\"Hello, world!\")\n\n# Gemini Developer API backend\nvectorizer = GoogleGenAIVectorizer(\n    model=\"gemini-embedding-001\",\n    api_config={\"api_key\": \"your-gemini-api-key\"},  # or set GEMINI_API_KEY\n)\n\n# Reduced dimensions (Matryoshka) + caching\nfrom redisvl.extensions.cache.embeddings import EmbeddingsCache\n\nvectorizer = GoogleGenAIVectorizer(\n    model=\"gemini-embedding-001\",\n    output_dimensionality=768,\n    cache=EmbeddingsCache(name=\"genai_embeddings_cache\"),\n)\n\n# Asynchronous batch embedding\nembeddings = await vectorizer.aembed_many(\n    [\"Hello, world!\", \"How are you?\"], batch_size=2\n)","section_id":"class-googlegenaivectorizermodelgemini-embedding-001-api_confignone-dtypefloat32-cachenone-task_typenone-output_dimensionalitynone--dimsnone"},{"id":"class-coheretextvectorizermodelembed-english-v30-api_confignone-dtypefloat32-cachenone--dimsnone-ex0","language":"python","code":"from redisvl.utils.vectorize import CohereTextVectorizer\n\n# Basic usage\nvectorizer = CohereTextVectorizer(\n    model=\"embed-english-v3.0\",\n    api_config={\"api_key\": \"your-cohere-api-key\"} # OR set COHERE_API_KEY in your env\n)\nquery_embedding = vectorizer.embed(\n    text=\"your input query text here\",\n    input_type=\"search_query\"\n)\ndoc_embeddings = vectorizer.embed_many(\n    texts=[\"your document text\", \"more document text\"],\n    input_type=\"search_document\"\n)\n\n# With caching enabled\nfrom redisvl.extensions.cache.embeddings import EmbeddingsCache\ncache = EmbeddingsCache(name=\"cohere_embeddings_cache\")\n\nvectorizer = CohereTextVectorizer(\n    model=\"embed-english-v3.0\",\n    api_config={\"api_key\": \"your-cohere-api-key\"},\n    cache=cache\n)\n\n# First call will compute and cache the embedding\nembedding1 = vectorizer.embed(\n    text=\"your input query text here\",\n    input_type=\"search_query\"\n)\n\n# Second call will retrieve from cache\nembedding2 = vectorizer.embed(\n    text=\"your input query text here\",\n    input_type=\"search_query\"\n)","section_id":"class-coheretextvectorizermodelembed-english-v30-api_confignone-dtypefloat32-cachenone--dimsnone"},{"id":"class-bedrockvectorizermodelamazontitan-embed-text-v20-api_confignone-dtypefloat32-cachenone--dimsnone-ex0","language":"python","code":"# Basic usage with explicit credentials\nvectorizer = BedrockVectorizer(\n    model=\"amazon.titan-embed-text-v2:0\",\n    api_config={\n        \"aws_access_key_id\": \"your_access_key\",\n        \"aws_secret_access_key\": \"your_secret_key\",\n        \"aws_region\": \"us-east-1\"\n    }\n)\n\n# With environment variables and caching\nfrom redisvl.extensions.cache.embeddings import EmbeddingsCache\ncache = EmbeddingsCache(name=\"bedrock_embeddings_cache\")\n\nvectorizer = BedrockVectorizer(\n    model=\"amazon.titan-embed-text-v2:0\",\n    cache=cache\n)\n\n# First call will compute and cache the embedding\nembedding1 = vectorizer.embed(\"Hello, world!\")\n\n# Second call will retrieve from cache\nembedding2 = vectorizer.embed(\"Hello, world!\")\n\n# Generate batch embeddings\nembeddings = vectorizer.embed_many([\"Hello\", \"World\"], batch_size=2)\n\n# Multimodal usage\nfrom pathlib import Path\nvectorizer = BedrockVectorizer(\n    model=\"amazon.titan-embed-image-v1:0\",\n    api_config={\n        \"aws_access_key_id\": \"your_access_key\",\n        \"aws_secret_access_key\": \"your_secret_key\",\n        \"aws_region\": \"us-east-1\"\n    }\n)\nimage_embedding = vectorizer.embed(Path(\"path/to/your/image.jpg\"))\n\n# Embedding a list of mixed modalities\nembeddings = vectorizer.embed_many(\n    [\"Hello\", \"world!\", Path(\"path/to/your/image.jpg\")],\n    batch_size=2\n)","section_id":"class-bedrockvectorizermodelamazontitan-embed-text-v20-api_confignone-dtypefloat32-cachenone--dimsnone"},{"id":"class-customvectorizerembed-embed_manynone-aembednone-aembed_manynone-dtypefloat32-cachenone-ex0","language":"python","code":"# Basic usage with a custom embedding function\nvectorizer = CustomVectorizer(\n    embed = my_vectorizer.generate_embedding\n)\nembedding = vectorizer.embed(\"Hello, world!\")\n\n# With caching enabled\nfrom redisvl.extensions.cache.embeddings import EmbeddingsCache\ncache = EmbeddingsCache(name=\"my_embeddings_cache\")\n\nvectorizer = CustomVectorizer(\n    embed=my_vectorizer.generate_embedding,\n    cache=cache\n)\n\n# First call will compute and cache the embedding\nembedding1 = vectorizer.embed(\"Hello, world!\")\n\n# Second call will retrieve from cache\nembedding2 = vectorizer.embed(\"Hello, world!\")\n\n# Asynchronous batch embedding of multiple texts\nembeddings = await vectorizer.aembed_many(\n    [\"Hello, world!\", \"How are you?\"],\n    batch_size=2\n)","section_id":"class-customvectorizerembed-embed_manynone-aembednone-aembed_manynone-dtypefloat32-cachenone"},{"id":"class-voyageaivectorizermodelvoyage-3-large-api_confignone-dtypefloat32-cachenone--dimsnone-ex0","language":"python","code":"from redisvl.utils.vectorize import VoyageAIVectorizer\n\n# Basic usage\nvectorizer = VoyageAIVectorizer(\n    model=\"voyage-3-large\",\n    api_config={\"api_key\": \"your-voyageai-api-key\"} # OR set VOYAGE_API_KEY in your env\n)\nquery_embedding = vectorizer.embed(\n    content=\"your input query text here\",\n    input_type=\"query\"\n)\ndoc_embeddings = vectorizer.embed_many(\n    contents=[\"your document text\", \"more document text\"],\n    input_type=\"document\"\n)\n\n# Contextualized embeddings (voyage-context-* models) - requires voyageai>=0.5.0\n# Each input string is treated as its own document (auto-chunked) and\n# embedded independently: inputs do not influence one another, which keeps\n# the one-embedding-per-input contract and cache determinism intact.\ncontext_vectorizer = VoyageAIVectorizer(\n    model=\"voyage-context-4\",\n    api_config={\"api_key\": \"your-voyageai-api-key\"}\n)\ncontext_embeddings = context_vectorizer.embed_many(\n    contents=[\"chunk one\", \"chunk two\", \"chunk three\"],\n    input_type=\"document\"\n)\n# Retrieval queries use input_type=\"query\"; auto-chunking is a\n# document-only feature, so query inputs are embedded as-is.\ncontext_query = context_vectorizer.embed(\n    content=\"your query text here\",\n    input_type=\"query\"\n)\n\n# Multimodal usage - requires Pillow and voyageai>=0.3.6\n\nvectorizer = VoyageAIVectorizer(\n    model=\"voyage-multimodal-3.5\",\n    api_config={\"api_key\": \"your-voyageai-api-key\"} # OR set VOYAGE_API_KEY in your env\n)\nimage_embedding = vectorizer.embed_image(\n    \"path/to/your/image.jpg\",\n    input_type=\"query\"\n)\nvideo_embedding = vectorizer.embed_video(\n    \"path/to/your/video.mp4\",\n    input_type=\"document\"\n)\n\n# With caching enabled\nfrom redisvl.extensions.cache.embeddings import EmbeddingsCache\ncache = EmbeddingsCache(name=\"voyageai_embeddings_cache\")\n\nvectorizer = VoyageAIVectorizer(\n    model=\"voyage-3-large\",\n    api_config={\"api_key\": \"your-voyageai-api-key\"},\n    cache=cache\n)\n\n# First call will compute and cache the embedding\nembedding1 = vectorizer.embed(\n    content=\"your input query text here\",\n    input_type=\"query\"\n)\n\n# Second call will retrieve from cache\nembedding2 = vectorizer.embed(\n    content=\"your input query text here\",\n    input_type=\"query\"\n)","section_id":"class-voyageaivectorizermodelvoyage-3-large-api_confignone-dtypefloat32-cachenone--dimsnone"},{"id":"notes-ex0","language":"plaintext","code":"*","section_id":"notes"},{"id":"class-mistralaitextvectorizermodelmistral-embed-api_confignone-dtypefloat32-cachenone--dimsnone-ex0","language":"python","code":"# Basic usage\nvectorizer = MistralAITextVectorizer(\n    model=\"mistral-embed\",\n    api_config={\"api_key\": \"your_api_key\"} # OR set MISTRAL_API_KEY in your env\n)\nembedding = vectorizer.embed(\"Hello, world!\")\n\n# With caching enabled\nfrom redisvl.extensions.cache.embeddings import EmbeddingsCache\ncache = EmbeddingsCache(name=\"mistral_embeddings_cache\")\n\nvectorizer = MistralAITextVectorizer(\n    model=\"mistral-embed\",\n    api_config={\"api_key\": \"your_api_key\"},\n    cache=cache\n)\n\n# First call will compute and cache the embedding\nembedding1 = vectorizer.embed(\"Hello, world!\")\n\n# Second call will retrieve from cache\nembedding2 = vectorizer.embed(\"Hello, world!\")\n\n# Asynchronous batch embedding of multiple texts\nembeddings = await vectorizer.aembed_many(\n    [\"Hello, world!\", \"How are you?\"],\n    batch_size=2\n)","section_id":"class-mistralaitextvectorizermodelmistral-embed-api_confignone-dtypefloat32-cachenone--dimsnone"}]}
{"schema_version":2,"id":"develop/ai/redisvl/concepts/architecture","title":"Architecture","url":"https://redis.io/docs/latest/develop/ai/redisvl/concepts/architecture/","summary":"","aliases":["/integrate/redisvl/concepts/architecture"],"tags":[],"last_updated":"2026-10-05T12:55:32+01:00","page_type":"content","content_hash":"d1a76ca9c804da334c4643bd3847d9874b66389e894646549e65bceeabaddec9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"RedisVL sits between your application and Redis, providing a structured way to define, populate, and query vector search indexes.\n\n![RedisVL Architecture](https://redis.io/docs/latest/images/redisvl/redisvl-architecture.svg)"},{"id":"the-core-pattern","title":"The Core Pattern","role":"content","text":"Every RedisVL application follows a consistent workflow: **define → create → load → query**.\n\nFirst, you define a **schema** that describes your data. The schema specifies which fields exist, what types they are, and how they should be indexed. This includes declaring vector fields with their dimensionality and the algorithm Redis should use for similarity search.\n\nNext, you create an **index** in Redis based on that schema. The index is a persistent structure that Redis uses to make searches fast. Creating an index tells Redis how to organize and access your data.\n\nThen you **load** your data. Documents are stored as Redis Hash or JSON objects. As documents are written, Redis automatically indexes them according to your schema—no separate indexing step required.\n\nFinally, you **query** the index. RedisVL provides query builders that construct Redis search commands for you. You can search by vector similarity, filter by metadata, combine multiple criteria, or mix full-text search with semantic search.\n\nThis pattern applies whether you’re building a simple semantic search or a complex multi-modal retrieval system."},{"id":"schemas-as-contracts","title":"Schemas as Contracts","role":"content","text":"The schema is the source of truth for your index. It defines the contract between your data and Redis.\n\nA schema includes the index name, a key prefix (so Redis knows which keys belong to this index), the storage type (Hash or JSON), and a list of field definitions. Each field has a name, a type, and type-specific configuration.\n\nFor vector fields, you specify the dimensionality (which must match your embedding model’s output), the distance metric (cosine, Euclidean, or inner product), and the indexing algorithm. These choices are locked in when the index is created—changing them requires building a new index.\n\nThe schema can be defined programmatically in Python or loaded from a YAML file. YAML schemas are useful for version control, sharing between environments, and keeping configuration separate from code."},{"id":"query-composition","title":"Query Composition","role":"content","text":"RedisVL’s query builders let you compose search operations without writing raw Redis commands.\n\n**Vector queries** find the K most similar items to a query vector. You provide an embedding, and Redis returns the nearest neighbors according to your configured distance metric.\n\n**Range queries** find all vectors within a distance threshold. Instead of asking for the top K, you’re asking for everything \"close enough\" to a query point.\n\n**Filter queries** narrow results by metadata. You can filter on text fields, tags, numeric ranges, and geographic areas. Filters apply before the vector search, reducing the candidate set.\n\n**Hybrid queries** combine keyword search with semantic search. This is useful when you want to match on specific terms while also considering semantic relevance.\n\nThese query types can be combined. A typical pattern is vector search with metadata filters—for example, finding similar products but only in a specific category or price range."},{"id":"extensions-as-patterns","title":"Extensions as Patterns","role":"content","text":"Extensions are higher-level abstractions built on RedisVL’s core primitives. Each extension encapsulates a common AI workflow pattern.\n\n**Semantic caching** stores LLM responses and retrieves them when similar prompts are seen again. This reduces API costs and latency without requiring exact-match caching.\n\n**Message history** stores conversation turns and retrieves context for LLM prompts. It can retrieve by recency (most recent messages) or by relevance (semantically similar messages).\n\n**Semantic routing** classifies queries into predefined categories based on similarity to reference phrases. This enables intent detection, topic routing, and guardrails.\n\nEach extension manages its own Redis index internally. You interact with a clean, purpose-specific API rather than managing schemas and queries yourself.\n\n---\n\n**Related concepts:** [Search & Indexing](https://redis.io/docs/latest/search-and-indexing) covers schemas and field types in detail. [Query Types](https://redis.io/docs/latest/queries) explains the different query types available.\n\n**Learn more:** [Getting Started](https://redis.io/docs/latest/../user_guide/getting_started) covers the core workflow. [Extensions](https://redis.io/docs/latest/extensions) explains each extension pattern in detail."}],"examples":[]}
{"schema_version":2,"id":"develop/ai/redisvl/concepts/extensions","title":"Extensions","url":"https://redis.io/docs/latest/develop/ai/redisvl/concepts/extensions/","summary":"","aliases":["/integrate/redisvl/concepts/extensions"],"tags":[],"last_updated":"2026-10-05T16:11:44+01:00","page_type":"content","content_hash":"fccc6e69dd9a43caec027624344bb4a43ac5bddcca2acd353fcbc4a030491663","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Extensions are opinionated, higher-level abstractions built on RedisVL’s core primitives. Each extension encapsulates a common AI application pattern, managing its own Redis index internally and exposing a clean, purpose-specific API.\n\nYou don’t need to understand schemas, indexes, or queries to use extensions—they handle that complexity for you."},{"id":"semantic-cache","title":"Semantic Cache","role":"content","text":"LLM API calls are expensive and slow. If users ask similar questions, you’re paying to generate similar answers repeatedly. Semantic caching solves this by storing responses and returning cached answers when similar prompts are seen again."},{"id":"how-it-works","title":"How It Works","role":"content","text":"When a prompt arrives, the cache embeds it and searches for similar cached prompts. If a match is found within the configured distance threshold, the cached response is returned immediately—no LLM call needed. If no match is found, you call the LLM, store the prompt-response pair, and return the response.\n\nThe key insight is \"similar\" rather than \"identical.\" Traditional caching requires exact matches. Semantic caching matches by meaning, so \"What’s the capital of France?\" and \"Tell me France’s capital city\" can hit the same cache entry."},{"id":"threshold-tuning","title":"Threshold Tuning","role":"content","text":"The distance threshold controls how similar prompts must be to match. A strict threshold (low value, like 0.05) requires near-identical prompts. A loose threshold (higher value, like 0.3) matches more liberally.\n\nToo strict, and you miss valid cache hits. Too loose, and you return wrong answers for different questions. Start strict, monitor cache quality in production, and loosen gradually based on observed behavior."},{"id":"multi-tenant-isolation","title":"Multi-Tenant Isolation","role":"content","text":"In applications serving multiple users or contexts, you often want separate cache spaces. Filters let you scope cache lookups—for example, caching per-user or per-conversation so one user’s cached answers don’t leak to another."},{"id":"redis-vs-langcache-managed-service","title":"Redis vs LangCache managed service","role":"content","text":"`SemanticCache` stores data in your Redis deployment and uses RedisVL’s search index under the hood—you control sizing, networking, and advanced filtering with [FilterExpression](https://redis.io/docs/latest/../api/filter).\n\nIf you prefer a hosted semantic cache that is operated as a service you can use `LangCacheSemanticCache` (install `redisvl[langcache]`). It uses the LangCache API endpoint instead of Redis directly. While these are similar, they do not share all the same properties. Refer to [Cache LLM Responses](https://redis.io/docs/latest/../user_guide/how_to_guides/llmcache) to see `SemanticCache` in detail, and [Use LangCache as the LLM Cache Backend](https://redis.io/docs/latest/../user_guide/how_to_guides/langcache_semantic_cache) covers `LangCacheSemanticCache` in detail."},{"id":"embeddings-cache","title":"Embeddings Cache","role":"content","text":"Embedding APIs have per-token costs, and computing the same embedding repeatedly wastes money. The embeddings cache stores computed embeddings and returns them on subsequent requests for the same content."},{"id":"how-it-works-1","title":"How It Works","role":"content","text":"Unlike semantic cache (which uses similarity search), embeddings cache uses exact key matching. A deterministic hash is computed from the input text and model name. If that hash exists in the cache, the stored embedding is returned. If not, the embedding is computed, stored, and returned.\n\nThis is useful when the same content is embedded multiple times—common in applications where users submit similar queries, or where documents are re-processed periodically."},{"id":"wrapping-vectorizers","title":"Wrapping Vectorizers","role":"content","text":"The embeddings cache can wrap any [vectorizer](https://redis.io/docs/latest/utilities), adding transparent caching. Calling the wrapped vectorizer checks the cache first. This requires no changes to your embedding code—just wrap the vectorizer and caching happens automatically."},{"id":"message-history","title":"Message History","role":"content","text":"LLMs are stateless. To have a conversation, you must include previous messages in each prompt. Message history manages this context, storing conversation turns and retrieving them when building prompts.\n\n> [!NOTE]\n> `SessionManager` and `SemanticSessionManager` have been renamed to `MessageHistory` and `SemanticMessageHistory`. The old names are deprecated and will be removed in a future release."},{"id":"storage-model","title":"Storage Model","role":"content","text":"Each message includes a role (user, assistant, system, or tool), the message content, a timestamp, and a session identifier. The session tag groups messages into conversations—you might have one session per user, per chat thread, or per agent instance."},{"id":"retrieval-strategies","title":"Retrieval Strategies","role":"content","text":"The simplest retrieval is by recency: get the N most recent messages. This works for short conversations but breaks down when context exceeds the LLM’s token limit or when relevant information appeared earlier in a long conversation.\n\nSemantic message history adds vector search. Messages are embedded, and you can retrieve by relevance rather than recency. This is powerful for long conversations where the user might reference something said much earlier, or for agents that need to recall specific instructions from their setup."},{"id":"session-isolation","title":"Session Isolation","role":"content","text":"Session tags are critical for multi-user applications. Each user’s conversation should be isolated, so retrieving context for User A doesn’t include messages from User B. The session tag provides this isolation, and you can structure sessions however makes sense—per-user, per-thread, per-agent, or any other grouping.\n\n**Learn more:** [Manage LLM Message History](https://redis.io/docs/latest/../user_guide/how_to_guides/message_history) explains conversation management in detail."},{"id":"semantic-router","title":"Semantic Router","role":"content","text":"Semantic routing classifies queries into predefined categories based on meaning. It’s a lightweight alternative to classification models, useful for intent detection, topic routing, and guardrails."},{"id":"how-it-works-2","title":"How It Works","role":"content","text":"You define routes, each with a name and a set of reference phrases that represent that category. The router embeds all references and indexes them. At runtime, an incoming query is embedded and compared against all route references. The route whose references are closest to the query wins—if it’s within the configured distance threshold.\n\nFor example, a customer support router might have routes for \"billing,\" \"technical support,\" and \"account management,\" each with 5-10 reference phrases. When a user asks \"I can’t log into my account,\" the router matches it to the \"account management\" route based on semantic similarity to that route’s references."},{"id":"threshold-and-aggregation","title":"Threshold and Aggregation","role":"content","text":"Each route has its own distance threshold, controlling how close queries must be to match. Routes can also specify how to aggregate distances when multiple references match—taking the average or minimum distance.\n\nIf no route matches (all distances exceed their thresholds), the router returns no match. This lets you handle out-of-scope queries gracefully rather than forcing a classification."},{"id":"use-cases","title":"Use Cases","role":"content","text":"Semantic routing is useful for intent classification (determining what a user wants), topic detection (categorizing content), guardrails (detecting and blocking certain query types), and agent dispatch (sending queries to specialized sub-agents).\n\n**Learn more:** [Route Queries with SemanticRouter](https://redis.io/docs/latest/../user_guide/how_to_guides/semantic_router) walks through routing setup in detail.\n\n---\n\n**Related concepts:** [Query Types](https://redis.io/docs/latest/queries) explains the query types used internally by extensions. [Utilities](https://redis.io/docs/latest/utilities) covers vectorizers used for embedding."}],"examples":[]}
{"schema_version":2,"id":"develop/ai/redisvl/concepts/field-attributes","title":"Field Attributes","url":"https://redis.io/docs/latest/develop/ai/redisvl/concepts/field-attributes/","summary":"","aliases":["/integrate/redisvl/concepts/field-attributes"],"tags":[],"last_updated":"2026-06-08T15:46:44+02:00","page_type":"content","content_hash":"807fdb66702f0ca8f62121ba0b00018e99cdc73b99c3473bcd4342d4c24b59d3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Field attributes customize how Redis indexes and searches your data. Each field type has specific attributes that control indexing behavior, search capabilities, and storage options."},{"id":"common-attributes","title":"Common Attributes","role":"content","text":"These attributes are available on most non-vector field types (text, tag, numeric, geo)."},{"id":"sortable","title":"sortable","role":"content","text":"Enables sorting results by this field. Without `sortable`, you cannot use the field in `ORDER BY` clauses.\n\n**Trade-off**: Sortable fields consume additional memory to maintain a sorted index.\n\n[code example]\n\n[code example]\n\n**Use when**: You need to sort search results by this field (e.g., \"newest first\", \"highest price\")."},{"id":"no_index","title":"no_index","role":"content","text":"Stores the field without indexing it. The field value is available in search results but cannot be used in queries or filters.\n\n**Important**: `no_index` only makes sense when combined with `sortable: true`. A field that is neither indexed nor sortable serves no purpose in the schema.\n\n[code example]\n\n**Use when**: You want to sort by a field but never filter on it, saving index space."},{"id":"index_missing","title":"index_missing","role":"content","text":"Allows searching for documents that don’t have this field. When enabled, you can use `ISMISSING` queries to find documents where the field is absent or null.\n\n[code example]\n\n[code example]\n\n**Use when**: Your data has optional fields and you need to query for documents missing those fields.\n\n**Query example**:\n\n[code example]"},{"id":"text-field-attributes","title":"Text Field Attributes","role":"content","text":"Text fields support full-text search with these additional attributes."},{"id":"weight","title":"weight","role":"content","text":"Controls the importance of this field in relevance scoring. Higher weights make matches in this field rank higher.\n\n[code example]\n\n**Use when**: Some text fields are more important than others for search relevance."},{"id":"no_stem","title":"no_stem","role":"content","text":"Disables stemming for this field. By default, Redis applies stemming so \"running\" matches \"run\". Disable when exact word forms matter.\n\n[code example]\n\n**Use when**: Field contains codes, identifiers, or technical terms where stemming would cause incorrect matches."},{"id":"withsuffixtrie","title":"withsuffixtrie","role":"content","text":"Maintains a suffix trie for optimized suffix and contains queries. Enables efficient `*suffix` and `*contains*` searches.\n\n[code example]\n\n**Use when**: You need to search for patterns like `*@gmail.com` or `*smith*`.\n\n**Trade-off**: Increases memory usage and index build time."},{"id":"phonetic_matcher","title":"phonetic_matcher","role":"content","text":"Enables phonetic matching using the specified algorithm. Matches words that sound similar.\n\n[code example]\n\n**Supported values**: `dm:en` (Double Metaphone English), `dm:fr` (French), `dm:pt` (Portuguese), `dm:es` (Spanish)\n\n**Use when**: Searching names or words where spelling variations should match (e.g., \"Smith\" matches \"Smyth\")."},{"id":"index_empty","title":"index_empty","role":"content","text":"Allows indexing and searching for empty strings. By default, empty strings are not indexed.\n\n[code example]\n\n**Use when**: Empty string is a meaningful value you need to query for."},{"id":"unf-un-normalized-form","title":"unf (Un-Normalized Form)","role":"content","text":"Preserves the original value for sortable fields without normalization. By default, sortable text fields are lowercased for consistent sorting.\n\n**Requires**: `sortable: true`\n\n[code example]\n\n**Use when**: You need case-sensitive sorting or must preserve exact original values."},{"id":"tag-field-attributes","title":"Tag Field Attributes","role":"content","text":"Tag fields are for exact-match filtering on categorical data."},{"id":"separator","title":"separator","role":"content","text":"Specifies the character that separates multiple tags in a single field value. Default is comma (`,`).\n\n[code example]\n\n**Use when**: Your tag values contain commas, or you’re using a different delimiter in your data."},{"id":"case_sensitive","title":"case_sensitive","role":"content","text":"Makes tag matching case-sensitive. By default, tags are lowercased for matching.\n\n[code example]\n\n**Use when**: Tag values are case-sensitive identifiers (SKUs, codes, etc.)."},{"id":"withsuffixtrie-1","title":"withsuffixtrie","role":"content","text":"Same as text fields—enables efficient suffix and contains queries on tags.\n\n[code example]"},{"id":"index_empty-1","title":"index_empty","role":"content","text":"Allows indexing empty tag values.\n\n[code example]"},{"id":"numeric-field-attributes","title":"Numeric Field Attributes","role":"content","text":"Numeric fields support range queries and sorting."},{"id":"unf-un-normalized-form-1","title":"unf (Un-Normalized Form)","role":"content","text":"For sortable numeric fields, preserves the exact numeric representation without normalization.\n\n**Requires**: `sortable: true`\n\n[code example]\n\n**Note**: Numeric fields do not support `index_empty` (empty numeric values are not meaningful)."},{"id":"geo-field-attributes","title":"Geo Field Attributes","role":"content","text":"Geo fields store geographic coordinates for location-based queries.\n\nGeo fields support the common attributes (`sortable`, `no_index`, `index_missing`) but have no geo-specific attributes. The field value should be a string in `\"longitude,latitude\"` format.\n\n[code example]\n\n**Note**: Geo fields do not support `index_empty` (empty coordinates are not meaningful)."},{"id":"vector-field-attributes","title":"Vector Field Attributes","role":"content","text":"Vector fields have a different attribute structure. See [Schema](https://redis.io/docs/latest/../api/schema) for complete vector field documentation.\n\nKey vector attributes:\n\n- `dims`: Vector dimensionality (required)\n- `algorithm`: `flat`, `hnsw`, or `svs-vamana`\n- `distance_metric`: `COSINE`, `L2`, or `IP`\n- `datatype`: Vector precision (see table below)\n- `index_missing`: Allow searching for documents without vectors\n\n[code example]"},{"id":"vector-datatypes","title":"Vector Datatypes","role":"content","text":"The `datatype` attribute controls how vector components are stored. Smaller datatypes reduce memory usage but may affect precision.\n\n| Datatype   |   Bits | Memory (768 dims)   | Use Case                                                                           |\n|------------|--------|---------------------|------------------------------------------------------------------------------------|\n| `float32`  |     32 | 3 KB                | Default. Best precision for most applications.                                     |\n| `float16`  |     16 | 1.5 KB              | Good balance of memory and precision. Recommended for large-scale deployments.     |\n| `bfloat16` |     16 | 1.5 KB              | Better dynamic range than float16. Useful when embeddings have large value ranges. |\n| `float64`  |     64 | 6 KB                | Maximum precision. Rarely needed.                                                  |\n| `int8`     |      8 | 768 B               | Integer quantization. Significant memory savings with some precision loss.         |\n| `uint8`    |      8 | 768 B               | Unsigned integer quantization. For embeddings with non-negative values.            |\n\n**Algorithm Compatibility:**\n\n| Datatype   | FLAT   | HNSW   | SVS-VAMANA   |\n|------------|--------|--------|--------------|\n| `float32`  | Yes    | Yes    | Yes          |\n| `float16`  | Yes    | Yes    | Yes          |\n| `bfloat16` | Yes    | Yes    | No           |\n| `float64`  | Yes    | Yes    | No           |\n| `int8`     | Yes    | Yes    | No           |\n| `uint8`    | Yes    | Yes    | No           |\n\n**Choosing a Datatype:**\n\n- **Start with `float32`** unless you have memory constraints\n- **Use `float16`** for production systems with millions of vectors (50% memory savings, minimal precision loss)\n- **Use `int8`/`uint8`** only after benchmarking recall on your specific dataset\n- **SVS-VAMANA users**: Must use `float16` or `float32`\n\n**Quantization with the Migrator:**\n\nYou can change vector datatypes on existing indexes using the migration wizard:\n\n[code example]\n\nThe migrator automatically re-encodes stored vectors to the new precision. See [Migrate an Index](https://redis.io/docs/latest/../user_guide/how_to_guides/migrate-indexes) for details.\nWhen you apply the resulting migration plan, pass `--backup-dir`; the backup directory is required before any migration starts and stores original vector bytes for resume and rollback."},{"id":"redis-specific-subtleties","title":"Redis-Specific Subtleties","role":"content","text":""},{"id":"modifier-ordering","title":"Modifier Ordering","role":"content","text":"Redis Search has specific requirements for the order of field modifiers. RedisVL handles this automatically, but it’s useful to understand:\n\n**Canonical order**: `INDEXEMPTY` → `INDEXMISSING` → `SORTABLE` → `UNF` → `NOINDEX`\n\nIf you’re debugging raw Redis commands, ensure modifiers appear in this order."},{"id":"field-type-limitations","title":"Field Type Limitations","role":"content","text":"Not all attributes work with all field types:\n\n| Attribute        | Text   | Tag   | Numeric   | Geo   | Vector   |\n|------------------|--------|-------|-----------|-------|----------|\n| `sortable`       | ✓      | ✓     | ✓         | ✓     | ✗        |\n| `no_index`       | ✓      | ✓     | ✓         | ✓     | ✗        |\n| `index_missing`  | ✓      | ✓     | ✓         | ✓     | ✓        |\n| `index_empty`    | ✓      | ✓     | ✗         | ✗     | ✗        |\n| `unf`            | ✓      | ✗     | ✓         | ✗     | ✗        |\n| `withsuffixtrie` | ✓      | ✓     | ✗         | ✗     | ✗        |"},{"id":"migration-support","title":"Migration Support","role":"content","text":"The migration wizard (`rvl migrate wizard`) supports updating field attributes on existing indexes. The table below shows which attributes can be updated via the wizard vs requiring manual schema patch editing.\n\n**Wizard Prompts:**\n\n| Attribute              | Text     | Tag    | Numeric   | Geo    | Vector   |\n|------------------------|----------|--------|-----------|--------|----------|\n| `sortable`             | Wizard   | Wizard | Wizard    | Wizard | N/A      |\n| `index_missing`        | Wizard   | Wizard | Wizard    | Wizard | N/A      |\n| `index_empty`          | Wizard   | Wizard | N/A       | N/A    | N/A      |\n| `no_index`             | Wizard   | Wizard | Wizard    | Wizard | N/A      |\n| `unf`                  | Wizard\\* | N/A    | Wizard\\*  | N/A    | N/A      |\n| `separator`            | N/A      | Wizard | N/A       | N/A    | N/A      |\n| `case_sensitive`       | N/A      | Wizard | N/A       | N/A    | N/A      |\n| `no_stem`              | Wizard   | N/A    | N/A       | N/A    | N/A      |\n| `weight`               | Wizard   | N/A    | N/A       | N/A    | N/A      |\n| `algorithm`            | N/A      | N/A    | N/A       | N/A    | Wizard   |\n| `datatype`             | N/A      | N/A    | N/A       | N/A    | Wizard   |\n| `distance_metric`      | N/A      | N/A    | N/A       | N/A    | Wizard   |\n| `m`, `ef_construction` | N/A      | N/A    | N/A       | N/A    | Wizard   |\n\n *\\* `unf` is only prompted when `sortable` is enabled.*\n\n**Manual Schema Patch Required:**\n\n| Attribute        | Notes                               |\n|------------------|-------------------------------------|\n| `withsuffixtrie` | Suffix/contains search optimization |\n\n*Note: `phonetic_matcher` is supported by the wizard for text fields.*\n\n**Example manual patch** for adding `index_missing` to a field:\n\n[code example]\n\n[code example]"},{"id":"json-path-for-nested-fields","title":"JSON Path for Nested Fields","role":"content","text":"When using JSON storage, use the `path` attribute to index nested fields:\n\n[code example]\n\nThe `name` becomes the field’s alias in queries, while `path` specifies where to find the data."},{"id":"complete-example","title":"Complete Example","role":"content","text":"[code example]\n\n**Learn more:** [Schema](https://redis.io/docs/latest/../api/schema) provides the complete API reference for all field types and attributes."}],"examples":[{"id":"sortable-ex0","language":"yaml","code":"# YAML\n- name: created_at\n  type: numeric\n  attrs:\n    sortable: true","section_id":"sortable"},{"id":"sortable-ex1","language":"python","code":"# Python\n{\"name\": \"created_at\", \"type\": \"numeric\", \"attrs\": {\"sortable\": True}}","section_id":"sortable"},{"id":"no_index-ex0","language":"yaml","code":"# YAML - Store for sorting but don't index for search\n- name: internal_score\n  type: numeric\n  attrs:\n    sortable: true\n    no_index: true","section_id":"no_index"},{"id":"index_missing-ex0","language":"yaml","code":"# YAML\n- name: optional_category\n  type: tag\n  attrs:\n    index_missing: true","section_id":"index_missing"},{"id":"index_missing-ex1","language":"python","code":"# Python\n{\"name\": \"optional_category\", \"type\": \"tag\", \"attrs\": {\"index_missing\": True}}","section_id":"index_missing"},{"id":"index_missing-ex2","language":"python","code":"from redisvl.query.filter import Tag\n\n# Find documents where category is missing\nfilter_expr = Tag(\"optional_category\").ismissing()","section_id":"index_missing"},{"id":"weight-ex0","language":"yaml","code":"- name: title\n  type: text\n  attrs:\n    weight: 2.0  # Title matches count double\n\n- name: description\n  type: text\n  attrs:\n    weight: 1.0  # Default weight","section_id":"weight"},{"id":"no_stem-ex0","language":"yaml","code":"- name: product_code\n  type: text\n  attrs:\n    no_stem: true","section_id":"no_stem"},{"id":"withsuffixtrie-ex0","language":"yaml","code":"- name: email\n  type: text\n  attrs:\n    withsuffixtrie: true","section_id":"withsuffixtrie"},{"id":"phonetic_matcher-ex0","language":"yaml","code":"- name: name\n  type: text\n  attrs:\n    phonetic_matcher: \"dm:en\"  # Double Metaphone, English","section_id":"phonetic_matcher"},{"id":"index_empty-ex0","language":"yaml","code":"- name: middle_name\n  type: text\n  attrs:\n    index_empty: true","section_id":"index_empty"},{"id":"unf-un-normalized-form-ex0","language":"yaml","code":"- name: title\n  type: text\n  attrs:\n    sortable: true\n    unf: true  # Keep original case for sorting","section_id":"unf-un-normalized-form"},{"id":"separator-ex0","language":"yaml","code":"- name: categories\n  type: tag\n  attrs:\n    separator: \"|\"  # Use pipe instead of comma","section_id":"separator"},{"id":"case_sensitive-ex0","language":"yaml","code":"- name: product_sku\n  type: tag\n  attrs:\n    case_sensitive: true","section_id":"case_sensitive"},{"id":"withsuffixtrie-1-ex0","language":"yaml","code":"- name: email_domain\n  type: tag\n  attrs:\n    withsuffixtrie: true","section_id":"withsuffixtrie-1"},{"id":"index_empty-1-ex0","language":"yaml","code":"- name: optional_tags\n  type: tag\n  attrs:\n    index_empty: true","section_id":"index_empty-1"},{"id":"unf-un-normalized-form-1-ex0","language":"yaml","code":"- name: price\n  type: numeric\n  attrs:\n    sortable: true\n    unf: true","section_id":"unf-un-normalized-form-1"},{"id":"geo-field-attributes-ex0","language":"yaml","code":"- name: location\n  type: geo\n  attrs:\n    sortable: true","section_id":"geo-field-attributes"},{"id":"vector-field-attributes-ex0","language":"yaml","code":"- name: embedding\n  type: vector\n  attrs:\n    algorithm: hnsw\n    dims: 768\n    distance_metric: cosine\n    datatype: float32\n    index_missing: true  # Handle documents without embeddings","section_id":"vector-field-attributes"},{"id":"vector-datatypes-ex0","language":"bash","code":"rvl migrate wizard --index my_index --url redis://localhost:6379\n# Select \"Update field\" > choose vector field > change datatype","section_id":"vector-datatypes"},{"id":"migration-support-ex0","language":"yaml","code":"# schema_patch.yaml\nversion: 1\nchanges:\n  update_fields:\n    - name: category\n      attrs:\n        index_missing: true","section_id":"migration-support"},{"id":"migration-support-ex1","language":"bash","code":"rvl migrate plan --index my_index --schema-patch schema_patch.yaml","section_id":"migration-support"},{"id":"json-path-for-nested-fields-ex0","language":"yaml","code":"- name: author_name\n  type: text\n  path: $.metadata.author.name\n  attrs:\n    sortable: true","section_id":"json-path-for-nested-fields"},{"id":"complete-example-ex0","language":"yaml","code":"version: \"0.1.0\"\nindex:\n  name: products\n  prefix: product\n  storage_type: json\n\nfields:\n  # Full-text searchable with high relevance\n  - name: title\n    type: text\n    path: $.title\n    attrs:\n      weight: 2.0\n      sortable: true\n\n  # Exact-match categories\n  - name: category\n    type: tag\n    path: $.category\n    attrs:\n      separator: \"|\"\n      index_missing: true\n\n  # Sortable price with range queries\n  - name: price\n    type: numeric\n    path: $.price\n    attrs:\n      sortable: true\n\n  # Store-only field for sorting\n  - name: internal_rank\n    type: numeric\n    path: $.internal_rank\n    attrs:\n      sortable: true\n      no_index: true\n\n  # Vector embeddings\n  - name: embedding\n    type: vector\n    path: $.embedding\n    attrs:\n      algorithm: hnsw\n      dims: 768\n      distance_metric: cosine\n\n  # Location search\n  - name: store_location\n    type: geo\n    path: $.location","section_id":"complete-example"}]}
{"schema_version":2,"id":"develop/ai/redisvl/concepts/index-migrations","title":"Index Migrations","url":"https://redis.io/docs/latest/develop/ai/redisvl/concepts/index-migrations/","summary":"","aliases":["/integrate/redisvl/concepts/index-migrations"],"tags":[],"last_updated":"2026-10-05T16:11:44+01:00","page_type":"content","content_hash":"645f130e62d676d417c9e0fe36cab36286439d399fb6b64c88fc7435012d6021","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!WARNING]\n> The index migrator is an **experimental** feature. APIs, CLI commands, and on-disk formats (plans, checkpoints, backups) may change in future releases. Review migration plans carefully before applying to production indexes.\n\nRedis Search indexes are immutable. To change an index schema, you must drop the existing index and create a new one. RedisVL provides a migration workflow that automates this process while preserving your data.\n\nThis page explains how migrations work and which changes are supported. For step by step instructions, see the [migration guide](https://redis.io/docs/latest/../user_guide/how_to_guides/migrate-indexes)."},{"id":"supported-and-blocked-changes","title":"Supported and blocked changes","role":"compatibility","text":"The migrator classifies schema changes into two categories:\n\n| Change                                                    | Status    |\n|-----------------------------------------------------------|-----------|\n| Add or remove a field                                     | Supported |\n| Rename a field                                            | Supported |\n| Change field options (sortable, separator)                | Supported |\n| Change key prefix                                         | Supported |\n| Rename the index                                          | Supported |\n| Change vector algorithm (FLAT, HNSW, SVS-VAMANA)          | Supported |\n| Change distance metric (COSINE, L2, IP)                   | Supported |\n| Tune algorithm parameters (M, EF_CONSTRUCTION)            | Supported |\n| Quantize vectors (float32 to float16/bfloat16/int8/uint8) | Supported |\n| Change vector dimensions                                  | Blocked   |\n| Change storage type (hash to JSON)                        | Blocked   |\n| Add a new vector field                                    | Blocked   |\n\n**Note:** INT8 and UINT8 vector datatypes require Redis 8.0+. SVS-VAMANA algorithm requires Redis 8.2+ and Intel AVX-512 hardware.\n\n**Supported** changes can be applied automatically using `rvl migrate`. The migrator handles the index rebuild and any necessary data transformations.\n\n**Blocked** changes require manual intervention because they involve incompatible data formats or missing data. The migrator will reject these changes and explain why."},{"id":"how-the-migrator-works","title":"How the migrator works","role":"content","text":"The migrator uses a plan first workflow:\n\n1. **Plan**: Capture the current schema, classify your changes, and generate a migration plan\n2. **Review**: Inspect the plan before making any changes\n3. **Apply**: Drop the index, transform data if needed, and recreate with the new schema\n4. **Validate**: Verify the result matches expectations\n\nThis separation ensures you always know what will happen before any changes are made."},{"id":"migration-mode-drop_recreate","title":"Migration mode: drop_recreate","role":"content","text":"The `drop_recreate` mode rebuilds the index in place while preserving your documents.\n\nThe process:\n\n1. Drop only the index structure (documents remain in Redis)\n2. For datatype changes, re-encode vectors to the target precision\n3. Recreate the index with the new schema\n4. Wait for Redis to re-index the existing documents\n5. Validate the result\n\n**Tradeoff**: The index is unavailable during the rebuild. Review the migration plan carefully before applying."},{"id":"index-only-vs-document-dependent-changes","title":"Index only vs document dependent changes","role":"content","text":"Schema changes fall into two categories based on whether they require modifying stored data.\n\n**Index only changes** affect how Redis Search indexes data, not the data itself:\n\n- Algorithm changes: The stored vector bytes are identical. Only the index structure differs.\n- Distance metric changes: Same vectors, different similarity calculation.\n- Adding or removing fields: The documents already contain the data. The index just starts or stops indexing it.\n\nThese changes complete quickly because they only require rebuilding the index.\n\n**Document dependent changes** require modifying the stored data:\n\n- Datatype changes (float32 to float16): Stored vector bytes must be re-encoded.\n- Field renames: Stored field names must be updated in every document.\n- Dimension changes: Vectors must be re-embedded with a different model.\n\nThe migrator handles datatype changes and field renames automatically. Dimension changes are blocked because they require re-embedding with a different model (application level logic)."},{"id":"vector-quantization","title":"Vector quantization","role":"content","text":"Changing vector precision from float32 to float16 reduces memory usage at the cost of slight precision loss. The migrator handles this automatically by:\n\n1. Reading all vectors from Redis\n2. Converting to the target precision\n3. Writing updated vectors back\n4. Recreating the index with the new schema\n\nTypical reductions:\n\n| Metric               | Value   |\n|----------------------|---------|\n| Index size reduction | ~50%    |\n| Memory reduction     | ~35%    |\n\nQuantization time is proportional to document count. Plan for downtime accordingly."},{"id":"vector-backups-mandatory-for-quantization","title":"Vector backups (mandatory for quantization)","role":"content","text":"Quantization mutates the raw bytes of every vector in place. If the\nmigration is interrupted partway through, or if the converted bytes turn\nout to be unacceptable for your application, there is no way to recover\nthe original precision from the quantized values. To make these\nmigrations safe to run, the migrator **always writes a vector backup\nbefore mutating any data** when a quantization step is needed.\n\nThere is no opt-out. The previous `--keep-backup` flag and any code path\nthat allowed quantizing without a backup have been removed."},{"id":"where-backups-are-written","title":"Where backups are written","role":"content","text":"Pass `--backup-dir <dir>` (CLI) or `backup_dir=\"<dir>\"` (Python API) to\nchoose the location. If you do not supply one, or if you pass an empty\nstring, the migrator raises a `ValueError` before any data is touched.\nThis argument is required for every migration apply. Quantization\nmigrations write `.header` and `.data` backup files there; multi-worker\nquantization also writes a `.manifest` file that lets the executor resume\nfrom worker shards after the source index has been dropped. Index-only\nmigrations record the resolved directory in the report but do not write\nvector backup files.\n\nEach hash index that mutates vector bytes produces backup files like:\n\n[code example]\n\nThe migration report records the resolved `backup_dir` and any backup file\nprefixes used for the run. For index-only migrations and JSON datatype\nchanges, the directory is still validated and recorded, but no vector backup\nfiles are written. Batch checkpoint state also records `backup_dir` so\n`batch-resume` can verify it is using the same recovery location.\n\nDisk usage is roughly `num_docs × dims × bytes_per_element`. For 1M\ndocuments with 768-dimensional float32 vectors that is approximately\n2.9 GB."},{"id":"what-backups-enable","title":"What backups enable","role":"content","text":"1. **Crash-safe resume.** If the executor dies mid-migration (process\n   killed, network drop, OOM), re-running the same command with the same\n   `--backup-dir` reads the header file, detects partial progress, and\n   resumes from the last completed batch instead of re-quantizing the\n   keys that already converted successfully. If the header is already\n   `completed`, the executor only treats it as a no-op resume when the live\n   index already matches the target schema. If the live index has been\n   rolled back to the source schema, the completed backup is stale for the\n   new run and the executor creates a fresh backup.\n2. **Manual rollback.** The data file contains the original\n   pre-quantization vector bytes. After a migration, you can use the\n   rollback CLI (`rvl migrate rollback`) or the Python API to restore\n   those bytes if you need to back out the change."},{"id":"retention","title":"Retention","role":"content","text":"Backup files are **retained on disk** after a successful migration.\nCleanup is now a deliberate operator action, performed only after the\nnew vectors have been verified and rollback is no longer needed. Delete\nthe backup directory manually when you are done."},{"id":"shared-keys-and-overlapping-indexes","title":"Shared keys and overlapping indexes","role":"content","text":"Hash vector quantization rewrites the vector bytes stored in the Redis\ndocument key. It is supported only when the documents being quantized are\nnot also indexed by another live RediSearch index that still expects the\nold vector datatype.\n\nIf the same Redis key is covered by multiple indexes, quantizing it for\none index mutates the bytes seen by all other indexes. Those other\nindexes are not migrated at the same time, so the document can disappear\nfrom those indexes or fail to re-index because the stored vector bytes no\nlonger match their schemas. The migrator does not support this topology\nfor hash vector datatype changes.\n\nBefore applying a quantization migration, verify that the migrating\nindex’s keyspace is exclusive for the vector field being changed. If\ndocuments must be searchable through multiple indexes, use a coordinated\napplication-level migration instead: create new physical keys or new\nvector fields, migrate every affected index schema together, and then\nswitch traffic after validation.\n\nFor batch migrations, `batch-plan` performs a conservative prefix overlap\ncheck across every applicable index. Two indexes whose key prefixes\noverlap (one prefix is a literal string-prefix of the other, matching\n`FT.CREATE PREFIX` semantics) are refused because a batch quantization\nmigration could re-read vectors that an earlier index in the batch has\nalready quantized. The error names the conflicting indexes and the\nspecific prefix pairs that overlap.\n\nThe batch overlap check is plan-time only — no data is mutated when a\nbatch is refused. Resolve by splitting the indexes into prefix-disjoint\ngroups and creating one batch plan per group. Indexes that are skipped\nfor other reasons (e.g. `applicable: false` because a field is missing)\ndo not participate in the check."},{"id":"why-some-changes-are-blocked","title":"Why some changes are blocked","role":"content","text":""},{"id":"vector-dimension-changes","title":"Vector dimension changes","role":"content","text":"Vector dimensions are determined by your embedding model. A 384 dimensional vector from one model is mathematically incompatible with a 768 dimensional index expecting vectors from a different model. There is no way to resize an embedding.\n\n**Resolution**: Re-embed your documents using the new model and load them into a new index."},{"id":"storage-type-changes","title":"Storage type changes","role":"content","text":"Hash and JSON have different data layouts. Hash stores flat key value pairs. JSON stores nested structures. Converting between them requires understanding your schema and restructuring each document.\n\n**Resolution**: Export your data, transform it to the new format, and reload into a new index."},{"id":"adding-a-vector-field","title":"Adding a vector field","role":"content","text":"Adding a vector field means all existing documents need vectors for that field. The migrator cannot generate these vectors because it does not know which embedding model to use or what content to embed.\n\n**Resolution**: Add vectors to your documents using your application, then run the migration."},{"id":"downtime-considerations","title":"Downtime considerations","role":"content","text":"With `drop_recreate`, your index is unavailable between the drop and when re-indexing completes.\n\n**CRITICAL**: Downtime requires both reads AND writes to be paused:\n\n| Requirement      | Reason                                                                                                         |\n|------------------|----------------------------------------------------------------------------------------------------------------|\n| **Pause reads**  | Index is unavailable during migration                                                                          |\n| **Pause writes** | Redis updates indexes synchronously. Writes during migration may conflict with vector re-encoding or be missed |\n\nPlan for:\n\n- Search unavailability during the migration window\n- Partial results while indexing is in progress\n- Resource usage from the re-indexing process\n- Quantization time if changing vector datatypes\n\nThe duration depends on document count, field count, and vector dimensions. For large indexes, consider running migrations during low traffic periods."},{"id":"sync-vs-async-execution","title":"Sync vs async execution","role":"content","text":"The migrator provides both synchronous and asynchronous execution modes."},{"id":"what-becomes-async-and-what-stays-sync","title":"What becomes async and what stays sync","role":"content","text":"The migration workflow has distinct phases. Here is what each mode affects:\n\n| Phase                 | Sync mode                        | Async mode                            | Notes                                    |\n|-----------------------|----------------------------------|---------------------------------------|------------------------------------------|\n| **Plan generation**   | `MigrationPlanner.create_plan()` | `AsyncMigrationPlanner.create_plan()` | Reads index metadata from Redis          |\n| **Schema snapshot**   | Sync Redis calls                 | Async Redis calls                     | Single `FT.INFO` command                 |\n| **Enumeration**       | FT.AGGREGATE (or SCAN fallback)  | FT.AGGREGATE (or SCAN fallback)       | Before drop, only if quantization needed |\n| **Drop index**        | `index.delete()`                 | `await index.delete()`                | Single `FT.DROPINDEX` command            |\n| **Quantization**      | Sequential HGET + HSET           | Sequential HGET + batched HSET        | Uses pre-enumerated keys                 |\n| **Create index**      | `index.create()`                 | `await index.create()`                | Single `FT.CREATE` command               |\n| **Readiness polling** | `time.sleep()` loop              | `asyncio.sleep()` loop                | Polls `FT.INFO` until indexed            |\n| **Validation**        | Sync Redis calls                 | Async Redis calls                     | Schema and doc count checks              |\n| **CLI interaction**   | Always sync                      | Always sync                           | User prompts, file I/O                   |\n| **YAML read/write**   | Always sync                      | Always sync                           | Local filesystem only                    |"},{"id":"when-to-use-sync-default","title":"When to use sync (default)","role":"content","text":"Sync execution is simpler and sufficient for most migrations:\n\n- Small to medium indexes (under 100K documents)\n- Index-only changes (algorithm, distance metric, field options)\n- Interactive CLI usage where blocking is acceptable\n\nFor migrations without quantization, the Redis operations are fast single commands. Sync mode adds no meaningful overhead."},{"id":"when-to-use-async","title":"When to use async","role":"content","text":"Async execution (`--async` flag) provides benefits in specific scenarios:\n\n**Large quantization jobs (1M+ vectors)**\n\nConverting float32 to float16 requires reading every vector, converting it, and writing it back. The async executor:\n\n- Enumerates documents using `FT.AGGREGATE WITHCURSOR` for index-specific enumeration (falls back to `SCAN` only if indexing failures exist)\n- Pipelines `HSET` operations in batches (100-1000 operations per pipeline is optimal for Redis)\n- Yields to the event loop between batches so other tasks can proceed\n\n**Large keyspaces (40M+ keys)**\n\nWhen your Redis instance has many keys and the index has indexing failures (requiring SCAN fallback), async mode yields between batches.\n\n**Async application integration**\n\nIf your application uses asyncio, you can integrate migration directly:\n\n[code example]"},{"id":"why-async-helps-with-quantization","title":"Why async helps with quantization","role":"content","text":"The migrator uses an optimized enumeration strategy:\n\n1. **Index-based enumeration**: Uses `FT.AGGREGATE WITHCURSOR` to enumerate only indexed documents (not the entire keyspace)\n2. **Fallback for safety**: If the index has indexing failures (`hash_indexing_failures > 0`), falls back to `SCAN` to ensure completeness\n3. **Enumerate before drop**: Captures the document list while the index still exists, then drops and quantizes\n\nThis optimization provides 10-1000x speedup for sparse indexes (where only a small fraction of prefix-matching keys are indexed).\n\n**Sync quantization:**\n\n[code example]\n\n**Async quantization:**\n\n[code example]\n\nEach `await` is a yield point where other coroutines can run. For millions of vectors, this prevents your application from freezing."},{"id":"what-async-does-not-improve","title":"What async does NOT improve","role":"content","text":"Async execution does not reduce:\n\n- **Total migration time**: Same work, different scheduling\n- **Redis server load**: Same commands execute on the server\n- **Downtime window**: Index remains unavailable during rebuild\n- **Network round trips**: Same number of Redis calls\n\nThe benefit is application responsiveness, not faster migration."},{"id":"learn-more","title":"Learn more","role":"related","text":"- [Migration guide](https://redis.io/docs/latest/../user_guide/how_to_guides/migrate-indexes): Step by step instructions\n- [Search and indexing](https://redis.io/docs/latest/search-and-indexing): How Redis Search indexes work"}],"examples":[{"id":"where-backups-are-written-ex0","language":"default","code":"<backup-dir>/\n  migration_backup_<index_name>.header   # JSON: phase, progress counters, field metadata\n  migration_backup_<index_name>.data     # Binary: length-prefixed batches of original vectors\n  migration_backup_<index_name>.manifest # JSON: multi-worker shard resume metadata, when workers > 1","section_id":"where-backups-are-written"},{"id":"when-to-use-async-ex0","language":"python","code":"import asyncio\nfrom redisvl.migration import AsyncMigrationPlanner, AsyncMigrationExecutor\n\nasync def migrate():\n    planner = AsyncMigrationPlanner()\n    plan = await planner.create_plan(\"myindex\", redis_url=\"redis://localhost:6379\")\n\n    executor = AsyncMigrationExecutor()\n    report = await executor.apply(\n        plan,\n        redis_url=\"redis://localhost:6379\",\n        backup_dir=\"/tmp/migration_backups\",\n    )\n\nasyncio.run(migrate())","section_id":"when-to-use-async"},{"id":"why-async-helps-with-quantization-ex0","language":"default","code":"enumerate keys (FT.AGGREGATE or SCAN) -> store list\nfor each batch of 500 keys:\n    for each key:\n        HGET field (blocks)\n        convert array\n        pipeline.HSET(field, new_bytes)\n    pipeline.execute() (blocks)","section_id":"why-async-helps-with-quantization"},{"id":"why-async-helps-with-quantization-ex1","language":"default","code":"enumerate keys (FT.AGGREGATE or SCAN) -> store list\nfor each batch of 500 keys:\n    for each key:\n        await HGET field (yields)\n        convert array\n        pipeline.HSET(field, new_bytes)\n    await pipeline.execute() (yields)","section_id":"why-async-helps-with-quantization"}]}
{"schema_version":2,"id":"develop/ai/redisvl/concepts","title":"Concepts","url":"https://redis.io/docs/latest/develop/ai/redisvl/concepts/","summary":"","aliases":["/integrate/redisvl/concepts"],"tags":[],"last_updated":"2026-06-08T15:46:44+02:00","children":[{"id":"develop/ai/redisvl/concepts/architecture","summary":"","title":"Architecture","url":"https://redis.io/docs/latest/develop/ai/redisvl/concepts/architecture/"},{"id":"develop/ai/redisvl/concepts/extensions","summary":"","title":"Extensions","url":"https://redis.io/docs/latest/develop/ai/redisvl/concepts/extensions/"},{"id":"develop/ai/redisvl/concepts/field-attributes","summary":"","title":"Field Attributes","url":"https://redis.io/docs/latest/develop/ai/redisvl/concepts/field-attributes/"},{"id":"develop/ai/redisvl/concepts/index-migrations","summary":"","title":"Index Migrations","url":"https://redis.io/docs/latest/develop/ai/redisvl/concepts/index-migrations/"},{"id":"develop/ai/redisvl/concepts/queries","summary":"","title":"Query Types","url":"https://redis.io/docs/latest/develop/ai/redisvl/concepts/queries/"},{"id":"develop/ai/redisvl/concepts/mcp","summary":"","title":"RedisVL MCP","url":"https://redis.io/docs/latest/develop/ai/redisvl/concepts/mcp/"},{"id":"develop/ai/redisvl/concepts/search-and-indexing","summary":"","title":"Search & Indexing","url":"https://redis.io/docs/latest/develop/ai/redisvl/concepts/search-and-indexing/"},{"id":"develop/ai/redisvl/concepts/utilities","summary":"","title":"Utilities","url":"https://redis.io/docs/latest/develop/ai/redisvl/concepts/utilities/"}],"page_type":"content","content_hash":"c0192f27956af769fd05a1950f3591296569ded7a685cad54fee1af811f12e7e","sections":[{"id":"content","title":"Content","role":"content","text":"Foundational knowledge for building AI applications with RedisVL. These concepts are language-agnostic and apply across all RedisVL implementations.\n\n<div class=\"grid grid-cols-1 md:grid-cols-2 gap-4 my-6\">\n<a href=\"architecture/\" class=\"block p-5 border border-redis-pen-300 rounded-lg hover:border-redis-red-500 hover:shadow-md transition-all duration-200 no-underline hover:no-underline\"><h3 class=\"mt-0 mb-2\">🏗️ Architecture</h3><p>How RedisVL components connect: schemas, indexes, queries, and extensions.</p></a>\n<a href=\"search-and-indexing/\" class=\"block p-5 border border-redis-pen-300 rounded-lg hover:border-redis-red-500 hover:shadow-md transition-all duration-200 no-underline hover:no-underline\"><h3 class=\"mt-0 mb-2\">🔍 Search & Indexing</h3><p>Schemas, fields, documents, storage types, and query patterns.</p></a>\n<a href=\"index-migrations/\" class=\"block p-5 border border-redis-pen-300 rounded-lg hover:border-redis-red-500 hover:shadow-md transition-all duration-200 no-underline hover:no-underline\"><h3 class=\"mt-0 mb-2\">🔄 Index Migrations</h3><p>How RedisVL handles migration planning, rebuilds, and future shadow migration.</p></a>\n<a href=\"field-attributes/\" class=\"block p-5 border border-redis-pen-300 rounded-lg hover:border-redis-red-500 hover:shadow-md transition-all duration-200 no-underline hover:no-underline\"><h3 class=\"mt-0 mb-2\">🏷️ Field Attributes</h3><p>Configure sortable, no_index, index_missing, and other field options.</p></a>\n<a href=\"queries/\" class=\"block p-5 border border-redis-pen-300 rounded-lg hover:border-redis-red-500 hover:shadow-md transition-all duration-200 no-underline hover:no-underline\"><h3 class=\"mt-0 mb-2\">🔎 Query Types</h3><p>Vector, filter, text, hybrid, and multi-vector query options.</p></a>\n<a href=\"utilities/\" class=\"block p-5 border border-redis-pen-300 rounded-lg hover:border-redis-red-500 hover:shadow-md transition-all duration-200 no-underline hover:no-underline\"><h3 class=\"mt-0 mb-2\">🔧 Utilities</h3><p>Vectorizers for embeddings and rerankers for result optimization.</p></a>\n<a href=\"mcp/\" class=\"block p-5 border border-redis-pen-300 rounded-lg hover:border-redis-red-500 hover:shadow-md transition-all duration-200 no-underline hover:no-underline\"><h3 class=\"mt-0 mb-2\">🧠 MCP</h3><p>How RedisVL exposes an existing Redis index to MCP clients through a stable tool contract.</p></a>\n<a href=\"extensions/\" class=\"block p-5 border border-redis-pen-300 rounded-lg hover:border-redis-red-500 hover:shadow-md transition-all duration-200 no-underline hover:no-underline\"><h3 class=\"mt-0 mb-2\">🧩 Extensions</h3><p>Pre-built patterns: caching, message history, and semantic routing.</p></a>\n</div>"}],"examples":[]}
{"schema_version":2,"id":"develop/ai/redisvl/concepts/mcp","title":"RedisVL MCP","url":"https://redis.io/docs/latest/develop/ai/redisvl/concepts/mcp/","summary":"","aliases":["/integrate/redisvl/concepts/mcp"],"tags":[],"last_updated":"2026-10-12T09:34:21+02:00","page_type":"content","content_hash":"ca71a7f700640bb645a39c21bda5a2ad54fa3ab7d58c6305c8228ba002b7930d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"RedisVL includes an MCP server that exposes a Redis-backed retrieval surface through a small, deterministic tool contract. It is designed for AI applications that want to search or maintain data in one or more existing Redis indexes without each client reimplementing Redis query logic."},{"id":"what-redisvl-mcp-does","title":"What RedisVL MCP Does","role":"content","text":"The RedisVL MCP server sits between an MCP client and Redis:\n\n1. It connects to one or more existing Redis Search indexes.\n2. It inspects each index at startup and reconstructs its schema.\n3. It initializes vector capabilities only when the configured search or upsert behavior needs them.\n4. It exposes stable MCP tools for discovery, search, and optionally upsert.\n\nThis keeps each Redis index as the source of truth for its search behavior while giving MCP clients a predictable interface."},{"id":"how-redisvl-mcp-runs","title":"How RedisVL MCP Runs","role":"content","text":"RedisVL MCP works with a focused model:\n\n- One server process binds to one *or several* existing Redis indexes, each addressed by a logical id.\n- The server supports stdio (default), Streamable HTTP, and SSE transports.\n- Search behavior is owned by per-index configuration, not by MCP callers.\n- Vector search and server-side embedding are optional capabilities configured explicitly per index.\n- Upsert is optional and can be disabled globally with read-only mode or per index with a `read_only` flag.\n\nA single-index server remains the simplest deployment: when exactly one index is configured, callers can omit the index selector entirely and every tool call targets that index. Multi-index support is fully formal — it adds discovery and explicit routing without changing the single-index contract."},{"id":"config-owned-search-behavior","title":"Config-Owned Search Behavior","role":"content","text":"MCP callers can control:\n\n- `query`\n- `limit`\n- `offset`\n- `filter`\n- `return_fields`\n\nThese request-time controls are still bounded by runtime config. In particular,\ndeep paging is limited by a configured maximum result window, enforced as\n`offset + limit`.\n\nOn a multi-index server, callers also choose **which index to target** through an optional `index` argument (see [Index Selection]()). Callers do not choose:\n\n- whether retrieval is `vector`, `fulltext`, or `hybrid`\n- query tuning parameters such as hybrid fusion or vector runtime settings\n\nThat behavior lives in the per-index server config under `indexes.<id>.search`. The response includes `search_type` as informational metadata, but it is not a request parameter."},{"id":"single-and-multiple-index-bindings","title":"Single and Multiple Index Bindings","role":"content","text":"The YAML config uses an `indexes` mapping. Each entry is a logical binding keyed by an id (for example `knowledge` or `tickets`) that points to an existing Redis index through `redis_name`. The mapping may contain one entry or several; each binding is inspected, validated, and given its own search config, runtime limits, and optional vectorizer independently at startup. Startup is all-or-nothing — if any binding fails to initialize, the server does not start.\n\nA single-binding config is the simplest case and behaves exactly as before: the lone binding is the implicit target of every call. With multiple bindings the server stays a single process and endpoint, but callers select a binding per call."},{"id":"index-selection-and-discovery","title":"Index Selection and Discovery","role":"content","text":"On a multi-index server, every tool call must say which logical index it targets:\n\n- `search-records` and `upsert-records` accept an optional `index` argument naming the logical id.\n- When exactly one index is configured, `index` may be omitted and resolves to that sole binding (backward compatible).\n- When multiple indexes are configured, omitting `index` is an `invalid_request`; the caller must name one.\n- An unknown logical id is an `invalid_request`.\n- Both tools echo the resolved `index` in their response so clients can confirm routing.\n\nBecause a client cannot guess the configured logical ids, multi-index servers expose a `list-indexes` discovery tool. **Clients should call `list-indexes` first** to enumerate the available indexes and their filterable fields, then pass the chosen id as `index` on subsequent calls."},{"id":"schema-inspection-and-overrides","title":"Schema Inspection and Overrides","role":"content","text":"RedisVL MCP is inspection-first:\n\n- the Redis index must already exist\n- the server reconstructs the schema from Redis metadata at startup\n- runtime field mappings remain explicit in config\n\nIn some environments, Redis metadata can be incomplete for vector field attributes. When that happens, `schema_overrides` can patch missing attrs for fields that were already discovered. It does not create new fields or change discovered field identity.\n\nStartup also validates that the inspected schema does not collide with\nMCP-reserved score metadata field names for the configured search mode."},{"id":"read-only-and-read-write-modes","title":"Read-Only and Read-Write Modes","role":"content","text":"RedisVL MCP registers `search-records` and `list-indexes` by default (see [Custom Tool Profiles]() for turning a built-in off deliberately).\n\nWrite availability is enforced at two levels:\n\n- **Global read-only mode** disables writes across every binding. It is controlled by the CLI flag `--read-only` or the environment variable `REDISVL_MCP_READ_ONLY=true`.\n- **Per-index read-only** disables writes for a single binding via `indexes.<id>.read_only: true`, while other bindings stay writable.\n\nThese combine into each binding’s *effective* write availability: a binding is read-only if global read-only is on **or** that binding sets `read_only: true`. The `upsert-records` tool is registered only when at least one binding is writable, so a fully read-only server does not advertise it at all. When the tool is registered, a write to a read-only binding is rejected with `forbidden` before any data is changed. `list-indexes` reports each binding’s effective write availability as `upsert_available`.\n\nUse read-only mode when Redis is serving approved content to assistants and another system owns ingestion — globally when no binding should accept writes, or per index when only some indexes are writable."},{"id":"authentication-and-authorization","title":"Authentication and Authorization","role":"security","text":"The HTTP transports can require a JWT bearer token issued by an existing identity provider. The server validates the token signature, issuer, and audience, and can gate read vs write by scope or role claim. A custom tool profile can also scope every query to a tenant carried in the token; see [Tenant Scoping From Token Claims](). What the server does not do is map token claims to Redis ACL users or to separate indexes, which remains a gateway concern. The `stdio` transport is local and is never authenticated.\n\nFor configuration and the gateway boundary, see [Authenticate RedisVL MCP](https://redis.io/docs/latest/../user_guide/how_to_guides/mcp_authentication)."},{"id":"tool-surface","title":"Tool Surface","role":"content","text":"RedisVL MCP exposes up to three built-in tools, plus any configured [custom tool profiles]():\n\n- `list-indexes` enumerates the configured logical indexes for discovery\n- `search-records` searches a selected index using that index’s server-owned search mode\n- `upsert-records` validates and upserts records into a selected writable index, embedding them only when that capability is configured\n\nAny of the three can be turned off with `server.builtin_tools`, independently of whether custom tools are configured — useful for a server that should only ever read, or one that serves nothing but curated profiles:\n\n[code example]\n\nOnly the three names above are accepted; anything else fails at startup rather than being silently ignored.\n\nDisabling a built-in adjusts what the rest of the surface advertises, so the published contract never points at something the server withholds:\n\n- `list-indexes` reports `upsert_available: false` for every binding when `upsert-records` is disabled, since a writable binding still cannot be written to through a tool that is not published.\n- On a multi-index server with `list-indexes` disabled, every tool that requires an `index` — `search-records` and `upsert-records` alike — names the available index ids in its own description instead of deferring to a discovery tool that does not exist. That server still logs a startup warning naming the affected tools, because inlining the ids is a fallback rather than an endorsement of the shape.\n\nA server whose tool set ends up unusable — no tools at all, or discovery disabled on a multi-index server — logs a warning at startup.\n\nTools register once per process. `builtin_tools` is re-read on restart, but the registered tool set is not rebuilt, so a stop/start against an edited config keeps the previous tools and logs a warning saying so. Start a new process to change the tool surface.\n\nThese built-in tools follow a stable contract (profiles differ where noted in [Custom Tool Profiles]() — notably they accept the object filter form only):\n\n- request validation happens before query or write execution\n- the resolved logical `index` is echoed in every `search-records` and `upsert-records` response\n- filters support either raw strings or a RedisVL-backed JSON DSL\n- on a single-index server, `search-records` describes the inspected schema by advertising typed JSON DSL filter fields, object-filter `exists` support, and valid `return_fields`; on a multi-index server those hints are ambiguous, so the description instead directs clients to call `list-indexes` and pass `index`\n- error codes are mapped into a stable set of MCP-facing categories"},{"id":"list-indexes","title":"`list-indexes`","role":"content","text":"`list-indexes` returns one entry per configured binding so clients can route subsequent calls. Each entry reports:\n\n- the logical `id`\n- an optional `description` (only when configured)\n- `upsert_available`, reflecting the binding’s effective write availability\n- `fields`, the filterable fields discovered from the index\n- `limits`, only the runtime limits that were explicitly configured\n\nThe discovery payload is deliberately minimal:\n\n- the underlying Redis index name (`redis_name`) is **never** exposed\n- the vector field and the configured embed-source text field are **omitted** from `fields`, since they are implementation inputs rather than fields a client filters on\n- `limits` shows only explicitly set values (such as `max_limit` or `max_upsert_records`); defaults are not echoed"},{"id":"custom-tool-profiles","title":"Custom Tool Profiles","role":"content","text":"The built-in tools expose the index generically, which leaves the model doing query engineering on every call: pick the index, understand the schema, build a filter, choose return fields. A **profile** moves those decisions into config. It is `search-records` with some arguments pre-filled and frozen and the rest still exposed, published under a name and description of your choosing.\n\nProfiles are pure configuration. You add a `custom_tools` entry to the same YAML the server already loads and restart it; there is no Python to write.\n\n[code example]\n\n`lock` holds what the author decides; `params` holds what the model may still pass. Anything not listed in `params` stays exposed, so a profile that only locks a filter keeps the rest of the built-in’s contract. `index` is pinned by the top-level `index:` key rather than exposed as a param, and may be omitted only when exactly one index is configured.\n\n`params.limit.max` bounds the result count. It applies whether the model names a limit or leaves it out — an omitted limit is capped rather than falling through to the binding’s default. An explicit request above the cap is rejected. When `limit` is hidden (`expose: false`), the cap becomes the fixed result count instead. The cap must not exceed the binding’s own `runtime.max_limit`, which is checked at startup. Note that it bounds page size, not total reachable data: `offset` is a separate argument, so paging is still possible up to the binding’s `max_result_window`.\n\n`suppress_schema_hints: true` drops the auto-generated field hints from the tool description. Those hints enumerate filterable and returnable fields, which is noise once those arguments are locked — and by default they are appended only for arguments the model can still use.\n\nTwo things about filters are easy to conflate:\n\n- `lock.filter` is an ordinary expression in the JSON filter DSL. Its `and`/`or`/`not` operators describe the locked filter’s own content.\n- How the locked filter combines with a model-supplied one is fixed and not configurable: the executed query is always `locked AND caller`. A compound model-supplied expression renders parenthesized, so its `or`/`not` nests *inside* the locked AND and cannot reach the top level — the model can only narrow within the locked scope and never widen past it.\n\nFor that reason a profile accepts only the **object** form of a filter from the model. A raw filter string is rejected both by the advertised schema and by the tool itself, because strings bypass the DSL’s field validation and have no safe composition with a locked expression.\n\nStructure is only half of it: the nesting guarantee holds only while every filter *value* stays inside its own clause. Text `eq`/`ne` values are handled by the `Text` filter itself, which renders them as a quoted phrase with any `\"` or `\\` replaced by a space, so a parenthesis or a `|` the value carries is literal text rather than syntax. Text `like` values are patterns, so the library leaves them raw and this boundary escapes them instead — the delimiters that would close the clause are escaped, the pattern metacharacters are not. Tag values have their delimiters escaped and numeric values are type-checked. A caller filter that still renders as something able to break out is refused rather than combined.\n\nProfiles resolve to a built-in call and nothing more, so they inherit the concurrency cap, request timeout, read-only policy, auth scoping, and error mapping already applied to `search-records`.\n\nBecause adding near-duplicate tools makes tool selection harder rather than easier, built-ins that curated profiles supersede can be turned off with `server.builtin_tools` (see [Tool Surface]()).\n\nMisconfiguration fails at startup rather than at the first call. Among the checks: a name colliding with a built-in or using a reserved `redisvl-`/`redisvl_` prefix; a duplicate tool name; a missing or unknown `index`; a `params` key that is not a real argument; `max` on anything but `limit`, or a cap above the binding’s `max_limit`; hiding `query`; locking `return_fields` while also exposing them; and a locked filter or projection naming a field the bound index does not have. Unrecognized keys are rejected too, so a typo in `lock` fails loudly instead of silently producing a tool that reads as locked but enforces nothing."},{"id":"tenant-scoping-from-token-claims","title":"Tenant Scoping From Token Claims","role":"content","text":"When several tenants share one index, separated by a field such as `org_id`, exposing `search-records` makes the tenant boundary depend on the model remembering to pass a filter. One forgotten filter is a cross-tenant read. A profile can remove that knob: `lock.inject` reads the tenant from the caller’s verified token and AND-combines it into every query the profile runs.\n\n[code example]\n\n`field` and `claim` are independent names: `field` is what the index schema calls the tenant column, and `claim` is what the identity provider calls it. A token carrying `\"https://acme.example/org\": \"acme\"` makes every query from that caller run as `@org_id:{acme} AND <everything else>`.\n\nThe model cannot set the injected value. The field is absent from the tool’s input schema, a call that names it is rejected, and it is left out of the field hints appended to the tool description. It is not hidden outright: unless `lock.return_fields` excludes it, results carry the field, and `list-indexes` describes the whole schema. What the model sees there is only ever its own tenant. If the model filters on the field, its clause ANDs with the injected one, so it can narrow within its own tenant and naming another tenant matches nothing. The rest of the profile works as before, so a static `lock.filter` on another field and a model-supplied filter both still apply.\n\n#### What Counts as a Usable Claim\n\nThe claim must be a single, non-empty string. Anything else refuses the request with a `forbidden` error, and no query runs:\n\n| Claim value                                  | Why it is refused                                                                                                                 |\n|----------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------|\n| Absent, or the request has no token          | There is no tenant to scope to.                                                                                                   |\n| `null` or `\"\"`                               | An empty tag value would drop the tenant clause from the query entirely.                                                          |\n| A list, such as `[\"acme\", \"victim\"]`         | It would render as a union, `@org_id:{acme|victim}`, which spans both tenants.                                                    |\n| An object, number or boolean                 | It is not a tenant identifier.                                                                                                    |\n| Padded with whitespace                       | It cannot be a real tenant identifier, and refusing is safer than guessing which tenant was meant.                                |\n| Containing a control character or a backtick | The query parser splits a tag term on these, so a value such as `acme` followed by a control character matches the tenant `acme`. |\n\nA `|` inside a single string is accepted. It is escaped, so an identifier such as the Auth0 subject `auth0|64f1c2` matches only its own documents.\n\nA list is refused because of its type, not because of what it renders as. The union it produces is indistinguishable from one a caller could legitimately ask for, so no inspection of the finished query could catch it.\n\nWith several `inject` entries, every entry ANDs into the query, and one unusable claim refuses the whole request rather than narrowing by the entries that did resolve.\n\n#### What Fails at Startup\n\nInjection is checked at startup wherever the configuration alone can show it would not hold:\n\n- authentication is not enabled, on any transport, including an unauthenticated loopback HTTP bind and any `--allow-unauthenticated` bind;\n- authentication is configured but the server runs over `stdio`, which is never authenticated (checked when the server starts through `rvl mcp` or `run_async`; an embedder that calls `startup()` directly is not, and every call is then refused at request time instead);\n- the Redis index is also reachable without the tenant scope: through `search-records`, through `upsert-records` unless every binding over it is read-only, or through another custom tool on it that does not inject exactly the same entries, since a tool scoped by another field, or by the same field from another claim, reads across the tenants this one separates. Bindings that share a `redis_name` count as one index;\n- the injected field is absent from the bound index, is not a tag field, or is declared `NOINDEX`;\n- an `inject` list is empty, names one field twice, or names a field that `lock.filter` also constrains;\n- `required` is anything but `true`, or `from` is anything but `claim`.\n\nAn injected field must be a tag. Text equality is a phrase match over tokenised text, and text is tokenised on punctuation, so the phrase `acme-corp` would also match `acme-corp-eu`.\n\nThe tool set registers once per process. If a restart reloads a configuration that differs from the registered one, the server normally logs a warning and keeps the old tools. When injection is configured on either side of the change, startup fails instead, because keeping the old tenant scoping in force is not something a log line should report.\n\n#### Threat Model\n\nThe guarantee is narrow: a client presenting a validly signed token cannot make the model widen or escape the tenant scope carried in that token. The trust boundary is the identity provider, not the MCP client, so the guarantee holds only while these hold:\n\n- The token is genuinely verified. Use a real signing key and an asymmetric algorithm. The server refuses to start an injecting profile without authentication, but it does not check which algorithm you configured.\n- The identity provider assigns the claim. If a tenant can mint its own token, or set the claim itself, nothing here stops it reading another tenant’s data.\n- The config names every route to the tenant data. The startup check compares bindings by `redis_name`, so it does not see an index alias, or a second index built over the same key prefix. Either one is another route to the same documents, and a tool on it needs the same `lock.inject`.\n- Only trusted ingestion writes the index. The server refuses `upsert-records` on a writable scoped index, because a write can retag another tenant’s document as the writer’s own. Whatever loads documents outside the server is inside the trust boundary.\n- Every document carries exactly its tenant. Stamp the tenant field on each document, indexed as a tag, with the identifier exactly as the identity provider emits it. Redis normalises the stored value, not the claim: it splits it on the field’s separator (`,` by default), so a document stamped `acme,victim` belongs to both tenants; it trims surrounding whitespace; and on JSON storage it indexes every element of an array. A document without the field matches no tenant, so it is invisible rather than shared.\n- Tenant identifiers differ by more than case. Tag fields fold case unless declared `CASESENSITIVE`, and the folding is Unicode-wide: `Acme` and `acme` are one tenant, and so are a Kelvin sign and `K`, or composed and decomposed forms of an accented letter. The server warns at startup when an injected field is not case-sensitive.\n\nWhere tenants share one index, the injected filter is the only isolation boundary. There is no Redis ACL or keyspace separation behind it, so a defect in filter combination or claim validation is a full cross-tenant read. If you need defence in depth, separate tenants at the Redis layer as well.\n\nListing the tenant claim under `auth.required_claims` is a cheap outer layer: the verifier then rejects a token that lacks the claim before any tool runs. That check confirms only that the claim is present. Its value is still validated by the profile on every call."},{"id":"why-use-mcp-instead-of-direct-redisvl-calls","title":"Why Use MCP Instead of Direct RedisVL Calls","role":"content","text":"Use RedisVL MCP when you want a standard tool boundary for agent frameworks or assistants that already speak MCP.\n\nUse direct RedisVL client code when your application should own index lifecycle, search construction, data loading, or richer RedisVL features directly in Python.\n\nRedisVL MCP is a good fit when:\n\n- multiple assistants should share one approved retrieval surface\n- you want search behavior fixed by deployment config\n- you need a read-only or tightly controlled write boundary\n- you want to reuse an existing Redis index without rebuilding retrieval logic in every client\n\nFor setup steps, config, commands, and examples, see [Run RedisVL MCP](https://redis.io/docs/latest/../user_guide/how_to_guides/mcp)."}],"examples":[{"id":"tool-surface-ex0","language":"yaml","code":"server:\n  builtin_tools:\n    upsert-records: disabled","section_id":"tool-surface"},{"id":"custom-tool-profiles-ex0","language":"yaml","code":"custom_tools:\n  - name: search-support-tickets\n    based_on: search-records\n    index: support_tickets\n    description: >\n      Search historical customer support tickets by semantic similarity.\n      Use this to find prior resolutions for a customer problem.\n    lock:\n      return_fields: [subject, resolution, created_at]\n      filter: { field: status, op: eq, value: resolved }\n    params:\n      limit: { expose: true, max: 20 }\n      filter: { expose: true }","section_id":"custom-tool-profiles"},{"id":"tenant-scoping-from-token-claims-ex0","language":"yaml","code":"custom_tools:\n  - name: search-customer-kb\n    index: customer_kb\n    description: Search this customer's knowledge base.\n    lock:\n      inject:\n        - field: org_id                      # the tenant field in the index schema\n          from: claim                        # the value comes from the verified token\n          claim: \"https://acme.example/org\"  # the claim name your identity provider emits\n          required: true","section_id":"tenant-scoping-from-token-claims"}]}
{"schema_version":2,"id":"develop/ai/redisvl/concepts/queries","title":"Query Types","url":"https://redis.io/docs/latest/develop/ai/redisvl/concepts/queries/","summary":"","aliases":["/integrate/redisvl/concepts/queries"],"tags":[],"last_updated":"2026-10-05T16:11:44+01:00","page_type":"content","content_hash":"1b534502d8574b89a24095ee5443440b6377516a7df3cbd2d510b3765166a139","sections":[{"id":"overview","title":"Overview","role":"overview","text":"RedisVL provides several query types, each optimized for different search patterns. Understanding when to use each helps you build efficient search applications."},{"id":"vector-queries","title":"Vector Queries","role":"content","text":"Vector queries find documents by semantic similarity. You provide a vector (typically an embedding of text or images), and Redis returns documents whose vectors are closest to yours."},{"id":"vectorquery","title":"VectorQuery","role":"content","text":"The most common query type. Returns the top K most similar documents using KNN (K-Nearest Neighbors) search.\n\n[code example]\n\nUse when you want to find the N most similar items regardless of how similar they actually are. Good for \"find me things like this\" searches."},{"id":"vectorrangequery","title":"VectorRangeQuery","role":"content","text":"Returns all documents within a specified distance threshold. Unlike VectorQuery, this doesn’t limit results to a fixed K—it returns everything within the radius.\n\n[code example]\n\nUse when similarity threshold matters more than result count. Good for \"find everything similar enough\" searches, like deduplication or clustering."},{"id":"filter-queries","title":"Filter Queries","role":"content","text":"Filter queries find documents by exact field matching without vector similarity."},{"id":"filterquery","title":"FilterQuery","role":"content","text":"Searches using filter expressions on indexed fields. Supports tag matching, numeric ranges, text search, and geographic filters.\n\n[code example]\n\nUse when you need precise filtering without semantic similarity—finding all products in a category, all users in a region, or all records within a date range."},{"id":"countquery","title":"CountQuery","role":"content","text":"Returns only the count of matching documents, not the documents themselves. More efficient than FilterQuery when you only need the count.\n\n[code example]\n\nUse for analytics, pagination totals, or checking if matches exist before running a full query."},{"id":"text-queries","title":"Text Queries","role":"content","text":"Text queries perform full-text search with relevance scoring."},{"id":"textquery","title":"TextQuery","role":"content","text":"Searches text fields using Redis’s full-text search capabilities. Supports multiple scoring algorithms (BM25, TF-IDF), stopword handling, and field weighting.\n\n[code example]\n\nUse when you need keyword-based search with relevance ranking—traditional search engine behavior where exact word matches matter."},{"id":"hybrid-queries","title":"Hybrid Queries","role":"content","text":"Hybrid queries combine multiple search strategies for better results than either alone."},{"id":"hybridquery","title":"HybridQuery","role":"content","text":"Combines text search and vector search in a single query using Redis’s native hybrid search. Supports multiple fusion methods:\n\n- **RRF (Reciprocal Rank Fusion)**: Combines rankings from both searches. Good when you trust both signals equally.\n- **Linear**: Weighted combination of scores. Good when you want to tune the balance between text and semantic relevance.\n\n[code example]\n\nUse when neither pure keyword search nor pure semantic search gives good enough results. Common in RAG applications where you want both exact matches and semantic understanding.\n\n> [!NOTE]\n> HybridQuery requires Redis >= 8.4.0 and redis-py >= 7.1.0."},{"id":"aggregatehybridquery","title":"AggregateHybridQuery","role":"content","text":"Similar to HybridQuery but uses Redis aggregation pipelines. Provides more control over score combination and result processing.\n\nUse when you need custom score normalization or complex result transformations that HybridQuery doesn’t support."},{"id":"multi-vector-queries","title":"Multi-Vector Queries","role":"content","text":""},{"id":"multivectorquery","title":"MultiVectorQuery","role":"content","text":"Searches across multiple vector fields simultaneously with configurable weights per field.\n\n[code example]\n\nUse for multimodal search—finding documents that match across text embeddings, image embeddings, and other vector representations. Each vector field can have different importance weights."},{"id":"sql-queries","title":"SQL Queries","role":"content","text":""},{"id":"sqlquery","title":"SQLQuery","role":"content","text":"Translates SQL SELECT statements into Redis queries. Provides a familiar interface for developers coming from relational databases.\n\n[code example]\n\n`SQLQuery` also accepts `sql_redis_options`, which are forwarded to the\nunderlying `sql-redis` executor. This is mainly useful for tuning schema\ncaching behavior.\n\n[code example]\n\n- `\"lazy\"` (default) loads schemas only when a query touches an index, which\n  keeps startup and one-off queries cheaper.\n- `\"load_all\"` preloads all schemas up front, which can help repeated query\n  workloads that span many indexes.\n\nFor TEXT fields with `sql-redis >= 0.4.0`:\n\n- `=` performs exact phrase or exact-term matching\n- `LIKE` performs prefix/suffix/contains matching using SQL `%` wildcards\n- `fuzzy(field, 'term')` performs typo-tolerant matching\n- `fulltext(field, 'query')` performs tokenized search\n\n[code example]\n\nUse `=` when you want an exact phrase, `LIKE` for prefix/suffix/contains\npatterns, `fuzzy()` for typo-tolerant lookup, and `fulltext()` for tokenized\nsearch operators such as `OR`, optional terms, or proximity.\n\n**Aggregations and grouping:**\n\n[code example]\n\n**Geographic queries** with `geo_distance()`:\n\n[code example]\n\n**Date queries** with ISO date literals and date functions:\n\n[code example]\n\n**Vector similarity search** with parameters:\n\n[code example]\n\n**Hybrid search (FT.HYBRID)** fuses a text query and a vector query server-side\nwith `hybrid_vector_search()`, composing `cosine_distance()` (vector leg) and\n`fulltext()` (text leg) with `rrf()` or `linear()` fusion. This is the SQL\nfront-end to the native `HybridQuery` (above):\n\n[code example]\n\nRequires Redis 8.4+ and `redis-py >= 7.1.0`.\n\nUse when your team is more comfortable with SQL syntax, or when integrating with tools that generate SQL.\n\n> [!NOTE]\n> SQLQuery requires the optional `sql-redis` package. Install with: `pip install redisvl[sql-redis]`\n\nFor comprehensive examples including geographic filtering, date functions, and vector search, see the [SQL to Redis Queries guide](https://redis.io/docs/latest/../user_guide/how_to_guides/sql_to_redis_queries)."},{"id":"choosing-the-right-query","title":"Choosing the Right Query","role":"content","text":"| Use Case                                   | Query Type       |\n|--------------------------------------------|------------------|\n| Semantic similarity search                 | VectorQuery      |\n| Find all items within similarity threshold | VectorRangeQuery |\n| Exact field matching                       | FilterQuery      |\n| Count matching records                     | CountQuery       |\n| Keyword search with relevance              | TextQuery        |\n| Combined keyword + semantic                | HybridQuery      |\n| Multimodal search                          | MultiVectorQuery |\n| SQL-familiar interface                     | SQLQuery         |"},{"id":"common-patterns","title":"Common Patterns","role":"content","text":""},{"id":"vector-search-with-filters","title":"Vector Search with Filters","role":"content","text":"All vector queries support filter expressions. Combine semantic search with metadata filtering:\n\n[code example]"},{"id":"hybrid-search-for-rag","title":"Hybrid Search for RAG","role":"content","text":"For retrieval-augmented generation, hybrid search often outperforms pure vector search:\n\n[code example]"},{"id":"results-and-expiring-documents","title":"Results and expiring documents","role":"returns","text":"RedisVL returns query results as a list of dictionaries, one per matched document. On Redis 8.8+ a document can occasionally come back with its field payload missing: the RediSearch worker pool now defaults to a nonzero number of background worker threads, and if a document — or one of its indexed fields — expires (via TTL or `HPEXPIRE`) at the exact moment a background search is reading it, the server returns the matched id with an empty field payload instead of dropping it from the result set. This is most likely in workloads that combine search with short TTLs, such as a semantic cache or chat history.\n\nRedisVL detects and **silently skips** such a document (logging at `WARNING` level) whenever the query type guarantees a field would be present on a healthy match:\n\n- **`VectorQuery` / `VectorRangeQuery`** with `return_score=True` (the default) — a healthy match always carries a `vector_distance`.\n- **`FilterQuery` on JSON storage** that returns the whole object — a healthy match always carries its JSON payload.\n\nFor a plain **`FilterQuery` / `TextQuery` on hash storage**, a race victim is indistinguishable from a legitimately sparse result (for example, a document matched via an `INDEXMISSING` field, or a query that requested only the `id`). RedisVL cannot safely tell those apart, so it returns the document as an id-only `{\"id\": ...}` dict rather than risk dropping a valid result. If you query hash storage directly this way, use `.get()` for optional fields rather than assuming every field is present."},{"id":"detecting-incomplete-results","title":"Detecting incomplete results","role":"content","text":"`SearchIndex.query()` returns a `SearchResults` object, which is a normal list of result dictionaries with two extra attributes so you can tell a race-shortened result set apart from one that genuinely had fewer matches:\n\n- `results.dropped_count` — how many matched documents were skipped because their field payload was missing (`0` in the normal case).\n- `results.complete` — `False` when any document was dropped.\n\n[code example]\n\n`SearchResults` behaves exactly like a `list` everywhere else, so existing code needs no changes. (Operations that build a new list — slicing, `sorted()`, concatenation — return a plain `list` without these attributes.)\n\nPractical consequences:\n\n- A query may return **fewer results than `num_results`** (or fewer than `page_size` when paginating) when some matched documents were expiring. Treat those limits as upper bounds, not guarantees; use `results.complete` to detect when it happened.\n- A `CountQuery` reports the server’s match count, which still includes the expiring document, so a count can legitimately exceed the number of documents a materializing query returns at the same instant.\n- **`paginate()` keeps going past a page it could not materialize.** If every match on one page was expiring, that page is skipped and iteration continues to the end of the offset range rather than stopping early. An empty batch is never yielded, and a skipped page’s `dropped_count` is added to the next batch you receive, so `results.complete` stays meaningful across the whole stream. Two caveats: a result set whose *trailing* pages were entirely dropped has no later batch to report on, so those drops appear only in the logged warning; and paging is only stable if the query sorts on a unique field — Redis documents `LIMIT` without sorting as non-deterministic, which can duplicate or miss documents independently of expiry.\n- The higher-level extensions build on this: the **semantic cache** drops an expiring hit (a benign cache miss), **message history** drops an expiring message from its formatted output, and the **semantic router** drops an expiring route candidate (which, in the rare case the best match is the one expiring, can shift the selected route). Message history’s `raw=True` mode returns the unprocessed hash entries and may therefore still surface an id-only record.\n\n**Learn more:** [Use Advanced Query Types](https://redis.io/docs/latest/../user_guide/how_to_guides/advanced_queries) demonstrates these query types in detail."}],"examples":[{"id":"vectorquery-ex0","language":"python","code":"from redisvl.query import VectorQuery\n\nquery = VectorQuery(\n    vector=embedding,           # Your query embedding\n    vector_field_name=\"embedding\",\n    num_results=10\n)\nresults = index.query(query)","section_id":"vectorquery"},{"id":"vectorrangequery-ex0","language":"python","code":"from redisvl.query import VectorRangeQuery\n\nquery = VectorRangeQuery(\n    vector=embedding,\n    vector_field_name=\"embedding\",\n    distance_threshold=0.3      # Return all within this distance\n)\nresults = index.query(query)","section_id":"vectorrangequery"},{"id":"filterquery-ex0","language":"python","code":"from redisvl.query import FilterQuery\nfrom redisvl.query.filter import Tag, Num\n\nquery = FilterQuery(\n    filter_expression=(Tag(\"category\") == \"electronics\") & (Num(\"price\") < 100),\n    return_fields=[\"title\", \"price\"],\n    num_results=20\n)\nresults = index.query(query)","section_id":"filterquery"},{"id":"countquery-ex0","language":"python","code":"from redisvl.query import CountQuery\nfrom redisvl.query.filter import Tag\n\nquery = CountQuery(filter_expression=Tag(\"status\") == \"active\")\ncount = index.query(query)","section_id":"countquery"},{"id":"textquery-ex0","language":"python","code":"from redisvl.query import TextQuery\n\nquery = TextQuery(\n    text=\"machine learning\",\n    text_field_name=\"content\",\n    text_scorer=\"BM25STD\",\n    num_results=10\n)\nresults = index.query(query)","section_id":"textquery"},{"id":"hybridquery-ex0","language":"python","code":"from redisvl.query import HybridQuery\n\nquery = HybridQuery(\n    text=\"machine learning frameworks\",\n    text_field_name=\"content\",\n    vector=embedding,\n    vector_field_name=\"embedding\",\n    combination_method=\"RRF\",\n    num_results=10\n)\nresults = index.query(query)","section_id":"hybridquery"},{"id":"multivectorquery-ex0","language":"python","code":"from redisvl.query import MultiVectorQuery, Vector\n\nquery = MultiVectorQuery(\n    vectors=[\n        Vector(vector=text_embedding, field_name=\"text_vector\", weight=0.7),\n        Vector(vector=image_embedding, field_name=\"image_vector\", weight=0.3),\n    ],\n    num_results=10\n)\nresults = index.query(query)","section_id":"multivectorquery"},{"id":"sqlquery-ex0","language":"python","code":"from redisvl.query import SQLQuery\n\nquery = SQLQuery(\"\"\"\n    SELECT title, price, category\n    FROM products\n    WHERE category = 'electronics' AND price < 100\n\"\"\")\nresults = index.query(query)","section_id":"sqlquery"},{"id":"sqlquery-ex1","language":"python","code":"query = SQLQuery(\n    \"\"\"\n    SELECT title, price, category\n    FROM products\n    WHERE category = 'electronics' AND price < 100\n    \"\"\",\n    sql_redis_options={\"schema_cache_strategy\": \"lazy\"},\n)","section_id":"sqlquery"},{"id":"sqlquery-ex2","language":"python","code":"query = SQLQuery(\"SELECT * FROM products WHERE title = 'gaming laptop'\")\nquery = SQLQuery(\"SELECT * FROM products WHERE title LIKE 'lap%'\")\nquery = SQLQuery(\"SELECT * FROM products WHERE fuzzy(title, 'laptap')\")\nquery = SQLQuery(\"SELECT * FROM products WHERE fulltext(title, 'laptop OR tablet')\")","section_id":"sqlquery"},{"id":"sqlquery-ex3","language":"python","code":"query = SQLQuery(\"\"\"\n    SELECT category, COUNT(*) as count, AVG(price) as avg_price\n    FROM products\n    GROUP BY category\n    ORDER BY count DESC\n\"\"\")","section_id":"sqlquery"},{"id":"sqlquery-ex4","language":"python","code":"# Find stores within 50km of San Francisco\nquery = SQLQuery(\"\"\"\n    SELECT name, category\n    FROM stores\n    WHERE geo_distance(location, POINT(-122.4194, 37.7749), 'km') < 50\n\"\"\")\n\n# Calculate distances\nquery = SQLQuery(\"\"\"\n    SELECT name, geo_distance(location, POINT(-122.4194, 37.7749)) AS distance\n    FROM stores\n\"\"\")","section_id":"sqlquery"},{"id":"sqlquery-ex5","language":"python","code":"# Filter by date range\nquery = SQLQuery(\"\"\"\n    SELECT name FROM events\n    WHERE created_at BETWEEN '2024-01-01' AND '2024-03-31'\n\"\"\")\n\n# Extract date parts\nquery = SQLQuery(\"\"\"\n    SELECT YEAR(created_at) AS year, COUNT(*) AS count\n    FROM events\n    GROUP BY year\n\"\"\")","section_id":"sqlquery"},{"id":"sqlquery-ex6","language":"python","code":"query = SQLQuery(\"\"\"\n    SELECT title, vector_distance(embedding, :vec) AS score\n    FROM products\n    LIMIT 5\n\"\"\", params={\"vec\": embedding_bytes})","section_id":"sqlquery"},{"id":"sqlquery-ex7","language":"python","code":"query = SQLQuery(\"\"\"\n    SELECT title,\n           hybrid_vector_search(\n               cosine_distance(embedding, :vec),\n               fulltext(description, 'gaming laptop'),\n               rrf()\n           ) AS hybrid_score\n    FROM products\n    ORDER BY hybrid_score DESC\n    LIMIT 5\n\"\"\", params={\"vec\": embedding_bytes})","section_id":"sqlquery"},{"id":"vector-search-with-filters-ex0","language":"python","code":"from redisvl.query import VectorQuery\nfrom redisvl.query.filter import Tag, Num\n\nquery = VectorQuery(\n    vector=embedding,\n    vector_field_name=\"embedding\",\n    filter_expression=(Tag(\"category\") == \"electronics\") & (Num(\"price\") < 100),\n    num_results=10\n)","section_id":"vector-search-with-filters"},{"id":"hybrid-search-for-rag-ex0","language":"python","code":"from redisvl.query import HybridQuery\n\nquery = HybridQuery(\n    text=\"machine learning frameworks\",\n    text_field_name=\"content\",\n    vector=embedding,\n    vector_field_name=\"embedding\",\n    combination_method=\"RRF\",\n    num_results=5\n)","section_id":"hybrid-search-for-rag"},{"id":"detecting-incomplete-results-ex0","language":"python","code":"results = index.query(query)\n\nif not results.complete:\n    # e.g. retry, reconcile against a CountQuery, or annotate the answer\n    logger.warning(\"Result set is incomplete: %d dropped\", results.dropped_count)","section_id":"detecting-incomplete-results"}]}
{"schema_version":2,"id":"develop/ai/redisvl/concepts/search-and-indexing","title":"Search & Indexing","url":"https://redis.io/docs/latest/develop/ai/redisvl/concepts/search-and-indexing/","summary":"","aliases":["/integrate/redisvl/concepts/search-and-indexing"],"tags":[],"last_updated":"2026-09-03T16:12:11+02:00","page_type":"content","content_hash":"2603995367648058b806795c3cfde3b3b46d8c17f08e0fe320d4d7625bf75353","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Vector search in Redis works differently from traditional databases. Understanding the underlying model helps you design better schemas and write more effective queries."},{"id":"how-redis-indexes-work","title":"How Redis Indexes Work","role":"content","text":"Redis search indexes are secondary structures that sit alongside your data. When you create an index, you’re telling Redis: \"Watch all keys that match this prefix, and build a searchable structure from these specific fields.\"\n\nThe index doesn’t store your data—it references it. Your documents live as Redis Hash or JSON objects, and the index maintains pointers and optimized structures for fast lookup. When you write a document, Redis automatically updates the index. When you delete a document, the index entry is removed.\n\nThis design means searches are fast (the index is optimized for queries) while writes remain efficient (only the affected index entries are updated). It also means you can have multiple indexes over the same data with different field configurations."},{"id":"field-types-and-their-purpose","title":"Field Types and Their Purpose","role":"content","text":"Each field type serves a different search use case.\n\n**Text fields** enable full-text search. Redis tokenizes the content, applies stemming (so \"running\" matches \"run\"), and builds an inverted index. Text search finds documents containing specific words or phrases, ranked by relevance.\n\n**Tag fields** are for exact-match filtering. Unlike text fields, tags are not tokenized or stemmed. A tag value is treated as an atomic unit. This is ideal for categories, statuses, IDs, and other discrete values where you want exact matches.\n\n**Numeric fields** support range queries and sorting. You can filter for values greater than, less than, or between bounds. Numeric fields are also used for sorting results.\n\n**Geo fields** enable location-based queries. You can find documents within a radius of a point or within a bounding box.\n\n**Vector fields** enable similarity search. Each document has an embedding vector, and queries find documents whose vectors are closest to a query vector. This is the foundation of semantic search."},{"id":"vector-indexing-algorithms","title":"Vector Indexing Algorithms","role":"content","text":"Vector similarity search requires specialized data structures. Redis offers three algorithms, each with different trade-offs.\n\n**Flat indexing** performs exact nearest-neighbor search by comparing the query vector against every indexed vector. This guarantees finding the true closest matches, but search time grows linearly with dataset size. Use flat indexing for small datasets (under ~100K vectors) where exact results matter.\n\n**HNSW (Hierarchical Navigable Small World)** is an approximate algorithm that builds a multi-layer graph structure. Queries navigate this graph to find approximate nearest neighbors in logarithmic time. HNSW typically achieves 95-99% recall (meaning it finds 95-99% of the true nearest neighbors) while being orders of magnitude faster than flat search on large datasets. This is the default choice for most applications.\n\n**SVS (Scalable Vector Search)** is designed for very large datasets with memory constraints. It supports vector compression techniques that reduce memory footprint at the cost of some recall. SVS is useful when you have millions of vectors and memory is a limiting factor.\n\nThe algorithm choice is made at index creation and cannot be changed without rebuilding the index."},{"id":"distance-metrics","title":"Distance Metrics","role":"content","text":"When comparing vectors, you need a way to measure how \"close\" two vectors are. Redis supports three distance metrics.\n\n**Cosine distance** measures the angle between vectors, ignoring their magnitude. Two vectors pointing in the same direction have distance 0; opposite directions have distance 2. Cosine is widely used because most embedding models produce vectors where direction encodes meaning and magnitude is less important. Similarity equals 1 minus distance.\n\n**Euclidean distance (L2)** measures the straight-line distance between vector endpoints. Unlike cosine, it considers magnitude. Euclidean distance ranges from 0 to infinity.\n\n**Inner product (IP)** is the dot product of two vectors. It combines both direction and magnitude. When vectors are normalized (magnitude 1), inner product equals cosine similarity. Inner product can be negative and ranges from negative infinity to positive infinity.\n\nChoose your metric based on how your embedding model was trained. Most text embedding models use cosine."},{"id":"storage-hash-vs-json","title":"Storage: Hash vs JSON","role":"content","text":"Redis offers two storage formats for documents.\n\n**Hash storage** is a flat key-value structure where each field is a top-level key. It’s simple, fast, and works well when your documents don’t have nested structures. Field names in your schema map directly to hash field names.\n\n**JSON storage** supports nested documents. You can store complex objects and use JSONPath expressions to index nested fields. This is useful when your data is naturally hierarchical or when you want to store the original document structure without flattening it.\n\nThe choice affects how you structure data and how you reference fields in schemas. Hash is simpler; JSON is more flexible."},{"id":"index-lifecycle","title":"Index Lifecycle","role":"content","text":"Indexes have a straightforward lifecycle: create, use, and eventually delete.\n\n**Creating an index** registers the schema with Redis. The `create()` method sends the schema definition to Redis, which builds the necessary data structures. If an index with the same name exists, you can choose to overwrite it (optionally dropping existing data) or raise an error.\n\n**Checking existence** with `exists()` tells you whether an index is registered in Redis. This is useful before creating (to avoid errors) or before querying (to ensure the index is ready).\n\n**Connecting to existing indexes** is possible with `from_existing()`. If an index was created elsewhere—by another application, a previous deployment, or the CLI—you can connect to it by name. RedisVL fetches the index metadata from Redis and reconstructs the schema automatically.\n\n[code example]\n\n**Deleting an index** with `delete()` removes the index definition from Redis. By default, this also deletes all documents associated with the index. Pass `drop=False` to keep the documents while removing only the index structure.\n\n**Clearing data** with `clear()` removes all documents from the index without deleting the index itself. The schema remains intact, ready for new data."},{"id":"search-visibility-after-writes","title":"Search Visibility After Writes","role":"content","text":"On Redis 8.8 and later, deployments with a nonzero `search-workers` setting use background workers for search indexing. Under concurrent write and query load, a document can already exist in the Redis keyspace but not yet be visible to a search issued immediately after the write. For example, a query directly after `load()` can temporarily return fewer documents than were loaded:\n\n[code example]\n\nThis is a short indexing delay, not data loss. A later query sees the documents after the index catches up. The measurements reported in [Issue #662](https://github.com/redis/redis-vl-python/issues/662) typically converged within 1–3 ms, but that observation is not a latency guarantee and the window can vary with workload and server capacity.\n\nApplications that require strict search read-after-write behavior can start Redis with `--search-workers 0`. This is a Redis server setting, not a RedisVL option: it keeps indexing in the foreground but gives up the throughput benefits of multithreaded background indexing."},{"id":"bulk-delete-and-update","title":"Bulk Delete and Update","role":"content","text":"Redis has no server-side \"delete/update by query\", so mutating many documents traditionally means scanning for keys and issuing writes yourself. RedisVL wraps that pattern behind two filter-driven methods (available on both `SearchIndex` and `AsyncSearchIndex`).\n\n**Deleting by filter** resolves every document matching a filter expression and removes it with non-blocking `UNLINK`, in batches:\n\n[code example]\n\n**Updating by filter** applies a partial field update to every match. Fields you don’t mention are left untouched—hash fields are written with `HSET`, and JSON documents are merged at the root with `JSON.MERGE` (RFC 7396: nested objects merge recursively, arrays are replaced wholesale, and a `None` value deletes that path):\n\n[code example]\n\nTwo caveats for `update_by_filter` values:\n\n- **No schema validation.** Unlike `load()`, values are written as-is—pre-encode vectors/bytes and format numerics as your schema expects, or you may corrupt query results.\n- **JSON keys must match the document layout, not the schema field name.** Values merge at the document root (`$`). If a field is indexed at a nested path (e.g. `$.metadata.status`), pass the correspondingly nested mapping (`{\"metadata\": {\"status\": \"published\"}}`); passing the flat field name writes the wrong path and leaves the indexed field unchanged. Only static values are supported (no callable/expression transforms).\n\nBoth methods share the same safety-oriented options and return a `BulkResult`:\n\n- The return value carries `matched` (documents matching the filter), `processed` (documents actually affected), `completed` (False if a delete stopped early at its runaway backstop; always True for update), and `dry_run`. Read the fields explicitly—`result.processed`, `result.completed`, etc.\n- `dry_run=True` reports how many documents *would* be affected—via a count query—without changing anything.\n- A match-all filter (empty, `\"*\"`, or `None`) is refused unless you pass `allow_all=True`; use `clear()` when you intentionally want to empty the index. This guard catches the canonical match-all forms only—it is a convenience backstop, not a security control.\n- `on_progress(processed, matched)` is called after each write batch for observability on large operations (it runs synchronously—don’t pass a coroutine—and raising from it aborts the run). Note `update_by_filter` resolves all matching keys before it writes, so progress callbacks begin only once the write phase starts. `batch_size` tunes how many documents are processed per round-trip.\n\nThe related `drop_documents()` and `drop_keys()` helpers delete by document ID or full Redis key and batch large inputs. On Redis Cluster, `drop_keys()` unlinks per-key so it works across hash slots, while `drop_documents()` requires the target keys to share a hash tag and raises `ValueError` otherwise.\n\n**Durability and partial failure.** Because Redis has no server-side delete/update-by-query, these operations run as a series of batched writes rather than a single transaction—they are **not atomic across the match set** and there is no rollback. The unit of atomicity is a single document: each key is deleted with one `UNLINK`, and each document is updated with one `HSET` or `JSON.MERGE`, so you never get a half-deleted key or a half-updated document. But batches are applied incrementally, so a crash or connection error mid-run leaves some documents changed and the rest untouched.\n\nThe intended recovery is simply to **re-run the same call**: both operations are idempotent, so a repeat pass removes (or re-sets) only what still needs it and converges on the desired state. There is no built-in checkpoint or resume token—`on_progress` reports live progress but is not a restart point. `update_by_filter` resolves keys before it writes, so a document can be deleted by another client in between; each write is applied only if the key still exists (atomically), so such a document is **skipped, not recreated** as a partial document—it just isn’t counted in `processed`, which is why `processed` can be less than `matched` under concurrent deletion. Conversely, `processed` never double-counts: a document is written *at most* once, so `processed` counts *distinct* documents even though the cursor that resolves them can return the same document on more than one page. It can exceed `matched` only if the match set grew after the initial count—a concurrent insert, or a count query that itself returned a partial total under a server-side search timeout. Note there is no matching *at least* once guarantee: a cursor may return fewer rows than matched (a [partial result](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/aggregations/#cursor-api) even when it reports a cursor id of 0, or rows missing during a slot migration on Redis Software), and a document the cursor never returns is never written—so `processed` short of `matched` is not by itself proof of a concurrent deletion. Note this guard is *existence*-based, not *identity*-based: if a key is deleted and a different document is recreated at the same key mid-run, the update can land on that new document. Running against a quiescent partition (below) avoids all of these concurrency cases.\n\n**Operational considerations at scale.** These are single-threaded, foreground operations that issue one round-trip per batch; a very large match set is a long-running job. Keep the following in mind for production use:\n\n- **Live-traffic impact.** Every delete/update also updates the search index synchronously. Running a large bulk job against a node that is serving queries competes for CPU and reindex bandwidth and can raise query latency—prefer off-peak windows, or narrow the filter and run in waves.\n- **Memory.** `drop_by_filter` holds only one batch of keys at a time (`O(batch_size)`). `update_by_filter` must resolve *all* matching keys before it can write (an open aggregation cursor can’t be read while the index is being written), so its client memory grows with the match count—roughly the total size of the matched keys, plus about a third again for the set that de-duplicates them (an aggregation cursor can return the same document on more than one page, so keys are de-duplicated to guarantee one write per document). Budget on the order of 120 MB per million 40-character keys. For very large match sets, partition the filter (see below) rather than updating everything in one call.\n- **Redis Cluster.** Cross-slot multi-key commands aren’t allowed, so writes are issued per key (no pipelining across slots)—expect this to be slower on Cluster for large match sets.\n- **Resumability.** There is no checkpoint; recovery is a full re-run. For very large corpora, **partition the filter** (e.g. by a tag or numeric range) and process partition-by-partition. This is the recommended pattern at scale: it bounds memory and per-run time, keeps each call independently retryable, and lets you spread load across off-peak windows."},{"id":"data-validation","title":"Data Validation","role":"content","text":"RedisVL can validate data against your schema before loading it to Redis. This catches type mismatches, missing required fields, and invalid values early—before they cause problems in production.\n\nEnable validation by setting `validate_on_load=True` when creating the index:\n\n[code example]\n\nWhen validation is enabled, each object is checked against the schema during `load()`. If validation fails, a `SchemaValidationError` is raised with details about which field failed and why.\n\nValidation adds overhead, so it’s typically enabled during development and testing, then disabled in production once you’re confident in your data pipeline."},{"id":"schema-evolution","title":"Schema Evolution","role":"content","text":"Redis doesn’t support modifying an existing index schema. Once an index is created, its field definitions are fixed.\n\nTo change a schema, you create a new index with the updated configuration, reindex your data into it, update your application to use the new index, and then delete the old index. This pattern—create new, migrate, switch, drop old—is the standard approach for schema changes in production.\n\nPlanning your schema carefully upfront reduces the need for migrations, but the capability exists when requirements evolve.\n\nRedisVL now includes a dedicated migration workflow for this lifecycle:\n\n- `drop_recreate` for document-preserving rebuilds, including vector quantization (`float32` → `float16`)\n\nThat means schema evolution is no longer only a manual operational pattern. It is also a product surface in RedisVL with a planner, CLI, and validation artifacts.\n\n---\n\n**Related concepts:** [Field Attributes](https://redis.io/docs/latest/field-attributes) explains how to configure field options like `sortable` and `index_missing`. [Query Types](https://redis.io/docs/latest/queries) covers the different query types available. [Index Migrations](https://redis.io/docs/latest/index-migrations) explains migration modes, supported changes, and architecture.\n\n**Learn more:** [Getting Started](https://redis.io/docs/latest/../user_guide/getting_started) walks through building your first index. [Choose a Storage Type](https://redis.io/docs/latest/../user_guide/how_to_guides/hash_vs_json) compares storage options in depth. [Query and Filter Data](https://redis.io/docs/latest/../user_guide/how_to_guides/complex_filtering) covers query composition. [Migrate an Index](https://redis.io/docs/latest/../user_guide/how_to_guides/migrate-indexes) shows how to use the migration CLI in practice."}],"examples":[{"id":"index-lifecycle-ex0","language":"python","code":"# Connect to an index that already exists in Redis\nindex = SearchIndex.from_existing(\"my-index\", redis_url=\"redis://localhost:6379\")","section_id":"index-lifecycle"},{"id":"search-visibility-after-writes-ex0","language":"python","code":"index.load(records)\nresults = index.query(query)  # may not include every newly loaded document yet","section_id":"search-visibility-after-writes"},{"id":"bulk-delete-and-update-ex0","language":"python","code":"from redisvl.query.filter import Tag, Num\n\n# Remove every archived document from before 2020\nindex.drop_by_filter((Tag(\"status\") == \"archived\") & (Num(\"year\") < 2020))","section_id":"bulk-delete-and-update"},{"id":"bulk-delete-and-update-ex1","language":"python","code":"# Mark all draft documents as published, leaving other fields intact\nindex.update_by_filter(Tag(\"status\") == \"draft\", {\"status\": \"published\"})","section_id":"bulk-delete-and-update"},{"id":"data-validation-ex0","language":"python","code":"index = SearchIndex.from_dict(schema, redis_url=\"redis://localhost:6379\", validate_on_load=True)","section_id":"data-validation"}]}
{"schema_version":2,"id":"develop/ai/redisvl/concepts/utilities","title":"Utilities","url":"https://redis.io/docs/latest/develop/ai/redisvl/concepts/utilities/","summary":"","aliases":["/integrate/redisvl/concepts/utilities"],"tags":[],"last_updated":"2026-07-27T11:50:00+02:00","page_type":"content","content_hash":"8a89ad1a714a4a56081ce63507a996a2fa4e9257a043de52fdda6425afffd595","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Utilities are optional components that enhance search workflows. They’re not required—you can bring your own embeddings and skip reranking—but they simplify common tasks."},{"id":"vectorizers","title":"Vectorizers","role":"content","text":"A vectorizer converts text into an embedding vector. Embeddings are dense numerical representations that capture semantic meaning: similar texts produce similar vectors, enabling similarity search."},{"id":"why-vectorizers-matter","title":"Why Vectorizers Matter","role":"content","text":"Creating embeddings requires calling an embedding model—either a cloud API (OpenAI, Cohere, etc.) or a local model (HuggingFace sentence-transformers). Each provider has different APIs, authentication methods, and response formats.\n\nRedisVL vectorizers provide a unified interface across providers. You choose a provider, configure it once, and use the same methods regardless of which service is behind it. This makes it easy to switch providers, compare models, or use different providers in different environments."},{"id":"the-dimensionality-contract","title":"The Dimensionality Contract","role":"content","text":"Every embedding model produces vectors of a specific size (dimensionality). OpenAI’s text-embedding-3-small produces 1536-dimensional vectors. Other models produce 384, 768, 1024, or other sizes.\n\nYour schema’s vector field must specify the same dimensionality as your embedding model. If there’s a mismatch—your model produces 1536-dimensional vectors but your schema expects 768—you’ll get errors when loading data or running queries.\n\nThis constraint means you should choose your embedding model before designing your schema, and changing models requires rebuilding your index."},{"id":"batching-and-performance","title":"Batching and Performance","role":"content","text":"Embedding APIs have rate limits and per-request overhead. Embedding one text at a time is inefficient. Vectorizers support batch embedding, sending multiple texts in a single request. This dramatically improves throughput for indexing large datasets.\n\nVectorizers handle batching internally, breaking large batches into provider-appropriate chunks and respecting rate limits. You provide a list of texts; the vectorizer manages the logistics."},{"id":"supported-providers","title":"Supported Providers","role":"compatibility","text":"RedisVL includes vectorizers for OpenAI, Azure OpenAI, Cohere, HuggingFace (local), Mistral, Google (via `google-genai`, covering both Vertex AI and the Gemini Developer API), AWS Bedrock, VoyageAI, and others. See the [Vectorizers](https://redis.io/docs/latest/../api/vectorizer) for the complete list. You can also create custom vectorizers that wrap any embedding function."},{"id":"rerankers","title":"Rerankers","role":"content","text":"A reranker takes initial search results and reorders them by relevance to the query. It’s a second-stage filter that improves precision after the first-stage retrieval."},{"id":"why-reranking-works","title":"Why Reranking Works","role":"content","text":"Vector search uses bi-encoder models: the query and documents are embedded independently, then compared by vector distance. This is fast but approximate—the embedding captures general meaning, not the specific relationship between query and document.\n\nRerankers use cross-encoder models that score query-document pairs directly. The model sees both the query and document together and predicts a relevance score. This is more accurate but slower, because each candidate requires a separate model inference.\n\nThe combination is powerful: use fast vector search to retrieve a broad set of candidates (high recall), then use the slower but more accurate reranker to select the best results (high precision)."},{"id":"the-recall-precision-trade-off","title":"The Recall-Precision Trade-off","role":"content","text":"With only vector search, you might retrieve 10 results and hope the best one is in there. With reranking, you can retrieve 50 candidates—casting a wider net—then rerank to find the 5 best. The initial retrieval prioritizes recall (not missing relevant documents); reranking prioritizes precision (surfacing the most relevant ones)."},{"id":"cost-and-latency","title":"Cost and Latency","role":"content","text":"Reranking adds latency (typically 50-200ms depending on the provider and number of candidates) and cost (API-based rerankers charge per request). These trade-offs are usually worthwhile when result quality matters, but you should measure the impact for your use case."},{"id":"supported-providers-1","title":"Supported Providers","role":"compatibility","text":"RedisVL includes rerankers for HuggingFace cross-encoders (local), Cohere Rerank API, and VoyageAI Rerank API."},{"id":"two-stage-retrieval","title":"Two-Stage Retrieval","role":"content","text":"The most effective retrieval pipelines combine both utilities: vectorize the query, retrieve a candidate set with vector search, then rerank to select the final results.\n\nThis pattern separates recall (finding everything potentially relevant) from precision (selecting the best matches). Vector search handles recall efficiently; reranking handles precision accurately. Together, they deliver better results than either approach alone.\n\n---\n\n**Related concepts:** [Query Types](https://redis.io/docs/latest/queries) explains how to use embeddings in vector search queries. [Search & Indexing](https://redis.io/docs/latest/search-and-indexing) covers schema configuration for vector fields.\n\n**Learn more:** [Create Embeddings with Vectorizers](https://redis.io/docs/latest/../user_guide/how_to_guides/vectorizers) covers embedding providers. [Rerank Search Results](https://redis.io/docs/latest/../user_guide/how_to_guides/rerankers) explains reranking in practice."}],"examples":[]}
{"schema_version":2,"id":"develop/ai/redisvl","title":"RedisVL","url":"https://redis.io/docs/latest/develop/ai/redisvl/","summary":"This is the Redis vector library (RedisVL).","tags":["docs","integrate","stack","oss","rs","rc","oss","clients"],"last_updated":"2025-07-01T09:19:46+02:00","children":[{"id":"develop/ai/redisvl/install","summary":"","title":"Install RedisVL","url":"https://redis.io/docs/latest/develop/ai/redisvl/install/"},{"id":"develop/ai/redisvl/concepts","summary":"","title":"Concepts","url":"https://redis.io/docs/latest/develop/ai/redisvl/concepts/"},{"id":"develop/ai/redisvl/interactive-demo","summary":"","title":"RedisVL queries interactive demo","url":"https://redis.io/docs/latest/develop/ai/redisvl/interactive-demo/"},{"id":"develop/ai/redisvl/user_guide","summary":"","title":"Guides","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/"},{"id":"develop/ai/redisvl/api","summary":"","title":"RedisVL API","url":"https://redis.io/docs/latest/develop/ai/redisvl/api/"}],"page_type":"content","content_hash":"1d536072093d288ee3781d4b90ebc4e196cfa4b5450ff438631b1bc01e93b640","sections":[{"id":"content","title":"Content","role":"content","text":"RedisVL is a powerful, dedicated Python client library for Redis that enables seamless integration and management of high-dimensional vector data.\nBuilt to support machine learning and artificial intelligence workflows, RedisVL simplifies the process of storing, searching, and analyzing vector embeddings, which are commonly used for tasks like recommendation systems, semantic search, and anomaly detection.\n\nKey features of RedisVL include:\n\n- Vector Similarity Search: Efficiently find nearest neighbors in high-dimensional spaces using algorithms like HNSW (Hierarchical Navigable Small World).\n- Integration with AI Frameworks: RedisVL works seamlessly with popular frameworks such as TensorFlow, PyTorch, and Hugging Face, making it easy to deploy AI models.\n- Scalable and Fast: Leveraging Redis's in-memory architecture, RedisVL provides low-latency access to vector data, even at scale.\n- By bridging the gap between data storage and AI model deployment, RedisVL empowers developers to build intelligent, real-time applications with minimal infrastructure complexity."}],"examples":[]}
{"schema_version":2,"id":"develop/ai/redisvl/install","title":"Install RedisVL","url":"https://redis.io/docs/latest/develop/ai/redisvl/install/","summary":"","aliases":["/integrate/redisvl/install"],"tags":[],"last_updated":"2025-10-17T14:03:42-07:00","page_type":"content","content_hash":"4d4feb468fd77d35fba05ac397abfb9ae12b7ea4fbc07872ac897bffa8ddac5e","sections":[{"id":"installation","title":"Installation","role":"setup","text":"Install the `redisvl` package into your Python (>=3.8) environment using the `pip` command:\n\n[code example]\n\nThen make sure to have a Redis instance with the Redis Query Engine features enabled on Redis Cloud or locally in docker with Redis Stack:\n\n[code example]\n\nAfter running the previous command, the Redis Insight GUI will be available at http://localhost:8001."}],"examples":[{"id":"installation-ex0","language":"shell","code":"pip install redisvl","section_id":"installation"},{"id":"installation-ex1","language":"shell","code":"docker run -d --name redis -p 6379:6379 -p 8001:8001 redis/redis-stack:latest","section_id":"installation"}]}
{"schema_version":2,"id":"develop/ai/redisvl/interactive-demo","title":"RedisVL queries interactive demo","url":"https://redis.io/docs/latest/develop/ai/redisvl/interactive-demo/","summary":"","tags":[],"last_updated":"2026-10-14T09:23:01+02:00","page_type":"content","content_hash":"5959d1e93987a3d3c77f666f3c41463fb3104c8414d67ae853cd2051823e53d8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"RedisVL builds Redis queries from Python objects. This demo runs three of its query types on the restaurants of a food delivery app: a vector query that searches by meaning, a filter query, and a range query that limits the search to a distance threshold. For each query, it shows the RedisVL code, the `FT.SEARCH` command RedisVL sends, and the results.\n\nThe demo runs in your browser and doesn't connect to Redis or run an embedding model. Its results are the ones Redis returned for these queries, with descriptions embedded by `sentence-transformers/all-mpnet-base-v2`, and its code and commands are what RedisVL 0.27.1 builds. The restaurants are the same sample data as the [Context Retriever interactive demo](https://redis.io/docs/latest/develop/ai/context-engine/context-retriever/interactive-demo)."},{"id":"what-the-demo-shows","title":"What the demo shows","role":"content","text":"1. **Vector query.** A `VectorQuery` embeds a phrase and returns the nearest restaurants by cosine distance, even when their descriptions share no words with the phrase. With `Tag` and `Num` filters, it returns the nearest restaurants among those that pass the filters.\n2. **Filter query.** A `FilterQuery` matches on the same filter expressions, with no vector.\n3. **Range query.** A `VectorRangeQuery` returns every restaurant within a distance threshold, instead of a fixed number of nearest neighbors."},{"id":"next-steps","title":"Next steps","role":"content","text":"- Read about each [query type](https://redis.io/docs/latest/develop/ai/redisvl/concepts/queries) RedisVL supports.\n- Follow the [query and filter data](https://redis.io/docs/latest/develop/ai/redisvl/user_guide/how_to_guides/complex_filtering) guide for text, geo, and timestamp filters."}],"examples":[]}
{"schema_version":2,"id":"develop/ai/redisvl/user_guide/cli","title":"The RedisVL CLI","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/cli/","summary":"","aliases":["/integrate/redisvl/user_guide/cli"],"tags":[],"last_updated":"2026-08-10T10:42:48+02:00","page_type":"content","content_hash":"0d86b4fe4dc52f14b588d3cbf857d3f0c51196fb06cc58e6b1056e2c0efea983","sections":[{"id":"overview","title":"Overview","role":"overview","text":"RedisVL is a Python library with a dedicated CLI to create, inspect, list, migrate, and delete Redis search indexes, inspect index statistics, and run the RedisVL MCP server.\n\nThis notebook will walk through how to use the Redis Vector Library CLI (``rvl``).\n\nBefore running this notebook, be sure to\n1. Have installed ``redisvl`` and have that environment active for this notebook.\n2. Have a running Redis instance with Redis Search enabled\n\nFor complete command syntax and options, see the CLI Reference.\n\n\n[code example]"},{"id":"commands","title":"Commands","role":"syntax","text":"The table below documents the current CLI tree. Use ``rvl index --help`` and ``rvl stats --help`` for detailed flag help and examples.\n\n| Command | Purpose |\n|---------|---------|\n| `rvl version` | display the installed RedisVL version |\n| `rvl index create` | create a new Redis search index from a schema YAML file |\n| `rvl index info` | display schema and storage details for an index |\n| `rvl index listall` | list Redis search indexes available on the target Redis deployment |\n| `rvl index delete` | delete an index while leaving indexed data in Redis |\n| `rvl index destroy` | delete an index and drop its indexed data |\n| `rvl stats` | display statistics for an existing Redis search index |\n| `rvl mcp` | run the RedisVL MCP server |\n| `rvl migrate wizard` | interactively build a migration plan and schema patch (experimental) |\n| `rvl migrate plan` | generate `migration_plan.yaml` from a patch or target schema (experimental) |\n| `rvl migrate apply` | execute a reviewed `drop_recreate` migration (experimental) |\n| `rvl migrate validate` | validate a completed migration and emit report artifacts (experimental) |\n| `rvl migrate rollback` | restore original vector bytes from a migration backup (experimental) |\n| `rvl migrate batch-plan` | generate a batch plan for multiple indexes (experimental) |\n| `rvl migrate batch-apply` | execute a batch migration with checkpoint state (experimental) |\n| `rvl migrate batch-resume` | resume an interrupted batch migration (experimental) |\n| `rvl migrate batch-status` | inspect batch migration checkpoint state (experimental) |\n\nWithin data-plane commands, ``-i`` or ``--index`` targets an existing Redis index name and ``-s`` or ``--schema`` points to a schema YAML file. Shared Redis connection options such as ``--url``, ``--host``, and ``--port`` apply to ``rvl index`` and ``rvl stats``."},{"id":"index","title":"Index","role":"content","text":"The ``rvl index`` command groups the index management workflows. Use ``rvl index --help`` to see the documented subcommands: ``create``, ``info``, ``listall``, ``delete``, and ``destroy``. Whether you are working in Python or another language, this CLI can still be useful for managing and inspecting your indexes.\n\nFirst, we will create an index from a yaml schema that looks like the following:\n\n\n\n[code example]\n\n    Overwriting schema.yaml\n\n\n\n[code example]\n\n    Index created successfully\n\n\n\n[code example]\n\n    Indices:\n    1. vectorizers\n\n\n\n[code example]\n\n    \n    \n    Index Information:\n    ╭───────────────┬───────────────┬───────────────┬───────────────┬───────────────╮\n    │ Index Name    │ Storage Type  │ Prefixes      │ Index Options │ Indexing      │\n    ├───────────────┼───────────────┼───────────────┼───────────────┼───────────────┤\n    | vectorizers   | HASH          | ['doc']       | []            | 0             |\n    ╰───────────────┴───────────────┴───────────────┴───────────────┴───────────────╯\n    Index Fields:\n    ╭─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────╮\n    │ Name            │ Attribute       │ Type            │ Field Option    │ Option Value    │ Field Option    │ Option Value    │ Field Option    │ Option Value    │ Field Option    │ Option Value    │\n    ├─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┤\n    │ sentence        │ sentence        │ TEXT            │ WEIGHT          │ 1               │                 │                 │                 │                 │                 │                 │\n    │ embedding       │ embedding       │ VECTOR          │ algorithm       │ FLAT            │ data_type       │ FLOAT32         │ dim             │ 768             │ distance_metric │ COSINE          │\n    ╰─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────╯\n\n\n\n[code example]\n\n    Index deleted successfully\n\n\n\n[code example]\n\n    Indices:"},{"id":"stats","title":"Stats","role":"content","text":"The ``rvl stats`` command returns basic information about an index. Use ``-i`` or ``--index`` to target an existing Redis index name, or ``-s`` or ``--schema`` to target a schema-defined index. Shared Redis connection options such as ``--url``, ``--host``, and ``--port`` also apply here.\n\n\n[code example]\n\n    Index created successfully\n\n\n\n[code example]\n\n    Indices:\n    1. vectorizers\n\n\n\n[code example]\n\n    \n    Statistics:\n    ╭─────────────────────────────┬────────────╮\n    │ Stat Key                    │ Value      │\n    ├─────────────────────────────┼────────────┤\n    │ num_docs                    │ 0          │\n    │ num_terms                   │ 0          │\n    │ max_doc_id                  │ 0          │\n    │ num_records                 │ 0          │\n    │ percent_indexed             │ 1          │\n    │ hash_indexing_failures      │ 0          │\n    │ number_of_uses              │ 1          │\n    │ bytes_per_record_avg        │ nan        │\n    │ doc_table_size_mb           │ 0.00769805 │\n    │ inverted_sz_mb              │ 0          │\n    │ key_table_size_mb           │ 2.28881835 │\n    │ offset_bits_per_record_avg  │ nan        │\n    │ offset_vectors_sz_mb        │ 0          │\n    │ offsets_per_term_avg        │ nan        │\n    │ records_per_doc_avg         │ nan        │\n    │ sortable_values_size_mb     │ 0          │\n    │ total_indexing_time         │ 0          │\n    │ total_inverted_index_blocks │ 0          │\n    │ vector_index_sz_mb          │ 0          │\n    ╰─────────────────────────────┴────────────╯"},{"id":"migrate","title":"Migrate","role":"content","text":"The ``rvl migrate`` command provides a full workflow for changing index schemas without losing data. Common use cases include vector quantization (float32 → float16), algorithm changes (HNSW → FLAT), and adding/removing fields.\n\n[code example]\n\nSee the [Migration Guide](https://redis.io/docs/latest/how_to_guides/migrate-indexes) for detailed usage, performance tuning, and examples."},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"You can modify these commands with the below optional arguments\n\n| Argument       | Description | Default |\n|----------------|-------------|---------|\n| `-u --url`     | The full Redis URL to connect to | `redis://localhost:6379` |\n| `--host`       | Redis host to connect to | `localhost` |\n| `-p --port`    | Redis port to connect to. Must be an integer | `6379` |\n| `--user`       | Redis username, if one is required   | `default` |\n| `--ssl`        | Boolean flag indicating if ssl is required. If set the Redis base url changes to `rediss://` | None |\n| `-a --password`| Redis password, if one is required| `\"\"` |"},{"id":"choosing-your-redis-instance","title":"Choosing your Redis instance","role":"content","text":"By default rvl first checks if you have `REDIS_URL` environment variable defined and tries to connect to that. If not, it then falls back to `localhost:6379`, unless you pass the `--host` or `--port` arguments\n\n\n[code example]\n\n\n[code example]\n\n\n[code example]"}],"examples":[{"id":"overview-ex0","language":"python","code":"# First, see if the rvl tool is installed\n!rvl version","section_id":"overview"},{"id":"index-ex0","language":"python","code":"%%writefile schema.yaml\n\nversion: '0.1.0'\n\nindex:\n    name: vectorizers\n    prefix: doc\n    storage_type: hash\n\nfields:\n    - name: sentence\n      type: text\n    - name: embedding\n      type: vector\n      attrs:\n        dims: 768\n        algorithm: flat\n        distance_metric: cosine","section_id":"index"},{"id":"index-ex1","language":"python","code":"# Create an index from a yaml schema\n!rvl index create -s schema.yaml","section_id":"index"},{"id":"index-ex2","language":"python","code":"# list the indices that are available\n!rvl index listall","section_id":"index"},{"id":"index-ex3","language":"python","code":"# inspect the index fields\n!rvl index info -i vectorizers","section_id":"index"},{"id":"index-ex4","language":"python","code":"# delete an index without deleting the data within it\n!rvl index delete -i vectorizers","section_id":"index"},{"id":"index-ex5","language":"python","code":"# see the indices that still exist\n!rvl index listall","section_id":"index"},{"id":"stats-ex0","language":"python","code":"# create a new index with the same schema\n# recreating the index will reindex the documents\n!rvl index create -s schema.yaml","section_id":"stats"},{"id":"stats-ex1","language":"python","code":"# list the indices that are available\n!rvl index listall","section_id":"stats"},{"id":"stats-ex2","language":"python","code":"# see all the stats for the index\n!rvl stats -i vectorizers","section_id":"stats"},{"id":"migrate-ex0","language":"bash","code":"# List available indexes\nrvl index listall --url redis://localhost:6379\n\n# Build a migration plan interactively\nrvl migrate wizard --index myindex --url redis://localhost:6379\n\n# Or generate from a schema patch file\nrvl migrate plan --index myindex --schema-patch patch.yaml --url redis://localhost:6379\n\n# Apply with backup and multi-worker quantization\nrvl migrate apply --plan migration_plan.yaml --url redis://localhost:6379 \\\n  --backup-dir /tmp/backups --workers 4 --batch-size 500\n\n# Validate the result\nrvl migrate validate --plan migration_plan.yaml --url redis://localhost:6379","section_id":"migrate"},{"id":"choosing-your-redis-instance-ex0","language":"python","code":"# specify your Redis instance to connect to\n!rvl index listall --host localhost --port 6379","section_id":"choosing-your-redis-instance"},{"id":"choosing-your-redis-instance-ex1","language":"python","code":"# NBVAL_SKIP\n# Not run in CI: there is no TLS-enabled Redis to connect to, so the client\n# blocks on the handshake until the nbval cell timeout expires.\n# connect to rediss://jane_doe:password123@localhost:6379\n!rvl index listall --user jane_doe -a password123 --ssl","section_id":"choosing-your-redis-instance"},{"id":"choosing-your-redis-instance-ex2","language":"python","code":"!rvl index destroy -i vectorizers","section_id":"choosing-your-redis-instance"}]}
{"schema_version":2,"id":"develop/ai/redisvl/user_guide/getting_started","title":"Getting Started","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/getting_started/","summary":"","aliases":["/integrate/redisvl/user_guide/01_getting_started","/integrate/redisvl/user_guide/getting_started/"],"tags":[],"last_updated":"2026-10-01T14:49:09Z","page_type":"content","content_hash":"b9af3c58d15a32f0a8dd347e8ebb71a44af4222c207ce6006a7d4df448b559be","sections":[{"id":"overview","title":"Overview","role":"overview","text":"RedisVL is a Python library with an integrated CLI for building AI applications with Redis. This guide covers the core workflow:\n\n1. Defining an `IndexSchema`\n2. Preparing a sample dataset\n3. Creating a `SearchIndex`\n4. Using the `rvl` CLI\n5. Loading data into Redis\n6. Fetching and managing records\n7. Executing vector searches\n8. Updating an index"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before you begin, ensure you have:\n- Installed RedisVL: `pip install redisvl`\n- A running Redis instance ([Redis 8+](https://redis.io/downloads/) or [Redis Cloud](https://redis.io/cloud))"},{"id":"what-youll-learn","title":"What You'll Learn","role":"content","text":"By the end of this guide, you will be able to:\n- Create index schemas using Python dictionaries or YAML files\n- Build and manage `SearchIndex` objects\n- Use the `rvl` CLI for index management\n- Load data and execute vector similarity searches\n- Fetch individual records and list all keys in an index\n- Delete specific records by key or document ID\n- Update index schemas as your application evolves"},{"id":"define-an-indexschema","title":"Define an `IndexSchema`","role":"content","text":"The `IndexSchema` maintains crucial **index configuration** and **field definitions** to\nenable search with Redis. For ease of use, the schema can be constructed from a\npython dictionary or yaml file."},{"id":"example-schema-creation","title":"Example Schema Creation","role":"example","text":"Consider a dataset with user information, including `job`, `age`, `credit_score`,\nand a 3-dimensional `user_embedding` vector.\n\nYou must also decide on a Redis index name and key prefix to use for this\ndataset. Below are example schema definitions in both YAML and Dict format.\n\n**YAML Definition:**\n\n[code example]\nStore this in a local file, such as `schema.yaml`, for RedisVL usage.\n\n**Python Dictionary:**\n\n\n[code example]"},{"id":"sample-dataset-preparation","title":"Sample Dataset Preparation","role":"example","text":"Below, create a mock dataset with `user`, `job`, `age`, `credit_score`, and\n`user_embedding` fields. The `user_embedding` vectors are synthetic examples\nfor demonstration purposes.\n\nFor more information on creating real-world embeddings, refer to this\n[article](https://mlops.community/vector-similarity-search-from-basics-to-production/).\n\n\n[code example]\n\nThe `user_embedding` vectors are converted to bytes using NumPy's `.tobytes()` method."},{"id":"create-a-searchindex","title":"Create a `SearchIndex`","role":"content","text":"With the schema and sample dataset ready, create a `SearchIndex`."},{"id":"bring-your-own-redis-connection-instance","title":"Bring your own Redis connection instance","role":"content","text":"This is ideal in scenarios where you have custom settings on the connection instance or if your application will share a connection pool:\n\n\n[code example]"},{"id":"let-the-index-manage-the-connection-instance","title":"Let the index manage the connection instance","role":"content","text":"This is ideal for simple cases:\n\n\n[code example]"},{"id":"create-the-index","title":"Create the index","role":"content","text":"Now that we are connected to Redis, we need to run the create command.\n\n\n[code example]\n\nNote that at this point, the index has no entries. Data loading follows."},{"id":"inspect-with-the-rvl-cli","title":"Inspect with the `rvl` CLI","role":"content","text":"Use the `rvl` CLI to inspect the created index and its fields:\n\n\n[code example]\n\n    \n    \n    Index Information:\n    ╭──────────────────────┬──────────────────────┬──────────────────────┬──────────────────────┬──────────────────────╮\n    │ Index Name           │ Storage Type         │ Prefixes             │ Index Options        │ Indexing             │\n    ├──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┤\n    | user_simple          | HASH                 | ['user_simple_docs'] | []                   | 0                    |\n    ╰──────────────────────┴──────────────────────┴──────────────────────┴──────────────────────┴──────────────────────╯\n    Index Fields:\n    ╭─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────╮\n    │ Name            │ Attribute       │ Type            │ Field Option    │ Option Value    │ Field Option    │ Option Value    │ Field Option    │ Option Value    │ Field Option    │ Option Value    │\n    ├─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┤\n    │ user            │ user            │ TAG             │ SEPARATOR       │ ,               │                 │                 │                 │                 │                 │                 │\n    │ credit_score    │ credit_score    │ TAG             │ SEPARATOR       │ ,               │                 │                 │                 │                 │                 │                 │\n    │ job             │ job             │ TEXT            │ WEIGHT          │ 1               │                 │                 │                 │                 │                 │                 │\n    │ age             │ age             │ NUMERIC         │                 │                 │                 │                 │                 │                 │                 │                 │\n    │ user_embedding  │ user_embedding  │ VECTOR          │ algorithm       │ FLAT            │ data_type       │ FLOAT32         │ dim             │ 3               │ distance_metric │ COSINE          │\n    ╰─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────╯"},{"id":"load-data-to-searchindex","title":"Load Data to `SearchIndex`","role":"content","text":"Load the sample dataset to Redis."},{"id":"validate-data-entries-on-load","title":"Validate data entries on load","role":"content","text":"RedisVL uses pydantic validation under the hood to ensure loaded data is valid and confirms to your schema. This setting is optional and can be configured in the `SearchIndex` class.\n\n\n[code example]\n\n    ['user_simple_docs:01KHKHQYX95EDQN18FG8FRMRQ5', 'user_simple_docs:01KHKHQYXC97WY4ACG1V01GEPC', 'user_simple_docs:01KHKHQYXC97WY4ACG1V01GEPD']\n\n\nBy default, `load` will create a unique Redis key as a combination of the index key `prefix` and a random ULID. You can also customize the key by providing direct keys or pointing to a specified `id_field` on load."},{"id":"load-invalid-data","title":"Load INVALID data","role":"content","text":"This will raise a `SchemaValidationError` if `validate_on_load` is set to true in the `SearchIndex` class.\n\n\n[code example]\n\n    Schema validation failed for object at index 0. Field 'user_embedding' expects bytes (vector data), but got boolean value 'True'. If this should be a vector field, provide a list of numbers or bytes. If this should be a different field type, check your schema definition.\n    Object data: {\n      \"user_embedding\": true\n    }\n    Hint: Check that your data types match the schema field definitions. Use index.schema.fields to view expected field types."},{"id":"upsert-the-index-with-new-data","title":"Upsert the index with new data","role":"content","text":"Upsert data by using the `load` method again:\n\n\n[code example]\n\n    ['user_simple_docs:01KHKHR37CD6143DNQ41G3ADNA']"},{"id":"fetch-and-manage-records","title":"Fetch and Manage Records","role":"content","text":"RedisVL provides several methods to retrieve and manage individual records in your index."},{"id":"fetch-a-record-by-id","title":"Fetch a record by ID","role":"content","text":"Use `fetch()` to retrieve a single record when you know its ID. The ID is the unique identifier you provided during load (via `id_field`) or the auto-generated ULID.\n\n\n[code example]\n\nYou can also construct the full Redis key from an ID using the `key()` method:\n\n\n[code example]\n\n\n<table><tr><th>vector_distance</th><th>user</th><th>age</th><th>job</th><th>credit_score</th></tr><tr><td>0</td><td>john</td><td>1</td><td>engineer</td><td>high</td></tr><tr><td>0</td><td>mary</td><td>2</td><td>doctor</td><td>low</td></tr><tr><td>0.0566298961639</td><td>tyler</td><td>9</td><td>engineer</td><td>high</td></tr></table>"},{"id":"list-all-keys-in-the-index","title":"List all keys in the index","role":"content","text":"To enumerate all keys in your index, use `paginate()` with a `FilterQuery`. This is useful for batch processing or auditing your data.\n\n\n[code example]"},{"id":"delete-specific-records","title":"Delete specific records","role":"content","text":"Use `drop_keys()` to remove specific records by their full Redis key, or `drop_documents()` to remove by document ID.\n\n\n[code example]\n\n\n[code example]\n\n**Note:** `drop_keys()` expects the full Redis key (including prefix), while `drop_documents()` expects just the document ID."},{"id":"creating-vectorquery-objects","title":"Creating `VectorQuery` Objects","role":"content","text":"Next we will create a vector query object for our newly populated index. This example will use a simple vector to demonstrate how vector similarity works. Vectors in production will likely be much larger than 3 floats and often require Machine Learning models (i.e. Huggingface sentence transformers) or an embeddings API (Cohere, OpenAI). `redisvl` provides a set of [Vectorizers](https://redis.io/docs/latest/how_to_guides/vectorizers#openai) to assist in vector creation.\n\n\n[code example]\n\n\n<table><tr><th>vector_distance</th><th>user</th><th>age</th><th>job</th><th>credit_score</th></tr><tr><td>0</td><td>john</td><td>1</td><td>engineer</td><td>high</td></tr><tr><td>0</td><td>mary</td><td>2</td><td>doctor</td><td>low</td></tr><tr><td>0.0566298961639</td><td>tyler</td><td>9</td><td>engineer</td><td>high</td></tr></table>\n\n\n**Note:** For HNSW and SVS-VAMANA indexes, you can tune search performance using runtime parameters:\n\n[code example]\n\nSee the [SVS-VAMANA guide](https://redis.io/docs/latest/how_to_guides/svs_vamana) and [Advanced Queries guide](https://redis.io/docs/latest/how_to_guides/advanced_queries) for more details on runtime parameters."},{"id":"executing-queries","title":"Executing queries","role":"content","text":"With our `VectorQuery` object defined above, we can execute the query over the `SearchIndex` using the `query` method.\n\n\n[code example]"},{"id":"using-an-asynchronous-redis-client","title":"Using an Asynchronous Redis Client","role":"content","text":"The `AsyncSearchIndex` class along with an async Redis python client allows for queries, index creation, and data loading to be done asynchronously. This is the\nrecommended route for working with `redisvl` in production-like settings.\n\n\n[code example]\n\n\n\n\n    4\n\n\n\n\n[code example]\n\n\n\n\n    True"},{"id":"updating-a-schema","title":"Updating a schema","role":"content","text":"In some scenarios, it makes sense to update the index schema. With Redis and `redisvl`, this is easy because Redis can keep the underlying data in place while you change or make updates to the index configuration.\n\nSo for our scenario, let's imagine we want to reindex this data in 2 ways:\n- by using a `Tag` type for `job` field instead of `Text`\n- by using an `hnsw` vector index for the `user_embedding` field instead of a `flat` vector index\n\n\n[code example]\n\n\n[code example]\n\n\n[code example]"},{"id":"check-index-stats","title":"Check Index Stats","role":"content","text":"Use the `rvl` CLI to check the stats for the index:\n\n\n[code example]"},{"id":"next-steps","title":"Next Steps","role":"content","text":"Now that you understand the basics of RedisVL, explore these related guides:\n\n- [Query and Filter Data](https://redis.io/docs/latest/how_to_guides/complex_filtering) - Learn advanced filtering with tag, numeric, text, and geo filters\n- [Create Embeddings with Vectorizers](https://redis.io/docs/latest/how_to_guides/vectorizers) - Generate embeddings using OpenAI, HuggingFace, Cohere, and more\n- [Choose a Storage Type](https://redis.io/docs/latest/how_to_guides/hash_vs_json) - Understand when to use Hash vs JSON storage"},{"id":"cleanup","title":"Cleanup","role":"content","text":"Use `.clear()` to flush all data from Redis associated with the index while leaving the index in place for future insertions.\n\nUse `.delete()` to remove both the index and the underlying data.\n\n\n[code example]\n\n\n[code example]\n\n\n[code example]"}],"examples":[{"id":"example-schema-creation-ex0","language":"yaml","code":"version: '0.1.0'\n\nindex:\n  name: user_simple\n  prefix: user_simple_docs\n\nfields:\n    - name: user\n      type: tag\n    - name: credit_score\n      type: tag\n    - name: job\n      type: text\n    - name: age\n      type: numeric\n    - name: user_embedding\n      type: vector\n      attrs:\n        algorithm: flat\n        dims: 3\n        distance_metric: cosine\n        datatype: float32","section_id":"example-schema-creation"},{"id":"example-schema-creation-ex1","language":"python","code":"schema = {\n    \"index\": {\n        \"name\": \"user_simple\",\n        \"prefix\": \"user_simple_docs\",\n    },\n    \"fields\": [\n        {\"name\": \"user\", \"type\": \"tag\"},\n        {\"name\": \"credit_score\", \"type\": \"tag\"},\n        {\"name\": \"job\", \"type\": \"text\"},\n        {\"name\": \"age\", \"type\": \"numeric\"},\n        {\n            \"name\": \"user_embedding\",\n            \"type\": \"vector\",\n            \"attrs\": {\n                \"dims\": 3,\n                \"distance_metric\": \"cosine\",\n                \"algorithm\": \"flat\",\n                \"datatype\": \"float32\"\n            }\n        }\n    ]\n}","section_id":"example-schema-creation"},{"id":"sample-dataset-preparation-ex0","language":"python","code":"import numpy as np\n\n\ndata = [\n    {\n        'user': 'john',\n        'age': 1,\n        'job': 'engineer',\n        'credit_score': 'high',\n        'user_embedding': np.array([0.1, 0.1, 0.5], dtype=np.float32).tobytes()\n    },\n    {\n        'user': 'mary',\n        'age': 2,\n        'job': 'doctor',\n        'credit_score': 'low',\n        'user_embedding': np.array([0.1, 0.1, 0.5], dtype=np.float32).tobytes()\n    },\n    {\n        'user': 'joe',\n        'age': 3,\n        'job': 'dentist',\n        'credit_score': 'medium',\n        'user_embedding': np.array([0.9, 0.9, 0.1], dtype=np.float32).tobytes()\n    }\n]","section_id":"sample-dataset-preparation"},{"id":"bring-your-own-redis-connection-instance-ex0","language":"python","code":"from redisvl.index import SearchIndex\nfrom redis import Redis\n\nclient = Redis.from_url(\"redis://localhost:6379\")\nindex = SearchIndex.from_dict(schema, redis_client=client, validate_on_load=True)","section_id":"bring-your-own-redis-connection-instance"},{"id":"let-the-index-manage-the-connection-instance-ex0","language":"python","code":"index = SearchIndex.from_dict(schema, redis_url=\"redis://localhost:6379\", validate_on_load=True)\n\n# If you don't specify a client or Redis URL, the index will attempt to\n# connect to Redis at the default address \"redis://localhost:6379\".","section_id":"let-the-index-manage-the-connection-instance"},{"id":"create-the-index-ex0","language":"python","code":"index.create(overwrite=True)","section_id":"create-the-index"},{"id":"inspect-with-the-rvl-cli-ex0","language":"python","code":"!rvl index info -i user_simple","section_id":"inspect-with-the-rvl-cli"},{"id":"validate-data-entries-on-load-ex0","language":"python","code":"keys = index.load(data)\n\nprint(keys)","section_id":"validate-data-entries-on-load"},{"id":"load-invalid-data-ex0","language":"python","code":"# NBVAL_SKIP\n\ntry:\n    keys = index.load([{\"user_embedding\": True}])\nexcept Exception as e:\n    print(str(e))","section_id":"load-invalid-data"},{"id":"upsert-the-index-with-new-data-ex0","language":"python","code":"# Add more data\nnew_data = [{\n    'user': 'tyler',\n    'age': 9,\n    'job': 'engineer',\n    'credit_score': 'high',\n    'user_embedding': np.array([0.1, 0.3, 0.5], dtype=np.float32).tobytes()\n}]\nkeys = index.load(new_data)\n\nprint(keys)","section_id":"upsert-the-index-with-new-data"},{"id":"fetch-a-record-by-id-ex0","language":"python","code":"# Fetch a record by its ID (e.g., the user field value if used as id_field)\n# First, let's reload data with a specific id_field\nindex.load(data, id_field=\"user\")\n\n# Now fetch by the user ID\nrecord = index.fetch(\"john\")\nprint(record)","section_id":"fetch-a-record-by-id"},{"id":"fetch-a-record-by-id-ex1","language":"python","code":"# Get the full Redis key for a given ID\nfull_key = index.key(\"john\")\nprint(f\"Full Redis key: {full_key}\")","section_id":"fetch-a-record-by-id"},{"id":"list-all-keys-in-the-index-ex0","language":"python","code":"from redisvl.query import FilterQuery\nfrom redisvl.query.filter import FilterExpression\n\n# Create a query that matches all documents\nquery = FilterQuery(\n    filter_expression=FilterExpression(\"*\"),\n    return_fields=[\"user\", \"age\", \"job\"]\n)\n\n# Paginate through all results\nfor batch in index.paginate(query, page_size=10):\n    for doc in batch:\n        print(f\"Key: {doc['id']}, User: {doc['user']}\")","section_id":"list-all-keys-in-the-index"},{"id":"delete-specific-records-ex0","language":"python","code":"# Delete by full Redis key\nfull_key = index.key(\"john\")\ndeleted_count = index.drop_keys(full_key)\nprint(f\"Deleted {deleted_count} record(s) by key\")\n\n# Delete multiple keys at once\n# index.drop_keys([\"key1\", \"key2\", \"key3\"])","section_id":"delete-specific-records"},{"id":"delete-specific-records-ex1","language":"python","code":"# Delete by document ID (without the prefix)\ndeleted_count = index.drop_documents(\"mary\")\nprint(f\"Deleted {deleted_count} record(s) by document ID\")\n\n# Delete multiple documents at once\n# index.drop_documents([\"id1\", \"id2\", \"id3\"])","section_id":"delete-specific-records"},{"id":"creating-vectorquery-objects-ex0","language":"python","code":"from redisvl.query import VectorQuery\nfrom jupyterutils import result_print\n\nquery = VectorQuery(\n    vector=[0.1, 0.1, 0.5],\n    vector_field_name=\"user_embedding\",\n    return_fields=[\"user\", \"age\", \"job\", \"credit_score\", \"vector_distance\"],\n    num_results=3\n)","section_id":"creating-vectorquery-objects"},{"id":"creating-vectorquery-objects-ex1","language":"python","code":"# Example with HNSW runtime parameters\nquery = VectorQuery(\n    vector=[0.1, 0.1, 0.5],\n    vector_field_name=\"user_embedding\",\n    return_fields=[\"user\", \"age\", \"job\"],\n    num_results=3,\n    ef_runtime=50  # Higher for better recall (HNSW only)\n)","section_id":"creating-vectorquery-objects"},{"id":"executing-queries-ex0","language":"python","code":"results = index.query(query)\nresult_print(results)","section_id":"executing-queries"},{"id":"using-an-asynchronous-redis-client-ex0","language":"python","code":"from redisvl.index import AsyncSearchIndex\nfrom redis.asyncio import Redis\n\nclient = Redis.from_url(\"redis://localhost:6379\")\nindex = AsyncSearchIndex.from_dict(schema, redis_client=client)","section_id":"using-an-asynchronous-redis-client"},{"id":"using-an-asynchronous-redis-client-ex1","language":"python","code":"# execute the vector query async\nresults = await index.query(query)\nresult_print(results)","section_id":"using-an-asynchronous-redis-client"},{"id":"updating-a-schema-ex0","language":"python","code":"# Modify this schema to have what we want\nindex.schema.remove_field(\"job\")\nindex.schema.remove_field(\"user_embedding\")\nindex.schema.add_fields([\n    {\"name\": \"job\", \"type\": \"tag\"},\n    {\n        \"name\": \"user_embedding\",\n        \"type\": \"vector\",\n        \"attrs\": {\n            \"dims\": 3,\n            \"distance_metric\": \"cosine\",\n            \"algorithm\": \"hnsw\",\n            \"datatype\": \"float32\"\n        }\n    }\n])","section_id":"updating-a-schema"},{"id":"updating-a-schema-ex1","language":"python","code":"# Run the index update but keep underlying data in place\nawait index.create(overwrite=True, drop=False)","section_id":"updating-a-schema"},{"id":"updating-a-schema-ex2","language":"python","code":"# Execute the vector query async\nresults = await index.query(query)\nresult_print(results)","section_id":"updating-a-schema"},{"id":"check-index-stats-ex0","language":"python","code":"!rvl stats -i user_simple","section_id":"check-index-stats"},{"id":"cleanup-ex0","language":"python","code":"# Clear all data from Redis associated with the index\nawait index.clear()","section_id":"cleanup"},{"id":"cleanup-ex1","language":"python","code":"# But the index is still in place\nawait index.exists()","section_id":"cleanup"},{"id":"cleanup-ex2","language":"python","code":"# Remove / delete the index in its entirety\nawait index.delete()","section_id":"cleanup"}]}
{"schema_version":2,"id":"develop/ai/redisvl/user_guide/how_to_guides/advanced_queries","title":"Use Advanced Query Types","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/how_to_guides/advanced_queries/","summary":"","aliases":["/integrate/redisvl/user_guide/how_to_guides/11_advanced_queries","/develop/ai/redisvl/user_guide/advanced_queries/"],"tags":[],"last_updated":"2026-10-01T14:49:09Z","page_type":"content","content_hash":"7e489bf228545abd7c8ebcd7bbdf9b5331ebd363c40cec45a3a0cf5597ac94d6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This guide covers advanced query types available in RedisVL:\n\n1. **`TextQuery`**: Full text search with advanced scoring\n2. **`AggregateHybridQuery` and `HybridQuery`**: Combines text and vector search for hybrid retrieval\n3. **`MultiVectorQuery`**: Search over multiple vector fields simultaneously\n\nThese query types enable sophisticated search applications that go beyond simple vector similarity search."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before you begin, ensure you have:\n- Installed RedisVL: `pip install redisvl`\n- A running Redis instance ([Redis 8+](https://redis.io/downloads/) or [Redis Cloud](https://redis.io/cloud))\n- For `HybridQuery`: Redis >= 8.4.0 and redis-py >= 7.1.0"},{"id":"what-youll-learn","title":"What You'll Learn","role":"content","text":"By the end of this guide, you will be able to:\n- Perform full-text search with `TextQuery` and advanced scoring options\n- Combine text and vector search using `HybridQuery` and `AggregateHybridQuery`\n- Search across multiple vector fields with `MultiVectorQuery`\n- Configure custom stopwords for text search"},{"id":"setup-and-data-preparation","title":"Setup and Data Preparation","role":"setup","text":"First, let's create a schema and prepare sample data that includes text fields, numeric fields, and vector fields.\n\n\n[code example]"},{"id":"define-the-schema","title":"Define the Schema","role":"content","text":"Our schema includes:\n- **Tag fields**: `product_id`, `category`\n- **Text fields**: `brief_description` and `full_description` for full-text search\n- **Numeric fields**: `price`, `rating`\n- **Vector fields**: `text_embedding` (3 dimensions) and `image_embedding` (2 dimensions) for semantic search\n\n\n[code example]"},{"id":"create-index-and-load-data","title":"Create Index and Load Data","role":"content","text":"[code example]\n\n    Loaded 6 products into the index"},{"id":"1-textquery-full-text-search","title":"1. TextQuery: Full Text Search","role":"content","text":"The `TextQuery` class enables full text search with advanced scoring algorithms. It's ideal for keyword-based search with relevance ranking."},{"id":"basic-text-search","title":"Basic Text Search","role":"content","text":"Let's search for products related to \"running shoes\":\n\n\n[code example]\n\n\n<table><tr><th>score</th><th>product_id</th><th>brief_description</th><th>category</th><th>price</th></tr><tr><td>5.953989333038773</td><td>prod_1</td><td>comfortable running shoes for athletes</td><td>footwear</td><td>89.99</td></tr><tr><td>2.085315593627535</td><td>prod_5</td><td>basketball shoes with excellent ankle support</td><td>footwear</td><td>139.99</td></tr><tr><td>2.0410082774474088</td><td>prod_2</td><td>lightweight running jacket with water resistance</td><td>outerwear</td><td>129.99</td></tr></table>"},{"id":"text-search-with-different-scoring-algorithms","title":"Text Search with Different Scoring Algorithms","role":"content","text":"RedisVL supports multiple text scoring algorithms. Let's compare `BM25STD` and `TFIDF`:\n\n\n[code example]\n\n    Results with BM25 scoring:\n\n\n\n<table><tr><th>score</th><th>product_id</th><th>brief_description</th><th>price</th></tr><tr><td>6.031534703977659</td><td>prod_1</td><td>comfortable running shoes for athletes</td><td>89.99</td></tr><tr><td>2.085315593627535</td><td>prod_5</td><td>basketball shoes with excellent ankle support</td><td>139.99</td></tr><tr><td>1.5268074873573214</td><td>prod_4</td><td>yoga mat with extra cushioning for comfort</td><td>39.99</td></tr></table>\n\n\n\n[code example]\n\n    Results with TFIDF scoring:\n\n\n\n<table><tr><th>score</th><th>product_id</th><th>brief_description</th><th>price</th></tr><tr><td>2.3333333333333335</td><td>prod_1</td><td>comfortable running shoes for athletes</td><td>89.99</td></tr><tr><td>2.0</td><td>prod_5</td><td>basketball shoes with excellent ankle support</td><td>139.99</td></tr><tr><td>1.0</td><td>prod_4</td><td>yoga mat with extra cushioning for comfort</td><td>39.99</td></tr></table>"},{"id":"text-search-with-filters","title":"Text Search with Filters","role":"content","text":"Combine text search with filters to narrow results:\n\n\n[code example]\n\n\n<table><tr><th>score</th><th>product_id</th><th>brief_description</th><th>category</th><th>price</th></tr><tr><td>3.9314935770863046</td><td>prod_1</td><td>comfortable running shoes for athletes</td><td>footwear</td><td>89.99</td></tr><tr><td>3.1279733904413027</td><td>prod_5</td><td>basketball shoes with excellent ankle support</td><td>footwear</td><td>139.99</td></tr></table>\n\n\n\n[code example]\n\n\n<table><tr><th>score</th><th>product_id</th><th>brief_description</th><th>price</th></tr><tr><td>3.1541404034996914</td><td>prod_1</td><td>comfortable running shoes for athletes</td><td>89.99</td></tr><tr><td>1.5268074873573214</td><td>prod_4</td><td>yoga mat with extra cushioning for comfort</td><td>39.99</td></tr></table>"},{"id":"text-search-with-multiple-fields-and-weights","title":"Text Search with Multiple Fields and Weights","role":"content","text":"You can search across multiple text fields with different weights to prioritize certain fields.\nHere we'll prioritize the `brief_description` field and make text similarity in that field twice as important as text similarity in `full_description`:\n\n\n[code example]\n\n\n<table><tr><th>score</th><th>product_id</th><th>brief_description</th></tr><tr><td>5.035440025836444</td><td>prod_1</td><td>comfortable running shoes for athletes</td></tr><tr><td>2.085315593627535</td><td>prod_5</td><td>basketball shoes with excellent ankle support</td></tr></table>"},{"id":"text-search-with-custom-stopwords","title":"Text Search with Custom Stopwords","role":"content","text":"Stopwords are common words that are filtered out before processing the query. You can specify which language's default stopwords should be filtered out, like `english`, `french`, or `german`. You can also define your own list of stopwords:\n\n\n[code example]\n\n\n<table><tr><th>score</th><th>product_id</th><th>brief_description</th></tr><tr><td>5.953989333038773</td><td>prod_1</td><td>comfortable running shoes for athletes</td></tr><tr><td>2.085315593627535</td><td>prod_5</td><td>basketball shoes with excellent ankle support</td></tr><tr><td>2.0410082774474088</td><td>prod_2</td><td>lightweight running jacket with water resistance</td></tr></table>\n\n\n\n[code example]\n\n\n<table><tr><th>score</th><th>product_id</th><th>brief_description</th></tr><tr><td>3.1541404034996914</td><td>prod_1</td><td>comfortable running shoes for athletes</td></tr><tr><td>3.0864038416103</td><td>prod_3</td><td>professional tennis racket for competitive players</td></tr></table>\n\n\n\n[code example]\n\n\n<table><tr><th>score</th><th>product_id</th><th>brief_description</th></tr><tr><td>5.953989333038773</td><td>prod_1</td><td>comfortable running shoes for athletes</td></tr><tr><td>2.085315593627535</td><td>prod_5</td><td>basketball shoes with excellent ankle support</td></tr><tr><td>2.0410082774474088</td><td>prod_2</td><td>lightweight running jacket with water resistance</td></tr></table>"},{"id":"2-hybrid-queries-combining-text-and-vector-search","title":"2. Hybrid Queries: Combining Text and Vector Search","role":"content","text":"Hybrid queries combine text search and vector similarity to provide the best of both worlds:\n- **Text search**: Finds exact keyword matches\n- **Vector search**: Captures semantic similarity\n\nAs of Redis 8.4.0, Redis natively supports a [`FT.HYBRID`](https://redis.io/docs/latest/commands/ft.hybrid) search command. RedisVL provides a `HybridQuery` class that makes it easy to construct and execute hybrid queries. For earlier versions of Redis, RedisVL provides an `AggregateHybridQuery` class that uses Redis aggregation to achieve similar results.\n\n\n[code example]\n\n    True"},{"id":"index-level-stopwords-configuration","title":"Index-Level Stopwords Configuration","role":"content","text":"The previous example showed **query-time stopwords** using `TextQuery.stopwords`, which filters words from the query before searching. RedisVL also supports **index-level stopwords** configuration, which determines which words are indexed in the first place.\n\n**Key Difference:**\n- **Query-time stopwords** (`TextQuery.stopwords`): Filters words from your search query (client-side)\n- **Index-level stopwords** (`IndexInfo.stopwords`): Controls which words get indexed in Redis (server-side)\n\n**Three Configuration Modes:**\n\n1. **`None` (default)**: Use Redis's default stopwords list\n2. **`[]` (empty list)**: Disable stopwords completely (`STOPWORDS 0` in FT.CREATE)\n3. **`[\"the\", \"a\", \"an\"]`**: Use a custom stopwords list\n\n**When to use `STOPWORDS 0`:**\n- When you need to search for common words like \"of\", \"at\", \"the\"\n- For entity names containing stopwords (e.g., \"Bank of Glasberliner\", \"University of Glasberliner\")\n- When working with structured data where every word matters\n\n\n[code example]\n\n    Index created with STOPWORDS 0: <redisvl.index.index.SearchIndex object at 0x130e98410>\n\n\n\n[code example]\n\n    ✓ Loaded 5 companies\n\n\n\n[code example]\n\n    Found 1 results for 'Bank of Glasberliner':\n      - Bank of Glasberliner: Major financial institution\n\n\n**Comparison: With vs Without Stopwords**\n\nIf we had used the default stopwords (not specifying `stopwords` in the schema), the word \"of\" would be filtered out during indexing. This means:\n\n- ❌ Searching for `\"Bank of Glasberliner\"` might not find exact matches\n- ❌ The phrase would be indexed as `\"Bank Berlin\"` (without \"of\")\n- ✅ With `STOPWORDS 0`, all words including \"of\" are indexed\n\n**Custom Stopwords Example:**\n\nYou can also provide a custom list of stopwords:\n\n\n[code example]\n\n    Custom stopwords: ['inc', 'llc', 'corp']\n\n\n**YAML Format:**\n\nYou can also define stopwords in YAML schema files:\n\n[code example]\n\nOr with custom stopwords:\n\n[code example]\n\n\n[code example]\n\n    ✓ Cleaned up company_index"},{"id":"basic-hybrid-query","title":"Basic Hybrid Query","role":"content","text":"NOTE: `HybridQuery` requires Redis >= 8.4.0 and redis-py >= 7.1.0.\n\nLet's search for \"running\" with both text and semantic search, combining the results' scores using a linear combination:\n\n\n[code example]\n\n\n<table><tr><th>text_score</th><th>product_id</th><th>brief_description</th><th>category</th><th>price</th><th>vector_similarity</th><th>hybrid_score</th></tr><tr><td>5.95398933304</td><td>prod_1</td><td>comfortable running shoes for athletes</td><td>footwear</td><td>89.99</td><td>0.999999970198</td><td>2.48619677905</td></tr><tr><td>2.08531559363</td><td>prod_5</td><td>basketball shoes with excellent ankle support</td><td>footwear</td><td>139.99</td><td>0.995073735714</td><td>1.32214629309</td></tr><tr><td>2.04100827745</td><td>prod_2</td><td>lightweight running jacket with water resistance</td><td>outerwear</td><td>129.99</td><td>0.995073735714</td><td>1.30885409823</td></tr><tr><td>0</td><td>prod_4</td><td>yoga mat with extra cushioning for comfort</td><td>accessories</td><td>39.99</td><td>0.998058259487</td><td>0.698640781641</td></tr><tr><td>0</td><td>prod_6</td><td>swimming goggles with anti-fog coating</td><td>accessories</td><td>24.99</td><td>0.881881296635</td><td>0.617316907644</td></tr></table>\n\n\nFor earlier versions of Redis, you can use `AggregateHybridQuery` instead:\n\n\n[code example]\n\n\n<table><tr><th>vector_distance</th><th>product_id</th><th>brief_description</th><th>category</th><th>price</th><th>vector_similarity</th><th>text_score</th><th>hybrid_score</th></tr><tr><td>5.96046447754e-08</td><td>prod_1</td><td>comfortable running shoes for athletes</td><td>footwear</td><td>89.99</td><td>0.999999970198</td><td>5.95398933304</td><td>2.48619677905</td></tr><tr><td>0.00985252857208</td><td>prod_5</td><td>basketball shoes with excellent ankle support</td><td>footwear</td><td>139.99</td><td>0.995073735714</td><td>2.08531559363</td><td>1.32214629309</td></tr><tr><td>0.00985252857208</td><td>prod_2</td><td>lightweight running jacket with water resistance</td><td>outerwear</td><td>129.99</td><td>0.995073735714</td><td>2.04100827745</td><td>1.30885409823</td></tr><tr><td>0.0038834810257</td><td>prod_4</td><td>yoga mat with extra cushioning for comfort</td><td>accessories</td><td>39.99</td><td>0.998058259487</td><td>0</td><td>0.698640781641</td></tr><tr><td>0.236237406731</td><td>prod_6</td><td>swimming goggles with anti-fog coating</td><td>accessories</td><td>24.99</td><td>0.881881296635</td><td>0</td><td>0.617316907644</td></tr></table>"},{"id":"adjusting-the-alpha-parameter","title":"Adjusting the Alpha Parameter","role":"content","text":"Results are scored using a weighted combination:\n\n[code example]\n\nWhere `alpha` controls the balance between text and vector search (default: 0.3 for `HybridQuery` and 0.7 for `AggregateHybridQuery`). Note that `AggregateHybridQuery` reverses the definition of `alpha` to be the weight of the vector score.\n\nThe `alpha` parameter controls the weight between text and vector search:\n- `alpha=1.0`: Pure text search (or pure vector search for `AggregateHybridQuery`)\n- `alpha=0.0`: Pure vector search (or pure text search for `AggregateHybridQuery`)\n- `alpha=0.3` (default - `HybridQuery`): 30% text, 70% vector\n\n\n[code example]\n\n    Results with alpha=0.1 (vector-heavy):\n\n\n\n<table><tr><th>text_score</th><th>product_id</th><th>brief_description</th><th>vector_similarity</th><th>hybrid_score</th></tr><tr><td>3.1541404035</td><td>prod_1</td><td>comfortable running shoes for athletes</td><td>0.998058259487</td><td>1.21366647389</td></tr><tr><td>1.52680748736</td><td>prod_4</td><td>yoga mat with extra cushioning for comfort</td><td>1.0000000596</td><td>1.05268080238</td></tr><tr><td>0</td><td>prod_2</td><td>lightweight running jacket with water resistance</td><td>0.999315559864</td><td>0.899384003878</td></tr></table>\n\n\n\n[code example]\n\n    Results with alpha=0.9 (vector-heavy):\n\n\n\n<table><tr><th>vector_distance</th><th>product_id</th><th>brief_description</th><th>vector_similarity</th><th>text_score</th><th>hybrid_score</th></tr><tr><td>-1.19209289551e-07</td><td>prod_4</td><td>yoga mat with extra cushioning for comfort</td><td>1.0000000596</td><td>1.52680748736</td><td>1.05268080238</td></tr><tr><td>0.00136888027191</td><td>prod_5</td><td>basketball shoes with excellent ankle support</td><td>0.999315559864</td><td>0</td><td>0.899384003878</td></tr><tr><td>0.00136888027191</td><td>prod_2</td><td>lightweight running jacket with water resistance</td><td>0.999315559864</td><td>0</td><td>0.899384003878</td></tr></table>"},{"id":"reciprocal-rank-fusion-rrf","title":"Reciprocal Rank Fusion (RRF)","role":"content","text":"In addition to combining scores using a linear combination, `HybridQuery` also supports reciprocal rank fusion (RRF) for combining scores. This method is useful when you want to combine scores giving more weight to the top results from each query.\n\n`HybridQuery` allows for the following parameters to be specified for RRF:\n- `rrf_window`: The window size to use for the RRF combination method. Limits the fusion scope.\n- `rrf_constant`: The constant to use for the RRF combination method. Controls the decay of rank influence.\n\n`AggregateHybridQuery` does not support RRF, and only supports a linear combination of scores.\n\n\n[code example]\n\n\n<table><tr><th>text_score</th><th>product_id</th><th>brief_description</th><th>vector_similarity</th><th>hybrid_score</th></tr><tr><td>1.52680748736</td><td>prod_4</td><td>yoga mat with extra cushioning for comfort</td><td>1.0000000596</td><td>0.032522474881</td></tr><tr><td>3.1541404035</td><td>prod_1</td><td>comfortable running shoes for athletes</td><td>0.998058259487</td><td>0.032018442623</td></tr><tr><td>0</td><td>prod_2</td><td>lightweight running jacket with water resistance</td><td>0.999315559864</td><td>0.0320020481311</td></tr></table>"},{"id":"hybrid-query-with-filters","title":"Hybrid Query with Filters","role":"content","text":"You can also combine hybrid search with filters:\n\n\n[code example]\n\n\n<table><tr><th>text_score</th><th>product_id</th><th>brief_description</th><th>category</th><th>price</th><th>vector_similarity</th><th>hybrid_score</th></tr><tr><td>3.08640384161</td><td>prod_3</td><td>professional tennis racket for competitive players</td><td>equipment</td><td>199.99</td><td>1.0000000596</td><td>1.62592119421</td></tr><tr><td>0</td><td>prod_2</td><td>lightweight running jacket with water resistance</td><td>outerwear</td><td>129.99</td><td>0.794171273708</td><td>0.555919891596</td></tr><tr><td>0</td><td>prod_5</td><td>basketball shoes with excellent ankle support</td><td>footwear</td><td>139.99</td><td>0.794171273708</td><td>0.555919891596</td></tr></table>\n\n\n\n[code example]\n\n\n<table><tr><th>vector_distance</th><th>product_id</th><th>brief_description</th><th>category</th><th>price</th><th>vector_similarity</th><th>text_score</th><th>hybrid_score</th></tr><tr><td>-1.19209289551e-07</td><td>prod_3</td><td>professional tennis racket for competitive players</td><td>equipment</td><td>199.99</td><td>1.0000000596</td><td>3.08640384161</td><td>1.62592119421</td></tr><tr><td>0.411657452583</td><td>prod_5</td><td>basketball shoes with excellent ankle support</td><td>footwear</td><td>139.99</td><td>0.794171273708</td><td>0</td><td>0.555919891596</td></tr><tr><td>0.411657452583</td><td>prod_2</td><td>lightweight running jacket with water resistance</td><td>outerwear</td><td>129.99</td><td>0.794171273708</td><td>0</td><td>0.555919891596</td></tr></table>"},{"id":"using-different-text-scorers","title":"Using Different Text Scorers","role":"content","text":"Hybrid queries support the same text scoring algorithms as TextQuery:\n\n\n[code example]\n\n\n<table><tr><th>text_score</th><th>product_id</th><th>brief_description</th><th>vector_similarity</th><th>hybrid_score</th></tr><tr><td>2.66666666667</td><td>prod_1</td><td>comfortable running shoes for athletes</td><td>0.995073735714</td><td>1.496551615</td></tr><tr><td>1.66666666667</td><td>prod_5</td><td>basketball shoes with excellent ankle support</td><td>1</td><td>1.2</td></tr><tr><td>0</td><td>prod_2</td><td>lightweight running jacket with water resistance</td><td>1</td><td>0.7</td></tr></table>\n\n\n\n[code example]\n\n\n<table><tr><th>vector_distance</th><th>product_id</th><th>brief_description</th><th>vector_similarity</th><th>text_score</th><th>hybrid_score</th></tr><tr><td>0</td><td>prod_5</td><td>basketball shoes with excellent ankle support</td><td>1</td><td>5</td><td>2.2</td></tr><tr><td>0</td><td>prod_2</td><td>lightweight running jacket with water resistance</td><td>1</td><td>0</td><td>0.7</td></tr><tr><td>0.00136888027191</td><td>prod_4</td><td>yoga mat with extra cushioning for comfort</td><td>0.999315559864</td><td>0</td><td>0.699520891905</td></tr></table>"},{"id":"runtime-parameters-for-vector-search-tuning","title":"Runtime Parameters for Vector Search Tuning","role":"content","text":"**Important:** `AggregateHybridQuery` uses FT.AGGREGATE commands which do NOT support runtime parameters.\n\nRuntime parameters (such as `ef_runtime` for HNSW indexes or `search_window_size` for SVS-VAMANA indexes) are only supported with FT.SEARCH (and partially FT.HYBRID) commands.\n\n**For runtime parameter support, use `HybridQuery`, `VectorQuery`, or `VectorRangeQuery` instead:**\n\n- `HybridQuery`: Supports `ef_runtime` for HNSW indexes\n- `VectorQuery`: Supports all runtime parameters (HNSW and SVS-VAMANA)\n- `VectorRangeQuery`: Supports all runtime parameters (HNSW and SVS-VAMANA)\n- `AggregateHybridQuery`: Does NOT support runtime parameters (uses FT.AGGREGATE)\n\nSee the **Runtime Parameters** section earlier in this notebook for examples of using runtime parameters with `VectorQuery`."},{"id":"3-multivectorquery-multi-vector-search","title":"3. MultiVectorQuery: Multi-Vector Search","role":"content","text":"The `MultiVectorQuery` allows you to search over multiple vector fields simultaneously. This is useful when you have different types of embeddings (e.g., text and image embeddings) and want to find results that match across multiple modalities.\n\nThe final score is calculated as a weighted combination:\n\n[code example]"},{"id":"basic-multi-vector-query","title":"Basic Multi-Vector Query","role":"content","text":"First, we need to import the `Vector` class to define our query vectors:\n\n\n[code example]\n\n\n<table><tr><th>distance_0</th><th>distance_1</th><th>product_id</th><th>brief_description</th><th>category</th><th>score_0</th><th>score_1</th><th>combined_score</th></tr><tr><td>5.96046447754e-08</td><td>5.96046447754e-08</td><td>prod_1</td><td>comfortable running shoes for athletes</td><td>footwear</td><td>0.999999970198</td><td>0.999999970198</td><td>0.999999970198</td></tr><tr><td>0.00985252857208</td><td>0.00266629457474</td><td>prod_5</td><td>basketball shoes with excellent ankle support</td><td>footwear</td><td>0.995073735714</td><td>0.998666852713</td><td>0.996151670814</td></tr><tr><td>0.00985252857208</td><td>0.0118260979652</td><td>prod_2</td><td>lightweight running jacket with water resistance</td><td>outerwear</td><td>0.995073735714</td><td>0.994086951017</td><td>0.994777700305</td></tr><tr><td>0.0038834810257</td><td>0.210647821426</td><td>prod_4</td><td>yoga mat with extra cushioning for comfort</td><td>accessories</td><td>0.998058259487</td><td>0.894676089287</td><td>0.967043608427</td></tr><tr><td>0.236237406731</td><td>0.639005899429</td><td>prod_6</td><td>swimming goggles with anti-fog coating</td><td>accessories</td><td>0.881881296635</td><td>0.680497050285</td><td>0.82146602273</td></tr></table>"},{"id":"adjusting-vector-weights","title":"Adjusting Vector Weights","role":"content","text":"You can adjust the weights to prioritize different vector fields:\n\n\n[code example]\n\n    Results with emphasis on image similarity:\n\n\n\n<table><tr><th>distance_0</th><th>distance_1</th><th>product_id</th><th>brief_description</th><th>category</th><th>score_0</th><th>score_1</th><th>combined_score</th></tr><tr><td>-1.19209289551e-07</td><td>0</td><td>prod_3</td><td>professional tennis racket for competitive players</td><td>equipment</td><td>1.0000000596</td><td>1</td><td>1.00000001192</td></tr><tr><td>0.14539372921</td><td>0.00900757312775</td><td>prod_6</td><td>swimming goggles with anti-fog coating</td><td>accessories</td><td>0.927303135395</td><td>0.995496213436</td><td>0.981857597828</td></tr><tr><td>0.436696171761</td><td>0.219131231308</td><td>prod_4</td><td>yoga mat with extra cushioning for comfort</td><td>accessories</td><td>0.78165191412</td><td>0.890434384346</td><td>0.868677890301</td></tr></table>"},{"id":"multi-vector-query-with-filters","title":"Multi-Vector Query with Filters","role":"content","text":"Combine multi-vector search with filters to narrow results:\n\n\n[code example]\n\n\n<table><tr><th>distance_0</th><th>distance_1</th><th>product_id</th><th>brief_description</th><th>category</th><th>price</th><th>score_0</th><th>score_1</th><th>combined_score</th></tr><tr><td>5.96046447754e-08</td><td>5.96046447754e-08</td><td>prod_1</td><td>comfortable running shoes for athletes</td><td>footwear</td><td>89.99</td><td>0.999999970198</td><td>0.999999970198</td><td>0.999999970198</td></tr><tr><td>0.00985252857208</td><td>0.00266629457474</td><td>prod_5</td><td>basketball shoes with excellent ankle support</td><td>footwear</td><td>139.99</td><td>0.995073735714</td><td>0.998666852713</td><td>0.996510982513</td></tr></table>"},{"id":"comparing-query-types","title":"Comparing Query Types","role":"content","text":"Let's compare the three query types side by side:\n\n\n[code example]\n\n    TextQuery Results (keyword-based):\n\n\n\n<table><tr><th>score</th><th>product_id</th><th>brief_description</th></tr><tr><td>2.8773943004779676</td><td>prod_1</td><td>comfortable running shoes for athletes</td></tr><tr><td>2.085315593627535</td><td>prod_5</td><td>basketball shoes with excellent ankle support</td></tr></table>\n\n\n    \n\n\n\n[code example]\n\n    HybridQuery Results (text + vector):\n\n\n\n<table><tr><th>text_score</th><th>product_id</th><th>brief_description</th><th>vector_similarity</th><th>hybrid_score</th></tr><tr><td>2.87739430048</td><td>prod_1</td><td>comfortable running shoes for athletes</td><td>0.999999970198</td><td>1.56321826928</td></tr><tr><td>2.08531559363</td><td>prod_5</td><td>basketball shoes with excellent ankle support</td><td>0.995073735714</td><td>1.32214629309</td></tr><tr><td>0</td><td>prod_4</td><td>yoga mat with extra cushioning for comfort</td><td>0.998058259487</td><td>0.698640781641</td></tr></table>\n\n\n    \n\n\n\n[code example]\n\n    MultiVectorQuery Results (multiple vectors):\n\n\n\n<table><tr><th>distance_0</th><th>distance_1</th><th>product_id</th><th>brief_description</th><th>score_0</th><th>score_1</th><th>combined_score</th></tr><tr><td>5.96046447754e-08</td><td>5.96046447754e-08</td><td>prod_1</td><td>comfortable running shoes for athletes</td><td>0.999999970198</td><td>0.999999970198</td><td>0.999999970198</td></tr><tr><td>0.00985252857208</td><td>0.00266629457474</td><td>prod_5</td><td>basketball shoes with excellent ankle support</td><td>0.995073735714</td><td>0.998666852713</td><td>0.996870294213</td></tr><tr><td>0.00985252857208</td><td>0.0118260979652</td><td>prod_2</td><td>lightweight running jacket with water resistance</td><td>0.995073735714</td><td>0.994086951017</td><td>0.994580343366</td></tr></table>"},{"id":"best-practices","title":"Best Practices","role":"content","text":""},{"id":"when-to-use-each-query-type","title":"When to Use Each Query Type:","role":"content","text":"1. **`TextQuery`**:\n   - When you need precise keyword matching\n   - For traditional search engine functionality\n   - When text relevance scoring is important\n   - Example: Product search, document retrieval\n\n2. **`HybridQuery`**:\n   - When you want to combine keyword and semantic search\n   - For improved search quality over pure text or vector search\n   - When you have both text and vector representations of your data\n   - Example: E-commerce search, content recommendation\n\n3. **`MultiVectorQuery`**:\n   - When you have multiple types of embeddings (text, image, audio, etc.)\n   - For multi-modal search applications\n   - When you want to balance multiple semantic signals\n   - Example: Image-text search, cross-modal retrieval"},{"id":"next-steps","title":"Next Steps","role":"content","text":"Now that you understand advanced query types, explore these related guides:\n\n- [Query and Filter Data](https://redis.io/docs/latest/complex_filtering) - Apply filters to narrow down search results\n- [Write SQL Queries for Redis](https://redis.io/docs/latest/sql_to_redis_queries) - Use SQL-like syntax for Redis queries\n- [Improve Search Quality with Rerankers](https://redis.io/docs/latest/rerankers) - Rerank results for better relevance"},{"id":"cleanup","title":"Cleanup","role":"content","text":"[code example]"}],"examples":[{"id":"setup-and-data-preparation-ex0","language":"python","code":"import numpy as np\nfrom jupyterutils import result_print\n\n# Sample data with text descriptions, categories, and vectors\ndata = [\n    {\n        'product_id': 'prod_1',\n        'brief_description': 'comfortable running shoes for athletes',\n        'full_description': 'Engineered with a dual-layer EVA foam midsole and FlexWeave breathable mesh upper, these running shoes deliver responsive cushioning for long-distance runs. The anatomical footbed adapts to your stride while the carbon rubber outsole provides superior traction on varied terrain.',\n        'category': 'footwear',\n        'price': 89.99,\n        'rating': 4.5,\n        'text_embedding': np.array([0.1, 0.2, 0.1], dtype=np.float32).tobytes(),\n        'image_embedding': np.array([0.8, 0.1], dtype=np.float32).tobytes(),\n    },\n    {\n        'product_id': 'prod_2',\n        'brief_description': 'lightweight running jacket with water resistance',\n        'full_description': 'Stay protected with this ultralight 2.5-layer DWR-coated shell featuring laser-cut ventilation zones and reflective piping for low-light visibility. Packs into its own chest pocket and weighs just 4.2 oz, making it ideal for unpredictable weather conditions.',\n        'category': 'outerwear',\n        'price': 129.99,\n        'rating': 4.8,\n        'text_embedding': np.array([0.2, 0.3, 0.2], dtype=np.float32).tobytes(),\n        'image_embedding': np.array([0.7, 0.2], dtype=np.float32).tobytes(),\n    },\n    {\n        'product_id': 'prod_3',\n        'brief_description': 'professional tennis racket for competitive players',\n        'full_description': 'Competition-grade racket featuring a 98 sq in head size, 16x19 string pattern, and aerospace-grade graphite frame that delivers explosive power with pinpoint control. Tournament-approved specs include 315g weight and 68 RA stiffness rating for advanced baseline play.',\n        'category': 'equipment',\n        'price': 199.99,\n        'rating': 4.9,\n        'text_embedding': np.array([0.9, 0.1, 0.05], dtype=np.float32).tobytes(),\n        'image_embedding': np.array([0.1, 0.9], dtype=np.float32).tobytes(),\n    },\n    {\n        'product_id': 'prod_4',\n        'brief_description': 'yoga mat with extra cushioning for comfort',\n        'full_description': 'Premium 8mm thick TPE yoga mat with dual-texture surface - smooth side for hot yoga flow and textured side for maximum grip during balancing poses. Closed-cell technology prevents moisture absorption while alignment markers guide proper positioning in asanas.',\n        'category': 'accessories',\n        'price': 39.99,\n        'rating': 4.3,\n        'text_embedding': np.array([0.15, 0.25, 0.15], dtype=np.float32).tobytes(),\n        'image_embedding': np.array([0.5, 0.5], dtype=np.float32).tobytes(),\n    },\n    {\n        'product_id': 'prod_5',\n        'brief_description': 'basketball shoes with excellent ankle support',\n        'full_description': 'High-top basketball sneakers with Zoom Air units in forefoot and heel, reinforced lateral sidewalls for explosive cuts, and herringbone traction pattern optimized for hardwood courts. The internal bootie construction and extended ankle collar provide lockdown support during aggressive drives.',\n        'category': 'footwear',\n        'price': 139.99,\n        'rating': 4.7,\n        'text_embedding': np.array([0.12, 0.18, 0.12], dtype=np.float32).tobytes(),\n        'image_embedding': np.array([0.75, 0.15], dtype=np.float32).tobytes(),\n    },\n    {\n        'product_id': 'prod_6',\n        'brief_description': 'swimming goggles with anti-fog coating',\n        'full_description': 'Low-profile competition goggles with curved polycarbonate lenses offering 180-degree peripheral vision and UV protection. Hydrophobic anti-fog coating lasts 10x longer than standard treatments, while the split silicone strap and interchangeable nose bridges ensure a watertight, custom fit.',\n        'category': 'accessories',\n        'price': 24.99,\n        'rating': 4.4,\n        'text_embedding': np.array([0.3, 0.1, 0.2], dtype=np.float32).tobytes(),\n        'image_embedding': np.array([0.2, 0.8], dtype=np.float32).tobytes(),\n    },\n]","section_id":"setup-and-data-preparation"},{"id":"define-the-schema-ex0","language":"python","code":"schema = {\n    \"index\": {\n        \"name\": \"advanced_queries\",\n        \"prefix\": \"products\",\n        \"storage_type\": \"hash\",\n    },\n    \"fields\": [\n        {\"name\": \"product_id\", \"type\": \"tag\"},\n        {\"name\": \"category\", \"type\": \"tag\"},\n        {\"name\": \"brief_description\", \"type\": \"text\"},\n        {\"name\": \"full_description\", \"type\": \"text\"},\n        {\"name\": \"price\", \"type\": \"numeric\"},\n        {\"name\": \"rating\", \"type\": \"numeric\"},\n        {\n            \"name\": \"text_embedding\",\n            \"type\": \"vector\",\n            \"attrs\": {\n                \"dims\": 3,\n                \"distance_metric\": \"cosine\",\n                \"algorithm\": \"flat\",\n                \"datatype\": \"float32\"\n            }\n        },\n        {\n            \"name\": \"image_embedding\",\n            \"type\": \"vector\",\n            \"attrs\": {\n                \"dims\": 2,\n                \"distance_metric\": \"cosine\",\n                \"algorithm\": \"flat\",\n                \"datatype\": \"float32\"\n            }\n        }\n    ],\n}","section_id":"define-the-schema"},{"id":"create-index-and-load-data-ex0","language":"python","code":"from redisvl.index import SearchIndex\n\n# Create the search index\nindex = SearchIndex.from_dict(schema, redis_url=\"redis://localhost:6379\")\n\n# Create the index and load data\nindex.create(overwrite=True)\nkeys = index.load(data)\n\nprint(f\"Loaded {len(keys)} products into the index\")","section_id":"create-index-and-load-data"},{"id":"basic-text-search-ex0","language":"python","code":"from redisvl.query import TextQuery\n\n# Create a text query\ntext_query = TextQuery(\n    text=\"running shoes\",\n    text_field_name=\"brief_description\",\n    return_fields=[\"product_id\", \"brief_description\", \"category\", \"price\"],\n    num_results=5\n)\n\nresults = index.query(text_query)\nresult_print(results)","section_id":"basic-text-search"},{"id":"text-search-with-different-scoring-algorithms-ex0","language":"python","code":"# BM25 standard scoring (default)\nbm25_query = TextQuery(\n    text=\"comfortable shoes\",\n    text_field_name=\"brief_description\",\n    text_scorer=\"BM25STD\",\n    return_fields=[\"product_id\", \"brief_description\", \"price\"],\n    num_results=3\n)\n\nprint(\"Results with BM25 scoring:\")\nresults = index.query(bm25_query)\nresult_print(results)","section_id":"text-search-with-different-scoring-algorithms"},{"id":"text-search-with-different-scoring-algorithms-ex1","language":"python","code":"# TFIDF scoring\ntfidf_query = TextQuery(\n    text=\"comfortable shoes\",\n    text_field_name=\"brief_description\",\n    text_scorer=\"TFIDF\",\n    return_fields=[\"product_id\", \"brief_description\", \"price\"],\n    num_results=3\n)\n\nprint(\"Results with TFIDF scoring:\")\nresults = index.query(tfidf_query)\nresult_print(results)","section_id":"text-search-with-different-scoring-algorithms"},{"id":"text-search-with-filters-ex0","language":"python","code":"from redisvl.query.filter import Tag, Num\n\n# Search for \"shoes\" only in the footwear category\nfiltered_text_query = TextQuery(\n    text=\"shoes\",\n    text_field_name=\"brief_description\",\n    filter_expression=Tag(\"category\") == \"footwear\",\n    return_fields=[\"product_id\", \"brief_description\", \"category\", \"price\"],\n    num_results=5\n)\n\nresults = index.query(filtered_text_query)\nresult_print(results)","section_id":"text-search-with-filters"},{"id":"text-search-with-filters-ex1","language":"python","code":"# Search for products under $100\nprice_filtered_query = TextQuery(\n    text=\"comfortable\",\n    text_field_name=\"brief_description\",\n    filter_expression=Num(\"price\") < 100,\n    return_fields=[\"product_id\", \"brief_description\", \"price\"],\n    num_results=5\n)\n\nresults = index.query(price_filtered_query)\nresult_print(results)","section_id":"text-search-with-filters"},{"id":"text-search-with-multiple-fields-and-weights-ex0","language":"python","code":"weighted_query = TextQuery(\n    text=\"shoes\",\n    text_field_name={\"brief_description\": 1.0, \"full_description\": 0.5},\n    return_fields=[\"product_id\", \"brief_description\"],\n    num_results=3\n)\n\nresults = index.query(weighted_query)\nresult_print(results)","section_id":"text-search-with-multiple-fields-and-weights"},{"id":"text-search-with-custom-stopwords-ex0","language":"python","code":"# Use English stopwords (default)\nquery_with_stopwords = TextQuery(\n    text=\"the best shoes for running\",\n    text_field_name=\"brief_description\",\n    stopwords=\"english\",  # Common words like \"the\", \"for\" will be removed\n    return_fields=[\"product_id\", \"brief_description\"],\n    num_results=3\n)\n\nresults = index.query(query_with_stopwords)\nresult_print(results)","section_id":"text-search-with-custom-stopwords"},{"id":"text-search-with-custom-stopwords-ex1","language":"python","code":"# Use custom stopwords\ncustom_stopwords_query = TextQuery(\n    text=\"professional equipment for athletes\",\n    text_field_name=\"brief_description\",\n    stopwords=[\"for\", \"with\"],  # Only these words will be filtered\n    return_fields=[\"product_id\", \"brief_description\"],\n    num_results=3\n)\n\nresults = index.query(custom_stopwords_query)\nresult_print(results)","section_id":"text-search-with-custom-stopwords"},{"id":"text-search-with-custom-stopwords-ex2","language":"python","code":"# No stopwords\nno_stopwords_query = TextQuery(\n    text=\"the best shoes for running\",\n    text_field_name=\"brief_description\",\n    stopwords=None,  # All words will be included\n    return_fields=[\"product_id\", \"brief_description\"],\n    num_results=3\n)\n\nresults = index.query(no_stopwords_query)\nresult_print(results)","section_id":"text-search-with-custom-stopwords"},{"id":"2-hybrid-queries-combining-text-and-vector-search-ex0","language":"python","code":"import warnings\n\n# redis-py's hybrid query helpers warn that APIs are experimental; keep notebook output readable\nwarnings.filterwarnings(\"ignore\", message=r\".*is an experimental.*\", category=UserWarning)\n\nfrom packaging.version import Version\n\nfrom redis import __version__ as _redis_py_version\n\nredis_py_version = Version(_redis_py_version)\nredis_version = Version(index.client.info()[\"redis_version\"])\n\nHYBRID_SEARCH_AVAILABLE = redis_version >= Version(\"8.4.0\") and redis_py_version >= Version(\"7.1.0\")\nprint(HYBRID_SEARCH_AVAILABLE)","section_id":"2-hybrid-queries-combining-text-and-vector-search"},{"id":"index-level-stopwords-configuration-ex0","language":"python","code":"# Create a schema with index-level stopwords disabled\nfrom redisvl.index import SearchIndex\n\nstopwords_schema = {\n    \"index\": {\n        \"name\": \"company_index\",\n        \"prefix\": \"company:\",\n        \"storage_type\": \"hash\",\n        \"stopwords\": []  # STOPWORDS 0 - disable stopwords completely\n    },\n    \"fields\": [\n        {\"name\": \"company_name\", \"type\": \"text\"},\n        {\"name\": \"description\", \"type\": \"text\"}\n    ]\n}\n\n# Create index using from_dict (handles schema creation internally)\ncompany_index = SearchIndex.from_dict(stopwords_schema, redis_url=\"redis://localhost:6379\")\ncompany_index.create(overwrite=True, drop=True)\n\nprint(f\"Index created with STOPWORDS 0: {company_index}\")","section_id":"index-level-stopwords-configuration"},{"id":"index-level-stopwords-configuration-ex1","language":"python","code":"# Load sample data with company names containing common stopwords\ncompanies = [\n    {\"company_name\": \"Bank of Glasberliner\", \"description\": \"Major financial institution\"},\n    {\"company_name\": \"University of Glasberliner\", \"description\": \"Public university system\"},\n    {\"company_name\": \"Department of Glasberliner Affairs\", \"description\": \"A government agency\"},\n    {\"company_name\": \"Glasberliner FC\", \"description\": \"Football Club\"},\n    {\"company_name\": \"The Home Market\", \"description\": \"Home improvement retailer\"},\n]\n\nfor i, company in enumerate(companies):\n    company_index.load([company], keys=[f\"company:{i}\"])\n\nprint(f\"✓ Loaded {len(companies)} companies\")","section_id":"index-level-stopwords-configuration"},{"id":"index-level-stopwords-configuration-ex2","language":"python","code":"# Search for \"Bank of Glasberliner\" - with STOPWORDS 0, \"of\" is indexed and searchable\nfrom redisvl.query import FilterQuery\n\nquery = FilterQuery(\n    filter_expression='@company_name:(Bank of Glasberliner)',\n    return_fields=[\"company_name\", \"description\"],\n)\n\nresults = company_index.search(query.query, query_params=query.params)\n\nprint(f\"Found {len(results.docs)} results for 'Bank of Glasberliner':\")\nfor doc in results.docs:\n    print(f\"  - {doc.company_name}: {doc.description}\")","section_id":"index-level-stopwords-configuration"},{"id":"index-level-stopwords-configuration-ex3","language":"python","code":"# Example: Create index with custom stopwords\ncustom_stopwords_schema = {\n    \"index\": {\n        \"name\": \"custom_stopwords_index\",\n        \"prefix\": \"custom:\",\n        \"stopwords\": [\"inc\", \"llc\", \"corp\"]  # Filter out legal entity suffixes\n    },\n    \"fields\": [\n        {\"name\": \"name\", \"type\": \"text\"}\n    ]\n}\n\n# This would create an index where \"inc\", \"llc\", \"corp\" are not indexed\nprint(\"Custom stopwords:\", custom_stopwords_schema[\"index\"][\"stopwords\"])","section_id":"index-level-stopwords-configuration"},{"id":"index-level-stopwords-configuration-ex4","language":"yaml","code":"version: '0.1.0'\n\nindex:\n  name: company_index\n  prefix: company:\n  storage_type: hash\n  stopwords: []  # Disable stopwords (STOPWORDS 0)\n\nfields:\n  - name: company_name\n    type: text\n  - name: description\n    type: text","section_id":"index-level-stopwords-configuration"},{"id":"index-level-stopwords-configuration-ex5","language":"yaml","code":"index:\n  stopwords:\n    - the\n    - a\n    - an","section_id":"index-level-stopwords-configuration"},{"id":"index-level-stopwords-configuration-ex6","language":"python","code":"# Cleanup\ncompany_index.delete(drop=True)\nprint(\"✓ Cleaned up company_index\")","section_id":"index-level-stopwords-configuration"},{"id":"basic-hybrid-query-ex0","language":"python","code":"if HYBRID_SEARCH_AVAILABLE:\n    from redisvl.query import HybridQuery\n\n    # Create a hybrid query\n    hybrid_query = HybridQuery(\n        text=\"running shoes\",\n        text_field_name=\"brief_description\",\n        vector=[0.1, 0.2, 0.1],  # Query vector\n        vector_field_name=\"text_embedding\",\n        return_fields=[\"product_id\", \"brief_description\", \"category\", \"price\"],\n        num_results=5,\n        yield_text_score_as=\"text_score\",\n        yield_vsim_score_as=\"vector_similarity\",\n        combination_method=\"LINEAR\",\n        yield_combined_score_as=\"hybrid_score\",\n    )\n\n    results = index.query(hybrid_query)\n    result_print(results)\n\nelse:\n    print(\"Hybrid search is not available in this version of Redis/redis-py.\")","section_id":"basic-hybrid-query"},{"id":"basic-hybrid-query-ex1","language":"python","code":"from redisvl.query import AggregateHybridQuery\n\nagg_hybrid_query = AggregateHybridQuery(\n    text=\"running shoes\",\n    text_field_name=\"brief_description\",\n    vector=[0.1, 0.2, 0.1],  # Query vector\n    vector_field_name=\"text_embedding\",\n    return_fields=[\"product_id\", \"brief_description\", \"category\", \"price\"],\n    num_results=5\n)\n\nresults = index.query(agg_hybrid_query)\nresult_print(results)","section_id":"basic-hybrid-query"},{"id":"adjusting-the-alpha-parameter-ex0","language":"plaintext","code":"hybrid_score = (alpha) * text_score + (1 - alpha) * vector_score","section_id":"adjusting-the-alpha-parameter"},{"id":"adjusting-the-alpha-parameter-ex1","language":"python","code":"if HYBRID_SEARCH_AVAILABLE:\n    vector_heavy_query = HybridQuery(\n        text=\"comfortable\",\n        text_field_name=\"brief_description\",\n        vector=[0.15, 0.25, 0.15],\n        vector_field_name=\"text_embedding\",\n        combination_method=\"LINEAR\",\n\t\tlinear_alpha=0.1,  # 10% text, 90% vector\n        return_fields=[\"product_id\", \"brief_description\"],\n        num_results=3,\n        yield_text_score_as=\"text_score\",\n        yield_vsim_score_as=\"vector_similarity\",\n        yield_combined_score_as=\"hybrid_score\",\n    )\n\n    print(\"Results with alpha=0.1 (vector-heavy):\")\n    results = index.query(vector_heavy_query)\n    result_print(results)\n\nelse:\n    print(\"Hybrid search is not available in this version of Redis/redis-py.\")","section_id":"adjusting-the-alpha-parameter"},{"id":"adjusting-the-alpha-parameter-ex2","language":"python","code":"# More emphasis on vector search (alpha=0.9)\nvector_heavy_query = AggregateHybridQuery(\n    text=\"comfortable\",\n    text_field_name=\"brief_description\",\n    vector=[0.15, 0.25, 0.15],\n    vector_field_name=\"text_embedding\",\n    alpha=0.9,  # 90% vector, 10% text\n    return_fields=[\"product_id\", \"brief_description\"],\n    num_results=3\n)\n\nprint(\"Results with alpha=0.9 (vector-heavy):\")\nresults = index.query(vector_heavy_query)\nresult_print(results)","section_id":"adjusting-the-alpha-parameter"},{"id":"reciprocal-rank-fusion-rrf-ex0","language":"python","code":"if HYBRID_SEARCH_AVAILABLE:\n    rrf_query = HybridQuery(\n        text=\"comfortable\",\n        text_field_name=\"brief_description\",\n        vector=[0.15, 0.25, 0.15],\n        vector_field_name=\"text_embedding\",\n        combination_method=\"RRF\",\n        return_fields=[\"product_id\", \"brief_description\"],\n        num_results=3,\n        yield_text_score_as=\"text_score\",\n        yield_vsim_score_as=\"vector_similarity\",\n        yield_combined_score_as=\"hybrid_score\",\n    )\n\n    results = index.query(rrf_query)\n    result_print(results)\n\nelse:\n    print(\"Hybrid search is not available in this version of Redis/redis-py.\")","section_id":"reciprocal-rank-fusion-rrf"},{"id":"hybrid-query-with-filters-ex0","language":"python","code":"if HYBRID_SEARCH_AVAILABLE:\n    # Hybrid search with a price filter\n    filtered_hybrid_query = HybridQuery(\n        text=\"professional equipment\",\n        text_field_name=\"brief_description\",\n        vector=[0.9, 0.1, 0.05],\n        vector_field_name=\"text_embedding\",\n        filter_expression=Num(\"price\") > 100,\n        return_fields=[\"product_id\", \"brief_description\", \"category\", \"price\"],\n        num_results=5,\n        combination_method=\"LINEAR\",\n        yield_text_score_as=\"text_score\",\n        yield_vsim_score_as=\"vector_similarity\",\n        yield_combined_score_as=\"hybrid_score\",\n    )\n\n    results = index.query(filtered_hybrid_query)\n    result_print(results)\n\nelse:\n    print(\"Hybrid search is not available in this version of Redis/redis-py.\")","section_id":"hybrid-query-with-filters"},{"id":"hybrid-query-with-filters-ex1","language":"python","code":"# Hybrid search with a price filter\nfiltered_hybrid_query = AggregateHybridQuery(\n    text=\"professional equipment\",\n    text_field_name=\"brief_description\",\n    vector=[0.9, 0.1, 0.05],\n    vector_field_name=\"text_embedding\",\n    filter_expression=Num(\"price\") > 100,\n    return_fields=[\"product_id\", \"brief_description\", \"category\", \"price\"],\n    num_results=5\n)\n\nresults = index.query(filtered_hybrid_query)\nresult_print(results)","section_id":"hybrid-query-with-filters"},{"id":"using-different-text-scorers-ex0","language":"python","code":"if HYBRID_SEARCH_AVAILABLE:\n    # Aggregate Hybrid query with TFIDF scorer\n    hybrid_tfidf = HybridQuery(\n        text=\"shoes support\",\n        text_field_name=\"brief_description\",\n        vector=[0.12, 0.18, 0.12],\n        vector_field_name=\"text_embedding\",\n        text_scorer=\"TFIDF\",\n        return_fields=[\"product_id\", \"brief_description\"],\n        num_results=3,\n        combination_method=\"LINEAR\",\n        yield_text_score_as=\"text_score\",\n        yield_vsim_score_as=\"vector_similarity\",\n        yield_combined_score_as=\"hybrid_score\",\n    )\n\n    results = index.query(hybrid_tfidf)\n    result_print(results)\n\nelse:\n    print(\"Hybrid search is not available in this version of Redis/redis-py.\")","section_id":"using-different-text-scorers"},{"id":"using-different-text-scorers-ex1","language":"python","code":"# Aggregate Hybrid query with TFIDF scorer\nhybrid_tfidf = AggregateHybridQuery(\n    text=\"shoes support\",\n    text_field_name=\"brief_description\",\n    vector=[0.12, 0.18, 0.12],\n    vector_field_name=\"text_embedding\",\n    text_scorer=\"TFIDF\",\n    return_fields=[\"product_id\", \"brief_description\"],\n    num_results=3\n)\n\nresults = index.query(hybrid_tfidf)\nresult_print(results)","section_id":"using-different-text-scorers"},{"id":"3-multivectorquery-multi-vector-search-ex0","language":"plaintext","code":"combined_score = w_1 * score_1 + w_2 * score_2 + w_3 * score_3 + ...","section_id":"3-multivectorquery-multi-vector-search"},{"id":"basic-multi-vector-query-ex0","language":"python","code":"from redisvl.query import MultiVectorQuery, Vector\n\n# Define multiple vectors for the query\ntext_vector = Vector(\n    vector=[0.1, 0.2, 0.1],\n    field_name=\"text_embedding\",\n    dtype=\"float32\",\n    weight=0.7  # 70% weight for text embedding\n)\n\nimage_vector = Vector(\n    vector=[0.8, 0.1],\n    field_name=\"image_embedding\",\n    dtype=\"float32\",\n    weight=0.3  # 30% weight for image embedding\n)\n\n# Create a multi-vector query\nmulti_vector_query = MultiVectorQuery(\n    vectors=[text_vector, image_vector],\n    return_fields=[\"product_id\", \"brief_description\", \"category\"],\n    num_results=5\n)\n\nresults = index.query(multi_vector_query)\nresult_print(results)","section_id":"basic-multi-vector-query"},{"id":"adjusting-vector-weights-ex0","language":"python","code":"# More emphasis on image similarity\ntext_vec = Vector(\n    vector=[0.9, 0.1, 0.05],\n    field_name=\"text_embedding\",\n    dtype=\"float32\",\n    weight=0.2  # 20% weight\n)\n\nimage_vec = Vector(\n    vector=[0.1, 0.9],\n    field_name=\"image_embedding\",\n    dtype=\"float32\",\n    weight=0.8  # 80% weight\n)\n\nimage_heavy_query = MultiVectorQuery(\n    vectors=[text_vec, image_vec],\n    return_fields=[\"product_id\", \"brief_description\", \"category\"],\n    num_results=3\n)\n\nprint(\"Results with emphasis on image similarity:\")\nresults = index.query(image_heavy_query)\nresult_print(results)","section_id":"adjusting-vector-weights"},{"id":"multi-vector-query-with-filters-ex0","language":"python","code":"# Multi-vector search with category filter\ntext_vec = Vector(\n    vector=[0.1, 0.2, 0.1],\n    field_name=\"text_embedding\",\n    dtype=\"float32\",\n    weight=0.6\n)\n\nimage_vec = Vector(\n    vector=[0.8, 0.1],\n    field_name=\"image_embedding\",\n    dtype=\"float32\",\n    weight=0.4\n)\n\nfiltered_multi_query = MultiVectorQuery(\n    vectors=[text_vec, image_vec],\n    filter_expression=Tag(\"category\") == \"footwear\",\n    return_fields=[\"product_id\", \"brief_description\", \"category\", \"price\"],\n    num_results=5\n)\n\nresults = index.query(filtered_multi_query)\nresult_print(results)","section_id":"multi-vector-query-with-filters"},{"id":"comparing-query-types-ex0","language":"python","code":"# TextQuery - keyword-based search\ntext_q = TextQuery(\n    text=\"shoes\",\n    text_field_name=\"brief_description\",\n    return_fields=[\"product_id\", \"brief_description\"],\n    num_results=3\n)\n\nprint(\"TextQuery Results (keyword-based):\")\nresult_print(index.query(text_q))\nprint()","section_id":"comparing-query-types"},{"id":"comparing-query-types-ex1","language":"python","code":"if HYBRID_SEARCH_AVAILABLE:\n    # HybridQuery - combines text and vector search\n    hybrid_q = HybridQuery(\n        text=\"shoes\",\n        text_field_name=\"brief_description\",\n        vector=[0.1, 0.2, 0.1],\n        vector_field_name=\"text_embedding\",\n        return_fields=[\"product_id\", \"brief_description\"],\n        num_results=3,\n        combination_method=\"LINEAR\",\n        yield_text_score_as=\"text_score\",\n        yield_vsim_score_as=\"vector_similarity\",\n        yield_combined_score_as=\"hybrid_score\",\n    )\n\n    results = index.query(hybrid_q)\n\nelse:\n    hybrid_q = AggregateHybridQuery(\n        text=\"shoes\",\n        text_field_name=\"brief_description\",\n        vector=[0.1, 0.2, 0.1],\n        vector_field_name=\"text_embedding\",\n        return_fields=[\"product_id\", \"brief_description\"],\n        num_results=3,\n    )\n\n    results = index.query(hybrid_q)\n\n\nprint(f\"{hybrid_q.__class__.__name__} Results (text + vector):\")\nresult_print(results)\nprint()","section_id":"comparing-query-types"},{"id":"comparing-query-types-ex2","language":"python","code":"# MultiVectorQuery - searches multiple vector fields\nmv_text = Vector(\n    vector=[0.1, 0.2, 0.1],\n    field_name=\"text_embedding\",\n    dtype=\"float32\",\n    weight=0.5\n)\n\nmv_image = Vector(\n    vector=[0.8, 0.1],\n    field_name=\"image_embedding\",\n    dtype=\"float32\",\n    weight=0.5\n)\n\nmulti_q = MultiVectorQuery(\n    vectors=[mv_text, mv_image],\n    return_fields=[\"product_id\", \"brief_description\"],\n    num_results=3\n)\n\nprint(\"MultiVectorQuery Results (multiple vectors):\")\nresult_print(index.query(multi_q))","section_id":"comparing-query-types"},{"id":"cleanup-ex0","language":"python","code":"# Cleanup\nindex.delete()","section_id":"cleanup"}]}
{"schema_version":2,"id":"develop/ai/redisvl/user_guide/how_to_guides/complex_filtering","title":"Query and Filter Data","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/how_to_guides/complex_filtering/","summary":"","aliases":["/integrate/redisvl/user_guide/how_to_guides/02_complex_filtering","/develop/ai/redisvl/user_guide/complex_filtering/"],"tags":[],"last_updated":"2026-10-01T14:49:09Z","page_type":"content","content_hash":"a504d76869e54172bf0f5bd6f508cb17bd85ed97d02a4c97222a3035ecc40dfe","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This guide covers the filtering capabilities in RedisVL, including tag, numeric, text, geo, and timestamp filters. You'll learn how to combine filters to create complex queries that narrow down search results precisely."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before you begin, ensure you have:\n- Installed RedisVL: `pip install redisvl`\n- A running Redis instance ([Redis 8+](https://redis.io/downloads/) or [Redis Cloud](https://redis.io/cloud))"},{"id":"what-youll-learn","title":"What You'll Learn","role":"content","text":"By the end of this guide, you will be able to:\n- Create and apply tag, numeric, text, and geo filters\n- Combine multiple filters using AND/OR logic\n- Use FilterQuery for non-vector searches\n- Execute CountQuery to get matching record counts\n- Apply RangeQuery for distance-based vector searches\n\n\n[code example]\n\n\n<table><tr><th>user</th><th>age</th><th>job</th><th>credit_score</th><th>office_location</th><th>user_embedding</th><th>last_updated</th></tr><tr><td>john</td><td>18</td><td>engineer</td><td>high</td><td>-122.4194,37.7749</td><td>b'\\xcd\\xcc\\xcc=\\xcd\\xcc\\xcc=\\x00\\x00\\x00?'</td><td>1741627789</td></tr><tr><td>derrick</td><td>14</td><td>doctor</td><td>low</td><td>-122.4194,37.7749</td><td>b'\\xcd\\xcc\\xcc=\\xcd\\xcc\\xcc=\\x00\\x00\\x00?'</td><td>1741627789</td></tr><tr><td>nancy</td><td>94</td><td>doctor</td><td>high</td><td>-122.4194,37.7749</td><td>b'333?\\xcd\\xcc\\xcc=\\x00\\x00\\x00?'</td><td>1710696589</td></tr><tr><td>tyler</td><td>100</td><td>engineer</td><td>high</td><td>-122.0839,37.3861</td><td>b'\\xcd\\xcc\\xcc=\\xcd\\xcc\\xcc>\\x00\\x00\\x00?'</td><td>1742232589</td></tr><tr><td>tim</td><td>12</td><td>dermatologist</td><td>high</td><td>-122.0839,37.3861</td><td>b'\\xcd\\xcc\\xcc>\\xcd\\xcc\\xcc>\\x00\\x00\\x00?'</td><td>1739644189</td></tr><tr><td>taimur</td><td>15</td><td>CEO</td><td>low</td><td>-122.0839,37.3861</td><td>b'\\x9a\\x99\\x19?\\xcd\\xcc\\xcc=\\x00\\x00\\x00?'</td><td>1742232589</td></tr><tr><td>joe</td><td>35</td><td>dentist</td><td>medium</td><td>-122.0839,37.3861</td><td>b'fff?fff?\\xcd\\xcc\\xcc='</td><td>1742232589</td></tr></table>\n\n\n\n[code example]\n\n\n[code example]\n\n\n[code example]\n\n\n[code example]\n\n\n\n\n    7"},{"id":"complex-filtering","title":"Complex Filtering","role":"content","text":"Complex filtering allows you to combine multiple types of filters in your queries. For example, you may want to search for a user that is a certain age, has a certain job, and is within a certain distance of a location. This is a complex filtering query that combines numeric, tag, and geographic filters."},{"id":"tag-filters","title":"Tag Filters","role":"content","text":"Tag filters are filters that are applied to tag fields. These are fields that are not tokenized and are used to store a single categorical value.\n\n\n[code example]\n\n\n<table><tr><th>vector_distance</th><th>user</th><th>credit_score</th><th>age</th><th>job</th><th>office_location</th><th>last_updated</th></tr><tr><td>0</td><td>john</td><td>high</td><td>18</td><td>engineer</td><td>-122.4194,37.7749</td><td>1741627789</td></tr><tr><td>0.109129190445</td><td>tyler</td><td>high</td><td>100</td><td>engineer</td><td>-122.0839,37.3861</td><td>1742232589</td></tr><tr><td>0.158808887005</td><td>tim</td><td>high</td><td>12</td><td>dermatologist</td><td>-122.0839,37.3861</td><td>1739644189</td></tr><tr><td>0.266666650772</td><td>nancy</td><td>high</td><td>94</td><td>doctor</td><td>-122.4194,37.7749</td><td>1710696589</td></tr></table>\n\n\n\n\n\n    '(@credit_score:{high})=>[KNN 10 @user_embedding $vector AS vector_distance] RETURN 7 user credit_score age job office_location last_updated vector_distance SORTBY vector_distance ASC DIALECT 2 LIMIT 0 10'\n\n\n\n\n[code example]\n\n\n\n\n    '(@credit_score:{high})=>[KNN 10 @user_embedding $vector AS vector_distance]'\n\n\n\n\n[code example]\n\n\n<table><tr><th>vector_distance</th><th>user</th><th>credit_score</th><th>age</th><th>job</th><th>office_location</th><th>last_updated</th></tr><tr><td>0</td><td>derrick</td><td>low</td><td>14</td><td>doctor</td><td>-122.4194,37.7749</td><td>1741627789</td></tr><tr><td>0.217881977558</td><td>taimur</td><td>low</td><td>15</td><td>CEO</td><td>-122.0839,37.3861</td><td>1742232589</td></tr><tr><td>0.653301358223</td><td>joe</td><td>medium</td><td>35</td><td>dentist</td><td>-122.0839,37.3861</td><td>1742232589</td></tr></table>\n\n\n**Performance Tip:** For HNSW and SVS-VAMANA indexes, you can add runtime parameters to tune search performance:\n\n[code example]\n\nThese parameters can be adjusted at query time without rebuilding the index. See the [Advanced Queries guide](https://redis.io/docs/latest/advanced_queries) for more details.\n\n\n[code example]\n\n\n<table><tr><th>vector_distance</th><th>user</th><th>credit_score</th><th>age</th><th>job</th><th>office_location</th><th>last_updated</th></tr><tr><td>0</td><td>john</td><td>high</td><td>18</td><td>engineer</td><td>-122.4194,37.7749</td><td>1741627789</td></tr><tr><td>0.109129190445</td><td>tyler</td><td>high</td><td>100</td><td>engineer</td><td>-122.0839,37.3861</td><td>1742232589</td></tr><tr><td>0.158808887005</td><td>tim</td><td>high</td><td>12</td><td>dermatologist</td><td>-122.0839,37.3861</td><td>1739644189</td></tr><tr><td>0.266666650772</td><td>nancy</td><td>high</td><td>94</td><td>doctor</td><td>-122.4194,37.7749</td><td>1710696589</td></tr><tr><td>0.653301358223</td><td>joe</td><td>medium</td><td>35</td><td>dentist</td><td>-122.0839,37.3861</td><td>1742232589</td></tr></table>\n\n\n\n[code example]\n\n\n<table><tr><th>vector_distance</th><th>user</th><th>credit_score</th><th>age</th><th>job</th><th>office_location</th><th>last_updated</th></tr><tr><td>0</td><td>john</td><td>high</td><td>18</td><td>engineer</td><td>-122.4194,37.7749</td><td>1741627789</td></tr><tr><td>0.109129190445</td><td>tyler</td><td>high</td><td>100</td><td>engineer</td><td>-122.0839,37.3861</td><td>1742232589</td></tr><tr><td>0.158808887005</td><td>tim</td><td>high</td><td>12</td><td>dermatologist</td><td>-122.0839,37.3861</td><td>1739644189</td></tr><tr><td>0.266666650772</td><td>nancy</td><td>high</td><td>94</td><td>doctor</td><td>-122.4194,37.7749</td><td>1710696589</td></tr><tr><td>0.653301358223</td><td>joe</td><td>medium</td><td>35</td><td>dentist</td><td>-122.0839,37.3861</td><td>1742232589</td></tr></table>\n\n\nWhat about scenarios where you might want to dynamically generate a list of tags? Have no fear. RedisVL allows you to do this gracefully without having to check for the **empty case**. The **empty case** is when you attempt to run a Tag filter on a field with no defined values to match:\n\n`Tag(\"credit_score\") == []`\n\nAn empty filter like the one above will yield a `*` Redis query filter which implies the base case -- there is no filter here to use.\n\n\n[code example]\n\n\n<table><tr><th>vector_distance</th><th>user</th><th>credit_score</th><th>age</th><th>job</th><th>office_location</th><th>last_updated</th></tr><tr><td>0</td><td>john</td><td>high</td><td>18</td><td>engineer</td><td>-122.4194,37.7749</td><td>1741627789</td></tr><tr><td>0</td><td>derrick</td><td>low</td><td>14</td><td>doctor</td><td>-122.4194,37.7749</td><td>1741627789</td></tr><tr><td>0.109129190445</td><td>tyler</td><td>high</td><td>100</td><td>engineer</td><td>-122.0839,37.3861</td><td>1742232589</td></tr><tr><td>0.158808887005</td><td>tim</td><td>high</td><td>12</td><td>dermatologist</td><td>-122.0839,37.3861</td><td>1739644189</td></tr><tr><td>0.217881977558</td><td>taimur</td><td>low</td><td>15</td><td>CEO</td><td>-122.0839,37.3861</td><td>1742232589</td></tr><tr><td>0.266666650772</td><td>nancy</td><td>high</td><td>94</td><td>doctor</td><td>-122.4194,37.7749</td><td>1710696589</td></tr><tr><td>0.653301358223</td><td>joe</td><td>medium</td><td>35</td><td>dentist</td><td>-122.0839,37.3861</td><td>1742232589</td></tr></table>"},{"id":"numeric-filters","title":"Numeric Filters","role":"content","text":"Numeric filters are filters that are applied to numeric fields and can be used to isolate a range of values for a given field.\n\n\n[code example]\n\n\n<table><tr><th>vector_distance</th><th>user</th><th>credit_score</th><th>age</th><th>job</th><th>office_location</th><th>last_updated</th></tr><tr><td>0</td><td>john</td><td>high</td><td>18</td><td>engineer</td><td>-122.4194,37.7749</td><td>1741627789</td></tr><tr><td>0.217881977558</td><td>taimur</td><td>low</td><td>15</td><td>CEO</td><td>-122.0839,37.3861</td><td>1742232589</td></tr><tr><td>0.653301358223</td><td>joe</td><td>medium</td><td>35</td><td>dentist</td><td>-122.0839,37.3861</td><td>1742232589</td></tr></table>\n\n\n\n[code example]\n\n\n<table><tr><th>vector_distance</th><th>user</th><th>credit_score</th><th>age</th><th>job</th><th>office_location</th><th>last_updated</th></tr><tr><td>0</td><td>derrick</td><td>low</td><td>14</td><td>doctor</td><td>-122.4194,37.7749</td><td>1741627789</td></tr></table>\n\n\n\n[code example]\n\n\n<table><tr><th>vector_distance</th><th>user</th><th>credit_score</th><th>age</th><th>job</th><th>office_location</th><th>last_updated</th></tr><tr><td>0</td><td>john</td><td>high</td><td>18</td><td>engineer</td><td>-122.4194,37.7749</td><td>1741627789</td></tr><tr><td>0.109129190445</td><td>tyler</td><td>high</td><td>100</td><td>engineer</td><td>-122.0839,37.3861</td><td>1742232589</td></tr><tr><td>0.158808887005</td><td>tim</td><td>high</td><td>12</td><td>dermatologist</td><td>-122.0839,37.3861</td><td>1739644189</td></tr><tr><td>0.217881977558</td><td>taimur</td><td>low</td><td>15</td><td>CEO</td><td>-122.0839,37.3861</td><td>1742232589</td></tr><tr><td>0.266666650772</td><td>nancy</td><td>high</td><td>94</td><td>doctor</td><td>-122.4194,37.7749</td><td>1710696589</td></tr><tr><td>0.653301358223</td><td>joe</td><td>medium</td><td>35</td><td>dentist</td><td>-122.0839,37.3861</td><td>1742232589</td></tr></table>"},{"id":"timestamp-filters","title":"Timestamp Filters","role":"content","text":"In redis all times are stored as an epoch time numeric however, this class allows you to filter with python datetime for ease of use. \n\n\n[code example]\n\n    Epoch comparison: 1742129139.132589\n\n\n\n<table><tr><th>vector_distance</th><th>user</th><th>credit_score</th><th>age</th><th>job</th><th>office_location</th><th>last_updated</th></tr><tr><td>0.109129190445</td><td>tyler</td><td>high</td><td>100</td><td>engineer</td><td>-122.0839,37.3861</td><td>1742232589</td></tr><tr><td>0.217881977558</td><td>taimur</td><td>low</td><td>15</td><td>CEO</td><td>-122.0839,37.3861</td><td>1742232589</td></tr><tr><td>0.653301358223</td><td>joe</td><td>medium</td><td>35</td><td>dentist</td><td>-122.0839,37.3861</td><td>1742232589</td></tr></table>\n\n\n\n[code example]\n\n    Epoch comparison: 1742129139.132589\n\n\n\n<table><tr><th>vector_distance</th><th>user</th><th>credit_score</th><th>age</th><th>job</th><th>office_location</th><th>last_updated</th></tr><tr><td>0</td><td>john</td><td>high</td><td>18</td><td>engineer</td><td>-122.4194,37.7749</td><td>1741627789</td></tr><tr><td>0</td><td>derrick</td><td>low</td><td>14</td><td>doctor</td><td>-122.4194,37.7749</td><td>1741627789</td></tr><tr><td>0.158808887005</td><td>tim</td><td>high</td><td>12</td><td>dermatologist</td><td>-122.0839,37.3861</td><td>1739644189</td></tr><tr><td>0.266666650772</td><td>nancy</td><td>high</td><td>94</td><td>doctor</td><td>-122.4194,37.7749</td><td>1710696589</td></tr></table>\n\n\n\n[code example]\n\n    Epoch between: 1736858739.132589 - 1742129139.132589\n\n\n\n<table><tr><th>vector_distance</th><th>user</th><th>credit_score</th><th>age</th><th>job</th><th>office_location</th><th>last_updated</th></tr><tr><td>0</td><td>john</td><td>high</td><td>18</td><td>engineer</td><td>-122.4194,37.7749</td><td>1741627789</td></tr><tr><td>0</td><td>derrick</td><td>low</td><td>14</td><td>doctor</td><td>-122.4194,37.7749</td><td>1741627789</td></tr><tr><td>0.158808887005</td><td>tim</td><td>high</td><td>12</td><td>dermatologist</td><td>-122.0839,37.3861</td><td>1739644189</td></tr></table>"},{"id":"text-filters","title":"Text Filters","role":"content","text":"Text filters are filters that are applied to text fields. These filters are applied to the entire text field. For example, if you have a text field that contains the text \"The quick brown fox jumps over the lazy dog\", a text filter of \"quick\" will match this text field.\n\nA value passed to `==` or `!=` is matched as a literal phrase, so a `\"` or a\n`\\` inside it is insignificant rather than interpreted. The `%` operator is the\nopposite: its value is a raw pattern, so pass it only patterns your own code\ncomposes.\n\n\n[code example]\n\n\n<table><tr><th>vector_distance</th><th>user</th><th>credit_score</th><th>age</th><th>job</th><th>office_location</th><th>last_updated</th></tr><tr><td>0</td><td>derrick</td><td>low</td><td>14</td><td>doctor</td><td>-122.4194,37.7749</td><td>1741627789</td></tr><tr><td>0.266666650772</td><td>nancy</td><td>high</td><td>94</td><td>doctor</td><td>-122.4194,37.7749</td><td>1710696589</td></tr></table>\n\n\n\n[code example]\n\n\n<table><tr><th>vector_distance</th><th>user</th><th>credit_score</th><th>age</th><th>job</th><th>office_location</th><th>last_updated</th></tr><tr><td>0</td><td>john</td><td>high</td><td>18</td><td>engineer</td><td>-122.4194,37.7749</td><td>1741627789</td></tr><tr><td>0.109129190445</td><td>tyler</td><td>high</td><td>100</td><td>engineer</td><td>-122.0839,37.3861</td><td>1742232589</td></tr><tr><td>0.158808887005</td><td>tim</td><td>high</td><td>12</td><td>dermatologist</td><td>-122.0839,37.3861</td><td>1739644189</td></tr><tr><td>0.217881977558</td><td>taimur</td><td>low</td><td>15</td><td>CEO</td><td>-122.0839,37.3861</td><td>1742232589</td></tr><tr><td>0.653301358223</td><td>joe</td><td>medium</td><td>35</td><td>dentist</td><td>-122.0839,37.3861</td><td>1742232589</td></tr></table>\n\n\n\n[code example]\n\n\n<table><tr><th>vector_distance</th><th>user</th><th>credit_score</th><th>age</th><th>job</th><th>office_location</th><th>last_updated</th></tr><tr><td>0</td><td>derrick</td><td>low</td><td>14</td><td>doctor</td><td>-122.4194,37.7749</td><td>1741627789</td></tr><tr><td>0.266666650772</td><td>nancy</td><td>high</td><td>94</td><td>doctor</td><td>-122.4194,37.7749</td><td>1710696589</td></tr></table>\n\n\n\n[code example]\n\n\n<table><tr><th>vector_distance</th><th>user</th><th>credit_score</th><th>age</th><th>job</th><th>office_location</th><th>last_updated</th></tr><tr><td>0</td><td>john</td><td>high</td><td>18</td><td>engineer</td><td>-122.4194,37.7749</td><td>1741627789</td></tr><tr><td>0.109129190445</td><td>tyler</td><td>high</td><td>100</td><td>engineer</td><td>-122.0839,37.3861</td><td>1742232589</td></tr></table>\n\n\n\n[code example]\n\n\n<table><tr><th>vector_distance</th><th>user</th><th>credit_score</th><th>age</th><th>job</th><th>office_location</th><th>last_updated</th></tr><tr><td>0</td><td>john</td><td>high</td><td>18</td><td>engineer</td><td>-122.4194,37.7749</td><td>1741627789</td></tr><tr><td>0</td><td>derrick</td><td>low</td><td>14</td><td>doctor</td><td>-122.4194,37.7749</td><td>1741627789</td></tr><tr><td>0.109129190445</td><td>tyler</td><td>high</td><td>100</td><td>engineer</td><td>-122.0839,37.3861</td><td>1742232589</td></tr><tr><td>0.266666650772</td><td>nancy</td><td>high</td><td>94</td><td>doctor</td><td>-122.4194,37.7749</td><td>1710696589</td></tr></table>\n\n\n\n[code example]\n\n\n<table><tr><th>vector_distance</th><th>user</th><th>credit_score</th><th>age</th><th>job</th><th>office_location</th><th>last_updated</th></tr><tr><td>0</td><td>john</td><td>high</td><td>18</td><td>engineer</td><td>-122.4194,37.7749</td><td>1741627789</td></tr><tr><td>0</td><td>derrick</td><td>low</td><td>14</td><td>doctor</td><td>-122.4194,37.7749</td><td>1741627789</td></tr><tr><td>0.109129190445</td><td>tyler</td><td>high</td><td>100</td><td>engineer</td><td>-122.0839,37.3861</td><td>1742232589</td></tr><tr><td>0.158808887005</td><td>tim</td><td>high</td><td>12</td><td>dermatologist</td><td>-122.0839,37.3861</td><td>1739644189</td></tr><tr><td>0.217881977558</td><td>taimur</td><td>low</td><td>15</td><td>CEO</td><td>-122.0839,37.3861</td><td>1742232589</td></tr><tr><td>0.266666650772</td><td>nancy</td><td>high</td><td>94</td><td>doctor</td><td>-122.4194,37.7749</td><td>1710696589</td></tr><tr><td>0.653301358223</td><td>joe</td><td>medium</td><td>35</td><td>dentist</td><td>-122.0839,37.3861</td><td>1742232589</td></tr></table>\n\n\nUse raw query strings as input. Below we use the `~` flag to indicate that the full text query is optional. We also choose the BM25 scorer and return document scores along with the result.\n\n\n[code example]\n\n\n\n\n    [{'id': 'user_queries_docs:01KHKHSW68SH7A1AT1RDG5FC1A',\n      'score': 1.8181817787737895,\n      'vector_distance': '0',\n      'user': 'john',\n      'credit_score': 'high',\n      'age': '18',\n      'job': 'engineer',\n      'office_location': '-122.4194,37.7749',\n      'last_updated': '1741627789'},\n     {'id': 'user_queries_docs:01KHKHSW68SH7A1AT1RDG5FC1B',\n      'score': 0.0,\n      'vector_distance': '0',\n      'user': 'derrick',\n      'credit_score': 'low',\n      'age': '14',\n      'job': 'doctor',\n      'office_location': '-122.4194,37.7749',\n      'last_updated': '1741627789'},\n     {'id': 'user_queries_docs:01KHKHSW68SH7A1AT1RDG5FC1D',\n      'score': 1.8181817787737895,\n      'vector_distance': '0.109129190445',\n      'user': 'tyler',\n      'credit_score': 'high',\n      'age': '100',\n      'job': 'engineer',\n      'office_location': '-122.0839,37.3861',\n      'last_updated': '1742232589'},\n     {'id': 'user_queries_docs:01KHKHSW69W3GWRFZADZM7XMYV',\n      'score': 0.0,\n      'vector_distance': '0.158808887005',\n      'user': 'tim',\n      'credit_score': 'high',\n      'age': '12',\n      'job': 'dermatologist',\n      'office_location': '-122.0839,37.3861',\n      'last_updated': '1739644189'},\n     {'id': 'user_queries_docs:01KHKHSW69W3GWRFZADZM7XMYW',\n      'score': 0.0,\n      'vector_distance': '0.217881977558',\n      'user': 'taimur',\n      'credit_score': 'low',\n      'age': '15',\n      'job': 'CEO',\n      'office_location': '-122.0839,37.3861',\n      'last_updated': '1742232589'},\n     {'id': 'user_queries_docs:01KHKHSW68SH7A1AT1RDG5FC1C',\n      'score': 0.0,\n      'vector_distance': '0.266666650772',\n      'user': 'nancy',\n      'credit_score': 'high',\n      'age': '94',\n      'job': 'doctor',\n      'office_location': '-122.4194,37.7749',\n      'last_updated': '1710696589'},\n     {'id': 'user_queries_docs:01KHKHSW69W3GWRFZADZM7XMYX',\n      'score': 0.0,\n      'vector_distance': '0.653301358223',\n      'user': 'joe',\n      'credit_score': 'medium',\n      'age': '35',\n      'job': 'dentist',\n      'office_location': '-122.0839,37.3861',\n      'last_updated': '1742232589'}]"},{"id":"geographic-filters","title":"Geographic Filters","role":"content","text":"Geographic filters are filters that are applied to geographic fields. These filters are used to find results that are within a certain distance of a given point. The distance is specified in kilometers, miles, meters, or feet. A radius can also be specified to find results within a certain radius of a given point.\n\n\n[code example]\n\n\n<table><tr><th>score</th><th>vector_distance</th><th>user</th><th>credit_score</th><th>age</th><th>job</th><th>office_location</th><th>last_updated</th></tr><tr><td>0.4545454446934474</td><td>0</td><td>john</td><td>high</td><td>18</td><td>engineer</td><td>-122.4194,37.7749</td><td>1741627789</td></tr><tr><td>0.4545454446934474</td><td>0</td><td>derrick</td><td>low</td><td>14</td><td>doctor</td><td>-122.4194,37.7749</td><td>1741627789</td></tr><tr><td>0.4545454446934474</td><td>0.266666650772</td><td>nancy</td><td>high</td><td>94</td><td>doctor</td><td>-122.4194,37.7749</td><td>1710696589</td></tr></table>\n\n\n\n[code example]\n\n\n<table><tr><th>score</th><th>vector_distance</th><th>user</th><th>credit_score</th><th>age</th><th>job</th><th>office_location</th><th>last_updated</th></tr><tr><td>0.4545454446934474</td><td>0</td><td>john</td><td>high</td><td>18</td><td>engineer</td><td>-122.4194,37.7749</td><td>1741627789</td></tr><tr><td>0.4545454446934474</td><td>0</td><td>derrick</td><td>low</td><td>14</td><td>doctor</td><td>-122.4194,37.7749</td><td>1741627789</td></tr><tr><td>0.4545454446934474</td><td>0.109129190445</td><td>tyler</td><td>high</td><td>100</td><td>engineer</td><td>-122.0839,37.3861</td><td>1742232589</td></tr><tr><td>0.4545454446934474</td><td>0.158808887005</td><td>tim</td><td>high</td><td>12</td><td>dermatologist</td><td>-122.0839,37.3861</td><td>1739644189</td></tr><tr><td>0.4545454446934474</td><td>0.217881977558</td><td>taimur</td><td>low</td><td>15</td><td>CEO</td><td>-122.0839,37.3861</td><td>1742232589</td></tr><tr><td>0.4545454446934474</td><td>0.266666650772</td><td>nancy</td><td>high</td><td>94</td><td>doctor</td><td>-122.4194,37.7749</td><td>1710696589</td></tr><tr><td>0.4545454446934474</td><td>0.653301358223</td><td>joe</td><td>medium</td><td>35</td><td>dentist</td><td>-122.0839,37.3861</td><td>1742232589</td></tr></table>\n\n\n\n[code example]\n\n\n<table><tr><th>score</th><th>vector_distance</th><th>user</th><th>credit_score</th><th>age</th><th>job</th><th>office_location</th><th>last_updated</th></tr><tr><td>0.0</td><td>0.109129190445</td><td>tyler</td><td>high</td><td>100</td><td>engineer</td><td>-122.0839,37.3861</td><td>1742232589</td></tr><tr><td>0.0</td><td>0.158808887005</td><td>tim</td><td>high</td><td>12</td><td>dermatologist</td><td>-122.0839,37.3861</td><td>1739644189</td></tr><tr><td>0.0</td><td>0.217881977558</td><td>taimur</td><td>low</td><td>15</td><td>CEO</td><td>-122.0839,37.3861</td><td>1742232589</td></tr><tr><td>0.0</td><td>0.653301358223</td><td>joe</td><td>medium</td><td>35</td><td>dentist</td><td>-122.0839,37.3861</td><td>1742232589</td></tr></table>"},{"id":"combining-filters","title":"Combining Filters","role":"content","text":"This example combines a numeric filter with a tag filter to search for users between ages 20 and 30 who have the job \"engineer\"."},{"id":"intersection-and","title":"Intersection (\"and\")","role":"content","text":"[code example]\n\n\n<table><tr><th>vector_distance</th><th>user</th><th>credit_score</th><th>age</th><th>job</th><th>office_location</th></tr><tr><td>0.109129190445</td><td>tyler</td><td>high</td><td>100</td><td>engineer</td><td>-122.0839,37.3861</td></tr></table>"},{"id":"union-or","title":"Union (\"or\")","role":"content","text":"The union of two queries is the set of all results that are returned by either of the two queries. The union of two queries is performed using the `|` operator.\n\n\n[code example]\n\n\n<table><tr><th>vector_distance</th><th>user</th><th>credit_score</th><th>age</th><th>job</th><th>office_location</th></tr><tr><td>0</td><td>derrick</td><td>low</td><td>14</td><td>doctor</td><td>-122.4194,37.7749</td></tr><tr><td>0.109129190445</td><td>tyler</td><td>high</td><td>100</td><td>engineer</td><td>-122.0839,37.3861</td></tr><tr><td>0.158808887005</td><td>tim</td><td>high</td><td>12</td><td>dermatologist</td><td>-122.0839,37.3861</td></tr><tr><td>0.217881977558</td><td>taimur</td><td>low</td><td>15</td><td>CEO</td><td>-122.0839,37.3861</td></tr><tr><td>0.266666650772</td><td>nancy</td><td>high</td><td>94</td><td>doctor</td><td>-122.4194,37.7749</td></tr></table>"},{"id":"dynamic-combination","title":"Dynamic Combination","role":"content","text":"There are often situations where you may or may not want to use a filter in a\ngiven query. As shown above, filters will except the ``None`` type and revert\nto a wildcard filter essentially returning all results.\n\nThe same goes for filter combinations which enables rapid reuse of filters in\nrequests with different parameters as shown below. This removes the need for\na number of \"if-then\" conditionals to test for the empty case.\n\n\n\n\n[code example]\n\n\n[code example]\n\n\n<table><tr><th>vector_distance</th><th>user</th><th>credit_score</th><th>age</th><th>job</th><th>office_location</th></tr><tr><td>0.109129190445</td><td>tyler</td><td>high</td><td>100</td><td>engineer</td><td>-122.0839,37.3861</td></tr></table>\n\n\n\n[code example]\n\n\n<table><tr><th>vector_distance</th><th>user</th><th>credit_score</th><th>age</th><th>job</th><th>office_location</th></tr><tr><td>0.109129190445</td><td>tyler</td><td>high</td><td>100</td><td>engineer</td><td>-122.0839,37.3861</td></tr><tr><td>0.266666650772</td><td>nancy</td><td>high</td><td>94</td><td>doctor</td><td>-122.4194,37.7749</td></tr></table>\n\n\n\n[code example]\n\n\n<table><tr><th>vector_distance</th><th>user</th><th>credit_score</th><th>age</th><th>job</th><th>office_location</th></tr><tr><td>0.109129190445</td><td>tyler</td><td>high</td><td>100</td><td>engineer</td><td>-122.0839,37.3861</td></tr><tr><td>0.266666650772</td><td>nancy</td><td>high</td><td>94</td><td>doctor</td><td>-122.4194,37.7749</td></tr><tr><td>0.653301358223</td><td>joe</td><td>medium</td><td>35</td><td>dentist</td><td>-122.0839,37.3861</td></tr></table>\n\n\n\n[code example]\n\n\n<table><tr><th>vector_distance</th><th>user</th><th>credit_score</th><th>age</th><th>job</th><th>office_location</th></tr><tr><td>0</td><td>john</td><td>high</td><td>18</td><td>engineer</td><td>-122.4194,37.7749</td></tr><tr><td>0</td><td>derrick</td><td>low</td><td>14</td><td>doctor</td><td>-122.4194,37.7749</td></tr><tr><td>0.109129190445</td><td>tyler</td><td>high</td><td>100</td><td>engineer</td><td>-122.0839,37.3861</td></tr><tr><td>0.158808887005</td><td>tim</td><td>high</td><td>12</td><td>dermatologist</td><td>-122.0839,37.3861</td></tr><tr><td>0.217881977558</td><td>taimur</td><td>low</td><td>15</td><td>CEO</td><td>-122.0839,37.3861</td></tr><tr><td>0.266666650772</td><td>nancy</td><td>high</td><td>94</td><td>doctor</td><td>-122.4194,37.7749</td></tr><tr><td>0.653301358223</td><td>joe</td><td>medium</td><td>35</td><td>dentist</td><td>-122.0839,37.3861</td></tr></table>"},{"id":"non-vector-queries","title":"Non-vector Queries","role":"content","text":"When you need to query without vector similarity (similar to a SQL WHERE clause), use the ``FilterQuery`` class. It accepts a ``FilterExpression`` and returns matching records.\n\n\n[code example]\n\n\n<table><tr><th>user</th><th>credit_score</th><th>age</th><th>job</th></tr><tr><td>derrick</td><td>low</td><td>14</td><td>doctor</td></tr><tr><td>taimur</td><td>low</td><td>15</td><td>CEO</td></tr></table>"},{"id":"count-queries","title":"Count Queries","role":"content","text":"Use ``CountQuery`` with a ``FilterExpression`` to get the count of matching records without retrieving the actual data.\n\n\n[code example]\n\n    2 records match the filter expression @credit_score:{low} for the given index."},{"id":"range-queries","title":"Range Queries","role":"content","text":"Range Queries are a useful method to perform a vector search where only results within a vector ``distance_threshold`` are returned. This enables the user to find all records within their dataset that are similar to a query vector where \"similar\" is defined by a quantitative value.\n\n\n[code example]\n\n\n<table><tr><th>vector_distance</th><th>user</th><th>credit_score</th><th>age</th><th>job</th></tr><tr><td>0</td><td>john</td><td>high</td><td>18</td><td>engineer</td></tr><tr><td>0</td><td>derrick</td><td>low</td><td>14</td><td>doctor</td></tr><tr><td>0.109129190445</td><td>tyler</td><td>high</td><td>100</td><td>engineer</td></tr><tr><td>0.158808887005</td><td>tim</td><td>high</td><td>12</td><td>dermatologist</td></tr></table>\n\n\nThe distance threshold can be changed between queries. Setting ``distance_threshold=0.1`` returns only matches within 0.1 distance of the query vector, resulting in fewer but more similar matches.\n\n\n[code example]\n\n\n<table><tr><th>vector_distance</th><th>user</th><th>credit_score</th><th>age</th><th>job</th></tr><tr><td>0</td><td>john</td><td>high</td><td>18</td><td>engineer</td></tr><tr><td>0</td><td>derrick</td><td>low</td><td>14</td><td>doctor</td></tr></table>\n\n\nRange queries can also be used with filters like any other query type. The following limits the results to only include records with a ``job`` of ``engineer`` while also being within the vector range (aka distance).\n\n\n[code example]\n\n\n<table><tr><th>vector_distance</th><th>user</th><th>credit_score</th><th>age</th><th>job</th></tr><tr><td>0</td><td>john</td><td>high</td><td>18</td><td>engineer</td></tr></table>"},{"id":"advanced-query-modifiers","title":"Advanced Query Modifiers","role":"content","text":"See all modifier options available on the query API docs: https://redis.io/docs/latest/develop/ai/redisvl/api/query\n\n\n[code example]\n\n\n<table><tr><th>vector_distance</th><th>age</th><th>user</th><th>credit_score</th><th>job</th><th>office_location</th></tr><tr><td>0.109129190445</td><td>100</td><td>tyler</td><td>high</td><td>engineer</td><td>-122.0839,37.3861</td></tr><tr><td>0</td><td>18</td><td>john</td><td>high</td><td>engineer</td><td>-122.4194,37.7749</td></tr></table>"},{"id":"raw-redis-query-string","title":"Raw Redis Query String","role":"content","text":"Sometimes it's helpful to convert these classes into their raw Redis query strings.\n\n\n[code example]\n\n\n\n\n    '(@job:(\"engineer\"))=>[KNN 5 @user_embedding $vector AS vector_distance] RETURN 6 user credit_score age job office_location vector_distance SORTBY age DESC DIALECT 3 LIMIT 0 5'\n\n\n\n\n[code example]\n\n\n\n\n    '@credit_score:{high}'\n\n\n\n\n[code example]\n\n\n\n\n    '((@credit_score:{high} @age:[18 +inf]) @age:[-inf 100])'\n\n\n\nThe RedisVL `SearchIndex` class exposes a `search()` method which is a simple wrapper around the `FT.SEARCH` API.\nProvide any valid Redis query string.\n\n\n[code example]\n\n    {'id': 'user_queries_docs:01KHKHSW68SH7A1AT1RDG5FC1A', 'payload': None, 'user': 'john', 'age': '18', 'job': 'engineer', 'credit_score': 'high', 'office_location': '-122.4194,37.7749', 'user_embedding': '==\\x00\\x00\\x00?', 'last_updated': '1741627789'}\n    {'id': 'user_queries_docs:01KHKHSW68SH7A1AT1RDG5FC1C', 'payload': None, 'user': 'nancy', 'age': '94', 'job': 'doctor', 'credit_score': 'high', 'office_location': '-122.4194,37.7749', 'user_embedding': '333?=\\x00\\x00\\x00?', 'last_updated': '1710696589'}\n    {'id': 'user_queries_docs:01KHKHSW68SH7A1AT1RDG5FC1D', 'payload': None, 'user': 'tyler', 'age': '100', 'job': 'engineer', 'credit_score': 'high', 'office_location': '-122.0839,37.3861', 'user_embedding': '=>\\x00\\x00\\x00?', 'last_updated': '1742232589'}\n    {'id': 'user_queries_docs:01KHKHSW69W3GWRFZADZM7XMYV', 'payload': None, 'user': 'tim', 'age': '12', 'job': 'dermatologist', 'credit_score': 'high', 'office_location': '-122.0839,37.3861', 'user_embedding': '>>\\x00\\x00\\x00?', 'last_updated': '1739644189'}"},{"id":"next-steps","title":"Next Steps","role":"content","text":"Now that you understand filtering in RedisVL, explore these related guides:\n\n- [Use Advanced Query Types](https://redis.io/docs/latest/advanced_queries) - Learn about TextQuery, HybridQuery, and MultiVectorQuery\n- [Cache LLM Responses](https://redis.io/docs/latest/llmcache) - Use filters with semantic caching for multi-user scenarios\n- [Write SQL Queries for Redis](https://redis.io/docs/latest/sql_to_redis_queries) - Use SQL-like syntax for Redis queries"},{"id":"cleanup","title":"Cleanup","role":"content","text":"[code example]"}],"examples":[{"id":"what-youll-learn-ex0","language":"python","code":"import pickle\nfrom jupyterutils import table_print, result_print\n\n# load in the example data and printing utils\ndata = pickle.load(open(\"hybrid_example_data.pkl\", \"rb\"))\ntable_print(data)","section_id":"what-youll-learn"},{"id":"what-youll-learn-ex1","language":"python","code":"schema = {\n    \"index\": {\n        \"name\": \"user_queries\",\n        \"prefix\": \"user_queries_docs\",\n        \"storage_type\": \"hash\", # default setting -- HASH\n    },\n    \"fields\": [\n        {\"name\": \"user\", \"type\": \"tag\"},\n        {\"name\": \"credit_score\", \"type\": \"tag\"},\n        {\"name\": \"job\", \"type\": \"text\"},\n        {\"name\": \"age\", \"type\": \"numeric\"},\n        {\"name\": \"last_updated\", \"type\": \"numeric\"},\n        {\"name\": \"office_location\", \"type\": \"geo\"},\n        {\n            \"name\": \"user_embedding\",\n            \"type\": \"vector\",\n            \"attrs\": {\n                \"dims\": 3,\n                \"distance_metric\": \"cosine\",\n                \"algorithm\": \"flat\",\n                \"datatype\": \"float32\"\n            }\n\n        }\n    ],\n}","section_id":"what-youll-learn"},{"id":"what-youll-learn-ex2","language":"python","code":"from redisvl.index import SearchIndex\n\n# construct a search index from the schema\nindex = SearchIndex.from_dict(schema, redis_url=\"redis://localhost:6379\")\n\n# create the index (no data yet)\nindex.create(overwrite=True)","section_id":"what-youll-learn"},{"id":"what-youll-learn-ex3","language":"python","code":"# load data to redis\nkeys = index.load(data)","section_id":"what-youll-learn"},{"id":"what-youll-learn-ex4","language":"python","code":"index.info()['num_docs']","section_id":"what-youll-learn"},{"id":"tag-filters-ex0","language":"python","code":"from redisvl.query import VectorQuery\nfrom redisvl.query.filter import Tag\n\nt = Tag(\"credit_score\") == \"high\"\n\nv = VectorQuery(\n    vector=[0.1, 0.1, 0.5],\n    vector_field_name=\"user_embedding\",\n    return_fields=[\"user\", \"credit_score\", \"age\", \"job\", \"office_location\", \"last_updated\"],\n    filter_expression=t\n)\n\nresults = index.query(v)\nresult_print(results)\nstr(v)","section_id":"tag-filters"},{"id":"tag-filters-ex1","language":"python","code":"v.query_string()","section_id":"tag-filters"},{"id":"tag-filters-ex2","language":"python","code":"# negation\nt = Tag(\"credit_score\") != \"high\"\n\nv.set_filter(t)\nresult_print(index.query(v))","section_id":"tag-filters"},{"id":"tag-filters-ex3","language":"python","code":"# Example with runtime parameters for better recall\nv = VectorQuery(\n    vector=[0.1, 0.1, 0.5],\n    vector_field_name=\"user_embedding\",\n    return_fields=[\"user\", \"credit_score\", \"age\"],\n    filter_expression=t,\n    ef_runtime=100,  # HNSW: higher for better recall\n    search_window_size=40  # SVS-VAMANA: larger window for better recall\n)","section_id":"tag-filters"},{"id":"tag-filters-ex4","language":"python","code":"# use multiple tags as a list\nt = Tag(\"credit_score\") == [\"high\", \"medium\"]\n\nv.set_filter(t)\nresult_print(index.query(v))","section_id":"tag-filters"},{"id":"tag-filters-ex5","language":"python","code":"# use multiple tags as a set (to enforce uniqueness)\nt = Tag(\"credit_score\") == set([\"high\", \"high\", \"medium\"])\n\nv.set_filter(t)\nresult_print(index.query(v))","section_id":"tag-filters"},{"id":"tag-filters-ex6","language":"python","code":"# gracefully fallback to \"*\" filter if empty case\nempty_case = Tag(\"credit_score\") == []\n\nv.set_filter(empty_case)\nresult_print(index.query(v))","section_id":"tag-filters"},{"id":"numeric-filters-ex0","language":"python","code":"from redisvl.query.filter import Num\n\nnumeric_filter = Num(\"age\").between(15, 35)\n\nv.set_filter(numeric_filter)\nresult_print(index.query(v))","section_id":"numeric-filters"},{"id":"numeric-filters-ex1","language":"python","code":"# exact match query\nnumeric_filter = Num(\"age\") == 14\n\nv.set_filter(numeric_filter)\nresult_print(index.query(v))","section_id":"numeric-filters"},{"id":"numeric-filters-ex2","language":"python","code":"# negation\nnumeric_filter = Num(\"age\") != 14\n\nv.set_filter(numeric_filter)\nresult_print(index.query(v))","section_id":"numeric-filters"},{"id":"timestamp-filters-ex0","language":"python","code":"from redisvl.query.filter import Timestamp\nfrom datetime import datetime\n\ndt = datetime(2025, 3, 16, 13, 45, 39, 132589)\nprint(f'Epoch comparison: {dt.timestamp()}')\n\ntimestamp_filter = Timestamp(\"last_updated\") > dt\n\nv.set_filter(timestamp_filter)\nresult_print(index.query(v))","section_id":"timestamp-filters"},{"id":"timestamp-filters-ex1","language":"python","code":"from redisvl.query.filter import Timestamp\nfrom datetime import datetime\n\ndt = datetime(2025, 3, 16, 13, 45, 39, 132589)\n\nprint(f'Epoch comparison: {dt.timestamp()}')\n\ntimestamp_filter = Timestamp(\"last_updated\") < dt\n\nv.set_filter(timestamp_filter)\nresult_print(index.query(v))","section_id":"timestamp-filters"},{"id":"timestamp-filters-ex2","language":"python","code":"from redisvl.query.filter import Timestamp\nfrom datetime import datetime\n\ndt_1 = datetime(2025, 1, 14, 13, 45, 39, 132589)\ndt_2 = datetime(2025, 3, 16, 13, 45, 39, 132589)\n\nprint(f'Epoch between: {dt_1.timestamp()} - {dt_2.timestamp()}')\n\ntimestamp_filter = Timestamp(\"last_updated\").between(dt_1, dt_2)\n\nv.set_filter(timestamp_filter)\nresult_print(index.query(v))","section_id":"timestamp-filters"},{"id":"text-filters-ex0","language":"python","code":"from redisvl.query.filter import Text\n\n# exact match filter -- document must contain the exact word doctor\ntext_filter = Text(\"job\") == \"doctor\"\n\nv.set_filter(text_filter)\nresult_print(index.query(v))","section_id":"text-filters"},{"id":"text-filters-ex1","language":"python","code":"# negation -- document must not contain the exact word doctor\nnegate_text_filter = Text(\"job\") != \"doctor\"\n\nv.set_filter(negate_text_filter)\nresult_print(index.query(v))","section_id":"text-filters"},{"id":"text-filters-ex2","language":"python","code":"# wildcard match filter\nwildcard_filter = Text(\"job\") % \"doct*\"\n\nv.set_filter(wildcard_filter)\nresult_print(index.query(v))","section_id":"text-filters"},{"id":"text-filters-ex3","language":"python","code":"# fuzzy match filter\nfuzzy_match = Text(\"job\") % \"%%engine%%\"\n\nv.set_filter(fuzzy_match)\nresult_print(index.query(v))","section_id":"text-filters"},{"id":"text-filters-ex4","language":"python","code":"# conditional -- match documents with job field containing engineer OR doctor\nconditional = Text(\"job\") % \"engineer|doctor\"\n\nv.set_filter(conditional)\nresult_print(index.query(v))","section_id":"text-filters"},{"id":"text-filters-ex5","language":"python","code":"# gracefully fallback to \"*\" filter if empty case\nempty_case = Text(\"job\") % \"\"\n\nv.set_filter(empty_case)\nresult_print(index.query(v))","section_id":"text-filters"},{"id":"text-filters-ex6","language":"python","code":"v.set_filter(\"(~(@job:engineer))\")\nv.scorer(\"BM25\").with_scores()\n\nindex.query(v)","section_id":"text-filters"},{"id":"geographic-filters-ex0","language":"python","code":"from redisvl.query.filter import Geo, GeoRadius\n\n# within 10 km of San Francisco office\ngeo_filter = Geo(\"office_location\") == GeoRadius(-122.4194, 37.7749, 10, \"km\")\n\nv.set_filter(geo_filter)\nresult_print(index.query(v))","section_id":"geographic-filters"},{"id":"geographic-filters-ex1","language":"python","code":"# within 100 km Radius of San Francisco office\ngeo_filter = Geo(\"office_location\") == GeoRadius(-122.4194, 37.7749, 100, \"km\")\n\nv.set_filter(geo_filter)\nresult_print(index.query(v))","section_id":"geographic-filters"},{"id":"geographic-filters-ex2","language":"python","code":"# not within 10 km Radius of San Francisco office\ngeo_filter = Geo(\"office_location\") != GeoRadius(-122.4194, 37.7749, 10, \"km\")\n\nv.set_filter(geo_filter)\nresult_print(index.query(v))","section_id":"geographic-filters"},{"id":"intersection-and-ex0","language":"python","code":"t = Tag(\"credit_score\") == \"high\"\nlow = Num(\"age\") >= 18\nhigh = Num(\"age\") <= 100\nts = Timestamp(\"last_updated\") > datetime(2025, 3, 16, 13, 45, 39, 132589)\n\ncombined = t & low & high & ts\n\nv = VectorQuery([0.1, 0.1, 0.5],\n                \"user_embedding\",\n                return_fields=[\"user\", \"credit_score\", \"age\", \"job\",  \"office_location\"],\n                filter_expression=combined)\n\n\nresult_print(index.query(v))","section_id":"intersection-and"},{"id":"union-or-ex0","language":"python","code":"low = Num(\"age\") < 18\nhigh = Num(\"age\") > 93\n\ncombined = low | high\n\nv.set_filter(combined)\nresult_print(index.query(v))","section_id":"union-or"},{"id":"dynamic-combination-ex0","language":"python","code":"def make_filter(age=None, credit=None, job=None):\n    flexible_filter = (\n        (Num(\"age\") > age) &\n        (Tag(\"credit_score\") == credit) &\n        (Text(\"job\") % job)\n    )\n    return flexible_filter","section_id":"dynamic-combination"},{"id":"dynamic-combination-ex1","language":"python","code":"# all parameters\ncombined = make_filter(age=18, credit=\"high\", job=\"engineer\")\nv.set_filter(combined)\nresult_print(index.query(v))","section_id":"dynamic-combination"},{"id":"dynamic-combination-ex2","language":"python","code":"# just age and credit_score\ncombined = make_filter(age=18, credit=\"high\")\nv.set_filter(combined)\nresult_print(index.query(v))","section_id":"dynamic-combination"},{"id":"dynamic-combination-ex3","language":"python","code":"# just age\ncombined = make_filter(age=18)\nv.set_filter(combined)\nresult_print(index.query(v))","section_id":"dynamic-combination"},{"id":"dynamic-combination-ex4","language":"python","code":"# no filters\ncombined = make_filter()\nv.set_filter(combined)\nresult_print(index.query(v))","section_id":"dynamic-combination"},{"id":"non-vector-queries-ex0","language":"python","code":"from redisvl.query import FilterQuery\n\nhas_low_credit = Tag(\"credit_score\") == \"low\"\n\nfilter_query = FilterQuery(\n    return_fields=[\"user\", \"credit_score\", \"age\", \"job\", \"location\"],\n    filter_expression=has_low_credit\n)\n\nresults = index.query(filter_query)\n\nresult_print(results)","section_id":"non-vector-queries"},{"id":"count-queries-ex0","language":"python","code":"from redisvl.query import CountQuery\n\nhas_low_credit = Tag(\"credit_score\") == \"low\"\n\nfilter_query = CountQuery(filter_expression=has_low_credit)\n\ncount = index.query(filter_query)\n\nprint(f\"{count} records match the filter expression {str(has_low_credit)} for the given index.\")","section_id":"count-queries"},{"id":"range-queries-ex0","language":"python","code":"from redisvl.query import RangeQuery\n\nrange_query = RangeQuery(\n    vector=[0.1, 0.1, 0.5],\n    vector_field_name=\"user_embedding\",\n    return_fields=[\"user\", \"credit_score\", \"age\", \"job\", \"location\"],\n    distance_threshold=0.2\n)\n\n# same as the vector query or filter query\nresults = index.query(range_query)\n\nresult_print(results)","section_id":"range-queries"},{"id":"range-queries-ex1","language":"python","code":"range_query.set_distance_threshold(0.1)\n\nresult_print(index.query(range_query))","section_id":"range-queries"},{"id":"range-queries-ex2","language":"python","code":"is_engineer = Text(\"job\") == \"engineer\"\n\nrange_query.set_filter(is_engineer)\n\nresult_print(index.query(range_query))","section_id":"range-queries"},{"id":"advanced-query-modifiers-ex0","language":"python","code":"# Sort by a different field and change dialect\nv = VectorQuery(\n    vector=[0.1, 0.1, 0.5],\n    vector_field_name=\"user_embedding\",\n    return_fields=[\"user\", \"credit_score\", \"age\", \"job\",  \"office_location\"],\n    num_results=5,\n    filter_expression=is_engineer\n).sort_by(\"age\", asc=False).dialect(3)\n\nresult = index.query(v)\nresult_print(result)","section_id":"advanced-query-modifiers"},{"id":"raw-redis-query-string-ex0","language":"python","code":"# check out the complex query from above\nstr(v)","section_id":"raw-redis-query-string"},{"id":"raw-redis-query-string-ex1","language":"python","code":"t = Tag(\"credit_score\") == \"high\"\n\nstr(t)","section_id":"raw-redis-query-string"},{"id":"raw-redis-query-string-ex2","language":"python","code":"t = Tag(\"credit_score\") == \"high\"\nlow = Num(\"age\") >= 18\nhigh = Num(\"age\") <= 100\n\ncombined = t & low & high\n\nstr(combined)","section_id":"raw-redis-query-string"},{"id":"raw-redis-query-string-ex3","language":"python","code":"results = index.search(str(t))\nfor r in results.docs:\n    print(r.__dict__)","section_id":"raw-redis-query-string"},{"id":"cleanup-ex0","language":"python","code":"index.delete()","section_id":"cleanup"}]}
{"schema_version":2,"id":"develop/ai/redisvl/user_guide/how_to_guides/embeddings_cache","title":"Cache Embeddings","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/how_to_guides/embeddings_cache/","summary":"","aliases":["/integrate/redisvl/user_guide/how_to_guides/10_embeddings_cache","/integrate/redisvl/user_guide/embeddings_cache/","/develop/ai/redisvl/user_guide/embeddings_cache/"],"tags":[],"last_updated":"2026-10-01T14:49:09Z","page_type":"content","content_hash":"4972943e0338e6f16175246efeaf4f35d50b13f02cd3c8478bb1a1e2142db983","sections":[{"id":"overview","title":"Overview","role":"overview","text":"RedisVL provides an `EmbeddingsCache` that stores and retrieves embedding vectors with their associated text and metadata. This cache is useful for applications that frequently compute the same embeddings."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before you begin, ensure you have:\n- Installed RedisVL: `pip install redisvl`\n- A running Redis instance ([Redis 8+](https://redis.io/downloads/) or [Redis Cloud](https://redis.io/cloud))"},{"id":"what-youll-learn","title":"What You'll Learn","role":"content","text":"By the end of this guide, you will be able to:\n- Store and retrieve embedding vectors with the `EmbeddingsCache`\n- Use batch operations for efficient processing\n- Configure time-to-live (TTL) for cache entries\n- Integrate the cache with vectorizers for automatic caching"},{"id":"setup","title":"Setup","role":"setup","text":"First, let's import the necessary libraries. We'll use a text embedding model from HuggingFace to generate our embeddings.\n\n\n[code example]\n\nLet's create a vectorizer to generate embeddings for our texts:\n\n\n[code example]"},{"id":"initializing-the-embeddingscache","title":"Initializing the EmbeddingsCache","role":"content","text":"Now let's initialize our `EmbeddingsCache`. The cache requires a Redis connection to store the embeddings and their associated data.\n\n\n[code example]"},{"id":"basic-usage","title":"Basic Usage","role":"content","text":""},{"id":"storing-embeddings","title":"Storing Embeddings","role":"content","text":"Let's store some text with its embedding in the cache. The `set` method takes the following parameters:\n- `content`: The input text that was embedded\n- `model_name`: The name of the embedding model used\n- `embedding`: The embedding vector\n- `metadata`: Optional metadata associated with the embedding\n- `ttl`: Optional time-to-live override for this specific entry\n\n\n[code example]\n\n    Stored with key: embedcache:909f..."},{"id":"retrieving-embeddings","title":"Retrieving Embeddings","role":"content","text":"To retrieve an embedding from the cache, use the `get` method with the original text and model name:\n\n\n[code example]\n\n    Found in cache: What is machine learning?\n    Model: redis/langcache-embed-v1\n    Metadata: {'category': 'ai', 'source': 'user_query'}\n    Embedding shape: (768,)"},{"id":"checking-existence","title":"Checking Existence","role":"content","text":"You can check if an embedding exists in the cache without retrieving it using the `exists` method:\n\n\n[code example]\n\n    First query exists in cache: True\n    New query exists in cache: False"},{"id":"removing-entries","title":"Removing Entries","role":"content","text":"To remove an entry from the cache, use the `drop` method:\n\n\n[code example]\n\n    After dropping: False"},{"id":"advanced-usage","title":"Advanced Usage","role":"content","text":""},{"id":"key-based-operations","title":"Key-Based Operations","role":"content","text":"The `EmbeddingsCache` also provides methods that work directly with Redis keys, which can be useful for advanced use cases:\n\n\n[code example]\n\n    Stored with key: embedcache:909f...\n    Exists by key: True\n    Retrieved by key: What is machine learning?"},{"id":"batch-operations","title":"Batch Operations","role":"content","text":"When working with multiple embeddings, batch operations can significantly improve performance by reducing network roundtrips. The `EmbeddingsCache` provides methods prefixed with `m` (for \"multi\") that handle batches efficiently.\n\n\n[code example]\n\n    Stored 3 embeddings with batch operation\n    All embeddings exist: True\n    Retrieved 3 embeddings in one operation\n\n\nBatch operations are particularly beneficial when working with large numbers of embeddings. They provide the same functionality as individual operations but with better performance by reducing network roundtrips.\n\nFor asynchronous applications, async versions of all batch methods are also available with the `am` prefix (e.g., `amset`, `amget`, `amexists`, `amdrop`)."},{"id":"working-with-ttl-time-to-live","title":"Working with TTL (Time-To-Live)","role":"content","text":"You can set a global TTL when initializing the cache, or specify TTL for individual entries:\n\n\n[code example]\n\n    Immediately after setting: True\n    After waiting: False\n\n\nYou can also override the default TTL for individual entries:\n\n\n[code example]\n\n    Entry with custom TTL after 2 seconds: False\n    Entry with default TTL after 2 seconds: True"},{"id":"async-support","title":"Async Support","role":"content","text":"The `EmbeddingsCache` provides async versions of all methods for use in async applications. The async methods are prefixed with `a` (e.g., `aset`, `aget`, `aexists`, `adrop`).\n\n\n[code example]\n\n    Async set successful? True\n    Async get successful? True"},{"id":"real-world-example","title":"Real-World Example","role":"content","text":"Let's build a simple embeddings caching system for a text classification task. We'll check the cache before computing new embeddings to save computation time.\n\n\n[code example]\n\n    You try to use a model that was created with version 4.1.0, however, your version is 3.4.1. This might cause unexpected behavior or errors. In that case, try to update to the latest version.\n    \n    \n    \n\n\n    \n    Statistics:\n    Total queries: 5\n    Cache hits: 2\n    Cache misses: 3\n    Cache hit rate: 40.0%"},{"id":"performance-benchmark","title":"Performance Benchmark","role":"performance","text":"Let's run benchmarks to compare the performance of embedding with and without caching, as well as batch versus individual operations.\n\n\n[code example]\n\n    Benchmarking without caching:\n    Time taken without caching: 0.4564 seconds\n    Average time per embedding: 0.0456 seconds\n    \n    Benchmarking with caching:\n    Time taken with caching: 0.0619 seconds\n    Average time per embedding: 0.0062 seconds\n    \n    Performance comparison:\n    Speedup with caching: 7.37x faster\n    Time saved: 0.3945 seconds (86.4%)\n    Latency reduction: 0.0395 seconds per query"},{"id":"common-use-cases-for-embedding-caching","title":"Common Use Cases for Embedding Caching","role":"content","text":"Embedding caching is particularly useful in the following scenarios:\n\n1. **Search applications**: Cache embeddings for frequently searched queries to reduce latency\n2. **Content recommendation systems**: Cache embeddings for content items to speed up similarity calculations\n3. **API services**: Reduce costs and improve response times when generating embeddings through paid APIs\n4. **Batch processing**: Speed up processing of datasets that contain duplicate texts\n5. **Chatbots and virtual assistants**: Cache embeddings for common user queries to provide faster responses\n6. **Development** workflows"},{"id":"cleanup","title":"Cleanup","role":"content","text":"Let's clean up our caches to avoid leaving data in Redis:\n\n\n[code example]"},{"id":"next-steps","title":"Next Steps","role":"content","text":"Now that you understand embeddings caching, explore these related guides:\n\n- [Cache LLM Responses](https://redis.io/docs/latest/llmcache) - Cache full LLM responses based on semantic similarity\n- [Create Embeddings with Vectorizers](https://redis.io/docs/latest/vectorizers) - Learn about different embedding providers\n- [Manage LLM Message History](https://redis.io/docs/latest/message_history) - Store and retrieve conversation history"},{"id":"summary","title":"Summary","role":"content","text":"The `EmbeddingsCache` provides an efficient way to store and retrieve embeddings with their associated text and metadata. Key features include:\n\n- Simple API for storing and retrieving individual embeddings (`set`/`get`)\n- Batch operations for working with multiple embeddings efficiently (`mset`/`mget`/`mexists`/`mdrop`)\n- Support for metadata storage alongside embeddings\n- Configurable time-to-live (TTL) for cache entries\n- Key-based operations for advanced use cases\n- Async support for use in asynchronous applications\n- Significant performance improvements (15-20x faster with batch operations)\n\nBy using the `EmbeddingsCache`, you can reduce computational costs and improve the performance of applications that rely on embeddings."}],"examples":[{"id":"setup-ex0","language":"python","code":"import os\nimport time\nimport numpy as np\n\n# Disable tokenizers parallelism to avoid deadlocks\nos.environ[\"TOKENIZERS_PARALLELISM\"] = \"False\"\n\n# Import the EmbeddingsCache\nfrom redisvl.extensions.cache.embeddings import EmbeddingsCache\nfrom redisvl.utils.vectorize import HFTextVectorizer","section_id":"setup"},{"id":"setup-ex1","language":"python","code":"# Initialize the vectorizer\nvectorizer = HFTextVectorizer(\n    model=\"redis/langcache-embed-v1\",\n    cache_folder=os.getenv(\"SENTENCE_TRANSFORMERS_HOME\")\n)","section_id":"setup"},{"id":"initializing-the-embeddingscache-ex0","language":"python","code":"# Initialize the embeddings cache\ncache = EmbeddingsCache(\n    name=\"embedcache\",                  # name prefix for Redis keys\n    redis_url=\"redis://localhost:6379\",  # Redis connection URL\n    ttl=None                            # Optional TTL in seconds (None means no expiration)\n)","section_id":"initializing-the-embeddingscache"},{"id":"storing-embeddings-ex0","language":"python","code":"# Text to embed\ntext = \"What is machine learning?\"\nmodel_name = \"redis/langcache-embed-v1\"\n\n# Generate the embedding\nembedding = vectorizer.embed(text)\n\n# Optional metadata\nmetadata = {\"category\": \"ai\", \"source\": \"user_query\"}\n\n# Store in cache\nkey = cache.set(\n    content=text,\n    model_name=model_name,\n    embedding=embedding,\n    metadata=metadata\n)\n\nprint(f\"Stored with key: {key[:15]}...\")","section_id":"storing-embeddings"},{"id":"retrieving-embeddings-ex0","language":"python","code":"# Retrieve from cache\n\nif result := cache.get(content=text, model_name=model_name):\n    print(f\"Found in cache: {result['content']}\")\n    print(f\"Model: {result['model_name']}\")\n    print(f\"Metadata: {result['metadata']}\")\n    print(f\"Embedding shape: {np.array(result['embedding']).shape}\")\nelse:\n    print(\"Not found in cache.\")","section_id":"retrieving-embeddings"},{"id":"checking-existence-ex0","language":"python","code":"# Check if existing text is in cache\nexists = cache.exists(content=text, model_name=model_name)\nprint(f\"First query exists in cache: {exists}\")\n\n# Check if a new text is in cache\nnew_text = \"What is deep learning?\"\nexists = cache.exists(content=new_text, model_name=model_name)\nprint(f\"New query exists in cache: {exists}\")","section_id":"checking-existence"},{"id":"removing-entries-ex0","language":"python","code":"# Remove from cache\ncache.drop(content=text, model_name=model_name)\n\n# Verify it's gone\nexists = cache.exists(content=text, model_name=model_name)\nprint(f\"After dropping: {exists}\")","section_id":"removing-entries"},{"id":"key-based-operations-ex0","language":"python","code":"# Store an entry again\nkey = cache.set(\n    content=text,\n    model_name=model_name,\n    embedding=embedding,\n    metadata=metadata\n)\nprint(f\"Stored with key: {key[:15]}...\")\n\n# Check existence by key\nexists_by_key = cache.exists_by_key(key)\nprint(f\"Exists by key: {exists_by_key}\")\n\n# Retrieve by key\nresult_by_key = cache.get_by_key(key)\nprint(f\"Retrieved by key: {result_by_key['content']}\")\n\n# Drop by key\ncache.drop_by_key(key)","section_id":"key-based-operations"},{"id":"batch-operations-ex0","language":"python","code":"# Create multiple embeddings\ntexts = [\n    \"What is machine learning?\",\n    \"How do neural networks work?\",\n    \"What is deep learning?\"\n]\nembeddings = [vectorizer.embed(t) for t in texts]\n\n# Prepare batch items as dictionaries\nbatch_items = [\n    {\n        \"content\": texts[0],\n        \"model_name\": model_name,\n        \"embedding\": embeddings[0],\n        \"metadata\": {\"category\": \"ai\", \"type\": \"question\"}\n    },\n    {\n        \"content\": texts[1],\n        \"model_name\": model_name,\n        \"embedding\": embeddings[1],\n        \"metadata\": {\"category\": \"ai\", \"type\": \"question\"}\n    },\n    {\n        \"content\": texts[2],\n        \"model_name\": model_name,\n        \"embedding\": embeddings[2],\n        \"metadata\": {\"category\": \"ai\", \"type\": \"question\"}\n    }\n]\n\n# Store multiple embeddings in one operation\nkeys = cache.mset(batch_items)\nprint(f\"Stored {len(keys)} embeddings with batch operation\")\n\n# Check if multiple embeddings exist in one operation\nexist_results = cache.mexists(texts, model_name)\nprint(f\"All embeddings exist: {all(exist_results)}\")\n\n# Retrieve multiple embeddings in one operation\nresults = cache.mget(texts, model_name)\nprint(f\"Retrieved {len(results)} embeddings in one operation\")\n\n# Delete multiple embeddings in one operation\ncache.mdrop(texts, model_name)\n\n# Alternative: key-based batch operations\n# cache.mget_by_keys(keys)     # Retrieve by keys\n# cache.mexists_by_keys(keys)  # Check existence by keys\n# cache.mdrop_by_keys(keys)    # Delete by keys","section_id":"batch-operations"},{"id":"working-with-ttl-time-to-live-ex0","language":"python","code":"# Create a cache with a default 5-second TTL\nttl_cache = EmbeddingsCache(\n    name=\"ttl_cache\",\n    redis_url=\"redis://localhost:6379\",\n    ttl=5  # 5 second TTL\n)\n\n# Store an entry\nkey = ttl_cache.set(\n    content=text,\n    model_name=model_name,\n    embedding=embedding\n)\n\n# Check if it exists\nexists = ttl_cache.exists_by_key(key)\nprint(f\"Immediately after setting: {exists}\")\n\n# Wait for it to expire\ntime.sleep(6)\n\n# Check again\nexists = ttl_cache.exists_by_key(key)\nprint(f\"After waiting: {exists}\")","section_id":"working-with-ttl-time-to-live"},{"id":"working-with-ttl-time-to-live-ex1","language":"python","code":"# Store an entry with a custom 1-second TTL\nkey1 = ttl_cache.set(\n    content=\"Short-lived entry\",\n    model_name=model_name,\n    embedding=embedding,\n    ttl=1  # Override with 1 second TTL\n)\n\n# Store another entry with the default TTL (5 seconds)\nkey2 = ttl_cache.set(\n    content=\"Default TTL entry\",\n    model_name=model_name,\n    embedding=embedding\n    # No TTL specified = uses the default 5 seconds\n)\n\n# Wait for 2 seconds\ntime.sleep(2)\n\n# Check both entries\nexists1 = ttl_cache.exists_by_key(key1)\nexists2 = ttl_cache.exists_by_key(key2)\n\nprint(f\"Entry with custom TTL after 2 seconds: {exists1}\")\nprint(f\"Entry with default TTL after 2 seconds: {exists2}\")\n\n# Cleanup\nttl_cache.drop_by_key(key2)","section_id":"working-with-ttl-time-to-live"},{"id":"async-support-ex0","language":"python","code":"async def async_cache_demo():\n    # Store an entry asynchronously\n    key = await cache.aset(\n        content=\"Async embedding\",\n        model_name=model_name,\n        embedding=embedding,\n        metadata={\"async\": True}\n    )\n\n    # Check if it exists\n    exists = await cache.aexists_by_key(key)\n    print(f\"Async set successful? {exists}\")\n\n    # Retrieve it\n    result = await cache.aget_by_key(key)\n    success = result is not None and result[\"content\"] == \"Async embedding\"\n    print(f\"Async get successful? {success}\")\n\n    # Remove it\n    await cache.adrop_by_key(key)\n\n# Run the async demo\nawait async_cache_demo()","section_id":"async-support"},{"id":"real-world-example-ex0","language":"python","code":"# Create a fresh cache for this example\nexample_cache = EmbeddingsCache(\n    name=\"example_cache\",\n    redis_url=\"redis://localhost:6379\",\n    ttl=3600  # 1 hour TTL\n)\n\nvectorizer = HFTextVectorizer(\n    model=model_name,\n    cache=example_cache,\n    cache_folder=os.getenv(\"SENTENCE_TRANSFORMERS_HOME\")\n)\n\n# Simulate processing a stream of queries\nqueries = [\n    \"What is artificial intelligence?\",\n    \"How does machine learning work?\",\n    \"What is artificial intelligence?\",  # Repeated query\n    \"What are neural networks?\",\n    \"How does machine learning work?\"   # Repeated query\n]\n\n# Process the queries and track statistics\ntotal_queries = 0\ncache_hits = 0\n\nfor query in queries:\n    total_queries += 1\n\n    # Check cache before computing\n    before = example_cache.exists(content=query, model_name=model_name)\n    if before:\n        cache_hits += 1\n\n    # Get embedding (will compute or use cache)\n    embedding = vectorizer.embed(query)\n\n# Report statistics\ncache_misses = total_queries - cache_hits\nhit_rate = (cache_hits / total_queries) * 100\n\nprint(\"\\nStatistics:\")\nprint(f\"Total queries: {total_queries}\")\nprint(f\"Cache hits: {cache_hits}\")\nprint(f\"Cache misses: {cache_misses}\")\nprint(f\"Cache hit rate: {hit_rate:.1f}%\")\n\n# Cleanup\nfor query in set(queries):  # Use set to get unique queries\n    example_cache.drop(content=query, model_name=model_name)","section_id":"real-world-example"},{"id":"performance-benchmark-ex0","language":"python","code":"# Text to use for benchmarking\nbenchmark_text = \"This is a benchmark text to measure the performance of embedding caching.\"\n\n# Create a fresh cache for benchmarking\nbenchmark_cache = EmbeddingsCache(\n    name=\"benchmark_cache\",\n    redis_url=\"redis://localhost:6379\",\n    ttl=3600  # 1 hour TTL\n)\nvectorizer.cache = benchmark_cache\n\n# Number of iterations for the benchmark\nn_iterations = 10\n\n# Benchmark without caching\nprint(\"Benchmarking without caching:\")\nstart_time = time.time()\nfor _ in range(n_iterations):\n    embedding = vectorizer.embed(text, skip_cache=True)\nno_cache_time = time.time() - start_time\nprint(f\"Time taken without caching: {no_cache_time:.4f} seconds\")\nprint(f\"Average time per embedding: {no_cache_time/n_iterations:.4f} seconds\")\n\n# Benchmark with caching\nprint(\"\\nBenchmarking with caching:\")\nstart_time = time.time()\nfor _ in range(n_iterations):\n    embedding = vectorizer.embed(text)\ncache_time = time.time() - start_time\nprint(f\"Time taken with caching: {cache_time:.4f} seconds\")\nprint(f\"Average time per embedding: {cache_time/n_iterations:.4f} seconds\")\n\n# Compare performance\nspeedup = no_cache_time / cache_time\nlatency_reduction = (no_cache_time/n_iterations) - (cache_time/n_iterations)\nprint(f\"\\nPerformance comparison:\")\nprint(f\"Speedup with caching: {speedup:.2f}x faster\")\nprint(f\"Time saved: {no_cache_time - cache_time:.4f} seconds ({(1 - cache_time/no_cache_time) * 100:.1f}%)\")\nprint(f\"Latency reduction: {latency_reduction:.4f} seconds per query\")","section_id":"performance-benchmark"},{"id":"cleanup-ex0","language":"python","code":"# Clean up all caches\ncache.clear()\nttl_cache.clear()\nexample_cache.clear()\nbenchmark_cache.clear()","section_id":"cleanup"}]}
{"schema_version":2,"id":"develop/ai/redisvl/user_guide/how_to_guides/hash_vs_json","title":"Choose a Storage Type","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/how_to_guides/hash_vs_json/","summary":"","aliases":["/integrate/redisvl/user_guide/how_to_guides/05_hash_vs_json","/integrate/redisvl/user_guide/hash_vs_json/","/develop/ai/redisvl/user_guide/hash_vs_json/"],"tags":[],"last_updated":"2026-10-01T14:49:09Z","page_type":"content","content_hash":"8d580f44d67d4154d729b73ba7bfb2f0ee1bfec5bbbbab588210e82c4b920f26","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis provides a [variety of data structures](https://redis.com/redis-enterprise/data-structures/) that can adapt to your domain-specific applications. This guide demonstrates how to use RedisVL with both [Hash](https://redis.io/docs/latest/develop/data-types/#hashes) and [JSON](https://redis.io/docs/latest/develop/data-types/json/) storage types, helping you choose the right approach for your use case."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before you begin, ensure you have:\n- Installed RedisVL: `pip install redisvl`\n- A running Redis instance ([Redis 8+](https://redis.io/downloads/) or [Redis Cloud](https://redis.io/cloud))"},{"id":"what-youll-learn","title":"What You'll Learn","role":"content","text":"By the end of this guide, you will be able to:\n- Understand the differences between Hash and JSON storage types\n- Define schemas for both Hash and JSON storage\n- Load and query data using each storage type\n- Access nested JSON fields using JSONPath expressions\n- Choose the right storage type for your application\n\n\n[code example]\n\n\n[code example]\n\n\n<table><tr><th>user</th><th>age</th><th>job</th><th>credit_score</th><th>office_location</th><th>user_embedding</th><th>last_updated</th></tr><tr><td>john</td><td>18</td><td>engineer</td><td>high</td><td>-122.4194,37.7749</td><td>b'\\xcd\\xcc\\xcc=\\xcd\\xcc\\xcc=\\x00\\x00\\x00?'</td><td>1741627789</td></tr><tr><td>derrick</td><td>14</td><td>doctor</td><td>low</td><td>-122.4194,37.7749</td><td>b'\\xcd\\xcc\\xcc=\\xcd\\xcc\\xcc=\\x00\\x00\\x00?'</td><td>1741627789</td></tr><tr><td>nancy</td><td>94</td><td>doctor</td><td>high</td><td>-122.4194,37.7749</td><td>b'333?\\xcd\\xcc\\xcc=\\x00\\x00\\x00?'</td><td>1710696589</td></tr><tr><td>tyler</td><td>100</td><td>engineer</td><td>high</td><td>-122.0839,37.3861</td><td>b'\\xcd\\xcc\\xcc=\\xcd\\xcc\\xcc>\\x00\\x00\\x00?'</td><td>1742232589</td></tr><tr><td>tim</td><td>12</td><td>dermatologist</td><td>high</td><td>-122.0839,37.3861</td><td>b'\\xcd\\xcc\\xcc>\\xcd\\xcc\\xcc>\\x00\\x00\\x00?'</td><td>1739644189</td></tr><tr><td>taimur</td><td>15</td><td>CEO</td><td>low</td><td>-122.0839,37.3861</td><td>b'\\x9a\\x99\\x19?\\xcd\\xcc\\xcc=\\x00\\x00\\x00?'</td><td>1742232589</td></tr><tr><td>joe</td><td>35</td><td>dentist</td><td>medium</td><td>-122.0839,37.3861</td><td>b'fff?fff?\\xcd\\xcc\\xcc='</td><td>1742232589</td></tr></table>"},{"id":"hash-or-json-how-to-choose","title":"Hash or JSON: How to Choose","role":"content","text":"Both storage options offer different features and tradeoffs. This section walks through a sample dataset to illustrate when and how to use each option."},{"id":"working-with-hashes","title":"Working with Hashes","role":"content","text":"Hashes in Redis are simple collections of field-value pairs. Think of it like a mutable single-level dictionary contains multiple \"rows\":\n\n\n[code example]\n\nHashes are best suited for use cases with the following characteristics:\n- Performance (speed) and storage space (memory consumption) are top concerns\n- Data can be easily normalized and modeled as a single-level dict\n\nHashes are typically the default recommendation.\n\n\n[code example]\n\n\n[code example]\n\n\n[code example]\n\n\n\n\n    <StorageType.HASH: 'hash'>\n\n\n\n#### Vectors as byte strings\nOne nuance when working with Hashes in Redis, is that all vectorized data must be passed as a byte string (for efficient storage, indexing, and processing). An example of that can be seen below:\n\n\n[code example]\n\n\n\n\n    {'user': 'john',\n     'age': 18,\n     'job': 'engineer',\n     'credit_score': 'high',\n     'office_location': '-122.4194,37.7749',\n     'user_embedding': b'\\xcd\\xcc\\xcc=\\xcd\\xcc\\xcc=\\x00\\x00\\x00?',\n     'last_updated': 1741627789}\n\n\n\n\n[code example]\n\n\n[code example]\n\n    \n    Statistics:\n    ╭─────────────────────────────┬────────────╮\n    │ Stat Key                    │ Value      │\n    ├─────────────────────────────┼────────────┤\n    │ num_docs                    │ 7          │\n    │ num_terms                   │ 6          │\n    │ max_doc_id                  │ 7          │\n    │ num_records                 │ 44         │\n    │ percent_indexed             │ 1          │\n    │ hash_indexing_failures      │ 0          │\n    │ number_of_uses              │ 1          │\n    │ bytes_per_record_avg        │ 39.0681800 │\n    │ doc_table_size_mb           │ 0.00837230 │\n    │ inverted_sz_mb              │ 0.00163936 │\n    │ key_table_size_mb           │ 3.50952148 │\n    │ offset_bits_per_record_avg  │ 8          │\n    │ offset_vectors_sz_mb        │ 8.58306884 │\n    │ offsets_per_term_avg        │ 0.20454545 │\n    │ records_per_doc_avg         │ 6.28571414 │\n    │ sortable_values_size_mb     │ 0          │\n    │ total_indexing_time         │ 0.55204    │\n    │ total_inverted_index_blocks │ 18         │\n    │ vector_index_sz_mb          │ 0.02820587 │\n    ╰─────────────────────────────┴────────────╯\n\n\n#### Performing Queries\nOnce our index is created and data is loaded into the right format, we can run queries against the index with RedisVL:\n\n\n[code example]\n\n\n<table><tr><th>vector_distance</th><th>user</th><th>credit_score</th><th>age</th><th>job</th><th>office_location</th></tr><tr><td>0</td><td>john</td><td>high</td><td>18</td><td>engineer</td><td>-122.4194,37.7749</td></tr><tr><td>0.109129190445</td><td>tyler</td><td>high</td><td>100</td><td>engineer</td><td>-122.0839,37.3861</td></tr></table>\n\n\n\n[code example]"},{"id":"working-with-json","title":"Working with JSON","role":"content","text":"JSON is best suited for use cases with the following characteristics:\n- Ease of use and data model flexibility are top concerns\n- Application data is already native JSON\n- Replacing another document storage/db solution\n\n\n[code example]\n\n\n[code example]\n\n#### Vectors as Float Arrays\nVectorized data stored in JSON must be a pure array (Python list) of floats. The following code modifies the sample data to use this format:\n\n\n[code example]\n\n\n[code example]\n\n\n\n\n    {'user': 'john',\n     'age': 18,\n     'job': 'engineer',\n     'credit_score': 'high',\n     'office_location': '-122.4194,37.7749',\n     'user_embedding': [0.10000000149011612, 0.10000000149011612, 0.5],\n     'last_updated': 1741627789}\n\n\n\n\n[code example]\n\n\n[code example]\n\n\n<table><tr><th>vector_distance</th><th>user</th><th>credit_score</th><th>age</th><th>job</th><th>office_location</th></tr><tr><td>0</td><td>john</td><td>high</td><td>18</td><td>engineer</td><td>-122.4194,37.7749</td></tr><tr><td>0.109129190445</td><td>tyler</td><td>high</td><td>100</td><td>engineer</td><td>-122.0839,37.3861</td></tr></table>"},{"id":"cleanup","title":"Cleanup","role":"content","text":"[code example]"},{"id":"working-with-nested-data-in-json","title":"Working with nested data in JSON","role":"content","text":"Redis also supports native **JSON** objects. These can be multi-level (nested) objects, with full JSONPath support for updating/retrieving sub elements:\n\n[code example]"},{"id":"full-json-path-support","title":"Full JSON Path support","role":"content","text":"Because Redis enables full JSON path support, when creating an index schema, elements need to be indexed and selected by their path with the desired `name` AND `path` that points to where the data is located within the objects.\n\nBy default, RedisVL will assume the path as `$.{name}` if not provided in JSON fields schema. If nested provide path as `$.object.attribute`"},{"id":"as-an-example","title":"As an example:","role":"content","text":"[code example]\n\n\n[code example]\n\n\n[code example]\n\n\n\n\n    ['bike-json:01KHKJ5WW3DJE0X6E85GG27V0X',\n     'bike-json:01KHKJ5WW3DJE0X6E85GG27V0Y']\n\n\n\n\n[code example]\n\n**Note:** As shown in the example if you want to retrieve a field from json object that was not indexed you will also need to supply the full path as with `$.metadata.type`.\n\n\n[code example]\n\n\n\n\n    [{'id': 'bike-json:01KHKJ5WW3DJE0X6E85GG27V0Y',\n      'vector_distance': '0.519988954067',\n      'brand': 'Trek',\n      '$.metadata.type': 'Enduro bikes'},\n     {'id': 'bike-json:01KHKJ5WW3DJE0X6E85GG27V0X',\n      'vector_distance': '0.65762424469',\n      'brand': 'Specialized',\n      '$.metadata.type': 'Enduro bikes'}]"},{"id":"next-steps","title":"Next Steps","role":"content","text":"Now that you understand Hash vs JSON storage, explore these related guides:\n\n- [Getting Started](https://redis.io/docs/latest/../getting_started) - Learn the basics of RedisVL indexes and queries\n- [Query and Filter Data](https://redis.io/docs/latest/complex_filtering) - Apply filters to narrow down search results\n- [Use Advanced Query Types](https://redis.io/docs/latest/advanced_queries) - Explore TextQuery, HybridQuery, and more\n\n\n[code example]"}],"examples":[{"id":"what-youll-learn-ex0","language":"python","code":"# import necessary modules\nimport pickle\n\nfrom redisvl.redis.utils import buffer_to_array\nfrom redisvl.index import SearchIndex\n\n\n# load in the example data and printing utils\ndata = pickle.load(open(\"hybrid_example_data.pkl\", \"rb\"))","section_id":"what-youll-learn"},{"id":"what-youll-learn-ex1","language":"python","code":"from jupyterutils import result_print, table_print\n\ntable_print(data)","section_id":"what-youll-learn"},{"id":"working-with-hashes-ex0","language":"python","code":"{\n    \"model\": \"Deimos\",\n    \"brand\": \"Ergonom\",\n    \"type\": \"Enduro bikes\",\n    \"price\": 4972,\n}","section_id":"working-with-hashes"},{"id":"working-with-hashes-ex1","language":"python","code":"# define the hash index schema\nhash_schema = {\n    \"index\": {\n        \"name\": \"user-hash\",\n        \"prefix\": \"user-hash-docs\",\n        \"storage_type\": \"hash\", # default setting -- HASH\n    },\n    \"fields\": [\n        {\"name\": \"user\", \"type\": \"tag\"},\n        {\"name\": \"credit_score\", \"type\": \"tag\"},\n        {\"name\": \"job\", \"type\": \"text\"},\n        {\"name\": \"age\", \"type\": \"numeric\"},\n        {\"name\": \"office_location\", \"type\": \"geo\"},\n        {\n            \"name\": \"user_embedding\",\n            \"type\": \"vector\",\n            \"attrs\": {\n                \"dims\": 3,\n                \"distance_metric\": \"cosine\",\n                \"algorithm\": \"flat\",\n                \"datatype\": \"float32\"\n            }\n\n        }\n    ],\n}","section_id":"working-with-hashes"},{"id":"working-with-hashes-ex2","language":"python","code":"# construct a search index from the hash schema\nhindex = SearchIndex.from_dict(hash_schema, redis_url=\"redis://localhost:6379\")\n\n# create the index (no data yet)\nhindex.create(overwrite=True)","section_id":"working-with-hashes"},{"id":"working-with-hashes-ex3","language":"python","code":"# show the underlying storage type\nhindex.storage_type","section_id":"working-with-hashes"},{"id":"working-with-hashes-ex4","language":"python","code":"# show a single entry from the data that will be loaded\ndata[0]","section_id":"working-with-hashes"},{"id":"working-with-hashes-ex5","language":"python","code":"# load hash data\nkeys = hindex.load(data)","section_id":"working-with-hashes"},{"id":"working-with-hashes-ex6","language":"python","code":"!rvl stats -i user-hash","section_id":"working-with-hashes"},{"id":"working-with-hashes-ex7","language":"python","code":"from redisvl.query import VectorQuery\nfrom redisvl.query.filter import Tag, Text, Num\n\nt = (Tag(\"credit_score\") == \"high\") & (Text(\"job\") % \"enginee*\") & (Num(\"age\") > 17)  # codespell:ignore enginee\n\nv = VectorQuery(\n    vector=[0.1, 0.1, 0.5],\n    vector_field_name=\"user_embedding\",\n    return_fields=[\"user\", \"credit_score\", \"age\", \"job\", \"office_location\"],\n    filter_expression=t\n)\n\n\nresults = hindex.query(v)\nresult_print(results)","section_id":"working-with-hashes"},{"id":"working-with-hashes-ex8","language":"python","code":"# clean up\nhindex.delete()","section_id":"working-with-hashes"},{"id":"working-with-json-ex0","language":"python","code":"# define the json index schema\njson_schema = {\n    \"index\": {\n        \"name\": \"user-json\",\n        \"prefix\": \"user-json-docs\",\n        \"storage_type\": \"json\", # JSON storage type\n    },\n    \"fields\": [\n        {\"name\": \"user\", \"type\": \"tag\"},\n        {\"name\": \"credit_score\", \"type\": \"tag\"},\n        {\"name\": \"job\", \"type\": \"text\"},\n        {\"name\": \"age\", \"type\": \"numeric\"},\n        {\"name\": \"office_location\", \"type\": \"geo\"},\n        {\n            \"name\": \"user_embedding\",\n            \"type\": \"vector\",\n            \"attrs\": {\n                \"dims\": 3,\n                \"distance_metric\": \"cosine\",\n                \"algorithm\": \"flat\",\n                \"datatype\": \"float32\"\n            }\n\n        }\n    ],\n}","section_id":"working-with-json"},{"id":"working-with-json-ex1","language":"python","code":"# construct a search index from the json schema\njindex = SearchIndex.from_dict(json_schema, redis_url=\"redis://localhost:6379\")\n\n# create the index (no data yet)\njindex.create(overwrite=True)","section_id":"working-with-json"},{"id":"working-with-json-ex2","language":"python","code":"json_data = data.copy()\n\nfor d in json_data:\n    d['user_embedding'] = buffer_to_array(d['user_embedding'], dtype='float32')","section_id":"working-with-json"},{"id":"working-with-json-ex3","language":"python","code":"# inspect a single JSON record\njson_data[0]","section_id":"working-with-json"},{"id":"working-with-json-ex4","language":"python","code":"keys = jindex.load(json_data)","section_id":"working-with-json"},{"id":"working-with-json-ex5","language":"python","code":"# we can now run the exact same query as above\nresult_print(jindex.query(v))","section_id":"working-with-json"},{"id":"cleanup-ex0","language":"python","code":"jindex.delete()","section_id":"cleanup"},{"id":"working-with-nested-data-in-json-ex0","language":"json","code":"{\n    \"name\": \"Specialized Stump jumper\",\n    \"metadata\": {\n        \"model\": \"Stumpjumper\",\n        \"brand\": \"Specialized\",\n        \"type\": \"Enduro bikes\",\n        \"price\": 3000\n    },\n}","section_id":"working-with-nested-data-in-json"},{"id":"as-an-example-ex0","language":"python","code":"from redisvl.utils.vectorize import HFTextVectorizer\n\nemb_model = HFTextVectorizer()\n\nbike_data = [\n    {\n        \"name\": \"Specialized Stump jumper\",\n        \"metadata\": {\n            \"model\": \"Stumpjumper\",\n            \"brand\": \"Specialized\",\n            \"type\": \"Enduro bikes\",\n            \"price\": 3000\n        },\n        \"description\": \"The Specialized Stumpjumper is a versatile enduro bike that dominates both climbs and descents. Features a FACT 11m carbon fiber frame, FOX FLOAT suspension with 160mm travel, and SRAM X01 Eagle drivetrain. The asymmetric frame design and internal storage compartment make it a practical choice for all-day adventures.\"\n    },\n    {\n        \"name\": \"bike_2\",\n        \"metadata\": {\n            \"model\": \"Slash\",\n            \"brand\": \"Trek\",\n            \"type\": \"Enduro bikes\",\n            \"price\": 5000\n        },\n        \"description\": \"Trek's Slash is built for aggressive enduro riding and racing. Featuring Trek's Alpha Aluminum frame with RE:aktiv suspension technology, 160mm travel, and Knock Block frame protection. Equipped with Bontrager components and a Shimano XT drivetrain, this bike excels on technical trails and enduro race courses.\"\n    }\n]\n\nbike_data = [{**d, \"bike_embedding\": emb_model.embed(d[\"description\"])} for d in bike_data]\n\nbike_schema = {\n    \"index\": {\n        \"name\": \"bike-json\",\n        \"prefix\": \"bike-json\",\n        \"storage_type\": \"json\", # JSON storage type\n    },\n    \"fields\": [\n        {\n            \"name\": \"model\",\n            \"type\": \"tag\",\n            \"path\": \"$.metadata.model\" # note the '$'\n        },\n        {\n            \"name\": \"brand\",\n            \"type\": \"tag\",\n            \"path\": \"$.metadata.brand\"\n        },\n        {\n            \"name\": \"price\",\n            \"type\": \"numeric\",\n            \"path\": \"$.metadata.price\"\n        },\n        {\n            \"name\": \"bike_embedding\",\n            \"type\": \"vector\",\n            \"attrs\": {\n                \"dims\": len(bike_data[0][\"bike_embedding\"]),\n                \"distance_metric\": \"cosine\",\n                \"algorithm\": \"flat\",\n                \"datatype\": \"float32\"\n            }\n\n        }\n    ],\n}","section_id":"as-an-example"},{"id":"as-an-example-ex1","language":"python","code":"# construct a search index from the json schema\nbike_index = SearchIndex.from_dict(bike_schema, redis_url=\"redis://localhost:6379\")\n\n# create the index (no data yet)\nbike_index.create(overwrite=True)","section_id":"as-an-example"},{"id":"as-an-example-ex2","language":"python","code":"bike_index.load(bike_data)","section_id":"as-an-example"},{"id":"as-an-example-ex3","language":"python","code":"from redisvl.query import VectorQuery\n\nvec = emb_model.embed(\"I'd like a bike for aggressive riding\")\n\nv = VectorQuery(\n    vector=vec,\n    vector_field_name=\"bike_embedding\",\n    return_fields=[\n        \"brand\",\n        \"name\",\n        \"$.metadata.type\"\n    ]\n)\n\n\nresults = bike_index.query(v)","section_id":"as-an-example"},{"id":"as-an-example-ex4","language":"python","code":"results","section_id":"as-an-example"},{"id":"next-steps-ex0","language":"python","code":"# Cleanup\nbike_index.delete()","section_id":"next-steps"}]}
{"schema_version":2,"id":"develop/ai/redisvl/user_guide/how_to_guides","title":"How-To Guides","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/how_to_guides/","summary":"","aliases":["/integrate/redisvl/user_guide/how_to_guides"],"tags":[],"last_updated":"2026-07-02T16:24:13+02:00","children":[{"id":"develop/ai/redisvl/user_guide/how_to_guides/complex_filtering","summary":"","title":"Query and Filter Data","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/how_to_guides/complex_filtering/"},{"id":"develop/ai/redisvl/user_guide/how_to_guides/llmcache","summary":"","title":"Cache LLM Responses","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/how_to_guides/llmcache/"},{"id":"develop/ai/redisvl/user_guide/how_to_guides/vectorizers","summary":"","title":"Create Embeddings with Vectorizers","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/how_to_guides/vectorizers/"},{"id":"develop/ai/redisvl/user_guide/how_to_guides/hash_vs_json","summary":"","title":"Choose a Storage Type","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/how_to_guides/hash_vs_json/"},{"id":"develop/ai/redisvl/user_guide/how_to_guides/rerankers","summary":"","title":"Rerank Search Results","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/how_to_guides/rerankers/"},{"id":"develop/ai/redisvl/user_guide/how_to_guides/message_history","summary":"","title":"Manage LLM Message History","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/how_to_guides/message_history/"},{"id":"develop/ai/redisvl/user_guide/how_to_guides/semantic_router","summary":"","title":"Route Queries with SemanticRouter","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/how_to_guides/semantic_router/"},{"id":"develop/ai/redisvl/user_guide/how_to_guides/svs_vamana","summary":"","title":"Optimize Indexes with SVS-VAMANA","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/how_to_guides/svs_vamana/"},{"id":"develop/ai/redisvl/user_guide/how_to_guides/embeddings_cache","summary":"","title":"Cache Embeddings","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/how_to_guides/embeddings_cache/"},{"id":"develop/ai/redisvl/user_guide/how_to_guides/advanced_queries","summary":"","title":"Use Advanced Query Types","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/how_to_guides/advanced_queries/"},{"id":"develop/ai/redisvl/user_guide/how_to_guides/sql_to_redis_queries","summary":"","title":"Write SQL Queries for Redis","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/how_to_guides/sql_to_redis_queries/"},{"id":"develop/ai/redisvl/user_guide/how_to_guides/langcache_semantic_cache","summary":"","title":"Use LangCache as the LLM Cache Backend","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/how_to_guides/langcache_semantic_cache/"},{"id":"develop/ai/redisvl/user_guide/how_to_guides/index_migration","summary":"","title":"Migrate an Index: Vector Quantization, Resume, Backup, and Wizard","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/how_to_guides/index_migration/"},{"id":"develop/ai/redisvl/user_guide/how_to_guides/mcp_authentication","summary":"","title":"Authenticate RedisVL MCP","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/how_to_guides/mcp_authentication/"},{"id":"develop/ai/redisvl/user_guide/how_to_guides/migrate-indexes","summary":"","title":"Migrate an Index","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/how_to_guides/migrate-indexes/"},{"id":"develop/ai/redisvl/user_guide/how_to_guides/mcp","summary":"","title":"Run RedisVL MCP","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/how_to_guides/mcp/"}],"page_type":"content","content_hash":"0b37c7e258685743e9f877087e582e57fca0c738d40efa9517c5366ce1c829c4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"How-to guides are **task-oriented** recipes that help you accomplish specific goals. Each guide focuses on solving a particular problem and can be completed independently.\n\n<div class=\"grid grid-cols-1 md:grid-cols-2 gap-4 my-6\">\n<div class=\"p-5 border border-redis-pen-300 rounded-lg\"><h3 class=\"mt-0 mb-2\">🤖 LLM Extensions</h3><ul>\n<li><a href=\"llmcache/\">Cache LLM Responses</a>: semantic caching to reduce costs and latency</li>\n<li><a href=\"langcache_semantic_cache/\">Use LangCache as the LLM cache</a>: managed cache service with LangCache</li>\n<li><a href=\"message_history/\">Manage LLM Message History</a>: persistent chat history with relevancy retrieval</li>\n<li><a href=\"semantic_router/\">Route Queries with SemanticRouter</a>: classify intents and route queries</li>\n</ul></div>\n<div class=\"p-5 border border-redis-pen-300 rounded-lg\"><h3 class=\"mt-0 mb-2\">🔍 Querying</h3><ul>\n<li><a href=\"complex_filtering/\">Query and Filter Data</a>: combine tag, numeric, geo, and text filters</li>\n<li><a href=\"advanced_queries/\">Use Advanced Query Types</a>: hybrid, multi-vector, range, and text queries</li>\n<li><a href=\"sql_to_redis_queries/\">Write SQL Queries for Redis</a>: translate SQL to Redis query syntax</li>\n</ul></div>\n<div class=\"p-5 border border-redis-pen-300 rounded-lg\"><h3 class=\"mt-0 mb-2\">🧮 Embeddings</h3><ul>\n<li><a href=\"vectorizers/\">Create Embeddings with Vectorizers</a>: OpenAI, Cohere, HuggingFace, and more</li>\n<li><a href=\"embeddings_cache/\">Cache Embeddings</a>: reduce costs by caching embedding vectors</li>\n</ul></div>\n<div class=\"p-5 border border-redis-pen-300 rounded-lg\"><h3 class=\"mt-0 mb-2\">⚡ Optimization</h3><ul>\n<li><a href=\"rerankers/\">Rerank Search Results</a>: improve relevance with cross-encoders and rerankers</li>\n<li><a href=\"svs_vamana/\">Optimize Indexes with SVS-VAMANA</a>: graph-based vector search with compression</li>\n</ul></div>\n<div class=\"p-5 border border-redis-pen-300 rounded-lg\"><h3 class=\"mt-0 mb-2\">💾 Storage</h3><ul>\n<li><a href=\"hash_vs_json/\">Choose a Storage Type</a>: Hash vs JSON formats and nested data</li>\n<li><a href=\"migrate-indexes/\">Migrate an Index</a>: use the migrator helper, wizard, plan, apply, and validate workflow</li>\n<li><a href=\"index_migration/\">Migrate an Index: Quantization, Resume, Backup, Wizard</a>: hands-on notebook for vector quantization with crash-safe resume, rollback, and wizard flow</li>\n</ul></div>\n<div class=\"p-5 border border-redis-pen-300 rounded-lg\"><h3 class=\"mt-0 mb-2\">💻 CLI Operations</h3><ul>\n<li><a href=\"../cli/\">Manage Indices with the CLI</a>: create, inspect, and delete indices from your terminal</li>\n<li><a href=\"mcp/\">Run RedisVL MCP</a>: expose an existing Redis index to MCP clients</li>\n<li><a href=\"mcp_authentication/\">Authenticate RedisVL MCP</a>: require JWT bearer tokens and gate read vs write</li>\n</ul></div>\n</div>"},{"id":"quick-reference","title":"Quick Reference","role":"content","text":"| I want to... | Guide |\n|--------------|-------|\n| Cache LLM responses | [Cache LLM Responses](llmcache/) |\n| Use LangCache (managed) for LLM caching | [Use LangCache as the LLM cache](langcache_semantic_cache/) |\n| Store chat history | [Manage LLM Message History](message_history/) |\n| Route queries by intent | [Route Queries with SemanticRouter](semantic_router/) |\n| Filter results by multiple criteria | [Query and Filter Data](complex_filtering/) |\n| Use hybrid or multi-vector queries | [Use Advanced Query Types](advanced_queries/) |\n| Translate SQL to Redis | [Write SQL Queries for Redis](sql_to_redis_queries/) |\n| Choose an embedding model | [Create Embeddings with Vectorizers](vectorizers/) |\n| Speed up embedding generation | [Cache Embeddings](embeddings_cache/) |\n| Improve search accuracy | [Rerank Search Results](rerankers/) |\n| Optimize index performance | [Optimize Indexes with SVS-VAMANA](svs_vamana/) |\n| Decide on storage format | [Choose a Storage Type](hash_vs_json/) |\n| Manage indices from terminal | [Manage Indices with the CLI](../cli/) |\n| Expose an index through MCP | [Run RedisVL MCP](mcp/) |\n| Authenticate the MCP server | [Authenticate RedisVL MCP](mcp_authentication/) |\n| Plan and run a supported index migration | [Migrate an Index](migrate-indexes/) |\n| Quantize vectors with resume, rollback, and the wizard | [Migrate an Index: Quantization, Resume, Backup, Wizard](index_migration/) |"}],"examples":[]}
{"schema_version":2,"id":"develop/ai/redisvl/user_guide/how_to_guides/index_migration","title":"Migrate an Index: Vector Quantization, Resume, Backup, and Wizard","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/how_to_guides/index_migration/","summary":"","aliases":["/integrate/redisvl/user_guide/how_to_guides/14_index_migration"],"tags":[],"last_updated":"2026-10-05T16:11:44+01:00","page_type":"content","content_hash":"62df488f3fc096cb159994024179d1361da256e074c5207d371b8279fa3a544e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!WARNING]\n> The index migrator is an **experimental** feature. APIs, CLI commands, and\n> on-disk formats (plans, backups) may change in future releases. Review\n> migration plans carefully before applying them to production indexes.\n\nThis guide walks through a **vector quantization** migration\n(`float32` -> `float16`) end to end using the programmatic API. You will\nlearn how to:\n\n- Build a schema patch that describes the change\n- Generate and review a migration **plan** (read-only)\n- **Apply** the migration with a mandatory on-disk backup\n- Find **where the backup lives** and inspect its progress header\n- Understand **crash-safe resume** and safely re-run a migration\n- **Reload original vectors from the backup** (rollback)\n- Build and apply the same migration through the **wizard**\n\nFor conceptual background see\n[Index Migrations](https://redis.io/docs/latest/../../concepts/index-migrations) and the\n[Migrate an Index how-to](https://redis.io/docs/latest/migrate-indexes).\n\n**Prerequisites:** a running Redis 8.0+ (or Redis Stack) at\n`redis://localhost:6379` and `redisvl` installed.\n\n\n[code example]"},{"id":"1-create-a-source-index-with-float32-vectors","title":"1. Create a source index with float32 vectors","role":"content","text":"We start with a small Hash index whose `embedding` field stores full\nprecision `float32` vectors, then load some random data.\n\n\n[code example]\n\n    Loaded 600 documents into 'products'\n\n\n\n[code example]\n\n    Indices:\n    1. products"},{"id":"2-describe-the-change-with-a-schema-patch","title":"2. Describe the change with a schema patch","role":"content","text":"A **schema patch** lists only what changes. Here we update the\n`embedding` field's datatype from `float32` to `float16` (a 2x memory\nreduction). We write it to a YAML file the planner can read.\n\n\n[code example]\n\n    version: 1\n    changes:\n      update_fields:\n      - name: embedding\n        attrs:\n          datatype: float16"},{"id":"3-create-a-migration-plan-read-only","title":"3. Create a migration plan (read-only)","role":"content","text":"`create_plan` snapshots the live index, diffs it against the patch, and\nreturns a `MigrationPlan`. **No data is modified.** Review the warnings\nand the classified changes before applying.\n\n\n[code example]\n\n    Index:      products\n    Mode:       drop_recreate\n    Requested:  {'version': 1, 'changes': {'add_fields': [], 'remove_fields': [], 'update_fields': [{'name': 'embedding', 'attrs': {'datatype': 'float16'}, 'options': {}}], 'rename_fields': [], 'index': {}}}\n    Warnings:  \n      - Index downtime is required\n    \n    Saved plan to migration_plan.yaml"},{"id":"4-apply-the-migration-with-a-mandatory-backup","title":"4. Apply the migration with a mandatory backup","role":"content","text":"The executor requires `backup_dir` before applying any migration. For\nquantization, it writes original vectors to disk before mutating them. If\nyou omit `backup_dir`, or pass a path that cannot be created or written,\nthe migration fails before touching the index. The returned report records\nthe resolved backup directory and any backup file prefixes used.\n\nWe also pass a `progress_callback` to watch each phase.\n\n> [!NOTE]\n> This drops and recreates the index definition. Documents are preserved;\n> only the index structure and vector encoding change. Pause writes during\n> the migration window.\n\n\n[code example]\n\n    Missing backup_dir is rejected before migration: A backup directory is required to apply migrations. Provide --backup-dir or backup_dir=...; migrations are not started without a backup directory.\n    Invalid backup_dir is rejected before migration: Could not create or access backup directory './not_a_backup_dir': [Errno 17] File exists: 'not_a_backup_dir'. A writable backup directory is required to safely migrate.\n    [enumerate] Enumerating indexed documents...\n    [enumerate] found 600 documents (0.003s)\n    [dump] Backing up original vectors...\n    [dump] 100/600 docs\n    [dump] 200/600 docs\n    [dump] 300/600 docs\n    [dump] 400/600 docs\n    [dump] 500/600 docs\n    [dump] 600/600 docs\n    [dump] done (0.009s)\n    [drop] Dropping index definition...\n    [drop] done (0.001s)\n    [quantize] Re-encoding vectors from backup...\n    [quantize] 100/600 docs\n    [quantize] 200/600 docs\n    [quantize] 300/600 docs\n    [quantize] 400/600 docs\n    [quantize] 500/600 docs\n    [quantize] 600/600 docs\n    [quantize] done (600 docs in 0.009s)\n    [create] Creating index with new schema...\n\n\n    [create] done (0.004s)\n    [index] Waiting for re-indexing...\n    [index] 22/115 docs (19%)\n\n\n    [index] 600/600 docs (100%)\n    [index] done (0.508s)\n    [validate] Validating migration...\n    [validate] done (0.01s)\n    \n    Result:            succeeded\n    Total duration:    0.554 s\n    Quantize duration: 0.009 s\n    Schema match:      True\n    Doc count match:   True\n    Backup dir:        /Users/nitin.kanukolanu/workspace/redis-vl-python/docs/user_guide/migration_backups\n    Backup prefixes:   ['/Users/nitin.kanukolanu/workspace/redis-vl-python/docs/user_guide/migration_backups/migration_backup_products_0a3e27b8']"},{"id":"5-where-is-the-backup-and-whats-in-it","title":"5. Where is the backup, and what's in it?","role":"content","text":"Backups are written under `report.backup.backup_dir`. For a single-worker\nHash quantization migration there are two files per index:\n\n- `migration_backup_<index>_<hash>.header` -- JSON: phase + progress counters\n- `migration_backup_<index>_<hash>.data` -- binary: original vectors, batched\n\nThe `<hash>` suffix is a short digest of the index name, which avoids\ncollisions. `report.backup.backup_paths` stores the path prefix without\n`.header` or `.data`. Multi-worker migrations record one prefix per\nworker.\n\nBackups are **retained after success** so you can audit or roll back;\ndelete them manually when no longer needed.\n\n\n[code example]\n\n    /Users/nitin.kanukolanu/workspace/redis-vl-python/docs/user_guide/migration_backups/migration_backup_products_0a3e27b8.data  (47,730 bytes)\n    /Users/nitin.kanukolanu/workspace/redis-vl-python/docs/user_guide/migration_backups/migration_backup_products_0a3e27b8.header  (209 bytes)\n\n\n\n[code example]\n\n    backup prefix:              /Users/nitin.kanukolanu/workspace/redis-vl-python/docs/user_guide/migration_backups/migration_backup_products_0a3e27b8\n    index_name:                 products\n    phase:                      completed\n    batch_size:                 100\n    dump_completed_batches:     6\n    quantize_completed_batches: 6"},{"id":"6-crash-safe-resume-and-checkpointing","title":"6. Crash-safe resume and checkpointing","role":"content","text":"If a migration is interrupted by a crash, network drop, or `Ctrl+C`, just\nre-run the same command with the same `backup_dir`. The executor loads the\nbackup header, validates that it belongs to the planned source index, and\ncontinues from the next unfinished batch.\n\nThe header is the checkpoint for single-index migrations:\n\n- `phase` shows where the previous run stopped: `dump`, `ready`, `active`, or `completed`\n- `dump_completed_batches` counts original-vector batches safely written to `.data`\n- `quantize_completed_batches` counts batches already re-encoded and written back to Redis\n\n[code example]\n\nBatch migrations use the same per-index backup headers, plus a batch state\nYAML file that records the current index, completed indexes, failed\nindexes, and the `backup_dir`. Resume rejects a different backup directory\nso the checkpoint and backup files stay together.\n\nWhen `phase` is `completed`, re-running is safe if the live index already\nmatches the target schema: the executor detects the finished backup, skips\ncompleted work, and leaves the already-created index in place. If you have\nrolled back and the live index is back on the source schema, the old\ncompleted backup is stale for a new migration run; the executor discards\nthat checkpoint and writes a fresh backup.\n\n\n[code example]\n\n    Checkpoint says 6 batch(es) were already quantized.\n    Current phase: completed\n    \n    Re-running apply with the same backup_dir to exercise resume detection...\n    [enumerate] skipped (resume from backup)\n    [drop] skipped (already dropped)\n    [quantize] skipped (already completed)\n    [create] Creating index with new schema...\n    [create] done (0.004s)\n    [index] Waiting for re-indexing...\n    [index] 600/600 docs (100%)\n    [index] done (0.001s)\n    [validate] Validating migration...\n    [validate] done (0.008s)\n    Resume result:      succeeded\n    Resume backup dir:  /Users/nitin.kanukolanu/workspace/redis-vl-python/docs/user_guide/migration_backups\n    Resume prefixes:    ['/Users/nitin.kanukolanu/workspace/redis-vl-python/docs/user_guide/migration_backups/migration_backup_products_0a3e27b8']"},{"id":"7-verify-the-quantized-index","title":"7. Verify the quantized index","role":"content","text":"The documents were preserved and the `embedding` field is now `float16`.\nWe reconnect to the live index and run a vector query (encoding the query\nvector to match the new datatype).\n\n\n[code example]\n\n    embedding datatype now: float16\n    product 0 | category: electronics | dist: 0\n    product 223 | category: books | dist: 0.0458984375\n    product 23 | category: books | dist: 0.04736328125"},{"id":"8-recover-original-vectors-from-the-backup-rollback","title":"8. Recover original vectors from the backup (rollback)","role":"content","text":"Because the backup holds the original `float32` bytes, you can recover the\npre-migration vector data. The CLI provides a one-liner:\n\n[code example]\n\nBelow is the equivalent **Python API**: iterate the backup batches and\nwrite the original bytes back with `HSET`. Rollback restores **data only**;\nafterwards recreate the original index definition so the index encoding\nmatches the restored vectors again.\n\n\n[code example]\n\n    Restored original bytes for 600 vector field(s)\n    embedding datatype after rollback: float32"},{"id":"9-build-and-apply-a-migration-with-the-wizard","title":"9. Build and apply a migration with the wizard","role":"content","text":"For exploratory work, `MigrationWizard` can build the same schema patch and\nmigration plan interactively. In a notebook, we script the answers so the\ncell can execute without blocking. The sequence below means: update a\nfield, choose `embedding`, keep the current algorithm, change datatype to\n`float16`, keep the distance metric, then finish.\n\nThe wizard still only creates the patch and plan. Applying the plan remains\na separate reviewed step, and `backup_dir` is still required.\n\n\n[code example]\n\n    Loaded 600 documents into 'wizard_products'\n    Building a migration plan for index 'wizard_products'\n    Current schema:\n    - Index name: wizard_products\n    - Storage type: hash\n      - name (text)\n      - category (tag)\n      - embedding (vector)\n    \n    Choose an action:\n    1. Add field        (text, tag, numeric, geo)\n    2. Update field     (sortable, weight, separator, vector config)\n    3. Remove field\n    4. Rename field     (rename field in all documents)\n    5. Rename index     (change index name)\n    6. Change prefix    (rename all keys)\n    7. Preview patch    (show pending changes as YAML)\n    8. Finish\n    Enter a number: 2\n    Updatable fields:\n    1. name (text)\n    2. category (tag)\n    3. embedding (vector)\n    Select a field to update by number or name: embedding\n    Current vector config for 'embedding':\n      algorithm: FLAT\n      datatype: float32\n      distance_metric: cosine\n      dims: 8 (cannot be changed)\n    \n    Leave blank to keep current value.\n      Algorithm: vector search method (FLAT=brute force, HNSW=graph, SVS-VAMANA=compressed graph)\n    Algorithm [current: FLAT]: \n      Datatype: float16, float32, bfloat16, float64, int8, uint8\n                (float16 reduces memory ~50%, int8/uint8 reduce ~75%)\n    Datatype [current: float32]: float16\n      Distance metric: how similarity is measured (cosine, l2, ip)\n    Distance metric [current: cosine]: \n    \n    Choose an action:\n    1. Add field        (text, tag, numeric, geo)\n    2. Update field     (sortable, weight, separator, vector config)\n    3. Remove field\n    4. Rename field     (rename field in all documents)\n    5. Rename index     (change index name)\n    6. Change prefix    (rename all keys)\n    7. Preview patch    (show pending changes as YAML)\n    8. Finish\n    Enter a number: 8\n    \n    Wizard patch:\n    version: 1\n    changes:\n      add_fields: []\n      remove_fields: []\n      update_fields:\n      - name: embedding\n        attrs:\n          datatype: float16\n        options: {}\n      rename_fields: []\n      index: {}\n    \n    Wizard plan mode: drop_recreate\n    Wizard warnings: ['Index downtime is required']\n\n\n\n[code example]\n\n    Wizard migration result: succeeded\n    Wizard schema match:     True\n    Wizard doc count match:  True\n    Wizard backup dir:       /Users/nitin.kanukolanu/workspace/redis-vl-python/docs/user_guide/wizard_migration_backups\n    Wizard backup prefixes:  ['/Users/nitin.kanukolanu/workspace/redis-vl-python/docs/user_guide/wizard_migration_backups/migration_backup_wizard_products_def8cdf8']\n    Wizard embedding dtype:  float16"},{"id":"10-cleanup-optional","title":"10. Cleanup (optional)","role":"content","text":"Remove the demo indexes and the artifacts this notebook created. In\nproduction, delete backups only once you are certain rollback is no longer\nneeded.\n\n\n[code example]\n\n    Cleaned up demo indexes, demo keys, backups, and YAML files"},{"id":"learn-more","title":"Learn more","role":"related","text":"- [Migrate an Index (how-to)](https://redis.io/docs/latest/migrate-indexes) -- full CLI\n  workflow, batch migration, performance tuning, and troubleshooting\n- [Index Migrations (concepts)](https://redis.io/docs/latest/../../concepts/index-migrations) -- modes,\n  supported vs blocked changes, backup internals, sync vs async\n- For very large datasets, use `num_workers > 1` and the async executor\n  (`AsyncMigrationExecutor`) to parallelize re-encoding."}],"examples":[{"id":"overview-ex0","language":"python","code":"import os\nimport glob\nimport numpy as np\nimport yaml\n\nfrom redisvl.index import SearchIndex\nfrom redisvl.redis.utils import array_to_buffer\nfrom redisvl.query import VectorQuery\n\nREDIS_URL = \"redis://localhost:6379\"\nINDEX_NAME = \"products\"\nDIMS = 8\nN_DOCS = 600\n\nnp.random.seed(42)\n\n\ndef delete_matching(client, pattern, batch_size=500):\n    deleted = 0\n    batch = []\n    for key in client.scan_iter(match=pattern, count=batch_size):\n        batch.append(key)\n        if len(batch) >= batch_size:\n            deleted += client.delete(*batch)\n            batch = []\n    if batch:\n        deleted += client.delete(*batch)\n    return deleted","section_id":"overview"},{"id":"1-create-a-source-index-with-float32-vectors-ex0","language":"python","code":"schema = {\n    \"index\": {\n        \"name\": INDEX_NAME,\n        \"prefix\": \"product\",\n        \"storage_type\": \"hash\",\n    },\n    \"fields\": [\n        {\"name\": \"name\", \"type\": \"text\"},\n        {\"name\": \"category\", \"type\": \"tag\"},\n        {\n            \"name\": \"embedding\",\n            \"type\": \"vector\",\n            \"attrs\": {\n                \"algorithm\": \"flat\",\n                \"dims\": DIMS,\n                \"distance_metric\": \"cosine\",\n                \"datatype\": \"float32\",\n            },\n        },\n    ],\n}\n\nindex = SearchIndex.from_dict(schema, redis_url=REDIS_URL)\nif index.exists():\n    index.delete(drop=True)\nstale_keys = delete_matching(index.client, \"product:*\")\nif stale_keys:\n    print(f\"Removed {stale_keys} stale demo key(s)\")\nindex.create()\n\nvectors = np.random.rand(N_DOCS, DIMS).astype(np.float32)\ndata = [\n    {\n        \"name\": f\"product {i}\",\n        \"category\": \"electronics\" if i % 2 == 0 else \"books\",\n        \"embedding\": array_to_buffer(vectors[i], dtype=\"float32\"),\n    }\n    for i in range(N_DOCS)\n]\nkeys = index.load(data)\nprint(f\"Loaded {len(keys)} documents into '{INDEX_NAME}'\")","section_id":"1-create-a-source-index-with-float32-vectors"},{"id":"1-create-a-source-index-with-float32-vectors-ex1","language":"python","code":"!rvl index listall --url redis://localhost:6379","section_id":"1-create-a-source-index-with-float32-vectors"},{"id":"2-describe-the-change-with-a-schema-patch-ex0","language":"python","code":"patch = {\n    \"version\": 1,\n    \"changes\": {\n        \"update_fields\": [\n            {\"name\": \"embedding\", \"attrs\": {\"datatype\": \"float16\"}},\n        ]\n    },\n}\n\nwith open(\"schema_patch.yaml\", \"w\") as f:\n    yaml.safe_dump(patch, f, sort_keys=False)\n\nprint(open(\"schema_patch.yaml\").read())","section_id":"2-describe-the-change-with-a-schema-patch"},{"id":"3-create-a-migration-plan-read-only-ex0","language":"python","code":"from redisvl.migration import MigrationPlanner, MigrationExecutor\nfrom redisvl.migration.utils import write_yaml\n\nplanner = MigrationPlanner()\nplan = planner.create_plan(\n    index_name=INDEX_NAME,\n    schema_patch_path=\"schema_patch.yaml\",\n    redis_url=REDIS_URL,\n)\n\nprint(\"Index:     \", plan.source.index_name)\nprint(\"Mode:      \", plan.mode)\nprint(\"Requested: \", plan.requested_changes)\nprint(\"Warnings:  \")\nfor w in plan.warnings:\n    print(\"  -\", w)\n\n# Plans can be persisted to YAML and reloaded later (or via the CLI)\nwrite_yaml(plan.model_dump(), \"migration_plan.yaml\")\nprint(\"\\nSaved plan to migration_plan.yaml\")","section_id":"3-create-a-migration-plan-read-only"},{"id":"4-apply-the-migration-with-a-mandatory-backup-ex0","language":"python","code":"BACKUP_DIR = \"./migration_backups\"\n\n# The migration does not start without a usable backup directory.\nexecutor = MigrationExecutor()\ntry:\n    executor.apply(plan, redis_url=REDIS_URL, backup_dir=None)\nexcept ValueError as exc:\n    print(\"Missing backup_dir is rejected before migration:\", exc)\n\nBAD_BACKUP_DIR = \"./not_a_backup_dir\"\nif os.path.isdir(BAD_BACKUP_DIR):\n    os.rmdir(BAD_BACKUP_DIR)\nwith open(BAD_BACKUP_DIR, \"w\") as f:\n    f.write(\"this file intentionally blocks directory creation\")\ntry:\n    executor.apply(plan, redis_url=REDIS_URL, backup_dir=BAD_BACKUP_DIR)\nexcept ValueError as exc:\n    print(\"Invalid backup_dir is rejected before migration:\", exc)\nfinally:\n    if os.path.exists(BAD_BACKUP_DIR):\n        os.remove(BAD_BACKUP_DIR)\n\n\ndef on_progress(step, detail=None):\n    print(f\"[{step}] {detail or ''}\")\n\n\nreport = executor.apply(\n    plan,\n    redis_url=REDIS_URL,\n    backup_dir=BACKUP_DIR,\n    batch_size=100,\n    num_workers=1,\n    progress_callback=on_progress,\n)\n\nprint(\"\\nResult:           \", report.result)\nprint(\"Total duration:   \", report.timings.total_migration_duration_seconds, \"s\")\nprint(\"Quantize duration:\", report.timings.quantize_duration_seconds, \"s\")\nprint(\"Schema match:     \", report.validation.schema_match)\nprint(\"Doc count match:  \", report.validation.doc_count_match)\nprint(\"Backup dir:       \", report.backup.backup_dir)\nprint(\"Backup prefixes:  \", report.backup.backup_paths)\n\nBACKUP_PREFIX = report.backup.backup_paths[0]","section_id":"4-apply-the-migration-with-a-mandatory-backup"},{"id":"5-where-is-the-backup-and-whats-in-it-ex0","language":"python","code":"for path in sorted(glob.glob(os.path.join(report.backup.backup_dir, '*'))):\n    size = os.path.getsize(path)\n    print(f\"{path}  ({size:,} bytes)\")","section_id":"5-where-is-the-backup-and-whats-in-it"},{"id":"5-where-is-the-backup-and-whats-in-it-ex1","language":"python","code":"from redisvl.migration.backup import VectorBackup\n\n# load() takes the path prefix, without .header or .data.\nbackup = VectorBackup.load(BACKUP_PREFIX)\nh = backup.header\nprint(\"backup prefix:             \", BACKUP_PREFIX)\nprint(\"index_name:                \", h.index_name)\nprint(\"phase:                     \", h.phase)\nprint(\"batch_size:                \", h.batch_size)\nprint(\"dump_completed_batches:    \", h.dump_completed_batches)\nprint(\"quantize_completed_batches:\", h.quantize_completed_batches)","section_id":"5-where-is-the-backup-and-whats-in-it"},{"id":"6-crash-safe-resume-and-checkpointing-ex0","language":"bash","code":"# Re-running the same CLI command resumes automatically:\nrvl migrate apply --plan migration_plan.yaml \\\n  --backup-dir ./migration_backups --url redis://localhost:6379","section_id":"6-crash-safe-resume-and-checkpointing"},{"id":"6-crash-safe-resume-and-checkpointing-ex1","language":"python","code":"skip = backup.header.quantize_completed_batches\nprint(f\"Checkpoint says {skip} batch(es) were already quantized.\")\nprint(f\"Current phase: {backup.header.phase}\")\n\nprint(\"\\nRe-running apply with the same backup_dir to exercise resume detection...\")\nresume_report = executor.apply(\n    plan,\n    redis_url=REDIS_URL,\n    backup_dir=BACKUP_DIR,\n    batch_size=100,\n    num_workers=1,\n    progress_callback=on_progress,\n)\nprint(\"Resume result:     \", resume_report.result)\nprint(\"Resume backup dir: \", resume_report.backup.backup_dir)\nprint(\"Resume prefixes:   \", resume_report.backup.backup_paths)","section_id":"6-crash-safe-resume-and-checkpointing"},{"id":"7-verify-the-quantized-index-ex0","language":"python","code":"restored = SearchIndex.from_existing(INDEX_NAME, redis_url=REDIS_URL)\nemb = next(f for f in restored.schema.to_dict()['fields'] if f['name'] == 'embedding')\nprint(\"embedding datatype now:\", emb['attrs']['datatype'])\n\nq = VectorQuery(\n    vector=vectors[0].tolist(),\n    vector_field_name=\"embedding\",\n    return_fields=[\"name\", \"category\"],\n    dtype=\"float16\",\n    num_results=3,\n)\nfor r in restored.query(q):\n    print(r[\"name\"], \"| category:\", r[\"category\"], \"| dist:\", r[\"vector_distance\"])","section_id":"7-verify-the-quantized-index"},{"id":"8-recover-original-vectors-from-the-backup-rollback-ex0","language":"bash","code":"rvl migrate rollback --backup-dir ./migration_backups \\\n  --index products --url redis://localhost:6379","section_id":"8-recover-original-vectors-from-the-backup-rollback"},{"id":"8-recover-original-vectors-from-the-backup-rollback-ex1","language":"python","code":"client = restored.client\n\nrestored_count = 0\nfor batch_keys, originals in backup.iter_batches():\n    pipe = client.pipeline(transaction=False)\n    for key in batch_keys:\n        if key in originals:\n            for field_name, original_bytes in originals[key].items():\n                pipe.hset(key, field_name, original_bytes)\n                restored_count += 1\n    pipe.execute()\n\nprint(f\"Restored original bytes for {restored_count} vector field(s)\")\n\n# Recreate the ORIGINAL float32 index definition over the restored data\noriginal_index = SearchIndex.from_dict(schema, redis_url=REDIS_URL)\noriginal_index.create(overwrite=True, drop=False)\n\ncheck = SearchIndex.from_existing(INDEX_NAME, redis_url=REDIS_URL)\nemb = next(f for f in check.schema.to_dict()['fields'] if f['name'] == 'embedding')\nprint(\"embedding datatype after rollback:\", emb['attrs']['datatype'])","section_id":"8-recover-original-vectors-from-the-backup-rollback"},{"id":"9-build-and-apply-a-migration-with-the-wizard-ex0","language":"python","code":"import builtins\nimport copy\nfrom contextlib import contextmanager\n\nfrom redisvl.migration import MigrationWizard\nfrom redisvl.migration.utils import wait_for_index_ready\n\nWIZARD_INDEX_NAME = \"wizard_products\"\nWIZARD_PREFIX = \"wizard_product\"\nWIZARD_PATCH_PATH = \"wizard_schema_patch.yaml\"\nWIZARD_PLAN_PATH = \"wizard_migration_plan.yaml\"\nWIZARD_TARGET_SCHEMA_PATH = \"wizard_target_schema.yaml\"\nWIZARD_BACKUP_DIR = \"./wizard_migration_backups\"\n\nwizard_schema = copy.deepcopy(schema)\nwizard_schema[\"index\"][\"name\"] = WIZARD_INDEX_NAME\nwizard_schema[\"index\"][\"prefix\"] = WIZARD_PREFIX\n\n# Start from a clean wizard demo index and keyspace.\ntry:\n    existing_wizard_index = SearchIndex.from_existing(\n        WIZARD_INDEX_NAME, redis_url=REDIS_URL\n    )\n    existing_wizard_index.delete(drop=True)\nexcept Exception:\n    pass\ndelete_matching(client, f\"{WIZARD_PREFIX}:*\")\n\nwizard_index = SearchIndex.from_dict(wizard_schema, redis_url=REDIS_URL)\nwizard_index.create()\nwizard_index.load(data, id_field=None)\nwait_for_index_ready(wizard_index)\nprint(f\"Loaded {N_DOCS} documents into '{WIZARD_INDEX_NAME}'\")\n\n\n@contextmanager\ndef scripted_inputs(answers):\n    original_input = builtins.input\n    iterator = iter(answers)\n\n    def fake_input(prompt=\"\"):\n        answer = next(iterator)\n        print(f\"{prompt}{answer}\")\n        return answer\n\n    builtins.input = fake_input\n    try:\n        yield\n    finally:\n        builtins.input = original_input\n\n\nwizard_answers = [\n    \"2\",          # Update field\n    \"embedding\",  # Select the vector field\n    \"\",           # Keep algorithm\n    \"float16\",    # Quantize datatype\n    \"\",           # Keep distance metric\n    \"8\",          # Finish\n]\n\nwith scripted_inputs(wizard_answers):\n    wizard_plan = MigrationWizard().run(\n        index_name=WIZARD_INDEX_NAME,\n        redis_url=REDIS_URL,\n        plan_out=WIZARD_PLAN_PATH,\n        patch_out=WIZARD_PATCH_PATH,\n        target_schema_out=WIZARD_TARGET_SCHEMA_PATH,\n    )\n\nprint(\"\\nWizard patch:\")\nprint(open(WIZARD_PATCH_PATH).read())\nprint(\"Wizard plan mode:\", wizard_plan.mode)\nprint(\"Wizard warnings:\", wizard_plan.warnings)","section_id":"9-build-and-apply-a-migration-with-the-wizard"},{"id":"9-build-and-apply-a-migration-with-the-wizard-ex1","language":"python","code":"wizard_report = MigrationExecutor().apply(\n    wizard_plan,\n    redis_url=REDIS_URL,\n    backup_dir=WIZARD_BACKUP_DIR,\n    batch_size=100,\n    num_workers=1,\n)\n\nwizard_live = SearchIndex.from_existing(WIZARD_INDEX_NAME, redis_url=REDIS_URL)\nwizard_embedding = next(\n    f for f in wizard_live.schema.to_dict()[\"fields\"] if f[\"name\"] == \"embedding\"\n)\n\nprint(\"Wizard migration result:\", wizard_report.result)\nprint(\"Wizard schema match:    \", wizard_report.validation.schema_match)\nprint(\"Wizard doc count match: \", wizard_report.validation.doc_count_match)\nprint(\"Wizard backup dir:      \", wizard_report.backup.backup_dir)\nprint(\"Wizard backup prefixes: \", wizard_report.backup.backup_paths)\nprint(\"Wizard embedding dtype: \", wizard_embedding[\"attrs\"][\"datatype\"])","section_id":"9-build-and-apply-a-migration-with-the-wizard"},{"id":"10-cleanup-optional-ex0","language":"python","code":"delete_matching(client, \"product:*\")\nif check.exists():\n    check.delete(drop=False)\n\ndelete_matching(client, f\"{WIZARD_PREFIX}:*\")\nif wizard_live.exists():\n    wizard_live.delete(drop=False)\n\nfor backup_dir in (report.backup.backup_dir, wizard_report.backup.backup_dir):\n    for f in glob.glob(os.path.join(backup_dir, '*')):\n        os.remove(f)\n    if os.path.isdir(backup_dir):\n        os.rmdir(backup_dir)\n\nfor f in (\n    \"schema_patch.yaml\",\n    \"migration_plan.yaml\",\n    \"not_a_backup_dir\",\n    WIZARD_PATCH_PATH,\n    WIZARD_PLAN_PATH,\n    WIZARD_TARGET_SCHEMA_PATH,\n):\n    if os.path.exists(f):\n        os.remove(f)\nprint(\"Cleaned up demo indexes, demo keys, backups, and YAML files\")","section_id":"10-cleanup-optional"}]}
{"schema_version":2,"id":"develop/ai/redisvl/user_guide/how_to_guides/langcache_semantic_cache","title":"Use LangCache as the LLM Cache Backend","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/how_to_guides/langcache_semantic_cache/","summary":"","aliases":["/integrate/redisvl/user_guide/how_to_guides/13_langcache_semantic_cache","/develop/ai/redisvl/user_guide/langcache_semantic_cache/"],"tags":[],"last_updated":"2026-10-01T14:49:09Z","page_type":"content","content_hash":"016290cbff918d970e86efa9cc9c18bbe9011dc199da1ededfabe3a32523dc07","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This guide shows how to use RedisVL's `LangCacheSemanticCache`, a thin wrapper around the [LangCache](https://redis.io/langcache/) managed semantic cache service. You get the same high-level `check` / `store` workflow as `SemanticCache`, backed by LangCache's HTTP API instead of a Redis index you manage yourself.\n\nFor more on semantic caching, see [Extensions](https://redis.io/docs/latest/../../concepts/extensions), and to use RedisVL's semantic caching class see our [llm cache notebook](https://redis.io/docs/latest/llmcache). API entries for both classes live in the [LLM cache API](https://redis.io/docs/latest/../../api/cache)."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before you begin, ensure you have:\n- Installed RedisVL with the LangCache extra: `pip install redisvl[langcache]`\n- Python 3.10+ (same as RedisVL)\n- A LangCache service with a **cache ID** and **API key**. You can set up a LangCache service in Redis Cloud [here](https://cloud.redis.io/#/)\n- Optionally: **attributes** configured on your LangCache cache if you plan to pass `metadata` / `attributes` from RedisVL"},{"id":"what-youll-learn","title":"What You'll Learn","role":"content","text":"By the end of this guide, you will be able to:\n- Choose between `SemanticCache` and `LangCacheSemanticCache` for your deployment\n- Initialize `LangCacheSemanticCache` with credentials and TTL defaults\n- Implement read-through caching (`check` → LLM → `store`)\n- Use LangCache attributes for scoping and deletion\n- Override TTL per store, use async APIs, and run delete operations\n- Understand current limitations compared to `SemanticCache`"},{"id":"choose-semanticcache-or-langcachesemanticcache","title":"Choose `SemanticCache` or `LangCacheSemanticCache`","role":"content","text":"| | `SemanticCache` | `LangCacheSemanticCache` |\n|---|------------------|--------------------------|\n| **Where data lives** | Your Redis deployment; RedisVL creates and queries a search index | LangCache managed service (hosted API) |\n| **Best when** | You control Redis, need full RedisVL query/filter features, or co-locate cache with app data | You want a managed semantic cache without operating Redis or the index |\n| **Vector search by raw embedding** | Supported (`vector=` on `check`) | **Not supported** — search is prompt-based via the LangCache API |\n| **Filter expressions** | `FilterExpression` on `check` | **Not supported** — use LangCache **attributes** (pre-configured on the cache) |\n| **Partial entry updates** | Supported where the backend allows | **`update` / `aupdate` raise** — delete and re-store instead |\n\n**Note:** `SemanticCache` is covered in depth in the [llmcache notebook](https://redis.io/docs/latest/llmcache) guide."},{"id":"install-the-langcache-extra","title":"Install the LangCache extra","role":"setup","text":"The `redisvl[langcache]` extra installs compatible `langcache` dependencies:\n\n[code example]\n\n\n\n[code example]"},{"id":"initialize-langcachesemanticcache","title":"Initialize `LangCacheSemanticCache`","role":"content","text":"Create `LangCacheSemanticCache` with your LangCache credentials. The default `server_url` points at the managed LangCache API; override it if your provider gives a different endpoint.\n\nThe following example reads credentials from environment variables (recommended for applications). Replace placeholder values when experimenting locally.\n\n\n\n[code example]\n\n| Parameter | Purpose |\n|-----------|---------|\n| `cache_id`, `api_key` | Required. Identify your LangCache cache and authenticate. |\n| `server_url` | LangCache API base URL (default matches typical managed deployments). |\n| `ttl` | Default time-to-live for stored entries, in seconds; can be overridden per `store` call. |\n| `use_exact_search` / `use_semantic_search` | Enable exact and/or semantic matching (at least one must be `True`). |\n| `distance_threshold` (on `check`) | Works with `distance_scale`: `\"normalized\"` (0–1 distance) or `\"redis\"` (cosine-style 0–2). |"},{"id":"attributes-and-metadata","title":"Attributes and metadata","role":"content","text":"LangCache **attributes** are key/value metadata attached to entries. They can be used when **searching** (`check` / `acheck` via the `attributes` argument) and when **deleting** (`delete_by_attributes` / `adelete_by_attributes`).\n\n**You must define the same attribute names (and types) in the LangCache console or API for your cache before RedisVL can use them.** If you pass `metadata` to `store` or `attributes` to `check` but the cache has no attributes configured, the LangCache API returns an error; RedisVL surfaces a clear `RuntimeError` explaining that attributes need to be configured or removed from the call.\n\nString values are encoded for the API and decoded when reading hits so special characters remain usable."},{"id":"read-through-caching-pattern","title":"Read-through caching pattern","role":"content","text":"Typical flow: try `check`, call the LLM on a miss, then `store` the result.\n\n\n\n[code example]\n\nOptional scoping with attributes (only if those attributes are configured on LangCache):\n\n\n\n[code example]"},{"id":"ttl","title":"TTL","role":"content","text":"- **Constructor** `ttl=` sets the default lifetime for new entries (seconds).\n- **Per call**, pass `ttl=` to `store` / `astore` to override the default for that entry.\n\n\n\n[code example]"},{"id":"async-usage","title":"Async usage","role":"content","text":"Use the `a`-prefixed methods with `asyncio` — for example `acheck`, `astore`, `adelete`, `adelete_by_id`, `adelete_by_attributes`, `aclear`.\n\n\n\n[code example]"},{"id":"delete-operations","title":"Delete operations","role":"content","text":"| Method | What it does |\n|--------|----------------|\n| `delete()` / `adelete()` | **Flush** the entire cache (all entries). Aliases: `clear()` / `aclear()`. |\n| `delete_by_id(entry_id)` / `adelete_by_id` | Remove one entry by LangCache entry ID (returned from `store`). |\n| `delete_by_attributes` / `adelete_by_attributes` | Remove entries matching the given attribute map (non-empty dict required). |"},{"id":"current-limitations","title":"Current limitations","role":"content","text":"The wrapper follows the LangCache API. The following RedisVL features either do not apply or are explicitly unsupported:\n\n- **No direct vector search** — Passing `vector=` to `check` / `acheck` logs a warning and does not search by embedding.\n- **No `filter_expression`** — RedisVL filter expressions are not translated; use LangCache attributes only.\n- **No `update()` / `aupdate()`** — The LangCache API does not update individual entries; these methods raise `NotImplementedError`. Delete the entry (or store a new pair) instead.\n- **`filters` on `store`** — Not supported by LangCache; a warning is logged if provided.\n\n**Tip:** See the **LangCacheSemanticCache** section in the [LLM cache API](https://redis.io/docs/latest/../../api/cache) for parameter and method listings."}],"examples":[{"id":"install-the-langcache-extra-ex0","language":"bash","code":"pip install redisvl[langcache]","section_id":"install-the-langcache-extra"},{"id":"install-the-langcache-extra-ex1","language":"python","code":"# NBVAL_SKIP\n%pip install redisvl[langcache]","section_id":"install-the-langcache-extra"},{"id":"initialize-langcachesemanticcache-ex0","language":"python","code":"# NBVAL_SKIP\nimport os\n\nfrom redisvl.extensions.cache.llm import LangCacheSemanticCache\n\nCACHE_ID = os.environ.get(\"LANGCACHE_CACHE_ID\", \"YOUR_CACHE_ID\")\nAPI_KEY = os.environ.get(\"LANGCACHE_API_KEY\", \"YOUR_API_KEY\")\n\ncache = LangCacheSemanticCache(\n    name=\"my_app_cache\",\n    server_url=\"https://aws-us-east-1.langcache.redis.io\",\n    cache_id=CACHE_ID,\n    api_key=API_KEY,\n    ttl=3600,  # default TTL for entries, in seconds (optional)\n)","section_id":"initialize-langcachesemanticcache"},{"id":"read-through-caching-pattern-ex0","language":"python","code":"# NBVAL_SKIP\ndef call_your_llm(prompt: str) -> str:\n    \"\"\"Replace with your LLM client (OpenAI, Anthropic, etc.).\"\"\"\n    return f\"Answer for: {prompt}\"\n\n\ndef answer(user_prompt: str) -> str:\n    hits = cache.check(prompt=user_prompt, num_results=1)\n    if hits:\n        return hits[0][\"response\"]\n\n    response = call_your_llm(user_prompt)\n    cache.store(prompt=user_prompt, response=response)\n    return response","section_id":"read-through-caching-pattern"},{"id":"read-through-caching-pattern-ex1","language":"python","code":"# NBVAL_SKIP\nuser_prompt = \"Example prompt\"\n\nhits = cache.check(\n    prompt=user_prompt,\n    attributes={\"tenant_id\": \"acme\", \"model\": \"gpt-4o\"},\n    num_results=1,\n)","section_id":"read-through-caching-pattern"},{"id":"ttl-ex0","language":"python","code":"# NBVAL_SKIP\nprompt = \"What is Redis?\"\nresponse = \"Redis is an in memory data store.\"\n\ncache.store(prompt=prompt, response=response, ttl=300)  # this entry expires in 5 minutes","section_id":"ttl"},{"id":"async-usage-ex0","language":"python","code":"# NBVAL_SKIP\nasync def call_your_llm_async(prompt: str) -> str:\n    return f\"Async answer for: {prompt}\"\n\n\nasync def answer_async(user_prompt: str) -> str:\n    hits = await cache.acheck(prompt=user_prompt, num_results=1)\n    if hits:\n        return hits[0][\"response\"]\n\n    response = await call_your_llm_async(user_prompt)\n    await cache.astore(prompt=user_prompt, response=response)\n    return response","section_id":"async-usage"}]}
{"schema_version":2,"id":"develop/ai/redisvl/user_guide/how_to_guides/llmcache","title":"Cache LLM Responses","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/how_to_guides/llmcache/","summary":"","aliases":["/integrate/redisvl/user_guide/how_to_guides/03_llmcache","/integrate/redisvl/user_guide/llmcache/","/develop/ai/redisvl/user_guide/llmcache/"],"tags":[],"last_updated":"2026-10-01T14:49:09Z","page_type":"content","content_hash":"c223fbb9d5117e3a6a258a29a1b47bdce694e3146f7559182ece5cd365400a9a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This guide demonstrates how to use RedisVL's `SemanticCache` to cache LLM responses based on semantic similarity. Semantic caching reduces API costs and latency by retrieving cached responses for semantically similar prompts instead of making redundant API calls."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before you begin, ensure you have:\n- Installed RedisVL: `pip install redisvl`\n- A running Redis instance ([Redis 8+](https://redis.io/downloads/) or [Redis Cloud](https://redis.io/cloud))\n- An OpenAI API key for the examples"},{"id":"what-youll-learn","title":"What You'll Learn","role":"content","text":"By the end of this guide, you will be able to:\n- Set up and configure a `SemanticCache`\n- Store and retrieve cached LLM responses\n- Understand entry IDs and keys for fetching and deleting specific entries\n- Customize semantic similarity thresholds\n- Configure TTL policies and understand TTL refresh behavior\n- Implement access controls with tags and filters for multi-user scenarios\n\nFirst, import [OpenAI](https://platform.openai.com) to use their API for responding to user prompts. The following code creates a simple `ask_openai` helper method to assist.\n\n\n[code example]\n\n\n[code example]\n\n    Paris."},{"id":"initializing-semanticcache","title":"Initializing ``SemanticCache``","role":"content","text":"``SemanticCache`` will automatically create an index within Redis upon initialization for the semantic cache content.\n\n\n[code example]\n\n    You try to use a model that was created with version 4.1.0, however, your version is 3.4.1. This might cause unexpected behavior or errors. In that case, try to update to the latest version.\n    \n    \n    \n\n\n\n[code example]\n\n    \n    \n    Index Information:\n    ╭───────────────┬───────────────┬───────────────┬───────────────┬───────────────╮\n    │ Index Name    │ Storage Type  │ Prefixes      │ Index Options │ Indexing      │\n    ├───────────────┼───────────────┼───────────────┼───────────────┼───────────────┤\n    | llmcache      | HASH          | ['llmcache']  | []            | 0             |\n    ╰───────────────┴───────────────┴───────────────┴───────────────┴───────────────╯\n    Index Fields:\n    ╭─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────╮\n    │ Name            │ Attribute       │ Type            │ Field Option    │ Option Value    │ Field Option    │ Option Value    │ Field Option    │ Option Value    │ Field Option    │ Option Value    │\n    ├─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┤\n    │ prompt          │ prompt          │ TEXT            │ WEIGHT          │ 1               │                 │                 │                 │                 │                 │                 │\n    │ response        │ response        │ TEXT            │ WEIGHT          │ 1               │                 │                 │                 │                 │                 │                 │\n    │ inserted_at     │ inserted_at     │ NUMERIC         │                 │                 │                 │                 │                 │                 │                 │                 │\n    │ updated_at      │ updated_at      │ NUMERIC         │                 │                 │                 │                 │                 │                 │                 │                 │\n    │ prompt_vector   │ prompt_vector   │ VECTOR          │ algorithm       │ FLAT            │ data_type       │ FLOAT32         │ dim             │ 768             │ distance_metric │ COSINE          │\n    ╰─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────╯"},{"id":"basic-cache-usage","title":"Basic Cache Usage","role":"content","text":"[code example]\n\n\n[code example]\n\n    Empty cache\n\n\nOur initial cache check should be empty since we have not yet stored anything in the cache. Below, store the `question`,\nproper `response`, and any arbitrary `metadata` (as a python dictionary object) in the cache.\n\n\n[code example]\n\n\n\n\n    'llmcache:115049a298532be2f181edb03f766770c0db84c22aff39003fec340deaec7545'\n\n\n\nNow check the cache again with the same question and with a semantically similar question:\n\n\n[code example]\n\n    [{'prompt': 'What is the capital of France?', 'response': 'Paris', 'metadata': {'city': 'Paris', 'country': 'france'}, 'key': 'llmcache:115049a298532be2f181edb03f766770c0db84c22aff39003fec340deaec7545'}]\n\n\n\n[code example]\n\n\n\n\n    'Paris'"},{"id":"entry-ids-and-keys","title":"Entry IDs and Keys","role":"content","text":"Each cache entry has two identifiers:\n\n- **`entry_id`**: A deterministic hash of the prompt + filters. Used to identify the entry within the cache.\n- **`key`**: The full Redis key (prefix + entry_id). Used for direct Redis operations.\n\nThe `entry_id` is generated as a SHA256 hash of the prompt and any filters, meaning:\n- Same prompt + same filters = same `entry_id` (overwrites previous entry)\n- Same prompt + different filters = different `entry_id` (both stored)\n\n\n[code example]\n\n    Full Redis key: llmcache:115049a298532be2f181edb03f766770c0db84c22aff39003fec340deaec7545\n\n\n\n[code example]\n\n    Entry ID: 115049a298532be2f181edb03f766770c0db84c22aff39003fec340deaec7545\n    Key: llmcache:115049a298532be2f181edb03f766770c0db84c22aff39003fec340deaec7545"},{"id":"fetch-and-delete-specific-entries","title":"Fetch and Delete Specific Entries","role":"content","text":"You can fetch or delete specific cache entries using the underlying index. First, let's see what keys exist in the cache:\n\n\n[code example]\n\n    Found 1 entries in cache:\n      - entry_id: 115049a298532be2f181... prompt: What is the capital of France?...\n\n\n\n[code example]\n\n    Fetched record: {'metadata': '{\"source\": \"geography\"}', 'entry_id': '115049a298532be2f181edb03f766770c0db84c22aff39003fec340deaec7545', 'inserted_at': '1776288885.186736', 'updated_at': '1776288885.1867368', 'prompt': 'What is the capital of France?', 'response': 'Paris', 'prompt_vector': b'\\xa15\\x06@\\x1c\\x10\\xba\\xbf$\\xa28\\xbf[\\x8aU\\xc0\\x02E\\x89?q\\x825\\xbd\\xca\\xc1\\xf1\\xbf\\x186\\xfb\\xbe\\xb5\\n\\x12\\xc0\\x9es\\x83\\xc0\\xe2\\'\\'?\\xb7T\\xfc\\xbf\\x9b\\xef\\xa1\\xbf\\x0bo\\xae\\xbf\"\\xebG>-\\x16\\x81\\xbf\\x8e&\\x13\\xbf\\xb2l_\\xbf\\x8b\\xf9>?\\x96\\xdf(\\xbf:7\\x99\\xbf\\xf7\\xbd\\x8f\\xbfLoQ\\xbf\\x89\\xb2M?c\\xea\\x16\\xc0s\\x1e\\xdc\\xbe\\xf2fx\\xbf\\x8b\\xa5B>\\xfaM\\xef\\xbf9\\xdd$?(\\xa1=\\xbf\\x1a\\xd0\\x99\\xbf[dd?\\x04\\x9c\\xc9>\\xff\\xf9\\x0f\\xbe0\\x90X\\xber[H\\xc0=.\\xb1=\\x0bO\\x90\\xbd\\xba\\xaa\\x81\\xbe\\xca<A;\\xdb\\x8ev@.\\'0@$\\x08\\xdf?\\xe9\\x94\\x1c\\xc0\\xce\\x89\\xf9>\\xfd\\x04\\xb4\\xbf\\xb6)U\\xbf#h\\xea?\\xef\\xba\\x1b?\\xb9e\\x04\\xbe\\x0fb\\x0e@\\xdc\\xbdL?\\xb8i\\xb5?\\xd8\\xf0u\\xbf\\xf3g\\x9b>\\n\\xc2\\x83\\xbf\\xe6\\xaa\\x83\\xc0\\xc1\\xf7\\'?!\\xe1\\xd8\\xbe\\xc3\\xd3\\'\\xc0)\\x9c\\x90?xx\\xd3\\xbe\\xa5\\x9dz?\\xf8\\x10\\x9f?5\\x99\\xc5?\\x03\\xd5\\x9f?\\xf2\\xc0B?\\x08\\x9f\\xb1>oW|?\\xa0\\xa4e@\\xe9\\xed\\x90?\\xfcf\\xe9>\\xe8\\xe9\\xe8\\xbf\\xc5\\x04T\\xbf\\x9du\\xa6\\xbe\\x14\\xa2\\x98\\xbf#\\xb7+?\\xe9\\x8a`\\xc0\\x18uU?\\xbd\\x1a<@2~\\xbf\\xbf&1\\xa4?8M\\x19@\\xb0\\x883?\\x94\\xf1\\x84\\xc0\\xea=\\xd8=^\\x1b\\xdc\\xbe\\x1e\\xbd\\x14\\xc0\\x10x\\x87?\\xde\\xe5\\xf7>;[\\xee=N\\xf1\"?\\xda\\x8ci\\xbf\\xed\\xdb\\x07\\xbe\\x12 Z\\xc0i\\xfb\\x03\\xbd\\x9f\\x82\\xea>\\x9b=\\x0f\\xc0\\xe6D\\xbf\\xbe\\xf7\\x91\\xb9\\xbfU)(\\xc0\\xdbP\\x92\\xbd\\xf6\\xd6\\x86\\xbfoD\\xe0>\\x1a9\\x01@\\x9c?\\x1a\\xbf\\x9a\\xc9\\n\\xc0gd\\'?\\xcdqr?3%<\\xbf\\x8f\\xe8\\xc2\\xbe\\xbe\\xae\\x9d\\xbf\\xc4\\x0e+\\xbf-e\\x02@4+\\x9a\\xbf\\xa5\\x88\\xe7?W\\xe9K\\xbe\\xd7;Y?\\xd9\\x8a\\x94?\\r\\xaf\\xc2?*\\xd1\\xa6?\\xb7\\x82\\x00\\xc0\\xb2\\x8d\\xbf>X\\xd2\\x8f?\\x1c\\xe7\\xcf?A\\x89J?\\xcem\\xe4\\xbfP2\\x15\\xbd\\xc3I9\\xbe)\\x14H?\\xbe\\x87\\x87\\xbe\\xb6\\xcb\\xa2?Un\\xc4\\xbf\\xb2\\xaf^>\\x1b%\\xd6\\xbe\\x1e\\tW?\\xcf\\x82\\x0f@\\xa5K\\xc8\\xbeD\\xe1\\x90=\\xfd\\xa4\\x12\\xbf\\x05\\xcb>\\xbe\\xd3\\xcf\\x08\\xbf\\xe4}\\xc4\\xbev\\x02\\x92\\xbe\\x07\\xc7\\x19\\xbc\\xd5\\x9b\\xd9?\\xdev\\x03@\\x9a\\xbff\\xbe\\xdd\\x94\\x8f>Y\\xad\\xd8\\xbe\\x9f\\xcf$\\xbf0\\x0cz\\xbe\\x1d\\xfd\\x96\\xbf\\x7f?n\\xbfh_\\x12\\xc0\\x02v\\xdd\\xbf^E\\xa4>\\xda{\\xe3?l\\xa5\\xab\\xbe\\xad.\\x12@T\\xed>\\xbf]\\xe9Y\\xbf\\xc5\\xad\\xca\\xbd\\xc8\\x01\\x1e@\\x0f\\xbf=\\xbf\\x8b\\xe2\\xa0>Pf\\x98\\xbd@>\\x00\\xbe\\xb6\\xe5g\\xbf\\xec~\\xa9@\\xb6\\x10L\\xc0Q\\x1fA?\\xeb!\\xa6\\xbdf^u?\\xb6\\xabi\\xbfm\\x02\\x11\\xc0\\xa3\\x0e\\xc7?\\x9c\\xd6%\\xc0\\x9d\\x85\\x94\\xbf\\x04\\xa98>\\x9a\\xe8_\\xbe\\x86\\xbdr\\xbf\\xa9p\\x8e\\xbf6G0\\xbb\\xf1\\xc9\\xa3?\\xe36$\\xbf\\x04O\\x05\\xc0r\\xf2\\xa8>\\x9a\\xe3\\x14?\\x07M\\xc8<S>\\xe8\\xbfR@\\x13<\\xfaZ\\xe0?l\\x00\\xde\\xbf\\xfc\\xa5\\x03?\\xd0\\xe5.\\xc03m\\xdc\\xbf\\x9f\\xa7\\x90?eB\\x97\\xbeqE\\x04@\\xfc\\x07<?\\xe9\\xbe3\\xc0\\x1eG\\xa8?\\xd7\\xf9\\xd8\\xbfvmi?J\\xe4g\\xbfj:\\xa3?\\xcb\\x16\\xd1\\xbfP\\x04\\xee?/e\\x9e>\\x8cU\\x98?\\xa8s\\xc6=\\xf5/\\xc1?\\x0fl\\x80\\xc0d\\xfa0\\xbf\\x81#\\xf0?z4\\x03\\xbf\\x95\\x88l\\xbf\\xbf\\xbe\\'@$\\x9c\\xd5?\\x82\\n)\\xbfxd\\x17\\xc0\\x02EQ?\\x83\\xc7\\x83\\xbf\\x8f\\xcc\\x84\\xbe\\xa9\\xa0Q\\xbf:;\\xf9?\\xdc>\\x17\\xbf\\x02$\\xe1?\\xf4\\x8e\\x1b\\xbf\\x1c\\x19:\\xc0\\x81\\xd4k?#\\x83\\x85?\\x9a\\x8c\\xdd?\\xa3\\x10\\xa9\\xbe\\xc69\\xff>s\\x81T=T\\xf7\\x85\\xbfF4\\xc6\\xbe\\x10\\xf5\\x1c@vf\\x93\\xbf\\xb4\\xa8\\x8d?\\xd5>\\x1f\\xc0\\xe3\\xc0k\\xbf/WA\\xbf89\\xcd?\\xe1[\\x94?\\x81$\\xa4\\xbe&\\'\\xd6\\xbf,:[@\\xad/\\xd2\\xbf\\xba\\x17\\x86?\\xc4\\xe2\\x01\\xc0\\xa0\\xf9\\xf0\\xbe]\\x03\\xec\\xbdv\\xe3\\x0f@\\xdd\\xfb\\x13@\\xdc\\xe2\\x8a\\xc0\\x88c]?\\xb4\\xb4K\\xbf\\xd69\\x1a?.\\xc1\\xe1\\xbf\\xaaNd?\\tJE@\\x9bB\\t\\xc0\\xa7,\\x95;\\xa5\\xde\\x0b\\xbe\\x8a9)>;\\xd7\\xc4\\xbf\\x11\\xe7\\x0e?8\\x9c\\x9d\\xbd}\\xfd\\x0c\\xc0G\\xa1\\x7f?\\xc7\\x14\\x19?\\x8bi\\xc1?\\x88\\xfaS?\\x8dg\\x19@\\x16\\xf9\\x9e?\\x7fa0\\xbf\\x96\\x0c\\xe8>\\xec\\x03\\xc9?Z>\\xf0>\\xcf\\xaa\\xda\\xbf1WI\\xbed\\xc9\\x9c\\xbf$L\\x13?:\\x02\\x9b?\\t\\xe9G?\\xf8\\x10\\x07@1\\xe1\\xab>\\xca\\xa6\\xdc\\xbf\\x1b0\\xac\\xbf\\xb9\\xb7\\xd2?l.\\xbd>\\x92\\xa0\\xf4\\xbf!W\\x03\\xbf\\x88l\\xdd>o\\xa1-@\\xc7kb?b\\x0e\\xfd>\\x12\\xb5\\xb7\\xbf\\x02,\\xfa?,\\x84t?\"\\xa3\\x0e\\xbfT\\xb7\\xd6?\\x89\\xb9w\\xbe`#\\xd4=\\xfcC=@\\x8b\\xa68@\\xe8\\x88f\\xbfV\\x0c\\xd7\\xbe\\x12\\xe8\\x0b?=A\\x82\\xbf\\x83\\x15\\xa9?ogq\\xbf\\xa1<\\xca\\xbe\\x8e\\n\\x13\\xc0\\xbe\\x95\\xce\\xbd\\x075!\\xc09\\x8a\\xdd?\\xec\\xce\\x9b?\\xfd\\x8f3\\xbf\\xf6\\xc8\\xf2?rO\\x0e@\\x18\\x0e\\xd0?\\x00u\\'\\xbf\\x8f\\xca\\x90\\xbe5C\\x11\\xbf\\x1b\\xe1\\xcb?Z\\x94\\x96\\xbfc!\\r\\xc0H\\x83\\t\\xc0\\x90\\xf6\\x1c\\xc0\\x84M\\xf1\\xbf\\x0e\\x0b\\x0c\\xbf\\xd0\\xe1\"\\xbe\\n8?\\xc0\\x8ad\\x1a?\\x9a\\xc4\\xbb>\\xee\\x99\\xad\\xbf{z\\xbc\\xbf\\xcc\\xfb\\x03\\xbe\\xffI~\\xbd\\xd6d\\x82>q\\x0c\\xd5?\\x17\\x0b\\x08\\xc0\\x07|C@\\xb80{>\\xd9+\\x1c>\\x11d\\x12\\xc0?%,\\xc0\\xd1 \\x9b\\xbf\\x82\\x84S?XE\\x90?\\xba\\xc52?;l\\x06@Z\\xfd\\x80?\\x1a\\x97\\xac?Fn\\xcc\\xbf\\xf4I\\xc1?\\x9b\\xa7\\xdf?\\xfeOs\\xbf\\xacN\\xa6=B\\xfe#==\\xfe\"?\\xa8\\\\\\x1d\\xbf\\xbd(\\xf0?\\xa1~<\\xbe\\xfd\\xda\\x8f>\\x83\\x19\\xab\\xbemi\\xcc:\\x0e\\xbc\\xba\\xbe\\xe3e\\xef?\\xdag\\x8d?\\x82\\xf1\\xfe\\xbf\\xcb\\xf4\\x06\\xbd6CD@\\xfeqg\\xbf\\xce\\xc0\\x96?\\x89 M?\\x9b)\\x87\\xbe\\x934(\\xc0W\\xba\\xa0\\xbe\\xeb\\x84s>\\x1fi\\xfc?\\xfc\\x0fs\\xbf\\xd9\\xfd\\x98?Y\\xb7\\x9e>\\x18\\xfe\\x00\\xbe^)\\n?\\xa8\\xb4\\xff\\xbe\\xec\\xde)=Y\\x9b\\x89@M\\x9a\\xb5\\xbe\\xef\\xa2G?\\xf3\\xa3\\x8b\\xbf[\\xf9\\x8b\\xbfw\\xb5\\x86?\\xcb\\x16\\x04\\xc04\\xd6\\xa3\\xbf\\xb09\\x9a?}\\xd5!@\\xdb\\xc5\\x87?\\xcf(\\x86?\\xaa\\x80\\x1e\\xc0\\x14&\\xa6\\xbf\\xc18\\x11?\\x88IH\\xbf\\x82CH?\\xb6G\\x17@9\\xa9&\\xbe#Y\\xc8>\\xa5\\xc9\\x0e\\xc0\\x15\\x93*?\\x97\\xe5\\x02?\\xf0\\xcaW\\xbf\\xfeh&\\xc0C\\x7f\\r@\\x0c\\xf0Z\\xbf\\x91DM@\\nzm?R\\xc4\\xb6<\\x7fv\\xd4\\xbfA\\x96\\x91\\xbeg\\xd3\\x8e>\\xd8\\xd3!?\\xf1\\x838\\xc0\\xf9^\\xbf?|@{>x\\xfc2@}/\\x99?\\x96hj\\xbf\\x86\\xd2\\xbe?3\\x10m\\xc0\\x0f\\xe9\\xcb?\\xc6\\xc2\\x9c?\\xb2\\xf4\\x9d?-\\xa6A\\xbf*\\xe5\\xed?D\\xf01=\\xb2\\xd5\\xd9\\xbf\\t\\x00\\n\\xc0\\xca\\xecb>f\\xb6r\\xc0\\xa4\\'\\xe7\\xbf\\xea\\x00\\xa0\\xbfD\\xa03?9\\xf6\\x04@\\x1b\\x07\\xbd?\\x18^\\x8a\\xbf[\\xcf\\xb8=L\\xfd\\xa1\\xbes;c\\xbe\\x1e7*\\xbf\\xb8~\\xb7\\xbd\\x96\\xa2\\xaa<\\x8e\\xc0k?&\\xf0\\x1a\\xbf\\x1f\\x8cM\\xc0\\xb1)\\xe7?\\xe4\\xfeu\\xbf\\xd1\\xd2\"@\\xf6\\xa5%@y\\xdf\\xe9?\\x87S\\xae?\\x8e\\xef.\\xc0\\xc9\\x91\\xb5\\xbf\\x00\\xdb\\x12\\xbf\\x05}#\\xbd\\x1d\\x8a\\x16\\xbf%\\xcd\\x1a\\xbfb\\xd9Z\\xbf\\xde\\xbfC\\xbf\\xb1\\xee\\x18?\\xa7\\x1b\\x9b\\xbf(j\\x05@\\xbd\\x06r? \\xd7\\x1d>\\xbaWL\\xbf\\x9f\\xf8\\xfc>\\x99\\xa2)\\xc0\\xbf\\xed\\x99\\xbf\\x1d\\xbd\\x99\\xbf\\xea\\x80h\\xbe\\x82H\\x1d\\xc0>G\\xbc\\xbf\\x9f\\xd8\\x0e\\xbf\\xa2\\x1aV?\\xba\\x0f\\x14@\\xb0\\xed\\xad?8\\x11>\\xbei\\x18\\x05\\xc0\\x19\\x0c\\xfd\\xbf8\\xf37@#\\x14\\x06@F\\r\\xf4?\\x8a\\x9er\\xbf?p\\xd8\\xbe}\\xdb\\x99\\xbf\\x88\\xf6\\x19?\\xab`w>]\\xf6\\x00\\xbf\\x8c\\xd0+\\xbf=V\\xda?\\xab3\\x9f?Kq\\xec\\xbd\\x893&\\xbf\\xf3A\\x01\\xc0R\\xa8\\x0c\\xc0\\xe7\\xe5\\n@\\xd5\\xc2\\x00@\\xc9V0@o\\xfe\\x82\\xc0\\xb1\\x8a\\x1f\\xbf\\xcd\\x81!>\\xbb\\x17`\\xbf\\x16\\xe9\\xac?\\xd9^\\x95>):\\x91\\xbe\\x0f\\x08h\\xbf\\xe6R\\xc9\\xbf\\xff\\'E\\xbe\\x01\\x85z?g\\xb5\\x1b@\\xe80G@U\\x1b\\xd4\\xbe\\xf9\\xb7\\x8a?\\x1a\\xb3\\xd1\\xbf5\\xb7\\xb3>\\x9c\\x86h\\xbf`\\xd06\\xbf\\x89\\x10\\xa7?\\xd0\\x19\\xda\\xbf\\x10\\x81x?6Kh?\\x0bT\\x03@\\xfeSd>\\xce\\x1d\\xba?\\t\\xe6\\x8b\\xbe\\x12\\x8a\\x96\\xbe\\x16\\x84\\xb8=OS!\\xc0a=\\x02@\\xba\\xd6\\x95\\xbe\\xcb\\xdd\\x0b=h\\xf5\\xb5?f\\x8c\\x80\\xbe&\\xd5\\x98\\xbf\\x10\\t\\xf1>I\\xba\\x89\\xbf!\\xcb7>q0\\n@\\xf4\\x8e\\x10\\xbf86\\x84?\\xec\\n\\x06@?\\x9b\\x8b\\xbe\\x07\\x99\\xc5?\\xb5Zp?,}<\\xbf\\xc1I\\x98\\xbfU\\xc5\\x9d\\xbe\\x9dS\\x19\\xbfhL\\xd1\\xbfre\\xed\\xbd:\\x0fv\\xbe\\x07Vf\\xbe\\x82H\\xa7\\xbf\\xe8\\xf7\\x04\\xbf\\x94\\xf1P?\\x04K\\xe2\\xbf|\\xa4\\x1c@&?5@\\xc8\\x92\\x12\\xbf\\xc9\\xe7i>\\xff\\x85-?\\x1cJ]\\xbf\\xf50\\xc2=\\x02\\xcdK@\\xdc\\x9c\\x11\\xc0\\x13\\x02\\x99\\xbf\\xb1\\xc9\\xd7\\xbe\\x9d\\x1c]\\xbf\\xf6\\xcd\\x93\\xbf\\xe02\\n@R\\xb2\\'?\\xed\\xa2\\x88?Oq\\xeb\\xbf\\xb0\\x05\\x01\\xbc/\\xc7@?\\'\\xed\\xde\\xbe\\xe3\\x95\\xdd\\xbd\\x06\\x8b\\xd8?K\\x10\\xaa>\\nL\\xfd\\xbe\\xe8\\xd4\\xec?\\xea\\x07d?M\\xae\\xeb>\\xe9\\x9e\\xa0\\xbe\\xb7y\\x88?\\xfd\\xf1\\x16?\\x1e\\x07a?b|+?\\xada\\xff\\xbe\\x865V?\\x01\\xb5\\xe9\\xbf\\xf62(\\xc0\\x1e/\\xcb?\\xd7\\x8f\\xb6=\\xcd\\x88]\\xc0&\\xfc\\xde?\\xb7 \\x1f\\xc0\\xe1\\xd9\\x0b\\xbf\\x85\\xb0I\\xbf\\x84\\xc9\\xc1\\xbe\\x1b\\xea\\x84?\\xf8\\xd00\\xbfeg\\xb3=\\xa1t\\t\\xc0\\xf0\\xda1@(\\x14\\xa5?\\xfc\\x06\\xf0\\xbe\\n\\x1a*@\\x04+\\x99?\\xd8\\xa9\\xbe?\\xc7$\\x84?Q]\\xcd?E\\xf0\\x1b\\xc0\\xfdma\\xbe\\x1d\\xb3\\x12\\xc0\\x8f\\xe9\\xc4=\\xcc\\xe4\\xfa\\xbe\\x14\\xect\\xbe\\xc9\\x05\\xbe=`\\xda\\x05\\xc0\\xb5z\\xc3?|\\x15}?\\xc3\\\\\\xac?\\xc7QW\\xbfIu\\xf4\\xbe%**?\\xb4Q,\\xc0y-\\x12@\\xc1\\xd0\\x0f@:j\\x01@k\\x87\\xba?b/\\xe1\\xbeh\\xaa<\\xbe59\\x98\\xbfa_\\x07@\\xd9a\\x0c>\\x01\\x12\\xa1?R\\x16\\x94\\xbf\\xc2\\xdb\\x03@\\xef\\xe0m\\xbfl:\\xb3=~\\x95\\x19\\xbf\\x9c\\x1b]\\xbd^o\\x82\\xbf\\xa1i \\xc0\\xdd\\x8dD\\xc0\\xcc\\x04\\xe3\\xbf\\x90\\xbb\\xba?\\xb5\\xf8\\xa5\\xbf%\\xab\\t\\xc07\\x05\\x8b?\\xa5\\x16\\xbe?\\x0b`\\x01\\xbe/\\xc2e?s\\x0c\\x88>z\\xf2\\x0e>\\x97\\x93#\\xc0h\\x9b\\r@\\x0bJ\\x95\\xbf4\\xb8\\x93>vSZ@\\x0c7\\xae\\xbf\\'\\x82\\x1a\\xc0\\xd1\\x8b!>\\x8ag\\xa0?G\\xcb\\xa5>n@\\xd5\\xbf\\x84\\x0f\\\\@2\\x88H?\\x9bD\\x8d?\\xc5n\\x80\\xbf\\xa7\\xc8\\x9d\\xbfk\\x10\\xa1>\\xa9< \\xbe\\xba\\xc4\\xc5=\\x94\\xc9\\x1f\\xc0\\xdd\\xc1\\xd5>\\xa0\\x9c\\xc6?)\\x8d}\\xbf\\xd83\\x91\\xbeEb\\xed<%\\x1aE?\\xaf\\xd4\\x05\\xbe\\xbc\\xa3 ?\\xd3#\\x9e?xi\\x14?\\xe0G#@\\xc7\\xef \\xbfI\\xde:\\xbf\\x9a\\xcf+\\xbe\\x8b\\xa7\\xaa?>0\\x8e\\xbf\\xf1\\xdd\\x13\\xc09\\xea\\xe4\\xbfB\\x1d#@*(\\t\\xc0+\\x7f\\x14@|@\\x9e\\xbfD\\x89\\xe4\\xbf\\x96<L?\\x1f\\x0e\\x15\\xc05\\xfey\\xbf&P\\xae>\\x95\\xa3\\xcb?\\xa0z\\xa4?6p\\xf6\\xbf!\\xd4\\x02@\\xf2\\xbe-\\xbd(\\xc8\\x12\\xbf\\xa8R\"=pGQ@>\\xc2\\x16\\xbf\\xc9\\xd3.\\xc0\\x95\\xb4\\x83?\\x94\\x13\\xcb?\\xb4\\x14\\xee\\xbf*\\x8c\\x1f@ZK\\x1d@\\xc2\\x08J\\xbfSsz\\xbf\\xc0\"\\xee\\xbe\\xd72\\xb6\\xbe\\xd1\\x03\\x11\\xbf;\\xe6?>F\\xbcK\\xbfP]\\xa7\\xc0\\xbaW\\xbe>\\xd18%>\\xdbo\\x1b\\xc0k\\xb8a\\xbe\\x15\\xdd\\xaa\\xbfY\\xaeW@\\x03\\x93\\x11\\xc0\\xf3<M?^\\x9a\\xf2?U\\rU\\xbf\\xe8\\xde\\x1f>\\x07\\xa8\\x9c?\\x04\\x0f\\x81\\xbeI\\x1b\\xd7\\xbe\\xae\\xbe\\x1a\\xbff\\xb2?\\xc0\\xdd\\xe3p>\\xc3\\xa1\\xc7=\\x12\\xa9K>\\x1b\\x18\\xdc=\\x92\\x97\\xe2\\xbf\\x1ety\\xbey\\xa2X?k\\xc3p?\\x9b]\\xb6?\\xce\\xb8\\x1b?uG\\x19@'}\n\n\n\n[code example]\n\n    After deletion: []"},{"id":"customize-the-distance-threshold","title":"Customize the Distance Threshold","role":"content","text":"For most use cases, the right semantic similarity threshold is not a fixed quantity. Depending on the choice of embedding model,\nthe properties of the input query, and even business use case -- the threshold might need to change.\n\nThe distance threshold uses Redis COSINE distance units [0-2], where 0 means identical and 2 means completely different.\n\nFortunately, you can seamlessly adjust the threshold at any point like below:\n\n\n[code example]\n\n\n\n\n    'llmcache:115049a298532be2f181edb03f766770c0db84c22aff39003fec340deaec7545'\n\n\n\n\n[code example]\n\n\n\n\n    'Paris'\n\n\n\n\n[code example]\n\n\n\n\n    []"},{"id":"utilize-ttl","title":"Utilize TTL","role":"content","text":"Redis uses TTL policies (optional) to expire individual keys at points in time in the future.\nThis allows you to focus on your data flow and business logic without bothering with complex cleanup tasks.\n\nA TTL policy set on the `SemanticCache` allows you to temporarily hold onto cache entries. Below, the TTL policy is set to 5 seconds.\n\n\n[code example]\n\n\n[code example]\n\n\n[code example]\n\n    []\n\n\n\n[code example]"},{"id":"ttl-behavior-details","title":"TTL Behavior Details","role":"content","text":"Understanding how TTL works with `SemanticCache` is important for production use:\n\n| Scenario | Behavior |\n|----------|----------|\n| `ttl=None` (default) | Entries persist forever. `check()` does not affect TTL. |\n| `ttl=3600` at init | Entries get TTL on `store()`. TTL is **refreshed** on every `check()` hit. |\n| Set TTL later with `set_ttl(3600)` | **Existing entries keep no TTL**, but `check()` will now **add** TTL to matched entries. |\n| Remove TTL with `set_ttl(None)` | Existing entries keep their TTL, but `check()` no longer refreshes it. |\n\n**Important:** The `check()` method automatically refreshes TTL on all matched entries (sliding window pattern). This keeps frequently accessed entries alive but can unexpectedly add TTL to entries that were originally stored without one."},{"id":"ttl-refresh-on-check","title":"TTL Refresh on Check","role":"content","text":"When `check()` finds matching entries, it refreshes the TTL on **all** matched results, not just the one you use:\n\n\n[code example]"},{"id":"simple-performance-testing","title":"Simple Performance Testing","role":"content","text":"Next, measure the speedup obtained by using ``SemanticCache``. The ``time`` module measures the time taken to generate responses with and without ``SemanticCache``.\n\n\n[code example]\n\n\n[code example]\n\n    Without caching, a call to openAI to answer this simple question took 1.346540927886963 seconds.\n\n\n\n\n\n    'llmcache:67e0f6e28fe2a61c0022fd42bf734bb8ffe49d3e375fd69d692574295a20fc1a'\n\n\n\n\n[code example]\n\n    Avg time taken with LLM cache enabled: 0.04209451675415039\n    Percentage of time saved: 96.87%\n\n\n\n[code example]\n\n    \n    Statistics:\n    ╭─────────────────────────────┬────────────╮\n    │ Stat Key                    │ Value      │\n    ├─────────────────────────────┼────────────┤\n    │ num_docs                    │ 1          │\n    │ num_terms                   │ 24         │\n    │ max_doc_id                  │ 9          │\n    │ num_records                 │ 83         │\n    │ percent_indexed             │ 1          │\n    │ hash_indexing_failures      │ 0          │\n    │ number_of_uses              │ 23         │\n    │ bytes_per_record_avg        │ 36.7469863 │\n    │ doc_table_size_mb           │ 0.01546192 │\n    │ inverted_sz_mb              │ 0.00290870 │\n    │ key_table_size_mb           │ 2.76565551 │\n    │ offset_bits_per_record_avg  │ 8          │\n    │ offset_vectors_sz_mb        │ 5.72204589 │\n    │ offsets_per_term_avg        │ 0.72289156 │\n    │ records_per_doc_avg         │ 83         │\n    │ sortable_values_size_mb     │ 0          │\n    │ total_indexing_time         │ 4.01000022 │\n    │ total_inverted_index_blocks │ 26         │\n    │ vector_index_sz_mb          │ 3.01629638 │\n    ╰─────────────────────────────┴────────────╯\n\n\n\n[code example]"},{"id":"cache-access-controls-tags--filters","title":"Cache Access Controls, Tags & Filters","role":"content","text":"When running complex workflows with similar applications, or handling multiple users it's important to keep data segregated. Building on top of RedisVL's support for complex and hybrid queries we can tag and filter cache entries using custom-defined `filterable_fields`.\n\nLet's store multiple users' data in our cache with similar prompts and ensure we return only the correct user information:\n\n\n[code example]\n\n    You try to use a model that was created with version 4.1.0, however, your version is 3.4.1. This might cause unexpected behavior or errors. In that case, try to update to the latest version.\n    \n    \n    \n\n\n\n\n\n    'private_cache:2831a0659fb888e203cd9fedb9f65681bfa55e4977c092ed1bf87d42d2655081'\n\n\n\n\n[code example]\n\n    found 1 entry \n    The number on file is 123-555-0000\n\n\n\n[code example]\n\nMultiple `filterable_fields` can be defined on a cache, and complex filter expressions can be constructed to filter on these fields, as well as the default fields already present.\n\n\n[code example]\n\n    You try to use a model that was created with version 4.1.0, however, your version is 3.4.1. This might cause unexpected behavior or errors. In that case, try to update to the latest version.\n    \n    \n    \n\n\n\n\n\n    'account_data:944f89729b09ca46b99923d223db45e0bccf584cfd53fcaf87d2a58f072582d3'\n\n\n\n\n[code example]\n\n    found 1 entry\n    Your most recent transaction was for $350"},{"id":"next-steps","title":"Next Steps","role":"content","text":"Now that you understand semantic caching, explore these related guides:\n\n- [Cache Embeddings](https://redis.io/docs/latest/embeddings_cache) - Cache embedding vectors for faster repeated computations\n- [Manage LLM Message History](https://redis.io/docs/latest/message_history) - Store and retrieve conversation history\n- [Query and Filter Data](https://redis.io/docs/latest/complex_filtering) - Learn more about filter expressions for cache access control"},{"id":"cleanup","title":"Cleanup","role":"content","text":"[code example]"}],"examples":[{"id":"what-youll-learn-ex0","language":"python","code":"import os\nimport getpass\nimport time\nimport numpy as np\n\nfrom openai import OpenAI\n\n\nos.environ[\"TOKENIZERS_PARALLELISM\"] = \"False\"\n\napi_key = os.getenv(\"OPENAI_API_KEY\") or getpass.getpass(\"Enter your OpenAI API key: \")\n\nclient = OpenAI(api_key=api_key)\n\ndef ask_openai(question: str) -> str:\n    response = client.completions.create(\n      model=\"gpt-4o-mini\",\n      prompt=f\"Answer the following question simply: {question}\",\n      max_tokens=200\n    )\n    return response.choices[0].text.strip()","section_id":"what-youll-learn"},{"id":"what-youll-learn-ex1","language":"python","code":"# Test\nprint(ask_openai(\"What is the capital of France?\"))","section_id":"what-youll-learn"},{"id":"initializing-semanticcache-ex0","language":"python","code":"import warnings\nwarnings.filterwarnings('ignore')\n\nfrom redisvl.extensions.cache.llm import SemanticCache\nfrom redisvl.utils.vectorize import HFTextVectorizer\n\nllmcache = SemanticCache(\n    name=\"llmcache\",                                          # underlying search index name\n    redis_url=\"redis://localhost:6379\",                       # redis connection url string\n    distance_threshold=0.1,                                   # semantic cache distance threshold (Redis COSINE [0-2], lower is stricter)\n    vectorizer=HFTextVectorizer(\"redis/langcache-embed-v2\"),  # embedding model\n)","section_id":"initializing-semanticcache"},{"id":"initializing-semanticcache-ex1","language":"python","code":"# look at the index specification created for the semantic cache lookup\n!rvl index info -i llmcache","section_id":"initializing-semanticcache"},{"id":"basic-cache-usage-ex0","language":"python","code":"question = \"What is the capital of France?\"","section_id":"basic-cache-usage"},{"id":"basic-cache-usage-ex1","language":"python","code":"# Check the semantic cache -- should be empty\nif response := llmcache.check(prompt=question):\n    print(response)\nelse:\n    print(\"Empty cache\")","section_id":"basic-cache-usage"},{"id":"basic-cache-usage-ex2","language":"python","code":"# Cache the question, answer, and arbitrary metadata\nllmcache.store(\n    prompt=question,\n    response=\"Paris\",\n    metadata={\"city\": \"Paris\", \"country\": \"france\"}\n)","section_id":"basic-cache-usage"},{"id":"basic-cache-usage-ex3","language":"python","code":"# Check the cache again\nif response := llmcache.check(prompt=question, return_fields=[\"prompt\", \"response\", \"metadata\"]):\n    print(response)\nelse:\n    print(\"Empty cache\")","section_id":"basic-cache-usage"},{"id":"basic-cache-usage-ex4","language":"python","code":"# Check for a semantically similar result\nquestion = \"What actually is the capital of France?\"\nllmcache.check(prompt=question)[0]['response']","section_id":"basic-cache-usage"},{"id":"entry-ids-and-keys-ex0","language":"python","code":"# Store an entry and capture the returned key\nkey = llmcache.store(\n    prompt=\"What is the capital of France?\",\n    response=\"Paris\",\n    metadata={\"source\": \"geography\"}\n)\nprint(f\"Full Redis key: {key}\")","section_id":"entry-ids-and-keys"},{"id":"entry-ids-and-keys-ex1","language":"python","code":"# Check and see both entry_id and key in the response\nresult = llmcache.check(\n    prompt=\"What is the capital of France?\",\n    return_fields=[\"entry_id\", \"prompt\", \"response\"]\n)\nprint(f\"Entry ID: {result[0]['entry_id']}\")\nprint(f\"Key: {result[0]['key']}\")","section_id":"entry-ids-and-keys"},{"id":"fetch-and-delete-specific-entries-ex0","language":"python","code":"# List all keys in the cache using the underlying index\nfrom redisvl.query import FilterQuery\nfrom redisvl.query.filter import FilterExpression\n\nquery = FilterQuery(\n    filter_expression=FilterExpression(\"*\"),\n    return_fields=[\"entry_id\", \"prompt\", \"response\"]\n)\nall_entries = llmcache._index.query(query)\nprint(f\"Found {len(all_entries)} entries in cache:\")\nfor entry in all_entries:\n    print(f\"  - entry_id: {entry['entry_id'][:20]}... prompt: {entry['prompt'][:30]}...\")","section_id":"fetch-and-delete-specific-entries"},{"id":"fetch-and-delete-specific-entries-ex1","language":"python","code":"# Fetch a specific entry by its entry_id\nentry_id = result[0]['entry_id']\nrecord = llmcache._index.fetch(entry_id)\nprint(f\"Fetched record: {record}\")","section_id":"fetch-and-delete-specific-entries"},{"id":"fetch-and-delete-specific-entries-ex2","language":"python","code":"# Delete specific entries by ID or key\n# By entry IDs (without prefix):\nllmcache.drop(ids=[entry_id])\n\n# Or by full Redis keys (with prefix):\n# llmcache.drop(keys=[key])\n\n# Verify it's deleted\nresult = llmcache.check(prompt=\"What is the capital of France?\")\nprint(f\"After deletion: {result}\")","section_id":"fetch-and-delete-specific-entries"},{"id":"customize-the-distance-threshold-ex0","language":"python","code":"# Widen the semantic distance threshold (allow less similar matches)\nllmcache.set_threshold(0.5)\n\n# Re-store an entry for the threshold demo\nllmcache.store(prompt=\"What is the capital of France?\", response=\"Paris\")","section_id":"customize-the-distance-threshold"},{"id":"customize-the-distance-threshold-ex1","language":"python","code":"# Really try to trick it by asking around the point\n# But is able to slip just under our new threshold\nquestion = \"What is the capital of the country where Nice is located?\"\nllmcache.check(prompt=question)[0]['response']","section_id":"customize-the-distance-threshold"},{"id":"customize-the-distance-threshold-ex2","language":"python","code":"# Invalidate the cache completely by clearing it out\nllmcache.clear()\n\n# Should be empty now\nllmcache.check(prompt=question)","section_id":"customize-the-distance-threshold"},{"id":"utilize-ttl-ex0","language":"python","code":"llmcache.set_ttl(5) # 5 seconds","section_id":"utilize-ttl"},{"id":"utilize-ttl-ex1","language":"python","code":"llmcache.store(\"This is a TTL test\", \"This is a TTL test response\")\n\ntime.sleep(6)","section_id":"utilize-ttl"},{"id":"utilize-ttl-ex2","language":"python","code":"# confirm that the cache has cleared by now on it's own\nresult = llmcache.check(\"This is a TTL test\")\n\nprint(result)","section_id":"utilize-ttl"},{"id":"utilize-ttl-ex3","language":"python","code":"# Reset the TTL to null (long lived data)\nllmcache.set_ttl()","section_id":"utilize-ttl"},{"id":"ttl-refresh-on-check-ex0","language":"python","code":"# Example: TTL refresh behavior\nllmcache.set_ttl(300)  # 5 minutes\n\n# Store an entry\nllmcache.store(\"What is Python?\", \"A programming language\")\n\n# Every time check() finds this entry, TTL resets to 300 seconds\nresult = llmcache.check(\"What is Python?\")  # TTL refreshed\n\n# Reset for next examples\nllmcache.set_ttl()\nllmcache.clear()","section_id":"ttl-refresh-on-check"},{"id":"simple-performance-testing-ex0","language":"python","code":"def answer_question(question: str) -> str:\n    \"\"\"Helper function to answer a simple question using OpenAI with a wrapper\n    check for the answer in the semantic cache first.\n\n    Args:\n        question (str): User input question.\n\n    Returns:\n        str: Response.\n    \"\"\"\n    results = llmcache.check(prompt=question)\n    if results:\n        return results[0][\"response\"]\n    else:\n        answer = ask_openai(question)\n        return answer","section_id":"simple-performance-testing"},{"id":"simple-performance-testing-ex1","language":"python","code":"start = time.time()\n# asking a question -- openai response time\nquestion = \"What was the name of the first US President?\"\nanswer = answer_question(question)\nend = time.time()\n\nprint(f\"Without caching, a call to openAI to answer this simple question took {end-start} seconds.\")\n\n# add the entry to our LLM cache\nllmcache.store(prompt=question, response=\"George Washington\")","section_id":"simple-performance-testing"},{"id":"simple-performance-testing-ex2","language":"python","code":"# Calculate the avg latency for caching over LLM usage\ntimes = []\n\nfor _ in range(10):\n    cached_start = time.time()\n    cached_answer = answer_question(question)\n    cached_end = time.time()\n    times.append(cached_end-cached_start)\n\navg_time_with_cache = np.mean(times)\nprint(f\"Avg time taken with LLM cache enabled: {avg_time_with_cache}\")\nprint(f\"Percentage of time saved: {round(((end - start) - avg_time_with_cache) / (end - start) * 100, 2)}%\")","section_id":"simple-performance-testing"},{"id":"simple-performance-testing-ex3","language":"python","code":"# check the stats of the index\n!rvl stats -i llmcache","section_id":"simple-performance-testing"},{"id":"simple-performance-testing-ex4","language":"python","code":"# Clear the cache AND delete the underlying index\nllmcache.delete()","section_id":"simple-performance-testing"},{"id":"cache-access-controls-tags--filters-ex0","language":"python","code":"private_cache = SemanticCache(\n    name=\"private_cache\",\n    filterable_fields=[{\"name\": \"user_id\", \"type\": \"tag\"}]\n)\n\nprivate_cache.store(\n    prompt=\"What is the phone number linked to my account?\",\n    response=\"The number on file is 123-555-0000\",\n    filters={\"user_id\": \"abc\"},\n)\n\nprivate_cache.store(\n    prompt=\"What's the phone number linked in my account?\",\n    response=\"The number on file is 123-555-1111\",\n    filters={\"user_id\": \"def\"},\n)","section_id":"cache-access-controls-tags--filters"},{"id":"cache-access-controls-tags--filters-ex1","language":"python","code":"from redisvl.query.filter import Tag\n\n# define user id filter\nuser_id_filter = Tag(\"user_id\") == \"abc\"\n\nresponse = private_cache.check(\n    prompt=\"What is the phone number linked to my account?\",\n    filter_expression=user_id_filter,\n    num_results=2\n)\n\nprint(f\"found {len(response)} entry \\n{response[0]['response']}\")","section_id":"cache-access-controls-tags--filters"},{"id":"cache-access-controls-tags--filters-ex2","language":"python","code":"# Cleanup\nprivate_cache.delete()","section_id":"cache-access-controls-tags--filters"},{"id":"cache-access-controls-tags--filters-ex3","language":"python","code":"complex_cache = SemanticCache(\n    name='account_data',\n    filterable_fields=[\n        {\"name\": \"user_id\", \"type\": \"tag\"},\n        {\"name\": \"account_type\", \"type\": \"tag\"},\n        {\"name\": \"account_balance\", \"type\": \"numeric\"},\n        {\"name\": \"transaction_amount\", \"type\": \"numeric\"}\n    ]\n)\ncomplex_cache.store(\n    prompt=\"what is my most recent checking account transaction under $100?\",\n    response=\"Your most recent transaction was for $75\",\n    filters={\"user_id\": \"abc\", \"account_type\": \"checking\", \"transaction_amount\": 75},\n)\ncomplex_cache.store(\n    prompt=\"what is my most recent savings account transaction?\",\n    response=\"Your most recent deposit was for $300\",\n    filters={\"user_id\": \"abc\", \"account_type\": \"savings\", \"transaction_amount\": 300},\n)\ncomplex_cache.store(\n    prompt=\"what is my most recent checking account transaction over $200?\",\n    response=\"Your most recent transaction was for $350\",\n    filters={\"user_id\": \"abc\", \"account_type\": \"checking\", \"transaction_amount\": 350},\n)\ncomplex_cache.store(\n    prompt=\"what is my checking account balance?\",\n    response=\"Your current checking account is $1850\",\n    filters={\"user_id\": \"abc\", \"account_type\": \"checking\"},\n)","section_id":"cache-access-controls-tags--filters"},{"id":"cache-access-controls-tags--filters-ex4","language":"python","code":"from redisvl.query.filter import Num\n\nvalue_filter = Num(\"transaction_amount\") > 100\naccount_filter = Tag(\"account_type\") == \"checking\"\ncomplex_filter = value_filter & account_filter\n\n# check for checking account transactions over $100\ncomplex_cache.set_threshold(0.3)\nresponse = complex_cache.check(\n    prompt=\"what is my most recent checking account transaction?\",\n    filter_expression=complex_filter,\n    num_results=5\n)\nprint(f'found {len(response)} entry')\nprint(response[0][\"response\"])","section_id":"cache-access-controls-tags--filters"},{"id":"cleanup-ex0","language":"python","code":"complex_cache.delete()","section_id":"cleanup"}]}
{"schema_version":2,"id":"develop/ai/redisvl/user_guide/how_to_guides/mcp","title":"Run RedisVL MCP","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/how_to_guides/mcp/","summary":"","aliases":["/integrate/redisvl/user_guide/how_to_guides/mcp"],"tags":[],"last_updated":"2026-10-12T09:34:21+02:00","page_type":"content","content_hash":"1bb3ab12f883b5c55ac4cb81eadeefcd97018cdcbb5ae0792f117981f36b3ffb","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This guide shows how to run the RedisVL MCP server against an existing Redis index, configure its behavior, and use the MCP tools it exposes.\n\nFor the higher-level design, see [RedisVL MCP](https://redis.io/docs/latest/../../concepts/mcp)."},{"id":"before-you-start","title":"Before You Start","role":"content","text":"RedisVL MCP assumes all of the following are already true:\n\n- you have Python 3.10 or newer\n- you have Redis with Search capabilities available\n- the Redis index already exists\n- you know which text field and vector field the server should use\n- you have installed the vectorizer provider dependencies your config needs\n\nInstall the MCP extra:\n\n[code example]\n\nIf your vectorizer needs a provider extra, install that too:\n\n[code example]"},{"id":"start-the-server","title":"Start the Server","role":"content","text":"Run the server over stdio (default):\n\n[code example]\n\nRun it over Streamable HTTP for remote MCP clients. Binding to a non-loopback host (`--host 0.0.0.0`) requires either JWT authentication (see [Authenticate RedisVL MCP](https://redis.io/docs/latest/mcp_authentication)) or the explicit `--allow-unauthenticated` flag; otherwise the server refuses to start:\n\n[code example]\n\nRun it over SSE:\n\n[code example]\n\n> [!WARNING]\n> Streamable HTTP and SSE endpoints are **unauthenticated by default**. Binding to a non-loopback host without auth fails closed unless you pass `--allow-unauthenticated`; binding to loopback without auth only warns. For real deployments, enable JWT authentication (see [Authenticate RedisVL MCP](https://redis.io/docs/latest/mcp_authentication)) rather than using `--allow-unauthenticated`. When not using `--read-only`, the `upsert-records` tool is also exposed to any client that can reach the server."},{"id":"transport-security-host--origin-validation","title":"Transport Security (Host / Origin Validation)","role":"content","text":"On the HTTP transports the server validates the request `Host` and `Origin` headers against allowlists before any tool runs. This is **on by default** and defends against [DNS rebinding](https://en.wikipedia.org/wiki/DNS_rebinding): without it, a malicious web page could rebind its own hostname to `127.0.0.1` and use the victim’s browser to reach a local MCP server, even one bound to loopback.\n\n- **Host:** the allowlist is derived automatically from the bind address. Loopback binds accept `localhost`, `127.0.0.1`, and `[::1]` (with or without the port), so local MCP clients work with no configuration.\n- **Origin:** requests with no `Origin` header (typical of non-browser MCP clients) always pass. A request that carries a cross-site `Origin` is rejected unless that origin is explicitly allowlisted.\n\nYou only need to configure this for deployments where the client-visible `Host` differs from the bind address (a reverse proxy, or a `--host 0.0.0.0` bind reached via a public hostname). Use the `REDISVL_MCP_ALLOWED_HOSTS` / `REDISVL_MCP_ALLOWED_ORIGINS` env vars, or a `server.transport_security` block in the config:\n\n[code example]\n\n> [!NOTE]\n> Behind a reverse proxy or gateway that already validates `Host`/`Origin`, set `server.transport_security.enabled: false` (or `allow_any_origin: true`) so the proxy’s rewritten headers are not rejected.\n\nRun it in read-only mode to expose search without upsert:\n\n[code example]"},{"id":"cli-flags","title":"CLI Flags","role":"content","text":"| Flag          | Default     | Purpose                                                   |\n|---------------|-------------|-----------------------------------------------------------|\n| `--config`    | —           | Path to the MCP YAML config (required)                    |\n| `--transport` | `stdio`     | Transport protocol: `stdio`, `sse`, or `streamable-http`  |\n| `--host`      | `127.0.0.1` | Bind address (only used with `sse` and `streamable-http`) |\n| `--port`      | `8000`      | Bind port (only used with `sse` and `streamable-http`)    |\n| `--read-only` | off         | Disable writes across every index (global read-only)      |"},{"id":"environment-variables","title":"Environment Variables","role":"content","text":"You can also control boot settings through environment variables:\n\n| Variable                                 | Purpose                                                                                                                                                                                                                                                   |\n|------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `REDISVL_MCP_CONFIG`                     | Path to the MCP YAML config                                                                                                                                                                                                                               |\n| `REDISVL_MCP_READ_ONLY`                  | Disable `upsert-records` when set to `true`                                                                                                                                                                                                               |\n| `REDISVL_MCP_TOOL_SEARCH_DESCRIPTION`    | Set the base search tool description text. On a single-index server RedisVL appends schema-derived typed filter, `exists`, and `return_fields` hints; on a multi-index server it appends a note directing clients to call `list-indexes` and pass `index` |\n| `REDISVL_MCP_TOOL_UPSERT_DESCRIPTION`    | Override the upsert tool description                                                                                                                                                                                                                      |\n| `REDISVL_MCP_ALLOWED_HOSTS`              | Comma-separated extra `Host` header values to accept on HTTP transports (in addition to the bind-derived defaults)                                                                                                                                        |\n| `REDISVL_MCP_ALLOWED_ORIGINS`            | Comma-separated browser `Origin` values to accept on HTTP transports                                                                                                                                                                                      |\n| `REDISVL_MCP_ALLOW_ANY_ORIGIN`           | Accept any `Origin` (for trusted-proxy setups) when set to `true`                                                                                                                                                                                         |\n| `REDISVL_MCP_TRANSPORT_SECURITY_ENABLED` | Set to `false` to disable Host/Origin validation (e.g. behind a validating proxy)                                                                                                                                                                         |"},{"id":"connect-a-remote-mcp-client","title":"Connect a Remote MCP Client","role":"content","text":"When using Streamable HTTP or SSE transport, point your MCP client at the server URL:\n\n- **Streamable HTTP**: `http://<host>:<port>/mcp`\n- **SSE**: `http://<host>:<port>/sse`\n\n**Note:** `<host>` here is the bind address the server was started with. The default `127.0.0.1` only accepts connections from the same machine. To allow connections from other machines, start the server with `--host 0.0.0.0` and use the machine’s actual IP or hostname in the client URL. Because a `0.0.0.0` bind has no single canonical `Host`, add the client-visible hostname(s) to `REDISVL_MCP_ALLOWED_HOSTS` (or `server.transport_security.allowed_hosts`), or Host validation will reject those requests. See [Transport Security]().\n\nFor example, to configure a remote MCP client to connect to a Streamable HTTP server running on `192.168.1.10:8000`:\n\n[code example]"},{"id":"example-config","title":"Example Config","role":"example","text":"This example binds one logical MCP server to one existing Redis index called `knowledge`. A single configured index is the simplest deployment, and callers never need to name it. See [Multiple Indexes]() below to expose several indexes from the same server.\n\nThe config uses `${REDIS_URL}` and `${OPENAI_API_KEY}` as environment-variable placeholders. These values are resolved when the server starts. You can also use `${VAR:-default}` to provide a fallback value.\n\n[code example]"},{"id":"what-this-config-means","title":"What This Config Means","role":"content","text":"- `redis_name` must point to an index that already exists in Redis\n- `search.type` fixes retrieval behavior for every MCP caller\n- `runtime.text_field_name` is required for `fulltext` and `hybrid` search\n- `runtime.vector_field_name` is required for `vector` and `hybrid` search, and optional for plain full-text deployments\n- `runtime.default_embed_text_field` is only required when the server should generate embeddings during upsert\n- `vectorizer` is required for query embedding and server-side embedding, but optional for fulltext-only configs\n- `runtime.max_result_window` caps deep paging by limiting the maximum `offset + limit`\n- `schema_overrides` is only for patching incomplete field attrs discovered from Redis"},{"id":"fulltext-only-config","title":"Fulltext-Only Config","role":"content","text":"For a non-vector deployment, omit vector-only settings entirely:\n\n[code example]"},{"id":"multiple-indexes","title":"Multiple Indexes","role":"content","text":"The `indexes` mapping can hold more than one binding. Each entry is keyed by a logical id, points at its own existing Redis index through `redis_name`, and carries its own `search`, `runtime`, and optional `vectorizer`. The example below exposes a writable vector index `knowledge` alongside a read-only fulltext index `tickets` from the same server:\n\n[code example]\n\nNotes:\n\n- Each binding is inspected and validated independently at startup. Startup is all-or-nothing: if any binding fails, the server does not start.\n- The optional per-index `description` and `read_only` flags are surfaced through `list-indexes`.\n- `read_only: true` makes that binding reject writes even though the server as a whole is not in global read-only mode. Because `knowledge` is still writable, the `upsert-records` tool is registered; an upsert targeting `tickets` is rejected with `forbidden`.\n- A single-index config keeps working unchanged — adding bindings does not change how the sole-binding case behaves."},{"id":"index-selection","title":"Index Selection","role":"content","text":"On a multi-index server, `search-records` and `upsert-records` take an optional `index` argument naming the logical id to target:\n\n- With exactly one index configured, `index` may be omitted and resolves to that binding.\n- With multiple indexes configured, omitting `index` returns `invalid_request`; an unknown id also returns `invalid_request`.\n- Clients should call [`list-indexes`]() first to discover the available ids and their filterable fields."},{"id":"custom-tool-profiles","title":"Custom Tool Profiles","role":"content","text":"A **profile** publishes `search-records` under your own name and description with some arguments pre-filled and frozen. It is pure config — no Python — and it resolves to a built-in call, so it inherits the same limits, read-only policy, auth scoping, and error contract.\n\n[code example]\n\nWhat the client sees for `search-resolved-tickets`:\n\n- `query` (required), `limit` (≤ 10), `offset`, and `filter` — and nothing else.\n- No `index` argument: the binding is pinned by `index:` in the config.\n- No `return_fields` argument: locking a projection removes it from the contract.\n- `filter` accepts the **object** form only. A raw string filter is refused — by the advertised schema for a compliant client, and by the tool itself otherwise.\n\nRules worth knowing:\n\n- **Anything not listed under `params` stays exposed.** Locking only a filter keeps the rest of the built-in’s contract intact.\n- **Locked and caller filters are AND-combined.** The caller can narrow within the locked scope but never widen past it — an `or` from the caller nests inside the locked AND rather than replacing it.\n- **`params.limit.max` bounds the result count either way.** An omitted `limit` is capped rather than falling through to `default_limit`; an explicit request above the cap returns `invalid_request`. With `expose: false`, the cap becomes the fixed result count. The cap may not exceed the binding’s `max_limit`. It bounds page size, not total reachable data — `offset` still pages within `max_result_window`.\n- **`suppress_schema_hints: true`** drops the auto-generated filter/return-field hints from the description. By default they are appended only for arguments the model can still use.\n- **`index` may be omitted only with exactly one index configured.**\n\n`builtin_tools` is independent of profiles: use it to stop advertising any built-in, for example `upsert-records: disabled` on a server that should only read. Disabling `list-indexes` on a multi-index server logs a warning, because `search-records` tells clients to call it to discover index ids.\n\nMisconfiguration fails at startup, not at the first call — a name colliding with a built-in or using a reserved `redisvl-`/`redisvl_` prefix, a duplicate name, a missing or unknown `index`, a cap above `max_limit`, hiding `query`, locking `return_fields` while also exposing them, or a locked filter or projection naming a field the index does not have. Unrecognized keys are rejected too, so a typo in `lock` fails loudly instead of quietly producing a tool that reads as locked but enforces nothing."},{"id":"tenant-scoping-with-claim-injection","title":"Tenant Scoping With Claim Injection","role":"content","text":"When tenants share one index, a profile can take the tenant from the caller’s verified token instead of trusting the model to pass a filter. This needs authentication, so the example configures it; see [Authenticate RedisVL MCP](https://redis.io/docs/latest/mcp_authentication) for the rest of that block.\n\nThe index needs the tenant on every document, as a tag. Declare it `CASESENSITIVE` unless your identity provider guarantees one case, because tag fields otherwise treat `Acme` and `acme` as the same tenant:\n\n[code example]\n\nThen point a profile at it:\n\n[code example]\n\nServe it over HTTP:\n\n[code example]\n\nWhat the client sees for `search-customer-kb`:\n\n- `query` (required), `limit`, `offset`, `filter` and `return_fields`, with no argument for `org_id`. A call that passes `org_id` anyway is rejected.\n- A description whose field hints list `content` but not `org_id`.\n- Results from its own tenant only. A `filter` naming `org_id` ANDs with the injected value, so naming another tenant returns nothing. Results still carry `org_id`, always with the caller’s own value, unless you lock `return_fields` to leave it out.\n\nThe server refuses to start while anything else can reach the same Redis index without the tenant scope: `search-records`, `upsert-records` unless every binding over the index is `read_only`, or another custom tool on that index whose `lock.inject` differs, including one with none. Bindings that share a `redis_name` are the same index, so a vector tool and a fulltext tool over one set of documents must inject the same scope. Each of those routes would hand every caller a way round the profile, and a write could retag another tenant’s document as the writer’s own. Ingest documents outside the server, stamping `org_id` exactly as the identity provider emits it.\n\nA tool meant to read across tenants, such as an internal support search, belongs on a separate server with its own authentication. Every tool on one server sits behind the same read scope, so any tenant could call it.\n\nListing the tenant claim under `required_claims` makes the verifier reject a token without it before any tool runs. That checks presence only: the profile still validates the value on every call, and refuses a missing, empty, list-valued or otherwise unusable claim with a `forbidden` error before any query runs.\n\nThe server refuses to start an injecting profile without authentication, over `stdio`, or on a field the index does not hold as an indexed tag. The `stdio` check applies when the server starts through `rvl mcp` or `run_async`. For what the guarantee covers and what it rests on, read the threat model in [RedisVL MCP](https://redis.io/docs/latest/../../concepts/mcp)."},{"id":"tool-contracts","title":"Tool Contracts","role":"content","text":"RedisVL MCP exposes a small, implementation-owned contract."},{"id":"list-indexes","title":"`list-indexes`","role":"content","text":"`list-indexes` is always available and takes no arguments. Call it first on a multi-index server to discover which logical ids exist and how to filter each one.\n\nExample response payload:\n\n[code example]\n\nNotes:\n\n- `upsert_available` reflects the binding’s effective write availability (global read-only **or** the per-index `read_only` flag)\n- `fields` lists the filterable fields discovered from the index; the vector field and the configured embed-source text field are intentionally omitted\n- `limits` includes only runtime limits that were explicitly configured (such as `max_limit` or `max_upsert_records`); defaults are not echoed\n- the underlying Redis index name (`redis_name`) is never exposed\n- `description` appears only when configured for that binding"},{"id":"search-records","title":"`search-records`","role":"content","text":"Arguments:\n\n- `index` (optional; required when multiple indexes are configured)\n- `query`\n- `limit`\n- `offset`\n- `filter`\n- `return_fields`\n\nExample request payload:\n\n[code example]\n\nExample response payload:\n\n[code example]\n\nNotes:\n\n- `index` selects the logical binding; omit it only on a single-index server. The resolved id is echoed back in the response\n- `search_type` is response metadata, not a request argument\n- when `return_fields` is omitted, RedisVL MCP returns all non-vector fields\n- returning the configured vector field is rejected\n- `filter` accepts either a raw string or a JSON DSL object\n- the `search-records` tool description includes schema-derived hints for typed JSON DSL filter fields, object-filter `exists` support, and valid `return_fields`\n- `offset + limit` must stay within `runtime.max_result_window`\n- startup rejects schemas that use MCP-reserved score metadata field names:\n  `id`, `__key`, `key`, `score`, `vector_distance`, `__score`, `text_score`, `vector_similarity`, `hybrid_score`"},{"id":"upsert-records","title":"`upsert-records`","role":"content","text":"Arguments:\n\n- `index` (optional; required when multiple indexes are configured)\n- `records`\n- `id_field`\n- `skip_embedding_if_present`\n\nExample request payload:\n\n[code example]\n\nExample response payload:\n\n[code example]\n\nNotes:\n\n- `index` selects the logical binding; omit it only on a single-index server. The resolved id is echoed back in the response\n- this tool is not registered when every binding is read-only (global read-only mode or every binding setting `read_only: true`)\n- a write targeting a read-only binding is rejected with `forbidden` before any data is changed, even when the tool is registered because other bindings are writable\n- when server-side embedding is configured, records that need embedding must contain `runtime.default_embed_text_field`\n- when `skip_embedding_if_present` is `true`, records that already contain the configured vector field can skip re-embedding\n- when a vector field is configured but server-side embedding is disabled, callers must supply vectors explicitly"},{"id":"search-examples","title":"Search Examples","role":"content","text":""},{"id":"discovery-first-multi-index-flow","title":"Discovery-First Multi-Index Flow","role":"content","text":"On a multi-index server, call `list-indexes` first, pick a logical id from the response, then pass it as `index`:\n\n[code example]\n\nThe same `index` argument routes an `upsert-records` write to a specific binding:\n\n[code example]\n\nOn a single-index server you can omit `index` entirely; the examples below show that backward-compatible shape."},{"id":"read-only-vector-search","title":"Read-Only Vector Search","role":"content","text":"Use read-only mode when assistants should only retrieve data:\n\n[code example]\n\nWith a `search.type` of `vector`, callers send only the query, filters, pagination, and field projection:\n\n[code example]"},{"id":"raw-string-filter","title":"Raw String Filter","role":"content","text":"Pass a raw Redis filter string through unchanged:\n\n[code example]"},{"id":"json-dsl-filter","title":"JSON DSL Filter","role":"content","text":"The DSL supports logical operators and type-checked field operators:\n\n[code example]"},{"id":"pagination-and-field-projection","title":"Pagination and Field Projection","role":"content","text":"[code example]"},{"id":"hybrid-search-with-schema_overrides","title":"Hybrid Search With `schema_overrides`","role":"content","text":"Use `schema_overrides` when Redis inspection cannot recover complete vector attrs, then keep hybrid behavior in config:\n\n[code example]\n\nThe MCP caller still sends the same request shape:\n\n[code example]"},{"id":"upsert-examples","title":"Upsert Examples","role":"content","text":""},{"id":"auto-embed-new-records","title":"Auto-Embed New Records","role":"content","text":"If a record does not include the configured vector field, RedisVL MCP embeds `runtime.default_embed_text_field` and writes the result:\n\n[code example]"},{"id":"update-existing-records-with-id_field","title":"Update Existing Records With `id_field`","role":"content","text":"[code example]"},{"id":"control-re-embedding-with-skip_embedding_if_present","title":"Control Re-Embedding With `skip_embedding_if_present`","role":"content","text":"[code example]\n\nSet `skip_embedding_if_present` to `false` when you want the server to regenerate embeddings during upsert. In most cases, the caller should omit the vector field and let the server manage embeddings from `runtime.default_embed_text_field`."},{"id":"plain-writes-without-embedding","title":"Plain Writes Without Embedding","role":"content","text":"For fulltext-only indexes, `upsert-records` can write records without any vectorizer or vector field configuration:\n\n[code example]\n\nIf you configure a vector field but omit server-side embedding support, the caller must send vectors in each record instead of relying on the server to generate them."},{"id":"troubleshooting","title":"Troubleshooting","role":"errors","text":""},{"id":"missing-mcp-dependencies","title":"Missing MCP Dependencies","role":"content","text":"If `rvl mcp` reports missing optional dependencies, install the MCP extra:\n\n[code example]\n\nIf the configured vectorizer needs a provider SDK, install that provider extra too. Fulltext-only configs can omit the vectorizer entirely."},{"id":"configured-redis-index-does-not-exist","title":"Configured Redis Index Does Not Exist","role":"configuration","text":"The server only binds to an existing index. Create the index first, then point `indexes.<id>.redis_name` at that index name."},{"id":"missing-required-environment-variables","title":"Missing Required Environment Variables","role":"content","text":"YAML values support `${VAR}` and `${VAR:-default}` substitution. Missing required variables fail startup before the server registers tools."},{"id":"vectorizer-dimension-mismatch","title":"Vectorizer Dimension Mismatch","role":"content","text":"If the vectorizer dims do not match the configured vector field dims, startup fails. Make sure the embedding model and the effective vector field dimensions are aligned."},{"id":"hybrid-config-requires-native-runtime-support","title":"Hybrid Config Requires Native Runtime Support","role":"content","text":"Some hybrid params depend on native hybrid support in Redis and redis-py. If your environment does not support that path, remove native-only params such as `knn_ef_runtime` or upgrade Redis and redis-py."},{"id":"claim-injection-requires-authentication","title":"Claim Injection Requires Authentication","role":"content","text":"A profile with `lock.inject` refuses to start when authentication is not enabled, or when the server runs over `stdio`, because neither can supply a verified token. Configure `server.auth` and serve over `sse` or `streamable-http`. The check runs before the server connects to Redis, so it reports even when Redis is unreachable."},{"id":"claim-injection-refuses-an-unscoped-route","title":"Claim Injection Refuses an Unscoped Route","role":"content","text":"A profile with `lock.inject` refuses to start while its Redis index is also reachable through `search-records`, through `upsert-records` on any writable binding over it, or through another custom tool on it whose `lock.inject` differs, including one with none. Bindings that share a `redis_name` are the same index. The error names the Redis index and the bindings over it, each route it found, and each tool’s injected scope. Disable the built-ins under `server.builtin_tools`, mark every binding over the index `read_only`, and give every custom tool on the index the same `lock.inject`. Move a tool meant to read across tenants to a separate server with its own authentication."},{"id":"claim-injection-fails-every-request-with-forbidden","title":"Claim Injection Fails Every Request With `forbidden`","role":"content","text":"The token is verified but its claim is unusable: missing, empty, padded with whitespace, not a single string, or containing a control character or backtick. The error names the claim and the tool. A misspelled `claim` name in the config is the usual cause, since a JWT claim name such as `https://acme.example/org` must match exactly."}],"examples":[{"id":"before-you-start-ex0","language":"bash","code":"pip install redisvl[mcp]","section_id":"before-you-start"},{"id":"before-you-start-ex1","language":"bash","code":"pip install redisvl[mcp,openai]","section_id":"before-you-start"},{"id":"start-the-server-ex0","language":"bash","code":"uvx --from redisvl[mcp] rvl mcp --config /path/to/mcp.yaml","section_id":"start-the-server"},{"id":"start-the-server-ex1","language":"bash","code":"uvx --from redisvl[mcp] rvl mcp --config /path/to/mcp.yaml --transport streamable-http --host 0.0.0.0 --port 8000 --allow-unauthenticated","section_id":"start-the-server"},{"id":"start-the-server-ex2","language":"bash","code":"uvx --from redisvl[mcp] rvl mcp --config /path/to/mcp.yaml --transport sse --host 0.0.0.0 --port 9000 --allow-unauthenticated","section_id":"start-the-server"},{"id":"transport-security-host--origin-validation-ex0","language":"yaml","code":"server:\n  redis_url: ${REDIS_URL}\n  transport_security:\n    allowed_hosts: [mcp.example.com]          # extra Host values to accept\n    allowed_origins: [https://app.example.com]  # browser origins to accept\n    # allow_any_origin: true                   # trust upstream Origin validation\n    # enabled: false                           # disable when a trusted proxy validates","section_id":"transport-security-host--origin-validation"},{"id":"transport-security-host--origin-validation-ex1","language":"bash","code":"uvx --from redisvl[mcp] rvl mcp --config /path/to/mcp.yaml --read-only","section_id":"transport-security-host--origin-validation"},{"id":"connect-a-remote-mcp-client-ex0","language":"json","code":"{\n  \"mcpServers\": {\n    \"redisvl\": {\n      \"url\": \"http://192.168.1.10:8000/mcp\",\n      \"transport\": \"streamable-http\"\n    }\n  }\n}","section_id":"connect-a-remote-mcp-client"},{"id":"example-config-ex0","language":"yaml","code":"server:\n  redis_url: ${REDIS_URL}\n\nindexes:\n  knowledge:\n    redis_name: knowledge\n\n    vectorizer:\n      class: OpenAITextVectorizer\n      model: text-embedding-3-small\n      api_config:\n        api_key: ${OPENAI_API_KEY}\n\n    schema_overrides:\n      fields:\n        - name: embedding\n          type: vector\n          attrs:\n            dims: 1536\n            datatype: float32\n\n    search:\n      type: hybrid\n      params:\n        text_scorer: BM25STD\n        stopwords: english\n        vector_search_method: KNN\n        combination_method: LINEAR\n        linear_text_weight: 0.3\n\n    runtime:\n      text_field_name: content\n      vector_field_name: embedding\n      default_embed_text_field: content\n      default_limit: 10\n      max_limit: 25\n      max_result_window: 1000\n      max_upsert_records: 64\n      skip_embedding_if_present: true\n      startup_timeout_seconds: 30\n      request_timeout_seconds: 60\n      max_concurrency: 16","section_id":"example-config"},{"id":"fulltext-only-config-ex0","language":"yaml","code":"server:\n  redis_url: ${REDIS_URL}\n\nindexes:\n  knowledge:\n    redis_name: knowledge\n\n    search:\n      type: fulltext\n      params:\n        text_scorer: BM25STD\n        stopwords: english\n\n    runtime:\n      text_field_name: content\n      default_limit: 10\n      max_limit: 25\n      max_result_window: 1000\n      max_upsert_records: 64\n      skip_embedding_if_present: true\n      startup_timeout_seconds: 30\n      request_timeout_seconds: 60\n      max_concurrency: 16","section_id":"fulltext-only-config"},{"id":"multiple-indexes-ex0","language":"yaml","code":"server:\n  redis_url: ${REDIS_URL}\n\nindexes:\n  knowledge:\n    redis_name: knowledge\n    description: Internal runbooks and operational guidance.\n\n    vectorizer:\n      class: OpenAITextVectorizer\n      model: text-embedding-3-small\n      api_config:\n        api_key: ${OPENAI_API_KEY}\n\n    search:\n      type: vector\n\n    runtime:\n      text_field_name: content\n      vector_field_name: embedding\n      default_embed_text_field: content\n      default_limit: 10\n      max_limit: 25\n\n  tickets:\n    redis_name: support-tickets\n    description: Read-only mirror of resolved support tickets.\n    read_only: true\n\n    search:\n      type: fulltext\n      params:\n        text_scorer: BM25STD\n        stopwords: english\n\n    runtime:\n      text_field_name: body\n      default_limit: 10\n      max_limit: 50","section_id":"multiple-indexes"},{"id":"custom-tool-profiles-ex0","language":"yaml","code":"server:\n  redis_url: redis://localhost:6379\n  builtin_tools:\n    search-records: disabled          # the curated tool replaces it\n\nindexes:\n  tickets:\n    redis_name: support-tickets\n    search:\n      type: fulltext\n    runtime:\n      text_field_name: body\n      default_limit: 5\n      max_limit: 20\n\ncustom_tools:\n  - name: search-resolved-tickets\n    based_on: search-records\n    index: tickets\n    description: >\n      Search resolved customer support tickets by relevance.\n      Use this to find how a similar problem was fixed before.\n    lock:\n      return_fields: [subject, resolution, created_at]\n      filter: { field: status, op: eq, value: resolved }\n    params:\n      limit: { expose: true, max: 10 }\n      filter: { expose: true }","section_id":"custom-tool-profiles"},{"id":"tenant-scoping-with-claim-injection-ex0","language":"yaml","code":"# The RedisVL schema the index was created from\nindex:\n  name: customer-kb\n  prefix: kb\nfields:\n  - name: content\n    type: text\n  - name: org_id\n    type: tag\n    attrs:\n      case_sensitive: true","section_id":"tenant-scoping-with-claim-injection"},{"id":"tenant-scoping-with-claim-injection-ex1","language":"yaml","code":"server:\n  redis_url: redis://localhost:6379\n  builtin_tools:\n    search-records: disabled          # both built-ins reach the index unscoped,\n    upsert-records: disabled          # so the server refuses to start with either on\n  auth:\n    type: jwt\n    jwks_uri: ${MCP_JWKS_URI}\n    issuer: ${MCP_ISSUER}\n    audience: api://redisvl-mcp\n    required_claims: [exp, iat, \"https://acme.example/org\"]\n\nindexes:\n  customer_kb:\n    redis_name: customer-kb\n    search:\n      type: fulltext\n    runtime:\n      text_field_name: content\n\ncustom_tools:\n  - name: search-customer-kb\n    index: customer_kb\n    description: Search this customer's knowledge base.\n    lock:\n      inject:\n        - field: org_id\n          from: claim\n          claim: \"https://acme.example/org\"\n          required: true","section_id":"tenant-scoping-with-claim-injection"},{"id":"tenant-scoping-with-claim-injection-ex2","language":"bash","code":"rvl mcp --config /path/to/mcp_config.yaml --transport streamable-http","section_id":"tenant-scoping-with-claim-injection"},{"id":"list-indexes-ex0","language":"json","code":"{\n  \"indexes\": [\n    {\n      \"id\": \"knowledge\",\n      \"description\": \"Internal runbooks and operational guidance.\",\n      \"upsert_available\": true,\n      \"fields\": [\n        { \"name\": \"title\", \"type\": \"text\" },\n        { \"name\": \"category\", \"type\": \"tag\" },\n        { \"name\": \"rating\", \"type\": \"numeric\" }\n      ],\n      \"limits\": { \"max_limit\": 25 }\n    },\n    {\n      \"id\": \"tickets\",\n      \"description\": \"Read-only mirror of resolved support tickets.\",\n      \"upsert_available\": false,\n      \"fields\": [\n        { \"name\": \"category\", \"type\": \"tag\" }\n      ],\n      \"limits\": { \"max_limit\": 50 }\n    }\n  ]\n}","section_id":"list-indexes"},{"id":"search-records-ex0","language":"json","code":"{\n  \"index\": \"knowledge\",\n  \"query\": \"incident response runbook\",\n  \"limit\": 2,\n  \"offset\": 0,\n  \"filter\": {\n    \"and\": [\n      { \"field\": \"category\", \"op\": \"eq\", \"value\": \"operations\" },\n      { \"field\": \"rating\", \"op\": \"gte\", \"value\": 4 }\n    ]\n  },\n  \"return_fields\": [\"title\", \"content\", \"category\", \"rating\"]\n}","section_id":"search-records"},{"id":"search-records-ex1","language":"json","code":"{\n  \"index\": \"knowledge\",\n  \"search_type\": \"hybrid\",\n  \"offset\": 0,\n  \"limit\": 2,\n  \"results\": [\n    {\n      \"id\": \"knowledge:runbook:eu-failover\",\n      \"score\": 0.82,\n      \"score_type\": \"hybrid_score\",\n      \"record\": {\n        \"title\": \"EU failover runbook\",\n        \"content\": \"Restore traffic after a regional failover.\",\n        \"category\": \"operations\",\n        \"rating\": 5\n      }\n    }\n  ]\n}","section_id":"search-records"},{"id":"upsert-records-ex0","language":"json","code":"{\n  \"index\": \"knowledge\",\n  \"records\": [\n    {\n      \"doc_id\": \"doc-42\",\n      \"content\": \"Updated operational guidance for failover handling.\",\n      \"category\": \"operations\",\n      \"rating\": 5\n    }\n  ],\n  \"id_field\": \"doc_id\"\n}","section_id":"upsert-records"},{"id":"upsert-records-ex1","language":"json","code":"{\n  \"index\": \"knowledge\",\n  \"status\": \"success\",\n  \"keys_upserted\": 1,\n  \"keys\": [\"knowledge:doc-42\"]\n}","section_id":"upsert-records"},{"id":"discovery-first-multi-index-flow-ex0","language":"json","code":"{\n  \"index\": \"knowledge\",\n  \"query\": \"cache invalidation incident\",\n  \"limit\": 3,\n  \"return_fields\": [\"title\", \"content\", \"category\"]\n}","section_id":"discovery-first-multi-index-flow"},{"id":"discovery-first-multi-index-flow-ex1","language":"json","code":"{\n  \"index\": \"knowledge\",\n  \"records\": [\n    { \"doc_id\": \"doc-7\", \"content\": \"New runbook entry\", \"category\": \"operations\" }\n  ],\n  \"id_field\": \"doc_id\"\n}","section_id":"discovery-first-multi-index-flow"},{"id":"read-only-vector-search-ex0","language":"bash","code":"uvx --from redisvl[mcp] rvl mcp --config /path/to/mcp.yaml --read-only","section_id":"read-only-vector-search"},{"id":"read-only-vector-search-ex1","language":"json","code":"{\n  \"query\": \"cache invalidation incident\",\n  \"limit\": 3,\n  \"return_fields\": [\"title\", \"content\", \"category\"]\n}","section_id":"read-only-vector-search"},{"id":"raw-string-filter-ex0","language":"json","code":"{\n  \"query\": \"science\",\n  \"filter\": \"@category:{science}\",\n  \"return_fields\": [\"content\", \"category\"]\n}","section_id":"raw-string-filter"},{"id":"json-dsl-filter-ex0","language":"json","code":"{\n  \"query\": \"science\",\n  \"filter\": {\n    \"and\": [\n      { \"field\": \"category\", \"op\": \"eq\", \"value\": \"science\" },\n      { \"field\": \"rating\", \"op\": \"gte\", \"value\": 4 }\n    ]\n  },\n  \"return_fields\": [\"content\", \"category\", \"rating\"]\n}","section_id":"json-dsl-filter"},{"id":"pagination-and-field-projection-ex0","language":"json","code":"{\n  \"query\": \"science\",\n  \"limit\": 1,\n  \"offset\": 1,\n  \"return_fields\": [\"content\", \"category\"]\n}","section_id":"pagination-and-field-projection"},{"id":"hybrid-search-with-schema_overrides-ex0","language":"yaml","code":"schema_overrides:\n  fields:\n    - name: embedding\n      type: vector\n      attrs:\n        algorithm: flat\n        dims: 1536\n        datatype: float32\n        distance_metric: cosine\n\nsearch:\n  type: hybrid\n  params:\n    text_scorer: BM25STD\n    stopwords: english\n    vector_search_method: KNN\n    combination_method: LINEAR\n    linear_text_weight: 0.3","section_id":"hybrid-search-with-schema_overrides"},{"id":"hybrid-search-with-schema_overrides-ex1","language":"json","code":"{\n  \"query\": \"legacy cache invalidation flow\",\n  \"filter\": { \"field\": \"category\", \"op\": \"eq\", \"value\": \"release-notes\" },\n  \"return_fields\": [\"title\", \"content\", \"release_version\"]\n}","section_id":"hybrid-search-with-schema_overrides"},{"id":"auto-embed-new-records-ex0","language":"json","code":"{\n  \"records\": [\n    {\n      \"content\": \"First upserted document\",\n      \"category\": \"science\",\n      \"rating\": 5\n    },\n    {\n      \"content\": \"Second upserted document\",\n      \"category\": \"health\",\n      \"rating\": 4\n    }\n  ]\n}","section_id":"auto-embed-new-records"},{"id":"update-existing-records-with-id_field-ex0","language":"json","code":"{\n  \"records\": [\n    {\n      \"doc_id\": \"doc-1\",\n      \"content\": \"Updated content\",\n      \"category\": \"engineering\",\n      \"rating\": 5\n    }\n  ],\n  \"id_field\": \"doc_id\"\n}","section_id":"update-existing-records-with-id_field"},{"id":"control-re-embedding-with-skip_embedding_if_present-ex0","language":"json","code":"{\n  \"records\": [\n    {\n      \"doc_id\": \"doc-2\",\n      \"content\": \"Existing content\",\n      \"category\": \"science\",\n      \"rating\": 4\n    }\n  ],\n  \"id_field\": \"doc_id\",\n  \"skip_embedding_if_present\": false\n}","section_id":"control-re-embedding-with-skip_embedding_if_present"},{"id":"plain-writes-without-embedding-ex0","language":"json","code":"{\n  \"records\": [\n    {\n      \"content\": \"Updated FAQ entry\",\n      \"category\": \"support\",\n      \"rating\": 5\n    }\n  ]\n}","section_id":"plain-writes-without-embedding"},{"id":"missing-mcp-dependencies-ex0","language":"bash","code":"pip install redisvl[mcp]","section_id":"missing-mcp-dependencies"}]}
{"schema_version":2,"id":"develop/ai/redisvl/user_guide/how_to_guides/mcp_authentication","title":"Authenticate RedisVL MCP","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/how_to_guides/mcp_authentication/","summary":"","aliases":["/integrate/redisvl/user_guide/how_to_guides/mcp_authentication"],"tags":[],"last_updated":"2026-10-12T09:34:21+02:00","page_type":"content","content_hash":"ab33465189d27b0d68a04765f361d7671b22bf254c2476a411e3e0b2406fd746","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This guide explains how the RedisVL MCP server authenticates clients on its HTTP\ntransports and how it gates read vs write access. It also draws the boundary\nbetween what RedisVL enforces and what belongs in a gateway or policy layer.\n\n> [!NOTE]\n> Authentication applies only to the HTTP transports (`streamable-http`, `sse`).\n> The `stdio` transport is a local subprocess with no network surface and is never\n> authenticated.\n\n> [!NOTE]\n> Authentication is a separate concern from **transport security** (Host/Origin\n> validation), which is always on for the HTTP transports and defends against DNS\n> rebinding independently of auth. See\n> [Transport Security](mcp.md#transport-security-host-origin-validation). Both\n> layers apply together: auth decides *who* may call; the Host/Origin guard\n> rejects requests whose claimed authority is not allowlisted."},{"id":"what-redisvl-enforces","title":"What RedisVL Enforces","role":"content","text":"RedisVL validates a bearer **JWT** that an existing identity provider (IdP)\nissued. It does not run an OAuth authorization server and does not issue tokens.\n\nOn each request it checks:\n\n- **Signature**, against a JWKS endpoint or a static public key.\n- **Issuer** (`iss`), so only tokens from your IdP are accepted.\n- **Audience** (`aud`), so a token minted for a different service cannot be\n  replayed against this server (RFC 8707).\n- **Expiration**: a present `exp` in the past is rejected, and tokens are\n  required to carry `exp` and `iat` (configurable via `required_claims`), so a\n  token with no expiration, which would never expire, is rejected.\n- **Required scopes** to connect, and (optionally) a **read scope** to call\n  `search-records` and a **write scope** to call `upsert-records`.\n\n> [!NOTE]\n> This is **coarse** authorization: it decides whether a caller may connect and whether it may read or write. It does **not** map token claims to a Redis ACL user or a per-tenant index. A custom tool profile can additionally scope its queries to a tenant claim; see [The Authorization Boundary]()."},{"id":"oauth-which-part-redisvl-handles","title":"OAuth: Which Part RedisVL Handles","role":"content","text":"In OAuth terms, RedisVL MCP is a **resource server**: it validates access\ntokens that your identity provider issued. It does not run an OAuth login flow\nand does not issue tokens.\n\n[code example]\n\nSteps 1 and 2 (obtaining the token) are handled by your client and IdP. RedisVL\nonly performs the validation in step 3. As a result:\n\n- It works with any OAuth 2.0 / OIDC provider (for example Auth0, Okta, Azure AD\n  / Entra, Cognito, Keycloak): point `jwks_uri` at the provider and validate its\n  tokens.\n- RedisVL does not broker interactive \"log in with…\" flows and does not mint\n  tokens.\n\nYou would only need more than token validation when you want the server itself\nto drive an interactive browser login (an OAuth proxy), or to act as its own\nauthorization server that issues tokens. Both are out of scope today. If\ninteractive login is ever needed, a single generic `oauth-proxy` option can be\nadded behind the `auth.type` switch. For enterprise and agent deployments where\nthe caller already holds a token, JWT validation is sufficient."},{"id":"request-flow","title":"Request Flow","role":"content","text":"[code example]"},{"id":"configure-jwt-authentication","title":"Configure JWT Authentication","role":"configuration","text":"Add a `server.auth` block to your MCP config. Secrets can be injected with\n`${ENV}` substitution.\n\n[code example]\n\nEvery field is also settable through `REDISVL_MCP_AUTH_*` environment variables,\nwhich take precedence over the YAML block."},{"id":"choosing-the-authorization-claim","title":"Choosing the Authorization Claim","role":"content","text":"Different identity providers carry authorization in different claims. The\ndefault JWT scope claim is `scp` (or `scope`). Some enterprise providers carry\nauthorization in a **`roles`** claim instead, which does not appear in the\nstandard scope set.\n\n[code example]\n\nSet the claim that holds your authorization values so read and write gating\nreads the right place:\n\n[code example]\n\nA token like the following would then pass the read gate, because\n`kb.search.read` is present in `roles`:\n\n[code example]"},{"id":"the-authorization-boundary","title":"The Authorization Boundary","role":"content","text":"RedisVL MCP authenticates the caller, gates read vs write, and can scope every query a custom tool profile runs to a tenant carried in the token. It does **not** translate token claims into a specific Redis ACL user or a per-tenant index: every caller shares the server’s Redis connection, established at startup.\n\nTo scope queries by tenant inside RedisVL, add `lock.inject` to a profile; see Tenant Scoping With Claim Injection in [Run RedisVL MCP](https://redis.io/docs/latest/mcp). That filter is then the only thing separating tenants, so read the threat model in [RedisVL MCP](https://redis.io/docs/latest/../../concepts/mcp) before relying on it.\n\nIsolation enforced by Redis itself, rather than by a query filter, belongs in a **gateway or policy layer** in front of the MCP server, which validates the token, looks up a binding of claim to Redis identity, and injects the matching credentials.\n\n[code example]\n\nUse RedisVL’s JWT validation for authentication, read/write authorization, and tenant-scoped queries. Layer a gateway on top when you need per-tenant Redis ACL enforcement.\n\nWhen such a gateway or reverse proxy terminates the connection and forwards a\nrewritten `Host` header, set `server.transport_security.enabled: false` (or\ndeclare the public host via `server.transport_security.allowed_hosts`) so the\nproxy’s rewritten `Host` is not rejected by the Host/Origin guard."},{"id":"see-also","title":"See Also","role":"related","text":"- [Run RedisVL MCP](https://redis.io/docs/latest/mcp): run and configure the RedisVL MCP server.\n- [RedisVL MCP](https://redis.io/docs/latest/../../concepts/mcp): custom tool profiles, tenant scoping, and its threat model."}],"examples":[{"id":"oauth-which-part-redisvl-handles-ex0","language":"mermaid","code":"flowchart LR\n    Client -->|1 - OAuth login flow| IdP[Identity Provider]\n    IdP -->|2 - issues JWT access token| Client\n    Client -->|3 - Bearer JWT| MCP[RedisVL MCP]\n    MCP -->|validates token| Redis[(Redis)]","section_id":"oauth-which-part-redisvl-handles"},{"id":"request-flow-ex0","language":"mermaid","code":"sequenceDiagram\n    actor User\n    participant IdP as Identity Provider\n    participant MCP as RedisVL MCP Server\n    participant Redis\n\n    User->>IdP: Authenticate\n    IdP-->>User: Signed JWT (iss, aud, scopes/roles)\n    User->>MCP: MCP request + Bearer JWT\n    MCP->>MCP: Validate signature (JWKS / public key)\n    MCP->>MCP: Check issuer + audience\n    alt token invalid / wrong audience / missing connect scope\n        MCP-->>User: 401 Unauthorized\n    else token valid\n        MCP->>MCP: Gate tool by read / write scope\n        alt scope present\n            MCP->>Redis: Search or upsert (single configured ACL user)\n            Redis-->>MCP: Results\n            MCP-->>User: Tool result\n        else scope missing\n            MCP-->>User: Forbidden\n        end\n    end","section_id":"request-flow"},{"id":"configure-jwt-authentication-ex0","language":"yaml","code":"server:\n  redis_url: ${REDIS_URL:-redis://localhost:6379}\n  auth:\n    type: jwt\n    jwks_uri: ${MCP_JWKS_URI}          # or set public_key for a static key\n    issuer: ${MCP_ISSUER}\n    audience: api://redisvl-mcp\n    required_scopes: [kb.read]         # required to connect\n    required_claims: [exp, iat]        # claims every token must carry (default)\n    read_scope: kb.search.read         # required for search-records\n    write_scope: kb.search.write       # required for upsert-records\n\nindexes:\n  knowledge:\n    redis_name: docs_index\n    search:\n      type: fulltext\n    runtime:\n      text_field_name: content","section_id":"configure-jwt-authentication"},{"id":"choosing-the-authorization-claim-ex0","language":"mermaid","code":"flowchart TD\n    A[Validated JWT claims] --> B{authorization_claim}\n    B -->|scp / scope| C[\"access.scopes<br/>e.g. kb.read\"]\n    B -->|roles| D[\"access.claims.roles<br/>e.g. kb.search.read\"]\n    C --> E[Check read_scope / write_scope]\n    D --> E\n    E -->|present| F[Allow tool]\n    E -->|absent| G[Deny tool]","section_id":"choosing-the-authorization-claim"},{"id":"choosing-the-authorization-claim-ex1","language":"yaml","code":"server:\n  auth:\n    type: jwt\n    # ...\n    authorization_claim: roles   # default: scp\n    read_scope: kb.search.read\n    write_scope: kb.search.write","section_id":"choosing-the-authorization-claim"},{"id":"choosing-the-authorization-claim-ex2","language":"json","code":"{\n  \"iss\":   \"https://your-idp.example/{tenant}/v2.0\",\n  \"aud\":   \"api://redisvl-mcp\",\n  \"sub\":   \"nitin\",\n  \"roles\": [\"kb.search.read\"],\n  \"scp\":   \"kb.read\"\n}","section_id":"choosing-the-authorization-claim"},{"id":"the-authorization-boundary-ex0","language":"mermaid","code":"flowchart LR\n    subgraph Gateway[\"Gateway / policy layer (out of scope for RedisVL)\"]\n        T[Validate token] --> M[\"Map claims to<br/>Redis user + index\"]\n    end\n    subgraph RedisVL[\"RedisVL MCP (this guide)\"]\n        A[Validate JWT] --> S[Gate read / write by scope] --> I[\"Inject tenant filter<br/>(profiles with lock.inject)\"]\n    end\n    Client --> Gateway --> RedisVL --> Redis[(Redis)]","section_id":"the-authorization-boundary"}]}
{"schema_version":2,"id":"develop/ai/redisvl/user_guide/how_to_guides/message_history","title":"Manage LLM Message History","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/how_to_guides/message_history/","summary":"","aliases":["/integrate/redisvl/user_guide/how_to_guides/07_message_history","/integrate/redisvl/user_guide/message_history/","/develop/ai/redisvl/user_guide/message_history/"],"tags":[],"last_updated":"2026-10-01T14:49:09Z","page_type":"content","content_hash":"8878efc8ca914865ae6e445ff18af9d307f621d9ea7beac43fdfd5d433e20613","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Large Language Models are inherently stateless with no knowledge of previous interactions. This becomes a challenge when engaging in long conversations that rely on context. The solution is to store and retrieve conversation history with each LLM call.\n\nThis guide demonstrates how to use Redis to structure, store, and retrieve conversational message history."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before you begin, ensure you have:\n- Installed RedisVL: `pip install redisvl`\n- A running Redis instance ([Redis 8+](https://redis.io/downloads/) or [Redis Cloud](https://redis.io/cloud))"},{"id":"what-youll-learn","title":"What You'll Learn","role":"content","text":"By the end of this guide, you will be able to:\n- Store and retrieve conversation messages with `MessageHistory`\n- Manage multiple users and conversations with session tags\n- Use `SemanticMessageHistory` for relevance-based context retrieval\n- Prune incorrect or unwanted messages from conversation history\n\n\n[code example]\n\nTo align with common LLM APIs, Redis stores messages with `role` and `content` fields.\nThe supported roles are \"system\", \"user\" and \"llm\".\n\nYou can store messages one at a time or all at once.\n\n\n[code example]\n\nAt any point we can retrieve the recent history of the conversation. It will be ordered by entry time.\n\n\n[code example]\n\n    {'role': 'llm', 'content': 'The capital is Paris.'}\n    {'role': 'user', 'content': 'And what is the capital of Spain?'}\n    {'role': 'llm', 'content': 'The capital is Madrid.'}\n    {'role': 'user', 'content': 'What is the population of Great Britain?'}\n    {'role': 'llm', 'content': 'As of 2023 the population of Great Britain is approximately 67 million people.'}\n\n\nIn many LLM flows, conversations progress through a series of prompt and response pairs. MessageHistory provides a `store()` convenience function to add these efficiently.\n\n\n[code example]\n\n    {'role': 'user', 'content': 'And what is the capital of Spain?'}\n    {'role': 'llm', 'content': 'The capital is Madrid.'}\n    {'role': 'user', 'content': 'What is the population of Great Britain?'}\n    {'role': 'llm', 'content': 'As of 2023 the population of Great Britain is approximately 67 million people.'}\n    {'role': 'user', 'content': 'what is the size of England compared to Portugal?'}\n    {'role': 'llm', 'content': 'England is larger in land area than Portal by about 15000 square miles.'}"},{"id":"managing-multiple-users-and-conversations","title":"Managing multiple users and conversations","role":"content","text":"For applications that need to handle multiple conversations concurrently, Redis supports tagging messages to keep conversations separated.\n\n\n[code example]\n\n    {'role': 'system', 'content': 'You are a helpful algebra tutor, giving simple answers to math problems.'}\n    {'role': 'user', 'content': 'What is the value of x in the equation 2x + 3 = 7?'}\n    {'role': 'llm', 'content': 'The value of x is 2.'}\n    {'role': 'user', 'content': 'What is the value of y in the equation 3y - 5 = 7?'}\n    {'role': 'llm', 'content': 'The value of y is 4.'}"},{"id":"semantic-message-history","title":"Semantic message history","role":"content","text":"For longer conversations our list of messages keeps growing. Since LLMs are stateless we have to continue to pass this conversation history on each subsequent call to ensure the LLM has the correct context.\n\nA typical flow looks like this:\n[code example]\n\nThis works, but as context keeps growing so too does our LLM token count, which increases latency and cost.\n\nConversation histories can be truncated, but that can lead to losing relevant information that appeared early on.\n\nA better solution is to pass only the relevant conversational context on each subsequent call.\n\nFor this, RedisVL has the `SemanticMessageHistory`, which uses vector similarity search to return only semantically relevant sections of the conversation.\n\n\n[code example]\n\n\n[code example]\n\n    {'role': 'user', 'content': 'what is the size of England compared to Portugal?'}\n\n\nYou can adjust the degree of semantic similarity needed to be included in your context.\n\nSetting a distance threshold close to 0.0 will require an exact semantic match, while a distance threshold of 2.0 will include everything (Redis COSINE distance range is [0-2]).\n\n\n[code example]\n\n    {'role': 'user', 'content': 'what is the size of England compared to Portugal?'}\n    {'role': 'llm', 'content': 'England is larger in land area than Portal by about 15000 square miles.'}\n    {'role': 'user', 'content': 'What is the population of Great Britain?'}\n    {'role': 'llm', 'content': 'As of 2023 the population of Great Britain is approximately 67 million people.'}\n    {'role': 'user', 'content': 'And what is the capital of Spain?'}"},{"id":"conversation-control","title":"Conversation control","role":"content","text":"LLMs can hallucinate on occasion and when this happens it can be useful to prune incorrect information from conversational histories so this incorrect information doesn't continue to be passed as context.\n\n\n[code example]\n\n    {'role': 'user', 'content': 'What is the population of Great Britain?'}\n    {'role': 'llm', 'content': 'As of 2023 the population of Great Britain is approximately 67 million people.'}\n    {'role': 'user', 'content': 'what is the size of England compared to Portugal?'}\n    {'role': 'llm', 'content': 'England is larger in land area than Portal by about 15000 square miles.'}\n    {'role': 'user', 'content': 'what is the smallest country in Europe?'}"},{"id":"retrieving-message-counts","title":"Retrieving message counts","role":"content","text":"To get the total number of  messages stored in a session, use the `.count()` method.  \nYou can optionally pass a `session_tag` argument to retrieve the count for a different conversation session.\n\n\n[code example]\n\n    Total messages in the session: 7"},{"id":"next-steps","title":"Next Steps","role":"content","text":"Now that you understand message history management, explore these related guides:\n\n- [Cache LLM Responses](https://redis.io/docs/latest/llmcache) - Reduce API costs with semantic caching\n- [Route Queries with SemanticRouter](https://redis.io/docs/latest/semantic_router) - Classify user queries to routes\n- [Create Embeddings with Vectorizers](https://redis.io/docs/latest/vectorizers) - Use different embedding providers"},{"id":"cleanup","title":"Cleanup","role":"content","text":"[code example]"}],"examples":[{"id":"what-youll-learn-ex0","language":"python","code":"from redisvl.extensions.message_history import MessageHistory\n\nchat_history = MessageHistory(name='student tutor')","section_id":"what-youll-learn"},{"id":"what-youll-learn-ex1","language":"python","code":"chat_history.add_message({\"role\":\"system\", \"content\":\"You are a helpful geography tutor, giving simple and short answers to questions about European countries.\"})\nchat_history.add_messages([\n    {\"role\":\"user\", \"content\":\"What is the capital of France?\"},\n    {\"role\":\"llm\", \"content\":\"The capital is Paris.\"},\n    {\"role\":\"user\", \"content\":\"And what is the capital of Spain?\"},\n    {\"role\":\"llm\", \"content\":\"The capital is Madrid.\"},\n    {\"role\":\"user\", \"content\":\"What is the population of Great Britain?\"},\n    {\"role\":\"llm\", \"content\":\"As of 2023 the population of Great Britain is approximately 67 million people.\"},]\n    )","section_id":"what-youll-learn"},{"id":"what-youll-learn-ex2","language":"python","code":"context = chat_history.get_recent()\nfor message in context:\n    print(message)","section_id":"what-youll-learn"},{"id":"what-youll-learn-ex3","language":"python","code":"prompt = \"what is the size of England compared to Portugal?\"\nresponse = \"England is larger in land area than Portal by about 15000 square miles.\"\nchat_history.store(prompt, response)\n\ncontext = chat_history.get_recent(top_k=6)\nfor message in context:\n    print(message)","section_id":"what-youll-learn"},{"id":"managing-multiple-users-and-conversations-ex0","language":"python","code":"chat_history.add_message({\"role\":\"system\", \"content\":\"You are a helpful algebra tutor, giving simple answers to math problems.\"}, session_tag='student two')\nchat_history.add_messages([\n    {\"role\":\"user\", \"content\":\"What is the value of x in the equation 2x + 3 = 7?\"},\n    {\"role\":\"llm\", \"content\":\"The value of x is 2.\"},\n    {\"role\":\"user\", \"content\":\"What is the value of y in the equation 3y - 5 = 7?\"},\n    {\"role\":\"llm\", \"content\":\"The value of y is 4.\"}],\n    session_tag='student two'\n    )\n\nfor math_message in chat_history.get_recent(session_tag='student two'):\n    print(math_message)","section_id":"managing-multiple-users-and-conversations"},{"id":"semantic-message-history-ex0","language":"plaintext","code":"while True:\n    prompt = input('enter your next question')\n    context = chat_history.get_recent()\n    response = LLM_api_call(prompt=prompt, context=context)\n    chat_history.store(prompt, response)","section_id":"semantic-message-history"},{"id":"semantic-message-history-ex1","language":"python","code":"from redisvl.extensions.message_history import SemanticMessageHistory\nsemantic_history = SemanticMessageHistory(name='tutor')\n\nsemantic_history.add_messages(chat_history.get_recent(top_k=8))","section_id":"semantic-message-history"},{"id":"semantic-message-history-ex2","language":"python","code":"prompt = \"what have I learned about the size of England?\"\nsemantic_history.set_distance_threshold(0.35)\ncontext = semantic_history.get_relevant(prompt)\nfor message in context:\n    print(message)","section_id":"semantic-message-history"},{"id":"semantic-message-history-ex3","language":"python","code":"semantic_history.set_distance_threshold(0.7)\n\nlarger_context = semantic_history.get_relevant(prompt)\nfor message in larger_context:\n    print(message)","section_id":"semantic-message-history"},{"id":"conversation-control-ex0","language":"python","code":"semantic_history.store(\n    prompt=\"what is the smallest country in Europe?\",\n    response=\"Monaco is the smallest country in Europe at 0.78 square miles.\" # Incorrect. Vatican City is the smallest country in Europe\n)\n\n# get the key of the incorrect message\ncontext = semantic_history.get_recent(top_k=1, raw=True)\nbad_key = context[0]['entry_id']\nsemantic_history.drop(bad_key)\n\ncorrected_context = semantic_history.get_recent()\nfor message in corrected_context:\n    print(message)","section_id":"conversation-control"},{"id":"retrieving-message-counts-ex0","language":"python","code":"print(f\"Total messages in the session: {chat_history.count()}\")","section_id":"retrieving-message-counts"},{"id":"cleanup-ex0","language":"python","code":"chat_history.clear()\nsemantic_history.clear()","section_id":"cleanup"}]}
{"schema_version":2,"id":"develop/ai/redisvl/user_guide/how_to_guides/migrate-indexes","title":"Migrate an Index","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/how_to_guides/migrate-indexes/","summary":"","aliases":["/integrate/redisvl/user_guide/how_to_guides/migrate-indexes"],"tags":[],"last_updated":"2026-10-05T16:11:44+01:00","page_type":"content","content_hash":"c693c6e89c80b35b9a8f02c0f5024b12fbf02430560288f88e2a5ad854bfdc04","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!WARNING]\n> The index migrator is an **experimental** feature. APIs, CLI commands, and on-disk formats (plans, checkpoints, backups) may change in future releases. Review migration plans carefully before applying to production indexes.\n\nThis guide shows how to safely change your index schema using the RedisVL migrator."},{"id":"quick-start","title":"Quick Start","role":"content","text":"Add a field to your index in 4 commands:\n\n[code example]"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"- Redis with the Search module (Redis Stack, Redis Cloud, or Redis Software)\n- An existing index to migrate\n- `redisvl` installed (`pip install redisvl`)\n\n[code example]\n\n**Note:** Redis 8.0+ is required for INT8/UINT8 vector datatypes. SVS-VAMANA algorithm requires Redis 8.2+ and Intel AVX-512 hardware."},{"id":"how-it-works","title":"How It Works","role":"content","text":"Every migration follows the same three-phase flow: **describe what changed** (the patch),\n**generate a plan** (diffing the patch against the live schema), and **execute the plan**."},{"id":"single-index-flow-wizardplan-then-apply","title":"Single-Index Flow: wizard/plan then apply","role":"content","text":"[code example]\n\n**Phase 1: Build a SchemaPatch.**\nA patch is a small YAML file that declares *what you want to change*, not the full target schema.\nYou can build it interactively with `rvl migrate wizard`, or write it by hand. The patch has\nfive sections, each optional:\n\n| Patch Section   | What it does                                                                               |\n|-----------------|--------------------------------------------------------------------------------------------|\n| `add_fields`    | Adds new field definitions to the index                                                    |\n| `remove_fields` | Removes fields from the index (document data is kept, just no longer indexed)              |\n| `rename_fields` | Renames fields in both the index schema and all documents (HGET old, HSET new, HDEL old)   |\n| `update_fields` | Modifies field attributes: algorithm, datatype, distance metric, sortable, separator, etc. |\n| `index`         | Changes the index name or key prefix                                                       |\n\n**Phase 2: Generate a MigrationPlan.**\nThe planner connects to Redis, snapshots the live index schema and stats,\nthen merges the patch into the source schema to produce a `merged_target_schema`.\nIt classifies every change as supported or blocked and extracts rename operations.\n\nThe plan YAML contains:\n\n- `source`: frozen snapshot of the live index at planning time (schema, stats, key sample, prefixes)\n- `requested_changes`: the patch that was applied\n- `merged_target_schema`: source + patch = what the index will look like after migration\n- `diff_classification`: whether the migration is supported and any blocked reasons\n- `rename_operations`: extracted index renames, prefix changes, and field renames\n- `warnings`: any important notes (downtime required, lossy quantization, etc.)\n\nThe same patch produces different plans per index because each index has a different source schema.\n\n**Phase 3: Apply.**\nThe executor reads the plan and runs the migration steps:\n\n1. Enumerate keys (SCAN with source prefix)\n2. Field renames (pipelined HGET/HSET/HDEL)\n3. Prepare vector backups, if hash vector bytes will be quantized\n4. Drop index (FT.DROPINDEX, documents are preserved)\n5. Key prefix renames (RENAME or DUMP/RESTORE for cluster)\n6. Quantize vectors from backup (pipelined read/convert/write)\n7. Create index (FT.CREATE with merged target schema)\n8. Wait for re-indexing to complete\n9. Validate (doc count, schema match, key sample)\n\n`--backup-dir` / `backup_dir` is required before any apply starts. For\nquantization, the directory stores original vector bytes for resume and\nrollback. For index-only migrations, the directory is still validated and\nrecorded in the report, but no vector backup files are written.\n\n> [!WARNING]\n> Hash vector quantization is supported only when the Redis keys being\n> quantized are not also indexed by another live RediSearch index that\n> expects the old vector datatype. Quantization rewrites vector bytes in\n> the document itself; any other index that covers the same key sees those\n> new bytes and may silently drop the document or fail to index it. If the\n> same documents are intentionally shared across multiple indexes, do not\n> use the migrator for that quantization change. Use an application-level\n> migration that creates new keys or fields and coordinates every affected\n> index schema."},{"id":"batch-flow-wizardplan-then-batch-plan-then-batch-apply","title":"Batch Flow: wizard/plan then batch-plan then batch-apply","role":"content","text":"For applying the same change across multiple indexes:\n\n[code example]\n\nThe batch planner takes a **single shared patch** and tests it against every target index.\nIndexes where the patch doesn’t apply (e.g., it references a field that doesn’t exist in that\nindex, or the change is blocked) are marked `applicable: false` with a `skip_reason` and skipped\nduring apply. Each applicable index gets its own full `MigrationPlan` written to disk.\n\nThis means you can review each per-index plan individually before running `batch-apply`."},{"id":"step-1-discover-available-indexes","title":"Step 1: Discover Available Indexes","role":"content","text":"[code example]\n\n**Example output:**\n\n[code example]"},{"id":"step-2-build-your-schema-change","title":"Step 2: Build Your Schema Change","role":"content","text":"Choose one of these approaches:"},{"id":"option-a-use-the-wizard-recommended","title":"Option A: Use the Wizard (Recommended)","role":"parameters","text":"The wizard guides you through building a migration interactively. Run:\n\n[code example]\n\n**Example wizard session (adding a field):**\n\n[code example]\n\n**Example wizard session (quantizing vectors):**\n\n[code example]"},{"id":"option-b-write-a-schema-patch-yaml","title":"Option B: Write a Schema Patch (YAML)","role":"parameters","text":"Create `schema_patch.yaml` manually:\n\n[code example]\n\nThen generate the plan:\n\n[code example]"},{"id":"option-c-provide-a-target-schema","title":"Option C: Provide a Target Schema","role":"parameters","text":"If you have the complete target schema, use it directly:\n\n[code example]"},{"id":"step-3-review-the-migration-plan","title":"Step 3: Review the Migration Plan","role":"content","text":"Before applying, review `migration_plan.yaml`:\n\n[code example]\n\n**Key fields to check:**\n\n- `diff_classification.supported` - Must be `true` to proceed\n- `diff_classification.blocked_reasons` - Must be empty\n- `warnings` - Top-level warnings about the migration\n- `merged_target_schema` - The final schema after migration"},{"id":"understanding-downtime-requirements","title":"Understanding Downtime Requirements","role":"content","text":"**CRITICAL**: During a `drop_recreate` migration, your application must:\n\n| Requirement      | Description                                              |\n|------------------|----------------------------------------------------------|\n| **Pause reads**  | Index is unavailable during migration                    |\n| **Pause writes** | Writes during migration may be missed or cause conflicts |"},{"id":"why-both-reads-and-writes-must-be-paused","title":"Why Both Reads AND Writes Must Be Paused","role":"content","text":"- **Reads**: The index definition is dropped and recreated. Any queries during this window will fail.\n- **Writes**: Redis updates indexes synchronously on every write. If your app writes documents while the index is dropped, those writes are not indexed. Additionally, if you’re quantizing vectors (float32 → float16), concurrent writes may conflict with the migration’s re-encoding process."},{"id":"what-downtime-means","title":"What \"Downtime\" Means","role":"content","text":"| Downtime Type              | Reads   | Writes     | Safe?   |\n|----------------------------|---------|------------|---------|\n| Full quiesce (recommended) | Stopped | Stopped    | **YES** |\n| Read-only pause            | Stopped | Continuing | **NO**  |\n| Active                     | Active  | Active     | **NO**  |"},{"id":"recovery-from-interrupted-migration","title":"Recovery from Interrupted Migration","role":"content","text":"| Interruption Point                | Documents           | Index      | Recovery                                                  |\n|-----------------------------------|---------------------|------------|-----------------------------------------------------------|\n| After drop, before quantize       | Unchanged           | **None**   | Re-run apply with the same `--backup-dir`                 |\n| During quantization               | Partially quantized | **None**   | Re-run with same `--backup-dir` to resume from last batch |\n| After quantization, before create | Quantized           | **None**   | Re-run apply (will recreate index)                        |\n| After create                      | Correct             | Rebuilding | Wait for index ready                                      |\n\nThe underlying documents are **never deleted** by `drop_recreate` mode. `--backup-dir` is required for apply and enables crash-safe recovery for vector quantization. See [Crash-safe resume for quantization]() below."},{"id":"step-4-apply-the-migration","title":"Step 4: Apply the Migration","role":"content","text":"The `apply` command executes the migration. The index will be temporarily unavailable during the drop-recreate process.\n\n[code example]"},{"id":"what-apply-does","title":"What `apply` does","role":"content","text":"The migration executor follows this sequence:\n\n**STEP 1: Enumerate keys** (before any modifications)\n\n- Discovers all document keys belonging to the source index\n- Uses `FT.AGGREGATE WITHCURSOR` for efficient enumeration\n- Falls back to `SCAN` if the index has indexing failures\n- Keys are stored in memory for quantization or rename operations\n\n**STEP 2: Field renames** (if renaming fields)\n\n- Renames document fields before the source index is dropped\n- Uses pipelined `HGET`/`HSET`/`HDEL` for Hash storage or JSON path updates for JSON storage\n- Skipped if the plan has no field rename operations\n\n**STEP 3: Back up original vectors** (if hash vector bytes will be quantized)\n\n- Single-worker hash quantization writes original vector bytes to `<backup-dir>` before the index is dropped\n- Multi-worker hash quantization writes per-worker backup shards during the quantization phase after the drop\n- JSON datatype changes and index-only migrations validate and record `--backup-dir` but do not write vector backup files\n\n**STEP 4: Drop source index**\n\n- Issues `FT.DROPINDEX` to remove the index structure\n- **The underlying documents remain in Redis** - only the index metadata is deleted\n- After this point, the index is unavailable until the target index is recreated and ready\n\n**STEP 5: Key renames** (if changing key prefix)\n\n- If the migration changes the key prefix, renames each key from old prefix to new prefix\n- Skipped if no prefix change\n\n**STEP 6: Quantize vectors** (if changing hash vector datatype)\n\n- For each document in the enumerated key list:\n  - Reads the document (including the old vector)\n  - Converts the vector to the new datatype (e.g., float32 → float16)\n  - Writes back the converted vector to the same document\n- Processes documents in batches of 500 using Redis pipelines\n- Skipped for JSON storage (vectors are re-indexed automatically on recreate)\n- **Backup support**: `--backup-dir` is required and enables crash-safe recovery and rollback for vector quantization\n- **Shared-key limitation**: unsupported if the same Redis keys are also\n  indexed by another live index that expects the old vector datatype\n\n**STEP 7: Create target index**\n\n- Issues `FT.CREATE` with the merged target schema\n- Redis begins background indexing of existing documents\n\n**STEP 8: Wait for re-indexing**\n\n- Polls `FT.INFO` until indexing completes\n- The index becomes available for queries when this completes\n\n**Summary**: The migration preserves all documents, drops only the index structure, performs any document-level transformations (quantization, renames), then recreates the index with the new schema."},{"id":"async-execution-for-large-migrations","title":"Async execution for large migrations","role":"content","text":"For large migrations (especially those involving vector quantization), use the `--async` flag:\n\n[code example]\n\n**What becomes async:**\n\n- Document enumeration during quantization (uses `FT.AGGREGATE WITHCURSOR` for index-specific enumeration, falling back to SCAN only if indexing failures exist)\n- Vector read/write operations (sequential async HGET, batched HSET via pipeline)\n- Index readiness polling (uses `asyncio.sleep()` instead of blocking)\n- Validation checks\n\n**What stays sync:**\n\n- CLI prompts and user interaction\n- YAML file reading/writing\n- Progress display\n\n**When to use async:**\n\n- Quantizing millions of vectors (float32 to float16)\n- Integrating into an async application\n\nFor most migrations (index-only changes, small datasets), sync mode is sufficient and simpler.\n\nSee [Index Migrations](https://redis.io/docs/latest/../../concepts/index-migrations) for detailed async vs sync guidance."},{"id":"crash-safe-resume-for-quantization","title":"Crash-safe resume for quantization","role":"content","text":"When migrating large datasets with vector quantization (e.g. float32 to float16), the re-encoding step can take minutes or hours. If the process is interrupted (crash, network drop, OOM kill), you don’t want to start over. The `--backup-dir` flag enables crash-safe recovery.\n\n#### How it works\n\nFor hash vector datatype changes, the migrator saves original vector bytes to disk before mutating them. Single-worker migrations create two files:\n\n[code example]\n\nMulti-worker migrations also create a `.manifest` file at the canonical\nbackup path. The manifest records worker shard paths and key slices so a\nretry can resume even if the source index was already dropped.\n\nThe **header file** is a small JSON file that tracks progress through a state machine:\n\n[code example]\n\n- **dump**: original vectors are being read from Redis and written to the data file, one batch at a time\n- **ready**: all original vectors have been backed up; the source index may still be live\n- **index_dropped**: the source index definition has been dropped, but vectors have not all been rewritten\n- **active**: quantization is in progress; the header tracks which batches have been written back to Redis\n- **completed**: all batches have been quantized; target index creation may still be pending\n- **target_created**: the target index was recreated and Redis is re-indexing or ready for validation\n- **validated**: post-migration validation passed\n\nThe header is atomically updated (temp file + rename) after every batch, so a crash never corrupts it.\n\nThe **data file** is append-only binary. Each batch is stored as a 4-byte big-endian length prefix followed by a pickled blob containing the batch’s keys and their original vector bytes.\n\nOn resume, the executor loads the header, sees how many batches were already quantized (`quantize_completed_batches`), and skips ahead in the data file to continue from the next unfinished batch.\n\n**Disk usage:** approximately `num_docs × dims × bytes_per_element`. For example, 1M docs with 768-dim float32 vectors ≈ 2.9 GB.\n\n#### Step-by-step: using crash-safe resume\n\n**1. Estimate disk space (dry-run, no mutations):**\n\n[code example]\n\nExample output:\n\n[code example]\n\nIf AOF is enabled:\n\n[code example]\n\n**2. Apply with backup enabled:**\n\n[code example]\n\nThe `--backup-dir` flag takes a directory path. If no backup exists there, a new one is created. If one already exists (from a previous interrupted run), the migrator resumes from where it left off. A `completed` backup is treated as a no-op resume only when the live index already matches the target schema; after rollback, the live index matches the source schema, so the old completed backup is treated as stale and a fresh backup is written.\n\n**3. If the process crashes or is interrupted:**\n\nThe header file will contain the progress:\n\n[code example]\n\nThis tells you: all 2000 batches of original vectors were backed up, and 900 of them have been quantized so far.\n\n**4. Resume the migration:**\n\nRe-run the exact same command:\n\n[code example]\n\nThe migrator will:\n\n- Detect the existing backup and skip already-quantized batches\n- Continue quantizing from batch 901 onward\n- Print progress like `Quantize vectors: 450,000/1,000,000 docs`\n\n**5. On successful completion:**\n\nThe backup phase is set to `completed`. Backup files are **always retained** on disk for post-migration auditing and rollback. Delete them manually from `--backup-dir` once you have verified the migrated data and no longer need a recovery path.\n\n#### Limitations\n\n- **Same-width conversions** (float16 to bfloat16, or int8 to uint8) are **not supported** for resume. These conversions cannot be detected by byte-width inspection, so idempotent skip is impossible.\n- **Shared keys across indexes** are **not supported** for hash vector\n  quantization. The migrator mutates vector bytes in the Redis document\n  key; if another index also covers that key and still expects the old\n  datatype, the document may be dropped from that index or fail to\n  re-index.\n- **JSON storage** does not need vector re-encoding (Redis re-indexes JSON vectors on `FT.CREATE`). The backup directory is still required, validated, and recorded, but no vector backup files are written.\n- The backup must match the migration plan. If you change the plan, delete the old backup directory and start fresh."},{"id":"step-5-validate-the-result","title":"Step 5: Validate the Result","role":"content","text":"Validation happens automatically during `apply`, but you can run it separately:\n\n[code example]\n\n**Validation checks:**\n\n- Live schema matches `merged_target_schema`\n- Document count matches the source snapshot\n- Sampled keys still exist\n- No increase in indexing failures"},{"id":"whats-supported","title":"What’s Supported","role":"content","text":"| Change                                                   | Supported   | Notes                                                                                                             |\n|----------------------------------------------------------|-------------|-------------------------------------------------------------------------------------------------------------------|\n| Add text/tag/numeric/geo field                           | ✅          |                                                                                                                   |\n| Remove a field                                           | ✅          |                                                                                                                   |\n| Rename a field                                           | ✅          | Renames field in all documents                                                                                    |\n| Change key prefix                                        | ✅          | Renames keys via RENAME command                                                                                   |\n| Rename the index                                         | ✅          | Index-only                                                                                                        |\n| Make a field sortable                                    | ✅          |                                                                                                                   |\n| Change field options (separator, stemming)               | ✅          |                                                                                                                   |\n| Change vector algorithm (FLAT ↔ HNSW ↔ SVS-VAMANA)       | ✅          | Index-only                                                                                                        |\n| Change distance metric (COSINE ↔ L2 ↔ IP)                | ✅          | Index-only                                                                                                        |\n| Tune HNSW parameters (M, EF_CONSTRUCTION)                | ✅          | Index-only                                                                                                        |\n| Quantize vectors (float32 → float16/bfloat16/int8/uint8) | ✅          | Auto re-encode; unsupported when the same Redis keys are indexed by another live index expecting the old datatype |"},{"id":"whats-blocked","title":"What’s Blocked","role":"content","text":"| Change                            | Why                           | Workaround                           |\n|-----------------------------------|-------------------------------|--------------------------------------|\n| Change vector dimensions          | Requires re-embedding         | Re-embed with new model, reload data |\n| Change storage type (hash ↔ JSON) | Different data format         | Export, transform, reload            |\n| Add a new vector field            | Requires vectors for all docs | Add vectors first, then migrate      |"},{"id":"cli-reference","title":"CLI Reference","role":"content","text":""},{"id":"single-index-commands","title":"Single-Index Commands","role":"content","text":"| Command                | Description                                   |\n|------------------------|-----------------------------------------------|\n| `rvl migrate wizard`   | Build a migration interactively               |\n| `rvl migrate plan`     | Generate a migration plan                     |\n| `rvl migrate apply`    | Execute a migration                           |\n| `rvl migrate estimate` | Estimate disk space for a migration (dry-run) |\n| `rvl migrate validate` | Verify a migration result                     |"},{"id":"batch-commands","title":"Batch Commands","role":"content","text":"| Command                    | Description                   |\n|----------------------------|-------------------------------|\n| `rvl migrate batch-plan`   | Create a batch migration plan |\n| `rvl migrate batch-apply`  | Execute a batch migration     |\n| `rvl migrate batch-resume` | Resume an interrupted batch   |\n| `rvl migrate batch-status` | Check batch progress          |\n\n**Common flags:**\n\n- `--url` : Redis connection URL\n- `--index` : Index name to migrate\n- `--plan` / `--plan-out` : Path to migration plan\n- `--async` : Use async executor for large migrations (apply only)\n- `--report-out` : Path for validation report\n- `--benchmark-out` : Path for performance metrics\n\n**Apply flags (quantization & reliability):**\n\n- `--backup-dir <dir>` : Required migration backup directory. Hash vector datatype changes write vector backup files there for resume and rollback; index-only and JSON migrations validate and record the directory without writing vector backup files.\n- `--batch-size <N>` : Keys per pipeline batch (default 500). Values 200 to 1000 are typical.\n- `--workers <N>` : Parallel quantization workers (default 1). Each worker opens its own Redis connection. See [Performance]() for guidance.\n\n**Batch-specific flags:**\n\n- `--pattern` : Glob pattern to match index names (e.g., `*_idx`)\n- `--indexes` : Explicit list of index names\n- `--indexes-file` : File containing index names (one per line)\n- `--schema-patch` : Path to shared schema patch YAML\n- `--state` : Path to batch state file for resume\n- `--failure-policy` : `fail_fast` or `continue_on_error`\n- `--accept-data-loss` : Required for quantization (lossy changes)\n- `--retry-failed` : Retry previously failed indexes on resume"},{"id":"troubleshooting","title":"Troubleshooting","role":"errors","text":""},{"id":"migration-blocked-unsupported-change","title":"Migration blocked: \"unsupported change\"","role":"content","text":"The planner detected a change that requires data transformation. Check `diff_classification.blocked_reasons` in the plan for details."},{"id":"apply-failed-source-schema-mismatch","title":"Apply failed: \"source schema mismatch\"","role":"content","text":"The live index schema changed since the plan was generated. Re-run `rvl migrate plan` to create a fresh plan."},{"id":"apply-failed-timeout-waiting-for-index-ready","title":"Apply failed: \"timeout waiting for index ready\"","role":"content","text":"The index is taking longer to rebuild than expected. This can happen with large datasets. Check Redis logs and consider increasing the timeout or running during lower traffic periods."},{"id":"validation-failed-document-count-mismatch","title":"Validation failed: \"document count mismatch\"","role":"content","text":"Documents were added or removed between plan and apply. This is expected if your application is actively writing. Re-run `plan` and `apply` during a quieter period when the document count is stable, or verify the mismatch is due only to normal application traffic."},{"id":"quantized-documents-disappeared-from-another-index","title":"Quantized documents disappeared from another index","role":"content","text":"This topology is unsupported. Hash vector quantization rewrites vector\nbytes in the Redis document key. If another live RediSearch index also\ncovers that key and still expects the old vector datatype, Redis may drop\nthat document from the other index or report indexing failures for it.\n\nRecover by rolling back the vector bytes from the migration backup, then\nrecreate any affected index schemas. To perform the change safely, use an\napplication-level migration that writes new physical keys or new vector\nfields and coordinates all affected indexes before switching traffic."},{"id":"batch-plan-failed-overlapping-indexes-detected","title":"batch-plan failed: \"overlapping indexes detected\"","role":"content","text":"`batch-plan` refuses to write a plan when two or more applicable indexes\nshare a key prefix (one prefix is a literal string-prefix of the other,\nmatching `FT.CREATE PREFIX` semantics). Running such a batch would\ndouble-quantize the shared keys and corrupt vector data. The error lists\neach conflicting index pair under a `Conflicts:` section:\n\n[code example]\n\nSplit the selected indexes into prefix-disjoint groups (for example,\n`prod_*` separately from `staging_*`) and run `batch-plan` once per group.\nIndexes that are skipped for other reasons (e.g. `applicable: false`\nbecause a field is missing) do not participate in this check."},{"id":"how-to-recover-from-a-failed-migration","title":"How to recover from a failed migration","role":"content","text":"If `apply` fails mid-migration:\n\n1. **Check if the index exists:** `rvl index info --index myindex`\n2. **If the index exists but is wrong:** Re-run `apply` with the same plan\n3. **If the index was dropped:** Recreate it from the plan’s `merged_target_schema`\n\nThe underlying documents are never deleted by `drop_recreate`."},{"id":"backup-resume--rollback","title":"Backup, Resume & Rollback","role":"content","text":""},{"id":"how-backups-work","title":"How Backups Work","role":"content","text":"`--backup-dir` / `backup_dir` is required for all migrations. If it is omitted\nor empty, the executor raises `ValueError` before any migration starts.\nMigration reports include the resolved backup directory and backup file\nprefixes. Batch checkpoint state also stores the backup directory used by the\nrun, and resume refuses a different directory for the same checkpoint.\n\nFor hash vector datatype changes, the migration executor saves **original\nvector bytes** to disk before mutating them. This enables two key capabilities:\n\n1. **Crash-safe resume**: if the process dies mid-migration, re-running the\n   same command with the same `--backup-dir` automatically detects partial\n   progress and resumes from the last completed batch.\n2. **Manual rollback**: the backup files contain the original (pre-quantization)\n   vector values, which can be restored to undo a migration.\n\nFor index-only migrations and JSON datatype changes, the directory is still\nvalidated and recorded, but no `.header` or `.data` vector backup files are\nwritten.\n\nBackup files are written to the specified directory with this layout:\n\n[code example]\n\n**Disk usage:** approximately `num_docs × dims × bytes_per_element`.\nFor example, 1M docs with 768-dim float32 vectors ≈ 2.9 GB.\n\nBackup files are **always retained** on disk after a successful migration\nso they remain available for post-migration auditing and rollback. Delete\nthe files manually from the backup directory once you no longer need a\nrecovery path."},{"id":"crash-safe-resume","title":"Crash-Safe Resume","role":"content","text":"If a migration is interrupted (crash, network error, Ctrl+C), simply re-run\nthe exact same command:\n\n[code example]\n\nThe executor detects the existing backup header, reads how many batches were\ncompleted, and resumes from the next unfinished batch. No data is duplicated\nor lost. If a retained completed backup is found after rollback, the executor\ndoes not skip the migration unless the live index already matches the target\nschema; it treats the completed backup as stale and starts a fresh backup.\n\n> [!NOTE]\n> **Single-worker vs multi-worker resume:** In single-worker mode, the full\n> backup is written *before* the index is dropped, so a crash at any point\n> leaves a complete backup on disk. In multi-worker mode, dump and quantize\n> are fused (each worker reads, backs up, and converts its shard in one pass\n> *after* the index drop). A crash during this fused phase may leave partial\n> backup shards. Re-running detects and resumes from partial state."},{"id":"rollback","title":"Rollback","role":"content","text":"If you need to undo a quantization migration and restore original vectors,\nuse the `rollback` command:\n\n[code example]\n\nThis reads every batch from the backup files and pipeline-HSETs the original\n(pre-quantization) vector bytes back into Redis. After rollback completes:\n\n- Your vector data is restored to its original datatype\n- You will need to **manually recreate the original index schema** if the\n  index was changed during migration (the rollback command restores data\n  only, not the index definition)\n\n[code example]\n\n> [!NOTE]\n> Rollback requires that the backup directory still contains the original\n> backup files. Backups are retained automatically after migration; do not\n> delete the directory until you are certain rollback is no longer needed."},{"id":"python-api-for-rollback","title":"Python API for Rollback","role":"content","text":"[code example]"},{"id":"python-api","title":"Python API","role":"content","text":"For programmatic migrations, use the migration classes directly:"},{"id":"sync-api","title":"Sync API","role":"content","text":"[code example]\n\nWith backup and multi-worker quantization:\n\n[code example]"},{"id":"async-api","title":"Async API","role":"content","text":"[code example]"},{"id":"batch-migration","title":"Batch Migration","role":"content","text":"When you need to apply the same schema change to multiple indexes, use batch migration. This is common for:\n\n- Quantizing all indexes from float32 → float16\n- Standardizing vector algorithms across indexes\n- Coordinated migrations during maintenance windows"},{"id":"quick-start-batch-migration","title":"Quick Start: Batch Migration","role":"content","text":"[code example]"},{"id":"batch-plan-options","title":"Batch Plan Options","role":"content","text":"**Select indexes by pattern:**\n\n[code example]\n\n**Select indexes by explicit list:**\n\n[code example]\n\n**Select indexes from a file (for 100+ indexes):**\n\n[code example]"},{"id":"batch-plan-review","title":"Batch Plan Review","role":"content","text":"The generated `batch_plan.yaml` shows which indexes will be migrated:\n\n[code example]\n\n**Key fields:**\n\n- `applicable: true` means the patch applies to this index\n- `skip_reason` explains why an index will be skipped\n\n**Overlap check.** `batch-plan` refuses to write a plan when two applicable\nindexes have key prefixes that overlap — i.e. one prefix is a literal\nstring-prefix of the other, matching `FT.CREATE PREFIX` semantics. Migrating\noverlapping indexes in a single batch can corrupt vector data because every\nindex after the first reads bytes that an earlier index has already\nquantized. Split the indexes into prefix-disjoint groups and create a batch\nplan per group. See the troubleshooting entry below for the exact error\nmessage."},{"id":"applying-a-batch-plan","title":"Applying a Batch Plan","role":"content","text":"[code example]\n\n**Flags for batch-apply:**\n\n- `--accept-data-loss` : Required when quantizing vectors (float32 → float16 is lossy)\n- `--backup-dir` : Required directory for per-index backup metadata and vector backup files when hash vector bytes are mutated\n- `--state` : Path to batch state file (default: `batch_state.yaml`)\n- `--report-dir` : Directory for per-index reports (default: `./reports/`)\n\n**Note:** `--failure-policy` is set during `batch-plan`, not `batch-apply`. The policy is stored in the batch plan file."},{"id":"resume-after-failure","title":"Resume After Failure","role":"content","text":"Batch migration automatically tracks progress in the state file. If interrupted:\n\n[code example]\n\n`batch-resume` uses the `backup_dir` stored in `batch_state.yaml` unless you\npass `--backup-dir` explicitly. If you pass a different directory for the same\ncheckpoint, resume is rejected.\n\n**Note:** If the batch plan involves quantization (e.g., `float32` → `float16`), you must pass `--accept-data-loss` to `batch-resume`, just as with `batch-apply`. Omit `--accept-data-loss` if the batch plan does not involve quantization."},{"id":"checking-batch-status","title":"Checking Batch Status","role":"content","text":"[code example]\n\n**Example output:**\n\n[code example]"},{"id":"batch-report","title":"Batch Report","role":"content","text":"After completion, a `batch_report.yaml` is generated:\n\n[code example]"},{"id":"python-api-for-batch-migration","title":"Python API for Batch Migration","role":"content","text":"[code example]"},{"id":"batch-migration-tips","title":"Batch Migration Tips","role":"content","text":"1. **Test on a single index first**: Run a single-index migration to verify the patch works before applying to a batch.\n2. **Use `continue_on_error` for large batches**: This ensures one failure doesn’t block all remaining indexes.\n3. **Schedule during low-traffic periods**: Each index has downtime during migration.\n4. **Review skipped indexes**: The `skip_reason` often indicates schema differences that need attention.\n5. **Keep state files**: The `batch_state.yaml` is essential for resume. Don’t delete it until the batch completes successfully."},{"id":"performance-tuning","title":"Performance Tuning","role":"performance","text":""},{"id":"batch-size","title":"Batch Size","role":"content","text":"The `--batch-size` flag controls how many keys are read/written per Redis\npipeline round-trip. The default of 500 is a good balance. Larger batches\n(1000+) reduce round-trips but increase per-batch memory and latency."},{"id":"backup-disk-space","title":"Backup Disk Space","role":"content","text":"For quantization migrations, original vectors are saved to `--backup-dir`\nbefore mutation. Approximate size: `num_docs × dims × bytes_per_element`.\n\n| Docs   |   Dims | Source dtype   | Backup size   |\n|--------|--------|----------------|---------------|\n| 100K   |    768 | float32        | ~292 MB       |\n| 1M     |    768 | float32        | ~2.9 GB       |\n| 1M     |   1536 | float32        | ~5.7 GB       |"},{"id":"hnsw-vs-flat-index-capacity","title":"HNSW vs FLAT Index Capacity","role":"content","text":"> [!NOTE]\n> When migrating from **HNSW** to **FLAT**, the target index may report a\n> *higher* document count than the source. This is not a bug; it reflects\n> a fundamental difference in how the two algorithms store vectors.\n\nHNSW maintains a navigable small-world graph with per-node neighbor lists.\nThis graph overhead limits how many vectors can fit in available memory.\nFLAT stores vectors as a simple array with no graph overhead.\n\nIf the source HNSW index was operating near its memory capacity, some\ndocuments may have been registered in Redis Search’s document table but\nnot fully indexed into the HNSW graph. After migration to FLAT, those\nsame documents become fully searchable because FLAT requires less memory\nper vector.\n\nThe migration validator compares the total key count\n(`num_docs + hash_indexing_failures`) between source and target, so this\nscenario is handled correctly in the general case."},{"id":"learn-more","title":"Learn more","role":"related","text":"- [Index Migrations](https://redis.io/docs/latest/../../concepts/index-migrations): How migrations work and which changes are supported"}],"examples":[{"id":"quick-start-ex0","language":"bash","code":"# 1. See what indexes exist\nrvl index listall --url redis://localhost:6379\n\n# 2. Use the wizard to build a migration plan\nrvl migrate wizard --index myindex --url redis://localhost:6379\n\n# 3. Apply the migration\nrvl migrate apply --plan migration_plan.yaml --backup-dir ./migration_backups --url redis://localhost:6379\n\n# 4. Verify the result\nrvl migrate validate --plan migration_plan.yaml --url redis://localhost:6379","section_id":"quick-start"},{"id":"prerequisites-ex0","language":"bash","code":"# Local development with Redis 8.0+ (recommended for full feature support)\ndocker run -d --name redis -p 6379:6379 redis:8.0","section_id":"prerequisites"},{"id":"single-index-flow-wizardplan-then-apply-ex0","language":"default","code":"wizard (interactive)                   plan (non-interactive)\n        |                                    |\n        v                                    v\n  SchemaPatch YAML  <----or---->  SchemaPatch YAML\n        |                                    |\n        +------ planner.create_plan() -------+\n                       |\n                       v\n              MigrationPlan YAML\n                       |\n                       v\n              executor.apply()\n                       |\n                       v\n              MigrationReport YAML","section_id":"single-index-flow-wizardplan-then-apply"},{"id":"batch-flow-wizardplan-then-batch-plan-then-batch-apply-ex0","language":"default","code":"SchemaPatch YAML  (shared, written once)\n        |\n        v\nbatch_planner.create_batch_plan()\n  for each index:\n    snapshot live schema\n    merge patch into source\n    if applicable: write per-index MigrationPlan\n    if not: mark skip_reason\n        |\n        v\nBatchPlan YAML\n  shared_patch: { ... }\n  indexes:\n    - name: idx_a, applicable: true, plan_path: plans/idx_a.yaml\n    - name: idx_b, applicable: true, plan_path: plans/idx_b.yaml\n    - name: idx_c, applicable: false, skip_reason: \"field not found\"\n        |\n        v\nbatch_executor.apply()\n  for each applicable index (sequentially):\n    executor.apply(per_index_plan)","section_id":"batch-flow-wizardplan-then-batch-plan-then-batch-apply"},{"id":"step-1-discover-available-indexes-ex0","language":"bash","code":"rvl index listall --url redis://localhost:6379","section_id":"step-1-discover-available-indexes"},{"id":"step-1-discover-available-indexes-ex1","language":"default","code":"Indices:\n  1. products_idx\n  2. users_idx\n  3. orders_idx","section_id":"step-1-discover-available-indexes"},{"id":"option-a-use-the-wizard-recommended-ex0","language":"bash","code":"rvl migrate wizard --index myindex --url redis://localhost:6379","section_id":"option-a-use-the-wizard-recommended"},{"id":"option-a-use-the-wizard-recommended-ex1","language":"text","code":"Building a migration plan for index 'myindex'\nCurrent schema:\n- Index name: myindex\n- Storage type: hash\n  - title (text)\n  - embedding (vector)\n\nChoose an action:\n1. Add field        (text, tag, numeric, geo)\n2. Update field     (sortable, weight, separator, vector config)\n3. Remove field\n4. Rename field     (rename field in all documents)\n5. Rename index     (change index name)\n6. Change prefix    (rename all keys)\n7. Preview patch    (show pending changes as YAML)\n8. Finish\nEnter a number: 1\n\nField name: category\nField type options: text, tag, numeric, geo\nField type: tag\n  Sortable: enables sorting and aggregation on this field\nSortable [y/n]: n\n  Separator: character that splits multiple values (default: comma)\nSeparator [leave blank to keep existing/default]: |\n\nChoose an action:\n1. Add field        (text, tag, numeric, geo)\n2. Update field     (sortable, weight, separator, vector config)\n3. Remove field\n4. Rename field     (rename field in all documents)\n5. Rename index     (change index name)\n6. Change prefix    (rename all keys)\n7. Preview patch    (show pending changes as YAML)\n8. Finish\nEnter a number: 8\n\nMigration plan written to /path/to/migration_plan.yaml\nMode: drop_recreate\nSupported: True\nWarnings:\n- Index downtime is required","section_id":"option-a-use-the-wizard-recommended"},{"id":"option-a-use-the-wizard-recommended-ex2","language":"text","code":"Choose an action:\n1. Add field        (text, tag, numeric, geo)\n2. Update field     (sortable, weight, separator, vector config)\n3. Remove field\n4. Rename field     (rename field in all documents)\n5. Rename index     (change index name)\n6. Change prefix    (rename all keys)\n7. Preview patch    (show pending changes as YAML)\n8. Finish\nEnter a number: 2\n\nUpdatable fields:\n1. title (text)\n2. embedding (vector)\nSelect a field to update by number or name: 2\n\nCurrent vector config for 'embedding':\n  algorithm: HNSW\n  datatype: float32\n  distance_metric: cosine\n  dims: 384 (cannot be changed)\n  m: 16\n  ef_construction: 200\n\nLeave blank to keep current value.\n  Algorithm: vector search method (FLAT=brute force, HNSW=graph, SVS-VAMANA=compressed graph)\nAlgorithm [current: HNSW]:\n  Datatype: float16, float32, bfloat16, float64, int8, uint8\n            (float16 reduces memory ~50%, int8/uint8 reduce ~75%)\nDatatype [current: float32]: float16\n  Distance metric: how similarity is measured (cosine, l2, ip)\nDistance metric [current: cosine]:\n  M: number of connections per node (higher=better recall, more memory)\nM [current: 16]:\n  EF_CONSTRUCTION: build-time search depth (higher=better recall, slower build)\nEF_CONSTRUCTION [current: 200]:\n\nChoose an action:\n...\n8. Finish\nEnter a number: 8\n\nMigration plan written to /path/to/migration_plan.yaml\nMode: drop_recreate\nSupported: True","section_id":"option-a-use-the-wizard-recommended"},{"id":"option-b-write-a-schema-patch-yaml-ex0","language":"yaml","code":"version: 1\nchanges:\n  add_fields:\n    - name: category\n      type: tag\n      path: $.category\n      attrs:\n        separator: \"|\"\n  remove_fields:\n    - legacy_field\n  update_fields:\n    - name: title\n      attrs:\n        sortable: true\n    - name: embedding\n      attrs:\n        datatype: float16        # quantize vectors\n        algorithm: HNSW\n        distance_metric: cosine","section_id":"option-b-write-a-schema-patch-yaml"},{"id":"option-b-write-a-schema-patch-yaml-ex1","language":"bash","code":"rvl migrate plan \\\n  --index myindex \\\n  --schema-patch schema_patch.yaml \\\n  --url redis://localhost:6379 \\\n  --plan-out migration_plan.yaml","section_id":"option-b-write-a-schema-patch-yaml"},{"id":"option-c-provide-a-target-schema-ex0","language":"bash","code":"rvl migrate plan \\\n  --index myindex \\\n  --target-schema target_schema.yaml \\\n  --url redis://localhost:6379 \\\n  --plan-out migration_plan.yaml","section_id":"option-c-provide-a-target-schema"},{"id":"step-3-review-the-migration-plan-ex0","language":"yaml","code":"# migration_plan.yaml (example)\nversion: 1\nmode: drop_recreate\n\nsource:\n  schema_snapshot:\n    index:\n      name: myindex\n      prefix: \"doc:\"\n      storage_type: json\n    fields:\n      - name: title\n        type: text\n      - name: embedding\n        type: vector\n        attrs:\n          dims: 384\n          algorithm: hnsw\n          datatype: float32\n  stats_snapshot:\n    num_docs: 10000\n  keyspace:\n    prefixes: [\"doc:\"]\n    key_sample: [\"doc:1\", \"doc:2\", \"doc:3\"]\n\nrequested_changes:\n  add_fields:\n    - name: category\n      type: tag\n\ndiff_classification:\n  supported: true\n  blocked_reasons: []\n\nrename_operations:\n  rename_index: null\n  change_prefix: null\n  rename_fields: []\n\nmerged_target_schema:\n  index:\n    name: myindex\n    prefix: \"doc:\"\n    storage_type: json\n  fields:\n    - name: title\n      type: text\n    - name: category\n      type: tag\n    - name: embedding\n      type: vector\n      attrs:\n        dims: 384\n        algorithm: hnsw\n        datatype: float32\n\nwarnings:\n  - \"Index downtime is required\"","section_id":"step-3-review-the-migration-plan"},{"id":"step-4-apply-the-migration-ex0","language":"bash","code":"rvl migrate apply \\\n  --plan migration_plan.yaml \\\n  --url redis://localhost:6379 \\\n  --backup-dir ./migration_backups \\\n  --report-out migration_report.yaml \\\n  --benchmark-out benchmark_report.yaml","section_id":"step-4-apply-the-migration"},{"id":"async-execution-for-large-migrations-ex0","language":"bash","code":"rvl migrate apply \\\n  --plan migration_plan.yaml \\\n  --async \\\n  --backup-dir ./migration_backups \\\n  --url redis://localhost:6379","section_id":"async-execution-for-large-migrations"},{"id":"crash-safe-resume-for-quantization-ex0","language":"default","code":"<backup-dir>/\n  migration_backup_<index_name>.header   # JSON: phase, progress counters, field metadata\n  migration_backup_<index_name>.data     # Binary: length-prefixed batches of original vectors","section_id":"crash-safe-resume-for-quantization"},{"id":"crash-safe-resume-for-quantization-ex1","language":"default","code":"dump → ready → index_dropped → active → completed → target_created → validated","section_id":"crash-safe-resume-for-quantization"},{"id":"crash-safe-resume-for-quantization-ex2","language":"bash","code":"rvl migrate estimate --plan migration_plan.yaml","section_id":"crash-safe-resume-for-quantization"},{"id":"crash-safe-resume-for-quantization-ex3","language":"text","code":"Pre-migration disk space estimate:\n  Index: products_idx (1,000,000 documents)\n  Vector field 'embedding': 768 dims, float32 -> float16\n\n  RDB snapshot (BGSAVE):        ~2.87 GB\n  AOF growth:                  not estimated (pass aof_enabled=True if AOF is on)\n  Total new disk required:      ~2.87 GB\n\n  Post-migration memory savings: ~1.43 GB (50% reduction)","section_id":"crash-safe-resume-for-quantization"},{"id":"crash-safe-resume-for-quantization-ex4","language":"bash","code":"rvl migrate estimate --plan migration_plan.yaml --aof-enabled","section_id":"crash-safe-resume-for-quantization"},{"id":"crash-safe-resume-for-quantization-ex5","language":"bash","code":"rvl migrate apply \\\n  --plan migration_plan.yaml \\\n  --backup-dir /tmp/migration_backups \\\n  --url redis://localhost:6379 \\\n  --report-out migration_report.yaml","section_id":"crash-safe-resume-for-quantization"},{"id":"crash-safe-resume-for-quantization-ex6","language":"json","code":"{\n  \"index_name\": \"products_idx\",\n  \"fields\": {\"embedding\": {\"source\": \"float32\", \"target\": \"float16\", \"dims\": 768}},\n  \"batch_size\": 500,\n  \"phase\": \"active\",\n  \"dump_completed_batches\": 2000,\n  \"quantize_completed_batches\": 900\n}","section_id":"crash-safe-resume-for-quantization"},{"id":"crash-safe-resume-for-quantization-ex7","language":"bash","code":"rvl migrate apply \\\n  --plan migration_plan.yaml \\\n  --backup-dir /tmp/migration_backups \\\n  --url redis://localhost:6379 \\\n  --report-out migration_report.yaml","section_id":"crash-safe-resume-for-quantization"},{"id":"step-5-validate-the-result-ex0","language":"bash","code":"rvl migrate validate \\\n  --plan migration_plan.yaml \\\n  --url redis://localhost:6379 \\\n  --report-out migration_report.yaml","section_id":"step-5-validate-the-result"},{"id":"batch-plan-failed-overlapping-indexes-detected-ex0","language":"default","code":"Error: Refusing to create batch plan: overlapping indexes detected.\n\nMultiple indexes in the batch share Redis key prefixes. Running a\nbatch migration over overlapping indexes can mutate the same keys\nmore than once (e.g., double-quantization of vectors), corrupting\nthe underlying data.\n\nConflicts:\n  - products_main <-> products_premium: 'product:' <-> 'product:premium:'\n\nResolve by migrating overlapping indexes one at a time, or by\nnarrowing the batch to a set of indexes with disjoint prefixes.","section_id":"batch-plan-failed-overlapping-indexes-detected"},{"id":"how-backups-work-ex0","language":"default","code":"<backup-dir>/\n  migration_backup_<index_name>.header   # JSON: phase, progress counters, field metadata\n  migration_backup_<index_name>.data     # Binary: length-prefixed batches of original vectors\n  migration_backup_<index_name>.manifest # JSON: multi-worker shard resume metadata, when workers > 1","section_id":"how-backups-work"},{"id":"crash-safe-resume-ex0","language":"bash","code":"# Original command that was interrupted\nrvl migrate apply --plan plan.yaml --url redis://localhost:6379 \\\n  --backup-dir /tmp/backups --workers 4\n\n# Just re-run it. Progress is resumed automatically\nrvl migrate apply --plan plan.yaml --url redis://localhost:6379 \\\n  --backup-dir /tmp/backups --workers 4","section_id":"crash-safe-resume"},{"id":"rollback-ex0","language":"bash","code":"rvl migrate rollback --backup-dir /tmp/backups --url redis://localhost:6379","section_id":"rollback"},{"id":"rollback-ex1","language":"bash","code":"# After rollback, recreate the original index if needed:\nrvl index create --schema original_schema.yaml --url redis://localhost:6379","section_id":"rollback"},{"id":"python-api-for-rollback-ex0","language":"python","code":"from redisvl.migration.backup import VectorBackup\nimport redis\n\nr = redis.from_url(\"redis://localhost:6379\")\nbackup = VectorBackup.load(\"/tmp/backups/migration_backup_myindex\")\n\nfor keys, originals in backup.iter_batches():\n    pipe = r.pipeline(transaction=False)\n    for key in keys:\n        if key in originals:\n            for field_name, original_bytes in originals[key].items():\n                pipe.hset(key, field_name, original_bytes)\n    pipe.execute()\n\nprint(\"Rollback complete\")","section_id":"python-api-for-rollback"},{"id":"sync-api-ex0","language":"python","code":"from redisvl.migration import MigrationPlanner, MigrationExecutor\n\nplanner = MigrationPlanner()\nplan = planner.create_plan(\n    \"myindex\",\n    redis_url=\"redis://localhost:6379\",\n    schema_patch_path=\"schema_patch.yaml\",\n)\n\nexecutor = MigrationExecutor()\nreport = executor.apply(\n    plan,\n    redis_url=\"redis://localhost:6379\",\n    backup_dir=\"/tmp/migration_backups\",\n)\nprint(f\"Migration result: {report.result}\")","section_id":"sync-api"},{"id":"sync-api-ex1","language":"python","code":"report = executor.apply(\n    plan,\n    redis_url=\"redis://localhost:6379\",\n    backup_dir=\"/tmp/migration_backups\",   # enables crash-safe resume\n    batch_size=500,                        # keys per pipeline batch\n    num_workers=4,                         # parallel quantization workers\n)\nprint(f\"Quantized in {report.timings.quantize_duration_seconds}s\")","section_id":"sync-api"},{"id":"async-api-ex0","language":"python","code":"import asyncio\nfrom redisvl.migration import AsyncMigrationPlanner, AsyncMigrationExecutor\n\nasync def migrate():\n    planner = AsyncMigrationPlanner()\n    plan = await planner.create_plan(\n        \"myindex\",\n        redis_url=\"redis://localhost:6379\",\n        schema_patch_path=\"schema_patch.yaml\",\n    )\n\n    executor = AsyncMigrationExecutor()\n    report = await executor.apply(\n        plan,\n        redis_url=\"redis://localhost:6379\",\n        backup_dir=\"/tmp/migration_backups\",\n        num_workers=4,\n    )\n    print(f\"Migration result: {report.result}\")\n\nasyncio.run(migrate())","section_id":"async-api"},{"id":"quick-start-batch-migration-ex0","language":"bash","code":"# 1. Create a shared patch (applies to any index with an 'embedding' field)\ncat > quantize_patch.yaml << 'EOF'\nversion: 1\nchanges:\n  update_fields:\n    - name: embedding\n      attrs:\n        datatype: float16\nEOF\n\n# 2. Create a batch plan for all indexes matching a pattern\nrvl migrate batch-plan \\\n  --pattern \"*_idx\" \\\n  --schema-patch quantize_patch.yaml \\\n  --plan-out batch_plan.yaml \\\n  --url redis://localhost:6379\n\n# 3. Apply the batch plan\nrvl migrate batch-apply \\\n  --plan batch_plan.yaml \\\n  --backup-dir ./migration_backups \\\n  --accept-data-loss \\\n  --url redis://localhost:6379\n\n# 4. Check status\nrvl migrate batch-status --state batch_state.yaml","section_id":"quick-start-batch-migration"},{"id":"batch-plan-options-ex0","language":"bash","code":"rvl migrate batch-plan \\\n  --pattern \"*_idx\" \\\n  --schema-patch quantize_patch.yaml \\\n  --plan-out batch_plan.yaml \\\n  --url redis://localhost:6379","section_id":"batch-plan-options"},{"id":"batch-plan-options-ex1","language":"bash","code":"rvl migrate batch-plan \\\n  --indexes \"products_idx,users_idx,orders_idx\" \\\n  --schema-patch quantize_patch.yaml \\\n  --plan-out batch_plan.yaml \\\n  --url redis://localhost:6379","section_id":"batch-plan-options"},{"id":"batch-plan-options-ex2","language":"bash","code":"# Create index list file\necho -e \"products_idx\\nusers_idx\\norders_idx\" > indexes.txt\n\nrvl migrate batch-plan \\\n  --indexes-file indexes.txt \\\n  --schema-patch quantize_patch.yaml \\\n  --plan-out batch_plan.yaml \\\n  --url redis://localhost:6379","section_id":"batch-plan-options"},{"id":"batch-plan-review-ex0","language":"yaml","code":"version: 1\nbatch_id: \"batch_20260320_100000\"\nmode: drop_recreate\nfailure_policy: fail_fast\nrequires_quantization: true\n\nshared_patch:\n  version: 1\n  changes:\n    update_fields:\n      - name: embedding\n        attrs:\n          datatype: float16\n\nindexes:\n  - name: products_idx\n    applicable: true\n    skip_reason: null\n  - name: users_idx\n    applicable: true\n    skip_reason: null\n  - name: legacy_idx\n    applicable: false\n    skip_reason: \"Field 'embedding' not found\"\n\ncreated_at: \"2026-03-20T10:00:00Z\"","section_id":"batch-plan-review"},{"id":"applying-a-batch-plan-ex0","language":"bash","code":"# Apply with fail-fast (default: stop on first error)\nrvl migrate batch-apply \\\n  --plan batch_plan.yaml \\\n  --backup-dir ./migration_backups \\\n  --accept-data-loss \\\n  --url redis://localhost:6379\n\n# Apply with continue-on-error (set at batch-plan time)\n# Note: failure_policy is set during batch-plan, not batch-apply\nrvl migrate batch-plan \\\n  --pattern \"*_idx\" \\\n  --schema-patch quantize_patch.yaml \\\n  --failure-policy continue_on_error \\\n  --plan-out batch_plan.yaml \\\n  --url redis://localhost:6379\n\nrvl migrate batch-apply \\\n  --plan batch_plan.yaml \\\n  --backup-dir ./migration_backups \\\n  --accept-data-loss \\\n  --url redis://localhost:6379","section_id":"applying-a-batch-plan"},{"id":"resume-after-failure-ex0","language":"bash","code":"# Resume from where it left off\nrvl migrate batch-resume \\\n  --state batch_state.yaml \\\n  --accept-data-loss \\\n  --url redis://localhost:6379\n\n# Retry previously failed indexes\nrvl migrate batch-resume \\\n  --state batch_state.yaml \\\n  --retry-failed \\\n  --accept-data-loss \\\n  --url redis://localhost:6379","section_id":"resume-after-failure"},{"id":"checking-batch-status-ex0","language":"bash","code":"rvl migrate batch-status --state batch_state.yaml","section_id":"checking-batch-status"},{"id":"checking-batch-status-ex1","language":"default","code":"Batch Migration Status\n======================\nBatch ID: batch_20260320_100000\nStarted: 2026-03-20T10:00:00Z\nUpdated: 2026-03-20T10:25:00Z\n\nCompleted: 2\n  - products_idx: success (10:02:30)\n  - users_idx: failed - Redis connection timeout (10:05:45)\n\nIn Progress: inventory_idx\nRemaining: 1 (analytics_idx)","section_id":"checking-batch-status"},{"id":"batch-report-ex0","language":"yaml","code":"version: 1\nbatch_id: \"batch_20260320_100000\"\nstatus: completed  # or partial_failure, failed\nsummary:\n  total_indexes: 3\n  successful: 3\n  failed: 0\n  skipped: 0\n  total_duration_seconds: 127.5\nindexes:\n  - name: products_idx\n    status: success\n    report_path: ./reports/products_idx_report.yaml\n  - name: users_idx\n    status: success\n    report_path: ./reports/users_idx_report.yaml\n  - name: orders_idx\n    status: success\n    report_path: ./reports/orders_idx_report.yaml\ncompleted_at: \"2026-03-20T10:02:07Z\"","section_id":"batch-report"},{"id":"python-api-for-batch-migration-ex0","language":"python","code":"from redisvl.migration import BatchMigrationPlanner, BatchMigrationExecutor\n\n# Create batch plan\nplanner = BatchMigrationPlanner()\nbatch_plan = planner.create_batch_plan(\n    redis_url=\"redis://localhost:6379\",\n    pattern=\"*_idx\",\n    schema_patch_path=\"quantize_patch.yaml\",\n)\n\n# Review applicability\nfor idx in batch_plan.indexes:\n    if idx.applicable:\n        print(f\"Will migrate: {idx.name}\")\n    else:\n        print(f\"Skipping {idx.name}: {idx.skip_reason}\")\n\n# Execute batch\nexecutor = BatchMigrationExecutor()\nreport = executor.apply(\n    batch_plan,\n    redis_url=\"redis://localhost:6379\",\n    state_path=\"batch_state.yaml\",\n    report_dir=\"./reports/\",\n    backup_dir=\"/tmp/migration_backups\",\n    progress_callback=lambda name, pos, total, status: print(f\"[{pos}/{total}] {name}: {status}\"),\n)\n\nprint(f\"Batch status: {report.status}\")\nprint(f\"Successful: {report.summary.successful}/{report.summary.total_indexes}\")","section_id":"python-api-for-batch-migration"}]}
{"schema_version":2,"id":"develop/ai/redisvl/user_guide/how_to_guides/rerankers","title":"Rerank Search Results","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/how_to_guides/rerankers/","summary":"","aliases":["/integrate/redisvl/user_guide/how_to_guides/06_rerankers","/integrate/redisvl/user_guide/rerankers/","/develop/ai/redisvl/user_guide/rerankers/"],"tags":[],"last_updated":"2026-10-01T14:49:09Z","page_type":"content","content_hash":"bcca8a9bfdafe795a6d9c54767256b50bac0261f5b5e70bcba62de13adebf64f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This guide demonstrates how to use RedisVL to rerank search results (documents, chunks, or records) based on query relevance. RedisVL supports reranking through Cross-Encoders from [Hugging Face](https://huggingface.co/cross-encoder), [Cohere Rerank API](https://docs.cohere.com/docs/rerank-2), and [VoyageAI Rerank API](https://docs.voyageai.com/docs/reranker)."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before you begin, ensure you have:\n- Installed RedisVL: `pip install redisvl`\n- A running Redis instance ([Redis 8+](https://redis.io/downloads/) or [Redis Cloud](https://redis.io/cloud))"},{"id":"what-youll-learn","title":"What You'll Learn","role":"content","text":"By the end of this guide, you will be able to:\n- Rerank search results using HuggingFace Cross-Encoders\n- Use the Cohere Rerank API with search results\n- Use the VoyageAI Rerank API for result reranking\n- Control the number of returned results after reranking\n\n\n[code example]"},{"id":"simple-reranking","title":"Simple Reranking","role":"content","text":"Reranking provides a relevance boost to search results generated by\ntraditional (lexical) or semantic search strategies.\n\nAs a simple demonstration, take the passages and user query below:\n\n\n[code example]\n\nThe goal of reranking is to provide a more fine-grained quality improvement to\ninitial search results. With RedisVL, this would likely be results coming back\nfrom a search operation like full text or vector."},{"id":"using-the-cross-encoder-reranker","title":"Using the Cross-Encoder Reranker","role":"content","text":"To use the cross-encoder reranker we initialize an instance of `HFCrossEncoderReranker` passing a suitable model (if no model is provided, the `cross-encoder/ms-marco-MiniLM-L-6-v2` model is used):  \n\n\n[code example]"},{"id":"rerank-documents-with-hfcrossencoderreranker","title":"Rerank documents with HFCrossEncoderReranker","role":"content","text":"With the obtained reranker instance we can rerank and truncate the list of\ndocuments based on relevance to the initial query.\n\n\n[code example]\n\n\n[code example]\n\n    0.07461103051900864  --  {'content': 'Washington, D.C. (also known as simply Washington or D.C., and officially as the District of Columbia) is the capital of the United States. It is a federal district. The President of the USA and many major national government offices are in the territory. This makes it the political center of the United States of America.'}\n    0.052202966064214706  --  {'content': 'Charlotte Amalie is the capital and largest city of the United States Virgin Islands. It has about 20,000 people. The city is on the island of Saint Thomas.'}\n    0.3802356719970703  --  {'content': 'Carson City is the capital city of the American state of Nevada. At the 2010 United States Census, Carson City had a population of 55,274.'}"},{"id":"using-the-cohere-reranker","title":"Using the Cohere Reranker","role":"content","text":"To initialize the Cohere reranker you'll need to install the cohere library and provide the right Cohere API Key.\n\n\n[code example]\n\n\n[code example]\n\n\n[code example]"},{"id":"rerank-documents-with-coherereranker","title":"Rerank documents with CohereReranker","role":"content","text":"The following example uses `CohereReranker` to rerank and truncate the list of\ndocuments based on relevance to the initial query.\n\n\n[code example]\n\n\n[code example]\n\n    0.9990564  --  Washington, D.C. (also known as simply Washington or D.C., and officially as the District of Columbia) is the capital of the United States. It is a federal district. The President of the USA and many major national government offices are in the territory. This makes it the political center of the United States of America.\n    0.7516481  --  Capital punishment (the death penalty) has existed in the United States since before the United States was a country. As of 2017, capital punishment is legal in 30 of the 50 states. The federal government (including the United States military) also uses capital punishment.\n    0.08882029  --  The Commonwealth of the Northern Mariana Islands is a group of islands in the Pacific Ocean that are a political division controlled by the United States. Its capital is Saipan."},{"id":"working-with-semi-structured-documents","title":"Working with semi-structured documents","role":"content","text":"Often times the initial result set includes other metadata and components that could be used to steer the reranking relevancy. To accomplish this, we can set the `rank_by` argument and provide documents with those additional fields.\n\n\n[code example]\n\n\n[code example]\n\n\n[code example]\n\n    0.9988121  --  {'source': 'textbook', 'passage': 'Washington, D.C. (also known as simply Washington or D.C., and officially as the District of Columbia) is the capital of the United States. It is a federal district. The President of the USA and many major national government offices are in the territory. This makes it the political center of the United States of America.'}\n    0.5974905  --  {'source': 'wiki', 'passage': 'Capital punishment (the death penalty) has existed in the United States since before the United States was a country. As of 2017, capital punishment is legal in 30 of the 50 states. The federal government (including the United States military) also uses capital punishment.'}\n    0.059101548  --  {'source': 'encyclopedia', 'passage': 'The Commonwealth of the Northern Mariana Islands is a group of islands in the Pacific Ocean that are a political division controlled by the United States. Its capital is Saipan.'}"},{"id":"using-the-voyageai-reranker","title":"Using the VoyageAI Reranker","role":"content","text":"To initialize the VoyageAI reranker you'll need to install the voyaeai library and provide the right VoyageAI API Key.\n\n\n[code example]\n\n\n[code example]\n\n\n[code example]"},{"id":"rerank-documents-with-voyageaireranker","title":"Rerank documents with VoyageAIReranker","role":"content","text":"The following example uses `VoyageAIReranker` to rerank and truncate the list of\ndocuments based on relevance to the initial query.\n\n\n[code example]\n\n\n[code example]\n\n    0.796875  --  Washington, D.C. (also known as simply Washington or D.C., and officially as the District of Columbia) is the capital of the United States. It is a federal district. The President of the USA and many major national government offices are in the territory. This makes it the political center of the United States of America.\n    0.578125  --  Charlotte Amalie is the capital and largest city of the United States Virgin Islands. It has about 20,000 people. The city is on the island of Saint Thomas.\n    0.5625  --  Carson City is the capital city of the American state of Nevada. At the 2010 United States Census, Carson City had a population of 55,274."},{"id":"next-steps","title":"Next Steps","role":"content","text":"Now that you understand reranking, explore these related guides:\n\n- [Create Embeddings with Vectorizers](https://redis.io/docs/latest/vectorizers) - Generate embeddings using various providers\n- [Query and Filter Data](https://redis.io/docs/latest/complex_filtering) - Build complex filter expressions for search\n- [Use Advanced Query Types](https://redis.io/docs/latest/advanced_queries) - Learn about HybridQuery and other query types"},{"id":"cleanup","title":"Cleanup","role":"content","text":"This guide does not create a persistent index, so no cleanup is required."}],"examples":[{"id":"what-youll-learn-ex0","language":"python","code":"# import necessary modules\nimport os","section_id":"what-youll-learn"},{"id":"simple-reranking-ex0","language":"python","code":"query = \"What is the capital of the United States?\"\ndocs = [\n    \"Carson City is the capital city of the American state of Nevada. At the 2010 United States Census, Carson City had a population of 55,274.\",\n    \"The Commonwealth of the Northern Mariana Islands is a group of islands in the Pacific Ocean that are a political division controlled by the United States. Its capital is Saipan.\",\n    \"Charlotte Amalie is the capital and largest city of the United States Virgin Islands. It has about 20,000 people. The city is on the island of Saint Thomas.\",\n    \"Washington, D.C. (also known as simply Washington or D.C., and officially as the District of Columbia) is the capital of the United States. It is a federal district. The President of the USA and many major national government offices are in the territory. This makes it the political center of the United States of America.\",\n    \"Capital punishment (the death penalty) has existed in the United States since before the United States was a country. As of 2017, capital punishment is legal in 30 of the 50 states. The federal government (including the United States military) also uses capital punishment.\"\n]","section_id":"simple-reranking"},{"id":"using-the-cross-encoder-reranker-ex0","language":"python","code":"from redisvl.utils.rerank import HFCrossEncoderReranker\n\ncross_encoder_reranker = HFCrossEncoderReranker(\"BAAI/bge-reranker-base\")","section_id":"using-the-cross-encoder-reranker"},{"id":"rerank-documents-with-hfcrossencoderreranker-ex0","language":"python","code":"results, scores = cross_encoder_reranker.rank(query=query, docs=docs)","section_id":"rerank-documents-with-hfcrossencoderreranker"},{"id":"rerank-documents-with-hfcrossencoderreranker-ex1","language":"python","code":"for result, score in zip(results, scores):\n    print(score, \" -- \", result)","section_id":"rerank-documents-with-hfcrossencoderreranker"},{"id":"using-the-cohere-reranker-ex0","language":"python","code":"#!pip install cohere","section_id":"using-the-cohere-reranker"},{"id":"using-the-cohere-reranker-ex1","language":"python","code":"import getpass\n\n# setup the API Key\napi_key = os.environ.get(\"COHERE_API_KEY\") or getpass.getpass(\"Enter your Cohere API key: \")","section_id":"using-the-cohere-reranker"},{"id":"using-the-cohere-reranker-ex2","language":"python","code":"from redisvl.utils.rerank import CohereReranker\n\ncohere_reranker = CohereReranker(limit=3, api_config={\"api_key\": api_key})","section_id":"using-the-cohere-reranker"},{"id":"rerank-documents-with-coherereranker-ex0","language":"python","code":"results, scores = cohere_reranker.rank(query=query, docs=docs)","section_id":"rerank-documents-with-coherereranker"},{"id":"rerank-documents-with-coherereranker-ex1","language":"python","code":"for result, score in zip(results, scores):\n    print(score, \" -- \", result)","section_id":"rerank-documents-with-coherereranker"},{"id":"working-with-semi-structured-documents-ex0","language":"python","code":"docs = [\n    {\n        \"source\": \"wiki\",\n        \"passage\": \"Carson City is the capital city of the American state of Nevada. At the 2010 United States Census, Carson City had a population of 55,274.\"\n    },\n    {\n        \"source\": \"encyclopedia\",\n        \"passage\": \"The Commonwealth of the Northern Mariana Islands is a group of islands in the Pacific Ocean that are a political division controlled by the United States. Its capital is Saipan.\"\n    },\n    {\n        \"source\": \"textbook\",\n        \"passage\": \"Charlotte Amalie is the capital and largest city of the United States Virgin Islands. It has about 20,000 people. The city is on the island of Saint Thomas.\"\n    },\n    {\n        \"source\": \"textbook\",\n        \"passage\": \"Washington, D.C. (also known as simply Washington or D.C., and officially as the District of Columbia) is the capital of the United States. It is a federal district. The President of the USA and many major national government offices are in the territory. This makes it the political center of the United States of America.\"\n    },\n    {\n        \"source\": \"wiki\",\n        \"passage\": \"Capital punishment (the death penalty) has existed in the United States since before the United States was a country. As of 2017, capital punishment is legal in 30 of the 50 states. The federal government (including the United States military) also uses capital punishment.\"\n    }\n]","section_id":"working-with-semi-structured-documents"},{"id":"working-with-semi-structured-documents-ex1","language":"python","code":"results, scores = cohere_reranker.rank(query=query, docs=docs, rank_by=[\"passage\", \"source\"])","section_id":"working-with-semi-structured-documents"},{"id":"working-with-semi-structured-documents-ex2","language":"python","code":"for result, score in zip(results, scores):\n    print(score, \" -- \", result)","section_id":"working-with-semi-structured-documents"},{"id":"using-the-voyageai-reranker-ex0","language":"python","code":"#!pip install voyageai","section_id":"using-the-voyageai-reranker"},{"id":"using-the-voyageai-reranker-ex1","language":"python","code":"import getpass\n\n# setup the API Key\napi_key = os.environ.get(\"VOYAGE_API_KEY\") or getpass.getpass(\"Enter your VoyageAI API key: \")","section_id":"using-the-voyageai-reranker"},{"id":"using-the-voyageai-reranker-ex2","language":"python","code":"from redisvl.utils.rerank import VoyageAIReranker\n\nreranker = VoyageAIReranker(model=\"rerank-2.5\", limit=3, api_config={\"api_key\": api_key})\n# Please check the available models at https://docs.voyageai.com/docs/reranker","section_id":"using-the-voyageai-reranker"},{"id":"rerank-documents-with-voyageaireranker-ex0","language":"python","code":"results, scores = reranker.rank(query=query, docs=docs)","section_id":"rerank-documents-with-voyageaireranker"},{"id":"rerank-documents-with-voyageaireranker-ex1","language":"python","code":"for result, score in zip(results, scores):\n    print(score, \" -- \", result)","section_id":"rerank-documents-with-voyageaireranker"}]}
{"schema_version":2,"id":"develop/ai/redisvl/user_guide/how_to_guides/semantic_router","title":"Route Queries with SemanticRouter","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/how_to_guides/semantic_router/","summary":"","aliases":["/integrate/redisvl/user_guide/how_to_guides/08_semantic_router","/integrate/redisvl/user_guide/semantic_router/","/develop/ai/redisvl/user_guide/semantic_router/"],"tags":[],"last_updated":"2026-10-01T14:49:09Z","page_type":"content","content_hash":"27056b5446c930253531cdea4a6522eba00582bd769ebb064ad86814d5970429","sections":[{"id":"overview","title":"Overview","role":"overview","text":"RedisVL provides a `SemanticRouter` interface that uses Redis' built-in search and aggregation to perform KNN-style classification over a set of `Route` references to determine the best match.\n\nThis guide covers how to use Redis as a Semantic Router for your applications."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before you begin, ensure you have:\n- Installed RedisVL: `pip install redisvl`\n- A running Redis instance ([Redis 8+](https://redis.io/downloads/) or [Redis Cloud](https://redis.io/cloud))"},{"id":"what-youll-learn","title":"What You'll Learn","role":"content","text":"By the end of this guide, you will be able to:\n- Define routes with references and distance thresholds\n- Initialize and configure a `SemanticRouter`\n- Route queries to single or multiple matching routes\n- Serialize and restore router configurations\n- Manage route references dynamically"},{"id":"define-the-routes","title":"Define the Routes","role":"content","text":"Below we define 3 different routes. One for `technology`, one for `sports`, and\nanother for `entertainment`. Now for this example, the goal here is\nsurely topic \"classification\". But you can create routes and references for\nalmost anything.\n\nEach route has a set of references that cover the \"semantic surface area\" of the\nroute. The incoming query from a user needs to be semantically similar to one or\nmore of the references in order to \"match\" on the route.\n\nAdditionally, each route has a `distance_threshold` which determines the maximum distance between the query and the reference for the query to be routed to the route. This value is unique to each route and uses Redis COSINE distance units (0-2], where lower values require stricter matching.\n\n\n[code example]"},{"id":"initialize-the-semanticrouter","title":"Initialize the SemanticRouter","role":"content","text":"``SemanticRouter`` will automatically create an index within Redis upon initialization for the route references. By default, it uses the `HFTextVectorizer` to \ngenerate embeddings for each route reference.\n\n\n[code example]\n\n\n[code example]\n\n    \n    \n    Index Information:\n    ╭──────────────────┬──────────────────┬──────────────────┬──────────────────┬──────────────────╮\n    │ Index Name       │ Storage Type     │ Prefixes         │ Index Options    │ Indexing         │\n    ├──────────────────┼──────────────────┼──────────────────┼──────────────────┼──────────────────┤\n    | topic-router     | HASH             | ['topic-router'] | []               | 0                |\n    ╰──────────────────┴──────────────────┴──────────────────┴──────────────────┴──────────────────╯\n    Index Fields:\n    ╭─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────╮\n    │ Name            │ Attribute       │ Type            │ Field Option    │ Option Value    │ Field Option    │ Option Value    │ Field Option    │ Option Value    │ Field Option    │ Option Value    │\n    ├─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┤\n    │ reference_id    │ reference_id    │ TAG             │ SEPARATOR       │ ,               │                 │                 │                 │                 │                 │                 │\n    │ route_name      │ route_name      │ TAG             │ SEPARATOR       │ ,               │                 │                 │                 │                 │                 │                 │\n    │ reference       │ reference       │ TEXT            │ WEIGHT          │ 1               │                 │                 │                 │                 │                 │                 │\n    │ vector          │ vector          │ VECTOR          │ algorithm       │ FLAT            │ data_type       │ FLOAT32         │ dim             │ 768             │ distance_metric │ COSINE          │\n    ╰─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────╯\n\n\n\n[code example]\n\n\n\n\n    11"},{"id":"simple-routing","title":"Simple routing","role":"content","text":"[code example]\n\n\n\n\n    RouteMatch(name='technology', distance=0.419146001339)\n\n\n\n\n[code example]\n\n\n\n\n    RouteMatch(name=None, distance=None)\n\n\n\nWe can also route a statement to many routes and order them by distance:\n\n\n[code example]\n\n\n\n\n    [RouteMatch(name='technology', distance=0.556494116783),\n     RouteMatch(name='sports', distance=0.671060025692)]\n\n\n\n\n[code example]\n\n\n\n\n    [RouteMatch(name='technology', distance=0.556494116783),\n     RouteMatch(name='sports', distance=0.629264295101)]\n\n\n\nNote the different route match distances. This is because we used the `min` aggregation method instead of the default `avg` approach."},{"id":"update-the-routing-config","title":"Update the routing config","role":"content","text":"[code example]\n\n\n[code example]\n\n\n\n\n    [RouteMatch(name='sports', distance=0.663253962994)]"},{"id":"router-serialization","title":"Router serialization","role":"content","text":"[code example]\n\n\n\n\n    {'name': 'topic-router',\n     'routes': [{'name': 'technology',\n       'references': ['what are the latest advancements in AI?',\n        'tell me about the newest gadgets',\n        \"what's trending in tech?\"],\n       'metadata': {'category': 'tech', 'priority': 1},\n       'distance_threshold': 0.71},\n      {'name': 'sports',\n       'references': ['who won the game last night?',\n        'tell me about the upcoming sports events',\n        \"what's the latest in the world of sports?\",\n        'sports',\n        'basketball and football'],\n       'metadata': {'category': 'sports', 'priority': 2},\n       'distance_threshold': 0.72},\n      {'name': 'entertainment',\n       'references': ['what are the top movies right now?',\n        'who won the best actor award?',\n        \"what's new in the entertainment industry?\"],\n       'metadata': {'category': 'entertainment', 'priority': 3},\n       'distance_threshold': 0.7}],\n     'vectorizer': {'type': 'hf',\n      'model': 'sentence-transformers/all-mpnet-base-v2'},\n     'routing_config': {'max_k': 3, 'aggregation_method': 'min'}}\n\n\n\n\n[code example]\n\n\n[code example]\n\n\n[code example]"},{"id":"add-route-references","title":"Add route references","role":"content","text":"[code example]\n\n\n\n\n    ['topic-router:technology:f243fb2d073774e81c7815247cb3013794e6225df3cbe3769cee8c6cefaca777',\n     'topic-router:technology:7e4bca5853c1c3298b4d001de13c3c7a79a6e0f134f81acc2e7cddbd6845961f']"},{"id":"get-route-references","title":"Get route references","role":"content","text":"[code example]\n\n\n\n\n    [{'id': 'topic-router:technology:f243fb2d073774e81c7815247cb3013794e6225df3cbe3769cee8c6cefaca777',\n      'reference_id': 'f243fb2d073774e81c7815247cb3013794e6225df3cbe3769cee8c6cefaca777',\n      'route_name': 'technology',\n      'reference': 'latest AI trends'},\n     {'id': 'topic-router:technology:851f51cce5a9ccfbbcb66993908be6b7871479af3e3a4b139ad292a1bf7e0676',\n      'reference_id': '851f51cce5a9ccfbbcb66993908be6b7871479af3e3a4b139ad292a1bf7e0676',\n      'route_name': 'technology',\n      'reference': 'what are the latest advancements in AI?'},\n     {'id': 'topic-router:technology:7e4bca5853c1c3298b4d001de13c3c7a79a6e0f134f81acc2e7cddbd6845961f',\n      'reference_id': '7e4bca5853c1c3298b4d001de13c3c7a79a6e0f134f81acc2e7cddbd6845961f',\n      'route_name': 'technology',\n      'reference': 'new tech gadgets'},\n     {'id': 'topic-router:technology:149a9c9919c58534aa0f369e85ad95ba7f00aa0513e0f81e2aff2ea4a717b0e0',\n      'reference_id': '149a9c9919c58534aa0f369e85ad95ba7f00aa0513e0f81e2aff2ea4a717b0e0',\n      'route_name': 'technology',\n      'reference': \"what's trending in tech?\"},\n     {'id': 'topic-router:technology:85cc73a1437df27caa2f075a29c497e5a2e532023fbb75378aedbae80779ab37',\n      'reference_id': '85cc73a1437df27caa2f075a29c497e5a2e532023fbb75378aedbae80779ab37',\n      'route_name': 'technology',\n      'reference': 'tell me about the newest gadgets'}]\n\n\n\n\n[code example]\n\n\n\n\n    [{'id': 'topic-router:technology:f243fb2d073774e81c7815247cb3013794e6225df3cbe3769cee8c6cefaca777',\n      'reference_id': 'f243fb2d073774e81c7815247cb3013794e6225df3cbe3769cee8c6cefaca777',\n      'route_name': 'technology',\n      'reference': 'latest AI trends'}]"},{"id":"delete-route-references","title":"Delete route references","role":"content","text":"[code example]\n\n\n\n\n    5\n\n\n\n\n[code example]\n\n\n\n\n    1"},{"id":"clean-up-the-router","title":"Clean up the router","role":"content","text":"[code example]\n\n\n[code example]"},{"id":"next-steps","title":"Next Steps","role":"content","text":"Now that you understand semantic routing, explore these related guides:\n\n- [Manage LLM Message History](https://redis.io/docs/latest/message_history) - Store and retrieve conversation history\n- [Cache LLM Responses](https://redis.io/docs/latest/llmcache) - Reduce API costs with semantic caching\n- [Query and Filter Data](https://redis.io/docs/latest/complex_filtering) - Learn more about filter expressions"}],"examples":[{"id":"define-the-routes-ex0","language":"python","code":"from redisvl.extensions.router import Route\n\n# Define routes for the semantic router\ntechnology = Route(\n    name=\"technology\",\n    references=[\n        \"what are the latest advancements in AI?\",\n        \"tell me about the newest gadgets\",\n        \"what's trending in tech?\"\n    ],\n    metadata={\"category\": \"tech\", \"priority\": 1},\n    distance_threshold=0.71\n)\n\nsports = Route(\n    name=\"sports\",\n    references=[\n        \"who won the game last night?\",\n        \"tell me about the upcoming sports events\",\n        \"what's the latest in the world of sports?\",\n        \"sports\",\n        \"basketball and football\"\n    ],\n    metadata={\"category\": \"sports\", \"priority\": 2},\n    distance_threshold=0.72\n)\n\nentertainment = Route(\n    name=\"entertainment\",\n    references=[\n        \"what are the top movies right now?\",\n        \"who won the best actor award?\",\n        \"what's new in the entertainment industry?\"\n    ],\n    metadata={\"category\": \"entertainment\", \"priority\": 3},\n    distance_threshold=0.7\n)","section_id":"define-the-routes"},{"id":"initialize-the-semanticrouter-ex0","language":"python","code":"import os\nfrom redisvl.extensions.router import SemanticRouter\nfrom redisvl.utils.vectorize import HFTextVectorizer\n\nos.environ[\"TOKENIZERS_PARALLELISM\"] = \"false\"\n\n# Initialize the SemanticRouter\nrouter = SemanticRouter(\n    name=\"topic-router\",\n    vectorizer=HFTextVectorizer(),\n    routes=[technology, sports, entertainment],\n    redis_url=\"redis://localhost:6379\",\n    overwrite=True # Blow away any other routing index with this name\n)","section_id":"initialize-the-semanticrouter"},{"id":"initialize-the-semanticrouter-ex1","language":"python","code":"# look at the index specification created for the semantic router\n!rvl index info -i topic-router","section_id":"initialize-the-semanticrouter"},{"id":"initialize-the-semanticrouter-ex2","language":"python","code":"router._index.info()[\"num_docs\"]","section_id":"initialize-the-semanticrouter"},{"id":"simple-routing-ex0","language":"python","code":"# Query the router with a statement\nroute_match = router(\"Can you tell me about the latest in artificial intelligence?\")\nroute_match","section_id":"simple-routing"},{"id":"simple-routing-ex1","language":"python","code":"# Query the router with a statement and return a miss\nroute_match = router(\"are aliens real?\")\nroute_match","section_id":"simple-routing"},{"id":"simple-routing-ex2","language":"python","code":"# Perform multi-class classification with route_many() -- toggle the max_k and the distance_threshold\nroute_matches = router.route_many(\"How is AI used in basketball?\", max_k=3)\nroute_matches","section_id":"simple-routing"},{"id":"simple-routing-ex3","language":"python","code":"# Toggle the aggregation method -- note the different distances in the result\nfrom redisvl.extensions.router.schema import DistanceAggregationMethod\n\nroute_matches = router.route_many(\"How is AI used in basketball?\", aggregation_method=DistanceAggregationMethod.min, max_k=3)\nroute_matches","section_id":"simple-routing"},{"id":"update-the-routing-config-ex0","language":"python","code":"from redisvl.extensions.router import RoutingConfig\n\nrouter.update_routing_config(\n    RoutingConfig(aggregation_method=DistanceAggregationMethod.min, max_k=3)\n)","section_id":"update-the-routing-config"},{"id":"update-the-routing-config-ex1","language":"python","code":"route_matches = router.route_many(\"Lebron James\")\nroute_matches","section_id":"update-the-routing-config"},{"id":"router-serialization-ex0","language":"python","code":"router.to_dict()","section_id":"router-serialization"},{"id":"router-serialization-ex1","language":"python","code":"router2 = SemanticRouter.from_dict(router.to_dict(), redis_url=\"redis://localhost:6379\")\n\nassert router2.to_dict() == router.to_dict()","section_id":"router-serialization"},{"id":"router-serialization-ex2","language":"python","code":"router.to_yaml(\"router.yaml\", overwrite=True)","section_id":"router-serialization"},{"id":"router-serialization-ex3","language":"python","code":"router3 = SemanticRouter.from_yaml(\"router.yaml\", redis_url=\"redis://localhost:6379\")\n\nassert router3.to_dict() == router2.to_dict() == router.to_dict()","section_id":"router-serialization"},{"id":"add-route-references-ex0","language":"python","code":"router.add_route_references(route_name=\"technology\", references=[\"latest AI trends\", \"new tech gadgets\"])","section_id":"add-route-references"},{"id":"get-route-references-ex0","language":"python","code":"# by route name\nrefs = router.get_route_references(route_name=\"technology\")\nrefs","section_id":"get-route-references"},{"id":"get-route-references-ex1","language":"python","code":"# by reference id\nrefs = router.get_route_references(reference_ids=[refs[0][\"reference_id\"]])\nrefs","section_id":"get-route-references"},{"id":"delete-route-references-ex0","language":"python","code":"# by route name\ndeleted_count = router.delete_route_references(route_name=\"sports\")\ndeleted_count","section_id":"delete-route-references"},{"id":"delete-route-references-ex1","language":"python","code":"# by id\ndeleted_count = router.delete_route_references(reference_ids=[refs[0][\"reference_id\"]])\ndeleted_count","section_id":"delete-route-references"},{"id":"clean-up-the-router-ex0","language":"python","code":"# Use clear to flush all routes from the index\nrouter.clear()","section_id":"clean-up-the-router"},{"id":"clean-up-the-router-ex1","language":"python","code":"# Use delete to clear the index and remove it completely\nrouter.delete()","section_id":"clean-up-the-router"}]}
{"schema_version":2,"id":"develop/ai/redisvl/user_guide/how_to_guides/sql_to_redis_queries","title":"Write SQL Queries for Redis","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/how_to_guides/sql_to_redis_queries/","summary":"","aliases":["/integrate/redisvl/user_guide/how_to_guides/12_sql_to_redis_queries","/develop/ai/redisvl/user_guide/sql_to_redis_queries/"],"tags":[],"last_updated":"2026-10-12T09:34:21+02:00","page_type":"content","content_hash":"33313a65ed3501ce82c7b43e3767abc7cbb41432c06863c201a22419a100d332","sections":[{"id":"overview","title":"Overview","role":"overview","text":"While Redis does not natively support SQL, RedisVL provides a `SQLQuery` class that translates SQL-like queries into Redis queries.\n\nThe `SQLQuery` class wraps the [`sql-redis`](https://pypi.org/project/sql-redis/) package. This package is not installed by default, so install it with:\n\n[code example]"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before you begin, ensure you have:\n- Installed RedisVL with SQL support: `pip install redisvl[sql-redis]`\n- A running Redis instance ([Redis 8+](https://redis.io/downloads/) or [Redis Cloud](https://redis.io/cloud))"},{"id":"what-youll-learn","title":"What You'll Learn","role":"content","text":"By the end of this guide, you will be able to:\n- Write SQL-like queries for Redis using `SQLQuery`\n- Translate SELECT, WHERE, and ORDER BY clauses to Redis queries\n- Combine SQL queries with vector search\n- Use aggregate functions and grouping\n- Query geographic data with `geo_distance()`\n- Filter and extract date/time data with `YEAR()`, `MONTH()`, and `DATE_FORMAT()`"},{"id":"table-of-contents","title":"Table of Contents","role":"content","text":"1. [Define the schema](#define-the-schema)\n2. [Create sample dataset](#create-sample-dataset)\n3. [Create a SearchIndex](#create-a-searchindex)\n4. [Load data](#load-data)\n5. [Write SQL queries](#write-sql-queries)\n6. [Query types](#query-types)\n   - [Text searches](#text-searches)\n   - [Aggregations](#aggregations)\n   - [Vector search](#vector-search)\n   - [Geographic queries](#geographic-queries)\n   - [Date and datetime queries](#date-and-datetime-queries)\n7. [Async support](#async-support)\n8. [Additional query examples](#additional-query-examples)\n9. [Cleanup](#cleanup)"},{"id":"define-the-schema","title":"Define the schema","role":"content","text":"[code example]\n\n    /Users/robert.shelton/Documents/redis-vl-python/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n      from .autonotebook import tqdm as notebook_tqdm"},{"id":"create-sample-dataset","title":"Create sample dataset","role":"content","text":"[code example]"},{"id":"create-a-searchindex","title":"Create a SearchIndex","role":"content","text":"With the schema and sample dataset ready, create a `SearchIndex`."},{"id":"bring-your-own-redis-connection-instance","title":"Bring your own Redis connection instance","role":"content","text":"This is ideal in scenarios where you have custom settings on the connection instance or if your application will share a connection pool:\n\n\n[code example]"},{"id":"let-the-index-manage-the-connection-instance","title":"Let the index manage the connection instance","role":"content","text":"This is ideal for simple cases:\n\n\n[code example]"},{"id":"create-the-index","title":"Create the index","role":"content","text":"Now that we are connected to Redis, we need to run the create command.\n\n\n[code example]"},{"id":"load-data","title":"Load data","role":"content","text":"Load the sample dataset to Redis."},{"id":"validate-data-entries-on-load","title":"Validate data entries on load","role":"content","text":"RedisVL uses pydantic validation under the hood to ensure loaded data is valid and conforms to your schema. This setting is optional and can be configured via `validate_on_load=True` in the `SearchIndex` class.\n\n\n[code example]\n\n    ['user_simple_docs:01KN7Y4J630537VY4Y5D9EZMYX', 'user_simple_docs:01KN7Y4J630537VY4Y5D9EZMYY', 'user_simple_docs:01KN7Y4J630537VY4Y5D9EZMYZ', 'user_simple_docs:01KN7Y4J630537VY4Y5D9EZMZ0', 'user_simple_docs:01KN7Y4J630537VY4Y5D9EZMZ1']"},{"id":"write-sql-queries","title":"Write SQL queries","role":"content","text":"First, let's test a simple select statement such as the one below.\n\n\n[code example]"},{"id":"check-the-created-query-string","title":"Check the created query string","role":"content","text":"[code example]\n\n\n\n\n    'FT.SEARCH user_simple \"@age:[(17 +inf]\" RETURN 4 user region job age DIALECT 2'"},{"id":"executing-the-query","title":"Executing the query","role":"content","text":"[code example]\n\n\n\n\n    [{'user': 'john',\n      'region': 'us-west',\n      'job': 'software engineer',\n      'age': '34'},\n     {'user': 'bill', 'region': 'us-central', 'job': 'engineer', 'age': '54'},\n     {'user': 'mary', 'region': 'us-central', 'job': 'doctor', 'age': '24'},\n     {'user': 'joe', 'region': 'us-east', 'job': 'dentist', 'age': '27'},\n     {'user': 'stacy', 'region': 'us-west', 'job': 'project manager', 'age': '61'}]"},{"id":"query-types","title":"Query types","role":"content","text":""},{"id":"conditional-operators","title":"Conditional operators","role":"content","text":"[code example]\n\n    Resulting redis query:  FT.SEARCH user_simple \"@age:[(17 +inf] @region:{us\\-west}\" RETURN 4 user region job age DIALECT 2\n\n\n\n\n\n    [{'user': 'john',\n      'region': 'us-west',\n      'job': 'software engineer',\n      'age': '34'},\n     {'user': 'stacy', 'region': 'us-west', 'job': 'project manager', 'age': '61'}]\n\n\n\n\n[code example]\n\n    Resulting redis query:  FT.SEARCH user_simple \"((@region:{us\\-west})|(@region:{us\\-central}))\" RETURN 4 user region job age\n\n\n\n\n\n    [{'user': 'mary', 'region': 'us-central', 'job': 'doctor', 'age': '24'},\n     {'user': 'bill', 'region': 'us-central', 'job': 'engineer', 'age': '54'},\n     {'user': 'stacy', 'region': 'us-west', 'job': 'project manager', 'age': '61'},\n     {'user': 'john',\n      'region': 'us-west',\n      'job': 'software engineer',\n      'age': '34'}]\n\n\n\n\n[code example]\n\n    Resulting redis query:  FT.SEARCH user_simple \"@job:{software engineer|engineer|pancake tester}\" RETURN 4 user region job age\n\n\n\n\n\n    [{'user': 'bill', 'region': 'us-central', 'job': 'engineer', 'age': '54'},\n     {'user': 'john',\n      'region': 'us-west',\n      'job': 'software engineer',\n      'age': '34'}]"},{"id":"text-searches","title":"Text searches","role":"content","text":"See [the docs](https://redis.io/docs/latest/develop/ai/search-and-query/query/full-text/) for available text queries in Redis.\n\nFor more on exact matching see [here](https://redis.io/docs/latest/develop/ai/search-and-query/query/exact-match/).\n\nWith `sql-redis >= 0.4.0`, TEXT search operators are explicit:\n\n- `WHERE job_description = 'healthcare including'` for exact phrase matching\n- `WHERE job_description LIKE 'sci%'`, `LIKE '%care'`, or `LIKE '%diagnose%'` for wildcard matching\n- `WHERE fuzzy(job_description, 'diagnose')` for typo-tolerant matching\n- `WHERE fulltext(job_description, 'healthcare OR diagnosing')` for tokenized search\n\n\n\n[code example]\n\n    Resulting redis query:  FT.SEARCH user_simple \"@job_description:sci*\" RETURN 5 user region job job_description age\n\n\n\n\n\n    [{'user': 'bill',\n      'region': 'us-central',\n      'job': 'engineer',\n      'job_description': 'Applies scientific and mathematical principles to solve technical problems.',\n      'age': '54'}]\n\n\n\n\n[code example]\n\n    Resulting redis query:  FT.SEARCH user_simple \"@job_description:*care\" RETURN 5 user region job job_description age\n\n\n\n\n\n    [{'user': 'mary',\n      'region': 'us-central',\n      'job': 'doctor',\n      'job_description': 'Diagnoses and treats illnesses, injuries, and other medical conditions in the healthcare field.',\n      'age': '24'},\n     {'user': 'joe',\n      'region': 'us-east',\n      'job': 'dentist',\n      'job_description': 'Provides oral healthcare including diagnosing and treating teeth and gum issues.',\n      'age': '27'}]\n\n\n\n\n[code example]\n\n    Resulting redis query:  FT.SEARCH user_simple \"@job_description:*diagnose*\" RETURN 5 user region job job_description age\n\n\n\n\n\n    [{'user': 'mary',\n      'region': 'us-central',\n      'job': 'doctor',\n      'job_description': 'Diagnoses and treats illnesses, injuries, and other medical conditions in the healthcare field.',\n      'age': '24'}]\n\n\n\n\n[code example]\n\n    Resulting redis query:  FT.SEARCH user_simple \"@job_description:\"healthcare including\"\" RETURN 5 user region job job_description age\n\n\n\n\n\n    [{'user': 'joe',\n      'region': 'us-east',\n      'job': 'dentist',\n      'job_description': 'Provides oral healthcare including diagnosing and treating teeth and gum issues.',\n      'age': '27'}]\n\n\n\n\n[code example]\n\n    Resulting redis query:  FT.SEARCH user_simple \"@job_description:\"diagnosing treating\"\" RETURN 5 user region job job_description age\n\n\n\n\n\n    [{'user': 'joe',\n      'region': 'us-east',\n      'job': 'dentist',\n      'job_description': 'Provides oral healthcare including diagnosing and treating teeth and gum issues.',\n      'age': '27'}]\n\n\n\n\n[code example]\n\n    Resulting redis query:  FT.SEARCH user_simple \"@age:[40 60]\" RETURN 4 user region job age\n\n\n\n\n\n    [{'user': 'bill', 'region': 'us-central', 'job': 'engineer', 'age': '54'}]"},{"id":"aggregations","title":"Aggregations","role":"content","text":"See docs for redis supported reducer functions: [docs](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/aggregations/#supported-groupby-reducers).\n\n\n[code example]\n\n    Resulting redis query:  FT.AGGREGATE user_simple \"*\" LOAD 3 @age @region @user GROUPBY 1 @region REDUCE COUNT 0 AS count_age REDUCE COUNT_DISTINCT 1 @age AS count_distinct_age REDUCE MIN 1 @age AS min_age REDUCE MAX 1 @age AS max_age REDUCE AVG 1 @age AS avg_age REDUCE STDDEV 1 @age AS std_age REDUCE FIRST_VALUE 1 @age AS fist_value_age REDUCE TOLIST 1 @age AS to_list_age REDUCE QUANTILE 2 @age 0.99 AS quantile_age\n\n\n\n\n\n    [{'region': 'us-west',\n      'count_age': '2',\n      'count_distinct_age': '2',\n      'min_age': '34',\n      'max_age': '61',\n      'avg_age': '47.5',\n      'std_age': '19.091883092',\n      'fist_value_age': '61',\n      'to_list_age': ['34', '61'],\n      'quantile_age': '61'},\n     {'region': 'us-central',\n      'count_age': '2',\n      'count_distinct_age': '2',\n      'min_age': '24',\n      'max_age': '54',\n      'avg_age': '39',\n      'std_age': '21.2132034356',\n      'fist_value_age': '24',\n      'to_list_age': ['24', '54'],\n      'quantile_age': '54'},\n     {'region': 'us-east',\n      'count_age': '1',\n      'count_distinct_age': '1',\n      'min_age': '27',\n      'max_age': '27',\n      'avg_age': '27',\n      'std_age': '0',\n      'fist_value_age': '27',\n      'to_list_age': ['27'],\n      'quantile_age': '27'}]"},{"id":"vector-search","title":"Vector search","role":"content","text":"[code example]\n\n    Resulting redis query:  FT.SEARCH user_simple \"*=>[KNN 10 @job_embedding $vector AS vector_distance]\" PARAMS 2 vector $vector DIALECT 2 RETURN 4 user job job_description vector_distance SORTBY vector_distance ASC\n\n\n\n\n\n    [{'vector_distance': '0.823510587215',\n      'user': 'bill',\n      'job': 'engineer',\n      'job_description': 'Applies scientific and mathematical principles to solve technical problems.'},\n     {'vector_distance': '0.965160369873',\n      'user': 'john',\n      'job': 'software engineer',\n      'job_description': 'Designs, develops, and maintains software applications and systems.'},\n     {'vector_distance': '1.00401353836',\n      'user': 'mary',\n      'job': 'doctor',\n      'job_description': 'Diagnoses and treats illnesses, injuries, and other medical conditions in the healthcare field.'},\n     {'vector_distance': '1.0062687397',\n      'user': 'stacy',\n      'job': 'project manager',\n      'job_description': 'Plans, organizes, and oversees projects from inception to completion.'},\n     {'vector_distance': '1.01110625267',\n      'user': 'joe',\n      'job': 'dentist',\n      'job_description': 'Provides oral healthcare including diagnosing and treating teeth and gum issues.'}]\n\n\n\n\n[code example]\n\n    Resulting redis query:  FT.SEARCH user_simple \"(@region:{us\\-central})=>[KNN 10 @job_embedding $vector AS vector_distance]\" PARAMS 2 vector $vector DIALECT 2 RETURN 3 user region vector_distance SORTBY vector_distance ASC\n\n\n\n\n\n    [{'vector_distance': '0.823510587215', 'user': 'bill', 'region': 'us-central'},\n     {'vector_distance': '1.00401353836', 'user': 'mary', 'region': 'us-central'}]"},{"id":"hybrid-search-fthybrid","title":"Hybrid search (FT.HYBRID)","role":"content","text":"Use `hybrid_vector_search()` to fuse a full-text query and a vector query into a single ranking **server-side** with Redis's native `FT.HYBRID`. It composes the `cosine_distance()` (vector leg) and `fulltext()` (text leg) functions, with `rrf()` or `linear()` selecting the fusion method (reciprocal rank fusion or a linear weighting).\n\nThis differs from the *pre-filter* hybrid search above (a `WHERE` clause narrowing a KNN search): here both legs are ranked independently and fused, rather than text acting only as a filter.\n\nRequires Redis 8.4+ and `redis-py >= 7.1.0`.\n\n\n[code example]"},{"id":"geographic-queries","title":"Geographic queries","role":"content","text":"Use `geo_distance()` to filter by location or calculate distances between points.\n\n**Syntax:**\n- Filter: `WHERE geo_distance(field, POINT(lon, lat), 'unit') < radius`\n- Distance: `SELECT geo_distance(field, POINT(lon, lat)) AS distance`\n\n**Units:** `'km'` (kilometers), `'mi'` (miles), `'m'` (meters), `'ft'` (feet)\n\n**Note:** `POINT()` uses longitude first, then latitude - matching Redis conventions.\n\n\n[code example]\n\n    Resulting redis query:  FT.SEARCH user_simple \"*\" GEOFILTER office_location -122.4194 37.7749 500.0 km RETURN 4 user job region office_location\n\n\n\n\n\n    [{'user': 'stacy',\n      'job': 'project manager',\n      'region': 'us-west',\n      'office_location': '-122.4194,37.7749'},\n     {'user': 'john',\n      'job': 'software engineer',\n      'region': 'us-west',\n      'office_location': '-122.4194,37.7749'}]\n\n\n\n\n[code example]\n\n    Resulting redis query:  FT.SEARCH user_simple \"*\" GEOFILTER office_location -87.6298 41.8781 50.0 mi RETURN 3 user job region\n\n\n\n\n\n    [{'user': 'mary', 'job': 'doctor', 'region': 'us-central'},\n     {'user': 'bill', 'job': 'engineer', 'region': 'us-central'}]\n\n\n\n\n[code example]\n\n    Resulting redis query:  FT.SEARCH user_simple \"@job:{engineer}\" GEOFILTER office_location -87.6298 41.8781 50.0 mi RETURN 3 user job region\n\n\n\n\n\n    [{'user': 'bill', 'job': 'engineer', 'region': 'us-central'}]\n\n\n\n\n[code example]\n\n    Resulting redis query:  FT.SEARCH user_simple \"@age:[(30 +inf]\" GEOFILTER office_location -122.4194 37.7749 100.0 km RETURN 3 user job age\n\n\n\n\n\n    [{'user': 'stacy', 'job': 'project manager', 'age': '61'},\n     {'user': 'john', 'job': 'software engineer', 'age': '34'}]\n\n\n\n\n[code example]\n\n    Resulting redis query:  FT.SEARCH user_simple \"@job_description:technical*\" GEOFILTER office_location -87.6298 41.8781 100.0 km RETURN 3 user job job_description\n\n\n\n\n\n    [{'user': 'bill',\n      'job': 'engineer',\n      'job_description': 'Applies scientific and mathematical principles to solve technical problems.'}]\n\n\n\n\n[code example]\n\n    Resulting redis query:  FT.AGGREGATE user_simple \"*\" LOAD 3 @office_location @region @user APPLY geodistance(@office_location, -73.9857, 40.758) AS distance_meters\n    \n    Distances from NYC:\n      joe        | us-east      | 0 km\n      mary       | us-central   | 1,145 km\n      bill       | us-central   | 1,145 km\n      stacy      | us-west      | 4,131 km\n      john       | us-west      | 4,131 km"},{"id":"geo-query-summary","title":"GEO Query Summary","role":"content","text":"| Method | Pattern | Example |\n|--------|---------|---------|\n| **SQL - Basic radius** | `WHERE geo_distance(field, POINT(lon, lat), 'unit') < radius` | `WHERE geo_distance(location, POINT(-122.4, 37.8), 'km') < 50` |\n| **SQL - With miles** | Same with `'mi'` unit | `WHERE geo_distance(location, POINT(-73.9, 40.7), 'mi') < 10` |\n| **SQL - With TAG** | Combined with `AND` | `WHERE category = 'retail' AND geo_distance(...) < 100` |\n| **SQL - With NUMERIC** | Combined with `AND` | `WHERE age > 30 AND geo_distance(...) < 100` |\n| **SQL - Distance calc** | `SELECT geo_distance(...)` | `SELECT geo_distance(location, POINT(lon, lat)) AS dist` |\n| **Native - Within** | `Geo(field) == GeoRadius(...)` | `Geo(\"location\") == GeoRadius(-122.4, 37.8, 100, \"km\")` |\n| **Native - Outside** | `Geo(field) != GeoRadius(...)` | `Geo(\"location\") != GeoRadius(-87.6, 41.9, 1000, \"km\")` |\n| **Native - Combined** | Use `&` and `\\|` operators | `geo_filter & tag_filter & num_filter` |\n\n**Key Points:**\n1. **Coordinate Format**: `\"longitude,latitude\"` - longitude first!\n2. **POINT() Syntax**: `POINT(lon, lat)` - longitude first (matches Redis)\n3. **Units**: `'km'`, `'mi'`, `'m'`, `'ft'`\n4. **geo_distance()**: Returns meters, divide by 1000 for km"},{"id":"date-and-datetime-queries","title":"Date and datetime queries","role":"content","text":"Use date literals and functions to query timestamp data. Redis stores dates as Unix timestamps in NUMERIC fields.\n\n**Key Concepts:**\n- Date literals like `'2024-01-01'` are auto-converted to Unix timestamps\n- Date functions (`YEAR()`, `MONTH()`, `DAY()`) extract date parts\n- `DATE_FORMAT()` formats timestamps as readable strings\n\n\n[code example]\n\n    Loaded 7 events:\n      - New Year Kickoff          | 2024-01-01 | meeting\n      - Q1 Planning               | 2024-01-15 | meeting\n      - Product Launch            | 2024-02-20 | release\n      - Team Offsite              | 2024-03-10 | meeting\n      - Summer Summit             | 2024-07-15 | conference\n      - Holiday Party 2023        | 2023-12-15 | conference\n      - Year End Review 2023      | 2023-12-20 | meeting\n\n\n\n[code example]\n\n    Resulting redis query:  FT.SEARCH events \"@created_at:[(1704067200 +inf]\" RETURN 2 name category\n    \n    Events after 2024-01-01 (4 found):\n      - Summer Summit\n      - Q1 Planning\n      - Team Offsite\n      - Product Launch\n\n\n\n[code example]\n\n    Resulting redis query:  FT.SEARCH events \"@created_at:[1704067200 1711843200]\" RETURN 2 name category\n    \n    Events in Q1 2024 (4 found):\n      - Q1 Planning (meeting)\n      - New Year Kickoff (meeting)\n      - Team Offsite (meeting)\n      - Product Launch (release)\n\n\n\n[code example]\n\n    Meetings in H1 2024 (3 found):\n      - Q1 Planning\n      - New Year Kickoff\n      - Team Offsite"},{"id":"date-query-summary","title":"Date Query Summary","role":"content","text":"| Pattern | Example |\n|---------|---------|\n| **After date** | `WHERE created_at > '2024-01-01'` |\n| **Before date** | `WHERE created_at < '2024-12-31'` |\n| **Date range** | `WHERE created_at BETWEEN '2024-01-01' AND '2024-03-31'` |\n| **Extract year** | `SELECT YEAR(created_at) AS year` |\n| **Extract month** | `SELECT MONTH(created_at) AS month` (returns 0-11) |\n| **Filter by year** | `WHERE YEAR(created_at) = 2024` |\n| **Group by date** | `GROUP BY YEAR(created_at)` |\n| **Format date** | `DATE_FORMAT(created_at, '%Y-%m-%d')` |\n\n**Key Points:**\n1. **Storage**: Dates stored as Unix timestamps in NUMERIC fields\n2. **Date Literals**: ISO 8601 strings auto-converted to timestamps\n3. **Timezone**: Dates without timezone are treated as UTC\n4. **Month Index**: Redis `MONTH()` returns 0-11, not 1-12"},{"id":"async-support","title":"Async support","role":"content","text":"SQL queries also work with `AsyncSearchIndex` for async applications:\n\n\n[code example]"},{"id":"additional-query-examples","title":"Additional Query Examples","role":"content","text":"The following sections provide more detailed examples for geographic and date queries."},{"id":"native-geo-filters","title":"Native GEO filters","role":"content","text":"As an alternative to SQL syntax, RedisVL provides native `Geo` and `GeoRadius` filter classes.\nThese can be combined with other filters using `&` (AND) and `|` (OR) operators.\n\n\n[code example]\n\n    Filter expression: @office_location:[-87.6298 41.8781 100 km]\n    \n    Users within 100km of Chicago (2 found):\n      - mary (doctor) - us-central\n      - bill (engineer) - us-central\n\n\n\n[code example]\n\n    Filter expression: (-@office_location:[-87.6298 41.8781 1000 km])\n    \n    Users OUTSIDE 1000km of Chicago (3 found):\n      - joe (us-east)\n      - stacy (us-west)\n      - john (us-west)\n\n\n\n[code example]\n\n    Combined filter: ((@office_location:[-87.6298 41.8781 500 km] @job:{engineer}) @age:[(40 +inf])\n    \n    Engineers over 40 within 500km of Chicago (1 found):\n      - bill (age: 54) - us-central"},{"id":"additional-date-examples","title":"Additional Date Examples","role":"content","text":"More advanced date query patterns including date function extraction and formatting.\n\n\n[code example]\n\n    Resulting redis query:  FT.AGGREGATE events \"*\" LOAD 2 @created_at @name APPLY year(@created_at) AS year APPLY monthofyear(@created_at) AS month\n    \n    Events with year/month:\n      - Summer Summit             | 2024-07\n      - Q1 Planning               | 2024-01\n      - Year End Review 2023      | 2023-12\n      - New Year Kickoff          | 2024-01\n      - Holiday Party 2023        | 2023-12\n      - Team Offsite              | 2024-03\n      - Product Launch            | 2024-02\n\n\n\n[code example]\n\n    Resulting redis query:  FT.AGGREGATE events \"*\" LOAD 2 @created_at @name APPLY year(@created_at) AS year_created_at FILTER @year_created_at == 2024\n    \n    Events in 2024 (5 found):\n      - Summer Summit\n      - Q1 Planning\n      - New Year Kickoff\n      - Team Offsite\n      - Product Launch\n\n\n\n[code example]\n\n    Resulting redis query:  FT.AGGREGATE events \"*\" LOAD 2 @created_at @year APPLY year(@created_at) AS year GROUPBY 1 @year REDUCE COUNT 0 AS event_count\n    \n    Events per year:\n      2023: 2 events\n      2024: 5 events\n\n\n\n[code example]\n\n    Resulting redis query:  FT.AGGREGATE events \"*\" LOAD 2 @created_at @name APPLY timefmt(@created_at, \"%Y-%m-%d\") AS event_date\n    \n    Events with formatted dates:\n      - Summer Summit             | 2024-07-15\n      - Q1 Planning               | 2024-01-15\n      - Year End Review 2023      | 2023-12-20\n      - New Year Kickoff          | 2024-01-01\n      - Holiday Party 2023        | 2023-12-15\n      - Team Offsite              | 2024-03-10\n      - Product Launch            | 2024-02-20"},{"id":"next-steps","title":"Next Steps","role":"content","text":"Now that you understand SQL queries for Redis, explore these related guides:\n\n- [Use Advanced Query Types](https://redis.io/docs/latest/advanced_queries) - Learn about TextQuery, HybridQuery, and MultiVectorQuery\n- [Query and Filter Data](https://redis.io/docs/latest/complex_filtering) - Apply filters using native RedisVL query syntax\n- [Getting Started](https://redis.io/docs/latest/../getting_started) - Review the basics of RedisVL indexes"},{"id":"cleanup","title":"Cleanup","role":"content","text":"To remove all data from Redis associated with the index, use the `.clear()` method. This leaves the index in place for future insertions or updates.\n\nTo remove everything including the index, use `.delete()` which removes both the index and the underlying data.\n\n\n[code example]"}],"examples":[{"id":"overview-ex0","language":"bash","code":"pip install redisvl[sql-redis]","section_id":"overview"},{"id":"define-the-schema-ex0","language":"python","code":"from redisvl.utils.vectorize import HFTextVectorizer\n\nhf = HFTextVectorizer()\n\nschema = {\n    \"index\": {\n        \"name\": \"user_simple\",\n        \"prefix\": \"user_simple_docs\",\n        \"storage_type\": \"json\",\n    },\n    \"fields\": [\n        {\"name\": \"user\", \"type\": \"tag\"},\n        {\"name\": \"region\", \"type\": \"tag\"},\n        {\"name\": \"job\", \"type\": \"tag\"},\n        {\"name\": \"job_description\", \"type\": \"text\"},\n        {\"name\": \"age\", \"type\": \"numeric\"},\n        {\"name\": \"office_location\", \"type\": \"geo\"},\n        {\n            \"name\": \"job_embedding\",\n            \"type\": \"vector\",\n            \"attrs\": {\n                \"dims\": len(hf.embed(\"get embed length\")),\n                \"distance_metric\": \"cosine\",\n                \"algorithm\": \"flat\",\n                \"datatype\": \"float32\"\n            }\n        }\n    ]\n}","section_id":"define-the-schema"},{"id":"create-sample-dataset-ex0","language":"python","code":"# Office locations use \"longitude,latitude\" format (lon,lat - Redis convention)\n# San Francisco: -122.4194, 37.7749\n# Chicago: -87.6298, 41.8781\n# New York: -73.9857, 40.7580\ndata = [\n    {\n        'user': 'john',\n        'age': 34,\n        'job': 'software engineer',\n        'region': 'us-west',\n        'job_description': 'Designs, develops, and maintains software applications and systems.',\n        'office_location': '-122.4194,37.7749'  # San Francisco\n    },\n    {\n        'user': 'bill',\n        'age': 54,\n        'job': 'engineer',\n        'region': 'us-central',\n        'job_description': 'Applies scientific and mathematical principles to solve technical problems.',\n        'office_location': '-87.6298,41.8781'  # Chicago\n    },\n    {\n        'user': 'mary',\n        'age': 24,\n        'job': 'doctor',\n        'region': 'us-central',\n        'job_description': 'Diagnoses and treats illnesses, injuries, and other medical conditions in the healthcare field.',\n        'office_location': '-87.6298,41.8781'  # Chicago\n    },\n    {\n        'user': 'joe',\n        'age': 27,\n        'job': 'dentist',\n        'region': 'us-east',\n        'job_description': 'Provides oral healthcare including diagnosing and treating teeth and gum issues.',\n        'office_location': '-73.9857,40.7580'  # New York\n    },\n    {\n        'user': 'stacy',\n        'age': 61,\n        'job': 'project manager',\n        'region': 'us-west',\n        'job_description': 'Plans, organizes, and oversees projects from inception to completion.',\n        'office_location': '-122.4194,37.7749'  # San Francisco\n    }\n]\n\ndata = [\n    {  \n        **d,\n        \"job_embedding\": hf.embed(f\"{d['job_description']=} {d['job']=}\"),\n    } \n    for d in data\n]","section_id":"create-sample-dataset"},{"id":"bring-your-own-redis-connection-instance-ex0","language":"python","code":"from redisvl.index import SearchIndex\nfrom redis import Redis\n\nclient = Redis.from_url(\"redis://localhost:6379\")\nindex = SearchIndex.from_dict(schema, redis_client=client)","section_id":"bring-your-own-redis-connection-instance"},{"id":"let-the-index-manage-the-connection-instance-ex0","language":"python","code":"index = SearchIndex.from_dict(schema, redis_url=\"redis://localhost:6379\", validate_on_load=True)","section_id":"let-the-index-manage-the-connection-instance"},{"id":"create-the-index-ex0","language":"python","code":"index.create(overwrite=True, drop=True)","section_id":"create-the-index"},{"id":"validate-data-entries-on-load-ex0","language":"python","code":"keys = index.load(data)\n\nprint(keys)","section_id":"validate-data-entries-on-load"},{"id":"write-sql-queries-ex0","language":"python","code":"from redisvl.query import SQLQuery\n\nsql_str = \"\"\"\n    SELECT user, region, job, age\n    FROM user_simple\n    WHERE age > 17\n    \"\"\"\n\n# Optional sql_redis_options are passed through to sql-redis.\n# schema_cache_strategy balances startup cost vs repeated-query speed:\n# use \"lazy\" (default) to load schemas on demand, or \"load_all\"\n# to preload schemas up front for broader repeated-query workloads.\nsql_query = SQLQuery(\n    sql_str, sql_redis_options={\"schema_cache_strategy\": \"lazy\"}\n)","section_id":"write-sql-queries"},{"id":"check-the-created-query-string-ex0","language":"python","code":"sql_query.redis_query_string(redis_url=\"redis://localhost:6379\")","section_id":"check-the-created-query-string"},{"id":"executing-the-query-ex0","language":"python","code":"results = index.query(sql_query)\nresults","section_id":"executing-the-query"},{"id":"conditional-operators-ex0","language":"python","code":"sql_str = \"\"\"\n    SELECT user, region, job, age\n    FROM user_simple\n    WHERE age > 17 and region = 'us-west'\n\"\"\"\n\nsql_query = SQLQuery(sql_str)\nredis_query = sql_query.redis_query_string(redis_url=\"redis://localhost:6379\")\nprint(\"Resulting redis query: \", redis_query)\nresults = index.query(sql_query)\nresults","section_id":"conditional-operators"},{"id":"conditional-operators-ex1","language":"python","code":"sql_str = \"\"\"\n    SELECT user, region, job, age\n    FROM user_simple\n    WHERE region = 'us-west' or region = 'us-central'\n    \"\"\"\n\nsql_query = SQLQuery(sql_str)\nredis_query = sql_query.redis_query_string(redis_url=\"redis://localhost:6379\")\nprint(\"Resulting redis query: \", redis_query)\nresults = index.query(sql_query)\nresults","section_id":"conditional-operators"},{"id":"conditional-operators-ex2","language":"python","code":"# job is a tag field therefore this syntax works\nsql_str = \"\"\"\n    SELECT user, region, job, age\n    FROM user_simple\n    WHERE job IN ('software engineer', 'engineer', 'pancake tester')\n    \"\"\"\n\nsql_query = SQLQuery(sql_str)\nredis_query = sql_query.redis_query_string(redis_url=\"redis://localhost:6379\")\nprint(\"Resulting redis query: \", redis_query)\nresults = index.query(sql_query)\nresults","section_id":"conditional-operators"},{"id":"text-searches-ex0","language":"python","code":"# Prefix (LIKE)\nsql_str = \"\"\"\n    SELECT user, region, job, job_description, age\n    FROM user_simple\n    WHERE job_description LIKE 'sci%'\n\"\"\"\n\nsql_query = SQLQuery(sql_str)\nredis_query = sql_query.redis_query_string(redis_url=\"redis://localhost:6379\")\nprint(\"Resulting redis query: \", redis_query)\nresults = index.query(sql_query)\nresults","section_id":"text-searches"},{"id":"text-searches-ex1","language":"python","code":"# Suffix (LIKE)\nsql_str = \"\"\"\n    SELECT user, region, job, job_description, age\n    FROM user_simple\n    WHERE job_description LIKE '%care'\n\"\"\"\n\nsql_query = SQLQuery(sql_str)\nredis_query = sql_query.redis_query_string(redis_url=\"redis://localhost:6379\")\nprint(\"Resulting redis query: \", redis_query)\nresults = index.query(sql_query)\nresults","section_id":"text-searches"},{"id":"text-searches-ex2","language":"python","code":"# Contains (LIKE)\nsql_str = \"\"\"\n    SELECT user, region, job, job_description, age\n    FROM user_simple\n    WHERE job_description LIKE '%diagnose%'\n\"\"\"\n\nsql_query = SQLQuery(sql_str)\nredis_query = sql_query.redis_query_string(redis_url=\"redis://localhost:6379\")\nprint(\"Resulting redis query: \", redis_query)\nresults = index.query(sql_query)\nresults","section_id":"text-searches"},{"id":"text-searches-ex3","language":"python","code":"# Phrase no stop words\nsql_str = \"\"\"\n    SELECT user, region, job, job_description, age\n    FROM user_simple\n    WHERE job_description = 'healthcare including'\n\"\"\"\n\nsql_query = SQLQuery(sql_str)\nredis_query = sql_query.redis_query_string(redis_url=\"redis://localhost:6379\")\nprint(\"Resulting redis query: \", redis_query)\nresults = index.query(sql_query)\nresults","section_id":"text-searches"},{"id":"text-searches-ex4","language":"python","code":"# Phrase with stop words (sql-redis strips default stopwords and warns)\nsql_str = \"\"\"\n    SELECT user, region, job, job_description, age\n    FROM user_simple\n    WHERE job_description = 'diagnosing and treating'\n\"\"\"\n\nsql_query = SQLQuery(sql_str)\nredis_query = sql_query.redis_query_string(redis_url=\"redis://localhost:6379\")\nprint(\"Resulting redis query: \", redis_query)\nresults = index.query(sql_query)\nresults","section_id":"text-searches"},{"id":"text-searches-ex5","language":"python","code":"sql_str = \"\"\"\n    SELECT user, region, job, age\n    FROM user_simple\n    WHERE age BETWEEN 40 and 60\n    \"\"\"\n\nsql_query = SQLQuery(sql_str)\nredis_query = sql_query.redis_query_string(redis_url=\"redis://localhost:6379\")\nprint(\"Resulting redis query: \", redis_query)\nresults = index.query(sql_query)\nresults","section_id":"text-searches"},{"id":"aggregations-ex0","language":"python","code":"sql_str = \"\"\"\n    SELECT\n        user,\n        COUNT(age) as count_age,\n        COUNT_DISTINCT(age) as count_distinct_age,\n        MIN(age) as min_age,\n        MAX(age) as max_age,\n        AVG(age) as avg_age,\n        STDEV(age) as std_age,\n        FIRST_VALUE(age) as fist_value_age,\n        ARRAY_AGG(age) as to_list_age,\n        QUANTILE(age, 0.99) as quantile_age\n    FROM user_simple\n    GROUP BY region\n    \"\"\"\n\nsql_query = SQLQuery(sql_str)\nredis_query = sql_query.redis_query_string(redis_url=\"redis://localhost:6379\")\nprint(\"Resulting redis query: \", redis_query)\nresults = index.query(sql_query)\nresults","section_id":"aggregations"},{"id":"vector-search-ex0","language":"python","code":"sql_str = \"\"\"\n    SELECT user, job, job_description, cosine_distance(job_embedding, :vec) AS vector_distance\n    FROM user_simple\n    ORDER BY vector_distance ASC\n    \"\"\"\n\nvec = hf.embed(\"looking for someone to use base principles to solve problems\", as_buffer=True)\nsql_query = SQLQuery(sql_str, params={\"vec\": vec})\n\nredis_query = sql_query.redis_query_string(redis_url=\"redis://localhost:6379\")\nprint(\"Resulting redis query: \", redis_query)\nresults = index.query(sql_query)\n\nresults","section_id":"vector-search"},{"id":"vector-search-ex1","language":"python","code":"sql_str = \"\"\"\n    SELECT user, region, cosine_distance(job_embedding, :vec) AS vector_distance\n    FROM user_simple\n    WHERE region = 'us-central'\n    ORDER BY vector_distance ASC\n    \"\"\"\n\nvec = hf.embed(\"looking for someone to use base principles to solve problems\", as_buffer=True)\nsql_query = SQLQuery(sql_str, params={\"vec\": vec})\n\nredis_query = sql_query.redis_query_string(redis_url=\"redis://localhost:6379\")\nprint(\"Resulting redis query: \", redis_query)\nresults = index.query(sql_query)\n\nresults","section_id":"vector-search"},{"id":"hybrid-search-fthybrid-ex0","language":"python","code":"sql_str = \"\"\"\n    SELECT user, job, job_description,\n           hybrid_vector_search(\n               cosine_distance(job_embedding, :vec),\n               fulltext(job_description, 'principles solve problems'),\n               rrf()\n           ) AS hybrid_score\n    FROM user_simple\n    ORDER BY hybrid_score DESC\n    LIMIT 5\n    \"\"\"\n\nvec = hf.embed(\"looking for someone to use base principles to solve problems\", as_buffer=True)\nsql_query = SQLQuery(sql_str, params={\"vec\": vec})\n\nredis_query = sql_query.redis_query_string(redis_url=\"redis://localhost:6379\")\nprint(\"Resulting redis query: \", redis_query)\nresults = index.query(sql_query)\n\nresults","section_id":"hybrid-search-fthybrid"},{"id":"geographic-queries-ex0","language":"python","code":"# Find users within 500km of San Francisco\nsql_str = \"\"\"\n    SELECT user, job, region, office_location\n    FROM user_simple\n    WHERE geo_distance(office_location, POINT(-122.4194, 37.7749), 'km') < 500\n\"\"\"\n\nsql_query = SQLQuery(sql_str)\nredis_query = sql_query.redis_query_string(redis_url=\"redis://localhost:6379\")\nprint(\"Resulting redis query: \", redis_query)\nresults = index.query(sql_query)\nresults","section_id":"geographic-queries"},{"id":"geographic-queries-ex1","language":"python","code":"# Find users within 50 miles of Chicago (using miles)\nsql_str = \"\"\"\n    SELECT user, job, region\n    FROM user_simple\n    WHERE geo_distance(office_location, POINT(-87.6298, 41.8781), 'mi') < 50\n\"\"\"\n\nsql_query = SQLQuery(sql_str)\nredis_query = sql_query.redis_query_string(redis_url=\"redis://localhost:6379\")\nprint(\"Resulting redis query: \", redis_query)\nresults = index.query(sql_query)\nresults","section_id":"geographic-queries"},{"id":"geographic-queries-ex2","language":"python","code":"# Combine GEO filter with TAG filter - find engineers near Chicago\nsql_str = \"\"\"\n    SELECT user, job, region\n    FROM user_simple\n    WHERE job = 'engineer' AND geo_distance(office_location, POINT(-87.6298, 41.8781), 'mi') < 50\n\"\"\"\n\nsql_query = SQLQuery(sql_str)\nredis_query = sql_query.redis_query_string(redis_url=\"redis://localhost:6379\")\nprint(\"Resulting redis query: \", redis_query)\nresults = index.query(sql_query)\nresults","section_id":"geographic-queries"},{"id":"geographic-queries-ex3","language":"python","code":"# Combine GEO with NUMERIC filter - find users over 30 near San Francisco\nsql_str = \"\"\"\n    SELECT user, job, age\n    FROM user_simple\n    WHERE age > 30 AND geo_distance(office_location, POINT(-122.4194, 37.7749), 'km') < 100\n\"\"\"\n\nsql_query = SQLQuery(sql_str)\nredis_query = sql_query.redis_query_string(redis_url=\"redis://localhost:6379\")\nprint(\"Resulting redis query: \", redis_query)\nresults = index.query(sql_query)\nresults","section_id":"geographic-queries"},{"id":"geographic-queries-ex4","language":"python","code":"# Combine GEO with TEXT search - find users with \"technical\" in job description near Chicago\nsql_str = \"\"\"\n    SELECT user, job, job_description\n    FROM user_simple\n    WHERE job_description LIKE 'technical%' AND geo_distance(office_location, POINT(-87.6298, 41.8781), 'km') < 100\n\"\"\"\n\nsql_query = SQLQuery(sql_str)\nredis_query = sql_query.redis_query_string(redis_url=\"redis://localhost:6379\")\nprint(\"Resulting redis query: \", redis_query)\nresults = index.query(sql_query)\nresults","section_id":"geographic-queries"},{"id":"geographic-queries-ex5","language":"python","code":"# Calculate distances from New York to all users\n# Note: geo_distance() in SELECT uses FT.AGGREGATE and returns distance in meters\nsql_str = \"\"\"\n    SELECT user, region, geo_distance(office_location, POINT(-73.9857, 40.7580)) AS distance_meters\n    FROM user_simple\n\"\"\"\n\nsql_query = SQLQuery(sql_str)\nredis_query = sql_query.redis_query_string(redis_url=\"redis://localhost:6379\")\nprint(\"Resulting redis query: \", redis_query)\nresults = index.query(sql_query)\n\n# Convert meters to km for readability and sort by distance\nprint(\"\\nDistances from NYC:\")\nfor r in sorted(results, key=lambda x: float(x.get('distance_meters', 0))):\n    dist_km = float(r.get('distance_meters', 0)) / 1000\n    print(f\"  {r['user']:10} | {r['region']:12} | {dist_km:,.0f} km\")","section_id":"geographic-queries"},{"id":"date-and-datetime-queries-ex0","language":"python","code":"# Create a separate index for date examples\nfrom datetime import datetime, timezone\n\ndef to_timestamp(date_str):\n    \"\"\"Convert ISO date string to Unix timestamp (UTC).\"\"\"\n    dt = datetime.strptime(date_str, \"%Y-%m-%d\")\n    dt = dt.replace(tzinfo=timezone.utc)\n    return int(dt.timestamp())\n\n# Define schema with NUMERIC fields for timestamps\nevents_schema = {\n    \"index\": {\n        \"name\": \"events\",\n        \"prefix\": \"event:\",\n        \"storage_type\": \"hash\",\n    },\n    \"fields\": [\n        {\"name\": \"name\", \"type\": \"text\", \"attrs\": {\"sortable\": True}},\n        {\"name\": \"category\", \"type\": \"tag\", \"attrs\": {\"sortable\": True}},\n        {\"name\": \"created_at\", \"type\": \"numeric\", \"attrs\": {\"sortable\": True}},\n    ],\n}\n\nevents_index = SearchIndex.from_dict(events_schema, redis_url=\"redis://localhost:6379\")\nevents_index.create(overwrite=True)\n\n# Sample events spanning 2023-2024\nevents = [\n    {\"name\": \"New Year Kickoff\", \"category\": \"meeting\", \"created_at\": to_timestamp(\"2024-01-01\")},\n    {\"name\": \"Q1 Planning\", \"category\": \"meeting\", \"created_at\": to_timestamp(\"2024-01-15\")},\n    {\"name\": \"Product Launch\", \"category\": \"release\", \"created_at\": to_timestamp(\"2024-02-20\")},\n    {\"name\": \"Team Offsite\", \"category\": \"meeting\", \"created_at\": to_timestamp(\"2024-03-10\")},\n    {\"name\": \"Summer Summit\", \"category\": \"conference\", \"created_at\": to_timestamp(\"2024-07-15\")},\n    {\"name\": \"Holiday Party 2023\", \"category\": \"conference\", \"created_at\": to_timestamp(\"2023-12-15\")},\n    {\"name\": \"Year End Review 2023\", \"category\": \"meeting\", \"created_at\": to_timestamp(\"2023-12-20\")},\n]\n\nevents_index.load(events)\n\nprint(f\"Loaded {len(events)} events:\")\nfor e in events:\n    date = datetime.fromtimestamp(e[\"created_at\"], tz=timezone.utc).strftime(\"%Y-%m-%d\")\n    print(f\"  - {e['name']:25} | {date} | {e['category']}\")","section_id":"date-and-datetime-queries"},{"id":"date-and-datetime-queries-ex1","language":"python","code":"# Find events after January 1st, 2024 using date literal\nsql_str = \"\"\"\n    SELECT name, category\n    FROM events\n    WHERE created_at > '2024-01-01'\n\"\"\"\n\nsql_query = SQLQuery(sql_str)\nredis_query = sql_query.redis_query_string(redis_url=\"redis://localhost:6379\")\nprint(\"Resulting redis query: \", redis_query)\nresults = events_index.query(sql_query)\n\nprint(f\"\\nEvents after 2024-01-01 ({len(results)} found):\")\nfor r in results:\n    print(f\"  - {r['name']}\")","section_id":"date-and-datetime-queries"},{"id":"date-and-datetime-queries-ex2","language":"python","code":"# Find events in Q1 2024 using BETWEEN\nsql_str = \"\"\"\n    SELECT name, category\n    FROM events\n    WHERE created_at BETWEEN '2024-01-01' AND '2024-03-31'\n\"\"\"\n\nsql_query = SQLQuery(sql_str)\nredis_query = sql_query.redis_query_string(redis_url=\"redis://localhost:6379\")\nprint(\"Resulting redis query: \", redis_query)\nresults = events_index.query(sql_query)\n\nprint(f\"\\nEvents in Q1 2024 ({len(results)} found):\")\nfor r in results:\n    print(f\"  - {r['name']} ({r['category']})\")","section_id":"date-and-datetime-queries"},{"id":"date-and-datetime-queries-ex3","language":"python","code":"# Combine date filter with TAG filter - find meetings in H1 2024\nsql_str = \"\"\"\n    SELECT name\n    FROM events\n    WHERE category = 'meeting' AND created_at BETWEEN '2024-01-01' AND '2024-06-30'\n\"\"\"\n\nsql_query = SQLQuery(sql_str)\nresults = events_index.query(sql_query)\n\nprint(f\"Meetings in H1 2024 ({len(results)} found):\")\nfor r in results:\n    print(f\"  - {r['name']}\")","section_id":"date-and-datetime-queries"},{"id":"async-support-ex0","language":"python","code":"from redisvl.index import AsyncSearchIndex\nfrom redisvl.query import SQLQuery\n\n# Create async index\nasync_index = AsyncSearchIndex.from_dict(schema, redis_url=\"redis://localhost:6379\")\n\n# Execute SQL query asynchronously\nsql_query = SQLQuery(f\"SELECT user, age FROM {async_index.name} WHERE age > 30\")\nresults = await async_index.query(sql_query)\n\n# Cleanup\nawait async_index.disconnect()","section_id":"async-support"},{"id":"native-geo-filters-ex0","language":"python","code":"from redisvl.query import FilterQuery\nfrom redisvl.query.filter import Geo, GeoRadius, Tag, Num\n\n# Find users within 100km of Chicago using native filters\ngeo_filter = Geo(\"office_location\") == GeoRadius(-87.6298, 41.8781, 100, \"km\")\n\nprint(f\"Filter expression: {geo_filter}\\n\")\n\nquery = FilterQuery(\n    filter_expression=geo_filter,\n    return_fields=[\"user\", \"job\", \"region\"]\n)\n\nresults = index.query(query)\nprint(f\"Users within 100km of Chicago ({len(results)} found):\")\nfor r in results:\n    print(f\"  - {r['user']} ({r['job']}) - {r['region']}\")","section_id":"native-geo-filters"},{"id":"native-geo-filters-ex1","language":"python","code":"# Find users OUTSIDE 1000km of Chicago (using !=)\ngeo_filter_outside = Geo(\"office_location\") != GeoRadius(-87.6298, 41.8781, 1000, \"km\")\n\nprint(f\"Filter expression: {geo_filter_outside}\\n\")\n\nquery = FilterQuery(\n    filter_expression=geo_filter_outside,\n    return_fields=[\"user\", \"region\"]\n)\n\nresults = index.query(query)\nprint(f\"Users OUTSIDE 1000km of Chicago ({len(results)} found):\")\nfor r in results:\n    print(f\"  - {r['user']} ({r['region']})\")","section_id":"native-geo-filters"},{"id":"native-geo-filters-ex2","language":"python","code":"# Combine GEO + TAG + NUMERIC filters\n# Find engineers over 40 within 500km of Chicago\ngeo_filter = Geo(\"office_location\") == GeoRadius(-87.6298, 41.8781, 500, \"km\")\njob_filter = Tag(\"job\") == \"engineer\"\nage_filter = Num(\"age\") > 40\n\ncombined_filter = geo_filter & job_filter & age_filter\n\nprint(f\"Combined filter: {combined_filter}\\n\")\n\nquery = FilterQuery(\n    filter_expression=combined_filter,\n    return_fields=[\"user\", \"job\", \"age\", \"region\"]\n)\n\nresults = index.query(query)\nprint(f\"Engineers over 40 within 500km of Chicago ({len(results)} found):\")\nfor r in results:\n    print(f\"  - {r['user']} (age: {r['age']}) - {r['region']}\")","section_id":"native-geo-filters"},{"id":"additional-date-examples-ex0","language":"python","code":"# Extract YEAR and MONTH using date functions in SELECT\nsql_str = \"\"\"\n    SELECT name, YEAR(created_at) AS year, MONTH(created_at) AS month\n    FROM events\n\"\"\"\n\nsql_query = SQLQuery(sql_str)\nredis_query = sql_query.redis_query_string(redis_url=\"redis://localhost:6379\")\nprint(\"Resulting redis query: \", redis_query)\nresults = events_index.query(sql_query)\n\nprint(f\"\\nEvents with year/month:\")\nfor r in results:\n    # Note: MONTH returns 0-11 in Redis (0=January)\n    month_num = int(r.get('month', 0)) + 1\n    print(f\"  - {r['name']:25} | {r.get('year')}-{month_num:02d}\")","section_id":"additional-date-examples"},{"id":"additional-date-examples-ex1","language":"python","code":"# Filter by YEAR using date function in WHERE\nsql_str = \"\"\"\n    SELECT name\n    FROM events\n    WHERE YEAR(created_at) = 2024\n\"\"\"\n\nsql_query = SQLQuery(sql_str)\nredis_query = sql_query.redis_query_string(redis_url=\"redis://localhost:6379\")\nprint(\"Resulting redis query: \", redis_query)\nresults = events_index.query(sql_query)\n\nprint(f\"\\nEvents in 2024 ({len(results)} found):\")\nfor r in results:\n    print(f\"  - {r['name']}\")","section_id":"additional-date-examples"},{"id":"additional-date-examples-ex2","language":"python","code":"# Count events per year using GROUP BY\nsql_str = \"\"\"\n    SELECT YEAR(created_at) AS year, COUNT(*) AS event_count\n    FROM events\n    GROUP BY year\n\"\"\"\n\nsql_query = SQLQuery(sql_str)\nredis_query = sql_query.redis_query_string(redis_url=\"redis://localhost:6379\")\nprint(\"Resulting redis query: \", redis_query)\nresults = events_index.query(sql_query)\n\nprint(\"\\nEvents per year:\")\nfor r in sorted(results, key=lambda x: x.get('year', 0)):\n    print(f\"  {r['year']}: {r['event_count']} events\")","section_id":"additional-date-examples"},{"id":"additional-date-examples-ex3","language":"python","code":"# Format dates using DATE_FORMAT\nsql_str = \"\"\"\n    SELECT name, DATE_FORMAT(created_at, '%Y-%m-%d') AS event_date\n    FROM events\n\"\"\"\n\nsql_query = SQLQuery(sql_str)\nredis_query = sql_query.redis_query_string(redis_url=\"redis://localhost:6379\")\nprint(\"Resulting redis query: \", redis_query)\nresults = events_index.query(sql_query)\n\nprint(\"\\nEvents with formatted dates:\")\nfor r in results:\n    print(f\"  - {r['name']:25} | {r.get('event_date', 'N/A')}\")","section_id":"additional-date-examples"},{"id":"cleanup-ex0","language":"python","code":"# Delete both indexes and all associated data\nevents_index.delete(drop=True)\nindex.delete(drop=True)","section_id":"cleanup"}]}
{"schema_version":2,"id":"develop/ai/redisvl/user_guide/how_to_guides/svs_vamana","title":"Optimize Indexes with SVS-VAMANA","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/how_to_guides/svs_vamana/","summary":"","aliases":["/integrate/redisvl/user_guide/how_to_guides/09_svs_vamana","/develop/ai/redisvl/user_guide/svs_vamana/"],"tags":[],"last_updated":"2026-10-01T14:49:09Z","page_type":"content","content_hash":"6677123b20451025dc0e2e5394e7c4709aa05473a88150910b9bab752cda55da","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This guide covers SVS-VAMANA (Scalable Vector Search with VAMANA graph algorithm), a graph-based vector search algorithm optimized for compression methods to reduce memory usage. It combines the Vamana graph algorithm with advanced compression techniques (LVQ and LeanVec) and is optimized for Intel hardware."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before you begin, ensure you have:\n- Installed RedisVL: `pip install redisvl`\n- A running Redis instance with Redis >= 8.2.0 and Redis Search >= 2.8.10 ([Redis 8+](https://redis.io/downloads/) or [Redis Cloud](https://redis.io/cloud))\n\n**Note:** SVS-VAMANA only supports FLOAT16 and FLOAT32 datatypes."},{"id":"what-youll-learn","title":"What You'll Learn","role":"content","text":"By the end of this guide, you will be able to:\n- Understand when to use SVS-VAMANA for vector search\n- Configure compression settings for memory optimization\n- Use the CompressionAdvisor for automatic optimization\n- Trade off between memory usage, speed, and search quality\n\n**SVS-VAMANA offers:**\n- **Fast approximate nearest neighbor search** using graph-based algorithms\n- **Vector compression** (LVQ, LeanVec) with up to 87.5% memory savings\n- **Dimensionality reduction** (optional, with LeanVec)\n- **Automatic performance optimization** through CompressionAdvisor\n\n**Use SVS-VAMANA when:**\n- Large datasets where memory is expensive\n- Cloud deployments with memory-based pricing\n- When 90-95% recall is acceptable\n- High-dimensional vectors (>1024 dims) with LeanVec compression\n\n\n[code example]\n\n\n[code example]"},{"id":"quick-start-with-compressionadvisor","title":"Quick Start with CompressionAdvisor","role":"content","text":"The easiest way to get started with SVS-VAMANA is using the `CompressionAdvisor` utility, which automatically recommends optimal configuration based on your vector dimensions and performance priorities.\n\n\n[code example]\n\n    Recommended Configuration:\n      algorithm: svs-vamana\n      datatype: float16\n      compression: LeanVec4x8\n      reduce: 512\n      graph_max_degree: 64\n      construction_window_size: 300\n      search_window_size: 30\n    \n    Estimated Memory Savings: 81.2%"},{"id":"creating-an-svs-vamana-index","title":"Creating an SVS-VAMANA Index","role":"content","text":"Let's create an index using the recommended configuration. We'll use a simple schema with text content and vector embeddings.\n\n\n[code example]\n\n    ✅ Created SVS-VAMANA index: svs_demo\n       Algorithm: svs-vamana\n       Compression: LeanVec4x8\n       Dimensions: 1024\n       Reduced to: 512 dimensions"},{"id":"loading-sample-data","title":"Loading Sample Data","role":"content","text":"Let's create some sample documents with embeddings to demonstrate SVS-VAMANA search capabilities.\n\n\n[code example]\n\n    Creating vectors with 512 dimensions (reduced from 1024 if applicable)\n    ✅ Loaded 10 documents into the index\n       Index now contains 0 documents"},{"id":"performing-vector-searches","title":"Performing Vector Searches","role":"content","text":"Now let's perform some vector similarity searches using our SVS-VAMANA index.\n\n\n[code example]\n\n    🔍 Vector Search Results:\n    =================================================="},{"id":"runtime-parameters-for-performance-tuning","title":"Runtime Parameters for Performance Tuning","role":"content","text":"SVS-VAMANA supports runtime parameters that can be adjusted at query time without rebuilding the index. These parameters allow you to fine-tune the trade-off between search speed and accuracy.\n\n**Available Runtime Parameters:**\n\n- **`search_window_size`**: Controls the size of the search window during KNN search (higher = better recall, slower search)\n- **`epsilon`**: Approximation factor for range queries (default: 0.01)\n- **`use_search_history`**: Whether to use search buffer (OFF/ON/AUTO, default: AUTO)\n- **`search_buffer_capacity`**: Tuning parameter for 2-level compression (default: search_window_size)\n\nLet's see how these parameters affect search performance:\n\n\n[code example]\n\n    🔍 Basic Query (default parameters):\n    Found 0 results\n    \n    🎯 Tuned Query (higher recall parameters):\n    Found 0 results\n    \n    Note: Higher search_window_size improves recall but may increase latency"},{"id":"range-queries-with-svs-vamana","title":"Range Queries with SVS-VAMANA","role":"content","text":"Range queries find all vectors within a certain distance threshold. For range queries, you can use the `epsilon` parameter to control the approximation factor:\n\n\n[code example]\n\n    🎯 Range Query Results: Found 0 vectors within distance threshold 0.3"},{"id":"understanding-compression-types","title":"Understanding Compression Types","role":"content","text":"SVS-VAMANA supports different compression algorithms that trade off between memory usage and search quality. Let's explore the available options.\n\n\n[code example]\n\n    Compression Recommendations for Different Priorities:\n    ============================================================\n    \n    MEMORY Priority:\n      Compression: LeanVec4x8\n      Datatype: float16\n      Dimensionality reduction: 1024 → 512\n      Search window size: 20\n      Memory savings: 81.2%\n    \n    SPEED Priority:\n      Compression: LeanVec4x8\n      Datatype: float16\n      Dimensionality reduction: 1024 → 256\n      Search window size: 40\n      Memory savings: 90.6%\n    \n    BALANCED Priority:\n      Compression: LeanVec4x8\n      Datatype: float16\n      Dimensionality reduction: 1024 → 512\n      Search window size: 30\n      Memory savings: 81.2%"},{"id":"compression-types-explained","title":"Compression Types Explained","role":"content","text":"SVS-VAMANA offers several compression algorithms:"},{"id":"lvq-learned-vector-quantization","title":"LVQ (Learned Vector Quantization)","role":"content","text":"- **LVQ4**: 4 bits per dimension (87.5% memory savings)\n- **LVQ4x4**: 8 bits per dimension (75% memory savings)\n- **LVQ4x8**: 12 bits per dimension (62.5% memory savings)\n- **LVQ8**: 8 bits per dimension (75% memory savings)"},{"id":"leanvec-compression--dimensionality-reduction","title":"LeanVec (Compression + Dimensionality Reduction)","role":"content","text":"- **LeanVec4x8**: 12 bits per dimension + dimensionality reduction\n- **LeanVec8x8**: 16 bits per dimension + dimensionality reduction\n\nThe CompressionAdvisor automatically chooses the best compression type based on your vector dimensions and priority.\n\n\n[code example]\n\n    Memory Savings by Vector Dimension:\n    ==================================================\n    Dims   Compression  Savings  Strategy\n    --------------------------------------------------\n    384    LVQ4x4         75.0% LVQ\n    768    LVQ4x4         75.0% LVQ\n    1024   LeanVec4x8     81.2% LeanVec\n    1536   LeanVec4x8     81.2% LeanVec\n    3072   LeanVec4x8     81.2% LeanVec"},{"id":"hybrid-queries-with-svs-vamana","title":"Hybrid Queries with SVS-VAMANA","role":"content","text":"SVS-VAMANA can be combined with other Redis search capabilities for powerful hybrid queries that filter by metadata while performing vector similarity search.\n\n\n[code example]\n\n    🔍 Hybrid Search Results (Technology category only):\n    ======================================================="},{"id":"performance-monitoring","title":"Performance Monitoring","role":"performance","text":"Let's examine the index statistics to understand the performance characteristics of our SVS-VAMANA index.\n\n\n[code example]\n\n    📊 Index Statistics:\n    ==============================\n    Documents: 0\n    Vector index size: 0.00 MB\n    Total indexing time: 0.27 seconds\n    Memory efficiency calculation requires documents and vector index size > 0"},{"id":"advanced-manual-configuration","title":"Advanced Manual Configuration","role":"content","text":"For advanced users who want full control over SVS-VAMANA parameters, you can manually configure the algorithm instead of using CompressionAdvisor.\n\n\n[code example]\n\n    Manual SVS-VAMANA Configuration:\n    ========================================\n      algorithm: svs-vamana\n      datatype: float32\n      distance_metric: cosine\n      graph_max_degree: 64\n      construction_window_size: 300\n      search_window_size: 40\n      compression: LVQ4x4\n      training_threshold: 10000\n    \n    Estimated memory savings: 75.0%"},{"id":"best-practices-and-tips","title":"Best Practices and Tips","role":"content","text":""},{"id":"when-to-use-svs-vamana","title":"When to Use SVS-VAMANA","role":"content","text":"- **Large datasets** (>10K vectors) where memory efficiency matters\n- **High-dimensional vectors** (>512 dimensions) that benefit from compression\n- **Applications** that can tolerate slight recall trade-offs for speed and memory savings"},{"id":"parameter-tuning-guidelines","title":"Parameter Tuning Guidelines","role":"parameters","text":"**Index-time parameters** (set during index creation):\n- **Start with CompressionAdvisor** recommendations for compression and datatype\n- **Use LeanVec** for high-dimensional vectors (≥1024 dims)\n- **Use LVQ** for lower-dimensional vectors (<1024 dims)\n- **graph_max_degree**: Higher values improve recall but increase memory usage\n- **construction_window_size**: Higher values improve index quality but slow down build time\n\n**Runtime parameters** (adjustable at query time without rebuilding index):\n- **search_window_size**: Start with 20, increase to 40-100 for higher recall\n- **epsilon**: Use 0.01-0.05 for range queries (higher = faster but less accurate)\n- **use_search_history**: Use 'AUTO' (default) or 'ON' for better recall\n- **search_buffer_capacity**: Usually set equal to search_window_size"},{"id":"performance-considerations","title":"Performance Considerations","role":"performance","text":"- **Index build time** increases with higher construction_window_size\n- **Search latency** increases with higher search_window_size (tunable at query time!)\n- **Memory usage** decreases with more aggressive compression\n- **Recall quality** may decrease with more aggressive compression or lower search_window_size"},{"id":"next-steps","title":"Next Steps","role":"content","text":"Now that you understand SVS-VAMANA optimization, explore these related guides:\n\n- [Getting Started](https://redis.io/docs/latest/../getting_started) - Learn the basics of RedisVL indexes\n- [Choose a Storage Type](https://redis.io/docs/latest/hash_vs_json) - Understand Hash vs JSON storage\n- [Query and Filter Data](https://redis.io/docs/latest/complex_filtering) - Apply filters to narrow down search results"},{"id":"cleanup","title":"Cleanup","role":"content","text":"Clean up the indices created in this demo.\n\n\n[code example]"}],"examples":[{"id":"what-youll-learn-ex0","language":"python","code":"# Import necessary modules\nimport numpy as np\nfrom redisvl.index import SearchIndex\nfrom redisvl.query import VectorQuery\nfrom redisvl.utils import CompressionAdvisor\nfrom redisvl.redis.utils import array_to_buffer\n\n# Set random seed for reproducible results\nnp.random.seed(42)","section_id":"what-youll-learn"},{"id":"what-youll-learn-ex1","language":"python","code":"# Redis connection\nREDIS_URL = \"redis://localhost:6379\"","section_id":"what-youll-learn"},{"id":"quick-start-with-compressionadvisor-ex0","language":"python","code":"# Get recommended configuration for common embedding dimensions\ndims = 1024  # Common embedding dimensions (works reliably with SVS-VAMANA)\n\nconfig = CompressionAdvisor.recommend(\n    dims=dims,\n    priority=\"balanced\"  # Options: \"memory\", \"speed\", \"balanced\"\n)\n\nprint(\"Recommended Configuration:\")\nfor key, value in config.model_dump().items():\n    print(f\"  {key}: {value}\")\n\n# Estimate memory savings\nsavings = CompressionAdvisor.estimate_memory_savings(\n    config.compression,\n    dims,\n    config.reduce\n)\nprint(f\"\\nEstimated Memory Savings: {savings}%\")","section_id":"quick-start-with-compressionadvisor"},{"id":"creating-an-svs-vamana-index-ex0","language":"python","code":"# Create index schema with recommended SVS-VAMANA configuration\nconfig_dict = config.model_dump(exclude_none=True)\nschema = {\n    \"index\": {\n        \"name\": \"svs_demo\",\n        \"prefix\": \"doc\",\n    },\n    \"fields\": [\n        {\"name\": \"content\", \"type\": \"text\"},\n        {\"name\": \"category\", \"type\": \"tag\"},\n        {\n            \"name\": \"embedding\",\n            \"type\": \"vector\",\n            \"attrs\": {\n                \"dims\": dims,\n                **config_dict,  # Use the recommended configuration\n                \"distance_metric\": \"cosine\"\n            }\n        }\n    ]\n}\n\n# Create the index\nindex = SearchIndex.from_dict(schema, redis_url=REDIS_URL)\nindex.create(overwrite=True)\n\nprint(f\"✅ Created SVS-VAMANA index: {index.name}\")\nprint(f\"   Algorithm: {config.algorithm}\")\nprint(f\"   Compression: {config.compression}\")\nprint(f\"   Dimensions: {dims}\")\nif config.reduce is not None:\n    print(f\"   Reduced to: {config.reduce} dimensions\")","section_id":"creating-an-svs-vamana-index"},{"id":"loading-sample-data-ex0","language":"python","code":"# Generate sample data\nsample_documents = [\n    {\"content\": \"Machine learning algorithms for data analysis\", \"category\": \"technology\"},\n    {\"content\": \"Natural language processing and text understanding\", \"category\": \"technology\"},\n    {\"content\": \"Computer vision and image recognition systems\", \"category\": \"technology\"},\n    {\"content\": \"Delicious pasta recipes from Italy\", \"category\": \"food\"},\n    {\"content\": \"Traditional French cooking techniques\", \"category\": \"food\"},\n    {\"content\": \"Healthy meal planning and nutrition\", \"category\": \"food\"},\n    {\"content\": \"Travel guide to European destinations\", \"category\": \"travel\"},\n    {\"content\": \"Adventure hiking in mountain regions\", \"category\": \"travel\"},\n    {\"content\": \"Cultural experiences in Asian cities\", \"category\": \"travel\"},\n    {\"content\": \"Financial planning for retirement\", \"category\": \"finance\"},\n]\n\n# Generate random embeddings for demonstration\n# In practice, you would use a real embedding model\ndata_to_load = []\n\n# Use reduced dimensions if LeanVec compression is applied\nvector_dims = config.reduce if config.reduce is not None else dims\nprint(f\"Creating vectors with {vector_dims} dimensions (reduced from {dims} if applicable)\")\n\nfor i, doc in enumerate(sample_documents):\n    # Create a random vector with some category-based clustering\n    base_vector = np.random.random(vector_dims).astype(np.float32)\n    \n    # Add some category-based similarity (optional, for demo purposes)\n    category_offset = hash(doc[\"category\"]) % 100 / 1000.0\n    base_vector[0] += category_offset\n    \n    # Convert to the datatype specified in config\n    if config.datatype == \"float16\":\n        base_vector = base_vector.astype(np.float16)\n    \n    data_to_load.append({\n        \"content\": doc[\"content\"],\n        \"category\": doc[\"category\"],\n        \"embedding\": array_to_buffer(base_vector, dtype=config.datatype)\n    })\n\n# Load data into the index\nindex.load(data_to_load)\nprint(f\"✅ Loaded {len(data_to_load)} documents into the index\")\n\n# Wait a moment for indexing to complete\nimport time\ntime.sleep(2)\n\n# Verify the data was loaded\ninfo = index.info()\nprint(f\"   Index now contains {info.get('num_docs', 0)} documents\")","section_id":"loading-sample-data"},{"id":"performing-vector-searches-ex0","language":"python","code":"# Create a query vector (in practice, this would be an embedding of your query text)\n# Important: Query vector must match the index datatype and dimensions\nvector_dims = config.reduce if config.reduce is not None else dims\nif config.datatype == \"float16\":\n    query_vector = np.random.random(vector_dims).astype(np.float16)\nelse:\n    query_vector = np.random.random(vector_dims).astype(np.float32)\n\n# Perform a vector similarity search\nquery = VectorQuery(\n    vector=query_vector.tolist(),\n    vector_field_name=\"embedding\",\n    return_fields=[\"content\", \"category\"],\n    num_results=5\n)\n\nresults = index.query(query)\n\nprint(\"🔍 Vector Search Results:\")\nprint(\"=\" * 50)\nfor i, result in enumerate(results, 1):\n    distance = result.get('vector_distance', 'N/A')\n    print(f\"{i}. [{result['category']}] {result['content']}\")\n    print(f\"   Distance: {distance:.4f}\" if isinstance(distance, (int, float)) else f\"   Distance: {distance}\")\n    print()","section_id":"performing-vector-searches"},{"id":"runtime-parameters-for-performance-tuning-ex0","language":"python","code":"# Example 1: Basic query with default parameters\nbasic_query = VectorQuery(\n    vector=query_vector.tolist(),\n    vector_field_name=\"embedding\",\n    return_fields=[\"content\", \"category\"],\n    num_results=5\n)\n\nprint(\"🔍 Basic Query (default parameters):\")\nresults = index.query(basic_query)\nprint(f\"Found {len(results)} results\\n\")\n\n# Example 2: Query with tuned runtime parameters for higher recall\ntuned_query = VectorQuery(\n    vector=query_vector.tolist(),\n    vector_field_name=\"embedding\",\n    return_fields=[\"content\", \"category\"],\n    num_results=5,\n    search_window_size=40,      # Larger window for better recall\n    use_search_history='ON',    # Use search history\n    search_buffer_capacity=50   # Larger buffer capacity\n)\n\nprint(\"🎯 Tuned Query (higher recall parameters):\")\nresults = index.query(tuned_query)\nprint(f\"Found {len(results)} results\")\nprint(\"\\nNote: Higher search_window_size improves recall but may increase latency\")","section_id":"runtime-parameters-for-performance-tuning"},{"id":"range-queries-with-svs-vamana-ex0","language":"python","code":"from redisvl.query import VectorRangeQuery\n\n# Range query with epsilon parameter for approximation control\n# Note: search_window_size and use_search_history are only supported for KNN queries (VectorQuery),\n# not for range queries (VectorRangeQuery). Use epsilon to control the approximation factor.\nrange_query = VectorRangeQuery(\n    vector=query_vector.tolist(),\n    vector_field_name=\"embedding\",\n    return_fields=[\"content\", \"category\"],\n    distance_threshold=0.3,\n    epsilon=0.05,               # Approximation factor for range queries\n)\n\nresults = index.query(range_query)\nprint(f\"🎯 Range Query Results: Found {len(results)} vectors within distance threshold 0.3\")\nfor i, result in enumerate(results[:3], 1):\n    distance = result.get('vector_distance', 'N/A')\n    print(f\"{i}. {result['content'][:50]}... (distance: {distance})\")","section_id":"range-queries-with-svs-vamana"},{"id":"understanding-compression-types-ex0","language":"python","code":"# Compare different compression priorities\nprint(\"Compression Recommendations for Different Priorities:\")\nprint(\"=\" * 60)\n\npriorities = [\"memory\", \"speed\", \"balanced\"]\nfor priority in priorities:\n    config = CompressionAdvisor.recommend(dims=dims, priority=priority)\n    savings = CompressionAdvisor.estimate_memory_savings(\n        config.compression,\n        dims,\n        config.reduce\n    )\n    \n    print(f\"\\n{priority.upper()} Priority:\")\n    print(f\"  Compression: {config.compression}\")\n    print(f\"  Datatype: {config.datatype}\")\n    if config.reduce is not None:\n        print(f\"  Dimensionality reduction: {dims} → {config.reduce}\")\n    print(f\"  Search window size: {config.search_window_size}\")\n    print(f\"  Memory savings: {savings}%\")","section_id":"understanding-compression-types"},{"id":"leanvec-compression--dimensionality-reduction-ex0","language":"python","code":"# Demonstrate compression savings for different vector dimensions\ntest_dimensions = [384, 768, 1024, 1536, 3072]\n\nprint(\"Memory Savings by Vector Dimension:\")\nprint(\"=\" * 50)\nprint(f\"{'Dims':<6} {'Compression':<12} {'Savings':<8} {'Strategy'}\")\nprint(\"-\" * 50)\n\nfor dims in test_dimensions:\n    config = CompressionAdvisor.recommend(dims=dims, priority=\"balanced\")\n    savings = CompressionAdvisor.estimate_memory_savings(\n        config.compression,\n        dims,\n        config.reduce\n    )\n    \n    strategy = \"LeanVec\" if dims >= 1024 else \"LVQ\"\n    print(f\"{dims:<6} {config.compression:<12} {savings:>6.1f}% {strategy}\")","section_id":"leanvec-compression--dimensionality-reduction"},{"id":"hybrid-queries-with-svs-vamana-ex0","language":"python","code":"# Perform a hybrid search: vector similarity + category filter\nhybrid_query = VectorQuery(\n    vector=query_vector.tolist(),\n    vector_field_name=\"embedding\",\n    return_fields=[\"content\", \"category\"],\n    num_results=3\n)\n\n# Add a filter to only search within \"technology\" category\nhybrid_query.set_filter(\"@category:{technology}\")\n\nfiltered_results = index.query(hybrid_query)\n\nprint(\"🔍 Hybrid Search Results (Technology category only):\")\nprint(\"=\" * 55)\nfor i, result in enumerate(filtered_results, 1):\n    distance = result.get('vector_distance', 'N/A')\n    print(f\"{i}. [{result['category']}] {result['content']}\")\n    print(f\"   Distance: {distance:.4f}\" if isinstance(distance, (int, float)) else f\"   Distance: {distance}\")\n    print()","section_id":"hybrid-queries-with-svs-vamana"},{"id":"performance-monitoring-ex0","language":"python","code":"# Get detailed index information\ninfo = index.info()\n\nprint(\"📊 Index Statistics:\")\nprint(\"=\" * 30)\nprint(f\"Documents: {info.get('num_docs', 0)}\")\n\n# Handle vector_index_sz_mb which might be a string\nvector_size = info.get('vector_index_sz_mb', 0)\nif isinstance(vector_size, str):\n    try:\n        vector_size = float(vector_size)\n    except ValueError:\n        vector_size = 0.0\nprint(f\"Vector index size: {vector_size:.2f} MB\")\n\n# Handle total_indexing_time which might also be a string\nindexing_time = info.get('total_indexing_time', 0)\nif isinstance(indexing_time, str):\n    try:\n        indexing_time = float(indexing_time)\n    except ValueError:\n        indexing_time = 0.0\nprint(f\"Total indexing time: {indexing_time:.2f} seconds\")\n\n# Calculate memory efficiency\nif info.get('num_docs', 0) > 0 and vector_size > 0:\n    mb_per_doc = vector_size / info.get('num_docs', 1)\n    print(f\"Memory per document: {mb_per_doc:.4f} MB\")\n    \n    # Estimate for larger datasets\n    for scale in [1000, 10000, 100000]:\n        estimated_mb = mb_per_doc * scale\n        print(f\"Estimated size for {scale:,} docs: {estimated_mb:.1f} MB\")\nelse:\n    print(\"Memory efficiency calculation requires documents and vector index size > 0\")","section_id":"performance-monitoring"},{"id":"advanced-manual-configuration-ex0","language":"python","code":"# Example of manual SVS-VAMANA configuration\nmanual_schema = {\n    \"index\": {\n        \"name\": \"svs_manual\",\n        \"prefix\": \"manual\",\n    },\n    \"fields\": [\n        {\"name\": \"content\", \"type\": \"text\"},\n        {\n            \"name\": \"embedding\",\n            \"type\": \"vector\",\n            \"attrs\": {\n                \"dims\": 768,\n                \"algorithm\": \"svs-vamana\",\n                \"datatype\": \"float32\",\n                \"distance_metric\": \"cosine\",\n                \n                # Graph construction parameters\n                \"graph_max_degree\": 64,           # Higher = better recall, more memory\n                \"construction_window_size\": 300,  # Higher = better quality, slower build\n                \n                # Search parameters\n                \"search_window_size\": 40,         # Higher = better recall, slower search\n                \n                # Compression settings\n                \"compression\": \"LVQ4x4\",          # Choose compression type\n                \"training_threshold\": 10000,      # Min vectors before compression training\n            }\n        }\n    ]\n}\n\nprint(\"Manual SVS-VAMANA Configuration:\")\nprint(\"=\" * 40)\nvector_attrs = manual_schema[\"fields\"][1][\"attrs\"]\nfor key, value in vector_attrs.items():\n    if key != \"dims\":  # Skip dims as it's obvious\n        print(f\"  {key}: {value}\")\n\n# Calculate memory savings for this configuration\nmanual_savings = CompressionAdvisor.estimate_memory_savings(\n    \"LVQ4x4\", 768, None\n)\nprint(f\"\\nEstimated memory savings: {manual_savings}%\")","section_id":"advanced-manual-configuration"},{"id":"cleanup-ex0","language":"python","code":"# Clean up demo indices\ntry:\n    index.delete()\n    print(\"Cleaned up svs_demo index\")\nexcept:\n    print(\"- svs_demo index was already deleted or doesn't exist\")","section_id":"cleanup"}]}
{"schema_version":2,"id":"develop/ai/redisvl/user_guide/how_to_guides/vectorizers","title":"Create Embeddings with Vectorizers","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/how_to_guides/vectorizers/","summary":"","aliases":["/integrate/redisvl/user_guide/how_to_guides/04_vectorizers","/integrate/redisvl/user_guide/vectorizers/","/develop/ai/redisvl/user_guide/vectorizers/"],"tags":[],"last_updated":"2026-10-01T14:49:09Z","page_type":"content","content_hash":"de30ad5fb41b3f418b899db2e92557d3e6d34867fd21fd1749b7adf36e250416","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This guide demonstrates how to create embeddings using RedisVL's built-in text vectorizers. RedisVL supports multiple embedding providers: OpenAI, HuggingFace, Ollama, Google (Vertex AI and the Gemini Developer API), Cohere, Mistral AI, Amazon Bedrock, VoyageAI, and custom vectorizers."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before you begin, ensure you have:\n- Installed RedisVL: `pip install redisvl`\n- A running Redis instance ([Redis 8+](https://redis.io/downloads/) or [Redis Cloud](https://redis.io/cloud))\n- API keys or local model servers for the embedding providers you plan to use"},{"id":"what-youll-learn","title":"What You'll Learn","role":"content","text":"By the end of this guide, you will be able to:\n- Create embeddings using multiple providers (OpenAI, HuggingFace, Ollama, Cohere, etc.)\n- Use synchronous and asynchronous embedding methods\n- Batch embed multiple texts efficiently\n- Build custom vectorizers for your own embedding functions\n- Integrate vectorizers with RedisVL indexes for semantic search\n\n\n[code example]"},{"id":"creating-text-embeddings","title":"Creating Text Embeddings","role":"content","text":"This example will show how to create an embedding from 3 simple sentences with a number of different text vectorizers in RedisVL.\n\n- \"That is a happy dog\"\n- \"That is a happy person\"\n- \"Today is a nice day\""},{"id":"openai","title":"OpenAI","role":"content","text":"The ``OpenAITextVectorizer`` makes it simple to use RedisVL with the embeddings models at OpenAI. For this you will need to install ``openai``. \n\n[code example]\n\n\n\n[code example]\n\n\n[code example]\n\n    Vector dimensions:  1536\n\n\n\n\n\n    [-0.0011391325388103724,\n     -0.003206387162208557,\n     0.002380132209509611,\n     -0.004501554183661938,\n     -0.010328996926546097,\n     0.012922565452754498,\n     -0.005491119809448719,\n     -0.0029864837415516376,\n     -0.007327961269766092,\n     -0.03365817293524742]\n\n\n\n\n[code example]\n\n\n\n\n    [-0.017466850578784943,\n     1.8471690054866485e-05,\n     0.00129731057677418,\n     -0.02555876597762108,\n     -0.019842341542243958,\n     0.01603139191865921,\n     -0.0037347301840782166,\n     0.0009670283179730177,\n     0.006618348415941,\n     -0.02497442066669464]\n\n\n\n\n[code example]\n\n    Number of Embeddings: 3"},{"id":"azure-openai","title":"Azure OpenAI","role":"content","text":"The ``AzureOpenAITextVectorizer`` is a variation of the OpenAI vectorizer that calls OpenAI models within Azure. If you've already installed ``openai``, then you're ready to use Azure OpenAI.\n\nThe only practical difference between OpenAI and Azure OpenAI is the variables required to call the API.\n\n\n[code example]\n\n\n[code example]\n\n\n[code example]"},{"id":"huggingface","title":"Huggingface","role":"content","text":"[Huggingface](https://huggingface.co/models) is a popular NLP platform that has a number of pre-trained models you can use off the shelf. RedisVL supports using Huggingface \"Sentence Transformers\" to create embeddings from text. To use Huggingface, you will need to install the ``sentence-transformers`` library.\n\n[code example]\n\n\n[code example]\n\n\n[code example]"},{"id":"ollama","title":"Ollama","role":"content","text":"[Ollama](https://ollama.com/) lets you run embedding models locally. RedisVL supports Ollama through the `OllamaTextVectorizer`.\n\nInstall the Python client and pull an embedding model before running this example:\n\n[code example]\n\nMake sure the Ollama daemon is running with `ollama serve`. By default, the Ollama client uses `OLLAMA_HOST` if set, otherwise it connects to `http://localhost:11434`. To connect to a custom Ollama server explicitly, pass `host=\"http://your-host:11434\"` when creating the vectorizer.\n\n\n[code example]\n\n\n[code example]\n\n\n[code example]\n\n**⚠️ Deprecated.** `VertexAIVectorizer` uses Google's Vertex AI model-garden SDK, which Google has deprecated with a scheduled removal. For text embeddings use the **Google Gen AI** vectorizer shown in the next section (`GoogleGenAIVectorizer`) on the supported `google-genai` SDK. Multimodal (image/video) migration is tracked in [issue #620](https://github.com/redis/redis-vl-python/issues/620)."},{"id":"vertexai","title":"VertexAI","role":"content","text":"[VertexAI](https://cloud.google.com/vertex-ai/docs/generative-ai/embeddings/get-text-embeddings) is GCP's fully-featured AI platform including a number of pretrained LLMs. RedisVL supports using VertexAI to create embeddings from these models. To use VertexAI, you will first need to install the ``google-cloud-aiplatform`` library.\n\n[code example]\n\n1. Then you need to gain access to a [Google Cloud Project](https://cloud.google.com/gcp?hl=en) and provide [access to credentials](https://cloud.google.com/docs/authentication/application-default-credentials). This is accomplished by setting the `GOOGLE_APPLICATION_CREDENTIALS` environment variable pointing to the path of a JSON key file downloaded from your service account on GCP.\n2. Lastly, you need to find your [project ID](https://support.google.com/googleapi/answer/7014113?hl=en) and [geographic region for VertexAI](https://cloud.google.com/vertex-ai/docs/general/locations).\n\n\n**Make sure the following env vars are set:**\n\n[code example]\n\n\n[code example]"},{"id":"google-gen-ai","title":"Google Gen AI","role":"content","text":"The `GoogleGenAIVectorizer` uses Google's [`google-genai`](https://pypi.org/project/google-genai/) SDK — the supported replacement for the deprecated Vertex AI model-garden SDK. It reaches **both** Google embedding backends from a single client:\n\n- **Vertex AI / Gemini Enterprise** — GCP project auth (`project_id` + `location`, credentials via ADC).\n- **Gemini Developer API** — a single `api_key`.\n\nInstall it with `pip install redisvl[google-genai]`.\n\n**Migrating from `VertexAIVectorizer`:**\n\n| | `VertexAIVectorizer` (deprecated) | `GoogleGenAIVectorizer` |\n|---|---|---|\n| SDK | `google-cloud-aiplatform` (deprecated) | `google-genai` (supported) |\n| Default model | `textembedding-gecko` (768 dims) | `gemini-embedding-001` (3072 dims) |\n| Backends | Vertex AI only | Vertex AI **and** Gemini Developer API |\n| Async | no | yes (`aembed`, `aembed_many`) |\n\nBecause the default model and dimensions differ, embeddings are **not** interchangeable — reindex when you switch. When you request a reduced `output_dimensionality`, the vectorizer L2-normalizes the result so it stays valid for Redis COSINE / inner-product search.\n\n**Set one of the following:**\n\n[code example]\n\n\n\n[code example]"},{"id":"cohere","title":"Cohere","role":"content","text":"[Cohere](https://dashboard.cohere.ai/) allows you to implement language AI into your product. The `CohereTextVectorizer` makes it simple to use RedisVL with the embeddings models at Cohere. For this you will need to install `cohere`.\n\n[code example]\n\n\n[code example]\n\n\nSpecial attention needs to be paid to the `input_type` parameter for each `embed` call. For example, for embedding \nqueries, you should set `input_type='search_query'`; for embedding documents, set `input_type='search_document'`. See\nmore information [here](https://docs.cohere.com/reference/embed)\n\n\n[code example]\n\nLearn more about using RedisVL and Cohere together through [this dedicated user guide](https://docs.cohere.com/docs/redis-and-cohere)."},{"id":"voyageai","title":"VoyageAI","role":"content","text":"[VoyageAI](https://dash.voyageai.com/) allows you to implement language AI into your product. The `VoyageAIVectorizer` makes it simple to use RedisVL with the embeddings models at VoyageAI. For this you will need to install `voyageai`.\n\n[code example]\n\n\n[code example]\n\n\nSpecial attention needs to be paid to the `input_type` parameter for each `embed` call. For example, for embedding \nqueries, you should set `input_type='query'`; for embedding documents, set `input_type='document'`. See\nmore information [here](https://docs.voyageai.com/docs/embeddings)\n\n\n[code example]\n\n#### Contextualized embeddings\n\nVoyageAI's `voyage-context-*` models support *contextualized* chunk embeddings. The `VoyageAIVectorizer` automatically routes `voyage-context-*` models to the contextualized embeddings API: pass your list of chunk strings to `embed_many` and you get one embedding back per chunk. Each input string is sent as its own auto-chunked document, so the inputs are embedded **independently** (a chunk's embedding does not depend on the other strings in the list). This preserves the one-embedding-per-input contract and keeps the embeddings cache deterministic.\n\n\n[code example]"},{"id":"mistral-ai","title":"Mistral AI","role":"content","text":"[Mistral](https://console.mistral.ai/) offers LLM and embedding APIs for you to implement into your product. The `MistralAITextVectorizer` makes it simple to use RedisVL with their embeddings model.\nYou will need to install `mistralai`.\n\n[code example]\n\n\n[code example]"},{"id":"amazon-bedrock","title":"Amazon Bedrock","role":"content","text":"Amazon Bedrock provides fully managed foundation models for text embeddings. Install the required dependencies:\n\n[code example]\n\n#### Configure AWS credentials:\n\n\n[code example]\n\n#### Create embeddings:\n\n\n[code example]"},{"id":"custom-vectorizers","title":"Custom Vectorizers","role":"content","text":"RedisVL supports the use of other vectorizers and provides a class to enable compatibility with any function that generates a vector or vectors from string data\n\n\n[code example]\n\nThis enables the use of custom vectorizers with other RedisVL components\n\n\n[code example]"},{"id":"search-with-provider-embeddings","title":"Search with Provider Embeddings","role":"content","text":"Now that we've created our embeddings, we can use them to search for similar sentences. We will use the same 3 sentences from above and search for similar sentences.\n\nFirst, we need to create the schema for our index.\n\nHere's what the schema for the example looks like in yaml for the HuggingFace vectorizer:\n\n[code example]\n\n\n[code example]\n\n\n[code example]\n\nLoading data to RedisVL is easy. It expects a list of dictionaries. The vector is stored as bytes.\n\n\n[code example]\n\n\n[code example]"},{"id":"selecting-your-float-data-type","title":"Selecting your float data type","role":"content","text":"When embedding text as byte arrays RedisVL supports 4 different floating point data types, `float16`, `float32`, `float64` and `bfloat16`, and 2 integer types, `int8` and `uint8`.\nYour dtype set for your vectorizer must match what is defined in your search index. If one is not explicitly set the default is `float32`.\n\n\n[code example]"},{"id":"next-steps","title":"Next Steps","role":"content","text":"Now that you understand how to create embeddings, explore these related guides:\n\n- [Getting Started](https://redis.io/docs/latest/../getting_started) - Learn the basics of indexes and queries\n- [Rerank Results](https://redis.io/docs/latest/rerankers) - Improve search quality with reranking models\n- [Cache Embeddings](https://redis.io/docs/latest/embeddings_cache) - Cache embedding vectors for faster repeated computations"},{"id":"cleanup","title":"Cleanup","role":"content","text":"[code example]"}],"examples":[{"id":"what-youll-learn-ex0","language":"python","code":"# import necessary modules\nimport os","section_id":"what-youll-learn"},{"id":"openai-ex0","language":"bash","code":"pip install openai","section_id":"openai"},{"id":"openai-ex1","language":"python","code":"import getpass\n\n# setup the API Key\napi_key = os.environ.get(\"OPENAI_API_KEY\") or getpass.getpass(\"Enter your OpenAI API key: \")","section_id":"openai"},{"id":"openai-ex2","language":"python","code":"from redisvl.utils.vectorize import OpenAITextVectorizer\n\n# create a vectorizer\noai = OpenAITextVectorizer(\n    model=\"text-embedding-ada-002\",\n    api_config={\"api_key\": api_key},\n)\n\ntest = oai.embed(\"This is a test sentence.\")\nprint(\"Vector dimensions: \", len(test))\ntest[:10]","section_id":"openai"},{"id":"openai-ex3","language":"python","code":"# Create many embeddings at once\nsentences = [\n    \"That is a happy dog\",\n    \"That is a happy person\",\n    \"Today is a sunny day\"\n]\n\nembeddings = oai.embed_many(sentences)\nembeddings[0][:10]","section_id":"openai"},{"id":"openai-ex4","language":"python","code":"# openai also supports asynchronous requests, which we can use to speed up the vectorization process.\nembeddings = await oai.aembed_many(sentences)\nprint(\"Number of Embeddings:\", len(embeddings))","section_id":"openai"},{"id":"azure-openai-ex0","language":"python","code":"# NBVAL_SKIP\n# additionally to the API Key, setup the API endpoint and version\napi_key = os.environ.get(\"AZURE_OPENAI_API_KEY\") or getpass.getpass(\"Enter your AzureOpenAI API key: \")\napi_version = os.environ.get(\"OPENAI_API_VERSION\") or getpass.getpass(\"Enter your AzureOpenAI API version: \")\nazure_endpoint = os.environ.get(\"AZURE_OPENAI_ENDPOINT\") or getpass.getpass(\"Enter your AzureOpenAI API endpoint: \")\ndeployment_name = os.environ.get(\"AZURE_OPENAI_DEPLOYMENT_NAME\", \"text-embedding-ada-002\")\n\n# Skip Azure examples when required values are missing (e.g. Run All without Azure configured).\n_azure_configured = bool(azure_endpoint and api_key and api_version)","section_id":"azure-openai"},{"id":"azure-openai-ex1","language":"python","code":"# NBVAL_SKIP\n# Depends on the Azure OpenAI cell above, which is not executed in CI.\nfrom redisvl.utils.vectorize import AzureOpenAITextVectorizer\n\nif not _azure_configured:\n    print(\"Skipping Azure OpenAI example: set AZURE_OPENAI_ENDPOINT, AZURE_OPENAI_API_KEY and OPENAI_API_VERSION\")\n    az_oai = None\nelse:\n    az_oai = AzureOpenAITextVectorizer(\n        model=deployment_name,  # Must be your CUSTOM deployment name\n        api_config={\n            \"api_key\": api_key,\n            \"api_version\": api_version,\n            \"azure_endpoint\": azure_endpoint,\n        },\n    )\n\n    test = az_oai.embed(\"This is a test sentence.\")\n    print(\"Vector dimensions: \", len(test))\n    test[:10]","section_id":"azure-openai"},{"id":"azure-openai-ex2","language":"python","code":"# NBVAL_SKIP\n# Depends on the Azure OpenAI cell above, which is not executed in CI.\n# Just like OpenAI, AzureOpenAI supports batching embeddings and asynchronous requests.\nsentences = [\n    \"That is a happy dog\",\n    \"That is a happy person\",\n    \"Today is a sunny day\",\n]\n\nif _azure_configured and az_oai is not None:\n    embeddings = await az_oai.aembed_many(sentences)\n    embeddings[0][:10]\nelse:\n    print(\"Skipping: run the Azure cells above with valid configuration.\")","section_id":"azure-openai"},{"id":"huggingface-ex0","language":"bash","code":"pip install sentence-transformers","section_id":"huggingface"},{"id":"huggingface-ex1","language":"python","code":"os.environ[\"TOKENIZERS_PARALLELISM\"] = \"false\"\nfrom redisvl.utils.vectorize import HFTextVectorizer\n\n\n# create a vectorizer\n# choose your model from the huggingface website\nhf = HFTextVectorizer(model=\"sentence-transformers/all-mpnet-base-v2\")\n\n# embed a sentence\ntest = hf.embed(\"This is a test sentence.\")\ntest[:10]","section_id":"huggingface"},{"id":"huggingface-ex2","language":"python","code":"# You can also create many embeddings at once\nembeddings = hf.embed_many(sentences, as_buffer=True)","section_id":"huggingface"},{"id":"ollama-ex0","language":"bash","code":"pip install 'redisvl[ollama]'\nollama pull nomic-embed-text","section_id":"ollama"},{"id":"ollama-ex1","language":"python","code":"# NBVAL_SKIP\n# No Ollama server in CI, so this only burns time on connection retries.\nfrom redisvl.utils.vectorize import OllamaTextVectorizer\n\nollama_model = os.environ.get(\"OLLAMA_MODEL\", \"nomic-embed-text\")\n\ntry:\n    ollama = OllamaTextVectorizer(model=ollama_model)\n\n    test = ollama.embed(\"This is a test sentence.\")\n    print(\"Vector dimensions:\", len(test))\n    print(test[:10])\nexcept (ImportError, ConnectionError, ValueError) as exc:\n    print(\"Skipping Ollama example:\", exc)\n    ollama = None","section_id":"ollama"},{"id":"ollama-ex2","language":"python","code":"# NBVAL_SKIP\n# Depends on the Ollama cell above, which is not executed in CI.\nif ollama is not None:\n    embeddings = ollama.embed_many(sentences, batch_size=2)\n    print(\"Number of embeddings:\", len(embeddings))\n    print(\"Vector dimensions:\", len(embeddings[0]))\nelse:\n    print(\"Skipping: run the Ollama cell above with a running Ollama server and pulled model.\")","section_id":"ollama"},{"id":"ollama-ex3","language":"python","code":"# NBVAL_SKIP\n# Depends on the Ollama cell above, which is not executed in CI.\nif ollama is not None:\n    embeddings = await ollama.aembed_many(sentences, batch_size=2)\n    print(\"Number of async embeddings:\", len(embeddings))\nelse:\n    print(\"Skipping: run the Ollama cell above with a running Ollama server and pulled model.\")","section_id":"ollama"},{"id":"vertexai-ex0","language":"bash","code":"pip install google-cloud-aiplatform>=1.26","section_id":"vertexai"},{"id":"vertexai-ex1","language":"plaintext","code":"GOOGLE_APPLICATION_CREDENTIALS=<path to your gcp JSON creds>\nGCP_PROJECT_ID=<your gcp project id>\nGCP_LOCATION=<your gcp geo region for vertex ai>","section_id":"vertexai"},{"id":"vertexai-ex2","language":"python","code":"# NBVAL_SKIP\n# Deprecated vectorizer; not executed in CI so notebook validation makes no API calls.\nfrom redisvl.utils.vectorize import VertexAIVectorizer\n\n\n# create a vectorizer\nvtx = VertexAIVectorizer(api_config={\n    \"project_id\": os.environ.get(\"GCP_PROJECT_ID\") or getpass.getpass(\"Enter your GCP Project ID: \"),\n    \"location\": os.environ.get(\"GCP_LOCATION\") or getpass.getpass(\"Enter your GCP Location: \"),\n    \"google_application_credentials\": os.environ.get(\"GOOGLE_APPLICATION_CREDENTIALS\") or getpass.getpass(\"Enter your Google App Credentials path: \")\n})\n\n# embed a sentence\ntest = vtx.embed(\"This is a test sentence.\")\ntest[:10]","section_id":"vertexai"},{"id":"google-gen-ai-ex0","language":"plaintext","code":"# Gemini Developer API\nGEMINI_API_KEY=<your gemini api key>\n\n# or Vertex AI\nGOOGLE_CLOUD_PROJECT=<your gcp project id>\nGOOGLE_CLOUD_LOCATION=<your gcp region>\nGOOGLE_APPLICATION_CREDENTIALS=<path to your gcp JSON creds>","section_id":"google-gen-ai"},{"id":"google-gen-ai-ex1","language":"python","code":"# NBVAL_SKIP\n# Docs example; not executed in CI so notebook validation makes no API calls.\nfrom redisvl.utils.vectorize import GoogleGenAIVectorizer\n\n# Auto-detects the backend: GEMINI_API_KEY -> Gemini, else GCP project/location -> Vertex AI.\n# Guarded so this notebook still runs when Google credentials aren't configured.\ntry:\n    genai_vectorizer = GoogleGenAIVectorizer(model=\"gemini-embedding-001\")\n    print(f\"backend={genai_vectorizer.backend}, dims={genai_vectorizer.dims}\")\n    genai_test = genai_vectorizer.embed(\"This is a test sentence.\")\n    print(genai_test[:10])\nexcept (ImportError, ValueError) as e:\n    print(f\"Skipping GoogleGenAIVectorizer demo: {e}\")\n    genai_vectorizer = None","section_id":"google-gen-ai"},{"id":"cohere-ex0","language":"bash","code":"pip install cohere","section_id":"cohere"},{"id":"cohere-ex1","language":"python","code":"import getpass\n# setup the API Key\napi_key = os.environ.get(\"COHERE_API_KEY\") or getpass.getpass(\"Enter your Cohere API key: \")","section_id":"cohere"},{"id":"cohere-ex2","language":"python","code":"from redisvl.utils.vectorize import CohereTextVectorizer\n\n# create a vectorizer\nco = CohereTextVectorizer(\n    model=\"embed-english-v3.0\",\n    api_config={\"api_key\": api_key},\n)\n\n# embed a search query\ntest = co.embed(\"This is a test sentence.\", input_type='search_query')\nprint(\"Vector dimensions: \", len(test))\nprint(test[:10])\n\n# embed a document\ntest = co.embed(\"This is a test sentence.\", input_type='search_document')\nprint(\"Vector dimensions: \", len(test))\nprint(test[:10])","section_id":"cohere"},{"id":"voyageai-ex0","language":"bash","code":"pip install voyageai","section_id":"voyageai"},{"id":"voyageai-ex1","language":"python","code":"import getpass\n# setup the API Key\napi_key = os.environ.get(\"VOYAGE_API_KEY\") or getpass.getpass(\"Enter your VoyageAI API key: \")","section_id":"voyageai"},{"id":"voyageai-ex2","language":"python","code":"from redisvl.utils.vectorize import VoyageAIVectorizer\n\n# create a vectorizer\nvo = VoyageAIVectorizer(\n    model=\"voyage-law-2\",  # Please check the available models at https://docs.voyageai.com/docs/embeddings\n    api_config={\"api_key\": api_key},\n)\n\n# embed a search query\ntest = vo.embed(\"This is a test sentence.\", input_type='query')\nprint(\"Vector dimensions: \", len(test))\nprint(test[:10])\n\n# embed a document\ntest = vo.embed(\"This is a test sentence.\", input_type='document')\nprint(\"Vector dimensions: \", len(test))\nprint(test[:10])","section_id":"voyageai"},{"id":"voyageai-ex3","language":"python","code":"# NBVAL_SKIP\n# Contextualized embeddings (voyage-context-* models). Each input string is sent\n# as its own auto-chunked document and embedded independently, so the vectorizer\n# returns exactly one embedding per input string. Requires voyageai>=0.5.0.\ncontext_vo = VoyageAIVectorizer(\n    model=\"voyage-context-4\",  # See https://docs.voyageai.com/docs/contextualized-chunk-embeddings\n    api_config={\"api_key\": api_key},\n)\n\nchunks = [\n    \"That is a happy dog\",\n    \"That is a happy person\",\n    \"Today is a sunny day\",\n]\ncontext_embeddings = context_vo.embed_many(chunks, input_type=\"document\")\nprint(\"Number of embeddings:\", len(context_embeddings))\nprint(\"Vector dimensions:\", len(context_embeddings[0]))","section_id":"voyageai"},{"id":"mistral-ai-ex0","language":"bash","code":"pip install mistralai","section_id":"mistral-ai"},{"id":"mistral-ai-ex1","language":"python","code":"from redisvl.utils.vectorize import MistralAITextVectorizer\n\nmistral = MistralAITextVectorizer()\n\n# embed a sentence using their asynchronous method\ntest = await mistral.aembed(\"This is a test sentence.\")\nprint(\"Vector dimensions: \", len(test))\nprint(test[:10])","section_id":"mistral-ai"},{"id":"amazon-bedrock-ex0","language":"bash","code":"pip install 'redisvl[bedrock]'  # Installs boto3","section_id":"amazon-bedrock"},{"id":"amazon-bedrock-ex1","language":"python","code":"import os\nimport getpass\n\nif \"AWS_ACCESS_KEY_ID\" not in os.environ:\n    os.environ[\"AWS_ACCESS_KEY_ID\"] = getpass.getpass(\"Enter AWS Access Key ID: \")\nif \"AWS_SECRET_ACCESS_KEY\" not in os.environ:\n    os.environ[\"AWS_SECRET_ACCESS_KEY\"] = getpass.getpass(\"Enter AWS Secret Key: \")\n\nos.environ[\"AWS_REGION\"] = \"us-east-1\"  # Change as needed","section_id":"amazon-bedrock"},{"id":"amazon-bedrock-ex2","language":"python","code":"from redisvl.utils.vectorize import BedrockVectorizer\n\nbedrock = BedrockVectorizer(\n    model=\"amazon.titan-embed-text-v2:0\"\n)\n\n# Single embedding\ntext = \"This is a test sentence.\"\nembedding = bedrock.embed(text)\nprint(f\"Vector dimensions: {len(embedding)}\")\n\n# Multiple embeddings\nsentences = [\n    \"That is a happy dog\",\n    \"That is a happy person\",\n    \"Today is a sunny day\"\n]\nembeddings = bedrock.embed_many(sentences)","section_id":"amazon-bedrock"},{"id":"custom-vectorizers-ex0","language":"python","code":"from redisvl.utils.vectorize import CustomVectorizer\n\ndef generate_embeddings(text_input, **kwargs):\n    return [0.101] * 768\n\ncustom_vectorizer = CustomVectorizer(generate_embeddings)\n\ncustom_vectorizer.embed(\"This is a test sentence.\")[:10]","section_id":"custom-vectorizers"},{"id":"custom-vectorizers-ex1","language":"python","code":"from redisvl.extensions.cache.llm import SemanticCache\n\ncache = SemanticCache(name=\"custom_cache\", vectorizer=custom_vectorizer)\n\ncache.store(\"this is a test prompt\", \"this is a test response\")\ncache.check(\"this is also a test prompt\")","section_id":"custom-vectorizers"},{"id":"search-with-provider-embeddings-ex0","language":"yaml","code":"version: '0.1.0'\n\nindex:\n    name: vectorizers\n    prefix: doc\n    storage_type: hash\n\nfields:\n    - name: sentence\n      type: text\n    - name: embedding\n      type: vector\n      attrs:\n        dims: 768\n        algorithm: flat\n        distance_metric: cosine","section_id":"search-with-provider-embeddings"},{"id":"search-with-provider-embeddings-ex1","language":"python","code":"from redisvl.index import SearchIndex\n\n# construct a search index from the schema\nindex = SearchIndex.from_yaml(\"./schema.yaml\", redis_url=\"redis://localhost:6379\")\n\n# create the index (no data yet)\nindex.create(overwrite=True)","section_id":"search-with-provider-embeddings"},{"id":"search-with-provider-embeddings-ex2","language":"python","code":"# use the CLI to see the created index\n!rvl index listall","section_id":"search-with-provider-embeddings"},{"id":"search-with-provider-embeddings-ex3","language":"python","code":"from redisvl.redis.utils import array_to_buffer\n\nembeddings = hf.embed_many(sentences)\n\ndata = [{\"text\": t,\n         \"embedding\": array_to_buffer(v, dtype=\"float32\")}\n        for t, v in zip(sentences, embeddings)]\n\nindex.load(data)","section_id":"search-with-provider-embeddings"},{"id":"search-with-provider-embeddings-ex4","language":"python","code":"from redisvl.query import VectorQuery\n\n# use the HuggingFace vectorizer again to create a query embedding\nquery_embedding = hf.embed(\"That is a happy cat\")\n\nquery = VectorQuery(\n    vector=query_embedding,\n    vector_field_name=\"embedding\",\n    return_fields=[\"text\"],\n    num_results=3\n)\n\nresults = index.query(query)\nfor doc in results:\n    print(doc[\"text\"], doc[\"vector_distance\"])","section_id":"search-with-provider-embeddings"},{"id":"selecting-your-float-data-type-ex0","language":"python","code":"vectorizer = HFTextVectorizer(dtype=\"float16\")\n\n# subsequent calls to embed('', as_buffer=True) and embed_many('', as_buffer=True) will now encode as float16\nfloat16_bytes = vectorizer.embed('test sentence', as_buffer=True)\n\n# to generate embeddings with different dtype instantiate a new vectorizer\nvectorizer_64 = HFTextVectorizer(dtype='float64')\nfloat64_bytes = vectorizer_64.embed('test sentence', as_buffer=True)\n\nfloat16_bytes != float64_bytes","section_id":"selecting-your-float-data-type"},{"id":"cleanup-ex0","language":"python","code":"index.delete()","section_id":"cleanup"}]}
{"schema_version":2,"id":"develop/ai/redisvl/user_guide","title":"Guides","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/","summary":"","aliases":["/integrate/redisvl/user_guide"],"tags":[],"last_updated":"2026-06-08T15:46:44+02:00","children":[{"id":"develop/ai/redisvl/user_guide/getting_started","summary":"","title":"Getting Started","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/getting_started/"},{"id":"develop/ai/redisvl/user_guide/how_to_guides","summary":"","title":"How-To Guides","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/how_to_guides/"},{"id":"develop/ai/redisvl/user_guide/use_cases","summary":"","title":"Use Cases","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/use_cases/"},{"id":"develop/ai/redisvl/user_guide/installation","summary":"","title":"Install RedisVL","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/installation/"},{"id":"develop/ai/redisvl/user_guide/cli","summary":"","title":"The RedisVL CLI","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/cli/"}],"page_type":"content","content_hash":"4b40d119411f0017bbe6c41d6351f20d8f90dcbcbe2293d850511db692ab130f","sections":[{"id":"content","title":"Content","role":"content","text":"Welcome to the RedisVL guides! Whether you're just getting started or building advanced AI applications, these guides will help you make the most of Redis as your vector database.\n\n<div class=\"grid grid-cols-1 md:grid-cols-2 gap-4 my-6\">\n<a href=\"installation/\" class=\"block p-5 border border-redis-pen-300 rounded-lg hover:border-redis-red-500 hover:shadow-md transition-all duration-200 no-underline hover:no-underline\"><h3 class=\"mt-0 mb-2\">📦 Installation</h3><p><strong>Set up RedisVL.</strong> Install the library and configure your Redis instance for vector search.</p><p class=\"text-sm opacity-75 mt-3\">pip install • Redis Cloud • Docker</p></a>\n<a href=\"getting_started/\" class=\"block p-5 border border-redis-pen-300 rounded-lg hover:border-redis-red-500 hover:shadow-md transition-all duration-200 no-underline hover:no-underline\"><h3 class=\"mt-0 mb-2\">🚀 Getting Started</h3><p><strong>New to RedisVL?</strong> Start here to learn the basics and build your first vector search application in minutes.</p><p class=\"text-sm opacity-75 mt-3\">Schema → Index → Load → Query</p></a>\n<a href=\"how_to_guides/\" class=\"block p-5 border border-redis-pen-300 rounded-lg hover:border-redis-red-500 hover:shadow-md transition-all duration-200 no-underline hover:no-underline\"><h3 class=\"mt-0 mb-2\">🛠️ How-To Guides</h3><p><strong>Solve specific problems.</strong> Task-oriented recipes for LLM extensions, querying, embeddings, optimization, and storage.</p><p class=\"text-sm opacity-75 mt-3\">LLM Caching • Filtering • MCP • Reranking • Migrations</p></a>\n<a href=\"how_to_guides/mcp/\" class=\"block p-5 border border-redis-pen-300 rounded-lg hover:border-redis-red-500 hover:shadow-md transition-all duration-200 no-underline hover:no-underline\"><h3 class=\"mt-0 mb-2\">🧠 MCP Setup</h3><p><strong>Expose Redis through MCP.</strong> Run the RedisVL MCP server, configure one existing index, and use search or optional upsert tools.</p><p class=\"text-sm opacity-75 mt-3\">stdio, HTTP, SSE • One index • Search and upsert</p></a>\n<a href=\"cli/\" class=\"block p-5 border border-redis-pen-300 rounded-lg hover:border-redis-red-500 hover:shadow-md transition-all duration-200 no-underline hover:no-underline\"><h3 class=\"mt-0 mb-2\">💻 CLI Reference</h3><p><strong>Command-line tools.</strong> Manage indices, inspect stats, and work with schemas using the <code>rvl</code> CLI.</p><p class=\"text-sm opacity-75 mt-3\">rvl index • rvl stats • rvl migrate • Schema YAML</p></a>\n<a href=\"use_cases/\" class=\"block p-5 border border-redis-pen-300 rounded-lg hover:border-redis-red-500 hover:shadow-md transition-all duration-200 no-underline hover:no-underline\"><h3 class=\"mt-0 mb-2\">💡 Use Cases</h3><p><strong>Apply RedisVL to real-world problems.</strong> See which guides map to your use case.</p><p class=\"text-sm opacity-75 mt-3\">Agent Context • Agent Optimization • Search • RecSys</p></a>\n</div>"}],"examples":[]}
{"schema_version":2,"id":"develop/ai/redisvl/user_guide/installation","title":"Install RedisVL","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/installation/","summary":"","aliases":["/integrate/redisvl/user_guide/installation"],"tags":[],"last_updated":"2026-09-11T10:30:42+02:00","page_type":"content","content_hash":"d4d31731c195aa6390f06185f03af22e32497df894d681d6fc96f18ddfed991e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"There are a few ways to install RedisVL. The easiest way is to use pip."},{"id":"install-redisvl-with-pip","title":"Install RedisVL with Pip","role":"setup","text":"Install `redisvl` into your Python (>=3.10) environment using `pip`:\n\n[code example]\n\nRedisVL comes with a few dependencies that are automatically installed, however, several optional\ndependencies can be installed separately based on your needs:\n\n[code example]\n\nIf you use ZSH, remember to escape the brackets:\n\n[code example]\n\nYou can install multiple optional dependencies at once:\n\n[code example]\n\nTo install **all** optional dependencies at once:\n\n[code example]"},{"id":"install-redisvl-from-source","title":"Install RedisVL from Source","role":"setup","text":"To install RedisVL from source, clone the repository and install the package using `pip`:\n\n[code example]"},{"id":"development-installation","title":"Development Installation","role":"content","text":"For contributors who want to develop RedisVL, we recommend using [uv](https://docs.astral.sh/uv/) for dependency management:\n\n[code example]\n\nThis installs the package in editable mode along with all development dependencies (testing, linting, type checking) and documentation dependencies."},{"id":"running-tests-and-linting","title":"Running Tests and Linting","role":"content","text":"[code example]"},{"id":"pre-commit-hooks","title":"Pre-commit Hooks","role":"content","text":"We use pre-commit hooks to ensure code quality. Install them with:\n\n[code example]\n\nRun hooks manually on all files:\n\n[code example]"},{"id":"installing-redis","title":"Installing Redis","role":"setup","text":"RedisVL requires Redis with [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query/) available. There are several options:\n\n1. [Redis Cloud](https://redis.io/cloud), a fully managed cloud offering with a free tier\n2. [Redis 8+ (Docker)](https://redis.io/downloads/), for local development and testing\n3. [Redis Software](https://redis.io/software/), a commercial self-hosted option"},{"id":"redis-cloud","title":"Redis Cloud","role":"content","text":"Redis Cloud is the easiest way to get started with RedisVL. You can sign up for a free account [here](https://redis.io/cloud). Make sure to have `Redis Search`\nenabled when creating your database."},{"id":"redis-8-local-development","title":"Redis 8+ (local development)","role":"content","text":"For local development and testing, we recommend running Redis 8+ in a Docker container:\n\n[code example]\n\nRedis 8 includes Redis Search and built-in vector search capabilities."},{"id":"redis-software-self-hosted","title":"Redis Software (self-hosted)","role":"content","text":"Redis Software is a commercial offering that can be self-hosted. You can download the latest version [here](https://redis.io/downloads/).\n\nIf you are considering a self-hosted Redis Software deployment on Kubernetes, there is the [Redis Software Operator](https://docs.redis.com/latest/kubernetes/) for Kubernetes. This will allow you to easily deploy and manage a Redis Software cluster on Kubernetes."},{"id":"redis-sentinel","title":"Redis Sentinel","role":"content","text":"For high availability deployments, RedisVL supports connecting to Redis through Sentinel. Use the `redis+sentinel://` URL scheme to connect. Both sync and async connections are fully supported.\n\n[code example]\n\nThe Sentinel URL format supports:\n\n- Multiple sentinel hosts (comma-separated)\n- Optional authentication (username:password)\n- Service name (defaults to `mymaster` if not specified)\n- Optional database number (defaults to 0)\n- Both sync (`SearchIndex`) and async (`AsyncSearchIndex`) connections"},{"id":"redis-permissions-acls","title":"Redis permissions (ACLs)","role":"content","text":"RedisVL reaches Redis through Redis Search commands, but not all of them need the `@search` category. Querying and loading work under an ordinary `+@read +@write` role; it is the commands that inspect or manage an index — `FT.INFO`, `FT.CREATE`, `FT._LIST` — that need `@search` or an explicit grant.\n\nThe command-to-category mapping below was measured against live servers rather than quoted from published documentation, which does not list ACL categories per `FT.*` command. It was identical on Redis 8.0.6, 8.2.7, 8.4.5, 8.6.4, 8.8.0 and 8.8.1. Check your own deployment with `COMMAND INFO ft.info`, `ACL CAT search`, or `ACL DRYRUN <user> FT.INFO <index>`."},{"id":"what-each-operation-needs","title":"What each operation needs","role":"content","text":"| Operation                                                                                      | Redis command                           | `+@all -@admin`   | `+@read +@write`   |\n|------------------------------------------------------------------------------------------------|-----------------------------------------|-------------------|--------------------|\n| `index.query()`, `index.search()`, `index.aggregate()`                                         | `FT.SEARCH`, `FT.AGGREGATE`             | Yes               | Yes                |\n| `index.load()`                                                                                 | `HSET` or `JSON.SET` (needs key access) | Yes               | Yes                |\n| `index.clear()`                                                                                | `FT.SEARCH`, then `DEL` per batch       | Yes               | Yes                |\n| `index.exists()`, `index.info()`, `SearchIndex.from_existing()`, `rvl index info`, `rvl stats` | `FT.INFO`                               | Yes               | **No**             |\n| `index.create()`                                                                               | `FT.CREATE`                             | Yes               | **No**             |\n| `index.delete()`, `rvl index delete`, `rvl index destroy`                                      | `FT.DROPINDEX`                          | Yes               | Yes                |\n| Enumerating indexes (see below)                                                                | `FT._LIST`                              | **No**            | **No**             |\n\nExcept for `FT.CREATE`, every `Yes` above assumes key patterns that cover the index prefix — see [Key permissions](). `FT.CREATE` is not checked against those patterns, so a credential can create an index it cannot query. Adding `-@dangerous` to the second column additionally denies `FT.DROPINDEX`, so `index.delete()` becomes `No`. An SVS-VAMANA schema needs more than `FT.CREATE`: `index.create()` first probes capabilities with `INFO` (`@slow @dangerous`) and `MODULE LIST` (`@admin @slow @dangerous`), so both `-@admin` and `-@dangerous` policies break creation for those schemas.\n\nTwo of the rows above deserve their own explanation.\n\n`FT._LIST` is tagged `@admin` as well as `@search` and `@slow`, and ACL rules are applied left to right — so a rule that grants search access and then takes back administrative commands, such as `+@search -@admin` or `+@all -@admin`, denies it:\n\n[code example]\n\n`FT._LIST` does not *require* `@admin`: granting `+@search` on its own permits it. Only rules that subtract `@admin` after granting search are affected. To keep such a policy and still enumerate indexes, grant the command back explicitly with `+ft._list`. Enumeration is reached by `SearchIndex.listall()` and `AsyncSearchIndex.listall()`, by `rvl index listall`, and by the migration entry points that discover indexes for you: `rvl migrate helper`, `rvl migrate wizard` when no `-i/--index` is given, and `rvl migrate batch-plan --pattern`.\n\n`FT.DROPINDEX` is tagged `@dangerous` and `@write` as well as `@search`, so a policy that subtracts `@dangerous` denies `index.delete()`, `rvl index delete`, and `rvl index destroy`."},{"id":"roles-built-from-read-and-write","title":"Roles built from `@read` and `@write`","role":"content","text":"`FT.INFO` is in neither `@read` nor `@write` — its only category is `@search` — and `FT.CREATE` is the same. So an application role assembled from `+@read +@write -@dangerous` — a natural least-privilege shape for a runtime that must query and load but must not manage indexes — can query and load, but cannot ask whether an index exists and cannot create or drop one. Subtracting `@dangerous` is not what denies `FT.INFO` or `FT.CREATE`: `+@all -@dangerous` permits both. Those commands are simply never granted by `@read` or `@write`.\n\nEvery extension constructor checks whether its index exists, so under such a credential all of them fail while being constructed:\n\n[code example]\n\nReading a cache is not read-only either. When a cache has a TTL configured — at construction or later via `set_ttl()` — every read that hits refreshes the matched entries’ TTL, so it issues `EXPIRE`. That includes `SemanticCache.check()`, every `EmbeddingsCache` getter, their async equivalents, and the reads a vectorizer performs for you when constructed with `cache=`. `EXPIRE` is in `@write`, not `@read` (measured with `ACL CAT write`), so a lookup-only credential fails on a cache *hit* rather than on the write that populated it.\n\nGranting the command is only half of it, because the key patterns must permit writes too. Measured on 8.4.5 with `ACL DRYRUN <user> EXPIRE llmcache:abc 60`:\n\n| Rules                          | Result                                            |\n|--------------------------------|---------------------------------------------------|\n| `+@read ~llmcache:*`           | `no permissions to run the 'expire' command`      |\n| `+@read +expire ~llmcache:*`   | Permitted                                         |\n| `+@read +expire %R~llmcache:*` | `no permissions to access the 'llmcache:abc' key` |\n\nSo `%R~llmcache:*` — the read-only shape the [Key permissions]() table below presents as sufficient for querying — is denied on the key, not the command. `+@read +@write` is unaffected, as is a cache with no TTL. See [Cache LLM Responses](https://redis.io/docs/latest/how_to_guides/llmcache) for the refresh behaviour itself, including that `set_ttl()` starts adding TTLs to entries stored without one."},{"id":"no-permissions-to-run-the-ftinfo-command","title":"\"no permissions to run the ‘FT.INFO’ command\"","role":"content","text":"RedisVL does not guess its way around this. A credential that cannot ask whether the index exists also cannot create one, so there is nothing useful to infer — instead, tell RedisVL that the index is already there:\n\n[code example]\n\n`create_index=False` is available on `SemanticCache`, `MessageHistory`, `SemanticMessageHistory` and `SemanticRouter`. It skips the existence check, the comparison of your schema against the live index, and index creation — the constructor issues no index command at all. Pass it when the index is managed externally, or when the credential cannot run `FT.INFO`. It cannot be combined with `overwrite=True`, which asks for the opposite.\n\nA `SearchIndex` used directly needs nothing special: build it with `from_dict()` or `from_yaml()`, then load and query. The methods that stay unavailable are the ones that read index metadata — `exists()`, `info()`, and `from_existing()`, which reconstructs a schema out of Redis. `clear()` is not among them: it enumerates with `FT.SEARCH` and deletes in batches, so it needs no more than querying does.\n\nThe flag also skips the SVS-VAMANA capability probe described above, since that runs inside `create()`."},{"id":"provisioning-a-router","title":"Provisioning a router","role":"content","text":"`SemanticRouter` with `create_index=False` writes nothing at all: not the reference vectors for its routes, and not the stored route config that `SemanticRouter.from_existing()` reads. Preparing a router for this mode therefore means constructing it once with a privileged credential — a hand-written `FT.CREATE` is not enough, because the reference vectors have to be embedded and written too. Without them the router matches nothing, which looks like a distance-threshold problem rather than an empty index.\n\nAfterwards, `SemanticRouter.from_existing(name, create_index=False)` is the way to attach to it: it recovers the routes and thresholds with `JSON.GET` and needs no `FT.INFO`. The stored config must contain the full route set. Each route’s distance threshold is applied from that recovered list, so an incomplete stored config silently narrows matching — and `add_route()` and `remove_route()` rewrite the stored config from the same list, so mutating an incomplete config permanently drops the omitted routes from the config every other client reads."},{"id":"when-the-schema-diverges","title":"When the schema diverges","role":"content","text":"With `create_index=False` nothing verifies that the live index matches the schema you described. Some mismatches are loud on first use, and several are silent:\n\n| Mismatch                                                     | What happens                                                                                                                                                                                                                                                                       |\n|--------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| The index does not exist                                     | `RedisSearchError` on the first query, naming the missing index                                                                                                                                                                                                                    |\n| Vector dimensions disagree                                   | `Error parsing vector similarity query: query vector blob size (32) does not match index's expected size (16)` — but only once the index holds a document. On an empty index the same query returns nothing, so a freshly provisioned index hides this until the first write lands |\n| The prefix does not cover your keys                          | **Silent.** Documents are written but never indexed, so queries return nothing, forever                                                                                                                                                                                            |\n| The index is `ON JSON` and you write hashes (or the reverse) | **Silent**, the same way                                                                                                                                                                                                                                                           |\n| The datatype or distance metric differs                      | **Silent.** Neither is restated by a query, so nothing compares them — results come back ranked by the index’s metric, not yours                                                                                                                                                   |\n\nFor the silent cases the tell is `FT.INFO`’s `key_type`, `prefixes` and `attributes` — not `hash_indexing_failures`, which stays `0` because those keys were never indexing candidates. Diagnosing it therefore needs a credential that can run `FT.INFO`."},{"id":"what-an-attach-only-instance-may-still-do","title":"What an attach-only instance may still do","role":"content","text":"Removing *entries* is available on every path, and is how a caller invalidates an externally managed cache without holding the provisioning credential: `clear()` (plus `SemanticCache.aclear()`), and targeted removal of a specific cache entry, message or route. None of it removes the index, and all of it runs under `+@read +@write`.\n\nWhat `create_index=False` refuses is `delete()` (and `SemanticCache.adelete()`), because that drops the index. Refusing it protects an externally managed index — including one reached through an alias — from being destroyed through an attach-only instance. Drop the index through the privileged provisioning path that owns it.\n\nThe two kinds of `clear()` decide *which keys go* differently, and neither choice is verified against the live index under this flag:\n\n| Method                                                                               | Deletes                              | Chooses keys by                                                             |\n|--------------------------------------------------------------------------------------|--------------------------------------|-----------------------------------------------------------------------------|\n| `SemanticCache.clear()`, `aclear()`                                                  | every key under `{name}:`            | `SCAN`/`DEL` on the prefix this instance declares — no index command at all |\n| `MessageHistory.clear()`, `SemanticMessageHistory.clear()`, `SemanticRouter.clear()` | every document the live index covers | `FT.SEARCH` paging via `SearchIndex.clear()`                                |\n\n`FT.SEARCH` is in `@read` as well as `@search`, so a `+@read +@write` credential is granted it — unlike `FT.INFO`, which is in neither and is what made these three unavailable before. Note that `FT.SEARCH` additionally requires the credential’s key patterns to be a superset of the index prefixes, the same rule described under [Key permissions]().\n\nBecause the two enumerate differently, they fail differently, and the section above is what decides which failure you get. Both are silent:\n\n- **Prefix-based clearing deletes too much, or nothing.** `SCAN`/`DEL` is blind to the index and to the key type, so it removes every key under `{name}:` — another writer’s entries, and unrelated application data sharing that namespace root. And if the live index covers a *different* prefix, or is an alias onto one, `clear()` deletes only what this instance itself wrote and leaves every served entry in place: it reports success and the cache still returns the stale hits you called it to invalidate.\n- **Index-based clearing deletes documents you never wrote.** `SearchIndex.clear()` deletes what the live index covers, so against an index on a different prefix — or a multi-`PREFIX` index, or an alias — it removes another application’s documents while leaving this instance’s own unindexed entries behind.\n\nDiagnosing either needs `FT.INFO`, which is the command an attach-only credential does not have. If the index is provisioned for you, get its `prefixes` and `key_type` from whoever provisions it and make your extension’s name match, rather than inferring it from a successful query."},{"id":"key-permissions","title":"Key permissions","role":"content","text":"Command categories are only half of it. Redis also scopes the search commands by key pattern: [the ACL documentation](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl/#command-categories) states that only users with access to a *superset* of the prefixes defined at index creation can create, modify, or read an index.\n\nMeasured on 8.4.5 against an index prefixed `doc:`, with the command categories held constant at `+@all`:\n\n| Key patterns                      | `FT.SEARCH`, `FT.INFO`, `FT.AGGREGATE`                                  |\n|-----------------------------------|-------------------------------------------------------------------------|\n| `~doc:*` (superset)               | Permitted                                                               |\n| `%R~doc:*` (read permission only) | Permitted                                                               |\n| `~doc:1` (partial overlap)        | `NOPERM User does not have the required permissions to query the index` |\n| `~other:*` (no overlap)           | The same denial                                                         |\n\nPartial overlap is worth emphasising: it fails exactly like no overlap at all, rather than returning the subset you can read. `FT.CREATE` is not checked this way, so a credential can create an index it is then unable to query.\n\nKey patterns are glob-style, matched by the same engine as `SCAN MATCH`, so metacharacters in a prefix do not mean what they look like. Measured on 8.4.5, `~cache[ab]:*` grants `cachea:1` and `cacheb:1` while *denying* the literal key `cache[ab]:1`; escaped as `~cache\\[ab]:*` it does the reverse. Escape `*`, `?`, `[` and `\\` in a prefix, and confirm the rule with `ACL DRYRUN`.\n\n`create_index=False` does not help here — the very commands it lets you avoid are joined by the ones it cannot, so widen the key patterns instead.\n\nOutside of Redis Search, RedisVL identifies itself on connect with `CLIENT SETINFO`. That command is tagged `@connection` and `@slow`, and belongs to neither `@read` nor `@write`, so a rule built up from those categories never grants it. A credential that cannot run it still connects: identification only populates the `lib-name` field that `CLIENT LIST` and `CLIENT INFO` display, so a refusal is ignored (and logged, if you have configured logging at debug level). Grant `+client|setinfo` if you want RedisVL to appear as the connecting library there — note that this labels the connection RedisVL opens, while redis-py labels the rest of the pool as plain `redis-py`.\n\nCluster deployments need one more grant. `RedisCluster` discovers the topology with `CLUSTER SLOTS`, which is tagged `@slow` only, so a credential assembled from `+@read +@write` cannot open a clustered connection at all — redis-py reports this as `Redis Cluster cannot be connected`, with the underlying permission error chained beneath it. Grant `+cluster|slots` alongside the rules above."},{"id":"redis-cloud-and-redis-software","title":"Redis Cloud and Redis Software","role":"content","text":"Both manage ACLs through their own control plane rather than the `ACL SETUSER` command, so permissions are attached to roles and users instead of being set on a connection.\n\n- **Redis Cloud** provides three predefined ACL rules that cannot be edited — Full-Access, Read-Write (\"read and write commands and excludes dangerous commands\"), and Read-Only — which you assign to a data access role. See [Configure permissions with Redis ACLs](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/configure-acls/). Custom rules use the same syntax as above.\n- **Redis Software** ships one predefined ACL, Full Access, and you define others in the Cluster Manager UI or with a [`POST /v1/redis_acls`](https://redis.io/docs/latest/operate/rs/security/access-control/create-db-roles/) request. It [does not support every `ACL` command](https://redis.io/docs/latest/operate/rs/security/access-control/redis-acl-overview/#acl-command-support), nor nested selectors, nor `(` and `)` in key patterns.\n\nBecause the predefined rules’ exact command sets are not published, confirm a credential against the database rather than inferring what its policy name implies — `ACL DRYRUN <user> FT.INFO <index>` answers it directly. Read the descriptions carefully before assuming you are unaffected: Read-Only allows read commands, and Read-Write \"allows read and write commands and excludes dangerous commands\", so both read as `@read`/`@write`-shaped — the shape that denies `FT.INFO` and `FT.CREATE` and wants `create_index=False`. Only Full-Access is clearly unaffected.\n\nRedis Software’s documentation uses `+@read +FT.INFO +FT.SEARCH` as an example rule, which is a good illustration: it permits querying and `index.exists()`, but not `index.create()` or index enumeration. Grant `FT.CREATE` explicitly when the application creates its own index. When the index is provisioned for the application instead, leave it out and construct with `create_index=False`."}],"examples":[{"id":"install-redisvl-with-pip-ex0","language":"bash","code":"$ pip install -U redisvl","section_id":"install-redisvl-with-pip"},{"id":"install-redisvl-with-pip-ex1","language":"bash","code":"# Vectorizer providers\n$ pip install redisvl[openai]              # OpenAI embeddings\n$ pip install redisvl[cohere]              # Cohere embeddings and reranking\n$ pip install redisvl[mistralai]           # Mistral AI embeddings\n$ pip install redisvl[voyageai]            # Voyage AI embeddings and reranking\n$ pip install redisvl[sentence-transformers]  # HuggingFace local embeddings\n$ pip install redisvl[google-genai]        # Google embeddings (Vertex AI + Gemini API)\n$ pip install redisvl[vertexai]            # Google Vertex AI embeddings (legacy; deprecated, multimodal only)\n$ pip install redisvl[bedrock]             # AWS Bedrock embeddings\n\n# Other optional features\n$ pip install redisvl[mcp]                 # RedisVL MCP server support (Python 3.10+)\n$ pip install redisvl[langcache]           # LangCache managed service integration\n$ pip install redisvl[sql-redis]           # SQL query support","section_id":"install-redisvl-with-pip"},{"id":"install-redisvl-with-pip-ex2","language":"bash","code":"$ pip install redisvl\\[openai\\]","section_id":"install-redisvl-with-pip"},{"id":"install-redisvl-with-pip-ex3","language":"bash","code":"$ pip install redisvl[mcp,openai,cohere,sentence-transformers]","section_id":"install-redisvl-with-pip"},{"id":"install-redisvl-with-pip-ex4","language":"bash","code":"$ pip install redisvl[all]","section_id":"install-redisvl-with-pip"},{"id":"install-redisvl-from-source-ex0","language":"bash","code":"$ git clone https://github.com/redis/redis-vl-python.git && cd redis-vl-python\n$ pip install .\n\n# or for an editable installation (for developers of RedisVL)\n$ pip install -e .","section_id":"install-redisvl-from-source"},{"id":"development-installation-ex0","language":"bash","code":"# Clone the repository\n$ git clone https://github.com/redis/redis-vl-python.git && cd redis-vl-python\n\n# Install uv if you don't have it\n$ pip install uv\n\n# Install all dependencies (including dev and docs)\n$ uv sync\n\n# Or use make\n$ make install","section_id":"development-installation"},{"id":"running-tests-and-linting-ex0","language":"bash","code":"# Run tests (no external APIs required)\n$ make test\n\n# Run all tests (includes API-dependent tests)\n$ make test-all\n\n# Format code\n$ make format\n\n# Run type checking\n$ make check-types\n\n# Run full check (lint + test)\n$ make check","section_id":"running-tests-and-linting"},{"id":"pre-commit-hooks-ex0","language":"bash","code":"$ pre-commit install","section_id":"pre-commit-hooks"},{"id":"pre-commit-hooks-ex1","language":"bash","code":"$ pre-commit run --all-files","section_id":"pre-commit-hooks"},{"id":"redis-8-local-development-ex0","language":"bash","code":"docker run -d --name redis -p 6379:6379 redis:8.4","section_id":"redis-8-local-development"},{"id":"redis-sentinel-ex0","language":"python","code":"from redisvl.index import SearchIndex, AsyncSearchIndex\n\n# Sync connection via Sentinel\n# Format: redis+sentinel://[username:password@]host1:port1,host2:port2/service_name[/db]\nindex = SearchIndex.from_yaml(\n    \"schema.yaml\",\n    redis_url=\"redis+sentinel://sentinel1:26379,sentinel2:26379/mymaster\"\n)\n\n# Async connection via Sentinel\nasync_index = AsyncSearchIndex.from_yaml(\n    \"schema.yaml\",\n    redis_url=\"redis+sentinel://sentinel1:26379,sentinel2:26379/mymaster\"\n)\n\n# With authentication and database selection\nindex = SearchIndex.from_yaml(\n    \"schema.yaml\",\n    redis_url=\"redis+sentinel://user:pass@sentinel1:26379,sentinel2:26379/mymaster/0\"\n)","section_id":"redis-sentinel"},{"id":"what-each-operation-needs-ex0","language":"text","code":"User <name> has no permissions to run the 'FT._LIST' command","section_id":"what-each-operation-needs"},{"id":"roles-built-from-read-and-write-ex0","language":"text","code":"RedisSearchError: Error while fetching llmcache index info:\nUser <name> has no permissions to run the 'FT.INFO' command","section_id":"roles-built-from-read-and-write"},{"id":"no-permissions-to-run-the-ftinfo-command-ex0","language":"python","code":"cache = SemanticCache(\n    name=\"llmcache\",\n    redis_url=\"redis://localhost:6379\",\n    create_index=False,\n)","section_id":"no-permissions-to-run-the-ftinfo-command"}]}
{"schema_version":2,"id":"develop/ai/redisvl/user_guide/use_cases","title":"Use Cases","url":"https://redis.io/docs/latest/develop/ai/redisvl/user_guide/use_cases/","summary":"","aliases":["/integrate/redisvl/user_guide/use_cases"],"tags":[],"last_updated":"2026-04-30T14:25:09+02:00","children":[],"page_type":"content","content_hash":"e86366c699463f6bf868ff6abb6aaad011e7f17b81fa40007e4488566988d355","sections":[{"id":"content","title":"Content","role":"content","text":"RedisVL powers a wide range of AI applications. Here's how to apply its features to common use cases.\n\n<div class=\"grid grid-cols-1 md:grid-cols-2 gap-4 my-6\">\n<div class=\"p-5 border border-redis-pen-300 rounded-lg\"><h3 class=\"mt-0 mb-2\">🧠 Agent Context</h3><p>Provide agents with the right information at the right time.</p>\n<ul>\n<li><strong>RAG</strong> — Retrieval-Augmented Generation with <a href=\"../getting_started/\">vector search</a> and <a href=\"../how_to_guides/advanced_queries/\">hybrid queries</a></li>\n<li><strong>Memory</strong> — Persistent <a href=\"../how_to_guides/message_history/\">message history</a> across sessions</li>\n<li><strong>Context Engineering</strong> — Combine <a href=\"../how_to_guides/complex_filtering/\">filtering</a>, <a href=\"../how_to_guides/rerankers/\">reranking</a>, and <a href=\"../how_to_guides/vectorizers/\">embeddings</a> to curate the optimal context window</li>\n</ul></div>\n<div class=\"p-5 border border-redis-pen-300 rounded-lg\"><h3 class=\"mt-0 mb-2\">⚡ Agent Optimization</h3><p>Reduce latency and cost for AI workloads.</p>\n<ul>\n<li><strong>Semantic Caching</strong> — Cache LLM responses by meaning with <a href=\"../how_to_guides/llmcache/\">SemanticCache</a></li>\n<li><strong>Embeddings Caching</strong> — Avoid redundant embedding calls with <a href=\"../how_to_guides/embeddings_cache/\">EmbeddingsCache</a></li>\n<li><strong>Semantic Routing</strong> — Route queries to the right handler with <a href=\"../how_to_guides/semantic_router/\">SemanticRouter</a></li>\n</ul></div>\n<div class=\"p-5 border border-redis-pen-300 rounded-lg\"><h3 class=\"mt-0 mb-2\">🔍 General Search</h3><p>Build search experiences that understand meaning, not just keywords.</p>\n<ul>\n<li><strong>Semantic Search</strong> — <a href=\"../getting_started/\">Vector queries</a> with <a href=\"../how_to_guides/complex_filtering/\">complex filtering</a></li>\n<li><strong>Hybrid Search</strong> — Combine keyword and vector search with <a href=\"../how_to_guides/advanced_queries/\">advanced query types</a></li>\n<li><strong>SQL Translation</strong> — Use familiar SQL syntax with <a href=\"../how_to_guides/sql_to_redis_queries/\">SQLQuery</a></li>\n</ul></div>\n<div class=\"p-5 border border-redis-pen-300 rounded-lg\"><h3 class=\"mt-0 mb-2\">🎯 Personalization & RecSys</h3><p>Drive engagement with personalized recommendations.</p>\n<ul>\n<li><strong>User Similarity</strong> — Find similar users or items using <a href=\"../getting_started/\">vector search</a></li>\n<li><strong>Real-Time Ranking</strong> — Combine vector similarity with <a href=\"../how_to_guides/complex_filtering/\">metadata filtering</a> and <a href=\"../how_to_guides/rerankers/\">reranking</a></li>\n<li><strong>Multi-Signal Matching</strong> — Search across multiple embedding fields with <a href=\"../how_to_guides/advanced_queries/\">MultiVectorQuery</a></li>\n</ul></div>\n</div>"}],"examples":[]}
{"schema_version":2,"id":"develop/ai/search-and-query/administration/configuration","title":"Configuration parameters","url":"https://redis.io/docs/latest/develop/ai/search-and-query/administration/configuration/","summary":"Redis Search can be tuned through multiple configuration parameters. Some of these parameters can only be set at load-time, while other parameters can be set either at load-time or at run-time.","aliases":["/develop/interact/search-and-query/administration/configuration","/develop/interact/search-and-query/basic-constructs/configuration-parameters"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-15T15:16:04-07:00","page_type":"content","content_hash":"8857c528e0f4d64f3252cc68764e1af742c04f3c1b9fc7c7bfe288d2337210b6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> As of Redis 8 in Redis Open Source (Redis 8), configuration parameters for Redis Search are now set in the following ways:\n> * At load time via your `redis.conf` file.\n> * At run time (where applicable) using the [`CONFIG SET`](https://redis.io/docs/latest/commands/config-set) command.\n>\n> Also, Redis 8 persists Redis Search configuration parameters just like any other configuration parameters (e.g., using the [`CONFIG REWRITE`](https://redis.io/docs/latest/commands/config-rewrite) command)."},{"id":"redis-search-configuration-parameters","title":"Redis Search configuration parameters","role":"content","text":"The following table summarizes which configuration parameters can be set at run-time, the versions in which each parameter is available, and compatibility with Redis Software and Redis Cloud.\n\nThe version columns indicate availability per Redis Query Engine module version (2.6, 2.8, and 2.10) and per Redis Open Source version (8.0, 8.2, 8.4, and 8.10). In those columns, &#x2705; means the parameter is available, &#x274c; means it is not available, and :warning: means it is deprecated (but still accepted) in that version.\n\n#### Table part 1 of 2, rows 1-10\n\n| Parameter name<br />(version < 8.0) | Parameter name<br />(version &#8805; 8.0) | 2.6 | 2.8 | 2.10 | 8.0 | 8.2 |\n| :------- | :------- | :----: | :----: | :----: | :----: | :----: |\n| BG_INDEX_SLEEP_GAP | [search-bg-index-sleep-gap](#search-bg-index-sleep-gap) | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| BG_INDEX_SLEEP_DURATION_US | [search-bg-index-sleep-duration-us](#search-bg-index-sleep-duration-us) | <span title=\"Not available\">&#x274c;</span> | <span title=\"Not available\">&#x274c;</span> | <span title=\"Not available\">&#x274c;</span> | <span title=\"Not available\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> |\n| CONCURRENT_WRITE_MODE | Removed in v2.10. | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Not available\">&#x274c;</span> | <span title=\"Not available\">&#x274c;</span> | <span title=\"Not available\">&#x274c;</span> |\n| CONN_PER_SHARD | [search-conn-per-shard](#search-conn-per-shard) | <span title=\"Not available\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| CURSOR_MAX_IDLE | [search-cursor-max-idle](#search-cursor-max-idle) | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| CURSOR_REPLY_THRESHOLD | [search-cursor-reply-threshold](#search-cursor-reply-threshold) | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| DEFAULT_DIALECT | [search-default-dialect](#search-default-dialect) | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| EXTLOAD | [search-ext-load](#search-ext-load) | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| FORK_GC_CLEAN_THRESHOLD | [search-fork-gc-clean-threshold](#search-fork-gc-clean-threshold) | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| FORK_GC_RETRY_INTERVAL | [search-fork-gc-retry-interval](#search-fork-gc-retry-interval) | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n#### Table part 1 of 2, rows 11-20\n\n| Parameter name<br />(version < 8.0) | Parameter name<br />(version &#8805; 8.0) | 2.6 | 2.8 | 2.10 | 8.0 | 8.2 |\n| :------- | :------- | :----: | :----: | :----: | :----: | :----: |\n| FORK_GC_RUN_INTERVAL | [search-fork-gc-run-interval](#search-fork-gc-run-interval) | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| FORKGC_SLEEP_BEFORE_EXIT | [search-fork-gc-sleep-before-exit](#search-fork-gc-sleep-before-exit) | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| FRISOINI | [search-friso-ini](#search-friso-ini) | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| [GC_POLICY](#gc_policy) | There is no matching `CONFIG` parameter. | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| GCSCANSIZE | [search-gc-scan-size](#search-gc-scan-size) | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| INDEX_CURSOR_LIMIT | [search-index-cursor-limit](#search-index-cursor-limit) | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| INDEX_THREADS | Removed in v2.10. | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Not available\">&#x274c;</span> | <span title=\"Not available\">&#x274c;</span> | <span title=\"Not available\">&#x274c;</span> |\n| MAXAGGREGATERESULTS | [search-max-aggregate-results](#search-max-aggregate-results) | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| MAXDOCTABLESIZE | [search-max-doctablesize](#search-max-doctablesize) | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| MAXEXPANSIONS | [search-max-expansions](#search-max-expansions) | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n#### Table part 1 of 2, rows 21-30\n\n| Parameter name<br />(version < 8.0) | Parameter name<br />(version &#8805; 8.0) | 2.6 | 2.8 | 2.10 | 8.0 | 8.2 |\n| :------- | :------- | :----: | :----: | :----: | :----: | :----: |\n| MAXPREFIXEXPANSIONS | [search-max-prefix-expansions](#search-max-prefix-expansions) | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| MAXSEARCHRESULTS | [search-max-search-results](#search-max-search-results) | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| _MAX_FOREGROUND_TIMEOUT_LIMIT | [search-_max-foreground-timeout-limit](#search-_max-foreground-timeout-limit) | <span title=\"Not available\">&#x274c;</span> | <span title=\"Not available\">&#x274c;</span> | <span title=\"Not available\">&#x274c;</span> | <span title=\"Not available\">&#x274c;</span> | <span title=\"Not available\">&#x274c;</span> |\n| MIN_OPERATION_WORKERS | [search-min-operation-workers](#search-min-operation-workers) | <span title=\"Not available\">&#x274c;</span> | <span title=\"Not available\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| MIN_PHONETIC_TERM_LEN | [search-min-phonetic-term-len](#search-min-phonetic-term-len) | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| MINPREFIX | [search-min-prefix](#search-min-prefix) | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| MINSTEMLEN | [search-min-stem-len](#search-min-stem-len) | <span title=\"Not available\">&#x274c;</span> | <span title=\"Not available\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| MULTI_TEXT_SLOP | [search-multi-text-slop](#search-multi-text-slop) | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| NO_MEM_POOLS | [search-no-mem-pools](#search-no-mem-pools) | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| NOGC | [search-no-gc](#search-no-gc) | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n#### Table part 1 of 2, rows 31-40\n\n| Parameter name<br />(version < 8.0) | Parameter name<br />(version &#8805; 8.0) | 2.6 | 2.8 | 2.10 | 8.0 | 8.2 |\n| :------- | :------- | :----: | :----: | :----: | :----: | :----: |\n| ON_TIMEOUT | [search-on-timeout](#search-on-timeout) | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| PARTIAL_INDEXED_DOCS | [search-partial-indexed-docs](#search-partial-indexed-docs) | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| [PARTITIONS](#partitions) | There is no matching `CONFIG` parameter. | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RAW_DOCID_ENCODING | [search-raw-docid-encoding](#search-raw-docid-encoding) | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| SEARCH_THREADS | [search-threads](#search-threads) | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| SEARCH_IO_THREADS | [search-io-threads](#search-io-threads) | <span title=\"Not available\">&#x274c;</span> | <span title=\"Not available\">&#x274c;</span> | <span title=\"Not available\">&#x274c;</span> | <span title=\"Not available\">&#x274c;</span> | <span title=\"Not available\">&#x274c;</span> |\n| TIERED_HNSW_BUFFER_LIMIT | [search-tiered-hnsw-buffer-limit](#search-tiered-hnsw-buffer-limit) | <span title=\"Not available\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| TIMEOUT | [search-timeout](#search-timeout) | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| TOPOLOGY_VALIDATION_TIMEOUT | [search-topology-validation-timeout](#search-topology-validation-timeout) | <span title=\"Not available\">&#x274c;</span> | <span title=\"Not available\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| UNION_ITERATOR_HEAP | [search-union-iterator-heap](#search-union-iterator-heap) | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n#### Table part 1 of 2, rows 41-50\n\n| Parameter name<br />(version < 8.0) | Parameter name<br />(version &#8805; 8.0) | 2.6 | 2.8 | 2.10 | 8.0 | 8.2 |\n| :------- | :------- | :----: | :----: | :----: | :----: | :----: |\n| [UPGRADE_INDEX](#upgrade_index) | There is no matching `CONFIG` parameter. | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| VSS_MAX_RESIZE | [search-vss-max-resize](#search-vss-max-resize) | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| WORKERS_PRIORITY_BIAS_THRESHOLD | [search-workers-priority-bias-threshold](#search-workers-priority-bias-threshold) | <span title=\"Not available\">&#x274c;</span> | <span title=\"Not available\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| WORKERS | [search-workers](#search-workers) | <span title=\"Not available\">&#x274c;</span> | <span title=\"Not available\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| OSS_GLOBAL_PASSWORD | Deprecated in v8.0.0. Replace with the `masterauth` password. | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| MT_MODE | Deprecated in v8.0.0. Use search-workers. | <span title=\"Not available\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| PRIVILEGED_THREADS_NUM | Deprecated in v8.0.0. Use search-workers-priority-bias-threshold. | <span title=\"Not available\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| WORKER_THREADS | Deprecated in v8.0.0. Use search-min-operation-workers. | <span title=\"Not available\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| SAFEMODE | Deprecated in v1.6.0. This is now the default setting. | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Not available\">&#x274c;</span> | <span title=\"Not available\">&#x274c;</span> | <span title=\"Not available\">&#x274c;</span> |\n| FORK_GC_CLEAN_NUMERIC_EMPTY_NODES | Deprecated in v8.0.0. | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n\n#### Table part 2 of 2, rows 1-10\n\n| Parameter name<br />(version < 8.0) | 8.4 | 8.10 | Run-time | Redis<br />Software | Redis<br />Cloud |\n| :------- | :----: | :----: | :------- | :------- | :------- |\n| BG_INDEX_SLEEP_GAP | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_large_square: | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Not supported\"><nobr>&#x274c; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> |\n| BG_INDEX_SLEEP_DURATION_US | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_large_square: |  |  |\n| CONCURRENT_WRITE_MODE | <span title=\"Not available\">&#x274c;</span> | <span title=\"Not available\">&#x274c;</span> |  |  |  |\n| CONN_PER_SHARD | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_check_mark: |  |  |\n| CURSOR_MAX_IDLE | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_check_mark: | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> |\n| CURSOR_REPLY_THRESHOLD | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_check_mark: |  |  |\n| DEFAULT_DIALECT | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_check_mark: | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> |\n| EXTLOAD | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_large_square: | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> |\n| FORK_GC_CLEAN_THRESHOLD | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_check_mark: | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> |\n| FORK_GC_RETRY_INTERVAL | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_check_mark: | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> |\n\n#### Table part 2 of 2, rows 11-20\n\n| Parameter name<br />(version < 8.0) | 8.4 | 8.10 | Run-time | Redis<br />Software | Redis<br />Cloud |\n| :------- | :----: | :----: | :------- | :------- | :------- |\n| FORK_GC_RUN_INTERVAL | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_check_mark: | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> |\n| FORKGC_SLEEP_BEFORE_EXIT | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_check_mark: |  |  |\n| FRISOINI | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_large_square: | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> |\n| [GC_POLICY](#gc_policy) | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_large_square: | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> |\n| GCSCANSIZE | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_large_square: | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> |\n| INDEX_CURSOR_LIMIT | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_large_square: |  |  |\n| INDEX_THREADS | <span title=\"Not available\">&#x274c;</span> | <span title=\"Not available\">&#x274c;</span> |  |  |  |\n| MAXAGGREGATERESULTS | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_check_mark: | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> |\n| MAXDOCTABLESIZE | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_large_square: | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> |\n| MAXEXPANSIONS | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_check_mark: |  |  |\n\n#### Table part 2 of 2, rows 21-30\n\n| Parameter name<br />(version < 8.0) | 8.4 | 8.10 | Run-time | Redis<br />Software | Redis<br />Cloud |\n| :------- | :----: | :----: | :------- | :------- | :------- |\n| MAXPREFIXEXPANSIONS | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_check_mark: | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> |\n| MAXSEARCHRESULTS | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_check_mark: | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> |\n| _MAX_FOREGROUND_TIMEOUT_LIMIT | <span title=\"Not available\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> | :white_check_mark: |  |  |\n| MIN_OPERATION_WORKERS | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_check_mark: |  |  |\n| MIN_PHONETIC_TERM_LEN | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_check_mark: |  |  |\n| MINPREFIX | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_check_mark: | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> |\n| MINSTEMLEN | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_check_mark: | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> |\n| MULTI_TEXT_SLOP | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_large_square: |  |  |\n| NO_MEM_POOLS | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_large_square: |  |  |\n| NOGC | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_large_square: | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> |\n\n#### Table part 2 of 2, rows 31-40\n\n| Parameter name<br />(version < 8.0) | 8.4 | 8.10 | Run-time | Redis<br />Software | Redis<br />Cloud |\n| :------- | :----: | :----: | :------- | :------- | :------- |\n| ON_TIMEOUT | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_check_mark: | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> |\n| PARTIAL_INDEXED_DOCS | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_large_square: | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> |\n| [PARTITIONS](#partitions) | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_large_square: | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> |\n| RAW_DOCID_ENCODING | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_large_square: |  |  |\n| SEARCH_THREADS | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_large_square: |  |  |\n| SEARCH_IO_THREADS | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_large_square: |  |  |\n| TIERED_HNSW_BUFFER_LIMIT | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_large_square: |  |  |\n| TIMEOUT | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_check_mark: | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> |\n| TOPOLOGY_VALIDATION_TIMEOUT | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_check_mark: |  |  |\n| UNION_ITERATOR_HEAP | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_check_mark: | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> |\n\n#### Table part 2 of 2, rows 41-50\n\n| Parameter name<br />(version < 8.0) | 8.4 | 8.10 | Run-time | Redis<br />Software | Redis<br />Cloud |\n| :------- | :----: | :----: | :------- | :------- | :------- |\n| [UPGRADE_INDEX](#upgrade_index) | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_large_square: | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> |\n| VSS_MAX_RESIZE | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_check_mark: |  |  |\n| WORKERS_PRIORITY_BIAS_THRESHOLD | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_large_square: |  |  |\n| WORKERS | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | :white_check_mark: |  |  |\n| OSS_GLOBAL_PASSWORD | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | :white_large_square: | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Not supported\"><nobr>&#x274c; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> |\n| MT_MODE | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | :white_large_square: |  |  |\n| PRIVILEGED_THREADS_NUM | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | :white_large_square: |  |  |\n| WORKER_THREADS | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | :white_large_square: |  |  |\n| SAFEMODE | <span title=\"Not available\">&#x274c;</span> | <span title=\"Not available\">&#x274c;</span> | :white_large_square: |  |  |\n| FORK_GC_CLEAN_NUMERIC_EMPTY_NODES | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | :white_large_square: |  |  |\n\n\n\n> [!NOTE]\n> Parameter names for Redis Open Source versions < 8.0, while deprecated, will still be supported in Redis 8.\n\n---"},{"id":"search-bg-index-sleep-gap","title":"search-bg-index-sleep-gap","role":"content","text":"The number of iterations to run while performing background indexing before `usleep(1)` (sleep for 1 microsecond) is called, ensuring that Redis can process other commands.\n\nType: integer\n\nValid range: `[1 .. 4294967295]`\n\nDefault: `100`"},{"id":"search-bg-index-sleep-duration-us","title":"search-bg-index-sleep-duration-us","role":"content","text":"Added in the 8.2 maintenance line (v8.2.12) and the 8.4 maintenance line (v8.4.9). Not available in Redis Open Source 8.0, and not present in the initial 8.2.0 or 8.4.0 releases.\n\nThe sleep duration (in microseconds) used during background indexing. During background indexing (triggered by `FT.CREATE` on existing keys), Redis periodically sleeps to allow the main thread to process commands. This parameter controls how long each sleep lasts. Works in conjunction with [`search-bg-index-sleep-gap`](#search-bg-index-sleep-gap), which controls how many iterations occur between sleeps.\n\nType: integer\n\nValid range: `[1 .. 999999]`\n\nDefault: `1`"},{"id":"search-conn-per-shard","title":"search-conn-per-shard","role":"content","text":"The number of connections to each shard in a cluster.\nIf `0`, the number of connections is set to `search-workers` + 1.\n\nType: integer\n\nValid range: `[0 .. 9,223,372,036,854,775,807]`\n\nDefault: `0`"},{"id":"search-cursor-max-idle","title":"search-cursor-max-idle","role":"content","text":"The maximum idle time (in ms) that can be set to the [cursor api](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/aggregations#cursor-api).\n\nType: integer\n\nValid range: `[0 .. 9,223,372,036,854,775,807]`\n\nDefault: `300000`"},{"id":"search-cursor-reply-threshold","title":"search-cursor-reply-threshold","role":"content","text":"The maximum number of replies to accumulate before triggering `_FT.CURSOR READ` on the shards.\n\nType: integer\n\nValid range: `[1 .. 9,223,372,036,854,775,807]`\n\nDefault: `1`"},{"id":"search-default-dialect","title":"search-default-dialect","role":"content","text":"The default\n[DIALECT](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/dialects)\nto be used by [`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create), [`FT.AGGREGATE`](https://redis.io/docs/latest/commands/ft.aggregate), [`FT.EXPLAIN`](https://redis.io/docs/latest/commands/ft.explain), [`FT.EXPLAINCLI`](https://redis.io/docs/latest/commands/ft.explaincli), and [`FT.SPELLCHECK`](https://redis.io/docs/latest/commands/ft.spellcheck).\nSee [Query dialects](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/dialects)\nfor more information.\n\nDefault: `1`"},{"id":"search-ext-load","title":"search-ext-load","role":"content","text":"If present, Redis will try to load an extension dynamic library from the specified file path.\nSee [Extensions](https://redis.io/docs/latest/develop/ai/search-and-query/administration/extensions) for details.\n\nType: string\n\nDefault: not set"},{"id":"search-fork-gc-clean-numeric-empty-nodes","title":"search-fork-gc-clean-numeric-empty-nodes","role":"content","text":"Clean empty nodes from numeric tree.\n\nType: boolean\n\nDefault: `TRUE`"},{"id":"search-fork-gc-clean-threshold","title":"search-fork-gc-clean-threshold","role":"content","text":"The fork GC will only start to clean when the number of uncleaned documents exceeds this threshold, otherwise it will skip this run.\n\nType: integer\n\nValid range: `[1 .. 9,223,372,036,854,775,807]`\n\nDefault: `100`"},{"id":"search-fork-gc-retry-interval","title":"search-fork-gc-retry-interval","role":"content","text":"Interval (in seconds) in which Redis will retry to run fork GC in case of a failure.\nThis setting can only be combined with the [`search-gc-policy`](#search-gc-policy) `FORK` setting.\n\nType: integer\n\nValid range: `[1 .. 9,223,372,036,854,775,807]`\n\nDefault: `5`"},{"id":"search-fork-gc-run-interval","title":"search-fork-gc-run-interval","role":"content","text":"Interval (in seconds) between two consecutive fork GC runs.\nThis setting can only be combined with the [`search-gc-policy`](#search-gc-policy) `FORK` setting.\n\nType: integer\n\nValid range: `[1 .. 9,223,372,036,854,775,807]`\n\nDefault: `30`"},{"id":"search-fork-gc-sleep-before-exit","title":"search-fork-gc-sleep-before-exit","role":"content","text":"The number of seconds for the fork GC to sleep before exit. This value should always be set to 0 except when testing.\n\nType: integer\n\nValid range: `[1 .. 9,223,372,036,854,775,807]`\n\nDefault: `0`"},{"id":"search-friso-ini","title":"search-friso-ini","role":"content","text":"If present, load the custom Chinese dictionary from the specified path. See [Using custom dictionaries](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/chinese#using-custom-dictionaries) for more details.\n\nType: string\n\nDefault: not set"},{"id":"gc_policy","title":"GC_POLICY","role":"content","text":"The garbage collection policy. The two supported policies are:\n* FORK: uses a forked thread for garbage collection (v1.4.1 and above). This is the default GC policy since v1.6.1 and is ideal for general purpose workloads.\n* LEGACY: uses a synchronous, in-process fork. This is ideal for read-heavy and append-heavy workloads with very few updates/deletes. Deprecated in v2.6.0.\n\nNote: When `GC_POLICY` is set to `FORK`, it can be combined with the `search-fork-gc-run-interval` and `search-fork-gc-retry-interval` settings. \n\nType: string\n\nValid values: `FORK` or `DEFAULT`\n\nDefault: `FORK`"},{"id":"search-gc-scan-size","title":"search-gc-scan-size","role":"content","text":"The bulk size of the internal GC used for cleaning up indexes.\n\nType: integer\n\nValid range: `[1 .. 9,223,372,036,854,775,807]`\n\nRedis Open Source default: `100`\n\nRedis Software default: `-1` (unlimited)\n\nRedis Cloud defaults:\n- Flexible & Annual: `-1` (unlimited)\n- Free & Fixed: `10000`"},{"id":"search-index-cursor-limit","title":"search-index-cursor-limit","role":"content","text":"Available in Redis Query Engine v2.6.24, v2.8.21, and v2.10.9 and their later maintenance releases, and in Redis Open Source 8.0 and later.\n\nThe maximum number of cursors that can be opened, per shard, at any given time. Cursors can be opened by the user via [`FT.AGGREGATE WITHCURSOR`](https://redis.io/docs/latest/commands/ft.aggregate). Cursors are also opened internally by  Redis Search for long-running queries. Once `INDEX_CURSOR_LIMIT` is reached, any further attempts to open a cursor will result in an error.\n\n> [!NOTE] Notes\n> * Caution should be used in modifying this parameter.  Every open cursor results in additional memory usage.\n> * Cursor usage should be regulated first by use of [`FT.CURSOR DEL`](https://redis.io/docs/latest/commands/ft.cursor-del) and/or [`MAXIDLE`](https://redis.io/docs/latest/commands/ft.aggregate) prior to modifying `INDEX_CURSOR_LIMIT`\n> * See [Cursor API](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/aggregations#cursor-api) for more details.\n\nType: integer\n\nDefault: `128`"},{"id":"search-max-aggregate-results","title":"search-max-aggregate-results","role":"content","text":"The maximum number of results to be returned by the `FT.AGGREGATE` command if `LIMIT` is used. It also caps the `offset` and `count` of the [`COLLECT` reducer's](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/aggregations#collect) `LIMIT`.\n\nType: integer\n\nValid range: `[1 .. 9,223,372,036,854,775,807]`\n\nRedis Open Source default: `-1` (unlimited)\n\nRedis Software default: `-1` (unlimited)\n\nRedis Cloud defaults:\n- Flexible & Annual: `-1` (unlimited)\n- Free & Fixed: `10000`"},{"id":"search-max-doctablesize","title":"search-max-doctablesize","role":"content","text":"The maximum size of the internal hash table used for storing documents. \nNote: this configuration option doesn't limit the number of documents that can be stored. It only affects the hash table internal array maximum size.\nDecreasing this property can decrease the memory overhead in cases where the index holds a small number of documents that are constantly updated.\n\nType: integer\n\nValid range: `[1 .. 18,446,744,073,709,551,615]`\n\nDefault: `1000000`"},{"id":"search-max-expansions","title":"search-max-expansions","role":"content","text":"This parameter is an alias for [search-max-prefix-expansions](#search-max-prefix-expansions)."},{"id":"search-max-prefix-expansions","title":"search-max-prefix-expansions","role":"content","text":"The maximum number of expansions allowed for query prefixes.\nThe maximum number of expansions allowed for query prefixes. Setting it too high can cause performance issues. If `search-max-prefix-expansions` is reached, the query will continue with the first acquired results. The configuration is applicable for all affix queries including prefix, suffix, and infix (contains) queries.\n\nType: integer\n\nValid range: `[1 .. 9,223,372,036,854,775,807]`\n\nDefault: `200`"},{"id":"search-max-search-results","title":"search-max-search-results","role":"content","text":"The maximum number of results to be returned by the `FT.SEARCH` command if `LIMIT` is used. Set it to `-1` to remove the limit.\n\nType: integer\n\nValid range: `[1 .. 9,223,372,036,854,775,807]`\n\nRedis Open Source default: `1000000`\n\nRedis Software default: `1000000`\n\nRedis Cloud defaults:\n- Flexible & Annual: `1000000`\n- Free & Fixed: `10000`"},{"id":"search-_max-foreground-timeout-limit","title":"search-_max-foreground-timeout-limit","role":"content","text":"Available in Redis Search 8.10 and later.\n\nSets the hard upper bound, in milliseconds, for the effective query timeout when\n[`search-workers`](#search-workers) is `0`. The effective timeout is either a query's\n`TIMEOUT` value or, when the query doesn't specify one, the value inherited from\n[`search-timeout`](#search-timeout).\n\nIf the effective timeout exceeds this limit, Redis caps it instead of rejecting the query.\nThis also applies to `TIMEOUT 0`, which normally means unlimited. RESP3 clients receive a\n`MaxTimeoutCapped` warning when the timeout is capped. The limit applies to\n[`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search),\n[`FT.AGGREGATE`](https://redis.io/docs/latest/commands/ft.aggregate),\n[`FT.CURSOR READ`](https://redis.io/docs/latest/commands/ft.cursor-read), and\n[`FT.HYBRID`](https://redis.io/docs/latest/commands/ft.hybrid).\n\nSet this parameter to `0` to disable the limit. The limit is also inactive when\n`search-workers` is greater than `0`.\n\nChanges to this parameter, `search-timeout`, and `search-workers` are accepted and logged\nat notice level regardless of their current combination. Redis evaluates the settings and\napplies the limit when each query runs.\n\nThe legacy `FT.CONFIG` name is `_MAX_FOREGROUND_TIMEOUT_LIMIT`.\n\nType: integer\n\nValid range: `[0 .. 9,223,372,036,854,775,807]`\n\nDefault: `60000`"},{"id":"search-min-operation-workers","title":"search-min-operation-workers","role":"content","text":"The number of worker threads to use for background tasks when the server is in an operation event.\n\nType: integer\n\nValid range: `[0 .. 8192]`\n\nDefault: `4`"},{"id":"search-min-phonetic-term-len","title":"search-min-phonetic-term-len","role":"content","text":"The minimum length of term to be considered for phonetic matching.\n\nType: integer\n\nValid range: `[1 .. 9,223,372,036,854,775,807]`\n\nDefault: `3`"},{"id":"search-min-prefix","title":"search-min-prefix","role":"content","text":"The minimum number of characters allowed for prefix queries (for example, hel*). Setting it to `1` can reduce performance.\n\nType: integer\n\nValid range: `[1 .. 9,223,372,036,854,775,807]`\n\nDefault: `2`"},{"id":"search-min-stem-len","title":"search-min-stem-len","role":"content","text":"The minimum word length to stem. Setting it lower than `4` can reduce performance.\n\nType: integer\n\nValid range: `[2 .. 4,294,967,295]`\n\nRedis Open Source default: `4`\n\nRedis Software and Redis Cloud default: `2`"},{"id":"search-multi-text-slop","title":"search-multi-text-slop","role":"content","text":"Set the delta that is used to increase positional offsets between array slots for multi text values.\nThis will allow you to control the level of separation between phrases in different array slots; related to the `SLOP` parameter of `FT.SEARCH` command.\n\nType: integer\n\nValid range: `[0 .. 4,294,967,295]`\n\nDefault: `100`"},{"id":"search-no-mem-pools","title":"search-no-mem-pools","role":"content","text":"Set Redis Search to run without memory pools.\n\nType: boolean\n\nDefault: `FALSE`"},{"id":"search-no-gc","title":"search-no-gc","role":"content","text":"If set to `TRUE`, garbage collection is disabled for all indexes.\n\nType: boolean\n\nDefault: `FALSE`"},{"id":"search-on-timeout","title":"search-on-timeout","role":"content","text":"The response policy for queries that exceed their effective timeout can be one of the following:\n\n* `RETURN`: this policy will return the top results accumulated by the query until it timed out.\n* `RETURN_STRICT`: like `RETURN`, returns the partial results accumulated when a query exceeds the timeout instead of failing, but strictly enforces the configured timeout as a hard deadline. This matters most in clustered deployments: with `RETURN`, the first shard to time out aborts the whole query early; with `RETURN_STRICT`, the coordinator keeps gathering results from all shards up to the deadline and then returns whatever accumulated at that point. Available in Redis Query Engine 8.10 and later.\n* `FAIL`: will return an error when the query exceeds the timeout value.\n\nType: string\n\nValid values: `RETURN`, `RETURN_STRICT`, `FAIL`\n\nDefault: `RETURN`"},{"id":"search-partial-indexed-docs","title":"search-partial-indexed-docs","role":"content","text":"Available in Redis Query Engine 2.0 and later.\n\nEnable/disable the Redis command filter. The filter optimizes partial updates of hashes\nand may avoid re-indexing the hash if changed fields are not part of the schema. \n\nThe Redis command filter will be executed upon each Redis command.  Though the filter is\noptimized, this will introduce a small increase in latency on all commands.  \nThis configuration is best used with partially indexed documents where the non-indexed fields are updated frequently.\n\nType: integer\n\nValid values: `0` (false), `1` (true)\n\nDefault: `0`"},{"id":"partitions","title":"PARTITIONS","role":"content","text":"The number of coordinator partitions to use in a clustered deployment (Redis Software,\nRedis Cloud, or Redis Open Source in cluster mode). This parameter is deprecated and\nimmutable: any value passed to it is ignored, and the number of partitions is always\ndetermined automatically.\n\nType: string\n\nValid values: `AUTO`\n\nDefault: `AUTO`"},{"id":"search-raw-docid-encoding","title":"search-raw-docid-encoding","role":"content","text":"Disable compression for DocID inverted indexes to boost CPU performance.\n\nType: boolean\n\nDefault: `FALSE`"},{"id":"search-threads","title":"search-threads","role":"content","text":"Sets the number of search threads in the coordinator thread pool.\n\nType: integer"},{"id":"search-io-threads","title":"search-io-threads","role":"content","text":"Sets the number of threads used in the coordinator to run I/O tasks with other shards.\n\nType: integer\n\nValid range: `[1 .. 256]`\n\nDefault: 1"},{"id":"search-tiered-hnsw-buffer-limit","title":"search-tiered-hnsw-buffer-limit","role":"content","text":"Used for setting the buffer limit threshold for vector tiered HNSW indexes. If Redis is using `WORKERS` for indexing, and the number of vectors waiting in the buffer to be indexed exceeds this limit, new vectors are inserted directly into HNSW.\n\nType: integer\n\nValid range: `[0 .. 9,223,372,036,854,775,807]`\n\nDefault: `1024`"},{"id":"search-timeout","title":"search-timeout","role":"content","text":"The default amount of time in milliseconds that a search query is allowed to run when the query doesn't specify its own `TIMEOUT`. A query-specific `TIMEOUT` overrides this value, so `search-timeout` is a default, not a ceiling. When [`search-_max-foreground-timeout-limit`](#search-_max-foreground-timeout-limit) is active, it provides the ceiling for the effective timeout.\n\nIf the effective timeout is exceeded, Redis returns the top results accumulated so far, or an error depending on the policy set with [`search-on-timeout`](#search-on-timeout). The timeout can be disabled by setting it to `0`.\n\n> [!NOTE] Notes\n> * `search-timeout` refers to query time only.\n> * Parsing the query is not counted towards `search-timeout`.\n> * If `search-timeout` was not reached during the search, finalizing operations such as loading document content or reducers continue.\n\nType: integer\n\nValue range: `[1 .. 9,223,372,036,854,775,807]`\n\nRedis Open Source default: `500`\n\nRedis Software default: `500`\n\nRedis Cloud defaults:\n- Flexible & Annual: `500`\n- Free & Fixed: `100`"},{"id":"search-topology-validation-timeout","title":"search-topology-validation-timeout","role":"content","text":"Sets the timeout in milliseconds for topology validation. After this timeout, any pending requests will be processed, even if the topology is not fully connected. A value of `0` means no timeout.\n\nType: integer\n\nValid range: `[1 .. 9,223,372,036,854,775,807]`\n\nDefault: `30000`"},{"id":"search-union-iterator-heap","title":"search-union-iterator-heap","role":"content","text":"The minimum number of iterators in a union at which the iterator will switch to a heap based implementation.\n\nType: integer\n\nValid range: `[1 .. 9,223,372,036,854,775,807]`\n\nDefault: `20`"},{"id":"upgrade_index","title":"UPGRADE_INDEX","role":"content","text":"There is no matching `CONFIG` parameter for `UPGRADE_INDEX`; it is available only as an `FT.CONFIG` parameter or as a module load-time argument.\n\nRelevant only when loading an v1.x RDB file. Specify the argument for upgrading the index.\nThis configuration setting is a special configuration option introduced to upgrade indexes from v1.x Redis Search versions, otherwise known as legacy indexes. This configuration option needs to be given for each legacy index, followed by the index name and all valid options for the index description (also referred to as the `ON` arguments for following hashes) as described on [FT.CREATE](https://redis.io/docs/latest/commands/ft.create) command page. \n\nType: string\n\nDefault: there is no default for index name, and the other arguments have the same defaults as with the [`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create) command.\n\n**Example**\n\n[code example]\n\n> [!NOTE] Notes\n> * If the RDB file does not contain a legacy index that's specified in the configuration, a warning message will be added to the log file, and loading will continue.\n> * If the RDB file contains a legacy index that wasn't specified in the configuration, loading will fail and the server won't start."},{"id":"search-vss-max-resize","title":"search-vss-max-resize","role":"content","text":"Available in Redis Query Engine 2.4.8 and later.\n\nThe maximum memory resize (in bytes) for vector indexes.\nThe maximum memory resize (in bytes) for vector indexes. This value will override default memory limits if you need to allow for a large [`BLOCK_SIZE`](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#create-a-vector-index).\n\nType: integer\n\nValid range: `[0 .. 4,294,967,295]`\n\nDefault: `0`"},{"id":"search-workers-priority-bias-threshold","title":"search-workers-priority-bias-threshold","role":"content","text":"The number of high priority tasks to be executed at any given time by the worker thread pool before executing low priority tasks. After this number of high priority tasks are being executed, the worker thread pool will execute high and low priority tasks alternately.\n\nType: integer\n\nValid range: `[1 .. 9,223,372,036,854,775,807]`\n\nDefault: `1`"},{"id":"search-workers","title":"search-workers","role":"content","text":"The number of worker threads to use for query processing and background tasks.\n\nType: integer\n\nValid range: `[0 .. 8192]`\n\nDefault: `0`"},{"id":"search-on-oom","title":"search-on-oom","role":"content","text":"Specifies the response policy for queries when the server's current memory usage exceeds the configured [maxmemory](https://redis.io/docs/latest/develop/reference/eviction/#maxmem) limit.\n\n* `IGNORE`: Execute the query regardless of current memory usage.\n* `RETURN`: In cluster mode, the query returns partial results from shards that did not exceed the memory limit. Shards that exceed the limit will not contribute results.\n* `FAIL`: Will return an error if memory usage exceeds the memory limit.\n\nType: string\n\nValid values: `IGNORE`, `RETURN`, `FAIL`\n\nDefault: `IGNORE`\n\n> [!NOTE] Notes\n> To prevent potential out-of-memory conditions, it is recommended that you set this parameter to FAIL or RETURN rather than IGNORE."},{"id":"set-configuration-parameters-at-module-load-time-deprecated","title":"Set configuration parameters at module load-time (deprecated)","role":"content","text":"These methods are deprecated beginning with Redis 8.\n\nSetting configuration parameters at load-time is done by appending arguments after the `--loadmodule` argument when starting a server from the command line, or after the `loadmodule` directive in a Redis config file. For example:\n\nIn [redis.conf](https://redis.io/docs/latest/operate/oss_and_stack/management/config):\n\n[code example]\n\nFrom the [Redis CLI](https://redis.io/docs/latest/develop/tools/cli), using the [MODULE LOAD](https://redis.io/docs/latest/commands/module-load) command:\n\n[code example]\n\nFrom the command line:\n\n[code example]"},{"id":"set-configuration-parameters-at-run-time-for-supported-parameters-deprecated","title":"Set configuration parameters at run-time (for supported parameters, deprecated)","role":"content","text":"These methods are deprecated beginning with Redis 8.\n\nRedis Search exposes the `FT.CONFIG` endpoint to allow for the setting and retrieval of configuration parameters at run-time.\n\nTo set the value of a configuration parameter at run-time (for supported parameters), simply run:\n\n[code example]\n\nSimilarly, you can retrieve current configuration parameter values using:\n\n[code example]\n\nValues set using [`FT.CONFIG SET`](https://redis.io/docs/latest/commands/ft.config-set) are not persisted after server restart."}],"examples":[{"id":"upgrade_index-ex0","language":"plaintext","code":"UPGRADE_INDEX idx PREFIX 1 tt LANGUAGE french LANGUAGE_FIELD MyLang SCORE 0.5 SCORE_FIELD MyScore\n    PAYLOAD_FIELD MyPayload UPGRADE_INDEX idx1","section_id":"upgrade_index"},{"id":"set-configuration-parameters-at-module-load-time-deprecated-ex0","language":"plaintext","code":"loadmodule ./redisearch.so [OPT VAL]...","section_id":"set-configuration-parameters-at-module-load-time-deprecated"},{"id":"set-configuration-parameters-at-module-load-time-deprecated-ex1","language":"plaintext","code":"127.0.0.6379> MODULE LOAD redisearch.so [OPT VAL]...","section_id":"set-configuration-parameters-at-module-load-time-deprecated"},{"id":"set-configuration-parameters-at-module-load-time-deprecated-ex2","language":"plaintext","code":"$ redis-server --loadmodule ./redisearch.so [OPT VAL]...","section_id":"set-configuration-parameters-at-module-load-time-deprecated"},{"id":"set-configuration-parameters-at-run-time-for-supported-parameters-deprecated-ex0","language":"sh","code":"FT.CONFIG SET OPT1 VAL1","section_id":"set-configuration-parameters-at-run-time-for-supported-parameters-deprecated"},{"id":"set-configuration-parameters-at-run-time-for-supported-parameters-deprecated-ex1","language":"sh","code":"FT.CONFIG GET OPT1\nFT.CONFIG GET *","section_id":"set-configuration-parameters-at-run-time-for-supported-parameters-deprecated"}]}
{"schema_version":2,"id":"develop/ai/search-and-query/administration/design","title":"Internal design","url":"https://redis.io/docs/latest/develop/ai/search-and-query/administration/design/","summary":"Details about design choices and implementations","aliases":["/develop/interact/search-and-query/administration/design"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:33:04+01:00","page_type":"content","content_hash":"8f974d7863c6da2e79666e2970d0631ab7a0c5d04992e31e42f7f74e08add61b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Open Source implements inverted indexes on top of Redis, but unlike previous implementations of Redis inverted indexes, it uses a custom data encoding that allows more memory and CPU efficient searches, and more advanced search features.\n\nThis document details some of the design choices and how these features are implemented."},{"id":"intro-redis-string-dma","title":"Intro: Redis String DMA","role":"content","text":"The main feature that this module takes advantage of is Redis Modules Strings Direct Memory Access (DMA).\n\nThis feature is simple, yet very powerful. It allows modules to allocate data on Redis string keys, and then get direct pointers to the data allocated by the keys without copying or serializing.\n\nThis allows very fast access to huge amounts of memory. From the module's perspective, the string value is exposed simply as `char *`, meaning it can be cast to any data structure.\n\nYou simply call `RedisModule_StringTruncate` to resize a memory chunk to the size needed. Then you call `RedisModule_StringDMA` to get direct access to the memory in that key. See [https://github.com/RedisLabs/RedisModulesSDK/blob/master/FUNCTIONS.md#redismodule_stringdma](https://github.com/RedisLabs/RedisModulesSDK/blob/master/FUNCTIONS.md#redismodule_stringdma)\n\nThis API is used in the module mainly to encode inverted indexes, and also for other auxiliary data structures.\n\nA generic \"Buffer\" implementation using DMA strings can be found in [buffer.c](https://github.com/RediSearch/RediSearch/blob/master/src/buffer/buffer.c). It automatically resizes the Redis string it uses as raw memory when the capacity needs to grow."},{"id":"inverted-index-encoding","title":"Inverted index encoding","role":"content","text":"An [inverted index](https://en.wikipedia.org/wiki/Inverted_index) is the data structure at the heart of all search engines. The idea is simple. For each word or search term, a list of all the documents it appears in is kept. Other data is kept as well, such as term frequency, and the offsets where a term appeared in the document. Offsets are used for exact match type searches, or for ranking of results.\n\nWhen a search is performed, either a single index is traversed, or the intersection or union of two or more indexes is traversed. Classic Redis implementations of search engines use sorted sets as inverted indexes. This works but has significant memory overhead, and it also does not allow for encoding of offsets, as explained above.\n\nRedis Open Source uses string DMA (see above) to efficiently encode inverted indexes. It combines [delta encoding](https://en.wikipedia.org/wiki/Delta_encoding) and [varint encoding](https://developers.google.com/protocol-buffers/docs/encoding#varints) to encode entries, minimizing space used for indexes, while keeping decompression and traversal efficient.\n\nFor each hit (document/word entry), the following items are encoded:\n\n* The document ID as a delta from the previous document.\n* The term frequency, factored by the document's rank (see below).\n* Flags, that can be used to filter only specific fields or other user-defined properties.\n* An offset vector of all the document offsets of the word.\n\n> [!NOTE]\n> Document IDs as entered by the user are converted to internal incremental document IDs, that allow delta encoding to be efficient and let the inverted indexes be sorted by document ID.\n\nThis allows for a single index hit entry to be encoded in as little as 6 bytes. Note: this is the best case. Depending on the number of occurrences of the word in the document, this can get much higher.\n\nTo optimize searches, two additional auxiliary data structures are kept in different DMA string keys:\n\n1. **Skip index**: a table of the index offset of 1/50th of the index entries. This allows faster lookup when intersecting inverted indexes, as the entire list doesn't need to be traversed.\n2. **Score index**: In simple single-word searches, there is no real need to traverse all the results, just the top N results the user is interested in. So an auxiliary index of the top 20 or so entries is stored for each term, which are used when applicable."},{"id":"document-and-result-ranking","title":"Document and result ranking","role":"content","text":"Each document entered to the engine has a [TF-IDF](https://en.wikipedia.org/wiki/Tf%E2%80%93idf) scoring of each word to rank the results.\n\nAs an optimization, each inverted index hit is encoded with `TF * Document_rank` as its score, and only IDF is applied during searches. This may change in the future.\n\nOn top of that, in the case of intersection queries, the minimal distance between the terms in the query is factored into the ranking. The closest the terms are to each other, the better the result.\n\nWhen searching, priority queue of the top N results requested is maintained, which are eventually returned, sorted by rank."},{"id":"index-ppecs-and-field-weights","title":"Index ppecs and field weights","role":"content","text":"When creating an \"index\" using [`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create), the user specifies the fields to be indexed and their respective weights. This can be used to give some document fields, like a title, more weight in ranking results.\n\nFor example:\n\n[code example]\n\nwill create an index on fields named title, body, and url, with scores of 10, 1, and 2 respectively.\n\nWhen documents are indexed, the weights are taken from the saved *index Spec* that is stored in a special Redis key, and only fields that appear in this spec are indexed."},{"id":"query-execution-engine","title":"Query execution engine","role":"content","text":"A chained-iterator based approach is used as part of query execution, which is similar to [Python generators](https://wiki.python.org/moin/Generators) in concept.\n\nIterators that yield index hits are chained together. Those can be:\n\n1. **Read Iterators**, reading hits one by one from an inverted index. For example, `hello`.\n2. **Intersect Iterators**, aggregating two or more iterators, yielding only their intersection points. For example, `hello AND world`.\n3. **Exact Intersect Iterators** - same as above, but yielding results only if the intersection is an exact phrase. For example,  `hello NEAR world`.\n4. **Union Iterators** - combining two or more iterators, and yielding a union of their hits. For example,  `hello OR world`.\n\nThese are combined based on the query as an execution plan that is evaluated lazily. For example:\n\n[code example]\n\nAll these iterators are lazily evaluated, entry by entry, with constant memory overhead.\n\nThe root iterator is read by the query execution engine and filtered for the top N results contained in it."},{"id":"numeric-filters","title":"Numeric filters","role":"content","text":"It's possible to define a field in the index schema as `NUMERIC`, meaning you will be able to limit search results only to those where the given value falls within a specific range. Filtering is done by adding `FILTER` predicates (more than one is supported) to your query. For example:\n\n[code example]\n\nThe filter syntax follows the ZRANGEBYSCORE semantics of Redis, meaning `-inf` and `+inf` are supported, and prepending `(` to a number means an exclusive range.\n\nAs of release 0.6, the implementation uses a multi-level range tree, saving ranges at multiple resolutions to allow efficient range scanning. Adding numeric filters can accelerate slow queries if the numeric range is small relative to the entire span of the filtered field. For example, a filter on dates focusing on a few days out of years of data can speed a heavy query by an order of magnitude."},{"id":"auto-complete-and-fuzzy-suggestions","title":"Auto-complete and fuzzy suggestions","role":"content","text":"Another important feature for searching and querying is auto-completion or suggestion. It allows you to create dictionaries of weighted terms, and then query them for completion suggestions to a given user prefix.  For example, if you put the term \"lcd tv\" into a dictionary, sending the prefix \"lc\" will return it as a result. The dictionary is modeled as a compressed trie (prefix tree) with weights, that is traversed to find the top suffixes of a prefix.\n\nRedis Open Source also allows for fuzzy suggestions, meaning you can get suggestions to user prefixes even if the user has a typo in the prefix. This is enabled using a Levenshtein automaton, allowing efficient searching of a dictionary for all terms within a maximal Levenshtein distance of a term or prefix. Suggestions are weighted based on both their original score and their distance from a prefix typed by the user. Only suggestions where the prefix is up to one Levenshtein distance away from the typed prefix are supported for performance reasons.\n\nHowever, since searching for fuzzy prefixes, especially very short ones, will traverse an enormous amount of suggestions (in fact, fuzzy suggestions for any single letter will traverse the entire dictionary!), it is recommended that you use this feature carefully, and only when considering the performance penalty it incurs. Since Redis is single threaded, blocking it for any amount of time means no other queries can be processed at that time.\n\nTo support unicode fuzzy matching, 16-bit runes are used inside the trie and not bytes. This increases memory consumption if the text is purely ASCII, but allows completion with the same level of support to all modern languages. This is done in the following manner:\n\n1. Assume all input to `FT.SUG*` commands is valid UTF-8.\n2. Input strings are converted to 32-bit Unicode, optionally normalizing, case-folding, and removing accents on the way. If the conversion fails it's because the input is not valid UTF-8.\n3. The 32-bit runes are trimmed to 16-bit runes using the lower 16 bits. These can be used for insertion, deletion, and search.\n4. The output of searches is converted back to UTF-8."}],"examples":[{"id":"index-ppecs-and-field-weights-ex0","language":"plaintext","code":"FT.CREATE my_index title 10.0 body 1.0 url 2.0","section_id":"index-ppecs-and-field-weights"},{"id":"query-execution-engine-ex0","language":"plaintext","code":"hello ==> read(\"hello\")\n\nhello world ==> intersect( read(\"hello\"), read(\"world\") )\n\n\"hello world\" ==> exact_intersect( read(\"hello\"), read(\"world\") )\n\n\"hello world\" foo ==> intersect(\n                            exact_intersect(\n                                read(\"hello\"),\n                                read(\"world\")\n                            ),\n                            read(\"foo\")\n                      )","section_id":"query-execution-engine"},{"id":"numeric-filters-ex0","language":"plaintext","code":"FT.SEARCH products \"hd tv\" FILTER price 100 (300","section_id":"numeric-filters"}]}
{"schema_version":2,"id":"develop/ai/search-and-query/administration/extensions","title":"Extend existing search and query features","url":"https://redis.io/docs/latest/develop/ai/search-and-query/administration/extensions/","summary":"Details about extensions for query expanders and scoring functions","aliases":["/develop/interact/search-and-query/administration/extensions","/interact/search-and-query/administration/extensions/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-04-07T16:24:18+01:00","page_type":"content","content_hash":"956322f18e40a8fdd648932df2cb1a49aef311c1357c17b44dd1697dd9696299","sections":[{"id":"overview","title":"Overview","role":"overview","text":"RediSearch supports an extension mechanism, much like Redis supports modules. The API is very minimal at the moment, and it does not yet support dynamic loading of extensions on a running server. Instead, extensions must be written in C (or a language that has an interface with C) and compiled into dynamic libraries that can be loaded at start up.\n\nThere are two kinds of extension APIs at the moment: \n\n1. **Query expanders**, whose role is to expand query tokens (i.e., stemmers).\n2. **Scoring functions**, whose role is to rank search results at query time."},{"id":"registering-and-loading-extensions","title":"Registering and loading extensions","role":"content","text":"Extensions should be compiled into dynamic library files (e.g., `.so` files), and loaded into the RediSearch module during initialization."},{"id":"compiling","title":"Compiling","role":"content","text":"Extensions should be compiled and linked as dynamic libraries. An example Makefile for an extension [can be found here](https://github.com/RediSearch/RediSearch/blob/master/tests/ctests/ext-example/Makefile). \n\n    That folder also contains an example extension that is used for testing and can be taken as a skeleton for implementing your own extension."},{"id":"loading","title":"Loading","role":"content","text":"Loading an extension is done by appending `EXTLOAD {path/to/ext.so}` after the `loadmodule` configuration directive when loading the RediSearch module. For example:\n\n    [code example]\n\n    This causes the RediSearch module to automatically load the extension and register its expanders and scorers."},{"id":"initializing-an-extension","title":"Initializing an extension","role":"content","text":"The entry point of an extension is a function with the signature:\n\n[code example]\n\nWhen loading an extension, RediSearch looks for this function and calls it. This function is responsible for registering and initializing the expanders and scorers. \n\nIt should return REDISEARCH_ERR on error or REDISEARCH_OK on success."},{"id":"example-init-function","title":"Example init function","role":"example","text":"[code example]"},{"id":"calling-your-custom-functions","title":"Calling your custom functions","role":"content","text":"When performing a query, you can use your scorers or expanders by specifying the SCORER or EXPANDER arguments with the given alias. For example:\n\n[code example]\n\n**NOTE**: Expander and scorer aliases are **case sensitive**."},{"id":"the-query-expander-api","title":"The query expander API","role":"content","text":"Only basic query expansion is supported, one token at a time. An expander can decide to expand any given token with as many tokens it wishes, which will be union-merged in query time.\n\nThe API for an expander is the following:\n\n[code example]"},{"id":"rsqueryexpanderctx","title":"RSQueryExpanderCtx","role":"content","text":"`RSQueryExpanderCtx` is a context that contains private data of the extension, and a callback method to expand the query. It is defined as:\n\n[code example]"},{"id":"rstoken","title":"RSToken","role":"content","text":"`RSToken` represents a single query token to be expanded, and is defined as:\n\n[code example]"},{"id":"the-scoring-function-api","title":"The scoring function API","role":"content","text":"For the final ranking, the scoring function analyzes each document retrieved by the query, taking into account not only the terms that triggered the document's retrieval but also metadata like its prior score, length, and so on.\n\nSince the scoring function is evaluated for each document, potentially millions of times, and since\nredis is single threaded, it is important that it works as fast as possible and be heavily optimized. \n\nA scoring function is applied to each potential result for each document and is implemented with the following signature:\n\n[code example]\n\n`RSScoringFunctionCtx` is a context that implements some helper methods. \n\n`RSIndexResult` is the result information containing the document id, frequency, terms, and offsets. \n\n`RSDocumentMetadata` is an object holding global information about the document, such as its presumptive score. \n\n`minScore` is the minimal score that will yield a result that is relevant to the search. It can be used to stop processing midway or before or even before it starts.\n\nThe return value of the function is a `double` representing the final score of the result. \nReturning 0 causes the result to be counted, but if there are results with a score greater than 0, they will appear above it. \nTo completely filter out a result and not count it in the totals, the scorer should return the special value `RS_SCORE_FILTEROUT`, which is internally set to negative infinity, or -1/0."},{"id":"rsscoringfunctionctx","title":"RSScoringFunctionCtx","role":"content","text":"This is an object containing the following members:\n\n* `void *privdata`: a pointer to an object set by the extension on initialization time.\n* `RSPayload payload*`: A Payload object set either by the query expander or the client.\n* `int GetSlop(RSIndexResult *res)*`: A callback method that yields the total minimal distance between the query terms. This can be used to prefer results where the slop is smaller and the terms are nearer to each other."},{"id":"rsindexresult","title":"RSIndexResult","role":"content","text":"This is an object holding the information about the current result in the index, which is an aggregate of all the terms that resulted in the current document being considered a valid result. See `redisearch.h` for details."},{"id":"rsdocumentmetadata","title":"RSDocumentMetadata","role":"content","text":"This is an object describing global information, unrelated to the current query, about the document being evaluated by the scoring function."},{"id":"example-query-expander","title":"Example query expander","role":"example","text":"This example query expander expands each token with the term foo:\n\n[code example]"},{"id":"example-scoring-function","title":"Example scoring function","role":"example","text":"This is an actual scoring function, which calculates TF-IDF for the document, multiplies it by the document score, and divides it by the slop:\n\n[code example]"}],"examples":[{"id":"loading-ex0","language":"sh","code":"$ redis-server --loadmodule ./redisearch.so EXTLOAD ./ext/my_extension.so","section_id":"loading"},{"id":"initializing-an-extension-ex0","language":"c","code":"int RS_ExtensionInit(RSExtensionCtx *ctx);","section_id":"initializing-an-extension"},{"id":"example-init-function-ex0","language":"c","code":"#include <redisearch.h> //must be in the include path\n\nint RS_ExtensionInit(RSExtensionCtx *ctx) {\n\n  /* Register  a scoring function with an alias my_scorer and no special private data and free function */\n  if (ctx->RegisterScoringFunction(\"my_scorer\", MyCustomScorer, NULL, NULL) == REDISEARCH_ERR) {\n    return REDISEARCH_ERR;\n  }\n\n  /* Register a query expander  */\n  if (ctx->RegisterQueryExpander(\"my_expander\", MyExpander, NULL, NULL) ==\n      REDISEARCH_ERR) {\n    return REDISEARCH_ERR;\n  }\n\n  return REDISEARCH_OK;\n}","section_id":"example-init-function"},{"id":"calling-your-custom-functions-ex0","language":"plaintext","code":"FT.SEARCH my_index \"foo bar\" EXPANDER my_expander SCORER my_scorer","section_id":"calling-your-custom-functions"},{"id":"the-query-expander-api-ex0","language":"c","code":"#include <redisearch.h> //must be in the include path\n\nvoid MyQueryExpander(RSQueryExpanderCtx *ctx, RSToken *token) {\n    ...\n}","section_id":"the-query-expander-api"},{"id":"rsqueryexpanderctx-ex0","language":"c","code":"typedef struct RSQueryExpanderCtx {\n\n  /* Opaque query object used internally by the engine, and should not be accessed */\n  struct RSQuery *query;\n\n  /* Opaque query node object used internally by the engine, and should not be accessed */\n  struct RSQueryNode **currentNode;\n\n  /* Private data of the extension, set on extension initialization */\n  void *privdata;\n\n  /* The language of the query, defaults to \"english\" */\n  const char *language;\n\n  /* ExpandToken allows the user to add an expansion of the token in the query, that will be\n   * union-merged with the given token in query time. str is the expanded string, len is its length,\n   * and flags is a 32 bit flag mask that can be used by the extension to set private information on\n   * the token */\n  void (*ExpandToken)(struct RSQueryExpanderCtx *ctx, const char *str, size_t len,\n                      RSTokenFlags flags);\n\n  /* SetPayload allows the query expander to set GLOBAL payload on the query (not unique per token)\n   */\n  void (*SetPayload)(struct RSQueryExpanderCtx *ctx, RSPayload payload);\n\n} RSQueryExpanderCtx;","section_id":"rsqueryexpanderctx"},{"id":"rstoken-ex0","language":"c","code":"/* A token in the query. The expanders receive query tokens and can expand the query with more query\n * tokens */\ntypedef struct {\n  /* The token string - which may or may not be NULL terminated */\n  const char *str;\n  /* The token length */\n  size_t len;\n  \n  /* 1 if the token is the result of query expansion */\n  uint8_t expanded:1;\n\n  /* Extension specific token flags that can be examined later by the scoring function */\n  RSTokenFlags flags;\n} RSToken;","section_id":"rstoken"},{"id":"the-scoring-function-api-ex0","language":"c","code":"double MyScoringFunction(RSScoringFunctionCtx *ctx, RSIndexResult *res,\n                                    RSDocumentMetadata *dmd, double minScore);","section_id":"the-scoring-function-api"},{"id":"example-query-expander-ex0","language":"c","code":"#include <redisearch.h> //must be in the include path\n\nvoid DummyExpander(RSQueryExpanderCtx *ctx, RSToken *token) {\n    ctx->ExpandToken(ctx, strdup(\"foo\"), strlen(\"foo\"), 0x1337);  \n}","section_id":"example-query-expander"},{"id":"example-scoring-function-ex0","language":"c","code":"#include <redisearch.h> //must be in the include path\n\ndouble TFIDFScorer(RSScoringFunctionCtx *ctx, RSIndexResult *h, RSDocumentMetadata *dmd,\n                   double minScore) {\n  // no need to evaluate documents with score 0 \n  if (dmd->score == 0) return 0;\n\n  // calculate sum(tf-idf) for each term in the result\n  double tfidf = 0;\n  for (int i = 0; i < h->numRecords; i++) {\n    // take the term frequency and multiply by the term IDF, add that to the total\n    tfidf += (float)h->records[i].freq * (h->records[i].term ? h->records[i].term->idf : 0);\n  }\n  // normalize by the maximal frequency of any term in the document   \n  tfidf /=  (double)dmd->maxFreq;\n\n  // multiply by the document score (between 0 and 1)\n  tfidf *= dmd->score;\n\n  // no need to factor the slop if tfidf is already below minimal score\n  if (tfidf < minScore) {\n    return 0;\n  }\n\n  // get the slop and divide the result by it, making sure we prefer results with closer terms\n  tfidf /= (double)ctx->GetSlop(h);\n  \n  return tfidf;\n}","section_id":"example-scoring-function"}]}
{"schema_version":2,"id":"develop/ai/search-and-query/administration/gc","title":"Garbage collection","url":"https://redis.io/docs/latest/develop/ai/search-and-query/administration/gc/","summary":"Details about garbage collection","aliases":["/develop/interact/search-and-query/administration/gc"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2025-06-26T15:11:30-07:00","page_type":"content","content_hash":"09deb8bf3e12a618a9cea6897967bf99fcfd523c8bb3d97f0a0cb5cf0502bf99","sections":[{"id":"the-need-for-garbage-collection","title":"The need for garbage collection","role":"overview","text":"* When documents are deleted by the user, Redis only marks them as deleted in the global document table rather than deleting them outright. This is done for efficiency. Depending on the length of a document, deletion can be a long operation.\n* This means that it is no longer the case that an internal numeric id is assigned to a deleted document. When the index is traversed, a check is made for deletion.\n* All inverted index entries belonging to deleted document ids are garbage.\n* Updating a document is basically the same as deleting it and then adding it again with a new incremental internal ID. No diffing is performed, and the indexes are appended, so the IDs remain incremental, and the updates fast.\n\nAll of the above means that if there are a lot of updates and deletes, a large portion of our inverted index will become garbage, both slowing things down and consuming unnecessary memory.\n\nYou want to optimize the index, but you also don't want to disturb normal operation. This means that optimization or garbage collection should be a background process that's non-intrusive. It only needs to be faster than the deletion rate over a sufficient period of time so that you don't create more garbage than you can collect."},{"id":"garbage-collecting-a-single-term-index","title":"Garbage collecting a single-term index","role":"content","text":"A single-term inverted index is an array of blocks, each of which contains an encoded list of records; e.g., a document id delta plus other data depending on the index encoding scheme. When some of these records refer to deleted documents this is called garbage. \n\nThe algorithm is simple: \n\n0. Create a reader and writer for each block.\n1. Read each block's records one by one.\n2. If no record is invalid, do nothing.\n3. When a garbage record is found, the reader is advanced, but not the writer.\n4. When at least one garbage record is found, the next records are encoded to the writer, recalculating the deltas.\n\nPseudo code:\n\n[code example]"},{"id":"gc-on-numeric-indexes","title":"GC on numeric indexes","role":"content","text":"Numeric indexes are a tree of inverted indexes with a special encoding of (docId delta, value). This means the same algorithm can be applied to them, only traversing each inverted index object in the tree."},{"id":"fork-gc","title":"FORK GC","role":"content","text":"Information about FORK GC can be found in this [blog](https://redislabs.com/blog/increased-garbage-collection-performance-redisearch-1-4-1/).\n\nSince v1.6, the FORK GC is the default GC policy and was proven very efficient both in cleaning the index and not reducing query and indexing performance, even for very write-internsive use cases."}],"examples":[{"id":"garbage-collecting-a-single-term-index-ex0","language":"plaintext","code":"foreach index_block as block:\n   \n   reader = new_reader(block)\n   writer = new_write(block)\n   garbage = 0\n   while not reader.end():\n        record = reader.decode_next()\n        if record.is_valid():\n            if garbage != 0:\n                # Write the record at the writer's tip with a newly calculated delta\n                writer.write_record(record)\n            else:\n                writer.advance(record.length)\n        else:\n            garbage += record.length","section_id":"garbage-collecting-a-single-term-index"}]}
{"schema_version":2,"id":"develop/ai/search-and-query/administration","title":"Administration","url":"https://redis.io/docs/latest/develop/ai/search-and-query/administration/","summary":"Redis Search Administration","aliases":["/develop/interact/search-and-query/administration","/develop/interact/search-and-query/administration/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-03-09T06:38:54-07:00","children":[{"id":"develop/ai/search-and-query/administration/configuration","summary":"Redis Search can be tuned through multiple configuration parameters. Some of these parameters can only be set at load-time, while other parameters can be set either at load-time or at run-time.","title":"Configuration parameters","url":"https://redis.io/docs/latest/develop/ai/search-and-query/administration/configuration/"},{"id":"develop/ai/search-and-query/administration/design","summary":"Details about design choices and implementations","title":"Internal design","url":"https://redis.io/docs/latest/develop/ai/search-and-query/administration/design/"},{"id":"develop/ai/search-and-query/administration/overview","summary":"Technical overview of the search and query features of Redis Open Source","title":"Technical overview","url":"https://redis.io/docs/latest/develop/ai/search-and-query/administration/overview/"},{"id":"develop/ai/search-and-query/administration/gc","summary":"Details about garbage collection","title":"Garbage collection","url":"https://redis.io/docs/latest/develop/ai/search-and-query/administration/gc/"},{"id":"develop/ai/search-and-query/administration/extensions","summary":"Details about extensions for query expanders and scoring functions","title":"Extend existing search and query features","url":"https://redis.io/docs/latest/develop/ai/search-and-query/administration/extensions/"},{"id":"develop/ai/search-and-query/administration/indexing","summary":"This document describes how documents are added to the index.","title":"Document Indexing","url":"https://redis.io/docs/latest/develop/ai/search-and-query/administration/indexing/"}],"page_type":"index","sections":[],"examples":[]}
{"schema_version":2,"id":"develop/ai/search-and-query/administration/indexing","title":"Document Indexing","url":"https://redis.io/docs/latest/develop/ai/search-and-query/administration/indexing/","summary":"This document describes how documents are added to the index.","aliases":["/develop/interact/search-and-query/administration/indexing"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2025-06-26T15:11:30-07:00","page_type":"content","content_hash":"d6e3b5ec10fa57756ddef1ea8a9469641c5a102b611e2bcfbeb48e7cefaa5156","sections":[{"id":"components","title":"Components","role":"overview","text":"* `Document` - contains the actual document and its fields.\n* `RSAddDocumentCtx` - the per-document state that is used while it\n  is being indexed. The state is discarded after the indexing is complete.\n* `ForwardIndex` - contains terms found in the document. The forward index\n  is used to write the `InvertedIndex`.\n* `InvertedIndex` - an index that maps terms to occurrences within applicable\n  documents."},{"id":"architecture","title":"Architecture","role":"content","text":"The indexing process begins by creating a new `RSAddDocumentCtx` and adding a\ndocument to it. Internally, this is divided into several steps.\n\n\n1. Submission\n\n   A `DocumentContext` is created, and is associated with a document (as received)\n   from input. The submission process will also perform some preliminary caching.\n\n2. Preprocessing\n\n   After a document has been submitted, it is preprocessed. Preprocessing performs\n   stateless processing on all document input fields. For text fields, this\n   means tokenizing the document and creating a forward index. The preprocesors\n   will store this information in per-field variables within the `AddDocumentCtx`.\n   This computed result is then written to the persistent index later on during\n   the indexing phase.\n\n   If the document is sufficiently large, the preprocessing is done in a separate\n   thread, which allows concurrent preprocessing and also avoids blocking other\n   threads. If the document is smaller, the preprocessing is done within the main\n   thread, avoiding the overhead of additional context switching.\n   The `SELF_EXC_THRESHOLD` macro contains the threshold for 'sufficiently large'.\n\n   After the document is preprocessed, it is submitted to be indexed.\n\n3. Indexing\n\n   Indexing proper consists of committing the precomputed results of the\n   preprocessing phase. It is done in a single thread, and is in the form\n   of a queue.\n\n   Because documents must be written to the index in the exact order of their\n   document ID assignment, and because the indexing process must also yield to other potential\n   indexing processes, you may end up in a situation where document IDs are written\n   to the index out-of-order. To solve that, the order in which documents\n   are actually written must be well-defined. If there is only one thread writing\n   documents, then this thread will not need to worry about out-of-order IDs\n   while writing.\n\n   Having a single background thread also helps optimize in several areas, as\n   will be seen later on. The basic idea is that when there are a lot of\n   documents queued for the indexing thread, the indexing thread may treat them\n   as batch commands, greatly reducing the number of locks/unlocks of the GIL\n   and the number of times term keys need to be opened and closed.\n\n4. Skipping already indexed documents\n\n   The phases below may operate on more than one document at a time. When a document\n   is fully indexed, it is marked as done. When the thread iterates over the queue\n   it will only perform processing/indexing on items not yet marked as done.\n\n5. Term merging\n\n   Term merging, or forward index merging, is done when there is more than a\n   single document in the queue. The forward index of each document in the queue\n   is scanned, and a larger, master forward index is constructed in its place.\n   Each entry in the forward index contains a reference to the origin document\n   as well as the normal offset/score/frequency information.\n   \n   Creating a master forward index avoids opening common term keys more than once per\n   document.\n\n   If there is only one document within the queue, a master forward index\n   is not created.\n\n   Note that the internal type of the master forward index is not actually\n   `ForwardIndex`.\n\n6. Document ID assignment\n   \n   At this point, the GIL is locked and every document in the queue is assigned\n   a document ID. The assignment is done immediately before writing to the index\n   so as to reduce the number of times the GIL is locked; thus, the GIL is\n   locked only once, right before the index is written.\n\n7. Writing to Indexes\n\n   With the GIL being locked, any pending index data is written to the indexes.\n   This usually involves opening one or more Redis keys, and writing/copying\n   computed data into those keys.\n\n   After this is done, the reply for the given document is sent, and the\n   `AddDocumentCtx` freed."}],"examples":[]}
{"schema_version":2,"id":"develop/ai/search-and-query/administration/overview","title":"Technical overview","url":"https://redis.io/docs/latest/develop/ai/search-and-query/administration/overview/","summary":"Technical overview of the search and query features of Redis Open Source","aliases":["/develop/interact/search-and-query/administration/overview"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:33:04+01:00","page_type":"content","content_hash":"ac8808224b9d30f3a8fb3f2ea54fbb50703b9107a5053d8359b194df638fbce5","sections":[{"id":"abstract","title":"Abstract","role":"overview","text":"Redis Search is a powerful text search and secondary indexing engine that is built on top of Redis Open Source. \n\nUnlike other Redis search libraries, it does not use the internal data structures of Redis such as sorted sets. Using its own highly optimized data structures and algorithms, it allows for advanced search features, high performance, and a low memory footprint. It can perform simple text searches, as well as complex structured queries, filtering by numeric properties and geographical distances.\n\nRedis Search supports continuous indexing with no performance degradation, maintaining concurrent loads of querying and indexing. This makes it ideal for searching frequently updated databases without the need for batch indexing and service interrupts. \n\nThe Enterprise version of Redis Search supports scaling the search engine across many servers, allowing it to easily grow to billions of documents on hundreds of servers. \n\nAll of this is done while taking advantage of Redis's robust architecture and infrastructure. Using Redis's protocol, replication, persistence, and clustering, Redis Search delivers a powerful yet simple to manage and maintain search and indexing engine that can be used as a standalone database, or to augment existing Redis databases with advanced powerful indexing capabilities.\n\n---"},{"id":"main-features","title":"Main features","role":"content","text":"* Full-Text indexing of multiple fields in a document, including:\n    * Exact phrase matching.\n    * Stemming in many languages.\n    * Chinese tokenization support.\n    * Prefix queries.\n    * Optional, negative, and union queries.\n* Distributed search on billions of documents.\n* Numeric property indexing.\n* Geographical indexing and radius filters.\n* Incremental indexing without performance loss.\n* A structured query language for advanced queries:\n    * Unions and intersections\n    * Optional and negative queries\n    * Tag filtering\n    * Prefix matching\n* A powerful autocomplete engine with fuzzy matching.\n* Multiple scoring models and sorting by values.\n* Concurrent, low-latency insertion and updates of documents.\n* Concurrent searches allowing long-running queries without blocking Redis.\n* An extension mechanism allowing custom scoring models and query extension.\n* Support for indexing existing Hash objects in Redis databases.\n\n---"},{"id":"indexing-documents","title":"Indexing documents","role":"content","text":"Redis needs to know how to index documents in order to search effectively. A document may have several fields, each with its own weight. For example, a title is usually more important than the text itself. The engine can also use numeric or geographical fields for filtering. Hence, the first step is to create the index definition, which tells Redis how to treat the documents that will be added. For example, to define an index of products, indexing their title, description, brand, and price fields, the index creation would look like:\n\n[code example]\n\nWhen a document is added to this index, as in the following example, each field of the document is broken into its terms (tokenization), and indexed by marking the index for each of the terms in the index. As a result, the product is added immediately to the index and can now be found in future searches.\n\n[code example]\n\nThis tells Redis to take the document, break each field into its terms (tokenization), and index it by marking the index for each of the terms in the index as contained in this document. Thus, the product is added immediately to the index and can now be found in future searches."},{"id":"searching","title":"Searching","role":"content","text":"Now that the products have been added to our index, searching is very simple:\n\n[code example]\n\nThis will tell Redis to intersect the lists of documents for each term and return all documents containing the three terms. Of course, more complex queries can be performed, and the full syntax of the query language is detailed below."},{"id":"data-structures","title":"Data structures","role":"content","text":"Redis uses its own custom data structures and uses Redis' native structures only for storing the actual document content (using Hash objects).\n\nUsing specialized data structures allows faster searches and more memory effective storage of index records, utilizing compression techniques like delta encoding. \n\nThese are the data structures Redis uses under the hood:"},{"id":"index-and-document-metadata","title":"Index and document metadata","role":"content","text":"For each search _index_, there is a root data structure containing the schema, statistics, etc., but most importantly, compact metadata about each document indexed. \n\nInternally, inside the index, Redis uses delta encoded lists of numeric, incremental, 32-bit document ids. This means that the user given keys or ids for documents, need to be replaced with the internal ids on indexing, and back to the original ids on search. \n\nFor that, Redis saves two tables, mapping the two kinds of ids in two ways (one table uses a compact trie, the other is simply an array where the internal document ID is the array index). On top of that, for each document, its user given presumptive score is stored, along with some status bits and any optional payload attached to the document by the user. \n\nAccessing the document metadata table is an order of magnitude faster than accessing the hash object where the document is actually saved, so scoring functions that need to access metadata about the document can operate fast enough."},{"id":"inverted-index","title":"Inverted index","role":"content","text":"For each term appearing in at least one document, an inverted index is kept, which is basically a list of all the documents where this term appears. The list is compressed using delta coding, and the document ids are always incrementing. \n\nFor example, when a user indexes the documents \"foo\", \"bar\", and \"baz\", they are assigned incrementing ids, e.g., `1025, 1045, 1080`. When encoding them into the index,  only the first ID is encoded, followed by the deltas between each entry and the previous one, e.g., `1025, 20, 35`. \n\nUsing variable-width encoding, one byte is used to express numbers under 255, two bytes for numbers between 256 and 16,383, and so on. This can compress the index by up to 75%. \n\nOn top of the IDs, the frequency of each term in each document, a bit mask representing the fields in which the term appeared in the document, and a list of the positions in which the term appeared is saved.\n\nThe structure of the default search record is as follows. Usually, all the entries are one byte long:\n\n[code example]\n\nOptionally, you can choose not to save any one of those attributes besides the ID, degrading the features available to the engine."},{"id":"numeric-index","title":"Numeric index","role":"content","text":"Numeric properties are indexed in a special data structure that enables filtering by numeric ranges in an efficient way. One could view a numeric value as a term operating just like an inverted index. For example, all the products with the price $100 are in a specific list, which is intersected with the rest of the query. See [query execution engine](https://redis.io/docs/latest/develop/ai/search-and-query/administration/design#query-execution-engine) for more information. \n\nHowever, in order to filter by a range of prices, you would have to intersect the query with all the distinct prices within that range, or perform a union query. If the range has many values in it, this becomes highly inefficient. \n\nTo avoid this, numeric entries are grouped, with close values together, in a single range node. These nodes are stored in a binary range tree, which allows the engine to select the relevant nodes and union them together. Each entry in a range node contains a document Id and the actual numeric value for that document. To further optimize, the tree uses an adaptive algorithm to try to merge as many nodes as possible within the same range node."},{"id":"tag-index","title":"Tag index","role":"content","text":"Tag indexes are similar to full-text indexes, but use simpler tokenization and encoding in the index. The values in these fields cannot be accessed by general field-less search and can be used only with a special syntax.\n\nThe main differences between tag fields and full-text fields are:\n\n1. The tokenization is simpler. The user can determine a separator (defaults to a comma) for multiple tags. Whitespace trimming is done only at the end of tags. Thus, tags can contain spaces, punctuation marks, accents, etc. The only two transformations that are performed are lower-casing (for latin languages only as of now) and whitespace trimming.\n\n2. Tags cannot be found from a general full-text search. If a document has a field called *tags* with the values *foo* and *bar*, searching for foo or bar without a special tag modifier (see below) will not return this document.\n\n3. The index is much simpler and more compressed. Only the document IDs are stored in the index, usually resulting in 1-2 bytes per index entry."},{"id":"geo-index","title":"Geo index","role":"content","text":"Geo indexes utilize Redis's own geo-indexing capabilities. At query time, the geographical part of the query (a radius filter) is sent to Redis, returning only the ids of documents that are within that radius. Longitude and latitude should be passed as a string `lon,lat`. For example, `1.23,4.56`."},{"id":"autocomplete","title":"Autocomplete","role":"content","text":"The autocomplete engine (see below for a fuller description) uses a compact trie or prefix tree to encode terms and search them by prefix."},{"id":"query-language","title":"Query language","role":"content","text":"Simple syntax is supported for complex queries that can be combined together to express complex filtering and matching rules. The query is a text string in the [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) request that is parsed using a complex query processor.\n\n* Multi-word phrases are lists of tokens, e.g., `foo bar baz`, and imply intersection (logical AND) of the terms.\n* Exact phrases are wrapped in quotes, e.g `\"hello world\"`.\n* OR unions (e.g., `word1 OR word2`), are expressed with a pipe (`|`) character. For example, `hello|hallo|shalom|hola`.\n* NOT negation (e.g., `word1 NOT word2`) of expressions or sub-queries use the dash (`-`) character. For example, `hello -world`. \n* Prefix matches (all terms starting with a prefix) are expressed with a `*` following a 2-letter or longer prefix.\n* Selection of specific fields using the syntax `@field:hello world`.\n* Numeric Range matches on numeric fields with the syntax `@field:[{min} {max}]`.\n* Geo radius matches on geo fields with the syntax `@field:[{lon} {lat} {radius} {m|km|mi|ft}]`\n* Tag field filters with the syntax `@field:{tag | tag | ...}`. See the [full documentation on tag fields](https://redis.io/docs/latest/develop/ai/search-and-query/query#tag-filters).\n* Optional terms or clauses: `foo ~bar` means bar is optional but documents with bar in them will rank higher."},{"id":"complex-query-examples","title":"Complex query examples","role":"content","text":"Expressions can be combined together to express complex rules. For example, given a database of products, where each entity has the fields `title`, `brand`, `tags` and `price`, expressing a generic search would be simply:\n\n[code example]\n\nThis would return documents containing these terms in any field. Limiting the search to specific fields (title only in this case) is expressed as:\n\n[code example]\n\nNumeric filters can be combined to filter by price within a given price range:\n\n[code example]\n\nMultiple text fields can be accessed in different query clauses. For example, to select products of multiple brands:\n\n[code example]\n\nTag fields can be used to index multi-term properties without actual full-text tokenization:\n\n[code example]\n\nAnd negative clauses can also be added to filter out plasma and CRT TVs:\n\n[code example]"},{"id":"scoring-model","title":"Scoring model","role":"content","text":"Redis 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 (see below). Scoring functions are specified by adding the `SCORER {scorer_name}` argument to a search request.\n\nIf you prefer a custom scoring function, it is possible to add more functions using the [extension API](https://redis.io/docs/latest/develop/ai/search-and-query/administration/extensions).\n\nThese are the pre-bundled scoring functions available in Redis:\n\n* **TFIDF** (default)\n\n    Basic [TF-IDF scoring](https://en.wikipedia.org/wiki/Tf%E2%80%93idf) with document score and proximity boosting factored in.\n\n* **TFIDF.DOCNORM**\n* \n    Identical to the default TFIDF scorer, with one important distinction:\n\n* **BM25STD (default)**\n\n    A variation on the basic `TFIDF` scorer, see [this Wikipedia article for more info](https://en.wikipedia.org/wiki/Okapi_BM25).\n\n    The relevance score for each document is multiplied by the presumptive document score and a penalty is applied based on slop as in `TFIDF`.\n\n    > [!NOTE]\n    > The `BM25` scorer was renamed `BM25STD` in Redis Open Source 8.4. `BM25` is deprecated.\n\n* **BM25STD.NORM**\n\n    A variation of `BM25STD`, where the scores are normalized by the minimum and maximum score.\n\n* **BM25STD.TANH**\n\n    A variation of `BM25STD.NORM`, where the scores are normalised by the 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.\n\n* **DISMAX**\n\n    A simple scorer that sums up the frequencies of the matched terms. In the case of union clauses, it will give the maximum value of those matches.\n\n* **DOCSCORE**\n\n    A scoring function that just returns the presumptive score of the document without applying any calculations to it. Since document scores can be updated, this can be useful if you'd like to use an external score and nothing further."},{"id":"sortable-fields","title":"Sortable fields","role":"content","text":"It is possible to bypass the scoring function mechanism and order search results by the value of different document properties (fields) directly, even if the sorting field is not used by the query. For example, you can search for first name and sort by the last name. \n\nWhen creating the index with [`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create), you can declare `TEXT`, `TAG`, `NUMERIC`, and `GEO` properties as `SORTABLE`. When a property is sortable, you can later decide to order the results by its values with relatively low latency. When a property is not sortable, it can still be sorted by its values, but may increase latency. For example, the following schema:\n\n[code example]\n\nWould allow the following query:\n\n[code example]"},{"id":"result-highlighting-and-summarization","title":"Result highlighting and summarization","role":"returns","text":"Redis uses advanced algorithms for highlighting and summarizing, which enable only the relevant portions of a document to appear in response to a search query. This feature allows users to immediately understand the relevance of a document to their search criteria, typically highlighting the matching terms in bold text. The syntax is as follows:\n\n[code example]\n\nSummarization will fragment the text into smaller sized snippets. Each snippet will contain the found term(s) and some additional surrounding context.\n\nHighlighting will highlight the found term and its variants with a user-defined tag. This may be used to display the matched text in a different typeface using a markup language, or to otherwise make the text appear differently."},{"id":"autocomplete-1","title":"Autocomplete","role":"content","text":"Another important feature for Redis Open Source is its autocomplete engine. This allows users to create dictionaries of weighted terms, and then query them for completion suggestions to a given user prefix. Completions can have payloads, which are user-provided pieces of data that can be used for display. For example, completing the names of users, it is possible to add extra metadata about users to be displayed.\n\nFor example, if a user starts to put the term \"lcd tv\" into a dictionary, sending the prefix \"lc\" will return the full term as a result. The dictionary is modeled as a compact trie (prefix tree) with weights, which is traversed to find the top suffixes of a prefix.\n\nRedis also allows fuzzy suggestions, meaning you can get suggestions to prefixes even if the user makes a typo in their prefix. This is enabled using a Levenshtein automaton, allowing efficient searching of the dictionary for all terms within a maximal Levenshtein distance of a term or prefix. Suggestions are then weighted based on both their original score and their distance from the prefix typed by the user. \n\nHowever, searching for fuzzy prefixes (especially very short ones) will traverse an enormous number of suggestions. In fact, fuzzy suggestions for any single letter will traverse the entire dictionary, so the recommendation is to use this feature carefully and in full consideration of the performance penalty it incurs. \n\nThe autocomplete engine supports Unicode, allowing for fuzzy matches in non-latin languages as well.\n\nSee the [autocomplete page](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/autocomplete) for more information and examples."},{"id":"search-engine-internals","title":"Search engine internals","role":"content","text":""},{"id":"the-redis-module-api","title":"The Redis module API","role":"content","text":"Redis Search is implemented using the [Redis module API](https://redis.io/docs/latest/develop/reference/modules) and is loaded into Redis as an extension module at start-up.\n\nRedis modules make it possible to extend Redis's core functionality, implementing new Redis commands, data structures, and capabilities with similar performance to native core Redis itself. Redis modules are dynamic libraries that can be loaded into Redis at start-up or loaded at run-time using the [`MODULE LOAD`](https://redis.io/docs/latest/commands/module-load) command. Redis exports a C API, in the form of a single C header file called `redismodule.h`. \n\nWhile the logic of Redis Search and its algorithms are mostly independent, and it could be ported quite easily to run as a stand-alone server, it still takes advantage of Redis as a robust infrastructure for a database server. Building on top of Redis means that, by default, modules are afforded:\n\n* A high performance network protocol server\n* Robust replication\n* Highly durable persistence as snapshots of transaction logs\n* Cluster mode"},{"id":"query-execution-engine","title":"Query execution engine","role":"content","text":"Redis uses a high-performance flexible query processing engine that can evaluate very complex queries in real time. \n\nThe above query language is compiled into an execution plan that consists of a tree of index iterators or filters. These can be any of:\n\n* Numeric filter\n* Tag filter\n* Text filter\n* Geo filter\n* Intersection operation (combining 2 or more filters)\n* Union operation (combining 2 or more filters)\n* NOT operation (negating the results of an underlying filter)\n* Optional operation (wrapping an underlying filter in an optional matching filter)\n\nThe query parser generates a tree of these filters. For example, a multi-word search would be resolved into an intersect operation of multiple text filters, each traversing an inverted index of a different term. Simple optimizations such as removing redundant layers in the tree are applied.\n\nEach of the filters in the resulting tree evaluates one match at a time. This means that at any given moment, the query processor is busy evaluating and scoring one matching document. This means that very little memory allocation is done at run-time, resulting in higher performance. \n\nThe resulting matching documents are then fed to a post-processing chain of result processors that are responsible for scoring them, extracting the top-N results, loading the documents from storage, and sending them to the client. That chain is dynamic as well, which adapts based on the attributes of the query. For example, a query that only needs to return document ids will not include a stage for loading documents from storage."},{"id":"concurrent-updates-and-searches","title":"Concurrent updates and searches","role":"content","text":"While Redis Search is extremely fast and uses highly optimized data structures and algorithms, it was facing the same problem with regards to concurrency. Depending on the size of your data set and the cardinality of search queries, queries can take anywhere between a few microseconds to hundreds of milliseconds, or even seconds in extreme cases. When that happens, the entire Redis server process is blocked. \n\nThink, for example, of a full-text query intersecting the terms \"hello\" and \"world\", each with a million entries, and a half-million common intersection points. To perform that query in a millisecond, Redis would have to scan, intersect, and rank each result in one nanosecond, [which is impossible with current hardware](https://gist.github.com/jboner/2841832). The same goes for indexing a 1,000 word document. It blocks Redis entirely for the duration of the query.\n\nRedis Search uses the Redis Module API's concurrency features to avoid stalling the server for long periods of time. The idea is simple - while Redis itself is single-threaded, a module can run many threads, and any one of those threads can acquire the **Global Lock** when it needs to access Redis data, operate on it, and release it. \n\nRedis cannot be queried in parallel, as only one thread can acquire the lock, including the Redis main thread, but care is taken to make sure that a long-running query will give other queries time to run by yielding this lock from time to time.\n\nThe following design principles were adopted to allow concurrency:\n\n1. Redis Search has a thread pool for running concurrent search queries. \n\n2. When a search request arrives, it is passed to the handler, parsed on the main thread, and then a request object is passed to the thread pool via a queue.\n\n3. The thread pool runs a query processing function in its own thread.\n\n4. The function locks the Redis Global lock and starts executing the query.\n\n5. Since the search execution is basically an iterator running in a cycle, the elapsed time is sampled every several iterations (sampling on each iteration would slow things down as it has a cost of its own).\n\n6. If enough time has elapsed, the query processor releases the Global Lock and immediately tries to acquire it again. When the lock is released, the kernel will schedule another thread to run - be it Redis's main thread, or another query thread.\n\n7. When the lock is acquired again, all Redis resources that were held before releasing the lock are re-opened (keys might have been deleted while the thread has been sleeping) and work resumes from the previous state. \n\nThus the operating system's scheduler makes sure all query threads get CPU time to run. While one is running the rest wait idly, but since execution is yielded about 5,000 times a second, it creates the effect of concurrency. Fast queries will finish in one go without yielding execution, slow ones will take many iterations to finish, but will allow other queries to run concurrently."},{"id":"index-garbage-collection","title":"Index garbage collection","role":"content","text":"Redis Search is optimized for high write, update, and delete throughput. One of the main design choices dictated by this goal is that deleting and updating documents do not actually delete anything from the index: \n\n1. Deletion simply marks the document deleted in a global document metadata table using a single bit. \n2. Updating, on the other hand, marks a document as deleted, assigns it a new incremental document ID, and re-indexes the document under a new ID, without performing a comparison of the change. \n\nWhat this means, is that index entries belonging to deleted documents are not removed from the index, and can be seen as garbage. Over time, an index with many deletes and updates will contain mostly garbage, both slowing things down and consuming unnecessary memory. \n\nTo overcome this, Redis Search employs a background garbage collection (GC) mechanism. During normal operation of the index, a special thread randomly samples indexes, traverses them, and looks for garbage. Index sections containing garbage are cleaned and memory is reclaimed. This is done in a non- intrusive way, operating on very small amounts of data per scan, and utilizing Redis's concurrency mechanism (see above) to avoid interrupting searches and indexing. The algorithm also tries to adapt to the state of the index, increasing the GC's frequency if the index contains a lot of garbage, and decreasing it if it doesn't, to the point of hardly scanning if the index does not contain garbage."},{"id":"extension-model","title":"Extension model","role":"content","text":"RedisSearch supports an extension mechanism, much like Redis supports modules. The API is very minimal at the moment, and it does not yet support dynamic loading of extensions at run-time. Instead, extensions must be written in C (or a language that has an interface with C) and compiled into dynamic libraries that will be loaded at start-up.\n\nThere are two kinds of extension APIs at the moment: \n\n1. **Query expanders**, whose role is to expand query tokens (i.e., stemmers).\n2. **Scoring functions**, whose role is to rank search results at query time.\n\nExtensions are compiled into dynamic libraries and loaded into Redis Search on initialization of the module. The mechanism is based on the code of Redis's own module system, albeit far simpler.\n\n---"},{"id":"scalable-distributed-search","title":"Scalable distributed search","role":"content","text":"While Redis Search is very fast and memory efficient, if an index is big enough, at some point it will be too slow or consume too much memory. It must then be scaled out and partitioned over several machines, each of which will hold a small part of the complete search index.\n\nTraditional clusters map different keys to different shards to achieve this. However, with search indexes this approach is not practical. If each word’s index was mapped to a different shard, it would be necessary to intersect records from different servers for multi-term queries. \n\nThe way to address this challenge is to employ a technique called index partitioning, which is very simple at its core:\n\n* The index is split across many machines/partitions by document ID.\n* Every partition has a complete index of all the documents mapped to it.\n* All shards are queried concurrently and the results from each shard are merged into a single result.\n\nTo facilitate this, a new component called a coordinator is added to the cluster. When searching for documents, the coordinator receives the query and sends it to N partitions, each holding a sub-index of 1/N documents. Since we’re only interested in the top K results of all partitions, each partition returns just its own top K results. Then, the N lists of K elements are merged and the top K elements are extracted from the merged list."}],"examples":[{"id":"indexing-documents-ex0","language":"plaintext","code":"FT.CREATE idx PREFIX 1 doc: SCHEMA \n    title TEXT WEIGHT 5\n    description TEXT \n    brand TEXT \n    PRICE numeric","section_id":"indexing-documents"},{"id":"indexing-documents-ex1","language":"plaintext","code":"HSET doc:1 \n    title \"Acme 42 inch LCD TV\"\n    description \"42 inch brand new Full-HD tv with smart tv capabilities\"\n    brand \"Acme\"\n    price 300","section_id":"indexing-documents"},{"id":"searching-ex0","language":"plaintext","code":"FT.SEARCH idx \"full hd tv\"","section_id":"searching"},{"id":"inverted-index-ex0","language":"plaintext","code":"+----------------+------------+------------------+-------------+------------------------+\n|  docId_delta   |  frequency | field mask       | offsets len | offset, offset, ....   |\n|  (1-4 bytes)   | (1-2 bytes)| (1-16 bytes)     |  (1-2 bytes)| (1-2 bytes per offset) |\n+----------------+------------+------------------+-------------+------------------------+","section_id":"inverted-index"},{"id":"complex-query-examples-ex0","language":"plaintext","code":"lcd tv","section_id":"complex-query-examples"},{"id":"complex-query-examples-ex1","language":"plaintext","code":"@title:(lcd tv)","section_id":"complex-query-examples"},{"id":"complex-query-examples-ex2","language":"plaintext","code":"@title:(lcd tv) \n    @price:[100 500.2]","section_id":"complex-query-examples"},{"id":"complex-query-examples-ex3","language":"plaintext","code":"@title:(lcd tv)\n    @brand:(sony | samsung | lg)\n    @price:[100 500.2]","section_id":"complex-query-examples"},{"id":"complex-query-examples-ex4","language":"plaintext","code":"@title:(lcd tv) \n    @brand:(sony | samsung | lg) \n    @tags:{42 inch | smart tv} \n    @price:[100 500.2]","section_id":"complex-query-examples"},{"id":"complex-query-examples-ex5","language":"plaintext","code":"@title:(lcd tv) \n    @brand:(sony | samsung | lg) \n    @tags:{42 inch | smart tv} \n    @price:[100 500.2]\n\n    -@tags:{plasma | crt}","section_id":"complex-query-examples"},{"id":"sortable-fields-ex0","language":"plaintext","code":"FT.CREATE users SCHEMA first_name TEXT last_name TEXT SORTABLE age NUMERIC SORTABLE","section_id":"sortable-fields"},{"id":"sortable-fields-ex1","language":"plaintext","code":"FT.SEARCH users \"john lennon\" SORTBY age DESC","section_id":"sortable-fields"},{"id":"result-highlighting-and-summarization-ex0","language":"plaintext","code":"FT.SEARCH ...\n    SUMMARIZE [FIELDS {num} {field}] [FRAGS {numFrags}] [LEN {fragLen}] [SEPARATOR {separator}]\n    HIGHLIGHT [FIELDS {num} {field}] [TAGS {openTag} {closeTag}]","section_id":"result-highlighting-and-summarization"}]}
{"schema_version":2,"id":"develop/ai/search-and-query/advanced-concepts/aggregations","title":"Aggregations","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/aggregations/","summary":"Groupings, projections, and aggregation functions","aliases":["/interact/search-and-query/search/aggregations/","/develop/interact/search-and-query/advanced-concepts/aggregations","/interact/search-and-query/advanced-concepts/aggregations/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-15T15:16:04-07:00","page_type":"content","content_hash":"dbf37ee0b3f158e4ca280eeb58b3ecaf389a745a6b498620214559360f491e78","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Aggregations are a way to process the results of a search query. Aggregation allows you to group, sort, and transform your result data, and to extract analytic insights from it. Much like aggregation queries in other databases and search engines, they can be used to create analytics reports, or perform [faceted search](https://en.wikipedia.org/wiki/Faceted_search) style queries.\n\nFor example, indexing a web-server's logs, you can create a report for unique users by hour, country, or any other breakdown. Or you can create different reports for errors, warnings, etc."},{"id":"core-concepts","title":"Core concepts","role":"content","text":"The basic idea of an aggregate query is this:\n\n* Perform a search query, filtering for records you wish to process.\n* Build a pipeline of operations that transform the results by zero or more sequences of:\n  * **Group and reduce**: grouping by fields in the results, and applying reducer functions to each group.\n  * **Sort**: sort the results based on one or more fields.\n  * **Apply transformations**: apply mathematical and string functions on fields in the pipeline, optionally creating new fields or replacing existing ones.\n  * **Limit**: limit the result, regardless of how the result is sorted.\n  * **Filter**: filter the results (post-query) based on predicates relating to its values.\n\nThe pipeline is dynamic and re-entrant, and every operation can be repeated. For example, you can group by property X, sort the top 100 results by group size, then group by property Y and sort the results by some other property, then apply a transformation on the output.\n\nFigure 1: Aggregation Pipeline Example\n\n<img src=\"../../img/pipeline.png\">"},{"id":"aggregate-request-format","title":"Aggregate request format","role":"content","text":"The aggregate request's syntax is defined as follows:\n\n[code example]"},{"id":"parameters-in-detail","title":"Parameters in detail","role":"parameters","text":"Parameters that can take a variable number of arguments are expressed in the\nform of `param {nargs} {property_1... property_N}`. The first argument to the\nparameter is the number of arguments following the parameter. This allows\nRedis to avoid a parsing ambiguity in case one of your arguments has the\nname of another parameter. For example, to sort by first name, last name, and\ncountry, one would specify `SORTBY 6 firstName ASC lastName DESC country ASC`.\n\n* **index_name**: The index the query is executed against.\n\n* **query_string**: The base filtering query that retrieves the documents. It follows the exact same syntax as the search query, including filters, unions, not, optional, etc.\n\n* **LOAD {nargs} {property} ...** : Load document fields from the document HASH objects. This should be avoided as a general rule of thumb. Fields needed for aggregations should be stored as SORTABLE (and optionally UNF to avoid any normalization), where they are available to the aggregation pipeline with very low latency. LOAD hurts the performance of aggregate queries considerably since every processed record needs to execute the equivalent of HMGET against a Redis key, which when executed over millions of keys, amounts to very high processing times.\nThe document ID can be loaded using `@__key`.\n\n* **GROUPBY {nargs} {property} ...** : Group the results in the pipeline based on one or more properties. Each group should have at least one reducer (See below), a function that handles the group entries, either counting them or performing multiple aggregate operations (see below).\n\n* **REDUCE {func} {nargs} {arg} ... [AS {name}]**: Reduce the matching results in each group into a single record, using a reduction function. For example, COUNT will count the number of records in the group. See the Reducers section below for more details on available reducers.\n\n    The reducers can have their own property names using the `AS {name}` optional argument. If a name is not given, the resulting name will be the name of the reduce function and the group properties. For example, if a name is not given to COUNT_DISTINCT by property `@foo`, the resulting name will be `count_distinct(@foo)`.\n\n* **SORTBY {nargs} {property} {ASC|DESC} [MAX {num}]**: Sort the pipeline up until the point of SORTBY, using a list of properties. By default, sorting is ascending, but `ASC` or `DESC ` can be added for each property. `nargs` is the number of sorting parameters, including ASC and DESC. for example: `SORTBY 4 @foo ASC @bar DESC`.\n\n    `MAX` is used to optimized sorting, by sorting only for the n-largest elements. Although it is not connected to `LIMIT`, you usually need just `SORTBY … MAX` for common queries.\n\n* **APPLY {expr} AS {name}**: Apply a one-to-one transformation on one or more properties, and either store the result as a new property down the pipeline, or replace any property using this transformation. `expr` is an expression that can be used to perform arithmetic operations on numeric properties, or functions that can be applied on properties depending on their types (see below), or any combination thereof. For example: `APPLY \"sqrt(@foo)/log(@bar) + 5\" AS baz` will evaluate this expression dynamically for each record in the pipeline and store the result as a new property called baz, that can be referenced by further APPLY / SORTBY / GROUPBY / REDUCE operations down the pipeline.\n\n* **LIMIT {offset} {num}**. Limit the number of results to return just `num` results starting at index `offset` (zero based). AS mentioned above, it is much more efficient to use `SORTBY … MAX` if you are interested in just limiting the output of a sort operation.\n\n     However, limit can be used to limit results without sorting, or for paging the n-largest results as determined by `SORTBY MAX`. For example, getting results 50-100 of the top 100 results is most efficiently expressed as `SORTBY 1 @foo MAX 100 LIMIT 50 50`. Removing the MAX from SORTBY will result in the pipeline sorting all the records and then paging over results 50-100.\n\n* **FILTER {expr}**. Filter the results using predicate expressions relating to values in each result. The expressions are applied post-query and relate to the current state of the pipeline. See FILTER Expressions below for full details.\n\n* **PARAMS {nargs} {name} {value}**. Define one or more value parameters. Each parameter has a name and a value. Parameters can be referenced in the query string by a `$`, followed by the parameter name, e.g., `$user`, and each such reference in the search query to a parameter name is substituted by the corresponding parameter value. For example, with parameter definition `PARAMS 4 lon 29.69465 lat 34.95126`, the expression `@loc:[$lon $lat 10 km]` would be evaluated to `@loc:[29.69465 34.95126 10 km]`. Parameters cannot be referenced in the query string where concrete values are not allowed, such as in field names, e.g., `@loc`\n*"},{"id":"example","title":"Example","role":"example","text":"A log of visits to a website might look like the following, each record of which has the following fields/properties:\n\n* **url** (text, sortable)\n* **timestamp** (numeric, sortable) - Unix timestamp of visit entry.\n* **country** (tag, sortable)\n* **user_id** (text, sortable, not indexed)"},{"id":"example-1-unique-users-by-hour-ordered-chronologically","title":"Example 1: unique users by hour, ordered chronologically.","role":"example","text":"The first step is to determine the index name and the filtering query. A filter query of `*` means \"get all records\":\n\n[code example]\n\nNext, group the results by hour. The data contains visit times as unix timestamps in second resolution, so you'll need to extract the hour component of the timestamp. To do so, add an APPLY step that strips the sub-hour information from the timestamp and stores is as a new property, `hour`:\n\n[code example]\n\nNext, group the results by hour and count the distinct user ids in each hour. This is done by a GROUPBY/REDUCE step:\n\n[code example]\n\nNext, sort the results by hour, ascending:\n\n[code example]\n\nAnd as a final step, format the hour as a human readable timestamp. This is done by calling the transformation function `timefmt` that formats Unix timestamps. You can specify a format to be passed to the system's `strftime` function ([see documentation](https://pubs.opengroup.org/onlinepubs/9699919799/functions/strftime.html)), but not specifying one is equivalent to specifying `%FT%TZ` to `strftime`.\n\n[code example]"},{"id":"example-2-sort-visits-to-a-specific-url-by-day-and-country","title":"Example 2: Sort visits to a specific URL by day and country:","role":"example","text":"The next example filters by the url, transforms the timestamp to its day part, and groups by the day and country, counting the number of visits per group, sorting by day ascending and country descending.\n\n[code example]"},{"id":"groupby-reducers","title":"GROUPBY reducers","role":"content","text":"`GROUPBY` works similarly to SQL `GROUP BY` clauses, and creates groups of results based on one or more properties in each record. For each group, Redis returns the group keys, or the values common to all records in the group, and the results of zero or more `REDUCE` clauses.\n\nEach `GROUPBY` step in the pipeline may be accompanied by zero or more `REDUCE` clauses. Reducers apply an accumulation function to each record in the group and reduces them into a single record representing the group. When the processing is complete, all the records upstream of the `GROUPBY` step emit their reduced record.\n\nFor example, the simplest reducer is COUNT, which simply counts the number of records in each group.\n\nIf multiple `REDUCE` clauses exist for a single `GROUPBY` step, each reducer works independently on each result and writes its final output once. Each reducer may have its own alias determined using the `AS` optional parameter. If `AS` is not specified, the alias is the reduce function and its parameters, e.g. `count_distinct(foo,bar)`."},{"id":"supported-groupby-reducers","title":"Supported GROUPBY reducers","role":"compatibility","text":"#### COUNT\n\n**Format**\n\n[code example]\n\n**Description**\n\nCount the number of records in each group\n\n#### COUNT_DISTINCT\n\n**Format**\n\n[code example]`\n\n**Description**\n\nCount the number of distinct values for `property`.\n\n> [!NOTE]\n> The reducer creates a hash-set per group, and hashes each record. This can be memory heavy if the groups are big.\n\n#### COUNT_DISTINCTISH\n\n**Format**\n\n[code example]\n\n**Description**\n\nSame as COUNT_DISTINCT, provides an approximation instead of an exact count, which consumes less memory and CPU for big groups.\n\n> [!NOTE]\n> The reducer uses [HyperLogLog](https://en.wikipedia.org/wiki/HyperLogLog) counters per group, at ~3% error rate, and 1024 bytes of constant space  per group. This means it is ideal for a few huge groups and not ideal for many small groups. In the former case, it can be an order of magnitude faster and consume much less memory than COUNT_DISTINCT, but again, it does not fit every use case.\n\n#### SUM\n\n**Format**\n\n[code example]\n\n**Description**\n\nReturn the sum of all numeric values of a given property in a group. Non-numeric values in the group are counted as 0.\n\n#### MIN\n\n**Format**\n\n[code example]\n\n**Description**\n\nReturn the minimal value of a property, whether it is a string, number, or NULL.\n\n#### MAX\n\n**Format**\n\n[code example]\n\n**Description**\n\nReturn the maximal value of a property, whether it is a string, number or NULL.\n\n#### AVG\n\n**Format**\n\n[code example]\n\n**Description**\n\nReturn the average value of a numeric property. This is equivalent to reducing by sum and count, and later, applying the ratio of them as an APPLY step.\n\n#### STDDEV\n\n**Format**\n\n[code example]\n\n**Description**\n\nReturn the [standard deviation](https://en.wikipedia.org/wiki/Standard_deviation) of a numeric property in the group.\n\n#### QUANTILE\n\n**Format**\n\n[code example]\n\n**Description**\n\nReturn the value of a numeric property at a given quantile of the results. Quantile is expressed as a number between 0 and 1. For example, the median can be expressed as the quantile at 0.5, e.g. `REDUCE QUANTILE 2 @foo 0.5 AS median` .\n\nIf multiple quantiles are required, just repeat the QUANTILE reducer for each quantile. For example, `REDUCE QUANTILE 2 @foo 0.5 AS median REDUCE QUANTILE 2 @foo 0.99 AS p99`.\n\n#### TOLIST\n\n**Format**\n\n[code example]\n\n**Description**\n\nMerge all distinct values of a given property into a single array.\n\n#### FIRST_VALUE\n\n**Format**\n\n[code example]\n\n**Description**\n\nReturn the first or top value of a given property in the group, optionally by comparing it to another property. For example, you can extract the name of the oldest user in the group:\n\n[code example]\n\nIf no `BY` is specified, the first value encountered in the group is returned.\n\nIf you wish to get the top or bottom value in the group sorted by the same value, you are better off using the `MIN/MAX` reducers, but the same effect will be achieved by doing `REDUCE FIRST_VALUE 4 @foo BY @foo DESC`.\n\n#### RANDOM_SAMPLE\n\n**Format**\n\n[code example]\n\n**Description**\n\nPerform a reservoir sampling of the group elements with a given size, and return an array of the sampled items with an even distribution.\n\n#### COLLECT\n\n> [!NOTE]\n> The `COLLECT` reducer was introduced in version 8.8.0.\n\n**Format**\n\n[code example]\n\n**Description**\n\nCollect documents within each group as an array of key-value maps, projecting either all fields currently available in the pipeline or an explicit list of fields, with optional sorting, limiting, and deduplication.\n\n- `FIELDS *` returns all document fields available at the current pipeline stage. This is stage-local: it does not trigger an implicit load and does not reach back to fields from earlier stages that were not preserved as grouping keys or reducer aliases.\n- `FIELDS {num} {field_1} ...` projects a specific list of fields. The special fields `@__key` (document key) and `@__score` (document score) are supported as projectable fields alongside document fields.\n- Fields missing from a document are omitted from that entry (sparse output, with no NULL placeholders).\n- `DISTINCT` (optional) deduplicates entries with identical projected fields, keeping the best representative according to the sort keys. When `DISTINCT` is omitted, all documents are retained including duplicates.\n- `SORTBY` (optional) sorts documents within each group by one or more fields with `ASC`/`DESC`.\n- `LIMIT` (optional) limits the number of documents returned per group. Neither `offset` nor `count` can exceed [`search-max-aggregate-results`](https://redis.io/docs/latest/develop/ai/search-and-query/administration/configuration#search-max-aggregate-results). A larger value returns the error `LIMIT offset exceeds maximum of <N>` or `LIMIT count exceeds maximum of <N>`.\n\nEach collected entry is returned as a key-value map (matching the standard `FT.AGGREGATE` result format). The `COLLECT` output is an array of such maps nested under the alias. For example:\n\n[code example]\n\nFor example, to group movies by genre and collect the top 5 movies per group, sorted by rating in descending order:\n\n[code example]"},{"id":"apply-expressions","title":"APPLY expressions","role":"content","text":"`APPLY` performs a one-to-one transformation on one or more properties in each record. It either stores the result as a new property down the pipeline, or replaces any property using this transformation.\n\nThe transformations are expressed as a combination of arithmetic expressions and built in functions. Evaluating functions and expressions is recursively nested and can be composed without limit. For example: `sqrt(log(foo) * floor(@bar/baz)) + (3^@qaz % 6)` or simply `@foo/@bar`.\n\nIf an expression or a function is applied to values that do not match the expected types, no error is emitted and a NULL value is set as the result.\n\nAPPLY steps must have an explicit alias determined by the `AS` parameter."},{"id":"literals-inside-expressions","title":"Literals inside expressions","role":"content","text":"* Numbers are expressed as integers or floating point numbers, e.g., `2`, `3.141`, and `-34`. `inf` and `-inf` are acceptable as well.\n* Strings are quoted with either single or double quotes. Single quotes are acceptable inside strings quoted with double quotes and vice versa. Punctuation marks can be escaped with backslashes. e.g. `\"foo's bar\"` ,`'foo\\'s bar'`, `\"foo \\\"bar\\\"\"` .\n* Any literal or sub-expression can be wrapped in parentheses to resolve ambiguities of operator precedence."},{"id":"arithmetic-operations","title":"Arithmetic operations","role":"content","text":"For numeric expressions and properties, addition (`+`), subtraction (`-`), multiplication (`*`), division (`/`), modulo (`%`), and power (`^`) are supported. Bitwise logical operators are not supported.\n\nNote that these operators apply only to numeric values and numeric sub-expressions. Any attempt to multiply a string by a number, for instance, will result in a NULL output."},{"id":"list-of-field-apply-functions","title":"List of field APPLY functions","role":"content","text":"| Function | Description                                                  | Example            |\n| -------- | ------------------------------------------------------------ | ------------------ |\n| exists(s)| Checks whether a field exists in a document.                 | `exists(@field)`   |\n| case(condition, if_true, if_false) | If condition is non-zero, return if_true, otherwise return if_false. | `case(exists(@foo), @foo, \"no foo\")` |"},{"id":"list-of-numeric-apply-functions","title":"List of numeric APPLY functions","role":"content","text":"| Function | Description                                                  | Example            |\n| -------- | ------------------------------------------------------------ | ------------------ |\n| log(x)   | Return the logarithm of a number, property or subexpression  | `log(@foo)`        |\n| abs(x)   | Return the absolute number of a numeric expression           | `abs(@foo-@bar)`   |\n| ceil(x)  | Round to the smallest value not less than x                  | `ceil(@foo/3.14)`  |\n| floor(x) | Round to largest value not greater than x                    | `floor(@foo/3.14)` |\n| log2(x)  | Return the logarithm of x to base 2                          | `log2(2^@foo)`     |\n| exp(x)   | Return the exponent of x, e.g., `e^x`                        | `exp(@foo)`        |\n| sqrt(x)  | Return the square root of x                                  | `sqrt(@foo)`       |"},{"id":"list-of-string-apply-functions","title":"List of string APPLY functions","role":"content","text":"| Function                         |                                                              |                                                          |\n| -------------------------------- | ------------------------------------------------------------ | -------------------------------------------------------- |\n| upper(s)                         | Return the uppercase conversion of s                         | `upper('hello world')`                                   |\n| lower(s)                         | Return the lowercase conversion of s                         | `lower(\"HELLO WORLD\")`                                   |\n| startswith(s1,s2)                | Return `1` if s2 is the prefix of s1, `0` otherwise.         | `startswith(@field, \"company\")`                          |\n| contains(s1,s2)                  | Return the number of occurrences of s2 in s1, `0` otherwise. If s2 is an empty string, return `length(s1) + 1`.  | `contains(@field, \"pa\")`                                 |\n| strlen(s)                        | Return the length of s                                       | `strlen(@t)`                                             |\n| substr(s, offset, count)         | Return the substring of s, starting at _offset_ and having _count_ characters. <br />If offset is negative, it represents the distance from the end of the string. <br />If count is -1, it means \"the rest of the string starting at offset\". | `substr(\"hello\", 0, 3)` <br> `substr(\"hello\", -2, -1)`  |\n| format( fmt, ...)                | Use the arguments following `fmt` to format a string. <br />Currently the only format argument supported is `%s` and it applies to all types of arguments. | `format(\"Hello, %s, you are %s years old\", @name, @age)` |\n| matched_terms([max_terms=100])   | Return the query terms that matched for each record (up to 100), as a list. If a limit is specified, Redis will return the first N matches found, based on query order. | `matched_terms()`                                        |\n| split(s, [sep=\",\"], [strip=\" \"]) | Split a string by any character in the string sep, and strip any characters in strip. If only s is specified, it is split by commas and spaces are stripped. The output is an array. | split(\"foo,bar\")                                         |"},{"id":"list-of-datetime-apply-functions","title":"List of date/time APPLY functions","role":"content","text":"| Function            | Description                                                  |\n| ------------------- | ------------------------------------------------------------ |\n| timefmt(x, [fmt])      | Return a formatted time string based on a numeric timestamp value x. <br /> See [strftime](https://pubs.opengroup.org/onlinepubs/9699919799/functions/strftime.html) for formatting options. <br />Not specifying `fmt` is equivalent to `%FT%TZ`. |\n| parsetime(timesharing, [fmt]) | The opposite of timefmt() - parse a time format using a given format string |\n| day(timestamp) | Round a Unix timestamp to midnight (00:00) start of the current day. |\n| hour(timestamp) | Round a Unix timestamp to the beginning of the current hour. |\n| minute(timestamp) | Round a Unix timestamp to the beginning of the current minute. |\n| month(timestamp) | Round a unix timestamp to the beginning of the current month. |\n| dayofweek(timestamp) | Convert a Unix timestamp to the day number (Sunday = 0). |\n| dayofmonth(timestamp) | Convert a Unix timestamp to the day of month number (1 .. 31). |\n| dayofyear(timestamp) | Convert a Unix timestamp to the day of year number (0 .. 365). |\n| year(timestamp) | Convert a Unix timestamp to the current year (e.g. 2018). |\n| monthofyear(timestamp) | Convert a Unix timestamp to the current month (0 .. 11). |"},{"id":"list-of-geo-apply-functions","title":"List of geo APPLY functions","role":"content","text":"| Function | Description                                                  | Example            |\n| -------- | ------------------------------------------------------------ | ------------------ |\n| geodistance(field,field)        | Return distance in meters.    | `geodistance(@field1,@field2)`       |\n| geodistance(field,\"lon,lat\")    | Return distance in meters.    | `geodistance(@field,\"1.2,-3.4\")`     |\n| geodistance(field,lon,lat)      | Return distance in meters.    | `geodistance(@field,1.2,-3.4)`       |\n| geodistance(\"lon,lat\",field)    | Return distance in meters.    | `geodistance(\"1.2,-3.4\",@field)`     |\n| geodistance(\"lon,lat\",\"lon,lat\")| Return distance in meters.    | `geodistance(\"1.2,-3.4\",\"5.6,-7.8\")` |\n| geodistance(\"lon,lat\",lon,lat)  | Return distance in meters.    | `geodistance(\"1.2,-3.4\",5.6,-7.8)`   |\n| geodistance(lon,lat,field)      | Return distance in meters.    | `geodistance(1.2,-3.4,@field)`       |\n| geodistance(lon,lat,\"lon,lat\")  | Return distance in meters.    | `geodistance(1.2,-3.4,\"5.6,-7.8\")`   |\n| geodistance(lon,lat,lon,lat)    | Return distance in meters.    | `geodistance(1.2,-3.4,5.6,-7.8)`     |\n\n[code example]\n\nTo retrieve the distance:\n\n[code example]\n\nNote: the geo field must be preloaded using `LOAD`.\n\nResults can also be sorted by distance:\n\n[code example]\n\nNote: Make sure no location is missing, otherwise the SORTBY will not return any results.\nUse FILTER to make sure you do the sorting on all valid locations."},{"id":"filter-expressions","title":"FILTER expressions","role":"content","text":"FILTER expressions filter the results using predicates relating to values in the result set.\n\nThe FILTER expressions are evaluated post-query and relate to the current state of the pipeline. Thus they can be useful to prune the results based on group calculations. Note that the filters are not indexed and will not speed up processing.\n\nFilter expressions follow the syntax of APPLY expressions, with the addition of the conditions `==`, `!=`, `<`, `<=`, `>`, `>=`. Two or more predicates can be combined with logical AND (`&&`) and OR (`||`). A single predicate can be negated with a NOT prefix (`!`).\n\nFor example, filtering all results where the user name is 'foo' and the age is less than 20 is expressed as:\n\n[code example]\n\nSeveral filter steps can be added, although at the same stage in the pipeline, it is more efficient to combine several predicates into a single filter step."},{"id":"cursor-api","title":"Cursor API","role":"content","text":"[code example]\n\nYou can use cursors with [`FT.AGGREGATE`](https://redis.io/docs/latest/commands/ft.aggregate), with the `WITHCURSOR` keyword. Cursors allow you to\nconsume only part of the response, allowing you to fetch additional results as needed.\nThis is much quicker than using `LIMIT` with offset, since the query is executed only\nonce, and its state is stored on the server.\n\nTo use cursors, specify the `WITHCURSOR` keyword in [`FT.AGGREGATE`](https://redis.io/docs/latest/commands/ft.aggregate). For example:\n\n[code example]\n\nThis will return a response of an array with two elements. The first element is\nthe actual (partial) result, and the second is the cursor ID. The cursor ID\ncan then be fed to [`FT.CURSOR READ`](https://redis.io/docs/latest/commands/ft.cursor-read) repeatedly until the cursor ID is 0, in\nwhich case all results have been returned.\n\nTo read from an existing cursor, use [`FT.CURSOR READ`](https://redis.io/docs/latest/commands/ft.cursor-read). For example:\n\n[code example]\n\nAssuming `342459320` is the cursor ID returned from the [`FT.AGGREGATE`](https://redis.io/docs/latest/commands/ft.aggregate) request, here is an example in pseudo-code:\n\n[code example]\n\nNote that even if the cursor is 0, a partial result may still be returned."},{"id":"cursor-settings","title":"Cursor settings","role":"content","text":"#### Read size\n\nYou can control how many rows are read for each cursor fetch by using the\n`COUNT` parameter. This parameter can be specified both in [`FT.AGGREGATE`](https://redis.io/docs/latest/commands/ft.aggregate)\n(immediately after `WITHCURSOR`) or in [`FT.CURSOR READ`](https://redis.io/docs/latest/commands/ft.cursor-read).\n\nThe following example will read 10 rows at a time:\n[code example]\n\nYou can override this setting by also specifying `COUNT` in `CURSOR READ`. The following example will return at most 50 results:\n\n[code example]\n\nThe default read size is 1000.\n\n#### Timeouts and limits\n\nBecause cursors are stateful resources that occupy memory on the server, they\nhave a limited lifetime. To safeguard against orphaned/stale cursors,\ncursors have an idle timeout value. If no activity occurs on the cursor before\nthe idle timeout, the cursor is deleted. The idle timer resets to 0 whenever\nthe cursor is read from using `CURSOR READ`.\n\nThe default idle timeout is 300000 milliseconds (or 300 seconds). You can modify\nthe idle timeout using the `MAXIDLE` keyword when creating the cursor. Note that\nthe value cannot exceed the default 300s.\n\nFor example, to set a limit of ten seconds:\n\n[code example]\n\nDuring a cursor’s lifetime, CURSOR READ may return results that exclude newer updates.\n\nAlso, note that in case of any load rebalancing operations (such as Atomic Slot Migration) occurring during the lifetime of a cursor, some results may be missing. In a Redis Software deployment, these operations may happen behind the scenes without any user intervention."},{"id":"other-cursor-commands","title":"Other cursor commands","role":"content","text":"Cursors can be explicitly deleted using the `CURSOR DEL` command. For example:\n\n[code example]\n\nNote that cursors are automatically deleted if all their results have been\nreturned, or if they have timed out.\n\nAll idle cursors can be forcefully purged at the same time using `FT.CURSOR GC idx 0` command.\nBy default, Redis uses a lazy throttled approach to garbage collection, which\ncollects idle cursors every 500 operations, or every second, whichever is later."}],"examples":[{"id":"aggregate-request-format-ex0","language":"sql","code":"FT.AGGREGATE\n  {index_name:string}\n  {query_string:string}\n  [VERBATIM]\n  [LOAD {nargs:integer} {property:string} ...]\n  [GROUPBY\n    {nargs:integer} {property:string} ...\n    REDUCE\n      {FUNC:string}\n      {nargs:integer} {arg:string} ...\n      [AS {name:string}]\n    ...\n  ] ...\n  [SORTBY\n    {nargs:integer} {string} ...\n    [MAX {num:integer}] ...\n  ] ...\n  [APPLY\n    {EXPR:string}\n    AS {name:string}\n  ] ...\n  [FILTER {EXPR:string}] ...\n  [LIMIT {offset:integer} {num:integer} ] ...\n  [PARAMS {nargs} {name} {value} ... ]","section_id":"aggregate-request-format"},{"id":"example-1-unique-users-by-hour-ordered-chronologically-ex0","language":"plaintext","code":"FT.AGGREGATE myIndex \"*\"","section_id":"example-1-unique-users-by-hour-ordered-chronologically"},{"id":"example-1-unique-users-by-hour-ordered-chronologically-ex1","language":"plaintext","code":"FT.AGGREGATE myIndex \"*\"\n  APPLY \"@timestamp - (@timestamp % 3600)\" AS hour","section_id":"example-1-unique-users-by-hour-ordered-chronologically"},{"id":"example-1-unique-users-by-hour-ordered-chronologically-ex2","language":"plaintext","code":"FT.AGGREGATE myIndex \"*\"\n  APPLY \"@timestamp - (@timestamp % 3600)\" AS hour\n\n  GROUPBY 1 @hour\n  \tREDUCE COUNT_DISTINCT 1 @user_id AS num_users","section_id":"example-1-unique-users-by-hour-ordered-chronologically"},{"id":"example-1-unique-users-by-hour-ordered-chronologically-ex3","language":"plaintext","code":"FT.AGGREGATE myIndex \"*\"\n  APPLY \"@timestamp - (@timestamp % 3600)\" AS hour\n\n  GROUPBY 1 @hour\n  \tREDUCE COUNT_DISTINCT 1 @user_id AS num_users\n\n  SORTBY 2 @hour ASC","section_id":"example-1-unique-users-by-hour-ordered-chronologically"},{"id":"example-1-unique-users-by-hour-ordered-chronologically-ex4","language":"plaintext","code":"FT.AGGREGATE myIndex \"*\"\n  APPLY \"@timestamp - (@timestamp % 3600)\" AS hour\n\n  GROUPBY 1 @hour\n  \tREDUCE COUNT_DISTINCT 1 @user_id AS num_users\n\n  SORTBY 2 @hour ASC\n\n  APPLY timefmt(@hour) AS hour","section_id":"example-1-unique-users-by-hour-ordered-chronologically"},{"id":"example-2-sort-visits-to-a-specific-url-by-day-and-country-ex0","language":"plaintext","code":"FT.AGGREGATE myIndex \"@url:\\\"about.html\\\"\"\n    APPLY \"@timestamp - (@timestamp % 86400)\" AS day\n    GROUPBY 2 @day @country\n    \tREDUCE count 0 AS num_visits\n    SORTBY 4 @day ASC @country DESC","section_id":"example-2-sort-visits-to-a-specific-url-by-day-and-country"},{"id":"supported-groupby-reducers-ex0","language":"plaintext","code":"REDUCE COUNT 0","section_id":"supported-groupby-reducers"},{"id":"supported-groupby-reducers-ex1","language":"`","code":"REDUCE COUNT_DISTINCT 1 {property}","section_id":"supported-groupby-reducers"},{"id":"supported-groupby-reducers-ex2","language":"plaintext","code":"REDUCE COUNT_DISTINCTISH 1 {property}","section_id":"supported-groupby-reducers"},{"id":"supported-groupby-reducers-ex3","language":"plaintext","code":"REDUCE SUM 1 {property}","section_id":"supported-groupby-reducers"},{"id":"supported-groupby-reducers-ex4","language":"plaintext","code":"REDUCE MIN 1 {property}","section_id":"supported-groupby-reducers"},{"id":"supported-groupby-reducers-ex5","language":"plaintext","code":"REDUCE MAX 1 {property}","section_id":"supported-groupby-reducers"},{"id":"supported-groupby-reducers-ex6","language":"plaintext","code":"REDUCE AVG 1 {property}","section_id":"supported-groupby-reducers"},{"id":"supported-groupby-reducers-ex7","language":"plaintext","code":"REDUCE STDDEV 1 {property}","section_id":"supported-groupby-reducers"},{"id":"supported-groupby-reducers-ex8","language":"plaintext","code":"REDUCE QUANTILE 2 {property} {quantile}","section_id":"supported-groupby-reducers"},{"id":"supported-groupby-reducers-ex9","language":"plaintext","code":"REDUCE TOLIST 1 {property}","section_id":"supported-groupby-reducers"},{"id":"supported-groupby-reducers-ex10","language":"plaintext","code":"REDUCE FIRST_VALUE {nargs} {property} [BY {property} [ASC|DESC]]","section_id":"supported-groupby-reducers"},{"id":"supported-groupby-reducers-ex11","language":"plaintext","code":"REDUCE FIRST_VALUE 4 @name BY @age DESC","section_id":"supported-groupby-reducers"},{"id":"supported-groupby-reducers-ex12","language":"plaintext","code":"REDUCE RANDOM_SAMPLE {nargs} {property} {sample_size}","section_id":"supported-groupby-reducers"},{"id":"supported-groupby-reducers-ex13","language":"plaintext","code":"REDUCE COLLECT {nargs}\n    FIELDS ( * | {num_fields} {field_1} [{field_2} ...] )\n    [DISTINCT]\n    [SORTBY {nargs} {@field} [ASC|DESC] [{@field} [ASC|DESC] ...]]\n    [LIMIT {offset} {count}]\n  [AS {alias}]","section_id":"supported-groupby-reducers"},{"id":"supported-groupby-reducers-ex14","language":"plaintext","code":"\"top_fruits\" =>\n    1) \"__key\"    => \"doc_10\"\n       \"__score\"  => \"0.95\"\n       \"fruit\"    => \"apple\"\n       \"color\"    => \"yellow\"\n       \"sweetness\"=> \"6\"\n    2) \"__key\"    => \"doc_1\"\n       \"__score\"  => \"0.82\"\n       \"fruit\"    => \"banana\"\n       \"color\"    => \"yellow\"\n       \"sweetness\"=> \"5\"","section_id":"supported-groupby-reducers"},{"id":"supported-groupby-reducers-ex15","language":"plaintext","code":"FT.AGGREGATE idx:movies \"*\"\n    LOAD *\n    GROUPBY 1 @genre\n      REDUCE COLLECT 10 FIELDS 1 * SORTBY 2 @rating DESC LIMIT 0 5 AS top_movies\n    SORTBY 1 @genre LIMIT 0 50","section_id":"supported-groupby-reducers"},{"id":"list-of-geo-apply-functions-ex0","language":"plaintext","code":"FT.AGGREGATE myIdx \"*\"  LOAD 1 location  APPLY \"geodistance(@location,\\\"-1.1,2.2\\\")\" AS dist","section_id":"list-of-geo-apply-functions"},{"id":"list-of-geo-apply-functions-ex1","language":"plaintext","code":"FT.AGGREGATE myIdx \"*\"  LOAD 1 location  APPLY \"geodistance(@location,\\\"-1.1,2.2\\\")\" AS dist","section_id":"list-of-geo-apply-functions"},{"id":"list-of-geo-apply-functions-ex2","language":"plaintext","code":"FT.AGGREGATE idx \"*\" LOAD 1 @location FILTER \"exists(@location)\" APPLY \"geodistance(@location,-117.824722,33.68590)\" AS dist SORTBY 2 @dist DESC","section_id":"list-of-geo-apply-functions"},{"id":"filter-expressions-ex0","language":"plaintext","code":"FT.AGGREGATE\n  ...\n  FILTER \"@name=='foo' && @age < 20\"\n  ...","section_id":"filter-expressions"},{"id":"cursor-api-ex0","language":"plaintext","code":"FT.AGGREGATE ... WITHCURSOR [COUNT {read size} MAXIDLE {idle timeout}]\nFT.CURSOR READ {idx} {cid} [COUNT {read size}]\nFT.CURSOR DEL {idx} {cid}","section_id":"cursor-api"},{"id":"cursor-api-ex1","language":"plaintext","code":"FT.AGGREGATE idx * WITHCURSOR","section_id":"cursor-api"},{"id":"cursor-api-ex2","language":"plaintext","code":"FT.CURSOR READ idx 342459320","section_id":"cursor-api"},{"id":"cursor-api-ex3","language":"plaintext","code":"response, cursor = FT.AGGREGATE \"idx\" \"redis\" \"WITHCURSOR\";\nwhile (1) {\n  processResponse(response)\n  if (!cursor) {\n    break;\n  }\n  response, cursor = FT.CURSOR read \"idx\" cursor\n}","section_id":"cursor-api"},{"id":"cursor-settings-ex0","language":"plaintext","code":"FT.AGGREGATE idx query WITHCURSOR COUNT 10","section_id":"cursor-settings"},{"id":"cursor-settings-ex1","language":"plaintext","code":"FT.CURSOR READ idx 342459320 COUNT 50","section_id":"cursor-settings"},{"id":"cursor-settings-ex2","language":"plaintext","code":"FT.AGGREGATE idx query WITHCURSOR MAXIDLE 10000","section_id":"cursor-settings"},{"id":"other-cursor-commands-ex0","language":"plaintext","code":"FT.CURSOR DEL idx 342459320","section_id":"other-cursor-commands"}]}
{"schema_version":2,"id":"develop/ai/search-and-query/advanced-concepts/aggregations-syntax","title":"FT.AGGREGATE order of operations","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/aggregations-syntax/","summary":"Order of operations for the FT.AGGREGATE command","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:33:04+01:00","page_type":"content","content_hash":"01da9106f60e8c5e6908b19e3f4ec526e6b5b3d5c0c70eb41e0ec3e9d1fa0284","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[`FT.AGGREGATE`](https://redis.io/docs/latest/commands/ft.aggregate) is a powerful Redis Search command for performing advanced data aggregation, filtering, sorting, and transformations on indexed hash or JSON documents. This reference page provides a structured breakdown of syntax, ordering rules, and best practices.\n\nThe [main aggregations page](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/aggregations) has a simple diagram showing how `FT.AGGREGATE` pipelines are constructed, but it doesn't tell the whole story. For example, you can create more complex aggregation pipelines by applying multiple `REDUCE` functions under a single `GROUPBY` clause, or you can chain groupings and mix in additional mapping steps:\n\n`GROUPBY` ... `REDUCE` ... `APPLY` ... `GROUPBY` ... `REDUCE`"},{"id":"syntax-and-expression-ordering","title":"Syntax and expression ordering","role":"syntax","text":"The `FT.AGGREGATE` command processes multiple expressions in a pipeline. Below is the recommended order:\n\n1. `index` – the name of your index, which must be the first argument.\n1. `query` – your query, which must be the second argument.\n1. `FILTER` – filters raw documents before transformations or aggregation.\n1. `LOAD` – loads document fields.\n1. `APPLY` – applies transformations on fields.\n1. `GROUPBY` – groups results by specific fields.\n1. `REDUCE` – performs aggregations. For example, `SUM`, `COUNT`, and `AVG`.\n1. `SORTBY` – orders the results based on specified fields.\n1. `LIMIT` – restricts the number of results returned.\n1. `DIALECT 2` - provides for more comprehensive query syntax, for example using parameters in `FILTER` expressions.\n\nOther keywords will be discussed toward the end of this page."},{"id":"when-to-use-","title":"When to use `@`","role":"content","text":"You must add `@` at the start of a field name in the following circumstances:\n\n- When referencing fields loaded from documents. In the following example, `price` is a document field and must be prefixed with `@`.\n\n[code example]\n\n- When referencing fields inside a `FILTER` clause that were loaded from documents.\n\n[code example]\n\n- When referencing fields inside `GROUPBY` or `REDUCE` clauses.\n\n[code example]\n\n- When referencing fields created by `REDUCE` in `APPLY` or `FILTER` clauses.\n\n[code example]\n\n- When referencing fields created by `APPLY` in another `APPLY` or `FILTER` clause.\n\n[code example]\n\n- When referencing fields created by `APPLY` in a `SORTBY` clause.\n\n[code example]"},{"id":"groupby-with-multiple-reduce-operations","title":"GROUPBY with multiple REDUCE operations","role":"content","text":"You can use multiple `REDUCE` operations after `GROUPBY` for different aggregations.\n\n[code example]"},{"id":"multiple-apply-operations-followed-by-groupby-and-reduce","title":"Multiple APPLY operations followed by GROUPBY and REDUCE","role":"content","text":"You can use `APPLY` in various ways before and after `GROUPBY` and `REDUCE`.\n\n[code example]"},{"id":"filter-and-params","title":"FILTER and PARAMS","role":"content","text":"Use `FILTER` to remove unwanted records, and `PARAMS` to pass values to parameterized queries.\n\n[code example]"},{"id":"placement-of-filter-before-and-after-groupbyapply","title":"Placement of FILTER before and after GROUPBY/APPLY","role":"content","text":"- **Before GROUPBY:** Removes records before aggregation.\n- **After GROUPBY:** Filters based on aggregated results."},{"id":"load-after-groupbyreduce","title":"LOAD after GROUPBY/REDUCE","role":"content","text":"This is not allowed and you'll get a syntax error."},{"id":"placement-rules-for-specific-parameters","title":"Placement rules for specific parameters","role":"content","text":"| Parameter    | Placement                                                 |\n|-----         |-----                                                      |\n| `TIMEOUT`    | Can be placed anywhere.                                   |\n| `LIMIT`      | Must be at the end, before `DIALECT`.                     |\n| `WITHCURSOR` | Must be at the end, before `DIALECT`.                     |\n| `SCORER`     | Must be placed between the query and pipeline operations. |\n| `ADDSCORES`  | Must be placed between the query and pipeline operations. |\n| `DIALECT`    | Must be at the end.                                       |"},{"id":"limit-and-withcursor-used-together","title":"LIMIT and WITHCURSOR used together","role":"limits","text":"While you wouldn't ordinarily use `LIMIT` and `WITHCURSOR` together in the same query, you can do so if necessary.\n`LIMIT`, as the name suggests, will limit the total number of results returned for the given query. `WITHCURSOR` will paginate the results in chunks of size `COUNT`. You can use the [cursor API](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/aggregations#cursor-api) to retrieve more results, as shown below.\n\n[code example]\n\nSee the following resources for more information:\n\n- [Aggregations](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/aggregations) discussion page.\n- [`FT.AGGREGATE` command page](https://redis.io/docs/latest/commands/ft.aggregate/)\n- [Redis Search source code](https://github.com/RediSearch/RediSearch/tree/master/src/aggregate)"}],"examples":[{"id":"when-to-use--ex0","language":"sh","code":"FT.AGGREGATE products \"*\"\n  LOAD 1 @price\n  APPLY \"@price * 1.1\" AS adjusted_price\n  SORTBY 2 @adjusted_price DESC\n  LIMIT 0 10\n\n 1) (integer) 200\n 2) 1) \"price\"\n    2) \"623\"\n    3) \"adjusted_price\"\n    4) \"685.3\"\n 3) 1) \"price\"\n    2) \"619.75\"\n    3) \"adjusted_price\"\n    4) \"681.725\"\n  .\n  .\n  .","section_id":"when-to-use-"},{"id":"when-to-use--ex1","language":"sh","code":"FT.AGGREGATE products \"*\"\n  LOAD 1 @rating\n  FILTER \"@rating >= 4.5\"\n  LIMIT 0 10\n\n 1) (integer) 5\n 2) 1) \"rating\"\n    2) \"4.5\"\n 3) 1) \"rating\"\n    2) \"4.8\"\n 4) 1) \"rating\"\n    2) \"4.5\"\n  .\n  .\n  .","section_id":"when-to-use-"},{"id":"when-to-use--ex2","language":"sh","code":"FT.AGGREGATE products \"*\"\n  GROUPBY 1 @category\n  REDUCE SUM 1 @price AS total_price\n  LIMIT 0 10\n\n1) (integer) 6\n2) 1) \"category\"\n   2) \"Toys\"\n   3) \"total_price\"\n   4) \"9799.25\"\n3) 1) \"category\"\n   2) \"Electronics\"\n   3) \"total_price\"\n   4) \"10683.5\"\n4) 1) \"category\"\n   2) \"Apparel\"\n   3) \"total_price\"\n   4) \"10273.5\"\n  .\n  .\n  .","section_id":"when-to-use-"},{"id":"when-to-use--ex3","language":"sh","code":"FT.AGGREGATE products \"*\"\n  GROUPBY 1 @category\n  REDUCE SUM 1 @price AS total_price\n  APPLY \"@total_price * 1.2\" AS boosted_price\n  FILTER \"@total_price > 1000\"\n  LIMIT 0 10\n\n1) (integer) 6\n2) 1) \"category\"\n   2) \"Toys\"\n   3) \"total_price\"\n   4) \"9799.25\"\n   5) \"boosted_price\"\n   6) \"11759.1\"\n3) 1) \"category\"\n   2) \"Electronics\"\n   3) \"total_price\"\n   4) \"10683.5\"\n   5) \"boosted_price\"\n   6) \"12820.2\"\n  .\n  .\n  .","section_id":"when-to-use-"},{"id":"when-to-use--ex4","language":"sh","code":"FT.AGGREGATE products \"*\"\n  LOAD 2 @price @discount\n  APPLY \"@price - @discount\" AS net_price\n  APPLY \"@net_price * 1.1\" AS marked_up\n  FILTER \"@net_price > 200\"\n  LIMIT 0 10\n\n1) (integer) 60\n2) 1) \"price\"\n   2) \"220\"\n   3) \"discount\"\n   4) \"0\"\n   5) \"net_price\"\n   6) \"220\"\n   7) \"marked_up\"\n   8) \"242\"\n3) 1) \"price\"\n   2) \"223.25\"\n   3) \"discount\"\n   4) \"1.5\"\n   5) \"net_price\"\n   6) \"221.75\"\n   7) \"marked_up\"\n   8) \"243.925\"\n  .\n  .\n  .","section_id":"when-to-use-"},{"id":"when-to-use--ex5","language":"sh","code":"FT.AGGREGATE products \"*\"\n  LOAD 2 @price @discount\n  APPLY \"@price - @discount\" AS net_price\n  SORTBY 2 @net_price DESC\n  LIMIT 0 10\n\n 1) (integer) 200\n 2) 1) \"price\"\n    2) \"623\"\n    3) \"discount\"\n    4) \"6\"\n    5) \"net_price\"\n    6) \"617\"\n 3) 1) \"price\"\n    2) \"619.75\"\n    3) \"discount\"\n    4) \"4.5\"\n    5) \"net_price\"\n    6) \"615.25\"\n  .\n  .\n  .","section_id":"when-to-use-"},{"id":"groupby-with-multiple-reduce-operations-ex0","language":"sh","code":"FT.AGGREGATE products \"*\"\n  GROUPBY 1 @category\n  REDUCE COUNT 0 AS product_count\n  REDUCE SUM 1 @price AS total_price\n  REDUCE AVG 1 @rating AS avg_rating\n  SORTBY 2 @total_price DESC\n  LIMIT 0 10\n\n1) (integer) 6\n2) 1) \"category\"\n   2) \"Groceries\"\n   3) \"product_count\"\n   4) \"44\"\n   5) \"total_price\"\n   6) \"13495.25\"\n   7) \"avg_rating\"\n   8) \"3.94090909091\"\n3) 1) \"category\"\n   2) \"Home\"\n   3) \"product_count\"\n   4) \"40\"\n   5) \"total_price\"\n   6) \"11026.75\"\n   7) \"avg_rating\"\n   8) \"3.78\"\n  .\n  .\n  .","section_id":"groupby-with-multiple-reduce-operations"},{"id":"multiple-apply-operations-followed-by-groupby-and-reduce-ex0","language":"sh","code":"FT.AGGREGATE products \"*\"\n  LOAD 3 @price @discount @quantity\n  APPLY \"@price - @discount\" AS final_price\n  APPLY \"@final_price * @quantity\" AS total_revenue\n  GROUPBY 1 @category\n  REDUCE SUM 1 @total_revenue AS total_category_revenue\n  SORTBY 2 @total_category_revenue DESC\n  LIMIT 0 10\n\n1) (integer) 6\n2) 1) \"category\"\n   2) \"Groceries\"\n   3) \"total_category_revenue\"\n   4) \"81373\"\n3) 1) \"category\"\n   2) \"Home\"\n   3) \"total_category_revenue\"\n   4) \"55797.5\"\n  .\n  .\n  .","section_id":"multiple-apply-operations-followed-by-groupby-and-reduce"},{"id":"filter-and-params-ex0","language":"sh","code":"FT.AGGREGATE products \"*\"\n  LOAD 3 @price @rating @quantity\n  FILTER \"@price >= 500\"\n  FILTER \"@rating >= 4.0\"\n  APPLY \"@price * @quantity\" AS total_value\n  SORTBY 2 @total_value DESC\n  LIMIT 0 10\n  DIALECT 2\n\n1) (integer) 200\n 2) 1) \"price\"\n    2) \"606.75\"\n    3) \"rating\"\n    4) \"4.2\"\n    5) \"quantity\"\n    6) \"10\"\n    7) \"total_value\"\n    8) \"6067.5\"\n 3) 1) \"price\"\n    2) \"541.75\"\n    3) \"rating\"\n    4) \"4.5\"\n    5) \"quantity\"\n    6) \"10\"\n    7) \"total_value\"\n    8) \"5417.5\"\n  .\n  .\n  .","section_id":"filter-and-params"},{"id":"limit-and-withcursor-used-together-ex0","language":"sh","code":"FT.AGGREGATE products \"*\"\n  GROUPBY 1 @category\n  REDUCE COUNT 0 AS product_count\n  LIMIT 0 100\n  WITHCURSOR COUNT 3\n\n1) 1) (integer) 6\n   2) 1) \"category\"\n      2) \"Toys\"\n      3) \"product_count\"\n      4) \"28\"\n   3) 1) \"category\"\n      2) \"Electronics\"\n      3) \"product_count\"\n      4) \"31\"\n   4) 1) \"category\"\n      2) \"Apparel\"\n      3) \"product_count\"\n      4) \"36\"\n2) (integer) 89400486\n127.0.0.1:6379> FT.CURSOR READ products 89400486 COUNT 3\n1) 1) (integer) 0\n   2) 1) \"category\"\n      2) \"Home\"\n      3) \"product_count\"\n      4) \"40\"\n   3) 1) \"category\"\n      2) \"Groceries\"\n      3) \"product_count\"\n      4) \"44\"\n   4) 1) \"category\"\n      2) \"Books\"\n      3) \"product_count\"\n      4) \"21\"\n2) (integer) 89400486\n  .\n  .\n  .","section_id":"limit-and-withcursor-used-together"}]}
{"schema_version":2,"id":"develop/ai/search-and-query/advanced-concepts/autocomplete","title":"Autocomplete with Redis","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/autocomplete/","summary":"Learn how to use the autocomplete feature of Redis for efficient prefix-based suggestion retrieval.","aliases":["/develop/interact/search-and-query/advanced-concepts/autocomplete","/interact/search-and-query/advanced-concepts/autocomplete/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:33:04+01:00","page_type":"content","content_hash":"3ee50e0362f3ffccab007d9b2231731063810f96137450c6de0843fc46d07f7e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Search provides an autocomplete feature using suggestions that are stored in a [trie-based](https://en.wikipedia.org/wiki/Trie) data structure.\nThis feature allows you to store and retrieve ranked suggestions based on user input prefixes, making it useful for applications like search boxes, command completion, and chatbot responses.\n\nThis guide covers how to use the [`FT.SUGADD`](https://redis.io/docs/latest/commands/ft.sugadd), [`FT.SUGGET`](https://redis.io/docs/latest/commands/ft.sugget), [`FT.SUGDEL`](https://redis.io/docs/latest/commands/ft.sugdel), and [`FT.SUGLEN`](https://redis.io/docs/latest/commands/ft.suglen) commands to implement autocomplete, and some examples of how you can use these commands with [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search)."},{"id":"add-autocomplete-suggestions","title":"Add autocomplete suggestions","role":"content","text":"To add phrases or words to a suggestions dictionary, use the [`FT.SUGADD`](https://redis.io/docs/latest/commands/ft.sugadd) command.\nYou will assign a score to each entry, which determines its ranking in the results.\n\n[code example]\n\nInteger scores were used in the above examples, but the scores argument is described as being floating point.\nInteger scores are converted to floating point internally.\nAlso, \"`autocomplete`\" in the above examples is just the name of the key; you can use any key name you wish."},{"id":"optional-arguments","title":"Optional arguments","role":"parameters","text":"The `FT.SUGADD` command can take two optional arguments:\n\n* `INCR`: increments the existing entry of the suggestion by the given score instead of replacing the score. This is useful for updating the dictionary based on user queries in real time.\n* `PAYLOAD`: saves a string with the suggestion, which can be fetched by adding the `WITHPAYLOADS` argument to `FT.SUGGET`."},{"id":"retrieve-suggestions","title":"Retrieve suggestions","role":"content","text":"To get autocomplete suggestions for a given prefix, use the [`FT.SUGGET`](https://redis.io/docs/latest/commands/ft.sugget) command.\n\n[code example]\n\nIf you wish to see the scores, use the `WITHSCORES` option:\n\n[code example]"},{"id":"enable-fuzzy-matching","title":"Enable fuzzy matching","role":"content","text":"If you want to allow for small spelling mistakes or typos, use the `FUZZY` option. This option performs a fuzzy prefix search, including prefixes at a [Levenshtein distance](https://en.wikipedia.org/wiki/Levenshtein_distance) of 1 from the provided prefix.\n\n[code example]"},{"id":"optional-arguments-1","title":"Optional arguments","role":"parameters","text":"There are three additional arguments you can use with `FT.SUGGET`:\n\n* `MAX num`: limits the results to a maximum of `num`. The default for `MAX` is 5.\n* `WITHSCORES`: returns the score of each suggestion.\n* `WITHPAYLOADS`: returns optional payloads saved with the suggestions. If no payload is present for an entry, a `nil` reply is returned.\n    [code example]"},{"id":"delete-suggestions","title":"Delete suggestions","role":"content","text":"To remove a specific suggestion from the dictionary, use the `FT.SUGDEL` command.\n\n[code example]\n\nAfter deletion, running `FT.SUGGET autocomplete hell FUZZY` will no longer return \"help me\"."},{"id":"check-the-number-of-suggestions","title":"Check the number of suggestions","role":"content","text":"To get a count of the number of entries in a given suggestions dictionary, use the `FT.SUGLEN` command.\n\n[code example]"},{"id":"use-autocomplete-with-search","title":"Use autocomplete with search","role":"content","text":"A common approach is to:\n\n1. Use FT.SUGGET to suggest query completions as users type in a text field.\n1. Once the user selects a suggestion, run FT.SEARCH using the selected term to get full search results.\n\nExample workflow\n\n1. Get suggestions for a given user input.\n\n    [code example]\n1. Capture the user's selection.\n1. Use the selected suggestion in a full-text search.\n\n    [code example]"},{"id":"when-to-use-autocomplete-versus-full-text-search","title":"When to use autocomplete versus full-text search","role":"content","text":"* Use `FT.SUGGET` when you need fast, real-time prefix-based suggestion retrieval.\n* Use `FT.SEARCH` when you need document retrieval, filtering, and ranking based on relevance."},{"id":"autocomplete-use-cases","title":"Autocomplete use cases","role":"content","text":"The autocomplete feature in Redis Search is useful for:\n\n- **Search box suggestions**: providing live suggestions as users type.\n- **Command completion**: offering autocompletion for CLI tools.\n- **Product search**: suggesting product names in e-commerce applications.\n- **Chatbot responses**: recommending common phrases dynamically."}],"examples":[{"id":"add-autocomplete-suggestions-ex0","language":"plaintext","code":"FT.SUGADD autocomplete \"hello world\" 100\nFT.SUGADD autocomplete \"hello there\" 90\nFT.SUGADD autocomplete \"help me\" 80\nFT.SUGADD autocomplete \"hero\" 70","section_id":"add-autocomplete-suggestions"},{"id":"retrieve-suggestions-ex0","language":"plaintext","code":"redis> FT.SUGGET autocomplete \"he\"\n1) \"hero\"\n2) \"help me\"\n3) \"hello world\"\n4) \"hello there\"","section_id":"retrieve-suggestions"},{"id":"retrieve-suggestions-ex1","language":"plaintext","code":"redis> FT.SUGGET autocomplete \"he\" WITHSCORES\n1) \"hero\"\n2) \"40.414520263671875\"\n3) \"help me\"\n4) \"32.65986251831055\"\n5) \"hello world\"\n6) \"31.62277603149414\"\n7) \"hello there\"\n8) \"28.460498809814453\"","section_id":"retrieve-suggestions"},{"id":"enable-fuzzy-matching-ex0","language":"plaintext","code":"redis> FT.SUGGET autocomplete hell FUZZY\n1) \"hello world\"\n2) \"hello there\"\n3) \"help me\"","section_id":"enable-fuzzy-matching"},{"id":"optional-arguments-1-ex0","language":"plaintext","code":"redis> FT.SUGADD autocomplete hero 70 PAYLOAD \"you're no hero\"\n    (integer) 4\n    redis> FT.SUGGET autocomplete \"hr\" FUZZY WITHPAYLOADS\n    1) \"hero\"\n    2) \"you're no hero\"\n    3) \"help me\"\n    4) (nil)\n    5) \"hello world\"\n    6) (nil)\n    7) \"hello there\"\n    8) (nil)","section_id":"optional-arguments-1"},{"id":"delete-suggestions-ex0","language":"plaintext","code":"redis> FT.SUGDEL autocomplete \"help me\"\n(integer) 1","section_id":"delete-suggestions"},{"id":"check-the-number-of-suggestions-ex0","language":"plaintext","code":"redis> FT.SUGLEN autocomplete\n(integer) 3","section_id":"check-the-number-of-suggestions"},{"id":"use-autocomplete-with-search-ex0","language":"plaintext","code":"FT.SUGGET autocomplete \"hel\"","section_id":"use-autocomplete-with-search"},{"id":"use-autocomplete-with-search-ex1","language":"plaintext","code":"FT.SEARCH index \"hello world\"","section_id":"use-autocomplete-with-search"}]}
{"schema_version":2,"id":"develop/ai/search-and-query/advanced-concepts/chinese","title":"Chinese support","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/chinese/","summary":"Chinese support for searching and querying in Redis Open Source","aliases":["/develop/interact/search-and-query/advanced-concepts/chinese","/interact/search-and-query/advanced-concepts/chinese/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-04-07T16:24:18+01:00","page_type":"content","content_hash":"317cd004dc280ff73b9c46061cac13e6caa91ae7f4e94faad137334912dbe6dc","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Support for adding documents in Chinese is available starting at version 0.99.0.\n\nChinese support allows Chinese documents to be added and tokenized using segmentation\nrather than simple tokenization using whitespace and/or punctuation.\n\nIndexing a Chinese document is different than indexing a document in most other\nlanguages because of how tokens are extracted. While most languages can have\ntheir tokens distinguished by separation characters and whitespace, this\nis not common in Chinese.\n\nChinese tokenization is done by scanning the input text and checking every\ncharacter or sequence of characters against a dictionary of predefined terms,\nand determining the most likely match based on the surrounding terms and characters.\n\nRedis makes use of the [Friso](https://github.com/lionsoul2014/friso)\nChinese tokenization library for this purpose. This is largely transparent to\nthe user and often no additional configuration is required."},{"id":"example-using-chinese-in-queries","title":"Example: using chinese in queries","role":"example","text":"In pseudo-code:\n\n[code example]\n\nUsing the Python client:\n\n[code example]"},{"id":"using-custom-dictionaries","title":"Using custom dictionaries","role":"content","text":"If you wish to use a custom dictionary, you can do so at the module level when\nloading the module. The `FRISOINI` setting can point to the location of a\n`friso.ini` file that contains the relevant settings and paths to the dictionary\nfiles.\n\nNote that there is no default `friso.ini` file location. RediSearch comes with\nits own `friso.ini` and dictionary files, which are compiled into the module\nbinary at build-time."}],"examples":[{"id":"example-using-chinese-in-queries-ex0","language":"plaintext","code":"FT.CREATE idx ON HASH SCHEMA txt TEXT\nHSET docCn txt \"Redis支持主从同步。数据可以从主服务器向任意数量的从服务器上同步，从服务器可以是关联其他从服务器的主服务器。这使得Redis可执行单层树复制。从盘可以有意无意的对数据进行写操作。由于完全实现了发布/订阅机制，使得从数据库在任何地方同步树时，可订阅一个频道并接收主服务器完整的消息发布记录。同步对读取操作的可扩展性和数据冗余很有帮助。[8]\"\nFT.SEARCH idx \"数据\" LANGUAGE chinese HIGHLIGHT SUMMARIZE\n# Outputs:\n# <b>数据</b>?... <b>数据</b>进行写操作。由于完全实现了发布... <b>数据</b>冗余很有帮助。[8...","section_id":"example-using-chinese-in-queries"},{"id":"example-using-chinese-in-queries-ex1","language":"plaintext","code":"# -*- coding: utf-8 -*-\n\nfrom redisearch.client import Client, Query\nfrom redisearch import TextField\n\nclient = Client('idx')\ntry:\n    client.drop_index()\nexcept:\n    pass\n\nclient.create_index([TextField('txt')])\n\n# Add a document\nclient.add_document('docCn1',\n                    txt='Redis支持主从同步。数据可以从主服务器向任意数量的从服务器上同步从服务器可以是关联其他从服务器的主服务器。这使得Redis可执行单层树复制。从盘可以有意无意的对数据进行写操作。由于完全实现了发布/订阅机制，使得从数据库在任何地方同步树时，可订阅一个频道并接收主服务器完整的消息发布记录。同步对读取操作的可扩展性和数据冗余很有帮助。[8]',\n                    language='chinese')\nprint client.search(Query('数据').summarize().highlight().language('chinese')).docs[0].txt\n# Outputs:\n# <b>数据</b>?... <b>数据</b>进行写操作。由于完全实现了发布... <b>数据</b>冗余很有帮助。[8...","section_id":"example-using-chinese-in-queries"}]}
{"schema_version":2,"id":"develop/ai/search-and-query/advanced-concepts/dialects","title":"Query dialects","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/dialects/","summary":"Learn how to use query dialects","aliases":["/develop/interact/search-and-query/advanced-concepts/dialects"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:33:04+01:00","page_type":"content","content_hash":"62596ae38468ab015e67ae3d1e246b3975dd381ecc3773b623de680761dcc074","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Open Source currently supports four query dialects for use with the [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search), [`FT.AGGREGATE`](https://redis.io/docs/latest/commands/ft.aggregate), and other Redis Search commands.\nDialects provide for enhancing the query API incrementally, introducing innovative behaviors and new features that support new use cases in a way that does not break the API for existing applications.\n\n> [!NOTE]\n> Dialects 1, 3, and 4 are deprecated in Redis 8 in Redis Open Source. However, DIALECT 1 remains the default."},{"id":"dialect-1-deprecated","title":"`DIALECT 1` (Deprecated)","role":"content","text":"Dialect version 1 was the default query syntax dialect from the first release of search and query until dialect version 2 was introduced with version [2.4](https://github.com/RediSearch/RediSearch/releases/tag/v2.4.3).\nThis dialect is also the default dialect. See below for information about changing the default dialect."},{"id":"dialect-2","title":"`DIALECT 2`","role":"content","text":"Dialect version 2 was introduced in the [2.4](https://github.com/RediSearch/RediSearch/releases/tag/v2.4.3) release to address query parser inconsistencies found in previous versions of Redis. Dialect version 1 remains the default dialect. To use dialect version 2, append `DIALECT 2` to your query command.\nSupport for vector search also was introduced in the 2.4 release and requires `DIALECT 2`. See [here](https://redis.io/docs/latest/develop/ai/search-and-query/query/vector-search) for more details.\n`FT.SEARCH ... DIALECT 2`\n\nIt was determined that under certain conditions some query parsing rules did not behave as originally intended.\nParticularly, some queries containing the operators below could return unexpected results.\n\n1. AND, multi-word phrases that imply intersection\n1. `\"...\"` (exact), `~` (optional), `-` (negation), and `%` (fuzzy)\n1. OR, words separated by the `|` (pipe) character that imply union\n1. wildcard characters\n\nExisting queries that used dialect 1 may behave differently using dialect 2 if they fall into any of the following categories:\n\n1. Your query has a field modifier followed by multiple words. Consider the sample query:\n\n    `@name:James Brown`\n\n    Here, the field modifier `@name` is followed by two words, `James` and `Brown`.\n\n    In `DIALECT 1`, this query would be interpreted as find `James Brown` in the `@name` field.\n    In `DIALECT 2`, this query would be interpreted as find `James` in the `@name` field, and `Brown` in any text field. In other words, it would be interpreted as `(@name:James) Brown`. \n    In `DIALECT 2`, you could achieve the dialect 1 behavior by updating your query to `@name:(James Brown)`.\n\n1. Your query uses `\"...\"`, `~`, `-`, and/or `%`. Consider a simple query with negation:\n\n    `-hello world`\n\n    In `DIALECT 1`, this query is interpreted as find values in any field that do not contain `hello` and do not contain `world`; the equivalent of `-(hello world)` or `-hello -world`.\n    In `DIALECT 2`, this query is interpreted as `-hello` and `world` (only `hello` is negated).\n    In `DIALECT 2`, you could achieve the dialect 1 behavior by updating your query to `-(hello world)`.\n\n1. Your query used `|`. Consider the simple query:\n\n    `hello world | \"goodbye\" moon`\n\n    In `DIALECT 1`, this query is interpreted as searching for `(hello world | \"goodbye\") moon`.\n    In `DIALECT 2`, this query is interpreted as searching for either `hello world` `\"goodbye\" moon`.\n\n1. Your query uses a wildcard pattern. Consider the simple query:\n\n    `\"w'foo*bar?'\"`\n\n    As shown above, you must use double quotes to contain the `w` pattern.\n\nWith `DIALECT 2` you can use un-escaped spaces in tag queries, even with stopwords.\n\n> [!NOTE]\n> `DIALECT 2` is required with vector searches.\n\n`DIALECT 2` functionality was enhanced in the 2.10 release.\nIt introduces support for new comparison operators for `NUMERIC` fields:\n\n* `==` (equal).\n  \n  `FT.SEARCH idx \"@numeric==3456\" DIALECT 2`\n\n  and\n\n  `FT.SEARCH idx \"@numeric:[3456]\" DIALECT 2`\n* `!=` (not equal).\n  \n  `FT.SEARCH idx \"@numeric!=3456\" DIALECT 2`\n* `>` (greater than).\n  \n  `FT.SEARCH idx \"@numeric>3456\" DIALECT 2`\n* `>=` (greater than or equal).\n  \n  `FT.SEARCH idx \"@numeric>=3456\" DIALECT 2`\n* `<` (less than).\n  \n  `FT.SEARCH idx \"@numeric<3456\" DIALECT 2`\n* `<=` (less than or equal).\n  \n  `FT.SEARCH idx \"@numeric<=3456\" DIALECT 2`\n\nThe Dialect version 2 enhancements also introduce simplified syntax for logical operations:\n\n* `|` (or).\n\n  `FT.SEARCH idx \"@tag:{3d3586fe-0416-4572-8ce1 | 3d3586fe-0416-6758-4ri8}\" DIALECT 2`\n\n  which is equivalent to\n\n  `FT.SEARCH idx \"(@tag:{3d3586fe-0416-4572-8ce1} | @tag{3d3586fe-0416-6758-4ri8})\" DIALECT 2`\n\n* `<space>` (and).\n\n  `FT.SEARCH idx \"(@tag:{3d3586fe-0416-4572-8ce1} @tag{3d3586fe-0416-6758-4ri8})\" DIALECT 2`\n\n* `-` (negation).\n\n  `FT.SEARCH idx \"(@tag:{3d3586fe-0416-4572-8ce1} -@tag{3d3586fe-0416-6758-4ri8})\" DIALECT 2`\n\n* `~` (optional/proximity).\n\n  `FT.SEARCH idx \"(@tag:{3d3586fe-0416-4572-8ce1} ~@tag{3d3586fe-0416-6758-4ri8})\" DIALECT 2`"},{"id":"dialect-3-deprecated","title":"`DIALECT 3` (Deprecated)","role":"content","text":"Dialect version 3 was introduced in the [2.6](https://github.com/RediSearch/RediSearch/releases/tag/v2.6.3) release. This version introduced support for multi-value indexing and querying of attributes for any attribute type ( [TEXT](https://redis.io/docs/latest/develop/ai/search-and-query/indexing#index-json-arrays-as-text), [TAG](https://redis.io/docs/latest/develop/ai/search-and-query/indexing#index-json-arrays-as-tag), [NUMERIC](https://redis.io/docs/latest/develop/ai/search-and-query/indexing#index-json-arrays-as-numeric), [GEO](https://redis.io/docs/latest/develop/ai/search-and-query/indexing#index-json-arrays-as-geo) and [VECTOR](https://redis.io/docs/latest/develop/ai/search-and-query/indexing#index-json-arrays-as-vector)) defined by a [JSONPath](https://redis.io/docs/latest/develop/data-types/json/path) leading to an array or multiple scalar values. Support for [GEOSHAPE](https://redis.io/docs/latest/develop/ai/search-and-query/query/geo-spatial) queries was also introduced in this version.\n\nThe primary difference between dialects version 2 and version 3 is that JSON is returned rather than scalars for multi-value attributes. Apart from specifying `DIALECT 3` at the end of a [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) command, there are no other syntactic changes. Dialect version 1 remains the default dialect. To use dialect version 3, append `DIALECT 3` to your query command.\n\n`FT.SEARCH ... DIALECT 3`\n\n**Example**\n\nSample JSON:\n\n[code example]\n\nCreate an index:\n\n[code example]\n\nNow search, with and without `DIALECT 3`.\n\n- With dialect 1 (default):\n\n    [code example]\n    \n    Only the first element of the expected two elements is returned.\n\n- With dialect 3:\n\n    [code example]\n\n    Both elements are returned."},{"id":"dialect-4-deprecated","title":"`DIALECT 4` (Deprecated)","role":"content","text":"Dialect version 4 was introduced in the [2.8](https://github.com/RediSearch/RediSearch/releases/tag/v2.8.4) release. It introduces performance optimizations for sorting operations on [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) and [`FT.AGGREGATE`](https://redis.io/docs/latest/commands/ft.aggregate). Apart from specifying `DIALECT 4` at the end of a [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) command, there are no other syntactic changes. Dialect version 1 remains the default dialect. To use dialect version 4, append `DIALECT 4` to your query command.\n\n`FT.SEARCH ... DIALECT 4`\n\nDialect version 4 will improve performance in four different scenarios:\n\n1. **Skip sorter** - applied when there is no sorting to be done. The query can return once it reaches the `LIMIT` of requested results.\n1. **Partial range** - applied when there is a `SORTBY` on a numeric field, either with no filter or with a filter by the same numeric field. Such queries will iterate on a range large enough to satisfy the `LIMIT` of requested results.\n1. **Hybrid** - applied when there is a `SORTBY` on a numeric field in addition to another non-numeric filter. It could be the case that some results will get filtered, leaving too small a range to satisfy any specified `LIMIT`. In such cases, the iterator then is re-wound and additional iterations occur to collect result up to the requested `LIMIT`.\n1. **No optimization** - If there is a sort by score or by a non-numeric field, there is no other option but to retrieve all results and compare their values to the search parameters.\n\nYou can also use `WITHOUTCOUNT` in place of `DIALECT 4` when used with either FT.SEARCH or FT.AGGREGATE."},{"id":"use-ftexplaincli-to-compare-dialects","title":"Use `FT.EXPLAINCLI` to compare dialects","role":"content","text":"The [`FT.EXPLAINCLI`](https://redis.io/docs/latest/commands/ft.explaincli) command is a powerful tool that provides a window into the inner workings of your queries. It's like a roadmap that details your query's journey from start to finish.\n\nWhen you run [`FT.EXPLAINCLI`](https://redis.io/docs/latest/commands/ft.explaincli), it returns an array representing the execution plan of a complex query. This plan is a step-by-step guide of how Redis interprets your query and how it plans to fetch results. It's a behind-the-scenes look at the process, giving you insights into how the search engine works.\n\nThe [`FT.EXPLAINCLI`](https://redis.io/docs/latest/commands/ft.explaincli) accepts a `DIALECT` argument, allowing you to execute the query using different dialect versions, allowing you to compare the resulting query plans.\n\nTo use [`FT.EXPLAINCLI`](https://redis.io/docs/latest/commands/ft.explaincli), you need to provide an index and a query predicate. The index is the name of the index you created using [`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create), and the query predicate is the same as if you were sending it to [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) or [`FT.AGGREGATE`](https://redis.io/docs/latest/commands/ft.aggregate).\n\nHere's an example of how to use [`FT.EXPLAINCLI`](https://redis.io/docs/latest/commands/ft.explaincli) to understand differences in dialect versions 1 and 2.\n\nNegation of the intersection between tokens `hello` and `world`:\n\n[code example]\n\nIntersection of the negation of the token `hello` together with token `world`:\n\n[code example]\n\nSame result as `DIALECT 1`:\n\n[code example]\n\n> [!NOTE]\n> [`FT.EXPLAIN`](https://redis.io/docs/latest/commands/ft.explain) doesn't execute the query. It only explains the plan. It's a way to understand how your query is interpreted by Redis Search, which can be invaluable when you're trying to optimize your searches."},{"id":"change-the-default-dialect","title":"Change the default dialect","role":"content","text":"The default dialect is `DIALECT 1`. If you wish to change that, you can do so by using the `DEFAULT_DIALECT` parameter when loading the RediSearch module:\n\n[code example]\n\nYou can also change the query dialect on an already running server using the `FT.CONFIG` command:\n\n[code example]"}],"examples":[{"id":"dialect-3-deprecated-ex0","language":"plaintext","code":"{\n  \"id\": 123,\n  \"underlyings\": [\n    {\n      \"currency\": \"USD\",\n      \"spot\": 99,\n      \"underlier\": \"AAPL UW\"\n    },\n    {\n      \"currency\": \"USD\",\n      \"spot\": 100,\n      \"underlier\": \"NFLX UW\"\n    }\n  ]\n}","section_id":"dialect-3-deprecated"},{"id":"dialect-3-deprecated-ex1","language":"plaintext","code":"FT.CREATE js_idx ON JSON PREFIX 1 js: SCHEMA $.underlyings[*].underlier AS und TAG","section_id":"dialect-3-deprecated"},{"id":"dialect-3-deprecated-ex2","language":"plaintext","code":"ft.search js_idx * return 1 und\n      1) (integer) 1\n      2) \"js:1\"\n      3) 1) \"und\"\n         2) \"AAPL UW\"","section_id":"dialect-3-deprecated"},{"id":"dialect-3-deprecated-ex3","language":"plaintext","code":"ft.search js_idx * return 1 und DIALECT 3\n        1) (integer) 1\n        2) \"js:1\"\n        3) 1) \"und\"\n           2) \"[\\\"AAPL UW\\\",\\\"NFLX UW\\\"]\"","section_id":"dialect-3-deprecated"},{"id":"use-ftexplaincli-to-compare-dialects-ex0","language":"FT.EXPLAINCLI idx:dialects \"-hello world\" DIALECT 1","code":"1) NOT {\n2)   INTERSECT {\n3)     hello\n4)     world\n5)   }\n6) }\n7)","section_id":"use-ftexplaincli-to-compare-dialects"},{"id":"use-ftexplaincli-to-compare-dialects-ex1","language":"plaintext","code":"FT.EXPLAINCLI idx:dialects \"-hello world\" DIALECT 2\n 1) INTERSECT {\n 2)   NOT {\n 3)     hello\n 4)   }\n 5)   UNION {\n 6)     world\n 7)     +world(expanded)\n 8)   }\n 9) }\n10)","section_id":"use-ftexplaincli-to-compare-dialects"},{"id":"use-ftexplaincli-to-compare-dialects-ex2","language":"plaintext","code":"FT.EXPLAINCLI idx:dialects \"-(hello world)\" DIALECT 2\n1) NOT {\n2)   INTERSECT {\n3)     hello\n4)     world\n5)   }\n6) }\n7)","section_id":"use-ftexplaincli-to-compare-dialects"},{"id":"change-the-default-dialect-ex0","language":"plaintext","code":"$ redis-server --loadmodule ./redisearch.so DEFAULT_DIALECT 2","section_id":"change-the-default-dialect"},{"id":"change-the-default-dialect-ex1","language":"plaintext","code":"FT.CONFIG SET DEFAULT_DIALECT 2","section_id":"change-the-default-dialect"}]}
{"schema_version":2,"id":"develop/ai/search-and-query/advanced-concepts/escaping","title":"Tokenization","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/escaping/","summary":"Controlling text tokenization and escaping","aliases":["/develop/interact/search-and-query/advanced-concepts/escaping"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:33:04+01:00","page_type":"content","content_hash":"a08aa188eda0bdb7d1a441c9f075a4a55a8772e9dc02970639861172c348b3b3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Full-text search works by comparing words, URLs, numbers, and other elements of the query\nagainst the text in the searchable fields of each document. However,\nit would be very inefficient to compare the entire text of the query against the\nentire text of each field over and over again, so the search system doesn't do this.\nInstead, it splits the document text into short, significant sections\ncalled *tokens* during the indexing process and stores the tokens as part of the document's\nindex data.\n\nDuring a search, the query system also tokenizes the\nquery text and then simply compares the tokens from the query against the tokens stored\nfor each document. Finding a match like this is much more efficient than pattern-matching on\nthe whole text and also lets you use\n[stemming](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/stemming) and\n[stop words](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/stopwords)\nto improve the search even further. See this article about\n[Tokenization](https://queryunderstanding.com/tokenization-c8cdd6aef7ff)\nfor a general introduction to the concepts.\n\nRedis uses a very simple tokenizer for documents and a slightly more sophisticated tokenizer for queries. Both allow a degree of control over string escaping and tokenization. \n\nThe sections below describe the rules for tokenizing text fields and queries.\nNote that\n[Tag fields](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/tags) \nare essentially text fields but they use a simpler form of tokenization, as described\nseparately in the\n[Tokenization rules for tag fields](#tokenization-rules-for-tag-fields) section."},{"id":"tokenization-rules-for-text-fields","title":"Tokenization rules for text fields","role":"content","text":"1. All punctuation marks and whitespace (besides underscores) separate the document and queries into tokens. For example, any character of `,.<>{}[]\"':;!@#$%^&*()-+=~` will break the text into terms, so the text `foo-bar.baz...bag` will be tokenized into `[foo, bar, baz, bag]`\n\n2. Escaping separators in both queries and documents is done by prepending a backslash to any separator. For example, the text `hello\\-world hello-world` will be tokenized as `[hello-world, hello, world]`. In most languages you will need an extra backslash to signify an actual backslash when formatting the document or query, so the actual text entered into redis-cli will be `hello\\\\-world`. \n\n3. Underscores (`_`) are not used as separators in either document or query, so the text `hello_world` will remain as is after tokenization. \n\n4. Repeating spaces or punctuation marks are stripped. \n\n5. Latin characters are converted to lowercase. \n\n6. A backslash before the first digit will tokenize it as a term. This will translate the `-` sign as NOT, which otherwise would make the number negative. Add a backslash before `.` if you are searching for a float. For example, `-20 -> {-20} vs -\\20 -> {NOT{20}}`."},{"id":"tokenization-rules-for-tag-fields","title":"Tokenization rules for tag fields","role":"content","text":"[Tag fields](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/tags) interpret\na text field as a list of *tags* delimited by a\n[separator](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/tags#creating-a-tag-field)\ncharacter (which is a comma \",\" by\ndefault). The tokenizer simply splits the text wherever it finds the separator and so most\npunctuation marks and whitespace are valid characters within each tag token. The only\nchanges that the tokenizer makes to the tags are:\n\n-   Trimming whitespace at the start and end of the tag. Other whitespace in the tag text is left intact.\n-   Converting Latin alphabet characters to lowercase. You can override this by adding the\n    [`CASESENSITIVE`](https://redis.io/docs/latest/develop/ai/search-and-query/indexing/field-and-type-options#tag-fields) option in the indexing schema for the tag field.\n\nThis means that when you define a tag field, you don't need to escape any characters, except\nin the unusual case where you want leading or trailing spaces to be part of the tag text.\nHowever, you do need to escape certain characters in a *query* against a tag field. See the\n[Query syntax](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/query_syntax#tag-filters) and\n[Exact match](https://redis.io/docs/latest/develop/ai/search-and-query/query/exact-match) pages for more information about escaping\nand how to use [DIALECT 2](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/dialects#dialect-2), which is required for\nexact match queries involving tags."}],"examples":[]}
{"schema_version":2,"id":"develop/ai/search-and-query/advanced-concepts/expiration","title":"Key and field expiration behavior","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/expiration/","summary":"How Redis Search handles expiring keys and hash fields","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:33:04+01:00","page_type":"content","content_hash":"98ee8be89efcad536798ac8dddb20fd6adc710418c3ee83dff4c198118717f64","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The Redis Search behavior with expiring keys and hash fields has been enhanced starting with Redis 8 to provide more consistent and predictable results."},{"id":"key-expiration","title":"Key expiration","role":"content","text":""},{"id":"expiration-times","title":"Expiration times","role":"content","text":"**Before Redis 8**: Expiration times were not taken into account when computing the result set.\n\n**Redis 8 and later**: Redis Search returns only documents that are valid (not expired) at the time when the query or cursor read started."},{"id":"active-expiration","title":"Active expiration","role":"content","text":"Active expiration can affect the number of results returned. For example, if a user requests 100 documents but 10 are actively expired during query execution, only 90 documents will be returned.\n\n**Note**: This behavior did not change in Redis 8 - active expiration has always affected result counts."},{"id":"passive-expiration","title":"Passive expiration","role":"content","text":"**Before Redis 8**: A query could return `nil` as a document name in the result set for a key that was passively expired.\n\n**Redis 8 and later**: Only valid document names will be returned. Passively expired keys are filtered out from the result set."},{"id":"field-expiration","title":"Field expiration","role":"content","text":"Field expiration was introduced in Redis 7.4 and provides fine-grained control over hash field lifecycles."},{"id":"expiration-times-1","title":"Expiration times","role":"content","text":"**Before Redis 8**: Field expiration times were not taken into account when computing the result set.\n\n**Redis 8 and later**: Redis Search returns only documents that are valid (fields not expired) at the time when the query or cursor read started."},{"id":"active-expiration-1","title":"Active expiration","role":"content","text":"Similar to key expiration, active field expiration can affect the number of results returned. If fields that match query criteria are actively expired during execution, fewer results will be returned than requested.\n\n**Note**: This behavior did not change in Redis 8 - active expiration has always affected result counts."},{"id":"passive-expiration-1","title":"Passive expiration","role":"content","text":"**Before Redis 8**: Redis Search could return documents without fields that were passively expired, even if the expired field caused the document to match the query. This behavior depended to some degree on the `SORTABLE` keyword usage.\n\n**Redis 8 and later**: Documents will return with all fields that existed when the query or cursor read started. Passively expired fields are handled consistently."},{"id":"best-practices","title":"Best practices","role":"content","text":"* **Consistent timing**: Be aware that query results are based on the state at query start time, not when individual documents are processed.\n\n* **Result count expectations**: Be aware that when you use expiring keys or fields, the actual number of results may be less than you expected due to active expiration during the execution of the query.\n\n* **Field-level expiration**: Use hash field expiration (available since Redis 7.4) for fine-grained control over document field lifecycles without affecting the entire document.\n\n* **Query planning**: Consider expiration patterns when designing queries, especially for time-sensitive applications where expired data should not appear in results."},{"id":"related-commands","title":"Related commands","role":"related","text":"The following commands are directly related to key and field expiration:\n\n- [`EXPIRE`](https://redis.io/docs/latest/commands/expire) - Set expiration time for keys\n- [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) - Set expiration time for hash fields\n- [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) - Search queries affected by expiration behavior\n- [`FT.AGGREGATE`](https://redis.io/docs/latest/commands/ft.aggregate) - Aggregation queries affected by expiration behavior"}],"examples":[]}
{"schema_version":2,"id":"develop/ai/search-and-query/advanced-concepts/geo","title":"Geospatial","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/geo/","summary":"Learn how to use geospatial fields and perform geospatial queries in Redis","aliases":["/develop/interact/search-and-query/advanced-concepts/geo","/develop/ai/search-and-query/indexing/geo"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-29T16:29:37+01:00","page_type":"content","content_hash":"5157e7bc5ec5a0b09d21034884e04412ad15dd7513fba4d0b8e2cd37754ea1d9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Search supports geospatial data. This feature\nlets you store geographical locations and geometric shapes\nin the fields of JSON objects.\n\n> [!NOTE]\n> Take care not to confuse the geospatial indexing\n> features in Redis Search with the\n> [Geospatial data type](https://redis.io/docs/latest/develop/data-types/geospatial)\n> that Redis also supports. Although there are some similarities between\n> these two features, the data type is intended for simpler use\n> cases and doesn't have the range of format options and queries\n> available in Redis Search.\n\nYou can index these fields and use queries to find the objects\nby their location or the relationship of their shape to other shapes.\nFor example, if you add the locations of a set of shops, you can\nfind all the shops within 5km of a user's position or determine\nwhich ones are within the boundary of a particular town.\n\nRedis uses coordinate points to represent geospatial locations.\nYou can store individual points but you can also\nuse a set of points to define a polygon shape (the shape of a\ntown, for example). You can query several types of interactions\nbetween points and shapes, such as whether a point lies within\na shape or whether two shapes overlap.\n\nRedis can interpret coordinates either as geographical longitude\nand latitude or as Cartesian coordinates on a flat plane.\nGeographical coordinates are ideal for large real-world locations\nand areas (such as towns and countries). Cartesian coordinates\nare more suitable for smaller areas (such as rooms in a building)\nor for games, simulations, and other artificial scenarios."},{"id":"storing-geospatial-data","title":"Storing geospatial data","role":"content","text":"Redis supports two different\n[schema types](https://redis.io/docs/latest/develop/ai/search-and-query/indexing/field-and-type-options)\nfor geospatial data:\n\n-   [`GEO`](#geo): This uses a simple format where individual geospatial\n    points are specified as numeric longitude-latitude pairs.\n    \n-   [`GEOSHAPE`](#geoshape): [Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack) also\n    supports `GEOSHAPE` indexing in v7.2 and later.\n    This uses a subset of the \n    [Well-Known Text (WKT)](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry)\n    format to specify both points and polygons using either geographical\n    coordinates or Cartesian coordinates. A\n    `GEOSHAPE` field supports more advanced queries than `GEO`,\n    such as checking if one shape overlaps or contains another.\n\nThe sections below describe these schema types in more detail."},{"id":"geo","title":"`GEO`","role":"content","text":"A `GEO` index lets you represent geospatial data either as\na string containing a longitude-latitude pair (for example,\n\"-104.991531, 39.742043\") or as a JSON array of these\nstrings. Note that the longitude value comes first in the\nstring.\n\nFor example, you could index the `location` fields of the\nthe [JSON](https://redis.io/docs/latest/develop/data-types/json) objects\nshown below as `GEO`:\n\n[code example]\n\n`GEO` fields allow only basic point and radius queries.\nFor example, the query below finds products within a 100 mile radius of Colorado Springs\n(Longitude=-104.800644, Latitude=38.846127).\n\n[code example]\n\nNote that `GEO` fields are stored in C `double` type variables and they are limited to approximately 15 to 17 digits of precision.\n\nSee [Geospatial queries](https://redis.io/docs/latest/develop/ai/search-and-query/query/geo-spatial)\nfor more information about the available query options and see\n[Geospatial indexing](https://redis.io/docs/latest/develop/ai/search-and-query/indexing/geoindex)\nfor examples of indexing `GEO` fields."},{"id":"geoshape","title":"`GEOSHAPE`","role":"content","text":"Fields indexed as `GEOSHAPE` support the `POINT` and `POLYGON` primitives from the\n[Well-Known Text](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry)\nrepresentation of geometry. The `POINT` primitive defines a single point\nin a similar way to a `GEO` field.\nThe `geom` field of the example JSON object shown below specifies a point\n(in Cartesian coordinates, using the standard x,y order):\n\n[code example]\n\nThe `POLYGON` primitive can approximate the outline of any shape using a\nsequence of points. Specify the coordinates of the corners in the order they\noccur around the shape (either clockwise or counter-clockwise) and ensure the\nshape is \"closed\" by making the final coordinate exactly the same as the first.\n\nNote that `POLYGON` requires double parentheses around the coordinate list.\nThis is because you can specify additional shapes as a comma-separated list\nthat define \"holes\" within the enclosing polygon. The holes must have the opposite\nwinding order to the outer polygon (so, if the outer polygon uses a clockwise winding\norder, the holes must use counter-clockwise).\nThe `geom` field of the example JSON object shown below specifies a\nsquare using Cartesian coordinates in a clockwise winding order:\n\n[code example]\n\nThe following examples define one `POINT` and three `POLYGON` primitives,\nwhich are shown in the image below:\n\n[code example]\n\n![](https://redis.io/docs/latest/images/dev/rqe/geoshapes.jpg)\n\nYou can run various types of queries against a geospatial index. For\nexample, the query below returns one primitive that lies within the boundary\nof the green square (from the example above) but omits the square itself:\n\n[code example]\n\nThere are four query operations that you can use with `GEOSHAPE` fields:\n\n-   `WITHIN`: Find points or shapes that lie entirely within an\n    enclosing shape that you specify in the query.\n-   `CONTAINS`: Find shapes that completely contain the specified point\n    or shape.\n-   `INTERSECTS`: Find shapes whose boundary overlaps another specified\n    shape.\n-   `DISJOINT`: Find shapes whose boundary does not overlap another specified\n    shape.\n\nSee\n[Geospatial queries](https://redis.io/docs/latest/develop/ai/search-and-query/query/geo-spatial)\nfor more information about these query types and see\n[Geospatial indexing](https://redis.io/docs/latest/develop/ai/search-and-query/indexing/geoindex)\nfor examples of indexing `GEOSHAPE` fields."},{"id":"limitations-of-geographical-coordinates","title":"Limitations of geographical coordinates","role":"limits","text":"Planet Earth is actually shaped more like an\n[ellipsoid](https://en.wikipedia.org/wiki/Earth_ellipsoid) than a perfect sphere.\nThe spherical coordinate system used by Redis Search is a close\napproximation to the shape of the Earth but not exact. For most practical\nuses of geospatial queries, the approximation works very well, but you\nshouldn't rely on it if you need very precise location data (for example, to track\nthe GPS locations of boats in an emergency response system)."}],"examples":[{"id":"geo-ex0","language":"json","code":"{\n    \"description\": \"Navy Blue Slippers\",\n    \"price\": 45.99,\n    \"city\": \"Denver\",\n    \"location\": \"-104.991531, 39.742043\"\n}\n\n{\n    \"description\": \"Bright Red Boots\",\n    \"price\": 185.75,\n    \"city\": \"Various\",\n    \"location\": [\n        \"-104.991531, 39.742043\",\n        \"-105.0618814,40.5150098\"\n    ]\n}","section_id":"geo"},{"id":"geo-ex1","language":"bash","code":"FT.SEARCH productidx '@location:[-104.800644 38.846127 100 mi]'","section_id":"geo"},{"id":"geoshape-ex0","language":"json","code":"{\n    \"name\": \"Purple Point\",\n    \"geom\": \"POINT (2 2)\"\n}","section_id":"geoshape"},{"id":"geoshape-ex1","language":"json","code":"{\n    \"name\": \"Green Square\",\n    \"geom\": \"POLYGON ((1 1, 1 3, 3 3, 3 1, 1 1))\"\n}","section_id":"geoshape"},{"id":"geoshape-ex2","language":"plaintext","code":"POINT (2 2)\nPOLYGON ((1 1, 1 3, 3 3, 3 1, 1 1))\nPOLYGON ((2 2.5, 2 3.5, 3.5 3.5, 3.5 2.5, 2 2.5))\nPOLYGON ((3.5 1, 3.75 2, 4 1, 3.5 1))","section_id":"geoshape"},{"id":"geoshape-ex3","language":"bash","code":"> FT.SEARCH geomidx \"(-@name:(Green Square) @geom:[WITHIN $qshape])\" PARAMS 2 qshape \"POLYGON ((1 1, 1 3, 3 3, 3 1, 1 1))\" RETURN 1 name DIALECT 2\n\n1) (integer) 1\n2) \"shape:4\"\n3) 1) \"name\"\n   2) \"[\\\"Purple Point\\\"]\"","section_id":"geoshape"}]}
{"schema_version":2,"id":"develop/ai/search-and-query/advanced-concepts/highlight","title":"Highlighting","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/highlight/","summary":"Highlighting full-text results","aliases":["/develop/interact/search-and-query/advanced-concepts/highlight"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2025-11-18T12:32:34-08:00","page_type":"content","content_hash":"a992ae084c1282d64f317ec50a1edc04124b6b39f2046434ed39645d49da1f5d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Open Source uses advanced algorithms for highlighting and summarizing, which enable only the relevant portions of a document to appear in response to a search query. This feature allows users to immediately understand the relevance of a document to their search criteria, typically highlighting the matching terms in bold text."},{"id":"command-syntax","title":"Command syntax","role":"syntax","text":"[code example]\n\nThere are two sub-commands used for highlighting. The first is `HIGHLIGHT`, which surrounds matching text with an open and/or close tag. The second is `SUMMARIZE`, which splits a field into contextual fragments surrounding the found terms. It is possible to summarize a field, highlight a field, or perform both actions in the same query."},{"id":"summarization","title":"Summarization","role":"content","text":"[code example]\n\nSummarization will fragment the text into smaller sized snippets, each of which containing the found term(s) and some additional surrounding context.\n\nRedis can perform summarization using the `SUMMARIZE` keyword. If no additional arguments are passed, all returned fields are summarized using built-in defaults.\n\nThe `SUMMARIZE` keyword accepts the following arguments:\n\n* **`FIELDS`**: If present, it must be the first argument. This should be followed\n    by the number of fields to summarize, which itself is followed by a list of\n    fields. Each field is summarized. If no `FIELDS` directive is passed,\n    then all returned fields are summarized.\n\n* **`FRAGS`**: The number of fragments to be returned. If not specified, a default is 3.\n\n* **`LEN`**: The number of context words each fragment should contain. Context\n    words surround the found term. A higher value will return a larger block of\n    text. If not specified, the default value is 20.\n\n* **`SEPARATOR`**: The string used to divide individual summary snippets.\n    The default is `... ` which is common among search engines, but you may\n    override this with any other string if you desire to programmatically divide the snippets\n    later on. You may also use a newline sequence, as newlines are stripped from the\n    result body during processing."},{"id":"highlighting","title":"Highlighting","role":"content","text":"[code example]\n\nHighlighting will surround the found term (and its variants) with a user-defined pair of tags. This may be used to display the matched text in a different typeface using a markup language, or to otherwise make the text appear differently.\n\nRedis performs highlighting using the `HIGHLIGHT` keyword. If no additional arguments are passed, all returned fields are highlighted using built-in defaults.\n\nThe `HIGHLIGHT` keyword accepts the following arguments:\n\n* **`FIELDS`**: If present, it must be the first argument. This should be followed\n    by the number of fields to highlight, which itself is followed by a list of\n    fields. Each field present is highlighted. If no `FIELDS` directive is passed,\n    then all returned fields are highlighted.\n    \n* **`TAGS`**: If present, it must be followed by two strings. The first string is prepended\n    to each matched term. The second string is appended to each matched term. If no `TAGS` are\n    specified, a built-in tag pair is prepended and appended to each matched term.\n\n\n#### Field selection\n\nIf no specific fields are passed to the `RETURN`, `SUMMARIZE`, or `HIGHLIGHT` keywords, then all of a document's fields are returned. However, if any of these keywords contain a `FIELD` directive, then the `SEARCH` command will only return the sum total of all fields enumerated in any of those directives.\n\nThe `RETURN` keyword is treated specially, as it overrides any fields specified in `SUMMARIZE` or `HIGHLIGHT`.\n\nIn the command `RETURN 1 foo SUMMARIZE FIELDS 1 bar HIGHLIGHT FIELDS 1 baz`, the fields `foo` is returned as-is, while `bar` and `baz` are not returned, because `RETURN` was specified, but did not include those fields.\n\nIn the command `SUMMARIZE FIELDS 1 bar HIGHLIGHT FIELDS 1 baz`, `bar` is returned summarized and `baz` is returned highlighted."}],"examples":[{"id":"command-syntax-ex0","language":"plaintext","code":"FT.SEARCH ...\n    SUMMARIZE [FIELDS {num} {field}] [FRAGS {numFrags}] [LEN {fragLen}] [SEPARATOR {sepstr}]\n    HIGHLIGHT [FIELDS {num} {field}] [TAGS {openTag} {closeTag}]","section_id":"command-syntax"},{"id":"summarization-ex0","language":"plaintext","code":"FT.SEARCH ...\n    SUMMARIZE [FIELDS {num} {field}] [FRAGS {numFrags}] [LEN {fragLen}] [SEPARATOR {sepStr}]","section_id":"summarization"},{"id":"highlighting-ex0","language":"plaintext","code":"FT.SEARCH ... HIGHLIGHT [FIELDS {num} {field}] [TAGS {openTag} {closeTag}]","section_id":"highlighting"}]}
{"schema_version":2,"id":"develop/ai/search-and-query/advanced-concepts","title":"Search concepts","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/","summary":"Details about query syntax, aggregation, scoring, and other search and query options","aliases":["/develop/interact/search-and-query/advanced-concepts","/develop/interact/search-and-query/advanced-concepts/","/interact/search-and-query/advanced-concepts/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:33:04+01:00","children":[{"id":"develop/ai/search-and-query/advanced-concepts/aggregations","summary":"Groupings, projections, and aggregation functions","title":"Aggregations","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/aggregations/"},{"id":"develop/ai/search-and-query/advanced-concepts/aggregations-syntax","summary":"Order of operations for the FT.AGGREGATE command","title":"FT.AGGREGATE order of operations","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/aggregations-syntax/"},{"id":"develop/ai/search-and-query/advanced-concepts/autocomplete","summary":"Learn how to use the autocomplete feature of Redis for efficient prefix-based suggestion retrieval.","title":"Autocomplete with Redis","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/autocomplete/"},{"id":"develop/ai/search-and-query/advanced-concepts/escaping","summary":"Controlling text tokenization and escaping","title":"Tokenization","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/escaping/"},{"id":"develop/ai/search-and-query/advanced-concepts/stopwords","summary":"Stop words support","title":"Stop words","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/stopwords/"},{"id":"develop/ai/search-and-query/advanced-concepts/dialects","summary":"Learn how to use query dialects","title":"Query dialects","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/dialects/"},{"id":"develop/ai/search-and-query/advanced-concepts/query_syntax","summary":"Learn how to use query syntax","title":"Query syntax","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/query_syntax/"},{"id":"develop/ai/search-and-query/advanced-concepts/query_attributes","summary":"Learn how to use query attributes","title":"Query attributes","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/query_attributes/"},{"id":"develop/ai/search-and-query/advanced-concepts/sorting","summary":"Support for sorting query results","title":"Sorting by indexed fields","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/sorting/"},{"id":"develop/ai/search-and-query/advanced-concepts/tags","summary":"How to use tag fields for exact match searches and high-performance filtering","title":"Tag fields","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/tags/"},{"id":"develop/ai/search-and-query/advanced-concepts/highlight","summary":"Highlighting full-text results","title":"Highlighting","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/highlight/"},{"id":"develop/ai/search-and-query/advanced-concepts/expiration","summary":"How Redis Search handles expiring keys and hash fields","title":"Key and field expiration behavior","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/expiration/"},{"id":"develop/ai/search-and-query/advanced-concepts/transactions","summary":"How Redis Search commands behave inside MULTI/EXEC transactions and Lua scripts","title":"Search commands in MULTI/EXEC transactions and Lua scripts","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/transactions/"},{"id":"develop/ai/search-and-query/advanced-concepts/scoring","summary":"Full-text scoring functions","title":"Scoring documents","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/scoring/"},{"id":"develop/ai/search-and-query/advanced-concepts/stemming","summary":"Stemming support","title":"Stemming","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/stemming/"},{"id":"develop/ai/search-and-query/advanced-concepts/synonyms","summary":"Details on synonym support with Redis Open Source","title":"Synonym support","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/synonyms/"},{"id":"develop/ai/search-and-query/advanced-concepts/spellcheck","summary":"Query spelling correction support","title":"Spellchecking","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/spellcheck/"},{"id":"develop/ai/search-and-query/advanced-concepts/geo","summary":"Learn how to use geospatial fields and perform geospatial queries in Redis","title":"Geospatial","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/geo/"},{"id":"develop/ai/search-and-query/advanced-concepts/phonetic_matching","summary":"Details about phonetic matching capabilities","title":"Phonetic matching","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/phonetic_matching/"},{"id":"develop/ai/search-and-query/advanced-concepts/chinese","summary":"Chinese support for searching and querying in Redis Open Source","title":"Chinese support","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/chinese/"}],"page_type":"content","content_hash":"f33c550c3917b1ae6ea7655749ad864811437cad733468561e42f045b4c155d5","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Open Source supports the following Redis Search features. This article provides you an overview."},{"id":"indexing-features","title":"Indexing features","role":"content","text":"* Secondary indexing\n* Vector indexing\n* Index on [JSON](https://redis.io/docs/latest/develop/data-types/json) documents\n* Full-text indexing of multiple fields in a document\n* Incremental indexing without performance loss\n* Document deletion and updating with index garbage collection"},{"id":"query-features","title":"Query features","role":"content","text":"* Multi-field queries\n* Query on [JSON](https://redis.io/docs/latest/develop/data-types/json) documents\n* [Aggregation](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/aggregations)\n* Boolean queries with AND, OR, and NOT operators between subqueries\n* Optional query clauses\n* Retrieval of full document contents or only their IDs\n* Exact phrase search and slop-based search\n* Numeric filters and ranges\n* Geo-filtering using Redis [geo commands](/commands/?group=geo)\n* [Vector search](https://redis.io/docs/latest/develop/ai/search-and-query/vectors)\n* [Key and field expiration behavior](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/expiration)\n* [Search commands in MULTI/EXEC transactions and Lua scripts](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/transactions)"},{"id":"full-text-search-features","title":"Full-text search features","role":"content","text":"* [Prefix-based searches](https://redis.io/docs/latest/develop/ai/search-and-query/query#prefix-matching)\n* Field weights\n* [Auto-complete](https://redis.io/docs/latest/develop/ai/search-and-query/administration/overview#auto-complete) and fuzzy prefix suggestions\n* [Stemming](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/stemming)-based query expansion for [many languages](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/stemming#supported-languages) using [Snowball](http://snowballstem.org/)\n* Support for custom functions for query expansion and scoring (see [Extensions](https://redis.io/docs/latest/develop/ai/search-and-query/administration/extensions))\n* Unicode support (UTF-8 input required)\n* Document ranking"},{"id":"cluster-support","title":"Cluster support","role":"content","text":"The Redis Search features of Redis Open Source are also available for distributed databases that can scale to billions of documents and hundreds of servers."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"Redis Open Source is developed and tested on Linux and macOS on x86_64 CPUs.\n\nAtom CPUs are not supported.\n\n<br/>"}],"examples":[]}
{"schema_version":2,"id":"develop/ai/search-and-query/advanced-concepts/phonetic_matching","title":"Phonetic matching","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/phonetic_matching/","summary":"Details about phonetic matching capabilities","aliases":["/develop/interact/search-and-query/advanced-concepts/phonetic_matching"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:33:04+01:00","page_type":"content","content_hash":"2c81cc7f711aa5a2a802c51af4e0760a346ad59b7715b704038eb885687b0f33","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Phonetic matching, for example \"Jon\" vs. \"John\", allows searching for terms based on their pronunciation. This capability can be a useful tool when searching for names of people.\n\nPhonetic matching is based on the use of a phonetic algorithm. A phonetic algorithm transforms the input term to an approximate representation of its pronunciation. This allows terms to be indexed and searched by their pronunciation.\n\nAs of v1.4, Redis Search, which is included in Redis Open Source, provides phonetic matching of text fields specified with the `PHONETIC` attribute. This causes the terms in such fields to be indexed both by their textual value as well as their phonetic approximation.\n\nPerforming a search on `PHONETIC` fields will, by default, also return results for phonetically similar terms. This behavior can be controlled with the [`$phonetic` query attribute](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/query_attributes)."},{"id":"phonetic-algorithms-support","title":"Phonetic algorithms support","role":"content","text":"Redis currently supports a single phonetic algorithm, the [Double Metaphone](https://en.wikipedia.org/wiki/Metaphone#Double_Metaphone) (DM). It uses the implementation at the [slacy/double-metaphone GitHub site](https://github.com/slacy/double-metaphone), which provides general support for Latin languages."}],"examples":[]}
{"schema_version":2,"id":"develop/ai/search-and-query/advanced-concepts/query_attributes","title":"Query attributes","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/query_attributes/","summary":"Learn how to use query attributes","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:33:04+01:00","page_type":"content","content_hash":"3cd4797c3aaae74b6ff4b64d7bd0ae3fb08256c9edd20dcc05db12818cc62e1c","sections":[{"id":"query-attributes","title":"Query attributes","role":"overview","text":"As of v1.2.0, you can apply specific query modifying attributes to specific clauses of the query.\n\nThe syntax is `(foo bar) => { $attribute: value; $attribute:value; ...}`:\n\n[code example]\n\nThe supported attributes are:\n\n1. **$weight**: determines the weight of the sub-query or token in the overall ranking on the result (default: 1.0).\n2. **$slop**: determines the maximum allowed slop (space between terms) in the query clause (default: 0).\n3. **$inorder**: whether or not the terms in a query clause must appear in the same order as in the query. This is usually set alongside with `$slop` (default: false).\n4. **$phonetic**: whether or not to perform phonetic matching (default: true). Note: setting this attribute to true for fields which were not created as `PHONETIC` will produce an error.\n\nAs of v2.6.1, the query attributes syntax supports these additional attributes:\n\n* **$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).\n* **$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](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#cluster-specific-query-parameters) for detailed information.\n* **vector query params**: pass optional parameters for [vector queries](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#querying-vector-fields) in key-value format."}],"examples":[{"id":"query-attributes-ex0","language":"plaintext","code":"(foo bar) => { $weight: 2.0; $slop: 1; $inorder: true; }\n~(bar baz) => { $weight: 0.5; }","section_id":"query-attributes"}]}
{"schema_version":2,"id":"develop/ai/search-and-query/advanced-concepts/query_syntax","title":"Query syntax","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/query_syntax/","summary":"Learn how to use query syntax","aliases":["/develop/interact/search-and-query/advanced-concepts/query_syntax","/interact/search-and-query/advanced-concepts/query_syntax/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-29T16:29:37+01:00","page_type":"content","content_hash":"bef4fa6fee7db1443eb0a729578ab8a631a18329a108ccecdd32c1e6ab06792e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> The query syntax that RediSearch uses has improved over time,\n> adding new features and making queries simpler to write. However,\n> changing the syntax like this could potentially break existing queries that rely on\n> an older version of the syntax. To avoid this problem, RediSearch supports\n> different query syntax *dialects* to ensure backward compatibility.\n> Any breaking changes to the syntax are introduced in a new dialect, while\n> RediSearch continues to support older dialects. This means you can always choose\n> the correct dialect to support the query you are using.\n> See\n> [Query dialects](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/dialects)\n> for full details of the dialects and the RediSearch versions that introduced them."},{"id":"basic-syntax","title":"Basic syntax","role":"content","text":"You can use simple syntax for complex queries using these rules:\n\n* Exact phrases are wrapped in quotes, for example, `\"hello world\"`.\n* Multiword phrases are lists of tokens, for example, `foo bar baz`, and imply intersection (AND) of the terms.\n* `OR` unions are expressed with a pipe (`|`) character, for example, `hello|hallo|shalom|hola`. \n\n   **Notes**:   \n   \n   Consider the differences in parser behavior in example `hello world | \"goodbye\" moon`:\n   * In DIALECT 1, this query is interpreted as searching for `(hello world | \"goodbye\") moon`.\n   * In DIALECT 2 or greater, this query is interpreted as searching for either `hello world` **OR** `\"goodbye\" moon`.\n   \n* `NOT` negation of expressions or subqueries is expressed with a subtraction symbol (`-`), for example, `hello -world`. Purely negative queries such as `-foo` and `-@title:(foo|bar)` are also supported.\n\n   **Notes**:\n   \n   Consider a simple query with negation `-hello world`:\n   * In DIALECT 1, this query is interpreted as \"find values in any field that does not contain `hello` **AND** does not contain `world`\". The equivalent is `-(hello world)` or `-hello -world`.\n   * In DIALECT 2 or greater, this query is interpreted `as -hello` **AND** `world` (only `hello` is negated).\n   * In DIALECT 2 or greater, to achieve the default behavior of DIALECT 1, update your query to `-(hello world)`.\n  \n* Prefix/infix/suffix matches (all terms starting/containing/ending with a term) are expressed with an asterisk `*`. For performance reasons, a minimum term length is enforced. The default is 2, but it's configurable.\n* In DIALECT 2 or greater, wildcard pattern matches are expressed as `\"w'foo*bar?'\"`. Note the use of double quotes to contain the _w_ pattern.\n* A special wildcard query that returns all results in the index is just the asterisk `*`. This cannot be combined with other options.\n* As of v2.6.1, `DIALECT 3` returns JSON rather than scalars from multivalue attributes.\n* Selection of specific fields using the syntax `hello @field:world`.\n* Numeric range matches on numeric fields with the syntax `@field:[{min} {max}]`.\n* Georadius matches on geo fields with the syntax `@field:[{lon} {lat} {radius} {m|km|mi|ft}]`.\n* As of 2.6, range queries on vector fields with the syntax `@field:[VECTOR_RANGE {radius} $query_vec]`, where `query_vec` is given as a query parameter.\n* As of v2.4, k-nearest neighbors (KNN) queries on vector fields with or without pre-filtering with the syntax `{filter_query}=>[KNN {num} @field $query_vec]`.\n* Tag field filters with the syntax `@field:{tag | tag | ...}`. See the full documentation on [tags](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/tags).\n* Optional terms or clauses: `foo ~bar` means bar is optional but documents containing `bar` will rank higher.\n* Fuzzy matching on terms: `%hello%` means all terms with Levenshtein distance of 1 from it. Use multiple pairs of '%' brackets, up to three deep, to increase the Levenshtein distance.\n* An expression in a query can be wrapped in parentheses to disambiguate, for example, `(hello|hella) (world|werld)`.\n* Query attributes can be applied to individual clauses, for example, `(foo bar) => { $weight: 2.0; $slop: 1; $inorder: false; }`.\n* Combinations of the above can be used together, for example, `hello (world|foo) \"bar baz\" bbbb`."},{"id":"pure-negative-queries","title":"Pure negative queries","role":"content","text":"As of v0.19.3, it is possible to have a query consisting of just a negative expression. For example `-hello` or `-(@title:(foo|bar))`. The results are all the documents not containing the query terms.\n\n> [!WARNING]\n> Any complex expression can be negated this way, however, caution should be taken here: if a negative expression has little or no results, this is equivalent to traversing and ranking all the documents in the index, which can be slow and cause high CPU consumption."},{"id":"field-modifiers","title":"Field modifiers","role":"content","text":"You can specify field modifiers in a query, and not just by using the `INFIELDS` global keyword.\n\nTo specify which fields the query matches, prepend the expression with the `@` symbol, the field name, and a `:` (colon) symbol, for each expression or subexpression.\n\nIf a field modifier precedes multiple words or expressions, it applies only to the adjacent expression with DIALECT 1. With DIALECT 2 or greater, you extend the query to other fields.\n\nConsider this simple query: `@name:James Brown`. Here, the field modifier `@name` is followed by two words: `James` and `Brown`.\n\n* In DIALECT 1, this query would be interpreted as \"find `James Brown` in the `@name` field\".\n* In DIALECT 2 or greater, this query will be interpreted as \"find `James` in the `@name` field **AND** `Brown` in **ANY** text field. In other words, it would be interpreted as `(@name:James) Brown`.\n* In DIALECT 2 or greater, to achieve the default behavior of DIALECT 1, update your query to `@name:(James Brown)`.\n\nIf a field modifier precedes an expression in parentheses, it applies only to the expression inside the parentheses. The expression should be valid for the specified field, otherwise it is skipped.\n\nTo create complex filtering on several fields, you can combine multiple modifiers. For example, if you have an index of car models, with a vehicle class, country of origin, and engine type, you can search for SUVs made in Korea with hybrid or diesel engines using the following query:\n\n[code example]\n\nYou can apply multiple modifiers to the same term or grouped terms:\n\n[code example]\n\nNow, you search for documents that have `\"hello\"` and `\"world\"` either in the body or the title and the term `mydomain` in their `url` or `image` fields."},{"id":"numeric-filters-in-query","title":"Numeric filters in query","role":"content","text":"If a field in the schema is defined as NUMERIC, it is possible to use the FILTER argument in the Redis request or filter with it by specifying filtering rules in the query. The syntax is `@field:[{min} {max}]`, for example, `@price:[100 200]`."},{"id":"a-few-notes-on-numeric-predicates","title":"A few notes on numeric predicates","role":"content","text":"1. It is possible to specify a numeric predicate as the entire query, whereas it is impossible to do it with the `FILTER` argument.\n\n2. It is possible to intersect or union multiple numeric filters in the same query, be it for the same field or different ones.\n\n3. `-inf`, `inf` and `+inf` are acceptable numbers in a range. Therefore, _greater than 100_ is expressed as `[(100 inf]`.\n\n4. Numeric filters are inclusive. Exclusive min or max are expressed with `(` prepended to the number, for example, `[(100 (200]`.\n\n5. It is possible to negate a numeric filter by prepending a `-` sign to the filter. For example, returning a result where price differs from 100 is expressed as: `@title:foo -@price:[100 100]`."},{"id":"tag-filters","title":"Tag filters","role":"content","text":"As of v0.91, you can use a special field type called a\n[_tag field_](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/tags), with simpler\n[tokenization](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/escaping#tokenization-rules-for-tag-fields)\nand encoding in the index. You can't access the values in these fields using a general fieldless search. Instead, you use special syntax:\n\n[code example]\n\nExample:\n\n[code example]\n\nTags can have multiple words or include other punctuation marks other than the field's separator (`,` by default). The following characters in tags should be escaped with a backslash (`\\`): `$`, `{`, `}`, `\\`, and `|`.\n\n> [!NOTE]\n> Before RediSearch 2.4, it was also recommended to escape spaces. The reason was that, if a multiword tag included stopwords, a syntax error was returned. So tags, like \"to be or not to be\" needed be escaped as \"to\\ be\\ or\\ not\\ to\\ be\". For good measure, you also could escape all spaces within tags. Starting with RediSearch 2.4, using [`DIALECT 2`](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/dialects#dialect-2) or greater you can use spaces in a `tag` query, even with stopwords.\n\nNotice that multiple tags in the same clause create a union of documents containing either tags. To create an intersection of documents containing all tags, you should repeat the tag filter several times. For example:\n\n[code example]\n\nTag clauses can be combined into any subclause, used as negative expressions, optional expressions, and so on."},{"id":"geo-filters","title":"Geo filters","role":"content","text":"As of v0.21, it is possible to add geo radius queries directly into the query language with the syntax `@field:[{lon} {lat} {radius} {m|km|mi|ft}]`. This filters the result to a given radius from a lon,lat point, defined in meters, kilometers, miles or feet. See Redis's own [`GEORADIUS`](https://redis.io/docs/latest/commands/georadius) command for more details.\n\nRadius filters can be added into the query just like numeric filters. For example, in a database of businesses, looking for Chinese restaurants near San Francisco (within a 5km radius) would be expressed as: `chinese restaurant @location:[-122.41 37.77 5 km]`."},{"id":"polygon-search","title":"Polygon search","role":"content","text":"Geospatial databases are essential for managing and analyzing location-based data in a variety of industries. They help organizations make data-driven decisions, optimize operations, and achieve their strategic goals more efficiently. Polygon search extends Redis's geospatial search capabilities to be able to query against a value in a `GEOSHAPE` attribute. This value must follow a [\"well-known text\"](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry) (WKT) representation of geometry. Two such geometries are supported:\n\n- `POINT`, for example `POINT(2 4)`.\n- `POLYGON`, for example `POLYGON((2 2, 2 8, 6 11, 10 8, 10 2, 2 2))`.\n\nThere is a new schema field type called `GEOSHAPE`, which can be specified as either:\n\n- `FLAT` for Cartesian X Y coordinates\n- `SPHERICAL` for geographic longitude and latitude coordinates. This is the default coordinate system.\n\nFinally, there's new [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) syntax that allows you to query for polygons that either contain or are within a given geoshape.\n\n`@field:[{WITHIN|CONTAINS} $geometry] PARAMS 2 geometry {geometry}`\n\nHere's an example using two stacked polygons that represent a box contained within a house.\n\n![](https://redis.io/docs/latest/images/dev/rqe/polygons.png)\n\nFirst, create an index using a `FLAT` `GEOSHAPE`, representing a 2D X Y coordinate system.\n\n`FT.CREATE polygon_idx PREFIX 1 shape: SCHEMA g GEOSHAPE FLAT t TEXT`\n\nNext, create the data structures that represent the geometries in the picture.\n\n[code example]\nFinally, use [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) to query the geometries. Note the use of `DIALECT 3`, which is required. Here are a few examples.\n\nSearch for a polygon that contains a specified point:\n\n[code example]\n\nSearch for geometries contained in a specified polygon:\n\n[code example]\n\nSearch for a polygon that is not contained in the indexed geometries:\n\n[code example]\n\nSearch for a polygon that is known to be contained within the geometries (the box):\n\n[code example]\n\nNote that both the house and box shapes were returned.\n\n> [!NOTE]\n> GEOSHAPE does not support JSON multi-value or SORTABLE options.\n\nFor more examples, see the [`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create) and [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) command pages."},{"id":"vector-search","title":"Vector search","role":"content","text":"You can add vector similarity queries directly into the query language by:\n\n1. Using a **range** query with the syntax of `@vector:[VECTOR_RANGE {radius} $query_vec]`, which filters the results to a given radius from a given query vector. The distance metric derives from the definition of a @vector field in the index schema, for example, Cosine or L2 (as of v2.6.1).\n\n2. Running a k nearest neighbors (KNN) query on a @vector field. The basic syntax is `\"*=>[ KNN {num|$num} @vector $query_vec ]\"`.\nIt is also possible to run a hybrid query on filtered results. A hybrid query allows the user to specify a filter criteria that all results in a KNN query must satisfy. The filter criteria can include any type of field (i.e., indexes created on both vectors and other values, such as TEXT, PHONETIC, NUMERIC, GEO, etc.).\nThe general syntax for hybrid query is `{some filter query}=>[ KNN {num|$num} @vector $query_vec]`, where `=>` separates the filter query from the vector KNN query. \n\n**Examples:**\n\n* Return 10 nearest neighbors entities in which `query_vec` is closest to the vector stored in `@vector_field`: \n  \n  `*=>[KNN 10 @vector_field $query_vec]`\n\n* Among entities published between 2020 and 2022, return 10 nearest neighbors entities in which `query_vec` is closest to the vector stored in `@vector_field`: \n\n  `@published_year:[2020 2022]=>[KNN 10 @vector_field $query_vec]`\n\n* Return every entity for which the distance between the vector stored under its @vector_field and `query_vec` is at most 0.5, in terms of the @vector_field distance metric:\n\n  `@vector_field:[VECTOR_RANGE 0.5 $query_vec]`\n\nAs of v2.4, the KNN vector search can be used at most once in a query, while, as of v2.6, the vector range filter can be used multiple times in a query. For more information on vector similarity syntax, see [Querying vector fields](https://redis.io/docs/latest/develop/ai/search-and-query/vectors), and [Vector search examples](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#vector-search-examples) sections."},{"id":"prefix-matching","title":"Prefix matching","role":"content","text":"When indexes are updated, Redis maintains a dictionary of all terms in the index. This can be used to match all terms starting with a given prefix. Selecting prefix matches is done by appending `*` to a prefix token. For example:\n\n[code example]\n\nWill be expanded to cover `(hello|help|helm|...) world`."},{"id":"a-few-notes-on-prefix-searches","title":"A few notes on prefix searches","role":"content","text":"1. As prefixes can be expanded into many terms, use them with caution. The expansion will create a Union operation of all suffixes.\n\n2. As a protective measure to avoid selecting too many terms, blocking Redis, which is single threaded, there are two limitations on prefix matching:\n\n  * Prefixes are limited to 2 letters or more. You can change this number by using the `MINPREFIX` setting on the module command line.\n\n  * The minimum word length to stem is 4 letters or more. You can change this number by using the `MINSTEMLEN` setting on the module command line.\n\n  * Expansion is limited to 200 terms or less. You can change this number by using the `MAXEXPANSIONS` setting on the module command line.\n\n3. Prefix matching fully supports Unicode and is case insensitive.\n\n4. Currently, there is no sorting or bias based on suffix popularity."},{"id":"infixsuffix-matching","title":"Infix/suffix matching","role":"content","text":"As of v2.6.0, the dictionary can be used for infix (contains) or suffix queries by appending `*` to the token. For example:\n\n[code example]\n\nThese queries are CPU intensive because they require iteration over the whole dictionary.\n\n> [!NOTE]\n> All notes about prefix searches also apply to infix/suffix queries."},{"id":"using-a-suffix-trie","title":"Using a suffix trie","role":"content","text":"A suffix trie maintains a list of terms that match the suffix. If you add a suffix trie to a field using the `WITHSUFFIXTRIE` keyword, you can create more efficient infix and suffix queries because it eliminates the need to iterate over the whole dictionary. However, the iteration on the union does not change. \n\nSuffix queries create a union of the list of terms from the suffix term node. Infix queries use the suffix terms as prefixes to the trie and create a union of all terms from all matching nodes."},{"id":"wildcard-matching","title":"Wildcard matching","role":"content","text":"As of v2.6.0, you can use the dictionary for wildcard matching queries with these parameters.\n\n* `?` - for any single character\n* `*` - for any character repeating zero or more times\n* `\\` - for escaping; other special characters are ignored\n\nAn example of the syntax is `\"w'foo*bar?'\"`."},{"id":"using-a-suffix-trie-1","title":"Using a suffix trie","role":"content","text":"A suffix trie maintains a list of terms which match the suffix. If you add a suffix trie to a field using the `WITHSUFFIXTRIE` keyword, you can create more efficient wildcard matching queries because it eliminates the need to iterate over the whole dictionary. However, the iteration on the union does not change. \n\nWith a suffix trie, the wildcard pattern is broken into tokens at every `*` character. A heuristic is used to choose the token with the least terms, and each term is matched with the wildcard pattern."},{"id":"fuzzy-matching","title":"Fuzzy matching","role":"content","text":"As of v1.2.0, the dictionary of all terms in the index can also be used to perform [fuzzy matching](https://en.wikipedia.org/wiki/Approximate_string_matching). \nFuzzy matches are performed based on [Levenshtein distance](https://en.wikipedia.org/wiki/Levenshtein_distance) (LD). \nFuzzy matching on a term is performed by surrounding the term with '%', for example:\n\n[code example]\n\nThis performs fuzzy matching on `hello` for all terms where LD is 1.\n\nAs of v1.4.0, the LD of the fuzzy match can be set by the number of '%' characters surrounding it, so that `%%hello%%` will perform fuzzy matching on 'hello' for all terms where LD is 2.\n\nThe maximum LD for fuzzy matching is 3."},{"id":"wildcard-queries","title":"Wildcard queries","role":"content","text":"As of v1.1.0, you can use a special query to retrieve all the documents in an index. This is meant mostly for the aggregation engine. You can call it by specifying only a single star sign as the query string, in other words, `FT.SEARCH myIndex *`.\n\nYou can't combine this with any other filters, field modifiers, or anything inside the query. It is technically possible to use the deprecated `FILTER` and `GEOFILTER` request parameters outside the query string in conjunction with a wildcard, but this makes the wildcard meaningless and only hurts performance."},{"id":"a-few-query-examples","title":"A few query examples","role":"content","text":"* Simple phrase query - `hello` _AND_ `world`:\n\n        hello world\n\n* Exact phrase query - `hello` _FOLLOWED BY_ `world`:\n\n        \"hello world\"\n\n* Union - documents containing either `hello` _OR_ `world`:\n\n        hello|world\n\n* Not - documents containing `hello` _BUT NOT_ `world`:\n\n        hello -world\n\n* Intersection of unions:\n\n        (hello|halo) (world|werld)\n\n* Negation of union:\n\n        hello -(world|werld)\n\n* Union inside phrase:\n\n        (barack|barrack) obama\n\n* Optional terms with higher priority to ones containing more matches:\n\n        obama ~barack ~michelle\n\n* Exact phrase in one field, one word in another field:\n\n        @title:\"barack obama\" @job:president\n\n* Combined _AND_, _OR_ with field specifiers:\n\n        @title:\"hello world\" @body:(foo bar) @category:(articles|biographies)\n\n* Prefix/infix/suffix queries:\n\n        hello worl*\n\n        hel* *worl\n\n        hello -*worl*\n\n* Wildcard matching queries:\n\n        \"w'foo??bar??baz'\"\n\n        \"w'???????'\"\n\n        \"w'hello*world'\"\n\n* Numeric filtering - products named `tv` with a price range of 200 to 500:\n\n        @name:tv @price:[200 500]\n\n* Numeric filtering - users with age greater than 18:\n\n        @age:[(18 +inf]\n\n* Vector search with cluster optimization - retrieve 100 nearest neighbors with each shard providing 50% of results:\n\n        *=>[KNN 100 @doc_embedding $BLOB]=>{$SHARD_K_RATIO: 0.5; $YIELD_DISTANCE_AS: vector_distance}"},{"id":"mapping-common-sql-predicates-to-redis-search","title":"Mapping common SQL predicates to Redis Search","role":"content","text":"| SQL Condition | Redis Search Equivalent | Comments |\n|---------------|-----------------------|----------|\n| WHERE x='foo' AND y='bar' | @x:foo @y:bar | for less ambiguity use (@x:foo) (@y:bar) |\n| WHERE x='foo' AND y!='bar' | @x:foo -@y:bar |\n| WHERE x='foo' OR y='bar' | (@x:foo)\\|(@y:bar) |\n| WHERE x IN ('foo', 'bar','hello world') | @x:(foo\\|bar\\|\"hello world\") | quotes mean exact phrase |\n| WHERE y='foo' AND x NOT IN ('foo','bar') | @y:foo (-@x:foo) (-@x:bar) |\n| WHERE x NOT IN ('foo','bar') | -@x:(foo\\|bar) |\n| WHERE num BETWEEN 10 AND 20 | @num:[10 20] |\n| WHERE num >= 10 | @num:[10 +inf] |\n| WHERE num > 10 | @num:[(10 +inf] |\n| WHERE num < 10 | @num:[-inf (10] |\n| WHERE num <= 10 | @num:[-inf 10] |\n| WHERE num < 10 OR num > 20 | @num:[-inf (10] \\| @num:[(20 +inf] |\n| WHERE name LIKE 'john%' | @name:john* |"},{"id":"technical-notes","title":"Technical notes","role":"content","text":"The query parser is built using the Lemon Parser Generator and a Ragel based lexer. You can see the `DIALECT 2` grammar definition [at this git repo](https://github.com/RediSearch/RediSearch/blob/master/src/query_parser/v2/parser.y).\n\nYou can also see the [search-default-dialect](https://redis.io/docs/latest/develop/ai/search-and-query/administration/configuration#search-default-dialect) configuration parameter."}],"examples":[{"id":"field-modifiers-ex0","language":"plaintext","code":"FT.SEARCH cars \"@country:korea @engine:(diesel|hybrid) @class:suv\"","section_id":"field-modifiers"},{"id":"field-modifiers-ex1","language":"plaintext","code":"FT.SEARCH idx \"@title|body:(hello world) @url|image:mydomain\"","section_id":"field-modifiers"},{"id":"tag-filters-ex0","language":"plaintext","code":"@field:{ tag | tag | ...}","section_id":"tag-filters"},{"id":"tag-filters-ex1","language":"plaintext","code":"@cities:{ New York | Los Angeles | Barcelona }","section_id":"tag-filters"},{"id":"tag-filters-ex2","language":"plaintext","code":"# Return all documents containing all three cities as tags\n@cities:{ New York } @cities:{Los Angeles} @cities:{ Barcelona }\n\n# Now, return all documents containing either city\n@cities:{ New York | Los Angeles | Barcelona }","section_id":"tag-filters"},{"id":"polygon-search-ex0","language":"bash","code":"HSET shape:1 t \"this is my house\" g \"POLYGON((2 2, 2 8, 6 11, 10 8, 10 2, 2 2))\"\nHSET shape:2 t \"this is a square in my house\" g \"POLYGON((4 4, 4 6, 6 6, 6 4, 4 4))\"","section_id":"polygon-search"},{"id":"polygon-search-ex1","language":"bash","code":"FT.SEARCH polygon_idx \"@g:[CONTAINS $point]\" PARAMS 2 point 'POINT(8 8)' DIALECT 3\n1) (integer) 1\n2) \"shape:1\"\n3) 1) \"t\"\n   2) \"this is my house\"\n   3) \"g\"\n   4) \"POLYGON((2 2, 2 8, 6 11, 10 8, 10 2, 2 2))\"","section_id":"polygon-search"},{"id":"polygon-search-ex2","language":"bash","code":"FT.SEARCH polygon_idx \"@g:[WITHIN $poly]\" PARAMS 2 poly 'POLYGON((0 0, 0 100, 100 100, 100 0, 0 0))' DIALECT 3\n1) (integer) 2\n2) \"shape:2\"\n3) 1) \"t\"\n   2) \"this is a square in my house\"\n   3) \"g\"\n   4) \"POLYGON((4 4, 4 6, 6 6, 6 4, 4 4))\"\n4) \"shape:1\"\n5) 1) \"t\"\n   2) \"this is my house\"\n   3) \"g\"\n   4) \"POLYGON((2 2, 2 8, 6 11, 10 8, 10 2, 2 2))\"","section_id":"polygon-search"},{"id":"polygon-search-ex3","language":"bash","code":"FT.SEARCH polygon_idx \"@g:[CONTAINS $poly]\" PARAMS 2 poly 'POLYGON((14 4, 14 6, 16 6, 16 4, 14 4))' DIALECT 3\n1) (integer) 0","section_id":"polygon-search"},{"id":"polygon-search-ex4","language":"bash","code":"FT.SEARCH polygon_idx \"@g:[CONTAINS $poly]\" PARAMS 2 poly 'POLYGON((4 4, 4 6, 6 6, 6 4, 4 4))' DIALECT 3\n1) (integer) 2\n2) \"shape:1\"\n3) 1) \"t\"\n   2) \"this is my house\"\n   3) \"g\"\n   4) \"POLYGON((2 2, 2 8, 6 11, 10 8, 10 2, 2 2))\"\n4) \"shape:2\"\n5) 1) \"t\"\n   2) \"this is a square in my house\"\n   3) \"g\"\n   4) \"POLYGON((4 4, 4 6, 6 6, 6 4, 4 4))\"","section_id":"polygon-search"},{"id":"prefix-matching-ex0","language":"plaintext","code":"hel* world","section_id":"prefix-matching"},{"id":"infixsuffix-matching-ex0","language":"plaintext","code":"*sun* *ing","section_id":"infixsuffix-matching"},{"id":"fuzzy-matching-ex0","language":"plaintext","code":"%hello% world","section_id":"fuzzy-matching"}]}
{"schema_version":2,"id":"develop/ai/search-and-query/advanced-concepts/scoring","title":"Scoring documents","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/scoring/","summary":"Full-text scoring functions","aliases":["/develop/interact/search-and-query/advanced-concepts/scoring","/interact/search-and-query/advanced-concepts/scoring/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:33:04+01:00","page_type":"content","content_hash":"fe51a836c11d718165601b932e9722768ccec198f8a740a1db4cda882c11f2d4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"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.\n\nRedis 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](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/sorting). Scoring functions are specified by adding the `SCORER {scorer_name}` argument to a search query.\n\nIf you prefer a custom scoring function, it is possible to add more functions using the [extension API](https://redis.io/docs/latest/develop/ai/search-and-query/administration/extensions).\n\nThe following is a list of the pre-bundled scoring functions available in Redis and a short explanation about how they work. Each function is mentioned by registered name, which can be passed as a `SCORER` argument in [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search)."},{"id":"tfidf","title":"TFIDF","role":"content","text":"Basic [TF-IDF scoring](https://en.wikipedia.org/wiki/Tf%E2%80%93idf) with a few extra features:\n\n1. For each term in each result, the TF-IDF score of that term is calculated to that document. Frequencies are weighted based on field weights that are pre-determined, and each term's frequency is normalized by the highest term frequency in each document.\n\n2. The total TF-IDF for the query term is multiplied by the presumptive document score given on `FT.CREATE` via `SCORE_FIELD`.\n\n3. A penalty is assigned to each result based on \"slop\" or cumulative distance between the search terms. Exact matches will get no penalty, but matches where the search terms are distant will have their score reduced significantly. For each bigram of consecutive terms, the minimal distance between them is determined. The penalty is the square root of the sum of the distances squared; e.g., `1/sqrt(d(t2-t1)^2 + d(t3-t2)^2 + ...)`.\n\nGiven N terms in document D, `T1...Tn`, the resulting score could be described with this Python function:\n\n[code example]"},{"id":"tfidfdocnorm","title":"TFIDF.DOCNORM","role":"content","text":"Identical to the default `TFIDF` scorer, with one important distinction:\n\nTerm frequencies are normalized by the length of the document, expressed as the total number of terms. The length is weighted, so that if a document contains two terms, one in a field that has a weight 1 and one in a field with a weight of 5, the total frequency is 6, not 2.\n\n[code example]"},{"id":"bm25std-default","title":"BM25STD (default)","role":"content","text":"A variation on the basic `TFIDF` scorer, see [this Wikipedia article for more info](https://en.wikipedia.org/wiki/Okapi_BM25).\n\nThe relevance score for each document is multiplied by the presumptive document score and a penalty is applied based on slop as in `TFIDF`.\n\n> [!NOTE]\n> The `BM25` scorer was renamed `BM25STD` in Redis Open Source 8.4. `BM25` is deprecated.\n\n[code example]"},{"id":"bm25stdnorm","title":"BM25STD.NORM","role":"content","text":"A variation of `BM25STD`, where the scores are normalized by the minimum and maximum scores.\n\n`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."},{"id":"bm25stdtanh","title":"BM25STD.TANH","role":"content","text":"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.\n\n`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.\n\nFollowing is an example of how to use `BM25STD_TANH_FACTOR Y` in a query.\n\n[code example]"},{"id":"dismax","title":"DISMAX","role":"content","text":"A simple scorer that sums up the frequencies of matched terms. In the case of union clauses, it will give the maximum value of those matches. No other penalties or factors are applied.\n\nIt is not a one-to-one implementation of [Solr's DISMAX algorithm](https://wiki.apache.org/solr/DisMax), but it follows it in broad terms.\n\n[code example]"},{"id":"docscore","title":"DOCSCORE","role":"content","text":"A scoring function that just returns the presumptive score of the document without applying any calculations to it. Since document scores can be updated, this can be useful if you'd like to use an external score and nothing further.\n\n[code example]"},{"id":"hamming","title":"HAMMING","role":"content","text":"Scoring by the inverse Hamming distance between the document's payload and the query payload is performed. Since the nearest neighbors are of interest, the inverse Hamming distance (`1/(1+d)`) is used so that a distance of 0 gives a perfect score of 1 and is the highest rank.\n\nThis only works if:\n\n1. The document has a payload.\n2. The query has a payload.\n3. Both are exactly the same length.\n\nPayloads are binary-safe, and having payloads with a length that is a multiple of 64 bits yields slightly faster results.\n\nExample:\n\n[code example]"}],"examples":[{"id":"tfidf-ex0","language":"py","code":"def get_score(terms, doc):\n    # the sum of tf-idf\n    score = 0\n\n    # the distance penalty for all terms\n    dist_penalty = 0\n\n    for i, term in enumerate(terms):\n        # tf normalized by maximum frequency\n        tf = doc.freq(term) / doc.max_freq\n\n        # idf is global for the index, and not calculated each time in real life\n        idf = log2(1 + total_docs / docs_with_term(term))\n\n        score += tf*idf\n\n        # sum up the distance penalty\n        if i > 0:\n            dist_penalty += min_distance(term, terms[i-1])**2\n\n    # multiply the score by the document score\n    score *= doc.score\n\n    # divide the score by the root of the cumulative distance\n    if len(terms) > 1:\n        score /= sqrt(dist_penalty)\n\n    return score","section_id":"tfidf"},{"id":"tfidfdocnorm-ex0","language":"plaintext","code":"FT.SEARCH myIndex \"foo\" SCORER TFIDF.DOCNORM","section_id":"tfidfdocnorm"},{"id":"bm25std-default-ex0","language":"plaintext","code":"FT.SEARCH myIndex \"foo\" SCORER BM25STD","section_id":"bm25std-default"},{"id":"bm25stdtanh-ex0","language":"plaintext","code":"FT.SEARCH idx \"term\" SCORER BM25STD.TANH BM25STD_TANH_FACTOR 12 WITHSCORES","section_id":"bm25stdtanh"},{"id":"dismax-ex0","language":"plaintext","code":"FT.SEARCH myIndex \"foo\" SCORER DISMAX","section_id":"dismax"},{"id":"docscore-ex0","language":"plaintext","code":"FT.SEARCH myIndex \"foo\" SCORER DOCSCORE","section_id":"docscore"},{"id":"hamming-ex0","language":"plaintext","code":"> HSET key:1 foo hello payload aaaabbbb\n(integer) 2\n\n> HSET key:2 foo bar payload aaaacccc \n(integer) 2\n\n> FT.CREATE idx ON HASH PREFIX 1 key: PAYLOAD_FIELD payload SCHEMA foo TEXT\n\"OK\"\n\n> FT.SEARCH idx \"*\" PAYLOAD \"aaaabbbc\" SCORER HAMMING WITHSCORES\n1) \"2\"\n2) \"key:1\"\n3) \"0.5\"\n4) 1) \"foo\"\n   2) \"hello\"\n5) \"key:2\"\n6) \"0.25\"\n7) 1) \"foo\"\n   2) \"bar\"","section_id":"hamming"}]}
{"schema_version":2,"id":"develop/ai/search-and-query/advanced-concepts/sorting","title":"Sorting by indexed fields","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/sorting/","summary":"Support for sorting query results","aliases":["/develop/interact/search-and-query/advanced-concepts/sorting"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:33:04+01:00","page_type":"content","content_hash":"471cc9e69cb9d8ef69a173460799ce5957ac5fcd724959d331f1277ddd9bd88b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"As of RediSearch 0.15, you can bypass the scoring function mechanism and order search results by the value of different document attributes (fields) directly, even if the sorting field is not used by the query. For example, you can search for first name and sort by last name."},{"id":"declaring-sortable-fields","title":"Declaring sortable fields","role":"content","text":"When creating an index with [`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create), you can declare `TEXT`, `TAG`, `NUMERIC`, and `GEO` attributes as `SORTABLE`. When an attribute is sortable, you can order the results by its values with relatively low latency. When an attribute is not sortable, it can still be sorted by its values, but with increased latency. For example, in the following schema:\n\n[code example]\n\nThe fields `last_name` and `age` are sortable, but `first_name` isn't. This means you can search by either first and/or last name, and sort by last name or age."},{"id":"note-on-sortable-fields","title":"Note on sortable fields","role":"content","text":"In the current implementation, when declaring a sortable field, its content gets copied into a special location in the index that provides for fast access during sorting. This means that making long fields sortable is very expensive and you should be careful with it."},{"id":"normalization-unf-option","title":"Normalization (UNF option)","role":"content","text":"By default, text fields get normalized and lowercased in a Unicode-safe way when stored for sorting. For example, `America` and `america` are considered equal in terms of sorting.\n\nUsing the `UNF` (un-normalized form) argument, it is possible to disable the normalization and keep the original form of the value. Therefore, `America` will come before `america`."},{"id":"specifying-sortby","title":"Specifying SORTBY","role":"content","text":"If an index includes sortable fields, you can add the `SORTBY` parameter to the search request (outside the query body) to order the results. This overrides the scoring function mechanism, and the two cannot be combined. If `WITHSCORES` is specified together with `SORTBY`, the scores returned are simply the relative position of each result in the result set.\n\nThe syntax for `SORTBY` is:\n\n[code example]\n\n* `field_name` must be a sortable field defined in the schema.\n\n* `ASC` means ascending order, `DESC` means descending order.\n\n* The default ordering is `ASC`."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"declaring-sortable-fields-ex0","language":"plaintext","code":"FT.CREATE users SCHEMA first_name TEXT last_name TEXT SORTABLE age NUMERIC SORTABLE","section_id":"declaring-sortable-fields"},{"id":"specifying-sortby-ex0","language":"plaintext","code":"SORTBY {field_name} [ASC|DESC]","section_id":"specifying-sortby"},{"id":"example-ex0","language":"plaintext","code":"> FT.CREATE users ON HASH PREFIX 1 \"user\" SCHEMA first_name TEXT SORTABLE last_name TEXT age NUMERIC SORTABLE\n\n# Add some users\n> HSET user1 first_name \"alice\" last_name \"jones\" age 35\n> HSET user2 first_name \"bob\" last_name \"jones\" age 36\n\n# Searching while sorting\n\n# Searching by last name and sorting by first name\n> FT.SEARCH users \"@last_name:jones\" SORTBY first_name DESC\n\n# Searching by both first and last name, and sorting by age\n> FT.SEARCH users \"jones\" SORTBY age ASC","section_id":"example"}]}
{"schema_version":2,"id":"develop/ai/search-and-query/advanced-concepts/spellcheck","title":"Spellchecking","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/spellcheck/","summary":"Query spelling correction support","aliases":["/develop/interact/search-and-query/advanced-concepts/spellcheck","/interact/search-and-query/advanced-concepts/spellcheck/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:33:04+01:00","page_type":"content","content_hash":"d99eb355db270e934e328ea1b10c95905b2a59dc80c5c3623e1cb08f165c808f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Query spelling correction provides suggestions for misspelled search terms. For example, the term 'reids' may be a misspelled version of 'redis'.\n\nIn such cases, and as of v1.4, RediSearch can be used for generating alternatives to misspelled query terms. A misspelled term is a full text term (i.e., a word) that is:\n\n  1. Not a stop word\n  2. Not in the index\n  3. At least 3 characters long\n\nThe alternatives for a misspelled term are generated from the corpus of already-indexed terms and, optionally, one or more custom dictionaries. Alternatives become spelling suggestions based on their respective Levenshtein distances from the misspelled term. Each spelling suggestion is given a normalized score based on its occurrences in the index.\n\nTo obtain the spelling corrections for a query, refer to the documentation of the [`FT.SPELLCHECK`](https://redis.io/docs/latest/commands/ft.spellcheck) command."},{"id":"custom-dictionaries","title":"Custom dictionaries","role":"content","text":"A dictionary is a set of terms. Dictionaries can be added with terms, have terms deleted from them, and have their entire contents dumped using the [`FT.DICTADD`](https://redis.io/docs/latest/commands/ft.dictadd), [`FT.DICTDEL`](https://redis.io/docs/latest/commands/ft.dictdel) and [`FT.DICTDUMP`](https://redis.io/docs/latest/commands/ft.dictdump) commands, respectively.\n\nDictionaries can be used to modify the behavior of spelling corrections by including or excluding their contents from potential spelling correction suggestions.\n\nWhen used for term inclusion, the terms in a dictionary can be provided as spelling suggestions regardless of their occurrence in the index. Scores of suggestions from inclusion dictionaries are always 0. \n\nConversely, terms in an exclusion dictionary will never be returned as spelling alternatives."}],"examples":[]}
{"schema_version":2,"id":"develop/ai/search-and-query/advanced-concepts/stemming","title":"Stemming","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/stemming/","summary":"Stemming support","aliases":["/develop/interact/search-and-query/advanced-concepts/stemming"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:33:04+01:00","page_type":"content","content_hash":"32b9c86ecce01646077af9b512c22f21177bb14a85182c663b6e2d1cf550ea22","sections":[{"id":"overview","title":"Overview","role":"overview","text":"RediSearch supports stemming - that is adding the base form of a word to the index. This allows the query for \"`hiring`\" to also return results for \"`hire`\" and \"`hired`\", for example.\n\nThe current stemming support is based on the Snowball stemmer library, which supports most European languages, as well as Arabic and other. See the \"[Supported languages](#supported-languages)\" section below. We hope to include more languages soon (if you need a specific language support, please open an issue).\n\nFor further details see the [Snowball Stemmer website](https://snowballstem.org/)."},{"id":"how-it-works","title":"How it works?","role":"content","text":"Stemming maps different forms of the same word to a common root - \"stem\" - for example, the English stemmer maps *studied* ,*studies* and *study* to *studi* . So a searching for *studied* would also find documents which only have the other forms.\n\n\nIn order to define which language the Stemmer should apply when building the index, you need to specify the `LANGUAGE` parameter for the entire index or for the specific field. For more details check the [FT.CREATE](https://redis.io/docs/latest/commands/ft.create) syntax.\n\n**Create a index with language definition**\n\nCreate a index for words in German \"`wort:`\" with a single `TEXT` field \"`wort`\"\n\n\nredis> FT.CREATE idx:german ON HASH PREFIX 1 \"wort:\" LANGUAGE GERMAN SCHEMA wort TEXT\n\n\n**Adding words**\n\nAdding some words with same stem in German, all variations of the word `stück` ( `piece` in english): `stück stücke stuck stucke` => `stuck`\n\n\nredis> HSET wort:1 wort stück\n(integer) 1\nredis> HSET wort:2 wort stücke\n(integer) 1\nredis> HSET wort:3 wort stuck\n(integer) 1\nredis> HSET wort:4 wort stucke\n(integer) 1\n\n\n**Searching for a common stem**\n\nSearch for \"stuck\" (german for \"piece\"). As of v2.10, it's only necessary to specify the `LANGUAGE` argument when it wasn't specified to create the index being used to search.\nNote the results for words that contains \"`ü`\" are encoded in UTF-8.\n\n\nredis> FT.SEARCH idx:german '@wort:(stuck)' German\n1) (integer) 4\n2) \"wort:3\"\n3) 1) \"wort\"\n   2) \"stuck\"\n4) \"wort:4\"\n5) 1) \"wort\"\n   2) \"stucke\"\n6) \"wort:1\"\n7) 1) \"wort\"\n   2) \"st\\xc3\\xbcck\"\n8) \"wort:2\"\n9) 1) \"wort\"\n   2) \"st\\xc3\\xbccke\""},{"id":"supported-languages","title":"Supported languages","role":"compatibility","text":"The following languages are supported and can be passed to the engine when indexing or querying using lowercase:\n\n* `arabic`\n* `armenian`\n* `basque`\n* `catalan`\n* `chinese` (see below)\n* `danish`\n* `dutch`\n* `english`\n* `finnish`\n* `french`\n* `german`\n* `greek`\n* `hindi`\n* `hungarian`\n* `indonesian`\n* `irish`\n* `italian`\n* `lithuanian`\n* `malay`\n* `nepali`\n* `norwegian`\n* `portuguese`\n* `romanian`\n* `russian`\n* `serbian`\n* `spanish`\n* `swedish`\n* `tagalog`\n* `tamil`\n* `turkish`\n* `yiddish`"},{"id":"chinese-support","title":"Chinese support","role":"content","text":"Indexing a Chinese document is different than indexing a document in most other languages because of how tokens are extracted. While most languages can have their tokens distinguished by separation characters and whitespace, this is not common in Chinese.\n\nChinese tokenization is done by scanning the input text and checking every character or sequence of characters against a dictionary of predefined terms and determining the most likely match based on the surrounding terms and characters.\n\nRedis Open Source makes use of the [Friso](https://github.com/lionsoul2014/friso) chinese tokenization library for this purpose. This is largely transparent to the user and often no additional configuration is required."},{"id":"using-custom-dictionaries","title":"Using custom dictionaries","role":"content","text":"If you wish to use a custom dictionary, you can do so at the module level when loading the module. The `FRISOINI` setting can point to the location of a `friso.ini` file which contains the relevant settings and paths to the dictionary files.\n\nNote that there is no default `friso.ini` file location. RedisSearch comes with its own `friso.ini` and dictionary files that are compiled into the module binary at build-time."}],"examples":[]}
{"schema_version":2,"id":"develop/ai/search-and-query/advanced-concepts/stopwords","title":"Stop words","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/stopwords/","summary":"Stop words support","aliases":["/interact/search-and-query/advanced-concepts/stopwords","/develop/interact/search-and-query/advanced-concepts/stopwords/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:33:04+01:00","page_type":"content","content_hash":"d4350a2de5ee76e0126bf5e604e0f167c66de31526af5818f5395c7e533e24ec","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Open Source has a default list of [stop words](https://en.wikipedia.org/wiki/Stop_words). These are words that are usually so common that they do not add much information to search, but take up a lot of space and CPU time in the index. \n\nWhen indexing, stop words are discarded and not indexed. When searching, they are also ignored and treated as if they were not sent to the query processor. This is done when parsing the query. \n\nAt the moment, the default stop word list applies to all full-text indexes in all languages and can be overridden manually at index creation time."},{"id":"default-stop-word-list","title":"Default stop word list","role":"content","text":"The following words are treated as stop words by default: \n\n[code example]"},{"id":"overriding-the-default-stop-word-list","title":"Overriding the default stop word list","role":"content","text":"Stop words for an index can be defined (or disabled completely) on index creation using the `STOPWORDS` argument with the [[`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create) command.\n\nThe format is `STOPWORDS {number} {stopword} ...` where number is the number of stop words given. The `STOPWORDS` argument must come before the `SCHEMA` argument. For example:\n\n[code example]"},{"id":"disable-the-use-of-stop-words","title":"Disable the use of stop words","role":"content","text":"Disabling stop words completely can be done by passing `STOPWORDS 0` to [`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create)."},{"id":"avoiding-stop-word-detection-in-search-queries","title":"Avoiding stop word detection in search queries","role":"content","text":"In rare use cases, where queries are very long and are guaranteed by the client application not to contain stop words, it is possible to avoid checking for them when parsing the query. This saves some CPU time and is only worth it if the query has dozens or more terms in it. Using this without verifying that the query doesn't contain stop words might result in empty queries."}],"examples":[{"id":"default-stop-word-list-ex0","language":"plaintext","code":"a,    is,    the,   an,   and,  are, as,  at,   be,   but,  by,   for,\n if,   in,    into,  it,   no,   not, of,  on,   or,   such, that, their,\n then, there, these, they, this, to,  was, will, with","section_id":"default-stop-word-list"},{"id":"overriding-the-default-stop-word-list-ex0","language":"plaintext","code":"FT.CREATE myIndex STOPWORDS 3 foo bar baz SCHEMA title TEXT body TEXT","section_id":"overriding-the-default-stop-word-list"}]}
{"schema_version":2,"id":"develop/ai/search-and-query/advanced-concepts/synonyms","title":"Synonym support","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/synonyms/","summary":"Details on synonym support with Redis Open Source","aliases":["/develop/interact/search-and-query/advanced-concepts/synonyms","/interact/search-and-query/advanced-concepts/synonyms/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-04-07T16:24:18+01:00","page_type":"content","content_hash":"ae64be702034e20bef207a2fae6e99ec3341b40feabdc67c512a1e7c3dccb36e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Open Source supports synonyms. That is, searching for synonym words defined by the synonym data structure.\n\nThe synonym data structure is a set of groups, each of which contains synonym terms. For example, the following synonym data structure contains three groups, and each group contains three synonym terms:\n\n[code example]\n\nWhen these three groups are located inside the synonym data structure, it is possible to search for \"child\" and receive documents containing \"boy\", \"girl\", \"child\" and \"baby\"."},{"id":"the-synonym-search-technique","title":"The synonym search technique","role":"content","text":"A simple HashMap is used to map between the terms and the group IDs. During index creation, a check is made to see if the current term appears in the synonym map, and if it does, take all the group IDs that the term belongs to.\n\nFor each group ID, another record is added to the inverted index called \"\\~\\<id\\>\" that contains the same information as the term itself. When performing a search, a check is made to see if the searched term appears in the synonym map, and if it does, take all the group IDs the term belongs to. For each group ID, search for \"\\~\\<id\\>\" and return the combined results. This technique ensures that all the synonyms of a given term will be returned."},{"id":"handling-concurrency","title":"Handling concurrency","role":"content","text":"Since the indexing is performed in a separate thread, the synonyms map may change during indexing, which in turn may cause data corruption or crashes during indexing or searching. To solve this issue, a read-only copy is created for indexing purposes. The read-only copy is maintained using reference count.\n\nAs long as the synonyms map does not change, the original synonym map holds a reference to its read-only copy, so it will not be freed. After the data inside the synonyms map has changed, the synonyms map decreses the reference count of its read only copy. This ensures that when all the indexers are done using the read-only copy, it will automatically be freed. This ensures that the next time an indexer asks for a read-only copy, the synonyms map will create a new copy (containing the new data) and return it."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"plaintext","code":"{boy, child, baby}\n{girl, child, baby}\n{man, person, adult}","section_id":"overview"},{"id":"example-ex0","language":"plaintext","code":"# Create an index\n> FT.CREATE idx schema t text\n\n# Create a synonym group \n> FT.SYNUPDATE idx group1 hello world\n\n# Insert documents\n> HSET foo t hello\n(integer) 1\n> HSET bar t world\n(integer) 1\n\n# Search\n> FT.SEARCH idx hello\n1) (integer) 2\n2) \"foo\"\n3) 1) \"t\"\n   2) \"hello\"\n4) \"bar\"\n5) 1) \"t\"\n   2) \"world\"","section_id":"example"}]}
{"schema_version":2,"id":"develop/ai/search-and-query/advanced-concepts/tags","title":"Tag fields","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/tags/","summary":"How to use tag fields for exact match searches and high-performance filtering","aliases":["/develop/interact/search-and-query/advanced-concepts/tags","/develop/ai/search-and-query/advanced-concepts/tags"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:33:04+01:00","page_type":"content","content_hash":"21cc55f2c648996f071ac137c55ae412db242b897921913da702212864ac8d93","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Tag fields provide exact match search capabilities with high performance and memory efficiency. Use tag fields when you need to filter documents by specific values without the complexity of full-text search tokenization.\n\nTag fields interpret text as a simple list of *tags* delimited by a [separator](#separator-options) character. This approach enables simpler [tokenization](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/escaping#tokenization-rules-for-tag-fields) and encoding, making tag indexes much more efficient than full-text indexes. Note: even though tag and text fields both use text, they are two separate field types and so you don't query them the same way.\n\n> [!NOTE] Important: Different defaults for HASH vs JSON\n> - The default separator for hash documents is a comma (`,`).\n> - There is no default separator for JSON documents. You must explicitly specify one if needed.\n>\n> Specifying a tag from the text `\"foo,bar\"` behaves differently:\n> - For hash documents, two tags are created: `\"foo\"` and `\"bar\"`.\n> - For JSON documents, one tag is created: `\"foo,bar\"` (unless you add `SEPARATOR \",\"`)."},{"id":"tag-fields-vs-text-fields","title":"Tag fields vs text fields","role":"content","text":"Tag fields excel in scenarios requiring exact matching rather than full-text search. Choose tag fields when you need to index categorical data such as:\n\n- **Product categories**: Electronics, Clothing, Books\n- **User roles**: Admin, Editor, Viewer\n- **Status values**: Active, Pending, Completed\n- **Geographic regions**: US, EU, APAC\n- **Content types**: Video, Image, Document"},{"id":"key-differences","title":"Key differences","role":"content","text":"| Feature | Tag fields | Text fields |\n|---------|------------|-------------|\n| **Search type** | Exact match | Full-text search |\n| **Tokenization** | Simple delimiter splitting | Complex word tokenization |\n| **Stemming** | None | Language-specific stemming |\n| **Memory usage** | Very low (1-2 bytes per entry) | Higher (frequencies, positions) |\n| **Performance** | Fastest | Slower for exact matches |\n| **Multiple values** | Support comma-separated lists | Single text content |\n| **Case control** | Optional case-sensitive matching | Typically case-insensitive |\n| **Use case** | Categories, filters, IDs | Content search, descriptions |"},{"id":"technical-details","title":"Technical details","role":"content","text":""},{"id":"index-structure","title":"Index structure","role":"content","text":"- **Compressed storage**: Only document IDs encoded as deltas (1-2 bytes per entry)\n- **No frequencies**: Unlike TEXT fields, tag indexes don't store term frequencies\n- **No positions**: No offset vectors or field flags stored\n- **Limit**: You can create up to 1024 tag fields per index"},{"id":"tokenization-differences","title":"Tokenization differences","role":"content","text":"- **Simple splitting**: Text is split only at separator characters\n- **No stemming**: Words are indexed exactly as written\n- **Case handling**: Optional case-sensitive or case-insensitive matching\n- **No stop words**: All tag values are indexed regardless of content"},{"id":"create-a-tag-field","title":"Create a tag field","role":"content","text":"Add tag fields to your schema using this syntax:\n\n[code example]"},{"id":"separator-options","title":"Separator options","role":"content","text":"The separator behavior differs significantly between hash and JSON documents:\n\n**Hash documents**\n\n- The default separator is the comma (`,`).\n- Strings are automatically splits at commas. For example,\n  the string `\"red,blue,green\"` becomes three tags: `\"red\"`, `\"blue\"`, and `\"green\"`.\n- You can use any printable ASCII character as a custom separator.\n\n**JSON documents**\n\n- There is no default separator; it's effectively `null`.\n- Treats the entire string as single tag unless you specify a separator with the `SEPARATOR` option. For example,\n  the string `\"red,blue,green\"` becomes one tag: `\"red,blue,green\"`\n- Add `SEPARATOR \",\"` to your schema to allow splitting.\n- You should use JSON arrays instead of comma-separated strings\n\n**Why the difference?**\n\nJSON has native array support, so the preferred approach is:\n\n[code example]\nRather than:\n\n[code example]"},{"id":"case-sensitivity","title":"Case sensitivity","role":"content","text":"- **Default**: Case-insensitive matching (`red` matches `Red`, `RED`)\n- **CASESENSITIVE**: Preserves original case for exact matching"},{"id":"examples","title":"Examples","role":"example","text":"**Hash examples**\n\n1. Basic hash tag field (automatic comma splitting):\n\n    [code example]\n\n1. Hash with custom separator:\n\n    [code example]\n\n1. Case-sensitive hash tags:\n\n    [code example]\n\n**JSON examples**\n\n1. JSON with string and explicit separator (not recommended):\n\n    [code example]\n\n1. JSON with array of strings (recommended approach):\n\n    [code example]\n\n1. JSON without separator (single tag):\n\n    [code example]"},{"id":"query-tag-fields","title":"Query tag fields","role":"content","text":"**Important**: Tag fields require special query syntax - you cannot find tag values with general field-less searches."},{"id":"basic-tag-query-syntax","title":"Basic tag query syntax","role":"content","text":"Use curly braces to specify tag values (the braces are part of the syntax):\n\n[code example]"},{"id":"single-tag-match","title":"Single tag match","role":"content","text":"Find documents with a specific tag:\n\n[code example]"},{"id":"multiple-tag-match-or","title":"Multiple tag match (OR)","role":"content","text":"Find documents with any of the specified tags:\n\n[code example]"},{"id":"combining-with-other-queries","title":"Combining with other queries","role":"content","text":"Tag queries work seamlessly with other field types:\n\n[code example]"},{"id":"prefix-matching","title":"Prefix matching","role":"content","text":"Use the `*` wildcard for prefix matching:\n\n[code example]"},{"id":"negative-matching","title":"Negative matching","role":"content","text":"Exclude documents with specific tags:\n\n[code example]"},{"id":"advanced-tag-queries","title":"Advanced tag queries","role":"content","text":""},{"id":"or-vs-and-logic","title":"OR vs AND logic","role":"content","text":"**Single clause (OR logic)**: Find documents with ANY of the specified tags\n[code example]\n\n**Multiple clauses (AND logic)**: Find documents with ALL of the specified tags\n[code example]"},{"id":"practical-example","title":"Practical example","role":"content","text":"Consider a travel database:\n\n[code example]\n\n**Find travelers who visited any of these cities:**\n[code example]\n\n**Find travelers who visited all of these cities:**\n[code example]"},{"id":"handle-special-characters","title":"Handle special characters","role":"content","text":"Tag fields can contain any punctuation except the field separator, but you need to escape certain characters in queries."},{"id":"defining-tags-with-special-characters","title":"Defining tags with special characters","role":"content","text":"You can store tags with punctuation without escaping:\n\n[code example]"},{"id":"querying-tags-with-special-characters","title":"Querying tags with special characters","role":"content","text":"**Escape punctuation in queries** using backslash (`\\`):\n\n[code example]"},{"id":"characters-that-need-escaping","title":"Characters that need escaping","role":"content","text":"In tag queries, escape these characters:\n- Single quotes: `'` → `\\\\'`\n- Hyphens: `-` → `\\\\-`\n- Parentheses: `()` → `\\\\(\\\\)`\n- Brackets: `[]{}` → `\\\\[\\\\]\\\\{\\\\}`\n- Pipes: `|` → `\\\\|`"},{"id":"spaces-in-tags","title":"Spaces in tags","role":"content","text":"**Modern Redis** (v2.4+): Spaces don't need escaping in tag queries\n[code example]\n\n**Older versions** or **dialect 1**: Escape spaces\n[code example]"},{"id":"best-practices","title":"Best practices","role":"content","text":"- **Use simple separators**: Stick to comma (`,`) or semicolon (`;`)\n- **Avoid complex punctuation**: Keep tag values simple when possible\n- **Test your queries**: Verify escaping works with your specific characters\n- **Use consistent casing**: Decide on case sensitivity early in your design\n\nSee [Query syntax](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/query_syntax#tag-filters) for complete escaping rules."},{"id":"performance-and-architecture-considerations","title":"Performance and architecture considerations","role":"performance","text":""},{"id":"multiple-tag-fields-versus-a-single-tag-field","title":"Multiple TAG fields versus a single TAG field","role":"content","text":"You can structure your data in two ways:\n\n1. Multiple single-value TAG fields\n\n    [code example]\n\n1. Single multi-value TAG field\n\n    [code example]"},{"id":"performance-comparison","title":"Performance comparison","role":"performance","text":"Both approaches have similar performance characteristics:\n\n- Memory usage is comparable: TAG indexes are highly compressed regardless of structure.\n- Query speed is also comparable, though single-value tags may offer a slight edge: both use the same underlying inverted index structure.\n- Index efficiency; TAG fields store only document IDs (1-2 bytes per entry)."},{"id":"choose-tag-fields-based-on-your-use-case","title":"Choose TAG fields based on your use case","role":"content","text":"Use multiple TAG fields when:\n\n- You need field-specific queries (`@color:{blue}` vs `@brand:{ASUS}`).\n- Your schema is stable and well-defined.\n- You want cleaner, more readable queries.\n- You need different configurations per field (for example, case-sensitive versus case-insensitive).\n\nUse single TAG field when:\n\n- You have dynamic or unknown tag categories.\n- You want maximum flexibility for adding new tag types.\n- Your application manages tag prefixing/namespacing.\n- You have many sparse categorical fields."},{"id":"an-e-commerce-use-case","title":"An e-commerce use case","role":"content","text":"[code example]"},{"id":"next-steps","title":"Next steps","role":"content","text":"- Learn about [tokenization rules](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/escaping#tokenization-rules-for-tag-fields) for tag fields\n- Explore [field and type options](https://redis.io/docs/latest/develop/ai/search-and-query/indexing/field-and-type-options) for other field types\n- See [query syntax](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/query_syntax) for advanced query patterns"}],"examples":[{"id":"create-a-tag-field-ex0","language":"plaintext","code":"FT.CREATE ... SCHEMA ... {field_name} TAG [SEPARATOR {sep}] [CASESENSITIVE]","section_id":"create-a-tag-field"},{"id":"separator-options-ex0","language":"json","code":"{\"colors\": [\"red\", \"blue\", \"green\"]}  // Use with $.colors[*] AS colors TAG","section_id":"separator-options"},{"id":"separator-options-ex1","language":"json","code":"{\"colors\": \"red,blue,green\"}  // Requires SEPARATOR \",\"","section_id":"separator-options"},{"id":"examples-ex0","language":"sql","code":"HSET product:1 categories \"Electronics,Gaming,PC\"\n    FT.CREATE products ON HASH PREFIX 1 product: SCHEMA categories TAG\n\n    > FT.SEARCH products '@categories:{Gaming}'\n    1) \"1\"\n    2) \"product:1\"","section_id":"examples"},{"id":"examples-ex1","language":"sql","code":"HSET book:1 genres \"Fiction;Mystery;Thriller\"\n    FT.CREATE books ON HASH PREFIX 1 book: SCHEMA genres TAG SEPARATOR \";\"","section_id":"examples"},{"id":"examples-ex2","language":"sql","code":"HSET product:1 categories \"Electronics,Gaming,PC\"\n    FT.CREATE products ON HASH PREFIX 1 product: SCHEMA categories TAG CASESENSITIVE\n\n    > FT.SEARCH products '@categories:{PC}'  # Case matters\n    1) \"1\"\n    2) \"product:1\"","section_id":"examples"},{"id":"examples-ex3","language":"sql","code":"JSON.SET key:1 $ '{\"colors\": \"red, orange, yellow\"}'\n    FT.CREATE idx ON JSON PREFIX 1 key: SCHEMA $.colors AS colors TAG SEPARATOR \",\"\n\n    > FT.SEARCH idx '@colors:{orange}'\n    1) \"1\"\n    2) \"key:1\"\n    3) 1) \"$\"\n       2) \"{\\\"colors\\\":\\\"red, orange, yellow\\\"}\"","section_id":"examples"},{"id":"examples-ex4","language":"sql","code":"JSON.SET key:1 $ '{\"colors\": [\"red\", \"orange\", \"yellow\"]}'\n    FT.CREATE idx ON JSON PREFIX 1 key: SCHEMA $.colors[*] AS colors TAG\n\n    > FT.SEARCH idx '@colors:{orange}'\n    1) \"1\"\n    2) \"key:1\"\n    3) 1) \"$\"\n       2) \"{\\\"colors\\\":[\\\"red\\\",\\\"orange\\\",\\\"yellow\\\"]}\"","section_id":"examples"},{"id":"examples-ex5","language":"sql","code":"JSON.SET key:1 $ '{\"category\": \"Electronics,Gaming\"}'\n    FT.CREATE idx ON JSON PREFIX 1 key: SCHEMA $.category AS category TAG\n    # No SEPARATOR specified - entire string becomes one tag\n\n    > FT.SEARCH idx '@category:{Electronics,Gaming}'  # Must match exactly\n    1) \"1\"\n    2) \"key:1\"","section_id":"examples"},{"id":"basic-tag-query-syntax-ex0","language":"plaintext","code":"@<field_name>:{ <tag> | <tag> | ...}","section_id":"basic-tag-query-syntax"},{"id":"single-tag-match-ex0","language":"sql","code":"FT.SEARCH idx \"@category:{Electronics}\"\nFT.SEARCH idx \"@status:{Active}\"","section_id":"single-tag-match"},{"id":"multiple-tag-match-or-ex0","language":"sql","code":"FT.SEARCH idx \"@tags:{ hello world | foo bar }\"\nFT.SEARCH idx \"@category:{ Electronics | Gaming | Software }\"","section_id":"multiple-tag-match-or"},{"id":"combining-with-other-queries-ex0","language":"sql","code":"FT.CREATE idx ON HASH PREFIX 1 product: SCHEMA\n    title TEXT\n    price NUMERIC\n    category TAG SEPARATOR \";\"\n\n# Combine text search, numeric range, and tag filter\nFT.SEARCH idx \"@title:laptop @price:[500 1500] @category:{ Electronics | Gaming }\"","section_id":"combining-with-other-queries"},{"id":"prefix-matching-ex0","language":"sql","code":"FT.SEARCH idx \"@tags:{ tech* }\"        # Matches: technology, technical, tech\nFT.SEARCH idx \"@tags:{ hello\\\\ w* }\"   # Matches: \"hello world\", \"hello web\"","section_id":"prefix-matching"},{"id":"negative-matching-ex0","language":"sql","code":"FT.SEARCH idx \"-@category:{Discontinued}\"\nFT.SEARCH idx \"@title:phone -@category:{Refurbished}\"","section_id":"negative-matching"},{"id":"or-vs-and-logic-ex0","language":"sql","code":"@cities:{ New York | Los Angeles | Barcelona }\n# Returns: Documents with New York OR Los Angeles OR Barcelona","section_id":"or-vs-and-logic"},{"id":"or-vs-and-logic-ex1","language":"sql","code":"@cities:{ New York } @cities:{ Los Angeles } @cities:{ Barcelona }\n# Returns: Documents with New York AND Los Angeles AND Barcelona","section_id":"or-vs-and-logic"},{"id":"practical-example-ex0","language":"sql","code":"FT.CREATE travelers ON HASH PREFIX 1 traveler: SCHEMA\n    name TEXT\n    cities TAG\n\nHSET traveler:1 name \"John Doe\" cities \"New York, Barcelona, San Francisco\"\nHSET traveler:2 name \"Jane Smith\" cities \"New York, Los Angeles, Tokyo\"","section_id":"practical-example"},{"id":"practical-example-ex1","language":"sql","code":"FT.SEARCH travelers \"@cities:{ New York | Los Angeles | Barcelona }\"\n# Returns: Both John and Jane","section_id":"practical-example"},{"id":"practical-example-ex2","language":"sql","code":"FT.SEARCH travelers \"@cities:{ New York } @cities:{ Barcelona }\"\n# Returns: Only John (has both New York and Barcelona)","section_id":"practical-example"},{"id":"defining-tags-with-special-characters-ex0","language":"sql","code":"FT.CREATE products ON HASH PREFIX 1 test: SCHEMA tags TAG\n\nHSET test:1 tags \"Andrew's Top 5,Justin's Top 5,5-Star Rating\"\nHSET test:2 tags \"Best Buy,Top-Rated,Editor's Choice\"","section_id":"defining-tags-with-special-characters"},{"id":"querying-tags-with-special-characters-ex0","language":"sql","code":"# Query for \"Andrew's Top 5\"\nFT.SEARCH products \"@tags:{ Andrew\\\\'s Top 5 }\"\n\n# Query for \"5-Star Rating\"\nFT.SEARCH products \"@tags:{ 5\\\\-Star Rating }\"\n\n# Query for \"Editor's Choice\"\nFT.SEARCH products \"@tags:{ Editor\\\\'s Choice }\"","section_id":"querying-tags-with-special-characters"},{"id":"spaces-in-tags-ex0","language":"sql","code":"FT.SEARCH products \"@tags:{ Top Rated Product }\"","section_id":"spaces-in-tags"},{"id":"spaces-in-tags-ex1","language":"sql","code":"FT.SEARCH products \"@tags:{ Top\\\\ Rated\\\\ Product }\"","section_id":"spaces-in-tags"},{"id":"multiple-tag-fields-versus-a-single-tag-field-ex0","language":"sql","code":"FT.CREATE products ON JSON PREFIX 1 product: SCHEMA\n        $.color AS color TAG\n        $.brand AS brand TAG\n        $.type AS type TAG\n\n    JSON.SET product:1 $ '{\"color\": \"blue\", \"brand\": \"ASUS\", \"type\": \"laptop\"}'\n\n    # Query specific fields\n    FT.SEARCH products '@color:{blue} @brand:{ASUS}'","section_id":"multiple-tag-fields-versus-a-single-tag-field"},{"id":"multiple-tag-fields-versus-a-single-tag-field-ex1","language":"sql","code":"FT.CREATE products ON JSON PREFIX 1 product: SCHEMA\n        $.tags[*] AS tags TAG\n\n    JSON.SET product:1 $ '{\"tags\": [\"color:blue\", \"brand:ASUS\", \"type:laptop\"]}'\n\n    # Query with prefixed values\n    FT.SEARCH products '@tags:{color:blue} @tags:{brand:ASUS}'","section_id":"multiple-tag-fields-versus-a-single-tag-field"},{"id":"an-e-commerce-use-case-ex0","language":"sql","code":"# Product categories and attributes\nFT.CREATE products ON HASH PREFIX 1 product: SCHEMA\n    name TEXT\n    category TAG\n    brand TAG\n    features TAG SEPARATOR \";\"\n\nHSET product:1 name \"Gaming Laptop\" category \"Electronics\" brand \"ASUS\" features \"RGB;16GB RAM;SSD\"\n\n# Find gaming products with specific features\nFT.SEARCH products \"@category:{Electronics} @features:{RGB} @features:{SSD}\"","section_id":"an-e-commerce-use-case"}]}
{"schema_version":2,"id":"develop/ai/search-and-query/advanced-concepts/transactions","title":"Search commands in MULTI/EXEC transactions and Lua scripts","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/transactions/","summary":"How Redis Search commands behave inside MULTI/EXEC transactions and Lua scripts","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:33:04+01:00","page_type":"content","content_hash":"cb0a408105745e92875b67d8263a73433bdf0e69a03db894129e7e8f56bee3aa","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Search commands ([`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search),\n[`FT.AGGREGATE`](https://redis.io/docs/latest/commands/ft.aggregate),\n[`FT.HYBRID`](https://redis.io/docs/latest/commands/ft.hybrid),\n[`FT.PROFILE`](https://redis.io/docs/latest/commands/ft.profile), and\n[`FT.CURSOR READ`](https://redis.io/docs/latest/commands/ft.cursor-read))\ncan be used inside [`MULTI`](https://redis.io/docs/latest/commands/multi)/[`EXEC`](https://redis.io/docs/latest/commands/exec)\ntransactions and [Lua scripts](https://redis.io/docs/latest/develop/programmability/lua-api),\nbut the behavior differs depending on your deployment topology."},{"id":"standalone-and-single-shard-deployments","title":"Standalone and single-shard deployments","role":"content","text":"Query commands inside a `MULTI`/`EXEC` block or Lua script (including when issued\nthrough a client pipeline that wraps commands in a transaction) execute synchronously\non the main Redis thread, regardless of the\n[`search-workers`](https://redis.io/docs/latest/develop/ai/search-and-query/administration/configuration#search-workers)\nsetting.\n\nThe worker thread pool is bypassed in this context because Redis transactions\nrequire all commands to complete sequentially without yielding to other clients.\nAs a result, queries inside transactions do not benefit from the parallelism\nprovided by `search-workers > 0`, but they execute correctly and return results\nas expected."},{"id":"multi-shard-deployments-oss-cluster-and-redis-software-with-multiple-shards","title":"Multi-shard deployments (OSS Cluster and Redis Software with multiple shards)","role":"content","text":"Redis Search commands inside a `MULTI`/`EXEC` block or a Lua script are rejected with\nthe following error:\n\n[code example]\n\nThis is because the coordinator must fan out the query to multiple shards and\ncollect results asynchronously, which is incompatible with the sequential\nexecution model of transactions. This limitation applies regardless of the\n`search-workers` setting."},{"id":"related-commands","title":"Related commands","role":"related","text":"- [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search)\n- [`FT.AGGREGATE`](https://redis.io/docs/latest/commands/ft.aggregate)\n- [`FT.HYBRID`](https://redis.io/docs/latest/commands/ft.hybrid)\n- [`FT.PROFILE`](https://redis.io/docs/latest/commands/ft.profile)\n- [`FT.CURSOR READ`](https://redis.io/docs/latest/commands/ft.cursor-read)\n- [`MULTI`](https://redis.io/docs/latest/commands/multi)\n- [`EXEC`](https://redis.io/docs/latest/commands/exec)"}],"examples":[{"id":"multi-shard-deployments-oss-cluster-and-redis-software-with-multiple-shards-ex0","language":"plaintext","code":"Cannot perform FT.SEARCH: Cannot block","section_id":"multi-shard-deployments-oss-cluster-and-redis-software-with-multiple-shards"}]}
{"schema_version":2,"id":"develop/ai/search-and-query/best-practices/dev-to-prod-best-practices","title":"Move from Development to Production with Redis Search","url":"https://redis.io/docs/latest/develop/ai/search-and-query/best-practices/dev-to-prod-best-practices/","summary":"","aliases":["/develop/interact/search-and-query/best-practices/dev-to-prod-best-practices"],"tags":["docs","develop","stack","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:33:04+01:00","page_type":"content","content_hash":"175ff3d7f7d2bd36bb7ead969fec9ab392e1da959392232c8419428928934e1f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Transitioning a Redis Open Source with Redis Search environment from development to production requires thoughtful consideration of configuration, performance tuning, and resource allocation. This guide outlines key practices to ensure your Redis deployment operates optimally under production workloads."},{"id":"configuration-parameter-considerations","title":"Configuration parameter considerations","role":"configuration","text":"Redis Search offers several configurable parameters that influence query results and performance. While a full list of these parameters and their functions can be found [here](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/dialects), this section highlights the most commonly adjusted parameters for production environments."},{"id":"1-timeout","title":"1. `TIMEOUT`","role":"content","text":"- Purpose: limits the duration a query is allowed to execute. \n- Default: 500 milliseconds.\n- Behavior:\n  - Ensures that queries do not monopolize the main Redis thread.\n  - If a query exceeds the `TIMEOUT` value, its outcome is determined by the `ON_TIMEOUT` setting:\n    - `FAIL`: the query will return an error.\n    - `PARTIAL`: this setting will return the top results accumulated by the query until it timed out.\n- Recommendations:\n  - Caution: be mindful when increasing `TIMEOUT`,<!-- especially in environments without QPF,--> as long-running queries can degrade overall system performance.\n <!-- - With Query Performance Factor (QPF): if QPF (multi-threaded query execution) is enabled, the risks of increasing this value are mitigated, as queries execute against index memory using multiple threads.-->"},{"id":"2-minprefix","title":"2. `MINPREFIX`","role":"content","text":"- Purpose: sets the minimum number of characters required for wildcard searches.\n- Default: 2 characters.\n- Behavior:\n  - Queries like `he*` are valid, while `h*` would not meet the threshold.\n- Recommendations:\n  - Lowering this value to 1 can significantly increase result sets, which may lead to degraded performance.\n  - Keep the default unless there is a strong use case for single-character wildcards."},{"id":"3-maxprefixexpansions","title":"3. `MAXPREFIXEXPANSIONS`","role":"content","text":"- Purpose: Defines the maximum number of expansions for a wildcard query term.\n- Default: 200 expansions.\n- Behavior:\n  - Expansions: when a wildcard query term is processed, Redis generates a list of all possible matches from the index that satisfy the wildcard. For example, the query he* might expand to terms like hello, hero, and heat. Each of these matches is an \"expansion.\"\n  - This parameter limits how many of these expansions Redis will generate and process. If the number of possible matches exceeds the limit, the query may return incomplete results or fail, depending on the query context.\n- Recommendations:\n  - Avoid increasing this parameter excessively, as it can lead to performance bottlenecks during query execution.\n  - If wildcard searches are common, consider optimizing your index to reduce the reliance on large wildcard expansions."},{"id":"4-default_dialect","title":"4. `DEFAULT_DIALECT`","role":"content","text":"- Purpose: specifies the default query dialect used by [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) and [`FT.AGGREGATE`](https://redis.io/docs/latest/commands/ft.aggregate) commands.\n- Default: [Dialect 1](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/dialects).\n- Recommendations:\n  - Update the default to [**Dialect 4**](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/dialects#dialect-4) for better performance and access to advanced features.\n  - Individual commands can override this parameter if necessary, but setting a higher default ensures consistent performance across queries."},{"id":"testing","title":"Testing","role":"content","text":""},{"id":"1-correctness","title":"1. Correctness","role":"content","text":"- Run a few test queries and check the results are what you expect.\n- Use the following tools to validate and debug:\n  - Redis CLI: use the [`MONITOR`](https://redis.io/docs/latest/commands/monitor) command or [profiling features](https://redis.io/docs/latest/develop/tools/insight#profiler) in Redis Insight to analyze commands.\n  - [`FT.PROFILE`](https://redis.io/docs/latest/commands/ft.profile): Provides detailed insights into individual query execution paths, helping identify bottlenecks and inefficiencies."},{"id":"2-performance","title":"2. Performance","role":"content","text":"- Test query performance in a controlled test environment that mirrors production as closely as possible.\n- Use tools like `memtier_benchmark` or custom test applications to simulate load.\n- Network Considerations:\n  - Minimize latency during testing by locating test clients in the same network as the Redis instance.\n  - For Redis Cloud, ensure test machines are in a **VPC-peered environment** with the target Redis database."},{"id":"sizing-requirements","title":"Sizing requirements","role":"content","text":"Redis Search has resource requirements distinct from general caching use cases. Proper sizing ensures that the system can handle production workloads efficiently."},{"id":"key-considerations","title":"Key considerations:","role":"content","text":"1. CPU: \n   - Adequate CPU resources are critical<!--, especially when using QPF-->.\n   - Ensure CPUs are not over-subscribed with search threads and shard processes.\n2. RAM:\n   - Plan for sufficient memory to store the dataset and indexes, plus overhead for operations.\n3. Network:\n   - High throughput and low latency are essential, particularly for applications with demanding query patterns."},{"id":"tools","title":"Tools:","role":"content","text":"- Use [Redis Search Sizing Calculator](https://redis.io/redisearch-sizing-calculator/) to estimate resource requirements based on your dataset and workload."},{"id":"demand-spikes","title":"Demand spikes","role":"content","text":"Production environments must be sized for peak load scenarios to ensure performance remains acceptable under maximum stress."},{"id":"recommendations","title":"Recommendations:","role":"content","text":"1. Plan for Spikes:\n   - If query workloads are expected to vary significantly, ensure the infrastructure can handle peak loads.\n   - Monitor real-world usage patterns and adjust capacity as needed.\n2. Autoscaling:\n   - Consider using autoscaling strategies in cloud environments to dynamically adjust resources based on load.\n\nBy following these best practices, you can ensure a smooth and efficient transition from development to production with Redis Open Source and Redis Search. Proper configuration, rigorous testing, and careful resource planning are critical to delivering a reliable and high-performance Redis deployment."}],"examples":[]}
{"schema_version":2,"id":"develop/ai/search-and-query/best-practices/index-mgmt-best-practices","title":"Index management best practices for Redis Search","url":"https://redis.io/docs/latest/develop/ai/search-and-query/best-practices/index-mgmt-best-practices/","summary":"","aliases":["/develop/interact/search-and-query/best-practices/index-mgmt-best-practices"],"tags":["docs","develop","stack","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:33:04+01:00","page_type":"content","content_hash":"9e71eef7f44735a77c3f839e5a2f7fcb968b955b8da7a11dc0b410ffd92f5397","sections":[{"id":"introduction-to-managing-redis-search-indexes","title":"Introduction to managing Redis Search indexes","role":"overview","text":"Redis Search is a powerful tool for executing complex search and query operations on structured, semi-structured, and unstructured data. Indexes are the backbone of this functionality, enabling fast and efficient data retrieval.\nProper management of these indexes is essential for optimal performance, scalability, and resource utilization.\n\nThis guide outlines best practices for managing Redis Search indexes throughout their lifecycle. It provides recommendations on:\n\n- Planning and creating indexes to suit your query patterns.\n- Using index aliasing to manage schema updates and minimize downtime.\n- Monitoring and verifying index population to ensure query readiness.\n- Optimizing performance through query profiling and memory management.\n- Maintaining and scaling indexes in both standalone and clustered Redis environments.\n- Versioning, testing, and automating index management."},{"id":"why-index-management-matters","title":"Why index management matters","role":"content","text":"Indexes directly impact query speed and resource consumption.\nPoorly managed indexes can lead to increased memory usage, slower query times, and challenges in maintaining data consistency.\nBy following the strategies outlined in this guide, you can:\n\n- Reduce operational overhead.\n- Improve application performance.\n- Ensure smooth transitions during schema changes.\n- Scale efficiently with your growing datasets."},{"id":"plan-your-indexes-strategically","title":"Plan your indexes strategically","role":"content","text":"Planning your indexes strategically requires understanding your application’s query patterns and tailoring indexes to match.\nBegin by identifying the types of searches your application performs—such as full-text search, range queries, or geospatial lookups—and the fields involved.\nCategorize fields based on their purpose: searchable fields (e.g., [`TEXT`](https://redis.io/docs/latest/develop/ai/search-and-query/indexing/field-and-type-options#text-fields) for full-text searches), filterable fields (e.g., [`TAG`](https://redis.io/docs/latest/develop/ai/search-and-query/indexing/field-and-type-options#tag-fields) for exact match searches), and sortable fields (e.g., [`NUMERIC`](https://redis.io/docs/latest/develop/ai/search-and-query/indexing/field-and-type-options#numeric-fields) for range queries or sorting).\nMatch field types to their intended use and avoid indexing fields that are rarely queried to conserve resources. Here's the list of index types:\n\n- [`TEXT`](https://redis.io/docs/latest/develop/ai/search-and-query/indexing/field-and-type-options#text-fields): use `TEXT` for free-text searches and set weights if some fields are more important.\n- [`TAG`](https://redis.io/docs/latest/develop/ai/search-and-query/indexing/field-and-type-options#tag-fields): use `TAG` for categorical data (e.g., product categories) that benefit from exact matching and filtering.\n- [`NUMERIC`](https://redis.io/docs/latest/develop/ai/search-and-query/indexing/field-and-type-options#numeric-fields): use `NUMERIC` for numeric ranges (e.g., prices, timestamps).\n- [`GEO`](https://redis.io/docs/latest/develop/ai/search-and-query/indexing/field-and-type-options#geo-fields): use `GEO` for geospatial coordinates (e.g., latitude/longitude).\n- [`GEOSHAPE`](https://redis.io/docs/latest/develop/ai/search-and-query/indexing/field-and-type-options#geoshape-fields): use `GEOSHAPE` to represent locations as points, but also to define shapes and query the interactions between points and shapes (e.g., to find all points that are contained within an enclosing shape).\n- [`VECTOR`](https://redis.io/docs/latest/develop/ai/search-and-query/indexing/field-and-type-options#vector-fields): use `VECTOR` for high-dimensional similarity searches.\n\nSee [these pages](https://redis.io/docs/latest/develop/ai/search-and-query/query) for discussions and examples on how best to use these index types.\n\nNext, simulate queries on a sample dataset to identify potential bottlenecks.\nUse tools like [`FT.PROFILE`](https://redis.io/docs/latest/commands/ft.profile) to analyze query execution and refine your schema if needed.\nFor example, assign weights to `TEXT` fields for prioritizing results or use the `PREFIX` option of [`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create) to limit indexing to specific key patterns. Note that you can use multiple `PREFIX` clauses when you create an index (see [below](#index-creation))\nAfter creating the index, validate its performance with real queries and monitor usage with the available tools:\n\n- [`FT.EXPLAIN`](https://redis.io/docs/latest/commands/ft.explain) and [`FT.EXPLAINCLI`](https://redis.io/docs/latest/commands/ft.explaincli) allow you to see how Redis Search parses a given search query. `FT.EXPLAIN` returns a structured breakdown of the query execution plan, while `FT.EXPLAINCLI` presents a more readable, tree-like format for easier interpretation. These commands are useful for diagnosing query structure and ensuring it aligns with the intended logic.\n- [`FT.INFO`](https://redis.io/docs/latest/commands/ft.info) provides detailed statistics about an index, including the number of indexed documents, memory usage, and configuration settings. It helps in monitoring index growth, assessing memory consumption, and verifying index structure to detect potential inefficiencies.\n- [`FT.PROFILE`](https://redis.io/docs/latest/commands/ft.profile) runs a query while capturing execution details, which helps to reveal query performance bottlenecks. It provides insights into processing time, key accesses, and filter application, making it a crucial tool for fine-tuning complex queries and optimizing search efficiency.\n\nAvoid over-indexing. Indexing every field increases memory usage and can slow down updates.\nOnly index the fields that are essential for your planned queries."},{"id":"index-creation","title":"Index creation","role":"content","text":"- Use the [`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create) command to define an index schema.\n   - Assign weights to `TEXT` fields to prioritize certain fields in full-text search results.\n   - Use the `PREFIX` option to restrict indexing to keys with specific patterns.\n      Using multiple PREFIX clauses when creating an index allows you to index multiple key patterns under a single index. This is useful in several scenarios:\n      - If your Redis database stores different types of entities under distinct key prefixes (e.g., `user:123`, `order:456`), a single index can cover both by specifying multiple prefixes. For example:\n\n          [code example]\n\n          This approach enables searching across multiple entity types without needing separate indexes.\n\n      - Instead of querying multiple indexes separately, you can search across related data structures using a single query. This is particularly helpful when data structures share common fields, such as searching both customer and vendor records under a unified contacts index.\n\n      - Maintaining multiple indexes for similar data types can be inefficient in terms of memory and query performance. By consolidating data under one index with multiple prefixes, you reduce overhead while still allowing for distinct key organization.\n\n      - If your data model evolves and new key patterns are introduced, using multiple `PREFIX` clauses from the start ensures future compatibility without requiring a full reindexing.\n   - Data loading strategy: load data into Redis before creating an index when working with large datasets. Use the `ON HASH` or `ON JSON` options to match the data structure."},{"id":"index-aliasing","title":"Index aliasing","role":"content","text":"Index aliases act as abstracted names for the underlying indexes, enabling applications to reference the alias instead of the actual index name. This approach simplifies schema updates and index management.\n\nThere are several use cases for index aliasing, including:\n\n- Schema updates: when updating an index schema, create a new index and associate the same alias with it. This allows a seamless transition without requiring application-level changes.\n- Version control: use aliases to manage different versions of an index. For example, assign the alias products to `products_v1` initially and later to `products_v2` when the schema evolves.\n- Testing and rollback: assign an alias to a test index during staged deployments. If issues arise, quickly switch the alias back to the stable index.\n\nBest practices for aliasing:\n\n- Always create an alias for your indexes during initial setup, even if you don’t anticipate immediate schema changes.\n- Use clear and descriptive alias names to avoid confusion (e.g., `users_current` or `orders_live`).\n- Make sure that an alias points to only one index at a time to maintain predictable query results.\n- Use aliases to provide tenant-specific access. For example, assign tenant-specific aliases like `tenant1_products` and `tenant2_products` to different indexes for isolated query performance.\n\nTools for managing aliases:\n\n- Assign an alias: [`FT.ALIASADD`](https://redis.io/docs/latest/commands/ft.aliasadd) `my_alias my_index`\n- Update an alias: [`FT.ALIASUPDATE`](https://redis.io/docs/latest/commands/ft.aliasupdate) `my_alias new_index`\n- Remove an alias: [`FT.ALIASDEL`](https://redis.io/docs/latest/commands/ft.aliasdel) `my_alias`\n\nMonitoring and troubleshooting aliases:\n\n- Use the `FT.INFO` command to check which aliases are associated with an index.\n- Make sure your aliases always points to valid indexes and are correctly updated during schema changes."},{"id":"monitor-index-population","title":"Monitor index population","role":"content","text":"- Use the `FT.INFO` command to monitor the `num_docs` and `indexing` fields, to check that all expected documents are indexed.\n    [code example]\n- Validate data with sample queries to ensure proper indexing:\n    [code example]\n- Use `FT.PROFILE` to analyze query plans and validate performance:\n\n    [code example]\n - Implement scripts to periodically verify document counts and query results. For example, in Python:\n\n    [code example]"},{"id":"monitoring-index-performance","title":"Monitoring index performance","role":"content","text":"- Use the `FT.PROFILE` command to analyze query performance and identify bottlenecks.\n- Regularly monitor memory usage with the [`INFO`](https://redis.io/docs/latest/commands/info) `memory` and `FT.INFO` commands to detect growth patterns and optimize resource allocation."},{"id":"index-maintenance","title":"Index maintenance","role":"content","text":"- If schema changes are required, create a new index with the updated schema and reassign the alias once the index is ready.\n- Use [Redis key expiration](https://redis.io/docs/latest/develop/using-commands/keyspace#key-expiration) to automatically remove outdated records and keep indexes lean."},{"id":"ftalter-vs-aliasing","title":"FT.ALTER vs. aliasing","role":"content","text":"Use [`FT.ALTER`](https://redis.io/docs/latest/commands/ft.alter) when you need to add new fields to an existing index without rebuilding it, minimizing downtime and resource usage. However, `FT.ALTER` cannot remove or modify existing fields, limiting its flexibility.\n\nUse index aliasing when making schema changes that require reindexing, such as modifying field types or removing fields. In this case, create a new index with the updated schema, populate it, and then use `FT.ALIASUPDATE` to seamlessly switch queries to the new index without disrupting application functionality."},{"id":"scaling-and-high-availability","title":"Scaling and high availability","role":"content","text":"- In a clustered Redis setup, make sure indexes are designed with key distribution in mind to prevent query inefficiencies.\n- Test how indexes behave under replica promotion to ensure consistent query behavior across nodes."},{"id":"versioning-and-testing","title":"Versioning and testing","role":"compatibility","text":"- When changing schemas, create a new version of the index alongside the old one and migrate data progressively.\n- Test index changes in a staging environment before deploying them to production."},{"id":"cleaning-up","title":"Cleaning up","role":"content","text":"- Use the [`FT.DROPINDEX`](https://redis.io/docs/latest/commands/ft.dropindex) command to remove unused indexes and free up memory. Be cautious with the `DD` (Delete Documents) flag to avoid unintended data deletion.\n- Make sure no keys remain that were previously associated with dropped indexes if the data is no longer relevant."},{"id":"documentation-and-automation","title":"Documentation and automation","role":"content","text":"- Document your index configurations to facilitate future maintenance.\n- Use scripts or orchestration tools to automate index creation, monitoring, and cleanup."}],"examples":[{"id":"index-creation-ex0","language":"bash","code":"FT.CREATE my_index ON HASH PREFIX 2 \"user:\" \"order:\" SCHEMA name TEXT age NUMERIC status TAG","section_id":"index-creation"},{"id":"monitor-index-population-ex0","language":"bash","code":"FT.INFO my_new_index","section_id":"monitor-index-population"},{"id":"monitor-index-population-ex1","language":"bash","code":"FT.SEARCH my_new_index \"*\"","section_id":"monitor-index-population"},{"id":"monitor-index-population-ex2","language":"bash","code":"FT.PROFILE my_new_index SEARCH QUERY \"your_query\"","section_id":"monitor-index-population"},{"id":"monitor-index-population-ex3","language":"python","code":"import re    \n    def check_index_readiness(index_name, expected_docs):\n        r = redis.StrictRedis(host='localhost', port=6379, decode_responses=True)\n        info = r.execute_command('FT.INFO', index_name)\n        num_docs = int(info[info.index('num_docs') + 1])\n        return num_docs >= expected_d    \n    if check_index_readiness('my_new_index', 100000):\n        print(\"Index is fully populated!\")\n    else:\n        print(\"Index is still populating...\")","section_id":"monitor-index-population"}]}
{"schema_version":2,"id":"develop/ai/search-and-query/best-practices","title":"Best practices","url":"https://redis.io/docs/latest/develop/ai/search-and-query/best-practices/","summary":"Redis Search best practices","aliases":["/develop/interact/search-and-query/best-practices","/develop/interact/search-and-query/best-practices/"],"tags":["docs","develop","stack","oss"],"last_updated":"2026-03-09T06:38:54-07:00","children":[{"id":"develop/ai/search-and-query/best-practices/scalable-query-best-practices","summary":"","title":"Best practices for Redis Search performance","url":"https://redis.io/docs/latest/develop/ai/search-and-query/best-practices/scalable-query-best-practices/"},{"id":"develop/ai/search-and-query/best-practices/dev-to-prod-best-practices","summary":"","title":"Move from Development to Production with Redis Search","url":"https://redis.io/docs/latest/develop/ai/search-and-query/best-practices/dev-to-prod-best-practices/"},{"id":"develop/ai/search-and-query/best-practices/index-mgmt-best-practices","summary":"","title":"Index management best practices for Redis Search","url":"https://redis.io/docs/latest/develop/ai/search-and-query/best-practices/index-mgmt-best-practices/"}],"page_type":"index","sections":[],"examples":[]}
{"schema_version":2,"id":"develop/ai/search-and-query/best-practices/scalable-query-best-practices","title":"Best practices for Redis Search performance","url":"https://redis.io/docs/latest/develop/ai/search-and-query/best-practices/scalable-query-best-practices/","summary":"","aliases":["/develop/interact/search-and-query/best-practices/scalable-query-best-practices"],"tags":["docs","develop","stack","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:33:04+01:00","page_type":"content","content_hash":"3c386c3cad51e38d5a279532c02ed8350fb7b42e6d8ca2ab1f05a46d1a74c36e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> If you're using Redis Software or Redis Cloud, see the [best practices for scalable Redis Search](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/scalable-query-best-practices) page."},{"id":"checklist","title":"Checklist","role":"content","text":"Below are some basic steps to ensure good performance of Redis Search .\n\n* Create a Redis data model with your query patterns in mind.\n* Ensure the Redis architecture has been sized for the expected load using the [sizing calculator](https://redis.io/redisearch-sizing-calculator/).\n* Provision Redis nodes with sufficient resources (RAM, CPU, network) to support the expected maximum load.\n* Review [`FT.INFO`](https://redis.io/docs/latest/commands/ft.info) and [`FT.PROFILE`](https://redis.io/docs/latest/commands/ft.profile) outputs for anomalies and/or errors.\n* Conduct load testing in a test environment with real-world queries and a load generated by either [memtier_benchmark](https://github.com/redislabs/memtier_benchmark) or a custom load application."},{"id":"indexing-considerations","title":"Indexing considerations","role":"content","text":""},{"id":"general","title":"General","role":"content","text":"- Favor [`TAG`](https://redis.io/docs/latest/develop/ai/search-and-query/indexing/field-and-type-options#tag-fields) over [`NUMERIC`](https://redis.io/docs/latest/develop/ai/search-and-query/indexing/field-and-type-options#numeric-fields) for use cases that only require matching.\n- Favor [`TAG`](https://redis.io/docs/latest/develop/ai/search-and-query/indexing/field-and-type-options#tag-fields) over [`TEXT`](https://redis.io/docs/latest/develop/ai/search-and-query/indexing/field-and-type-options#text-fields) for use cases that don’t require full-text capabilities (pure match)."},{"id":"non-threaded-search","title":"Non-threaded search","role":"content","text":"- Put only those fields used in your queries in the index.\n- Only make fields [`SORTABLE`](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/sorting) if they are used in [`SORTBY`](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/sorting#specifying-sortby)\nqueries.\n- Use [`DIALECT 2`](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/dialects#dialect-2)."},{"id":"threaded-query-performance-factor-or-qpf-search","title":"Threaded (query performance factor or QPF) search","role":"content","text":"- Put both query fields and any projected fields (`RETURN` or `LOAD`) in the index.\n- Set all fields to `SORTABLE`.\n- Set TAG fields to [UNF](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/sorting#normalization-unf-option).\n- Optional: Set `TEXT` fields to `NOSTEM` if the use case will support it.\n- Use [`DIALECT 2`](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/dialects#dialect-2)."},{"id":"query-optimization","title":"Query optimization","role":"content","text":"- Avoid returning large result sets.  Use `CURSOR` or `LIMIT`.\n- Avoid wildcard searches.\n- Avoid projecting all fields (e.g., `LOAD *`). Project only those fields that are part of the index schema.\n- If queries are long-running, enable threading (query performance factor) to reduce contention for the main Redis thread."},{"id":"validate-performance-ftprofile","title":"Validate performance (`FT.PROFILE`)","role":"content","text":"You can analyze [`FT.PROFILE`](https://redis.io/docs/latest/commands/ft.profile) output to gain insights about query execution.\nThe following informational items are available for analysis:\n\n- Total execution time\n- Execution time per shard\n- Coordination time (for multi-sharded environments)\n- Breakdown of the query into fundamental components, such as `UNION` and `INTERSECT`\n- Warnings, such as `TIMEOUT`"},{"id":"anti-patterns","title":"Anti-patterns","role":"content","text":"When designing and querying indexes in Redis Search, certain practices can hinder performance, scalability, and maintainability. Below are some common anti-patterns to avoid:\n\n- **Large documents**: storing excessively large documents in Redis makes data retrieval slower and increases memory usage. Break data into smaller, focused records whenever possible.\n- **Deeply-nested fields**: retrieving or indexing deeply-nested JSON fields is computationally expensive. Use a flatter schema for better performance.\n- **Large result sets**: fetching unnecessarily large result sets puts a strain on memory and network resources. Limit results to only what is needed.\n- **Wildcarding**: using wildcard patterns indiscriminately in queries can lead to large and inefficient scans, especially if the index size is significant.\n- **Large projections**: including excessive fields in query results increases memory overhead and slows down query execution. Limit projections to essential fields.\n\nThe following examples depict an anti-pattern index schema and query, followed by corrected versions designed for scalability with Redis Search."},{"id":"anti-pattern-index-schema","title":"Anti-pattern index schema","role":"content","text":"The following schema introduces challenges for scalability and performance:\n\n[code example]\n\nIssues:\n\n- Minimal schema definition: the schema is sparse and lacks fields like `lastName`, `id`, and `version` that might be frequently queried. This results in additional operations to fetch these fields separately, reducing efficiency.\n- Missing `SORTABLE` flag for text fields: sorting operations on unsortable fields require full-text processing, which is slow.\n- Wildcard indexing: `$.tags.*` creates a broad index that can lead to excessive memory usage and reduced query performance."},{"id":"anti-pattern-query","title":"Anti-pattern query","role":"content","text":"The following query is inefficient and not optimized for vertical scaling:\n\n[code example]\nIssues:\n\n- Wildcard projection (`LOAD *`): retrieving all fields in the result set is inefficient and increases memory usage, especially if the documents are large.\n- Unnecessary fields: fields that aren't required for the current operation are still fetched, slowing down execution.\n- Lack of advanced query syntax: without specifying a query dialect or leveraging features like tagging, the query may perform unnecessary computations."},{"id":"improved-index-schema","title":"Improved index schema","role":"content","text":"Here’s an optimized schema that adheres to best practices for vertical scaling:\n\n[code example]\n\nImprovements:\n\n- `NOSTEM` for text fields: prevents stemming on fields like `firstName` and `lastName` to allow for exact matches (e.g., \"Smith\" stays \"Smith\").\n- Expanded schema: adds commonly queried fields like `lastName`, `id`, and `version`, making queries more efficient by reducing the need for post-query data retrieval.\n- `TAG` fields: `id` and `ver` are defined as `TAG` fields to support fast filtering with exact matches.\n- `SORTABLE` for all relevant fields: ensures that sorting operations are efficient without requiring full-text scanning.\n\nYou might be wondering why `$.tags.* as t NUMERIC SORTABLE` is acceptable in the improved schema and it wasn't previously.\nThe inclusion of `$.tags.*` is acceptable when:\n\n- It has a clear purpose: it is actively used in queries, such as filtering on numeric ranges or matching specific values.\n- Other fields in the schema complement it: these fields reduce over-reliance on `$.tags.*` for all query operations, distributing the load more evenly.\n- Projections and limits are managed carefully: queries that use `$.tags.*` should avoid loading unnecessary fields or returning excessively large result sets."},{"id":"improved-query","title":"Improved query","role":"content","text":"The following query is better suited for vertical scaling:\n\n[code example]\n\nImprovements:\n\n- Targeted projection: the `LOAD` clause specifies only essential fields (`id, t, name, lastname, loc, ver`), reducing memory and network overhead.\n- Limited results: the `LIMIT` clause ensures the query retrieves only the first 10 results, avoiding large result sets.\n- [`DIALECT 2`](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/dialects#dialect-2): enables the latest Redis Search syntax and features, ensuring compatibility with modern capabilities."}],"examples":[{"id":"anti-pattern-index-schema-ex0","language":"sh","code":"FT.CREATE jsonidx:profiles ON JSON PREFIX 1 profiles: \n          SCHEMA $.tags.* as t NUMERIC SORTABLE \n                 $.firstName as name TEXT \n                 $.location as loc GEO","section_id":"anti-pattern-index-schema"},{"id":"anti-pattern-query-ex0","language":"sh","code":"FT.AGGREGATE jsonidx:profiles '@t:[1299 1299]' LOAD * LIMIT 0 10","section_id":"anti-pattern-query"},{"id":"improved-index-schema-ex0","language":"sh","code":"FT.CREATE jsonidx:profiles ON JSON PREFIX 1 profiles: \n          SCHEMA $.tags.* as t NUMERIC SORTABLE \n                 $.firstName as name TEXT NOSTEM SORTABLE \n                 $.lastName as lastname TEXT NOSTEM SORTABLE \n                 $.location as loc GEO SORTABLE \n                 $.id as id TAG SORTABLE UNF \n                 $.ver as ver TAG SORTABLE UNF","section_id":"improved-index-schema"},{"id":"improved-query-ex0","language":"sh","code":"FT.AGGREGATE jsonidx:profiles '@t:[1299 1299]' \n                LOAD 6 id t name lastname loc ver \n                LIMIT 0 10\n                DIALECT 2","section_id":"improved-query"}]}
{"schema_version":2,"id":"develop/ai/search-and-query/deprecated/development","title":"Developer notes","url":"https://redis.io/docs/latest/develop/ai/search-and-query/deprecated/development/","summary":"Notes on RediSearch debugging, testing, and documentation","aliases":["/develop/interact/search-and-query/deprecated/development"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2025-06-26T15:11:30-07:00","page_type":"content","content_hash":"72b28a5a7f1e186f25bba7ab260181669f9046e760aa4ca05705984c21c1f005","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Developing RediSearch features involves setting up a development environment (which can be either Linux-based or macOS-based), building the module, running tests and benchmarks, and debugging both the module and its tests."},{"id":"cloning-the-git-repository","title":"Cloning the git repository","role":"content","text":"Run the following command to clone the RediSearch module and its submodules:\n\n[code example]"},{"id":"working-in-an-isolated-environment","title":"Working in an isolated environment","role":"content","text":"There are several reasons to develop in an isolated environment, like keeping your workstation clean, and developing for a different Linux distribution.\nThe most general option for an isolated environment is a virtual machine. It's very easy to set one up using [Vagrant](https://www.vagrantup.com)).\nDocker is even more agile, as it offers an almost instant solution:\n\n[code example]\n\nThen, from within the container, `cd /build` and go on as usual.\n\nIn this mode, all installations remain in the scope of the Docker container.\nUpon exiting the container, you can either re-invoke it with the above `docker exec` or commit the state of the container to an image and re-invoke it at a later stage:\n\n[code example]\n\nYou can replace `debian:bullseye` with your choice of OS, with the host OS being the best choice allowing you to run the RediSearch binary on your host after it is built."},{"id":"installing-prerequisites","title":"Installing prerequisites","role":"setup","text":"To build and test RediSearch you need to install several packages, depending on the underlying OS. The following OSes are supported:\n- Ubuntu 18.04\n- Ubuntu 20.04\n- Ubuntu 22.04\n- Debian linux 11\n- Rocky linux 8\n- Rocky linux 9\n- Amazon linux 2\n- Mariner 2.0\n- MacOS\n\nTo install the prerequisites on your system using a setup script, first enter the `RediSearch` directory and then run:\n\n[code example]\n\nNote that this will install various packages on your system using the native package manager (`sudo` is not required in a Docker environment).\n\nIf you prefer to avoid that, you can:\n\n* Review the relevant setup scripts under the `./install` directory and install packages manually.\n* Use an isolated environment as explained above."},{"id":"installing-redis","title":"Installing Redis","role":"setup","text":"As a rule of thumb, you should run the latest Redis version.\n\nIf your OS has a Redis 7.x package, you can install it using the OS package manager.\n\nOtherwise, you can build it from source and install it as described in [redis GitHub page](https://github.com/redis/redis)."},{"id":"getting-help","title":"Getting help","role":"content","text":"\nmake fetch         # download and prepare dependant modules\n\nmake build          # compile and link\n  COORD=1             # build coordinator\n  DEBUG=1             # build for debugging\n  NO_TESTS=1          # disable unit tests\n  WHY=1               # explain CMake decisions (in /tmp/cmake-why)\n  FORCE=1             # Force CMake rerun (default)\n  CMAKE_ARGS=...      # extra arguments to CMake\n  VG=1                # build for Valgrind\n  SAN=type            # build with LLVM sanitizer (type=address|memory|leak|thread) \n  SLOW=1              # do not parallelize build (for diagnostics)\n  GCC=1               # build with GCC (default unless Sanitizer)\n  CLANG=1             # build with CLang\n  STATIC_LIBSTDCXX=0  # link libstdc++ dynamically (default: 1)\nmake parsers       # build parsers code (required after chaging files under query_parser dir)\nmake clean         # remove build artifacts\n  ALL=1              # remove entire artifacts directory\n\nmake run           # run redis with RediSearch\n  COORD=1            # run three local shards with coordinator (assuming the module was built with coordinator support)\n  GDB=1              # invoke using gdb\n\nmake test          # run all tests\n  COORD=1            # test coordinator\n  TEST=name          # run specified test\nmake pytest        # run python tests (tests/pytests)\n  COORD=1            # test coordinator \n  TEST=name          # e.g. TEST=test:testSearch\n  RLTEST_ARGS=...    # pass args to RLTest\n  REJSON=1|0|get     # also load JSON module (default: 1)\n  REJSON_PATH=path   # use JSON module at `path`\n  EXT=1              # External (existing) environment\n  GDB=1              # RLTest interactive debugging\n  VG=1               # use Valgrind\n  VG_LEAKS=0         # do not search leaks with Valgrind\n  SAN=type           # use LLVM sanitizer (type=address|memory|leak|thread) \nmake unit-tests    # run unit tests (C and C++)\n  TEST=name          # e.g. TEST=FGCTest.testRemoveLastBlock\nmake c_tests       # run C tests (from tests/ctests)\nmake cpp_tests     # run C++ tests (from tests/cpptests)\n\nmake callgrind     # produce a call graph\n  REDIS_ARGS=\"args\"\n\nmake sanbox        # create container with CLang Sanitizer\n```"},{"id":"building-from-source","title":"Building from source","role":"content","text":"Run the following from the project root dir:\n\n[code example]make build[code example]make clean``` to remove build artifacts. ```make clean ALL=1``` will remove the entire `bin` subdirectory."},{"id":"diagnosing-the-build-process","title":"Diagnosing the build process","role":"content","text":"`make build` will build in parallel by default.\n\nFor the purposes of build diagnosis, `make build SLOW=1 VERBOSE=1` can be used to examine compilation commands."},{"id":"running-redis-with-redisearch","title":"Running Redis with RediSearch","role":"content","text":"The following will run \nmake run\n[code example]redis-cli``` in another terminal to interact with it."},{"id":"running-tests","title":"Running tests","role":"content","text":"There are several sets of unit tests:\n* C tests, located in [code example]tests/cpptests[code example]tests/pytests[code example]make test[code example]TEST``` parameter, e.g., ```make test TEST=regex```."},{"id":"debugging","title":"Debugging","role":"content","text":"To build for debugging (enabling symbolic information and disabling optimization), run [code example]make run DEBUG=1[code example]gdb[code example]BB()``` function inside a test."}],"examples":[{"id":"cloning-the-git-repository-ex0","language":"sh","code":"git clone --recursive https://github.com/RediSearch/RediSearch.git","section_id":"cloning-the-git-repository"},{"id":"working-in-an-isolated-environment-ex0","language":"plaintext","code":"search=$(docker run -d -it -v $PWD:/build debian:bullseye bash)\ndocker exec -it $search bash","section_id":"working-in-an-isolated-environment"},{"id":"working-in-an-isolated-environment-ex1","language":"plaintext","code":"docker commit $search redisearch1\ndocker stop $search\nsearch=$(docker run -d -it -v $PWD:/build rediseatch1 bash)\ndocker exec -it $search bash","section_id":"working-in-an-isolated-environment"},{"id":"installing-prerequisites-ex0","language":"plaintext","code":"cd ./install\n./install_script.sh sudo\n./install_boost.sh 1.83.0","section_id":"installing-prerequisites"},{"id":"building-from-source-ex0","language":"make build``` will build RediSearch.","code":"`make build COORD=1` will build Redis Open Source RediSearch Coordinator.\n\n`make build STATIC=1` will build as a static library.\n\nNotes:\n\n* Binary files are placed under `bin`, according to platform and build variant.\n* RediSearch uses [CMake](https://cmake.org) as its build system.","section_id":"building-from-source"},{"id":"building-from-source-ex1","language":" will invoke both CMake and the subsequent make command that's required to complete the build.","code":"Use","section_id":"building-from-source"},{"id":"running-redis-with-redisearch-ex0","language":"plaintext","code":"You can open","section_id":"running-redis-with-redisearch"},{"id":"running-tests-ex0","language":"tests/ctests```, run by ```make c-tests```.","code":"* C++ tests (enabled by GTest), located in","section_id":"running-tests"},{"id":"running-tests-ex1","language":", run by ```make cpp-tests```.","code":"* Python tests (enabled by RLTest), located in","section_id":"running-tests"},{"id":"running-tests-ex2","language":", run by ```make pytest```.","code":"You can run all tests by invoking","section_id":"running-tests"},{"id":"running-tests-ex3","language":".","code":"A single test can be run using the","section_id":"running-tests"},{"id":"debugging-ex0","language":"make DEBUG=1```.","code":"You can then use","section_id":"debugging"},{"id":"debugging-ex1","language":" to invoke ```gdb```.","code":"In addition to the usual way to set breakpoints in","section_id":"debugging"},{"id":"debugging-ex2","language":", it is possible to use the ```BB``` macro to set a breakpoint inside the RediSearch code. It will only have an effect when running under ```gdb```.","code":"Similarly, Python tests in a single-test mode, you can set a breakpoint by using the","section_id":"debugging"}]}
{"schema_version":2,"id":"develop/ai/search-and-query/deprecated","title":"Deprecated","url":"https://redis.io/docs/latest/develop/ai/search-and-query/deprecated/","summary":"Deprecated features","aliases":["/develop/interact/search-and-query/deprecated","/develop/interact/search-and-query/deprecated/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2025-07-02T18:18:33-07:00","children":[{"id":"develop/ai/search-and-query/deprecated/development","summary":"Notes on RediSearch debugging, testing, and documentation","title":"Developer notes","url":"https://redis.io/docs/latest/develop/ai/search-and-query/deprecated/development/"},{"id":"develop/ai/search-and-query/deprecated/payloads","summary":"Payload support(deprecated)","title":"Document payloads","url":"https://redis.io/docs/latest/develop/ai/search-and-query/deprecated/payloads/"}],"page_type":"index","sections":[],"examples":[]}
{"schema_version":2,"id":"develop/ai/search-and-query/deprecated/payloads","title":"Document payloads","url":"https://redis.io/docs/latest/develop/ai/search-and-query/deprecated/payloads/","summary":"Payload support(deprecated)","aliases":["/develop/interact/search-and-query/deprecated/payloads"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:33:04+01:00","page_type":"content","content_hash":"64b36d58c504120c1a05a76a27aa1457c42e035f725c7c29bb0430ea2ad757c7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!WARNING]\n> The payload feature is deprecated in 2.0\n    \nUsually, Redis Open Source stores documents as hashes or JSON. But if you want to access some data for aggregation or scoring functions, Redis can store that data as an inline payload. This will allow us to evaluate the properties of a document for scoring purposes at a very low cost.\n\nSince the scoring functions already have access to the DocumentMetaData, which contains document flags and score, Redis can add custom payloads that can be evaluated in run-time.\n\nPayloads are NOT indexed and are not treated by the engine in any way. They are simply there for the purpose of evaluating them in query time, and optionally retrieving them. They can be JSON objects, strings, or preferably, if you are interested in fast evaluation, some sort of binary encoded data which is fast to decode."},{"id":"evaluating-payloads-in-query-time","title":"Evaluating payloads in query time","role":"content","text":"When implementing a scoring function, the signature of the function exposed is:\n\n[code example]\n\n> [!NOTE]\n> Currently, scoring functions cannot be dynamically added, and forking the engine and replacing them is required.\n\nDocumentMetaData includes a few fields, one of them being the payload. It wraps a simple byte array with arbitrary length:\n\n[code example]\n\nIf no payload was set to the document, it is simply NULL. If it is not, you can go ahead and decode it. It is recommended to encode some metadata about the payload inside it, like a leading version number, etc."},{"id":"retrieving-payloads-from-documents","title":"Retrieving payloads from documents","role":"content","text":"When searching, it is possible to request the document payloads from the engine. \n\nThis is done by adding the keyword `WITHPAYLOADS` to [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search). \n\nIf `WITHPAYLOADS` is set, the payloads follow the document id in the returned result. \nIf `WITHSCORES` is set as well, the payloads follow the scores."}],"examples":[{"id":"evaluating-payloads-in-query-time-ex0","language":"c","code":"double (*ScoringFunction)(DocumentMetadata *dmd, IndexResult *h);","section_id":"evaluating-payloads-in-query-time"},{"id":"evaluating-payloads-in-query-time-ex1","language":"c","code":"typedef struct  {\n    char *data,\n    uint32_t len;\n} DocumentPayload;","section_id":"evaluating-payloads-in-query-time"}]}
{"schema_version":2,"id":"develop/ai/search-and-query","title":"Redis Search","url":"https://redis.io/docs/latest/develop/ai/search-and-query/","summary":"Searching and querying Redis data using Redis Search","aliases":["/develop/interact/search-and-query","/develop/interact/search-and-query/","/interact/search-and-query/","/search/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:33:04+01:00","children":[{"id":"develop/ai/search-and-query/indexing","summary":"How to index and search JSON documents","title":"Indexing","url":"https://redis.io/docs/latest/develop/ai/search-and-query/indexing/"},{"id":"develop/ai/search-and-query/query","summary":"Understand how to query, search, and aggregate Redis data","title":"Querying data","url":"https://redis.io/docs/latest/develop/ai/search-and-query/query/"},{"id":"develop/ai/search-and-query/advanced-concepts","summary":"Details about query syntax, aggregation, scoring, and other search and query options","title":"Search concepts","url":"https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/"},{"id":"develop/ai/search-and-query/vectors","summary":"Learn how to use vector fields and perform vector searches in Redis","title":"Vector search concepts","url":"https://redis.io/docs/latest/develop/ai/search-and-query/vectors/"},{"id":"develop/ai/search-and-query/administration","summary":"Redis Search Administration","title":"Administration","url":"https://redis.io/docs/latest/develop/ai/search-and-query/administration/"},{"id":"develop/ai/search-and-query/best-practices","summary":"Redis Search best practices","title":"Best practices","url":"https://redis.io/docs/latest/develop/ai/search-and-query/best-practices/"},{"id":"develop/ai/search-and-query/deprecated","summary":"Deprecated features","title":"Deprecated","url":"https://redis.io/docs/latest/develop/ai/search-and-query/deprecated/"}],"page_type":"content","content_hash":"3ef391919cbb3eb9cd7fd58328d152ba1da7e6a0fe6968bbb8bbb3984eadcf4e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Search offers an enhanced Redis experience via the following search and query features:\n\n- A rich query language\n- Incremental indexing on JSON and hash documents\n- Vector search\n- Full-text search\n- Geospatial queries\n- Aggregations\n\nYou can find a complete list of features in the [reference documentation](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts).\n\nRedis Search features allow you to use Redis as a:\n\n- Document database\n- Vector database\n- Secondary index\n- Search engine\n\nHere are the next steps to get you started:\n\n1. Follow our [quick start guide](https://redis.io/docs/latest/develop/get-started/search-tutorial) to get some initial hands-on experience.\n1. Learn how to [create an index](https://redis.io/docs/latest/develop/ai/search-and-query/indexing).\n1. Learn how to [query your data](https://redis.io/docs/latest/develop/ai/search-and-query/query).\n1. [Install Redis Insight](https://redis.io/docs/latest/operate/redisinsight), connect it to your Redis database, and then use [Redis Copilot](https://redis.io/docs/latest/develop/tools/insight#redis-copilot) to help you learn how to execute complex queries against your own data using simple, plain language prompts.\n1. Open the [AI agent builder](https://redis.io/docs/latest/develop/ai/agent-builder) and choose the **Knowledge Assistant** template to generate a working RAG agent built on Redis vector search.\n\n> [!TIP]\n> See Redis vector search in a real workflow: [Redis Repo Memory](https://github.com/marketplace/actions/redis-repo-memory) is a GitHub Action that surfaces related past PRs, issues, and commits on every pull request. Add it to any repository in a few minutes."},{"id":"enable-redis-search","title":"Enable Redis Search","role":"content","text":"Redis Search is available in Redis Open Source, Redis Software, and Redis Cloud.\nSee\n[Install Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack) or\n[Install Redis Software](https://redis.io/docs/latest/operate/rs/installing-upgrading/install)\nfor full installation instructions.\n\n> [!NOTE] Try it out\n> Experiment with Redis Search interactively in the [Redis playground](https://redis.io/try/sandbox) — no installation required."},{"id":"license-and-source-code","title":"License and source code","role":"content","text":"The Redis Search features of Redis are available under the Source Available License 2.0 (RSALv2), the Server Side Public License v1 (SSPLv1), or the GNU Affero General Public License version 3 (AGPLv3). Please read the [license file](https://raw.githubusercontent.com/RediSearch/RediSearch/master/LICENSE.txt) for further details. The source code and the [detailed release notes](https://github.com/RediSearch/RediSearch/releases) are available on [GitHub](https://github.com/RediSearch/RediSearch).\n\nDo you have questions? Feel free to ask at the [RediSearch forum](https://forum.redis.com/c/modules/redisearch/).\n\n<br/>"}],"examples":[]}
{"schema_version":2,"id":"develop/ai/search-and-query/indexing/field-and-type-options","title":"Field and type options","url":"https://redis.io/docs/latest/develop/ai/search-and-query/indexing/field-and-type-options/","summary":"Available field types and options.","aliases":["/develop/interact/search-and-query/indexing/field-and-type-options","/develop/interact/search-and-query/basic-constructs/field-and-type-options"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:33:04+01:00","page_type":"content","content_hash":"bbf825a37e69a08d70683d9eb68661cfc5732c405881085b3a144fa4f2ca3bae","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Open Source provides various field types that allow you to store and search different kinds of data in your indexes. This page explains the available field types, their characteristics, and how they can be used effectively."},{"id":"numeric-fields","title":"Numeric fields","role":"content","text":"Numeric fields are used to store non-textual, countable values. They can hold integer or floating-point values. Numeric fields are sortable, meaning you can perform range-based queries and retrieve documents based on specific numeric conditions. For example, you can search for documents with a price between a certain range or retrieve documents with a specific rating value.\n\nYou can add number fields to a schema in [`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create) using this syntax:\n\n[code example]\n\nwhere:\n\n- `SORTABLE` indicates that the field can be sorted. This is useful for performing range queries and sorting search results based on numeric values.\n- `NOINDEX` indicates that the field is not indexed. This is useful for storing numeric values that you don't want to search for, but that you want to retrieve in search results.\n\nYou can search for documents with specific numeric values using the `@<field_name>:[<min> <max>]` query syntax. For example, this query finds documents with a price between 200 and 300:\n\n[code example]\n\nYou can also use the following query syntax to perform more complex numeric queries:  \n\n| **Comparison operator** | **Query string**              | **Comment**              |\n|-------------------------|-------------------------------|--------------------------|\n| min <= x <= max   | @field:[min max]              | Fully inclusive range    |\n|                         | \"@field>=min @field<=max\"     | Fully inclusive range \\* |\n| min < x < max     | @field:[(min (max]            | Fully exclusive range    |\n|                         | \"@field>min @field<max\"       | Fully exclusive range \\* |\n|                         |                               |   grouping with a space denotes AND relationship |\n| x >= min                | @field:[min +inf]             | Upper open range         |\n|                         | @field>=min                   | Upper open range \\*      |\n| x <= max             | @field:[-inf max]             | Lower open range         |\n|                         | @field<=max                   | Lower open range \\*      |\n| x == val                | @field:[val val]              | Equal                    |\n|                         | @field:[val]                  | Equal \\*                 |\n|                         | @field==val                   | Equal \\*                 |\n| x != val                | -@field:[val val]             | Not equal                |\n|                         | @field!=val                   | Not equal \\*             |\n| x == val1 or x == val2  | \"@field==val1 \\| @field==val2\" | Grouping with a bar denotes OR relationship \\* |\n\n\\* New syntax as of RediSearch v2.10. Requires [`DIALECT 2`](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/dialects#dialect-2)."},{"id":"geo-fields","title":"Geo fields","role":"content","text":"Geo fields are used to store geographical coordinates such as longitude and latitude. They enable geospatial radius queries, which allow you to implement location-based search functionality in your applications such as finding nearby restaurants, stores, or any other points of interest.\n\nRedis Search also supports [geoshape fields](#geoshape-fields) for more advanced\ngeospatial queries. See the\n[Geospatial](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/geo)\nreference page for an introduction to the format and usage of both schema types.\n\nYou can add geo fields to the schema in [`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create) using this syntax:\n\n[code example]\n\nWhere:\n- `SORTABLE` indicates that the field can be sorted. This is useful for performing range queries and sorting search results based on coordinates.\n- `NOINDEX` indicates that the field is not indexed. This is useful for storing coordinates that you don't want to search for, but that you still want to retrieve in search results.\n\nYou can query geo fields using the `@<field_name>:[<lon> <lat> <radius> <unit>]` query syntax. For example, this query finds documents within 1000 kilometers from the point `2.34, 48.86`:\n\n[code example]\n\nSee\n[Geospatial queries](https://redis.io/docs/latest/develop/ai/search-and-query/query/geo-spatial)\nfor more information and code examples."},{"id":"geoshape-fields","title":"Geoshape fields","role":"content","text":"Geoshape fields provide more advanced functionality than [Geo](#geo-fields).\nYou can use them to represent locations as points but also to define\nshapes and query the interactions between points and shapes (for example,\nto find all points that are contained within an enclosing shape). You can\nalso choose between geographical coordinates (on the surface of a sphere)\nor standard Cartesian coordinates. Use geoshape fields for spatial queries\nsuch as finding all office locations in a specified region or finding\nall rooms in a building that fall within range of a wi-fi router.\n\nSee the\n[Geospatial](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/geo)\nreference page for an introduction to the format and usage of both the\ngeoshape and geo schema types.\n\nAdd geoshape fields to the schema in\n[`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create) using the following syntax:\n\n[code example]\n\nWhere:\n-   `FLAT` indicates Cartesian (planar) coordinates.\n-   `SPHERICAL` indicates spherical (geographical) coordinates. This is the\n    default option if you don't specify one explicitly.\n-   `NOINDEX` indicates that the field is not indexed. This is useful for storing\n    coordinates that you don't want to search for, but that you still want to retrieve\n    in search results.\n\nNote that unlike geo fields, geoshape fields don't support the `SORTABLE` option.\n\nQuery geoshape fields using the syntax `@<field_name>:[<OPERATION> <shape>]`\nwhere `<operation>` is one of `WITHIN`, `CONTAINS`, `INTERSECTS`, or `DISJOINT`,\nand `<shape>` is the shape of interest, specified in the\n[Well-known text](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry)\nformat. For example, the query below finds shapes that contain the point (2, 2):\n\n[code example]\n\nSee\n[Geospatial queries](https://redis.io/docs/latest/develop/ai/search-and-query/query/geo-spatial)\nfor more information and code examples."},{"id":"vector-fields","title":"Vector fields","role":"content","text":"Vector fields are floating-point vectors that are typically generated by external machine learning models. These vectors represent unstructured data such as text, images, or other complex features. Redis allows you to search for similar vectors using vector search algorithms like cosine similarity, Euclidean distance, and inner product. This enables you to build advanced search applications, recommendation systems, or content similarity analysis.\n\nYou can add vector fields to the schema in [`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create) using this syntax:\n\n[code example]\n\nWhere:\n\n* `{algorithm}` must be specified and be a supported vector similarity index algorithm. The supported algorithms are:\n\n    - `FLAT`: brute force algorithm.\n    - `HNSW`: hierarchical, navigable, small world algorithm.\n    - `SVS-VAMANA`: a graph-based nearest neighbor search algorithm, which is optimized for use with compression methods to reduce its memory footprint.\n\n    The `{algorithm}` attribute specifies the algorithm to use when searching `k` most similar vectors in the index or filtering vectors by range.\n\n* `{count}` specifies the number of attributes for the index and it must be present. \nNotice that `{count}` represents the total number of attribute pairs passed in the command. Algorithm parameters should be submitted as named arguments. \n\n    For example:\n\n    [code example]\n\n    Here, three parameters are passed for the index ([`TYPE`](https://redis.io/docs/latest/commands/type), `DIM`, `DISTANCE_METRIC`), and `count` is the total number of attributes (6).\n\n* `{attribute_name} {attribute_value}` are algorithm attributes for the creation of the vector index. Every algorithm has its own mandatory and optional attributes.\n\nFor more information about vector fields, see [vector fields](https://redis.io/docs/latest/develop/ai/search-and-query/vectors)."},{"id":"tag-fields","title":"Tag fields","role":"content","text":"Tag fields are used to store textual data that represents a collection of data tags or labels. Tag fields are characterized by their low cardinality, meaning they typically have a limited number of distinct values. Unlike text fields, tag fields are stored as-is without tokenization or stemming. They are useful for organizing and categorizing data, making it easier to filter and retrieve documents based on specific tags.\n\nTag fields can be added to the schema with the following syntax:\n\n[code example]\n\nwhere\n\n- `SEPARATOR` defaults to a comma (`,`), and can be any printable ASCII character. It is used to separate tags in the field value. For example, if the field value is `hello,world`, the tags are `hello` and `world`.\n\n- `CASESENSITIVE` indicates that the field is case-sensitive. By default, tag fields are case-insensitive.\n\nYou can search for documents with specific tags using the `@<field_name>:{<tag>}` query syntax. For example, this query finds documents with the tag `blue`:\n\n[code example]\n\nFor more information about tag fields, see [Tag Fields](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/tags)."},{"id":"text-fields","title":"Text fields","role":"content","text":"Text fields are specifically designed for storing human language text. When indexing text fields, Redis performs several transformations to optimize search capabilities. The text is transformed to lowercase, allowing case-insensitive searches. The data is tokenized, meaning it is split into individual words or tokens, which enables efficient full-text search functionality. Text fields can be weighted to assign different levels of importance to specific fields during search operations. Additionally, text fields can be sorted based on their values, enabling the sorting of search results by relevance or other criteria.\n\nText fields can be added to the schema with the following syntax:\n\n[code example]\n\nwhere\n\n- `WEIGHT` indicates that the field is weighted. This is useful for assigning different levels of importance to specific fields during search operations.\n- `NOSTEM` indicates that the field is not stemmed. This is useful for storing text that you don't want to be tokenized, such as URLs or email addresses.\n- `PHONETIC {matcher}` Declaring a text attribute as `PHONETIC` will perform phonetic matching on it in searches by default. The obligatory matcher argument specifies the phonetic algorithm and language used. The following matchers are supported:\n\n   - `dm:en` - double metaphone for English\n   - `dm:fr` - double metaphone for French\n   - `dm:pt` - double metaphone for Portuguese\n   - `dm:es` - double metaphone for Spanish\n\n    For more information, see [Phonetic Matching](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/phonetic_matching).\n- `SORTABLE` indicates that the field can be sorted. This is useful for performing range queries and sorting search results based on text values.\n- `NOINDEX` indicates that the field is not indexed. This is useful for storing text that you don't want to search for, but that you still want to retrieve in search results.\n- `WITHSUFFIXTRIE` indicates that the field will be indexed with a suffix trie. The index will keep a suffix trie with all terms which match the suffix. It is used to optimize `contains (*foo*)` and `suffix (*foo)` queries. Otherwise, a brute-force search on the trie is performed. If a suffix trie exists for some fields, these queries will be disabled for other fields.\n\nYou can search for documents with specific text values using the `<term>` or the `@<field_name>:{<term>}` query syntax. Here are a couple of examples:\n\n- Search for a term in every text attribute:\n    [code example]\n\n- Search for a term only in the `title` attribute\n    [code example]"},{"id":"unicode-considerations","title":"Unicode considerations","role":"content","text":"Redis Search only supports Unicode characters in the [basic multilingual plane](https://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane); U+0000 to U+FFFF. Unicode characters beyond U+FFFF, such as Emojis, are not supported and would not be retrieved by queries including such characters in the following use cases:\n\n* Querying TEXT fields with Prefix/Suffix/Infix\n* Querying TEXT fields with fuzzy\n\nExamples:\n\n[code example]"}],"examples":[{"id":"numeric-fields-ex0","language":"plaintext","code":"FT.CREATE ... SCHEMA ... {field_name} NUMERIC [SORTABLE] [NOINDEX]","section_id":"numeric-fields"},{"id":"numeric-fields-ex1","language":"plaintext","code":"FT.SEARCH products \"@price:[200 300]\"","section_id":"numeric-fields"},{"id":"geo-fields-ex0","language":"plaintext","code":"FT.CREATE ... SCHEMA ... {field_name} GEO [SORTABLE] [NOINDEX]","section_id":"geo-fields"},{"id":"geo-fields-ex1","language":"plaintext","code":"FT.SEARCH cities \"@coords:[2.34 48.86 1000 km]\"","section_id":"geo-fields"},{"id":"geoshape-fields-ex0","language":"plaintext","code":"FT.CREATE ... SCHEMA ... {field_name} GEOSHAPE [FLAT|SPHERICAL] [NOINDEX]","section_id":"geoshape-fields"},{"id":"geoshape-fields-ex1","language":"plaintext","code":"FT.SEARCH idx \"(@geom:[CONTAINS $qshape])\" PARAMS 2 qshape \"POINT (2 2)\" RETURN 1 name DIALECT 2","section_id":"geoshape-fields"},{"id":"vector-fields-ex0","language":"plaintext","code":"FT.CREATE ... SCHEMA ... {field_name} VECTOR {algorithm} {count} [{attribute_name} {attribute_value} ...]","section_id":"vector-fields"},{"id":"vector-fields-ex1","language":"plaintext","code":"FT.CREATE my_idx SCHEMA vec_field VECTOR FLAT 6 TYPE FLOAT32 DIM 128 DISTANCE_METRIC L2","section_id":"vector-fields"},{"id":"tag-fields-ex0","language":"plaintext","code":"FT.CREATE ... SCHEMA ... {field_name} TAG [SEPARATOR {sep}] [CASESENSITIVE]","section_id":"tag-fields"},{"id":"tag-fields-ex1","language":"plaintext","code":"FT.SEARCH idx \"@tags:{blue}\"","section_id":"tag-fields"},{"id":"text-fields-ex0","language":"plaintext","code":"FT.CREATE ... SCHEMA ... {field_name} TEXT [WEIGHT] [NOSTEM] [PHONETIC {matcher}] [SORTABLE] [NOINDEX] [WITHSUFFIXTRIE]","section_id":"text-fields"},{"id":"text-fields-ex1","language":"plaintext","code":"FT.SEARCH books-idx \"wizard\"","section_id":"text-fields"},{"id":"text-fields-ex2","language":"plaintext","code":"FT.SEARCH books-idx \"@title:dogs\"","section_id":"text-fields"},{"id":"unicode-considerations-ex0","language":"plaintext","code":"redis> FT.CREATE idx SCHEMA tag TAG text TEXT\nOK\nredis> HSET doc:1 tag '😀😁🙂' text '😀😁🙂'\n(integer) 2\nredis> HSET doc:2 tag '😀😁🙂abc' text '😀😁🙂abc'\n(integer) 2\nredis> FT.SEARCH idx '@text:(*😀😁🙂)' NOCONTENT\n1) (integer) 0\nredis> FT.SEARCH idx '@text:(*😀😁🙂*)' NOCONTENT\n1) (integer) 0\nredis> FT.SEARCH idx '@text:(😀😁🙂*)' NOCONTENT\n1) (integer) 0\n\nredis> FT.SEARCH idx '@text:(%😀😁🙃%)' NOCONTENT\n1) (integer) 0","section_id":"unicode-considerations"}]}
{"schema_version":2,"id":"develop/ai/search-and-query/indexing/geoindex","title":"Geospatial indexing","url":"https://redis.io/docs/latest/develop/ai/search-and-query/indexing/geoindex/","summary":"Options for indexing geospatial data","aliases":["/develop/interact/search-and-query/indexing/geoindex"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:33:04+01:00","page_type":"content","content_hash":"ea6cf3d914b3e077175020b8b6ada1b9d84c5a80232a34cbd568441e544b1f3f","sections":[{"id":"geo","title":"`GEO`","role":"content","text":"The following example creates a `GEO` index for JSON objects that store\ngeospatial data in a field called `location`, adds two product documents (any\nJSON object with the `product:` prefix and a `location` field is indexed\nautomatically), and then queries for products within a 100 mile radius of\nColorado Springs (Longitude=-104.800644, Latitude=38.846127). This returns only\nthe location in Denver, but a radius of 200 miles would also include the\nlocation in Fort Collins:\n\nGEO index: create a GEO index, add geospatial point documents, then run a radius query to find nearby locations\n\n**Difficulty:** Beginner\n\n**Commands:** FT.CREATE, JSON.SET, FT.SEARCH\n\n**Complexity:**\n- FT.CREATE: O(K)\n- JSON.SET: O(M+N)\n- FT.SEARCH: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nSee [Geospatial queries](https://redis.io/docs/latest/develop/ai/search-and-query/query/geo-spatial)\nfor more information about the available options."},{"id":"geoshape","title":"`GEOSHAPE`","role":"content","text":"The following example creates an index for JSON objects that include\ngeospatial data in a field called `geom`. The `FLAT` option at the end\nof the field definition specifies Cartesian coordinates instead of\nthe default spherical geographical coordinates (use `SPHERICAL` in\nplace of `FLAT` to choose the coordinate space explicitly). It then adds\nseveral shapes using the `shape:` prefix and runs a query that returns any\nshapes within the boundary of the green square but omits the green square\nitself:\n\nGEOSHAPE index: create a GEOSHAPE index, add polygon and point documents, then run a WITHIN query to test shape containment\n\n**Difficulty:** Intermediate\n\n**Commands:** FT.CREATE, JSON.SET, FT.SEARCH\n\n**Complexity:**\n- FT.CREATE: O(K)\n- JSON.SET: O(M+N)\n- FT.SEARCH: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nYou can also run queries to find whether shapes in the index completely contain\nor overlap each other. See\n[Geospatial queries](https://redis.io/docs/latest/develop/ai/search-and-query/query/geo-spatial)\nfor more information."}],"examples":[{"id":"geo-ex0","language":"plaintext","code":"> FT.CREATE productidx ON JSON PREFIX 1 product: SCHEMA $.location AS location GEO\nOK\n> JSON.SET product:46885 $ '{\"description\": \"Navy Blue Slippers\",\"price\": 45.99,\"city\": \"Denver\",\"location\": \"-104.991531, 39.742043\"}'\nOK\n> JSON.SET product:46886 $ '{\"description\": \"Bright Green Socks\",\"price\": 25.50,\"city\": \"Fort Collins\",\"location\": \"-105.0618814,40.5150098\"}'\nOK\n> FT.SEARCH productidx '@location:[-104.800644 38.846127 100 mi]'\n1) (integer) 1\n2) \"product:46885\"\n3) 1) \"$\"\n   2) \"{\\\"description\\\":\\\"Navy Blue Slippers\\\",\\\"price\\\":45.99,\\\"city\\\":\\\"Denver\\\",\\\"location\\\":\\\"-104.991531, 39.742043\\\"}\"","section_id":"geo"},{"id":"geo-ex1","language":"csharp","code":"Schema geoSchema = new Schema()\n            .AddGeoField(new FieldName(\"$.location\", \"location\"));\n\n        bool geoCreateResult = db.FT().Create(\n            \"productidx\",\n            new FTCreateParams()\n                .On(IndexDataType.JSON)\n                .Prefix(\"product:\"),\n            geoSchema\n        );\n        Console.WriteLine(geoCreateResult); // >>> True\n\n        var product46885 = new\n        {\n            description = \"Navy Blue Slippers\",\n            price = 45.99,\n            city = \"Denver\",\n            location = \"-104.991531, 39.742043\"\n        };\n\n        bool gjAddResult1 = db.JSON().Set(\"product:46885\", \"$\", product46885);\n        Console.WriteLine(gjAddResult1); // >>> True\n\n        var product46886 = new\n        {\n            description = \"Bright Green Socks\",\n            price = 25.50,\n            city = \"Fort Collins\",\n            location = \"-105.0618814,40.5150098\"\n        };\n\n        bool gjAddResult2 = db.JSON().Set(\"product:46886\", \"$\", product46886);\n        Console.WriteLine(gjAddResult2); // >>> True\n\n        SearchResult geoQueryResult = db.FT().Search(\n            \"productidx\",\n            new Query(\"@location:[-104.800644 38.846127 100 mi]\")\n        );\n        Console.WriteLine(geoQueryResult.Documents.Count); // >>> 1\n\n        Console.WriteLine(\n            string.Join(\", \", geoQueryResult.Documents.Select(x => x[\"json\"]))\n        );\n        // >>> {\"description\":\"Navy Blue Slippers\",\"price\":45.99,\"city\":\"Denver\"...","section_id":"geo"},{"id":"geo-ex2","language":"go","code":"geoCreateResult, err := rdb.FTCreate(ctx,\n\t\t\"productidx\",\n\t\t&redis.FTCreateOptions{\n\t\t\tOnJSON: true,\n\t\t\tPrefix: []interface{}{\"product:\"},\n\t\t},\n\t\t&redis.FieldSchema{\n\t\t\tFieldName: \"$.location\",\n\t\t\tAs:        \"location\",\n\t\t\tFieldType: redis.SearchFieldTypeGeo,\n\t\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(geoCreateResult) // >>> OK\n\n\tprd46885 := map[string]interface{}{\n\t\t\"description\": \"Navy Blue Slippers\",\n\t\t\"price\":       45.99,\n\t\t\"city\":        \"Denver\",\n\t\t\"location\":    \"-104.991531, 39.742043\",\n\t}\n\n\tgjResult1, err := rdb.JSONSet(ctx, \"product:46885\", \"$\", prd46885).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(gjResult1) // >>> OK\n\n\tprd46886 := map[string]interface{}{\n\t\t\"description\": \"Bright Green Socks\",\n\t\t\"price\":       25.50,\n\t\t\"city\":        \"Fort Collins\",\n\t\t\"location\":    \"-105.0618814,40.5150098\",\n\t}\n\n\tgjResult2, err := rdb.JSONSet(ctx, \"product:46886\", \"$\", prd46886).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(gjResult2) // >>> OK\n\n\tgeoQueryResult, err := rdb.FTSearch(ctx, \"productidx\",\n\t\t\"@location:[-104.800644 38.846127 100 mi]\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(geoQueryResult)\n\t// >>> {1 [{product:46885...","section_id":"geo"},{"id":"geo-ex3","language":"java","code":"SchemaField[] geoSchema = {\n            GeoField.of(\"$.location\").as(\"location\")\n        };\n\n        String geoIdxCreateResult = jedis.ftCreate(\"productidx\",\n            FTCreateParams.createParams()\n                    .on(IndexDataType.JSON)\n                    .addPrefix(\"product:\"),\n            geoSchema\n        );\n        \n        JSONObject prd46885 = new JSONObject()\n                .put(\"description\", \"Navy Blue Slippers\")\n                .put(\"price\", 45.99)\n                .put(\"city\", \"Denver\")\n                .put(\"location\", \"-104.991531, 39.742043\");\n        \n        String jsonAddResult1 = jedis.jsonSet(\"product:46885\", new Path2(\"$\"), prd46885);\n        System.out.println(jsonAddResult1); // >>> OK\n\n        JSONObject prd46886 = new JSONObject()\n                .put(\"description\", \"Bright Green Socks\")\n                .put(\"price\", 25.50)\n                .put(\"city\", \"Fort Collins\")\n                .put(\"location\", \"-105.0618814,40.5150098\");\n        \n        String jsonAddResult2 = jedis.jsonSet(\"product:46886\", new Path2(\"$\"), prd46886);\n        System.out.println(jsonAddResult2); // >>> OK\n\n        SearchResult geoResult = jedis.ftSearch(\"productidx\",\n            \"@location:[-104.800644 38.846127 100 mi]\"\n        );\n\n        System.out.println(geoResult.getTotalResults()); // >>> 1\n\n        for (Document doc: geoResult.getDocuments()) {\n            System.out.println(doc.getId());\n        }\n        // >>> product:46885","section_id":"geo"},{"id":"geo-ex4","language":"python","code":"geo_schema = (\n    GeoField(\"$.location\", as_name=\"location\")\n)\n\ngeo_index_create_result = r.ft(\"productidx\").create_index(\n    geo_schema,\n    definition=IndexDefinition(\n        prefix=[\"product:\"], index_type=IndexType.JSON\n    )\n)\nprint(geo_index_create_result)  # >>> True\n\nprd46885 = {\n    \"description\": \"Navy Blue Slippers\",\n    \"price\": 45.99,\n    \"city\": \"Denver\",\n    \"location\": \"-104.991531, 39.742043\"\n}\n\njson_add_result_1 = r.json().set(\"product:46885\", Path.root_path(), prd46885)\nprint(json_add_result_1)  # >>> True\n\nprd46886 = {\n    \"description\": \"Bright Green Socks\",\n    \"price\": 25.50,\n    \"city\": \"Fort Collins\",\n    \"location\": \"-105.0618814,40.5150098\"\n}\n\njson_add_result_2 = r.json().set(\"product:46886\", Path.root_path(), prd46886)\nprint(json_add_result_2)  # >>> True\n\ngeo_result = r.ft(\"productidx\").search(\n    \"@location:[-104.800644 38.846127 100 mi]\"\n)\nprint(geo_result)\n# >>> Result{1 total, docs: [Document {'id': 'product:46885'...","section_id":"geo"},{"id":"geoshape-ex0","language":"plaintext","code":"> FT.CREATE geomidx ON JSON PREFIX 1 shape: SCHEMA $.name AS name TEXT $.geom AS geom GEOSHAPE FLAT\nOK\n> JSON.SET shape:1 $ '{\"name\": \"Green Square\", \"geom\": \"POLYGON ((1 1, 1 3, 3 3, 3 1, 1 1))\"}'\nOK\n> JSON.SET shape:2 $ '{\"name\": \"Red Rectangle\", \"geom\": \"POLYGON ((2 2.5, 2 3.5, 3.5 3.5, 3.5 2.5, 2 2.5))\"}'\nOK\n> JSON.SET shape:3 $ '{\"name\": \"Blue Triangle\", \"geom\": \"POLYGON ((3.5 1, 3.75 2, 4 1, 3.5 1))\"}'\nOK\n> JSON.SET shape:4 $ '{\"name\": \"Purple Point\", \"geom\": \"POINT (2 2)\"}'\nOK\n> FT.SEARCH geomidx \"(-@name:(Green Square) @geom:[WITHIN $qshape])\" PARAMS 2 qshape \"POLYGON ((1 1, 1 3, 3 3, 3 1, 1 1))\" RETURN 1 name DIALECT 2\n1) (integer) 1\n2) \"shape:4\"\n3) 1) \"name\"\n   2) \"Purple Point\"","section_id":"geoshape"},{"id":"geoshape-ex1","language":"csharp","code":"Version version = muxer.GetServer(\"localhost:6379\").Version;\n        if (version.Major >= 7)\n        {\n            Schema geomSchema = new Schema()\n                .AddGeoShapeField(\n                    new FieldName(\"$.geom\", \"geom\"),\n                    Schema.GeoShapeField.CoordinateSystem.FLAT\n                )\n                .AddTextField(new FieldName(\"$.name\", \"name\"));\n\n            bool geomCreateResult = db.FT().Create(\n                \"geomidx\",\n                new FTCreateParams()\n                    .On(IndexDataType.JSON)\n                    .Prefix(\"shape:\"),\n                    geomSchema\n            );\n            Console.WriteLine(geomCreateResult); // >>> True\n        }\n\n\n        var shape1 = new\n        {\n            name = \"Green Square\",\n            geom = \"POLYGON ((1 1, 1 3, 3 3, 3 1, 1 1))\"\n        };\n\n        bool gmJsonRes1 = db.JSON().Set(\"shape:1\", \"$\", shape1);\n        Console.WriteLine(gmJsonRes1); // >>> True\n\n        var shape2 = new\n        {\n            name = \"Red Rectangle\",\n            geom = \"POLYGON ((2 2.5, 2 3.5, 3.5 3.5, 3.5 2.5, 2 2.5))\"\n        };\n\n        bool gmJsonRes2 = db.JSON().Set(\"shape:2\", \"$\", shape2);\n        Console.WriteLine(gmJsonRes2); // >>> True\n\n        var shape3 = new\n        {\n            name = \"Blue Triangle\",\n            geom = \"POLYGON ((3.5 1, 3.75 2, 4 1, 3.5 1))\"\n        };\n\n        bool gmJsonRes3 = db.JSON().Set(\"shape:3\", \"$\", shape3);\n        Console.WriteLine(gmJsonRes3); // >>> True\n\n        var shape4 = new\n        {\n            name = \"Purple Point\",\n            geom = \"POINT (2 2)\"\n        };\n\n        bool gmJsonRes4 = db.JSON().Set(\"shape:4\", \"$\", shape4);\n        Console.WriteLine(gmJsonRes4); // >>> True\n\n        if (version.Major >= 7)\n        {\n            SearchResult geomQueryResult = db.FT().Search(\n                \"geomidx\",\n                new Query(\"(-@name:(Green Square) @geom:[WITHIN $qshape])\")\n                    .AddParam(\"qshape\", \"POLYGON ((1 1, 1 3, 3 3, 3 1, 1 1))\")\n                    .Limit(0, 1)\n                    .Dialect(2)\n            );\n\n            Console.WriteLine(geomQueryResult.Documents.Count); // >>> 1\n            var res = string.Join(\", \", geomQueryResult.Documents.Select(x => x[\"json\"]));\n\n            Console.WriteLine(\n                string.Join(\", \", geomQueryResult.Documents.Select(x => x[\"json\"]))\n            );\n            // >>> {\"name\":\"Purple Point\",\"geom\":\"POINT (2 2)\"}\n        }","section_id":"geoshape"},{"id":"geoshape-ex2","language":"go","code":"geomCreateResult, err := rdb.FTCreate(ctx, \"geomidx\",\n\t\t&redis.FTCreateOptions{\n\t\t\tOnJSON: true,\n\t\t\tPrefix: []interface{}{\"shape:\"},\n\t\t},\n\t\t&redis.FieldSchema{\n\t\t\tFieldName: \"$.name\",\n\t\t\tAs:        \"name\",\n\t\t\tFieldType: redis.SearchFieldTypeText,\n\t\t},\n\t\t&redis.FieldSchema{\n\t\t\tFieldName:         \"$.geom\",\n\t\t\tAs:                \"geom\",\n\t\t\tFieldType:         redis.SearchFieldTypeGeoShape,\n\t\t\tGeoShapeFieldType: \"FLAT\",\n\t\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(geomCreateResult) // >>> OK\n\n\tshape1 := map[string]interface{}{\n\t\t\"name\": \"Green Square\",\n\t\t\"geom\": \"POLYGON ((1 1, 1 3, 3 3, 3 1, 1 1))\",\n\t}\n\n\tgmjResult1, err := rdb.JSONSet(ctx, \"shape:1\", \"$\", shape1).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(gmjResult1) // >>> OK\n\n\tshape2 := map[string]interface{}{\n\t\t\"name\": \"Red Rectangle\",\n\t\t\"geom\": \"POLYGON ((2 2.5, 2 3.5, 3.5 3.5, 3.5 2.5, 2 2.5))\",\n\t}\n\n\tgmjResult2, err := rdb.JSONSet(ctx, \"shape:2\", \"$\", shape2).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(gmjResult2) // >>> OK\n\n\tshape3 := map[string]interface{}{\n\t\t\"name\": \"Blue Triangle\",\n\t\t\"geom\": \"POLYGON ((3.5 1, 3.75 2, 4 1, 3.5 1))\",\n\t}\n\n\tgmjResult3, err := rdb.JSONSet(ctx, \"shape:3\", \"$\", shape3).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(gmjResult3) // >>> OK\n\n\tshape4 := map[string]interface{}{\n\t\t\"name\": \"Purple Point\",\n\t\t\"geom\": \"POINT (2 2)\",\n\t}\n\n\tgmjResult4, err := rdb.JSONSet(ctx, \"shape:4\", \"$\", shape4).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(gmjResult4) // >>> OK\n\n\tgeomQueryResult, err := rdb.FTSearchWithArgs(ctx, \"geomidx\",\n\t\t\"(-@name:(Green Square) @geom:[WITHIN $qshape])\",\n\t\t&redis.FTSearchOptions{\n\t\t\tParams: map[string]interface{}{\n\t\t\t\t\"qshape\": \"POLYGON ((1 1, 1 3, 3 3, 3 1, 1 1))\",\n\t\t\t},\n\t\t\tDialectVersion: 4,\n\t\t\tLimit:          1,\n\t\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(geomQueryResult)\n\t// >>> {1 [{shape:4...","section_id":"geoshape"},{"id":"geoshape-ex3","language":"java","code":"SchemaField[] geomSchema = {\n            TextField.of(\"$.name\").as(\"name\"),\n            GeoShapeField.of(\"$.geom\", CoordinateSystem.FLAT).as(\"geom\")\n        };\n\n        String geomIndexCreateResult = jedis.ftCreate(\"geomidx\",\n            FTCreateParams.createParams()\n                    .on(IndexDataType.JSON)\n                    .addPrefix(\"shape:\"),\n            geomSchema\n        );\n        System.out.println(geomIndexCreateResult); // >>> OK\n\n        JSONObject shape1 = new JSONObject()\n                .put(\"name\", \"Green Square\")\n                .put(\"geom\", \"POLYGON ((1 1, 1 3, 3 3, 3 1, 1 1))\");\n        \n        String gmJsonRes1 = jedis.jsonSet(\"shape:1\", new Path2(\"$\"), shape1);\n        System.out.println(gmJsonRes1); // >>> OK\n\n        JSONObject shape2 = new JSONObject()\n                .put(\"name\", \"Red Rectangle\")\n                .put(\"geom\", \"POLYGON ((2 2.5, 2 3.5, 3.5 3.5, 3.5 2.5, 2 2.5))\");\n        \n        String gmJsonRes2 = jedis.jsonSet(\"shape:2\", new Path2(\"$\"), shape2);\n        System.out.println(gmJsonRes2); // >>> OK\n\n        JSONObject shape3 = new JSONObject()\n                .put(\"name\", \"Blue Triangle\")\n                .put(\"geom\", \"POLYGON ((3.5 1, 3.75 2, 4 1, 3.5 1))\");\n        \n        String gmJsonRes3 = jedis.jsonSet(\"shape:3\", new Path2(\"$\"), shape3);\n        System.out.println(gmJsonRes3); // >>> OK\n\n        JSONObject shape4 = new JSONObject()\n                .put(\"name\", \"Purple Point\")\n                .put(\"geom\", \"POINT (2 2)\");\n        \n        String gmJsonRes4 = jedis.jsonSet(\"shape:4\", new Path2(\"$\"), shape4);\n        System.out.println(gmJsonRes4); // >>> OK\n\n        SearchResult geomResult = jedis.ftSearch(\"geomidx\",\n            \"(-@name:(Green Square) @geom:[WITHIN $qshape])\",\n            FTSearchParams.searchParams()\n                    .addParam(\"qshape\", \"POLYGON ((1 1, 1 3, 3 3, 3 1, 1 1))\")\n                    .dialect(4)\n                    .limit(0, 1)\n        );\n        System.out.println(geomResult.getTotalResults()); // >>> 1\n\n        for (Document doc: geomResult.getDocuments()) {\n            System.out.println(doc.getId());\n        }\n        // shape:4","section_id":"geoshape"},{"id":"geoshape-ex4","language":"python","code":"geom_schema = (\n    TextField(\"$.name\", as_name=\"name\"),\n    GeoShapeField(\n        \"$.geom\", as_name=\"geom\", coord_system=GeoShapeField.FLAT\n    )\n)\n\ngeom_index_create_result = r.ft(\"geomidx\").create_index(\n    geom_schema,\n    definition=IndexDefinition(\n        prefix=[\"shape:\"], index_type=IndexType.JSON\n    )\n)\nprint(geom_index_create_result)  # True\n\nshape1 = {\n    \"name\": \"Green Square\",\n    \"geom\": \"POLYGON ((1 1, 1 3, 3 3, 3 1, 1 1))\"\n}\n\ngm_json_res_1 = r.json().set(\"shape:1\", Path.root_path(), shape1)\nprint(gm_json_res_1)  # >>> True\n\nshape2 = {\n    \"name\": \"Red Rectangle\",\n    \"geom\": \"POLYGON ((2 2.5, 2 3.5, 3.5 3.5, 3.5 2.5, 2 2.5))\"\n}\n\ngm_json_res_2 = r.json().set(\"shape:2\", Path.root_path(), shape2)\nprint(gm_json_res_2)  # >>> True\n\nshape3 = {\n    \"name\": \"Blue Triangle\",\n    \"geom\": \"POLYGON ((3.5 1, 3.75 2, 4 1, 3.5 1))\"\n}\n\ngm_json_res_3 = r.json().set(\"shape:3\", Path.root_path(), shape3)\nprint(gm_json_res_3)  # >>> True\n\nshape4 = {\n    \"name\": \"Purple Point\",\n    \"geom\": \"POINT (2 2)\"\n}\n\ngm_json_res_4 = r.json().set(\"shape:4\", Path.root_path(), shape4)\nprint(gm_json_res_4)  # >>> True\n\ngeom_result = r.ft(\"geomidx\").search(\n    Query(\n        \"(-@name:(Green Square) @geom:[WITHIN $qshape])\"\n    ).dialect(4).paging(0, 1),\n    query_params={\n        \"qshape\": \"POLYGON ((1 1, 1 3, 3 3, 3 1, 1 1))\"\n    }\n)\nprint(geom_result)\n# >>> Result{1 total, docs: [Document {'id': 'shape:4'...","section_id":"geoshape"}]}
{"schema_version":2,"id":"develop/ai/search-and-query/indexing","title":"Indexing","url":"https://redis.io/docs/latest/develop/ai/search-and-query/indexing/","summary":"How to index and search JSON documents","aliases":["/develop/interact/search-and-query/indexing","/develop/interact/search-and-query/indexing/","/interact/search-and-query/indexing/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:33:04+01:00","children":[{"id":"develop/ai/search-and-query/indexing/schema-definition","summary":"How to define the schema of an index.","title":"Schema definition","url":"https://redis.io/docs/latest/develop/ai/search-and-query/indexing/schema-definition/"},{"id":"develop/ai/search-and-query/indexing/field-and-type-options","summary":"Available field types and options.","title":"Field and type options","url":"https://redis.io/docs/latest/develop/ai/search-and-query/indexing/field-and-type-options/"},{"id":"develop/ai/search-and-query/indexing/geoindex","summary":"Options for indexing geospatial data","title":"Geospatial indexing","url":"https://redis.io/docs/latest/develop/ai/search-and-query/indexing/geoindex/"},{"id":"develop/ai/search-and-query/indexing/interactive-demo","summary":"Change an index's field types, prefixes, and options, and see what each query finds, with every redis-cli command and reply.","title":"Indexing interactive demo","url":"https://redis.io/docs/latest/develop/ai/search-and-query/indexing/interactive-demo/"}],"page_type":"content","content_hash":"b79c9897579bcd1a640313dc0ee34e9f947cfdb3c17cbf55b80fa8c6b0829059","sections":[{"id":"overview","title":"Overview","role":"overview","text":"In addition to indexing Redis hashes, Redis Open Source can also index JSON documents."},{"id":"create-index-with-json-schema","title":"Create index with JSON schema","role":"content","text":"When you create an index with the [`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create) command, include the `ON JSON` keyword to index any existing and future JSON documents stored in the database.\n\nTo define the `SCHEMA`, you can provide [JSONPath](https://redis.io/docs/latest/develop/data-types/json/path) expressions.\nThe result of each JSONPath expression is indexed and associated with a logical name called an `attribute` (previously known as a `field`).\nYou can use these attributes in queries.\n\n> [!NOTE]\n> `attribute` is optional for [`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create).\n\nUse the following syntax to create a JSON index:\n\n[code example]\n\nFor example, this command creates an index that indexes the name, description, price, and image vector embedding of each JSON document that represents an inventory item:\n\n[code example]\n\nSee [Index limitations](#index-limitations) for more details about JSON index `SCHEMA` restrictions."},{"id":"add-json-documents","title":"Add JSON documents","role":"content","text":"After you create an index, Redis automatically indexes any existing, modified, or newly created JSON documents stored in the database. For existing documents, indexing runs asynchronously in the background, so it can take some time before the document is available. Modified and newly created documents are indexed synchronously, so the document will be available by the time the add or modify command finishes.\n\nYou can use any JSON write command, such as [`JSON.SET`](https://redis.io/docs/latest/commands/json.set) and [`JSON.ARRAPPEND`](https://redis.io/docs/latest/commands/json.arrappend), to create or modify JSON documents.\n\nThe following examples use these JSON documents to represent individual inventory items.\n\nItem 1 JSON document:\n\n[code example]\n\nItem 2 JSON document:\n\n[code example]\n\nUse [`JSON.SET`](https://redis.io/docs/latest/commands/json.set) to store these documents in the database:\n\n[code example]\n\nBecause indexing is synchronous in this case, the documents will be available on the index as soon as the [`JSON.SET`](https://redis.io/docs/latest/commands/json.set) command returns.\nAny subsequent queries that match the indexed content will return the document."},{"id":"search-the-index","title":"Search the index","role":"content","text":"To search the index for JSON documents, use the [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) command.\nYou can search any attribute defined in the `SCHEMA`.\n\nFor example, use this query to search for items with the word \"earbuds\" in the name:\n\n[code example]\n\nThis query searches for all items that include \"bluetooth\" and \"headphones\" in the description:\n\n[code example]\n\nNow search for Bluetooth headphones with a price less than 70:\n\n[code example]\n\nAnd lastly, search for the Bluetooth headphones that are most similar to an image whose embedding is [1.0, 1.0, 1.0, 1.0]:\n\n[code example]\n\nFor more information about search queries, see [Search query syntax](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/query_syntax).\n\n> [!NOTE]\n> [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) queries require `attribute` modifiers. Don't use JSONPath expressions in queries because the query parser doesn't fully support them."},{"id":"understanding-tag-field-behavior-hash-versus-json","title":"Understanding TAG field behavior: hash versus JSON","role":"content","text":"TAG fields behave differently depending on whether you're indexing hash or JSON documents. This difference is a common source of confusion."},{"id":"hash-documents","title":"Hash documents","role":"content","text":"[code example]"},{"id":"json-documents","title":"JSON documents","role":"content","text":"[code example]"},{"id":"making-json-documents-behave-like-hash-documents","title":"Making JSON documents behave like hash documents","role":"content","text":"To get hash-like behavior in JSON, explicitly add `SEPARATOR \",\"`:\n\n[code example]"},{"id":"recommended-approach-for-json","title":"Recommended approach for JSON","role":"content","text":"Instead of comma-separated strings, use JSON arrays:\n\n[code example]"},{"id":"index-json-arrays-as-tag","title":"Index JSON arrays as TAG","role":"content","text":"For JSON documents, you have two approaches to create TAG fields with multiple values:"},{"id":"approach-1-json-arrays-recommended","title":"Approach 1: JSON arrays (recommended)","role":"content","text":"The preferred method for indexing multiple tag values is using JSON arrays. Each array element becomes a separate tag value. Use the [JSONPath](https://redis.io/docs/latest/develop/data-types/json/path) wildcard operator `[*]` to index array elements.\n\n[code example]\n\nNow you can search for silver headphones:\n\n[code example]"},{"id":"approach-2-strings-with-explicit-separators","title":"Approach 2: strings with explicit separators","role":"content","text":"You can also use comma-separated strings, but you must explicitly specify the `SEPARATOR`:\n\n[code example]\n\n> [!NOTE] Important: JSON vs HASH behavior\n> - **JSON without SEPARATOR**: `\"black,silver\"` becomes one tag: `\"black,silver\"`.\n> - **JSON with SEPARATOR \",\"**: `\"black,silver\"` becomes two tags: `\"black\"` and `\"silver\"`.\n> - **Hash (default)**: `\"black,silver\"` becomes two tags: `\"black\"` and `\"silver\"`.\n>\n> For JSON, always specify `SEPARATOR \",\"` if you want to split comma-separated strings, or use arrays instead."},{"id":"which-approach-to-choose","title":"Which approach to choose?","role":"content","text":"Use JSON arrays when:\n\n- You control the data structure.\n- You want clean, structured data.\n- You need to store complex values (strings with spaces, punctuation).\n\nUse strings with separators when:\n\n- You're migrating from hashes to JSON.\n- You receive data as delimited strings.\n- You need compatibility with existing systems."},{"id":"index-json-arrays-as-text","title":"Index JSON arrays as TEXT","role":"content","text":"Starting with RediSearch v2.6.0, full text search can be done on an array of strings or on a JSONPath leading to multiple strings.\n\nIf you want to index multiple string values as TEXT, use either a JSONPath leading to a single array of strings, or a JSONPath leading to multiple string values, using JSONPath operators such as wildcard, filter, union, array slice, and/or recursive descent.\n\nTo index an item's list of available colors, specify the JSONPath `$.colors` in the `SCHEMA` definition during index creation:\n\n[code example]\n\n[code example]\n\nNow you can do full text search for light colored headphones:\n\n[code example]"},{"id":"limitations","title":"Limitations","role":"limits","text":"- When a JSONPath may lead to multiple values and not only to a single array, e.g., when a JSONPath contains wildcards, etc., specifying `SLOP` or `INORDER` in [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) will return an error, since the order of the values matching the JSONPath is not well defined, leading to potentially inconsistent results.\n\n   For example, using a JSONPath such as `$..b[*]` on a JSON value such as\n   [code example]\n   may match values in various orderings, depending on the specific implementation of the JSONPath library being used.\n\n   Since `SLOP` and `INORDER` consider relative ordering among the indexed values, and results may change in future releases, an error will be returned.\n\n- When JSONPath leads to multiple values:\n  - String values are indexed\n  - `null` values are skipped\n  - Any other value type will cause an indexing failure"},{"id":"handling-phrases-in-different-array-slots","title":"Handling phrases in different array slots:","role":"content","text":"When indexing, a predefined delta is used to increase positional offsets between array slots for multiple text values. This delta controls the level of separation between phrases in different array slots (related to the `SLOP` parameter of [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search)).\nThis predefined value is set by the configuration parameter `MULTI_TEXT_SLOP` (at module load-time). The default value is 100."},{"id":"index-json-arrays-as-numeric","title":"Index JSON arrays as NUMERIC","role":"content","text":"Starting with RediSearch v2.6.1, search can be done on an array of numerical values or on a JSONPath leading to multiple numerical values.\n\nIf you want to index multiple numerical values as NUMERIC, use either a JSONPath leading to a single array of numbers, or a JSONPath leading to multiple numbers, using JSONPath operators such as wildcard, filter, union, array slice, and/or recursive descent.\n\nFor example, add to the item's list the available `max_level` of volume (in decibels):\n\n[code example]\n\nTo index the `max_level` array, specify the JSONPath `$.max_level` in the `SCHEMA` definition during index creation:\n\n[code example]\n\nYou can now search for headphones with specific max volume levels, for example, between 70 and 80 (inclusive), returning items with at least one value in their `max_level` array, which is in the requested range:\n\n[code example]\n\nYou can also search for items with all values in a specific range. For example, all values are in the range [90, 120] (inclusive):\n\n[code example]"},{"id":"limitations-1","title":"Limitations","role":"limits","text":"When JSONPath leads to multiple numerical values:\n  - Numerical values are indexed\n  - `null` values are skipped\n  - Any other value type will cause an indexing failure"},{"id":"index-json-arrays-as-geo-and-geoshape","title":"Index JSON arrays as GEO and GEOSHAPE","role":"content","text":"You can use `GEO` and `GEOSHAPE` fields to store geospatial data,\nsuch as geographical locations and geometric shapes. See\n[Geospatial indexing](https://redis.io/docs/latest/develop/ai/search-and-query/indexing/geoindex)\nto learn how to use these schema types and see the\n[Geospatial](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/geo)\nreference page for an introduction to their format and usage."},{"id":"index-json-arrays-as-vector","title":"Index JSON arrays as VECTOR","role":"content","text":"Starting with RediSearch 2.6.0, you can index a JSONPath leading to an array of numeric values as a VECTOR type in the index schema.\n\nFor example, assume that your JSON items include an array of vector embeddings, where each vector represents an image of a product. To index these vectors, specify the JSONPath `$.embedding` in the schema definition during index creation:\n\n[code example]\n\nNow you can search for the two headphones that are most similar to the image embedding by using vector search KNN query. (Note that the vector queries are supported as of dialect 2.) For example:\n\n[code example]\n\nIf you want to index multiple numeric arrays as VECTOR, use a [JSONPath](https://redis.io/docs/latest/develop/data-types/json/path) leading to multiple numeric arrays using JSONPath operators such as wildcard, filter, union, array slice, and/or recursive descent.\n\nFor example, assume that your JSON items include an array of vector embeddings, where each vector represents a different image of the same product. To index these vectors, specify the JSONPath `$.embeddings[*]` in the schema definition during index creation:\n\n[code example]\n\n> [!NOTE] Important note\n> Unlike the case with the NUMERIC type, setting a static path such as `$.embedding` in the schema for the VECTOR type does not allow you to index multiple vectors stored under that field. Hence, if you set `$.embedding` as the path to the index schema, specifying an array of vectors in the `embedding` field in your JSON will cause an indexing failure.\n\nNow you can search for the two headphones that are most similar to an image embedding by using vector search KNN query. (Note that the vector queries are supported as of dialect 2.) The distance between a document to the query vector is defined as the minimum distance between the query vector to a vector that matches the JSONPath specified in the schema. For example:\n\n[code example]\nNote that `0.771500051022` is the L2 distance between the query vector and `[-0.8,-0.15,0.33,-0.01]`, which is the second element in the embedding array, and it is lower than the L2 distance between the query vector and `[-0.7,-0.51,0.88,0.14]`, which is the first element in the embedding array.\n\nFor more information on vector similarity syntax, see [Vector fields](https://redis.io/docs/latest/develop/ai/search-and-query/vectors)."},{"id":"index-json-objects","title":"Index JSON objects","role":"content","text":"You cannot index JSON objects. FT.CREATE will return an error if the JSONPath expression returns an object.\n\nTo index the contents of a JSON object, you need to index the individual elements within the object in separate attributes.\n\nFor example, to index the `connection` JSON object, define the `$.connection.wireless` and `$.connection.type` fields as separate attributes when you create the index:\n\n[code example]\n\nAfter you create the new index, you can search for items with the wireless TAG set to `true`:\n\n[code example]\n\nYou can also search for items with a Bluetooth connection type:\n\n[code example]"},{"id":"field-projection","title":"Field projection","role":"content","text":"[`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) returns the entire JSON document by default. If you want to limit the returned search results to specific attributes, you can use field projection."},{"id":"return-specific-attributes","title":"Return specific attributes","role":"returns","text":"When you run a search query, you can use the `RETURN` keyword to specify which attributes you want to include in the search results. You also need to specify the number of fields to return.\n\nFor example, this query only returns the `name` and `price` of each set of headphones:\n\n[code example]"},{"id":"project-with-jsonpath","title":"Project with JSONPath","role":"content","text":"You can use [JSONPath](https://redis.io/docs/latest/develop/data-types/json/path) expressions in a `RETURN` statement to extract any part of the JSON document, even fields that were not defined in the index `SCHEMA`.\n\nFor example, the following query uses the JSONPath expression `$.stock` to return each item's stock in addition to the name and price attributes.\n\n[code example]\n\nNote that the returned property name is the JSONPath expression itself: `\"$.stock\"`.\n\nYou can use the `AS` option to specify an alias for the returned property:\n\n[code example]\n\nThis query returns the field as the alias `\"stock\"` instead of the JSONPath expression `\"$.stock\"`."},{"id":"highlight-search-terms","title":"Highlight search terms","role":"content","text":"You can [highlight](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/highlight) relevant search terms in any indexed `TEXT` attribute.\n\nFor JSON documents, you must use the `RETURN` parameter to specify the attributes, followed by `HIGHLIGHT` to indicate which of those attributes to highlight.\n\nUse the optional `TAGS` keyword to specify the strings that will surround (or highlight) the matching search terms.\n\n> [!NOTE]\n> `HIGHLIGHT` and `SUMMARIZE` are not supported when the JSONPath leads to multiple values (such as arrays indexed as `TEXT`). See [Index limitations](#index-limitations) for details.\n\nFor example, highlight the word \"bluetooth\" with bold HTML tags in item names and descriptions:\n\n[code example]"},{"id":"aggregate-with-jsonpath","title":"Aggregate with JSONPath","role":"content","text":"You can use [aggregation](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/aggregations) to generate statistics or build facet queries.\n\nThe `LOAD` option accepts [JSONPath](https://redis.io/docs/latest/develop/data-types/json/path) expressions. You can use any value in the pipeline, even if the value is not indexed.\n\nThis example uses aggregation to calculate a 10% price discount for each item and sorts the items from least expensive to most expensive:\n\n[code example]\n\n> [!NOTE]\n> [`FT.AGGREGATE`](https://redis.io/docs/latest/commands/ft.aggregate) queries require `attribute` modifiers. Don't use JSONPath expressions in queries, except with the `LOAD` option, because the query parser doesn't fully support them."},{"id":"index-missing-or-empty-values","title":"Index missing or empty values","role":"content","text":"As of v2.10, you can search for missing properties, that is, properties that do not exist in a given document, using the `INDEXMISSING` option to `FT.CREATE` in conjunction with the `ismissing` query function with `FT.SEARCH`. You can also search for existing properties with no value (i.e., empty) using the `INDEXEMPTY` option with `FT.CREATE`. Both query types require DIALECT 2. Examples below:\n\n[code example]\n\n[code example]"},{"id":"index-limitations","title":"Index limitations","role":"content","text":""},{"id":"schema-mapping","title":"Schema mapping","role":"content","text":"During index creation, you need to map the JSON elements to `SCHEMA` fields as follows:\n\n- Strings as `TEXT`, `TAG`, or `GEO`.\n- Numbers as `NUMERIC`.\n- Booleans as `TAG`.\n- JSON array\n  - Array of strings as `TAG` or `TEXT`.\n  - Array of numbers as `NUMERIC` or `VECTOR`.\n  - Array of geo coordinates as `GEO`.\n  - `null` values in such arrays are ignored.\n- You cannot index JSON objects. Index the individual elements as separate attributes instead.\n- `null` values are ignored."},{"id":"jsonpath-with-multiple-values","title":"JSONPath with multiple values","role":"content","text":"When a JSONPath leads to an array or to multiple values:\n\n- No `HIGHLIGHT` and `SUMMARIZE` support.\n- `SORTBY` only sorts by the first value.\n- `RETURN` of a schema attribute returns the values as a JSON string.\n- If a JSONPath is specified by the `RETURN`, instead of a schema attribute, all values are returned (as a JSON string)."}],"examples":[{"id":"create-index-with-json-schema-ex0","language":"sql","code":"FT.CREATE {index_name} ON JSON SCHEMA {json_path} AS {attribute} {type}","section_id":"create-index-with-json-schema"},{"id":"create-index-with-json-schema-ex1","language":"sql","code":"127.0.0.1:6379> FT.CREATE itemIdx ON JSON PREFIX 1 item: SCHEMA $.name AS name TEXT $.description as description TEXT $.price AS price NUMERIC $.embedding AS embedding VECTOR FLAT 6 DIM 4 DISTANCE_METRIC L2 TYPE FLOAT32","section_id":"create-index-with-json-schema"},{"id":"add-json-documents-ex0","language":"json","code":"{\n  \"name\": \"Noise-cancelling Bluetooth headphones\",\n  \"description\": \"Wireless Bluetooth headphones with noise-cancelling technology\",\n  \"connection\": {\n    \"wireless\": true,\n    \"type\": \"Bluetooth\"\n  },\n  \"price\": 99.98,\n  \"stock\": 25,\n  \"colors\": [\n    \"black\",\n    \"silver\"\n  ],\n  \"embedding\": [0.87, -0.15, 0.55, 0.03]\n}","section_id":"add-json-documents"},{"id":"add-json-documents-ex1","language":"json","code":"{\n  \"name\": \"Wireless earbuds\",\n  \"description\": \"Wireless Bluetooth in-ear headphones\",\n  \"connection\": {\n    \"wireless\": true,\n    \"type\": \"Bluetooth\"\n  },\n  \"price\": 64.99,\n  \"stock\": 17,\n  \"colors\": [\n    \"black\",\n    \"white\"\n  ],\n  \"embedding\": [-0.7, -0.51, 0.88, 0.14]\n}","section_id":"add-json-documents"},{"id":"add-json-documents-ex2","language":"sql","code":"127.0.0.1:6379> JSON.SET item:1 $ '{\"name\":\"Noise-cancelling Bluetooth headphones\",\"description\":\"Wireless Bluetooth headphones with noise-cancelling technology\",\"connection\":{\"wireless\":true,\"type\":\"Bluetooth\"},\"price\":99.98,\"stock\":25,\"colors\":[\"black\",\"silver\"],\"embedding\":[0.87,-0.15,0.55,0.03]}'\n\"OK\"\n127.0.0.1:6379> JSON.SET item:2 $ '{\"name\":\"Wireless earbuds\",\"description\":\"Wireless Bluetooth in-ear headphones\",\"connection\":{\"wireless\":true,\"type\":\"Bluetooth\"},\"price\":64.99,\"stock\":17,\"colors\":[\"black\",\"white\"],\"embedding\":[-0.7,-0.51,0.88,0.14]}'\n\"OK\"","section_id":"add-json-documents"},{"id":"search-the-index-ex0","language":"sql","code":"127.0.0.1:6379> FT.SEARCH itemIdx '@name:(earbuds)'\n1) \"1\"\n2) \"item:2\"\n3) 1) \"$\"\n   2) \"{\\\"name\\\":\\\"Wireless earbuds\\\",\\\"description\\\":\\\"Wireless Bluetooth in-ear headphones\\\",\\\"connection\\\":{\\\"wireless\\\":true,\\\"connection\\\":\\\"Bluetooth\\\"},\\\"price\\\":64.99,\\\"stock\\\":17,\\\"colors\\\":[\\\"black\\\",\\\"white\\\"],\\\"embedding\\\":[-0.7,-0.51,0.88,0.14]}\"","section_id":"search-the-index"},{"id":"search-the-index-ex1","language":"sql","code":"127.0.0.1:6379> FT.SEARCH itemIdx '@description:(bluetooth headphones)'\n1) \"2\"\n2) \"item:1\"\n3) 1) \"$\"\n   2) \"{\\\"name\\\":\\\"Noise-cancelling Bluetooth headphones\\\",\\\"description\\\":\\\"Wireless Bluetooth headphones with noise-cancelling technology\\\",\\\"connection\\\":{\\\"wireless\\\":true,\\\"type\\\":\\\"Bluetooth\\\"},\\\"price\\\":99.98,\\\"stock\\\":25,\\\"colors\\\":[\\\"black\\\",\\\"silver\\\"], \\\"embedding\\\":[0.87,-0.15,0.55,0.03]}\"\n4) \"item:2\"\n5) 1) \"$\"\n   2) \"{\\\"name\\\":\\\"Wireless earbuds\\\",\\\"description\\\":\\\"Wireless Bluetooth in-ear headphones\\\",\\\"connection\\\":{\\\"wireless\\\":true,\\\"connection\\\":\\\"Bluetooth\\\"},\\\"price\\\":64.99,\\\"stock\\\":17,\\\"colors\\\":[\\\"black\\\",\\\"white\\\"],\\\"embedding\\\":[-0.7,-0.51,0.88,0.14]}\"","section_id":"search-the-index"},{"id":"search-the-index-ex2","language":"sql","code":"127.0.0.1:6379> FT.SEARCH itemIdx '@description:(bluetooth headphones) @price:[0 70]'\n1) \"1\"\n2) \"item:2\"\n3) 1) \"$\"\n   2) \"{\\\"name\\\":\\\"Wireless earbuds\\\",\\\"description\\\":\\\"Wireless Bluetooth in-ear headphones\\\",\\\"connection\\\":{\\\"wireless\\\":true,\\\"connection\\\":\\\"Bluetooth\\\"},\\\"price\\\":64.99,\\\"stock\\\":17,\\\"colors\\\":[\\\"black\\\",\\\"white\\\"],\\\"embedding\\\":[-0.7,-0.51,0.88,0.14]}\"","section_id":"search-the-index"},{"id":"search-the-index-ex3","language":"sql","code":"127.0.0.1:6379> FT.SEARCH itemIdx '@description:(bluetooth headphones)=>[KNN 2 @embedding $blob]' PARAMS 2 blob \\x01\\x01\\x01\\x01 DIALECT 2\n1) \"2\"\n2) \"item:1\"\n3) 1) \"__embedding_score\"\n   2) \"1.08280003071\"\n   1) \"$\"\n   2) \"{\\\"name\\\":\\\"Noise-cancelling Bluetooth headphones\\\",\\\"description\\\":\\\"Wireless Bluetooth headphones with noise-cancelling technology\\\",\\\"connection\\\":{\\\"wireless\\\":true,\\\"type\\\":\\\"Bluetooth\\\"},\\\"price\\\":99.98,\\\"stock\\\":25,\\\"colors\\\":[\\\"black\\\",\\\"silver\\\"],\\\"embedding\\\":[0.87,-0.15,0.55,0.03]}\"\n2) \"item:2\"\n3) 1) \"__embedding_score\"\n   2) \"1.54409992695\"\n   3) \"$\"\n   4) \"{\\\"name\\\":\\\"Wireless earbuds\\\",\\\"description\\\":\\\"Wireless Bluetooth in-ear headphones\\\",\\\"connection\\\":{\\\"wireless\\\":true,\\\"connection\\\":\\\"Bluetooth\\\"},\\\"price\\\":64.99,\\\"stock\\\":17,\\\"colors\\\":[\\\"black\\\",\\\"white\\\"],\\\"embedding\\\":[-0.7,-0.51,0.88,0.14]}\"","section_id":"search-the-index"},{"id":"hash-documents-ex0","language":"sql","code":"# HASH: Comma is the default separator\nHSET product:1 category \"Electronics,Gaming,PC\"\nFT.CREATE products ON HASH PREFIX 1 product: SCHEMA category TAG\n\n# Result: Creates 3 separate tags: \"Electronics\", \"Gaming\", \"PC\"\nFT.SEARCH products '@category:{Gaming}'  # ✅ Finds the document","section_id":"hash-documents"},{"id":"json-documents-ex0","language":"sql","code":"# JSON: No default separator - the entire string becomes one tag\nJSON.SET product:1 $ '{\"category\": \"Electronics,Gaming,PC\"}'\nFT.CREATE products ON JSON PREFIX 1 product: SCHEMA $.category AS category TAG\n\n# Result: Creates 1 tag: \"Electronics,Gaming,PC\"\nFT.SEARCH products '@category:{Gaming}'           # ❌ Does NOT find the document\nFT.SEARCH products '@category:{Electronics,Gaming,PC}'  # ✅ Finds the document","section_id":"json-documents"},{"id":"making-json-documents-behave-like-hash-documents-ex0","language":"sql","code":"JSON.SET product:1 $ '{\"category\": \"Electronics,Gaming,PC\"}'\nFT.CREATE products ON JSON PREFIX 1 product: SCHEMA $.category AS category TAG SEPARATOR \",\"\n\n# Result: Creates 3 separate tags: \"Electronics\", \"Gaming\", \"PC\"\nFT.SEARCH products '@category:{Gaming}'  # ✅ Now finds the document","section_id":"making-json-documents-behave-like-hash-documents"},{"id":"recommended-approach-for-json-ex0","language":"sql","code":"JSON.SET product:1 $ '{\"category\": [\"Electronics\", \"Gaming\", \"PC\"]}'\nFT.CREATE products ON JSON PREFIX 1 product: SCHEMA $.category[*] AS category TAG\n\n# Result: Creates 3 separate tags: \"Electronics\", \"Gaming\", \"PC\"\nFT.SEARCH products '@category:{Gaming}'  # ✅ Finds the document","section_id":"recommended-approach-for-json"},{"id":"approach-1-json-arrays-recommended-ex0","language":"sql","code":"# Create index with array indexing\n127.0.0.1:6379> FT.CREATE itemIdx2 ON JSON PREFIX 1 item: SCHEMA $.colors[*] AS colors TAG $.name AS name TEXT $.description as description TEXT\n\n# The JSON data uses arrays\n# Each array element (\"black\", \"silver\") becomes a separate tag","section_id":"approach-1-json-arrays-recommended"},{"id":"approach-1-json-arrays-recommended-ex1","language":"sql","code":"127.0.0.1:6379> FT.SEARCH itemIdx2 \"@colors:{silver} (@name:(headphones)|@description:(headphones))\"\n1) \"1\"\n2) \"item:1\"\n3) 1) \"$\"\n   2) \"{\\\"name\\\":\\\"Noise-cancelling Bluetooth headphones\\\",\\\"description\\\":\\\"Wireless Bluetooth headphones with noise-cancelling technology\\\",\\\"connection\\\":{\\\"wireless\\\":true,\\\"type\\\":\\\"Bluetooth\\\"},\\\"price\\\":99.98,\\\"stock\\\":25,\\\"colors\\\":[\\\"black\\\",\\\"silver\\\"]}\"","section_id":"approach-1-json-arrays-recommended"},{"id":"approach-2-strings-with-explicit-separators-ex0","language":"sql","code":"# JSON with comma-separated string\nJSON.SET item:1 $ '{\"colors\": \"black,silver,gold\"}'\n\n# Index with explicit separator\nFT.CREATE itemIdx3 ON JSON PREFIX 1 item: SCHEMA $.colors AS colors TAG SEPARATOR \",\"\n\n# Now you can search individual colors\nFT.SEARCH itemIdx3 \"@colors:{silver}\"","section_id":"approach-2-strings-with-explicit-separators"},{"id":"index-json-arrays-as-text-ex0","language":"sql","code":"127.0.0.1:6379> FT.CREATE itemIdx3 ON JSON PREFIX 1 item: SCHEMA $.colors AS colors TEXT $.name AS name TEXT $.description as description TEXT","section_id":"index-json-arrays-as-text"},{"id":"index-json-arrays-as-text-ex1","language":"sql","code":"127.0.0.1:6379> JSON.SET item:3 $ '{\"name\":\"True Wireless earbuds\",\"description\":\"True Wireless Bluetooth in-ear headphones\",\"connection\":{\"wireless\":true,\"type\":\"Bluetooth\"},\"price\":74.99,\"stock\":20,\"colors\":[\"red\",\"light blue\"]}'\n\"OK\"","section_id":"index-json-arrays-as-text"},{"id":"index-json-arrays-as-text-ex2","language":"sql","code":"127.0.0.1:6379> FT.SEARCH itemIdx3 '@colors:(white|light) (@name|description:(headphones))' RETURN 1 $.colors\n1) (integer) 2\n2) \"item:2\"\n3) 1) \"$.colors\"\n   2) \"[\\\"black\\\",\\\"white\\\"]\"\n4) \"item:3\"\n5) 1) \"$.colors\"\n   2) \"[\\\"red\\\",\\\"light blue\\\"]\"","section_id":"index-json-arrays-as-text"},{"id":"limitations-ex0","language":"json","code":"{\n      \"a\": [\n         {\"b\": [\"first first\", \"first second\"]},\n         {\"c\":\n            {\"b\": [\"second first\", \"second second\"]}},\n         {\"b\": [\"third first\", \"third second\"]}\n      ]\n   }","section_id":"limitations"},{"id":"index-json-arrays-as-numeric-ex0","language":"sql","code":"127.0.0.1:6379> JSON.SET item:1 $ '{\"name\":\"Noise-cancelling Bluetooth headphones\",\"description\":\"Wireless Bluetooth headphones with noise-cancelling technology\",\"connection\":{\"wireless\":true,\"type\":\"Bluetooth\"},\"price\":99.98,\"stock\":25,\"colors\":[\"black\",\"silver\"], \"max_level\":[60, 70, 80, 90, 100]}'\nOK\n\n127.0.0.1:6379> JSON.SET item:2 $ '{\"name\":\"Wireless earbuds\",\"description\":\"Wireless Bluetooth in-ear headphones\",\"connection\":{\"wireless\":true,\"type\":\"Bluetooth\"},\"price\":64.99,\"stock\":17,\"colors\":[\"black\",\"white\"], \"max_level\":[80, 100, 120]}'\nOK\n\n127.0.0.1:6379> JSON.SET item:3 $ '{\"name\":\"True Wireless earbuds\",\"description\":\"True Wireless Bluetooth in-ear headphones\",\"connection\":{\"wireless\":true,\"type\":\"Bluetooth\"},\"price\":74.99,\"stock\":20,\"colors\":[\"red\",\"light blue\"], \"max_level\":[90, 100, 110, 120]}'\nOK","section_id":"index-json-arrays-as-numeric"},{"id":"index-json-arrays-as-numeric-ex1","language":"sql","code":"127.0.0.1:6379> FT.CREATE itemIdx4 ON JSON PREFIX 1 item: SCHEMA $.max_level AS dB NUMERIC\nOK","section_id":"index-json-arrays-as-numeric"},{"id":"index-json-arrays-as-numeric-ex2","language":"sql","code":"127.0.0.1:6379> FT.SEARCH itemIdx4 '@dB:[70 80]'\n1) (integer) 2\n2) \"item:1\"\n3) 1) \"$\"\n   2) \"{\\\"name\\\":\\\"Noise-cancelling Bluetooth headphones\\\",\\\"description\\\":\\\"Wireless Bluetooth headphones with noise-cancelling technology\\\",\\\"connection\\\":{\\\"wireless\\\":true,\\\"type\\\":\\\"Bluetooth\\\"},\\\"price\\\":99.98,\\\"stock\\\":25,\\\"colors\\\":[\\\"black\\\",\\\"silver\\\"],\\\"max_level\\\":[60,70,80,90,100]}\"\n4) \"item:2\"\n5) 1) \"$\"\n   2) \"{\\\"name\\\":\\\"Wireless earbuds\\\",\\\"description\\\":\\\"Wireless Bluetooth in-ear headphones\\\",\\\"connection\\\":{\\\"wireless\\\":true,\\\"type\\\":\\\"Bluetooth\\\"},\\\"price\\\":64.99,\\\"stock\\\":17,\\\"colors\\\":[\\\"black\\\",\\\"white\\\"],\\\"max_level\\\":[80,100,120]}\"","section_id":"index-json-arrays-as-numeric"},{"id":"index-json-arrays-as-numeric-ex3","language":"sql","code":"127.0.0.1:6379> FT.SEARCH itemIdx4 '-@dB:[-inf (90] -@dB:[(120 +inf]'\n1) (integer) 1\n2) \"item:3\"\n3) 1) \"$\"\n   2) \"{\\\"name\\\":\\\"True Wireless earbuds\\\",\\\"description\\\":\\\"True Wireless Bluetooth in-ear headphones\\\",\\\"connection\\\":{\\\"wireless\\\":true,\\\"type\\\":\\\"Bluetooth\\\"},\\\"price\\\":74.99,\\\"stock\\\":20,\\\"colors\\\":[\\\"red\\\",\\\"light blue\\\"],\\\"max_level\\\":[90,100,110,120]}\"","section_id":"index-json-arrays-as-numeric"},{"id":"index-json-arrays-as-vector-ex0","language":"sql","code":"127.0.0.1:6379> FT.CREATE itemIdx5 ON JSON PREFIX 1 item: SCHEMA $.embedding AS embedding VECTOR FLAT 6 DIM 4 DISTANCE_METRIC L2 TYPE FLOAT32\nOK\n127.0.0.1:6379> JSON.SET item:1 $ '{\"name\":\"Noise-cancelling Bluetooth headphones\",\"description\":\"Wireless Bluetooth headphones with noise-cancelling technology\",\"price\":99.98,\"stock\":25,\"colors\":[\"black\",\"silver\"],\"embedding\":[0.87,-0.15,0.55,0.03]}'\nOK\n127.0.0.1:6379> JSON.SET item:2 $ '{\"name\":\"Wireless earbuds\",\"description\":\"Wireless Bluetooth in-ear headphones\",\"price\":64.99,\"stock\":17,\"colors\":[\"black\",\"white\"],\"embedding\":[-0.7,-0.51,0.88,0.14]}'\nOK","section_id":"index-json-arrays-as-vector"},{"id":"index-json-arrays-as-vector-ex1","language":"sql","code":"127.0.0.1:6379> FT.SEARCH itemIdx5 '*=>[KNN 2 @embedding $blob AS dist]' SORTBY dist PARAMS 2 blob \\x01\\x01\\x01\\x01 DIALECT 2\n1) (integer) 2\n2) \"item:1\"\n3) 1) \"dist\"\n   2) \"1.08280003071\"\n   3) \"$\"\n   4) \"{\\\"name\\\":\\\"Noise-cancelling Bluetooth headphones\\\",\\\"description\\\":\\\"Wireless Bluetooth headphones with noise-cancelling technology\\\",\\\"price\\\":99.98,\\\"stock\\\":25,\\\"colors\\\":[\\\"black\\\",\\\"silver\\\"],\\\"embedding\\\":[0.87,-0.15,0.55,0.03]}\"\n4) \"item:2\"\n5) 1) \"dist\"\n   2) \"1.54409992695\"\n   3) \"$\"\n   4) \"{\\\"name\\\":\\\"Wireless earbuds\\\",\\\"description\\\":\\\"Wireless Bluetooth in-ear headphones\\\",\\\"price\\\":64.99,\\\"stock\\\":17,\\\"colors\\\":[\\\"black\\\",\\\"white\\\"],\\\"embedding\\\":[-0.7,-0.51,0.88,0.14]}\"","section_id":"index-json-arrays-as-vector"},{"id":"index-json-arrays-as-vector-ex2","language":"sql","code":"127.0.0.1:6379> FT.CREATE itemIdx5 ON JSON PREFIX 1 item: SCHEMA $.embeddings[*] AS embeddings VECTOR FLAT 6 DIM 4 DISTANCE_METRIC L2 TYPE FLOAT32\nOK\n127.0.0.1:6379> JSON.SET item:1 $ '{\"name\":\"Noise-cancelling Bluetooth headphones\",\"description\":\"Wireless Bluetooth headphones with noise-cancelling technology\",\"price\":99.98,\"stock\":25,\"colors\":[\"black\",\"silver\"],\"embeddings\":[[0.87,-0.15,0.55,0.03]]}'\nOK\n127.0.0.1:6379> JSON.SET item:2 $ '{\"name\":\"Wireless earbuds\",\"description\":\"Wireless Bluetooth in-ear headphones\",\"price\":64.99,\"stock\":17,\"colors\":[\"black\",\"white\"],\"embeddings\":[[-0.7,-0.51,0.88,0.14],[-0.8,-0.15,0.33,-0.01]]}'\nOK","section_id":"index-json-arrays-as-vector"},{"id":"index-json-arrays-as-vector-ex3","language":"sql","code":"127.0.0.1:6379> FT.SEARCH itemIdx5 '*=>[KNN 2 @embeddings $blob AS dist]' SORTBY dist PARAMS 2 blob \\x01\\x01\\x01\\x01 DIALECT 2\n1) (integer) 2\n2) \"item:2\"\n3) 1) \"dist\"\n   2) \"0.771500051022\"\n   3) \"$\"\n   4) \"{\\\"name\\\":\\\"Wireless earbuds\\\",\\\"description\\\":\\\"Wireless Bluetooth in-ear headphones\\\",\\\"price\\\":64.99,\\\"stock\\\":17,\\\"colors\\\":[\\\"black\\\",\\\"white\\\"],\\\"embeddings\\\":[[-0.7,-0.51,0.88,0.14],[-0.8,-0.15,0.33,-0.01]]}\"\n4) \"item:1\"\n5) 1) \"dist\"\n   2) \"1.08280003071\"\n   3) \"$\"\n   4) \"{\\\"name\\\":\\\"Noise-cancelling Bluetooth headphones\\\",\\\"description\\\":\\\"Wireless Bluetooth headphones with noise-cancelling technology\\\",\\\"price\\\":99.98,\\\"stock\\\":25,\\\"colors\\\":[\\\"black\\\",\\\"silver\\\"],\\\"embeddings\\\":[[0.87,-0.15,0.55,0.03]]}\"","section_id":"index-json-arrays-as-vector"},{"id":"index-json-objects-ex0","language":"sql","code":"127.0.0.1:6379> FT.CREATE itemIdx3 ON JSON SCHEMA $.connection.wireless AS wireless TAG $.connection.type AS connectionType TEXT\n\"OK\"","section_id":"index-json-objects"},{"id":"index-json-objects-ex1","language":"sql","code":"127.0.0.1:6379> FT.SEARCH itemIdx3 '@wireless:{true}'\n1) \"2\"\n2) \"item:2\"\n3) 1) \"$\"\n   2) \"{\\\"name\\\":\\\"Wireless earbuds\\\",\\\"description\\\":\\\"Wireless Bluetooth in-ear headphones\\\",\\\"connection\\\":{\\\"wireless\\\":true,\\\"connection\\\":\\\"Bluetooth\\\"},\\\"price\\\":64.99,\\\"stock\\\":17,\\\"colors\\\":[\\\"black\\\",\\\"white\\\"]}\"\n4) \"item:1\"\n5) 1) \"$\"\n   2) \"{\\\"name\\\":\\\"Noise-cancelling Bluetooth headphones\\\",\\\"description\\\":\\\"Wireless Bluetooth headphones with noise-cancelling technology\\\",\\\"connection\\\":{\\\"wireless\\\":true,\\\"type\\\":\\\"Bluetooth\\\"},\\\"price\\\":99.98,\\\"stock\\\":25,\\\"colors\\\":[\\\"black\\\",\\\"silver\\\"]}\"","section_id":"index-json-objects"},{"id":"index-json-objects-ex2","language":"sql","code":"127.0.0.1:6379> FT.SEARCH itemIdx3 '@connectionType:(bluetooth)'\n1) \"2\"\n2) \"item:1\"\n3) 1) \"$\"\n   2) \"{\\\"name\\\":\\\"Noise-cancelling Bluetooth headphones\\\",\\\"description\\\":\\\"Wireless Bluetooth headphones with noise-cancelling technology\\\",\\\"connection\\\":{\\\"wireless\\\":true,\\\"type\\\":\\\"Bluetooth\\\"},\\\"price\\\":99.98,\\\"stock\\\":25,\\\"colors\\\":[\\\"black\\\",\\\"silver\\\"]}\"\n4) \"item:2\"\n5) 1) \"$\"\n   2) \"{\\\"name\\\":\\\"Wireless earbuds\\\",\\\"description\\\":\\\"Wireless Bluetooth in-ear headphones\\\",\\\"connection\\\":{\\\"wireless\\\":true,\\\"type\\\":\\\"Bluetooth\\\"},\\\"price\\\":64.99,\\\"stock\\\":17,\\\"colors\\\":[\\\"black\\\",\\\"white\\\"]}\"","section_id":"index-json-objects"},{"id":"return-specific-attributes-ex0","language":"sql","code":"127.0.0.1:6379> FT.SEARCH itemIdx '@description:(headphones)' RETURN 2 name price\n1) \"2\"\n2) \"item:1\"\n3) 1) \"name\"\n   2) \"Noise-cancelling Bluetooth headphones\"\n   3) \"price\"\n   4) \"99.98\"\n4) \"item:2\"\n5) 1) \"name\"\n   2) \"Wireless earbuds\"\n   3) \"price\"\n   4) \"64.99\"","section_id":"return-specific-attributes"},{"id":"project-with-jsonpath-ex0","language":"sql","code":"127.0.0.1:6379> FT.SEARCH itemIdx '@description:(headphones)' RETURN 3 name price $.stock\n1) \"2\"\n2) \"item:1\"\n3) 1) \"name\"\n   2) \"Noise-cancelling Bluetooth headphones\"\n   3) \"price\"\n   4) \"99.98\"\n   5) \"$.stock\"\n   6) \"25\"\n4) \"item:2\"\n5) 1) \"name\"\n   2) \"Wireless earbuds\"\n   3) \"price\"\n   4) \"64.99\"\n   5) \"$.stock\"\n   6) \"17\"","section_id":"project-with-jsonpath"},{"id":"project-with-jsonpath-ex1","language":"sql","code":"127.0.0.1:6379> FT.SEARCH itemIdx '@description:(headphones)' RETURN 5 name price $.stock AS stock\n1) \"2\"\n2) \"item:1\"\n3) 1) \"name\"\n   2) \"Noise-cancelling Bluetooth headphones\"\n   3) \"price\"\n   4) \"99.98\"\n   5) \"stock\"\n   6) \"25\"\n4) \"item:2\"\n5) 1) \"name\"\n   2) \"Wireless earbuds\"\n   3) \"price\"\n   4) \"64.99\"\n   5) \"stock\"\n   6) \"17\"","section_id":"project-with-jsonpath"},{"id":"highlight-search-terms-ex0","language":"sql","code":"127.0.0.1:6379> FT.SEARCH itemIdx '(@name:(bluetooth))|(@description:(bluetooth))' RETURN 3 name description price HIGHLIGHT FIELDS 2 name description TAGS '<b>' '</b>'\n1) \"2\"\n2) \"item:1\"\n3) 1) \"name\"\n   2) \"Noise-cancelling <b>Bluetooth</b> headphones\"\n   3) \"description\"\n   4) \"Wireless <b>Bluetooth</b> headphones with noise-cancelling technology\"\n   5) \"price\"\n   6) \"99.98\"\n4) \"item:2\"\n5) 1) \"name\"\n   2) \"Wireless earbuds\"\n   3) \"description\"\n   4) \"Wireless <b>Bluetooth</b> in-ear headphones\"\n   5) \"price\"\n   6) \"64.99\"","section_id":"highlight-search-terms"},{"id":"aggregate-with-jsonpath-ex0","language":"sql","code":"127.0.0.1:6379> FT.AGGREGATE itemIdx '*' LOAD 4 name $.price AS originalPrice APPLY '@originalPrice - (@originalPrice * 0.10)' AS salePrice SORTBY 2 @salePrice ASC\n1) \"2\"\n2) 1) \"name\"\n   2) \"Wireless earbuds\"\n   3) \"originalPrice\"\n   4) \"64.99\"\n   5) \"salePrice\"\n   6) \"58.491\"\n3) 1) \"name\"\n   2) \"Noise-cancelling Bluetooth headphones\"\n   3) \"originalPrice\"\n   4) \"99.98\"\n   5) \"salePrice\"\n   6) \"89.982\"","section_id":"aggregate-with-jsonpath"},{"id":"index-missing-or-empty-values-ex0","language":"plaintext","code":"JSON.SET key:1 $ '{\"propA\": \"foo\"}'\nJSON.SET key:2 $ '{\"propA\": \"bar\", \"propB\":\"abc\"}'\nFT.CREATE idx ON JSON PREFIX 1 key: SCHEMA $.propA AS propA TAG $.propB AS propB TAG INDEXMISSING\n\n> FT.SEARCH idx 'ismissing(@propB)' DIALECT 2\n1) \"1\"\n2) \"key:1\"\n3) 1) \"$\"\n   2) \"{\\\"propA\\\":\\\"foo\\\"}\"","section_id":"index-missing-or-empty-values"},{"id":"index-missing-or-empty-values-ex1","language":"plaintext","code":"JSON.SET key:1 $ '{\"propA\": \"foo\", \"propB\":\"\"}'\nJSON.SET key:2 $ '{\"propA\": \"bar\", \"propB\":\"abc\"}'\nFT.CREATE idx ON JSON PREFIX 1 key: SCHEMA $.propA AS propA TAG $.propB AS propB TAG INDEXEMPTY\n\n> FT.SEARCH idx '@propB:{\"\"}' DIALECT 2\n1) \"1\"\n2) \"key:1\"\n3) 1) \"$\"\n   2) \"{\\\"propA\\\":\\\"foo\\\",\\\"propB\\\":\\\"\\\"}\"","section_id":"index-missing-or-empty-values"}]}
{"schema_version":2,"id":"develop/ai/search-and-query/indexing/interactive-demo","title":"Indexing interactive demo","url":"https://redis.io/docs/latest/develop/ai/search-and-query/indexing/interactive-demo/","summary":"Change an index's field types, prefixes, and options, and see what each query finds, with every redis-cli command and reply.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-15T10:36:51+02:00","page_type":"content","content_hash":"4a847410817586af16b75756a939a0a16cc737aeb974cc41227e2cff6de050da","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Search only finds data that's in an index. You create an index with `FT.CREATE`, which says which keys to include and how to treat each of their fields, and Redis keeps it up to date as the data changes. How you define the index decides what queries can find.\n\nThis demo starts with the data itself, the restaurants of a food delivery app, then creates an index on it and lets you change it: the field types, the keys it covers, how it handles missing and invalid values, and whether the restaurants are hashes or JSON documents. For each change, it shows the commands, the stored data, and which restaurants the query finds.\n\nThe demo runs in your browser and doesn't connect to Redis. Its commands and replies were recorded from Redis, each starting from an empty database, and they work as shown in `redis-cli`."},{"id":"what-the-demo-shows","title":"What the demo shows","role":"content","text":"1. **The data.** Each restaurant is a hash: a key whose name starts with `restaurant:`, holding named fields. Every value in a hash is a string, and Redis finds a hash by its key, not by its contents, until you index it.\n2. **Field types.** `TEXT` fields match words, including other forms of each word, such as plurals. `TAG` fields match whole values. `NUMERIC` fields match ranges. A query that doesn't fit the field's type finds nothing, and with `DIALECT 2` it returns an error instead. See [Field and type options](https://redis.io/docs/latest/develop/ai/search-and-query/indexing/field-and-type-options).\n3. **Which keys.** `PREFIX` decides which keys an index covers, and `FILTER` narrows them down by value. Redis indexes each later write as it happens. See [`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create).\n4. **Missing and bad values.** A value that doesn't fit its field's type keeps Redis from indexing the whole hash, and [`FT.INFO`](https://redis.io/docs/latest/commands/ft.info) counts the failure. `INDEXMISSING` and `INDEXEMPTY` let you query for missing and empty values. See [Index missing or empty values](https://redis.io/docs/latest/develop/ai/search-and-query/indexing#index-missing-or-empty-values).\n5. **Hash or JSON.** An index on JSON documents names JSON paths and gives each one a name with `AS`. Queries are the same as for hashes, but a JSON index returns whole documents as JSON, and Redis 8 doesn't support `HIGHLIGHT` on it. See [Indexing](https://redis.io/docs/latest/develop/ai/search-and-query/indexing)."}],"examples":[]}
{"schema_version":2,"id":"develop/ai/search-and-query/indexing/schema-definition","title":"Schema definition","url":"https://redis.io/docs/latest/develop/ai/search-and-query/indexing/schema-definition/","summary":"How to define the schema of an index.","aliases":["/develop/interact/search-and-query/indexing/schema-definition","/develop/interact/search-and-query/basic-constructs/schema-definition"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:33:04+01:00","page_type":"content","content_hash":"c3e5ad203629ce9b6399bc6ae98274ed5b94c1ac75d0e91c12c6d466e7282b91","sections":[{"id":"overview","title":"Overview","role":"overview","text":"An index structure is defined by a schema. The schema specifies the fields, their types, whether they should be indexed or stored, and other additional configuration options. By properly configuring the schema, you can optimize search performance and control the storage requirements of your index.\n\n[code example]\n\nIn this example, a schema is defined for an index named `idx` that will index all hash documents whose keyname starts with `blog:post:`. \nThe schema includes the fields `title`, `content`, `author`, `created_date`, and `views`. The `TEXT` type indicates that the `title` and `content` fields are text-based, the `TAG` type is used for the `author` field, and the `NUMERIC` type is used for the `created_date` and `views` fields. Additionally, a weight of 5.0 is assigned to the `title` field to give it more relevance in search results, and `created_date` is marked as `SORTABLE` to enable sorting based on this field.\n\nYou can learn more about the available field types and options on the [`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create) page."},{"id":"more-schema-definition-examples","title":"More schema definition examples","role":"content","text":"##### Index tags with a separator\n\nIndex books that have a `categories` attribute, where each category is separated by a `;` character.\n\n[code example]\n\n##### Index a single field in multiple ways\n\nIndex the `sku` attribute from a hash as both a `TAG` and as `TEXT`:\n\n[code example]\n\n##### Index documents with multiple prefixes\n\nIndex two different hashes, one containing author data and one containing book data:\n[code example]\n\nIn this example, keys for author data use the key pattern `author:details:<id>`, while keys for book data use the pattern `book:details:<id>`.\n\n##### Only index documents if a field specifies a certain value using `FILTER`\n\nIndex authors whose names start with G:\n\n[code example]\n\nIndex only books that have a subtitle:\n\n[code example]\n\n##### Index a JSON document using a JSONPath expression\n\nIndex a JSON document that has `title` and `categories` fields. The `title` field is indexed as `TEXT` and the `categories` field is indexed as `TAG`.\n\n[code example]\n\n\nYou can learn more about the available field types and options on the [`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create) page."}],"examples":[{"id":"overview-ex0","language":"plaintext","code":"FT.CREATE idx \n    ON HASH \n    PREFIX 1 blog:post: \nSCHEMA \n    title TEXT WEIGHT 5.0\n    content TEXT\n    author TAG\n    created_date NUMERIC SORTABLE\n    views NUMERIC","section_id":"overview"},{"id":"more-schema-definition-examples-ex0","language":"plaintext","code":"FT.CREATE books-idx \n    ON HASH \n    PREFIX 1 book:details \nSCHEMA \n    title TEXT \n    categories TAG SEPARATOR \";\"","section_id":"more-schema-definition-examples"},{"id":"more-schema-definition-examples-ex1","language":"plaintext","code":"FT.CREATE idx \n    ON HASH \n    PREFIX 1 blog:post: \nSCHEMA \n    sku AS sku_text TEXT \n    sku AS sku_tag TAG SORTABLE","section_id":"more-schema-definition-examples"},{"id":"more-schema-definition-examples-ex2","language":"plaintext","code":"FT.CREATE author-books-idx \n    ON HASH \n    PREFIX 2 author:details: book:details: \nSCHEMA\n    author_id TAG SORTABLE \n    author_ids TAG \n    title TEXT name TEXT","section_id":"more-schema-definition-examples"},{"id":"more-schema-definition-examples-ex3","language":"plaintext","code":"FT.CREATE g-authors-idx \n    ON HASH \n    PREFIX 1 author:details \n    FILTER 'startswith(@name, \"G\")' \nSCHEMA \n    name TEXT","section_id":"more-schema-definition-examples"},{"id":"more-schema-definition-examples-ex4","language":"plaintext","code":"FT.CREATE subtitled-books-idx\n    ON HASH \n    PREFIX 1 book:details \n    FILTER '@subtitle != \"\"' \nSCHEMA \n    title TEXT","section_id":"more-schema-definition-examples"},{"id":"more-schema-definition-examples-ex5","language":"plaintext","code":"FT.CREATE idx \n    ON JSON \nSCHEMA \n    $.title AS title TEXT \n    $.categories AS categories TAG","section_id":"more-schema-definition-examples"}]}
{"schema_version":2,"id":"develop/ai/search-and-query/query/aggregation","title":"Aggregation queries","url":"https://redis.io/docs/latest/develop/ai/search-and-query/query/aggregation/","summary":"Group and aggregate query results","aliases":["/develop/interact/search-and-query/query/aggregation"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:33:04+01:00","page_type":"content","content_hash":"7b05674fc750cff8fa0c725378797bc50573967229d57395d80c1ef759f6fc44","sections":[{"id":"simple-mapping","title":"Simple mapping","role":"content","text":"The `APPLY` clause allows you to apply a simple mapping function to a result set that is returned based on the query expression.\n\n[code example]\n\nHere is a more detailed explanation of the query syntax:\n\n1. **Query expression**: you can use the same query expressions as you would use with the [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) command. You can substitute `query_expr` with any of the expressions explained in the articles of this [query topic](https://redis.io/docs/latest/develop/ai/search-and-query/query). Vector search queries are an exception. You can't combine a vector search with an aggregation query.\n2. **Loaded fields**: if field values weren't already loaded into the aggregation pipeline, you can force their presence via the `LOAD` clause. This clause takes the number of fields (`n`), followed by the field names (`\"field_1\" .. \"field_n\"`).\n3. **Mapping function**: this mapping function operates on the field values. A specific field is referenced as `@field_name` within the function expression. The result is returned as `result_field`.\n\nThe following example shows you how to calculate a discounted price for new bicycles:\n\nFoundational: Apply mapping functions to transform field values using APPLY when you need to calculate derived values\n\n**Difficulty:** Beginner\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nThe field `__key` is a built-in field. \n\nThe output of this query is:\n\n[code example]"},{"id":"grouping-with-aggregation","title":"Grouping with aggregation","role":"content","text":"The previous example did not group the data. You can group and aggregate data based on one or many criteria in the following way:\n\n[code example]\n\nHere is an explanation of the additional constructs:\n\n1. **Grouping**: you can group by one or many fields. Each ordered sequence of field values then defines one group. It's also possible to group by values that resulted from a previous `APPLY ... AS`.\n2. **Aggregation**: you must replace `AGG_FUNC` with one of the supported aggregation functions (e.g., `SUM` or `COUNT`). A complete list of functions is available in the [aggregations reference documentation](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/aggregations). Replace `aggregated_result_field` with a value of your choice.\n\nThe following query shows you how to group by the field `condition` and apply a reduction based on the previously derived `price_category`. The expression `@price<1000` causes a bicycle to have the price category `1` if its price is lower than 1000 USD. Otherwise, it has the price category `0`. The output is the number of affordable bicycles grouped by price category.\n\nGrouping with aggregation: Use GROUPBY with REDUCE to group results by field values and apply aggregation functions when you need to summarize data across groups\n\n**Difficulty:** Intermediate\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\n[code example]\n\n> [!NOTE]\n> You can also create more complex aggregation pipelines with [FT.AGGREGATE](https://redis.io/docs/latest/commands/ft.aggregate). Applying multiple reduction functions under one `GROUPBY` clause is possible. In addition, you can also chain groupings and mix in additional mapping steps (e.g., `GROUPBY ... REDUCE ... APPLY ... GROUPBY ... REDUCE`)"},{"id":"aggregating-without-grouping","title":"Aggregating without grouping","role":"content","text":"You can't use an aggregation function outside of a `GROUPBY` clause, but you can construct your pipeline in a way that the aggregation happens on a single group that spans all documents. If your documents don't share a common attribute, you can add it via an extra `APPLY` step.\n\nHere is an example that adds a type attribute `bicycle` to each document before counting all documents with that type:\n\nAggregating without grouping: Create a dummy grouping field with APPLY to aggregate across all documents when you need a single aggregate result\n\n**Difficulty:** Intermediate\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nThe result is:\n\n[code example]"},{"id":"grouping-without-aggregation","title":"Grouping without aggregation","role":"content","text":"It's sometimes necessary to group your data without applying a mathematical aggregation function. If you need a grouped list of values, then the `TOLIST` function is helpful.\n\nThe following example shows how to group all bicycles by `condition`:\n\nGrouping without aggregation: Use TOLIST to collect grouped values without applying mathematical functions when you need to organize results by category\n\n**Difficulty:** Intermediate\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nThe output of this query is:\n\n[code example]"}],"examples":[{"id":"simple-mapping-ex0","language":"plaintext","code":"FT.AGGREGATE index \"query_expr\" LOAD n \"field_1\" .. \"field_n\" APPLY \"function_expr\" AS \"result_field\"","section_id":"simple-mapping"},{"id":"simple-mapping-ex1","language":"plaintext","code":"FT.AGGREGATE idx:bicycle \"@condition:{new}\" LOAD 2 \"__key\" \"price\" APPLY \"@price - (@price * 0.1)\" AS \"discounted\"","section_id":"simple-mapping"},{"id":"simple-mapping-ex2","language":"csharp","code":"AggregationResult res1 = db.FT().Aggregate(\n            \"idx:bicycle\",\n            new AggregationRequest(\"@condition:{new}\")\n                .Load(new FieldName(\"__key\"), new FieldName(\"price\"))\n                .Apply(\"@price - (@price * 0.1)\", \"discounted\")\n        );\n        Console.WriteLine(res1.TotalResults);   // >>> 5\n\n        for (int i = 0; i < res1.TotalResults; i++)\n        {\n            Row res1Row = res1.GetRow(i);\n\n            Console.WriteLine(\n                $\"Key: {res1Row[\"__key\"]}, Price: {res1Row[\"price\"]}, Discounted: {res1Row[\"discounted\"]}\"\n            );\n        }\n        // >>> Key: bicycle:0, Price: 270, Discounted: 243\n        // >>> Key: bicycle:5, Price: 810, Discounted: 729\n        // >>> Key: bicycle:6, Price: 2300, Discounted: 2070\n        // >>> Key: bicycle:7, Price: 430, Discounted: 387\n        // >>> Key: bicycle:8, Price: 1200, Discounted: 1080","section_id":"simple-mapping"},{"id":"simple-mapping-ex3","language":"go","code":"res1, err := rdb.FTAggregateWithArgs(ctx,\n\t\t\"idx:bicycle\",\n\t\t\"@condition:{new}\",\n\t\t&redis.FTAggregateOptions{\n\t\t\tSteps: []redis.FTAggregateStep{\n\t\t\t\t{Load: &redis.FTAggregateLoad{Field: \"__key\"}},\n\t\t\t\t{Load: &redis.FTAggregateLoad{Field: \"price\"}},\n\t\t\t\t{Apply: &redis.FTAggregateApply{\n\t\t\t\t\tField: \"@price - (@price * 0.1)\",\n\t\t\t\t\tAs:    \"discounted\",\n\t\t\t\t}},\n\t\t\t},\n\t\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(len(res1.Rows)) // >>> 5\n\n\tsort.Slice(res1.Rows, func(i, j int) bool {\n\t\treturn res1.Rows[i].Fields[\"__key\"].(string) <\n\t\t\tres1.Rows[j].Fields[\"__key\"].(string)\n\t})\n\n\tfor _, row := range res1.Rows {\n\t\tfmt.Printf(\n\t\t\t\"__key=%v, discounted=%v, price=%v\\n\",\n\t\t\trow.Fields[\"__key\"],\n\t\t\trow.Fields[\"discounted\"],\n\t\t\trow.Fields[\"price\"],\n\t\t)\n\t}\n\t// >>> __key=bicycle:0, discounted=243, price=270\n\t// >>> __key=bicycle:5, discounted=729, price=810\n\t// >>> __key=bicycle:6, discounted=2070, price=2300\n\t// >>> __key=bicycle:7, discounted=387, price=430\n\t// >>> __key=bicycle:8, discounted=1080, price=1200","section_id":"simple-mapping"},{"id":"simple-mapping-ex4","language":"java","code":"AggregateArgs<String, String> agg1Args = AggregateArgs.<String, String> builder().load(\"__key\").load(\"price\")\n                    .apply(\"@price - (@price * 0.1)\", \"discounted\").build();\n\n            CompletableFuture<AggregationReply<String, String>> agg1 = asyncCommands\n                    .ftAggregate(\"idx:bicycle\", \"@condition:{new}\", agg1Args).thenApply(result -> {\n                        result.getReplies().get(0).getResults().stream()\n                                .sorted((doc1, doc2) -> doc1.getFields().get(\"__key\").compareTo(doc2.getFields().get(\"__key\")))\n                                .forEach(doc -> {\n                                    System.out.printf(\"Key: %s, Price: %s, Discounted: %s\\n\", doc.getFields().get(\"__key\"),\n                                            doc.getFields().get(\"price\"), doc.getFields().get(\"discounted\"));\n                                });\n                        // >>> Key: bicycle:0, Price: 270, Discounted: 243\n                        // >>> Key: bicycle:5, Price: 810, Discounted: 729\n                        // >>> Key: bicycle:6, Price: 2300, Discounted: 2070\n                        // >>> Key: bicycle:7, Price: 430, Discounted: 387\n                        // >>> Key: bicycle:8, Price: 1200, Discounted: 1080\n                        return result;\n                    }).toCompletableFuture();","section_id":"simple-mapping"},{"id":"simple-mapping-ex5","language":"java","code":"AggregateArgs<String, String> agg1Args = AggregateArgs.<String, String> builder().load(\"__key\").load(\"price\")\n                    .apply(\"@price - (@price * 0.1)\", \"discounted\").build();\n\n            Mono<AggregationReply<String, String>> agg1 = reactiveCommands\n                    .ftAggregate(\"idx:bicycle\", \"@condition:{new}\", agg1Args).doOnNext(result -> {\n                        result.getReplies().get(0).getResults().stream()\n                                .sorted((doc1, doc2) -> doc1.getFields().get(\"__key\").compareTo(doc2.getFields().get(\"__key\")))\n                                .forEach(doc -> {\n                                    System.out.printf(\"Key: %s, Price: %s, Discounted: %s\\n\", doc.getFields().get(\"__key\"),\n                                            doc.getFields().get(\"price\"), doc.getFields().get(\"discounted\"));\n                                });\n                        // >>> Key: bicycle:0, Price: 270, Discounted: 243\n                        // >>> Key: bicycle:5, Price: 810, Discounted: 729\n                        // >>> Key: bicycle:6, Price: 2300, Discounted: 2070\n                        // >>> Key: bicycle:7, Price: 430, Discounted: 387\n                        // >>> Key: bicycle:8, Price: 1200, Discounted: 1080\n                    });","section_id":"simple-mapping"},{"id":"simple-mapping-ex6","language":"java","code":"AggregationResult res1 = jedis.ftAggregate(\"idx:bicycle\",\n            new AggregationBuilder(\"@condition:{new}\")\n                    .load(\"__key\", \"price\")\n                    .apply(\"@price - (@price * 0.1)\", \"discounted\")\n        );\n        \n        List<Row> rows1 = res1.getRows();\n        System.out.println(rows1.size());   // >>> 5\n\n        for (int i = 0; i < rows1.size(); i++) {\n            System.out.println(rows1.get(i));\n        }\n        // >>> {__key=bicycle:0, discounted=243, price=270}\n        // >>> {__key=bicycle:5, discounted=729, price=810}\n        // >>> {__key=bicycle:6, discounted=2070, price=2300}\n        // >>> {__key=bicycle:7, discounted=387, price=430}\n        // >>> {__key=bicycle:8, discounted=1080, price=1200}","section_id":"simple-mapping"},{"id":"simple-mapping-ex7","language":"javascript","code":"const res1 = await client.ft.aggregate('idx:bicycle', '@condition:{new}', {\n  LOAD: ['__key', 'price'],\n  APPLY: {\n    expression: '@price - (@price * 0.1)',\n    AS: 'discounted'\n  }\n});\n\nconsole.log(res1.results.length); // >>> 5\nconsole.log(res1.results); // >>>\n//[\n//  [Object: null prototype] { __key: 'bicycle:0', price: '270' },\n//  [Object: null prototype] { __key: 'bicycle:5', price: '810' },\n//  [Object: null prototype] { __key: 'bicycle:6', price: '2300' },\n//  [Object: null prototype] { __key: 'bicycle:7', price: '430' },\n//  [Object: null prototype] { __key: 'bicycle:8', price: '1200' }\n//]","section_id":"simple-mapping"},{"id":"simple-mapping-ex8","language":"python","code":"search = Search(r, index_name=\"idx:bicycle\")\naggregate_request = AggregateRequest(query='@condition:{new}') \\\n    .load('__key', 'price') \\\n    .apply(discounted='@price - (@price * 0.1)')\nres = search.aggregate(aggregate_request)\nprint(len(res.rows)) # >>> 5\nprint(res.rows) # >>> [['__key', 'bicycle:0', ...\n#[['__key', 'bicycle:0', 'price', '270', 'discounted', '243'],\n# ['__key', 'bicycle:5', 'price', '810', 'discounted', '729'],\n# ['__key', 'bicycle:6', 'price', '2300', 'discounted', '2070'],\n# ['__key', 'bicycle:7', 'price', '430', 'discounted', '387'],\n# ['__key', 'bicycle:8', 'price', '1200', 'discounted', '1080']]","section_id":"simple-mapping"},{"id":"simple-mapping-ex9","language":"plaintext","code":"1) \"1\"\n2) 1) \"__key\"\n   1) \"bicycle:0\"\n   2) \"price\"\n   3) \"270\"\n   4) \"discounted\"\n   5) \"243\"\n3) 1) \"__key\"\n   1) \"bicycle:5\"\n   2) \"price\"\n   3) \"810\"\n   4) \"discounted\"\n   5) \"729\"\n4) 1) \"__key\"\n   1) \"bicycle:6\"\n   2) \"price\"\n   3) \"2300\"\n   4) \"discounted\"\n   5) \"2070\"\n...","section_id":"simple-mapping"},{"id":"grouping-with-aggregation-ex0","language":"plaintext","code":"FT.AGGREGATE index \"query_expr\" ...  GROUPBY n \"field_1\" .. \"field_n\" REDUCE AGG_FUNC m \"@field_param_1\" .. \"@field_param_m\" AS \"aggregated_result_field\"","section_id":"grouping-with-aggregation"},{"id":"grouping-with-aggregation-ex1","language":"plaintext","code":"FT.AGGREGATE idx:bicycle \"*\" LOAD 1 price APPLY \"@price<1000\" AS price_category GROUPBY 1 @condition REDUCE SUM 1 \"@price_category\" AS \"num_affordable\"","section_id":"grouping-with-aggregation"},{"id":"grouping-with-aggregation-ex2","language":"csharp","code":"AggregationResult res2 = db.FT().Aggregate(\n            \"idx:bicycle\",\n            new AggregationRequest(\"*\")\n                .Load(new FieldName(\"price\"))\n                .Apply(\"@price<1000\", \"price_category\")\n                .GroupBy(\n                    \"@condition\",\n                    Reducers.Sum(\"@price_category\").As(\"num_affordable\")\n                )\n        );\n        Console.WriteLine(res2.TotalResults);   // >>> 3\n\n        for (int i = 0; i < res2.TotalResults; i++)\n        {\n            Row res2Row = res2.GetRow(i);\n\n            Console.WriteLine(\n                $\"Condition: {res2Row[\"condition\"]}, Num affordable: {res2Row[\"num_affordable\"]}\"\n            );\n        }\n        // >>> Condition: refurbished, Num affordable: 1\n        // >>> Condition: used, Num affordable: 1\n        // >>> Condition: new, Num affordable: 3","section_id":"grouping-with-aggregation"},{"id":"grouping-with-aggregation-ex3","language":"go","code":"res2, err := rdb.FTAggregateWithArgs(ctx,\n\t\t\"idx:bicycle\", \"*\",\n\t\t&redis.FTAggregateOptions{\n\t\t\tSteps: []redis.FTAggregateStep{\n\t\t\t\t{Load: &redis.FTAggregateLoad{Field: \"price\"}},\n\t\t\t\t{Apply: &redis.FTAggregateApply{\n\t\t\t\t\tField: \"@price<1000\",\n\t\t\t\t\tAs:    \"price_category\",\n\t\t\t\t}},\n\t\t\t\t{GroupBy: &redis.FTAggregateGroupBy{\n\t\t\t\t\tFields: []interface{}{\"@condition\"},\n\t\t\t\t\tReduce: []redis.FTAggregateReducer{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tReducer: redis.SearchSum,\n\t\t\t\t\t\t\tArgs:    []interface{}{\"@price_category\"},\n\t\t\t\t\t\t\tAs:      \"num_affordable\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t}},\n\t\t\t},\n\t\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(len(res2.Rows)) // >>> 3\n\n\tsort.Slice(res2.Rows, func(i, j int) bool {\n\t\treturn res2.Rows[i].Fields[\"condition\"].(string) <\n\t\t\tres2.Rows[j].Fields[\"condition\"].(string)\n\t})\n\n\tfor _, row := range res2.Rows {\n\t\tfmt.Printf(\n\t\t\t\"condition=%v, num_affordable=%v\\n\",\n\t\t\trow.Fields[\"condition\"],\n\t\t\trow.Fields[\"num_affordable\"],\n\t\t)\n\t}\n\t// >>> condition=new, num_affordable=3\n\t// >>> condition=refurbished, num_affordable=1\n\t// >>> condition=used, num_affordable=1","section_id":"grouping-with-aggregation"},{"id":"grouping-with-aggregation-ex4","language":"java","code":"AggregateArgs<String, String> agg2Args = AggregateArgs.<String, String> builder().load(\"price\")\n                    .apply(\"@price<1000\", \"price_category\").groupBy(GroupBy.<String, String> of(\"condition\")\n                            .reduce(Reducer.<String, String> sum(\"@price_category\").as(\"num_affordable\")))\n                    .build();\n\n            CompletableFuture<AggregationReply<String, String>> agg2 = asyncCommands.ftAggregate(\"idx:bicycle\", \"*\", agg2Args)\n                    .thenApply(result -> {\n                        result.getReplies().get(0).getResults().stream().sorted(\n                                (doc1, doc2) -> doc1.getFields().get(\"condition\").compareTo(doc2.getFields().get(\"condition\")))\n                                .forEach(doc -> {\n                                    System.out.printf(\"Condition: %s, Num Affordable: %s\\n\", doc.getFields().get(\"condition\"),\n                                            doc.getFields().get(\"num_affordable\"));\n                                });\n                        // >>> Condition: new, Num Affordable: 3\n                        // >>> Condition: refurbished, Num Affordable: 1\n                        // >>> Condition: used, Num Affordable: 1\n                        return result;\n                    }).toCompletableFuture();","section_id":"grouping-with-aggregation"},{"id":"grouping-with-aggregation-ex5","language":"java","code":"AggregateArgs<String, String> agg2Args = AggregateArgs.<String, String> builder().load(\"price\")\n                    .apply(\"@price<1000\", \"price_category\").groupBy(GroupBy.<String, String> of(\"condition\")\n                            .reduce(Reducer.<String, String> sum(\"@price_category\").as(\"num_affordable\")))\n                    .build();\n\n            Mono<AggregationReply<String, String>> agg2 = reactiveCommands.ftAggregate(\"idx:bicycle\", \"*\", agg2Args)\n                    .doOnNext(result -> {\n                        result.getReplies().get(0).getResults().stream().sorted(\n                                (doc1, doc2) -> doc1.getFields().get(\"condition\").compareTo(doc2.getFields().get(\"condition\")))\n                                .forEach(doc -> {\n                                    System.out.printf(\"Condition: %s, Num Affordable: %s\\n\", doc.getFields().get(\"condition\"),\n                                            doc.getFields().get(\"num_affordable\"));\n                                });\n                        // >>> Condition: new, Num Affordable: 3\n                        // >>> Condition: refurbished, Num Affordable: 1\n                        // >>> Condition: used, Num Affordable: 1\n                    });","section_id":"grouping-with-aggregation"},{"id":"grouping-with-aggregation-ex6","language":"java","code":"AggregationResult res2 = jedis.ftAggregate(\"idx:bicycle\",\n            new AggregationBuilder(\"*\")\n                    .load(\"price\")\n                    .apply(\"@price<1000\", \"price_category\")\n                    .groupBy(\"@condition\",\n                        Reducers.sum(\"@price_category\").as(\"num_affordable\"))\n        );\n\n        List<Row> rows2 = res2.getRows();\n        System.out.println(rows2.size());   // >>> 3\n\n        for (int i = 0; i < rows2.size(); i++) {\n            System.out.println(rows2.get(i));\n        }\n        // >>> {condition=refurbished, num_affordable=1}\n        // >>> {condition=used, num_affordable=1}\n        // >>> {condition=new, num_affordable=3}","section_id":"grouping-with-aggregation"},{"id":"grouping-with-aggregation-ex7","language":"javascript","code":"const res2 = await client.ft.aggregate('idx:bicycle', '*', {\n  LOAD: ['@price'],\n  STEPS: [{\n      type: FT_AGGREGATE_STEPS.APPLY,\n      expression: '@price<1000',\n      AS: 'price_category'\n    },{\n      type: FT_AGGREGATE_STEPS.GROUPBY,\n      properties: '@condition',\n      REDUCE:[{\n        type: FT_AGGREGATE_GROUP_BY_REDUCERS.SUM,\n        property: '@price_category',\n        AS: 'num_affordable'\n      }]\n    }]\n});\nconsole.log(res2.results.length); // >>> 3\nconsole.log(res2.results); // >>>\n//[[Object: null prototype] { condition: 'refurbished', num_affordable: '1' },\n//  [Object: null prototype] { condition: 'used', num_affordable: '1' },\n//  [Object: null prototype] { condition: 'new', num_affordable: '3' }\n//]","section_id":"grouping-with-aggregation"},{"id":"grouping-with-aggregation-ex8","language":"python","code":"search = Search(r, index_name=\"idx:bicycle\")\naggregate_request = AggregateRequest(query='*') \\\n    .load('price') \\\n    .apply(price_category='@price<1000') \\\n    .group_by('@condition', reducers.sum('@price_category').alias('num_affordable'))\nres = search.aggregate(aggregate_request)\nprint(len(res.rows)) # >>> 3\nprint(res.rows) # >>>\n#[['condition', 'refurbished', 'num_affordable', '1'],\n# ['condition', 'used', 'num_affordable', '1'],\n# ['condition', 'new', 'num_affordable', '3']]","section_id":"grouping-with-aggregation"},{"id":"grouping-with-aggregation-ex9","language":"plaintext","code":"1) \"3\"\n2) 1) \"condition\"\n   1) \"refurbished\"\n   2) \"num_affordable\"\n   3) \"1\"\n3) 1) \"condition\"\n   1) \"used\"\n   2) \"num_affordable\"\n   3) \"1\"\n4) 1) \"condition\"\n   1) \"new\"\n   2) \"num_affordable\"\n   3) \"3\"","section_id":"grouping-with-aggregation"},{"id":"aggregating-without-grouping-ex0","language":"plaintext","code":"FT.AGGREGATE idx:bicycle \"*\" APPLY \"'bicycle'\" AS type GROUPBY 1 @type REDUCE COUNT 0 AS num_total","section_id":"aggregating-without-grouping"},{"id":"aggregating-without-grouping-ex1","language":"csharp","code":"AggregationResult res3 = db.FT().Aggregate(\n            \"idx:bicycle\",\n            new AggregationRequest(\"*\")\n                .Apply(\"'bicycle'\", \"type\")\n                .GroupBy(\"@type\", Reducers.Count().As(\"num_total\"))\n        );\n        Console.WriteLine(res3.TotalResults);   // >>> 1\n\n        Row res3Row = res3.GetRow(0);\n        Console.WriteLine($\"Type: {res3Row[\"type\"]}, Num total: {res3Row[\"num_total\"]}\");\n        // >>> Type: bicycle, Num total: 10","section_id":"aggregating-without-grouping"},{"id":"aggregating-without-grouping-ex2","language":"go","code":"res3, err := rdb.FTAggregateWithArgs(ctx,\n\t\t\"idx:bicycle\", \"*\",\n\t\t&redis.FTAggregateOptions{\n\t\t\tSteps: []redis.FTAggregateStep{\n\t\t\t\t{Apply: &redis.FTAggregateApply{\n\t\t\t\t\tField: \"'bicycle'\",\n\t\t\t\t\tAs:    \"type\",\n\t\t\t\t}},\n\t\t\t\t{GroupBy: &redis.FTAggregateGroupBy{\n\t\t\t\t\tFields: []interface{}{\"@type\"},\n\t\t\t\t\tReduce: []redis.FTAggregateReducer{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tReducer: redis.SearchCount,\n\t\t\t\t\t\t\tAs:      \"num_total\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t}},\n\t\t\t},\n\t\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(len(res3.Rows)) // >>> 1\n\n\tfor _, row := range res3.Rows {\n\t\tfmt.Printf(\n\t\t\t\"type=%v, num_total=%v\\n\",\n\t\t\trow.Fields[\"type\"],\n\t\t\trow.Fields[\"num_total\"],\n\t\t)\n\t}\n\t// type=bicycle, num_total=10","section_id":"aggregating-without-grouping"},{"id":"aggregating-without-grouping-ex3","language":"java","code":"AggregateArgs<String, String> agg3Args = AggregateArgs.<String, String> builder().apply(\"'bicycle'\", \"type\")\n                    .groupBy(GroupBy.<String, String> of(\"type\").reduce(Reducer.<String, String> count().as(\"num_total\")))\n                    .build();\n\n            CompletableFuture<AggregationReply<String, String>> agg3 = asyncCommands.ftAggregate(\"idx:bicycle\", \"*\", agg3Args)\n                    .thenApply(result -> {\n                        result.getReplies().get(0).getResults().stream()\n                                .sorted((doc1, doc2) -> doc1.getFields().get(\"type\").compareTo(doc2.getFields().get(\"type\")))\n                                .forEach(doc -> {\n                                    System.out.printf(\"Type: %s, Total Count: %s\\n\", doc.getFields().get(\"type\"),\n                                            doc.getFields().get(\"num_total\"));\n                                });\n                        // >>> Type: bicycle, Total Count: 10\n                        return result;\n                    }).toCompletableFuture();","section_id":"aggregating-without-grouping"},{"id":"aggregating-without-grouping-ex4","language":"java","code":"AggregateArgs<String, String> agg3Args = AggregateArgs.<String, String> builder().apply(\"'bicycle'\", \"type\")\n                    .groupBy(GroupBy.<String, String> of(\"type\").reduce(Reducer.<String, String> count().as(\"num_total\")))\n                    .build();\n\n            Mono<AggregationReply<String, String>> agg3 = reactiveCommands.ftAggregate(\"idx:bicycle\", \"*\", agg3Args)\n                    .doOnNext(result -> {\n                        result.getReplies().get(0).getResults().stream()\n                                .sorted((doc1, doc2) -> doc1.getFields().get(\"type\").compareTo(doc2.getFields().get(\"type\")))\n                                .forEach(doc -> {\n                                    System.out.printf(\"Type: %s, Total Count: %s\\n\", doc.getFields().get(\"type\"),\n                                            doc.getFields().get(\"num_total\"));\n                                });\n                        // >>> Type: bicycle, Total Count: 10\n                    });","section_id":"aggregating-without-grouping"},{"id":"aggregating-without-grouping-ex5","language":"java","code":"AggregationResult res3 = jedis.ftAggregate(\"idx:bicycle\",\n            new AggregationBuilder(\"*\")\n                    .apply(\"'bicycle'\", \"type\")\n                    .groupBy(\"@type\", Reducers.count().as(\"num_total\"))\n        );\n\n        List<Row> rows3 = res3.getRows();\n        System.out.println(rows3.size());   // >>> 1\n\n        for (int i = 0; i < rows3.size(); i++) {\n            System.out.println(rows3.get(i));\n        }\n        // >>> {type=bicycle, num_total=10}","section_id":"aggregating-without-grouping"},{"id":"aggregating-without-grouping-ex6","language":"javascript","code":"const res3 = await client.ft.aggregate('idx:bicycle', '*', {\n  STEPS: [{\n      type: FT_AGGREGATE_STEPS.APPLY,\n      expression: \"'bicycle'\",\n      AS: 'type'\n    }, {\n      type: FT_AGGREGATE_STEPS.GROUPBY,\n      properties: '@type',\n      REDUCE: [{\n        type: FT_AGGREGATE_GROUP_BY_REDUCERS.COUNT,\n        property: null,\n        AS: 'num_total'\n      }]\n    }]\n});\nconsole.log(res3.results.length); // >>> 1\nconsole.log(res3.results); // >>>\n//[ [Object: null prototype] { type: 'bicycle', num_total: '10' } ]","section_id":"aggregating-without-grouping"},{"id":"aggregating-without-grouping-ex7","language":"python","code":"search = Search(r, index_name=\"idx:bicycle\")\naggregate_request = AggregateRequest(query='*') \\\n    .apply(type=\"'bicycle'\") \\\n    .group_by('@type', reducers.count().alias('num_total'))\nres = search.aggregate(aggregate_request)\nprint(len(res.rows)) # >>> 1\nprint(res.rows) # >>> [['type', 'bicycle', 'num_total', '10']]","section_id":"aggregating-without-grouping"},{"id":"aggregating-without-grouping-ex8","language":"plaintext","code":"1) \"1\"\n2) 1) \"type\"\n   1) \"bicycle\"\n   2) \"num_total\"\n   3) \"10\"","section_id":"aggregating-without-grouping"},{"id":"grouping-without-aggregation-ex0","language":"plaintext","code":"FT.AGGREGATE idx:bicycle \"*\" LOAD 1 \"__key\" GROUPBY 1 \"@condition\" REDUCE TOLIST 1 \"__key\" AS bicycles","section_id":"grouping-without-aggregation"},{"id":"grouping-without-aggregation-ex1","language":"csharp","code":"// Not supported in NRedisStack.","section_id":"grouping-without-aggregation"},{"id":"grouping-without-aggregation-ex2","language":"go","code":"res4, err := rdb.FTAggregateWithArgs(ctx,\n\t\t\"idx:bicycle\", \"*\",\n\t\t&redis.FTAggregateOptions{\n\t\t\tSteps: []redis.FTAggregateStep{\n\t\t\t\t{Load: &redis.FTAggregateLoad{Field: \"__key\"}},\n\t\t\t\t{GroupBy: &redis.FTAggregateGroupBy{\n\t\t\t\t\tFields: []interface{}{\"@condition\"},\n\t\t\t\t\tReduce: []redis.FTAggregateReducer{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tReducer: redis.SearchToList,\n\t\t\t\t\t\t\tArgs:    []interface{}{\"__key\"},\n\t\t\t\t\t\t\tAs:      \"bicycles\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t}},\n\t\t\t},\n\t\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(len(res4.Rows)) // >>> 3\n\n\tsort.Slice(res4.Rows, func(i, j int) bool {\n\t\treturn res4.Rows[i].Fields[\"condition\"].(string) <\n\t\t\tres4.Rows[j].Fields[\"condition\"].(string)\n\t})\n\n\tfor _, row := range res4.Rows {\n\t\trowBikes := row.Fields[\"bicycles\"].([]interface{})\n\t\tbikes := make([]string, len(rowBikes))\n\n\t\tfor i, rowBike := range rowBikes {\n\t\t\tbikes[i] = rowBike.(string)\n\t\t}\n\n\t\tsort.Slice(bikes, func(i, j int) bool {\n\t\t\treturn bikes[i] < bikes[j]\n\t\t})\n\n\t\tfmt.Printf(\n\t\t\t\"condition=%v, bicycles=%v\\n\",\n\t\t\trow.Fields[\"condition\"],\n\t\t\tbikes,\n\t\t)\n\t}\n\t// >>> condition=new, bicycles=[bicycle:0 bicycle:5 bicycle:6 bicycle:7 bicycle:8]\n\t// >>> condition=refurbished, bicycles=[bicycle:9]\n\t// >>> condition=used, bicycles=[bicycle:1 bicycle:2 bicycle:3 bicycle:4]","section_id":"grouping-without-aggregation"},{"id":"grouping-without-aggregation-ex3","language":"java","code":"// The `TOLIST` reducer is not currently available in Lettuce.","section_id":"grouping-without-aggregation"},{"id":"grouping-without-aggregation-ex4","language":"java","code":"// The `TOLIST` reducer is not currently available in Lettuce.","section_id":"grouping-without-aggregation"},{"id":"grouping-without-aggregation-ex5","language":"java","code":"AggregationResult res4 = jedis.ftAggregate(\"idx:bicycle\",\n            new AggregationBuilder(\"*\")\n                    .load(\"__key\")\n                    .groupBy(\"@condition\",\n                        Reducers.to_list(\"__key\").as(\"bicycles\"))\n        );\n\n        List<Row> rows4 = res4.getRows();\n        System.out.println(rows4.size());   // >>> 3\n\n        for (int i = 0; i < rows4.size(); i++) {\n            System.out.println(rows4.get(i));\n        }\n        // >>> {condition=refurbished, bicycles=[bicycle:9]}\n        // >>> {condition=used, bicycles=[bicycle:3, bicycle:4, bicycle:1, bicycle:2]}\n        // >>> {condition=new, bicycles=[bicycle:7, bicycle:0, bicycle:5, bicycle:6, bicycle:8]}","section_id":"grouping-without-aggregation"},{"id":"grouping-without-aggregation-ex6","language":"javascript","code":"const res4 = await client.ft.aggregate('idx:bicycle', '*', {\n  LOAD: ['__key'],\n  STEPS: [{\n      type: FT_AGGREGATE_STEPS.GROUPBY,\n      properties: '@condition',\n      REDUCE: [{\n        type: FT_AGGREGATE_GROUP_BY_REDUCERS.TOLIST,\n        property: '__key',\n        AS: 'bicycles'\n      }]\n    }]\n});\nconsole.log(res4.results.length); // >>> 3\nconsole.log(res4.results); // >>>\n//[[Object: null prototype] {condition: 'refurbished', bicycles: [ 'bicycle:9' ]},\n//  [Object: null prototype] {condition: 'used', bicycles: [ 'bicycle:1', 'bicycle:2', 'bicycle:3', 'bicycle:4' ]},\n//  [Object: null prototype] {condition: 'new', bicycles: [ 'bicycle:5', 'bicycle:6', 'bicycle:7', 'bicycle:0', 'bicycle:8' ]}]","section_id":"grouping-without-aggregation"},{"id":"grouping-without-aggregation-ex7","language":"python","code":"search = Search(r, index_name=\"idx:bicycle\")\naggregate_request = AggregateRequest(query='*') \\\n    .load('__key') \\\n    .group_by('@condition', reducers.tolist('__key').alias('bicycles'))\nres = search.aggregate(aggregate_request)\nprint(len(res.rows)) # >>> 3\nprint(res.rows) # >>>\n#[['condition', 'refurbished', 'bicycles', ['bicycle:9']],\n# ['condition', 'used', 'bicycles', ['bicycle:1', 'bicycle:2', 'bicycle:3', 'bicycle:4']],\n# ['condition', 'new', 'bicycles', ['bicycle:5', 'bicycle:6', 'bicycle:7', 'bicycle:0', 'bicycle:8']]]","section_id":"grouping-without-aggregation"},{"id":"grouping-without-aggregation-ex8","language":"plaintext","code":"1) \"3\"\n2) 1) \"condition\"\n   1) \"refurbished\"\n   2) \"bicycles\"\n   3) 1) \"bicycle:9\"\n3) 1) \"condition\"\n   1) \"used\"\n   2) \"bicycles\"\n   3) 1) \"bicycle:1\"\n      1) \"bicycle:2\"\n      2) \"bicycle:3\"\n      3) \"bicycle:4\"\n4) 1) \"condition\"\n   1) \"new\"\n   2) \"bicycles\"\n   3) 1) \"bicycle:0\"\n      1) \"bicycle:5\"\n      2) \"bicycle:6\"\n      3) \"bicycle:8\"\n      4) \"bicycle:7\"","section_id":"grouping-without-aggregation"}]}
{"schema_version":2,"id":"develop/ai/search-and-query/query/combined","title":"Combined queries","url":"https://redis.io/docs/latest/develop/ai/search-and-query/query/combined/","summary":"Combine query expressions","aliases":["/develop/interact/search-and-query/query/combined"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:33:04+01:00","page_type":"content","content_hash":"1f1fbd4cde09d6decf4eae44ab0e63d02ed25d5e39be739f0f19386fae39330f","sections":[{"id":"and","title":"AND","role":"content","text":"The binary operator ` ` (space) is used to intersect the results of two or more expressions.\n\n[code example]\n\nIf you want to perform an intersection based on multiple values within a specific text field, then you should use the following simplified notion:\n\n[code example]\n\nThe following example shows you a query that finds bicycles in new condition and in a price range from 500 USD to 1000 USD:\n\nAND operator: Combine multiple query expressions using space to find documents matching all criteria when you need to narrow results with multiple filters\n\n**Difficulty:** Beginner\n\n**Available in:** Redis CLI, JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nYou might also be interested in bicycles for kids. The query below shows you how to combine a full-text search with the criteria from the previous query:\n\nAND with full-text: Combine full-text search with structured queries using AND when you need to find documents with specific text and matching field values\n\n**Difficulty:** Beginner\n\n**Available in:** Redis CLI, JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]"},{"id":"or","title":"OR","role":"content","text":"You can use the binary operator `|` (vertical bar) to perform a union.\n\n[code example]\n\n> [!NOTE]\n> The logical `AND` takes precedence over `OR` when using dialect version two. The expression `expr1 expr2 | expr3 expr4` means `(expr1 expr2) | (expr3 expr4)`. Version one of the query dialect behaves differently. Using parentheses in query strings is advised to ensure the order is clear.\n>  \n\n\nIf you want to perform the union based on multiple values within a single tag or text field, then you should use the following simplified notion:\n\n[code example]\n\n[code example]\n\nThe following query shows you how to find used bicycles that contain either the word 'kids' or 'small':\n\nOR operator: Combine multiple expressions using the pipe operator to find documents matching any criteria when you need to broaden results with alternatives\n\n**Difficulty:** Intermediate\n\n**Available in:** Redis CLI, JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nThe previous query searches across all text fields. The following example shows you how to limit the search to the description field:\n\nOR within a field: Use OR to search for multiple values within a single field when you need to match any of several options in one field\n\n**Difficulty:** Intermediate\n\n**Available in:** Redis CLI, JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nIf you want to extend the search to new bicycles, then the below example shows you how to do that:\n\nOR across multiple fields: Combine OR operators across different fields when you need to match alternatives in multiple field criteria\n\n**Difficulty:** Intermediate\n\n**Available in:** Redis CLI, JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]"},{"id":"not","title":"NOT","role":"content","text":"A minus (`-`) in front of a query expression negates the expression.\n\n[code example]\n\nIf you want to exclude new bicycles from the search within the previous price range, you can use this query:\n\nNOT operator: Negate query expressions using minus to exclude documents matching criteria when you need to filter out unwanted results\n\n**Difficulty:** Intermediate\n\n**Available in:** Redis CLI, JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]"},{"id":"numeric-filter","title":"Numeric filter","role":"content","text":"The [FT.SEARCH](https://redis.io/docs/latest/commands/ft.search) command allows you to combine any query expression with a numeric filter.\n\n[code example]\n\nPlease see the [range query article](https://redis.io/docs/latest/develop/ai/search-and-query/query/range) to learn more about numeric range queries and such filters."},{"id":"pre-filter-for-a-knn--vector-query","title":"Pre-filter for a KNN  vector query","role":"content","text":"You can use a simple or more complex query expression with logical operators as a pre-filter in a KNN vector query. \n\n[code example]\n\nHere is an example:\n\nVector pre-filtering: Use structured queries as pre-filters before KNN vector search when you need to find similar items within a specific subset of data\n\n**Difficulty:** Advanced\n\n**Available in:** Redis CLI, JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nThe [vector search article](https://redis.io/docs/latest/develop/ai/search-and-query/query/vector-search) provides further details about vector queries in general."}],"examples":[{"id":"and-ex0","language":"plaintext","code":"FT.SEARCH index \"(expr1) (expr2)\"","section_id":"and"},{"id":"and-ex1","language":"plaintext","code":"FT.SEARCH index \"@text_field:( value1 value2 ... )\"","section_id":"and"},{"id":"and-ex2","language":"plaintext","code":"FT.SEARCH idx:bicycle \"@price:[500 1000] @condition:{new}\"","section_id":"and"},{"id":"and-ex3","language":"javascript","code":"const res1 = await client.ft.search('idx:bicycle', '@price:[500 1000] @condition:{new}');\nconsole.log(res1.total); // >>> 1\nconsole.log(res1); // >>>\n//{\n//  total: 1,\n//  documents: [ { id: 'bicycle:5', value: [Object: null prototype] } ]\n//}","section_id":"and"},{"id":"and-ex4","language":"python","code":"q = Query(\"@price:[500 1000] @condition:{new}\")\nres = index.search(q)\nprint(res.total) # >>> 1","section_id":"and"},{"id":"and-ex5","language":"plaintext","code":"FT.SEARCH idx:bicycle \"kids (@price:[500 1000] @condition:{used})\"","section_id":"and"},{"id":"and-ex6","language":"javascript","code":"const res2 = await client.ft.search('idx:bicycle', 'kids @price:[500 1000] @condition:{used}');\nconsole.log(res2.total); // >>> 1\nconsole.log(res2); // >>>\n// {\n//   total: 1,\n//   documents: [ { id: 'bicycle:2', value: [Object: null prototype] } ]\n// }","section_id":"and"},{"id":"and-ex7","language":"python","code":"q = Query(\"kids @price:[500 1000] @condition:{used}\")\nres = index.search(q)\nprint(res.total) # >>> 1","section_id":"and"},{"id":"or-ex0","language":"plaintext","code":"FT.SEARCH index \"(expr1) | (expr2)\"","section_id":"or"},{"id":"or-ex1","language":"plaintext","code":"FT.SEARCH index \"@text_field:( value1 | value2 | ... )\"","section_id":"or"},{"id":"or-ex2","language":"plaintext","code":"FT.SEARCH index \"@tag_field:{ value1 | value2 | ... }\"","section_id":"or"},{"id":"or-ex3","language":"plaintext","code":"FT.SEARCH idx:bicycle \"(kids | small) @condition:{used}\"","section_id":"or"},{"id":"or-ex4","language":"javascript","code":"const res3 = await client.ft.search('idx:bicycle', '(kids | small) @condition:{used}');\nconsole.log(res3.total); // >>> 2\nconsole.log(res3); // >>>\n//{\n//  total: 2,\n//  documents: [\n//    { id: 'bicycle:2', value: [Object: null prototype] },\n//    { id: 'bicycle:1', value: [Object: null prototype] }\n//  ]\n//}","section_id":"or"},{"id":"or-ex5","language":"python","code":"q = Query(\"(kids | small) @condition:{used}\")\nres = index.search(q)\nprint(res.total) # >>> 2","section_id":"or"},{"id":"or-ex6","language":"plaintext","code":"FT.SEARCH idx:bicycle \"@description:(kids | small) @condition:{used}\"","section_id":"or"},{"id":"or-ex7","language":"javascript","code":"const res4 = await client.ft.search('idx:bicycle', '@description:(kids | small) @condition:{used}');\nconsole.log(res4.total); // >>> 2\nconsole.log(res4); // >>>\n//{\n//  total: 2,\n//  documents: [\n//    { id: 'bicycle:2', value: [Object: null prototype] },\n//    { id: 'bicycle:1', value: [Object: null prototype] }\n//  ]\n//}","section_id":"or"},{"id":"or-ex8","language":"python","code":"q = Query(\"@description:(kids | small) @condition:{used}\")\nres = index.search(q)\nprint(res.total) # >>> 0","section_id":"or"},{"id":"or-ex9","language":"plaintext","code":"FT.SEARCH idx:bicycle \"@description:(kids | small) @condition:{new | used}\"","section_id":"or"},{"id":"or-ex10","language":"javascript","code":"const res5 = await client.ft.search('idx:bicycle', '@description:(kids | small) @condition:{new | used}');\nconsole.log(res5.total); // >>> 3\nconsole.log(res5); // >>>\n//{\n//  total: 3,\n//  documents: [\n//    { id: 'bicycle:1', value: [Object: null prototype] },\n//    { id: 'bicycle:0', value: [Object: null prototype] },\n//    { id: 'bicycle:2', value: [Object: null prototype] }\n//  ]\n//}","section_id":"or"},{"id":"or-ex11","language":"python","code":"q = Query(\"@description:(kids | small) @condition:{new | used}\")\nres = index.search(q)\nprint(res.total) # >>> 0","section_id":"or"},{"id":"not-ex0","language":"plaintext","code":"FT.SEARCH index \"-(expr)\"","section_id":"not"},{"id":"not-ex1","language":"plaintext","code":"FT.SEARCH idx:bicycle \"@price:[500 1000] -@condition:{new}\"","section_id":"not"},{"id":"not-ex2","language":"javascript","code":"const res6 = await client.ft.search('idx:bicycle', '@price:[500 1000] -@condition:{new}');\nconsole.log(res6.total); // >>> 2\nconsole.log(res6); // >>>\n//{\n//  total: 2,\n//  documents: [\n//    { id: 'bicycle:2', value: [Object: null prototype] },\n//    { id: 'bicycle:9', value: [Object: null prototype] }\n//  ]\n//}","section_id":"not"},{"id":"not-ex3","language":"python","code":"q = Query(\"@price:[500 1000] -@condition:{new}\")\nres = index.search(q)\nprint(res.total) # >>> 2","section_id":"not"},{"id":"numeric-filter-ex0","language":"plaintext","code":"FT.SEARCH index \"expr\" FILTER numeric_field start end","section_id":"numeric-filter"},{"id":"pre-filter-for-a-knn--vector-query-ex0","language":"plaintext","code":"FT.SEARCH index \"(filter_expr)=>[KNN num_neighbours @field $vector]\" PARAMS 2 vector \"binary_data\" DIALECT 2","section_id":"pre-filter-for-a-knn--vector-query"},{"id":"pre-filter-for-a-knn--vector-query-ex1","language":"plaintext","code":"FT.SEARCH idx:bikes_vss \"(@price:[500 1000] @condition:{new})=>[KNN 3 @vector $query_vector]\" PARAMS 2 \"query_vector\" \"Z\\xf8\\x15:\\xf23\\xa1\\xbfZ\\x1dI>\\r\\xca9...\" DIALECT 2","section_id":"pre-filter-for-a-knn--vector-query"},{"id":"pre-filter-for-a-knn--vector-query-ex2","language":"javascript","code":"const res7 = await client.ft.search('idx:bicycle', \n  '(@price:[500 1000] -@condition:{new})=>[KNN 3 @vector $query_vector]', {\n    PARAMS: { query_vector: vector_query },\n    DIALECT: 2\n  }\n);\nconsole.log(res7.total); // >>> 2\nconsole.log(res7); // >>>\n//{\n//  total: 2,\n//  documents: [\n//    { id: 'bicycle:2', value: [Object: null prototype] },\n//    { id: 'bicycle:9', value: [Object: null prototype] }\n//  ]\n//}","section_id":"pre-filter-for-a-knn--vector-query"},{"id":"pre-filter-for-a-knn--vector-query-ex3","language":"python","code":"q = Query(\"(@price:[500 1000] -@condition:{new})=>[KNN 3 @vector $query_vector]\").dialect(2)\n# put query string here\nres = index.search(q,{ 'query_vector': query_vector })\nprint(res.total) # >>> 2","section_id":"pre-filter-for-a-knn--vector-query"}]}
{"schema_version":2,"id":"develop/ai/search-and-query/query/exact-match","title":"Exact match queries","url":"https://redis.io/docs/latest/develop/ai/search-and-query/query/exact-match/","summary":"Perform simple exact match queries","aliases":["/develop/interact/search-and-query/query/exact-match"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:33:04+01:00","page_type":"content","content_hash":"5d9201a30b146b8ea5defbe053f579b522cde7ed72564a684bed9e309fd12dd2","sections":[{"id":"numeric-field","title":"Numeric field","role":"content","text":"To perform an exact match query on a numeric field, you need to construct a range query with the same start and end value:\n\n[code example]\n\nAs described in the [article about range queries](https://redis.io/docs/latest/develop/ai/search-and-query/query/range), you can also use the `FILTER` argument:\n\n[code example]\n\nThe following examples show you how to query for bicycles with a price of exactly 270 USD:\n\nFoundational: Query numeric fields for exact matches using range syntax or equality operators when you need to find documents with specific numeric values\n\n**Difficulty:** Beginner\n\n**Commands:** FT.SEARCH\n\n**Complexity:**\n- FT.SEARCH: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]"},{"id":"tag-field","title":"Tag field","role":"content","text":"A tag is a short sequence of text, for example, \"new\" or \"Los Angeles\". \n\n> [!NOTE] Important\n> If you need to query for short texts, use a tag query instead of a full-text query. Tag fields are more space-efficient for storing index entries and often lead to lower query complexity for exact match queries.\n\nYou can construct a tag query for a single tag in the following way:\n\n[code example]\n\n> [!NOTE]\n> The curly brackets are mandatory for tag queries.\n\nThis short example shows you how to query for new bicycles:\n\nFoundational: Query tag fields for exact matches using curly bracket syntax when you need to find documents with specific categorical values\n\n**Difficulty:** Beginner\n\n**Commands:** FT.SEARCH\n\n**Complexity:**\n- FT.SEARCH: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nUse double quotes and [DIALECT 2](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/dialects#dialect-2) for exact match queries involving tags that contain special characters. As of v2.10, the only character that needs escaping in queries involving double-quoted tags is the double-quote character. Here's an example of using double-quoted tags that contain special characters:\n\nTag fields: Query tag fields with special characters using double-quoted syntax and DIALECT 2 when you need to match tags containing symbols or spaces\n\n**Difficulty:** Advanced\n\n**Commands:** FT.CREATE, JSON.SET, FT.SEARCH\n\n**Complexity:**\n- FT.CREATE: O(K)\n- JSON.SET: O(M+N)\n- FT.SEARCH: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]"},{"id":"full-text-field","title":"Full-text field","role":"content","text":"A detailed explanation of full-text queries is available in the [full-text queries documentation](https://redis.io/docs/latest/develop/ai/search-and-query/query/full-text). You can also query for an exact match of a phrase within a text field:\n\n[code example]\n\n> [!NOTE] Important\n> The phrase must be wrapped by escaped double quotes for an exact match query.\n>\n> You can't use a phrase that starts with a [stop word](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/stopwords).\n\nHere is an example for finding all bicycles that have a description containing the exact text 'rough terrain':\n\nText fields: Query text fields for exact phrase matches using escaped double quotes when you need to find documents containing specific multi-word phrases\n\n**Difficulty:** Intermediate\n\n**Commands:** FT.SEARCH\n\n**Complexity:**\n- FT.SEARCH: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]"}],"examples":[{"id":"numeric-field-ex0","language":"plaintext","code":"FT.SEARCH index \"@field:[value value]\"\n\nor\n\nFT.SEARCH index \"@field:[value]\" DIALECT 2 # requires v2.10\n\nor\n\nFT.SEARCH index \"@field==value\" DIALECT 2 # requires v2.10","section_id":"numeric-field"},{"id":"numeric-field-ex1","language":"plaintext","code":"FT.SEARCH index \"*\" FILTER field start end","section_id":"numeric-field"},{"id":"numeric-field-ex2","language":"plaintext","code":"> FT.SEARCH idx:bicycle \"@price:[270 270]\"\n1) (integer) 1\n2) \"bicycle:0\"\n3) 1) \"$\"\n   2) \"{\\\"pickup_zone\\\":\\\"POLYGON((-74.0610 40.7578, ...\n\n> FT.SEARCH idx:bicycle \"@price:[270]\" # requires v2.10\n1) (integer) 1\n2) \"bicycle:0\"\n3) 1) \"$\"\n   2) \"{\\\"pickup_zone\\\":\\\"POLYGON((-74.0610 40.7578, ...\n\n> FT.SEARCH idx:bicycle \"@price==270\" # requires v2.10\n1) (integer) 1\n2) \"bicycle:0\"\n3) 1) \"$\"\n   2) \"{\\\"pickup_zone\\\":\\\"POLYGON((-74.0610 40.7578, ...\n\n> FT.SEARCH idx:bicycle \"*\" FILTER price 270 270\n1) (integer) 1\n2) \"bicycle:0\"\n3) 1) \"$\"\n   2) \"{\\\"pickup_zone\\\":\\\"POLYGON((-74.0610 40.7578, ...","section_id":"numeric-field"},{"id":"numeric-field-ex3","language":"csharp","code":"SearchResult res1 = db.FT().Search(\n            \"idx:bicycle\",\n            new(\"@price:[270 270]\")\n        );\n        Console.WriteLine(res1.TotalResults); //    >>> 1\n\n        SearchResult res2 = db.FT().Search(\n            \"idx:bicycle\",\n            new Query().AddFilter(\n                new Query.NumericFilter(\"price\", 270, 270)\n            )\n        );\n        Console.WriteLine(res2.TotalResults); //    >>> 1","section_id":"numeric-field"},{"id":"numeric-field-ex4","language":"go","code":"res1, err := rdb.FTSearch(ctx,\n\t\t\"idx:bicycle\", \"@price:[270 270]\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1.Total) // >>> 1\n\n\tfor _, doc := range res1.Docs {\n\t\tfmt.Println(doc.ID)\n\t}\n\t// >>> bicycle:0\n\n\tres2, err := rdb.FTSearchWithArgs(ctx,\n\t\t\"idx:bicycle\",\n\t\t\"*\",\n\t\t&redis.FTSearchOptions{\n\t\t\tFilters: []redis.FTSearchFilter{\n\t\t\t\t{\n\t\t\t\t\tFieldName: \"price\",\n\t\t\t\t\tMin:       270,\n\t\t\t\t\tMax:       270,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2.Total) // >>> 1\n\n\tfor _, doc := range res2.Docs {\n\t\tfmt.Println(doc.ID)\n\t}\n\t// >>> bicycle:0","section_id":"numeric-field"},{"id":"numeric-field-ex5","language":"java","code":"CompletableFuture<SearchReply<String, String>> priceResults = asyncCommands.ftSearch(\"idx:bicycle\", \"@price:[270]\")\n\n                    .thenApply(res -> {\n\n                        res.getResults().forEach(doc -> {\n                            System.out.printf(\"ID: %s\\n\", doc.getId());\n                        });\n                        // >>> ID: bicycle:0\n\n                        return res;\n                    }).toCompletableFuture();","section_id":"numeric-field"},{"id":"numeric-field-ex6","language":"java","code":"Mono<SearchReply<String, String>> priceResults = reactiveCommands.ftSearch(\"idx:bicycle\", \"@price:[270]\")\n                    .doOnNext(res -> {\n                        res.getResults().forEach(doc -> {\n                            System.out.printf(\"ID: %s\\n\", doc.getId());\n                        });\n                        // >>> ID: bicycle:0\n                    });","section_id":"numeric-field"},{"id":"numeric-field-ex7","language":"java","code":"SearchResult res1 = jedis.ftSearch(\"idx:bicycle\", \"@price:[270 270]\");\n        System.out.println(res1.getTotalResults()); // >>> 1\n\n        List<Document> docs1 = res1.getDocuments();\n\n        for (int i = 0; i < docs1.size(); i++) {\n            System.out.println(docs1.get(i).getId());\n        }\n        // >>> bicycle:0\n\n        SearchResult res2 = jedis.ftSearch(\"idx:bicycle\",\n            \"*\",\n            FTSearchParams.searchParams()\n                    .filter(\"price\", 270, 270)\n        );\n        System.out.println(res2.getTotalResults()); // >>> 1\n\n        List<Document> docs2 = res2.getDocuments();\n\n        for (int i = 0; i < docs2.size(); i++) {\n            System.out.println(docs2.get(i).getId());\n        }\n        // >>> bicycle:0","section_id":"numeric-field"},{"id":"numeric-field-ex8","language":"javascript","code":"const res1 = await client.ft.search('idx:bicycle', '@price:[270 270]');\nconsole.log(res1.total); // >>> 1\n\ntry {\n    const res2 = await client.ft.search('idx:bicycle', '@price:[270]');\n    console.log(res2.total); // >>> 1\n    assert.strictEqual(res2.total, 1);\n} catch (err) {\n    console.log(\"'@price:[270]' syntax not yet supported.\");\n}\n\ntry {\n    const res3 = await client.ft.search('idx:bicycle', '@price==270');\n    console.log(res3.total); // >>> 1\n    assert.strictEqual(res3.total, 1);\n} catch (err) {\n    console.log(\"'@price==270' syntax not yet supported.\");\n}\n\n// FILTER is not supported\n// const res4 = await client.ft.search('idx:bicycle', '*', {\n//   FILTER: {\n//       field: 'price',\n//       min: 270,\n//       max: 270,\n//   }\n// });\n// console.log(res4.total); // >>> 1","section_id":"numeric-field"},{"id":"numeric-field-ex9","language":"python","code":"res = index.search(Query(\"@price:[270 270]\"))\nprint(res.total)\n# >>> 1\n\ntry:\n    res = index.search(Query(\"@price:[270]\")) # not yet supported in redis-py\n    print(res.total)\n    # >>> 1\n    assert res.total == 1\nexcept Exception:\n    print(\"'@price:[270]' syntax not yet supported.\")\n\ntry:\n    res = index.search(Query(\"@price==270\")) # not yet supported in redis-py\n    print(res.total)\n    # >>> 1\n    assert res.total == 1\nexcept Exception:\n    print(\"'@price==270' syntax not yet supported.\")\n\nquery = Query(\"*\").add_filter(NumericFilter(\"price\", 270, 270))\nres = index.search(query)\nprint(res.total)\n# >>> 1","section_id":"numeric-field"},{"id":"tag-field-ex0","language":"plaintext","code":"FT.SEARCH index \"@field:{tag}\"","section_id":"tag-field"},{"id":"tag-field-ex1","language":"plaintext","code":"> FT.SEARCH idx:bicycle \"@condition:{new}\"\n 1) (integer) 5\n 2) \"bicycle:0\"\n 3) 1) \"$\"\n    2) \"{\\\"pickup_zone\\\":\\\"POLYGON((-74.0610 40.7578, -73.9510 40.7578, -73.9510 40.6678, -74.0610 40.6678, -74.0610 40.7578))\\\",\\\"store_location\\\":\\\"-74.0060,40.7128\\\",\\\"brand\\\":\\\"Velorim\\\",\\\"model\\\":\\\"Jigger\\\",\\\"price\\\":270,\\\"description\\\":\\\"Small and powerful, the Jigger is the best ride for the smallest of tikes! This is the tiniest kids\\xe2\\x80\\x99 pedal bike on the market available without a coaster brake, the Jigger is the vehicle of choice for the rare tenacious little rider raring to go.\\\",\\\"condition\\\":\\\"new\\\"}\"\n 4) \"bicycle:5\"\n... output truncated for AI-facing Markdown ...\n    2) \"{\\\"pickup_zone\\\":\\\"POLYGON((1.9450 41.4301, 2.4018 41.4301, 2.4018 41.1987, 1.9450 41.1987, 1.9450 41.4301))\\\",\\\"store_location\\\":\\\"2.1734, 41.3851\\\",\\\"brand\\\":\\\"nHill\\\",\\\"model\\\":\\\"Summit\\\",\\\"price\\\":1200,\\\"description\\\":\\\"This budget mountain bike from nHill performs well both on bike paths and on the trail. The fork with 100mm of travel absorbs rough terrain. Fat Kenda Booster tires give you grip in corners and on wet trails. The Shimano Tourney drivetrain offered enough gears for finding a comfortable pace to ride uphill, and the Tektro hydraulic disc brakes break smoothly. Whether you want an affordable bike that you can take to work, but also take trail in mountains on the weekends or you\\xe2\\x80\\x99re just after a stable, comfortable ride for the bike path, the Summit gives a good value for money.\\\",\\\"condition\\\":\\\"new\\\"}\"","section_id":"tag-field"},{"id":"tag-field-ex2","language":"csharp","code":"SearchResult res3 = db.FT().Search(\n            \"idx:bicycle\",\n            new(\"@condition:{new}\")\n        );\n        Console.WriteLine(res3.TotalResults);   // >>> 4","section_id":"tag-field"},{"id":"tag-field-ex3","language":"go","code":"res3, err := rdb.FTSearch(ctx,\n\t\t\"idx:bicycle\", \"@condition:{new}\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3.Total) // >>> 5\n\n\tdocs := res3.Docs\n\tslices.SortFunc(docs, func(a, b redis.Document) int {\n\t\treturn strings.Compare(a.ID, b.ID)\n\t})\n\n\tfor _, doc := range docs {\n\t\tfmt.Println(doc.ID)\n\t}\n\t// >>> bicycle:0\n\t// >>> bicycle:5\n\t// >>> bicycle:6\n\t// >>> bicycle:7\n\t// >>> bicycle:8","section_id":"tag-field"},{"id":"tag-field-ex4","language":"java","code":"CompletableFuture<SearchReply<String, String>> conditionResults = asyncCommands\n                    .ftSearch(\"idx:bicycle\", \"@condition:{new}\").thenApply(res -> {\n                        res.getResults().stream().sorted((doc1, doc2) -> doc1.getId().compareTo(doc2.getId())).forEach(doc -> {\n                            System.out.printf(\"ID: %s\\n\", doc.getId());\n                        });\n                        // >>> ID: bicycle:0\n                        // >>> ID: bicycle:5\n                        // >>> ID: bicycle:6\n                        // >>> ID: bicycle:7\n                        // >>> ID: bicycle:8\n\n                        return res;\n                    }).toCompletableFuture();","section_id":"tag-field"},{"id":"tag-field-ex5","language":"java","code":"Mono<SearchReply<String, String>> conditionResults = reactiveCommands.ftSearch(\"idx:bicycle\", \"@condition:{new}\")\n                    .doOnNext(res -> {\n                        res.getResults().stream().sorted((doc1, doc2) -> doc1.getId().compareTo(doc2.getId())).forEach(doc -> {\n                            System.out.printf(\"ID: %s\\n\", doc.getId());\n                        });\n                        // >>> ID: bicycle:0\n                        // >>> ID: bicycle:5\n                        // >>> ID: bicycle:6\n                        // >>> ID: bicycle:7\n                        // >>> ID: bicycle:8\n                    });","section_id":"tag-field"},{"id":"tag-field-ex6","language":"java","code":"SearchResult res3 = jedis.ftSearch(\"idx:bicycle\", \"@condition:{new}\");\n        System.out.println(res3.getTotalResults()); // >>> 5\n\n        List<Document> docs3 = res3.getDocuments();\n\n        for (int i = 0; i < docs3.size(); i++) {\n            System.out.println(docs3.get(i).getId());\n        }\n        // >>> bicycle:5\n        // >>> bicycle:0\n        // >>> bicycle:6\n        // >>> bicycle:7\n        // >>> bicycle:8","section_id":"tag-field"},{"id":"tag-field-ex7","language":"javascript","code":"const res5 = await client.ft.search('idx:bicycle', '@condition:{new}');\nconsole.log(res5.total); // >>> 5","section_id":"tag-field"},{"id":"tag-field-ex8","language":"python","code":"res = index.search(Query(\"@condition:{new}\"))\nprint(res.total)\n# >>> 5","section_id":"tag-field"},{"id":"tag-field-ex9","language":"plaintext","code":"> FT.CREATE idx:email ON JSON PREFIX 1 key: SCHEMA $.email AS email TAG\nOK\n> JSON.SET key:1 $ '{\"email\": \"test@redis.com\"}'\nOK\n> FT.SEARCH idx:email '@email:{\"test@redis.com\"}' DIALECT 2\n1) (integer) 1\n2) \"key:1\"\n3) 1) \"$\"\n   2) \"{\\\"email\\\":\\\"test@redis.com\\\"}\"","section_id":"tag-field"},{"id":"tag-field-ex10","language":"csharp","code":"Schema emailSchema = new Schema()\n            .AddTagField(new FieldName(\"$.email\", \"email\"));\n\n        FTCreateParams emailParams = new FTCreateParams()\n            .AddPrefix(\"key:\")\n            .On(IndexDataType.JSON);\n\n        db.FT().Create(\"idx:email\", emailParams, emailSchema);\n\n        db.JSON().Set(\"key:1\", \"$\", \"{\\\"email\\\": \\\"test@redis.com\\\"}\");\n\n\n\n        SearchResult res4 = db.FT().Search(\n            \"idx:email\",\n            new Query(\"@email:{test\\\\@redis\\\\.com}\").Dialect(2)\n        );\n        Console.WriteLine(res4.TotalResults);   // >>> 1","section_id":"tag-field"},{"id":"tag-field-ex11","language":"go","code":"res4, err := rdb.FTCreate(ctx,\n\t\t\"idx:email\",\n\t\t&redis.FTCreateOptions{\n\t\t\tOnJSON: true,\n\t\t\tPrefix: []interface{}{\"key:\"},\n\t\t},\n\t\t&redis.FieldSchema{\n\t\t\tFieldName: \"$.email\",\n\t\t\tAs:        \"email\",\n\t\t\tFieldType: redis.SearchFieldTypeTag,\n\t\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4) // >>> OK\n\n\tres5, err := rdb.JSONSet(ctx, \"key:1\", \"$\",\n\t\tmap[string]interface{}{\n\t\t\t\"email\": \"test@redis.com\",\n\t\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res5) // >>> OK\n\n\n\tres6, err := rdb.FTSearch(ctx, \"idx:email\",\n\t\t\"@email:{test\\\\@redis\\\\.com}\",\n\t).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tfmt.Println(res6.Total) // >>> 1","section_id":"tag-field"},{"id":"tag-field-ex12","language":"java","code":"List<FieldArgs<String>> emailSchema = Arrays\n                    .asList(TagFieldArgs.<String> builder().name(\"$.email\").as(\"email\").build());\n            CreateArgs<String, String> emailCreateArgs = CreateArgs.<String, String> builder().on(CreateArgs.TargetType.JSON)\n                    .withPrefix(\"key:\").build();\n\n            CompletableFuture<SearchReply<String, String>> emailResults = asyncCommands\n                    .ftCreate(\"idx:email\", emailCreateArgs, emailSchema).thenCompose(res -> {\n                        System.out.println(res); // >>> OK\n                        return asyncCommands.jsonSet(\"key:1\", JsonPath.ROOT_PATH,\n                                parser.createJsonObject().put(\"email\", parser.createJsonValue(\"\\\"test@redis.com\\\"\")));\n                    }).thenCompose(res2 -> {\n                        System.out.println(res2); // >>> OK\n                        return asyncCommands.ftSearch(\"idx:email\", \"@email:{test\\\\@redis\\\\.com}\");\n                    }).thenApply(res3 -> {\n                        System.out.println(res3.getResults().size()); // >>> 1\n                        System.out.println(res3.getResults().get(0).getId());\n                        // >>> key:1\n                        return res3;\n                    }).toCompletableFuture();","section_id":"tag-field"},{"id":"tag-field-ex13","language":"java","code":"List<FieldArgs<String>> emailSchema = Arrays\n                    .asList(TagFieldArgs.<String> builder().name(\"$.email\").as(\"email\").build());\n\n            CreateArgs<String, String> emailCreateArgs = CreateArgs.<String, String> builder().on(CreateArgs.TargetType.JSON)\n                    .withPrefix(\"key:\").build();\n\n            Mono<Void> emailResults = reactiveCommands.ftCreate(\"idx:email\", emailCreateArgs, emailSchema).doOnNext(res -> {\n                System.out.println(res); // >>> OK\n            }).flatMap(res -> reactiveCommands.jsonSet(\"key:1\", JsonPath.ROOT_PATH,\n                    parser.createJsonObject().put(\"email\", parser.createJsonValue(\"\\\"test@redis.com\\\"\")))).doOnNext(res -> {\n                        System.out.println(res); // >>> OK\n                    }).flatMap(res -> reactiveCommands.ftSearch(\"idx:email\", \"@email:{test\\\\@redis\\\\.com}\")).doOnNext(res -> {\n                        System.out.println(res.getResults().size()); // >>> 1\n                        System.out.println(res.getResults().get(0).getId());\n                        // >>> key:1\n                    }).then();","section_id":"tag-field"},{"id":"tag-field-ex14","language":"java","code":"SchemaField[] emailSchema = {\n            TextField.of(\"$.email\").as(\"email\")\n        };\n\n        jedis.ftCreate(\"idx:email\", \n            new FTCreateParams()\n                    .addPrefix(\"key:\")\n                    .on(IndexDataType.JSON),\n            emailSchema\n        );\n\n        jedis.jsonSet(\"key:1\", Path2.ROOT_PATH, \"{\\\"email\\\": \\\"test@redis.com\\\"}\");\n        \n        SearchResult res4 = jedis.ftSearch(\"idx:email\",\n            RediSearchUtil.escapeQuery(\"@email{test@redis.com}\"),\n            new FTSearchParams().dialect(2)\n        );\n        System.out.println(res4.getTotalResults());","section_id":"tag-field"},{"id":"tag-field-ex15","language":"javascript","code":"await client.ft.create('idx:email', {\n  '$.email': {\n    type: SCHEMA_FIELD_TYPE.TAG,\n    AS: 'email'\n  }\n}, {\n  ON: 'JSON',\n  PREFIX: 'key:'\n})\n\nawait client.json.set('key:1', '$', { email: 'test@redis.com' });\n\ntry {\n    const res6 = await client.ft.search('idx:email', 'test@redis.com', { DIALECT: 2 });\n    console.log(res6);\n} catch (err) {\n    console.log(\"'test@redis.com' syntax not yet supported.\");\n}","section_id":"tag-field"},{"id":"tag-field-ex16","language":"python","code":"schema = (\n    TagField(\"$.email\", as_name=\"email\")\n)\n\nidx_email = r.ft(\"idx:email\")\nidx_email.create_index(\n    schema,\n    definition=IndexDefinition(prefix=[\"key:\"], index_type=IndexType.JSON),\n)\nr.json().set('key:1', Path.root_path(), '{\"email\": \"test@redis.com\"}')\n\ntry:\n    res = idx_email.search(Query(\"test@redis.com\").dialect(2))\n    print(res)\nexcept Exception:\n    print(\"'test@redis.com' syntax not yet supported.\")","section_id":"tag-field"},{"id":"full-text-field-ex0","language":"plaintext","code":"FT.SEARCH index \"@field:\\\"phrase\\\"\"","section_id":"full-text-field"},{"id":"full-text-field-ex1","language":"plaintext","code":"> FT.SEARCH idx:bicycle \"@description:\\\"rough terrain\\\"\"\n1) (integer) 1\n2) \"bicycle:8\"\n3) 1) \"$\"\n   2) \"{\\\"pickup_zone\\\":\\\"POLYGON((1.9450 41.4301, 2.4018 41.4301, 2.4018 41.1987, 1.9450 41.1987, 1.9450 41.4301))\\\",\\\"store_location\\\":\\\"2.1734, 41.3851\\\",\\\"brand\\\":\\\"nHill\\\",\\\"model\\\":\\\"Summit\\\",\\\"price\\\":1200,\\\"description\\\":\\\"This budget mountain bike from nHill performs well both on bike paths and on the trail. The fork with 100mm of travel absorbs rough terrain. Fat Kenda Booster tires give you grip in corners and on wet trails. The Shimano Tourney drivetrain offered enough gears for finding a comfortable pace to ride uphill, and the Tektro hydraulic disc brakes break smoothly. Whether you want an affordable bike that you can take to work, but also take trail in mountains on the weekends or you\\xe2\\x80\\x99re just after a stable, comfortable ride for the bike path, the Summit gives a good value for money.\\\",\\\"condition\\\":\\\"new\\\"}\"","section_id":"full-text-field"},{"id":"full-text-field-ex2","language":"csharp","code":"SearchResult res5 = db.FT().Search(\n            \"idx:bicycle\",\n            new(\"@description:\\\"rough terrain\\\"\")\n        );\n        Console.WriteLine(res5.TotalResults);   // >>> 1","section_id":"full-text-field"},{"id":"full-text-field-ex3","language":"go","code":"res7, err := rdb.FTSearch(ctx,\n\t\t\"idx:bicycle\", \"@description:\\\"rough terrain\\\"\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res7.Total) // >>> 1\n\n\tfor _, doc := range res7.Docs {\n\t\tfmt.Println(doc.ID)\n\t}\n\t// >>> bicycle:8","section_id":"full-text-field"},{"id":"full-text-field-ex4","language":"java","code":"CompletableFuture<SearchReply<String, String>> textMatchResults = asyncCommands\n                    .ftSearch(\"idx:bicycle\", \"@description:\\\"rough terrain\\\"\").thenApply(res -> {\n                        res.getResults().forEach(doc -> {\n                            System.out.printf(\"ID: %s\\n\", doc.getId());\n                        });\n                        // >>> ID: bicycle:8\n\n                        return res;\n                    }).toCompletableFuture();","section_id":"full-text-field"},{"id":"full-text-field-ex5","language":"java","code":"Mono<SearchReply<String, String>> textMatchResults = reactiveCommands\n                    .ftSearch(\"idx:bicycle\", \"@description:\\\"rough terrain\\\"\").doOnNext(res -> {\n                        res.getResults().forEach(doc -> {\n                            System.out.printf(\"ID: %s\\n\", doc.getId());\n                        });\n                        // >>> ID: bicycle:8\n                    });","section_id":"full-text-field"},{"id":"full-text-field-ex6","language":"java","code":"SearchResult res5 = jedis.ftSearch(\"idx:bicycle\",\n            \"@description:\\\"rough terrain\\\"\"\n        );\n        System.out.println(res5.getTotalResults()); // >>> 1\n\n        List<Document> docs5 = res5.getDocuments();\n\n        for (int i = 0; i < docs5.size(); i++) {\n            System.out.println(docs5.get(i).getId());\n        }\n        // >>> bicycle:8","section_id":"full-text-field"},{"id":"full-text-field-ex7","language":"javascript","code":"const res7 = await client.ft.search('idx:bicycle', '@description:\"rough terrain\"');\nconsole.log(res7.total); // >>> 1 (Result{1 total, docs: [Document {'id': 'bicycle:8'...)","section_id":"full-text-field"},{"id":"full-text-field-ex8","language":"python","code":"res = index.search(Query(\"@description:\\\"rough terrain\\\"\"))\nprint(res.total)\n# >>> 1 (Result{1 total, docs: [Document {'id': 'bicycle:8'...)","section_id":"full-text-field"}]}
{"schema_version":2,"id":"develop/ai/search-and-query/query/full-text","title":"Full-text search","url":"https://redis.io/docs/latest/develop/ai/search-and-query/query/full-text/","summary":"Perform a full-text search","aliases":["/develop/interact/search-and-query/query/full-text","/interact/search-and-query/query/full-text/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:33:04+01:00","page_type":"content","content_hash":"fb5de03557893a9e11767e5af95d1b5811666a81a215719e08980b1b3c762252","sections":[{"id":"single-word","title":"Single word","role":"content","text":"To search for a word (or word stem) across all text fields, you can construct the following simple query:\n\n[code example]\n\nInstead of searching across all text fields, you might want to limit the search to a specific text field.\n\n[code example]\n\nWords that occur very often in natural language, such as `the` or `a` for the English language, aren't indexed and will not return a search result. You can find further details in the [stop words article](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/stopwords).\n\nThe following example searches for all bicycles that have the word 'kids' in the description:\n\nFoundational: Search for single words across text fields using simple word queries when you need to find documents containing specific terms\n\n**Difficulty:** Beginner\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]"},{"id":"phrase","title":"Phrase","role":"content","text":"A phrase is a sentence, sentence fragment, or small group of words. You can find further details about how to find exact phrases in the [exact match article](https://redis.io/docs/latest/develop/ai/search-and-query/query/exact-match)."},{"id":"word-prefix","title":"Word prefix","role":"content","text":"You can also search for words that match a given prefix.\n\n[code example]\n\n[code example]\n\n> [!NOTE] Important\n> The prefix needs to be at least two characters long.\n\nHere is an example that shows you how to search for bicycles with a brand that starts with 'ka':\n\nPrefix matching: Search for words with a specific prefix using wildcard syntax when you need to find documents with words starting with certain characters\n\n**Difficulty:** Intermediate\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]"},{"id":"word-suffix","title":"Word suffix","role":"content","text":"Similar to the prefix, it is also possible to search for words that share the same suffix.\n\n[code example]\n\nYou can also combine prefix- and suffix-based searches within a query expression.\n\n[code example]\n\nHere is an example that finds all brands that end with 'bikes':\n\nSuffix/infix matching: Search for words with a specific suffix or infix using wildcard syntax when you need to find documents with words ending or containing certain characters\n\n**Difficulty:** Intermediate\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]"},{"id":"fuzzy-search","title":"Fuzzy search","role":"content","text":"A fuzzy search allows you to find documents with words that approximately match your search term. To perform a fuzzy search, you wrap search terms with pairs of `%` characters. A single pair represents a (Levenshtein) distance of one, two pairs represent a distance of two, and three pairs, the maximum distance, represents a distance of three.\n\nHere is the command that searches across all text fields with a distance of one:\n\n[code example]\n\nThe following example finds all documents that contain a word that has a distance of one to the incorrectly spelled word 'optamized'. You can see that this matches the word 'optimized'.\n\nFuzzy search: Search for words with approximate matches using fuzzy search with single % delimiters when you need to find documents despite spelling variations or typos\n\n**Difficulty:** Intermediate\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nIf you want to increase the maximum word distance to two, you can use the following query:\n\nFuzzy search: Search for words with larger approximate matches using fuzzy search with double %% delimiters when you need to tolerate greater spelling variations\n\n**Difficulty:** Intermediate\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]"},{"id":"fuzzy-search-on-a-specific-attribute","title":"Fuzzy search on a specific attribute","role":"content","text":"To perform a fuzzy search on a specific text field, use the `@field:(...)` syntax with the fuzzy term inside parentheses.\n\n[code example]\n\n> [!NOTE] Important\n> Do not use quotes around the fuzzy term when targeting a specific field. Wrapping the term in quotes (for example, `@field:(\"%word%\")`) converts the query into an exact match search, and the `%` characters are treated as literal characters rather than fuzzy operators.\n\nThe following example shows the correct way to perform a fuzzy search with distance one on the `description` field:\n\n[code example]\n\nFor a fuzzy search with distance two on a specific field:\n\n[code example]\n\nYou can also combine fuzzy searches on multiple fields using boolean operators:\n\n[code example]"},{"id":"unicode-considerations","title":"Unicode considerations","role":"content","text":"Redis Search only supports Unicode characters in the [basic multilingual plane](https://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane); U+0000 to U+FFFF. Unicode characters beyond U+FFFF, such as Emojis, are not supported and would not be retrieved by queries including such characters in the following use cases:\n\n* Querying TEXT fields with Prefix/Suffix/Infix\n* Querying TEXT fields with fuzzy\n\nExamples:\n\n[code example]"}],"examples":[{"id":"single-word-ex0","language":"plaintext","code":"FT.SEARCH index \"word\"","section_id":"single-word"},{"id":"single-word-ex1","language":"plaintext","code":"FT.SEARCH index \"@field: word\"","section_id":"single-word"},{"id":"single-word-ex2","language":"plaintext","code":"FT.SEARCH idx:bicycle \"@description: kids\"","section_id":"single-word"},{"id":"single-word-ex3","language":"csharp","code":"SearchResult res1 = db.FT().Search(\n           \"idx:bicycle\",\n           new(\"@description: kids\")\n       );\n        Console.WriteLine(res1);    // >>> 2","section_id":"single-word"},{"id":"single-word-ex4","language":"go","code":"res1, err := rdb.FTSearch(ctx,\n\t\t\"idx:bicycle\", \"@description: kids\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1.Total) // >>> 2\n\n\tsort.Slice(res1.Docs, func(i, j int) bool {\n\t\treturn res1.Docs[i].ID < res1.Docs[j].ID\n\t})\n\n\tfor _, doc := range res1.Docs {\n\t\tfmt.Println(doc.ID)\n\t}\n\t// >>> bicycle:1\n\t// >>> bicycle:2","section_id":"single-word"},{"id":"single-word-ex5","language":"java","code":"CompletableFuture<SearchReply<String, String>> descriptionResults = asyncCommands\n                    .ftSearch(\"idx:bicycle\", \"@description: kids\").thenApply(res -> {\n                        res.getResults().stream().sorted((doc1, doc2) -> doc1.getId().compareTo(doc2.getId())).forEach(doc -> {\n                            System.out.printf(\"ID: %s\\n\", doc.getId());\n                        });\n                        // >>> ID: bicycle:1\n                        // >>> ID: bicycle:2\n                        return res;\n                    }).toCompletableFuture();","section_id":"single-word"},{"id":"single-word-ex6","language":"java","code":"Mono<SearchReply<String, String>> descriptionResults = reactiveCommands\n                    .ftSearch(\"idx:bicycle\", \"@description: kids\").doOnNext(res -> {\n                        res.getResults().stream().sorted((doc1, doc2) -> doc1.getId().compareTo(doc2.getId())).forEach(doc -> {\n                            System.out.printf(\"ID: %s\\n\", doc.getId());\n                            // >>> ID: bicycle:1\n                            // >>> ID: bicycle:2\n                        });\n                    });","section_id":"single-word"},{"id":"single-word-ex7","language":"java","code":"SearchResult res1 = jedis.ftSearch(\"idx:bicycle\", \"@description: kids\");\n        System.out.println(res1.getTotalResults()); // >>> 2\n\n        List<Document> docs1 = res1.getDocuments();\n\n        for (int i = 0; i < docs1.size(); i++) {\n            System.out.println(docs1.get(i).getId());\n        }\n        // >>> bicycle:2\n        // >>> bicycle:1","section_id":"single-word"},{"id":"single-word-ex8","language":"javascript","code":"const res1 = await client.ft.search('idx:bicycle', '@description: kids');\nconsole.log(res1.total); // >>> 2","section_id":"single-word"},{"id":"single-word-ex9","language":"python","code":"res = index.search(Query(\"@description: kids\"))\nprint(res.total)\n# >>> 2","section_id":"single-word"},{"id":"word-prefix-ex0","language":"plaintext","code":"FT.SEARCH index \"prefix*\"","section_id":"word-prefix"},{"id":"word-prefix-ex1","language":"plaintext","code":"FT.SEARCH index \"@field: prefix*\"","section_id":"word-prefix"},{"id":"word-prefix-ex2","language":"plaintext","code":"FT.SEARCH idx:bicycle \"@model: ka*\"","section_id":"word-prefix"},{"id":"word-prefix-ex3","language":"csharp","code":"SearchResult res2 = db.FT().Search(\n            \"idx:bicycle\",\n            new(\"@model: ka*\")\n        );\n        Console.WriteLine(res2.TotalResults);   // >>> 1","section_id":"word-prefix"},{"id":"word-prefix-ex4","language":"go","code":"res2, err := rdb.FTSearch(ctx,\n\t\t\"idx:bicycle\", \"@model: ka*\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2.Total) // >>> 1\n\n\tfor _, doc := range res2.Docs {\n\t\tfmt.Println(doc.ID)\n\t}\n\t// >>> bicycle:4","section_id":"word-prefix"},{"id":"word-prefix-ex5","language":"java","code":"CompletableFuture<SearchReply<String, String>> startsWithResults = asyncCommands\n                    .ftSearch(\"idx:bicycle\", \"@model: ka*\").thenApply(res -> {\n                        res.getResults().stream().sorted((doc1, doc2) -> doc1.getId().compareTo(doc2.getId())).forEach(doc -> {\n                            System.out.printf(\"ID: %s\\n\", doc.getId());\n                        });\n                        // >>> ID: bicycle:4\n                        return res;\n                    }).toCompletableFuture();","section_id":"word-prefix"},{"id":"word-prefix-ex6","language":"java","code":"Mono<SearchReply<String, String>> startsWithResults = reactiveCommands.ftSearch(\"idx:bicycle\", \"@model: ka*\")\n                    .doOnNext(res -> {\n                        res.getResults().stream().sorted((doc1, doc2) -> doc1.getId().compareTo(doc2.getId())).forEach(doc -> {\n                            System.out.printf(\"ID: %s\\n\", doc.getId());\n                            // >>> ID: bicycle:4\n                        });\n                    });","section_id":"word-prefix"},{"id":"word-prefix-ex7","language":"java","code":"SearchResult res2 = jedis.ftSearch(\"idx:bicycle\", \"@model: ka*\");\n        System.out.println(res2.getTotalResults()); // >>> 1\n\n        List<Document> docs2 = res2.getDocuments();\n\n        for (int i = 0; i < docs2.size(); i++) {\n            System.out.println(docs2.get(i).getId());\n        }\n        // >>> bicycle:4","section_id":"word-prefix"},{"id":"word-prefix-ex8","language":"javascript","code":"const res2 = await client.ft.search('idx:bicycle', '@model: ka*');\nconsole.log(res2.total); // >>> 1","section_id":"word-prefix"},{"id":"word-prefix-ex9","language":"python","code":"res = index.search(Query(\"@model: ka*\"))\nprint(res.total)\n# >>> 1","section_id":"word-prefix"},{"id":"word-suffix-ex0","language":"plaintext","code":"FT.SEARCH index \"*suffix\"","section_id":"word-suffix"},{"id":"word-suffix-ex1","language":"plaintext","code":"FT.SEARCH index \"*infix*\"","section_id":"word-suffix"},{"id":"word-suffix-ex2","language":"plaintext","code":"FT.SEARCH idx:bicycle \"@brand:*bikes\"","section_id":"word-suffix"},{"id":"word-suffix-ex3","language":"csharp","code":"SearchResult res3 = db.FT().Search(\n            \"idx:bicycle\",\n            new(\"@brand: *bikes\")\n        );\n        Console.WriteLine(res3.TotalResults);   // >>> 2","section_id":"word-suffix"},{"id":"word-suffix-ex4","language":"go","code":"res3, err := rdb.FTSearch(ctx,\n\t\t\"idx:bicycle\", \"@brand: *bikes\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3.Total) // >>> 2\n\n\tsort.Slice(res3.Docs, func(i, j int) bool {\n\t\treturn res3.Docs[i].ID < res3.Docs[j].ID\n\t})\n\tfor _, doc := range res3.Docs {\n\t\tfmt.Println(doc.ID)\n\t}\n\t// >>> bicycle:4\n\t// >>> bicycle:6","section_id":"word-suffix"},{"id":"word-suffix-ex5","language":"java","code":"CompletableFuture<SearchReply<String, String>> endsWithResults = asyncCommands\n                    .ftSearch(\"idx:bicycle\", \"@brand: *bikes\").thenApply(res -> {\n                        res.getResults().stream().sorted((doc1, doc2) -> doc1.getId().compareTo(doc2.getId())).forEach(doc -> {\n                            System.out.printf(\"ID: %s\\n\", doc.getId());\n                        });\n                        // >>> ID: bicycle:4\n                        // >>> ID: bicycle:6\n                        return res;\n                    }).toCompletableFuture();","section_id":"word-suffix"},{"id":"word-suffix-ex6","language":"java","code":"Mono<SearchReply<String, String>> endsWithResults = reactiveCommands.ftSearch(\"idx:bicycle\", \"@brand: *bikes\")\n                    .doOnNext(res -> {\n                        res.getResults().stream().sorted((doc1, doc2) -> doc1.getId().compareTo(doc2.getId())).forEach(doc -> {\n                            System.out.printf(\"ID: %s\\n\", doc.getId());\n                            // >>> ID: bicycle:4\n                            // >>> ID: bicycle:6\n                        });\n                    });","section_id":"word-suffix"},{"id":"word-suffix-ex7","language":"java","code":"SearchResult res3 = jedis.ftSearch(\"idx:bicycle\", \"@brand: *bikes\");\n        System.out.println(res3.getTotalResults()); // >>> 2\n\n        List<Document> docs3 = res3.getDocuments();\n\n        for (int i = 0; i < docs3.size(); i++) {\n            System.out.println(docs3.get(i).getId());\n        }\n        // >>> bicycle:6\n        // >>> bicycle:4","section_id":"word-suffix"},{"id":"word-suffix-ex8","language":"javascript","code":"const res3 = await client.ft.search('idx:bicycle', '@brand: *bikes');\nconsole.log(res3.total); // >>> 2","section_id":"word-suffix"},{"id":"word-suffix-ex9","language":"python","code":"res = index.search(Query(\"@brand: *bikes\"))\nprint(res.total)\n# >>> 2","section_id":"word-suffix"},{"id":"fuzzy-search-ex0","language":"plaintext","code":"FT.SEARCH index \"%word%\"","section_id":"fuzzy-search"},{"id":"fuzzy-search-ex1","language":"plaintext","code":"FT.SEARCH idx:bicycle \"%optamized%\"","section_id":"fuzzy-search"},{"id":"fuzzy-search-ex2","language":"csharp","code":"SearchResult res4 = db.FT().Search(\n            \"idx:bicycle\",\n            new(\"%optamized%\")\n        );\n        Console.WriteLine(res4.TotalResults);   // >>> 1","section_id":"fuzzy-search"},{"id":"fuzzy-search-ex3","language":"go","code":"res4, err := rdb.FTSearch(ctx,\n\t\t\"idx:bicycle\", \"%optamized%\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4.Total) // >>> 1\n\n\tfor _, doc := range res4.Docs {\n\t\tfmt.Println(doc.ID)\n\t}\n\t// >>> bicycle:3","section_id":"fuzzy-search"},{"id":"fuzzy-search-ex4","language":"java","code":"CompletableFuture<SearchReply<String, String>> fuzzyResults = asyncCommands.ftSearch(\"idx:bicycle\", \"%optamized%\")\n                    .thenApply(res -> {\n                        res.getResults().stream().sorted((doc1, doc2) -> doc1.getId().compareTo(doc2.getId())).forEach(doc -> {\n                            System.out.printf(\"ID: %s\\n\", doc.getId());\n                        });\n                        // >>> ID: bicycle:3\n                        return res;\n                    }).toCompletableFuture();","section_id":"fuzzy-search"},{"id":"fuzzy-search-ex5","language":"java","code":"Mono<SearchReply<String, String>> fuzzyResults = reactiveCommands.ftSearch(\"idx:bicycle\", \"%optamized%\")\n                    .doOnNext(res -> {\n                        res.getResults().stream().sorted((doc1, doc2) -> doc1.getId().compareTo(doc2.getId())).forEach(doc -> {\n                            System.out.printf(\"ID: %s\\n\", doc.getId());\n                            // >>> ID: bicycle:3\n                        });\n                    });","section_id":"fuzzy-search"},{"id":"fuzzy-search-ex6","language":"java","code":"SearchResult res4 = jedis.ftSearch(\"idx:bicycle\", \"%optamized%\");\n        System.out.println(res4.getTotalResults()); // >>> 1\n\n        List<Document> docs4 = res4.getDocuments();\n\n        for (int i = 0; i < docs4.size(); i++) {\n            System.out.println(docs4.get(i).getId());\n        }\n        // >>> bicycle:3","section_id":"fuzzy-search"},{"id":"fuzzy-search-ex7","language":"javascript","code":"const res4 = await client.ft.search('idx:bicycle', '%optamized%');\nconsole.log(res4); // >>> { total: 1, documents: [ { id: 'bicycle:3', value: [Object: null prototype] } ]}","section_id":"fuzzy-search"},{"id":"fuzzy-search-ex8","language":"python","code":"res = index.search(Query(\"%optamized%\"))\nprint(res)\n# >>> Result{1 total, docs: [Document {'id': 'bicycle:3', 'payload': None, 'json': '{\"pickup_zone\":\"POLYGON((-80.2433 25.8067, -80.1333 25.8067, -80.1333 25.6967, -80.2433 25.6967, -80.2433 25.8067))\",\"store_location\":\"-80.1918,25.7617\",\"brand\":\"Eva\",\"model\":\"Eva 291\",\"price\":3400,\"description\":\"The sister company to Nord, Eva launched in 2005 as the first and only women-dedicated bicycle brand. Designed by women for women, allEva bikes are optimized for the feminine physique using analytics from a body metrics database. If you like 29ers, try the Eva 291. It’s a brand new bike for 2022.. This full-suspension, cross-country ride has been designed for velocity. The 291 has 100mm of front and rear travel, a superlight aluminum frame and fast-rolling 29-inch wheels. Yippee!\",\"condition\":\"used\"}'}]}","section_id":"fuzzy-search"},{"id":"fuzzy-search-ex9","language":"plaintext","code":"FT.SEARCH idx:bicycle \"%%optamised%%\"","section_id":"fuzzy-search"},{"id":"fuzzy-search-ex10","language":"csharp","code":"SearchResult res5 = db.FT().Search(\n            \"idx:bicycle\",\n            new(\"%%optamised%%\")\n        );\n        Console.WriteLine(res5.TotalResults);   // >>> 1","section_id":"fuzzy-search"},{"id":"fuzzy-search-ex11","language":"go","code":"res5, err := rdb.FTSearch(ctx,\n\t\t\"idx:bicycle\", \"%%optamised%%\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res5.Total) // >>> 1\n\n\tfor _, doc := range res5.Docs {\n\t\tfmt.Println(doc.ID)\n\t}\n\t// >>> bicycle:3","section_id":"fuzzy-search"},{"id":"fuzzy-search-ex12","language":"java","code":"CompletableFuture<SearchReply<String, String>> fuzzierResults = asyncCommands\n                    .ftSearch(\"idx:bicycle\", \"%%optamised%%\").thenApply(res -> {\n                        res.getResults().stream().sorted((doc1, doc2) -> doc1.getId().compareTo(doc2.getId())).forEach(doc -> {\n                            System.out.printf(\"ID: %s\\n\", doc.getId());\n                        });\n                        // >>> ID: bicycle:3\n                        return res;\n                    }).toCompletableFuture();","section_id":"fuzzy-search"},{"id":"fuzzy-search-ex13","language":"java","code":"Mono<SearchReply<String, String>> fuzzierResults = reactiveCommands.ftSearch(\"idx:bicycle\", \"%%optamised%%\")\n                    .doOnNext(res -> {\n                        res.getResults().stream().sorted((doc1, doc2) -> doc1.getId().compareTo(doc2.getId())).forEach(doc -> {\n                            System.out.printf(\"ID: %s\\n\", doc.getId());\n                            // >>> ID: bicycle:3\n                        });\n                    });","section_id":"fuzzy-search"},{"id":"fuzzy-search-ex14","language":"java","code":"SearchResult res5 = jedis.ftSearch(\"idx:bicycle\", \"%%optamised%%\");\n        System.out.println(res5.getTotalResults()); // >>> 1\n\n        List<Document> docs5 = res5.getDocuments();\n\n        for (int i = 0; i < docs5.size(); i++) {\n            System.out.println(docs5.get(i).getId());\n        }\n        // >>> bicycle:3","section_id":"fuzzy-search"},{"id":"fuzzy-search-ex15","language":"javascript","code":"const res5 = await client.ft.search('idx:bicycle', '%%optamised%%');\nconsole.log(res5); // >>> { total: 1, documents: [ { id: 'bicycle:3', value: [Object: null prototype] } ]}","section_id":"fuzzy-search"},{"id":"fuzzy-search-ex16","language":"python","code":"res = index.search(Query(\"%%optamised%%\"))\nprint(res)\n# >>> Result{1 total, docs: [Document {'id': 'bicycle:3', 'payload': None, 'json': '{\"pickup_zone\":\"POLYGON((-80.2433 25.8067, -80.1333 25.8067, -80.1333 25.6967, -80.2433 25.6967, -80.2433 25.8067))\",\"store_location\":\"-80.1918,25.7617\",\"brand\":\"Eva\",\"model\":\"Eva 291\",\"price\":3400,\"description\":\"The sister company to Nord, Eva launched in 2005 as the first and only women-dedicated bicycle brand. Designed by women for women, allEva bikes are optimized for the feminine physique using analytics from a body metrics database. If you like 29ers, try the Eva 291. It’s a brand new bike for 2022.. This full-suspension, cross-country ride has been designed for velocity. The 291 has 100mm of front and rear travel, a superlight aluminum frame and fast-rolling 29-inch wheels. Yippee!\",\"condition\":\"used\"}'}]}","section_id":"fuzzy-search"},{"id":"fuzzy-search-on-a-specific-attribute-ex0","language":"plaintext","code":"FT.SEARCH index \"@field:(%word%)\"","section_id":"fuzzy-search-on-a-specific-attribute"},{"id":"fuzzy-search-on-a-specific-attribute-ex1","language":"plaintext","code":"FT.SEARCH idx:bicycle \"@description:(%optamized%)\"","section_id":"fuzzy-search-on-a-specific-attribute"},{"id":"fuzzy-search-on-a-specific-attribute-ex2","language":"plaintext","code":"FT.SEARCH idx:bicycle \"@description:(%%optamised%%)\"","section_id":"fuzzy-search-on-a-specific-attribute"},{"id":"fuzzy-search-on-a-specific-attribute-ex3","language":"plaintext","code":"FT.SEARCH idx:bicycle \"@description:(%optamized%) | @model:(%jigar%)\"","section_id":"fuzzy-search-on-a-specific-attribute"},{"id":"unicode-considerations-ex0","language":"plaintext","code":"redis> FT.CREATE idx SCHEMA tag TAG text TEXT\nOK\nredis> HSET doc:1 tag '😀😁🙂' text '😀😁🙂'\n(integer) 2\nredis> HSET doc:2 tag '😀😁🙂abc' text '😀😁🙂abc'\n(integer) 2\nredis> FT.SEARCH idx '@text:(*😀😁🙂)' NOCONTENT\n1) (integer) 0\nredis> FT.SEARCH idx '@text:(*😀😁🙂*)' NOCONTENT\n1) (integer) 0\nredis> FT.SEARCH idx '@text:(😀😁🙂*)' NOCONTENT\n1) (integer) 0\n\nredis> FT.SEARCH idx '@text:(%😀😁🙃%)' NOCONTENT\n1) (integer) 0","section_id":"unicode-considerations"}]}
{"schema_version":2,"id":"develop/ai/search-and-query/query/geo-spatial","title":"Geospatial queries","url":"https://redis.io/docs/latest/develop/ai/search-and-query/query/geo-spatial/","summary":"Query based on geographic data","aliases":["/develop/interact/search-and-query/query/geo-spatial"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:33:04+01:00","page_type":"content","content_hash":"1576e0fe2560229e152d8fc67ad30c89863f3332196c0ac59357e7f5b7f88c5e","sections":[{"id":"radius","title":"Radius","role":"content","text":"You can construct a radius query by passing the center coordinates (longitude, latitude), the radius, and the distance unit to the [FT.SEARCH](https://redis.io/docs/latest/commands/ft.search) command.\n\n[code example]\n\nAllowed units are `m`, `km`, `mi`, and `ft`.\n\nThe following query finds all bicycle stores within a radius of 20 miles around London:\n\nFoundational: Query geographic locations within a radius using center coordinates and distance when you need to find nearby points of interest\n\n**Difficulty:** Beginner\n\n**Available in:** Redis CLI, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]"},{"id":"shape","title":"Shape","role":"content","text":"The only supported shapes are points and polygons. You can query for polygons or points that either contain or are within a given geometric shape.\n\n[code example]\n\nHere is a more detailed explanation of this query:\n\n1. **Field name**: you need to replace `geo_shape_field` with the `GEOSHAPE` field's name on which you want to query.\n2. **Spatial operator**: spatial operators define the relationship between the shapes in the database and the shape you are searching for. You can either use `WITHIN`, `CONTAINS`, `INTERSECTS`, or `DISJOINT`. `WITHIN` finds any shape in the database that is inside the given shape. `CONTAINS` queries for any shape that surrounds the given shape. `INTERSECTS` finds any shape that has coordinates in common with the provided shape. `DISJOINT` finds any shapes that have nothing in common with the provided shape. `INTERSECTS` and `DISJOINT` were introduced in v2.10.\n3. **Parameter**: the query refers to a parameter named `shape`. You can use any parameter name here. You need to use the `PARAMS` clause to set the parameter value. The value follows the [well-known text representation of a geometry](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry). Supported types are `POINT(x y)` and `POLYGON((x1 y1, x2 y2, ...))`.\n4. **Dialect**: Shape-based queries are not supported in `DIALECT 1`.\n\nThe following example query verifies if a bicycle is within a pickup zone:\n\nSpatial operators: Query geometric shapes using CONTAINS to test which shapes enclose another shape when you need to find bounding regions\n\n**Difficulty:** Intermediate\n\n**Available in:** Redis CLI, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nIf you want to find all pickup zones that are approximately within Europe, then you can use the following query:\n\nSpatial operators: Query geometric shapes using the WITHIN operator to find shapes contained in a region when you need to identify items within geographic boundaries\n\n**Difficulty:** Intermediate\n\n**Available in:** Redis CLI, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]"}],"examples":[{"id":"radius-ex0","language":"plaintext","code":"FT.SEARCH index \"@geo_field:[lon lat radius unit]\"","section_id":"radius"},{"id":"radius-ex1","language":"plaintext","code":"FT.SEARCH idx:bicycle \"@store_location:[-0.1778 51.5524 20 mi]\"","section_id":"radius"},{"id":"radius-ex2","language":"go","code":"res1, err := rdb.FTSearchWithArgs(ctx,\n\t\t\"idx:bicycle\", \"@store_location:[$lon $lat $radius $units]\",\n\t\t&redis.FTSearchOptions{\n\t\t\tParams: map[string]interface{}{\n\t\t\t\t\"lon\":    -0.1778,\n\t\t\t\t\"lat\":    51.5524,\n\t\t\t\t\"radius\": 20,\n\t\t\t\t\"units\":  \"mi\",\n\t\t\t},\n\t\t\tDialectVersion: 2,\n\t\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1.Total) // >>> 1\n\n\tfor _, doc := range res1.Docs {\n\t\tfmt.Println(doc.ID)\n\t}\n\t// >>> bicycle:5","section_id":"radius"},{"id":"radius-ex3","language":"java","code":"SearchResult res1 = jedis.ftSearch(\"idx:bicycle\",\n            \"@store_location:[$lon $lat $radius $units]\",\n            FTSearchParams.searchParams()\n                    .addParam(\"lon\", -0.1778)\n                    .addParam(\"lat\", 51.5524)\n                    .addParam(\"radius\", 20)\n                    .addParam(\"units\", \"mi\")\n                    .dialect(2)\n        );\n        System.out.println(res1.getTotalResults()); // >>> 1\n\n        List<Document> docs1 = res1.getDocuments();\n\n        for (Document document : docs1) {\n            System.out.println(document.getId());\n        }\n        // >>> bicycle:5","section_id":"radius"},{"id":"radius-ex4","language":"javascript","code":"const res1= await client.ft.search('idx:bicycle', '@store_location:[-0.1778 51.5524 20 mi]');\nconsole.log(res1.total); // >>> 1\nconsole.log(res1); // >>> {total: 1, documents: [ { id: 'bicycle:5', value: [Object: null prototype] } ]}","section_id":"radius"},{"id":"radius-ex5","language":"python","code":"params_dict = {\"lon\": -0.1778, \"lat\": 51.5524, \"radius\": 20, \"units\": \"mi\"}\nq = Query(\"@store_location:[$lon $lat $radius $units]\").dialect(2)\nres = index.search(q, query_params=params_dict)\nprint(res)\n# >>> Result{1 total, docs: [Document {'id': 'bicycle:5', ...","section_id":"radius"},{"id":"shape-ex0","language":"plaintext","code":"FT.SEARCH index \"@geo_shape_field:[{WITHIN|CONTAINS|INTERSECTS|DISJOINT} $shape] PARAMS 2 shape \"shape_as_wkt\" DIALECT 3","section_id":"shape"},{"id":"shape-ex1","language":"plaintext","code":"FT.SEARCH idx:bicycle \"@pickup_zone:[CONTAINS $bike]\" PARAMS 2 bike \"POINT(-0.1278 51.5074)\" DIALECT 2","section_id":"shape"},{"id":"shape-ex2","language":"go","code":"res2, err := rdb.FTSearchWithArgs(ctx,\n\t\t\"idx:bicycle\",\n\t\t\"@pickup_zone:[CONTAINS $bike]\",\n\t\t&redis.FTSearchOptions{\n\t\t\tParams: map[string]interface{}{\n\t\t\t\t\"bike\": \"POINT(-0.1278 51.5074)\",\n\t\t\t},\n\t\t\tDialectVersion: 3,\n\t\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2.Total) // >>> 1\n\n\tfor _, doc := range res2.Docs {\n\t\tfmt.Println(doc.ID)\n\t}\n\t// >>> bicycle:5","section_id":"shape"},{"id":"shape-ex3","language":"java","code":"SearchResult res2 = jedis.ftSearch(\"idx:bicycle\",\n            \"@pickup_zone:[CONTAINS $bike]\",\n            FTSearchParams.searchParams()\n                    .addParam(\"bike\", \"POINT(-0.1278 51.5074)\")\n                    .dialect(3)\n        );\n        System.out.println(res2.getTotalResults());   // >>> 1\n\n        List<Document> docs2 = res2.getDocuments();\n\n        for (Document document : docs2) {\n            System.out.println(document.getId());\n        }\n        // >>> bicycle:5","section_id":"shape"},{"id":"shape-ex4","language":"javascript","code":"const params_dict_geo2 = { bike: 'POINT(-0.1278 51.5074)' };\nconst q_geo2 = '@pickup_zone:[CONTAINS $bike]';\nconst res2 = await client.ft.search('idx:bicycle', q_geo2, { PARAMS: params_dict_geo2, DIALECT: 3 });\nconsole.log(res2.total); // >>> 1\nconsole.log(res2); // >>> {total: 1, documents: [ { id: 'bicycle:5', value: [Object: null prototype] } ]}","section_id":"shape"},{"id":"shape-ex5","language":"python","code":"params_dict = {\"bike\": \"POINT(-0.1278 51.5074)\"}\nq = Query(\"@pickup_zone:[CONTAINS $bike]\").dialect(3)\nres = index.search(q, query_params=params_dict)\nprint(res.total) # >>> 1","section_id":"shape"},{"id":"shape-ex6","language":"plaintext","code":"FT.SEARCH idx:bicycle \"@pickup_zone:[WITHIN $europe]\" PARAMS 2 europe \"POLYGON((-25 35, 40 35, 40 70, -25 70, -25 35))\" DIALECT 2","section_id":"shape"},{"id":"shape-ex7","language":"go","code":"res3, err := rdb.FTSearchWithArgs(ctx,\n\t\t\"idx:bicycle\",\n\t\t\"@pickup_zone:[WITHIN $europe]\",\n\t\t&redis.FTSearchOptions{\n\t\t\tParams: map[string]interface{}{\n\t\t\t\t\"europe\": \"POLYGON((-25 35, 40 35, 40 70, -25 70, -25 35))\",\n\t\t\t},\n\t\t\tDialectVersion: 3,\n\t\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3.Total) // >>> 5\n\n\tsort.Slice(res3.Docs, func(i, j int) bool {\n\t\treturn res3.Docs[i].ID < res3.Docs[j].ID\n\t})\n\n\tfor _, doc := range res3.Docs {\n\t\tfmt.Println(doc.ID)\n\t}\n\t// >>> bicycle:5\n\t// >>> bicycle:6\n\t// >>> bicycle:7\n\t// >>> bicycle:8\n\t// >>> bicycle:9","section_id":"shape"},{"id":"shape-ex8","language":"java","code":"SearchResult res3 = jedis.ftSearch(\"idx:bicycle\",\n            \"@pickup_zone:[WITHIN $europe]\",\n            FTSearchParams.searchParams()\n                    .addParam(\"europe\", \"POLYGON((-25 35, 40 35, 40 70, -25 70, -25 35))\")\n                    .dialect(3)\n        );\n        System.out.println(res3.getTotalResults()); // >>> 5\n\n        List<Document> docs3 = res3.getDocuments();\n\n        for (Document document : docs3) {\n            System.out.println(document.getId());\n        }\n        // >>> bicycle:5\n        // >>> bicycle:6\n        // >>> bicycle:7\n        // >>> bicycle:8\n        // >>> bicycle:9","section_id":"shape"},{"id":"shape-ex9","language":"javascript","code":"const params_dict_geo3 = { europe: 'POLYGON((-25 35, 40 35, 40 70, -25 70, -25 35))' };\nconst q_geo3 = '@pickup_zone:[WITHIN $europe]';\nconst res3 = await client.ft.search('idx:bicycle', q_geo3, { PARAMS: params_dict_geo3, DIALECT: 3 });\nconsole.log(res3.total); // >>> 5\nconsole.log(res3); // >>>\n// {\n//   total: 5,\n//   documents: [\n//     { id: 'bicycle:5', value: [Object: null prototype] },\n//     { id: 'bicycle:6', value: [Object: null prototype] },\n//     { id: 'bicycle:7', value: [Object: null prototype] },\n//     { id: 'bicycle:8', value: [Object: null prototype] },\n//     { id: 'bicycle:9', value: [Object: null prototype] }\n//   ]\n// }","section_id":"shape"},{"id":"shape-ex10","language":"python","code":"params_dict = {\"europe\": \"POLYGON((-25 35, 40 35, 40 70, -25 70, -25 35))\"}\nq = Query(\"@pickup_zone:[WITHIN $europe]\").dialect(3)\nres = index.search(q, query_params=params_dict)\nprint(res.total) # >>> 5","section_id":"shape"}]}
{"schema_version":2,"id":"develop/ai/search-and-query/query","title":"Querying data","url":"https://redis.io/docs/latest/develop/ai/search-and-query/query/","summary":"Understand how to query, search, and aggregate Redis data","aliases":["/develop/interact/search-and-query/query","/develop/interact/search-and-query/query/","/interact/search-and-query/query/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:33:04+01:00","children":[{"id":"develop/ai/search-and-query/query/exact-match","summary":"Perform simple exact match queries","title":"Exact match queries","url":"https://redis.io/docs/latest/develop/ai/search-and-query/query/exact-match/"},{"id":"develop/ai/search-and-query/query/range","summary":"Perform numeric range queries","title":"Range queries","url":"https://redis.io/docs/latest/develop/ai/search-and-query/query/range/"},{"id":"develop/ai/search-and-query/query/full-text","summary":"Perform a full-text search","title":"Full-text search","url":"https://redis.io/docs/latest/develop/ai/search-and-query/query/full-text/"},{"id":"develop/ai/search-and-query/query/geo-spatial","summary":"Query based on geographic data","title":"Geospatial queries","url":"https://redis.io/docs/latest/develop/ai/search-and-query/query/geo-spatial/"},{"id":"develop/ai/search-and-query/query/vector-search","summary":"Query for data based on vector embeddings","title":"Vector search","url":"https://redis.io/docs/latest/develop/ai/search-and-query/query/vector-search/"},{"id":"develop/ai/search-and-query/query/combined","summary":"Combine query expressions","title":"Combined queries","url":"https://redis.io/docs/latest/develop/ai/search-and-query/query/combined/"},{"id":"develop/ai/search-and-query/query/aggregation","summary":"Group and aggregate query results","title":"Aggregation queries","url":"https://redis.io/docs/latest/develop/ai/search-and-query/query/aggregation/"},{"id":"develop/ai/search-and-query/query/interactive-demo","summary":"Run full-text, filter, and aggregation queries on a food delivery app's restaurants and orders, and see each redis-cli command and reply.","title":"Query interactive demo","url":"https://redis.io/docs/latest/develop/ai/search-and-query/query/interactive-demo/"}],"page_type":"content","content_hash":"55e25db721fecba0f34dca6b3e6c6951f4ba6fde8f34fe5f113b36b9e264ed5c","sections":[{"id":"content","title":"Content","role":"content","text":"Redis Open Source distinguishes between the [FT.SEARCH](https://redis.io/docs/latest/commands/ft.search) and [FT.AGGREGATE](https://redis.io/docs/latest/commands/ft.aggregate) query commands. You should use [FT.SEARCH](https://redis.io/docs/latest/commands/ft.search) if you want to perform selections and projections only. If you also need to apply mapping functions, group, or aggregate data, use the [FT.AGGREGATE](https://redis.io/docs/latest/commands/ft.aggregate) command. \n\n* **Selection**: A selection allows you to return all documents that fulfill specific criteria.\n* **Projection**: Projections are used to return specific fields of the result set. You can also map/project to calculated field values.\n* **Aggregation**: Aggregations collect and summarize data across several fields.\n\nHere is a short SQL comparison using the [bicycle dataset](./data/bicycles.txt):\n\n|Type| SQL | Redis |\n|----------| --- | ----------- |\n| Selection | `SELECT * FROM bicycles WHERE price >= 1000` | `FT.SEARCH idx:bicycle \"@price:[1000 +inf]\"` |\n| Simple projection | `SELECT id, price FROM bicycles` | `FT.SEARCH idx:bicycle \"*\" RETURN 2 __key, price` |\n| Calculated projection| `SELECT id, price-price*0.1 AS discounted FROM bicycles`| `FT.AGGREGATE idx:bicycle \"*\" LOAD 2 __key price APPLY \"@price-@price*0.1\" AS discounted`| \n| Aggregation | `SELECT condition, AVG(price) AS avg_price FROM bicycles GROUP BY condition` | `FT.AGGREGATE idx:bicycle \"*\" GROUPBY 1 @condition REDUCE AVG 1 @price AS avg_price` |\n\nThe following articles provide an overview of how to query data with the [FT.SEARCH](https://redis.io/docs/latest/commands/ft.search) command:\n\n* [Exact match queries](https://redis.io/docs/latest/develop/ai/search-and-query/query/exact-match)\n* [Range queries](https://redis.io/docs/latest/develop/ai/search-and-query/query/range)\n* [Full-text search ](https://redis.io/docs/latest/develop/ai/search-and-query/query/full-text)\n* [Geospatial queries](https://redis.io/docs/latest/develop/ai/search-and-query/query/geo-spatial)\n* [Vector search](https://redis.io/docs/latest/develop/ai/search-and-query/query/vector-search)\n* [Combined queries](https://redis.io/docs/latest/develop/ai/search-and-query/query/combined)\n\nYou can find further details about aggregation queries with [FT.AGGREGATE](https://redis.io/docs/latest/commands/ft.aggregate) in the following article:\n\n* [Aggregation queries](https://redis.io/docs/latest/develop/ai/search-and-query/query/aggregation)"}],"examples":[]}
{"schema_version":2,"id":"develop/ai/search-and-query/query/interactive-demo","title":"Query interactive demo","url":"https://redis.io/docs/latest/develop/ai/search-and-query/query/interactive-demo/","summary":"Run full-text, filter, and aggregation queries on a food delivery app's restaurants and orders, and see each redis-cli command and reply.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-16T09:51:00+02:00","page_type":"content","content_hash":"1f699ec85ec028785737308d5a4ac643fc88f515b8bd17fb33bac942a5d7d5e1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Search lets you query data through an index you create. This demo uses restaurant and order data from a food delivery app, stored as hashes. It starts by showing the data and a possible index. Next, three kinds of queries are demonstrated: full-text search, filter and sort, and aggregation. For each query, it shows the command, the matching data, and the exact reply.\n\nThe demo runs in your browser and doesn't connect to Redis. Its commands and replies were recorded from Redis, and each command works as shown in `redis-cli` once you create the indexes and add the data. The restaurants and orders are the same sample data as the [Context Retriever interactive demo](https://redis.io/docs/latest/develop/ai/context-engine/context-retriever/interactive-demo)."},{"id":"what-the-demo-shows","title":"What the demo shows","role":"content","text":"1. **The data.** Each restaurant is a hash. An index, created with [`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create), tells Redis Search which fields to search and how. See [Indexing](https://redis.io/docs/latest/develop/ai/search-and-query/indexing).\n2. **Full-text search.** [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) finds words in `TEXT` fields, including other forms of each word, such as plurals. It also handles exact phrases, prefixes, fuzzy matches, and the `|`, `-`, and `@field:` operators. `HIGHLIGHT` marks the words that matched. See [Full-text search](https://redis.io/docs/latest/develop/ai/search-and-query/query/full-text).\n3. **Filter and sort.** `TAG` and `NUMERIC` fields take exact conditions, and conditions side by side must all match. `SORTBY` orders the matches. See [Exact match queries](https://redis.io/docs/latest/develop/ai/search-and-query/query/exact-match) and [Range queries](https://redis.io/docs/latest/develop/ai/search-and-query/query/range).\n4. **Aggregate.** [`FT.AGGREGATE`](https://redis.io/docs/latest/commands/ft.aggregate) groups the matching orders with `GROUPBY` and reduces each group with `REDUCE`, here with `COUNT`, `SUM`, and `AVG`. See [Aggregation queries](https://redis.io/docs/latest/develop/ai/search-and-query/query/aggregation)."}],"examples":[]}
{"schema_version":2,"id":"develop/ai/search-and-query/query/range","title":"Range queries","url":"https://redis.io/docs/latest/develop/ai/search-and-query/query/range/","summary":"Perform numeric range queries","aliases":["/develop/interact/search-and-query/query/range"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:33:04+01:00","page_type":"content","content_hash":"103966df3a5bb7b415e6a365a0e49065e903743cdb71d0ffd9968d9237b2964f","sections":[{"id":"start-and-end-values","title":"Start and end values","role":"content","text":"Start and end values are by default inclusive, but you can prepend `(` to a value to exclude it from the range.\n\nThe values `-inf`, `inf`, and `+inf` are valid values that allow you to define open ranges."},{"id":"result-set","title":"Result set","role":"returns","text":"An open-range query can lead to a large result set. \n\nBy default, [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) returns only the first ten results. The `LIMIT` argument helps you to scroll through the result set. The `SORTBY` argument ensures that the documents in the result set are returned in the specified order.\n\n[code example]\n\nYou can find further details about using the `LIMIT` and `SORTBY` in the [[`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) command reference](https://redis.io/docs/latest/commands/ft.search)."},{"id":"examples","title":"Examples","role":"example","text":"The examples in this section use a schema with the following fields:\n\n| Field name | Field type |\n| ---------- | ---------- |\n| `price` | `NUMERIC` |\n\nThe following query finds bicycles within a price range greater than or equal to 500 USD and smaller than or equal to 1000 USD (`500 <= price <= 1000`):\n\nFoundational: Query numeric fields with inclusive range syntax when you need to find documents with values between two bounds\n\n**Difficulty:** Beginner\n\n**Commands:** FT.SEARCH\n\n**Complexity:**\n- FT.SEARCH: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nThis is semantically equivalent to:\n\nFoundational: Query numeric fields using a FILTER clause when you need an alternative syntax for range queries with different query execution semantics\n\n**Difficulty:** Beginner\n\n**Commands:** FT.SEARCH\n\n**Complexity:**\n- FT.SEARCH: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nFor bicycles with a price greater than 1000 USD (`price > 1000`), you can use:\n\nOpen ranges: Query numeric fields with open ranges using infinity notation and exclusive bounds when you need to find documents above or below a threshold\n\n**Difficulty:** Intermediate\n\n**Commands:** FT.SEARCH\n\n**Complexity:**\n- FT.SEARCH: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nThe example below returns bicycles with a price lower than or equal to 2000 USD (`price <= 2000`) by returning the five cheapest bikes:\n\nSorting and pagination: Combine range queries with SORTBY and LIMIT to retrieve sorted results in pages when you need to handle large result sets efficiently\n\n**Difficulty:** Intermediate\n\n**Commands:** FT.SEARCH\n\n**Complexity:**\n- FT.SEARCH: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]"},{"id":"non-numeric-range-queries","title":"Non-numeric range queries","role":"content","text":"You can learn more about non-numeric range queries, such as [geospatial](https://redis.io/docs/latest/develop/ai/search-and-query/query/geo-spatial) or [vector search](https://redis.io/docs/latest/develop/ai/search-and-query/query/vector-search) queries, in their dedicated articles."}],"examples":[{"id":"result-set-ex0","language":"plaintext","code":"FT.SEARCH index \"@field:[start end]\" SORTBY field LIMIT page_start page_end","section_id":"result-set"},{"id":"examples-ex0","language":"plaintext","code":"> FT.SEARCH idx:bicycle \"@price:[500 1000]\"\n1) (integer) 3\n2) \"bicycle:2\"\n3) 1) \"$\"\n   2) \"{\\\"pickup_zone\\\":\\\"POLYGON((-87.6848 41.9331, -87.5748 41.9331, -87.5748 41.8231, -87.6848 41.8231, -87.6848 41.9331))\\\",\\\"store_location\\\":\\\"-87.6298,41.8781\\\",\\\"brand\\\":\\\"Nord\\\",\\\"model\\\":\\\"Chook air 5\\\",\\\"price\\\":815,\\\"description\\\":\\\"The Chook Air 5  gives kids aged six years and older a durable and uberlight mountain bike for their first experience on tracks and easy cruising through forests and fields. The lower  top tube makes it easy to mount and dismount in any situation, giving your kids greater safety on the trails.\\\",\\\"condition\\\":\\\"used\\\"}\"\n4) \"bicycle:5\"\n... output truncated for AI-facing Markdown ...\n7) 1) \"$\"\n   2) \"{\\\"pickup_zone\\\":\\\"POLYGON((12.4464 42.1028, 12.5464 42.1028, 12.5464 41.7028, 12.4464 41.7028, 12.4464 42.1028))\\\",\\\"store_location\\\":\\\"12.4964,41.9028\\\",\\\"model\\\":\\\"ThrillCycle\\\",\\\"brand\\\":\\\"BikeShind\\\",\\\"price\\\":815,\\\"description\\\":\\\"An artsy,  retro-inspired bicycle that\\xe2\\x80\\x99s as functional as it is pretty: The ThrillCycle steel frame offers a smooth ride. A 9-speed drivetrain has enough gears for coasting in the city, but we wouldn\\xe2\\x80\\x99t suggest taking it to the mountains. Fenders protect you from mud, and a rear basket lets you transport groceries, flowers and books. The ThrillCycle comes with a limited lifetime warranty, so this little guy will last you long past graduation.\\\",\\\"condition\\\":\\\"refurbished\\\"}\"","section_id":"examples"},{"id":"examples-ex1","language":"csharp","code":"SearchResult res1 = db.FT().Search(\n            \"idx:bicycle\",\n            new(\"@price:[500 1000]\")\n        );\n        Console.WriteLine(res1.TotalResults);   // >>> 3","section_id":"examples"},{"id":"examples-ex2","language":"go","code":"res1, err := rdb.FTSearchWithArgs(ctx,\n\t\t\"idx:bicycle\", \"@price:[500 1000]\",\n\t\t&redis.FTSearchOptions{\n\t\t\tReturn: []redis.FTSearchReturn{\n\t\t\t\t{\n\t\t\t\t\tFieldName: \"price\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tSortBy: []redis.FTSearchSortBy{\n\t\t\t\t{\n\t\t\t\t\tFieldName: \"price\",\n\t\t\t\t\tAsc:       true,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1.Total) // >>> 3\n\n\tfor _, doc := range res1.Docs {\n\t\tfmt.Printf(\"%v : price %v\\n\", doc.ID, doc.Fields[\"price\"])\n\t}\n\t// >>> bicycle:2 : price 815\n\t// >>> bicycle:5 : price 810\n\t// >>> bicycle:9 : price 815","section_id":"examples"},{"id":"examples-ex3","language":"java","code":"CompletableFuture<SearchReply<String, String>> priceResults = asyncCommands\n                    .ftSearch(\"idx:bicycle\", \"@price:[500 1000]\").thenApply(res -> {\n                        res.getResults().stream().sorted((doc1, doc2) -> doc1.getId().compareTo(doc2.getId())).forEach(doc -> {\n                            System.out.printf(\"ID: %s\\n\", doc.getId());\n                        });\n                        // >>> ID: bicycle:2\n                        // >>> ID: bicycle:5\n                        // >>> ID: bicycle:9\n                        return res;\n                    }).toCompletableFuture();","section_id":"examples"},{"id":"examples-ex4","language":"java","code":"Mono<SearchReply<String, String>> priceResults = reactiveCommands.ftSearch(\"idx:bicycle\", \"@price:[500 1000]\")\n                    .doOnNext(res -> {\n                        res.getResults().stream().sorted((doc1, doc2) -> doc1.getId().compareTo(doc2.getId())).forEach(doc -> {\n                            System.out.printf(\"ID: %s\\n\", doc.getId());\n                        });\n                        // >>> ID: bicycle:2\n                        // >>> ID: bicycle:5\n                        // >>> ID: bicycle:9\n                    });","section_id":"examples"},{"id":"examples-ex5","language":"java","code":"SearchResult res1 = jedis.ftSearch(\n            \"idx:bicycle\", \"@price:[500 1000]\",\n            FTSearchParams.searchParams().returnFields(\"price\"));\n        System.out.println(res1.getTotalResults()); // >>> 3\n\n        List<Document> docs1 = res1.getDocuments();\n\n        for (Document document : docs1) {\n            System.out.println(document.getId() + \" : price \" + document.getString(\"price\"));\n        }\n        // >>> bicycle:2 : price 815\n        // >>> bicycle:5 : price 810\n        // >>> bicycle:9 : price 815","section_id":"examples"},{"id":"examples-ex6","language":"javascript","code":"const res1 = await client.ft.search('idx:bicycle', '@price:[500 1000]');\nconsole.log(res1.total); // >>> 3","section_id":"examples"},{"id":"examples-ex7","language":"python","code":"res = index.search(Query(\"@price:[500 1000]\"))\nprint(res.total)\n# >>> 3","section_id":"examples"},{"id":"examples-ex8","language":"plaintext","code":"> FT.SEARCH idx:bicycle \"*\" FILTER price 500 1000\n1) (integer) 3\n2) \"bicycle:2\"\n3) 1) \"$\"\n   2) \"{\\\"pickup_zone\\\":\\\"POLYGON((-87.6848 41.9331, -87.5748 41.9331, -87.5748 41.8231, -87.6848 41.8231, -87.6848 41.9331))\\\",\\\"store_location\\\":\\\"-87.6298,41.8781\\\",\\\"brand\\\":\\\"Nord\\\",\\\"model\\\":\\\"Chook air 5\\\",\\\"price\\\":815,\\\"description\\\":\\\"The Chook Air 5  gives kids aged six years and older a durable and uberlight mountain bike for their first experience on tracks and easy cruising through forests and fields. The lower  top tube makes it easy to mount and dismount in any situation, giving your kids greater safety on the trails.\\\",\\\"condition\\\":\\\"used\\\"}\"\n4) \"bicycle:5\"\n... output truncated for AI-facing Markdown ...\n7) 1) \"$\"\n   2) \"{\\\"pickup_zone\\\":\\\"POLYGON((12.4464 42.1028, 12.5464 42.1028, 12.5464 41.7028, 12.4464 41.7028, 12.4464 42.1028))\\\",\\\"store_location\\\":\\\"12.4964,41.9028\\\",\\\"model\\\":\\\"ThrillCycle\\\",\\\"brand\\\":\\\"BikeShind\\\",\\\"price\\\":815,\\\"description\\\":\\\"An artsy,  retro-inspired bicycle that\\xe2\\x80\\x99s as functional as it is pretty: The ThrillCycle steel frame offers a smooth ride. A 9-speed drivetrain has enough gears for coasting in the city, but we wouldn\\xe2\\x80\\x99t suggest taking it to the mountains. Fenders protect you from mud, and a rear basket lets you transport groceries, flowers and books. The ThrillCycle comes with a limited lifetime warranty, so this little guy will last you long past graduation.\\\",\\\"condition\\\":\\\"refurbished\\\"}\"","section_id":"examples"},{"id":"examples-ex9","language":"csharp","code":"SearchResult res2 = db.FT().Search(\n            \"idx:bicycle\",\n            new Query().AddFilter(\n                new Query.NumericFilter(\"price\", 500, 1000)\n            )\n        );\n        Console.WriteLine(res2.TotalResults);   // >>> 3","section_id":"examples"},{"id":"examples-ex10","language":"go","code":"res2, err := rdb.FTSearchWithArgs(ctx,\n\t\t\"idx:bicycle\", \"*\",\n\t\t&redis.FTSearchOptions{\n\t\t\tFilters: []redis.FTSearchFilter{\n\t\t\t\t{\n\t\t\t\t\tFieldName: \"price\",\n\t\t\t\t\tMin:       500,\n\t\t\t\t\tMax:       1000,\n\t\t\t\t},\n\t\t\t},\n\t\t\tReturn: []redis.FTSearchReturn{\n\t\t\t\t{\n\t\t\t\t\tFieldName: \"price\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tSortBy: []redis.FTSearchSortBy{\n\t\t\t\t{\n\t\t\t\t\tFieldName: \"price\",\n\t\t\t\t\tAsc:       true,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2.Total) // >>> 3\n\n\tfor _, doc := range res2.Docs {\n\t\tfmt.Printf(\"%v : price %v\\n\", doc.ID, doc.Fields[\"price\"])\n\t}\n\t// >>> bicycle:2 : price 815\n\t// >>> bicycle:5 : price 810\n\t// >>> bicycle:9 : price 815","section_id":"examples"},{"id":"examples-ex11","language":"java","code":"// `Filter` is not supported.","section_id":"examples"},{"id":"examples-ex12","language":"java","code":"// `Filter` is not supported.","section_id":"examples"},{"id":"examples-ex13","language":"java","code":"SearchResult res2 = jedis.ftSearch(\"idx:bicycle\",\n            \"*\",\n            FTSearchParams.searchParams()\n                .returnFields(\"price\")\n                .filter(\"price\", 500, 1000)\n        );\n        System.out.println(res2.getTotalResults()); // >>> 3\n\n        List<Document> docs2 = res2.getDocuments();\n\n        for (Document document : docs2) {\n            System.out.println(document.getId() + \" : price \" + document.getString(\"price\"));\n        }\n        // >>> bicycle:2 : price 815\n        // >>> bicycle:5 : price 810\n        // >>> bicycle:9 : price 815","section_id":"examples"},{"id":"examples-ex14","language":"javascript","code":"// FILTER is not supported\n// const res2 = await client.ft.search('idx:bicycle', '*', {\n//   FILTER: {\n//     field: 'price',\n//     min: 500,\n//     max: 1000,\n//   }\n// });\n// console.log(res2.total); // >>> 3","section_id":"examples"},{"id":"examples-ex15","language":"python","code":"query = Query(\"*\").add_filter(NumericFilter(\"price\", 500, 1000))\nres = index.search(query)\nprint(res.total)\n# >>> 3","section_id":"examples"},{"id":"examples-ex16","language":"plaintext","code":"> FT.SEARCH idx:bicycle \"@price:[(1000 +inf]\"\n 1) (integer) 5\n 2) \"bicycle:1\"\n 3) 1) \"$\"\n    2) \"{\\\"pickup_zone\\\":\\\"POLYGON((-118.2887 34.0972, -118.1987 34.0972, -118.1987 33.9872, -118.2887 33.9872, -118.2887 34.0972))\\\",\\\"store_location\\\":\\\"-118.2437,34.0522\\\",\\\"brand\\\":\\\"Bicyk\\\",\\\"model\\\":\\\"Hillcraft\\\",\\\"price\\\":1200,\\\"description\\\":\\\"Kids want to ride with as little weight as possible. Especially on an incline! They may be at the age when a 27.5\\\\\\\" wheel bike is just too clumsy coming off a 24\\\\\\\" bike. The Hillcraft 26 is just the solution they need!\\\",\\\"condition\\\":\\\"used\\\"}\"\n 4) \"bicycle:4\"\n... output truncated for AI-facing Markdown ...\n    2) \"{\\\"pickup_zone\\\":\\\"POLYGON((1.9450 41.4301, 2.4018 41.4301, 2.4018 41.1987, 1.9450 41.1987, 1.9450 41.4301))\\\",\\\"store_location\\\":\\\"2.1734, 41.3851\\\",\\\"brand\\\":\\\"nHill\\\",\\\"model\\\":\\\"Summit\\\",\\\"price\\\":1200,\\\"description\\\":\\\"This budget mountain bike from nHill performs well both on bike paths and on the trail. The fork with 100mm of travel absorbs rough terrain. Fat Kenda Booster tires give you grip in corners and on wet trails. The Shimano Tourney drivetrain offered enough gears for finding a comfortable pace to ride uphill, and the Tektro hydraulic disc brakes break smoothly. Whether you want an affordable bike that you can take to work, but also take trail in mountains on the weekends or you\\xe2\\x80\\x99re just after a stable, comfortable ride for the bike path, the Summit gives a good value for money.\\\",\\\"condition\\\":\\\"new\\\"}\"","section_id":"examples"},{"id":"examples-ex17","language":"csharp","code":"SearchResult res3 = db.FT().Search(\n            \"idx:bicycle\",\n            new Query(\"*\").AddFilter(new Query.NumericFilter(\n                    \"price\", 1000, true, Double.PositiveInfinity, false\n                )\n            )\n        );\n        Console.WriteLine(res3.TotalResults);   // >>> 5","section_id":"examples"},{"id":"examples-ex18","language":"go","code":"res3, err := rdb.FTSearchWithArgs(ctx,\n\t\t\"idx:bicycle\", \"*\",\n\t\t&redis.FTSearchOptions{\n\t\t\tReturn: []redis.FTSearchReturn{\n\t\t\t\t{\n\t\t\t\t\tFieldName: \"price\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tSortBy: []redis.FTSearchSortBy{\n\t\t\t\t{\n\t\t\t\t\tFieldName: \"price\",\n\t\t\t\t\tAsc:       true,\n\t\t\t\t},\n\t\t\t},\n\t\t\tFilters: []redis.FTSearchFilter{\n\t\t\t\t{\n\t\t\t\t\tFieldName: \"price\",\n\t\t\t\t\tMin:       \"(1000\",\n\t\t\t\t\tMax:       \"+inf\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3.Total) // >>> 5\n\n\tfor _, doc := range res3.Docs {\n\t\tfmt.Printf(\"%v : price %v\\n\", doc.ID, doc.Fields[\"price\"])\n\t}\n\t// >>> bicycle:1 : price 1200\n\t// >>> bicycle:4 : price 3200\n\t// >>> bicycle:6 : price 2300\n\t// >>> bicycle:3 : price 3400\n\t// >>> bicycle:8 : price 1200","section_id":"examples"},{"id":"examples-ex19","language":"java","code":"SearchArgs<String, String> priceSearchArgs2 = SearchArgs.<String, String> builder().returnField(\"price\").build();\n\n            CompletableFuture<SearchReply<String, String>> priceResults2 = asyncCommands\n                    .ftSearch(\"idx:bicycle\", \"@price:[(1000 +inf]\", priceSearchArgs2).thenApply(res -> {\n                        res.getResults().stream().sorted((doc1, doc2) -> doc1.getId().compareTo(doc2.getId())).forEach(doc -> {\n                            System.out.printf(\"ID: %s, price: %s\\n\", doc.getId(), doc.getFields().get(\"price\"));\n                        });\n                        // >>> ID: bicycle:1, price: 1200\n                        // >>> ID: bicycle:3, price: 3400\n                        // >>> ID: bicycle:4, price: 3200\n                        // >>> ID: bicycle:6, price: 2300\n                        // >>> ID: bicycle:8, price: 1200\n                        return res;\n                    }).toCompletableFuture();","section_id":"examples"},{"id":"examples-ex20","language":"java","code":"SearchArgs<String, String> priceSearchArgs2 = SearchArgs.<String, String> builder().returnField(\"price\").build();\n\n            Mono<SearchReply<String, String>> priceResults2 = reactiveCommands\n                    .ftSearch(\"idx:bicycle\", \"@price:[(1000 +inf]\", priceSearchArgs2).doOnNext(res -> {\n                        res.getResults().stream().sorted((doc1, doc2) -> doc1.getId().compareTo(doc2.getId())).forEach(doc -> {\n                            System.out.printf(\"ID: %s, price: %s\\n\", doc.getId(), doc.getFields().get(\"price\"));\n                        });\n                        // >>> ID: bicycle:1, price: 1200\n                        // >>> ID: bicycle:3, price: 3400\n                        // >>> ID: bicycle:4, price: 3200\n                        // >>> ID: bicycle:6, price: 2300\n                        // >>> ID: bicycle:8, price: 1200\n                    });","section_id":"examples"},{"id":"examples-ex21","language":"java","code":"SearchResult res3 = jedis.ftSearch(\"idx:bicycle\",\n            \"*\",\n            FTSearchParams.searchParams()\n                .returnFields(\"price\")\n                .filter(\"price\", 1000, true, Double.POSITIVE_INFINITY, false)\n        );\n        System.out.println(res3.getTotalResults()); // >>> 5\n\n        List<Document> docs3 = res3.getDocuments();\n\n        for (Document document : docs3) {\n            System.out.println(document.getId() + \" : price \" + document.getString(\"price\"));\n        }\n        // >>> bicycle:1 : price 1200\n        // >>> bicycle:4 : price 3200\n        // >>> bicycle:6 : price 2300\n        // >>> bicycle:3 : price 3400\n        // >>> bicycle:8 : price 1200","section_id":"examples"},{"id":"examples-ex22","language":"javascript","code":"// FILTER is not supported\n// const res3 = await client.ft.search('idx:bicycle', '*', {\n//   FILTER: {\n//     field: 'price',\n//     min: '(1000',\n//     max: '+inf,\n//   }\n// });\n// console.log(res3.total); // >>> 5","section_id":"examples"},{"id":"examples-ex23","language":"python","code":"query = Query(\"*\").add_filter(NumericFilter(\"price\", \"(1000\", \"+inf\"))\nres = index.search(query)\nprint(res.total)\n# >>> 5","section_id":"examples"},{"id":"examples-ex24","language":"plaintext","code":"> FT.SEARCH idx:bicycle \"@price:[-inf 2000]\" SORTBY price LIMIT 0 5\n 1) (integer) 7\n 2) \"bicycle:0\"\n 3) 1) \"price\"\n    2) \"270\"\n    3) \"$\"\n    4) \"{\\\"pickup_zone\\\":\\\"POLYGON((-74.0610 40.7578, -73.9510 40.7578, -73.9510 40.6678, -74.0610 40.6678, -74.0610 40.7578))\\\",\\\"store_location\\\":\\\"-74.0060,40.7128\\\",\\\"brand\\\":\\\"Velorim\\\",\\\"model\\\":\\\"Jigger\\\",\\\"price\\\":270,\\\"description\\\":\\\"Small and powerful, the Jigger is the best ride for the smallest of tikes! This is the tiniest kids\\xe2\\x80\\x99 pedal bike on the market available without a coaster brake, the Jigger is the vehicle of choice for the rare tenacious little rider raring to go.\\\",\\\"condition\\\":\\\"new\\\"}\"\n... output truncated for AI-facing Markdown ...\n    3) \"$\"\n    4) \"{\\\"pickup_zone\\\":\\\"POLYGON((12.4464 42.1028, 12.5464 42.1028, 12.5464 41.7028, 12.4464 41.7028, 12.4464 42.1028))\\\",\\\"store_location\\\":\\\"12.4964,41.9028\\\",\\\"model\\\":\\\"ThrillCycle\\\",\\\"brand\\\":\\\"BikeShind\\\",\\\"price\\\":815,\\\"description\\\":\\\"An artsy,  retro-inspired bicycle that\\xe2\\x80\\x99s as functional as it is pretty: The ThrillCycle steel frame offers a smooth ride. A 9-speed drivetrain has enough gears for coasting in the city, but we wouldn\\xe2\\x80\\x99t suggest taking it to the mountains. Fenders protect you from mud, and a rear basket lets you transport groceries, flowers and books. The ThrillCycle comes with a limited lifetime warranty, so this little guy will last you long past graduation.\\\",\\\"condition\\\":\\\"refurbished\\\"}\"","section_id":"examples"},{"id":"examples-ex25","language":"csharp","code":"SearchResult res4 = db.FT().Search(\n            \"idx:bicycle\",\n            new Query(\"@price:[-inf 2000]\")\n                .SetSortBy(\"price\")\n                .Limit(0, 5)\n        );\n        Console.WriteLine(res4.TotalResults);   // >>> 7\n        Console.WriteLine($\"Prices: {string.Join(\", \", res4.Documents.Select(d => d[\"price\"]))}\");\n        // >>> Prices: 270, 430, 810, 815, 815","section_id":"examples"},{"id":"examples-ex26","language":"go","code":"res4, err := rdb.FTSearchWithArgs(ctx,\n\t\t\"idx:bicycle\",\n\t\t\"@price:[-inf 2000]\",\n\t\t&redis.FTSearchOptions{\n\t\t\tReturn: []redis.FTSearchReturn{\n\t\t\t\t{\n\t\t\t\t\tFieldName: \"price\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tSortBy: []redis.FTSearchSortBy{\n\t\t\t\t{\n\t\t\t\t\tFieldName: \"price\",\n\t\t\t\t\tAsc:       true,\n\t\t\t\t},\n\t\t\t},\n\t\t\tLimitOffset: 0,\n\t\t\tLimit:       5,\n\t\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4.Total) // >>> 7\n\n\tfor _, doc := range res4.Docs {\n\t\tfmt.Printf(\"%v : price %v\\n\", doc.ID, doc.Fields[\"price\"])\n\t}\n\t// >>> bicycle:0 : price 270\n\t// >>> bicycle:7 : price 430\n\t// >>> bicycle:5 : price 810\n\t// >>> bicycle:2 : price 815\n\t// >>> bicycle:9 : price 815","section_id":"examples"},{"id":"examples-ex27","language":"java","code":"SearchArgs<String, String> priceSearchArgs3 = SearchArgs.<String, String> builder().returnField(\"price\")\n                    .sortBy(SortByArgs.<String> builder().attribute(\"price\").build()).limit(0, 5).build();\n\n            CompletableFuture<SearchReply<String, String>> priceResults3 = asyncCommands\n                    .ftSearch(\"idx:bicycle\", \"@price:[-inf 2000]\", priceSearchArgs3).thenApply(res -> {\n                        res.getResults().stream().sorted((doc1, doc2) -> doc1.getId().compareTo(doc2.getId())).forEach(doc -> {\n                            System.out.printf(\"ID: %s, price: %s\\n\", doc.getId(), doc.getFields().get(\"price\"));\n                        });\n                        // >>> ID: bicycle:0, price: 270\n                        // >>> ID: bicycle:2, price: 815\n                        // >>> ID: bicycle:5, price: 810\n                        // >>> ID: bicycle:7, price: 430\n                        // >>> ID: bicycle:9, price: 815\n                        return res;\n                    }).toCompletableFuture();","section_id":"examples"},{"id":"examples-ex28","language":"java","code":"SearchArgs<String, String> priceSearchArgs3 = SearchArgs.<String, String> builder().returnField(\"price\")\n                    .sortBy(SortByArgs.<String> builder().attribute(\"price\").build()).limit(0, 5).build();\n\n            Mono<SearchReply<String, String>> priceResults3 = reactiveCommands\n                    .ftSearch(\"idx:bicycle\", \"@price:[-inf 2000]\", priceSearchArgs3).doOnNext(res -> {\n                        res.getResults().stream().sorted((doc1, doc2) -> doc1.getId().compareTo(doc2.getId())).forEach(doc -> {\n                            System.out.printf(\"ID: %s, price: %s\\n\", doc.getId(), doc.getFields().get(\"price\"));\n                        });\n                        // >>> ID: bicycle:0, price: 270\n                        // >>> ID: bicycle:2, price: 815\n                        // >>> ID: bicycle:5, price: 810\n                        // >>> ID: bicycle:7, price: 430\n                        // >>> ID: bicycle:9, price: 815\n                    });","section_id":"examples"},{"id":"examples-ex29","language":"java","code":"SearchResult res4 = jedis.ftSearch(\"idx:bicycle\",\n            \"@price:[-inf 2000]\",\n            FTSearchParams.searchParams()\n                    .returnFields(\"price\")\n                    .sortBy(\"price\", SortingOrder.ASC)\n                    .limit(0, 5) \n        );\n        System.out.println(res4.getTotalResults()); // >>> 7\n\n        List<Document> docs4 = res4.getDocuments();\n\n        for (Document document : docs4) {\n            System.out.println(document.getId() + \" : price \" + document.getString(\"price\"));\n        }\n        // >>> bicycle:0 : price 270\n        // >>> bicycle:7 : price 430\n        // >>> bicycle:5 : price 810\n        // >>> bicycle:2 : price 815\n        // >>> bicycle:9 : price 815","section_id":"examples"},{"id":"examples-ex30","language":"javascript","code":"const res4 = await client.ft.search(\n  'idx:bicycle',\n  '@price:[-inf 2000]',\n  {\n    SORTBY: 'price',\n    LIMIT: { from: 0, size: 5 }\n  }\n);\nconsole.log(res4.total); // >>> 7\nconsole.log(res4); // >>> { total: 7, documents: [ { id: 'bicycle:0', value: [Object: null prototype] }, { id: 'bicycle:7', value: [Object: null prototype] }, { id: 'bicycle:5', value: [Object: null prototype] }, { id: 'bicycle:2', value: [Object: null prototype] }, { id: 'bicycle:9', value: [Object: null prototype] } ] }","section_id":"examples"},{"id":"examples-ex31","language":"python","code":"query = Query('@price:[-inf 2000]').sort_by('price').paging(0, 5)\nres = index.search(query)\nprint(res.total)\nprint(res)\n# >>> Result{7 total, docs: [Document {'id': 'bicycle:0', ... }, Document {'id': 'bicycle:7', ... }, Document {'id': 'bicycle:5', ... }, ...]","section_id":"examples"}]}
{"schema_version":2,"id":"develop/ai/search-and-query/query/vector-search","title":"Vector search","url":"https://redis.io/docs/latest/develop/ai/search-and-query/query/vector-search/","summary":"Query for data based on vector embeddings","aliases":["/develop/interact/search-and-query/query/vector-search"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:33:04+01:00","page_type":"content","content_hash":"711daee047d46675ed8a1fd7f0da94ea163dd2cf166377993be21ec30ce0622a","sections":[{"id":"k-neareast-neighbours-knn","title":"K-neareast neighbours (KNN)","role":"content","text":"The Redis command [FT.SEARCH](https://redis.io/docs/latest/commands/ft.search) takes the index name, the query string, and additional query parameters as arguments. You need to pass the number of nearest neighbors, the vector field name, and the vector's binary representation in the following way:\n\n[code example]\n\nHere is a more detailed explanation of this query:\n\n1. **Pre-filter**: The first expression within the round brackets is a filter. It allows you to decide which vectors should be taken into account before the vector search is performed. The expression `(*)` means that all vectors are considered.\n2. **Next step**: The `=>` arrow indicates that the pre-filtering happens before the vector search.\n3. **KNN query**: The expression `[KNN num_neighbours @field $vector]` is a parameterized query expression. A parameter name is indicated by the `$` prefix within the query string.\n4. **Vector binary data**: You need to use the `PARAMS` argument to substitute `$vector` with the binary representation of the vector. The value `2` indicates that `PARAMS` is followed by two arguments, the parameter name `vector` and the parameter value.\n5. **Dialect**: The vector search feature has been available since version two of the query dialect.\n\nYou can read more about the `PARAMS` argument in the [FT.SEARCH](https://redis.io/docs/latest/commands/ft.search) command reference.\n\nThe following example shows you how to query for three bikes based on their description embeddings, and by using the field alias `vector`. The result is returned in ascending order based on the distance. You can see that the query only returns the fields `__vector_score` and `description`. The field `__vector_score` is present by default. Because you can have multiple vector fields in your schema, the vector score field name depends on the name of the vector field. If you change the field name `@vector` to `@foo`, the score field name changes to `__foo_score`.\n\nK-nearest neighbors: Find the k closest vectors to a query vector using KNN when you need to retrieve similar items based on embeddings\n\n**Difficulty:** Beginner\n\n**Available in:** Redis CLI, JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\n\n\n> [!NOTE]\n> The binary value of the query vector is significantly shortened in the CLI example above."},{"id":"radius","title":"Radius","role":"content","text":"Instead of the number of nearest neighbors, you need to pass the radius along with the index name, the vector field name, and the vector's binary value:\n\n[code example]\n\nIf you want to sort by distance, then you must yield the distance via the range query parameter `$YIELD_DISTANCE_AS`:\n\n[code example]\n\nHere is a more detailed explanation of this query:\n\n1. **Range query**: the syntax of a radius query is very similar to the regular range query, except for the keyword `VECTOR_RANGE`. You can also combine a vector radius query with other queries in the same way as regular range queries.  See [combined queries article](https://redis.io/docs/latest/develop/ai/search-and-query/query/combined) for more details.\n2. **Additional step**: the `=>` arrow means that the range query is followed by evaluating additional parameters.\n3. **Range query parameters**: parameters such as `$YIELD_DISTANCE_AS` can be found in the [vectors reference documentation](https://redis.io/docs/latest/develop/ai/search-and-query/vectors).\n4. **Vector binary data**: you need to use `PARAMS` to pass the binary representation of the vector.\n5. **Dialect**: vector search has been available since version two of the query dialect.\n\n\n> [!NOTE]\n> By default, [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) returns only the first ten results. The [range query article](https://redis.io/docs/latest/develop/ai/search-and-query/query/range) explains to you how to scroll through the result set.\n\nThe example below shows a radius query that returns the description and the distance within a radius of `0.5`. The result is sorted by the distance.\n\nRadius search: Find all vectors within a specified distance threshold using VECTOR_RANGE when you need to retrieve similar items with a distance constraint\n\n**Difficulty:** Intermediate\n\n**Available in:** Redis CLI, JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]"},{"id":"cluster-optimization","title":"Cluster optimization","role":"content","text":"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."},{"id":"basic-cluster-optimization","title":"Basic cluster optimization","role":"content","text":"Retrieve 100 nearest neighbors with each shard providing 60% of the requested results:\n\nCluster optimization: Optimize KNN queries in cluster environments using SHARD_K_RATIO to balance accuracy and performance when you need efficient distributed vector search\n\n**Difficulty:** Advanced\n\n**Available in:** Redis CLI, JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]"},{"id":"combined-with-filtering","title":"Combined with filtering","role":"content","text":"You can combine `$SHARD_K_RATIO` with pre-filtering to optimize searches on specific subsets of data:\n\nFiltered vector search: Combine pre-filtering with KNN and cluster optimization to search within a subset of data when you need to find similar items matching specific criteria\n\n**Difficulty:** Advanced\n\n**Available in:** Redis CLI, JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\n> [!NOTE]\n> The `$SHARD_K_RATIO` parameter is only applicable in Redis cluster environments and has no effect in standalone Redis instances."}],"examples":[{"id":"k-neareast-neighbours-knn-ex0","language":"plaintext","code":"FT.SEARCH index \"(*)=>[KNN num_neighbours @field $vector]\" PARAMS 2 vector \"binary_data\" DIALECT 2","section_id":"k-neareast-neighbours-knn"},{"id":"k-neareast-neighbours-knn-ex1","language":"plaintext","code":"FT.SEARCH idx:bikes_vss \"(*)=>[KNN 3 @vector $query_vector]\" PARAMS 2 \"query_vector\" \"Z\\xf8\\x15:\\xf23\\xa1\\xbfZ\\x1dI>\\r\\xca9...\" SORTBY \"__vector_score\" ASC RETURN 2 \"__vector_score\" \"description\" DIALECT 2","section_id":"k-neareast-neighbours-knn"},{"id":"k-neareast-neighbours-knn-ex2","language":"javascript","code":"const res1 = await client.ft.search('idx:bicycle', \n  '*=>[KNN 3 @vector $query_vector AS score]', {\n    PARAMS: { query_vector: vector_query },\n    RETURN: ['description'],\n    DIALECT: 2\n  }\n);\nconsole.log(res1.total); // >>> 3\nconsole.log(res1); // >>>\n//{\n//  total: 3,\n//  documents: [\n//    { id: 'bicycle:0', value: [Object: null prototype] {} },\n//    { id: 'bicycle:2', value: [Object: null prototype] {} },\n//    { id: 'bicycle:9', value: [Object: null prototype] {} }\n//  ]\n//}","section_id":"k-neareast-neighbours-knn"},{"id":"k-neareast-neighbours-knn-ex3","language":"python","code":"query = \"Bike for small kids\"\nquery_vector = embed_text(model, query)\nprint(query_vector[:10]) # >>> b'\\x02=c=\\x93\\x0e\\xe0=aC'\n\nvquery = VectorQuery(\n    vector=query_vector,\n    vector_field_name=\"description_embeddings\",\n    num_results=3,\n    return_score=True,\n    return_fields=[\"description\"]\n)\nres = index.query(vquery)\nprint(res) # >>> [{'id': 'bicycle:6b702e8b...', 'vector_distance': '0.399111807346', 'description': 'Kids want...","section_id":"k-neareast-neighbours-knn"},{"id":"radius-ex0","language":"plaintext","code":"FT.SEARCH index \"@field:[VECTOR_RANGE radius $vector]\" PARAMS 2 vector \"binary_data\" DIALECT 2","section_id":"radius"},{"id":"radius-ex1","language":"plaintext","code":"FT.SEARCH index \"@field:[VECTOR_RANGE radius $vector]=>{$YIELD_DISTANCE_AS: dist_field}\" PARAMS 2 vector \"binary_data\" SORTBY dist_field DIALECT 2","section_id":"radius"},{"id":"radius-ex2","language":"plaintext","code":"FT.SEARCH idx:bikes_vss \"@vector:[VECTOR_RANGE 0.5 $query_vector]=>{$YIELD_DISTANCE_AS: vector_dist}\" PARAMS 2 \"query_vector\" \"Z\\xf8\\x15:\\xf23\\xa1\\xbfZ\\x1dI>\\r\\xca9...\" SORTBY vector_dist ASC RETURN 2 vector_dist description DIALECT 2","section_id":"radius"},{"id":"radius-ex3","language":"javascript","code":"const res2 = await client.ft.search('idx:bicycle', \n  '@vector:[VECTOR_RANGE 0.9 $query_vector]=>{$YIELD_DISTANCE_AS: vector_dist}', {\n    PARAMS: { query_vector: vector_query },\n    SORTBY: 'vector_dist',\n    RETURN: ['vector_dist', 'description'],\n    DIALECT: 2\n  }\n);\nconsole.log(res2.total); // >>> 1\nconsole.log(res2); // >>>\n//{\n//  total: 1,\n//  documents: [ { id: 'bicycle:0', value: [Object: null prototype] } ]\n//}","section_id":"radius"},{"id":"radius-ex4","language":"python","code":"vquery = RangeQuery(\n    vector=query_vector,\n    vector_field_name=\"description_embeddings\",\n    distance_threshold=0.5,\n    return_score=True\n).return_fields(\"description\").dialect(2)\nres = index.query(vquery)\nprint(res) # >>> [{'id': 'bicycle:6bcb1bb4...', 'vector_distance': '0.399111807346', 'description': 'Kids want...","section_id":"radius"},{"id":"basic-cluster-optimization-ex0","language":"plaintext","code":"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","section_id":"basic-cluster-optimization"},{"id":"basic-cluster-optimization-ex1","language":"javascript","code":"import assert from 'node:assert';\nimport fs from 'node:fs';\nimport { createClient } from 'redis';\nimport { SCHEMA_FIELD_TYPE, SCHEMA_VECTOR_FIELD_ALGORITHM } from '@redis/search';\nimport { pipeline } from '@xenova/transformers';\n\nfunction float32Buffer(arr) {\n  const floatArray = new Float32Array(arr);\n  const float32Buffer = Buffer.from(floatArray.buffer);\n  return float32Buffer;\n}\n\nasync function embedText(sentence) {\n  let modelName = 'Xenova/all-MiniLM-L6-v2';\n  let pipe = await pipeline('feature-extraction', modelName);\n\n  let vectorOutput = await pipe(sentence, {\n      pooling: 'mean',\n      normalize: true,\n  });\n\n  const embedding = Object.values(vectorOutput?.data);\n\n  return embedding;\n}\n\nconst vector_query = float32Buffer(await embedText('That is a very happy person'));\n\nconst client = createClient();\nawait client.connect().catch(console.error);\n\n// create index\nawait client.ft.create('idx:bicycle', {\n    '$.description': {\n      type: SCHEMA_FIELD_TYPE.TEXT,\n      AS: 'description'\n    },\n    '$.description_embeddings': {\n        type: SCHEMA_FIELD_TYPE.VECTOR,\n        TYPE: 'FLOAT32',\n        ALGORITHM: SCHEMA_VECTOR_FIELD_ALGORITHM.FLAT,\n        DIM: 384,\n        DISTANCE_METRIC: 'COSINE',\n        AS: 'vector'\n    }\n}, {\n    ON: 'JSON',\n    PREFIX: 'bicycle:'\n});\n\n// load data\nconst bicycles = JSON.parse(fs.readFileSync('data/query_vector.json', 'utf8'));\n\nawait Promise.all(\n  bicycles.map((bicycle, bid) => {\n      return client.json.set(`bicycle:${bid}`, '$', bicycle);\n  })\n);\n\nconst res1 = await client.ft.search('idx:bicycle', \n  '*=>[KNN 3 @vector $query_vector AS score]', {\n    PARAMS: { query_vector: vector_query },\n    RETURN: ['description'],\n    DIALECT: 2\n  }\n);\nconsole.log(res1.total); // >>> 3\nconsole.log(res1); // >>>\n//{\n//  total: 3,\n//  documents: [\n//    { id: 'bicycle:0', value: [Object: null prototype] {} },\n//    { id: 'bicycle:2', value: [Object: null prototype] {} },\n//    { id: 'bicycle:9', value: [Object: null prototype] {} }\n//  ]\n//}\n\nconst res2 = await client.ft.search('idx:bicycle', \n  '@vector:[VECTOR_RANGE 0.9 $query_vector]=>{$YIELD_DISTANCE_AS: vector_dist}', {\n    PARAMS: { query_vector: vector_query },\n    SORTBY: 'vector_dist',\n    RETURN: ['vector_dist', 'description'],\n    DIALECT: 2\n  }\n);\nconsole.log(res2.total); // >>> 1\nconsole.log(res2); // >>>\n//{\n//  total: 1,\n//  documents: [ { id: 'bicycle:0', value: [Object: null prototype] } ]\n//}","section_id":"basic-cluster-optimization"},{"id":"basic-cluster-optimization-ex2","language":"python","code":"import json\nimport warnings\nimport redis\nimport numpy as np\nfrom redisvl.index import SearchIndex\nfrom redisvl.query import RangeQuery, VectorQuery\nfrom redisvl.schema import IndexSchema\nfrom sentence_transformers import SentenceTransformer\n\n\ndef embed_text(model, text):\n    return np.array(model.encode(text)).astype(np.float32).tobytes()\n\nr = redis.Redis(decode_responses=True)\n\nwarnings.filterwarnings(\"ignore\", category=FutureWarning, message=r\".*clean_up_tokenization_spaces.*\")\nmodel = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')\n\n# create index\nschema = IndexSchema.from_yaml('data/query_vector_idx.yaml')\nindex = SearchIndex(schema, r)\nindex.create(overwrite=True, drop=True)\n\n# load data\nwith open(\"data/query_vector.json\") as f:\n    bicycles = json.load(f)\nindex.load(bicycles)\n\nquery = \"Bike for small kids\"\nquery_vector = embed_text(model, query)\nprint(query_vector[:10]) # >>> b'\\x02=c=\\x93\\x0e\\xe0=aC'\n\nvquery = VectorQuery(\n    vector=query_vector,\n    vector_field_name=\"description_embeddings\",\n    num_results=3,\n    return_score=True,\n    return_fields=[\"description\"]\n)\nres = index.query(vquery)\nprint(res) # >>> [{'id': 'bicycle:6b702e8b...', 'vector_distance': '0.399111807346', 'description': 'Kids want...\n\nvquery = RangeQuery(\n    vector=query_vector,\n    vector_field_name=\"description_embeddings\",\n    distance_threshold=0.5,\n    return_score=True\n).return_fields(\"description\").dialect(2)\nres = index.query(vquery)\nprint(res) # >>> [{'id': 'bicycle:6bcb1bb4...', 'vector_distance': '0.399111807346', 'description': 'Kids want...","section_id":"basic-cluster-optimization"},{"id":"combined-with-filtering-ex0","language":"plaintext","code":"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","section_id":"combined-with-filtering"},{"id":"combined-with-filtering-ex1","language":"javascript","code":"import assert from 'node:assert';\nimport fs from 'node:fs';\nimport { createClient } from 'redis';\nimport { SCHEMA_FIELD_TYPE, SCHEMA_VECTOR_FIELD_ALGORITHM } from '@redis/search';\nimport { pipeline } from '@xenova/transformers';\n\nfunction float32Buffer(arr) {\n  const floatArray = new Float32Array(arr);\n  const float32Buffer = Buffer.from(floatArray.buffer);\n  return float32Buffer;\n}\n\nasync function embedText(sentence) {\n  let modelName = 'Xenova/all-MiniLM-L6-v2';\n  let pipe = await pipeline('feature-extraction', modelName);\n\n  let vectorOutput = await pipe(sentence, {\n      pooling: 'mean',\n      normalize: true,\n  });\n\n  const embedding = Object.values(vectorOutput?.data);\n\n  return embedding;\n}\n\nconst vector_query = float32Buffer(await embedText('That is a very happy person'));\n\nconst client = createClient();\nawait client.connect().catch(console.error);\n\n// create index\nawait client.ft.create('idx:bicycle', {\n    '$.description': {\n      type: SCHEMA_FIELD_TYPE.TEXT,\n      AS: 'description'\n    },\n    '$.description_embeddings': {\n        type: SCHEMA_FIELD_TYPE.VECTOR,\n        TYPE: 'FLOAT32',\n        ALGORITHM: SCHEMA_VECTOR_FIELD_ALGORITHM.FLAT,\n        DIM: 384,\n        DISTANCE_METRIC: 'COSINE',\n        AS: 'vector'\n    }\n}, {\n    ON: 'JSON',\n    PREFIX: 'bicycle:'\n});\n\n// load data\nconst bicycles = JSON.parse(fs.readFileSync('data/query_vector.json', 'utf8'));\n\nawait Promise.all(\n  bicycles.map((bicycle, bid) => {\n      return client.json.set(`bicycle:${bid}`, '$', bicycle);\n  })\n);\n\nconst res1 = await client.ft.search('idx:bicycle', \n  '*=>[KNN 3 @vector $query_vector AS score]', {\n    PARAMS: { query_vector: vector_query },\n    RETURN: ['description'],\n    DIALECT: 2\n  }\n);\nconsole.log(res1.total); // >>> 3\nconsole.log(res1); // >>>\n//{\n//  total: 3,\n//  documents: [\n//    { id: 'bicycle:0', value: [Object: null prototype] {} },\n//    { id: 'bicycle:2', value: [Object: null prototype] {} },\n//    { id: 'bicycle:9', value: [Object: null prototype] {} }\n//  ]\n//}\n\nconst res2 = await client.ft.search('idx:bicycle', \n  '@vector:[VECTOR_RANGE 0.9 $query_vector]=>{$YIELD_DISTANCE_AS: vector_dist}', {\n    PARAMS: { query_vector: vector_query },\n    SORTBY: 'vector_dist',\n    RETURN: ['vector_dist', 'description'],\n    DIALECT: 2\n  }\n);\nconsole.log(res2.total); // >>> 1\nconsole.log(res2); // >>>\n//{\n//  total: 1,\n//  documents: [ { id: 'bicycle:0', value: [Object: null prototype] } ]\n//}","section_id":"combined-with-filtering"},{"id":"combined-with-filtering-ex2","language":"python","code":"import json\nimport warnings\nimport redis\nimport numpy as np\nfrom redisvl.index import SearchIndex\nfrom redisvl.query import RangeQuery, VectorQuery\nfrom redisvl.schema import IndexSchema\nfrom sentence_transformers import SentenceTransformer\n\n\ndef embed_text(model, text):\n    return np.array(model.encode(text)).astype(np.float32).tobytes()\n\nr = redis.Redis(decode_responses=True)\n\nwarnings.filterwarnings(\"ignore\", category=FutureWarning, message=r\".*clean_up_tokenization_spaces.*\")\nmodel = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')\n\n# create index\nschema = IndexSchema.from_yaml('data/query_vector_idx.yaml')\nindex = SearchIndex(schema, r)\nindex.create(overwrite=True, drop=True)\n\n# load data\nwith open(\"data/query_vector.json\") as f:\n    bicycles = json.load(f)\nindex.load(bicycles)\n\nquery = \"Bike for small kids\"\nquery_vector = embed_text(model, query)\nprint(query_vector[:10]) # >>> b'\\x02=c=\\x93\\x0e\\xe0=aC'\n\nvquery = VectorQuery(\n    vector=query_vector,\n    vector_field_name=\"description_embeddings\",\n    num_results=3,\n    return_score=True,\n    return_fields=[\"description\"]\n)\nres = index.query(vquery)\nprint(res) # >>> [{'id': 'bicycle:6b702e8b...', 'vector_distance': '0.399111807346', 'description': 'Kids want...\n\nvquery = RangeQuery(\n    vector=query_vector,\n    vector_field_name=\"description_embeddings\",\n    distance_threshold=0.5,\n    return_score=True\n).return_fields(\"description\").dialect(2)\nres = index.query(vquery)\nprint(res) # >>> [{'id': 'bicycle:6bcb1bb4...', 'vector_distance': '0.399111807346', 'description': 'Kids want...","section_id":"combined-with-filtering"}]}
{"schema_version":2,"id":"develop/ai/search-and-query/vectors","title":"Vector search concepts","url":"https://redis.io/docs/latest/develop/ai/search-and-query/vectors/","summary":"Learn how to use vector fields and perform vector searches in Redis","aliases":["/develop/interact/search-and-query/advanced-concepts/vectors","/interact/search-and-query/advanced-concepts/vectors/","/develop/ai/vector-fields/","/stack/search/reference/vectors/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-30T13:16:11-07:00","children":[{"id":"develop/ai/search-and-query/vectors/svs-compression","summary":"Vector quantization and compression for efficient memory usage and search performance","title":"Vector quantization and compression","url":"https://redis.io/docs/latest/develop/ai/search-and-query/vectors/svs-compression/"}],"page_type":"content","content_hash":"7885e036e2edf8fb02550d02f6d2386ecc3004481b01277b1a3158eb757bb87c","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"Redis includes a [high-performance vector database](https://redis.io/blog/benchmarking-results-for-vector-databases/) that lets you perform semantic searches over vector embeddings. You can augment these searches with filtering over text, numerical, geospatial, and tag metadata.\n\nTo quickly get started, check out the [Redis vector quickstart guide](https://redis.io/docs/latest/develop/get-started/search-tutorial/vector-search) and the [Redis AI Resources](https://github.com/redis-developer/redis-ai-resources) Github repo. To generate a working example, open the [AI agent builder](https://redis.io/docs/latest/develop/ai/agent-builder) and choose the **Knowledge Assistant** template.\n\n> [!NOTE] See vector search in action\n> [Redis Repo Memory](https://github.com/marketplace/actions/redis-repo-memory) is a GitHub Action that gives your repository a memory: on every pull request it embeds the change and uses Redis KNN vector search to surface semantically related past PRs, issues, and commits — a concise, real-world example of the concepts on this page. See the [source on GitHub](https://github.com/redis-learn/redis-repo-memory)."},{"id":"overview","title":"Overview","role":"overview","text":"1. [**Create a vector index**](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#create-a-vector-index): Redis maintains a secondary index over your data with a defined schema (including vector fields and metadata). Redis supports [`FLAT`](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#flat-index), [`HNSW`](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#hnsw-index) and [`SVS-VAMANA`](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#svs-vamana-index) vector index types.\n1. [**Store and update vectors**](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#store-and-update-vectors): Redis stores vectors and metadata in hashes or JSON objects.\n1. [**Search with vectors**](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#search-with-vectors): Redis supports several advanced querying strategies with vector fields including k-nearest neighbor ([KNN](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#knn-vector-search)), [vector range queries](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#vector-range-queries), and [metadata filters](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#filters).\n1. [**Configure vector queries at runtime**](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#runtime-query-params).\n1. [**Vector search examples**](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#vector-search-examples): Explore several vector search examples that cover different use cases and techniques."},{"id":"create-a-vector-index","title":"Create a vector index","role":"content","text":"When you define the schema for an index, you can include one or more vector fields as shown below.\n\n**Syntax**\n\n[code example]\n\nRefer to the full [indexing](https://redis.io/docs/latest/develop/ai/search-and-query/indexing) documentation for additional fields, options, and noted limitations.\n\n**Parameters**\n\n| Parameter | Description |\n|:----------|:------------|\n| `index_name`      | Name of the index.  |\n| `storage_type`    | Storage option (`HASH` or `JSON`).  |\n| `prefix` (optional) | Key prefix used to select which keys should be indexed. Defaults to all keys if omitted. |\n| `field_name`      | Name of the vector field.  |\n| `algorithm`       | Vector index algorithm (`FLAT` or `HNSW`).  |\n| `index_attribute_count` | Number of vector field attributes.  |\n| `index_attribute_name`  | Vector field attribute name.  |\n| `index_attribute_value` | Vector field attribute value.  |"},{"id":"flat-index","title":"FLAT index","role":"content","text":"Choose the `FLAT` index when you have small datasets (< 1M vectors) or when perfect search accuracy is more important than search latency.\n\n**Required attributes**\n\n| Attribute          | Description                              |\n|:-------------------|:-----------------------------------------|\n| `TYPE`             | Vector type (`BFLOAT16`, `FLOAT16`, `FLOAT32`, `FLOAT64`, `INT8`, `UINT8`). `BFLOAT16` and `FLOAT16` require v2.10 or later, while `INT8` and `UINT8` require 8.0 or later. |\n| `DIM`              | The width, or number of dimensions, of the vector embeddings stored in this field. In other words, the number of floating point elements comprising the vector. `DIM` must be a positive integer. The vector used to query this field must have the exact dimensions as the field itself.  |\n| `DISTANCE_METRIC`  | Distance metric (`L2`, `IP`, `COSINE`).  |\n\n**Example**\n\n[code example]\nIn the example above, an index named `documents` is created over hashes with the key prefix `docs:` and a `FLAT` vector field named `doc_embedding` with three index attributes: `TYPE`, `DIM`, and `DISTANCE_METRIC`."},{"id":"hnsw-index","title":"HNSW index","role":"content","text":"`HNSW`, or hierarchical navigable small world, is an approximate nearest neighbors algorithm that uses a multi-layered graph to make vector search more scalable.\n- The lowest layer contains all data points, and each higher layer contains a subset, forming a hierarchy.\n- At runtime, the search traverses the graph on each layer from top to bottom, finding the local minima before dropping to the subsequent layer.\n\nChoose the `HNSW` index type when you have larger datasets (> 1M documents) or when search performance and scalability are more important than perfect search accuracy.\n\n**Required attributes**\n\n| Attribute          | Description                              |\n|:-------------------|:-----------------------------------------|\n| `TYPE`             | Vector type (`BFLOAT16`, `FLOAT16`, `FLOAT32`, `FLOAT64`, `INT8`, `UINT8`). `BFLOAT16` and `FLOAT16` require v2.10 or later, while `INT8` and `UINT8` require 8.0 or later. |\n| `DIM`              | The width, or number of dimensions, of the vector embeddings stored in this field. In other words, the number of floating point elements comprising the vector. `DIM` must be a positive integer. The vector used to query this field must have the exact dimensions as the field itself. |\n| `DISTANCE_METRIC`  | Distance metric (`L2`, `IP`, `COSINE`). |\n\n**Optional attributes**\n\n[`HNSW`](https://arxiv.org/abs/1603.09320) supports a number of additional parameters to tune\nthe accuracy of the queries, while trading off performance.\n\n| Attribute          | Description                                                        | Default value |\n|:-------------------|:-------------------------------------------------------------------|:-------------:|\n| `M`                | Max number of outgoing edges (connections) for each node in a graph layer. On layer zero, the max number of connections will be `2 * M`. Higher values increase accuracy, but also increase memory usage and index build time. | 16 |\n| `EF_CONSTRUCTION`  | Max number of connected neighbors to consider during graph building. Higher values increase accuracy, but also increase index build time. | 200 |\n| `EF_RUNTIME`       | Max top candidates during KNN search. Higher values increase accuracy, but also increase search latency. | 10 |\n| `EPSILON`          | Relative factor that sets the boundaries in which a range query may search for candidates. That is, vector candidates whose distance from the query vector is `radius * (1 + EPSILON)` are potentially scanned, allowing more extensive search and more accurate results, at the expense of run time. | 0.01 |\n\n**Example**\n\n[code example]\n\nIn the example above, an index named `documents` is created over hashes with the key prefix `docs:` and an `HNSW` vector field named `doc_embedding` with five index attributes: `TYPE`, `DIM`, `DISTANCE_METRIC`, `M`, and `EF_CONSTRUCTION`."},{"id":"svs-vamana-index","title":"SVS-VAMANA index","role":"content","text":"Scalable Vector Search (SVS) is an Intel project featuring a graph-based vector search algorithm that is optimized to work with compression methods to reduce memory usage. You can read more about the project [here](https://intel.github.io/ScalableVectorSearch/intro.html). Support for SVS-VAMANA indexing was added in Redis 8.2.\n\nChoose the `SVS-VAMANA` index type when all of the following requirements apply:\n- High search performance and scalability are more important than exact search accuracy (similar to HNSW)\n- Reduced memory usage\n- Performance optimizations for Intel hardware\n\n**Required attributes**\n\n| Attribute          | Description                              |\n|:-------------------|:-----------------------------------------|\n| `TYPE`             | Vector type (`FLOAT16` or `FLOAT32`). Note: `COMPRESSION` is supported with both types. |\n| `DIM`              | The width, or number of dimensions, of the vector embeddings stored in this field. In other words, the number of floating point elements comprising the vector. `DIM` must be a positive integer. The vector used to query this field must have the exact same dimensions as the field itself.  |\n| `DISTANCE_METRIC`  | Distance metric (`L2`, `IP`, `COSINE`).  |\n\n**Optional attributes**\n\n`SVS-VAMANA` supports a number of additional parameters to tune the accuracy of the queries.\n\n| Attribute                  | Description                              | Default value |\n|:---------------------------|:-----------------------------------------|:-------------:|\n| `COMPRESSION`              | Compression algorithm; one of `LVQ8`, `LVQ4`, `LVQ4x4`, `LVQ4x8`, `LeanVec4x8`, or `LeanVec8x8`. See [this page](https://redis.io/docs/latest/develop/ai/search-and-query/vectors/svs-compression) for more information. | none |\n| `CONSTRUCTION_WINDOW_SIZE` | The search window size to use during graph construction. A higher search window size will yield a higher quality graph since more overall vertexes are considered, but will increase construction time. | 200 |\n| `GRAPH_MAX_DEGREE`         | Sets the maximum number of edges per node; equivalent to `HNSW’s M*2`. A higher max degree may yield a higher quality graph in terms of recall for performance, but the memory footprint of the graph is directly proportional to the maximum degree. | 32 |\n| `SEARCH_WINDOW_SIZE`       | The size of the search window; the same as `HSNW's EF_RUNTIME`. Increasing the search window size and capacity generally yields more accurate but slower search results. | 10 |\n| `EPSILON`                  | The range search approximation factor; the same as `HSNW's EPSILON`. | 0.01 |\n| `TRAINING_THRESHOLD`       | Number of vectors needed to learn compression parameters. Applicable only when used with `COMPRESSION`. Increase if recall is low. Note: setting this too high may slow down search.If a value is provided, it must be less than `100 * DEFAULT_BLOCK_SIZE`, where `DEFAULT_BLOCK_SIZE` is 1024. | `10 * DEFAULT_BLOCK_SIZE` |\n| `REDUCE`              | The dimension used when using `LeanVec4x8` or `LeanVec8x8` compression for dimensionality reduction. If a value is provided, it should be less than `DIM`. Lowering it can speed up search and reduce memory use. | `DIM / 2` |\n\n> [!WARNING]\n> Some advanced vector compression features may depend on hardware or Intel's proprietary optimizations. Intel's proprietary LVQ and LeanVec optimizations are not available in Redis Open Source. On non-Intel platforms and Redis Open Source platforms, `SVS-VAMANA` with `COMPRESSION` will fall back to basic, 8-bit scalar quantization implementation: all values in a vector are scaled using the global minimum and maximum, and then each dimension is quantized independently into 256 levels using 8-bit precision.\n\n**Example**\n\n[code example]\n\nIn the example above, an index named `documents` is created over hashes with the key prefix `docs:` and an `SVS-VAMANA` vector field named `doc_embedding` with six index attributes: `TYPE`, `DIM`, `DISTANCE_METRIC`, `GRAPH_MAX_DEGREE`, `CONSTRUCTION_WINDOW_SIZE` and `COMPRESSION`."},{"id":"distance-metrics","title":"Distance metrics","role":"content","text":"Redis supports three popular distance metrics to measure the degree of similarity between two vectors $u$, $v$ $\\in \\mathbb{R}^n$, where $n$ is the length of the vectors:\n\n| Distance metric | Description | Mathematical representation |\n|:--------------- |:----------- |:--------------------------- |\n| `L2` | Euclidean distance between two vectors. | $d(u, v) = \\sqrt{ \\displaystyle\\sum_{i=1}^n{(u_i - v_i)^2}}$ |\n| `IP` | Inner product of two vectors. | $d(u, v) = 1 -u\\cdot v$ |\n| `COSINE` | Cosine distance of two vectors. | $d(u, v) = 1 -\\frac{u \\cdot v}{\\lVert u \\rVert \\lVert v  \\rVert}$ |\n\nThe above metrics calculate distance between two vectors, where the smaller the value is, the closer the two vectors are in the vector space."},{"id":"store-and-update-vectors","title":"Store and update vectors","role":"content","text":"On index creation, the `<storage_type>` dictates how vector and metadata are structured and loaded into Redis."},{"id":"hash","title":"Hash","role":"content","text":"Store or update vectors and any metadata in [hashes](https://redis.io/docs/latest/develop/data-types/hashes) using the [`HSET`](https://redis.io/docs/latest/commands/hset) command.\n\n**Example**\n\n[code example]\n\n> [!TIP]\n> Hash values are stored as binary-safe strings. The value `<vector_bytes>` represents the vector's underlying memory buffer.\n\nA common method for converting vectors to bytes uses the [redis-py](https://redis.readthedocs.io/en/stable/examples/search_vector_similarity_examples.html) client library and the Python [NumPy](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.tobytes.html) library.\n\n**Example**\n\n[code example]\n\n> [!TIP]\n> The vector blob size must match the dimension and float type of the vector field specified in the index's schema; otherwise, indexing will fail."},{"id":"json","title":"JSON","role":"content","text":"You can store or update vectors and any associated metadata in [JSON](https://redis.io/docs/latest/develop/data-types/json) using the [`JSON.SET`](https://redis.io/docs/latest/commands/json.set) command.\n\nTo store vectors in Redis as JSON, you store the vector as a JSON array of floats. Note that this differs from vector storage in Redis hashes, which are instead stored as raw bytes.\n\n**Example**\n\n[code example]\n\nOne of the benefits of JSON is schema flexibility. As of v2.6.1, JSON supports multi-value indexing.\nThis allows you to index multiple vectors under the same [JSONPath](https://redis.io/docs/latest/develop/data-types/json/path).\n\nHere are some examples of multi-value indexing with vectors:\n\n**Multi-value indexing example**\n\n[code example]\n\nAdditional information and examples are available in the [Indexing JSON documents](https://redis.io/docs/latest/develop/ai/search-and-query/indexing#index-json-arrays-as-vector) section."},{"id":"search-with-vectors","title":"Search with vectors","role":"content","text":"You can run vector search queries with the [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) or [`FT.AGGREGATE`](https://redis.io/docs/latest/commands/ft.aggregate) commands.\n\nTo issue a vector search query with `FT.SEARCH`, you must set the `DIALECT` option to >= `2`. See the [dialects documentation](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/dialects) for more information."},{"id":"knn-vector-search","title":"KNN vector search","role":"content","text":"KNN vector search finds the top k nearest neighbors to a query vector. It has the following syntax:\n\n**Syntax**\n\n[code example]\n**Parameters**\n\n| Parameter         | Description                                                                                       |\n|:------------------|:--------------------------------------------------------------------------------------------------|\n| `index_name`  | Name of the index.  |\n| `primary_filter_query`  | [Filter](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#filters) criteria. Use `*` when no filters are required.  |\n| `top_k` | Number of nearest neighbors to fetch from the index.  |\n| `vector_field`  | Name of the vector field to search against.  |\n| `vector_blob_param`  | The query vector, passed in as a blob of raw bytes. The blob's byte size must match the vector field's dimensions and type.  |\n| `vector_query_params` (optional) | An optional section for marking one or more vector query parameters passed through the `PARAMS` section. Valid parameters should be provided as key-value pairs. See which [runtime query params](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#runtime-query-params) are supported for each vector index type.  |\n| `distance_field` (optional) | The optional distance field name used in the response and/or for sorting. By default, the distance field name is `__<vector_field>_score` and it can be used for sorting without using `AS <distance_field>` in the query.  |\n| `vector_query_params_count` | The number of vector query parameters.  |\n| `vector_query_param_name` | The name of the vector query parameter.  |\n| `vector_query_param_value` | The value of the vector query parameter.  |\n\n**Example**\n\n[code example]\n\n**Use query attributes**\n\nAlternatively, as of v2.6, `<vector_query_params>` and `<distance_field>` name can be specified in runtime\n[query attributes](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/query_attributes) as shown below.\n\n[code example]"},{"id":"vector-range-queries","title":"Vector range queries","role":"content","text":"Vector range queries allow you to filter the index using a `radius` parameter representing the semantic distance between an input query vector and indexed vector fields. This is useful in scenarios when you don't know exactly how many nearest (`top_k`) neighbors to fetch, but you do know how similar the results should be.\n\nFor example, imagine a fraud or anomaly detection scenario where you aren't sure if there are any matches in the vector index. You can issue a vector range query to quickly check if there are any records of interest in the index within the specified radius.\n\nVector range queries operate slightly different than KNN vector queries:\n- Vector range queries can appear multiple times in a query as filter criteria.\n- Vector range queries can be a part of the `<primary_filter_query>` in KNN vector search.\n\n**Syntax**\n\n[code example]\n\n| Parameter         | Description                                                                                       |\n|:------------------|:--------------------------------------------------------------------------------------------------|\n| `index_name`  | Name of the index.  |\n| `vector_field`  | Name of the vector field in the index. |\n| `radius` or `radius_param` | The maximum semantic distance allowed between the query vector and indexed vectors. You can provide the value directly in the query, passed to the `PARAMS` section, or as a query attribute.\n| `vector_blob_param`  | The query vector, passed in as a blob of raw bytes. The blob's byte size must match the vector field's dimensions and type. |\n| `vector_query_params` (optional) | An optional section for marking one or more vector query parameters passed through the `PARAMS` section. Valid parameters should be provided as key-value pairs. See which [runtime query params](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#runtime-query-params) are supported for each vector index type.  |\n| `vector_query_params_count` | The number of vector query parameters.  |\n| `vector_query_param_name` | The name of the vector query parameter.  |\n| `vector_query_param_value` | The value of the vector query parameter.  |\n\n\n**Use query attributes**\n\nA vector range query clause can be followed by a query attributes section as follows:\n\n[code example]\n\nwhere the relevant parameters in that case are `$yield_distance_as` and `$epsilon`. Note that there is no default distance field name in range queries."},{"id":"filters","title":"Filters","role":"content","text":"Redis supports vector searches that include filters to narrow the search space based on defined criteria. If your index contains searchable fields (for example, `TEXT`, `TAG`, `NUMERIC`, `GEO`, `GEOSHAPE`, and `VECTOR`), you can perform vector searches with filters.\n\n**Supported filter types**\n\n- [Exact match](https://redis.io/docs/develop/ai/search-and-query/query/exact-match/)\n- [Numeric range](https://redis.io/docs/develop/ai/search-and-query/query/range/)\n- [Full-text](https://redis.io/docs/develop/ai/search-and-query/query/full-text/)\n- [Geospatial](https://redis.io/docs/develop/ai/search-and-query/query/geo-spatial/)\n\nYou can also [combine multiple queries](https://redis.io/docs/develop/ai/search-and-query/query/combined/) as a filter.\n\n**Syntax**\n\nVector search queries with filters follow this basic structure:\n\n[code example]\n\nwhere `<primary_filter_query>` defines document selection and filtering.\n\n**Example**\n\n[code example]"},{"id":"how-filtering-works","title":"How filtering works","role":"content","text":"Redis uses internal algorithms to optimize the filtering computation for vector search.\nThe runtime algorithm is determined by heuristics that aim to minimize query latency based on several factors derived from the query and the index.\n\n**Batches mode**\n\nBatches mode works by paginating through small batches of nearest neighbors from the index:\n- A batch of high-scoring documents from the vector index is retrieved. These documents are yielded only if the `<primary_filter_query>` is satisfied. In other words, the document must contain a similar vector and meet the filter criteria.\n- The iterative procedure terminates when `<top_k>` documents that pass the filter criteria are yielded, or after every vector in the index has been processed.\n- The batch size is determined automatically by heuristics based on `<top_k>` and the ratio between the expected number of documents in the index that pass the `<primary_filter_query>` and the vector index size.\n- The goal is to minimize the total number of batches required to get the `<top_k>` results while preserving the smallest batch size possible. Note that the batch size may change dynamically in each iteration based on the number of results that pass the filter in previous batches.\n\n**Ad-hoc brute force mode** \n\n- The score of every vector corresponding to a document that passes the filter is computed, and the `<top_k>` results are selected and returned.\n- This approach is preferable when the number of documents passing the `<primary_filter_query>` is relatively small.\n- The results of the KNN query will always be accurate in this mode, even if the underlying vector index algorithm is an approximate one.\n\nThe execution mode may switch from batch mode to ad-hoc brute-force mode during the run, based on updated estimations of relevant factors from one batch to another."},{"id":"runtime-query-parameters","title":"Runtime query parameters","role":"content","text":""},{"id":"filter-mode","title":"Filter mode","role":"content","text":"By default, Redis selects the best filter mode to optimize query execution. You can override the auto-selected policy using these optional parameters:\n\n| Parameter        | Description | Options |\n|:-----------------|:------------|:--------|\n| `HYBRID_POLICY`  | Specifies the filter mode to use during vector search with filters (hybrid). | `BATCHES` or `ADHOC_BF` |\n| `BATCH_SIZE`     | A fixed batch size to use in every iteration when the `BATCHES` policy is auto-selected or requested. | Positive integer. |"},{"id":"cluster-specific-query-parameters","title":"Cluster-specific query parameters","role":"content","text":"**$SHARD_K_RATIO**\n\nThe `$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.\n\n| Parameter        | Description | Value Range | Default |\n|:-----------------|:------------|:------------|:--------|\n| `$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 |\n\n**Important considerations:**\n\n- Only applicable to vector KNN queries in Redis cluster environments\n- Each shard must retrieve at least `max(top_k / #shards, ceil(top_k × ratio))` results\n- If the user-defined ratio is lower than `top_k / #shards`, the server ignores it and uses the minimum required ratio\n- Unbalanced shards returning fewer results than required may lead to fewer total results than requested in `top_k`\n- Invalid ratios return descriptive error messages"},{"id":"index-specific-query-parameters","title":"Index-specific query parameters","role":"content","text":"**FLAT**\n\nCurrently, there are no runtime parameters available for FLAT indexes.\n\n**HNSW**\n\nOptional runtime parameters for HNSW indexes are:\n\n| Parameter       | Description                                                                                               | Default value       |\n|:----------------|:----------------------------------------------------------------------------------------------------------|:--------------------|\n| `EF_RUNTIME`    | The maximum number of top candidates to hold during the KNN search. Higher values lead to more accurate results at the expense of a longer query runtime. | The value passed during index creation. The default is 10. |\n| `EPSILON`       | The relative factor that sets the boundaries for a vector range query. Vector candidates whose distance from the query vector is `radius * (1 + EPSILON)` are potentially scanned, allowing a more extensive search and more accurate results at the expense of runtime. | The value passed during index creation. The default is 0.01. |\n\n**SVS-VAMANA**\n\nOptional runtime parameters for SVS-VAMANA indexes are:\n\n| Parameter | Description | Default value |\n|:----------|:------------|:--------------|\n| `SEARCH_WINDOW_SIZE` | The size of the search window (applies only to KNN searches). | 10 or the value that was passed upon index creation. |\n| `EPSILON` | The range search approximation factor. | 0.01 or the value that was passed upon index creation. |\n| `USE_SEARCH_HISTORY` | When building an index, either the contents of the search buffer is used or the entire search history is used. The latter case may yield a slightly better graph at the cost of more search time. Boolean options are `OFF`, `ON`, and `AUTO`. `AUTO` is always evaluated internally as `ON`. | `AUTO` |\n| `SEARCH_BUFFER_CAPACITY` | A tuning parameter used in compressed `SVS-VAMANA` indexes, which are using a two-level compression type (`LVQ<X>x<Y>` or one of the `LeanVec` types), that determines the number of vector candidates to collect in the first level of the search, before the re-ranking level (which is the second level). | `SEARCH_WINDOW_SIZE` |"},{"id":"important-notes","title":"Important notes","role":"content","text":"> [!NOTE] Important notes\n> 1. When performing a KNN vector search, you specify `<top_k>` nearest neighbors. However, the default Redis query `LIMIT` parameter (used for pagination) is 10. In order to get `<top_k>` returned results, you must also specify `LIMIT 0 <top_k>` in your search command. See examples below.\n>\n> 2. By default, the results are sorted by their document's score. To sort by vector similarity score, use `SORTBY <distance_field>`. See examples below.\n>\n> 3. Depending on your chosen distance metric, the calculated distance between vectors in an index have different bounds. For example, `Cosine` distance is bounded by `2`, while `L2` distance is not bounded. When performing a vector range query, the best practice is to adjust the `<radius>` parameter based on your use case and required recall or precision metrics."},{"id":"vector-search-examples","title":"Vector search examples","role":"content","text":"Below are a number of examples to help you get started. For more comprehensive walkthroughs, see the [Redis vector quickstart guide](https://redis.io/docs/latest/develop/get-started/search-tutorial/vector-search) and the [Redis AI Resources](https://github.com/redis-developer/redis-ai-resources) Github repo."},{"id":"knn-vector-search-examples","title":"KNN vector search examples","role":"content","text":"Return the 10 nearest neighbor documents for which the `doc_embedding` vector field is the closest to the query vector represented by the following 4-byte blob:\n\n[code example]\n\nReturn the top 10 nearest neighbors and customize the `K` and `EF_RUNTIME` parameters using query parameters. See the \"Optional arguments\" section in [FT.SEARCH command](https://redis.io/docs/latest/commands/ft.search). Set the `EF_RUNTIME` value to 150, assuming `doc_embedding` is an `HNSW` index:\n\n[code example]\n\nAssign a custom name to the distance field (`vector_distance`) and then sort using that name:\n\n[code example]\n\nUse [query attributes](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/query_attributes) syntax to specify optional parameters and the distance field name:\n\n[code example]\n\nTo 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)."},{"id":"filter-examples","title":"Filter examples","role":"content","text":"For these examples, assume you created an index named `movies` with records of different movies and their metadata.\n\nAmong the movies that have `'Dune'` in the `title` field and `year` between `[2020, 2022]`, return the top 10 nearest neighbors, sorted by `movie_distance`:\n\n[code example]\n\nAmong the movies that have `action` as a category tag, but not `drama`, return the top 10 nearest neighbors, sorted by `movie_distance`:\n\n[code example]\n\nAmong the movies that have `drama` or `action` as a category tag, return the top 10 nearest neighbors and explicitly set the filter mode (hybrid policy) to \"ad-hoc brute force\" rather than it being auto-selected:\n\n[code example]\n\nAmong the movies that have `action` as a category tag, return the top 10 nearest neighbors and explicitly set the filter mode (hybrid policy) to \"batches\" and batch size 50 using a query parameter:\n\n[code example]\n\nRun the same query as above and use the query attributes syntax to specify optional parameters:\n\n[code example]\n\nTo 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)."},{"id":"cluster-optimization-examples","title":"Cluster optimization examples","role":"content","text":"The following examples demonstrate using `$SHARD_K_RATIO` to optimize vector search performance in Redis cluster environments.\n\nReturn the top 100 nearest neighbors with each shard providing 50% of the requested results (50 results per shard):\n\n[code example]\n\nCombine 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:\n\n[code example]\n\nUse a higher ratio for better accuracy when precision is more important than performance:\n\n[code example]\n\n[`FT.HYBRID`](https://redis.io/docs/latest/commands/ft.hybrid) also supports `SHARD_K_RATIO` as part of its `KNN` clause. The following query combines a text search for `laptop` with vector similarity, returning the top 100 nearest neighbors with each shard providing 50% of the requested results:\n\n[code example]"},{"id":"cluster-considerations-and-best-practices","title":"Cluster considerations and best practices","role":"content","text":"When using `$SHARD_K_RATIO` in Redis cluster environments, consider the following behavioral characteristics and best practices:\n\n#### Minimum results guarantee\n\nEach 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.\n\n**Example scenarios:**\n- `top_k=100`, 4 shards, `ratio=0.5` → 50 results per shard (valid)\n- `top_k=100`, 4 shards, `ratio=0.2` → 25 results per shard (minimum: 25, so valid)\n- `top_k=100`, 8 shards, `ratio=0.1` → 10 results per shard (minimum: 13, so Redis uses 13)\n\n#### Handling unbalanced shards\n\nIn 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:\n\n1. **Monitor shard distribution**: Ensure your data is relatively balanced across shards\n2. **Adjust ratio dynamically**: If you consistently get fewer results than expected, increase the ratio\n3. **Use higher ratios for critical queries**: Do this when result completeness is more important than performance\n\n#### Performance vs accuracy trade-offs\n\n| Ratio Range | Use Case | Performance | Accuracy |\n|:------------|:---------|:------------|:---------|\n| 0.1 - 0.3   | High-performance, approximate results | Excellent | Good |\n| 0.4 - 0.6   | Balanced performance and accuracy | Good | Very Good |\n| 0.7 - 1.0   | High-accuracy, precision-critical | Fair | Excellent |\n\n#### Error handling\n\nRedis returns descriptive error messages for invalid `$SHARD_K_RATIO` values:\n- Values outside the 0.1 - 1.0 range\n- Values with more than 2 decimal places\n- Non-numeric values"},{"id":"range-query-examples","title":"Range query examples","role":"content","text":"For these examples, assume you created an index named `products` with records of different products and metadata from an ecommerce site.\n\nReturn 100 products for which the distance between the `description_vector` field and the specified query vector blob is at most 5:\n\n[code example]\n\nRun the same query as above and set the `EPSILON` parameter to `0.5`, assuming `description_vector` is HNSW index, yield the vector distance between `description_vector` and the query result in a field named `vector_distance`, and sort the results by that distance.\n\n[code example]\n\nUse the vector range query as a filter: return all the documents that contain either `'shirt'` in their `type` tag with their `year` value in the range `[2020, 2022]` or a vector stored in `description_vector` whose distance from the query vector is no more than `0.8`, then sort the results by their vector distance, if it is in the range:\n\n[code example]\n\nTo 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)."},{"id":"memory-consumption-comparison","title":"Memory consumption comparison","role":"content","text":"Following is a Python+NumPy example of vector sizes for the supported vector types; `BFLOAT16`, `FLOAT16`, `FLOAT32`, and `FLOAT64`.\n\n[code example]"},{"id":"next-steps","title":"Next steps","role":"content","text":"Vector embeddings and vector search are not new concepts. Many of the largest companies have used\nvectors to represent products in ecommerce catalogs or content in advertising pipelines for well over a decade.\n\nWith the emergence of Large Language Models (LLMs) and the proliferation of applications that require advanced information\nretrieval techniques, Redis is well positioned to serve as your high performance query engine for semantic search and more.\n\nHere are some additional resources that apply vector search for different use cases:\n\n- [Retrieval augmented generation from scratch](https://github.com/redis-developer/redis-ai-resources/blob/main/python-recipes/RAG/01_redisvl.ipynb)\n- [Semantic caching](https://github.com/redis-developer/redis-ai-resources/blob/main/python-recipes/semantic-cache/00_semantic_caching_gemini.ipynb)"},{"id":"continue-learning-with-redis-university","title":"Continue learning with Redis University","role":"content","text":""}],"examples":[{"id":"create-a-vector-index-ex0","language":"plaintext","code":"FT.CREATE <index_name>\n  ON <storage_type>\n  PREFIX 1 <key_prefix>\n  SCHEMA ... <field_name> VECTOR <algorithm> <index_attribute_count> <index_attribute_name> <index_attribute_value>\n    [<index_attribute_name> <index_attribute_value> ...]","section_id":"create-a-vector-index"},{"id":"flat-index-ex0","language":"plaintext","code":"FT.CREATE documents\n  ON HASH\n  PREFIX 1 docs:\n  SCHEMA doc_embedding VECTOR FLAT 6\n    TYPE FLOAT32\n    DIM 1536\n    DISTANCE_METRIC COSINE","section_id":"flat-index"},{"id":"hnsw-index-ex0","language":"plaintext","code":"FT.CREATE documents\n  ON HASH\n  PREFIX 1 docs:\n  SCHEMA doc_embedding VECTOR HNSW 10\n    TYPE FLOAT64\n    DIM 1536\n    DISTANCE_METRIC COSINE\n    M 40\n    EF_CONSTRUCTION 250","section_id":"hnsw-index"},{"id":"svs-vamana-index-ex0","language":"plaintext","code":"FT.CREATE documents\n  ON HASH\n  PREFIX 1 docs:\n  SCHEMA doc_embedding VECTOR SVS-VAMANA 12\n    TYPE FLOAT32\n    DIM 1536\n    DISTANCE_METRIC COSINE\n    GRAPH_MAX_DEGREE 40\n    CONSTRUCTION_WINDOW_SIZE 250\n    COMPRESSION LVQ8","section_id":"svs-vamana-index"},{"id":"hash-ex0","language":"plaintext","code":"HSET docs:01 doc_embedding <vector_bytes> category sports","section_id":"hash"},{"id":"hash-ex1","language":"py","code":"import numpy as np\nfrom redis import Redis\n\nredis_client = Redis(host='localhost', port=6379)\n\n# Create a FLOAT32 vector\nvector = np.array([0.34, 0.63, -0.54, -0.69, 0.98, 0.61], dtype=np.float32)\n\n# Convert vector to bytes\nvector_bytes = vector.tobytes()\n\n# Use the Redis client to store the vector bytes and metadata at a specified key\nredis_client.hset('docs:01', mapping = {\"vector\": vector_bytes, \"category\": \"sports\"})","section_id":"hash"},{"id":"json-ex0","language":"plaintext","code":"JSON.SET docs:01 $ '{\"doc_embedding\":[0.34,0.63,-0.54,-0.69,0.98,0.61], \"category\": \"sports\"}'","section_id":"json"},{"id":"json-ex1","language":"plaintext","code":"JSON.SET docs:01 $ '{\"doc_embedding\":[[1,2,3,4], [5,6,7,8]]}'\nJSON.SET docs:01 $ '{\"chunk1\":{\"doc_embedding\":[1,2,3,4]}, \"chunk2\":{\"doc_embedding\":[5,6,7,8]}}'","section_id":"json"},{"id":"knn-vector-search-ex0","language":"plaintext","code":"FT.SEARCH <index_name>\n  <primary_filter_query>=>[KNN <top_k> @<vector_field> $<vector_blob_param> $<vector_query_params> AS <distance_field>]\n  PARAMS <query_params_count> [$<vector_blob_param> <vector_blob> <query_param_name> <query_param_value> ...]\n  SORTBY <distance_field>\n  DIALECT 2","section_id":"knn-vector-search"},{"id":"knn-vector-search-ex1","language":"plaintext","code":"FT.SEARCH documents \"*=>[KNN 10 @doc_embedding $BLOB]\" PARAMS 2 BLOB \"\\x12\\xa9\\xf5\\x6c\" DIALECT 2","section_id":"knn-vector-search"},{"id":"knn-vector-search-ex2","language":"plaintext","code":"[KNN <top_k> @<vector_field> $<vector_blob_param>]=>{$yield_distance_as: <distance_field>}","section_id":"knn-vector-search"},{"id":"vector-range-queries-ex0","language":"plaintext","code":"FT.SEARCH <index_name>\n  @<vector_field>:[VECTOR_RANGE (<radius> | $<radius_param>) $<vector_blob_param> $<vector_query_params>]\n  PARAMS <vector_query_params_count> [<vector_query_param_name> <vector_query_param_value> ...]\n  SORTBY <distance_field>\n  DIALECT 2","section_id":"vector-range-queries"},{"id":"vector-range-queries-ex1","language":"plaintext","code":"@<vector_field>: [VECTOR_RANGE (<radius> | $<radius_param>) $<vector_blob_param>]=>{$<param>: (<value> |\n    $<value_attribute>); ... }","section_id":"vector-range-queries"},{"id":"filters-ex0","language":"plaintext","code":"FT.SEARCH <index_name> <primary_filter_query>=>[...]","section_id":"filters"},{"id":"filters-ex1","language":"plaintext","code":"FT.SEARCH documents \"(@title:Sports @year:[2020 2022])=>[KNN 10 @doc_embedding $BLOB]\" PARAMS 2 BLOB \"\\x12\\xa9\\xf5\\x6c\" DIALECT 2","section_id":"filters"},{"id":"knn-vector-search-examples-ex0","language":"plaintext","code":"FT.SEARCH documents \"*=>[KNN 10 @doc_embedding $BLOB]\" PARAMS 2 BLOB \"\\x12\\xa9\\xf5\\x6c\" SORTBY __vector_score DIALECT 2","section_id":"knn-vector-search-examples"},{"id":"knn-vector-search-examples-ex1","language":"plaintext","code":"FT.SEARCH documents \"*=>[KNN $K @doc_embedding $BLOB EF_RUNTIME $EF]\" PARAMS 6 BLOB \"\\x12\\xa9\\xf5\\x6c\" K 10 EF 150 DIALECT 2","section_id":"knn-vector-search-examples"},{"id":"knn-vector-search-examples-ex2","language":"plaintext","code":"FT.SEARCH documents \"*=>[KNN 10 @doc_embedding $BLOB AS vector_distance]\" PARAMS 2 BLOB \"\\x12\\xa9\\xf5\\x6c\" SORTBY vector_distance DIALECT 2","section_id":"knn-vector-search-examples"},{"id":"knn-vector-search-examples-ex3","language":"plaintext","code":"FT.SEARCH documents \"*=>[KNN 10 @doc_embedding $BLOB]=>{$EF_RUNTIME: $EF; $YIELD_DISTANCE_AS: vector_distance}\" PARAMS 4 EF 150 BLOB \"\\x12\\xa9\\xf5\\x6c\" SORTBY vector_distance DIALECT 2","section_id":"knn-vector-search-examples"},{"id":"filter-examples-ex0","language":"plaintext","code":"FT.SEARCH movies \"(@title:Dune @year:[2020 2022])=>[KNN 10 @movie_embedding $BLOB AS movie_distance]\" PARAMS 2 BLOB \"\\x12\\xa9\\xf5\\x6c\" SORTBY movie_distance DIALECT 2","section_id":"filter-examples"},{"id":"filter-examples-ex1","language":"plaintext","code":"FT.SEARCH movies \"(@category:{action} ~@category:{drama})=>[KNN 10 @doc_embedding $BLOB AS movie_distance]\" PARAMS 2 BLOB \"\\x12\\xa9\\xf5\\x6c\" SORTBY movie_distance DIALECT 2","section_id":"filter-examples"},{"id":"filter-examples-ex2","language":"plaintext","code":"FT.SEARCH movies \"(@category:{drama | action})=>[KNN 10 @doc_embedding $BLOB HYBRID_POLICY ADHOC_BF]\" PARAMS 2 BLOB \"\\x12\\xa9\\xf5\\x6c\" SORTBY __vec_score DIALECT 2","section_id":"filter-examples"},{"id":"filter-examples-ex3","language":"plaintext","code":"FT.SEARCH movies \"(@category:{action})=>[KNN 10 @doc_embedding $BLOB HYBRID_POLICY BATCHES BATCH_SIZE $BATCH_SIZE]\" PARAMS 4 BLOB \"\\x12\\xa9\\xf5\\x6c\" BATCH_SIZE 50 DIALECT 2","section_id":"filter-examples"},{"id":"filter-examples-ex4","language":"plaintext","code":"FT.SEARCH movies \"(@category:{action})=>[KNN 10 @doc_embedding $BLOB]=>{$HYBRID_POLICY: BATCHES; $BATCH_SIZE: 50}\" PARAMS 2 BLOB \"\\x12\\xa9\\xf5\\x6c\" DIALECT 2","section_id":"filter-examples"},{"id":"cluster-optimization-examples-ex0","language":"plaintext","code":"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","section_id":"cluster-optimization-examples"},{"id":"cluster-optimization-examples-ex1","language":"plaintext","code":"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","section_id":"cluster-optimization-examples"},{"id":"cluster-optimization-examples-ex2","language":"plaintext","code":"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","section_id":"cluster-optimization-examples"},{"id":"cluster-optimization-examples-ex3","language":"plaintext","code":"FT.HYBRID products-idx SEARCH \"laptop\" VSIM @description_vector $query_vec KNN 4 K 100 SHARD_K_RATIO 0.5 PARAMS 2 query_vec \"\\x12\\xa9\\xf5\\x6c\"","section_id":"cluster-optimization-examples"},{"id":"range-query-examples-ex0","language":"plaintext","code":"FT.SEARCH products \"@description_vector:[VECTOR_RANGE 5 $BLOB]\" PARAMS 2 BLOB \"\\x12\\xa9\\xf5\\x6c\" LIMIT 0 100 DIALECT 2","section_id":"range-query-examples"},{"id":"range-query-examples-ex1","language":"plaintext","code":"FT.SEARCH products \"@description_vector:[VECTOR_RANGE 5 $BLOB]=>{$EPSILON:0.5; $YIELD_DISTANCE_AS: vector_distance}\" PARAMS 2 BLOB \"\\x12\\xa9\\xf5\\x6c\" SORTBY vector_distance LIMIT 0 100 DIALECT 2","section_id":"range-query-examples"},{"id":"range-query-examples-ex2","language":"plaintext","code":"FT.SEARCH products \"(@type:{shirt} @year:[2020 2022]) | @description_vector:[VECTOR_RANGE 0.8 $BLOB]=>{$YIELD_DISTANCE_AS: vector_distance}\" PARAMS 2 BLOB \"\\x12\\xa9\\xf5\\x6c\" SORTBY vector_distance DIALECT 2","section_id":"range-query-examples"},{"id":"memory-consumption-comparison-ex0","language":"python","code":"import numpy as np\n\n#install ml_dtypes from pip install ml-dtypes\nfrom ml_dtypes import bfloat16\n\n# random float64 100 dimensions\ndouble_precision_vec = np.random.rand(100)\n\n# for float64 and float32\nprint(f'length of float64 vector: {len(double_precision_vec.tobytes())}') # >>> 800\nprint(f'length of float32 vector: {len(double_precision_vec.astype(np.float32).tobytes())}') # >>> 400\n\n# for float16\nnp_data_type = np.float16\nhalf_precision_vec_float16 = double_precision_vec.astype(np_data_type)\nprint(f'length of float16 vector: {len(half_precision_vec_float16.tobytes())}') # >>> 200\n\n# for bfloat16\nbfloat_dtype = bfloat16\nhalf_precision_vec_bfloat16 = double_precision_vec.astype(bfloat_dtype)\nprint(f'length of bfloat16 vector: {len(half_precision_vec_bfloat16.tobytes())}') # >>> 200","section_id":"memory-consumption-comparison"}]}
{"schema_version":2,"id":"develop/ai/search-and-query/vectors/svs-compression","title":"Vector quantization and compression","url":"https://redis.io/docs/latest/develop/ai/search-and-query/vectors/svs-compression/","summary":"Vector quantization and compression for efficient memory usage and search performance","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:33:04+01:00","page_type":"content","content_hash":"692b08a1dd23595d5dc334c0ca09414fca229791da3923a8d8d5944cac29f91f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Efficient management of high-dimensional vector data is crucial for scalable search and retrieval. Advanced methods for vector quantization and compression, such as LVQ (Locally-adaptive Vector Quantization) and LeanVec, can dramatically optimize memory usage and improve search speed, without sacrificing much accuracy. This page describes practical approaches to quantizing and compressing vectors for scalable search.\n\n> [!WARNING]\n> Some advanced vector compression features may depend on hardware or Intel's proprietary optimizations. Intel's proprietary LVQ and LeanVec optimizations are not available in Redis Open Source. On non-Intel platforms and Redis Open Source platforms, `SVS-VAMANA` with `COMPRESSION` will fall back to basic, 8-bit scalar quantization implementation: all values in a vector are scaled using the global minimum and maximum, and then each dimension is quantized independently into 256 levels using 8-bit precision."},{"id":"quantization-and-compression-techniques","title":"Quantization and compression techniques","role":"content","text":""},{"id":"lvq-locally-adaptive-vector-quantization","title":"LVQ (Locally-adaptive Vector Quantization)","role":"content","text":"* **Method:** Applies per-vector normalization and scalar quantization; learns parameters directly from the data.\n* **Advantages:**\n    * Enables fast, on-the-fly distance computations.\n    * SIMD-optimized layout for efficient search.\n    * Learns compression parameters from representative vectors.\n* **Variants:**\n    * **LVQ4x4:** 8 bits per dimension, fast search, large memory savings.\n    * **LVQ8:** Faster ingestion, slower search.\n    * **LVQ4x8:** Two-level quantization for improved recall."},{"id":"leanvec-lvq-with-dimensionality-reduction","title":"LeanVec (LVQ with dimensionality reduction)","role":"content","text":"* **Method:** Combines dimensionality reduction with LVQ, applying quantization after reducing vector dimensions.\n* **Advantages:**\n    * Best suited for high-dimensional vectors.\n    * Significant speed and memory improvements.\n* **Variants:**\n    * **LeanVec4x8:** Recommended for high-dimensional datasets, fastest search and ingestion.\n    * **LeanVec8x8:** Improved recall when more granularity is needed.\n* **LeanVec Dimension:** For faster search and lower memory usage, reduce the dimension further by using the optional `REDUCE` argument. The default is typically `input dimension / 2`, but more aggressive reduction (such as `input dimension / 4`) is possible for greater efficiency."},{"id":"choosing-a-compression-type","title":"Choosing a compression type","role":"content","text":"| Compression type     | Best for                                         | Observations                                            |\n|----------------------|--------------------------------------------------|---------------------------------------------------------|\n| LVQ4x4               | Fast search and low memory use                   | Consider LeanVec for even faster search                 |\n| LeanVec4x8           | Fastest search and ingestion                     | LeanVec dimensionality reduction might reduce recall    |\n| LVQ4                 | Maximum memory saving                            | Recall might be insufficient                            |\n| LVQ8                 | Faster ingestion than LVQ4x4                     | Search likely slower than LVQ4x4                        |\n| LeanVec8x8           | Improved recall when LeanVec4x8 is insufficient  | LeanVec dimensionality reduction might reduce recall    |\n| LVQ4x8               | Improved recall when LVQ4x4 is insufficient      | Slightly worse memory savings                           |"},{"id":"two-level-compression","title":"Two-level compression","role":"content","text":"Both LVQ and LeanVec support two-level compression schemes. LVQ's two-level compression works by first quantizing each vector individually to capture its main structure, then encoding the residual error&mdash;the difference between the original and quantized vector&mdash;using a second quantization step. This allows fast search using only the first level, with the second level used for re-ranking to boost accuracy when needed.\n\nSimilarly, LeanVec uses a two-level approach: the first level reduces dimensionality and applies LVQ to speed up candidate retrieval, while the second level applies LVQ to the original high-dimensional vectors for accurate re-ranking.\n\nNote that the original full-precision embeddings are never used by either LVQ or LeanVec, as both operate entirely on compressed representations.\n\nThis two-level approach allows for:\n\n* Fast candidate retrieval using the first-level compressed vectors.\n* High-accuracy re-ranking using the second-level residuals.\n\nThe naming convention used for the configurations reflects the number of bits allocated per dimension at each level of compression."},{"id":"naming-convention-lvqbxb","title":"Naming convention: LVQ<B₁>x<B₂>","role":"content","text":"* **B₁:** Number of bits per dimension used in the first-level quantization.\n* **B₂:** Number of bits per dimension used in the second-level quantization (residual encoding).\n\n#### Examples\n\n* **LVQ4x8:**\n    * First level: 4 bits per dimension.\n    * Second level: 8 bits per dimension.\n    * Total: 12 bits per dimension (used across two stages).\n* **LVQ8:**\n    * Single-level compression only.\n    * 8 bits per dimension.\n    * No second-level residuals.\n\nSame notation is used for LeanVec."},{"id":"learning-compression-parameters-from-vector-data","title":"Learning compression parameters from vector data","role":"content","text":"The strong performance of LVQ and LeanVec stems from their ability to adapt to the structure of the input vectors. By learning compression parameters directly from the data, they achieve more accurate representations with fewer bits."},{"id":"what-does-this-mean-in-practice","title":"What does this mean in practice?","role":"content","text":"* **Initial training requirement:**\n    A minimum number of representative vectors is required during index initialization to train the compression parameters (see the [TRAINING_THRESHOLD](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#svs-vamana-index) parameter). A random sample from the dataset typically works well.\n* **Handling data drift:**\n    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."},{"id":"build-redis-open-source-with-intel-svs-support","title":"Build Redis Open Source with Intel SVS support","role":"content","text":"By default, Redis Open Source with Redis Search 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.\n\n> [!WARNING]\n> 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.\n> The LeanVec and LVQ techniques are closed source and are only available for use with Redis Open Source when distributed under the RSALv2 license.\n> For more details, please refer to the [information provided by Intel](https://github.com/intel/ScalableVectorSearch)."},{"id":"build-redis-open-source","title":"Build Redis Open Source","role":"content","text":"Follow the [Redis Open Source build instructions](https://redis.io/docs/latest/operate/oss_and_stack/install/build-stack). Before executing `make`, define the following environment variable.\n\n[code example]\n\nAlternatively, you can define the `BUILD_INTEL_SVS_OPT` variable as part of the `make` command:\n\n[code example]"}],"examples":[{"id":"build-redis-open-source-ex0","language":"sh","code":"export BUILD_INTEL_SVS_OPT=yes","section_id":"build-redis-open-source"},{"id":"build-redis-open-source-ex1","language":"sh","code":"make BUILD_INTEL_SVS_OPT=yes","section_id":"build-redis-open-source"}]}
{"schema_version":2,"id":"develop/ai/when-to-choose-redis","title":"When to choose Redis over alternatives","url":"https://redis.io/docs/latest/develop/ai/when-to-choose-redis/","summary":"Determine when Redis is the optimal choice compared to other databases","tags":[],"last_updated":"2026-10-01T15:06:58-07:00","page_type":"content","content_hash":"0eeefea9b5d5fc9ef92c6a0debc71074970002e6358339f3cf00a8a693798c0f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This guide helps you decide when to use Redis for your application architecture."},{"id":"redis-vs-pinecone","title":"Redis vs Pinecone","role":"content","text":"Choose Redis when your application needs:\n\n- State and memory in one database: Redis provides both key-value storage for application state and vector search for semantic memory in a single database.\n- Simplified architecture: Avoid managing two separate database connections (one for state, one for vectors).\n- Sub-millisecond latency: Both vector search and data operations respond in under one millisecond.\n- Unified caching and search: Store frequently accessed data alongside vector embeddings.\n- Transactional consistency: Perform atomic operations across state and memory.\n- Agent memory: Session and long-term memory without building your own vector index.\n- Semantic caching: Return cached LLM responses for semantically similar prompts.\n\nExample: An AI agent that maintains conversation history (state), performs semantic search over past conversations (vectors), and caches API responses (key-value) with sub-millisecond latency."},{"id":"redis-vs-mongodb","title":"Redis vs MongoDB","role":"content","text":"Choose Redis when your application needs:\n\n- Real-time data patterns: Use Pub/Sub, Streams, and instant cache updates.\n- In-memory performance: Access data with in-memory speed for reads and writes.\n- Simple data structures with complex queries: Work with [Lists](https://redis.io/docs/latest/develop/data-types/lists), [Sets](https://redis.io/docs/latest/develop/data-types/sets), [Sorted Sets](https://redis.io/docs/latest/develop/data-types/sorted-sets), [Hashes](https://redis.io/docs/latest/develop/data-types/hashes), and [JSON](https://redis.io/docs/latest/develop/data-types/json) documents with vector search.\n- Horizontal scalability: Scale with built-in clustering and sharding.\n\nExample: A real-time recommendation engine that processes user events via Streams, caches user profiles in Hashes, maintains session state, and performs vector similarity search on user embeddings."},{"id":"redis-vs-postgres","title":"Redis vs Postgres","role":"content","text":"Choose Redis when your application needs:\n\n- Sub-millisecond response times: Applications that require responses in under 10 milliseconds.\n- Streaming and pub/sub patterns: Real-time data processing with Redis Streams.\n- High-throughput workloads: Process millions of operations per second.\n- Flexible data modeling: Store schema-less JSON documents, time-series data, and vectors.\n- Simplified deployment: Avoid query planner tuning or index optimization.\n- Context retrieval: Give agents governed tools to query business data instead of direct database access.\n\nExample: A live analytics dashboard that ingests events via Streams, maintains counters in Sorted Sets, caches computed results, and performs real-time vector similarity search on user behavior patterns.\n\nYou don't have to replace Postgres to get these benefits. [Redis Data Integration](https://redis.io/docs/latest/develop/ai/context-engine/data-integration) streams changes from PostgreSQL and other relational databases into Redis within seconds. Postgres stays your system of record, and your application or agents read from Redis."},{"id":"decision-matrix","title":"Decision matrix","role":"content","text":"Use Redis when your application needs:\n\n- Vector search and state in one database\n- Sub-millisecond latency\n- Real-time streaming\n- Pub/Sub messaging\n- Agent memory, semantic caching, or context retrieval"},{"id":"selection-criteria","title":"Selection criteria","role":"content","text":"Use this decision tree to determine if Redis is the right choice for your use case:\n\n[code example]"},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Redis for AI applications](https://redis.io/docs/latest/develop/ai)\n- [Redis Iris context engine](https://redis.io/docs/latest/develop/ai/context-engine)\n- [Redis Streams documentation](https://redis.io/docs/latest/develop/data-types/streams)\n- [Redis JSON documentation](https://redis.io/docs/latest/develop/data-types/json)\n- [Redis client libraries](https://redis.io/docs/latest/develop/clients)"}],"examples":[{"id":"selection-criteria-ex0","language":"decision-tree {id=\"redis-selection-tree\"}","code":"id: redis-selection-tree\nscope: database-selection\nrootQuestion: agent-context\nquestions:\n    agent-context:\n        text: \"Do you need agent memory, semantic caching, or context retrieval?\"\n        whyAsk: \"Redis provides agent memory, semantic caching, and governed context retrieval for AI agents through the Redis Iris services\"\n        answers:\n            yes:\n                value: \"Yes\"\n                outcome:\n                    label: \"Choose Redis\"\n                    id: redis-agent-context\n                    sentiment: positive\n            no:\n                value: \"No\"\n                nextQuestion: state-and-vectors\n    state-and-vectors:\n        text: \"Do you need both state management and vector search in one database?\"\n        whyAsk: \"Redis combines key-value storage and vector search, eliminating the need for separate databases - this is Redis's key AI differentiator\"\n        answers:\n            yes:\n                value: \"Yes\"\n                outcome:\n                    label: \"Choose Redis\"\n                    id: redis-unified\n                    sentiment: positive\n            no:\n                value: \"No\"\n                nextQuestion: latency\n    latency:\n        text: \"Do you need sub-10ms response times?\"\n        whyAsk: \"Redis provides sub-millisecond latency for both reads and writes, which is critical for real-time applications\"\n        answers:\n            yes:\n                value: \"Yes\"\n                outcome:\n                    label: \"Choose Redis\"\n                    id: redis-latency\n                    sentiment: positive\n            no:\n                value: \"No\"\n                nextQuestion: realtime\n    realtime:\n        text: \"Do you need real-time streaming, pub/sub, or event processing?\"\n        whyAsk: \"Redis Streams and Pub/Sub provide built-in real-time messaging patterns\"\n        answers:\n            yes:\n                value: \"Yes\"\n                outcome:\n                    label: \"Choose Redis\"\n                    id: redis-realtime\n                    sentiment: positive\n            no:\n                value: \"No\"\n                nextQuestion: complexity\n    complexity:\n        text: \"Do you want minimal operational complexity?\"\n        whyAsk: \"Redis requires minimal configuration compared to traditional relational databases\"\n        answers:\n            yes:\n                value: \"Yes\"\n                outcome:\n                    label: \"Choose Redis\"\n                    id: redis-simple\n                    sentiment: positive\n            no:\n                value: \"No\"\n                outcome:\n                    label: \"Consider alternatives\"\n                    id: alternatives\n                    sentiment: indeterminate","section_id":"selection-criteria"}]}
{"schema_version":2,"id":"develop/clients/client-side-caching","title":"Client-side caching introduction","url":"https://redis.io/docs/latest/develop/clients/client-side-caching/","summary":"Server-assisted, client-side caching in Redis","aliases":["/develop/connect/clients/client-side-caching","/manual/client-side-caching/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-29T16:29:37+01:00","page_type":"content","content_hash":"9786f8abfe238f6f97388a13ecc8a7c0f975078e208d404a9bda758c4e97bee1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"*Client-side caching* reduces network traffic between\na Redis client and the server, which generally improves performance.\n\nBy default, an [application server](https://en.wikipedia.org/wiki/Application_server)\n(which sits between the user app and the database) contacts the\nRedis database server through the client library for every read request.\nThe diagram below shows the flow of communication from the user app,\nthrough the application server to the database and back again:\n\n![](https://redis.io/docs/latest/images/csc/CSCNoCache.drawio.svg)\n\nWhen you use client-side caching, the client library\nmaintains a local cache of data items as it retrieves them\nfrom the database. When the same items are needed again, the client\ncan satisfy the read requests from the cache instead of the database:\n\n![](https://redis.io/docs/latest/images/csc/CSCWithCache.drawio.svg)\n\nAccessing the cache is much faster than communicating with the database over the\nnetwork and it reduces network traffic. Client-side caching reduces\nthe load on the database server, so you may be able to run it using less hardware\nresources.\n\nAs with other forms of [caching](https://en.wikipedia.org/wiki/Cache_(computing)),\nclient-side caching works well in the very common use case where a small subset of the data\nis accessed much more frequently than the rest of the data (according\nto the [Pareto principle](https://en.wikipedia.org/wiki/Pareto_principle))."},{"id":"tracking","title":"Updating the cache when the data changes","role":"content","text":"All caching systems must implement a scheme to update data in the cache\nwhen the corresponding data changes in the main database. Redis uses an\napproach called *tracking*.\n\nWhen client-side caching is enabled, the Redis server remembers or *tracks* the set of keys\nthat each client connection has previously read. This includes cases where the client\nreads data directly, as with the [`GET`](https://redis.io/docs/latest/commands/get)\ncommand, and also where the server calculates values from the stored data,\nas with [`STRLEN`](https://redis.io/docs/latest/commands/strlen). When any client\nwrites new data to a tracked key, the server sends an invalidation message\nto all clients that have accessed that key previously. This message warns\nthe clients that their cached copies of the data are no longer valid and the clients\nwill evict the stale data in response. Next time a client reads from\nthe same key, it will access the database directly and refresh its cache\nwith the updated data.\n\n> [!NOTE]\n> If any connection from a client gets disconnected (including\n> one from a connection pool), then the client will flush all keys from the\n> client-side cache. Caching then resumes for subsequent reads from the\n> connections that are still active.\n\nThe sequence diagram below shows how two clients might interact as they\naccess and update the same key:\n\n![](https://redis.io/docs/latest/images/csc/CSCSeqDiagram.drawio.svg)"},{"id":"which-client-libraries-support-client-side-caching","title":"Which client libraries support client-side caching?","role":"content","text":"The following client libraries support CSC from the stated version onwards:\n\n| Client | Version |\n| :-- | :-- |\n| [`redis-py`](https://redis.io/docs/latest/develop/clients/redis-py/connect#connect-using-client-side-caching) | v5.1.0 |\n| [`Jedis`](https://redis.io/docs/latest/develop/clients/jedis/connect#connect-using-client-side-caching) | v5.2.0 |\n| [`node-redis`](https://redis.io/docs/latest/develop/clients/nodejs/connect#connect-using-client-side-caching) | v5.1.0 |\n| [`go-redis`](https://redis.io/docs/latest/develop/clients/go/connect#connect-using-client-side-caching) | v9.22.0 |\n\nNote that some other clients support the [`CLIENT TRACKING`](https://redis.io/docs/latest/commands/client-tracking) command to configure CSC on the server, but this does not mean they support the\nfeatures required for CSC themselves."},{"id":"which-commands-can-cache-data","title":"Which commands can cache data?","role":"content","text":"All read-only commands (with the `@read`\n[ACL category](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl))\nwill use cached data, except for the following:\n\n-   Any commands for the\n    [probabilistic](https://redis.io/docs/latest/develop/data-types/probabilistic) and\n    [time series](https://redis.io/docs/latest/develop/data-types/timeseries) data types.\n    These types are designed to be updated frequently, which means that caching\n    has little or no benefit.\n-   Non-deterministic commands such as [`HRANDFIELD`](https://redis.io/docs/latest/commands/hrandfield),\n    [`HSCAN`](https://redis.io/docs/latest/commands/hscan),\n    and [`ZRANDMEMBER`](https://redis.io/docs/latest/commands/zrandmember). By design, these commands\n    give different results each time they are called.\n-   Redis Search commands (with the `FT.*` prefix), such as\n    [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search).\n\nYou can use the [`MONITOR`](https://redis.io/docs/latest/commands/monitor) command to\ncheck the server's behavior when you are using client-side caching. Because `MONITOR` only\nreports activity from the server, you should find the first cacheable\naccess to a key causes a response from the server. However, subsequent\naccesses are satisfied by the cache, and so `MONITOR` should report no\nserver activity if client-side caching is working correctly."},{"id":"what-data-gets-cached-for-a-command","title":"What data gets cached for a command?","role":"content","text":"Broadly speaking, the data from the specific response to a command invocation\ngets cached after it is used for the first time. Subsets of that data\nor values calculated from it are retrieved from the server as usual and\nthen cached separately. For example:\n\n-   The whole string retrieved by [`GET`](https://redis.io/docs/latest/commands/get)\n    is added to the cache. Parts of the same string retrieved by\n    [`SUBSTR`](https://redis.io/docs/latest/commands/substr) are calculated on the\n    server the first time and then cached separately from the original\n    string.\n-   Using [`GETBIT`](https://redis.io/docs/latest/commands/getbit) or\n    [`BITFIELD`](https://redis.io/docs/latest/commands/bitfield) on a string\n    caches the returned values separately from the original string.\n-   For composite data types accessed by keys\n    ([hash](https://redis.io/docs/latest/develop/data-types/hashes),\n    [JSON](https://redis.io/docs/latest/develop/data-types/json),\n    [set](https://redis.io/docs/latest/develop/data-types/sets), and\n    [sorted set](https://redis.io/docs/latest/develop/data-types/sorted-sets)),\n    the whole object is cached separately from the individual fields.\n    So the results of `JSON.GET mykey $` and `JSON.GET mykey $.myfield` create\n    separate entries in the cache.\n-   Ranges from [lists](https://redis.io/docs/latest/develop/data-types/lists),\n    [streams](https://redis.io/docs/latest/develop/data-types/streams),\n    and [sorted sets](https://redis.io/docs/latest/develop/data-types/sorted-sets)\n    are cached separately from the object they form a part of. Likewise,\n    subsets returned by [`SINTER`](https://redis.io/docs/latest/commands/sinter) and\n    [`SDIFF`](https://redis.io/docs/latest/commands/sdiff) create separate cache entries.\n-   For multi-key read commands such as [`MGET`](https://redis.io/docs/latest/commands/mget),\n    the ordering of the keys is significant. For example `MGET name:1 name:2` is\n    cached separately from `MGET name:2 name:1` because the server returns the\n    values in the order you specify.\n-   Boolean or numeric values calculated from data types (for example \n    [`SISMEMBER`](https://redis.io/docs/latest/commands/sismember)) and\n    [`LLEN`](https://redis.io/docs/latest/commands/llen) are cached separately from the\n    object they refer to."},{"id":"usage-recommendations","title":"Usage recommendations","role":"syntax","text":"Like any caching system, client-side caching has some limitations:\n\n-   The cache has only a limited amount of memory available. When the limit\n    is reached, the client must *evict* potentially useful items from the\n    cache to make room for new ones.\n-   Cache misses, tracking, and invalidation messages always add a slight\n    performance penalty.\n\nBelow are some guidelines to help you use client-side caching efficiently, within these\nlimitations:\n\n-   **Use a separate connection for data that is not cache-friendly**:\n    Caching gives the most benefit\n    for keys that are read frequently and updated infrequently. However, you\n    may also have data, such as counters and scoreboards, that receives frequent\n    updates. In cases like this, the performance overhead of the invalidation\n    messages can be greater than the savings made by caching. Avoid this problem\n    by using a separate connection *without* client-side caching for any data that is\n    not cache-friendly.\n-   **Estimate how many items you can cache**: The client libraries let you\n    specify the maximum number of items you want to hold in the cache. You\n    can calculate an estimate for this number by dividing the \n    maximum desired size of the\n    cache in memory by the average size of the items you want to store\n    (use the [`MEMORY USAGE`](https://redis.io/docs/latest/commands/memory-usage)\n    command to get the memory footprint of a key). For example, if you had\n    10MB (or 10485760 bytes) available for the cache, and the average\n    size of an item was 80 bytes, you could fit approximately\n    10485760 / 80 = 131072 items in the cache. Monitor memory usage\n    on your server with a realistic test load to adjust your estimate\n    up or down.\n\n    ## Reference\n\n    The Redis server implements extra features for client-side caching that are not used by\n    the main Redis clients, but may be useful for custom clients and other\n    advanced applications. See\n    [Client-side caching reference](https://redis.io/docs/latest/develop/reference/client-side-caching)\n    for a full technical guide to all the options available for client-side caching."}],"examples":[]}
{"schema_version":2,"id":"develop/clients/connection-urls","title":"Connection URLs","url":"https://redis.io/docs/latest/develop/clients/connection-urls/","summary":"Learn the Redis connection URL format and how each client library interprets it","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-12T14:48:04+01:00","page_type":"content","content_hash":"c83144f18294158d0b63577f9a30859efdf818f677350f0e2046d91a33b8de13","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Most Redis client libraries can connect using a URL such as\n`redis://default:<password>@<host>:6379/0`. Only the basic form of the URL\nmeans the same thing in every client. Everything else, including how you\nset timeouts, TLS options, and the RESP protocol version, is specific to\neach client library.\n\nThe `redis` and `rediss` URL schemes have a\n[provisional IANA registration](https://www.iana.org/assignments/uri-schemes/prov/redis),\nbut this is not a formal standard. The registration describes the basic form\nand says that query parameters are implementation-defined and not portable\nbetween clients."},{"id":"url-format","title":"URL format","role":"content","text":"The basic form of a Redis URL is:\n\n[code example]\n\n| Part | Meaning | Default |\n| :-- | :-- | :-- |\n| `redis` or `rediss` | Use `redis` for a plain TCP connection and `rediss` for a TLS connection. | (required) |\n| `<username>` | Username for [ACL authentication](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl). | `default` |\n| `<password>` | Password for authentication. | No authentication |\n| `<host>` | Hostname or IP address of the server. Enclose an IPv6 address in square brackets, for example `[::1]`. | `localhost` |\n| `<port>` | Port number of the server. | `6379` |\n| `<db-number>` | Number of the logical database to select. | `0` |"},{"id":"write-portable-urls","title":"Write portable URLs","role":"content","text":"Follow these rules to write a URL that works the same way in every client\nthat accepts URLs:\n\n- **Use a lowercase scheme.** Lettuce, Predis, and synchronous redis-py\n  reject `REDIS://`, and ioredis only enables TLS for a lowercase `rediss://`.\n- **Always include the port.** Jedis rejects a URL without one.\n- **Always include the username with the password.** Write\n  `redis://default:<password>@<host>:6379` rather than leaving out the username.\n  The form `redis://:<password>@<host>` also means \"password only\" in every client.\n  However, the form `redis://<value>@<host>`, without a colon, is not portable:\n\n  | Client | `<value>` in `redis://<value>@<host>` means |\n  | :-- | :-- |\n  | redis-py, node-redis, ioredis, go-redis, redis-rs, Predis | Username, with no password |\n  | Lettuce, redis-rb | Password |\n  | Jedis | Error |\n\n- **Percent-encode special characters in the username and password.**\n  Encode any character that has a special meaning in URLs, such as `@`, `:`, `/`,\n  `?`, `#`, `%`, and `+`. For example, the password `p@ss:w+rd` becomes\n  `p%40ss%3Aw%2Brd`. See [Predis](#predis-php) and [redis-rb](#redis-rb-ruby)\n  for exceptions.\n- **Select the database with the path, not a query parameter.**\n  `redis://<host>:6379/2` selects database 2 in every client. The `?db=` query\n  parameter is ignored by some clients and has a different name in others.\n- **Don't put query parameters in a URL that more than one client reads.**\n  Clients recognize different parameter names, and they handle a name they\n  don't recognize differently:\n\n  | Behavior for an unknown query parameter | Clients |\n  | :-- | :-- |\n  | Ignored | node-redis, Jedis, Lettuce, redis-rs, redis-rb |\n  | Passed to the client as an option | redis-py, ioredis, Predis |\n  | Error | go-redis |\n\n- **Don't set the same option in both the URL and the client's options.**\n  Clients disagree about which one takes precedence.\n\nA URL often contains a password, so treat it as a secret. For example, keep it\nout of logs and error messages."},{"id":"client-support","title":"Client support","role":"content","text":"The following table summarizes how each client library handles URLs. See the\nsections after the table for details.\n\n| Client | URL entry point | Schemes | Unix socket | Database in query | RESP version in query |\n| :-- | :-- | :-- | :-- | :-- | :-- |\n| [redis-py](#redis-py-python) | `redis.from_url()` | `redis`, `rediss`, `unix` | `unix:///<path>?db=<n>` | `?db=` | `?protocol=2` or `3` |\n| [node-redis](#node-redis-javascript) | `createClient({ url })` | `redis`, `rediss`, `unix` | `unix:///<path>?db=<n>` | Unix socket only | Not supported |\n| [ioredis](#ioredis-javascript) | `new Redis(url)` | `redis`, `rediss` | `/<path>?db=<n>` (no scheme) | `?db=` | Not supported |\n| [Jedis](#jedis-java) | `RedisClient.create(url)` | `redis`, `rediss` | Not supported | Not supported | `?protocol=2` or `3` |\n| [Lettuce](#lettuce-java) | `RedisURI.create(url)` | `redis`, `rediss`, `redis-socket`, `redis-sentinel`, `rediss-sentinel`, and others | `redis-socket:///<path>?database=<n>` | `?database=` or `?db=` | Not supported |\n| [go-redis](#go-redis-go) | `redis.ParseURL()` | `redis`, `rediss`, `unix` | `unix:///<path>?db=<n>` | `?db=` | `?protocol=2` or `3` |\n| [redis-rs](#redis-rs-rust) | `Client::open(url)` | `redis`, `rediss`, `unix`, `redis+unix` | `unix:///<path>?db=<n>` | Unix socket only | `?protocol=2` or `3` |\n| [redis-rb](#redis-rb-ruby) | `Redis.new(url: url)` | `redis`, `rediss`, `unix` | `unix:///<path>?db=<n>` | `?db=` | Not supported |\n| [Predis](#predis-php) | `new Predis\\Client(url)` | `redis`, `rediss`, `tcp`, `tls`, `unix` | `unix:///<path>?database=<n>` | `?database=` | `?protocol=3` |\n| [SE.Redis and NRedisStack](#stackexchangeredis-and-nredisstack-net) | Not supported | | | | |\n| [hiredis](#hiredis-c) | Not supported | | | | |\n\nThe `rediss` scheme turns on TLS, but most clients only let you configure\ncertificates in code. See the TLS section of each client's connection page,\nfor example\n[Connect to your production Redis with TLS](https://redis.io/docs/latest/develop/clients/redis-py/connect#connect-to-your-production-redis-with-tls)\nfor redis-py."},{"id":"redis-py-python","title":"redis-py (Python)","role":"content","text":"Pass the URL to `redis.from_url()`, `Redis.from_url()`, or\n`ConnectionPool.from_url()`. The `redis.asyncio` module has the same functions.\n\n[code example]\n\n- Query parameters become keyword arguments for the connection. redis-py\n  converts the types of the parameters it recognizes, such as `socket_timeout`,\n  `socket_connect_timeout`, `health_check_interval`, `protocol`, and\n  `ssl_check_hostname`. It passes any other parameter as a string, and reports\n  an error for an unknown name when it creates the first connection.\n- Pass Boolean options that redis-py doesn't convert, such as\n  `decode_responses`, as keyword arguments rather than in the URL. For example,\n  `?decode_responses=False` passes the string `\"False\"`, which Python treats as\n  true.\n- A query parameter overrides the keyword argument with the same name.\n  If you specify both `?db=` and a path, `?db=` wins.\n- For TLS connections, you can set `ssl_cert_reqs` (`none`, `optional`, or\n  `required`) and `ssl_check_hostname` in the query.\n- `RedisCluster.from_url()` accepts only database 0 and doesn't support Unix\n  sockets. There is no URL form for Sentinel.\n\nSee the\n[`from_url()` reference](https://redis.readthedocs.io/en/stable/connections.html#redis.Redis.from_url)\nfor more information."},{"id":"node-redis-javascript","title":"node-redis (JavaScript)","role":"content","text":"Pass the URL in the `url` option of `createClient()`:\n\n[code example]\n\n- node-redis ignores all query parameters in `redis://` and `rediss://` URLs.\n  Set other options, such as `RESP`, in the options object.\n- Values in the URL override the `username`, `password`, and `database`\n  options.\n- For a Unix socket, use `unix:///<path>`. You can add the database with\n  `?db=<n>`.\n- For a cluster, set `url` for each of the `rootNodes`. The credentials in\n  these URLs are only used for the initial connections, so set credentials for\n  the other nodes in the `defaults` option. There is no URL form for Sentinel."},{"id":"ioredis-javascript","title":"ioredis (JavaScript)","role":"content","text":"Pass the URL as an argument to the `Redis` constructor:\n\n[code example]\n\n- ioredis doesn't reject other schemes. It adds `redis://` to the start of\n  any string that doesn't begin with `redis://` or `rediss://`, so a URL such as\n  `unix:///tmp/redis.sock` is read as a host named `unix`.\n- For a Unix socket, pass the socket path without a scheme, for example\n  `new Redis('/tmp/redis.sock?db=1')`.\n- ioredis passes query parameters to the client as options, with string\n  values. Set non-string options, such as `protocol`, in the options object\n  instead.\n- If you pass the URL before the options object, values in the URL take\n  precedence.\n- A `rediss://` URL for a cluster startup node doesn't turn on TLS. Set the\n  `tls` option in `redisOptions` instead."},{"id":"jedis-java","title":"Jedis (Java)","role":"content","text":"Pass the URL to `RedisClient.create()`:\n\n[code example]\n\n- The URL must include a port.\n- For a password with no username, use `redis://:<password>@<host>:<port>`.\n  A URL such as `redis://<password>@<host>:<port>`, without a colon, is an error.\n- The only query parameter is `protocol`, with the value `2` or `3`.\n  Jedis ignores any other parameter, including `db`.\n- Don't add a slash after the database number. Jedis can't parse\n  `redis://<host>:6379/1/?protocol=3`.\n- There is no URL form for a Unix socket, a cluster, or Sentinel."},{"id":"lettuce-java","title":"Lettuce (Java)","role":"content","text":"Pass the URL to `RedisURI.create()` or `RedisClient.create()`:\n\n[code example]\n\n- Lettuce recognizes these schemes:\n  - `redis` for TCP.\n  - `rediss` or `redis+ssl` for TLS, and `redis+tls` for TLS with STARTTLS.\n  - `redis-socket` or `redis+socket` for a Unix socket.\n  - `redis-sentinel` or `rediss-sentinel` for Sentinel.\n- Lettuce reads `redis://<password>@<host>`, without a colon, as a password.\n- Lettuce recognizes these query parameters:\n  - `timeout`: a number with an optional unit suffix, such as `10s`. With no\n    suffix, the unit is milliseconds.\n  - `database` or `db`: the database number. This takes precedence over the\n    path.\n  - `clientName`, `libraryName`, and `libraryVersion`.\n  - `verifyPeer`: `NONE`, `CA`, or `FULL`.\n  - `sentinelMasterId`: the master name, for Sentinel.\n- For Sentinel, list the Sentinel hosts separated by commas and name the\n  master, for example\n  `redis-sentinel://<password>@<host1>:26379,<host2>:26379/0?sentinelMasterId=mymaster`.\n  The password in the URL is for the data nodes, not the Sentinels.\n- For a cluster, pass `RedisClusterClient.create()` a URL with the seed nodes\n  separated by commas, for example `redis://<password>@<host1>:7000,<host2>:7001`.\n  Lettuce doesn't apply `clientName` or the database from this URL to the\n  cluster nodes.\n\nSee [URI syntax](https://redis.github.io/lettuce/user-guide/connecting-redis/)\nin the Lettuce documentation for more information."},{"id":"go-redis-go","title":"go-redis (Go)","role":"content","text":"Parse the URL with `redis.ParseURL()` and pass the result to\n`redis.NewClient()`:\n\n[code example]\n\n- Query parameters set fields of `redis.Options`, using names in snake case\n  such as `dial_timeout`, `read_timeout`, `pool_size`, `client_name`, and\n  `protocol`. `ParseURL()` returns an error for any parameter it doesn't\n  recognize.\n- A duration parameter such as `dial_timeout` takes a number of seconds, such as\n  `5`, or a Go duration string, such as `500ms`.\n- For a `rediss` URL, `skip_verify=true` turns off certificate verification.\n  `skip_verify` is an error in a `redis` URL.\n- For a cluster, use `redis.ParseClusterURL()` and add the other seed nodes with\n  `addr=<host>:<port>` parameters. This function ignores the path and returns\n  an error for `db`.\n- For Sentinel, use `redis.ParseFailoverURL()`. The credentials in the URL are\n  for the Sentinels. Set the master name with `master_name=`, the master's\n  credentials with `username=` and `password=`, and other Sentinels with\n  `addr=`.\n\nSee the [`ParseURL()` reference](https://pkg.go.dev/github.com/redis/go-redis/v9#ParseURL)\nfor the full list of parameters."},{"id":"redis-rs-rust","title":"redis-rs (Rust)","role":"content","text":"Pass the URL to `Client::open()`:\n\n[code example]\n\n- The URL must include a host.\n- The only query parameter for a TCP connection is `protocol`, with the value\n  `2`, `3`, `resp2`, or `resp3`. If you don't set it, redis-rs uses RESP2.\n- To skip certificate verification for a `rediss` URL, add the fragment\n  `#insecure` to the end of the URL. With the `tls-rustls` feature, this also\n  needs the `tls-rustls-insecure` feature.\n- For a Unix socket, use `unix:///<path>` or `redis+unix:///<path>`. Pass the\n  database and credentials as the `db`, `user`, and `pass` query parameters,\n  not in the address.\n- For a cluster, pass a URL for each node to `ClusterClient::new()`. The URLs\n  must all have the same credentials, protocol, and TLS mode.\n\nSee the [crate documentation](https://docs.rs/redis/latest/redis/) for more\ninformation."},{"id":"redis-rb-ruby","title":"redis-rb (Ruby)","role":"content","text":"Pass the URL in the `url` option of `Redis.new`:\n\n[code example]\n\n- If you don't pass `url`, `host`, `port`, or `path`, redis-rb reads the URL from\n  the `REDIS_URL` environment variable.\n- Options that you pass to `Redis.new` take precedence over values in the URL.\n- The only query parameter is `db`. Set other options, such as `protocol`, in\n  `Redis.new`.\n- redis-rb reads `redis://<password>@<host>`, without a colon, as a password.\n- redis-rb decodes percent-encoding in the password, but not in the username.\n- For Sentinel, put the master name in place of the host and pass the\n  Sentinels in the `sentinels` option, for example\n  `Redis.new(url: 'redis://mymaster', sentinels: [...])`."},{"id":"predis-php","title":"Predis (PHP)","role":"content","text":"Pass the URL to the `Predis\\Client` constructor:\n\n[code example]\n\n- Predis only reads the credentials and database from the address for the\n  `redis` and `rediss` schemes. For `tcp`, `tls`, and `unix`, use the\n  `username`, `password`, and `database` query parameters.\n- Predis doesn't decode percent-encoding in the username or password in the\n  address. If your password contains special characters, pass it as a\n  percent-encoded `password` query parameter instead.\n- The query parameter for the database is `database`, not `db`.\n- Predis passes all query parameters to the connection, for example\n  `timeout`, `read_write_timeout`, `persistent`, and `protocol`.\n- You can set TLS options with `ssl` parameters, for example\n  `?ssl[cafile]=<path-to-ca-file>&ssl[verify_peer]=1`."},{"id":"stackexchangeredis-and-nredisstack-net","title":"StackExchange.Redis and NRedisStack (.NET)","role":"content","text":"StackExchange.Redis doesn't accept URLs. It uses its own configuration string\nformat, which is a comma-separated list of endpoints and `key=value` options.\nNRedisStack connects through StackExchange.Redis and uses the same format.\n\n[code example]\n\nIf you have a URL, convert it to a configuration string:\n\n| URL part | Configuration string |\n| :-- | :-- |\n| `rediss` scheme | `ssl=true` |\n| `<host>:<port>` | `<host>:<port>` |\n| `<username>` | `user=<username>` |\n| `<password>` | `password=<password>` |\n| `/<db-number>` | `defaultDatabase=<db-number>` |\n| `?protocol=3` | `protocol=resp3` |\n\nAlways include the port. If you set `ssl=true` and leave out the port,\nStackExchange.Redis uses port 6380, not 6379. Don't percent-encode the values.\nThe configuration string format has no way to escape a comma in a value.\n\nSee [Configuration](https://seredis.dev/Configuration) in the\nStackExchange.Redis documentation for the full list of options."},{"id":"hiredis-c","title":"hiredis (C)","role":"content","text":"hiredis doesn't accept URLs. Pass the host and port to `redisConnect()`, or the\nsocket path to `redisConnectUnix()`. You can also use `redisConnectWithOptions()`\nwith a `redisOptions` struct. See [Connect](https://redis.io/docs/latest/develop/clients/hiredis/connect)\nfor examples."}],"examples":[{"id":"url-format-ex0","language":"plaintext","code":"redis[s]://[[<username>][:<password>]@][<host>][:<port>][/<db-number>]","section_id":"url-format"},{"id":"redis-py-python-ex0","language":"python","code":"import redis\n\nr = redis.from_url(\"rediss://default:<password>@<host>:6379/0?protocol=3\")","section_id":"redis-py-python"},{"id":"node-redis-javascript-ex0","language":"js","code":"import { createClient } from 'redis';\n\nconst client = createClient({\n  url: 'rediss://default:<password>@<host>:6379/0'\n});","section_id":"node-redis-javascript"},{"id":"ioredis-javascript-ex0","language":"js","code":"import Redis from 'ioredis';\n\nconst redis = new Redis('rediss://default:<password>@<host>:6379/0');","section_id":"ioredis-javascript"},{"id":"jedis-java-ex0","language":"java","code":"RedisClient jedis = RedisClient.create(\"rediss://default:<password>@<host>:6379/0\");","section_id":"jedis-java"},{"id":"lettuce-java-ex0","language":"java","code":"RedisClient client = RedisClient.create(\"rediss://default:<password>@<host>:6379/0\");","section_id":"lettuce-java"},{"id":"go-redis-go-ex0","language":"go","code":"opt, err := redis.ParseURL(\"rediss://default:<password>@<host>:6379/0?protocol=3\")\nif err != nil {\n    panic(err)\n}\n\nclient := redis.NewClient(opt)","section_id":"go-redis-go"},{"id":"redis-rs-rust-ex0","language":"rust","code":"let client = redis::Client::open(\"rediss://default:<password>@<host>:6379/0?protocol=3\")?;","section_id":"redis-rs-rust"},{"id":"redis-rb-ruby-ex0","language":"ruby","code":"require 'redis'\n\nr = Redis.new(url: 'rediss://default:<password>@<host>:6379/0')","section_id":"redis-rb-ruby"},{"id":"predis-php-ex0","language":"php","code":"$client = new Predis\\Client('rediss://default:<password>@<host>:6379/0');","section_id":"predis-php"},{"id":"stackexchangeredis-and-nredisstack-net-ex0","language":"csharp","code":"ConnectionMultiplexer redis = ConnectionMultiplexer.Connect(\n    \"<host>:6379,user=default,password=<password>,ssl=true,defaultDatabase=0\"\n);","section_id":"stackexchangeredis-and-nredisstack-net"}]}
{"schema_version":2,"id":"develop/clients/dotnet/condexec","title":"Conditional execution","url":"https://redis.io/docs/latest/develop/clients/dotnet/condexec/","summary":"Understand how `StackExchange.Redis` uses conditional execution.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-08T15:21:05+01:00","page_type":"content","content_hash":"cfa7911d7995689e565338d7bb0ac8f8d4a89f0d544869de8b24cd035e2dc666","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Most Redis client libraries use transactions with the\n[`WATCH`](https://redis.io/docs/latest/commands/watch) command as the main way to prevent\ntwo clients writing to the same key at once (see [Transactions](https://redis.io/docs/latest/develop/using-commands/transactions) for more information). Unfortunately, this approach is\ndifficult to use explicitly in `StackExchange.Redis`. Its\n[multiplexing](https://redis.io/docs/latest/develop/clients/pools-and-muxing) system\nis highly efficient and convenient but can also cause bad interactions\nwhen different connections use watched transactions at the same time.\n\nInstead, `StackExchange.Redis` relies more heavily on conditional execution. This comes\nin two basic forms, `When` conditions and transaction conditions, both of which\nare explained in the sections below."},{"id":"when-conditions","title":"`When` conditions","role":"content","text":"Several commands have variants that only execute if the key they change\nalready exists (or alternatively, if it doesn't already exist). For\nexample, the [`SET`](https://redis.io/docs/latest/commands/set) command has the\nvariants [`SETEX`](https://redis.io/docs/latest/commands/setex) (set when the key exists),\nand [`SETNX`](https://redis.io/docs/latest/commands/setnx) (set when the key doesn't exist).\n\nInstead of providing the different variants of these commands, `StackExchange.Redis`\nlets you add a `When` condition to the basic command to access its variants.\nThe following example demonstrates this for the\n[`HashSet()`](https://redis.io/docs/latest/commands/hset) command.\n\n\n\n[code example]\n\nThe available conditions are `When.Exists`, `When.NotExists`, and the default\n`When.Always`."},{"id":"transaction-conditions","title":"Transaction conditions","role":"content","text":"`StackExchange.Redis` also supports a more extensive set of conditions that you\ncan add to transactions. They are implemented internally using\n[`WATCH`](https://redis.io/docs/latest/commands/watch) commands in a way that is\nguaranteed to be safe, without interactions between different clients.\nAlthough conditions don't provide exactly the same behavior as\nexplicit `WATCH` commands, they are convenient to use and execute\nefficiently.\n\nThe example below shows how to use the `AddCondition()` method on\na transaction to let it run only if a specified hash key does not\nalready exist. See\n[Pipelines and transactions](https://redis.io/docs/latest/develop/clients/dotnet/transpipe)\nfor more information about transactions.\n\n\n\n[code example]\n\nThe table below describes the full set of conditions you can add to\na transaction. Note that you can add more than one condition to the\nsame transaction if necessary.\n\n| Condition | Description |\n| :-- | :-- |\n| `HashEqual` | Enforces that the given hash-field must have the specified value. |\n| `HashExists` | Enforces that the given hash-field must exist. |\n| `HashNotEqual` | Enforces that the given hash-field must not have the specified value. |\n| `HashNotExists` | Enforces that the given hash-field must not exist. |\n| `KeyExists` | Enforces that the given key must exist. |\n| `KeyNotExists` | Enforces that the given key must not exist. |\n| `ListIndexEqual` | Enforces that the given list index must have the specified value. |\n| `ListIndexExists` | Enforces that the given list index must exist. |\n| `ListIndexNotEqual` | Enforces that the given list index must not have the specified value. |\n| `ListIndexNotExists` | Enforces that the given list index must not exist. |\n| `StringEqual` | Enforces that the given key must have the specified value. |\n| `StringNotEqual` | Enforces that the given key must not have the specified value. |\n| `HashLengthEqual` | Enforces that the given hash length is a certain value. |\n| `HashLengthLessThan` | Enforces that the given hash length is less than a certain value. |\n| `HashLengthGreaterThan` | Enforces that the given hash length is greater than a certain value. |\n| `StringLengthEqual` | Enforces that the given string length is a certain value. |\n| `StringLengthLessThan` | Enforces that the given string length is less than a certain value. |\n| `StringLengthGreaterThan` | Enforces that the given string length is greater than a certain value. |\n| `ListLengthEqual` | Enforces that the given list length is a certain value. |\n| `ListLengthLessThan` | Enforces that the given list length is less than a certain value. |\n| `ListLengthGreaterThan` | Enforces that the given list length is greater than a certain value. |\n| `SetLengthEqual` | Enforces that the given set cardinality is a certain value. |\n| `SetLengthLessThan` | Enforces that the given set cardinality is less than a certain value. |\n| `SetLengthGreaterThan` | Enforces that the given set cardinality is greater than a certain value. |\n| `SetContains` | Enforces that the given set contains a certain member. |\n| `SetNotContains` | Enforces that the given set does not contain a certain member. |\n| `SortedSetLengthEqual` | Enforces that the given sorted set cardinality is a certain value. |\n| `SortedSetLengthEqual` | Enforces that the given sorted set contains a certain number of members with scores in the given range. |\n| `SortedSetLengthLessThan` | Enforces that the given sorted set cardinality is less than a certain value. |\n| `SortedSetLengthLessThan` | Enforces that the given sorted set contains less than a certain number of members with scores in the given range. |\n| `SortedSetLengthGreaterThan` | Enforces that the given sorted set cardinality is greater than a certain value. |\n| `SortedSetLengthGreaterThan` | Enforces that the given sorted set contains more than a certain number of members with scores in the given range. |\n| `SortedSetContains` | Enforces that the given sorted set contains a certain member. |\n| `SortedSetNotContains` | Enforces that the given sorted set does not contain a certain member. |\n| `SortedSetEqual` | Enforces that the given sorted set member must have the specified score. |\n| `SortedSetNotEqual` | Enforces that the given sorted set member must not have the specified score. |\n| `SortedSetScoreExists` | Enforces that the given sorted set must have the given score. |\n| `SortedSetScoreNotExists` | Enforces that the given sorted set must not have the given score. |\n| `SortedSetScoreExists` | Enforces that the given sorted set must have the specified count of the given score. |\n| `SortedSetScoreNotExists` | Enforces that the given sorted set must not have the specified count of the given score. |\n| `StreamLengthEqual` | Enforces that the given stream length is a certain value. |\n| `StreamLengthLessThan` | Enforces that the given stream length is less than a certain value. |\n| `StreamLengthGreaterThan` | Enforces that the given stream length is greater than a certain value. |"}],"examples":[{"id":"when-conditions-ex0","language":"csharp","code":"bool resp7 = db.HashSet(\"Details\", \"SerialNumber\", \"12345\");\nConsole.WriteLine(resp7); // >>> true\n\ndb.HashSet(\"Details\", \"SerialNumber\", \"12345A\", When.NotExists);\nstring resp8 = db.HashGet(\"Details\", \"SerialNumber\");\nConsole.WriteLine(resp8); // >>> 12345\n\ndb.HashSet(\"Details\", \"SerialNumber\", \"12345A\");\nstring resp9 = db.HashGet(\"Details\", \"SerialNumber\");\nConsole.WriteLine(resp9); // >>> 12345A","section_id":"when-conditions"},{"id":"transaction-conditions-ex0","language":"csharp","code":"var watchedTrans = db.CreateTransaction();\n\nwatchedTrans.AddCondition(Condition.KeyNotExists(\"customer:39182\"));\n\nvar updateTask = watchedTrans.HashSetAsync(\n    \"customer:39182\",\n    new HashEntry[]{\n        new HashEntry(\"name\", \"David\"),\n        new HashEntry(\"age\", \"27\")\n    }\n);\n\nbool succeeded = db.Wait(watchedTrans.ExecuteAsync());\ndb.Wait(updateTask);\nConsole.WriteLine(succeeded); // >>> true","section_id":"transaction-conditions"}]}
{"schema_version":2,"id":"develop/clients/dotnet/connect","title":"Connect to the server","url":"https://redis.io/docs/latest/develop/clients/dotnet/connect/","summary":"Connect your .NET application to a Redis database","aliases":["/develop/connect/clients/dotnet/connect/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-13T14:53:04+01:00","page_type":"content","content_hash":"0129262f34172bee17a1215614ca6b13bbe905fe4e4c6a0940151ff29a3e9378","sections":[{"id":"basic-connection","title":"Basic connection","role":"overview","text":"You can connect to the server simply by passing a string of the\nform \"hostname:port\" to the `Connect()` method (for example,\n\"localhost:6379\"). However, you can also connect using a\n`ConfigurationOptions` parameter. Use this to specify a\nusername, password, and many other options:\n\n[code example]\n\n> [!NOTE]\n> Unlike other clients, StackExchange.Redis doesn't support\n> [Connection URLs](https://redis.io/docs/latest/develop/clients/connection-urls#stackexchangeredis-and-nredisstack-net)\n> but it does support its own similar configuration string format.\n> See [Connection URLs](https://redis.io/docs/latest/develop/clients/connection-urls#stackexchangeredis-and-nredisstack-net)\n> to learn how to convert a standard URL to a configuration string."},{"id":"connect-to-a-redis-cluster","title":"Connect to a Redis cluster","role":"content","text":"The basic connection will use the\n[Cluster API](https://redis.io/docs/latest/operate/rs/clusters/optimize/oss-cluster-api)\nif it is available without any special configuration. However, if you know\nthe addresses and ports of several cluster nodes, you can specify them all\nduring connection in the `Endpoints` parameter:\n\n[code example]"},{"id":"connect-to-redis-sentinel","title":"Connect to Redis Sentinel","role":"content","text":"To connect through [Redis Sentinel](https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel),\nlist the Sentinels in `EndPoints` and set `ServiceName` to the name of the primary that\nthe Sentinels monitor. The client asks the Sentinels for the current primary.\n\n[code example]\n\nYou can also use a configuration string, such as\n`\"localhost:26379,localhost:26380,localhost:26381,serviceName=mymaster\"`.\n\nIf the Sentinels and the data nodes have different credentials, connect to the Sentinels\nfirst with `SentinelConnect()`, using the Sentinel credentials. Then call\n`GetSentinelMasterConnection()` with the credentials for the primary and replicas:\n\n[code example]\n\nThe connection also includes the replicas that the Sentinels report. To read from a\nreplica, pass `CommandFlags.PreferReplica` (or `CommandFlags.DemandReplica`) to a read-only\ncommand. Replication is asynchronous, so a replica can return stale data.\n\n[code example]"},{"id":"connect-to-your-production-redis-with-tls","title":"Connect to your production Redis with TLS","role":"content","text":"When you deploy your application, use TLS and follow the [Redis security](https://redis.io/docs/latest/operate/oss_and_stack/management/security) guidelines.\n\nBefore connecting your application to the TLS-enabled Redis server, ensure that your certificates and private keys are in the correct format.\n\nTo convert user certificate and private key from the PEM format to `pfx`, use this command:\n\n[code example]\n\nEnter password to protect your `pfx` file.\n\nEstablish a secure connection with your Redis database using this snippet.\n\n[code example]"},{"id":"sni-hostname-selection-for-cluster-connections","title":"SNI hostname selection for cluster connections","role":"content","text":"> [!NOTE]\n> The SNI precedence scheme described in this section\n> requires `StackExchange.Redis` v3.3.1 or later.\n\nWhen you connect with TLS to a cluster that `StackExchange.Redis` discovers through\n`CLUSTER SLOTS`, each discovered node needs its own\n[SNI hostname](https://en.wikipedia.org/wiki/Server_Name_Indication). This allows a shared load\nbalancer or proxy to route the TLS handshake to the right backend. `StackExchange.Redis`\nresolves the SNI hostname for a connection in this order:\n\n1. An explicitly configured `ConfigurationOptions.SslHost` always wins.\n2. Otherwise, a `DnsEndPoint` uses its own host.\n3. Otherwise, the client falls back to the default SNI provider.\n4. If none of these is available, the client uses the endpoint address.\n\nBecause of this order, `ConfigurationOptions.SslHost` only reports a value if you set one\nexplicitly; it doesn't report a host that the client inferred for you."},{"id":"connect-using-smart-client-handoffs-sch","title":"Connect using Smart client handoffs (SCH)","role":"content","text":"*Smart client handoffs (SCH)* is a feature of Redis Cloud and\nRedis Software servers that lets them actively notify clients\nabout planned server maintenance shortly before it happens. This\nlets a client take action to avoid disruptions in service.\nSee [Smart client handoffs](https://redis.io/docs/latest/develop/clients/sch)\nfor more information about SCH.\n\n> [!NOTE]\n> SCH support in `StackExchange.Redis` requires v3.3.0 or later. In v3.3.0 and\n> v3.3.1, the SCH API is marked `[Experimental]`, and SCH is never enabled\n> unless you set `MaintenanceNotifications` yourself. From v3.4.0, the API is no\n> longer experimental, so the compiler no longer reports the `SER010`\n> diagnostic and you can remove any suppression you added for it.\n\nThe client enables SCH automatically, in `Auto` mode, when either of the\nfollowing is true:\n\n- You connect using a hostname that the client recognizes as Redis Cloud\n  (`*.cloud.redislabs.com`, `*.cloud.redis.io`, or `*.redislabs.com`) or\n  Azure Managed Redis (`*.redis.azure.net` or `*.redisenterprise.cache.azure.net`).\n- You name a provider profile with the `defaults` configuration key\n  (`defaults=enterprise`, `defaults=rediscloud`, or `defaults=amr`).\n\nFor any other endpoint, SCH is disabled by default. This includes Redis\nSoftware databases, which have no hostname pattern to recognize, and hosted\ndatabases that you reach through a CNAME, private DNS, or a proxy. For these,\neither set `defaults=enterprise`, which also applies that profile's other\ndefault settings, or enable SCH with the `MaintenanceNotifications`\nconfiguration option, in code or using the `maintNotifications` key in a\nconfiguration string:\n\n[code example]\n\nThe `ConfigurationOptions` object accepts the following SCH-related parameters:\n\n| Name | Description |\n| :-- | :-- |\n| `MaintenanceNotifications` | Whether to request SCH. The options are `Disabled` (the default, unless the client recognizes the endpoint or you set `defaults`), `Enabled` (require SCH and reject the connection, including a fallback to RESP2, if the server can't deliver it), and `Auto` (request SCH and tolerate a server that doesn't support it). |\n| `MaintenanceMovingEndpointType` | The endpoint type to request for a replacement node during a handoff. The options are `ServerDefault` (no preference), `Auto` (the default; derived from the connection's scheme and encryption), `InternalIp`, `InternalFqdn`, `ExternalIp`, `ExternalFqdn`, and `None`. |\n| `MaintenanceRelaxedTimeout` | The timeout to use for commands and connections while the server has announced maintenance. The default is 10 seconds. |\n| `MaintenanceRelaxedWindowMax` | The maximum time to keep using the relaxed timeout if no notification arrives to close the window. The default is three times `MaintenanceRelaxedTimeout`. |\n| `MaintenancePostEventRelaxedDuration` | How long to keep using the relaxed timeout after a closing notification, to cover trailing effects of the maintenance. |\n\nSubscribe to the `ConnectionMultiplexer.ServerMaintenanceEvent` event to\nobserve SCH notifications. This is the same event that some servers (such as\nAzure Cache for Redis) already use for their own maintenance notifications\n(see [Production usage](https://redis.io/docs/latest/develop/clients/dotnet/produsage#server-notification-events)),\nso check the runtime type of the event arguments to tell the two apart:\n\n[code example]\n\n`RedisConnectionException` and `RedisTimeoutException` both expose a\n`MaintenanceType` property, so you can tell whether a failure happened\nbecause of ongoing maintenance."},{"id":"multiplexing","title":"Multiplexing","role":"content","text":"Although example code typically works with a single connection,\nreal-world code often uses multiple connections at the same time.\nOpening and closing connections repeatedly is inefficient, so it is best\nto manage open connections carefully to avoid this.\n\nSeveral other\nRedis client libraries use *connection pools* to reuse a set of open\nconnections efficiently. StackExchange.Redis uses a different approach called\n*multiplexing*, which sends all client commands and responses over a\nsingle connection. StackExchange.Redis manages multiplexing for you automatically.\nThis gives high performance without requiring any extra coding.\nSee\n[Connection pools and multiplexing](https://redis.io/docs/latest/develop/clients/pools-and-muxing)\nfor more information."}],"examples":[{"id":"basic-connection-ex0","language":"csharp","code":"using StackExchange.Redis;\n\nConfigurationOptions conf = new ConfigurationOptions {\n    EndPoints = { \"localhost:6379\" },\n    User = \"yourUsername\",\n    Password = \"yourPassword\"\n};\n\nConnectionMultiplexer redis = ConnectionMultiplexer.Connect(conf);\nIDatabase db = redis.GetDatabase();\n\ndb.StringSet(\"foo\", \"bar\");\nConsole.WriteLine(db.StringGet(\"foo\")); // prints bar","section_id":"basic-connection"},{"id":"connect-to-a-redis-cluster-ex0","language":"csharp","code":"ConfigurationOptions options = new ConfigurationOptions\n{\n    //list of available nodes of the cluster along with the endpoint port.\n    EndPoints = {\n        { \"localhost\", 16379 },\n        { \"localhost\", 16380 },\n        // ...\n    },            \n};\n\nConnectionMultiplexer cluster = ConnectionMultiplexer.Connect(options);\nIDatabase db = cluster.GetDatabase();\n\ndb.StringSet(\"foo\", \"bar\");\nConsole.WriteLine(db.StringGet(\"foo\")); // prints bar","section_id":"connect-to-a-redis-cluster"},{"id":"connect-to-redis-sentinel-ex0","language":"csharp","code":"using StackExchange.Redis;\n\nConfigurationOptions conf = new ConfigurationOptions {\n    EndPoints = { \"localhost:26379\", \"localhost:26380\", \"localhost:26381\" },\n    ServiceName = \"mymaster\"\n};\n\nConnectionMultiplexer redis = ConnectionMultiplexer.Connect(conf);\nIDatabase db = redis.GetDatabase();\n\ndb.StringSet(\"foo\", \"bar\");\nConsole.WriteLine(db.StringGet(\"foo\")); // prints bar","section_id":"connect-to-redis-sentinel"},{"id":"connect-to-redis-sentinel-ex1","language":"csharp","code":"using StackExchange.Redis;\n\n// Connect to the Sentinels with the Sentinel credentials.\nConfigurationOptions sentinelConf = new ConfigurationOptions {\n    EndPoints = { \"localhost:26379\", \"localhost:26380\", \"localhost:26381\" },\n    User = \"yourSentinelUsername\",\n    Password = \"yourSentinelPassword\"\n};\nConnectionMultiplexer sentinel = ConnectionMultiplexer.SentinelConnect(sentinelConf);\n\n// Connect to the primary with the Redis credentials.\nConfigurationOptions conf = new ConfigurationOptions {\n    ServiceName = \"mymaster\",\n    User = \"yourUsername\",\n    Password = \"yourPassword\"\n};\nConnectionMultiplexer redis = sentinel.GetSentinelMasterConnection(conf);\nIDatabase db = redis.GetDatabase();","section_id":"connect-to-redis-sentinel"},{"id":"connect-to-redis-sentinel-ex2","language":"csharp","code":"// Read from a replica if one is available, otherwise from the primary.\nConsole.WriteLine(db.StringGet(\"foo\", CommandFlags.PreferReplica)); // prints bar","section_id":"connect-to-redis-sentinel"},{"id":"connect-to-your-production-redis-with-tls-ex0","language":"bash","code":"openssl pkcs12 -inkey redis_user_private.key -in redis_user.crt -export -out redis.pfx","section_id":"connect-to-your-production-redis-with-tls"},{"id":"connect-to-your-production-redis-with-tls-ex1","language":"csharp","code":"ConfigurationOptions options = new ConfigurationOptions\n{\n    EndPoints = { { \"my-redis.cloud.redislabs.com\", 6379 } },\n    User = \"default\",  // use your Redis user. More info https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl/\n    Password = \"secret\", // use your Redis password\n    Ssl = true,\n    SslProtocols = System.Security.Authentication.SslProtocols.Tls12                \n};\n\noptions.CertificateSelection += delegate\n{\n    return X509CertificateLoader.LoadPkcs12FromFile(\"redis.pfx\", \"secret\"); // use the password you specified for pfx file\n};\noptions.CertificateValidation += ValidateServerCertificate;\n\nbool ValidateServerCertificate(\n        object sender,\n        X509Certificate? certificate,\n        X509Chain? chain,\n        SslPolicyErrors sslPolicyErrors)\n{\n    if (certificate == null) {\n        return false;       \n    }\n\n    var ca = X509CertificateLoader.LoadCertificateFromFile(\"redis_ca.pem\");\n    bool verdict = (certificate.Issuer == ca.Subject);\n    if (verdict) {\n        return true;\n    }\n    Console.WriteLine(\"Certificate error: {0}\", sslPolicyErrors);\n    return false;\n}\n\nConnectionMultiplexer muxer = ConnectionMultiplexer.Connect(options);   \n            \n//Creation of the connection to the DB\nIDatabase conn = muxer.GetDatabase();\n\n//send SET command\nconn.StringSet(\"foo\", \"bar\");\n\n//send GET command and print the value\nConsole.WriteLine(conn.StringGet(\"foo\"));","section_id":"connect-to-your-production-redis-with-tls"},{"id":"connect-using-smart-client-handoffs-sch-ex0","language":"csharp","code":"var options = ConfigurationOptions.Parse(\"host:6379,maintNotifications=Auto,maintRelaxedTimeout=15\");\n\n// or, in code:\nvar options = new ConfigurationOptions {\n    EndPoints = { \"host:6379\" },\n    MaintenanceNotifications = MaintenanceNotificationMode.Auto,\n    MaintenanceRelaxedTimeout = TimeSpan.FromSeconds(15),\n};\n\nvar muxer = await ConnectionMultiplexer.ConnectAsync(options);","section_id":"connect-using-smart-client-handoffs-sch"},{"id":"connect-using-smart-client-handoffs-sch-ex1","language":"csharp","code":"muxer.ServerMaintenanceEvent += (object sender, ServerMaintenanceEvent e) => {\n    if (e is PushMaintenanceEvent pme) {\n        // A server-native SCH notification.\n        Console.WriteLine($\"SCH notification: {pme.NotificationType} at {pme.EndPoint}\");\n    } else {\n        // An older, provider-specific notification (for example, from Azure Cache for Redis).\n        Console.WriteLine($\"Maintenance event: {e.RawMessage}\");\n    }\n};","section_id":"connect-using-smart-client-handoffs-sch"}]}
{"schema_version":2,"id":"develop/clients/dotnet/error-handling","title":"Error handling","url":"https://redis.io/docs/latest/develop/clients/dotnet/error-handling/","summary":"Learn how to handle errors when using StackExchange.Redis.","tags":[],"last_updated":"2026-09-08T15:21:05+01:00","page_type":"content","content_hash":"5b672d498f9ef6de1e42d8a72b6fdfaf8b808e44267c42ead08ad653c3a06479","sections":[{"id":"overview","title":"Overview","role":"overview","text":"`StackExchange.Redis` uses **exceptions** to signal errors. Code examples in the\ndocumentation often omit error handling for brevity, but it is essential in production code.\nThis page explains how error handling works in `StackExchange.Redis` and how to apply common\nerror handling patterns.\n\nFor an overview of error types and handling strategies, see [Error handling](https://redis.io/docs/latest/develop/clients/error-handling).\nSee also [Production usage](https://redis.io/docs/latest/develop/clients/dotnet/produsage)\nfor more information on connection management, timeouts, and other aspects of\napp reliability."},{"id":"exception-types","title":"Exception types","role":"errors","text":"`StackExchange.Redis` throws exceptions to signal errors. Common exception types include:\n\n| Exception | When it occurs | Recoverable | Recommended action |\n|---|---|---|---|\n| `RedisConnectionException` | Connection refused or lost | ✅ | Retry with backoff or fall back |\n| `RedisTimeoutException` | Operation exceeded timeout | ✅ | Retry with backoff |\n| `RedisCommandException` | Invalid command or arguments | ❌ | Fix the command or arguments |\n| `RedisServerException` | Invalid operation on server | ❌ | Fix the operation or data |\n\nSee [Categories of errors](https://redis.io/docs/latest/develop/clients/error-handling#categories-of-errors)\nfor a more detailed discussion of these errors and their causes."},{"id":"applying-error-handling-patterns","title":"Applying error handling patterns","role":"content","text":"The [Error handling](https://redis.io/docs/latest/develop/clients/error-handling) overview\ndescribes four main patterns. The sections below show how to implement them in\n`StackExchange.Redis`:"},{"id":"pattern-1-fail-fast","title":"Pattern 1: Fail fast","role":"content","text":"Catch specific exceptions that represent unrecoverable errors and re-throw them (see\n[Pattern 1: Fail fast](https://redis.io/docs/latest/develop/clients/error-handling#pattern-1-fail-fast)\nfor a full description):\n\n[code example]"},{"id":"pattern-2-graceful-degradation","title":"Pattern 2: Graceful degradation","role":"content","text":"Catch specific errors and fall back to an alternative, where possible (see\n[Pattern 2: Graceful degradation](https://redis.io/docs/latest/develop/clients/error-handling#pattern-2-graceful-degradation)\nfor a full description):\n\n[code example]"},{"id":"pattern-3-retry-with-backoff","title":"Pattern 3: Retry with backoff","role":"content","text":"Retry on temporary errors such as timeouts (see\n[Pattern 3: Retry with backoff](https://redis.io/docs/latest/develop/clients/error-handling#pattern-3-retry-with-backoff)\nfor a full description):\n\n[code example]\n\nSee also [Timeouts](https://redis.io/docs/latest/develop/clients/dotnet/produsage#timeouts)\nfor more information on configuring timeouts in `StackExchange.Redis`."},{"id":"pattern-4-log-and-continue","title":"Pattern 4: Log and continue","role":"content","text":"Log non-critical errors and continue (see\n[Pattern 4: Log and continue](https://redis.io/docs/latest/develop/clients/error-handling#pattern-4-log-and-continue)\nfor a full description):\n\n[code example]"},{"id":"async-error-handling","title":"Async error handling","role":"content","text":"Error handling works the usual way with `async`/`await`, as shown in the\nexample below:\n\n[code example]"},{"id":"see-also","title":"See also","role":"related","text":"- [Error handling](https://redis.io/docs/latest/develop/clients/error-handling)\n- [Production usage](https://redis.io/docs/latest/develop/clients/dotnet/produsage)"}],"examples":[{"id":"pattern-1-fail-fast-ex0","language":"csharp","code":"using StackExchange.Redis;\n\nvar muxer = ConnectionMultiplexer.Connect(\"localhost:6379\");\nvar db = muxer.GetDatabase();\n\ntry {\n    var result = db.StringGet(\"key\");\n} catch (RedisCommandException) {\n    // This indicates a bug in the code, such as using\n    // StringGet on a list key.\n    throw;\n}","section_id":"pattern-1-fail-fast"},{"id":"pattern-2-graceful-degradation-ex0","language":"csharp","code":"try {\n    var cachedValue = db.StringGet(key);\n    return cachedValue.ToString();\n} catch (RedisConnectionException) {\n    logger.LogWarning(\"Cache unavailable, using database\");\n\n    // Fallback to database\n    return database.Get(key);\n}","section_id":"pattern-2-graceful-degradation"},{"id":"pattern-3-retry-with-backoff-ex0","language":"csharp","code":"const int maxRetries = 3;\nint retryDelay = 100;\n\nfor (int attempt = 0; attempt < maxRetries; attempt++) {\n    try {\n        return db.StringGet(key).ToString();\n    } catch (RedisTimeoutException) {\n        if (attempt < maxRetries - 1) {\n            Thread.Sleep(retryDelay);\n            retryDelay *= 2;  // Exponential backoff\n        } else {\n            throw;\n        }\n    }\n}","section_id":"pattern-3-retry-with-backoff"},{"id":"pattern-4-log-and-continue-ex0","language":"csharp","code":"try {\n    db.StringSet(key, value, TimeSpan.FromSeconds(3600));\n} catch (RedisConnectionException) {\n    logger.LogWarning($\"Failed to cache {key}, continuing without cache\");\n    // Application continues normally\n}","section_id":"pattern-4-log-and-continue"},{"id":"async-error-handling-ex0","language":"csharp","code":"using StackExchange.Redis;\n\nvar muxer = ConnectionMultiplexer.Connect(\"localhost:6379\");\nvar db = muxer.GetDatabase();\n\nasync Task<string> GetWithFallbackAsync(string key) {\n    try {\n        var result = await db.StringGetAsync(key);\n        if (result.HasValue) {\n            return result.ToString();\n        }\n    } catch (RedisConnectionException) {\n        logger.LogWarning(\"Cache unavailable\");\n        return await database.GetAsync(key);\n    }\n    \n    return await database.GetAsync(key);\n}","section_id":"async-error-handling"}]}
{"schema_version":2,"id":"develop/clients/dotnet/failover","title":"Client-side geographic failover","url":"https://redis.io/docs/latest/develop/clients/dotnet/failover/","summary":"Improve reliability using the failover features of StackExchange.Redis.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-15T09:31:49+01:00","page_type":"content","content_hash":"1cd5e3d3fe39225084983d5f3213ce4d8f487b0a17e2ab9c27043980e64f83a6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"StackExchange.Redis supports [Client-side geographic failover](https://en.wikipedia.org/wiki/Failover)\nto improve the availability of connections to Redis databases. This page explains\nhow to configure StackExchange.Redis for failover. For an overview of the concepts,\nsee the main [Client-side geographic failover](https://redis.io/docs/latest/develop/clients/failover) page."},{"id":"failover-configuration","title":"Failover configuration","role":"content","text":"Failover support is available in StackExchange.Redis v3.1.0 and later, and the\n`SER007` experimental marker was retired in v3.3.0. The failover\ntypes live in the `StackExchange.Redis.Availability` namespace, so you should add the following\n`using` directives to your source file:\n\n[code example]\n\nThe example below shows a simple case with a list of two servers,\n`redis-east` and `redis-west`, where `redis-east` is the preferred\ntarget. If `redis-east` fails, StackExchange.Redis should fail over to\n`redis-west`.\n\nA failover-capable connection is a *group* of endpoints, each of which could serve\nyour workload. Create the group using `ConnectionMultiplexer.ConnectGroupAsync()`, passing\nan array of `ConnectionGroupMember` instances instead of a single connection string,\nas shown in the example below:\n\n[code example]\n\n`ConnectGroupAsync()` returns a connection that you use\njust like a standard multiplexer, but it also handles the connection management and\nfailover transparently. The [`IDatabase`](https://redis.io/docs/latest/develop/clients/dotnet/connect)\nand `ISubscriber` instances obtained from the multiplexer also work with a group\nas they do with an individual endpoint."},{"id":"endpoint-configuration","title":"Endpoint configuration","role":"content","text":"Each endpoint is represented by a `ConnectionGroupMember`, which you can create from a\nconnection string or from a\n[`ConfigurationOptions`](https://redis.io/docs/latest/develop/clients/dotnet/connect) instance.\nUse `ConfigurationOptions` when you need to provide credentials, TLS settings, or other\noptions for each endpoint individually:\n\n[code example]\n\nThe `ConnectionGroupMember` class provides the following properties to configure each\nendpoint:\n\n| Property | Default | Description |\n| :-- | :-- | :-- |\n| `Weight` | `0` | Priority of the endpoint, with higher values being tried first (see [Selecting a failover target](https://redis.io/docs/latest/develop/clients/failover#selecting-a-failover-target) for a full description of how the weighted list is used). |\n| `HealthCheck` | Group default | Per-member [health check](#health-check-configuration) override. |\n| `CircuitBreaker` | Group default | Per-member [circuit breaker](#circuit-breaker-configuration) override. |\n| `FailbackDelay` | Group default | Per-member [failback delay](#failback-configuration) override. |\n| `FailbackHealthCheck` | Group default | Per-member [failback health check](#failback-health-checks) override. |\n| `SkipInitialHealthCheck` | `false` | If `true`, skip the health check performed when the member is first added to the group. |\n\nNote that you can also adjust weights at runtime, for example in response to changing conditions:\n\n[code example]"},{"id":"group-configuration","title":"Group configuration","role":"content","text":"Health checks, circuit breakers, and retries are each configured using\nimmutable *policy types* (`HealthCheck`, `CircuitBreaker`, and `RetryPolicy`).\nEach of the three policy types follows the same basic pattern:\n\n1. The policy type is immutable and safe to share between members.\n2. Each policy has a nested `Builder` that carries the mutable settings. A new\n   `Builder` starts from the default values, so you only set what you want to change.\n   A `Builder` converts implicitly to its policy type, so you can assign or pass it inline.\n   Alternatively, you can call the `Create()` method explicitly to create the policy.\n   `Create()` also validates the values, throwing\n   `ArgumentOutOfRangeException` or `ArgumentException` at the point of configuration if\n   a value is invalid.\n3. `MultiGroupOptions` (itself immutable, with its own `Builder`) holds the group-wide\n   defaults. `ConnectionGroupMember` contains the same properties as `MultiGroupOptions`\n   but allows you to override these shared values for each member.\n\nSupply the group-wide defaults by passing a `MultiGroupOptions` instance as the second\nargument to `ConnectGroupAsync()`:\n\n[code example]"},{"id":"circuit-breaker-configuration","title":"Circuit breaker configuration","role":"content","text":"A circuit breaker passively monitors the traffic already flowing over a connection and\ncloses the connection when it detects that the connection has become unstable\n(see [Detecting connection problems](https://redis.io/docs/latest/develop/clients/failover#detecting-connection-problems) for more information on how the\ncircuit breaker works). Configure the circuit breaker for the whole group using the\n`CircuitBreaker` option of `MultiGroupOptions`:\n\n[code example]\n\nThe `CircuitBreaker.Builder` class provides the following properties:\n\n| Property | Default | Description |\n| :-- | :-- | :-- |\n| `FailureRateThreshold` | `10` | Percentage of failures within the window that trips the breaker. |\n| `MinimumNumberOfFailures` | `1000` | Minimum number of tracked failures in the window before the breaker can trip (this avoids acting on tiny samples). |\n| `MetricsWindowSize` | `2` | Length of rolling time window (in seconds) over which successes and failures are counted. |\n\nThe circuit breaker counts transient and connection-level errors (including timeouts) as\nfailures, but counts\napplication-level errors (such as a `WRONGTYPE` for a bad command) as successes\nbecause they don't indicate an unhealthy connection.\n\nYou can also configure a circuit breaker for a single connection (outside a group) using\nthe `ConfigurationOptions.CircuitBreaker` option. Use `CircuitBreaker.None` to disable\nthe circuit breaker."},{"id":"retry-configuration","title":"Retry configuration","role":"content","text":"You can configure an `IDatabaseAsync` instance to retry commands that fail due\nto transient errors (such as temporary network delays) using the `WithRetry()` method:\n\n[code example]\n\nNote that you can enable retries only for `IDatabaseAsync`, not for the\nsynchronous equivalent `IDatabase`.\n\nIf you call `WithRetry()` without parameters, it will use the default retry\npolicy (see [Group configuration](#group-configuration) for details). However,\nyou can also pass a policy explicitly to override the defaults for an individual\ndatabase:\n\n[code example]\n\nThe `RetryPolicy.Builder` class provides the following properties:\n\n| Property | Default | Description |\n| :-- | :-- | :-- |\n| `MaxAttempts` | `3` | Maximum number of attempts (including the first) before giving up. |\n| `MaxAttemptsBeforeFailover` | `1` | Maximum number of attempts against the current member before a retry is allowed to move to a failover member (group connections only). |\n| `RetryDelay` | `1` | Delay (in seconds) between retries on the same server. |\n| `JitterMax` | `0.5` | Upper bound of the random delay (in seconds) added to each retry, to avoid stampedes. |\n| `FailoverDelay` | `5` | Maximum time (in seconds) to wait when a retry is expecting a failover. |\n| `MaxCommandRetryCategory` | `CommandRetryWriteLastWins` | The most \"dangerous\" command category that will be retried (see [Which operations are safe to retry?](#which-operations-are-safe-to-retry)) |\n| `MaxAttemptsOnWatchConflict` | `3` | Maximum number of attempts allowed for a watched transaction that keeps failing (see [Watch keys for changes](https://redis.io/docs/latest/develop/clients/dotnet/transpipe#watch-keys-for-changes) for more information). |\n\nThe retry mechanism classifies errors in the same way as the circuit breaker\n(see [Circuit breaker configuration](#circuit-breaker-configuration)). Only transient\nerrors are retried.\n\n#### Which operations are safe to retry?\n\nCommands are generally safe to retry if they are *idempotent* (that is to say, multiple invocations\nof the same command have the same result as a single invocation). This excludes\ncommands like [`INCR`](https://redis.io/docs/latest/commands/incr) that modify whatever value\nis currently stored in the database.\n\nEach command belongs to a *retry category* that describes how \"dangerous\" it is\nfrom the perspective of retrying. Categories are ordered from least to most dangerous,\nand each has its own `CommandFlags` value as described in the table below:\n\n| `CommandFlags` value | Meaning |\n|----------------------|---------|\n| `CommandRetryAlways` | Always safe to retry, regardless of connection/server state |\n| `CommandRetryConnection` | Connection-level or safe metadata (e.g. `CLIENT SETNAME`, `CONFIG GET`) |\n| `CommandRetryReadOnly` | Pure reads (e.g. `GET`) |\n| `CommandRetryWriteChecked` | Conditional writes (e.g. `SETNX`, `SET ... IFEQ`) |\n| `CommandRetryWriteLastWins` | Unconditional overwrite — last-writer-wins (e.g. `SET`) |\n| `CommandRetryWriteAccumulating` | Cumulative writes where a retry can double-apply (e.g. `INCR`, `LPUSH`) |\n| `CommandRetryServerAdmin` | Server administration (e.g. `CONFIG SET`) |\n| `CommandRetryNever` | Never retry |\n\nA policy only retries\ncommands at or below its `MaxCommandRetryCategory` level. For the built-in typed methods\n(such as `StringGet`, `StringSet`, and `HashSet`) `StackExchange.Redis` assigns the appropriate\ncategory automatically.\n\nArbitrary commands issued using `Execute()`/`ExecuteAsync()`, and Lua scripts run using\n`ScriptEvaluate()`/`ScriptEvaluateAsync()`, have side-effects that the library cannot\ninfer, so they are not retried by default. However, you can pass your own choice of\nretry category using the `flags` parameter:\n\n[code example]"},{"id":"failback-configuration","title":"Failback configuration","role":"content","text":"Each member tracks two independent pieces of state:\n\n- `IsConnected`: the last observed connectivity status of the underlying connection.\n- `IsUnhealthy`: whether the member has been disabled by a failed health check\n  or a tripped circuit breaker.\n\nA member can be selected as active only when it is both connected and\nhealthy.\n\n`MultiGroupOptions.FailbackDelay` is the minimum interval over which a member must remain healthy\n(measured from its most recent failure) before it is automatically returned to rotation.\nThis helps to protect against *flapping*, where a member is only intermittently available\nbetween repeated failures.\n\n[code example]\n\n`FailbackDelay` defaults to a value of `TimeSpan.Zero`, which means the member comes back into\nrotation as soon as it passes a health check. If you use a value of `TimeSpan.MaxValue` then\nautomatic failback is effectively disabled. However, you can use the `ResetIsUnhealthy()` or\n`TryFailoverTo()` methods to enable a member manually (see [Manual failover](#manual-failover)\nfor more information).\n\nWhen two members are otherwise equally good candidates, the group keeps the member that is\nalready active rather than switching between them.\n\n#### Failback health checks\n\nA [health check](#health-check-configuration) tells you whether a member is alive, but\nthat isn't always enough to decide whether to fail back to it. For example, in an\n[Active-Active](https://redis.io/docs/latest/operate/rs/databases/active-active) deployment, a region that\nhas just recovered can be reachable but still behind the other regions. Failing back to it\ntoo early means your app reads stale data and makes changes based on it.\n\nFrom v3.4.5, you can add a *failback health check* that a member must pass before it can\nbecome the active member. Set it for the whole group with\n`MultiGroupOptions.FailbackHealthCheck`, or for one member with\n`ConnectionGroupMember.FailbackHealthCheck`. The value is a `HealthCheck`, so you can use a\n[custom probe](#custom-health-check-probes) or the built-in\n[lag-aware check](#lag-aware-failback-check) for Redis Software. No failback health check\nis configured by default.\n\nA failback health check works differently from the main health check:\n\n- It only decides whether an inactive member is *eligible* to become active. It never\n  marks a member as unhealthy, and it never moves traffic away from the active member.\n- A member is eligible only if the check reports that it is healthy. A result of\n  unhealthy or inconclusive (for example, when the probe can't reach the service it\n  queries) leaves the member ineligible.\n- If no connected member is eligible, the group falls back to selecting from the members\n  that are alive, so a precaution against stale data doesn't cause an outage. A member\n  selected this way is *provisional*: the group switches to an eligible member as soon as\n  one is available, regardless of weight.\n- Calling `TryFailoverTo()` overrides the failback health check, in the same way that it\n  overrides `IsUnhealthy`.\n\n#### Lag-aware failback check (Redis Software only) {#lag-aware-failback-check}\n\nFor Redis Software Active-Active databases, `HealthCheck.LagAware()` uses the cluster's\n[database availability API](https://redis.io/docs/latest/operate/rs/monitoring/db-availability) to check\nwhether a member is available and has caught up with the other regions. The only option\nyou must supply is a callback that returns the cluster's management credentials:\n\n[code example]\n\nThese are the cluster's REST API credentials, not the database's. A user with the\n`db_viewer` management role is sufficient. If the cluster can't be queried (for\nexample, if the REST API is unreachable or rejects the credentials), the result is\ninconclusive, so the member stays ineligible but nothing fails over.\n\nThe `LagAwareOptions` class provides the following properties:\n\n| Property | Default | Description |\n| :-- | :-- | :-- |\n| `Credentials` | (Required) | Callback that returns the cluster management credentials as a `NetworkCredential`. |\n| `RestEndpoint` | Derived | URL of the cluster's REST API. By default, this is derived from the member's first endpoint: `https://<cluster>:9443/` for an endpoint named `redis-<port>.<cluster>`, or `https://<host>:9443/` otherwise. Set it explicitly if you connect using a hostname that doesn't follow this pattern. Redirects are never followed. |\n| `DatabaseId` | Discovered | The database ID on the cluster. By default, the database is found by matching the member's first endpoint against the cluster's databases. |\n| `LagCheck` | `LagCheckMode.Auto` | Whether to check lag as well as availability. `Auto` checks lag only for Active-Active databases, `Enabled` always checks it (so a database that isn't Active-Active is always reported unavailable), and `Disabled` checks availability only. |\n| `LagTolerance` | Cluster setting | Maximum acceptable lag. By default, no tolerance is sent, so the cluster's own `availability_lag_tolerance_ms` setting applies. |\n| `CertificateValidation` | Platform default | Callback to validate the REST API's TLS certificate. |\n| `ClientCertificate` | None | Client certificate to present if the REST API requires mutual TLS. |\n\nThe cluster's management certificate is usually self-signed, so it won't validate against\nthe platform's trusted certificates. Call `TrustIssuer()` on your `LagAwareOptions` instance\nwith the path to the cluster's certificate (or its issuer) to trust it. This is a separate\nsetting from the TLS configuration for the database connection. Similarly, use\n`SetUserPfxCertificate()` (or `SetUserPemCertificate()`, on .NET 6 and later) to load a\nclient certificate from a file.\n\n`HealthCheck.LagAware()` runs a single probe per check. To change the probe count or\ntimeout, pass `HealthCheckProbe.LagAware(options)` as the `Probe` of a\n`HealthCheck.Builder` instead.\n\nWhen the link between regions breaks, the database availability API can keep reporting\nthe database as caught up for around a minute before it detects the problem (as measured\non Redis Software 8.0). A failback during that window isn't protected by the lag check, so\nset a [`FailbackDelay`](#failback-configuration) of at least a minute alongside it."},{"id":"health-check-configuration","title":"Health check configuration","role":"content","text":"Each health check consists of one or more separate *probes*, each of which is a simple\ntest (such as a [`PING`](https://redis.io/docs/latest/commands/ping) command) to determine if the\nmember is available. The results of the separate probes are combined using a configurable\npolicy to determine whether the member is healthy. When a member fails its health check,\nit is flagged as unhealthy and traffic is routed to other healthy members instead. When\nan unhealthy member recovers, traffic can be routed to it again, after the\n[failback delay](#failback-configuration) has elapsed.\n\nConfigure health checks for the whole group using the `HealthCheck` option of\n`MultiGroupOptions`:\n\n[code example]\n\nUse the `MultiGroupOptions.HealthCheckInterval` option to set the interval between health\nchecks for all members of the group. Set this to `TimeSpan.MaxValue` if you want to\ndisable periodic health checking.\n\nThe `HealthCheck.Builder` class provides the following properties to configure each probe:\n\n| Property | Default | Description |\n| :-- | :-- | :-- |\n| `ProbeCount` | `3` | Number of probe operations to perform per health check. |\n| `ProbeTimeout` | `3` | Maximum time allowed (in seconds) for an individual probe to complete. |\n| `ProbeInterval` | `500` | Delay (in milliseconds) between consecutive failed probes. |\n| `Probe` | `Ping` | The probe operation to execute (see [Probe types](#probe-types) below). |\n| `ProbePolicy` | `AllSuccess` | Policy for evaluating multiple probe results (see [Probe policies](#probe-policies) below). |"},{"id":"probe-types","title":"Probe types","role":"content","text":"StackExchange.Redis provides the following built-in probe types:\n\n| Probe | Description |\n| :-- | :-- |\n| `HealthCheckProbe.Ping` (default) | Sends a [`PING`](https://redis.io/docs/latest/commands/ping) command. Lightweight, and recommended for most scenarios. |\n| `HealthCheckProbe.IsConnected` | Checks the socket connection status without sending any command. Even more lightweight than `Ping`, but only verifies the connection, not that Redis is responsive. |\n| `HealthCheckProbe.StringSet` | Writes a random value and reads it back to verify read/write capability. More comprehensive, but higher overhead than `Ping`. This probe automatically skips replica servers. |"},{"id":"probe-policies","title":"Probe policies","role":"content","text":"When `ProbeCount` is greater than 1, the probe policy determines how the individual\nprobe results are combined to give an overall verdict. The available policies are:\n\n| Policy | Description |\n| :-- | :-- |\n| `HealthCheckProbePolicy.AnySuccess` | Healthy if *any* probe succeeds (most lenient). |\n| `HealthCheckProbePolicy.AllSuccess` (default) | Healthy only if *all* probes succeed (strictest). |\n| `HealthCheckProbePolicy.MajoritySuccess` | Healthy if a *majority* of probes succeed. |"},{"id":"custom-health-check-probes","title":"Custom health check probes","role":"content","text":"You can supply your own health check probe by deriving a new class from\n`HealthCheckProbe`, or from `HealthCheckProbePolicy` for a custom evaluation policy. For\nexample, you might use this to integrate with external monitoring tools or to implement\nchecks that are specific to your application. See the\n[StackExchange.Redis failover documentation](https://seredis.dev/Failover.html#advanced-customization)\nfor details and examples."},{"id":"managing-members-at-runtime","title":"Managing members at runtime","role":"content","text":"Although you will typically configure all members during the initial connection, you can\nalso modify the group at runtime by casting the connection to `IConnectionGroup`. For\nexample, you can add a new datacenter before decommissioning an old one for a\nzero-downtime migration:\n\n[code example]\n\nEach `ConnectionGroupMember` also exposes its current status, which you can inspect at\nany time using the `GetMembers()` method of the connection:\n\n[code example]"},{"id":"manual-failover","title":"Manual failover","role":"content","text":"By default, the group selects the active member automatically based on weight and\nlatency. However, you can also use the `TryFailoverTo()` method to select which member to\nuse manually, for example to route traffic away from a region during maintenance:\n\n[code example]\n\n`TryFailoverTo()` returns `false` if the target member is not connected or is not part of\nthe group. While an explicit failover is active, the chosen member is preferred for all\ntraffic and weight and latency are ignored. However, if the chosen member becomes\nunavailable, the group still falls back automatically to other connected members. Explicit\nfailovers are not persisted across application restarts."},{"id":"monitoring-failover-events","title":"Monitoring failover events","role":"content","text":"You may want to take some custom action when a failover occurs. For example, you could log\na warning, increment a metric, or externally persist the connection state. Use the\n`ConnectionChanged` event to react when the active member changes:\n\n[code example]"},{"id":"pubsub-and-re-subscription","title":"Pub/Sub and re-subscription","role":"content","text":"A connection group supports [Pub/Sub](https://redis.io/docs/latest/develop/pubsub) messaging with\nautomatic re-subscription to channels during failover, so you don't have to detect\nfailovers and re-subscribe manually. When you subscribe to a channel, the subscription is\nestablished against *all* members (for immediate pickup during a failover), and the library\nfilters received messages so that you only observe messages from the active member.\nPublishing occurs only to the active member.\n\nCreate an `ISubscriber` from the connection in the usual way using the `GetSubscriber()`\nmethod, then subscribe to one or more channels:\n\n[code example]"}],"examples":[{"id":"failover-configuration-ex0","language":"csharp","code":"using StackExchange.Redis;\nusing StackExchange.Redis.Availability;","section_id":"failover-configuration"},{"id":"failover-configuration-ex1","language":"csharp","code":"// Define your Redis endpoints, with the highest weight being tried first.\nConnectionGroupMember[] members = [\n    new(\"redis-east.example.com:6379\", name: \"US East\") { Weight = 1.0 },\n    new(\"redis-west.example.com:6379\", name: \"US West\") { Weight = 0.5 }\n];\n\n// Connect to all members.\nawait using var conn = await ConnectionMultiplexer.ConnectGroupAsync(members);\n\n// Use the connection exactly as you would a normal multiplexer.\nIDatabase db = conn.GetDatabase();\nawait db.StringSetAsync(\"mykey\", \"myvalue\");\nRedisValue value = await db.StringGetAsync(\"mykey\");","section_id":"failover-configuration"},{"id":"endpoint-configuration-ex0","language":"csharp","code":"var eastConfig = new ConfigurationOptions\n{\n    EndPoints = { \"redis-east-1.example.com:6379\", \"redis-east-2.example.com:6379\" },\n    Password = \"east-password\",\n    Ssl = true,\n};\n\nvar westConfig = new ConfigurationOptions\n{\n    EndPoints = { \"redis-west-1.example.com:6379\", \"redis-west-2.example.com:6379\" },\n    Password = \"west-password\",\n    Ssl = true,\n};\n\nConnectionGroupMember[] members = [\n    new(eastConfig, name: \"US East\"),\n    new(westConfig, name: \"US West\")\n];\n\nawait using var conn = await ConnectionMultiplexer.ConnectGroupAsync(members);","section_id":"endpoint-configuration"},{"id":"endpoint-configuration-ex1","language":"csharp","code":"members[0].Weight = 1;   // Reduce preference for the local DC\nmembers[1].Weight = 10;  // Increase preference for the remote DC","section_id":"endpoint-configuration"},{"id":"group-configuration-ex0","language":"csharp","code":"HealthCheck healthCheck = new HealthCheck.Builder { ProbeCount = 5 };\nCircuitBreaker breaker = new CircuitBreaker.Builder { FailureRateThreshold = 25 };\nRetryPolicy retry = new RetryPolicy.Builder { MaxAttempts = 5 };\n\nMultiGroupOptions options = new MultiGroupOptions.Builder\n{\n    HealthCheck = healthCheck,\n    CircuitBreaker = breaker,\n    RetryPolicy = retry,\n    HealthCheckInterval = TimeSpan.FromSeconds(2),\n};\n\nawait using var conn = await ConnectionMultiplexer.ConnectGroupAsync(members, options);","section_id":"group-configuration"},{"id":"circuit-breaker-configuration-ex0","language":"csharp","code":"MultiGroupOptions options = new MultiGroupOptions.Builder\n{\n    CircuitBreaker = new CircuitBreaker.Builder\n    {\n        FailureRateThreshold = 25,                   // Trip above 25% failures.\n        MinimumNumberOfFailures = 100,               // ...but only after 100 failures in the window.\n        MetricsWindowSize = TimeSpan.FromSeconds(5), // Rolling window to measure over.\n    }\n};\n\nawait using var conn = await ConnectionMultiplexer.ConnectGroupAsync(members, options);","section_id":"circuit-breaker-configuration"},{"id":"retry-configuration-ex0","language":"csharp","code":"await using var conn = await ConnectionMultiplexer.ConnectGroupAsync(members);\n\n// Wrap the database once, then reuse the wrapper like any other IDatabaseAsync.\n// The parameterless overload uses the policy configured on the connection.\nIDatabaseAsync db = conn.GetDatabase().WithRetry();\n\n// A transient fault is retried automatically. If the group fails over in the\n// meantime, the retry lands on the new active member.\nRedisValue value = await db.StringGetAsync(\"mykey\");","section_id":"retry-configuration"},{"id":"retry-configuration-ex1","language":"csharp","code":"RetryPolicy policy = new RetryPolicy.Builder\n{\n    MaxAttempts = 5,\n    RetryDelay = TimeSpan.FromMilliseconds(200),\n    JitterMax = TimeSpan.FromMilliseconds(100),\n};\nIDatabaseAsync db = conn.GetDatabase().WithRetry(policy);","section_id":"retry-configuration"},{"id":"retry-configuration-ex2","language":"csharp","code":"// a Lua script that only reads: opt into retries\nvar value = await db.ScriptEvaluateAsync(\n    \"return redis.call('GET', KEYS[1])\",\n    keys: [key],\n    flags: CommandFlags.CommandRetryReadOnly\n);","section_id":"retry-configuration"},{"id":"failback-configuration-ex0","language":"csharp","code":"MultiGroupOptions options = new MultiGroupOptions.Builder\n{\n    FailbackDelay = TimeSpan.FromMinutes(2), // Must be healthy for 2 minutes after its last failure.\n};","section_id":"failback-configuration"},{"id":"failback-configuration-ex1","language":"csharp","code":"ConnectionGroupMember member = new(\"redis-14460.us-west.example.com:14460,password=<db-password>\", name: \"US West\")\n{\n    Weight = 10,\n    FailbackHealthCheck = HealthCheck.LagAware(new LagAwareOptions\n    {\n        // Called for every request, so the credentials can rotate.\n        Credentials = _ => new(new NetworkCredential(\"<mgmt-username>\", \"<mgmt-password>\")),\n    }),\n};","section_id":"failback-configuration"},{"id":"health-check-configuration-ex0","language":"csharp","code":"HealthCheck healthCheck = new HealthCheck.Builder\n{\n    ProbeCount = 3,                                 // Maximum probe attempts per check.\n    ProbeTimeout = TimeSpan.FromSeconds(3),         // Timeout for each probe attempt.\n    ProbeInterval = TimeSpan.FromMilliseconds(500), // Delay between failed probes.\n    Probe = HealthCheckProbe.Ping,                  // Which probe type to use.\n    ProbePolicy = HealthCheckProbePolicy.AllSuccess // How to evaluate the probe results.\n};\n\nMultiGroupOptions options = new MultiGroupOptions.Builder\n{\n    HealthCheck = healthCheck,\n    HealthCheckInterval = TimeSpan.FromSeconds(5),  // How often checks run (a group-level concern).\n};\n\nawait using var conn = await ConnectionMultiplexer.ConnectGroupAsync(members, options);","section_id":"health-check-configuration"},{"id":"managing-members-at-runtime-ex0","language":"csharp","code":"var group = (IConnectionGroup)conn;\n\n// Add a new member at runtime.\nvar newMember = new ConnectionGroupMember(\"new-dc.example.com:6379\", name: \"New Datacenter\") { Weight = 5 };\nawait group.AddAsync(newMember);\n\n// Remove a member.\ngroup.Remove(newMember);","section_id":"managing-members-at-runtime"},{"id":"managing-members-at-runtime-ex1","language":"csharp","code":"foreach (ConnectionGroupMember member in conn.GetMembers())\n{\n    Console.WriteLine($\"{member.Name}: Connected={member.IsConnected}, \" +\n                      $\"Unhealthy={member.IsUnhealthy}, Weight={member.Weight}, Latency={member.Latency}\");\n}","section_id":"managing-members-at-runtime"},{"id":"manual-failover-ex0","language":"csharp","code":"// Get the members and find the one you want to fail over to.\nConnectionGroupMember target = conn.GetMembers().First(m => m.Name == \"US West\");\n\n// Attempt to fail over to the specified member.\nif (conn.TryFailoverTo(target))\n{\n    Console.WriteLine($\"Successfully failed over to {target.Name}\");\n}\n\n// Later, pass null to remove the explicit failover and restore automatic selection.\nconn.TryFailoverTo(null);","section_id":"manual-failover"},{"id":"monitoring-failover-events-ex0","language":"csharp","code":"conn.ConnectionChanged += (sender, args) =>\n{\n    if (args.Type == GroupConnectionChangedEventArgs.ChangeType.ActiveChanged)\n    {\n        Console.WriteLine($\"Active member changed from {args.PreviousGroup?.Name ?? \"none\"} to {args.Group.Name}\");\n    }\n};","section_id":"monitoring-failover-events"},{"id":"pubsub-and-re-subscription-ex0","language":"csharp","code":"ISubscriber subscriber = conn.GetSubscriber();\n\n// If a failover happens, the subscription is automatically re-established\n// on the new active member.\nawait subscriber.SubscribeAsync(RedisChannel.Literal(\"notifications\"), (channel, message) =>\n{\n    Console.WriteLine($\"Received: {message}\");\n});\n\nawait subscriber.PublishAsync(RedisChannel.Literal(\"notifications\"), \"Hello, World!\");","section_id":"pubsub-and-re-subscription"}]}
{"schema_version":2,"id":"develop/clients/dotnet","title":"StackExchange.Redis guide (C#/.NET)","url":"https://redis.io/docs/latest/develop/clients/dotnet/","summary":"Connect your .NET application to a Redis database","aliases":["/develop/connect/clients/dotnet","/connect/clients/dotnet/","/clients/dotnet/","/develop/clients/stackexchange.redis"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-08T15:21:05+01:00","children":[{"id":"develop/clients/dotnet/connect","summary":"Connect your .NET application to a Redis database","title":"Connect to the server","url":"https://redis.io/docs/latest/develop/clients/dotnet/connect/"},{"id":"develop/clients/dotnet/nredisstack","summary":"Access Redis Open Source modules","title":"NRedisStack extensions guide","url":"https://redis.io/docs/latest/develop/clients/dotnet/nredisstack/"},{"id":"develop/clients/dotnet/vecsets","summary":"Index and query embeddings with Redis vector sets","title":"Vector set embeddings","url":"https://redis.io/docs/latest/develop/clients/dotnet/vecsets/"},{"id":"develop/clients/dotnet/transpipe","summary":"Learn how to use Redis pipelines and transactions","title":"Pipelines and transactions","url":"https://redis.io/docs/latest/develop/clients/dotnet/transpipe/"},{"id":"develop/clients/dotnet/failover","summary":"Improve reliability using the failover features of StackExchange.Redis.","title":"Client-side geographic failover","url":"https://redis.io/docs/latest/develop/clients/dotnet/failover/"},{"id":"develop/clients/dotnet/condexec","summary":"Understand how `StackExchange.Redis` uses conditional execution.","title":"Conditional execution","url":"https://redis.io/docs/latest/develop/clients/dotnet/condexec/"},{"id":"develop/clients/dotnet/error-handling","summary":"Learn how to handle errors when using StackExchange.Redis.","title":"Error handling","url":"https://redis.io/docs/latest/develop/clients/dotnet/error-handling/"},{"id":"develop/clients/dotnet/produsage","summary":"Get your `StackExchange.Redis` app ready for production","title":"Production usage","url":"https://redis.io/docs/latest/develop/clients/dotnet/produsage/"}],"page_type":"content","content_hash":"272c6201292a4f50533ddf2d206e6fbf18bd1745fd0745c90f3648e4c034c03d","sections":[{"id":"install","title":"Install","role":"setup","text":"Using the `dotnet` CLI, run:\n\n[code example]"},{"id":"connect-and-test","title":"Connect and test","role":"content","text":"Add the following imports to your source file:\n\nFoundational: Import required SE.Redis namespaces for Redis client functionality\n\n**Difficulty:** Beginner\n\n**Available in:** C#, C#\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n\n\nConnect to localhost on port 6379. The client supports both synchronous and asynchronous commands.\n\nFoundational: Connect to a Redis server and establish a client connection\n\n**Difficulty:** Beginner\n\n**Available in:** C#, C#\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n\n\nYou can test the connection by storing and retrieving a simple string.\n\nFoundational: Set and retrieve string values using SET and GET commands\n\n**Difficulty:** Beginner\n\n**Available in:** C#, C#\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n\n\nStore and retrieve a HashMap.\n\nFoundational: Store and retrieve hash data structures using HSET and HGETALL\n\n**Difficulty:** Beginner\n\n**Available in:** C#, C#\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]"},{"id":"more-information","title":"More information","role":"content","text":"See the other pages in this section for more information and examples."}],"examples":[{"id":"install-ex0","language":"bash","code":"dotnet add package StackExchange.Redis","section_id":"install"},{"id":"connect-and-test-ex0","language":"csharp","code":"using StackExchange.Redis;","section_id":"connect-and-test"},{"id":"connect-and-test-ex1","language":"csharp","code":"using StackExchange.Redis;","section_id":"connect-and-test"},{"id":"connect-and-test-ex2","language":"csharp","code":"using StackExchange.Redis;","section_id":"connect-and-test"},{"id":"connect-and-test-ex3","language":"csharp","code":"using StackExchange.Redis;","section_id":"connect-and-test"},{"id":"connect-and-test-ex4","language":"csharp","code":"var muxer = await ConnectionMultiplexer.ConnectAsync(\"localhost:6379\");\n        var db = muxer.GetDatabase();","section_id":"connect-and-test"},{"id":"connect-and-test-ex5","language":"csharp","code":"var muxer = ConnectionMultiplexer.Connect(\"localhost:6379\");\n        var db = muxer.GetDatabase();","section_id":"connect-and-test"},{"id":"connect-and-test-ex6","language":"csharp","code":"var muxer = await ConnectionMultiplexer.ConnectAsync(\"localhost:6379\");\n        var db = muxer.GetDatabase();","section_id":"connect-and-test"},{"id":"connect-and-test-ex7","language":"csharp","code":"var muxer = ConnectionMultiplexer.Connect(\"localhost:6379\");\n        var db = muxer.GetDatabase();","section_id":"connect-and-test"},{"id":"connect-and-test-ex8","language":"csharp","code":"await db.StringSetAsync(\"foo\", \"bar\");\n        string? fooResult = await db.StringGetAsync(\"foo\");\n        Console.WriteLine(fooResult); // >>> bar","section_id":"connect-and-test"},{"id":"connect-and-test-ex9","language":"csharp","code":"db.StringSet(\"foo\", \"bar\");\n        Console.WriteLine(db.StringGet(\"foo\")); // >>> bar","section_id":"connect-and-test"},{"id":"connect-and-test-ex10","language":"csharp","code":"await db.StringSetAsync(\"foo\", \"bar\");\n        string? fooResult = await db.StringGetAsync(\"foo\");\n        Console.WriteLine(fooResult); // >>> bar","section_id":"connect-and-test"},{"id":"connect-and-test-ex11","language":"csharp","code":"db.StringSet(\"foo\", \"bar\");\n        Console.WriteLine(db.StringGet(\"foo\")); // >>> bar","section_id":"connect-and-test"},{"id":"connect-and-test-ex12","language":"csharp","code":"var hash = new HashEntry[] { \n            new HashEntry(\"name\", \"John\"), \n            new HashEntry(\"surname\", \"Smith\"),\n            new HashEntry(\"company\", \"Redis\"),\n            new HashEntry(\"age\", \"29\"),\n            };\n        await db.HashSetAsync(\"user-session:123\", hash);\n\n        var hashFields = await db.HashGetAllAsync(\"user-session:123\");\n        Console.WriteLine(String.Join(\"; \", hashFields));\n        // >>> name: John; surname: Smith; company: Redis; age: 29","section_id":"connect-and-test"},{"id":"connect-and-test-ex13","language":"csharp","code":"var hash = new HashEntry[] {\n            new HashEntry(\"name\", \"John\"),\n            new HashEntry(\"surname\", \"Smith\"),\n            new HashEntry(\"company\", \"Redis\"),\n            new HashEntry(\"age\", \"29\"),\n        };\n        db.HashSet(\"user-session:123\", hash);\n\n        var hashFields = db.HashGetAll(\"user-session:123\");\n        Console.WriteLine(String.Join(\"; \", hashFields));\n        // >>> name: John; surname: Smith; company: Redis; age: 29","section_id":"connect-and-test"},{"id":"connect-and-test-ex14","language":"csharp","code":"var hash = new HashEntry[] { \n            new HashEntry(\"name\", \"John\"), \n            new HashEntry(\"surname\", \"Smith\"),\n            new HashEntry(\"company\", \"Redis\"),\n            new HashEntry(\"age\", \"29\"),\n            };\n        await db.HashSetAsync(\"user-session:123\", hash);\n\n        var hashFields = await db.HashGetAllAsync(\"user-session:123\");\n        Console.WriteLine(String.Join(\"; \", hashFields));\n        // >>> name: John; surname: Smith; company: Redis; age: 29","section_id":"connect-and-test"},{"id":"connect-and-test-ex15","language":"csharp","code":"var hash = new HashEntry[] {\n            new HashEntry(\"name\", \"John\"),\n            new HashEntry(\"surname\", \"Smith\"),\n            new HashEntry(\"company\", \"Redis\"),\n            new HashEntry(\"age\", \"29\"),\n        };\n        db.HashSet(\"user-session:123\", hash);\n\n        var hashFields = db.HashGetAll(\"user-session:123\");\n        Console.WriteLine(String.Join(\"; \", hashFields));\n        // >>> name: John; surname: Smith; company: Redis; age: 29","section_id":"connect-and-test"}]}
{"schema_version":2,"id":"develop/clients/dotnet/nredisstack","title":"NRedisStack extensions guide","url":"https://redis.io/docs/latest/develop/clients/dotnet/nredisstack/","summary":"Access Redis Open Source modules","aliases":["/develop/connect/clients/dotnet"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-08T15:21:05+01:00","children":[{"id":"develop/clients/dotnet/nredisstack/queryjson","summary":"Learn how to use Redis Search with JSON and hash documents.","title":"Index and query documents","url":"https://redis.io/docs/latest/develop/clients/dotnet/nredisstack/queryjson/"},{"id":"develop/clients/dotnet/nredisstack/vecsearch","summary":"Learn how to index and query vector embeddings with Redis","title":"Index and query vectors","url":"https://redis.io/docs/latest/develop/clients/dotnet/nredisstack/vecsearch/"},{"id":"develop/clients/dotnet/nredisstack/prob","summary":"Learn how to use approximate calculations with Redis.","title":"Probabilistic data types","url":"https://redis.io/docs/latest/develop/clients/dotnet/nredisstack/prob/"}],"page_type":"content","content_hash":"09a22b741e2fa1eb4f0ad1ff658e28b62dc40f9c62308861bfb53a5786d22055","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[NRedisStack](https://github.com/redis/NRedisStack) is a library that builds upon [StackExchange.Redis](https://github.com/StackExchange/StackExchange.Redis) and provides an API for the extended set of Redis data types and features, such as [JSON](https://redis.io/docs/latest/develop/data-types/json), [Redis search](https://redis.io/docs/latest/develop/ai/search-and-query),\n[probabilistic data types](https://redis.io/docs/latest/develop/data-types/probabilistic), and [Time series](https://redis.io/docs/latest/develop/data-types/timeseries).\nThe sections below explain how to install `NRedisStack`. Note that this also installs\n`StackExchange.Redis` as a dependency, so you don't need to install it as a separate step.\n\n`NRedisStack` requires a running Redis server. For production apps, provision\na hosted Redis resource such as [Redis Cloud](https://redis.io/docs/latest/operate/rc) or\n[Azure Managed Redis](https://learn.microsoft.com/en-us/azure/redis/overview). For\nlocal development and testing, you can also run Redis Open Source locally. See\n[Install Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack/install)\nfor installation instructions.\n\n> [!NOTE]\n> You can also access Redis with an object-mapping client interface. See\n> [Redis OM for .NET](https://redis.io/docs/latest/integrate/redisom-for-net)\n> for more information."},{"id":"install","title":"Install","role":"setup","text":"Using the `dotnet` CLI, run:\n\n[code example]"},{"id":"redis-open-source-modules","title":"Redis Open Source modules","role":"content","text":"To access Redis Open Source capabilities, use the appropriate interface like this:\n\n[code example]"},{"id":"more-information","title":"More information","role":"content","text":"See the other pages in this section for more information and examples."}],"examples":[{"id":"install-ex0","language":"bash","code":"dotnet add package NRedisStack","section_id":"install"},{"id":"redis-open-source-modules-ex0","language":"cs","code":"IBloomCommands bf = db.BF();    // Bloom filter commands\nICuckooCommands cf = db.CF();  // Cuckoo filter commands\nICmsCommands cms = db.CMS();   // Count-min sketch commands\nITopKCommands topk = db.TOPK(); // Top-K commands\nITdigestCommands tdigest = db.TDIGEST(); // T-digest commands\nISearchCommands ft = db.FT();  // Redis search commands\nIJsonCommands json = db.JSON(); // JSON commands\nITimeSeriesCommands ts = db.TS(); // Time series commands","section_id":"redis-open-source-modules"}]}
{"schema_version":2,"id":"develop/clients/dotnet/nredisstack/prob","title":"Probabilistic data types","url":"https://redis.io/docs/latest/develop/clients/dotnet/nredisstack/prob/","summary":"Learn how to use approximate calculations with Redis.","aliases":["/develop/clients/dotnet/prob"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-08T15:21:05+01:00","page_type":"content","content_hash":"e8b932ca6495eaedc12358557debc4f9c477728fad2a8e3c99b7c88517b3889d","sections":[{"id":"set-operations","title":"Set operations","role":"content","text":"Redis supports the following approximate set operations:\n\n-   [Membership](#set-membership): The\n    [Bloom filter](https://redis.io/docs/latest/develop/data-types/probabilistic/bloom-filter) and\n    [Cuckoo filter](https://redis.io/docs/latest/develop/data-types/probabilistic/cuckoo-filter)\n    data types let you track whether or not a given item is a member of a set.\n-   [Cardinality](#set-cardinality): The\n    [HyperLogLog](https://redis.io/docs/latest/develop/data-types/probabilistic/hyperloglogs)\n    data type gives you an approximate value for the number of items in a set, also\n    known as the *cardinality* of the set.\n\nThe sections below describe these operations in more detail."},{"id":"set-membership","title":"Set membership","role":"content","text":"[Bloom filter](https://redis.io/docs/latest/develop/data-types/probabilistic/bloom-filter) and\n[Cuckoo filter](https://redis.io/docs/latest/develop/data-types/probabilistic/cuckoo-filter)\nobjects provide a set membership operation that lets you track whether or not a\nparticular item has been added to a set. These two types provide different\ntrade-offs for memory usage and speed, so you can select the best one for your\nuse case. Note that for both types, there is an asymmetry between presence and\nabsence of items in the set. If an item is reported as absent, then it is definitely\nabsent, but if it is reported as present, then there is a small chance it may really be\nabsent.\n\nInstead of storing strings directly, like a [set](https://redis.io/docs/latest/develop/data-types/sets),\na Bloom filter records the presence or absence of the\n[hash value](https://en.wikipedia.org/wiki/Hash_function) of a string.\nThis gives a very compact representation of the\nset's membership with a fixed memory size, regardless of how many items you\nadd. The following example adds some names to a Bloom filter representing\na list of users and checks for the presence or absence of users in the list.\nNote that you must use the `BF()` method to access the Bloom filter commands.\n\nFoundational: Use Bloom filters for memory-efficient set membership testing with false positive possibility\n\n**Difficulty:** Beginner\n\n**Available in:** C#\n\n##### C#\n\n[code example]\n\n\n\nA Cuckoo filter has similar features to a Bloom filter, but also supports\na deletion operation to remove hashes from a set, as shown in the example\nbelow. Note that you must use the `CF()` method to access the Cuckoo filter\ncommands.\n\nFoundational: Use Cuckoo filters for set membership testing with deletion support and faster lookups than Bloom filters\n\n**Difficulty:** Beginner\n\n**Available in:** C#\n\n##### C#\n\n[code example]\n\n\n\nWhich of these two data types you choose depends on your use case.\nBloom filters are generally faster than Cuckoo filters when adding new items,\nand also have better memory usage. Cuckoo filters are generally faster\nat checking membership and also support the delete operation. See the\n[Bloom filter](https://redis.io/docs/latest/develop/data-types/probabilistic/bloom-filter) and\n[Cuckoo filter](https://redis.io/docs/latest/develop/data-types/probabilistic/cuckoo-filter)\nreference pages for more information and comparison between the two types."},{"id":"set-cardinality","title":"Set cardinality","role":"content","text":"A [HyperLogLog](https://redis.io/docs/latest/develop/data-types/probabilistic/hyperloglogs)\nobject calculates the cardinality of a set. As you add\nitems, the HyperLogLog tracks the number of distinct set members but\ndoesn't let you retrieve them or query which items have been added.\nYou can also merge two or more HyperLogLogs to find the cardinality of the\n[union](https://en.wikipedia.org/wiki/Union_(set_theory)) of the sets they\nrepresent.\n\nFoundational: Estimate set cardinality with HyperLogLog for memory-efficient counting of distinct items\n\n**Difficulty:** Beginner\n\n**Available in:** C#\n\n##### C#\n\n[code example]\n\n\n\nThe main benefit that HyperLogLogs offer is their very low\nmemory usage. They can count up to 2^64 items with less than\n1% standard error using a maximum 12KB of memory. This makes\nthem very useful for counting things like the total of distinct\nIP addresses that access a website or the total of distinct\nbank card numbers that make purchases within a day."},{"id":"statistics","title":"Statistics","role":"content","text":"Redis supports several approximate statistical calculations\non numeric data sets:\n\n-   [Frequency](#frequency): The\n    [Count-min sketch](https://redis.io/docs/latest/develop/data-types/probabilistic/count-min-sketch)\n    data type lets you find the approximate frequency of a labeled item in a data stream.\n-   [Quantiles](#quantiles): The\n    [t-digest](https://redis.io/docs/latest/develop/data-types/probabilistic/t-digest)\n    data type estimates the quantile of a query value in a data stream.\n-   [Ranking](#ranking): The\n    [Top-K](https://redis.io/docs/latest/develop/data-types/probabilistic/top-k) data type\n    estimates the ranking of labeled items by frequency in a data stream.\n\nThe sections below describe these operations in more detail."},{"id":"frequency","title":"Frequency","role":"content","text":"A [Count-min sketch](https://redis.io/docs/latest/develop/data-types/probabilistic/count-min-sketch)\n(CMS) object keeps count of a set of related items represented by\nstring labels. The count is approximate, but you can specify\nhow close you want to keep the count to the true value (as a fraction)\nand the acceptable probability of failing to keep it in this\ndesired range. For example, you can request that the count should\nstay within 0.1% of the true value and have a 0.05% probability\nof going outside this limit. The example below shows how to create\na Count-min sketch object, add data to it, and then query it.\nNote that you must use the `CMS()` method to access the Count-min\nsketch commands.\n\nFoundational: Track approximate item frequencies with Count-min sketch for memory-efficient statistics on data streams\n\n**Difficulty:** Intermediate\n\n**Available in:** C#\n\n##### C#\n\n[code example]\n\n\n\nThe advantage of using a CMS over keeping an exact count with a\n[sorted set](https://redis.io/docs/latest/develop/data-types/sorted-sets)\nis that a CMS has very low and fixed memory usage, even for\nlarge numbers of items. Use CMS objects to keep daily counts of\nitems sold, accesses to individual web pages on your site, and\nother similar statistics."},{"id":"quantiles","title":"Quantiles","role":"content","text":"A [quantile](https://en.wikipedia.org/wiki/Quantile) is the value\nbelow which a certain fraction of samples lie. For example, with\na set of measurements of people's heights, the quantile of 0.75 is\nthe value of height below which 75% of all people's heights lie.\n[Percentiles](https://en.wikipedia.org/wiki/Percentile) are equivalent\nto quantiles, except that the fraction is expressed as a percentage.\n\nA [t-digest](https://redis.io/docs/latest/develop/data-types/probabilistic/t-digest)\nobject can estimate quantiles from a set of values added to it\nwithout having to store each value in the set explicitly. This can\nsave a lot of memory when you have a large number of samples.\n\nThe example below shows how to add data samples to a t-digest\nobject and obtain some basic statistics, such as the minimum and\nmaximum values, the quantile of 0.75, and the\n[cumulative distribution function](https://en.wikipedia.org/wiki/Cumulative_distribution_function)\n(CDF), which is effectively the inverse of the quantile function. It also\nshows how to merge two or more t-digest objects to query the combined\ndata set. Note that you must use the `TDIGEST()` method to access the\nt-digest commands.\n\nFoundational: Estimate quantiles and percentiles with t-digest for memory-efficient statistical analysis of large datasets\n\n**Difficulty:** Intermediate\n\n**Available in:** C#\n\n##### C#\n\n[code example]\n\n\n\nA t-digest object also supports several other related commands, such\nas querying by rank. See the\n[t-digest](https://redis.io/docs/latest/develop/data-types/probabilistic/t-digest)\nreference for more information."},{"id":"ranking","title":"Ranking","role":"content","text":"A [Top-K](https://redis.io/docs/latest/develop/data-types/probabilistic/top-k)\nobject estimates the rankings of different labeled items in a data\nstream according to frequency. For example, you could use this to\ntrack the top ten most frequently-accessed pages on a website, or the\ntop five most popular items sold.\n\nThe example below adds several different items to a Top-K object\nthat tracks the top three items (this is the second parameter to\nthe `TOPK().Reserve()` method). It also shows how to list the\ntop *k* items and query whether or not a given item is in the\nlist. Note that you must use the `TOPK()` method to access the\nTop-K commands.\n\nFoundational: Track top K most frequent items in a data stream with Top-K for efficient ranking without storing all items\n\n**Difficulty:** Intermediate\n\n**Available in:** C#\n\n##### C#\n\n[code example]"}],"examples":[{"id":"set-membership-ex0","language":"csharp","code":"bool[] res1 = db.BF().MAdd(\n            \"recorded_users\", \"andy\", \"cameron\", \"david\", \"michelle\"\n        );\n        Console.WriteLine(string.Join(\", \", res1));\n        // >>> true, true, true, true\n\n        bool res2 = db.BF().Exists(\"recorded_users\", \"cameron\");\n        Console.WriteLine(res2); // >>> true\n\n        bool res3 = db.BF().Exists(\"recorded_users\", \"kaitlyn\");\n        Console.WriteLine(res3); // >>> false","section_id":"set-membership"},{"id":"set-membership-ex1","language":"csharp","code":"bool res4 = db.CF().Add(\"other_users\", \"paolo\");\n        Console.WriteLine(res4); // >>> true\n\n        bool res5 = db.CF().Add(\"other_users\", \"kaitlyn\");\n        Console.WriteLine(res5); // >>> true\n\n        bool res6 = db.CF().Add(\"other_users\", \"rachel\");\n        Console.WriteLine(res6); // >>> true\n\n        bool[] res7 = db.CF().MExists(\"other_users\", \"paolo\", \"rachel\", \"andy\");\n        Console.WriteLine(string.Join(\", \", res7));\n        // >>> true, true, false\n\n        bool res8 = db.CF().Del(\"other_users\", \"paolo\");\n        Console.WriteLine(res8); // >>> true\n\n        bool res9 = db.CF().Exists(\"other_users\", \"paolo\");\n        Console.WriteLine(res9); // >>> false","section_id":"set-membership"},{"id":"set-cardinality-ex0","language":"csharp","code":"bool res10 = db.HyperLogLogAdd(\n            \"group:1\",\n            [\"andy\", \"cameron\", \"david\"]\n        );\n        Console.WriteLine(res10); // >>> true\n\n        long res11 = db.HyperLogLogLength(\"group:1\");\n        Console.WriteLine(res11); // >>> 3\n\n        bool res12 = db.HyperLogLogAdd(\n            \"group:2\",\n            [\"kaitlyn\", \"michelle\", \"paolo\", \"rachel\"]\n        );\n        Console.WriteLine(res12); // >>> true\n\n        long res13 = db.HyperLogLogLength(\"group:2\");\n        Console.WriteLine(res13); // >>> 4\n\n        db.HyperLogLogMerge(\n            \"both_groups\",\n            \"group:1\", \"group:2\"\n        );\n\n        long res14 = db.HyperLogLogLength(\"both_groups\");\n        Console.WriteLine(res14); // >>> 7","section_id":"set-cardinality"},{"id":"frequency-ex0","language":"csharp","code":"// Specify that you want to keep the counts within 0.01\n        // (0.1%) of the true value with a 0.005 (0.05%) chance\n        // of going outside this limit.\n        bool res15 = db.CMS().InitByProb(\"items_sold\", 0.01, 0.005);\n        Console.WriteLine(res15); // >>> true\n\n        long[] res16 = db.CMS().IncrBy(\n            \"items_sold\",\n            [\n                new(\"bread\", 300),\n                new(\"tea\", 200),\n                new(\"coffee\", 200),\n                new(\"beer\", 100)\n            ]\n        );\n        Console.WriteLine(string.Join(\", \", res16));\n        // >>> 300, 200, 200, 100\n\n        long[] res17 = db.CMS().IncrBy(\n            \"items_sold\",\n            [\n                new(\"bread\", 100),\n                new(\"coffee\", 150)\n            ]\n        );\n        Console.WriteLine(string.Join(\", \", res17));\n        // >>> 400, 350\n\n        long[] res18 = db.CMS().Query(\n            \"items_sold\",\n            \"bread\", \"tea\", \"coffee\", \"beer\"\n        );\n        Console.WriteLine(string.Join(\", \", res18));\n        // >>> 400, 200, 350, 100","section_id":"frequency"},{"id":"quantiles-ex0","language":"csharp","code":"bool res19 = db.TDIGEST().Create(\"male_heights\");\n        Console.WriteLine(res19); // >>> true\n\n        bool res20 = db.TDIGEST().Add(\n            \"male_heights\",\n            175.5, 181, 160.8, 152, 177, 196, 164\n        );\n        Console.WriteLine(res20); // >>> true\n\n        double res21 = db.TDIGEST().Min(\"male_heights\");\n        Console.WriteLine(res21); // >>> 152.0\n\n        double res22 = db.TDIGEST().Max(\"male_heights\");\n        Console.WriteLine(res22); // >>> 196.0\n\n        double[] res23 = db.TDIGEST().Quantile(\"male_heights\", 0.75);\n        Console.WriteLine(string.Join(\", \", res23)); // >>> 181.0\n\n        // Note that the CDF value for 181.0 is not exactly\n        // 0.75. Both values are estimates.\n        double[] res24 = db.TDIGEST().CDF(\"male_heights\", 181.0);\n        Console.WriteLine(string.Join(\", \", res24)); // >>> 0.7857142857142857\n\n        bool res25 = db.TDIGEST().Create(\"female_heights\");\n        Console.WriteLine(res25); // >>> true\n\n        bool res26 = db.TDIGEST().Add(\n            \"female_heights\",\n            155.5, 161, 168.5, 170, 157.5, 163, 171\n        );\n        Console.WriteLine(res26); // >>> true\n\n        double[] res27 = db.TDIGEST().Quantile(\"female_heights\", 0.75);\n        Console.WriteLine(string.Join(\", \", res27)); // >>> 170.0\n\n        // Specify 0 for `compression` and false for `override`.\n        bool res28 = db.TDIGEST().Merge(\n            \"all_heights\", 0, false, \"male_heights\", \"female_heights\"\n        );\n        Console.WriteLine(res28); // >>> true\n\n        double[] res29 = db.TDIGEST().Quantile(\"all_heights\", 0.75);\n        Console.WriteLine(string.Join(\", \", res29)); // >>> 175.5","section_id":"quantiles"},{"id":"ranking-ex0","language":"csharp","code":"bool res30 = db.TOPK().Reserve(\"top_3_songs\", 3, 7, 8, 0.9);\n        Console.WriteLine(res30); // >>> true\n\n        RedisResult[] res31 = db.TOPK().IncrBy(\n            \"top_3_songs\",\n            new Tuple<RedisValue, long>[] {\n                new(\"Starfish Trooper\", 3000),\n                new(\"Only one more time\", 1850),\n                new(\"Rock me, Handel\", 1325),\n                new(\"How will anyone know?\", 3890),\n                new(\"Average lover\", 4098),\n                new(\"Road to everywhere\", 770)\n            }\n        );\n        Console.WriteLine(\n            string.Join(\n                \", \",\n                string.Join(\n                    \", \",\n                    res31.Select(\n                        r => $\"{(r.IsNull ? \"Null\" : r)}\"\n                    )\n                )\n            )\n        );\n        // >>> Null, Null, Null, Rock me, Handel, Only one more time, Null\n\n        RedisResult[] res32 = db.TOPK().List(\"top_3_songs\");\n        Console.WriteLine(\n            string.Join(\n                \", \",\n                string.Join(\n                    \", \",\n                    res32.Select(\n                        r => $\"{(r.IsNull ? \"Null\" : r)}\"\n                    )\n                )\n            )\n        );\n        // >>> Average lover, How will anyone know?, Starfish Trooper\n\n        bool[] res33 = db.TOPK().Query(\n            \"top_3_songs\",\n            \"Starfish Trooper\", \"Road to everywhere\"\n        );\n        Console.WriteLine(string.Join(\", \", res33));\n        // >>> true, false","section_id":"ranking"}]}
{"schema_version":2,"id":"develop/clients/dotnet/nredisstack/queryjson","title":"Index and query documents","url":"https://redis.io/docs/latest/develop/clients/dotnet/nredisstack/queryjson/","summary":"Learn how to use Redis Search with JSON and hash documents.","aliases":["/develop/clients/dotnet/queryjson","/develop/connect/clients/dotnet/queryjson/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-16T16:25:49+01:00","page_type":"content","content_hash":"02d4489b9abf6e716146326c2885f2e91427dfde145507f6241cdfc2c582edec","sections":[{"id":"initialize","title":"Initialize","role":"content","text":"Make sure that you have [Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack)\nor another Redis server available. Also install the\n[`NRedisStack`](https://redis.io/docs/latest/develop/clients/dotnet) client library if you\nhaven't already done so. \n\nAdd the following dependencies:\n\nFoundational: Import required libraries for Redis Search, JSON operations, and search functionality\n\n**Difficulty:** Beginner\n\n**Available in:** C#\n\n##### C#\n\n[code example]"},{"id":"create-data","title":"Create data","role":"content","text":"Create some test data to add to the database:\n\nFoundational: Define sample user data structures for indexing and querying\n\n**Difficulty:** Beginner\n\n**Available in:** C#\n\n##### C#\n\n[code example]"},{"id":"add-the-index","title":"Add the index","role":"content","text":"Connect to your Redis database. The code below shows the most\nbasic connection but see\n[Connect to the server](https://redis.io/docs/latest/develop/clients/dotnet/connect)\nto learn more about the available connection options.\n\nFoundational: Establish a connection to a Redis server for query operations\n\n**Difficulty:** Beginner\n\n**Available in:** C#\n\n##### C#\n\n[code example]\n\n\n\nDelete any existing index called `idx:users` and any keys that start with `user:`.\n\nFoundational: Clean up existing indexes and documents to prepare for fresh example data\n\n**Difficulty:** Beginner\n\n**Available in:** C#\n\n##### C#\n\n[code example]\n\n\n\nCreate an index. In this example, only JSON documents with the key prefix `user:` are indexed. For more information, see [Query syntax](https://redis.io/docs/latest/develop/ai/search-and-query/query).\n\nFoundational: Create a search index for JSON documents with field definitions and key prefix filtering\n\n**Difficulty:** Intermediate\n\n**Available in:** C#\n\n##### C#\n\n[code example]"},{"id":"add-the-data","title":"Add the data","role":"content","text":"Add the three sets of user data to the database as\n[JSON](https://redis.io/docs/latest/develop/data-types/json) objects.\nIf you use keys with the `user:` prefix then Redis will index the\nobjects automatically as you add them:\n\nFoundational: Store JSON documents in Redis with automatic indexing based on key prefix\n\n**Difficulty:** Beginner\n\n**Available in:** C#\n\n##### C#\n\n[code example]"},{"id":"query-the-data","title":"Query the data","role":"content","text":"You can now use the index to search the JSON objects. The\n[query](https://redis.io/docs/latest/develop/ai/search-and-query/query)\nbelow searches for objects that have the text \"Paul\" in any field\nand have an `age` value in the range 30 to 40:\n\nQuery with filters: Search JSON documents using text matching and numeric range filters to find specific records\n\n**Difficulty:** Intermediate\n\n**Available in:** C#\n\n##### C#\n\n[code example]\n\n\n\nSpecify query options to return only the `city` field:\n\nQuery with field projection: Retrieve only specific fields from search results to reduce data transfer\n\n**Difficulty:** Intermediate\n\n**Available in:** C#\n\n##### C#\n\n[code example]\n\n\n\nUse an\n[aggregation query](https://redis.io/docs/latest/develop/ai/search-and-query/query/aggregation)\nto count all users in each city.\n\nAggregation queries: Use GROUP BY and COUNT operations to summarize and analyze indexed data\n\n**Difficulty:** Advanced\n\n**Available in:** C#\n\n##### C#\n\n[code example]"},{"id":"differences-with-hash-documents","title":"Differences with hash documents","role":"content","text":"Indexing for hash documents is very similar to JSON indexing but you\nneed to specify some slightly different options.\n\nWhen you create the schema for a hash index, you don't need to\nadd aliases for the fields, since you use the basic names to access\nthe fields anyway. Also, you must set the `On` option to `IndexDataType.HASH`\nin the `FTCreateParams` object when you create the index. The code below shows\nthese changes with a new index called `hash-idx:users`, which is otherwise the\nsame as the `idx:users` index used for JSON documents in the previous examples.\n\nFirst, delete any existing index called `hash-idx:users` and any keys that start with `huser:`.\n\nFoundational: Clean up existing hash indexes and documents to prepare for fresh example data\n\n**Difficulty:** Beginner\n\n**Available in:** C#\n\n##### C#\n\n[code example]\n\n\n\nNow create the new index:\n\nFoundational: Create a search index for hash documents with HASH index type and field definitions\n\n**Difficulty:** Intermediate\n\n**Available in:** C#\n\n##### C#\n\n[code example]\n\n\n\nYou use [`HashSet()`](https://redis.io/docs/latest/commands/hset) to add the hash\ndocuments instead of [`JSON.Set()`](https://redis.io/docs/latest/commands/json.set).\nAlso, you must add the fields as key-value pairs instead of combining them\ninto a single object.\n\nFoundational: Store hash documents in Redis with automatic indexing based on key prefix\n\n**Difficulty:** Beginner\n\n**Available in:** C#\n\n##### C#\n\n[code example]\n\n\n\nThe query commands work the same here for hash as they do for JSON (but\nthe name of the hash index is different). The format of the result is\nalmost the same except that the fields are returned directly in the\n`Document` object of the result (for JSON, the fields are all enclosed\nin a string under the key `json`):\n\nQuery with filters: Search hash documents using text matching and numeric range filters (same as JSON queries)\n\n**Difficulty:** Intermediate\n\n**Available in:** C#\n\n##### C#\n\n[code example]"},{"id":"bulk-import-with-himport","title":"Bulk import with HIMPORT","role":"content","text":"From Redis Open Source 8.10, you can also add the hash documents with the\n[`HIMPORT`](https://redis.io/docs/latest/commands/himport) command family. This creates the same\nhashes as `HashSet()`, but lets Redis store them as\n[compact hashes](https://redis.io/docs/latest/develop/data-types/hashes#compact-hashes), which use\nless memory when many keys share the same field names. Redis Search indexes\nthese hashes in the same way.\n\nDeclare the field names once by creating a `HashImport` object with\n`HashImport.Create()`. The client declares the field names on each connection\nwith [`HIMPORT PREPARE`](https://redis.io/docs/latest/commands/himport-prepare) the first time\nyou use the object there, so you don't call it yourself. Then create each\ndocument with [`HashImportAsync()`](https://redis.io/docs/latest/commands/himport-set), passing\nonly its values, in the same order as the field names. Use a\n[batch](https://redis.io/docs/latest/develop/clients/dotnet/transpipe) to send all the\nimports in one round trip:\n\nBulk ingestion: Import hash documents as compact hashes using a HashImport fieldset and a batch of HIMPORT SET calls\n\n**Difficulty:** Intermediate\n\n**Builds upon:** add_hash_data\n\n**Available in:** C#\n\n##### C#\n\n[code example]\n\n\n\n`HashImportAsync()` replaces any existing key, so running this code after the\n`HashSet()` calls leaves you with the same three documents. The query returns\nthe same result, although the fields can appear in a different order.\n\n> [!NOTE]\n> `HIMPORT` requires `StackExchange.Redis` v3.3.1 or later. If your\n> `NRedisStack` version depends on an earlier version, add the\n> `StackExchange.Redis` package to your project explicitly. Redis Cloud and\n> Redis Software don't support `HIMPORT`."},{"id":"more-information","title":"More information","role":"content","text":"See the [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) docs\nfor a full description of all query features with examples."}],"examples":[{"id":"initialize-ex0","language":"csharp","code":"using NRedisStack.RedisStackCommands;\nusing NRedisStack.Search;\nusing NRedisStack.Search.Aggregation;\nusing NRedisStack.Search.Literals.Enums;\nusing StackExchange.Redis;","section_id":"initialize"},{"id":"create-data-ex0","language":"csharp","code":"var user1 = new\n        {\n            name = \"Paul John\",\n            email = \"paul.john@example.com\",\n            age = 42,\n            city = \"London\"\n        };\n\n        var user2 = new\n        {\n            name = \"Eden Zamir\",\n            email = \"eden.zamir@example.com\",\n            age = 29,\n            city = \"Tel Aviv\"\n        };\n\n        var user3 = new\n        {\n            name = \"Paul Zamir\",\n            email = \"paul.zamir@example.com\",\n            age = 35,\n            city = \"Tel Aviv\"\n        };","section_id":"create-data"},{"id":"add-the-index-ex0","language":"csharp","code":"var muxer = ConnectionMultiplexer.Connect(\"localhost:6379\");\n        var db = muxer.GetDatabase();","section_id":"add-the-index"},{"id":"add-the-index-ex1","language":"csharp","code":"db.KeyDelete([\"user:1\", \"user:2\", \"user:3\"]);\n        try { db.FT().DropIndex(\"idx:users\"); } catch { }","section_id":"add-the-index"},{"id":"add-the-index-ex2","language":"csharp","code":"var schema = new Schema()\n            .AddTextField(new FieldName(\"$.name\", \"name\"))\n            .AddTagField(new FieldName(\"$.city\", \"city\"))\n            .AddNumericField(new FieldName(\"$.age\", \"age\"));\n\n        bool indexCreated = db.FT().Create(\n            \"idx:users\",\n            new FTCreateParams()\n                .On(IndexDataType.JSON)\n                .Prefix(\"user:\"),\n            schema\n        );","section_id":"add-the-index"},{"id":"add-the-data-ex0","language":"csharp","code":"bool user1Set = db.JSON().Set(\"user:1\", \"$\", user1);\n        bool user2Set = db.JSON().Set(\"user:2\", \"$\", user2);\n        bool user3Set = db.JSON().Set(\"user:3\", \"$\", user3);","section_id":"add-the-data"},{"id":"query-the-data-ex0","language":"csharp","code":"SearchResult findPaulResult = db.FT().Search(\n            \"idx:users\",\n            new(\"Paul @age:[30 40]\")\n        );\n        Console.WriteLine(string.Join(\n            \", \",\n            findPaulResult.Documents.Select(x => x[\"json\"])\n        ));\n        // >>> {\"name\":\"Paul Zamir\",\"email\":\"paul.zamir@example.com\", ...","section_id":"query-the-data"},{"id":"query-the-data-ex1","language":"csharp","code":"var citiesResult = db.FT().Search(\n            \"idx:users\",\n            new Query(\"Paul\")\n                .ReturnFields(new FieldName(\"$.city\", \"city\"))\n        );\n        Console.WriteLine(string.Join(\n            \", \",\n            citiesResult.Documents.Select(x => x[\"city\"]).OrderBy(x => x)\n        ));\n        // >>> London, Tel Aviv","section_id":"query-the-data"},{"id":"query-the-data-ex2","language":"csharp","code":"AggregationRequest aggRequest = new AggregationRequest(\"*\")\n            .GroupBy(\"@city\", Reducers.Count().As(\"count\"));\n\n        AggregationResult aggResult = db.FT().Aggregate(\"idx:users\", aggRequest);\n        IReadOnlyList<Dictionary<string, RedisValue>> resultsList =\n                                                        aggResult.GetResults();\n\n        for (var i = 0; i < resultsList.Count; i++)\n        {\n            Dictionary<string, RedisValue> item = resultsList.ElementAt(i);\n            Console.WriteLine($\"{item[\"city\"]} - {item[\"count\"]}\");\n        }\n        // >>> London - 1\n        // >>> Tel Aviv - 2","section_id":"query-the-data"},{"id":"differences-with-hash-documents-ex0","language":"csharp","code":"db.KeyDelete([\"huser:1\", \"huser:2\", \"huser:3\"]);\n        try { db.FT().DropIndex(\"hash-idx:users\"); } catch { }","section_id":"differences-with-hash-documents"},{"id":"differences-with-hash-documents-ex1","language":"csharp","code":"var hashSchema = new Schema()\n            .AddTextField(\"name\")\n            .AddTagField(\"city\")\n            .AddNumericField(\"age\");\n\n        bool hashIndexCreated = db.FT().Create(\n            \"hash-idx:users\",\n            new FTCreateParams()\n                .On(IndexDataType.HASH)\n                .Prefix(\"huser:\"),\n            hashSchema\n        );","section_id":"differences-with-hash-documents"},{"id":"differences-with-hash-documents-ex2","language":"csharp","code":"db.HashSet(\"huser:1\", [\n            new(\"name\", \"Paul John\"),\n            new(\"email\", \"paul.john@example.com\"),\n            new(\"age\", 42),\n            new(\"city\", \"London\")\n        ]);\n\n        db.HashSet(\"huser:2\", [\n            new(\"name\", \"Eden Zamir\"),\n            new(\"email\", \"eden.zamir@example.com\"),\n            new(\"age\", 29),\n            new(\"city\", \"Tel Aviv\")\n        ]);\n\n        db.HashSet(\"huser:3\", [\n            new(\"name\", \"Paul Zamir\"),\n            new(\"email\", \"paul.zamir@example.com\"),\n            new(\"age\", 35),\n            new(\"city\", \"Tel Aviv\")\n        ]);","section_id":"differences-with-hash-documents"},{"id":"differences-with-hash-documents-ex3","language":"csharp","code":"SearchResult findPaulHashResult = db.FT().Search(\n            \"hash-idx:users\",\n            new(\"Paul @age:[30 40]\")\n        );\n\n        foreach (Document doc in findPaulHashResult.Documents)\n        {\n            Console.WriteLine(\n                $\"Name: {doc[\"name\"]}, email: {doc[\"email\"]}, \" +\n                $\"age: {doc[\"age\"]}, city:{doc[\"city\"]}\"\n            );\n        }\n        // >>> Name: Paul Zamir, email: paul.zamir@example.com, age: 35, ...","section_id":"differences-with-hash-documents"},{"id":"bulk-import-with-himport-ex0","language":"csharp","code":"using var userFields = HashImport.Create(\"name\", \"email\", \"age\", \"city\");\n\n        var batch = db.CreateBatch();\n        var imports = new List<Task>();\n\n        foreach (var (key, user) in new[] {\n            (\"huser:1\", user1), (\"huser:2\", user2), (\"huser:3\", user3)\n        })\n        {\n            imports.Add(batch.HashImportAsync(\n                key,\n                userFields,\n                new RedisValue[] { user.name, user.email, user.age, user.city }\n            ));\n        }\n\n        batch.Execute();\n        Task.WaitAll(imports.ToArray());","section_id":"bulk-import-with-himport"}]}
{"schema_version":2,"id":"develop/clients/dotnet/nredisstack/vecsearch","title":"Index and query vectors","url":"https://redis.io/docs/latest/develop/clients/dotnet/nredisstack/vecsearch/","summary":"Learn how to index and query vector embeddings with Redis","aliases":["/develop/clients/dotnet/vecsearch"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-16T16:44:30+01:00","page_type":"content","content_hash":"f0fb53087258eb6cd80ceafc207d6f304ab22cf50a434d65855c0c2afb7a6225","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query)\nlets you index vector fields in [hash](https://redis.io/docs/latest/develop/data-types/hashes)\nor [JSON](https://redis.io/docs/latest/develop/data-types/json) objects (see the\n[Vectors](https://redis.io/docs/latest/develop/ai/search-and-query/vectors) \nreference page for more information).\nAmong other things, vector fields can store *text embeddings*, which are AI-generated vector\nrepresentations of the semantic information in pieces of text. The\n[vector distance](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#distance-metrics)\nbetween two embeddings indicates how similar they are semantically. By comparing the\nsimilarity of an embedding generated from some query text with embeddings stored in hash\nor JSON fields, Redis can retrieve documents that closely match the query in terms\nof their meaning.\n\nIn the example below, we use [Microsoft.ML](https://dotnet.microsoft.com/en-us/apps/ai/ml-dotnet)\nto generate the vector embeddings to store and index with Redis Search.\nWe also show how to adapt the code to use\n[Azure OpenAI](https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/embeddings?tabs=csharp)\nfor the embeddings. The code is first demonstrated for hash documents with a\nseparate section to explain the\n[differences with JSON documents](#differences-with-json-documents).\n\n> [!NOTE]\n> From [v1.0.0](https://github.com/redis/NRedisStack/releases/tag/v1.0.0)\n> onwards, `NRedisStack` uses query dialect 2 by default.\n> Redis Search methods such as [`FT().Search()`](https://redis.io/docs/latest/commands/ft.search)\n> will explicitly request this dialect, overriding the default set for the server.\n> See\n> [Query dialects](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/dialects)\n> for more information."},{"id":"initialize","title":"Initialize","role":"content","text":"The example is probably easiest to follow if you start with a new\nconsole app, which you can create using the following command:\n\n[code example]\n\nIn the app's project folder, add\n[`NRedisStack`](https://redis.io/docs/latest/develop/clients/dotnet):\n\n[code example]\n\nThen, add the `Microsoft.ML` package.\n\n[code example]\n\nIf you want to try the optional\n[Azure embedding](#generate-an-embedding-from-azure-openai)\ndescribed below, you should also add `Azure.AI.OpenAI`:\n\n[code example]"},{"id":"import-dependencies","title":"Import dependencies","role":"content","text":"Add the following imports to your source file:\n\n[code example]\n\nIf you are using the Azure embeddings, also add:\n\n[code example]"},{"id":"define-a-function-to-obtain-the-embedding-model","title":"Define a function to obtain the embedding model","role":"content","text":"> [!NOTE]\n> Ignore this step if you are using an Azure OpenAI\n> embedding model.\n\nA few steps are involved in initializing the embedding model\n(known as a `PredictionEngine`, in Microsoft terminology), so\nwe declare a function to contain those steps together.\n(See the Microsoft.ML docs for more information about the\n[`ApplyWordEmbedding`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.ml.textcatalog.applywordembedding?view=ml-dotnet)\nmethod, including example code.)\n\nNote that we use two classes, `TextData` and `TransformedTextData`, to\nspecify the `PredictionEngine` model. C# syntax requires us to place these\nclasses after the main code in a console app source file. The section\n[Declare `TextData` and `TransformedTextData`](#declare-textdata-and-transformedtextdata)\nbelow shows how to declare them.\n\n[code example]"},{"id":"define-a-function-to-generate-an-embedding","title":"Define a function to generate an embedding","role":"content","text":"> [!NOTE]\n> Ignore this step if you are using an Azure OpenAI\n> embedding model.\n\nOur embedding model represents the vectors as an array of `float` values,\nbut when you store vectors in a Redis hash object, you must encode the vector\narray as a `byte` string. To simplify this, we declare a\n`GetEmbedding()` function that applies the `PredictionEngine` model described\n[above](#define-a-function-to-obtain-the-embedding-model), and\nthen encodes the returned `float` array as a `byte` string. If you are\nstoring your documents as JSON objects instead of hashes, then you should\nuse the `float` array for the embedding directly, without first converting\nit to a `byte` string (see [Differences with JSON documents](#differences-with-json-documents)\nbelow).\n\n\n[code example]"},{"id":"generate-an-embedding-from-azure-openai","title":"Generate an embedding from Azure OpenAI","role":"content","text":"> [!NOTE]\n> Ignore this step if you are using a Microsoft.ML\n> embedding model.\n\nAzure OpenAI can be a convenient way to access an embedding model, because\nyou don't need to manage and scale the server infrastructure yourself.\n\nYou can create an Azure OpenAI service and deployment to serve embeddings of\nwhatever type you need. Select your region, note the service endpoint and key,\nand add them where you see placeholders in the function below.\nSee\n[Learn how to generate embeddings with Azure OpenAI](https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/embeddings?tabs=csharp)\nfor more information.\n\n[code example]"},{"id":"create-the-index","title":"Create the index","role":"content","text":"Connect to Redis and delete any index previously created with the\nname `vector_idx`. (The `DropIndex()` call throws an exception if\nthe index doesn't already exist, which is why you need the\n`try...catch` block.)\n\n[code example]\n\nNext, create the index.\nThe schema in the example below includes three fields: the text content to index, a\n[tag](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/tags)\nfield to represent the \"genre\" of the text, and the embedding vector generated from\nthe original text content. The `embedding` field specifies\n[HNSW](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#hnsw-index) \nindexing, the\n[L2](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#distance-metrics)\nvector distance metric, `Float32` values to represent the vector's components,\nand 150 dimensions, as required by our embedding model.\n\nThe `FTCreateParams` object specifies hash objects for storage and a\nprefix `doc:` that identifies the hash objects we want to index.\n\n[code example]"},{"id":"add-data","title":"Add data","role":"content","text":"You can now supply the data objects, which will be indexed automatically\nwhen you add them with [`HashSet()`](https://redis.io/docs/latest/commands/hset), as long as\nyou use the `doc:` prefix specified in the index definition.\n\nFirstly, create an instance of the `PredictionEngine` model using our\n`GetPredictionEngine()` function.\nYou can then pass this to the `GetEmbedding()` function\nto create the embedding that represents the `content` field, as shown below .\n\n(If you are using an Azure OpenAI model for the embeddings, then\nuse `GetEmbeddingFromAzure()` instead of `GetEmbedding()`, and note that\nthe `PredictionModel` is managed by the server, so you don't need to create\nan instance yourself.)\n\n[code example]"},{"id":"bulk-import-with-himport","title":"Bulk import with HIMPORT","role":"content","text":"From Redis Open Source 8.10, you can also add the documents with the\n[`HIMPORT`](https://redis.io/docs/latest/commands/himport) command family. This creates the same\nhashes as `HashSet()`, but lets Redis store them as\n[compact hashes](https://redis.io/docs/latest/develop/data-types/hashes#compact-hashes), which use\nless memory when many keys share the same field names. Redis Search indexes\nthese hashes in the same way.\n\nDeclare the field names once by creating a `HashImport` object with\n`HashImport.Create()`. The client declares the field names on each connection\nwith [`HIMPORT PREPARE`](https://redis.io/docs/latest/commands/himport-prepare) the first time\nyou use the object there, so you don't call it yourself. Then create each\ndocument with [`HashImportAsync()`](https://redis.io/docs/latest/commands/himport-set), passing\nonly its values, in the same order as the field names. Use a\n[batch](https://redis.io/docs/latest/develop/clients/dotnet/transpipe) to send all the\nimports in one round trip:\n\n[code example]\n\n`HashImportAsync()` replaces any existing key, so running this code after the\n`HashSet()` calls leaves you with the same three documents.\n\n> [!NOTE]\n> `HIMPORT` requires `StackExchange.Redis` v3.3.1 or later. If your\n> `NRedisStack` version depends on an earlier version, add the\n> `StackExchange.Redis` package to your project explicitly. Redis Cloud and\n> Redis Software don't support `HIMPORT`."},{"id":"run-a-query","title":"Run a query","role":"content","text":"After you have created the index and added the data, you are ready to run a query.\nTo do this, you must create another embedding vector from your chosen query\ntext. Redis calculates the vector distance between the query vector and each\nembedding vector in the index as it runs the query. We can request the results to be\nsorted to rank them in order of ascending distance.\n\nThe code below creates the query embedding using the `GetEmbedding()` method, as with\nthe indexing, and passes it as a parameter when the query executes (see\n[Vector search](https://redis.io/docs/latest/develop/ai/search-and-query/query/vector-search)\nfor more information about using query parameters with embeddings).\nThe query is a\n[K nearest neighbors (KNN)](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#knn-vector-search)\nsearch that sorts the results in order of vector distance from the query vector.\n\n(As before, replace `GetEmbedding()` with `GetEmbeddingFromAzure()` if you are using\nAzure OpenAI.)\n\n[code example]"},{"id":"declare-textdata-and-transformedtextdata","title":"Declare `TextData` and `TransformedTextData`","role":"content","text":"> [!NOTE]\n> Ignore this step if you are using an Azure OpenAI\n> embedding model.\n\nAs we noted in the section above about the\n[embedding model](#define-a-function-to-obtain-the-embedding-model),\nwe must declare two very simple classes at the end of the source\nfile. These are required because the API that generates the model\nexpects classes with named fields for the input `string` and output \n`float` array.\n\n[code example]"},{"id":"run-the-code","title":"Run the code","role":"content","text":"Assuming you have added the code from the steps above to your source file,\nit is now ready to run, but note that it may take a while to complete when\nyou run it for the first time (which happens because the tokenizer must download the\nembedding model data before it can generate the embeddings). When you run the code,\nit outputs the following result text:\n\n[code example]\n\nThe exact distances may differ slightly on your system.\n\nThe results are ordered according to the value of the `score`\nfield, which represents the vector distance here. The lowest distance indicates\nthe greatest similarity to the query.\nAs you would expect, the result for `doc:1` with the content text\n*\"That is a very happy person\"*\nis the result that is most similar in meaning to the query text\n*\"That is a happy person\"*."},{"id":"differences-with-json-documents","title":"Differences with JSON documents","role":"content","text":"Indexing JSON documents is similar to hash indexing, but there are some\nimportant differences. JSON allows much richer data modeling with nested fields, so\nyou must supply a [path](https://redis.io/docs/latest/develop/data-types/json/path) in the schema\nto identify each field you want to index. However, you can declare a short alias for each\nof these paths to avoid typing it in full for\nevery query. Also, you must specify `IndexType.JSON` with the `On()` option when you\ncreate the index.\n\nThe code below shows these differences, but the index is otherwise very similar to\nthe one created previously for hashes:\n\n[code example]\n\nAn important difference with JSON indexing is that the vectors are\nspecified using arrays of `float` instead of binary strings. This requires a modification\nto the `GetEmbedding()` function declared in\n[Define a function to generate an embedding](#define-a-function-to-generate-an-embedding)\nabove:\n\n[code example]\n\nYou should make a similar modification to the `GetEmbeddingFromAzure()` function\nif you are using Azure OpenAI with JSON.\n\nUse [`JSON().set()`](https://redis.io/docs/latest/commands/json.set) to add the data\ninstead of [`HashSet()`](https://redis.io/docs/latest/commands/hset):\n\n[code example]\n\nThe query is almost identical to the one for the hash documents. This\ndemonstrates how the right choice of aliases for the JSON paths can\nsave you having to write complex queries. The only significant difference is\nthat the `FieldName` objects created for the `ReturnFields()` option must\ninclude the JSON path for the field.\n\nAn important thing to notice\nis that the vector parameter for the query is still specified as a\nbinary string (using the `GetEmbedding()` method), even though the data for\nthe `embedding` field of the JSON was specified as a `float` array.\n\n[code example]\n\nApart from the `jdoc:` prefixes for the keys, the result from the JSON\nquery is the same as for hash:\n\n[code example]"},{"id":"learn-more","title":"Learn more","role":"related","text":"See\n[Vector search](https://redis.io/docs/latest/develop/ai/search-and-query/query/vector-search)\nfor more information about the indexing options, distance metrics, and query format\nfor vectors."}],"examples":[{"id":"initialize-ex0","language":"bash","code":"dotnet new console -n VecQueryExample","section_id":"initialize"},{"id":"initialize-ex1","language":"bash","code":"dotnet add package NRedisStack","section_id":"initialize"},{"id":"initialize-ex2","language":"bash","code":"dotnet add package Microsoft.ML","section_id":"initialize"},{"id":"initialize-ex3","language":"plaintext","code":"dotnet add package Azure.AI.OpenAI --prerelease","section_id":"initialize"},{"id":"import-dependencies-ex0","language":"csharp","code":"// Redis connection and Redis Search.\nusing NRedisStack.RedisStackCommands;\nusing StackExchange.Redis;\nusing NRedisStack.Search;\nusing static NRedisStack.Search.Schema;\nusing NRedisStack.Search.Literals.Enums;\n\n// Text embeddings.\nusing Microsoft.ML;\nusing Microsoft.ML.Transforms.Text;","section_id":"import-dependencies"},{"id":"import-dependencies-ex1","language":"csharp","code":"// Azure embeddings.\nusing Azure;\nusing Azure.AI.OpenAI;","section_id":"import-dependencies"},{"id":"define-a-function-to-obtain-the-embedding-model-ex0","language":"csharp","code":"static PredictionEngine<TextData, TransformedTextData> GetPredictionEngine(){\n    // Create a new ML context, for ML.NET operations. It can be used for\n    // exception tracking and logging, as well as the source of randomness.\n    var mlContext = new MLContext();\n\n    // Create an empty list as the dataset\n    var emptySamples = new List<TextData>();\n\n    // Convert sample list to an empty IDataView.\n    var emptyDataView = mlContext.Data.LoadFromEnumerable(emptySamples);\n\n    // A pipeline for converting text into a 150-dimension embedding vector\n    var textPipeline = mlContext.Transforms.Text.NormalizeText(\"Text\")\n        .Append(mlContext.Transforms.Text.TokenizeIntoWords(\"Tokens\",\n            \"Text\"))\n        .Append(mlContext.Transforms.Text.ApplyWordEmbedding(\"Features\",\n            \"Tokens\", WordEmbeddingEstimator.PretrainedModelKind\n            .SentimentSpecificWordEmbedding));\n\n    // Fit to data.\n    var textTransformer = textPipeline.Fit(emptyDataView);\n\n    // Create the prediction engine to get the embedding vector from the input text/string.\n    var predictionEngine = mlContext.Model.CreatePredictionEngine<TextData,\n        TransformedTextData>(textTransformer);\n\n    return predictionEngine;\n}","section_id":"define-a-function-to-obtain-the-embedding-model"},{"id":"define-a-function-to-generate-an-embedding-ex0","language":"csharp","code":"static byte[] GetEmbedding(\n    PredictionEngine<TextData, TransformedTextData> model, string sentence\n)\n{\n    // Call the prediction API to convert the text into embedding vector.\n    var data = new TextData()\n    {\n        Text = sentence\n    };\n\n    var prediction = model.Predict(data);\n\n    // Convert prediction.Features to a binary blob\n    float[] floatArray = Array.ConvertAll(prediction.Features, x => (float)x);\n    byte[] byteArray = new byte[floatArray.Length * sizeof(float)];\n    Buffer.BlockCopy(floatArray, 0, byteArray, 0, byteArray.Length);\n\n    return byteArray;\n}","section_id":"define-a-function-to-generate-an-embedding"},{"id":"generate-an-embedding-from-azure-openai-ex0","language":"csharp","code":"private static byte[] GetEmbeddingFromAzure(string sentence){\n\tUri oaiEndpoint = new (\"your-azure-openai-endpoint\");\n\tstring oaiKey = \"your-openai-key\";\n\n\tAzureKeyCredential credentials = new (oaiKey);\n\tOpenAIClient openAIClient = new (oaiEndpoint, credentials);\n\n\tEmbeddingsOptions embeddingOptions = new() {\n    \t     DeploymentName = \"your-deployment-name\",\n    \t     Input = { sentence },\n\t};\n\n\t// Generate the vector embedding\n\tvar returnValue = openAIClient.GetEmbeddings(embeddingOptions);\n\n\t// Convert the array of floats to binary blob\n\tfloat[] floatArray = Array.ConvertAll(returnValue.Value.Data[0].Embedding.ToArray(), x => (float)x);\n\tbyte[] byteArray = new byte[floatArray.Length * sizeof(float)];\n\tBuffer.BlockCopy(floatArray, 0, byteArray, 0, byteArray.Length);\n\treturn byteArray;\n}","section_id":"generate-an-embedding-from-azure-openai"},{"id":"create-the-index-ex0","language":"csharp","code":"var muxer = ConnectionMultiplexer.Connect(\"localhost:6379\");\nvar db = muxer.GetDatabase();\n\ntry { db.FT().DropIndex(\"vector_idx\");} catch {}","section_id":"create-the-index"},{"id":"create-the-index-ex1","language":"csharp","code":"var schema = new Schema()\n    .AddTextField(new FieldName(\"content\", \"content\"))\n    .AddTagField(new FieldName(\"genre\", \"genre\"))\n    .AddVectorField(\"embedding\", VectorField.VectorAlgo.HNSW,\n        new Dictionary<string, object>()\n        {\n            [\"TYPE\"] = \"FLOAT32\",\n            [\"DIM\"] = \"150\",\n            [\"DISTANCE_METRIC\"] = \"L2\"\n        }\n    );\n\ndb.FT().Create(\n    \"vector_idx\",\n    new FTCreateParams()\n        .On(IndexDataType.HASH)\n        .Prefix(\"doc:\"),\n    schema\n);","section_id":"create-the-index"},{"id":"add-data-ex0","language":"csharp","code":"var predEngine = GetPredictionEngine();\n\nvar sentence1 = \"That is a very happy person\";\n\nHashEntry[] doc1 = {\n    new(\"content\", sentence1),\n    new(\"genre\", \"persons\"),\n    new(\"embedding\", GetEmbedding(predEngine, sentence1))\n};\n\ndb.HashSet(\"doc:1\", doc1);\n\nvar sentence2 = \"That is a happy dog\";\n\nHashEntry[] doc2 = {\n    new(\"content\", sentence2),\n    new(\"genre\", \"pets\"),\n    new(\"embedding\", GetEmbedding(predEngine, sentence2))\n};\n\ndb.HashSet(\"doc:2\", doc2);\n\nvar sentence3 = \"Today is a sunny day\";\n\nHashEntry[] doc3 = {\n    new(\"content\", sentence3),\n    new(\"genre\", \"weather\"),\n    new(\"embedding\", GetEmbedding(predEngine, sentence3))\n};\n\ndb.HashSet(\"doc:3\", doc3);","section_id":"add-data"},{"id":"bulk-import-with-himport-ex0","language":"csharp","code":"using var docFields = HashImport.Create(\"content\", \"genre\", \"embedding\");\n\nvar docs = new[] {\n    (\"doc:1\", \"That is a very happy person\", \"persons\"),\n    (\"doc:2\", \"That is a happy dog\", \"pets\"),\n    (\"doc:3\", \"Today is a sunny day\", \"weather\"),\n};\n\nvar batch = db.CreateBatch();\nvar imports = new List<Task>();\n\nforeach (var (key, content, genre) in docs)\n{\n    imports.Add(batch.HashImportAsync(\n        key,\n        docFields,\n        new RedisValue[] { content, genre, GetEmbedding(predEngine, content) }\n    ));\n}\n\nbatch.Execute();\nTask.WaitAll(imports.ToArray());","section_id":"bulk-import-with-himport"},{"id":"run-a-query-ex0","language":"csharp","code":"var res = db.FT().Search(\"vector_idx\",\n    new Query(\"*=>[KNN 3 @embedding $query_vec AS score]\")\n    .AddParam(\"query_vec\", GetEmbedding(predEngine, \"That is a happy person\"))\n    .ReturnFields(\n        new FieldName(\"content\", \"content\"),\n        new FieldName(\"score\", \"score\")\n    )\n    .SetSortBy(\"score\")\n    .Dialect(2));\n\nforeach (var doc in res.Documents) {\n    var props = doc.GetProperties();\n    var propText = string.Join(\n        \", \",\n        props.Select(p => $\"{p.Key}: '{p.Value}'\")\n    );\n\n    Console.WriteLine(\n        $\"ID: {doc.Id}, Properties: [\\n  {propText}\\n]\"\n    );\n}","section_id":"run-a-query"},{"id":"declare-textdata-and-transformedtextdata-ex0","language":"csharp","code":"class TextData\n{\n    public string Text { get; set; }\n}\n\nclass TransformedTextData : TextData\n{\n    public float[] Features { get; set; }\n}","section_id":"declare-textdata-and-transformedtextdata"},{"id":"run-the-code-ex0","language":"plaintext","code":"ID: doc:1, Properties: [\n  score: '4.30777168274', content: 'That is a very happy person'\n]\nID: doc:2, Properties: [\n  score: '25.9752807617', content: 'That is a happy dog'\n]\nID: doc:3, Properties: [\n  score: '68.8638000488', content: 'Today is a sunny day'\n]","section_id":"run-the-code"},{"id":"differences-with-json-documents-ex0","language":"cs","code":"var jsonSchema = new Schema()\n    .AddTextField(new FieldName(\"$.content\", \"content\"))\n    .AddTagField(new FieldName(\"$.genre\", \"genre\"))\n    .AddVectorField(\n        new FieldName(\"$.embedding\", \"embedding\"),\n        VectorField.VectorAlgo.HNSW,\n        new Dictionary<string, object>()\n        {\n            [\"TYPE\"] = \"FLOAT32\",\n            [\"DIM\"] = \"150\",\n            [\"DISTANCE_METRIC\"] = \"L2\"\n        }\n    );\n\n\ndb.FT().Create(\n    \"vector_json_idx\",\n    new FTCreateParams()\n        .On(IndexDataType.JSON)\n        .Prefix(\"jdoc:\"),\n    jsonSchema\n);","section_id":"differences-with-json-documents"},{"id":"differences-with-json-documents-ex1","language":"cs","code":"static float[] GetFloatEmbedding(\n    PredictionEngine<TextData, TransformedTextData> model, string sentence\n)\n{\n    // Call the prediction API to convert the text into embedding vector.\n    var data = new TextData()\n    {\n        Text = sentence\n    };\n\n    var prediction = model.Predict(data);\n\n    float[] floatArray = Array.ConvertAll(prediction.Features, x => (float)x);\n    return floatArray;\n}","section_id":"differences-with-json-documents"},{"id":"differences-with-json-documents-ex2","language":"cs","code":"var jSentence1 = \"That is a very happy person\";\n\nvar jdoc1 = new {\n    content = jSentence1,\n    genre = \"persons\",\n    embedding = GetFloatEmbedding(predEngine, jSentence1),\n};\n\ndb.JSON().Set(\"jdoc:1\", \"$\", jdoc1);\n\nvar jSentence2 = \"That is a happy dog\";\n\nvar jdoc2 = new {\n    content = jSentence2,\n    genre = \"pets\",\n    embedding = GetFloatEmbedding(predEngine, jSentence2),\n};\n\ndb.JSON().Set(\"jdoc:2\", \"$\", jdoc2);\n\nvar jSentence3 = \"Today is a sunny day\";\n\nvar jdoc3 = new {\n    content = jSentence3,\n    genre = \"weather\",\n    embedding = GetFloatEmbedding(predEngine, jSentence3),\n};\n\ndb.JSON().Set(\"jdoc:3\", \"$\", jdoc3);","section_id":"differences-with-json-documents"},{"id":"differences-with-json-documents-ex3","language":"cs","code":"var jRes = db.FT().Search(\"vector_json_idx\",\n    new Query(\"*=>[KNN 3 @embedding $query_vec AS score]\")\n    .AddParam(\"query_vec\", GetEmbedding(predEngine, \"That is a happy person\"))\n    .ReturnFields(\n        new FieldName(\"$.content\", \"content\"),\n        new FieldName(\"$.score\", \"score\")\n    )\n    .SetSortBy(\"score\")\n    .Dialect(2));\n\nforeach (var doc in jRes.Documents) {\n    var props = doc.GetProperties();\n    var propText = string.Join(\n        \", \",\n        props.Select(p => $\"{p.Key}: '{p.Value}'\")\n    );\n\n    Console.WriteLine(\n        $\"ID: {doc.Id}, Properties: [\\n  {propText}\\n]\"\n    );\n}","section_id":"differences-with-json-documents"},{"id":"differences-with-json-documents-ex4","language":"plaintext","code":"ID: jdoc:1, Properties: [\n  score: '4.30777168274', content: 'That is a very happy person'\n]\nID: jdoc:2, Properties: [\n  score: '25.9752807617', content: 'That is a happy dog'\n]\nID: jdoc:3, Properties: [\n  score: '68.8638000488', content: 'Today is a sunny day'\n]","section_id":"differences-with-json-documents"}]}
{"schema_version":2,"id":"develop/clients/dotnet/produsage","title":"Production usage","url":"https://redis.io/docs/latest/develop/clients/dotnet/produsage/","summary":"Get your `StackExchange.Redis` app ready for production","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:06:01+01:00","page_type":"content","content_hash":"d9e3f08482f43bbf46c27905f188766ad7b091283f6d97eb031d0a1843e509d1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This guide offers recommendations to get the best reliability and\nperformance in your production environment."},{"id":"checklist","title":"Checklist","role":"content","text":"Each item in the checklist below links to the section\nfor a recommendation. Use the checklist icons to record your\nprogress in implementing the recommendations.\n\n[code example]"},{"id":"recommendations","title":"Recommendations","role":"content","text":"The sections below offer recommendations for your production environment. Some\nof them may not apply to your particular use case."},{"id":"event-handling","title":"Event handling","role":"content","text":"The `ConnectionMultiplexer` class publishes several different types of\n[events](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/events/)\nfor situations such as configuration changes and connection failures.\nUse these events to record server activity in a log, which you can then use\nto monitor performance and diagnose problems when they occur.\nSee\nthe StackExchange.Redis\n[Events](https://seredis.dev/Events.html)\npage for the full list of events.\n\n#### Server notification events\n\nSome servers (such as Azure Cache for Redis) send notification events shortly\nbefore scheduled maintenance is due to happen. You can use code like the\nfollowing to respond to these events (see the \n[StackExchange.Redis](https://seredis.dev/ServerMaintenanceEvent.html)\ndocs for the full list of supported events). For example, you could\ninform users who try to connect that service is temporarily unavailable\nrather than letting them run into errors.\n\nThe same event also carries\n[Smart client handoffs](https://redis.io/docs/latest/develop/clients/dotnet/connect#connect-using-smart-client-handoffs-sch)\n(SCH) notifications, which are a different, Redis Cloud- and Redis Software-specific\nmechanism. Check the runtime type of the event arguments to tell the two apart.\n\n[code example]"},{"id":"timeouts","title":"Timeouts","role":"content","text":"If a network or server error occurs while your code is opening a\nconnection or issuing a command, it can end up hanging indefinitely.\nTo prevent this, `StackExchange.Redis` sets timeouts for socket\nreads and writes and for opening connections.\n\nBy default, the timeout is five seconds for `ConnectTimeout` and `SyncTimeout`,\nand `AsyncTimeout` uses the same value as `SyncTimeout`. You can set the time\n(in milliseconds) separately for connections and commands using these\nconfiguration options:\n\n[code example]\n\nThe default timeouts are a good starting point, but cloud applications may need\nlonger timeouts to handle transient network or server delays. Shorter timeouts\ndo not make commands run faster; they only fail sooner. Tune these values to\nimprove reliability and resilience for your workload."},{"id":"exception-handling","title":"Exception handling","role":"errors","text":"Redis handles many errors using return values from commands, but there\nare also situations where exceptions can be thrown. In production code,\nyou should handle exceptions as they occur. The list below describes some\nthe most common Redis exceptions:\n\n- `RedisConnectionException`: Thrown when a connection attempt fails.\n- `RedisTimeoutException`: Thrown when a command times out.\n- `RedisCommandException`: Thrown when you issue an invalid command.\n- `RedisServerException`: Thrown when you attempt an invalid operation\n  (for example, trying to access a\n  [stream entry](https://redis.io/docs/latest/develop/data-types/streams#entry-ids)\n  using an invalid ID).\n\nSee [Error handling](https://redis.io/docs/latest/develop/clients/dotnet/error-handling)\nfor more information on handling exceptions."},{"id":"retries","title":"Retries","role":"content","text":"During the initial `ConnectionMultiplexer.Connect()` call, `StackExchange.Redis`\nwill repeat failed connection attempts. By default, it will make three attempts,\nbut you can configure the number of retries using the `ConnectRetry`\nconfiguration option.\n\nYou should also consider the `AbortOnConnectFail` option. If this is `true`,\n`Connect()` fails when it can't connect after the configured number of attempts. If it is\n`false`, `Connect()` can return a disconnected multiplexer and keep trying to\nconnect in the background, which is helpful in cloud environments where\ndependencies may come online out of sequence. The default is `true`, except for\nAzure endpoints, where it defaults to `false`.\n\n[code example]\n\nAfter the initial `Connect()` call is successful, `StackExchange.Redis` will\nautomatically attempt to reconnect if the connection is lost. You can\nspecify a reconnection strategy with the `ReconnectRetryPolicy` configuration\noption. `StackExchange.Redis` provides two built-in classes that implement\nreconnection strategies:\n\n- `ExponentialRetry`: (Default) Uses an\n    [exponential backoff](https://en.wikipedia.org/wiki/Exponential_backoff)\n    strategy, where you specify an increment to the delay between successive\n    attempts and, optionally, a maximum delay, both in milliseconds.\n-   `LinearRetry`: Uses a linear backoff strategy with a fixed delay between\n    attempts, in milliseconds.\n\nThe example below shows how to use the `ExponentialRetry` class:\n\n[code example]\n\nYou can also implement your own custom reconnect policy by creating a class\nthat implements the `IReconnectRetryPolicy` interface.\n\n`StackExchange.Redis` doesn't provide an automated retry mechanism for commands.\nFor command retries, use a bounded retry strategy in your application code and\nretry only transient failures such as `RedisConnectionException` and\n`RedisTimeoutException`. Many .NET apps use a resilience library such as\n[Polly](https://www.pollydocs.org/strategies/retry.html) to manage retry delays,\nbackoff, jitter, and retry limits instead of writing retry loops by hand."}],"examples":[{"id":"checklist-ex0","language":"checklist {id=\"dotnetprodlist\"}","code":"- [ ] [Event handling](#event-handling)\n- [ ] [Timeouts](#timeouts)\n- [ ] [Exception handling](#exception-handling)\n- [ ] [Retries](#retries)","section_id":"checklist"},{"id":"event-handling-ex0","language":"cs","code":"using StackExchange.Redis;\n\nConnectionMultiplexer muxer = ConnectionMultiplexer.Connect(\"localhost:6379\");\n\nmuxer.ServerMaintenanceEvent += (object sender, ServerMaintenanceEvent e) => {\n    // Identify the event and respond to it here.\n    Console.WriteLine($\"Maintenance event: {e.RawMessage}\");\n};","section_id":"event-handling"},{"id":"timeouts-ex0","language":"cs","code":"var muxer = ConnectionMultiplexer.Connect(new ConfigurationOptions {\n    ConnectTimeout = 10000,  // 10 seconds for connections.\n    SyncTimeout = 10000,     // 10 seconds for synchronous commands.\n    AsyncTimeout = 10000     // 10 seconds for asynchronous commands.\n        .\n        .\n});\n\nvar db = muxer.GetDatabase();","section_id":"timeouts"},{"id":"retries-ex0","language":"cs","code":"var muxer = ConnectionMultiplexer.Connect(new ConfigurationOptions {\n    ConnectRetry = 5,  // Retry up to five times.\n    AbortOnConnectFail = false,\n        .\n        .\n});","section_id":"retries"},{"id":"retries-ex1","language":"cs","code":"var muxer = ConnectionMultiplexer.Connect(new ConfigurationOptions {\n    // 500ms increment per attempt, max 2000ms.\n    ReconnectRetryPolicy = new ExponentialRetry(500, 2000),  \n        .\n        .\n});","section_id":"retries"}]}
{"schema_version":2,"id":"develop/clients/dotnet/transpipe","title":"Pipelines and transactions","url":"https://redis.io/docs/latest/develop/clients/dotnet/transpipe/","summary":"Learn how to use Redis pipelines and transactions","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-08T15:21:05+01:00","page_type":"content","content_hash":"9fe9f01480151bbdfd518217eea0a7859f567232a65cf33fec2e97a1e0843eff","sections":[{"id":"execute-a-pipeline","title":"Execute a pipeline","role":"content","text":"`StackExchange.Redis` pipelines commands by using its asynchronous command\nmethods. Start the operations without waiting for each one immediately, then\nwait for their tasks after you have queued the commands. The multiplexer sends\nthe requests as soon as possible and processes the responses when they arrive.\nSee the StackExchange.Redis\n[Pipelines and Multiplexers](https://seredis.dev/PipelinesMultiplexers.html)\npage for more information.\n\nFoundational: Use pipelines to batch multiple commands together and reduce network round trips\n\n**Difficulty:** Beginner\n\n**Available in:** C#\n\n##### C#\n\n[code example]"},{"id":"execute-a-transaction","title":"Execute a transaction","role":"content","text":"A transaction queues commands on an `ITransaction` object that you create with\n`CreateTransaction()`. Call async command methods on that object, then call\n`Execute()` or `ExecuteAsync()` to attempt the transaction. The queued command\ntasks complete after the transaction executes.\n\nFoundational: Use transactions to execute multiple commands atomically without interruption from other clients\n\n**Difficulty:** Beginner\n\n**Available in:** C#\n\n##### C#\n\n[code example]"},{"id":"watch-keys-for-changes","title":"Watch keys for changes","role":"content","text":"Redis supports *optimistic locking* to avoid inconsistent updates\nto different keys. The basic idea is to watch for changes to any\nkeys that you use in a transaction while you are processing the\nupdates. If the watched keys do change, you must restart the updates\nwith the latest data from the keys. See\n[Transactions](https://redis.io/docs/latest/develop/using-commands/transactions)\nfor more information about optimistic locking.\n\nThe approach to optimistic locking that other clients use\n(adding the [`WATCH`](https://redis.io/docs/latest/commands/watch) command\nexplicitly to a transaction) doesn't work well with the\n[multiplexing](https://redis.io/docs/latest/develop/clients/pools-and-muxing)\nsystem that `StackExchange.Redis` uses.\nInstead, `StackExchange.Redis` relies on conditional execution of commands\nto get a similar effect.\n\nUse the `AddCondition()` method to abort a transaction if a particular\ncondition doesn't hold throughout its execution. If the transaction\ndoes abort then the `Execute()` method returns a `false` value,\nbut otherwise returns `true`.\n\nFor example, the `KeyNotExists` condition aborts the transaction\nif a specified key exists or is added by another client while the\ntransaction executes:\n\nOptimistic locking: Use conditions to monitor keys for changes and abort transactions when conflicts occur\n\n**Difficulty:** Intermediate\n\n**Available in:** C#\n\n##### C#\n\n[code example]\n\n\n\nYou can also use a `When` condition on certain individual commands to\nspecify that they only execute when a certain condition holds\n(for example, the command does not change an existing key).\nSee\n[Conditional execution](https://redis.io/docs/latest/develop/clients/dotnet/condexec)\nfor a full description of transaction and command conditions."}],"examples":[{"id":"execute-a-pipeline-ex0","language":"csharp","code":"var setTasks = new[]\n        {\n            db.StringSetAsync(\"seat:0\", \"#0\"),\n            db.StringSetAsync(\"seat:1\", \"#1\"),\n            db.StringSetAsync(\"seat:2\", \"#2\"),\n            db.StringSetAsync(\"seat:3\", \"#3\"),\n            db.StringSetAsync(\"seat:4\", \"#4\")\n        };\n        foreach (var setTask in setTasks)\n        {\n            db.Wait(setTask);\n        }\n\n        var resp1Task = db.StringGetAsync(\"seat:0\");\n        var resp2Task = db.StringGetAsync(\"seat:3\");\n        var resp3Task = db.StringGetAsync(\"seat:4\");\n\n        var resp1 = db.Wait(resp1Task);\n        Console.WriteLine(resp1); // >>> #0\n\n        var resp2 = db.Wait(resp2Task);\n        Console.WriteLine(resp2); // >>> #3\n\n        var resp3 = db.Wait(resp3Task);\n        Console.WriteLine(resp3); // >>> #4","section_id":"execute-a-pipeline"},{"id":"execute-a-transaction-ex0","language":"csharp","code":"var trans = db.CreateTransaction();\n\n        var incr1 = trans.StringIncrementAsync(\"counter:1\", 1);\n        var incr2 = trans.StringIncrementAsync(\"counter:2\", 2);\n        var incr3 = trans.StringIncrementAsync(\"counter:3\", 3);\n\n        bool committed = db.Wait(trans.ExecuteAsync());\n\n        var resp4 = db.Wait(incr1);\n        Console.WriteLine(resp4); // >>> 1\n\n        var resp5 = db.Wait(incr2);\n        Console.WriteLine(resp5); // >>> 2\n\n        var resp6 = db.Wait(incr3);\n        Console.WriteLine(resp6);  // >>> 3","section_id":"execute-a-transaction"},{"id":"watch-keys-for-changes-ex0","language":"csharp","code":"var watchedTrans = db.CreateTransaction();\n\n        watchedTrans.AddCondition(Condition.KeyNotExists(\"customer:39182\"));\n\n        var hashSetTask = watchedTrans.HashSetAsync(\n            \"customer:39182\",\n            [\n                new(\"name\", \"David\"),\n                new(\"age\", \"27\")\n            ]\n        );\n\n        bool succeeded = db.Wait(watchedTrans.ExecuteAsync());\n        db.Wait(hashSetTask);\n        Console.WriteLine(succeeded); // >>> true","section_id":"watch-keys-for-changes"}]}
{"schema_version":2,"id":"develop/clients/dotnet/vecsets","title":"Vector set embeddings","url":"https://redis.io/docs/latest/develop/clients/dotnet/vecsets/","summary":"Index and query embeddings with Redis vector sets","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-18T16:06:13+01:00","page_type":"content","content_hash":"b96fe805ff21959e903d786b914abf1ba61ebb42605630a01c9a21136cb0619c","sections":[{"id":"initialize","title":"Initialize","role":"content","text":"Start by installing `StackExchange.Redis` with the following\ncommand (version 2.9.17 or later is required for vector sets):\n\n[code example]\n\nAlso, install `Microsoft.ML`:\n\n[code example]\n\nIn a new C# file, import the required classes:\n\nFoundational: Import required libraries for vector sets, embeddings, and Redis operations\n\n**Difficulty:** Beginner\n\n**Available in:** C#\n\n##### C#\n\n[code example]"},{"id":"access-the-model","title":"Access the model","role":"content","text":"Use the `GetPredictionEngine()` helper function declared in the example below to load the model that creates the embeddings:\n\nPractical pattern: Initialize a SentenceTransformer model to generate vector embeddings from text\n\n**Difficulty:** Beginner\n\n**Available in:** C#\n\n##### C#\n\n[code example]\n\n\n\nThe `GetPredictionEngine()` function uses two classes, `TextData` and `TransformedTextData`,\nto specify the `PredictionEngine` model. These have a very simple definition\nand are required because the model expects the input and output to be\npassed in named object fields:\n\nFoundational: Define data classes for the embedding model's input and output structures\n\n**Difficulty:** Beginner\n\n**Available in:** C#\n\n##### C#\n\n[code example]\n\n\n\nNote that you must declare these classes at the end of the source file\nif you are using a console app without a main class.\n\nThe `GetEmbedding()` function declared below can then use this model to\ngenerate an embedding from a section of text and return it as a `float[]` array,\nwhich is the format required by the vector set API:\n\nFoundational: Create a function to generate vector embeddings from text using the prediction engine\n\n**Difficulty:** Beginner\n\n**Available in:** C#\n\n##### C#\n\n[code example]"},{"id":"create-the-data","title":"Create the data","role":"content","text":"The example data is contained a `Dictionary` object with some brief\ndescriptions of famous people:\n\nFoundational: Define sample data with text descriptions for vector embedding and storage\n\n**Difficulty:** Beginner\n\n**Available in:** C#\n\n##### C#\n\n[code example]"},{"id":"add-the-data-to-a-vector-set","title":"Add the data to a vector set","role":"content","text":"The next step is to connect to Redis and add the data to a new vector set.\n\nThe code below iterates through `peopleData` and adds corresponding\nelements to a vector set called `famousPeople`.\n\nUse the `GetEmbedding()` function declared above to generate the\nembedding as a `byte` array that you can pass to the\n[`VectorSetAdd()`](https://redis.io/docs/latest/commands/vadd) command to set the embedding.\n\nThe call to `VectorSetAdd()` also adds the `born` and `died` values from the\noriginal dictionary as attribute data. You can access this during a query\nor by using the [`VectorSetGetAttributesJson()`](https://redis.io/docs/latest/commands/vgetattr) method.\n\nFoundational: Add vector embeddings and attributes to a vector set using VADD command\n\n**Difficulty:** Beginner\n\n**Available in:** C#\n\n##### C#\n\n[code example]"},{"id":"query-the-vector-set","title":"Query the vector set","role":"content","text":"You can now query the data in the set. The basic approach is to use the\n`GetEmbedding()` function to generate another embedding vector for the query text.\n(This is the same method used to add the elements to the set.) Then, pass\nthe query vector to [`VectorSetSimilaritySearch()`](https://redis.io/docs/latest/commands/vsim) to \nreturn elements of the set, ranked in order of similarity to the query.\n\nStart with a simple query for \"actors\":\n\nVector similarity search: Find semantically similar items in a vector set using VSIM command\n\n**Difficulty:** Intermediate\n\n**Available in:** C#\n\n##### C#\n\n[code example]\n\n\n\nThis returns the following list of elements (formatted slightly for clarity):\n\n[code example]\n\nThe first two people in the list are the two actors, as expected, but none of the\npeople from Linus Pauling onward was especially well-known for acting (and there certainly\nisn't any information about that in the short description text).\nAs it stands, the search attempts to rank all the elements in the set, based\non the information contained in the embedding model.\nYou can use the `Count` property of `VectorSetSimilaritySearchRequest` to limit the\nlist of elements to just the most relevant few items:\n\nVector similarity search with limits: Restrict results to the top K most similar items using the count parameter\n\n**Difficulty:** Intermediate\n\n**Available in:** C#\n\n##### C#\n\n[code example]\n\n\n\nThe reason for using text embeddings rather than simple text search\nis that the embeddings represent semantic information. This allows a query\nto find elements with a similar meaning even if the text is\ndifferent. For example, the word \"entertainer\" doesn't appear in any of the\ndescriptions but if you use it as a query, the actors and musicians are ranked\nhighest in the results list:\n\nSemantic search: Leverage text embeddings to find semantically similar items even when exact keywords don't match\n\n**Difficulty:** Intermediate\n\n**Available in:** C#\n\n##### C#\n\n[code example]\n\n\n\nSimilarly, if you use \"science\" as a query, you get the following results:\n\n[code example]\n\nThe scientists are ranked highest but they are then followed by the\nmathematicians. This seems reasonable given the connection between mathematics\nand science.\n\nYou can also use\n[filter expressions](https://redis.io/docs/latest/develop/data-types/vector-sets/filtered-search)\nwith `VectorSetSimilaritySearch()` to restrict the search further. For example,\nrepeat the \"science\" query, but this time limit the results to people\nwho died before the year 2000:\n\nFiltered vector search: Combine vector similarity with attribute filters to narrow results based on metadata conditions\n\n**Difficulty:** Advanced\n\n**Available in:** C#\n\n##### C#\n\n[code example]\n\n\n\nNote that the boolean filter expression is applied to items in the list\nbefore the vector distance calculation is performed. Items that don't\npass the filter test are removed from the results completely, rather\nthan just reduced in rank. This can help to improve the performance of the\nsearch because there is no need to calculate the vector distance for\nelements that have already been filtered out of the search."},{"id":"more-information","title":"More information","role":"content","text":"See the [vector sets](https://redis.io/docs/latest/develop/data-types/vector-sets)\ndocs for more information and code examples. See the\n[Redis for AI](https://redis.io/docs/latest/develop/ai) section for more details\nabout text embeddings and other AI techniques you can use with Redis.\n\nYou may also be interested in\n[vector search](https://redis.io/docs/latest/develop/clients/dotnet/nredisstack/vecsearch).\nThis is a feature of \n[Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query)\nthat lets you retrieve\n[JSON](https://redis.io/docs/latest/develop/data-types/json) and\n[hash](https://redis.io/docs/latest/develop/data-types/hashes) documents based on\nvector data stored in their fields."}],"examples":[{"id":"initialize-ex0","language":"bash","code":"dotnet add package StackExchange.Redis --version 2.9.17","section_id":"initialize"},{"id":"initialize-ex1","language":"bash","code":"dotnet add package Microsoft.ML","section_id":"initialize"},{"id":"initialize-ex2","language":"csharp","code":"using StackExchange.Redis;\n\nusing Microsoft.ML;\nusing Microsoft.ML.Transforms.Text;","section_id":"initialize"},{"id":"access-the-model-ex0","language":"csharp","code":"static PredictionEngine<TextData, TransformedTextData> GetPredictionEngine()\n{\n    // Create a new ML context, for ML.NET operations. It can be used for\n    // exception tracking and logging, as well as the source of randomness.\n    var mlContext = new MLContext();\n\n    // Create an empty list as the dataset\n    var emptySamples = new List<TextData>();\n\n    // Convert sample list to an empty IDataView.\n    var emptyDataView = mlContext.Data.LoadFromEnumerable(emptySamples);\n\n    // A pipeline for converting text into a 150-dimension embedding vector\n    var textPipeline = mlContext.Transforms.Text.NormalizeText(\"Text\")\n        .Append(mlContext.Transforms.Text.TokenizeIntoWords(\"Tokens\",\n            \"Text\"))\n        .Append(mlContext.Transforms.Text.ApplyWordEmbedding(\"Features\",\n            \"Tokens\", WordEmbeddingEstimator.PretrainedModelKind\n            .SentimentSpecificWordEmbedding));\n\n    // Fit to data.\n    var textTransformer = textPipeline.Fit(emptyDataView);\n\n    // Create the prediction engine to get the embedding vector from the input text/string.\n    var predictionEngine = mlContext.Model.CreatePredictionEngine<TextData,\n        TransformedTextData>(textTransformer);\n\n    return predictionEngine;\n}","section_id":"access-the-model"},{"id":"access-the-model-ex1","language":"csharp","code":"class TextData\n{\n    public string Text { get; set; } = string.Empty;\n}\n\nclass TransformedTextData : TextData\n{\n    public float[] Features { get; set; } = Array.Empty<float>();\n}","section_id":"access-the-model"},{"id":"access-the-model-ex2","language":"csharp","code":"static float[] GetEmbedding(\n    PredictionEngine<TextData, TransformedTextData> model, string sentence\n)\n{\n    // Call the prediction API to convert the text into embedding vector.\n    var data = new TextData()\n    {\n        Text = sentence\n    };\n\n    var prediction = model.Predict(data);\n\n    float[] floatArray = Array.ConvertAll(prediction.Features, x => (float)x);\n\n    return floatArray;\n}","section_id":"access-the-model"},{"id":"create-the-data-ex0","language":"csharp","code":"Dictionary<string, dynamic> peopleData = new Dictionary<string, dynamic>\n{\n    [\"Marie Curie\"] = new\n    {\n        born = 1867,\n        died = 1934,\n        description = @\"\n        Polish-French chemist and physicist. The only person ever to win\n        two Nobel prizes for two different sciences.\n        \"\n    },\n    [\"Linus Pauling\"] = new\n    {\n        born = 1901,\n        died = 1994,\n        description = @\"\n        American chemist and peace activist. One of only two people to win two\n        Nobel prizes in different fields (chemistry and peace).\n        \"\n    },\n    [\"Freddie Mercury\"] = new\n    {\n        born = 1946,\n        died = 1991,\n        description = @\"\n        British musician, best known as the lead singer of the rock band\n        Queen.\n        \"\n    },\n    [\"Marie Fredriksson\"] = new\n    {\n        born = 1958,\n        died = 2019,\n        description = @\"\n        Swedish multi-instrumentalist, mainly known as the lead singer and\n        keyboardist of the band Roxette.\n        \"\n    },\n    [\"Paul Erdos\"] = new\n    {\n        born = 1913,\n        died = 1996,\n        description = @\"\n        Hungarian mathematician, known for his eccentric personality almost\n        as much as his contributions to many different fields of mathematics.\n        \"\n    },\n    [\"Maryam Mirzakhani\"] = new\n    {\n        born = 1977,\n        died = 2017,\n        description = @\"\n        Iranian mathematician. The first woman ever to win the Fields medal\n        for her contributions to mathematics.\n        \"\n    },\n    [\"Masako Natsume\"] = new\n    {\n        born = 1957,\n        died = 1985,\n        description = @\"\n        Japanese actress. She was very famous in Japan but was primarily\n        known elsewhere in the world for her portrayal of Tripitaka in the\n        TV series Monkey.\n        \"\n    },\n    [\"Chaim Topol\"] = new\n    {\n        born = 1935,\n        died = 2023,\n        description = @\"\n        Israeli actor and singer, usually credited simply as 'Topol'. He was\n        best known for his many appearances as Tevye in the musical Fiddler\n        on the Roof.\n        \"\n    }\n};","section_id":"create-the-data"},{"id":"add-the-data-to-a-vector-set-ex0","language":"csharp","code":"ConnectionMultiplexer muxer = ConnectionMultiplexer.Connect(\"localhost:6379\");\nIDatabase db = muxer.GetDatabase();\n\nPredictionEngine<TextData, TransformedTextData> model = GetPredictionEngine();\n\nforeach (KeyValuePair<string, dynamic> person in peopleData)\n{\n    string name = person.Key;\n    dynamic details = person.Value;\n    float[] embedding = GetEmbedding(model, details.description);\n\n    VectorSetAddRequest addRequest = VectorSetAddRequest.Member(name, embedding, null);\n    db.VectorSetAdd(\"famousPeople\", addRequest);\n\n    // Set attributes separately\n    string attributesJson = $\"{{\\\"born\\\": {details.born}, \\\"died\\\": {details.died}}}\";\n    db.VectorSetSetAttributesJson(\"famousPeople\", name, attributesJson);\n}","section_id":"add-the-data-to-a-vector-set"},{"id":"query-the-vector-set-ex0","language":"csharp","code":"string queryValue = \"actors\";\n\nfloat[] queryEmbedding = GetEmbedding(model, queryValue);\n\nVectorSetSimilaritySearchRequest basicQuery = VectorSetSimilaritySearchRequest.ByVector(queryEmbedding);\nusing (Lease<VectorSetSimilaritySearchResult>? actorsResults = db.VectorSetSimilaritySearch(\"famousPeople\", basicQuery))\n{\n    IEnumerable<string> resultIds = actorsResults!.Span.ToArray()\n        .Select(r => (string?)r.Member)\n        .Where(id => id != null)\n        .Select(id => $\"'{id!}'\");\n    Console.WriteLine($\"'actors': [{string.Join(\", \", resultIds)}]\");\n}","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex1","language":"plaintext","code":"'actors': ['Masako Natsume', 'Chaim Topol', 'Linus Pauling',\n'Marie Fredriksson', 'Maryam Mirzakhani', 'Marie Curie',\n'Freddie Mercury', 'Paul Erdos']","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex2","language":"csharp","code":"queryValue = \"actors\";\n\nqueryEmbedding = GetEmbedding(model, queryValue);\n\nVectorSetSimilaritySearchRequest limitedQuery = VectorSetSimilaritySearchRequest.ByVector(queryEmbedding);\nlimitedQuery.Count = 2;\nusing (Lease<VectorSetSimilaritySearchResult>? twoActorsResults = db.VectorSetSimilaritySearch(\"famousPeople\", limitedQuery))\n{\n    IEnumerable<string> resultIds = twoActorsResults!.Span.ToArray()\n        .Select(r => (string?)r.Member)\n        .Where(id => id != null)\n        .Select(id => $\"'{id!}'\");\n    Console.WriteLine($\"'actors (2)': [{string.Join(\", \", resultIds)}]\");\n    // >>> 'actors (2)': ['Masako Natsume', 'Chaim Topol']\n}","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex3","language":"csharp","code":"queryValue = \"entertainer\";\n\nqueryEmbedding = GetEmbedding(model, queryValue);\n\nVectorSetSimilaritySearchRequest entertainerQuery = VectorSetSimilaritySearchRequest.ByVector(queryEmbedding);\nusing (Lease<VectorSetSimilaritySearchResult>? entertainerResults = db.VectorSetSimilaritySearch(\"famousPeople\", entertainerQuery))\n{\n    IEnumerable<string> resultIds = entertainerResults!.Span.ToArray()\n        .Select(r => (string?)r.Member)\n        .Where(id => id != null)\n        .Select(id => $\"'{id!}'\");\n    Console.WriteLine($\"'entertainer': [{string.Join(\", \", resultIds)}]\");\n    // >>> 'entertainer': ['Chaim Topol', 'Freddie Mercury',\n    // 'Marie Fredriksson', 'Masako Natsume', 'Linus Pauling',\n    // 'Paul Erdos', 'Maryam Mirzakhani', 'Marie Curie']\n}","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex4","language":"plaintext","code":"'science': ['Marie Curie', 'Linus Pauling', 'Maryam Mirzakhani',\n'Paul Erdos', 'Marie Fredriksson', 'Freddie Mercury', 'Masako Natsume',\n'Chaim Topol']","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex5","language":"csharp","code":"queryValue = \"science\";\n\nqueryEmbedding = GetEmbedding(model, queryValue);\n\nVectorSetSimilaritySearchRequest filteredQuery = VectorSetSimilaritySearchRequest.ByVector(queryEmbedding);\nfilteredQuery.FilterExpression = \".died < 2000\";\nusing (Lease<VectorSetSimilaritySearchResult>? science2000Results = db.VectorSetSimilaritySearch(\"famousPeople\", filteredQuery))\n{\n    IEnumerable<string> resultIds = science2000Results!.Span.ToArray()\n        .Select(r => (string?)r.Member)\n        .Where(id => id != null)\n        .Select(id => $\"'{id!}'\");\n    Console.WriteLine($\"'science2000': [{string.Join(\", \", resultIds)}]\");\n    // >>> 'science2000': ['Marie Curie', 'Linus Pauling',\n    // 'Paul Erdos', 'Freddie Mercury', 'Masako Natsume']\n}","section_id":"query-the-vector-set"}]}
{"schema_version":2,"id":"develop/clients/error-handling","title":"Error handling","url":"https://redis.io/docs/latest/develop/clients/error-handling/","summary":"Learn how to handle errors when using Redis client libraries.","tags":[],"last_updated":"2026-09-09T13:53:13+01:00","page_type":"content","content_hash":"a15bc7ba6a6cd3827537ebbd5c92707a202ef20bb106686be3c4c0fff1113811","sections":[{"id":"overview","title":"Overview","role":"overview","text":"When working with Redis, errors can occur for various reasons, including network issues, invalid commands, or resource constraints. This guide explains the types of errors you might encounter and how to handle them effectively."},{"id":"categories-of-errors","title":"Categories of errors","role":"content","text":"Redis errors fall into four main categories. The table below provides a quick overview of each type. Click on any error type to jump to its detailed section, which includes common causes, examples, handling strategies, and code examples.\n\n| Error Type | Common Causes | When to Handle | Examples |\n|---|---|---|---|\n| [Connection errors](#connection-errors) | Network issues, server down, auth failure, timeouts, pool exhaustion | Almost always | `ConnectionError`, `TimeoutError`, `AuthenticationError` |\n| [Command errors](#command-errors) | Typo in command, wrong arguments, invalid types, unsupported command | Rarely (usually indicates a bug) | `ResponseError`, `WRONGTYPE`, `ERR unknown command` |\n| [Data errors](#data-errors) | Serialization failures, corrupted data, type mismatches | Sometimes (depends on data source) | `JSONDecodeError`, `SerializationError`, `WRONGTYPE` |\n| [Resource errors](#resource-errors) | Memory limit, pool exhausted, too many connections, key eviction | Sometimes (some are temporary) | `OOM`, pool timeout, `LOADING` |"},{"id":"connection-errors","title":"Connection errors","role":"content","text":"Connection errors occur when your application cannot communicate with Redis. These are typically temporary and often recoverable.\n\n**Common causes:**\n- Network connectivity issues\n- Redis server is down or unreachable\n- Authentication failure\n- Connection timeout\n- Connection pool exhaustion\n\n**Examples:**\n- `ConnectionError`: Network failure or server unreachable\n- `TimeoutError`: Operation exceeded the configured timeout\n- `AuthenticationError`: Invalid credentials\n\n**When to handle:** Almost always. Connection errors are usually temporary, so implementing retry logic or fallback strategies is recommended.\n\n**Example strategy:**\n\n[code example]"},{"id":"command-errors","title":"Command errors","role":"syntax","text":"Command errors occur when Redis receives an invalid or malformed command. These typically indicate a bug in your code.\n\n**Common causes:**\n-   Typo in command name\n-   Wrong number of arguments\n-   Invalid argument types (for example, supplying a\n    [string](https://redis.io/docs/latest/develop/data-types/strings) key to a\n    [list](https://redis.io/docs/latest/develop/data-types/lists) command))\n-   Using a command that doesn't exist in your Redis version\n\n**Examples:**\n- `ResponseError`: Invalid command or syntax error\n- `WRONGTYPE Operation against a key holding the wrong kind of value`\n- `ERR unknown command`\n\n**When to handle:** Rarely. These usually indicate programming error and so you\nshould fix the errors in your code rather than attempt to handle them at runtime. However, some cases (like invalid user input) may be worth handling.\n\n**Example:**\n\n[code example]"},{"id":"data-errors","title":"Data errors","role":"content","text":"Data errors occur when there are problems with the data itself, such as\nserialization failures, or data corruption.\n\n**Common causes:**\n- Object cannot be serialized to JSON\n- Cached data is corrupted\n- Attempting to deserialize invalid data\n\n**Examples:**\n- `JSONDecodeError`: Cannot deserialize JSON data\n- `SerializationError`: Cannot serialize object\n\n**When to handle:** Sometimes. If the error is due to user input or external data, handle it gracefully. If it's due to your code, fix the code.\n\n**Example:**\n\n[code example]"},{"id":"resource-errors","title":"Resource errors","role":"content","text":"Resource errors occur when Redis runs out of resources or hits limits.\n\n**Common causes:**\n- Memory limit reached\n- Connection pool exhausted\n- Too many connections\n- Key eviction due to memory pressure\n\n**Examples:**\n- `OOM command not allowed when used memory > 'maxmemory'`\n- Connection pool timeout\n- `LOADING Redis is loading the dataset in memory`\n\n**When to handle:** Sometimes. Some resource errors are temporary (Redis loading), while others indicate a configuration problem.\n\n**Example:**\n\n[code example]"},{"id":"error-handling-patterns","title":"Error handling patterns","role":"errors","text":""},{"id":"pattern-1-fail-fast","title":"Pattern 1: Fail fast","role":"content","text":"Use this when the error is unrecoverable or indicates a bug in your code.\n\n**When to use:**\n\n- Command errors (invalid syntax)\n- Authentication errors\n- Programming errors\n\n**Example:**\n\n[code example]"},{"id":"pattern-2-graceful-degradation","title":"Pattern 2: Graceful degradation","role":"content","text":"Use this when you have an alternative way to get the data you need, so you can\nfall back to using the alternative instead of the preferred code.\n\n**When to use:**\n\n- Cache reads (fallback to database)\n- Session reads (fallback to default values)\n- Optional data (skip if unavailable)\n\n**Example:**\n\n[code example]"},{"id":"pattern-3-retry-with-backoff","title":"Pattern 3: Retry with backoff","role":"content","text":"Use this when the error could be due to network load or other temporary\nconditions.\n\n**When to use:**\n\n- Connection timeouts\n- Temporary network issues\n- Redis loading data\n\n**Example:**\n\n[code example]\n\nNote that client libraries often implement retry logic for you, so\nyou may just need to provide the right configuration rather than\nimplementing retries yourself. See [Client-specific error handling](#client-specific-error-handling) below for links to pages that\ndescribe retry configuration for each client library."},{"id":"pattern-4-log-and-continue","title":"Pattern 4: Log and continue","role":"content","text":"Use this when the operation is not critical to your application.\n\n**When to use:**\n\n- Cache writes (data loss is acceptable)\n- Non-critical updates\n- Metrics collection\n\n**Example:**\n\n[code example]"},{"id":"decision-tree-how-to-handle-errors","title":"Decision tree: How to handle errors","role":"content","text":"[code example]"},{"id":"logging-and-monitoring","title":"Logging and monitoring","role":"content","text":"In production, you may find it useful to log errors when they\noccur and monitor the logs for patterns. This can help you identify\nwhich errors are most common and whether your retry and fallback\nstrategies are effective. Note that some Redis client\nlibraries have built-in instrumentation that can provide this\ninformation for you (see [Observability](https://redis.io/docs/latest/develop/clients/observability)\nfor a full description)."},{"id":"what-to-log","title":"What to log","role":"content","text":"- **Error type and message:** What went wrong?\n- **Context:** Which key? Which operation?\n- **Timestamp:** When did it happen?\n- **Retry information:** Is this a retry? How many attempts?\n\n**Example:**\n[code example]"},{"id":"what-to-monitor","title":"What to monitor","role":"content","text":"- **Error rate:** How many errors per minute?\n- **Error types:** Which errors are most common?\n- **Recovery success:** How many retries succeed?\n- **Fallback usage:** How often do we use fallback strategies?\n\nThese metrics help you identify patterns and potential issues."},{"id":"common-mistakes","title":"Common mistakes","role":"content","text":""},{"id":"catching-all-exceptions","title":"Catching all exceptions","role":"content","text":"**Problem:** If you catch all exceptions, you might catch unexpected\nerrors and hide bugs.\n\n**Example (wrong):**\n\n[code example]\n\n**Better approach:** Catch specific exception types.\n\n**Example (correct):**\n\n[code example]"},{"id":"not-distinguishing-error-types","title":"Not distinguishing error types","role":"content","text":"**Problem:** Different errors need different handling. For example, retrying a syntax error won't help.\n\n**Example (wrong):**\n\n[code example]\n\n**Better approach:** Handle each error type differently based on whether or not\nit is recoverable.\n\n**Example (correct):**\n\n[code example]"},{"id":"ignoring-connection-pool-errors","title":"Ignoring connection pool errors","role":"content","text":"**Problem:** Connection pool errors indicate a configuration or concurrency issue that needs to be addressed.\n\n**Example (wrong):**\n\n[code example]\n\n**Better approach:** Monitor pool usage and increase size if needed."},{"id":"client-specific-error-handling","title":"Client-specific error handling","role":"content","text":"For detailed information about exceptions in your client library, see:\n\n- [redis-py error handling](https://redis.io/docs/latest/develop/clients/redis-py/error-handling)\n- [Node.js error handling](https://redis.io/docs/latest/develop/clients/nodejs/error-handling)\n- [Java (Jedis) error handling](https://redis.io/docs/latest/develop/clients/jedis/error-handling)\n- [Java (Lettuce) error handling](https://redis.io/docs/latest/develop/clients/lettuce/error-handling)\n- [Go (go-redis) error handling](https://redis.io/docs/latest/develop/clients/go/error-handling)\n- [.NET (StackExchange.Redis) error handling](https://redis.io/docs/latest/develop/clients/dotnet/error-handling)\n- [PHP (Predis) error handling](https://redis.io/docs/latest/develop/clients/php/error-handling)\n- [Ruby (redis-rb) error handling](https://redis.io/docs/latest/develop/clients/ruby/error-handling)"}],"examples":[{"id":"connection-errors-ex0","language":"mermaid {width=\"80%\"}","code":"graph TB\n    A[\"Try to connect<br/>to Redis\"]\n    A -->|Success| B([\"Use the result\"])\n    A -->|Failure| C{Error type?}\n    C -->|Timeout| D([\"Retry with<br/>exponential backoff\"])\n    C -->|Auth failure| E([\"Check credentials<br/>and fail\"])\n    C -->|Network error| F([\"Fallback to<br/>alternative data source\"])","section_id":"connection-errors"},{"id":"command-errors-ex0","language":"mermaid {width=\"60%\"}","code":"graph TB\n    A[\"User provides<br/>JSONPath expression\"]\n    A --> B[\"Try to execute it\"]\n    direction TB\n    B -->|ResponseError| C[\"Log the error\"]\n    C --> D([\"Return default value<br/>or error message to user\"])\n    B -->|Success| E([\"Use the result\"])","section_id":"command-errors"},{"id":"data-errors-ex0","language":"mermaid {width=\"50%\"}","code":"graph TB\n    A[\"Read cached data\"]\n    A --> B[\"Try to deserialize\"]\n    B -->|Success| C([\"Use the data\"])\n    B -->|Deserialization fails| D[\"Log the error\"]\n    D --> E[\"Delete corrupted<br/>cache entry\"]\n    E --> F([\"Fetch fresh data<br/>from source\"])","section_id":"data-errors"},{"id":"resource-errors-ex0","language":"mermaid {width=\"80%\"}","code":"graph TB\n    A{Resource error<br/>occurred?}\n    A -->|Redis loading| B([\"Retry after<br/>a delay\"])\n    A -->|Memory full| C([\"Check Redis<br/>configuration and data\"])\n    A -->|Pool exhausted| D([\"Increase pool size<br/>or reduce concurrency\"])","section_id":"resource-errors"},{"id":"pattern-1-fail-fast-ex0","language":"python","code":"try:\n    result = r.get(key)\nexcept redis.ResponseError as e:\n    # This indicates a bug in our code\n    raise  # Re-raise the exception","section_id":"pattern-1-fail-fast"},{"id":"pattern-2-graceful-degradation-ex0","language":"python","code":"try:\n    cached_value = r.get(key)\n    if cached_value:\n        return cached_value\nexcept redis.ConnectionError:\n    logger.warning(\"Cache unavailable, using database\")\n\n# Fallback to database\nreturn database.get(key)","section_id":"pattern-2-graceful-degradation"},{"id":"pattern-3-retry-with-backoff-ex0","language":"python","code":"import time\n\nmax_retries = 3\nretry_delay = 0.1\n\nfor attempt in range(max_retries):\n    try:\n        return r.get(key)\n    except redis.TimeoutError:\n        if attempt < max_retries - 1:\n            time.sleep(retry_delay)\n            retry_delay *= 2  # Exponential backoff\n        else:\n            raise","section_id":"pattern-3-retry-with-backoff"},{"id":"pattern-4-log-and-continue-ex0","language":"python","code":"try:\n    r.setex(key, 3600, value)\nexcept redis.ConnectionError:\n    logger.warning(f\"Failed to cache {key}, continuing without cache\")\n    # Application continues normally","section_id":"pattern-4-log-and-continue"},{"id":"decision-tree-how-to-handle-errors-ex0","language":"mermaid","code":"graph LR\n    Start{Error occurred?}\n\n    Start -->|Connection error| C1{Operation type?}\n    C1 -->|Read| C2[\"Graceful degradation<br/>fallback\"]\n    C1 -->|Write| C3[\"Log and continue<br/>or retry\"]\n    C1 -->|Critical| C4[\"Retry with backoff\"]\n\n    Start -->|Command error| Cmd1{Error source?}\n    Cmd1 -->|User input| Cmd2[\"Log and return<br/>error to user\"]\n    Cmd1 -->|Your code| Cmd3[\"Fail fast<br/>fix the bug\"]\n\n    Start -->|Data error| D1{Operation type?}\n    D1 -->|Read| D2[\"Log, invalidate,<br/>fallback\"]\n    D1 -->|Write| D3[\"Log and fail<br/>data is invalid\"]\n\n    Start -->|Resource error| R1{Error type?}\n    R1 -->|Redis loading| R2[\"Retry with backoff\"]\n    R1 -->|Pool exhausted| R3[\"Increase pool size\"]\n    R1 -->|Memory full| R4[\"Check configuration\"]","section_id":"decision-tree-how-to-handle-errors"},{"id":"what-to-log-ex0","language":"python","code":"logger.error(\n    \"Redis operation failed\",\n    extra={\n        \"error_type\": type(e).__name__,\n        \"operation\": \"get\",\n        \"key\": key,\n        \"attempt\": attempt,\n        \"timestamp\": datetime.now().isoformat(),\n    }\n)","section_id":"what-to-log"},{"id":"catching-all-exceptions-ex0","language":"python","code":"try:\n    result = r.get(key)\nexcept Exception:  # Too broad - some errors indicate code problems.\n    pass","section_id":"catching-all-exceptions"},{"id":"catching-all-exceptions-ex1","language":"python","code":"try:\n    result = r.get(key)\nexcept redis.ConnectionError:\n    # Handle connection error\n    pass","section_id":"catching-all-exceptions"},{"id":"not-distinguishing-error-types-ex0","language":"python","code":"try:\n    result = r.get(key)\nexcept redis.ResponseError:\n    # Retry? This won't help if it's a syntax error.\n    retry()","section_id":"not-distinguishing-error-types"},{"id":"not-distinguishing-error-types-ex1","language":"python","code":"try:\n    result = r.get(key)\nexcept redis.TimeoutError:\n    retry()  # Retry on timeout\nexcept redis.ResponseError:\n    raise   # Fail on syntax error","section_id":"not-distinguishing-error-types"},{"id":"ignoring-connection-pool-errors-ex0","language":"python","code":"# Pool is exhausted, but we don't handle it\nresult = r.get(key)  # Might timeout waiting for connection","section_id":"ignoring-connection-pool-errors"}]}
{"schema_version":2,"id":"develop/clients/failover","title":"Client-side geographic failover","url":"https://redis.io/docs/latest/develop/clients/failover/","summary":"Improve reliability using the failover/failback features of client libraries.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-29T16:29:37+01:00","page_type":"content","content_hash":"5df9685d826ae60d17c235d8380b4acf25310a837db920e0f1d5c65c5a662613","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Some Redis client libraries support\n[Client-side geographic failover](https://en.wikipedia.org/wiki/Failover)\nto improve the availability of connections to Redis databases. Use this page\nto get a general overview of the concepts and then see the documentation for\nyour client library to learn how to configure it for failover and failback."},{"id":"supported-client-libraries","title":"Supported client libraries","role":"compatibility","text":"The table below lists the clients that support client-side geographic failover\nalong with their release state and available features.\n\n| Client | Basic failover | Pub/sub failover | OSS Cluster failover | Failback |\n| :-- | :-- | :-- | :-- | :-- |\n| [Jedis](https://redis.io/docs/latest/develop/clients/jedis/failover) | Yes | No | No | Yes |\n| [redis-py](https://redis.io/docs/latest/develop/clients/redis-py/failover) | Yes  (Preview) | Yes | Yes | Yes |\n| [Lettuce](https://redis.io/docs/latest/develop/clients/lettuce/failover) | Yes (Preview) | Yes | No | Yes |\n| [StackExchange.Redis](https://redis.io/docs/latest/develop/clients/dotnet/failover) | Yes | Yes | Yes | Yes |"},{"id":"concepts","title":"Concepts","role":"content","text":"You may have several [Active-Active databases](https://redis.io/docs/latest/operate/rs/databases/active-active)\nor independent Redis servers that are all suitable to serve your app.\nTypically, you would prefer to use some database endpoints over others for a particular\ninstance of your app (perhaps the ones that are closest geographically to the app server\nto reduce network latency). However, if the best endpoint is not available due\nto a failure, it is generally better to switch to another, suboptimal endpoint\nthan to let the app fail completely.\n\n*Failover* is the technique of actively checking for connection failures or\nunacceptably slow connections and automatically switching to the best available endpoint \nwhen they occur. This requires you to specify a list of endpoints to try, ordered by priority. The diagram below shows this process:\n\n![Failover and client reconnection](https://redis.io/docs/latest/images/failover/failover-client-reconnect.svg)\n\nThe complementary technique of *failback* then involves periodically checking the health\nof all endpoints that have failed. If any endpoints recover, the failback mechanism\nautomatically switches the connection to the one with the highest priority. \nThis could potentially be repeated until the optimal endpoint is available again.\n\n![Failback: client switches back to original server](https://redis.io/docs/latest/images/failover/failover-client-failback.svg)"},{"id":"detecting-connection-problems","title":"Detecting connection problems","role":"content","text":"Redis clients use a [circuit breaker design pattern](https://en.wikipedia.org/wiki/Circuit_breaker_design_pattern) to detect connection problems.\n\nThe circuit breaker is a software component that tracks the sequence of recent\nRedis connection attempts and commands, recording which ones have succeeded and\nwhich have failed.\n(Note that many command failures are caused by transient errors such as timeouts,\nso before recording a failure, the first response should usually be just to retry\nthe command a few times.)\n\nThe status of the attempted command calls is kept in a \"sliding window\", which\nis simply a buffer where the least recent item is dropped as each new\none is added. The buffer can be configured to have a fixed number of failures and/or a failure ratio (specified as a percentage), both based on a time window.\n\n![Sliding window of recent connection attempts](https://redis.io/docs/latest/images/failover/failover-sliding-window.svg)\n\nWhen the number of failures in the window exceeds a configured\nthreshold, the circuit breaker declares the server to be unhealthy and triggers\na failover."},{"id":"selecting-a-failover-target","title":"Selecting a failover target","role":"content","text":"Since you may have multiple Redis servers available to fail over to, the client\nlets you configure a list of endpoints to try, ordered by priority or\n\"weight\". When a failover is triggered, the client selects the highest-weighted\nendpoint that is still healthy and uses it for the temporary connection."},{"id":"health-checks","title":"Health checks","role":"content","text":"Given that the original endpoint had some geographical or other advantage\nover the failover target, you will generally want to fail back to it as soon\nas it recovers. In the meantime, another server might recover that is\nstill better than the current failover target, so it might be worth\nfailing back to that server even if it is not optimal.\n\nClients periodically run a \"health check\" on each server to see if it has recovered.\nSeveral health check strategies are implemented in all clients:\n\n-   **Ping**: This is the default strategy, which just sends a\n    [`PING`](https://redis.io/docs/latest/commands/ping) command and ensures that it gives the\n    expected response.\n-   **Lag aware** (Redis Software only): This strategy uses the\n    [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api) to check the\n    synchronization lag between a specific database and the others in the Active-Active\n    setup. If the lag is within a specified tolerance, the server is considered healthy.\n-   **Custom**: You can implement your own health check strategy to use information\n    or take action that is specific to your application.\n\nSee the documentation for your client library for more information on how to\nconfigure health checks.\n\nYou can also configure the client to run health checks on the current target\nserver during periods of inactivity, even if no failover has occurred. This can\nhelp to detect problems even if your app is not actively using the server."}],"examples":[]}
{"schema_version":2,"id":"develop/clients/go/amr","title":"Connect to Azure Managed Redis","url":"https://redis.io/docs/latest/develop/clients/go/amr/","summary":"Learn how to authenticate to an Azure Managed Redis (AMR) database","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-09T09:42:14+01:00","page_type":"content","content_hash":"8ff3272fd7182939297514de42c7f43378489f826336396d557f052f53d88035","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The [`go-redis-entraid`](https://github.com/redis/go-redis-entraid) package\nlets you authenticate your app to\n[Azure Managed Redis (AMR)](https://azure.microsoft.com/en-us/products/managed-redis)\nusing [Microsoft Entra ID](https://learn.microsoft.com/en-us/entra/identity/).\nYou can authenticate using a system-assigned or user-assigned\n[managed identity](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview)\nor a [service principal](https://learn.microsoft.com/en-us/entra/identity-platform/app-objects-and-service-principals),\nletting `go-redis-entraid` fetch and renew the authentication tokens for you automatically."},{"id":"install","title":"Install","role":"setup","text":"Install [`go-redis`](https://redis.io/docs/latest/develop/clients/go) if you\nhave not already done so. Note that `go-redis-entraid`\nrequires `go-redis` v9.9.0 or above, so you should upgrade if you\nare using an earlier version.\n\nInstall `go-redis-entraid` with the following command:\n\n[code example]"},{"id":"create-a-streamingcredentialsprovider-instance","title":"Create a `StreamingCredentialsProvider` instance","role":"content","text":"The `StreamingCredentialsProvider` interface defines methods\nto provide credentials for authentication. Use an object that\nimplements this interface to obtain the authentication credentials you\nneed when you connect to Redis. See the sections below to learn how\nto create the `StreamingCredentialsProvider` instances for AMR\nusing the factory functions that `go-redis-entraid` provides."},{"id":"streamingcredentialsprovider-for-a-service-principal","title":"`StreamingCredentialsProvider` for a service principal","role":"content","text":"Use the `NewConfidentialCredentialsProvider()` factory function to create a\n`StreamingCredentialsProvider` that authenticates to AMR using a\nservice principal (see the\n[Microsoft documentation](https://learn.microsoft.com/en-us/entra/identity-platform/app-objects-and-service-principals) to learn more about service principals).\n\nYou will need the following details of your service principal to make the connection:\n\n- Client ID\n- Client secret\n- Tenant ID\n\nUse an `AuthorityConfiguration` instance to pass the tenant ID.\nThis type has the following fields:\n\n-   `AuthorityType`: This should have one of the values\n    - `identity.AuthorityTypeDefault` (\"default\")\n    - `identity.AuthorityTypeMultiTenant` (\"multi-tenant\")\n    - `identity.AuthorityTypeCustom` (\"custom\")\n-   `TenantID`: Pass your tenant ID string here, or use \"common\" for\n    a multi-tentant application.\n-   `Authority`: Custom authority URL. This is only required if you\n    specified `AuthorityTypeCustom` in the `AuthorityType` field.\n\nThe example below shows how to import the required modules and call\n`NewConfidentialCredentialsProvider()`:\n\n[code example]"},{"id":"streamingcredentialsprovider-for-a-managed-identity","title":"`StreamingCredentialsProvider` for a managed identity","role":"content","text":"Use the `NewManagedIdentityCredentialsProvider()` function to create a\n`StreamingCredentialsProvider` that authenticates to AMR using a\nmanaged identity (see the\n[Microsoft documentation](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview) to learn more about managed identities).\n\nThe example below shows how to import the required modules and call\n`NewManagedIdentityCredentialsProvider()`.\nPass `identity.SystemAssignedIdentity` or `identity.UserAssignedIdentity`\nas the `ManagedIdentityType` parameter.\n\n[code example]"},{"id":"custom-configuration","title":"Custom configuration","role":"content","text":"The examples above use a default configuration but you can also provide a custom\nconfiguration using the `TokenManagerOptions` field of `CredentialsProviderOptions`:\n\n[code example]\n\nYou can then pass this configuration when you create the\n`StreamingCredentialsProvider`. The example below shows how to do this\nwith the `NewManagedIdentityCredentialsProvider()` method:\n\n[code example]\n\nThe fields of `TokenManagerOptions` are explained below:\n\n-   `ExpirationRefreshRatio`: A `float` value representing the fraction\n    of a token's lifetime that should elapse before attempting to\n    refresh it. For example, a value of 0.75 means that you want to\n    refresh the token after 75% of its lifetime has passed.\n-   `LowerRefreshBounds`: The minimum amount of the token's lifetime\n    (in milliseconds) remaining before attempting to refresh, regardless\n    of the `expirationRefreshRatio` value. Set this to zero if you want\n    the refresh time to depend only on `expirationRefreshRatio`.\n-   `RetryOptions`: This object specifies how to retry a token request\n    after failure. The available options are:\n    -   `MaxAttempts`: The maximum number of times to retry a token request\n        The default value is 3.\n    -   `InitialDelay`: The initial delay between retries in milliseconds. This\n        will be modified during successive attempts by the `BackoffMultiplier`\n        value (see below). The default is 1000ms.\n    -   `BackoffMultiplier`: The factor by which the `InitialDelay` is multiplied\n        between attempts, following an\n        [exponential backoff](https://en.wikipedia.org/wiki/Exponential_backoff)\n        strategy. The default multiplier is 2.0.\n    -   `IsRetryable`: A function that receives an `error` parameter and returns\n        a boolean `true` result if an attempt that failed with that error is\n        retryable and `false` otherwise. Use this to implement your own custom\n        logic to decide which errors should be retried."},{"id":"connect","title":"Connect","role":"content","text":"When you have created your `StreamingCredentialsProvider` instance, you are ready to\nconnect to AMR.\nThe example below shows how to pass the instance as a parameter to the standard\n`NewClusterClient()` connection method. It also illustrates how to use\n[`os.Getenv()`](https://pkg.go.dev/os#Getenv) to get the connection details\nfrom environment variables rather than include their values in the code.\n\n[code example]"},{"id":"more-information","title":"More information","role":"content","text":"See the [`go-redis-entraid`](https://github.com/redis/go-redis-entraid)\nGitHub repository for full source code and more examples and details."}],"examples":[{"id":"install-ex0","language":"bash","code":"go get github.com/redis/go-redis-entraid","section_id":"install"},{"id":"streamingcredentialsprovider-for-a-service-principal-ex0","language":"go","code":"import (\n    \"github.com/redis-developer/go-redis-entraid/entraid\"\n    \"github.com/redis-developer/go-redis-entraid/identity\"\n        ...\n)\n    .\n    .\nprovider, err := entraid.NewConfidentialCredentialsProvider(\n    entraid.ConfidentialCredentialsProviderOptions{\n\t    ConfidentialIdentityProviderOptions: identity.ConfidentialIdentityProviderOptions{\n\t\t    ClientID:        \"<your-azure-client-id>\",\n\t\t    ClientSecret:    \"<your-azure-client-secret>\",\n\t\t    CredentialsType: identity.ClientSecretCredentialType,\n\t\t    Authority: identity.AuthorityConfiguration{\n\t\t\t    AuthorityType: identity.AuthorityTypeDefault,\n\t\t\t    TenantID:      \"<your-azure-tenant-id>\",\n\t\t    },\n\t    },\n    },\n)","section_id":"streamingcredentialsprovider-for-a-service-principal"},{"id":"streamingcredentialsprovider-for-a-managed-identity-ex0","language":"go","code":"import (\n    \"github.com/redis-developer/go-redis-entraid/entraid\"\n    \"github.com/redis-developer/go-redis-entraid/identity\"\n        ...\n)\n    .\n    .\nprovider, err := entraid.NewManagedIdentityCredentialsProvider(\n\tentraid.ManagedIdentityCredentialsProviderOptions{\n\t\tManagedIdentityProviderOptions: identity.ManagedIdentityProviderOptions{\n\t\t\tManagedIdentityType:  identity.UserAssignedObjectID,\n\t\t\tUserAssignedObjectID: \"<your-user-assigned-client-id>\",\n\t\t},\n\t},\n)","section_id":"streamingcredentialsprovider-for-a-managed-identity"},{"id":"custom-configuration-ex0","language":"go","code":"options := entraid.CredentialsProviderOptions{\n    TokenManagerOptions: manager.TokenManagerOptions{\n        ExpirationRefreshRatio: 0.7,\n        LowerRefreshBounds: 10000,\n        RetryOptions: manager.RetryOptions{\n            MaxAttempts: 3,\n            InitialDelay: 1000 * time.Millisecond,\n            MaxDelay: 30000 * time.Millisecond,\n            BackoffMultiplier: 2.0,\n            IsRetryable: func(err error) bool {\n                return strings.Contains(err.Error(), \"network error\") ||\n                    strings.Contains(err.Error(), \"timeout\")\n            },\n        },\n    },\n}","section_id":"custom-configuration"},{"id":"custom-configuration-ex1","language":"go","code":"provider, err := entraid.NewManagedIdentityCredentialsProvider(\n\tentraid.ManagedIdentityCredentialsProviderOptions{\n\t    CredentialsProviderOptions: options,\n\t\tManagedIdentityProviderOptions: identity.ManagedIdentityProviderOptions{\n\t\t\tManagedIdentityType:  identity.UserAssignedObjectID,\n\t\t\tUserAssignedObjectID: \"<your-user-assigned-client-id>\",\n\t\t},\n\t},\n)","section_id":"custom-configuration"},{"id":"connect-ex0","language":"go","code":"package main\n\nimport (\n    \"context\"\n    \"fmt\"\n    \"log\"\n    \"os\"\n    \"strings\"\n\n    \"github.com/redis-developer/go-redis-entraid/entraid\"\n    \"github.com/redis/go-redis/v9\"\n)\n\nfunc main() {\n    // Get required environment variables\n    redisEndpoint := os.Getenv(\"REDIS_ENDPOINT\")\n    if redisEndpoint == \"\" {\n        log.Fatal(\n            \"REDIS_ENDPOINT environment variable is required\"\n        )\n    }\n\n    // Create credentials provider for system assigned identity\n    provider, err := entraid.NewManagedIdentityCredentialsProvider(\n        entraid.ManagedIdentityCredentialsProviderOptions{\n\t\t    ManagedIdentityProviderOptions: identity.ManagedIdentityProviderOptions{\n\t\t\t    ManagedIdentityType: identity.SystemAssignedIdentity,\n\t\t    },\n        }\n    )\n    if err != nil {\n        log.Fatalf(\"Failed to create credentials provider: %v\", err)\n    }\n\n    // Create Redis client\n    // Note: AMR databases have clustering enabled by default.\n    client := redis.NewClusterClient(&redis.Options{\n        Addr: redisEndpoint,\n        StreamingCredentialsProvider: provider,\n    })\n    defer client.Close()\n\n    // Test connection\n    ctx := context.Background()\n    if err := client.Ping(ctx).Err(); err != nil {\n        log.Fatalf(\"Failed to connect to Redis: %v\", err)\n    }\n    log.Println(\"Connected to Redis!\")\n}","section_id":"connect"}]}
{"schema_version":2,"id":"develop/clients/go/autopipeline","title":"Automatic pipelining","url":"https://redis.io/docs/latest/develop/clients/go/autopipeline/","summary":"Batch concurrent go-redis commands into pipelines automatically for high-throughput workloads.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-12T14:54:04+01:00","page_type":"content","content_hash":"353e6b0d4c60adb64111e5c9c2e7679eecd733e98bfef69ff4e6833d8cf1466d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Pipelining](https://redis.io/docs/latest/develop/using-commands/pipelining) sends a batch\nof commands to the server in a single communication, which avoids the network\nand processing overhead of sending each command separately. Normally you build\na pipeline by hand (see [Pipelines and transactions](https://redis.io/docs/latest/develop/clients/go/transpipe)),\nbut this means you must know in advance which commands you want to batch.\n\n*Automatic pipelining* removes that requirement. When many goroutines issue\ncommands concurrently, `go-redis` coalesces them into deep pipelines for you,\nwithout any pipeline code in your application. This is useful in high-throughput or\nhigh-concurrency scenarios. At low concurrency, a plain client is\nsimpler and just as fast, and a hand-written pipeline is generally faster than\nan auto-generated one.\n\nAutomatic pipelining requires\n`github.com/redis/go-redis/v9` v9.22.0 or later."},{"id":"blocking-and-asynchronous-pipelining","title":"Blocking and asynchronous pipelining","role":"content","text":"Automatic pipelining has two methods that share the same underlying engine:\n\n-   **Blocking** (`AutoPipeline()`) is a drop-in replacement for a normal\n    client. Each command call blocks until it executes and returns its own\n    value and error, exactly like a plain client, so existing code keeps\n    working unchanged. Under concurrency, the engine batches commands from all\n    goroutines into back-to-back pipelines behind the scenes. Per-goroutine\n    ordering is preserved.\n-   **Asynchronous** (`AsyncAutoPipeline()`) offers the highest throughput.\n    Command calls return immediately; reading a result with\n    `Val()`, `Result()`, or `Err()` blocks until the batch executes. Submit a\n    sequence of commands and then read the results afterwards to keep each\n    pipeline as deep as possible.\n\nBoth methods are available on `Client`, `ClusterClient`, and `Ring`."},{"id":"blocking-usage","title":"Blocking usage","role":"content","text":"Call `AutoPipeline()` to get an `AutoPipeliner`, then call command methods on it\njust as you would on a normal client. Each call blocks until it executes, but\nconcurrent callers' commands are batched together automatically:\n\n[code example]"},{"id":"asynchronous-usage","title":"Asynchronous usage","role":"content","text":"For maximum throughput, use asynchronous execution. Command calls return\nimmediately, so you can submit a sequence of commands and read their results\nafterwards:\n\n[code example]"},{"id":"configuration","title":"Configuration","role":"configuration","text":"`AutoPipeline()` and `AsyncAutoPipeline()` take no arguments. They use the\n`AutoPipelineOptions` set on the client's options, if any, and otherwise use\nreasonable default values. To pass options for a single\nautopipeliner, use `AutoPipelineWithOptions()` or\n`AsyncAutoPipelineWithOptions()` instead:\n\n[code example]\n\nAll four methods return `(*AutoPipeliner, error)`. The error is non-nil only\nwhen the options are invalid (for example, setting `MaxConcurrentBatches`\ngreater than one without also setting `Unordered`). Invalid options never cause a\npanic.\n\nThe configuration options are:\n\n| Field | Description |\n| :---- | :---------- |\n| `MaxBatchSize` | Target number of commands the engine coalesces into a single pipeline before flushing. This is a soft threshold rather than a hard cap, so a busy queue can flush a larger batch. Defaults to 200. |\n| `MaxBatchBytes` | Soft limit on the total size of arguments (in bytes) for a batch, so that large values flush as several bounded writes instead of one very large one. Defaults to 128 KiB in v9.23.0 or later. In v9.22.0, the default is 0, meaning no byte limit. |\n| `MaxFlushDelay` | Maximum time the engine waits to accumulate more commands before flushing a batch. Larger values build deeper pipelines at the cost of latency. Defaults to 0, which adds no accumulation wait. |\n| `AdaptiveDelay` | Scales `MaxFlushDelay` down as the queue fills, so a busy queue flushes sooner. Requires `MaxFlushDelay` to be greater than 0. Defaults to `false`. |\n| `MaxConcurrentBatches` | Number of batches that may execute at once. Defaults to 1, which gives a single ordered stream. Values greater than 1 require `Unordered` set to `true` because concurrent batches do not preserve a single ordered stream. |\n| `Unordered` | Allows commands to execute without preserving a single ordered stream, which enables higher concurrency. |\n| `NumShards` | Number of independent command queues, or shards, that the engine flushes separately. Defaults to 0, meaning a single shard, which funnels every caller into one queue so batches stay deep. Cluster clients default to several slot-routed shards instead. With `AsyncAutoPipeline()`, values greater than 1 require `Unordered` to be set to `true`. |\n| `MaxQueuedCommands` | Maximum number of commands the autopipeliner has accepted but not yet completed. A command submitted at the limit fails immediately with `ErrAutoPipelineQueueFull` instead of being queued, which bounds client memory when the server is slower than your goroutines. Because each command is rejected separately, a later command can succeed after an earlier one failed, so check the error of any command that a later one depends on. Defaults to 0, meaning no limit. Requires v9.23.0 or later. |\n\n`MaxBatchSize` is the one default that differs between the two methods. If you\nset no options at all, `AutoPipeline()` uses a built-in preset that targets 300\ncommands instead of 200. As soon as you supply `AutoPipelineOptions`, either on\nthe client or to `AutoPipelineWithOptions()`, that preset no longer applies, and\na `MaxBatchSize` you leave unset means 200.\n\nConnection and buffer tuning is not part of `AutoPipelineOptions`. Batches run\non the client's separate pipeline connection pool, which you size with the\nfields described in\n[Connection pooling](https://redis.io/docs/latest/develop/clients/go/produsage#connection-pooling).\n\nEach client holds at most two autopipeliners: one for the blocking method and\none for the asynchronous method. Each of them is a\n[*singleton*](https://en.wikipedia.org/wiki/Singleton_pattern) that the client\ncreates on first use and then shares with every later caller.\n\nOptions therefore only take effect on the call that creates the singleton. If a\nblocking autopipeliner already exists, a later `AutoPipelineWithOptions()` call\nreturns that same instance and ignores the options you passed, because\n`AutoPipeline()` and `AutoPipelineWithOptions()` share one singleton between\nthem. `Close()` stops the singleton for every caller and the next call creates a\nfresh one, so closing is also how you apply different options. Closing the\nclient is permanent: both methods then return `ErrClosed`."},{"id":"cluster-usage","title":"Cluster usage","role":"content","text":"`AutoPipeline()` and `AsyncAutoPipeline()` also work on `ClusterClient`.\nCommands are routed to the correct shard by key, so the client installs\nslot-based shard routing to keep each shard's batch on a single master node\n(rather than splitting every batch across all nodes at flush time). This is why\ncluster clients default to several shards instead of one. A single batch may\nspan many slots. Ordering is per key: same-key commands stay in order, while\nsub-pipelines on different nodes run concurrently.\n\nCommands that must reach every node or shard, such as\n[`FLUSHALL`](https://redis.io/docs/latest/commands/flushall), cannot be added to a pipeline, so\nthe cluster client rejects them with an error rather than let them spoil a\nbatch shared with other callers. Run them on the plain client instead."},{"id":"caveats-and-limitations","title":"Caveats and limitations","role":"content","text":"-   A command's context is not honored once it is queued, because batches\n    execute on the autopipeliner's own context. Use a plain client if you need\n    per-command deadlines.\n-   Blocking commands such as [`BLPOP`](https://redis.io/docs/latest/commands/blpop) and\n    [`WAIT`](https://redis.io/docs/latest/commands/wait) are never batched and run directly\n    on your context.\n-   The generic `Do`, `DoRaw`, and `DoRawWriteTo` methods run outside the\n    pipeline, on a normal connection, because an arbitrary command name can\n    carry connection state or block the connection. Prefer the typed methods\n    (`ap.Set()`, `ap.Get()`, and so on), which are always batched.\n-   On a dropped connection, a batch is retried as a whole, up to the client's\n    `MaxRetries`, so non-idempotent commands may execute twice. Set\n    `MaxRetries: -1`, or use a plain client, for commands that must never be\n    retransmitted."},{"id":"more-information","title":"More information","role":"content","text":"See the [`go-redis`](https://github.com/redis/go-redis) repository for the\n`example/autopipeline` usage tour and further API details."}],"examples":[{"id":"blocking-usage-ex0","language":"go","code":"rdb := redis.NewClient(&redis.Options{Addr: \"localhost:6379\"})\ndefer rdb.Close()\nctx := context.Background()\n\n// Blocking: a drop-in for a normal client, batched under the hood.\nap, err := rdb.AutoPipeline()\nif err != nil { // only returned for invalid AutoPipelineOptions\n    log.Fatal(err)\n}\ndefer ap.Close()\n\nvar wg sync.WaitGroup\nfor i := 0; i < 1000; i++ {\n    wg.Add(1)\n    go func(i int) {\n        defer wg.Done()\n        key := fmt.Sprintf(\"key:%d\", i)\n        if err := ap.Set(ctx, key, i, 0).Err(); err != nil { // blocks until executed\n            log.Printf(\"set %s: %v\", key, err)\n        }\n    }(i)\n}\nwg.Wait()","section_id":"blocking-usage"},{"id":"asynchronous-usage-ex0","language":"go","code":"ctx := context.Background()\n\nap, err := rdb.AsyncAutoPipeline() // ordered by default\nif err != nil {\n    log.Fatal(err)\n}\ndefer ap.Close()\n\ncmds := make([]*redis.StatusCmd, 0, 200)\nfor i := 0; i < 200; i++ {\n    // Returns immediately without executing.\n    cmds = append(cmds, ap.Set(ctx, fmt.Sprintf(\"key:%d\", i), i, 0))\n}\nfor _, cmd := range cmds {\n    if err := cmd.Err(); err != nil { // blocks until the batch executes\n        log.Printf(\"set: %v\", err)\n    }\n}","section_id":"asynchronous-usage"},{"id":"configuration-ex0","language":"go","code":"// On the client, used by both methods.\nrdb := redis.NewClient(&redis.Options{\n    Addr:                \"localhost:6379\",\n    AutoPipelineOptions: &redis.AutoPipelineOptions{MaxFlushDelay: 100 * time.Microsecond},\n})\n\n// Or for a single autopipeliner.\nap, err := rdb.AsyncAutoPipelineWithOptions(&redis.AutoPipelineOptions{\n    MaxConcurrentBatches: 80,\n    Unordered:            true,\n})","section_id":"configuration"}]}
{"schema_version":2,"id":"develop/clients/go/connect","title":"Connect to the server","url":"https://redis.io/docs/latest/develop/clients/go/connect/","summary":"Connect your Go application to a Redis database","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-12T14:48:04+01:00","page_type":"content","content_hash":"67a97225721b203ca555812fbcdd916db8be41c3a8c03a7e58dca5d119a9277f","sections":[{"id":"basic-connection","title":"Basic connection","role":"overview","text":"The following example shows the simplest way to connect to a Redis server:\n\n[code example]\n\nYou can also connect using a connection URL:\n\n[code example]\n\n> [!NOTE]\n> go-redis supports [connection URLs](https://redis.io/docs/latest/develop/clients/connection-urls#go-redis-go) through `redis.ParseURL()`.\n> A URL can set the host, port, credentials, database, and whether to use TLS,\n> so you can use one instead of passing those options separately.\n\nAfter connecting, you can test the connection by  storing and retrieving\na simple [string](https://redis.io/docs/latest/develop/data-types/strings):\n\n[code example]"},{"id":"connect-to-a-redis-cluster","title":"Connect to a Redis cluster","role":"content","text":"To connect to a Redis cluster, use `NewClusterClient()`. You can specify\none or more cluster endpoints with the `Addrs` option:\n\n[code example]"},{"id":"connect-to-redis-sentinel","title":"Connect to Redis Sentinel","role":"content","text":"To connect through [Redis Sentinel](https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel),\nuse `NewFailoverClient()`. Set `MasterName` to the name of the primary that the Sentinels\nmonitor, and list one or more Sentinels in `SentinelAddrs`. The client asks the Sentinels\nfor the current primary and switches to the new one after a failover.\n\n[code example]\n\nThe Sentinels and the data nodes have separate credentials. Set `SentinelPassword` for the\nSentinels, and `Username` and `Password` for the primary and replicas:\n\n[code example]\n\nTo send commands to a replica, set `ReplicaOnly`. The client sends every command to a\nrandom replica, including writes, which fail, so use this option only for a client that\nreads:\n\n[code example]"},{"id":"connect-to-your-production-redis-with-tls","title":"Connect to your production Redis with TLS","role":"content","text":"When you deploy your application, use TLS and follow the\n[Redis security](https://redis.io/docs/latest/operate/oss_and_stack/management/security) guidelines.\n\nEstablish a secure connection with your Redis database:\n\n[code example]"},{"id":"connect-using-smart-client-handoffs-sch","title":"Connect using Smart client handoffs (SCH)","role":"content","text":"*Smart client handoffs (SCH)* is a feature of Redis Cloud and\nRedis Software servers that lets them actively notify clients\nabout planned server maintenance shortly before it happens. This\nlets a client take action to avoid disruptions in service.\nSee [Smart client handoffs](https://redis.io/docs/latest/develop/clients/sch)\nfor more information about SCH.\n\n> [!NOTE]\n> Using SCH with go-redis requires v9.16.0 or later for\n> standard routing, and v9.18.0 or later for\n> [OSS Cluster API](https://redis.io/docs/latest/operate/rs/databases/configure/oss-cluster-api) connections.\n\nBy default, `go-redis` always attempts to connect via SCH but falls back to\na non-SCH connection if the server doesn't support it. However, you can configure SCH\nexplicitly by passing a `MaintNotificationsConfig` object during the connection,\nas shown in the following example:\n\n[code example]\n\n> [!NOTE]\n> SCH requires the [RESP3](https://redis.io/docs/latest/develop/reference/protocol-spec#resp-versions)\n> protocol, so you must set `Protocol:3` explicitly when you connect.\n\nThe `maintnotifications.Config` object accepts the following parameters:\n\n| Name | Description |\n|------ |------------- |\n| `Mode` | Whether or not to enable SCH. The options are `ModeDisabled`, `ModeEnabled` (require SCH and abort the connection if not supported), and `ModeAuto` (require SCH and fall back to a non-SCH connection if not supported). The default is `ModeAuto`.   |\n| `EndpointType` | The type of endpoint to use for the connection. The options are `EndpointTypeExternalIP`, `EndpointTypeInternalIP`, `EndpointTypeExternalFQDN`, `EndpointTypeInternalFQDN`, `EndpointTypeAuto` (auto-detect based on connection), and `EndpointTypeNone` (reconnect with current config). The default is `EndpointTypeAuto`. |\n| `HandoffTimeout` | The timeout to connect to the replacement node. The default is 15 seconds. |\n| `RelaxedTimeout` | The timeout to use for commands and connections while the server is performing maintenance. The default is 10 seconds. |\n| `PostHandoffRelaxedDuration` | The duration to continue using relaxed timeouts after a successful handoff (this provides extra resilience during cluster transitions). The default is 20 seconds. |\n| `MaxHandoffRetries` | The maximum number of times to retry connecting to the replacement node. The default is 3. |\n\n> [!NOTE]\n>  Redis Cloud supports relaxed timeouts *only* (and not pre-handoffs) for SCH if you are using\n> either [AWS PrivateLink](https://redis.io/docs/latest/operate/rc/security/aws-privatelink) or\n> [Google Cloud Private Service Connect](https://redis.io/docs/latest/operate/rc/security/private-service-connect)\n> (see [Smart client handoffs](https://redis.io/docs/latest/develop/clients/sch#redis-cloud) for more information).\n> To use relaxed timeouts with these services, you should set `EndpointType: maintnotifications.EndpointTypeNone`\n> when you connect. All other configurations have full support for both relaxed timeouts and pre-handoffs."},{"id":"connect-using-client-side-caching","title":"Connect using client-side caching","role":"content","text":"Client-side caching is a technique to reduce network traffic between\nthe client and server, resulting in better performance. See\n[Client-side caching introduction](https://redis.io/docs/latest/develop/clients/client-side-caching)\nfor more information about how client-side caching works and how to use it effectively.\n\n> [!NOTE]\n> Client-side caching is an experimental feature of go-redis and its\n> API may change in a minor release.\n>\n> Client-side caching requires go-redis v9.22.0 or later.\n> To maximize compatibility with all Redis products, client-side caching\n> is supported by Redis v7.4 or later.\n>\n> Client-side caching requires the [RESP3](https://redis.io/docs/latest/develop/reference/protocol-spec#resp-versions)\n> protocol, so you must set `Protocol: 3` explicitly when you connect. On a RESP2\n> connection, client-side caching silently does nothing. It is also limited to\n> standalone clients and to logical database 0; on any other database it is\n> disabled with a log warning.\n\nTo enable client-side caching, pass a `ClientSideCacheConfig` object when you\nconnect on a `Protocol: 3` client. Passing an empty `ClientSideCacheConfig{}`\nenables caching with the default settings:\n\n[code example]\n\nYou can see the cache working if you connect to the same Redis database\nwith [`redis-cli`](https://redis.io/docs/latest/develop/tools/cli) and run the\n[`MONITOR`](https://redis.io/docs/latest/commands/monitor) command. With caching enabled,\nthe server sees the first `Get(\"city\")` call but not the second, which the\nclient satisfies from the cache."},{"id":"how-invalidation-works","title":"How invalidation works","role":"content","text":"Redis tracks the keys each connection reads and sends an invalidation message\nwhen one of them changes. go-redis keeps a single cache shared by every\nconnection in the client's pool: each connection enables tracking on the\nserver, and go-redis applies the invalidation messages in the background.\n\nBecause invalidation is asynchronous, an entry is evicted shortly after the\ndata changes rather than at the instant of the write. Use `DrainInterval` to\ncontrol how often invalidations are applied, and `MaxStaleness` to put a hard\ntime limit on how long any entry can be served (see\n[Configuration options](#configuration-options) for more information)."},{"id":"configuration-options","title":"Configuration options","role":"configuration","text":"The `ClientSideCacheConfig` object accepts the following options to tune the\ncache:\n\n| Name | Description |\n| :-- | :-- |\n| `MaxEntries` | The maximum number of entries the cache can hold. Zero or negative means unlimited. If both `MaxEntries` and `MaxMemoryBytes` are unlimited, `MaxEntries` defaults to 10,000 so the cache cannot grow without bound. |\n| `MaxMemoryBytes` | An approximate memory limit for the cache. Zero or negative means unlimited. The cache is divided into 16 shards that each enforce a 16th of this limit, so set it to at least 16 times the size of your largest cached reply. |\n| `MaxStaleness` | The longest time an entry can be served after it was cached, regardless of invalidation. This is a safety net for a missed invalidation rather than the main way entries are kept fresh, so set it well above the time an invalidation takes to arrive. Zero, the default, disables it. |\n| `DrainInterval` | How often go-redis checks idle connections for invalidation messages and applies them. The default is 5ms and the minimum is 1ms. |"},{"id":"monitoring-the-cache","title":"Monitoring the cache","role":"content","text":"Use the `CSCStats()` method to read the cache statistics for a client:\n\n[code example]"},{"id":"supplying-your-own-cache","title":"Supplying your own cache","role":"content","text":"Set the `ClientSideCache` option to use your own cache instead of the built-in\none. It accepts any value that implements the\n[`Cache`](https://pkg.go.dev/github.com/redis/go-redis/v9#Cache) interface and\ntakes precedence over `ClientSideCacheConfig`. You can also use it to share a\nsingle cache between several clients, but only when those clients connect to the\nsame server and database.\n\nThe simplest approach is to wrap the built-in cache, which `NewLocalCache()`\nreturns, and override only the methods you want to change. The example below\ncounts lookups and passes everything else through:\n\n[code example]\n\n> [!NOTE]\n> Embed the concrete `*redis.LocalCache` type, as shown above, rather\n> than the `Cache` interface. Cache statistics come from an optional `Stats()`\n> method that the `Cache` interface doesn't declare, so a wrapper that embeds the\n> interface still compiles but makes `CSCStats()` report zeros.\n\nTo write a cache from scratch, you should implement all eight `Cache` methods: `Get()` for\nlookups, `Reserve()`, `FulfillOwned()`, and `Cancel()` to ensure that only one\ncaller fetches a missing key, and `DeleteByRedisKey()`, `DeleteByCacheKey()`,\n`EvictByConn()`, and `Flush()` to remove entries when invalidation arrives.\ngo-redis calls these methods from several goroutines at once, so your\nimplementation must be thread-safe, and must treat cache keys and Redis keys as\nopaque strings and preserve them exactly. It must also stop waiting for an\nin-progress reservation when the context is canceled. You should also implement\n`Stats() redis.CSCStats` if you want to use the `CSCStats()` function with your cache.\n\nIf you only want to change how the cache estimates the memory used by an entry, you\ndon't need a full cache implementation. Instead, set the `Sizer` field of\n`ClientSideCacheConfig` to a function that returns a size in bytes. The\nbuilt-in cache will then use it in place of its own approximation."}],"examples":[{"id":"basic-connection-ex0","language":"go","code":"import (\n\t\"context\"\n\t\"fmt\"\n\t\"github.com/redis/go-redis/v9\"\n)\n\nfunc main() {    \n    client := redis.NewClient(&redis.Options{\n        Addr:\t  \"localhost:6379\",\n        Password: \"\", // No password set\n        DB:\t\t  0,  // Use default DB\n        Protocol: 2,  // Connection protocol\n    })\n}","section_id":"basic-connection"},{"id":"basic-connection-ex1","language":"go","code":"opt, err := redis.ParseURL(\"redis://<user>:<pass>@localhost:6379/<db>\")\nif err != nil {\n\tpanic(err)\n}\n\nclient := redis.NewClient(opt)","section_id":"basic-connection"},{"id":"basic-connection-ex2","language":"go","code":"ctx := context.Background()\n\nerr := client.Set(ctx, \"foo\", \"bar\", 0).Err()\nif err != nil {\n    panic(err)\n}\n\nval, err := client.Get(ctx, \"foo\").Result()\nif err != nil {\n    panic(err)\n}\nfmt.Println(\"foo\", val)","section_id":"basic-connection"},{"id":"connect-to-a-redis-cluster-ex0","language":"go","code":"client := redis.NewClusterClient(&redis.ClusterOptions{\n    Addrs: []string{\":16379\", \":16380\", \":16381\", \":16382\", \":16383\", \":16384\"},\n\n    // To route commands by latency or randomly, enable one of the following.\n    //RouteByLatency: true,\n    //RouteRandomly: true,\n})","section_id":"connect-to-a-redis-cluster"},{"id":"connect-to-redis-sentinel-ex0","language":"go","code":"client := redis.NewFailoverClient(&redis.FailoverOptions{\n    MasterName:    \"mymaster\",\n    SentinelAddrs: []string{\"localhost:26379\", \"localhost:26380\", \"localhost:26381\"},\n})","section_id":"connect-to-redis-sentinel"},{"id":"connect-to-redis-sentinel-ex1","language":"go","code":"client := redis.NewFailoverClient(&redis.FailoverOptions{\n    MasterName:       \"mymaster\",\n    SentinelAddrs:    []string{\"localhost:26379\", \"localhost:26380\", \"localhost:26381\"},\n    SentinelPassword: \"sentinel-secret\", // use your Sentinel password\n    Username:         \"default\",         // use your Redis user\n    Password:         \"secret\",          // use your Redis password\n})","section_id":"connect-to-redis-sentinel"},{"id":"connect-to-redis-sentinel-ex2","language":"go","code":"replica := redis.NewFailoverClient(&redis.FailoverOptions{\n    MasterName:    \"mymaster\",\n    SentinelAddrs: []string{\"localhost:26379\", \"localhost:26380\", \"localhost:26381\"},\n    ReplicaOnly:   true,\n})","section_id":"connect-to-redis-sentinel"},{"id":"connect-to-your-production-redis-with-tls-ex0","language":"go","code":"// Load client cert\ncert, err := tls.LoadX509KeyPair(\"redis_user.crt\", \"redis_user_private.key\")\nif err != nil {\n    log.Fatal(err)\n}\n\n// Load CA cert\ncaCert, err := os.ReadFile(\"redis_ca.pem\")\nif err != nil {\n    log.Fatal(err)\n}\ncaCertPool := x509.NewCertPool()\ncaCertPool.AppendCertsFromPEM(caCert)\n\nclient := redis.NewClient(&redis.Options{\n    Addr:     \"my-redis.cloud.redislabs.com:6379\",\n    Username: \"default\", // use your Redis user. More info https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl/\n    Password: \"secret\", // use your Redis password\n    TLSConfig: &tls.Config{\n        MinVersion:   tls.VersionTLS12,\n        Certificates: []tls.Certificate{cert},\n        RootCAs:      caCertPool,\n    },\n})\n\n//send SET command\nerr = client.Set(ctx, \"foo\", \"bar\", 0).Err()\nif err != nil {\n    panic(err)\n}\n\n//send GET command and print the value\nval, err := client.Get(ctx, \"foo\").Result()\nif err != nil {\n    panic(err)\n}\nfmt.Println(\"foo\", val)","section_id":"connect-to-your-production-redis-with-tls"},{"id":"connect-using-smart-client-handoffs-sch-ex0","language":"go","code":"rdb := redis.NewClient(&redis.Options{\n    Addr:     \"localhost:6379\",\n    Protocol: 3, // RESP3 required\n    MaintNotificationsConfig: &maintnotifications.Config{\n            Mode:                       maintnotifications.ModeEnabled,\n            EndpointType:               maintnotifications.EndpointTypeExternalIP,\n            HandoffTimeout:             10 * time.Second,\n            RelaxedTimeout:             10 * time.Second,\n            PostHandoffRelaxedDuration: 10 * time.Second,\n            MaxHandoffRetries:          5,\n    },\n})","section_id":"connect-using-smart-client-handoffs-sch"},{"id":"connect-using-client-side-caching-ex0","language":"go","code":"import (\n    \"context\"\n    \"github.com/redis/go-redis/v9\"\n)\n\nfunc main() {\n    ctx := context.Background()\n\n    client := redis.NewClient(&redis.Options{\n        Addr:                  \"localhost:6379\",\n        Protocol:              3, // RESP3 required for client-side caching\n        ClientSideCacheConfig: &redis.ClientSideCacheConfig{},\n    })\n\n    client.Set(ctx, \"city\", \"New York\", 0)\n    client.Get(ctx, \"city\") // Retrieved from the server and cached\n    client.Get(ctx, \"city\") // Retrieved from the cache\n}","section_id":"connect-using-client-side-caching"},{"id":"monitoring-the-cache-ex0","language":"go","code":"stats := client.CSCStats()\nfmt.Printf(\"Cache hits: %d, misses: %d\\n\", stats.Hits, stats.Misses)\nfmt.Printf(\"Entries: %d, memory: %d bytes\\n\", stats.Entries, stats.MemoryUsageBytes)","section_id":"monitoring-the-cache"},{"id":"supplying-your-own-cache-ex0","language":"go","code":"type countingCache struct {\n    *redis.LocalCache\n    lookups atomic.Int64\n}\n\nfunc (c *countingCache) Get(ctx context.Context, cacheKey string) ([]byte, bool) {\n    c.lookups.Add(1)\n    return c.LocalCache.Get(ctx, cacheKey)\n}\n\ncache := &countingCache{\n    LocalCache: redis.NewLocalCache(redis.ClientSideCacheConfig{MaxEntries: 1000}),\n}\n\nclient := redis.NewClient(&redis.Options{\n    Addr:            \"localhost:6379\",\n    Protocol:        3,\n    ClientSideCache: cache,\n})","section_id":"supplying-your-own-cache"}]}
{"schema_version":2,"id":"develop/clients/go/error-handling","title":"Error handling","url":"https://redis.io/docs/latest/develop/clients/go/error-handling/","summary":"Learn how to handle errors when using go-redis","tags":[],"last_updated":"2026-09-09T09:42:14+01:00","page_type":"content","content_hash":"574d5878c8a77ca670bf42d2c07a3fe334ad17079bcd319f9b622d9add5c877c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"go-redis uses **explicit error returns** following Go's idiomatic error handling pattern. Code examples in the documentation often omit error handling for brevity,\nbut it is essential in production code.\nThis page explains how go-redis's error handling works and how to apply\nsome common error handling patterns. For an overview of error types and handling\nstrategies, see [Error handling](https://redis.io/docs/latest/develop/clients/error-handling).\nSee also [Production usage](https://redis.io/docs/latest/develop/clients/go/produsage)\nfor more information on connection management, timeouts, and other aspects of\napp reliability."},{"id":"error-handling-in-go","title":"Error handling in Go","role":"errors","text":"In Go, functions return errors as a second return value. You can check for errors explicitly with code like the following:\n\n[code example]\n\nCommon error types from go-redis include:\n\n| Error | When it occurs | Recoverable | Recommended action |\n|---|---|---|---|\n| `redis.Nil` | Key does not exist | ✅ | Return default value or fetch from source |\n| `context.DeadlineExceeded` | Operation timeout | ✅ | Retry with backoff |\n| `net.OpError` | Network error | ✅ | Retry with backoff or fall back to alternative |\n| `redis.ResponseError` | Redis error response | ❌ | Fix the command or arguments |\n\nSee [Categories of errors](https://redis.io/docs/latest/develop/clients/error-handling#categories-of-errors)\nfor a more detailed discussion of these errors and their causes."},{"id":"applying-error-handling-patterns","title":"Applying error handling patterns","role":"content","text":"The [Error handling](https://redis.io/docs/latest/develop/clients/error-handling) overview\ndescribes four main patterns. The sections below show how to implement them in\ngo-redis:"},{"id":"pattern-1-fail-fast","title":"Pattern 1: Fail fast","role":"content","text":"Return the error immediately if it represents an unrecoverable situation (see\n[Pattern 1: Fail fast](https://redis.io/docs/latest/develop/clients/error-handling#pattern-1-fail-fast)\nfor a full description):\n\n[code example]"},{"id":"pattern-2-graceful-degradation","title":"Pattern 2: Graceful degradation","role":"content","text":"Check for specific errors and fall back to an alternative (see\n[Pattern 2: Graceful degradation](https://redis.io/docs/latest/develop/clients/error-handling#pattern-2-graceful-degradation)\nfor a full description):\n\n[code example]"},{"id":"pattern-3-retry-with-backoff","title":"Pattern 3: Retry with backoff","role":"content","text":"Retry on temporary errors such as timeouts (see\n[Pattern 3: Retry with backoff](https://redis.io/docs/latest/develop/clients/error-handling#pattern-3-retry-with-backoff)\nfor a full description). go-redis has built-in retry logic with\nconfigurable timing and backoffs. See [Retries](https://redis.io/docs/latest/develop/clients/go/produsage#retries) for more information. Note also that\nyou can configure timeouts (which are one of the most common causes of\ntemporary errors) for connections and commands. See [Timeouts](https://redis.io/docs/latest/develop/clients/go/produsage#timeouts) for more information."},{"id":"pattern-4-log-and-continue","title":"Pattern 4: Log and continue","role":"content","text":"Log non-critical errors and continue (see\n[Pattern 4: Log and continue](https://redis.io/docs/latest/develop/clients/error-handling#pattern-4-log-and-continue)\nfor a full description):\n\n[code example]\n\nNote that go-redis also supports [OpenTelemetry](https://opentelemetry.io/)\ninstrumentation to monitor performance and trace the execution of Redis commands. See [Observability](https://redis.io/docs/latest/develop/clients/go#observability) for more information."},{"id":"see-also","title":"See also","role":"related","text":"- [Error handling](https://redis.io/docs/latest/develop/clients/error-handling)\n- [Production usage](https://redis.io/docs/latest/develop/clients/go/produsage)\n- [Observability](https://redis.io/docs/latest/develop/clients/go#observability)"}],"examples":[{"id":"error-handling-in-go-ex0","language":"go","code":"result, err := rdb.Get(ctx, key).Result()\nif err != nil {\n    // Handle the error\n}","section_id":"error-handling-in-go"},{"id":"pattern-1-fail-fast-ex0","language":"go","code":"result, err := rdb.Get(ctx, key).Result()\nif err != nil {\n    // This indicates a problem that should be fixed\n    return err\n}","section_id":"pattern-1-fail-fast"},{"id":"pattern-2-graceful-degradation-ex0","language":"go","code":"result, err := rdb.Get(ctx, key).Result()\nif err != nil {\n    if err == redis.Nil {\n        // Key doesn't exist, try database\n        return database.Get(ctx, key)\n    }\n    if _, ok := err.(net.Error); ok {\n        // Network error, use fallback\n        logger.Warn(\"Cache unavailable, using database\")\n        return database.Get(ctx, key)\n    }\n    return err\n}\nreturn result, nil","section_id":"pattern-2-graceful-degradation"},{"id":"pattern-4-log-and-continue-ex0","language":"go","code":"err := rdb.SetEx(ctx, key, value, 3600*time.Second).Err()\nif err != nil {\n    if _, ok := err.(net.Error); ok {\n        logger.Warnf(\"Failed to cache %s, continuing without cache\", key)\n        // Application continues normally\n    } else {\n        return err\n    }\n}","section_id":"pattern-4-log-and-continue"}]}
{"schema_version":2,"id":"develop/clients/go","title":"go-redis guide (Go)","url":"https://redis.io/docs/latest/develop/clients/go/","summary":"Connect your Go application to a Redis database","aliases":["/develop/connect/clients/go","/connect/clients/go/","/clients/go/","/develop/clients/go-redis"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-12T14:48:04+01:00","children":[{"id":"develop/clients/go/connect","summary":"Connect your Go application to a Redis database","title":"Connect to the server","url":"https://redis.io/docs/latest/develop/clients/go/connect/"},{"id":"develop/clients/go/amr","summary":"Learn how to authenticate to an Azure Managed Redis (AMR) database","title":"Connect to Azure Managed Redis","url":"https://redis.io/docs/latest/develop/clients/go/amr/"},{"id":"develop/clients/go/queryjson","summary":"Learn how to use Redis Search with JSON and hash documents.","title":"Index and query documents","url":"https://redis.io/docs/latest/develop/clients/go/queryjson/"},{"id":"develop/clients/go/vecsearch","summary":"Learn how to index and query vector embeddings with Redis","title":"Index and query vectors","url":"https://redis.io/docs/latest/develop/clients/go/vecsearch/"},{"id":"develop/clients/go/vecsets","summary":"Index and query embeddings with Redis vector sets","title":"Vector set embeddings","url":"https://redis.io/docs/latest/develop/clients/go/vecsets/"},{"id":"develop/clients/go/scan-buffer","summary":"Scan go-redis command results into Go values and use buffers for large string payloads.","title":"Handle command results efficiently","url":"https://redis.io/docs/latest/develop/clients/go/scan-buffer/"},{"id":"develop/clients/go/transpipe","summary":"Learn how to use Redis pipelines and transactions","title":"Pipelines and transactions","url":"https://redis.io/docs/latest/develop/clients/go/transpipe/"},{"id":"develop/clients/go/autopipeline","summary":"Batch concurrent go-redis commands into pipelines automatically for high-throughput workloads.","title":"Automatic pipelining","url":"https://redis.io/docs/latest/develop/clients/go/autopipeline/"},{"id":"develop/clients/go/prob","summary":"Learn how to use approximate calculations with Redis.","title":"Probabilistic data types","url":"https://redis.io/docs/latest/develop/clients/go/prob/"},{"id":"develop/clients/go/error-handling","summary":"Learn how to handle errors when using go-redis","title":"Error handling","url":"https://redis.io/docs/latest/develop/clients/go/error-handling/"},{"id":"develop/clients/go/produsage","summary":"Get your `go-redis` app ready for production","title":"Production usage","url":"https://redis.io/docs/latest/develop/clients/go/produsage/"},{"id":"develop/clients/go/observability","summary":"Monitor your client's activity for optimization and debugging.","title":"Observability","url":"https://redis.io/docs/latest/develop/clients/go/observability/"}],"page_type":"content","content_hash":"9432ef8e79bf744f827759b5b8a087eb9a0a78ea1f859f49e12061b9ae5eed09","sections":[{"id":"install","title":"Install","role":"setup","text":"`go-redis` supports the last two Go versions. You can only use it from within\na Go module, so you must initialize a Go module before you start, or add your code to\nan existing module:\n\n[code example]\n\nUse the `go get` command to install `go-redis/v9`:\n\n[code example]"},{"id":"connect","title":"Connect","role":"content","text":"The following example shows the simplest way to connect to a Redis server.\nFirst, import the `go-redis` package:\n\nFoundational: Import the go-redis package\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nThen connect to localhost on port 6379 and add a\n[context](https://golang.org/pkg/context/) object:\n\nFoundational: Connect to a Redis server and establish a client connection\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nYou can also connect using a connection URL:\n\n[code example]\n\n> [!NOTE]\n> go-redis supports [connection URLs](https://redis.io/docs/latest/develop/clients/connection-urls#go-redis-go) through `redis.ParseURL()`.\n> A URL can set the host, port, credentials, database, and whether to use TLS,\n> so you can use one instead of passing those options separately.\n\nAfter connecting, you can test the connection by  storing and retrieving\na simple [string](https://redis.io/docs/latest/develop/data-types/strings):\n\nFoundational: Set and retrieve string values using SET and GET commands\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nYou can also easily store and retrieve a [hash](https://redis.io/docs/latest/develop/data-types/hashes):\n\nFoundational: Store and retrieve hash data structures using HSET and HGET commands\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\n Use\n [struct tags](https://stackoverflow.com/questions/10858787/what-are-the-uses-for-struct-tags-in-go)\n of the form `redis:\"<field-name>\"` with the `Scan()` method to parse fields from\n a hash directly into corresponding struct fields:\n\nFoundational: Parse hash data structures into struct fields using Scan()\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nClose the connection when you're done using a `Close()` call:\n\nFoundational: Close the Redis client connection\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nIn the common case where you want to close the connection at the end of the\nfunction where you opened it, you may find it convenient to use a `defer`\nstatement right after connecting:\n\n[code example]"},{"id":"more-information","title":"More information","role":"content","text":"See the other pages in this section for more information and examples.\nFurther examples are available at the [`go-redis`](https://redis.uptrace.dev/guide/) website\nand the [GitHub repository](https://github.com/redis/go-redis)."}],"examples":[{"id":"install-ex0","language":"plaintext","code":"go mod init github.com/my/repo","section_id":"install"},{"id":"install-ex1","language":"plaintext","code":"go get github.com/redis/go-redis/v9","section_id":"install"},{"id":"connect-ex0","language":"go","code":"import (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/redis/go-redis/v9\"\n)","section_id":"connect"},{"id":"connect-ex1","language":"go","code":"rdb := redis.NewClient(&redis.Options{\n\t\tAddr:     \"localhost:6379\",\n\t\tPassword: \"\", // no password\n\t\tDB:       0,  // use default DB\n\t\tProtocol: 2,\n\t})\n\n\tctx := context.Background()","section_id":"connect"},{"id":"connect-ex2","language":"go","code":"opt, err := redis.ParseURL(\"redis://<user>:<pass>@localhost:6379/<db>\")\nif err != nil {\n\tpanic(err)\n}\n\nclient := redis.NewClient(opt)","section_id":"connect"},{"id":"connect-ex3","language":"go","code":"err := rdb.Set(ctx, \"foo\", \"bar\", 0).Err()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tval, err := rdb.Get(ctx, \"foo\").Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tfmt.Println(\"foo\", val) // >>> foo bar","section_id":"connect"},{"id":"connect-ex4","language":"go","code":"hashFields := []string{\n\t\t\"model\", \"Deimos\",\n\t\t\"brand\", \"Ergonom\",\n\t\t\"type\", \"Enduro bikes\",\n\t\t\"price\", \"4972\",\n\t}\n\n\tres1, err := rdb.HSet(ctx, \"bike:1\", hashFields).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> 4\n\n\tres2, err := rdb.HGet(ctx, \"bike:1\", \"model\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> Deimos\n\n\tres3, err := rdb.HGet(ctx, \"bike:1\", \"price\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3) // >>> 4972\n\n\tres4, err := rdb.HGetAll(ctx, \"bike:1\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4)\n\t// >>> map[brand:Ergonom model:Deimos price:4972 type:Enduro bikes]","section_id":"connect"},{"id":"connect-ex5","language":"go","code":"type BikeInfo struct {\n\t\tModel string `redis:\"model\"`\n\t\tBrand string `redis:\"brand\"`\n\t\tType  string `redis:\"type\"`\n\t\tPrice int    `redis:\"price\"`\n\t}\n\n\tvar res4a BikeInfo\n\terr = rdb.HGetAll(ctx, \"bike:1\").Scan(&res4a)\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Printf(\"Model: %v, Brand: %v, Type: %v, Price: $%v\\n\",\n\t\tres4a.Model, res4a.Brand, res4a.Type, res4a.Price)\n\t// >>> Model: Deimos, Brand: Ergonom, Type: Enduro bikes, Price: $4972","section_id":"connect"},{"id":"connect-ex6","language":"go","code":"rdb.Close()","section_id":"connect"},{"id":"connect-ex7","language":"go","code":"func main() {    \n    rdb := redis.NewClient(&redis.Options{\n        ...\n    })\n    defer rdb.Close()\n    ...\n}","section_id":"connect"}]}
{"schema_version":2,"id":"develop/clients/go/observability","title":"Observability","url":"https://redis.io/docs/latest/develop/clients/go/observability/","summary":"Monitor your client's activity for optimization and debugging.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-09T09:42:14+01:00","page_type":"content","content_hash":"671298ead7af78e2530dc0fde8e3833d64092aba116d8a1c1a4e0b20c98513d1","sections":[{"id":"installation","title":"Installation","role":"setup","text":"Install OTel support for `go-redis` with the following commands:\n\n[code example]"},{"id":"import","title":"Import","role":"content","text":"Start by importing the required OTel and Redis modules:\n\nFoundational: Import required libraries for Redis observability, OpenTelemetry metrics, and Redis operations\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]"},{"id":"configure-the-meter-provider","title":"Configure the meter provider","role":"configuration","text":"Otel uses a [Meter provider](https://opentelemetry.io/docs/concepts/signals/metrics/#meter-provider)\nto create the objects that collect the metric information. The example below\nconfigures a meter provider to export metrics to a local Otel collector\nevery 10 seconds, but see the [OpenTelemetry Go docs](https://opentelemetry.io/docs/languages/go/)\nto learn more about other export options.\n\nFoundational: Configure a meter provider to export metrics to a local Grafana instance every 10 seconds\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]"},{"id":"configure-the-redis-client","title":"Configure the Redis client","role":"configuration","text":"You configure the client library for OTel only once per application. This will\nenable OTel for all Redis connections you create. The example below shows the\noptions you can pass to the observability instance via the `redisotel.Config` object\nduring initialization.\n\nFoundational: Configure Redis observability with a list of metric groups and optional command filtering and privacy controls\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nThe available option methods for `redisotel.Config` are described in the table below.\n\n| Method | Type | Description |\n| --- | --- | --- |\n| `WithEnabled` | `bool` | Enable or disable OTel instrumentation. Default is `false`, so you must enable it explicitly. |\n| `WithMetricGroups` | `MetricGroupFlag` | Bitmap of metric groups to enable. By default, all metrics are enabled (equivalent to `MetricGroupAll`). See [Redis metric groups](https://redis.io/docs/latest/develop/clients/observability#redis-metric-groups) for a list of available groups. |\n| `WithIncludeCommands` | `[]string` | List of Redis commands to track. If set, only these commands will be tracked. Note that you should use the Redis command name rather than the Go method name (for example `LPOP` rather than `LPopCount`). |\n| `WithExcludeCommands` | `[]string` | List of Redis commands to exclude from tracking. If set, all commands except these will be tracked. Note that you should use the Redis command name rather than the Go method name (for example `LPOP` rather than `LPopCount`). |\n| `WithHidePubSubChannelNames` | `bool` | If true, channel names in pub/sub metrics will be hidden. |\n| `WithHideStreamNames` | `bool` | If true, stream names in streaming metrics will be hidden. |\n| `WithHistogramBuckets` | `[]float64` | List of bucket boundaries for the histogram metrics. See [Custom histogram buckets](#custom-histogram-buckets) below for more information. |"},{"id":"custom-histogram-buckets","title":"Custom histogram buckets","role":"content","text":"For the histogram metrics, a reasonable default set of buckets is defined, but\nyou can customize the bucket boundaries to suit your needs (the buckets are the\nranges of data values counted for each bar of the histogram). Pass an increasing\nlist of float values to the `WithHistogramBuckets` option when you create the `redisotel.Config`\nobject. The first and last values in the list are the lower and upper bounds of the\nhistogram, respectively, and the values in between define the bucket boundaries."},{"id":"use-redis","title":"Use Redis","role":"content","text":"Once you have configured the client, all Redis connections you create will be\nautomatically instrumented and the collected metrics will be exported to your\nconfigured destination.\n\nThe example below shows the simplest Redis connection and a few commands,\nbut see the\n[observability demonstration repository](https://github.com/redis-developer/redis-client-observability)\nfor an example that calls a variety of commands in a more realistic way.\n\nFoundational: Use Redis with automatic instrumentation\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]"},{"id":"shutdown","title":"Shutdown","role":"content","text":"When your application exits, you should close the Redis connection and then\nyou should call the `Shutdown()` method on the\n`ObservabilityInstance` and `MeterProvider` instances to ensure that all pending\nmetrics are exported. You may find it useful to put the shutdown code in a\n`defer` statement to ensure that it is called even if the main function\nexits early due to an error.\n\nFoundational: Shutdown Redis observability\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]"}],"examples":[{"id":"installation-ex0","language":"bash","code":"go get github.com/redis/go-redis/extra/redisotel-native/v9\ngo get go.opentelemetry.io/otel","section_id":"installation"},{"id":"import-ex0","language":"go","code":"import (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/redis/go-redis/extra/redisotel-native/v9\"\n\t\"github.com/redis/go-redis/v9\"\n\t\"go.opentelemetry.io/otel\"\n\t\"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc\"\n\t\"go.opentelemetry.io/otel/sdk/metric\"\n\t\"go.opentelemetry.io/otel/sdk/resource\"\n\tsemconv \"go.opentelemetry.io/otel/semconv/v1.24.0\"\n)","section_id":"import"},{"id":"configure-the-meter-provider-ex0","language":"go","code":"ctx := context.Background()\n\n\t// Create OTLP exporter that sends metrics to the collector\n\t// Default endpoint is localhost:4317 (gRPC)\n\texporter, err := otlpmetricgrpc.New(ctx,\n\t\totlpmetricgrpc.WithInsecure(), // Use insecure for local development\n\t\t// For production, configure TLS and authentication:\n\t\t// otlpmetricgrpc.WithEndpoint(\"your-collector:4317\"),\n\t\t// otlpmetricgrpc.WithTLSCredentials(...),\n\t)\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Create resource with service name\n\tres, err := resource.New(ctx,\n\t\tresource.WithAttributes(\n\t\t\tsemconv.ServiceName(\n\t\t\t\tfmt.Sprintf(\"go-redis-examples:%d\", time.Now().Unix()),\n\t\t\t),\n\t\t),\n\t)\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Create meter provider with periodic reader\n\t// Metrics are exported every 10 seconds\n\tmeterProvider := metric.NewMeterProvider(\n\t\tmetric.WithResource(res),\n\t\tmetric.WithReader(\n\t\t\tmetric.NewPeriodicReader(exporter,\n\t\t\t\tmetric.WithInterval(10*time.Second),\n\t\t\t),\n\t\t),\n\t)\n\n\t// Set the global meter provider\n\totel.SetMeterProvider(meterProvider)","section_id":"configure-the-meter-provider"},{"id":"configure-the-redis-client-ex0","language":"go","code":"// Initialize OTel instrumentation BEFORE creating Redis clients\n\totelInstance := redisotel.GetObservabilityInstance()\n\tconfig := redisotel.NewConfig().\n\t\t// You must enable OTel explicitly\n\t\tWithEnabled(true).\n\t\t// Enable the metric groups you want to collect. Use bitwise OR\n\t\t// to combine multiple groups. The default is `MetricGroupAll`\n\t\t// which includes all groups.\n\t\tWithMetricGroups(redisotel.MetricGroupFlagCommand |\n\t\t\tredisotel.MetricGroupFlagConnectionBasic |\n\t\t\tredisotel.MetricGroupFlagResiliency |\n\t\t\tredisotel.MetricGroupFlagConnectionAdvanced).\n\t\t// Filter which commands to track\n\t\tWithIncludeCommands([]string{\"GET\", \"SET\"}).\n\t\tWithExcludeCommands([]string{\"DEBUG\", \"SLOWLOG\"}).\n\t\t// Privacy controls\n\t\tWithHidePubSubChannelNames(true).\n\t\tWithHideStreamNames(true).\n\t\t// Custom histogram buckets\n\t\tWithHistogramBuckets([]float64{\n\t\t\t0.0001, // 0.1ms\n\t\t\t0.0005, // 0.5ms\n\t\t\t0.001,  // 1ms\n\t\t\t0.005,  // 5ms\n\t\t\t0.01,   // 10ms\n\t\t\t0.05,   // 50ms\n\t\t\t0.1,    // 100ms\n\t\t\t0.5,    // 500ms\n\t\t\t1.0,    // 1s\n\t\t\t5.0,    // 5s\n\t\t\t10.0,   // 10s\n\t\t})\n\n\tif err := otelInstance.Init(config); err != nil {\n\t\tpanic(err)\n\t}","section_id":"configure-the-redis-client"},{"id":"use-redis-ex0","language":"go","code":"rdb := redis.NewClient(&redis.Options{\n\t\tAddr:     \"localhost:6379\",\n\t\tPassword: \"\", // no password set\n\t\tDB:       0,  // use default DB\n\t})\n\n\trdb.Set(ctx, \"key\", \"value\", 0)\n\tv, err := rdb.Get(ctx, \"key\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(v)","section_id":"use-redis"},{"id":"shutdown-ex0","language":"go","code":"rdb.Close()\n\totelInstance.Shutdown()\n\tmeterProvider.Shutdown(context.Background())","section_id":"shutdown"}]}
{"schema_version":2,"id":"develop/clients/go/prob","title":"Probabilistic data types","url":"https://redis.io/docs/latest/develop/clients/go/prob/","summary":"Learn how to use approximate calculations with Redis.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-09T09:42:14+01:00","page_type":"content","content_hash":"514ed78cb3bbca7f93d9df5b8fa8eaaa8f73773ba8feb862b1b77eb17e9e47be","sections":[{"id":"set-operations","title":"Set operations","role":"content","text":"Redis supports the following approximate set operations:\n\n-   [Membership](#set-membership): The\n    [Bloom filter](https://redis.io/docs/latest/develop/data-types/probabilistic/bloom-filter) and\n    [Cuckoo filter](https://redis.io/docs/latest/develop/data-types/probabilistic/cuckoo-filter)\n    data types let you track whether or not a given item is a member of a set.\n-   [Cardinality](#set-cardinality): The\n    [HyperLogLog](https://redis.io/docs/latest/develop/data-types/probabilistic/hyperloglogs)\n    data type gives you an approximate value for the number of items in a set, also\n    known as the *cardinality* of the set.\n\nThe sections below describe these operations in more detail."},{"id":"set-membership","title":"Set membership","role":"content","text":"[Bloom filter](https://redis.io/docs/latest/develop/data-types/probabilistic/bloom-filter) and\n[Cuckoo filter](https://redis.io/docs/latest/develop/data-types/probabilistic/cuckoo-filter)\nobjects provide a set membership operation that lets you track whether or not a\nparticular item has been added to a set. These two types provide different\ntrade-offs for memory usage and speed, so you can select the best one for your\nuse case. Note that for both types, there is an asymmetry between presence and\nabsence of items in the set. If an item is reported as absent, then it is definitely\nabsent, but if it is reported as present, then there is a small chance it may really be\nabsent.\n\nInstead of storing strings directly, like a [set](https://redis.io/docs/latest/develop/data-types/sets),\na Bloom filter records the presence or absence of the\n[hash value](https://en.wikipedia.org/wiki/Hash_function) of a string.\nThis gives a very compact representation of the\nset's membership with a fixed memory size, regardless of how many items you\nadd. The following example adds some names to a Bloom filter representing\na list of users and checks for the presence or absence of users in the list.\n\nSet membership: Use Bloom filter to efficiently track item presence with minimal memory overhead\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nA Cuckoo filter has similar features to a Bloom filter, but also supports\na deletion operation to remove hashes from a set, as shown in the example below.\n\nSet membership with deletion: Use Cuckoo filter for membership testing with support for item removal\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nWhich of these two data types you choose depends on your use case.\nBloom filters are generally faster than Cuckoo filters when adding new items,\nand also have better memory usage. Cuckoo filters are generally faster\nat checking membership and also support the delete operation. See the\n[Bloom filter](https://redis.io/docs/latest/develop/data-types/probabilistic/bloom-filter) and\n[Cuckoo filter](https://redis.io/docs/latest/develop/data-types/probabilistic/cuckoo-filter)\nreference pages for more information and comparison between the two types."},{"id":"set-cardinality","title":"Set cardinality","role":"content","text":"A [HyperLogLog](https://redis.io/docs/latest/develop/data-types/probabilistic/hyperloglogs)\nobject calculates the cardinality of a set. As you add\nitems, the HyperLogLog tracks the number of distinct set members but\ndoesn't let you retrieve them or query which items have been added.\nYou can also merge two or more HyperLogLogs to find the cardinality of the\n[union](https://en.wikipedia.org/wiki/Union_(set_theory)) of the sets they\nrepresent.\n\nSet cardinality: Estimate distinct item count using HyperLogLog with minimal memory usage\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nThe main benefit that HyperLogLogs offer is their very low\nmemory usage. They can count up to 2^64 items with less than\n1% standard error using a maximum 12KB of memory. This makes\nthem very useful for counting things like the total of distinct\nIP addresses that access a website or the total of distinct\nbank card numbers that make purchases within a day."},{"id":"statistics","title":"Statistics","role":"content","text":"Redis supports several approximate statistical calculations\non numeric data sets:\n\n-   [Frequency](#frequency): The\n    [Count-min sketch](https://redis.io/docs/latest/develop/data-types/probabilistic/count-min-sketch)\n    data type lets you find the approximate frequency of a labeled item in a data stream.\n-   [Quantiles](#quantiles): The\n    [t-digest](https://redis.io/docs/latest/develop/data-types/probabilistic/t-digest)\n    data type estimates the quantile of a query value in a data stream.\n-   [Ranking](#ranking): The\n    [Top-K](https://redis.io/docs/latest/develop/data-types/probabilistic/top-k) data type\n    estimates the ranking of labeled items by frequency in a data stream.\n\nThe sections below describe these operations in more detail."},{"id":"frequency","title":"Frequency","role":"content","text":"A [Count-min sketch](https://redis.io/docs/latest/develop/data-types/probabilistic/count-min-sketch)\n(CMS) object keeps count of a set of related items represented by\nstring labels. The count is approximate, but you can specify\nhow close you want to keep the count to the true value (as a fraction)\nand the acceptable probability of failing to keep it in this\ndesired range. For example, you can request that the count should\nstay within 0.1% of the true value and have a 0.05% probability\nof going outside this limit. The example below shows how to create\na Count-min sketch object, add data to it, and then query it.\n\nFrequency estimation: Track approximate item frequencies using Count-min sketch with configurable accuracy\n\n**Difficulty:** Intermediate\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nThe advantage of using a CMS over keeping an exact count with a\n[sorted set](https://redis.io/docs/latest/develop/data-types/sorted-sets)\nis that a CMS has very low and fixed memory usage, even for\nlarge numbers of items. Use CMS objects to keep daily counts of\nitems sold, accesses to individual web pages on your site, and\nother similar statistics."},{"id":"quantiles","title":"Quantiles","role":"content","text":"A [quantile](https://en.wikipedia.org/wiki/Quantile) is the value\nbelow which a certain fraction of samples lie. For example, with\na set of measurements of people's heights, the quantile of 0.75 is\nthe value of height below which 75% of all people's heights lie.\n[Percentiles](https://en.wikipedia.org/wiki/Percentile) are equivalent\nto quantiles, except that the fraction is expressed as a percentage.\n\nA [t-digest](https://redis.io/docs/latest/develop/data-types/probabilistic/t-digest)\nobject can estimate quantiles from a set of values added to it\nwithout having to store each value in the set explicitly. This can\nsave a lot of memory when you have a large number of samples.\n\nThe example below shows how to add data samples to a t-digest\nobject and obtain some basic statistics, such as the minimum and\nmaximum values, the quantile of 0.75, and the\n[cumulative distribution function](https://en.wikipedia.org/wiki/Cumulative_distribution_function)\n(CDF), which is effectively the inverse of the quantile function. It also\nshows how to merge two or more t-digest objects to query the combined\ndata set.\n\nQuantile estimation: Calculate percentiles and quantiles from data streams using t-digest\n\n**Difficulty:** Intermediate\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nA t-digest object also supports several other related commands, such\nas querying by rank. See the\n[t-digest](https://redis.io/docs/latest/develop/data-types/probabilistic/t-digest)\nreference for more information."},{"id":"ranking","title":"Ranking","role":"content","text":"A [Top-K](https://redis.io/docs/latest/develop/data-types/probabilistic/top-k)\nobject estimates the rankings of different labeled items in a data\nstream according to frequency. For example, you could use this to\ntrack the top ten most frequently-accessed pages on a website, or the\ntop five most popular items sold.\n\nThe example below adds several different items to a Top-K object\nthat tracks the top three items (this is the second parameter to\nthe `topk().reserve()` method). It also shows how to list the\ntop *k* items and query whether or not a given item is in the\nlist.\n\nRanking by frequency: Track top k most frequent items in a data stream using Top-K\n\n**Difficulty:** Intermediate\n\n**Available in:** Go\n\n##### Go\n\n[code example]"}],"examples":[{"id":"set-membership-ex0","language":"go","code":"res1, err := rdb.BFMAdd(\n\t\tctx,\n\t\t\"recorded_users\",\n\t\t\"andy\", \"cameron\", \"david\", \"michelle\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> [true true true true]\n\n\tres2, err := rdb.BFExists(ctx,\n\t\t\"recorded_users\", \"cameron\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> true\n\n\tres3, err := rdb.BFExists(ctx, \"recorded_users\", \"kaitlyn\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3) // >>> false","section_id":"set-membership"},{"id":"set-membership-ex1","language":"go","code":"res4, err := rdb.CFAdd(ctx, \"other_users\", \"paolo\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4) // >>> true\n\n\tres5, err := rdb.CFAdd(ctx, \"other_users\", \"kaitlyn\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res5) // >>> true\n\n\tres6, err := rdb.CFAdd(ctx, \"other_users\", \"rachel\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res6) // >>> true\n\n\tres7, err := rdb.CFMExists(ctx,\n\t\t\"other_users\", \"paolo\", \"rachel\", \"andy\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res7) // >>> [true true false]\n\n\tres8, err := rdb.CFDel(ctx, \"other_users\", \"paolo\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res8) // >>> true\n\n\tres9, err := rdb.CFExists(ctx, \"other_users\", \"paolo\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res9) // >>> false","section_id":"set-membership"},{"id":"set-cardinality-ex0","language":"go","code":"res10, err := rdb.PFAdd(\n\t\tctx,\n\t\t\"group:1\",\n\t\t\"andy\", \"cameron\", \"david\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res10) // >>> 1\n\n\tres11, err := rdb.PFCount(ctx, \"group:1\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res11) // >>> 3\n\n\tres12, err := rdb.PFAdd(ctx,\n\t\t\"group:2\",\n\t\t\"kaitlyn\", \"michelle\", \"paolo\", \"rachel\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res12) // >>> 1\n\n\tres13, err := rdb.PFCount(ctx, \"group:2\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res13) // >>> 4\n\n\tres14, err := rdb.PFMerge(\n\t\tctx,\n\t\t\"both_groups\",\n\t\t\"group:1\", \"group:2\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res14) // >>> OK\n\n\tres15, err := rdb.PFCount(ctx, \"both_groups\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res15) // >>> 7","section_id":"set-cardinality"},{"id":"frequency-ex0","language":"go","code":"// Specify that you want to keep the counts within 0.01\n\t// (0.1%) of the true value with a 0.005 (0.05%) chance\n\t// of going outside this limit.\n\tres16, err := rdb.CMSInitByProb(ctx, \"items_sold\", 0.01, 0.005).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res16) // >>> OK\n\n\t// The parameters for `CMSIncrBy()` are two lists. The count\n\t// for each item in the first list is incremented by the\n\t// value at the same index in the second list.\n\tres17, err := rdb.CMSIncrBy(ctx, \"items_sold\",\n\t\t\"bread\", 300,\n\t\t\"tea\", 200,\n\t\t\"coffee\", 200,\n\t\t\"beer\", 100,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res17) // >>> [300 200 200 100]\n\n\tres18, err := rdb.CMSIncrBy(ctx, \"items_sold\",\n\t\t\"bread\", 100,\n\t\t\"coffee\", 150,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res18) // >>> [400 350]\n\n\tres19, err := rdb.CMSQuery(ctx,\n\t\t\"items_sold\",\n\t\t\"bread\", \"tea\", \"coffee\", \"beer\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res19) // >>> [400 200 350 100]","section_id":"frequency"},{"id":"quantiles-ex0","language":"go","code":"res20, err := rdb.TDigestCreate(ctx, \"male_heights\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res20) // >>> OK\n\n\tres21, err := rdb.TDigestAdd(ctx, \"male_heights\",\n\t\t175.5, 181, 160.8, 152, 177, 196, 164,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res21) // >>> OK\n\n\tres22, err := rdb.TDigestMin(ctx, \"male_heights\").Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tfmt.Println(res22) // >>> 152\n\n\tres23, err := rdb.TDigestMax(ctx, \"male_heights\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res23) // >>> 196\n\n\tres24, err := rdb.TDigestQuantile(ctx, \"male_heights\", 0.75).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res24) // >>> [181]\n\n\t// Note that the CDF value for 181 is not exactly\n\t// 0.75. Both values are estimates.\n\tres25, err := rdb.TDigestCDF(ctx, \"male_heights\", 181).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Printf(\"%.4f\\n\", res25[0]) // >>> 0.7857\n\n\tres26, err := rdb.TDigestCreate(ctx, \"female_heights\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res26) // >>> OK\n\n\tres27, err := rdb.TDigestAdd(ctx, \"female_heights\",\n\t\t155.5, 161, 168.5, 170, 157.5, 163, 171,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res27) // >>> OK\n\n\tres28, err := rdb.TDigestQuantile(ctx, \"female_heights\", 0.75).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res28) // >>> [170]\n\n\tres29, err := rdb.TDigestMerge(ctx, \"all_heights\",\n\t\tnil,\n\t\t\"male_heights\", \"female_heights\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res29) // >>> OK\n\n\tres30, err := rdb.TDigestQuantile(ctx, \"all_heights\", 0.75).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res30) // >>> [175.5]","section_id":"quantiles"},{"id":"ranking-ex0","language":"go","code":"// Create a TopK filter that keeps track of the top 3 items\n\tres31, err := rdb.TopKReserve(ctx, \"top_3_songs\", 3).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res31) // >>> OK\n\n\t// Add some items to the filter\n\tres32, err := rdb.TopKIncrBy(ctx,\n\t\t\"top_3_songs\",\n\t\t\"Starfish Trooper\", 3000,\n\t\t\"Only one more time\", 1850,\n\t\t\"Rock me, Handel\", 1325,\n\t\t\"How will anyone know?\", 3890,\n\t\t\"Average lover\", 4098,\n\t\t\"Road to everywhere\", 770,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res32)\n\t// >>> [   Rock me, Handel Only one more time ]\n\n\tres33, err := rdb.TopKList(ctx, \"top_3_songs\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res33)\n\t// >>> [Average lover How will anyone know? Starfish Trooper]\n\n\t// Query the count for specific items\n\tres34, err := rdb.TopKQuery(\n\t\tctx,\n\t\t\"top_3_songs\",\n\t\t\"Starfish Trooper\", \"Road to everywhere\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res34) // >>> [true false]","section_id":"ranking"}]}
{"schema_version":2,"id":"develop/clients/go/produsage","title":"Production usage","url":"https://redis.io/docs/latest/develop/clients/go/produsage/","summary":"Get your `go-redis` app ready for production","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-12T14:54:04+01:00","page_type":"content","content_hash":"233109dda9f86337bdd4ea3dd4679544233fd9702be8c5c0f28a5f13024cbfd9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This guide offers recommendations to get the best reliability and\nperformance in your production environment."},{"id":"checklist","title":"Checklist","role":"content","text":"Each item in the checklist below links to the section\nfor a recommendation. Use the checklist icons to record your\nprogress in implementing the recommendations.\n\n[code example]"},{"id":"recommendations","title":"Recommendations","role":"content","text":"The sections below offer recommendations for your production environment. Some\nof them may not apply to your particular use case."},{"id":"health-checks","title":"Health checks","role":"content","text":"If your code doesn't access the Redis server continuously then it\nmight be useful to make a \"health check\" periodically (perhaps once\nevery few seconds). You can do this using a simple\n[`PING`](https://redis.io/docs/latest/commands/ping) command:\n\n[code example]\n\nHealth checks help to detect problems as soon as possible without\nwaiting for a user to report them."},{"id":"error-handling","title":"Error handling","role":"errors","text":"The `Result()` method of a command returns both the command result\nand an error value. Although you are mainly interested in the result,\nyou should also always check that the error value is `nil` before\nproceeding. Errors can be returned for failed connections, network\nproblems, and invalid command parameters, among other things.\n\nSee [Error handling](https://redis.io/docs/latest/develop/clients/go/error-handling) for a\nmore detailed discussion of error handling approaches in `go-redis`."},{"id":"monitor-performance-and-errors","title":"Monitor performance and errors","role":"content","text":"`go-redis` supports [OpenTelemetry](https://opentelemetry.io/). This lets\nyou trace command execution and monitor your server's performance.\nYou can use this information to detect problems before they are reported\nby users. See [Observability](https://redis.io/docs/latest/develop/clients/go/observability)\nfor more information.\n\nTo find the keys that use the most CPU time and network bandwidth on the\nserver, use the [`HOTKEYS`](https://redis.io/docs/latest/commands/hotkeys) command."},{"id":"retries","title":"Retries","role":"content","text":"`go-redis` will automatically retry failed connections and commands. By\ndefault, the number of attempts is set to three, but you can change this\nusing the `MaxRetries` field of `Options` when you connect. The retry\nstrategy starts with a short delay between the first and second attempts,\nand increases the delay with each attempt. The initial delay is set\nwith the `MinRetryBackoff` option (defaulting to 8 milliseconds) and the\nmaximum delay is set with the `MaxRetryBackoff` option (defaulting to\n512 milliseconds):\n\n[code example]\n\nYou can use the observability features of `go-redis` to monitor the\nnumber of retries and the time taken for each attempt, as noted in the\n[Monitor performance and errors](#monitor-performance-and-errors) section\nabove. Use this data to help you decide on the best retry settings\nfor your application."},{"id":"timeouts","title":"Timeouts","role":"content","text":"`go-redis` supports timeouts for connections and commands to avoid\nstalling your app if the server does not respond within a reasonable time.\nThe `DialTimeout` field of `Options` sets the timeout for connections,\nand the `ReadTimeout` and `WriteTimeout` fields set the timeouts for\nreading and writing data, respectively. The default timeout is five seconds\nfor connections and three seconds for reading and writing data, but you can\nset your own timeouts when you connect:\n\n[code example]\n\nYou can use the observability features of `go-redis` to monitor the\nfrequency of timeouts, as noted in the\n[Monitor performance and errors](#monitor-performance-and-errors) section\nabove. Use this data to help you decide on the best timeout settings\nfor your application. If timeouts are set too short, then `go-redis`\nmight retry commands that would have succeeded if given more time. However,\nif they are too long, your app might hang unnecessarily while waiting for a\nresponse that will never arrive."},{"id":"connection-pooling","title":"Connection pooling","role":"content","text":"`go-redis` manages connections for you with a\n[connection pool](https://redis.io/docs/latest/develop/clients/pools-and-muxing), so your\napp doesn't have to cache and reuse open connections itself. The `PoolSize`\nfield of `Options` sets the number of\nconnections the main pool keeps (with a default of ten times the value of `GOMAXPROCS`). `MinIdleConns` sets how many\nconnections to open before your app asks for them, `MaxActiveConns` caps the\ntotal number of connections, and `PoolTimeout` sets how long a command waits\nfor a free connection. By default, no connections are opened in advance, the\ntotal is uncapped, and a command waits one second longer than `ReadTimeout`.\n\nBy default, pipelines don't use the main pool. In go-redis v9.23.0 or later,\nevery client also creates a separate *pipeline pool* that\n[pipelines and transactions](https://redis.io/docs/latest/develop/clients/go/transpipe) and\n[automatic pipelining](https://redis.io/docs/latest/develop/clients/go/autopipeline) use\nfor each batch they run. This prevents a burst of batches from competing with your\nordinary commands for the same connections.\n\nUse the following fields of `Options` to size the pipeline pool:\n\n| Field | Description |\n| :---- | :---------- |\n| `PipelinePoolSize` | Number of connections the pipeline pool keeps. Defaults to 10. Set it to `0` to use the default number of connections, or to `-1` to disable the separate pipeline pool (which means connections are allocated to pipelines from the main pool). |\n| `PipelineReadBufferSize` | Size of the read buffer for each pipeline connection. Defaults to the larger of `ReadBufferSize` and 128 KiB, because a batch of commands can return many replies in a single round trip. If the value is set smaller than the minimum required by the [RESP3](https://redis.io/docs/latest/develop/reference/protocol-spec#resp-versions) protocol for push messages then that minimum is used instead. |\n| `PipelineWriteBufferSize` | Size of the write buffer for each pipeline connection. Defaults to the larger of `WriteBufferSize` and 128 KiB. |\n\nThe pipeline pool costs you nothing while you are not using pipelining. It never opens\nconnections in advance regardless of the value of `MinIdleConns`, so an idle pipeline\npool holds no connections at all. Also, it doesn't use\n`MaxActiveConns` as its upper limit (which would double the number of connections your client can\nopen). The limit is instead the value of `MaxActiveConns` plus `PipelinePoolSize`.\n`ClusterClient` and `Ring` create a pipeline pool for each node, so this limit\napplies for each node rather than for each client.\n\nIf every pipeline connection is busy, a batch runs on the main pool straight away\nrather than waiting for a pipeline connection. A burst of batches wider than\n`PipelinePoolSize` therefore opens extra connections in the main pool, which\nstill applies the `MaxActiveConns` limit. A `Limiter` counts such a batch once\nrather than twice.\n\nYou can find out the current size of the pipeline pool using the `PipelineStats`\nfield from the client's pool statistics:\n\n[code example]\n\nNo field counts the batches that run on the main pool, and they don't appear\nas timeouts. If `PipelineStats.TotalConns` stays at `PipelinePoolSize` while the\nmain pool's `TotalConns` grows during bursts of batches, increase\n`PipelinePoolSize`. Note that `PipelineStats` is `nil` when you disable the\npipeline pool (by setting `PipelinePoolSize` to `-1`). It also combines\nthe figures from every node for `ClusterClient` and `Ring` into the same count."},{"id":"smart-client-handoffs","title":"Smart client handoffs","role":"content","text":"*Smart client handoffs (SCH)* is a feature of Redis Cloud and\nRedis Software servers that lets them actively notify clients\nabout planned server maintenance shortly before it happens. This\nlets a client take action to avoid disruptions in service.\n\nSee [Smart client handoffs](https://redis.io/docs/latest/develop/clients/sch)\nfor more information about SCH and\n[Connect using Smart client handoffs](https://redis.io/docs/latest/develop/clients/go/connect#connect-using-smart-client-handoffs-sch)\nfor example code."}],"examples":[{"id":"checklist-ex0","language":"checklist {id=\"goprodlist\"}","code":"- [ ] [Health checks](#health-checks)\n- [ ] [Error handling](#error-handling)\n- [ ] [Monitor performance and errors](#monitor-performance-and-errors)\n- [ ] [Retries](#retries)\n- [ ] [Timeouts](#timeouts)\n- [ ] [Connection pooling](#connection-pooling)\n- [ ] [Smart client handoffs](#smart-client-handoffs)","section_id":"checklist"},{"id":"health-checks-ex0","language":"go","code":"err := rdb.Ping(ctx).Err()\n\nif err != nil {\n  // Report failed health check.\n}","section_id":"health-checks"},{"id":"retries-ex0","language":"go","code":"client := redis.NewClient(&redis.Options{\n    MinRetryBackoff: 10 * time.Millisecond,\n    MaxRetryBackoff: 100 * time.Millisecond,\n    MaxRetries: 5,\n})","section_id":"retries"},{"id":"timeouts-ex0","language":"go","code":"client := redis.NewClient(&redis.Options{\n    DialTimeout:  10 * time.Second,\n    ReadTimeout:  5 * time.Second,\n    WriteTimeout: 5 * time.Second,\n})","section_id":"timeouts"},{"id":"connection-pooling-ex0","language":"go","code":"stats := client.PoolStats()\nfmt.Printf(\"Main pool connections: %d, hits: %d, misses: %d\\n\",\n    stats.TotalConns, stats.Hits, stats.Misses)\nif ps := stats.PipelineStats; ps != nil {\n    fmt.Printf(\"Pipeline pool connections: %d, hits: %d, misses: %d\\n\",\n        ps.TotalConns, ps.Hits, ps.Misses)\n}","section_id":"connection-pooling"}]}
{"schema_version":2,"id":"develop/clients/go/queryjson","title":"Index and query documents","url":"https://redis.io/docs/latest/develop/clients/go/queryjson/","summary":"Learn how to use Redis Search with JSON and hash documents.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-16T16:25:49+01:00","page_type":"content","content_hash":"af3047cc2481accc11d0433018ff39928b621caab99994c26112ac01cc14493d","sections":[{"id":"initialize","title":"Initialize","role":"content","text":"Make sure that you have [Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack)\nor another Redis server available. Also install the\n[`go-redis`](https://redis.io/docs/latest/develop/clients/go) client library if you\nhaven't already done so.\n\nAdd the following dependencies:\n\nFoundational: Import go-redis package, which includes support for Redis Search operations\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]"},{"id":"create-data","title":"Create data","role":"content","text":"Create some test data to add to your database. The example data shown\nbelow is compatible with both JSON and hash objects.\n\nFoundational: Create sample user data structures for indexing and querying\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]"},{"id":"add-the-index","title":"Add the index","role":"content","text":"Connect to your Redis database. The code below shows the most\nbasic connection but see\n[Connect to the server](https://redis.io/docs/latest/develop/clients/go/connect)\nto learn more about the available connection options.\n\nFoundational: Establish a connection to Redis with RESP2 protocol for Redis Search operations\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n&nbsp;\n\n> [!NOTE]\n> The connection options in the example specify\n> [RESP2](https://redis.io/docs/latest/develop/reference/protocol-spec) in the `Protocol`\n> field. We recommend that you use RESP2 for Redis Search operations in `go-redis`\n> because some of the response structures for the default RESP3 are currently\n> incomplete and so you must handle the \"raw\" responses in your own code.\n>\n> If you do want to use RESP3, you should set the `UnstableResp3` option when\n> you connect:\n>\n> [code example]\n>\n> You must also access command results using the `RawResult()` and `RawVal()` methods\n> rather than the usual `Result()` and `Val()`:\n>\n> [code example]\n\nUse the code below to create a search index. The `FTCreateOptions` parameter enables\nindexing only for JSON objects where the key has a `user:` prefix.\nThe\n[schema](https://redis.io/docs/latest/develop/ai/search-and-query/indexing)\nfor the index has three fields for the user's name, age, and city.\nThe `FieldName` field of the `FieldSchema` struct specifies a\n[JSON path](https://redis.io/docs/latest/develop/data-types/json/path)\nthat identifies which data field to index. Use the `As` struct field\nto provide an alias for the JSON path expression. You can use\nthe alias in queries as a short and intuitive way to refer to the\nexpression, instead of typing it in full:\n\nFoundational: Create a search index for JSON documents with field definitions and key prefix filtering\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]"},{"id":"add-the-data","title":"Add the data","role":"content","text":"Add the three sets of user data to the database as\n[JSON](https://redis.io/docs/latest/develop/data-types/json) objects.\nIf you use keys with the `user:` prefix then Redis will index the\nobjects automatically as you add them:\n\nFoundational: Store JSON documents with automatic indexing using JSONSet with matching key prefix\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]"},{"id":"query-the-data","title":"Query the data","role":"content","text":"You can now use the index to search the JSON objects. The\n[query](https://redis.io/docs/latest/develop/ai/search-and-query/query)\nbelow searches for objects that have the text \"Paul\" in any field\nand have an `age` value in the range 30 to 40:\n\nQuery data: Execute a full-text search with numeric range filtering using FTSearchWithArgs\n\n**Difficulty:** Intermediate\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nSpecify query options to return only the `city` field:\n\nRestrict query results: Limit query results to specific fields using FTSearchOptions to reduce data transfer\n\n**Difficulty:** Intermediate\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nYou can also use the same query with the `CountOnly` option\nenabled to get the number of documents found without\nreturning the documents themselves.\n\nPerformance optimization: Get document count without retrieving results using CountOnly option for better performance\n\n**Difficulty:** Intermediate\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nUse an\n[aggregation query](https://redis.io/docs/latest/develop/ai/search-and-query/query/aggregation)\nto count all users in each city.\n\nAggregation: Perform aggregation queries to group and count documents by field values using FTAggregateWithArgs\n\n**Difficulty:** Intermediate\n\n**Available in:** Go\n\n##### Go\n\n[code example]"},{"id":"differences-with-hash-documents","title":"Differences with hash documents","role":"content","text":"Indexing for hash documents is very similar to JSON indexing but you\nneed to specify some slightly different options.\n\nWhen you create the schema for a hash index, you don't need to\nadd aliases for the fields, since you use the basic names to access\nthe fields anyway. Also, you must set `OnHash` to `true` in the `FTCreateOptions`\nobject when you create the index. The code below shows these changes with\na new index called `hash-idx:users`, which is otherwise the same as\nthe `idx:users` index used for JSON documents in the previous examples.\n\nFoundational: Create a search index for hash documents with OnHash option and simplified field schema\n\n**Difficulty:** Intermediate\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nYou use [`HSet()`](https://redis.io/docs/latest/commands/hset) to add the hash\ndocuments instead of [`JSONSet()`](https://redis.io/docs/latest/commands/json.set),\nbut the same flat `userX` maps work equally well with either\nhash or JSON:\n\nFoundational: Store hash documents with automatic indexing using HSet with matching key prefix\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nThe query commands work the same here for hash as they do for JSON (but\nthe name of the hash index is different). The format of the result is\nalmost the same except that the fields are returned directly in the\n`Document` object map of the result (for JSON, the fields are all enclosed\nin a string under the key \"$\"):\n\nQuery data: Execute search queries on hash documents with slightly different result structure compared to JSON\n\n**Difficulty:** Intermediate\n\n**Available in:** Go\n\n##### Go\n\n[code example]"},{"id":"bulk-import-with-himport","title":"Bulk import with HIMPORT","role":"content","text":"From Redis Open Source 8.10, you can also add the hash documents with the\n[`HIMPORT`](https://redis.io/docs/latest/commands/himport) command family. This creates the same\nhashes as `HSet()`, but lets Redis store them as\n[compact hashes](https://redis.io/docs/latest/develop/data-types/hashes#compact-hashes), which use\nless memory when many keys share the same field names. Redis Search indexes\nthese hashes in the same way.\n\nDeclare the field names once with\n[`HImportPrepare()`](https://redis.io/docs/latest/commands/himport-prepare), then create each\ndocument with [`HImportSet()`](https://redis.io/docs/latest/commands/himport-set), passing only\nits values, in the same order as the field names. Use a\n[pipeline](https://redis.io/docs/latest/develop/clients/go/transpipe) to send all the\nimports in one round trip:\n\nBulk ingestion: Import hash documents as compact hashes using HIMPORT PREPARE and a pipeline of HIMPORT SET calls\n\n**Difficulty:** Intermediate\n\n**Builds upon:** add_hash_data\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\n`HImportSet()` replaces any existing key, so running this code after the\n`HSet()` calls leaves you with the same three documents. The query returns\nthe same result.\n\n> [!NOTE]\n> `HIMPORT` requires `go-redis` v9.22.0 or later, and `go-redis` marks its\n> support as experimental, so the API might change. Redis Cloud and\n> Redis Software don't support `HIMPORT`."},{"id":"more-information","title":"More information","role":"content","text":"See the [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) docs\nfor a full description of all query features with examples."}],"examples":[{"id":"initialize-ex0","language":"go","code":"import (\n\t\"context\"\n\t\"fmt\"\n\t\"sort\"\n\n\t\"github.com/redis/go-redis/v9\"\n)","section_id":"initialize"},{"id":"create-data-ex0","language":"go","code":"user1 := map[string]interface{}{\n\t\t\"name\":  \"Paul John\",\n\t\t\"email\": \"paul.john@example.com\",\n\t\t\"age\":   42,\n\t\t\"city\":  \"London\",\n\t}\n\n\tuser2 := map[string]interface{}{\n\t\t\"name\":  \"Eden Zamir\",\n\t\t\"email\": \"eden.zamir@example.com\",\n\t\t\"age\":   29,\n\t\t\"city\":  \"Tel Aviv\",\n\t}\n\n\tuser3 := map[string]interface{}{\n\t\t\"name\":  \"Paul Zamir\",\n\t\t\"email\": \"paul.zamir@example.com\",\n\t\t\"age\":   35,\n\t\t\"city\":  \"Tel Aviv\",\n\t}","section_id":"create-data"},{"id":"add-the-index-ex0","language":"go","code":"ctx := context.Background()\n\n\trdb := redis.NewClient(&redis.Options{\n\t\tAddr:     \"localhost:6379\",\n\t\tPassword: \"\", // no password docs\n\t\tDB:       0,  // use default DB\n\t\tProtocol: 2,\n\t})","section_id":"add-the-index"},{"id":"add-the-index-ex1","language":"go","code":"> rdb := redis.NewClient(&redis.Options{\n>     UnstableResp3: true,\n>     // Other options...\n> })\n>","section_id":"add-the-index"},{"id":"add-the-index-ex2","language":"go","code":"> res1, err := client.FTSearchWithArgs(\n>     ctx, \"txt\", \"foo bar\", &redis.FTSearchOptions{},\n> ).RawResult()\n> val1 := client.FTSearchWithArgs(\n>     ctx, \"txt\", \"foo bar\", &redis.FTSearchOptions{},\n> ).RawVal()\n>","section_id":"add-the-index"},{"id":"add-the-index-ex3","language":"go","code":"_, err := rdb.FTCreate(\n\t\tctx,\n\t\t\"idx:users\",\n\t\t// Options:\n\t\t&redis.FTCreateOptions{\n\t\t\tOnJSON: true,\n\t\t\tPrefix: []interface{}{\"user:\"},\n\t\t},\n\t\t// Index schema fields:\n\t\t&redis.FieldSchema{\n\t\t\tFieldName: \"$.name\",\n\t\t\tAs:        \"name\",\n\t\t\tFieldType: redis.SearchFieldTypeText,\n\t\t},\n\t\t&redis.FieldSchema{\n\t\t\tFieldName: \"$.city\",\n\t\t\tAs:        \"city\",\n\t\t\tFieldType: redis.SearchFieldTypeTag,\n\t\t},\n\t\t&redis.FieldSchema{\n\t\t\tFieldName: \"$.age\",\n\t\t\tAs:        \"age\",\n\t\t\tFieldType: redis.SearchFieldTypeNumeric,\n\t\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}","section_id":"add-the-index"},{"id":"add-the-data-ex0","language":"go","code":"_, err = rdb.JSONSet(ctx, \"user:1\", \"$\", user1).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t_, err = rdb.JSONSet(ctx, \"user:2\", \"$\", user2).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t_, err = rdb.JSONSet(ctx, \"user:3\", \"$\", user3).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}","section_id":"add-the-data"},{"id":"query-the-data-ex0","language":"go","code":"findPaulResult, err := rdb.FTSearch(\n\t\tctx,\n\t\t\"idx:users\",\n\t\t\"Paul @age:[30 40]\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(findPaulResult)\n\t// >>> {1 [{user:3 <nil> <nil> <nil> map[$:{\"age\":35,\"city\":\"Tel Aviv\"...","section_id":"query-the-data"},{"id":"query-the-data-ex1","language":"go","code":"citiesResult, err := rdb.FTSearchWithArgs(\n\t\tctx,\n\t\t\"idx:users\",\n\t\t\"Paul\",\n\t\t&redis.FTSearchOptions{\n\t\t\tReturn: []redis.FTSearchReturn{\n\t\t\t\t{\n\t\t\t\t\tFieldName: \"$.city\",\n\t\t\t\t\tAs:        \"city\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tsort.Slice(citiesResult.Docs, func(i, j int) bool {\n\t\treturn citiesResult.Docs[i].Fields[\"city\"] < citiesResult.Docs[j].Fields[\"city\"]\n\t})\n\n\tfor _, result := range citiesResult.Docs {\n\t\tfmt.Println(result.Fields[\"city\"])\n\t}\n\t// >>> London\n\t// >>> Tel Aviv","section_id":"query-the-data"},{"id":"query-the-data-ex2","language":"go","code":"citiesResult2, err := rdb.FTSearchWithArgs(\n\t\tctx,\n\t\t\"idx:users\",\n\t\t\"Paul\",\n\t\t&redis.FTSearchOptions{\n\t\t\tReturn: []redis.FTSearchReturn{\n\t\t\t\t{\n\t\t\t\t\tFieldName: \"$.city\",\n\t\t\t\t\tAs:        \"city\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tCountOnly: true,\n\t\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// The `Total` field has the correct number of docs found\n\t// by the query but the `Docs` slice is empty.\n\tfmt.Println(len(citiesResult2.Docs)) // >>> 0\n\tfmt.Println(citiesResult2.Total)     // >>> 2","section_id":"query-the-data"},{"id":"query-the-data-ex3","language":"go","code":"aggOptions := redis.FTAggregateOptions{\n\t\tGroupBy: []redis.FTAggregateGroupBy{\n\t\t\t{\n\t\t\t\tFields: []interface{}{\"@city\"},\n\t\t\t\tReduce: []redis.FTAggregateReducer{\n\t\t\t\t\t{\n\t\t\t\t\t\tReducer: redis.SearchCount,\n\t\t\t\t\t\tAs:      \"count\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\taggResult, err := rdb.FTAggregateWithArgs(\n\t\tctx,\n\t\t\"idx:users\",\n\t\t\"*\",\n\t\t&aggOptions,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tsort.Slice(aggResult.Rows, func(i, j int) bool {\n\t\treturn aggResult.Rows[i].Fields[\"city\"].(string) <\n\t\t\taggResult.Rows[j].Fields[\"city\"].(string)\n\t})\n\n\tfor _, row := range aggResult.Rows {\n\t\tfmt.Printf(\"%v - %v\\n\",\n\t\t\trow.Fields[\"city\"], row.Fields[\"count\"],\n\t\t)\n\t}\n\t// >>> City: London - 1\n\t// >>> City: Tel Aviv - 2","section_id":"query-the-data"},{"id":"differences-with-hash-documents-ex0","language":"go","code":"_, err := rdb.FTCreate(\n\t\tctx,\n\t\t\"hash-idx:users\",\n\t\t// Options:\n\t\t&redis.FTCreateOptions{\n\t\t\tOnHash: true,\n\t\t\tPrefix: []interface{}{\"huser:\"},\n\t\t},\n\t\t// Index schema fields:\n\t\t&redis.FieldSchema{\n\t\t\tFieldName: \"name\",\n\t\t\tFieldType: redis.SearchFieldTypeText,\n\t\t},\n\t\t&redis.FieldSchema{\n\t\t\tFieldName: \"city\",\n\t\t\tFieldType: redis.SearchFieldTypeTag,\n\t\t},\n\t\t&redis.FieldSchema{\n\t\t\tFieldName: \"age\",\n\t\t\tFieldType: redis.SearchFieldTypeNumeric,\n\t\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}","section_id":"differences-with-hash-documents"},{"id":"differences-with-hash-documents-ex1","language":"go","code":"_, err = rdb.HSet(ctx, \"huser:1\", user1).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t_, err = rdb.HSet(ctx, \"huser:2\", user2).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t_, err = rdb.HSet(ctx, \"huser:3\", user3).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}","section_id":"differences-with-hash-documents"},{"id":"differences-with-hash-documents-ex2","language":"go","code":"findPaulHashResult, err := rdb.FTSearch(\n\t\tctx,\n\t\t\"hash-idx:users\",\n\t\t\"Paul @age:[30 40]\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(findPaulHashResult)\n\t// >>> {1 [{huser:3 <nil> <nil> <nil> map[age:35 city:Tel Aviv...","section_id":"differences-with-hash-documents"},{"id":"bulk-import-with-himport-ex0","language":"go","code":"fields := []string{\"name\", \"email\", \"age\", \"city\"}\n\n\terr = rdb.HImportPrepare(ctx, \"huser\", fields...).Err()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tpipe := rdb.Pipeline()\n\n\tfor i, user := range []map[string]interface{}{user1, user2, user3} {\n\t\tvalues := make([]interface{}, len(fields))\n\t\tfor j, field := range fields {\n\t\t\tvalues[j] = user[field]\n\t\t}\n\n\t\tpipe.HImportSet(ctx, fmt.Sprintf(\"huser:%d\", i+1), \"huser\", values...)\n\t}\n\n\tif _, err := pipe.Exec(ctx); err != nil {\n\t\tpanic(err)\n\t}","section_id":"bulk-import-with-himport"}]}
{"schema_version":2,"id":"develop/clients/go/scan-buffer","title":"Handle command results efficiently","url":"https://redis.io/docs/latest/develop/clients/go/scan-buffer/","summary":"Scan go-redis command results into Go values and use buffers for large string payloads.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-09T09:42:14+01:00","page_type":"content","content_hash":"d63f7a050a1a71cc2f77158ccc7c53faf4fb31a70c5a5c2bf5ad54e61d60132f","sections":[{"id":"initialize","title":"Initialize","role":"content","text":"Import the packages you need:\n\nFoundational: Import go-redis and supporting packages for scanning and buffer examples\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nConnect to Redis:\n\nFoundational: Connect to Redis before running scan and buffer examples\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]"},{"id":"scan-hashes-into-structs","title":"Scan hashes into structs","role":"content","text":"Use struct tags of the form `redis:\"field\"` to map Redis hash fields to Go\nstruct fields. The `Scan()` method converts matching fields to the destination\ntypes and returns an error if a conversion fails.\n\nThe following example stores a hash with [`HSET`](https://redis.io/docs/latest/commands/hset)\nand then scans the result of [`HGETALL`](https://redis.io/docs/latest/commands/hgetall)\ninto a `Bike` struct:\n\nStructured results: Scan all hash fields into a Go struct using redis field tags\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nYou can also scan a subset of fields with [`HMGET`](https://redis.io/docs/latest/commands/hmget).\nFields that are not returned keep their Go zero values:\n\nStructured results: Scan selected hash fields and leave missing fields at their zero values\n\n**Difficulty:** Intermediate\n\n**Builds upon:** scan_hash\n\n**Available in:** Go\n\n##### Go\n\n[code example]"},{"id":"scan-lists-into-slices","title":"Scan lists into slices","role":"content","text":"Commands that return a list of strings, such as [`LRANGE`](https://redis.io/docs/latest/commands/lrange),\ncan use `ScanSlice()` to convert the result into a typed Go slice:\n\nStructured results: Convert a list command result into a typed Go slice with ScanSlice\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]"},{"id":"use-byte-buffers","title":"Use byte buffers","role":"content","text":"For large string payloads, you can avoid creating a new string for every read by\nproviding a caller-owned byte buffer. Use `SetFromBuffer()` to write a `[]byte`\nvalue and `GetToBuffer()` to read the value into an existing buffer.\n\n> [!NOTE]\n> The buffer APIs require `github.com/redis/go-redis/v9` v9.21.0\n> or later.\n&nbsp;\n\nBuffer optimization: Write and read Redis string values using caller-owned byte buffers\n\n**Difficulty:** Intermediate\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\n`GetToBuffer()` returns a `*redis.ZeroCopyStringCmd`. Use `Val()` or `Result()`\nto get the number of bytes read, `Bytes()` to access the populated slice\n(`buf[:n]`), and `Err()` to check for errors such as `redis.Nil` when the key\ndoes not exist.\n\n> [!NOTE]\n> `GetToBuffer()` requires a buffer large enough to hold the whole value. It also\n> opts out of automatic retries because a failed read might already have written\n> partial data into your buffer. If a buffer is too small, `Err()` reports a\n> `buffer too small` error.\n\nThe following example shows how to detect a buffer that is too small:\n\nBuffer sizing: Detect and handle a buffer that is too small for the Redis value\n\n**Difficulty:** Intermediate\n\n**Builds upon:** buffer_round_trip\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\n`SetFromBuffer()` does not set an expiration. If you need a TTL, call\n[`EXPIRE`](https://redis.io/docs/latest/commands/expire) separately with `Expire()`, or use\n`Set()` with an expiration when the extra allocation is acceptable."},{"id":"more-information","title":"More information","role":"content","text":"See the [`go-redis`](https://github.com/redis/go-redis) repository for more\nexamples and API details."}],"examples":[{"id":"initialize-ex0","language":"go","code":"import (\n\t\"context\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/redis/go-redis/v9\"\n)","section_id":"initialize"},{"id":"initialize-ex1","language":"go","code":"ctx := context.Background()\n\n\trdb := redis.NewClient(&redis.Options{\n\t\tAddr:     \"localhost:6379\",\n\t\tPassword: \"\", // no password\n\t\tDB:       0,  // use default DB\n\t\tProtocol: 2,\n\t})\n\tdefer rdb.Close()","section_id":"initialize"},{"id":"scan-hashes-into-structs-ex0","language":"go","code":"type Bike struct {\n\t\tModel string `redis:\"model\"`\n\t\tBrand string `redis:\"brand\"`\n\t\tPrice int    `redis:\"price\"`\n\t}\n\n\tif err := rdb.HSet(ctx, \"scanbuf:bike:1\",\n\t\t\"model\", \"Deimos\",\n\t\t\"brand\", \"Ergonom\",\n\t\t\"price\", 4972,\n\t).Err(); err != nil {\n\t\tpanic(err)\n\t}\n\n\tvar bike Bike\n\tif err := rdb.HGetAll(ctx, \"scanbuf:bike:1\").Scan(&bike); err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Printf(\"Model: %s, brand: %s, price: $%d\\n\",\n\t\tbike.Model, bike.Brand, bike.Price)\n\t// >>> Model: Deimos, brand: Ergonom, price: $4972","section_id":"scan-hashes-into-structs"},{"id":"scan-hashes-into-structs-ex1","language":"go","code":"type BikeStock struct {\n\t\tModel string `redis:\"model\"`\n\t\tStock int    `redis:\"stock\"`\n\t\tPrice int    `redis:\"price\"`\n\t}\n\n\tvar partial BikeStock\n\tif err := rdb.HMGet(ctx, \"scanbuf:bike:1\", \"model\", \"stock\").Scan(&partial); err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Printf(\"Model: %s, stock: %d, price: $%d\\n\",\n\t\tpartial.Model, partial.Stock, partial.Price)\n\t// >>> Model: Deimos, stock: 0, price: $0","section_id":"scan-hashes-into-structs"},{"id":"scan-lists-into-slices-ex0","language":"go","code":"if err := rdb.RPush(ctx, \"scanbuf:stock\", 3, 4, 5).Err(); err != nil {\n\t\tpanic(err)\n\t}\n\n\tvar stockCounts []int\n\tif err := rdb.LRange(ctx, \"scanbuf:stock\", 0, -1).ScanSlice(&stockCounts); err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(\"Stock counts:\", stockCounts)\n\t// >>> Stock counts: [3 4 5]","section_id":"scan-lists-into-slices"},{"id":"use-byte-buffers-ex0","language":"go","code":"payload := []byte(\"compact bike data\")\n\n\tif err := rdb.SetFromBuffer(ctx, \"scanbuf:payload\", payload).Err(); err != nil {\n\t\tpanic(err)\n\t}\n\tfmt.Println(\"OK\") // >>> OK\n\n\tbuf := make([]byte, len(payload))\n\tcmd := rdb.GetToBuffer(ctx, \"scanbuf:payload\", buf)\n\tn, err := cmd.Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Printf(\"Read %d bytes: %s\\n\", n, string(cmd.Bytes()))\n\t// >>> Read 17 bytes: compact bike data","section_id":"use-byte-buffers"},{"id":"use-byte-buffers-ex1","language":"go","code":"smallBuf := make([]byte, 4)\n\tsmallCmd := rdb.GetToBuffer(ctx, \"scanbuf:payload\", smallBuf)\n\n\tif err := smallCmd.Err(); err != nil {\n\t\tif strings.Contains(err.Error(), \"buffer too small\") {\n\t\t\tfmt.Println(\"Buffer too small\")\n\t\t} else {\n\t\t\tpanic(err)\n\t\t}\n\t}\n\t// >>> Buffer too small","section_id":"use-byte-buffers"}]}
{"schema_version":2,"id":"develop/clients/go/transpipe","title":"Pipelines and transactions","url":"https://redis.io/docs/latest/develop/clients/go/transpipe/","summary":"Learn how to use Redis pipelines and transactions","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-12T14:54:04+01:00","page_type":"content","content_hash":"b435c9bdcc46ecabfc35250d4f9cbd714c2e9b6f4d3af0d0152f62258bdbddc5","sections":[{"id":"execute-a-pipeline","title":"Execute a pipeline","role":"content","text":"To execute commands in a pipeline, you first create a pipeline object\nand then add commands to it using methods that resemble the standard\ncommand methods (for example, `Set()` and `Get()`). The commands are\nbuffered in the pipeline and only execute when you call the `Exec()`\nmethod on the pipeline object.\n\nThe main difference with the pipeline commands is that their return\nvalues contain a valid result only after the pipeline has finished executing.\nYou can access the result using the `Val()` method instead of\n`Result()` (note that errors are reported by the `Exec()` method rather\nthan by the individual commands).\n\nBasic pipeline: Execute multiple commands in a single batch using Pipeline and Exec\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nYou can also create a pipeline using the `Pipelined()` method.\nThis executes pipeline commands in a callback function that you\nprovide and calls `Exec()` automatically after it returns:\n\nPipeline with callback: Use Pipelined method for automatic execution of batched commands\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]"},{"id":"execute-a-transaction","title":"Execute a transaction","role":"content","text":"A transaction works in a similar way to a pipeline. Create a\ntransaction object with the `TxPipeline()` method, call command methods\non that object, and then call the transaction object's\n`Exec()` method to execute it. You can access the results\nfrom commands in the transaction after it completes using the\n`Val()` method.\n\nBasic transaction: Execute commands atomically using TxPipeline to ensure consistency\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nThere is also a `TxPipelined()` method that works in a similar way\nto `Pipelined()`, described above:\n\nTransaction with callback: Use TxPipelined for automatic atomic execution with callback syntax\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]"},{"id":"watch-keys-for-changes","title":"Watch keys for changes","role":"content","text":"Redis supports *optimistic locking* to avoid inconsistent updates\nto different keys. The basic idea is to watch for changes to any\nkeys that you use in a transaction while you are processing the\nupdates. If the watched keys do change, you must restart the updates\nwith the latest data from the keys. See\n[Transactions](https://redis.io/docs/latest/develop/using-commands/transactions)\nfor more information about optimistic locking.\n\nThe code below reads a string\nthat represents a `PATH` variable for a command shell, then appends a new\ncommand path to the string before attempting to write it back. If the watched\nkey is modified by another client before writing, the transaction aborts.\nThe `Watch()` method receives a callback function where you execute the\ncommands you want to watch. In the body of this callback, you can execute\nread-only commands before the transaction using the usual client object\n(called `rdb` in our examples) and receive an immediate result. Start the\ntransaction itself by calling `TxPipeline()` or `TxPipelined()` on the\n`Tx` object passed to the callback. `Watch()` also receives one or more\n`string` parameters after the callback that represent the keys you want\nto watch.\n\nFor production usage, you would generally call code like the following in\na loop to retry it until it succeeds or else report or log the failure:\n\nOptimistic locking: Watch keys for changes and retry transactions when watched keys are modified\n\n**Difficulty:** Advanced\n\n**Available in:** Go\n\n##### Go\n\n[code example]"}],"examples":[{"id":"execute-a-pipeline-ex0","language":"go","code":"pipe := rdb.Pipeline()\n\n\tfor i := 0; i < 5; i++ {\n\t\tpipe.Set(ctx, fmt.Sprintf(\"seat:%v\", i), fmt.Sprintf(\"#%v\", i), 0)\n\t}\n\n\tcmds, err := pipe.Exec(ctx)\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfor _, c := range cmds {\n\t\tfmt.Printf(\"%v;\", c.(*redis.StatusCmd).Val())\n\t}\n\n\tfmt.Println(\"\")\n\t// >>> OK;OK;OK;OK;OK;\n\n\tpipe = rdb.Pipeline()\n\n\tget0Result := pipe.Get(ctx, \"seat:0\")\n\tget3Result := pipe.Get(ctx, \"seat:3\")\n\tget4Result := pipe.Get(ctx, \"seat:4\")\n\n\tcmds, err = pipe.Exec(ctx)\n\n\t// The results are available only after the pipeline\n\t// has finished executing.\n\tfmt.Println(get0Result.Val()) // >>> #0\n\tfmt.Println(get3Result.Val()) // >>> #3\n\tfmt.Println(get4Result.Val()) // >>> #4","section_id":"execute-a-pipeline"},{"id":"execute-a-pipeline-ex1","language":"go","code":"var pd0Result *redis.StatusCmd\n\tvar pd3Result *redis.StatusCmd\n\tvar pd4Result *redis.StatusCmd\n\n\tcmds, err = rdb.Pipelined(ctx, func(pipe redis.Pipeliner) error {\n\t\tpd0Result = (*redis.StatusCmd)(pipe.Get(ctx, \"seat:0\"))\n\t\tpd3Result = (*redis.StatusCmd)(pipe.Get(ctx, \"seat:3\"))\n\t\tpd4Result = (*redis.StatusCmd)(pipe.Get(ctx, \"seat:4\"))\n\t\treturn nil\n\t})\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// The results are available only after the pipeline\n\t// has finished executing.\n\tfmt.Println(pd0Result.Val()) // >>> #0\n\tfmt.Println(pd3Result.Val()) // >>> #3\n\tfmt.Println(pd4Result.Val()) // >>> #4","section_id":"execute-a-pipeline"},{"id":"execute-a-transaction-ex0","language":"go","code":"trans := rdb.TxPipeline()\n\n\ttrans.IncrBy(ctx, \"counter:1\", 1)\n\ttrans.IncrBy(ctx, \"counter:2\", 2)\n\ttrans.IncrBy(ctx, \"counter:3\", 3)\n\n\tcmds, err = trans.Exec(ctx)\n\n\tfor _, c := range cmds {\n\t\tfmt.Println(c.(*redis.IntCmd).Val())\n\t}\n\t// >>> 1\n\t// >>> 2\n\t// >>> 3","section_id":"execute-a-transaction"},{"id":"execute-a-transaction-ex1","language":"go","code":"var tx1Result *redis.IntCmd\n\tvar tx2Result *redis.IntCmd\n\tvar tx3Result *redis.IntCmd\n\n\tcmds, err = rdb.TxPipelined(ctx, func(trans redis.Pipeliner) error {\n\t\ttx1Result = trans.IncrBy(ctx, \"counter:1\", 1)\n\t\ttx2Result = trans.IncrBy(ctx, \"counter:2\", 2)\n\t\ttx3Result = trans.IncrBy(ctx, \"counter:3\", 3)\n\t\treturn nil\n\t})\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(tx1Result.Val()) // >>> 2\n\tfmt.Println(tx2Result.Val()) // >>> 4\n\tfmt.Println(tx3Result.Val()) // >>> 6","section_id":"execute-a-transaction"},{"id":"watch-keys-for-changes-ex0","language":"go","code":"// Set initial value of `shellpath`.\n\trdb.Set(ctx, \"shellpath\", \"/usr/syscmds/\", 0)\n\n\tconst maxRetries = 1000\n\n\t// Retry if the key has been changed.\n\tfor i := 0; i < maxRetries; i++ {\n\t\terr := rdb.Watch(ctx,\n\t\t\tfunc(tx *redis.Tx) error {\n\t\t\t\tcurrentPath, err := rdb.Get(ctx, \"shellpath\").Result()\n\t\t\t\tnewPath := currentPath + \":/usr/mycmds/\"\n\n\t\t\t\t_, err = tx.TxPipelined(ctx, func(pipe redis.Pipeliner) error {\n\t\t\t\t\tpipe.Set(ctx, \"shellpath\", newPath, 0)\n\t\t\t\t\treturn nil\n\t\t\t\t})\n\n\t\t\t\treturn err\n\t\t\t},\n\t\t\t\"shellpath\",\n\t\t)\n\n\t\tif err == nil {\n\t\t\t// Success.\n\t\t\tbreak\n\t\t} else if err == redis.TxFailedErr {\n\t\t\t// Optimistic lock lost. Retry the transaction.\n\t\t\tcontinue\n\t\t} else {\n\t\t\t// Panic for any other error.\n\t\t\tpanic(err)\n\t\t}\n\t}\n\n\tfmt.Println(rdb.Get(ctx, \"shellpath\").Val())\n\t// >>> /usr/syscmds/:/usr/mycmds/","section_id":"watch-keys-for-changes"}]}
{"schema_version":2,"id":"develop/clients/go/vecsearch","title":"Index and query vectors","url":"https://redis.io/docs/latest/develop/clients/go/vecsearch/","summary":"Learn how to index and query vector embeddings with Redis","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-16T16:44:37+01:00","page_type":"content","content_hash":"5c28d2dd4b6ff0f676657e9c4746f5427e1bddb54cc8a706e6f32799dc215563","sections":[{"id":"initialize","title":"Initialize","role":"content","text":"First, install [`go-redis`](https://redis.io/docs/latest/develop/clients/go)\nif you haven't already done so. Then, install v0.7.7 of\n[`Hugot`](https://pkg.go.dev/github.com/knights-analytics/hugot),\nwhich requires Go 1.26.5 or later, using the following command:\n\n[code example]\n\nAdd the following imports to your module's main program file:\n\nFoundational: Import required packages for vector search and embedding operations\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]"},{"id":"add-a-helper-function","title":"Add a helper function","role":"content","text":"The `Hugot` model outputs the embeddings as a\n`[]float32` array. If you are storing your documents as\n[hash](https://redis.io/docs/latest/develop/data-types/hashes) objects, then you\nmust convert this array to a `byte` string before adding it as a hash field.\nThe function shown below uses Go's [`binary`](https://pkg.go.dev/encoding/binary)\npackage to produce the `byte` string:\n\nFoundational: Create a helper function to convert float32 embeddings to binary format for hash storage\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nNote that if you are using [JSON](https://redis.io/docs/latest/develop/data-types/json)\nobjects to store your documents instead of hashes, then you should store\nthe `[]float32` array directly without first converting it to a `byte`\nstring (see [Differences with JSON documents](#differences-with-json-documents)\nbelow)."},{"id":"create-the-index","title":"Create the index","role":"content","text":"In the `main()` function, connect to Redis and delete any index previously\ncreated with the name `vector_idx`:\n\nFoundational: Connect to Redis and clean up existing vector indexes\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nNext, create the index.\nThe schema in the example below specifies hash objects for storage and includes\nthree fields: the text content to index, a\n[tag](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/tags)\nfield to represent the \"genre\" of the text, and the embedding vector generated from\nthe original text content. The `embedding` field specifies\n[HNSW](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#hnsw-index)\nindexing, the\n[L2](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#distance-metrics)\nvector distance metric, `Float32` values to represent the vector's components,\nand 384 dimensions, as required by the `all-MiniLM-L6-v2` embedding model.\n\nFoundational: Create a vector search index with HNSW algorithm and L2 distance metric for hash documents\n\n**Difficulty:** Intermediate\n\n**Available in:** Go\n\n##### Go\n\n[code example]"},{"id":"create-an-embedder-instance","title":"Create an embedder instance","role":"content","text":"You need an instance of the `FeatureExtractionPipeline` class to\ngenerate the embeddings. Use the code below to create an\ninstance that uses the `sentence-transformers/all-MiniLM-L6-v2`\nmodel:\n\nPractical pattern: Initialize a feature extraction pipeline for generating text embeddings\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]"},{"id":"add-data","title":"Add data","role":"content","text":"You can now supply the data objects, which will be indexed automatically\nwhen you add them with [`HSet()`](https://redis.io/docs/latest/commands/hset), as long as\nyou use the `doc:` prefix specified in the index definition.\n\nUse the `RunPipeline()` method of `FeatureExtractionPipeline`\nas shown below to create the embeddings that represent the `content` fields.\nThis method takes a context and an array of strings and returns a single\n`FeatureExtractionOutput` object.\nIts `Embeddings` field contains one array of float values for each input\nstring, in the same order. These are the vectors that you need for the index. Use the `floatsToBytes()` function defined\nabove to convert this array to a `byte` string.\n\nFoundational: Generate embeddings and store hash documents with vector fields using HSet\n\n**Difficulty:** Intermediate\n\n**Available in:** Go\n\n##### Go\n\n[code example]"},{"id":"bulk-import-with-himport","title":"Bulk import with HIMPORT","role":"content","text":"From Redis Open Source 8.10, you can also add the documents with the\n[`HIMPORT`](https://redis.io/docs/latest/commands/himport) command family. This creates the same\nhashes as `HSet()`, but lets Redis store them as\n[compact hashes](https://redis.io/docs/latest/develop/data-types/hashes#compact-hashes), which use\nless memory when many keys share the same field names. Redis Search indexes\nthese hashes in the same way.\n\nDeclare the field names once with\n[`HImportPrepare()`](https://redis.io/docs/latest/commands/himport-prepare), then create each\ndocument with [`HImportSet()`](https://redis.io/docs/latest/commands/himport-set), passing only\nits values, in the same order as the field names. Use a\n[pipeline](https://redis.io/docs/latest/develop/clients/go/transpipe) to send all the\nimports in one round trip:\n\nBulk ingestion: Import hash documents with vector embeddings as compact hashes using HIMPORT PREPARE and a pipeline of HIMPORT SET calls\n\n**Difficulty:** Intermediate\n\n**Builds upon:** add_data\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\n`HImportSet()` replaces any existing key, so running this code after the\n`HSet()` calls leaves you with the same three documents.\n\n> [!NOTE]\n> `HIMPORT` requires `go-redis` v9.22.0 or later, and `go-redis` marks its\n> support as experimental, so the API might change. Redis Cloud and\n> Redis Software don't support `HIMPORT`."},{"id":"run-a-query","title":"Run a query","role":"content","text":"After you have created the index and added the data, you are ready to run a query.\nTo do this, you must create another embedding vector from your chosen query\ntext. Redis calculates the similarity between the query vector and each\nembedding vector in the index as it runs the query. It then ranks the\nresults in order of this numeric similarity value.\n\nThe code below creates the query embedding using `RunPipeline()`, as with\nthe indexing, and passes it as a parameter when the query executes\n(see\n[Vector search](https://redis.io/docs/latest/develop/ai/search-and-query/query/vector-search)\nfor more information about using query parameters with embeddings).\n\nVector similarity search: Find semantically similar documents by comparing query embeddings with indexed vectors using L2 distance\n\n**Difficulty:** Intermediate\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nThe code is now ready to run, but note that it may take a while to complete when\nyou run it for the first time (which happens because `Hugot`\nmust download the `all-MiniLM-L6-v2` model data before it can\ngenerate the embeddings). When you run the code, it outputs the following text:\n\n[code example]\n\nThe results are ordered according to the value of the `vector_distance`\nfield, with the lowest distance indicating the greatest similarity to the query.\nAs you would expect, the result for `doc:0` with the content text *\"That is a very happy person\"*\nis the result that is most similar in meaning to the query text\n*\"That is a happy person\"*."},{"id":"differences-with-json-documents","title":"Differences with JSON documents","role":"content","text":"Indexing JSON documents is similar to hash indexing, but there are some\nimportant differences. JSON allows much richer data modelling with nested fields, so\nyou must supply a [path](https://redis.io/docs/latest/develop/data-types/json/path) in the schema\nto identify each field you want to index. However, you can declare a short alias for each\nof these paths (using the `As` option) to avoid typing it in full for\nevery query. Also, you must set `OnJSON` to `true` when you create the index.\n\nThe code below shows these differences, but the index is otherwise very similar to\nthe one created previously for hashes:\n\nFoundational: Create a vector search index for JSON documents with OnJSON option and JSON path specifications\n\n**Difficulty:** Intermediate\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nUse [`JSONSet()`](https://redis.io/docs/latest/commands/json.set) to add the data\ninstead of [`HSet()`](https://redis.io/docs/latest/commands/hset). The maps\nthat specify the fields have the same structure as the ones used for `HSet()`.\n\nAn important difference with JSON indexing is that the vectors are\nspecified using lists instead of binary strings. The loop below is similar\nto the one used previously to add the hash data, but it doesn't use the\n`floatsToBytes()` function to encode the `float32` array.\n\nFoundational: Store JSON documents with vector embeddings as arrays using JSONSet\n\n**Difficulty:** Intermediate\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nThe query is almost identical to the one for the hash documents. This\ndemonstrates how the right choice of aliases for the JSON paths can\nsave you having to write complex queries. An important thing to notice\nis that the vector parameter for the query is still specified as a\nbinary string (using the `floatsToBytes()` method), even though the data for\nthe `embedding` field of the JSON was specified as an array.\n\nVector query: Execute vector similarity search on JSON documents using path aliases for cleaner queries\n\n**Difficulty:** Intermediate\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nApart from the `jdoc:` prefixes for the keys, the result from the JSON\nquery is the same as for hash:\n\n[code example]"},{"id":"learn-more","title":"Learn more","role":"related","text":"See\n[Vector search](https://redis.io/docs/latest/develop/ai/search-and-query/query/vector-search)\nfor more information about the indexing options, distance metrics, and query format\nfor vectors."}],"examples":[{"id":"initialize-ex0","language":"bash","code":"go get github.com/knights-analytics/hugot@v0.7.7","section_id":"initialize"},{"id":"initialize-ex1","language":"go","code":"package main\n\nimport (\n\t\"context\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"math\"\n\n\t\"github.com/knights-analytics/hugot\"\n\t\"github.com/redis/go-redis/v9\"\n)","section_id":"initialize"},{"id":"add-a-helper-function-ex0","language":"go","code":"func floatsToBytes(fs []float32) []byte {\n\tbuf := make([]byte, len(fs)*4)\n\n\tfor i, f := range fs {\n\t\tu := math.Float32bits(f)\n\t\tbinary.NativeEndian.PutUint32(buf[i*4:], u)\n\t}\n\n\treturn buf\n}","section_id":"add-a-helper-function"},{"id":"create-the-index-ex0","language":"go","code":"ctx := context.Background()\n\trdb := redis.NewClient(&redis.Options{\n\t\tAddr:     \"localhost:6379\",\n\t\tPassword: \"\", // no password docs\n\t\tDB:       0,  // use default DB\n\t\tProtocol: 2,\n\t})\n\n\trdb.FTDropIndexWithArgs(ctx,\n\t\t\"vector_idx\",\n\t\t&redis.FTDropIndexOptions{\n\t\t\tDeleteDocs: true,\n\t\t},\n\t)","section_id":"create-the-index"},{"id":"create-the-index-ex1","language":"go","code":"_, err := rdb.FTCreate(ctx,\n\t\t\"vector_idx\",\n\t\t&redis.FTCreateOptions{\n\t\t\tOnHash: true,\n\t\t\tPrefix: []any{\"doc:\"},\n\t\t},\n\t\t&redis.FieldSchema{\n\t\t\tFieldName: \"content\",\n\t\t\tFieldType: redis.SearchFieldTypeText,\n\t\t},\n\t\t&redis.FieldSchema{\n\t\t\tFieldName: \"genre\",\n\t\t\tFieldType: redis.SearchFieldTypeTag,\n\t\t},\n\t\t&redis.FieldSchema{\n\t\t\tFieldName: \"embedding\",\n\t\t\tFieldType: redis.SearchFieldTypeVector,\n\t\t\tVectorArgs: &redis.FTVectorArgs{\n\t\t\t\tHNSWOptions: &redis.FTHNSWOptions{\n\t\t\t\t\tDim:            384,\n\t\t\t\t\tDistanceMetric: \"L2\",\n\t\t\t\t\tType:           \"FLOAT32\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}","section_id":"create-the-index"},{"id":"create-an-embedder-instance-ex0","language":"go","code":"// Create a Hugot session\n\tsession, err := hugot.NewGoSession(ctx)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer func() {\n\t\terr := session.Destroy()\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}()\n\n\t// Download the model\n\tdownloadOptions := hugot.NewDownloadOptions()\n\tdownloadOptions.OnnxFilePath = \"onnx/model.onnx\" // Specify which ONNX file to use\n\tmodelPath, err := hugot.DownloadModel(ctx, \"sentence-transformers/all-MiniLM-L6-v2\", \"./models/\", downloadOptions)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Create feature extraction pipeline configuration\n\tconfig := hugot.FeatureExtractionConfig{\n\t\tModelPath: modelPath,\n\t\tName:      \"embeddingPipeline\",\n\t}\n\n\t// Create the feature extraction pipeline\n\tembeddingPipeline, err := hugot.NewPipeline(session, config)\n\tif err != nil {\n\t\tpanic(err)\n\t}","section_id":"create-an-embedder-instance"},{"id":"add-data-ex0","language":"go","code":"sentences := []string{\n\t\t\"That is a very happy person\",\n\t\t\"That is a happy dog\",\n\t\t\"Today is a sunny day\",\n\t}\n\n\ttags := []string{\n\t\t\"persons\", \"pets\", \"weather\",\n\t}\n\n\t// Generate embeddings using Hugot\n\tembeddingResult, err := embeddingPipeline.RunPipeline(ctx, sentences)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Extract the embeddings from the result\n\tembeddings := embeddingResult.Embeddings\n\n\tfor i, emb := range embeddings {\n\t\tbuffer := floatsToBytes(emb)\n\n\t\t_, err = rdb.HSet(ctx,\n\t\t\tfmt.Sprintf(\"doc:%v\", i),\n\t\t\tmap[string]any{\n\t\t\t\t\"content\":   sentences[i],\n\t\t\t\t\"genre\":     tags[i],\n\t\t\t\t\"embedding\": buffer,\n\t\t\t},\n\t\t).Result()\n\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}","section_id":"add-data"},{"id":"bulk-import-with-himport-ex0","language":"go","code":"err = rdb.HImportPrepare(ctx, \"doc\", \"content\", \"genre\", \"embedding\").Err()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tpipe := rdb.Pipeline()\n\n\tfor i, emb := range embeddings {\n\t\tpipe.HImportSet(ctx,\n\t\t\tfmt.Sprintf(\"doc:%v\", i),\n\t\t\t\"doc\",\n\t\t\tsentences[i], tags[i], floatsToBytes(emb),\n\t\t)\n\t}\n\n\tif _, err := pipe.Exec(ctx); err != nil {\n\t\tpanic(err)\n\t}","section_id":"bulk-import-with-himport"},{"id":"run-a-query-ex0","language":"go","code":"// Generate query embedding using Hugot\n\tqueryResult, err := embeddingPipeline.RunPipeline(ctx, []string{\n\t\t\"That is a happy person\",\n\t})\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tbuffer := floatsToBytes(queryResult.Embeddings[0])\n\n\tresults, err := rdb.FTSearchWithArgs(ctx,\n\t\t\"vector_idx\",\n\t\t\"*=>[KNN 3 @embedding $vec AS vector_distance]\",\n\t\t&redis.FTSearchOptions{\n\t\t\tReturn: []redis.FTSearchReturn{\n\t\t\t\t{FieldName: \"vector_distance\"},\n\t\t\t\t{FieldName: \"content\"},\n\t\t\t},\n\t\t\tDialectVersion: 2,\n\t\t\tParams: map[string]any{\n\t\t\t\t\"vec\": buffer,\n\t\t\t},\n\t\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfor _, doc := range results.Docs {\n\t\tfmt.Printf(\n\t\t\t\"ID: %v, Distance:%v, Content:'%v'\\n\",\n\t\t\tdoc.ID, doc.Fields[\"vector_distance\"], doc.Fields[\"content\"],\n\t\t)\n\t}","section_id":"run-a-query"},{"id":"run-a-query-ex1","language":"plaintext","code":"ID: doc:0, Distance:2.96992516518, Content:'That is a very happy person'\nID: doc:1, Distance:17.3678302765, Content:'That is a happy dog'\nID: doc:2, Distance:43.7771987915, Content:'Today is a sunny day'","section_id":"run-a-query"},{"id":"differences-with-json-documents-ex0","language":"go","code":"rdb.FTDropIndexWithArgs(ctx,\n\t\t\"vector_json_idx\",\n\t\t&redis.FTDropIndexOptions{\n\t\t\tDeleteDocs: true,\n\t\t},\n\t)\n\n\t_, err = rdb.FTCreate(ctx,\n\t\t\"vector_json_idx\",\n\t\t&redis.FTCreateOptions{\n\t\t\tOnJSON: true,\n\t\t\tPrefix: []any{\"jdoc:\"},\n\t\t},\n\t\t&redis.FieldSchema{\n\t\t\tFieldName: \"$.content\",\n\t\t\tAs:        \"content\",\n\t\t\tFieldType: redis.SearchFieldTypeText,\n\t\t},\n\t\t&redis.FieldSchema{\n\t\t\tFieldName: \"$.genre\",\n\t\t\tAs:        \"genre\",\n\t\t\tFieldType: redis.SearchFieldTypeTag,\n\t\t},\n\t\t&redis.FieldSchema{\n\t\t\tFieldName: \"$.embedding\",\n\t\t\tAs:        \"embedding\",\n\t\t\tFieldType: redis.SearchFieldTypeVector,\n\t\t\tVectorArgs: &redis.FTVectorArgs{\n\t\t\t\tHNSWOptions: &redis.FTHNSWOptions{\n\t\t\t\t\tDim:            384,\n\t\t\t\t\tDistanceMetric: \"L2\",\n\t\t\t\t\tType:           \"FLOAT32\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}","section_id":"differences-with-json-documents"},{"id":"differences-with-json-documents-ex1","language":"go","code":"for i, emb := range embeddings {\n\t\t_, err = rdb.JSONSet(ctx,\n\t\t\tfmt.Sprintf(\"jdoc:%v\", i),\n\t\t\t\"$\",\n\t\t\tmap[string]any{\n\t\t\t\t\"content\":   sentences[i],\n\t\t\t\t\"genre\":     tags[i],\n\t\t\t\t\"embedding\": emb,\n\t\t\t},\n\t\t).Result()\n\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}","section_id":"differences-with-json-documents"},{"id":"differences-with-json-documents-ex2","language":"go","code":"// Generate query embedding for JSON search using Hugot\n\tjsonQueryResult, err := embeddingPipeline.RunPipeline(ctx, []string{\n\t\t\"That is a happy person\",\n\t})\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tjsonBuffer := floatsToBytes(jsonQueryResult.Embeddings[0])\n\n\tjsonResults, err := rdb.FTSearchWithArgs(ctx,\n\t\t\"vector_json_idx\",\n\t\t\"*=>[KNN 3 @embedding $vec AS vector_distance]\",\n\t\t&redis.FTSearchOptions{\n\t\t\tReturn: []redis.FTSearchReturn{\n\t\t\t\t{FieldName: \"vector_distance\"},\n\t\t\t\t{FieldName: \"content\"},\n\t\t\t},\n\t\t\tDialectVersion: 2,\n\t\t\tParams: map[string]any{\n\t\t\t\t\"vec\": jsonBuffer,\n\t\t\t},\n\t\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfor _, doc := range jsonResults.Docs {\n\t\tfmt.Printf(\n\t\t\t\"ID: %v, Distance:%v, Content:'%v'\\n\",\n\t\t\tdoc.ID, doc.Fields[\"vector_distance\"], doc.Fields[\"content\"],\n\t\t)\n\t}","section_id":"differences-with-json-documents"},{"id":"differences-with-json-documents-ex3","language":"plaintext","code":"ID: jdoc:0, Distance:2.96992516518, Content:'That is a very happy person'\nID: jdoc:1, Distance:17.3678302765, Content:'That is a happy dog'\nID: jdoc:2, Distance:43.7771987915, Content:'Today is a sunny day'","section_id":"differences-with-json-documents"}]}
{"schema_version":2,"id":"develop/clients/go/vecsets","title":"Vector set embeddings","url":"https://redis.io/docs/latest/develop/clients/go/vecsets/","summary":"Index and query embeddings with Redis vector sets","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-09T09:42:14+01:00","page_type":"content","content_hash":"3db91fea74512b9e59cda76cec3c96cb5b11bac5d9b09ec55d0a3d7ef733fa1d","sections":[{"id":"initialize","title":"Initialize","role":"content","text":"Start by [installing](https://redis.io/docs/latest/develop/clients/go#install) `go-redis` if you haven't already done so. Note that you need `go-redis`\n[v9.10.0](https://github.com/redis/go-redis/releases/tag/v9.10.0)\nor later to use vector sets.\n\nAlso, install `hugot`:\n\n[code example]\n\nIn a new Go file, add the required imports:\n\nFoundational: Import required packages for vector set operations and text embeddings\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nThese include the `Hugot` library to generate an embedding from a section of text.\nThis example uses an instance of the `SentenceTransformer` model\n[`all-MiniLM-L6-v2`](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2)\nfor the embeddings. This model generates vectors with 384 dimensions, regardless\nof the length of the input text, but note that the input is truncated to 256\ntokens (see\n[Word piece tokenization](https://huggingface.co/learn/nlp-course/en/chapter6/6)\nat the [Hugging Face](https://huggingface.co/) docs to learn more about the way tokens\nare related to the original text).\n\nPractical pattern: Initialize a sentence transformer model for generating text embeddings\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]"},{"id":"create-the-data","title":"Create the data","role":"content","text":"The example data is contained in a map with some brief\ndescriptions of famous people:\n\nFoundational: Create sample data with descriptions of famous people for vector set storage\n\n**Difficulty:** Beginner\n\n**Available in:** Go\n\n##### Go\n\n[code example]"},{"id":"add-the-data-to-a-vector-set","title":"Add the data to a vector set","role":"content","text":"The next step is to connect to Redis and add the data to a new vector set.\n\nThe code below iterates through the `peopleData` map and adds corresponding\nelements to a vector set called `famousPeople`.\n\nUse the\n`RunPipeline()` method of `embeddingPipeline` to generate the\nembedding as an array of `float32` values, then use a loop like the one\nshown below to convert the `float32` array to a `float64` array.\nYou can then pass this array to the\n[`VAdd()`](https://redis.io/docs/latest/commands/vadd) command to set the embedding.\n\nThe call to `VAdd()` also adds the `born` and `died` values from the\noriginal map as attribute data. You can access this during a query\nor by using the [`VGetAttr()`](https://redis.io/docs/latest/commands/vgetattr) method.\n\nFoundational: Generate embeddings and add data to a vector set with VAdd including attribute metadata\n\n**Difficulty:** Intermediate\n\n**Available in:** Go\n\n##### Go\n\n[code example]"},{"id":"query-the-vector-set","title":"Query the vector set","role":"content","text":"You can now query the data in the set. The basic approach is to use the\n`RunPipeline()` method to generate another embedding vector for the query text.\n(This is the same method used to add the elements to the set.) Then, pass\nthe query vector to [`VSim()`](https://redis.io/docs/latest/commands/vsim) to return elements\nof the set, ranked in order of similarity to the query.\n\nStart with a simple query for \"actors\":\n\nVector similarity search: Find semantically similar items in a vector set using VSIM command\n\n**Difficulty:** Intermediate\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nThis returns the following list of elements (formatted slightly for clarity):\n\n[code example]\n\nThe first two people in the list are the two actors, as expected, but none of the\npeople from Linus Pauling onward was especially well-known for acting (and there certainly\nisn't any information about that in the short description text).\nAs it stands, the search attempts to rank all the elements in the set, based\non the information contained in the embedding model.\nYou can use the `Count` parameter of `VSimWithArgs()` to limit the list of elements\nto just the most relevant few items:\n\nVector similarity search with limits: Restrict results to the top K most similar items using the count parameter\n\n**Difficulty:** Intermediate\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nThe reason for using text embeddings rather than simple text search\nis that the embeddings represent semantic information. This allows a query\nto find elements with a similar meaning even if the text is\ndifferent. For example, the word \"entertainer\" doesn't appear in any of the\ndescriptions but if you use it as a query, the actors and musicians are ranked\nhighest in the results list:\n\nSemantic search: Leverage text embeddings to find semantically similar items even when exact keywords don't match\n\n**Difficulty:** Intermediate\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nSimilarly, if you use \"science\" as a query, you get the following results:\n\n[code example]\n\nThe scientists are ranked highest but they are then followed by the\nmathematicians. This seems reasonable given the connection between mathematics\nand science.\n\nYou can also use\n[filter expressions](https://redis.io/docs/latest/develop/data-types/vector-sets/filtered-search)\nwith `VSimWithArgs()` to restrict the search further. For example,\nrepeat the \"science\" query, but this time limit the results to people\nwho died before the year 2000:\n\nFiltered vector search: Combine vector similarity with attribute filters to narrow results based on metadata conditions\n\n**Difficulty:** Advanced\n\n**Available in:** Go\n\n##### Go\n\n[code example]\n\n\n\nNote that the boolean filter expression is applied to items in the list\nbefore the vector distance calculation is performed. Items that don't\npass the filter test are removed from the results completely, rather\nthan just reduced in rank. This can help to improve the performance of the\nsearch because there is no need to calculate the vector distance for\nelements that have already been filtered out of the search."},{"id":"more-information","title":"More information","role":"content","text":"See the [vector sets](https://redis.io/docs/latest/develop/data-types/vector-sets)\ndocs for more information and code examples. See the\n[Redis for AI](https://redis.io/docs/latest/develop/ai) section for more details\nabout text embeddings and other AI techniques you can use with Redis.\n\nYou may also be interested in\n[vector search](https://redis.io/docs/latest/develop/clients/go/vecsearch).\nThis is a feature of\n[Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query)\nthat lets you retrieve\n[JSON](https://redis.io/docs/latest/develop/data-types/json) and\n[hash](https://redis.io/docs/latest/develop/data-types/hashes) documents based on\nvector data stored in their fields."}],"examples":[{"id":"initialize-ex0","language":"bash","code":"go get github.com/knights-analytics/hugot","section_id":"initialize"},{"id":"initialize-ex1","language":"go","code":"package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/knights-analytics/hugot\"\n\t\"github.com/redis/go-redis/v9\"\n)","section_id":"initialize"},{"id":"initialize-ex2","language":"go","code":"// Create a new Hugot session\n\tsession, err := hugot.NewGoSession()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer func() {\n\t\terr := session.Destroy()\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}()\n\n\t// Download the model.\n\tdownloadOptions := hugot.NewDownloadOptions()\n\tdownloadOptions.OnnxFilePath = \"onnx/model.onnx\"\n\tmodelPath, err := hugot.DownloadModel(\n\t\t\"sentence-transformers/all-MiniLM-L6-v2\",\n\t\t\"./models/\",\n\t\tdownloadOptions,\n\t)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Create feature extraction pipeline configuration\n\tconfig := hugot.FeatureExtractionConfig{\n\t\tModelPath: modelPath,\n\t\tName:      \"embeddingPipeline\",\n\t}\n\n\t// Create the feature extraction pipeline\n\tembeddingPipeline, err := hugot.NewPipeline(session, config)\n\tif err != nil {\n\t\tpanic(err)\n\t}","section_id":"initialize"},{"id":"create-the-data-ex0","language":"go","code":"type PersonData struct {\n\tBorn        int\n\tDied        int\n\tDescription string\n}\n\nvar peopleData = map[string]PersonData{\n\t\"Marie Curie\": {\n\t\tBorn: 1867, Died: 1934,\n\t\tDescription: `Polish-French chemist and physicist. The only person ever to win\n\t\ttwo Nobel prizes for two different sciences.\n\t\t`,\n\t},\n\t\"Linus Pauling\": {\n\t\tBorn: 1901, Died: 1994,\n\t\tDescription: `American chemist and peace activist. One of only two people to win two\n\t\tNobel prizes in different fields (chemistry and peace).\n\t\t`,\n\t},\n\t\"Freddie Mercury\": {\n\t\tBorn: 1946, Died: 1991,\n\t\tDescription: `British musician, best known as the lead singer of the rock band\n\t\tQueen.\n\t\t`,\n\t},\n\t\"Marie Fredriksson\": {\n\t\tBorn: 1958, Died: 2019,\n\t\tDescription: `Swedish multi-instrumentalist, mainly known as the lead singer and\n\t\tkeyboardist of the band Roxette.\n\t\t`,\n\t},\n\t\"Paul Erdos\": {\n\t\tBorn: 1913, Died: 1996,\n\t\tDescription: `Hungarian mathematician, known for his eccentric personality almost\n\t\tas much as his contributions to many different fields of mathematics.\n\t\t`,\n\t},\n\t\"Maryam Mirzakhani\": {\n\t\tBorn: 1977, Died: 2017,\n\t\tDescription: `Iranian mathematician. The first woman ever to win the Fields medal\n\t\tfor her contributions to mathematics.\n\t\t`,\n\t},\n\t\"Masako Natsume\": {\n\t\tBorn: 1957, Died: 1985,\n\t\tDescription: `Japanese actress. She was very famous in Japan but was primarily\n\t\tknown elsewhere in the world for her portrayal of Tripitaka in the\n\t\tTV series Monkey.\n\t\t`,\n\t},\n\t\"Chaim Topol\": {\n\t\tBorn: 1935, Died: 2023,\n\t\tDescription: `Israeli actor and singer, usually credited simply as 'Topol'. He was\n\t\tbest known for his many appearances as Tevye in the musical Fiddler\n\t\ton the Roof.\n\t\t`,\n\t},\n}","section_id":"create-the-data"},{"id":"add-the-data-to-a-vector-set-ex0","language":"go","code":"for name, details := range peopleData {\n\t\t// Generate embeddings using Hugot\n\t\tresult, err := embeddingPipeline.RunPipeline([]string{details.Description})\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\n\t\t// Convert embedding to float64 slice\n\t\tembFloat32 := result.Embeddings[0]\n\t\tembFloat64 := make([]float64, len(embFloat32))\n\t\tfor i, v := range embFloat32 {\n\t\t\tembFloat64[i] = float64(v)\n\t\t}\n\n\t\t// Add vector to vector set\n\t\t_, err = rdb.VAdd(ctx, \"famousPeople\", name, &redis.VectorValues{Val: embFloat64}).Result()\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\n\t\t// Set attributes for the element\n\t\t_, err = rdb.VSetAttr(ctx, \"famousPeople\", name, map[string]interface{}{\n\t\t\t\"born\": details.Born,\n\t\t\t\"died\": details.Died,\n\t\t}).Result()\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}","section_id":"add-the-data-to-a-vector-set"},{"id":"query-the-vector-set-ex0","language":"go","code":"queryValue := \"actors\"\n\n\tqueryResult, err := embeddingPipeline.RunPipeline([]string{queryValue})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Convert embedding to float64 slice\n\tqueryFloat32 := queryResult.Embeddings[0]\n\tqueryFloat64 := make([]float64, len(queryFloat32))\n\tfor i, v := range queryFloat32 {\n\t\tqueryFloat64[i] = float64(v)\n\t}\n\n\tactorsResults, err := rdb.VSim(ctx, \"famousPeople\", &redis.VectorValues{Val: queryFloat64}).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Printf(\"'actors': %v\\n\", strings.Join(actorsResults, \", \"))\n\t// >>> 'actors': Masako Natsume, Chaim Topol, Linus Pauling,\n\t// Marie Fredriksson, Maryam Mirzakhani, Marie Curie, Freddie Mercury,\n\t// Paul Erdos","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex1","language":"plaintext","code":"'actors': Masako Natsume, Chaim Topol, Linus Pauling, Marie Fredriksson,\n    Maryam Mirzakhani, Marie Curie, Freddie Mercury, Paul Erdos","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex2","language":"go","code":"queryValue = \"actors\"\n\n\tqueryResult, err = embeddingPipeline.RunPipeline([]string{queryValue})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Convert embedding to float64 slice\n\tqueryFloat32 = queryResult.Embeddings[0]\n\tqueryFloat64 = make([]float64, len(queryFloat32))\n\tfor i, v := range queryFloat32 {\n\t\tqueryFloat64[i] = float64(v)\n\t}\n\n\ttwoActorsResults, err := rdb.VSimWithArgs(ctx, \"famousPeople\",\n\t\t&redis.VectorValues{Val: queryFloat64},\n\t\t&redis.VSimArgs{Count: 2}).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Printf(\"'actors (2)': %v\\n\", strings.Join(twoActorsResults, \", \"))\n\t// >>> 'actors (2)': Masako Natsume, Chaim Topol","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex3","language":"go","code":"queryValue = \"entertainer\"\n\n\tqueryResult, err = embeddingPipeline.RunPipeline([]string{queryValue})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Convert embedding to float64 slice\n\tqueryFloat32 = queryResult.Embeddings[0]\n\tqueryFloat64 = make([]float64, len(queryFloat32))\n\tfor i, v := range queryFloat32 {\n\t\tqueryFloat64[i] = float64(v)\n\t}\n\n\tentertainerResults, err := rdb.VSim(ctx, \"famousPeople\", &redis.VectorValues{Val: queryFloat64}).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Printf(\"'entertainer': %v\\n\", strings.Join(entertainerResults, \", \"))\n\t// >>> 'entertainer': Chaim Topol, Freddie Mercury, Marie Fredriksson,\n\t// Linus Pauling, Masako Natsume, Paul Erdos,\n\t// Maryam Mirzakhani, Marie Curie","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex4","language":"plaintext","code":"'science': Marie Curie, Linus Pauling, Maryam Mirzakhani, Paul Erdos,\nMarie Fredriksson, Freddie Mercury, Masako Natsume, Chaim Topol","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex5","language":"go","code":"queryValue = \"science\"\n\n\tqueryResult, err = embeddingPipeline.RunPipeline([]string{queryValue})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Convert embedding to float64 slice\n\tqueryFloat32 = queryResult.Embeddings[0]\n\tqueryFloat64 = make([]float64, len(queryFloat32))\n\tfor i, v := range queryFloat32 {\n\t\tqueryFloat64[i] = float64(v)\n\t}\n\n\tscience2000Results, err := rdb.VSimWithArgs(ctx, \"famousPeople\",\n\t\t&redis.VectorValues{Val: queryFloat64},\n\t\t&redis.VSimArgs{Filter: \".died < 2000\"}).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Printf(\"'science2000': %v\\n\", strings.Join(science2000Results, \", \"))\n\t// >>> 'science2000': Marie Curie, Linus Pauling, Paul Erdos, Freddie Mercury,\n\t// Masako Natsume","section_id":"query-the-vector-set"}]}
{"schema_version":2,"id":"develop/clients/hiredis/connect","title":"Connect","url":"https://redis.io/docs/latest/develop/clients/hiredis/connect/","summary":"Connect to the server with `hiredis`.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-12T14:34:35+01:00","page_type":"content","content_hash":"2a7f71daf5edf5f600622ab627a4b2e1e04b3883818f98c7e8908ef62fc9b5d0","sections":[{"id":"basic-synchronous-connection","title":"Basic synchronous connection","role":"overview","text":"The example below creates a simple synchronous connection to a local\nRedis server and tests the connection, before closing it with\n`redisFree()`. The `redisConnect()` function takes just a hostname\nand port as its arguments, and returns a context object.\n\n[code example]"},{"id":"asynchronous-connection","title":"Asynchronous connection","role":"content","text":"You can also connect to Redis using an asynchronous API.\nThe `redisAsyncConnect()` call that creates the context is\nsimilar to the synchronous function `redisConnect()`, but it returns the\ncontext object immediately before the connection is complete.\nIt lets you supply callbacks to respond when a connection is successful\nor to handle any errors that may occur.\n\nThe following code creates an asynchronous connection and\nsets the context callbacks. Note that you must also include the\n`async.h` header to access the asynchronous API.\n\n[code example]\n\nThe callback functions have a simple signature that receives\nthe context object and a status code. See\n[Handling errors](https://redis.io/docs/latest/develop/clients/hiredis/handle-replies#handling-errors)\nfor a list of the possible status codes.\n\n[code example]\n\nUse the `redisAsyncCommand()` function to issue Redis commands\nwith an asynchronous connection. This is similar to the equivalent\nsynchronous function `redisCommand()` but also lets you supply a callback\nand a custom data pointer to process the response to the command. See\n[Construct asynchronous commands](https://redis.io/docs/latest/develop/clients/hiredis/issue-commands#construct-asynchronous-commands) for more\ninformation.\n\nNote that you should normally disconnect asynchronously from a\ncallback when you have finished using the connection.\nUse `redisAsyncDisconnect()` to disconnect gracefully, letting\npending commands execute and activate their callbacks.\nUse `redisAsyncFree()` to disconnect immediately. If you do this then\nany pending callbacks from commands that have already executed will be\ncalled with a `NULL` reply pointer."},{"id":"redis-sentinel","title":"Redis Sentinel","role":"content","text":"`hiredis` doesn't support [Redis Sentinel](https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel).\nIt has no API to find the current primary from a set of Sentinels or to reconnect after a\nfailover."}],"examples":[{"id":"basic-synchronous-connection-ex0","language":"c","code":"#include <stdio.h>\n#include <stdlib.h>\n\n#include <hiredis/hiredis.h>\n    .\n    .\n    .\n\n// The `redisContext` type represents the connection\n// to the Redis server. Here, we connect to the\n// default host and port.\nredisContext *c = redisConnect(\"127.0.0.1\", 6379);\n\n// Check if the context is null or if a specific\n// error occurred.\nif (c == NULL || c->err) {\n    if (c != NULL) {\n        printf(\"Error: %s\\n\", c->errstr);\n        // handle error\n    } else {\n        printf(\"Can't allocate redis context\\n\");\n    }\n\n    exit(1);\n}\n\n// Set a string key.\nredisReply *reply = redisCommand(c, \"SET foo bar\");\nprintf(\"Reply: %s\\n\", reply->str); // >>> Reply: OK\nfreeReplyObject(reply);\n\n// Get the key we have just stored.\nreply = redisCommand(c, \"GET foo\");\nprintf(\"Reply: %s\\n\", reply->str); // >>> Reply: bar\nfreeReplyObject(reply);\n\n// Close the connection.\nredisFree(c);","section_id":"basic-synchronous-connection"},{"id":"asynchronous-connection-ex0","language":"c","code":"#include <stdio.h>\n#include <stdlib.h>\n\n#include <hiredis/hiredis.h>\n#include <hiredis/async.h>\n    .\n    .\n    .\n\nredisAsyncContext *c = redisAsyncConnect(\"127.0.0.1\", 6379);\n\nif (c->err) {\n    printf(\"Error: %s\\n\", c->errstr);\n    return 1;\n}\n\n// Set callbacks to respond to successful or unsuccessful\n// connection and disconnection.\nredisAsyncSetConnectCallback(c, connectCallback);\nredisAsyncSetDisconnectCallback(c, disconnectCallback);\n\nchar *key = \"testkey\";\nchar *value = \"testvalue\";\n\n// Status reply is ignored.\nredisAsyncCommand(c, NULL, NULL, \"SET %s %s\", key, value);\n\n// Reply handled by `getCallback()` function.\nredisAsyncCommand(c, getCallback, key, \"GET %s\", key);","section_id":"asynchronous-connection"},{"id":"asynchronous-connection-ex1","language":"c","code":"void connectCallback(const redisAsyncContext *c, int status) {\n    if (status != REDIS_OK) {\n        printf(\"Error: %s\\n\", c->errstr);\n        return;\n    }\n    printf(\"Connected...\\n\");\n}\n\nvoid disconnectCallback(const redisAsyncContext *c, int status) {\n    if (status != REDIS_OK) {\n        printf(\"Error: %s\\n\", c->errstr);\n        return;\n    }\n    printf(\"Disconnected...\\n\");\n}","section_id":"asynchronous-connection"}]}
{"schema_version":2,"id":"develop/clients/hiredis/handle-replies","title":"Handle command replies","url":"https://redis.io/docs/latest/develop/clients/hiredis/handle-replies/","summary":"Handle command replies with `hiredis`.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-09T11:06:49+01:00","page_type":"content","content_hash":"608bbe726f3074b28f7d8c194774d537b8e0ec3cfee3623ec6c97bc9b159159c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `redisCommand()` and `redisCommandArgv()` functions return\na pointer to a `redisReply` object when you issue a command (see\n[Issue commands](https://redis.io/docs/latest/develop/clients/hiredis/issue-commands)\nfor more information). This type supports all\nreply formats defined in the\n[RESP2 and RESP3](https://redis.io/docs/latest/develop/reference/protocol-spec#resp-protocol-description)\nprotocols, so its content varies greatly between calls.\n\nA simple example is the status response returned by the [`SET`](https://redis.io/docs/latest/commands/set)\ncommand. The code below shows how to get this from the `redisReply`\nobject:\n\n[code example]\n\nA null reply indicates an error, so you should always check for this.\nIf an error does occur, then the `redisContext` object will have a\nnon-zero error number in its integer `err` field and a textual\ndescription of the error in its `errstr` field.\n\nFor `SET`, a successful call will simply return an \"OK\" string that you\ncan access with the `reply->str` field. The code in the example prints\nthis to the console, but you should check for the specific value to ensure\nthe command executed correctly.\n\nThe `redisCommand()` call allocates memory for the reply, so you should\nalways free it using `freeReplyObject()` when you have finished using\nthe reply. If you want to reuse the reply variable then it is wise to\nset it to `NULL` after you free it, so that you don't accidentally use\nthe stale pointer later."},{"id":"reply-formats","title":"Reply formats","role":"content","text":"The Redis\n[`RESP`](https://redis.io/docs/latest/develop/reference/protocol-spec#resp-protocol-description)\nprotocols support several different reply formats for commands.\n\nYou can find the reply format for a command at the end of its\nreference page in the RESP2/RESP3 Reply section (for example, the\n[`INCRBY`](https://redis.io/docs/latest/commands/incrby) page shows that the\ncommand has an integer result). You can also determine the format\nusing the `type` field of the reply object. This contains a\ndifferent integer value for each type. The `hiredis.h` header file\ndefines constants for all of these integer values (for example `REDIS_REPLY_STRING`).\n\nThe `redisReply` struct has several fields to contain different\ntypes of replies, with different fields being set depending on\nthe value of the `type` field. The table below shows the type\nconstants, the corresponding reply type, and the fields you can\nuse to access the reply value:\n\n| Constant | Type | Relevant fields of `redisReply` | RESP protocol |\n| :- | :- |:- | :- |\n| `REDIS_REPLY_STATUS` | [Simple string](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) | `reply->str`: the string value (`char*`)<br/> `reply->len`: the string length (`size_t`) | 2, 3 |\n| `REDIS_REPLY_ERROR` | [Simple error](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) | `reply->str`: the string value (`char*`)<br/> `reply->len`: the string length (`size_t`) | 2, 3 |\n| `REDIS_REPLY_INTEGER` | [Integer](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) | `reply->integer`: the integer value (`long long`)| 2, 3 |\n| `REDIS_REPLY_NIL` | [Null](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls) | No data | 2, 3 |\n| `REDIS_REPLY_STRING` | [Bulk string](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) |`reply->str`: the string value (`char*`)<br/> `reply->len`: the string length (`size_t`) | 2, 3 |\n| `REDIS_REPLY_ARRAY` | [Array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) | `reply->elements`: number of elements (`size_t`)<br/> `reply->element`: array elements (`redisReply`) | 2, 3 |\n| `REDIS_REPLY_DOUBLE` | [Double](https://redis.io/docs/latest/develop/reference/protocol-spec#doubles) | `reply->str`: double value as string (`char*`)<br/> `reply->len`: the string length (`size_t`) | 3 |\n| `REDIS_REPLY_BOOL` | [Boolean](https://redis.io/docs/latest/develop/reference/protocol-spec#booleans) | `reply->integer`: the boolean value, 0 or 1 (`long long`) | 3 |\n| `REDIS_REPLY_MAP` | [Map](https://redis.io/docs/latest/develop/reference/protocol-spec#maps) | `reply->elements`: number of elements (`size_t`)<br/> `reply->element`: array elements (`redisReply`) | 3 |\n| `REDIS_REPLY_SET` | [Set](https://redis.io/docs/latest/develop/reference/protocol-spec#sets) | `reply->elements`: number of elements (`size_t`)<br/> `reply->element`: array elements (`redisReply`) | 3 |\n| `REDIS_REPLY_PUSH` | [Push](https://redis.io/docs/latest/develop/reference/protocol-spec#pushes) | `reply->elements`: number of elements (`size_t`)<br/> `reply->element`: array elements (`redisReply`) | 3 |\n| `REDIS_REPLY_BIGNUM` | [Big number](https://redis.io/docs/latest/develop/reference/protocol-spec#big-numbers) | `reply->str`: number value as string (`char*`)<br/> `reply->len`: the string length (`size_t`) | 3 |\n| `REDIS_REPLY_VERB` | [Verbatim string](https://redis.io/docs/latest/develop/reference/protocol-spec#verbatim-strings) |`reply->str`: the string value (`char*`)<br/> `reply->len`: the string length (`size_t`)<br/> `reply->vtype`: content type (`char[3]`) | 3 |"},{"id":"reply-format-processing-examples","title":"Reply format processing examples","role":"content","text":"The sections below explain how to process specific reply types in\nmore detail."},{"id":"integers","title":"Integers","role":"content","text":"The `REDIS_REPLY_INTEGER` and `REDIS_REPLY_BOOL` reply types both\ncontain values in `reply->integer`. However, `REDIS_REPLY_BOOL` is\nrarely used. Even when the command essentially returns a boolean value,\nthe reply is usually reported as an integer. \n\n[code example]"},{"id":"strings","title":"Strings","role":"content","text":"The `REDIS_REPLY_STATUS`, `REDIS_REPLY_ERROR`, `REDIS_REPLY_STRING`,\n`REDIS_REPLY_DOUBLE`, `REDIS_REPLY_BIGNUM`, and `REDIS_REPLY_VERB`\nare all returned as strings, with the main difference lying in how\nyou interpret them. For all these types, the string value is\nreturned in `reply->str` and the length of the string is in\n`reply->len`. The example below shows some of the possibilities.\n\n[code example]"},{"id":"arrays-and-maps","title":"Arrays and maps","role":"content","text":"Arrays (reply type `REDIS_REPLY_ARRAY`) and maps (reply type `REDIS_REPLY_MAP`)\nare returned by commands that retrieve several values at the\nsame time. For both types, the number of elements in the reply is contained in\n`reply->elements` and the pointer to the array itself is `reply->element`.\nEach item in the array is of type `redisReply`. The array elements\nare typically simple types rather than arrays or maps.\n\nThe example below shows how to get the items from a\n[list](https://redis.io/docs/latest/develop/data-types/lists):\n\n[code example]\n\nA map is essentially the same as an array but it has the extra\nguarantee that the items will be listed in key-value pairs.\nThe example below shows how to get all the fields from a\n[hash](https://redis.io/docs/latest/develop/data-types/hashes) using\n[`HGETALL`](https://redis.io/docs/latest/commands/hgetall):\n\n[code example]"},{"id":"handling-errors","title":"Handling errors","role":"content","text":"When a command executes successfully, the `err` field of the context\nobject will be set to zero. If a command fails, it will return either\n`NULL` or `REDIS_ERR`, depending on which function and command you used. When\nthis happens, `context->err` will contain an error code\n\n-   `REDIS_ERR_IO`: There was an I/O error while creating the connection,\n    or while trying to write or read data. Whenever `context->err` contains\n    `REDIS_ERR_IO`, you can use the features of the standard library file\n    [`errno.h`](https://en.wikipedia.org/wiki/Errno.h) to find out more\n    information about the error.\n-   `REDIS_ERR_EOF`: The server closed the connection which resulted in an empty read.\n-   `REDIS_ERR_PROTOCOL`: There was an error while parsing the\n    [RESP protocol](https://redis.io/docs/latest/develop/reference/protocol-spec).\n-   `REDIS_ERR_OTHER`: Any other error. Currently, it is only used when the connection\n    hostname can't be resolved.\n\nThe context object also has an `errstr` field that contains a descriptive error message."}],"examples":[{"id":"overview-ex0","language":"c","code":"redisReply *reply = redisCommand(c, \"SET greeting Hello\");\n\n// Check and free the reply.\nif (reply != NULL) {\n    printf(\"Reply: %s\\n\", reply->str);\n    freeReplyObject(reply);\n    reply = NULL;\n}","section_id":"overview"},{"id":"integers-ex0","language":"c","code":"// Add some values to a set.\nredisReply *reply = redisCommand(c, \"SADD items bread milk peas\");\n\nif (reply->type == REDIS_REPLY_INTEGER) {\n    // Report status.\n    printf(\"Integer reply\\n\");\n    printf(\"Number added: %lld\\n\", reply->integer);\n    // >>> Number added: 3\n}\n\nfreeReplyObject(reply);\nreply = NULL;\n\n\nreply = redisCommand(c, \"SISMEMBER items bread\");\n\n// This also gives an integer reply but you should interpret\n// it as a boolean value.\nif (reply->type == REDIS_REPLY_INTEGER) {\n    // Respond to boolean integer value.\n    printf(\"Integer reply\\n\");\n    \n    if (reply->integer == 0) {\n        printf(\"Items set has no member 'bread'\\n\");\n    } else {\n        printf(\"'Bread' is a member of items set\\n\");\n    }\n    // >>> 'Bread' is a member of items set\n}\n\nfreeReplyObject(reply);\nreply = NULL;","section_id":"integers"},{"id":"strings-ex0","language":"c","code":"// Set a numeric value in a string.\nreply = redisCommand(c, \"SET number 1.5\");\n\n// This gives a status reply.\nif (reply->type == REDIS_REPLY_STATUS) {\n    // Report status.\n    printf(\"Status reply\\n\");\n    printf(\"Reply: %s\\n\", reply->str); // >>> Reply: OK\n}\n\nfreeReplyObject(reply);\nreply = NULL;\n\n\n// Attempt to interpret the key as a hash.\nreply = redisCommand(c, \"HGET number field1\");\n\n// This gives an error reply.\nif (reply->type == REDIS_REPLY_ERROR) {\n    // Report the error.\n    printf(\"Error reply\\n\");\n    printf(\"Reply: %s\\n\", reply->str);\n    // >>> Reply: WRONGTYPE Operation against a key holding the wrong kind of value\n}\n\nfreeReplyObject(reply);\nreply = NULL;\n\n\nreply = redisCommand(c, \"GET number\");\n\n// This gives a simple string reply.\nif (reply->type == REDIS_REPLY_STRING) {\n    // Display the string.\n    printf(\"Simple string reply\\n\");\n    printf(\"Reply: %s\\n\", reply->str); // >>> Reply: 1.5\n}\n\nfreeReplyObject(reply);\nreply = NULL;\n\n\nreply = redisCommand(c, \"ZADD prices 1.75 bread 5.99 beer\");\n\n// This gives an integer reply.\nif (reply->type == REDIS_REPLY_INTEGER) {\n    // Display the integer.\n    printf(\"Integer reply\\n\");\n    printf(\"Number added: %lld\\n\", reply->integer);\n    // >>> Number added: 2\n}\n\nfreeReplyObject(reply);\nreply = NULL;\n\n\nreply = redisCommand(c, \"ZSCORE prices bread\");\n\n// This gives a string reply with RESP2 and a double reply\n// with RESP3, but you handle it the same way in either case.\nif (reply->type == REDIS_REPLY_STRING) {\n    printf(\"String reply\\n\");\n    \n    char *endptr; // Not used.\n    double price = strtod(reply->str, &endptr);\n    double discounted = price * 0.75;\n    printf(\"Discounted price: %.2f\\n\", discounted);\n    // >>> Discounted price: 1.31\n}\n\nfreeReplyObject(reply);\nreply = NULL;","section_id":"strings"},{"id":"arrays-and-maps-ex0","language":"c","code":"reply = redisCommand(c, \"RPUSH things thing0 thing1 thing2 thing3\");\n\nprintf(\"Added %lld items\\n\", reply->integer);\n// >>> Added 4 items\nfreeReplyObject(reply);\nreply = NULL;\n\n\nreply = redisCommand(c, \"LRANGE things 0 -1\");\n\nfor (int i = 0; i < reply->elements; ++i) {\n    if (reply->element[i]->type == REDIS_REPLY_STRING) {\n        printf(\"List item %d: %s\\n\", i, reply->element[i]->str);\n    }\n}\n// >>> List item 0: thing0\n// >>> List item 1: thing1\n// >>> List item 2: thing2\n// >>> List item 3: thing3","section_id":"arrays-and-maps"},{"id":"arrays-and-maps-ex1","language":"c","code":"const char *hashCommand[] = {\n    \"HSET\", \"details\",\n    \"name\", \"Mr Benn\",\n    \"address\", \"52 Festive Road\",\n    \"hobbies\", \"Cosplay\"\n};\n\nreply = redisCommandArgv(c, 8, hashCommand, NULL);\n\nprintf(\"Added %lld fields\\n\", reply->integer);\n// >>> Added 3 fields\n\nfreeReplyObject(reply);\nreply = NULL;\n\n\nreply = redisCommand(c, \"HGETALL details\");\n\n// This gives an array reply with RESP2 and a map reply with\n// RESP3, but you handle it the same way in either case.\nif (reply->type == REDIS_REPLY_ARRAY) {        \n    for (int i = 0; i < reply->elements; i += 2) {\n        char *key = reply->element[i]->str;\n        char *value = reply->element[i + 1]->str;\n        printf(\"Key: %s, value: %s\\n\", key, value);\n    }\n    // >>> Key: name, value: Mr Benn\n    // >>> Key: address, value: 52 Festive Road\n    // >>> Key: hobbies, value: Cosplay\n}","section_id":"arrays-and-maps"}]}
{"schema_version":2,"id":"develop/clients/hiredis","title":"hiredis guide (C)","url":"https://redis.io/docs/latest/develop/clients/hiredis/","summary":"Connect your C application to a Redis database.","aliases":["/connect/clients/hiredis/","/clients/hiredis/","/develop/clients/c"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-09T11:06:49+01:00","children":[{"id":"develop/clients/hiredis/connect","summary":"Connect to the server with `hiredis`.","title":"Connect","url":"https://redis.io/docs/latest/develop/clients/hiredis/connect/"},{"id":"develop/clients/hiredis/issue-commands","summary":"Construct commands and send them to the Redis server.","title":"Issue commands","url":"https://redis.io/docs/latest/develop/clients/hiredis/issue-commands/"},{"id":"develop/clients/hiredis/handle-replies","summary":"Handle command replies with `hiredis`.","title":"Handle command replies","url":"https://redis.io/docs/latest/develop/clients/hiredis/handle-replies/"},{"id":"develop/clients/hiredis/transpipe","summary":"Learn how to use Redis pipelines and transactions","title":"Pipelines and transactions","url":"https://redis.io/docs/latest/develop/clients/hiredis/transpipe/"},{"id":"develop/clients/hiredis/int-examples","summary":"Integrate hiredis with C++ and external frameworks.","title":"Integration guides","url":"https://redis.io/docs/latest/develop/clients/hiredis/int-examples/"}],"page_type":"content","content_hash":"c07c16c615d2e385874aeee0601a52fa945195662f493c7eb3286ec1ad5b6c0b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[`hiredis`](https://github.com/redis/hiredis) is the\n[C language](https://en.wikipedia.org/wiki/C_(programming_language))\nclient for Redis.\nThe sections below explain how to install `hiredis` and connect your application\nto a Redis database.\n\n`hiredis` requires a running Redis or [Redis Stack](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack) server. See [Getting started](https://redis.io/docs/latest/operate/oss_and_stack/install) for Redis installation instructions."},{"id":"build-and-install","title":"Build and install","role":"content","text":"Clone or download the `hiredis` source from the [Github repository](https://github.com/redis/hiredis).\nThen, in a terminal, go into the `hiredis` folder and run the `make` command to build\nthe dynamically-loaded library for `hiredis` (this has the name `libhiredis.dylib` on\nMacOS and `libhiredis.so` on Linux). You can copy this library to your\nproject folder or run `sudo make install` to install it to `/usr/local/lib`."},{"id":"connect-and-test","title":"Connect and test","role":"content","text":"The code in the example below connects to the server, stores and retrieves\na string key using [`SET`](https://redis.io/docs/latest/commands/set) and\n[`GET`](https://redis.io/docs/latest/commands/get), and then finally closes the\nconnection. An explanation of the code follows the example.\n\nFoundational: Connect to a Redis server, set and retrieve string values using SET and GET, then close the connection\n\n[code example]\n\n\n\nFor a real project, you would build your code with a makefile, but for\nthis simple test, you can just place it in a file called `main.c` and\nbuild it with the following command. (If you didn't install `hiredis`\nusing `make install`, then you should also use the `-I` option to\nspecify the folder that contains the `hiredis` headers.)\n\n[code example]\n\nThe default executable filename is `a.out`. If you run this file from\nthe terminal, you should see the following output:\n\n[code example]\n\nThe code first uses `redisConnect()` to open the connection for\nall subsequent commands to use. See\n[Connect](https://redis.io/docs/latest/develop/clients/hiredis/connect) for\nmore information about connecting to Redis.\n\nThe `redisCommand()` function\nissues commands to the server, each of which returns a\n`redisReply` pointer. Here, the reply is a string, which you can\naccess using the `str` field of the reply. The `redisCommand()`\ncall allocates memory for the reply, so you should free this\nwith `freeReplyObject()` when you have finished using it.\nSee [Issue commands](https://redis.io/docs/latest/develop/clients/hiredis/issue-commands)\nand [Handle replies](https://redis.io/docs/latest/develop/clients/hiredis/handle-replies)\nfor more information.\n\nFinally, you should close the connection to Redis with a\ncall to `redisFree()`. This is not strictly necessary\nfor this short test program, but real-world code will typically\nopen and use many connections. You must free them after using them\nto prevent errors."},{"id":"more-information","title":"More information","role":"content","text":"The [`hiredis`](https://github.com/redis/hiredis) Github repository contains\nexamples and details that may be useful if you are using `hiredis` to\nimplement a higher-level client for another programming language. There are\nalso examples showing how to use `hiredis` adapter headers to integrate with\nvarious event handling frameworks.\n\nSee the other pages in this section for more information and examples."}],"examples":[{"id":"connect-and-test-ex0","language":"c","code":"// The following comment is required to make the example interactive.\n//%cflags:-lhiredis\n#include <stdio.h>\n#include <stdlib.h>\n\n#include <hiredis/hiredis.h>\n\n\nint main() {\n    // The `redisContext` type represents the connection\n    // to the Redis server. Here, we connect to the\n    // default host and port.\n    redisContext *c = redisConnect(\"127.0.0.1\", 6379);\n\n    // Check if the context is null or if a specific\n    // error occurred.\n    if (c == NULL || c->err) {\n        if (c != NULL) {\n            printf(\"Error: %s\\n\", c->errstr);\n            // handle error\n        } else {\n            printf(\"Can't allocate redis context\\n\");\n        }\n\n        exit(1);\n    }\n\n    // Set a string key.\n    redisReply *reply = redisCommand(c, \"SET foo bar\");\n    printf(\"Reply: %s\\n\", reply->str);\n    freeReplyObject(reply);\n\n    // Get the key we have just stored.\n    reply = redisCommand(c, \"GET foo\");\n    printf(\"Reply: %s\\n\", reply->str);\n    freeReplyObject(reply);\n\n    // Close the connection.\n    redisFree(c);\n}","section_id":"connect-and-test"},{"id":"connect-and-test-ex1","language":"bash","code":"cc main.c -L/usr/local/lib -lhiredis","section_id":"connect-and-test"},{"id":"connect-and-test-ex2","language":"plaintext","code":"% ./a.out                             \nReply: OK\nReply: bar","section_id":"connect-and-test"}]}
{"schema_version":2,"id":"develop/clients/hiredis/int-examples","title":"Integration guides","url":"https://redis.io/docs/latest/develop/clients/hiredis/int-examples/","summary":"Integrate hiredis with C++ and external frameworks.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2025-04-03T13:13:55+01:00","children":[{"id":"develop/clients/hiredis/int-examples/qt-integration","summary":"Use `hiredis` in conjunction with the Qt app framework.","title":"Integrate hiredis with a Qt app","url":"https://redis.io/docs/latest/develop/clients/hiredis/int-examples/qt-integration/"},{"id":"develop/clients/hiredis/int-examples/libevent-integration","summary":"Use `hiredis` in conjunction with the `libevent` framework.","title":"Integrate hiredis with a libevent app","url":"https://redis.io/docs/latest/develop/clients/hiredis/int-examples/libevent-integration/"}],"page_type":"content","content_hash":"823e10647cce518ea0f3bc0dbfe58d7f15bbc25dfd68d5f2f61d3afab8440a78","sections":[{"id":"content","title":"Content","role":"content","text":"`hiredis` is compatible with C++ and the library source includes a set of\n[adapters](https://github.com/redis/hiredis/tree/master/adapters)\nto help you use it in conjunction with C and C++ libraries and frameworks.\nThe pages in this section explain how to integrate `hiredis` into\nyour app."}],"examples":[]}
{"schema_version":2,"id":"develop/clients/hiredis/int-examples/libevent-integration","title":"Integrate hiredis with a libevent app","url":"https://redis.io/docs/latest/develop/clients/hiredis/int-examples/libevent-integration/","summary":"Use `hiredis` in conjunction with the `libevent` framework.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-09T11:06:49+01:00","page_type":"content","content_hash":"64747af2bf9666eb1d60297cc46c36f65aaba41009cf788aa34c34b768b3b541","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The [`libevent`](https://libevent.org/) library provides an\nimplementation of an event loop that lets you call functions\nasynchronously in response to events. This guide explains\nhow to use `hiredis` to connect to a Redis server from a\n`libevent` app."},{"id":"install-libevent","title":"Install `libevent`","role":"setup","text":"The [`libevent` home page](https://libevent.org/) has links to download\nall versions of the library, but you should use the latest version\nunless there is a specific version you need to target.\n\nWhen you have downloaded `libevent`, follow the instructions in the\n`README` file to compile and install the library."},{"id":"create-a-simple-app","title":"Create a simple app","role":"content","text":"For a real project, you would build your code with a makefile, but for\nthis simple test, you can just place it in a file called `main.c` and\nbuild it with the following command (assuming you used `make install` to\ninstall the `libhiredis` and `libevent` libraries):\n\n[code example]\n\nSee [Build and install](https://redis.io/docs/latest/develop/clients/hiredis#build-and-install)\nto learn how to build `hiredis`, if you have not already done so.\n\nNow, add the following code in `main.c`. An explanation follows the\ncode example:\n\n[code example]\n\nThe code calls\n[`event_base_new()`](https://libevent.org/doc/event_8h.html#af34c025430d445427a2a5661082405c3)\nto initialize the core\n[`event_base`](https://libevent.org/doc/structevent__base.html)\nobject that manages the event loop. It then creates a standard\n[asynchronous connection](https://redis.io/docs/latest/develop/clients/hiredis/connect#asynchronous-connection)\nto Redis and uses the `libevent` adapter function `redisLibeventAttach()` to\nattach the connection to the event loop.\n\nAfter setting the [connection callbacks](https://redis.io/docs/latest/develop/clients/hiredis/connect#asynchronous-connection), the code issues two asynchronous\nRedis commands (see\n[Construct asynchronous commands](https://redis.io/docs/latest/develop/clients/hiredis/issue-commands#construct-asynchronous-commands)\nfor more information).\nThe final step is to call\n[`event_base_dispatch()`](https://libevent.org/doc/event_8h.html#a19d60cb72a1af398247f40e92cf07056)\nto start the event loop. This will wait for the commands to be processed and\nthen exit when the Redis connection is closed in the `getCallback()` function."},{"id":"run-the-code","title":"Run the code","role":"content","text":"If you compile and run the code, you will see the following output,\nshowing that the callbacks executed correctly:\n\n[code example]\n\nYou can use the\n[`KEYS`](https://redis.io/docs/latest/commands/keys) command from\n[`redis-cli`](https://redis.io/docs/latest/develop/tools/cli) or\n[Redis Insight](https://redis.io/docs/latest/develop/tools/insight) to check\nthat the \"testkey\" string key was added to the Redis database."}],"examples":[{"id":"create-a-simple-app-ex0","language":"bash","code":"cc main.c -L/usr/local/lib -lhiredis -levent","section_id":"create-a-simple-app"},{"id":"create-a-simple-app-ex1","language":"c","code":"#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <signal.h>\n\n#include <hiredis/hiredis.h>\n#include <hiredis/async.h>\n#include <hiredis/adapters/libevent.h>\n\n// Callback for the `GET` command. \nvoid getCallback(redisAsyncContext *c, void *r, void *privdata) {\n    redisReply *reply = r;\n    char *key = privdata;\n\n    if (reply == NULL) {\n        if (c->errstr) {\n            printf(\"errstr: %s\\n\", c->errstr);\n        }\n        return;\n    }\n\n    printf(\"Key: %s, value: %s\\n\", key, reply->str);\n\n    /* Disconnect after receiving the reply to GET */\n    redisAsyncDisconnect(c);\n}\n\n// Callback to respond to successful or unsuccessful connection.\nvoid connectCallback(const redisAsyncContext *c, int status) {\n    if (status != REDIS_OK) {\n        printf(\"Error: %s\\n\", c->errstr);\n        return;\n    }\n    printf(\"Connected...\\n\");\n}\n\n// Callback to respond to intentional or unexpected disconnection.\nvoid disconnectCallback(const redisAsyncContext *c, int status) {\n    if (status != REDIS_OK) {\n        printf(\"Error: %s\\n\", c->errstr);\n        return;\n    }\n    printf(\"Disconnected...\\n\");\n}\n\n\nint main (int argc, char **argv) {\n#ifndef _WIN32\n    signal(SIGPIPE, SIG_IGN);\n#endif\n\n    // Create the libevent `event_base` object to track all\n    // events.\n    struct event_base *base = event_base_new();\n\n    redisAsyncContext *c = redisAsyncConnect(\"127.0.0.1\", 6379);\n\n    if (c->err) {\n        printf(\"Error: %s\\n\", c->errstr);\n        return 1;\n    }\n\n    // Use the Redis libevent adapter to attach the Redis connection\n    // to the libevent main loop.\n    redisLibeventAttach(c,base);\n    \n    redisAsyncSetConnectCallback(c, connectCallback);\n    redisAsyncSetDisconnectCallback(c, disconnectCallback);\n    \n    char *key = \"testkey\";\n    char *value = \"testvalue\";\n\n    redisAsyncCommand(c, NULL, NULL, \"SET %s %s\", key, value);\n    redisAsyncCommand(c, getCallback, key, \"GET %s\", key);\n    \n    // Run the event loop.\n    event_base_dispatch(base);\n\n    return 0;\n}","section_id":"create-a-simple-app"},{"id":"run-the-code-ex0","language":"plaintext","code":"Connected...\nKey: testkey, value: testvalue\nDisconnected...","section_id":"run-the-code"}]}
{"schema_version":2,"id":"develop/clients/hiredis/int-examples/qt-integration","title":"Integrate hiredis with a Qt app","url":"https://redis.io/docs/latest/develop/clients/hiredis/int-examples/qt-integration/","summary":"Use `hiredis` in conjunction with the Qt app framework.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-09T11:06:49+01:00","page_type":"content","content_hash":"0297224b393f5bcceda05ad574adf6adad17fcefc5cd5f60aeafe25a627b6ca4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Qt](https://www.qt.io/) is a popular cross-platform C++ framework that\nyou can use to build command line and GUI apps. This guide explains how\nto use `hiredis` to connect to a Redis server from a Qt app."},{"id":"install-qt","title":"Install Qt","role":"setup","text":"You should first download and install the\n[Qt development environment](https://www.qt.io/download-dev) for your\ndevelopment platform, if you have not already done so. The example\nbelow briefly explains how to use Qt Creator\nto manage your project, but see the [Qt Creator](https://doc.qt.io/qtcreator/)\ndocs for an extensive set of examples and tutorials."},{"id":"create-a-simple-app","title":"Create a simple app","role":"content","text":"We will use a simple console app to demonstrate how to connect\nto Redis from Qt. Create the app project in Qt Creator using the\n**File > New Project** command. The generated source code is a\nsingle C++ file, called `main.cpp`, that uses a\n[`QCoreApplication`](https://doc.qt.io/qt-6/qcoreapplication.html)\nobject to handle the main event loop. Although it will compile and run,\nit doesn't do anything useful at this stage."},{"id":"add-hiredis-files","title":"Add `hiredis` files","role":"content","text":"Build `hiredis` if you have not already done so (see\n[Build and install](https://redis.io/docs/latest/develop/clients/hiredis#build-and-install)\nfor more information).\n\nYou should also make the `libhiredis` library available to the project. For example,\nif you have used the default option of [`cmake`](https://cmake.org/) as the project\nbuild tool and you have installed the `.dylib` or `.so` file for `hiredis` in `/usr/local/lib`,\nyou should add the following lines to the `CMakeLists.txt` file:\n\n[code example]\n\nYou should also modify the `target_link_libraries` directive to include\n`hiredis`:\n\n[code example]"},{"id":"add-code-to-access-redis","title":"Add code to access Redis","role":"content","text":"You can add a class using the **Add new** context menu\non the project folder in Qt Creator. The sections below give\nexamples of the code you should add to this class to\nconnect to Redis. The code is separated into header and\nimplementation files."},{"id":"header-file","title":"Header file","role":"content","text":"The header file for a class called `RedisExample` is shown below.\nAn explanation follows the code.\n\n[code example]\n\n[`QObject`](https://doc.qt.io/qt-6/qobject.html) is a key Qt class that\nimplements the [Object model](https://doc.qt.io/qt-6/object.html) for\ncommunication between objects. When you create your class in Qt Creator,\nyou can specify that you want it to be a subclass of `QObject` (this will\nadd the appropriate header files and include the `Q_OBJECT` macro in the\nclass declaration).\n\nThe `QObject` communication model uses some instance methods as *signals*\nto report events and others as *slots* to act as callbacks that process the\nevents (see [Signals and slots](https://doc.qt.io/qt-6/signalsandslots.html)\nfor an introduction). The Qt [meta-object compiler](https://doc.qt.io/qt-6/moc.html)\nrecognizes the non-standard C++ access specifiers `signals:` and `slots:`  in the\nclass declaration and adds extra code for them during compilation to enable\nthe communication mechanism.\n\nIn our class, there is a `run()` slot that will implement the code to access Redis.\nThe code eventually emits a `finished()` signal when it is complete to indicate that\nthe app should exit.\n\nOur simple example code just sets and gets a Redis\n[string](https://redis.io/docs/latest/develop/data-types/strings) key. The class contains\nprivate attributes for the key and value (following the Qt `m_xxx` naming convention\nfor class members). These are set by the constructor along with a call to the\n`QObject` constructor. The other attributes represent the connection context for\nRedis (which should generally be\n[asynchronous](https://redis.io/docs/latest/develop/clients/hiredis/connect#asynchronous-connection)\nfor a Qt app) and an adapter object that `hiredis` uses to integrate with Qt."},{"id":"implementation-file","title":"Implementation file","role":"content","text":"The file that implements the methods declared in the header is shown\nbelow. A full explanation follows the code.\n\n[code example]\n\nThe code that accesses Redis is in the `run()` method (recall that this\nimplements a Qt slot that will be called in response to a signal). The\ncode connects to Redis and stores the connection context pointer in the\n`m_ctx` attribute of the class instance. The call to `m_adapter.setContext()`\ninitializes the Qt support for the context. Note that we need an\nasynchronous connection for Qt. See\n[Asynchronous connection](https://redis.io/docs/latest/develop/clients/hiredis/connect#asynchronous-connection)\nfor more information.\n\nThe code then issues two Redis commands to [`SET`](https://redis.io/docs/latest/commands/set)\nthe string key and value that were supplied using the class's constructor. We are\nnot interested in the response returned by this command, but we are interested in the\nresponse from the [`GET`](https://redis.io/docs/latest/commands/get) command that follows it.\nBecause the commands are asynchronous, we need to set a callback to handle\nthe `GET` response when it arrives. In the `redisAsyncCommand()` call, we pass\na pointer to our `getCallback()` function and also pass a pointer to the\n`RedisExample` instance. This is a custom data field that will simply\nbe passed on to the callback when it executes (see \n[Construct asynchronous commands](https://redis.io/docs/latest/develop/clients/hiredis/issue-commands#construct-asynchronous-commands)\nfor more information).\n\nThe code in the `getCallback()` function starts by casting the reply pointer\nparameter to [`redisReply`](https://redis.io/docs/latest/develop/clients/hiredis/handle-replies)\nand the custom data pointer to `RedisExample`. Here, the example just prints\nthe reply string to the console, but you can process it in any way you like.\nYou can add methods to your class and call them within the callback using the\ncustom data pointer passed during the `redisAsyncCommand()` call. Here, we\nsimply use the pointer to call the `finish()` method.\n\nThe `finish()` method calls\n`redisAsyncDisconnect()` to close the connection and then uses the\nQt signalling mechanism to emit the `finished()` signal. You may need to\nprocess several commands with a particular connection context, but you should\nclose it from a callback when you have finished using it."},{"id":"main-program","title":"Main program","role":"content","text":"To access the `RedisExample` class, you should use code like the\nfollowing in the `main()` function defined in `main.cpp`:\n\n[code example]\n\nThis creates the [`QCoreApplication`](https://doc.qt.io/qt-6/qcoreapplication.html)\ninstance that manages the main event loop for a console app. It\nthen creates the instance of `RedisExample` with the key (\"url\") and\nvalue (\"https://redis.io/\") for our Redis string.\n\nThe two lines below set up the `QObject` communication mechanism\nfor the app. The call to\n[`QTimer::singleShot()`](https://doc.qt.io/qt-6/qtimer.html#singleShot-2)\nactivates the `run()`\nslot method on our `RedisExample` instance. The \n[`QObject::connect()`](https://doc.qt.io/qt-6/qobject.html#connect-5)\ncall creates a communication link between the `finished()` signal of\nout `RedisExample` instance and the `quit()` slot of our\n`QCoreApplication` instance. This quits the application event loop and\nexits the app when the `finished()` signal is emitted by the\n`RedisExample` object. This happens when the `finish()` method is called\nat the end of the `GET` command callback."},{"id":"run-the-code","title":"Run the code","role":"content","text":"When you have added the code, you can run it from the **Build** menu of\nQt Creator or from the toolbar at the left hand side of the window.\nAssuming the connection to Redis succeeds, it will print the message\n`Value: https://redis.io/` and quit. You can use the\n[`KEYS`](https://redis.io/docs/latest/commands/keys) command from\n[`redis-cli`](https://redis.io/docs/latest/develop/tools/cli) or\n[Redis Insight](https://redis.io/docs/latest/develop/tools/insight) to check\nthat the \"url\" string key was added to the Redis database."},{"id":"key-information","title":"Key information","role":"content","text":"There are many ways you could use Redis with a Qt app, but our example\ndemonstrates some techniques that are broadly useful:\n\n-   Use the `QObject` communication mechanism to simplify your code.\n-   Use the `hiredis` asynchronous API. Add a `RedisQtAdapter` instance\n    to your code and ensure you call its `setContext()` method to\n    initialize it before issuing Redis commands.\n-   Place all code and data you need to interact with Redis\n    (including the connection context) in a single\n    class or ensure it is available from a class via pointers and\n    Qt signals. Pass a pointer to an instance of your class in the\n    custom data parameter when you issue a Redis command with\n    `redisAsyncCommand()` and use this to process the reply or\n    issue more commands from the callback."}],"examples":[{"id":"add-hiredis-files-ex0","language":"plaintext","code":"add_library(hiredis SHARED IMPORTED)\nset_property(TARGET hiredis PROPERTY\n             IMPORTED_LOCATION \"/usr/local/lib/libhiredis.dylib\")","section_id":"add-hiredis-files"},{"id":"add-hiredis-files-ex1","language":"plaintext","code":"target_link_libraries(ConsoleTest Qt${QT_VERSION_MAJOR}::Core hiredis)","section_id":"add-hiredis-files"},{"id":"header-file-ex0","language":"c++","code":"// redisexample.h\n\n#ifndef REDISEXAMPLE_H\n#define REDISEXAMPLE_H\n\n#include <QObject>\n\n#include <hiredis/hiredis.h>\n#include <hiredis/async.h>\n#include <hiredis/adapters/qt.h>\n\n\nclass RedisExample : public QObject\n{\n    Q_OBJECT\n\npublic:\n    // Constructor\n    RedisExample(const char *keyForRedis, const char *valueForRedis, QObject *parent = 0)\n        :QObject(parent), m_key(keyForRedis), m_value(valueForRedis) {}\n\npublic slots:\n    // Slot method to hold the code that connects to Redis and issues\n    // commands.\n    void run();\n\nsignals:\n    // Signal to indicate that our code has finished executing.\n    void finished();\n\npublic:\n    // Method to close the Redis connection and signal that we've\n    // finished.\n    void finish();\n\nprivate:\n    const char *m_key;          // Key for Redis string.\n    const char *m_value;        // Value for Redis string.\n    redisAsyncContext *m_ctx;   // Redis connection context.\n    RedisQtAdapter m_adapter;   // Adapter to let `hiredis` work with Qt.\n};\n\n#endif // REDISEXAMPLE_H","section_id":"header-file"},{"id":"implementation-file-ex0","language":"c++","code":"// redisexample.cpp\n\n#include <iostream>\n\n#include \"redisexample.h\"\n\n\nvoid RedisExample::finish() {\n    // Disconnect gracefully.\n    redisAsyncDisconnect(m_ctx);\n\n    // Emit the `finished()` signal to indicate that the\n    // execution is complete.\n    emit finished();\n}\n\n\n// Callback used by our `GET` command in the `run()` method.\nvoid getCallback(redisAsyncContext *, void * r, void * privdata) {\n\n    // Cast data pointers to their appropriate types.\n    redisReply *reply = static_cast<redisReply *>(r);\n    RedisExample *ex = static_cast<RedisExample *>(privdata);\n\n    if (reply == nullptr || ex == nullptr) {\n        return;\n    }\n\n    std::cout << \"Value: \" << reply->str << std::endl;\n\n    // Close the Redis connection and quit the app.\n    ex->finish();\n}\n\n\nvoid RedisExample::run() {\n    // Open the connection to Redis.\n    m_ctx = redisAsyncConnect(\"localhost\", 6379);\n\n    if (m_ctx->err) {\n        std::cout << \"Error: \" << m_ctx->errstr << std::endl;\n        finish();\n    }\n\n    // Configure the connection to work with Qt.\n    m_adapter.setContext(m_ctx);\n\n    // Issue some simple commands. For the `GET` command, pass a\n    // callback function and a pointer to this object instance\n    // so that we can access the object's members from the callback.\n    redisAsyncCommand(m_ctx, NULL, NULL, \"SET %s %s\", m_key, m_value);\n    redisAsyncCommand(m_ctx, getCallback, this, \"GET %s\", m_key);\n}","section_id":"implementation-file"},{"id":"main-program-ex0","language":"c++","code":"#include <QCoreApplication>\n#include <QTimer>\n\n#include \"redisexample.h\"\n\n\nint main(int argc, char *argv[])\n{\n    QCoreApplication app(argc, argv);\n\n    // Instance of our object.\n    RedisExample r(\"url\", \"https://redis.io/\");\n\n    // Call the `run()` slot on our `RedisExample` instance to\n    // run our Redis commands. \n    QTimer::singleShot(0, &r, SLOT(run()));\n\n    // Set up a communication connection between our `finished()`\n    // signal and the application's `quit()` slot.\n    QObject::connect(&r, SIGNAL(finished()), &app, SLOT(quit()));\n\n    // Start the app's main event loop.\n    return app.exec();\n}","section_id":"main-program"}]}
{"schema_version":2,"id":"develop/clients/hiredis/issue-commands","title":"Issue commands","url":"https://redis.io/docs/latest/develop/clients/hiredis/issue-commands/","summary":"Construct commands and send them to the Redis server.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-09T11:06:49+01:00","page_type":"content","content_hash":"e901d457a5a73b33a53663e1aad376b8ee1977404332d706af8b8bc44634f302","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Unlike the other [client libraries](https://redis.io/docs/latest/develop/clients),\n`hiredis` doesn't provide an extensive API to construct the many different\nRedis [commands](/commands). However, it does provide a lightweight and\nflexible API to help you construct commands and parse their replies from\nyour own code.\n\nThe sections below describe the available functions in\ndetail."},{"id":"construct-synchronous-commands","title":"Construct synchronous commands","role":"content","text":"Use the `redisCommand()` function to send commands to the server:\n\n[code example]\n\nThis function receives a `redisContext` pointer and a pointer\nto a string containing the command (see\n[Connect](https://redis.io/docs/latest/develop/clients/hiredis/connect)\nto learn how to obtain the context pointer). The command text is the\nsame as the equivalent [`redis-cli`](https://redis.io/docs/latest/develop/tools/cli)\ncommand. For example, to issue the command:\n\n[code example]\n\nyou would use the following command with an existing `redisContext* c`:\n\n[code example]\n\nSee the [Command reference](/commands) for examples\nof CLI commands that you can use with `hiredis`. Most code examples\nin other sections of the docs also have a CLI tab showing\ncommand sequences that are equivalent to the code.\n\nThe command string is interpreted in a similar way to the format\nstring for `printf()`, so you can easily interpolate string values from\nyour code into the command with the `%s` format specifier:\n\n[code example]\n\nYou may need to include binary data in the command (for example, to store\n[vector embeddings](https://redis.io/docs/latest/develop/ai/search-and-query/vectors)\nin fields of a [hash](https://redis.io/docs/latest/develop/data-types/hashes)) object.\nTo do this, use the `%b` format specifier and pass a pointer to the\ndata buffer, followed by a `size_t` value indicating its length in bytes.\nAs the example below shows, you can freely mix `%s` and `%b` specifiers\nin the same format string. Also, you can use the sequence `%%` to\ndenote a literal percent sign, but the other `printf()` specifiers,\nsuch as `%d`, are not supported.\n\n[code example]\n\nThe `redisCommand()` function has a variant called `redisCommandArgv()`:\n\n[code example]\n\nThis doesn't take a format string but instead builds the command from an array\nof strings passed in the `argv` parameter.\n\nUse the `argc` value to\nspecify the length of this array and the `argvlen` array to specify\nthe lengths of each of the strings in the array. If you pass `NULL`\nfor `argvlen` then the function will attempt to use `strlen()` to\nget the length of each string. However, this will not work if any of\nthe strings contains binary data, so you should pass `argvlen`\nexplicitly in this case. The example below shows how to use\n`redisCommandArgv()` with a simple command:\n\n[code example]"},{"id":"construct-asynchronous-commands","title":"Construct asynchronous commands","role":"content","text":"Use the `redisAsyncCommand()` and `redisAsyncCommandArgv()`\nfunctions to send commands to the server asynchronously:\n\n[code example]\n\nThese work the same way as `redisCommand()` and `redisCommandArgv()`\n(see [Construct synchronous commands](#construct-synchronous-commands)\nabove) but they have two extra parameters. The first is a pointer to\na optional callback function and the second is a pointer to your own\ncustom data, which will be passed to the callback when it\nexecutes. Pass `NULL` for both of these pointers if you don't need\nto use them.\n\nThe callback has the following signature:\n\n[code example]\n\nThe first parameter is the asynchronous connection context and\nthe second is a pointer to the reply object. Use a cast to\n`(redisReply *)` to access the reply in the usual way (see \n[Handle command replies](https://redis.io/docs/latest/develop/clients/hiredis/handle-replies)\nfor a full description of `redisReply`). The last parameter\nis the custom data pointer that you supplied during the\n`redisAsyncCommand()` call. This is passed to your function\nwithout any modification.\n\nThe example below shows how you can use `redisAsyncCommand()` with\nor without a reply callback:\n\n[code example]\n\nNote that you should normally disconnect asynchronously from a\ncallback when you have finished using the connection.\nUse `redisAsyncDisconnect()` to disconnect gracefully, letting\npending commands execute and activate their callbacks.\nUse `redisAsyncFree()` to disconnect immediately. If you do this then\nany pending callbacks from commands that have already executed will be\ncalled with a `NULL` reply pointer."},{"id":"command-replies","title":"Command replies","role":"syntax","text":"The information in the `redisReply` object has several formats,\nand the format for a particular reply depends on the command that generated it.\nSee\n[Handle replies](https://redis.io/docs/latest/develop/clients/hiredis/handle-replies)\nto learn about the different reply formats and how to use them."}],"examples":[{"id":"construct-synchronous-commands-ex0","language":"c","code":"void *redisCommand(redisContext *c, const char *format, ...);","section_id":"construct-synchronous-commands"},{"id":"construct-synchronous-commands-ex1","language":"plaintext","code":"SET foo bar","section_id":"construct-synchronous-commands"},{"id":"construct-synchronous-commands-ex2","language":"c","code":"redisReply *reply = redisCommand(c, \"SET foo bar\");","section_id":"construct-synchronous-commands"},{"id":"construct-synchronous-commands-ex3","language":"c","code":"char *myKeyNumber = \"1\";\nchar *myValue = \"Hello\";\n\n// This issues the command 'SET key:1 Hello'.\nredisReply *reply = redisCommand(c, \"SET key:%s %s\", myKeyNumber, myValue);","section_id":"construct-synchronous-commands"},{"id":"construct-synchronous-commands-ex4","language":"c","code":"char *entryNumber = \"1\";\nchar *embedding = \"<binary data>\";\nchar *url = \"https://redis.io/\";\nsize_t embLength = 13;\n\nredisReply *reply = redisCommand(c,\n    \"HSET entry:%s embedding %b  url %s\",\n    entryNumber,\n    embedding, embLength,\n    url\n);","section_id":"construct-synchronous-commands"},{"id":"construct-synchronous-commands-ex5","language":"c","code":"void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);","section_id":"construct-synchronous-commands"},{"id":"construct-synchronous-commands-ex6","language":"c","code":"const char *argv[3] = { \"SET\", \"greeting\", \"hello\"};\nint argc = 3;\nconst size_t argvlen[] = {3, 8, 5};\n\nredisReply *reply = redisCommandArgv(c, argc, argv, argvlen);","section_id":"construct-synchronous-commands"},{"id":"construct-asynchronous-commands-ex0","language":"c","code":"#include <hiredis/async.h>\n    .\n    .\n    .\nint redisAsyncCommand(\n  redisAsyncContext *ac, redisCallbackFn *fn, void *privdata,\n  const char *format, ...);\nint redisAsyncCommandArgv(\n  redisAsyncContext *ac, redisCallbackFn *fn, void *privdata,\n  int argc, const char **argv, const size_t *argvlen);","section_id":"construct-asynchronous-commands"},{"id":"construct-asynchronous-commands-ex1","language":"c","code":"void(redisAsyncContext *c, void *reply, void *privdata);","section_id":"construct-asynchronous-commands"},{"id":"construct-asynchronous-commands-ex2","language":"c","code":"// The callback expects the key for the data in the `privdata`\n// custom data parameter.\nvoid getCallback(redisAsyncContext *c, void *r, void *privdata) {\n    redisReply *reply = r;\n    char *key = privdata;\n\n    if (reply == NULL) {\n        if (c->errstr) {\n            printf(\"errstr: %s\\n\", c->errstr);\n        }\n        return;\n    }\n\n    printf(\"Key: %s, value: %s\\n\", key, reply->str);\n\n    /* Disconnect after receiving the reply to GET */\n    redisAsyncDisconnect(c);\n}\n    .\n    .\n    .\n\n// Key and string value to pass to `SET`.\nchar *key = \"testkey\";\nchar *value = \"testvalue\";\n\n// We aren't interested in the simple status reply for\n// `SET`, so use NULL for the callback and custom data\n// pointers.\nredisAsyncCommand(c, NULL, NULL, \"SET %s %s\", key, value);\n\n// The reply from `GET` is essential, so set a callback\n// to retrieve it. Also, pass the key to the callback\n// as the custom data.\nredisAsyncCommand(c, getCallback, key, \"GET %s\", key);","section_id":"construct-asynchronous-commands"}]}
{"schema_version":2,"id":"develop/clients/hiredis/transpipe","title":"Pipelines and transactions","url":"https://redis.io/docs/latest/develop/clients/hiredis/transpipe/","summary":"Learn how to use Redis pipelines and transactions","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-09T11:06:49+01:00","page_type":"content","content_hash":"5bcf4449fb6d24755db84078d4b299db46d5dff782a0d61ebd7f7eb268f8346f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis lets you send a sequence of commands to the server together in a batch.\nThere are two types of batch that you can use:\n\n-   **Pipelines** avoid network and processing overhead by sending several commands\n    to the server together in a single communication. The server then sends back\n    a single communication with all the responses. See the\n    [Pipelining](https://redis.io/docs/latest/develop/using-commands/pipelining) page for more\n    information.\n-   **Transactions** guarantee that all the included commands will execute\n    to completion without being interrupted by commands from other clients.\n    See the [Transactions](https://redis.io/docs/latest/develop/using-commands/transactions)\n    page for more information."},{"id":"execute-a-pipeline","title":"Execute a pipeline","role":"content","text":"There is no command to explicitly start a pipeline with `hiredis`,\nbut if you issue a command with the `redisAppendCommand()` function,\nit will be added to an output buffer without being sent\nimmediately to the server.\n\nThere is also an input buffer that receives replies from\ncommands. If you call `redisGetReply()` when the input buffer is empty,\nit will first send any commands that are queued in the output buffer and\nthen wait for replies to arrive in the input buffer. It will then return\nthe first reply only.\n\nIf you then make subsequent `redisGetReply()` calls, they will\nfind the input buffer is not empty, but still has replies\nqueued from previous commands. In this case, `redisGetReply()`\nwill just remove and return replies from the input buffer\nuntil it is empty again.\n\nThe example below shows how to use `redisAppendCommand()`\nand `redisGetReply()` together:\n\n[code example]\n\n`redisAppendCommand()` has the same call signature as `redisCommand()` except that\nit doesn't return a `redisReply`. There is also a `redisAppendCommandArgv()`\nfunction that is analogous to `redisCommandArgv()` (see\n[Issue commands](https://redis.io/docs/latest/develop/clients/hiredis/issue-commands)\nfor more information).\n\n`redisGetReply()` receives the usual\ncontext pointer and a pointer to a `redisReply` pointer (which you\nmust cast to `void**`). After `redisGetReply()` returns,\nthe reply pointer will point to the `redisReply` object returned by\nthe queued command (see\n[Handle command replies](https://redis.io/docs/latest/develop/clients/hiredis/handle-replies)\nfor more information). \n\nCall `redisGetReply()` once for each command that you added to the pipeline.\nYou should check for errors after each call and free each reply object\nwhen you have finished processing it, as in the example above."},{"id":"transactions","title":"Transactions","role":"content","text":"`hiredis` doesn't provide any special API to handle transactions, but\nyou can implement them yourself using the [`MULTI`](https://redis.io/docs/latest/commands/multi),\n[`EXEC`](https://redis.io/docs/latest/commands/exec), and [`WATCH`](https://redis.io/docs/latest/commands/watch)\ncommands as you would from [`redis-cli`](https://redis.io/docs/latest/develop/tools/cli).\nSee [Transactions](https://redis.io/docs/latest/develop/using-commands/transactions)\nfor more information."}],"examples":[{"id":"execute-a-pipeline-ex0","language":"c","code":"redisAppendCommand(c, \"SET fruit:0 Apple\");\nredisAppendCommand(c, \"SET fruit:1 Banana\");\nredisAppendCommand(c, \"SET fruit:2 Cherry\");\n\nredisAppendCommand(c, \"GET fruit:0\");\nredisAppendCommand(c, \"GET fruit:1\");\nredisAppendCommand(c, \"GET fruit:2\");\n\n\nredisReply *reply;\n\n// Iterate once for each of the six commands in the\n// pipeline.\nfor (int i = 0; i < 6; ++i) {\n    redisGetReply(c, (void**) &reply);\n\n    // If an error occurs, the context object will\n    // contain an error code and/or an error string.\n    if (reply->type == REDIS_REPLY_ERROR) {\n        printf(\"Error: %s\", c->errstr);\n    } else {\n        printf(\"%s\\n\", reply->str);\n    }\n\n    freeReplyObject(reply);\n}\n// >>> OK\n// >>> OK\n// >>> OK\n// >>> Apple\n// >>> Banana\n// >>> Cherry","section_id":"execute-a-pipeline"}]}
{"schema_version":2,"id":"develop/clients","title":"Connect with Redis client API libraries","url":"https://redis.io/docs/latest/develop/clients/","summary":"Connect your application to a Redis database and try an example","aliases":["/develop/connect/clients","/develop/connect/","/develop/connect","/connect/clients/","/clients/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-30T13:16:11-07:00","children":[{"id":"develop/clients/redis-py","summary":"Connect your Python application to a Redis database","title":"redis-py guide (Python)","url":"https://redis.io/docs/latest/develop/clients/redis-py/"},{"id":"develop/clients/redis-vl","summary":"Connect your Python vector application to a Redis vector database","title":"Redis vector library guide (Python)","url":"https://redis.io/docs/latest/develop/clients/redis-vl/"},{"id":"develop/clients/dotnet","summary":"Connect your .NET application to a Redis database","title":"StackExchange.Redis guide (C#/.NET)","url":"https://redis.io/docs/latest/develop/clients/dotnet/"},{"id":"develop/clients/nodejs","summary":"Connect your Node.js/JavaScript application to a Redis database","title":"node-redis guide (JavaScript)","url":"https://redis.io/docs/latest/develop/clients/nodejs/"},{"id":"develop/clients/ioredis","summary":"Connect your Node.js/JavaScript application to a Redis database","title":"ioredis guide (JavaScript)","url":"https://redis.io/docs/latest/develop/clients/ioredis/"},{"id":"develop/clients/jedis","summary":"Connect your Java application to a Redis database","title":"Jedis guide (Java)","url":"https://redis.io/docs/latest/develop/clients/jedis/"},{"id":"develop/clients/lettuce","summary":"Connect your Lettuce application to a Redis database","title":"Lettuce guide (Java)","url":"https://redis.io/docs/latest/develop/clients/lettuce/"},{"id":"develop/clients/go","summary":"Connect your Go application to a Redis database","title":"go-redis guide (Go)","url":"https://redis.io/docs/latest/develop/clients/go/"},{"id":"develop/clients/php","summary":"Connect your PHP application to a Redis database","title":"Predis guide (PHP)","url":"https://redis.io/docs/latest/develop/clients/php/"},{"id":"develop/clients/rust","summary":"Connect your Rust application to a Redis database","title":"redis-rs guide (Rust)","url":"https://redis.io/docs/latest/develop/clients/rust/"},{"id":"develop/clients/hiredis","summary":"Connect your C application to a Redis database.","title":"hiredis guide (C)","url":"https://redis.io/docs/latest/develop/clients/hiredis/"},{"id":"develop/clients/ruby","summary":"Connect your Ruby application to a Redis database","title":"redis-rb guide (Ruby)","url":"https://redis.io/docs/latest/develop/clients/ruby/"},{"id":"develop/clients/om-clients","summary":"Object-Mapper libraries for Redis Open Source","title":"Object-Mapper libraries","url":"https://redis.io/docs/latest/develop/clients/om-clients/"},{"id":"develop/clients/client-side-caching","summary":"Server-assisted, client-side caching in Redis","title":"Client-side caching introduction","url":"https://redis.io/docs/latest/develop/clients/client-side-caching/"},{"id":"develop/clients/connection-urls","summary":"Learn the Redis connection URL format and how each client library interprets it","title":"Connection URLs","url":"https://redis.io/docs/latest/develop/clients/connection-urls/"},{"id":"develop/clients/pools-and-muxing","summary":"Manage Redis connections efficiently","title":"Connection pools and multiplexing","url":"https://redis.io/docs/latest/develop/clients/pools-and-muxing/"},{"id":"develop/clients/error-handling","summary":"Learn how to handle errors when using Redis client libraries.","title":"Error handling","url":"https://redis.io/docs/latest/develop/clients/error-handling/"},{"id":"develop/clients/failover","summary":"Improve reliability using the failover/failback features of client libraries.","title":"Client-side geographic failover","url":"https://redis.io/docs/latest/develop/clients/failover/"},{"id":"develop/clients/sch","summary":"Learn how to avoid disruptions during Redis server maintenance.","title":"Smart client handoffs","url":"https://redis.io/docs/latest/develop/clients/sch/"},{"id":"develop/clients/observability","summary":"Monitor your client's activity for optimization and debugging.","title":"Observability","url":"https://redis.io/docs/latest/develop/clients/observability/"},{"id":"develop/clients/patterns","summary":"Novel patterns for working with Redis data structures","title":"Coding patterns","url":"https://redis.io/docs/latest/develop/clients/patterns/"},{"id":"develop/clients/version-support","summary":"Understand how Redis maintains and versions its official client libraries","title":"Client library support and versioning policy","url":"https://redis.io/docs/latest/develop/clients/version-support/"}],"page_type":"content","content_hash":"a17e8a31f19994437e2e9170d9c737901879b9aa41a30572dfc18e7fd61ac181","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Use the Redis client libraries to connect to Redis servers from\nyour own code. We document the following client libraries\nfor nine main languages:\n\n| Language | Client name | Docs | Supported |\n| :-- | :-- | :-- | :-- |\n| [Python](https://www.python.org/) | [`redis-py`](https://github.com/redis/redis-py) |[`redis-py` guide](https://redis.io/docs/latest/develop/clients/redis-py) | Yes |\n| [Python](https://www.python.org/) | [`RedisVL`](https://github.com/redis/redis-vl-python) |[RedisVL guide](https://redis.io/docs/latest/develop/ai/redisvl) | Yes \n| [C#/.NET](https://learn.microsoft.com/en-us/dotnet/csharp/) | [`StackExchange.Redis`](https://github.com/StackExchange/StackExchange.Redis) |[`StackExchange.Redis` guide](https://redis.io/docs/latest/develop/clients/dotnet) | Yes |\n| [C#/.NET](https://learn.microsoft.com/en-us/dotnet/csharp/) | [`NRedisStack`](https://github.com/redis/NRedisStack) |[`NRedisStack` guide](https://redis.io/docs/latest/develop/clients/dotnet/nredisstack) | Yes |\n| [JavaScript](https://nodejs.org/en) | [`node-redis`](https://github.com/redis/node-redis) | [`node-redis` guide](https://redis.io/docs/latest/develop/clients/nodejs) | Yes |\n| [JavaScript](https://nodejs.org/en) | [`ioredis`](https://github.com/redis/ioredis) | [`ioredis` guide](https://redis.io/docs/latest/develop/clients/ioredis) | Yes |\n| [Java](https://www.java.com/en/) | [`Jedis`](https://github.com/redis/jedis) | [`Jedis` guide](https://redis.io/docs/latest/develop/clients/jedis) |  Yes |\n| [Java](https://www.java.com/en/) | [`Lettuce`](https://github.com/redis/lettuce) | [`Lettuce` guide](https://redis.io/docs/latest/develop/clients/lettuce) | Yes |\n| [Go](https://go.dev/) | [`go-redis`](https://github.com/redis/go-redis) | [`go-redis` guide](https://redis.io/docs/latest/develop/clients/go) | Yes |\n| [PHP](https://www.php.net/)| [`Predis`](https://github.com/predis/predis) | [`Predis` guide](https://redis.io/docs/latest/develop/clients/php) | Best effort [*](#best-effort) |\n| [C](https://en.wikipedia.org/wiki/C_(programming_language)) | [`hiredis`](https://github.com/redis/hiredis) | [`hiredis` guide](https://redis.io/docs/latest/develop/clients/hiredis) | Yes |\n| [Rust](https://www.rust-lang.org/) | [`redis-rs`](https://github.com/redis-rs/redis-rs) | [`redis-rs` guide](https://redis.io/docs/latest/develop/clients/rust) | Best effort [*](#best-effort) |\n| [Ruby](https://www.ruby-lang.org/en/) | [`redis-rb`](https://github.com/redis/redis-rb) | [`redis-rb` guide](https://redis.io/docs/latest/develop/clients/ruby) | Yes |\n\n> [!NOTE]\n> *<a name=\"best-effort\"></a>\n> Redis does not provide official support for third-party client libraries.\n> However, we contribute new features, offer guidance, and collaborate with the community\n> on a best-effort basis to help ensure these libraries remain reliable and up-to-date.\n\nSee [Client library support and versioning policy](https://redis.io/docs/latest/develop/clients/version-support)\nfor details about how Redis maintains, versions, and tests the official client libraries.\n\nWe also provide several higher-level\n[object mapping (OM)](https://redis.io/docs/latest/develop/clients/om-clients)\nlibraries for [Python](https://redis.io/docs/latest/integrate/redisom-for-python),\n[C#/.NET](https://redis.io/docs/latest/integrate/redisom-for-net),\n[Node.js](https://redis.io/docs/latest/integrate/redisom-for-node-js), and\n[Java/Spring](https://redis.io/docs/latest/integrate/redisom-for-java)."},{"id":"community-supported-clients","title":"Community-supported clients","role":"content","text":"The table below shows some popular third-party client libraries. Note that Redis does not\ndevelop or contribute to these libraries directly.\n\n| Language | Client name | Github | Docs |\n| :-- | :-- | :-- | :-- |\n| [C++](https://en.wikipedia.org/wiki/C%2B%2B) | Boost.Redis | https://github.com/boostorg/redis | https://www.boost.org/doc/libs/develop/libs/redis/doc/html/index.html |\n| [Dart](https://dart.dev/) | redis_dart_link | https://github.com/toolsetlink/redis_dart_link | https://github.com/toolsetlink/redis_dart_link |\n| [Elixir](https://elixir-lang.org/) | Redix | https://github.com/whatyouhide/redix | https://hexdocs.pm/redix |\n| [PHP](https://www.php.net/) | PhpRedis extension | https://github.com/phpredis/phpredis | https://github.com/phpredis/phpredis/blob/develop/README.md |\n| [Python](https://www.python.org/) | coredis | https://github.com/alisaifee/coredis | https://coredis.readthedocs.io |\n| [Scala](https://www.scala-lang.org/) | Sage | https://github.com/ghostdogpr/sage | https://ghostdogpr.github.io/sage/ |"},{"id":"requirements","title":"Requirements","role":"content","text":"You will need access to a Redis server to use these libraries.\nYou can experiment with a local installation of Redis Open Source\n(see [Install Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack)) or with a free trial of [Redis Cloud](https://redis.io/docs/latest/operate/rc).\nTo interact with a Redis server without writing code, use the\n[Redis CLI](https://redis.io/docs/latest/develop/tools/cli) and\n[Redis Insight](https://redis.io/docs/latest/develop/tools/insight) tools."},{"id":"choose-a-client-library-for-your-language","title":"Choose a client library for your language","role":"content","text":"For some languages, we only recommend one client library, but Python, JavaScript,\nJava, C#, and PHP have multiple options. The sections below summarize the differences\nbetween the options for each language."},{"id":"python","title":"Python","role":"content","text":"-   [`redis-py`](https://github.com/redis/redis-py) is the recommended Python client library for \n    most use cases.\n-   [`RedisVL`](https://github.com/redis/redis-vl-python) is specialized for handling \n    high-dimensional vector data and is a good choice for AI/ML workflows.\n-   [RedisOM for Python](https://github.com/redis/redis-om-python) is an object mapping library that\n    provides a high-level API for working with Redis data structures."},{"id":"javascript","title":"JavaScript","role":"content","text":"-   [`node-redis`](https://github.com/redis/node-redis) is the recommended JavaScript client library \n    for most use cases.\n-   [`ioredis`](https://github.com/redis/ioredis) is an older library that is still supported, but\n    lacks some of the newer features and performance optimizations of `node-redis`.\n    Note that there is a migration guide available if you are interested in converting\n    your `ioredis` project to `node-redis`\n    (see [Migrate from ioredis](https://redis.io/docs/latest/develop/clients/nodejs/migration)).\n-   [RedisOM for Node.js](https://github.com/redis/redis-om-node) is an object mapping library that\n    provides a high-level API for working with Redis data structures."},{"id":"java","title":"Java","role":"content","text":"-   [`Jedis`](https://github.com/redis/jedis) supports the full feature set of Redis and has a  \n    straightforward API, but is limited to synchronous operations.\n-   [`Lettuce`](https://github.com/redis/lettuce) supports synchronous, asynchronous,\n    and reactive operations. However, it has a more complex API than Jedis and doesn't currently\n    support all Redis features (notably time series and probabilistic data structures).\n-   [RedisOM for Java](https://github.com/redis/redis-om-spring) is an object mapping library that\n    provides a high-level API for working with Redis data structures."},{"id":"c","title":"C#","role":"content","text":"-   [`StackExchange.Redis`](https://github.com/StackExchange/StackExchange.Redis) is the \n    recommended C# client library for using the core Redis data types and commands.\n-   [`NRedisStack`](https://github.com/redis/NRedisStack) builds upon\n    `StackExchange.Redis` with\n    support for an extended set of data types and features, such as\n    [JSON](https://redis.io/docs/latest/develop/data-types/json),\n    [Redis search](https://redis.io/docs/latest/develop/ai/search-and-query), and\n    [Time series](https://redis.io/docs/latest/develop/data-types/timeseries).\n-   [RedisOM for .NET](https://github.com/redis/redis-om-dotnet) is an object mapping library that\n    provides a high-level API for working with Redis data structures."},{"id":"php","title":"PHP","role":"content","text":"-   [`Predis`](https://github.com/predis/predis) is the recommended PHP client library for\n    most use cases. It has \"best effort\" support from the Redis team (it's a third-party library\n    but the Redis team contributes to it and helps with issues) and has good coverage in the\n    [Redis documentation](https://redis.io/docs/latest/develop/clients/php). However, it is implemented in\n    PHP, which limits its performance compared to [phpredis](https://github.com/phpredis/phpredis),\n    which is implemented in C.\n-   [phpredis](https://github.com/phpredis/phpredis) is a popular and well-maintained PHP client\n    library that is implemented using C extensions to PHP for better performance. However, it is\n    not actively supported or documented by the Redis team."},{"id":"client-library-decision-tree","title":"Client library decision tree","role":"content","text":"Use the decision tree below to help you choose the right client library for your needs,\nbased on the recommendations in the previous sections. Note that you should regard the\nconclusions as a guideline for your research, not as a definitive answer.\n\n[code example]"}],"examples":[{"id":"client-library-decision-tree-ex0","language":"decision-tree {id=\"client-recommendation\"}","code":"id: client-recommendation\nscope: clients\nindentWidth: 28\nrootQuestion: singleClientLanguageQuestion\nquestions:\n    singleClientLanguageQuestion:\n        text: |\n            Are you using Go, Rust, C, C++, Dart, or Ruby?\n        whyAsk: |\n            These languages have only one recommended client library,\n            so the choice is straightforward\n        answers:\n            yes:\n                value: \"Yes\"\n                outcome:\n                    label: |\n                        These languages only have one recommended client library. Use\n                        the client library for your language listed in the table above.\n                    id: singleClientLanguageOutcome\n            no:\n                value: \"No\"\n                nextQuestion: multiClientLanguageQuestion\n\n    multiClientLanguageQuestion:\n        text: |\n            Are you using Python, JavaScript, Java, or C#/.NET\n            and require object mapping (ORM-like) functionality?\n        whyAsk: |\n            RedisOM provides a high-level API for these languages.\n            If you don't need OM, we'll help you choose the best low-level client\n        answers:\n            yes:\n                value: \"Yes\"\n                outcome:\n                    label: |\n                        Use RedisOM for your language\n                        (available for Python, JavaScript, Java, and C#/.NET)\n                    id: redisOMOutcome\n            no:\n                value: \"No\"\n                nextQuestion: lowLevelClientQuestion\n\n    lowLevelClientQuestion:\n        text: Which language are you using?\n        whyAsk: |\n            Different languages have different low-level client libraries\n            optimized for their ecosystems\n        answers:\n            python:\n                value: Py\n                nextQuestion: pythonLowLevelQuestion\n            javascript:\n                value: JS\n                nextQuestion: javascriptLowLevelQuestion\n            java:\n                value: Java\n                nextQuestion: javaLowLevelQuestion\n            php:\n                value: PHP\n                nextQuestion: phpLowLevelQuestion\n            dotnet:\n                value: \"C#\"\n                nextQuestion: dotnetLowLevelQuestion\n\n    dotnetLowLevelQuestion:\n        text: |\n            Do you need Redis Stack features such as JSON,\n            Search, vector search, or Time series?\n        whyAsk: |\n            StackExchange.Redis is the recommended client for core Redis commands,\n            while NRedisStack extends it with support for Redis Stack data types and features\n        answers:\n            yes:\n                value: \"Yes\"\n                outcome:\n                    label: Use NRedisStack\n                    id: nRedisStackOutcome\n            no:\n                value: \"No\"\n                outcome:\n                    label: Use StackExchange.Redis\n                    id: stackExchangeRedisOutcome\n\n    pythonLowLevelQuestion:\n        text: |\n            Are you building an AI/ML application\n            mainly based around high-dimensional vector operations?\n        whyAsk: |\n            RedisVL is specialized for AI/ML workflows with vector data,\n            while redis-py is better for general-purpose use\n        answers:\n            yes:\n                value: \"Yes\"\n                outcome:\n                    label: Use RedisVL\n                    id: redisVLOutcome\n            No:\n                value: \"No\"\n                outcome:\n                    label: Use redis-py\n                    id: redisPyOutcome\n\n    javaLowLevelQuestion:\n        text: Do you need async or reactive operations?\n        whyAsk: |\n            Jedis supports only synchronous operations with a simpler API,\n            while Lettuce supports async and reactive patterns\n        answers:\n            yes:\n                value: \"Yes\"\n                outcome:\n                    label: Use Lettuce\n                    id: lettuceOutcome\n            no:\n                value: \"No\"\n                outcome:\n                    label: Use Jedis\n                    id: jedisOutcome\n\n    javascriptLowLevelQuestion:\n        text: Do you have an existing codebase using ioredis?\n        whyAsk: |\n            If you're already using ioredis, you can continue with it.\n            For new projects, node-redis is the recommended choice\n        answers:\n            yes:\n                value: \"Yes\"\n                nextQuestion: ioredisAdvancedFeaturesQuestion\n            no:\n                value: \"No\"\n                outcome:\n                    label: Use node-redis\n                    id: nodeRedisOutcome\n\n    ioredisAdvancedFeaturesQuestion:\n        text: Do you want to use advanced Redis features such as search, vector database, and JSON?\n        answers:\n            yes:\n                value: \"Yes\"\n                outcome:\n                    label: Migrate to node-redis using our migration guide\n                    id: migrateToNodeRedisOutcome\n            no:\n                value: \"No\"\n                outcome:\n                    label: Continue using ioredis\n                    id: continueIoredisOutcome\n\n    phpLowLevelQuestion:\n        text: |\n            Is maximum performance a higher priority than full support and documentation?\n        whyAsk: |\n            PhpRedis is a C extension with better performance,\n            while Predis is a pure PHP implementation that is better supported and documented\n        answers:\n            yes:\n                value: \"Yes\"\n                outcome:\n                    label: Use PhpRedis\n                    id: phpredisOutcome\n            no:\n                value: \"No\"\n                outcome:\n                    label: Use Predis\n                    id: predisOutcome","section_id":"client-library-decision-tree"}]}
{"schema_version":2,"id":"develop/clients/ioredis/connect","title":"Connect to the server","url":"https://redis.io/docs/latest/develop/clients/ioredis/connect/","summary":"Connect your Python application to a Redis database","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-12T14:48:04+01:00","page_type":"content","content_hash":"ca5bded6b210e5a14c39b52949c9b5b33db10d0bfd632a2dd7cf94a6468d9fac","sections":[{"id":"basic-connection","title":"Basic connection","role":"overview","text":"Connect to localhost on port 6379:\n\n[code example]\n\nYou can also specify a full set of connection options:\n\n[code example]\n\n> [!NOTE]\n> ioredis supports [connection URLs](https://redis.io/docs/latest/develop/clients/connection-urls#ioredis-javascript) through the `Redis` constructor.\n> A URL can set the host, port, credentials, database, and whether to use TLS,\n> so you can use one instead of passing those options separately.\n\nStore and retrieve a simple string.\n\n[code example]"},{"id":"connect-to-a-redis-cluster","title":"Connect to a Redis cluster","role":"content","text":"To connect to a Redis cluster, use `Redis.Cluster()`, passing an array of\nendpoints.\n\n[code example]"},{"id":"connect-to-redis-sentinel","title":"Connect to Redis Sentinel","role":"content","text":"To connect through [Redis Sentinel](https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel),\npass a list of Sentinels in the `sentinels` option and the name of the primary that the\nSentinels monitor in `name`. The client asks the Sentinels for the current primary.\n\n[code example]\n\nThe Sentinels and the data nodes have separate credentials. Set `sentinelPassword` (and\n`sentinelUsername`, if needed) for the Sentinels, and `username` and `password` for the\nprimary and replicas:\n\n[code example]\n\nIf the Sentinel password is wrong or missing, the error says that all Sentinels are\nunreachable.\n\nBy default, ioredis doesn't listen for failover announcements from the Sentinels. After a\nfailover, the client keeps using the old primary until Sentinel reconfigures it as a\nreplica and closes the connection. Any writes that the old primary accepts during that time\nare lost. Set `failoverDetector: true` to switch to the new primary as soon as the Sentinels\nannounce it.\n\nTo connect to a replica instead of the primary, set `role: \"slave\"`. The client connects to\none replica, chosen at random. Replication is asynchronous, so a replica can return stale\ndata.\n\n[code example]"},{"id":"connect-to-your-production-redis-with-tls","title":"Connect to your production Redis with TLS","role":"content","text":"When you deploy your application, use TLS and follow the [Redis security](https://redis.io/docs/latest/operate/oss_and_stack/management/security) guidelines.\n\n[code example]"}],"examples":[{"id":"basic-connection-ex0","language":"js","code":"const redis = new Redis();","section_id":"basic-connection"},{"id":"basic-connection-ex1","language":"js","code":"const redis = new Redis({\n  port: 6379,\n  host: \"127.0.0.1\",\n  username: \"default\",\n  password: \"my-password\",\n  db: 0,\n});","section_id":"basic-connection"},{"id":"basic-connection-ex2","language":"js","code":"await redis.set('foo', 'bar');\nconst value = await redis.get('foo');\nconsole.log(value); // >>> bar","section_id":"basic-connection"},{"id":"connect-to-a-redis-cluster-ex0","language":"js","code":"const redis = new Redis.Cluster([\n    {\n        host: '127.0.0.1',\n        port: 6380,\n        password: 'my-password',\n        username: 'default',\n    },\n    {\n        host: '127.0.0.1',\n        port: 6381,\n        password: 'my-other-password',\n        username: 'default',\n    },\n    // ...\n]);","section_id":"connect-to-a-redis-cluster"},{"id":"connect-to-redis-sentinel-ex0","language":"js","code":"const redis = new Redis({\n  sentinels: [\n    { host: \"localhost\", port: 26379 },\n    { host: \"localhost\", port: 26380 },\n    { host: \"localhost\", port: 26381 },\n  ],\n  name: \"mymaster\",\n});\n\nawait redis.set('foo', 'bar');\nconst value = await redis.get('foo');\nconsole.log(value); // >>> bar","section_id":"connect-to-redis-sentinel"},{"id":"connect-to-redis-sentinel-ex1","language":"js","code":"const redis = new Redis({\n  sentinels: [\n    { host: \"localhost\", port: 26379 },\n    { host: \"localhost\", port: 26380 },\n    { host: \"localhost\", port: 26381 },\n  ],\n  name: \"mymaster\",\n  // Credentials for the Sentinel instances.\n  sentinelPassword: \"my-sentinel-password\",\n  // Credentials for the primary and replica data nodes.\n  username: \"default\",\n  password: \"my-password\",\n});","section_id":"connect-to-redis-sentinel"},{"id":"connect-to-redis-sentinel-ex2","language":"js","code":"const replica = new Redis({\n  sentinels: [{ host: \"localhost\", port: 26379 }],\n  name: \"mymaster\",\n  role: \"slave\",\n});","section_id":"connect-to-redis-sentinel"},{"id":"connect-to-your-production-redis-with-tls-ex0","language":"js","code":"const redis = new Redis({\n  host: \"localhost\",\n  //...\n  tls: {\n    key: readFileSync('./redis_user_private.key'),\n    cert: readFileSync('./redis_user.crt'),\n    ca: fs.readFileSync('./redis_ca.pem'),\n  },\n});","section_id":"connect-to-your-production-redis-with-tls"}]}
{"schema_version":2,"id":"develop/clients/ioredis","title":"ioredis guide (JavaScript)","url":"https://redis.io/docs/latest/develop/clients/ioredis/","summary":"Connect your Node.js/JavaScript application to a Redis database","aliases":["/connect/clients/ioredis/","/clients/ioredis/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-09T11:51:21+01:00","children":[{"id":"develop/clients/ioredis/connect","summary":"Connect your Python application to a Redis database","title":"Connect to the server","url":"https://redis.io/docs/latest/develop/clients/ioredis/connect/"}],"page_type":"content","content_hash":"30fd5303d3dd715451ff77ea5d546c8ca00a0d26aa16cc3417c000932a66029d","sections":[{"id":"install","title":"Install","role":"setup","text":"To install `ioredis`, run:\n\n[code example]"},{"id":"connect-and-test","title":"Connect and test","role":"content","text":"Connect to localhost on port 6379. \n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]\n\n\n\nStore and retrieve a simple string.\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]\n\n\n\nStore and retrieve a map.\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]\n\n\n\nWhen you have finished using a connection, close it with `client.quit()`.\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]"},{"id":"more-information","title":"More information","role":"content","text":"The [Github repository](https://github.com/redis/ioredis) has useful\ninformation, including [API docs](https://redis.github.io/ioredis/index.html)\nand a set of [code examples](https://github.com/redis/ioredis/tree/main/examples)."}],"examples":[{"id":"install-ex0","language":"bash","code":"npm install ioredis","section_id":"install"},{"id":"connect-and-test-ex0","language":"javascript","code":"import { Redis } from 'ioredis';\n\nconst redis = new Redis();","section_id":"connect-and-test"},{"id":"connect-and-test-ex1","language":"javascript","code":"await redis.set('key', 'value');\nconst value = await redis.get('key');\nconsole.log(value); // >>> value","section_id":"connect-and-test"},{"id":"connect-and-test-ex2","language":"javascript","code":"await redis.hset('user-session:123', {\n    name: 'John',\n    surname: 'Smith',\n    company: 'Redis',\n    age: 29\n});\n\nconst userSession = await redis.hgetall('user-session:123');\nconsole.log(JSON.stringify(userSession, null, 2));\n/* >>>\n{\n  \"surname\": \"Smith\",\n  \"name\": \"John\",\n  \"company\": \"Redis\",\n  \"age\": \"29\"\n}\n */","section_id":"connect-and-test"},{"id":"connect-and-test-ex3","language":"javascript","code":"redis.disconnect();","section_id":"connect-and-test"}]}
{"schema_version":2,"id":"develop/clients/jedis/amr","title":"Connect to Azure Managed Redis","url":"https://redis.io/docs/latest/develop/clients/jedis/amr/","summary":"Learn how to authenticate to an Azure Managed Redis (AMR) database","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-08T15:41:24+01:00","page_type":"content","content_hash":"d9e480ae12624cab908ae0128f126c0f2eabd0bfa6387415bf09fc8a68e2fb70","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The [`redis-authx-entraid`](https://github.com/redis/jvm-redis-authx-entraid) package\nlets you authenticate your app to\n[Azure Managed Redis (AMR)](https://azure.microsoft.com/en-us/products/managed-redis)\nusing [Microsoft Entra ID](https://learn.microsoft.com/en-us/entra/identity/).\nYou can authenticate using a system-assigned or user-assigned\n[managed identity](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview)\nor a [service principal](https://learn.microsoft.com/en-us/entra/identity-platform/app-objects-and-service-principals),\nletting `redis-authx-entraid` fetch and renew the authentication tokens for you automatically.\n\nSee\n[Use Microsoft Entra for cache authentication](https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-azure-active-directory-for-authentication)\nin the Microsoft docs to learn how to configure Azure to use Entra ID authentication."},{"id":"install","title":"Install","role":"setup","text":"Install [`jedis`](https://redis.io/docs/latest/develop/clients/jedis) first,\nif you have not already done so.\n\nIf you are using Maven, add\nthe following dependency to your `pom.xml` file:\n\n[code example]\n\nIf you are using Gradle, add the following dependency to your\n`build.gradle` file:\n\n[code example]"},{"id":"create-a-tokenauthconfig-instance","title":"Create a `TokenAuthConfig` instance","role":"content","text":"The `TokenAuthConfig` class contains the authentication details that you\nmust supply when you connect to Redis. Chain the methods of the\n`EntraIDTokenAuthConfigBuilder` class together (starting with the `builder()`\nmethod) to include the details you need, as shown in the following example:\n\n[code example]\n\nSome of the details you can supply are common to different use cases:\n\n-   `secret()`: A string containing the [authentication secret](https://learn.microsoft.com/en-us/purview/sit-defn-azure-ad-client-secret).\n-   `authority()`: A string containing the [authority](https://learn.microsoft.com/en-us/entra/identity-platform/msal-client-application-configuration#authority)\n    URL.\n-   `scopes()`: A set of strings defining the [scopes](https://learn.microsoft.com/en-us/entra/identity-platform/scopes-oidc)\n    you want to apply. Configure your client application to acquire a Microsoft Entra token for scope, `https://redis.azure.com/.default` or `acca5fbb-b7e4-4009-81f1-37e38fd66d78/.default` (as detailed at [Microsoft Entra ID for authentication](https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/managed-redis/managed-redis-entra-for-authentication#microsoft-entra-client-workflow)) \n    with the\n    [Microsoft Authentication Library (MSAL)](https://learn.microsoft.com/en-us/entra/identity-platform/msal-overview)\n\n(See [Advanced configuration options](#advanced-configuration-options) below\nto learn more about the options for controlling token request retry and timeout\nbehavior.)\n\nYou can also add configuration to authenticate with a [service principal](#serv-principal)\nor a [managed identity](#mgd-identity) as described in the sections below."},{"id":"serv-principal","title":"Configuration for a service principal","role":"configuration","text":"Add `clientId()` to the `EntraIDTokenAuthConfigBuilder` chain to specify\nauthentication via a service principal, passing the ID token string as\na parameter. (See the\n[Microsoft EntraID docs](https://learn.microsoft.com/en-us/entra/identity-platform/app-objects-and-service-principals)\nfor more information about service principals.)\n\n[code example]"},{"id":"mgd-identity","title":"Configuration for a managed identity","role":"configuration","text":"You can also authenticate to AMR using a managed identity (see the\n[Microsoft documentation](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview) to learn more about managed identities).\n\nFor a system assigned managed identity, simply add the `systemAssignedManagedIdentity()`\nmethod to the `EntraIDTokenAuthConfigBuilder` chain:\n\n[code example]\n\nFor a user assigned managed identity, add `userAssignedManagedIdentity()`. This\nrequires a member of the `UserManagedIdentityType` enum (to select a\n`CLIENT_ID`, `OBJECT_ID`, or `RESOURCE_ID`) as well as the `id` string itself:\n\n[code example]"},{"id":"connect-using-defaultjedisclientconfig","title":"Connect using `DefaultJedisClientConfig`","role":"content","text":"When you have created your `TokenAuthConfig` instance, you are ready to\nconnect to AMR.\nThe example below shows how to include the `TokenAuthConfig` details in a\n`JedisClientConfig` instance and use it with the `RedisClusterClient` connection.\nThe connection uses\n[Transport Layer Security (TLS)](https://en.wikipedia.org/wiki/Transport_Layer_Security),\nwhich is recommended and enabled by default for managed identities. See\n[Connect to your production Redis with TLS](https://redis.io/docs/latest/develop/clients/jedis/connect#connect-to-your-production-redis-with-tls) for more information about\nTLS connections, including the implementation of the `createSslSocketFactory()`\nmethod used in the example.\n\n[code example]"},{"id":"advanced-configuration-options","title":"Advanced configuration options","role":"content","text":"The `TokenAuthConfig` class has several other options that you can\nset with the `EntraIDTokenAuthConfigBuilder.builder()`. These give you\nmore precise control over the way the token is renewed:\n\n[code example]\n\nThese options are explained below:\n\n-  `expirationRefreshRatio`: a `float` value representing the fraction\n   of a token's lifetime that should elapse before attempting to\n   refresh it. For example, a value of 0.75 means that you want to\n   refresh the token after 75% of its lifetime has passed.\n-  `lowerRefreshBoundMillis`: the minimum amount of the token's lifetime\n   (in milliseconds) remaining before attempting to refresh, regardless\n   of the `expirationRefreshRatio` value. Set this to zero if you want\n   the refresh time to depend only on `expirationRefreshRatio`.\n-  `tokenRequestExecTimeoutInMs`: the maximum time (in milliseconds) to\n   wait for a token request to receive a response. A timeout occurs if this limit is exceeded.\n-  `maxAttemptsToRetry`: the maximum number of times to retry a token\n   request before aborting.\n-  `delayInMsToRetry`: the time (in milliseconds) to wait before\n    retrying a token request after a failed attempt. This provides a mechanism to request throttling to prevent an excessive number of token requests."}],"examples":[{"id":"install-ex0","language":"xml","code":"<dependency>\n    <groupId>redis.clients.authentication</groupId>\n    <artifactId>redis-authx-entraid</artifactId>\n    <version>0.1.1-beta1</version>\n</dependency>","section_id":"install"},{"id":"install-ex1","language":"bash","code":"implementation 'redis.clients.authentication:redis-authx-entraid:0.1.1-beta1'","section_id":"install"},{"id":"create-a-tokenauthconfig-instance-ex0","language":"java","code":"TokenAuthConfig authConfig = EntraIDTokenAuthConfigBuilder.builder()\n        .secret(\"<secret>\")\n        .authority(\"<authority>\")\n        // Other options...\n        .build();","section_id":"create-a-tokenauthconfig-instance"},{"id":"serv-principal-ex0","language":"java","code":"TokenAuthConfig authConfig = EntraIDTokenAuthConfigBuilder.builder()\n        .clientId(\"<CLIENT-ID>\")\n         // ...\n        .build();","section_id":"serv-principal"},{"id":"mgd-identity-ex0","language":"java","code":"TokenAuthConfig authConfig = EntraIDTokenAuthConfigBuilder.builder()\n        .systemAssignedManagedIdentity()\n         // ...\n        .build();","section_id":"mgd-identity"},{"id":"mgd-identity-ex1","language":"java","code":"TokenAuthConfig authConfig = EntraIDTokenAuthConfigBuilder.builder()\n        .userAssignedManagedIdentity(\n            UserManagedIdentityType.CLIENT_ID,\n            \"<ID>\"\n        )\n         // ...\n        .build();","section_id":"mgd-identity"},{"id":"connect-using-defaultjedisclientconfig-ex0","language":"java","code":"TokenAuthConfig authConfig = EntraIDTokenAuthConfigBuilder.builder()\n        // Chain of options...\n        .build();\n\nSSLSocketFactory sslFactory = createSslSocketFactory(\n        \"./truststore.jks\",\n        \"secret!\", // Use the password you specified for `keytool`\n        \"./redis-user-keystore.p12\",\n        \"secret!\" // Use the password you specified for `openssl`\n);\n\nJedisClientConfig config = DefaultJedisClientConfig.builder()\n        // Include the `TokenAuthConfig` details.\n        .authXManager(new AuthXManager(authConfig))\n        .ssl(true).sslSocketFactory(sslFactory)\n        .build();\n\n// AMR databases have clustering enabled by default.\nRedisClusterClient jedis = RedisClusterClient.builder()\n        .hostAndPort(new HostAndPort(\"<host>\", <port>))\n        .clientConfig(config)\n        .build();\n\n// Test the connection.\nSystem.out.println(String.format(\"Database size is %d\", jedis.dbSize()));","section_id":"connect-using-defaultjedisclientconfig"},{"id":"advanced-configuration-options-ex0","language":"java","code":"TokenAuthConfig authConfig = EntraIDTokenAuthConfigBuilder.builder()\n        .expirationRefreshRatio(0.75)\n        .lowerRefreshBoundMillis(100)\n        .tokenRequestExecTimeoutInMs(100)\n        .maxAttemptsToRetry(10)\n        .delayInMsToRetry()\n         // ...\n        .build();","section_id":"advanced-configuration-options"}]}
{"schema_version":2,"id":"develop/clients/jedis/connect","title":"Connect to the server","url":"https://redis.io/docs/latest/develop/clients/jedis/connect/","summary":"Connect your Java application to a Redis database","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-12T14:48:04+01:00","page_type":"content","content_hash":"8ecfd2d1ad131aad2e36e52f1dc89e3859281ebd510c85b47d905f7a922a495b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> Jedis 7.2.0 introduced a new client connection API:\n>\n> | New API class | Replaces | Use case |\n> | :-- | :-- | :-- |\n> | `RedisClient` | `UnifiedJedis`, `JedisPool`, `JedisPooled` | Single connection (with connection pooling) |\n> | `RedisClusterClient` | `JedisCluster` | Redis Cluster connections |\n> | `RedisSentinelClient` | `JedisSentinelPool` | Redis Sentinel connections |\n>\n> The old client classes are now considered deprecated."},{"id":"basic-connection","title":"Basic connection","role":"content","text":"The following code opens a basic connection to a local Redis server:\n\n[code example]\n\n> [!NOTE]\n> Jedis supports [connection URLs](https://redis.io/docs/latest/develop/clients/connection-urls#jedis-java) through `RedisClient.create()`.\n> A URL can set the host, port, credentials, database, and whether to use TLS,\n> so you can use one instead of passing those options separately.\n\nAfter you have connected, you can check the connection by storing and\nretrieving a simple string value:\n\n[code example]"},{"id":"connect-to-a-redis-cluster","title":"Connect to a Redis cluster","role":"content","text":"To connect to a Redis cluster, use `RedisClusterClient`. \n\n[code example]"},{"id":"connect-to-redis-sentinel","title":"Connect to Redis Sentinel","role":"content","text":"To connect through [Redis Sentinel](https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel),\nuse `RedisSentinelClient`. Pass the name of the primary that the Sentinels monitor to\n`masterName()`, and the Sentinel addresses to `sentinels()`. The client asks the Sentinels\nfor the current primary and switches to the new one after a failover.\n\n[code example]\n\nThe Sentinels and the data nodes have separate credentials. Pass a client configuration for\nthe primary to `clientConfig()`, and one for the Sentinels to `sentinelClientConfig()`:\n\n[code example]\n\n`RedisSentinelClient` always sends commands to the primary. It doesn't support reading\nfrom replicas."},{"id":"connect-to-your-production-redis-with-tls","title":"Connect to your production Redis with TLS","role":"content","text":"When you deploy your application, use TLS and follow the [Redis security](https://redis.io/docs/latest/operate/oss_and_stack/management/security) guidelines.\n\nBefore connecting your application to the TLS-enabled Redis server, ensure that your certificates and private keys are in the correct format.\n\nTo convert user certificate and private key from the PEM format to `pkcs12`, use this command:\n\n[code example]\n\nEnter password to protect your `pkcs12` file.\n\nConvert the server (CA) certificate to the JKS format using the [keytool](https://docs.oracle.com/en/java/javase/12/tools/keytool.html) shipped with JDK.\n\n[code example]\n\nEstablish a secure connection with your Redis database using this snippet.\n\n[code example]"},{"id":"connect-using-client-side-caching","title":"Connect using client-side caching","role":"content","text":"Client-side caching is a technique to reduce network traffic between\nthe client and server, resulting in better performance. See\n[Client-side caching introduction](https://redis.io/docs/latest/develop/clients/client-side-caching)\nfor more information about how client-side caching works and how to use it effectively.\n\nTo enable client-side caching, specify the\n[RESP3](https://redis.io/docs/latest/develop/reference/protocol-spec#resp-versions)\nprotocol and pass a cache configuration object during the connection.\n\nThe example below shows the simplest client-side caching connection to the default host and port,\n`localhost:6379`.\nAll of the connection variants described above accept these parameters, so you can\nuse client-side caching with a connection pool or a cluster connection in exactly the same way.\n\n> [!NOTE]\n> Client-side caching requires Jedis v5.2.0 or later.\n> To maximize compatibility with all Redis products, client-side caching\n> is supported by Redis v7.4 or later.\n>\n> The [Redis server products](https://redis.io/docs/latest/operate) support\n> [opt-in/opt-out](https://redis.io/docs/latest/develop/reference/client-side-caching#opt-in-and-opt-out-caching) mode\n> and [broadcasting mode](https://redis.io/docs/latest/develop/reference/client-side-caching#broadcasting-mode)\n> for CSC, but these modes are not currently implemented by Jedis.\n\n[code example]\n\nOnce you have connected, the usual Redis commands will work transparently\nwith the cache:\n\n[code example]\n\nYou can see the cache working if you connect to the same Redis database\nwith [`redis-cli`](https://redis.io/docs/latest/develop/tools/cli) and run the\n[`MONITOR`](https://redis.io/docs/latest/commands/monitor) command. If you run the\ncode above but without passing `cacheConfig` during the connection,\nyou should see the following in the CLI among the output from `MONITOR`:\n\n[code example]\n\nThe server responds to both `get(\"city\")` calls.\nIf you run the code with `cacheConfig` added in again, you will see\n\n[code example]\n\nThe first `get(\"city\")` call contacted the server, but the second\ncall was satisfied by the cache."},{"id":"removing-items-from-the-cache","title":"Removing items from the cache","role":"content","text":"You can remove individual keys from the cache with the\n`deleteByRedisKey()` method of the cache object. This removes all cached items associated\nwith each specified key, so all results from multi-key commands (such as\n[`MGET`](https://redis.io/docs/latest/commands/mget)) and composite data structures\n(such as [hashes](https://redis.io/docs/latest/develop/data-types/hashes)) will be\ncleared at once. The example below shows the effect of removing a single\nkey from the cache:\n\n[code example]\n\nYou can also clear all cached items using the `flush()`\nmethod:\n\n[code example]\n\nThe client will also flush the cache automatically\nif any connection (including one from a connection pool)\nis disconnected."},{"id":"configure-a-connection-pool","title":"Configure a connection pool","role":"configuration","text":"A connection pool maintains several open connections and reuses them\nefficiently. When you open a connection from a pool, the pool allocates\none of its open connections. When you subsequently close the same connection,\nit is not actually closed but simply returned to the pool for reuse.\nThis avoids the overhead of repeated connecting and disconnecting.\nSee\n[Connection pools and multiplexing](https://redis.io/docs/latest/develop/clients/pools-and-muxing)\nfor more information.\n\nFrom Jedis 7.2.0, the `RedisClient` class provides connection pooling automatically,\nbut it's important to configure the connection pool correctly to get the best performance. \nEach pool holds a specified number of connections, creates more connections when necessary, and terminates them when they are no longer needed.\n\nHere is a simplified connection lifecycle in a pool:\n\n1. A connection is requested from the pool.\n2. A connection is served:\n   - An idle connection is served when non-active connections are available, or \n   - A new connection is created when the number of connections is under `maxTotal`. \n3. The connection becomes active.\n4. The connection is released back to the pool.\n5. The connection is marked as stale. \n6. The connection is kept idle for `minEvictableIdleTime`. \n7. The connection becomes evictable if the number of connections is greater than `minIdle`. \n8. The connection is ready to be closed.\n\nPass a `ConnectionPoolConfig` object to the `RedisClient` builder to configure the pool. \n(`ConnectionPoolConfig` is a wrapper around `GenericObjectPoolConfig` from [Apache Commons Pool2](https://commons.apache.org/proper/commons-pool/apidocs/org/apache/commons/pool2/impl/GenericObjectPoolConfig.html)).\n\n[code example]"},{"id":"retrying-a-command-after-a-connection-failure","title":"Retrying a command after a connection failure","role":"content","text":"If a connection is lost before a command is completed, the command will fail with a `JedisConnectionException`. Although the connection pool manages the connections\nfor you, you must request a new connection from the pool to retry the command.\nYou would typically do this in a loop that makes several attempts to reconnect\nbefore aborting and reporting that the error isn't transient. The example below\nshows a retry loop that uses a simple\n[exponential backoff](https://en.wikipedia.org/wiki/Exponential_backoff)\nstrategy:\n\n[code example]"}],"examples":[{"id":"basic-connection-ex0","language":"java","code":"package org.example;\nimport redis.clients.jedis.RedisClient;\n\npublic class Main {\n    public static void main(String[] args) {\n        RedisClient jedis = RedisClient.create(\"redis://localhost:6379\");\n\n        // Code that interacts with Redis...\n\n        jedis.close();\n    }\n}","section_id":"basic-connection"},{"id":"basic-connection-ex1","language":"java","code":"...\n\nString res1 = jedis.set(\"bike:1\", \"Deimos\");\nSystem.out.println(res1); // OK\n\nString res2 = jedis.get(\"bike:1\");\nSystem.out.println(res2); // Deimos\n\n...","section_id":"basic-connection"},{"id":"connect-to-a-redis-cluster-ex0","language":"java","code":"import redis.clients.jedis.RedisClusterClient;\nimport redis.clients.jedis.HostAndPort;\n\n//...\n\nSet<HostAndPort> jedisClusterNodes = new HashSet<HostAndPort>();\njedisClusterNodes.add(new HostAndPort(\"127.0.0.1\", 7379));\njedisClusterNodes.add(new HostAndPort(\"127.0.0.1\", 7380));\nRedisClusterClient jedis = RedisClusterClient.create(jedisClusterNodes);","section_id":"connect-to-a-redis-cluster"},{"id":"connect-to-redis-sentinel-ex0","language":"java","code":"import redis.clients.jedis.HostAndPort;\nimport redis.clients.jedis.RedisSentinelClient;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\n//...\n\nSet<HostAndPort> sentinels = new HashSet<>();\nsentinels.add(new HostAndPort(\"localhost\", 26379));\nsentinels.add(new HostAndPort(\"localhost\", 26380));\nsentinels.add(new HostAndPort(\"localhost\", 26381));\n\nRedisSentinelClient jedis = RedisSentinelClient.builder()\n        .masterName(\"mymaster\")\n        .sentinels(sentinels)\n        .build();\n\njedis.set(\"foo\", \"bar\");\nSystem.out.println(jedis.get(\"foo\")); // bar\n\njedis.close();","section_id":"connect-to-redis-sentinel"},{"id":"connect-to-redis-sentinel-ex1","language":"java","code":"import redis.clients.jedis.DefaultJedisClientConfig;\nimport redis.clients.jedis.JedisClientConfig;\n\n//...\n\n// Credentials for the primary and replica nodes.\nJedisClientConfig masterConfig = DefaultJedisClientConfig.builder()\n        .password(\"secret!\") // use your Redis password\n        .build();\n\n// Credentials for the Sentinel nodes.\nJedisClientConfig sentinelConfig = DefaultJedisClientConfig.builder()\n        .password(\"sentinelSecret!\") // use your Sentinel password\n        .build();\n\nRedisSentinelClient jedis = RedisSentinelClient.builder()\n        .masterName(\"mymaster\")\n        .sentinels(sentinels)\n        .clientConfig(masterConfig)\n        .sentinelClientConfig(sentinelConfig)\n        .build();","section_id":"connect-to-redis-sentinel"},{"id":"connect-to-your-production-redis-with-tls-ex0","language":"plaintext","code":"openssl pkcs12 -export -in ./redis_user.crt -inkey ./redis_user_private.key -out redis-user-keystore.p12 -name \"redis\"","section_id":"connect-to-your-production-redis-with-tls"},{"id":"connect-to-your-production-redis-with-tls-ex1","language":"plaintext","code":"keytool -importcert -keystore truststore.jks \\ \n  -storepass REPLACE_WITH_YOUR_PASSWORD \\\n  -file redis_ca.pem","section_id":"connect-to-your-production-redis-with-tls"},{"id":"connect-to-your-production-redis-with-tls-ex2","language":"java","code":"package org.example;\n\nimport redis.clients.jedis.*;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.security.GeneralSecurityException;\n\npublic class Main {\n\n    public static void main(String[] args) throws GeneralSecurityException, IOException {\n        HostAndPort address = new HostAndPort(\"my-redis-instance.cloud.redislabs.com\", 6379);\n\n        SslOptions sslOptions = SslOptions.builder()\n                .truststore(new File(\"./truststore.jks\"), \"secret!\".toCharArray())\n                .trustStoreType(\"jks\")\n                // optional: use if Redis requires client certificate\n                .keystore(new File(\"./redis-user-keystore.p12\"), \"secret!\".toCharArray())\n                .keyStoreType(\"pkcs12\")\n                .build();\n\n        JedisClientConfig config = DefaultJedisClientConfig.builder()\n                .sslOptions(sslOptions)\n                .user(\"default\") // use your Redis user. More info https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl/\n                .password(\"secret!\") // use your Redis password\n                .build();\n\n        RedisClient jedis = RedisClient.builder()\n              .hostAndPort(address)\n              .clientConfig(config)\n              .build();\n        jedis.set(\"foo\", \"bar\");\n        System.out.println(jedis.get(\"foo\")); // prints bar\n    }\n}","section_id":"connect-to-your-production-redis-with-tls"},{"id":"connect-using-client-side-caching-ex0","language":"java","code":"HostAndPort endpoint = new HostAndPort(\"localhost\", 6379);\n\nDefaultJedisClientConfig config = DefaultJedisClientConfig\n    .builder()\n    .password(\"secretPassword\")\n    .protocol(RedisProtocol.RESP3)\n    .build();\n\nCacheConfig cacheConfig = CacheConfig.builder().maxSize(1000).build();\n\nRedisClient client = RedisClient.builder()\n        .hostAndPort(endpoint)\n        .clientConfig(config)\n        .cacheConfig(cacheConfig)\n        .build();","section_id":"connect-using-client-side-caching"},{"id":"connect-using-client-side-caching-ex1","language":"java","code":"client.set(\"city\", \"New York\");\nclient.get(\"city\");     // Retrieved from Redis server and cached\nclient.get(\"city\");     // Retrieved from cache","section_id":"connect-using-client-side-caching"},{"id":"connect-using-client-side-caching-ex2","language":"plaintext","code":"1723109720.268903 [...] \"SET\" \"city\" \"New York\"\n1723109720.269681 [...] \"GET\" \"city\"\n1723109720.270205 [...] \"GET\" \"city\"","section_id":"connect-using-client-side-caching"},{"id":"connect-using-client-side-caching-ex3","language":"plaintext","code":"1723110248.712663 [...] \"SET\" \"city\" \"New York\"\n1723110248.713607 [...] \"GET\" \"city\"","section_id":"connect-using-client-side-caching"},{"id":"removing-items-from-the-cache-ex0","language":"java","code":"client.hget(\"person:1\", \"name\");    // Read from the server\nclient.hget(\"person:1\", \"name\");    // Read from the cache\n\nclient.hget(\"person:2\", \"name\");    // Read from the server\nclient.hget(\"person:2\", \"name\");    // Read from the cache\n\nCache myCache = client.getCache();\nmyCache.deleteByRedisKey(\"person:1\");\n\nclient.hget(\"person:1\", \"name\");    // Read from the server\nclient.hget(\"person:1\", \"name\");    // Read from the cache\n\nclient.hget(\"person:2\", \"name\");    // Still read from the cache","section_id":"removing-items-from-the-cache"},{"id":"removing-items-from-the-cache-ex1","language":"java","code":"client.hget(\"person:1\", \"name\");    // Read from the server\nclient.hget(\"person:1\", \"name\");    // Read from the cache\n\nclient.hget(\"person:2\", \"name\");    // Read from the server\nclient.hget(\"person:2\", \"name\");    // Read from the cache\n\nCache myCache = client.getCache();\nmyCache.flush();\n\nclient.hget(\"person:1\", \"name\");    // Read from the server\nclient.hget(\"person:1\", \"name\");    // Read from the cache\n\nclient.hget(\"person:2\", \"name\");    // Read from the server\nclient.hget(\"person:2\", \"name\");    // Read from the cache","section_id":"removing-items-from-the-cache"},{"id":"configure-a-connection-pool-ex0","language":"java","code":"ConnectionPoolConfig poolConfig = new ConnectionPoolConfig();\n// maximum active connections in the pool,\n// tune this according to your needs and application type\n// default is 8\npoolConfig.setMaxTotal(8);\n\n// maximum idle connections in the pool, default is 8\npoolConfig.setMaxIdle(8);\n// minimum idle connections in the pool, default 0\npoolConfig.setMinIdle(0);\n\n// Enables waiting for a connection to become available.\npoolConfig.setBlockWhenExhausted(true);\n// The maximum number of seconds to wait for a connection to become available\npoolConfig.setMaxWait(Duration.ofSeconds(1));\n\n// Enables sending a PING command periodically while the connection is idle.\npoolConfig.setTestWhileIdle(true);\n// controls the period between checks for idle connections in the pool\npoolConfig.setTimeBetweenEvictionRuns(Duration.ofSeconds(1));\n\nRedisClient jedis = RedisClient.builder()\n        .hostAndPort(\"localhost\", 6379)\n        .poolConfig(poolConfig)\n        .build();","section_id":"configure-a-connection-pool"},{"id":"retrying-a-command-after-a-connection-failure-ex0","language":"java","code":"RedisClient jedis = RedisClient.builder()\n        .hostAndPort(\"localhost\", 6379)\n        .poolConfig(poolConfig)\n        .clientConfig(config)\n        .build();\n\n// Set max retry attempts\nfinal int MAX_RETRIES = 5;\n\n// Example of retrying a command\nString key = \"retry-example\";\nString value = \"success\";\n\nint attempts = 0;\nboolean success = false;\n\nwhile (!success && attempts < MAX_RETRIES) {\n    try {\n        attempts++;\n        String result = jedis.set(key, value);\n        System.out.println(\"Command succeeded on attempt \" + attempts + \": \" + result);\n        success = true;\n    } catch (JedisConnectionException e) {\n        System.out.println(\"Connection failed on attempt \" + attempts + \": \" + e.getMessage());\n        if (attempts >= MAX_RETRIES) {\n            System.out.println(\"Max retries reached. Giving up.\");\n            throw e;\n        }\n\n        // Wait before retrying\n        try {\n            Thread.sleep(500 * attempts); // Exponential backoff\n        } catch (InterruptedException ie) {\n            Thread.currentThread().interrupt();\n        }\n    }\n}","section_id":"retrying-a-command-after-a-connection-failure"}]}
{"schema_version":2,"id":"develop/clients/jedis/error-handling","title":"Error handling","url":"https://redis.io/docs/latest/develop/clients/jedis/error-handling/","summary":"Learn how to handle errors when using Jedis.","tags":[],"last_updated":"2026-09-08T15:41:24+01:00","page_type":"content","content_hash":"6d722a9564d88704beed33ee67d23b26c2b5fc10ad0e7478d268590d777d29db","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Jedis uses **exceptions** to signal errors. Code examples in the documentation often omit error handling for brevity, but it is essential in production code. This page explains how Jedis's error handling works and how to apply common error handling patterns.\n\nFor an overview of error types and handling strategies, see [Error handling](https://redis.io/docs/latest/develop/clients/error-handling).\nSee also [Production usage](https://redis.io/docs/latest/develop/clients/jedis/produsage)\nfor more information on connection management, timeouts, and other aspects of\napp reliability."},{"id":"exception-hierarchy","title":"Exception hierarchy","role":"errors","text":"Jedis organizes exceptions in a hierarchy rooted at `JedisException`, which extends `RuntimeException`. All Jedis exceptions are unchecked exceptions:\n\n[code example]"},{"id":"key-exceptions","title":"Key exceptions","role":"content","text":"The following exceptions are the most commonly encountered in Jedis applications.\nSee [Categories of errors](https://redis.io/docs/latest/develop/clients/error-handling#categories-of-errors)\nfor a more detailed discussion of these errors and their causes.\n\n| Exception | When it occurs | Recoverable | Recommended action |\n|---|---|---|---|\n| `JedisConnectionException` | Connection lost or closed unexpectedly | ✅ | Retry with backoff or fall back |\n| `JedisAccessControlException` | Authentication failure or permission denied | ❌ | Fix credentials or permissions |\n| `JedisDataException` | Problem with data being sent or received | ❌ | Fix the data or command |\n| `JedisException` | Unexpected errors (catch-all) | ❌ | Log and investigate |"},{"id":"applying-error-handling-patterns","title":"Applying error handling patterns","role":"content","text":"The [Error handling](https://redis.io/docs/latest/develop/clients/error-handling) overview\ndescribes four main patterns. The sections below show how to implement them in\nJedis:"},{"id":"pattern-1-fail-fast","title":"Pattern 1: Fail fast","role":"content","text":"Catch specific exceptions that represent unrecoverable errors and re-throw them (see\n[Pattern 1: Fail fast](https://redis.io/docs/latest/develop/clients/error-handling#pattern-1-fail-fast)\nfor a full description):\n\n[code example]"},{"id":"pattern-2-graceful-degradation","title":"Pattern 2: Graceful degradation","role":"content","text":"Catch specific errors and fall back to an alternative, where possible (see\n[Pattern 2: Graceful degradation](https://redis.io/docs/latest/develop/clients/error-handling#pattern-2-graceful-degradation)\nfor a full description):\n\n[code example]"},{"id":"pattern-3-retry-with-backoff","title":"Pattern 3: Retry with backoff","role":"content","text":"Retry on temporary errors like connection failures (see\n[Pattern 3: Retry with backoff](https://redis.io/docs/latest/develop/clients/error-handling#pattern-3-retry-with-backoff)\nfor a full description):\n\n[code example]"},{"id":"pattern-4-log-and-continue","title":"Pattern 4: Log and continue","role":"content","text":"Log non-critical errors and continue (see\n[Pattern 4: Log and continue](https://redis.io/docs/latest/develop/clients/error-handling#pattern-4-log-and-continue)\nfor a full description):\n\n[code example]"},{"id":"see-also","title":"See also","role":"related","text":"- [Error handling](https://redis.io/docs/latest/develop/clients/error-handling)\n- [Production usage](https://redis.io/docs/latest/develop/clients/jedis/produsage)"}],"examples":[{"id":"exception-hierarchy-ex0","language":"hierarchy {type=\"exception\"}","code":"\"JedisException\":\n    _meta:\n        description: \"Base class for all Jedis exceptions\"\n    \"JedisDataException\":\n        \"JedisRedirectionException\":\n            \"JedisMovedDataException\":\n            \"JedisAskDataException\":\n        \"AbortedTransactionException\":\n        \"JedisAccessControlException\":\n        \"JedisNoScriptException\":\n    \"JedisClusterException\":\n        \"JedisClusterOperationException\":\n        \"JedisConnectionException\":\n        \"JedisValidationException\":\n    \"InvalidURIException\":","section_id":"exception-hierarchy"},{"id":"pattern-1-fail-fast-ex0","language":"java","code":"try (RedisClient jedis = RedisClient.create()) {\n    String result = jedis.get(key);\n} catch (JedisDataException e) {\n    // This indicates a bug in our code\n    throw e;\n}","section_id":"pattern-1-fail-fast"},{"id":"pattern-2-graceful-degradation-ex0","language":"java","code":"try (RedisClient jedis = RedisClient.create()) {\n    String cachedValue = jedis.get(key);\n    if (cachedValue != null) {\n        return cachedValue;\n    }\n} catch (JedisConnectionException e) {\n    logger.warn(\"Cache unavailable, using database\");\n    return database.get(key);\n}\n\n// Fallback to database\nreturn database.get(key);","section_id":"pattern-2-graceful-degradation"},{"id":"pattern-3-retry-with-backoff-ex0","language":"java","code":"int maxRetries = 3;\nint retryDelay = 100;\n\nfor (int attempt = 0; attempt < maxRetries; attempt++) {\n    try (RedisClient jedis = RedisClient.create()) {\n        return jedis.get(key);\n    } catch (JedisConnectionException e) {\n        if (attempt < maxRetries - 1) {\n            try {\n                Thread.sleep(retryDelay);\n                retryDelay *= 2;  // Exponential backoff\n            } catch (InterruptedException ie) {\n                Thread.currentThread().interrupt();\n                throw new RuntimeException(ie);\n            }\n        } else {\n            throw e;\n        }\n    }\n}","section_id":"pattern-3-retry-with-backoff"},{"id":"pattern-4-log-and-continue-ex0","language":"java","code":"try (RedisClient jedis = RedisClient.create()) {\n    jedis.setex(key, 3600, value);\n} catch (JedisConnectionException e) {\n    logger.warn(\"Failed to cache \" + key + \", continuing without cache\");\n    // Application continues normally\n}","section_id":"pattern-4-log-and-continue"}]}
{"schema_version":2,"id":"develop/clients/jedis/failover","title":"Client-side geographic failover","url":"https://redis.io/docs/latest/develop/clients/jedis/failover/","summary":"Improve reliability using the failover features of Jedis.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-08T15:41:24+01:00","page_type":"content","content_hash":"ae08adc51cebc30a56c8fab66af0a1611768395a6e423b87b4e6de68f48a1c14","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Jedis supports [Client-side geographic failover](https://en.wikipedia.org/wiki/Failover)\nto improve the availability of connections to Redis databases. This page explains\nhow to configure Jedis for failover. For an overview of the concepts,\nsee the main [Client-side geographic failover](https://redis.io/docs/latest/develop/clients/failover) page."},{"id":"failover-configuration","title":"Failover configuration","role":"content","text":"Jedis uses the [resilience4j](https://resilience4j.readme.io/docs/getting-started)\nlibrary to detect connection problems using a\n[circuit breaker design pattern](https://en.wikipedia.org/wiki/Circuit_breaker_design_pattern).\n\nThe example below shows a simple case with a list of two servers,\n`redis-east` and `redis-west`, where `redis-east` is the preferred\ntarget. If `redis-east` fails, Jedis should fail over to\n`redis-west`.\n\n> [!NOTE]\n> Jedis v6 supported failover/failback using a special\n> `UnifiedJedis` constructor. You should update existing code to use\n> the approach shown below for Jedis v7 and later.\n\nFirst, add the `resilience4j` dependencies to your project. If you\nare using [Maven](https://maven.apache.org/), add the following\ndependencies to your `pom.xml` file:\n\n[code example]\n\nIf you are using [Gradle](https://gradle.org/), add the following\ndependencies to your `build.gradle` file:\n\n[code example]\n\nIn your source file, create some simple configuration for the client and\n[connection pool](https://redis.io/docs/latest/develop/clients/jedis/connect#connect-with-a-connection-pool),\nas you would for a standard connection.\n\n[code example]\n\nSupply the weighted list of endpoints using the `MultiDbConfig` builder\n(see [Selecting a failover target](https://redis.io/docs/latest/develop/clients/failover#selecting-a-failover-target) for a full description of how\nthe weighted list is used).\nUse the `weight` option to order the endpoints, with the highest\nweight being tried first.\n\n[code example]\n\nThe builder lets you add several options to configure the\n[circuit breaker](#circuit-breaker-configuration) behavior\nand [retries](#retry-configuration) (these are explained in more detail below).\n\n[code example]\n\nFinally, use the configuration to build the `MultiDbClient`.\n\n[code example]\n\nLike `UnifiedJedis`, `MultiDbClient` implements the usual Redis commands,\nbut will also handle the connection management and failover transparently."},{"id":"circuit-breaker-configuration","title":"Circuit breaker configuration","role":"content","text":"The `MultiDbConfig.CircuitBreakerConfig` builder lets you pass several options to configure\nthe circuit breaker (see [Detecting connection problems](https://redis.io/docs/latest/develop/clients/failover#detecting-connection-problems) for more information on how the\ncircuit breaker works):\n\n| Builder method | Default value | Description|\n| --- | --- | --- |\n| `slidingWindowSize()` | `2` | Duration in seconds to keep failures and successes in the sliding window. |\n| `minNumOfFailures()` | `1000` | Minimum number of failures that must occur before the circuit breaker is tripped. |\n| `failureRateThreshold()` | `10.0f` | Percentage of failures to trigger the circuit breaker. |\n| `includedExceptionList()` | See description | `List` of `Throwable` classes that should be considered as failures. By default, it includes just `JedisConnectionException`. |\n| `ignoreExceptionList()` | `null` | `List` of `Throwable` classes that should be ignored for failure rate calculation. |"},{"id":"retry-configuration","title":"Retry configuration","role":"content","text":"The `MultiDbConfig.RetryConfig` builder has the following options to configure retries:\n\n| Builder method | Default value | Description|\n| --- | --- | --- |\n| `maxAttempts()` | `3` | Maximum number of retry attempts (including the initial call). Set to `1` to disable retries. |\n| `waitDuration()` | `500` | Initial number of milliseconds to wait between retry attempts. |\n| `exponentialBackoffMultiplier()` | `2` | [Exponential backoff](https://en.wikipedia.org/wiki/Exponential_backoff) factor multiplied by the wait duration between retries. For example, with a wait duration of 1 second and a multiplier of 2, the retries would occur after 1s, 2s, 4s, 8s, 16s, and so on. |\n| `includedExceptionList()` | See description | `List` of `Throwable` classes that should be considered as failures to be retried. By default, it includes just `JedisConnectionException`. |\n| `ignoreExceptionList()` | `null` | `List` of `Throwable` classes that should be ignored for retry. |"},{"id":"general-failover-configuration","title":"General failover configuration","role":"content","text":"The `MultiDbConfig` builder also has the following options to configure general failover behavior:\n\n| Builder method | Default value | Description|\n| --- | --- | --- |\n| `maxNumFailoverAttempts()` | `10` | Number of attempts to fail over to a new endpoint before giving up. |\n| `delayInBetweenFailoverAttempts()` | `12000` | Time interval in milliseconds between successive failover attempts. |\n| `gracePeriod()` | `60000` | Time interval in milliseconds to keep the unhealthy endpoint disabled even if it recovers (this prevents rapid oscillation between endpoints when intermittent faults occur). |\n| `fastFailover()` | `false` | If true, existing connections to an unhealthy endpoint are forced to close immediately when a failover occurs, otherwise the connections are closed gracefully. Forcefully closing connections can make failover faster, but it might also cause in-flight operations to fail. |\n| `retryOnFailover()` | `false` | If true, commands that fail during a failover are automatically retried on the replacement endpoint. |"},{"id":"failover-callbacks","title":"Failover callbacks","role":"content","text":"You may want to take some custom action when a failover occurs.\nFor example, you could log a warning, increment a metric, \nor externally persist the cluster connection state.\n\nYou can provide a custom failover action using a class that\nimplements `java.util.function.Consumer`. Place\nthe custom action in the `accept()` method, as shown in the example below.\n\n[code example]\n\nUse the `databaseSwitchListener()` method of the `MultiDbClient` builder to\nregister your custom action:\n\n[code example]\n\nYour `accept()` method is now called whenever a failover occurs.\n\nSince `Consumer` is a functional interface, you can also use a lambda\nexpression to supply the custom action directly.\n\n[code example]"},{"id":"failback-configuration","title":"Failback configuration","role":"content","text":"The `MultiDbConfig` builder lets you configure the failback behavior using the following options:\n\n| Builder method | Default value | Description|\n| --- | --- | --- |\n| `failbackSupported()` | `true` | Enable/disable failback (it is enabled by default). |\n| `failbackCheckInterval()` | `120000` | Interval in milliseconds to check if the unhealthy database has recovered. |"},{"id":"health-check-configuration","title":"Health check configuration","role":"content","text":"Each health check consists of one or more separate \"probes\", each of which is a simple\ntest (such as a [`PING`](https://redis.io/docs/latest/commands/ping) command) to determine if the database is available. The results of the separate probes are combined\nusing a configurable policy to determine if the database is healthy.\n\nThere are several strategies available for health checks that you can deploy using the\n`MultiDbConfig` builder. Each strategy is a class that implements the `HealthCheckStrategy` \ninterface. Use the constructor of a `HealthCheckStrategy` implementation to pass\na `HealthCheckStrategy.Config` object to configure the health check behavior.\nThe methods of the base `HealthCheckStrategy.Config` builder are shown below.\nNote that some strategies (including your own custom strategies) may use a\nsubclass of `HealthCheckStrategy.Config` to provide extra options.\n\n| Builder method | Default value | Description|\n| --- | --- | --- |\n| `interval()` | `1000` | Interval in milliseconds between health checks. |\n| `timeout()` | `1000` | Timeout in milliseconds for health check requests. |\n| `numProbes()` | `3` | Number of probes to perform during each health check. |\n| `delayInBetweenProbes()` | `100` | Delay in milliseconds between probes during a health check. |\n| `policy()` | `ProbingPolicy.BuiltIn.ALL_SUCCESS` | Policy to determine if the database is healthy based on the probe results. The options are `ALL_SUCCESS` (all probes must succeed), `ANY_SUCCESS` (at least one probe must succeed), and `MAJORITY_SUCCESS` (majority of probes must succeed). |\n\nThe sections below explain the available strategies in more detail."},{"id":"pingstrategy-default","title":"`PingStrategy` (default)","role":"content","text":"The default strategy, `PingStrategy`, periodically sends a Redis\n[`PING`](https://redis.io/docs/latest/commands/ping) command\nand checks that it gives the expected response. Any unexpected response\nor exception indicates an unhealthy server. Although `PingStrategy` is\nvery simple, it is a good basic approach for most Redis deployments.\n\nAlthough `PingStrategy` is the default, you can still activate it\nexplicitly using the `healthCheckStrategy()` method of the `MultiDbConfig.DatabaseConfig`\nbuilder. Use this approach if you want to configure the default\n`PingStrategy` with custom options, as shown in the example below.\n\n[code example]"},{"id":"lagawarestrategy-preview","title":"`LagAwareStrategy` (preview)","role":"content","text":"`LagAwareStrategy` (currently in preview) is designed specifically for\nRedis Software [Active-Active](https://redis.io/docs/latest/operate/rs/databases/active-active)\ndeployments. It uses the Redis Software REST API to check database availability\nand can also optionally check replication lag.\n\n`LagAwareStrategy` determines the health of the server using the\n[REST API](https://redis.io/docs/latest/operate/rs/references/rest-api). The example\nbelow shows how to configure `LagAwareStrategy` and activate it using\nthe `healthCheckStrategy()` method of the `MultiDbConfig.DatabaseConfig`\nbuilder.\n\n[code example]\n\nThe `LagAwareStrategy.Config` builder has the following options in\naddition to the standard options provided by `HealthCheckStrategy.Config`:\n\n| Builder method | Default value | Description|\n| --- | --- | --- |\n| `sslOptions()` | `null` | Standard SSL options for connecting to the REST API. |\n| `extendedCheckEnabled()` | `false` | Enable extended lag checking (this includes lag validation in addition to the standard datapath validation). |\n| `availabilityLagTolerance()` | `100` | Maximum lag tolerance in milliseconds for extended lag checking. |"},{"id":"custom-health-check-strategy","title":"Custom health check strategy","role":"content","text":"You can supply your own custom health check strategy by\nimplementing the `HealthCheckStrategy` interface. For example, you might\nuse this to integrate with external monitoring tools or to implement\nchecks that are specific to your application. The example below\nshows a simple custom strategy. Pass your custom strategy implementation to the `MultiDbConfig.DatabaseConfig`\nbuilder with the `healthCheckStrategySupplier()` method.\n\n[code example]"},{"id":"disable-health-checks","title":"Disable health checks","role":"content","text":"To disable health checks completely, use the\n`healthCheckEnabled()` method of the `MultiDbConfig.DatabaseConfig`\nbuilder:\n\n[code example]"},{"id":"managing-databases-at-runtime","title":"Managing databases at runtime","role":"content","text":"Although you will typically configure all databases during the initial connection, you can also modify the configuration at runtime. The example below shows how to add and remove database endpoints.\n\n[code example]"},{"id":"manual-failback","title":"Manual failback","role":"content","text":"By default, the failback mechanism runs health checks on all servers in the\nweighted list and selects the highest-weighted server that is\nhealthy. However, you can also use the `setActiveDatabase()` method of\n`MultiDbClient` to select which database to use manually:\n\n[code example]\n\nNote that `setActiveDatabase()` is thread-safe.\n\nIf you decide to implement manual failback, you will need a way for external\nsystems to trigger this method in your application. For example, if your application\nexposes a REST API, you might consider creating a REST endpoint to call\n`setActiveDatabase()`."},{"id":"behavior-when-all-endpoints-are-unhealthy","title":"Behavior when all endpoints are unhealthy","role":"content","text":"In the extreme case where no endpoint is healthy, a command will throw a\n`JedisTemporarilyNotAvailableException`. This indicates that the client is periodically\nchecking to see if any endpoint becomes healthy again. The number of\ntimes it will keep checking is configured by the `maxNumFailoverAttempts()` option in\nthe `MultiDbConfig` builder and\nthe delay between attempts is configured by the `delayInBetweenFailoverAttempts()` option (see [General failover configuration](#general-failover-configuration)). With the default settings,\n`maxNumFailoverAttempts` * `delayInBetweenFailoverAttempts`  gives a period of 120 seconds to find\na healthy endpoint.\n\nYou can still keep retrying commands after a `JedisTemporarilyNotAvailableException` is thrown (for example,\nyou could add this exception to the `includedExceptionList`, as described\nin the [Retry configuration](#retry-configuration) section). However, if the number of\nfailover attempts exceeds the value set by `maxNumFailoverAttempts()`, commands will throw a `JedisPermanentlyNotAvailableException`. Note that this is intended to notify your app\nthat the problem is likely to be persistent, but it *doesn't* mean that Jedis will stop trying\nto connect to a healthy endpoint if one becomes available."},{"id":"troubleshooting","title":"Troubleshooting","role":"errors","text":"This section lists some common problems and their solutions."},{"id":"excessive-or-constant-health-check-failures","title":"Excessive or constant health check failures","role":"content","text":"If all health checks fail, you should first rule out authentication\nproblems with the Redis server and also make sure there are no persistent\nnetwork connectivity problems. If you still see frequent or constant\nfailures, try increasing the timeout for health checks and the\ninterval between them:\n\n[code example]"},{"id":"slow-failback-after-recovery","title":"Slow failback after recovery","role":"content","text":"If failback is too slow after a server recovers, you can try\nincreasing the frequency of health checks and reducing the grace\nperiod before failback is attempted (the grace period is the\nminimum time after a failover before Jedis will check if a\nfailback is possible).\n\n[code example]"}],"examples":[{"id":"failover-configuration-ex0","language":"xml","code":"<dependency>\n    <groupId>io.github.resilience4j</groupId>\n    <artifactId>resilience4j-all</artifactId>\n    <version>1.7.1</version>\n</dependency>\n<dependency>\n    <groupId>io.github.resilience4j</groupId>\n    <artifactId>resilience4j-circuitbreaker</artifactId>\n    <version>1.7.1</version>\n</dependency>\n<dependency>\n    <groupId>io.github.resilience4j</groupId>\n    <artifactId>resilience4j-retry</artifactId>\n    <version>1.7.1</version>\n</dependency>","section_id":"failover-configuration"},{"id":"failover-configuration-ex1","language":"bash","code":"compileOnly 'io.github.resilience4j:resilience4j-resilience4j-all:1.7.1'\ncompileOnly 'io.github.resilience4j:resilience4j-circuitbreaker:1.7.1'\ncompileOnly 'io.github.resilience4j:resilience4j-retry:1.7.1'","section_id":"failover-configuration"},{"id":"failover-configuration-ex2","language":"java","code":"JedisClientConfig config = DefaultJedisClientConfig.builder().user(\"<username>\").password(\"<password>\")\n    .socketTimeoutMillis(5000).connectionTimeoutMillis(5000).build();\n\nConnectionPoolConfig poolConfig = new ConnectionPoolConfig();\npoolConfig.setMaxTotal(8);\npoolConfig.setMaxIdle(8);\npoolConfig.setMinIdle(0);\npoolConfig.setBlockWhenExhausted(true);\npoolConfig.setMaxWait(Duration.ofSeconds(1));\npoolConfig.setTestWhileIdle(true);\npoolConfig.setTimeBetweenEvictionRuns(Duration.ofSeconds(1));","section_id":"failover-configuration"},{"id":"failover-configuration-ex3","language":"java","code":"HostAndPort east = new HostAndPort(\"redis-east.example.com\", 14000);\nHostAndPort west = new HostAndPort(\"redis-west.example.com\", 14000);\n\nMultiDbConfig.Builder multiConfig = MultiDbConfig.builder()\n        .database(DatabaseConfig.builder(east, config).connectionPoolConfig(poolConfig).weight(1.0f).build())\n        .database(DatabaseConfig.builder(west, config).connectionPoolConfig(poolConfig).weight(0.5f).build());","section_id":"failover-configuration"},{"id":"failover-configuration-ex4","language":"java","code":"// Configure circuit breaker for failure detection\nmultiConfig\n        .failureDetector(MultiDbConfig.CircuitBreakerConfig.builder()\n                .slidingWindowSize(2)        // Sliding window size as a duration in seconds.\n                .failureRateThreshold(10.0f)    // Percentage of failures to trigger circuit breaker.\n                .minNumOfFailures(1000)          // Minimum number of failures before circuit breaker is tripped.\n                .build())\n        .failbackSupported(true)                // Enable failback.\n        .failbackCheckInterval(120000)          // Check every 2 minutes to see if the unhealthy database has recovered.\n        .gracePeriod(60000)                     // Keep database disabled for 60 seconds after it becomes unhealthy.\n        // Optional: configure retry settings\n        .commandRetry(MultiDbConfig.RetryConfig.builder()\n                .maxAttempts(3)                  // Maximum number of retry attempts (including the initial call)\n                .waitDuration(500)               // Number of milliseconds to wait between retry attempts.\n                .exponentialBackoffMultiplier(2) // Exponential backoff factor multiplied by the wait duration between retries.\n                .build())\n        // Optional: configure fast failover\n        .fastFailover(true)                       // Force closing connections to unhealthy database on failover.\n        .retryOnFailover(false);                  // Do not retry failed commands during failover.","section_id":"failover-configuration"},{"id":"failover-configuration-ex5","language":"java","code":"MultiDbClient multiDbClient = MultiDbClient.builder()\n        .multiDbConfig(multiConfig.build())\n        .build();","section_id":"failover-configuration"},{"id":"failover-callbacks-ex0","language":"java","code":"import org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.function.Consumer;\n\npublic class FailoverReporter implements Consumer<DatabaseSwitchEvent> {\n\n    @Override\n    public void accept(DatabaseSwitchEvent e) {\n        System.out.println(\"Jedis failover to database: \" + e.getDatabaseName() + \" due to \" + e.getReason());\n    }\n}","section_id":"failover-callbacks"},{"id":"failover-callbacks-ex1","language":"java","code":"FailoverReporter reporter = new FailoverReporter();\nMultiDbClient client = MultiDbClient.builder()\n        .databaseSwitchListener(reporter)\n        .build();","section_id":"failover-callbacks"},{"id":"failover-callbacks-ex2","language":"java","code":"MultiDbClient client = MultiDbClient.builder()\n        .databaseSwitchListener(\n            event -> System.out.println(\"Switched to: \" + event.getEndpoint())\n        )\n        .build();","section_id":"failover-callbacks"},{"id":"pingstrategy-default-ex0","language":"java","code":"MultiDbConfig.DatabaseConfig dbConfig =\n        MultiDbConfig.DatabaseConfig.builder(hostAndPort, clientConfig)\n                .healthCheckStrategy(new PingStrategy(PingStrategy.Config.builder()\n                        .interval(5000) // Check every 5 seconds\n                        .timeout(3000) // 3 second timeout\n                        .numProbes(5) // 5 probes per check\n                        .delayInBetweenProbes(100) // 100ms delay between probes\n                        .build()))\n                .build();","section_id":"pingstrategy-default"},{"id":"lagawarestrategy-preview-ex0","language":"java","code":"// Configure REST API endpoint and credentials\nHostAndPort restEndpoint = new HostAndPort(\"redis-enterprise-db-fqdn\", 9443);\nSupplier<RedisCredentials> credentialsSupplier = () -> new DefaultRedisCredentials(\"rest-api-user\", \"pwd\");\n\n// Build a single LagAwareStrategy based on REST endpoint and credentials\nLagAwareStrategy.Config lagConfig = LagAwareStrategy.Config\n        .builder(restEndpoint, credentialsSupplier)\n        .interval(5000) // Check every 5 seconds\n        .timeout(3000) // 3 second timeout\n        .extendedCheckEnabled(true)\n        .build();\n\n// Configure a database to use lag-aware health check\nMultiDbConfig.DatabaseConfig dbConfig =\n        MultiDbConfig.DatabaseConfig.builder(hostAndPort, clientConfig)\n                .healthCheckStrategy(new LagAwareStrategy(lagConfig))\n                .build();","section_id":"lagawarestrategy-preview"},{"id":"custom-health-check-strategy-ex0","language":"java","code":"// Custom strategy supplier\nMultiDbConfig.StrategySupplier customStrategy =\n        (hostAndPort, jedisClientConfig) -> {\n            // Return your custom HealthCheckStrategy implementation\n            return new MyCustomHealthCheckStrategy(hostAndPort, jedisClientConfig);\n        };\n\nMultiDbConfig.DatabaseConfig dbConfig =\n        MultiDbConfig.DatabaseConfig.builder(hostAndPort, clientConfig)\n                .healthCheckStrategySupplier(customStrategy)\n                .weight(1.0f)\n                .build();","section_id":"custom-health-check-strategy"},{"id":"disable-health-checks-ex0","language":"java","code":"MultiDbConfig.DatabaseConfig dbConfig = MultiDbConfig.DatabaseConfig.builder(east, config)\n    .healthCheckEnabled(false) // Disable health checks entirely\n    .build();","section_id":"disable-health-checks"},{"id":"managing-databases-at-runtime-ex0","language":"java","code":"HostAndPort other = new HostAndPort(\"redis-south.example.com\", 14000);\n\n// Create the database config as you would for the initial connection.\nclient.addDatabase(DatabaseConfig.builder(other, config)\n        // ...\n        .weight(0.5f)\n        .build()\n);\n\n// Remove the database from the failover set.\nclient.removeDatabase(other);","section_id":"managing-databases-at-runtime"},{"id":"manual-failback-ex0","language":"java","code":"// The `setActiveDatabase()` method receives the `Endpoint` (eg,`HostAndPort`)\n// of the cluster to switch to.\nclient.setActiveDatabase(west);","section_id":"manual-failback"},{"id":"excessive-or-constant-health-check-failures-ex0","language":"java","code":"HealthCheckStrategy.Config config = HealthCheckStrategy.Config.builder()\n    .interval(5000)                 // Less frequent checks\n    .timeout(2000)                  // More generous timeout\n    .build();","section_id":"excessive-or-constant-health-check-failures"},{"id":"slow-failback-after-recovery-ex0","language":"java","code":"// Faster recovery configuration\nHealthCheckStrategy.Config config = HealthCheckStrategy.Config.builder()\n    .interval(1000)                    // More frequent checks\n    .build();\n\n// Adjust failback timing\nMultiDbConfig multiConfig = MultiDbConfig.builder()\n        .gracePeriod(5000)                 // Shorter grace period\n        .build();","section_id":"slow-failback-after-recovery"}]}
{"schema_version":2,"id":"develop/clients/jedis","title":"Jedis guide (Java)","url":"https://redis.io/docs/latest/develop/clients/jedis/","summary":"Connect your Java application to a Redis database","aliases":["/develop/connect/clients/java/jedis","/connect/clients/java/","/connect/clients/jedis/","/clients/jedis/","/develop/clients/java","/develop/connect/clients/java/","/develop/clients/java/jedis/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-08T15:41:24+01:00","children":[{"id":"develop/clients/jedis/connect","summary":"Connect your Java application to a Redis database","title":"Connect to the server","url":"https://redis.io/docs/latest/develop/clients/jedis/connect/"},{"id":"develop/clients/jedis/amr","summary":"Learn how to authenticate to an Azure Managed Redis (AMR) database","title":"Connect to Azure Managed Redis","url":"https://redis.io/docs/latest/develop/clients/jedis/amr/"},{"id":"develop/clients/jedis/queryjson","summary":"Learn how to use Redis Search with JSON and hash documents.","title":"Index and query documents","url":"https://redis.io/docs/latest/develop/clients/jedis/queryjson/"},{"id":"develop/clients/jedis/vecsearch","summary":"Learn how to index and query vector embeddings with Redis","title":"Index and query vectors","url":"https://redis.io/docs/latest/develop/clients/jedis/vecsearch/"},{"id":"develop/clients/jedis/vecsets","summary":"Index and query embeddings with Redis vector sets","title":"Vector set embeddings","url":"https://redis.io/docs/latest/develop/clients/jedis/vecsets/"},{"id":"develop/clients/jedis/transpipe","summary":"Learn how to use Redis pipelines and transactions","title":"Pipelines and transactions","url":"https://redis.io/docs/latest/develop/clients/jedis/transpipe/"},{"id":"develop/clients/jedis/prob","summary":"Learn how to use approximate calculations with Redis.","title":"Probabilistic data types","url":"https://redis.io/docs/latest/develop/clients/jedis/prob/"},{"id":"develop/clients/jedis/error-handling","summary":"Learn how to handle errors when using Jedis.","title":"Error handling","url":"https://redis.io/docs/latest/develop/clients/jedis/error-handling/"},{"id":"develop/clients/jedis/failover","summary":"Improve reliability using the failover features of Jedis.","title":"Client-side geographic failover","url":"https://redis.io/docs/latest/develop/clients/jedis/failover/"},{"id":"develop/clients/jedis/produsage","summary":"Get your Jedis app ready for production","title":"Production usage","url":"https://redis.io/docs/latest/develop/clients/jedis/produsage/"}],"page_type":"content","content_hash":"cfd456049ddbb7d4b5ec94a43df955f98cded65ac8443b1aefeb9371283cdba1","sections":[{"id":"install","title":"Install","role":"setup","text":"To include `Jedis` as a dependency in your application, edit the dependency file, as follows.\n\n* If you use **Maven**:   \n\n  [code example]\n\n* If you use **Gradle**: \n\n  [code example]\n\n* If you use the JAR files, download the latest Jedis and Apache Commons Pool2 JAR files from [Maven Central](https://central.sonatype.com/) or any other Maven repository.\n\n* Build from [source](https://github.com/redis/jedis)"},{"id":"connect-and-test","title":"Connect and test","role":"content","text":"Add the following imports to your source file:\n\nFoundational: Import required Jedis classes for Redis client functionality\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n\n\nConnect to localhost on port 6379:\n\nFoundational: Connect to a Redis server and establish a client connection\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n\n\nAfter you have connected, you can check the connection by storing and\nretrieving a simple [string](https://redis.io/docs/latest/develop/data-types/strings) value:\n\nFoundational: Set and retrieve string values using SET and GET commands\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n\n\nStore and retrieve a [hash](https://redis.io/docs/latest/develop/data-types/hashes):\n\nFoundational: Store and retrieve hash data structures using HSET and HGETALL\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n\n\nClose the connection when you're done:\n\nFoundational: Properly close a Redis client connection to release resources\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]"},{"id":"more-information","title":"More information","role":"content","text":"`Jedis` has a complete [API reference](https://www.javadoc.io/doc/redis.clients/jedis/latest/index.html) available on [javadoc.io/](https://javadoc.io/).\nThe `Jedis` [GitHub repository](https://github.com/redis/jedis) also has useful docs\nand examples including a page about handling\n[failover with Jedis](https://github.com/redis/jedis/blob/master/docs/failover.md)\n\nSee also the other pages in this section for more information and examples:"}],"examples":[{"id":"install-ex0","language":"xml","code":"<dependency>\n      <groupId>redis.clients</groupId>\n      <artifactId>jedis</artifactId>\n      <version>7.2.0</version>\n  </dependency>","section_id":"install"},{"id":"install-ex1","language":"plaintext","code":"repositories {\n      mavenCentral()\n  }\n  //...\n  dependencies {\n      implementation 'redis.clients:jedis:7.2.0'\n      //...\n  }","section_id":"install"},{"id":"connect-and-test-ex0","language":"java","code":"import redis.clients.jedis.RedisClient;\nimport java.util.HashMap;\nimport java.util.Map;","section_id":"connect-and-test"},{"id":"connect-and-test-ex1","language":"java","code":"RedisClient jedis = RedisClient.create(\"redis://localhost:6379\");","section_id":"connect-and-test"},{"id":"connect-and-test-ex2","language":"java","code":"String res1 = jedis.set(\"bike:1\", \"Deimos\");\n        System.out.println(res1); // >>> OK\n\n        String res2 = jedis.get(\"bike:1\");\n        System.out.println(res2); // >>> Deimos","section_id":"connect-and-test"},{"id":"connect-and-test-ex3","language":"java","code":"Map<String, String> hash = new HashMap<>();\n        hash.put(\"name\", \"John\");\n        hash.put(\"surname\", \"Smith\");\n        hash.put(\"company\", \"Redis\");\n        hash.put(\"age\", \"29\");\n\n        Long res3 = jedis.hset(\"user-session:123\", hash);\n        System.out.println(res3); // >>> 4\n\n        Map<String, String> res4 = jedis.hgetAll(\"user-session:123\");\n        System.out.println(res4);\n        // >>> {name=John, surname=Smith, company=Redis, age=29}","section_id":"connect-and-test"},{"id":"connect-and-test-ex4","language":"java","code":"jedis.close();","section_id":"connect-and-test"}]}
{"schema_version":2,"id":"develop/clients/jedis/prob","title":"Probabilistic data types","url":"https://redis.io/docs/latest/develop/clients/jedis/prob/","summary":"Learn how to use approximate calculations with Redis.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-08T15:41:24+01:00","page_type":"content","content_hash":"3a6b40d5a283349853b10d3108fec0404cb7ca14312b221189e11dac354ba847","sections":[{"id":"set-operations","title":"Set operations","role":"content","text":"Redis supports the following approximate set operations:\n\n-   [Membership](#set-membership): The\n    [Bloom filter](https://redis.io/docs/latest/develop/data-types/probabilistic/bloom-filter) and\n    [Cuckoo filter](https://redis.io/docs/latest/develop/data-types/probabilistic/cuckoo-filter)\n    data types let you track whether or not a given item is a member of a set.\n-   [Cardinality](#set-cardinality): The\n    [HyperLogLog](https://redis.io/docs/latest/develop/data-types/probabilistic/hyperloglogs)\n    data type gives you an approximate value for the number of items in a set, also\n    known as the *cardinality* of the set.\n\nThe sections below describe these operations in more detail."},{"id":"set-membership","title":"Set membership","role":"content","text":"[Bloom filter](https://redis.io/docs/latest/develop/data-types/probabilistic/bloom-filter) and\n[Cuckoo filter](https://redis.io/docs/latest/develop/data-types/probabilistic/cuckoo-filter)\nobjects provide a set membership operation that lets you track whether or not a\nparticular item has been added to a set. These two types provide different\ntrade-offs for memory usage and speed, so you can select the best one for your\nuse case. Note that for both types, there is an asymmetry between presence and\nabsence of items in the set. If an item is reported as absent, then it is definitely\nabsent, but if it is reported as present, then there is a small chance it may really be\nabsent.\n\nInstead of storing strings directly, like a [set](https://redis.io/docs/latest/develop/data-types/sets),\na Bloom filter records the presence or absence of the\n[hash value](https://en.wikipedia.org/wiki/Hash_function) of a string.\nThis gives a very compact representation of the\nset's membership with a fixed memory size, regardless of how many items you\nadd. The following example adds some names to a Bloom filter representing\na list of users and checks for the presence or absence of users in the list.\n\nFoundational: Use Bloom filters for memory-efficient set membership testing with false positive possibility\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n\n\nA Cuckoo filter has similar features to a Bloom filter, but also supports\na deletion operation to remove hashes from a set, as shown in the example\nbelow.\n\nFoundational: Use Cuckoo filters for set membership testing with deletion support and faster lookups than Bloom filters\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n\n\nWhich of these two data types you choose depends on your use case.\nBloom filters are generally faster than Cuckoo filters when adding new items,\nand also have better memory usage. Cuckoo filters are generally faster\nat checking membership and also support the delete operation. See the\n[Bloom filter](https://redis.io/docs/latest/develop/data-types/probabilistic/bloom-filter) and\n[Cuckoo filter](https://redis.io/docs/latest/develop/data-types/probabilistic/cuckoo-filter)\nreference pages for more information and comparison between the two types."},{"id":"set-cardinality","title":"Set cardinality","role":"content","text":"A [HyperLogLog](https://redis.io/docs/latest/develop/data-types/probabilistic/hyperloglogs)\nobject calculates the cardinality of a set. As you add\nitems, the HyperLogLog tracks the number of distinct set members but\ndoesn't let you retrieve them or query which items have been added.\nYou can also merge two or more HyperLogLogs to find the cardinality of the\n[union](https://en.wikipedia.org/wiki/Union_(set_theory)) of the sets they\nrepresent.\n\nFoundational: Estimate set cardinality with HyperLogLog for memory-efficient counting of distinct items\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n\n\nThe main benefit that HyperLogLogs offer is their very low\nmemory usage. They can count up to 2^64 items with less than\n1% standard error using a maximum 12KB of memory. This makes\nthem very useful for counting things like the total of distinct\nIP addresses that access a website or the total of distinct\nbank card numbers that make purchases within a day."},{"id":"statistics","title":"Statistics","role":"content","text":"Redis supports several approximate statistical calculations\non numeric data sets:\n\n-   [Frequency](#frequency): The\n    [Count-min sketch](https://redis.io/docs/latest/develop/data-types/probabilistic/count-min-sketch)\n    data type lets you find the approximate frequency of a labeled item in a data stream.\n-   [Quantiles](#quantiles): The\n    [t-digest](https://redis.io/docs/latest/develop/data-types/probabilistic/t-digest)\n    data type estimates the quantile of a query value in a data stream.\n-   [Ranking](#ranking): The\n    [Top-K](https://redis.io/docs/latest/develop/data-types/probabilistic/top-k) data type\n    estimates the ranking of labeled items by frequency in a data stream.\n\nThe sections below describe these operations in more detail."},{"id":"frequency","title":"Frequency","role":"content","text":"A [Count-min sketch](https://redis.io/docs/latest/develop/data-types/probabilistic/count-min-sketch)\n(CMS) object keeps count of a set of related items represented by\nstring labels. The count is approximate, but you can specify\nhow close you want to keep the count to the true value (as a fraction)\nand the acceptable probability of failing to keep it in this\ndesired range. For example, you can request that the count should\nstay within 0.1% of the true value and have a 0.05% probability\nof going outside this limit. The example below shows how to create\na Count-min sketch object, add data to it, and then query it.\n\nFoundational: Track approximate item frequencies with Count-min sketch for memory-efficient statistics on data streams\n\n**Difficulty:** Intermediate\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n\n\nThe advantage of using a CMS over keeping an exact count with a\n[sorted set](https://redis.io/docs/latest/develop/data-types/sorted-sets)\nis that a CMS has very low and fixed memory usage, even for\nlarge numbers of items. Use CMS objects to keep daily counts of\nitems sold, accesses to individual web pages on your site, and\nother similar statistics."},{"id":"quantiles","title":"Quantiles","role":"content","text":"A [quantile](https://en.wikipedia.org/wiki/Quantile) is the value\nbelow which a certain fraction of samples lie. For example, with\na set of measurements of people's heights, the quantile of 0.75 is\nthe value of height below which 75% of all people's heights lie.\n[Percentiles](https://en.wikipedia.org/wiki/Percentile) are equivalent\nto quantiles, except that the fraction is expressed as a percentage.\n\nA [t-digest](https://redis.io/docs/latest/develop/data-types/probabilistic/t-digest)\nobject can estimate quantiles from a set of values added to it\nwithout having to store each value in the set explicitly. This can\nsave a lot of memory when you have a large number of samples.\n\nThe example below shows how to add data samples to a t-digest\nobject and obtain some basic statistics, such as the minimum and\nmaximum values, the quantile of 0.75, and the\n[cumulative distribution function](https://en.wikipedia.org/wiki/Cumulative_distribution_function)\n(CDF), which is effectively the inverse of the quantile function. It also\nshows how to merge two or more t-digest objects to query the combined\ndata set.\n\nFoundational: Estimate quantiles and percentiles with t-digest for memory-efficient statistical analysis of large datasets\n\n**Difficulty:** Intermediate\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n\n\nA t-digest object also supports several other related commands, such\nas querying by rank. See the\n[t-digest](https://redis.io/docs/latest/develop/data-types/probabilistic/t-digest)\nreference for more information."},{"id":"ranking","title":"Ranking","role":"content","text":"A [Top-K](https://redis.io/docs/latest/develop/data-types/probabilistic/top-k)\nobject estimates the rankings of different labeled items in a data\nstream according to frequency. For example, you could use this to\ntrack the top ten most frequently-accessed pages on a website, or the\ntop five most popular items sold.\n\nThe example below adds several different items to a Top-K object\nthat tracks the top three items (this is the second parameter to\nthe `topkReserve()` method). It also shows how to list the\ntop *k* items and query whether or not a given item is in the\nlist.\n\nFoundational: Track top K most frequent items in a data stream with Top-K for efficient ranking without storing all items\n\n**Difficulty:** Intermediate\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]"}],"examples":[{"id":"set-membership-ex0","language":"java","code":"List<Boolean> res1 = jedis.bfMAdd(\n            \"recorded_users\",\n            \"andy\", \"cameron\", \"david\", \"michelle\"\n        );\n        System.out.println(res1);  // >>> [true, true, true, true]\n\n        boolean res2 = jedis.bfExists(\"recorded_users\", \"cameron\");\n        System.out.println(res2);  // >>> true\n\n        boolean res3 = jedis.bfExists(\"recorded_users\", \"kaitlyn\");\n        System.out.println(res3);  // >>> false","section_id":"set-membership"},{"id":"set-membership-ex1","language":"java","code":"boolean res4 = jedis.cfAdd(\"other_users\", \"paolo\");\n        System.out.println(res4);  // >>> true\n\n        boolean res5 = jedis.cfAdd(\"other_users\", \"kaitlyn\");\n        System.out.println(res5);  // >>> true\n\n        boolean res6 = jedis.cfAdd(\"other_users\", \"rachel\");\n        System.out.println(res6);  // >>> true\n\n        List<Boolean> res7 = jedis.cfMExists(\n            \"other_users\",\n            \"paolo\", \"rachel\", \"andy\"\n        );\n        System.out.println(res7);  // >>> [true, true, false]\n\n        boolean res8 = jedis.cfDel(\"other_users\", \"paolo\");\n        System.out.println(res8);  // >>> true\n\n        boolean res9 = jedis.cfExists(\"other_users\", \"paolo\");\n        System.out.println(res9);  // >>> false","section_id":"set-membership"},{"id":"set-cardinality-ex0","language":"java","code":"long res10 = jedis.pfadd(\"group:1\", \"andy\", \"cameron\", \"david\");\n        System.out.println(res10);  // >>> 1\n\n        long res11 = jedis.pfcount(\"group:1\");\n        System.out.println(res11);  // >>> 3\n\n        long res12 = jedis.pfadd(\n            \"group:2\",\n            \"kaitlyn\", \"michelle\", \"paolo\", \"rachel\"\n        );\n        System.out.println(res12);  // >>> 1\n\n        long res13 = jedis.pfcount(\"group:2\");\n        System.out.println(res13);  // >>> 4\n\n        String res14 = jedis.pfmerge(\"both_groups\", \"group:1\", \"group:2\");\n        System.out.println(res14);  // >>> OK\n\n        long res15 = jedis.pfcount(\"both_groups\");\n        System.out.println(res15);  // >>> 7","section_id":"set-cardinality"},{"id":"frequency-ex0","language":"java","code":"// Specify that you want to keep the counts within 0.01\n        // (0.1%) of the true value with a 0.005 (0.05%) chance\n        // of going outside this limit.\n        String res16 = jedis.cmsInitByProb(\"items_sold\", 0.01, 0.005);\n        System.out.println(res16);  // >>> OK\n\n        Map<String, Long> firstItemIncrements = new HashMap<>();\n        firstItemIncrements.put(\"bread\", 300L);\n        firstItemIncrements.put(\"tea\", 200L);\n        firstItemIncrements.put(\"coffee\", 200L);\n        firstItemIncrements.put(\"beer\", 100L);\n\n        List<Long> res17 = jedis.cmsIncrBy(\"items_sold\",\n            firstItemIncrements\n        );\n        res17.sort(null);\n        System.out.println();  // >>> [100, 200, 200, 300]\n\n        Map<String, Long> secondItemIncrements = new HashMap<>();\n        secondItemIncrements.put(\"bread\", 100L);\n        secondItemIncrements.put(\"coffee\", 150L);\n\n        List<Long> res18 = jedis.cmsIncrBy(\"items_sold\",\n            secondItemIncrements\n        );\n        res18.sort(null);\n        System.out.println(res18);  // >>> [350, 400]\n\n        List<Long> res19 = jedis.cmsQuery(\n            \"items_sold\",\n            \"bread\", \"tea\", \"coffee\", \"beer\"\n        );\n        res19.sort(null);\n        System.out.println(res19);  // >>> [100, 200, 350, 400]","section_id":"frequency"},{"id":"quantiles-ex0","language":"java","code":"String res20 = jedis.tdigestCreate(\"male_heights\");\n        System.out.println(res20);  // >>> OK\n\n        String res21 = jedis.tdigestAdd(\"male_heights\", \n            175.5, 181, 160.8, 152, 177, 196, 164);\n        System.out.println(res21);  // >>> OK\n\n        double res22 = jedis.tdigestMin(\"male_heights\");\n        System.out.println(res22);  // >>> 152.0\n\n        double res23 = jedis.tdigestMax(\"male_heights\");\n        System.out.println(res23);  // >>> 196.0\n\n        List<Double> res24 = jedis.tdigestQuantile(\"male_heights\", 0.75);\n        System.out.println(res24);  // >>> [181.0]\n\n        // Note that the CDF value for 181 is not exactly 0.75.\n        // Both values are estimates.\n        List<Double> res25 = jedis.tdigestCDF(\"male_heights\", 181);\n        System.out.println(res25);  // >>> [0.7857142857142857]\n\n        String res26 = jedis.tdigestCreate(\"female_heights\");\n        System.out.println(res26);  // >>> OK\n\n        String res27 = jedis.tdigestAdd(\"female_heights\",\n            155.5, 161, 168.5, 170, 157.5, 163, 171);\n        System.out.println(res27);  // >>> OK\n\n        List<Double> res28 = jedis.tdigestQuantile(\"female_heights\", 0.75);\n        System.out.println(res28);  // >>> [170.0]\n\n        String res29 = jedis.tdigestMerge(\n            \"all_heights\",\n            \"male_heights\", \"female_heights\"\n        );\n        System.out.println(res29);  // >>> OK\n        List<Double> res30 = jedis.tdigestQuantile(\"all_heights\", 0.75);\n        System.out.println(res30);  // >>> [175.5]","section_id":"quantiles"},{"id":"ranking-ex0","language":"java","code":"String res31 = jedis.topkReserve(\"top_3_songs\", 3L, 2000L, 7L, 0.925D);\n        System.out.println(res31);  // >>> OK\n\n        Map<String, Long> songIncrements = new HashMap<>();\n        songIncrements.put(\"Starfish Trooper\", 3000L);\n        songIncrements.put(\"Only one more time\", 1850L);\n        songIncrements.put(\"Rock me, Handel\", 1325L);\n        songIncrements.put(\"How will anyone know?\", 3890L);\n        songIncrements.put(\"Average lover\", 4098L);\n        songIncrements.put(\"Road to everywhere\", 770L);\n\n        List<String> res32 = jedis.topkIncrBy(\"top_3_songs\",\n            songIncrements\n        );\n        System.out.println(res32);\n        // >>> [null, null, null, null, null, Rock me, Handel]\n\n        List<String> res33 = jedis.topkList(\"top_3_songs\");\n        System.out.println(res33);\n        // >>> [Average lover, How will anyone know?, Starfish Trooper]\n\n        List<Boolean> res34 = jedis.topkQuery(\"top_3_songs\",\n            \"Starfish Trooper\", \"Road to everywhere\"\n        );\n        System.out.println(res34);\n        // >>> [true, false]","section_id":"ranking"}]}
{"schema_version":2,"id":"develop/clients/jedis/produsage","title":"Production usage","url":"https://redis.io/docs/latest/develop/clients/jedis/produsage/","summary":"Get your Jedis app ready for production","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-08T15:41:24+01:00","page_type":"content","content_hash":"2636f2e2f2f4e5ff18b9eb692115151f1d43458d7ac0cb263888aec6d1e37938","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This guide offers recommendations to get the best reliability and\nperformance in your production environment."},{"id":"checklist","title":"Checklist","role":"content","text":"Each item in the checklist below links to the section\nfor a recommendation. Use the checklist icons to record your\nprogress in implementing the recommendations.\n\n[code example]"},{"id":"recommendations","title":"Recommendations","role":"content","text":"The sections below offer recommendations for your production environment. Some\nof them may not apply to your particular use case."},{"id":"connection-pooling","title":"Connection pooling","role":"content","text":"Example code often opens a connection at the start, demonstrates a feature,\nand then closes the connection at the end. However, production code\ntypically uses connections many times intermittently. Repeatedly opening\nand closing connections has a performance overhead.\n\nUse [connection pooling](https://redis.io/docs/latest/develop/clients/pools-and-muxing)\nto avoid the overhead of opening and closing connections without having to\nwrite your own code to cache and reuse open connections. See\n[Configure a connection pool](https://redis.io/docs/latest/develop/clients/jedis/connect#configure-a-connection-pool)\nto learn how to use this technique with Jedis."},{"id":"connection-retries","title":"Connection retries","role":"content","text":"If a connection is lost before a command is completed, the command will fail with a `JedisConnectionException`. However, a connection error is often transient, in which case the\ncommand will succeed after one or more reconnection attempts. See\n[Retrying a command after a connection failure](https://redis.io/docs/latest/develop/clients/jedis/connect#retrying-a-command-after-a-connection-failure)\nfor an example of a simple retry loop that can recover from a transient connection error."},{"id":"client-side-caching","title":"Client-side caching","role":"content","text":"[Client-side caching](https://redis.io/docs/latest/develop/clients/client-side-caching)\ninvolves storing the results from read-only commands in a local cache. If the\nsame command is executed again later, the results can be obtained from the cache,\nwithout contacting the server. This improves command execution time on the client,\nwhile also reducing network traffic and server load. See\n[Connect using client-side caching](https://redis.io/docs/latest/develop/clients/jedis/connect#connect-using-client-side-caching)\nfor more information and example code."},{"id":"timeouts","title":"Timeouts","role":"content","text":"If a network or server error occurs while your code is opening a\nconnection or issuing a command, it can end up hanging indefinitely.\nYou can prevent this from happening by setting timeouts for socket\nreads and writes and for opening connections.\n\nTo set a timeout for a connection, use `JedisClientConfig` with the `socketTimeout` and `connectionTimeout` parameters.\n(The socket timeout is the maximum time allowed for reading or writing data while executing a\ncommand. The connection timeout is the maximum time allowed for establishing a new connection.)\n\n[code example]"},{"id":"health-checks","title":"Health checks","role":"content","text":"If your code doesn't access the Redis server continuously then it\nmight be useful to make a \"health check\" periodically (perhaps once\nevery few seconds). You can do this using a simple\n[`PING`](https://redis.io/docs/latest/commands/ping) command:\n\n[code example]\n\nHealth checks help to detect problems as soon as possible without\nwaiting for a user to report them."},{"id":"exception-handling","title":"Exception handling","role":"errors","text":"Redis handles many errors using return values from commands, but there\nare also situations where exceptions can be thrown. In production code,\nyou should handle exceptions as they occur.\n\nThe Jedis exception hierarchy is rooted on `JedisException`, which implements\n`RuntimeException`. All exceptions in the hierarchy are therefore unchecked\nexceptions.\n\n[code example]\n\n#### General exceptions\n\nIn general, Jedis can throw the following exceptions while executing commands:\n\n- `JedisConnectionException` - when the connection to Redis is lost or closed unexpectedly. Configure failover to handle this exception automatically with Resilience4J and the built-in Jedis failover mechanism.  \n- `JedisAccessControlException` - when the user does not have the permission to execute the command or the user ID and/or password are incorrect.\n- `JedisDataException` - when there is a problem with the data being sent to or received from the Redis server. Usually, the error message will contain more information about the failed command.\n- `JedisException` - this exception is a catch-all exception that can be thrown for any other unexpected errors.\n\nConditions when `JedisException` can be thrown:\n- Bad return from a health check with the [`PING`](https://redis.io/docs/latest/commands/ping) command\n- Failure during SHUTDOWN\n- Pub/Sub failure when issuing commands (disconnect)\n- Any unknown server messages\n- Sentinel: can connect to sentinel but master is not monitored or all Sentinels are down.\n- MULTI or DISCARD command failed \n- Shard commands key hash check failed or no Reachable Shards\n- Retry deadline exceeded/number of attempts (Retry Command Executor)\n- POOL - pool exhausted, error adding idle objects, returning broken resources to the pool\n\nAll the Jedis exceptions are runtime exceptions and in most cases irrecoverable, so in general bubble up to the API capturing the error message."},{"id":"dns-cache-and-redis","title":"DNS cache and Redis","role":"content","text":"When you connect to a Redis server with multiple endpoints, such as [Redis Software Active-Active](https://redis.com/redis-enterprise/technology/active-active-geo-distribution/), you *must*\ndisable the JVM's DNS cache. If a server node or proxy fails, the IP address for any database\naffected by the failure will change. When this happens, your app will keep\ntrying to use the stale IP address if DNS caching is enabled.\n\nUse the following code to disable the DNS cache:\n\n[code example]"}],"examples":[{"id":"checklist-ex0","language":"checklist {id=\"jedisprodlist\"}","code":"- [ ] [Connection pooling](#connection-pooling)\n- [ ] [Connection retries](#connection-retries)\n- [ ] [Client-side caching](#client-side-caching)\n- [ ] [Timeouts](#timeouts)\n- [ ] [Health checks](#health-checks)\n- [ ] [Exception handling](#exception-handling)\n- [ ] [DNS cache and Redis](#dns-cache-and-redis)","section_id":"checklist"},{"id":"timeouts-ex0","language":"java","code":"RedisClient jedisWithTimeout = RedisClient.builder()\n    .hostAndPort(\"localhost\", 6379)\n    .clientConfig(\n        DefaultJedisClientConfig.builder()\n            .socketTimeoutMillis(5000)  // set timeout to 5 seconds\n            .connectionTimeoutMillis(5000) // set connection timeout to 5 seconds\n            .build())\n    .build();","section_id":"timeouts"},{"id":"health-checks-ex0","language":"java","code":"ConnectionPoolConfig poolConfig = new ConnectionPoolConfig();\npoolConfig.setMaxTotal(8);\npoolConfig.setMaxIdle(8);\npoolConfig.setMinIdle(0);\npoolConfig.setBlockWhenExhausted(true);\npoolConfig.setMaxWait(Duration.ofSeconds(1));\n\n// Enables sending a PING command once every second while the connection is idle.\npoolConfig.setTestWhileIdle(true);\npoolConfig.setTimeBetweenEvictionRuns(Duration.ofSeconds(1));\n\nRedisClient jedis = RedisClient.builder()\n        .hostAndPort(\"localhost\", 6379)\n        .poolConfig(poolConfig)\n        .build();","section_id":"health-checks"},{"id":"exception-handling-ex0","language":"hierarchy {type=\"exception\"}","code":"\"JedisException\":\n    _meta:\n        description: \"Base class for all Jedis exceptions\"\n    \"JedisDataException\":\n        \"JedisRedirectionException\":\n            \"JedisMovedDataException\":\n            \"JedisAskDataException\":\n        \"AbortedTransactionException\":\n        \"JedisAccessControlException\":\n        \"JedisNoScriptException\":\n    \"JedisClusterException\":\n        \"JedisClusterOperationException\":\n        \"JedisConnectionException\":\n        \"JedisValidationException\":\n    \"InvalidURIException\":","section_id":"exception-handling"},{"id":"dns-cache-and-redis-ex0","language":"java","code":"java.security.Security.setProperty(\"networkaddress.cache.ttl\",\"0\");\njava.security.Security.setProperty(\"networkaddress.cache.negative.ttl\", \"0\");","section_id":"dns-cache-and-redis"}]}
{"schema_version":2,"id":"develop/clients/jedis/queryjson","title":"Index and query documents","url":"https://redis.io/docs/latest/develop/clients/jedis/queryjson/","summary":"Learn how to use Redis Search with JSON and hash documents.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-16T16:25:49+01:00","page_type":"content","content_hash":"d16b757c7205e1031b51f31671769d296eafa33a7536a81b0f64a3301df027d9","sections":[{"id":"initialize","title":"Initialize","role":"content","text":"Make sure that you have [Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack)\nor another Redis server available. Also install the\n[Jedis](https://redis.io/docs/latest/develop/clients/jedis) client library if you\nhaven't already done so.\n\nAdd the following dependencies. All of them are applicable to both JSON and hash,\nexcept for the `Path` and `JSONObject` classes, which are specific to JSON (see\n[Path](https://redis.io/docs/latest/develop/data-types/json/path) for a description of the\nJSON path syntax).\n\nFoundational: Import required Jedis classes for JSON indexing and query operations\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]"},{"id":"create-data","title":"Create data","role":"content","text":"Create some test data to add to the database:\n\nFoundational: Define sample user data structures for indexing and querying\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]"},{"id":"add-the-index","title":"Add the index","role":"content","text":"Connect to your Redis database. The code below shows the most\nbasic connection but see\n[Connect to the server](https://redis.io/docs/latest/develop/clients/jedis/connect)\nto learn more about the available connection options.\n\nFoundational: Establish a connection to a Redis server for query operations\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n\n\nDelete any existing index called `idx:users` and any keys that start with `user:`.\n\nFoundational: Clean up existing indexes and data before creating new indexes\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n\n\nCreate an index. In this example, only JSON documents with the key prefix `user:` are indexed. For more information, see [Query syntax](https://redis.io/docs/latest/develop/ai/search-and-query/query).\n\nFoundational: Create a search index for JSON documents with field definitions and key prefix filtering\n\n**Difficulty:** Intermediate\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]"},{"id":"add-the-data","title":"Add the data","role":"content","text":"Add the three sets of user data to the database as\n[JSON](https://redis.io/docs/latest/develop/data-types/json) objects.\nIf you use keys with the `user:` prefix then Redis will index the\nobjects automatically as you add them:\n\nFoundational: Store JSON documents in Redis with automatic indexing based on key prefix\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]"},{"id":"query-the-data","title":"Query the data","role":"content","text":"You can now use the index to search the JSON objects. The\n[query](https://redis.io/docs/latest/develop/ai/search-and-query/query)\nbelow searches for objects that have the text \"Paul\" in any field\nand have an `age` value in the range 30 to 40:\n\nQuery with filters: Search JSON documents using text matching and numeric range filters to find specific records\n\n**Difficulty:** Intermediate\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n\n\nSpecify query options to return only the `city` field:\n\nQuery with field projection: Retrieve only specific fields from search results to reduce data transfer\n\n**Difficulty:** Intermediate\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n\n\nUse an\n[aggregation query](https://redis.io/docs/latest/develop/ai/search-and-query/query/aggregation)\nto count all users in each city.\n\nAggregation query: Group and count results by field values to analyze data patterns\n\n**Difficulty:** Intermediate\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]"},{"id":"differences-with-hash-documents","title":"Differences with hash documents","role":"content","text":"Indexing for hash documents is very similar to JSON indexing but you\nneed to specify some slightly different options.\n\nWhen you create the schema for a hash index, you don't need to\nadd aliases for the fields, since you use the basic names to access\nthe fields anyway. Also, you must use `IndexDataType.HASH` for the `On()`\noption of `FTCreateParams` when you create the index. The code below shows these\nchanges with a new index called `hash-idx:users`, which is otherwise the same as\nthe `idx:users` index used for JSON documents in the previous examples.\n\nFirst, delete any existing index called `hash-idx:users` and any keys that start with `huser:`.\n\nFoundational: Clean up existing hash indexes and data before creating new indexes\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n\n\nNow create the new index:\n\nFoundational: Create a search index for hash documents with field definitions and key prefix filtering\n\n**Difficulty:** Intermediate\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n\n\nUse [`hset()`](https://redis.io/docs/latest/commands/hset) to add the hash\ndocuments instead of [`jsonSet()`](https://redis.io/docs/latest/commands/json.set).\n\nFoundational: Store hash documents in Redis with automatic indexing based on key prefix\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n\n\nThe query commands work the same here for hash as they do for JSON (but\nthe name of the hash index is different). The results are returned in\na `List` of `Document` objects, as with JSON:\n\nQuery with filters: Search hash documents using text matching and numeric range filters (same as JSON queries)\n\n**Difficulty:** Intermediate\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]"},{"id":"bulk-import-with-himport","title":"Bulk import with HIMPORT","role":"content","text":"From Redis Open Source 8.10, you can also add the hash documents with the\n[`HIMPORT`](https://redis.io/docs/latest/commands/himport) command family. This creates the same\nhashes as `hset()`, but lets Redis store them as\n[compact hashes](https://redis.io/docs/latest/develop/data-types/hashes#compact-hashes), which use\nless memory when many keys share the same field names. Redis Search indexes\nthese hashes in the same way.\n\nCreate a `HashImport` object with the field names, then create each document\nwith [`himportSet()`](https://redis.io/docs/latest/commands/himport-set), passing only its values,\nin the same order as the field names. Jedis declares the field names with\n[`HIMPORT PREPARE`](https://redis.io/docs/latest/commands/himport-prepare) the first time you use\nthe `HashImport` object on a connection. Use a\n[pipeline](https://redis.io/docs/latest/develop/clients/jedis/transpipe) to send all the\nimports in one round trip:\n\nBulk ingestion: Import hash documents as compact hashes using a HashImport fieldset and a pipeline of HIMPORT SET calls\n\n**Difficulty:** Intermediate\n\n**Builds upon:** add_hash_data\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n\n\n`himportSet()` replaces any existing key, so running this code after the\n`hset()` calls leaves you with the same three documents. The query returns\nthe same result, although the fields can appear in a different order.\n\n> [!NOTE]\n> `HIMPORT` requires Jedis v8.0.0 or later, and Jedis marks its support as\n> experimental, so the API might change. Redis Cloud and Redis Software don't\n> support `HIMPORT`."},{"id":"more-information","title":"More information","role":"content","text":"See the [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) docs\nfor a full description of all query features with examples."}],"examples":[{"id":"initialize-ex0","language":"java","code":"import redis.clients.jedis.HashImport;\nimport redis.clients.jedis.Pipeline;\nimport redis.clients.jedis.RedisClient;\nimport redis.clients.jedis.Response;\nimport redis.clients.jedis.exceptions.JedisDataException;\nimport redis.clients.jedis.json.Path2;\nimport redis.clients.jedis.search.*;\nimport redis.clients.jedis.search.aggr.*;\nimport redis.clients.jedis.search.schemafields.*;\nimport org.json.JSONObject;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;","section_id":"initialize"},{"id":"create-data-ex0","language":"java","code":"JSONObject user1 = new JSONObject()\n                .put(\"name\", \"Paul John\")\n                .put(\"email\", \"paul.john@example.com\")\n                .put(\"age\", 42)\n                .put(\"city\", \"London\");\n        \n        JSONObject user2 = new JSONObject()\n                .put(\"name\", \"Eden Zamir\")\n                .put(\"email\", \"eden.zamir@example.com\")\n                .put(\"age\", 29)\n                .put(\"city\", \"Tel Aviv\");\n        \n        JSONObject user3 = new JSONObject()\n                .put(\"name\", \"Paul Zamir\")\n                .put(\"email\", \"paul.zamir@example.com\")\n                .put(\"age\", 35)\n                .put(\"city\", \"Tel Aviv\");","section_id":"create-data"},{"id":"add-the-index-ex0","language":"java","code":"RedisClient jedis = RedisClient.create(\"redis://localhost:6379\");","section_id":"add-the-index"},{"id":"add-the-index-ex1","language":"java","code":"try {jedis.ftDropIndex(\"idx:users\");} catch (JedisDataException j){}\n        jedis.del(\"user:1\", \"user:2\", \"user:3\");","section_id":"add-the-index"},{"id":"add-the-index-ex2","language":"java","code":"SchemaField[] schema = {\n            TextField.of(\"$.name\").as(\"name\"),\n            TextField.of(\"$.city\").as(\"city\"),\n            NumericField.of(\"$.age\").as(\"age\")\n        };\n\n        String createResult = jedis.ftCreate(\"idx:users\",\n            FTCreateParams.createParams()\n                .on(IndexDataType.JSON)\n                .addPrefix(\"user:\"),\n                schema\n        );\n\n        System.out.println(createResult); // >>> OK","section_id":"add-the-index"},{"id":"add-the-data-ex0","language":"java","code":"String user1Set = jedis.jsonSet(\"user:1\", new Path2(\"$\"), user1);\n        String user2Set = jedis.jsonSet(\"user:2\", new Path2(\"$\"), user2);\n        String user3Set = jedis.jsonSet(\"user:3\", new Path2(\"$\"), user3);","section_id":"add-the-data"},{"id":"query-the-data-ex0","language":"java","code":"SearchResult findPaulResult = jedis.ftSearch(\"idx:users\",\n             \"Paul @age:[30 40]\"\n        );\n        \n        System.out.println(findPaulResult.getTotalResults()); // >>> 1\n\n        List<Document> paulDocs = findPaulResult.getDocuments();\n\n        for (Document doc: paulDocs) {\n            System.out.println(doc.getId());\n        }\n        // >>> user:3","section_id":"query-the-data"},{"id":"query-the-data-ex1","language":"java","code":"SearchResult citiesResult = jedis.ftSearch(\"idx:users\",\n            \"Paul\",\n            FTSearchParams.searchParams()\n                .returnFields(\"city\")\n        );\n\n        System.out.println(citiesResult.getTotalResults()); // >>> 2\n\n        for (Document doc: citiesResult.getDocuments()) {\n            System.out.println(doc.getId());\n        }\n        // >>> user:1\n        // >>> user:3","section_id":"query-the-data"},{"id":"query-the-data-ex2","language":"java","code":"AggregationResult aggResult = jedis.ftAggregate(\"idx:users\",\n            new AggregationBuilder(\"*\")\n                .groupBy(\"@city\", Reducers.count().as(\"count\"))\n        );\n\n        System.out.println(aggResult.getTotalResults()); // >>> 2\n\n        for (Row cityRow: aggResult.getRows()) {\n            System.out.printf(\"%s - %d%n\",\n                cityRow.getString(\"city\"), cityRow.getLong(\"count\"));\n        }\n        // >>> London - 1\n        // >>> Tel Aviv - 2","section_id":"query-the-data"},{"id":"differences-with-hash-documents-ex0","language":"java","code":"try {jedis.ftDropIndex(\"hash-idx:users\");} catch (JedisDataException j){}\n        jedis.del(\"huser:1\", \"huser:2\", \"huser:3\");","section_id":"differences-with-hash-documents"},{"id":"differences-with-hash-documents-ex1","language":"java","code":"SchemaField[] hashSchema = {\n            TextField.of(\"name\"),\n            TextField.of(\"city\"),\n            NumericField.of(\"age\")\n        };\n\n        String hashCreateResult = jedis.ftCreate(\"hash-idx:users\",\n            FTCreateParams.createParams()\n                .on(IndexDataType.HASH)\n                .addPrefix(\"huser:\"),\n                hashSchema\n        );\n\n        System.out.println(hashCreateResult); // >>> OK","section_id":"differences-with-hash-documents"},{"id":"differences-with-hash-documents-ex2","language":"java","code":"Map<String, String> user1Info = new HashMap<>();\n        user1Info.put(\"name\", \"Paul John\");\n        user1Info.put(\"email\", \"paul.john@example.com\");\n        user1Info.put(\"age\", \"42\");\n        user1Info.put(\"city\", \"London\");\n        long huser1Set = jedis.hset(\"huser:1\", user1Info);\n        \n        System.out.println(huser1Set); // >>> 4\n\n        Map<String, String> user2Info = new HashMap<>();\n        user2Info.put(\"name\", \"Eden Zamir\");\n        user2Info.put(\"email\", \"eden.zamir@example.com\");\n        user2Info.put(\"age\", \"29\");\n        user2Info.put(\"city\", \"Tel Aviv\");\n        long huser2Set = jedis.hset(\"huser:2\", user2Info);\n        \n        System.out.println(huser2Set); // >>> 4\n\n        Map<String, String> user3Info = new HashMap<>();\n        user3Info.put(\"name\", \"Paul Zamir\");\n        user3Info.put(\"email\", \"paul.zamir@example.com\");\n        user3Info.put(\"age\", \"35\");\n        user3Info.put(\"city\", \"Tel Aviv\");\n        long huser3Set = jedis.hset(\"huser:3\", user3Info);\n        \n        System.out.println(huser3Set); // >>> 4","section_id":"differences-with-hash-documents"},{"id":"differences-with-hash-documents-ex3","language":"java","code":"SearchResult findPaulHashResult = jedis.ftSearch(\"hash-idx:users\",\n             \"Paul @age:[30 40]\"\n        );\n        \n        System.out.println(findPaulHashResult.getTotalResults()); // >>> 1\n\n        List<Document> paulHashDocs = findPaulHashResult.getDocuments();\n\n        for (Document doc: paulHashDocs) {\n            System.out.println(doc.getId());\n        }\n        // >>> huser:3","section_id":"differences-with-hash-documents"},{"id":"bulk-import-with-himport-ex0","language":"java","code":"List<Response<String>> himportRes = new ArrayList<>();\n\n        try (HashImport fields = HashImport.of(\"name\", \"email\", \"age\", \"city\");\n                Pipeline pipeline = jedis.pipelined()) {\n            himportRes.add(pipeline.himportSet(\"huser:1\", fields,\n                    \"Paul John\", \"paul.john@example.com\", \"42\", \"London\"));\n            himportRes.add(pipeline.himportSet(\"huser:2\", fields,\n                    \"Eden Zamir\", \"eden.zamir@example.com\", \"29\", \"Tel Aviv\"));\n            himportRes.add(pipeline.himportSet(\"huser:3\", fields,\n                    \"Paul Zamir\", \"paul.zamir@example.com\", \"35\", \"Tel Aviv\"));\n            pipeline.sync();\n        }","section_id":"bulk-import-with-himport"}]}
{"schema_version":2,"id":"develop/clients/jedis/transpipe","title":"Pipelines and transactions","url":"https://redis.io/docs/latest/develop/clients/jedis/transpipe/","summary":"Learn how to use Redis pipelines and transactions","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-08T15:41:24+01:00","page_type":"content","content_hash":"cc2ac59c7454375d97f1393e76eb67b6c1566c6c67bb1065e4f2d2ac5e5b8d16","sections":[{"id":"execute-a-pipeline","title":"Execute a pipeline","role":"content","text":"To execute commands in a pipeline, you first create a pipeline object\nand then add commands to it using methods that resemble the standard\ncommand methods (for example, `set()` and `get()`). The commands are\nbuffered in the pipeline and only execute when you call the `sync()`\nmethod on the pipeline object.\n\nThe main difference with the pipeline commands is that they return\n`Response<Type>` objects, where `Type` is the return type of the\nstandard command method. A `Response` object contains a valid result\nonly after the pipeline has finished executing. You can access the\nresult using the `Response` object's `get()` method.\n\nFoundational: Use pipelines to batch multiple commands together and reduce network round trips\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]"},{"id":"execute-a-transaction","title":"Execute a transaction","role":"content","text":"A transaction works in a similar way to a pipeline. Create a\ntransaction object with the `multi()` command, call command methods\non that object, and then call the transaction object's\n`exec()` method to execute it. You can access the results\nfrom commands in the transaction using `Response` objects, as\nyou would with a pipeline. However, the `exec()` method also\nreturns a `List<Object>` value that contains all the result\nvalues in the order the commands were executed (see\n[Watch keys for changes](#watch-keys-for-changes) below for\nan example that uses the results list).\n\nFoundational: Use transactions to execute multiple commands atomically without interruption from other clients\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]"},{"id":"watch-keys-for-changes","title":"Watch keys for changes","role":"content","text":"Redis supports *optimistic locking* to avoid inconsistent updates\nto different keys. The basic idea is to watch for changes to any\nkeys that you use in a transaction while you are processing the\nupdates. If the watched keys do change, you must restart the updates\nwith the latest data from the keys. See\n[Transactions](https://redis.io/docs/latest/develop/using-commands/transactions)\nfor more information about optimistic locking.\n\nThe code below reads a string\nthat represents a `PATH` variable for a command shell, then appends a new\ncommand path to the string before attempting to write it back. If the watched\nkey is modified by another client before writing, the transaction aborts.\nNote that you should call read-only commands for the watched keys synchronously on\nthe usual client object (called `jedis` in our examples) but you still call commands\nfor the transaction on the transaction object.\n\nFor production usage, you would generally call code like the following in\na loop to retry it until it succeeds or else report or log the failure.\n\nOptimistic locking: Use WATCH to monitor keys for changes and retry transactions when conflicts occur\n\n**Difficulty:** Intermediate\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]"}],"examples":[{"id":"execute-a-pipeline-ex0","language":"java","code":"// Make sure you close the pipeline after use to release resources\n        // and return the connection to the pool.\n        try (AbstractPipeline pipe = jedis.pipelined()) {\n\n            for (int i = 0; i < 5; i++) {\n                pipe.set(String.format(\"seat:%d\", i), String.format(\"#%d\", i));\n            }\n\n            pipe.sync();\n        }\n\n        try (AbstractPipeline pipe = jedis.pipelined()) {\n\n            Response<String> resp0 = pipe.get(\"seat:0\");\n            Response<String> resp3 = pipe.get(\"seat:3\");\n            Response<String> resp4 = pipe.get(\"seat:4\");\n\n            pipe.sync();\n\n            // Responses are available after the pipeline has executed.\n            System.out.println(resp0.get()); // >>> #0\n            System.out.println(resp3.get()); // >>> #3\n            System.out.println(resp4.get()); // >>> #4\n\n\n        }","section_id":"execute-a-pipeline"},{"id":"execute-a-transaction-ex0","language":"java","code":"try ( AbstractTransaction trans = jedis.multi()) {\n\n           trans.incrBy(\"counter:1\", 1);\n           trans.incrBy(\"counter:2\", 2);\n           trans.incrBy(\"counter:3\", 3);\n\n           trans.exec();\n        }\n        System.out.println(jedis.get(\"counter:1\")); // >>> 1\n        System.out.println(jedis.get(\"counter:2\")); // >>> 2\n        System.out.println(jedis.get(\"counter:3\")); // >>> 3","section_id":"execute-a-transaction"},{"id":"watch-keys-for-changes-ex0","language":"java","code":"// Set initial value of `shellpath`.\n        jedis.set(\"shellpath\", \"/usr/syscmds/\");\n\n        // Start the transaction and watch the key we are about to update.\n        try (AbstractTransaction trans = jedis.transaction(false)) { // create a Transaction object without sending MULTI command\n            trans.watch(\"shellpath\"); // send WATCH command(s)\n            trans.multi(); // send MULTI command\n\n            String currentPath = jedis.get(\"shellpath\");\n            String newPath = currentPath + \":/usr/mycmds/\";\n\n            // Commands added to the `trans` object\n            // will be buffered until `trans.exec()` is called.\n            Response<String> setResult = trans.set(\"shellpath\", newPath);\n            List<Object> transResults = trans.exec();\n\n            // The `exec()` call returns null if the transaction failed.\n            if (transResults != null) {\n                // Responses are available if the transaction succeeded.\n                System.out.println(setResult.get()); // >>> OK\n\n                // You can also get the results from the list returned by\n                // `trans.exec()`.\n                for (Object item: transResults) {\n                    System.out.println(item);\n                }\n                // >>> OK\n\n                System.out.println(jedis.get(\"shellpath\"));\n                // >>> /usr/syscmds/:/usr/mycmds/\n            }\n        }","section_id":"watch-keys-for-changes"}]}
{"schema_version":2,"id":"develop/clients/jedis/vecsearch","title":"Index and query vectors","url":"https://redis.io/docs/latest/develop/clients/jedis/vecsearch/","summary":"Learn how to index and query vector embeddings with Redis","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-16T16:44:30+01:00","page_type":"content","content_hash":"72aaffc80964a5ffc7b9d0f7de3a6b6bc28e51eb54aceb33c67dede80173b041","sections":[{"id":"initialize","title":"Initialize","role":"content","text":"If you are using [Maven](https://maven.apache.org/), add the following\ndependencies to your `pom.xml` file:\n\n[code example]\n\nIf you are using [Gradle](https://gradle.org/), add the following\ndependencies to your `build.gradle` file:\n\n[code example]"},{"id":"import-dependencies","title":"Import dependencies","role":"content","text":"Import the following classes in your source file:\n\nFoundational: Import required libraries for vector embeddings, Redis operations, and search functionality\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]"},{"id":"define-a-helper-method","title":"Define a helper method","role":"content","text":"The embedding model represents the vectors as an array of `float` values,\nwhich is the format required by Redis Search.\nAlso, when you store vectors in a hash object, you must encode the vector\narray as a `byte` string. To simplify this situation, you can declare a helper\nmethod `floatsToByteString()` that takes the `float` array that the\nembedding model returns and encodes it as a `byte` string:\n\nFoundational: Create a helper method to convert float arrays to binary strings for vector storage in hash objects\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]"},{"id":"create-a-tokenizer-instance","title":"Create a tokenizer instance","role":"content","text":"The next step is to generate the embeddings using the\n[`all-MiniLM-L6-v2`](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2)\nmodel. The vectors that represent the\nembeddings have 384 components, regardless of the length of the input\ntext, but note that the input is truncated to 256\ntokens (see\n[Word piece tokenization](https://huggingface.co/learn/nlp-course/en/chapter6/6)\n\nPractical pattern: Initialize a tokenizer and embedding model for generating vector representations from text\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]"},{"id":"create-the-index","title":"Create the index","role":"content","text":"Connect to Redis and delete any index previously created with the\nname `vector_idx`. (The `ftDropIndex()` call throws an exception if\nthe index doesn't already exist, which is why you need the\n`try...catch` block.)\n\nFoundational: Connect to Redis and clean up existing vector indexes\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n\n\nNext, create the index.\nThe schema in the example below includes three fields: the text content to index, a\n[tag](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/tags)\nfield to represent the \"genre\" of the text, and the embedding vector generated from\nthe original text content. The `embedding` field specifies\n[HNSW](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#hnsw-index)\nindexing, the\n[L2](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#distance-metrics)\nvector distance metric, `Float32` values to represent the vector's components,\nand 384 dimensions, as required by the `all-MiniLM-L6-v2` embedding model.\n\nThe `FTCreateParams` object specifies hash objects for storage and a\nprefix `doc:` that identifies the hash objects to index.\n\nFoundational: Create a vector search index for hash documents with HNSW algorithm and L2 distance metric\n\n**Difficulty:** Intermediate\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]"},{"id":"add-data","title":"Add data","role":"content","text":"You can now supply the data objects, which will be indexed automatically\nwhen you add them with [`hset()`](https://redis.io/docs/latest/commands/hset), as long as\nyou use the `doc:` prefix specified in the index definition.\n\nUse the `predict()` method of the `Predictor` object\nas shown below to create the embedding that represents the `content` field.\nThe `predict()` method returns a `float[]` array which is then converted to a `byte`\nstring using the helper method. Use the `byte` string representation when you are\nindexing hash objects (as in this example), but use the array of `float` directly for\nJSON objects (see [Differences with JSON objects](#differences-with-json-documents)\nbelow). Note that when you set the `embedding` field, you must use an overload\nof `hset()` that requires `byte` arrays for each of the key, the field name, and\nthe value, which is why you must include the `getBytes()` calls on the strings.\n\nFoundational: Store hash documents with vector embeddings generated from text content\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]"},{"id":"bulk-import-with-himport","title":"Bulk import with HIMPORT","role":"content","text":"From Redis Open Source 8.10, you can also add the documents with the\n[`HIMPORT`](https://redis.io/docs/latest/commands/himport) command family. This creates the same\nhashes as `hset()`, but lets Redis store them as\n[compact hashes](https://redis.io/docs/latest/develop/data-types/hashes#compact-hashes), which use\nless memory when many keys share the same field names. Redis Search indexes\nthese hashes in the same way.\n\nCreate a `HashImport` object with the field names, then create each document\nwith [`himportSet()`](https://redis.io/docs/latest/commands/himport-set), passing only its values,\nin the same order as the field names. Jedis declares the field names with\n[`HIMPORT PREPARE`](https://redis.io/docs/latest/commands/himport-prepare) the first time you use\nthe `HashImport` object on a connection. As with `hset()`, use the overload of\n`himportSet()` that takes `byte` arrays so that you can pass the binary embedding.\nUse a [pipeline](https://redis.io/docs/latest/develop/clients/jedis/transpipe) to send all the\nimports in one round trip:\n\nBulk ingestion: Import hash documents with vector embeddings as compact hashes using a HashImport fieldset and a pipeline of HIMPORT SET calls\n\n**Difficulty:** Intermediate\n\n**Builds upon:** add_data\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n\n\n`himportSet()` replaces any existing key, so running this code after the\n`hset()` calls leaves you with the same three documents.\n\n> [!NOTE]\n> `HIMPORT` requires Jedis v8.0.0 or later, and Jedis marks its support as\n> experimental, so the API might change. Redis Cloud and Redis Software don't\n> support `HIMPORT`."},{"id":"run-a-query","title":"Run a query","role":"content","text":"After you have created the index and added the data, you are ready to run a query.\nTo do this, you must create another embedding vector from your chosen query\ntext. Redis calculates the vector distance between the query vector and each\nembedding vector in the index as it runs the query. You can request the results to be\nsorted to rank them in order of ascending distance.\n\nThe code below creates the query embedding using the `predict()` method, as with\nthe indexing, and passes it as a parameter when the query executes (see\n[Vector search](https://redis.io/docs/latest/develop/ai/search-and-query/query/vector-search)\nfor more information about using query parameters with embeddings).\nThe query is a\n[K nearest neighbors (KNN)](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#knn-vector-search)\nsearch that sorts the results in order of vector distance from the query vector.\n\nVector similarity search: Find semantically similar documents by comparing query embeddings with indexed vectors using L2 distance\n\n**Difficulty:** Intermediate\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n\n\nAssuming you have added the code from the steps above to your source file,\nit is now ready to run, but note that it may take a while to complete when\nyou run it for the first time (which happens because the tokenizer must download the\n`all-MiniLM-L6-v2` model data before it can\ngenerate the embeddings). When you run the code, it outputs the following result text:\n\n[code example]\n\nThe exact distances may differ slightly on your system.\n\nNote that the results are ordered according to the value of the `distance`\nfield, with the lowest distance indicating the greatest similarity to the query.\nAs expected, the text *\"That is a very happy person\"*\nis the result judged to be most similar in meaning to the query text\n*\"That is a happy person\"*."},{"id":"differences-with-json-documents","title":"Differences with JSON documents","role":"content","text":"Indexing JSON documents is similar to hash indexing, but there are some\nimportant differences. JSON allows much richer data modeling with nested fields, so\nyou must supply a [path](https://redis.io/docs/latest/develop/data-types/json/path) in the schema\nto identify each field you want to index. However, you can declare a short alias for each\nof these paths (using the `as()` option) to avoid typing it in full for\nevery query. Also, you must specify `IndexDataType.JSON` when you create the index.\n\nThe code below shows these differences, but the index is otherwise very similar to\nthe one created previously for hashes:\n\nFoundational: Create a vector search index for JSON documents with JSON paths and field aliases\n\n**Difficulty:** Intermediate\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n\n\nAn important difference with JSON indexing is that the vectors are\nspecified using arrays of `float` instead of binary strings, so you don't need\na helper method to convert the array to a binary string.\n\nUse [`jsonSet()`](https://redis.io/docs/latest/commands/json.set) to add the data\ninstead of [`hset()`](https://redis.io/docs/latest/commands/hset). Use instances\nof `JSONObject` to supply the data instead of `Map`, as you would for\nhash objects.\n\nFoundational: Store JSON documents with vector embeddings as arrays (different from hash binary format)\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n\n\nThe query is almost identical to the one for the hash documents. This\ndemonstrates how the right choice of aliases for the JSON paths can\nsave you having to write complex queries. An important thing to notice\nis that the vector parameter for the query is still specified as a\nbinary string (created using the `floatsToByteString()` method), even though\nthe data for the `embedding` field of the JSON was specified as an array.\n\nVector similarity search: Query JSON documents using vector embeddings with field aliases for simplified syntax\n\n**Difficulty:** Intermediate\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n\n\nApart from the `jdoc:` prefixes for the keys, the result from the JSON\nquery is the same as for hash:\n\n[code example]"},{"id":"learn-more","title":"Learn more","role":"related","text":"See\n[Vector search](https://redis.io/docs/latest/develop/ai/search-and-query/query/vector-search)\nfor more information about the indexing options, distance metrics, and query format\nfor vectors."}],"examples":[{"id":"initialize-ex0","language":"xml","code":"<dependency>\n    <groupId>redis.clients</groupId>\n    <artifactId>jedis</artifactId>\n    <version>8.0.2</version>\n</dependency>\n<dependency>\n    <groupId>ai.djl.huggingface</groupId>\n    <artifactId>tokenizers</artifactId>\n    <version>0.33.0</version>\n</dependency>\n<dependency>\n    <groupId>ai.djl.pytorch</groupId>\n    <artifactId>pytorch-model-zoo</artifactId>\n    <version>0.33.0</version>\n</dependency>\n<dependency>\n    <groupId>ai.djl</groupId>\n    <artifactId>api</artifactId>\n    <version>0.33.0</version>\n</dependency>","section_id":"initialize"},{"id":"initialize-ex1","language":"bash","code":"implementation 'redis.clients:jedis:8.0.2'\nimplementation 'ai.djl.huggingface:tokenizers:0.33.0'\nimplementation 'ai.djl.pytorch:pytorch-model-zoo:0.33.0'\nimplementation 'ai.djl:api:0.33.0'","section_id":"initialize"},{"id":"import-dependencies-ex0","language":"java","code":"import redis.clients.jedis.HashImport;\nimport redis.clients.jedis.Pipeline;\nimport redis.clients.jedis.RedisClient;\nimport redis.clients.jedis.search.*;\nimport redis.clients.jedis.search.schemafields.*;\nimport redis.clients.jedis.search.schemafields.VectorField.VectorAlgorithm;\nimport redis.clients.jedis.exceptions.JedisDataException;\nimport redis.clients.jedis.json.Path2;\n\nimport org.json.JSONObject;\n\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.util.Map;\nimport java.util.List;\n\n// DJL classes for model loading and inference.\nimport ai.djl.huggingface.translator.TextEmbeddingTranslatorFactory;\nimport ai.djl.inference.Predictor;\nimport ai.djl.repository.zoo.Criteria;\nimport ai.djl.training.util.ProgressBar;","section_id":"import-dependencies"},{"id":"define-a-helper-method-ex0","language":"java","code":"public static byte[] floatsToByteString(float[] floats) {\n        byte[] bytes = new byte[Float.BYTES * floats.length];\n        ByteBuffer\n            .wrap(bytes)\n            .order(ByteOrder.LITTLE_ENDIAN)\n            .asFloatBuffer()\n            .put(floats);\n        return bytes;\n    }","section_id":"define-a-helper-method"},{"id":"create-a-tokenizer-instance-ex0","language":"java","code":"Predictor<String, float[]> predictor = null;\n\n        try {\n            Criteria<String, float[]> criteria = Criteria.builder().setTypes(String.class, float[].class)\n                    .optModelUrls(\"djl://ai.djl.huggingface.pytorch/sentence-transformers/all-MiniLM-L6-v2\")\n                    .optEngine(\"PyTorch\").optTranslatorFactory(new TextEmbeddingTranslatorFactory())\n                    .optProgress(new ProgressBar()).build();\n\n            predictor = criteria.loadModel().newPredictor();\n        } catch (Exception e) {\n            // ...\n        }","section_id":"create-a-tokenizer-instance"},{"id":"create-the-index-ex0","language":"java","code":"RedisClient jedis = RedisClient.create(\"redis://localhost:6379\");\n        \n        try {jedis.ftDropIndex(\"vector_idx\");} catch (JedisDataException j){}","section_id":"create-the-index"},{"id":"create-the-index-ex1","language":"java","code":"SchemaField[] schema = {\n            TextField.of(\"content\"),\n            TagField.of(\"genre\"),\n            VectorField.builder()\n                .fieldName(\"embedding\")\n                .algorithm(VectorAlgorithm.HNSW)\n                .attributes(\n                    Map.of(\n                        \"TYPE\", \"FLOAT32\",\n                        \"DIM\", 384,\n                        \"DISTANCE_METRIC\", \"L2\"\n                    )\n                )\n                .build()\n        };\n        \n        jedis.ftCreate(\"vector_idx\",\n            FTCreateParams.createParams()\n                .addPrefix(\"doc:\")\n                .on(IndexDataType.HASH),\n                schema\n        );","section_id":"create-the-index"},{"id":"add-data-ex0","language":"java","code":"String sentence1 = \"That is a very happy person\";\n        byte[] embedding1;\n\n        try {\n            embedding1 = floatsToByteString(predictor.predict(sentence1));\n        } catch (Exception e) {\n            // This just allows the code to compile without errors.\n            // In a real-world scenario, you would handle the exception properly.\n            embedding1 = new byte[384 * Float.BYTES];\n        }\n\n        jedis.hset(\"doc:1\", Map.of(  \"content\", sentence1, \"genre\", \"persons\"));\n        jedis.hset(\"doc:1\".getBytes(), \"embedding\".getBytes(), embedding1);\n        \n        String sentence2 = \"That is a happy dog\";\n        byte[] embedding2;\n\n        try {\n            embedding2 = floatsToByteString(predictor.predict(sentence2));\n        } catch (Exception e) {\n            embedding2 = new byte[384 * Float.BYTES];\n        }\n        \n        jedis.hset(\"doc:2\", Map.of(  \"content\", sentence2, \"genre\", \"pets\"));\n        jedis.hset(\"doc:2\".getBytes(), \"embedding\".getBytes(), embedding2);\n\n        String sentence3 = \"Today is a sunny day\";\n        byte[] embedding3;\n\n        try {\n            embedding3 = floatsToByteString(predictor.predict(sentence3));\n        } catch (Exception e) {\n            embedding3 = new byte[384 * Float.BYTES];\n        }\n\n        Map<String, String> doc3 = Map.of(  \"content\", sentence3, \"genre\", \"weather\");\n        jedis.hset(\"doc:3\", doc3);\n        jedis.hset(\"doc:3\".getBytes(), \"embedding\".getBytes(), embedding3);","section_id":"add-data"},{"id":"bulk-import-with-himport-ex0","language":"java","code":"String[][] docData = {\n            {\"doc:1\", \"That is a very happy person\", \"persons\"},\n            {\"doc:2\", \"That is a happy dog\", \"pets\"},\n            {\"doc:3\", \"Today is a sunny day\", \"weather\"}\n        };\n\n        try (HashImport fields = HashImport.of(\"content\", \"genre\", \"embedding\");\n                Pipeline pipeline = jedis.pipelined()) {\n            for (String[] doc : docData) {\n                byte[] docEmbedding;\n\n                try {\n                    docEmbedding = floatsToByteString(predictor.predict(doc[1]));\n                } catch (Exception e) {\n                    docEmbedding = new byte[384 * Float.BYTES];\n                }\n\n                pipeline.himportSet(doc[0].getBytes(), fields,\n                        doc[1].getBytes(), doc[2].getBytes(), docEmbedding);\n            }\n            pipeline.sync();\n        }","section_id":"bulk-import-with-himport"},{"id":"run-a-query-ex0","language":"java","code":"String sentence = \"That is a happy person\";\n        byte[] embedding;\n\n        try {\n            embedding = floatsToByteString(predictor.predict(sentence));\n        } catch (Exception e) {\n            embedding = new byte[384 * Float.BYTES];\n        }\n        \n        int K = 3;\n        Query q = new Query(\"*=>[KNN $K @embedding $BLOB AS distance]\").\n                            returnFields(\"content\", \"distance\").\n                            addParam(\"K\", K).\n                            addParam(\"BLOB\", embedding)\n                            .setSortBy(\"distance\", true)\n                            .dialect(2);\n\n        // Execute the query\n        List<Document> docs = jedis.ftSearch(\"vector_idx\", q).getDocuments();\n        System.out.println(\"Results:\");\n        \n        for (Document doc: docs) {\n            System.out.println(\n                String.format(\n                    \"ID: %s, Distance: %s, Content: %s\",\n                    doc.getId(),\n                    doc.get(\"distance\"),\n                    doc.get(\"content\")\n                )\n            );\n        }","section_id":"run-a-query"},{"id":"run-a-query-ex1","language":"plaintext","code":"Results:\nID: doc:1, Distance: 0.114169836044, Content: That is a very happy person\nID: doc:2, Distance: 0.610845506191, Content: That is a happy dog\nID: doc:3, Distance: 1.48624765873, Content: Today is a sunny day","section_id":"run-a-query"},{"id":"differences-with-json-documents-ex0","language":"java","code":"SchemaField[] jsonSchema = {\n            TextField.of(\"$.content\").as(\"content\"),\n            TagField.of(\"$.genre\").as(\"genre\"),\n            VectorField.builder()\n                .fieldName(\"$.embedding\").as(\"embedding\")\n                .algorithm(VectorAlgorithm.HNSW)\n                .attributes(\n                    Map.of(\n                        \"TYPE\", \"FLOAT32\",\n                        \"DIM\", 384,\n                        \"DISTANCE_METRIC\", \"L2\"\n                    )\n                )\n                .build()\n        };\n        \n        jedis.ftCreate(\"vector_json_idx\",\n            FTCreateParams.createParams()\n                .addPrefix(\"jdoc:\")\n                .on(IndexDataType.JSON),\n                jsonSchema\n        );","section_id":"differences-with-json-documents"},{"id":"differences-with-json-documents-ex1","language":"java","code":"String jSentence1 = \"That is a very happy person\";\n\n        float[] jEmbedding1;\n\n        try {\n            jEmbedding1 = predictor.predict(jSentence1);\n        } catch (Exception e) {\n            // This just allows the code to compile without errors.\n            // In a real-world scenario, you would handle the exception properly.\n            jEmbedding1 = new float[384];\n        }\n\n        JSONObject jdoc1 = new JSONObject()\n                .put(\"content\", jSentence1)\n                .put(\"genre\", \"persons\")\n                .put(\n                    \"embedding\",\n                    jEmbedding1\n                );\n\n        jedis.jsonSet(\"jdoc:1\", Path2.ROOT_PATH, jdoc1);\n\n        String jSentence2 = \"That is a happy dog\";\n\n        float[] jEmbedding2;\n\n        try {\n            jEmbedding2 = predictor.predict(jSentence2);\n        } catch (Exception e) {\n            jEmbedding2 = new float[384];\n        }\n\n        JSONObject jdoc2 = new JSONObject()\n                .put(\"content\", jSentence2)\n                .put(\"genre\", \"pets\")\n                .put(\n                    \"embedding\",\n                    jEmbedding2\n                );\n        \n        jedis.jsonSet(\"jdoc:2\", Path2.ROOT_PATH, jdoc2);\n\n        String jSentence3 = \"Today is a sunny day\";\n\n        float[] jEmbedding3;\n\n        try {\n            jEmbedding3 = predictor.predict(jSentence3);\n        } catch (Exception e) {\n            jEmbedding3 = new float[384];\n        }\n\n        JSONObject jdoc3 = new JSONObject()\n                .put(\"content\", jSentence3)\n                .put(\"genre\", \"weather\")\n                .put(\n                    \"embedding\",\n                    jEmbedding3\n                );\n\n        jedis.jsonSet(\"jdoc:3\", Path2.ROOT_PATH, jdoc3);","section_id":"differences-with-json-documents"},{"id":"differences-with-json-documents-ex2","language":"java","code":"String jSentence = \"That is a happy person\";\n        byte[] jEmbedding;\n\n        try {\n            jEmbedding = floatsToByteString(predictor.predict(jSentence));\n        } catch (Exception e) {\n            jEmbedding = new byte[384 * Float.BYTES];\n        }\n\n        int jK = 3;\n        Query jq = new Query(\"*=>[KNN $K @embedding $BLOB AS distance]\").\n                            returnFields(\"content\", \"distance\").\n                            addParam(\"K\", jK).\n                            addParam(\n                                \"BLOB\",\n                                jEmbedding\n                            )\n                            .setSortBy(\"distance\", true)\n                            .dialect(2);\n\n        // Execute the query\n        List<Document> jDocs = jedis\n                .ftSearch(\"vector_json_idx\", jq)\n                .getDocuments();\n\n        System.out.println(\"Results:\");\n        \n        for (Document doc: jDocs) {\n            System.out.println(\n                String.format(\n                    \"ID: %s, Distance: %s, Content: %s\",\n                    doc.getId(),\n                    doc.get(\"distance\"),\n                    doc.get(\"content\")\n                )\n            );\n        }","section_id":"differences-with-json-documents"},{"id":"differences-with-json-documents-ex3","language":"plaintext","code":"Results:\nID: jdoc:1, Distance: 0.114169836044, Content: That is a very happy person\nID: jdoc:2, Distance: 0.610845506191, Content: That is a happy dog\nID: jdoc:3, Distance: 1.48624765873, Content: Today is a sunny day","section_id":"differences-with-json-documents"}]}
{"schema_version":2,"id":"develop/clients/jedis/vecsets","title":"Vector set embeddings","url":"https://redis.io/docs/latest/develop/clients/jedis/vecsets/","summary":"Index and query embeddings with Redis vector sets","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-08T15:41:24+01:00","page_type":"content","content_hash":"25f2347e5f8d2f2d4eca6757e836d5f204cdf4fd2af015378226101ef50f36ed","sections":[{"id":"initialize","title":"Initialize","role":"content","text":"If you are using [Maven](https://maven.apache.org/), add the following\ndependencies to your `pom.xml` file\n(note that you need `Jedis` v6.2.0 or later to use vector sets):\n\n[code example]\n\nIf you are using [Gradle](https://gradle.org/), add the following\ndependencies to your `build.gradle` file:\n\n[code example]\n\nIn a new Java file, import the required classes:\n\nFoundational: Import required libraries for vector sets, embeddings, and Redis operations\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n\n\nThe imports include the classes required to generate embeddings from text.\nThis example uses an instance of the `Predictor` class with the\n[`all-MiniLM-L6-v2`](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2)\nmodel for the embeddings. This model generates vectors with 384 dimensions, regardless\nof the length of the input text, but note that the input is truncated to 256\ntokens (see\n[Word piece tokenization](https://huggingface.co/learn/nlp-course/en/chapter6/6)\nat the [Hugging Face](https://huggingface.co/) docs to learn more about the way tokens\nare related to the original text).\n\nFoundational: Initialize a tokenizer and embedding model to generate vector representations from text\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]"},{"id":"create-the-data","title":"Create the data","role":"content","text":"The example data is contained in a `List<Person>` object with some brief\ndescriptions of famous people.\n\nFoundational: Define sample data with text descriptions for vector embedding and storage\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]"},{"id":"add-the-data-to-a-vector-set","title":"Add the data to a vector set","role":"content","text":"The code below connects to Redis, then iterates through all the items in the `people` list,\ngenerates embeddings for each person's description, and then\nadds the appropriate elements to a vector set called `famousPeople`.\nNote that the `predict()` call is in a `try`/`catch` block because it can throw\nexceptions if it can't download the embedding model (you should add code to handle\nthe exceptions for production).\n\nThe call to `vadd()` also adds the `born` and `died` values from the\noriginal `people` list as attribute data. You can access this during a query\nor by using the [`vgetattr()`](https://redis.io/docs/latest/commands/vgetattr) method.\n\nFoundational: Add vector embeddings and attributes to a vector set using VADD command\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]"},{"id":"query-the-vector-set","title":"Query the vector set","role":"content","text":"You can now query the data in the set. The basic approach is to use the\n`predict()` method to generate another embedding vector for the query text.\n(This is the same method used to add the elements to the set.) Then, pass\nthe query vector to [`vsim()`](https://redis.io/docs/latest/commands/vsim) to return elements\nof the set, ranked in order of similarity to the query.\n\nStart with a simple query for \"actors\":\n\nVector similarity search: Find semantically similar items in a vector set using VSIM command\n\n**Difficulty:** Intermediate\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n\n\nThis returns the following list of elements (formatted slightly for clarity):\n\n[code example]\n\nThe first two people in the list are the two actors, as expected, but none of the\npeople from Linus Pauling onward was especially well-known for acting (and there certainly\nisn't any information about that in the short description text).\nAs it stands, the search attempts to rank all the elements in the set, based\non the information contained in the embedding model.\nYou can use the `count` parameter of `vsim()` to limit the list of elements\nto just the most relevant few items:\n\nVector similarity search with limits: Restrict results to the top K most similar items using the count parameter\n\n**Difficulty:** Intermediate\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n\n\nThe reason for using text embeddings rather than simple text search\nis that the embeddings represent semantic information. This allows a query\nto find elements with a similar meaning even if the text is\ndifferent. For example, the word \"entertainer\" doesn't appear in any of the\ndescriptions, but if you use it as a query, the actors and musicians are ranked\nhighest in the results list:\n\nSemantic search: Leverage text embeddings to find semantically similar items even when exact keywords don't match\n\n**Difficulty:** Intermediate\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n\n\nSimilarly, if you use \"science\" as a query, you get the following results:\n\n[code example]\n\nThe scientists are ranked highest, followed by the\nmathematicians. This ranking seems reasonable given the connection between mathematics and science.\n\nYou can also use\n[filter expressions](https://redis.io/docs/latest/develop/data-types/vector-sets/filtered-search)\nwith `vsim()` to restrict the search further. For example,\nrepeat the \"science\" query, but this time limit the results to people\nwho died before the year 2000:\n\nFiltered vector search: Combine vector similarity with attribute filters to narrow results based on metadata conditions\n\n**Difficulty:** Advanced\n\n**Available in:** Java (Synchronous - Jedis)\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n\n\nNote that the boolean filter expression is applied to items in the list\nbefore the vector distance calculation is performed. Items that don't\npass the filter test are removed from the results completely, rather\nthan just reduced in rank. This can help to improve the performance of the\nsearch because there is no need to calculate the vector distance for\nelements that have already been filtered out of the search."},{"id":"more-information","title":"More information","role":"content","text":"See the [vector sets](https://redis.io/docs/latest/develop/data-types/vector-sets)\ndocs for more information and code examples. See the\n[Redis for AI](https://redis.io/docs/latest/develop/ai) section for more details\nabout text embeddings and other AI techniques you can use with Redis.\n\nYou may also be interested in\n[vector search](https://redis.io/docs/latest/develop/clients/jedis/vecsearch).\nThis is a feature of\n[Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query)\nthat lets you retrieve\n[JSON](https://redis.io/docs/latest/develop/data-types/json) and\n[hash](https://redis.io/docs/latest/develop/data-types/hashes) documents based on\nvector data stored in their fields."}],"examples":[{"id":"initialize-ex0","language":"xml","code":"<dependency>\n    <groupId>redis.clients</groupId>\n    <artifactId>jedis</artifactId>\n    <version>7.2.0</version>\n</dependency>\n\n<dependency>\n    <groupId>ai.djl.huggingface</groupId>\n    <artifactId>tokenizers</artifactId>\n    <version>0.33.0</version>\n</dependency>\n\n<dependency>\n    <groupId>ai.djl.pytorch</groupId>\n    <artifactId>pytorch-model-zoo</artifactId>\n    <version>0.33.0</version>\n</dependency>\n\n<dependency>\n    <groupId>ai.djl</groupId>\n    <artifactId>api</artifactId>\n    <version>0.33.0</version>\n</dependency>","section_id":"initialize"},{"id":"initialize-ex1","language":"bash","code":"compileOnly 'redis.clients:jedis:7.2.0'\ncompileOnly 'ai.djl.huggingface:tokenizers:0.33.0'\ncompileOnly 'ai.djl.pytorch:pytorch-model-zoo:0.33.0'\ncompileOnly 'ai.djl:api:0.33.0'","section_id":"initialize"},{"id":"initialize-ex2","language":"java","code":"import redis.clients.jedis.RedisClient;\nimport redis.clients.jedis.params.VAddParams;\nimport redis.clients.jedis.params.VSimParams;\n\nimport java.util.*;\n\n// DJL classes for model loading and inference.\nimport ai.djl.huggingface.translator.TextEmbeddingTranslatorFactory;\nimport ai.djl.inference.Predictor;\nimport ai.djl.repository.zoo.Criteria;\nimport ai.djl.training.util.ProgressBar;","section_id":"initialize"},{"id":"initialize-ex3","language":"java","code":"Predictor<String, float[]> predictor = null;\n\n        try {\n            Criteria<String, float[]> criteria = Criteria.builder().setTypes(String.class, float[].class)\n                    .optModelUrls(\"djl://ai.djl.huggingface.pytorch/sentence-transformers/all-MiniLM-L6-v2\")\n                    .optEngine(\"PyTorch\").optTranslatorFactory(new TextEmbeddingTranslatorFactory())\n                    .optProgress(new ProgressBar()).build();\n\n            predictor = criteria.loadModel().newPredictor();\n        } catch (Exception e) {\n            // ...\n        }","section_id":"initialize"},{"id":"create-the-data-ex0","language":"java","code":"final class Person {\n            final String name;\n            final int born;\n            final int died;\n            final String description;\n            Person(String name, int born, int died, String description) {\n                this.name = name; this.born = born; this.died = died; this.description = description;\n            }\n        }\n\n        List<Person> people = Arrays.asList(\n            new Person(\n                \"Marie Curie\",\n                1867, 1934,\n                \"Polish-French chemist and physicist. The only person ever to win\" +\n                \" two Nobel prizes for two different sciences.\"\n            ),\n            new Person(\n                \"Linus Pauling\",\n                1901, 1994,\n                \"American chemist and peace activist. One of only two people to\" +\n                \" win two Nobel prizes in different fields (chemistry and peace).\"\n            ),\n            new Person(\n                \"Freddie Mercury\",\n                1946, 1991,\n                \"British musician, best known as the lead singer of the rock band Queen.\"\n            ),\n            new Person(\n                \"Marie Fredriksson\",\n                1958, 2019,\n                \"Swedish multi-instrumentalist, mainly known as the lead singer and\" +\n                \" keyboardist of the band Roxette.\"\n            ),\n            new Person(\n                \"Paul Erdos\",\n                1913, 1996,\n                \"Hungarian mathematician, known for his eccentric personality almost\" +\n                \" as much as his contributions to many different fields of mathematics.\"\n            ),\n            new Person(\n                \"Maryam Mirzakhani\",\n                1977, 2017,\n                \"Iranian mathematician. The first woman ever to win the Fields medal\" +\n                \" for her contributions to mathematics.\"\n            ),\n            new Person(\n                \"Masako Natsume\",\n                1957, 1985,\n                \"Japanese actress. She was very famous in Japan but was primarily\" +\n                \" known elsewhere in the world for her portrayal of Tripitaka in the\" +\n                \" TV series Monkey.\"\n            ),\n            new Person(\n                \"Chaim Topol\",\n                1935, 2023,\n                \"Israeli actor and singer, usually credited simply as 'Topol'. He was\" +\n                \" best known for his many appearances as Tevye in the musical Fiddler\" +\n                \" on the Roof.\"\n            )\n        );","section_id":"create-the-data"},{"id":"add-the-data-to-a-vector-set-ex0","language":"java","code":"RedisClient jedis = RedisClient.create(\"redis://localhost:6379\");\n\n        for (Person person : people) {\n            float[] embedding;\n            try {\n                embedding = predictor.predict(person.description);\n            } catch (Exception e) {\n                // This just allows the code to compile without errors.\n                // In a real-world scenario, you would handle the exception properly.\n                embedding = new float[384];\n            }\n\n            // Add element with attributes using VAddParams\n            String attrs = String.format(\"{\\\"born\\\": %d, \\\"died\\\": %d}\", person.born, person.died);\n            boolean added = jedis.vadd(\"famousPeople\", embedding, person.name, new VAddParams().setAttr(attrs));\n            System.out.println(added); // >>> true\n        }","section_id":"add-the-data-to-a-vector-set"},{"id":"query-the-vector-set-ex0","language":"java","code":"float[] actorsEmbedding;\n        try {\n            actorsEmbedding = predictor.predict(\"actors\");\n        } catch (Exception e) {\n            actorsEmbedding = new float[384];\n        }\n\n        List<String> basicResults = jedis.vsim(\"famousPeople\", actorsEmbedding);\n        System.out.println(basicResults);\n        // >>> [Masako Natsume, Chaim Topol, Linus Pauling, Marie Fredriksson,\n        // >>> Maryam Mirzakhani, Marie Curie, Freddie Mercury, Paul Erdos]","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex1","language":"plaintext","code":"['Masako Natsume', 'Chaim Topol', 'Linus Pauling',\n'Marie Fredriksson', 'Maryam Mirzakhani', 'Marie Curie',\n'Freddie Mercury', 'Paul Erdos']","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex2","language":"java","code":"VSimParams limitParams = new VSimParams().count(2);\n        List<String> limitedResults = jedis.vsim(\"famousPeople\", actorsEmbedding, limitParams);\n        System.out.println(limitedResults);\n        // >>> [Masako Natsume, Chaim Topol]","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex3","language":"java","code":"float[] entertainerEmbedding;\n        try {\n            entertainerEmbedding = predictor.predict(\"entertainers\");\n        } catch (Exception e) {\n            entertainerEmbedding = new float[384];\n        }\n\n        List<String> entertainerResults = jedis.vsim(\"famousPeople\", entertainerEmbedding);\n        System.out.println(entertainerResults);\n        // >>> [Freddie Mercury, Chaim Topol, Linus Pauling, Marie Fredriksson,\n        // >>> Masako Natsume, Paul Erdos, Maryam Mirzakhani, Marie Curie]","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex4","language":"plaintext","code":"['Marie Curie', 'Linus Pauling', 'Maryam Mirzakhani',\n'Paul Erdos', 'Marie Fredriksson', 'Freddie Mercury', 'Masako Natsume',\n'Chaim Topol']","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex5","language":"java","code":"float[] science2000Embedding;\n        try {\n            science2000Embedding = predictor.predict(\"science\");\n        } catch (Exception e) {\n            science2000Embedding = new float[384];\n        }\n\n        VSimParams filterParams = new VSimParams().filter(\".died < 2000\");\n        List<String> filteredResults = jedis.vsim(\"famousPeople\", science2000Embedding, filterParams);\n        System.out.println(filteredResults);\n        // >>> [Marie Curie, Linus Pauling, Paul Erdos, Freddie Mercury, Masako Natsume]","section_id":"query-the-vector-set"}]}
{"schema_version":2,"id":"develop/clients/lettuce/amr","title":"Connect to Azure Managed Redis","url":"https://redis.io/docs/latest/develop/clients/lettuce/amr/","summary":"Learn how to authenticate to an Azure Managed Redis (AMR) database","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-16T10:19:56+01:00","page_type":"content","content_hash":"5f19851f934140a8460278657e0093efaf1639c199067d038ba5afa5e2a6838b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The [`redis-authx-entraid`](https://github.com/redis/jvm-redis-authx-entraid) package\nlets you authenticate your app to\n[Azure Managed Redis (AMR)](https://azure.microsoft.com/en-us/products/managed-redis)\nusing [Microsoft Entra ID](https://learn.microsoft.com/en-us/entra/identity/).\nYou can authenticate using a system-assigned or user-assigned\n[managed identity](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview)\nor a [service principal](https://learn.microsoft.com/en-us/entra/identity-platform/app-objects-and-service-principals),\nletting `redis-authx-entraid` fetch and renew the authentication tokens for you automatically.\n\nSee\n[Use Microsoft Entra for cache authentication](https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-azure-active-directory-for-authentication)\nin the Microsoft docs to learn how to configure Azure to use Entra ID authentication."},{"id":"install","title":"Install","role":"setup","text":"Install [`Lettuce`](https://redis.io/docs/latest/develop/clients/lettuce) first,\nif you have not already done so.\n\nIf you are using Maven, add\nthe following dependency to your `pom.xml` file:\n\n[code example]\n\nIf you are using Gradle, add the following dependency to your\n`build.gradle` file:\n\n[code example]"},{"id":"create-a-tokenbasedrediscredentialsprovider-instance","title":"Create a `TokenBasedRedisCredentialsProvider` instance","role":"content","text":"The `TokenBasedRedisCredentialsProvider` class contains the authentication details that you\nmust supply when you connect to Redis. Chain the methods of the\n`EntraIDTokenAuthConfigBuilder` class together (starting with the `builder()`\nmethod) to include the details you need, as shown in the following example:\n\n[code example]\n\nSome of the details you can supply are common to different use cases:\n\n-   `secret()`: A string containing the [authentication secret](https://learn.microsoft.com/en-us/purview/sit-defn-azure-ad-client-secret).\n-   `authority()`: A string containing the [authority](https://learn.microsoft.com/en-us/entra/identity-platform/msal-client-application-configuration#authority)\n    URL.\n-   `scopes()`: A set of strings defining the [scopes](https://learn.microsoft.com/en-us/entra/identity-platform/scopes-oidc)\n    you want to apply. Configure your client application to acquire a Microsoft Entra token for scope, `https://redis.azure.com/.default` or `acca5fbb-b7e4-4009-81f1-37e38fd66d78/.default`\n    with the\n    [Microsoft Authentication Library (MSAL)](https://learn.microsoft.com/en-us/entra/identity-platform/msal-overview)\n\nYou can also add configuration to authenticate with a [service principal](#serv-principal)\nor a [managed identity](#mgd-identity) as described in the sections below.\n\nWhen you have created your `TokenBasedRedisCredentialsProvider` instance, you may want to\ntest it by obtaining a token, as shown in the following example. The\n`resolveCredentialsAsync()` method requires Lettuce 7.9 or later.\n\n[code example]"},{"id":"serv-principal","title":"Configuration for a service principal","role":"configuration","text":"Add `clientId()` to the `EntraIDTokenAuthConfigBuilder` chain to specify\nauthentication via a service principal, passing the ID token string as\na parameter. (See the\n[Microsoft EntraID docs](https://learn.microsoft.com/en-us/entra/identity-platform/app-objects-and-service-principals)\nfor more information about service principals.)\n\n[code example]"},{"id":"mgd-identity","title":"Configuration for a managed identity","role":"configuration","text":"You can also authenticate to AMR using a managed identity (see the\n[Microsoft documentation](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview) to learn more about managed identities).\n\nFor a system assigned managed identity, simply add the `systemAssignedManagedIdentity()`\nmethod to the `EntraIDTokenAuthConfigBuilder` chain:\n\n[code example]\n\nFor a user assigned managed identity, add `userAssignedManagedIdentity()`. This\nrequires a member of the `UserManagedIdentityType` enum (to select a\n`CLIENT_ID`, `OBJECT_ID`, or `RESOURCE_ID`) as well as the `id` string itself:\n\n[code example]"},{"id":"connect-using-the-withauthentication-option","title":"Connect using the `withAuthentication()` option","role":"content","text":"When you have created your `TokenBasedRedisCredentialsProvider` instance, you are ready to\nconnect to AMR.\nThe example below shows how to include the authentication details in a\n`TokenBasedRedisCredentialsProvider` instance and pass it to the `RedisURI.Builder`\nusing the `withAuthentication()` option. It also uses a `ClusterClientOptions` object to\nenable automatic re-authentication.\n\nThe connection uses\n[Transport Layer Security (TLS)](https://en.wikipedia.org/wiki/Transport_Layer_Security),\nwhich is recommended and enabled by default for managed identities. See\n[TLS connection](https://redis.io/docs/latest/develop/clients/lettuce/connect#tls-connection) for more information.\n\n> [!NOTE]\n> The `Lettuce` client library doesn't manage the lifecycle of\n> the `TokenBasedRedisCredentialsProvider` instance for you. You can reuse the\n> same instance for as many clients and connections as you want. When you have\n> finished using the credentials provider, call its `close()` method, as shown\n> at the end of the example.\n\n[code example]"}],"examples":[{"id":"install-ex0","language":"xml","code":"<dependency>\n    <groupId>redis.clients.authentication</groupId>\n    <artifactId>redis-authx-entraid</artifactId>\n    <version>0.1.1-beta1</version>\n</dependency>","section_id":"install"},{"id":"install-ex1","language":"bash","code":"implementation 'redis.clients.authentication:redis-authx-entraid:0.1.1-beta1'","section_id":"install"},{"id":"create-a-tokenbasedrediscredentialsprovider-instance-ex0","language":"java","code":"TokenBasedRedisCredentialsProvider credentials;\n\ntry ( EntraIDTokenAuthConfigBuilder builder = EntraIDTokenAuthConfigBuilder.builder()) {\n        builder.clientId(CLIENT_ID)\n                .secret(CLIENT_SECRET)\n                .authority(AUTHORITY) // \"https://login.microsoftonline.com/{YOUR_TENANT_ID}\";\n                .scopes(SCOPES);      // \"https://redis.azure.com/.default\";\n\n        credentials = TokenBasedRedisCredentialsProvider.create(builder.build());\n}","section_id":"create-a-tokenbasedrediscredentialsprovider-instance"},{"id":"create-a-tokenbasedrediscredentialsprovider-instance-ex1","language":"java","code":"// Test that the Entra ID credentials provider can resolve credentials.\ncredentials.resolveCredentialsAsync()\n        .thenAccept(c -> System.out.println(c.getUsername()))\n        .toCompletableFuture()\n        .join();","section_id":"create-a-tokenbasedrediscredentialsprovider-instance"},{"id":"serv-principal-ex0","language":"java","code":"TokenBasedRedisCredentialsProvider credentials;\n\ntry ( EntraIDTokenAuthConfigBuilder builder = EntraIDTokenAuthConfigBuilder.builder()) {\n        builder.clientId(CLIENT_ID)\n                .secret(CLIENT_SECRET)\n                .authority(AUTHORITY) // \"https://login.microsoftonline.com/{YOUR_TENANT_ID}\";\n                .scopes(SCOPES);      // \"https://redis.azure.com/.default\";\n\n        credentials = TokenBasedRedisCredentialsProvider.create(builder.build());\n}","section_id":"serv-principal"},{"id":"mgd-identity-ex0","language":"java","code":"TokenBasedRedisCredentialsProvider credentials;\n\ntry ( EntraIDTokenAuthConfigBuilder builder = EntraIDTokenAuthConfigBuilder.builder()) {\n        builder.clientId(CLIENT_ID)\n                // ...\n                .systemAssignedManagedIdentity();\n\n        credentials = TokenBasedRedisCredentialsProvider.create(builder.build());\n}","section_id":"mgd-identity"},{"id":"mgd-identity-ex1","language":"java","code":"TokenBasedRedisCredentialsProvider credentials;\n\ntry ( EntraIDTokenAuthConfigBuilder builder = EntraIDTokenAuthConfigBuilder.builder()) {\n        builder.clientId(CLIENT_ID)\n                // ...\n                .userAssignedManagedIdentity(\n                        UserManagedIdentityType.CLIENT_ID,\n                        \"<ID>\"\n                );\n\n        credentials = TokenBasedRedisCredentialsProvider.create(builder.build());\n}","section_id":"mgd-identity"},{"id":"connect-using-the-withauthentication-option-ex0","language":"java","code":"// Entra ID credentials provider for Service Principal Identity with Client Secret.\nTokenBasedRedisCredentialsProvider credentialsSP;\ntry (EntraIDTokenAuthConfigBuilder builder = EntraIDTokenAuthConfigBuilder.builder()) {\n        builder\n                .clientId(CLIENT_ID)\n                .secret(CLIENT_SECRET).authority(AUTHORITY) // \"https://login.microsoftonline.com/{YOUR_TENANT_ID}\"\n                .scopes(SCOPES); // \"https://redis.azure.com/.default\"\n\n        credentialsSP = TokenBasedRedisCredentialsProvider.create(builder.build());\n}\n\n// Optionally test the credentials provider.\n// credentialsSP.resolveCredentialsAsync().thenAccept(c -> System.out.println(\"SPI ID :\" + c.getUsername())).toCompletableFuture().join();\n\n// Enable automatic re-authentication.\nClusterClientOptions clientOptions = ClusterClientOptions.builder()\n        .reauthenticateBehavior(\n                ClientOptions.ReauthenticateBehavior.ON_NEW_CREDENTIALS\n        ).build();\n\n// Use the Entra ID credentials provider.\nRedisURI redisURI = RedisURI.builder()\n        .withHost(HOST)\n        .withPort(PORT)\n        .withAuthentication(credentialsSP)\n        .withSsl(true)\n        .build();\n\n// Create the RedisClusterClient and set the re-authentication options.\n// Note: AMR databases have clustering enabled by default.\nRedisClusterClient redisClient = RedisClusterClient.create(redisURI);\nredisClient.setOptions(clientOptions);\n\n// Connect with the credentials provider.\ntry (StatefulRedisClusterConnection<String, String> user1 = redisClient.connect(StringCodec.UTF8)) {\n        System.out.println(\"Connected to redis as :\" + user1.sync().aclWhoami());\n        System.out.println(\"Db size :\" + user1.sync().dbsize());\n} finally {\n        redisClient.shutdown();  // Shutdown Redis client and close connections.\n        credentialsSP.close(); // Shutdown Entra ID Credentials provider.\n}","section_id":"connect-using-the-withauthentication-option"}]}
{"schema_version":2,"id":"develop/clients/lettuce/connect","title":"Connect to the server","url":"https://redis.io/docs/latest/develop/clients/lettuce/connect/","summary":"Connect your Java application to a Redis database","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-12T14:48:04+01:00","page_type":"content","content_hash":"b6a9ea80aa89efda80465512c5246358419a6ce5cb61cdf231e2151fcdb6f008","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Start by creating a connection to your Redis server. There are many ways to achieve this using Lettuce. Here are a few."},{"id":"basic-connection","title":"Basic connection","role":"content","text":"[code example]\n\n> [!NOTE]\n> Lettuce supports [connection URLs](https://redis.io/docs/latest/develop/clients/connection-urls#lettuce-java) through `RedisClient.create()`.\n> A URL can set the host, port, credentials, database, and whether to use TLS,\n> so you can use one instead of passing those options separately."},{"id":"connect-to-a-redis-cluster","title":"Connect to a Redis cluster","role":"content","text":"To connect to a Redis cluster, use `RedisClusterClient`. \n\n[code example]\n\nLearn more about Cluster connections and how to configure them in [the reference guide](https://redis.github.io/lettuce/ha-sharding/#redis-cluster)."},{"id":"connect-to-redis-sentinel","title":"Connect to Redis Sentinel","role":"content","text":"To connect through [Redis Sentinel](https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel),\nbuild a `RedisURI` with `RedisURI.Builder.sentinel()`, passing the address of one Sentinel\nand the name of the primary that the Sentinels monitor. Add the other Sentinels with\n`withSentinel()`. The client asks the Sentinels for the current primary.\n\n[code example]\n\nThe Sentinels and the data nodes have separate credentials. Pass the Sentinel password with\neach Sentinel address, and set the credentials for the primary and replicas with\n`withAuthentication()`:\n\n[code example]\n\nA `RedisClient` connection doesn't listen for failover announcements from the Sentinels.\nAfter a failover, it keeps using the old primary until Sentinel reconfigures it as a\nreplica and closes the connection. Any writes that the old primary accepts during that time\nare lost.\n\nTo read from replicas, open a connection with `MasterReplica.connect()` and set a\n`ReadFrom` policy. This connection also listens for failover announcements from the\nSentinels, so it switches to a new primary sooner. Replication is asynchronous, so a\nreplica can return stale data.\n\n[code example]"},{"id":"asynchronous-connection","title":"Asynchronous connection","role":"content","text":"[code example]\n\nLearn more about asynchronous Lettuce API in [the reference guide](https://redis.github.io/lettuce/#asynchronous-api)."},{"id":"reactive-connection","title":"Reactive connection","role":"content","text":"[code example]\n\nLearn more about reactive Lettuce API in [the reference guide](https://redis.github.io/lettuce/#reactive-api)."},{"id":"connect-to-a-redis-cluster-1","title":"Connect to a Redis cluster","role":"content","text":"[code example]"},{"id":"tls-connection","title":"TLS connection","role":"content","text":"When you deploy your application, use TLS and follow the [Redis security guidelines](https://redis.io/docs/latest/operate/oss_and_stack/management/security).\n\n[code example]"},{"id":"connection-management-in-lettuce","title":"Connection Management in Lettuce","role":"content","text":"Lettuce uses `ClientResources` for efficient management of shared resources like event loop groups and thread pools.\nFor connection pooling, Lettuce leverages `RedisClient` or `RedisClusterClient`, which can handle multiple concurrent connections efficiently."},{"id":"connection-pooling","title":"Connection pooling","role":"content","text":"A typical approach with Lettuce is to create a single `RedisClient` instance and reuse it to establish connections to your Redis server(s).\nThese connections are multiplexed; that is, multiple commands can be run concurrently over a single or a small set of connections, making explicit pooling less practical.\nSee\n[Connection pools and multiplexing](https://redis.io/docs/latest/develop/clients/pools-and-muxing)\nfor more information.\n\nLettuce provides pool config to be used with Lettuce asynchronous connection methods.\n\n[code example]\n\nIn this setup, `LettuceConnectionFactory` is a custom class you would need to implement, adhering to Apache Commons Pool's `PooledObjectFactory` interface, to manage lifecycle events of pooled `StatefulRedisConnection` objects."},{"id":"connect-using-smart-client-handoffs-sch","title":"Connect using Smart client handoffs (SCH)","role":"content","text":"*Smart client handoffs (SCH)* is a feature of Redis Cloud and\nRedis Software servers that lets them actively notify clients\nabout planned server maintenance shortly before it happens. This\nlets a client take action to avoid disruptions in service.\nSee [Smart client handoffs](https://redis.io/docs/latest/develop/clients/sch)\nfor more information about SCH.\n\n> [!NOTE]\n> Support for SCH in Lettuce requires v7.0.0 or later.\n\nBy default, `Lettuce` always attempts to connect via SCH but falls back to\na non-SCH connection if the server doesn't support it. However, you can configure SCH\nexplicitly by creating a `MaintNotificationsConfig` object and/or a `TimeoutOptions`\nobject and passing them to the `ClientOptions` builder as shown in the example below.\nNote that SCH also requires the\n[RESP3](https://redis.io/docs/latest/develop/reference/protocol-spec#resp-versions)\nprotocol. Lettuce uses this by default, but make sure you don't set\n`protocolVersion(ProtocolVersion.RESP2)` in the `ClientOptions` builder.\n\n[code example]\n\nTo disable SCH, use `MaintNotificationsConfig.disabled()` and pass the instance it\nreturns to the `ClientOptions` builder:\n\n[code example]\n\nThe `MaintNotificationsConfig` builder accepts the following options:\n\n| Method | Description |\n|--------|-------------|\n| `enableMaintNotifications(boolean enabled)` | Enable/disable SCH. The default is `true` (enabled). |\n| `endpointType(EndpointType type)` | Set the type of endpoint to use for the connection. The options are `EndpointType.EXTERNAL_IP`, `EndpointType.INTERNAL_IP`, `EndpointType.EXTERNAL_FQDN`, `EndpointType.INTERNAL_FQDN`, and `EndpointType.NONE`. Use the separate `autoResolveEndpointType()` method to auto-detect based on the connection (this is the default behavior). |\n| `autoResolveEndpointType()` | Auto-detect the type of endpoint to use for the connection. This is the default behavior. Use `endpointType()` to set a specific endpoint type. |\n\nAmong other options, the `TimeoutOptions` builder accepts the following option\nthat is relevant to SCH:\n\n| Method | Description |\n|--------|-------------|\n| `relaxedTimeoutsDuringMaintenance(Duration duration)` | Set the *command* timeout to use while the server is performing maintenance (this doesn't change the connection timeout). The default is 10 seconds. Note that relaxed timeouts are only available for the asynchronous and reactive APIs. |\n|\n\n> [!NOTE]\n> Redis Cloud supports relaxed timeouts *only* (and not pre-handoffs) for SCH if you are using\n> either [AWS PrivateLink](https://redis.io/docs/latest/operate/rc/security/aws-privatelink) or\n> [Google Cloud Private Service Connect](https://redis.io/docs/latest/operate/rc/security/private-service-connect)\n> (see [Smart client handoffs](https://redis.io/docs/latest/develop/clients/sch#redis-cloud) for more information).\n> To use relaxed timeouts with these services, you should set `endpointType(EndpointType.NONE)`\n> when you connect. All other configurations have full support for both relaxed timeouts and pre-handoffs."}],"examples":[{"id":"basic-connection-ex0","language":"java","code":"import io.lettuce.core.*;\nimport io.lettuce.core.api.StatefulRedisConnection;\nimport io.lettuce.core.api.sync.RedisCommands;\n\npublic class ConnectBasicTest {\n\n    public void connectBasic() {\n        RedisURI uri = RedisURI.Builder\n                .redis(\"localhost\", 6379)\n                .withAuthentication(\"default\", \"yourPassword\")\n                .build();\n        RedisClient client = RedisClient.create(uri);\n        StatefulRedisConnection<String, String> connection = client.connect();\n        RedisCommands<String, String> commands = connection.sync();\n\n        commands.set(\"foo\", \"bar\");\n        String result = commands.get(\"foo\");\n        System.out.println(result); // >>> bar\n\n        connection.close();\n\n        client.shutdown();\n    }\n}","section_id":"basic-connection"},{"id":"connect-to-a-redis-cluster-ex0","language":"java","code":"import io.lettuce.core.cluster.RedisClusterClient;\nimport io.lettuce.core.cluster.api.StatefulRedisClusterConnection;\n\n//...\ntry (RedisClusterClient clusterClient = RedisClusterClient.create(redisURI)) {    \n    StatefulRedisClusterConnection<String, String> connection = clusterClient.connect();\n        \n    //...\n    \n    connection.close();    \n}","section_id":"connect-to-a-redis-cluster"},{"id":"connect-to-redis-sentinel-ex0","language":"java","code":"import io.lettuce.core.RedisClient;\nimport io.lettuce.core.RedisURI;\nimport io.lettuce.core.api.StatefulRedisConnection;\nimport io.lettuce.core.api.sync.RedisCommands;\n\n//...\n\nRedisURI redisUri = RedisURI.Builder\n        .sentinel(\"localhost\", 26379, \"mymaster\")\n        .withSentinel(\"localhost\", 26380)\n        .withSentinel(\"localhost\", 26381)\n        .build();\n\ntry (RedisClient client = RedisClient.create(redisUri)) {\n    StatefulRedisConnection<String, String> connection = client.connect();\n    RedisCommands<String, String> commands = connection.sync();\n\n    commands.set(\"foo\", \"bar\");\n    System.out.println(commands.get(\"foo\")); // >>> bar\n\n    connection.close();\n}","section_id":"connect-to-redis-sentinel"},{"id":"connect-to-redis-sentinel-ex1","language":"java","code":"RedisURI redisUri = RedisURI.Builder\n        // The password argument authenticates to each Sentinel.\n        .sentinel(\"localhost\", 26379, \"mymaster\", \"yourSentinelPassword\")\n        .withSentinel(\"localhost\", 26380, \"yourSentinelPassword\")\n        .withSentinel(\"localhost\", 26381, \"yourSentinelPassword\")\n        // withAuthentication() authenticates to the primary and replicas.\n        .withAuthentication(\"default\", \"yourPassword\")\n        .build();","section_id":"connect-to-redis-sentinel"},{"id":"connect-to-redis-sentinel-ex2","language":"java","code":"import io.lettuce.core.ReadFrom;\nimport io.lettuce.core.codec.StringCodec;\nimport io.lettuce.core.masterreplica.MasterReplica;\nimport io.lettuce.core.masterreplica.StatefulRedisMasterReplicaConnection;\n\n//...\n\ntry (RedisClient client = RedisClient.create()) {\n    StatefulRedisMasterReplicaConnection<String, String> connection =\n            MasterReplica.connect(client, StringCodec.UTF8, redisUri);\n    connection.setReadFrom(ReadFrom.REPLICA);\n\n    System.out.println(connection.sync().get(\"foo\")); // >>> bar\n\n    connection.close();\n}","section_id":"connect-to-redis-sentinel"},{"id":"asynchronous-connection-ex0","language":"java","code":"package org.example;\nimport java.util.*;\nimport java.util.concurrent.ExecutionException;\n\nimport io.lettuce.core.*;\nimport io.lettuce.core.api.async.RedisAsyncCommands;\nimport io.lettuce.core.api.StatefulRedisConnection;\n\npublic class Async {\n  public static void main(String[] args) {\n    RedisClient redisClient = RedisClient.create(\"redis://localhost:6379\");\n\n    try (StatefulRedisConnection<String, String> connection = redisClient.connect()) {\n      RedisAsyncCommands<String, String> asyncCommands = connection.async();\n\n      // Asynchronously store & retrieve a simple string\n      asyncCommands.set(\"foo\", \"bar\").get();\n      System.out.println(asyncCommands.get(\"foo\").get()); // prints bar\n\n      // Asynchronously store key-value pairs in a hash directly\n      Map<String, String> hash = new HashMap<>();\n      hash.put(\"name\", \"John\");\n      hash.put(\"surname\", \"Smith\");\n      hash.put(\"company\", \"Redis\");\n      hash.put(\"age\", \"29\");\n      asyncCommands.hset(\"user-session:123\", hash).get();\n\n      System.out.println(asyncCommands.hgetall(\"user-session:123\").get());\n      // Prints: {name=John, surname=Smith, company=Redis, age=29}\n    } catch (ExecutionException | InterruptedException e) {\n      throw new RuntimeException(e);\n    } finally {\n      redisClient.shutdown();\n    }\n  }\n}","section_id":"asynchronous-connection"},{"id":"reactive-connection-ex0","language":"java","code":"package org.example;\nimport java.util.*;\nimport io.lettuce.core.*;\nimport io.lettuce.core.api.reactive.RedisReactiveCommands;\nimport io.lettuce.core.api.StatefulRedisConnection;\n\npublic class Main {\n  public static void main(String[] args) {\n    RedisClient redisClient = RedisClient.create(\"redis://localhost:6379\");\n\n    try (StatefulRedisConnection<String, String> connection = redisClient.connect()) {\n      RedisReactiveCommands<String, String> reactiveCommands = connection.reactive();\n\n      // Reactively store & retrieve a simple string\n      reactiveCommands.set(\"foo\", \"bar\").block();\n      reactiveCommands.get(\"foo\").doOnNext(System.out::println).block(); // prints bar\n\n      // Reactively store key-value pairs in a hash directly\n      Map<String, String> hash = new HashMap<>();\n      hash.put(\"name\", \"John\");\n      hash.put(\"surname\", \"Smith\");\n      hash.put(\"company\", \"Redis\");\n      hash.put(\"age\", \"29\");\n\n      reactiveCommands.hset(\"user-session:124\", hash).then(\n              reactiveCommands.hgetall(\"user-session:124\")\n                  .collectMap(KeyValue::getKey, KeyValue::getValue).doOnNext(System.out::println))\n          .block();\n      // Prints: {surname=Smith, name=John, company=Redis, age=29}\n\n    } finally {\n      redisClient.shutdown();\n    }\n  }\n}","section_id":"reactive-connection"},{"id":"connect-to-a-redis-cluster-1-ex0","language":"java","code":"import io.lettuce.core.RedisURI;\nimport io.lettuce.core.cluster.RedisClusterClient;\nimport io.lettuce.core.cluster.api.StatefulRedisClusterConnection;\nimport io.lettuce.core.cluster.api.async.RedisAdvancedClusterAsyncCommands;\n\n// ...\n\nRedisURI redisUri = RedisURI.Builder.redis(\"localhost\").withPassword(\"authentication\").build();\n\nRedisClusterClient clusterClient = RedisClusterClient.create(redisUri);\nStatefulRedisClusterConnection<String, String> connection = clusterClient.connect();\nRedisAdvancedClusterAsyncCommands<String, String> commands = connection.async();\n\n// ...\n\nconnection.close();\nclusterClient.shutdown();","section_id":"connect-to-a-redis-cluster-1"},{"id":"tls-connection-ex0","language":"java","code":"RedisURI redisUri = RedisURI.Builder.redis(\"localhost\")\n                                 .withSsl(true)\n                                 .withPassword(\"secret!\") // use your Redis password\n                                 .build();\n\nRedisClient client = RedisClient.create(redisUri);","section_id":"tls-connection"},{"id":"connection-pooling-ex0","language":"java","code":"package org.example;\nimport io.lettuce.core.RedisClient;\nimport io.lettuce.core.RedisURI;\nimport io.lettuce.core.TransactionResult;\nimport io.lettuce.core.api.StatefulRedisConnection;\nimport io.lettuce.core.api.async.RedisAsyncCommands;\nimport io.lettuce.core.codec.StringCodec;\nimport io.lettuce.core.support.*;\n\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.CompletionStage;\n\npublic class Pool {\n  public static void main(String[] args) {\n    RedisClient client = RedisClient.create();\n\n    String host = \"localhost\";\n    int port = 6379;\n\n    CompletionStage<BoundedAsyncPool<StatefulRedisConnection<String, String>>> poolFuture\n        = AsyncConnectionPoolSupport.createBoundedObjectPoolAsync(\n            () -> client.connectAsync(StringCodec.UTF8, RedisURI.create(host, port)),\n            BoundedPoolConfig.create());\n\n    // await poolFuture initialization to avoid NoSuchElementException: Pool exhausted when starting your application\n    AsyncPool<StatefulRedisConnection<String, String>> pool = poolFuture.toCompletableFuture()\n        .join();\n\n    // execute work\n    CompletableFuture<TransactionResult> transactionResult = pool.acquire()\n        .thenCompose(connection -> {\n\n          RedisAsyncCommands<String, String> async = connection.async();\n\n          async.multi();\n          async.set(\"key\", \"value\");\n          async.set(\"key2\", \"value2\");\n          System.out.println(\"Executed commands in pipeline\");\n          return async.exec().whenComplete((s, throwable) -> pool.release(connection));\n        });\n    transactionResult.join();\n\n    // terminating\n    pool.closeAsync();\n\n    // after pool completion\n    client.shutdownAsync();\n  }\n}","section_id":"connection-pooling"},{"id":"connect-using-smart-client-handoffs-sch-ex0","language":"java","code":"import io.lettuce.core.*;\nimport io.lettuce.core.api.StatefulRedisConnection;\nimport io.lettuce.core.protocol.ProtocolVersion;\nimport java.time.Duration;\n//  ...\n//  ...\n\nRedisClient redisClient = RedisClient.create(\"redis://localhost:6379\");\n\nMaintNotificationsConfig maintNotificationsConfig = MaintNotificationsConfig.builder()\n        .enableMaintNotifications()\n        // .autoResolveEndpointType() // default is auto-resolve\n        .endpointType(EndpointType.INTERNAL_IP)\n        .build();\n\nTimeoutOptions timeoutOptions = TimeoutOptions.builder()\n        .relaxedTimeoutsDuringMaintenance(Duration.ofSeconds(10))\n        .build();\n\nClientOptions clientOptions = ClientOptions.builder()\n        .maintNotificationsConfig(maintNotificationsConfig)\n        .timeoutOptions(timeoutOptions)\n        // SCH requires RESP3. Don't override the default protocol version\n        // to RESP2:\n        // .protocolVersion(ProtocolVersion.RESP2) // <- Wrong\n        .build();\n\nredisClient.setOptions(clientOptions);\n\n// or\nClientOptions clientOptions = ClientOptions.builder()\n        .maintNotificationsConfig(MaintNotificationsConfig.enabled(EndpointType.INTERNAL_IP))\n        .build();\n\nredisClient.setOptions(clientOptions);\n\n// or\nClientOptions clientOptions = ClientOptions.builder()\n        .maintNotificationsConfig(MaintNotificationsConfig.enabled())\n        .build();\n\nredisClient.setOptions(clientOptions);","section_id":"connect-using-smart-client-handoffs-sch"},{"id":"connect-using-smart-client-handoffs-sch-ex1","language":"java","code":"ClientOptions clientOptions = ClientOptions.builder()\n        .maintNotificationsConfig(MaintNotificationsConfig.disabled())\n        .build();\n\nredisClient.setOptions(clientOptions);","section_id":"connect-using-smart-client-handoffs-sch"}]}
{"schema_version":2,"id":"develop/clients/lettuce/error-handling","title":"Error handling","url":"https://redis.io/docs/latest/develop/clients/lettuce/error-handling/","summary":"Learn how to handle errors when using Lettuce.","tags":[],"last_updated":"2026-09-09T09:47:45+01:00","page_type":"content","content_hash":"e63bd1b5cc298efd7bb8ad445350e51ba91ac0fc36e801b56ff62bf2feed2f22","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Lettuce uses **unchecked exceptions** to signal errors. Documentation examples\noften focus on the \"happy path\", but production code should catch and handle\nthe exceptions that matter for your workload. This page explains how Lettuce's\nerror handling works and how to apply common error handling patterns.\n\nFor an overview of error types and handling strategies, see\n[Error handling](https://redis.io/docs/latest/develop/clients/error-handling).\nSee also [Production usage](https://redis.io/docs/latest/develop/clients/lettuce/produsage)\nfor more information on connection management, timeouts, and other aspects of\napp reliability."},{"id":"exception-hierarchy","title":"Exception hierarchy","role":"errors","text":"Lettuce organizes its exceptions under `RedisException`, which extends\n`RuntimeException`:\n\n[code example]"},{"id":"key-exceptions","title":"Key exceptions","role":"content","text":"The following exceptions are the most commonly encountered in Lettuce\napplications. See\n[Categories of errors](https://redis.io/docs/latest/develop/clients/error-handling#categories-of-errors)\nfor a more detailed discussion of these errors and their causes.\n\n| Exception | When it occurs | Recoverable | Recommended action |\n|---|---|---|---|\n| `RedisConnectionException` | Connection setup failed or the connection was lost | ✅ | Retry with backoff or fall back |\n| `RedisCommandTimeoutException` | A command exceeded its timeout | ✅ | Retry with backoff and review timeout settings |\n| `RedisCommandExecutionException` | Redis returned an error reply such as `WRONGTYPE` | ❌ | Fix the command, arguments, or data model |\n| `RedisCommandInterruptedException` | A waiting thread was interrupted while waiting for a result | ⚠️ | Restore interrupt status and abort cleanly |"},{"id":"applying-error-handling-patterns","title":"Applying error handling patterns","role":"content","text":"The [Error handling](https://redis.io/docs/latest/develop/clients/error-handling) overview\ndescribes four main patterns. The sections below show how to implement them in\nLettuce:"},{"id":"pattern-1-fail-fast","title":"Pattern 1: Fail fast","role":"content","text":"Catch specific exceptions that represent unrecoverable errors and re-throw them\n(see\n[Pattern 1: Fail fast](https://redis.io/docs/latest/develop/clients/error-handling#pattern-1-fail-fast)\nfor a full description):\n\n[code example]"},{"id":"pattern-2-graceful-degradation","title":"Pattern 2: Graceful degradation","role":"content","text":"Catch temporary connectivity failures and fall back to an alternative (see\n[Pattern 2: Graceful degradation](https://redis.io/docs/latest/develop/clients/error-handling#pattern-2-graceful-degradation)\nfor a full description):\n\n[code example]"},{"id":"pattern-3-retry-with-backoff","title":"Pattern 3: Retry with backoff","role":"content","text":"Retry on temporary errors such as timeouts or disconnections (see\n[Pattern 3: Retry with backoff](https://redis.io/docs/latest/develop/clients/error-handling#pattern-3-retry-with-backoff)\nfor a full description):\n\n[code example]"},{"id":"pattern-4-log-and-continue","title":"Pattern 4: Log and continue","role":"content","text":"Log non-critical cache failures and continue (see\n[Pattern 4: Log and continue](https://redis.io/docs/latest/develop/clients/error-handling#pattern-4-log-and-continue)\nfor a full description):\n\n[code example]"},{"id":"async-and-reactive-error-handling","title":"Async and reactive error handling","role":"content","text":"Lettuce's async and reactive APIs report the same underlying exceptions, but\nthey usually surface them through `CompletionStage` failures or stream errors\ninstead of direct `throw` statements:\n\n[code example]"},{"id":"see-also","title":"See also","role":"related","text":"- [Error handling](https://redis.io/docs/latest/develop/clients/error-handling)\n- [Production usage](https://redis.io/docs/latest/develop/clients/lettuce/produsage)"}],"examples":[{"id":"exception-hierarchy-ex0","language":"hierarchy {type=\"exception\"}","code":"\"RedisException\":\n    _meta:\n        description: \"Base class for Lettuce runtime exceptions\"\n    \"RedisConnectionException\":\n    \"RedisCommandTimeoutException\":\n    \"RedisCommandInterruptedException\":\n    \"RedisCommandExecutionException\":\n        \"RedisLoadingException\":\n    \"...\":\n        _meta:\n            ellipsis: true\n            description: \"Other Lettuce exception types\"","section_id":"exception-hierarchy"},{"id":"pattern-1-fail-fast-ex0","language":"java","code":"try {\n    return commands.get(key);\n} catch (RedisCommandExecutionException e) {\n    // This indicates a bug in our code or data model.\n    throw e;\n}","section_id":"pattern-1-fail-fast"},{"id":"pattern-2-graceful-degradation-ex0","language":"java","code":"try {\n    String cachedValue = commands.get(key);\n    if (cachedValue != null) {\n        return cachedValue;\n    }\n} catch (RedisConnectionException e) {\n    logger.warn(\"Cache unavailable, using database\");\n}\n\nreturn database.get(key);","section_id":"pattern-2-graceful-degradation"},{"id":"pattern-3-retry-with-backoff-ex0","language":"java","code":"int maxRetries = 3;\nlong delayMs = 100;\n\nfor (int attempt = 0; attempt < maxRetries; attempt++) {\n    try {\n        return commands.get(key);\n    } catch (RedisConnectionException | RedisCommandTimeoutException e) {\n        if (attempt == maxRetries - 1) {\n            throw e;\n        }\n        try {\n            Thread.sleep(delayMs);\n            delayMs *= 2;  // Exponential backoff\n        } catch (InterruptedException ie) {\n            Thread.currentThread().interrupt();\n            throw new RuntimeException(ie);\n        }\n    }\n}\n\nthrow new IllegalStateException(\"unreachable\");","section_id":"pattern-3-retry-with-backoff"},{"id":"pattern-4-log-and-continue-ex0","language":"java","code":"try {\n    commands.setex(key, 3600, value);\n} catch (RedisConnectionException | RedisCommandTimeoutException e) {\n    logger.warn(\"Failed to cache {}, continuing without cache\", key, e);\n}","section_id":"pattern-4-log-and-continue"},{"id":"async-and-reactive-error-handling-ex0","language":"java","code":"async.get(key).whenComplete((value, error) -> {\n    if (error == null) {\n        use(value);\n        return;\n    }\n\n    Throwable cause = error.getCause() != null ? error.getCause() : error;\n    if (cause instanceof RedisConnectionException) {\n        logger.warn(\"Cache unavailable\");\n        return;\n    }\n\n    throw new RuntimeException(cause);\n});","section_id":"async-and-reactive-error-handling"}]}
{"schema_version":2,"id":"develop/clients/lettuce/failover","title":"Client-side geographic failover","url":"https://redis.io/docs/latest/develop/clients/lettuce/failover/","summary":"Improve reliability using the failover features of Lettuce.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-09T09:47:45+01:00","page_type":"content","content_hash":"f218585784c848c974f8e8a018fa0acc9217bf5afdb9f20a9c95bfaa7537d107","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Lettuce supports [Client-side geographic failover](https://en.wikipedia.org/wiki/Failover)\nto improve the availability of connections to Redis databases. This page explains\nhow to configure Lettuce for failover. For an overview of the concepts,\nsee the main [Client-side geographic failover](https://redis.io/docs/latest/develop/clients/failover) page."},{"id":"connection-configuration","title":"Connection configuration","role":"content","text":"The example below shows a simple case with a list of two servers,\n`redis-east` and `redis-west`, where `redis-east` is the preferred\ntarget. If `redis-east` fails, Lettuce should fail over to\n`redis-west`.\n\nIn your source file, import the required classes:\n\n[code example]\n\nSupply the weighted endpoints using a `List` of `DatabaseConfig` objects\n(see [Selecting a failover target](https://redis.io/docs/latest/develop/clients/failover#selecting-a-failover-target)\nfor a full description of how the weighted list is used).\nUse the `weight` option in the builder to order the endpoints, with the highest\nweight being tried first.\n\n[code example]\n\nPass the list of `DatabaseConfig` objects to a `MultiDbClient` instance\nduring creation. This class provides the same features as the standard\n`RedisClient` class, but will also handle the connection management and failover transparently.\nLikewise, the `StatefulRedisMultiDbConnection` implements the same interface\nto Redis commands as the standard `StatefulRedisConnection` class.\n\n[code example]"},{"id":"database-configuration","title":"Database configuration","role":"content","text":"Use the `DatabaseConfig` builder to configure each database in the list.\nThe builder provides the following methods:\n\n| Method | Default | Description |\n| --- | --- | --- |\n| `weight()` | `1.0f` | Priority weight for database selection. |\n| `circuitBreakerConfig()` | Default config | `CircuitBreakerConfig` object to configure failure detection. See [Circuit breaker configuration](#circuit-breaker-configuration) below for details. |\n| `healthCheckStrategySupplier()` | `PingStrategy.DEFAULT` | `HealthCheckStrategySupplier` object to configure health checks. See [Health check configuration](#health-check-configuration) below for details. |\n| `clientOptions()` | Default options | Per-database `ClientOptions` configuration. |"},{"id":"client-configuration","title":"Client configuration","role":"content","text":"Use the `MultiDbOptions` builder to supply global options for the multi-database client\nincluding failback configuration:\n\n| Method | Default | Description |\n| --- | --- | --- |\n| `failbackSupported()` | `true` | Enable automatic failback to higher-priority databases. |\n| `failbackCheckInterval()` | `Duration.ofSeconds(120)` | Interval for checking if failed databases have recovered. |\n| `gracePeriod()` | `Duration.ofSeconds(60)` | Time to wait before allowing failback to a recovered database. |\n| `delayInBetweenFailoverAttempts()` | `Duration.ofSeconds(12)` | Delay between failover attempts when no healthy database is available. |\n| `initializationPolicy()` | `MAJORITY_AVAILABLE` | `InitializationPolicy` enum value for connection initialization. |\n\nThe initialization policy is used to decide if the number of healthy databases available\nat startup is sufficient to consider the client initialized and ready to use. The\npolicies are:\n\n- `ALL_AVAILABLE` - All databases must be available.\n- `MAJORITY_AVAILABLE` - A majority of databases must be available.\n- `ONE_AVAILABLE` - At least one database must be available.\n\nThe example below shows how to supply the configuration when you create the client.\n\n[code example]"},{"id":"failover-configuration","title":"Failover configuration","role":"content","text":"The `DatabaseConfig` builder lets you pass configuration objects to specify\nthe behavior of the circuit breaker and health checks. These\nare described in the sections below."},{"id":"circuit-breaker-configuration","title":"Circuit breaker configuration","role":"content","text":"The `CircuitBreakerConfig` builder lets you pass several options to configure\nthe circuit breaker, as shown in the example below (see [Detecting connection problems](https://redis.io/docs/latest/develop/clients/failover#detecting-connection-problems) for more information on how the\ncircuit breaker works):\n\n[code example]\n\nThe builder provides the following methods:\n\n| Method | Default value | Description|\n| --- | --- | --- |\n| `metricsWindowSize()` | `2` | Duration in seconds to keep failures and successes in the sliding window. |\n| `minimumNumberOfFailures()` | `1000` | Minimum number of failures that must occur before the circuit breaker is tripped. |\n| `failureRateThreshold()` | `10.0f` | Percentage of failures to trigger the circuit breaker. |\n| `trackedExceptions()` | See description | `Set` of `Throwable` classes that should be considered as failures. The default set contains `RedisConnectionException`, `IOException`, `ConnectException`, `RedisCommandTimeoutException`, and `TimeoutException`. |"},{"id":"failover-events","title":"Failover events","role":"content","text":"You may want to take some custom action when a failover occurs.\nFor example, you could log a warning, increment a metric, \nor externally persist the cluster connection state. Lettuce provides\ntwo `Event` classes that you can subscribe to for this purpose,\n`DatabaseSwitchEvent` and `AllDatabasesUnhealthyEvent`.\n\n#### `DatabaseSwitchEvent`\n\nUse `DatabaseSwitchEvent` to detect when the client switches to a different database.\nThe class implements three methods to get information about the event:\n\n- `getFromDb()` - Returns the `RedisURI` of the database that was previously in use.\n- `getToDb()` - Returns the `RedisURI` of the database that is now in use.\n- `getReason()` - Returns one of the following `SwitchReason` enum values to describe why the switch occurred:\n   - `HEALTH_CHECK` - Database failed health check\n   - `CIRCUIT_BREAKER` - Circuit breaker opened due to failures\n   - `FAILBACK` - Automatic failback to higher-priority database\n   - `FORCED` - Manual switch via `switchTo()` (see [Manual failback](#manual-failback) below for details)\n\nThe example below shows a simple event handler that logs information about the switch.\n\n[code example]\n\n#### `AllDatabasesUnhealthyEvent`\n\nUse `AllDatabasesUnhealthyEvent` to detect when all databases become unhealthy.\nThis class implements two methods to get information about the event:\n\n- `getFailedAttempts()` - Returns the number of consecutive failover attempts that have failed.\n- `getUnhealthyDatabases()` - Returns a `List` of `RedisURI` objects for the unhealthy databases.\n\nThe example below shows a simple event handler that logs information about the unhealthy databases.\n\n[code example]"},{"id":"health-check-configuration","title":"Health check configuration","role":"content","text":"Each health check consists of one or more separate \"probes\", each of which is a simple\ntest (such as a [`PING`](https://redis.io/docs/latest/commands/ping) command) to determine if the database is available. The results of the separate probes are combined\nusing a configurable policy to determine if the database is healthy.\n\nThere are several strategies available for health checks that you can deploy using the\n`DatabaseConfig` builder. Each strategy is a class that implements the `HealthCheckStrategy` \ninterface. Use the constructor of a `HealthCheckStrategy` implementation to pass\na `HealthCheckStrategy.Config` object to configure the health check behavior.\nThe methods of the base `HealthCheckStrategy` builder are shown in the table below.\nNote that some strategies (including your own custom strategies) may use a\nsubclass of `HealthCheckStrategy.Config` to provide extra options.\n\n| Method | Default value | Description |\n| --- | --- | --- |\n| `interval()` | `1000` | Interval in milliseconds between health checks. |\n| `timeout()` | `1000` | Timeout in milliseconds for health check requests. |\n| `numProbes()` | `3` | Number of probes to perform during each health check. |\n| `delayInBetweenProbes()` | `100` | Delay in milliseconds between probes during a health check. |\n| `policy()` | `ProbingPolicy.BuiltIn.ALL_SUCCESS` | Policy to determine if the database is healthy based on the probe results. The options are `ALL_SUCCESS` (all probes must succeed), `ANY_SUCCESS` (at least one probe must succeed), and `MAJORITY_SUCCESS` (majority of probes must succeed). |\n\nThe sections below explain the available strategies in more detail."},{"id":"pingstrategy-default","title":"`PingStrategy` (default)","role":"content","text":"The default strategy, `PingStrategy`, periodically sends a Redis\n[`PING`](https://redis.io/docs/latest/commands/ping) command\nand checks that it gives the expected response. Any unexpected response\nor exception indicates an unhealthy server. Although `PingStrategy` is\nvery simple, it is a good basic approach for most Redis deployments.\n\nAlthough `PingStrategy` is the default, you can still activate it\nexplicitly using the `healthCheckStrategySupplier()` method of the `DatabaseConfig`\nbuilder. Use this approach if you want to configure the default\n`PingStrategy` with custom options, as shown in the example below.\n\n[code example]"},{"id":"lagawarestrategy","title":"`LagAwareStrategy`","role":"content","text":"`LagAwareStrategy` is designed specifically for\nRedis Software [Active-Active](https://redis.io/docs/latest/operate/rs/databases/active-active)\ndeployments. It uses the Redis Software REST API to check database availability\nand can also optionally check replication lag.\n\n`LagAwareStrategy` determines the health of the server using the\n[REST API](https://redis.io/docs/latest/operate/rs/references/rest-api). The example\nbelow shows how to configure and use `LagAwareStrategy`. Note that\n`LagAwareStrategy` requires the following dependencies (although they are\nadded automatically with Lettuce):\n\n[code example]\n\nConfigure `LagAwareStrategy` using its configuration builder and then\npass the configured strategy to the `healthCheckStrategySupplier()` method of the `DatabaseConfig`\nbuilder:\n\n[code example]\n\nThe `LagAwareStrategy.Config` builder has the following options in\naddition to the standard options provided by `HealthCheckStrategy.Config`:\n\n| Method | Default value | Description|\n| --- | --- | --- |\n| `restApiUri()` | (required) | URI of the Redis Software REST API. |\n| `credentials()` | (required) | Credentials for accessing the REST API. |\n| `extendedCheckEnabled()` | `false` | Enable extended lag checking (this includes lag validation in addition to the standard datapath validation). |\n| `availabilityLagTolerance()` | `Duration.ofMillis(100)` | Maximum lag tolerance in milliseconds for extended lag checking. |"},{"id":"custom-health-check-strategy","title":"Custom health check strategy","role":"content","text":"You can supply your own custom health check strategy by\nimplementing the `HealthCheckStrategy` interface. For example, you might\nuse this to integrate with external monitoring tools or to implement\nchecks that are specific to your application. The example below\nshows a simple custom strategy. Pass your custom strategy implementation to the `DatabaseConfig`\nbuilder with the `healthCheckStrategySupplier()` method, as shown in the example below.\n\n[code example]"},{"id":"disable-health-checks","title":"Disable health checks","role":"content","text":"To disable health checks completely for a specific database, pass a value of\n`HealthCheckStrategySupplier.NO_HEALTH_CHECK` to the\n`healthCheckStrategySupplier()` method of the `DatabaseConfig`\nbuilder:\n\n[code example]"},{"id":"managing-databases-at-runtime","title":"Managing databases at runtime","role":"content","text":"Although you will typically configure all databases during the initial connection, you can also modify the configuration at runtime. The example below shows how to add and remove database endpoints.\n\n[code example]"},{"id":"manual-failback","title":"Manual failback","role":"content","text":"By default, the failback mechanism runs health checks on all servers in the\nweighted list and selects the highest-weighted server that is\nhealthy. However, you can also use the `switchTo()` method of\n`MultiDbClient` to select which database to use manually:\n\n[code example]\n\nNote that `switchTo()` is thread-safe.\n\nIf you decide to implement manual failback, you will need a way for external\nsystems to trigger this method in your application. For example, if your application\nexposes a REST API, you might consider creating a REST endpoint to call\n`switchTo()`."},{"id":"pubsub-and-re-subscription","title":"Pub/Sub and re-subscription","role":"content","text":"`MultiDbClient` supports [Pub/Sub](https://redis.io/docs/latest/develop/pubsub)\nmessaging with automatic re-subscription to channels during failover.\nThis means you don't have to detect failovers and re-subscribe manually:\n\n- **Subscriber failover**: When the active database becomes unhealthy, the\n  subscriber automatically reconnects to the next available database and\n  re-subscribes to the same channels.\n- **Publisher failover**: The publisher switches to the next available\n  database and continues publishing to the same channels.\n\nFor reliable Pub/Sub delivery during failover, use `MultiDbClient` instances\nfor *both* publishers and subscribers.\n\nUnlike the standard commands, which use the connection returned by `connect()`,\nPub/Sub uses a dedicated connection that you obtain with the `connectPubSub()`\nmethod. This returns a `StatefulRedisMultiDbPubSubConnection`, which implements\nthe same interface as the standard `StatefulRedisPubSubConnection`. Register a\n`RedisPubSubListener` (or extend `RedisPubSubAdapter` to override only the\ncallbacks you need) to receive messages, then subscribe to one or more channels:\n\n[code example]\n\nUse a separate Pub/Sub connection to publish messages:\n\n[code example]\n\n> [!NOTE]\n> The only addition over a standard Lettuce Pub/Sub connection is that\n> you obtain the connection with `connectPubSub()` on the `MultiDbClient`. The\n> subscription and publishing API is otherwise identical, but the active\n> subscriptions and registered listeners are automatically migrated to the new\n> database when a failover occurs.\n>\n> Message loss can still occur if the failover events happen in the reverse order,\n> with the publisher failing over to the new database before the subscriber.\n> Messages published during this window may not reach a subscriber that is still\n> connected to the previous database."},{"id":"behavior-when-all-endpoints-are-unhealthy","title":"Behavior when all endpoints are unhealthy","role":"content","text":"In the extreme case where no endpoint is healthy, Lettuce will keep trying\nindefinitely to reconnect to a healthy endpoint. The frequency of these attempts\nis configured by the `delayInBetweenFailoverAttempts()` option in the `MultiDbConfig`\nbuilder (see [Client configuration](#client-configuration)) with\na default value of 12 seconds.\n\nNote that you can subscribe to the `AllDatabasesUnhealthyEvent` event to be notified when\nall endpoints become unhealthy (see [Failover events](#failover-events) for more information). Your\nevent handler could include code to close the connection if the indefinite search for\na healthy endpoint is not suitable for your application (see\n[Connection configuration](#connection-configuration) to learn how to close a connection)."},{"id":"troubleshooting","title":"Troubleshooting","role":"errors","text":"This section lists some common problems and their solutions. Note that you can\nuse debug logging for the failover client to get more information when problems occur.\nTo enable this, set the log level to `DEBUG` in your logging configuration:\n\n[code example]"},{"id":"excessive-or-constant-health-check-failures","title":"Excessive or constant health check failures","role":"content","text":"If all health checks fail, you should first rule out authentication\nproblems with the Redis server and also make sure there are no persistent\nnetwork connectivity problems. If you still see frequent or constant\nfailures, try increasing the timeout for health checks and the\ninterval between them:\n\n[code example]"},{"id":"slow-failback-after-recovery","title":"Slow failback after recovery","role":"content","text":"If failback is too slow after a server recovers, you can try\nincreasing the frequency of health checks and reducing the grace\nperiod before failback is attempted (the grace period is the\nminimum time after a failover before Lettuce will check if a\nfailback is possible).\n\n[code example]"}],"examples":[{"id":"connection-configuration-ex0","language":"java","code":"import io.lettuce.core.RedisURI;\nimport io.lettuce.core.failover.DatabaseConfig;\nimport io.lettuce.core.failover.MultiDbClient;\nimport io.lettuce.core.failover.MultiDbOptions;\nimport io.lettuce.core.failover.api.StatefulRedisMultiDbConnection;","section_id":"connection-configuration"},{"id":"connection-configuration-ex1","language":"java","code":"RedisURI eastUri = RedisURI.builder()\n        .withHost(\"redis-east.example.com\")\n        .withPort(6379)\n        .withPassword(\"secret\".toCharArray())\n        .build();\n\nDatabaseConfig east = DatabaseConfig.builder(eastUri)\n        .weight(1.0f)\n        .build();\n\nRedisURI westUri = RedisURI.builder()\n        .withHost(\"redis-west.example.com\")\n        .withPort(6379)\n        .withPassword(\"secret\".toCharArray())\n        .build();\n\nDatabaseConfig west = DatabaseConfig.builder(westUri)\n        .weight(0.5f)\n        .build();\n\nList<DatabaseConfig> databases = Arrays.asList(east, west);","section_id":"connection-configuration"},{"id":"connection-configuration-ex2","language":"java","code":"MultiDbClient client = MultiDbClient.create(databases);\n\n// Connect and use like a regular Redis connection\nStatefulRedisMultiDbConnection<String, String> connection = client.connect();\n\n// Execute commands asynchronously - they go to the highest-weighted\n// healthy database\nconnection.async().set(\"key\", \"value\");\nString value = connection.async().get(\"key\").get();\n\n// Clean up\nconnection.close();\nclient.shutdown();","section_id":"connection-configuration"},{"id":"client-configuration-ex0","language":"java","code":"MultiDbOptions options = MultiDbOptions.builder()\n        .failbackSupported(true)\n        .failbackCheckInterval(Duration.ofSeconds(30))\n        .gracePeriod(Duration.ofSeconds(10))\n        .delayInBetweenFailoverAttempts(Duration.ofSeconds(5))\n        .initializationPolicy(InitializationPolicy.ALL_AVAILABLE)\n        .build();\n\nMultiDbClient client = MultiDbClient.create(Arrays.asList(db1, db2), options);","section_id":"client-configuration"},{"id":"circuit-breaker-configuration-ex0","language":"java","code":"import io.lettuce.core.failover.api.CircuitBreakerConfig;\n\nCircuitBreakerConfig cbConfig = CircuitBreakerConfig.builder()\n        .failureRateThreshold(50.0f)\n        .minimumNumberOfFailures(100)\n        .metricsWindowSize(5)\n        .build();\n\nDatabaseConfig db = DatabaseConfig.builder(redisUri)\n        .circuitBreakerConfig(cbConfig)\n        .build();","section_id":"circuit-breaker-configuration"},{"id":"failover-events-ex0","language":"java","code":"import io.lettuce.core.failover.event.DatabaseSwitchEvent;\nimport io.lettuce.core.failover.event.SwitchReason;\n\nclient.getResources().eventBus().get()\n        .filter(event -> event instanceof DatabaseSwitchEvent)\n        .cast(DatabaseSwitchEvent.class)\n        .subscribe(event -> log.info(\"Switch: {} -> {} ({})\",\n                event.getFromDb(), event.getToDb(), event.getReason()));","section_id":"failover-events"},{"id":"failover-events-ex1","language":"java","code":"import io.lettuce.core.failover.event.AllDatabasesUnhealthyEvent;\n\nclient.getResources().eventBus().get()\n        .filter(event -> event instanceof AllDatabasesUnhealthyEvent)\n        .cast(AllDatabasesUnhealthyEvent.class)\n        .subscribe(event -> log.warn(\"All databases unhealthy! Attempts: {}, DBs: {}\",\n                event.getFailedAttempts(), event.getUnhealthyDatabases()));","section_id":"failover-events"},{"id":"pingstrategy-default-ex0","language":"java","code":"import io.lettuce.core.failover.health.PingStrategy;\nimport io.lettuce.core.failover.health.HealthCheckStrategy;\n\nHealthCheckStrategy.Config healthConfig = HealthCheckStrategy.Config.builder()\n        .interval(5000)                 // Check every 5 seconds\n        .timeout(1000)                  // 1 second timeout\n        .numProbes(3)                   // 3 probes per check\n        .delayInBetweenProbes(500)      // 500ms between probes\n        .build();\n\nDatabaseConfig db = DatabaseConfig.builder(redisUri)\n        .healthCheckStrategySupplier((uri, factory) ->\n                new PingStrategy(factory, healthConfig))\n        .build();","section_id":"pingstrategy-default"},{"id":"lagawarestrategy-ex0","language":"xml","code":"<dependency>\n    <groupId>io.netty</groupId>\n    <artifactId>netty-codec-http</artifactId>\n</dependency>\n<dependency>\n    <groupId>com.fasterxml.jackson.core</groupId>\n    <artifactId>jackson-databind</artifactId>\n</dependency>","section_id":"lagawarestrategy"},{"id":"lagawarestrategy-ex1","language":"java","code":"import io.lettuce.core.failover.health.LagAwareStrategy;\n\nLagAwareStrategy.Config lagConfig = LagAwareStrategy.Config.builder()\n        .restApiUri(URI.create(\"https://cluster.redis.local:9443\"))\n        .credentials(() -> RedisCredentials.just(\"admin\", \"password\"))\n        .extendedCheckEnabled(true)  // Enable lag-aware checks\n        .availabilityLagTolerance(Duration.ofMillis(100))\n        .build();\n\nDatabaseConfig db = DatabaseConfig.builder(redisUri)\n        .healthCheckStrategySupplier((uri, factory) -> new LagAwareStrategy(lagConfig))\n        .build();","section_id":"lagawarestrategy"},{"id":"custom-health-check-strategy-ex0","language":"java","code":"// Custom strategy supplier\npublic class CustomHealthCheck extends AbstractHealthCheckStrategy {\n\n    public CustomHealthCheck(HealthCheckStrategy.Config config) {\n        super(config);\n    }\n\n    @Override\n    public HealthStatus doHealthCheck(RedisURI endpoint) {\n        // Return HealthStatus.HEALTHY, UNHEALTHY, or UNKNOWN\n        return checkExternalMonitoringSystem(endpoint)\n                ? HealthStatus.HEALTHY : HealthStatus.UNHEALTHY;\n    }\n}\n\n// Use with healthCheckStrategySupplier()\nDatabaseConfig db = DatabaseConfig.builder(redisUri)\n        .healthCheckStrategySupplier((uri, factory) ->\n                new CustomHealthCheck(HealthCheckStrategy.Config.create()))\n        .build();","section_id":"custom-health-check-strategy"},{"id":"disable-health-checks-ex0","language":"java","code":"import io.lettuce.core.failover.health.HealthCheckStrategySupplier;\n\nDatabaseConfig db = DatabaseConfig.builder(redisUri)\n        .healthCheckStrategySupplier(HealthCheckStrategySupplier.NO_HEALTH_CHECK)\n        .build();","section_id":"disable-health-checks"},{"id":"managing-databases-at-runtime-ex0","language":"java","code":"StatefulRedisMultiDbConnection<String, String> connection = client.connect();\n\n// Add a new database\nRedisURI newDb = RedisURI.create(\"redis://new-server:6379\");\nDatabaseConfig newConfig = DatabaseConfig.builder(newDb)\n        .weight(0.8f)\n        .build();\nconnection.addDatabase(newConfig);\n\n// Remove a database\nconnection.removeDatabase(existingUri);","section_id":"managing-databases-at-runtime"},{"id":"manual-failback-ex0","language":"java","code":"StatefulRedisMultiDbConnection<String, String> connection = client.connect();\n\n// Get current endpoint\nRedisURI current = connection.getCurrentEndpoint();\n\n// Get all available endpoints\nCollection<RedisURI> endpoints = connection.getEndpoints();\n\n// Check if a specific endpoint is healthy\nboolean healthy = connection.isHealthy(targetUri);\n\n// Force switch to a specific endpoint\nconnection.switchTo(targetUri);","section_id":"manual-failback"},{"id":"pubsub-and-re-subscription-ex0","language":"java","code":"import io.lettuce.core.failover.api.StatefulRedisMultiDbPubSubConnection;\nimport io.lettuce.core.pubsub.RedisPubSubAdapter;\n\nStatefulRedisMultiDbPubSubConnection<String, String> pubSubConnection =\n        client.connectPubSub();\n\n// Register a listener to handle incoming messages.\npubSubConnection.addListener(new RedisPubSubAdapter<String, String>() {\n    @Override\n    public void message(String channel, String message) {\n        System.out.printf(\"Received \\\"%s\\\" on channel \\\"%s\\\"%n\", message, channel);\n    }\n});\n\n// Subscribe to one or more channels. If a failover happens, the\n// subscriptions are automatically re-established on the new active database.\npubSubConnection.sync().subscribe(\"news\", \"alerts\");","section_id":"pubsub-and-re-subscription"},{"id":"pubsub-and-re-subscription-ex1","language":"java","code":"StatefulRedisMultiDbPubSubConnection<String, String> publisher =\n        client.connectPubSub();\n\npublisher.sync().publish(\"news\", \"Hello World\");","section_id":"pubsub-and-re-subscription"},{"id":"troubleshooting-ex0","language":"plaintext","code":"logging.level.io.lettuce.core.failover=DEBUG","section_id":"troubleshooting"},{"id":"excessive-or-constant-health-check-failures-ex0","language":"java","code":"HealthCheckStrategy.Config config = HealthCheckStrategy.Config.builder()\n    .interval(5000)                 // Less frequent checks\n    .timeout(2000)                  // More generous timeout\n    .build();","section_id":"excessive-or-constant-health-check-failures"},{"id":"slow-failback-after-recovery-ex0","language":"java","code":"// Faster recovery configuration\nHealthCheckStrategy.Config config = HealthCheckStrategy.Config.builder()\n    .interval(1000)                    // More frequent checks\n    .build();\n\n// Adjust failback timing\nMultiDbOptions multiConfig = MultiDbOptions.builder()\n        .gracePeriod(5000)                 // Shorter grace period\n        .build();","section_id":"slow-failback-after-recovery"}]}
{"schema_version":2,"id":"develop/clients/lettuce","title":"Lettuce guide (Java)","url":"https://redis.io/docs/latest/develop/clients/lettuce/","summary":"Connect your Lettuce application to a Redis database","aliases":["/develop/connect/clients/java/lettuce","/connect/clients/lettuce/","/clients/lettuce/","/develop/clients/java/lettuce/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-09T09:47:45+01:00","children":[{"id":"develop/clients/lettuce/connect","summary":"Connect your Java application to a Redis database","title":"Connect to the server","url":"https://redis.io/docs/latest/develop/clients/lettuce/connect/"},{"id":"develop/clients/lettuce/amr","summary":"Learn how to authenticate to an Azure Managed Redis (AMR) database","title":"Connect to Azure Managed Redis","url":"https://redis.io/docs/latest/develop/clients/lettuce/amr/"},{"id":"develop/clients/lettuce/queryjson","summary":"Learn how to use Redis Search with JSON and hash documents.","title":"Index and query documents","url":"https://redis.io/docs/latest/develop/clients/lettuce/queryjson/"},{"id":"develop/clients/lettuce/vecsearch","summary":"Learn how to index and query vector embeddings with Redis","title":"Index and query vectors","url":"https://redis.io/docs/latest/develop/clients/lettuce/vecsearch/"},{"id":"develop/clients/lettuce/transpipe","summary":"Learn how to use Redis pipelines and transactions","title":"Pipelines and transactions","url":"https://redis.io/docs/latest/develop/clients/lettuce/transpipe/"},{"id":"develop/clients/lettuce/vecsets","summary":"Index and query embeddings with Redis vector sets","title":"Vector set embeddings","url":"https://redis.io/docs/latest/develop/clients/lettuce/vecsets/"},{"id":"develop/clients/lettuce/prob","summary":"Learn how to use approximate calculations with Redis.","title":"Probabilistic data types","url":"https://redis.io/docs/latest/develop/clients/lettuce/prob/"},{"id":"develop/clients/lettuce/failover","summary":"Improve reliability using the failover features of Lettuce.","title":"Client-side geographic failover","url":"https://redis.io/docs/latest/develop/clients/lettuce/failover/"},{"id":"develop/clients/lettuce/produsage","summary":"Get your Lettuce app ready for production","title":"Production usage","url":"https://redis.io/docs/latest/develop/clients/lettuce/produsage/"},{"id":"develop/clients/lettuce/error-handling","summary":"Learn how to handle errors when using Lettuce.","title":"Error handling","url":"https://redis.io/docs/latest/develop/clients/lettuce/error-handling/"}],"page_type":"content","content_hash":"1064c2efbb305d04a9e0733103061e38384d9e4d3e1b0f5eb4909cd84a4692a4","sections":[{"id":"install","title":"Install","role":"setup","text":"To include Lettuce as a dependency in your application, edit the appropriate dependency file as shown below.\n\nIf you use Maven, add the following dependency to your `pom.xml`:\n\n[code example]\n\nIf you use Gradle, include this line in your `build.gradle` file:\n\n[code example]\n\nIf you wish to use the JAR files directly, download the latest Lettuce and, optionally, Apache Commons Pool2 JAR files from Maven Central or any other Maven repository.\n\nTo build from source, see the instructions on the [Lettuce source code GitHub repo](https://github.com/lettuce-io/lettuce-core)."},{"id":"connect-and-test","title":"Connect and test","role":"content","text":"Connect to a local server using the following code. First, import\nthe required classes.\n\nFoundational: Import required Lettuce classes for synchronous Redis connections\n\n**Difficulty:** Beginner\n\n**Available in:** Java\n\n##### Java\n\n[code example]\n\n\n\nUse the following code to connect to the server.\n\nFoundational: Establish a synchronous connection to a Redis server using Lettuce\n\n**Difficulty:** Beginner\n\n**Available in:** Java\n\n##### Java\n\n[code example]\n\n\n\nTest the connection by storing and retrieving a simple string.\n\nFoundational: Set and retrieve string values using SET and GET commands to verify the connection works\n\n**Difficulty:** Beginner\n\n**Available in:** Java\n\n##### Java\n\n[code example]\n\n\n\nClose the connection when you're done.\n\nFoundational: Properly close the Lettuce connection to release resources\n\n**Difficulty:** Beginner\n\n**Available in:** Java\n\n##### Java\n\n[code example]"},{"id":"more-information","title":"More information","role":"content","text":"The [Lettuce reference guide](https://redis.github.io/lettuce/) has more examples\nand an API reference. You may also be interested in the\n[Project Reactor](https://projectreactor.io/) library that Lettuce uses.\n\nSee also the other pages in this section for more information and examples:"}],"examples":[{"id":"install-ex0","language":"xml","code":"<dependency>\n    <groupId>io.lettuce</groupId>\n    <artifactId>lettuce-core</artifactId>\n    <version>6.7.1.RELEASE</version> <!-- Check for the latest version on Maven Central -->\n</dependency>","section_id":"install"},{"id":"install-ex1","language":"plaintext","code":"dependencies {\n    compileOnly 'io.lettuce:lettuce-core:6.7.1.RELEASE'\n}","section_id":"install"},{"id":"connect-and-test-ex0","language":"java","code":"import io.lettuce.core.*;\nimport io.lettuce.core.api.StatefulRedisConnection;\nimport io.lettuce.core.api.sync.RedisCommands;","section_id":"connect-and-test"},{"id":"connect-and-test-ex1","language":"java","code":"RedisURI uri = RedisURI.Builder\n                .redis(\"localhost\", 6379)\n                .build();\n\n        RedisClient client = RedisClient.create(uri);\n        StatefulRedisConnection<String, String> connection = client.connect();\n        RedisCommands<String, String> commands = connection.sync();","section_id":"connect-and-test"},{"id":"connect-and-test-ex2","language":"java","code":"commands.set(\"foo\", \"bar\");\n        String result = commands.get(\"foo\");\n        System.out.println(result); // >>> bar","section_id":"connect-and-test"},{"id":"connect-and-test-ex3","language":"java","code":"connection.close();\n        client.shutdown();","section_id":"connect-and-test"}]}
{"schema_version":2,"id":"develop/clients/lettuce/prob","title":"Probabilistic data types","url":"https://redis.io/docs/latest/develop/clients/lettuce/prob/","summary":"Learn how to use approximate calculations with Redis.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-10T14:04:50+01:00","page_type":"content","content_hash":"6832ae6eb54eb65dd7cb2d5e702753fa45ed64a2ea09671dfbf5db30b340c59c","sections":[{"id":"set-operations","title":"Set operations","role":"content","text":"Redis supports the following approximate set operations:\n\n-   [Membership](#set-membership): The\n    [Bloom filter](https://redis.io/docs/latest/develop/data-types/probabilistic/bloom-filter) and\n    [Cuckoo filter](https://redis.io/docs/latest/develop/data-types/probabilistic/cuckoo-filter)\n    data types let you track whether or not a given item is a member of a set.\n-   [Cardinality](#set-cardinality): The\n    [HyperLogLog](https://redis.io/docs/latest/develop/data-types/probabilistic/hyperloglogs)\n    data type gives you an approximate value for the number of items in a set, also\n    known as the *cardinality* of the set.\n\nThe sections below describe these operations in more detail."},{"id":"set-membership","title":"Set membership","role":"content","text":"[Bloom filter](https://redis.io/docs/latest/develop/data-types/probabilistic/bloom-filter) and\n[Cuckoo filter](https://redis.io/docs/latest/develop/data-types/probabilistic/cuckoo-filter)\nobjects provide a set membership operation that lets you track whether or not a\nparticular item has been added to a set. These two types provide different\ntrade-offs for memory usage and speed, so you can select the best one for your\nuse case. Note that for both types, there is an asymmetry between presence and\nabsence of items in the set. If an item is reported as absent, then it is definitely\nabsent, but if it is reported as present, then there is a small chance it may really be\nabsent.\n\nInstead of storing strings directly, like a [set](https://redis.io/docs/latest/develop/data-types/sets),\na Bloom filter records the presence or absence of the\n[hash value](https://en.wikipedia.org/wiki/Hash_function) of a string.\nThis gives a very compact representation of the\nset's membership with a fixed memory size, regardless of how many items you\nadd. The following example adds some names to a Bloom filter representing\na list of users and checks for the presence or absence of users in the list.\n\nFoundational: Use Bloom filters for memory-efficient set membership testing with false positive possibility\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n\n\nA Cuckoo filter has similar features to a Bloom filter, but also supports\na deletion operation to remove hashes from a set, as shown in the example\nbelow.\n\nFoundational: Use Cuckoo filters for set membership testing with deletion support and faster lookups than Bloom filters\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n\n\nWhich of these two data types you choose depends on your use case.\nBloom filters are generally faster than Cuckoo filters when adding new items,\nand also have better memory usage. Cuckoo filters are generally faster\nat checking membership and also support the delete operation. See the\n[Bloom filter](https://redis.io/docs/latest/develop/data-types/probabilistic/bloom-filter) and\n[Cuckoo filter](https://redis.io/docs/latest/develop/data-types/probabilistic/cuckoo-filter)\nreference pages for more information and comparison between the two types."},{"id":"set-cardinality","title":"Set cardinality","role":"content","text":"A [HyperLogLog](https://redis.io/docs/latest/develop/data-types/probabilistic/hyperloglogs)\nobject calculates the cardinality of a set. As you add\nitems, the HyperLogLog tracks the number of distinct set members but\ndoesn't let you retrieve them or query which items have been added.\nYou can also merge two or more HyperLogLogs to find the cardinality of the\n[union](https://en.wikipedia.org/wiki/Union_(set_theory)) of the sets they\nrepresent.\n\nFoundational: Estimate set cardinality with HyperLogLog for memory-efficient counting of distinct items\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n\n\nThe main benefit that HyperLogLogs offer is their very low\nmemory usage. They can count up to 2^64 items with less than\n1% standard error using a maximum 12KB of memory. This makes\nthem very useful for counting things like the total of distinct\nIP addresses that access a website or the total of distinct\nbank card numbers that make purchases within a day."},{"id":"statistics","title":"Statistics","role":"content","text":"Redis supports several approximate statistical calculations\non numeric data sets:\n\n-   [Frequency](#frequency): The\n    [Count-min sketch](https://redis.io/docs/latest/develop/data-types/probabilistic/count-min-sketch)\n    data type lets you find the approximate frequency of a labeled item in a data stream.\n-   [Quantiles](#quantiles): The\n    [t-digest](https://redis.io/docs/latest/develop/data-types/probabilistic/t-digest)\n    data type estimates the quantile of a query value in a data stream.\n-   [Ranking](#ranking): The\n    [Top-K](https://redis.io/docs/latest/develop/data-types/probabilistic/top-k) data type\n    estimates the ranking of labeled items by frequency in a data stream.\n\nThe sections below describe these operations in more detail."},{"id":"frequency","title":"Frequency","role":"content","text":"A [Count-min sketch](https://redis.io/docs/latest/develop/data-types/probabilistic/count-min-sketch)\n(CMS) object keeps count of a set of related items represented by\nstring labels. The count is approximate, but you can specify\nhow close you want to keep the count to the true value (as a fraction)\nand the acceptable probability of failing to keep it in this\ndesired range. For example, you can request that the count should\nstay within 0.1% of the true value and have a 0.05% probability\nof going outside this limit. The example below shows how to create\na Count-min sketch object, add data to it, and then query it.\n\nFoundational: Track approximate item frequencies with Count-min sketch for memory-efficient statistics on data streams\n\n**Difficulty:** Intermediate\n\n**Available in:** Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n\n\nThe advantage of using a CMS over keeping an exact count with a\n[sorted set](https://redis.io/docs/latest/develop/data-types/sorted-sets)\nis that that a CMS has very low and fixed memory usage, even for\nlarge numbers of items. Use CMS objects to keep daily counts of\nitems sold, accesses to individual web pages on your site, and\nother similar statistics."},{"id":"quantiles","title":"Quantiles","role":"content","text":"A [quantile](https://en.wikipedia.org/wiki/Quantile) is the value\nbelow which a certain fraction of samples lie. For example, with\na set of measurements of people's heights, the quantile of 0.75 is\nthe value of height below which 75% of all people's heights lie.\n[Percentiles](https://en.wikipedia.org/wiki/Percentile) are equivalent\nto quantiles, except that the fraction is expressed as a percentage.\n\nA [t-digest](https://redis.io/docs/latest/develop/data-types/probabilistic/t-digest)\nobject can estimate quantiles from a set of values added to it\nwithout having to store each value in the set explicitly. This can\nsave a lot of memory when you have a large number of samples.\n\nThe example below shows how to add data samples to a t-digest\nobject and obtain some basic statistics, such as the minimum and\nmaximum values, the quantile of 0.75, and the\n[cumulative distribution function](https://en.wikipedia.org/wiki/Cumulative_distribution_function)\n(CDF), which is effectively the inverse of the quantile function. It also\nshows how to merge two or more t-digest objects to query the combined\ndata set.\n\nFoundational: Estimate quantiles and percentiles with t-digest for memory-efficient statistical analysis of large datasets\n\n**Difficulty:** Intermediate\n\n**Available in:** Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n\n\nA t-digest object also supports several other related commands, such\nas querying by rank. See the\n[t-digest](https://redis.io/docs/latest/develop/data-types/probabilistic/t-digest)\nreference for more information."},{"id":"ranking","title":"Ranking","role":"content","text":"A [Top-K](https://redis.io/docs/latest/develop/data-types/probabilistic/top-k)\nobject estimates the rankings of different labeled items in a data\nstream according to frequency. For example, you could use this to\ntrack the top ten most frequently-accessed pages on a website, or the\ntop five most popular items sold.\n\nThe example below adds several different items to a Top-K object\nthat tracks the top three items (this is the second parameter to\nthe `topKReserve()` method). It also shows how to list the\ntop *k* items and query whether or not a given item is in the\nlist.\n\nFoundational: Track top K most frequent items in a data stream with Top-K for efficient ranking without storing all items\n\n**Difficulty:** Intermediate\n\n**Available in:** Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]"}],"examples":[{"id":"set-membership-ex0","language":"java","code":"CompletableFuture<Void> bloomExample = asyncCommands\n                    .bfMAdd(\"recorded_users\", \"andy\", \"cameron\", \"david\", \"michelle\")\n                    .thenCompose(res1 -> {\n                        System.out.println(res1);\n                        // >>> [true, true, true, true]\n                        return asyncCommands.bfExists(\"recorded_users\", \"cameron\");\n                    })\n                    .thenCompose(res2 -> {\n                        System.out.println(res2);\n                        // >>> true\n                        return asyncCommands.bfExists(\"recorded_users\", \"kaitlyn\");\n                    })\n                    .thenAccept(res3 -> {\n                        System.out.println(res3);\n                        // >>> false\n                    })\n                    .toCompletableFuture();\n\n            bloomExample.join();","section_id":"set-membership"},{"id":"set-membership-ex1","language":"java","code":"List<Boolean> res1 = reactiveCommands\n                    .bfMAdd(\"recorded_users\", \"andy\", \"cameron\", \"david\", \"michelle\")\n                    .map(Value::getValue)\n                    .collectList()\n                    .block();\n            System.out.println(res1);\n            // >>> [true, true, true, true]\n\n            Boolean res2 = reactiveCommands.bfExists(\"recorded_users\", \"cameron\").block();\n            System.out.println(res2);\n            // >>> true\n\n            Boolean res3 = reactiveCommands.bfExists(\"recorded_users\", \"kaitlyn\").block();\n            System.out.println(res3);\n            // >>> false","section_id":"set-membership"},{"id":"set-membership-ex2","language":"java","code":"CompletableFuture<Void> cuckooExample = asyncCommands\n                    .cfAdd(\"other_users\", \"paolo\")\n                    .thenCompose(res4 -> {\n                        System.out.println(res4);\n                        // >>> true\n                        return asyncCommands.cfAdd(\"other_users\", \"kaitlyn\");\n                    })\n                    .thenCompose(res5 -> {\n                        System.out.println(res5);\n                        // >>> true\n                        return asyncCommands.cfAdd(\"other_users\", \"rachel\");\n                    })\n                    .thenCompose(res6 -> {\n                        System.out.println(res6);\n                        // >>> true\n                        return asyncCommands.cfMExists(\"other_users\", \"paolo\", \"rachel\", \"andy\");\n                    })\n                    .thenCompose(res7 -> {\n                        System.out.println(res7);\n                        // >>> [true, true, false]\n                        return asyncCommands.cfDel(\"other_users\", \"paolo\");\n                    })\n                    .thenCompose(res8 -> {\n                        System.out.println(res8);\n                        // >>> true\n                        return asyncCommands.cfExists(\"other_users\", \"paolo\");\n                    })\n                    .thenAccept(res9 -> {\n                        System.out.println(res9);\n                        // >>> false\n                    })\n                    .toCompletableFuture();\n\n            cuckooExample.join();","section_id":"set-membership"},{"id":"set-membership-ex3","language":"java","code":"Boolean res4 = reactiveCommands.cfAdd(\"other_users\", \"paolo\").block();\n            System.out.println(res4);\n            // >>> true\n\n            Boolean res5 = reactiveCommands.cfAdd(\"other_users\", \"kaitlyn\").block();\n            System.out.println(res5);\n            // >>> true\n\n            Boolean res6 = reactiveCommands.cfAdd(\"other_users\", \"rachel\").block();\n            System.out.println(res6);\n            // >>> true\n\n            List<Boolean> res7 = reactiveCommands\n                    .cfMExists(\"other_users\", \"paolo\", \"rachel\", \"andy\")\n                    .collectList()\n                    .block();\n            System.out.println(res7);\n            // >>> [true, true, false]\n\n            Boolean res8 = reactiveCommands.cfDel(\"other_users\", \"paolo\").block();\n            System.out.println(res8);\n            // >>> true\n\n            Boolean res9 = reactiveCommands.cfExists(\"other_users\", \"paolo\").block();\n            System.out.println(res9);\n            // >>> false","section_id":"set-membership"},{"id":"set-cardinality-ex0","language":"java","code":"CompletableFuture<Void> hllExample = asyncCommands\n                    .pfadd(\"group:1\", \"andy\", \"cameron\", \"david\")\n                    .thenCompose(res10 -> {\n                        System.out.println(res10);\n                        // >>> 1\n                        return asyncCommands.pfcount(\"group:1\");\n                    })\n                    .thenCompose(res11 -> {\n                        System.out.println(res11);\n                        // >>> 3\n                        return asyncCommands.pfadd(\"group:2\", \"kaitlyn\", \"michelle\", \"paolo\", \"rachel\");\n                    })\n                    .thenCompose(res12 -> {\n                        System.out.println(res12);\n                        // >>> 1\n                        return asyncCommands.pfcount(\"group:2\");\n                    })\n                    .thenCompose(res13 -> {\n                        System.out.println(res13);\n                        // >>> 4\n                        return asyncCommands.pfmerge(\"both_groups\", \"group:1\", \"group:2\");\n                    })\n                    .thenCompose(res14 -> {\n                        System.out.println(res14);\n                        // >>> OK\n                        return asyncCommands.pfcount(\"both_groups\");\n                    })\n                    .thenAccept(res15 -> {\n                        System.out.println(res15);\n                        // >>> 7\n                    })\n                    .toCompletableFuture();\n\n            hllExample.join();","section_id":"set-cardinality"},{"id":"set-cardinality-ex1","language":"java","code":"Long res10 = reactiveCommands.pfadd(\"group:1\", \"andy\", \"cameron\", \"david\").block();\n            System.out.println(res10);\n            // >>> 1\n\n            Long res11 = reactiveCommands.pfcount(\"group:1\").block();\n            System.out.println(res11);\n            // >>> 3\n\n            Long res12 = reactiveCommands.pfadd(\"group:2\", \"kaitlyn\", \"michelle\", \"paolo\", \"rachel\").block();\n            System.out.println(res12);\n            // >>> 1\n\n            Long res13 = reactiveCommands.pfcount(\"group:2\").block();\n            System.out.println(res13);\n            // >>> 4\n\n            String res14 = reactiveCommands.pfmerge(\"both_groups\", \"group:1\", \"group:2\").block();\n            System.out.println(res14);\n            // >>> OK\n\n            Long res15 = reactiveCommands.pfcount(\"both_groups\").block();\n            System.out.println(res15);\n            // >>> 7","section_id":"set-cardinality"},{"id":"frequency-ex0","language":"java","code":"CompletableFuture<Void> cmsExample = asyncCommands\n                    // Specify that you want to keep the counts within 0.01\n                    // (1%) of the true value with a 0.005 (0.5%) chance\n                    // of going outside this limit.\n                    .cmsInitByProb(\"items_sold\", 0.01, 0.005)\n                    .thenCompose(res16 -> {\n                        System.out.println(res16);\n                        // >>> OK\n                        return asyncCommands.cmsIncrBy(\"items_sold\",\n                                IncrementPair.of(\"bread\", 300L),\n                                IncrementPair.of(\"tea\", 200L),\n                                IncrementPair.of(\"coffee\", 200L),\n                                IncrementPair.of(\"beer\", 100L));\n                    })\n                    .thenCompose(res17 -> {\n                        List<Long> sorted17 = new ArrayList<>(res17);\n                        sorted17.sort(null);\n                        System.out.println(sorted17);\n                        // >>> [100, 200, 200, 300]\n                        return asyncCommands.cmsIncrBy(\"items_sold\",\n                                IncrementPair.of(\"bread\", 100L),\n                                IncrementPair.of(\"coffee\", 150L));\n                    })\n                    .thenCompose(res18 -> {\n                        List<Long> sorted18 = new ArrayList<>(res18);\n                        sorted18.sort(null);\n                        System.out.println(sorted18);\n                        // >>> [350, 400]\n                        return asyncCommands.cmsQuery(\"items_sold\", \"bread\", \"tea\", \"coffee\", \"beer\");\n                    })\n                    .thenAccept(res19 -> {\n                        List<Long> sorted19 = new ArrayList<>(res19);\n                        sorted19.sort(null);\n                        System.out.println(sorted19);\n                        // >>> [100, 200, 350, 400]\n                    })\n                    .toCompletableFuture();\n\n            cmsExample.join();","section_id":"frequency"},{"id":"frequency-ex1","language":"java","code":"// Specify that you want to keep the counts within 0.01\n            // (1%) of the true value with a 0.005 (0.5%) chance\n            // of going outside this limit.\n            String res16 = reactiveCommands.cmsInitByProb(\"items_sold\", 0.01, 0.005).block();\n            System.out.println(res16);\n            // >>> OK\n\n            List<Long> res17 = new ArrayList<>(reactiveCommands\n                    .cmsIncrBy(\"items_sold\",\n                            IncrementPair.of(\"bread\", 300L),\n                            IncrementPair.of(\"tea\", 200L),\n                            IncrementPair.of(\"coffee\", 200L),\n                            IncrementPair.of(\"beer\", 100L))\n                    .collectList()\n                    .block());\n            res17.sort(null);\n            System.out.println(res17);\n            // >>> [100, 200, 200, 300]\n\n            List<Long> res18 = new ArrayList<>(reactiveCommands\n                    .cmsIncrBy(\"items_sold\",\n                            IncrementPair.of(\"bread\", 100L),\n                            IncrementPair.of(\"coffee\", 150L))\n                    .collectList()\n                    .block());\n            res18.sort(null);\n            System.out.println(res18);\n            // >>> [350, 400]\n\n            List<Long> res19 = new ArrayList<>(reactiveCommands\n                    .cmsQuery(\"items_sold\", \"bread\", \"tea\", \"coffee\", \"beer\")\n                    .collectList()\n                    .block());\n            res19.sort(null);\n            System.out.println(res19);\n            // >>> [100, 200, 350, 400]","section_id":"frequency"},{"id":"quantiles-ex0","language":"java","code":"CompletableFuture<Void> tdigestExample = asyncCommands\n                    .tdigestCreate(\"male_heights\")\n                    .thenCompose(res20 -> {\n                        System.out.println(res20);\n                        // >>> OK\n                        return asyncCommands.tdigestAdd(\"male_heights\",\n                                175.5, 181, 160.8, 152, 177, 196, 164);\n                    })\n                    .thenCompose(res21 -> {\n                        System.out.println(res21);\n                        // >>> OK\n                        return asyncCommands.tdigestMin(\"male_heights\");\n                    })\n                    .thenCompose(res22 -> {\n                        System.out.println(res22);\n                        // >>> 152.0\n                        return asyncCommands.tdigestMax(\"male_heights\");\n                    })\n                    .thenCompose(res23 -> {\n                        System.out.println(res23);\n                        // >>> 196.0\n                        return asyncCommands.tdigestQuantile(\"male_heights\", 0.75);\n                    })\n                    .thenCompose(res24 -> {\n                        System.out.println(res24);\n                        // >>> [181.0]\n                        // Note that the CDF value for 181 is not exactly 0.75.\n                        // Both values are estimates.\n                        return asyncCommands.tdigestCDF(\"male_heights\", 181);\n                    })\n                    .thenCompose(res25 -> {\n                        System.out.println(res25);\n                        // >>> [0.7857142857142857]\n                        return asyncCommands.tdigestCreate(\"female_heights\");\n                    })\n                    .thenCompose(res26 -> {\n                        System.out.println(res26);\n                        // >>> OK\n                        return asyncCommands.tdigestAdd(\"female_heights\",\n                                155.5, 161, 168.5, 170, 157.5, 163, 171);\n                    })\n                    .thenCompose(res27 -> {\n                        System.out.println(res27);\n                        // >>> OK\n                        return asyncCommands.tdigestQuantile(\"female_heights\", 0.75);\n                    })\n                    .thenCompose(res28 -> {\n                        System.out.println(res28);\n                        // >>> [170.0]\n                        return asyncCommands.tdigestMerge(\"all_heights\", \"male_heights\", \"female_heights\");\n                    })\n                    .thenCompose(res29 -> {\n                        System.out.println(res29);\n                        // >>> OK\n                        return asyncCommands.tdigestQuantile(\"all_heights\", 0.75);\n                    })\n                    .thenAccept(res30 -> {\n                        System.out.println(res30);\n                        // >>> [175.5]\n                    })\n                    .toCompletableFuture();\n\n            tdigestExample.join();","section_id":"quantiles"},{"id":"quantiles-ex1","language":"java","code":"String res20 = reactiveCommands.tdigestCreate(\"male_heights\").block();\n            System.out.println(res20);\n            // >>> OK\n\n            String res21 = reactiveCommands.tdigestAdd(\"male_heights\",\n                    175.5, 181, 160.8, 152, 177, 196, 164).block();\n            System.out.println(res21);\n            // >>> OK\n\n            Double res22 = reactiveCommands.tdigestMin(\"male_heights\").block();\n            System.out.println(res22);\n            // >>> 152.0\n\n            Double res23 = reactiveCommands.tdigestMax(\"male_heights\").block();\n            System.out.println(res23);\n            // >>> 196.0\n\n            List<Double> res24 = reactiveCommands.tdigestQuantile(\"male_heights\", 0.75)\n                    .collectList().block();\n            System.out.println(res24);\n            // >>> [181.0]\n\n            // Note that the CDF value for 181 is not exactly 0.75.\n            // Both values are estimates.\n            List<Double> res25 = reactiveCommands.tdigestCDF(\"male_heights\", 181)\n                    .collectList().block();\n            System.out.println(res25);\n            // >>> [0.7857142857142857]\n\n            String res26 = reactiveCommands.tdigestCreate(\"female_heights\").block();\n            System.out.println(res26);\n            // >>> OK\n\n            String res27 = reactiveCommands.tdigestAdd(\"female_heights\",\n                    155.5, 161, 168.5, 170, 157.5, 163, 171).block();\n            System.out.println(res27);\n            // >>> OK\n\n            List<Double> res28 = reactiveCommands.tdigestQuantile(\"female_heights\", 0.75)\n                    .collectList().block();\n            System.out.println(res28);\n            // >>> [170.0]\n\n            String res29 = reactiveCommands.tdigestMerge(\"all_heights\", \"male_heights\", \"female_heights\").block();\n            System.out.println(res29);\n            // >>> OK\n\n            List<Double> res30 = reactiveCommands.tdigestQuantile(\"all_heights\", 0.75)\n                    .collectList().block();\n            System.out.println(res30);\n            // >>> [175.5]","section_id":"quantiles"},{"id":"ranking-ex0","language":"java","code":"CompletableFuture<Void> topkExample = asyncCommands\n                    .topKReserve(\"top_3_songs\", 3L,\n                            TopKReserveArgs.Builder.width(2000L).depth(7L).decay(0.925))\n                    .thenCompose(res31 -> {\n                        System.out.println(res31);\n                        // >>> OK\n                        return asyncCommands.topKIncrBy(\"top_3_songs\",\n                                IncrementPair.of(\"Starfish Trooper\", 3000L),\n                                IncrementPair.of(\"Only one more time\", 1850L),\n                                IncrementPair.of(\"Rock me, Handel\", 1325L),\n                                IncrementPair.of(\"How will anyone know?\", 3890L),\n                                IncrementPair.of(\"Average lover\", 4098L),\n                                IncrementPair.of(\"Road to everywhere\", 770L));\n                    })\n                    .thenCompose(res32 -> {\n                        System.out.println(res32);\n                        // >>> [null, null, null, Rock me, Handel, Only one more time, null]\n                        return asyncCommands.topKList(\"top_3_songs\");\n                    })\n                    .thenCompose(res33 -> {\n                        System.out.println(res33);\n                        // >>> [Average lover, How will anyone know?, Starfish Trooper]\n                        return asyncCommands.topKQuery(\"top_3_songs\", \"Starfish Trooper\", \"Road to everywhere\");\n                    })\n                    .thenAccept(res34 -> {\n                        System.out.println(res34);\n                        // >>> [true, false]\n                    })\n                    .toCompletableFuture();\n\n            topkExample.join();","section_id":"ranking"},{"id":"ranking-ex1","language":"java","code":"String res31 = reactiveCommands.topKReserve(\"top_3_songs\", 3L,\n                    TopKReserveArgs.Builder.width(2000L).depth(7L).decay(0.925)).block();\n            System.out.println(res31);\n            // >>> OK\n\n            List<Value<String>> res32 = reactiveCommands\n                    .topKIncrBy(\"top_3_songs\",\n                            IncrementPair.of(\"Starfish Trooper\", 3000L),\n                            IncrementPair.of(\"Only one more time\", 1850L),\n                            IncrementPair.of(\"Rock me, Handel\", 1325L),\n                            IncrementPair.of(\"How will anyone know?\", 3890L),\n                            IncrementPair.of(\"Average lover\", 4098L),\n                            IncrementPair.of(\"Road to everywhere\", 770L))\n                    .collectList()\n                    .block();\n            System.out.println(res32);\n            // >>> [Value.empty, Value.empty, Value.empty, Value[Rock me, Handel], Value[Only one more time], Value.empty]\n\n            List<String> res33 = reactiveCommands.topKList(\"top_3_songs\").collectList().block();\n            System.out.println(res33);\n            // >>> [Average lover, How will anyone know?, Starfish Trooper]\n\n            List<Boolean> res34 = reactiveCommands\n                    .topKQuery(\"top_3_songs\", \"Starfish Trooper\", \"Road to everywhere\")\n                    .collectList()\n                    .block();\n            System.out.println(res34);\n            // >>> [true, false]","section_id":"ranking"}]}
{"schema_version":2,"id":"develop/clients/lettuce/produsage","title":"Production usage","url":"https://redis.io/docs/latest/develop/clients/lettuce/produsage/","summary":"Get your Lettuce app ready for production","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-05T16:11:44+01:00","page_type":"content","content_hash":"47e0e14e4dc97e0b554be802c8870b86a61bbdf45dd2e56977c1acf0ba19741d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This guide offers recommendations to get the best reliability and\nperformance in your production environment."},{"id":"checklist","title":"Checklist","role":"content","text":"Each item in the checklist below links to the section\nfor a recommendation. Use the checklist icons to record your\nprogress in implementing the recommendations.\n\n[code example]"},{"id":"recommendations","title":"Recommendations","role":"content","text":"The sections below offer recommendations for your production environment. Some\nof them may not apply to your particular use case."},{"id":"timeouts","title":"Timeouts","role":"content","text":"Lettuce provides timeouts for many operations, such as command execution, SSL handshake, and Sentinel discovery. By default, Lettuce uses a global timeout value of 60 seconds for these operations, but you can override the global timeout value with individual timeout values for each operation.\n\n> [!TIP]\n> Choosing suitable timeout values is crucial for your application's performance and stability and is specific to each environment.\n> Configuring timeouts is only necessary if you have issues with the default values. \n> In some cases, the defaults are based on environment-specific settings (e.g., operating system settings), while in other cases, they are built into the Lettuce driver. \n> For more details on setting specific timeouts, see the [Lettuce reference guide](https://redis.github.io/lettuce/)."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"To set TCP-level timeouts, you need to ensure you have one of [Netty Native Transports](https://netty.io/wiki/native-transports.html) installed. The most common one is `netty-transport-native-epoll`, which is used for Linux systems. You can add it to your project by including the following dependency in your `pom.xml` file:\n\n[code example]\n\nOnce you have the native transport dependency, you can verify that by using the following code:\n\n[code example]\n\nIf the snippet above returns `false`, you need to enable debugging logging for `io.lettuce.core` and `io.netty` to see why the native transport is not available.\n\nFor more information on using Netty Native Transport, see the [Lettuce reference guide](https://redis.github.io/lettuce/advanced-usage/native-transports/)."},{"id":"setting-timeouts","title":"Setting timeouts","role":"configuration","text":"Below is an example of setting socket-level timeouts. The `TCP_USER_TIMEOUT` setting is useful for scenarios where the server stops responding without acknowledging the last request, while the `KEEPALIVE` setting is good for detecting dead connections where there is no traffic between the client and the server.\n\n[code example]"},{"id":"setting-timeouts-in-spring-data-redis","title":"Setting timeouts in Spring Data Redis","role":"configuration","text":"If you are using Spring Data Redis, you can set timeouts and keepalive settings using `LettuceClientConfigurationBuilderCustomizer`:\n\n[code example]"},{"id":"cluster-topology-refresh","title":"Cluster topology refresh","role":"content","text":"The Redis Cluster configuration is dynamic and can change at runtime. \nNew nodes may be added, and the primary node for a specific slot can shift.\nLettuce automatically handles [MOVED](https://redis.io/docs/latest/operate/oss_and_stack/reference/cluster-spec#moved-redirection) and [ASK](https://redis.io/docs/latest/operate/oss_and_stack/reference/cluster-spec#ask-redirection) redirects, but to enhance your application's resilience, you should enable adaptive topology refreshing:\n\n[code example]\nLearn more about topology refresh configuration settings in [the reference guide](https://redis.github.io/lettuce/ha-sharding/#redis-cluster)."},{"id":"warm-up-cluster-connections","title":"Warm up cluster connections","role":"content","text":"With a Redis Cluster, Lettuce opens connections to individual nodes *lazily* -\nthe connection to a given shard is created the first time a command is routed\nto it. This keeps the connection footprint minimal, but it means the *first*\nrequests after startup each pay the cost of establishing a new connection\n(a TCP connection plus, when TLS is enabled, a TLS handshake) to a node that\nhas not been contacted yet. On a TLS cluster with several shards, this can add\na noticeable latency spike to a freshly started application's first burst of\ntraffic. Under constrained CPU (for example, a container that is CPU-throttled\nduring startup) that spike can be large enough to breach command timeouts.\n\nTo avoid this, open the per-node connections *before* your application starts\nserving traffic. The `upstream()` node selection targets every primary node;\nsending a `PING` to the selection forces each per-node connection to be\nestablished:\n\n[code example]\n\nBecause the cluster topology can change at runtime, connections to *new* nodes\nare still opened lazily after a topology change. If you want those warmed as\nwell, re-run the warm-up when the topology changes (for example, from a\n`ClusterTopologyChangedEvent` listener)."},{"id":"warming-up-connections-in-spring-data-redis","title":"Warming up connections in Spring Data Redis","role":"content","text":"With [Spring Data Redis](https://redis.io/docs/latest/integrate/spring-framework-cache), run the warm-up once at startup, before the instance is\nmarked ready. Obtain the shared native cluster connection from the\n`LettuceConnectionFactory` and warm it with the same `upstream()` call:\n\n[code example]\n\n> [!NOTE]\n> The `LettuceConnectionFactory` `eagerInitialization` option is not\n> sufficient on its own. It establishes the cluster topology and a single\n> connection at startup, but the remaining per-node connections are still opened\n> lazily on first use. Use the warm-up shown above to open connections to all\n> nodes."},{"id":"dns-cache-and-redis","title":"DNS cache and Redis","role":"content","text":"When you connect to a Redis server with multiple endpoints, such as [Redis Software Active-Active](https://redis.com/redis-enterprise/technology/active-active-geo-distribution/), you *must*\ndisable the JVM's DNS cache. If a server node or proxy fails, the IP address for any database\naffected by the failure will change. When this happens, your app will keep\ntrying to use the stale IP address if DNS caching is enabled.\n\nUse the following code to disable the DNS cache:\n\n[code example]"},{"id":"exception-handling","title":"Exception handling","role":"errors","text":"Redis handles many errors using return values from commands, but there\nare also situations where exceptions can be thrown. In production code,\nyou should handle exceptions as they occur.\n\nSee the Error handling sections of the\n[Lettuce async](https://redis.github.io/lettuce/user-guide/async-api/#error-handling) and\n[Lettuce reactive](https://redis.github.io/lettuce/user-guide/reactive-api/#error-handling)\nAPI guides to learn more about handling exceptions."},{"id":"connection-and-execution-reliability","title":"Connection and execution reliability","role":"content","text":"By default, Lettuce uses an *at-least-once* strategy for command execution.\nIt will automatically reconnect after a disconnection and resume executing\nany commands that were queued when the connection was lost. If you\nswitch to *at-most-once* execution, Lettuce will\nnot reconnect after a disconnection and will discard commands\ninstead of queuing them. You can enable at-most-once execution by setting\n`autoReconnect(false)` in the\n`ClientOptions` when you create the client, as shown in the example below:\n\n[code example]\n\nIf you need finer control over which commands you want to execute in which mode, you can\nconfigure a *replay filter* to choose the commands that should retry after a disconnection.\nThe example below shows a filter that retries all commands except for\n[`DECR`](https://redis.io/docs/latest/commands/decr)\n(this command is not [idempotent](https://en.wikipedia.org/wiki/Idempotence) and\nso you might need to avoid executing it more than once). Note that\nreplay filters are only available in Lettuce v6.6 and above.\n\n[code example]\n\nSee\n[Command execution reliability](https://redis.github.io/lettuce/advanced-usage/command-execution-reliability/)\nin the Lettuce reference guide for more information."},{"id":"smart-client-handoffs","title":"Smart client handoffs","role":"content","text":"*Smart client handoffs (SCH)* is a feature of Redis Cloud and\nRedis Software servers that lets them actively notify clients\nabout planned server maintenance shortly before it happens. This\nlets a client take action to avoid disruptions in service.\n\nSee [Smart client handoffs](https://redis.io/docs/latest/develop/clients/sch)\nfor more information about SCH and\n[Connect using Smart client handoffs](https://redis.io/docs/latest/develop/clients/lettuce/connect#connect-using-smart-client-handoffs-sch)\nfor example code."}],"examples":[{"id":"checklist-ex0","language":"checklist {id=\"lettuceprodlist\"}","code":"- [ ] [Timeouts](#timeouts)\n- [ ] [Cluster topology refresh](#cluster-topology-refresh)\n- [ ] [Warm up cluster connections](#warm-up-cluster-connections)\n- [ ] [DNS cache and Redis](#dns-cache-and-redis)\n- [ ] [Exception handling](#exception-handling)\n- [ ] [Connection and execution reliability](#connection-and-execution-reliability)\n- [ ] [Smart client handoffs](#seamless-client-experience)","section_id":"checklist"},{"id":"prerequisites-ex0","language":"xml","code":"<dependency>\n    <groupId>io.netty</groupId>\n    <artifactId>netty-transport-native-epoll</artifactId>\n    <version>${netty.version}</version> <!-- e.g., 4.1.118.Final -->\n    <classifier>linux-x86_64</classifier>\n</dependency>","section_id":"prerequisites"},{"id":"prerequisites-ex1","language":"java","code":"logger.info(\"Lettuce epool is available: {}\", EpollProvider.isAvailable());","section_id":"prerequisites"},{"id":"setting-timeouts-ex0","language":"java","code":"RedisURI redisURI = RedisURI.Builder\n        .redis(\"localhost\")\n        // set the global default from the default 60 seconds to 30 seconds\n        .withTimeout(Duration.ofSeconds(30)) \n        .build();\n\ntry (RedisClient client = RedisClient.create(redisURI)) {\n    // or set specific timeouts for things such as the TCP_USER_TIMEOUT and TCP_KEEPALIVE\n\n    // A good general rule of thumb is to follow the rule\n    // TCP_USER_TIMEOUT = TCP_KEEP_IDLE+TCP_KEEPINTVL * TCP_KEEPCNT\n    // in this case, 20 = 5 + 5 * 3\n\n    SocketOptions.TcpUserTimeoutOptions tcpUserTimeout = SocketOptions.TcpUserTimeoutOptions.builder()\n            .tcpUserTimeout(Duration.ofSeconds(20))\n            .enable().build();\n\n    SocketOptions.KeepAliveOptions keepAliveOptions = SocketOptions.KeepAliveOptions.builder()\n            .interval(Duration.ofSeconds(5))\n            .idle(Duration.ofSeconds(5))\n            .count(3).enable().build();\n\n    SocketOptions socketOptions = SocketOptions.builder()\n            .tcpUserTimeout(tcpUserTimeout)\n            .keepAlive(keepAliveOptions)\n            .build();\n\n    client.setOptions(ClientOptions.builder()\n            .socketOptions(socketOptions)\n            .build());\n\n    StatefulRedisConnection<String, String> connection = client.connect();\n    System.out.println(connection.sync().ping());\n}","section_id":"setting-timeouts"},{"id":"setting-timeouts-in-spring-data-redis-ex0","language":"java","code":"@Bean\npublic LettuceClientConfigurationBuilderCustomizer lettuceClientConfigurationBuilderCustomizer() {\n    return clientConfigurationBuilder -> {\n        // Configure TCP User Timeout\n        // This is useful for scenarios where the server stops responding without\n        // acknowledging the last request\n        SocketOptions.TcpUserTimeoutOptions tcpUserTimeout = SocketOptions.TcpUserTimeoutOptions.builder()\n                .tcpUserTimeout(Duration.ofSeconds(20))\n                .enable()\n                .build();\n\n        // Configure TCP Keep-Alive\n        // This is good for detecting dead connections where there is no traffic\n        // between the client and the server\n        SocketOptions.KeepAliveOptions keepAliveOptions = SocketOptions.KeepAliveOptions.builder()\n                .interval(Duration.ofSeconds(5))  // TCP_KEEPINTVL: interval between probes\n                .idle(Duration.ofSeconds(5))      // TCP_KEEPIDLE: time before first probe\n                .count(3)                         // TCP_KEEPCNT: number of probes\n                .enable()\n                .build();\n\n        // Build SocketOptions with both TCP User Timeout and Keep-Alive\n        SocketOptions socketOptions = SocketOptions.builder()\n                .tcpUserTimeout(tcpUserTimeout)\n                .keepAlive(keepAliveOptions)\n                .build();\n\n        // Build ClientOptions with the configured SocketOptions\n        ClientOptions clientOptions = ClientOptions.builder()\n                .socketOptions(socketOptions)\n                .build();\n\n        // Apply the client options and command timeout to the builder\n        clientConfigurationBuilder\n                .clientOptions(clientOptions)\n                .commandTimeout(Duration.ofSeconds(30));  // Global command timeout\n    };\n}","section_id":"setting-timeouts-in-spring-data-redis"},{"id":"cluster-topology-refresh-ex0","language":"java","code":"RedisURI redisURI = RedisURI.Builder\n        .redis(\"localhost\")\n        // set the global default from the default 60 seconds to 30 seconds\n        .withTimeout(Duration.ofSeconds(30)) \n        .build();\n        \n// Create a RedisClusterClient with adaptive topology refresh\ntry (RedisClusterClient clusterClient = RedisClusterClient.create(redisURI)) {\n    // Enable TCP keep-alive and TCP user timeout just like in the standalone example\n    SocketOptions.TcpUserTimeoutOptions tcpUserTimeout = SocketOptions.TcpUserTimeoutOptions.builder()\n            .tcpUserTimeout(Duration.ofSeconds(20))\n            .enable()\n            .build();\n\n    SocketOptions.KeepAliveOptions keepAliveOptions = SocketOptions.KeepAliveOptions.builder()\n            .interval(Duration.ofSeconds(5))\n            .idle(Duration.ofSeconds(5))\n            .count(3)\n            .enable()\n            .build();\n\n    SocketOptions socketOptions = SocketOptions.builder()\n            .tcpUserTimeout(tcpUserTimeout)\n            .keepAlive(keepAliveOptions)\n            .build();\n\n    // Enable adaptive topology refresh\n    // Configure adaptive topology refresh options\n    ClusterTopologyRefreshOptions topologyRefreshOptions = ClusterTopologyRefreshOptions.builder()\n            .enableAllAdaptiveRefreshTriggers()\n            .adaptiveRefreshTriggersTimeout(Duration.ofSeconds(30))\n            .build();\n    \n    ClusterClientOptions options = ClusterClientOptions.builder()\n            .topologyRefreshOptions(topologyRefreshOptions)\n            .socketOptions(socketOptions).build();\n\n    clusterClient.setOptions(options);\n\n    StatefulRedisClusterConnection<String, String> connection = clusterClient.connect();\n    System.out.println(connection.sync().ping());\n    connection.close();\n}","section_id":"cluster-topology-refresh"},{"id":"warm-up-cluster-connections-ex0","language":"java","code":"RedisURI redisURI = RedisURI.Builder\n        .redis(\"localhost\")\n        .withSsl(true)\n        .build();\n\ntry (RedisClusterClient clusterClient = RedisClusterClient.create(redisURI)) {\n\n    StatefulRedisClusterConnection<String, String> connection = clusterClient.connect();\n\n    // Warm up: open a connection to every primary node before serving traffic.\n    // upstream() selects all primaries; the PING forces each per-node connection to open.\n    connection.sync().upstream().commands().ping();\n\n    // If you read from replicas (ReadFrom.REPLICA / REPLICA_PREFERRED),\n    // warm the replica connections too:\n    // connection.sync()\n    //         .readonly(node -> node.is(RedisClusterNode.NodeFlag.REPLICA))\n    //         .commands().ping();\n\n    System.out.println(connection.sync().ping());\n}","section_id":"warm-up-cluster-connections"},{"id":"warming-up-connections-in-spring-data-redis-ex0","language":"java","code":"@Component\nclass RedisClusterWarmUp {\n\n    private final LettuceConnectionFactory factory;\n\n    RedisClusterWarmUp(RedisConnectionFactory factory) {\n        this.factory = (LettuceConnectionFactory) factory;\n    }\n\n    // Runs after the context starts. To keep traffic off the instance until the\n    // warm-up completes, gate your readiness probe on it (for example, with a\n    // HealthIndicator that reports \"up\" only after this method succeeds).\n    @EventListener(ApplicationStartedEvent.class)\n    void warmUp() {\n        try (RedisClusterConnection clusterConnection = factory.getClusterConnection()) {\n            @SuppressWarnings(\"unchecked\")\n            StatefulRedisClusterConnection<byte[], byte[]> connection =\n                    ((RedisAdvancedClusterAsyncCommands<byte[], byte[]>) clusterConnection.getNativeConnection())\n                            .getStatefulConnection();\n            connection.sync().upstream().commands().ping();\n        }\n    }\n}","section_id":"warming-up-connections-in-spring-data-redis"},{"id":"dns-cache-and-redis-ex0","language":"java","code":"java.security.Security.setProperty(\"networkaddress.cache.ttl\",\"0\");\njava.security.Security.setProperty(\"networkaddress.cache.negative.ttl\", \"0\");","section_id":"dns-cache-and-redis"},{"id":"connection-and-execution-reliability-ex0","language":"java","code":"RedisURI uri = RedisURI.Builder\n                .redis(\"localhost\", 6379)\n                .withAuthentication(\"default\", \"yourPassword\")\n                .build();\n\nRedisClient client = RedisClient.create(uri);\n\nclient.setOptions(ClientOptions.builder()\n    .autoReconnect(false)\n        .\n        .\n    .build());","section_id":"connection-and-execution-reliability"},{"id":"connection-and-execution-reliability-ex1","language":"java","code":"Predicate<RedisCommand<?, ?, ?> > filter =\n        cmd -> cmd.getType().toString().equalsIgnoreCase(\"DECR\");\n\nclient.setOptions(ClientOptions.builder()\n    .replayFilter(filter)\n    .build());","section_id":"connection-and-execution-reliability"}]}
{"schema_version":2,"id":"develop/clients/lettuce/queryjson","title":"Index and query documents","url":"https://redis.io/docs/latest/develop/clients/lettuce/queryjson/","summary":"Learn how to use Redis Search with JSON and hash documents.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-16T16:25:49+01:00","page_type":"content","content_hash":"cfd7112e4e04245f6a34bc4ff9b07e1d4a7dd92a672dd1dbd4013265d5751641","sections":[{"id":"initialize","title":"Initialize","role":"content","text":"Make sure that you have [Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack)\nor another Redis server available. Also install the\n[Lettuce](https://redis.io/docs/latest/develop/clients/lettuce) client library if you\nhaven't already done so.\n\nAdd the following dependencies. All of them are applicable to both JSON and hash,\nexcept for the `JsonParser`, `JsonPath`, and `JsonObject` classes.\n\nFoundational: Import required Lettuce and JSON libraries for querying JSON documents\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]"},{"id":"create-data","title":"Create data","role":"content","text":"Create some test data to add to the database:\n\nFoundational: Define sample JSON data structures for users with fields like name, age, and city\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]"},{"id":"add-the-index","title":"Add the index","role":"content","text":"Connect to your Redis database. The code below shows the most\nbasic connection but see\n[Connect to the server](https://redis.io/docs/latest/develop/clients/lettuce/connect)\nto learn more about the available connection options.\n\nFoundational: Establish a connection to Redis for executing search and query operations\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n\n\nCreate an index. In this example, only JSON documents with the key prefix `user:` are indexed. For more information, see [Query syntax](https://redis.io/docs/latest/develop/ai/search-and-query/query).\n\nFoundational: Create a search index on JSON documents with field mappings and aliases for efficient querying\n\n**Difficulty:** Intermediate\n\n**Available in:** Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]"},{"id":"add-the-data","title":"Add the data","role":"content","text":"Add the three sets of user data to the database as\n[JSON](https://redis.io/docs/latest/develop/data-types/json) objects.\nIf you use keys with the `user:` prefix then Redis will index the\nobjects automatically as you add them:\n\nFoundational: Store JSON documents in Redis using the JSON.SET command with keys matching the index prefix\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]"},{"id":"query-the-data","title":"Query the data","role":"content","text":"You can now use the index to search the JSON objects. The\n[query](https://redis.io/docs/latest/develop/ai/search-and-query/query)\nbelow searches for objects that have the text \"Paul\" in any field\nand have an `age` value in the range 30 to 40:\n\nQuery data: Execute a full-text search combined with numeric range filtering to find matching documents\n\n**Difficulty:** Intermediate\n\n**Available in:** Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n\n\nSpecify query options to return only the `city` field:\n\nRestrict query results: Use query options to project specific fields from search results, reducing data transfer\n\n**Difficulty:** Intermediate\n\n**Available in:** Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n\n\nUse an\n[aggregation query](https://redis.io/docs/latest/develop/ai/search-and-query/query/aggregation)\nto count all users in each city.\n\nAggregation: Use aggregation queries to group and count results, performing server-side data analysis\n\n**Difficulty:** Advanced\n\n**Available in:** Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]"},{"id":"differences-with-hash-documents","title":"Differences with hash documents","role":"content","text":"Indexing for hash documents is very similar to JSON indexing but you\nneed to specify some slightly different options.\n\nWhen you create the schema for a hash index, you don't need to\nadd aliases for the fields, since you use the basic names to access\nthe fields. Also, you must use `CreateArgs.TargetType.HASH` for the `On()`\noption of `CreateArgs` when you create the index. The code below shows these\nchanges with a new index called `hash-idx:users`, which is otherwise the same as\nthe `idx:users` index used for JSON documents in the previous examples.\n\nFoundational: Create a search index on hash documents with TargetType.HASH configuration\n\n**Difficulty:** Intermediate\n\n**Available in:** Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n\n\nUse [`hset()`](https://redis.io/docs/latest/commands/hset) to add the hash\ndocuments instead of [`jsonSet()`](https://redis.io/docs/latest/commands/json.set).\n\nFoundational: Store hash documents in Redis using HSET command with keys matching the index prefix\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n\n\nThe query commands work the same here for hash as they do for JSON (but\nthe name of the hash index is different). The results are returned in\na `List` of `SearchReply.SearchResult<String, String>` objects, as with JSON:\n\nQuery data: Execute the same search query on hash documents as you would on JSON documents\n\n**Difficulty:** Intermediate\n\n**Available in:** Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]"},{"id":"bulk-import-with-himport","title":"Bulk import with HIMPORT","role":"content","text":"From Redis Open Source 8.10, you can also add the hash documents with the\n[`HIMPORT`](https://redis.io/docs/latest/commands/himport) command family. This creates the same\nhashes as `hset()`, but lets Redis store them as\n[compact hashes](https://redis.io/docs/latest/develop/data-types/hashes#compact-hashes), which use\nless memory when many keys share the same field names. Redis Search indexes\nthese hashes in the same way.\n\nCreate a `HashImport` object with the field names, then create each document\nwith [`himportSet()`](https://redis.io/docs/latest/commands/himport-set), passing only its values,\nin the same order as the field names. Lettuce declares the field names with\n[`HIMPORT PREPARE`](https://redis.io/docs/latest/commands/himport-prepare) the first time you use\nthe `HashImport` object on a connection. Issue all the imports before you wait\nfor any reply, so that Lettuce\n[pipelines](https://redis.io/docs/latest/develop/clients/lettuce/transpipe) them instead of\nwaiting for each reply in turn. With the reactive API, wait for the imports\ninside the `try` block, because a `Mono` that you subscribe to after the\n`HashImport` object closes fails:\n\nBulk ingestion: Import hash documents as compact hashes using a HashImport fieldset and pipelined HIMPORT SET calls\n\n**Difficulty:** Intermediate\n\n**Builds upon:** add_hash_data\n\n**Available in:** Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n\n\n`himportSet()` replaces any existing key, so running this code after the\n`hset()` calls leaves you with the same three documents. The query returns\nthe same result, although the fields can appear in a different order.\n\n> [!NOTE]\n> `HIMPORT` requires Lettuce v7.7.0 or later, and Lettuce marks its support as\n> experimental, so the API might change. Redis Cloud and Redis Software don't\n> support `HIMPORT`."},{"id":"more-information","title":"More information","role":"content","text":"See the [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) docs\nfor a full description of all query features with examples."}],"examples":[{"id":"initialize-ex0","language":"java","code":"import io.lettuce.core.*;\n\nimport io.lettuce.core.api.async.RedisAsyncCommands;\nimport io.lettuce.core.api.async.RediSearchAsyncCommands;\nimport io.lettuce.core.search.arguments.*;\nimport io.lettuce.core.search.arguments.AggregateArgs.*;\nimport io.lettuce.core.search.SearchReply;\nimport io.lettuce.core.search.AggregationReply;\n\nimport io.lettuce.core.json.JsonParser;\nimport io.lettuce.core.json.JsonObject;\nimport io.lettuce.core.json.JsonPath;\n\nimport io.lettuce.core.api.StatefulRedisConnection;\n\nimport java.util.*;\nimport java.util.concurrent.CompletableFuture;","section_id":"initialize"},{"id":"initialize-ex1","language":"java","code":"import io.lettuce.core.*;\n\nimport io.lettuce.core.api.reactive.RedisReactiveCommands;\nimport io.lettuce.core.api.reactive.RediSearchReactiveCommands;\nimport io.lettuce.core.search.arguments.*;\nimport io.lettuce.core.search.arguments.AggregateArgs.*;\nimport io.lettuce.core.search.SearchReply;\nimport io.lettuce.core.search.AggregationReply;\n\nimport io.lettuce.core.json.JsonParser;\nimport io.lettuce.core.json.JsonObject;\nimport io.lettuce.core.json.JsonPath;\n\nimport io.lettuce.core.api.StatefulRedisConnection;\n\nimport java.util.*;\nimport reactor.core.publisher.Mono;","section_id":"initialize"},{"id":"create-data-ex0","language":"java","code":"JsonParser parser = asyncCommands.getJsonParser();\n\n            JsonObject user1 = parser.createJsonObject().put(\"name\", parser.createJsonValue(\"\\\"Paul John\\\"\"))\n                    .put(\"email\", parser.createJsonValue(\"\\\"paul.john@example.com\\\"\")).put(\"age\", parser.createJsonValue(\"42\"))\n                    .put(\"city\", parser.createJsonValue(\"\\\"London\\\"\"));\n\n            JsonObject user2 = parser.createJsonObject().put(\"name\", parser.createJsonValue(\"\\\"Eden Zamir\\\"\"))\n                    .put(\"email\", parser.createJsonValue(\"\\\"eden.zamir@example.com\\\"\")).put(\"age\", parser.createJsonValue(\"29\"))\n                    .put(\"city\", parser.createJsonValue(\"\\\"Tel Aviv\\\"\"));\n\n            JsonObject user3 = parser.createJsonObject().put(\"name\", parser.createJsonValue(\"\\\"Paul Zamir\\\"\"))\n                    .put(\"email\", parser.createJsonValue(\"\\\"paul.zamir@example.com\\\"\")).put(\"age\", parser.createJsonValue(\"35\"))\n                    .put(\"city\", parser.createJsonValue(\"\\\"Tel Aviv\\\"\"));","section_id":"create-data"},{"id":"create-data-ex1","language":"java","code":"JsonParser parser = reactiveCommands.getJsonParser();\n            JsonObject user1 = parser.createJsonObject().put(\"name\", parser.createJsonValue(\"\\\"Paul John\\\"\"))\n                    .put(\"email\", parser.createJsonValue(\"\\\"paul.john@example.com\\\"\")).put(\"age\", parser.createJsonValue(\"42\"))\n                    .put(\"city\", parser.createJsonValue(\"\\\"London\\\"\"));\n\n            JsonObject user2 = parser.createJsonObject().put(\"name\", parser.createJsonValue(\"\\\"Eden Zamir\\\"\"))\n                    .put(\"email\", parser.createJsonValue(\"\\\"eden.zamir@example.com\\\"\")).put(\"age\", parser.createJsonValue(\"29\"))\n                    .put(\"city\", parser.createJsonValue(\"\\\"Tel Aviv\\\"\"));\n\n            JsonObject user3 = parser.createJsonObject().put(\"name\", parser.createJsonValue(\"\\\"Paul Zamir\\\"\"))\n                    .put(\"email\", parser.createJsonValue(\"\\\"paul.zamir@example.com\\\"\")).put(\"age\", parser.createJsonValue(\"35\"))\n                    .put(\"city\", parser.createJsonValue(\"\\\"Tel Aviv\\\"\"));","section_id":"create-data"},{"id":"add-the-index-ex0","language":"java","code":"RedisClient redisClient = RedisClient.create(\"redis://localhost:6379\");\n\n        try (StatefulRedisConnection<String, String> connection = redisClient.connect()) {\n            RedisAsyncCommands<String, String> asyncCommands = connection.async();\n            RediSearchAsyncCommands<String, String> searchCommands = connection.async();\n            // ...","section_id":"add-the-index"},{"id":"add-the-index-ex1","language":"java","code":"RedisClient redisClient = RedisClient.create(\"redis://localhost:6379\");\n\n        try (StatefulRedisConnection<String, String> connection = redisClient.connect()) {\n            RedisReactiveCommands<String, String> reactiveCommands = connection.reactive();\n            RediSearchReactiveCommands<String, String> searchCommands = connection.reactive();\n            // ...","section_id":"add-the-index"},{"id":"add-the-index-ex2","language":"java","code":"List<FieldArgs<String>> schema = Arrays.asList(TextFieldArgs.<String> builder().name(\"$.name\").as(\"name\").build(),\n                    NumericFieldArgs.<String> builder().name(\"$.age\").as(\"age\").build(),\n                    TagFieldArgs.<String> builder().name(\"$.city\").as(\"city\").build());\n\n            CreateArgs<String, String> createArgs = CreateArgs.<String, String> builder().on(CreateArgs.TargetType.JSON)\n                    .withPrefix(\"user:\").build();\n\n            CompletableFuture<Void> make_index = searchCommands.ftCreate(\"idx:users\", createArgs, schema)\n                    .thenAccept(System.out::println) // >>> OK\n                    .toCompletableFuture();","section_id":"add-the-index"},{"id":"add-the-index-ex3","language":"java","code":"List<FieldArgs<String>> schema = Arrays.asList(TextFieldArgs.<String> builder().name(\"$.name\").as(\"name\").build(),\n                    NumericFieldArgs.<String> builder().name(\"$.age\").as(\"age\").build(),\n                    TagFieldArgs.<String> builder().name(\"$.city\").as(\"city\").build());\n\n            CreateArgs<String, String> createArgs = CreateArgs.<String, String> builder().on(CreateArgs.TargetType.JSON)\n                    .withPrefix(\"user:\").build();\n\n            Mono<Void> make_index = searchCommands.ftCreate(\"idx:users\", createArgs, schema).doOnNext(res -> {\n                System.out.println(res); // >>> OK\n            }).then();","section_id":"add-the-index"},{"id":"add-the-data-ex0","language":"java","code":"CompletableFuture<String> addUser1 = asyncCommands.jsonSet(\"user:1\", JsonPath.ROOT_PATH, user1).thenApply(r -> {\n                System.out.println(r); // >>> OK\n                return r;\n            }).toCompletableFuture();\n\n            CompletableFuture<String> addUser2 = asyncCommands.jsonSet(\"user:2\", JsonPath.ROOT_PATH, user2).thenApply(r -> {\n                System.out.println(r); // >>> OK\n                return r;\n            }).toCompletableFuture();\n\n            CompletableFuture<String> addUser3 = asyncCommands.jsonSet(\"user:3\", JsonPath.ROOT_PATH, user3).thenApply(r -> {\n                System.out.println(r); // >>> OK\n                return r;\n            }).toCompletableFuture();","section_id":"add-the-data"},{"id":"add-the-data-ex1","language":"java","code":"Mono<String> addUser1 = reactiveCommands.jsonSet(\"user:1\", JsonPath.ROOT_PATH, user1).doOnNext(r -> {\n                System.out.println(r); // >>> OK\n            });\n\n            Mono<String> addUser2 = reactiveCommands.jsonSet(\"user:2\", JsonPath.ROOT_PATH, user2).doOnNext(r -> {\n                System.out.println(r); // >>> OK\n            });\n\n            Mono<String> addUser3 = reactiveCommands.jsonSet(\"user:3\", JsonPath.ROOT_PATH, user3).doOnNext(r -> {\n                System.out.println(r); // >>> OK\n            });","section_id":"add-the-data"},{"id":"query-the-data-ex0","language":"java","code":"CompletableFuture<SearchReply<String, String>> query1 = searchCommands.ftSearch(\"idx:users\", \"Paul @age:[30 40]\")\n                    .thenApply(res -> {\n                        List<SearchReply.SearchResult<String, String>> results = res.getResults();\n\n                        results.forEach(result -> {\n                            System.out.println(result.getId());\n                        });\n                        // >>> user:3\n                        return res;\n                    }).toCompletableFuture();","section_id":"query-the-data"},{"id":"query-the-data-ex1","language":"java","code":"Mono<SearchReply<String, String>> query1 = searchCommands.ftSearch(\"idx:users\", \"Paul @age:[30 40]\")\n                    .doOnNext(res -> {\n                        List<SearchReply.SearchResult<String, String>> results = res.getResults();\n\n                        results.forEach(result -> {\n                            System.out.println(result.getId());\n                        });\n                        // >>> user:3\n                    });","section_id":"query-the-data"},{"id":"query-the-data-ex2","language":"java","code":"SearchArgs<String, String> query2Args = SearchArgs.<String, String> builder().returnField(\"city\").build();\n            CompletableFuture<SearchReply<String, String>> query2 = searchCommands.ftSearch(\"idx:users\", \"Paul\", query2Args)\n                    .thenApply(res -> {\n                        List<SearchReply.SearchResult<String, String>> results = res.getResults();\n\n                        results.forEach(result -> {\n                            System.out.printf(\"ID: %s, City: %s\\n\", result.getId(), result.getFields().get(\"city\"));\n                        });\n                        // >>> ID: user:1, City: London\n                        // >>> ID: user:3, City: Tel Aviv\n                        return res;\n                    }).toCompletableFuture();","section_id":"query-the-data"},{"id":"query-the-data-ex3","language":"java","code":"SearchArgs<String, String> query2Args = SearchArgs.<String, String> builder().returnField(\"city\").build();\n\n            Mono<SearchReply<String, String>> query2 = searchCommands.ftSearch(\"idx:users\", \"Paul\", query2Args)\n                    .doOnNext(res -> {\n                        List<SearchReply.SearchResult<String, String>> results = res.getResults();\n\n                        results.forEach(result -> {\n                            System.out.printf(\"ID: %s, City: %s\\n\", result.getId(), result.getFields().get(\"city\"));\n                        });\n                        // >>> ID: user:1, City: London\n                        // >>> ID: user:3, City: Tel Aviv\n                    });","section_id":"query-the-data"},{"id":"query-the-data-ex4","language":"java","code":"AggregateArgs<String, String> aggArgs = AggregateArgs.<String, String> builder()\n                    .groupBy(GroupBy.<String, String> of(\"@city\").reduce(Reducer.<String, String> count().as(\"count\"))).build();\n            CompletableFuture<AggregationReply<String, String>> query3 = searchCommands.ftAggregate(\"idx:users\", \"*\", aggArgs)\n                    .thenApply(res -> {\n                        List<SearchReply<String, String>> replies = res.getReplies();\n                        replies.forEach(reply -> {\n                            reply.getResults().forEach(result -> {\n                                System.out.printf(\"City: %s, Count: %s\\n\", result.getFields().get(\"city\"),\n                                        result.getFields().get(\"count\"));\n                            });\n                            // >>> City: London, Count: 1\n                            // >>> City: Tel Aviv, Count: 2\n                        });\n                        return res;\n                    }).toCompletableFuture();","section_id":"query-the-data"},{"id":"query-the-data-ex5","language":"java","code":"AggregateArgs<String, String> aggArgs = AggregateArgs.<String, String> builder()\n                    .groupBy(GroupBy.<String, String> of(\"@city\").reduce(Reducer.<String, String> count().as(\"count\"))).build();\n\n            Mono<AggregationReply<String, String>> query3 = searchCommands.ftAggregate(\"idx:users\", \"*\", aggArgs)\n                    .doOnNext(res -> {\n                        List<SearchReply<String, String>> replies = res.getReplies();\n                        replies.forEach(reply -> {\n                            reply.getResults().forEach(result -> {\n                                System.out.printf(\"City: %s, Count: %s\\n\", result.getFields().get(\"city\"),\n                                        result.getFields().get(\"count\"));\n                            });\n                            // >>> City: London, Count: 1\n                            // >>> City: Tel Aviv, Count: 2\n                        });\n                    });","section_id":"query-the-data"},{"id":"differences-with-hash-documents-ex0","language":"java","code":"List<FieldArgs<String>> hashSchema = Arrays.asList(TextFieldArgs.<String> builder().name(\"name\").build(),\n                    NumericFieldArgs.<String> builder().name(\"age\").build(),\n                    TagFieldArgs.<String> builder().name(\"city\").build());\n\n            CreateArgs<String, String> hashCreateArgs = CreateArgs.<String, String> builder().on(CreateArgs.TargetType.HASH)\n                    .withPrefix(\"huser:\").build();\n\n            CompletableFuture<Void> makeHashIndex = searchCommands.ftCreate(\"hash-idx:users\", hashCreateArgs, hashSchema)\n                    .thenAccept(System.out::println) // >>> OK\n                    .toCompletableFuture();","section_id":"differences-with-hash-documents"},{"id":"differences-with-hash-documents-ex1","language":"java","code":"List<FieldArgs<String>> hashSchema = Arrays.asList(TextFieldArgs.<String> builder().name(\"name\").build(),\n                    NumericFieldArgs.<String> builder().name(\"age\").build(),\n                    TagFieldArgs.<String> builder().name(\"city\").build());\n\n            CreateArgs<String, String> hashCreateArgs = CreateArgs.<String, String> builder().on(CreateArgs.TargetType.HASH)\n                    .withPrefix(\"huser:\").build();\n\n            Mono<String> makeHashIndex = searchCommands.ftCreate(\"hash-idx:users\", hashCreateArgs, hashSchema).doOnNext(res -> {\n                System.out.println(res); // >>> OK\n            });","section_id":"differences-with-hash-documents"},{"id":"differences-with-hash-documents-ex2","language":"java","code":"Map<String, String> huser1 = new HashMap<>();\n            huser1.put(\"name\", \"Paul John\");\n            huser1.put(\"email\", \"paul.john@example.com\");\n            huser1.put(\"age\", \"42\");\n            huser1.put(\"city\", \"London\");\n\n            Map<String, String> huser2 = new HashMap<>();\n            huser2.put(\"name\", \"Eden Zamir\");\n            huser2.put(\"email\", \"eden.zamir@example.com\");\n            huser2.put(\"age\", \"29\");\n            huser2.put(\"city\", \"Tel Aviv\");\n\n            Map<String, String> huser3 = new HashMap<>();\n            huser3.put(\"name\", \"Paul Zamir\");\n            huser3.put(\"email\", \"paul.zamir@example.com\");\n            huser3.put(\"age\", \"35\");\n            huser3.put(\"city\", \"Tel Aviv\");\n\n            CompletableFuture<Long> addHashUser1 = asyncCommands.hset(\"huser:1\", huser1).thenApply(r -> {\n                System.out.println(r); // >>> 4\n                return r;\n            }).toCompletableFuture();\n\n            CompletableFuture<Long> addHashUser2 = asyncCommands.hset(\"huser:2\", huser2).thenApply(r -> {\n                System.out.println(r); // >>> 4\n                return r;\n            }).toCompletableFuture();\n\n            CompletableFuture<Long> addHashUser3 = asyncCommands.hset(\"huser:3\", huser3).thenApply(r -> {\n                System.out.println(r); // >>> 4\n                return r;\n            }).toCompletableFuture();","section_id":"differences-with-hash-documents"},{"id":"differences-with-hash-documents-ex3","language":"java","code":"Map<String, String> huser1 = new HashMap<>();\n            huser1.put(\"name\", \"Paul John\");\n            huser1.put(\"email\", \"paul.john@example.com\");\n            huser1.put(\"age\", \"42\");\n            huser1.put(\"city\", \"London\");\n\n            Map<String, String> huser2 = new HashMap<>();\n            huser2.put(\"name\", \"Eden Zamir\");\n            huser2.put(\"email\", \"eden.zamir@example.com\");\n            huser2.put(\"age\", \"29\");\n            huser2.put(\"city\", \"Tel Aviv\");\n\n            Map<String, String> huser3 = new HashMap<>();\n            huser3.put(\"name\", \"Paul Zamir\");\n            huser3.put(\"email\", \"paul.zamir@example.com\");\n            huser3.put(\"age\", \"35\");\n            huser3.put(\"city\", \"Tel Aviv\");\n\n            Mono<Long> addHashUser1 = reactiveCommands.hset(\"huser:1\", huser1).doOnNext(r -> {\n                System.out.println(r); // >>> 4\n            });\n\n            Mono<Long> addHashUser2 = reactiveCommands.hset(\"huser:2\", huser2).doOnNext(r -> {\n                System.out.println(r); // >>> 4\n            });\n\n            Mono<Long> addHashUser3 = reactiveCommands.hset(\"huser:3\", huser3).doOnNext(r -> {\n                System.out.println(r); // >>> 4\n            });","section_id":"differences-with-hash-documents"},{"id":"differences-with-hash-documents-ex4","language":"java","code":"CompletableFuture<SearchReply<String, String>> query1Hash = searchCommands\n                    .ftSearch(\"hash-idx:users\", \"Paul @age:[30 40]\").thenApply(res -> {\n                        List<SearchReply.SearchResult<String, String>> results = res.getResults();\n\n                        results.forEach(result -> {\n                            System.out.println(result.getId());\n                        });\n                        // >>> huser:3\n                        return res;\n                    }).toCompletableFuture();","section_id":"differences-with-hash-documents"},{"id":"differences-with-hash-documents-ex5","language":"java","code":"Mono<SearchReply<String, String>> query1Hash = searchCommands.ftSearch(\"hash-idx:users\", \"Paul @age:[30 40]\")\n                    .doOnNext(res -> {\n                        List<SearchReply.SearchResult<String, String>> results = res.getResults();\n\n                        results.forEach(result -> {\n                            System.out.println(result.getId());\n                        });\n                        // >>> huser:3\n                    });","section_id":"differences-with-hash-documents"},{"id":"bulk-import-with-himport-ex0","language":"java","code":"List<RedisFuture<String>> himportUsers = new ArrayList<>();\n\n            try (HashImport<String> fieldset = HashImport.of(\"name\", \"email\", \"age\", \"city\")) {\n                himportUsers.add(asyncCommands.himportSet(\"huser:1\", fieldset,\n                        \"Paul John\", \"paul.john@example.com\", \"42\", \"London\"));\n                himportUsers.add(asyncCommands.himportSet(\"huser:2\", fieldset,\n                        \"Eden Zamir\", \"eden.zamir@example.com\", \"29\", \"Tel Aviv\"));\n                himportUsers.add(asyncCommands.himportSet(\"huser:3\", fieldset,\n                        \"Paul Zamir\", \"paul.zamir@example.com\", \"35\", \"Tel Aviv\"));\n            }\n\n            CompletableFuture<Void> importUsers = CompletableFuture\n                    .allOf(himportUsers.stream().map(RedisFuture::toCompletableFuture).toArray(CompletableFuture[]::new));","section_id":"bulk-import-with-himport"},{"id":"bulk-import-with-himport-ex1","language":"java","code":"try (HashImport<String> fieldset = HashImport.of(\"name\", \"email\", \"age\", \"city\")) {\n                Mono<String> import1 = reactiveCommands.himportSet(\"huser:1\", fieldset,\n                        \"Paul John\", \"paul.john@example.com\", \"42\", \"London\");\n                Mono<String> import2 = reactiveCommands.himportSet(\"huser:2\", fieldset,\n                        \"Eden Zamir\", \"eden.zamir@example.com\", \"29\", \"Tel Aviv\");\n                Mono<String> import3 = reactiveCommands.himportSet(\"huser:3\", fieldset,\n                        \"Paul Zamir\", \"paul.zamir@example.com\", \"35\", \"Tel Aviv\");\n\n                // Subscribe before the fieldset closes.\n                Mono.when(import1, import2, import3).block();\n            }","section_id":"bulk-import-with-himport"}]}
{"schema_version":2,"id":"develop/clients/lettuce/transpipe","title":"Pipelines and transactions","url":"https://redis.io/docs/latest/develop/clients/lettuce/transpipe/","summary":"Learn how to use Redis pipelines and transactions","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-09T09:47:45+01:00","page_type":"content","content_hash":"7839a2fb06cafe381bf3501e84163c3776e51121da9579c906e009efb33a5dd5","sections":[{"id":"execute-a-pipeline","title":"Execute a pipeline","role":"content","text":"With Lettuce, you pipeline commands by buffering them on the client and then\nflushing them to the server together. The synchronous and asynchronous APIs do\nthis by temporarily disabling auto-flushing with `setAutoFlushCommands()` and\nthen calling `flushCommands()`; each buffered command returns a `RedisFuture`\nthat you can inspect after the batch is flushed. The reactive API pipelines\ncommands by composing the corresponding `Mono` and `Flux` publishers:\n\nFoundational: Batch multiple Lettuce commands and flush them to the server together\n\n**Difficulty:** Beginner\n\n**Available in:** Java, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]"},{"id":"execute-a-transaction","title":"Execute a transaction","role":"content","text":"Lettuce transactions use the Redis `MULTI` and `EXEC` commands. Commands issued\nbetween `multi()` and `exec()` are queued on the server and only run when\n`exec()` completes. The return value from `exec()` is a `TransactionResult`\nthat contains the results in order:\n\nFoundational: Use MULTI and EXEC with Lettuce to execute multiple commands atomically\n\n**Difficulty:** Beginner\n\n**Available in:** Java, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]"},{"id":"watch-keys-for-changes","title":"Watch keys for changes","role":"content","text":"Redis supports *optimistic locking* to avoid inconsistent updates to keys that\nseveral clients may modify at the same time. The basic idea is to watch for\nchanges to any keys that you use in a transaction while you are preparing the\nupdate. If the watched keys do change, Redis discards the transaction and you\nmust retry using the latest value from Redis. See\n[Transactions](https://redis.io/docs/latest/develop/using-commands/transactions)\nfor more information about optimistic locking.\n\nThe example below watches a key, reads its current value, queues an update\ninside `MULTI`, and then checks `TransactionResult.wasDiscarded()` after\n`EXEC`:\n\nOptimistic locking: Use WATCH with EXEC and check whether Lettuce discarded the transaction\n\n**Difficulty:** Intermediate\n\n**Available in:** Java, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]"}],"examples":[{"id":"execute-a-pipeline-ex0","language":"java","code":"// Lettuce pipelines commands by buffering them on the client while\n            // auto-flushing is disabled. Use the asynchronous API to obtain a\n            // `RedisFuture` for each buffered command, then read the results\n            // after the batch has been flushed to the server.\n            RedisAsyncCommands<String, String> async = connection.async();\n            connection.setAutoFlushCommands(false);\n\n            for (int i = 0; i < 5; i++) {\n                async.set(\"seat:\" + i, \"#\" + i);\n            }\n\n            RedisFuture<String> seat0 = async.get(\"seat:0\");\n            RedisFuture<String> seat3 = async.get(\"seat:3\");\n            RedisFuture<String> seat4 = async.get(\"seat:4\");\n\n            connection.flushCommands();\n            connection.setAutoFlushCommands(true);\n\n            System.out.println(seat0.get()); // >>> #0\n            System.out.println(seat3.get()); // >>> #3\n            System.out.println(seat4.get()); // >>> #4","section_id":"execute-a-pipeline"},{"id":"execute-a-pipeline-ex1","language":"java","code":"// Disable auto-flushing to buffer the commands on the client, then\n            // flush them to the server as a single batch. Each command returns\n            // a `RedisFuture` that completes when its response arrives.\n            connection.setAutoFlushCommands(false);\n\n            for (int i = 0; i < 5; i++) {\n                async.set(\"seat:\" + i, \"#\" + i);\n            }\n\n            RedisFuture<String> seat0 = async.get(\"seat:0\");\n            RedisFuture<String> seat3 = async.get(\"seat:3\");\n            RedisFuture<String> seat4 = async.get(\"seat:4\");\n\n            connection.flushCommands();\n            connection.setAutoFlushCommands(true);\n\n            System.out.println(seat0.get()); // >>> #0\n            System.out.println(seat3.get()); // >>> #3\n            System.out.println(seat4.get()); // >>> #4","section_id":"execute-a-pipeline"},{"id":"execute-a-pipeline-ex2","language":"java","code":"// Reactive commands are pipelined automatically: the writes below are\n            // sent to the server back to back, then the reads are sent as a batch\n            // once you subscribe (here, by calling `block()`).\n            List<String> seats = Flux.range(0, 5)\n                    .flatMap(i -> reactive.set(\"seat:\" + i, \"#\" + i))\n                    .thenMany(Flux.just(\"seat:0\", \"seat:3\", \"seat:4\")\n                            .flatMapSequential(reactive::get))\n                    .collectList()\n                    .block();\n\n            System.out.println(seats.get(0)); // >>> #0\n            System.out.println(seats.get(1)); // >>> #3\n            System.out.println(seats.get(2)); // >>> #4","section_id":"execute-a-pipeline"},{"id":"execute-a-transaction-ex0","language":"java","code":"commands.multi();\n\n            commands.incrby(\"counter:1\", 1);\n            commands.incrby(\"counter:2\", 2);\n            commands.incrby(\"counter:3\", 3);\n\n            TransactionResult transResult = commands.exec();\n\n            System.out.println((Long) transResult.get(0)); // >>> 1\n            System.out.println((Long) transResult.get(1)); // >>> 2\n            System.out.println((Long) transResult.get(2)); // >>> 3","section_id":"execute-a-transaction"},{"id":"execute-a-transaction-ex1","language":"java","code":"// Commands issued between `multi()` and `exec()` are queued on the\n            // server. Their futures only complete when `exec()` runs.\n            async.multi();\n\n            RedisFuture<Long> counter1 = async.incrby(\"counter:1\", 1);\n            RedisFuture<Long> counter2 = async.incrby(\"counter:2\", 2);\n            RedisFuture<Long> counter3 = async.incrby(\"counter:3\", 3);\n\n            RedisFuture<TransactionResult> execResult = async.exec();\n            execResult.get();\n\n            System.out.println(counter1.get()); // >>> 1\n            System.out.println(counter2.get()); // >>> 2\n            System.out.println(counter3.get()); // >>> 3","section_id":"execute-a-transaction"},{"id":"execute-a-transaction-ex2","language":"java","code":"// Queue the commands between `multi()` and `exec()`. Subscribing to\n            // each one inside the `multi()` callback sends it to the server, where\n            // it is held until `exec()` runs the transaction.\n            TransactionResult transResult = reactive.multi()\n                    .flatMap(ok -> {\n                        reactive.incrby(\"counter:1\", 1).subscribe();\n                        reactive.incrby(\"counter:2\", 2).subscribe();\n                        reactive.incrby(\"counter:3\", 3).subscribe();\n                        return reactive.exec();\n                    })\n                    .block();\n\n            System.out.println((Long) transResult.get(0)); // >>> 1\n            System.out.println((Long) transResult.get(1)); // >>> 2\n            System.out.println((Long) transResult.get(2)); // >>> 3","section_id":"execute-a-transaction"},{"id":"watch-keys-for-changes-ex0","language":"java","code":"commands.set(\"shellpath\", \"/usr/syscmds/\");\n\n            // Watch the key for changes while we prepare the update.\n            commands.watch(\"shellpath\");\n\n            String currentPath = commands.get(\"shellpath\");\n            String newPath = currentPath + \":/usr/mycmds/\";\n\n            commands.multi();\n            commands.set(\"shellpath\", newPath);\n\n            TransactionResult watchedResult = commands.exec();\n\n            // `exec()` returns an empty result that reports `wasDiscarded()` if\n            // the watched key changed before the transaction ran.\n            if (!watchedResult.wasDiscarded()) {\n                System.out.println(commands.get(\"shellpath\"));\n                // >>> /usr/syscmds/:/usr/mycmds/\n            }","section_id":"watch-keys-for-changes"},{"id":"watch-keys-for-changes-ex1","language":"java","code":"async.set(\"shellpath\", \"/usr/syscmds/\").get();\n\n            // Watch the key for changes while we prepare the update.\n            async.watch(\"shellpath\").get();\n\n            String currentPath = async.get(\"shellpath\").get();\n            String newPath = currentPath + \":/usr/mycmds/\";\n\n            async.multi().get();\n            async.set(\"shellpath\", newPath);\n\n            TransactionResult watchedResult = async.exec().get();\n\n            // `exec()` reports `wasDiscarded()` if the watched key changed\n            // before the transaction ran.\n            if (!watchedResult.wasDiscarded()) {\n                System.out.println(async.get(\"shellpath\").get());\n                // >>> /usr/syscmds/:/usr/mycmds/\n            }","section_id":"watch-keys-for-changes"},{"id":"watch-keys-for-changes-ex2","language":"java","code":"reactive.set(\"shellpath\", \"/usr/syscmds/\").block();\n\n            // Watch the key, read its current value, then queue the update in a\n            // transaction. `exec()` reports `wasDiscarded()` if the watched key\n            // changed before the transaction ran.\n            TransactionResult watchedResult = reactive.watch(\"shellpath\")\n                    .then(reactive.get(\"shellpath\"))\n                    .flatMap(currentPath -> reactive.multi()\n                            .flatMap(ok -> {\n                                reactive.set(\"shellpath\", currentPath + \":/usr/mycmds/\").subscribe();\n                                return reactive.exec();\n                            }))\n                    .block();\n\n            if (!watchedResult.wasDiscarded()) {\n                System.out.println(reactive.get(\"shellpath\").block());\n                // >>> /usr/syscmds/:/usr/mycmds/\n            }","section_id":"watch-keys-for-changes"}]}
{"schema_version":2,"id":"develop/clients/lettuce/vecsearch","title":"Index and query vectors","url":"https://redis.io/docs/latest/develop/clients/lettuce/vecsearch/","summary":"Learn how to index and query vector embeddings with Redis","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-16T16:44:37+01:00","page_type":"content","content_hash":"d9070186172ba2fad075f601cdaf9b4afe24372732417e8c5f26e15726212b66","sections":[{"id":"initialize","title":"Initialize","role":"content","text":"If you are using [Maven](https://maven.apache.org/), add the following\ndependencies to your `pom.xml` file:\n\n[code example]\n\nIf you are using [Gradle](https://gradle.org/), add the following\ndependencies to your `build.gradle` file:\n\n[code example]"},{"id":"import-dependencies","title":"Import dependencies","role":"content","text":"Import the following classes in your source file:\n\nFoundational: Import Lettuce, DJL, and embedding model libraries for vector search\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]"},{"id":"define-a-helper-method","title":"Define a helper method","role":"content","text":"When you store vectors in a hash object, or pass them as query parameters,\nyou must encode the `float` components of the vector\narray as a `byte` string. The helper method `floatArrayToByteArray()`\nshown below does this for you:\n\nFoundational: Convert float arrays to byte arrays for storing vectors in hash objects\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]"},{"id":"create-an-embedding-model-instance","title":"Create an embedding model instance","role":"content","text":"The example below uses the\n[`all-MiniLM-L6-v2`](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2)\nmodel to generate the embeddings. This model generates vectors with 384 dimensions, regardless of the length of the input text, but note that the input is truncated to 256\ntokens (see\n[Word piece tokenization](https://huggingface.co/learn/nlp-course/en/chapter6/6)\nat the [Hugging Face](https://huggingface.co/) docs to learn more about the way tokens\nare related to the original text).\n\nThe [`Predictor`](https://javadoc.io/doc/ai.djl/api/latest/ai/djl/inference/Predictor.html)\nclass implements the model to generate the embeddings. The code below\ncreates an instance of `Predictor` that uses the `all-MiniLM-L6-v2` model:\n\nPractical pattern: Initialize a DJL Predictor with the all-MiniLM-L6-v2 embedding model\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]"},{"id":"create-the-index","title":"Create the index","role":"content","text":"As noted in [Define a helper method](#define-a-helper-method) above, you must\npass the embeddings to the hash and query commands as a binary string.\n\nLettuce lets you choose a codec for each connection to Redis (see\n[Codecs](https://redis.github.io/lettuce/integration-extension/#codecs)\nin the Lettuce documentation for more information).\n`RedisCodec.of(StringCodec.UTF8, ByteArrayCodec.INSTANCE)` combines two codecs\nso that keys, field names, and index names are `String` objects, while values are\n`byte[]` arrays. The embeddings are binary strings, and you pass text values as\nUTF-8 encoded `byte[]` arrays. However, you will probably find it more convenient\nto use the default `StringCodec` for commands that don't require binary strings.\nIt is therefore helpful to have two connections available, one using the\ncomposed codec and one using `StringCodec`.\n\nThe code below shows how to declare one connection with the\ncomposed codec and another without in the try-with-resources\nblock. You also need two separate instances of `RedisAsyncCommands` to\nuse the two connections:\n\nPractical pattern: Create dual connections with a composed String/byte[] codec and StringCodec for vector operations\n\n**Difficulty:** Intermediate\n\n**Available in:** Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n\n\nNext, create the index.\nThe schema in the example below includes three fields:\n\n-   The text content to index\n-   A [tag](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/tags)\n    field to represent the \"genre\" of the text\n-   The embedding vector generated from the original text content\n\nThe `embedding` field specifies\n[HNSW](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#hnsw-index) \nindexing, the\n[L2](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#distance-metrics)\nvector distance metric, `Float32` values to represent the vector's components,\nand 384 dimensions, as required by the `all-MiniLM-L6-v2` embedding model.\n\nThe `CreateArgs` object specifies hash objects for storage and a\nprefix `doc:` that identifies the hash objects to index.\n\nFoundational: Create a vector search index with HNSW algorithm, L2 distance metric, and 384-dimensional embeddings\n\n**Difficulty:** Intermediate\n\n**Available in:** Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]"},{"id":"add-data","title":"Add data","role":"content","text":"You can now supply the data objects, which will be indexed automatically\nwhen you add them with [`hset()`](https://redis.io/docs/latest/commands/hset), as long as\nyou use the `doc:` prefix specified in the index definition.\n\nUse the `predict()` method of the `Predictor` object\nas shown below to create the embedding that represents the `content` field\nand use the `floatArrayToByteArray()` helper method to convert it to a binary string.\nUse the binary string representation when you are\nindexing hash objects, but use an array of `float` for\nJSON objects (see [Differences with JSON objects](#differences-with-json-documents)\nbelow).\n\nYou must use instances of `Map<String, byte[]>` to supply the data to `hset()`\nwhen using the binary connection, so you encode the text values with `getBytes()`. Note\nthat the `predict()` call is in a `try`/`catch` block because it will throw\nexceptions if it can't download the embedding model (you should add code to handle\nthe exceptions in production code).\n\nFoundational: Generate embeddings and store hash documents with vector data using a String/byte[] codec\n\n**Difficulty:** Intermediate\n\n**Available in:** Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]"},{"id":"bulk-import-with-himport","title":"Bulk import with HIMPORT","role":"content","text":"From Redis Open Source 8.10, you can also add the documents with the\n[`HIMPORT`](https://redis.io/docs/latest/commands/himport) command family. This creates the same\nhashes as `hset()`, but lets Redis store them as\n[compact hashes](https://redis.io/docs/latest/develop/data-types/hashes#compact-hashes), which use\nless memory when many keys share the same field names. Redis Search indexes\nthese hashes in the same way.\n\nCreate a `HashImport` object with the field names, then create each document\nwith [`himportSet()`](https://redis.io/docs/latest/commands/himport-set), passing only its values,\nin the same order as the field names. Lettuce declares the field names with\n[`HIMPORT PREPARE`](https://redis.io/docs/latest/commands/himport-prepare) the first time you use\nthe `HashImport` object on a connection. Because the binary connection uses\n`String` keys, `HashImport.of()` takes the field names as strings. Issue all the imports before you wait\nfor any reply, so that Lettuce\n[pipelines](https://redis.io/docs/latest/develop/clients/lettuce/transpipe) them instead of\nwaiting for each reply in turn. With the reactive API, wait for the imports\ninside the `try` block, because a `Mono` that you subscribe to after the\n`HashImport` object closes fails:\n\nBulk ingestion: Import hash documents with vector embeddings as compact hashes using a HashImport fieldset and pipelined HIMPORT SET calls\n\n**Difficulty:** Intermediate\n\n**Builds upon:** add_data\n\n**Available in:** Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n\n\n`himportSet()` replaces any existing key, so running this code after the\n`hset()` calls leaves you with the same three documents.\n\n> [!NOTE]\n> `HIMPORT` requires Lettuce v7.7.0 or later, and Lettuce marks its support as\n> experimental, so the API might change. Redis Cloud and Redis Software don't\n> support `HIMPORT`."},{"id":"run-a-query","title":"Run a query","role":"content","text":"After you have created the index and added the data, you are ready to run a query.\nTo do this, you must create another embedding vector from your chosen query\ntext. Redis calculates the vector distance between the query vector and each\nembedding vector in the index as it runs the query. You can request the results to be\nsorted to rank them in order of ascending distance.\n\nThe code below creates the query embedding using the `predict()` method, as with\nthe indexing, and passes it as a parameter when the query executes (see\n[Vector search](https://redis.io/docs/latest/develop/ai/search-and-query/query/vector-search)\nfor more information about using query parameters with embeddings).\nThe query is a\n[K nearest neighbors (KNN)](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#knn-vector-search)\nsearch that sorts the results in order of vector distance from the query vector.\n\nSemantic search: Execute a KNN vector search with semantic similarity ranking on hash documents\n\n**Difficulty:** Advanced\n\n**Available in:** Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n\n\nAssuming you have added the code from the steps above to your source file,\nit is now ready to run, but note that it may take a while to complete when\nyou run it for the first time (which happens because the model must download the\n`all-MiniLM-L6-v2` model data before it can\ngenerate the embeddings). When you run the code, it outputs the following result text:\n\n[code example]\n\nNote that the results are ordered according to the value of the `distance`\nfield, with the lowest distance indicating the greatest similarity to the query.\nAs you would expect, the result for `doc:1` with the content text\n*\"That is a very happy person\"*\nis the result that is most similar in meaning to the query text\n*\"That is a happy person\"*."},{"id":"differences-with-json-documents","title":"Differences with JSON documents","role":"content","text":"Indexing JSON documents is similar to hash indexing, but there are some\nimportant differences. JSON allows much richer data modeling with nested fields, so\nyou must supply a [path](https://redis.io/docs/latest/develop/data-types/json/path) in the schema\nto identify each field you want to index. However, you can declare a short alias for each\nof these paths (using the `as()` option) to avoid typing it in full for\nevery query. Also, you must specify `CreateArgs.TargetType.JSON` when you create the index.\n\nThe code below shows these differences, but the index is otherwise very similar to\nthe one created previously for hashes:\n\nFoundational: Create a vector search index on JSON documents with JSON paths and field aliases\n\n**Difficulty:** Intermediate\n\n**Available in:** Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n\n\nAn important difference with JSON indexing is that the vectors are\nspecified using arrays of `float` instead of binary strings. This means\nyou don't need to use the binary connection, and you can use\n[`Arrays.toString()`](https://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html#toString-float:A-) to convert the `float` array to a suitable JSON string.\n\nUse [`jsonSet()`](https://redis.io/docs/latest/commands/json.set) to add the data\ninstead of [`hset()`](https://redis.io/docs/latest/commands/hset). Use instances\nof `JSONObject` to supply the data instead of `Map`, as you would for\nhash objects.\n\nFoundational: Store JSON documents with vector embeddings as float arrays using JSON.SET\n\n**Difficulty:** Intermediate\n\n**Available in:** Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n\n\nThe query is almost identical to the one for the hash documents. This\ndemonstrates how the right choice of aliases for the JSON paths can\nsave you having to write complex queries. An important thing to notice\nis that the vector parameter for the query is still specified as a\nbinary string, even though the data for the `embedding` field of the JSON\nwas specified as an array.\n\nSemantic search: Execute a KNN vector search on JSON documents with semantic similarity ranking\n\n**Difficulty:** Advanced\n\n**Available in:** Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n\n\nThe embeddings are generated from the same text as the hash documents, so the\nquery returns the same distances:\n\n[code example]"},{"id":"learn-more","title":"Learn more","role":"related","text":"See\n[Vector search](https://redis.io/docs/latest/develop/ai/search-and-query/query/vector-search)\nfor more information about the indexing options, distance metrics, and query format\nfor vectors."}],"examples":[{"id":"initialize-ex0","language":"xml","code":"<dependency>\n    <groupId>io.lettuce</groupId>\n    <artifactId>lettuce-core</artifactId>\n     <!-- Check for the latest version on Maven Central -->\n    <version>7.7.0.RELEASE</version>\n</dependency>\n\n<dependency>\n    <groupId>ai.djl.huggingface</groupId>\n    <artifactId>tokenizers</artifactId>\n    <version>0.33.0</version>\n</dependency>\n\n<dependency>\n    <groupId>ai.djl.pytorch</groupId>\n    <artifactId>pytorch-model-zoo</artifactId>\n    <version>0.33.0</version>\n</dependency>\n\n<dependency>\n    <groupId>ai.djl</groupId>\n    <artifactId>api</artifactId>\n    <version>0.33.0</version>\n</dependency>","section_id":"initialize"},{"id":"initialize-ex1","language":"bash","code":"compileOnly 'io.lettuce:lettuce-core:7.7.0.RELEASE'\ncompileOnly 'ai.djl.huggingface:tokenizers:0.33.0'\ncompileOnly 'ai.djl.pytorch:pytorch-model-zoo:0.33.0'\ncompileOnly 'ai.djl:api:0.33.0'","section_id":"initialize"},{"id":"import-dependencies-ex0","language":"java","code":"// Lettuce client and Redis Search classes.\nimport io.lettuce.core.*;\nimport io.lettuce.core.api.StatefulRedisConnection;\nimport io.lettuce.core.codec.ByteArrayCodec;\nimport io.lettuce.core.codec.RedisCodec;\nimport io.lettuce.core.codec.StringCodec;\nimport io.lettuce.core.api.async.RedisAsyncCommands;\nimport io.lettuce.core.search.arguments.*;\nimport io.lettuce.core.search.SearchReply;\nimport io.lettuce.core.json.JsonParser;\nimport io.lettuce.core.json.JsonObject;\nimport io.lettuce.core.json.JsonPath;\n\n// Standard library classes for data manipulation and\n// asynchronous programming.\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.nio.charset.StandardCharsets;\nimport java.util.*;\nimport java.util.concurrent.CompletableFuture;\n\n// DJL classes for model loading and inference.\nimport ai.djl.huggingface.translator.TextEmbeddingTranslatorFactory;\nimport ai.djl.inference.Predictor;\nimport ai.djl.repository.zoo.Criteria;\nimport ai.djl.training.util.ProgressBar;","section_id":"import-dependencies"},{"id":"import-dependencies-ex1","language":"java","code":"// Lettuce client and Redis Search classes.\nimport io.lettuce.core.*;\nimport io.lettuce.core.api.StatefulRedisConnection;\nimport io.lettuce.core.codec.ByteArrayCodec;\nimport io.lettuce.core.codec.RedisCodec;\nimport io.lettuce.core.codec.StringCodec;\nimport io.lettuce.core.api.reactive.RedisReactiveCommands;\nimport io.lettuce.core.search.arguments.*;\nimport io.lettuce.core.search.SearchReply;\nimport io.lettuce.core.json.JsonParser;\nimport io.lettuce.core.json.JsonObject;\nimport io.lettuce.core.json.JsonPath;\n\n// Standard library classes for data manipulation and\n// reactive programming.\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.nio.charset.StandardCharsets;\nimport java.util.*;\nimport reactor.core.publisher.Mono;\n\n// DJL classes for model loading and inference.\nimport ai.djl.huggingface.translator.TextEmbeddingTranslatorFactory;\nimport ai.djl.inference.Predictor;\nimport ai.djl.repository.zoo.Criteria;\nimport ai.djl.training.util.ProgressBar;","section_id":"import-dependencies"},{"id":"define-a-helper-method-ex0","language":"java","code":"private byte[] floatArrayToByteArray(float[] vector) {\n        ByteBuffer buffer = ByteBuffer.allocate(vector.length * 4).order(ByteOrder.LITTLE_ENDIAN);\n        for (float value : vector) {\n            buffer.putFloat(value);\n        }\n        return buffer.array();\n    }","section_id":"define-a-helper-method"},{"id":"define-a-helper-method-ex1","language":"java","code":"private byte[] floatArrayToByteArray(float[] vector) {\n        ByteBuffer buffer = ByteBuffer.allocate(vector.length * 4).order(ByteOrder.LITTLE_ENDIAN);\n        for (float value : vector) {\n            buffer.putFloat(value);\n        }\n        return buffer.array();\n    }","section_id":"define-a-helper-method"},{"id":"create-an-embedding-model-instance-ex0","language":"java","code":"Predictor<String, float[]> predictor = null;\n\n        try {\n            Criteria<String, float[]> criteria = Criteria.builder().setTypes(String.class, float[].class)\n                    .optModelUrls(\"djl://ai.djl.huggingface.pytorch/sentence-transformers/all-MiniLM-L6-v2\")\n                    .optEngine(\"PyTorch\").optTranslatorFactory(new TextEmbeddingTranslatorFactory())\n                    .optProgress(new ProgressBar()).build();\n\n            predictor = criteria.loadModel().newPredictor();\n        } catch (Exception e) {\n            // ...\n        }","section_id":"create-an-embedding-model-instance"},{"id":"create-an-embedding-model-instance-ex1","language":"java","code":"Predictor<String, float[]> predictor = null;\n\n        try {\n            Criteria<String, float[]> criteria = Criteria.builder().setTypes(String.class, float[].class)\n                    .optModelUrls(\"djl://ai.djl.huggingface.pytorch/sentence-transformers/all-MiniLM-L6-v2\")\n                    .optEngine(\"PyTorch\").optTranslatorFactory(new TextEmbeddingTranslatorFactory())\n                    .optProgress(new ProgressBar()).build();\n\n            predictor = criteria.loadModel().newPredictor();\n        } catch (Exception e) {\n            // ...\n        }","section_id":"create-an-embedding-model-instance"},{"id":"create-the-index-ex0","language":"java","code":"RedisClient redisClient = RedisClient.create(\"redis://localhost:6379\");\n\n        try (StatefulRedisConnection<String, String> connection = redisClient.connect();\n                StatefulRedisConnection<String, byte[]> binConnection = redisClient\n                        .connect(RedisCodec.of(StringCodec.UTF8, ByteArrayCodec.INSTANCE))) {\n            RedisAsyncCommands<String, String> asyncCommands = connection.async();\n            RedisAsyncCommands<String, byte[]> binAsyncCommands = binConnection.async();\n            // ...","section_id":"create-the-index"},{"id":"create-the-index-ex1","language":"java","code":"RedisClient redisClient = RedisClient.create(\"redis://localhost:6379\");\n\n        try (StatefulRedisConnection<String, String> connection = redisClient.connect();\n                StatefulRedisConnection<String, byte[]> binConnection = redisClient\n                        .connect(RedisCodec.of(StringCodec.UTF8, ByteArrayCodec.INSTANCE))) {\n            RedisReactiveCommands<String, String> reactiveCommands = connection.reactive();\n            RedisReactiveCommands<String, byte[]> binReactiveCommands = binConnection.reactive();\n            // ...","section_id":"create-the-index"},{"id":"create-the-index-ex2","language":"java","code":"List<FieldArgs<String>> schema = Arrays.asList(TextFieldArgs.<String> builder().name(\"content\").build(),\n                    TagFieldArgs.<String> builder().name(\"genre\").build(),\n                    VectorFieldArgs.<String> builder().name(\"embedding\").hnsw().type(VectorFieldArgs.VectorType.FLOAT32)\n                            .dimensions(384).distanceMetric(VectorFieldArgs.DistanceMetric.L2).build());\n\n            CreateArgs<String, String> createArgs = CreateArgs.<String, String> builder().on(CreateArgs.TargetType.HASH)\n                    .withPrefix(\"doc:\").build();\n\n            CompletableFuture<Void> createIndex = asyncCommands.ftCreate(\"vector_idx\", createArgs, schema)\n                    .thenAccept(System.out::println).toCompletableFuture();","section_id":"create-the-index"},{"id":"create-the-index-ex3","language":"java","code":"List<FieldArgs<String>> schema = Arrays.asList(TextFieldArgs.<String> builder().name(\"content\").build(),\n                    TagFieldArgs.<String> builder().name(\"genre\").build(),\n                    VectorFieldArgs.<String> builder().name(\"embedding\").hnsw().type(VectorFieldArgs.VectorType.FLOAT32)\n                            .dimensions(384).distanceMetric(VectorFieldArgs.DistanceMetric.L2).build());\n\n            CreateArgs<String, String> createArgs = CreateArgs.<String, String> builder().on(CreateArgs.TargetType.HASH)\n                    .withPrefix(\"doc:\").build();\n\n            Mono<Void> createIndex = reactiveCommands.ftCreate(\"vector_idx\", createArgs, schema).doOnNext(result -> {\n                System.out.println(result); // >>> OK\n            }).then();","section_id":"create-the-index"},{"id":"add-data-ex0","language":"java","code":"String sentence1 = \"That is a very happy person\";\n\n            Map<String, byte[]> doc1 = new HashMap<>();\n            doc1.put(\"content\", sentence1.getBytes(StandardCharsets.UTF_8));\n            doc1.put(\"genre\", \"persons\".getBytes(StandardCharsets.UTF_8));\n\n            try {\n                doc1.put(\"embedding\", floatArrayToByteArray(predictor.predict(sentence1)));\n            } catch (Exception e) {\n                // ...\n            }\n\n            CompletableFuture<Long> addDoc1 = binAsyncCommands.hset(\"doc:1\", doc1)\n                    .thenApply(result -> {\n                        System.out.println(result); // >>> 3\n                        return result;\n                    }).toCompletableFuture();\n\n            String sentence2 = \"That is a happy dog\";\n\n            Map<String, byte[]> doc2 = new HashMap<>();\n            doc2.put(\"content\", sentence2.getBytes(StandardCharsets.UTF_8));\n            doc2.put(\"genre\", \"pets\".getBytes(StandardCharsets.UTF_8));\n\n            try {\n                doc2.put(\"embedding\", floatArrayToByteArray(predictor.predict(sentence2)));\n            } catch (Exception e) {\n                // ...\n            }\n\n            CompletableFuture<Long> addDoc2 = binAsyncCommands.hset(\"doc:2\", doc2)\n                    .thenApply(result -> {\n                        System.out.println(result); // >>> 3\n                        return result;\n                    }).toCompletableFuture();\n\n            String sentence3 = \"Today is a sunny day\";\n\n            Map<String, byte[]> doc3 = new HashMap<>();\n            doc3.put(\"content\", sentence3.getBytes(StandardCharsets.UTF_8));\n            doc3.put(\"genre\", \"weather\".getBytes(StandardCharsets.UTF_8));\n\n            try {\n                doc3.put(\"embedding\", floatArrayToByteArray(predictor.predict(sentence3)));\n            } catch (Exception e) {\n                // ...\n            }\n\n            CompletableFuture<Long> addDoc3 = binAsyncCommands.hset(\"doc:3\", doc3)\n                    .thenApply(result -> {\n                        System.out.println(result); // >>> 3\n                        return result;\n                    }).toCompletableFuture();","section_id":"add-data"},{"id":"add-data-ex1","language":"java","code":"String sentence1 = \"That is a very happy person\";\n\n            Map<String, byte[]> doc1 = new HashMap<>();\n            doc1.put(\"content\", sentence1.getBytes(StandardCharsets.UTF_8));\n            doc1.put(\"genre\", \"persons\".getBytes(StandardCharsets.UTF_8));\n\n            try {\n                doc1.put(\"embedding\", floatArrayToByteArray(predictor.predict(sentence1)));\n            } catch (Exception e) {\n                // ...\n            }\n\n            Mono<Long> addDoc1 = binReactiveCommands.hset(\"doc:1\", doc1).doOnNext(result -> {\n                System.out.println(result); // >>> 3\n            });\n\n            String sentence2 = \"That is a happy dog\";\n\n            Map<String, byte[]> doc2 = new HashMap<>();\n            doc2.put(\"content\", sentence2.getBytes(StandardCharsets.UTF_8));\n            doc2.put(\"genre\", \"pets\".getBytes(StandardCharsets.UTF_8));\n\n            try {\n                doc2.put(\"embedding\", floatArrayToByteArray(predictor.predict(sentence2)));\n            } catch (Exception e) {\n                // ...\n            }\n\n            Mono<Long> addDoc2 = binReactiveCommands.hset(\"doc:2\", doc2).doOnNext(result -> {\n                System.out.println(result); // >>> 3\n            });\n\n            String sentence3 = \"Today is a sunny day\";\n\n            Map<String, byte[]> doc3 = new HashMap<>();\n            doc3.put(\"content\", sentence3.getBytes(StandardCharsets.UTF_8));\n            doc3.put(\"genre\", \"weather\".getBytes(StandardCharsets.UTF_8));\n\n            try {\n                doc3.put(\"embedding\", floatArrayToByteArray(predictor.predict(sentence3)));\n            } catch (Exception e) {\n                // ...\n            }\n\n            Mono<Long> addDoc3 = binReactiveCommands.hset(\"doc:3\", doc3).doOnNext(result -> {\n                System.out.println(result); // >>> 3\n            });","section_id":"add-data"},{"id":"bulk-import-with-himport-ex0","language":"java","code":"String[][] docData = {\n                    { \"doc:1\", \"That is a very happy person\", \"persons\" },\n                    { \"doc:2\", \"That is a happy dog\", \"pets\" },\n                    { \"doc:3\", \"Today is a sunny day\", \"weather\" } };\n\n            List<RedisFuture<String>> himportDocs = new ArrayList<>();\n\n            try (HashImport<String> fieldset = HashImport.of(\"content\", \"genre\", \"embedding\")) {\n                for (String[] doc : docData) {\n                    byte[] docEmbedding = null;\n\n                    try {\n                        docEmbedding = floatArrayToByteArray(predictor.predict(doc[1]));\n                    } catch (Exception e) {\n                        // ...\n                    }\n\n                    himportDocs.add(binAsyncCommands.himportSet(doc[0], fieldset, doc[1].getBytes(StandardCharsets.UTF_8),\n                            doc[2].getBytes(StandardCharsets.UTF_8), docEmbedding));\n                }\n            }\n\n            CompletableFuture<Void> importDocs = CompletableFuture\n                    .allOf(himportDocs.stream().map(RedisFuture::toCompletableFuture).toArray(CompletableFuture[]::new));","section_id":"bulk-import-with-himport"},{"id":"bulk-import-with-himport-ex1","language":"java","code":"String[][] docData = {\n                    { \"doc:1\", \"That is a very happy person\", \"persons\" },\n                    { \"doc:2\", \"That is a happy dog\", \"pets\" },\n                    { \"doc:3\", \"Today is a sunny day\", \"weather\" } };\n\n            List<Mono<String>> himportDocs = new ArrayList<>();\n\n            try (HashImport<String> fieldset = HashImport.of(\"content\", \"genre\", \"embedding\")) {\n                for (String[] doc : docData) {\n                    byte[] docEmbedding = null;\n\n                    try {\n                        docEmbedding = floatArrayToByteArray(predictor.predict(doc[1]));\n                    } catch (Exception e) {\n                        // ...\n                    }\n\n                    himportDocs.add(binReactiveCommands.himportSet(doc[0], fieldset, doc[1].getBytes(StandardCharsets.UTF_8),\n                            doc[2].getBytes(StandardCharsets.UTF_8), docEmbedding));\n                }\n\n                // Subscribe before the fieldset closes.\n                Mono.when(himportDocs).block();\n            }","section_id":"bulk-import-with-himport"},{"id":"run-a-query-ex0","language":"java","code":"String query = \"That is a happy person\";\n            float[] queryEmbedding = null;\n\n            try {\n                queryEmbedding = predictor.predict(query);\n            } catch (Exception e) {\n                // ...\n            }\n\n            SearchArgs<String, byte[]> searchArgs = SearchArgs.<String, byte[]> builder()\n                    .param(\"vec\", floatArrayToByteArray(queryEmbedding)).returnField(\"content\").returnField(\"vector_distance\")\n                    .sortBy(SortByArgs.<String> builder().attribute(\"vector_distance\").build()).build();\n\n            CompletableFuture<SearchReply<String, byte[]>> hashQuery = binAsyncCommands\n                    .ftSearch(\"vector_idx\", \"*=>[KNN 3 @embedding $vec AS vector_distance]\".getBytes(StandardCharsets.UTF_8),\n                            searchArgs)\n                    .thenApply(result -> {\n                        List<SearchReply.SearchResult<String, byte[]>> results = result.getResults();\n\n                        results.forEach(r -> {\n                            String content = new String(r.getFields().get(\"content\"), StandardCharsets.UTF_8);\n                            String distance = new String(r.getFields().get(\"vector_distance\"), StandardCharsets.UTF_8);\n\n                            System.out.println(\"ID: \" + r.getId() + \", Content: \" + content + \", Distance: \" + distance);\n                        });\n                        // >>> ID: doc:1, Content: That is a very happy person, Distance: 0.114169836044\n                        // >>> ID: doc:2, Content: That is a happy dog, Distance: 0.610845506191\n                        // >>> ID: doc:3, Content: Today is a sunny day, Distance: 1.48624765873\n\n                        return result;\n                    }).toCompletableFuture();","section_id":"run-a-query"},{"id":"run-a-query-ex1","language":"java","code":"String query = \"That is a happy person\";\n            float[] queryEmbedding = null;\n\n            try {\n                queryEmbedding = predictor.predict(query);\n            } catch (Exception e) {\n                // ...\n            }\n\n            SearchArgs<String, byte[]> searchArgs = SearchArgs.<String, byte[]> builder()\n                    .param(\"vec\", floatArrayToByteArray(queryEmbedding)).returnField(\"content\").returnField(\"vector_distance\")\n                    .sortBy(SortByArgs.<String> builder().attribute(\"vector_distance\").build()).build();\n\n            Mono<SearchReply<String, byte[]>> hashQuery = binReactiveCommands\n                    .ftSearch(\"vector_idx\", \"*=>[KNN 3 @embedding $vec AS vector_distance]\".getBytes(StandardCharsets.UTF_8),\n                            searchArgs)\n                    .doOnNext(result -> {\n                        List<SearchReply.SearchResult<String, byte[]>> results = result.getResults();\n\n                        results.forEach(r -> {\n                            String content = new String(r.getFields().get(\"content\"), StandardCharsets.UTF_8);\n                            String distance = new String(r.getFields().get(\"vector_distance\"), StandardCharsets.UTF_8);\n\n                            System.out.println(\"ID: \" + r.getId() + \", Content: \" + content + \", Distance: \" + distance);\n                        });\n                        // >>> ID: doc:1, Content: That is a very happy person, Distance: 0.114169836044\n                        // >>> ID: doc:2, Content: That is a happy dog, Distance: 0.610845506191\n                        // >>> ID: doc:3, Content: Today is a sunny day, Distance: 1.48624765873\n                    });","section_id":"run-a-query"},{"id":"run-a-query-ex2","language":"plaintext","code":"ID: doc:1, Content: That is a very happy person, Distance: 0.114169836044\nID: doc:2, Content: That is a happy dog, Distance: 0.610845506191\nID: doc:3, Content: Today is a sunny day, Distance: 1.48624765873","section_id":"run-a-query"},{"id":"differences-with-json-documents-ex0","language":"java","code":"List<FieldArgs<String>> jsonSchema = Arrays.asList(\n                    TextFieldArgs.<String> builder().name(\"$.content\").as(\"content\").build(),\n                    TagFieldArgs.<String> builder().name(\"$.genre\").as(\"genre\").build(),\n                    VectorFieldArgs.<String> builder().name(\"$.embedding\").as(\"embedding\").hnsw()\n                            .type(VectorFieldArgs.VectorType.FLOAT32).dimensions(384)\n                            .distanceMetric(VectorFieldArgs.DistanceMetric.L2).build());\n\n            CreateArgs<String, String> jsonCreateArgs = CreateArgs.<String, String> builder().on(CreateArgs.TargetType.JSON)\n                    .withPrefix(\"jdoc:\").build();\n\n            CompletableFuture<Void> jsonCreateIndex = asyncCommands.ftCreate(\"vector_json_idx\", jsonCreateArgs, jsonSchema)\n                    .thenAccept(System.out::println).toCompletableFuture();","section_id":"differences-with-json-documents"},{"id":"differences-with-json-documents-ex1","language":"java","code":"List<FieldArgs<String>> jsonSchema = Arrays.asList(\n                    TextFieldArgs.<String> builder().name(\"$.content\").as(\"content\").build(),\n                    TagFieldArgs.<String> builder().name(\"$.genre\").as(\"genre\").build(),\n                    VectorFieldArgs.<String> builder().name(\"$.embedding\").as(\"embedding\").hnsw()\n                            .type(VectorFieldArgs.VectorType.FLOAT32).dimensions(384)\n                            .distanceMetric(VectorFieldArgs.DistanceMetric.L2).build());\n\n            CreateArgs<String, String> jsonCreateArgs = CreateArgs.<String, String> builder().on(CreateArgs.TargetType.JSON)\n                    .withPrefix(\"jdoc:\").build();\n\n            Mono<Void> jsonCreateIndex = reactiveCommands.ftCreate(\"vector_json_idx\", jsonCreateArgs, jsonSchema)\n                    .doOnNext(result -> {\n                        System.out.println(result); // >>> OK\n                    }).then();","section_id":"differences-with-json-documents"},{"id":"differences-with-json-documents-ex2","language":"java","code":"JsonParser parser = asyncCommands.getJsonParser();\n\n            String jSentence1 = \"That is a very happy person\";\n\n            JsonObject jDoc1 = parser.createJsonObject();\n            jDoc1.put(\"content\", parser.createJsonValue(\"\\\"\" + jSentence1 + \"\\\"\"));\n            jDoc1.put(\"genre\", parser.createJsonValue(\"\\\"persons\\\"\"));\n\n            try {\n                jDoc1.put(\"embedding\", parser.createJsonValue(Arrays.toString(predictor.predict(jSentence1))));\n            } catch (Exception e) {\n                // ...\n            }\n\n            CompletableFuture<String> jsonAddDoc1 = asyncCommands.jsonSet(\"jdoc:1\", JsonPath.ROOT_PATH, jDoc1)\n                    .thenApply(result -> {\n                        System.out.println(result); // >>> OK\n                        return result;\n                    }).toCompletableFuture();\n\n            String jSentence2 = \"That is a happy dog\";\n\n            JsonObject jDoc2 = parser.createJsonObject();\n            jDoc2.put(\"content\", parser.createJsonValue(\"\\\"\" + jSentence2 + \"\\\"\"));\n            jDoc2.put(\"genre\", parser.createJsonValue(\"\\\"pets\\\"\"));\n\n            try {\n                jDoc2.put(\"embedding\", parser.createJsonValue(Arrays.toString(predictor.predict(jSentence2))));\n            } catch (Exception e) {\n                // ...\n            }\n\n            CompletableFuture<String> jsonAddDoc2 = asyncCommands.jsonSet(\"jdoc:2\", JsonPath.ROOT_PATH, jDoc2)\n                    .thenApply(result -> {\n                        System.out.println(result); // >>> OK\n                        return result;\n                    }).toCompletableFuture();\n\n            String jSentence3 = \"Today is a sunny day\";\n\n            JsonObject jDoc3 = parser.createJsonObject();\n            jDoc3.put(\"content\", parser.createJsonValue(\"\\\"\" + jSentence3 + \"\\\"\"));\n            jDoc3.put(\"genre\", parser.createJsonValue(\"\\\"weather\\\"\"));\n\n            try {\n                jDoc3.put(\"embedding\", parser.createJsonValue(Arrays.toString(predictor.predict(jSentence3))));\n            } catch (Exception e) {\n                // ...\n            }\n\n            CompletableFuture<String> jsonAddDoc3 = asyncCommands.jsonSet(\"jdoc:3\", JsonPath.ROOT_PATH, jDoc3)\n                    .thenApply(result -> {\n                        System.out.println(result); // >>> OK\n                        return result;\n                    }).toCompletableFuture();","section_id":"differences-with-json-documents"},{"id":"differences-with-json-documents-ex3","language":"java","code":"JsonParser parser = reactiveCommands.getJsonParser();\n\n            String jSentence1 = \"That is a very happy person\";\n            JsonObject jDoc1 = parser.createJsonObject();\n            jDoc1.put(\"content\", parser.createJsonValue(\"\\\"\" + jSentence1 + \"\\\"\"));\n            jDoc1.put(\"genre\", parser.createJsonValue(\"\\\"persons\\\"\"));\n\n            try {\n                jDoc1.put(\"embedding\", parser.createJsonValue(Arrays.toString(predictor.predict(jSentence1))));\n            } catch (Exception e) {\n                // ...\n            }\n\n            Mono<Void> jsonAddDoc1 = reactiveCommands.jsonSet(\"jdoc:1\", JsonPath.ROOT_PATH, jDoc1).doOnNext(result -> {\n                System.out.println(result); // >>> OK\n            }).then();\n\n            String jSentence2 = \"That is a happy dog\";\n            JsonObject jDoc2 = parser.createJsonObject();\n            jDoc2.put(\"content\", parser.createJsonValue(\"\\\"\" + jSentence2 + \"\\\"\"));\n            jDoc2.put(\"genre\", parser.createJsonValue(\"\\\"pets\\\"\"));\n\n            try {\n                jDoc2.put(\"embedding\", parser.createJsonValue(Arrays.toString(predictor.predict(jSentence2))));\n            } catch (Exception e) {\n                // ...\n            }\n\n            Mono<Void> jsonAddDoc2 = reactiveCommands.jsonSet(\"jdoc:2\", JsonPath.ROOT_PATH, jDoc2).doOnNext(result -> {\n                System.out.println(result); // >>> OK\n            }).then();\n\n            String jSentence3 = \"Today is a sunny day\";\n            JsonObject jDoc3 = parser.createJsonObject();\n            jDoc3.put(\"content\", parser.createJsonValue(\"\\\"\" + jSentence3 + \"\\\"\"));\n            jDoc3.put(\"genre\", parser.createJsonValue(\"\\\"weather\\\"\"));\n\n            try {\n                jDoc3.put(\"embedding\", parser.createJsonValue(Arrays.toString(predictor.predict(jSentence3))));\n            } catch (Exception e) {\n                // ...\n            }\n\n            Mono<Void> jsonAddDoc3 = reactiveCommands.jsonSet(\"jdoc:3\", JsonPath.ROOT_PATH, jDoc3).doOnNext(result -> {\n                System.out.println(result); // >>> OK\n            }).then();","section_id":"differences-with-json-documents"},{"id":"differences-with-json-documents-ex4","language":"java","code":"String jQuery = \"That is a happy person\";\n            float[] jsonQueryEmbedding = null;\n\n            try {\n                jsonQueryEmbedding = predictor.predict(jQuery);\n            } catch (Exception e) {\n                // ...\n            }\n\n            SearchArgs<String, byte[]> jsonSearchArgs = SearchArgs.<String, byte[]> builder()\n                    .param(\"vec\", floatArrayToByteArray(jsonQueryEmbedding)).returnField(\"content\").returnField(\"vector_distance\")\n                    .sortBy(SortByArgs.<String> builder().attribute(\"vector_distance\").build()).build();\n\n            CompletableFuture<SearchReply<String, byte[]>> jsonQuery = binAsyncCommands\n                    .ftSearch(\"vector_json_idx\", \"*=>[KNN 3 @embedding $vec AS vector_distance]\".getBytes(StandardCharsets.UTF_8),\n                            jsonSearchArgs)\n                    .thenApply(result -> {\n                        List<SearchReply.SearchResult<String, byte[]>> results = result.getResults();\n\n                        results.forEach(r -> {\n                            String content = new String(r.getFields().get(\"content\"), StandardCharsets.UTF_8);\n                            String distance = new String(r.getFields().get(\"vector_distance\"), StandardCharsets.UTF_8);\n\n                            System.out.println(\"ID: \" + r.getId() + \", Content: \" + content + \", Distance: \" + distance);\n                        });\n                        // >>> ID: jdoc:1, Content: That is a very happy person, Distance: 0.114169836044\n                        // >>> ID: jdoc:2, Content: That is a happy dog, Distance: 0.610845506191\n                        // >>> ID: jdoc:3, Content: Today is a sunny day, Distance: 1.48624765873\n\n                        return result;\n                    }).toCompletableFuture();","section_id":"differences-with-json-documents"},{"id":"differences-with-json-documents-ex5","language":"java","code":"String jQuery = \"That is a happy person\";\n            float[] jsonQueryEmbedding = null;\n\n            try {\n                jsonQueryEmbedding = predictor.predict(jQuery);\n            } catch (Exception e) {\n                // ...\n            }\n\n            SearchArgs<String, byte[]> jsonSearchArgs = SearchArgs.<String, byte[]> builder()\n                    .param(\"vec\", floatArrayToByteArray(jsonQueryEmbedding)).returnField(\"content\").returnField(\"vector_distance\")\n                    .sortBy(SortByArgs.<String> builder().attribute(\"vector_distance\").build()).build();\n\n            Mono<SearchReply<String, byte[]>> jsonQuery = binReactiveCommands\n                    .ftSearch(\"vector_json_idx\", \"*=>[KNN 3 @embedding $vec AS vector_distance]\".getBytes(StandardCharsets.UTF_8),\n                            jsonSearchArgs)\n                    .doOnNext(result -> {\n                        List<SearchReply.SearchResult<String, byte[]>> results = result.getResults();\n\n                        results.forEach(r -> {\n                            String content = new String(r.getFields().get(\"content\"), StandardCharsets.UTF_8);\n                            String distance = new String(r.getFields().get(\"vector_distance\"), StandardCharsets.UTF_8);\n\n                            System.out.println(\"ID: \" + r.getId() + \", Content: \" + content + \", Distance: \" + distance);\n                        });\n                        // >>> ID: jdoc:1, Content: That is a very happy person, Distance: 0.114169836044\n                        // >>> ID: jdoc:2, Content: That is a happy dog, Distance: 0.610845506191\n                        // >>> ID: jdoc:3, Content: Today is a sunny day, Distance: 1.48624765873\n                    });","section_id":"differences-with-json-documents"},{"id":"differences-with-json-documents-ex6","language":"plaintext","code":"ID: jdoc:1, Content: That is a very happy person, Distance: 0.114169836044\nID: jdoc:2, Content: That is a happy dog, Distance: 0.610845506191\nID: jdoc:3, Content: Today is a sunny day, Distance: 1.48624765873","section_id":"differences-with-json-documents"}]}
{"schema_version":2,"id":"develop/clients/lettuce/vecsets","title":"Vector set embeddings","url":"https://redis.io/docs/latest/develop/clients/lettuce/vecsets/","summary":"Index and query embeddings with Redis vector sets","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-09T09:47:45+01:00","page_type":"content","content_hash":"f9b5b4fdf2ede598180982309187bb90a78456874e3ab39c3f7a0ebbfdb9bc75","sections":[{"id":"initialize","title":"Initialize","role":"content","text":"If you are using [Maven](https://maven.apache.org/), add the following\ndependencies to your `pom.xml` file\n(note that you need `Lettuce` v6.8.0 or later to use vector sets):\n\n[code example]\n\nIf you are using [Gradle](https://gradle.org/), add the following\ndependencies to your `build.gradle` file:\n\n[code example]\n\nIn a new Java file, import the required classes:\n\nFoundational: Import Lettuce, DJL, and embedding model libraries for vector set operations\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n\n\nThe imports include the classes required to generate embeddings from text.\nThis example uses an instance of the `Predictor` class with the\n[`all-MiniLM-L6-v2`](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2)\nmodel for the embeddings. This model generates vectors with 384 dimensions, regardless\nof the length of the input text, but note that the input is truncated to 256\ntokens (see\n[Word piece tokenization](https://huggingface.co/learn/nlp-course/en/chapter6/6)\nat the [Hugging Face](https://huggingface.co/) docs to learn more about the way tokens\nare related to the original text).\n\nPractical pattern: Initialize a DJL Predictor with the all-MiniLM-L6-v2 embedding model\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]"},{"id":"create-the-data","title":"Create the data","role":"content","text":"The example data is contained in a `List<Person>` object with some brief\ndescriptions of famous people.\n\nFoundational: Define sample data with person objects containing names, descriptions, and biographical information\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]"},{"id":"add-the-data-to-a-vector-set","title":"Add the data to a vector set","role":"content","text":"The next step is to connect to Redis and add the data to a new vector set.\n\nThe `predictor.predict()` method that generates the embeddings returns a `float[]` array.\nThe `vadd()` method that adds the embeddings to the vector set accepts a `Double[]` array,\nso it is useful to define a helper method to perform the conversion:\n\nFoundational: Convert float arrays to Double arrays for storing embeddings in vector sets\n\n**Difficulty:** Beginner\n\n**Available in:** Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n\n\nThe code below connects to Redis, then iterates through all the items in the `people` list,\ngenerates embeddings for each person's description, and then\nadds the appropriate elements to a vector set called `famousPeople`.\nNote that the `predict()` call is in a `try`/`catch` block because it can throw\nexceptions if it can't download the embedding model (you should add code to handle\nthe exceptions for production).\n\nThe call to `vadd()` also adds the `born` and `died` values from the\noriginal `people` list as attribute data. You can access this during a query\nor by using the [`vgetattr()`](https://redis.io/docs/latest/commands/vgetattr) method.\n\nFoundational: Generate embeddings and add vector data with attributes to a vector set\n\n**Difficulty:** Intermediate\n\n**Available in:** Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]"},{"id":"query-the-vector-set","title":"Query the vector set","role":"content","text":"You can now query the data in the set. The basic approach is to use the\n`predict()` method to generate another embedding vector for the query text.\n(This is the same method used to add the elements to the set.) Then, pass\nthe query vector to [`vsim()`](https://redis.io/docs/latest/commands/vsim) to return elements\nof the set, ranked in order of similarity to the query.\n\nStart with a simple query for \"actors\":\n\nSemantic search: Execute a semantic similarity search on a vector set using text embeddings\n\n**Difficulty:** Intermediate\n\n**Available in:** Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n\n\nThis returns the following list of elements (formatted slightly for clarity):\n\n[code example]\n\nThe first two people in the list are the two actors, as expected, but none of the\npeople from Linus Pauling onward was especially well-known for acting (and there certainly\nisn't any information about that in the short description text).\nAs it stands, the search attempts to rank all the elements in the set, based\non the information contained in the embedding model.\nYou can use the `count` parameter of `vsim()` to limit the list of elements\nto just the most relevant few items:\n\nLimit results: Limit vector search results to the top K most similar items using the count parameter\n\n**Difficulty:** Intermediate\n\n**Available in:** Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n\n\nThe reason for using text embeddings rather than simple text search\nis that the embeddings represent semantic information. This allows a query\nto find elements with a similar meaning even if the text is\ndifferent. For example, the word \"entertainer\" doesn't appear in any of the\ndescriptions, but if you use it as a query, the actors and musicians are ranked\nhighest in the results list:\n\nSemantic search: Leverage text embeddings to find semantically similar items even when exact keywords don't match\n\n**Difficulty:** Intermediate\n\n**Available in:** Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n\n\nSimilarly, if you use \"science\" as a query, you get the following results:\n\n[code example]\n\nThe scientists are ranked highest, followed by the\nmathematicians. This ranking seems reasonable given the connection between mathematics and science.\n\nYou can also use\n[filter expressions](https://redis.io/docs/latest/develop/data-types/vector-sets/filtered-search)\nwith `vsim()` to restrict the search further. For example,\nrepeat the \"science\" query, but this time limit the results to people\nwho died before the year 2000:\n\nFiltered search: Combine vector similarity search with attribute filtering to narrow results based on metadata\n\n**Difficulty:** Advanced\n\n**Available in:** Java (Asynchronous - Lettuce), Java (Reactive - Lettuce)\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n\n\nNote that the boolean filter expression is applied to items in the list\nbefore the vector distance calculation is performed. Items that don't\npass the filter test are removed from the results completely, rather\nthan just reduced in rank. This can help to improve the performance of the\nsearch because there is no need to calculate the vector distance for\nelements that have already been filtered out of the search."},{"id":"more-information","title":"More information","role":"content","text":"See the [vector sets](https://redis.io/docs/latest/develop/data-types/vector-sets)\ndocs for more information and code examples. See the\n[Redis for AI](https://redis.io/docs/latest/develop/ai) section for more details\nabout text embeddings and other AI techniques you can use with Redis.\n\nYou may also be interested in\n[vector search](https://redis.io/docs/latest/develop/clients/lettuce/vecsearch).\nThis is a feature of\n[Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query)\nthat lets you retrieve\n[JSON](https://redis.io/docs/latest/develop/data-types/json) and\n[hash](https://redis.io/docs/latest/develop/data-types/hashes) documents based on\nvector data stored in their fields."}],"examples":[{"id":"initialize-ex0","language":"xml","code":"<dependency>\n    <groupId>io.lettuce</groupId>\n    <artifactId>lettuce-core</artifactId>\n    <version>6.8.0.RELEASE</version>\n</dependency>\n\n<dependency>\n    <groupId>ai.djl.huggingface</groupId>\n    <artifactId>tokenizers</artifactId>\n    <version>0.33.0</version>\n</dependency>\n\n<dependency>\n    <groupId>ai.djl.pytorch</groupId>\n    <artifactId>pytorch-model-zoo</artifactId>\n    <version>0.33.0</version>\n</dependency>\n\n<dependency>\n    <groupId>ai.djl</groupId>\n    <artifactId>api</artifactId>\n    <version>0.33.0</version>\n</dependency>","section_id":"initialize"},{"id":"initialize-ex1","language":"bash","code":"compileOnly 'io.lettuce:lettuce-core:6.8.0.RELEASE'\ncompileOnly 'ai.djl.huggingface:tokenizers:0.33.0'\ncompileOnly 'ai.djl.pytorch:pytorch-model-zoo:0.33.0'\ncompileOnly 'ai.djl:api:0.33.0'","section_id":"initialize"},{"id":"initialize-ex2","language":"java","code":"// Lettuce client and Redis Search classes.\nimport io.lettuce.core.*;\nimport io.lettuce.core.api.StatefulRedisConnection;\nimport io.lettuce.core.api.async.RedisAsyncCommands;\n\n// Standard library classes for data manipulation and\n// asynchronous programming.\nimport java.util.*;\nimport java.util.concurrent.CompletableFuture;\n\n// DJL classes for model loading and inference.\nimport ai.djl.huggingface.translator.TextEmbeddingTranslatorFactory;\nimport ai.djl.inference.Predictor;\nimport ai.djl.repository.zoo.Criteria;\nimport ai.djl.training.util.ProgressBar;","section_id":"initialize"},{"id":"initialize-ex3","language":"java","code":"// Lettuce client and Redis Search classes.\nimport io.lettuce.core.*;\nimport io.lettuce.core.api.StatefulRedisConnection;\nimport io.lettuce.core.api.reactive.RedisReactiveCommands;\n\n// Standard library classes for data manipulation and\n// reactive programming.\nimport java.util.*;\nimport reactor.core.publisher.Mono;\n\n// DJL classes for model loading and inference.\nimport ai.djl.huggingface.translator.TextEmbeddingTranslatorFactory;\nimport ai.djl.inference.Predictor;\nimport ai.djl.repository.zoo.Criteria;\nimport ai.djl.training.util.ProgressBar;","section_id":"initialize"},{"id":"initialize-ex4","language":"java","code":"Predictor<String, float[]> predictor = null;\n\n        try {\n            Criteria<String, float[]> criteria = Criteria.builder().setTypes(String.class, float[].class)\n                    .optModelUrls(\"djl://ai.djl.huggingface.pytorch/sentence-transformers/all-MiniLM-L6-v2\")\n                    .optEngine(\"PyTorch\").optTranslatorFactory(new TextEmbeddingTranslatorFactory())\n                    .optProgress(new ProgressBar()).build();\n\n            predictor = criteria.loadModel().newPredictor();\n        } catch (Exception e) {\n            // ...\n        }","section_id":"initialize"},{"id":"initialize-ex5","language":"java","code":"Predictor<String, float[]> predictor = null;\n\n        try {\n            Criteria<String, float[]> criteria = Criteria.builder().setTypes(String.class, float[].class)\n                    .optModelUrls(\"djl://ai.djl.huggingface.pytorch/sentence-transformers/all-MiniLM-L6-v2\")\n                    .optEngine(\"PyTorch\").optTranslatorFactory(new TextEmbeddingTranslatorFactory())\n                    .optProgress(new ProgressBar()).build();\n\n            predictor = criteria.loadModel().newPredictor();\n        } catch (Exception e) {\n            // ...\n        }","section_id":"initialize"},{"id":"create-the-data-ex0","language":"java","code":"final class Person {\n            final String name;\n            final int born;\n            final int died;\n            final String description;\n            Person(String name, int born, int died, String description) {\n                this.name = name; this.born = born; this.died = died; this.description = description;\n            }\n        }\n\n        List<Person> people = Arrays.asList(\n            new Person(\n                \"Marie Curie\",\n                1867, 1934,\n                \"Polish-French chemist and physicist. The only person ever to win\" +\n                \" two Nobel prizes for two different sciences.\"\n            ),\n            new Person(\n                \"Linus Pauling\",\n                1901, 1994,\n                \"American chemist and peace activist. One of only two people to\" +\n                \" win two Nobel prizes in different fields (chemistry and peace).\"\n            ),\n            new Person(\n                \"Freddie Mercury\",\n                1946, 1991,\n                \"British musician, best known as the lead singer of the rock band Queen.\"\n            ),\n            new Person(\n                \"Marie Fredriksson\",\n                1958, 2019,\n                \"Swedish multi-instrumentalist, mainly known as the lead singer and\" +\n                \" keyboardist of the band Roxette.\"\n            ),\n            new Person(\n                \"Paul Erdos\",\n                1913, 1996,\n                \"Hungarian mathematician, known for his eccentric personality almost\" +\n                \" as much as his contributions to many different fields of mathematics.\"\n            ),\n            new Person(\n                \"Maryam Mirzakhani\",\n                1977, 2017,\n                \"Iranian mathematician. The first woman ever to win the Fields medal\" +\n                \" for her contributions to mathematics.\"\n            ),\n            new Person(\n                \"Masako Natsume\",\n                1957, 1985,\n                \"Japanese actress. She was very famous in Japan but was primarily\" +\n                \" known elsewhere in the world for her portrayal of Tripitaka in the\" +\n                \" TV series Monkey.\"\n            ),\n            new Person(\n                \"Chaim Topol\",\n                1935, 2023,\n                \"Israeli actor and singer, usually credited simply as 'Topol'. He was\" +\n                \" best known for his many appearances as Tevye in the musical Fiddler\" +\n                \" on the Roof.\"\n            )\n        );","section_id":"create-the-data"},{"id":"create-the-data-ex1","language":"java","code":"final class Person {\n            final String name;\n            final int born;\n            final int died;\n            final String description;\n            Person(String name, int born, int died, String description) {\n                this.name = name; this.born = born; this.died = died; this.description = description;\n            }\n        }\n\n        List<Person> people = Arrays.asList(\n            new Person(\n                \"Marie Curie\",\n                1867, 1934,\n                \"Polish-French chemist and physicist. The only person ever to win\" +\n                \" two Nobel prizes for two different sciences.\"\n            ),\n            new Person(\n                \"Linus Pauling\",\n                1901, 1994,\n                \"American chemist and peace activist. One of only two people to\" +\n                \" win two Nobel prizes in different fields (chemistry and peace).\"\n            ),\n            new Person(\n                \"Freddie Mercury\",\n                1946, 1991,\n                \"British musician, best known as the lead singer of the rock band Queen.\"\n            ),\n            new Person(\n                \"Marie Fredriksson\",\n                1958, 2019,\n                \"Swedish multi-instrumentalist, mainly known as the lead singer and\" +\n                \" keyboardist of the band Roxette.\"\n            ),\n            new Person(\n                \"Paul Erdos\",\n                1913, 1996,\n                \"Hungarian mathematician, known for his eccentric personality almost\" +\n                \" as much as his contributions to many different fields of mathematics.\"\n            ),\n            new Person(\n                \"Maryam Mirzakhani\",\n                1977, 2017,\n                \"Iranian mathematician. The first woman ever to win the Fields medal\" +\n                \" for her contributions to mathematics.\"\n            ),\n            new Person(\n                \"Masako Natsume\",\n                1957, 1985,\n                \"Japanese actress. She was very famous in Japan but was primarily\" +\n                \" known elsewhere in the world for her portrayal of Tripitaka in the\" +\n                \" TV series Monkey.\"\n            ),\n            new Person(\n                \"Chaim Topol\",\n                1935, 2023,\n                \"Israeli actor and singer, usually credited simply as 'Topol'. He was\" +\n                \" best known for his many appearances as Tevye in the musical Fiddler\" +\n                \" on the Roof.\"\n            )\n        );","section_id":"create-the-data"},{"id":"add-the-data-to-a-vector-set-ex0","language":"java","code":"public static Double[] convertFloatsToDoubles(float[] input) {\n        if (input == null) {\n            return null;\n        }\n\n        Double[] output = new Double[input.length];\n\n        for (int i = 0; i < input.length; i++) {\n            output[i] = Double.valueOf(input[i]);\n        }\n\n        return output;\n    }","section_id":"add-the-data-to-a-vector-set"},{"id":"add-the-data-to-a-vector-set-ex1","language":"java","code":"public static Double[] convertFloatsToDoubles(float[] input) {\n        if (input == null) {\n            return null;\n        }\n\n        Double[] output = new Double[input.length];\n\n        for (int i = 0; i < input.length; i++) {\n            output[i] = Double.valueOf(input[i]);\n        }\n\n        return output;\n    }","section_id":"add-the-data-to-a-vector-set"},{"id":"add-the-data-to-a-vector-set-ex2","language":"java","code":"RedisClient redisClient = RedisClient.create(\"redis://localhost:6379\");\n\n        try (StatefulRedisConnection<String, String> connection = redisClient.connect()) {\n            RedisAsyncCommands<String, String> asyncCommands = connection.async();\n\n            CompletableFuture<?>[] vaddFutures = new CompletableFuture[people.size()];\n\n            for (int i = 0; i < people.size(); i++) {\n                Person person = people.get(i);\n                Double[] embedding = null;\n\n                try {\n                    embedding = convertFloatsToDoubles(predictor.predict(person.description));\n                } catch (Exception e) {\n                    // ...\n                }\n\n                VAddArgs personArgs = VAddArgs.Builder.attributes(String.format(\"{\\\"born\\\": %d, \\\"died\\\": %d}\", person.born, person.died));\n                \n                vaddFutures[i] = asyncCommands.vadd(\"famousPeople\", person.name, personArgs, embedding)\n                        .thenApply(result -> {\n                            System.out.println(result); // >>> true\n                            return result;\n                        }).toCompletableFuture();\n            }\n            \n            CompletableFuture.allOf(vaddFutures).join();","section_id":"add-the-data-to-a-vector-set"},{"id":"add-the-data-to-a-vector-set-ex3","language":"java","code":"RedisClient redisClient = RedisClient.create(\"redis://localhost:6379\");\n\n        try (StatefulRedisConnection<String, String> connection = redisClient.connect()) {\n            RedisReactiveCommands<String, String> reactiveCommands = connection.reactive();\n\n            Mono<?>[] vaddFutures = new Mono[people.size()];\n\n            for (int i = 0; i < people.size(); i++) {\n                Person person = people.get(i);\n                Double[] embedding = null;\n\n                try {\n                    embedding = convertFloatsToDoubles(predictor.predict(person.description));\n                } catch (Exception e) {\n                    // ...\n                }\n\n                VAddArgs personArgs = VAddArgs.Builder.attributes(String.format(\"{\\\"born\\\": %d, \\\"died\\\": %d}\", person.born, person.died));\n\n                vaddFutures[i] = reactiveCommands.vadd(\"famousPeople\", person.name, personArgs, embedding)\n                        .doOnNext(result -> {\n                            System.out.println(result); // >>> true\n                        });\n            }\n\n            Mono.when(vaddFutures).block();","section_id":"add-the-data-to-a-vector-set"},{"id":"query-the-vector-set-ex0","language":"java","code":"Double[] actorsEmbedding = null;\n            \n            try {\n                actorsEmbedding = convertFloatsToDoubles(predictor.predict(\"actors\"));\n            } catch (Exception e) {\n                // ...\n            }\n\n            CompletableFuture<List<String>> basicQuery = asyncCommands.vsim(\"famousPeople\", actorsEmbedding)\n                    .thenApply(result -> {\n                        System.out.println(result);\n                        // >>> [Masako Natsume, Chaim Topol, Linus Pauling, Marie Fredriksson, Maryam Mirzakhani, Marie Curie, Freddie Mercury, Paul Erdos]\n                        return result;\n                    }).toCompletableFuture();","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex1","language":"java","code":"Double[] actorsEmbedding = null;\n\n            try {\n                actorsEmbedding = convertFloatsToDoubles(predictor.predict(\"actors\"));\n            } catch (Exception e) {\n                // ...\n            }\n\n            Mono<java.util.List<String>> basicQuery = reactiveCommands.vsim(\"famousPeople\", actorsEmbedding)\n                    .collectList().doOnNext(result -> {\n                        System.out.println(result);\n                        // >>> [Masako Natsume, Chaim Topol, Linus Pauling, Marie Fredriksson, Maryam Mirzakhani, Marie Curie, Freddie Mercury, Paul Erdos]\n                    });","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex2","language":"plaintext","code":"['Masako Natsume', 'Chaim Topol', 'Linus Pauling',\n'Marie Fredriksson', 'Maryam Mirzakhani', 'Marie Curie',\n'Freddie Mercury', 'Paul Erdos']","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex3","language":"java","code":"VSimArgs limitArgs = VSimArgs.Builder.count(2L);\n            \n            CompletableFuture<List<String>> limitedQuery = asyncCommands.vsim(\"famousPeople\", limitArgs, actorsEmbedding)\n                    .thenApply(result -> {\n                        System.out.println(result);\n                        // >>> [Masako Natsume, Chaim Topol]\n                        return result;\n                    }).toCompletableFuture();","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex4","language":"java","code":"VSimArgs limitArgs = VSimArgs.Builder.count(2L);\n\n            Mono<java.util.List<String>> limitedQuery = reactiveCommands.vsim(\"famousPeople\", limitArgs, actorsEmbedding)\n                    .collectList().doOnNext(result -> {\n                        System.out.println(result);\n                        // >>> [Masako Natsume, Chaim Topol]\n                    });","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex5","language":"java","code":"Double[] entertainerEmbedding = null;\n\n            try {\n                entertainerEmbedding = convertFloatsToDoubles(predictor.predict(\"entertainers\"));\n            } catch (Exception e) {\n                // ...\n            }\n\n            CompletableFuture<List<String>> entertainerQuery = asyncCommands.vsim(\"famousPeople\", entertainerEmbedding)\n                    .thenApply(result -> {\n                        System.out.println(result);\n                        // >>> [Freddie Mercury, Chaim Topol, Linus Pauling, Marie Fredriksson, Masako Natsume, Paul Erdos, Maryam Mirzakhani, Marie Curie]\n                        return result;\n                    }).toCompletableFuture();","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex6","language":"java","code":"Double[] entertainerEmbedding = null;\n\n            try {\n                entertainerEmbedding = convertFloatsToDoubles(predictor.predict(\"entertainers\"));\n            } catch (Exception e) {\n                // ...\n            }\n\n            Mono<java.util.List<String>> entertainerQuery = reactiveCommands.vsim(\"famousPeople\", entertainerEmbedding)\n                    .collectList().doOnNext(result -> {\n                        System.out.println(result);\n                        // >>> [Freddie Mercury, Chaim Topol, Linus Pauling, Marie Fredriksson, Masako Natsume, Paul Erdos, Maryam Mirzakhani, Marie Curie]\n                    });","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex7","language":"plaintext","code":"['Marie Curie', 'Linus Pauling', 'Maryam Mirzakhani',\n'Paul Erdos', 'Marie Fredriksson', 'Freddie Mercury', 'Masako Natsume',\n'Chaim Topol']","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex8","language":"java","code":"Double[] science2000Embedding = null;\n\n            try {\n                science2000Embedding = convertFloatsToDoubles(predictor.predict(\"science\"));\n            } catch (Exception e) {\n                // ...\n            }\n\n            VSimArgs filterArgs = VSimArgs.Builder.filter(\".died < 2000\");\n\n            CompletableFuture<List<String>> filteredQuery = asyncCommands.vsim(\"famousPeople\", filterArgs, science2000Embedding)\n                    .thenApply(result -> {\n                        System.out.println(result);\n                        // >>> [Marie Curie, Linus Pauling, Paul Erdos, Freddie Mercury, Masako Natsume]\n                        return result;\n                    }).toCompletableFuture();","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex9","language":"java","code":"Double[] science2000Embedding = null;\n\n            try {\n                science2000Embedding = convertFloatsToDoubles(predictor.predict(\"science\"));\n            } catch (Exception e) {\n                // ...\n            }\n\n            VSimArgs filterArgs = VSimArgs.Builder.filter(\".died < 2000\");\n\n            Mono<java.util.List<String>> filteredQuery = reactiveCommands.vsim(\"famousPeople\", filterArgs, science2000Embedding)\n                    .collectList().doOnNext(result -> {\n                        System.out.println(result);\n                        // >>> [Marie Curie, Linus Pauling, Paul Erdos, Freddie Mercury, Masako Natsume]\n                    });","section_id":"query-the-vector-set"}]}
{"schema_version":2,"id":"develop/clients/nodejs/amr","title":"Connect to Azure Managed Redis","url":"https://redis.io/docs/latest/develop/clients/nodejs/amr/","summary":"Learn how to authenticate to an Azure Managed Redis (AMR) database","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-08T15:03:39+01:00","page_type":"content","content_hash":"ba49d9e3e789c7ee654465d5f5c8e41ec935615a04cd90f0b2c439b3f77d95f8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The [`@redis/entraid`](https://github.com/redis/node-redis/tree/master/packages/entraid)\npackage lets you authenticate your app to\n[Azure Managed Redis (AMR)](https://azure.microsoft.com/en-us/products/managed-redis)\nusing [Microsoft Entra ID](https://learn.microsoft.com/en-us/entra/identity/).\nYou can authenticate using a system-assigned or user-assigned\n[managed identity](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview),\na [service principal](https://learn.microsoft.com/en-us/entra/identity-platform/app-objects-and-service-principals),\nan [auth code flow](https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-auth-code-flow),\nor a [`DefaultAzureCredential`](https://learn.microsoft.com/en-gb/dotnet/azure/sdk/authentication/credential-chains?tabs=dac#defaultazurecredential-overview) instance.\nThe `@redis/entraid` code fetches and renews the authentication tokens for you automatically."},{"id":"install","title":"Install","role":"setup","text":"Install [`node-redis`](https://redis.io/docs/latest/develop/clients/nodejs) and\n`@redis/entraid` with the following commands:\n\n[code example]"},{"id":"create-a-credentials-provider-instance","title":"Create a credentials provider instance","role":"content","text":"A credentials provider object obtains the authentication credentials you\nneed when you connect to Redis (see the [Connect](#connect) section below\nfor a connection example). The `EntraIdCredentialsProviderFactory`\nclass provides a factory method for each type of credentials provider.\nImport `EntraIdCredentialsProviderFactory` with the following code:\n\n[code example]\n\nThen, use the appropriate factory method to create your credentials provider:\n\n-   Use [`createForClientCredentials()`](#authenticate-with-a-service-principal)\n    to authenticate with a service principal using a client secret.\n-   Use [`createForClientCredentialsWithCertificate()`](#authenticate-with-a-service-principal)\n    to authenticate with a service principal using a certificate.\n-   Use [`createForSystemAssignedManagedIdentity()`](#authenticate-with-a-managed-identity)\n    to authenticate with a system-assigned managed identity.\n-   Use [`createForUserAssignedManagedIdentity()`](#authenticate-with-a-managed-identity)\n    to authenticate with a user-assigned managed identity.\n-   Use [`createForAuthorizationCodeWithPKCE()`](#auth-code-flow-with-pkce)\n    for interactive authentication flows in user applications.\n-   Use [`createForDefaultAzureCredential()`](#def-az-cred)\n    to authenticate with Azure Identity's `DefaultAzureCredential` class.\n\nThe sections below describe these factory functions in more detail."},{"id":"provider-parameters","title":"Provider parameters","role":"content","text":"All these factory functions receive an object containing the parameters\nto create the credentials provider. The object generally contains the following\nfields, but specific factory methods add or omit particular parameters:\n\n-   `clientId`: A string containing the client ID.\n-   `scopes`: (Optional) A string or an array of strings defining the\n    [scopes](https://learn.microsoft.com/en-us/entra/identity-platform/scopes-oidc)\n    you want to apply. Configure your client application to acquire a Microsoft Entra token for scope, https://redis.azure.com/.default or acca5fbb-b7e4-4009-81f1-37e38fd66d78/.default with the\n    [Microsoft Authentication Library (MSAL)](https://learn.microsoft.com/en-us/entra/identity-platform/msal-overview). Note that the `entra-id-credentials-provider-factory`\n    module exports a constant `REDIS_SCOPE_DEFAULT` for the default scope. See the\n    [`DefaultAzureCredential`](#def-az-cred) example below to learn how to use this.\n-   `authorityConfig`: (Optional) [Authority](https://learn.microsoft.com/en-us/entra/identity-platform/msal-client-application-configuration#authority)\n    settings. See the [`authorityConfig`](#authorityconfig) section below for a full\n    description.\n-   `tokenManagerConfig`: An object with fields that specify how to refresh the token.\n    See the [`tokenManagerConfig`](#tokenmanagerconfig) section below for a full\n    description.\n\n#### **tokenManagerConfig**\n\nYou can configure an authentication token to expire after a certain amount of\ntime, known as its *lifetime*. You must refresh a token before its lifetime\nis over to continue using it (see\n[Configurable token lifetimes in the Microsoft identity platform](https://learn.microsoft.com/en-us/entra/identity-platform/configurable-token-lifetimes)\nfor more information). The `tokenManagerConfig` object lets you pass parameters to\nspecify how you want to manage token refreshes. The fields of the object are listed\nbelow:\n\n-   `expirationRefreshRatio`: This is the fraction of the token's lifetime that should\n    elapse before a refresh is triggered. For example, a value of 0.75 means the token\n    should be refreshed when 75% of its lifetime has elapsed.\n-   `retry`: This object specifies the policy to retry refreshing the token if\n    an error occurs. It has the following fields:\n    -   `maxAttempts`: The maximum number of times to attempt a refresh before\n        aborting.\n    -   `initialDelayMs`: The delay (in milliseconds) before retrying after the\n        first failed attempt.\n    -   `maxDelayMs`: The maximum time (in milliseconds) to wait between attempts\n        to refresh.\n    -   `backoffMultiplier`: The\n        [exponential backoff](https://en.wikipedia.org/wiki/Exponential_backoff)\n        for the time between attempts to refresh. For example, a value of 2\n        will double the delay for each attempt. Use a value of 1 to keep\n        the delay roughly constant.\n    -   `jitterPercentage`: (Optional) The maximum fraction of the calculated delay time to\n        randomly add or subtract. For example, a value of 0.1 will add or subtract\n        up to 10% of the delay time. This random component helps to prevent\n        repeated collisions between clients that have the same retry settings.\n    -   `isRetryable`: (Optional) This specifies a function with the signature <br/><br/>\n        `(error, attempt) => boolean`<br/><br/>\n        where `error` is the error object from\n        a failed attempt and `attempt` is the number of attempts previously made.\n        This function classifies errors from the identity provider as \n        retryable (returning `true`) or non-retryable (returning `false`).\n        Implement your own custom logic here to determine if a token refresh failure\n        should be retried, based on the type of error. For example, a refresh failure\n        caused by a transient network error would probably succeed eventually if you retry\n        it, but an invalid certificate is generally a fatal error. If you don't supply a custom\n        function in this parameter, the default behavior is to retry all types of errors.\n\n#### **authorityConfig**\n\nThe `authorityConfig` object has a `type` field that can take any of\nthe string values `default`, `multi-tenant`, or `custom`. If you use\n`multi-tenant` then you must also supply a `tenantId` field containing\nthe tenant ID string:\n\n[code example]\n\nIf you use `custom` then you must also supply an `authorityUrl`\ncontaining the authority URL string:\n\n[code example]\nSee Microsoft's [Authority](https://learn.microsoft.com/en-us/entra/identity-platform/msal-client-application-configuration#authority)\ndocs for more information."},{"id":"authenticate-with-a-service-principal","title":"Authenticate with a service principal","role":"security","text":"Use the `createForClientCredentials()` factory function to create a\ncredentials provider that authenticates to AMR using a\nservice principal (see the\n[Microsoft documentation](https://learn.microsoft.com/en-us/entra/identity-platform/app-objects-and-service-principals) to learn more about service principals).\n\nYou will need the following details of your service principal to make the connection:\n\n- Client ID\n- Client secret\n- Tenant ID\n\nThe example below shows how to call `createForClientCredentials()`. Note that the\n[parameter object](#provider-parameters) includes an extra field here for the\nclient secret.\n\n[code example]\n\nIf you want to authenticate using a service principal with a certificate,\nuse `createForClientCredentialsWithCertificate()` to create the credentials\nprovider. This is similar to `createForClientCredentials()` but it takes\na `clientCertificate` parameter object instead of the `clientSecret` parameter.\nThis object has the following string fields:\n\n-   `x5c`: X.509 certificate.\n-   `privateKey`: The certificate's private key.\n-   `thumbprintSha256`: (Optional) SHA-256 hash of the certificate.\n\nThe example below shows how to call `createForClientCredentialsWithCertificate()` and\ndemonstrates the `retry` parameter in `tokenManagerConfig`:\n\n[code example]"},{"id":"authenticate-with-a-managed-identity","title":"Authenticate with a managed identity","role":"security","text":"You can authenticate to AMR using a managed identity (see the\n[Microsoft documentation](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview) to learn more about managed identities).\n\nFor a system-assigned managed identity, use the `createForSystemAssignedManagedIdentity()` factory function as shown in the example below:\n\n[code example]\n\nFor a user-assigned managed identity, use `createForUserAssignedManagedIdentity()`.\nHere, the [parameter object](#provider-parameters) includes an extra field for\nthe user-assigned client ID.\n\n[code example]"},{"id":"auth-code-flow-with-pkce","title":"Auth code flow with PKCE","role":"security","text":"*Auth code flow with Proof Key for Code Exchange (PKCE)* lets a client app\nauthenticate for access to web APIs and other restricted resources. This\nrequires a redirect URI to return control to your app after authentication.\nSee\n[Microsoft identity platform and OAuth 2.0 authorization code flow](https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-auth-code-flow)\nfor more information.\n\nUse the `createForAuthorizationCodeWithPKCE()` factory method to use auth code flow\nwith PKCE. The example below shows the extra field `redirectUri` in the parameter\nobject:\n\n[code example]"},{"id":"def-az-cred","title":"Authenticate with `DefaultAzureCredential`","role":"security","text":"The\n[`DefaultAzureCredential`](https://learn.microsoft.com/en-gb/dotnet/azure/sdk/authentication/credential-chains?tabs=dac#defaultazurecredential-overview)\nclass in `@azure/identity` is designed for use during development. It simplifies\nauthentication by automatically trying different credentials until one succeeds.\nSee [`DefaultAzureCredential` overview](https://learn.microsoft.com/en-gb/dotnet/azure/sdk/authentication/credential-chains?tabs=dac#defaultazurecredential-overview)\nin the Microsoft docs for more information.\n\nThe example below shows how to use `createForDefaultAzureCredential()`:\n\n[code example]\n\nNote that when you use `createForDefaultAzureCredential()`, you must:\n\n-   Create your own instance of `DefaultAzureCredential`.\n-   Pass the same parameters to the factory method that you would use with the `getToken()`\n    method:\n    -   `scopes`: The Redis scope (use the exported `REDIS_SCOPE_DEFAULT` constant).\n    -   `options`: Any other options for the `getToken()` method."},{"id":"connect","title":"Connect","role":"content","text":"When you have created your credential provider instance, you are ready to\nconnect to AMR.\nThe example below shows how to pass the instance as a parameter to the standard\n`createCluster()` connection method.\n\n[code example]"},{"id":"resp2-pubsub-limitations","title":"RESP2 PUB/SUB limitations","role":"content","text":"If you are using the\n[RESP2](https://redis.io/docs/latest/develop/reference/protocol-spec#resp-versions)\nprotocol, you should\nbe aware that [pub/sub](https://redis.io/docs/latest/develop/pubsub) can\ncause complications with reauthentication.\n\nAfter a connection enters PUB/SUB mode, the socket is blocked and can't process\nout-of-band commands like [`AUTH`](https://redis.io/docs/latest/commands/auth). This means that\nconnections in PUB/SUB mode can't be reauthenticated when the tokens are refreshed.\nAs a result, PUB/SUB connections will be evicted by the Redis proxy when their tokens expire. \nYou must reconnect with fresh tokens when this happens."},{"id":"note-about-transactions","title":"Note about transactions","role":"content","text":"If you use\n[transactions](https://redis.io/docs/latest/develop/clients/nodejs/transpipe)\nin code that also uses token-based authentication, ensure that you use\nthe `node-redis` client multi/exec API rather than explicit commands to create\neach transaction (see\n[Execute a transaction](https://redis.io/docs/latest/develop/clients/nodejs/transpipe/#execute-a-transaction)\nfor an example of correct usage).\nThis is because the token manager might attempt to reauthenticate while your code\nissues the separate transaction commands, which will interfere with the transaction.\nThe transaction API handles this case for you safely."}],"examples":[{"id":"install-ex0","language":"bash","code":"npm install \"@redis/client\"\nnpm install \"@redis/entraid\"","section_id":"install"},{"id":"create-a-credentials-provider-instance-ex0","language":"js","code":"import { EntraIdCredentialsProviderFactory }\n    from '@redis/entraid';","section_id":"create-a-credentials-provider-instance"},{"id":"provider-parameters-ex0","language":"js","code":"// Other fields...\nauthorityConfig: {\n    type: 'multi-tenant',\n    tenantId: 'your-tenant-id'\n  },\n// ...","section_id":"provider-parameters"},{"id":"provider-parameters-ex1","language":"js","code":"// Other fields...\nauthorityConfig: {\n    type: 'custom',\n    authorityUrl: 'your-authority-url'\n  },\n// ...","section_id":"provider-parameters"},{"id":"authenticate-with-a-service-principal-ex0","language":"js","code":"const provider = EntraIdCredentialsProviderFactory.createForClientCredentials({\n  clientId: 'your-client-id',\n  clientSecret: 'your-client-secret',\n  authorityConfig: {\n    type: 'multi-tenant',\n    tenantId: 'your-tenant-id'\n  },\n  tokenManagerConfig: {\n    expirationRefreshRatio: 0.8 // Refresh token after 80% of its lifetime\n  }\n});","section_id":"authenticate-with-a-service-principal"},{"id":"authenticate-with-a-service-principal-ex1","language":"js","code":"const provider = EntraIdCredentialsProviderFactory.createForClientCredentialsWithCertificate({\n  clientId: 'your-client-id',\n  clientCertificate: {\n    x5c: '<certificate>',\n    privateKey: '<private-key>',\n    thumbprintSha256: '<certificate-SHA-256-hash>'\n  },\n  tokenManagerConfig: {\n    expirationRefreshRatio: 0.75,\n    retry: {\n        maxAttempts: 3,\n        initialDelayMs: 100,\n        maxDelayMs: 1000,\n        backoffMultiplier: 2\n    }\n  }\n});","section_id":"authenticate-with-a-service-principal"},{"id":"authenticate-with-a-managed-identity-ex0","language":"js","code":"const provider = EntraIdCredentialsProviderFactory.createForSystemAssignedManagedIdentity({\n    clientId: 'your-client-id'\n});","section_id":"authenticate-with-a-managed-identity"},{"id":"authenticate-with-a-managed-identity-ex1","language":"js","code":"const provider = EntraIdCredentialsProviderFactory.createForUserAssignedManagedIdentity({\n  clientId: 'your-client-id',\n  userAssignedClientId: 'your-user-assigned-client-id'\n});","section_id":"authenticate-with-a-managed-identity"},{"id":"auth-code-flow-with-pkce-ex0","language":"js","code":"const provider = EntraIdCredentialsProviderFactory.createForAuthorizationCodeWithPKCE({\n  clientId: 'your-client-id',\n  redirectUri: '<uri-for-your-app>'\n});","section_id":"auth-code-flow-with-pkce"},{"id":"def-az-cred-ex0","language":"js","code":"import { DefaultAzureCredential } from '@azure/identity';\nimport { EntraIdCredentialsProviderFactory, REDIS_SCOPE_DEFAULT }\n    from '@redis/entraid';\n\n// ...\n\n// Create a DefaultAzureCredential instance\nconst credential = new DefaultAzureCredential();\n\n// Create a provider using DefaultAzureCredential\nconst provider = EntraIdCredentialsProviderFactory.createForDefaultAzureCredential({\n  // Use the same parameters you would pass to credential.getToken()\n  credential,\n  scopes: REDIS_SCOPE_DEFAULT, // The Redis scope\n  // Optional additional parameters for getToken\n  options: {\n    // Any options you would normally pass to credential.getToken()\n  },\n  tokenManagerConfig: {\n    expirationRefreshRatio: 0.8\n  }\n});","section_id":"def-az-cred"},{"id":"connect-ex0","language":"js","code":"import { createCluster } from '@redis/client';\nimport { EntraIdCredentialsProviderFactory }\n    from '@redis/entraid';\n    \n// Create credentials provider instance...\n// Note: AMR databases have clustering enabled by default.\nconst client = createCluster({\n  url: 'redis://localhost',\n  credentialsProvider: provider\n});\n\nawait client.connect();\n\nconst size = await client.dbSize();\nconsole.log(`Database size is ${size}`);","section_id":"connect"}]}
{"schema_version":2,"id":"develop/clients/nodejs/connect","title":"Connect to the server","url":"https://redis.io/docs/latest/develop/clients/nodejs/connect/","summary":"Connect your Node.js application to a Redis database","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-12T14:48:04+01:00","page_type":"content","content_hash":"472ba948b3f0f194090dea842b991a1ce09934a111614b46a5e3a2b938bc202d","sections":[{"id":"basic-connection","title":"Basic connection","role":"overview","text":"Connect to localhost on port 6379. \n\n[code example]\n\nStore and retrieve a simple string.\n\n[code example]\n\nStore and retrieve a map.\n\n[code example]\n\nTo connect to a different host or port, use a\n[connection URL](https://redis.io/docs/latest/develop/clients/connection-urls#node-redis-javascript):\n\n[code example]\n\n> [!NOTE]\n> A [connection URL](https://redis.io/docs/latest/develop/clients/connection-urls#node-redis-javascript) can\n> also set the host, port, credentials, database, and whether to use TLS,\n> so you can use one instead of passing those options separately.\n\nTo check if the client is connected and ready to send commands, use `client.isReady`, which returns a Boolean. `client.isOpen` is also available. This returns `true` when the client's underlying socket is open, and `false` when it isn't (for example, when the client is still connecting or reconnecting after a network error)."},{"id":"connect-to-a-redis-cluster","title":"Connect to a Redis cluster","role":"content","text":"To connect to a Redis cluster, use `createCluster`.\n\n[code example]"},{"id":"connect-to-redis-sentinel","title":"Connect to Redis Sentinel","role":"content","text":"To connect through [Redis Sentinel](https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel),\nuse `createSentinel()`. Set `name` to the name of the primary that the Sentinels monitor,\nand list one or more Sentinels in `sentinelRootNodes`. The client asks the Sentinels for\nthe current primary and switches to the new one after a failover.\n\n[code example]\n\nThe Sentinels and the data nodes have separate credentials. Set the Sentinel credentials in\n`sentinelClientOptions`, and the credentials for the primary and replicas in\n`nodeClientOptions`:\n\n[code example]\n\nNeither option accepts a `url`, so you can't use a connection string with\n`createSentinel()`.\n\nBy default, the client sends all commands to the primary. To send read-only commands such\nas `GET` to the replicas, set `replicaPoolSize` to the number of connections to open to each\nreplica. Replication is asynchronous, so a read that follows a write can return stale data.\n\n[code example]"},{"id":"connect-to-your-production-redis-with-tls","title":"Connect to your production Redis with TLS","role":"content","text":"When you deploy your application, use TLS and follow the [Redis security](https://redis.io/docs/latest/operate/oss_and_stack/management/security) guidelines.\n\n[code example]\n\nYou can also use discrete parameters and UNIX sockets. Details can be found in the [client configuration guide](https://github.com/redis/node-redis/blob/master/docs/client-configuration.md)."},{"id":"connect-using-client-side-caching","title":"Connect using client-side caching","role":"content","text":"Client-side caching is a technique to reduce network traffic between\nthe client and server, resulting in better performance. See\n[Client-side caching introduction](https://redis.io/docs/latest/develop/clients/client-side-caching)\nfor more information about how client-side caching works and how to use it effectively.\n\n> [!NOTE]\n> Client-side caching requires `node-redis` v5.1.0 or later.\n> To maximize compatibility with all Redis products, client-side caching\n> is supported by Redis v7.4 or later.\n>\n> The [Redis server products](https://redis.io/docs/latest/operate) support\n> [opt-in/opt-out](https://redis.io/docs/latest/develop/reference/client-side-caching#opt-in-and-opt-out-caching) mode\n> and [broadcasting mode](https://redis.io/docs/latest/develop/reference/client-side-caching#broadcasting-mode)\n> for CSC, but these modes are not currently implemented by `node-redis`.\n\nTo enable client-side caching, specify the\n[RESP3](https://redis.io/docs/latest/develop/reference/protocol-spec#resp-versions)\nprotocol and configure the cache with the `clientSideCache` parameter\nwhen you connect. If you want `node-redis` to create the cache for you,\nthen you can pass a simple configuration object in `clientSideCache`, as\nshown below:\n\n[code example]\n\nHowever, you can get more control over the cache very easily by creating\nyour own cache object and passing that as `clientSideCache` instead:\n\n[code example]\n\nThe main advantage of using your own cache instance is that you can\nuse its methods to clear all entries, invalidate individual keys,\nand gather useful performance statistics:\n\n[code example]\n\nWhen you have connected, the usual Redis commands will work transparently\nwith the cache:\n\n[code example]\n\nYou can see the cache working if you connect to the same Redis database\nwith [`redis-cli`](https://redis.io/docs/latest/develop/tools/cli) and run the\n[`MONITOR`](https://redis.io/docs/latest/commands/monitor) command. If you run the\ncode above but without passing `clientSideCache` during the connection,\nyou should see the following in the CLI among the output from `MONITOR`:\n\n[code example]\n\nThe server responds to both `get(\"city\")` calls.\nIf you run the code with `clientSideCache` added in again, you will see\n\n[code example]\n\nThe first `get(\"city\")` call contacted the server, but the second\ncall was satisfied by the cache."},{"id":"pooled-caching","title":"Pooled caching","role":"content","text":"You can also use client-side caching with client pools. Note that the same\ncache is shared among all the clients in the same pool. As with the\nnon-pooled connection, you can let `node-redis` create the cache for you:\n\n[code example]\n\nIf you want to access the cache, provide an instance of\n`BasicPooledClientSideCache` instead of `BasicClientSideCache`:\n\n[code example]"},{"id":"reconnect-after-disconnection","title":"Reconnect after disconnection","role":"content","text":"`node-redis` can attempt to reconnect automatically when\nthe connection to the server is lost. By default, it will retry\nthe connection using an\n[exponential backoff](https://en.wikipedia.org/wiki/Exponential_backoff)\nstrategy with some random \"jitter\" added to avoid multiple\nclients retrying in sync with each other.\n\nYou can also set the\n`socket.reconnectionStrategy` field in the configuration to decide\nwhether to try to reconnect and how to approach it. Choose one of the following values for\n`socket.reconnectionStrategy`:\n\n-   `false`: Don't attempt to reconnect.\n-   `number`: Wait for this number of milliseconds and then attempt to reconnect.\n-   `<function>`: Use a custom\n    function to decide how to handle reconnection.\n\nThe custom function has the following signature:\n\n[code example]\n\nIt is called before each attempt to reconnect, with the `retries`\nindicating how many attempts have been made so far. The `cause` parameter is an\n[`Error`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)\nobject with information about how the connection was lost. The return value\nfrom the function can be any of the following:\n\n-   `false`: Don't attempt to reconnect.\n-   `number`: Wait this number of milliseconds and then try again.\n-   `Error`: Same as `false`, but lets you supply extra information about why\n    no attempt was made to reconnect.\n\nThe example below shows a `reconnectionStrategy` function that implements a\ncustom exponential backoff strategy:\n\n[code example]"},{"id":"connect-using-smart-client-handoffs-sch","title":"Connect using Smart client handoffs (SCH)","role":"content","text":"*Smart client handoffs (SCH)* is a feature of Redis Cloud and\nRedis Software servers that lets them actively notify clients\nabout planned server maintenance shortly before it happens. This\nlets a client take action to avoid disruptions in service.\nSee [Smart client handoffs](https://redis.io/docs/latest/develop/clients/sch)\nfor more information about SCH.\n\n> [!NOTE]\n> Using SCH with node-redis requires v5.9.0 or later for\n> standard routing, and v5.11.0 or later for\n> [OSS Cluster API](https://redis.io/docs/latest/operate/rs/databases/configure/oss-cluster-api) connections.\n\nUse the configuration options shown in the example below to enable SCH\nduring the connection:\n\n[code example]\n\n> [!NOTE]\n> SCH requires the [RESP3](https://redis.io/docs/latest/develop/reference/protocol-spec#resp-versions)\n> protocol, so you must set the `RESP:3` option explicitly when you connect.\n\nThe available options are:\n\n-   `maintNotifications`: (`string`) Whether or not to enable SCH. The options are  \n    -   `'disabled'`: don't use SCH\n    -   `'enabled'`: attempt to activate SCH on the server and abort the connection if it isn't supported\n    -   `'auto'`: attempt to activate SCH on the server and fall back to a non-SCH\n        connection if it isn't supported. This is the default.\n-   `maintEndpointType`: (`MovingEndpointType`) The type of endpoint to use for the connection. The options are:\n    -   `'external-ip'`: use the external IP address of the server\n    -   `'internal-ip'`: use the internal IP address of the server\n    -   `'external-fqdn'`: use the external FQDN of the server\n    -   `'internal-fqdn'`: use the internal FQDN of the server\n    -   `'auto'`: auto-detect based on connection. This is the default.\n    -   `'none'`: attempt a graceful reconnect to the current endpoint after a delay.\n-   `maintRelaxedCommandTimeout`: (`number`) The command timeout to use while the server is \n    performing maintenance. The default is 10000 (10 seconds). If a timeout happens during the maintenance period, the client receives a `CommandTimeoutDuringMaintenance` error.\n-   `maintRelaxedSocketTimeout`: (`number`) The socket timeout to use while the server is \n    performing maintenance. The default is 10000 (10 seconds). If a timeout happens during the maintenance period, the client receives a `SocketTimeoutDuringMaintenance` error.\n\n> [!NOTE]\n>  Redis Cloud supports relaxed timeouts *only* (and not pre-handoffs) for SCH if you are using\n> either [AWS PrivateLink](https://redis.io/docs/latest/operate/rc/security/aws-privatelink) or\n> [Google Cloud Private Service Connect](https://redis.io/docs/latest/operate/rc/security/private-service-connect)\n> (see [Smart client handoffs](https://redis.io/docs/latest/develop/clients/sch#redis-cloud) for more information).\n> To use relaxed timeouts with these services, you should set `maintEndpointType: 'none'`\n> when you connect. All other configurations have full support for both relaxed timeouts and pre-handoffs."},{"id":"connection-events","title":"Connection events","role":"content","text":"The client object emits the following\n[events](https://developer.mozilla.org/en-US/docs/Web/API/Event) that are\nrelated to connection:\n\n-   `connect`: (No parameters) The client is about to start connecting to the server.\n-   `ready`: (No parameters) The client has connected and is ready to use.\n-   `end`: (No parameters) The client has been intentionally closed using `client.quit()`.\n-   `error`: An error has occurred, which is described by the\n    [`Error`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)\n    parameter. This is usually a network issue such as \"Socket closed unexpectedly\".\n-   `reconnecting`: (No parameters) The client is about to try reconnecting after the\n    connection was lost due to an error.\n-   `sharded-channel-moved`: The cluster slot of a subscribed\n    [sharded pub/sub channel](https://redis.io/docs/latest/develop/pubsub#sharded-pubsub)\n    has been moved to another shard. Note that when you use a\n    [`RedisCluster`](#connect-to-a-redis-cluster) connection, this event is automatically\n    handled for you. See\n    [`sharded-channel-moved` event](https://github.com/redis/node-redis/blob/master/docs/pub-sub.md#sharded-channel-moved-event) for more information.\n\nUse code like the following to respond to these events:\n\n[code example]"}],"examples":[{"id":"basic-connection-ex0","language":"js","code":"import { createClient } from 'redis';\n\nconst client = createClient();\n\nclient.on('error', err => console.log('Redis Client Error', err));\n\nawait client.connect();","section_id":"basic-connection"},{"id":"basic-connection-ex1","language":"js","code":"await client.set('key', 'value');\nconst value = await client.get('key');","section_id":"basic-connection"},{"id":"basic-connection-ex2","language":"js","code":"await client.hSet('user-session:123', {\n    name: 'John',\n    surname: 'Smith',\n    company: 'Redis',\n    age: 29\n})\n\nlet userSession = await client.hGetAll('user-session:123');\nconsole.log(JSON.stringify(userSession, null, 2));\n/*\n{\n  \"surname\": \"Smith\",\n  \"name\": \"John\",\n  \"company\": \"Redis\",\n  \"age\": \"29\"\n}\n */","section_id":"basic-connection"},{"id":"basic-connection-ex3","language":"js","code":"createClient({\n  url: 'redis://alice:foobared@awesome.redis.server:6380'\n});","section_id":"basic-connection"},{"id":"connect-to-a-redis-cluster-ex0","language":"js","code":"import { createCluster } from 'redis';\n\nconst cluster = createCluster({\n    rootNodes: [\n        {\n            url: 'redis://127.0.0.1:16379'\n        },\n        {\n            url: 'redis://127.0.0.1:16380'\n        },\n        // ...\n    ]\n});\n\ncluster.on('error', (err) => console.log('Redis Cluster Error', err));\n\nawait cluster.connect();\n\nawait cluster.set('foo', 'bar');\nconst value = await cluster.get('foo');\nconsole.log(value); // returns 'bar'\n\nawait cluster.close();","section_id":"connect-to-a-redis-cluster"},{"id":"connect-to-redis-sentinel-ex0","language":"js","code":"import { createSentinel } from 'redis';\n\nconst sentinel = createSentinel({\n    name: 'mymaster',\n    sentinelRootNodes: [\n        { host: 'localhost', port: 26379 },\n        { host: 'localhost', port: 26380 },\n        { host: 'localhost', port: 26381 }\n    ]\n});\n\nsentinel.on('error', (err) => console.log('Redis Sentinel Error', err));\n\nawait sentinel.connect();\n\nawait sentinel.set('foo', 'bar');\nconst value = await sentinel.get('foo');\nconsole.log(value); // returns 'bar'\n\nawait sentinel.close();","section_id":"connect-to-redis-sentinel"},{"id":"connect-to-redis-sentinel-ex1","language":"js","code":"const sentinel = createSentinel({\n    name: 'mymaster',\n    sentinelRootNodes: [\n        { host: 'localhost', port: 26379 },\n        { host: 'localhost', port: 26380 },\n        { host: 'localhost', port: 26381 }\n    ],\n    // Credentials for the Sentinel instances.\n    sentinelClientOptions: {\n        password: 'sentinel-secret'\n    },\n    // Credentials for the primary and replica data nodes.\n    nodeClientOptions: {\n        username: 'default',\n        password: 'secret'\n    }\n});","section_id":"connect-to-redis-sentinel"},{"id":"connect-to-redis-sentinel-ex2","language":"js","code":"const sentinel = createSentinel({\n    name: 'mymaster',\n    sentinelRootNodes: [\n        { host: 'localhost', port: 26379 }\n    ],\n    replicaPoolSize: 1\n});","section_id":"connect-to-redis-sentinel"},{"id":"connect-to-your-production-redis-with-tls-ex0","language":"js","code":"const client = createClient({\n    username: 'default', // use your Redis user. More info https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl/\n    password: 'secret', // use your password here\n    socket: {\n        host: 'my-redis.cloud.redislabs.com',\n        port: 6379,\n        tls: true,\n        key: readFileSync('./redis_user_private.key'),\n        cert: readFileSync('./redis_user.crt'),\n        ca: [readFileSync('./redis_ca.pem')]\n    }\n});\n\nclient.on('error', (err) => console.log('Redis Client Error', err));\n\nawait client.connect();\n\nawait client.set('foo', 'bar');\nconst value = await client.get('foo');\nconsole.log(value) // returns 'bar'\n\nawait client.destroy();","section_id":"connect-to-your-production-redis-with-tls"},{"id":"connect-using-client-side-caching-ex0","language":"js","code":"const client = createClient({\n  RESP: 3,\n  clientSideCache: {\n    ttl: 0,             // Time-to-live in milliseconds (0 = no expiration)\n    maxEntries: 0,      // Maximum entries to store (0 = unlimited)\n    evictPolicy: \"LRU\"  // Eviction policy: \"LRU\" or \"FIFO\"\n  }\n});","section_id":"connect-using-client-side-caching"},{"id":"connect-using-client-side-caching-ex1","language":"js","code":"import { BasicClientSideCache } from 'redis';\n\nconst cache = new BasicClientSideCache({\n  ttl: 0,\n  maxEntries: 0,\n  evictPolicy: \"LRU\"\n});\n\nconst client = createClient({\n  RESP: 3,\n  clientSideCache: cache\n});","section_id":"connect-using-client-side-caching"},{"id":"connect-using-client-side-caching-ex2","language":"js","code":"// Manually invalidate keys\ncache.invalidate(key);\n\n// Clear the entire cache\ncache.clear();\n\n// Get cache metrics\n// `cache.stats()` returns a `CacheStats` object with comprehensive statistics.\nconst statistics = cache.stats();\n\n// Key metrics:\nconst hits = statistics.hitCount;        // Number of cache hits\nconst misses = statistics.missCount;      // Number of cache misses\nconst hitRate = statistics.hitRate();     // Cache hit rate (0.0 to 1.0)\n\n// Many other metrics are available on the `statistics` object, e.g.:\n// statistics.missRate(), statistics.loadSuccessCount,\n// statistics.averageLoadPenalty(), statistics.requestCount()","section_id":"connect-using-client-side-caching"},{"id":"connect-using-client-side-caching-ex3","language":"java","code":"client.set(\"city\", \"New York\");\nclient.get(\"city\");     // Retrieved from Redis server and cached\nclient.get(\"city\");     // Retrieved from cache","section_id":"connect-using-client-side-caching"},{"id":"connect-using-client-side-caching-ex4","language":"plaintext","code":"1723109720.268903 [...] \"SET\" \"city\" \"New York\"\n1723109720.269681 [...] \"GET\" \"city\"\n1723109720.270205 [...] \"GET\" \"city\"","section_id":"connect-using-client-side-caching"},{"id":"connect-using-client-side-caching-ex5","language":"plaintext","code":"1723110248.712663 [...] \"SET\" \"city\" \"New York\"\n1723110248.713607 [...] \"GET\" \"city\"","section_id":"connect-using-client-side-caching"},{"id":"pooled-caching-ex0","language":"js","code":"const client = createClientPool({RESP: 3}, {\n  clientSideCache: {\n    ttl: 0,\n    maxEntries: 0,\n    evictPolicy: \"LRU\"\n  },\n  minimum: 5\n});","section_id":"pooled-caching"},{"id":"pooled-caching-ex1","language":"js","code":"import { BasicPooledClientSideCache } from 'redis';\n\nconst cache = new BasicPooledClientSideCache({\n  ttl: 0,\n  maxEntries: 0,\n  evictPolicy: \"LRU\"\n});\n\nconst client = createClientPool({RESP: 3}, {\n  clientSideCache: cache,\n  minimum: 5\n});","section_id":"pooled-caching"},{"id":"reconnect-after-disconnection-ex0","language":"js","code":"(retries: number, cause: Error) => false | number | Error","section_id":"reconnect-after-disconnection"},{"id":"reconnect-after-disconnection-ex1","language":"js","code":"createClient({\n  socket: {\n    reconnectStrategy: retries => {\n        // Generate a random jitter between 0 – 100 ms:\n        const jitter = Math.floor(Math.random() * 100);\n\n        // Delay is an exponential backoff, (2^retries) * 50 ms, with a\n        // maximum value of 3000 ms:\n        const delay = Math.min(Math.pow(2, retries) * 50, 3000);\n\n        return delay + jitter;\n    }\n  }\n});","section_id":"reconnect-after-disconnection"},{"id":"connect-using-smart-client-handoffs-sch-ex0","language":"js","code":"const client = createClient({\n  RESP: 3,\n  maintNotifications: 'auto',\n  maintEndpointType: 'auto',\n  maintRelaxedCommandTimeout: 10000,\n  maintRelaxedSocketTimeout: 10000,\n  ...\n});","section_id":"connect-using-smart-client-handoffs-sch"},{"id":"connection-events-ex0","language":"js","code":"client.on('error', error => {\n    console.error(`Redis client error:`, error);\n});","section_id":"connection-events"}]}
{"schema_version":2,"id":"develop/clients/nodejs/error-handling","title":"Error handling","url":"https://redis.io/docs/latest/develop/clients/nodejs/error-handling/","summary":"Learn how to handle errors when using node-redis.","tags":[],"last_updated":"2026-09-08T15:03:39+01:00","page_type":"content","content_hash":"82b433ca55198a284acb6a3bc4621d9a5311b0ad6befa5410bb36a973072810e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"node-redis uses\n[**promises**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)\nfor error handling. Most Redis JavaScript examples\nthroughout the documentation mainly show the \"happy path\" and omit error\nhandling for brevity. This page shows how to apply error handling\ntechniques in node-redis for real world code.\nFor an overview of some common general error types and strategies for\nhandling them, see\n[Error handling](https://redis.io/docs/latest/develop/clients/error-handling).\nSee also [Production usage](https://redis.io/docs/latest/develop/clients/nodejs/produsage)\nfor more information on connection management, timeouts, and other aspects of\napp reliability."},{"id":"common-error-types","title":"Common error types","role":"content","text":"node-redis throws errors as rejected promises. Common error types include:\n\n| Error | When it occurs | Recoverable | Recommended action |\n|---|---|---|---|\n| `ECONNREFUSED` | Connection refused | ✅ | Retry with backoff or fall back |\n| `ETIMEDOUT` | Command timeout | ✅ | Retry with backoff |\n| `ECONNRESET` | Connection reset by peer | ✅ | Retry with backoff |\n| `EAI_AGAIN` | DNS resolution failure | ✅ | Retry with backoff |\n| `ReplyError` (`WRONGTYPE`) | Type mismatch | ❌ | Fix schema or code |\n| `ReplyError` (`BUSY`, `TRYAGAIN`, `LOADING`) | Redis busy/loading | ⚠️ | Retry with backoff (bounded) |\n\nSee [Categories of errors](https://redis.io/docs/latest/develop/clients/error-handling#categories-of-errors)\nfor a more detailed discussion of these errors and their causes."},{"id":"asyncawait-in-examples","title":"Async/await in examples","role":"content","text":"The examples on this page and throughout the node-redis docs use `async/await`\nstyle for clarity.\n\n[code example]\n\nAlternatively, you can use promise chains with `.then()` and `.catch()`:\n\n[code example]"},{"id":"error-events","title":"Error events","role":"errors","text":"Node-Redis provides [multiple events to handle various scenarios](https://github.com/redis/node-redis?tab=readme-ov-file#events), among which the most critical is the `error` event.\n\nThis event is triggered whenever an error occurs within the client.\n\nIt is crucial to listen for error events.\n\nIf a client does not register at least one error listener and an error occurs, the system will throw that error, potentially causing the Node.js process to exit unexpectedly.\nSee [the EventEmitter docs](https://nodejs.org/api/events.html#events_error_events) for more details.\n\n[code example]"},{"id":"applying-error-handling-patterns","title":"Applying error handling patterns","role":"content","text":"The [Error handling](https://redis.io/docs/latest/develop/clients/error-handling)\noverview describes four common error handling patterns. The sections\nbelow show how to implement these patterns in node-redis:"},{"id":"pattern-1-fail-fast","title":"Pattern 1: Fail fast","role":"content","text":"Catch specific errors that represent unrecoverable errors and re-throw them (see\n[Pattern 1: Fail fast](https://redis.io/docs/latest/develop/clients/error-handling#pattern-1-fail-fast)\nfor a full description).\n\n[code example]"},{"id":"pattern-2-graceful-degradation","title":"Pattern 2: Graceful degradation","role":"content","text":"Catch connection errors and fall back to an alternative (see\n[Pattern 2: Graceful degradation](https://redis.io/docs/latest/develop/clients/error-handling#pattern-2-graceful-degradation)\nfor a full description).\n\n[code example]"},{"id":"pattern-3-retry-with-backoff","title":"Pattern 3: Retry with backoff","role":"content","text":"Retry on temporary errors like timeouts (see\n[Pattern 3: Retry with backoff](https://redis.io/docs/latest/develop/clients/error-handling#pattern-3-retry-with-backoff)\nfor a full description).\n\n[code example]\n\nNote that you can also configure node-redis to reconnect to the\nserver automatically when the connection is lost. See\n[Reconnect after disconnection](https://redis.io/docs/latest/develop/clients/nodejs/connect#reconnect-after-disconnection)\nfor more information."},{"id":"pattern-4-log-and-continue","title":"Pattern 4: Log and continue","role":"content","text":"Log non-critical errors and continue (see\n[Pattern 4: Log and continue](https://redis.io/docs/latest/develop/clients/error-handling#pattern-4-log-and-continue)\nfor a full description).\n\n[code example]"},{"id":"see-also","title":"See also","role":"related","text":"- [Error handling](https://redis.io/docs/latest/develop/clients/error-handling)\n- [Production usage](https://redis.io/docs/latest/develop/clients/nodejs/produsage)"}],"examples":[{"id":"asyncawait-in-examples-ex0","language":"javascript","code":"// Using async/await (shown in examples below)\ntry {\n    const result = await client.get(key);\n    // Handle success\n} catch (error) {\n    // Handle error\n}","section_id":"asyncawait-in-examples"},{"id":"asyncawait-in-examples-ex1","language":"javascript","code":"// Using promise chains (equivalent approach)\nclient.get(key)\n    .then(result => {\n        // Handle success\n    })\n    .catch(error => {\n        // Handle error\n    });","section_id":"asyncawait-in-examples"},{"id":"error-events-ex0","language":"typescript","code":"const client = createClient({\n  // ... client options\n});\n// Always ensure there's a listener for errors in the client to prevent process crashes due to unhandled errors\nclient.on('error', error => {\n    console.error(`Redis client error:`, error);\n});","section_id":"error-events"},{"id":"pattern-1-fail-fast-ex0","language":"javascript","code":"try {\n    await client.get(key);\n} catch (err) {\n    if (err.name === 'ReplyError' && /WRONGTYPE|ERR /.test(err.message)) {\n        throw err; // Fix code or data type\n    }\n    throw err;\n}","section_id":"pattern-1-fail-fast"},{"id":"pattern-2-graceful-degradation-ex0","language":"javascript","code":"try {\n    const val = await client.get(key);\n    if (val != null) return val;\n} catch (err) {\n    if (['ECONNREFUSED','ECONNRESET','ETIMEDOUT','EAI_AGAIN'].includes(err.code)) {\n        logger.warn('Cache unavailable; falling back to DB');\n        return database.get(key);\n    }\n    throw err;\n}\nreturn database.get(key);","section_id":"pattern-2-graceful-degradation"},{"id":"pattern-3-retry-with-backoff-ex0","language":"javascript","code":"async function getWithRetry(key, { attempts = 3, baseDelayMs = 100 } = {}) {\n  let delay = baseDelayMs;\n  for (let i = 0; i < attempts; i++) {\n    try {\n      return await client.get(key);\n    } catch (err) {\n      if (\n        i < attempts - 1 &&\n        (['ETIMEDOUT','ECONNRESET','EAI_AGAIN'].includes(err.code) ||\n         (err.name === 'ReplyError' && /(BUSY|TRYAGAIN|LOADING)/.test(err.message)))\n      ) {\n        await new Promise(r => setTimeout(r, delay));\n        delay *= 2;\n        continue;\n      }\n      throw err;\n    }\n  }\n}","section_id":"pattern-3-retry-with-backoff"},{"id":"pattern-4-log-and-continue-ex0","language":"javascript","code":"try {\n    await client.setEx(key, 3600, value);\n} catch (err) {\n    if (['ECONNREFUSED','ECONNRESET','ETIMEDOUT','EAI_AGAIN'].includes(err.code)) {\n        logger.warn(`Failed to cache ${key}, continuing without cache`);\n    } else {\n        throw err;\n    }\n}","section_id":"pattern-4-log-and-continue"}]}
{"schema_version":2,"id":"develop/clients/nodejs","title":"node-redis guide (JavaScript)","url":"https://redis.io/docs/latest/develop/clients/nodejs/","summary":"Connect your Node.js/JavaScript application to a Redis database","aliases":["/develop/connect/clients/nodejs","/connect/clients/nodejs/","/clients/nodejs/","/develop/clients/node-redis","/develop/clients/javascript"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-12T14:48:04+01:00","children":[{"id":"develop/clients/nodejs/connect","summary":"Connect your Node.js application to a Redis database","title":"Connect to the server","url":"https://redis.io/docs/latest/develop/clients/nodejs/connect/"},{"id":"develop/clients/nodejs/amr","summary":"Learn how to authenticate to an Azure Managed Redis (AMR) database","title":"Connect to Azure Managed Redis","url":"https://redis.io/docs/latest/develop/clients/nodejs/amr/"},{"id":"develop/clients/nodejs/queryjson","summary":"Learn how to use Redis Search with JSON and hash documents.","title":"Index and query documents","url":"https://redis.io/docs/latest/develop/clients/nodejs/queryjson/"},{"id":"develop/clients/nodejs/vecsearch","summary":"Learn how to index and query vector embeddings with Redis","title":"Index and query vectors","url":"https://redis.io/docs/latest/develop/clients/nodejs/vecsearch/"},{"id":"develop/clients/nodejs/vecsets","summary":"Index and query embeddings with Redis vector sets","title":"Vector set embeddings","url":"https://redis.io/docs/latest/develop/clients/nodejs/vecsets/"},{"id":"develop/clients/nodejs/transpipe","summary":"Learn how to use Redis pipelines and transactions","title":"Pipelines and transactions","url":"https://redis.io/docs/latest/develop/clients/nodejs/transpipe/"},{"id":"develop/clients/nodejs/prob","summary":"Learn how to use approximate calculations with Redis.","title":"Probabilistic data types","url":"https://redis.io/docs/latest/develop/clients/nodejs/prob/"},{"id":"develop/clients/nodejs/error-handling","summary":"Learn how to handle errors when using node-redis.","title":"Error handling","url":"https://redis.io/docs/latest/develop/clients/nodejs/error-handling/"},{"id":"develop/clients/nodejs/produsage","summary":"Get your Node.js app ready for production","title":"Production usage","url":"https://redis.io/docs/latest/develop/clients/nodejs/produsage/"},{"id":"develop/clients/nodejs/migration","summary":"Discover the differences between `ioredis` and `node-redis`.","title":"Migrate from ioredis","url":"https://redis.io/docs/latest/develop/clients/nodejs/migration/"},{"id":"develop/clients/nodejs/observability","summary":"Monitor your client's activity for optimization and debugging.","title":"Observability","url":"https://redis.io/docs/latest/develop/clients/nodejs/observability/"}],"page_type":"content","content_hash":"0bea12547084779997122e3d3e5b73cd7dfa399eb82d32d30fdc262efea02745","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[`node-redis`](https://github.com/redis/node-redis) is the Redis client for Node.js/JavaScript.\nThe sections below explain how to install `node-redis` and connect your application\nto a Redis database.\n\n> [!NOTE]\n> node-redis is the recommended client library for Node.js/JavaScript,\n> but we also support and document our older JavaScript client\n> [`ioredis`](https://redis.io/docs/latest/develop/clients/ioredis). See\n> [Migrate from ioredis](https://redis.io/docs/latest/develop/clients/nodejs/migration)\n> if you are interested in converting an existing `ioredis` project to `node-redis`.\n\n`node-redis` requires a running Redis server. See [here](https://redis.io/docs/latest/operate/oss_and_stack/install) for Redis Open Source installation instructions.\n\nYou can also access Redis with an object-mapping client interface. See\n[RedisOM for Node.js](https://redis.io/docs/latest/integrate/redisom-for-node-js)\nfor more information."},{"id":"install","title":"Install","role":"setup","text":"To install node-redis, run:\n\n[code example]"},{"id":"connect-and-test","title":"Connect and test","role":"content","text":"Connect to localhost on port 6379.\n\nFoundational: Create a client connection to a Redis server using node-redis\n\n[code example]\n\n\n\nStore and retrieve a simple string.\n\nFoundational: Set and retrieve string values using SET and GET commands\n\n[code example]\n\n\n\nStore and retrieve a map.\n\nFoundational: Store and retrieve hash data structures using HSET and HGET commands\n\n[code example]\n\n\n\nTo connect to a different host or port, use a connection URL:\n\n[code example]\n\n> [!NOTE]\n> node-redis supports [connection URLs](https://redis.io/docs/latest/develop/clients/connection-urls#node-redis-javascript) through the `url` option of `createClient()`.\n> A URL can set the host, port, credentials, database, and whether to use TLS,\n> so you can use one instead of passing those options separately.\n\nTo check if the client is connected and ready to send commands, use `client.isReady`, which returns a Boolean. `client.isOpen` is also available. This returns `true` when the client's underlying socket is open, and `false` when it isn't (for example, when the client is still connecting or reconnecting after a network error).\n\nWhen you have finished using a connection, close it with `client.quit()`.\n\nFoundational: Close a Redis client connection using the quit() method\n\n[code example]"},{"id":"more-information","title":"More information","role":"content","text":"The [`node-redis` website](https://redis.js.org/) has more examples.\nThe [Github repository](https://github.com/redis/node-redis) also has useful\ninformation, including a guide to the\n[connection configuration options](https://github.com/redis/node-redis/blob/master/docs/client-configuration.md) you can use.\n\nSee also the other pages in this section for more information and examples:"}],"examples":[{"id":"install-ex0","language":"bash","code":"npm install redis","section_id":"install"},{"id":"connect-and-test-ex0","language":"javascript","code":"import { createClient } from 'redis';\n\nconst client = createClient();\n\nclient.on('error', err => console.log('Redis Client Error', err));\n\nawait client.connect();","section_id":"connect-and-test"},{"id":"connect-and-test-ex1","language":"javascript","code":"await client.set('key', 'value');\nconst value = await client.get('key');\nconsole.log(value);","section_id":"connect-and-test"},{"id":"connect-and-test-ex2","language":"javascript","code":"await client.hSet('user-session:123', {\n    name: 'John',\n    surname: 'Smith',\n    company: 'Redis',\n    age: 29\n})\n\nlet userSession = await client.hGetAll('user-session:123');\nconsole.log(JSON.stringify(userSession, null, 2));","section_id":"connect-and-test"},{"id":"connect-and-test-ex3","language":"js","code":"createClient({\n  url: 'redis://alice:foobared@awesome.redis.server:6380'\n});","section_id":"connect-and-test"},{"id":"connect-and-test-ex4","language":"javascript","code":"await client.quit();","section_id":"connect-and-test"}]}
{"schema_version":2,"id":"develop/clients/nodejs/migration","title":"Migrate from ioredis","url":"https://redis.io/docs/latest/develop/clients/nodejs/migration/","summary":"Discover the differences between `ioredis` and `node-redis`.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-08T15:03:39+01:00","page_type":"content","content_hash":"0f3fcdc4cc7ca2a9fd9bbb1bff8a4e3c63614d8e6bc03cfc8e14b7a37bcf919e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis previously recommended the [`ioredis`](https://redis.io/docs/latest/develop/clients/ioredis)\nclient library for development with [Node.js](https://nodejs.org/en),\nbut this library is now deprecated in favor of\n[`node-redis`](https://redis.io/docs/latest/develop/clients/nodejs). This guide\noutlines the main similarities and differences between the two libraries.\nYou may find this information useful if you are an `ioredis` user and you want to\nstart a new Node.js project or migrate an existing `ioredis` project to `node-redis`."},{"id":"comparison-of-ioredis-and-node-redis","title":"Comparison of `ioredis` and `node-redis`","role":"content","text":"The tables below summarize how `ioredis` and `node-redis` implement some\nkey features of Redis. See the following sections for more information about\neach feature."},{"id":"connection","title":"Connection","role":"content","text":"| Feature | `ioredis` | `node-redis` |\n| :-- | :-- | :-- |\n| [Initial connection](#initial-connection) | Happens when you create a client instance | Requires you to call a method on the client instance |\n| [Reconnection after a connection is lost](#reconnection) | Automatic by default | Automatic by default and configurable |\n| [Connection events](#connection-events) | Emits `connect`, `ready`, `error`, and `close` events | Emits `connect`, `ready`, `error`, `end`, and `reconnecting` events |"},{"id":"command-handling","title":"Command handling","role":"syntax","text":"| Feature | `ioredis` | `node-redis` |\n| :-- | :-- | :-- |\n| [Command case](#command-case) | Lowercase only (eg, `hset`) | Uppercase or camel case (eg, `HSET` or `hSet`) |\n| [Command argument handling](#command-argument-handling) | Argument objects flattened and items passed directly | Argument objects parsed to generate correct argument list |\n| [Asynchronous command result handling](#async-result) | Callbacks and Promises | Promises (but supports callbacks via Legacy Mode) |\n| [Arbitrary command execution](#arbitrary-command-execution) | Uses the `call()` method | Uses the `sendCommand()` method |"},{"id":"techniques","title":"Techniques","role":"content","text":"| Feature | `ioredis` | `node-redis` |\n| :-- | :-- | :-- |\n| [Pipelining](#pipelining) | Automatic, or with `pipeline()` command | Automatic, or with `multi()` command |\n| [Scan iteration](#scan-iteration) | Uses `scanStream()`, etc | Uses `scanIterator()`, etc |\n| [Subscribing to channels](#subscribing-to-channels) | Uses `client.on('message', ...)` event | Uses `subscribe(...)` command |"},{"id":"specific-commands","title":"Specific commands","role":"content","text":"| Command | `ioredis` | `node-redis` |\n| :-- | :-- | :-- |\n| [`SETNX`](#setnx-command) | Supported explicitly | Supported as an option for `SET` |\n| [`HMSET`](#hmset-command) | Supported explicitly | Supported with standard `HSET` functionality |\n| [`CONFIG`](#config-command) | Supported explicitly | Supported with separate `configGet()`, `configSet()`, etc |co"},{"id":"details","title":"Details","role":"content","text":"The sections below explain the points of comparison between `ioredis` and\n`node-redis` in more detail."},{"id":"initial-connection","title":"Initial connection","role":"content","text":"`ioredis` makes the connection to the Redis server when you create an instance\nof the client object:\n\n[code example]\n\n`node-redis` requires you to call the `connect()` method on the client object\nto make the connection:\n\n[code example]"},{"id":"reconnection","title":"Reconnection after a connection is lost","role":"content","text":"Both `ioredis` and `node-redis` automatically attempt to reconnect if the connection\nwas lost due to an error. `node-redis` also lets you add a custom reconnection strategy\nwhen you create the client object. See\n[Reconnect after disconnection](https://redis.io/docs/latest/develop/clients/nodejs/connect#reconnect-after-disconnection)\nfor more information."},{"id":"connection-events","title":"Connection events","role":"content","text":"The `connect`, `ready`, `error`, and `close` events that `ioredis` emits\nare equivalent to the `connect`, `ready`, `error`, and `end` events\nin `node-redis`, but `node-redis` also emits a `reconnecting` event.\nSee [Connection events](https://redis.io/docs/latest/develop/clients/nodejs/connect#connection-events)\nfor more information."},{"id":"command-case","title":"Command case","role":"syntax","text":"Command methods in `ioredis` are always lowercase. With `node-redis`, you can\nuse uppercase or camel case versions of the method names.\n\n[code example]"},{"id":"command-argument-handling","title":"Command argument handling","role":"syntax","text":"`ioredis` parses command arguments to strings and then passes them to\nthe server, in a similar way to [`redis-cli`](https://redis.io/docs/latest/develop/tools/cli).\n\n[code example]\n\nArrays passed as arguments are flattened into individual elements and\nobjects are flattened into sequential key-value pairs:\n\n[code example]\n\n`node-redis` uses predefined formats for command arguments. These include specific\nclasses for command options that generally don't correspond to the syntax\nof the CLI command. Internally, `node-redis` constructs the correct command using\nthe method arguments you pass:\n\n[code example]"},{"id":"async-result","title":"Asynchronous command result handling","role":"content","text":"All commands for both `ioredis` and `node-redis` are executed\nasynchronously. `ioredis` supports both callbacks and\n[`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)\nreturn values to respond to command results:\n\n[code example]\n\n`node-redis` supports only `Promise` objects for results, so\nyou must always use a `then()` handler or the\n[`await`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await)\noperator to receive them. However, you can still use callbacks with the legacy mode if you need them:\n\n[code example]"},{"id":"arbitrary-command-execution","title":"Arbitrary command execution","role":"content","text":"`ioredis` lets you issue arbitrary commands in a similar format to\n[`redis-cli`](https://redis.io/docs/latest/develop/tools/cli) using the `call()`\ncommand:\n\n[code example]\n\nIn `node-redis`, you can get the same effect outside a transaction using `sendCommand()`:\n\n[code example]\n\nWithin a transaction, use `addCommand()` to include arbitrary commands. Note that\nyou can freely mix `addCommand()` calls with standard commands in the same\ntransaction:\n\n[code example]"},{"id":"pipelining","title":"Pipelining","role":"content","text":"Both `ioredis` and `node-redis` will pipeline commands automatically if\nthey are executed in the same \"tick\" of the\n[event loop](https://nodejs.org/en/learn/asynchronous-work/event-loop-timers-and-nexttick#what-is-the-event-loop)\n(see\n[Execute a pipeline](https://redis.io/docs/latest/develop/clients/nodejs/transpipe#execute-a-pipeline)\nfor more information).\n\nYou can also create a pipeline with explicit commands in both clients.\nWith `ioredis`, you use the `pipeline()` command with a chain of\ncommands, ending with `exec()` to run the pipeline:\n\n[code example]\n\nFor `node-redis`, the approach is similar, except that you call the `multi()`\ncommand to start the pipeline and `execAsPipeline()` to run it:\n\n[code example]"},{"id":"scan-iteration","title":"Scan iteration","role":"content","text":"`ioredis` supports the `scanStream()` method to create a readable stream\nfrom the set of keys returned by the [`SCAN`](https://redis.io/docs/latest/commands/scan)\ncommand:\n\n[code example]\n\nYou can also use the similar `hscanStream()`, `sscanStream()`, and\n`zscanStream()` to iterate over the items of a hash, set, or sorted set,\nrespectively.\n\n`node-redis` handles scan iteration using the `scanIterator()` method\n(and the corresponding `hscanIterator()`, `sscanIterator()`, and\n`zscanIterator()` methods). These return a collection object for\neach page scanned by the cursor (this can be helpful to improve\nefficiency using [`MGET`](https://redis.io/docs/latest/commands/mget) and\nother multi-key commands):\n\n[code example]"},{"id":"subscribing-to-channels","title":"Subscribing to channels","role":"content","text":"`ioredis` reports incoming pub/sub messages with a `message`\nevent on the client object (see\n[Publish/subscribe](https://redis.io/docs/latest/develop/pubsub) for more\ninformation about messages):\n\n[code example]\n\nWith `node-redis`, you use the `subscribe()` command to register the\nmessage callback. Also, when you use a connection to subscribe, that\nconnection can't issue any other commands, so you must create a\ndedicated connection for the subscription. Use the `client.duplicate()`\nmethod to create a new connection with the same settings as the original:\n\n[code example]"},{"id":"setnx-command","title":"`SETNX` command","role":"content","text":"`ioredis` implements the  [`SETNX`](https://redis.io/docs/latest/commands/setnx)\ncommand with an explicit method:\n\n[code example]\n\n`node-redis` provides a `SETNX` method but this command is deprecated. Use the `NX` option to the [`SET`](https://redis.io/docs/latest/commands/set)\ncommand to get the same functionality as `SETNX`:\n\n[code example]"},{"id":"hmset-command","title":"`HMSET` command","role":"content","text":"The [`HMSET`](https://redis.io/docs/latest/commands/hmset) command has been deprecated\nsince Redis v4.0.0, but it is still supported by `ioredis`. With `node-redis`\nyou should use the [`HSET`](https://redis.io/docs/latest/commands/hset) command with\nmultiple key-value pairs. See the [`HSET`](https://redis.io/docs/latest/commands/hset)\ncommand page for more information."},{"id":"config-command","title":"`CONFIG` command","role":"content","text":"`ioredis` supports a `config()` method to set or get server configuration\noptions:\n\n[code example]\n\n`node-redis` doesn't have a `config()` method, but instead supports the\nstandard commands [`configSet()`](https://redis.io/docs/latest/commands/config-set),\n[`configGet()`](https://redis.io/docs/latest/commands/config-get),\n[`configResetStat()`](https://redis.io/docs/latest/commands/config-resetstat), and\n[`configRewrite`](https://redis.io/docs/latest/commands/config-rewrite):\n\n[code example]"}],"examples":[{"id":"initial-connection-ex0","language":"js","code":"const client = require('ioredis');\n\n// Connects to localhost:6379 on instantiation.\nconst client = new Redis();","section_id":"initial-connection"},{"id":"initial-connection-ex1","language":"js","code":"import { createClient } from 'redis';\n\nconst client = createClient();\nawait client.connect(); // Requires explicit connection.","section_id":"initial-connection"},{"id":"command-case-ex0","language":"js","code":"// ioredis\nclient.hset('key', 'field', 'value');\n\n// node-redis\nclient.HSET('key', 'field', 'value');\n\n// ...or\nclient.hSet('key', 'field', 'value');","section_id":"command-case"},{"id":"command-argument-handling-ex0","language":"js","code":"// Equivalent to the command line `SET key 100 EX 10`.\nclient.set('key', 100, 'EX', 10);","section_id":"command-argument-handling"},{"id":"command-argument-handling-ex1","language":"js","code":"// These commands are all equivalent.\nclient.hset('user', {\n    name: 'Bob',\n    age: 20,\n    description: 'I am a programmer',\n});\n\nclient.hset('user', ['name', 'Bob', 'age', 20, 'description', 'I am a programmer']);\n\nclient.hset('user', 'name', 'Bob', 'age', 20, 'description', 'I am a programmer');","section_id":"command-argument-handling"},{"id":"command-argument-handling-ex2","language":"js","code":"// Equivalent to the command line `SET bike:5 bike EX 10`.\nclient.set('bike:5', 'bike', {EX: 10});","section_id":"command-argument-handling"},{"id":"async-result-ex0","language":"js","code":"// Callback\nclient.get('mykey', (err, result) => {\n  if (err) {\n    console.error(err);\n  } else {\n    console.log(result);\n  }\n});\n\n// Promise\nclient.get('mykey').then(\n    (result) => {\n        console.log(result);\n    },\n    (err) => {\n        console.error(err);\n    }\n);","section_id":"async-result"},{"id":"async-result-ex1","language":"js","code":"// Promise\nawait client.set('mykey', 'myvalue');\n\n// Callback\nconst legacyClient = client.legacy();\nlegacyClient.set(\"mykey\", \"myvalue\", (err, result) => {\n  if (err) {\n    console.error(err);\n  } else {\n    console.log(result);\n  }\n});","section_id":"async-result"},{"id":"arbitrary-command-execution-ex0","language":"js","code":"await client.call('JSON.SET', 'doc', \"$\", '{\"f1\": {\"a\":1}, \"f2\":{\"a\":2}}');","section_id":"arbitrary-command-execution"},{"id":"arbitrary-command-execution-ex1","language":"js","code":"await client.sendCommand(['hset', 'hash2', 'number', '3']);","section_id":"arbitrary-command-execution"},{"id":"arbitrary-command-execution-ex2","language":"js","code":"const responses = await client.multi()\n  .addCommand(['hset', 'hash3', 'number', '4'])\n  .hGet('hash3', 'number')\n  .exec();","section_id":"arbitrary-command-execution"},{"id":"pipelining-ex0","language":"js","code":"// ioredis example\nclient.pipeline()\n    .set('foo', '1')\n    .get('foo')\n    .set('foo', '2')\n    .incr('foo')\n    .get('foo')\n    .exec(function (err, results) {\n      // Handle results or errors.\n    });","section_id":"pipelining"},{"id":"pipelining-ex1","language":"js","code":"client.multi()\n    .set('seat:3', '#3')\n    .set('seat:4', '#4')\n    .set('seat:5', '#5')\n    .execAsPipeline()\n    .then((results) => {\n        // Handle array of results.\n    },\n    (err) => {\n        // Handle errors.\n    });","section_id":"pipelining"},{"id":"scan-iteration-ex0","language":"js","code":"const client = new Redis();\n// Create a readable stream (object mode)\nconst stream = client.scanStream();\nstream.on('data', (resultKeys) => {\n  // `resultKeys` is an array of strings representing key names.\n  // Note that resultKeys may contain 0 keys, and that it will sometimes\n  // contain duplicates due to SCAN's implementation in Redis.\n  for (let i = 0; i < resultKeys.length; i++) {\n    console.log(resultKeys[i]);\n  }\n});\nstream.on('end', () => {\n  console.log('all keys have been visited');\n});","section_id":"scan-iteration"},{"id":"scan-iteration-ex1","language":"js","code":"for await (const keys of client.scanIterator()) {\n  const values = await client.mGet(keys);\n  // Process values...\n}","section_id":"scan-iteration"},{"id":"subscribing-to-channels-ex0","language":"js","code":"client.on('message', (channel, message) => {\n   console.log(Received message from ${channel}: ${message});\n});","section_id":"subscribing-to-channels"},{"id":"subscribing-to-channels-ex1","language":"js","code":"const subscriber = client.duplicate();\nawait subscriber.connect();\n\nawait subscriber.subscribe('channel', (message) => {\n   console.log(Received message: ${message});\n});","section_id":"subscribing-to-channels"},{"id":"setnx-command-ex0","language":"js","code":"client.setnx('bike:1', 'bike');","section_id":"setnx-command"},{"id":"setnx-command-ex1","language":"js","code":"await client.set('bike:1', 'bike', {'NX': true});","section_id":"setnx-command"},{"id":"config-command-ex0","language":"js","code":"client.config('SET', 'notify-keyspace-events', 'KEA');","section_id":"config-command"},{"id":"config-command-ex1","language":"js","code":"await client.configSet('maxclients', '2000');","section_id":"config-command"}]}
{"schema_version":2,"id":"develop/clients/nodejs/observability","title":"Observability","url":"https://redis.io/docs/latest/develop/clients/nodejs/observability/","summary":"Monitor your client's activity for optimization and debugging.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-08T15:03:39+01:00","page_type":"content","content_hash":"c20b21a4c320f2caea6c5d8614edd7a72ea23ba3cb3ab0f3e8d88bdd7328b1b6","sections":[{"id":"installation","title":"Installation","role":"setup","text":"Install the OTel dependencies with the following commands:\n\n[code example]"},{"id":"import","title":"Import","role":"content","text":"Start by importing the required OTel and Redis modules:\n\nFoundational: Import required libraries for Redis observability, OpenTelemetry metrics, and Redis operations\n\n**Difficulty:** Beginner\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]"},{"id":"configure-the-meter-provider","title":"Configure the meter provider","role":"configuration","text":"Otel uses a [Meter provider](https://opentelemetry.io/docs/concepts/signals/metrics/#meter-provider)\nto create the objects that collect the metric information. The example below\nconfigures a meter provider to export metrics to a local Grafana instance\nevery 10 seconds, but see the [OpenTelemetry Node.js docs](https://opentelemetry.io/docs/languages/js/)\nto learn more about other export options.\n\nFoundational: Configure a meter provider to export metrics to a local Grafana instance every 10 seconds\n\n**Difficulty:** Beginner\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]"},{"id":"configure-the-redis-client","title":"Configure the Redis client","role":"configuration","text":"You configure the client library for OTel only once per application. This will\nenable OTel for all Redis connections you create. The example below shows the\noptions you can pass to the observability instance via the `MetricConfig` object\nduring initialization.\n\nFoundational: Configure Redis observability with a list of metric groups and optional command filtering and privacy controls\n\n**Difficulty:** Beginner\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]\n\n\n\nThe available options for `MetricConfig` are described in the table below:\n\n| Property | Default | Description |\n| -------- | ------- | ----------- |\n| `enabled` | `false` | Set this to `true` explicitly to enable metrics. |\n| `meterProvider` |  | Uses this provider instead of the global provider from `@opentelemetry/api`. |\n| `includeCommands` | `[]` | List of Redis commands to track. If set, only these commands will be tracked. Note that you should use the Redis command name rather than the node-redis method name where the two differ. |\n| `excludeCommands` | `[]` | List of Redis commands to exclude from tracking. If set, all commands except these will be tracked. Note that you should use the Redis command name rather than the node-redis method name where the two differ. |\n| `enabledMetricGroups` | `['connection-basic', 'resiliency']` | List of metric groups to enable. By default, only `connection-basic` and `resiliency` are enabled. See [Redis metric groups](https://redis.io/docs/latest/develop/clients/observability#redis-metric-groups) for a list of available groups. |\n| `hidePubSubChannelNames` | `false` | If true, channel names in pub/sub metrics will be hidden. |\n| `hideStreamNames` | `false` | If true, stream names in streaming metrics will be hidden. |\n| `bucketsOperationDuration` | `[0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 5, 10]` | List of bucket boundaries for the [`operation.duration`](https://redis.io/docs/latest/develop/clients/observability#metric-db.client.operation.duration) histogram (see [Custom histogram buckets](#custom-histogram-buckets) below). |\n| `bucketsConnectionCreateTime` | `[0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 5, 10]` | List of bucket boundaries for the [`connection.create_time`](https://redis.io/docs/latest/develop/clients/observability#metric-db.client.connection.create_time) histogram (see [Custom histogram buckets](#custom-histogram-buckets) below). |\n| `bucketsConnectionWaitTime` | `[0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 5, 10]` | List of bucket boundaries for the [`connection.wait_time`](https://redis.io/docs/latest/develop/clients/observability#metric-db.client.connection.wait_time) histogram (see [Custom histogram buckets](#custom-histogram-buckets) below). |\n| `bucketsStreamProcessingDuration` | `[0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 5, 10]` | List of bucket boundaries for the [`stream.lag`](https://redis.io/docs/latest/develop/clients/observability#metric-redis.client.stream.lag) histogram (see [Custom histogram buckets](#custom-histogram-buckets) below). |"},{"id":"custom-histogram-buckets","title":"Custom histogram buckets","role":"content","text":"For the histogram metrics, a reasonable default set of buckets is defined, but\nyou can customize the bucket boundaries to suit your needs (the buckets are the\nranges of data values counted for each bar of the histogram). Pass an increasing\nlist of float values to the `bucketsXxx` options when you create the `MetricConfig`\nobject. The first and last values in the list are the lower and upper bounds of the\nhistogram, respectively, and the values in between define the bucket boundaries."},{"id":"use-redis","title":"Use Redis","role":"content","text":"Once you have configured the client, all Redis connections you create will be\nautomatically instrumented and the collected metrics will be exported to your\nconfigured destination.\n\nThe example below shows the simplest Redis connection and a few commands,\nbut see the\n[observability demonstration repository](https://github.com/redis-developer/redis-client-observability)\nfor an example that calls a variety of commands in a more realistic way.\n\nFoundational: Use Redis with automatic instrumentation\n\n**Difficulty:** Beginner\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]"},{"id":"shutdown","title":"Shutdown","role":"content","text":"When your application exits, you should call the `shutdown()` method to ensure\nthat all pending metrics are exported.\n\nFoundational: Shutdown Redis observability\n\n**Difficulty:** Beginner\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]"}],"examples":[{"id":"installation-ex0","language":"bash","code":"npm install @opentelemetry/api\nnpm install @opentelemetry/sdk-metrics","section_id":"installation"},{"id":"import-ex0","language":"javascript","code":"import { createClient, OpenTelemetry } from 'redis';\nimport { metrics } from '@opentelemetry/api';\nimport {\n  ConsoleMetricExporter,\n  MeterProvider,\n  PeriodicExportingMetricReader\n} from '@opentelemetry/sdk-metrics';","section_id":"import"},{"id":"configure-the-meter-provider-ex0","language":"javascript","code":"const reader = new PeriodicExportingMetricReader({\n    exporter: new ConsoleMetricExporter(),\n});\n\nconst meterProvider = new MeterProvider({\n    readers: [reader]\n});\n\nmetrics.setGlobalMeterProvider(meterProvider);","section_id":"configure-the-meter-provider"},{"id":"configure-the-redis-client-ex0","language":"javascript","code":"OpenTelemetry.init({\n    metrics: {\n        enabled: true,\n        enabledMetricGroups: [\"command\", \"pubsub\", \"streaming\", \"resiliency\"],\n        includeCommands: [\"GET\", \"HSET\", \"XREADGROUP\", \"PUBLISH\"],\n        excludeCommands: [\"SET\"],\n        hidePubSubChannelNames: true,\n        hideStreamNames: false,\n        bucketsOperationDuration: [0.001, 0.01, 0.1, 1],\n        bucketsStreamProcessingDuration: [0.01, 0.1, 1, 5],\n    },\n});","section_id":"configure-the-redis-client"},{"id":"use-redis-ex0","language":"javascript","code":"const client = createClient();\n\nawait client.connect();\n\nawait client.set('key', 'value');\nconst value = await client.get('key');\nconsole.log(value); // >>> value","section_id":"use-redis"},{"id":"shutdown-ex0","language":"javascript","code":"await meterProvider.forceFlush();\nawait meterProvider.shutdown();\nawait client.destroy();","section_id":"shutdown"}]}
{"schema_version":2,"id":"develop/clients/nodejs/prob","title":"Probabilistic data types","url":"https://redis.io/docs/latest/develop/clients/nodejs/prob/","summary":"Learn how to use approximate calculations with Redis.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-08T15:03:39+01:00","page_type":"content","content_hash":"9e7c9e7b17fe376200b6aca2b0adeb97dd42b726b4b48442c87de8540f3a26fc","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis supports several\n[probabilistic data types](https://redis.io/docs/latest/develop/data-types/probabilistic)\nthat let you calculate values approximately rather than exactly.\nThe types fall into two basic categories:\n\n-   [Set operations](#set-operations): These types let you calculate (approximately)\n    the number of items in a set of distinct values, and whether or not a given value is\n    a member of a set.\n-   [Statistics](#statistics): These types give you an approximation of\n    statistics such as the quantiles, ranks, and frequencies of numeric data points in\n    a list.\n\nTo see why these approximate calculations would be useful, consider the task of\ncounting the number of distinct IP addresses that access a website in one day.\n\nAssuming that you already have code that supplies you with each IP\naddress as a string, you could record the addresses in Redis using\na [set](https://redis.io/docs/latest/develop/data-types/sets):\n\n[code example]\n\nThe set can only contain each key once, so if the same address\nappears again during the day, the new instance will not change\nthe set. At the end of the day, you could get the exact number of\ndistinct addresses using the `sCard()` function:\n\n[code example]\n\nThis approach is simple, effective, and precise but if your website\nis very busy, the `ip_tracker` set could become very large and consume\na lot of memory.\n\nYou would probably round the count of distinct IP addresses to the\nnearest thousand or more to deliver the usage statistics, so\ngetting it exactly right is not important. It would be useful\nif you could trade off some accuracy in exchange for lower memory\nconsumption. The probabilistic data types provide exactly this kind of\ntrade-off. Specifically, you can count the approximate number of items in a\nset using the [HyperLogLog](#set-cardinality) data type, as described below.\n\nIn general, the probabilistic data types let you perform approximations with a\nbounded degree of error that have much lower memory consumption or execution\ntime than the equivalent precise calculations."},{"id":"set-operations","title":"Set operations","role":"content","text":"Redis supports the following approximate set operations:\n\n-   [Membership](#set-membership): The\n    [Bloom filter](https://redis.io/docs/latest/develop/data-types/probabilistic/bloom-filter) and\n    [Cuckoo filter](https://redis.io/docs/latest/develop/data-types/probabilistic/cuckoo-filter)\n    data types let you track whether or not a given item is a member of a set.\n-   [Cardinality](#set-cardinality): The\n    [HyperLogLog](https://redis.io/docs/latest/develop/data-types/probabilistic/hyperloglogs)\n    data type gives you an approximate value for the number of items in a set, also\n    known as the *cardinality* of the set.\n\nThe sections below describe these operations in more detail."},{"id":"set-membership","title":"Set membership","role":"content","text":"[Bloom filter](https://redis.io/docs/latest/develop/data-types/probabilistic/bloom-filter) and\n[Cuckoo filter](https://redis.io/docs/latest/develop/data-types/probabilistic/cuckoo-filter)\nobjects provide a set membership operation that lets you track whether or not a\nparticular item has been added to a set. These two types provide different\ntrade-offs for memory usage and speed, so you can select the best one for your\nuse case. Note that for both types, there is an asymmetry between presence and\nabsence of items in the set. If an item is reported as absent, then it is definitely\nabsent, but if it is reported as present, then there is a small chance it may really be\nabsent.\n\nInstead of storing strings directly, like a [set](https://redis.io/docs/latest/develop/data-types/sets),\na Bloom filter records the presence or absence of the\n[hash value](https://en.wikipedia.org/wiki/Hash_function) of a string.\nThis gives a very compact representation of the\nset's membership with a fixed memory size, regardless of how many items you\nadd. The following example adds some names to a Bloom filter representing\na list of users and checks for the presence or absence of users in the list.\nNote that you must use the `bf` property to access the Bloom filter commands.\n\n[code example]\n\nA Cuckoo filter has similar features to a Bloom filter, but also supports\na deletion operation to remove hashes from a set, as shown in the example\nbelow. Note that you must use the `cf` property to access the Cuckoo filter\ncommands.\n\n[code example]\n\nWhich of these two data types you choose depends on your use case.\nBloom filters are generally faster than Cuckoo filters when adding new items,\nand also have better memory usage. Cuckoo filters are generally faster\nat checking membership and also support the delete operation. See the\n[Bloom filter](https://redis.io/docs/latest/develop/data-types/probabilistic/bloom-filter) and\n[Cuckoo filter](https://redis.io/docs/latest/develop/data-types/probabilistic/cuckoo-filter)\nreference pages for more information and comparison between the two types."},{"id":"set-cardinality","title":"Set cardinality","role":"content","text":"A [HyperLogLog](https://redis.io/docs/latest/develop/data-types/probabilistic/hyperloglogs)\nobject calculates the cardinality of a set. As you add\nitems, the HyperLogLog tracks the number of distinct set members but\ndoesn't let you retrieve them or query which items have been added.\nYou can also merge two or more HyperLogLogs to find the cardinality of the\n[union](https://en.wikipedia.org/wiki/Union_(set_theory)) of the sets they\nrepresent.\n\n[code example]\n\nThe main benefit that HyperLogLogs offer is their very low\nmemory usage. They can count up to 2^64 items with less than\n1% standard error using a maximum 12KB of memory. This makes\nthem very useful for counting things like the total of distinct\nIP addresses that access a website or the total of distinct\nbank card numbers that make purchases within a day."},{"id":"statistics","title":"Statistics","role":"content","text":"Redis supports several approximate statistical calculations\non numeric data sets:\n\n-   [Frequency](#frequency): The\n    [Count-min sketch](https://redis.io/docs/latest/develop/data-types/probabilistic/count-min-sketch)\n    data type lets you find the approximate frequency of a labeled item in a data stream.\n-   [Quantiles](#quantiles): The\n    [t-digest](https://redis.io/docs/latest/develop/data-types/probabilistic/t-digest)\n    data type estimates the quantile of a query value in a data stream.\n-   [Ranking](#ranking): The\n    [Top-K](https://redis.io/docs/latest/develop/data-types/probabilistic/top-k) data type\n    estimates the ranking of labeled items by frequency in a data stream.\n\nThe sections below describe these operations in more detail."},{"id":"frequency","title":"Frequency","role":"content","text":"A [Count-min sketch](https://redis.io/docs/latest/develop/data-types/probabilistic/count-min-sketch)\n(CMS) object keeps count of a set of related items represented by\nstring labels. The count is approximate, but you can specify\nhow close you want to keep the count to the true value (as a fraction)\nand the acceptable probability of failing to keep it in this\ndesired range. For example, you can request that the count should\nstay within 0.1% of the true value and have a 0.05% probability\nof going outside this limit. The example below shows how to create\na Count-min sketch object, add data to it, and then query it.\nNote that you must use the `cms` property to access the Count-min\nsketch commands.\n\n[code example]\n\nThe advantage of using a CMS over keeping an exact count with a\n[sorted set](https://redis.io/docs/latest/develop/data-types/sorted-sets)\nis that a CMS has very low and fixed memory usage, even for\nlarge numbers of items. Use CMS objects to keep daily counts of\nitems sold, accesses to individual web pages on your site, and\nother similar statistics."},{"id":"quantiles","title":"Quantiles","role":"content","text":"A [quantile](https://en.wikipedia.org/wiki/Quantile) is the value\nbelow which a certain fraction of samples lie. For example, with\na set of measurements of people's heights, the quantile of 0.75 is\nthe value of height below which 75% of all people's heights lie.\n[Percentiles](https://en.wikipedia.org/wiki/Percentile) are equivalent\nto quantiles, except that the fraction is expressed as a percentage.\n\nA [t-digest](https://redis.io/docs/latest/develop/data-types/probabilistic/t-digest)\nobject can estimate quantiles from a set of values added to it\nwithout having to store each value in the set explicitly. This can\nsave a lot of memory when you have a large number of samples.\n\nThe example below shows how to add data samples to a t-digest\nobject and obtain some basic statistics, such as the minimum and\nmaximum values, the quantile of 0.75, and the \n[cumulative distribution function](https://en.wikipedia.org/wiki/Cumulative_distribution_function)\n(CDF), which is effectively the inverse of the quantile function. It also\nshows how to merge two or more t-digest objects to query the combined\ndata set. Note that you must use the `tDigest` property to access the\nt-digest commands.\n\n[code example]\n\nA t-digest object also supports several other related commands, such\nas querying by rank. See the\n[t-digest](https://redis.io/docs/latest/develop/data-types/probabilistic/t-digest)\nreference for more information."},{"id":"ranking","title":"Ranking","role":"content","text":"A [Top-K](https://redis.io/docs/latest/develop/data-types/probabilistic/top-k)\nobject estimates the rankings of different labeled items in a data\nstream according to frequency. For example, you could use this to\ntrack the top ten most frequently-accessed pages on a website, or the\ntop five most popular items sold.\n\nThe example below adds several different items to a Top-K object\nthat tracks the top three items (this is the second parameter to\nthe `topK.reserve()` method). It also shows how to list the\ntop *k* items and query whether or not a given item is in the\nlist. Note that you must use the `topK` property to access the\nTop-K commands.\n\n[code example]"}],"examples":[{"id":"overview-ex0","language":"js","code":"await client.sAdd(\"ip_tracker\", new_ip_address);","section_id":"overview"},{"id":"overview-ex1","language":"js","code":"const num_distinct_ips = await client.sCard(\"ip_tracker\");","section_id":"overview"},{"id":"set-membership-ex0","language":"js","code":"const res1 = await client.bf.mAdd(\n    \"recorded_users\",\n    [\"andy\", \"cameron\", \"david\", \"michelle\"]\n);\nconsole.log(res1);  // >>> [true, true, true, true]\n\nconst res2 = await client.bf.exists(\"recorded_users\", \"cameron\");\nconsole.log(res2);  // >>> true\n\nconst res3 = await client.bf.exists(\"recorded_users\", \"kaitlyn\");\nconsole.log(res3);  // >>> false","section_id":"set-membership"},{"id":"set-membership-ex1","language":"js","code":"const res4 = await client.cf.add(\"other_users\", \"paolo\");\nconsole.log(res4);  // >>> true\n\nconst res5 = await client.cf.add(\"other_users\", \"kaitlyn\");\nconsole.log(res5);  // >>> true\n\nconst res6 = await client.cf.add(\"other_users\", \"rachel\");\nconsole.log(res6);  // >>> true\n\nconst res7 = await client.cf.exists(\"other_users\", \"paolo\");\nconst res7a = await client.cf.exists(\"other_users\", \"kaitlyn\");\nconst res7b = await client.cf.exists(\"other_users\", \"rachel\");\nconst res7c = await client.cf.exists(\"other_users\", \"andy\");\nconsole.log([res7, res7a, res7b, res7c]);  // >>> [true, true, true, false]\n\nconst res8 = await client.cf.del(\"other_users\", \"paolo\");\nconsole.log(res8);  // >>> true\n\nconst res9 = await client.cf.exists(\"other_users\", \"paolo\");\nconsole.log(res9);  // >>> false","section_id":"set-membership"},{"id":"set-cardinality-ex0","language":"js","code":"const res10 = await client.pfAdd(\"group:1\", [\"andy\", \"cameron\", \"david\"]);\nconsole.log(res10);  // >>> true\n\nconst res11 = await client.pfCount(\"group:1\");\nconsole.log(res11);  // >>> 3\n\nconst res12 = await client.pfAdd(\"group:2\", [\"kaitlyn\", \"michelle\", \"paolo\", \"rachel\"]);\nconsole.log(res12);  // >>> true\n\nconst res13 = await client.pfCount(\"group:2\");\nconsole.log(res13);  // >>> 4\n\nconst res14 = await client.pfMerge(\"both_groups\", [\"group:1\", \"group:2\"]);\nconsole.log(res14);  // >>> OK\n\nconst res15 = await client.pfCount(\"both_groups\");\nconsole.log(res15);  // >>> 7","section_id":"set-cardinality"},{"id":"frequency-ex0","language":"js","code":"// Specify that you want to keep the counts within 0.01\n// (1%) of the true value with a 0.005 (0.5%) chance\n// of going outside this limit.\nconst res16 = await client.cms.initByProb(\"items_sold\", 0.01, 0.005);\nconsole.log(res16);  // >>> OK\n\n// The parameters for `incrBy()` are passed as an array of objects\n// each containing an `item` and `incrementBy` property.\nconst res17 = await client.cms.incrBy(\n    \"items_sold\",\n    [\n        { item: \"bread\", incrementBy: 300},\n        { item: \"tea\", incrementBy: 200},\n        { item: \"coffee\", incrementBy: 200},\n        { item: \"beer\", incrementBy: 100}\n    ]\n);\nconsole.log(res17);  // >>> [300, 200, 200, 100]\n\nconst res18 = await client.cms.incrBy(\n    \"items_sold\",    \n    [\n        { item: \"bread\", incrementBy: 100},\n        { item: \"coffee\", incrementBy: 150}\n    ]\n);\nconsole.log(res18);  // >>> [400, 350]\n\nconst res19 = await client.cms.query(\n    \"items_sold\",\n    [\"bread\", \"tea\", \"coffee\", \"beer\"]\n);\nconsole.log(res19);  // >>> [400, 200, 350, 100]","section_id":"frequency"},{"id":"quantiles-ex0","language":"js","code":"const res20 = await client.tDigest.create(\"male_heights\");\nconsole.log(res20);  // >>> OK\n\nconst res21 = await client.tDigest.add(\n    \"male_heights\",\n    [175.5, 181, 160.8, 152, 177, 196, 164]\n);\nconsole.log(res21);  // >>> OK\n\nconst res22 = await client.tDigest.min(\"male_heights\");\nconsole.log(res22);  // >>> 152\n\nconst res23 = await client.tDigest.max(\"male_heights\");\nconsole.log(res23);  // >>> 196\n\nconst res24 = await client.tDigest.quantile(\"male_heights\", [0.75]);\nconsole.log(res24);  // >>> [181]\n\n// Note that the CDF value for 181 is not exactly\n// 0.75. Both values are estimates.\nconst res25 = await client.tDigest.cdf(\"male_heights\", [181]);\nconsole.log(res25);  // >>> [0.7857142857142857]\n\nconst res26 = await client.tDigest.create(\"female_heights\");\nconsole.log(res26);  // >>> OK\n\nconst res27 = await client.tDigest.add(\n    \"female_heights\",\n    [155.5, 161, 168.5, 170, 157.5, 163, 171]\n);\nconsole.log(res27);  // >>> OK\n\nconst res28 = await client.tDigest.quantile(\"female_heights\", [0.75]);\nconsole.log(res28);  // >>> [170]\n\nconst res29 = await client.tDigest.merge(\n    \"all_heights\", [\"male_heights\", \"female_heights\"]\n);\nconsole.log(res29);  // >>> OK\n\nconst res30 = await client.tDigest.quantile(\"all_heights\", [0.75]);\nconsole.log(res30);  // >>> [175.5]","section_id":"quantiles"},{"id":"ranking-ex0","language":"js","code":"// The `reserve()` method creates the Top-K object with\n// the given key. The parameters are the number of items\n// in the ranking and values for `width`, `depth`, and\n// `decay`, described in the Top-K reference page.\nconst res31 = await client.topK.reserve(\n    \"top_3_songs\", 3,\n    { width: 7, depth: 8, decay: 0.9 }\n);\nconsole.log(res31);  // >>> OK\n\n// The parameters for `incrBy()` are passed as an array of objects\n// each containing an `item` and `incrementBy` property.\nconst res32 = await client.topK.incrBy(\n    \"top_3_songs\",\n    [\n        { item: \"Starfish Trooper\", incrementBy: 3000},\n        { item: \"Only one more time\", incrementBy: 1850},\n        { item: \"Rock me, Handel\", incrementBy: 1325},\n        { item: \"How will anyone know?\", incrementBy: 3890},\n        { item: \"Average lover\", incrementBy: 4098},\n        { item: \"Road to everywhere\", incrementBy: 770}\n    ]\n);\nconsole.log(res32);\n// >>> [null, null, null, 'Rock me, Handel', 'Only one more time', null]\n\nconst res33 = await client.topK.list(\"top_3_songs\");\nconsole.log(res33);\n// >>> ['Average lover', 'How will anyone know?', 'Starfish Trooper']\n\nconst res34 = await client.topK.query(\n    \"top_3_songs\", [\"Starfish Trooper\", \"Road to everywhere\"]\n);\nconsole.log(res34);  // >>> [true, false]","section_id":"ranking"}]}
{"schema_version":2,"id":"develop/clients/nodejs/produsage","title":"Production usage","url":"https://redis.io/docs/latest/develop/clients/nodejs/produsage/","summary":"Get your Node.js app ready for production","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-08T15:03:39+01:00","page_type":"content","content_hash":"29fca2b40152fe17832e9518267c4e1069388005dfb99af0d81789a235930459","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This guide offers recommendations to get the best reliability and\nperformance in your production environment."},{"id":"checklist","title":"Checklist","role":"content","text":"Each item in the checklist below links to the section\nfor a recommendation. Use the checklist icons to record your\nprogress in implementing the recommendations.\n\n[code example]"},{"id":"recommendations","title":"Recommendations","role":"content","text":""},{"id":"handling-errors","title":"Handling errors","role":"content","text":"Node-Redis provides [multiple events to handle various scenarios](https://github.com/redis/node-redis?tab=readme-ov-file#events), among which the most critical is the `error` event.\n\nThis event is triggered whenever an error occurs within the client, and\nit is very important to set a handler to listen for it.\nSee [Error events](https://redis.io/docs/latest/develop/clients/nodejs/error-handling#error-events)\nfor more information and an example of setting an error handler."},{"id":"handling-reconnections","title":"Handling reconnections","role":"content","text":"When the socket closes unexpectedly (without calling the `quit()` or `disconnect()` methods),\nthe client can automatically restore the connection.  A simple\n[exponential backoff](https://en.wikipedia.org/wiki/Exponential_backoff) strategy\nfor reconnection is enabled by default, but you can replace this with your\nown custom strategy. See\n[Reconnect after disconnection](https://redis.io/docs/latest/develop/clients/nodejs/connect#reconnect-after-disconnection)\nfor more information."},{"id":"timeouts","title":"Timeouts","role":"content","text":"To set a timeout for a connection, use the `connectTimeout` option\n(the default timeout is 5 seconds):\n\n[code example]\n\nYou can also set timeouts for individual commands using `AbortController`:\n\n[code example]"},{"id":"command-execution-reliability","title":"Command execution reliability","role":"syntax","text":"By default, `node-redis` reconnects automatically when the connection is lost\n(but see [Handling reconnections](#handling-reconnections), if you want to\ncustomize this behavior). While the connection is down, any commands that you\nexecute will be queued and sent to the server when the connection is restored.\nThis might occasionally cause problems if the connection fails while a\n[non-idempotent](https://en.wikipedia.org/wiki/Idempotence) command\nis being executed. In this case, the command could change the data on the server\nwithout the client removing it from the queue. When the connection is restored,\nthe command will be sent again, resulting in incorrect data.\n\nIf you need to avoid this situation, set the `disableOfflineQueue` option\nto `true` when you create the client. This will cause the client to discard\nunexecuted commands rather than queuing them:\n\n[code example]\n\nUse a separate connection with the queue disabled if you want to avoid queuing\nonly for specific commands."},{"id":"smart-client-handoffs","title":"Smart client handoffs","role":"content","text":"*Smart client handoffs (SCH)* is a feature of Redis Cloud and\nRedis Software servers that lets them actively notify clients\nabout planned server maintenance shortly before it happens. This\nlets a client take action to avoid disruptions in service.\n\nSee [Smart client handoffs](https://redis.io/docs/latest/develop/clients/sch)\nfor more information about SCH and\n[Connect using Smart client handoffs](https://redis.io/docs/latest/develop/clients/nodejs/connect#connect-using-smart-client-handoffs-sch)\nfor example code."}],"examples":[{"id":"checklist-ex0","language":"checklist {id=\"nodeprodlist\"}","code":"- [ ] [Handling errors](#handling-errors)\n- [ ] [Handling reconnections](#handling-reconnections)\n- [ ] [Connection timeouts](#connection-timeouts)\n- [ ] [Command execution reliability](#command-execution-reliability)\n- [ ] [Smart client handoffs](#seamless-client-experience)","section_id":"checklist"},{"id":"timeouts-ex0","language":"js","code":"const client = createClient({\n  socket: {\n    // setting a 10-second timeout  \n    connectTimeout: 10000 // in milliseconds\n  }\n});\nclient.on('error', error => console.error('Redis client error:', error));","section_id":"timeouts"},{"id":"timeouts-ex1","language":"javascript","code":"import { createClient, commandOptions } from 'redis';\n\nconst client = createClient({ url: 'redis://localhost:6379' });\nawait client.connect();\n\nconst ac = new AbortController();\nconst t = setTimeout(() => ac.abort(), 1000);\ntry {\n  const val = await client.get(commandOptions({ signal: ac.signal }), key);\n} finally {\n  clearTimeout(t);\n}","section_id":"timeouts"},{"id":"command-execution-reliability-ex0","language":"js","code":"const client = createClient({\n  disableOfflineQueue: true,\n      .\n      .\n});","section_id":"command-execution-reliability"}]}
{"schema_version":2,"id":"develop/clients/nodejs/queryjson","title":"Index and query documents","url":"https://redis.io/docs/latest/develop/clients/nodejs/queryjson/","summary":"Learn how to use Redis Search with JSON and hash documents.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-16T16:25:49+01:00","page_type":"content","content_hash":"853a498216b091ad0dc90220b854b876b07ddc1e4f4dc08a2c55ff388ddf12b2","sections":[{"id":"initialize","title":"Initialize","role":"content","text":"Make sure that you have [Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack)\nor another Redis server available. Also install the\n[`node-redis`](https://redis.io/docs/latest/develop/clients/nodejs) client library if you\nhaven't already done so.\n\nAdd the following dependencies:\n\nFoundational: Import required modules for Redis client and query operations\n\n**Difficulty:** Beginner\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]"},{"id":"create-data","title":"Create data","role":"content","text":"Create some test data to add to your database. The example data shown\nbelow is compatible with both JSON and hash objects.\n\nFoundational: Define sample data structures for indexing and querying examples\n\n**Difficulty:** Beginner\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]"},{"id":"add-the-index","title":"Add the index","role":"content","text":"Connect to your Redis database. The code below shows the most\nbasic connection but see\n[Connect to the server](https://redis.io/docs/latest/develop/clients/nodejs/connect)\nto learn more about the available connection options.\n\nFoundational: Establish a connection to Redis for query operations\n\n**Difficulty:** Beginner\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]\n\n\n\nCreate an index. In this example, only JSON documents with the key prefix `user:` are indexed. For more information, see [Query syntax](https://redis.io/docs/latest/develop/ai/search-and-query/query).\n\nFirst, drop any existing index to avoid a collision. (The callback is required\nto avoid an error if the index doesn't already exist.)\n\nFoundational: Drop an existing search index safely using error handling to avoid collisions\n\n**Difficulty:** Intermediate\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]\n\n\n\nThen create the index:\n\nFoundational: Create a search index for JSON documents with field definitions and aliases\n\n**Difficulty:** Intermediate\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]"},{"id":"add-the-data","title":"Add the data","role":"content","text":"Add the three sets of user data to the database as\n[JSON](https://redis.io/docs/latest/develop/data-types/json) objects.\nIf you use keys with the `user:` prefix then Redis will index the\nobjects automatically as you add them. Note that placing\nthe commands in a `Promise.all()` call is an easy way to create a\n[pipeline](https://redis.io/docs/latest/develop/clients/nodejs/transpipe),\nwhich is more efficient than sending the commands individually.\n\nFoundational: Add JSON documents with indexed key prefixes using Promise.all() for efficient pipelining\n\n**Difficulty:** Intermediate\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]"},{"id":"query-the-data","title":"Query the data","role":"content","text":"You can now use the index to search the JSON objects. The\n[query](https://redis.io/docs/latest/develop/ai/search-and-query/query)\nbelow searches for objects that have the text \"Paul\" in any field\nand have an `age` value in the range 30 to 40:\n\nQuery data: Execute a full-text search combined with numeric range filtering on indexed JSON documents\n\n**Difficulty:** Intermediate\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]\n\n\n\nSpecify query options to return only the `city` field:\n\nRestrict query results: Project specific fields in query results to reduce data transfer and improve performance\n\n**Difficulty:** Intermediate\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]\n\n\n\nUse an\n[aggregation query](https://redis.io/docs/latest/develop/ai/search-and-query/query/aggregation)\nto count all users in each city.\n\nAggregate query results: Use aggregation queries to group and count results by field values for analytics\n\n**Difficulty:** Advanced\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]"},{"id":"differences-with-hash-documents","title":"Differences with hash documents","role":"content","text":"Indexing for hash documents is very similar to JSON indexing but you\nneed to specify some slightly different options.\n\nWhen you create the schema for a hash index, you don't need to\nadd aliases for the fields, since you use the basic names to access\nthe fields anyway. Also, you must use `HASH` for the `ON` option\nwhen you create the index. The code below shows these changes with\na new index called `hash-idx:users`, which is otherwise the same as\nthe `idx:users` index used for JSON documents in the previous examples.\n\nFirst, drop any existing index to avoid a collision.\n\nFoundational: Drop an existing search index safely using error handling to avoid collisions\n\n**Difficulty:** Intermediate\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]\n\n\n\nThen create the new index:\n\nFoundational: Create a search index for hash documents with HASH type specification\n\n**Difficulty:** Intermediate\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]\n\n\n\nYou use [`hSet()`](https://redis.io/docs/latest/commands/hset) to add the hash\ndocuments instead of [`json.set()`](https://redis.io/docs/latest/commands/json.set),\nbut the same flat `userX` objects work equally well with either\nhash or JSON:\n\nFoundational: Add hash documents with indexed key prefixes using hSet() for automatic indexing\n\n**Difficulty:** Intermediate\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]\n\n\n\nThe query commands work the same here for hash as they do for JSON (but\nthe name of the hash index is different). The format of the result is\nalso the same:\n\nQuery data: Execute the same full-text query pattern on hash documents as on JSON documents\n\n**Difficulty:** Intermediate\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]"},{"id":"bulk-import-with-himport","title":"Bulk import with HIMPORT","role":"content","text":"From Redis Open Source 8.10, you can also add the hash documents with the\n[`HIMPORT`](https://redis.io/docs/latest/commands/himport) command family. This creates the same\nhashes as `hSet()`, but lets Redis store them as\n[compact hashes](https://redis.io/docs/latest/develop/data-types/hashes#compact-hashes), which use\nless memory when many keys share the same field names. Redis Search indexes\nthese hashes in the same way.\n\nDeclare the field names once with\n[`hImportPrepare()`](https://redis.io/docs/latest/commands/himport-prepare), then create each\ndocument with [`hImportSet()`](https://redis.io/docs/latest/commands/himport-set), passing only\nits values, in the same order as the field names. Use `Promise.all()` to send\nall the imports as a [pipeline](https://redis.io/docs/latest/develop/clients/nodejs/transpipe),\nin one round trip:\n\nBulk ingestion: Import hash documents as compact hashes using HIMPORT PREPARE and pipelined HIMPORT SET calls\n\n**Difficulty:** Intermediate\n\n**Builds upon:** add_hash_data\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]\n\n\n\n`hImportSet()` replaces any existing key, so running this code after the\n`hSet()` calls leaves you with the same three documents. The query returns\nthe same result, although the fields can appear in a different order.\n\n> [!NOTE]\n> `HIMPORT` requires `node-redis` v6.2.0 or later, and `node-redis` marks its\n> support as experimental, so the API might change. Redis Cloud and\n> Redis Software don't support `HIMPORT`."},{"id":"more-information","title":"More information","role":"content","text":"See the [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) docs\nfor a full description of all query features with examples."}],"examples":[{"id":"initialize-ex0","language":"javascript","code":"import {\n    createClient,\n    SCHEMA_FIELD_TYPE,\n    FT_AGGREGATE_GROUP_BY_REDUCERS,\n    FT_AGGREGATE_STEPS,\n} from 'redis';","section_id":"initialize"},{"id":"create-data-ex0","language":"javascript","code":"const user1 = {\n    name: 'Paul John',\n    email: 'paul.john@example.com',\n    age: 42,\n    city: 'London'\n};\n\nconst user2 = {\n    name: 'Eden Zamir',\n    email: 'eden.zamir@example.com',\n    age: 29,\n    city: 'Tel Aviv'\n};\n\nconst user3 = {\n    name: 'Paul Zamir',\n    email: 'paul.zamir@example.com',\n    age: 35,\n    city: 'Tel Aviv'\n};","section_id":"create-data"},{"id":"add-the-index-ex0","language":"javascript","code":"const client = await createClient();\nawait client.connect();","section_id":"add-the-index"},{"id":"add-the-index-ex1","language":"javascript","code":"await client.ft.dropIndex('idx:users', { DD: true }).then(() => {}, () => {});","section_id":"add-the-index"},{"id":"add-the-index-ex2","language":"javascript","code":"await client.ft.create('idx:users', {\n    '$.name': {\n        type: SCHEMA_FIELD_TYPE.TEXT,\n        AS: 'name'\n    },\n    '$.city': {\n        type: SCHEMA_FIELD_TYPE.TEXT,\n        AS: 'city'\n    },\n    '$.age': {\n        type: SCHEMA_FIELD_TYPE.NUMERIC,\n        AS: 'age'\n    }\n}, {\n    ON: 'JSON',\n    PREFIX: 'user:'\n});","section_id":"add-the-index"},{"id":"add-the-data-ex0","language":"javascript","code":"const [user1Reply, user2Reply, user3Reply] = await Promise.all([\n    client.json.set('user:1', '$', user1),\n    client.json.set('user:2', '$', user2),\n    client.json.set('user:3', '$', user3)\n]);","section_id":"add-the-data"},{"id":"query-the-data-ex0","language":"javascript","code":"let findPaulResult = await client.ft.search('idx:users', 'Paul @age:[30 40]');\n\nconsole.log(findPaulResult.total); // >>> 1\n\nfindPaulResult.documents.forEach(doc => {\n    console.log(`ID: ${doc.id}, name: ${doc.value.name}, age: ${doc.value.age}`);\n});\n// >>> ID: user:3, name: Paul Zamir, age: 35","section_id":"query-the-data"},{"id":"query-the-data-ex1","language":"javascript","code":"let citiesResult = await client.ft.search('idx:users', '*',{\n    RETURN: 'city'\n});\n\nconsole.log(citiesResult.total); // >>> 3\n\ncitiesResult.documents.forEach(cityDoc => {\n    console.log(cityDoc.value);\n});\n// >>> { city: 'London' }\n// >>> { city: 'Tel Aviv' }\n// >>> { city: 'Tel Aviv' }","section_id":"query-the-data"},{"id":"query-the-data-ex2","language":"javascript","code":"let aggResult = await client.ft.aggregate('idx:users', '*', {\n    STEPS: [{\n        type: FT_AGGREGATE_STEPS.GROUPBY,\n        properties: '@city',\n        REDUCE: [{\n            type: FT_AGGREGATE_GROUP_BY_REDUCERS.COUNT,\n            AS: 'count'\n        }]\n    }]\n});\n\nconsole.log(aggResult.total); // >>> 2\n\naggResult.results.forEach(result => {\n    console.log(`${result.city} - ${result.count}`);\n});\n// >>> London - 1\n// >>> Tel Aviv - 2","section_id":"query-the-data"},{"id":"differences-with-hash-documents-ex0","language":"javascript","code":"await client.ft.dropIndex('hash-idx:users', { DD: true }).then(() => {}, () => {});","section_id":"differences-with-hash-documents"},{"id":"differences-with-hash-documents-ex1","language":"javascript","code":"await client.ft.create('hash-idx:users', {\n    'name': {\n        type: SCHEMA_FIELD_TYPE.TEXT\n    },\n    'city': {\n        type: SCHEMA_FIELD_TYPE.TEXT\n    },\n    'age': {\n        type: SCHEMA_FIELD_TYPE.NUMERIC\n    }\n}, {\n    ON: 'HASH',\n    PREFIX: 'huser:'\n});","section_id":"differences-with-hash-documents"},{"id":"differences-with-hash-documents-ex2","language":"javascript","code":"const [huser1Reply, huser2Reply, huser3Reply] = await Promise.all([\n    client.hSet('huser:1', user1),\n    client.hSet('huser:2', user2),\n    client.hSet('huser:3', user3)\n]);","section_id":"differences-with-hash-documents"},{"id":"differences-with-hash-documents-ex3","language":"javascript","code":"let findPaulHashResult = await client.ft.search(\n    'hash-idx:users', 'Paul @age:[30 40]'\n);\n\nconsole.log(findPaulHashResult.total); // >>> 1\n\nfindPaulHashResult.documents.forEach(doc => {\n    console.log(`ID: ${doc.id}, name: ${doc.value.name}, age: ${doc.value.age}`);\n});\n// >>> ID: huser:3, name: Paul Zamir, age: 35","section_id":"differences-with-hash-documents"},{"id":"bulk-import-with-himport-ex0","language":"javascript","code":"const fields = ['name', 'email', 'age', 'city'];\nawait client.hImportPrepare('huser', fields);\n\nconst himportReplies = await Promise.all(\n    [['huser:1', user1], ['huser:2', user2], ['huser:3', user3]].map(\n        ([key, user]) => client.hImportSet(\n            key, 'huser', fields.map(field => String(user[field]))\n        )\n    )\n);","section_id":"bulk-import-with-himport"}]}
{"schema_version":2,"id":"develop/clients/nodejs/transpipe","title":"Pipelines and transactions","url":"https://redis.io/docs/latest/develop/clients/nodejs/transpipe/","summary":"Learn how to use Redis pipelines and transactions","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-08T15:03:39+01:00","page_type":"content","content_hash":"52b9ac09b652a6bd78f97eafd7e9d4fea63c9456357aa122cd247d1db24b7973","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis lets you send a sequence of commands to the server together in a batch.\nThere are two types of batch that you can use:\n\n-   **Pipelines** avoid network and processing overhead by sending several commands\n    to the server together in a single communication. The server then sends back\n    a single communication with all the responses. See the\n    [Pipelining](https://redis.io/docs/latest/develop/using-commands/pipelining) page for more\n    information.\n-   **Transactions** guarantee that all the included commands will execute\n    to completion without being interrupted by commands from other clients.\n    See the [Transactions](https://redis.io/docs/latest/develop/using-commands/transactions)\n    page for more information."},{"id":"execute-a-pipeline","title":"Execute a pipeline","role":"content","text":"There are two ways to execute commands in a pipeline. Firstly, `node-redis` will\nautomatically pipeline commands that execute within the same \"tick\" of the\n[event loop](https://nodejs.org/en/learn/asynchronous-work/event-loop-timers-and-nexttick#what-is-the-event-loop).\nYou can ensure that commands happen in the same tick very easily by including them in a\n[`Promise.all()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all)\ncall, as shown in the following example. The chained `then(...)` callback is optional\nand you can often omit it for commands that write data and only return a\nstatus result.\n\n[code example]\n\nYou can also create a pipeline object using the\n[`multi()`](https://redis.io/docs/latest/commands/multi) method\nand then add commands to it using methods that resemble the standard\ncommand methods (for example, `set()` and `get()`). The commands are\nbuffered in the pipeline and only execute when you call the\n`execAsPipeline()` method on the pipeline object. Again, the\n`then(...)` callback is optional.\n\n[code example]\n\nThe two approaches are almost equivalent, but they have different behavior\nwhen the connection is lost during the execution of the pipeline. After\nthe connection is re-established, a `Promise.all()` pipeline will\ncontinue execution from the point where the interruption happened,\nbut a `multi()` pipeline will discard any remaining commands that\ndidn't execute."},{"id":"execute-a-transaction","title":"Execute a transaction","role":"content","text":"A transaction works in a similar way to a pipeline. Create a\ntransaction object with the `multi()` command, call command methods\non that object, and then call the transaction object's \n`exec()` method to execute it.\n\n[code example]"},{"id":"watch-keys-for-changes","title":"Watch keys for changes","role":"content","text":"Redis supports *optimistic locking* to avoid inconsistent updates\nto different keys. The basic idea is to watch for changes to any\nkeys that you use in a transaction while you are processing the\nupdates. If the watched keys do change, you must restart the updates\nwith the latest data from the keys. See\n[Transactions](https://redis.io/docs/latest/develop/using-commands/transactions)\nfor more information about optimistic locking.\n\nThe code below reads a string\nthat represents a `PATH` variable for a command shell, then appends a new\ncommand path to the string before attempting to write it back. If the watched\nkey is modified by another client before writing, the transaction aborts.\nNote that you should call read-only commands for the watched keys synchronously on\nthe usual `client` object but you still call commands for the transaction on the\ntransaction object created with `multi()`.\n\nFor production usage, you would generally call code like the following in\na loop to retry it until it succeeds or else report or log the failure.\n\n[code example]\n\nIn an environment where multiple concurrent requests are sharing a connection\n(such as a web server), you must use a connection pool to get an isolated connection,\nas shown below:\n\n[code example]\n\nThis is important because the server tracks the state of the WATCH on a\nper-connection basis, and concurrent WATCH and MULTI/EXEC calls on the same\nconnection will interfere with one another. See\n[`RedisClientPool`](https://github.com/redis/node-redis/blob/master/docs/pool.md)\nfor more information."}],"examples":[{"id":"execute-a-pipeline-ex0","language":"js","code":"await Promise.all([\n  client.set('seat:0', '#0'),\n  client.set('seat:1', '#1'),\n  client.set('seat:2', '#2'),\n]).then((results) =>{\n    console.log(results);\n    // >>> ['OK', 'OK', 'OK']\n});\n\nawait Promise.all([\n    client.get('seat:0'),\n    client.get('seat:1'),\n    client.get('seat:2'),\n]).then((results) =>{\n    console.log(results);\n    // >>> ['#0', '#1', '#2']\n});","section_id":"execute-a-pipeline"},{"id":"execute-a-pipeline-ex1","language":"js","code":"await client.multi()\n    .set('seat:3', '#3')\n    .set('seat:4', '#4')\n    .set('seat:5', '#5')\n    .execAsPipeline()\n    .then((results) => {\n        console.log(results);\n        // >>> ['OK', 'OK', 'OK']\n    });","section_id":"execute-a-pipeline"},{"id":"execute-a-transaction-ex0","language":"js","code":"const [res1, res2, res3] = await client.multi()\n    .incrBy(\"counter:1\", 1)\n    .incrBy(\"counter:2\", 2)\n    .incrBy(\"counter:3\", 3)\n    .exec();\n\nconsole.log(res1); // >>> 1\nconsole.log(res2); // >>> 2\nconsole.log(res3); // >>> 3","section_id":"execute-a-transaction"},{"id":"watch-keys-for-changes-ex0","language":"js","code":"// Set initial value of `shellpath`.\nclient.set('shellpath', '/usr/syscmds/');\n\n// Watch the key we are about to update.\nawait client.watch('shellpath');\n\nconst currentPath = await client.get('shellpath');\nconst newPath = currentPath + ':/usr/mycmds/';\n\n// Attempt to write the watched key.\nawait client.multi()    \n    .set('shellpath', newPath)\n    .exec()\n    .then((result) => {\n        // This is called when the pipeline executes\n        // successfully.\n        console.log(result);\n    }, (err) => {\n        // This is called when a watched key was changed.\n        // Handle the error here.\n        console.log(err);\n    });\n\nconst updatedPath = await client.get('shellpath');\nconsole.log(updatedPath);\n// >>> /usr/syscmds/:/usr/mycmds/","section_id":"watch-keys-for-changes"},{"id":"watch-keys-for-changes-ex1","language":"js","code":"import { createClientPool } from 'redis';\n\nconst pool = await createClientPool()\n  .on('error', err => console.error('Redis Client Pool Error', err));\n\ntry {\n  await pool.execute(async client => {\n    await client.watch('key');\n\n    const multi = client.multi()\n      .ping()\n      .get('key');\n\n    if (Math.random() > 0.5) {\n      await client.watch('another-key');\n      multi.set('another-key', await client.get('another-key') / 2);\n    }\n\n    return multi.exec();\n  });\n} catch (err) {\n  if (err instanceof WatchError) {\n    // the transaction aborted\n  }\n}","section_id":"watch-keys-for-changes"}]}
{"schema_version":2,"id":"develop/clients/nodejs/vecsearch","title":"Index and query vectors","url":"https://redis.io/docs/latest/develop/clients/nodejs/vecsearch/","summary":"Learn how to index and query vector embeddings with Redis","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-16T16:44:30+01:00","page_type":"content","content_hash":"87b2fd0a51a15fb67eeea5d297f019e22fa0c2cb4bdf9c987ae6c77367e9db9f","sections":[{"id":"initialize","title":"Initialize","role":"content","text":"Install the required dependencies:\n\n1. Install [`node-redis`](https://redis.io/docs/latest/develop/clients/nodejs) if you haven't already.\n2. Install `@xenova/transformers`:\n\n[code example]\n\nIn your JavaScript source file, import the required classes:\n\nFoundational: Import Redis client and transformer modules for vector embedding operations\n\n**Difficulty:** Beginner\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]\n\n\n\nThe `@xenova/transformers` module handles embedding models. This example uses the\n[`all-distilroberta-v1`](https://huggingface.co/sentence-transformers/all-distilroberta-v1)\nmodel, which:\n- Generates 768-dimensional vectors\n- Truncates input to 128 tokens\n- Uses word piece tokenization (see [Word piece tokenization](https://huggingface.co/learn/nlp-course/en/chapter6/6)\n  at the [Hugging Face](https://huggingface.co/) docs for details)\n\nThe `pipe` function generates embeddings. The `pipeOptions` object specifies how to generate sentence embeddings from token embeddings (see the\n[`all-distilroberta-v1`](https://huggingface.co/sentence-transformers/all-distilroberta-v1)\ndocumentation for details):\n\nFoundational: Configure a transformer pipeline to generate vector embeddings from text\n\n**Difficulty:** Beginner\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]"},{"id":"create-the-index","title":"Create the index","role":"content","text":"First, connect to Redis and remove any existing index named `vector_idx`:\n\nFoundational: Connect to Redis and clean up existing vector indexes\n\n**Difficulty:** Beginner\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]\n\n\n\nNext, create the index with the following schema:\n-   `content`: Text field for the content to index\n-   `genre`: [Tag](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/tags)\n    field representing the text's genre\n-   `embedding`: [Vector](https://redis.io/docs/latest/develop/ai/search-and-query/vectors)\n    field with:\n    -   [HNSW](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#hnsw-index)\n        indexing\n    -   [L2](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#distance-metrics)\n        distance metric\n    -   Float32 values\n    -   768 dimensions (matching the embedding model)\n\nFoundational: Create a vector search index with HNSW algorithm and L2 distance metric for semantic search\n\n**Difficulty:** Intermediate\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]"},{"id":"add-data","title":"Add data","role":"content","text":"Add data objects to the index using `hSet()`. The index automatically processes objects with the `doc:` prefix.\n\nFor each document:\n1. Generate an embedding using the `pipe()` function and `pipeOptions`\n2. Convert the embedding to a binary string using `Buffer.from()`\n3. Store the document with `hSet()`\n\nUse `Promise.all()` to batch the commands and reduce network round trips:\n\nPractical pattern: Generate embeddings and store documents with vector fields using pipelined hSet() commands\n\n**Difficulty:** Intermediate\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]"},{"id":"bulk-import-with-himport","title":"Bulk import with HIMPORT","role":"content","text":"From Redis Open Source 8.10, you can also add the documents with the\n[`HIMPORT`](https://redis.io/docs/latest/commands/himport) command family. This creates the same\nhashes as `hSet()`, but lets Redis store them as\n[compact hashes](https://redis.io/docs/latest/develop/data-types/hashes#compact-hashes), which use\nless memory when many keys share the same field names. Redis Search indexes\nthese hashes in the same way.\n\nDeclare the field names once with\n[`hImportPrepare()`](https://redis.io/docs/latest/commands/himport-prepare), then create each\ndocument with [`hImportSet()`](https://redis.io/docs/latest/commands/himport-set), passing only\nits values, in the same order as the field names. Use `Promise.all()` to send\nall the imports as a [pipeline](https://redis.io/docs/latest/develop/clients/nodejs/transpipe),\nin one round trip:\n\nBulk ingestion: Import hash documents with vector embeddings as compact hashes using HIMPORT PREPARE and pipelined HIMPORT SET calls\n\n**Difficulty:** Intermediate\n\n**Builds upon:** add_data\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]\n\n\n\n`hImportSet()` replaces any existing key, so running this code after the\n`hSet()` calls leaves you with the same three documents.\n\n> [!NOTE]\n> `HIMPORT` requires `node-redis` v6.2.0 or later, and `node-redis` marks its\n> support as experimental, so the API might change. Redis Cloud and\n> Redis Software don't support `HIMPORT`."},{"id":"run-a-query","title":"Run a query","role":"content","text":"To query the index:\n1. Generate an embedding for your query text\n2. Pass the embedding as a parameter to the search\n3. Redis calculates vector distances and ranks results\n\nThe query returns an array of document objects. Each object contains:\n- `id`: The document's key\n- `value`: An object with fields specified in the `RETURN` option\n\nVector query: Execute a vector similarity search by generating a query embedding and finding nearest neighbors\n\n**Difficulty:** Intermediate\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]\n\n\n\nThe first run may take longer as it downloads the model data. The output shows results ordered by score (vector distance), with lower scores indicating greater similarity:\n\n[code example]\n\nThe exact distances may differ slightly on your system."},{"id":"differences-with-json-documents","title":"Differences with JSON documents","role":"content","text":"JSON documents support richer data modeling with nested fields. Key differences from hash documents:\n\n1. Use paths in the schema to identify fields\n2. Declare aliases for paths using the `AS` option\n3. Set `ON` to `JSON` when creating the index\n4. Use arrays instead of binary strings for vectors\n5. Use `json.set()` instead of `hSet()`\n\nCreate the index with path aliases:\n\nFoundational: Create a vector search index for JSON documents with path aliases and nested field support\n\n**Difficulty:** Intermediate\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]\n\n\n\nAdd data using `json.set()`. Convert the `Float32Array` to a standard JavaScript array using the spread operator:\n\nPractical pattern: Store JSON documents with vector embeddings converted to arrays for nested field support\n\n**Difficulty:** Intermediate\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]\n\n\n\nQuery JSON documents using the same syntax, but note that the vector parameter must still be a binary string:\n\nVector query: Execute vector similarity search on JSON documents with the same query syntax as hash documents\n\n**Difficulty:** Intermediate\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]\n\n\n\nThe results are identical to the hash document query, except for the `jdoc:` prefix:\n\n[code example]"},{"id":"learn-more","title":"Learn more","role":"related","text":"See\n[Vector search](https://redis.io/docs/latest/develop/ai/search-and-query/query/vector-search)\nfor more information about indexing options, distance metrics, and query format."}],"examples":[{"id":"initialize-ex0","language":"bash","code":"npm install @xenova/transformers","section_id":"initialize"},{"id":"initialize-ex1","language":"javascript","code":"import * as transformers from '@xenova/transformers';\nimport {\n    SCHEMA_VECTOR_FIELD_ALGORITHM,\n    createClient,\n    SCHEMA_FIELD_TYPE,\n} from 'redis';","section_id":"initialize"},{"id":"initialize-ex2","language":"javascript","code":"let pipe = await transformers.pipeline(\n    'feature-extraction', 'Xenova/all-distilroberta-v1'\n);\n\nconst pipeOptions = {\n    pooling: 'mean',\n    normalize: true,\n};","section_id":"initialize"},{"id":"create-the-index-ex0","language":"javascript","code":"const client = createClient({url: 'redis://localhost:6379'});\nawait client.connect();\n\ntry { \n    await client.ft.dropIndex('vector_idx'); \n} catch (e) {\n    // Index doesn't exist, which is fine\n}","section_id":"create-the-index"},{"id":"create-the-index-ex1","language":"javascript","code":"await client.ft.create('vector_idx', {\n    'content': {\n        type: SCHEMA_FIELD_TYPE.TEXT,\n    },\n    'genre': {\n        type: SCHEMA_FIELD_TYPE.TAG,\n    },\n    'embedding': {\n        type: SCHEMA_FIELD_TYPE.VECTOR,\n        TYPE: 'FLOAT32',\n        ALGORITHM: SCHEMA_VECTOR_FIELD_ALGORITHM.HNSW,\n        DISTANCE_METRIC: 'L2',\n        DIM: 768,\n    }\n}, {\n    ON: 'HASH',\n    PREFIX: 'doc:'\n});","section_id":"create-the-index"},{"id":"add-data-ex0","language":"javascript","code":"const sentence1 = 'That is a very happy person';\nconst doc1 = {\n    'content': sentence1, \n    'genre': 'persons', \n    'embedding': Buffer.from(\n        (await pipe(sentence1, pipeOptions)).data.buffer\n    ),\n};\n\nconst sentence2 = 'That is a happy dog';\nconst doc2 = {\n    'content': sentence2, \n    'genre': 'pets', \n    'embedding': Buffer.from(\n        (await pipe(sentence2, pipeOptions)).data.buffer\n    )\n};\n\nconst sentence3 = 'Today is a sunny day';\nconst doc3 = {\n    'content': sentence3, \n    'genre': 'weather', \n    'embedding': Buffer.from(\n        (await pipe(sentence3, pipeOptions)).data.buffer\n    )\n};\n\nawait Promise.all([\n    client.hSet('doc:1', doc1),\n    client.hSet('doc:2', doc2),\n    client.hSet('doc:3', doc3)\n]);","section_id":"add-data"},{"id":"bulk-import-with-himport-ex0","language":"javascript","code":"await client.hImportPrepare('doc', ['content', 'genre', 'embedding']);\n\nawait Promise.all(\n    [doc1, doc2, doc3].map((doc, i) => client.hImportSet(\n        `doc:${i + 1}`, 'doc', [doc.content, doc.genre, doc.embedding]\n    ))\n);","section_id":"bulk-import-with-himport"},{"id":"run-a-query-ex0","language":"javascript","code":"const similar = await client.ft.search(\n    'vector_idx',\n    '*=>[KNN 3 @embedding $B AS score]',\n    {\n        'PARAMS': {\n            B: Buffer.from(\n                (await pipe('That is a happy person', pipeOptions)).data.buffer\n            ),\n        },\n        'RETURN': ['score', 'content'],\n        'DIALECT': '2'\n    },\n);\n\nfor (const doc of similar.documents) {\n    console.log(`${doc.id}: '${doc.value.content}', Score: ${doc.value.score}`);\n}","section_id":"run-a-query"},{"id":"run-a-query-ex1","language":"plaintext","code":"doc:1: 'That is a very happy person', Score: 0.127055495977\ndoc:2: 'That is a happy dog', Score: 0.836842417717\ndoc:3: 'Today is a sunny day', Score: 1.50889515877","section_id":"run-a-query"},{"id":"differences-with-json-documents-ex0","language":"javascript","code":"await client.ft.create('vector_json_idx', {\n    '$.content': {\n        type: SCHEMA_FIELD_TYPE.TEXT,\n        AS: 'content',\n    },\n    '$.genre': {\n        type: SCHEMA_FIELD_TYPE.TAG,\n        AS: 'genre',\n    },\n    '$.embedding': {\n        type: SCHEMA_FIELD_TYPE.VECTOR,\n        TYPE: 'FLOAT32',\n        ALGORITHM: SCHEMA_VECTOR_FIELD_ALGORITHM.HNSW,\n        DISTANCE_METRIC: 'L2',\n        DIM: 768,\n        AS: 'embedding',\n    }\n}, {\n    ON: 'JSON',\n    PREFIX: 'jdoc:'\n});","section_id":"differences-with-json-documents"},{"id":"differences-with-json-documents-ex1","language":"javascript","code":"const jSentence1 = 'That is a very happy person';\nconst jdoc1 = {\n    'content': jSentence1,\n    'genre': 'persons',\n    'embedding': [...(await pipe(jSentence1, pipeOptions)).data],\n};\n\nconst jSentence2 = 'That is a happy dog';\nconst jdoc2 = {\n    'content': jSentence2,\n    'genre': 'pets',\n    'embedding': [...(await pipe(jSentence2, pipeOptions)).data],\n};\n\nconst jSentence3 = 'Today is a sunny day';\nconst jdoc3 = {\n    'content': jSentence3,\n    'genre': 'weather',\n    'embedding': [...(await pipe(jSentence3, pipeOptions)).data],\n};\n\nawait Promise.all([\n    client.json.set('jdoc:1', '$', jdoc1),\n    client.json.set('jdoc:2', '$', jdoc2),\n    client.json.set('jdoc:3', '$', jdoc3)\n]);","section_id":"differences-with-json-documents"},{"id":"differences-with-json-documents-ex2","language":"javascript","code":"const jsons = await client.ft.search(\n    'vector_json_idx',\n    '*=>[KNN 3 @embedding $B AS score]',\n    {\n        \"PARAMS\": {\n            B: Buffer.from(\n                (await pipe('That is a happy person', pipeOptions)).data.buffer\n            ),\n        },\n        'RETURN': ['score', 'content'],\n        'DIALECT': '2'\n    },\n);","section_id":"differences-with-json-documents"},{"id":"differences-with-json-documents-ex3","language":"plaintext","code":"jdoc:1: 'That is a very happy person', Score: 0.127055495977\njdoc:2: 'That is a happy dog', Score: 0.836842417717\njdoc:3: 'Today is a sunny day', Score: 1.50889515877","section_id":"differences-with-json-documents"}]}
{"schema_version":2,"id":"develop/clients/nodejs/vecsets","title":"Vector set embeddings","url":"https://redis.io/docs/latest/develop/clients/nodejs/vecsets/","summary":"Index and query embeddings with Redis vector sets","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-08T15:03:39+01:00","page_type":"content","content_hash":"3d2593cfc48d277097022f9c9eedef4922c2fd8543ce6438bd8bd8a41439d68b","sections":[{"id":"initialize","title":"Initialize","role":"content","text":"Start by [installing](https://redis.io/docs/latest/develop/clients/nodejs#install) `node-redis`\nif you haven't already done so. Also, install `@xenova/transformers`:\n\n[code example]\n\nIn your JavaScript source file, import the required classes:\n\nFoundational: Import Redis client and transformer modules for vector set operations\n\n**Difficulty:** Beginner\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]\n\n\n\nThe first of these imports is the\n`@xenova/transformers` class, which generates an embedding from a section of text.\nThis example uses `transformers.pipeline` with the\n[`all-MiniLM-L6-v2`](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2)\nmodel for the embeddings. This model generates vectors with 384 dimensions, regardless\nof the length of the input text, but note that the input is truncated to 256\ntokens (see\n[Word piece tokenization](https://huggingface.co/learn/nlp-course/en/chapter6/6)\nat the [Hugging Face](https://huggingface.co/) docs to learn more about the way tokens\nare related to the original text).\n\nThe output from `transformers.pipeline` is a function (called `pipe` in the examples)\nthat you can call to generate embeddings. The `pipeOptions` object is a parameter for\n`pipe` that specifies how to generate sentence embeddings from token embeddings (see the\n[`all-MiniLM-L6-v2`](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2)\ndocumentation for details).\n\nFoundational: Configure a transformer pipeline to generate text embeddings for vector set operations\n\n**Difficulty:** Beginner\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]"},{"id":"create-the-data","title":"Create the data","role":"content","text":"The example data is contained in an object with some brief\ndescriptions of famous people:\n\nFoundational: Define sample data structures with descriptions for vector set examples\n\n**Difficulty:** Beginner\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]"},{"id":"add-the-data-to-a-vector-set","title":"Add the data to a vector set","role":"content","text":"The next step is to connect to Redis and add the data to a new vector set.\n\nThe code below iterates through all the key-value pairs in the `peopleData` object\nand adds corresponding elements to a vector set called `famousPeople`.\n\nUse the `pipe()` function created above to generate the\nembedding and then use `Array.from()` to convert the embedding to an array\nof `float32` values that you can pass to the\n[`vAdd()`](https://redis.io/docs/latest/commands/vadd) command to set the embedding.\n\nThe call to `vAdd()` also adds the `born` and `died` values from the\n`peopleData` object as attribute data. You can access this during a query\nor by using the [`vGetAttr()`](https://redis.io/docs/latest/commands/vgetattr) method.\n\nPractical pattern: Generate embeddings and add elements to a vector set with vAdd() and attribute metadata\n\n**Difficulty:** Intermediate\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]"},{"id":"query-the-vector-set","title":"Query the vector set","role":"content","text":"You can now query the data in the set. The basic approach is to use the\n`pipe()` function to generate another embedding vector for the query text.\n(This is the same method used to add the elements to the set.) Then, pass\nthe query vector to [`vSim()`](https://redis.io/docs/latest/commands/vsim) to return elements\nof the set, ranked in order of similarity to the query.\n\nStart with a simple query for \"actors\":\n\nVector set query: Execute a basic vector similarity search using vSim() to find semantically similar elements\n\n**Difficulty:** Intermediate\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]\n\n\n\nThis returns the following list of elements (formatted slightly for clarity):\n\n[code example]\n\nThe first two people in the list are the two actors, as expected, but none of the\npeople from Linus Pauling onward was especially well-known for acting (and there certainly\nisn't any information about that in the short description text).\nAs it stands, the search attempts to rank all the elements in the set, based\non the information contained in the embedding model.\nYou can use the `COUNT` parameter of `vSim()` to limit the list of elements\nto just the most relevant few items:\n\nRestrict results: Limit vector search results using the COUNT parameter to return only the most relevant matches\n\n**Difficulty:** Intermediate\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]\n\n\n\nThe reason for using text embeddings rather than simple text search\nis that the embeddings represent semantic information. This allows a query\nto find elements with a similar meaning even if the text is\ndifferent. For example, the word \"entertainer\" doesn't appear in any of the\ndescriptions but if you use it as a query, the actors and musicians are ranked\nhighest in the results list:\n\nSemantic search: Find elements with similar meaning despite different text\n\n**Difficulty:** Intermediate\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]\n\n\n\nSimilarly, if you use \"science\" as a query, you get the following results:\n\n[code example]\n\nThe scientists are ranked highest but they are then followed by the\nmathematicians. This seems reasonable given the connection between mathematics\nand science.\n\nYou can also use\n[filter expressions](https://redis.io/docs/latest/develop/data-types/vector-sets/filtered-search)\nwith `vSim()` to restrict the search further. For example,\nrepeat the \"science\" query, but this time limit the results to people\nwho died before the year 2000:\n\nPre-filtering: Combine vector similarity search with filter expressions to restrict results based on attribute metadata\n\n**Difficulty:** Advanced\n\n**Available in:** JavaScript (Node.js)\n\n##### JavaScript (Node.js)\n\n[code example]\n\n\n\nNote that the boolean filter expression is applied to items in the list\nbefore the vector distance calculation is performed. Items that don't\npass the filter test are removed from the results completely, rather\nthan just reduced in rank. This can help to improve the performance of the\nsearch because there is no need to calculate the vector distance for\nelements that have already been filtered out of the search."},{"id":"more-information","title":"More information","role":"content","text":"See the [vector sets](https://redis.io/docs/latest/develop/data-types/vector-sets)\ndocs for more information and code examples. See the\n[Redis for AI](https://redis.io/docs/latest/develop/ai) section for more details\nabout text embeddings and other AI techniques you can use with Redis.\n\nYou may also be interested in\n[vector search](https://redis.io/docs/latest/develop/clients/nodejs/vecsearch).\nThis is a feature of\n[Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query)\nthat lets you retrieve\n[JSON](https://redis.io/docs/latest/develop/data-types/json) and\n[hash](https://redis.io/docs/latest/develop/data-types/hashes) documents based on\nvector data stored in their fields."}],"examples":[{"id":"initialize-ex0","language":"bash","code":"npm install @xenova/transformers","section_id":"initialize"},{"id":"initialize-ex1","language":"javascript","code":"import * as transformers from '@xenova/transformers';\nimport { createClient } from 'redis';","section_id":"initialize"},{"id":"initialize-ex2","language":"javascript","code":"const pipe = await transformers.pipeline(\n    'feature-extraction', 'Xenova/all-MiniLM-L6-v2'\n);\n\nconst pipeOptions = {\n    pooling: 'mean',\n    normalize: true,\n};","section_id":"initialize"},{"id":"create-the-data-ex0","language":"javascript","code":"const peopleData = {\n    \"Marie Curie\": {\n        \"born\": 1867, \"died\": 1934,\n        \"description\": `\n        Polish-French chemist and physicist. The only person ever to win\n        two Nobel prizes for two different sciences.\n        `\n    },\n    \"Linus Pauling\": {\n        \"born\": 1901, \"died\": 1994,\n        \"description\": `\n        American chemist and peace activist. One of only two people to win two\n        Nobel prizes in different fields (chemistry and peace).\n        `\n    },\n    \"Freddie Mercury\": {\n        \"born\": 1946, \"died\": 1991,\n        \"description\": `\n        British musician, best known as the lead singer of the rock band\n        Queen.\n        `\n    },\n    \"Marie Fredriksson\": {\n        \"born\": 1958, \"died\": 2019,\n        \"description\": `\n        Swedish multi-instrumentalist, mainly known as the lead singer and\n        keyboardist of the band Roxette.\n        `\n    },\n    \"Paul Erdos\": {\n        \"born\": 1913, \"died\": 1996,\n        \"description\": `\n        Hungarian mathematician, known for his eccentric personality almost\n        as much as his contributions to many different fields of mathematics.\n        `\n    },\n    \"Maryam Mirzakhani\": {\n        \"born\": 1977, \"died\": 2017,\n        \"description\": `\n        Iranian mathematician. The first woman ever to win the Fields medal\n        for her contributions to mathematics.\n        `\n    },\n    \"Masako Natsume\": {\n        \"born\": 1957, \"died\": 1985,\n        \"description\": `\n        Japanese actress. She was very famous in Japan but was primarily\n        known elsewhere in the world for her portrayal of Tripitaka in the\n        TV series Monkey.\n        `\n    },\n    \"Chaim Topol\": {\n        \"born\": 1935, \"died\": 2023,\n        \"description\": `\n        Israeli actor and singer, usually credited simply as 'Topol'. He was\n        best known for his many appearances as Tevye in the musical Fiddler\n        on the Roof.\n        `\n    }\n};","section_id":"create-the-data"},{"id":"add-the-data-to-a-vector-set-ex0","language":"javascript","code":"const client = createClient({ url: 'redis://localhost:6379' });\n\nclient.on('error', err => console.log('Redis Client Error', err));\nawait client.connect();\n\nfor (const [name, details] of Object.entries(peopleData)) {\n    const embedding = await pipe(details.description, pipeOptions);\n    const embeddingArray = Array.from(embedding.data);\n\n    await client.vAdd('famousPeople', embeddingArray, name);\n    await client.vSetAttr('famousPeople', name, JSON.stringify({\n        born: details.born,\n        died: details.died\n    }));\n}","section_id":"add-the-data-to-a-vector-set"},{"id":"query-the-vector-set-ex0","language":"javascript","code":"const queryValue = \"actors\";\n\nconst queryEmbedding = await pipe(queryValue, pipeOptions);\nconst queryArray = Array.from(queryEmbedding.data);\n\nconst actorsResults = await client.vSim('famousPeople', queryArray);\n\nconsole.log(`'actors': ${JSON.stringify(actorsResults)}`);\n// >>> 'actors': [\"Masako Natsume\",\"Chaim Topol\",\"Linus Pauling\",\n// \"Marie Fredriksson\",\"Maryam Mirzakhani\",\"Freddie Mercury\",\n// \"Marie Curie\",\"Paul Erdos\"]","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex1","language":"plaintext","code":"'actors': [\"Masako Natsume\",\"Chaim Topol\",\"Linus Pauling\",\n    \"Marie Fredriksson\",\"Maryam Mirzakhani\",\"Freddie Mercury\",\n    \"Marie Curie\",\"Paul Erdos\"]","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex2","language":"javascript","code":"const queryValue2 = \"actors\";\n\nconst queryEmbedding2 = await pipe(queryValue2, pipeOptions);\nconst queryArray2 = Array.from(queryEmbedding2.data);\n\nconst twoActorsResults = await client.vSim('famousPeople', queryArray2, {\n    COUNT: 2\n});\n\nconsole.log(`'actors (2)': ${JSON.stringify(twoActorsResults)}`);\n// >>> 'actors (2)': [\"Masako Natsume\",\"Chaim Topol\"]","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex3","language":"javascript","code":"const queryValue3 = \"entertainer\";\n\nconst queryEmbedding3 = await pipe(queryValue3, pipeOptions);\nconst queryArray3 = Array.from(queryEmbedding3.data);\n\nconst entertainerResults = await client.vSim('famousPeople', queryArray3);\n\nconsole.log(`'entertainer': ${JSON.stringify(entertainerResults)}`);\n// >>> 'actors': [\"Masako Natsume\",\"Chaim Topol\",\"Linus Pauling\",\n// \"Marie Fredriksson\",\"Maryam Mirzakhani\",\"Freddie Mercury\",\n// \"Marie Curie\",\"Paul Erdos\"]","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex4","language":"plaintext","code":"'science': [\"Linus Pauling\",\"Marie Curie\",\"Maryam Mirzakhani\",\"Paul Erdos\",\n\"Marie Fredriksson\",\"Masako Natsume\",\"Freddie Mercury\",\"Chaim Topol\"]","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex5","language":"javascript","code":"const queryValue5 = \"science\";\n\nconst queryEmbedding5 = await pipe(queryValue5, pipeOptions);\nconst queryArray5 = Array.from(queryEmbedding5.data);\n\nconst science2000Results = await client.vSim('famousPeople', queryArray5, {\n    FILTER: '.died < 2000'\n});\n\nconsole.log(`'science2000': ${JSON.stringify(science2000Results)}`);\n// >>> 'science2000': [\"Linus Pauling\",\"Marie Curie\",\"Paul Erdos\",\n// \"Masako Natsume\",\"Freddie Mercury\"]","section_id":"query-the-vector-set"}]}
{"schema_version":2,"id":"develop/clients/observability","title":"Observability","url":"https://redis.io/docs/latest/develop/clients/observability/","summary":"Monitor your client's activity for optimization and debugging.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-09T13:53:13+01:00","page_type":"content","content_hash":"d9e2e61e69226a9a7ce101d01cda2d1a03e0b6e2eed8de05609ebe0f77189909","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Some Redis client libraries implement the [OpenTelemetry](https://opentelemetry.io/) (OTel)\nobservability framework to let you gather performance metrics\nfor your application. This can help you optimize performance and pinpoint problems\nquickly. Currently, the following clients support OTel:\n\n- [redis-py](https://redis.io/docs/latest/develop/clients/redis-py/observability)\n- [go-redis](https://redis.io/docs/latest/develop/clients/go/observability)\n- [node-redis](https://redis.io/docs/latest/develop/clients/nodejs/observability)"},{"id":"metrics-overview","title":"Metrics overview","role":"content","text":"Metrics are quantitative measurements of the behavior of your application. They\nprovide information such as how often a certain operation occurs, how long it\ntakes to complete, or how many errors have occurred. By analyzing these metrics,\nyou can identify performance bottlenecks, errors, and other issues that need to\nbe addressed.\n\nRedis clients follow the OTel push model. This uses the\n[OLTP](https://opentelemetry.io/docs/specs/otlp/) \nprotocol to send metrics to a [collector](https://opentelemetry.io/docs/collector/)\n(both [OLTP/gRPC](https://opentelemetry.io/docs/specs/otlp/#otlpgrpc) and\n[OTLP/HTTP](https://opentelemetry.io/docs/specs/otlp/#otlphttp) are supported).\nA storage layer (such as [Prometheus](https://prometheus.io/)) can then pull the\nmetrics from a collector endpoint, making the data available for monitoring tools\n(such as [Grafana](https://grafana.com/)) to query and visualize. The basic\nflow of data is shown below.\n\n[code example]\n\nThe [Redis client observability demonstration](https://github.com/redis-developer/redis-client-observability)\non GitHub contains examples showing how to set up a local Grafana instance, then\nconnect it to a Redis client and visualize the metric data as it arrives."},{"id":"redis-metric-groups","title":"Redis metric groups","role":"content","text":"In Redis clients, the metrics collected by OTel are organized into the following\nmetric groups:\n\n- [`resiliency`](#group-resiliency): data related to the availability and health of the Redis connection.\n- [`connection-basic`](#group-connection-basic): minimal metrics about Redis connections made by the client.\n- [`connection-advanced`](#group-connection-advanced): more detailed metrics about Redis connections.\n- [`command`](#group-command): metrics about Redis commands executed by the client.\n- [`client-side-caching`](#group-client-side-caching): metrics about\n  [client-side caching](https://redis.io/docs/latest/develop/clients/client-side-caching) operations.\n- [`streaming`](#group-streaming): metrics about\n  [stream](https://redis.io/docs/latest/develop/data-types/streams) operations.\n- [`pubsub`](#group-pubsub): metrics about\n  [pub/sub](https://redis.io/docs/latest/develop/pubsub) operations.\n\nWhen you configure the client to activate OTel, you can select which metric groups\nyou are interested in. By default, only the `resiliency` and `connection-basic` groups are enabled.\nThe metrics in each group are described in the\n[Metrics reference](#metrics-reference) below."},{"id":"metrics-reference","title":"Metrics reference","role":"content","text":"The metric groups and the metrics they contain are described below. The\nname in parentheses after each group name is the group's identifier, which you\nuse when you configure the client to select which metrics to collect.\n\nThe metrics contain *attributes* that provide extra information (such as\nthe client library and server details) that you can use to filter and\naggregate the data. The attributes are described in the Attributes\nsection following the metric groups. The badge shown after the attribute\nname can be any of the following:\n\n- `required`: This attribute will always be present in the metrics.\n- `optional`: This attribute may be present in the metrics.\n- `conditionally required`: This attribute will be present in the metrics only if a certain condition is met,\n  such as when a specific error occurs. The condition is described in the attribute description.\n- `recommended`: Specific client libraries may not support this attribute in some situations."}],"examples":[{"id":"metrics-overview-ex0","language":"mermaid {width=\"100%\"}","code":"sequenceDiagram\n    participant App as Redis Client\n    participant Collector as OTel Collector\n    participant Storage as Storage Layer<br/>(Prometheus)\n    participant Monitor as Monitoring Tool<br/>(Grafana)\n\n    App->>Collector: Push metrics\n    Note over App,Collector: OTel push model\n\n    Storage->>Collector: Pull metrics from endpoint\n    Collector-->>Storage: Return metrics\n\n    Monitor->>Storage: Query metrics data\n    Storage-->>Monitor: Return metrics\n    Note over Monitor: Visualize","section_id":"metrics-overview"}]}
{"schema_version":2,"id":"develop/clients/om-clients","title":"Object-Mapper libraries","url":"https://redis.io/docs/latest/develop/clients/om-clients/","summary":"Object-Mapper libraries for Redis Open Source","aliases":["/develop/connect/om-clients","/connect/clients/om-clients/","/clients/om-clients/","/develop/connect/clients/om-clients/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-09T14:11:35+01:00","children":[],"page_type":"content","content_hash":"28cce4b617b282b7a5c348e4aab4d7cade6951c3f6fee6277b5e480d7709cc73","sections":[{"id":"content","title":"Content","role":"content","text":"Redis OM (pronounced *REDiss OHM*) is a library that provides object mapping for Redis. With the help of Redis OM, you can map Redis data types, specifically Hashes and JSON documents, to objects of your preferred programming language or framework. Redis OM relies on the JSON and Redis Search features of Redis Open Source, allowing you to search and/or query for objects. \n\nYou can use Redis OM with the following four programming languages:\n\n* [Python](https://redis.io/docs/latest/integrate/redisom-for-python)\n* [C#/.NET](https://redis.io/docs/latest/integrate/redisom-for-net)\n* [Node.js](https://redis.io/docs/latest/integrate/redisom-for-node-js)\n* [Java/Spring](https://redis.io/docs/latest/integrate/redisom-for-java)"}],"examples":[]}
{"schema_version":2,"id":"develop/clients/patterns/bulk-loading","title":"Bulk loading","url":"https://redis.io/docs/latest/develop/clients/patterns/bulk-loading/","summary":"Writing data in bulk using the Redis protocol","aliases":["/develop/use/patterns/bulk-loading","/manual/patterns/bulk-loading/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-09T10:55:35+01:00","page_type":"content","content_hash":"8ec4f7878d83160cfacf6caf366f07433faa8f72a3c4b24e780216e19ab23ff2","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Bulk loading is the process of loading Redis with a large amount of pre-existing data. Ideally, you want to perform this operation quickly and efficiently. This document describes some strategies for bulk loading data in Redis."},{"id":"bulk-loading-using-the-redis-protocol","title":"Bulk loading using the Redis protocol","role":"content","text":"Using a normal Redis client to perform bulk loading is not a good idea\nfor a few reasons: the naive approach of sending one command after the other\nis slow because you have to pay for the round trip time for every command.\nIt is possible to use pipelining, but for bulk loading of many records\nyou need to write new commands while you read replies at the same time to\nmake sure you are inserting as fast as possible.\n\nOnly a small percentage of clients support non-blocking I/O, and not all the\nclients are able to parse the replies in an efficient way in order to maximize\nthroughput. For all of these reasons the preferred way to mass import data into\nRedis is to generate a text file containing the Redis protocol, in raw format,\nin order to call the commands needed to insert the required data.\n\nFor instance if I need to generate a large data set where there are billions\nof keys in the form: `keyN -> ValueN' I will create a file containing the\nfollowing commands in the Redis protocol format:\n\n    SET Key0 Value0\n    SET Key1 Value1\n    ...\n    SET KeyN ValueN\n\nOnce this file is created, the remaining action is to feed it to Redis\nas fast as possible. In the past the way to do this was to use the\n`netcat` with the following command:\n\n    (cat data.txt; sleep 10) | nc localhost 6379 > /dev/null\n\nHowever this is not a very reliable way to perform mass import because netcat\ndoes not really know when all the data was transferred and can't check for\nerrors. In 2.6 or later versions of Redis the `redis-cli` utility\nsupports a new mode called **pipe mode** that was designed in order to perform\nbulk loading.\n\nUsing the pipe mode the command to run looks like the following:\n\n    cat data.txt | redis-cli --pipe\n\nThat will produce an output similar to this:\n\n    All data transferred. Waiting for the last reply...\n    Last reply received from server.\n    errors: 0, replies: 1000000\n\nThe redis-cli utility will also make sure to only redirect errors received\nfrom the Redis instance to the standard output."},{"id":"generating-redis-protocol","title":"Generating Redis Protocol","role":"content","text":"The Redis protocol is extremely simple to generate and parse, and is\n[Documented here](https://redis.io/docs/latest/develop/reference/protocol-spec). However in order to generate protocol for\nthe goal of bulk loading you don't need to understand every detail of the\nprotocol, but just that every command is represented in the following way:\n\n    *<args><cr><lf>\n    $<len><cr><lf>\n    <arg0><cr><lf>\n    <arg1><cr><lf>\n    ...\n    <argN><cr><lf>\n\nWhere `<cr>` means \"\\r\" (or ASCII character 13) and `<lf>` means \"\\n\" (or ASCII character 10).\n\nFor instance the command **SET key value** is represented by the following protocol:\n\n    *3<cr><lf>\n    $3<cr><lf>\n    SET<cr><lf>\n    $3<cr><lf>\n    key<cr><lf>\n    $5<cr><lf>\n    value<cr><lf>\n\nOr represented as a quoted string:\n\n    \"*3\\r\\n$3\\r\\nSET\\r\\n$3\\r\\nkey\\r\\n$5\\r\\nvalue\\r\\n\"\n\nThe file you need to generate for bulk loading is just composed of commands\nrepresented in the above way, one after the other.\n\nThe following Ruby function generates valid protocol:\n\n    def gen_redis_proto(*cmd)\n        proto = \"\"\n        proto << \"*\"+cmd.length.to_s+\"\\r\\n\"\n        cmd.each{|arg|\n            proto << \"$\"+arg.to_s.bytesize.to_s+\"\\r\\n\"\n            proto << arg.to_s+\"\\r\\n\"\n        }\n        proto\n    end\n\n    puts gen_redis_proto(\"SET\",\"mykey\",\"Hello World!\").inspect\n\nUsing the above function it is possible to easily generate the key value pairs\nin the above example, with this program:\n\n    (0...1000).each{|n|\n        STDOUT.write(gen_redis_proto(\"SET\",\"Key#{n}\",\"Value#{n}\"))\n    }\n\nWe can run the program directly in pipe to redis-cli in order to perform our\nfirst mass import session.\n\n    $ ruby proto.rb | redis-cli --pipe\n    All data transferred. Waiting for the last reply...\n    Last reply received from server.\n    errors: 0, replies: 1000"},{"id":"how-the-pipe-mode-works-under-the-hood","title":"How the pipe mode works under the hood","role":"content","text":"The magic needed inside the pipe mode of redis-cli is to be as fast as netcat\nand still be able to understand when the last reply was sent by the server\nat the same time.\n\nThis is obtained in the following way:\n\n+ redis-cli --pipe tries to send data as fast as possible to the server.\n+ At the same time it reads data when available, trying to parse it.\n+ Once there is no more data to read from stdin, it sends a special **ECHO**\ncommand with a random 20 byte string: we are sure this is the latest command\nsent, and we are sure we can match the reply checking if we receive the same\n20 bytes as a bulk reply.\n+ Once this special final command is sent, the code receiving replies starts\nto match replies with these 20 bytes. When the matching reply is reached it\ncan exit with success.\n\nUsing this trick we don't need to parse the protocol we send to the server\nin order to understand how many commands we are sending, but just the replies.\n\nHowever while parsing the replies we take a counter of all the replies parsed\nso that at the end we are able to tell the user the amount of commands\ntransferred to the server by the mass insert session."}],"examples":[]}
{"schema_version":2,"id":"develop/clients/patterns/distributed-locks","title":"Distributed Locks with Redis","url":"https://redis.io/docs/latest/develop/clients/patterns/distributed-locks/","summary":"A distributed lock pattern with Redis","aliases":["/develop/use/patterns/distributed-locks","/manual/patterns/distributed-locks/","/reference/patterns/distributed-locks/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-30T13:16:11-07:00","page_type":"content","content_hash":"15e8738f2384df5db03226f005a613f5bc4a079468cc9e90f65a7af0e2b467a4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Distributed locks are a very useful primitive in many environments where\ndifferent processes must operate with shared resources in a mutually\nexclusive way.\n\nThere are a number of libraries and blog posts describing how to implement\na DLM (Distributed Lock Manager) with Redis, but every library uses a different\napproach, and many use a simple approach with lower guarantees compared to\nwhat can be achieved with slightly more complex designs.\n\nThis page describes a more canonical algorithm to implement\ndistributed locks with Redis. We propose an algorithm, called **Redlock**,\nwhich implements a DLM which we believe to be safer than the vanilla single\ninstance approach. We hope that the community will analyze it, provide\nfeedback, and use it as a starting point for the implementations of more\ncomplex or alternative designs."},{"id":"implementations","title":"Implementations","role":"content","text":"Before describing the algorithm, here are a few links to implementations\nalready available that can be used for reference.\n\n* [Redlock-rb](https://github.com/antirez/redlock-rb) (Ruby implementation). There is also a [fork of Redlock-rb](https://github.com/leandromoreira/redlock-rb) that adds a gem for easy distribution.\n* [RedisQueuedLocks](https://github.com/0exp/redis_queued_locks) (Ruby implementation).\n* [redlock-ng](https://github.com/alwaysvivek/redlock) (Modern Python implementation, sync & async).\n* [Pottery](https://github.com/brainix/pottery#redlock) (Python implementation).\n* [Aioredlock](https://github.com/joanvila/aioredlock) (Asyncio Python implementation).\n* [RedisMutex](https://github.com/malkusch/lock#redismutex) (PHP implementation with both [Redis extension](https://github.com/phpredis/phpredis) and [Predis library](https://github.com/predis/predis) clients support).\n* [Redlock-php](https://github.com/ronnylt/redlock-php) (PHP implementation).\n* [cheprasov/php-redis-lock](https://github.com/cheprasov/php-redis-lock) (PHP library for locks).\n* [rtckit/react-redlock](https://github.com/rtckit/reactphp-redlock) (Async PHP implementation).\n* [Redsync](https://github.com/go-redsync/redsync) (Go implementation).\n* [Redisson](https://github.com/mrniko/redisson) (Java implementation).\n* [Redis::DistLock](https://github.com/sbertrang/redis-distlock) (Perl implementation).\n* [Redlock-cpp](https://github.com/jacket-code/redlock-cpp) (C++ implementation).\n* [Redis-plus-plus](https://github.com/sewenew/redis-plus-plus/#redlock) (C++ implementation).\n* [Redlock-cs](https://github.com/kidfashion/redlock-cs) (C#/.NET implementation).\n* [RedLock.net](https://github.com/samcook/RedLock.net) (C#/.NET implementation). Includes async and lock extension support.\n* [Redlock4Net](https://github.com/LiZhenNet/Redlock4Net) (C# .NET implementation).\n* [node-redlock](https://github.com/mike-marcacci/node-redlock) (NodeJS implementation). Includes support for lock extension.\n* [redlock-universal](https://github.com/alexpota/redlock-universal) (NodeJS implementation). Supports both node-redis and ioredis clients.\n* [Deno DLM](https://github.com/oslabs-beta/Deno-Redlock) (Deno implementation)\n* [Rslock](https://github.com/hexcowboy/rslock) (Rust implementation). Includes async and lock extension support."},{"id":"safety-and-liveness-guarantees","title":"Safety and Liveness Guarantees","role":"content","text":"We are going to model our design with just three properties that, from our point of view, are the minimum guarantees needed to use distributed locks in an effective way.\n\n1. Safety property: Mutual exclusion. At any given moment, only one client can hold a lock.\n2. Liveness property A: Deadlock free. Eventually it is always possible to acquire a lock, even if the client that locked a resource crashes or gets partitioned.\n3. Liveness property B: Fault tolerance. As long as the majority of Redis nodes are up, clients are able to acquire and release locks."},{"id":"why-failover-based-implementations-are-not-enough","title":"Why Failover-based Implementations Are Not Enough","role":"content","text":"To understand what we want to improve, let’s analyze the current state of affairs with most Redis-based distributed lock libraries.\n\nThe simplest way to use Redis to lock a resource is to create a key in an instance. The key is usually created with a limited time to live, using the Redis expires feature, so that eventually it will get released (property 2 in our list). When the client needs to release the resource, it deletes the key.\n\nSuperficially this works well, but there is a problem: this is a single point of failure in our architecture. What happens if the Redis master goes down?\nWell, let’s add a replica! And use it if the master is unavailable. This is unfortunately not viable. By doing so we can’t implement our safety property of mutual exclusion, because Redis replication is asynchronous.\n\nThere is a race condition with this model:\n\n1. Client A acquires the lock in the master.\n2. The master crashes before the write to the key is transmitted to the replica.\n3. The replica gets promoted to master.\n4. Client B acquires the lock to the same resource A already holds a lock for. **SAFETY VIOLATION!**\n\nSometimes it is perfectly fine that, under special circumstances, for example during a failure, multiple clients can hold the lock at the same time.\nIf this is the case, you can use your replication based solution. Otherwise we suggest to implement the solution described in this document."},{"id":"correct-implementation-with-a-single-instance","title":"Correct Implementation with a Single Instance","role":"content","text":"Before trying to overcome the limitation of the single instance setup described above, let’s check how to do it correctly in this simple case, since this is actually a viable solution in applications where a race condition from time to time is acceptable, and because locking into a single instance is the foundation we’ll use for the distributed algorithm described here.\n\nTo acquire the lock, the way to go is the following:\n\n    SET resource_name my_random_value NX PX 30000\n\nThe command will set the key only if it does not already exist (`NX` option), with an expire of 30000 milliseconds (`PX` option).\nThe key is set to a value \"my\\_random\\_value\". This value must be unique across all clients and all lock requests.\n\nBasically the random value is used in order to release the lock in a safe way, with a script that tells Redis: remove the key only if it exists and the value stored at the key is exactly the one I expect to be. \nThis is accomplished with the following command:\n\n    DELEX key IFEQ my_random_value\n\nThe `DELEX` command was introduced in Redis 8.4. On previous Redis versions, this could be accomplished with the following Lua script:\n\n    if redis.call(\"get\",KEYS[1]) == ARGV[1] then\n        return redis.call(\"del\",KEYS[1])\n    else\n        return 0\n    end\n\nThis is important in order to avoid removing a lock that was created by another client. For example a client may acquire the lock, get blocked performing some operation for longer than the lock validity time (the time at which the key will expire), and later remove the lock, that was already acquired by some other client.\nUsing just [`DEL`](https://redis.io/docs/latest/commands/del) is not safe as a client may remove another client's lock. With the `DELEX` command or the above script instead every lock is \"signed\" with a random string, so the lock will be removed only if it is still the one that was set by the client trying to remove it.\n\nWhat should this random string be? We assume it’s 20 bytes from `/dev/urandom`, but you can find cheaper ways to make it unique enough for your tasks.\nFor example a safe pick is to seed RC4 with `/dev/urandom`, and generate a pseudo random stream from that.\nA simpler solution is to use a UNIX timestamp with microsecond precision, concatenating the timestamp with a client ID. It is not as safe, but probably sufficient for most environments.\n\nThe \"lock validity time\" is the time we use as the key's time to live. It is both the auto release time, and the time the client has in order to perform the operation required before another client may be able to acquire the lock again, without technically violating the mutual exclusion guarantee, which is only limited to a given window of time from the moment the lock is acquired.\n\nSo now we have a good way to acquire and release the lock. With this system, reasoning about a non-distributed system composed of a single, always available, instance, is safe. Let’s extend the concept to a distributed system where we don’t have such guarantees."},{"id":"the-redlock-algorithm","title":"The Redlock Algorithm","role":"content","text":"In the distributed version of the algorithm we assume we have N Redis masters. Those nodes are totally independent, so we don’t use replication or any other implicit coordination system. We already described how to acquire and release the lock safely in a single instance. We take for granted that the algorithm will use this method to acquire and release the lock in a single instance. In our examples we set N=5, which is a reasonable value, so we need to run 5 Redis masters on different computers or virtual machines in order to ensure that they’ll fail in a mostly independent way.\n\nIn order to acquire the lock, the client performs the following operations:\n\n1. It gets the current time in milliseconds.\n2. It tries to acquire the lock in all the N instances in parallel, using the same key name and random value in all the instances. During step 2, when setting the lock in each instance, the client uses a timeout which is small compared to the total lock auto-release time in order to acquire it. For example if the auto-release time is 10 seconds, the timeout could be in the ~ 5-50 milliseconds range. This prevents the client from staying blocked too long when communicating with an unavailable Redis node, ensuring the connection attempt times out quickly.\n3. The client computes how much time elapsed in order to acquire the lock, by subtracting from the current time the timestamp obtained in step 1. If and only if the client was able to acquire the lock in the majority of the instances (at least 3), and the total time elapsed to acquire the lock is less than lock validity time, the lock is considered to be acquired.\n4. If the lock was acquired, its validity time is considered to be the initial validity time minus the time elapsed, as computed in step 3.\n5. If the client failed to acquire the lock for some reason (either it was not able to lock N/2+1 instances or the validity time is negative), it will try to unlock all the instances (even the instances it believed it was not able to lock)."},{"id":"is-the-algorithm-asynchronous","title":"Is the Algorithm Asynchronous?","role":"content","text":"The algorithm relies on the assumption that while there is no synchronized clock across the processes, the local time in every process updates at approximately at the same rate, with a small margin of error compared to the auto-release time of the lock. This assumption closely resembles a real-world computer: every computer has a local clock and we can usually rely on different computers to have a clock drift which is small.\n\nAt this point we need to better specify our mutual exclusion rule: it is guaranteed only as long as the client holding the lock terminates its work within the lock validity time (as obtained in step 3), minus some time (just a few milliseconds in order to compensate for clock drift between processes).\n\nThis paper contains more information about similar systems requiring a bound *clock drift*: [Leases: an efficient fault-tolerant mechanism for distributed file cache consistency](http://dl.acm.org/citation.cfm?id=74870)."},{"id":"retry-on-failure","title":"Retry on Failure","role":"content","text":"When a client is unable to acquire the lock, it should try again after a random delay in order to try to desynchronize multiple clients trying to acquire the lock for the same resource at the same time (this may result in a split brain condition where nobody wins). Also the faster a client tries to acquire the lock in the majority of Redis instances, the smaller the window for a split brain condition (and the need for a retry), so ideally the client should try to send the [`SET`](https://redis.io/docs/latest/commands/set) commands to the N instances at the same time using multiplexing.\n\nIt is worth stressing how important it is for clients that fail to acquire the majority of locks, to release the (partially) acquired locks ASAP, so that there is no need to wait for key expiry in order for the lock to be acquired again (however if a network partition happens and the client is no longer able to communicate with the Redis instances, there is an availability penalty to pay as it waits for key expiration)."},{"id":"releasing-the-lock","title":"Releasing the Lock","role":"content","text":"Releasing the lock is simple, and can be performed whether or not the client believes it was able to successfully lock a given instance."},{"id":"safety-arguments","title":"Safety Arguments","role":"content","text":"Is the algorithm safe? Let's examine what happens in different scenarios.\n\nTo start let’s assume that a client is able to acquire the lock in the majority of instances. All the instances will contain a key with the same time to live. However, the key was set at different times, so the keys will also expire at different times. But if the first key was set at worst at time T1 (the time we sample before contacting the first server) and the last key was set at worst at time T2 (the time we obtained the reply from the last server), we are sure that the first key to expire in the set will exist for at least `MIN_VALIDITY=TTL-(T2-T1)-CLOCK_DRIFT`. All the other keys will expire later, so we are sure that the keys will be simultaneously set for at least this time.\n\nDuring the time that the majority of keys are set, another client will not be able to acquire the lock, since N/2+1 SET NX operations can’t succeed if N/2+1 keys already exist. So if a lock was acquired, it is not possible to re-acquire it at the same time (violating the mutual exclusion property).\n\nHowever we want to also make sure that multiple clients trying to acquire the lock at the same time can’t simultaneously succeed.\n\nIf a client locked the majority of instances using a time near, or greater, than the lock maximum validity time (the TTL we use for SET basically), it will consider the lock invalid and will unlock the instances, so we only need to consider the case where a client was able to lock the majority of instances in a time which is less than the validity time. In this case for the argument already expressed above, for `MIN_VALIDITY` no client should be able to re-acquire the lock. So multiple clients will be able to lock N/2+1 instances at the same time (with \"time\" being the end of Step 2) only when the time to lock the majority was greater than the TTL time, making the lock invalid."},{"id":"liveness-arguments","title":"Liveness Arguments","role":"content","text":"The system liveness is based on three main features:\n\n1. The auto release of the lock (since keys expire): eventually keys are available again to be locked.\n2. The fact that clients, usually, will cooperate removing the locks when the lock was not acquired, or when the lock was acquired and the work terminated, making it likely that we don’t have to wait for keys to expire to re-acquire the lock.\n3. The fact that when a client needs to retry a lock, it waits a time which is comparably greater than the time needed to acquire the majority of locks, in order to probabilistically make split brain conditions during resource contention unlikely.\n\nHowever, we pay an availability penalty equal to [`TTL`](https://redis.io/docs/latest/commands/ttl) time on network partitions, so if there are continuous partitions, we can pay this penalty indefinitely.\nThis happens every time a client acquires a lock and gets partitioned away before being able to remove the lock.\n\nBasically if there are infinite continuous network partitions, the system may become not available for an infinite amount of time."},{"id":"performance-crash-recovery-and-fsync","title":"Performance, Crash Recovery and fsync","role":"performance","text":"Many users using Redis as a lock server need high performance in terms of both latency to acquire and release a lock, and number of acquire / release operations that it is possible to perform per second. In order to meet this requirement, the strategy to talk with the N Redis servers to reduce latency is definitely multiplexing (putting the socket in non-blocking mode, send all the commands, and read all the commands later, assuming that the RTT between the client and each instance is similar).\n\nHowever there is another consideration around persistence if we want to target a crash-recovery system model.\n\nBasically to see the problem here, let’s assume we configure Redis without persistence at all. A client acquires the lock in 3 of 5 instances. One of the instances where the client was able to acquire the lock is restarted, at this point there are again 3 instances that we can lock for the same resource, and another client can lock it again, violating the safety property of exclusivity of lock.\n\nIf we enable AOF persistence, things will improve quite a bit. For example we can upgrade a server by sending it a [`SHUTDOWN`](https://redis.io/docs/latest/commands/shutdown) command and restarting it. Because Redis expires are semantically implemented so that time still elapses when the server is off, all our requirements are fine.\nHowever everything is fine as long as it is a clean shutdown. What about a power outage? If Redis is configured, as by default, to fsync on disk every second, it is possible that after a restart our key is missing. In theory, if we want to guarantee the lock safety in the face of any kind of instance restart, we need to enable `fsync=always` in the persistence settings. This will affect performance due to the additional sync overhead.\n\nHowever things are better than they look like at a first glance. Basically,\nthe algorithm safety is retained as long as when an instance restarts after a\ncrash, it no longer participates to any **currently active** lock.  This means that the\nset of currently active locks when the instance restarts were all obtained\nby locking instances other than the one which is rejoining the system.\n\nTo guarantee this we just need to make an instance, after a crash, unavailable\nfor at least a bit more than the max [`TTL`](https://redis.io/docs/latest/commands/ttl) we use.  This is the time needed\nfor all the keys about the locks that existed when the instance crashed to\nbecome invalid and be automatically released.\n\nUsing *delayed restarts* it is basically possible to achieve safety even\nwithout any kind of Redis persistence available, however note that this may\ntranslate into an availability penalty. For example if a majority of instances\ncrash, the system will become globally unavailable for [`TTL`](https://redis.io/docs/latest/commands/ttl) (here globally means\nthat no resource at all will be lockable during this time)."},{"id":"making-the-algorithm-more-reliable-extending-the-lock","title":"Making the algorithm more reliable: Extending the lock","role":"content","text":"If the work performed by clients consists of small steps, it is possible to\nuse smaller lock validity times by default, and extend the algorithm implementing\na lock extension mechanism. Basically the client, if in the middle of the\ncomputation while the lock validity is approaching a low value, may extend the\nlock by sending a Lua script to all the instances that extends the TTL of the key\nif the key exists and its value is still the random value the client assigned\nwhen the lock was acquired.\n\nThe client should only consider the lock re-acquired if it was able to extend\nthe lock into the majority of instances, and within the validity time\n(basically the algorithm to use is very similar to the one used when acquiring\nthe lock).\n\nHowever this does not technically change the algorithm, so the maximum number\nof lock reacquisition attempts should be limited, otherwise one of the liveness\nproperties is violated."},{"id":"disclaimer-about-consistency","title":"Disclaimer about consistency","role":"content","text":"Please consider thoroughly reviewing the [Analysis of Redlock](#analysis-of-redlock) section at the end of this page.\nMartin Kleppman's article and antirez's answer to it are very relevant.\nIf you are concerned about consistency and correctness, you should pay attention to the following topics:\n\n1. You should implement fencing tokens.\n  This is especially important for processes that can take significant time and applies to any distributed locking system.\n  Extending locks' lifetime is also an option, but don´t assume that a lock is retained as long as the process that had acquired it is alive.\n2. Redis is not using monotonic clock for TTL expiration mechanism.\n  That means that a wall-clock shift may result in a lock being acquired by more than one process.\n  Even though the problem can be mitigated by preventing admins from manually setting the server's time and setting up NTP properly, there's still a chance of this issue occurring in real life and compromising consistency."},{"id":"want-to-help","title":"Want to help?","role":"content","text":"If you are into distributed systems, it would be great to have your opinion / analysis. Also reference implementations in other languages could be great.\n\nThanks in advance!"},{"id":"analysis-of-redlock","title":"Analysis of Redlock","role":"content","text":"---\n\n1. Martin Kleppmann [analyzed Redlock here](http://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html). A counterpoint to this analysis can be [found here](http://antirez.com/news/101)."}],"examples":[]}
{"schema_version":2,"id":"develop/clients/patterns","title":"Coding patterns","url":"https://redis.io/docs/latest/develop/clients/patterns/","summary":"Novel patterns for working with Redis data structures","aliases":["/develop/use/patterns","/connect/clients/patterns/","/clients/patterns/","/manual/patterns/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-09T10:55:35+01:00","children":[{"id":"develop/clients/patterns/bulk-loading","summary":"Writing data in bulk using the Redis protocol","title":"Bulk loading","url":"https://redis.io/docs/latest/develop/clients/patterns/bulk-loading/"},{"id":"develop/clients/patterns/distributed-locks","summary":"A distributed lock pattern with Redis","title":"Distributed Locks with Redis","url":"https://redis.io/docs/latest/develop/clients/patterns/distributed-locks/"},{"id":"develop/clients/patterns/indexes/index","summary":"Building secondary indexes in Redis","title":"Secondary indexing","url":"https://redis.io/docs/latest/develop/clients/patterns/indexes/"},{"id":"develop/clients/patterns/twitter-clone","summary":"Learn several Redis patterns by building a Twitter clone","title":"Redis patterns example","url":"https://redis.io/docs/latest/develop/clients/patterns/twitter-clone/"}],"page_type":"content","content_hash":"ecc845f169c8e311197515bc3006edf9e0eda4c1e26db692fc7c60d11bc99101","sections":[{"id":"content","title":"Content","role":"content","text":"The following documents describe some novel development patterns you can use with Redis.\n\n[Bulk loading](https://redis.io/docs/latest/develop/clients/patterns/bulk-loading)\n\nWriting data in bulk using the Redis protocol\n\n[Distributed Locks with Redis](https://redis.io/docs/latest/develop/clients/patterns/distributed-locks)\n\nA distributed lock pattern with Redis\n\n[Secondary indexing](https://redis.io/docs/latest/develop/clients/patterns/indexes)\n\nBuilding secondary indexes in Redis\n\n[Redis patterns example](https://redis.io/docs/latest/develop/clients/patterns/twitter-clone)\n\nLearn several Redis patterns by building a Twitter clone\n\n[Salvatore Sanfilippo's Redis design patterns](https://redis.antirez.com/)\n\nSalvatore Sanfilippo (also known as *antirez*, the creator of Redis) has provided the Redis community with a resource containing very useful Redis-oriented design patterns. See [this page](https://redis.antirez.com/) for more information."}],"examples":[]}
{"schema_version":2,"id":"develop/clients/patterns/indexes/index","title":"Secondary indexing","url":"https://redis.io/docs/latest/develop/clients/patterns/indexes/","summary":"Building secondary indexes in Redis","aliases":["/develop/use/patterns/indexes","/manual/patterns/indexes/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-09T10:55:35+01:00","page_type":"content","content_hash":"57329342f853ef3bbe23520cc7bfab860f0b963d1b7fa5d597b879117c03013f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis is not exactly a key-value store, since values can be complex data structures. However it has an external key-value shell: at API level data is addressed by the key name. It is fair to say that, natively, Redis only offers *primary key access*. However since Redis is a data structures server, its capabilities can be used for indexing, in order to create secondary indexes of different kinds, including composite (multi-column) indexes.\n\nThis document explains how it is possible to create indexes in Redis using the following data structures:\n\n* Hashes and JSON documents, using a variety of field types; used in conjunction with Redis Search.\n* Sorted sets to create secondary indexes by ID or other numerical fields.\n* Sorted sets with lexicographical ranges for creating more advanced secondary indexes, composite indexes and graph traversal indexes.\n* Sets for creating random indexes.\n* Lists for creating simple iterable indexes and last N items indexes.\n* Time series with labels.\n\nImplementing and maintaining indexes with Redis is an advanced topic, so most\nusers that need to perform complex queries on data should understand if they\nare better served by a relational store. However often, especially in caching\nscenarios, there is the explicit need to store indexed data into Redis in order to speedup common queries which require some form of indexing in order to be executed."},{"id":"hashes-and-json-indexes","title":"Hashes and JSON indexes","role":"content","text":"Redis Search provides capabilities to index and query both hash and JSON keys using a variety of field types:\n\n* `TEXT`\n* `TAG`\n* `NUMERIC`\n* `GEO`\n* `VECTOR`\n* `GEOSHAPE`\n\nOnce hash or JSON keys have been indexed using the [`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create) command, all keys that use the prefix defined in the index can be queried using the [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) and [`FT.AGGREGATE`](https://redis.io/docs/latest/commands/ft.aggregate) commands.\n\nFor more information on creating hash and JSON indexes, see the following pages.\n\n* [Hash indexes](https://redis.io/docs/latest/develop/ai/search-and-query/indexing/schema-definition)\n* [JSON indexes](https://redis.io/docs/latest/develop/ai/search-and-query/indexing)"},{"id":"simple-numerical-indexes-with-sorted-sets","title":"Simple numerical indexes with sorted sets","role":"content","text":"The simplest secondary index you can create with Redis is by using the\nsorted set data type, which is a data structure representing a set of\nelements ordered by a floating point number which is the *score* of\neach element. Elements are ordered from the smallest to the highest score.\n\nSince the score is a double precision float, indexes you can build with\nvanilla sorted sets are limited to things where the indexing field is a number\nwithin a given range.\n\nThe two commands to build these kind of indexes are [`ZADD`](https://redis.io/docs/latest/commands/zadd) and\n[`ZRANGE`](https://redis.io/docs/latest/commands/zrange) with the `BYSCORE` argument to respectively add items and retrieve items within a\nspecified range.\n\nFor instance, it is possible to index a set of person names by their\nage by adding element to a sorted set. The element will be the name of the\nperson and the score will be the age.\n\n    ZADD myindex 25 Manuel\n    ZADD myindex 18 Anna\n    ZADD myindex 35 Jon\n    ZADD myindex 67 Helen\n\nIn order to retrieve all persons with an age between 20 and 40, the following\ncommand can be used:\n\n    ZRANGE myindex 20 40 BYSCORE\n    1) \"Manuel\"\n    2) \"Jon\"\n\nBy using the **WITHSCORES** option of [`ZRANGE`](https://redis.io/docs/latest/commands/zrange) it is also possible\nto obtain the scores associated with the returned elements.\n\nThe [`ZCOUNT`](https://redis.io/docs/latest/commands/zcount) command can be used in order to retrieve the number of elements\nwithin a given range, without actually fetching the elements, which is also\nuseful, especially given the fact the operation is executed in logarithmic\ntime regardless of the size of the range.\n\nRanges can be inclusive or exclusive, please refer to the [`ZRANGE`](https://redis.io/docs/latest/commands/zrange)\ncommand documentation for more information.\n\n**Note**: Using the [`ZRANGE`](https://redis.io/docs/latest/commands/zrange) with the `BYSCORE` and `REV` arguments, it is possible to query a range in\nreversed order, which is often useful when data is indexed in a given\ndirection (ascending or descending) but we want to retrieve information\nthe other way around."},{"id":"use-object-ids-as-associated-values","title":"Use object IDs as associated values","role":"content","text":"In the above example we associated names to ages. However in general we\nmay want to index some field of an object which is stored elsewhere.\nInstead of using the sorted set value directly to store the data associated\nwith the indexed field, it is possible to store just the ID of the object.\n\nFor example I may have Redis hashes representing users. Each user is\nrepresented by a single key, directly accessible by ID:\n\n    HMSET user:1 id 1 username antirez ctime 1444809424 age 38\n    HMSET user:2 id 2 username maria ctime 1444808132 age 42\n    HMSET user:3 id 3 username jballard ctime 1443246218 age 33\n\nIf I want to create an index in order to query users by their age, I\ncould do:\n\n    ZADD user.age.index 38 1\n    ZADD user.age.index 42 2\n    ZADD user.age.index 33 3\n\nThis time the value associated with the score in the sorted set is the\nID of the object. So once I query the index with [`ZRANGE`](https://redis.io/docs/latest/commands/zrange) with the `BYSCORE` argument, I'll\nalso have to retrieve the information I need with [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) or similar\ncommands. The obvious advantage is that objects can change without touching\nthe index, as long as we don't change the indexed field.\n\nIn the next examples we'll almost always use IDs as values associated with\nthe index, since this is usually the more sounding design, with a few\nexceptions."},{"id":"update-simple-sorted-set-indexes","title":"Update simple sorted set indexes","role":"content","text":"Often we index things which change over time. In the above\nexample, the age of the user changes every year. In such a case it would\nmake sense to use the birth date as index instead of the age itself,\nbut there are other cases where we simply want some field to change from\ntime to time, and the index to reflect this change.\n\nThe [`ZADD`](https://redis.io/docs/latest/commands/zadd) command makes updating simple indexes a very trivial operation\nsince re-adding back an element with a different score and the same value\nwill simply update the score and move the element at the right position,\nso if the user `antirez` turned 39 years old, in order to update the\ndata in the hash representing the user, and in the index as well, we need\nto execute the following two commands:\n\n    HSET user:1 age 39\n    ZADD user.age.index 39 1\n\nThe operation may be wrapped in a [`MULTI`](https://redis.io/docs/latest/commands/multi)/[`EXEC`](https://redis.io/docs/latest/commands/exec) transaction in order to\nmake sure both fields are updated or none."},{"id":"turn-multi-dimensional-data-into-linear-data","title":"Turn multi-dimensional data into linear data","role":"content","text":"Indexes created with sorted sets are able to index only a single numerical\nvalue. Because of this you may think it is impossible to index something\nwhich has multiple dimensions using this kind of indexes, but actually this\nis not always true. If you can efficiently represent something\nmulti-dimensional in a linear way, they it is often possible to use a simple\nsorted set for indexing.\n\nFor example the [Redis geo indexing API](https://redis.io/docs/latest/commands/geoadd) uses a sorted\nset to index places by latitude and longitude using a technique called\n[Geo hash](https://en.wikipedia.org/wiki/Geohash). The sorted set score\nrepresents alternating bits of longitude and latitude, so that we map the\nlinear score of a sorted set to many small *squares* in the earth surface.\nBy doing an 8+1 style center plus neighborhoods search it is possible to\nretrieve elements by radius."},{"id":"limits-of-the-score","title":"Limits of the score","role":"limits","text":"Sorted set elements scores are double precision floats. It means that\nthey can represent different decimal or integer values with different\nerrors, because they use an exponential representation internally.\nHowever what is interesting for indexing purposes is that the score is\nalways able to represent without any error numbers between -9007199254740992\nand 9007199254740992, which is `-/+ 2^53`.\n\nWhen representing much larger numbers, you need a different form of indexing\nthat is able to index numbers at any precision, called a lexicographical\nindex."},{"id":"time-series-indexes","title":"Time series indexes","role":"content","text":"When you create a new time series using the [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create) command, you can associate one or more `LABELS` with it. Each label is a name-value pair, where the both name and value are text. Labels serve as a secondary index that allows you to execute queries on groups of time series keys using various time series commands.\n\nSee the [time series quickstart guide](https://redis.io/docs/latest/develop/data-types/timeseries#create-a-time-series) for an example of creating a time series with a label.\n\nThe [`TS.MGET`](https://redis.io/docs/latest/commands/ts.mget), [`TS.MRANGE`](https://redis.io/docs/latest/commands/ts.mrange), and [`TS.MREVRANGE`](https://redis.io/docs/latest/commands/ts.mrevrange) commands operate on multiple time series based on specified labels or using a label-related filter expression. The [`TS.QUERYINDEX`](https://redis.io/docs/latest/commands/ts.queryindex) command returns all time series keys matching a given label-related filter expression."},{"id":"lexicographical-indexes","title":"Lexicographical indexes","role":"content","text":"Redis sorted sets have an interesting property. When elements are added\nwith the same score, they are sorted lexicographically, comparing the\nstrings as binary data with the `memcmp()` function.\n\nFor people that don't know the C language nor the `memcmp` function, what\nit means is that elements with the same score are sorted comparing the\nraw values of their bytes, byte after byte. If the first byte is the same,\nthe second is checked and so forth. If the common prefix of two strings is\nthe same then the longer string is considered the greater of the two,\nso \"foobar\" is greater than \"foo\".\n\nThere are commands such as [`ZRANGE`](https://redis.io/docs/latest/commands/zrange) and [`ZLEXCOUNT`](https://redis.io/docs/latest/commands/zlexcount) that\nare able to query and count ranges in a lexicographically fashion, assuming\nthey are used with sorted sets where all the elements have the same score.\n\nThis Redis feature is basically equivalent to a `b-tree` data structure which\nis often used in order to implement indexes with traditional databases.\nAs you can guess, because of this, it is possible to use this Redis data\nstructure in order to implement pretty fancy indexes.\n\nBefore we dive into using lexicographical indexes, let's check how\nsorted sets behave in this special mode of operation. Since we need to\nadd elements with the same score, we'll always use the special score of\nzero.\n\n    ZADD myindex 0 baaa\n    ZADD myindex 0 abbb\n    ZADD myindex 0 aaaa\n    ZADD myindex 0 bbbb\n\nFetching all the elements from the sorted set immediately reveals that they\nare ordered lexicographically.\n\n    ZRANGE myindex 0 -1\n    1) \"aaaa\"\n    2) \"abbb\"\n    3) \"baaa\"\n    4) \"bbbb\"\n\nNow we can use [`ZRANGE`](https://redis.io/docs/latest/commands/zrange) with the `BYLEX` argument in order to perform range queries.\n\n    ZRANGE myindex [a (b BYLEX\n    1) \"aaaa\"\n    2) \"abbb\"\n\nNote that in the range queries we prefixed the `min` and `max` elements\nidentifying the range with the special characters `[` and `(`.\nThis prefixes are mandatory, and they specify if the elements\nof the range are inclusive or exclusive. So the range `[a (b` means give me\nall the elements lexicographically between `a` inclusive and `b` exclusive,\nwhich are all the elements starting with `a`.\n\nThere are also two more special characters indicating the infinitely negative\nstring and the infinitely positive string, which are `-` and `+`.\n\n    ZRANGE myindex [b + BYLEX\n    1) \"baaa\"\n    2) \"bbbb\"\n\nThat's it basically. Let's see how to use these features to build indexes."},{"id":"a-first-example-completion","title":"A first example: completion","role":"content","text":"An interesting application of indexing is completion. Completion is what\nhappens when you start typing your query into a search engine: the user\ninterface will anticipate what you are likely typing, providing common\nqueries that start with the same characters.\n\nA naive approach to completion is to just add every single query we\nget from the user into the index. For example if the user searches `banana`\nwe'll just do:\n\n    ZADD myindex 0 banana\n\nAnd so forth for each search query ever encountered. Then when we want to\ncomplete the user input, we execute a range query using [`ZRANGE`](https://redis.io/docs/latest/commands/zrange) with the `BYLEX` argument.\nImagine the user is typing \"bit\" inside the search form, and we want to\noffer possible search keywords starting for \"bit\". We send Redis a command\nlike that:\n\n    ZRANGE myindex \"[bit\" \"[bit\\xff\" BYLEX\n\nBasically we create a range using the string the user is typing right now\nas start, and the same string plus a trailing byte set to 255, which is `\\xff` in the example, as the end of the range. This way we get all the strings that start for the string the user is typing.\n\nNote that we don't want too many items returned, so we may use the **LIMIT** option in order to reduce the number of results."},{"id":"add-frequency-into-the-mix","title":"Add frequency into the mix","role":"content","text":"The above approach is a bit naive, because all the user searches are the same\nin this way. In a real system we want to complete strings according to their\nfrequency: very popular searches will be proposed with a higher probability\ncompared to search strings typed very rarely.\n\nIn order to implement something which depends on the frequency, and at the\nsame time automatically adapts to future inputs, by purging searches that\nare no longer popular, we can use a very simple *streaming algorithm*.\n\nTo start, we modify our index in order to store not just the search term,\nbut also the frequency the term is associated with. So instead of just adding\n`banana` we add `banana:1`, where 1 is the frequency.\n\n    ZADD myindex 0 banana:1\n\nWe also need logic in order to increment the index if the search term\nalready exists in the index, so what we'll actually do is something like\nthat:\n\n    ZRANGE myindex \"[banana:\" + BYLEX LIMIT 0 1\n    1) \"banana:1\"\n\nThis will return the single entry of `banana` if it exists. Then we\ncan increment the associated frequency and send the following two\ncommands:\n\n    ZREM myindex 0 banana:1\n    ZADD myindex 0 banana:2\n\nNote that because it is possible that there are concurrent updates, the\nabove three commands should be send via a [Lua script](https://redis.io/docs/latest/commands/eval)\ninstead, so that the Lua script will atomically get the old count and\nre-add the item with incremented score.\n\nSo the result will be that, every time a user searches for `banana` we'll\nget our entry updated.\n\nThere is more: our goal is to just have items searched very frequently.\nSo we need some form of purging. When we actually query the index\nin order to complete the user input, we may see something like that:\n\n    ZRANGE myindex \"[banana:\" + BYLEX LIMIT 0 10\n    1) \"banana:123\"\n    2) \"banaooo:1\"\n    3) \"banned user:49\"\n    4) \"banning:89\"\n\nApparently nobody searches for \"banaooo\", for example, but the query was\nperformed a single time, so we end presenting it to the user.\n\nThis is what we can do. Out of the returned items, we pick a random one,\ndecrement its score by one, and re-add it with the new score.\nHowever if the score reaches 0, we simply remove the item from the list.\nYou can use much more advanced systems, but the idea is that the index in\nthe long run will contain top searches, and if top searches will change over\nthe time it will adapt automatically.\n\nA refinement to this algorithm is to pick entries in the list according to\ntheir weight: the higher the score, the less likely entries are picked\nin order to decrement its score, or evict them."},{"id":"normalize-strings-for-case-and-accents","title":"Normalize strings for case and accents","role":"content","text":"In the completion examples we always used lowercase strings. However\nreality is much more complex than that: languages have capitalized names,\naccents, and so forth.\n\nOne simple way do deal with this issues is to actually normalize the\nstring the user searches. Whatever the user searches for \"Banana\",\n\"BANANA\" or \"Ba'nana\" we may always turn it into \"banana\".\n\nHowever sometimes we may like to present the user with the original\nitem typed, even if we normalize the string for indexing. In order to\ndo this, what we do is to change the format of the index so that instead\nof just storing `term:frequency` we store `normalized:frequency:original`\nlike in the following example:\n\n    ZADD myindex 0 banana:273:Banana\n\nBasically we add another field that we'll extract and use only for\nvisualization. Ranges will always be computed using the normalized strings\ninstead. This is a common trick which has multiple applications."},{"id":"add-auxiliary-information-in-the-index","title":"Add auxiliary information in the index","role":"content","text":"When using a sorted set in a direct way, we have two different attributes\nfor each object: the score, which we use as an index, and an associated\nvalue. When using lexicographical indexes instead, the score is always\nset to 0 and basically not used at all. We are left with a single string,\nwhich is the element itself.\n\nLike we did in the previous completion examples, we are still able to\nstore associated data using separators. For example we used the colon in\norder to add the frequency and the original word for completion.\n\nIn general we can add any kind of associated value to our indexing key.\nIn order to use a lexicographical index to implement a simple key-value store\nwe just store the entry as `key:value`:\n\n    ZADD myindex 0 mykey:myvalue\n\nAnd search for the key with:\n\n    ZRANGE myindex [mykey: + BYLEX LIMIT 0 1\n    1) \"mykey:myvalue\"\n\nThen we extract the part after the colon to retrieve the value.\nHowever a problem to solve in this case is collisions. The colon character\nmay be part of the key itself, so it must be chosen in order to never\ncollide with the key we add.\n\nSince lexicographical ranges in Redis are binary safe you can use any\nbyte or any sequence of bytes. However if you receive untrusted user\ninput, it is better to use some form of escaping in order to guarantee\nthat the separator will never happen to be part of the key.\n\nFor example if you use two null bytes as separator `\"\\0\\0\"`, you may\nwant to always escape null bytes into two bytes sequences in your strings."},{"id":"numerical-padding","title":"Numerical padding","role":"content","text":"Lexicographical indexes may look like good only when the problem at hand\nis to index strings. Actually it is very simple to use this kind of index\nin order to perform indexing of arbitrary precision numbers.\n\nIn the ASCII character set, digits appear in the order from 0 to 9, so\nif we left-pad numbers with leading zeroes, the result is that comparing\nthem as strings will order them by their numerical value.\n\n    ZADD myindex 0 00324823481:foo\n    ZADD myindex 0 12838349234:bar\n    ZADD myindex 0 00000000111:zap\n\n    ZRANGE myindex 0 -1\n    1) \"00000000111:zap\"\n    2) \"00324823481:foo\"\n    3) \"12838349234:bar\"\n\nWe effectively created an index using a numerical field which can be as\nbig as we want. This also works with floating point numbers of any precision\nby making sure we left pad the numerical part with leading zeroes and the\ndecimal part with trailing zeroes like in the following list of numbers:\n\n        01000000000000.11000000000000\n        01000000000000.02200000000000\n        00000002121241.34893482930000\n        00999999999999.00000000000000"},{"id":"use-numbers-in-binary-form","title":"Use numbers in binary form","role":"content","text":"Storing numbers in decimal may use too much memory. An alternative approach\nis just to store numbers, for example 128 bit integers, directly in their\nbinary form. However for this to work, you need to store the numbers in\n*big endian format*, so that the most significant bytes are stored before\nthe least significant bytes. This way when Redis compares the strings with\n`memcmp()`, it will effectively sort the numbers by their value.\n\nKeep in mind that data stored in binary format is less observable for\ndebugging, harder to parse and export. So it is definitely a trade off."},{"id":"composite-indexes","title":"Composite indexes","role":"content","text":"So far we explored ways to index single fields. However we all know that\nSQL stores are able to create indexes using multiple fields. For example\nI may index products in a very large store by room number and price.\n\nI need to run queries in order to retrieve all the products in a given\nroom having a given price range. What I can do is to index each product\nin the following way:\n\n    ZADD myindex 0 0056:0028.44:90\n    ZADD myindex 0 0034:0011.00:832\n\nHere the fields are `room:price:product_id`. I used just four digits padding\nin the example for simplicity. The auxiliary data (the product ID) does not\nneed any padding.\n\nWith an index like that, to get all the products in room 56 having a price\nbetween 10 and 30 dollars is very easy. We can just run the following\ncommand:\n\n    ZRANGE myindex [0056:0010.00 [0056:0030.00 BYLEX\n\nThe above is called a composed index. Its effectiveness depends on the\norder of the fields and the queries I want to run. For example the above\nindex cannot be used efficiently in order to get all the products having\na specific price range regardless of the room number. However I can use\nthe primary key in order to run queries regardless of the price, like\n*give me all the products in room 44*.\n\nComposite indexes are very powerful, and are used in traditional stores\nin order to optimize complex queries. In Redis they could be useful both\nto implement a very fast in-memory Redis index of something stored into\na traditional data store, or in order to directly index Redis data."},{"id":"update-lexicographical-indexes","title":"Update lexicographical indexes","role":"content","text":"The value of the index in a lexicographical index can get pretty fancy\nand hard or slow to rebuild from what we store about the object. So one\napproach to simplify the handling of the index, at the cost of using more\nmemory, is to also take alongside to the sorted set representing the index\na hash mapping the object ID to the current index value.\n\nSo for example, when we index we also add to a hash:\n\n    MULTI\n    ZADD myindex 0 0056:0028.44:90\n    HSET index.content 90 0056:0028.44:90\n    EXEC\n\nThis is not always needed, but simplifies the operations of updating\nthe index. In order to remove the old information we indexed for the object\nID 90, regardless of the *current* fields values of the object, we just\nhave to retrieve the hash value by object ID and [`ZREM`](https://redis.io/docs/latest/commands/zrem) it in the sorted\nset view."},{"id":"represent-and-query-graphs-using-a-hexastore","title":"Represent and query graphs using a hexastore","role":"content","text":"One cool thing about composite indexes is that they are handy in order\nto represent graphs, using a data structure which is called\n[Hexastore](http://www.vldb.org/pvldb/vol1/1453965.pdf).\n\nThe hexastore provides a representation for relations between objects,\nformed by a *subject*, a *predicate* and an *object*.\nA simple relation between objects could be:\n\n    antirez is-friend-of matteocollina\n\nIn order to represent this relation I can store the following element\nin my lexicographical index:\n\n    ZADD myindex 0 spo:antirez:is-friend-of:matteocollina\n\nNote that I prefixed my item with the string **spo**. It means that\nthe item represents a subject,predicate,object relation.\n\nIn can add 5 more entries for the same relation, but in a different order:\n\n    ZADD myindex 0 sop:antirez:matteocollina:is-friend-of\n    ZADD myindex 0 ops:matteocollina:is-friend-of:antirez\n    ZADD myindex 0 osp:matteocollina:antirez:is-friend-of\n    ZADD myindex 0 pso:is-friend-of:antirez:matteocollina\n    ZADD myindex 0 pos:is-friend-of:matteocollina:antirez\n\nNow things start to be interesting, and I can query the graph in many\ndifferent ways. For example, who are all the people `antirez`\n*is friend of*?\n\n    ZRANGE myindex \"[spo:antirez:is-friend-of:\" \"[spo:antirez:is-friend-of:\\xff\" BYLEX\n    1) \"spo:antirez:is-friend-of:matteocollina\"\n    2) \"spo:antirez:is-friend-of:wonderwoman\"\n    3) \"spo:antirez:is-friend-of:spiderman\"\n\nOr, what are all the relationships `antirez` and `matteocollina` have where\nthe first is the subject and the second is the object?\n\n    ZRANGE myindex \"[sop:antirez:matteocollina:\" \"[sop:antirez:matteocollina:\\xff\" BYLEX\n    1) \"sop:antirez:matteocollina:is-friend-of\"\n    2) \"sop:antirez:matteocollina:was-at-conference-with\"\n    3) \"sop:antirez:matteocollina:talked-with\"\n\nBy combining different queries, I can ask fancy questions. For example:\n*Who are all my friends that, like beer, live in Barcelona, and matteocollina consider friends as well?*\nTo get this information I start with an `spo` query to find all the people\nI'm friend with. Then for each result I get I perform an `spo` query\nto check if they like beer, removing the ones for which I can't find\nthis relation. I do it again to filter by city. Finally I perform an `ops`\nquery to find, of the list I obtained, who is considered friend by\nmatteocollina.\n\nMake sure to check [Matteo Collina's slides about Levelgraph](http://nodejsconfit.levelgraph.io/) in order to better understand these ideas."},{"id":"multi-dimensional-indexes","title":"Multi-dimensional indexes","role":"content","text":"A more complex type of index is an index that allows you to perform queries\nwhere two or more variables are queried at the same time for specific\nranges. For example I may have a data set representing persons age and\nsalary, and I want to retrieve all the people between 50 and 55 years old\nhaving a salary between 70000 and 85000.\n\nThis query may be performed with a multi column index, but this requires\nus to select the first variable and then scan the second, which means we\nmay do a lot more work than needed. It is possible to perform these kinds of\nqueries involving multiple variables using different data structures.\nFor example, multi-dimensional trees such as *k-d trees* or *r-trees* are\nsometimes used. Here we'll describe a different way to index data into\nmultiple dimensions, using a representation trick that allows us to perform\nthe query in a very efficient way using Redis lexicographical ranges.\n\nLet's say we have points in the space, which represent our data samples, where `x` and `y` are our coordinates. The max value of both variables is 400.\n\nIn the next figure, the blue box represents our query. We want all the points where `x` is between 50 and 100, and where `y` is between 100 and 300.\n\n![Points in the space](2idx_0.png)\n\nIn order to represent data that makes these kinds of queries fast to perform,\nwe start by padding our numbers with 0. So for example imagine we want to\nadd the point 10,25 (x,y) to our index. Given that the maximum range in the\nexample is 400 we can just pad to three digits, so we obtain:\n\n    x = 010\n    y = 025\n\nNow what we do is to interleave the digits, taking the leftmost digit\nin x, and the leftmost digit in y, and so forth, in order to create a single\nnumber:\n\n    001205\n\nThis is our index, however in order to more easily reconstruct the original\nrepresentation, if we want (at the cost of space), we may also add the\noriginal values as additional columns:\n\n    001205:10:25\n\nNow, let's reason about this representation and why it is useful in the\ncontext of range queries. For example let's take the center of our blue\nbox, which is at `x=75` and `y=200`. We can encode this number as we did\nearlier by interleaving the digits, obtaining:\n\n    027050\n\nWhat happens if we substitute the last two digits respectively with 00 and 99?\nWe obtain a range which is lexicographically continuous:\n\n    027000 to 027099\n\nWhat this maps to is to a square representing all values where the `x`\nvariable is between 70 and 79, and the `y` variable is between 200 and 209.\nTo identify this specific area, we can write random points in that interval.\n\n![Small area](2idx_1.png)\n\nSo the above lexicographic query allows us to easily query for points in\na specific square in the picture. However the square may be too small for\nthe box we are searching, so that too many queries are needed.\nSo we can do the same but instead of replacing the last two digits with 00\nand 99, we can do it for the last four digits, obtaining the following\nrange:\n\n    020000 029999\n\nThis time the range represents all the points where `x` is between 0 and 99\nand `y` is between 200 and 299. Drawing random points in this interval\nshows us this larger area.\n\n![Large area](2idx_2.png)\n\nSo now our area is too big for our query, and still our search box is\nnot completely included. We need more granularity, but we can easily obtain\nit by representing our numbers in binary form. This time, when we replace\ndigits instead of getting squares which are ten times bigger, we get squares\nwhich are just two times bigger.\n\nOur numbers in binary form, assuming we need just 9 bits for each variable\n(in order to represent numbers up to 400 in value) would be:\n\n    x = 75  -> 001001011\n    y = 200 -> 011001000\n\nSo by interleaving digits, our representation in the index would be:\n\n    000111000011001010:75:200\n\nLet's see what are our ranges as we substitute the last 2, 4, 6, 8, ...\nbits with 0s ad 1s in the interleaved representation:\n\n    2 bits: x between 74 and 75, y between 200 and 201 (range=2)\n    4 bits: x between 72 and 75, y between 200 and 203 (range=4)\n    6 bits: x between 72 and 79, y between 200 and 207 (range=8)\n    8 bits: x between 64 and 79, y between 192 and 207 (range=16)\n\nAnd so forth. Now we have definitely better granularity!\nAs you can see substituting N bits from the index gives us\nsearch boxes of side `2^(N/2)`.\n\nSo what we do is check the dimension where our search box is smaller,\nand check the nearest power of two to this number. Our search box\nwas 50,100 to 100,300, so it has a width of 50 and a height of 200.\nWe take the smaller of the two, 50, and check the nearest power of two\nwhich is 64. 64 is 2^6, so we would work with indexes obtained replacing\nthe latest 12 bits from the interleaved representation (so that we end\nreplacing just 6 bits of each variable).\n\nHowever single squares may not cover all our search, so we may need more.\nWhat we do is to start with the left bottom corner of our search box,\nwhich is 50,100, and find the first range by substituting the last 6 bits\nin each number with 0. Then we do the same with the right top corner.\n\nWith two trivial nested for loops where we increment only the significant\nbits, we can find all the squares between these two. For each square we\nconvert the two numbers into our interleaved representation, and create\nthe range using the converted representation as our start, and the same\nrepresentation but with the latest 12 bits turned on as end range.\n\nFor each square found we perform our query and get the elements inside,\nremoving the elements which are outside our search box.\n\nTurning this into code is simple. Here is a Ruby example:\n\n    def spacequery(x0,y0,x1,y1,exp)\n        bits=exp*2\n        x_start = x0/(2**exp)\n        x_end = x1/(2**exp)\n        y_start = y0/(2**exp)\n        y_end = y1/(2**exp)\n        (x_start..x_end).each{|x|\n            (y_start..y_end).each{|y|\n                x_range_start = x*(2**exp)\n                x_range_end = x_range_start | ((2**exp)-1)\n                y_range_start = y*(2**exp)\n                y_range_end = y_range_start | ((2**exp)-1)\n                puts \"#{x},#{y} x from #{x_range_start} to #{x_range_end}, y from #{y_range_start} to #{y_range_end}\"\n\n                # Turn it into interleaved form for ZRANGE query.\n                # We assume we need 9 bits for each integer, so the final\n                # interleaved representation will be 18 bits.\n                xbin = x_range_start.to_s(2).rjust(9,'0')\n                ybin = y_range_start.to_s(2).rjust(9,'0')\n                s = xbin.split(\"\").zip(ybin.split(\"\")).flatten.compact.join(\"\")\n                # Now that we have the start of the range, calculate the end\n                # by replacing the specified number of bits from 0 to 1.\n                e = s[0..-(bits+1)]+(\"1\"*bits)\n                puts \"ZRANGE myindex [#{s} [#{e} BYLEX\"\n            }\n        }\n    end\n\n    spacequery(50,100,100,300,6)\n\nWhile non immediately trivial this is a very useful indexing strategy that\nin the future may be implemented in Redis in a native way.\nFor now, the good thing is that the complexity may be easily encapsulated\ninside a library that can be used in order to perform indexing and queries.\nOne example of such library is [Redimension](https://github.com/antirez/redimension), a proof of concept Ruby library which indexes N-dimensional data inside Redis using the technique described here."},{"id":"multi-dimensional-indexes-with-negative-or-floating-point-numbers","title":"Multi-dimensional indexes with negative or floating point numbers","role":"content","text":"The simplest way to represent negative values is just to work with unsigned\nintegers and represent them using an offset, so that when you index, before\ntranslating numbers in the indexed representation, you add the absolute value\nof your smaller negative integer.\n\nFor floating point numbers, the simplest approach is probably to convert them\nto integers by multiplying the integer for a power of ten proportional to the\nnumber of digits after the dot you want to retain."},{"id":"non-range-indexes","title":"Non-range indexes","role":"content","text":"So far we checked indexes which are useful to query by range or by single\nitem. However other Redis data structures such as Sets or Lists can be used\nin order to build other kind of indexes. They are very commonly used but\nmaybe we don't always realize they are actually a form of indexing.\n\nFor instance I can index object IDs into a Set data type in order to use\nthe *get random elements* operation via [`SRANDMEMBER`](https://redis.io/docs/latest/commands/srandmember) in order to retrieve\na set of random objects. Sets can also be used to check for existence when\nall I need is to test if a given item exists or not or has a single boolean\nproperty or not.\n\nSimilarly lists can be used in order to index items into a fixed order.\nI can add all my items into a Redis list and rotate the list with\n[`RPOPLPUSH`](https://redis.io/docs/latest/commands/rpoplpush) using the same key name as source and destination. This is useful\nwhen I want to process a given set of items again and again forever in the\nsame order. Think of an RSS feed system that needs to refresh the local copy\nperiodically.\n\nAnother popular index often used with Redis is a **capped list**, where items\nare added with [`LPUSH`](https://redis.io/docs/latest/commands/lpush) and trimmed with [`LTRIM`](https://redis.io/docs/latest/commands/ltrim), in order to create a view\nwith just the latest N items encountered, in the same order they were\nseen."},{"id":"index-inconsistency","title":"Index inconsistency","role":"content","text":"Keeping the index updated may be challenging, in the course of months\nor years it is possible that inconsistencies are added because of software\nbugs, network partitions or other events.\n\nDifferent strategies could be used. If the index data is outside Redis\n*read repair* can be a solution, where data is fixed in a lazy way when\nit is requested. When we index data which is stored in Redis itself\nthe [`SCAN`](https://redis.io/docs/latest/commands/scan) family of commands can be used in order to verify, update or\nrebuild the index from scratch, incrementally."}],"examples":[]}
{"schema_version":2,"id":"develop/clients/patterns/twitter-clone","title":"Redis patterns example","url":"https://redis.io/docs/latest/develop/clients/patterns/twitter-clone/","summary":"Learn several Redis patterns by building a Twitter clone","aliases":["/develop/use/patterns/twitter-clone"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-09T10:55:35+01:00","page_type":"content","content_hash":"69d804fc0857e0e26589d12eb845fc27cb1f1c084e1207ee5f98bfc0c1c54dd9","sections":[{"id":"content","title":"Content","role":"content","text":"This article describes the design and implementation of a [very simple Twitter clone](https://github.com/antirez/retwis) written using PHP with Redis as the only database. The programming community has traditionally considered key-value stores as a special purpose database that couldn't be used as a drop-in replacement for a relational database for the development of web applications. This article will try to show that Redis data structures on top of a key-value layer are an effective data model to implement many kinds of applications.\n\nNote: the original version of this article was written in 2009 when Redis was\nreleased. It was not exactly clear at that time that the Redis data model was\nsuitable to write entire applications. Now after 5 years there are many cases of\napplications using Redis as their main store, so the goal of the article today\nis to be a tutorial for Redis newcomers. You'll learn how to design a simple\ndata layout using Redis, and how to apply different data structures.\n\nOur Twitter clone, called [Retwis](https://github.com/antirez/retwis), is structurally simple, has very good performance, and can be distributed among any number of web and Redis servers with little efforts. [View the Retwis source code](https://github.com/antirez/retwis).\n\nI used PHP for the example because of its universal readability. The same (or better) results can be obtained using Ruby, Python, Erlang, and so on.\nA few clones exist (however not all the clones use the same data layout as the\ncurrent version of this tutorial, so please, stick with the official PHP\nimplementation for the sake of following the article better).\n\n* [Retwis-RB](https://github.com/danlucraft/retwis-rb) is a port of Retwis to Ruby and Sinatra written by Daniel Lucraft.\n* [Retwis-J](https://docs.spring.io/spring-data/data-keyvalue/examples/retwisj/current/) is a port of Retwis to Java, using the Spring Data Framework, written by [Costin Leau](http://twitter.com/costinl). Its source code can be found on [GitHub](https://github.com/SpringSource/spring-data-keyvalue-examples), and there is comprehensive documentation available at [springsource.org](http://j.mp/eo6z6I).\n\nWhat is a key-value store?\n---\nThe essence of a key-value store is the ability to store some data, called a _value_, inside a key. The value can be retrieved later only if we know the specific key it was stored in. There is no direct way to search for a key by value. In some sense, it is like a very large hash/dictionary, but it is persistent, i.e. when your application ends, the data doesn't go away. So, for example, I can use the command [`SET`](https://redis.io/docs/latest/commands/set) to store the value *bar* in the key *foo*:\n\n    SET foo bar\n\nRedis stores data permanently, so if I later ask \"_What is the value stored in key foo?_\" Redis will reply with *bar*:\n\n    GET foo => bar\n\nOther common operations provided by key-value stores are [`DEL`](https://redis.io/docs/latest/commands/del), to delete a given key and its associated value, SET-if-not-exists (called [`SETNX`](https://redis.io/docs/latest/commands/setnx) on Redis), to assign a value to a key only if the key does not already exist, and [`INCR`](https://redis.io/docs/latest/commands/incr), to atomically increment a number stored in a given key:\n\n    SET foo 10\n    INCR foo => 11\n    INCR foo => 12\n    INCR foo => 13\n\nAtomic operations\n---\n\nThere is something special about [`INCR`](https://redis.io/docs/latest/commands/incr). You may wonder why Redis provides such an operation if we can do it ourselves with a bit of code? After all, it is as simple as:\n\n    x = GET foo\n    x = x + 1\n    SET foo x\n\nThe problem is that incrementing this way will work as long as there is only one client working with the key _foo_ at one time. See what happens if two clients are accessing this key at the same time:\n\n    x = GET foo (yields 10)\n    y = GET foo (yields 10)\n    x = x + 1 (x is now 11)\n    y = y + 1 (y is now 11)\n    SET foo x (foo is now 11)\n    SET foo y (foo is now 11)\n\nSomething is wrong! We incremented the value two times, but instead of going from 10 to 12, our key holds 11. This is because the increment done with `GET / increment / SET` *is not an atomic operation*. Instead the INCR provided by Redis, Memcached, ..., are atomic implementations, and the server will take care of protecting the key during the time needed to complete the increment in order to prevent simultaneous accesses.\n\nWhat makes Redis different from other key-value stores is that it provides other operations similar to INCR that can be used to model complex problems. This is why you can use Redis to write whole web applications without using another database like an SQL database, and without going crazy.\n\nBeyond key-value stores: lists\n---\n\nIn this section we will see which Redis features we need to build our Twitter clone. The first thing to know is that Redis values can be more than strings. Redis supports Lists, Sets, Hashes, Sorted Sets, Bitmaps, and HyperLogLog types as values, and there are atomic operations to operate on them so we are safe even with multiple accesses to the same key. Let's start with Lists:\n\n    LPUSH mylist a (now mylist holds 'a')\n    LPUSH mylist b (now mylist holds 'b','a')\n    LPUSH mylist c (now mylist holds 'c','b','a')\n\n[`LPUSH`](https://redis.io/docs/latest/commands/lpush) means _Left Push_, that is, add an element to the left (or to the head) of the list stored in _mylist_. If the key _mylist_ does not exist it is automatically created as an empty list before the PUSH operation. As you can imagine, there is also an [`RPUSH`](https://redis.io/docs/latest/commands/rpush) operation that adds the element to the right of the list (on the tail). This is very useful for our Twitter clone. User updates can be added to a list stored in `username:updates`, for instance.\n\nThere are operations to get data from Lists, of course. For instance, LRANGE returns a range from the list, or the whole list.\n\n    LRANGE mylist 0 1 => c,b\n\nLRANGE uses zero-based indexes - that is the first element is 0, the second 1, and so on. The command arguments are `LRANGE key first-index last-index`. The _last-index_ argument can be negative, with a special meaning: -1 is the last element of the list, -2 the penultimate, and so on. So, to get the whole list use:\n\n    LRANGE mylist 0 -1 => c,b,a\n\nOther important operations are LLEN that returns the number of elements in the list, and LTRIM that is like LRANGE but instead of returning the specified range *trims* the list, so it is like _Get range from mylist, Set this range as new value_ but does so atomically.\n\nThe Set data type\n---\n\nCurrently we don't use the Set type in this tutorial, but since we use\nSorted Sets, which are kind of a more capable version of Sets, it is better\nto start introducing Sets first (which are a very useful data structure\nper se), and later Sorted Sets.\n\nThere are more data types than just Lists. Redis also supports Sets, which are unsorted collections of elements. It is possible to add, remove, and test for existence of members, and perform the intersection between different Sets. Of course it is possible to get the elements of a Set. Some examples will make it more clear. Keep in mind that [`SADD`](https://redis.io/docs/latest/commands/sadd) is the _add to set_ operation, [`SREM`](https://redis.io/docs/latest/commands/srem) is the _remove from set_ operation, [`SISMEMBER`](https://redis.io/docs/latest/commands/sismember) is the _test if member_ operation, and [`SINTER`](https://redis.io/docs/latest/commands/sinter) is the _perform intersection_ operation. Other operations are [`SCARD`](https://redis.io/docs/latest/commands/scard) to get the cardinality (the number of elements) of a Set, and [`SMEMBERS`](https://redis.io/docs/latest/commands/smembers) to return all the members of a Set.\n\n    SADD myset a\n    SADD myset b\n    SADD myset foo\n    SADD myset bar\n    SCARD myset => 4\n    SMEMBERS myset => bar,a,foo,b\n\nNote that [`SMEMBERS`](https://redis.io/docs/latest/commands/smembers) does not return the elements in the same order we added them since Sets are *unsorted* collections of elements. When you want to store in order it is better to use Lists instead. Some more operations against Sets:\n\n    SADD mynewset b\n    SADD mynewset foo\n    SADD mynewset hello\n    SINTER myset mynewset => foo,b\n\n[`SINTER`](https://redis.io/docs/latest/commands/sinter) can return the intersection between Sets but it is not limited to two Sets. You may ask for the intersection of 4,5, or 10000 Sets. Finally let's check how [`SISMEMBER`](https://redis.io/docs/latest/commands/sismember) works:\n\n    SISMEMBER myset foo => 1\n    SISMEMBER myset notamember => 0\n\nThe Sorted Set data type\n---\n\nSorted Sets are similar to Sets: collection of elements. However in Sorted\nSets each element is associated with a floating point value, called the\n*element score*. Because of the score, elements inside a Sorted Set are\nordered, since we can always compare two elements by score (and if the score\nhappens to be the same, we compare the two elements as strings).\n\nLike Sets in Sorted Sets it is not possible to add repeated elements, every\nelement is unique. However it is possible to update an element's score.\n\nSorted Set commands are prefixed with `Z`. The following is an example\nof Sorted Sets usage:\n\n    ZADD zset 10 a\n    ZADD zset 5 b\n    ZADD zset 12.55 c\n    ZRANGE zset 0 -1 => b,a,c\n\nIn the above example we added a few elements with [`ZADD`](https://redis.io/docs/latest/commands/zadd), and later retrieved\nthe elements with [`ZRANGE`](https://redis.io/docs/latest/commands/zrange). As you can see the elements are returned in order\naccording to their score. In order to check if a given element exists, and\nalso to retrieve its score if it exists, we use the [`ZSCORE`](https://redis.io/docs/latest/commands/zscore) command:\n\n    ZSCORE zset a => 10\n    ZSCORE zset non_existing_element => NULL\n\nSorted Sets are a very powerful data structure, you can query elements by\nscore range, lexicographically, in reverse order, and so forth.\nTo know more [please check the Sorted Set sections in the official Redis commands documentation](/commands/?group=sorted-set).\n\nThe Hash data type\n---\n\nThis is the last data structure we use in our program, and is extremely easy\nto grasp since there is an equivalent in almost every programming language out\nthere: Hashes. Redis Hashes are basically like Ruby or Python hashes, a\ncollection of fields associated with values:\n\n    HMSET myuser name Salvatore surname Sanfilippo country Italy\n    HGET myuser surname => Sanfilippo\n\n[`HMSET`](https://redis.io/docs/latest/commands/hmset) can be used to set fields in the hash, that can be retrieved with\n[`HGET`](https://redis.io/docs/latest/commands/hget) later. It is possible to check if a field exists with [`HEXISTS`](https://redis.io/docs/latest/commands/hexists), or\nto increment a hash field with [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby) and so forth.\n\nHashes are the ideal data structure to represent *objects*. For example we\nuse Hashes in order to represent Users and Updates in our Twitter clone.\n\nOkay, we just exposed the basics of the Redis main data structures,\nwe are ready to start coding!\n\nPrerequisites\n---\n\nIf you haven't downloaded the [Retwis source code](https://github.com/antirez/retwis) already please grab it now. It contains a few PHP files, and also a copy of [Predis](https://github.com/nrk/predis), the PHP client library we use in this example.\n\nAnother thing you probably want is a working Redis server. Just get the source, build with `make`, run with `./redis-server`, and you're ready to go. No configuration is required at all in order to play with or run Retwis on your computer.\n\nData layout\n---\n\nWhen working with a relational database, a database schema must be designed so that we'd know the tables, indexes, and so on that the database will contain. We don't have tables in Redis, so what do we need to design? We need to identify what keys are needed to represent our objects and what kind of values these keys need to hold.\n\nLet's start with Users. We need to represent users, of course, with their username, userid, password, the set of users following a given user, the set of users a given user follows, and so on. The first question is, how should we identify a user? Like in a relational DB, a good solution is to identify different users with different numbers, so we can associate a unique ID with every user. Every other reference to this user will be done by id. Creating unique IDs is very simple to do by using our atomic [`INCR`](https://redis.io/docs/latest/commands/incr) operation. When we create a new user we can do something like this, assuming the user is called \"antirez\":\n\n    INCR next_user_id => 1000\n    HMSET user:1000 username antirez password p1pp0\n\n*Note: you should use a hashed password in a real application, for simplicity\nwe store the password in clear text.*\n\nWe use the `next_user_id` key in order to always get a unique ID for every new user. Then we use this unique ID to name the key holding a Hash with user's data. *This is a common design pattern* with key-values stores! Keep it in mind.\nBesides the fields already defined, we need some more stuff in order to fully define a User. For example, sometimes it can be useful to be able to get the user ID from the username, so every time we add a user, we also populate the `users` key, which is a Hash, with the username as field, and its ID as value.\n\n    HSET users antirez 1000\n\nThis may appear strange at first, but remember that we are only able to access data in a direct way, without secondary indexes. It's not possible to tell Redis to return the key that holds a specific value. This is also *our strength*. This new paradigm is forcing us to organize data so that everything is accessible by _primary key_, speaking in relational DB terms.\n\nFollowers, following, and updates\n---\n\nThere is another central need in our system. A user might have users who follow them, which we'll call their followers. A user might follow other users, which we'll call a following. We have a perfect data structure for this. That is... Sets.\nThe uniqueness of Sets elements, and the fact we can test in constant time for\nexistence, are two interesting features. However what about also remembering\nthe time at which a given user started following another one? In an enhanced\nversion of our simple Twitter clone this may be useful, so instead of using\na simple Set, we use a Sorted Set, using the user ID of the following or follower\nuser as element, and the unix time at which the relation between the users\nwas created, as our score.\n\nSo let's define our keys:\n\n    followers:1000 => Sorted Set of uids of all the followers users\n    following:1000 => Sorted Set of uids of all the following users\n\nWe can add new followers with:\n\n    ZADD followers:1000 1401267618 1234 => Add user 1234 with time 1401267618\n\nAnother important thing we need is a place where we can add the updates to display in the user's home page. We'll need to access this data in chronological order later, from the most recent update to the oldest, so the perfect kind of data structure for this is a List. Basically every new update will be [`LPUSH`](https://redis.io/docs/latest/commands/lpush)ed in the user updates key, and thanks to [`LRANGE`](https://redis.io/docs/latest/commands/lrange), we can implement pagination and so on. Note that we use the words _updates_ and _posts_ interchangeably, since updates are actually \"little posts\" in some way.\n\n    posts:1000 => a List of post ids - every new post is LPUSHed here.\n\nThis list is basically the User timeline. We'll push the IDs of her/his own\nposts, and, the IDs of all the posts of created by the following users.\nBasically, we'll implement a write fanout.\n\nAuthentication\n---\n\nOK, we have more or less everything about the user except for authentication. We'll handle authentication in a simple but robust way: we don't want to use PHP sessions, as our system must be ready to be distributed among different web servers easily, so we'll keep the whole state in our Redis database. All we need is a random **unguessable** string to set as the cookie of an authenticated user, and a key that will contain the user ID of the client holding the string.\n\nWe need two things in order to make this thing work in a robust way.\nFirst: the current authentication *secret* (the random unguessable string)\nshould be part of the User object, so when the user is created we also set\nan `auth` field in its Hash:\n\n    HSET user:1000 auth fea5e81ac8ca77622bed1c2132a021f9\n\nMoreover, we need a way to map authentication secrets to user IDs, so\nwe also take an `auths` key, which has as value a Hash type mapping\nauthentication secrets to user IDs.\n\n    HSET auths fea5e81ac8ca77622bed1c2132a021f9 1000\n\nIn order to authenticate a user we'll do these simple steps (see the `login.php` file in the Retwis source code):\n\n * Get the username and password via the login form.\n * Check if the `username` field actually exists in the `users` Hash.\n * If it exists we have the user id, (i.e. 1000).\n * Check if user:1000 password matches, if not, return an error message.\n * Ok authenticated! Set \"fea5e81ac8ca77622bed1c2132a021f9\" (the value of user:1000 `auth` field) as the \"auth\" cookie.\n\nThis is the actual code:\n\n    include(\"retwis.php\");\n\n    # Form sanity checks\n    if (!gt(\"username\") || !gt(\"password\"))\n        goback(\"You need to enter both username and password to login.\");\n\n    # The form is ok, check if the username is available\n    $username = gt(\"username\");\n    $password = gt(\"password\");\n    $r = redisLink();\n    $userid = $r->hget(\"users\",$username);\n    if (!$userid)\n        goback(\"Wrong username or password\");\n    $realpassword = $r->hget(\"user:$userid\",\"password\");\n    if ($realpassword != $password)\n        goback(\"Wrong username or password\");\n\n    # Username / password OK, set the cookie and redirect to index.php\n    $authsecret = $r->hget(\"user:$userid\",\"auth\");\n    setcookie(\"auth\",$authsecret,time()+3600*24*365);\n    header(\"Location: index.php\");\n\nThis happens every time a user logs in, but we also need a function `isLoggedIn` in order to check if a given user is already authenticated or not. These are the logical steps performed by the `isLoggedIn` function:\n\n * Get the \"auth\" cookie from the user. If there is no cookie, the user is not logged in, of course. Let's call the value of the cookie `<authcookie>`.\n * Check if `<authcookie>` field in the `auths` Hash exists, and what the value (the user ID) is (1000 in the example).\n * In order for the system to be more robust, also verify that user:1000 auth field also matches.\n * OK the user is authenticated, and we loaded a bit of information in the `$User` global variable.\n\nThe code is simpler than the description, possibly:\n\n    function isLoggedIn() {\n        global $User, $_COOKIE;\n\n        if (isset($User)) return true;\n\n        if (isset($_COOKIE['auth'])) {\n            $r = redisLink();\n            $authcookie = $_COOKIE['auth'];\n            if ($userid = $r->hget(\"auths\",$authcookie)) {\n                if ($r->hget(\"user:$userid\",\"auth\") != $authcookie) return false;\n                loadUserInfo($userid);\n                return true;\n            }\n        }\n        return false;\n    }\n\n    function loadUserInfo($userid) {\n        global $User;\n\n        $r = redisLink();\n        $User['id'] = $userid;\n        $User['username'] = $r->hget(\"user:$userid\",\"username\");\n        return true;\n    }\n\nHaving `loadUserInfo` as a separate function is overkill for our application, but it's a good approach in a complex application. The only thing that's missing from all the authentication is the logout. What do we do on logout? That's simple, we'll just change the random string in user:1000 `auth` field, remove the old authentication secret from the `auths` Hash, and add the new one.\n\n*Important:* the logout procedure explains why we don't just authenticate the user after looking up the authentication secret in the `auths` Hash, but double check it against user:1000 `auth` field. The true authentication string is the latter, while the `auths` Hash is just an authentication field that may even be volatile, or, if there are bugs in the program or a script gets interrupted, we may even end with multiple entries in the `auths` key pointing to the same user ID. The logout code is the following (`logout.php`):\n\n    include(\"retwis.php\");\n\n    if (!isLoggedIn()) {\n        header(\"Location: index.php\");\n        exit;\n    }\n\n    $r = redisLink();\n    $newauthsecret = getrand();\n    $userid = $User['id'];\n    $oldauthsecret = $r->hget(\"user:$userid\",\"auth\");\n\n    $r->hset(\"user:$userid\",\"auth\",$newauthsecret);\n    $r->hset(\"auths\",$newauthsecret,$userid);\n    $r->hdel(\"auths\",$oldauthsecret);\n\n    header(\"Location: index.php\");\n\nThat is just what we described and should be simple to understand.\n\nUpdates\n---\n\nUpdates, also known as posts, are even simpler. In order to create a new post in the database we do something like this:\n\n    INCR next_post_id => 10343\n    HMSET post:10343 user_id $owner_id time $time body \"I'm having fun with Retwis\"\n\nAs you can see each post is just represented by a Hash with three fields. The ID of the user owning the post, the time at which the post was published, and finally, the body of the post, which is, the actual status message.\n\nAfter we create a post and we obtain the post ID, we need to LPUSH the ID in the timeline of every user that is following the author of the post, and of course in the list of posts of the author itself (everybody is virtually following herself/himself). This is the file `post.php` that shows how this is performed:\n\n    include(\"retwis.php\");\n\n    if (!isLoggedIn() || !gt(\"status\")) {\n        header(\"Location:index.php\");\n        exit;\n    }\n\n    $r = redisLink();\n    $postid = $r->incr(\"next_post_id\");\n    $status = str_replace(\"\\n\",\" \",gt(\"status\"));\n    $r->hmset(\"post:$postid\",\"user_id\",$User['id'],\"time\",time(),\"body\",$status);\n    $followers = $r->zrange(\"followers:\".$User['id'],0,-1);\n    $followers[] = $User['id']; /* Add the post to our own posts too */\n\n    foreach($followers as $fid) {\n        $r->lpush(\"posts:$fid\",$postid);\n    }\n    # Push the post on the timeline, and trim the timeline to the\n    # newest 1000 elements.\n    $r->lpush(\"timeline\",$postid);\n    $r->ltrim(\"timeline\",0,1000);\n\n    header(\"Location: index.php\");\n\nThe core of the function is the `foreach` loop. We use [`ZRANGE`](https://redis.io/docs/latest/commands/zrange) to get all the followers of the current user, then the loop will [`LPUSH`](https://redis.io/docs/latest/commands/lpush) the push the post in every follower timeline List.\n\nNote that we also maintain a global timeline for all the posts, so that in the Retwis home page we can show everybody's updates easily. This requires just doing an [`LPUSH`](https://redis.io/docs/latest/commands/lpush) to the `timeline` List. Let's face it, aren't you starting to think it was a bit strange to have to sort things added in chronological order using `ORDER BY` with SQL? I think so.\n\nThere is an interesting thing to notice in the code above: we used a new\ncommand called [`LTRIM`](https://redis.io/docs/latest/commands/ltrim) after we perform the [`LPUSH`](https://redis.io/docs/latest/commands/lpush) operation in the global\ntimeline. This is used in order to trim the list to just 1000 elements. The\nglobal timeline is actually only used in order to show a few posts in the\nhome page, there is no need to have the full history of all the posts.\n\nBasically [`LTRIM`](https://redis.io/docs/latest/commands/ltrim) + [`LPUSH`](https://redis.io/docs/latest/commands/lpush) is a way to create a *capped collection* in Redis.\n\nPaginating updates\n---\n\nNow it should be pretty clear how we can use [`LRANGE`](https://redis.io/docs/latest/commands/lrange) in order to get ranges of posts, and render these posts on the screen. The code is simple:\n\n    function showPost($id) {\n        $r = redisLink();\n        $post = $r->hgetall(\"post:$id\");\n        if (empty($post)) return false;\n\n        $userid = $post['user_id'];\n        $username = $r->hget(\"user:$userid\",\"username\");\n        $elapsed = strElapsed($post['time']);\n        $userlink = \"<a class=\\\"username\\\" href=\\\"profile.php?u=\".urlencode($username).\"\\\">\".utf8entities($username).\"</a>\";\n\n        echo('<div class=\"post\">'.$userlink.' '.utf8entities($post['body']).\"<br>\");\n        echo('<i>posted '.$elapsed.' ago via web</i></div>');\n        return true;\n    }\n\n    function showUserPosts($userid,$start,$count) {\n        $r = redisLink();\n        $key = ($userid == -1) ? \"timeline\" : \"posts:$userid\";\n        $posts = $r->lrange($key,$start,$start+$count);\n        $c = 0;\n        foreach($posts as $p) {\n            if (showPost($p)) $c++;\n            if ($c == $count) break;\n        }\n        return count($posts) == $count+1;\n    }\n\n`showPost` will simply convert and print a Post in HTML while `showUserPosts` gets a range of posts and then passes them to `showPosts`.\n\n*Note: [`LRANGE`](https://redis.io/docs/latest/commands/lrange) is not very efficient if the list of posts start to be very\nbig, and we want to access elements which are in the middle of the list, since Redis Lists are backed by linked lists. If a system is designed for\ndeep pagination of million of items, it is better to resort to Sorted Sets\ninstead.*\n\nFollowing users\n---\n\nIt is not hard, but we did not yet check how we create following / follower relationships. If user ID 1000 (antirez) wants to follow user ID 5000 (pippo), we need to create both a following and a follower relationship. We just need to [`ZADD`](https://redis.io/docs/latest/commands/zadd) calls:\n\n        ZADD following:1000 5000\n        ZADD followers:5000 1000\n\nNote the same pattern again and again. In theory with a relational database, the list of following and followers would be contained in a single table with fields like `following_id` and `follower_id`. You can extract the followers or following of every user using an SQL query. With a key-value DB things are a bit different since we need to set both the `1000 is following 5000` and `5000 is followed by 1000` relations. This is the price to pay, but on the other hand accessing the data is simpler and extremely fast. Having these things as separate sets allows us to do interesting stuff. For example, using [`ZINTERSTORE`](https://redis.io/docs/latest/commands/zinterstore) we can have the intersection of `following` of two different users, so we may add a feature to our Twitter clone so that it is able to tell you very quickly when you visit somebody else's profile, \"you and Alice have 34 followers in common\", and things like that.\n\nYou can find the code that sets or removes a following / follower relation in the `follow.php` file.\n\nMaking it horizontally scalable\n---\n\nGentle reader, if you read till this point you are already a hero. Thank you. Before talking about scaling horizontally it is worth checking performance on a single server. Retwis is *extremely fast*, without any kind of cache. On a very slow and loaded server, an Apache benchmark with 100 parallel clients issuing 100000 requests measured the average pageview to take 5 milliseconds. This means you can serve millions of users every day with just a single Linux box, and this one was monkey ass slow... Imagine the results with more recent hardware.\n\nHowever you can't go with a single server forever, how do you scale a key-value\nstore?\n\nRetwis does not perform any multi-keys operation, so making it scalable is\nsimple: you may use client-side sharding, or something like a sharding proxy\nlike Twemproxy, or the upcoming Redis Cluster.\n\nTo know more about those topics please read\n[our documentation about sharding](https://redis.io/docs/latest/operate/oss_and_stack/management/scaling). However, the point here\nto stress is that in a key-value store, if you design with care, the data set\nis split among **many independent small keys**. To distribute those keys\nto multiple nodes is more straightforward and predictable compared to using\na semantically more complex database system."}],"examples":[]}
{"schema_version":2,"id":"develop/clients/php/connect","title":"Connect to the server","url":"https://redis.io/docs/latest/develop/clients/php/connect/","summary":"Connect your PHP application to a Redis database","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-12T14:48:04+01:00","page_type":"content","content_hash":"3f87587c2795724b5604df85d58764ba70579e74cb1d2faf11bc8c3f7d6dcec5","sections":[{"id":"basic-connection","title":"Basic connection","role":"overview","text":"Connect to a locally-running server on the standard port (6379)\nwith the following code:\n\n[code example]\n\n> [!NOTE]\n> Predis supports [connection URLs](https://redis.io/docs/latest/develop/clients/connection-urls#predis-php) through the `PredisClient` constructor.\n> A URL can set the host, port, credentials, database, and whether to use TLS,\n> so you can use one instead of passing those options separately.\n\nStore and retrieve a simple string to test the connection:\n\n[code example]\n\nStore and retrieve a [hash](https://redis.io/docs/latest/develop/data-types/hashes)\nobject:\n\n[code example]"},{"id":"connect-to-a-redis-cluster","title":"Connect to a Redis cluster","role":"content","text":"To connect to a Redis cluster, specify one or more of the nodes in\nthe `clusterNodes` parameter and set `'cluster'=>'redis'` in\n`options`:\n\n[code example]"},{"id":"connect-to-redis-sentinel","title":"Connect to Redis Sentinel","role":"content","text":"To connect through [Redis Sentinel](https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel),\npass a list of Sentinels as the first parameter. In `options`, set\n`'replication' => 'sentinel'` and set `service` to the name of the primary that the\nSentinels monitor:\n\n[code example]\n\nThe default timeout for connecting to a Sentinel is 100 milliseconds. If your Sentinels\nare on remote hosts, add a longer timeout to each Sentinel address, such as\n`tcp://<host>:26379?timeout=0.5`.\n\nThe Sentinels and the data nodes have separate credentials. Add the Sentinel credentials\nto each Sentinel address, and set the credentials for the primary and replicas in the\n`parameters` option. Include the Sentinel `username` even if it's `default`, because\notherwise Predis uses the `username` from `parameters` for the Sentinels too:\n\n[code example]\n\nPredis sends read-only commands to a replica until the first write command. After that, it\nsends all commands to the primary. Call `switchToSlave()` to send reads to a replica again.\nReplication is asynchronous, so a replica can return stale data.\n\n[code example]\n\nPredis doesn't listen for failover announcements from the Sentinels. After a failover, the\nclient keeps using the old primary until Sentinel reconfigures it as a replica and closes\nthe connection. Any writes that the old primary accepts during that time are lost."},{"id":"connect-to-your-production-redis-with-tls","title":"Connect to your production Redis with TLS","role":"content","text":"When you deploy your application, use TLS and follow the\n[Redis security](https://redis.io/docs/latest/operate/oss_and_stack/management/security)\nguidelines.\n\nUse the following commands to generate the client certificate and private key:\n\n[code example]\n\nIf you have the [Redis source folder](https://github.com/redis/redis) available,\nyou can also generate the certificate and private key with these commands:\n\n[code example]\n\nPass this information during connection using the `ssl` section of `options`:\n\n[code example]"}],"examples":[{"id":"basic-connection-ex0","language":"php","code":"<?php\n\nrequire 'vendor/autoload.php';\n\nuse Predis\\Client as PredisClient;\n\n$r = new PredisClient([\n                'scheme'   => 'tcp',\n                'host'     => '127.0.0.1',\n                'port'     => 6379,\n                'password' => '',\n                'database' => 0,\n            ]);","section_id":"basic-connection"},{"id":"basic-connection-ex1","language":"php","code":"echo $r->set('foo', 'bar'), PHP_EOL;\n// >>> OK\n\necho $r->get('foo'), PHP_EOL;\n// >>> bar","section_id":"basic-connection"},{"id":"basic-connection-ex2","language":"php","code":"$r->hset('user-session:123', 'name', 'John');\n$r->hset('user-session:123', 'surname', 'Smith');\n$r->hset('user-session:123', 'company', 'Redis');\n$r->hset('user-session:123', 'age', 29);\n\necho var_export($r->hgetall('user-session:123')), PHP_EOL;\n/* >>>\narray (\n  'name' => 'John',\n  'surname' => 'Smith',\n  'company' => 'Redis',\n  'age' => '29',\n)\n*/","section_id":"basic-connection"},{"id":"connect-to-a-redis-cluster-ex0","language":"php","code":"$clusterNodes = [\n    'tcp://127.0.0.1:30001', // Node 1\n    'tcp://127.0.0.1:30002', // Node 2\n    'tcp://127.0.0.1:30003', // Node 3\n];\n$options    = ['cluster' => 'redis'];\n\n// Create a Predis client for the cluster\n$rc = new PredisClient($clusterNodes, $options);\n\necho $rc->cluster('nodes'), PHP_EOL;\n/* >>>\nd8773e888e92d015b7c52fc66798fd6815afefec 127.0.0.1:30004@40004 slave cde97d1f7dce13e9253ace5cafd3fb0aa67cda63 0 1730713764217 1 connected\n58fe1346de4c425d60db24e9b153926fbde0d174 127.0.0.1:30002@40002 master - 0 1730713763361 2 connected 5461-10922\n015ecc8148a05377dda22f19921d16efcdd6d678 127.0.0.1:30006@40006 slave c019b75d8b52e83e7e52724eccc716ac553f71d6 0 1730713764218 3 connected\naca365963a72642e6ae0c9503aabf3be5c260806 127.0.0.1:30005@40005 slave 58fe1346de4c425d60db24e9b153926fbde0d174 0 1730713763363 2 connected\nc019b75d8b52e83e7e52724eccc716ac553f71d6 127.0.0.1:30003@40003 myself,master - 0 1730713764000 3 connected 10923-16383\ncde97d1f7dce13e9253ace5cafd3fb0aa67cda63 127.0.0.1:30001@40001 master - 0 1730713764113 1 connected 0-5460\n*/\n\necho $rc->set('foo', 'bar'), PHP_EOL;\n// >>> OK\necho $rc->get('foo'), PHP_EOL;\n// >>> bar","section_id":"connect-to-a-redis-cluster"},{"id":"connect-to-redis-sentinel-ex0","language":"php","code":"$sentinels = [\n    'tcp://localhost:26379',\n    'tcp://localhost:26380',\n    'tcp://localhost:26381',\n];\n$options = [\n    'replication' => 'sentinel',\n    'service'     => 'mymaster',\n];\n\n$r = new PredisClient($sentinels, $options);\n\necho $r->set('foo', 'bar'), PHP_EOL;\n// >>> OK\n\necho $r->get('foo'), PHP_EOL;\n// >>> bar","section_id":"connect-to-redis-sentinel"},{"id":"connect-to-redis-sentinel-ex1","language":"php","code":"// Credentials for the Sentinels.\n$sentinels = [\n    'tcp://localhost:26379?username=yourSentinelUsername&password=yourSentinelPassword',\n    'tcp://localhost:26380?username=yourSentinelUsername&password=yourSentinelPassword',\n    'tcp://localhost:26381?username=yourSentinelUsername&password=yourSentinelPassword',\n];\n$options = [\n    'replication' => 'sentinel',\n    'service'     => 'mymaster',\n    // Credentials for the primary and replicas.\n    'parameters'  => [\n        'username' => 'yourUsername',\n        'password' => 'yourPassword',\n    ],\n];\n\n$r = new PredisClient($sentinels, $options);","section_id":"connect-to-redis-sentinel"},{"id":"connect-to-redis-sentinel-ex2","language":"php","code":"// A write command switches the client to the primary.\necho $r->set('foo', 'baz'), PHP_EOL;\n// >>> OK\n\n// Switch back to a replica for later reads.\n$r->getConnection()->switchToSlave();\necho $r->get('foo'), PHP_EOL;\n// >>> baz","section_id":"connect-to-redis-sentinel"},{"id":"connect-to-your-production-redis-with-tls-ex0","language":"bash","code":"openssl genrsa -out redis_user_private.key 2048\nopenssl req -new -key redis_user_private.key -out redis_user.csr\nopenssl x509 -req -days 365 -in redis_user.csr -signkey redis_user_private.key -out redis_user.crt","section_id":"connect-to-your-production-redis-with-tls"},{"id":"connect-to-your-production-redis-with-tls-ex1","language":"bash","code":"./utils/gen-test-certs.sh\n./src/redis-server --tls-port 6380 --port 0 --tls-cert-file ./tests/tls/redis.crt --tls-key-file ./tests/tls/redis.key --tls-ca-cert-file ./tests/tls/ca.crt","section_id":"connect-to-your-production-redis-with-tls"},{"id":"connect-to-your-production-redis-with-tls-ex2","language":"php","code":"$options = [\n    'scheme' => 'tls', // Use 'tls' for SSL connections\n    'host' => '127.0.0.1', // Redis server hostname\n    'port' => 6379, // Redis server port\n    'username' => 'default', // Redis username\n    'password' => '', // Redis password\n    'options' => [\n        'ssl' => [\n            'verify_peer' => true, // Verify the server's SSL certificate\n            'cafile' => './redis_ca.pem', // Path to CA certificate\n            'local_cert' => './redis_user.crt', // Path to client certificate\n            'local_pk' => './redis_user_private.key', // Path to client private key\n        ],\n    ],\n];\n\n$tlsConnection = new PredisClient($options);\n\necho $tlsConnection->set('foo', 'bar'), PHP_EOL;\n// >>> OK\necho $tlsConnection->get('foo'), PHP_EOL;\n// >>> bar","section_id":"connect-to-your-production-redis-with-tls"}]}
{"schema_version":2,"id":"develop/clients/php/error-handling","title":"Error handling","url":"https://redis.io/docs/latest/develop/clients/php/error-handling/","summary":"Learn how to handle errors when using Predis.","tags":[],"last_updated":"2026-09-09T09:49:12+01:00","page_type":"content","content_hash":"dc37788296c2aef7e9edef501640eab3aa102605e23104957303cac746b7ff76","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Predis uses **exceptions** to signal errors. Documentation examples often omit\nerror handling for brevity, but production code should distinguish between\ntransient transport errors and server-side command errors. This page explains\nhow Predis error handling works and how to apply common error handling\npatterns.\n\nFor an overview of error types and handling strategies, see\n[Error handling](https://redis.io/docs/latest/develop/clients/error-handling)."},{"id":"exception-hierarchy","title":"Exception hierarchy","role":"errors","text":"Predis groups its exceptions under `PredisException`:\n\n[code example]"},{"id":"key-exceptions","title":"Key exceptions","role":"content","text":"The following exceptions are the most commonly encountered in Predis\napplications. See\n[Categories of errors](https://redis.io/docs/latest/develop/clients/error-handling#categories-of-errors)\nfor a more detailed discussion of these errors and their causes.\n\n| Exception | When it occurs | Recoverable | Recommended action |\n|---|---|---|---|\n| `Predis\\Connection\\ConnectionException` | Predis could not connect or lost the connection | ✅ | Retry with backoff or fall back |\n| `Predis\\CommunicationException` | A transport-level error occurred while reading or writing | ✅ | Retry with backoff and reconnect |\n| `Predis\\Response\\ServerException` | Redis returned an error reply such as `WRONGTYPE` | ❌ | Fix the command, arguments, or data model |\n| `Predis\\Transaction\\AbortedMultiExecException` | A watched transaction was aborted | ✅ | Reload state and retry the transaction |"},{"id":"applying-error-handling-patterns","title":"Applying error handling patterns","role":"content","text":"The [Error handling](https://redis.io/docs/latest/develop/clients/error-handling) overview\ndescribes four main patterns. The sections below show how to implement them in\nPredis:"},{"id":"pattern-1-fail-fast","title":"Pattern 1: Fail fast","role":"content","text":"Catch specific exceptions that represent unrecoverable errors and re-throw them\n(see\n[Pattern 1: Fail fast](https://redis.io/docs/latest/develop/clients/error-handling#pattern-1-fail-fast)\nfor a full description):\n\n[code example]"},{"id":"pattern-2-graceful-degradation","title":"Pattern 2: Graceful degradation","role":"content","text":"Catch connection problems and fall back to an alternative (see\n[Pattern 2: Graceful degradation](https://redis.io/docs/latest/develop/clients/error-handling#pattern-2-graceful-degradation)\nfor a full description):\n\n[code example]"},{"id":"pattern-3-retry-with-backoff","title":"Pattern 3: Retry with backoff","role":"content","text":"Retry on temporary communication failures (see\n[Pattern 3: Retry with backoff](https://redis.io/docs/latest/develop/clients/error-handling#pattern-3-retry-with-backoff)\nfor a full description):\n\n[code example]"},{"id":"pattern-4-log-and-continue","title":"Pattern 4: Log and continue","role":"content","text":"Log non-critical failures and continue (see\n[Pattern 4: Log and continue](https://redis.io/docs/latest/develop/clients/error-handling#pattern-4-log-and-continue)\nfor a full description):\n\n[code example]"},{"id":"transaction-retries","title":"Transaction retries","role":"content","text":"Predis raises `AbortedMultiExecException` when an optimistic-locking\ntransaction aborts. Handle this the same way as other retryable conflicts:\n\n[code example]"},{"id":"see-also","title":"See also","role":"related","text":"- [Error handling](https://redis.io/docs/latest/develop/clients/error-handling)\n- [Pipelines and transactions](https://redis.io/docs/latest/develop/clients/php/transpipe)"}],"examples":[{"id":"exception-hierarchy-ex0","language":"hierarchy {type=\"exception\"}","code":"\"PredisException\":\n    _meta:\n        description: \"Base class for Predis exceptions\"\n    \"CommunicationException\":\n        \"ConnectionException\":\n        \"ProtocolException\":\n    \"ClientException\":\n    \"ServerException\":\n    \"AbortedMultiExecException\":\n    \"...\":\n        _meta:\n            ellipsis: true\n            description: \"Other Predis exception types\"","section_id":"exception-hierarchy"},{"id":"pattern-1-fail-fast-ex0","language":"php","code":"use Predis\\Response\\ServerException;\n\ntry {\n    return $r->get($key);\n} catch (ServerException $e) {\n    // This indicates a bug in our code or schema.\n    throw $e;\n}","section_id":"pattern-1-fail-fast"},{"id":"pattern-2-graceful-degradation-ex0","language":"php","code":"use Predis\\Connection\\ConnectionException;\n\ntry {\n    $cachedValue = $r->get($key);\n    if ($cachedValue !== null) {\n        return $cachedValue;\n    }\n} catch (ConnectionException $e) {\n    $logger->warning('Cache unavailable, using database');\n}\n\nreturn $database->get($key);","section_id":"pattern-2-graceful-degradation"},{"id":"pattern-3-retry-with-backoff-ex0","language":"php","code":"use Predis\\CommunicationException;\n\n$delayMs = 100;\n\nfor ($attempt = 0; $attempt < 3; $attempt++) {\n    try {\n        return $r->get($key);\n    } catch (CommunicationException $e) {\n        if ($attempt === 2) {\n            throw $e;\n        }\n\n        usleep($delayMs * 1000);\n        $delayMs *= 2;\n    }\n}","section_id":"pattern-3-retry-with-backoff"},{"id":"pattern-4-log-and-continue-ex0","language":"php","code":"use Predis\\CommunicationException;\n\ntry {\n    $r->setex($key, 3600, $value);\n} catch (CommunicationException $e) {\n    $logger->warning(\"Failed to cache {$key}, continuing without cache\");\n}","section_id":"pattern-4-log-and-continue"},{"id":"transaction-retries-ex0","language":"php","code":"use Predis\\Transaction\\AbortedMultiExecException;\n\nfor ($attempt = 0; $attempt < 3; $attempt++) {\n    try {\n        return $r->transaction(['cas' => true, 'watch' => $key], function ($tx) use ($key) {\n            $current = $tx->get($key);\n            $tx->multi();\n            $tx->set($key, strtoupper($current));\n        });\n    } catch (AbortedMultiExecException $e) {\n        if ($attempt === 2) {\n            throw $e;\n        }\n    }\n}","section_id":"transaction-retries"}]}
{"schema_version":2,"id":"develop/clients/php","title":"Predis guide (PHP)","url":"https://redis.io/docs/latest/develop/clients/php/","summary":"Connect your PHP application to a Redis database","aliases":["/develop/connect/clients/php","/connect/clients/php/","/clients/php/","/develop/clients/predis"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-09T09:49:12+01:00","children":[{"id":"develop/clients/php/connect","summary":"Connect your PHP application to a Redis database","title":"Connect to the server","url":"https://redis.io/docs/latest/develop/clients/php/connect/"},{"id":"develop/clients/php/queryjson","summary":"Learn how to use Redis Search with JSON and hash documents.","title":"Index and query documents","url":"https://redis.io/docs/latest/develop/clients/php/queryjson/"},{"id":"develop/clients/php/vecsearch","summary":"Learn how to index and query vector embeddings with Redis","title":"Index and query vectors","url":"https://redis.io/docs/latest/develop/clients/php/vecsearch/"},{"id":"develop/clients/php/transpipe","summary":"Learn how to use Redis pipelines and transactions","title":"Pipelines and transactions","url":"https://redis.io/docs/latest/develop/clients/php/transpipe/"},{"id":"develop/clients/php/vecsets","summary":"Index and query embeddings with Redis vector sets","title":"Vector set embeddings","url":"https://redis.io/docs/latest/develop/clients/php/vecsets/"},{"id":"develop/clients/php/prob","summary":"Learn how to use approximate calculations with Redis.","title":"Probabilistic data types","url":"https://redis.io/docs/latest/develop/clients/php/prob/"},{"id":"develop/clients/php/error-handling","summary":"Learn how to handle errors when using Predis.","title":"Error handling","url":"https://redis.io/docs/latest/develop/clients/php/error-handling/"}],"page_type":"content","content_hash":"73894555131365fda0c751e6f76cec138218354e3bf5800941e5a57d0c941da4","sections":[{"id":"install","title":"Install","role":"setup","text":"Use [Composer](https://getcomposer.org/) to install the `Predis` library\nwith the following command line:\n\n[code example]"},{"id":"connect-and-test","title":"Connect and test","role":"content","text":"Connect to a locally-running server on the standard port (6379)\nwith the following code:\n\nFoundational: Connect to a Redis server and establish a client connection\n\n**Difficulty:** Beginner\n\n**Available in:** PHP\n\n##### PHP\n\n[code example]\n\n\n\nStore and retrieve a simple string to test the connection:\n\nFoundational: Set and retrieve string values using SET and GET commands\n\n**Difficulty:** Beginner\n\n**Available in:** PHP\n\n##### PHP\n\n[code example]\n\n\n\nStore and retrieve a [hash](https://redis.io/docs/latest/develop/data-types/hashes)\nobject:\n\nFoundational: Store and retrieve hash data structures using HSET and HGETALL\n\n**Difficulty:** Beginner\n\n**Available in:** PHP\n\n##### PHP\n\n[code example]"},{"id":"more-information","title":"More information","role":"content","text":"The [Predis wiki on Github](https://github.com/predis/predis/wiki) has\ninformation about the different connection options you can use.\n\nSee also the pages in this section for more information and examples:"}],"examples":[{"id":"install-ex0","language":"bash","code":"composer require predis/predis","section_id":"install"},{"id":"connect-and-test-ex0","language":"php","code":"<?php\n\nrequire 'vendor/autoload.php';\n\nuse Predis\\Client as PredisClient;\n\n$r = new PredisClient([\n                'scheme'   => 'tcp',\n                'host'     => '127.0.0.1',\n                'port'     => 6379,\n                'password' => '',\n                'database' => 0,\n            ]);","section_id":"connect-and-test"},{"id":"connect-and-test-ex1","language":"php","code":"echo $r->set('foo', 'bar'), PHP_EOL;\n// >>> OK\n\necho $r->get('foo'), PHP_EOL;\n// >>> bar","section_id":"connect-and-test"},{"id":"connect-and-test-ex2","language":"php","code":"$r->hset('user-session:123', 'name', 'John');\n$r->hset('user-session:123', 'surname', 'Smith');\n$r->hset('user-session:123', 'company', 'Redis');\n$r->hset('user-session:123', 'age', 29);\n\necho var_export($r->hgetall('user-session:123')), PHP_EOL;\n/* >>>\narray (\n  'name' => 'John',\n  'surname' => 'Smith',\n  'company' => 'Redis',\n  'age' => '29',\n)\n*/","section_id":"connect-and-test"}]}
{"schema_version":2,"id":"develop/clients/php/prob","title":"Probabilistic data types","url":"https://redis.io/docs/latest/develop/clients/php/prob/","summary":"Learn how to use approximate calculations with Redis.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-09T09:49:12+01:00","page_type":"content","content_hash":"d53650f22b61d5ef4fa6ad60d697263acfb88dc27898d15583675cf424f36239","sections":[{"id":"set-operations","title":"Set operations","role":"content","text":"Redis supports the following approximate set operations:\n\n-   [Membership](#set-membership): The\n    [Bloom filter](https://redis.io/docs/latest/develop/data-types/probabilistic/bloom-filter) and\n    [Cuckoo filter](https://redis.io/docs/latest/develop/data-types/probabilistic/cuckoo-filter)\n    data types let you track whether or not a given item is a member of a set.\n-   [Cardinality](#set-cardinality): The\n    [HyperLogLog](https://redis.io/docs/latest/develop/data-types/probabilistic/hyperloglogs)\n    data type gives you an approximate value for the number of items in a set, also\n    known as the *cardinality* of the set.\n\nThe sections below describe these operations in more detail."},{"id":"set-membership","title":"Set membership","role":"content","text":"[Bloom filter](https://redis.io/docs/latest/develop/data-types/probabilistic/bloom-filter) and\n[Cuckoo filter](https://redis.io/docs/latest/develop/data-types/probabilistic/cuckoo-filter)\nobjects provide a set membership operation that lets you track whether or not a\nparticular item has been added to a set. These two types provide different\ntrade-offs for memory usage and speed, so you can select the best one for your\nuse case. Note that for both types, there is an asymmetry between presence and\nabsence of items in the set. If an item is reported as absent, then it is definitely\nabsent, but if it is reported as present, then there is a small chance it may really be\nabsent.\n\nInstead of storing strings directly, like a [set](https://redis.io/docs/latest/develop/data-types/sets),\na Bloom filter records the presence or absence of the\n[hash value](https://en.wikipedia.org/wiki/Hash_function) of a string.\nThis gives a very compact representation of the\nset's membership with a fixed memory size, regardless of how many items you\nadd. The following example adds some names to a Bloom filter representing\na list of users and checks for the presence or absence of users in the list.\n\nFoundational: Use Bloom filters for memory-efficient set membership testing with false positive possibility\n\n**Difficulty:** Beginner\n\n**Available in:** PHP\n\n##### PHP\n\n[code example]\n\n\n\nA Cuckoo filter has similar features to a Bloom filter, but also supports\na deletion operation to remove hashes from a set, as shown in the example\nbelow.\n\nFoundational: Use Cuckoo filters for set membership testing with deletion support and faster lookups than Bloom filters\n\n**Difficulty:** Beginner\n\n**Available in:** PHP\n\n##### PHP\n\n[code example]\n\n\n\nWhich of these two data types you choose depends on your use case.\nBloom filters are generally faster than Cuckoo filters when adding new items,\nand also have better memory usage. Cuckoo filters are generally faster\nat checking membership and also support the delete operation. See the\n[Bloom filter](https://redis.io/docs/latest/develop/data-types/probabilistic/bloom-filter) and\n[Cuckoo filter](https://redis.io/docs/latest/develop/data-types/probabilistic/cuckoo-filter)\nreference pages for more information and comparison between the two types."},{"id":"set-cardinality","title":"Set cardinality","role":"content","text":"A [HyperLogLog](https://redis.io/docs/latest/develop/data-types/probabilistic/hyperloglogs)\nobject calculates the cardinality of a set. As you add\nitems, the HyperLogLog tracks the number of distinct set members but\ndoesn't let you retrieve them or query which items have been added.\nYou can also merge two or more HyperLogLogs to find the cardinality of the\n[union](https://en.wikipedia.org/wiki/Union_(set_theory)) of the sets they\nrepresent.\n\nFoundational: Estimate set cardinality with HyperLogLog for memory-efficient counting of distinct items\n\n**Difficulty:** Beginner\n\n**Available in:** PHP\n\n##### PHP\n\n[code example]\n\n\n\nThe main benefit that HyperLogLogs offer is their very low\nmemory usage. They can count up to 2^64 items with less than\n1% standard error using a maximum 12KB of memory. This makes\nthem very useful for counting things like the total of distinct\nIP addresses that access a website or the total of distinct\nbank card numbers that make purchases within a day."},{"id":"statistics","title":"Statistics","role":"content","text":"Redis supports several approximate statistical calculations\non numeric data sets:\n\n-   [Frequency](#frequency): The\n    [Count-min sketch](https://redis.io/docs/latest/develop/data-types/probabilistic/count-min-sketch)\n    data type lets you find the approximate frequency of a labeled item in a data stream.\n-   [Quantiles](#quantiles): The\n    [t-digest](https://redis.io/docs/latest/develop/data-types/probabilistic/t-digest)\n    data type estimates the quantile of a query value in a data stream.\n-   [Ranking](#ranking): The\n    [Top-K](https://redis.io/docs/latest/develop/data-types/probabilistic/top-k) data type\n    estimates the ranking of labeled items by frequency in a data stream.\n\nThe sections below describe these operations in more detail."},{"id":"frequency","title":"Frequency","role":"content","text":"A [Count-min sketch](https://redis.io/docs/latest/develop/data-types/probabilistic/count-min-sketch)\n(CMS) object keeps count of a set of related items represented by\nstring labels. The count is approximate, but you can specify\nhow close you want to keep the count to the true value (as a fraction)\nand the acceptable probability of failing to keep it in this\ndesired range. For example, you can request that the count should\nstay within 0.1% of the true value and have a 0.05% probability\nof going outside this limit. The example below shows how to create\na Count-min sketch object, add data to it, and then query it.\n\nFoundational: Track approximate item frequencies with Count-min sketch for memory-efficient statistics on data streams\n\n**Difficulty:** Intermediate\n\n**Available in:** PHP\n\n##### PHP\n\n[code example]\n\n\n\nThe advantage of using a CMS over keeping an exact count with a\n[sorted set](https://redis.io/docs/latest/develop/data-types/sorted-sets)\nis that a CMS has very low and fixed memory usage, even for\nlarge numbers of items. Use CMS objects to keep daily counts of\nitems sold, accesses to individual web pages on your site, and\nother similar statistics."},{"id":"quantiles","title":"Quantiles","role":"content","text":"A [quantile](https://en.wikipedia.org/wiki/Quantile) is the value\nbelow which a certain fraction of samples lie. For example, with\na set of measurements of people's heights, the quantile of 0.75 is\nthe value of height below which 75% of all people's heights lie.\n[Percentiles](https://en.wikipedia.org/wiki/Percentile) are equivalent\nto quantiles, except that the fraction is expressed as a percentage.\n\nA [t-digest](https://redis.io/docs/latest/develop/data-types/probabilistic/t-digest)\nobject can estimate quantiles from a set of values added to it\nwithout having to store each value in the set explicitly. This can\nsave a lot of memory when you have a large number of samples.\n\nThe example below shows how to add data samples to a t-digest\nobject and obtain some basic statistics, such as the minimum and\nmaximum values, the quantile of 0.75, and the\n[cumulative distribution function](https://en.wikipedia.org/wiki/Cumulative_distribution_function)\n(CDF), which is effectively the inverse of the quantile function. It also\nshows how to merge two or more t-digest objects to query the combined\ndata set.\n\nFoundational: Estimate quantiles and percentiles with t-digest for memory-efficient statistical analysis of large datasets\n\n**Difficulty:** Intermediate\n\n**Available in:** PHP\n\n##### PHP\n\n[code example]\n\n\n\nA t-digest object also supports several other related commands, such\nas querying by rank. See the\n[t-digest](https://redis.io/docs/latest/develop/data-types/probabilistic/t-digest)\nreference for more information."},{"id":"ranking","title":"Ranking","role":"content","text":"A [Top-K](https://redis.io/docs/latest/develop/data-types/probabilistic/top-k)\nobject estimates the rankings of different labeled items in a data\nstream according to frequency. For example, you could use this to\ntrack the top ten most frequently-accessed pages on a website, or the\ntop five most popular items sold.\n\nThe example below adds several different items to a Top-K object\nthat tracks the top three items (this is the second parameter to\nthe `topkreserve()` method). It also shows how to list the\ntop *k* items and query whether or not a given item is in the\nlist.\n\nFoundational: Track top K most frequent items in a data stream with Top-K for efficient ranking without storing all items\n\n**Difficulty:** Intermediate\n\n**Available in:** PHP\n\n##### PHP\n\n[code example]"}],"examples":[{"id":"set-membership-ex0","language":"php","code":"$r->del('recorded_users');\n$r->bfreserve('recorded_users', 0.01, 1000);\n\n$added = $r->bfmadd('recorded_users', 'andy', 'cameron', 'david', 'michelle');\necho json_encode($added), PHP_EOL;\n// >>> [1,1,1,1]\n\n$known = $r->bfexists('recorded_users', 'cameron');\necho $known, PHP_EOL;\n// >>> 1\n\n$unknown = $r->bfexists('recorded_users', 'kaitlyn');\necho $unknown, PHP_EOL;\n// >>> 0","section_id":"set-membership"},{"id":"set-membership-ex1","language":"php","code":"$r->del('other_users');\n$r->cfreserve('other_users', 1000);\n\n$r->cfadd('other_users', 'paolo');\n$r->cfadd('other_users', 'kaitlyn');\n$r->cfadd('other_users', 'rachel');\n\n$beforeDelete = [\n    $r->cfexists('other_users', 'paolo'),\n    $r->cfexists('other_users', 'kaitlyn'),\n    $r->cfexists('other_users', 'rachel'),\n    $r->cfexists('other_users', 'andy'),\n];\necho json_encode($beforeDelete), PHP_EOL;\n// >>> [1,1,1,0]\n\n$r->cfdel('other_users', 'paolo');\n$afterDelete = $r->cfexists('other_users', 'paolo');\necho $afterDelete, PHP_EOL;\n// >>> 0","section_id":"set-membership"},{"id":"set-cardinality-ex0","language":"php","code":"$r->del('group:1', 'group:2', 'both_groups');\n\n$r->pfadd('group:1', ['andy', 'cameron', 'david']);\n$group1 = $r->pfcount('group:1');\necho $group1, PHP_EOL;\n// >>> 3\n\n$r->pfadd('group:2', ['kaitlyn', 'michelle', 'paolo', 'rachel']);\n$group2 = $r->pfcount('group:2');\necho $group2, PHP_EOL;\n// >>> 4\n\n$r->pfmerge('both_groups', 'group:1', 'group:2');\n$bothGroups = $r->pfcount('both_groups');\necho $bothGroups, PHP_EOL;\n// >>> 7","section_id":"set-cardinality"},{"id":"frequency-ex0","language":"php","code":"$r->del('items_sold');\n$r->cmsinitbyprob('items_sold', 0.01, 0.005);\n\n$firstCounts = $r->cmsincrby(\n    'items_sold',\n    'bread', 300,\n    'tea', 200,\n    'coffee', 200,\n    'beer', 100\n);\necho json_encode($firstCounts), PHP_EOL;\n// >>> [300,200,200,100]\n\n$secondCounts = $r->cmsincrby(\n    'items_sold',\n    'bread', 100,\n    'coffee', 150\n);\necho json_encode($secondCounts), PHP_EOL;\n// >>> [400,350]\n\n$queriedCounts = $r->cmsquery('items_sold', 'bread', 'tea', 'coffee', 'beer');\necho json_encode($queriedCounts), PHP_EOL;\n// >>> [400,200,350,100]","section_id":"frequency"},{"id":"quantiles-ex0","language":"php","code":"$r->del('male_heights', 'female_heights', 'all_heights');\n\n$r->tdigestcreate('male_heights');\n$r->tdigestadd('male_heights', 175.5, 181, 160.8, 152, 177, 196, 164);\n\n$maleMin = $r->tdigestmin('male_heights');\necho $maleMin, PHP_EOL;\n// >>> 152\n\n$maleMax = $r->tdigestmax('male_heights');\necho $maleMax, PHP_EOL;\n// >>> 196\n\n$maleQuantile = $r->tdigestquantile('male_heights', 0.75);\necho json_encode($maleQuantile), PHP_EOL;\n// >>> [\"181\"]\n\n$maleCdf = $r->tdigestcdf('male_heights', 181);\necho json_encode($maleCdf), PHP_EOL;\n// >>> [\"0.7857142857142857\"]\n\n$r->tdigestcreate('female_heights');\n$r->tdigestadd('female_heights', 155.5, 161, 168.5, 170, 157.5, 163, 171);\n\n$femaleQuantile = $r->tdigestquantile('female_heights', 0.75);\necho json_encode($femaleQuantile), PHP_EOL;\n// >>> [\"170\"]\n\n$r->tdigestmerge('all_heights', ['male_heights', 'female_heights']);\n$allQuantile = $r->tdigestquantile('all_heights', 0.75);\necho json_encode($allQuantile), PHP_EOL;\n// >>> [\"175.5\"]","section_id":"quantiles"},{"id":"ranking-ex0","language":"php","code":"$r->del('top_3_songs');\n$r->topkreserve('top_3_songs', 3, 7, 8, 0.9);\n\n$evicted = $r->topkadd(\n    'top_3_songs',\n    'Starfish Trooper',\n    'Only one more time',\n    'Rock me, Handel',\n    'How will anyone know?',\n    'Average lover',\n    'Road to everywhere'\n);\necho json_encode($evicted), PHP_EOL;\n// >>> [null,null,null,\"Rock me, Handel\",\"Only one more time\",null]\n\n$leaders = $r->topklist('top_3_songs');\necho json_encode($leaders), PHP_EOL;\n// >>> [\"Average lover\",\"How will anyone know?\",\"Starfish Trooper\"]\n\n$membership = $r->topkquery('top_3_songs', 'Starfish Trooper', 'Road to everywhere');\necho json_encode($membership), PHP_EOL;\n// >>> [1,0]","section_id":"ranking"}]}
{"schema_version":2,"id":"develop/clients/php/queryjson","title":"Index and query documents","url":"https://redis.io/docs/latest/develop/clients/php/queryjson/","summary":"Learn how to use Redis Search with JSON and hash documents.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-16T16:25:49+01:00","page_type":"content","content_hash":"a6a7d06c47ec87523525b73972546dab14e5de26150ed3d88547eb868fef90d1","sections":[{"id":"initialize","title":"Initialize","role":"content","text":"Make sure that you have [Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack)\nor another Redis server available. Also install the\n[`Predis`](https://redis.io/docs/latest/develop/clients/php) client library if you\nhaven't already done so.\n\nAdd the following dependencies:\n\nFoundational: Import required dependencies for Redis Search and JSON operations\n\n**Difficulty:** Beginner\n\n**Available in:** PHP\n\n##### PHP\n\n[code example]"},{"id":"create-data","title":"Create data","role":"content","text":"Create some test data to add to your database:\n\nFoundational: Create sample JSON data structures for indexing and querying\n\n**Difficulty:** Beginner\n\n**Available in:** PHP\n\n##### PHP\n\n[code example]"},{"id":"add-the-index","title":"Add the index","role":"content","text":"Connect to your Redis database. The code below shows the most\nbasic connection but see\n[Connect to the server](https://redis.io/docs/latest/develop/clients/php/connect)\nto learn more about the available connection options.\n\nFoundational: Connect to a Redis server and establish a client connection\n\n**Difficulty:** Beginner\n\n**Available in:** PHP\n\n##### PHP\n\n[code example]\n\n\n\nCreate an\n[index](https://redis.io/docs/latest/develop/ai/search-and-query/indexing).\nIn this example, only JSON documents with the key prefix `user:` are indexed.\nFor more information, see\n[Query syntax](https://redis.io/docs/latest/develop/ai/search-and-query/query).\n\nFoundational: Create a search index for JSON documents with field schema and prefix filtering\n\n**Difficulty:** Intermediate\n\n**Available in:** PHP\n\n##### PHP\n\n[code example]"},{"id":"add-the-data","title":"Add the data","role":"content","text":"Add the three sets of user data to the database as\n[JSON](https://redis.io/docs/latest/develop/data-types/json) objects.\nIf you use keys with the `user:` prefix then Redis will index the\nobjects automatically as you add them:\n\nFoundational: Store JSON documents with indexed key prefixes for automatic indexing\n\n**Difficulty:** Beginner\n\n**Available in:** PHP\n\n##### PHP\n\n[code example]"},{"id":"query-the-data","title":"Query the data","role":"content","text":"You can now use the index to search the JSON objects. The\n[query](https://redis.io/docs/latest/develop/ai/search-and-query/query)\nbelow searches for objects that have the text \"Paul\" in any field\nand have an `age` value in the range 30 to 40:\n\nQuery data: Execute full-text and range queries on indexed JSON documents using FT.SEARCH\n\n**Difficulty:** Intermediate\n\n**Available in:** PHP\n\n##### PHP\n\n[code example]\n\n\n\nSpecify query options to return only the `city` field:\n\nRestrict query results: Use query options to return specific fields from search results\n\n**Difficulty:** Intermediate\n\n**Available in:** PHP\n\n##### PHP\n\n[code example]\n\n\n\nUse an\n[aggregation query](https://redis.io/docs/latest/develop/ai/search-and-query/query/aggregation)\nto count all users in each city.\n\nAggregation: Use aggregation queries to group and count results from indexed documents\n\n**Difficulty:** Advanced\n\n**Available in:** PHP\n\n##### PHP\n\n[code example]"},{"id":"differences-with-hash-documents","title":"Differences with hash documents","role":"content","text":"Indexing for hash documents is very similar to JSON indexing but you\nneed to specify some slightly different options.\n\nWhen you create the schema for a hash index, you don't need to\nadd aliases for the fields, since you use the basic names to access\nthe fields anyway. Also, you must use `HASH` for the `On()` option\nwhen you create the index. The code below shows these changes with\na new index called `hash-idx:users`, which is otherwise the same as\nthe `idx:users` index used for JSON documents in the previous examples.\n\nFoundational: Create a search index for hash documents with HASH type specification\n\n**Difficulty:** Intermediate\n\n**Available in:** PHP\n\n##### PHP\n\n[code example]\n\n\n\nYou use [`hmset()`](https://redis.io/docs/latest/commands/hset) to add the hash\ndocuments instead of [`jsonset()`](https://redis.io/docs/latest/commands/json.set).\nSupply the fields as an array directly, without using\n[`json_encode()`](https://www.php.net/manual/en/function.json-encode.php).\n\nFoundational: Store hash documents using HSET for indexed retrieval\n\n**Difficulty:** Beginner\n\n**Available in:** PHP\n\n##### PHP\n\n[code example]\n\n\n\nThe query commands work the same here for hash as they do for JSON (but\nthe name of the hash index is different). The format of the result is\nalmost the same except that the fields are returned directly in the\nresult array rather than in a JSON string with `$` as its key:\n\nQuery data: Query hash documents using the same search syntax as JSON with different result formatting\n\n**Difficulty:** Intermediate\n\n**Available in:** PHP\n\n##### PHP\n\n[code example]"},{"id":"bulk-import-with-himport","title":"Bulk import with HIMPORT","role":"content","text":"From Redis Open Source 8.10, you can also add the hash documents with the\n[`HIMPORT`](https://redis.io/docs/latest/commands/himport) command family. This creates the same\nhashes as `hmset()`, but lets Redis store them as\n[compact hashes](https://redis.io/docs/latest/develop/data-types/hashes#compact-hashes), which use\nless memory when many keys share the same field names. Redis Search indexes\nthese hashes in the same way.\n\nDeclare the field names once by calling `himport()` with\n[`PREPARE`](https://redis.io/docs/latest/commands/himport-prepare), then create each\ndocument by calling it with [`SET`](https://redis.io/docs/latest/commands/himport-set), passing only\nits values, in the same order as the field names. Use a\n[pipeline](https://redis.io/docs/latest/develop/clients/php/transpipe) to send all the\nimports in one round trip:\n\nBulk ingestion: Import hash documents as compact hashes using HIMPORT PREPARE and a pipeline of HIMPORT SET calls\n\n**Difficulty:** Intermediate\n\n**Builds upon:** add_hash_data\n\n**Available in:** PHP\n\n##### PHP\n\n[code example]\n\n\n\n`HIMPORT SET` replaces any existing key, so running this code after the\n`hmset()` calls leaves you with the same three documents. The query returns\nthe same result, although the fields can appear in a different order.\n\n> [!NOTE]\n> `HIMPORT` requires `Predis` v3.6.0 or later, and `Predis` marks its\n> support as experimental, so the API might change. Redis Cloud and\n> Redis Software don't support `HIMPORT`."},{"id":"more-information","title":"More information","role":"content","text":"See the [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) docs\nfor a full description of all query features with examples."}],"examples":[{"id":"initialize-ex0","language":"php","code":"<?php\n\nrequire 'vendor/autoload.php';\n\nuse Predis\\Client as PredisClient;\n\nuse Predis\\Command\\Argument\\Search\\AggregateArguments;\nuse Predis\\Command\\Argument\\Search\\CreateArguments;\nuse Predis\\Command\\Argument\\Search\\SearchArguments;\nuse Predis\\Command\\Argument\\Search\\SchemaFields\\NumericField;\nuse Predis\\Command\\Argument\\Search\\SchemaFields\\TextField;\nuse Predis\\Command\\Argument\\Search\\SchemaFields\\TagField;","section_id":"initialize"},{"id":"create-data-ex0","language":"php","code":"$user1 = json_encode([\n            'name' => 'Paul John',\n            'email' => 'paul.john@example.com',\n            'age' => 42,\n            'city' => 'London',\n        ], JSON_THROW_ON_ERROR);\n        \n        $user2 = json_encode([\n            'name' => 'Eden Zamir',\n            'email' => 'eden.zamir@example.com',\n            'age' => 29,\n            'city' => 'Tel Aviv',\n        ], JSON_THROW_ON_ERROR);\n        \n        $user3 = json_encode([\n            'name' => 'Paul Zamir',\n            'email' => 'paul.zamir@example.com',\n            'age' => 35,\n            'city' => 'Tel Aviv',\n        ], JSON_THROW_ON_ERROR);","section_id":"create-data"},{"id":"add-the-index-ex0","language":"php","code":"$r = new PredisClient([\n            'scheme'   => 'tcp',\n            'host'     => '127.0.0.1',\n            'port'     => 6379,\n            'password' => '',\n            'database' => 0,\n        ]);","section_id":"add-the-index"},{"id":"add-the-index-ex1","language":"php","code":"$schema = [\n            new TextField('$.name', 'name'),\n            new TagField('$.city', 'city'),\n            new NumericField('$.age', \"age\"),\n        ];\n        \n        try {\n        $r->ftCreate(\"idx:users\", $schema,\n            (new CreateArguments())\n                ->on('JSON')\n                ->prefix([\"user:\"]));\n        }\n        catch (Exception $e) {\n            echo $e->getMessage(), PHP_EOL;\n        }","section_id":"add-the-index"},{"id":"add-the-data-ex0","language":"php","code":"$r->jsonset('user:1', '$', $user1);\n        $r->jsonset('user:2', '$', $user2);\n        $r->jsonset('user:3', '$', $user3);","section_id":"add-the-data"},{"id":"query-the-data-ex0","language":"php","code":"$res = $r->ftSearch(\"idx:users\", \"Paul @age:[30 40]\");\n        echo json_encode($res), PHP_EOL;\n        // >>> [1,\"user:3\",[\"$\",\"{\\\"name\\\":\\\"Paul Zamir\\\",\\\"email\\\":\\\"paul.zamir@example.com\\\",\\\"age\\\":35,\\\"city\\\":\\\"Tel Aviv\\\"}\"]]","section_id":"query-the-data"},{"id":"query-the-data-ex1","language":"php","code":"$arguments = new SearchArguments();\n        $arguments->addReturn(3, '$.city', true, 'thecity');\n        $arguments->dialect(2);\n        $arguments->limit(0, 5);\n\n        $res = $r->ftSearch(\"idx:users\", \"Paul\", $arguments);\n\n        echo json_encode($res), PHP_EOL;\n        // >>> [2,\"user:1\",[\"thecity\",\"London\"],\"user:3\",[\"thecity\",\"Tel Aviv\"]]","section_id":"query-the-data"},{"id":"query-the-data-ex2","language":"php","code":"$ftAggregateArguments = (new AggregateArguments())\n            ->groupBy('@city')\n            ->reduce('COUNT', true, 'count');\n\n        $res = $r->ftAggregate('idx:users', '*', $ftAggregateArguments);\n        echo json_encode($res), PHP_EOL;\n        // >>> [2,[\"city\",\"London\",\"count\",\"1\"],[\"city\",\"Tel Aviv\",\"count\",\"2\"]]","section_id":"query-the-data"},{"id":"differences-with-hash-documents-ex0","language":"php","code":"$hashSchema = [\n            new TextField('name'),\n            new TagField('city'),\n            new NumericField('age'),\n        ];\n        \n        try {\n        $r->ftCreate(\"hash-idx:users\", $hashSchema,\n            (new CreateArguments())\n                ->on('HASH')\n                ->prefix([\"huser:\"]));\n        }\n        catch (Exception $e) {\n            echo $e->getMessage(), PHP_EOL;\n        }","section_id":"differences-with-hash-documents"},{"id":"differences-with-hash-documents-ex1","language":"php","code":"$r->hmset('huser:1', [\n            'name' => 'Paul John',\n            'email' => 'paul.john@example.com',\n            'age' => 42,\n            'city' => 'London',\n        ]);\n        \n        $r->hmset('huser:2', [\n            'name' => 'Eden Zamir',\n            'email' => 'eden.zamir@example.com',\n            'age' => 29,\n            'city' => 'Tel Aviv',\n        ]);\n        \n        $r->hmset('huser:3', [\n            'name' => 'Paul Zamir',\n            'email' => 'paul.zamir@example.com',\n            'age' => 35,\n            'city' => 'Tel Aviv',\n        ]);","section_id":"differences-with-hash-documents"},{"id":"differences-with-hash-documents-ex2","language":"php","code":"$res = $r->ftSearch(\"hash-idx:users\", \"Paul @age:[30 40]\");\n        echo json_encode($res), PHP_EOL;\n        // >>> [1,\"huser:3\",[\"name\",\"Paul Zamir\",\"email\",\"paul.zamir@example.com\",\"age\",\"35\",\"city\",\"Tel Aviv\"]]","section_id":"differences-with-hash-documents"},{"id":"bulk-import-with-himport-ex0","language":"php","code":"$himportRes = $r->pipeline(function ($pipe) {\n            $pipe->himport('PREPARE', 'huser', ['name', 'email', 'age', 'city']);\n\n            $pipe->himport('SET', 'huser:1', 'huser', ['Paul John', 'paul.john@example.com', 42, 'London']);\n            $pipe->himport('SET', 'huser:2', 'huser', ['Eden Zamir', 'eden.zamir@example.com', 29, 'Tel Aviv']);\n            $pipe->himport('SET', 'huser:3', 'huser', ['Paul Zamir', 'paul.zamir@example.com', 35, 'Tel Aviv']);\n        });","section_id":"bulk-import-with-himport"}]}
{"schema_version":2,"id":"develop/clients/php/transpipe","title":"Pipelines and transactions","url":"https://redis.io/docs/latest/develop/clients/php/transpipe/","summary":"Learn how to use Redis pipelines and transactions","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-09T09:49:12+01:00","page_type":"content","content_hash":"a285a7f817fc05453bdf366ffd2715191f216eb5d352ca43e8c1aeb1742fe58b","sections":[{"id":"execute-a-pipeline","title":"Execute a pipeline","role":"content","text":"To execute commands in a pipeline, create a pipeline object with\n[`pipeline()`](https://github.com/predis/predis) and then add commands to it\nusing methods that resemble the standard command methods (for example,\n`set()` and `get()`). The commands are buffered in the pipeline and only\nexecute when you call the `execute()` method on the pipeline object. This\nmethod returns an array containing the results from all the commands in order.\n\nThe command methods for a pipeline always return the original pipeline object,\nso you can chain several commands together. You can also pass a callback to\n`pipeline()` and let Predis execute the batch automatically when the callback\nreturns, as the example below shows:\n\nFoundational: Use pipelines to batch multiple commands together and reduce network round trips\n\n**Difficulty:** Beginner\n\n**Available in:** PHP\n\n##### PHP\n\n[code example]"},{"id":"execute-a-transaction","title":"Execute a transaction","role":"content","text":"A transaction works in a similar way to a pipeline, but all the queued commands\nexecute atomically. With Predis, you can create a transaction by calling\n`transaction()` and adding commands in a callback. Predis wraps those commands\nwith `MULTI` and `EXEC` automatically:\n\nBasic transaction: Execute commands atomically using transaction() to group related writes\n\n**Difficulty:** Beginner\n\n**Available in:** PHP\n\n##### PHP\n\n[code example]"},{"id":"watch-keys-for-changes","title":"Watch keys for changes","role":"content","text":"Redis supports *optimistic locking* to avoid inconsistent updates to keys that\nseveral clients may modify at the same time. The basic idea is to watch for\nchanges to any keys that you use in a transaction while you are preparing the\nupdate. If the watched keys do change, you must restart the update using the\nlatest value from Redis. See\n[Transactions](https://redis.io/docs/latest/develop/using-commands/transactions)\nfor more information about optimistic locking.\n\nThe example below reads a string that represents a `PATH` variable for a\ncommand shell, appends a new command path, and then writes it back inside a\ntransaction. The `cas` option enables check-and-set behavior, `watch` tells\nPredis which key to monitor for changes, and `retry` lets Predis retry the\ntransaction automatically if another client changes the watched key before\n`EXEC` runs:\n\nOptimistic locking: Use WATCH with a CAS transaction to retry updates when another client modifies the key\n\n**Difficulty:** Intermediate\n\n**Available in:** PHP\n\n##### PHP\n\n[code example]"}],"examples":[{"id":"execute-a-pipeline-ex0","language":"php","code":"$pipe = $r->pipeline();\n$pipe->set('seat:0', '#0')\n    ->set('seat:1', '#1')\n    ->set('seat:2', '#2')\n    ->set('seat:3', '#3')\n    ->set('seat:4', '#4');\n$pipe->execute();\n\n$pipe = $r->pipeline();\n$pipe->get('seat:0')\n    ->get('seat:1')\n    ->get('seat:2')\n    ->get('seat:3')\n    ->get('seat:4');\n$seats = $pipe->execute();\n\necho implode(', ', $seats), PHP_EOL;\n// >>> #0, #1, #2, #3, #4\n\n$responses = $r->pipeline(function ($pipe) {\n    $pipe->set('seat:5', '#5');\n    $pipe->set('seat:6', '#6');\n    $pipe->set('seat:7', '#7');\n    $pipe->get('seat:5');\n    $pipe->get('seat:6');\n    $pipe->get('seat:7');\n});\n\necho implode(', ', array_slice($responses, 3)), PHP_EOL;\n// >>> #5, #6, #7","section_id":"execute-a-pipeline"},{"id":"execute-a-transaction-ex0","language":"php","code":"$r->transaction(function (MultiExec $tx) {\n    $tx->incr('counter:1');\n    $tx->incrby('counter:2', 2);\n    $tx->incrby('counter:3', 3);\n});\n\necho implode(', ', $r->mget('counter:1', 'counter:2', 'counter:3')), PHP_EOL;\n// >>> 1, 2, 3","section_id":"execute-a-transaction"},{"id":"watch-keys-for-changes-ex0","language":"php","code":"$r->set('shellpath', '/usr/syscmds/');\n\n$r->transaction(\n    ['cas' => true, 'watch' => 'shellpath', 'retry' => 3],\n    function (MultiExec $tx) {\n        $path = $tx->get('shellpath');\n        $tx->multi();\n        $tx->set('shellpath', $path . ':/usr/mycmds/');\n    }\n);\n\necho $r->get('shellpath'), PHP_EOL;\n// >>> /usr/syscmds/:/usr/mycmds/","section_id":"watch-keys-for-changes"}]}
{"schema_version":2,"id":"develop/clients/php/vecsearch","title":"Index and query vectors","url":"https://redis.io/docs/latest/develop/clients/php/vecsearch/","summary":"Learn how to index and query vector embeddings with Redis","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-16T16:44:37+01:00","page_type":"content","content_hash":"7051f6ee0e3b0aeb327fb8744a9cab66722e5205ea5ce84d8a27cf04e09e07d1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query)\nlets you index vector fields in [hash](https://redis.io/docs/latest/develop/data-types/hashes)\nor [JSON](https://redis.io/docs/latest/develop/data-types/json) objects (see the\n[Vectors](https://redis.io/docs/latest/develop/ai/search-and-query/vectors) \nreference page for more information).\nAmong other things, vector fields can store *text embeddings*, which are AI-generated vector\nrepresentations of the semantic information in pieces of text. The\n[vector distance](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#distance-metrics)\nbetween two embeddings indicates how similar they are semantically. By comparing the\nsimilarity of an embedding generated from some query text with embeddings stored in hash\nor JSON fields, Redis can retrieve documents that closely match the query in terms\nof their meaning.\n\nThe example below uses the [HuggingFace](https://huggingface.co/) model\n[`all-MiniLM-L6-v2`](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2)\nto generate the vector embeddings to store and index with Redis Search.\nThe code is first demonstrated for hash documents with a\nseparate section to explain the\n[differences with JSON documents](#differences-with-json-documents).\n\n> [!NOTE]\n> From [v3.0.0](https://github.com/predis/predis/releases/tag/v3.0.0) onwards,\n> `Predis` uses query dialect 2 by default.\n> Redis Search methods such as [`ftSearch()`](https://redis.io/docs/latest/commands/ft.search)\n> will explicitly request this dialect, overriding the default set for the server.\n> See\n> [Query dialects](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/dialects)\n> for more information."},{"id":"initialize","title":"Initialize","role":"content","text":"You can use the [TransformersPHP](https://transformers.codewithkyrian.com/)\nlibrary to create the vector embeddings. Install the library with the following\ncommand:\n\n[code example]"},{"id":"import-dependencies","title":"Import dependencies","role":"content","text":"Import the following classes and function in your source file:\n\n[code example]"},{"id":"create-a-tokenizer-instance","title":"Create a tokenizer instance","role":"content","text":"The code below shows how to use the\n[`all-MiniLM-L6-v2`](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2)\ntokenizer to generate the embeddings. The vectors that represent the\nembeddings have 384 dimensions, regardless of the length of the input\ntext. Here, the `pipeline()` call creates the `$extractor` function that\ngenerates embeddings from text.\n\nThe `all-MiniLM-L6-v2` model expects mean pooling followed by normalization\nto unit length (see the\n[`all-MiniLM-L6-v2`](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2)\npage for more information). In TransformersPHP v0.6.2, the `normalize: true`\noption has no effect. The `$embed()` function below calls `$extractor()` with\nmean pooling and then normalizes the vector itself:\n\n[code example]"},{"id":"create-the-index","title":"Create the index","role":"content","text":"Connect to Redis and delete any index previously created with the\nname `vector_idx`. (The\n[`ftdropindex()`](https://redis.io/docs/latest/commands/ft.dropindex)\ncall throws an exception if the index doesn't already exist, which is\nwhy you need the `try...catch` block.)\n\n[code example]\n\nNext, create the index.\nThe schema in the example below includes three fields: the text content to index, a\n[tag](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/tags)\nfield to represent the \"genre\" of the text, and the embedding vector generated from\nthe original text content. The `embedding` field specifies\n[HNSW](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#hnsw-index) \nindexing, the\n[L2](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#distance-metrics)\nvector distance metric, `Float32` values to represent the vector's components,\nand 384 dimensions, as required by the `all-MiniLM-L6-v2` embedding model.\n\nThe `CreateArguments` parameter to [`ftcreate()`](https://redis.io/docs/latest/commands/ft.create)\nspecifies hash objects for storage and a prefix `doc:` that identifies the hash objects\nto index.\n\n[code example]"},{"id":"add-data","title":"Add data","role":"content","text":"You can now supply the data objects, which will be indexed automatically\nwhen you add them with [`hmset()`](https://redis.io/docs/latest/commands/hset), as long as\nyou use the `doc:` prefix specified in the index definition.\n\nUse the `$embed()` function as shown below to create the embedding that\nrepresents the `content` field.\n\nTo add an embedding as a field of a hash object, you must encode the\nvector array as a binary string. The built-in\n[`pack()`](https://www.php.net/manual/en/function.pack.php) function is a convenient\nway to do this in PHP, using the `g*` format specifier to denote a packed\narray of `float` values. Note that if you are using\n[JSON](https://redis.io/docs/latest/develop/data-types/json)\nobjects to store your documents instead of hashes, then you should store\nthe `float` array directly without first converting it to a binary\nstring (see [Differences with JSON documents](#differences-with-json-documents)\nbelow).\n\n[code example]"},{"id":"bulk-import-with-himport","title":"Bulk import with HIMPORT","role":"content","text":"From Redis Open Source 8.10, you can also add the documents with the\n[`HIMPORT`](https://redis.io/docs/latest/commands/himport) command family. This creates the same\nhashes as `hmset()`, but lets Redis store them as\n[compact hashes](https://redis.io/docs/latest/develop/data-types/hashes#compact-hashes), which use\nless memory when many keys share the same field names. Redis Search indexes\nthese hashes in the same way.\n\nDeclare the field names once by calling `himport()` with\n[`PREPARE`](https://redis.io/docs/latest/commands/himport-prepare), then create each\ndocument by calling it with [`SET`](https://redis.io/docs/latest/commands/himport-set), passing only\nits values, in the same order as the field names. Use a\n[pipeline](https://redis.io/docs/latest/develop/clients/php/transpipe) to send all the\nimports in one round trip:\n\n[code example]\n\n`HIMPORT SET` replaces any existing key, so running this code after the\n`hmset()` calls leaves you with the same three documents.\n\n> [!NOTE]\n> `HIMPORT` requires `Predis` v3.6.0 or later, and `Predis` marks its\n> support as experimental, so the API might change. Redis Cloud and\n> Redis Software don't support `HIMPORT`."},{"id":"run-a-query","title":"Run a query","role":"content","text":"After you have created the index and added the data, you are ready to run a query.\nTo do this, you must create another embedding vector from your chosen query\ntext. Redis calculates the vector distance between the query vector and each\nembedding vector in the index as it runs the query. You can request the results to be\nsorted to rank them in order of ascending distance.\n\nThe code below creates the query embedding using the `$embed()` function, as with\nthe indexing, and passes it as a parameter when the query executes (see\n[Vector search](https://redis.io/docs/latest/develop/ai/search-and-query/query/vector-search)\nfor more information about using query parameters with embeddings).\nThe query is a\n[K nearest neighbors (KNN)](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#knn-vector-search)\nsearch that sorts the results in order of vector distance from the query vector.\n\nThe results are returned as an array with the number of results in the\nfirst element. The remaining elements are alternating pairs with the\nkey of the returned document (for example, `doc:0`) first, followed by an array containing\nthe fields you requested (again as alternating key-value pairs).\n\n[code example]\n\nAssuming you have added the code from the steps above to your source file,\nit is now ready to run, but note that it may take a while to complete when\nyou run it for the first time (which happens because the tokenizer must download the\n`all-MiniLM-L6-v2` model data before it can\ngenerate the embeddings). When you run the code, it outputs the following result text:\n\n[code example]\n\nThe exact distances may differ slightly on your system.\n\nNote that the results are ordered according to the value of the `vector_distance`\nfield, with the lowest distance indicating the greatest similarity to the query.\nAs you would expect, the text *\"That is a very happy person\"* (from the `doc:0`\ndocument)\nis the result judged to be most similar in meaning to the query text\n*\"That is a happy person\"*."},{"id":"differences-with-json-documents","title":"Differences with JSON documents","role":"content","text":"Indexing JSON documents is similar to hash indexing, but there are some\nimportant differences. JSON allows much richer data modeling with nested fields, so\nyou must supply a [path](https://redis.io/docs/latest/develop/data-types/json/path) in the schema\nto identify each field you want to index. However, you can declare a short alias for each\nof these paths to avoid typing it in full for\nevery query. Also, you must specify `JSON` with the `on()` option when you create the index.\n\nThe code below shows these differences, but the index is otherwise very similar to\nthe one created previously for hashes:\n\n[code example]\n\nUse [`jsonset()`](https://redis.io/docs/latest/commands/json.set) to add the data\ninstead of [`hmset()`](https://redis.io/docs/latest/commands/hset). The arrays\nthat specify the fields have roughly the same structure as the ones used for\n`hmset()` but you should use the standard library function\n[`json_encode()`](https://www.php.net/manual/en/function.json-encode.php)\nto generate a JSON string representation of the array.\n\nAn important difference with JSON indexing is that the vectors are\nspecified using arrays instead of binary strings. Add the\nembedding as an array field without using the `pack()` function as you\nwould with a hash.\n\n[code example]\n\nThe query is almost identical to the one for the hash documents. This\ndemonstrates how the right choice of aliases for the JSON paths can\nsave you having to write complex queries. An important thing to notice\nis that the vector parameter for the query is still specified as a\nbinary string (using the `pack()` function), even though the data for\nthe `embedding` field of the JSON was specified as an array.\n\n[code example]\n\nApart from the `jdoc:` prefixes for the keys, the result from the JSON\nquery is the same as for hash:\n\n[code example]"},{"id":"learn-more","title":"Learn more","role":"related","text":"See\n[Vector search](https://redis.io/docs/latest/develop/ai/search-and-query/query/vector-search)\nfor more information about the indexing options, distance metrics, and query format\nfor vectors."}],"examples":[{"id":"initialize-ex0","language":"bash","code":"composer require codewithkyrian/transformers","section_id":"initialize"},{"id":"import-dependencies-ex0","language":"php","code":"<?php\n\nrequire 'vendor/autoload.php';\n\n// TransformersPHP\nuse function Codewithkyrian\\Transformers\\Pipelines\\pipeline;\n\n// Redis client and Redis Search classes.\nuse Predis\\Client;\nuse Predis\\Command\\Argument\\Search\\CreateArguments;\nuse Predis\\Command\\Argument\\Search\\SearchArguments;\nuse Predis\\Command\\Argument\\Search\\SchemaFields\\TextField;\nuse Predis\\Command\\Argument\\Search\\SchemaFields\\TagField;\nuse Predis\\Command\\Argument\\Search\\SchemaFields\\VectorField;","section_id":"import-dependencies"},{"id":"create-a-tokenizer-instance-ex0","language":"php","code":"$extractor = pipeline('embeddings', 'Xenova/all-MiniLM-L6-v2');\n\n$embed = function (string $text) use ($extractor): array {\n    $emb = $extractor($text, pooling: 'mean')[0];\n    $norm = sqrt(array_sum(array_map(fn($x) => $x * $x, $emb)));\n\n    return array_map(fn($x) => $x / $norm, $emb);\n};","section_id":"create-a-tokenizer-instance"},{"id":"create-the-index-ex0","language":"php","code":"$client = new Predis\\Client([\n    'host' => 'localhost',\n    'port' => 6379,\n]);\n\ntry {\n    $client->ftdropindex(\"vector_idx\");\n} catch (Exception $e){}","section_id":"create-the-index"},{"id":"create-the-index-ex1","language":"php","code":"$schema = [\n    new TextField(\"content\"),\n    new TagField(\"genre\"),\n    new VectorField(\n        \"embedding\",\n        \"HNSW\",\n        [\n            \"TYPE\", \"FLOAT32\",\n            \"DIM\", 384,\n            \"DISTANCE_METRIC\", \"L2\"\n        ]\n    )   \n];\n\n$client->ftcreate(\"vector_idx\", $schema,\n    (new CreateArguments())\n        ->on('HASH')\n        ->prefix([\"doc:\"])\n);","section_id":"create-the-index"},{"id":"add-data-ex0","language":"php","code":"$content = \"That is a very happy person\";\n$emb = $embed($content);\n\n$client->hmset(\"doc:0\",[\n    \"content\" => $content,\n    \"genre\" => \"persons\",\n    \"embedding\" => pack('g*', ...$emb)\n]);\n\n$content = \"That is a happy dog\";\n$emb = $embed($content);\n\n$client->hmset(\"doc:1\",[\n    \"content\" => $content,\n    \"genre\" => \"pets\",\n    \"embedding\" => pack('g*', ...$emb)\n]);\n\n$content = \"Today is a sunny day\";\n$emb = $embed($content);\n\n$client->hmset(\"doc:2\",[\n    \"content\" => $content,\n    \"genre\" => \"weather\",\n    \"embedding\" => pack('g*', ...$emb)\n]);","section_id":"add-data"},{"id":"bulk-import-with-himport-ex0","language":"php","code":"$docs = [\n    [\"doc:0\", \"That is a very happy person\", \"persons\"],\n    [\"doc:1\", \"That is a happy dog\", \"pets\"],\n    [\"doc:2\", \"Today is a sunny day\", \"weather\"],\n];\n\n$client->pipeline(function ($pipe) use ($docs, $embed) {\n    $pipe->himport('PREPARE', \"doc\", [\"content\", \"genre\", \"embedding\"]);\n\n    foreach ($docs as [$key, $content, $genre]) {\n        $emb = $embed($content);\n\n        $pipe->himport('SET', $key, \"doc\", [\n            $content,\n            $genre,\n            pack('g*', ...$emb)\n        ]);\n    }\n});","section_id":"bulk-import-with-himport"},{"id":"run-a-query-ex0","language":"php","code":"$queryText = \"That is a happy person\";\n$queryEmb = $embed($queryText);\n\n$result = $client->ftsearch(\n    \"vector_idx\",\n    '*=>[KNN 3 @embedding $vec AS vector_distance]',\n    (new SearchArguments())\n        ->addReturn(1, \"vector_distance\")\n        ->dialect(\"2\")\n        ->params([\n            \"vec\", pack('g*', ...$queryEmb)\n        ])\n        ->sortBy(\"vector_distance\")\n);\n\n$numResults = $result[0];\necho \"Number of results: $numResults\" . PHP_EOL;\n// >>> Number of results: 3\n\nfor ($i = 1; $i < ($numResults * 2 + 1); $i += 2) {\n    $key = $result[$i];\n    echo \"Key: $key\" . PHP_EOL;\n    $fields = $result[$i + 1];\n    echo \"Field: {$fields[0]}, Value: {$fields[1]}\" . PHP_EOL; \n}        \n// >>> Key: doc:0\n// >>> Field: vector_distance, Value: 0.133717402816\n// >>> Key: doc:1\n// >>> Field: vector_distance, Value: 0.621603608131\n// >>> Key: doc:2\n// >>> Field: vector_distance, Value: 1.46157670021","section_id":"run-a-query"},{"id":"run-a-query-ex1","language":"plaintext","code":"Number of results: 3\nKey: doc:0\nField: vector_distance, Value: 0.133717402816\nKey: doc:1\nField: vector_distance, Value: 0.621603608131\nKey: doc:2\nField: vector_distance, Value: 1.46157670021","section_id":"run-a-query"},{"id":"differences-with-json-documents-ex0","language":"php","code":"$jsonSchema = [\n    new TextField(\"$.content\", \"content\"),\n    new TagField(\"$.genre\", \"genre\"),\n    new VectorField(\n        \"$.embedding\",\n        \"HNSW\",\n        [\n            \"TYPE\", \"FLOAT32\",\n            \"DIM\", 384,\n            \"DISTANCE_METRIC\", \"L2\"\n        ],\n        \"embedding\",\n    )   \n];\n\n$client->ftcreate(\"vector_json_idx\", $jsonSchema,\n    (new CreateArguments())\n        ->on('JSON')\n        ->prefix([\"jdoc:\"])\n);","section_id":"differences-with-json-documents"},{"id":"differences-with-json-documents-ex1","language":"php","code":"$content = \"That is a very happy person\";\n$emb = $embed($content);\n\n$client->jsonset(\"jdoc:0\", \"$\",\n    json_encode(\n        [\n            \"content\" => $content,\n            \"genre\" => \"persons\",\n            \"embedding\" => $emb\n        ],\n        JSON_THROW_ON_ERROR\n    )\n);\n\n$content = \"That is a happy dog\";\n$emb = $embed($content);\n\n$client->jsonset(\"jdoc:1\",\"$\", \n    json_encode(\n        [\n            \"content\" => $content,\n            \"genre\" => \"pets\",\n            \"embedding\" => $emb\n        ],\n        JSON_THROW_ON_ERROR\n    )\n);\n\n$content = \"Today is a sunny day\";\n$emb = $embed($content);\n\n$client->jsonset(\"jdoc:2\", \"$\",\n    json_encode(\n        [\n            \"content\" => $content,\n            \"genre\" => \"weather\",\n            \"embedding\" => $emb\n        ],\n        JSON_THROW_ON_ERROR\n    )\n);","section_id":"differences-with-json-documents"},{"id":"differences-with-json-documents-ex2","language":"php","code":"$queryText = \"That is a happy person\";\n$queryEmb = $embed($queryText);\n\n$result = $client->ftsearch(\n    \"vector_json_idx\",\n    '*=>[KNN 3 @embedding $vec AS vector_distance]',\n    (new SearchArguments())\n        ->addReturn(1, \"vector_distance\")\n        ->dialect(\"2\")\n        ->params([\n            \"vec\", pack('g*', ...$queryEmb)\n        ])\n        ->sortBy(\"vector_distance\")\n);","section_id":"differences-with-json-documents"},{"id":"differences-with-json-documents-ex3","language":"plaintext","code":"Number of results: 3\nKey: jdoc:0\nField: vector_distance, Value: 0.133717402816\nKey: jdoc:1\nField: vector_distance, Value: 0.621603608131\nKey: jdoc:2\nField: vector_distance, Value: 1.46157670021","section_id":"differences-with-json-documents"}]}
{"schema_version":2,"id":"develop/clients/php/vecsets","title":"Vector set embeddings","url":"https://redis.io/docs/latest/develop/clients/php/vecsets/","summary":"Index and query embeddings with Redis vector sets","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-09T09:49:12+01:00","page_type":"content","content_hash":"159d893bd07b661f434a33d4ce6b49c69275384723cf8184812b33950b09a74e","sections":[{"id":"initialize","title":"Initialize","role":"content","text":"Install `Predis` and `TransformersPHP` with Composer:\n\n[code example]\n\nIn a new PHP file, import the required classes and function:\n\nFoundational: Import required libraries for vector sets, embeddings, and Redis operations\n\n**Difficulty:** Beginner\n\n**Available in:** PHP\n\n##### PHP\n\n[code example]\n\n\n\nThe `pipeline()` function below creates an embedding generator for the\n[`all-MiniLM-L6-v2`](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2)\nmodel. This model generates vectors with 384 dimensions, regardless of the\nlength of the input text:\n\nFoundational: Initialize a TransformersPHP embedding pipeline to generate vectors from text\n\n**Difficulty:** Beginner\n\n**Available in:** PHP\n\n##### PHP\n\n[code example]"},{"id":"create-the-data","title":"Create the data","role":"content","text":"The example data is an array containing brief descriptions of famous people:\n\nFoundational: Define sample data with text descriptions and metadata for vector embedding and storage\n\n**Difficulty:** Beginner\n\n**Available in:** PHP\n\n##### PHP\n\n[code example]"},{"id":"add-the-data-to-a-vector-set","title":"Add the data to a vector set","role":"content","text":"The next step is to connect to Redis and add the data to a new vector set.\n\nThe code below iterates through the array, uses the embedding pipeline to\ngenerate a `float` vector from each description, and then adds the result to a\nvector set called `famousPeople` with [`vadd()`](https://redis.io/docs/latest/commands/vadd).\nIt then stores the `born` and `died` values as element attributes using\n[`vsetattr()`](https://redis.io/docs/latest/commands/vsetattr), so you can use the metadata\nlater during queries.\n\nFoundational: Add embedding vectors to a vector set and attach metadata attributes for later filtering\n\n**Difficulty:** Beginner\n\n**Available in:** PHP\n\n##### PHP\n\n[code example]"},{"id":"query-the-vector-set","title":"Query the vector set","role":"content","text":"You can now query the data in the set. The basic approach is to generate\nanother embedding vector from the query text and pass it to\n[`vsim()`](https://redis.io/docs/latest/commands/vsim), which returns elements ranked in\norder of similarity to that query vector.\n\nStart with a simple query for \"actors\":\n\nVector similarity search: Find semantically similar items in a vector set using VSIM\n\n**Difficulty:** Intermediate\n\n**Available in:** PHP\n\n##### PHP\n\n[code example]\n\n\n\nThis returns the following list of elements:\n\n[code example]\n\nThe first two people in the list are the two actors, as expected, but the\nremaining results are less directly related. By default, the search attempts\nto rank all the elements in the set. You can use the `count` parameter of\n`vsim()` to limit the list to the most relevant few results:\n\nVector similarity search with limits: Restrict results to the top K most similar items using the count parameter\n\n**Difficulty:** Intermediate\n\n**Available in:** PHP\n\n##### PHP\n\n[code example]\n\n\n\nThe reason for using text embeddings rather than simple text search is that\nthe embeddings capture semantic information. This allows a query to find\nelements with a similar meaning even if the text is different. For example, the\nword \"entertainer\" doesn't appear in any of the descriptions but if you use it\nas a query, the actors and musicians rank highly in the results:\n\nSemantic search: Use text embeddings to find related concepts even when exact keywords do not appear in the source text\n\n**Difficulty:** Intermediate\n\n**Available in:** PHP\n\n##### PHP\n\n[code example]\n\n\n\nSimilarly, if you use \"science\" as a query, you get the scientists first,\nfollowed by the mathematicians:\n\n[code example]\n\nYou can also use\n[filter expressions](https://redis.io/docs/latest/develop/data-types/vector-sets/filtered-search)\nwith `vsim()` to restrict the search further. For example, repeat the\n\"science\" query, but this time limit the results to people who died before the\nyear 2000:\n\nFiltered vector search: Combine vector similarity with attribute filters to narrow results based on metadata conditions\n\n**Difficulty:** Advanced\n\n**Available in:** PHP\n\n##### PHP\n\n[code example]"},{"id":"more-information","title":"More information","role":"content","text":"See the [vector sets](https://redis.io/docs/latest/develop/data-types/vector-sets)\ndocs for more information and code examples. See the\n[Redis for AI](https://redis.io/docs/latest/develop/ai) section for more details\nabout text embeddings and other AI techniques you can use with Redis.\n\nYou may also be interested in\n[vector search](https://redis.io/docs/latest/develop/clients/php/vecsearch).\nThis is a feature of\n[Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query)\nthat lets you retrieve\n[JSON](https://redis.io/docs/latest/develop/data-types/json) and\n[hash](https://redis.io/docs/latest/develop/data-types/hashes) documents based on\nvector data stored in their fields."}],"examples":[{"id":"initialize-ex0","language":"bash","code":"composer require predis/predis codewithkyrian/transformers","section_id":"initialize"},{"id":"initialize-ex1","language":"php","code":"<?php\n\nrequire 'vendor/autoload.php';\n\nuse function Codewithkyrian\\Transformers\\Pipelines\\pipeline;\n\nuse Predis\\Client as PredisClient;","section_id":"initialize"},{"id":"initialize-ex2","language":"php","code":"$extractor = pipeline('embeddings', 'Xenova/all-MiniLM-L6-v2');","section_id":"initialize"},{"id":"create-the-data-ex0","language":"php","code":"$peopleData = [\n    'Marie Curie' => [\n        'born' => 1867,\n        'died' => 1934,\n        'description' => 'Polish-French chemist and physicist. The only person ever to win two Nobel prizes for two different sciences.',\n    ],\n    'Linus Pauling' => [\n        'born' => 1901,\n        'died' => 1994,\n        'description' => 'American chemist and peace activist. One of only two people to win two Nobel prizes in different fields (chemistry and peace).',\n    ],\n    'Freddie Mercury' => [\n        'born' => 1946,\n        'died' => 1991,\n        'description' => 'British musician, best known as the lead singer of the rock band Queen.',\n    ],\n    'Marie Fredriksson' => [\n        'born' => 1958,\n        'died' => 2019,\n        'description' => 'Swedish multi-instrumentalist, mainly known as the lead singer and keyboardist of the band Roxette.',\n    ],\n    'Paul Erdos' => [\n        'born' => 1913,\n        'died' => 1996,\n        'description' => 'Hungarian mathematician, known for his eccentric personality almost as much as his contributions to many different fields of mathematics.',\n    ],\n    'Maryam Mirzakhani' => [\n        'born' => 1977,\n        'died' => 2017,\n        'description' => 'Iranian mathematician. The first woman ever to win the Fields medal for her contributions to mathematics.',\n    ],\n    'Masako Natsume' => [\n        'born' => 1957,\n        'died' => 1985,\n        'description' => 'Japanese actress. She was very famous in Japan but was primarily known elsewhere in the world for her portrayal of Tripitaka in the TV series Monkey.',\n    ],\n    'Chaim Topol' => [\n        'born' => 1935,\n        'died' => 2023,\n        'description' => \"Israeli actor and singer, usually credited simply as 'Topol'. He was best known for his many appearances as Tevye in the musical Fiddler on the Roof.\",\n    ],\n];","section_id":"create-the-data"},{"id":"add-the-data-to-a-vector-set-ex0","language":"php","code":"$r->del('famousPeople');\n\nforeach ($peopleData as $name => $details) {\n    $embedding = $extractor($details['description'], normalize: true, pooling: 'mean');\n\n    $r->vadd('famousPeople', $embedding[0], $name);\n    $r->vsetattr('famousPeople', $name, [\n        'born' => $details['born'],\n        'died' => $details['died'],\n    ]);\n}","section_id":"add-the-data-to-a-vector-set"},{"id":"query-the-vector-set-ex0","language":"php","code":"$actorsEmbedding = $extractor('actors', normalize: true, pooling: 'mean');\n$actorsResults = $r->vsim('famousPeople', $actorsEmbedding[0]);\n\necho \"'actors': \" . json_encode($actorsResults), PHP_EOL;\n// >>> 'actors': [\"Masako Natsume\",\"Chaim Topol\",\"Linus Pauling\",\"Marie Fredriksson\",\"Maryam Mirzakhani\",\"Freddie Mercury\",\"Marie Curie\",\"Paul Erdos\"]","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex1","language":"plaintext","code":"'actors': [\"Masako Natsume\",\"Chaim Topol\",\"Linus Pauling\",\n\"Marie Fredriksson\",\"Maryam Mirzakhani\",\"Freddie Mercury\",\n\"Marie Curie\",\"Paul Erdos\"]","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex2","language":"php","code":"$twoActorsResults = $r->vsim('famousPeople', $actorsEmbedding[0], false, false, 2);\n\necho \"'actors (2)': \" . json_encode($twoActorsResults), PHP_EOL;\n// >>> 'actors (2)': [\"Masako Natsume\",\"Chaim Topol\"]","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex3","language":"php","code":"$entertainerEmbedding = $extractor('entertainer', normalize: true, pooling: 'mean');\n$entertainerResults = $r->vsim('famousPeople', $entertainerEmbedding[0]);\n\necho \"'entertainer': \" . json_encode($entertainerResults), PHP_EOL;\n// >>> 'entertainer': [\"Chaim Topol\",\"Freddie Mercury\",\"Linus Pauling\",\"Marie Fredriksson\",\"Masako Natsume\",\"Paul Erdos\",\"Maryam Mirzakhani\",\"Marie Curie\"]","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex4","language":"text","code":"'science': [\"Linus Pauling\",\"Marie Curie\",\"Maryam Mirzakhani\",\n\"Paul Erdos\",\"Marie Fredriksson\",\"Masako Natsume\",\n\"Freddie Mercury\",\"Chaim Topol\"]","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex5","language":"php","code":"$science2000Results = $r->vsim('famousPeople', $scienceEmbedding[0], false, false, null, null, null, '.died < 2000');\n\necho \"'science2000': \" . json_encode($science2000Results), PHP_EOL;\n// >>> 'science2000': [\"Linus Pauling\",\"Marie Curie\",\"Paul Erdos\",\"Masako Natsume\",\"Freddie Mercury\"]","section_id":"query-the-vector-set"}]}
{"schema_version":2,"id":"develop/clients/pools-and-muxing","title":"Connection pools and multiplexing","url":"https://redis.io/docs/latest/develop/clients/pools-and-muxing/","summary":"Manage Redis connections efficiently","aliases":["/develop/connect/clients/pools-and-muxing"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-29T16:29:37+01:00","page_type":"content","content_hash":"b8d652fbc706e8c6fa591737b23a1a7000b959a85ac67936b4599c4b95297f57","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis example code generally opens a connection, demonstrates\na command or feature, and then closes. Real-world code typically\nhas short bursts of communication with the server and periods of\ninactivity in between. Opening and closing connections\ninvolves some overhead and leads to inefficiency if you do\nit frequently. This means that you can improve the performance of production\ncode by making as few separate connections as possible.\n\nManaging connections in your own code can be tricky, so the Redis\nclient libraries give you some help. The two basic approaches to\nconnection management are called *connection pooling* and *multiplexing*.\nThe [`redis-py`](https://redis.io/docs/latest/develop/clients/redis-py),\n[`jedis`](https://redis.io/docs/latest/develop/clients/jedis), and\n[`go-redis`](https://redis.io/docs/latest/develop/clients/go) clients support\nconnection pooling, while\n[`StackExchange.Redis`](https://redis.io/docs/latest/develop/clients/dotnet)\nsupports multiplexing.\n[`Lettuce`](https://redis.io/docs/latest/develop/clients/lettuce)\nsupports both approaches."},{"id":"connection-pooling","title":"Connection pooling","role":"content","text":"When you initialize a connection pool, the client opens a small number\nof connections and adds them to the pool.\n\n![](https://redis.io/docs/latest/images/dev/connect/pool-and-mux/ConnPoolInit.drawio.svg)\n\nEach time you \"open\" a connection\nfrom the pool, the client returns one of these existing\nconnections and notes the fact that it is in use.\n\n![](https://redis.io/docs/latest/images/dev/connect/pool-and-mux/ConnPoolInUse.drawio.svg)\n\nWhen you later \"close\"\nthe connection, the client puts it back into the pool of available\nconnections without actually closing it.\n\n![](https://redis.io/docs/latest/images/dev/connect/pool-and-mux/ConnPoolDiscon.drawio.svg)\n\nIf all connections in the pool are in use but the app needs more, then\nthe client can simply open new connections as necessary. In this way, the client\neventually finds the right number of connections to satisfy your\napp's demands."},{"id":"multiplexing","title":"Multiplexing","role":"content","text":"Instead of pooling several connections, a multiplexer keeps a\nsingle connection open and uses it for all traffic between the\nclient and the server. The \"connections\" returned to your code are\nused to identify where to send the response data from your commands.\n\n![](https://redis.io/docs/latest/images/dev/connect/pool-and-mux/ConnMux.drawio.svg)\n\nNote that it is not a problem if the multiplexer receives several commands close\ntogether in time. When this happens, the multiplexer can often combine the commands into a\n[pipeline](https://redis.io/docs/latest/develop/using-commands/pipelining), which\nimproves efficiency.\n\nMultiplexing offers high efficiency but works transparently without requiring\nany special code to enable it in your app. The main disadvantage of multiplexing compared to\nconnection pooling is that it can't support the blocking \"pop\" commands (such as\n[`BLPOP`](https://redis.io/docs/latest/commands/blpop)) since these would stall the\nconnection for all callers."}],"examples":[]}
{"schema_version":2,"id":"develop/clients/redis-py/amr","title":"Connect to Azure Managed Redis","url":"https://redis.io/docs/latest/develop/clients/redis-py/amr/","summary":"Learn how to authenticate to an Azure Managed Redis (AMR) database","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-08-27T15:55:18+01:00","page_type":"content","content_hash":"59e12b2f5b0241c2d6cd81a10505e378bfbede42cac1c5f3d16c2c56df9f1fad","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The [`redis-entra-id`](https://github.com/redis/redis-py-entraid) package\nlets you authenticate your app to\n[Azure Managed Redis (AMR)](https://azure.microsoft.com/en-us/products/managed-redis)\nusing [Microsoft Entra ID](https://learn.microsoft.com/en-us/entra/identity/).\nYou can authenticate using a system-assigned or user-assigned\n[managed identity](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview)\nor a [service principal](https://learn.microsoft.com/en-us/entra/identity-platform/app-objects-and-service-principals),\nletting `redis-entra-id` fetch and renew the authentication tokens for you automatically."},{"id":"install","title":"Install","role":"setup","text":"Install [`redis-py`](https://redis.io/docs/latest/develop/clients/redis-py#install) first,\nif you have not already done so. Then, install `redis-entra-id` with the\nfollowing command:\n\n[code example]"},{"id":"create-a-credentialprovider-instance","title":"Create a `CredentialProvider` instance","role":"content","text":"A `CredentialProvider` object obtains the authentication credentials you\nneed when you connect to Redis. See the sections below to learn how\nto create the `CredentialProvider` instances for AMR\nusing the factory functions that `redis-entra-id` provides."},{"id":"credentialprovider-for-a-service-principal","title":"`CredentialProvider` for a service principal","role":"content","text":"Use the `create_from_service_principal()` factory function to create a\n`CredentialProvider` that authenticates to AMR using a\nservice principal (see the\n[Microsoft documentation](https://learn.microsoft.com/en-us/entra/identity-platform/app-objects-and-service-principals) to learn more about service principals).\n\nYou will need the following details of your service principal to make the connection:\n\n- Client ID\n- Client secret\n- Tenant ID\n\nThe example below shows how to import the required modules and call\n`create_from_service_principal()`:\n\n[code example]\n\nThis uses a default configuration but you can also provide a custom\nconfiguration using the `token_manager_config` parameter:\n\n[code example]"},{"id":"credentialprovider-for-a-managed-identity","title":"`CredentialProvider` for a managed identity","role":"content","text":"Use the `create_from_managed_identity()` factory function to create a\n`CredentialProvider` that authenticates to AMR using a\nmanaged identity (see the\n[Microsoft documentation](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview) to learn more about managed identities).\n\nThe example below shows how to import the required modules and call\n`create_from_managed_identity()`.\nPass `ManagedIdentityType.USER_ASSIGNED` or `ManagedIdentityType.SYSTEM_ASSIGNED`\nas the `identity_type` parameter.\n\n[code example]\n\nThis uses a default configuration but you can also provide a custom\nconfiguration using the `token_manager_config` parameter:\n\n[code example]"},{"id":"connect","title":"Connect","role":"content","text":"When you have created your `CredentialProvider` instance, you are ready to\nconnect to AMR.\nThe example below shows how to pass the instance as a parameter to the standard\n`RedisCluster()` connection method.\n> [!NOTE]\n> Azure requires you to use\n> [Transport Layer Security (TLS)](https://en.wikipedia.org/wiki/Transport_Layer_Security)\n> when you connect (see\n> [Connect with TLS](https://redis.io/docs/latest/develop/clients/redis-py/connect#connect-to-your-production-redis-with-tls) for more information).\n\n[code example]"}],"examples":[{"id":"install-ex0","language":"bash","code":"pip install redis-entraid","section_id":"install"},{"id":"credentialprovider-for-a-service-principal-ex0","language":"python","code":"from redis_entraid.cred_provider import *\n\ncredential_provider = create_from_service_principal(\n    <CLIENT_ID>,\n    <CLIENT_SECRET>,\n    <TENANT_ID>\n)","section_id":"credentialprovider-for-a-service-principal"},{"id":"credentialprovider-for-a-service-principal-ex1","language":"python","code":"credential_provider = create_from_service_principal(\n    <CLIENT_ID>, \n    <CLIENT_SECRET>, \n    <TENANT_ID>,\n    token_manager_config=TokenManagerConfig(\n        expiration_refresh_ratio=0.9,\n        lower_refresh_bound_millis=DEFAULT_LOWER_REFRESH_BOUND_MILLIS,\n        token_request_execution_timeout_in_ms=DEFAULT_TOKEN_REQUEST_EXECUTION_TIMEOUT_IN_MS,\n        retry_policy=RetryPolicy(\n            max_attempts=5,\n            delay_in_ms=50\n        )\n    )\n)","section_id":"credentialprovider-for-a-service-principal"},{"id":"credentialprovider-for-a-managed-identity-ex0","language":"python","code":"from redis_entraid.cred_provider import *\n\ncredential_provider = create_from_managed_identity(\n    identity_type=ManagedIdentityType.SYSTEM_ASSIGNED,\n)","section_id":"credentialprovider-for-a-managed-identity"},{"id":"credentialprovider-for-a-managed-identity-ex1","language":"python","code":"credential_provider = create_from_managed_identity(\n    identity_type=ManagedIdentityType.SYSTEM_ASSIGNED, \n    ...\n\n    token_manager_config=TokenManagerConfig(\n        expiration_refresh_ratio=0.9,\n        lower_refresh_bound_millis=DEFAULT_LOWER_REFRESH_BOUND_MILLIS,\n        token_request_execution_timeout_in_ms=DEFAULT_TOKEN_REQUEST_EXECUTION_TIMEOUT_IN_MS,\n        retry_policy=RetryPolicy(\n            max_attempts=5,\n            delay_in_ms=50\n        )\n    )\n)","section_id":"credentialprovider-for-a-managed-identity"},{"id":"connect-ex0","language":"python","code":"from redis import RedisCluster\n# ...\n\n# AMR databases have clustering enabled by default.\nr = RedisCluster(\n    host=<HOST>, port=<PORT>,\n    credential_provider=credential_provider,\n    ssl=True,\n    ssl_certfile=\"./redis_user.crt\",\n    ssl_keyfile=\"./redis_user_private.key\",\n    ssl_ca_certs=\"./redis_ca.pem\"\n)\n\n// Test the connection.\nprint(\"The database size is: {}\".format(client.dbsize()))","section_id":"connect"}]}
{"schema_version":2,"id":"develop/clients/redis-py/async","title":"Asynchronous operations with redis-py","url":"https://redis.io/docs/latest/develop/clients/redis-py/async/","summary":"Use redis-py with asyncio for non-blocking Redis access","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-05T16:11:44+01:00","page_type":"content","content_hash":"8e9d37aabf70210d413ac8a0949af73f89073d28ba6a4a0fc7bc83ce622bb014","sections":[{"id":"basic-connection","title":"Basic connection","role":"content","text":"Import the async client from `redis.asyncio` and `await` each command.\nConstruction is synchronous and doesn't open a connection — the pool\nestablishes one lazily the first time you issue a command. Call `aclose()`\nwhen you're done to release the underlying socket.\n\nFoundational: Connect to Redis with the async client and run a basic SET/GET\n\n**Difficulty:** Beginner\n\n**Available in:** Python\n\n##### Python\n\n[code example]\n\n\n\nThe recommended pattern is to use the client as an async context manager,\nwhich ensures `aclose()` runs even if an exception is raised:\n\nFoundational: Use the async client as a context manager for automatic cleanup\n\n**Difficulty:** Beginner\n\n**Available in:** Python\n\n##### Python\n\n[code example]"},{"id":"connection-pools","title":"Connection pools","role":"content","text":"For production usage, you should manage connections with a connection pool\nrather than opening and closing them individually.\nSee [Connection pools and multiplexing](https://redis.io/docs/latest/develop/clients/pools-and-muxing)\nfor more information about how this works.\n\nA `Redis` client instance already creates and manages its own connection\npool internally, so in a long-running async application the recommended\npattern is to create a single client at startup, share it across requests\nand tasks, and close it at shutdown. Avoid creating a new `Redis()` per\nrequest — it defeats pooling and pays the connection cost on every call.\n\nFoundational: Create and share a single async client across the app\n\n**Difficulty:** Beginner\n\n**Available in:** Python\n\n##### Python\n\n[code example]\n\n\n\nTune `max_connections` to the maximum number of concurrent Redis operations\nyou expect from the process. If you'd rather block on pool exhaustion than\nraise an error, construct the client with a `BlockingConnectionPool`.\n\n> [!NOTE]\n> Don't share a single `ConnectionPool` across multiple `Redis(connection_pool=...)`\n> instances. Closing any one of those clients also closes the shared pool,\n> which silently invalidates the connections held by every other client using\n> it. Share the `Redis` client object instead."},{"id":"awaiting-commands","title":"Awaiting commands","role":"content","text":"Every command method on the async client returns a coroutine — each call\nmust be `await`ed. Forgetting `await` returns a coroutine object instead of\na result, which is the most common async mistake to watch for.\n\nBecause each command is a coroutine, you can run several concurrently with\n`asyncio.gather()`:\n\nRun several Redis commands concurrently with asyncio.gather\n\n**Difficulty:** Intermediate\n\n**Available in:** Python\n\n##### Python\n\n[code example]\n\n\n\nEach in-flight command consumes one pooled connection while it's executing,\nso size `max_connections` to match your peak concurrency. (If you instantiate\nthe client with `single_connection_client=True`, all commands serialize\nthrough a single connection instead.)"},{"id":"pipelines-and-transactions","title":"Pipelines and transactions","role":"content","text":"Pipelines and transactions work the same way as in the synchronous client\n(see [Pipelines and transactions](https://redis.io/docs/latest/develop/clients/redis-py/transpipe)\nfor the conceptual background). The only difference is that you create the\npipeline inside an `async with` block and `await pipe.execute()`.\n\nFoundational: Execute commands in an async pipeline\n\n**Difficulty:** Beginner\n\n**Available in:** Python\n\n##### Python\n\n[code example]\n\n\n\n`WATCH`/`MULTI`/`EXEC` for optimistic locking also has an async form.\n`watch()` and `execute()` are coroutines; `multi()` remains synchronous\nbecause it only toggles internal pipeline state.\n\nOptimistic locking with an async pipeline and WATCH\n\n**Difficulty:** Intermediate\n\n**Available in:** Python\n\n##### Python\n\n[code example]"},{"id":"pubsub","title":"Pub/Sub","role":"content","text":"The async pub/sub object follows the same shape as the sync version. Call\n`await pubsub.subscribe(...)` to register channels, then iterate messages\nwith `async for message in pubsub.listen():`. Use the `PubSub` object as an\nasync context manager (`async with r.pubsub() as pubsub:`) or call\n`await pubsub.aclose()` explicitly to release the connection.\n\nSubscribe and receive messages with the async pub/sub API\n\n**Difficulty:** Intermediate\n\n**Available in:** Python\n\n##### Python\n\n[code example]\n\n\n\nA single `PubSub` object isn't safe to share across tasks — give each\nconsuming task its own subscription."},{"id":"cluster-connections","title":"Cluster connections","role":"content","text":"To connect to a Redis cluster asynchronously, import `RedisCluster` from\n`redis.asyncio.cluster`. The API matches the synchronous cluster client\n(see [Connect to a Redis cluster](https://redis.io/docs/latest/develop/clients/redis-py/connect#connect-to-a-redis-cluster)),\nwith `await` in front of each command.\n\nFoundational: Connect to a Redis cluster with the async client\n\n**Difficulty:** Beginner\n\n**Available in:** Python\n\n##### Python\n\n[code example]"},{"id":"cleanup-and-lifecycle","title":"Cleanup and lifecycle","role":"content","text":"Always close clients and pools when you're done:\n\n- Use `async with Redis(...) as r:` whenever the client's lifetime fits a\n  single scope.\n- For longer-lived clients, call `await r.aclose()` explicitly. (The older\n  `close()` method is deprecated.)\n- For frameworks with startup/shutdown hooks — for example [FastAPI](https://redis.io/docs/latest/integrate/fastapi)'s\n  `lifespan` — create the client or pool at startup and close it at\n  shutdown so connections aren't leaked between process restarts."},{"id":"cancellation-and-timeouts","title":"Cancellation and timeouts","role":"content","text":"You can cancel an in-flight command by wrapping it in `asyncio.wait_for()`\n(or `asyncio.timeout()` on Python 3.11+). If the command is canceled\nmid-flight, `redis-py` disconnects the underlying connection to avoid\nresponse/request misalignment on subsequent reads. The next command\ntransparently picks up a new connection from the pool.\n\nApply an asyncio timeout to a Redis command\n\n**Difficulty:** Intermediate\n\n**Available in:** Python\n\n##### Python\n\n[code example]\n\n\n\nA `Redis` client instance is safe to share across tasks (the pool handles\nconcurrency), but stateful objects derived from it — pipelines and pub/sub\nsubscriptions — are not. Give each task its own pipeline or `PubSub`."},{"id":"translating-sync-examples","title":"Translating sync examples","role":"content","text":"To adapt any synchronous example elsewhere in this guide to the async\nclient, apply these rules:\n\n- Replace `import redis` with `import redis.asyncio as redis`.\n- Wrap the example in an `async def` function and call it with\n  `asyncio.run(...)`.\n- Add `await` in front of every command call. (Exception: buffered pipeline\n  commands such as `pipe.set(...)` stay un-awaited; only `pipe.watch(...)`,\n  any reads issued before `pipe.multi()`, and `pipe.execute()` are awaited.)\n- Replace `with r.pipeline(...) as pipe:` with\n  `async with r.pipeline(...) as pipe:`, and `await pipe.execute()`.\n- Replace `r.close()` with `await r.aclose()`, or use\n  `async with redis.Redis(...) as r:` as a context manager."},{"id":"more-information","title":"More information","role":"content","text":"- The [`redis-py` asyncio examples](https://redis.readthedocs.io/en/stable/examples/asyncio_examples.html)\n  on Read the Docs cover further patterns.\n- See [Error handling](https://redis.io/docs/latest/develop/clients/redis-py/error-handling) and\n  [Client-side geographic failover](https://redis.io/docs/latest/develop/clients/redis-py/failover) for\n  resiliency patterns that apply to both sync and async clients."}],"examples":[{"id":"basic-connection-ex0","language":"python","code":"import redis.asyncio as redis\n\nasync def basic_example():\n    r = redis.Redis(host='localhost', port=6379, decode_responses=True)\n    await r.set('foo', 'bar')\n    value = await r.get('foo')\n    print(value)\n    # bar\n    await r.aclose()\n\nasyncio.run(basic_example())","section_id":"basic-connection"},{"id":"basic-connection-ex1","language":"python","code":"async def context_example():\n    async with redis.Redis(\n        host='localhost', port=6379, decode_responses=True\n    ) as r:\n        await r.set('foo', 'bar')\n        value = await r.get('foo')\n        print(value)\n        # bar\n\nasyncio.run(context_example())","section_id":"basic-connection"},{"id":"connection-pools-ex0","language":"python","code":"async def shared_client_example():\n    # Create one client at startup. It owns its own internal pool,\n    # which is sized by max_connections.\n    r = redis.Redis(\n        host='localhost', port=6379, decode_responses=True,\n        max_connections=10,\n    )\n    try:\n        # Share `r` across every request and task for the app's lifetime.\n        await r.set('foo', 'bar')\n        value = await r.get('foo')\n        print(value)\n        # bar\n    finally:\n        # Close the client at shutdown to release pooled connections.\n        await r.aclose()\n\nasyncio.run(shared_client_example())","section_id":"connection-pools"},{"id":"awaiting-commands-ex0","language":"python","code":"async def gather_example():\n    async with redis.Redis(\n        host='localhost', port=6379, decode_responses=True\n    ) as r:\n        await r.mset({'a': '1', 'b': '2', 'c': '3'})\n        a, b, c = await asyncio.gather(\n            r.get('a'),\n            r.get('b'),\n            r.get('c'),\n        )\n        print(a, b, c)\n        # 1 2 3\n\nasyncio.run(gather_example())","section_id":"awaiting-commands"},{"id":"pipelines-and-transactions-ex0","language":"python","code":"async def pipeline_example():\n    async with redis.Redis(\n        host='localhost', port=6379, decode_responses=True\n    ) as r:\n        async with r.pipeline(transaction=True) as pipe:\n            pipe.set('a', '1')\n            pipe.set('b', '2')\n            pipe.get('a')\n            pipe.get('b')\n            results = await pipe.execute()\n            print(results)\n            # [True, True, '1', '2']\n\nasyncio.run(pipeline_example())","section_id":"pipelines-and-transactions"},{"id":"pipelines-and-transactions-ex1","language":"python","code":"from redis.exceptions import WatchError\n\nasync def watch_example():\n    async with redis.Redis(\n        host='localhost', port=6379, decode_responses=True\n    ) as r:\n        await r.set('counter', '0')\n        async with r.pipeline(transaction=True) as pipe:\n            while True:\n                try:\n                    await pipe.watch('counter')\n                    current = int(await pipe.get('counter'))\n                    pipe.multi()\n                    pipe.set('counter', str(current + 1))\n                    await pipe.execute()\n                    break\n                except WatchError:\n                    continue\n        print(await r.get('counter'))\n        # 1\n\nasyncio.run(watch_example())","section_id":"pipelines-and-transactions"},{"id":"pubsub-ex0","language":"python","code":"async def pubsub_example():\n    async with redis.Redis(\n        host='localhost', port=6379, decode_responses=True\n    ) as r:\n        async with r.pubsub() as pubsub:\n            await pubsub.subscribe('channel-1')\n\n            async def reader():\n                async for message in pubsub.listen():\n                    if message['type'] == 'message':\n                        print(message['data'])\n                        # hello\n                        break\n\n            reader_task = asyncio.create_task(reader())\n            await asyncio.sleep(0.1)\n            await r.publish('channel-1', 'hello')\n            await reader_task\n\nasyncio.run(pubsub_example())","section_id":"pubsub"},{"id":"cluster-connections-ex0","language":"python","code":"from redis.asyncio.cluster import RedisCluster\n\nasync def cluster_example():\n    rc = RedisCluster(host='localhost', port=16379, decode_responses=True)\n    await rc.set('foo', 'bar')\n    value = await rc.get('foo')\n    print(value)\n    # bar\n    await rc.aclose()\n\nasyncio.run(cluster_example())","section_id":"cluster-connections"},{"id":"cancellation-and-timeouts-ex0","language":"python","code":"async def timeout_example():\n    async with redis.Redis(\n        host='localhost', port=6379, decode_responses=True\n    ) as r:\n        try:\n            # BLPOP blocks waiting for a value, so the timeout reliably fires.\n            await asyncio.wait_for(r.blpop('empty-queue'), timeout=0.1)\n        except asyncio.TimeoutError:\n            print('command canceled')\n\nasyncio.run(timeout_example())","section_id":"cancellation-and-timeouts"}]}
{"schema_version":2,"id":"develop/clients/redis-py/connect","title":"Connect to the server","url":"https://redis.io/docs/latest/develop/clients/redis-py/connect/","summary":"Connect your Python application to a Redis database","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-12T14:48:04+01:00","page_type":"content","content_hash":"cb30d74eaf324ee40dcfff85dc628bf4e6a5b51b271ef37941d2c66b4d869312","sections":[{"id":"basic-connection","title":"Basic connection","role":"overview","text":"Connect to localhost on port 6379, set a value in Redis, and retrieve it. All responses are returned as bytes in Python. To receive decoded strings, set `decode_responses=True`. For more connection options, see [these examples](https://redis.readthedocs.io/en/stable/examples.html).\n\n[code example]\n\n> [!NOTE]\n> redis-py supports [connection URLs](https://redis.io/docs/latest/develop/clients/connection-urls#redis-py-python) through `redis.from_url()`.\n> A URL can set the host, port, credentials, database, and whether to use TLS,\n> so you can use one instead of passing those options separately.\n\nStore and retrieve a simple string.\n\n[code example]\n\nStore and retrieve a dict.\n\n[code example]"},{"id":"connect-to-a-redis-cluster","title":"Connect to a Redis cluster","role":"content","text":"To connect to a Redis cluster, use `RedisCluster`.\n\n[code example]\nFor more information, see [redis-py Clustering](https://redis.readthedocs.io/en/stable/clustering.html)."},{"id":"connect-to-redis-sentinel","title":"Connect to Redis Sentinel","role":"content","text":"To connect through [Redis Sentinel](https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel),\ncreate a `Sentinel` object with a list of Sentinel addresses. Then call `master_for()` with\nthe name of the primary that the Sentinels monitor. The client asks the Sentinels for the\ncurrent address of the primary.\n\n[code example]\n\nThe Sentinels and the data nodes have separate credentials. Pass the Sentinel credentials\nin `sentinel_kwargs`, and the credentials for the primary and replicas to `master_for()`:\n\n[code example]\n\nIf you set `sentinel_kwargs`, the Sentinel connections don't inherit `socket_timeout` or the\nother `socket_*` options, so add any that you need to `sentinel_kwargs`.\n\nTo send read-only commands to a replica, use `slave_for()`. Replication is asynchronous, so\na replica can return stale data.\n\n[code example]\n\nredis-py doesn't listen for failover announcements from the Sentinels. After a failover,\nthe client keeps using the old primary until Sentinel reconfigures it as a replica and it\nstarts rejecting writes. Any writes that the old primary accepts during that time are lost."},{"id":"connect-to-your-production-redis-with-tls","title":"Connect to your production Redis with TLS","role":"content","text":"When you deploy your application, use TLS and follow the [Redis security](https://redis.io/docs/latest/operate/oss_and_stack/management/security) guidelines.\n\n[code example]\nFor more information, see [redis-py TLS examples](https://redis.readthedocs.io/en/stable/examples/ssl_connection_examples.html)."},{"id":"connect-using-client-side-caching","title":"Connect using client-side caching","role":"content","text":"Client-side caching is a technique to reduce network traffic between\nthe client and server, resulting in better performance. See\n[Client-side caching introduction](https://redis.io/docs/latest/develop/clients/client-side-caching)\nfor more information about how client-side caching works and how to use it effectively.\n\nTo enable client-side caching, add some extra parameters when you connect\nto the server:\n\n-   `protocol`: (Required) You must pass a value of `3` here because\n    client-side caching requires the [RESP3](https://redis.io/docs/latest/develop/reference/protocol-spec#resp-versions)\n    protocol.\n-   `cache_config`: (Required) Pass `cache_config=CacheConfig()` here to enable client-side caching.\n\nThe example below shows the simplest client-side caching connection to the default host and port,\n`localhost:6379`.\nAll of the connection variants described above accept these parameters, so you can\nuse client-side caching with a connection pool or a cluster connection in exactly the same way.\n\n> [!NOTE]\n> Client-side caching requires redis-py v5.1.0 or later.\n> To maximize compatibility with all Redis products, client-side caching\n> is supported by Redis v7.4 or later.\n>\n> The [Redis server products](https://redis.io/docs/latest/operate) support\n> [opt-in/opt-out](https://redis.io/docs/latest/develop/reference/client-side-caching#opt-in-and-opt-out-caching) mode\n> and [broadcasting mode](https://redis.io/docs/latest/develop/reference/client-side-caching#broadcasting-mode)\n> for CSC, but these modes are not currently implemented by `redis-py`.\n\n[code example]\n\nYou can see the cache working if you connect to the same Redis database\nwith [`redis-cli`](https://redis.io/docs/latest/develop/tools/cli) and run the\n[`MONITOR`](https://redis.io/docs/latest/commands/monitor) command. If you run the\ncode above with the `cache_config` line commented out, you should see\nthe following in the CLI among the output from `MONITOR`:\n\n[code example]\n\nThe server responds to both `get(\"city\")` calls.\nIf you run the code again with `cache_config` uncommented, you will see\n\n[code example]\n\nThe first `get(\"city\")` call contacted the server but the second\ncall was satisfied by the cache."},{"id":"removing-items-from-the-cache","title":"Removing items from the cache","role":"content","text":"You can remove individual keys from the cache with the\n`delete_by_redis_keys()` method. This removes all cached items associated\nwith the keys, so all results from multi-key commands (such as\n[`MGET`](https://redis.io/docs/latest/commands/mget)) and composite data structures\n(such as [hashes](https://redis.io/docs/latest/develop/data-types/hashes)) will be\ncleared at once. The example below shows the effect of removing a single\nkey from the cache:\n\n[code example]\n\nYou can also clear all cached items using the `flush()`\nmethod:\n\n[code example]\n\nThe client will also flush the cache automatically\nif any connection (including one from a connection pool)\nis disconnected."},{"id":"connect-with-a-connection-pool","title":"Connect with a connection pool","role":"content","text":"For production usage, you should use a connection pool to manage\nconnections rather than opening and closing connections individually.\nA connection pool maintains several open connections and reuses them\nefficiently. When you open a connection from a pool, the pool allocates\none of its open connections. When you subsequently close the same connection,\nit is not actually closed but simply returned to the pool for reuse.\nThis avoids the overhead of repeated connecting and disconnecting.\nSee\n[Connection pools and multiplexing](https://redis.io/docs/latest/develop/clients/pools-and-muxing)\nfor more information.\n\nUse the following code to connect with a connection pool:\n\n[code example]"},{"id":"retrying-connections","title":"Retrying connections","role":"content","text":"A connection will sometimes fail because of a transient problem, such as a\nnetwork outage or a server that is temporarily unavailable. In these cases,\nretrying the connection after a short delay will usually succeed. `redis-py` uses\na simple retry strategy by default, but there are various ways you can customize\nthis behavior to suit your use case. See\n[Retries](https://redis.io/docs/latest/develop/clients/redis-py/produsage#retries)\nfor more information about custom retry strategies, with example code."},{"id":"connect-using-smart-client-handoffs-sch","title":"Connect using Smart client handoffs (SCH)","role":"content","text":"*Smart client handoffs (SCH)* is a feature of Redis Cloud and\nRedis Software servers that lets them actively notify clients\nabout planned server maintenance shortly before it happens. This\nlets a client take action to avoid disruptions in service.\nSee [Smart client handoffs](https://redis.io/docs/latest/develop/clients/sch)\nfor more information about SCH.\n\n> [!NOTE]\n> Using SCH with redis-py requires v7.0.0 or later for\n> standard routing, and v7.2.0 or later for\n> [OSS Cluster API](https://redis.io/docs/latest/operate/rs/databases/configure/oss-cluster-api) connections.\n\nBy default, `redis-py` always attempts to connect via SCH but falls back to\na non-SCH connection if the server doesn't support it. However, you can configure SCH\nexplicitly by passing a `MaintNotificationsConfig` object during the connection,\nas shown in the following example:\n\n[code example]\n\nTo disable SCH, pass `enabled=False` in the `MaintNotificationsConfig` object:\n\n[code example]\n\n> [!NOTE]\n> SCH requires the [RESP3](https://redis.io/docs/latest/develop/reference/protocol-spec#resp-versions)\n> protocol, so you must set `protocol=3` explicitly when you connect.\n\nThe `MaintNotificationsConfig` constructor accepts the following parameters:\n\n| Name | Type | Default | Description |\n|------|------|---------|-------------|\n| `enabled` | `bool` | `True` | Whether or not to enable SCH. |\n| `proactive_reconnect` | `bool` | `True` | Whether or not to automatically reconnect when a node is replaced. |\n| `endpoint_type` | `EndpointType` | Auto-detect | The type of endpoint to use for the connection. The options are `EndpointType.EXTERNAL_IP`, `EndpointType.INTERNAL_IP`, `EndpointType.EXTERNAL_FQDN`, `EndpointType.INTERNAL_FQDN`, and `EndpointType.NONE`. |\n| `relaxed_timeout` | `int` | `20` | The timeout (in seconds) to use while the server is performing maintenance. A value of `-1` disables the relax timeout and just uses the normal timeout during maintenance. |\n\n> [!NOTE]\n> Redis Cloud supports relaxed timeouts *only* (and not pre-handoffs) for SCH if you are using\n> either [AWS PrivateLink](https://redis.io/docs/latest/operate/rc/security/aws-privatelink) or\n> [Google Cloud Private Service Connect](https://redis.io/docs/latest/operate/rc/security/private-service-connect)\n> (see [Smart client handoffs](https://redis.io/docs/latest/develop/clients/sch#redis-cloud) for more information).\n> To use relaxed timeouts with these services, you should set `endpoint_type=EndpointType.NONE`\n> when you connect. All other configurations have full support for both relaxed timeouts and pre-handoffs."}],"examples":[{"id":"basic-connection-ex0","language":"python","code":"r = redis.Redis(host='localhost', port=6379, decode_responses=True)","section_id":"basic-connection"},{"id":"basic-connection-ex1","language":"python","code":"r.set('foo', 'bar')\n# True\nr.get('foo')\n# bar","section_id":"basic-connection"},{"id":"basic-connection-ex2","language":"python","code":"r.hset('user-session:123', mapping={\n    'name': 'John',\n    \"surname\": 'Smith',\n    \"company\": 'Redis',\n    \"age\": 29\n})\n# True\n\nr.hgetall('user-session:123')\n# {'surname': 'Smith', 'name': 'John', 'company': 'Redis', 'age': '29'}","section_id":"basic-connection"},{"id":"connect-to-a-redis-cluster-ex0","language":"python","code":"from redis.cluster import RedisCluster\n\nrc = RedisCluster(host='localhost', port=16379)\n\nprint(rc.get_nodes())\n# [[host=127.0.0.1,port=16379,name=127.0.0.1:16379,server_type=primary,redis_connection=Redis<ConnectionPool<Connection<host=127.0.0.1,port=16379,db=0>>>], ...\n\nrc.set('foo', 'bar')\n# True\n\nrc.get('foo')\n# b'bar'","section_id":"connect-to-a-redis-cluster"},{"id":"connect-to-redis-sentinel-ex0","language":"python","code":"from redis.sentinel import Sentinel\n\nsentinel = Sentinel([('localhost', 26379), ('localhost', 26380), ('localhost', 26381)])\nr = sentinel.master_for('mymaster', decode_responses=True)\n\nr.set('foo', 'bar')\n# True\n\nr.get('foo')\n# bar","section_id":"connect-to-redis-sentinel"},{"id":"connect-to-redis-sentinel-ex1","language":"python","code":"from redis.sentinel import Sentinel\n\nsentinel = Sentinel(\n    [('localhost', 26379), ('localhost', 26380), ('localhost', 26381)],\n    sentinel_kwargs={'password': 'sentinel-secret'},  # use your Sentinel password\n)\nr = sentinel.master_for(\n    'mymaster',\n    username='default',  # use your Redis user\n    password='secret',  # use your Redis password\n    decode_responses=True,\n)","section_id":"connect-to-redis-sentinel"},{"id":"connect-to-redis-sentinel-ex2","language":"python","code":"replica = sentinel.slave_for('mymaster', decode_responses=True)\n\nreplica.get('foo')\n# bar","section_id":"connect-to-redis-sentinel"},{"id":"connect-to-your-production-redis-with-tls-ex0","language":"python","code":"import redis\n\nr = redis.Redis(\n    host=\"my-redis.cloud.redislabs.com\", port=6379,\n    username=\"default\", # use your Redis user. More info https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl/\n    password=\"secret\", # use your Redis password\n    ssl=True,\n    ssl_certfile=\"./redis_user.crt\",\n    ssl_keyfile=\"./redis_user_private.key\",\n    ssl_ca_certs=\"./redis_ca.pem\",\n)\nr.set('foo', 'bar')\n# True\n\nr.get('foo')\n# b'bar'","section_id":"connect-to-your-production-redis-with-tls"},{"id":"connect-using-client-side-caching-ex0","language":"python","code":"import redis\nfrom redis.cache import CacheConfig\n\nr = redis.Redis(\n    protocol=3,\n    cache_config=CacheConfig(),\n    decode_responses=True\n)\n\nr.set(\"city\", \"New York\")\ncityNameAttempt1 = r.get(\"city\")    # Retrieved from Redis server and cached\ncityNameAttempt2 = r.get(\"city\")    # Retrieved from cache","section_id":"connect-using-client-side-caching"},{"id":"connect-using-client-side-caching-ex1","language":"plaintext","code":"1723109720.268903 [...] \"SET\" \"city\" \"New York\"\n1723109720.269681 [...] \"GET\" \"city\"\n1723109720.270205 [...] \"GET\" \"city\"","section_id":"connect-using-client-side-caching"},{"id":"connect-using-client-side-caching-ex2","language":"plaintext","code":"1723110248.712663 [...] \"SET\" \"city\" \"New York\"\n1723110248.713607 [...] \"GET\" \"city\"","section_id":"connect-using-client-side-caching"},{"id":"removing-items-from-the-cache-ex0","language":"python","code":"r.hget(\"person:1\", \"name\") # Read from the server\nr.hget(\"person:1\", \"name\") # Read from the cache\n\nr.hget(\"person:2\", \"name\") # Read from the server\nr.hget(\"person:2\", \"name\") # Read from the cache\n\ncache = r.get_cache()\ncache.delete_by_redis_keys([\"person:1\"])\n\nr.hget(\"person:1\", \"name\") # Read from the server\nr.hget(\"person:1\", \"name\") # Read from the cache\n\nr.hget(\"person:2\", \"name\") # Still read from the cache","section_id":"removing-items-from-the-cache"},{"id":"removing-items-from-the-cache-ex1","language":"python","code":"r.hget(\"person:1\", \"name\") # Read from the server\nr.hget(\"person:1\", \"name\") # Read from the cache\n\nr.hget(\"person:2\", \"name\") # Read from the cache\nr.hget(\"person:2\", \"name\") # Read from the cache\n\ncache = r.get_cache()\ncache.flush()\n\nr.hget(\"person:1\", \"name\") # Read from the server\nr.hget(\"person:1\", \"name\") # Read from the cache\n\nr.hget(\"person:2\", \"name\") # Read from the server\nr.hget(\"person:2\", \"name\") # Read from the cache","section_id":"removing-items-from-the-cache"},{"id":"connect-with-a-connection-pool-ex0","language":"python","code":"import redis\n\npool = redis.ConnectionPool().from_url(\"redis://localhost\")\nr1 = redis.Redis().from_pool(pool)\nr2 = redis.Redis().from_pool(pool)\nr3 = redis.Redis().from_pool(pool)\n\nr1.set(\"wind:1\", \"Hurricane\")\nr2.set(\"wind:2\", \"Tornado\")\nr3.set(\"wind:3\", \"Mistral\")\n\nr1.close()\nr2.close()\nr3.close()\n\npool.close()","section_id":"connect-with-a-connection-pool"},{"id":"connect-using-smart-client-handoffs-sch-ex0","language":"py","code":"import redis\nfrom redis.maint_notifications import MaintNotificationsConfig, EndpointType\n\nr = redis.Redis(\n    decode_responses=True,\n    protocol=3,\n    maint_notifications_config=MaintNotificationsConfig(\n        proactive_reconnect=True,\n        relaxed_timeout=10,\n        endpoint_type=EndpointType.EXTERNAL_IP\n    ),\n    ...\n)","section_id":"connect-using-smart-client-handoffs-sch"},{"id":"connect-using-smart-client-handoffs-sch-ex1","language":"python","code":"r = redis.Redis(\n    maint_notifications_config=MaintNotificationsConfig(\n        enabled=False,\n    ),\n    ...\n)","section_id":"connect-using-smart-client-handoffs-sch"}]}
{"schema_version":2,"id":"develop/clients/redis-py/error-handling","title":"Error handling","url":"https://redis.io/docs/latest/develop/clients/redis-py/error-handling/","summary":"Learn how to handle errors when using redis-py","tags":[],"last_updated":"2026-08-27T15:55:18+01:00","page_type":"content","content_hash":"eee2b6552e05dd60c198cad0131a225dba147c42c7b247fe7760a9f8c165ea98","sections":[{"id":"overview","title":"Overview","role":"overview","text":"redis-py uses **exceptions** to signal errors. The redis-py documentation mainly\nshows the \"happy path\" in code examples and omits error handling for brevity.\nThis page explains how\nredis-py's error handling works and how to apply common error handling patterns.\nFor an overview of error types and handling strategies, see\n[Error handling](https://redis.io/docs/latest/develop/clients/error-handling).\nSee also [Production usage](https://redis.io/docs/latest/develop/clients/redis-py/produsage)\nfor more information on connection management, timeouts, and other aspects of\napp reliability."},{"id":"exception-hierarchy","title":"Exception hierarchy","role":"errors","text":"redis-py organizes exceptions in a hierarchy. The base exception is `redis.RedisError`, with specific subclasses for different error types, as shown below:\n\n[code example]"},{"id":"key-exceptions","title":"Key exceptions","role":"content","text":"The following exceptions are the most commonly encountered in redis-py applications.\nSee\n[Categories of errors](https://redis.io/docs/latest/develop/clients/error-handling#categories-of-errors)\nfor a more detailed discussion of these errors and their causes.\n\n| Exception | When it occurs | Recoverable | Recommended action |\n|---|---|---|---|\n| `redis.ConnectionError` | Network or connection issues | ✅ | Retry with backoff or fall back to alternative |\n| `redis.TimeoutError` | Operation exceeded timeout | ✅ | Retry with backoff |\n| `redis.ResponseError` | Invalid command or Redis error response | ❌ | Fix the command or arguments |\n| `redis.DataError` | Data serialization/deserialization issues | ⚠️ | Log, invalidate cache, fetch fresh data |"},{"id":"applying-error-handling-patterns","title":"Applying error handling patterns","role":"content","text":"The [Error handling](https://redis.io/docs/latest/develop/clients/error-handling) overview\ndescribes four main patterns. The sections below show how to implement them in\nredis-py:"},{"id":"pattern-1-fail-fast","title":"Pattern 1: Fail fast","role":"content","text":"Catch specific exceptions that represent unrecoverable errors and re-raise them (see\n[Pattern 1: Fail fast](https://redis.io/docs/latest/develop/clients/error-handling#pattern-1-fail-fast)\nfor a full description):\n\n[code example]"},{"id":"pattern-2-graceful-degradation","title":"Pattern 2: Graceful degradation","role":"content","text":"Catch connection errors and fall back to an alternative (see\n[Pattern 2: Graceful degradation](https://redis.io/docs/latest/develop/clients/error-handling#pattern-2-graceful-degradation)\nfor a full description):\n\n[code example]"},{"id":"pattern-3-retry-with-backoff","title":"Pattern 3: Retry with backoff","role":"content","text":"Retry on temporary errors like timeouts (see\n[Pattern 3: Retry with backoff](https://redis.io/docs/latest/develop/clients/error-handling#pattern-3-retry-with-backoff)\nfor a full description). redis-py has built-in retry logic\nwhich is highly configurable. You can customize the retry strategy\n(or supply your own custom strategy) and you can also specify which errors\nshould be retried. See\n[Production usage](https://redis.io/docs/latest/develop/clients/redis-py/produsage#retries)\nfor more information."},{"id":"pattern-4-log-and-continue","title":"Pattern 4: Log and continue","role":"content","text":"Log non-critical errors and continue (see\n[Pattern 4: Log and continue](https://redis.io/docs/latest/develop/clients/error-handling#pattern-4-log-and-continue)\nfor a full description):\n\n[code example]"},{"id":"async-error-handling","title":"Async error handling","role":"content","text":"Error handling works the usual way when you use `async`/`await`,\nas shown in the example below:\n\n[code example]"},{"id":"see-also","title":"See also","role":"related","text":"- [Error handling](https://redis.io/docs/latest/develop/clients/error-handling)\n- [Production usage](https://redis.io/docs/latest/develop/clients/redis-py/produsage)"}],"examples":[{"id":"exception-hierarchy-ex0","language":"hierarchy {type=\"exception\"}","code":"\"RedisError\":\n    _meta:\n        description: \"Base class for all redis-py exceptions\"\n    \"ConnectionError\":\n        \"TimeoutError\":\n        \"BusyLoadingError\":\n    \"ResponseError\":\n    \"InvalidResponse\":\n    \"DataError\":\n    \"PubSubError\":\n    \"...\":\n        _meta:\n            ellipsis: true\n            description: \"Other exception types\"","section_id":"exception-hierarchy"},{"id":"pattern-1-fail-fast-ex0","language":"python","code":"import redis\n\nr = redis.Redis()\n\ntry:\n    result = r.get(key)\nexcept redis.ResponseError:\n    # This indicates a bug in the code\n    raise","section_id":"pattern-1-fail-fast"},{"id":"pattern-2-graceful-degradation-ex0","language":"python","code":"try:\n    cached_value = r.get(key)\n    if cached_value:\n        return cached_value\nexcept redis.ConnectionError:\n    logger.warning(\"Cache unavailable, using database\")\n\n# Fallback to database\nreturn database.get(key)","section_id":"pattern-2-graceful-degradation"},{"id":"pattern-4-log-and-continue-ex0","language":"python","code":"try:\n    r.setex(key, 3600, value)\nexcept redis.ConnectionError:\n    logger.warning(f\"Failed to cache {key}, continuing without cache\")\n    # Application continues normally","section_id":"pattern-4-log-and-continue"},{"id":"async-error-handling-ex0","language":"python","code":"import redis.asyncio as redis\n\nasync def get_with_fallback(key):\n    r = await redis.from_url(\"redis://localhost\")\n    try:\n        return await r.get(key)\n    except redis.ConnectionError:\n        logger.warning(\"Cache unavailable\")\n        return await database.get(key)\n    finally:\n        await r.close()","section_id":"async-error-handling"}]}
{"schema_version":2,"id":"develop/clients/redis-py/failover","title":"Client-side geographic failover","url":"https://redis.io/docs/latest/develop/clients/redis-py/failover/","summary":"Improve reliability using the failover features of redis-py.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-08-27T15:55:18+01:00","page_type":"content","content_hash":"41dcdad06042365bd92075d27a6b85e246f0aaf90ed8853b14d3b6699ae5ef9d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"redis-py supports [Client-side geographic failover](https://en.wikipedia.org/wiki/Failover)\nto improve the availability of connections to Redis databases. This page explains\nhow to configure redis-py for failover. For an overview of the concepts,\nsee the main [Client-side geographic failover](https://redis.io/docs/latest/develop/clients/failover) page."},{"id":"failover-configuration","title":"Failover configuration","role":"content","text":"The example below shows a simple case with a list of two servers,\n`redis-east` and `redis-west`, where `redis-east` is the preferred\ntarget. If `redis-east` fails, redis-py should fail over to\n`redis-west`.\n\nSupply the weighted endpoints using a list of `DatabaseConfig` objects\n(see [Selecting a failover target](https://redis.io/docs/latest/develop/clients/failover#selecting-a-failover-target) for a full description of how\nthe weighted list is used).\nUse the `weight` option to order the endpoints, with the highest\nweight being tried first. Then, use the list to create a `MultiDbConfig` object,\nwhich you can pass to the `MultiDBClient` constructor to create the client.\n`MultiDBClient` implements the usual Redis commands using an internal\n`RedisClient` instance, but will also handle the connection management and failover transparently.\n\n[code example]"},{"id":"endpoint-configuration","title":"Endpoint configuration","role":"content","text":"The `DatabaseConfig` class provides several options to configure each endpoint, as\ndescribed in the table below. Supply the configurations for the whole set of\nendpoints by passing a list of `DatabaseConfig` objects to the `MultiDbConfig`\nconstructor in the `databases_config` parameter.\n\n| Option | Description |\n| --- | --- |\n| `client_kwargs` | Keyword parameters to pass to the internal client constructor for this endpoint. Use it to specify the host, port, username, password, and other connection parameters (see [Connect to the server](https://redis.io/docs/latest/develop/clients/redis-py/connect) for more information). This is especially useful if you are using a custom client class (see [Client configuration](#client-configuration) below for more information). |\n| `from_url` | Redis URL to connect to this endpoint, as an alternative to passing the host and port in `client_kwargs`. |\n| `from_pool` | A `ConnectionPool` to supply the endpoint connection (see [Connect with a connection pool](https://redis.io/docs/latest/develop/clients/redis-py/connect#connect-with-a-connection-pool) for more information) |\n| `weight` | Priority of the endpoint, with higher values being tried first. Default is `1.0`. |\n| `grace_period` | Duration in seconds to keep an unhealthy endpoint disabled before attempting a failback. Default is `60` seconds. |\n| `health_check_url` | URL for health checks that use the database's REST API (see [`LagAwareHealthCheck`](#lag-aware-health-check) for more information). |"},{"id":"client-configuration","title":"Client configuration","role":"content","text":"`MultiDbConfig` provides the `client_class` option to specify the class of the internal client to use for each endpoint. The default is the basic `redis.Redis` client, but\nyou could, for example, replace this with `redis.asyncio.client.Redis` for an asynchronous basic client, or with `redis.cluster.RedisCluster`/`redis.asyncio.cluster.RedisCluster` for a cluster client. Use the `client_kwargs` option of `DatabaseConfig` to supply any extra parameters required by the client class (see [Endpoint configuration](#endpoint-configuration) above for more information).\n\n[code example]"},{"id":"circuit-breaker-configuration","title":"Circuit breaker configuration","role":"content","text":"`MultiDbConfig` gives you several options to configure the circuit breaker\n(see [Detecting connection problems](https://redis.io/docs/latest/develop/clients/failover#detecting-connection-problems) for more information on how the\ncircuit breaker works):\n\n| Option | Description |\n| --- | --- |\n| `failures_detection_window` | Duration in seconds to keep failures and successes in the sliding window. Default is `2` seconds. |\n| `min_num_failures` | Minimum number of failures that must occur to trigger a failover. Default is `1000`. |\n| `failure_rate_threshold` | Fraction of failed commands required to trigger a failover. Default is `0.1` (10%). |"},{"id":"retry-configuration","title":"Retry configuration","role":"content","text":"`MultiDbConfig` provides the `command_retry` option to configure retries for failed commands. This follows the usual approach to configuring retries used with a standard\n`RedisClient` connection (see [Retries](https://redis.io/docs/latest/develop/clients/redis-py/produsage#retries) for more information).\n\n[code example]"},{"id":"failback-configuration","title":"Failback configuration","role":"content","text":"The `MultiDbConfig` constructor provides the following option to configure the failback behavior:\n\n| Option | Description |\n| --- | --- |\n| `auto_fallback_interval` | Time interval between automatic failback attempts. Set this to `-1` to disable automatic failback. Default value is `30` seconds (which means failback is enabled by default). |"},{"id":"general-failover-configuration","title":"General failover configuration","role":"content","text":"There are also a few other options you can pass to the `MultiDbConfig` constructor to control the failover behavior:\n\n| Option | Description |\n| --- | --- |\n| `failover_attempts` | Number of attempts to fail over to a new endpoint before giving up. Default is `10`. |\n| `failover_delay` | Time interval between successive failover attempts. Default is `12` seconds. |\n| `event_dispatcher` | `EventDispatcher` object to use for emitting events. Supply this to register custom event listeners (see [Failover callbacks](#failover-callbacks) below for more information). |"},{"id":"failover-callbacks","title":"Failover callbacks","role":"content","text":"You may want to take some custom action when a failover occurs. For example, you could log a warning, increment a metric, or externally persist the cluster connection state.\n\nYou can implement a custom event listener using a class that implements `EventListenerInterface`. This includes a `listen()` method that is called when\nthe event occurs. You should specifically listen for the `ActiveDatabaseChanged`\nevent, which is emitted when a failover happens.\n\nThe example below shows how to implement a simple listener class and register an instance\nof it using an `EventDispatcher` object. You can then pass the `EventDispatcher` to the\n`MultiDbConfig` constructor to enable the custom behavior.\n\n[code example]"},{"id":"health-check-configuration","title":"Health check configuration","role":"content","text":"Each health check consists of one or more separate \"probes\", each of which is a simple\ntest (such as a [`PING`](https://redis.io/docs/latest/commands/ping) command) to determine if the\ndatabase is available. The results of the separate probes are combined\nusing a configurable policy to determine if the database is healthy.\n\nNote that\nan initial health check is performed on each database when you create the client or when\nyou add a new database. This initial health check has a separate configuration from the\nperiodic health checks, so you can use a stricter policy here to ensure you don't add\na database that is already unhealthy.\n\n`MultiDbConfig` provides the following options to configure the health check behavior:\n\n| Option | Description |\n| --- | --- |\n| `health_check_interval` | Time interval between successive health checks (each of which may consist of multiple probes). Default is `5` seconds. |\n| `health_check_probes` | Number of separate probes performed during each health check. Default is `3`. |\n| `health_check_probes_delay` | Delay between probes during a health check. Default is `0.5` seconds. |\n| `health_check_timeout` | Timeout for the full health check operation (including all probes). |\n| `health_check_policy` | `HealthCheckPolicies` enum value to specify the policy for determining database health from the separate probes of a health check. The options are `HealthCheckPolicies.HEALTHY_ALL` (all probes must succeed), `HealthCheckPolicies.HEALTHY_ANY` (at least one probe must succeed), and `HealthCheckPolicies.HEALTHY_MAJORITY` (more than half the probes must succeed). The default policy is `HealthCheckPolicies.HEALTHY_MAJORITY`. |\n| `health_check` | Custom list of `HealthCheck` objects to specify how to perform each probe during a health check. This defaults to just the simple [`PingHealthCheck`](#pinghealthcheck-default). |\n| `initial_health_check_policy` | `InitialHealthCheck` enum value to specify the policy to use during the initial health check. The options are `InitialHealthCheck.ALL_AVAILABLE` (all probes must succeed), `InitialHealthCheck.ANY_AVAILABLE` (at least one probe must succeed), and `InitialHealthCheck.MAJORITY_AVAILABLE` (more than half the probes must succeed). The default policy is `InitialHealthCheck.ALL_AVAILABLE`. |\n\nThere are several strategies available for health checks that you can configure using the\n`MultiClusterClientConfig` builder. The sections below explain these strategies\nin more detail."},{"id":"pinghealthcheck-default","title":"`PingHealthCheck` (default)","role":"content","text":"The default strategy, `PingHealthCheck`, periodically sends a Redis\n[`PING`](https://redis.io/docs/latest/commands/ping) command\nand checks that it gives the expected response. Any unexpected response\nor exception indicates an unhealthy server. Although `PingHealthCheck` is\nvery simple, it is a good basic approach for most Redis deployments."},{"id":"lag-aware-health-check","title":"`LagAwareHealthCheck` (Redis Software only)","role":"content","text":"`LagAwareHealthCheck` is designed specifically for\nRedis Software [Active-Active](https://redis.io/docs/latest/operate/rs/databases/active-active)\ndeployments. It determines the health of the server by using the\n[REST API](https://redis.io/docs/latest/operate/rs/references/rest-api) to check the\nsynchronization lag between a specific database and the others in the Active-Active\nsetup. If the lag is within a specified tolerance, the server is considered healthy.\n\n`LagAwareHealthCheck` uses the `health_check_url` value for the endpoint\nto connect to the database's REST API, so you must specify this in\nthe `DatabaseConfig` for each endpoint:\n\n[code example]\n\nYou must also add a `LagAwareHealthCheck` instance to the `health_check` list in\nthe `MultiDbConfig` constructor:\n\n[code example]\n\nThe `LagAwareHealthCheck` constructor accepts the following options:\n\n| Option | Description |\n| --- | --- |\n| `rest_api_port` | Port number for Redis Software REST API (default is 9443). |\n| `lag_aware_tolerance` | Tolerable synchronization lag between databases in milliseconds (default is 100ms). |\n| `timeout` | REST API request timeout in seconds (default is 30 seconds). |\n| `auth_basic` | Tuple of (username, password) for basic authentication. |\n| `verify_tls` | Whether to verify TLS certificates (defaults to `True`). |\n| `ca_file` | Path to CA certificate file for TLS verification. |\n| `ca_path` | Path to CA certificates directory for TLS verification. |\n| `ca_data` | CA certificate data as string or bytes. |\n| `client_cert_file` | Path to client certificate file for mutual TLS. |\n| `client_key_file` | Path to client private key file for mutual TLS. |\n| `client_key_password` | Password for encrypted client private key |"},{"id":"custom-health-check-strategy","title":"Custom health check strategy","role":"content","text":"You can supply your own custom health check strategy by\nderiving a new class from the `AbstractHealthCheck` class.\nFor example, you might use this to integrate with external monitoring tools or\nto implement checks that are specific to your application. Add an\ninstance of your custom class to the `health_check` list in\nthe `MultiDbConfig` constructor, as with [`LagAwareHealthCheck`](#lag-aware-health-check).\nNote that health checks are executed in an asyncio event loop, so you \nmust implement the `check_health()` method as an async method.\n\nThe example below\nshows a simple custom strategy that sends a Redis [`ECHO`](https://redis.io/docs/latest/commands/echo)\ncommand and checks for the expected response.\n\n[code example]"},{"id":"managing-databases-at-runtime","title":"Managing databases at runtime","role":"content","text":"Although you will typically configure all databases during the\ninitial connection, you can also modify the configuration at runtime.\nYou can add and remove database endpoints, and update their weights:\n\n[code example]"},{"id":"manual-failback","title":"Manual failback","role":"content","text":"By default, the failback mechanism runs health checks on all servers in the weighted list and selects the highest-weighted server that is healthy. However, you can also use the `set_active_database()` method of `MultiDBClient` to select which database to use manually:\n\n[code example]\n\nNote that `set_active_database()` is thread-safe.\n\nIf you decide to implement manual failback, you will need a way for external systems to trigger this method in your application. For example, if your application exposes a REST API, you might consider creating a REST endpoint to call `set_active_database()`."},{"id":"pubsub-and-re-subscription","title":"Pub/Sub and re-subscription","role":"content","text":"`MultiDBClient` supports [Pub/Sub](https://redis.io/docs/latest/develop/pubsub)\nmessaging with automatic re-subscription to channels during failover.\nThis means you don't have to detect failovers and re-subscribe manually:\n\n- **Subscriber failover**: When the active database becomes unhealthy, the\n  subscriber automatically reconnects to the next available database and\n  re-subscribes to the same channels.\n- **Publisher failover**: The publisher switches to the next available\n  database and continues publishing to the same channels.\n\nFor reliable Pub/Sub delivery during failover, use `MultiDBClient` instances\nfor *both* publishers and subscribers. Create a Pub/Sub interface from the\nclient in the usual way using the `pubsub()` method, then subscribe to one or\nmore channels:\n\n[code example]\n\nRe-subscription happens transparently and is independent of any custom\nevent listeners you register (see [Failover callbacks](#failover-callbacks)).\n\n> [!NOTE]\n> Message loss can still occur if the failover events happen in\n> the reverse order, with the publisher failing over to the new database\n> before the subscriber. Messages published during this window may not reach\n> a subscriber that is still connected to the previous database."},{"id":"behavior-when-all-endpoints-are-unhealthy","title":"Behavior when all endpoints are unhealthy","role":"content","text":"In the extreme case where no endpoint is healthy, a command will throw a `TemporaryUnavailableException`.\nThis indicates that the client is periodically checking to see if any endpoint becomes healthy again. The number of\ntimes it will keep checking is configured by the `failover_attempts` option in `MultiDbConfig` and\nthe delay between attempts is configured by the `failover_delay` option (see [General failover configuration](#general-failover-configuration)). With the default settings, `failover_attempts` * `failover_delay` \ngives a period of 120 seconds to find a healthy endpoint.\n\nYou can still keep retrying commands after a `TemporaryUnavailableException` is thrown (for example,\nyou could add this exception to the `supported_errors` list in your `Retry` configuration, as described\nin [Retries](https://redis.io/docs/latest/develop/clients/redis-py/produsage#retries)). However, if the client exhausts\nall the available failover attempts before any endpoint becomes healthy again, commands will throw a `NoValidDatabaseException`. The client won't recover automatically from this situation, so you\nshould handle it by reconnecting with the `MultiDBClient` constructor after a suitable delay (see\n[Failover configuration](#failover-configuration) for a connection example)."},{"id":"troubleshooting","title":"Troubleshooting","role":"errors","text":"This section lists some common problems and their solutions."},{"id":"excessive-or-constant-health-check-failures","title":"Excessive or constant health check failures","role":"content","text":"If all health checks fail, you should first rule out authentication\nproblems with the Redis server and also make sure there are no persistent\nnetwork connectivity problems.\n\nIf you are using [`PingHealthCheck`](#pinghealthcheck-default) or a\n[custom health check strategy](#custom-health-check-strategy),\ncheck that the `socket_timeout` is not too low for your network conditions\n(see [Timeouts](https://redis.io/docs/latest/develop/clients/redis-py/produsage#timeouts) for more information).\n\nFor\n[`LagAwareHealthCheck`](#lag-aware-health-check), check that the `health_check_url`\nis set correctly for each endpoint. Note that health checks might be taking longer to\nexecute than you anticipated, so make sure your `timeout` setting is not too low."},{"id":"slow-failback-after-recovery","title":"Slow failback after recovery","role":"content","text":"If failback is too slow after a server recovers, you can try\nreducing the `health_check_interval` period and also reducing the `grace_period`\nbefore failback is attempted (see [Health check configuration](#health-check-configuration)\nfor more information about these options)."}],"examples":[{"id":"failover-configuration-ex0","language":"py","code":"from redis.multidb.client import MultiDBClient\nfrom redis.multidb.config import MultiDbConfig, DatabaseConfig\n\ndb_configs = [\n    DatabaseConfig(\n        client_kwargs={\"host\": \"redis-east.example.com\", \"port\": \"14000\"},\n        weight=1.0\n    ),\n    DatabaseConfig(\n        client_kwargs={\"host\": \"redis-west.example.com\", \"port\": \"14000\"},\n        weight=0.5\n    ),\n]\n\ncfg = MultiDbConfig(databases_config=db_configs)\nclient = MultiDBClient(cfg)","section_id":"failover-configuration"},{"id":"client-configuration-ex0","language":"py","code":"cfg = MultiDbConfig(\n    ...\n    client_class=redis.asyncio.client.Redis,\n    ...\n)","section_id":"client-configuration"},{"id":"retry-configuration-ex0","language":"py","code":"cfg = MultiDbConfig(\n    ...\n    # Retry failed commands up to three times using exponential backoff\n    # with jitter between attempts.\n    command_retry=Retry(\n        retries=3,\n        backoff=ExponentialWithJitterBackoff(base=1, cap=10),\n    ),\n    ...\n)","section_id":"retry-configuration"},{"id":"failover-callbacks-ex0","language":"py","code":"import logging\n\nfrom redis.multidb.client import MultiDBClient\nfrom redis.multidb.config import MultiDbConfig\n\nfrom redis.event import EventListenerInterface, EventDispatcher\nfrom redis.multidb.event import ActiveDatabaseChanged\n ...\n\nclass LogFailoverEventListener(EventListenerInterface):\n    def __init__(self, logger: logging.Logger):\n        self.logger = logger\n\n    def listen(self, event: ActiveDatabaseChanged):\n        self.logger.warning(\n            f\"Failover happened. Active database switched from {event.old_database} to {event.new_database}\"\n        )\n\nevent_dispatcher = EventDispatcher()\nlistener = LogFailoverEventListener(logging.getLogger(__name__))\n\n# Register custom listener\nevent_dispatcher.register_listeners(\n    {\n        ActiveDatabaseChanged: [listener],\n    }\n)\n\nconfig = MultiDbConfig(\n    event_dispatcher=event_dispatcher,\n    ...\n)\n\nclient = MultiDBClient(config)","section_id":"failover-callbacks"},{"id":"lag-aware-health-check-ex0","language":"py","code":"db_configs = [\n    DatabaseConfig(\n        client_kwargs={\"host\": \"redis-east.example.com\", \"port\": \"14000\"},\n        weight=1.0,\n        health_check_url=\"https://health.redis-east.example.com\"\n    ),\n    DatabaseConfig(\n        client_kwargs={\"host\": \"redis-west.example.com\", \"port\": \"14000\"},\n        weight=0.5,\n        health_check_url=\"https://health.redis-west.example.com\"\n    ),\n]","section_id":"lag-aware-health-check"},{"id":"lag-aware-health-check-ex1","language":"py","code":"cfg = MultiDbConfig(\n    databases_config=db_configs,\n    health_check=[LagAwareHealthCheck(\n        rest_api_port=9443,\n        lag_aware_tolerance=100,  # ms\n        verify_tls=True,\n        # auth_basic=(\"user\", \"pass\"),\n        # ca_file=\"/path/ca.pem\",\n        # client_cert_file=\"/path/cert.pem\",\n        # client_key_file=\"/path/key.pem\",\n    )],\n    ...\n)\n\nclient = MultiDBClient(cfg)","section_id":"lag-aware-health-check"},{"id":"custom-health-check-strategy-ex0","language":"py","code":"from redis.asyncio.multidb.healthcheck import AbstractHealthCheck, AsyncRedisClientT\n\n    class EchoHealthCheck(AbstractHealthCheck):\n        \"\"\"\n        Health check based on ECHO command.\n        \"\"\"\n\n        async def check_health(self, database, hc_client: AsyncRedisClientT) -> bool:\n            await connection.send_command(\"ECHO\", \"healthcheck\")\n            response = await connection.read_response()\n            return response in (b\"healthcheck\", \"healthcheck\")\n\ncfg = MultiDbConfig(\n    ...\n    health_check=[EchoHealthCheck(retry=Retry(retries=3))],\n    ...\n)\n\nclient = MultiDBClient(cfg)","section_id":"custom-health-check-strategy"},{"id":"managing-databases-at-runtime-ex0","language":"py","code":"from redis.multidb.client import MultiDBClient\nfrom redis.multidb.config import MultiDbConfig, DatabaseConfig\n\ncfg = MultiDbConfig(\n    databases_config = [\n        DatabaseConfig(\n            client_kwargs={\"host\": \"redis-east.example.com\", \"port\": \"14000\"},\n            weight=1.0\n        ),\n        DatabaseConfig(\n            client_kwargs={\"host\": \"redis-west.example.com\", \"port\": \"14000\"},\n            weight=0.5\n        ),\n    ]\n)\nclient = MultiDBClient(cfg)\n\n# Add a database programmatically. The `skip_initial_health_check`\n# parameter defaults to `True`, which avoids adding the database if it\n# is unhealthy, but you can override this by setting it to `False`\n# explicitly.\nother = DatabaseConfig(\n    client_kwargs={\"host\": \"redis-south.example.com\", \"port\": \"14000\"},\n    weight=0.5\n)\nclient.add_database(other, skip_initial_health_check=True)\n\n# Get a list of databases, sorted by weight.\ndatabases = client.get_databases()\n\n# Update the weights of each database whose weight is less than 1.\nfor db in databases:\n    if db.weight < 1.0:\n        client.update_database_weight(db, 0.9)\n\n# Remove a database from the failover set.\nclient.remove_database(databases[0])","section_id":"managing-databases-at-runtime"},{"id":"manual-failback-ex0","language":"py","code":"# Manually set `other` as the active database.\nclient.set_active_database(other)","section_id":"manual-failback"},{"id":"pubsub-and-re-subscription-ex0","language":"py","code":"pubsub = client.pubsub()\npubsub.subscribe(\"news\", \"alerts\")\n\n# If a failover happens here, the subscriptions are automatically\n# re-established on the new active database.\nmsg = pubsub.get_message(timeout=1.0)\nif msg:\n    print(msg)","section_id":"pubsub-and-re-subscription"}]}
{"schema_version":2,"id":"develop/clients/redis-py","title":"redis-py guide (Python)","url":"https://redis.io/docs/latest/develop/clients/redis-py/","summary":"Connect your Python application to a Redis database","aliases":["/develop/connect/clients/python/redis-py","/develop/connect/clients/python/","/develop/clients/python/","/clients/python/","/connect/clients/redis-py/","/clients/redis-py/","/develop/clients/python/redis-py/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-05T16:11:44+01:00","children":[{"id":"develop/clients/redis-py/connect","summary":"Connect your Python application to a Redis database","title":"Connect to the server","url":"https://redis.io/docs/latest/develop/clients/redis-py/connect/"},{"id":"develop/clients/redis-py/async","summary":"Use redis-py with asyncio for non-blocking Redis access","title":"Asynchronous operations with redis-py","url":"https://redis.io/docs/latest/develop/clients/redis-py/async/"},{"id":"develop/clients/redis-py/amr","summary":"Learn how to authenticate to an Azure Managed Redis (AMR) database","title":"Connect to Azure Managed Redis","url":"https://redis.io/docs/latest/develop/clients/redis-py/amr/"},{"id":"develop/clients/redis-py/queryjson","summary":"Learn how to use Redis Search with JSON and hash documents.","title":"Index and query documents","url":"https://redis.io/docs/latest/develop/clients/redis-py/queryjson/"},{"id":"develop/clients/redis-py/vecsearch","summary":"Learn how to index and query vector embeddings with Redis","title":"Index and query vectors","url":"https://redis.io/docs/latest/develop/clients/redis-py/vecsearch/"},{"id":"develop/clients/redis-py/vecsets","summary":"Index and query embeddings with Redis vector sets","title":"Vector set embeddings","url":"https://redis.io/docs/latest/develop/clients/redis-py/vecsets/"},{"id":"develop/clients/redis-py/prob","summary":"Learn how to use approximate calculations with Redis.","title":"Probabilistic data types","url":"https://redis.io/docs/latest/develop/clients/redis-py/prob/"},{"id":"develop/clients/redis-py/transpipe","summary":"Learn how to use Redis pipelines and transactions","title":"Pipelines and transactions","url":"https://redis.io/docs/latest/develop/clients/redis-py/transpipe/"},{"id":"develop/clients/redis-py/scaniter","summary":"Iterate through results from `SCAN`, `HSCAN`, etc.","title":"Scan iteration","url":"https://redis.io/docs/latest/develop/clients/redis-py/scaniter/"},{"id":"develop/clients/redis-py/error-handling","summary":"Learn how to handle errors when using redis-py","title":"Error handling","url":"https://redis.io/docs/latest/develop/clients/redis-py/error-handling/"},{"id":"develop/clients/redis-py/failover","summary":"Improve reliability using the failover features of redis-py.","title":"Client-side geographic failover","url":"https://redis.io/docs/latest/develop/clients/redis-py/failover/"},{"id":"develop/clients/redis-py/produsage","summary":"Get your `redis-py` app ready for production","title":"Production usage","url":"https://redis.io/docs/latest/develop/clients/redis-py/produsage/"},{"id":"develop/clients/redis-py/observability","summary":"Monitor your client's activity for optimization and debugging.","title":"Observability","url":"https://redis.io/docs/latest/develop/clients/redis-py/observability/"}],"page_type":"content","content_hash":"4d858bad12ea0becd46e98ab43fba8ed3a9a8c2129d8bfc7dd23232b300f4f17","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[redis-py](https://github.com/redis/redis-py) is the Python client for Redis. \nThe sections below explain how to install `redis-py` and connect your application\nto a Redis database.\n\n`redis-py` requires a running Redis server. See [here](https://redis.io/docs/latest/operate/oss_and_stack/install) for Redis Open Source installation instructions.\n\nYou can also access Redis with an object-mapping client interface. See\n[RedisOM for Python](https://redis.io/docs/latest/integrate/redisom-for-python)\nfor more information."},{"id":"install","title":"Install","role":"setup","text":"To install `redis-py`, enter:\n\n[code example]\n\nFor faster performance, install Redis with [`hiredis`](https://github.com/redis/hiredis) support. This provides a compiled response parser, and for most cases requires zero code changes. By default, if `hiredis` >= 1.0 is available, `redis-py` attempts to use it for response parsing.\n\n> [!NOTE]\n> The Python `distutils` packaging scheme is no longer part of Python 3.12 and greater. If you're having difficulties getting `redis-py` installed in a Python 3.12 environment, consider updating to a recent release of `redis-py`.\n\n[code example]"},{"id":"connect-and-test","title":"Connect and test","role":"content","text":"Connect to localhost on port 6379, set a value in Redis, and retrieve it. All responses are returned as bytes in Python. To receive decoded strings, set `decode_responses=True`. For more connection options, see [these examples](https://redis.readthedocs.io/en/stable/examples.html).\n\nConnect to a Redis server and establish a client connection\n\n[code example]\n\n\n\nStore and retrieve a simple string.\n\nSet and retrieve string values using SET and GET commands\n\n[code example]\n\n\n\nStore and retrieve a dict.\n\nStore and retrieve hash data structures using HSET and HGETALL\n\n[code example]\n\n\n\nClose the connection when you're done.\n\nClose the Redis connection\n\n[code example]"},{"id":"more-information","title":"More information","role":"content","text":"The [`redis-py`](https://redis.readthedocs.io/en/stable/index.html) website\nhas a [command reference](https://redis.readthedocs.io/en/stable/commands.html)\nand some [tutorials](https://redis.readthedocs.io/en/stable/examples.html) for\nvarious tasks. There are also some examples in the\n[GitHub repository](https://github.com/redis/redis-py) for `redis-py`.\n \nSee also the other pages in this section for more information and examples:"}],"examples":[{"id":"install-ex0","language":"bash","code":"pip install redis","section_id":"install"},{"id":"install-ex1","language":"bash","code":"pip install redis[hiredis]","section_id":"install"},{"id":"connect-and-test-ex0","language":"python","code":"import redis\nr = redis.Redis(host='localhost', port=6379, decode_responses=True)","section_id":"connect-and-test"},{"id":"connect-and-test-ex1","language":"python","code":"r.set('foo', 'bar')\n# True\nr.get('foo')\n# bar","section_id":"connect-and-test"},{"id":"connect-and-test-ex2","language":"python","code":"r.hset('user-session:123', mapping={\n    'name': 'John',\n    \"surname\": 'Smith',\n    \"company\": 'Redis',\n    \"age\": 29\n})\n# True\n\nr.hgetall('user-session:123')\n# {'surname': 'Smith', 'name': 'John', 'company': 'Redis', 'age': '29'}","section_id":"connect-and-test"},{"id":"connect-and-test-ex3","language":"python","code":"r.close()","section_id":"connect-and-test"}]}
{"schema_version":2,"id":"develop/clients/redis-py/observability","title":"Observability","url":"https://redis.io/docs/latest/develop/clients/redis-py/observability/","summary":"Monitor your client's activity for optimization and debugging.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-08-27T15:55:18+01:00","page_type":"content","content_hash":"c48b4106dcf6ab7a0f5b08437f3f0da50b952b1e4de4c1faffde40a625d828da","sections":[{"id":"installation","title":"Installation","role":"setup","text":"Install OTel support for `redis-py` with the following command:\n\n[code example]"},{"id":"import","title":"Import","role":"content","text":"Start by importing the required OTel and Redis modules:\n\nFoundational: Import required libraries for Redis observability, OpenTelemetry metrics, and Redis operations\n\n**Difficulty:** Beginner\n\n**Available in:** Python\n\n##### Python\n\n[code example]"},{"id":"configure-the-meter-provider","title":"Configure the meter provider","role":"configuration","text":"Otel uses a [Meter provider](https://opentelemetry.io/docs/concepts/signals/metrics/#meter-provider)\nto create the objects that collect the metric information. The example below\nconfigures a meter provider to export metrics to a local Grafana instance\nevery 10 seconds, but see the [OpenTelemetry Python docs](https://opentelemetry.io/docs/languages/python/)\nto learn more about other export options.\n\nFoundational: Configure a meter provider to export metrics to a local Grafana instance every 10 seconds\n\n**Difficulty:** Beginner\n\n**Available in:** Python\n\n##### Python\n\n[code example]"},{"id":"configure-the-redis-client","title":"Configure the Redis client","role":"configuration","text":"You configure the client library for OTel only once per application. This will\nenable OTel for all Redis connections you create. The example below shows the\noptions you can pass to the observability instance via the `OTelConfig` object\nduring initialization.\n\nFoundational: Configure Redis observability with a list of metric groups and optional command filtering and privacy controls\n\n**Difficulty:** Beginner\n\n**Available in:** Python\n\n##### Python\n\n[code example]\n\n\n\nThe available options for `OTelConfig` are described in the table below:\n\n| Option | Type | Description |\n| --- | --- | --- |\n| `metric_groups` | `List[MetricGroup]` | List of metric groups to enable. By default, only `CONNECTION_BASIC` and `RESILIENCY` are enabled. See [Redis metric groups](https://redis.io/docs/latest/develop/clients/observability#redis-metric-groups) for a list of available groups. |\n| `include_commands` | `List[str]` | List of Redis commands to track. If set, only these commands will be tracked. Note that you should use the Redis command name rather than the Python method name where the two differ. |\n| `exclude_commands` | `List[str]` | List of Redis commands to exclude from tracking. If set, all commands except these will be tracked. Note that you should use the Redis command name rather than the Python method name where the two differ. |\n| `hide_pubsub_channel_names` | `bool` | If true, channel names in pub/sub metrics will be hidden. |\n| `hide_stream_names` | `bool` | If true, stream names in streaming metrics will be hidden. |\n| `buckets_operation_duration` | `List[float]` | List of bucket boundaries for the [`operation.duration`](https://redis.io/docs/latest/develop/clients/observability#metric-db.client.operation.duration) histogram (see [Custom histogram buckets](#custom-histogram-buckets) below). |\n| `buckets_stream_processing_duration` | `List[float]` | List of bucket boundaries for the [`stream.lag`](https://redis.io/docs/latest/develop/clients/observability#metric-redis.client.stream.lag) histogram (see [Custom histogram buckets](#custom-histogram-buckets) below). |\n| `buckets_connection_create_time` | `List[float]` | List of bucket boundaries for the [`connection.create.time`](https://redis.io/docs/latest/develop/clients/observability#metric-db.client.connection.create_time) histogram (see [Custom histogram buckets](#custom-histogram-buckets) below). |\n| `buckets_connection_wait_time` | `List[float]` | List of bucket boundaries for the [`connection.wait.time`](https://redis.io/docs/latest/develop/clients/observability#metric-db.client.connection.wait_time) histogram (see [Custom histogram buckets](#custom-histogram-buckets) below). |"},{"id":"custom-histogram-buckets","title":"Custom histogram buckets","role":"content","text":"For the histogram metrics, a reasonable default set of buckets is defined, but\nyou can customize the bucket boundaries to suit your needs (the buckets are the\nranges of data values counted for each bar of the histogram). Pass an increasing\nlist of float values to the `buckets_xxx` options when you create the `OTelConfig`\nobject. The first and last values in the list are the lower and upper bounds of the\nhistogram, respectively, and the values in between define the bucket boundaries."},{"id":"use-redis","title":"Use Redis","role":"content","text":"Once you have configured the client, all Redis connections you create will be\nautomatically instrumented and the collected metrics will be exported to your\nconfigured destination.\n\nThe example below shows the simplest Redis connection and a few commands,\nbut see the\n[observability demonstration repository](https://github.com/redis-developer/redis-client-observability)\nfor an example that calls a variety of commands in a more realistic way.\n\nFoundational: Use Redis with automatic instrumentation\n\n**Difficulty:** Beginner\n\n**Available in:** Python\n\n##### Python\n\n[code example]"},{"id":"shutdown","title":"Shutdown","role":"content","text":"When your application exits, you should call the `shutdown()` method to ensure\nthat all pending metrics are exported.\n\nFoundational: Shutdown Redis observability\n\n**Difficulty:** Beginner\n\n**Available in:** Python\n\n##### Python\n\n[code example]"}],"examples":[{"id":"installation-ex0","language":"bash","code":"pip install redis[otel]","section_id":"installation"},{"id":"import-ex0","language":"python","code":"# OpenTelemetry metrics API\nfrom opentelemetry import metrics\nfrom opentelemetry.sdk.metrics import MeterProvider\nfrom opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader\nfrom opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter\n\n# Redis observability API\nfrom redis.observability.providers import get_observability_instance\nfrom redis.observability.config import OTelConfig, MetricGroup\n\n# Redis client\nimport redis","section_id":"import"},{"id":"configure-the-meter-provider-ex0","language":"python","code":"exporter = OTLPMetricExporter(endpoint=\"http://localhost:4318/v1/metrics\")\nreader = PeriodicExportingMetricReader(exporter=exporter, export_interval_millis=10000)\nprovider = MeterProvider(metric_readers=[reader])\nmetrics.set_meter_provider(provider)","section_id":"configure-the-meter-provider"},{"id":"configure-the-redis-client-ex0","language":"python","code":"otel = get_observability_instance()\notel.init(OTelConfig(\n    # Metric groups to enable (default: CONNECTION_BASIC | RESILIENCY)\n    metric_groups=[\n        MetricGroup.CONNECTION_BASIC,    # Connection creation time, relaxed timeout\n        MetricGroup.CONNECTION_ADVANCED, # Connection wait time, timeouts, closed connections\n        MetricGroup.COMMAND,             # Command execution duration\n        MetricGroup.RESILIENCY,          # Error counts, maintenance notifications\n        MetricGroup.PUBSUB,              # PubSub message counts\n        MetricGroup.STREAMING,           # Stream message lag\n        MetricGroup.CSC,                 # Client Side Caching metrics\n    ],\n\n    # Filter which commands to track\n    include_commands=['GET', 'SET', 'HGET'],  # Only track these commands\n    # OR\n    exclude_commands=['DEBUG', 'SLOWLOG'],    # Track all except these\n\n    # Privacy controls\n    hide_pubsub_channel_names=True,  # Hide channel names in PubSub metrics\n    hide_stream_names=True,          # Hide stream names in streaming metrics\n\n    # Custom histogram buckets\n    buckets_operation_duration=[\n        0.0001, 0.00025, 0.0005, 0.001, 0.0025, 0.005, 0.01, 0.025, 0.05, 0.1,\n        0.25, 0.5, 1, 2.5,\n    ],\n    buckets_stream_processing_duration=[\n        0.0001, 0.00025, 0.0005, 0.001, 0.0025, 0.005, 0.01, 0.025, 0.05, 0.1,\n        0.25, 0.5, 1, 2.5,\n    ],\n    buckets_connection_create_time=[\n        0.0001, 0.00025, 0.0005, 0.001, 0.0025, 0.005, 0.01, 0.025, 0.05, 0.1,\n        0.25, 0.5, 1, 2.5,\n    ],\n    buckets_connection_wait_time=[\n        0.0001, 0.00025, 0.0005, 0.001, 0.0025, 0.005, 0.01, 0.025, 0.05, 0.1,\n        0.25, 0.5, 1, 2.5,\n    ],\n))","section_id":"configure-the-redis-client"},{"id":"use-redis-ex0","language":"python","code":"r = redis.Redis(host='localhost', port=6379)\nr.set('key', 'value')  # Metrics collected automatically\nr.get('key')","section_id":"use-redis"},{"id":"shutdown-ex0","language":"python","code":"otel.shutdown()","section_id":"shutdown"}]}
{"schema_version":2,"id":"develop/clients/redis-py/prob","title":"Probabilistic data types","url":"https://redis.io/docs/latest/develop/clients/redis-py/prob/","summary":"Learn how to use approximate calculations with Redis.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-08-28T19:54:23+05:30","page_type":"content","content_hash":"52bf74f4bb457b567c92a21deb302fdcb710d1f4ffb9245eb62f2e1a417fea3e","sections":[{"id":"set-operations","title":"Set operations","role":"content","text":"Redis supports the following approximate set operations:\n\n-   [Membership](#set-membership): The\n    [Bloom filter](https://redis.io/docs/latest/develop/data-types/probabilistic/bloom-filter) and\n    [Cuckoo filter](https://redis.io/docs/latest/develop/data-types/probabilistic/cuckoo-filter)\n    data types let you track whether or not a given item is a member of a set.\n-   [Cardinality](#set-cardinality): The\n    [HyperLogLog](https://redis.io/docs/latest/develop/data-types/probabilistic/hyperloglogs)\n    data type gives you an approximate value for the number of items in a set, also\n    known as the *cardinality* of the set.\n\nThe sections below describe these operations in more detail."},{"id":"set-membership","title":"Set membership","role":"content","text":"[Bloom filter](https://redis.io/docs/latest/develop/data-types/probabilistic/bloom-filter) and\n[Cuckoo filter](https://redis.io/docs/latest/develop/data-types/probabilistic/cuckoo-filter)\nobjects provide a set membership operation that lets you track whether or not a\nparticular item has been added to a set. These two types provide different\ntrade-offs for memory usage and speed, so you can select the best one for your\nuse case. Note that for both types, there is an asymmetry between presence and\nabsence of items in the set. If an item is reported as absent, then it is definitely\nabsent, but if it is reported as present, then there is a small chance it may really be\nabsent.\n\nInstead of storing strings directly, like a [set](https://redis.io/docs/latest/develop/data-types/sets),\na Bloom filter records the presence or absence of the\n[hash value](https://en.wikipedia.org/wiki/Hash_function) of a string.\nThis gives a very compact representation of the\nset's membership with a fixed memory size, regardless of how many items you\nadd. The following example adds some names to a Bloom filter representing\na list of users and checks for the presence or absence of users in the list.\nNote that you must use the `bf()` method to access the Bloom filter commands.\n\nFoundational: Use Bloom filters for memory-efficient set membership testing with false positive possibility\n\n**Difficulty:** Beginner\n\n**Available in:** Python\n\n##### Python\n\n[code example]\n\n\n\nA Cuckoo filter has similar features to a Bloom filter, but also supports\na deletion operation to remove hashes from a set, as shown in the example\nbelow. Note that you must use the `cf()` method to access the Cuckoo filter\ncommands.\n\nFoundational: Use Cuckoo filters for set membership testing with deletion support and faster lookups than Bloom filters\n\n**Difficulty:** Beginner\n\n**Available in:** Python\n\n##### Python\n\n[code example]\n\n\n\nWhich of these two data types you choose depends on your use case.\nBloom filters are generally faster than Cuckoo filters when adding new items,\nand also have better memory usage. Cuckoo filters are generally faster\nat checking membership and also support the delete operation. See the\n[Bloom filter](https://redis.io/docs/latest/develop/data-types/probabilistic/bloom-filter) and\n[Cuckoo filter](https://redis.io/docs/latest/develop/data-types/probabilistic/cuckoo-filter)\nreference pages for more information and comparison between the two types."},{"id":"set-cardinality","title":"Set cardinality","role":"content","text":"A [HyperLogLog](https://redis.io/docs/latest/develop/data-types/probabilistic/hyperloglogs)\nobject calculates the cardinality of a set. As you add\nitems, the HyperLogLog tracks the number of distinct set members but\ndoesn't let you retrieve them or query which items have been added.\nYou can also merge two or more HyperLogLogs to find the cardinality of the\n[union](https://en.wikipedia.org/wiki/Union_(set_theory)) of the sets they\nrepresent.\n\nFoundational: Estimate set cardinality with HyperLogLog for memory-efficient counting of distinct items\n\n**Difficulty:** Beginner\n\n**Available in:** Python\n\n##### Python\n\n[code example]\n\n\n\nThe main benefit that HyperLogLogs offer is their very low\nmemory usage. They can count up to 2^64 items with less than\n1% standard error using a maximum 12KB of memory. This makes\nthem very useful for counting things like the total of distinct\nIP addresses that access a website or the total of distinct\nbank card numbers that make purchases within a day."},{"id":"statistics","title":"Statistics","role":"content","text":"Redis supports several approximate statistical calculations\non numeric data sets:\n\n-   [Frequency](#frequency): The\n    [Count-min sketch](https://redis.io/docs/latest/develop/data-types/probabilistic/count-min-sketch)\n    data type lets you find the approximate frequency of a labeled item in a data stream.\n-   [Quantiles](#quantiles): The\n    [t-digest](https://redis.io/docs/latest/develop/data-types/probabilistic/t-digest)\n    data type estimates the quantile of a query value in a data stream.\n-   [Ranking](#ranking): The\n    [Top-K](https://redis.io/docs/latest/develop/data-types/probabilistic/top-k) data type\n    estimates the ranking of labeled items by frequency in a data stream.\n\nThe sections below describe these operations in more detail."},{"id":"frequency","title":"Frequency","role":"content","text":"A [Count-min sketch](https://redis.io/docs/latest/develop/data-types/probabilistic/count-min-sketch)\n(CMS) object keeps count of a set of related items represented by\nstring labels. The count is approximate, but you can specify\nhow close you want to keep the count to the true value (as a fraction)\nand the acceptable probability of failing to keep it in this\ndesired range. For example, you can request that the count should\nstay within 0.1% of the true value and have a 0.05% probability\nof going outside this limit. The example below shows how to create\na Count-min sketch object, add data to it, and then query it.\nNote that you must use the `cms()` method to access the Count-min\nsketch commands.\n\nFoundational: Track approximate item frequencies with Count-min sketch for memory-efficient statistics on data streams\n\n**Difficulty:** Intermediate\n\n**Available in:** Python\n\n##### Python\n\n[code example]\n\n\n\nThe advantage of using a CMS over keeping an exact count with a\n[sorted set](https://redis.io/docs/latest/develop/data-types/sorted-sets)\nis that a CMS has very low and fixed memory usage, even for\nlarge numbers of items. Use CMS objects to keep daily counts of\nitems sold, accesses to individual web pages on your site, and\nother similar statistics."},{"id":"quantiles","title":"Quantiles","role":"content","text":"A [quantile](https://en.wikipedia.org/wiki/Quantile) is the value\nbelow which a certain fraction of samples lie. For example, with\na set of measurements of people's heights, the quantile of 0.75 is\nthe value of height below which 75% of all people's heights lie.\n[Percentiles](https://en.wikipedia.org/wiki/Percentile) are equivalent\nto quantiles, except that the fraction is expressed as a percentage.\n\nA [t-digest](https://redis.io/docs/latest/develop/data-types/probabilistic/t-digest)\nobject can estimate quantiles from a set of values added to it\nwithout having to store each value in the set explicitly. This can\nsave a lot of memory when you have a large number of samples.\n\nThe example below shows how to add data samples to a t-digest\nobject and obtain some basic statistics, such as the minimum and\nmaximum values, the quantile of 0.75, and the\n[cumulative distribution function](https://en.wikipedia.org/wiki/Cumulative_distribution_function)\n(CDF), which is effectively the inverse of the quantile function. It also\nshows how to merge two or more t-digest objects to query the combined\ndata set. Note that you must use the `tdigest()` method to access the\nt-digest commands.\n\nFoundational: Estimate quantiles and percentiles with t-digest for memory-efficient statistical analysis of large datasets\n\n**Difficulty:** Intermediate\n\n**Available in:** Python\n\n##### Python\n\n[code example]\n\n\n\nA t-digest object also supports several other related commands, such\nas querying by rank. See the\n[t-digest](https://redis.io/docs/latest/develop/data-types/probabilistic/t-digest)\nreference for more information."},{"id":"ranking","title":"Ranking","role":"content","text":"A [Top-K](https://redis.io/docs/latest/develop/data-types/probabilistic/top-k)\nobject estimates the rankings of different labeled items in a data\nstream according to frequency. For example, you could use this to\ntrack the top ten most frequently-accessed pages on a website, or the\ntop five most popular items sold.\n\nThe example below adds several different items to a Top-K object\nthat tracks the top three items (this is the second parameter to\nthe `topk().reserve()` method). It also shows how to list the\ntop *k* items and query whether or not a given item is in the\nlist. Note that you must use the `topk()` method to access the\nTop-K commands.\n\nFoundational: Track top K most frequent items in a data stream with Top-K for efficient ranking without storing all items\n\n**Difficulty:** Intermediate\n\n**Available in:** Python\n\n##### Python\n\n[code example]"}],"examples":[{"id":"set-membership-ex0","language":"python","code":"res1 = r.bf().madd(\"recorded_users\", \"andy\", \"cameron\", \"david\", \"michelle\")\nprint(res1)  # >>> [1, 1, 1, 1]\n\nres2 = r.bf().exists(\"recorded_users\", \"cameron\")\nprint(res2)  # >>> 1\n\nres3 = r.bf().exists(\"recorded_users\", \"kaitlyn\")\nprint(res3)  # >>> 0","section_id":"set-membership"},{"id":"set-membership-ex1","language":"python","code":"res4 = r.cf().add(\"other_users\", \"paolo\")\nprint(res4)  # >>> 1\n\nres5 = r.cf().add(\"other_users\", \"kaitlyn\")\nprint(res5)  # >>> 1\n\nres6 = r.cf().add(\"other_users\", \"rachel\")\nprint(res6)  # >>> 1\n\nres7 = r.cf().mexists(\"other_users\", \"paolo\", \"rachel\", \"andy\")\nprint(res7)  # >>> [1, 1, 0]\n\nres8 = r.cf().delete(\"other_users\", \"paolo\")\nprint(res8)  # >>> 1\n\nres9 = r.cf().exists(\"other_users\", \"paolo\")\nprint(res9)  # >>> 0","section_id":"set-membership"},{"id":"set-cardinality-ex0","language":"python","code":"res10 = r.pfadd(\"group:1\", \"andy\", \"cameron\", \"david\")\nprint(res10)  # >>> 1\n\nres11 = r.pfcount(\"group:1\")\nprint(res11)  # >>> 3\n\nres12 = r.pfadd(\"group:2\", \"kaitlyn\", \"michelle\", \"paolo\", \"rachel\")\nprint(res12)  # >>> 1\n\nres13 = r.pfcount(\"group:2\")\nprint(res13)  # >>> 4\n\nres14 = r.pfmerge(\"both_groups\", \"group:1\", \"group:2\")\nprint(res14)  # >>> True\n\nres15 = r.pfcount(\"both_groups\")\nprint(res15)  # >>> 7","section_id":"set-cardinality"},{"id":"frequency-ex0","language":"python","code":"# Specify that you want to keep the counts within 0.01\n# (1%) of the true value with a 0.005 (0.5%) chance\n# of going outside this limit.\nres16 = r.cms().initbyprob(\"items_sold\", 0.01, 0.005)\nprint(res16)  # >>> True\n\n# The parameters for `incrby()` are two lists. The count\n# for each item in the first list is incremented by the\n# value at the same index in the second list.\nres17 = r.cms().incrby(\n    \"items_sold\",\n    [\"bread\", \"tea\", \"coffee\", \"beer\"],  # Items sold\n    [300, 200, 200, 100]\n)\nprint(res17)  # >>> [300, 200, 200, 100]\n\nres18 = r.cms().incrby(\n    \"items_sold\",\n    [\"bread\", \"coffee\"],\n    [100, 150]\n)\nprint(res18)  # >>> [400, 350]\n\nres19 = r.cms().query(\"items_sold\", \"bread\", \"tea\", \"coffee\", \"beer\")\nprint(res19)  # >>> [400, 200, 350, 100]","section_id":"frequency"},{"id":"quantiles-ex0","language":"python","code":"res20 = r.tdigest().create(\"male_heights\")\nprint(res20)  # >>> True\n\nres21 = r.tdigest().add(\n    \"male_heights\",\n    [175.5, 181, 160.8, 152, 177, 196, 164]\n)\nprint(res21)  # >>> OK\n\nres22 = r.tdigest().min(\"male_heights\")\nprint(res22)  # >>> 152.0\n\nres23 = r.tdigest().max(\"male_heights\")\nprint(res23)  # >>> 196.0\n\nres24 = r.tdigest().quantile(\"male_heights\", 0.75)\nprint(res24)  # >>> 181\n\n# Note that the CDF value for 181 is not exactly\n# 0.75. Both values are estimates.\nres25 = r.tdigest().cdf(\"male_heights\", 181)\nprint(res25)  # >>> [0.7857142857142857]\n\nres26 = r.tdigest().create(\"female_heights\")\nprint(res26)  # >>> True\n\nres27 = r.tdigest().add(\n    \"female_heights\",\n    [155.5, 161, 168.5, 170, 157.5, 163, 171]\n)\nprint(res27)  # >>> OK\n\nres28 = r.tdigest().quantile(\"female_heights\", 0.75)\nprint(res28)  # >>> [170]\n\nres29 = r.tdigest().merge(\n    \"all_heights\", 2, \"male_heights\", \"female_heights\"\n)\nprint(res29)  # >>> OK\n\nres30 = r.tdigest().quantile(\"all_heights\", 0.75)\nprint(res30)  # >>> [175.5]","section_id":"quantiles"},{"id":"ranking-ex0","language":"python","code":"# The `reserve()` method creates the Top-K object with\n# the given key. The parameters are the number of items\n# in the ranking and values for `width`, `depth`, and\n# `decay`, described in the Top-K reference page.\nres31 = r.topk().reserve(\"top_3_songs\", 3, 7, 8, 0.9)\nprint(res31)  # >>> True\n\n# The parameters for `incrby()` are two lists. The count\n# for each item in the first list is incremented by the\n# value at the same index in the second list.\nres32 = r.topk().incrby(\n    \"top_3_songs\",\n    [\n        \"Starfish Trooper\",\n        \"Only one more time\",\n        \"Rock me, Handel\",\n        \"How will anyone know?\",\n        \"Average lover\",\n        \"Road to everywhere\"\n    ],\n    [\n        3000,\n        1850,\n        1325,\n        3890,\n        4098,\n        770\n    ]\n)\nprint(res32)\n# >>> [None, None, None, 'Rock me, Handel', 'Only one more time', None]\n\nres33 = r.topk().list(\"top_3_songs\")\nprint(res33)\n# >>> ['Average lover', 'How will anyone know?', 'Starfish Trooper']\n\nres34 = r.topk().query(\n    \"top_3_songs\", \"Starfish Trooper\", \"Road to everywhere\"\n)\nprint(res34)  # >>> [1, 0]","section_id":"ranking"}]}
{"schema_version":2,"id":"develop/clients/redis-py/produsage","title":"Production usage","url":"https://redis.io/docs/latest/develop/clients/redis-py/produsage/","summary":"Get your `redis-py` app ready for production","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-12T10:32:09+01:00","page_type":"content","content_hash":"5fae3a152d02c3172d0b9b44bf7bce7cf2df1b0506289bc4b2e9435ef33ae72d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This guide offers recommendations to get the best reliability and\nperformance in your production environment."},{"id":"checklist","title":"Checklist","role":"content","text":"Each item in the checklist below links to the section\nfor a recommendation. Use the checklist icons to record your\nprogress in implementing the recommendations.\n\n[code example]"},{"id":"recommendations","title":"Recommendations","role":"content","text":"The sections below offer recommendations for your production environment. Some\nof them may not apply to your particular use case."},{"id":"client-side-caching","title":"Client-side caching","role":"content","text":"[Client-side caching](https://redis.io/docs/latest/develop/clients/client-side-caching)\ninvolves storing the results from read-only commands in a local cache. If the\nsame command is executed again later, the results can be obtained from the cache,\nwithout contacting the server. This improves command execution time on the client,\nwhile also reducing network traffic and server load. See\n[Connect using client-side caching](https://redis.io/docs/latest/develop/clients/redis-py/connect#connect-using-client-side-caching)\nfor more information and example code."},{"id":"retries","title":"Retries","role":"content","text":"Redis connections and commands can often fail due to transient problems,\nsuch as temporary network outages or timeouts. When this happens,\nthe operation will generally succeed after a few attempts, despite\nfailing the first time.\n\n`redis-py` can retry commands automatically when\nerrors occur. From version 6.0.0 onwards, the default behavior is to\nattempt a failed command three times.\nThe timing between successive attempts is calculated using\n[exponential backoff](https://en.wikipedia.org/wiki/Exponential_backoff)\nwith some random \"jitter\" added to avoid two or more connections retrying\ncommands in sync with each other.\n\nYou can override the default behavior using an instance of the `Retry` class to\nspecify the number of times to retry after a failure along with your\nown choice of backoff strategy.\nPass the `Retry` object in the `retry` parameter when you connect.\nFor example, the connection in the code below uses an exponential backoff strategy\n(without jitter) that will make eight repeated attempts after a failure:\n\n[code example]\n\nA retry is triggered when a command throws any exception\nlisted in the `supported_errors` attribute of the `Retry` class.\nBy default, the list only includes `ConnectionError` and `TimeoutError`,\nbut you can set your own choice of exceptions when you create the\ninstance:\n\n[code example]\n\nYou can also add extra exceptions to the default list using the `retry_on_error`\nparameter when you connect:\n\n[code example]\n\nFor a connection to a Redis cluster, you can specify a `retry` instance,\nbut the list of exceptions is not configurable and is always set\nto `TimeoutError`, `ConnectionError`, and `ClusterDownError`."},{"id":"health-checks","title":"Health checks","role":"content","text":"If your code doesn't access the Redis server continuously then it\nmight be useful to make a \"health check\" periodically (perhaps once\nevery few seconds) to verify that the connection is working.\nSet the `health_check_interval` parameter during\na connection (with either `Redis` or `ConnectionPool`) to specify\nan integer number of seconds. If the connection remains idle for\nlonger than this interval, it will automatically issue a\n[`PING`](https://redis.io/docs/latest/commands/ping) command and check the\nresponse before continuing with any client commands.\n\n[code example]\n\nHealth checks help to detect problems as soon as possible without\nwaiting for a user to report them. Note that health checks, like\nother commands, will be [retried](#retries) using the strategy\nthat you specified for the connection."},{"id":"exception-handling","title":"Exception handling","role":"errors","text":"Redis handles many errors using return values from commands, but there\nare also situations where exceptions can be thrown. In production code,\nyou should handle exceptions wherever they can occur. \n\nImport the exceptions you need to check from the `redis.exceptions`\nmodule. The list below describes some of the most common exceptions.\n\n- `ConnectionError`: Thrown when a connection attempt fails\n  (for example, when connection parameters are invalid or the server\n  is unavailable) and sometimes when a [health check](#health-checks)\n  fails. There is also a subclass, `AuthenticationError`, specifically\n  for authentication failures.\n- `ResponseError`: Thrown when you attempt an operation that has no valid\n  response. Examples include executing a command on the wrong type of key\n  (as when you try an\n  ['LPUSH'](https://redis.io/docs/latest/develop/data-types/lists#automatic-creation-and-removal-of-keys)\n  command on a string key), creating an\n  [index](https://redis.io/docs/latest/develop/ai/search-and-query/indexing)\n  with a name that already exists, and using an invalid ID for a\n  [stream entry](https://redis.io/docs/latest/develop/data-types/streams#entry-ids).\n- `TimeoutError`: Thrown when a timeout persistently happens for a command,\n  despite any [retries](#retries).\n- `WatchError`: Thrown when a\n  [watched key](https://redis.io/docs/latest/develop/clients/redis-py/transpipe#watch-keys-for-changes) is\n  modified during a transaction."},{"id":"timeouts","title":"Timeouts","role":"content","text":"After you issue a command or a connection attempt, the client will wait\nfor a response from the server. If the server doesn't respond within a\ncertain time limit, the client will throw a `TimeoutError`. By default,\nthe timeout happens after 10 seconds for both connections and commands, but you\ncan set your own timeouts using the `socket_connect_timeout` and `socket_timeout` parameters\nwhen you connect:\n\n[code example]\n\nTake care to set the timeouts to appropriate values for your use case.\nIf you use timeouts that are too short, then `redis-py` might retry\ncommands that would have succeeded if given more time. However, if the\ntimeouts are too long, your app might hang unnecessarily while waiting for a\nresponse that will never arrive."},{"id":"smart-client-handoffs","title":"Smart client handoffs","role":"content","text":"*Smart client handoffs (SCH)* is a feature of Redis Cloud and\nRedis Software servers that lets them actively notify clients\nabout planned server maintenance shortly before it happens. This\nlets a client take action to avoid disruptions in service.\n\nSee [Smart client handoffs](https://redis.io/docs/latest/develop/clients/sch)\nfor more information about SCH and\n[Connect using Smart client handoffs](https://redis.io/docs/latest/develop/clients/redis-py/connect#connect-using-smart-client-handoffs-sch)\nfor example code."},{"id":"monitor-performance-and-errors","title":"Monitor performance and errors","role":"content","text":"`redis-py` supports [OpenTelemetry](https://opentelemetry.io/). This lets\nyou trace command execution and monitor your server's performance.\nYou can use this information to detect problems before they are reported\nby users. See [Observability](https://redis.io/docs/latest/develop/clients/redis-py/observability)\nfor more information.\n\nTo find the keys that use the most CPU time and network bandwidth on the\nserver, use the [`HOTKEYS`](https://redis.io/docs/latest/commands/hotkeys) command."}],"examples":[{"id":"checklist-ex0","language":"checklist {id=\"pyprodlist\"}","code":"- [ ] [Client-side caching](#client-side-caching)\n- [ ] [Retries](#retries)\n- [ ] [Health checks](#health-checks)\n- [ ] [Exception handling](#exception-handling)\n- [ ] [Timeouts](#timeouts)\n- [ ] [Smart client handoffs](#smart-client-handoffs)\n- [ ] [Monitor performance and errors](#monitor-performance-and-errors)","section_id":"checklist"},{"id":"retries-ex0","language":"py","code":"from redis.backoff import ExponentialBackoff\nfrom redis.retry import Retry\n\n# Run 8 retries with exponential backoff strategy.\nretry = Retry(ExponentialBackoff(), 8)\n\nr = Redis(\n  retry=retry,\n    .\n    .\n)","section_id":"retries"},{"id":"retries-ex1","language":"py","code":"# Only retry after a `TimeoutError`.\nretry = Retry(ExponentialBackoff(), 3, supported_errors=(TimeoutError,))","section_id":"retries"},{"id":"retries-ex2","language":"py","code":"# Add `BusyLoadingError` to the default list of exceptions.\nfrom redis.exceptions import (\n   BusyLoadingError,\n)\n    .\n    .\n\nr = Redis(\n  retry=retry,\n  retry_on_error=[BusyLoadingError],\n    .\n    .\n)","section_id":"retries"},{"id":"health-checks-ex0","language":"py","code":"# Issue a health check if the connection is idle for longer\n# than three seconds.\nr = Redis(\n  health_check_interval = 3,\n    .\n    .\n)","section_id":"health-checks"},{"id":"timeouts-ex0","language":"py","code":"# Set a 15-second timeout for connections and a\n# 5-second timeout for commands.\nr = Redis(\n  socket_connect_timeout=15,\n  socket_timeout=5,\n    .\n    .\n)","section_id":"timeouts"}]}
{"schema_version":2,"id":"develop/clients/redis-py/queryjson","title":"Index and query documents","url":"https://redis.io/docs/latest/develop/clients/redis-py/queryjson/","summary":"Learn how to use Redis Search with JSON and hash documents.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-16T16:25:49+01:00","page_type":"content","content_hash":"2ec0dcb3fba5b42068a5073a18bf411b850475e7d5fef7986ccaa2c3d2aa5f01","sections":[{"id":"initialize","title":"Initialize","role":"content","text":"Make sure that you have [Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack)\nor another Redis server available. Also install the\n[`redis-py`](https://redis.io/docs/latest/develop/clients/redis-py) client library if you\nhaven't already done so.\n\nAdd the following dependencies. All of them are applicable to both JSON and hash,\nexcept for the `Path` class, which is specific to JSON (see\n[Path](https://redis.io/docs/latest/develop/data-types/json/path) for a description of the\nJSON path syntax).\n\nFoundational: Import required libraries for Redis Search, JSON operations, and search functionality\n\n[code example]"},{"id":"create-data","title":"Create data","role":"content","text":"Create some test data to add to your database. The example data shown\nbelow is compatible with both JSON and hash objects.\n\nFoundational: Define sample user data structures for indexing and querying\n\n[code example]"},{"id":"add-the-index","title":"Add the index","role":"content","text":"Connect to your Redis database. The code below shows the most\nbasic connection but see\n[Connect to the server](https://redis.io/docs/latest/develop/clients/redis-py/connect)\nto learn more about the available connection options.\n\nFoundational: Establish a connection to a Redis server for query operations\n\n[code example]\n\n\n\nThe example uses an index called `idx:users` for JSON documents and adds\nsome JSON documents with the `user:` key prefix. To avoid errors, first\ndelete any existing index or documents whose names that might\nconflict with the example:\n\nFoundational: Clean up existing indexes and documents to prepare for fresh example data\n\n[code example]\n\n\n\nCreate an index for the JSON data. The code below specifies that only JSON documents with\nthe key prefix `user:` are indexed. For more information, see\n[Query syntax](https://redis.io/docs/latest/develop/ai/search-and-query/query).\n\nFoundational: Create a search index for JSON documents with field definitions and key prefix filtering\n\n[code example]"},{"id":"add-the-data","title":"Add the data","role":"content","text":"Add the three sets of user data to the database as\n[JSON](https://redis.io/docs/latest/develop/data-types/json) objects.\nIf you use keys with the `user:` prefix then Redis will index the\nobjects automatically as you add them:\n\nFoundational: Store JSON documents in Redis with automatic indexing based on key prefix\n\n[code example]"},{"id":"query-the-data","title":"Query the data","role":"content","text":"You can now use the index to search the JSON objects. The\n[query](https://redis.io/docs/latest/develop/ai/search-and-query/query)\nbelow searches for objects that have the text \"Paul\" in any field\nand have an `age` value in the range 30 to 40:\n\nQuery with filters: Search JSON documents using text matching and numeric range filters to find specific records\n\n[code example]\n\n\n\n\nSpecify query options to return only the `city` field:\n\nQuery with field projection: Retrieve only specific fields from search results to reduce data transfer\n\n[code example]\n\n\n\n\nUse an\n[aggregation query](https://redis.io/docs/latest/develop/ai/search-and-query/query/aggregation)\nto count all users in each city.\n\nAggregation queries: Use GROUP BY and COUNT operations to summarize and analyze indexed data\n\n[code example]"},{"id":"differences-with-hash-documents","title":"Differences with hash documents","role":"content","text":"Indexing for hash documents is very similar to JSON indexing but you\nneed to specify some slightly different options.\n\nWhen you create the schema for a hash index, you don't need to\nadd aliases for the fields, since you use the basic names to access\nthe fields anyway. Also, you must use `HASH` for the `IndexType`\nwhen you create the index.\n\nFirst delete any existing index or documents\nwhose names might conflict with the hash example:\n\nFoundational: Clean up existing hash indexes and documents to prepare for fresh example data\n\n[code example]\n\n\n\n\nCreate a new index called `hash-idx:users`, which is otherwise the same as\nthe `idx:users` index used for JSON documents in the previous examples:\n\nFoundational: Create a search index for hash documents with HASH index type and field definitions\n\n[code example]\n\n\n\n\nYou use [`hset()`](https://redis.io/docs/latest/commands/hset) to add the hash\ndocuments instead of [`json().set()`](https://redis.io/docs/latest/commands/json.set),\nbut the same flat `userX` dictionaries work equally well with either\nhash or JSON:\n\nFoundational: Store hash documents in Redis with automatic indexing based on key prefix\n\n[code example]\n\n\n\nThe query commands work the same here for hash as they do for JSON (but\nthe name of the hash index is different). The format of the result is\nalmost the same except that the fields are returned directly in the\nresult `Document` object instead of in an enclosing `json` dictionary:\n\nQuery with filters: Search hash documents using text matching and numeric range filters (same as JSON queries)\n\n[code example]"},{"id":"bulk-import-with-himport","title":"Bulk import with HIMPORT","role":"content","text":"From Redis Open Source 8.10, you can also add the hash documents with the\n[`HIMPORT`](https://redis.io/docs/latest/commands/himport) command family. This creates the same\nhashes as `hset()`, but lets Redis store them as\n[compact hashes](https://redis.io/docs/latest/develop/data-types/hashes#compact-hashes), which use\nless memory when many keys share the same field names. Redis Search indexes\nthese hashes in the same way.\n\nDeclare the field names once with\n[`himport_prepare()`](https://redis.io/docs/latest/commands/himport-prepare), then create each\ndocument with [`himport_set()`](https://redis.io/docs/latest/commands/himport-set), passing only\nits values, in the same order as the field names. Use a\n[pipeline](https://redis.io/docs/latest/develop/clients/redis-py/transpipe) to send all the\nimports in one round trip:\n\nBulk ingestion: Import hash documents as compact hashes using HIMPORT PREPARE and a pipeline of HIMPORT SET calls\n\n**Difficulty:** Intermediate\n\n**Builds upon:** add_hash_data\n\n**Available in:** Python\n\n##### Python\n\n[code example]\n\n\n\n`himport_set()` replaces any existing key, so running this code after the\n`hset()` calls leaves you with the same three documents. The query returns\nthe same result, although the fields can appear in a different order.\n\n> [!NOTE]\n> `HIMPORT` requires `redis-py` v8.1.0 or later, and `redis-py` marks its\n> support as experimental, so the API might change. Redis Cloud and\n> Redis Software don't support `HIMPORT`."},{"id":"more-information","title":"More information","role":"content","text":"See the [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) docs\nfor a full description of all query features with examples."}],"examples":[{"id":"initialize-ex0","language":"python","code":"import redis\nfrom redis.commands.json.path import Path\nimport redis.commands.search.aggregation as aggregations\nimport redis.commands.search.reducers as reducers\nfrom redis.commands.search.field import TextField, NumericField, TagField\nfrom redis.commands.search.index_definition import IndexDefinition, IndexType\nfrom redis.commands.search.query import Query\nimport redis.exceptions","section_id":"initialize"},{"id":"create-data-ex0","language":"python","code":"user1 = {\n    \"name\": \"Paul John\",\n    \"email\": \"paul.john@example.com\",\n    \"age\": 42,\n    \"city\": \"London\"\n}\n\nuser2 = {\n    \"name\": \"Eden Zamir\",\n    \"email\": \"eden.zamir@example.com\",\n    \"age\": 29,\n    \"city\": \"Tel Aviv\"\n}\n\nuser3 = {\n    \"name\": \"Paul Zamir\",\n    \"email\": \"paul.zamir@example.com\",\n    \"age\": 35,\n    \"city\": \"Tel Aviv\"\n}","section_id":"create-data"},{"id":"add-the-index-ex0","language":"python","code":"r = redis.Redis(decode_responses=True)","section_id":"add-the-index"},{"id":"add-the-index-ex1","language":"python","code":"try:\n    r.ft(\"idx:users\").dropindex(True)\nexcept redis.exceptions.ResponseError:\n    pass\n\nr.delete(\"user:1\", \"user:2\", \"user:3\")","section_id":"add-the-index"},{"id":"add-the-index-ex2","language":"python","code":"schema = (\n    TextField(\"$.name\", as_name=\"name\"),\n    TagField(\"$.city\", as_name=\"city\"),\n    NumericField(\"$.age\", as_name=\"age\")\n)\n\nindexCreated = r.ft(\"idx:users\").create_index(\n    schema,\n    definition=IndexDefinition(\n        prefix=[\"user:\"], index_type=IndexType.JSON\n    )\n)","section_id":"add-the-index"},{"id":"add-the-data-ex0","language":"python","code":"user1Set = r.json().set(\"user:1\", Path.root_path(), user1)\nuser2Set = r.json().set(\"user:2\", Path.root_path(), user2)\nuser3Set = r.json().set(\"user:3\", Path.root_path(), user3)","section_id":"add-the-data"},{"id":"query-the-data-ex0","language":"python","code":"findPaulResult = r.ft(\"idx:users\").search(\n    Query(\"Paul @age:[30 40]\")\n)\n\nprint(findPaulResult)\n# >>> Result{1 total, docs: [Document {'id': 'user:3', ...","section_id":"query-the-data"},{"id":"query-the-data-ex1","language":"python","code":"citiesResult = r.ft(\"idx:users\").search(\n    Query(\"Paul\").return_field(\"$.city\", as_field=\"city\")\n).docs\n\nprint(citiesResult)\n# >>> [Document {'id': 'user:1', 'payload': None, ...","section_id":"query-the-data"},{"id":"query-the-data-ex2","language":"python","code":"req = aggregations.AggregateRequest(\"*\").group_by(\n    '@city', reducers.count().alias('count')\n)\n\naggResult = r.ft(\"idx:users\").aggregate(req).rows\nprint(aggResult)\n# >>> [['city', 'London', 'count', '1'], ['city', 'Tel Aviv', 'count', '2']]","section_id":"query-the-data"},{"id":"differences-with-hash-documents-ex0","language":"python","code":"try:\n    r.ft(\"hash-idx:users\").dropindex(True)\nexcept redis.exceptions.ResponseError:\n    pass\n\nr.delete(\"huser:1\", \"huser:2\", \"huser:3\")","section_id":"differences-with-hash-documents"},{"id":"differences-with-hash-documents-ex1","language":"python","code":"hashSchema = (\n    TextField(\"name\"),\n    TagField(\"city\"),\n    NumericField(\"age\")\n)\n\nhashIndexCreated = r.ft(\"hash-idx:users\").create_index(\n    hashSchema,\n    definition=IndexDefinition(\n        prefix=[\"huser:\"], index_type=IndexType.HASH\n    )\n)","section_id":"differences-with-hash-documents"},{"id":"differences-with-hash-documents-ex2","language":"python","code":"huser1Set = r.hset(\"huser:1\", mapping=user1)\nhuser2Set = r.hset(\"huser:2\", mapping=user2)\nhuser3Set = r.hset(\"huser:3\", mapping=user3)","section_id":"differences-with-hash-documents"},{"id":"differences-with-hash-documents-ex3","language":"python","code":"findPaulHashResult = r.ft(\"hash-idx:users\").search(\n    Query(\"Paul @age:[30 40]\")\n)\n\nprint(findPaulHashResult)\n# >>> Result{1 total, docs: [Document {'id': 'huser:3',\n# >>>   'payload': None, 'name': 'Paul Zamir', ...","section_id":"differences-with-hash-documents"},{"id":"bulk-import-with-himport-ex0","language":"python","code":"fields = [\"name\", \"email\", \"age\", \"city\"]\nr.himport_prepare(\"huser\", fields)\n\nwith r.pipeline(transaction=False) as pipe:\n    for key, user in [(\"huser:1\", user1), (\"huser:2\", user2), (\"huser:3\", user3)]:\n        pipe.himport_set(key, \"huser\", [user[f] for f in fields])\n    himportRes = pipe.execute()","section_id":"bulk-import-with-himport"}]}
{"schema_version":2,"id":"develop/clients/redis-py/scaniter","title":"Scan iteration","url":"https://redis.io/docs/latest/develop/clients/redis-py/scaniter/","summary":"Iterate through results from `SCAN`, `HSCAN`, etc.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-08-27T15:55:18+01:00","page_type":"content","content_hash":"7e9636766fbe8c3c93420b051abca3f361f967065c459f6fb49452928be1bcde","sections":[{"id":"content","title":"Content","role":"content","text":"Redis has a small family of related commands that retrieve\nkeys and, in some cases, their associated values:\n\n-   [`SCAN`](https://redis.io/docs/latest/commands/scan) retrieves keys\n    from the main Redis keyspace.\n-   [`HSCAN`](https://redis.io/docs/latest/commands/hscan) retrieves keys and optionally,\n    their values from a\n    [hash](https://redis.io/docs/latest/develop/data-types/hashes) object.\n-   [`SSCAN`](https://redis.io/docs/latest/commands/sscan) retrieves keys from a\n    [set](https://redis.io/docs/latest/develop/data-types/sets) object.\n-   [`ZSCAN`](https://redis.io/docs/latest/commands/zscan) retrieves keys and their score values from a\n    [sorted set](https://redis.io/docs/latest/develop/data-types/sorted-sets) object.\n\nThese commands can potentially return large numbers of results, so Redis\nprovides a paging mechanism to access the results in small, separate batches.\nWith the basic commands, you must maintain a cursor value in your code\nto keep track of the current page. As a convenient alternative, `redis-py`\nalso lets you access the results using an\n[iterator](https://docs.python.org/3/glossary.html#term-iterable).\nThis handles the paging transparently, so you simply need to process\nthe items it returns one-by-one in a `for` loop or pass the iterator\nobject itself in place of a\n[sequence](https://docs.python.org/3/glossary.html#term-sequence).\n\nEach of the commands has its own equivalent iterator. The following example shows\nhow to use a `SCAN` iterator on the Redis keyspace. Note that, as with the `SCAN`\ncommand, the results are not sorted into any particular order, . Also, you\ncan pass `match`, `count`, and `_type` parameters to `scan_iter()` to constrain\nthe set of keys it returns (see the [`SCAN`](https://redis.io/docs/latest/commands/scan)\ncommand page for examples). \n\n[code example]\n\nThe iterators for the other commands are also named with `_iter()` after\nthe name of the basic command (`hscan_iter()`, `sscan_iter()`, and `zscan_iter()`).\nThey work in a similar way to `scan_iter()` except that you must pass a\nkey to identify the object you want to scan. The example below shows how to\niterate through the items in a sorted set using `zscan_iter()`.\n\n[code example]\n\nNote that in this case, the item returned by the iterator is a\n[tuple](https://docs.python.org/3/tutorial/datastructures.html#tuples-and-sequences)\nwith two elements for the key and score. By default, `hscan_iter()`\nalso returns a 2-tuple for the key and value, but you can\npass a value of `True` for the `no_values` parameter to retrieve just\nthe keys:\n\n[code example]"}],"examples":[{"id":"content-ex0","language":"py","code":"import redis\n\nr = redis.Redis(decode_responses=True)\n\nr.set(\"key:1\", \"a\")\nr.set(\"key:2\", \"b\")\nr.set(\"key:3\", \"c\")\nr.set(\"key:4\", \"d\")\nr.set(\"key:5\", \"e\")\n\nfor key in r.scan_iter():\n    print(f\"Key: {key}, value: {r.get(key)}\")\n# >>> Key: key:1, value: a\n# >>> Key: key:4, value: d\n# >>> Key: key:3, value: c\n# >>> Key: key:2, value: b\n# >>> Key: key:5, value: e","section_id":"content"},{"id":"content-ex1","language":"py","code":"r.zadd(\"battles\", mapping={\n    \"hastings\": 1066,\n    \"agincourt\": 1415,\n    \"trafalgar\": 1805,\n    \"somme\": 1916,\n})\n\nfor item in r.zscan_iter(\"battles\"):\n    print(f\"Key: {item[0]}, value: {int(item[1])}\")\n# >>> Key: hastings, value: 1066\n# >>> Key: agincourt, value: 1415\n# >>> Key: trafalgar, value: 1805\n# >>> Key: somme, value: 1916","section_id":"content"},{"id":"content-ex2","language":"py","code":"r.hset(\"details\", mapping={\n    \"name\": \"Mr Benn\",\n    \"address\": \"52 Festive Road\",\n    \"hobbies\": \"Cosplay\"\n})\n\nfor key in r.hscan_iter(\"details\", no_values=True):\n    print(f\"Key: {key}, value: {r.hget(\"details\", key)}\")\n# >>> Key: name, value: Mr Benn\n# >>> Key: address, value: 52 Festive Road\n# >>> Key: hobbies, value: Cosplay","section_id":"content"}]}
{"schema_version":2,"id":"develop/clients/redis-py/transpipe","title":"Pipelines and transactions","url":"https://redis.io/docs/latest/develop/clients/redis-py/transpipe/","summary":"Learn how to use Redis pipelines and transactions","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-08-27T15:55:18+01:00","page_type":"content","content_hash":"48bbf8fac2c1a90efe6cdda8bcab669d143bebd54f5635f2554d906a714efd83","sections":[{"id":"execute-a-pipeline","title":"Execute a pipeline","role":"content","text":"To execute commands in a pipeline, you first create a pipeline object\nand then add commands to it using methods that resemble the standard\ncommand methods (for example, `set()` and `get()`). The commands are\nbuffered in the pipeline and only execute when you call the `execute()`\nmethod on the pipeline object. This method returns a list that contains\nthe results from all the commands in order.\n\nNote that the command methods for a pipeline always return the original\npipeline object, so you can \"chain\" several commands together, as the\nexample below shows:\n\nFoundational: Use pipelines to batch multiple commands together and reduce network round trips\n\n**Difficulty:** Beginner\n\n**Available in:** Python\n\n##### Python\n\n[code example]"},{"id":"execute-a-transaction","title":"Execute a transaction","role":"content","text":"A pipeline actually executes as a transaction by default (that is to say,\nall commands are executed in an uninterrupted sequence). However, if you\nneed to switch this behavior off, you can set the `transaction` parameter\nto `False` when you create the pipeline:\n\n[code example]"},{"id":"watch-keys-for-changes","title":"Watch keys for changes","role":"content","text":"Redis supports *optimistic locking* to avoid inconsistent updates\nto different keys. The basic idea is to watch for changes to any\nkeys that you use in a transaction while you are processing the\nupdates. If the watched keys do change, you must restart the updates\nwith the latest data from the keys. See\n[Transactions](https://redis.io/docs/latest/develop/using-commands/transactions)\nfor more information about optimistic locking.\n\nThe example below shows how to repeatedly attempt a transaction with a watched\nkey until it succeeds. The code reads a string\nthat represents a `PATH` variable for a command shell, then appends a new\ncommand path to the string before attempting to write it back. If the watched\nkey is modified by another client before writing, the transaction aborts\nwith a `WatchError` exception, and the loop executes again for another attempt.\nOtherwise, the loop terminates successfully.\n\nOptimistic locking: Use WATCH to monitor keys for changes and retry transactions when conflicts occur\n\n**Difficulty:** Intermediate\n\n**Available in:** Python\n\n##### Python\n\n[code example]\n\n\n\nBecause this is a common pattern, the library includes a convenience\nmethod called `transaction()` that handles the code to watch keys,\nexecute the transaction, and retry if necessary. Pass\n`transaction()` a function that implements your main transaction code,\nand also pass the keys you want to watch. The example below implements\nthe same basic transaction as the previous example but this time\nusing `transaction()`. Note that `transaction()` can't add the `multi()`\ncall automatically, so you must still place this correctly in your\ntransaction function.\n\nOptimistic locking with convenience method: Use the transaction() helper to simplify watched transaction patterns\n\n**Difficulty:** Intermediate\n\n**Available in:** Python\n\n##### Python\n\n[code example]"}],"examples":[{"id":"execute-a-pipeline-ex0","language":"python","code":"r = redis.Redis(decode_responses=True)\n\npipe = r.pipeline()\n\nfor i in range(5):\n    pipe.set(f\"seat:{i}\", f\"#{i}\")\n\nset_5_result = pipe.execute()\nprint(set_5_result)  # >>> [True, True, True, True, True]\n\npipe = r.pipeline()\n\n# \"Chain\" pipeline commands together.\nget_3_result = pipe.get(\"seat:0\").get(\"seat:3\").get(\"seat:4\").execute()\nprint(get_3_result)  # >>> ['#0', '#3', '#4']","section_id":"execute-a-pipeline"},{"id":"execute-a-transaction-ex0","language":"python","code":"pipe = r.pipeline(transaction=False)","section_id":"execute-a-transaction"},{"id":"watch-keys-for-changes-ex0","language":"python","code":"r.set(\"shellpath\", \"/usr/syscmds/\")\n\nwith r.pipeline() as pipe:\n    # Repeat until successful.\n    while True:\n        try:\n            # Watch the key we are about to change.\n            pipe.watch(\"shellpath\")\n\n            # The pipeline executes commands directly (instead of\n            # buffering them) from immediately after the `watch()`\n            # call until we begin the transaction.\n            current_path = pipe.get(\"shellpath\")\n            new_path = current_path + \":/usr/mycmds/\"\n\n            # Start the transaction, which will enable buffering\n            # again for the remaining commands.\n            pipe.multi()\n\n            pipe.set(\"shellpath\", new_path)\n\n            pipe.execute()\n\n            # The transaction succeeded, so break out of the loop.\n            break\n        except redis.WatchError:\n            # The transaction failed, so continue with the next attempt.\n            continue\n\nget_path_result = r.get(\"shellpath\")\nprint(get_path_result)  # >>> '/usr/syscmds/:/usr/mycmds/'","section_id":"watch-keys-for-changes"},{"id":"watch-keys-for-changes-ex1","language":"python","code":"r.set(\"shellpath\", \"/usr/syscmds/\")\n\n\ndef watched_sequence(pipe):\n    current_path = pipe.get(\"shellpath\")\n    new_path = current_path + \":/usr/mycmds/\"\n\n    pipe.multi()\n\n    pipe.set(\"shellpath\", new_path)\n\n\ntrans_result = r.transaction(watched_sequence, \"shellpath\")\nprint(trans_result)  # True\n\nget_path_result = r.get(\"shellpath\")\nprint(get_path_result)  # >>> '/usr/syscmds/:/usr/mycmds/'","section_id":"watch-keys-for-changes"}]}
{"schema_version":2,"id":"develop/clients/redis-py/vecsearch","title":"Index and query vectors","url":"https://redis.io/docs/latest/develop/clients/redis-py/vecsearch/","summary":"Learn how to index and query vector embeddings with Redis","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-16T16:44:30+01:00","page_type":"content","content_hash":"eddce63e49a6fc4618cd43b4011826b90ce6e79fff60d5559c179f6aa631775c","sections":[{"id":"initialize","title":"Initialize","role":"content","text":"Install [`redis-py`](https://redis.io/docs/latest/develop/clients/redis-py) if you\nhave not already done so. Also, install `sentence-transformers` with the\nfollowing command:\n\n[code example]\n\nIn a new Python source file, start by importing the required classes:\n\nFoundational: Import required libraries for vector embeddings, Redis operations, and search functionality\n\n**Difficulty:** Beginner\n\n**Available in:** Python\n\n##### Python\n\n[code example]\n\n\n\nThe first of these imports is the\n`SentenceTransformer` class, which generates an embedding from a section of text.\nHere, we create an instance of `SentenceTransformer` that uses the\n[`all-MiniLM-L6-v2`](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2)\nmodel for the embeddings. This model generates vectors with 384 dimensions, regardless\nof the length of the input text, but note that the input is truncated to 256\ntokens (see\n[Word piece tokenization](https://huggingface.co/learn/nlp-course/en/chapter6/6)\nat the [Hugging Face](https://huggingface.co/) docs to learn more about the way tokens\nare related to the original text).\n\nFoundational: Initialize a SentenceTransformer model to generate vector embeddings from text\n\n**Difficulty:** Beginner\n\n**Available in:** Python\n\n##### Python\n\n[code example]"},{"id":"create-the-index","title":"Create the index","role":"content","text":"Connect to Redis and delete any index previously created with the\nname `vector_idx`. (The `dropindex()` call throws an exception if\nthe index doesn't already exist, which is why you need the\n`try: except:` block.)\n\nFoundational: Connect to a Redis server and clean up existing vector indexes\n\n**Difficulty:** Beginner\n\n**Available in:** Python\n\n##### Python\n\n[code example]\n\n\n\nNext, create the index.\nThe schema in the example below specifies hash objects for storage and includes\nthree fields: the text content to index, a\n[tag](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/tags)\nfield to represent the \"genre\" of the text, and the embedding vector generated from\nthe original text content. The `embedding` field specifies\n[HNSW](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#hnsw-index)\nindexing, the\n[L2](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#distance-metrics)\nvector distance metric, `Float32` values to represent the vector's components,\nand 384 dimensions, as required by the `all-MiniLM-L6-v2` embedding model.\n\nFoundational: Create a vector search index for hash documents with HNSW algorithm and L2 distance metric\n\n**Difficulty:** Intermediate\n\n**Available in:** Python\n\n##### Python\n\n[code example]"},{"id":"add-data","title":"Add data","role":"content","text":"You can now supply the data objects, which will be indexed automatically\nwhen you add them with [`hset()`](https://redis.io/docs/latest/commands/hset), as long as\nyou use the `doc:` prefix specified in the index definition.\n\nUse the `model.encode()` method of `SentenceTransformer`\nas shown below to create the embedding that represents the `content` field.\nThe `astype()` option that follows the `model.encode()` call specifies that\nwe want a vector of `float32` values. The `tobytes()` option encodes the\nvector components together as a single binary string.\nUse the binary string representation when you are indexing hashes\nor running a query (but use a list of `float` for\n[JSON documents](#differences-with-json-documents)).\n\nFoundational: Store hash documents with vector embeddings generated from text content\n\n**Difficulty:** Beginner\n\n**Available in:** Python\n\n##### Python\n\n[code example]"},{"id":"bulk-import-with-himport","title":"Bulk import with HIMPORT","role":"content","text":"From Redis Open Source 8.10, you can also add the documents with the\n[`HIMPORT`](https://redis.io/docs/latest/commands/himport) command family. This creates the same\nhashes as `hset()`, but lets Redis store them as\n[compact hashes](https://redis.io/docs/latest/develop/data-types/hashes#compact-hashes), which use\nless memory when many keys share the same field names. Redis Search indexes\nthese hashes in the same way.\n\nDeclare the field names once with\n[`himport_prepare()`](https://redis.io/docs/latest/commands/himport-prepare), then create each\ndocument with [`himport_set()`](https://redis.io/docs/latest/commands/himport-set), passing only\nits values, in the same order as the field names. Use a\n[pipeline](https://redis.io/docs/latest/develop/clients/redis-py/transpipe) to send all the\nimports in one round trip:\n\nBulk ingestion: Import hash documents with vector embeddings as compact hashes using HIMPORT PREPARE and a pipeline of HIMPORT SET calls\n\n**Difficulty:** Intermediate\n\n**Builds upon:** add_data\n\n**Available in:** Python\n\n##### Python\n\n[code example]\n\n\n\n`himport_set()` replaces any existing key, so running this code after the\n`hset()` calls leaves you with the same three documents.\n\n> [!NOTE]\n> `HIMPORT` requires `redis-py` v8.1.0 or later, and `redis-py` marks its\n> support as experimental, so the API might change. Redis Cloud and\n> Redis Software don't support `HIMPORT`."},{"id":"run-a-query","title":"Run a query","role":"content","text":"After you have created the index and added the data, you are ready to run a query.\nTo do this, you must create another embedding vector from your chosen query\ntext. Redis calculates the similarity between the query vector and each\nembedding vector in the index as it runs the query. It then ranks the\nresults in order of this numeric similarity value.\n\nThe code below creates the query embedding using `model.encode()`, as with\nthe indexing, and passes it as a parameter when the query executes\n(see\n[Vector search](https://redis.io/docs/latest/develop/ai/search-and-query/query/vector-search)\nfor more information about using query parameters with embeddings).\n\nVector similarity search: Find semantically similar documents by comparing query embeddings with indexed vectors using L2 distance\n\n**Difficulty:** Intermediate\n\n**Available in:** Python\n\n##### Python\n\n[code example]\n\n\n\nThe code is now ready to run, but note that it may take a while to complete when\nyou run it for the first time (which happens because `sentence-transformers` must download the\n`all-MiniLM-L6-v2` model data before it can\ngenerate the embeddings). When you run the code, it outputs the following result\nobject (slightly formatted here for clarity):\n\n[code example]\n\nThe exact distances may differ slightly on your system.\n\nNote that the results are ordered according to the value of the `vector_distance`\nfield, with the lowest distance indicating the greatest similarity to the query.\nAs you would expect, the result for `doc:0` with the content text *\"That is a very happy person\"*\nis the result that is most similar in meaning to the query text\n*\"That is a happy person\"*."},{"id":"differences-with-json-documents","title":"Differences with JSON documents","role":"content","text":"Indexing JSON documents is similar to hash indexing, but there are some\nimportant differences. JSON allows much richer data modelling with nested fields, so\nyou must supply a [path](https://redis.io/docs/latest/develop/data-types/json/path) in the schema\nto identify each field you want to index. However, you can declare a short alias for each\nof these paths (using the `as_name` keyword argument) to avoid typing it in full for\nevery query. Also, you must specify `IndexType.JSON` when you create the index.\n\nThe code below shows these differences, but the index is otherwise very similar to\nthe one created previously for hashes:\n\nFoundational: Create a vector search index for JSON documents with JSON paths and field aliases\n\n**Difficulty:** Intermediate\n\n**Available in:** Python\n\n##### Python\n\n[code example]\n\n\n\nUse [`json().set()`](https://redis.io/docs/latest/commands/json.set) to add the data\ninstead of [`hset()`](https://redis.io/docs/latest/commands/hset). The dictionaries\nthat specify the fields have the same structure as the ones used for `hset()`\nbut `json().set()` receives them in a positional argument instead of\nthe `mapping` keyword argument.\n\nAn important difference with JSON indexing is that the vectors are\nspecified using lists instead of binary strings. Generate the list\nusing the `tolist()` method instead of `tobytes()` as you would with a\nhash.\n\nFoundational: Store JSON documents with vector embeddings as lists (different from hash binary format)\n\n**Difficulty:** Beginner\n\n**Available in:** Python\n\n##### Python\n\n[code example]\n\n\n\nThe query is almost identical to the one for the hash documents. This\ndemonstrates how the right choice of aliases for the JSON paths can\nsave you having to write complex queries. An important thing to notice\nis that the vector parameter for the query is still specified as a\nbinary string (using the `tobytes()` method), even though the data for\nthe `embedding` field of the JSON was specified as a list.\n\nVector similarity search: Query JSON documents using vector embeddings with field aliases for simplified syntax\n\n**Difficulty:** Intermediate\n\n**Available in:** Python\n\n##### Python\n\n[code example]\n\n\n\nApart from the `jdoc:` prefixes for the keys, the result from the JSON\nquery is the same as for hash:\n\n[code example]"},{"id":"learn-more","title":"Learn more","role":"related","text":"See\n[Vector search](https://redis.io/docs/latest/develop/ai/search-and-query/query/vector-search)\nfor more information about the indexing options, distance metrics, and query format\nfor vectors."}],"examples":[{"id":"initialize-ex0","language":"bash","code":"pip install sentence-transformers","section_id":"initialize"},{"id":"initialize-ex1","language":"python","code":"from sentence_transformers import SentenceTransformer\nfrom redis.commands.search.query import Query\nfrom redis.commands.search.field import TextField, TagField, VectorField\nfrom redis.commands.search.index_definition import IndexDefinition, IndexType\nfrom redis.commands.json.path import Path\n\nimport numpy as np\nimport redis","section_id":"initialize"},{"id":"initialize-ex2","language":"python","code":"model = SentenceTransformer(\"sentence-transformers/all-MiniLM-L6-v2\")","section_id":"initialize"},{"id":"create-the-index-ex0","language":"python","code":"r = redis.Redis(decode_responses=True)\n\ntry:\n    r.ft(\"vector_idx\").dropindex(True)\nexcept redis.exceptions.ResponseError:\n    pass","section_id":"create-the-index"},{"id":"create-the-index-ex1","language":"python","code":"schema = (\n    TextField(\"content\"),\n    TagField(\"genre\"),\n    VectorField(\"embedding\", \"HNSW\", {\n        \"TYPE\": \"FLOAT32\",\n        \"DIM\": 384,\n        \"DISTANCE_METRIC\":\"L2\"\n    })\n)\n\nr.ft(\"vector_idx\").create_index(\n    schema,\n    definition=IndexDefinition(\n        prefix=[\"doc:\"], index_type=IndexType.HASH\n    )\n)","section_id":"create-the-index"},{"id":"add-data-ex0","language":"python","code":"content = \"That is a very happy person\"\n\nr.hset(\"doc:0\", mapping={\n    \"content\": content,\n    \"genre\": \"persons\",\n    \"embedding\": model.encode(content).astype(np.float32).tobytes(),\n})\n\ncontent = \"That is a happy dog\"\n\nr.hset(\"doc:1\", mapping={\n    \"content\": content,\n    \"genre\": \"pets\",\n    \"embedding\": model.encode(content).astype(np.float32).tobytes(),\n})\n\ncontent = \"Today is a sunny day\"\n\nr.hset(\"doc:2\", mapping={\n    \"content\": content,\n    \"genre\": \"weather\",\n    \"embedding\": model.encode(content).astype(np.float32).tobytes(),\n})","section_id":"add-data"},{"id":"bulk-import-with-himport-ex0","language":"python","code":"r.himport_prepare(\"doc\", [\"content\", \"genre\", \"embedding\"])\n\ndocs = [\n    (\"doc:0\", \"That is a very happy person\", \"persons\"),\n    (\"doc:1\", \"That is a happy dog\", \"pets\"),\n    (\"doc:2\", \"Today is a sunny day\", \"weather\"),\n]\n\nwith r.pipeline(transaction=False) as pipe:\n    for key, content, genre in docs:\n        pipe.himport_set(key, \"doc\", [\n            content,\n            genre,\n            model.encode(content).astype(np.float32).tobytes(),\n        ])\n    pipe.execute()","section_id":"bulk-import-with-himport"},{"id":"run-a-query-ex0","language":"python","code":"q = Query(\n    \"*=>[KNN 3 @embedding $vec AS vector_distance]\"\n).return_field(\"vector_distance\").return_field(\"content\").dialect(2)\n\nquery_text = \"That is a happy person\"\n\nres = r.ft(\"vector_idx\").search(\n    q, query_params={\n        \"vec\": model.encode(query_text).astype(np.float32).tobytes()\n    }\n)\n\nprint(res)","section_id":"run-a-query"},{"id":"run-a-query-ex1","language":"plaintext","code":"Result{\n    3 total,\n    docs: [\n        Document {\n            'id': 'doc:0',\n            'payload': None,\n            'vector_distance': '0.114169985056',\n            'content': 'That is a very happy person'\n        },\n        Document {\n            'id': 'doc:1',\n            'payload': None,\n            'vector_distance': '0.610845386982',\n            'content': 'That is a happy dog'\n        },\n        Document {\n            'id': 'doc:2',\n            'payload': None,\n            'vector_distance': '1.48624813557',\n            'content': 'Today is a sunny day'\n        }\n    ]\n}","section_id":"run-a-query"},{"id":"differences-with-json-documents-ex0","language":"python","code":"schema = (\n    TextField(\"$.content\", as_name=\"content\"),\n    TagField(\"$.genre\", as_name=\"genre\"),\n    VectorField(\n        \"$.embedding\", \"HNSW\", {\n            \"TYPE\": \"FLOAT32\",\n            \"DIM\": 384,\n            \"DISTANCE_METRIC\": \"L2\"\n        },\n        as_name=\"embedding\"\n    )\n)\n\nr.ft(\"vector_json_idx\").create_index(\n    schema,\n    definition=IndexDefinition(\n        prefix=[\"jdoc:\"], index_type=IndexType.JSON\n    )\n)","section_id":"differences-with-json-documents"},{"id":"differences-with-json-documents-ex1","language":"python","code":"content = \"That is a very happy person\"\n\nr.json().set(\"jdoc:0\", Path.root_path(), {\n    \"content\": content,\n    \"genre\": \"persons\",\n    \"embedding\": model.encode(content).astype(np.float32).tolist(),\n})\n\ncontent = \"That is a happy dog\"\n\nr.json().set(\"jdoc:1\", Path.root_path(), {\n    \"content\": content,\n    \"genre\": \"pets\",\n    \"embedding\": model.encode(content).astype(np.float32).tolist(),\n})\n\ncontent = \"Today is a sunny day\"\n\nr.json().set(\"jdoc:2\", Path.root_path(), {\n    \"content\": content,\n    \"genre\": \"weather\",\n    \"embedding\": model.encode(content).astype(np.float32).tolist(),\n})","section_id":"differences-with-json-documents"},{"id":"differences-with-json-documents-ex2","language":"python","code":"q = Query(\n    \"*=>[KNN 3 @embedding $vec AS vector_distance]\"\n).return_field(\"vector_distance\").return_field(\"content\").dialect(2)\n\nquery_text = \"That is a happy person\"\n\nres = r.ft(\"vector_json_idx\").search(\n    q, query_params={\n        \"vec\": model.encode(query_text).astype(np.float32).tobytes()\n    }\n)\n\nprint(repr(res))","section_id":"differences-with-json-documents"},{"id":"differences-with-json-documents-ex3","language":"plaintext","code":"Result{\n    3 total,\n    docs: [\n        Document {\n            'id': 'jdoc:0',\n            'payload': None,\n            'vector_distance': '0.114169985056',\n            'content': 'That is a very happy person'\n        },\n            .\n            .\n            .","section_id":"differences-with-json-documents"}]}
{"schema_version":2,"id":"develop/clients/redis-py/vecsets","title":"Vector set embeddings","url":"https://redis.io/docs/latest/develop/clients/redis-py/vecsets/","summary":"Index and query embeddings with Redis vector sets","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-08-27T15:55:18+01:00","page_type":"content","content_hash":"57b795b76a35253e9c6201fc3f3b68627d8a754fba87133aa3340c0809bc5e2f","sections":[{"id":"initialize","title":"Initialize","role":"content","text":"Start by installing the preview version of `redis-py` with the following\ncommand:\n\n[code example]\n\nAlso, install `sentence-transformers`:\n\n[code example]\n\nIn a new Python file, import the required classes:\n\nFoundational: Import required libraries for vector sets, embeddings, and Redis operations\n\n**Difficulty:** Beginner\n\n**Available in:** Python\n\n##### Python\n\n[code example]\n\n\n\nThe first of these imports is the\n`SentenceTransformer` class, which generates an embedding from a section of text.\nThis example uses an instance of `SentenceTransformer` with the\n[`all-MiniLM-L6-v2`](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2)\nmodel for the embeddings. This model generates vectors with 384 dimensions, regardless\nof the length of the input text, but note that the input is truncated to 256\ntokens (see\n[Word piece tokenization](https://huggingface.co/learn/nlp-course/en/chapter6/6)\nat the [Hugging Face](https://huggingface.co/) docs to learn more about the way tokens\nare related to the original text).\n\nFoundational: Initialize a SentenceTransformer model to generate vector embeddings from text\n\n**Difficulty:** Beginner\n\n**Available in:** Python\n\n##### Python\n\n[code example]"},{"id":"create-the-data","title":"Create the data","role":"content","text":"The example data is contained a dictionary with some brief\ndescriptions of famous people:\n\nFoundational: Define sample data with text descriptions for vector embedding and storage\n\n**Difficulty:** Beginner\n\n**Available in:** Python\n\n##### Python\n\n[code example]"},{"id":"add-the-data-to-a-vector-set","title":"Add the data to a vector set","role":"content","text":"The next step is to connect to Redis and add the data to a new vector set.\n\nThe code below uses the dictionary's\n[`items()`](https://docs.python.org/3/library/stdtypes.html#dict.items)\nview to iterate through all the key-value pairs and add corresponding\nelements to a vector set called `famousPeople`.\n\nUse the\n[`encode()`](https://sbert.net/docs/package_reference/sentence_transformer/SentenceTransformer.html#sentence_transformers.SentenceTransformer.encode)\nmethod of `SentenceTransformer` to generate the\nembedding as an array of `float32` values. The `tobytes()` method converts\nthe array to a byte string that you can pass to the\n[`vadd()`](https://redis.io/docs/latest/commands/vadd) command to set the embedding.\nNote that `vadd()` can also accept a list of `float` values to set the\nvector, but the byte string format is more compact and saves a little\ntransmission time. If you later use\n[`vemb()`](https://redis.io/docs/latest/commands/vemb) to retrieve the embedding,\nit will return the vector as an array rather than the original byte\nstring (note that this is different from the behavior of byte strings in\n[hash vector indexing](https://redis.io/docs/latest/develop/ai/search-and-query/vectors)).\n\nThe call to `vadd()` also adds the `born` and `died` values from the\noriginal dictionary as attribute data. You can access this during a query\nor by using the [`vgetattr()`](https://redis.io/docs/latest/commands/vgetattr) method.\n\nFoundational: Add vector embeddings and attributes to a vector set using VADD command\n\n**Difficulty:** Beginner\n\n**Available in:** Python\n\n##### Python\n\n[code example]"},{"id":"query-the-vector-set","title":"Query the vector set","role":"content","text":"You can now query the data in the set. The basic approach is to use the\n`encode()` method to generate another embedding vector for the query text.\n(This is the same method used to add the elements to the set.) Then, pass\nthe query vector to [`vsim()`](https://redis.io/docs/latest/commands/vsim) to return elements\nof the set, ranked in order of similarity to the query.\n\nStart with a simple query for \"actors\":\n\nVector similarity search: Find semantically similar items in a vector set using VSIM command\n\n**Difficulty:** Intermediate\n\n**Available in:** Python\n\n##### Python\n\n[code example]\n\n\n\nThis returns the following list of elements (formatted slightly for clarity):\n\n[code example]\n\nThe first two people in the list are the two actors, as expected, but none of the\npeople from Linus Pauling onward was especially well-known for acting (and there certainly\nisn't any information about that in the short description text).\nAs it stands, the search attempts to rank all the elements in the set, based\non the information contained in the embedding model.\nYou can use the `count` parameter of `vsim()` to limit the list of elements\nto just the most relevant few items:\n\nVector similarity search with limits: Restrict results to the top K most similar items using the count parameter\n\n**Difficulty:** Intermediate\n\n**Available in:** Python\n\n##### Python\n\n[code example]\n\n\n\nThe reason for using text embeddings rather than simple text search\nis that the embeddings represent semantic information. This allows a query\nto find elements with a similar meaning even if the text is\ndifferent. For example, the word \"entertainer\" doesn't appear in any of the\ndescriptions but if you use it as a query, the actors and musicians are ranked\nhighest in the results list:\n\nSemantic search: Leverage text embeddings to find semantically similar items even when exact keywords don't match\n\n**Difficulty:** Intermediate\n\n**Available in:** Python\n\n##### Python\n\n[code example]\n\n\n\nSimilarly, if you use \"science\" as a query, you get the following results:\n\n[code example]\n\nThe scientists are ranked highest but they are then followed by the\nmathematicians. This seems reasonable given the connection between mathematics\nand science.\n\nYou can also use\n[filter expressions](https://redis.io/docs/latest/develop/data-types/vector-sets/filtered-search)\nwith `vsim()` to restrict the search further. For example,\nrepeat the \"science\" query, but this time limit the results to people\nwho died before the year 2000:\n\nFiltered vector search: Combine vector similarity with attribute filters to narrow results based on metadata conditions\n\n**Difficulty:** Advanced\n\n**Available in:** Python\n\n##### Python\n\n[code example]\n\n\n\nNote that the boolean filter expression is applied to items in the list\nbefore the vector distance calculation is performed. Items that don't\npass the filter test are removed from the results completely, rather\nthan just reduced in rank. This can help to improve the performance of the\nsearch because there is no need to calculate the vector distance for\nelements that have already been filtered out of the search."},{"id":"more-information","title":"More information","role":"content","text":"See the [vector sets](https://redis.io/docs/latest/develop/data-types/vector-sets)\ndocs for more information and code examples. See the\n[Redis for AI](https://redis.io/docs/latest/develop/ai) section for more details\nabout text embeddings and other AI techniques you can use with Redis.\n\nYou may also be interested in\n[vector search](https://redis.io/docs/latest/develop/clients/redis-py/vecsearch).\nThis is a feature of\n[Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query)\nthat lets you retrieve\n[JSON](https://redis.io/docs/latest/develop/data-types/json) and\n[hash](https://redis.io/docs/latest/develop/data-types/hashes) documents based on\nvector data stored in their fields."}],"examples":[{"id":"initialize-ex0","language":"bash","code":"pip install redis==6.0.0b2","section_id":"initialize"},{"id":"initialize-ex1","language":"bash","code":"pip install sentence-transformers","section_id":"initialize"},{"id":"initialize-ex2","language":"python","code":"from sentence_transformers import SentenceTransformer\n\nimport redis\nimport numpy as np","section_id":"initialize"},{"id":"initialize-ex3","language":"python","code":"model = SentenceTransformer(\"sentence-transformers/all-MiniLM-L6-v2\")","section_id":"initialize"},{"id":"create-the-data-ex0","language":"python","code":"peopleData = {\n    \"Marie Curie\": {\n        \"born\": 1867, \"died\": 1934,\n        \"description\": \"\"\"\n        Polish-French chemist and physicist. The only person ever to win\n        two Nobel prizes for two different sciences.\n        \"\"\"\n    },\n    \"Linus Pauling\": {\n        \"born\": 1901, \"died\": 1994,\n        \"description\": \"\"\"\n        American chemist and peace activist. One of only two people to win two\n        Nobel prizes in different fields (chemistry and peace).\n        \"\"\"\n    },\n    \"Freddie Mercury\": {\n        \"born\": 1946, \"died\": 1991,\n        \"description\": \"\"\"\n        British musician, best known as the lead singer of the rock band\n        Queen.\n        \"\"\"\n    },\n    \"Marie Fredriksson\": {\n        \"born\": 1958, \"died\": 2019,\n        \"description\": \"\"\"\n        Swedish multi-instrumentalist, mainly known as the lead singer and\n        keyboardist of the band Roxette.\n        \"\"\"\n    },\n    \"Paul Erdos\": {\n        \"born\": 1913, \"died\": 1996,\n        \"description\": \"\"\"\n        Hungarian mathematician, known for his eccentric personality almost\n        as much as his contributions to many different fields of mathematics.\n        \"\"\"\n    },\n    \"Maryam Mirzakhani\": {\n        \"born\": 1977, \"died\": 2017,\n        \"description\": \"\"\"\n        Iranian mathematician. The first woman ever to win the Fields medal\n        for her contributions to mathematics.\n        \"\"\"\n    },\n    \"Masako Natsume\": {\n        \"born\": 1957, \"died\": 1985,\n        \"description\": \"\"\"\n        Japanese actress. She was very famous in Japan but was primarily\n        known elsewhere in the world for her portrayal of Tripitaka in the\n        TV series Monkey.\n        \"\"\"\n    },\n    \"Chaim Topol\": {\n        \"born\": 1935, \"died\": 2023,\n        \"description\": \"\"\"\n        Israeli actor and singer, usually credited simply as 'Topol'. He was\n        best known for his many appearances as Tevye in the musical Fiddler\n        on the Roof.\n        \"\"\"\n    }\n}","section_id":"create-the-data"},{"id":"add-the-data-to-a-vector-set-ex0","language":"python","code":"r = redis.Redis(decode_responses=True)\n\nfor name, details in peopleData.items():\n    emb = model.encode(details[\"description\"]).astype(np.float32).tobytes()\n\n    r.vset().vadd(\n        \"famousPeople\",\n        emb,\n        name,\n        attributes={\n            \"born\": details[\"born\"],\n            \"died\": details[\"died\"]\n        }\n    )","section_id":"add-the-data-to-a-vector-set"},{"id":"query-the-vector-set-ex0","language":"python","code":"query_value = \"actors\"\n\nactors_results = r.vset().vsim(\n    \"famousPeople\",\n    model.encode(query_value).astype(np.float32).tobytes(),\n)\n\nprint(f\"'actors': {actors_results}\")","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex1","language":"plaintext","code":"'actors': ['Masako Natsume', 'Chaim Topol', 'Linus Pauling',\n'Marie Fredriksson', 'Maryam Mirzakhani', 'Marie Curie',\n'Freddie Mercury', 'Paul Erdos']","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex2","language":"python","code":"query_value = \"actors\"\n\ntwo_actors_results = r.vset().vsim(\n    \"famousPeople\",\n    model.encode(query_value).astype(np.float32).tobytes(),\n    count=2\n)\n\nprint(f\"'actors (2)': {two_actors_results}\")\n# >>> 'actors (2)': ['Masako Natsume', 'Chaim Topol']","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex3","language":"python","code":"query_value = \"entertainer\"\n\nentertainer_results = r.vset().vsim(\n    \"famousPeople\",\n    model.encode(query_value).astype(np.float32).tobytes()\n)\n\nprint(f\"'entertainer': {entertainer_results}\")\n# >>> 'entertainer': ['Chaim Topol', 'Freddie Mercury',\n# 'Marie Fredriksson', 'Masako Natsume', 'Linus Pauling',\n# 'Paul Erdos', 'Maryam Mirzakhani', 'Marie Curie']","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex4","language":"plaintext","code":"'science': ['Marie Curie', 'Linus Pauling', 'Maryam Mirzakhani',\n'Paul Erdos', 'Marie Fredriksson', 'Freddie Mercury', 'Masako Natsume',\n'Chaim Topol']","section_id":"query-the-vector-set"},{"id":"query-the-vector-set-ex5","language":"python","code":"query_value = \"science\"\n\nscience2000_results = r.vset().vsim(\n    \"famousPeople\",\n    model.encode(query_value).astype(np.float32).tobytes(),\n    filter=\".died < 2000\"\n)\n\nprint(f\"'science2000': {science2000_results}\")\n# >>> 'science2000': ['Marie Curie', 'Linus Pauling',\n# 'Paul Erdos', 'Freddie Mercury', 'Masako Natsume']","section_id":"query-the-vector-set"}]}
{"schema_version":2,"id":"develop/clients/redis-vl","title":"Redis vector library guide (Python)","url":"https://redis.io/docs/latest/develop/clients/redis-vl/","summary":"Connect your Python vector application to a Redis vector database","aliases":["/develop/connect/clients/python/redis-vl","/develop/clients/python/redis-vl/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-09T13:53:13+01:00","page_type":"content","content_hash":"b1ba298e9e466527fcfe3460922da732b303e1aef46362254b7944cc2551519a","sections":[{"id":"content","title":"Content","role":"content","text":"See the [RedisVL Guide](https://redis.io/docs/latest/develop/ai/redisvl) for more information."}],"examples":[]}
{"schema_version":2,"id":"develop/clients/ruby/connect","title":"Connect to the server","url":"https://redis.io/docs/latest/develop/clients/ruby/connect/","summary":"Connect your Ruby application to a Redis database","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-12T12:46:23+01:00","page_type":"content","content_hash":"0d6a227b3146fe4e667c4914cff9b0af7ca849f1ec916becc3529fa50dabda70","sections":[{"id":"basic-connection","title":"Basic connection","role":"overview","text":"Create a `Redis` object with the host, port, and database number of your server. The\ndefaults are `localhost`, port 6379, and database 0. If you don't pass a host, port, socket\npath, or URL, the client uses the URL in the `REDIS_URL` environment variable, if it's set.\n\n[code example]\n\nStore and retrieve a string to test the connection.\n\n[code example]\n\nTo connect as an [access control list (ACL)](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl)\nuser, pass `username` and `password`:\n\n[code example]\n\nYou can also pass the connection details as a `redis://` URL. If you pass other options\nwith `url`, such as `password`, those options override the values in the URL.\n\n[code example]\n\nThe client URL-decodes the username and password, so you must URL-encode any special\ncharacters in them, such as `@`, `:`, `/`, or `+`:\n\n[code example]\n\nTo connect through a Unix socket, pass the socket path in `path`, or use a `unix://` URL:\n\n[code example]"},{"id":"connect-to-a-redis-cluster","title":"Connect to a Redis cluster","role":"content","text":"Cluster support is in the separate `redis-clustering` gem:\n\n[code example]\n\nCreate a `Redis::Cluster` object with a list of cluster nodes. The list doesn't need to\ninclude every node, because the client discovers the rest of the cluster from the nodes\nyou list.\n\n[code example]\n\nTo send read-only commands to replicas, set `replica: true`. Use `replica_affinity` to\nchoose a replica: `:random` (the default) picks a random replica, `:random_with_primary`\npicks from the replicas and the primary, and `:latency` picks the replica with the lowest\nlatency. Replication is asynchronous, so a replica can return stale data.\n\n[code example]"},{"id":"connect-to-redis-sentinel","title":"Connect to Redis Sentinel","role":"content","text":"To connect through [Redis Sentinel](https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel),\npass `sentinels` and `name` to `Redis.new`. Set `name` to the name of the primary that the\nSentinels monitor. The client asks the Sentinels for the current address of the primary.\n\n[code example]\n\nThe Sentinels and the data nodes have separate credentials, and neither inherits the\nother's. Set `sentinel_password` (and `sentinel_username`, if your Sentinels use ACL users)\nfor the Sentinels, and `username` and `password` for the primary and replicas:\n\n[code example]\n\nThe Sentinel connections use your other options, such as the timeouts and `ssl`.\n\nTo connect to a replica, set `role: :replica`. The client connects to a random replica.\nReplication is asynchronous, so a replica can return stale data, and writes fail with\n`Redis::ReadOnlyError`.\n\n[code example]\n\nredis-rb doesn't listen for failover announcements from the Sentinels. After a failover,\nthe client keeps using the old primary until Sentinel reconfigures it as a replica and it\nstarts rejecting writes. The client then reconnects to the new primary. Any writes that the\nold primary accepts during that time are lost."},{"id":"connect-to-your-production-redis-with-tls","title":"Connect to your production Redis with TLS","role":"content","text":"When you deploy your application, use Transport Layer Security (TLS) and follow the\n[Redis security](https://redis.io/docs/latest/operate/oss_and_stack/management/security) guidelines.\n\nSet `ssl: true`, or use a `rediss://` URL, and pass the TLS settings in `ssl_params`. Set\n`ca_file` to the certificate authority (CA) certificate that signed the server's\ncertificate:\n\n[code example]\n\nIf the server requires clients to authenticate with a certificate, also pass the client\ncertificate and private key in `cert` and `key`:\n\n[code example]\n\n`cert` and `key` also accept file paths, such as `cert: \"./redis_user.crt\"`. If you use the\n`hiredis` driver, `cert` and `key` must be file paths."},{"id":"connect-with-a-connection-pool","title":"Connect with a connection pool","role":"content","text":"A `Redis` object holds a single connection, and a lock serializes access to it. You can\nshare one object between threads safely, but each thread waits for the others to finish\ntheir commands. To run commands from several threads in parallel, use a pool of `Redis`\nobjects from the `connection_pool` gem:\n\n[code example]\n\nCreate a `ConnectionPool` and call `with` to check out a connection for a block of\ncommands:\n\n[code example]\n\n`ConnectionPool::Wrapper` wraps a pool in an object that you call like a `Redis` object.\nEach method call checks out a connection for that call only, so consecutive calls can run on\ndifferent connections. Use `with` when several commands must run on the same connection.\n\n[code example]\n\nSee [Connection pools and multiplexing](https://redis.io/docs/latest/develop/clients/pools-and-muxing) for\nmore information."},{"id":"timeouts-and-reconnection","title":"Timeouts and reconnection","role":"content","text":"The connect, read, and write timeouts default to 1 second. Set `timeout` to change all\nthree, or set `connect_timeout`, `read_timeout`, and `write_timeout` individually. All\nvalues are in seconds.\n\n[code example]\n\nAfter a connection error, the client reconnects and retries the command once by default.\nSet `reconnect_attempts` to an `Integer` to change the number of retries, or to an `Array`\nof delays in seconds to wait before each retry:\n\n[code example]\n\nA retry can run a command twice: if the connection fails after the server runs a command\nbut before the client reads the reply, the client sends the command again. For commands\nthat aren't idempotent, such as `INCR` or `LPUSH`, set `reconnect_attempts: 0`, or run them\ninside `without_reconnect`:\n\n[code example]"},{"id":"protocol","title":"Protocol","role":"content","text":"Starting with redis-rb 6.0, the client uses\n[RESP3](https://redis.io/docs/latest/develop/reference/protocol-spec#resp-versions) (Redis serialization\nprotocol version 3) by default. If the server doesn't support RESP3, the client falls\nback to RESP2 and prints a warning. To use RESP2, set `protocol: 2`:\n\n[code example]"}],"examples":[{"id":"basic-connection-ex0","language":"ruby","code":"require \"redis\"\n\nr = Redis.new(host: \"localhost\", port: 6379, db: 0)","section_id":"basic-connection"},{"id":"basic-connection-ex1","language":"ruby","code":"r.set(\"foo\", \"bar\")\n# => \"OK\"\n\nr.get(\"foo\")\n# => \"bar\"","section_id":"basic-connection"},{"id":"basic-connection-ex2","language":"ruby","code":"r = Redis.new(\n  host: \"localhost\",\n  port: 6379,\n  username: \"default\", # use your Redis user\n  password: \"secret\",  # use your Redis password\n)","section_id":"basic-connection"},{"id":"basic-connection-ex3","language":"ruby","code":"r = Redis.new(url: \"redis://default:secret@localhost:6379/0\")","section_id":"basic-connection"},{"id":"basic-connection-ex4","language":"ruby","code":"require \"uri\"\n\npassword = URI.encode_www_form_component(\"p@ss/word+1\")\nr = Redis.new(url: \"redis://default:#{password}@localhost:6379\")","section_id":"basic-connection"},{"id":"basic-connection-ex5","language":"ruby","code":"r = Redis.new(path: \"/var/run/redis/redis.sock\")\n\nr = Redis.new(url: \"unix:///var/run/redis/redis.sock\")","section_id":"basic-connection"},{"id":"connect-to-a-redis-cluster-ex0","language":"bash","code":"gem install redis-clustering","section_id":"connect-to-a-redis-cluster"},{"id":"connect-to-a-redis-cluster-ex1","language":"ruby","code":"require \"redis-clustering\"\n\nrc = Redis::Cluster.new(nodes: [\n  \"redis://localhost:16379\",\n  \"redis://localhost:16380\",\n  \"redis://localhost:16381\",\n])\n\nrc.set(\"foo\", \"bar\")\n# => \"OK\"\n\nrc.get(\"foo\")\n# => \"bar\"","section_id":"connect-to-a-redis-cluster"},{"id":"connect-to-a-redis-cluster-ex2","language":"ruby","code":"rc = Redis::Cluster.new(\n  nodes: [\"redis://localhost:16379\"],\n  replica: true,\n  replica_affinity: :latency,\n)","section_id":"connect-to-a-redis-cluster"},{"id":"connect-to-redis-sentinel-ex0","language":"ruby","code":"require \"redis\"\n\nSENTINELS = [\n  { host: \"localhost\", port: 26379 },\n  { host: \"localhost\", port: 26380 },\n  { host: \"localhost\", port: 26381 },\n]\n\nr = Redis.new(name: \"mymaster\", sentinels: SENTINELS)\n\nr.set(\"foo\", \"bar\")\n# => \"OK\"\n\nr.get(\"foo\")\n# => \"bar\"","section_id":"connect-to-redis-sentinel"},{"id":"connect-to-redis-sentinel-ex1","language":"ruby","code":"r = Redis.new(\n  name: \"mymaster\",\n  sentinels: SENTINELS,\n  sentinel_password: \"sentinel-secret\", # use your Sentinel password\n  username: \"default\",                  # use your Redis user\n  password: \"secret\",                   # use your Redis password\n)","section_id":"connect-to-redis-sentinel"},{"id":"connect-to-redis-sentinel-ex2","language":"ruby","code":"replica = Redis.new(name: \"mymaster\", sentinels: SENTINELS, role: :replica)\n\nreplica.get(\"foo\")\n# => \"bar\"","section_id":"connect-to-redis-sentinel"},{"id":"connect-to-your-production-redis-with-tls-ex0","language":"ruby","code":"r = Redis.new(\n  url: \"rediss://default:secret@my-redis.example.com:6379\",\n  ssl_params: { ca_file: \"./redis_ca.pem\" },\n)","section_id":"connect-to-your-production-redis-with-tls"},{"id":"connect-to-your-production-redis-with-tls-ex1","language":"ruby","code":"r = Redis.new(\n  host: \"my-redis.example.com\",\n  port: 6379,\n  username: \"default\", # use your Redis user\n  password: \"secret\",  # use your Redis password\n  ssl: true,\n  ssl_params: {\n    ca_file: \"./redis_ca.pem\",\n    cert: OpenSSL::X509::Certificate.new(File.read(\"./redis_user.crt\")),\n    key: OpenSSL::PKey.read(File.read(\"./redis_user_private.key\")),\n  },\n)\n\nr.set(\"foo\", \"bar\")\n# => \"OK\"\n\nr.get(\"foo\")\n# => \"bar\"","section_id":"connect-to-your-production-redis-with-tls"},{"id":"connect-with-a-connection-pool-ex0","language":"bash","code":"gem install connection_pool","section_id":"connect-with-a-connection-pool"},{"id":"connect-with-a-connection-pool-ex1","language":"ruby","code":"require \"connection_pool\"\nrequire \"redis\"\n\npool = ConnectionPool.new(size: 5, timeout: 5) do\n  Redis.new(host: \"localhost\", port: 6379)\nend\n\npool.with do |r|\n  r.set(\"foo\", \"bar\")\n  r.get(\"foo\")\nend\n# => \"bar\"","section_id":"connect-with-a-connection-pool"},{"id":"connect-with-a-connection-pool-ex2","language":"ruby","code":"redis = ConnectionPool::Wrapper.new(size: 5, timeout: 5) do\n  Redis.new(host: \"localhost\", port: 6379)\nend\n\nredis.set(\"foo\", \"bar\")\nredis.get(\"foo\")\n# => \"bar\"","section_id":"connect-with-a-connection-pool"},{"id":"timeouts-and-reconnection-ex0","language":"ruby","code":"r = Redis.new(\n  host: \"localhost\",\n  port: 6379,\n  connect_timeout: 0.5,\n  read_timeout: 2.0,\n  write_timeout: 2.0,\n)","section_id":"timeouts-and-reconnection"},{"id":"timeouts-and-reconnection-ex1","language":"ruby","code":"r = Redis.new(host: \"localhost\", port: 6379, reconnect_attempts: [0.05, 0.25, 1.0])","section_id":"timeouts-and-reconnection"},{"id":"timeouts-and-reconnection-ex2","language":"ruby","code":"r.without_reconnect do\n  r.incr(\"counter\")\nend","section_id":"timeouts-and-reconnection"},{"id":"protocol-ex0","language":"ruby","code":"r = Redis.new(host: \"localhost\", port: 6379, protocol: 2)","section_id":"protocol"}]}
{"schema_version":2,"id":"develop/clients/ruby/error-handling","title":"Error handling","url":"https://redis.io/docs/latest/develop/clients/ruby/error-handling/","summary":"Learn how to handle errors when using redis-rb.","tags":[],"last_updated":"2026-09-09T11:41:34+01:00","page_type":"content","content_hash":"f90f3ce83bab4b1baf5a4f7b0afaff93105baa5069e84308cc3a50bbcd546ce5","sections":[{"id":"overview","title":"Overview","role":"overview","text":"`redis-rb` uses **exceptions** to signal errors. Most documentation examples\nfocus on the \"happy path\", but production code should handle connection and\ncommand failures explicitly. This page explains how error handling works in\n`redis-rb` and how to apply common error handling patterns.\n\nFor an overview of error types and handling strategies, see\n[Error handling](https://redis.io/docs/latest/develop/clients/error-handling)."},{"id":"exception-hierarchy","title":"Exception hierarchy","role":"errors","text":"`redis-rb` organizes exceptions under `Redis::BaseError`:\n\n[code example]"},{"id":"key-exceptions","title":"Key exceptions","role":"content","text":"The following exceptions are the most commonly encountered in `redis-rb`\napplications. See\n[Categories of errors](https://redis.io/docs/latest/develop/clients/error-handling#categories-of-errors)\nfor a more detailed discussion of these errors and their causes.\n\n| Exception | When it occurs | Recoverable | Recommended action |\n|---|---|---|---|\n| `Redis::CannotConnectError` | A connection could not be established | ✅ | Retry with backoff or fall back |\n| `Redis::TimeoutError` | A read or blocking operation timed out | ✅ | Retry with backoff and review timeouts |\n| `Redis::CommandError` | Redis returned an error reply such as `ERR` or `WRONGTYPE` | ❌ | Fix the command, arguments, or data model |\n| `Redis::ConnectionError` | An established connection was lost | ✅ | Reconnect and retry cautiously |"},{"id":"applying-error-handling-patterns","title":"Applying error handling patterns","role":"content","text":"The [Error handling](https://redis.io/docs/latest/develop/clients/error-handling) overview\ndescribes four main patterns. The sections below show how to implement them in\n`redis-rb`:"},{"id":"pattern-1-fail-fast","title":"Pattern 1: Fail fast","role":"content","text":"Catch specific exceptions that represent unrecoverable errors and re-raise them\n(see\n[Pattern 1: Fail fast](https://redis.io/docs/latest/develop/clients/error-handling#pattern-1-fail-fast)\nfor a full description):\n\n[code example]"},{"id":"pattern-2-graceful-degradation","title":"Pattern 2: Graceful degradation","role":"content","text":"Catch connection failures and fall back to an alternative (see\n[Pattern 2: Graceful degradation](https://redis.io/docs/latest/develop/clients/error-handling#pattern-2-graceful-degradation)\nfor a full description):\n\n[code example]"},{"id":"pattern-3-retry-with-backoff","title":"Pattern 3: Retry with backoff","role":"content","text":"Retry on temporary errors such as timeouts (see\n[Pattern 3: Retry with backoff](https://redis.io/docs/latest/develop/clients/error-handling#pattern-3-retry-with-backoff)\nfor a full description):\n\n[code example]"},{"id":"pattern-4-log-and-continue","title":"Pattern 4: Log and continue","role":"content","text":"Log non-critical failures and continue (see\n[Pattern 4: Log and continue](https://redis.io/docs/latest/develop/clients/error-handling#pattern-4-log-and-continue)\nfor a full description):\n\n[code example]"},{"id":"transaction-conflicts","title":"Transaction conflicts","role":"content","text":"When a watched transaction in `redis-rb` loses an optimistic-locking race,\n`multi()` returns `nil` instead of raising an exception. Treat that as a\nretryable conflict:\n\n[code example]"},{"id":"see-also","title":"See also","role":"related","text":"- [Error handling](https://redis.io/docs/latest/develop/clients/error-handling)\n- [Pipelines and transactions](https://redis.io/docs/latest/develop/clients/ruby/transpipe)"}],"examples":[{"id":"exception-hierarchy-ex0","language":"hierarchy {type=\"exception\"}","code":"\"Redis::BaseError\":\n    _meta:\n        description: \"Base class for redis-rb exceptions\"\n    \"Redis::ProtocolError\":\n    \"Redis::CommandError\":\n        \"Redis::PermissionError\":\n        \"Redis::WrongTypeError\":\n        \"Redis::OutOfMemoryError\":\n        \"Redis::NoScriptError\":\n    \"Redis::BaseConnectionError\":\n        \"Redis::CannotConnectError\":\n        \"Redis::ConnectionError\":\n        \"Redis::TimeoutError\":\n        \"Redis::ReadOnlyError\":\n    \"...\":\n        _meta:\n            ellipsis: true\n            description: \"Other redis-rb exception types\"","section_id":"exception-hierarchy"},{"id":"pattern-1-fail-fast-ex0","language":"ruby","code":"begin\n  redis.get(key)\nrescue Redis::CommandError\n  # This indicates a bug in our code or schema.\n  raise\nend","section_id":"pattern-1-fail-fast"},{"id":"pattern-2-graceful-degradation-ex0","language":"ruby","code":"begin\n  cached_value = redis.get(key)\n  return cached_value unless cached_value.nil?\nrescue Redis::CannotConnectError, Redis::ConnectionError\n  logger.warn(\"Cache unavailable, using database\")\nend\n\ndatabase.get(key)","section_id":"pattern-2-graceful-degradation"},{"id":"pattern-3-retry-with-backoff-ex0","language":"ruby","code":"delay = 0.1\n\n3.times do |attempt|\n  begin\n    return redis.get(key)\n  rescue Redis::CannotConnectError, Redis::ConnectionError, Redis::TimeoutError => e\n    raise e if attempt == 2\n\n    sleep(delay)\n    delay *= 2\n  end\nend","section_id":"pattern-3-retry-with-backoff"},{"id":"pattern-4-log-and-continue-ex0","language":"ruby","code":"begin\n  redis.setex(key, 3600, value)\nrescue Redis::CannotConnectError, Redis::ConnectionError, Redis::TimeoutError\n  logger.warn(\"Failed to cache #{key}, continuing without cache\")\nend","section_id":"pattern-4-log-and-continue"},{"id":"transaction-conflicts-ex0","language":"ruby","code":"result = redis.watch(key) do |client|\n  current = client.get(key)\n\n  client.multi do |tx|\n    tx.set(key, current.upcase)\n  end\nend\n\nretry_transaction if result.nil?","section_id":"transaction-conflicts"}]}
{"schema_version":2,"id":"develop/clients/ruby","title":"redis-rb guide (Ruby)","url":"https://redis.io/docs/latest/develop/clients/ruby/","summary":"Connect your Ruby application to a Redis database","aliases":["/connect/clients/ruby/","/clients/ruby/","/develop/clients/redis-rb"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-12T14:48:04+01:00","children":[{"id":"develop/clients/ruby/connect","summary":"Connect your Ruby application to a Redis database","title":"Connect to the server","url":"https://redis.io/docs/latest/develop/clients/ruby/connect/"},{"id":"develop/clients/ruby/queryjson","summary":"Learn how to use Redis Search with JSON and hash documents.","title":"Index and query documents","url":"https://redis.io/docs/latest/develop/clients/ruby/queryjson/"},{"id":"develop/clients/ruby/vecsearch","summary":"Learn how to index and query vector embeddings with Redis","title":"Index and query vectors","url":"https://redis.io/docs/latest/develop/clients/ruby/vecsearch/"},{"id":"develop/clients/ruby/transpipe","summary":"Learn how to use Redis pipelines and transactions","title":"Pipelines and transactions","url":"https://redis.io/docs/latest/develop/clients/ruby/transpipe/"},{"id":"develop/clients/ruby/error-handling","summary":"Learn how to handle errors when using redis-rb.","title":"Error handling","url":"https://redis.io/docs/latest/develop/clients/ruby/error-handling/"}],"page_type":"content","content_hash":"b43e70929db99f404b1824123c45406de3a1419fdda59a56b26db69e3a6bafb1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[redis-rb](https://github.com/redis/redis-rb) is the Ruby client for Redis.\nThe sections below explain how to install `redis-rb` and connect your application\nto a Redis database.\n\n`redis-rb` requires a running Redis server. See [here](https://redis.io/docs/latest/operate/oss_and_stack/install) for Redis Open Source installation instructions."},{"id":"install","title":"Install","role":"setup","text":"To install `redis-rb`, run the following command:\n\n[code example]"},{"id":"connect-and-test","title":"Connect and test","role":"content","text":"Connect to localhost on port 6379:\n\nFoundational: Connect to a Redis server and establish a client connection\n\n[code example]\n\n\n\n> [!NOTE]\n> redis-rb supports [connection URLs](https://redis.io/docs/latest/develop/clients/connection-urls#redis-rb-ruby) through the `url` option of `Redis.new`.\n> A URL can set the host, port, credentials, database, and whether to use TLS,\n> so you can use one instead of passing those options separately.\n\nStore and retrieve a simple string.\n\nFoundational: Set and retrieve string values using SET and GET commands\n\n[code example]\n\n\n\nStore and retrieve a dict.\n\nFoundational: Store and retrieve hash data structures using HSET and HGETALL\n\n[code example]\n\n\n\nClose the connection when you're done.\n\nFoundational: Properly close a Redis client connection to release resources\n\n[code example]\n\n\n\nTo connect to a cluster, through Redis Sentinel, or over TLS, see\n[Connect to the server](https://redis.io/docs/latest/develop/clients/ruby/connect)."},{"id":"more-information","title":"More information","role":"content","text":"The\n[GitHub repository](https://github.com/redis/redis-rb) for `redis-rb` has a\nset of [examples](https://github.com/redis/redis-rb/tree/master/examples)\nand further information about using redis-rb."}],"examples":[{"id":"install-ex0","language":"bash","code":"gem install redis","section_id":"install"},{"id":"connect-and-test-ex0","language":"ruby","code":"require 'redis'\nr = Redis.new","section_id":"connect-and-test"},{"id":"connect-and-test-ex1","language":"ruby","code":"r.set 'foo', 'bar'\nvalue = r.get('foo')\nputs value","section_id":"connect-and-test"},{"id":"connect-and-test-ex2","language":"ruby","code":"r.hset 'user-session:123', 'name', 'John'\nr.hset 'user-session:123', 'surname', 'Smith'\nr.hset 'user-session:123', 'company', 'Redis'\nr.hset 'user-session:123', 'age', 29\n\nhash_value = r.hgetall('user-session:123')\nputs hash_value","section_id":"connect-and-test"},{"id":"connect-and-test-ex3","language":"ruby","code":"r.close()","section_id":"connect-and-test"}]}
{"schema_version":2,"id":"develop/clients/ruby/queryjson","title":"Index and query documents","url":"https://redis.io/docs/latest/develop/clients/ruby/queryjson/","summary":"Learn how to use Redis Search with JSON and hash documents.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-16T16:25:49+01:00","page_type":"content","content_hash":"0142b5ab5cbbf5d4abc151c62d30ba2ade40549d2ebb289c06c900ecbc552c89","sections":[{"id":"initialize","title":"Initialize","role":"content","text":"Make sure that you have [Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack)\nor another Redis server available. Also install the\n[`redis-rb`](https://redis.io/docs/latest/develop/clients/ruby) client library if you\nhaven't already done so.\n\nRequire the `redis` gem. The Query Engine classes live under the\n`Redis::Commands::Search` namespace, so the example below aliases it to\n`Search` to keep the code concise.\n\nFoundational: Require the redis gem and alias the Query Engine namespace\n\n**Difficulty:** Beginner\n\n**Available in:** Ruby\n\n##### Ruby\n\n[code example]"},{"id":"create-data","title":"Create data","role":"content","text":"Create some test data to add to the database:\n\nFoundational: Define sample user data structures for indexing and querying\n\n**Difficulty:** Beginner\n\n**Available in:** Ruby\n\n##### Ruby\n\n[code example]"},{"id":"add-the-index","title":"Add the index","role":"content","text":"Connect to your Redis database. The code below shows the most\nbasic connection but see the\n[`redis-rb` guide](https://redis.io/docs/latest/develop/clients/ruby)\nto learn more about the available connection options.\n\nFoundational: Establish a connection to a Redis server for query operations\n\n**Difficulty:** Beginner\n\n**Available in:** Ruby\n\n##### Ruby\n\n[code example]\n\n\n\nDelete any existing index called `idx:users` and any keys that start with `user:`.\n\nFoundational: Clean up existing indexes and data before creating new indexes\n\n**Difficulty:** Beginner\n\n**Available in:** Ruby\n\n##### Ruby\n\n[code example]\n\n\n\nCreate an index. In this example, only JSON documents with the key prefix `user:` are indexed. For more information, see [Query syntax](https://redis.io/docs/latest/develop/ai/search-and-query/query).\n\nBuild the schema with the field type helpers (`text_field`, `tag_field`,\n`numeric_field`) inside a `Search::Schema.build` block. Each field's first\nargument is the [JSON path](https://redis.io/docs/latest/develop/data-types/json/path) to\nthe value, and the `as:` option gives the field an alias you can refer to in\nqueries.\n\nFoundational: Create a search index for JSON documents with field definitions and key prefix filtering\n\n**Difficulty:** Intermediate\n\n**Available in:** Ruby\n\n##### Ruby\n\n[code example]"},{"id":"add-the-data","title":"Add the data","role":"content","text":"Add the three sets of user data to the database as\n[JSON](https://redis.io/docs/latest/develop/data-types/json) objects.\nIf you use keys with the `user:` prefix then Redis will index the\nobjects automatically as you add them:\n\nFoundational: Store JSON documents in Redis with automatic indexing based on key prefix\n\n**Difficulty:** Beginner\n\n**Available in:** Ruby\n\n##### Ruby\n\n[code example]"},{"id":"query-the-data","title":"Query the data","role":"content","text":"You can now use the index to search the JSON objects. The\n[query](https://redis.io/docs/latest/develop/ai/search-and-query/query)\nbelow searches for objects that have the text \"Paul\" in any field\nand have an `age` value in the range 30 to 40:\n\nQuery with filters: Search JSON documents using text matching and numeric range filters to find specific records\n\n**Difficulty:** Intermediate\n\n**Available in:** Ruby\n\n##### Ruby\n\n[code example]\n\n\n\nBecause the index has the key prefix `user:`, the client strips that prefix\nfrom the returned document IDs and reports the logical ID (for example, `3`\nrather than `user:3`).\n\nUse a `Search::Query` object to specify query options, such as returning only\nthe `city` field:\n\nQuery with field projection: Retrieve only specific fields from search results to reduce data transfer\n\n**Difficulty:** Intermediate\n\n**Available in:** Ruby\n\n##### Ruby\n\n[code example]\n\n\n\nUse an\n[aggregation query](https://redis.io/docs/latest/develop/ai/search-and-query/query/aggregation)\nto count all users in each city.\n\nAggregation query: Group and count results by field values to analyze data patterns\n\n**Difficulty:** Intermediate\n\n**Available in:** Ruby\n\n##### Ruby\n\n[code example]"},{"id":"differences-with-hash-documents","title":"Differences with hash documents","role":"content","text":"Indexing for hash documents is very similar to JSON indexing but you\nneed to specify some slightly different options.\n\nWhen you create the schema for a hash index, you don't need to\nadd aliases for the fields, since you use the basic names to access\nthe fields anyway. Also, you must use `Search::IndexType::HASH` for the\n`index_type:` option of the `IndexDefinition` when you create the index. The code\nbelow shows these changes with a new index called `hash-idx:users`, which is\notherwise the same as the `idx:users` index used for JSON documents in the\nprevious examples.\n\nFirst, delete any existing index called `hash-idx:users` and any keys that start with `huser:`.\n\nFoundational: Clean up existing hash indexes and data before creating new indexes\n\n**Difficulty:** Beginner\n\n**Available in:** Ruby\n\n##### Ruby\n\n[code example]\n\n\n\nNow create the new index:\n\nFoundational: Create a search index for hash documents with field definitions and key prefix filtering\n\n**Difficulty:** Intermediate\n\n**Available in:** Ruby\n\n##### Ruby\n\n[code example]\n\n\n\nUse [`hset()`](https://redis.io/docs/latest/commands/hset) to add the hash\ndocuments instead of [`json_set()`](https://redis.io/docs/latest/commands/json.set).\n\nFoundational: Store hash documents in Redis with automatic indexing based on key prefix\n\n**Difficulty:** Beginner\n\n**Available in:** Ruby\n\n##### Ruby\n\n[code example]\n\n\n\nThe query commands work the same here for hash as they do for JSON (but\nthe name of the hash index is different). The results are returned as\n`Document` objects, as with JSON:\n\nQuery with filters: Search hash documents using text matching and numeric range filters (same as JSON queries)\n\n**Difficulty:** Intermediate\n\n**Available in:** Ruby\n\n##### Ruby\n\n[code example]"},{"id":"bulk-import-with-himport","title":"Bulk import with HIMPORT","role":"content","text":"From Redis Open Source 8.10, you can also add the hash documents with the\n[`HIMPORT`](https://redis.io/docs/latest/commands/himport) command family. This creates the same\nhashes as `hset()`, but lets Redis store them as\n[compact hashes](https://redis.io/docs/latest/develop/data-types/hashes#compact-hashes), which use\nless memory when many keys share the same field names. Redis Search indexes\nthese hashes in the same way.\n\nDeclare the field names once with\n[`himport_prepare()`](https://redis.io/docs/latest/commands/himport-prepare), then create each\ndocument with [`himport_set()`](https://redis.io/docs/latest/commands/himport-set), passing only\nits values, in the same order as the field names. Use a\n[pipeline](https://redis.io/docs/latest/develop/clients/ruby/transpipe) to send all the\nimports in one round trip:\n\nBulk ingestion: Import hash documents as compact hashes using HIMPORT PREPARE and a pipeline of HIMPORT SET calls\n\n**Difficulty:** Intermediate\n\n**Builds upon:** add_hash_data\n\n**Available in:** Ruby\n\n##### Ruby\n\n[code example]\n\n\n\n`himport_set()` replaces any existing key, so running this code after the\n`hset()` calls leaves you with the same three documents. The query returns\nthe same result, although the fields can appear in a different order.\n\n> [!NOTE]\n> `HIMPORT` requires `redis-rb` v6.0.0 or later, and `redis-rb` marks its\n> support as experimental, so the API might change. Redis Cloud and\n> Redis Software don't support `HIMPORT`."},{"id":"more-information","title":"More information","role":"content","text":"See the [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) docs\nfor a full description of all query features with examples."}],"examples":[{"id":"initialize-ex0","language":"ruby","code":"require 'redis'\n\n# A short alias for the Query Engine namespace to keep the code below readable.\nSearch = Redis::Commands::Search","section_id":"initialize"},{"id":"create-data-ex0","language":"ruby","code":"user1 = {\n  'name' => 'Paul John',\n  'email' => 'paul.john@example.com',\n  'age' => 42,\n  'city' => 'London'\n}\n\nuser2 = {\n  'name' => 'Eden Zamir',\n  'email' => 'eden.zamir@example.com',\n  'age' => 29,\n  'city' => 'Tel Aviv'\n}\n\nuser3 = {\n  'name' => 'Paul Zamir',\n  'email' => 'paul.zamir@example.com',\n  'age' => 35,\n  'city' => 'Tel Aviv'\n}","section_id":"create-data"},{"id":"add-the-index-ex0","language":"ruby","code":"r = Redis.new","section_id":"add-the-index"},{"id":"add-the-index-ex1","language":"ruby","code":"begin\n  r.ft_dropindex('idx:users', delete_documents: true)\nrescue Redis::CommandError\n  # Index doesn't exist, so there is nothing to drop.\nend\n\nr.del('user:1', 'user:2', 'user:3')","section_id":"add-the-index"},{"id":"add-the-index-ex2","language":"ruby","code":"schema = Search::Schema.build do\n  text_field '$.name', as: 'name'\n  tag_field '$.city', as: 'city'\n  numeric_field '$.age', as: 'age'\nend\n\ndefinition = Search::IndexDefinition.new(\n  prefix: ['user:'],\n  index_type: Search::IndexType::JSON\n)\n\nindex = r.create_index('idx:users', schema, definition: definition)\nputs index.name # >>> idx:users","section_id":"add-the-index"},{"id":"add-the-data-ex0","language":"ruby","code":"user1_set = r.json_set('user:1', '$', user1)\nuser2_set = r.json_set('user:2', '$', user2)\nuser3_set = r.json_set('user:3', '$', user3)\nputs [user1_set, user2_set, user3_set].inspect # >>> [\"OK\", \"OK\", \"OK\"]","section_id":"add-the-data"},{"id":"query-the-data-ex0","language":"ruby","code":"find_paul_result = index.search('Paul @age:[30 40]')\n\nputs find_paul_result.total # >>> 1\n# The index has the key prefix `user:`, so the client returns the\n# logical document id with that prefix removed.\nfind_paul_result.each { |doc| puts doc.id } # >>> 3","section_id":"query-the-data"},{"id":"query-the-data-ex1","language":"ruby","code":"cities_query = Search::Query.new('Paul').return_field('$.city', as_field: 'city')\ncities_result = index.search(cities_query)\n\ncities_result.documents.sort_by(&:id).each do |doc|\n  puts \"#{doc.id}: #{doc['city']}\"\nend\n# >>> 1: London\n# >>> 3: Tel Aviv","section_id":"query-the-data"},{"id":"query-the-data-ex2","language":"ruby","code":"request = Search::AggregateRequest.new('*')\n                                  .group_by('@city', Search::Reducers.count.as('count'))\n\nagg_result = index.aggregate(request)\n\nagg_result.rows.sort_by { |row| row['city'] }.each do |row|\n  puts \"#{row['city']} - #{row['count']}\"\nend\n# >>> London - 1\n# >>> Tel Aviv - 2","section_id":"query-the-data"},{"id":"differences-with-hash-documents-ex0","language":"ruby","code":"begin\n  r.ft_dropindex('hash-idx:users', delete_documents: true)\nrescue Redis::CommandError\n  # Index doesn't exist, so there is nothing to drop.\nend\n\nr.del('huser:1', 'huser:2', 'huser:3')","section_id":"differences-with-hash-documents"},{"id":"differences-with-hash-documents-ex1","language":"ruby","code":"hash_schema = Search::Schema.build do\n  text_field 'name'\n  tag_field 'city'\n  numeric_field 'age'\nend\n\nhash_definition = Search::IndexDefinition.new(\n  prefix: ['huser:'],\n  index_type: Search::IndexType::HASH\n)\n\nhash_index = r.create_index('hash-idx:users', hash_schema, definition: hash_definition)\nputs hash_index.name # >>> hash-idx:users","section_id":"differences-with-hash-documents"},{"id":"differences-with-hash-documents-ex2","language":"ruby","code":"huser1_set = r.hset('huser:1', user1)\nhuser2_set = r.hset('huser:2', user2)\nhuser3_set = r.hset('huser:3', user3)\nputs [huser1_set, huser2_set, huser3_set].inspect # >>> [4, 4, 4]","section_id":"differences-with-hash-documents"},{"id":"differences-with-hash-documents-ex3","language":"ruby","code":"find_paul_hash_result = hash_index.search('Paul @age:[30 40]')\n\nputs find_paul_hash_result.total # >>> 1\nfind_paul_hash_result.each do |doc|\n  puts \"#{doc.id}: #{doc['name']}, #{doc['city']}\"\nend\n# >>> 3: Paul Zamir, Tel Aviv","section_id":"differences-with-hash-documents"},{"id":"bulk-import-with-himport-ex0","language":"ruby","code":"fields = %w[name email age city]\n\nhimport_res = r.pipelined do |pipe|\n  pipe.himport_prepare('huser', fields)\n  [['huser:1', user1], ['huser:2', user2], ['huser:3', user3]].each do |key, user|\n    pipe.himport_set(key, 'huser', fields.map { |f| user[f] })\n  end\nend","section_id":"bulk-import-with-himport"}]}
{"schema_version":2,"id":"develop/clients/ruby/transpipe","title":"Pipelines and transactions","url":"https://redis.io/docs/latest/develop/clients/ruby/transpipe/","summary":"Learn how to use Redis pipelines and transactions","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-09T11:41:34+01:00","page_type":"content","content_hash":"69b385c19b7ac48c4541f74a4c680432ca78b35313ea6f3c7571835fe8222faf","sections":[{"id":"execute-a-pipeline","title":"Execute a pipeline","role":"content","text":"To execute commands in a pipeline with `redis-rb`, call `pipelined()` and queue\ncommands in the block. Redis executes them as a batch and `pipelined()` returns\nthe results in order:\n\nFoundational: Use pipelined to batch multiple commands together and reduce network round trips\n\n**Difficulty:** Beginner\n\n**Available in:** Ruby\n\n##### Ruby\n\n[code example]"},{"id":"execute-a-transaction","title":"Execute a transaction","role":"content","text":"Transactions use `multi()`. Commands queued inside the block run atomically\nwhen the block finishes, and `multi()` returns the results in order:\n\nFoundational: Use multi to execute multiple commands atomically without interruption from other clients\n\n**Difficulty:** Beginner\n\n**Available in:** Ruby\n\n##### Ruby\n\n[code example]"},{"id":"watch-keys-for-changes","title":"Watch keys for changes","role":"content","text":"Redis supports *optimistic locking* to avoid inconsistent updates to keys that\nseveral clients may modify at the same time. The basic idea is to watch for\nchanges to any keys that you use in a transaction while you are preparing the\nupdate. If the watched keys do change, the transaction returns `nil` and you\nmust retry using the latest value from Redis. See\n[Transactions](https://redis.io/docs/latest/develop/using-commands/transactions)\nfor more information about optimistic locking.\n\nThe example below watches a key, reads its current value, and then updates it\ninside `multi()`:\n\nOptimistic locking: Use watch with multi and retry when another client modifies the watched key\n\n**Difficulty:** Intermediate\n\n**Available in:** Ruby\n\n##### Ruby\n\n[code example]"}],"examples":[{"id":"execute-a-pipeline-ex0","language":"ruby","code":"r.pipelined do |pipe|\n  (0..4).each { |i| pipe.set(\"seat:#{i}\", \"##{i}\") }\nend\n\nseats = r.pipelined do |pipe|\n  pipe.get('seat:0')\n  pipe.get('seat:3')\n  pipe.get('seat:4')\nend\n\nputs seats[0] # >>> #0\nputs seats[1] # >>> #3\nputs seats[2] # >>> #4","section_id":"execute-a-pipeline"},{"id":"execute-a-transaction-ex0","language":"ruby","code":"trans_results = r.multi do |tx|\n  tx.incrby('counter:1', 1)\n  tx.incrby('counter:2', 2)\n  tx.incrby('counter:3', 3)\nend\n\nputs trans_results[0] # >>> 1\nputs trans_results[1] # >>> 2\nputs trans_results[2] # >>> 3","section_id":"execute-a-transaction"},{"id":"watch-keys-for-changes-ex0","language":"ruby","code":"r.set('shellpath', '/usr/syscmds/')\n\n# Watch the key, read its current value, then queue the update in a\n# transaction. `multi` returns `nil` if the watched key changed before\n# `EXEC` ran, in which case you would retry with the latest value.\nresult = r.watch('shellpath') do |client|\n  current_path = client.get('shellpath')\n  new_path = current_path + ':/usr/mycmds/'\n\n  client.multi do |tx|\n    tx.set('shellpath', new_path)\n  end\nend\n\nif result.nil?\n  puts 'Transaction aborted; retry with the latest value'\nelse\n  puts r.get('shellpath')\n  # >>> /usr/syscmds/:/usr/mycmds/\nend","section_id":"watch-keys-for-changes"}]}
{"schema_version":2,"id":"develop/clients/ruby/vecsearch","title":"Index and query vectors","url":"https://redis.io/docs/latest/develop/clients/ruby/vecsearch/","summary":"Learn how to index and query vector embeddings with Redis","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-16T16:25:49+01:00","page_type":"content","content_hash":"ff00c4409de53ab00e4ab2bda74e5c52bd8f01344be628413f94314c762d5a45","sections":[{"id":"initialize","title":"Initialize","role":"content","text":"Install [`redis-rb`](https://redis.io/docs/latest/develop/clients/ruby) if you\nhave not already done so. Also, install `informers` with the\nfollowing command:\n\n[code example]\n\nThe `informers` gem pulls in [`onnxruntime`](https://rubygems.org/gems/onnxruntime)\n(which ships the ONNX Runtime shared library as a native extension) and\n[`tokenizers`](https://rubygems.org/gems/tokenizers) (a Hugging Face fast-tokenizer\nbinding). Both come as pre-built binary gems for `arm64-darwin`, `x86_64-darwin`,\n`aarch64-linux`, and `x86_64-linux`, so there is no system ONNX install step on\nthose platforms."},{"id":"import-dependencies","title":"Import dependencies","role":"content","text":"In a new Ruby source file, start by requiring the libraries and declaring a\nshort alias for the Query Engine namespace:\n\nFoundational: Import required libraries for vector embeddings, Redis operations, and search functionality\n\n**Difficulty:** Beginner\n\n**Available in:** Ruby\n\n##### Ruby\n\n[code example]"},{"id":"initialize-the-embedding-model","title":"Initialize the embedding model","role":"content","text":"The [`Informers.pipeline`](https://github.com/ankane/informers#usage) method\nreturns a callable that generates an embedding from a section of text.\nHere, we create a pipeline that uses the\n[`all-MiniLM-L6-v2`](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2)\nmodel. This model generates vectors with 384 dimensions, regardless\nof the length of the input text, but note that the input is truncated to 256\ntokens (see\n[Word piece tokenization](https://huggingface.co/learn/nlp-course/en/chapter6/6)\nat the [Hugging Face](https://huggingface.co/) docs to learn more about the way tokens\nare related to the original text).\n\nFoundational: Initialize an informers pipeline to generate vector embeddings from text\n\n**Difficulty:** Beginner\n\n**Available in:** Ruby\n\n##### Ruby\n\n[code example]"},{"id":"define-a-helper-method","title":"Define a helper method","role":"content","text":"The embedding pipeline returns the vector as an `Array` of `Float` values.\nWhen you store a vector in a hash object, you must encode this array as a\nbinary string of raw little-endian `float32` values, which is the format\nRedis Search expects. Declare a helper method `to_bytes` that packs the\narray using Ruby's [`Array#pack`](https://docs.ruby-lang.org/en/master/Array.html#method-i-pack)\ndirective `'e*'`:\n\nFoundational: Create a helper method to pack float arrays into binary strings for vector storage in hash objects\n\n**Difficulty:** Beginner\n\n**Available in:** Ruby\n\n##### Ruby\n\n[code example]"},{"id":"create-the-index","title":"Create the index","role":"content","text":"Connect to Redis:\n\nFoundational: Connect to a Redis server\n\n**Difficulty:** Beginner\n\n**Available in:** Ruby\n\n##### Ruby\n\n[code example]\n\n\n\nNext, delete any index previously created with the name `vector_idx`\n(the `ft_dropindex` call raises an exception if the index doesn't already exist,\nwhich is why you need the `begin`/`rescue` block) and create the index.\nThe schema in the example below specifies hash objects for storage and includes\nthree fields: the text content to index, a\n[tag](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/tags)\nfield to represent the \"genre\" of the text, and the embedding vector generated from\nthe original text content. The `embedding` field specifies\n[HNSW](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#hnsw-index)\nindexing, the\n[L2](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#distance-metrics)\nvector distance metric, `FLOAT32` values to represent the vector's components,\nand 384 dimensions, as required by the `all-MiniLM-L6-v2` embedding model.\n\nFoundational: Create a vector search index for hash documents with HNSW algorithm and L2 distance metric\n\n**Difficulty:** Intermediate\n\n**Available in:** Ruby\n\n##### Ruby\n\n[code example]"},{"id":"add-data","title":"Add data","role":"content","text":"You can now supply the data objects, which will be indexed automatically\nwhen you add them with [`hset()`](https://redis.io/docs/latest/commands/hset), as long as\nyou use the `doc:` prefix specified in the index definition.\n\nCall the pipeline with `pooling: 'mean'` and `normalize: true` to create the\nembedding that represents the `content` field, then pass the result through the\n`to_bytes` helper to get the binary string that Redis Search indexes.\n\nFoundational: Store hash documents with vector embeddings generated from text content\n\n**Difficulty:** Beginner\n\n**Available in:** Ruby\n\n##### Ruby\n\n[code example]"},{"id":"bulk-import-with-himport","title":"Bulk import with HIMPORT","role":"content","text":"From Redis Open Source 8.10, you can also add the documents with the\n[`HIMPORT`](https://redis.io/docs/latest/commands/himport) command family. This creates the same\nhashes as `hset()`, but lets Redis store them as\n[compact hashes](https://redis.io/docs/latest/develop/data-types/hashes#compact-hashes), which use\nless memory when many keys share the same field names. Redis Search indexes\nthese hashes in the same way.\n\nDeclare the field names once with\n[`himport_prepare()`](https://redis.io/docs/latest/commands/himport-prepare), then create each\ndocument with [`himport_set()`](https://redis.io/docs/latest/commands/himport-set), passing only\nits values, in the same order as the field names. Use a\n[pipeline](https://redis.io/docs/latest/develop/clients/ruby/transpipe) to send all the\nimports in one round trip:\n\nBulk ingestion: Import hash documents with vector embeddings as compact hashes using HIMPORT PREPARE and a pipeline of HIMPORT SET calls\n\n**Difficulty:** Intermediate\n\n**Builds upon:** add_data\n\n**Available in:** Ruby\n\n##### Ruby\n\n[code example]\n\n\n\n`himport_set()` replaces any existing key, so running this code after the\n`hset()` calls leaves you with the same three documents.\n\n> [!NOTE]\n> `HIMPORT` requires `redis-rb` v6.0.0 or later, and `redis-rb` marks its\n> support as experimental, so the API might change. Redis Cloud and\n> Redis Software don't support `HIMPORT`."},{"id":"run-a-query","title":"Run a query","role":"content","text":"After you have created the index and added the data, you are ready to run a query.\nTo do this, you must create another embedding vector from your chosen query\ntext. Redis calculates the similarity between the query vector and each\nembedding vector in the index as it runs the query. It then ranks the\nresults in order of this numeric similarity value.\n\nThe code below creates the query embedding, packs it with the `to_bytes` helper,\nand passes it as a query parameter (see\n[Vector search](https://redis.io/docs/latest/develop/ai/search-and-query/query/vector-search)\nfor more information about using query parameters with embeddings).\n\nVector similarity search: Find semantically similar documents by comparing query embeddings with indexed vectors using L2 distance\n\n**Difficulty:** Intermediate\n\n**Available in:** Ruby\n\n##### Ruby\n\n[code example]\n\n\n\nThe code is now ready to run, but note that it may take a while to complete when\nyou run it for the first time (which happens because `informers` must download the\n`all-MiniLM-L6-v2` model data before it can generate the embeddings). When you run\nthe code, it outputs the following result (the exact distances may differ slightly\non your system):\n\n[code example]\n\nNote that the results are ordered according to the value of the `vector_distance`\nfield, with the lowest distance indicating the greatest similarity to the query.\nAs you would expect, the result for `doc:0` with the content text *\"That is a very happy person\"*\nis the result that is most similar in meaning to the query text\n*\"That is a happy person\"*.\n\nNote also that `redis-rb` strips the key prefix (`doc:`) that the index\ndefinition specifies, so the document id returned in the result is `0` rather\nthan `doc:0`."},{"id":"differences-with-json-documents","title":"Differences with JSON documents","role":"content","text":"Indexing JSON documents is similar to hash indexing, but there are some\nimportant differences. JSON allows much richer data modelling with nested fields, so\nyou must supply a [path](https://redis.io/docs/latest/develop/data-types/json/path) in the schema\nto identify each field you want to index. However, you can declare a short alias for each\nof these paths (using the `as:` keyword argument) to avoid typing it in full for\nevery query. Also, you must specify `Search::IndexType::JSON` when you create the index.\n\nThe code below shows these differences, but the index is otherwise very similar to\nthe one created previously for hashes:\n\nFoundational: Create a vector search index for JSON documents with JSON paths and field aliases\n\n**Difficulty:** Intermediate\n\n**Available in:** Ruby\n\n##### Ruby\n\n[code example]\n\n\n\nUse [`json_set()`](https://redis.io/docs/latest/commands/json.set) to add the data\ninstead of [`hset()`](https://redis.io/docs/latest/commands/hset).\n\nAn important difference with JSON indexing is that the vectors are\nspecified using arrays instead of binary strings. Pass the `Array<Float>`\nreturned by the embedding pipeline directly, without the `to_bytes` step\nyou use for a hash.\n\nFoundational: Store JSON documents with vector embeddings as arrays (different from hash binary format)\n\n**Difficulty:** Beginner\n\n**Available in:** Ruby\n\n##### Ruby\n\n[code example]\n\n\n\nThe query is almost identical to the one for the hash documents. This\ndemonstrates how the right choice of aliases for the JSON paths can\nsave you having to write complex queries. An important thing to notice\nis that the vector parameter for the query is still specified as a\nbinary string (using the `to_bytes` helper), even though the data for\nthe `embedding` field of the JSON was specified as an array.\n\nVector similarity search: Query JSON documents using vector embeddings with field aliases for simplified syntax\n\n**Difficulty:** Intermediate\n\n**Available in:** Ruby\n\n##### Ruby\n\n[code example]\n\n\n\nApart from the `jdoc:` prefixes for the keys, the result from the JSON\nquery is the same as for hash:\n\n[code example]"},{"id":"learn-more","title":"Learn more","role":"related","text":"See\n[Vector search](https://redis.io/docs/latest/develop/ai/search-and-query/query/vector-search)\nfor more information about the indexing options, distance metrics, and query format\nfor vectors."}],"examples":[{"id":"initialize-ex0","language":"bash","code":"gem install informers","section_id":"initialize"},{"id":"import-dependencies-ex0","language":"ruby","code":"require 'redis'\nrequire 'informers'\n\n# A short alias for the Query Engine namespace to keep the code below readable.\nSearch = Redis::Commands::Search","section_id":"import-dependencies"},{"id":"initialize-the-embedding-model-ex0","language":"ruby","code":"# `informers` is a Ruby port of Hugging Face transformers that runs the\n# ONNX-exported `all-MiniLM-L6-v2` encoder locally through `onnxruntime`.\n# `Informers.pipeline(\"embedding\", ...)` returns a callable that maps a\n# string to a 384-element `Array<Float>`. The model data is downloaded\n# into the local Hugging Face cache on the first call.\nmodel = Informers.pipeline('embedding', 'sentence-transformers/all-MiniLM-L6-v2')","section_id":"initialize-the-embedding-model"},{"id":"define-a-helper-method-ex0","language":"ruby","code":"# Redis Search stores a `FLOAT32` vector as raw little-endian bytes, with\n# no header. Ruby's `Array#pack` directive `'e'` is a little-endian\n# single-precision float, so `'e*'` packs every element of the vector.\n# The result is an ASCII-8BIT (binary) string that `redis-rb` sends\n# through unchanged.\ndef to_bytes(vector)\n  vector.pack('e*')\nend","section_id":"define-a-helper-method"},{"id":"create-the-index-ex0","language":"ruby","code":"r = Redis.new","section_id":"create-the-index"},{"id":"create-the-index-ex1","language":"ruby","code":"begin\n  r.ft_dropindex('vector_idx', delete_documents: true)\nrescue Redis::CommandError\n  # Index doesn't exist, so there is nothing to drop.\nend\n\nschema = Search::Schema.build do\n  text_field 'content'\n  tag_field 'genre'\n  vector_field 'embedding', 'HNSW',\n               type: 'FLOAT32', dim: 384, distance_metric: 'L2'\nend\n\ndefinition = Search::IndexDefinition.new(\n  prefix: ['doc:'],\n  index_type: Search::IndexType::HASH\n)\n\nindex = r.create_index('vector_idx', schema, definition: definition)\nputs index.name # >>> vector_idx","section_id":"create-the-index"},{"id":"add-data-ex0","language":"ruby","code":"# Pass `normalize: true` so `informers` L2-normalises each embedding in\n# the ONNX graph; the L2 distances Redis reports are then directly\n# comparable across documents.\nsentence1 = 'That is a very happy person'\nr.hset('doc:0', {\n  'content' => sentence1,\n  'genre' => 'persons',\n  'embedding' => to_bytes(model.(sentence1, pooling: 'mean', normalize: true))\n})\n\nsentence2 = 'That is a happy dog'\nr.hset('doc:1', {\n  'content' => sentence2,\n  'genre' => 'pets',\n  'embedding' => to_bytes(model.(sentence2, pooling: 'mean', normalize: true))\n})\n\nsentence3 = 'Today is a sunny day'\nr.hset('doc:2', {\n  'content' => sentence3,\n  'genre' => 'weather',\n  'embedding' => to_bytes(model.(sentence3, pooling: 'mean', normalize: true))\n})","section_id":"add-data"},{"id":"bulk-import-with-himport-ex0","language":"ruby","code":"docs = [\n  ['doc:0', 'That is a very happy person', 'persons'],\n  ['doc:1', 'That is a happy dog', 'pets'],\n  ['doc:2', 'Today is a sunny day', 'weather']\n]\n\nr.pipelined do |pipe|\n  pipe.himport_prepare('doc', %w[content genre embedding])\n  docs.each do |key, content, genre|\n    pipe.himport_set(key, 'doc', [\n      content,\n      genre,\n      to_bytes(model.(content, pooling: 'mean', normalize: true))\n    ])\n  end\nend","section_id":"bulk-import-with-himport"},{"id":"run-a-query-ex0","language":"ruby","code":"query_text = 'That is a happy person'\nquery_vec = to_bytes(model.(query_text, pooling: 'mean', normalize: true))\n\nres = index.search(\n  '*=>[KNN 3 @embedding $vec AS vector_distance]',\n  sort_by: 'vector_distance',\n  return_fields: ['content', 'vector_distance'],\n  params: { 'vec' => query_vec }\n)\n\nputs res.total # >>> 3\n# The index has the key prefix `doc:`, so `redis-rb` returns the logical\n# document id with that prefix removed (`0`, not `doc:0`).\nres.each do |doc|\n  puts \"#{doc.id}: #{doc['content']} (distance #{doc['vector_distance']})\"\nend","section_id":"run-a-query"},{"id":"run-a-query-ex1","language":"plaintext","code":"3\n0: That is a very happy person (distance 0.114169895649)\n1: That is a happy dog (distance 0.610845208168)\n2: Today is a sunny day (distance 1.48624789715)","section_id":"run-a-query"},{"id":"differences-with-json-documents-ex0","language":"ruby","code":"begin\n  r.ft_dropindex('vector_json_idx', delete_documents: true)\nrescue Redis::CommandError\n  # Index doesn't exist, so there is nothing to drop.\nend\n\njson_schema = Search::Schema.build do\n  text_field '$.content', as: 'content'\n  tag_field '$.genre', as: 'genre'\n  vector_field '$.embedding', 'HNSW', as: 'embedding',\n               type: 'FLOAT32', dim: 384, distance_metric: 'L2'\nend\n\njson_definition = Search::IndexDefinition.new(\n  prefix: ['jdoc:'],\n  index_type: Search::IndexType::JSON\n)\n\njson_index = r.create_index('vector_json_idx', json_schema, definition: json_definition)\nputs json_index.name # >>> vector_json_idx","section_id":"differences-with-json-documents"},{"id":"differences-with-json-documents-ex1","language":"ruby","code":"# For a JSON document, store the embedding as a plain `Array<Float>`\n# (a JSON array), not the packed binary string used for hashes.\nr.json_set('jdoc:0', '$', {\n  'content' => sentence1,\n  'genre' => 'persons',\n  'embedding' => model.(sentence1, pooling: 'mean', normalize: true)\n})\n\nr.json_set('jdoc:1', '$', {\n  'content' => sentence2,\n  'genre' => 'pets',\n  'embedding' => model.(sentence2, pooling: 'mean', normalize: true)\n})\n\nr.json_set('jdoc:2', '$', {\n  'content' => sentence3,\n  'genre' => 'weather',\n  'embedding' => model.(sentence3, pooling: 'mean', normalize: true)\n})","section_id":"differences-with-json-documents"},{"id":"differences-with-json-documents-ex2","language":"ruby","code":"# The query vector is still passed as a packed binary string, even though\n# the stored `embedding` field is a JSON array.\njson_res = json_index.search(\n  '*=>[KNN 3 @embedding $vec AS vector_distance]',\n  sort_by: 'vector_distance',\n  return_fields: ['content', 'vector_distance'],\n  params: { 'vec' => query_vec }\n)\n\nputs json_res.total # >>> 3\njson_res.each do |doc|\n  puts \"#{doc.id}: #{doc['content']} (distance #{doc['vector_distance']})\"\nend","section_id":"differences-with-json-documents"},{"id":"differences-with-json-documents-ex3","language":"plaintext","code":"3\n0: That is a very happy person (distance 0.114169895649)\n1: That is a happy dog (distance 0.610845208168)\n2: Today is a sunny day (distance 1.48624789715)","section_id":"differences-with-json-documents"}]}
{"schema_version":2,"id":"develop/clients/rust/amr","title":"Connect to Azure Managed Redis","url":"https://redis.io/docs/latest/develop/clients/rust/amr/","summary":"Learn how to authenticate to an Azure Managed Redis (AMR) database","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-21T18:36:12+03:00","page_type":"content","content_hash":"989257aef2b1ac73f9b88860a3bfab184d2d251a341b9d8a31af4726f0ba912c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `entra-id` feature lets you authenticate your app to\n[Azure Managed Redis (AMR)](https://azure.microsoft.com/en-us/products/managed-redis)\nusing [Microsoft Entra ID](https://learn.microsoft.com/en-us/entra/identity/).\nYou can authenticate using a system-assigned or user-assigned\n[managed identity](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview)\nor a [service principal](https://learn.microsoft.com/en-us/entra/identity-platform/app-objects-and-service-principals),\nletting the `redis-rs` connection fetch and renew the authentication tokens for you automatically."},{"id":"install","title":"Install","role":"setup","text":"Add the `entra-id` feature to your `Cargo.toml` file:\n\n[code example]"},{"id":"create-a-entraidcredentialsprovider-instance","title":"Create a `EntraIdCredentialsProvider` instance","role":"content","text":"A `EntraIdCredentialsProvider` object obtains the authentication credentials you\nneed when you connect to Redis. See the sections below to learn how\nto create the `EntraIdCredentialsProvider` instances for AMR\nusing the factory methods that the class provides."},{"id":"entraidcredentialsprovider-for-a-service-principal","title":"`EntraIdCredentialsProvider` for a service principal","role":"content","text":"Use the `new_client_secret()` factory method to create a\n`EntraIdCredentialsProvider` that authenticates to AMR using a\nservice principal (see the\n[Microsoft documentation](https://learn.microsoft.com/en-us/entra/identity-platform/app-objects-and-service-principals) to learn more about service principals).\n\nYou will need the following details of your service principal to make the connection:\n\n- Client ID\n- Client secret\n- Tenant ID\n\nThe example below shows how to import the required modules and call\n`new_client_secret()`:\n\n[code example]\n\nFor extra security, you can also supply a certificate:\n\n[code example]"},{"id":"entraidcredentialsprovider-for-a-managed-identity","title":"`EntraIdCredentialsProvider` for a managed identity","role":"content","text":"`EntraIdCredentialsProvider` provides two factory methods that authenticate to AMR using a\nmanaged identity:\n\n- `new_system_assigned_managed_identity()` for system-assigned managed identities\n- `new_user_assigned_managed_identity()` for user-assigned managed identities\n\nSee the\n[Microsoft documentation](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview) to learn more about managed identities.\n\nThe example below shows how to import the required modules and call both methods for\nAzure-hosted applications:\n\n[code example]\n\nYou can also create a user-assigned managed identity with custom scopes and\nidentity specification:\n\n[code example]"},{"id":"advanced-configuration","title":"Advanced configuration","role":"content","text":"The examples above use the default `RetryConfig` when starting the provider.\nHowever, the `RetryConfig` class provides configuration methods that let you customise\nthe way the provider retries token requests:\n\n[code example]\n\n`RetryConfig` provides the following configuration methods:\n\n| Method | Description |\n| --- | --- |\n| `set_number_of_retries()` | The maximum number of times to retry a token request before aborting. |\n| `set_min_delay()` | Minimum time to wait before retrying a token request after a failed attempt. This provides a mechanism to request throttling to prevent an excessive number of token requests. |\n| `set_max_delay()` | Maximum time to wait before retrying a token request after a failed attempt. |\n| `set_exponent_base()` | An `f32` multiplier used to calculate exponential backoff between failed token-refresh attempts. The delay is approximately `min_delay × exponent_base^n`, where `n` is the number of previous attempts, subject to `max_delay`. For example, with `min_delay = 100 ms` and `exponent_base = 2.0`, successive delays are approximately 100 ms, 200 ms, 400 ms, and so on. |"},{"id":"connect","title":"Connect","role":"content","text":"When you have created your `EntraIdCredentialsProvider` instance, you are ready to\nconnect to AMR. Create a connection configuration with the credentials provider and\nuse it to create a connection, as shown in the example below.\n\n[code example]"}],"examples":[{"id":"install-ex0","language":"toml","code":"[dependencies]\nredis = { version = \"1.0.4\", features = [\"entra-id\"] }","section_id":"install"},{"id":"entraidcredentialsprovider-for-a-service-principal-ex0","language":"rust","code":"use redis::{EntraIdCredentialsProvider, RetryConfig, RedisResult};\n\nfn example() -> RedisResult<()> {\n    let mut provider = EntraIdCredentialsProvider::new_client_secret(\n        \"your-tenant-id\".to_string(),\n        \"your-client-id\".to_string(),\n        \"your-client-secret\".to_string(),\n    )?;\n\n    provider.start(RetryConfig::default());\n    Ok(())\n}","section_id":"entraidcredentialsprovider-for-a-service-principal"},{"id":"entraidcredentialsprovider-for-a-service-principal-ex1","language":"rust","code":"use redis::{\n    ClientCertificate, EntraIdCredentialsProvider,\n    RetryConfig, RedisResult\n};\nuse std::fs;\n\nfn example() -> RedisResult<()> {\n    // Load certificate from file\n    let certificate_base64 = fs::read_to_string(\n            \"path/to/base64_pkcs12_certificate\"\n        )\n        .expect(\"Base64 PKCS12 certificate not found.\")\n        .trim()\n        .to_string();\n\n    // Create the credentials provider using service principal with\n    // client certificate\n    let mut provider = EntraIdCredentialsProvider::new_client_certificate(\n        \"your-tenant-id\".to_string(),\n        \"your-client-id\".to_string(),\n        ClientCertificate {\n            base64_pkcs12: certificate_base64, // Base64 encoded PKCS12 data\n            password: None,\n        },\n    )?;\n    provider.start(RetryConfig::default());\n    Ok(())\n}","section_id":"entraidcredentialsprovider-for-a-service-principal"},{"id":"entraidcredentialsprovider-for-a-managed-identity-ex0","language":"rust","code":"use redis::{EntraIdCredentialsProvider, RetryConfig, RedisResult};\n\nfn example() -> RedisResult<()> {\n    // System-assigned managed identity\n    let mut provider = EntraIdCredentialsProvider::new_system_assigned_managed_identity()?;\n    provider.start(RetryConfig::default());\n\n    // User-assigned managed identity\n    let mut provider = EntraIdCredentialsProvider::new_user_assigned_managed_identity()?;\n    provider.start(RetryConfig::default());\n    Ok(())\n}","section_id":"entraidcredentialsprovider-for-a-managed-identity"},{"id":"entraidcredentialsprovider-for-a-managed-identity-ex1","language":"rust","code":"use redis::{EntraIdCredentialsProvider, RetryConfig, RedisResult};\nuse azure_identity::{ManagedIdentityCredentialOptions, UserAssignedId};\n\nfn example() -> RedisResult<()> {\n    let mut provider = EntraIdCredentialsProvider::new_user_assigned_managed_identity_with_scopes(\n        vec![\"your-scope\".to_string()],\n        Some(ManagedIdentityCredentialOptions {\n            // Specify the user-assigned identity using one of:\n            user_assigned_id: Some(UserAssignedId::ClientId(\"your-client-id\".to_string())),\n            // or: user_assigned_id: Some(UserAssignedId::ObjectId(\"your-object-id\".to_string())),\n            // or: user_assigned_id: Some(UserAssignedId::ResourceId(\"your-resource-id\".to_string())),\n            ..Default::default()\n        }),\n    )?;\n\n    provider.start(RetryConfig::default());\n    Ok(())\n}","section_id":"entraidcredentialsprovider-for-a-managed-identity"},{"id":"advanced-configuration-ex0","language":"rust","code":"use redis::{EntraIdCredentialsProvider, RetryConfig, RedisResult};\nuse std::time::Duration;\n\nfn example() -> RedisResult<()> {\n    let mut provider = EntraIdCredentialsProvider::new_system_assigned_managed_identity()?;\n\n    let retry_config = RetryConfig::default()\n        .set_number_of_retries(3)\n        .set_min_delay(Duration::from_millis(100))\n        .set_max_delay(Duration::from_secs(30))\n        .set_exponent_base(2.0);\n\n    provider.start(retry_config);\n    Ok(())\n}","section_id":"advanced-configuration"},{"id":"connect-ex0","language":"rust","code":"use redis::{\n    Client, EntraIdCredentialsProvider,\n    RetryConfig, AsyncConnectionConfig\n};\n\nasync fn example() -> redis::RedisResult<()> {\n    // Create the credentials provider.\n    let mut provider = EntraIdCredentialsProvider::new_system_assigned_managed_identity()?;\n    provider.start(RetryConfig::default());\n\n    // Create Redis client.\n    let client = Client::open(\"redis://your-redis-instance.com:6380\")?;\n\n    // Create a connection configuration with the credentials provider.\n    let config = AsyncConnectionConfig::new().set_credentials_provider(provider);\n\n    // Get a multiplexed connection with the configuration.\n    let mut r = client.get_multiplexed_async_connection_with_config(&config).await?;\n    \n    // Use the connection.\n    r.set(\"foo\", \"bar\").await?;\n\n    let res: String = r.get(\"foo\").await?;\n    println!(\"foo={res}\");\n\n    Ok(())\n}","section_id":"connect"}]}
{"schema_version":2,"id":"develop/clients/rust/connect","title":"Connect to the server","url":"https://redis.io/docs/latest/develop/clients/rust/connect/","summary":"Connect your Rust application to a Redis database","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-12T12:46:28+01:00","page_type":"content","content_hash":"91d1406608a530870ba56fa261b1d566169227b3177136d3fc34009ae506e9a7","sections":[{"id":"basic-connection","title":"Basic connection","role":"overview","text":"Synchronous connections need only `redis = \"1.7.1\"` in your `Cargo.toml` file. For\nasynchronous connections, also enable the `tokio-comp` feature and add\n[`tokio`](https://tokio.rs/):\n\n[code example]\n\nThe following example opens a synchronous connection, then stores and retrieves a\n[string](https://redis.io/docs/latest/develop/data-types/strings):\n\n[code example]\n\nThe asynchronous version uses `get_multiplexed_async_connection()`:\n\n[code example]\n\nTo connect as an [access control list (ACL)](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl)\nuser, add the username and password to the URL. The path selects the database number:\n\n[code example]\n\nYou can also build the connection details in code with `ConnectionInfo` and\n`RedisConnectionInfo`:\n\n[code example]\n\nTo connect through a Unix socket, use a `redis+unix://` URL. A Unix socket URL takes the\nusername, password, and database number as the `user`, `pass`, and `db` query parameters.\nThis differs from a `redis://` URL, which takes the credentials before the host and the\ndatabase number from the path.\n\n[code example]\n\nTo authenticate with Microsoft Entra ID, see\n[Connect to Azure Managed Redis](https://redis.io/docs/latest/develop/clients/rust/amr)."},{"id":"connect-to-a-redis-cluster","title":"Connect to a Redis cluster","role":"content","text":"To connect to a Redis cluster, enable the `cluster` feature:\n\n[code example]\n\nThen pass one or more cluster nodes to `ClusterClient::new()`. The client discovers the\nrest of the cluster from these nodes:\n\n[code example]\n\nFor asynchronous connections, enable the `cluster-async` and `tokio-comp` features instead,\nand call `get_async_connection()`:\n\n[code example]\n\nBy default, the client sends every command to the primary node. To send read-only\ncommands to replicas, create the client with `ClusterClientBuilder` and set a read\nrouting strategy. `RandomReplicaStrategy` sends each read to a random replica of the\nkey's shard, and `RoundRobinReplicaStrategy` rotates through them. Writes still go to the\nprimary. Replication is asynchronous, so a replica can return stale data.\n\n[code example]"},{"id":"connect-to-redis-sentinel","title":"Connect to Redis Sentinel","role":"content","text":"To connect through [Redis Sentinel](https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel),\nenable the `sentinel` feature:\n\n[code example]\n\nThen create a client with `SentinelClientBuilder`. Pass one or more Sentinel addresses,\nthe name of the primary that the Sentinels monitor, and `SentinelServerType::Master`.\nEach call to `get_connection()` asks the Sentinels for the current primary and checks\nits role before it connects:\n\n[code example]\n\nThe Sentinels and the data nodes have separate credentials. Use the\n`set_client_to_sentinel_*` methods for the Sentinels, and the `set_client_to_redis_*`\nmethods for the primary and replicas:\n\n[code example]\n\nTo connect to a replica instead, pass `SentinelServerType::Replica`. The client connects\nto a replica chosen at random, and writes on that connection fail. Replication is\nasynchronous, so a replica can return stale data.\n\n[code example]\n\nA connection from `get_connection()` doesn't reconnect, and redis-rs doesn't listen for\nfailover announcements from the Sentinels. After a failover, the connection keeps using\nthe old primary until Sentinel reconfigures it as a replica and closes the connection.\nAny writes that the old primary accepts during that time are lost. When a command fails\nwith a connection error, call `get_connection()` again to connect to the new primary."},{"id":"connect-to-your-production-redis-with-tls","title":"Connect to your production Redis with TLS","role":"content","text":"When you deploy your application, use Transport Layer Security (TLS) and follow the\n[Redis security](https://redis.io/docs/latest/operate/oss_and_stack/management/security) guidelines.\n\nCustom certificates need the rustls backend, so enable the `tls-rustls` feature, or\n`tokio-rustls-comp` for asynchronous connections. Also add the `rustls` crate, which\nprovides the cryptography:\n\n[code example]\n\nIf a certificate authority (CA) in your system's trust store signed the server's\ncertificate, a `rediss://` URL is all you need. To use Mozilla's root certificates\ninstead of the system trust store, enable the `tls-rustls-webpki-roots` feature.\n\n[code example]\n\nTo use your own CA certificate, or a client certificate and key for mutual TLS, pass the\nPrivacy-Enhanced Mail (PEM) files to `Client::build_with_tls()` in a `TlsCertificates`\nobject. rustls chooses a crypto provider automatically only when your dependencies enable\nexactly one, and the connection panics otherwise, so the following example installs one\nbefore it connects:\n\n[code example]"},{"id":"connect-using-client-side-caching","title":"Connect using client-side caching","role":"content","text":"Client-side caching is a technique to reduce network traffic between\nthe client and server, resulting in better performance. See\n[Client-side caching introduction](https://redis.io/docs/latest/develop/clients/client-side-caching)\nfor more information about how client-side caching works and how to use it effectively.\n\n> [!NOTE]\n> Client-side caching is an\n> [experimental feature](https://docs.rs/redis/1.7.1/redis/caching/index.html) of\n> redis-rs. To maximize compatibility with all Redis products, client-side caching\n> is supported by Redis v7.4 or later.\n>\n> Client-side caching requires version 3 of the\n> [Redis serialization protocol (RESP3)](https://redis.io/docs/latest/develop/reference/protocol-spec#resp-versions),\n> and connecting with caching enabled over RESP2 fails with an error. It's\n> available only for asynchronous connections: `MultiplexedConnection`,\n> `ConnectionManager`, and the asynchronous `ClusterConnection`.\n\nEnable the `cache-aio` feature:\n\n[code example]\n\nThen pass a `CacheConfig` in an `AsyncConnectionConfig` when you connect.\n`CacheConfig::new()` enables caching with the default settings. Use\n`get_cache_statistics()` to check that the second `get()` call is served from the cache:\n\n[code example]"},{"id":"connection-management-and-pooling","title":"Connection management and pooling","role":"content","text":"A `MultiplexedConnection` is cheap to clone, and all the clones share one underlying\nconnection, so you don't need a connection pool for asynchronous connections. Give each task\nits own clone:\n\n[code example]"},{"id":"reconnect-automatically","title":"Reconnect automatically","role":"content","text":"A `MultiplexedConnection` doesn't reconnect after the connection drops. To reconnect\nautomatically, enable the `connection-manager` feature:\n\n[code example]\n\nThen use a `ConnectionManager`, which you can clone in the same way. The command that\nencounters the disconnection still fails, and the manager reconnects in the background\nfor the next command, so retry where that matters. To change the retry and timeout\nsettings, pass a `ConnectionManagerConfig` to `get_connection_manager_with_config()`.\n\n[code example]"},{"id":"pool-synchronous-connections","title":"Pool synchronous connections","role":"content","text":"For synchronous connections, use an [r2d2](https://docs.rs/r2d2) pool to handle\ndisconnections and share connections between threads. Enable the `r2d2` feature and add\nthe `r2d2` crate:\n\n[code example]\n\nThe pool opens `max_size` connections when you build it. The default is 10.\n\n[code example]"},{"id":"timeouts","title":"Timeouts","role":"content","text":"Asynchronous connections time out after 1 second when they connect, and after 500\nmilliseconds when they wait for a response. To change these timeouts, pass an\n`AsyncConnectionConfig` when you connect. Pass `None` to disable a timeout.\n`ConnectionManagerConfig` has the same defaults and setters.\n\n[code example]\n\nThe response timeout also applies to blocking commands. With the default timeout, a\n[`BLPOP`](https://redis.io/docs/latest/commands/blpop) that waits longer than 500 milliseconds fails with a\ntimeout error. Other commands on the same connection also time out until the server\nfinishes the blocking command, because the server doesn't process them in the meantime.\nSend blocking commands on a separate connection, with a response timeout longer than the\ncommand's own timeout, or none:\n\n[code example]\n\nSynchronous connections have no timeouts by default. `get_connection_with_timeout()`\nlimits the time to connect, and `set_read_timeout()` and `set_write_timeout()` limit each\nread and write on the connection:\n\n[code example]"},{"id":"protocol","title":"Protocol","role":"content","text":"redis-rs uses the [RESP2](https://redis.io/docs/latest/develop/reference/protocol-spec#resp-versions)\nprotocol by default. To use RESP3, add `protocol=resp3` to the URL:\n\n[code example]\n\nYou can also set the protocol with `RedisConnectionInfo`:\n\n[code example]\n\nFor cluster and Sentinel clients, use `ClusterClientBuilder::use_protocol()` and\n`SentinelClientBuilder::set_client_to_redis_protocol()`."}],"examples":[{"id":"basic-connection-ex0","language":"toml","code":"[dependencies]\nredis = { version = \"1.7.1\", features = [\"tokio-comp\"] }\ntokio = { version = \"1\", features = [\"full\"] }","section_id":"basic-connection"},{"id":"basic-connection-ex1","language":"rust","code":"use redis::Commands;\n\nfn main() -> redis::RedisResult<()> {\n    let client = redis::Client::open(\"redis://localhost:6379/\")?;\n    let mut con = client.get_connection()?;\n\n    let _: () = con.set(\"foo\", \"bar\")?;\n    let val: String = con.get(\"foo\")?;\n    println!(\"foo: {val}\"); // foo: bar\n    Ok(())\n}","section_id":"basic-connection"},{"id":"basic-connection-ex2","language":"rust","code":"use redis::AsyncCommands;\n\n#[tokio::main]\nasync fn main() -> redis::RedisResult<()> {\n    let client = redis::Client::open(\"redis://localhost:6379/\")?;\n    let mut con = client.get_multiplexed_async_connection().await?;\n\n    let _: () = con.set(\"foo\", \"bar\").await?;\n    let val: String = con.get(\"foo\").await?;\n    println!(\"foo: {val}\"); // foo: bar\n    Ok(())\n}","section_id":"basic-connection"},{"id":"basic-connection-ex3","language":"rust","code":"let client = redis::Client::open(\"redis://<username>:<password>@localhost:6379/0\")?;","section_id":"basic-connection"},{"id":"basic-connection-ex4","language":"rust","code":"use redis::{ConnectionAddr, IntoConnectionInfo, RedisConnectionInfo};\n\nlet info = ConnectionAddr::Tcp(\"localhost\".to_string(), 6379)\n    .into_connection_info()?\n    .set_redis_settings(\n        RedisConnectionInfo::default()\n            .set_username(\"default\") // use your Redis user\n            .set_password(\"secret\") // use your Redis password\n            .set_db(0),\n    );\nlet client = redis::Client::open(info)?;","section_id":"basic-connection"},{"id":"basic-connection-ex5","language":"rust","code":"let client = redis::Client::open(\"redis+unix:///tmp/redis.sock?user=<username>&pass=<password>&db=0\")?;","section_id":"basic-connection"},{"id":"connect-to-a-redis-cluster-ex0","language":"toml","code":"[dependencies]\nredis = { version = \"1.7.1\", features = [\"cluster\"] }","section_id":"connect-to-a-redis-cluster"},{"id":"connect-to-a-redis-cluster-ex1","language":"rust","code":"use redis::cluster::ClusterClient;\nuse redis::Commands;\n\nfn main() -> redis::RedisResult<()> {\n    let nodes = vec![\n        \"redis://localhost:16379/\",\n        \"redis://localhost:16380/\",\n        \"redis://localhost:16381/\",\n    ];\n    let client = ClusterClient::new(nodes)?;\n    let mut con = client.get_connection()?;\n\n    let _: () = con.set(\"foo\", \"bar\")?;\n    let val: String = con.get(\"foo\")?;\n    println!(\"foo: {val}\"); // foo: bar\n    Ok(())\n}","section_id":"connect-to-a-redis-cluster"},{"id":"connect-to-a-redis-cluster-ex2","language":"rust","code":"let mut con = client.get_async_connection().await?;","section_id":"connect-to-a-redis-cluster"},{"id":"connect-to-a-redis-cluster-ex3","language":"rust","code":"use redis::cluster::ClusterClientBuilder;\nuse redis::cluster_read_routing::RandomReplicaStrategy;\n\nlet client = ClusterClientBuilder::new(nodes)\n    .read_routing_strategy(RandomReplicaStrategy)\n    .build()?;\nlet mut con = client.get_connection()?;\n\nlet val: String = con.get(\"foo\")?; // Read from a replica","section_id":"connect-to-a-redis-cluster"},{"id":"connect-to-redis-sentinel-ex0","language":"toml","code":"[dependencies]\nredis = { version = \"1.7.1\", features = [\"sentinel\"] }","section_id":"connect-to-redis-sentinel"},{"id":"connect-to-redis-sentinel-ex1","language":"rust","code":"use redis::sentinel::{SentinelClientBuilder, SentinelServerType};\nuse redis::{Commands, ConnectionAddr};\n\nfn main() -> redis::RedisResult<()> {\n    let sentinels = vec![\n        ConnectionAddr::Tcp(\"localhost\".to_string(), 26379),\n        ConnectionAddr::Tcp(\"localhost\".to_string(), 26380),\n        ConnectionAddr::Tcp(\"localhost\".to_string(), 26381),\n    ];\n    let mut client =\n        SentinelClientBuilder::new(sentinels, \"mymaster\", SentinelServerType::Master)?.build()?;\n    let mut con = client.get_connection()?;\n\n    let _: () = con.set(\"foo\", \"bar\")?;\n    let val: String = con.get(\"foo\")?;\n    println!(\"foo: {val}\"); // foo: bar\n    Ok(())\n}","section_id":"connect-to-redis-sentinel"},{"id":"connect-to-redis-sentinel-ex2","language":"rust","code":"let mut client = SentinelClientBuilder::new(sentinels, \"mymaster\", SentinelServerType::Master)?\n    .set_client_to_sentinel_password(\"sentinel-secret\") // use your Sentinel password\n    .set_client_to_redis_username(\"default\") // use your Redis user\n    .set_client_to_redis_password(\"secret\") // use your Redis password\n    .build()?;","section_id":"connect-to-redis-sentinel"},{"id":"connect-to-redis-sentinel-ex3","language":"rust","code":"let mut replica =\n    SentinelClientBuilder::new(sentinels, \"mymaster\", SentinelServerType::Replica)?.build()?;\nlet mut con = replica.get_connection()?;\n\nlet val: String = con.get(\"foo\")?;","section_id":"connect-to-redis-sentinel"},{"id":"connect-to-your-production-redis-with-tls-ex0","language":"toml","code":"[dependencies]\nredis = { version = \"1.7.1\", features = [\"tls-rustls\"] }\nrustls = \"0.23\"","section_id":"connect-to-your-production-redis-with-tls"},{"id":"connect-to-your-production-redis-with-tls-ex1","language":"rust","code":"let client = redis::Client::open(\"rediss://<username>:<password>@my-redis.example.com:6379\")?;","section_id":"connect-to-your-production-redis-with-tls"},{"id":"connect-to-your-production-redis-with-tls-ex2","language":"rust","code":"use redis::{ClientTlsConfig, Commands, TlsCertificates};\nuse std::fs;\n\nfn main() -> redis::RedisResult<()> {\n    // Install a crypto provider for rustls before you connect.\n    rustls::crypto::aws_lc_rs::default_provider()\n        .install_default()\n        .expect(\"Failed to install the rustls crypto provider\");\n\n    let client = redis::Client::build_with_tls(\n        \"rediss://<username>:<password>@my-redis.example.com:6379\",\n        TlsCertificates {\n            client_tls: Some(ClientTlsConfig {\n                client_cert: fs::read(\"redis_user.crt\")?,\n                client_key: fs::read(\"redis_user_private.key\")?,\n            }),\n            root_cert: Some(fs::read(\"redis_ca.pem\")?),\n        },\n    )?;\n    let mut con = client.get_connection()?;\n\n    let _: () = con.set(\"foo\", \"bar\")?;\n    let val: String = con.get(\"foo\")?;\n    println!(\"foo: {val}\"); // foo: bar\n    Ok(())\n}","section_id":"connect-to-your-production-redis-with-tls"},{"id":"connect-using-client-side-caching-ex0","language":"toml","code":"[dependencies]\nredis = { version = \"1.7.1\", features = [\"tokio-comp\", \"cache-aio\"] }\ntokio = { version = \"1\", features = [\"full\"] }","section_id":"connect-using-client-side-caching"},{"id":"connect-using-client-side-caching-ex1","language":"rust","code":"use redis::caching::CacheConfig;\nuse redis::{AsyncCommands, AsyncConnectionConfig};\n\n#[tokio::main]\nasync fn main() -> redis::RedisResult<()> {\n    let client = redis::Client::open(\"redis://localhost:6379/?protocol=resp3\")?;\n    let config = AsyncConnectionConfig::new().set_cache_config(CacheConfig::new());\n    let mut con = client.get_multiplexed_async_connection_with_config(&config).await?;\n\n    let _: () = con.set(\"city\", \"New York\").await?;\n    let _: String = con.get(\"city\").await?; // Retrieved from the server and cached\n    let _: String = con.get(\"city\").await?; // Retrieved from the cache\n\n    let stats = con.get_cache_statistics().unwrap();\n    println!(\"hits: {}, misses: {}\", stats.hit, stats.miss); // hits: 1, misses: 1\n    Ok(())\n}","section_id":"connect-using-client-side-caching"},{"id":"connection-management-and-pooling-ex0","language":"rust","code":"let con = client.get_multiplexed_async_connection().await?;\n\nlet tasks: Vec<_> = (1..=3)\n    .map(|i| {\n        let mut con = con.clone(); // Shares the same underlying connection\n        tokio::spawn(async move { con.incr::<_, _, i64>(\"counter\", i).await })\n    })\n    .collect();\nfor task in tasks {\n    task.await.unwrap()?;\n}","section_id":"connection-management-and-pooling"},{"id":"reconnect-automatically-ex0","language":"toml","code":"[dependencies]\nredis = { version = \"1.7.1\", features = [\"tokio-comp\", \"connection-manager\"] }\ntokio = { version = \"1\", features = [\"full\"] }","section_id":"reconnect-automatically"},{"id":"reconnect-automatically-ex1","language":"rust","code":"let client = redis::Client::open(\"redis://localhost:6379/\")?;\nlet mut con = client.get_connection_manager().await?;\n\nlet _: () = con.set(\"foo\", \"bar\").await?;","section_id":"reconnect-automatically"},{"id":"pool-synchronous-connections-ex0","language":"toml","code":"[dependencies]\nredis = { version = \"1.7.1\", features = [\"r2d2\"] }\nr2d2 = \"0.8\"","section_id":"pool-synchronous-connections"},{"id":"pool-synchronous-connections-ex1","language":"rust","code":"use redis::Commands;\n\nlet client = redis::Client::open(\"redis://localhost:6379/\")?;\nlet pool = r2d2::Pool::builder().max_size(15).build(client).unwrap();\n\nlet mut con = pool.get().unwrap();\nlet _: () = con.set(\"foo\", \"bar\")?;","section_id":"pool-synchronous-connections"},{"id":"timeouts-ex0","language":"rust","code":"use redis::AsyncConnectionConfig;\nuse std::time::Duration;\n\nlet config = AsyncConnectionConfig::new()\n    .set_connection_timeout(Some(Duration::from_secs(5)))\n    .set_response_timeout(Some(Duration::from_secs(2)));\nlet mut con = client.get_multiplexed_async_connection_with_config(&config).await?;","section_id":"timeouts"},{"id":"timeouts-ex1","language":"rust","code":"let config = AsyncConnectionConfig::new().set_response_timeout(None);\nlet mut con = client.get_multiplexed_async_connection_with_config(&config).await?;\n\n// Waits up to 5 seconds for an item.\nlet item: Option<(String, String)> = con.blpop(\"queue\", 5.0).await?;","section_id":"timeouts"},{"id":"timeouts-ex2","language":"rust","code":"use std::time::Duration;\n\nlet con = client.get_connection_with_timeout(Duration::from_secs(5))?;\ncon.set_read_timeout(Some(Duration::from_secs(2)))?;\ncon.set_write_timeout(Some(Duration::from_secs(2)))?;","section_id":"timeouts"},{"id":"protocol-ex0","language":"rust","code":"let client = redis::Client::open(\"redis://localhost:6379/?protocol=resp3\")?;","section_id":"protocol"},{"id":"protocol-ex1","language":"rust","code":"use redis::{ConnectionAddr, IntoConnectionInfo, ProtocolVersion, RedisConnectionInfo};\n\nlet info = ConnectionAddr::Tcp(\"localhost\".to_string(), 6379)\n    .into_connection_info()?\n    .set_redis_settings(RedisConnectionInfo::default().set_protocol(ProtocolVersion::RESP3));\nlet client = redis::Client::open(info)?;","section_id":"protocol"}]}
{"schema_version":2,"id":"develop/clients/rust/error-handling","title":"Error handling","url":"https://redis.io/docs/latest/develop/clients/rust/error-handling/","summary":"Learn how to handle errors when using redis-rs","tags":[],"last_updated":"2026-09-09T11:06:34+01:00","page_type":"content","content_hash":"9ff0178d68493031787830fa1cd4da0ff966ede3d42e06aff2580209de112021","sections":[{"id":"overview","title":"Overview","role":"overview","text":"redis-rs uses **Result types** following Rust's idiomatic error handling pattern. Code examples in the documentation often omit error handling for brevity,\nbut it is essential in production code.\nThis page explains how error handling works in redis-rs and how to apply\nsome common error handling patterns. For an overview of error types and handling\nstrategies, see [Error handling](https://redis.io/docs/latest/develop/clients/error-handling)."},{"id":"error-handling-in-rust","title":"Error handling in Rust","role":"errors","text":"In Rust, functions return errors using the `Result<T, E>` type. You can check for errors explicitly with code like the following:\n\n[code example]"},{"id":"error-types","title":"Error types","role":"errors","text":"redis-rs provides a `RedisError` type with various error kinds. Common error kinds include:\n\n| Error Kind | When it occurs | Recoverable | Recommended action |\n|---|---|---|---|\n| `ErrorKind::Io` | Network or I/O error | ✅ | Retry with backoff or fall back to alternative |\n| `ErrorKind::AuthenticationFailed` | Invalid credentials | ❌ | Fix credentials and fail |\n| `ErrorKind::UnexpectedReturnType` | Type mismatch | ❌ | Fix the command or type conversion |\n| `ErrorKind::Server` | Redis server error response | ⚠️ | Check specific server error; some are retryable |\n| `ErrorKind::Parse` | Failed to parse server response | ❌ | Report as a bug |\n\nSee [Categories of errors](https://redis.io/docs/latest/develop/clients/error-handling#categories-of-errors)\nfor a more detailed discussion of these errors and their causes."},{"id":"applying-error-handling-patterns","title":"Applying error handling patterns","role":"content","text":"The [Error handling](https://redis.io/docs/latest/develop/clients/error-handling) overview\ndescribes four main patterns. The sections below show how to implement them in\nredis-rs:"},{"id":"pattern-1-fail-fast","title":"Pattern 1: Fail fast","role":"content","text":"Return the error immediately if it represents an unrecoverable situation (see\n[Pattern 1: Fail fast](https://redis.io/docs/latest/develop/clients/error-handling#pattern-1-fail-fast)\nfor a full description):\n\n[code example]"},{"id":"pattern-2-graceful-degradation","title":"Pattern 2: Graceful degradation","role":"content","text":"Check for specific errors and fall back to an alternative (see\n[Pattern 2: Graceful degradation](https://redis.io/docs/latest/develop/clients/error-handling#pattern-2-graceful-degradation)\nfor a full description):\n\n[code example]"},{"id":"pattern-3-retry-with-backoff","title":"Pattern 3: Retry with backoff","role":"content","text":"Retry on temporary errors such as timeouts (see\n[Pattern 3: Retry with backoff](https://redis.io/docs/latest/develop/clients/error-handling#pattern-3-retry-with-backoff)\nfor a full description):\n\n[code example]"},{"id":"pattern-4-log-and-continue","title":"Pattern 4: Log and continue","role":"content","text":"Log non-critical errors and continue (see\n[Pattern 4: Log and continue](https://redis.io/docs/latest/develop/clients/error-handling#pattern-4-log-and-continue)\nfor a full description):\n\n[code example]"},{"id":"async-error-handling","title":"Async error handling","role":"content","text":"Error handling works the same way when you use async/await with redis-rs,\nas shown in the example below:\n\n[code example]"},{"id":"see-also","title":"See also","role":"related","text":"- [Error handling](https://redis.io/docs/latest/develop/clients/error-handling)\n- [redis-rs documentation](https://docs.rs/redis/latest/redis/)"}],"examples":[{"id":"error-handling-in-rust-ex0","language":"rust","code":"use redis::Commands;\n\nlet client = redis::Client::open(\"redis://127.0.0.1/\")?;\nlet mut con = client.get_connection()?;\n\nmatch con.get::<_, String>(\"key\") {\n    Ok(value) => println!(\"Value: {}\", value),\n    Err(e) => {\n        // Handle the error\n        eprintln!(\"Error: {}\", e);\n    }\n}","section_id":"error-handling-in-rust"},{"id":"pattern-1-fail-fast-ex0","language":"rust","code":"use redis::{Commands, RedisResult};\n\nfn get_value(con: &mut redis::Connection, key: &str) -> RedisResult<String> {\n    // Using ? operator to propagate errors immediately\n    let value: String = con.get(key)?;\n    Ok(value)\n}","section_id":"pattern-1-fail-fast"},{"id":"pattern-2-graceful-degradation-ex0","language":"rust","code":"use redis::{Commands, ErrorKind};\n\nfn get_with_fallback(\n    con: &mut redis::Connection,\n    key: &str,\n) -> Result<String, Box<dyn std::error::Error>> {\n    match con.get::<_, String>(key) {\n        Ok(value) => Ok(value),\n        Err(e) => {\n            if e.kind() == ErrorKind::Io {\n                // Network error, use fallback\n                log::warn!(\"Cache unavailable, using database\");\n                database_get(key)\n            } else {\n                Err(Box::new(e))\n            }\n        }\n    }\n}","section_id":"pattern-2-graceful-degradation"},{"id":"pattern-3-retry-with-backoff-ex0","language":"rust","code":"use redis::{Commands, ErrorKind};\nuse std::thread;\nuse std::time::Duration;\n\nfn get_with_retry(\n    con: &mut redis::Connection,\n    key: &str,\n    max_attempts: u32,\n) -> redis::RedisResult<String> {\n    let mut delay = Duration::from_millis(100);\n    \n    for attempt in 0..max_attempts {\n        match con.get::<_, String>(key) {\n            Ok(value) => return Ok(value),\n            Err(e) => {\n                if attempt < max_attempts - 1 && e.kind() == ErrorKind::Io {\n                    thread::sleep(delay);\n                    delay *= 2; // Exponential backoff\n                    continue;\n                }\n                return Err(e);\n            }\n        }\n    }\n    \n    unreachable!()\n}","section_id":"pattern-3-retry-with-backoff"},{"id":"pattern-4-log-and-continue-ex0","language":"rust","code":"use redis::{Commands, ErrorKind};\n\nfn cache_value(con: &mut redis::Connection, key: &str, value: &str) {\n    if let Err(e) = con.set_ex::<_, _, _, ()>(key, value, 3600) {\n        if e.kind() == ErrorKind::Io {\n            log::warn!(\"Failed to cache {}, continuing without cache\", key);\n            // Application continues normally\n        } else {\n            log::error!(\"Unexpected error caching {}: {}\", key, e);\n        }\n    }\n}","section_id":"pattern-4-log-and-continue"},{"id":"async-error-handling-ex0","language":"rust","code":"use redis::AsyncCommands;\n\nasync fn get_with_fallback_async(\n    con: &mut redis::aio::MultiplexedConnection,\n    key: &str,\n) -> Result<String, Box<dyn std::error::Error>> {\n    match con.get::<_, String>(key).await {\n        Ok(value) => Ok(value),\n        Err(e) => {\n            if e.kind() == redis::ErrorKind::Io {\n                log::warn!(\"Cache unavailable\");\n                database_get_async(key).await\n            } else {\n                Err(Box::new(e))\n            }\n        }\n    }\n}","section_id":"async-error-handling"}]}
{"schema_version":2,"id":"develop/clients/rust","title":"redis-rs guide (Rust)","url":"https://redis.io/docs/latest/develop/clients/rust/","summary":"Connect your Rust application to a Redis database","aliases":["/connect/clients/rust/","/clients/rust/","/develop/clients/redis-rs"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-12T14:48:04+01:00","children":[{"id":"develop/clients/rust/connect","summary":"Connect your Rust application to a Redis database","title":"Connect to the server","url":"https://redis.io/docs/latest/develop/clients/rust/connect/"},{"id":"develop/clients/rust/amr","summary":"Learn how to authenticate to an Azure Managed Redis (AMR) database","title":"Connect to Azure Managed Redis","url":"https://redis.io/docs/latest/develop/clients/rust/amr/"},{"id":"develop/clients/rust/json","summary":"Learn how to store, read, and update JSON documents with redis-rs.","title":"Work with JSON documents","url":"https://redis.io/docs/latest/develop/clients/rust/json/"},{"id":"develop/clients/rust/prob","summary":"Learn how to use Bloom filter set membership and HyperLogLog approximate cardinality with redis-rs.","title":"Probabilistic data types","url":"https://redis.io/docs/latest/develop/clients/rust/prob/"},{"id":"develop/clients/rust/error-handling","summary":"Learn how to handle errors when using redis-rs","title":"Error handling","url":"https://redis.io/docs/latest/develop/clients/rust/error-handling/"},{"id":"develop/clients/rust/transpipe","summary":"Learn how to use Redis pipelines and transactions","title":"Pipelines and transactions","url":"https://redis.io/docs/latest/develop/clients/rust/transpipe/"}],"page_type":"content","content_hash":"7f84b6fada4364c168227b26834c2e46e8e70f35112bbefa61122433b1b22a09","sections":[{"id":"install","title":"Install","role":"setup","text":"To use the synchronous API, add the `redis` crate as a dependency in your\n`Cargo.toml` file:\n\n[code example]\n\nIf you want to use the asynchronous API, you should also enable either\n[`tokio`](https://tokio.rs/) or [`smol`](https://crates.io/crates/smol)\nas your async platform:\n\n[code example]"},{"id":"connect","title":"Connect","role":"content","text":"Start by importing the `Commands` or `AsyncCommands` trait from the `redis` crate:\n\nFoundational: Import the Commands trait to access Redis command methods\n\n**Difficulty:** Beginner\n\n**Available in:** Rust (Asynchronous), Rust (Synchronous)\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nThe following example shows the simplest way to connect to a Redis server:\n\nFoundational: Connect to a Redis server and establish a client connection\n\n**Difficulty:** Beginner\n\n**Available in:** Rust (Asynchronous), Rust (Synchronous)\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\n> [!NOTE]\n> redis-rs supports [connection URLs](https://redis.io/docs/latest/develop/clients/connection-urls#redis-rs-rust) through `Client::open()`.\n> A URL can set the host, port, credentials, database, and whether to use TLS,\n> so you can use one instead of passing those options separately.\n\nAfter connecting, you can test the connection by  storing and retrieving\na simple [string](https://redis.io/docs/latest/develop/data-types/strings):\n\nFoundational: Set and retrieve string values using SET and GET commands\n\n**Difficulty:** Beginner\n\n**Available in:** Rust (Asynchronous), Rust (Synchronous)\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nYou can also easily store and retrieve a [hash](https://redis.io/docs/latest/develop/data-types/hashes):\n\nFoundational: Store and retrieve hash data structures using HSET and HGETALL\n\n**Difficulty:** Beginner\n\n**Available in:** Rust (Asynchronous), Rust (Synchronous)\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nTo connect to a cluster, through Redis Sentinel, or over TLS, see\n[Connect to the server](https://redis.io/docs/latest/develop/clients/rust/connect)."},{"id":"more-information","title":"More information","role":"content","text":"See the [`redis-rs`](https://docs.rs/redis/latest/redis/) documentation\nand the [GitHub repository](https://github.com/redis-rs/redis-rs) for more\ninformation and examples."}],"examples":[{"id":"install-ex0","language":"toml","code":"[dependencies]\nredis = \"1.0.4\"","section_id":"install"},{"id":"install-ex1","language":"toml","code":"[dependencies]\n# if you use tokio\ntokio = { version = \"1.32.0\", features = [\"full\"] }\nredis = { version = \"1.0.4\", features = [\"tokio-comp\"] }\n\n# if you use smol\nsmol = \"2.0.2\"\nredis = { version = \"1.0.4\", features = [\"smol-comp\"] }","section_id":"install"},{"id":"connect-ex0","language":"rust","code":"use redis::AsyncCommands;","section_id":"connect"},{"id":"connect-ex1","language":"rust","code":"use redis::Commands;","section_id":"connect"},{"id":"connect-ex2","language":"rust","code":"let mut r = match redis::Client::open(\"redis://127.0.0.1\") {\n            Ok(client) => {\n                match client.get_multiplexed_async_connection().await {\n                    Ok(conn) => conn,\n                    Err(e) => {\n                        println!(\"Failed to connect to Redis: {e}\");\n                        return;\n                    }\n                }\n            },\n            Err(e) => {\n                println!(\"Failed to create Redis client: {e}\");\n                return;\n            }\n        };","section_id":"connect"},{"id":"connect-ex3","language":"rust","code":"let mut r = match redis::Client::open(\"redis://127.0.0.1\") {\n            Ok(client) => {\n                match client.get_connection() {\n                    Ok(conn) => conn,\n                    Err(e) => {\n                        println!(\"Failed to connect to Redis: {e}\");\n                        return;\n                    }\n                }\n            },\n            Err(e) => {\n                println!(\"Failed to create Redis client: {e}\");\n                return;\n            }\n        };","section_id":"connect"},{"id":"connect-ex4","language":"rust","code":"if let Ok(res) = r.set(\"foo\", \"bar\").await {\n            let res: String = res;\n            println!(\"{res}\"); // >>> OK\n        } else {\n            println!(\"Error setting foo\");\n        }\n\n        match r.get(\"foo\").await {\n            Ok(res) => {\n                let res: String = res;\n                println!(\"{res}\"); // >>> bar\n            },\n            Err(e) => {\n                println!(\"Error getting foo: {e}\");\n                return;\n            }\n        };","section_id":"connect"},{"id":"connect-ex5","language":"rust","code":"if let Ok(res) = r.set(\"foo\", \"bar\") {\n            let res: String = res;\n            println!(\"{res}\");    // >>> OK\n        } else {\n            println!(\"Error setting foo\");\n        }\n        \n        match r.get(\"foo\") {\n            Ok(res) => {\n                let res: String = res;\n                println!(\"{res}\");   // >>> bar\n            },\n            Err(e) => {\n                println!(\"Error getting foo: {e}\");\n                return;\n            }\n        };","section_id":"connect"},{"id":"connect-ex6","language":"rust","code":"let hash_fields = [\n            (\"model\", \"Deimos\"),\n            (\"brand\", \"Ergonom\"),\n            (\"type\", \"Enduro bikes\"),\n            (\"price\", \"4972\"),\n        ];\n\n        if let Ok(res) = r.hset_multiple(\"bike:1\", &hash_fields).await {\n            let res: String = res;\n            println!(\"{res}\"); // >>> OK\n        } else {\n            println!(\"Error setting bike:1\");\n        }\n\n        match r.hget(\"bike:1\", \"model\").await {\n            Ok(res) => {\n                let res: String = res;\n                println!(\"{res}\"); // >>> Deimos\n            },\n            Err(e) => {\n                println!(\"Error getting bike:1 model: {e}\");\n                return;\n            }\n        }\n\n        match r.hget(\"bike:1\", \"price\").await {\n            Ok(res) => {\n                let res: String = res;\n                println!(\"{res}\"); // >>> 4972\n            },\n            Err(e) => {\n                println!(\"Error getting bike:1 price: {e}\");\n                return;\n            }\n        }\n\n        match r.hgetall(\"bike:1\").await {\n            Ok(res) => {\n                let res: Vec<(String, String)> = res;\n\n                for (key, value) in res {\n                    println!(\"{key}: {value}\");\n                }\n                // >>> model: Deimos\n                // >>> brand: Ergonom\n                // >>> type: Enduro bikes\n                // >>> price: 4972\n            },\n            Err(e) => {\n                println!(\"Error getting bike:1: {e}\");\n                return;\n            }","section_id":"connect"},{"id":"connect-ex7","language":"rust","code":"let hash_fields = [\n            (\"model\", \"Deimos\"),\n            (\"brand\", \"Ergonom\"),\n            (\"type\", \"Enduro bikes\"),\n            (\"price\", \"4972\"),\n        ];\n    \n        if let Ok(res) = r.hset_multiple(\"bike:1\", &hash_fields) {\n            let res: String = res;\n            println!(\"{res}\"); // >>> OK\n        } else {\n            println!(\"Error setting bike:1\");\n        }\n    \n        match r.hget(\"bike:1\", \"model\") {\n            Ok(res) => {\n                let res: String = res;\n                println!(\"{res}\"); // >>> Deimos\n            },\n            Err(e) => {\n                println!(\"Error getting bike:1 model: {e}\");\n                return;\n            }   \n        }\n    \n        match r.hget(\"bike:1\", \"price\") {\n            Ok(res) => {\n                let res: String = res;\n                println!(\"{res}\"); // >>> 4972\n            },\n            Err(e) => {\n                println!(\"Error getting bike:1 price: {e}\");\n                return;\n            }   \n        }\n    \n        match r.hgetall(\"bike:1\") {\n            Ok(res) => {\n                let res: Vec<(String, String)> = res;\n                \n                for (key, value) in res {\n                    println!(\"{key}: {value}\");\n                }\n                // >>> model: Deimos\n                // >>> brand: Ergonom\n                // >>> type: Enduro bikes\n                // >>> price: 4972\n            },\n            Err(e) => {\n                println!(\"Error getting bike:1: {e}\");\n                return;\n            }   \n        }","section_id":"connect"}]}
{"schema_version":2,"id":"develop/clients/rust/json","title":"Work with JSON documents","url":"https://redis.io/docs/latest/develop/clients/rust/json/","summary":"Learn how to store, read, and update JSON documents with redis-rs.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-09T11:06:34+01:00","page_type":"content","content_hash":"cdf30a87ee96d9cb147818b803726443c95abc237fff7b1e267b86bd2260dcfc","sections":[{"id":"install","title":"Install","role":"setup","text":"Add `serde_json` and enable the `json` feature for `redis`.\nIf you want to use the async API, also enable a runtime integration such as `tokio-comp`.\n\n[code example]"},{"id":"import-the-required-crates","title":"Import the required crates","role":"content","text":"The example uses `serde_json::json!()` to build the document and the\n`JsonCommands` or `JsonAsyncCommands` trait to access Redis JSON commands.\n\nFoundational: Import the Redis JSON traits and serde_json helpers needed to work with JSON documents in Rust\n\n**Difficulty:** Beginner\n\n**Available in:** Rust (Asynchronous), Rust (Synchronous)\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"create-some-json-data","title":"Create some JSON data","role":"content","text":"Create a sample document representing a bike listing.\nThe nested `specs` and `inventory` objects and the `colors` array let you see how\nJSON paths work with more realistic data than a flat object.\n\nFoundational: Define a nested JSON document with objects and arrays using serde_json::json!\n\n**Difficulty:** Beginner\n\n**Available in:** Rust (Asynchronous), Rust (Synchronous)\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"connect-to-redis","title":"Connect to Redis","role":"content","text":"Connect to your Redis server in the usual way.\nSee [Connect to the server](https://redis.io/docs/latest/develop/clients/rust)\nfor more connection options.\n\nFoundational: Create a Redis client and open a sync or async connection from Rust\n\n**Difficulty:** Beginner\n\n**Available in:** Rust (Asynchronous), Rust (Synchronous)\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"store-and-retrieve-the-document","title":"Store and retrieve the document","role":"content","text":"Use [`JSON.SET`](https://redis.io/docs/latest/commands/json.set) to store the whole document at the root path `$`.\nYou can then retrieve the document again with [`JSON.GET`](https://redis.io/docs/latest/commands/json.get).\n\nFoundational: Store a complete JSON document with JSON.SET and fetch it again with JSON.GET\n\n**Difficulty:** Beginner\n\n**Available in:** Rust (Asynchronous), Rust (Synchronous)\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"read-nested-fields","title":"Read nested fields","role":"content","text":"JSON paths let you retrieve only the parts of the document you need.\nThis is useful when your application only needs a small subset of the data.\n\nRead nested data: Use JSON paths to retrieve selected fields and arrays without fetching the whole document\n\n**Difficulty:** Beginner\n\n**Available in:** Rust (Asynchronous), Rust (Synchronous)\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"update-part-of-the-document","title":"Update part of the document","role":"content","text":"You can update individual fields without replacing the whole document.\nThe example below changes the stock count and then applies a price change with\n[`JSON.NUMINCRBY`](https://redis.io/docs/latest/commands/json.numincrby).\n\nUpdate nested values: Modify individual fields in place with JSON.SET and JSON.NUMINCRBY\n\n**Difficulty:** Intermediate\n\n**Available in:** Rust (Asynchronous), Rust (Synchronous)\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"append-to-an-array","title":"Append to an array","role":"content","text":"You can also update arrays in place.\nThis example adds another color to the bike and then retrieves the updated array.\n\nUpdate arrays: Append new elements to a JSON array and read back the updated value\n\n**Difficulty:** Intermediate\n\n**Available in:** Rust (Asynchronous), Rust (Synchronous)\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"more-information","title":"More information","role":"content","text":"See the following pages to learn more:\n\n- [JSON data type](https://redis.io/docs/latest/develop/data-types/json)\n- [JSON path syntax](https://redis.io/docs/latest/develop/data-types/json/path)\n- [`redis-rs` documentation](https://docs.rs/redis/latest/redis/)"}],"examples":[{"id":"install-ex0","language":"toml","code":"[dependencies]\nserde_json = \"1\"\n\n# Sync API\nredis = { version = \"1.0.4\", features = [\"json\"] }\n\n# Async API with Tokio\ntokio = { version = \"1\", features = [\"full\"] }\nredis = { version = \"1.0.4\", features = [\"json\", \"tokio-comp\"] }","section_id":"install"},{"id":"import-the-required-crates-ex0","language":"rust","code":"use redis::{cmd, AsyncCommands, JsonAsyncCommands};\n    use serde_json::json;","section_id":"import-the-required-crates"},{"id":"import-the-required-crates-ex1","language":"rust","code":"use redis::{cmd, Commands, JsonCommands};\n    use serde_json::json;","section_id":"import-the-required-crates"},{"id":"create-some-json-data-ex0","language":"rust","code":"let bike = json!({\n            \"model\": \"Deimos\",\n            \"brand\": \"Ergonom\",\n            \"price\": 4972,\n            \"specs\": {\n                \"material\": \"carbon\",\n                \"weight\": 8.7\n            },\n            \"colors\": [\"black\", \"silver\"],\n            \"inventory\": {\n                \"in_stock\": 12,\n                \"warehouse\": \"w1\"\n            }\n        });","section_id":"create-some-json-data"},{"id":"create-some-json-data-ex1","language":"rust","code":"let bike = json!({\n            \"model\": \"Deimos\",\n            \"brand\": \"Ergonom\",\n            \"price\": 4972,\n            \"specs\": {\n                \"material\": \"carbon\",\n                \"weight\": 8.7\n            },\n            \"colors\": [\"black\", \"silver\"],\n            \"inventory\": {\n                \"in_stock\": 12,\n                \"warehouse\": \"w1\"\n            }\n        });","section_id":"create-some-json-data"},{"id":"connect-to-redis-ex0","language":"rust","code":"let client =\n            redis::Client::open(\"redis://127.0.0.1\").expect(\"Failed to create Redis client\");\n        let mut r = client\n            .get_multiplexed_async_connection()\n            .await\n            .expect(\"Failed to connect to Redis\");","section_id":"connect-to-redis"},{"id":"connect-to-redis-ex1","language":"rust","code":"let client =\n            redis::Client::open(\"redis://127.0.0.1\").expect(\"Failed to create Redis client\");\n        let mut r = client.get_connection().expect(\"Failed to connect to Redis\");","section_id":"connect-to-redis"},{"id":"store-and-retrieve-the-document-ex0","language":"rust","code":"let stored: bool = r\n            .json_set(\"bike:1\", \"$\", &bike)\n            .await\n            .expect(\"Failed to run JSON.SET\");\n        println!(\"{}\", if stored { \"OK\" } else { \"(nil)\" }); // >>> OK\n\n        let bike_json: String = r\n            .json_get(\"bike:1\", \"$\")\n            .await\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{bike_json}\");\n        // >>> [{\"model\":\"Deimos\",\"brand\":\"Ergonom\",\"price\":4972,\"specs\":{\"material\":\"carbon\",\"weight\":8.7},\"colors\":[\"black\",\"silver\"],\"inventory\":{\"in_stock\":12,\"warehouse\":\"w1\"}}]","section_id":"store-and-retrieve-the-document"},{"id":"store-and-retrieve-the-document-ex1","language":"rust","code":"let stored: bool = r\n            .json_set(\"bike:1\", \"$\", &bike)\n            .expect(\"Failed to run JSON.SET\");\n        println!(\"{}\", if stored { \"OK\" } else { \"(nil)\" }); // >>> OK\n\n        let bike_json: String = r.json_get(\"bike:1\", \"$\").expect(\"Failed to run JSON.GET\");\n        println!(\"{bike_json}\");\n        // >>> [{\"model\":\"Deimos\",\"brand\":\"Ergonom\",\"price\":4972,\"specs\":{\"material\":\"carbon\",\"weight\":8.7},\"colors\":[\"black\",\"silver\"],\"inventory\":{\"in_stock\":12,\"warehouse\":\"w1\"}}]","section_id":"store-and-retrieve-the-document"},{"id":"read-nested-fields-ex0","language":"rust","code":"let material: String = r\n            .json_get(\"bike:1\", \"$.specs.material\")\n            .await\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{material}\"); // >>> [\"carbon\"]\n\n        let colors: String = r\n            .json_get(\"bike:1\", \"$.colors\")\n            .await\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{colors}\"); // >>> [[\"black\",\"silver\"]]\n\n        let stock: String = r\n            .json_get(\"bike:1\", \"$.inventory.in_stock\")\n            .await\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{stock}\"); // >>> [12]","section_id":"read-nested-fields"},{"id":"read-nested-fields-ex1","language":"rust","code":"let material: String = r\n            .json_get(\"bike:1\", \"$.specs.material\")\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{material}\"); // >>> [\"carbon\"]\n\n        let colors: String = r\n            .json_get(\"bike:1\", \"$.colors\")\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{colors}\"); // >>> [[\"black\",\"silver\"]]\n\n        let stock: String = r\n            .json_get(\"bike:1\", \"$.inventory.in_stock\")\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{stock}\"); // >>> [12]","section_id":"read-nested-fields"},{"id":"update-part-of-the-document-ex0","language":"rust","code":"let stock_set: bool = r\n            .json_set(\"bike:1\", \"$.inventory.in_stock\", &json!(8))\n            .await\n            .expect(\"Failed to update stock\");\n        println!(\"{}\", if stock_set { \"OK\" } else { \"(nil)\" }); // >>> OK\n\n        let new_price: String = cmd(\"JSON.NUMINCRBY\")\n            .arg(\"bike:1\")\n            .arg(\"$.price\")\n            .arg(-500)\n            .query_async(&mut r)\n            .await\n            .expect(\"Failed to run JSON.NUMINCRBY\");\n        println!(\"{new_price}\"); // >>> [4472]\n\n        let updated_fields: String = r\n            .json_get(\"bike:1\", &[\"$.price\", \"$.inventory.in_stock\"])\n            .await\n            .expect(\"Failed to read updated fields\");\n        println!(\"{updated_fields}\"); // >>> {\"$.price\":[4472],\"$.inventory.in_stock\":[8]}","section_id":"update-part-of-the-document"},{"id":"update-part-of-the-document-ex1","language":"rust","code":"let stock_set: bool = r\n            .json_set(\"bike:1\", \"$.inventory.in_stock\", &json!(8))\n            .expect(\"Failed to update stock\");\n        println!(\"{}\", if stock_set { \"OK\" } else { \"(nil)\" }); // >>> OK\n\n        let new_price: String = cmd(\"JSON.NUMINCRBY\")\n            .arg(\"bike:1\")\n            .arg(\"$.price\")\n            .arg(-500)\n            .query(&mut r)\n            .expect(\"Failed to run JSON.NUMINCRBY\");\n        println!(\"{new_price}\"); // >>> [4472]\n\n        let updated_fields: String = r\n            .json_get(\"bike:1\", &[\"$.price\", \"$.inventory.in_stock\"])\n            .expect(\"Failed to read updated fields\");\n        println!(\"{updated_fields}\"); // >>> {\"$.price\":[4472],\"$.inventory.in_stock\":[8]}","section_id":"update-part-of-the-document"},{"id":"append-to-an-array-ex0","language":"rust","code":"let _: redis::Value = cmd(\"JSON.ARRAPPEND\")\n            .arg(\"bike:1\")\n            .arg(\"$.colors\")\n            .arg(\"\\\"red\\\"\")\n            .query_async(&mut r)\n            .await\n            .expect(\"Failed to run JSON.ARRAPPEND\");\n\n        let updated_colors: String = r\n            .json_get(\"bike:1\", \"$.colors\")\n            .await\n            .expect(\"Failed to read updated colors\");\n        println!(\"{updated_colors}\"); // >>> [[\"black\",\"silver\",\"red\"]]","section_id":"append-to-an-array"},{"id":"append-to-an-array-ex1","language":"rust","code":"let _: redis::Value = cmd(\"JSON.ARRAPPEND\")\n            .arg(\"bike:1\")\n            .arg(\"$.colors\")\n            .arg(\"\\\"red\\\"\")\n            .query(&mut r)\n            .expect(\"Failed to run JSON.ARRAPPEND\");\n\n        let updated_colors: String = r\n            .json_get(\"bike:1\", \"$.colors\")\n            .expect(\"Failed to read updated colors\");\n        println!(\"{updated_colors}\"); // >>> [[\"black\",\"silver\",\"red\"]]","section_id":"append-to-an-array"}]}
{"schema_version":2,"id":"develop/clients/rust/prob","title":"Probabilistic data types","url":"https://redis.io/docs/latest/develop/clients/rust/prob/","summary":"Learn how to use Bloom filter set membership and HyperLogLog approximate cardinality with redis-rs.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-09T11:06:34+01:00","page_type":"content","content_hash":"d06881bb9583e1bf5c3eedb98fedc7e9da11ac1fe90f9bc2e8b61c483f2fc587","sections":[{"id":"set-membership","title":"Set membership","role":"content","text":"A [Bloom filter](https://redis.io/docs/latest/develop/data-types/probabilistic/bloom-filter)\nlets you track whether or not a particular item has been added to a set.\nInstead of storing the items themselves, like a\n[set](https://redis.io/docs/latest/develop/data-types/sets), a Bloom filter records the\npresence or absence of the\n[hash value](https://en.wikipedia.org/wiki/Hash_function) of each item. This\ngives a very compact representation of the set's membership with a fixed memory\nsize, regardless of how many items you add. Note that there is an asymmetry\nbetween presence and absence of items in the set: if an item is reported as\nabsent, then it is definitely absent, but if it is reported as present, then\nthere is a small chance it may really be absent.\n\nThe `redis-rs` command traits provide high-level `bf_add`, `bf_madd`,\n`bf_exists`, and `bf_mexists` methods, among others. The following example adds\nsome names to a Bloom filter representing a list of users and then checks for\nthe presence or absence of users in the list.\n\nSet membership: Use Bloom filter to efficiently track item presence with minimal memory overhead\n\n**Difficulty:** Beginner\n\n**Available in:** Rust (Asynchronous), Rust (Synchronous)\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"set-cardinality","title":"Set cardinality","role":"content","text":"A HyperLogLog object calculates the approximate cardinality of a set. As you add\nitems, the HyperLogLog tracks the number of distinct set members, but it doesn't\nlet you retrieve those members or test whether a specific item was added.\n\nYou can also merge two or more HyperLogLogs to find the approximate cardinality\nof the [union](https://en.wikipedia.org/wiki/Union_(set_theory)) of the sets\nthey represent.\n\nSet cardinality: Estimate distinct item count using HyperLogLog with minimal memory usage\n\n**Difficulty:** Beginner\n\n**Available in:** Rust (Asynchronous), Rust (Synchronous)\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nThe main benefit that HyperLogLogs offer is their very low memory usage. They\ncan count up to 2^64 items with less than 1% standard error using a maximum\n12KB of memory."},{"id":"more-information","title":"More information","role":"content","text":"See the following pages to learn more:\n\n- [HyperLogLog](https://redis.io/docs/latest/develop/data-types/probabilistic/hyperloglogs)\n- [`redis-rs` command trait](https://docs.rs/redis/latest/redis/trait.Commands.html)\n- [`redis-rs` async command trait](https://docs.rs/redis/latest/redis/trait.AsyncCommands.html)"}],"examples":[{"id":"set-membership-ex0","language":"rust","code":"let res1: Vec<bool> = r\n            .bf_madd(\n                \"recorded_users\",\n                &[\"andy\", \"cameron\", \"david\", \"michelle\"],\n            )\n            .await\n            .expect(\"Failed to add users to Bloom filter\");\n        println!(\"{res1:?}\"); // >>> [true, true, true, true]\n\n        let res2: bool = r\n            .bf_exists(\"recorded_users\", \"cameron\")\n            .await\n            .expect(\"Failed to check Bloom filter\");\n        println!(\"{res2}\"); // >>> true\n\n        let res3: bool = r\n            .bf_exists(\"recorded_users\", \"kaitlyn\")\n            .await\n            .expect(\"Failed to check Bloom filter\");\n        println!(\"{res3}\"); // >>> false","section_id":"set-membership"},{"id":"set-membership-ex1","language":"rust","code":"let res1: Vec<bool> = r\n            .bf_madd(\n                \"recorded_users\",\n                &[\"andy\", \"cameron\", \"david\", \"michelle\"],\n            )\n            .expect(\"Failed to add users to Bloom filter\");\n        println!(\"{res1:?}\"); // >>> [true, true, true, true]\n\n        let res2: bool = r\n            .bf_exists(\"recorded_users\", \"cameron\")\n            .expect(\"Failed to check Bloom filter\");\n        println!(\"{res2}\"); // >>> true\n\n        let res3: bool = r\n            .bf_exists(\"recorded_users\", \"kaitlyn\")\n            .expect(\"Failed to check Bloom filter\");\n        println!(\"{res3}\"); // >>> false","section_id":"set-membership"},{"id":"set-cardinality-ex0","language":"rust","code":"let group1_added: bool = r\n            .pfadd(\"group:1\", &[\"andy\", \"cameron\", \"david\"])\n            .await\n            .expect(\"Failed to add items to group:1\");\n        println!(\"{group1_added}\"); // >>> true\n\n        let group1: usize = r.pfcount(\"group:1\").await.expect(\"Failed to count group:1\");\n        println!(\"{group1}\"); // >>> 3\n\n        let group2_added: bool = r\n            .pfadd(\"group:2\", &[\"kaitlyn\", \"michelle\", \"paolo\", \"rachel\"])\n            .await\n            .expect(\"Failed to add items to group:2\");\n        println!(\"{group2_added}\"); // >>> true\n\n        let group2: usize = r.pfcount(\"group:2\").await.expect(\"Failed to count group:2\");\n        println!(\"{group2}\"); // >>> 4\n\n        let _: () = r\n            .pfmerge(\"both_groups\", &[\"group:1\", \"group:2\"])\n            .await\n            .expect(\"Failed to merge HyperLogLogs\");\n        println!(\"OK\"); // >>> OK\n\n        let both_groups: usize = r\n            .pfcount(\"both_groups\")\n            .await\n            .expect(\"Failed to count both_groups\");\n        println!(\"{both_groups}\"); // >>> 7","section_id":"set-cardinality"},{"id":"set-cardinality-ex1","language":"rust","code":"let group1_added: bool = r\n            .pfadd(\"group:1\", &[\"andy\", \"cameron\", \"david\"])\n            .expect(\"Failed to add items to group:1\");\n        println!(\"{group1_added}\"); // >>> true\n\n        let group1: usize = r.pfcount(\"group:1\").expect(\"Failed to count group:1\");\n        println!(\"{group1}\"); // >>> 3\n\n        let group2_added: bool = r\n            .pfadd(\"group:2\", &[\"kaitlyn\", \"michelle\", \"paolo\", \"rachel\"])\n            .expect(\"Failed to add items to group:2\");\n        println!(\"{group2_added}\"); // >>> true\n\n        let group2: usize = r.pfcount(\"group:2\").expect(\"Failed to count group:2\");\n        println!(\"{group2}\"); // >>> 4\n\n        let _: () = r\n            .pfmerge(\"both_groups\", &[\"group:1\", \"group:2\"])\n            .expect(\"Failed to merge HyperLogLogs\");\n        println!(\"OK\"); // >>> OK\n\n        let both_groups: usize = r\n            .pfcount(\"both_groups\")\n            .expect(\"Failed to count both_groups\");\n        println!(\"{both_groups}\"); // >>> 7","section_id":"set-cardinality"}]}
{"schema_version":2,"id":"develop/clients/rust/transpipe","title":"Pipelines and transactions","url":"https://redis.io/docs/latest/develop/clients/rust/transpipe/","summary":"Learn how to use Redis pipelines and transactions","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-09T11:06:34+01:00","page_type":"content","content_hash":"637f74f84e364c4df0f90adb2c50a6ae4a2ac703b5ad3dd01306822e366e1de9","sections":[{"id":"execute-a-pipeline","title":"Execute a pipeline","role":"content","text":"To execute commands in a pipeline, you first create a pipeline object\nand then add commands to it using methods that resemble the standard\ncommand methods (for example, `set()` and `get()`). The commands are\nbuffered in the pipeline and only execute when you call the `exec()`\nmethod on the pipeline object. If you need the results from the\ncommands, use the `query()` method, which returns\nthe results from all the commands in order.\n\nNote that the command methods for a pipeline always return the original\npipeline object, so you can \"chain\" several commands together, as the\nexample below shows:\n\nFoundational: Use pipelines to batch multiple commands together and reduce network round trips\n\n**Difficulty:** Beginner\n\n**Available in:** Rust (Synchronous)\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"execute-a-transaction","title":"Execute a transaction","role":"content","text":"You can execute a simple transaction by adding the `atomic()` method to a pipeline.\n\nFoundational: Use transactions to execute multiple commands atomically without interruption from other clients\n\n**Difficulty:** Beginner\n\n**Available in:** Rust (Synchronous)\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"watch-keys-for-changes","title":"Watch keys for changes","role":"content","text":"Redis supports *optimistic locking* to avoid inconsistent updates\nto different keys. The basic idea is to watch for changes to any\nkeys that you use in a transaction while you are processing the\nupdates. If the watched keys do change, you must restart the updates\nwith the latest data from the keys. See\n[Transactions](https://redis.io/docs/latest/develop/using-commands/transactions)\nfor more information about optimistic locking.\n\nThe example below shows how to use the `transaction()` function to\nautomatically retry a transaction when watched keys are modified.\nPass the list of keys you want to watch and a closure representing the transaction.\nThe closure receives the original connection and a pipeline as parameters.\nUse the connection to read the latest values from the watched keys,\nbut always use the pipeline to add all the commands that make up the watched transaction.\nIf the watched keys are modified during the transaction, the `transaction()` function\nautomatically retries the transaction until it succeeds.\n\nOptimistic locking: Monitor keys for changes and retry the transaction when conflicts occur\n\n**Difficulty:** Intermediate\n\n**Available in:** Rust (Synchronous)\n\n##### Rust (Synchronous)\n\n[code example]"}],"examples":[{"id":"execute-a-pipeline-ex0","language":"rust","code":"// Check the success of the pipeline without checking the results\n        // individually.\n        match redis::pipe()\n            .set(\"seat:0\", \"#0\")\n            .set(\"seat:1\", \"#1\")\n            .set(\"seat:2\", \"#2\")\n            .set(\"seat:3\", \"#3\")\n            .set(\"seat:4\", \"#4\")\n            .exec(&mut r)\n        {\n            Ok(_) => {\n                println!(\"Pipe executed successfully\");\n            },\n            Err(e) => {\n                println!(\"Error executing pipe: {e}\");\n                return;\n            }\n        };\n        \n        // Check the success of the pipeline and the results individually.\n        let (seat_0, seat_1, seat_2, seat_3, seat_4) :\n        (String, String, String, String, String) = match redis::pipe()\n            .get(\"seat:0\")\n            .get(\"seat:1\")\n            .get(\"seat:2\")\n            .get(\"seat:3\")\n            .get(\"seat:4\")\n            .query(&mut r) {\n                Ok(res) => res,\n                Err(e) => {\n                    println!(\"Error executing pipe: {e}\");\n                    return;\n                }\n            };\n\n        println!(\"{seat_0}, {seat_1}, {seat_2}, {seat_3}, {seat_4}\");\n        // >>> #0, #1, #2, #3, #4\n\n        // Use `ignore()` to ignore the result of specific commands.\n        let (seat_5, seat_6, seat_7) :\n            (String, String, String) = match redis::pipe()\n            .set(\"seat:5\", \"#5\").ignore()\n            .set(\"seat:6\", \"#6\").ignore()\n            .set(\"seat:7\", \"#7\").ignore()\n            .get(\"seat:5\")\n            .get(\"seat:6\")\n            .get(\"seat:7\")\n            .query(&mut r) {\n                Ok(res) => res,\n                Err(e) => {\n                    println!(\"Error executing pipe: {e}\");\n                    return;\n                }\n            };\n\n        println!(\"{seat_5}, {seat_6}, {seat_7}\");\n        // >>> #5, #6, #7","section_id":"execute-a-pipeline"},{"id":"execute-a-transaction-ex0","language":"rust","code":"match redis::pipe()\n            .atomic()\n            .incr(\"counter:1\", 1)\n            .incr(\"counter:2\", 2)\n            .incr(\"counter:3\", 3)\n            .exec(&mut r)\n        {\n            Ok(_) => {\n                println!(\"Transaction executed successfully\");\n            },\n            Err(e) => {\n                println!(\"Error executing transaction: {e}\");\n                return;\n            }\n        };","section_id":"execute-a-transaction"},{"id":"watch-keys-for-changes-ex0","language":"rust","code":"let key = \"shellpath\";\n        let _: () = r.set(key, \"/usr/syscmds/\").unwrap();\n        \n        let Ok(_,): Result<((),), _> = redis::transaction(&mut r, &[key], |r, pipe| {\n            let mut path: String = r.get(key).unwrap();\n            path.push_str(\":/usr/mycmds/\");\n            pipe.set(key, path).query(r)\n        }) else {\n            println!(\"Error executing transaction\");\n            return;\n        };\n\n        match r.get(\"shellpath\") {\n            Ok(res) => {\n                let res: String = res;\n                println!(\"{res}\");\n                // >>> /usr/syscmds/:/usr/mycmds/\n            },\n            Err(e) => {\n                println!(\"Error getting shellpath: {e}\");\n                return;\n            }\n        };","section_id":"watch-keys-for-changes"}]}
{"schema_version":2,"id":"develop/clients/sch","title":"Smart client handoffs","url":"https://redis.io/docs/latest/develop/clients/sch/","summary":"Learn how to avoid disruptions during Redis server maintenance.","aliases":["/develop/clients/sce/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-13T14:53:04+01:00","page_type":"content","content_hash":"4c45ae2f2550b9d57f530c78fa395afac0fa97990e3d8f08313a5d1823adee3f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"*Smart client handoffs (SCH)* is a feature of Redis Cloud and\nRedis Software servers that lets them actively notify clients\nabout planned server maintenance shortly before it happens. This\nlets a client reconnect or otherwise respond gracefully without significant\ninterruptions in service.\n\nSCH is primarily useful when server software or hardware upgrades\nare required. It provides two main features to help the\nclient avoid disruptions in service during the maintenance period:\n\n-   **Relaxed timeouts**: Upgrades tend to impact the general performance of the server.\n    Advance notification of the upgrade lets a client adjust its command\n    timeouts to take this into account and avoid aborting commands too soon.\n-   **Pre-handoffs**: Upgrades also involve migrating\n    Redis shards to new nodes, which inevitably disconnects clients from\n    existing nodes. However, with some advance warning of the disconnection,\n    a client can buffer commands, connect to a new node, and then resume\n    the buffered commands without aborting any of them. As a result, users\n    see no disruption in service. These transparent reconnections to new endpoints\n    are known as *pre-handoffs*.\n\n> [!NOTE]\n> SCH does not work with blocking connections.\n> These include connections used for blocking operations like\n> [`BLPOP`](https://redis.io/docs/latest/commands/blpop) and also\n> [pub/sub](https://redis.io/docs/latest/develop/pubsub) subscriptions.\n> All non-blocking operations are safe to use with SCH."},{"id":"sch-support-in-redis-client-libraries","title":"SCH support in Redis client libraries","role":"content","text":"SCH is enabled automatically on the client side during connection\nif you select the [RESP3](https://redis.io/docs/latest/develop/reference/protocol-spec#resp-versions)\nprotocol, which is a requirement for SCH. However, you can\nconfigure some parameters, such as the timeouts to use\nduring maintenance.\n\nThe following table lists the Redis client libraries that support SCH,\nand the versions that added support for standard routing and for\n[OSS Cluster API](https://redis.io/docs/latest/operate/rs/databases/configure/oss-cluster-api) connections.\nStandard routing covers any connection that doesn't use the OSS Cluster API.\n\n| Client | Standard routing | OSS Cluster API |\n| :-- | :-- | :-- |\n| [redis-py](https://redis.io/docs/latest/develop/clients/redis-py/connect#connect-using-smart-client-handoffs-sch) | v7.0.0 | v7.2.0 |\n| [node-redis](https://redis.io/docs/latest/develop/clients/nodejs/connect#connect-using-smart-client-handoffs-sch) | v5.9.0 | v5.11.0 |\n| [Lettuce](https://redis.io/docs/latest/develop/clients/lettuce/connect#connect-using-smart-client-handoffs-sch) | v7.0.0 | - |\n| [go-redis](https://redis.io/docs/latest/develop/clients/go/connect#connect-using-smart-client-handoffs-sch) | v9.16.0 | v9.18.0 |\n| [StackExchange.Redis](https://redis.io/docs/latest/develop/clients/dotnet/connect#connect-using-smart-client-handoffs-sch) | v3.3.0 | v3.3.0 |\n\n> [!NOTE]\n> SCH is currently disabled when a client is configured for\n> [Client-side geographic failover](https://redis.io/docs/latest/develop/clients/failover).\n> Integration of the two features is planned for a future release."},{"id":"sch-support-in-redis-server-products","title":"SCH support in Redis server products","role":"content","text":""},{"id":"redis-cloud","title":"Redis Cloud","role":"content","text":"SCH is fully supported and enabled by default on Redis Cloud, except when you\nare using one of the following options:\n\n- [AWS PrivateLink](https://redis.io/docs/latest/operate/rc/security/aws-privatelink)\n- [Google Cloud Private Service Connect](https://redis.io/docs/latest/operate/rc/security/private-service-connect)\n\nThese services don't currently allow for pre-handoffs, but you still get the\nbenefit of relaxed timeouts during database version upgrades. All other\nconfigurations have full support for both relaxed timeouts and pre-handoffs."},{"id":"redis-software","title":"Redis Software","role":"content","text":"You must enable SCH explicitly on self-managed Redis Software servers by using the\n[v1/cluster](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster)\nREST API request to set the `client_maint_notifications` option to `true`.\nThe example below shows how to do this using the\n[`curl`](https://curl.se/) command line utility:\n\n[code example]\n\nRedis Software uses relaxed timeouts to avoid command failures during\ndatabase version upgrades. SCH is supported for Redis Software from v8.0.2 on,\nand OSS Cloud and OSS Cluster API from v8.0.16 on.\n\nThe degree of support for pre-handoffs depends on\nthe specific upgrade method you use, as detailed in the table below.\n\n| Upgrade method | SCH support | Expected behavior |\n| --- | --- | --- |\n| [Rolling upgrade](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-cluster#rolling-upgrade) | Full | New nodes and old ones removed sequentially. SCH pre-handoffs and relaxed timeouts greatly reduce disruptions during the upgrade. |\n| [In-place upgrade](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-cluster#in-place-upgrade) | Partial | Relaxed timeouts reduce errors but there are no pre-handoffs. Disconnections occur when processes are replaced during the upgrade, so clients should rely on auto-reconnect, which will cause brief lapses in service. |\n| [Maintenance mode](https://redis.io/docs/latest/operate/rs/clusters/maintenance-mode) | Full | SCH is fully supported during hardware or OS patching operations. Pre-handoffs and relaxed timeouts minimize application impact. |"},{"id":"redis-enterprise-for-kubernetes","title":"Redis Enterprise for Kubernetes","role":"content","text":"SCH is not currently supported for [Kubernetes](https://redis.io/docs/latest/operate/kubernetes) clusters."},{"id":"redis-open-source","title":"Redis Open Source","role":"content","text":"SCH is not currently supported for [Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack)."}],"examples":[{"id":"redis-software-ex0","language":"bash","code":"curl -k -X PUT -H \"accept: application/json\" \\\n    -H \"content-type: application/json\" \\\n    -u \"test@redis.com:test123\" \\\n    -d '{ \"client_maint_notifications\": true }' \\\n    https://localhost:9443/v1/cluster","section_id":"redis-software"}]}
{"schema_version":2,"id":"develop/clients/version-support","title":"Client library support and versioning policy","url":"https://redis.io/docs/latest/develop/clients/version-support/","summary":"Understand how Redis maintains and versions its official client libraries","aliases":["/develop/connect/clients/version-support"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-09T13:53:13+01:00","page_type":"content","content_hash":"7706d61175d3f041408810e91858aa2a0be7d7d5a35b3934b528fee88ad5a01d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Official Redis client libraries are covered by the\n[Redis Software Support Policy](https://redis.io/legal/software-support-policy/).\nThis page describes the client-library-specific maintenance and versioning\npolicy that clarifies active support, testing expectations, and backporting\nrules for those clients.\n\nThe policy is based on the non-EOL Redis Server **major.minor** versions\ncurrently available in Redis Cloud, as listed in\n[Supported database versions](https://redis.io/docs/latest/operate/rc/databases/version-management#supported-database-versions)."},{"id":"versioning","title":"Versioning","role":"compatibility","text":"Redis client libraries use a `major.minor.patch` versioning scheme that follows\n[Semantic Versioning 2.0.0](https://semver.org/):\n\n-   **Major** versions may introduce incompatible API changes.\n-   **Minor** versions add backward-compatible functionality.\n-   **Patch** versions contain backward-compatible bug fixes."},{"id":"maintained-release-line","title":"Maintained release line","role":"content","text":"The latest `major.minor` release of each official Redis client library is the\nprimary maintained release line. In practice, this means it is the line under\nactive development and the one where regular bug fixes and new features are\ndelivered.\n\nClient libraries follow a forward-moving model: each new `major.minor` release\nis cut from the main branch, and a version branch is created at the same time\nfor future patch and maintenance releases (for example, `5.2.x`)."},{"id":"supported-redis-api-versions","title":"Supported Redis API versions","role":"compatibility","text":"Each new client `major`, `minor`, and `patch` release is tested against a\ncompatibility matrix of the\n[non-EOL Redis API versions](https://redis.io/docs/latest/operate/rc/databases/version-management#supported-database-versions)\ncurrently available. For example, if the supported API versions are 6.2, 7.2,\nand 7.4, then each client release is tested against all three. The release notes\nfor each client report the compatibility matrix used for testing.\n\nThe table below shows some examples of client releases and the API versions they\nare tested against:\n\n| Example client release | Supported Redis API versions | Client testing targets |\n| :-- | :-- | :-- |\n| Jedis 5.2 | 6.2, 7.2, 7.4 | Test with 6.2, 7.2, 7.4 |\n| redis-py 5.3 | 6.2, 7.2, 7.4, 8.0 | Test with 6.2, 7.2, 7.4, 8.0 |\n| Jedis 6.0 | 6.2, 7.2, 7.4, 8.2 | Test with 6.2, 7.2, 7.4, 8.2 |\n\nA similar matrix is also published on each client library's GitHub repository."},{"id":"bugs-vulnerabilities-and-community-contributions","title":"Bugs, vulnerabilities, and community contributions","role":"content","text":"Redis keeps innovation on the latest maintained line and reserves older lines\nfor critical fixes and security updates. This keeps the support matrix clean and\nreduces the testing and maintenance overhead of supporting many branches at once.\n\n-   **Regular bug fixes** are delivered either in a new `major.minor` release or,\n    depending on the overall content, in a new patch release on the current\n    maintained line. Non-critical bug fixes and new features are *not* backported\n    to older client `major.minor` versions. For example, if a bug is found in a\n    client library release 5.2 and the fix is released in 5.2.1, the same\n    non-critical fix is not backported to older versions such as 5.1.\n-   **Critical bugs and security vulnerabilities**, including CVEs, are backported\n    to the latest patch version of a `major.minor` and may also be backported to\n    patch releases of older versions.\n-   **Community-contributed fixes** to older versions are reviewed on a\n    case-by-case basis. If a fix is considered useful for future versions, it is\n    also ported to the main branch for a future major, minor, or patch release."},{"id":"recommendations","title":"Recommendations","role":"content","text":"-   Use the latest `major.minor` client library release available.\n-   Read the release notes for version-specific changes and for the compatibility\n    matrix used in testing.\n-   Test upgrades with production-like workloads and configurations before rolling\n    them out broadly, especially when moving across major or minor versions."}],"examples":[]}
{"schema_version":2,"id":"develop/data-types/arrays","title":"Redis arrays","url":"https://redis.io/docs/latest/develop/data-types/arrays/","summary":"Introduction to Redis arrays","aliases":["/develop/data-types/array/"],"tags":["docs","develop","stack","oss","rs","rc","kubernetes","clients"],"last_updated":"2026-09-28T15:41:00+02:00","page_type":"content","content_hash":"e4e44b02607c4609a9868babcd030b64348b34b79e5453730099912abfc4dfdc","sections":[{"id":"array-command-summary","title":"Array command summary","role":"content","text":"**18 commands in this group:**\n\n[View all array commands](https://redis.io/commands/?group=array)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [ARCOUNT](https://redis.io/commands/arcount/) | Returns the number of non-empty elements in an array. | O(1) | 8.8.0 |\n| [ARDEL](https://redis.io/commands/ardel/) | Deletes elements at the specified indices in an array. | O(N) where N is the number of indices to delete | 8.8.0 |\n| [ARDELRANGE](https://redis.io/commands/ardelrange/) | Deletes elements in one or more ranges. | Proportional to the number of existing elements... | 8.8.0 |\n| [ARGET](https://redis.io/commands/arget/) | Gets the value at an index in an array. | O(1) | 8.8.0 |\n| [ARGETRANGE](https://redis.io/commands/argetrange/) | Gets values in a range of indices. | O(N) where N is the range length | 8.8.0 |\n| [ARGREP](https://redis.io/commands/argrep/) | Searches array elements in a range using textual predicates. | O(P * C) where P is the number of visited posit... | 8.8.0 |\n| [ARINFO](https://redis.io/commands/arinfo/) | Returns metadata about an array. | O(1), or O(N) with FULL option where N is the n... | 8.8.0 |\n| [ARINSERT](https://redis.io/commands/arinsert/) | Inserts one or more values at consecutive indices. | O(N) where N is the number of values | 8.8.0 |\n| [ARLASTITEMS](https://redis.io/commands/arlastitems/) | Returns the most recently inserted elements. | O(N) where N is the count | 8.8.0 |\n| [ARLEN](https://redis.io/commands/arlen/) | Returns the length of an array (max index + 1). | O(1) | 8.8.0 |\n\n#### Array command summary (part 2)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [ARMGET](https://redis.io/commands/armget/) | Gets values at multiple indices in an array. | O(N) where N is the number of indices | 8.8.0 |\n| [ARMSET](https://redis.io/commands/armset/) | Sets multiple index-value pairs in an array. | O(N) where N is the number of pairs | 8.8.0 |\n| [ARNEXT](https://redis.io/commands/arnext/) | Returns the next index ARINSERT would use. | O(1) | 8.8.0 |\n| [AROP](https://redis.io/commands/arop/) | Performs aggregate operations on array elements in a range. | O(P) where P is visited positions in touched sl... | 8.8.0 |\n| [ARRING](https://redis.io/commands/arring/) | Inserts values into a ring buffer of specified size, wrapping and truncating as needed. | O(M) normally, O(N+M) on ring resize, where N i... | 8.8.0 |\n| [ARSCAN](https://redis.io/commands/arscan/) | Iterates existing elements in a range, returning index-value pairs. | O(P) where P is visited positions in touched sl... | 8.8.0 |\n| [ARSEEK](https://redis.io/commands/arseek/) | Sets the ARINSERT / ARRING cursor to a specific index. | O(1) | 8.8.0 |\n| [ARSET](https://redis.io/commands/arset/) | Sets one or more contiguous values starting at an index in an array. | O(N) where N is the number of values | 8.8.0 |\n\n\n\nRedis arrays are sparse, index-addressable data structures that map integer indexes (in the range 0 to 2⁶⁴−1) to string values. Unlike lists, elements are accessed directly by index rather than by position in a sequence, and you can set any index without allocating the gaps between occupied slots. This makes arrays well-suited for timestamped event logs, ring buffers over streaming measurements, sliding-window analytics, and other workloads that involve sparse or high-index access patterns."},{"id":"basic-usage","title":"Basic usage","role":"content","text":"Use [`ARSET`](https://redis.io/docs/latest/commands/arset) to write one or more contiguous values starting at a given index, and [`ARGET`](https://redis.io/docs/latest/commands/arget) to read the value at an index. Accessing an unset index returns a nil reply.\n\nWrite contiguous values with ARSET and read a single index with ARGET; an unset index returns nil\n\n**Commands:** ARSET, ARGET\n\n**Complexity:**\n- ARSET: O(N)\n- ARGET: O(1)\n\n**Available in:** Redis CLI, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), JavaScript (Node.js), PHP, Python\n\n##### Redis CLI\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nTo write values at arbitrary, non-contiguous indexes, use [`ARMSET`](https://redis.io/docs/latest/commands/armset). To read several indexes in one round trip, use [`ARMGET`](https://redis.io/docs/latest/commands/armget):\n\nWrite to arbitrary, non-contiguous indexes with ARMSET and read several indexes in one round trip with ARMGET\n\n**Commands:** ARMSET, ARMGET\n\n**Complexity:**\n- ARMSET: O(N)\n- ARMGET: O(N)\n\n**Available in:** Redis CLI, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), JavaScript (Node.js), PHP, Python\n\n##### Redis CLI\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]"},{"id":"array-length-vs-element-count","title":"Array length vs. element count","role":"content","text":"Redis arrays expose two distinct size measurements:\n\n- [`ARLEN`](https://redis.io/docs/latest/commands/arlen) returns the *logical length*: the highest set index plus one.\n- [`ARCOUNT`](https://redis.io/docs/latest/commands/arcount) returns the number of *non-empty* elements.\n\nFor a sparse array, these values can differ substantially:\n\nCompare the logical length (ARLEN) with the number of non-empty elements (ARCOUNT) for a sparse array\n\n**Commands:** ARSET, ARLEN, ARCOUNT\n\n**Complexity:**\n- ARSET: O(N)\n- ARLEN: O(1)\n- ARCOUNT: O(1)\n\n**Available in:** Redis CLI, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), JavaScript (Node.js), PHP, Python\n\n##### Redis CLI\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]"},{"id":"reading-ranges","title":"Reading ranges","role":"content","text":"[`ARGETRANGE`](https://redis.io/docs/latest/commands/argetrange) returns every position in a range—including empty slots as nil—in index order. Reversing `start` and `end` reverses the direction:\n\nRead every position in a range with ARGETRANGE, including empty slots returned as nil\n\n**Commands:** ARMSET, ARGETRANGE\n\n**Complexity:**\n- ARMSET: O(N)\n- ARGETRANGE: O(N)\n\n**Available in:** Redis CLI, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), JavaScript (Node.js), PHP, Python\n\n##### Redis CLI\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nTo iterate only the elements that exist and retrieve their indexes alongside their values, use [`ARSCAN`](https://redis.io/docs/latest/commands/arscan). It skips empty slots and returns an array of index-value pairs, with an optional `LIMIT` to cap the result size:\n\nIterate only the elements that exist with ARSCAN, retrieving each index alongside its value\n\n**Builds upon:** argetrange\n\n**Commands:** ARSCAN\n\n**Complexity:**\n- ARSCAN: O(P)\n\n**Available in:** Redis CLI, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), JavaScript (Node.js), PHP, Python\n\n##### Redis CLI\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]"},{"id":"sequential-insertion","title":"Sequential insertion","role":"content","text":"[`ARINSERT`](https://redis.io/docs/latest/commands/arinsert) appends values using an internal cursor that advances automatically after each call. Use [`ARNEXT`](https://redis.io/docs/latest/commands/arnext) to inspect where the next insert would land, and [`ARSEEK`](https://redis.io/docs/latest/commands/arseek) to reposition the cursor:\n\nAppend values with ARINSERT using an auto-advancing cursor, inspect it with ARNEXT, and reposition it with ARSEEK\n\n**Commands:** ARINSERT, ARNEXT, ARSEEK\n\n**Complexity:**\n- ARINSERT: O(N)\n- ARNEXT: O(1)\n- ARSEEK: O(1)\n\n**Available in:** Redis CLI, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), JavaScript (Node.js), PHP, Python\n\n##### Redis CLI\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]"},{"id":"ring-buffer-mode","title":"Ring buffer mode","role":"content","text":"[`ARRING`](https://redis.io/docs/latest/commands/arring) turns an array into a fixed-size circular buffer. Each call inserts a value at `insert_idx % size`, wrapping back to index `0` once the window is full and overwriting the oldest entry:\n\nUse ARRING to maintain a fixed-size circular buffer that wraps and overwrites the oldest entry once full\n\n**Commands:** ARRING, ARGET\n\n**Complexity:**\n- ARRING: O(M)\n- ARGET: O(1)\n\n**Available in:** Redis CLI, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), JavaScript (Node.js), PHP, Python\n\n##### Redis CLI\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\n[`ARLASTITEMS`](https://redis.io/docs/latest/commands/arlastitems) retrieves the *N* most recently inserted elements in chronological order. Pass the `REV` flag to reverse the order:\n\nRetrieve the N most recently inserted elements with ARLASTITEMS, optionally reversing the order with REV\n\n**Builds upon:** arring\n\n**Commands:** ARLASTITEMS\n\n**Complexity:**\n- ARLASTITEMS: O(N)\n\n**Available in:** Redis CLI, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), JavaScript (Node.js), PHP, Python\n\n##### Redis CLI\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]"},{"id":"aggregate-operations","title":"Aggregate operations","role":"content","text":"[`AROP`](https://redis.io/docs/latest/commands/arop) performs a single-pass aggregate over a contiguous range of elements:\n\n| Operation | Description |\n|-----------|-------------|\n| `SUM` | Sum of numeric values |\n| `MIN` | Minimum numeric value |\n| `MAX` | Maximum numeric value |\n| `AND` / `OR` / `XOR` | Bitwise operation on integer values |\n| `MATCH value` | Count of elements equal to `value` |\n| `USED` | Count of non-empty elements in the range |\n\nRun a single-pass aggregate over a contiguous range with AROP, such as SUM, MAX, or a MATCH count\n\n**Commands:** ARMSET, AROP\n\n**Complexity:**\n- ARMSET: O(N)\n- AROP: O(P)\n\n**Available in:** Redis CLI, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), JavaScript (Node.js), PHP, Python\n\n##### Redis CLI\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]"},{"id":"searching-elements","title":"Searching elements","role":"content","text":"[`ARGREP`](https://redis.io/docs/latest/commands/argrep) finds elements in a range whose values match one or more textual predicates and returns their indexes. Empty slots are skipped. Four predicate forms are supported: `EXACT` (full equality), `MATCH` (substring), `GLOB` (the same wildcard syntax as [`SCAN`](https://redis.io/docs/latest/commands/scan) `MATCH`), and `RE` (regular expression). Multiple predicates are combined with `OR` by default, or with `AND` when the option is given. Pass `NOCASE` for case-insensitive comparisons, `WITHVALUES` to return matching values alongside their indexes, and `LIMIT` to cap the number of matches.\n\nThis is particularly useful when an array stores line-indexed text such as a log file, where each element holds one line:\n\nFind elements matching textual predicates (EXACT, MATCH, GLOB, RE) with ARGREP, combined with AND or OR\n\n**Commands:** DEL, ARMSET, ARGREP\n\n**Complexity:**\n- DEL: O(N)\n- ARMSET: O(N)\n- ARGREP: O(P * C)\n\n**Available in:** Redis CLI, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), JavaScript (Node.js), PHP, Python\n\n##### Redis CLI\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nThe special values `-` and `+` denote the first and last index of the array. Combined with [ring buffer mode](#ring-buffer-mode), this lets a fixed-size array hold the most recent *N* log lines and be searched in place."},{"id":"deleting-elements","title":"Deleting elements","role":"content","text":"[`ARDEL`](https://redis.io/docs/latest/commands/ardel) deletes one or more elements by index and returns the count of elements actually removed. [`ARDELRANGE`](https://redis.io/docs/latest/commands/ardelrange) removes all elements within an index range; reversing `start` and `end` is supported:\n\nDelete elements by index with ARDEL or remove a whole index range with ARDELRANGE\n\n**Builds upon:** arop\n\n**Commands:** ARDEL, ARDELRANGE\n\n**Complexity:**\n- ARDEL: O(N)\n\n**Available in:** Redis CLI, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), JavaScript (Node.js), PHP, Python\n\n##### Redis CLI\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nDeleting the last remaining element removes the key entirely."},{"id":"introspection","title":"Introspection","role":"content","text":"[`ARINFO`](https://redis.io/docs/latest/commands/arinfo) returns metadata about an array's internal structure, including its logical length, element count, and next insert index. Pass the `FULL` option to include per-slice statistics such as fill rates and counts of dense versus sparse slices:\n\n[code example]"},{"id":"configuration","title":"Configuration","role":"configuration","text":"The following configuration parameters affect array behavior:\n- `array-slice-size`\n- `array-sparse-kmax`\n- `array-sparse-kmin`\n\nSee the [Redis configuration page](https://redis.io/docs/latest/operate/oss_and_stack/management/config) for details."},{"id":"performance","title":"Performance","role":"performance","text":"Most array commands are O(1), including [`ARSET`](https://redis.io/docs/latest/commands/arset), [`ARGET`](https://redis.io/docs/latest/commands/arget), [`ARDEL`](https://redis.io/docs/latest/commands/ardel), [`ARINSERT`](https://redis.io/docs/latest/commands/arinsert), [`ARNEXT`](https://redis.io/docs/latest/commands/arnext), [`ARSEEK`](https://redis.io/docs/latest/commands/arseek), [`ARCOUNT`](https://redis.io/docs/latest/commands/arcount), and [`ARLEN`](https://redis.io/docs/latest/commands/arlen). Operations that touch N elements—such as [`ARGETRANGE`](https://redis.io/docs/latest/commands/argetrange), [`ARSCAN`](https://redis.io/docs/latest/commands/arscan), [`ARDELRANGE`](https://redis.io/docs/latest/commands/ardelrange), [`AROP`](https://redis.io/docs/latest/commands/arop), and [`ARLASTITEMS`](https://redis.io/docs/latest/commands/arlastitems)—are O(N). The underlying sliced-array encoding handles both dense and sparse access patterns efficiently, so large index gaps consume very little memory."},{"id":"alternatives","title":"Alternatives","role":"content","text":"Arrays complement rather than replace the other Redis collection types:\n\n- Use [Redis lists](https://redis.io/docs/latest/develop/data-types/lists) when you need push/pop operations at either end, or when you need to insert elements between existing ones.\n- Use [Redis hashes](https://redis.io/docs/latest/develop/data-types/hashes) when values are addressed by field name rather than by numeric index.\n- Use [Redis streams](https://redis.io/docs/latest/develop/data-types/streams) when you need an append-only event log with consumer groups and acknowledgements."},{"id":"limits","title":"Limits","role":"limits","text":"[`ARGETRANGE`](https://redis.io/docs/latest/commands/argetrange) enforces a hard limit of 1,000,000 elements per call to guard against accidentally large range reads."}],"examples":[{"id":"basic-usage-ex0","language":"plaintext","code":"> ARSET events:1 0 \"login\" \"click\" \"purchase\"\n(integer) 3\n> ARGET events:1 0\n\"login\"\n> ARGET events:1 999\n(nil)","section_id":"basic-usage"},{"id":"basic-usage-ex1","language":"go","code":"setRes, err := rdb.ARSet(ctx, \"events:1\", 0, \"login\", \"click\", \"purchase\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(setRes) // >>> 3\n\n\tgetRes, err := rdb.ARGet(ctx, \"events:1\", 0).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(getRes) // >>> login\n\n\tmissing, err := rdb.ARGet(ctx, \"events:1\", 999).Result()\n\n\tif err == redis.Nil {\n\t\tfmt.Println(\"<nil>\") // >>> <nil>\n\t} else if err != nil {\n\t\tpanic(err)\n\t} else {\n\t\tfmt.Println(missing)\n\t}","section_id":"basic-usage"},{"id":"basic-usage-ex2","language":"java","code":"CompletableFuture<Void> arsetArgetExample = asyncCommands\n                    .arset(\"events:1\", 0, \"login\", \"click\", \"purchase\")\n                    .thenCompose(res1 -> {\n                        System.out.println(res1);\n                        // >>> 3\n                        return asyncCommands.arget(\"events:1\", 0);\n                    })\n                    .thenCompose(res2 -> {\n                        System.out.println(res2);\n                        // >>> login\n                        return asyncCommands.arget(\"events:1\", 999);\n                    })\n                    .thenAccept(res3 -> {\n                        System.out.println(res3);\n                        // >>> null\n                    })\n                    .toCompletableFuture();\n\n            arsetArgetExample.join();","section_id":"basic-usage"},{"id":"basic-usage-ex3","language":"java","code":"Mono<Long> arsetArget1 = reactiveCommands.arset(\"events:1\", 0, \"login\", \"click\", \"purchase\")\n                    .doOnNext(result -> {\n                        System.out.println(result); // >>> 3\n                    });\n\n            arsetArget1.block();\n\n            Mono<String> arsetArget2 = reactiveCommands.arget(\"events:1\", 0).doOnNext(result -> {\n                System.out.println(result); // >>> login\n            });\n\n            arsetArget2.block();\n\n            Mono<Optional<String>> arsetArget3 = reactiveCommands.arget(\"events:1\", 999)\n                    .map(Optional::of)\n                    .defaultIfEmpty(Optional.empty())\n                    .doOnNext(result -> {\n                        System.out.println(result.orElse(null)); // >>> null\n                    });\n\n            arsetArget3.block();","section_id":"basic-usage"},{"id":"basic-usage-ex4","language":"javascript","code":"const setResult = await client.arSet('events:1', 0, ['login', 'click', 'purchase']);\nconsole.log(setResult); // >>> 3\n\nconst getResult = await client.arGet('events:1', 0);\nconsole.log(getResult); // >>> login\n\nconst missingResult = await client.arGet('events:1', 999);\nconsole.log(missingResult); // >>> null","section_id":"basic-usage"},{"id":"basic-usage-ex5","language":"php","code":"$res1 = $redis->arset('events:1', 0, ['login', 'click', 'purchase']);\n        echo $res1 . PHP_EOL; // >>> 3\n\n        $res2 = $redis->arget('events:1', 0);\n        echo $res2 . PHP_EOL; // >>> login\n\n        $res3 = $redis->arget('events:1', 999);\n        echo var_export($res3, true) . PHP_EOL; // >>> NULL","section_id":"basic-usage"},{"id":"basic-usage-ex6","language":"python","code":"res1 = r.arset(\"events:1\", 0, \"login\", \"click\", \"purchase\")\nprint(res1)\n# >>> 3\n\nres2 = r.arget(\"events:1\", 0)\nprint(res2)\n# >>> login\n\nres3 = r.arget(\"events:1\", 999)\nprint(res3)\n# >>> None","section_id":"basic-usage"},{"id":"basic-usage-ex7","language":"plaintext","code":"> ARMSET metrics 0 \"10\" 5 \"20\" 100 \"30\"\n(integer) 3\n> ARMGET metrics 0 5 100 999\n1) \"10\"\n2) \"20\"\n3) \"30\"\n4) (nil)","section_id":"basic-usage"},{"id":"basic-usage-ex8","language":"go","code":"msetRes, err := rdb.ARMSet(ctx, \"metrics\",\n\t\tredis.AREntry{Index: 0, Value: \"10\"},\n\t\tredis.AREntry{Index: 5, Value: \"20\"},\n\t\tredis.AREntry{Index: 100, Value: \"30\"},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(msetRes) // >>> 3\n\n\tmgetRes, err := rdb.ARMGet(ctx, \"metrics\", 0, 5, 100, 999).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(mgetRes) // >>> [10 20 30 <nil>]","section_id":"basic-usage"},{"id":"basic-usage-ex9","language":"java","code":"Map<Long, String> metricsValues = new HashMap<>();\n            metricsValues.put(0L, \"10\");\n            metricsValues.put(5L, \"20\");\n            metricsValues.put(100L, \"30\");\n\n            CompletableFuture<Void> armsetArmgetExample = asyncCommands\n                    .armset(\"metrics\", metricsValues)\n                    .thenCompose(res1 -> {\n                        System.out.println(res1);\n                        // >>> 3\n                        return asyncCommands.armget(\"metrics\", 0, 5, 100, 999);\n                    })\n                    .thenAccept(res2 -> {\n                        System.out.println(res2);\n                        // >>> [10, 20, 30, null]\n                    })\n                    .toCompletableFuture();\n\n            armsetArmgetExample.join();","section_id":"basic-usage"},{"id":"basic-usage-ex10","language":"java","code":"Map<Long, String> armsetParams = new HashMap<>();\n            armsetParams.put(0L, \"10\");\n            armsetParams.put(5L, \"20\");\n            armsetParams.put(100L, \"30\");\n\n            Mono<Long> armsetArmget1 = reactiveCommands.armset(\"metrics\", armsetParams).doOnNext(result -> {\n                System.out.println(result); // >>> 3\n            });\n\n            armsetArmget1.block();\n\n            Mono<List<String>> armsetArmget2 = reactiveCommands.armget(\"metrics\", 0, 5, 100, 999)\n                    .collectList()\n                    .map(values -> values.stream()\n                            .map(value -> value.getValueOrElse(null))\n                            .collect(Collectors.toList()))\n                    .doOnNext(result -> {\n                        System.out.println(result); // >>> [10, 20, 30, null]\n                    });\n\n            armsetArmget2.block();","section_id":"basic-usage"},{"id":"basic-usage-ex11","language":"javascript","code":"const mSetResult = await client.arMSet('metrics', { 0: '10', 5: '20', 100: '30' });\nconsole.log(mSetResult); // >>> 3\n\nconst mGetResult = await client.arMGet('metrics', [0, 5, 100, 999]);\nconsole.log(mGetResult); // >>> [ '10', '20', '30', null ]","section_id":"basic-usage"},{"id":"basic-usage-ex12","language":"php","code":"$res1 = $redis->armset('metrics', [0 => '10', 5 => '20', 100 => '30']);\n        echo $res1 . PHP_EOL; // >>> 3\n\n        $res2 = $redis->armget('metrics', [0, 5, 100, 999]);\n        echo json_encode($res2) . PHP_EOL; // >>> [\"10\",\"20\",\"30\",null]","section_id":"basic-usage"},{"id":"basic-usage-ex13","language":"python","code":"res4 = r.armset(\"metrics\", {0: \"10\", 5: \"20\", 100: \"30\"})\nprint(res4)\n# >>> 3\n\nres5 = r.armget(\"metrics\", 0, 5, 100, 999)\nprint(res5)\n# >>> ['10', '20', '30', None]","section_id":"basic-usage"},{"id":"array-length-vs-element-count-ex0","language":"plaintext","code":"> ARSET sparse 0 \"a\"\n(integer) 1\n> ARSET sparse 1000000 \"b\"\n(integer) 1\n> ARLEN sparse\n(integer) 1000001\n> ARCOUNT sparse\n(integer) 2","section_id":"array-length-vs-element-count"},{"id":"array-length-vs-element-count-ex1","language":"go","code":"set1, err := rdb.ARSet(ctx, \"sparse\", 0, \"a\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(set1) // >>> 1\n\n\tset2, err := rdb.ARSet(ctx, \"sparse\", 1000000, \"b\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(set2) // >>> 1\n\n\tlenRes, err := rdb.ARLen(ctx, \"sparse\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(lenRes) // >>> 1000001\n\n\tcountRes, err := rdb.ARCount(ctx, \"sparse\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(countRes) // >>> 2","section_id":"array-length-vs-element-count"},{"id":"array-length-vs-element-count-ex2","language":"java","code":"CompletableFuture<Void> lenCountExample = asyncCommands\n                    .arset(\"sparse\", 0, \"a\")\n                    .thenCompose(res1 -> {\n                        System.out.println(res1);\n                        // >>> 1\n                        return asyncCommands.arset(\"sparse\", 1000000, \"b\");\n                    })\n                    .thenCompose(res2 -> {\n                        System.out.println(res2);\n                        // >>> 1\n                        return asyncCommands.arlen(\"sparse\");\n                    })\n                    .thenCompose(res3 -> {\n                        System.out.println(res3);\n                        // >>> 1000001\n                        return asyncCommands.arcount(\"sparse\");\n                    })\n                    .thenAccept(res4 -> {\n                        System.out.println(res4);\n                        // >>> 2\n                    })\n                    .toCompletableFuture();\n\n            lenCountExample.join();","section_id":"array-length-vs-element-count"},{"id":"array-length-vs-element-count-ex3","language":"java","code":"Mono<Long> lenCount1 = reactiveCommands.arset(\"sparse\", 0, \"a\").doOnNext(result -> {\n                System.out.println(result); // >>> 1\n            });\n\n            lenCount1.block();\n\n            Mono<Long> lenCount2 = reactiveCommands.arset(\"sparse\", 1000000, \"b\").doOnNext(result -> {\n                System.out.println(result); // >>> 1\n            });\n\n            lenCount2.block();\n\n            Mono<Long> lenCount3 = reactiveCommands.arlen(\"sparse\").doOnNext(result -> {\n                System.out.println(result); // >>> 1000001\n            });\n\n            lenCount3.block();\n\n            Mono<Long> lenCount4 = reactiveCommands.arcount(\"sparse\").doOnNext(result -> {\n                System.out.println(result); // >>> 2\n            });\n\n            lenCount4.block();","section_id":"array-length-vs-element-count"},{"id":"array-length-vs-element-count-ex4","language":"javascript","code":"const setA = await client.arSet('sparse', 0, 'a');\nconsole.log(setA); // >>> 1\n\nconst setB = await client.arSet('sparse', 1000000, 'b');\nconsole.log(setB); // >>> 1\n\nconst lenResult = await client.arLen('sparse');\nconsole.log(lenResult); // >>> 1000001\n\nconst countResult = await client.arCount('sparse');\nconsole.log(countResult); // >>> 2","section_id":"array-length-vs-element-count"},{"id":"array-length-vs-element-count-ex5","language":"php","code":"$res1 = $redis->arset('sparse', 0, 'a');\n        echo $res1 . PHP_EOL; // >>> 1\n\n        $res2 = $redis->arset('sparse', 1000000, 'b');\n        echo $res2 . PHP_EOL; // >>> 1\n\n        $res3 = $redis->arlen('sparse');\n        echo $res3 . PHP_EOL; // >>> 1000001\n\n        $res4 = $redis->arcount('sparse');\n        echo $res4 . PHP_EOL; // >>> 2","section_id":"array-length-vs-element-count"},{"id":"array-length-vs-element-count-ex6","language":"python","code":"res6 = r.arset(\"sparse\", 0, \"a\")\nprint(res6)\n# >>> 1\n\nres7 = r.arset(\"sparse\", 1000000, \"b\")\nprint(res7)\n# >>> 1\n\nres8 = r.arlen(\"sparse\")\nprint(res8)\n# >>> 1000001\n\nres9 = r.arcount(\"sparse\")\nprint(res9)\n# >>> 2","section_id":"array-length-vs-element-count"},{"id":"reading-ranges-ex0","language":"plaintext","code":"> ARMSET seq 0 \"a\" 1 \"b\" 3 \"d\"\n(integer) 3\n> ARGETRANGE seq 0 3\n1) \"a\"\n2) \"b\"\n3) (nil)\n4) \"d\"","section_id":"reading-ranges"},{"id":"reading-ranges-ex1","language":"go","code":"msetRes, err := rdb.ARMSet(ctx, \"seq\",\n\t\tredis.AREntry{Index: 0, Value: \"a\"},\n\t\tredis.AREntry{Index: 1, Value: \"b\"},\n\t\tredis.AREntry{Index: 3, Value: \"d\"},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(msetRes) // >>> 3\n\n\trangeRes, err := rdb.ARGetRange(ctx, \"seq\", 0, 3).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(rangeRes) // >>> [a b <nil> d]","section_id":"reading-ranges"},{"id":"reading-ranges-ex2","language":"java","code":"Map<Long, String> seqRangeValues = new HashMap<>();\n            seqRangeValues.put(0L, \"a\");\n            seqRangeValues.put(1L, \"b\");\n            seqRangeValues.put(3L, \"d\");\n\n            CompletableFuture<Void> argetrangeExample = asyncCommands\n                    .armset(\"seq\", seqRangeValues)\n                    .thenCompose(res1 -> {\n                        System.out.println(res1);\n                        // >>> 3\n                        return asyncCommands.argetrange(\"seq\", 0, 3);\n                    })\n                    .thenAccept(res2 -> {\n                        System.out.println(res2);\n                        // >>> [a, b, null, d]\n                    })\n                    .toCompletableFuture();\n\n            argetrangeExample.join();","section_id":"reading-ranges"},{"id":"reading-ranges-ex3","language":"java","code":"Map<Long, String> argetrangeParams = new HashMap<>();\n            argetrangeParams.put(0L, \"a\");\n            argetrangeParams.put(1L, \"b\");\n            argetrangeParams.put(3L, \"d\");\n\n            Mono<Long> argetrange1 = reactiveCommands.armset(\"seq\", argetrangeParams).doOnNext(result -> {\n                System.out.println(result); // >>> 3\n            });\n\n            argetrange1.block();\n\n            Mono<List<String>> argetrange2 = reactiveCommands.argetrange(\"seq\", 0, 3)\n                    .collectList()\n                    .map(values -> values.stream()\n                            .map(value -> value.getValueOrElse(null))\n                            .collect(Collectors.toList()))\n                    .doOnNext(result -> {\n                        System.out.println(result); // >>> [a, b, null, d]\n                    });\n\n            argetrange2.block();","section_id":"reading-ranges"},{"id":"reading-ranges-ex4","language":"javascript","code":"const rangeSetResult = await client.arMSet('seq', { 0: 'a', 1: 'b', 3: 'd' });\nconsole.log(rangeSetResult); // >>> 3\n\nconst rangeResult = await client.arGetRange('seq', 0, 3);\nconsole.log(rangeResult); // >>> [ 'a', 'b', null, 'd' ]","section_id":"reading-ranges"},{"id":"reading-ranges-ex5","language":"php","code":"$res1 = $redis->armset('seq', [0 => 'a', 1 => 'b', 3 => 'd']);\n        echo $res1 . PHP_EOL; // >>> 3\n\n        $res2 = $redis->argetrange('seq', 0, 3);\n        echo json_encode($res2) . PHP_EOL; // >>> [\"a\",\"b\",null,\"d\"]","section_id":"reading-ranges"},{"id":"reading-ranges-ex6","language":"python","code":"res10 = r.armset(\"seq\", {0: \"a\", 1: \"b\", 3: \"d\"})\nprint(res10)\n# >>> 3\n\nres11 = r.argetrange(\"seq\", 0, 3)\nprint(res11)\n# >>> ['a', 'b', None, 'd']","section_id":"reading-ranges"},{"id":"reading-ranges-ex7","language":"plaintext","code":"> ARSCAN seq 0 3\n1) 1) (integer) 0\n   2) \"a\"\n2) 1) (integer) 1\n   2) \"b\"\n3) 1) (integer) 3\n   2) \"d\"","section_id":"reading-ranges"},{"id":"reading-ranges-ex8","language":"go","code":"msetRes, err := rdb.ARMSet(ctx, \"seq\",\n\t\tredis.AREntry{Index: 0, Value: \"a\"},\n\t\tredis.AREntry{Index: 1, Value: \"b\"},\n\t\tredis.AREntry{Index: 3, Value: \"d\"},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(msetRes) // >>> 3\n\n\tscanRes, err := rdb.ARScan(ctx, \"seq\", 0, 3, nil).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfor _, entry := range scanRes {\n\t\tfmt.Printf(\"%d -> %s\\n\", entry.Index, entry.Value)\n\t}\n\t// >>> 0 -> a\n\t// >>> 1 -> b\n\t// >>> 3 -> d","section_id":"reading-ranges"},{"id":"reading-ranges-ex9","language":"java","code":"Map<Long, String> seqScanValues = new HashMap<>();\n            seqScanValues.put(0L, \"a\");\n            seqScanValues.put(1L, \"b\");\n            seqScanValues.put(3L, \"d\");\n\n            CompletableFuture<Void> arscanExample = asyncCommands\n                    .armset(\"seq\", seqScanValues)\n                    .thenCompose(res1 -> {\n                        System.out.println(res1);\n                        // >>> 3\n                        return asyncCommands.arscan(\"seq\", 0, 3);\n                    })\n                    .thenAccept(res2 -> {\n                        for (IndexedValue<String> pair : res2) {\n                            System.out.println(pair.getIndex() + \" -> \" + pair.getValue());\n                        }\n                        // >>> 0 -> a\n                        // >>> 1 -> b\n                        // >>> 3 -> d\n                    })\n                    .toCompletableFuture();\n\n            arscanExample.join();","section_id":"reading-ranges"},{"id":"reading-ranges-ex10","language":"java","code":"Map<Long, String> arscanParams = new HashMap<>();\n            arscanParams.put(0L, \"a\");\n            arscanParams.put(1L, \"b\");\n            arscanParams.put(3L, \"d\");\n\n            Mono<Long> arscan1 = reactiveCommands.armset(\"seq\", arscanParams).doOnNext(result -> {\n                System.out.println(result); // >>> 3\n            });\n\n            arscan1.block();\n\n            Mono<List<IndexedValue<String>>> arscan2 = reactiveCommands.arscan(\"seq\", 0, 3)\n                    .doOnNext(entry -> {\n                        System.out.println(entry.getIndex() + \" -> \" + entry.getValue());\n                        // >>> 0 -> a\n                        // >>> 1 -> b\n                        // >>> 3 -> d\n                    })\n                    .collectList()\n            ;\n\n            arscan2.block();","section_id":"reading-ranges"},{"id":"reading-ranges-ex11","language":"javascript","code":"const scanSetResult = await client.arMSet('seq', { 0: 'a', 1: 'b', 3: 'd' });\nconsole.log(scanSetResult); // >>> 3\n\nconst scanResult = await client.arScan('seq', 0, 3);\nfor (const { index, value } of scanResult) {\n  console.log(`${index} -> ${value}`);\n}\n// >>> 0 -> a\n// >>> 1 -> b\n// >>> 3 -> d","section_id":"reading-ranges"},{"id":"reading-ranges-ex12","language":"php","code":"$res1 = $redis->armset('seq', [0 => 'a', 1 => 'b', 3 => 'd']);\n        echo $res1 . PHP_EOL; // >>> 3\n\n        $res2 = $redis->arscan('seq', 0, 3);\n        foreach ($res2 as $pair) {\n            echo $pair[0] . ' -> ' . $pair[1] . PHP_EOL;\n        }\n        // >>> 0 -> a\n        // >>> 1 -> b\n        // >>> 3 -> d","section_id":"reading-ranges"},{"id":"reading-ranges-ex13","language":"python","code":"res12 = r.armset(\"seq\", {0: \"a\", 1: \"b\", 3: \"d\"})\nprint(res12)\n# >>> 3\n\nres13 = r.arscan(\"seq\", 0, 3)\nfor index, value in res13:\n    print(f\"{index} -> {value}\")\n# >>> 0 -> a\n# >>> 1 -> b\n# >>> 3 -> d","section_id":"reading-ranges"},{"id":"sequential-insertion-ex0","language":"plaintext","code":"> ARINSERT log \"event1\"\n(integer) 0\n> ARINSERT log \"event2\"\n(integer) 1\n> ARNEXT log\n(integer) 2\n> ARSEEK log 10\n(integer) 1\n> ARINSERT log \"event3\"\n(integer) 10","section_id":"sequential-insertion"},{"id":"sequential-insertion-ex1","language":"go","code":"ins1, err := rdb.ARInsert(ctx, \"log\", \"event1\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(ins1) // >>> 0\n\n\tins2, err := rdb.ARInsert(ctx, \"log\", \"event2\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(ins2) // >>> 1\n\n\tnextRes, err := rdb.ARNext(ctx, \"log\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(nextRes) // >>> 2\n\n\tseekRes, err := rdb.ARSeek(ctx, \"log\", 10).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(seekRes) // >>> 1\n\n\tins3, err := rdb.ARInsert(ctx, \"log\", \"event3\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(ins3) // >>> 10","section_id":"sequential-insertion"},{"id":"sequential-insertion-ex2","language":"java","code":"CompletableFuture<Void> arinsertExample = asyncCommands\n                    .arinsert(\"log\", \"event1\")\n                    .thenCompose(res1 -> {\n                        System.out.println(res1);\n                        // >>> 0\n                        return asyncCommands.arinsert(\"log\", \"event2\");\n                    })\n                    .thenCompose(res2 -> {\n                        System.out.println(res2);\n                        // >>> 1\n                        return asyncCommands.arnext(\"log\");\n                    })\n                    .thenCompose(res3 -> {\n                        System.out.println(res3);\n                        // >>> 2\n                        return asyncCommands.arseek(\"log\", 10);\n                    })\n                    .thenCompose(res4 -> {\n                        System.out.println(res4);\n                        // >>> 1\n                        return asyncCommands.arinsert(\"log\", \"event3\");\n                    })\n                    .thenAccept(res5 -> {\n                        System.out.println(res5);\n                        // >>> 10\n                    })\n                    .toCompletableFuture();\n\n            arinsertExample.join();","section_id":"sequential-insertion"},{"id":"sequential-insertion-ex3","language":"java","code":"Mono<Long> arinsert1 = reactiveCommands.arinsert(\"log\", \"event1\").doOnNext(result -> {\n                System.out.println(result); // >>> 0\n            });\n\n            arinsert1.block();\n\n            Mono<Long> arinsert2 = reactiveCommands.arinsert(\"log\", \"event2\").doOnNext(result -> {\n                System.out.println(result); // >>> 1\n            });\n\n            arinsert2.block();\n\n            Mono<Long> arinsert3 = reactiveCommands.arnext(\"log\").doOnNext(result -> {\n                System.out.println(result); // >>> 2\n            });\n\n            arinsert3.block();\n\n            Mono<Long> arinsert4 = reactiveCommands.arseek(\"log\", 10).doOnNext(result -> {\n                System.out.println(result); // >>> 1\n            });\n\n            arinsert4.block();\n\n            Mono<Long> arinsert5 = reactiveCommands.arinsert(\"log\", \"event3\").doOnNext(result -> {\n                System.out.println(result); // >>> 10\n            });\n\n            arinsert5.block();","section_id":"sequential-insertion"},{"id":"sequential-insertion-ex4","language":"javascript","code":"const insert1 = await client.arInsert('log', 'event1');\nconsole.log(insert1); // >>> 0\n\nconst insert2 = await client.arInsert('log', 'event2');\nconsole.log(insert2); // >>> 1\n\nconst nextResult = await client.arNext('log');\nconsole.log(nextResult); // >>> 2\n\nconst seekResult = await client.arSeek('log', 10);\nconsole.log(seekResult); // >>> 1\n\nconst insert3 = await client.arInsert('log', 'event3');\nconsole.log(insert3); // >>> 10","section_id":"sequential-insertion"},{"id":"sequential-insertion-ex5","language":"php","code":"$res1 = $redis->arinsert('log', 'event1');\n        echo $res1 . PHP_EOL; // >>> 0\n\n        $res2 = $redis->arinsert('log', 'event2');\n        echo $res2 . PHP_EOL; // >>> 1\n\n        $res3 = $redis->arnext('log');\n        echo $res3 . PHP_EOL; // >>> 2\n\n        $res4 = $redis->arseek('log', 10);\n        echo $res4 . PHP_EOL; // >>> 1\n\n        $res5 = $redis->arinsert('log', 'event3');\n        echo $res5 . PHP_EOL; // >>> 10","section_id":"sequential-insertion"},{"id":"sequential-insertion-ex6","language":"python","code":"res14 = r.arinsert(\"log\", \"event1\")\nprint(res14)\n# >>> 0\n\nres15 = r.arinsert(\"log\", \"event2\")\nprint(res15)\n# >>> 1\n\nres16 = r.arnext(\"log\")\nprint(res16)\n# >>> 2\n\nres17 = r.arseek(\"log\", 10)\nprint(res17)\n# >>> 1\n\nres18 = r.arinsert(\"log\", \"event3\")\nprint(res18)\n# >>> 10","section_id":"sequential-insertion"},{"id":"ring-buffer-mode-ex0","language":"plaintext","code":"> ARRING readings 3 \"v0\"\n(integer) 0\n> ARRING readings 3 \"v1\"\n(integer) 1\n> ARRING readings 3 \"v2\"\n(integer) 2\n> ARRING readings 3 \"v3\"\n(integer) 0\n> ARGET readings 0\n\"v3\"","section_id":"ring-buffer-mode"},{"id":"ring-buffer-mode-ex1","language":"go","code":"ring0, err := rdb.ARRing(ctx, \"readings\", 3, \"v0\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(ring0) // >>> 0\n\n\tring1, err := rdb.ARRing(ctx, \"readings\", 3, \"v1\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(ring1) // >>> 1\n\n\tring2, err := rdb.ARRing(ctx, \"readings\", 3, \"v2\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(ring2) // >>> 2\n\n\tring3, err := rdb.ARRing(ctx, \"readings\", 3, \"v3\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(ring3) // >>> 0\n\n\tgetRes, err := rdb.ARGet(ctx, \"readings\", 0).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(getRes) // >>> v3","section_id":"ring-buffer-mode"},{"id":"ring-buffer-mode-ex2","language":"java","code":"CompletableFuture<Void> arringExample = asyncCommands\n                    .arring(\"readings\", 3, \"v0\")\n                    .thenCompose(res1 -> {\n                        System.out.println(res1);\n                        // >>> 0\n                        return asyncCommands.arring(\"readings\", 3, \"v1\");\n                    })\n                    .thenCompose(res2 -> {\n                        System.out.println(res2);\n                        // >>> 1\n                        return asyncCommands.arring(\"readings\", 3, \"v2\");\n                    })\n                    .thenCompose(res3 -> {\n                        System.out.println(res3);\n                        // >>> 2\n                        return asyncCommands.arring(\"readings\", 3, \"v3\");\n                    })\n                    .thenCompose(res4 -> {\n                        System.out.println(res4);\n                        // >>> 0\n                        return asyncCommands.arget(\"readings\", 0);\n                    })\n                    .thenAccept(res5 -> {\n                        System.out.println(res5);\n                        // >>> v3\n                    })\n                    .toCompletableFuture();\n\n            arringExample.join();","section_id":"ring-buffer-mode"},{"id":"ring-buffer-mode-ex3","language":"java","code":"Mono<Long> arring1 = reactiveCommands.arring(\"readings\", 3, \"v0\").doOnNext(result -> {\n                System.out.println(result); // >>> 0\n            });\n\n            arring1.block();\n\n            Mono<Long> arring2 = reactiveCommands.arring(\"readings\", 3, \"v1\").doOnNext(result -> {\n                System.out.println(result); // >>> 1\n            });\n\n            arring2.block();\n\n            Mono<Long> arring3 = reactiveCommands.arring(\"readings\", 3, \"v2\").doOnNext(result -> {\n                System.out.println(result); // >>> 2\n            });\n\n            arring3.block();\n\n            Mono<Long> arring4 = reactiveCommands.arring(\"readings\", 3, \"v3\").doOnNext(result -> {\n                System.out.println(result); // >>> 0\n            });\n\n            arring4.block();\n\n            Mono<String> arring5 = reactiveCommands.arget(\"readings\", 0).doOnNext(result -> {\n                System.out.println(result); // >>> v3\n            });\n\n            arring5.block();","section_id":"ring-buffer-mode"},{"id":"ring-buffer-mode-ex4","language":"javascript","code":"const ring0 = await client.arRing('readings', 3, 'v0');\nconsole.log(ring0); // >>> 0\n\nconst ring1 = await client.arRing('readings', 3, 'v1');\nconsole.log(ring1); // >>> 1\n\nconst ring2 = await client.arRing('readings', 3, 'v2');\nconsole.log(ring2); // >>> 2\n\nconst ring3 = await client.arRing('readings', 3, 'v3');\nconsole.log(ring3); // >>> 0\n\nconst ringGet = await client.arGet('readings', 0);\nconsole.log(ringGet); // >>> v3","section_id":"ring-buffer-mode"},{"id":"ring-buffer-mode-ex5","language":"php","code":"$res1 = $redis->arring('readings', 3, 'v0');\n        echo $res1 . PHP_EOL; // >>> 0\n\n        $res2 = $redis->arring('readings', 3, 'v1');\n        echo $res2 . PHP_EOL; // >>> 1\n\n        $res3 = $redis->arring('readings', 3, 'v2');\n        echo $res3 . PHP_EOL; // >>> 2\n\n        $res4 = $redis->arring('readings', 3, 'v3');\n        echo $res4 . PHP_EOL; // >>> 0\n\n        $res5 = $redis->arget('readings', 0);\n        echo $res5 . PHP_EOL; // >>> v3","section_id":"ring-buffer-mode"},{"id":"ring-buffer-mode-ex6","language":"python","code":"res19 = r.arring(\"readings\", 3, \"v0\")\nprint(res19)\n# >>> 0\n\nres20 = r.arring(\"readings\", 3, \"v1\")\nprint(res20)\n# >>> 1\n\nres21 = r.arring(\"readings\", 3, \"v2\")\nprint(res21)\n# >>> 2\n\nres22 = r.arring(\"readings\", 3, \"v3\")\nprint(res22)\n# >>> 0\n\nres23 = r.arget(\"readings\", 0)\nprint(res23)\n# >>> v3","section_id":"ring-buffer-mode"},{"id":"ring-buffer-mode-ex7","language":"plaintext","code":"> ARLASTITEMS readings 3\n1) \"v1\"\n2) \"v2\"\n3) \"v3\"\n> ARLASTITEMS readings 3 REV\n1) \"v3\"\n2) \"v2\"\n3) \"v1\"","section_id":"ring-buffer-mode"},{"id":"ring-buffer-mode-ex8","language":"go","code":"// Set up the ring: insert v0, v1, v2, v3 into a size-3 ring.\n\tfor _, v := range []string{\"v0\", \"v1\", \"v2\", \"v3\"} {\n\t\tif err := rdb.ARRing(ctx, \"readings\", 3, v).Err(); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}\n\n\tlastRes, err := rdb.ARLastItems(ctx, \"readings\", 3, false).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(lastRes) // >>> [v1 v2 v3]\n\n\tlastRevRes, err := rdb.ARLastItems(ctx, \"readings\", 3, true).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(lastRevRes) // >>> [v3 v2 v1]","section_id":"ring-buffer-mode"},{"id":"ring-buffer-mode-ex9","language":"java","code":"CompletableFuture<Void> arlastitemsExample = asyncCommands\n                    .arring(\"readings\", 3, \"v0\")\n                    .thenCompose(res1 -> asyncCommands.arring(\"readings\", 3, \"v1\"))\n                    .thenCompose(res2 -> asyncCommands.arring(\"readings\", 3, \"v2\"))\n                    .thenCompose(res3 -> asyncCommands.arring(\"readings\", 3, \"v3\"))\n                    .thenCompose(res4 -> asyncCommands.arlastitems(\"readings\", 3))\n                    .thenCompose(res5 -> {\n                        System.out.println(res5);\n                        // >>> [v1, v2, v3]\n                        return asyncCommands.arlastitems(\"readings\", 3, true);\n                    })\n                    .thenAccept(res6 -> {\n                        System.out.println(res6);\n                        // >>> [v3, v2, v1]\n                    })\n                    .toCompletableFuture();\n\n            arlastitemsExample.join();","section_id":"ring-buffer-mode"},{"id":"ring-buffer-mode-ex10","language":"java","code":"// Set up the ring: insert v0, v1, v2, v3 into a size-3 ring.\n            reactiveCommands.arring(\"readings\", 3, \"v0\").block();\n            reactiveCommands.arring(\"readings\", 3, \"v1\").block();\n            reactiveCommands.arring(\"readings\", 3, \"v2\").block();\n            reactiveCommands.arring(\"readings\", 3, \"v3\").block();\n\n            Mono<List<String>> arlastitems1 = reactiveCommands.arlastitems(\"readings\", 3).collectList()\n                    .doOnNext(result -> {\n                        System.out.println(result); // >>> [v1, v2, v3]\n                    });\n\n            arlastitems1.block();\n\n            Mono<List<String>> arlastitems2 = reactiveCommands.arlastitems(\"readings\", 3, true).collectList()\n                    .doOnNext(result -> {\n                        System.out.println(result); // >>> [v3, v2, v1]\n                    });\n\n            arlastitems2.block();","section_id":"ring-buffer-mode"},{"id":"ring-buffer-mode-ex11","language":"javascript","code":"await client.arRing('readings', 3, 'v0');\nawait client.arRing('readings', 3, 'v1');\nawait client.arRing('readings', 3, 'v2');\nawait client.arRing('readings', 3, 'v3');\n\nconst lastItems = await client.arLastItems('readings', 3);\nconsole.log(lastItems); // >>> [ 'v1', 'v2', 'v3' ]\n\nconst lastItemsRev = await client.arLastItems('readings', 3, { REV: true });\nconsole.log(lastItemsRev); // >>> [ 'v3', 'v2', 'v1' ]","section_id":"ring-buffer-mode"},{"id":"ring-buffer-mode-ex12","language":"php","code":"$redis->arring('readings', 3, 'v0');\n        $redis->arring('readings', 3, 'v1');\n        $redis->arring('readings', 3, 'v2');\n        $redis->arring('readings', 3, 'v3');\n\n        $res1 = $redis->arlastitems('readings', 3);\n        echo json_encode($res1) . PHP_EOL; // >>> [\"v1\",\"v2\",\"v3\"]\n\n        $res2 = $redis->arlastitems('readings', 3, true);\n        echo json_encode($res2) . PHP_EOL; // >>> [\"v3\",\"v2\",\"v1\"]","section_id":"ring-buffer-mode"},{"id":"ring-buffer-mode-ex13","language":"python","code":"r.arring(\"readings\", 3, \"v0\")\nr.arring(\"readings\", 3, \"v1\")\nr.arring(\"readings\", 3, \"v2\")\nr.arring(\"readings\", 3, \"v3\")\n\nres24 = r.arlastitems(\"readings\", 3)\nprint(res24)\n# >>> ['v1', 'v2', 'v3']\n\nres25 = r.arlastitems(\"readings\", 3, rev=True)\nprint(res25)\n# >>> ['v3', 'v2', 'v1']","section_id":"ring-buffer-mode"},{"id":"aggregate-operations-ex0","language":"plaintext","code":"> ARMSET scores 0 \"10\" 1 \"20\" 2 \"30\"\n(integer) 3\n> AROP scores 0 2 SUM\n\"60\"\n> AROP scores 0 2 MAX\n\"30\"\n> AROP scores 0 2 MATCH \"10\"\n(integer) 1","section_id":"aggregate-operations"},{"id":"aggregate-operations-ex1","language":"go","code":"msetRes, err := rdb.ARMSet(ctx, \"scores\",\n\t\tredis.AREntry{Index: 0, Value: \"10\"},\n\t\tredis.AREntry{Index: 1, Value: \"20\"},\n\t\tredis.AREntry{Index: 2, Value: \"30\"},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(msetRes) // >>> 3\n\n\tsumRes, err := rdb.AROpSum(ctx, \"scores\", 0, 2).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(sumRes) // >>> 60\n\n\tmaxRes, err := rdb.AROpMax(ctx, \"scores\", 0, 2).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(maxRes) // >>> 30\n\n\tmatchRes, err := rdb.AROpMatch(ctx, \"scores\", 0, 2, \"10\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(matchRes) // >>> 1","section_id":"aggregate-operations"},{"id":"aggregate-operations-ex2","language":"java","code":"Map<Long, String> aropScores = new HashMap<>();\n            aropScores.put(0L, \"10\");\n            aropScores.put(1L, \"20\");\n            aropScores.put(2L, \"30\");\n\n            CompletableFuture<Void> aropExample = asyncCommands\n                    .armset(\"scores\", aropScores)\n                    .thenCompose(res1 -> {\n                        System.out.println(res1);\n                        // >>> 3\n                        return asyncCommands.aropAggregate(\"scores\", 0, 2, ArAggregateType.SUM);\n                    })\n                    .thenCompose(res2 -> {\n                        System.out.println(res2);\n                        // >>> 60\n                        return asyncCommands.aropAggregate(\"scores\", 0, 2, ArAggregateType.MAX);\n                    })\n                    .thenCompose(res3 -> {\n                        System.out.println(res3);\n                        // >>> 30\n                        return asyncCommands.aropCount(\"scores\", 0, 2, \"10\");\n                    })\n                    .thenAccept(res4 -> {\n                        System.out.println(res4);\n                        // >>> 1\n                    })\n                    .toCompletableFuture();\n\n            aropExample.join();","section_id":"aggregate-operations"},{"id":"aggregate-operations-ex3","language":"java","code":"Map<Long, String> aropParams = new HashMap<>();\n            aropParams.put(0L, \"10\");\n            aropParams.put(1L, \"20\");\n            aropParams.put(2L, \"30\");\n\n            Mono<Long> arop1 = reactiveCommands.armset(\"scores\", aropParams).doOnNext(result -> {\n                System.out.println(result); // >>> 3\n            });\n\n            arop1.block();\n\n            Mono<String> arop2 = reactiveCommands.aropAggregate(\"scores\", 0, 2, ArAggregateType.SUM)\n                    .doOnNext(result -> {\n                        System.out.println(result); // >>> 60\n                    });\n\n            arop2.block();\n\n            Mono<String> arop3 = reactiveCommands.aropAggregate(\"scores\", 0, 2, ArAggregateType.MAX)\n                    .doOnNext(result -> {\n                        System.out.println(result); // >>> 30\n                    });\n\n            arop3.block();\n\n            Mono<Long> arop4 = reactiveCommands.aropCount(\"scores\", 0, 2, \"10\").doOnNext(result -> {\n                System.out.println(result); // >>> 1\n            });\n\n            arop4.block();","section_id":"aggregate-operations"},{"id":"aggregate-operations-ex4","language":"javascript","code":"const opSetResult = await client.arMSet('scores', { 0: '10', 1: '20', 2: '30' });\nconsole.log(opSetResult); // >>> 3\n\nconst sumResult = await client.arOp('scores', 0, 2, 'SUM');\nconsole.log(sumResult); // >>> 60\n\nconst maxResult = await client.arOp('scores', 0, 2, 'MAX');\nconsole.log(maxResult); // >>> 30\n\nconst matchResult = await client.arOp('scores', 0, 2, 'MATCH', '10');\nconsole.log(matchResult); // >>> 1","section_id":"aggregate-operations"},{"id":"aggregate-operations-ex5","language":"php","code":"$res1 = $redis->armset('scores', [0 => '10', 1 => '20', 2 => '30']);\n        echo $res1 . PHP_EOL; // >>> 3\n\n        $res2 = $redis->arop('scores', 0, 2, 'SUM');\n        echo $res2 . PHP_EOL; // >>> 60\n\n        $res3 = $redis->arop('scores', 0, 2, 'MAX');\n        echo $res3 . PHP_EOL; // >>> 30\n\n        $res4 = $redis->arop('scores', 0, 2, 'MATCH', '10');\n        echo $res4 . PHP_EOL; // >>> 1","section_id":"aggregate-operations"},{"id":"aggregate-operations-ex6","language":"python","code":"res26 = r.armset(\"scores\", {0: \"10\", 1: \"20\", 2: \"30\"})\nprint(res26)\n# >>> 3\n\nres27 = r.arop(\"scores\", 0, 2, ArrayAggregateOperations.SUM)\nprint(res27)\n# >>> 60\n\nres28 = r.arop(\"scores\", 0, 2, ArrayAggregateOperations.MAX)\nprint(res28)\n# >>> 30\n\nres29 = r.arop(\"scores\", 0, 2, ArrayAggregateOperations.MATCH, value=\"10\")\nprint(res29)\n# >>> 1","section_id":"aggregate-operations"},{"id":"searching-elements-ex0","language":"plaintext","code":"> DEL log\n(integer) 1\n> ARMSET log 0 \"boot: ok\" 1 \"warn: disk\" 2 \"ERROR: cpu\" 3 \"info: ready\" 4 \"error: net\"\n(integer) 5\n> ARGREP log - + MATCH \"error\" NOCASE\n1) (integer) 2\n2) (integer) 4\n> ARGREP log 0 4 GLOB \"warn:*\" OR GLOB \"error:*\" WITHVALUES\n1) 1) (integer) 1\n   2) \"warn: disk\"\n2) 1) (integer) 4\n   2) \"error: net\"","section_id":"searching-elements"},{"id":"searching-elements-ex1","language":"go","code":"msetRes, err := rdb.ARMSet(ctx, \"log\",\n\t\tredis.AREntry{Index: 0, Value: \"boot: ok\"},\n\t\tredis.AREntry{Index: 1, Value: \"warn: disk\"},\n\t\tredis.AREntry{Index: 2, Value: \"ERROR: cpu\"},\n\t\tredis.AREntry{Index: 3, Value: \"info: ready\"},\n\t\tredis.AREntry{Index: 4, Value: \"error: net\"},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(msetRes) // >>> 5\n\n\t// Case-insensitive match for \"error\".\n\tgrepRes, err := rdb.ARGrep(ctx, \"log\", \"0\", \"4\", &redis.ARGrepArgs{\n\t\tPredicates: []redis.ARGrepPredicate{\n\t\t\t{Type: redis.ARGrepMatch, Value: \"error\"},\n\t\t},\n\t\tNoCase: true,\n\t}).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(grepRes) // >>> [2 4]\n\n\t// Two GLOB predicates combined with the default OR, returning values too.\n\tgrepValsRes, err := rdb.ARGrepWithValues(ctx, \"log\", \"0\", \"4\", &redis.ARGrepArgs{\n\t\tPredicates: []redis.ARGrepPredicate{\n\t\t\t{Type: redis.ARGrepGlob, Value: \"warn:*\"},\n\t\t\t{Type: redis.ARGrepGlob, Value: \"error:*\"},\n\t\t},\n\t}).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfor _, entry := range grepValsRes {\n\t\tfmt.Printf(\"%d -> %s\\n\", entry.Index, entry.Value)\n\t}\n\t// >>> 1 -> warn: disk\n\t// >>> 4 -> error: net","section_id":"searching-elements"},{"id":"searching-elements-ex2","language":"java","code":"Map<Long, String> argrepLog = new HashMap<>();\n            argrepLog.put(0L, \"boot: ok\");\n            argrepLog.put(1L, \"warn: disk\");\n            argrepLog.put(2L, \"ERROR: cpu\");\n            argrepLog.put(3L, \"info: ready\");\n            argrepLog.put(4L, \"error: net\");\n\n            CompletableFuture<Void> argrepExample = asyncCommands\n                    .armset(\"log\", argrepLog)\n                    .thenCompose(res1 -> {\n                        System.out.println(res1);\n                        // >>> 5\n                        return asyncCommands.argrep(\"log\",\n                                ArGrepArgs.range(0, 4).match(\"error\").nocase());\n                    })\n                    .thenCompose(res2 -> {\n                        System.out.println(res2);\n                        // >>> [2, 4]\n                        return asyncCommands.argrepWithValues(\"log\",\n                                ArGrepArgs.range(0, 4).glob(\"warn:*\").glob(\"error:*\"));\n                    })\n                    .thenAccept(res3 -> {\n                        for (IndexedValue<String> pair : res3) {\n                            System.out.println(pair.getIndex() + \" -> \" + pair.getValue());\n                        }\n                        // >>> 1 -> warn: disk\n                        // >>> 4 -> error: net\n                    })\n                    .toCompletableFuture();\n\n            argrepExample.join();","section_id":"searching-elements"},{"id":"searching-elements-ex3","language":"java","code":"Map<Long, String> argrepParams = new HashMap<>();\n            argrepParams.put(0L, \"boot: ok\");\n            argrepParams.put(1L, \"warn: disk\");\n            argrepParams.put(2L, \"ERROR: cpu\");\n            argrepParams.put(3L, \"info: ready\");\n            argrepParams.put(4L, \"error: net\");\n\n            Mono<Long> argrep1 = reactiveCommands.armset(\"log\", argrepParams).doOnNext(result -> {\n                System.out.println(result); // >>> 5\n            });\n\n            argrep1.block();\n\n            Mono<List<Long>> argrep2 = reactiveCommands.argrep(\"log\", ArGrepArgs.range(0, 4).match(\"error\").nocase())\n                    .collectList()\n                    .doOnNext(result -> {\n                        System.out.println(result); // >>> [2, 4]\n                    });\n\n            argrep2.block();\n\n            Mono<List<IndexedValue<String>>> argrep3 = reactiveCommands\n                    .argrepWithValues(\"log\", ArGrepArgs.range(0, 4).glob(\"warn:*\").glob(\"error:*\"))\n                    .doOnNext(entry -> {\n                        System.out.println(entry.getIndex() + \" -> \" + entry.getValue());\n                        // >>> 1 -> warn: disk\n                        // >>> 4 -> error: net\n                    })\n                    .collectList()\n            ;\n\n            argrep3.block();","section_id":"searching-elements"},{"id":"searching-elements-ex4","language":"javascript","code":"const grepSetResult = await client.arMSet('log', {\n  0: 'boot: ok',\n  1: 'warn: disk',\n  2: 'ERROR: cpu',\n  3: 'info: ready',\n  4: 'error: net'\n});\nconsole.log(grepSetResult); // >>> 5\n\nconst grepResult = await client.arGrep(\n  'log',\n  0,\n  4,\n  [['MATCH', 'error']],\n  { NOCASE: true }\n);\nconsole.log(grepResult); // >>> [ 2, 4 ]\n\nconst grepWithValues = await client.arGrepWithValues(\n  'log',\n  0,\n  4,\n  [['GLOB', 'warn:*'], ['GLOB', 'error:*']],\n  { COMBINATOR: 'OR' }\n);\nfor (const { index, value } of grepWithValues) {\n  console.log(`${index} -> ${value}`);\n}\n// >>> 1 -> warn: disk\n// >>> 4 -> error: net","section_id":"searching-elements"},{"id":"searching-elements-ex5","language":"php","code":"$res1 = $redis->armset('log', [\n            0 => 'boot: ok',\n            1 => 'warn: disk',\n            2 => 'ERROR: cpu',\n            3 => 'info: ready',\n            4 => 'error: net',\n        ]);\n        echo $res1 . PHP_EOL; // >>> 5\n\n        // Predicates are [type, value] pairs. Positional argument order is:\n        // (key, start, end, predicates, combinator, limit, withValues, noCase)\n        $res2 = $redis->argrep('log', 0, 4, [['MATCH', 'error']], null, null, false, true);\n        echo json_encode($res2) . PHP_EOL; // >>> [2,4]\n\n        $res3 = $redis->argrep(\n            'log',\n            0,\n            4,\n            [['GLOB', 'warn:*'], ['GLOB', 'error:*']],\n            'OR',\n            null,\n            true\n        );\n        foreach ($res3 as $pair) {\n            echo $pair[0] . ' -> ' . $pair[1] . PHP_EOL;\n        }\n        // >>> 1 -> warn: disk\n        // >>> 4 -> error: net","section_id":"searching-elements"},{"id":"searching-elements-ex6","language":"python","code":"res30 = r.armset(\n    \"log\",\n    {\n        0: \"boot: ok\",\n        1: \"warn: disk\",\n        2: \"ERROR: cpu\",\n        3: \"info: ready\",\n        4: \"error: net\",\n    },\n)\nprint(res30)\n# >>> 5\n\nres31 = r.argrep(\n    \"log\",\n    0,\n    4,\n    [(ArrayPredicateType.MATCH, \"error\")],\n    nocase=True,\n)\nprint(res31)\n# >>> [2, 4]\n\nres32 = r.argrep(\n    \"log\",\n    0,\n    4,\n    [\n        (ArrayPredicateType.GLOB, \"warn:*\"),\n        (ArrayPredicateType.GLOB, \"error:*\"),\n    ],\n    combinator=ArrayPredicateCombinator.OR,\n    withvalues=True,\n)\nprint(res32)\n# >>> [[1, 'warn: disk'], [4, 'error: net']]","section_id":"searching-elements"},{"id":"deleting-elements-ex0","language":"plaintext","code":"> ARDEL scores 1\n(integer) 1\n> ARDELRANGE scores 0 2\n(integer) 2","section_id":"deleting-elements"},{"id":"deleting-elements-ex1","language":"go","code":"msetRes, err := rdb.ARMSet(ctx, \"scores\",\n\t\tredis.AREntry{Index: 0, Value: \"10\"},\n\t\tredis.AREntry{Index: 1, Value: \"20\"},\n\t\tredis.AREntry{Index: 2, Value: \"30\"},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(msetRes) // >>> 3\n\n\tdelRes, err := rdb.ARDel(ctx, \"scores\", 1).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(delRes) // >>> 1\n\n\tdelRangeRes, err := rdb.ARDelRange(ctx, \"scores\", redis.ARRange{Start: 0, End: 2}).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(delRangeRes) // >>> 2","section_id":"deleting-elements"},{"id":"deleting-elements-ex2","language":"java","code":"Map<Long, String> ardelScores = new HashMap<>();\n            ardelScores.put(0L, \"10\");\n            ardelScores.put(1L, \"20\");\n            ardelScores.put(2L, \"30\");\n\n            CompletableFuture<Void> ardelExample = asyncCommands\n                    .armset(\"scores\", ardelScores)\n                    .thenCompose(res1 -> {\n                        System.out.println(res1);\n                        // >>> 3\n                        return asyncCommands.ardel(\"scores\", 1);\n                    })\n                    .thenCompose(res2 -> {\n                        System.out.println(res2);\n                        // >>> 1\n                        return asyncCommands.ardelrange(\"scores\", 0, 2);\n                    })\n                    .thenAccept(res3 -> {\n                        System.out.println(res3);\n                        // >>> 2\n                    })\n                    .toCompletableFuture();\n\n            ardelExample.join();","section_id":"deleting-elements"},{"id":"deleting-elements-ex3","language":"java","code":"Map<Long, String> ardelParams = new HashMap<>();\n            ardelParams.put(0L, \"10\");\n            ardelParams.put(1L, \"20\");\n            ardelParams.put(2L, \"30\");\n\n            Mono<Long> ardel1 = reactiveCommands.armset(\"scores\", ardelParams).doOnNext(result -> {\n                System.out.println(result); // >>> 3\n            });\n\n            ardel1.block();\n\n            Mono<Long> ardel2 = reactiveCommands.ardel(\"scores\", 1).doOnNext(result -> {\n                System.out.println(result); // >>> 1\n            });\n\n            ardel2.block();\n\n            Mono<Long> ardel3 = reactiveCommands.ardelrange(\"scores\", 0, 2).doOnNext(result -> {\n                System.out.println(result); // >>> 2\n            });\n\n            ardel3.block();","section_id":"deleting-elements"},{"id":"deleting-elements-ex4","language":"javascript","code":"const delSetResult = await client.arMSet('scores', { 0: '10', 1: '20', 2: '30' });\nconsole.log(delSetResult); // >>> 3\n\nconst delResult = await client.arDel('scores', 1);\nconsole.log(delResult); // >>> 1\n\nconst delRangeResult = await client.arDelRange('scores', [[0, 2]]);\nconsole.log(delRangeResult); // >>> 2","section_id":"deleting-elements"},{"id":"deleting-elements-ex5","language":"php","code":"$res1 = $redis->armset('scores', [0 => '10', 1 => '20', 2 => '30']);\n        echo $res1 . PHP_EOL; // >>> 3\n\n        $res2 = $redis->ardel('scores', 1);\n        echo $res2 . PHP_EOL; // >>> 1\n\n        $res3 = $redis->ardelrange('scores', 0, 2);\n        echo $res3 . PHP_EOL; // >>> 2","section_id":"deleting-elements"},{"id":"deleting-elements-ex6","language":"python","code":"res33 = r.armset(\"scores\", {0: \"10\", 1: \"20\", 2: \"30\"})\nprint(res33)\n# >>> 3\n\nres34 = r.ardel(\"scores\", 1)\nprint(res34)\n# >>> 1\n\nres35 = r.ardelrange(\"scores\", (0, 2))\nprint(res35)\n# >>> 2","section_id":"deleting-elements"},{"id":"introspection-ex0","language":"plaintext","code":"> ARINFO readings\n 1) \"len\"\n 2) (integer) 3\n 3) \"count\"\n 4) (integer) 3\n 5) \"next-insert-index\"\n 6) (integer) 0\n...","section_id":"introspection"}]}
{"schema_version":2,"id":"develop/data-types/compare-data-types","title":"Compare data types","url":"https://redis.io/docs/latest/develop/data-types/compare-data-types/","summary":"Choose the best Redis data type for your task.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-10T15:45:53+01:00","page_type":"content","content_hash":"f1c9fc1d6c59a32b96080b06448e2883a9a9a46f81a8ced95de83a6f59598dbb","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis provides a wide range of data types to store your data.\nThe following are highly specialized for precise purposes:\n\n-   [Geospatial](https://redis.io/docs/latest/develop/data-types/geospatial):\n    store strings with associated coordinates for geospatial queries.\n-   [Probabilistic data types](https://redis.io/docs/latest/develop/data-types/probabilistic):\n    keep approximate counts and other statistics for large datasets.\n-   [Time series](https://redis.io/docs/latest/develop/data-types/timeseries):\n    store real-valued data points along with the time they were collected.\n-   [Vector sets](https://redis.io/docs/latest/develop/data-types/vector-sets):\n    store strings with associated vector data (and optional metadata)\n    for vector similarity queries.\n\nThe remaining data types are more general-purpose:\n\n-   [Strings](https://redis.io/docs/latest/develop/data-types/strings):\n    store text or binary data.\n-   [Arrays](https://redis.io/docs/latest/develop/data-types/arrays):\n    store strings addressed by integer index, with support for sparse\n    indices and server-side aggregation.\n-   [Hashes](https://redis.io/docs/latest/develop/data-types/hashes):\n    store key-value pairs within a single key.\n-   [JSON](https://redis.io/docs/latest/develop/data-types/json):\n    store structured, hierarchical arrays and key-value objects that match\n    the popular [JSON](https://www.json.org/json-en.html) text file format.\n-   [Lists](https://redis.io/docs/latest/develop/data-types/lists):\n    store a simple sequence of strings.\n-   [Sets](https://redis.io/docs/latest/develop/data-types/sets):\n    store a collection of unique strings.\n-   [Sorted sets](https://redis.io/docs/latest/develop/data-types/sorted-sets):\n    store a collection of unique strings with associated scores.\n-   [Streams](https://redis.io/docs/latest/develop/data-types/streams):\n    store a sequence of entries, each with a set of field-value pairs.\n\nThe general-purpose data types have some overlap among their features\nand indeed, you could probably emulate any of them using just strings\nand a little creativity. However, each data type provides different\ntradeoffs in terms of performance, memory usage, and functionality.\nThis guide helps you choose the best data type for your task."},{"id":"data-type-features","title":"Data type features","role":"content","text":"The sections below summarize the features of each data type."},{"id":"strings","title":"Strings","role":"content","text":"-   **Structure**: unstructured text/binary data or simple counters,\n    bit sets, or integer collections.\n-   **Operations**: get, set, append, increment, decrement, bitwise operations.\n-   **Suitable for**: Unstructured documents, counters, flags, bitmaps.\n\nStrings are mainly useful for storing text or binary data chunks\nwhose internal structure will be managed by your own application.\nHowever, they also support operations to access ranges of bits\nin the string to use as bit sets, integers, or floating-point numbers."},{"id":"arrays","title":"Arrays","role":"content","text":"-   **Structure**: sparse, index-addressable sequence of strings.\n-   **Operations**: get, set, delete, range read, range scan, sequential insert, aggregate.\n-   **Suitable for**: Event logs, ring buffers, sensor readings, and other append-heavy or sparse sequences.\n\nArrays store values addressed by integer index, making random access O(1) regardless of the array's logical size. Because arrays are sparse, setting an element at index 1,000,000 does not allocate memory for the million empty slots in between, so large index gaps are inexpensive. Arrays distinguish between *logical length* (the highest set index plus one, returned by [`ARLEN`](https://redis.io/docs/latest/commands/arlen)) and *element count* (the number of non-empty slots, returned by [`ARCOUNT`](https://redis.io/docs/latest/commands/arcount)).\n\nIn addition to direct index access, arrays support sequential insertion via [`ARINSERT`](https://redis.io/docs/latest/commands/arinsert), which advances an internal cursor automatically. The cursor can be repositioned with [`ARSEEK`](https://redis.io/docs/latest/commands/arseek), enabling flexible append patterns. [`ARRING`](https://redis.io/docs/latest/commands/arring) makes the ring buffer pattern explicit: it inserts values modulo a fixed size, wrapping around and overwriting the oldest entries when the buffer is full. [`AROP`](https://redis.io/docs/latest/commands/arop) performs single-pass aggregations (sum, min, max, bitwise operations, value matching) over a range without fetching each element individually."},{"id":"hashes","title":"Hashes","role":"content","text":"-   **Structure**: collection of key-value pairs.\n-   **Operations**: get, set, delete, increment, decrement, query.\n-   **Suitable for**: Simple objects with a small number of fields.\n\nHashes are mainly useful for storing objects with a small number of fields\nthat are not nested or intricately structured. However, there is\nno real limit to the number of fields you can store in a hash, so you\ncan use hashes in many different ways inside your application.\nThe field values are strings, but hashes provide commands to treat\nthem as integers or floating-point numbers and perform simple arithmetic\noperations on them. You can set expirations on individual hash fields\nand you can also index and query hash documents using\n[Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query)."},{"id":"json","title":"JSON","role":"content","text":"-   **Structure**: hierarchical arrays and key-value objects that match\n    the popular [JSON](https://www.json.org/json-en.html) text file format.\n-   **Operations**: get, set, update, delete, query.\n-   **Suitable for**: Complex, nested objects with many fields.\n\nJSON provides rich data modeling capabilities with nested fields and arrays.\nYou can use a simple path syntax to access any subset of the data within\na JSON document. JSON also has more powerful and flexible\n[Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query)\nfeatures compared to hashes."},{"id":"lists","title":"Lists","role":"content","text":"-   **Structure**: simple sequence of strings.\n-   **Operations**: push, pop, get, set, trim.\n-   **Suitable for**: Queues, stacks, logs, and other linear data structures.\n\nLists store sequences of string values. They are optimized for\nadding and removing small numbers of elements at the head or tail,\nand so they are very efficient for implementing queues, stacks,\nand deques."},{"id":"sets","title":"Sets","role":"content","text":"-   **Structure**: collection of unique strings.\n-   **Operations**: add, remove, test membership, intersect, union, difference.\n-   **Suitable for**: Unique items with no associated data.\n\nSets store collections of unique strings. They provide efficient\noperations for testing membership, adding and removing elements.\nThey also support set operations like intersection, union, and difference."},{"id":"sorted-sets","title":"Sorted sets","role":"content","text":"-   **Structure**: collection of unique strings with associated scores.\n-   **Operations**: add, remove, test membership, range by score or rank.\n-   **Suitable for**: Unique items with a score, or ordered collections.\n\nSorted sets store collections of unique strings with associated scores.\nThey are optimized for efficient range queries based on the score,\nand so they are useful for implementing priority queues and other ordered\ncollections."},{"id":"streams","title":"Streams","role":"content","text":"-   **Structure**: sequence of entries, each with a set of field-value pairs.\n-   **Operations**: add, read, trim.\n-   **Suitable for**: Log data, time series, and other append-only structures.\n\nStreams store sequences of entries, each with a set of field-value pairs.\nThey are optimized for appending new entries and reading them in order,\nand so they are useful for implementing log data, time series, and other\nappend-only data structures. They also have built-in support for consumer groups\nto manage multiple readers and ensure at-least-once delivery."},{"id":"choose-a-data-type","title":"Choose a data type","role":"content","text":"The sections below explore the pros and cons of each data type for\nparticular tasks. Note that you should regard\nthe suggestions as \"rules-of-thumb\" rather than strict prescriptions, since\nthere are potentially many subtle reasons to prefer one data type over another."},{"id":"documents","title":"Documents","role":"content","text":"You would normally store document data using the string, hash, or JSON\ntypes. JSON generally has the highest requirements for memory and processing,\nfollowed by hashes, and then strings. Use the decision tree below as a guide to\nchoosing the best data type for your task.\n\n[code example]"},{"id":"collections","title":"Collections","role":"content","text":"You would normally store collection data using the set or sorted set\ntypes and for very simple collections, you can even use strings. They all allow\nbasic membership tests, but have different additional features and tradeoffs.\nSorted sets have the highest memory overhead and processing requirements, followed\nby sets, and then strings.\nUse the decision tree below as a guide to choosing the best data type for your task.\nNote that if you need to store extra information for the keys in a set\nor sorted set, you can do so with an auxiliary hash or JSON object that has field\nnames matching the keys in the collection.\n\n[code example]"},{"id":"sequences","title":"Sequences","role":"content","text":"You would normally store sequences of string or binary data using sorted sets,\nlists, streams, or arrays. They each have advantages and disadvantages for particular purposes.  \nUse the decision tree below as a guide to choosing the best data type for your task.\n\n[code example]"}],"examples":[{"id":"documents-ex0","language":"decision-tree {id=\"documents-tree\"}","code":"id: documents-tree\nscope: documents\nrootQuestion: root\nquestions:\n    root:\n        text: |\n            Do you need nested data structures (fields and arrays) or geospatial\n            index/query with Redis Search?\n        whyAsk: |\n            JSON is the only document type that supports deeply nested structures and integrates with Redis Search for those structures\n        answers:\n            yes:\n                value: \"Yes\"\n                outcome:\n                    label: \"Use JSON\"\n                    id: jsonOutcome\n            no:\n                value: \"No\"\n                nextQuestion: hashQuestion\n    hashQuestion:\n        text: |\n            Do you need to index/query using Redis Search but can live\n            without nested data structures and geospatial indexing?\n        whyAsk: |\n            Hashes support indexing and querying with lower memory overhead and faster field access than JSON\n        answers:\n            yes:\n                value: \"Yes\"\n                outcome:\n                    label: \"Use hashes\"\n                    id: hashOutcome\n            no:\n                value: \"No\"\n                nextQuestion: expirationQuestion\n    expirationQuestion:\n        text: |\n            Do you need to set expiration times on individual pieces of data\n            within the document?\n        whyAsk: \"Only hashes support efficient field-level access and expiration\"\n        answers:\n            yes:\n                value: \"Yes\"\n                outcome:\n                    label: \"Use hashes\"\n                    id: hashOutcome\n            no:\n                value: \"No\"\n                nextQuestion: fieldAccessQuestion\n    fieldAccessQuestion:\n        text: |\n            Do you need frequent access to individual data fields within the\n            document, but the fields are simple integers or bits that you can easily \n            refer to by an integer index?\n        whyAsk: |\n            Strings and hashes support efficient field access, but strings are more compact and efficient if you only need bit fields with integer indices\n        answers:\n            yes:\n                value: \"Yes\"\n                outcome:\n                    label: \"Use strings\"\n                    id: stringOutcome\n            no:\n                value: \"No\"\n                nextQuestion: stringQuestion\n    stringQuestion:\n        text: |\n            Do you need frequent access to individual data fields within the\n            document that have string or binary data values?\n        whyAsk: |\n            Hashes support general field access, but strings are more compact and efficient if you don't need it\n        answers:\n            yes:\n                value: \"Yes\"\n                outcome:\n                    label: \"Use hashes\"\n                    id: hashOutcome\n            no:\n                value: \"No\"\n                outcome:\n                    label: \"Use strings\"\n                    id: stringOutcome","section_id":"documents"},{"id":"collections-ex0","language":"decision-tree {id=\"collections-tree\"}","code":"id: collections-tree\nscope: collections\nrootQuestion: root\nquestions:\n    root:\n        text: |\n            Do you need to store and retrieve the keys in an arbitrary order or in\n            lexicographical order?\n        whyAsk: |\n            Sorted sets are the only collection type that supports ordered iteration,\n            which is essential if you need to access elements in a specific order\n        answers:\n            yes:\n                value: \"Yes\"\n                outcome:\n                    label: \"Use sorted sets\"\n                    id: sortedSetsOutcome\n            no:\n                value: \"No\"\n                nextQuestion: extraInfo\n    extraInfo:\n        text: |\n            Do you need to store extra information for each key AND you don't need\n            set operations (union, intersection, difference)?\n        whyAsk: |\n            Hashes allow you to associate data with each key, but they don't support\n            set operations. If you need both extra data and set operations, sets are not suitable\n        answers:\n            yes:\n                value: \"Yes\"\n                outcome:\n                    label: \"Use hashes\"\n                    id: hashesOutcome\n            no:\n                value: \"No\"\n                nextQuestion: integerIndices\n    integerIndices:\n        text: |\n            Are the keys always simple integer indices in a known range?\n        whyAsk: |\n            String bitmaps provide minimum memory overhead and efficient random access\n            for integer indices, with bitwise operations equivalent to set operations\n        answers:\n            yes:\n                value: \"Yes\"\n                outcome:\n                    label: \"Use strings (bitmaps)\"\n                    id: stringsOutcome\n            no:\n                value: \"No\"\n                outcome:\n                    label: \"Use sets\"\n                    id: setsOutcome","section_id":"collections"},{"id":"sequences-ex0","language":"decision-tree {id=\"sequences-tree\"}","code":"id: sequences-tree\nscope: sequences\nrootQuestion: root\nquestions:\n    root:\n        text: |\n            Do you need to maintain an arbitrary priority order, lexicographical order,\n            or perform set operations?\n        whyAsk: |\n            Sorted sets are the only sequence type that supports both ordering and set operations\n        answers:\n            yes:\n                value: \"Yes\"\n                outcome:\n                    label: \"Use sorted sets\"\n                    id: sortedSetsOutcome\n            no:\n                value: \"No\"\n                nextQuestion: indexedAccess\n    indexedAccess:\n        text: |\n            Do you need to address elements by a caller-chosen integer index\n            (including sparse or non-contiguous indices), perform server-side\n            aggregation over index ranges (sum, min, max, bitwise), or use a\n            fixed-size ring buffer that overwrites the oldest entries?\n        whyAsk: |\n            Arrays map integer indices directly to string values with O(1) access,\n            support sparse indexing without allocating the gaps, and include\n            server-side aggregation and ring-buffer operations that lists and\n            streams do not provide\n        answers:\n            yes:\n                value: \"Yes\"\n                outcome:\n                    label: \"Use arrays\"\n                    id: arraysOutcome\n            no:\n                value: \"No\"\n                nextQuestion: timestampOrder\n    timestampOrder:\n        text: |\n            Do you need to store and retrieve elements primarily in timestamp order\n            or manage multiple consumers reading from the sequence?\n        whyAsk: |\n            Streams are the only sequence type that supports timestamp-based ordering\n            and consumer groups for managing multiple readers with at-least-once delivery\n        answers:\n            yes:\n                value: \"Yes\"\n                outcome:\n                    label: \"Use streams\"\n                    id: streamsOutcome\n            no:\n                value: \"No\"\n                outcome:\n                    label: \"Use lists\"\n                    id: listsOutcome","section_id":"sequences"}]}
{"schema_version":2,"id":"develop/data-types/geospatial","title":"Redis geospatial","url":"https://redis.io/docs/latest/develop/data-types/geospatial/","summary":"Introduction to the Redis Geospatial data type","aliases":["/data-types/geospatial/","/manual/data-types/geospatial/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-10T15:45:53+01:00","page_type":"content","content_hash":"2aa960bd910fe3b7dc4553b789ab8ed5f54156a929c2b7973090e910841b5607","sections":[{"id":"geospatial-command-summary","title":"Geospatial command summary","role":"content","text":"**10 commands in this group:**\n\n[View all geo commands](https://redis.io/commands/?group=geo)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [GEOADD](https://redis.io/commands/geoadd/) | Adds one or more members to a geospatial index. The key is created if it doesn't exist. | O(log(N)) for each item added, where N is the n... | 3.2.0 |\n| [GEODIST](https://redis.io/commands/geodist/) | Returns the distance between two members of a geospatial index. | O(1) | 3.2.0 |\n| [GEOHASH](https://redis.io/commands/geohash/) | Returns members from a geospatial index as geohash strings. | O(1) for each member requested. | 3.2.0 |\n| [GEOPOS](https://redis.io/commands/geopos/) | Returns the longitude and latitude of members from a geospatial index. | O(1) for each member requested. | 3.2.0 |\n| [GEORADIUS](https://redis.io/commands/georadius/) | Queries a geospatial index for members within a distance from a coordinate, optionally stores the result. | O(N+log(M)) where N is the number of elements i... | 3.2.0 |\n| [GEORADIUS_RO](https://redis.io/commands/georadius_ro/) | Returns members from a geospatial index that are within a distance from a coordinate. | O(N+log(M)) where N is the number of elements i... | 3.2.10 |\n| [GEORADIUSBYMEMBER](https://redis.io/commands/georadiusbymember/) | Queries a geospatial index for members within a distance from a member, optionally stores the result. | O(N+log(M)) where N is the number of elements i... | 3.2.0 |\n| [GEORADIUSBYMEMBER_RO](https://redis.io/commands/georadiusbymember_ro/) | Returns members from a geospatial index that are within a distance from a member. | O(N+log(M)) where N is the number of elements i... | 3.2.10 |\n| [GEOSEARCH](https://redis.io/commands/geosearch/) | Queries a geospatial index for members inside an area of a box or a circle. | O(N+log(M)) where N is the number of elements i... | 6.2.0 |\n| [GEOSEARCHSTORE](https://redis.io/commands/geosearchstore/) | Queries a geospatial index for members inside an area of a box or a circle, optionally stores the result. | O(N+log(M)) where N is the number of elements i... | 6.2.0 |\n\n\n\nRedis geospatial indexes let you store coordinates and search for them.\nThis data structure is useful for finding nearby points within a given radius or bounding box.\n\n> [!NOTE]\n> Take care not to confuse the Geospatial data type with the\n> [Geospatial](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/geo)\n> features in [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query).\n> Although there are some similarities between these two features, the data type is intended\n> for simpler use cases and doesn't have the range of format options and queries\n> available in Redis Search."},{"id":"examples","title":"Examples","role":"example","text":"Suppose you're building a mobile app that lets you find all of the bike rental stations closest to your current location.\n\nAdd several locations to a geospatial index:\nFoundational: Add geographic coordinates to a geospatial index using GEOADD to store location data\n\n**Commands:** GEOADD\n\n**Complexity:**\n- GEOADD: O(log(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nFind all locations within a 5 kilometer radius of a given location, and return the distance to each location:\nProximity search: Use GEOSEARCH to find locations within a radius or bounding box when you need to discover nearby points\n\n**Difficulty:** Intermediate\n\n**Builds upon:** geoadd\n\n**Commands:** GEOSEARCH\n\n**Complexity:**\n- GEOSEARCH: O(N+log(M)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]"},{"id":"learn-more","title":"Learn more","role":"related","text":"* [Redis Geospatial Explained](https://www.youtube.com/watch?v=qftiVQraxmI) introduces geospatial indexes by showing you how to build a map of local park attractions.\n* [Redis University's RU101](https://university.redis.com/courses/ru101/) covers Redis geospatial indexes in detail."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> GEOADD bikes:rentable -122.27652 37.805186 station:1\n(integer) 1\n> GEOADD bikes:rentable -122.2674626 37.8062344 station:2\n(integer) 1\n> GEOADD bikes:rentable -122.2469854 37.8104049 station:3\n(integer) 1","section_id":"examples"},{"id":"examples-ex1","language":"csharp","code":"bool res1 = db.GeoAdd(\"bikes:rentable\", -122.27652, 37.805186, \"station:1\");\n        Console.WriteLine(res1);    // >>> True\n\n        bool res2 = db.GeoAdd(\"bikes:rentable\", -122.2674626, 37.8062344, \"station:2\");\n        Console.WriteLine(res2);    // >>> True\n\n        bool res3 = db.GeoAdd(\"bikes:rentable\", -122.2469854, 37.8104049, \"station:3\");\n        Console.WriteLine(res3);    // >>> True","section_id":"examples"},{"id":"examples-ex2","language":"go","code":"res1, err := rdb.GeoAdd(ctx, \"bikes:rentable\",\n\t\t&redis.GeoLocation{\n\t\t\tLongitude: -122.27652,\n\t\t\tLatitude:  37.805186,\n\t\t\tName:      \"station:1\",\n\t\t}).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> 1\n\n\tres2, err := rdb.GeoAdd(ctx, \"bikes:rentable\",\n\t\t&redis.GeoLocation{\n\t\t\tLongitude: -122.2674626,\n\t\t\tLatitude:  37.8062344,\n\t\t\tName:      \"station:2\",\n\t\t}).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> 1\n\n\tres3, err := rdb.GeoAdd(ctx, \"bikes:rentable\",\n\t\t&redis.GeoLocation{\n\t\t\tLongitude: -122.2469854,\n\t\t\tLatitude:  37.8104049,\n\t\t\tName:      \"station:3\",\n\t\t}).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3) // >>> 1","section_id":"examples"},{"id":"examples-ex3","language":"java","code":"long res1 = jedis.geoadd(\"bikes:rentable\", -122.27652, 37.805186, \"station:1\");\n      System.out.println(res1); // 1\n\n      long res2 = jedis.geoadd(\"bikes:rentable\", -122.2674626, 37.8062344, \"station:2\");\n      System.out.println(res2); // 1\n\n      long res3 = jedis.geoadd(\"bikes:rentable\", -122.2469854, 37.8104049, \"station:3\");\n      System.out.println(res2); // 1","section_id":"examples"},{"id":"examples-ex4","language":"javascript","code":"const res1 = await client.geoAdd('bikes:rentable', { \n  longitude: -122.27652,\n  latitude: 37.805186,\n  member: 'station:1'\n});\nconsole.log(res1)  // 1\n\nconst res2 = await client.geoAdd('bikes:rentable', {\n  longitude: -122.2674626,\n  latitude: 37.8062344,\n  member: 'station:2'\n});\nconsole.log(res2)  // 1\n\nconst res3 = await client.geoAdd('bikes:rentable', {\n  longitude: -122.2469854,\n  latitude: 37.8104049,\n  member: 'station:3'\n})\nconsole.log(res3)  // 1","section_id":"examples"},{"id":"examples-ex5","language":"python","code":"res1 = r.geoadd(\"bikes:rentable\", [-122.27652, 37.805186, \"station:1\"])\nprint(res1)  # >>> 1\n\nres2 = r.geoadd(\"bikes:rentable\", [-122.2674626, 37.8062344, \"station:2\"])\nprint(res2)  # >>> 1\n\nres3 = r.geoadd(\"bikes:rentable\", [-122.2469854, 37.8104049, \"station:3\"])\nprint(res3)  # >>> 1","section_id":"examples"},{"id":"examples-ex6","language":"plaintext","code":"> GEOSEARCH bikes:rentable FROMLONLAT -122.2612767 37.7936847 BYRADIUS 5 km WITHDIST\n1) 1) \"station:1\"\n   2) \"1.8523\"\n2) 1) \"station:2\"\n   2) \"1.4979\"\n3) 1) \"station:3\"\n   2) \"2.2441\"","section_id":"examples"},{"id":"examples-ex7","language":"csharp","code":"GeoRadiusResult[] res4 = db.GeoSearch(\"bikes:rentable\",\n            -122.27652,\n            37.805186,\n            new GeoSearchCircle(5, GeoUnit.Kilometers)\n        );\n\n        foreach (GeoRadiusResult member in res4)\n        {\n            Console.WriteLine($\"Member: '{member.Member}', distance: {member.Distance}, position: {member.Position}\");\n        }\n        // >>> Member: 'station:1', distance: 0.0001, position: -122.27652043104172 37.80518485897756\n        // >>> Member: 'station:2', distance: 0.8047, position: -122.26745992898941 37.80623423353753\n        // >>> Member: 'station:3', distance: 2.6596, position: -122.24698394536972 37.81040384984464","section_id":"examples"},{"id":"examples-ex8","language":"go","code":"res4, err := rdb.GeoSearch(ctx, \"bikes:rentable\",\n\t\t&redis.GeoSearchQuery{\n\t\t\tLongitude:  -122.27652,\n\t\t\tLatitude:   37.805186,\n\t\t\tRadius:     5,\n\t\t\tRadiusUnit: \"km\",\n\t\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4) // >>> [station:1 station:2 station:3]","section_id":"examples"},{"id":"examples-ex9","language":"java","code":"List<GeoRadiusResponse> res4 = jedis.geosearch(\n          \"bikes:rentable\",\n          new GeoCoordinate(-122.27652, 37.805186),\n          5,\n          GeoUnit.KM\n      );\n      List<String> members = res4.stream() //\n          .map(GeoRadiusResponse::getMemberByString) //\n          .collect(Collectors.toList());\n      System.out.println(members); // [station:1, station:2, station:3]","section_id":"examples"},{"id":"examples-ex10","language":"javascript","code":"const res4 = await client.geoSearch(\n  'bikes:rentable', {\n    longitude: -122.27652,\n    latitude: 37.805186,\n  },\n  { radius: 5,\n    unit: 'km'\n  }\n);\nconsole.log(res4)  // ['station:1', 'station:2', 'station:3']","section_id":"examples"},{"id":"examples-ex11","language":"python","code":"res4 = r.geosearch(\n    \"bikes:rentable\",\n    longitude=-122.27652,\n    latitude=37.805186,\n    radius=5,\n    unit=\"km\",\n)\nprint(res4)  # >>> ['station:1', 'station:2', 'station:3']","section_id":"examples"}]}
{"schema_version":2,"id":"develop/data-types/hashes","title":"Redis hashes","url":"https://redis.io/docs/latest/develop/data-types/hashes/","summary":"Introduction to Redis hashes","aliases":["/data-types/hashes/","/manual/data-types/hashes/","/develop/data-types/hash/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-12T11:04:49+01:00","page_type":"content","content_hash":"b763bd3e13677fb00e008f6af4692a9c1eace741e32cd7b321b11f50774c4806","sections":[{"id":"hash-command-summary","title":"Hash command summary","role":"content","text":"**33 commands in this group:**\n\n[View all hash commands](https://redis.io/commands/?group=hash)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [HDEL](https://redis.io/commands/hdel/) | Deletes one or more fields and their values from a hash. Deletes the hash if no fields remain. | O(N) where N is the number of fields to be remo... | 2.0.0 |\n| [HEXISTS](https://redis.io/commands/hexists/) | Determines whether a field exists in a hash. | O(1) | 2.0.0 |\n| [HEXPIRE](https://redis.io/commands/hexpire/) | Set expiry for hash field using relative time to expire (seconds) | O(N) where N is the number of specified fields | 7.4.0 |\n| [HEXPIREAT](https://redis.io/commands/hexpireat/) | Set expiry for hash field using an absolute Unix timestamp (seconds) | O(N) where N is the number of specified fields | 7.4.0 |\n| [HEXPIRETIME](https://redis.io/commands/hexpiretime/) | Returns the expiration time of a hash field as a Unix timestamp, in seconds. | O(N) where N is the number of specified fields | 7.4.0 |\n| [HGET](https://redis.io/commands/hget/) | Returns the value of a field in a hash. | O(1) | 2.0.0 |\n| [HGETALL](https://redis.io/commands/hgetall/) | Returns all fields and values in a hash. | O(N) where N is the size of the hash. | 2.0.0 |\n| [HGETDEL](https://redis.io/commands/hgetdel/) | Returns the value of a field and deletes it from the hash. | O(N) where N is the number of specified fields | 8.0.0 |\n| [HGETEX](https://redis.io/commands/hgetex/) | Get the value of one or more fields of a given hash key, and optionally set their expiration. | O(N) where N is the number of specified fields | 8.0.0 |\n| [HIMPORT](https://redis.io/commands/himport/) | A container for session-based hash import commands using fieldsets. | Depends on subcommand. | 8.10.0 |\n\n#### Hash command summary (part 2)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [HIMPORT DISCARD](https://redis.io/commands/himport-discard/) | Removes a single session-local fieldset by name. | O(N) where N is the number of session-local fie... | 8.10.0 |\n| [HIMPORT DISCARDALL](https://redis.io/commands/himport-discardall/) | Removes all session-local fieldsets for the connection. | O(N) where N is the number of session-local fie... | 8.10.0 |\n| [HIMPORT PREPARE](https://redis.io/commands/himport-prepare/) | Defines a session-local fieldset that maps a name to a sorted set of field names. | O(N*log(N)) where N is the number of fields. | 8.10.0 |\n| [HIMPORT SET](https://redis.io/commands/himport-set/) | Creates a fieldset-based hash from values supplied in the order matching a previously prepared fieldset. | O(N) where N is the number of fields in the fie... | 8.10.0 |\n| [HINCRBY](https://redis.io/commands/hincrby/) | Increments the integer value of a field in a hash by a number. Uses 0 as initial value if the field doesn't exist. | O(1) | 2.0.0 |\n| [HINCRBYFLOAT](https://redis.io/commands/hincrbyfloat/) | Increments the floating point value of a field by a number. Uses 0 as initial value if the field doesn't exist. | O(1) | 2.6.0 |\n| [HKEYS](https://redis.io/commands/hkeys/) | Returns all fields in a hash. | O(N) where N is the size of the hash. | 2.0.0 |\n| [HLEN](https://redis.io/commands/hlen/) | Returns the number of fields in a hash. | O(1) | 2.0.0 |\n| [HMGET](https://redis.io/commands/hmget/) | Returns the values of all fields in a hash. | O(N) where N is the number of fields being requ... | 2.0.0 |\n| [HMSET](https://redis.io/commands/hmset/) | Sets the values of multiple fields. | O(N) where N is the number of fields being set. | 2.0.0 |\n\n#### Hash command summary (part 3)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [HPERSIST](https://redis.io/commands/hpersist/) | Removes the expiration time for each specified field | O(N) where N is the number of specified fields | 7.4.0 |\n| [HPEXPIRE](https://redis.io/commands/hpexpire/) | Set expiry for hash field using relative time to expire (milliseconds) | O(N) where N is the number of specified fields | 7.4.0 |\n| [HPEXPIREAT](https://redis.io/commands/hpexpireat/) | Set expiry for hash field using an absolute Unix timestamp (milliseconds) | O(N) where N is the number of specified fields | 7.4.0 |\n| [HPEXPIRETIME](https://redis.io/commands/hpexpiretime/) | Returns the expiration time of a hash field as a Unix timestamp, in msec. | O(N) where N is the number of specified fields | 7.4.0 |\n| [HPTTL](https://redis.io/commands/hpttl/) | Returns the TTL in milliseconds of a hash field. | O(N) where N is the number of specified fields | 7.4.0 |\n| [HRANDFIELD](https://redis.io/commands/hrandfield/) | Returns one or more random fields from a hash. | O(N) where N is the number of fields returned | 6.2.0 |\n| [HSCAN](https://redis.io/commands/hscan/) | Iterates over fields and values of a hash. | O(1) for every call. O(N) for a complete iterat... | 2.8.0 |\n| [HSET](https://redis.io/commands/hset/) | Creates or modifies the value of a field in a hash. | O(1) for each field/value pair added, so O(N) t... | 2.0.0 |\n| [HSETEX](https://redis.io/commands/hsetex/) | Set the value of one or more fields of a given hash key, and optionally set their expiration. | O(N) where N is the number of fields being set. | 8.0.0 |\n| [HSETNX](https://redis.io/commands/hsetnx/) | Sets the value of a field in a hash only when the field doesn't exist. | O(1) | 2.0.0 |\n\n#### Hash command summary (part 4)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [HSTRLEN](https://redis.io/commands/hstrlen/) | Returns the length of the value of a field. | O(1) | 3.2.0 |\n| [HTTL](https://redis.io/commands/httl/) | Returns the TTL in seconds of a hash field. | O(N) where N is the number of specified fields | 7.4.0 |\n| [HVALS](https://redis.io/commands/hvals/) | Returns all values in a hash. | O(N) where N is the size of the hash. | 2.0.0 |\n\n\n\nRedis hashes are record types structured as collections of field-value pairs.\nYou can use hashes to represent basic objects and to store groupings of counters, among other things.\n\nFoundational: Set and retrieve hash fields using HSET and HGET (overwrites existing field values)\n\n**Commands:** HSET, HGET, HGETALL\n\n**Complexity:**\n- HSET: O(1)\n- HGET: O(1)\n- HGETALL: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nWhile hashes are handy to represent *objects*, actually the number of fields you can\nput inside a hash has no practical limits (other than available memory), so you can use\nhashes in many different ways inside your application.\n\nThe command [`HSET`](https://redis.io/docs/latest/commands/hset) sets multiple fields of the hash, while [`HGET`](https://redis.io/docs/latest/commands/hget) retrieves\na single field. [`HMGET`](https://redis.io/docs/latest/commands/hmget) is similar to [`HGET`](https://redis.io/docs/latest/commands/hget) but returns an array of values:\n\nRetrieve multiple field values from a hash using HMGET when you need to reduce round trips to the server\n\n**Builds upon:** set_get_all\n\n**Commands:** DEL, HSET, HMGET\n\n**Complexity:**\n- DEL: O(N)\n- HSET: O(1)\n- HMGET: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nThere are commands that are able to perform operations on individual fields\nas well, like [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby):\n\nIncrement hash field values for counters using HINCRBY (creates field if missing, initializes to 0)\n\n**Builds upon:** set_get_all\n\n**Commands:** DEL, HSET, HINCRBY\n\n**Complexity:**\n- DEL: O(N)\n- HSET: O(1)\n- HINCRBY: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nYou can find the [full list of hash commands in the documentation](/commands?group=hash).\n\nIt is worth noting that small hashes (i.e., a few elements with small values) are\nencoded in special way in memory that make them very memory efficient."},{"id":"examples","title":"Examples","role":"example","text":"* Store counters for the number of times bike:1 has been ridden, has crashed, or has changed owners:\nPractical pattern: Combine HINCRBY and HMGET to track multiple counters when you need atomic updates across multiple fields\n\n**Difficulty:** Intermediate\n\n**Builds upon:** hincrby, hmget\n\n**Commands:** HINCRBY, HGET, HMGET\n\n**Complexity:**\n- HINCRBY: O(1)\n- HGET: O(1)\n- HMGET: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"field-expiration","title":"Field expiration","role":"content","text":"Redis 7.4 introduced the ability to specify an expiration time or a time-to-live (TTL) value for individual hash fields.\nThis capability is comparable to [key expiration](https://redis.io/docs/latest/develop/using-commands/keyspace#key-expiration) and includes a number of similar commands.\n\nUse the following commands to set either an exact expiration time or a TTL value for specific fields:\n\n* [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire): set the remaining TTL in seconds.\n* [`HPEXPIRE`](https://redis.io/docs/latest/commands/hpexpire): set the remaining TTL in milliseconds.\n* [`HEXPIREAT`](https://redis.io/docs/latest/commands/hexpireat): set the expiration time to a timestamp[^1] specified in seconds.\n* [`HPEXPIREAT`](https://redis.io/docs/latest/commands/hpexpireat): set the expiration time to a timestamp specified in milliseconds.\n\n[^1]: all timestamps are specified in seconds or milliseconds since the [Unix epoch](https://en.wikipedia.org/wiki/Unix_time).\n\nUse the following commands to retrieve either the exact time when or the remaining TTL until specific fields will expire:\n\n* [`HEXPIRETIME`](https://redis.io/docs/latest/commands/hexpiretime): get the expiration time as a timestamp in seconds.\n* [`HPEXPIRETIME`](https://redis.io/docs/latest/commands/hpexpiretime): get the expiration time as a timestamp in milliseconds.\n* [`HTTL`](https://redis.io/docs/latest/commands/httl): get the remaining TTL in seconds.\n* [`HPTTL`](https://redis.io/docs/latest/commands/hpttl): get the remaining TTL in milliseconds.\n\nUse the following command to remove the expiration of specific fields:\n\n* [`HPERSIST`](https://redis.io/docs/latest/commands/hpersist): remove the expiration.\n\nRedis 8.0 introduced the following commands:\n\n* [`HGETEX`](https://redis.io/docs/latest/commands/hgetex): Get the value of one or more fields of a given hash key and optionally set their expiration time or time-to-live (TTL).\n* [`HSETEX`](https://redis.io/docs/latest/commands/hsetex): Set the value of one or more fields of a given hash key and optionally set their expiration time or time-to-live (TTL)."},{"id":"common-field-expiration-use-cases","title":"Common field expiration use cases","role":"content","text":"1. **Event Tracking**: Use a hash key to store events from the last hour. Set each event's TTL to one hour. Use `HLEN` to count events from the past hour.\n\n1. **Fraud Detection**: Create a hash with hourly counters for events. Set each field's TTL to 48 hours. Query the hash to get the number of events per hour for the last 48 hours.\n\n1. **Customer Session Management**: Store customer data in hash keys. Create a new hash key for each session and add a session field to the customer’s hash key. Expire both the session key and the session field in the customer’s hash key automatically when the session expires.\n\n1. **Active Session Tracking**: Store all active sessions in a hash key. Set each session's TTL to expire automatically after inactivity. Use `HLEN` to count active sessions."},{"id":"field-expiration-examples","title":"Field expiration examples","role":"content","text":"Hash field expiration is supported by the official client libraries. The examples below\ndemonstrate the field expiration commands using a hash that stores sensor data with the\nfollowing structure:\n\n| Field           | Value |\n| :-------------- | :---- |\n| `air_quality`   | 256   |\n| `battery_level` | 89    |\n\nBecause the fields expire, each example recreates the `sensor:sensor1` hash first so that\nit runs on its own.\n\nSet a TTL of 60 seconds for two fields of a hash and then retrieve the remaining TTL for\nthose fields:\n\nField expiration: Set a TTL in seconds on individual hash fields using HEXPIRE, then read the remaining TTL with HTTL\n\n**Difficulty:** Intermediate\n\n**Builds upon:** set_get_all\n\n**Commands:** DEL, HSET, HEXPIRE, HTTL\n\n**Complexity:**\n- DEL: O(N)\n- HSET: O(1)\n- HEXPIRE: O(N)\n- HTTL: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nSet a hash field's TTL in milliseconds and then retrieve the remaining TTL in milliseconds:\n\nField expiration: Set a TTL in milliseconds on a hash field using HPEXPIRE, then read the remaining TTL with HPTTL\n\n**Difficulty:** Intermediate\n\n**Builds upon:** set_get_all\n\n**Commands:** DEL, HSET, HPEXPIRE, HPTTL\n\n**Complexity:**\n- DEL: O(N)\n- HSET: O(1)\n- HPEXPIRE: O(N)\n- HPTTL: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nSet a hash field's expiration to a specific timestamp and then retrieve that expiration\ntime (both as a Unix time in seconds):\n\nField expiration: Set an absolute expiration timestamp on a hash field using HEXPIREAT, then read it back with HEXPIRETIME\n\n**Difficulty:** Intermediate\n\n**Builds upon:** set_get_all\n\n**Commands:** DEL, HSET, HEXPIREAT, HEXPIRETIME\n\n**Complexity:**\n- DEL: O(N)\n- HSET: O(1)\n- HEXPIREAT: O(N)\n- HEXPIRETIME: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"compact-hashes","title":"Compact hashes","role":"content","text":"Redis 8.10 introduced *compact hashes*, a way to reduce the memory used by hashes that share the same field names. When many hashes have the same layout (for example, one hash per user, each with `name`, `email`, and `age`), Redis can store the shared set of field names once and keep only the values, plus a small reference to that set, in each key. This is an internal encoding: existing hash commands keep working exactly as before, with the same semantics and replies. The hash just uses less memory.\n\n**Compact hashes work well when:**\n\n- Many keys share the same, mostly stable set of field names — classic object mapping, such as one hash per user, order, or session. The more keys that share a field set, the greater the memory saving.\n- Reads and value updates stay as fast as on a regular hash. Reading any field (for example, [`HGET`](https://redis.io/docs/latest/commands/hget), [`HGETALL`](https://redis.io/docs/latest/commands/hgetall), or [`HRANDFIELD`](https://redis.io/docs/latest/commands/hrandfield)) and overwriting an existing field's value (`HSET key field value` where `field` already exists) are unaffected.\n\n**Compact hashes are not a good fit when:**\n\n- **Field names change often.** Adding a new field with [`HSET`](https://redis.io/docs/latest/commands/hset) or removing one with [`HDEL`](https://redis.io/docs/latest/commands/hdel) detaches the key from its shared field-name set and re-resolves it against the new set, creating a new one if that layout hasn't been seen before. A workload that constantly adds or removes field names erodes the benefit. Note also that once a hash becomes a compact hash, it stays one for the life of the key: it moves between field-name sets as its fields change, but never reverts to a plain hash.\n- **Field names are unique or highly dynamic per key.** With little sharing, a key ends up with its own field-name set, which carries some metadata overhead. A set used by only a few keys can therefore consume more memory than a plain hash. Compact hashes pay off when hundreds or thousands of keys share a set.\n- **Hashes are very large.** Because a compact hash is transferred as a single blob during replication and slot migration, very large hash keys may reduce or negate the benefit.\n\nThere are two ways to opt in."},{"id":"bulk-import-with-himport","title":"Bulk import with HIMPORT","role":"content","text":"The [`HIMPORT`](https://redis.io/docs/latest/commands/himport) command family lets a client import many hashes that share the same field names efficiently. You declare the shared field names once with [`HIMPORT PREPARE`](https://redis.io/docs/latest/commands/himport-prepare), then create each key with [`HIMPORT SET`](https://redis.io/docs/latest/commands/himport-set) by sending only its values. This reduces network traffic and per-command work compared with running [`HSET`](https://redis.io/docs/latest/commands/hset) once per key, and hints Redis to store the new keys as compact hashes. See [`HIMPORT`](https://redis.io/docs/latest/commands/himport) for the full workflow and its subcommands.\n\nFoundational: Declare shared field names once with HIMPORT PREPARE, then create each hash by sending only its values with HIMPORT SET\n\n**Difficulty:** Intermediate\n\n**Commands:** HIMPORT, HGETALL\n\n**Complexity:**\n- HIMPORT: Depends on subcommand\n- HGETALL: O(N)\n\n**Available in:** Redis CLI, C#, C#, Go, Java, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Ruby\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n\n\nEach key you create this way is an ordinary hash. Note that:\n\n- [`HIMPORT SET`](https://redis.io/docs/latest/commands/himport-set) replaces an existing key rather than merging into it.\n- [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) can return the fields in a different order from the one you declared.\n\nA fieldset belongs to the connection that declared it. Client libraries that support `HIMPORT` track your fieldsets and declare them again on each connection they use, including pooled connections, reconnections, and cluster nodes, so you don't manage this yourself. Some clients, such as Jedis, Lettuce, and StackExchange.Redis, have no prepare method: you create a fieldset object and the client declares it when it's first used. Most client libraries mark their `HIMPORT` support as experimental, so its API might change, and some clients don't support it inside a transaction.\n\nTo import many hashes, send the imports in a [pipeline](https://redis.io/docs/latest/develop/using-commands/pipelining) so they share network round trips instead of waiting for each reply in turn:\n\nBulk ingestion: Pipeline many HIMPORT SET calls that share one fieldset to load a large number of hashes with few network round trips\n\n**Difficulty:** Intermediate\n\n**Builds upon:** himport_basic\n\n**Commands:** HIMPORT, HGET\n\n**Complexity:**\n- HIMPORT: Depends on subcommand\n- HGET: O(1)\n\n**Available in:** Redis CLI, C#, C#, Go, Java, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Ruby\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]"},{"id":"automatic-conversion","title":"Automatic conversion","role":"content","text":"For workloads that can't adopt a new command, Redis can convert eligible hashes to compact hashes on its own, with no code change. All the following settings default to `0` (off) and can be changed at runtime with [`CONFIG SET`](https://redis.io/docs/latest/commands/config-set).\n\n#### On the write path\n\nThese convert hashes created or modified by normal commands (such as [`HSET`](https://redis.io/docs/latest/commands/hset)), so an existing application gains the memory saving with no code change. They take effect lazily, like `hash-max-listpack-entries`: a change applies to a given hash only on its next write, not the moment you run [`CONFIG SET`](https://redis.io/docs/latest/commands/config-set).\n\n| Config | Meaning |\n|---|---|\n| `hash-min-template-entries` | Minimum field count for a hash to be auto-converted to a compact hash on its next write. `0` disables auto-conversion. |\n| `hash-max-template-entries` | Maximum field count for auto-conversion: a hash wider than this is left a plain hash (keeps very wide hashes out of the shared registry). `0` means no upper bound. |\n\nA hash is not converted if it uses [field expiration](#field-expiration), even when its field count meets the minimum. After a hash has been converted, it stays a compact hash even if its field count later grows beyond `hash-max-template-entries`.\n\n#### On RDB load\n\nAn RDB saved before this feature contains only plain hashes. These configs let Redis convert them to compact hashes *as the RDB loads*, so an upgrade reclaims memory without rewriting data. They apply only to RDBs without compact hashes; an RDB that already contains them is loaded as-is, with no load-time conversion. In effect these are one-time upgrade configs: they have no effect once the RDB contains at least one compact hash.\n\n| Config | Meaning |\n|---|---|\n| `hash-rdb-load-min-template-entries` | Minimum field count to convert a plain hash to a compact hash during load. `0` disables load-time conversion. |\n| `hash-rdb-load-max-template-entries` | Maximum field count for load-time conversion. `0` means no upper bound. |\n| `hash-rdb-load-template-disassembly-threshold` | Minimum number of keys that must end up sharing a converted field-name set for it to be kept. `0` keeps every converted set. |\n\nThe disassembly threshold avoids wasting memory on field-name sets shared by only a few keys: at the end of the load, if a set is used by fewer keys than the threshold, those keys are converted back to plain hashes and the shared set is freed. It also acts as a safety valve during the load. Redis tracks how many converted sets are still below the threshold; once at least 1,000 have been created, if more than half of them are still below the threshold, Redis assumes the dataset is a poor fit for compact hashes and stops creating new ones partway through the load. From that point, only hashes whose field set matches one already created during this load are converted; the rest stay plain."},{"id":"performance","title":"Performance","role":"performance","text":"Most Redis hash commands are O(1).\n\nA few commands, such as [`HKEYS`](https://redis.io/docs/latest/commands/hkeys), [`HVALS`](https://redis.io/docs/latest/commands/hvals), [`HGETALL`](https://redis.io/docs/latest/commands/hgetall), and most of the expiration-related commands, are O(n), where _n_ is the number of field-value pairs."},{"id":"limits","title":"Limits","role":"limits","text":"Every hash can store up to 4,294,967,295 (2^32 - 1) field-value pairs.\nIn practice, your hashes are limited only by the overall memory on the VMs hosting your Redis deployment."},{"id":"learn-more","title":"Learn more","role":"related","text":"* [Redis Hashes Explained](https://www.youtube.com/watch?v=-KdITaRkQ-U) is a short, comprehensive video explainer covering Redis hashes.\n* [Redis University's RU101](https://university.redis.com/courses/ru101/) covers Redis hashes in detail."}],"examples":[{"id":"hash-command-summary-ex0","language":"plaintext","code":"> HSET bike:1 model Deimos brand Ergonom type 'Enduro bikes' price 4972\n(integer) 4\n> HGET bike:1 model\n\"Deimos\"\n> HGET bike:1 price\n\"4972\"\n> HGETALL bike:1\n1) \"model\"\n2) \"Deimos\"\n3) \"brand\"\n4) \"Ergonom\"\n5) \"type\"\n6) \"Enduro bikes\"\n7) \"price\"\n8) \"4972\"","section_id":"hash-command-summary"},{"id":"hash-command-summary-ex1","language":"csharp","code":"db.HashSet(\"bike:1\", [\n            new(\"model\", \"Deimos\"),\n            new(\"brand\", \"Ergonom\"),\n            new(\"type\", \"Enduro bikes\"),\n            new(\"price\", 4972)\n        ]);\n\n        Console.WriteLine(\"Hash Created\");\n        // Hash Created\n\n        var model = db.HashGet(\"bike:1\", \"model\");\n        Console.WriteLine($\"Model: {model}\");\n        // Model: Deimos\n\n        var price = db.HashGet(\"bike:1\", \"price\");\n        Console.WriteLine($\"Price: {price}\");\n        // Price: 4972\n\n        var bike = db.HashGetAll(\"bike:1\");\n        Console.WriteLine(\"bike:1\");\n        Console.WriteLine(string.Join(\"\\n\", bike.Select(b => $\"{b.Name}: {b.Value}\")));\n        // Bike:1:\n        // model: Deimos\n        // brand: Ergonom\n        // type: Enduro bikes\n        // price: 4972","section_id":"hash-command-summary"},{"id":"hash-command-summary-ex2","language":"go","code":"hashFields := []string{\n\t\t\"model\", \"Deimos\",\n\t\t\"brand\", \"Ergonom\",\n\t\t\"type\", \"Enduro bikes\",\n\t\t\"price\", \"4972\",\n\t}\n\n\tres1, err := rdb.HSet(ctx, \"bike:1\", hashFields).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> 4\n\n\tres2, err := rdb.HGet(ctx, \"bike:1\", \"model\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> Deimos\n\n\tres3, err := rdb.HGet(ctx, \"bike:1\", \"price\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3) // >>> 4972\n\n\tcmdReturn := rdb.HGetAll(ctx, \"bike:1\")\n\tres4, err := cmdReturn.Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4)\n\t// >>> map[brand:Ergonom model:Deimos price:4972 type:Enduro bikes]\n\n\ttype BikeInfo struct {\n\t\tModel string `redis:\"model\"`\n\t\tBrand string `redis:\"brand\"`\n\t\tType  string `redis:\"type\"`\n\t\tPrice int    `redis:\"price\"`\n\t}\n\n\tvar res4a BikeInfo\n\n\tif err := cmdReturn.Scan(&res4a); err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Printf(\"Model: %v, Brand: %v, Type: %v, Price: $%v\\n\",\n\t\tres4a.Model, res4a.Brand, res4a.Type, res4a.Price)\n\t// >>> Model: Deimos, Brand: Ergonom, Type: Enduro bikes, Price: $4972","section_id":"hash-command-summary"},{"id":"hash-command-summary-ex3","language":"java","code":"Map<String, String> bike1 = new HashMap<>();\n            bike1.put(\"model\", \"Deimos\");\n            bike1.put(\"brand\", \"Ergonom\");\n            bike1.put(\"type\", \"Enduro bikes\");\n            bike1.put(\"price\", \"4972\");\n\n            CompletableFuture<Void> setGetAll = asyncCommands.hset(\"bike:1\", bike1).thenCompose(res1 -> {\n                System.out.println(res1); // >>> 4\n                return asyncCommands.hget(\"bike:1\", \"model\");\n            }).thenCompose(res2 -> {\n                System.out.println(res2); // >>> Deimos\n                return asyncCommands.hget(\"bike:1\", \"price\");\n            }).thenCompose(res3 -> {\n                System.out.println(res3); // >>> 4972\n                return asyncCommands.hgetall(\"bike:1\");\n            })\n                    .thenAccept(System.out::println)\n                    // >>> {type=Enduro bikes, brand=Ergonom, price=4972, model=Deimos}\n                    .toCompletableFuture();","section_id":"hash-command-summary"},{"id":"hash-command-summary-ex4","language":"java","code":"Map<String, String> bike1 = new HashMap<>();\n            bike1.put(\"model\", \"Deimos\");\n            bike1.put(\"brand\", \"Ergonom\");\n            bike1.put(\"type\", \"Enduro bikes\");\n            bike1.put(\"price\", \"4972\");\n\n            Mono<Long> setGetAll = reactiveCommands.hset(\"bike:1\", bike1).doOnNext(result -> {\n                System.out.println(result); // >>> 4\n            });\n\n            setGetAll.block();\n\n            Mono<String> getModel = reactiveCommands.hget(\"bike:1\", \"model\").doOnNext(result -> {\n                System.out.println(result); // >>> Deimos\n            });\n\n            Mono<String> getPrice = reactiveCommands.hget(\"bike:1\", \"price\").doOnNext(result -> {\n                System.out.println(result); // >>> 4972\n            });\n\n            Mono<List<KeyValue<String, String>>> getAll = reactiveCommands.hgetall(\"bike:1\").collectList().doOnNext(result -> {\n                System.out.println(result);\n                // >>> [KeyValue[type, Enduro bikes], KeyValue[brand, Ergonom],\n                // KeyValue[price, 4972], KeyValue[model, Deimos]]\n            });","section_id":"hash-command-summary"},{"id":"hash-command-summary-ex5","language":"java","code":"Map<String, String> bike1 = new HashMap<>();\n      bike1.put(\"model\", \"Deimos\");\n      bike1.put(\"brand\", \"Ergonom\");\n      bike1.put(\"type\", \"Enduro bikes\");\n      bike1.put(\"price\", \"4972\");\n\n      Long res1 = jedis.hset(\"bike:1\", bike1);\n      System.out.println(res1); // 4\n\n      String res2 = jedis.hget(\"bike:1\", \"model\");\n      System.out.println(res2); // Deimos\n\n      String res3 = jedis.hget(\"bike:1\", \"price\");\n      System.out.println(res3); // 4972\n\n      Map<String, String> res4 = jedis.hgetAll(\"bike:1\");\n      System.out.println(res4); // {type=Enduro bikes, brand=Ergonom, price=4972, model=Deimos}","section_id":"hash-command-summary"},{"id":"hash-command-summary-ex6","language":"javascript","code":"const res1 = await client.hSet(\n  'bike:1',\n  {\n    'model': 'Deimos',\n    'brand': 'Ergonom',\n    'type': 'Enduro bikes',\n    'price': 4972,\n  }\n)\nconsole.log(res1) // 4\n\nconst res2 = await client.hGet('bike:1', 'model')\nconsole.log(res2)  // 'Deimos'\n\nconst res3 = await client.hGet('bike:1', 'price')\nconsole.log(res3)  // '4972'\n\nconst res4 = await client.hGetAll('bike:1')\nconsole.log(res4)  \n/*\n{\n  brand: 'Ergonom',\n  model: 'Deimos',\n  price: '4972',\n  type: 'Enduro bikes'\n}\n*/","section_id":"hash-command-summary"},{"id":"hash-command-summary-ex7","language":"php","code":"$res1 = $r->hmset('bike:1', [\n            'model' => 'Deimos',\n            'brand' => 'Ergonom',\n            'type' => 'Enduro bikes',\n            'price' => 4972,\n        ]);\n\n        echo $res1 . PHP_EOL;\n        // >>> 4\n\n        $res2 = $r->hget('bike:1', 'model');\n        echo $res2 . PHP_EOL;\n        // >>> Deimos\n\n        $res3 = $r->hget('bike:1', 'price');\n        echo $res3 . PHP_EOL;\n        // >>> 4972\n\n        $res4 = $r->hgetall('bike:1');\n        echo json_encode($res3) . PHP_EOL;\n        // >>> {\"name\":\"Deimos\",\"brand\":\"Ergonom\",\"type\":\"Enduro bikes\",\"price\":\"4972\"}","section_id":"hash-command-summary"},{"id":"hash-command-summary-ex8","language":"python","code":"res1 = r.hset(\n    \"bike:1\",\n    mapping={\n        \"model\": \"Deimos\",\n        \"brand\": \"Ergonom\",\n        \"type\": \"Enduro bikes\",\n        \"price\": 4972,\n    },\n)\nprint(res1)\n# >>> 4\n\nres2 = r.hget(\"bike:1\", \"model\")\nprint(res2)\n# >>> 'Deimos'\n\nres3 = r.hget(\"bike:1\", \"price\")\nprint(res3)\n# >>> '4972'\n\nres4 = r.hgetall(\"bike:1\")\nprint(res4)\n# >>> {'model': 'Deimos', 'brand': 'Ergonom', 'type': 'Enduro bikes', 'price': '4972'}","section_id":"hash-command-summary"},{"id":"hash-command-summary-ex9","language":"ruby","code":"res1 = r.hset('bike:1', {\n  'model' => 'Deimos',\n  'brand' => 'Ergonom',\n  'type' => 'Enduro bikes',\n  'price' => 4972\n})\nputs res1 # >>> 4\n\nres2 = r.hget('bike:1', 'model')\nputs res2 # >>> Deimos\n\nres3 = r.hget('bike:1', 'price')\nputs res3 # >>> 4972\n\nres4 = r.hgetall('bike:1')\nputs res4.inspect\n# >>> {\"model\" => \"Deimos\", \"brand\" => \"Ergonom\", \"type\" => \"Enduro bikes\", \"price\" => \"4972\"}","section_id":"hash-command-summary"},{"id":"hash-command-summary-ex10","language":"rust","code":"let hash_fields = [\n            (\"model\", \"Deimos\"),\n            (\"brand\", \"Ergonom\"),\n            (\"type\", \"Enduro bikes\"),\n            (\"price\", \"4972\"),\n        ];\n\n        if let Ok(res) = r.hset_multiple(\"bike:1\", &hash_fields).await {\n            let res: String = res;\n            println!(\"{res}\");    // >>> OK\n        }\n\n        match r.hget(\"bike:1\", \"model\").await {\n            Ok(res) => {\n                let res: String = res;\n                println!(\"{res}\");   // >>> Deimos\n            },\n            Err(e) => {\n                println!(\"Error getting bike:1 model: {e}\");\n                return;\n            }\n        };\n\n        match r.hget(\"bike:1\", \"price\").await {\n            Ok(res) => {\n                let res: String = res;\n                println!(\"{res}\");   // >>> 4972\n            },\n            Err(e) => {\n                println!(\"Error getting bike:1 price: {e}\");\n                return;\n            }\n        };\n\n        match r.hgetall(\"bike:1\").await {\n            Ok(res) => {\n                let res: Vec<(String, String)> = res;\n                println!(\"{res:?}\");\n                // >>> [(\"model\", \"Deimos\"), (\"brand\", \"Ergonom\"), (\"type\", \"Enduro bikes\"), (\"price\", \"4972\")]\n            },\n            Err(e) => {\n                println!(\"Error getting bike:1: {e}\");\n                return;\n            }\n        };","section_id":"hash-command-summary"},{"id":"hash-command-summary-ex11","language":"rust","code":"let hash_fields = [\n            (\"model\", \"Deimos\"),\n            (\"brand\", \"Ergonom\"),\n            (\"type\", \"Enduro bikes\"),\n            (\"price\", \"4972\"),\n        ];\n\n        if let Ok(res) = r.hset_multiple(\"bike:1\", &hash_fields) {\n            let res: String = res;\n            println!(\"{res}\");    // >>> OK\n        }\n\n        match r.hget(\"bike:1\", \"model\") {\n            Ok(res) => {\n                let res: String = res;\n                println!(\"{res}\");   // >>> Deimos\n            },\n            Err(e) => {\n                println!(\"Error getting bike:1 model: {e}\");\n                return;\n            }\n        };\n\n        match r.hget(\"bike:1\", \"price\") {\n            Ok(res) => {\n                let res: String = res;\n                println!(\"{res}\");   // >>> 4972\n            },\n            Err(e) => {\n                println!(\"Error getting bike:1 price: {e}\");\n                return;\n            }\n        };\n\n        match r.hgetall(\"bike:1\") {\n            Ok(res) => {\n                let res: Vec<(String, String)> = res;\n                println!(\"{res:?}\");\n                // >>> [(\"model\", \"Deimos\"), (\"brand\", \"Ergonom\"), (\"type\", \"Enduro bikes\"), (\"price\", \"4972\")]\n            },\n            Err(e) => {\n                println!(\"Error getting bike:1: {e}\");\n                return;\n            }\n        };","section_id":"hash-command-summary"},{"id":"hash-command-summary-ex12","language":"plaintext","code":"# Recreate the bike:1 hash so this example runs on its own.\n> DEL bike:1\n(integer) 1\n> HSET bike:1 model Deimos brand Ergonom type 'Enduro bikes' price 4972\n(integer) 4\n> HMGET bike:1 model price no-such-field\n1) \"Deimos\"\n2) \"4972\"\n3) (nil)","section_id":"hash-command-summary"},{"id":"hash-command-summary-ex13","language":"csharp","code":"// Recreate the bike:1 hash so this example runs on its own.\n        db.KeyDelete(\"bike:1\");\n        db.HashSet(\"bike:1\", [\n            new(\"model\", \"Deimos\"),\n            new(\"brand\", \"Ergonom\"),\n            new(\"type\", \"Enduro bikes\"),\n            new(\"price\", 4972)\n        ]);\n\n        var values = db.HashGet(\"bike:1\", [\"model\", \"price\"]);\n        Console.WriteLine(string.Join(\" \", values));\n        // Deimos 4972","section_id":"hash-command-summary"},{"id":"hash-command-summary-ex14","language":"go","code":"// Recreate the bike:1 hash so this example runs on its own.\n\trdb.Del(ctx, \"bike:1\")\n\thashFields := []string{\n\t\t\"model\", \"Deimos\",\n\t\t\"brand\", \"Ergonom\",\n\t\t\"type\", \"Enduro bikes\",\n\t\t\"price\", \"4972\",\n\t}\n\trdb.HSet(ctx, \"bike:1\", hashFields)\n\n\tcmdReturn := rdb.HMGet(ctx, \"bike:1\", \"model\", \"price\")\n\tres5, err := cmdReturn.Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res5) // >>> [Deimos 4972]\n\n\ttype BikeInfo struct {\n\t\tModel string `redis:\"model\"`\n\t\tBrand string `redis:\"-\"`\n\t\tType  string `redis:\"-\"`\n\t\tPrice int    `redis:\"price\"`\n\t}\n\n\tvar res5a BikeInfo\n\n\tif err := cmdReturn.Scan(&res5a); err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Printf(\"Model: %v, Price: $%v\\n\", res5a.Model, res5a.Price)\n\t// >>> Model: Deimos, Price: $4972","section_id":"hash-command-summary"},{"id":"hash-command-summary-ex15","language":"java","code":"// Recreate the bike:1 hash so this example runs on its own.\n            CompletableFuture<Void> hmGet = setGetAll\n                    .thenCompose(res4 -> asyncCommands.del(\"bike:1\"))\n                    .thenCompose(delRes -> asyncCommands.hset(\"bike:1\", bike1))\n                    .thenCompose(hsetRes -> asyncCommands.hmget(\"bike:1\", \"model\", \"price\"))\n                    .thenAccept(System.out::println)\n                    // [KeyValue[model, Deimos], KeyValue[price, 4972]]\n                    .toCompletableFuture();","section_id":"hash-command-summary"},{"id":"hash-command-summary-ex16","language":"java","code":"// Recreate the bike:1 hash so this example runs on its own.\n            Mono<List<KeyValue<String, String>>> hmGet = reactiveCommands.del(\"bike:1\")\n                    .then(reactiveCommands.hset(\"bike:1\", bike1))\n                    .then(reactiveCommands.hmget(\"bike:1\", \"model\", \"price\").collectList())\n                    .doOnNext(result -> {\n                        System.out.println(result);\n                        // >>> [KeyValue[model, Deimos], KeyValue[price, 4972]]\n                    });","section_id":"hash-command-summary"},{"id":"hash-command-summary-ex17","language":"java","code":"// Recreate the bike:1 hash so this example runs on its own.\n      jedis.del(\"bike:1\");\n      jedis.hset(\"bike:1\", bike1);\n\n      List<String> res5 = jedis.hmget(\"bike:1\", \"model\", \"price\");\n      System.out.println(res5); // [Deimos, 4972]","section_id":"hash-command-summary"},{"id":"hash-command-summary-ex18","language":"javascript","code":"// Recreate the bike:1 hash so this example runs on its own.\nawait client.del('bike:1')\nawait client.hSet(\n  'bike:1',\n  {\n    'model': 'Deimos',\n    'brand': 'Ergonom',\n    'type': 'Enduro bikes',\n    'price': 4972,\n  }\n)\n\nconst res5 = await client.hmGet('bike:1', ['model', 'price'])\nconsole.log(res5)  // ['Deimos', '4972']","section_id":"hash-command-summary"},{"id":"hash-command-summary-ex19","language":"php","code":"// Recreate the bike:1 hash so this example runs on its own.\n        $r->del('bike:1');\n        $r->hmset('bike:1', [\n            'model' => 'Deimos',\n            'brand' => 'Ergonom',\n            'type' => 'Enduro bikes',\n            'price' => 4972,\n        ]);\n\n        $res5 = $r->hmget('bike:1', ['model', 'price']);\n        echo json_encode($res5) . PHP_EOL;\n        // >>> [\"Deimos\",\"4972\"]","section_id":"hash-command-summary"},{"id":"hash-command-summary-ex20","language":"python","code":"# Recreate the bike:1 hash so this example runs on its own.\nr.delete(\"bike:1\")\nr.hset(\n    \"bike:1\",\n    mapping={\n        \"model\": \"Deimos\",\n        \"brand\": \"Ergonom\",\n        \"type\": \"Enduro bikes\",\n        \"price\": 4972,\n    },\n)\n\nres5 = r.hmget(\"bike:1\", [\"model\", \"price\"])\nprint(res5)\n# >>> ['Deimos', '4972']","section_id":"hash-command-summary"},{"id":"hash-command-summary-ex21","language":"ruby","code":"# Recreate the bike:1 hash so this example runs on its own.\nr.del('bike:1')\nr.hset('bike:1', {\n  'model' => 'Deimos',\n  'brand' => 'Ergonom',\n  'type' => 'Enduro bikes',\n  'price' => 4972\n})\n\nres5 = r.hmget('bike:1', 'model', 'price', 'no-such-field')\nputs res5.inspect # >>> [\"Deimos\", \"4972\", nil]","section_id":"hash-command-summary"},{"id":"hash-command-summary-ex22","language":"rust","code":"// Recreate the bike:1 hash so this example runs on its own.\n        let _: () = r.del(\"bike:1\").await.expect(\"Failed to del\");\n        let _: () = r.hset_multiple(\n            \"bike:1\",\n            &[(\"model\", \"Deimos\"), (\"brand\", \"Ergonom\"), (\"type\", \"Enduro bikes\"), (\"price\", \"4972\")],\n        ).await.expect(\"Failed to hset\");\n\n        match r.hmget(\"bike:1\", &[\"model\", \"price\"]).await {\n            Ok(res) => {\n                let res: Vec<String> = res;\n                println!(\"{res:?}\");   // >>> [\"Deimos\", \"4972\"]\n            },\n            Err(e) => {\n                println!(\"Error getting bike:1: {e}\");\n                return;\n            }\n        };","section_id":"hash-command-summary"},{"id":"hash-command-summary-ex23","language":"rust","code":"// Recreate the bike:1 hash so this example runs on its own.\n        let _: () = r.del(\"bike:1\").expect(\"Failed to del\");\n        let _: () = r.hset_multiple(\n            \"bike:1\",\n            &[(\"model\", \"Deimos\"), (\"brand\", \"Ergonom\"), (\"type\", \"Enduro bikes\"), (\"price\", \"4972\")],\n        ).expect(\"Failed to hset\");\n\n        match r.hmget(\"bike:1\", &[\"model\", \"price\"]) {\n            Ok(res) => {\n                let res: Vec<String> = res;\n                println!(\"{res:?}\");   // >>> [\"Deimos\", \"4972\"]\n            },\n            Err(e) => {\n                println!(\"Error getting bike:1: {e}\");\n                return;\n            }\n        };","section_id":"hash-command-summary"},{"id":"hash-command-summary-ex24","language":"plaintext","code":"# Recreate the bike:1 hash so this example runs on its own.\n> DEL bike:1\n(integer) 1\n> HSET bike:1 model Deimos brand Ergonom type 'Enduro bikes' price 4972\n(integer) 4\n> HINCRBY bike:1 price 100\n(integer) 5072\n> HINCRBY bike:1 price -100\n(integer) 4972","section_id":"hash-command-summary"},{"id":"hash-command-summary-ex25","language":"csharp","code":"// Recreate the bike:1 hash so this example runs on its own.\n        db.KeyDelete(\"bike:1\");\n        db.HashSet(\"bike:1\", [\n            new(\"model\", \"Deimos\"),\n            new(\"brand\", \"Ergonom\"),\n            new(\"type\", \"Enduro bikes\"),\n            new(\"price\", 4972)\n        ]);\n\n        var newPrice = db.HashIncrement(\"bike:1\", \"price\", 100);\n        Console.WriteLine($\"New price: {newPrice}\");\n        // New price: 5072\n\n        newPrice = db.HashIncrement(\"bike:1\", \"price\", -100);\n        Console.WriteLine($\"New price: {newPrice}\");\n        // New price: 4972","section_id":"hash-command-summary"},{"id":"hash-command-summary-ex26","language":"go","code":"// Recreate the bike:1 hash so this example runs on its own.\n\trdb.Del(ctx, \"bike:1\")\n\thashFields := []string{\n\t\t\"model\", \"Deimos\",\n\t\t\"brand\", \"Ergonom\",\n\t\t\"type\", \"Enduro bikes\",\n\t\t\"price\", \"4972\",\n\t}\n\trdb.HSet(ctx, \"bike:1\", hashFields)\n\n\tres6, err := rdb.HIncrBy(ctx, \"bike:1\", \"price\", 100).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res6) // >>> 5072\n\n\tres7, err := rdb.HIncrBy(ctx, \"bike:1\", \"price\", -100).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res7) // >>> 4972","section_id":"hash-command-summary"},{"id":"hash-command-summary-ex27","language":"java","code":"// Recreate the bike:1 hash so this example runs on its own.\n            CompletableFuture<Void> hIncrBy = hmGet\n                    .thenCompose(r -> asyncCommands.del(\"bike:1\"))\n                    .thenCompose(delRes -> asyncCommands.hset(\"bike:1\", bike1))\n                    .thenCompose(hsetRes -> asyncCommands.hincrby(\"bike:1\", \"price\", 100))\n                    .thenCompose(res6 -> {\n                        System.out.println(res6); // >>> 5072\n                        return asyncCommands.hincrby(\"bike:1\", \"price\", -100);\n                    })\n                    .thenAccept(System.out::println)\n                    // >>> 4972\n                    .toCompletableFuture();","section_id":"hash-command-summary"},{"id":"hash-command-summary-ex28","language":"java","code":"// Recreate the bike:1 hash so this example runs on its own.\n            Mono<Void> hIncrBy = reactiveCommands.del(\"bike:1\")\n                    .then(reactiveCommands.hset(\"bike:1\", bike1))\n                    .then(reactiveCommands.hincrby(\"bike:1\", \"price\", 100)).doOnNext(result -> {\n                System.out.println(result); // >>> 5072\n            }).flatMap(v -> reactiveCommands.hincrby(\"bike:1\", \"price\", -100)).doOnNext(result -> {\n                System.out.println(result); // >>> 4972\n            }).then();","section_id":"hash-command-summary"},{"id":"hash-command-summary-ex29","language":"java","code":"// Recreate the bike:1 hash so this example runs on its own.\n      jedis.del(\"bike:1\");\n      jedis.hset(\"bike:1\", bike1);\n\n      Long res6 = jedis.hincrBy(\"bike:1\", \"price\", 100);\n      System.out.println(res6); // 5072\n      Long res7 = jedis.hincrBy(\"bike:1\", \"price\", -100);\n      System.out.println(res7); // 4972","section_id":"hash-command-summary"},{"id":"hash-command-summary-ex30","language":"javascript","code":"// Recreate the bike:1 hash so this example runs on its own.\nawait client.del('bike:1')\nawait client.hSet(\n  'bike:1',\n  {\n    'model': 'Deimos',\n    'brand': 'Ergonom',\n    'type': 'Enduro bikes',\n    'price': 4972,\n  }\n)\n\nconst res6 = await client.hIncrBy('bike:1', 'price', 100)\nconsole.log(res6)  // 5072\nconst res7 = await client.hIncrBy('bike:1', 'price', -100)\nconsole.log(res7)  // 4972","section_id":"hash-command-summary"},{"id":"hash-command-summary-ex31","language":"php","code":"// Recreate the bike:1 hash so this example runs on its own.\n        $r->del('bike:1');\n        $r->hmset('bike:1', [\n            'model' => 'Deimos',\n            'brand' => 'Ergonom',\n            'type' => 'Enduro bikes',\n            'price' => 4972,\n        ]);\n\n        $res6 = $r->hincrby('bike:1', 'price', 100);\n        echo $res6 . PHP_EOL;\n        // >>> 5072\n\n        $res7 = $r->hincrby('bike:1', 'price', -100);\n        echo $res7 . PHP_EOL;\n        // >>> 4972","section_id":"hash-command-summary"},{"id":"hash-command-summary-ex32","language":"python","code":"# Recreate the bike:1 hash so this example runs on its own.\nr.delete(\"bike:1\")\nr.hset(\n    \"bike:1\",\n    mapping={\n        \"model\": \"Deimos\",\n        \"brand\": \"Ergonom\",\n        \"type\": \"Enduro bikes\",\n        \"price\": 4972,\n    },\n)\n\nres6 = r.hincrby(\"bike:1\", \"price\", 100)\nprint(res6)\n# >>> 5072\nres7 = r.hincrby(\"bike:1\", \"price\", -100)\nprint(res7)\n# >>> 4972","section_id":"hash-command-summary"},{"id":"hash-command-summary-ex33","language":"ruby","code":"# Recreate the bike:1 hash so this example runs on its own.\nr.del('bike:1')\nr.hset('bike:1', {\n  'model' => 'Deimos',\n  'brand' => 'Ergonom',\n  'type' => 'Enduro bikes',\n  'price' => 4972\n})\n\nres6 = r.hincrby('bike:1', 'price', 100)\nputs res6 # >>> 5072\n\nres7 = r.hincrby('bike:1', 'price', -100)\nputs res7 # >>> 4972","section_id":"hash-command-summary"},{"id":"hash-command-summary-ex34","language":"rust","code":"// Recreate the bike:1 hash so this example runs on its own.\n        let _: () = r.del(\"bike:1\").await.expect(\"Failed to del\");\n        let _: () = r.hset_multiple(\n            \"bike:1\",\n            &[(\"model\", \"Deimos\"), (\"brand\", \"Ergonom\"), (\"type\", \"Enduro bikes\"), (\"price\", \"4972\")],\n        ).await.expect(\"Failed to hset\");\n\n        if let Ok(res) = r.hincr(\"bike:1\", \"price\", 100).await {\n            let res: i32 = res;\n            println!(\"{res}\");    // >>> 5072\n        }\n\n        if let Ok(res) = r.hincr(\"bike:1\", \"price\", -100).await {\n            let res: i32 = res;\n            println!(\"{res}\");    // >>> 4972\n        }","section_id":"hash-command-summary"},{"id":"hash-command-summary-ex35","language":"rust","code":"// Recreate the bike:1 hash so this example runs on its own.\n        let _: () = r.del(\"bike:1\").expect(\"Failed to del\");\n        let _: () = r.hset_multiple(\n            \"bike:1\",\n            &[(\"model\", \"Deimos\"), (\"brand\", \"Ergonom\"), (\"type\", \"Enduro bikes\"), (\"price\", \"4972\")],\n        ).expect(\"Failed to hset\");\n\n        if let Ok(res) = r.hincr(\"bike:1\", \"price\", 100) {\n            let res: i32 = res;\n            println!(\"{res}\");    // >>> 5072\n        }\n\n        if let Ok(res) = r.hincr(\"bike:1\", \"price\", -100) {\n            let res: i32 = res;\n            println!(\"{res}\");    // >>> 4972\n        }","section_id":"hash-command-summary"},{"id":"examples-ex0","language":"plaintext","code":"> HINCRBY bike:1:stats rides 1\n(integer) 1\n> HINCRBY bike:1:stats rides 1\n(integer) 2\n> HINCRBY bike:1:stats rides 1\n(integer) 3\n> HINCRBY bike:1:stats crashes 1\n(integer) 1\n> HINCRBY bike:1:stats owners 1\n(integer) 1\n> HGET bike:1:stats rides\n\"3\"\n> HMGET bike:1:stats owners crashes\n1) \"1\"\n2) \"1\"","section_id":"examples"},{"id":"examples-ex1","language":"csharp","code":"var rides = db.HashIncrement(\"bike:1\", \"rides\");\n        Console.WriteLine($\"Rides: {rides}\");\n        // Rides: 1\n\n        rides = db.HashIncrement(\"bike:1\", \"rides\");\n        Console.WriteLine($\"Rides: {rides}\");\n        // Rides: 2\n\n        rides = db.HashIncrement(\"bike:1\", \"rides\");\n        Console.WriteLine($\"Rides: {rides}\");\n        // Rides: 3\n\n        var crashes = db.HashIncrement(\"bike:1\", \"crashes\");\n        Console.WriteLine($\"Crashes: {crashes}\");\n        // Crashes: 1\n\n        var owners = db.HashIncrement(\"bike:1\", \"owners\");\n        Console.WriteLine($\"Owners: {owners}\");\n        // Owners: 1\n\n        var stats = db.HashGet(\"bike:1\", [\"crashes\", \"owners\"]);\n        Console.WriteLine($\"Bike stats: crashes={stats[0]}, owners={stats[1]}\");\n        // Bike stats: crashes=1, owners=1","section_id":"examples"},{"id":"examples-ex2","language":"go","code":"res8, err := rdb.HIncrBy(ctx, \"bike:1:stats\", \"rides\", 1).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res8) // >>> 1\n\n\tres9, err := rdb.HIncrBy(ctx, \"bike:1:stats\", \"rides\", 1).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res9) // >>> 2\n\n\tres10, err := rdb.HIncrBy(ctx, \"bike:1:stats\", \"rides\", 1).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res10) // >>> 3\n\n\tres11, err := rdb.HIncrBy(ctx, \"bike:1:stats\", \"crashes\", 1).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res11) // >>> 1\n\n\tres12, err := rdb.HIncrBy(ctx, \"bike:1:stats\", \"owners\", 1).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res12) // >>> 1\n\n\tres13, err := rdb.HGet(ctx, \"bike:1:stats\", \"rides\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res13) // >>> 3\n\n\tres14, err := rdb.HMGet(ctx, \"bike:1:stats\", \"crashes\", \"owners\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res14) // >>> [1 1]","section_id":"examples"},{"id":"examples-ex3","language":"java","code":"CompletableFuture<Void> incrByGetMget = asyncCommands.hincrby(\"bike:1:stats\", \"rides\", 1).thenCompose(res7 -> {\n                System.out.println(res7); // >>> 1\n                return asyncCommands.hincrby(\"bike:1:stats\", \"rides\", 1);\n            }).thenCompose(res8 -> {\n                System.out.println(res8); // >>> 2\n                return asyncCommands.hincrby(\"bike:1:stats\", \"rides\", 1);\n            }).thenCompose(res9 -> {\n                System.out.println(res9); // >>> 3\n                return asyncCommands.hincrby(\"bike:1:stats\", \"crashes\", 1);\n            }).thenCompose(res10 -> {\n                System.out.println(res10); // >>> 1\n                return asyncCommands.hincrby(\"bike:1:stats\", \"owners\", 1);\n            }).thenCompose(res11 -> {\n                System.out.println(res11); // >>> 1\n                return asyncCommands.hget(\"bike:1:stats\", \"rides\");\n            }).thenCompose(res12 -> {\n                System.out.println(res12); // >>> 3\n                return asyncCommands.hmget(\"bike:1:stats\", \"crashes\", \"owners\");\n            })\n                    .thenAccept(System.out::println)\n                    // >>> [KeyValue[crashes, 1], KeyValue[owners, 1]]\n                    .toCompletableFuture();","section_id":"examples"},{"id":"examples-ex4","language":"java","code":"Mono<Void> incrByGetMget = reactiveCommands.hincrby(\"bike:1:stats\", \"rides\", 1).doOnNext(result -> {\n                System.out.println(result); // >>> 1\n            }).flatMap(v -> reactiveCommands.hincrby(\"bike:1:stats\", \"rides\", 1)).doOnNext(result -> {\n                System.out.println(result); // >>> 2\n            }).flatMap(v -> reactiveCommands.hincrby(\"bike:1:stats\", \"rides\", 1)).doOnNext(result -> {\n                System.out.println(result); // >>> 3\n            }).flatMap(v -> reactiveCommands.hincrby(\"bike:1:stats\", \"crashes\", 1)).doOnNext(result -> {\n                System.out.println(result); // >>> 1\n            }).flatMap(v -> reactiveCommands.hincrby(\"bike:1:stats\", \"owners\", 1)).doOnNext(result -> {\n                System.out.println(result); // >>> 1\n            }).then();\n\n            incrByGetMget.block();\n\n            Mono<String> getRides = reactiveCommands.hget(\"bike:1:stats\", \"rides\").doOnNext(result -> {\n                System.out.println(result); // >>> 3\n            });\n\n            Mono<List<KeyValue<String, String>>> getCrashesOwners = reactiveCommands.hmget(\"bike:1:stats\", \"crashes\", \"owners\")\n                    .collectList().doOnNext(result -> {\n                        System.out.println(result);\n                        // >>> [KeyValue[crashes, 1], KeyValue[owners, 1]]\n                    });","section_id":"examples"},{"id":"examples-ex5","language":"java","code":"Long res8 = jedis.hincrBy(\"bike:1:stats\", \"rides\", 1);\n      System.out.println(res8); // 1\n      Long res9 = jedis.hincrBy(\"bike:1:stats\", \"rides\", 1);\n      System.out.println(res9); // 2\n      Long res10 = jedis.hincrBy(\"bike:1:stats\", \"rides\", 1);\n      System.out.println(res10); // 3\n      Long res11 = jedis.hincrBy(\"bike:1:stats\", \"crashes\", 1);\n      System.out.println(res11); // 1\n      Long res12 = jedis.hincrBy(\"bike:1:stats\", \"owners\", 1);\n      System.out.println(res12); // 1\n      String res13 = jedis.hget(\"bike:1:stats\", \"rides\");\n      System.out.println(res13); // 3\n      List<String> res14 = jedis.hmget(\"bike:1:stats\", \"crashes\", \"owners\");\n      System.out.println(res14); // [1, 1]","section_id":"examples"},{"id":"examples-ex6","language":"javascript","code":"const res11 = await client.hIncrBy('bike:1:stats', 'rides', 1)\nconsole.log(res11)  // 1\nconst res12 = await client.hIncrBy('bike:1:stats', 'rides', 1)\nconsole.log(res12)  // 2\nconst res13 = await client.hIncrBy('bike:1:stats', 'rides', 1)\nconsole.log(res13)  // 3\nconst res14 = await client.hIncrBy('bike:1:stats', 'crashes', 1)\nconsole.log(res14)  // 1\nconst res15 = await client.hIncrBy('bike:1:stats', 'owners', 1)\nconsole.log(res15)  // 1\nconst res16 = await client.hGet('bike:1:stats', 'rides')\nconsole.log(res16)  // 3\nconst res17 = await client.hmGet('bike:1:stats', ['crashes', 'owners'])\nconsole.log(res17)  // ['1', '1']","section_id":"examples"},{"id":"examples-ex7","language":"php","code":"$res8 = $r->hincrby('bike:1:stats', 'rides', 1);\n        echo $res8 . PHP_EOL;\n        // >>> 1\n\n        $res9 = $r->hincrby('bike:1:stats', 'rides', 1);\n        echo $res9 . PHP_EOL;\n        // >>> 2\n\n        $res10 = $r->hincrby('bike:1:stats', 'rides', 1);\n        echo $res10 . PHP_EOL;\n        // >>> 3\n\n        $res11 = $r->hincrby('bike:1:stats', 'crashes', 1);\n        echo $res11 . PHP_EOL;\n        // >>> 1\n\n        $res12 = $r->hincrby('bike:1:stats', 'owners', 1);\n        echo $res12 . PHP_EOL;\n        // >>> 1\n\n        $res13 = $r->hget('bike:1:stats', 'rides');\n        echo $res13 . PHP_EOL;\n        // >>> 3\n\n        $res14 = $r->hmget('bike:1:stats', ['crashes', 'owners']);\n        echo json_encode($res14) . PHP_EOL;\n        // >>> [\"1\",\"1\"]","section_id":"examples"},{"id":"examples-ex8","language":"python","code":"res11 = r.hincrby(\"bike:1:stats\", \"rides\", 1)\nprint(res11)\n# >>> 1\nres12 = r.hincrby(\"bike:1:stats\", \"rides\", 1)\nprint(res12)\n# >>> 2\nres13 = r.hincrby(\"bike:1:stats\", \"rides\", 1)\nprint(res13)\n# >>> 3\nres14 = r.hincrby(\"bike:1:stats\", \"crashes\", 1)\nprint(res14)\n# >>> 1\nres15 = r.hincrby(\"bike:1:stats\", \"owners\", 1)\nprint(res15)\n# >>> 1\nres16 = r.hget(\"bike:1:stats\", \"rides\")\nprint(res16)\n# >>> 3\nres17 = r.hmget(\"bike:1:stats\", [\"crashes\", \"owners\"])\nprint(res17)\n# >>> ['1', '1']","section_id":"examples"},{"id":"examples-ex9","language":"ruby","code":"res8 = r.hincrby('bike:1:stats', 'rides', 1)\nputs res8 # >>> 1\n\nres9 = r.hincrby('bike:1:stats', 'rides', 1)\nputs res9 # >>> 2\n\nres10 = r.hincrby('bike:1:stats', 'rides', 1)\nputs res10 # >>> 3\n\nres11 = r.hincrby('bike:1:stats', 'crashes', 1)\nputs res11 # >>> 1\n\nres12 = r.hincrby('bike:1:stats', 'owners', 1)\nputs res12 # >>> 1\n\nres13 = r.hget('bike:1:stats', 'rides')\nputs res13 # >>> 3\n\nres14 = r.hmget('bike:1:stats', 'owners', 'crashes')\nputs res14.inspect # >>> [\"1\", \"1\"]","section_id":"examples"},{"id":"examples-ex10","language":"rust","code":"if let Ok(res) = r.hincr(\"bike:1:stats\", \"rides\", 1).await {\n            let res: i32 = res;\n            println!(\"{res}\");    // >>> 1\n        }\n\n        if let Ok(res) = r.hincr(\"bike:1:stats\", \"rides\", 1).await {\n            let res: i32 = res;\n            println!(\"{res}\");    // >>> 2\n        }\n\n        if let Ok(res) = r.hincr(\"bike:1:stats\", \"rides\", 1).await {\n            let res: i32 = res;\n            println!(\"{res}\");    // >>> 3\n        }\n\n        if let Ok(res) = r.hincr(\"bike:1:stats\", \"crashes\", 1).await {\n            let res: i32 = res;\n            println!(\"{res}\");    // >>> 1\n        }\n\n        if let Ok(res) = r.hincr(\"bike:1:stats\", \"owners\", 1).await {\n            let res: i32 = res;\n            println!(\"{res}\");    // >>> 1\n        }\n\n        match r.hget(\"bike:1:stats\", \"rides\").await {\n            Ok(res) => {\n                let res: i32 = res;\n                println!(\"{res}\");   // >>> 3\n            },\n            Err(e) => {\n                println!(\"Error getting bike:1:stats rides: {e}\");\n                return;\n            }\n        };\n\n        match r.hmget(\"bike:1:stats\", &[\"crashes\", \"owners\"]).await {\n            Ok(res) => {\n                let res: Vec<i32> = res;\n                println!(\"{res:?}\");   // >>> [1, 1]\n            },\n            Err(e) => {\n                println!(\"Error getting bike:1:stats crashes and owners: {e}\");\n                return;\n            }\n        };","section_id":"examples"},{"id":"examples-ex11","language":"rust","code":"if let Ok(res) = r.hincr(\"bike:1:stats\", \"rides\", 1) {\n            let res: i32 = res;\n            println!(\"{res}\");    // >>> 1\n        }\n\n        if let Ok(res) = r.hincr(\"bike:1:stats\", \"rides\", 1) {\n            let res: i32 = res;\n            println!(\"{res}\");    // >>> 2\n        }\n\n        if let Ok(res) = r.hincr(\"bike:1:stats\", \"rides\", 1) {\n            let res: i32 = res;\n            println!(\"{res}\");    // >>> 3\n        }\n\n        if let Ok(res) = r.hincr(\"bike:1:stats\", \"crashes\", 1) {\n            let res: i32 = res;\n            println!(\"{res}\");    // >>> 1\n        }\n\n        if let Ok(res) = r.hincr(\"bike:1:stats\", \"owners\", 1) {\n            let res: i32 = res;\n            println!(\"{res}\");    // >>> 1\n        }\n\n        match r.hget(\"bike:1:stats\", \"rides\") {\n            Ok(res) => {\n                let res: i32 = res;\n                println!(\"{res}\");   // >>> 3\n            },\n            Err(e) => {\n                println!(\"Error getting bike:1:stats rides: {e}\");\n                return;\n            }\n        };\n\n        match r.hmget(\"bike:1:stats\", &[\"crashes\", \"owners\"]) {\n            Ok(res) => {\n                let res: Vec<i32> = res;\n                println!(\"{res:?}\");   // >>> [1, 1]\n            },\n            Err(e) => {\n                println!(\"Error getting bike:1:stats crashes and owners: {e}\");\n                return;\n            }\n        };","section_id":"examples"},{"id":"field-expiration-examples-ex0","language":"plaintext","code":"> DEL sensor:sensor1\n(integer) 0\n> HSET sensor:sensor1 air_quality 256 battery_level 89\n(integer) 2\n> HEXPIRE sensor:sensor1 60 FIELDS 2 air_quality battery_level\n1) (integer) 1\n2) (integer) 1\n> HTTL sensor:sensor1 FIELDS 2 air_quality battery_level\n1) (integer) 60\n2) (integer) 60","section_id":"field-expiration-examples"},{"id":"field-expiration-examples-ex1","language":"csharp","code":"db.KeyDelete(\"sensor:sensor1\");\n        db.HashSet(\"sensor:sensor1\", [\n            new(\"air_quality\", 256),\n            new(\"battery_level\", 89)\n        ]);\n\n        // Set a TTL of 60 seconds on two fields of the hash.\n        ExpireResult[] hexpireResult = db.HashFieldExpire(\n            \"sensor:sensor1\",\n            new RedisValue[] { \"air_quality\", \"battery_level\" },\n            TimeSpan.FromSeconds(60)\n        );\n        Console.WriteLine(string.Join(\", \", hexpireResult));\n        // Success, Success\n\n        // Retrieve the remaining TTL for those fields.\n        long[] httlResult = db.HashFieldGetTimeToLive(\n            \"sensor:sensor1\",\n            new RedisValue[] { \"air_quality\", \"battery_level\" }\n        );\n        Console.WriteLine(httlResult.Length);\n        // 2","section_id":"field-expiration-examples"},{"id":"field-expiration-examples-ex2","language":"go","code":"rdb.Del(ctx, \"sensor:sensor1\")\n\trdb.HSet(ctx, \"sensor:sensor1\", \"air_quality\", 256, \"battery_level\", 89)\n\n\t// Set a TTL of 60 seconds on two fields of the hash.\n\tres18, err := rdb.HExpire(ctx, \"sensor:sensor1\", 60*time.Second,\n\t\t\"air_quality\", \"battery_level\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res18) // >>> [1 1]\n\n\t// Retrieve the remaining TTL for those fields (returns one value per field).\n\tres19, err := rdb.HTTL(ctx, \"sensor:sensor1\", \"air_quality\", \"battery_level\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(len(res19)) // >>> 2","section_id":"field-expiration-examples"},{"id":"field-expiration-examples-ex3","language":"java","code":"Map<String, String> sensor1 = new HashMap<>();\n            sensor1.put(\"air_quality\", \"256\");\n            sensor1.put(\"battery_level\", \"89\");\n\n            CompletableFuture<Void> hExpire = asyncCommands.del(\"sensor:sensor1\")\n                    .thenCompose(delRes -> asyncCommands.hset(\"sensor:sensor1\", sensor1))\n                    // Set a TTL of 60 seconds on two fields of the hash.\n                    .thenCompose(hsetRes -> asyncCommands.hexpire(\"sensor:sensor1\", 60, \"air_quality\", \"battery_level\"))\n                    .thenCompose(res15 -> {\n                        System.out.println(res15); // >>> [1, 1]\n                        // Retrieve the remaining TTL for those fields.\n                        return asyncCommands.httl(\"sensor:sensor1\", \"air_quality\", \"battery_level\");\n                    })\n                    .thenAccept(res16 -> System.out.println(res16.size()))\n                    // >>> 2\n                    .toCompletableFuture();","section_id":"field-expiration-examples"},{"id":"field-expiration-examples-ex4","language":"java","code":"Map<String, String> sensor1 = new HashMap<>();\n            sensor1.put(\"air_quality\", \"256\");\n            sensor1.put(\"battery_level\", \"89\");\n\n            // Set a TTL of 60 seconds on two fields of the hash.\n            Mono<List<Long>> hExpire = reactiveCommands.del(\"sensor:sensor1\")\n                    .then(reactiveCommands.hset(\"sensor:sensor1\", sensor1))\n                    .then(reactiveCommands.hexpire(\"sensor:sensor1\", 60, \"air_quality\", \"battery_level\").collectList())\n                    .doOnNext(result -> {\n                        System.out.println(result);\n                        // >>> [1, 1]\n                    });\n\n            hExpire.block();\n\n            // Retrieve the remaining TTL for those fields.\n            Mono<List<Long>> hTtl = reactiveCommands.httl(\"sensor:sensor1\", \"air_quality\", \"battery_level\")\n                    .collectList().doOnNext(result -> {\n                        System.out.println(result.size());\n                        // >>> 2\n                    });\n\n            hTtl.block();","section_id":"field-expiration-examples"},{"id":"field-expiration-examples-ex5","language":"java","code":"jedis.del(\"sensor:sensor1\");\n      Map<String, String> sensor1 = new HashMap<>();\n      sensor1.put(\"air_quality\", \"256\");\n      sensor1.put(\"battery_level\", \"89\");\n      jedis.hset(\"sensor:sensor1\", sensor1);\n\n      // Set a TTL of 60 seconds on two fields of the hash.\n      List<Long> res15 = jedis.hexpire(\"sensor:sensor1\", 60, \"air_quality\", \"battery_level\");\n      System.out.println(res15); // [1, 1]\n\n      // Retrieve the remaining TTL for those fields.\n      List<Long> res16 = jedis.httl(\"sensor:sensor1\", \"air_quality\", \"battery_level\");\n      System.out.println(res16.size()); // 2","section_id":"field-expiration-examples"},{"id":"field-expiration-examples-ex6","language":"javascript","code":"await client.del('sensor:sensor1')\nawait client.hSet('sensor:sensor1', { 'air_quality': 256, 'battery_level': 89 })\n\n// Set a TTL of 60 seconds on two fields of the hash.\nconst res18 = await client.hExpire('sensor:sensor1', ['air_quality', 'battery_level'], 60)\nconsole.log(res18)  // [1, 1]\n\n// Retrieve the remaining TTL for those fields.\nconst res19 = await client.hTTL('sensor:sensor1', ['air_quality', 'battery_level'])\nconsole.log(res19)  // [60, 60] (or close to 60)","section_id":"field-expiration-examples"},{"id":"field-expiration-examples-ex7","language":"php","code":"$r->del('sensor:sensor1');\n        $r->hmset('sensor:sensor1', [\n            'air_quality' => 256,\n            'battery_level' => 89,\n        ]);\n\n        // Set a TTL of 60 seconds on two fields of the hash.\n        $res15 = $r->hexpire('sensor:sensor1', 60, ['air_quality', 'battery_level']);\n        echo json_encode($res15) . PHP_EOL;\n        // >>> [1,1]\n\n        // Retrieve the remaining TTL for those fields.\n        $res16 = $r->httl('sensor:sensor1', ['air_quality', 'battery_level']);\n        echo count($res16) . PHP_EOL;\n        // >>> 2","section_id":"field-expiration-examples"},{"id":"field-expiration-examples-ex8","language":"python","code":"r.delete(\"sensor:sensor1\")\nr.hset(\"sensor:sensor1\", mapping={\"air_quality\": 256, \"battery_level\": 89})\n\n# Set a TTL of 60 seconds on two fields of the hash.\nres18 = r.hexpire(\"sensor:sensor1\", 60, \"air_quality\", \"battery_level\")\nprint(res18)\n# >>> [1, 1]\n\n# Retrieve the remaining TTL for those fields.\nres19 = r.httl(\"sensor:sensor1\", \"air_quality\", \"battery_level\")\nprint(res19)\n# >>> [60, 60]\n# (your actual values may be slightly lower)","section_id":"field-expiration-examples"},{"id":"field-expiration-examples-ex9","language":"ruby","code":"r.del('sensor:sensor1')\nr.hset('sensor:sensor1', { 'air_quality' => 256, 'battery_level' => 89 })\n\n# Set a TTL of 60 seconds on two fields of the hash.\nres15 = r.hexpire('sensor:sensor1', 60, 'air_quality', 'battery_level')\nputs res15.inspect # >>> [1, 1]\n\n# Retrieve the remaining TTL for those fields.\nres16 = r.httl('sensor:sensor1', 'air_quality', 'battery_level')\nputs res16.inspect # >>> [60, 60]\n# (your actual values may be slightly lower)","section_id":"field-expiration-examples"},{"id":"field-expiration-examples-ex10","language":"rust","code":"let _: () = r.del(\"sensor:sensor1\").await.expect(\"Failed to del\");\n        let _: () = r.hset_multiple(\n            \"sensor:sensor1\",\n            &[(\"air_quality\", \"256\"), (\"battery_level\", \"89\")],\n        ).await.expect(\"Failed to hset\");\n\n        // Set a TTL of 60 seconds on two fields of the hash.\n        match r.hexpire(\"sensor:sensor1\", 60, redis::ExpireOption::NONE, &[\"air_quality\", \"battery_level\"]).await {\n            Ok(res18) => {\n                let res18: Vec<i64> = res18;\n                println!(\"{:?}\", res18);    // >>> [1, 1]\n            },\n            Err(e) => {\n                println!(\"Error setting expiration: {e}\");\n                return;\n            }\n        }\n\n        // Retrieve the remaining TTL for those fields.\n        match r.httl(\"sensor:sensor1\", &[\"air_quality\", \"battery_level\"]).await {\n            Ok(res19) => {\n                let res19: Vec<i64> = res19;\n                println!(\"{}\", res19.len());    // >>> 2\n            },\n            Err(e) => {\n                println!(\"Error getting TTL: {e}\");\n                return;\n            }\n        }","section_id":"field-expiration-examples"},{"id":"field-expiration-examples-ex11","language":"rust","code":"let _: () = r.del(\"sensor:sensor1\").expect(\"Failed to del\");\n        let _: () = r.hset_multiple(\n            \"sensor:sensor1\",\n            &[(\"air_quality\", \"256\"), (\"battery_level\", \"89\")],\n        ).expect(\"Failed to hset\");\n\n        // Set a TTL of 60 seconds on two fields of the hash.\n        match r.hexpire(\"sensor:sensor1\", 60, redis::ExpireOption::NONE, &[\"air_quality\", \"battery_level\"]) {\n            Ok(res18) => {\n                let res18: Vec<i64> = res18;\n                println!(\"{:?}\", res18);    // >>> [1, 1]\n            },\n            Err(e) => {\n                println!(\"Error setting expiration: {e}\");\n                return;\n            }\n        }\n\n        // Retrieve the remaining TTL for those fields.\n        match r.httl(\"sensor:sensor1\", &[\"air_quality\", \"battery_level\"]) {\n            Ok(res19) => {\n                let res19: Vec<i64> = res19;\n                println!(\"{}\", res19.len());    // >>> 2\n            },\n            Err(e) => {\n                println!(\"Error getting TTL: {e}\");\n                return;\n            }\n        }","section_id":"field-expiration-examples"},{"id":"field-expiration-examples-ex12","language":"plaintext","code":"> DEL sensor:sensor1\n(integer) 1\n> HSET sensor:sensor1 air_quality 256 battery_level 89\n(integer) 2\n> HPEXPIRE sensor:sensor1 60000 FIELDS 1 air_quality\n1) (integer) 1\n> HPTTL sensor:sensor1 FIELDS 1 air_quality\n1) (integer) 59994","section_id":"field-expiration-examples"},{"id":"field-expiration-examples-ex13","language":"csharp","code":"db.KeyDelete(\"sensor:sensor1\");\n        db.HashSet(\"sensor:sensor1\", [\n            new(\"air_quality\", 256),\n            new(\"battery_level\", 89)\n        ]);\n\n        // Set the TTL of the 'air_quality' field, expressed in milliseconds.\n        ExpireResult[] hpexpireResult = db.HashFieldExpire(\n            \"sensor:sensor1\",\n            new RedisValue[] { \"air_quality\" },\n            TimeSpan.FromMilliseconds(60000)\n        );\n        Console.WriteLine(string.Join(\", \", hpexpireResult));\n        // Success\n\n        // Retrieve the remaining TTL.\n        long[] hpttlResult = db.HashFieldGetTimeToLive(\n            \"sensor:sensor1\",\n            new RedisValue[] { \"air_quality\" }\n        );\n        Console.WriteLine(hpttlResult.Length);\n        // 1","section_id":"field-expiration-examples"},{"id":"field-expiration-examples-ex14","language":"go","code":"rdb.Del(ctx, \"sensor:sensor1\")\n\trdb.HSet(ctx, \"sensor:sensor1\", \"air_quality\", 256, \"battery_level\", 89)\n\n\t// Set the TTL of the 'air_quality' field in milliseconds.\n\tres20, err := rdb.HPExpire(ctx, \"sensor:sensor1\", 60000*time.Millisecond,\n\t\t\"air_quality\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res20) // >>> [1]\n\n\t// Retrieve the remaining TTL in milliseconds (returns one value per field).\n\tres21, err := rdb.HPTTL(ctx, \"sensor:sensor1\", \"air_quality\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(len(res21)) // >>> 1","section_id":"field-expiration-examples"},{"id":"field-expiration-examples-ex15","language":"java","code":"CompletableFuture<Void> hpExpire = hExpire\n                    .thenCompose(prev -> asyncCommands.del(\"sensor:sensor1\"))\n                    .thenCompose(delRes -> asyncCommands.hset(\"sensor:sensor1\", sensor1))\n                    // Set the TTL of the 'air_quality' field in milliseconds.\n                    .thenCompose(hsetRes -> asyncCommands.hpexpire(\"sensor:sensor1\", 60000, \"air_quality\"))\n                    .thenCompose(res17 -> {\n                        System.out.println(res17); // >>> [1]\n                        // Retrieve the remaining TTL in milliseconds.\n                        return asyncCommands.hpttl(\"sensor:sensor1\", \"air_quality\");\n                    })\n                    .thenAccept(res18 -> System.out.println(res18.size()))\n                    // >>> 1\n                    .toCompletableFuture();","section_id":"field-expiration-examples"},{"id":"field-expiration-examples-ex16","language":"java","code":"// Set the TTL of the 'air_quality' field in milliseconds.\n            Mono<List<Long>> hpExpire = reactiveCommands.del(\"sensor:sensor1\")\n                    .then(reactiveCommands.hset(\"sensor:sensor1\", sensor1))\n                    .then(reactiveCommands.hpexpire(\"sensor:sensor1\", 60000, \"air_quality\").collectList())\n                    .doOnNext(result -> {\n                        System.out.println(result);\n                        // >>> [1]\n                    });\n\n            hpExpire.block();\n\n            // Retrieve the remaining TTL in milliseconds.\n            Mono<List<Long>> hpTtl = reactiveCommands.hpttl(\"sensor:sensor1\", \"air_quality\")\n                    .collectList().doOnNext(result -> {\n                        System.out.println(result.size());\n                        // >>> 1\n                    });\n\n            hpTtl.block();","section_id":"field-expiration-examples"},{"id":"field-expiration-examples-ex17","language":"java","code":"jedis.del(\"sensor:sensor1\");\n      jedis.hset(\"sensor:sensor1\", sensor1);\n\n      // Set the TTL of the 'air_quality' field in milliseconds.\n      List<Long> res17 = jedis.hpexpire(\"sensor:sensor1\", 60000, \"air_quality\");\n      System.out.println(res17); // [1]\n\n      // Retrieve the remaining TTL in milliseconds.\n      List<Long> res18 = jedis.hpttl(\"sensor:sensor1\", \"air_quality\");\n      System.out.println(res18.size()); // 1","section_id":"field-expiration-examples"},{"id":"field-expiration-examples-ex18","language":"javascript","code":"await client.del('sensor:sensor1')\nawait client.hSet('sensor:sensor1', { 'air_quality': 256, 'battery_level': 89 })\n\n// Set the TTL of the 'air_quality' field in milliseconds.\nconst res20 = await client.hpExpire('sensor:sensor1', ['air_quality'], 60000)\nconsole.log(res20)  // [1]\n\n// Retrieve the remaining TTL in milliseconds.\nconst res21 = await client.hpTTL('sensor:sensor1', ['air_quality'])\nconsole.log(res21)  // [59994] (your actual value may vary)","section_id":"field-expiration-examples"},{"id":"field-expiration-examples-ex19","language":"php","code":"$r->del('sensor:sensor1');\n        $r->hmset('sensor:sensor1', [\n            'air_quality' => 256,\n            'battery_level' => 89,\n        ]);\n\n        // Set the TTL of the 'air_quality' field in milliseconds.\n        $res17 = $r->hpexpire('sensor:sensor1', 60000, ['air_quality']);\n        echo json_encode($res17) . PHP_EOL;\n        // >>> [1]\n\n        // Retrieve the remaining TTL in milliseconds.\n        $res18 = $r->hpttl('sensor:sensor1', ['air_quality']);\n        echo count($res18) . PHP_EOL;\n        // >>> 1","section_id":"field-expiration-examples"},{"id":"field-expiration-examples-ex20","language":"python","code":"r.delete(\"sensor:sensor1\")\nr.hset(\"sensor:sensor1\", mapping={\"air_quality\": 256, \"battery_level\": 89})\n\n# Set the TTL of the 'air_quality' field in milliseconds.\nres20 = r.hpexpire(\"sensor:sensor1\", 60000, \"air_quality\")\nprint(res20)\n# >>> [1]\n\n# Retrieve the remaining TTL in milliseconds.\nres21 = r.hpttl(\"sensor:sensor1\", \"air_quality\")\nprint(res21)\n# >>> [59994]\n# (your actual value may vary)","section_id":"field-expiration-examples"},{"id":"field-expiration-examples-ex21","language":"ruby","code":"r.del('sensor:sensor1')\nr.hset('sensor:sensor1', { 'air_quality' => 256, 'battery_level' => 89 })\n\n# Set the TTL of the 'air_quality' field in milliseconds.\nres17 = r.hpexpire('sensor:sensor1', 60_000, 'air_quality')\nputs res17.inspect # >>> [1]\n\n# Retrieve the remaining TTL in milliseconds.\nres18 = r.hpttl('sensor:sensor1', 'air_quality')\nputs res18.inspect # >>> [59999]\n# (your actual value may vary)","section_id":"field-expiration-examples"},{"id":"field-expiration-examples-ex22","language":"rust","code":"let _: () = r.del(\"sensor:sensor1\").await.expect(\"Failed to del\");\n        let _: () = r.hset_multiple(\n            \"sensor:sensor1\",\n            &[(\"air_quality\", \"256\"), (\"battery_level\", \"89\")],\n        ).await.expect(\"Failed to hset\");\n\n        // Set the TTL of the 'air_quality' field in milliseconds.\n        match r.hpexpire(\"sensor:sensor1\", 60000, redis::ExpireOption::NONE, &[\"air_quality\"]).await {\n            Ok(res20) => {\n                let res20: Vec<i64> = res20;\n                println!(\"{:?}\", res20);    // >>> [1]\n            },\n            Err(e) => {\n                println!(\"Error setting expiration: {e}\");\n                return;\n            }\n        }\n\n        // Retrieve the remaining TTL in milliseconds.\n        match r.hpttl(\"sensor:sensor1\", &[\"air_quality\"]).await {\n            Ok(res21) => {\n                let res21: Vec<i64> = res21;\n                println!(\"{}\", res21.len());    // >>> 1\n            },\n            Err(e) => {\n                println!(\"Error getting PTTL: {e}\");\n                return;\n            }\n        }","section_id":"field-expiration-examples"},{"id":"field-expiration-examples-ex23","language":"rust","code":"let _: () = r.del(\"sensor:sensor1\").expect(\"Failed to del\");\n        let _: () = r.hset_multiple(\n            \"sensor:sensor1\",\n            &[(\"air_quality\", \"256\"), (\"battery_level\", \"89\")],\n        ).expect(\"Failed to hset\");\n\n        // Set the TTL of the 'air_quality' field in milliseconds.\n        match r.hpexpire(\"sensor:sensor1\", 60000, redis::ExpireOption::NONE, &[\"air_quality\"]) {\n            Ok(res20) => {\n                let res20: Vec<i64> = res20;\n                println!(\"{:?}\", res20);    // >>> [1]\n            },\n            Err(e) => {\n                println!(\"Error setting expiration: {e}\");\n                return;\n            }\n        }\n\n        // Retrieve the remaining TTL in milliseconds.\n        match r.hpttl(\"sensor:sensor1\", &[\"air_quality\"]) {\n            Ok(res21) => {\n                let res21: Vec<i64> = res21;\n                println!(\"{}\", res21.len());    // >>> 1\n            },\n            Err(e) => {\n                println!(\"Error getting PTTL: {e}\");\n                return;\n            }\n        }","section_id":"field-expiration-examples"},{"id":"field-expiration-examples-ex24","language":"plaintext","code":"> DEL sensor:sensor1\n(integer) 1\n> HSET sensor:sensor1 air_quality 256 battery_level 89\n(integer) 2\n# Set the expiration to a Unix time in the future\n# (4102444800 is just an example; use a timestamp appropriate for your use case).\n> HEXPIREAT sensor:sensor1 4102444800 FIELDS 1 air_quality\n1) (integer) 1\n> HEXPIRETIME sensor:sensor1 FIELDS 1 air_quality\n1) (integer) 4102444800","section_id":"field-expiration-examples"},{"id":"field-expiration-examples-ex25","language":"csharp","code":"db.KeyDelete(\"sensor:sensor1\");\n        db.HashSet(\"sensor:sensor1\", [\n            new(\"air_quality\", 256),\n            new(\"battery_level\", 89)\n        ]);\n\n        // Set the expiration of 'air_quality' to a time 24 hours from now.\n        ExpireResult[] hexpireAtResult = db.HashFieldExpire(\n            \"sensor:sensor1\",\n            new RedisValue[] { \"air_quality\" },\n            DateTime.UtcNow.AddHours(24)\n        );\n        Console.WriteLine(string.Join(\", \", hexpireAtResult));\n        // Success\n\n        // Retrieve the expiration time as a Unix timestamp.\n        long[] hexpireTimeResult = db.HashFieldGetExpireDateTime(\n            \"sensor:sensor1\",\n            new RedisValue[] { \"air_quality\" }\n        );\n        Console.WriteLine(hexpireTimeResult.Length);\n        // 1","section_id":"field-expiration-examples"},{"id":"field-expiration-examples-ex26","language":"go","code":"rdb.Del(ctx, \"sensor:sensor1\")\n\trdb.HSet(ctx, \"sensor:sensor1\", \"air_quality\", 256, \"battery_level\", 89)\n\n\t// Set the expiration of 'air_quality' to a time 24 hours from now.\n\tres22, err := rdb.HExpireAt(ctx, \"sensor:sensor1\", time.Now().Add(24*time.Hour),\n\t\t\"air_quality\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res22) // >>> [1]\n\n\t// Retrieve the expiration time as a Unix timestamp (returns one value per field).\n\tres23, err := rdb.HExpireTime(ctx, \"sensor:sensor1\", \"air_quality\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(len(res23)) // >>> 1","section_id":"field-expiration-examples"},{"id":"field-expiration-examples-ex27","language":"java","code":"long expireAtSeconds = System.currentTimeMillis() / 1000L + 24 * 60 * 60;\n            CompletableFuture<Void> hExpireAt = hpExpire\n                    .thenCompose(prev -> asyncCommands.del(\"sensor:sensor1\"))\n                    .thenCompose(delRes -> asyncCommands.hset(\"sensor:sensor1\", sensor1))\n                    // Set the expiration of 'air_quality' to a Unix time 24 hours from now.\n                    .thenCompose(hsetRes -> asyncCommands.hexpireat(\"sensor:sensor1\", expireAtSeconds, \"air_quality\"))\n                    .thenCompose(res19 -> {\n                        System.out.println(res19); // >>> [1]\n                        // Retrieve the expiration time as a Unix timestamp in seconds.\n                        return asyncCommands.hexpiretime(\"sensor:sensor1\", \"air_quality\");\n                    })\n                    .thenAccept(res20 -> System.out.println(res20.size()))\n                    // >>> 1\n                    .toCompletableFuture();","section_id":"field-expiration-examples"},{"id":"field-expiration-examples-ex28","language":"java","code":"long expireAtSeconds = System.currentTimeMillis() / 1000L + 24 * 60 * 60;\n\n            // Set the expiration of 'air_quality' to a Unix time 24 hours from now.\n            Mono<List<Long>> hExpireAt = reactiveCommands.del(\"sensor:sensor1\")\n                    .then(reactiveCommands.hset(\"sensor:sensor1\", sensor1))\n                    .then(reactiveCommands.hexpireat(\"sensor:sensor1\", expireAtSeconds, \"air_quality\").collectList())\n                    .doOnNext(result -> {\n                        System.out.println(result);\n                        // >>> [1]\n                    });\n\n            hExpireAt.block();\n\n            // Retrieve the expiration time as a Unix timestamp in seconds.\n            Mono<List<Long>> hExpireTime = reactiveCommands.hexpiretime(\"sensor:sensor1\", \"air_quality\")\n                    .collectList().doOnNext(result -> {\n                        System.out.println(result.size());\n                        // >>> 1\n                    });\n\n            hExpireTime.block();","section_id":"field-expiration-examples"},{"id":"field-expiration-examples-ex29","language":"java","code":"jedis.del(\"sensor:sensor1\");\n      jedis.hset(\"sensor:sensor1\", sensor1);\n\n      // Set the expiration of 'air_quality' to a Unix time 24 hours from now.\n      long expireAt = System.currentTimeMillis() / 1000L + 24 * 60 * 60;\n      List<Long> res19 = jedis.hexpireAt(\"sensor:sensor1\", expireAt, \"air_quality\");\n      System.out.println(res19); // [1]\n\n      // Retrieve the expiration time as a Unix timestamp in seconds.\n      List<Long> res20 = jedis.hexpireTime(\"sensor:sensor1\", \"air_quality\");\n      System.out.println(res20.size()); // 1","section_id":"field-expiration-examples"},{"id":"field-expiration-examples-ex30","language":"javascript","code":"await client.del('sensor:sensor1')\nawait client.hSet('sensor:sensor1', { 'air_quality': 256, 'battery_level': 89 })\n\n// Set the expiration of 'air_quality' to a Unix time 24 hours from now.\nconst expireAt = Math.floor(Date.now() / 1000) + 24 * 60 * 60\nconst res22 = await client.hExpireAt('sensor:sensor1', ['air_quality'], expireAt)\nconsole.log(res22)  // [1]\n\n// Retrieve the expiration time as a Unix timestamp in seconds.\nconst res23 = await client.hExpireTime('sensor:sensor1', ['air_quality'])\nconsole.log(res23)  // [1717668041] (your actual value will vary)","section_id":"field-expiration-examples"},{"id":"field-expiration-examples-ex31","language":"php","code":"$r->del('sensor:sensor1');\n        $r->hmset('sensor:sensor1', [\n            'air_quality' => 256,\n            'battery_level' => 89,\n        ]);\n\n        // Set the expiration of 'air_quality' to a Unix time 24 hours from now.\n        $res19 = $r->hexpireat('sensor:sensor1', time() + 24 * 60 * 60, ['air_quality']);\n        echo json_encode($res19) . PHP_EOL;\n        // >>> [1]\n\n        // Retrieve the expiration time as a Unix timestamp in seconds.\n        $res20 = $r->hexpiretime('sensor:sensor1', ['air_quality']);\n        echo count($res20) . PHP_EOL;\n        // >>> 1","section_id":"field-expiration-examples"},{"id":"field-expiration-examples-ex32","language":"python","code":"r.delete(\"sensor:sensor1\")\nr.hset(\"sensor:sensor1\", mapping={\"air_quality\": 256, \"battery_level\": 89})\n\n# Set the expiration of 'air_quality' to a Unix time 24 hours from now.\nres22 = r.hexpireat(\n    \"sensor:sensor1\",\n    int(time.time()) + 24 * 60 * 60,\n    \"air_quality\",\n)\nprint(res22)\n# >>> [1]\n\n# Retrieve the expiration time as a Unix timestamp in seconds.\nres23 = r.hexpiretime(\"sensor:sensor1\", \"air_quality\")\nprint(res23)\n# >>> [1717668041]\n# (your actual value will vary)","section_id":"field-expiration-examples"},{"id":"field-expiration-examples-ex33","language":"rust","code":"let _: () = r.del(\"sensor:sensor1\").await.expect(\"Failed to del\");\n        let _: () = r.hset_multiple(\n            \"sensor:sensor1\",\n            &[(\"air_quality\", \"256\"), (\"battery_level\", \"89\")],\n        ).await.expect(\"Failed to hset\");\n\n        // Set the expiration of 'air_quality' to a Unix time 24 hours from now.\n        let now = std::time::SystemTime::now()\n            .duration_since(std::time::UNIX_EPOCH)\n            .expect(\"Time went backwards\")\n            .as_secs() as i64;\n\n        match r.hexpire_at(\"sensor:sensor1\", now + 24 * 60 * 60, redis::ExpireOption::NONE, &[\"air_quality\"]).await {\n            Ok(res22) => {\n                let res22: Vec<i64> = res22;\n                println!(\"{:?}\", res22);    // >>> [1]\n            },\n            Err(e) => {\n                println!(\"Error setting expiration: {e}\");\n                return;\n            }\n        }\n\n        // Retrieve the expiration time as a Unix timestamp in seconds.\n        match r.hexpire_time(\"sensor:sensor1\", &[\"air_quality\"]).await {\n            Ok(res23) => {\n                let res23: Vec<i64> = res23;\n                println!(\"{}\", res23.len());    // >>> 1\n            },\n            Err(e) => {\n                println!(\"Error getting expiration time: {e}\");\n                return;\n            }\n        }","section_id":"field-expiration-examples"},{"id":"field-expiration-examples-ex34","language":"rust","code":"let _: () = r.del(\"sensor:sensor1\").expect(\"Failed to del\");\n        let _: () = r.hset_multiple(\n            \"sensor:sensor1\",\n            &[(\"air_quality\", \"256\"), (\"battery_level\", \"89\")],\n        ).expect(\"Failed to hset\");\n\n        // Set the expiration of 'air_quality' to a Unix time 24 hours from now.\n        let now = std::time::SystemTime::now()\n            .duration_since(std::time::UNIX_EPOCH)\n            .expect(\"Time went backwards\")\n            .as_secs() as i64;\n\n        match r.hexpire_at(\"sensor:sensor1\", now + 24 * 60 * 60, redis::ExpireOption::NONE, &[\"air_quality\"]) {\n            Ok(res22) => {\n                let res22: Vec<i64> = res22;\n                println!(\"{:?}\", res22);    // >>> [1]\n            },\n            Err(e) => {\n                println!(\"Error setting expiration: {e}\");\n                return;\n            }\n        }\n\n        // Retrieve the expiration time as a Unix timestamp in seconds.\n        match r.hexpire_time(\"sensor:sensor1\", &[\"air_quality\"]) {\n            Ok(res23) => {\n                let res23: Vec<i64> = res23;\n                println!(\"{}\", res23.len());    // >>> 1\n            },\n            Err(e) => {\n                println!(\"Error getting expiration time: {e}\");\n                return;\n            }\n        }","section_id":"field-expiration-examples"},{"id":"bulk-import-with-himport-ex0","language":"plaintext","code":"> HIMPORT PREPARE user name email age\nOK\n> HIMPORT SET user:1 user Alice alice@example.com 34\nOK\n> HIMPORT SET user:2 user Bob bob@example.com 41\nOK\n> HIMPORT SET user:3 user Carol carol@example.com 29\nOK\n> HGETALL user:2\n1) \"age\"\n2) \"41\"\n3) \"name\"\n4) \"Bob\"\n5) \"email\"\n6) \"bob@example.com\"","section_id":"bulk-import-with-himport"},{"id":"bulk-import-with-himport-ex1","language":"csharp","code":"// Declare the field names once.\n        await using var fields = HashImport.Create(\"name\", \"email\", \"age\");\n\n        // Create each hash by sending only its values.\n        await db.HashImportAsync(\"user:1\", fields, new RedisValue[] { \"Alice\", \"alice@example.com\", \"34\" });\n        await db.HashImportAsync(\"user:2\", fields, new RedisValue[] { \"Bob\", \"bob@example.com\", \"41\" });\n        await db.HashImportAsync(\"user:3\", fields, new RedisValue[] { \"Carol\", \"carol@example.com\", \"29\" });\n\n        // The result is an ordinary hash.\n        HashEntry[] res1 = await db.HashGetAllAsync(\"user:2\");\n        Console.WriteLine(string.Join(\", \", res1.Select(h => $\"{h.Name}: {h.Value}\")));\n        // >>> age: 41, name: Bob, email: bob@example.com","section_id":"bulk-import-with-himport"},{"id":"bulk-import-with-himport-ex2","language":"csharp","code":"// Declare the field names once.\n        using var fields = HashImport.Create(\"name\", \"email\", \"age\");\n\n        // Create each hash by sending only its values.\n        db.HashImport(\"user:1\", fields, new RedisValue[] { \"Alice\", \"alice@example.com\", \"34\" });\n        db.HashImport(\"user:2\", fields, new RedisValue[] { \"Bob\", \"bob@example.com\", \"41\" });\n        db.HashImport(\"user:3\", fields, new RedisValue[] { \"Carol\", \"carol@example.com\", \"29\" });\n\n        // The result is an ordinary hash.\n        HashEntry[] res1 = db.HashGetAll(\"user:2\");\n        Console.WriteLine(string.Join(\", \", res1.Select(h => $\"{h.Name}: {h.Value}\")));\n        // >>> age: 41, name: Bob, email: bob@example.com","section_id":"bulk-import-with-himport"},{"id":"bulk-import-with-himport-ex3","language":"csharp","code":"// Declare the field names once.\n        await using var fields = HashImport.Create(\"name\", \"email\", \"age\");\n\n        // Create each hash by sending only its values.\n        await db.HashImportAsync(\"user:1\", fields, new RedisValue[] { \"Alice\", \"alice@example.com\", \"34\" });\n        await db.HashImportAsync(\"user:2\", fields, new RedisValue[] { \"Bob\", \"bob@example.com\", \"41\" });\n        await db.HashImportAsync(\"user:3\", fields, new RedisValue[] { \"Carol\", \"carol@example.com\", \"29\" });\n\n        // The result is an ordinary hash.\n        HashEntry[] res1 = await db.HashGetAllAsync(\"user:2\");\n        Console.WriteLine(string.Join(\", \", res1.Select(h => $\"{h.Name}: {h.Value}\")));\n        // >>> age: 41, name: Bob, email: bob@example.com","section_id":"bulk-import-with-himport"},{"id":"bulk-import-with-himport-ex4","language":"csharp","code":"// Declare the field names once.\n        using var fields = HashImport.Create(\"name\", \"email\", \"age\");\n\n        // Create each hash by sending only its values.\n        db.HashImport(\"user:1\", fields, new RedisValue[] { \"Alice\", \"alice@example.com\", \"34\" });\n        db.HashImport(\"user:2\", fields, new RedisValue[] { \"Bob\", \"bob@example.com\", \"41\" });\n        db.HashImport(\"user:3\", fields, new RedisValue[] { \"Carol\", \"carol@example.com\", \"29\" });\n\n        // The result is an ordinary hash.\n        HashEntry[] res1 = db.HashGetAll(\"user:2\");\n        Console.WriteLine(string.Join(\", \", res1.Select(h => $\"{h.Name}: {h.Value}\")));\n        // >>> age: 41, name: Bob, email: bob@example.com","section_id":"bulk-import-with-himport"},{"id":"bulk-import-with-himport-ex5","language":"go","code":"// Declare the field names once.\n\tif err := rdb.HImportPrepare(ctx, \"user\", \"name\", \"email\", \"age\").Err(); err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Create each hash by sending only its values.\n\tres1, err := rdb.HImportSet(ctx, \"user:1\", \"user\", \"Alice\", \"alice@example.com\", \"34\").Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tfmt.Println(res1) // >>> OK\n\n\tif err := rdb.HImportSet(ctx, \"user:2\", \"user\", \"Bob\", \"bob@example.com\", \"41\").Err(); err != nil {\n\t\tpanic(err)\n\t}\n\tif err := rdb.HImportSet(ctx, \"user:3\", \"user\", \"Carol\", \"carol@example.com\", \"29\").Err(); err != nil {\n\t\tpanic(err)\n\t}\n\n\t// The result is an ordinary hash.\n\tres2, err := rdb.HGetAll(ctx, \"user:2\").Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tfmt.Println(res2) // >>> map[age:41 email:bob@example.com name:Bob]","section_id":"bulk-import-with-himport"},{"id":"bulk-import-with-himport-ex6","language":"java","code":"// Declare the field names once.\n            try (HashImport<String> fieldset = HashImport.of(\"name\", \"email\", \"age\")) {\n                // Create each hash by sending only its values.\n                String res1 = syncCommands.himportSet(\"user:1\", fieldset, \"Alice\", \"alice@example.com\", \"34\");\n                System.out.println(res1); // >>> OK\n\n                syncCommands.himportSet(\"user:2\", fieldset, \"Bob\", \"bob@example.com\", \"41\");\n                syncCommands.himportSet(\"user:3\", fieldset, \"Carol\", \"carol@example.com\", \"29\");\n            }\n\n            // The result is an ordinary hash.\n            Map<String, String> res2 = syncCommands.hgetall(\"user:2\");\n            System.out.println(res2);\n            // >>> {age=41, name=Bob, email=bob@example.com}","section_id":"bulk-import-with-himport"},{"id":"bulk-import-with-himport-ex7","language":"java","code":"CompletableFuture<Void> basicOps;\n\n            // Declare the field names once.\n            try (HashImport<String> fieldset = HashImport.of(\"name\", \"email\", \"age\")) {\n                // Create each hash by sending only its values.\n                RedisFuture<String> import1 = asyncCommands.himportSet(\n                        \"user:1\", fieldset, \"Alice\", \"alice@example.com\", \"34\");\n                asyncCommands.himportSet(\"user:2\", fieldset, \"Bob\", \"bob@example.com\", \"41\");\n                asyncCommands.himportSet(\"user:3\", fieldset, \"Carol\", \"carol@example.com\", \"29\");\n\n                basicOps = import1.thenCompose(res1 -> {\n                    System.out.println(res1); // >>> OK\n                    // The result is an ordinary hash.\n                    return asyncCommands.hgetall(\"user:2\");\n                }).thenAccept(res2 -> {\n                    System.out.println(res2);\n                    // >>> {age=41, name=Bob, email=bob@example.com}\n                }).toCompletableFuture();\n            }\n\n            basicOps.join();","section_id":"bulk-import-with-himport"},{"id":"bulk-import-with-himport-ex8","language":"java","code":"// Declare the field names once.\n            try (HashImport<String> fieldset = HashImport.of(\"name\", \"email\", \"age\")) {\n                // Create each hash by sending only its values.\n                Mono<String> import1 = reactiveCommands\n                    .himportSet(\"user:1\", fieldset, \"Alice\", \"alice@example.com\", \"34\")\n                    .doOnNext(res1 -> {\n                        System.out.println(res1); // >>> OK\n                    });\n\n                Mono<String> import2 = reactiveCommands\n                    .himportSet(\"user:2\", fieldset, \"Bob\", \"bob@example.com\", \"41\");\n                Mono<String> import3 = reactiveCommands\n                    .himportSet(\"user:3\", fieldset, \"Carol\", \"carol@example.com\", \"29\");\n\n                // Subscribe before the fieldset closes.\n                Mono.when(import1, import2, import3).block();\n            }\n\n            // The result is an ordinary hash.\n            Mono<List<KeyValue<String, String>>> getAll = reactiveCommands.hgetall(\"user:2\")\n                .collectList()\n                .doOnNext(res2 -> {\n                    System.out.println(res2);\n                    // >>> [KeyValue[age, 41], KeyValue[name, Bob], KeyValue[email, bob@example.com]]\n                });\n\n            getAll.block();","section_id":"bulk-import-with-himport"},{"id":"bulk-import-with-himport-ex9","language":"java","code":"// Declare the field names once.\n        try (HashImport fields = HashImport.of(\"name\", \"email\", \"age\")) {\n            // Create each hash by sending only its values.\n            String res1 = jedis.himportSet(\"user:1\", fields, \"Alice\", \"alice@example.com\", \"34\");\n            System.out.println(res1); // >>> OK\n\n            jedis.himportSet(\"user:2\", fields, \"Bob\", \"bob@example.com\", \"41\");\n            jedis.himportSet(\"user:3\", fields, \"Carol\", \"carol@example.com\", \"29\");\n        }\n\n        // The result is an ordinary hash.\n        Map<String, String> res2 = jedis.hgetAll(\"user:2\");\n        System.out.println(res2);\n        // >>> {name=Bob, age=41, email=bob@example.com}","section_id":"bulk-import-with-himport"},{"id":"bulk-import-with-himport-ex10","language":"javascript","code":"// Declare the field names once.\nawait client.hImportPrepare('user', ['name', 'email', 'age']);\n\n// Create each hash by sending only its values.\nconst res1 = await client.hImportSet('user:1', 'user', ['Alice', 'alice@example.com', '34']);\nconsole.log(res1); // >>> OK\n\nawait client.hImportSet('user:2', 'user', ['Bob', 'bob@example.com', '41']);\nawait client.hImportSet('user:3', 'user', ['Carol', 'carol@example.com', '29']);\n\n// The result is an ordinary hash.\nconst res2 = await client.hGetAll('user:2');\nconsole.log(res2);\n// >>> { age: '41', name: 'Bob', email: 'bob@example.com' }","section_id":"bulk-import-with-himport"},{"id":"bulk-import-with-himport-ex11","language":"javascript","code":"// Declare the field names once, when you create the client.\nconst redis = new Redis({\n  himportFieldsets: [{ name: 'user', fields: ['name', 'email', 'age'] }],\n});\n\n// Create each hash by sending only its values.\nconst res1 = await redis.himport('SET', 'user:1', 'user', 'Alice', 'alice@example.com', '34');\nconsole.log(res1); // >>> OK\n\nawait redis.himport('SET', 'user:2', 'user', 'Bob', 'bob@example.com', '41');\nawait redis.himport('SET', 'user:3', 'user', 'Carol', 'carol@example.com', '29');\n\n// The result is an ordinary hash.\nconst res2 = await redis.hgetall('user:2');\nconsole.log(res2);\n// >>> { age: '41', name: 'Bob', email: 'bob@example.com' }","section_id":"bulk-import-with-himport"},{"id":"bulk-import-with-himport-ex12","language":"javascript","code":"// Declare the field names once.\nawait client.hImportPrepare('user', ['name', 'email', 'age']);\n\n// Create each hash by sending only its values.\nconst res1 = await client.hImportSet('user:1', 'user', ['Alice', 'alice@example.com', '34']);\nconsole.log(res1); // >>> OK\n\nawait client.hImportSet('user:2', 'user', ['Bob', 'bob@example.com', '41']);\nawait client.hImportSet('user:3', 'user', ['Carol', 'carol@example.com', '29']);\n\n// The result is an ordinary hash.\nconst res2 = await client.hGetAll('user:2');\nconsole.log(res2);\n// >>> { age: '41', name: 'Bob', email: 'bob@example.com' }","section_id":"bulk-import-with-himport"},{"id":"bulk-import-with-himport-ex13","language":"javascript","code":"// Declare the field names once, when you create the client.\nconst redis = new Redis({\n  himportFieldsets: [{ name: 'user', fields: ['name', 'email', 'age'] }],\n});\n\n// Create each hash by sending only its values.\nconst res1 = await redis.himport('SET', 'user:1', 'user', 'Alice', 'alice@example.com', '34');\nconsole.log(res1); // >>> OK\n\nawait redis.himport('SET', 'user:2', 'user', 'Bob', 'bob@example.com', '41');\nawait redis.himport('SET', 'user:3', 'user', 'Carol', 'carol@example.com', '29');\n\n// The result is an ordinary hash.\nconst res2 = await redis.hgetall('user:2');\nconsole.log(res2);\n// >>> { age: '41', name: 'Bob', email: 'bob@example.com' }","section_id":"bulk-import-with-himport"},{"id":"bulk-import-with-himport-ex14","language":"php","code":"// Declare the field names once.\n        $r->himport->prepare('user', ['name', 'email', 'age']);\n\n        // Create each hash by sending only its values.\n        $res1 = $r->himport->set('user:1', 'user', ['Alice', 'alice@example.com', '34']);\n        echo $res1 . PHP_EOL; // >>> OK\n\n        $r->himport->set('user:2', 'user', ['Bob', 'bob@example.com', '41']);\n        $r->himport->set('user:3', 'user', ['Carol', 'carol@example.com', '29']);\n\n        // The result is an ordinary hash.\n        $res2 = $r->hgetall('user:2');\n        echo json_encode($res2) . PHP_EOL;\n        // >>> {\"age\":\"41\",\"name\":\"Bob\",\"email\":\"bob@example.com\"}","section_id":"bulk-import-with-himport"},{"id":"bulk-import-with-himport-ex15","language":"python","code":"# Declare the field names once.\nr.himport_prepare(\"user\", [\"name\", \"email\", \"age\"])\n\n# Create each hash by sending only its values.\nres1 = r.himport_set(\"user:1\", \"user\", [\"Alice\", \"alice@example.com\", \"34\"])\nprint(res1)\n# >>> True\n\nr.himport_set(\"user:2\", \"user\", [\"Bob\", \"bob@example.com\", \"41\"])\nr.himport_set(\"user:3\", \"user\", [\"Carol\", \"carol@example.com\", \"29\"])\n\n# The result is an ordinary hash.\nres2 = r.hgetall(\"user:2\")\nprint(res2)\n# >>> {'age': '41', 'name': 'Bob', 'email': 'bob@example.com'}","section_id":"bulk-import-with-himport"},{"id":"bulk-import-with-himport-ex16","language":"ruby","code":"# Declare the field names once.\nr.himport_prepare('user', %w[name email age])\n\n# Create each hash by sending only its values.\nres1 = r.himport_set('user:1', 'user', ['Alice', 'alice@example.com', '34'])\nputs res1 # >>> OK\n\nr.himport_set('user:2', 'user', ['Bob', 'bob@example.com', '41'])\nr.himport_set('user:3', 'user', ['Carol', 'carol@example.com', '29'])\n\n# The result is an ordinary hash.\nres2 = r.hgetall('user:2')\nputs res2.inspect\n# >>> {\"age\" => \"41\", \"name\" => \"Bob\", \"email\" => \"bob@example.com\"}","section_id":"bulk-import-with-himport"},{"id":"bulk-import-with-himport-ex17","language":"plaintext","code":"> HIMPORT PREPARE user name email age\nOK\n> HIMPORT SET user:1000 user user1000 user1000@example.com 20\nOK\n> HGET user:1000 email\n\"user1000@example.com\"","section_id":"bulk-import-with-himport"},{"id":"bulk-import-with-himport-ex18","language":"csharp","code":"await using var bulkFields = HashImport.Create(\"name\", \"email\", \"age\");\n\n        // Start many imports without awaiting each one, so they share round trips.\n        var imports = new List<Task>();\n\n        for (int i = 1; i <= 1000; i++)\n        {\n            imports.Add(db.HashImportAsync(\n                $\"user:{i}\",\n                bulkFields,\n                new RedisValue[] { $\"user{i}\", $\"user{i}@example.com\", (20 + i % 50).ToString() }\n            ));\n        }\n\n        await Task.WhenAll(imports);\n\n        int res2 = imports.Count(t => t.Status == TaskStatus.RanToCompletion);\n        Console.WriteLine(res2);    // >>> 1000\n\n        RedisValue res3 = await db.HashGetAsync(\"user:1000\", \"email\");\n        Console.WriteLine(res3);    // >>> user1000@example.com","section_id":"bulk-import-with-himport"},{"id":"bulk-import-with-himport-ex19","language":"csharp","code":"using var bulkFields = HashImport.Create(\"name\", \"email\", \"age\");\n\n        // Queue many imports and send them in one round trip.\n        var batch = db.CreateBatch();\n        var imports = new List<Task>();\n\n        for (int i = 1; i <= 1000; i++)\n        {\n            imports.Add(batch.HashImportAsync(\n                $\"user:{i}\",\n                bulkFields,\n                new RedisValue[] { $\"user{i}\", $\"user{i}@example.com\", (20 + i % 50).ToString() }\n            ));\n        }\n\n        batch.Execute();\n        Task.WaitAll(imports.ToArray());\n\n        int res2 = imports.Count(t => t.Status == TaskStatus.RanToCompletion);\n        Console.WriteLine(res2);    // >>> 1000\n\n        RedisValue res3 = db.HashGet(\"user:1000\", \"email\");\n        Console.WriteLine(res3);    // >>> user1000@example.com","section_id":"bulk-import-with-himport"},{"id":"bulk-import-with-himport-ex20","language":"csharp","code":"await using var bulkFields = HashImport.Create(\"name\", \"email\", \"age\");\n\n        // Start many imports without awaiting each one, so they share round trips.\n        var imports = new List<Task>();\n\n        for (int i = 1; i <= 1000; i++)\n        {\n            imports.Add(db.HashImportAsync(\n                $\"user:{i}\",\n                bulkFields,\n                new RedisValue[] { $\"user{i}\", $\"user{i}@example.com\", (20 + i % 50).ToString() }\n            ));\n        }\n\n        await Task.WhenAll(imports);\n\n        int res2 = imports.Count(t => t.Status == TaskStatus.RanToCompletion);\n        Console.WriteLine(res2);    // >>> 1000\n\n        RedisValue res3 = await db.HashGetAsync(\"user:1000\", \"email\");\n        Console.WriteLine(res3);    // >>> user1000@example.com","section_id":"bulk-import-with-himport"},{"id":"bulk-import-with-himport-ex21","language":"csharp","code":"using var bulkFields = HashImport.Create(\"name\", \"email\", \"age\");\n\n        // Queue many imports and send them in one round trip.\n        var batch = db.CreateBatch();\n        var imports = new List<Task>();\n\n        for (int i = 1; i <= 1000; i++)\n        {\n            imports.Add(batch.HashImportAsync(\n                $\"user:{i}\",\n                bulkFields,\n                new RedisValue[] { $\"user{i}\", $\"user{i}@example.com\", (20 + i % 50).ToString() }\n            ));\n        }\n\n        batch.Execute();\n        Task.WaitAll(imports.ToArray());\n\n        int res2 = imports.Count(t => t.Status == TaskStatus.RanToCompletion);\n        Console.WriteLine(res2);    // >>> 1000\n\n        RedisValue res3 = db.HashGet(\"user:1000\", \"email\");\n        Console.WriteLine(res3);    // >>> user1000@example.com","section_id":"bulk-import-with-himport"},{"id":"bulk-import-with-himport-ex22","language":"go","code":"if err := rdb.HImportPrepare(ctx, \"user\", \"name\", \"email\", \"age\").Err(); err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Queue many imports and send them in one round trip.\n\tpipe := rdb.Pipeline()\n\tfor i := 1; i <= 1000; i++ {\n\t\tpipe.HImportSet(ctx, fmt.Sprintf(\"user:%d\", i), \"user\",\n\t\t\tfmt.Sprintf(\"user%d\", i), fmt.Sprintf(\"user%d@example.com\", i), fmt.Sprint(20+i%50))\n\t}\n\tres3, err := pipe.Exec(ctx)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tallOK := true\n\tfor _, cmd := range res3 {\n\t\tif cmd.Err() != nil {\n\t\t\tallOK = false\n\t\t}\n\t}\n\tfmt.Println(len(res3), allOK) // >>> 1000 true\n\n\tres4, err := rdb.HGet(ctx, \"user:1000\", \"email\").Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tfmt.Println(res4) // >>> user1000@example.com","section_id":"bulk-import-with-himport"},{"id":"bulk-import-with-himport-ex23","language":"java","code":"List<String> res3 = new ArrayList<>();\n\n            // The sync API waits for each reply, so these imports aren't pipelined.\n            // To pipeline them, use the async or reactive API.\n            try (HashImport<String> fieldset = HashImport.of(\"name\", \"email\", \"age\")) {\n                for (int i = 1; i <= 1000; i++) {\n                    res3.add(syncCommands.himportSet(\"user:\" + i, fieldset,\n                            \"user\" + i, \"user\" + i + \"@example.com\", String.valueOf(20 + i % 50)));\n                }\n            }\n\n            boolean allOk = res3.stream().allMatch(\"OK\"::equals);\n            System.out.println(res3.size() + \" \" + allOk); // >>> 1000 true\n\n            String res4 = syncCommands.hget(\"user:1000\", \"email\");\n            System.out.println(res4); // >>> user1000@example.com","section_id":"bulk-import-with-himport"},{"id":"bulk-import-with-himport-ex24","language":"java","code":"List<RedisFuture<String>> res3 = new ArrayList<>();\n\n            // Issue many imports without waiting for each reply.\n            try (HashImport<String> fieldset = HashImport.of(\"name\", \"email\", \"age\")) {\n                for (int i = 1; i <= 1000; i++) {\n                    res3.add(asyncCommands.himportSet(\"user:\" + i, fieldset,\n                            \"user\" + i, \"user\" + i + \"@example.com\", String.valueOf(20 + i % 50)));\n                }\n            }\n\n            LettuceFutures.awaitAll(5, TimeUnit.SECONDS, res3.toArray(new RedisFuture[0]));\n\n            boolean allOk = res3.stream().allMatch(f -> \"OK\".equals(f.toCompletableFuture().join()));\n            System.out.println(res3.size() + \" \" + allOk); // >>> 1000 true\n\n            CompletableFuture<Void> getEmail = asyncCommands.hget(\"user:1000\", \"email\")\n                .thenAccept(res4 -> {\n                    System.out.println(res4); // >>> user1000@example.com\n                })\n                .toCompletableFuture();\n\n            getEmail.join();","section_id":"bulk-import-with-himport"},{"id":"bulk-import-with-himport-ex25","language":"java","code":"List<String> res3;\n\n            // Issue many imports without waiting for each reply.\n            try (HashImport<String> fieldset = HashImport.of(\"name\", \"email\", \"age\")) {\n                res3 = Flux.range(1, 1000)\n                    .flatMap(i -> reactiveCommands.himportSet(\"user:\" + i, fieldset,\n                            \"user\" + i, \"user\" + i + \"@example.com\", String.valueOf(20 + i % 50)))\n                    .collectList()\n                    .block();\n            }\n\n            boolean allOk = res3.stream().allMatch(\"OK\"::equals);\n            System.out.println(res3.size() + \" \" + allOk); // >>> 1000 true\n\n            Mono<String> getEmail = reactiveCommands.hget(\"user:1000\", \"email\").doOnNext(res4 -> {\n                System.out.println(res4); // >>> user1000@example.com\n            });\n\n            getEmail.block();","section_id":"bulk-import-with-himport"},{"id":"bulk-import-with-himport-ex26","language":"java","code":"List<Response<String>> res3 = new ArrayList<>();\n\n        // Queue many imports and send them in one round trip.\n        try (HashImport fields = HashImport.of(\"name\", \"email\", \"age\");\n                Pipeline pipeline = jedis.pipelined()) {\n            for (int i = 1; i <= 1000; i++) {\n                res3.add(pipeline.himportSet(\"user:\" + i, fields,\n                        \"user\" + i, \"user\" + i + \"@example.com\", String.valueOf(20 + i % 50)));\n            }\n            pipeline.sync();\n        }\n\n        boolean allOk = res3.stream().allMatch(r -> \"OK\".equals(r.get()));\n        System.out.println(res3.size() + \" \" + allOk); // >>> 1000 true\n\n        String res4 = jedis.hget(\"user:1000\", \"email\");\n        System.out.println(res4); // >>> user1000@example.com","section_id":"bulk-import-with-himport"},{"id":"bulk-import-with-himport-ex27","language":"javascript","code":"await client.hImportPrepare('user', ['name', 'email', 'age']);\n\n// Commands issued together are automatically pipelined.\nconst imports = [];\nfor (let i = 1; i <= 1000; i++) {\n  imports.push(\n    client.hImportSet(`user:${i}`, 'user', [`user${i}`, `user${i}@example.com`, String(20 + i % 50)])\n  );\n}\nconst res3 = await Promise.all(imports);\n\nconsole.log(res3.length, res3.every((r) => r === 'OK'));\n// >>> 1000 true\n\nconst res4 = await client.hGet('user:1000', 'email');\nconsole.log(res4); // >>> user1000@example.com","section_id":"bulk-import-with-himport"},{"id":"bulk-import-with-himport-ex28","language":"javascript","code":"const redis = new Redis({\n  himportFieldsets: [{ name: 'user', fields: ['name', 'email', 'age'] }],\n});\n\n// Queue many imports and send them in one round trip.\nconst pipeline = redis.pipeline();\nfor (let i = 1; i <= 1000; i++) {\n  pipeline.himport('SET', `user:${i}`, 'user', `user${i}`, `user${i}@example.com`, String(20 + i % 50));\n}\nconst res3 = await pipeline.exec();\n\nconsole.log(res3.length, res3.every(([err]) => err === null));\n// >>> 1000 true\n\nconst res4 = await redis.hget('user:1000', 'email');\nconsole.log(res4); // >>> user1000@example.com","section_id":"bulk-import-with-himport"},{"id":"bulk-import-with-himport-ex29","language":"javascript","code":"await client.hImportPrepare('user', ['name', 'email', 'age']);\n\n// Commands issued together are automatically pipelined.\nconst imports = [];\nfor (let i = 1; i <= 1000; i++) {\n  imports.push(\n    client.hImportSet(`user:${i}`, 'user', [`user${i}`, `user${i}@example.com`, String(20 + i % 50)])\n  );\n}\nconst res3 = await Promise.all(imports);\n\nconsole.log(res3.length, res3.every((r) => r === 'OK'));\n// >>> 1000 true\n\nconst res4 = await client.hGet('user:1000', 'email');\nconsole.log(res4); // >>> user1000@example.com","section_id":"bulk-import-with-himport"},{"id":"bulk-import-with-himport-ex30","language":"javascript","code":"const redis = new Redis({\n  himportFieldsets: [{ name: 'user', fields: ['name', 'email', 'age'] }],\n});\n\n// Queue many imports and send them in one round trip.\nconst pipeline = redis.pipeline();\nfor (let i = 1; i <= 1000; i++) {\n  pipeline.himport('SET', `user:${i}`, 'user', `user${i}`, `user${i}@example.com`, String(20 + i % 50));\n}\nconst res3 = await pipeline.exec();\n\nconsole.log(res3.length, res3.every(([err]) => err === null));\n// >>> 1000 true\n\nconst res4 = await redis.hget('user:1000', 'email');\nconsole.log(res4); // >>> user1000@example.com","section_id":"bulk-import-with-himport"},{"id":"bulk-import-with-himport-ex31","language":"php","code":"// Queue many imports and send them in one round trip. The fieldset is\n        // declared inside the pipeline so it runs on the same connection.\n        $replies = $r->pipeline(function ($pipe) {\n            $pipe->himport('PREPARE', 'user', ['name', 'email', 'age']);\n\n            for ($i = 1; $i <= 1000; $i++) {\n                $pipe->himport('SET', \"user:$i\", 'user', [\"user$i\", \"user$i@example.com\", (string) (20 + $i % 50)]);\n            }\n        });\n\n        // The first reply is for PREPARE.\n        $res3 = array_slice($replies, 1);\n        $allOk = true;\n        foreach ($res3 as $reply) {\n            $allOk = $allOk && $reply == 'OK';\n        }\n        echo count($res3) . ' ' . var_export($allOk, true) . PHP_EOL; // >>> 1000 true\n\n        $res4 = $r->hget('user:1000', 'email');\n        echo $res4 . PHP_EOL; // >>> user1000@example.com","section_id":"bulk-import-with-himport"},{"id":"bulk-import-with-himport-ex32","language":"python","code":"r.himport_prepare(\"user\", [\"name\", \"email\", \"age\"])\n\n# Queue many imports and send them in one round trip.\nwith r.pipeline(transaction=False) as pipe:\n    for i in range(1, 1001):\n        pipe.himport_set(\n            f\"user:{i}\", \"user\", [f\"user{i}\", f\"user{i}@example.com\", str(20 + i % 50)]\n        )\n    res3 = pipe.execute()\n\nprint(len(res3), all(res3))\n# >>> 1000 True\n\nres4 = r.hget(\"user:1000\", \"email\")\nprint(res4)\n# >>> user1000@example.com","section_id":"bulk-import-with-himport"},{"id":"bulk-import-with-himport-ex33","language":"ruby","code":"# Queue many imports and send them in one round trip. The fieldset is\n# declared inside the pipeline so it runs on the same connection.\nreplies = r.pipelined do |pipe|\n  pipe.himport_prepare('user', %w[name email age])\n  (1..1000).each do |i|\n    pipe.himport_set(\"user:#{i}\", 'user', [\"user#{i}\", \"user#{i}@example.com\", (20 + i % 50).to_s])\n  end\nend\n\n# The first reply is for PREPARE.\nres3 = replies.drop(1)\nputs \"#{res3.length} #{res3.all?('OK')}\" # >>> 1000 true\n\nres4 = r.hget('user:1000', 'email')\nputs res4 # >>> user1000@example.com","section_id":"bulk-import-with-himport"}]}
{"schema_version":2,"id":"develop/data-types","title":"Redis data types","url":"https://redis.io/docs/latest/develop/data-types/","summary":"Overview of data types supported by Redis","aliases":["/data-types/","/manual/data-types/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-10T15:45:53+01:00","children":[{"id":"develop/data-types/compare-data-types","summary":"Choose the best Redis data type for your task.","title":"Compare data types","url":"https://redis.io/docs/latest/develop/data-types/compare-data-types/"},{"id":"develop/data-types/strings","summary":"Introduction to Redis strings","title":"Redis Strings","url":"https://redis.io/docs/latest/develop/data-types/strings/"},{"id":"develop/data-types/arrays","summary":"Introduction to Redis arrays","title":"Redis arrays","url":"https://redis.io/docs/latest/develop/data-types/arrays/"},{"id":"develop/data-types/geospatial","summary":"Introduction to the Redis Geospatial data type","title":"Redis geospatial","url":"https://redis.io/docs/latest/develop/data-types/geospatial/"},{"id":"develop/data-types/hashes","summary":"Introduction to Redis hashes","title":"Redis hashes","url":"https://redis.io/docs/latest/develop/data-types/hashes/"},{"id":"develop/data-types/json","summary":"JSON support for Redis","title":"JSON","url":"https://redis.io/docs/latest/develop/data-types/json/"},{"id":"develop/data-types/lists","summary":"Introduction to Redis lists","title":"Redis lists","url":"https://redis.io/docs/latest/develop/data-types/lists/"},{"id":"develop/data-types/probabilistic","summary":"Probabilistic data structures in Redis","title":"Probabilistic","url":"https://redis.io/docs/latest/develop/data-types/probabilistic/"},{"id":"develop/data-types/sets","summary":"Introduction to Redis sets","title":"Redis sets","url":"https://redis.io/docs/latest/develop/data-types/sets/"},{"id":"develop/data-types/sorted-sets","summary":"Introduction to Redis sorted sets","title":"Redis sorted sets","url":"https://redis.io/docs/latest/develop/data-types/sorted-sets/"},{"id":"develop/data-types/streams","summary":"Introduction to Redis streams","title":"Redis Streams","url":"https://redis.io/docs/latest/develop/data-types/streams/"},{"id":"develop/data-types/timeseries","summary":"Ingest and query time series data with Redis","title":"Time series","url":"https://redis.io/docs/latest/develop/data-types/timeseries/"},{"id":"develop/data-types/vector-sets","summary":"Introduction to Redis vector sets","title":"Redis vector sets","url":"https://redis.io/docs/latest/develop/data-types/vector-sets/"}],"page_type":"content","content_hash":"5ef06f07f98abe9b1fd555f1ebe62131f81b1f5c96210cb4be38456ba7ad7d00","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis is a data structure server.\nAt its core, Redis provides a collection of native data types that help you solve a wide variety of problems, from [caching](https://redis.io/docs/latest/develop/data-types/strings) to\n[queuing](https://redis.io/docs/latest/develop/data-types/lists) to\n[event processing](https://redis.io/docs/latest/develop/data-types/streams).\nBelow is a short description of each data type, with links to broader overviews and command references.\nEach overview includes a comprehensive tutorial with code samples."},{"id":"data-types","title":"Data types","role":"content","text":"[Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack)\nimplements the following data types:\n\n- [Strings](#strings)\n    - [Bitmaps](#bitmaps)\n    - [Bitfields](#bitfields)\n- [Arrays](#arrays)\n- [Geospatial indexes](#geospatial-indexes)\n- [Hashes](#hashes)\n- [JSON](#json)\n- [Lists](#lists)\n- [Probabilistic data types](#probabilistic-data-types)\n- [Sets](#sets)\n- [Sorted sets](#sorted-sets)\n- [Streams](#streams)\n- [Time series](#time-series)\n- [Vector sets](#vector-sets)\n\nSee [Compare data types](https://redis.io/docs/latest/develop/data-types/compare-data-types)\nfor advice on which of the general-purpose data types is best for common tasks."},{"id":"strings","title":"Strings","role":"content","text":"[Redis strings](https://redis.io/docs/latest/develop/data-types/strings) are the most basic Redis data type, representing a sequence of bytes.\nFor more information, see:\n\n* [Overview of Redis strings](https://redis.io/docs/latest/develop/data-types/strings)\n* [Redis string command reference](/commands/?group=string)"},{"id":"bitfields","title":"Bitfields","role":"content","text":"[Redis bitfields](https://redis.io/docs/latest/develop/data-types/strings/bitfields) efficiently encode multiple counters in a string value.\nBitfields provide atomic get, set, and increment operations and support different overflow policies.\nFor more information, see:\n\n* [Overview of Redis bitfields](https://redis.io/docs/latest/develop/data-types/strings/bitfields)\n* The [`BITFIELD`](https://redis.io/docs/latest/commands/bitfield) command."},{"id":"bitmaps","title":"Bitmaps","role":"content","text":"[Redis bitmaps](https://redis.io/docs/latest/develop/data-types/strings/bitmaps) let you perform bitwise operations on strings. \nFor more information, see:\n\n* [Overview of Redis bitmaps](https://redis.io/docs/latest/develop/data-types/strings/bitmaps)\n* [Redis bitmap command reference](/commands/?group=bitmap)"},{"id":"arrays","title":"Arrays","role":"content","text":"[Redis arrays](https://redis.io/docs/latest/develop/data-types/arrays) are sparse, index-addressable sequences of strings.\nFor more information, see:\n\n* [Overview of Redis arrays](https://redis.io/docs/latest/develop/data-types/arrays)\n* [Redis array command reference](/commands/?group=array)"},{"id":"geospatial-indexes","title":"Geospatial indexes","role":"content","text":"[Redis geospatial indexes](https://redis.io/docs/latest/develop/data-types/geospatial) are useful for finding locations within a given geographic radius or bounding box.\nFor more information, see:\n\n* [Overview of Redis geospatial indexes](https://redis.io/docs/latest/develop/data-types/geospatial)\n* [Redis geospatial indexes command reference](/commands/?group=geo)"},{"id":"hashes","title":"Hashes","role":"content","text":"[Redis hashes](https://redis.io/docs/latest/develop/data-types/hashes) are record types modeled as collections of field-value pairs.\nAs such, Redis hashes resemble [Python dictionaries](https://docs.python.org/3/tutorial/datastructures.html#dictionaries), [Java HashMaps](https://docs.oracle.com/javase/8/docs/api/java/util/HashMap.html), and [Ruby hashes](https://ruby-doc.org/core-3.1.2/Hash.html).\nFor more information, see:\n\n* [Overview of Redis hashes](https://redis.io/docs/latest/develop/data-types/hashes)\n* [Redis hashes command reference](/commands/?group=hash)"},{"id":"json","title":"JSON","role":"content","text":"[Redis JSON](https://redis.io/docs/latest/develop/data-types/json) provides\nstructured, hierarchical arrays and key-value objects that match\nthe popular [JSON](https://www.json.org/json-en.html) text file\nformat. You can import JSON text into Redis objects and access,\nmodify, and query individual data elements.\nFor more information, see:\n\n- [Overview of Redis JSON](https://redis.io/docs/latest/develop/data-types/json)\n- [JSON command reference](/commands?group=json)"},{"id":"lists","title":"Lists","role":"content","text":"[Redis lists](https://redis.io/docs/latest/develop/data-types/lists) are lists of strings sorted by insertion order.\nFor more information, see:\n\n* [Overview of Redis lists](https://redis.io/docs/latest/develop/data-types/lists)\n* [Redis list command reference](/commands/?group=list)"},{"id":"probabilistic-data-types","title":"Probabilistic data types","role":"content","text":"These data types let you gather and calculate statistics in a way\nthat is approximate but highly efficient. The following types are\navailable:\n\n- [Bloom filter](#bloom-filter)\n- [Count-min sketch](#count-min-sketch)\n- [Cuckoo filter](#cuckoo-filter)\n- [HyperLogLog](#hyperloglog)\n- [t-digest](#t-digest)\n- [Top-K](#top-k)"},{"id":"bloom-filter","title":"Bloom filter","role":"content","text":"[Redis Bloom filters](https://redis.io/docs/latest/develop/data-types/probabilistic/bloom-filter)\nlet you check for the presence or absence of an element in a set. For more\ninformation, see:\n\n- [Overview of Redis Bloom filters](https://redis.io/docs/latest/develop/data-types/probabilistic/bloom-filter)\n- [Bloom filter command reference](/commands?group=bf)"},{"id":"count-min-sketch","title":"Count-min sketch","role":"content","text":"[Redis Count-min sketch](https://redis.io/docs/latest/develop/data-types/probabilistic/count-min-sketch)\nestimate the frequency of a data point within a stream of values.\nFor more information, see:\n\n- [Redis Count-min sketch overview](https://redis.io/docs/latest/develop/data-types/probabilistic/count-min-sketch)\n- [Count-min sketch command reference](/commands?group=cms)"},{"id":"cuckoo-filter","title":"Cuckoo filter","role":"content","text":"[Redis Cuckoo filters](https://redis.io/docs/latest/develop/data-types/probabilistic/cuckoo-filter)\nlet you check for the presence or absence of an element in a set. They are similar to\n[Bloom filters](#bloom-filter) but with slightly different trade-offs between features\nand performance. For more information, see:\n\n- [Overview of Redis Cuckoo filters](https://redis.io/docs/latest/develop/data-types/probabilistic/cuckoo-filter)\n- [Cuckoo filter command reference](/commands?group=cf)"},{"id":"hyperloglog","title":"HyperLogLog","role":"content","text":"The [Redis HyperLogLog](https://redis.io/docs/latest/develop/data-types/probabilistic/hyperloglogs) data structures provide probabilistic estimates of the cardinality (i.e., number of elements) of large sets. For more information, see:\n\n* [Overview of Redis HyperLogLog](https://redis.io/docs/latest/develop/data-types/probabilistic/hyperloglogs)\n* [Redis HyperLogLog command reference](/commands/?group=hyperloglog)"},{"id":"t-digest","title":"t-digest","role":"content","text":"[Redis t-digest](https://redis.io/docs/latest/develop/data-types/probabilistic/t-digest)\nstructures estimate percentiles from a stream of data values. For more\ninformation, see:\n\n- [Redis t-digest overview](https://redis.io/docs/latest/develop/data-types/probabilistic/t-digest)\n- [t-digest command reference](/commands?group=tdigest)"},{"id":"top-k","title":"Top-K","role":"content","text":"[Redis Top-K](https://redis.io/docs/latest/develop/data-types/probabilistic/top-k)\nstructures estimate the ranking of a data point within a stream of values.\nFor more information, see:\n\n- [Redis Top-K overview](https://redis.io/docs/latest/develop/data-types/probabilistic/top-k)\n- [Top-K command reference](/commands?group=topk)"},{"id":"sets","title":"Sets","role":"content","text":"[Redis sets](https://redis.io/docs/latest/develop/data-types/sets) are unordered collections of unique strings that act like the sets from your favorite programming language (for example, [Java HashSets](https://docs.oracle.com/javase/7/docs/api/java/util/HashSet.html), [Python sets](https://docs.python.org/3.10/library/stdtypes.html#set-types-set-frozenset), and so on).\nWith a Redis set, you can add, remove, and test for existence in O(1) time (in other words, regardless of the number of set elements).\nFor more information, see:\n\n* [Overview of Redis sets](https://redis.io/docs/latest/develop/data-types/sets)\n* [Redis set command reference](/commands/?group=set)"},{"id":"sorted-sets","title":"Sorted sets","role":"content","text":"[Redis sorted sets](https://redis.io/docs/latest/develop/data-types/sorted-sets) are collections of unique strings that maintain order by each string's associated score.\nFor more information, see:\n\n* [Overview of Redis sorted sets](https://redis.io/docs/latest/develop/data-types/sorted-sets)\n* [Redis sorted set command reference](/commands/?group=sorted-set)"},{"id":"streams","title":"Streams","role":"content","text":"A [Redis stream](https://redis.io/docs/latest/develop/data-types/streams) is a data structure that acts like an append-only log.\nStreams help record events in the order they occur and then syndicate them for processing.\nFor more information, see:\n\n* [Overview of Redis Streams](https://redis.io/docs/latest/develop/data-types/streams)\n* [Redis Streams command reference](/commands/?group=stream)"},{"id":"time-series","title":"Time series","role":"content","text":"[Redis time series](https://redis.io/docs/latest/develop/data-types/timeseries)\nstructures let you store and query timestamped data points.\nFor more information, see:\n\n- [Redis time series overview](https://redis.io/docs/latest/develop/data-types/timeseries)\n- [Time series command reference](/commands?group=timeseries)"},{"id":"vector-sets","title":"Vector sets","role":"content","text":"[Redis vector sets](https://redis.io/docs/latest/develop/data-types/vector-sets) are a specialized data type designed for managing high-dimensional vector data, enabling fast and efficient vector similarity search within Redis. Vector sets are optimized for use cases involving machine learning, recommendation systems, and semantic search, where each vector represents a data point in multi-dimensional space. Vector sets supports the [HNSW](https://en.wikipedia.org/wiki/Hierarchical_navigable_small_world) (hierarchical navigable small world) algorithm, allowing you to store, index, and query vectors based on the cosine similarity metric. With vector sets, Redis provides native support for hybrid search, combining vector similarity with structured [filters](https://redis.io/docs/latest/develop/data-types/vector-sets/filtered-search).\nFor more information, see:\n\n* [Overview of Redis vector sets](https://redis.io/docs/latest/develop/data-types/vector-sets)\n* [Redis vector set command reference](/commands/?group=vector_set)"},{"id":"adding-extensions","title":"Adding extensions","role":"content","text":"To extend the features provided by the included data types, use one of these options:\n\n1. Write your own custom [server-side functions in Lua](https://redis.io/docs/latest/develop/programmability).\n1. Write your own Redis module using the [modules API](https://redis.io/docs/latest/develop/reference/modules) or check out the [community-supported modules](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise)."}],"examples":[]}
{"schema_version":2,"id":"develop/data-types/json/developer","title":"Developer notes","url":"https://redis.io/docs/latest/develop/data-types/json/developer/","summary":"Notes on JSON debugging, testing and documentation.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-10T15:45:45+01:00","page_type":"content","content_hash":"8a9f22af40f32bf4f28088dae99f4b052d64d21cec24e725721f621871ddc010","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Developing Redis JSON involves setting up the development environment (which can be either Linux-based or macOS-based), building RedisJSON (the Redis module providing JSON), running tests and benchmarks, and debugging both the JSON module and its tests."},{"id":"cloning-the-git-repository","title":"Cloning the git repository","role":"content","text":"To clone the RedisJSON module and its submodules, run:\n[code example]"},{"id":"working-in-an-isolated-environment","title":"Working in an isolated environment","role":"content","text":"There are several reasons to use an isolated environment for development, like keeping your workstation clean and developing for a different Linux distribution.\n\nYou can use a virtual machine as an isolated development environment. To set one up, you can use [Vagrant](https://www.vagrantup.com) or Docker.\n\nTo set up a virtual machine with Docker:\n\n[code example]\nThen run [code example]docker exec\ndocker commit $rejson redisjson1\ndocker stop $rejson\nrejson=$(docker run -d -it -v $PWD:/build redisjson1 bash)\ndocker exec -it $rejson bash\n```\n\nYou can replace `debian:bullseye` with your OS of choice. If you use the same OS as your host machine, you can run the RedisJSON binary on your host after it is built."},{"id":"installing-prerequisites","title":"Installing prerequisites","role":"setup","text":"To build and test RedisJSON one needs to install several packages, depending on the underlying OS. Currently, we support the Ubuntu/Debian, CentOS, Fedora, and macOS.\n\nEnter the `RedisJSON` directory and run:\n\n[code example]\n\n**This will install various packages on your system** using the native package manager and pip. It will invoke `sudo` on its own, prompting for permission.\n\nIf you prefer to avoid that, you can:\n\n* Review `system-setup.py` and install packages manually,\n* Use `system-setup.py --nop` to display installation commands without executing them,\n* Use an isolated environment like explained above,\n* Use a Python virtual environment, as Python installations are known to be sensitive when not used in isolation: `python -m virtualenv venv; . ./venv/bin/activate`"},{"id":"installing-redis","title":"Installing Redis","role":"setup","text":"Generally, it is best to run the latest Redis version.\n\nIf your OS has a Redis 6.x package, you can install it using the OS package manager.\n\nOtherwise, you can invoke\n[code example]"},{"id":"getting-help","title":"Getting help","role":"content","text":"\nmake setup         # install prerequisites\n\nmake build\n  DEBUG=1          # build debug variant\n  SAN=type         # build with LLVM sanitizer (type=address|memory|leak|thread)\n  VALGRIND|VG=1    # build for testing with Valgrind\nmake clean         # remove binary files\n  ALL=1            # remove binary directories\n\nmake all           # build all libraries and packages\n\nmake test          # run both cargo and python tests\nmake cargo_test    # run inbuilt rust unit tests\nmake pytest        # run flow tests using RLTest\n  TEST=file:name     # run test matching `name` from `file`\n  TEST_ARGS=\"...\"    # RLTest arguments\n  QUICK=1            # run only general tests\n  GEN=1              # run general tests on a standalone Redis topology\n  AOF=1              # run AOF persistency tests on a standalone Redis topology\n  SLAVES=1           # run replication tests on standalone Redis topology\n  CLUSTER=1          # run general tests on a Redis Open Source Cluster topology\n  VALGRIND|VG=1      # run specified tests with Valgrind\n  VERBOSE=1          # display more RLTest-related information\n\nmake pack          # build package (RAMP file)\nmake upload-artifacts   # copy snapshot packages to S3\n  OSNICK=nick             # copy snapshots for specific OSNICK\nmake upload-release     # copy release packages to S3\n\ncommon options for upload operations:\n  STAGING=1             # copy to staging lab area (for validation)\n  FORCE=1               # allow operation outside CI environment\n  VERBOSE=1             # show more details\n  NOP=1                 # do not copy, just print commands\n\nmake coverage      # perform coverage analysis\nmake show-cov      # show coverage analysis results (implies COV=1)\nmake upload-cov    # upload coverage analysis results to codecov.io (implies COV=1)\n\nmake docker        # build for specific Linux distribution\n  OSNICK=nick        # Linux distribution to build for\n  REDIS_VER=ver      # use Redis version `ver`\n  TEST=1             # test after build\n  PACK=1             # create packages\n  ARTIFACTS=1        # copy artifacts from docker image\n  PUBLISH=1          # publish (i.e. docker push) after build\n\nmake sanbox        # create container for CLang Sanitizer tests\n```"},{"id":"building-from-source","title":"Building from source","role":"content","text":"Run [code example]make build[code example]make clean``` to remove built artifacts. ```make clean ALL=1``` will remove the entire bin subdirectory."},{"id":"running-tests","title":"Running tests","role":"content","text":"There are several sets of unit tests:\n* Rust tests, integrated in the source code, run by [code example]tests/pytests[code example]make test[code example]TEST[code example]bash\n$ # use an existing local Redis instance for testing the module\n$ REDIS_PORT=6379 make test\n```"},{"id":"debugging","title":"Debugging","role":"content","text":"To include debugging information, you need to set the [`DEBUG`](https://redis.io/docs/latest/commands/debug) environment variable before you compile RedisJSON. For example, run `export DEBUG=1`.\n\nYou can add breakpoints to Python tests in single-test mode. To set a breakpoint, call the ```BB()``` function inside a test."}],"examples":[{"id":"cloning-the-git-repository-ex0","language":"sh","code":"git clone --recursive https://github.com/RedisJSON/RedisJSON.git","section_id":"cloning-the-git-repository"},{"id":"working-in-an-isolated-environment-ex0","language":"plaintext","code":"rejson=$(docker run -d -it -v $PWD:/build debian:bullseye bash)\ndocker exec -it $rejson bash","section_id":"working-in-an-isolated-environment"},{"id":"working-in-an-isolated-environment-ex1","language":"cd /build``` from within the container.","code":"In this mode, all installations remain in the scope of the Docker container.\nAfter you exit the container, you can either restart it with the previous","section_id":"working-in-an-isolated-environment"},{"id":"installing-prerequisites-ex0","language":"sh","code":"$ ./sbin/setup","section_id":"installing-prerequisites"},{"id":"installing-redis-ex0","language":"sh","code":"$ ./deps/readies/bin/getredis","section_id":"installing-redis"},{"id":"building-from-source-ex0","language":"make build``` to build RedisJSON.","code":"Notes:\n\n* Binary files are placed under `target/release/`, according to platform and build variant.\n\n* RedisJSON uses [Cargo](https://github.com/rust-lang/cargo) as its build system.","section_id":"building-from-source"},{"id":"building-from-source-ex1","language":" will invoke both Cargo and the subsequent `make` command that's required to complete the build.","code":"Use","section_id":"building-from-source"},{"id":"running-tests-ex0","language":"make cargo_test```.","code":"* Python tests (enabled by RLTest), located in","section_id":"running-tests"},{"id":"running-tests-ex1","language":", run by ```make pytest```.","code":"You can run all tests with","section_id":"running-tests"},{"id":"running-tests-ex2","language":".","code":"To run only specific tests, use the","section_id":"running-tests"},{"id":"running-tests-ex3","language":" parameter. For example, run ```make test TEST=regex```.","code":"You can run the module's tests against an \"embedded\" disposable Redis instance or against an instance\nyou provide. To use the \"embedded\" mode, you must include the `redis-server` executable in your `PATH`.\n\nYou can override the spawning of the embedded server by specifying a Redis port via the `REDIS_PORT`\nenvironment variable, e.g.:","section_id":"running-tests"}]}
{"schema_version":2,"id":"develop/data-types/json","title":"JSON","url":"https://redis.io/docs/latest/develop/data-types/json/","summary":"JSON support for Redis","aliases":["/data-types/json/","/manual/data-types/json/","/stack/json/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-30T13:16:11-07:00","children":[{"id":"develop/data-types/json/indexing_JSON","summary":"Combine Redis JSON and Redis Search to index and search JSON documents","title":"Index/Search JSON documents","url":"https://redis.io/docs/latest/develop/data-types/json/indexing_json/"},{"id":"develop/data-types/json/path","summary":"Access specific elements within a JSON document","title":"Path","url":"https://redis.io/docs/latest/develop/data-types/json/path/"},{"id":"develop/data-types/json/use_cases","summary":"JSON use cases","title":"Use cases","url":"https://redis.io/docs/latest/develop/data-types/json/use_cases/"},{"id":"develop/data-types/json/performance","summary":"Performance benchmarks","title":"Performance","url":"https://redis.io/docs/latest/develop/data-types/json/performance/"},{"id":"develop/data-types/json/ram","summary":"Debugging memory consumption","title":"Redis JSON RAM Usage","url":"https://redis.io/docs/latest/develop/data-types/json/ram/"},{"id":"develop/data-types/json/resp3","summary":"JSON RESP2 to RESP3 replies reference for client developers","title":"Guide for migrating from RESP2 to RESP3 replies","url":"https://redis.io/docs/latest/develop/data-types/json/resp3/"},{"id":"develop/data-types/json/developer","summary":"Notes on JSON debugging, testing and documentation.","title":"Developer notes","url":"https://redis.io/docs/latest/develop/data-types/json/developer/"}],"page_type":"content","content_hash":"7678974010d7385c4c9d03d5f4e64214325a499e8043156c5ecdb0c348c50c0e","sections":[{"id":"json-command-summary","title":"JSON command summary","role":"content","text":"**25 commands in this group:**\n\n[View all json commands](https://redis.io/commands/?group=json)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [JSON.ARRAPPEND](https://redis.io/commands/json.arrappend/) | Append the JSON values into the array at path after the last element in it | O(1) when path is evaluated to a single value, ... | 1.0.0 |\n| [JSON.ARRINDEX](https://redis.io/commands/json.arrindex/) | Search for the first occurrence of a JSON value in an array | O(N) when path is evaluated to a single value w... | 1.0.0 |\n| [JSON.ARRINSERT](https://redis.io/commands/json.arrinsert/) | Insert the json values into the array at path before the index (shifts to the right) | O(N) when path is evaluated to a single value w... | 1.0.0 |\n| [JSON.ARRLEN](https://redis.io/commands/json.arrlen/) | Report the length of the JSON array at path in key | O(1) where path is evaluated to a single value,... | 1.0.0 |\n| [JSON.ARRPOP](https://redis.io/commands/json.arrpop/) | Remove and return the element at the specified index in the array at path | O(N) when path is evaluated to a single value w... | 1.0.0 |\n| [JSON.ARRTRIM](https://redis.io/commands/json.arrtrim/) | Trim an array so that it contains only the specified inclusive range of elements | O(N) when path is evaluated to a single value w... | 1.0.0 |\n| [JSON.CLEAR](https://redis.io/commands/json.clear/) | Clear container values (arrays/objects) and set numeric values to 0 | O(N) when path is evaluated to a single value w... | 2.0.0 |\n| [JSON.DEBUG](https://redis.io/commands/json.debug/) | This is a container command for debugging related tasks | N/A | 1.0.0 |\n| [JSON.DEL](https://redis.io/commands/json.del/) | Delete a value | O(N) when path is evaluated to a single value w... | 1.0.0 |\n| [JSON.FORGET](https://redis.io/commands/json.forget/) | Delete a value | O(N) when path is evaluated to a single value w... | 1.0.0 |\n\n#### JSON command summary (part 2)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [JSON.GET](https://redis.io/commands/json.get/) | Get JSON value at path | O(N) where N is the size of the JSON | 1.0.0 |\n| [JSON.MERGE](https://redis.io/commands/json.merge/) | Merge a given JSON value into matching paths. Consequently, JSON values at matching paths are updated, deleted, or expanded with new children | O(M+N) when path is evaluated to a single value... | 2.6.0 |\n| [JSON.MGET](https://redis.io/commands/json.mget/) | Return the values at path from multiple key arguments | O(M*N) when path is evaluated to a single value... | 1.0.0 |\n| [JSON.MSET](https://redis.io/commands/json.mset/) | Set or update one or more JSON values according to the specified key-path-value triplets | O(K*(M+N)) where k is the number of keys in the... | 2.6.0 |\n| [JSON.NUMINCRBY](https://redis.io/commands/json.numincrby/) | Increment the number value stored at path by number | O(1) when path is evaluated to a single value, ... | 1.0.0 |\n| [JSON.NUMMULTBY](https://redis.io/commands/json.nummultby/) | Multiply the number value stored at path by number | O(1) when path is evaluated to a single value, ... | 1.0.0 |\n| [JSON.NUMPOWBY](https://redis.io/commands/json.numpowby/) | Raise the number value stored at path to the power of number | O(1) when path is evaluated to a single value, ... | 1.0.0 |\n| [JSON.OBJKEYS](https://redis.io/commands/json.objkeys/) | Return the keys in the object that's referenced by path | O(1) when path is evaluated to a single value, ... | 1.0.0 |\n| [JSON.OBJLEN](https://redis.io/commands/json.objlen/) | Report the number of keys in the JSON object at path in key | O(1) when path is evaluated to a single value, ... | 1.0.0 |\n| [JSON.RESP](https://redis.io/commands/json.resp/) | Return the JSON in key in Redis serialization protocol specification form | O(N) when path is evaluated to a single value, ... | 1.0.0 |\n\n#### JSON command summary (part 3)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [JSON.SET](https://redis.io/commands/json.set/) | Set the JSON value at path in key | O(M+N) where M is the size of the original valu... | 1.0.0 |\n| [JSON.STRAPPEND](https://redis.io/commands/json.strappend/) | Append the json-string values to the string at path | O(1) when path is evaluated to a single value, ... | 1.0.0 |\n| [JSON.STRLEN](https://redis.io/commands/json.strlen/) | Report the length of the JSON String at path in key | O(1) when path is evaluated to a single value, ... | 1.0.0 |\n| [JSON.TOGGLE](https://redis.io/commands/json.toggle/) | Toggle the boolean value stored at path | O(1) when path is evaluated to a single value, ... | 2.0.0 |\n| [JSON.TYPE](https://redis.io/commands/json.type/) | Report the type of JSON value at path | O(1) when path is evaluated to a single value, ... | 1.0.0 |\n\n\n\n[![Discord](https://img.shields.io/discord/697882427875393627?style=flat-square)](https://discord.gg/QUkjSsk)\n[![Github](https://img.shields.io/static/v1?label=&message=repository&color=5961FF&logo=github)](https://github.com/RedisJSON/RedisJSON/)\n\nThe JSON capability of Redis Open Source provides JavaScript Object Notation (JSON) support for Redis. It lets you store, update, and retrieve JSON values in a Redis database, similar to any other Redis data type. Redis JSON also works seamlessly with [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) to let you [index and query JSON documents](https://redis.io/docs/latest/develop/ai/search-and-query/indexing)."},{"id":"primary-features","title":"Primary features","role":"content","text":"* Full support for the JSON standard\n* A [JSONPath](http://goessner.net/articles/JsonPath/) syntax for selecting/updating elements inside documents (see [JSONPath syntax](https://redis.io/docs/latest/develop/data-types/json/path#jsonpath-syntax))\n* Documents stored as binary data in a tree structure, allowing fast access to sub-elements\n* Typed atomic operations for all JSON value types"},{"id":"use-redis-with-json","title":"Use Redis with JSON","role":"content","text":"The first JSON command to try is [`JSON.SET`](https://redis.io/docs/latest/commands/json.set), which sets a Redis key with a JSON value. [`JSON.SET`](https://redis.io/docs/latest/commands/json.set) accepts all JSON value types. This example creates a JSON string:\n\nFoundational: Set and retrieve JSON values using JSON.SET and JSON.GET to store and access JSON documents\n\n**Commands:** JSON.SET, JSON.GET, JSON.TYPE\n\n**Complexity:**\n- JSON.SET: O(M+N)\n- JSON.GET: O(N)\n- JSON.TYPE: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nNote how the commands include the dollar sign character `$`. This is the [path](https://redis.io/docs/latest/develop/data-types/json/path) to the value in the JSON document (in this case it just means the root).\n\nHere are a few more string operations. [`JSON.STRLEN`](https://redis.io/docs/latest/commands/json.strlen) tells you the length of the string, and you can append another string to it with [`JSON.STRAPPEND`](https://redis.io/docs/latest/commands/json.strappend).\n\nString operations: Manipulate JSON strings using JSON.STRLEN to get length and JSON.STRAPPEND to concatenate values\n\n**Builds upon:** set_get\n\n**Commands:** JSON.STRLEN, JSON.STRAPPEND, JSON.GET\n\n**Complexity:**\n- JSON.STRLEN: O(1)\n- JSON.STRAPPEND: O(1)\n- JSON.GET: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nNumbers can be [incremented](https://redis.io/docs/latest/commands/json.numincrby) and [multiplied](https://redis.io/docs/latest/commands/json.nummultby):\n\nNumeric operations: Perform atomic arithmetic on JSON numbers using JSON.NUMINCRBY to increment and JSON.NUMMULTBY to multiply values\n\n**Builds upon:** set_get\n\n**Commands:** JSON.SET, JSON.NUMINCRBY, JSON.NUMMULTBY\n\n**Complexity:**\n- JSON.SET: O(M+N)\n- JSON.NUMINCRBY: O(1)\n- JSON.NUMMULTBY: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nHere's a more interesting example that includes JSON arrays and objects:\n\nArrays and objects: Work with complex JSON structures using JSONPath to access nested elements and JSON.DEL to remove values\n\n**Difficulty:** Intermediate\n\n**Builds upon:** set_get\n\n**Commands:** JSON.SET, JSON.GET, JSON.DEL\n\n**Complexity:**\n- JSON.SET: O(M+N)\n- JSON.GET: O(N)\n- JSON.DEL: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nBeginning with Redis 8.8, the JSON data type supports the ability to force a particular type when storing floating point homogeneous arrays (FPHAs)using the `FPHA BF16|FP16|FP32|FP64` option to the [`JSON.SET`](https://redis.io/docs/latest/commands/json.set) command. Here's an example:\n\n[code example]\n\nThe [`JSON.DEL`](https://redis.io/docs/latest/commands/json.del) command deletes any JSON value you specify with the `path` parameter.\n\nYou can manipulate arrays with a dedicated subset of JSON commands:\n\nArray manipulation: Use JSON.ARRAPPEND to add elements, JSON.ARRINSERT to insert at positions, JSON.ARRTRIM to keep ranges, and JSON.ARRPOP to remove elements\n\n**Difficulty:** Intermediate\n\n**Builds upon:** arr\n\n**Commands:** JSON.SET, JSON.ARRAPPEND, JSON.GET, JSON.ARRINSERT, JSON.ARRTRIM, JSON.ARRPOP\n\n**Complexity:**\n- JSON.SET: O(M+N)\n- JSON.ARRAPPEND: O(1)\n- JSON.GET: O(N)\n- JSON.ARRINSERT: O(N)\n- JSON.ARRTRIM: O(N)\n- JSON.ARRPOP: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nJSON objects also have their own commands:\n\nObject operations: Inspect JSON objects using JSON.OBJLEN to count fields and JSON.OBJKEYS to retrieve all keys\n\n**Difficulty:** Intermediate\n\n**Builds upon:** arr\n\n**Commands:** JSON.SET, JSON.OBJLEN, JSON.OBJKEYS\n\n**Complexity:**\n- JSON.SET: O(M+N)\n- JSON.OBJLEN: O(1)\n- JSON.OBJKEYS: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"format-cli-output","title":"Format CLI output","role":"content","text":"The CLI has a raw output mode that lets you add formatting to the output from\n[`JSON.GET`](https://redis.io/docs/latest/commands/json.get) to make\nit more readable. To use this, run `redis-cli` with the `--raw` option\nand include formatting keywords such as `INDENT`, `NEWLINE`, and `SPACE`\nwith [`JSON.GET`](https://redis.io/docs/latest/commands/json.get):\n\n[code example]"},{"id":"enable-redis-json","title":"Enable Redis JSON","role":"content","text":"The Redis JSON data type is part of Redis Open Source and it is also available in Redis Software and Redis Cloud.\nSee\n[Install Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack) or\n[Install Redis Software](https://redis.io/docs/latest/operate/rs/installing-upgrading/install)\nfor full installation instructions."},{"id":"limitation","title":"Limitation","role":"limits","text":"A JSON value passed to a command can have a depth of up to 128. If you pass to a command a JSON value that contains an object or an array with a nesting level of more than 128, the command returns an error."},{"id":"further-information","title":"Further information","role":"content","text":"Read the other pages in this section to learn more about Redis JSON"}],"examples":[{"id":"use-redis-with-json-ex0","language":"plaintext","code":"> JSON.SET bike $ '\"Hyperion\"'\nOK\n> JSON.GET bike $\n\"[\\\"Hyperion\\\"]\"\n> JSON.TYPE bike $\n1) \"string\"","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex1","language":"csharp","code":"bool res1 = db.JSON().Set(\"bike\", \"$\", \"\\\"Hyperion\\\"\");\n        Console.WriteLine(res1);    // >>> True\n\n        RedisResult res2 = db.JSON().Get(\"bike\", path: \"$\");\n        Console.WriteLine(res2);    // >>> [\"Hyperion\"]\n\n        JsonType[] res3 = db.JSON().Type(\"bike\", \"$\");\n        Console.WriteLine(string.Join(\", \", res3)); // >>> STRING","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex2","language":"go","code":"res1, err := rdb.JSONSet(ctx, \"bike\", \"$\",\n\t\t\"\\\"Hyperion\\\"\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> OK\n\n\tres2, err := rdb.JSONGet(ctx, \"bike\", \"$\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> [\"Hyperion\"]\n\n\tres3, err := rdb.JSONType(ctx, \"bike\", \"$\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3) // >>> [[string]]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex3","language":"java","code":"CompletableFuture<Void> setget = asyncCommands\n                    .jsonSet(\"bike\", JsonPath.ROOT_PATH, parser.createJsonValue(\"\\\"Hyperion\\\"\")).thenCompose(res1 -> {\n                        System.out.println(res1); // OK\n\n                        return asyncCommands.jsonGet(\"bike\", JsonPath.ROOT_PATH);\n                    }).thenCompose(res2 -> {\n                        System.out.println(res2); // >>> [[\"Hyperion\"]]\n\n                        return asyncCommands.jsonType(\"bike\", JsonPath.ROOT_PATH);\n                    })\n                    .thenAccept(System.out::println)\n                    // >>> [STRING]\n                    .toCompletableFuture();","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex4","language":"java","code":"Mono<Void> setget = reactiveCommands.jsonSet(\"bike\", JsonPath.ROOT_PATH, parser.createJsonValue(\"\\\"Hyperion\\\"\"))\n                    .doOnNext(res1 -> {\n                        System.out.println(res1); // OK\n                    }).flatMap(res1 -> reactiveCommands.jsonGet(\"bike\", JsonPath.ROOT_PATH).collectList()).doOnNext(res2 -> {\n                        System.out.println(res2); // >>> [[\"Hyperion\"]]\n                    }).flatMap(res2 -> reactiveCommands.jsonType(\"bike\", JsonPath.ROOT_PATH).collectList())\n                    .doOnNext(System.out::println) // >>> [STRING]\n                    .then();","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex5","language":"java","code":"String res1 = jedis.jsonSet(\"bike\", new Path2(\"$\"), \"\\\"Hyperion\\\"\");\n        System.out.println(res1);   // >>> OK\n\n        Object res2 = jedis.jsonGet(\"bike\", new Path2(\"$\"));\n        System.out.println(res2);   // >>> [\"Hyperion\"]\n\n        List<Class<?>> res3 = jedis.jsonType(\"bike\", new Path2(\"$\"));\n        System.out.println(res3);   // >>> [class java.lang.String]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex6","language":"javascript","code":"const res1 = await client.json.set(\"bike\", \"$\", '\"Hyperion\"');\nconsole.log(res1); // OK\n\nconst res2 = await client.json.get(\"bike\", { path: \"$\" });\nconsole.log(res2); // ['\"Hyperion\"']\n\nconst res3 = await client.json.type(\"bike\", { path: \"$\" });\nconsole.log(res3); //  [ 'string' ]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex7","language":"php","code":"$res1 = $r->jsonset('bike', '$', '\"Hyperion\"');\n        echo $res1 . PHP_EOL;\n        // >>> OK\n\n        $res2 = $r->jsonget('bike', '', '', '', '$');\n        echo $res2 . PHP_EOL;\n        // >>> [\"Hyperion\"]\n\n        $res3 = $r->jsontype('bike', '$');\n        echo json_encode($res3) . PHP_EOL;\n        // >>> [\"string\"]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex8","language":"python","code":"res1 = r.json().set(\"bike\", \"$\", '\"Hyperion\"')\nprint(res1)  # >>> True\n\nres2 = r.json().get(\"bike\", \"$\")\nprint(res2)  # >>> ['\"Hyperion\"']\n\nres3 = r.json().type(\"bike\", \"$\")\nprint(res3)  # >>> ['string']","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex9","language":"ruby","code":"res1 = r.json_set('bike', '$', 'Hyperion')\nputs res1 # >>> OK\n\nres2 = r.json_get('bike', '$')\np res2 # >>> [\"Hyperion\"]\n\nres3 = r.json_type('bike', '$')\np res3 # >>> [\"string\"]\n\n# With raw: true, json_set accepts an already-encoded JSON string (skipping\n# serialization) and json_get returns the unparsed JSON string rather than a\n# Ruby object — useful when you store or forward plain JSON.\nres_raw1 = r.json_set('bike', '$', '\"Hyperion\"', raw: true)\nputs res_raw1 # >>> OK\n\nres_raw2 = r.json_get('bike', '$', raw: true)\nputs res_raw2 # >>> [\"Hyperion\"]  (a JSON string, not a Ruby array)","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex10","language":"rust","code":"let res1: bool = r\n            .json_set(\"bike\", \"$\", &json!(\"Hyperion\"))\n            .await\n            .expect(\"Failed to run JSON.SET\");\n        print_set_result(res1); // >>> OK\n\n        let res2: String = r\n            .json_get(\"bike\", \"$\")\n            .await\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{res2}\"); // >>> [\"Hyperion\"]\n\n        let res3: Value = r\n            .json_type(\"bike\", \"$\")\n            .await\n            .expect(\"Failed to run JSON.TYPE\");\n        print_redis_value(&res3); // >>> [\"string\"]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex11","language":"rust","code":"let res1: bool = r\n            .json_set(\"bike\", \"$\", &json!(\"Hyperion\"))\n            .expect(\"Failed to run JSON.SET\");\n        print_set_result(res1); // >>> OK\n\n        let res2: String = r.json_get(\"bike\", \"$\").expect(\"Failed to run JSON.GET\");\n        println!(\"{res2}\"); // >>> [\"Hyperion\"]\n\n        let res3: Value = r.json_type(\"bike\", \"$\").expect(\"Failed to run JSON.TYPE\");\n        print_redis_value(&res3); // >>> [\"string\"]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex12","language":"plaintext","code":"> JSON.STRLEN bike $\n1) (integer) 8\n> JSON.STRAPPEND bike $ '\" (Enduro bikes)\"'\n1) (integer) 23\n> JSON.GET bike $\n\"[\\\"Hyperion (Enduro bikes)\\\"]\"","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex13","language":"csharp","code":"long?[] res4 = db.JSON().StrLen(\"bike\", \"$\");\n        Console.Write(string.Join(\", \", res4)); // >>> 8\n\n        long?[] res5 = db.JSON().StrAppend(\"bike\", \" (Enduro bikes)\");\n        Console.WriteLine(string.Join(\", \", res5)); // >>> 23\n\n        RedisResult res6 = db.JSON().Get(\"bike\", path: \"$\");\n        Console.WriteLine(res6);    // >>> [\"Hyperion (Enduro bikes)\"]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex14","language":"go","code":"res4, err := rdb.JSONStrLen(ctx, \"bike\", \"$\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(*res4[0]) // >>> 8\n\n\tres5, err := rdb.JSONStrAppend(ctx, \"bike\", \"$\", \"\\\" (Enduro bikes)\\\"\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(*res5[0]) // >>> 23\n\n\tres6, err := rdb.JSONGet(ctx, \"bike\", \"$\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res6) // >>> [\"Hyperion (Enduro bikes)\"]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex15","language":"java","code":"CompletableFuture<Void> str = asyncCommands.jsonStrlen(\"bike\", JsonPath.ROOT_PATH).thenCompose(res3 -> {\n                System.out.println(res3); // >>> [8]\n\n                return asyncCommands.jsonStrappend(\"bike\", JsonPath.ROOT_PATH, parser.createJsonValue(\"\\\" (Enduro bikes)\\\"\"));\n            }).thenCompose(res4 -> {\n                System.out.println(res4); // >>> [23]\n\n                return asyncCommands.jsonGet(\"bike\", JsonPath.ROOT_PATH);\n            })\n                    .thenAccept(System.out::println)\n                    // >>> [[\"Hyperion (Enduro bikes)\"]]\n                    .toCompletableFuture();","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex16","language":"java","code":"Mono<Void> str = reactiveCommands.jsonStrlen(\"bike\", JsonPath.ROOT_PATH).collectList().doOnNext(res3 -> {\n                System.out.println(res3); // >>> [8]\n            }).flatMap(res3 -> reactiveCommands\n                    .jsonStrappend(\"bike\", JsonPath.ROOT_PATH, parser.createJsonValue(\"\\\" (Enduro bikes)\\\"\")).collectList())\n                    .doOnNext(res4 -> {\n                        System.out.println(res4); // >>> [23]\n                    }).flatMap(res4 -> reactiveCommands.jsonGet(\"bike\", JsonPath.ROOT_PATH).collectList())\n                    .doOnNext(System.out::println) // >>> [[\"Hyperion (Enduro bikes)\"]]\n                    .then();","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex17","language":"java","code":"List<Long> res4 = jedis.jsonStrLen(\"bike\", new Path2(\"$\"));\n        System.out.println(res4);   // >>> [8]\n\n        List<Long> res5 = jedis.jsonStrAppend(\"bike\", new Path2(\"$\"), \" (Enduro bikes)\");\n        System.out.println(res5);   // >>> [23]\n\n        Object res6 = jedis.jsonGet(\"bike\", new Path2(\"$\"));\n        System.out.println(res6);   // >>> [\"Hyperion (Enduro bikes)\"]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex18","language":"javascript","code":"const res4 = await client.json.strLen(\"bike\", { path: \"$\" });\nconsole.log(res4) //  [10]\n\nconst res5 = await client.json.strAppend(\"bike\", '\" (Enduro bikes)\"');\nconsole.log(res5) //  27\n\nconst res6 = await client.json.get(\"bike\", { path: \"$\" });\nconsole.log(res6) //  ['\"Hyperion\"\" (Enduro bikes)\"']","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex19","language":"php","code":"$res4 = $r->jsonstrlen('bike', '$');\n        echo json_encode($res4) . PHP_EOL;\n        // >>> [8]\n\n        $res5 = $r->jsonstrappend('bike', '$', '\" (Enduro bikes)\"');\n        echo json_encode($res5) . PHP_EOL;\n        // >>> [23]\n\n        $res6 = $r->jsonget('bike', '', '', '', '$');\n        echo $res6 . PHP_EOL;\n        // >>> \"Hyperion (Enduro bikes)\"","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex20","language":"python","code":"res4 = r.json().strlen(\"bike\", \"$\")\nprint(res4)  # >>> [10]\n\nres5 = r.json().strappend(\"bike\", '\" (Enduro bikes)\"')\nprint(res5)  # >>> 27\n\nres6 = r.json().get(\"bike\", \"$\")\nprint(res6)  # >>> ['\"Hyperion\"\" (Enduro bikes)\"']","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex21","language":"ruby","code":"res4 = r.json_strlen('bike', '$')\np res4 # >>> [8]\n\nres5 = r.json_strappend('bike', '$', ' (Enduro bikes)')\np res5 # >>> [23]\n\nres6 = r.json_get('bike', '$')\np res6 # >>> [\"Hyperion (Enduro bikes)\"]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex22","language":"rust","code":"let res4: Value = r\n            .json_str_len(\"bike\", \"$\")\n            .await\n            .expect(\"Failed to run JSON.STRLEN\");\n        print_redis_value(&res4); // >>> [8]\n\n        let res5: Value = r\n            .json_str_append(\"bike\", \"$\", \"\\\" (Enduro bikes)\\\"\")\n            .await\n            .expect(\"Failed to run JSON.STRAPPEND\");\n        print_redis_value(&res5); // >>> [23]\n\n        let res6: String = r\n            .json_get(\"bike\", \"$\")\n            .await\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{res6}\"); // >>> [\"Hyperion (Enduro bikes)\"]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex23","language":"rust","code":"let res4: Value = r\n            .json_str_len(\"bike\", \"$\")\n            .expect(\"Failed to run JSON.STRLEN\");\n        print_redis_value(&res4); // >>> [8]\n\n        let res5: Value = r\n            .json_str_append(\"bike\", \"$\", \"\\\" (Enduro bikes)\\\"\")\n            .expect(\"Failed to run JSON.STRAPPEND\");\n        print_redis_value(&res5); // >>> [23]\n\n        let res6: String = r.json_get(\"bike\", \"$\").expect(\"Failed to run JSON.GET\");\n        println!(\"{res6}\"); // >>> [\"Hyperion (Enduro bikes)\"]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex24","language":"plaintext","code":"> JSON.SET crashes $ 0\nOK\n> JSON.NUMINCRBY crashes $ 1\n\"[1]\"\n> JSON.NUMINCRBY crashes $ 1.5\n\"[2.5]\"\n> JSON.NUMINCRBY crashes $ -0.75\n\"[1.75]\"\n> JSON.NUMMULTBY crashes $ 24\n\"[42.0]\"","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex25","language":"csharp","code":"bool res7 = db.JSON().Set(\"crashes\", \"$\", 0);\n        Console.WriteLine(res7);    // >>> True\n\n        double?[] res8 = db.JSON().NumIncrby(\"crashes\", \"$\", 1);\n        Console.WriteLine(string.Join(\", \", res8));    // >>> 1\n\n        double?[] res9 = db.JSON().NumIncrby(\"crashes\", \"$\", 1.5);\n        Console.WriteLine(string.Join(\", \", res9));    // >>> 2.5\n\n        double?[] res10 = db.JSON().NumIncrby(\"crashes\", \"$\", -0.75);\n        Console.WriteLine(string.Join(\", \", res10));    // >>> 1.75","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex26","language":"go","code":"res7, err := rdb.JSONSet(ctx, \"crashes\", \"$\", 0).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res7) // >>> OK\n\n\tres8, err := rdb.JSONNumIncrBy(ctx, \"crashes\", \"$\", 1).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res8) // >>> [1]\n\n\tres9, err := rdb.JSONNumIncrBy(ctx, \"crashes\", \"$\", 1.5).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res9) // >>> [2.5]\n\n\tres10, err := rdb.JSONNumIncrBy(ctx, \"crashes\", \"$\", -0.75).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res10) // >>> [1.75]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex27","language":"java","code":"CompletableFuture<Void> num = asyncCommands.jsonSet(\"crashes\", JsonPath.ROOT_PATH, parser.createJsonValue(\"0\"))\n                    .thenCompose(res5 -> {\n                        System.out.println(res5); // >>> OK\n\n                        return asyncCommands.jsonNumincrby(\"crashes\", JsonPath.ROOT_PATH, 1);\n                    }).thenCompose(res6 -> {\n                        System.out.println(res6); // >>> [1]\n\n                        return asyncCommands.jsonNumincrby(\"crashes\", JsonPath.ROOT_PATH, 1.5);\n                    }).thenCompose(res7 -> {\n                        System.out.println(res7); // >>> [2.5]\n\n                        return asyncCommands.jsonNumincrby(\"crashes\", JsonPath.ROOT_PATH, -0.75);\n                    })\n                    .thenAccept(System.out::println) // >>> [1.75]\n                    .toCompletableFuture();","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex28","language":"java","code":"Mono<Void> num = reactiveCommands.jsonSet(\"crashes\", JsonPath.ROOT_PATH, parser.createJsonValue(\"0\"))\n                    .doOnNext(res5 -> {\n                        System.out.println(res5); // >>> OK\n                    }).flatMap(res5 -> reactiveCommands.jsonNumincrby(\"crashes\", JsonPath.ROOT_PATH, 1).collectList())\n                    .doOnNext(res6 -> {\n                        System.out.println(res6); // >>> [1]\n                    }).flatMap(res6 -> reactiveCommands.jsonNumincrby(\"crashes\", JsonPath.ROOT_PATH, 1.5).collectList())\n                    .doOnNext(res7 -> {\n                        System.out.println(res7); // >>> [2.5]\n                    }).flatMap(res7 -> reactiveCommands.jsonNumincrby(\"crashes\", JsonPath.ROOT_PATH, -0.75).collectList())\n                    .doOnNext(System.out::println) // >>> [1.75]\n                    .then();","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex29","language":"java","code":"String res7 = jedis.jsonSet(\"crashes\", new Path2(\"$\"), 0);\n        System.out.println(res7);   // >>> OK\n\n        Object res8 = jedis.jsonNumIncrBy(\"crashes\", new Path2(\"$\"), 1);\n        System.out.println(res8);   // >>> [1]\n\n        Object res9 = jedis.jsonNumIncrBy(\"crashes\", new Path2(\"$\"), 1.5);\n        System.out.println(res9);   // >>> [2.5]\n\n        Object res10 = jedis.jsonNumIncrBy(\"crashes\", new Path2(\"$\"), -0.75);\n        System.out.println(res10);   // >>> [1.75]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex30","language":"javascript","code":"const res7 = await client.json.set(\"crashes\", \"$\", 0);\nconsole.log(res7) //  OK\n\nconst res8 = await client.json.numIncrBy(\"crashes\", \"$\", 1);\nconsole.log(res8) //  [1]\n\nconst res9 = await client.json.numIncrBy(\"crashes\", \"$\", 1.5);\nconsole.log(res9) //  [2.5]\n\nconst res10 = await client.json.numIncrBy(\"crashes\", \"$\", -0.75);\nconsole.log(res10) //  [1.75]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex31","language":"php","code":"$res7 = $r->jsonset('crashes', '$', '0');\n        echo $res7 . PHP_EOL;\n        // >>> OK\n\n        $res8 = $r->jsonnumincrby('crashes', '$', 1);\n        echo $res8 . PHP_EOL;\n        // >>> [1]\n\n        $res9 = $r->jsonnumincrby('crashes', '$', 1.5);\n        echo $res9 . PHP_EOL;\n        // >>> [2.5]\n\n        $res10 = $r->jsonnumincrby('crashes', '$', -0.75);\n        echo $res10 . PHP_EOL;\n        // >>> [1.75]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex32","language":"python","code":"res7 = r.json().set(\"crashes\", \"$\", 0)\nprint(res7)  # >>> True\n\nres8 = r.json().numincrby(\"crashes\", \"$\", 1)\nprint(res8)  # >>> [1]\n\nres9 = r.json().numincrby(\"crashes\", \"$\", 1.5)\nprint(res9)  # >>> [2.5]\n\nres10 = r.json().numincrby(\"crashes\", \"$\", -0.75)\nprint(res10)  # >>> [1.75]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex33","language":"ruby","code":"res7 = r.json_set('crashes', '$', 0)\nputs res7 # >>> OK\n\nres8 = r.json_numincrby('crashes', '$', 1)\np res8 # >>> [1]\n\nres9 = r.json_numincrby('crashes', '$', 1.5)\np res9 # >>> [2.5]\n\nres10 = r.json_numincrby('crashes', '$', -0.75)\np res10 # >>> [1.75]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex34","language":"rust","code":"let res7: bool = r\n            .json_set(\"crashes\", \"$\", &json!(0))\n            .await\n            .expect(\"Failed to run JSON.SET\");\n        print_set_result(res7); // >>> OK\n\n        let res8: String = cmd(\"JSON.NUMINCRBY\")\n            .arg(\"crashes\")\n            .arg(\"$\")\n            .arg(1)\n            .query_async(&mut r)\n            .await\n            .expect(\"Failed to run JSON.NUMINCRBY\");\n        println!(\"{res8}\"); // >>> [1]\n\n        let res9: String = cmd(\"JSON.NUMINCRBY\")\n            .arg(\"crashes\")\n            .arg(\"$\")\n            .arg(1.5)\n            .query_async(&mut r)\n            .await\n            .expect(\"Failed to run JSON.NUMINCRBY\");\n        println!(\"{res9}\"); // >>> [2.5]\n\n        let res10: String = cmd(\"JSON.NUMINCRBY\")\n            .arg(\"crashes\")\n            .arg(\"$\")\n            .arg(-0.75)\n            .query_async(&mut r)\n            .await\n            .expect(\"Failed to run JSON.NUMINCRBY\");\n        println!(\"{res10}\"); // >>> [1.75]\n\n        let res11: String = cmd(\"JSON.NUMMULTBY\")\n            .arg(\"crashes\")\n            .arg(\"$\")\n            .arg(24)\n            .query_async(&mut r)\n            .await\n            .expect(\"Failed to run JSON.NUMMULTBY\");\n        println!(\"{res11}\"); // >>> [42.0]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex35","language":"rust","code":"let res7: bool = r\n            .json_set(\"crashes\", \"$\", &json!(0))\n            .expect(\"Failed to run JSON.SET\");\n        print_set_result(res7); // >>> OK\n\n        let res8: String = cmd(\"JSON.NUMINCRBY\")\n            .arg(\"crashes\")\n            .arg(\"$\")\n            .arg(1)\n            .query(&mut r)\n            .expect(\"Failed to run JSON.NUMINCRBY\");\n        println!(\"{res8}\"); // >>> [1]\n\n        let res9: String = cmd(\"JSON.NUMINCRBY\")\n            .arg(\"crashes\")\n            .arg(\"$\")\n            .arg(1.5)\n            .query(&mut r)\n            .expect(\"Failed to run JSON.NUMINCRBY\");\n        println!(\"{res9}\"); // >>> [2.5]\n\n        let res10: String = cmd(\"JSON.NUMINCRBY\")\n            .arg(\"crashes\")\n            .arg(\"$\")\n            .arg(-0.75)\n            .query(&mut r)\n            .expect(\"Failed to run JSON.NUMINCRBY\");\n        println!(\"{res10}\"); // >>> [1.75]\n\n        let res11: String = cmd(\"JSON.NUMMULTBY\")\n            .arg(\"crashes\")\n            .arg(\"$\")\n            .arg(24)\n            .query(&mut r)\n            .expect(\"Failed to run JSON.NUMMULTBY\");\n        println!(\"{res11}\"); // >>> [42.0]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex36","language":"plaintext","code":"> JSON.SET newbike $ '[\"Deimos\", {\"crashes\": 0}, null]'\nOK\n> JSON.GET newbike $\n\"[[\\\"Deimos\\\",{\\\"crashes\\\":0},null]]\"\n> JSON.GET newbike $[1].crashes\n\"[0]\"\n> JSON.DEL newbike $[-1]\n(integer) 1\n> JSON.GET newbike $\n\"[[\\\"Deimos\\\",{\\\"crashes\\\":0}]]\"","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex37","language":"csharp","code":"bool res11 = db.JSON().Set(\"newbike\", \"$\", new object?[] { \"Deimos\", new { crashes = 0 }, null });\n        Console.WriteLine(res11);   // >>> True\n\n        RedisResult res12 = db.JSON().Get(\"newbike\", path: \"$\");\n        Console.WriteLine(res12);   //  >>> [[\"Deimos\",{\"crashes\":0},null]]\n\n        RedisResult res13 = db.JSON().Get(\"newbike\", path: \"$[1].crashes\");\n        Console.WriteLine(res13);   // >>> [0]\n\n        long res14 = db.JSON().Del(\"newbike\", \"$.[-1]\");\n        Console.WriteLine(res14);   // >>> 1\n\n        RedisResult res15 = db.JSON().Get(\"newbike\", path: \"$\");\n        Console.WriteLine(res15);   // >>> [[\"Deimos\",{\"crashes\":0}]]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex38","language":"go","code":"res11, err := rdb.JSONSet(ctx, \"newbike\", \"$\",\n\t\t[]interface{}{\n\t\t\t\"Deimos\",\n\t\t\tmap[string]interface{}{\"crashes\": 0},\n\t\t\tnil,\n\t\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res11) // >>> OK\n\n\tres12, err := rdb.JSONGet(ctx, \"newbike\", \"$\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res12) // >>> [[\"Deimos\",{\"crashes\":0},null]]\n\n\tres13, err := rdb.JSONGet(ctx, \"newbike\", \"$[1].crashes\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res13) // >>> [0]\n\n\tres14, err := rdb.JSONDel(ctx, \"newbike\", \"$.[-1]\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res14) // >>> 1\n\n\tres15, err := rdb.JSONGet(ctx, \"newbike\", \"$\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res15) // >>> [[\"Deimos\",{\"crashes\":0}]]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex39","language":"java","code":"JsonObject crashDetails = parser.createJsonObject();\n            crashDetails.put(\"crashes\", parser.createJsonValue(\"0\"));\n\n            JsonArray bikeDetails = parser.createJsonArray();\n            bikeDetails.add(parser.createJsonValue(\"\\\"Deimos\\\"\"));\n            bikeDetails.add(crashDetails);\n            bikeDetails.add(null);\n\n            CompletableFuture<Void> arr = asyncCommands.jsonSet(\"newbike\", JsonPath.ROOT_PATH, bikeDetails).thenCompose(r -> {\n                System.out.println(r); // >>> OK\n\n                return asyncCommands.jsonGet(\"newbike\", JsonPath.ROOT_PATH);\n            }).thenCompose(res8 -> {\n                System.out.println(res8);\n                // >>> [[\"Deimos\",{\"crashes\":0},null]]\n\n                return asyncCommands.jsonGet(\"newbike\", JsonPath.of(\"$[1].crashes\"));\n            }).thenCompose(res9 -> {\n                System.out.println(res9); // >>> [[0]]\n\n                return asyncCommands.jsonDel(\"newbike\", JsonPath.of(\"$.[-1]\"));\n            }).thenCompose(res10 -> {\n                System.out.println(res10); // >>> 1\n\n                return asyncCommands.jsonGet(\"newbike\", JsonPath.ROOT_PATH);\n            })\n                    .thenAccept(System.out::println)\n                    // >>> [[[\\\"Deimos\\\",{\\\"crashes\\\":0}]]]\n                    .toCompletableFuture();","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex40","language":"java","code":"JsonObject crashDetails = parser.createJsonObject();\n            crashDetails.put(\"crashes\", parser.createJsonValue(\"0\"));\n\n            JsonArray bikeDetails = parser.createJsonArray();\n            bikeDetails.add(parser.createJsonValue(\"\\\"Deimos\\\"\"));\n            bikeDetails.add(crashDetails);\n            bikeDetails.add(null);\n\n            Mono<Void> arr = reactiveCommands.jsonSet(\"newbike\", JsonPath.ROOT_PATH, bikeDetails).doOnNext(r -> {\n                System.out.println(r); // >>> OK\n            }).flatMap(r -> reactiveCommands.jsonGet(\"newbike\", JsonPath.ROOT_PATH).collectList()).doOnNext(res8 -> {\n                System.out.println(res8);\n                // >>> [[\"Deimos\",{\"crashes\":0},null]]\n            }).flatMap(res8 -> reactiveCommands.jsonGet(\"newbike\", JsonPath.of(\"$[1].crashes\")).collectList())\n                    .doOnNext(res9 -> {\n                        System.out.println(res9); // >>> [[0]]\n                    }).flatMap(res9 -> reactiveCommands.jsonDel(\"newbike\", JsonPath.of(\"$.[-1]\"))).doOnNext(res10 -> {\n                        System.out.println(res10); // >>> 1\n                    }).flatMap(res10 -> reactiveCommands.jsonGet(\"newbike\", JsonPath.ROOT_PATH).collectList())\n                    .doOnNext(System.out::println) // >>> [[[\\\"Deimos\\\",{\\\"crashes\\\":0}]]]\n                    .then();","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex41","language":"java","code":"String res11 = jedis.jsonSet(\"newbike\", new Path2(\"$\"),\n            new JSONArray()\n                .put(\"Deimos\")\n                .put(new JSONObject().put(\"crashes\", 0))\n                .put((Object) null)\n        );\n        System.out.println(res11);  // >>> OK\n        \n        Object res12 = jedis.jsonGet(\"newbike\", new Path2(\"$\"));\n        System.out.println(res12);  // >>> [[\"Deimos\",{\"crashes\":0},null]]\n\n        Object res13 = jedis.jsonGet(\"newbike\", new Path2(\"$[1].crashes\"));\n        System.out.println(res13);  // >>> [0]\n\n        long res14 = jedis.jsonDel(\"newbike\", new Path2(\"$.[-1]\"));\n        System.out.println(res14);  // >>> 1\n\n        Object res15 = jedis.jsonGet(\"newbike\", new Path2(\"$\"));\n        System.out.println(res15);  // >>> [[\"Deimos\",{\"crashes\":0}]]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex42","language":"javascript","code":"const res11 = await client.json.set(\"newbike\", \"$\", [\"Deimos\", {\"crashes\": 0 }, null]);\nconsole.log(res11); //  OK\n\nconst res12 = await client.json.get(\"newbike\", { path: \"$\" });\nconsole.log(res12); //  [[ 'Deimos', { crashes: 0 }, null ]]\n\nconst res13 = await client.json.get(\"newbike\", { path: \"$[1].crashes\" });\nconsole.log(res13); //  [0]\n\nconst res14 = await client.json.del(\"newbike\", { path: \"$.[-1]\"} );\nconsole.log(res14); //  1\n\nconst res15 = await client.json.get(\"newbike\", { path: \"$\" });\nconsole.log(res15); //  [[ 'Deimos', { crashes: 0 } ]]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex43","language":"php","code":"$newbike = json_encode([\"Deimos\", [\"crashes\" => 0], null], JSON_THROW_ON_ERROR);\n        $res11 = $r->jsonset('newbike', '$', $newbike);\n        echo $res11 . PHP_EOL;\n        // >>> OK\n\n        $res12 = $r->jsonget('newbike', '', '', '', '$');\n        echo $res12 . PHP_EOL;\n        // >>> [[\"Deimos\",{\"crashes\":0},null]]\n\n        $res13 = $r->jsonget('newbike', '', '', '', '$[1].crashes');\n        echo $res13 . PHP_EOL;\n        // >>> 0\n\n        $res14 = $r->jsondel('newbike', '$.[-1]');\n        echo $res14 . PHP_EOL;\n        // >>> 1\n\n        $res15 = $r->jsonget('newbike', '', '', '', '$');\n        echo $res15 . PHP_EOL;\n        // >>> [\"Deimos\",{\"crashes\":0}]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex44","language":"python","code":"res11 = r.json().set(\"newbike\", \"$\", [\"Deimos\", {\"crashes\": 0}, None])\nprint(res11)  # >>> True\n\nres12 = r.json().get(\"newbike\", \"$\")\nprint(res12)  # >>> ['[\"Deimos\", { \"crashes\": 0 }, null]']\n\nres13 = r.json().get(\"newbike\", \"$[1].crashes\")\nprint(res13)  # >>> [0]\n\nres14 = r.json().delete(\"newbike\", \"$.[-1]\")\nprint(res14)  # >>> [1]\n\nres15 = r.json().get(\"newbike\", \"$\")\nprint(res15)  # >>> [['Deimos', {'crashes': 0}]]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex45","language":"ruby","code":"res11 = r.json_set('newbike', '$', ['Deimos', { 'crashes' => 0 }, nil])\nputs res11 # >>> OK\n\nres12 = r.json_get('newbike', '$')\np res12 # >>> [[\"Deimos\", {\"crashes\"=>0}, nil]]\n\nres13 = r.json_get('newbike', '$[1].crashes')\np res13 # >>> [0]\n\nres14 = r.json_del('newbike', '$.[-1]')\np res14 # >>> 1\n\nres15 = r.json_get('newbike', '$')\np res15 # >>> [[\"Deimos\", {\"crashes\"=>0}]]\n\n# The same raw: true option returns the array as unparsed JSON text.\nres_raw3 = r.json_get('newbike', '$', raw: true)\nputs res_raw3 # >>> [[\"Deimos\",{\"crashes\":0}]]  (a JSON string)","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex46","language":"rust","code":"let res12: bool = r\n            .json_set(\"newbike\", \"$\", &json!([\"Deimos\", {\"crashes\": 0}, null]))\n            .await\n            .expect(\"Failed to run JSON.SET\");\n        print_set_result(res12); // >>> OK\n\n        let res13: String = r\n            .json_get(\"newbike\", \"$\")\n            .await\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{res13}\"); // >>> [[\"Deimos\",{\"crashes\":0},null]]\n\n        let res14: String = r\n            .json_get(\"newbike\", \"$[1].crashes\")\n            .await\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{res14}\"); // >>> [0]\n\n        let res15: i64 = r\n            .json_del(\"newbike\", \"$[-1]\")\n            .await\n            .expect(\"Failed to run JSON.DEL\");\n        println!(\"{res15}\"); // >>> 1\n\n        let res16: String = r\n            .json_get(\"newbike\", \"$\")\n            .await\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{res16}\"); // >>> [[\"Deimos\",{\"crashes\":0}]]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex47","language":"rust","code":"let res12: bool = r\n            .json_set(\"newbike\", \"$\", &json!([\"Deimos\", {\"crashes\": 0}, null]))\n            .expect(\"Failed to run JSON.SET\");\n        print_set_result(res12); // >>> OK\n\n        let res13: String = r.json_get(\"newbike\", \"$\").expect(\"Failed to run JSON.GET\");\n        println!(\"{res13}\"); // >>> [[\"Deimos\",{\"crashes\":0},null]]\n\n        let res14: String = r\n            .json_get(\"newbike\", \"$[1].crashes\")\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{res14}\"); // >>> [0]\n\n        let res15: i64 = r\n            .json_del(\"newbike\", \"$[-1]\")\n            .expect(\"Failed to run JSON.DEL\");\n        println!(\"{res15}\"); // >>> 1\n\n        let res16: String = r.json_get(\"newbike\", \"$\").expect(\"Failed to run JSON.GET\");\n        println!(\"{res16}\"); // >>> [[\"Deimos\",{\"crashes\":0}]]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex48","language":"plaintext","code":"> JSON.SET fp_array $ '[[1,2,3,4e3],[5,6.0,7,8]]' FPHA FP16\nOK\n> JSON.GET fp_array $\n\"[[[1.0,2.0,3.0,4000.0],[5.0,6.0,7.0,8.0]]]\"","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex49","language":"plaintext","code":"> JSON.SET riders $ []\nOK\n> JSON.ARRAPPEND riders $ '\"Norem\"'\n1) (integer) 1\n> JSON.GET riders $\n\"[[\\\"Norem\\\"]]\"\n> JSON.ARRINSERT riders $ 1 '\"Prickett\"' '\"Royce\"' '\"Castilla\"'\n1) (integer) 4\n> JSON.GET riders $\n\"[[\\\"Norem\\\",\\\"Prickett\\\",\\\"Royce\\\",\\\"Castilla\\\"]]\"\n> JSON.ARRTRIM riders $ 1 1\n1) (integer) 1\n> JSON.GET riders $\n\"[[\\\"Prickett\\\"]]\"\n> JSON.ARRPOP riders $\n1) \"\\\"Prickett\\\"\"\n> JSON.ARRPOP riders $\n1) (nil)","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex50","language":"csharp","code":"bool res16 = db.JSON().Set(\"riders\", \"$\", new object[] { });\n        Console.WriteLine(res16);   // >>> True\n\n        long?[] res17 = db.JSON().ArrAppend(\"riders\", \"$\", \"Norem\");\n        Console.WriteLine(string.Join(\", \", res17));    // >>> 1\n\n        RedisResult res18 = db.JSON().Get(\"riders\", path: \"$\");\n        Console.WriteLine(res18);   // >>> [[\"Norem\"]]\n\n        long?[] res19 = db.JSON().ArrInsert(\"riders\", \"$\", 1, \"Prickett\", \"Royce\", \"Castilla\");\n        Console.WriteLine(string.Join(\", \", res19));    // >>> 4\n\n        RedisResult res20 = db.JSON().Get(\"riders\", path: \"$\");\n        Console.WriteLine(res20);   // >>> [[\"Norem\",\"Prickett\",\"Royce\",\"Castilla\"]]\n\n        long?[] res21 = db.JSON().ArrTrim(\"riders\", \"$\", 1, 1);\n        Console.WriteLine(string.Join(\", \", res21));    // 1\n\n        RedisResult res22 = db.JSON().Get(\"riders\", path: \"$\");\n        Console.WriteLine(res22);   // >>> [[\"Prickett\"]]\n\n        RedisResult[] res23 = db.JSON().ArrPop(\"riders\", \"$\");\n        Console.WriteLine(string.Join(\", \", (object[])res23)); // >>> \"Prickett\"\n\n        RedisResult[] res24 = db.JSON().ArrPop(\"riders\", \"$\");\n        Console.WriteLine(string.Join(\", \", (object[])res24)); // >>> <Empty string>","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex51","language":"go","code":"res16, err := rdb.JSONSet(ctx, \"riders\", \"$\", []interface{}{}).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res16) // >>> OK\n\n\tres17, err := rdb.JSONArrAppend(ctx, \"riders\", \"$\", \"\\\"Norem\\\"\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res17) // >>> [1]\n\n\tres18, err := rdb.JSONGet(ctx, \"riders\", \"$\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res18) // >>> [[\"Norem\"]]\n\n\tres19, err := rdb.JSONArrInsert(ctx, \"riders\", \"$\", 1,\n\t\t\"\\\"Prickett\\\"\", \"\\\"Royce\\\"\", \"\\\"Castilla\\\"\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res19) // [3]\n\n\tres20, err := rdb.JSONGet(ctx, \"riders\", \"$\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res20) // >>> [[\"Norem\", \"Prickett\", \"Royce\", \"Castilla\"]]\n\n\trangeStop := 1\n\n\tres21, err := rdb.JSONArrTrimWithArgs(ctx, \"riders\", \"$\",\n\t\t&redis.JSONArrTrimArgs{Start: 1, Stop: &rangeStop},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res21) // >>> [1]\n\n\tres22, err := rdb.JSONGet(ctx, \"riders\", \"$\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res22) // >>> [[\"Prickett\"]]\n\n\tres23, err := rdb.JSONArrPop(ctx, \"riders\", \"$\", -1).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res23) // >>> [[\"Prickett\"]]\n\n\tres24, err := rdb.JSONArrPop(ctx, \"riders\", \"$\", -1).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res24) // []","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex52","language":"java","code":"CompletableFuture<Void> arr2 = asyncCommands.jsonSet(\"riders\", JsonPath.ROOT_PATH, parser.createJsonArray())\n                    .thenCompose(r -> {\n                        System.out.println(r); // >>> OK\n\n                        return asyncCommands.jsonArrinsert(\"riders\", JsonPath.ROOT_PATH, 0,\n                                parser.createJsonValue(\"\\\"Norem\\\"\"));\n                    }).thenCompose(res11 -> {\n                        System.out.println(res11); // >>> [1]\n\n                        return asyncCommands.jsonGet(\"riders\", JsonPath.ROOT_PATH);\n                    })\n\n                    .thenCompose(res12 -> {\n                        System.out.println(res12); // >>> [\"Norem\"]\n\n                        return asyncCommands.jsonArrinsert(\"riders\", JsonPath.ROOT_PATH, 1,\n                                parser.createJsonValue(\"\\\"Prickett\\\"\"), parser.createJsonValue(\"\\\"Royce\\\"\"),\n                                parser.createJsonValue(\"\\\"Castilla\\\"\"));\n                    }).thenCompose(res13 -> {\n                        System.out.println(res13); // >>> [4]\n\n                        return asyncCommands.jsonGet(\"riders\", JsonPath.ROOT_PATH);\n                    }).thenCompose(res14 -> {\n                        System.out.println(res14); // >>> [\"Norem\",\"Prickett\",\"Royce\",\"Castilla\"]\n                        //\n                        return asyncCommands.jsonArrtrim(\"riders\", JsonPath.ROOT_PATH, new JsonRangeArgs().start(1).stop(1));\n                    }).thenCompose(res15 -> {\n                        System.out.println(res15); // >>> [1]\n\n                        return asyncCommands.jsonGet(\"riders\", JsonPath.ROOT_PATH);\n                    }).thenCompose(res16 -> {\n                        System.out.println(res16); // >>> [[[\"Prickett\"]]]\n                        return asyncCommands.jsonArrpop(\"riders\", JsonPath.ROOT_PATH, 0);\n                    }).thenCompose(res17 -> {\n                        System.out.println(res17); // >>> [\"Prickett\"]\n                        return asyncCommands.jsonArrpop(\"riders\", JsonPath.ROOT_PATH);\n                    })\n                    .thenAccept(System.out::println)\n                    // >>> null\n                    .toCompletableFuture();","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex53","language":"java","code":"Mono<Void> arr2 = reactiveCommands.jsonSet(\"riders\", JsonPath.ROOT_PATH, parser.createJsonArray()).doOnNext(r -> {\n                System.out.println(r); // >>> OK\n            }).flatMap(r -> reactiveCommands.jsonArrinsert(\"riders\", JsonPath.ROOT_PATH, 0, parser.createJsonValue(\"\\\"Norem\\\"\"))\n                    .collectList()).doOnNext(res11 -> {\n                        System.out.println(res11); // >>> [1]\n                    }).flatMap(res11 -> reactiveCommands.jsonGet(\"riders\", JsonPath.ROOT_PATH).collectList())\n                    .doOnNext(res12 -> {\n                        System.out.println(res12); // >>> [\"Norem\"]\n                    })\n                    .flatMap(\n                            res12 -> reactiveCommands\n                                    .jsonArrinsert(\"riders\", JsonPath.ROOT_PATH, 1, parser.createJsonValue(\"\\\"Prickett\\\"\"),\n                                            parser.createJsonValue(\"\\\"Royce\\\"\"), parser.createJsonValue(\"\\\"Castilla\\\"\"))\n                                    .collectList())\n                    .doOnNext(res13 -> {\n                        System.out.println(res13); // >>> [4]\n                    }).flatMap(res13 -> reactiveCommands.jsonGet(\"riders\", JsonPath.ROOT_PATH).collectList())\n                    .doOnNext(System.out::println) // >>> [[\"Norem\",\"Prickett\",\"Royce\",\"Castilla\"]]\n                    .flatMap(res14 -> reactiveCommands\n                            .jsonArrtrim(\"riders\", JsonPath.ROOT_PATH, new JsonRangeArgs().start(1).stop(1)).collectList())\n                    .doOnNext(res15 -> {\n                        System.out.println(res15); // >>> [1]\n                    }).flatMap(res15 -> reactiveCommands.jsonGet(\"riders\", JsonPath.ROOT_PATH).collectList())\n                    .doOnNext(res16 -> {\n                        System.out.println(res16); // >>> [[[\"Prickett\"]]]\n                    }).flatMap(res16 -> reactiveCommands.jsonArrpop(\"riders\", JsonPath.ROOT_PATH, 0).collectList())\n                    .doOnNext(res17 -> {\n                        System.out.println(res17); // >>> [\"Prickett\"]\n                    }).flatMap(res17 -> reactiveCommands.jsonArrpop(\"riders\", JsonPath.ROOT_PATH).collectList())\n                    .doOnNext(System.out::println) // >>> null\n                    .then();","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex54","language":"java","code":"String res16 = jedis.jsonSet(\"riders\", new Path2(\"$\"), new JSONArray());\n        System.out.println(res16);  // >>> OK\n\n        List<Long> res17 = jedis.jsonArrAppendWithEscape(\"riders\", new Path2(\"$\"), \"Norem\");\n        System.out.println(res17);  // >>> [1]\n\n        Object res18 = jedis.jsonGet(\"riders\", new Path2(\"$\"));\n        System.out.println(res18);  // >>> [[\"Norem\"]]\n\n        List<Long> res19 = jedis.jsonArrInsertWithEscape(\n            \"riders\", new Path2(\"$\"), 1, \"Prickett\", \"Royce\", \"Castilla\"\n        );\n        System.out.println(res19);  // >>> [4]\n\n        Object res20 = jedis.jsonGet(\"riders\", new Path2(\"$\"));\n        System.out.println(res20);\n        // >>> [[\"Norem\",\"Prickett\",\"Royce\",\"Castilla\"]]\n        \n        List<Long> res21 = jedis.jsonArrTrim(\"riders\", new Path2(\"$\"), 1, 1);\n        System.out.println(res21);  // >>> [1]\n\n        Object res22 = jedis.jsonGet(\"riders\", new Path2(\"$\"));\n        System.out.println(res22);  // >>> [[\"Prickett\"]]\n\n        Object res23 = jedis.jsonArrPop(\"riders\", new Path2(\"$\"));\n        System.out.println(res23);  // >>> [Prickett]\n\n        Object res24 = jedis.jsonArrPop(\"riders\", new Path2(\"$\"));\n        System.out.println(res24);  // >>> [null]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex55","language":"javascript","code":"const res16 = await client.json.set(\"riders\", \"$\", []);\nconsole.log(res16); //  OK\n\nconst res17 = await client.json.arrAppend(\"riders\", \"$\", \"Norem\");\nconsole.log(res17); //  [1]\n\nconst res18 = await client.json.get(\"riders\", { path: \"$\" });\nconsole.log(res18); //  [[ 'Norem' ]]\n\nconst res19 = await client.json.arrInsert(\"riders\", \"$\", 1, \"Prickett\", \"Royse\", \"Castilla\");\nconsole.log(res19); //  [4]\n\nconst res20 = await client.json.get(\"riders\", { path: \"$\" });\nconsole.log(res20); //  [[ 'Norem', 'Prickett', 'Royse', 'Castilla' ]]\n\nconst res21 = await client.json.arrTrim(\"riders\", \"$\", 1, 1);\nconsole.log(res21); //  [1]\n\nconst res22 = await client.json.get(\"riders\", { path: \"$\" });\nconsole.log(res22); //  [[ 'Prickett' ]]\n\nconst res23 = await client.json.arrPop(\"riders\", { path: \"$\" });\nconsole.log(res23); //  [ 'Prickett' ]\n\nconst res24 = await client.json.arrPop(\"riders\", { path: \"$\" });\nconsole.log(res24); //  [null]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex56","language":"php","code":"$res16 = $r->jsonset('riders', '$', '[]');\n        echo $res16 . PHP_EOL;\n        // >>> OK\n\n        $res17 = $r->jsonarrappend('riders', '$', '\"Norem\"');\n        echo json_encode($res17) . PHP_EOL;\n        // >>> [1]\n\n        $res18 = $r->jsonget('riders', '', '', '', '$');\n        echo $res18 . PHP_EOL;\n        // >>> [\"Norem\"]\n\n        $res19 = $r->jsonarrinsert('riders', '$', 1, '\"Prickett\"', '\"Royce\"', '\"Castilla\"');\n        echo json_encode($res19) . PHP_EOL;\n        // >>> [4]\n\n        $res20 = $r->jsonget('riders', '', '', '', '$');\n        echo $res20 . PHP_EOL;\n        // >>> [\"Norem\",\"Prickett\",\"Royce\",\"Castilla\"]\n\n        $res21 = $r->jsonarrtrim('riders', '$', 1, 1);\n        echo json_encode($res21) . PHP_EOL;\n        // >>> [1]\n\n        $res22 = $r->jsonget('riders', '', '', '', '$');\n        echo $res22 . PHP_EOL;\n        // >>> [\"Prickett\"]\n\n        $res23 = $r->jsonarrpop('riders', '$');\n        echo json_encode($res23) . PHP_EOL;\n        // >>> [\"\\\"Prickett\\\"\"]\n\n        $res24 = $r->jsonarrpop('riders', '$');\n        echo json_encode($res24) . PHP_EOL;\n        // >>> [null]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex57","language":"python","code":"res16 = r.json().set(\"riders\", \"$\", [])\nprint(res16)  # >>> True\n\nres17 = r.json().arrappend(\"riders\", \"$\", \"Norem\")\nprint(res17)  # >>> [1]\n\nres18 = r.json().get(\"riders\", \"$\")\nprint(res18)  # >>> [['Norem']]\n\nres19 = r.json().arrinsert(\"riders\", \"$\", 1, \"Prickett\", \"Royce\", \"Castilla\")\nprint(res19)  # >>> [4]\n\nres20 = r.json().get(\"riders\", \"$\")\nprint(res20)  # >>> [['Norem', 'Prickett', 'Royce', 'Castilla']]\n\nres21 = r.json().arrtrim(\"riders\", \"$\", 1, 1)\nprint(res21)  # >>> [1]\n\nres22 = r.json().get(\"riders\", \"$\")\nprint(res22)  # >>> [['Prickett']]\n\nres23 = r.json().arrpop(\"riders\", \"$\")\nprint(res23)  # >>> ['\"Prickett\"']\n\nres24 = r.json().arrpop(\"riders\", \"$\")\nprint(res24)  # >>> [None]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex58","language":"ruby","code":"res16 = r.json_set('riders', '$', [])\nputs res16 # >>> OK\n\nres17 = r.json_arrappend('riders', '$', 'Norem')\np res17 # >>> [1]\n\nres18 = r.json_get('riders', '$')\np res18 # >>> [[\"Norem\"]]\n\nres19 = r.json_arrinsert('riders', '$', 1, 'Prickett', 'Royce', 'Castilla')\np res19 # >>> [4]\n\nres20 = r.json_get('riders', '$')\np res20 # >>> [[\"Norem\", \"Prickett\", \"Royce\", \"Castilla\"]]\n\nres21 = r.json_arrtrim('riders', '$', 1, 1)\np res21 # >>> [1]\n\nres22 = r.json_get('riders', '$')\np res22 # >>> [[\"Prickett\"]]\n\nres23 = r.json_arrpop('riders', '$')\np res23 # >>> [\"Prickett\"]\n\nres24 = r.json_arrpop('riders', '$')\np res24 # >>> [nil]\n\n# json_arrappend also takes a pre-encoded JSON value with raw: true.\nres_raw4 = r.json_arrappend('riders', '$', '\"Castilla\"', raw: true)\np res_raw4 # >>> [1]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex59","language":"rust","code":"let res17: bool = r\n            .json_set(\"riders\", \"$\", &json!([]))\n            .await\n            .expect(\"Failed to run JSON.SET\");\n        print_set_result(res17); // >>> OK\n\n        let res18: Value = r\n            .json_arr_append(\"riders\", \"$\", &json!(\"Norem\"))\n            .await\n            .expect(\"Failed to run JSON.ARRAPPEND\");\n        print_redis_value(&res18); // >>> [1]\n\n        let res19: String = r\n            .json_get(\"riders\", \"$\")\n            .await\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{res19}\"); // >>> [[\"Norem\"]]\n\n        let res20: Value = cmd(\"JSON.ARRINSERT\")\n            .arg(\"riders\")\n            .arg(\"$\")\n            .arg(1)\n            .arg(\"\\\"Prickett\\\"\")\n            .arg(\"\\\"Royce\\\"\")\n            .arg(\"\\\"Castilla\\\"\")\n            .query_async(&mut r)\n            .await\n            .expect(\"Failed to run JSON.ARRINSERT\");\n        print_redis_value(&res20); // >>> [4]\n\n        let res21: String = r\n            .json_get(\"riders\", \"$\")\n            .await\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{res21}\"); // >>> [[\"Norem\",\"Prickett\",\"Royce\",\"Castilla\"]]\n\n        let res22: Value = r\n            .json_arr_trim(\"riders\", \"$\", 1, 1)\n            .await\n            .expect(\"Failed to run JSON.ARRTRIM\");\n        print_redis_value(&res22); // >>> [1]\n\n        let res23: String = r\n            .json_get(\"riders\", \"$\")\n            .await\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{res23}\"); // >>> [[\"Prickett\"]]\n\n        let res24: Value = r\n            .json_arr_pop(\"riders\", \"$\", -1)\n            .await\n            .expect(\"Failed to run JSON.ARRPOP\");\n        print_redis_value(&res24); // >>> [\"Prickett\"]\n\n        let res25: Value = r\n            .json_arr_pop(\"riders\", \"$\", -1)\n            .await\n            .expect(\"Failed to run JSON.ARRPOP\");\n        print_redis_value(&res25); // >>> [null]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex60","language":"rust","code":"let res17: bool = r\n            .json_set(\"riders\", \"$\", &json!([]))\n            .expect(\"Failed to run JSON.SET\");\n        print_set_result(res17); // >>> OK\n\n        let res18: Value = r\n            .json_arr_append(\"riders\", \"$\", &json!(\"Norem\"))\n            .expect(\"Failed to run JSON.ARRAPPEND\");\n        print_redis_value(&res18); // >>> [1]\n\n        let res19: String = r.json_get(\"riders\", \"$\").expect(\"Failed to run JSON.GET\");\n        println!(\"{res19}\"); // >>> [[\"Norem\"]]\n\n        let res20: Value = cmd(\"JSON.ARRINSERT\")\n            .arg(\"riders\")\n            .arg(\"$\")\n            .arg(1)\n            .arg(\"\\\"Prickett\\\"\")\n            .arg(\"\\\"Royce\\\"\")\n            .arg(\"\\\"Castilla\\\"\")\n            .query(&mut r)\n            .expect(\"Failed to run JSON.ARRINSERT\");\n        print_redis_value(&res20); // >>> [4]\n\n        let res21: String = r.json_get(\"riders\", \"$\").expect(\"Failed to run JSON.GET\");\n        println!(\"{res21}\"); // >>> [[\"Norem\",\"Prickett\",\"Royce\",\"Castilla\"]]\n\n        let res22: Value = r\n            .json_arr_trim(\"riders\", \"$\", 1, 1)\n            .expect(\"Failed to run JSON.ARRTRIM\");\n        print_redis_value(&res22); // >>> [1]\n\n        let res23: String = r.json_get(\"riders\", \"$\").expect(\"Failed to run JSON.GET\");\n        println!(\"{res23}\"); // >>> [[\"Prickett\"]]\n\n        let res24: Value = r\n            .json_arr_pop(\"riders\", \"$\", -1)\n            .expect(\"Failed to run JSON.ARRPOP\");\n        print_redis_value(&res24); // >>> [\"Prickett\"]\n\n        let res25: Value = r\n            .json_arr_pop(\"riders\", \"$\", -1)\n            .expect(\"Failed to run JSON.ARRPOP\");\n        print_redis_value(&res25); // >>> [null]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex61","language":"plaintext","code":"> JSON.SET bike:1 $ '{\"model\": \"Deimos\", \"brand\": \"Ergonom\", \"price\": 4972}'\nOK\n> JSON.OBJLEN bike:1 $\n1) (integer) 3\n> JSON.OBJKEYS bike:1 $\n1) 1) \"model\"\n   2) \"brand\"\n   3) \"price\"","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex62","language":"csharp","code":"bool res25 = db.JSON().Set(\"bike:1\", \"$\",\n            new { model = \"Deimos\", brand = \"Ergonom\", price = 4972 }\n        );\n        Console.WriteLine(res25);   // >>> True\n\n        long?[] res26 = db.JSON().ObjLen(\"bike:1\", \"$\");\n        Console.WriteLine(string.Join(\", \", res26));    // >>> 3\n\n        IEnumerable<HashSet<string>> res27 = db.JSON().ObjKeys(\"bike:1\", \"$\");\n        Console.WriteLine(\n            string.Join(\", \", res27.Select(b => $\"{string.Join(\", \", b.Select(c => $\"{c}\"))}\"))\n        ); // >>> model, brand, price","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex63","language":"go","code":"res25, err := rdb.JSONSet(ctx, \"bike:1\", \"$\",\n\t\tmap[string]interface{}{\n\t\t\t\"model\": \"Deimos\",\n\t\t\t\"brand\": \"Ergonom\",\n\t\t\t\"price\": 4972,\n\t\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res25) // >>> OK\n\n\tres26, err := rdb.JSONObjLen(ctx, \"bike:1\", \"$\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(*res26[0]) // >>> 3\n\n\tres27, err := rdb.JSONObjKeys(ctx, \"bike:1\", \"$\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res27) // >>> [brand model price]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex64","language":"java","code":"JsonObject bikeObj = parser.createJsonObject().put(\"model\", parser.createJsonValue(\"\\\"Deimos\\\"\"))\n                    .put(\"brand\", parser.createJsonValue(\"\\\"Ergonom\\\"\")).put(\"price\", parser.createJsonValue(\"\\\"4972\\\"\"));\n\n            CompletableFuture<Void> obj = asyncCommands.jsonSet(\"bike:1\", JsonPath.ROOT_PATH, bikeObj).thenCompose(r -> {\n                System.out.println(r); // >>> OK\n\n                return asyncCommands.jsonObjlen(\"bike:1\", JsonPath.ROOT_PATH);\n            }).thenCompose(res18 -> {\n                System.out.println(res18); // >>> [3]\n\n                return asyncCommands.jsonObjkeys(\"bike:1\", JsonPath.ROOT_PATH);\n            })\n                    .thenAccept(System.out::println)\n                    // >>> [model, brand, price]\n                    .toCompletableFuture();","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex65","language":"java","code":"JsonObject bikeObj = parser.createJsonObject().put(\"model\", parser.createJsonValue(\"\\\"Deimos\\\"\"))\n                    .put(\"brand\", parser.createJsonValue(\"\\\"Ergonom\\\"\")).put(\"price\", parser.createJsonValue(\"\\\"4972\\\"\"));\n\n            Mono<Void> obj = reactiveCommands.jsonSet(\"bike:1\", JsonPath.ROOT_PATH, bikeObj).doOnNext(r -> {\n                System.out.println(r); // >>> OK\n            }).flatMap(r -> reactiveCommands.jsonObjlen(\"bike:1\", JsonPath.ROOT_PATH).collectList()).doOnNext(res18 -> {\n                System.out.println(res18); // >>> [3]\n            }).flatMap(res18 -> reactiveCommands.jsonObjkeys(\"bike:1\", JsonPath.ROOT_PATH).collectList())\n                    .doOnNext(System.out::println) // >>> [model, brand, price]\n                    .then();","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex66","language":"java","code":"String res25 = jedis.jsonSet(\"bike:1\", new Path2(\"$\"),\n            new JSONObject()\n                .put(\"model\", \"Deimos\")\n                .put(\"brand\", \"Ergonom\")\n                .put(\"price\", 4972)\n        );\n        System.out.println(res25);  // >>> OK\n\n        List<Long> res26 = jedis.jsonObjLen(\"bike:1\", new Path2(\"$\"));\n        System.out.println(res26);  // >>> [3]\n\n        List<List<String>> res27 = jedis.jsonObjKeys(\"bike:1\", new Path2(\"$\"));\n        System.out.println(res27);  // >>> [[price, model, brand]]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex67","language":"javascript","code":"const res25 = await client.json.set(\n  \"bike:1\", \"$\", {\n    \"model\": \"Deimos\",\n    \"brand\": \"Ergonom\",\n    \"price\": 4972\n  }\n);\nconsole.log(res25); //  OK\n\nconst res26 = await client.json.objLen(\"bike:1\", { path: \"$\" });\nconsole.log(res26); //  [3]\n\nconst res27 = await client.json.objKeys(\"bike:1\", { path: \"$\" });\nconsole.log(res27); //  [['model', 'brand', 'price']]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex68","language":"php","code":"$bike1 = json_encode([\n            'model' => 'Deimos',\n            'brand' => 'Ergonom',\n            'price' => 4972,\n        ], JSON_THROW_ON_ERROR);\n        $res25 = $r->jsonset('bike:1', '$', $bike1);\n        echo $res25 . PHP_EOL;\n        // >>> OK\n\n        $res26 = $r->jsonobjlen('bike:1', '$');\n        echo json_encode($res26) . PHP_EOL;\n        // >>> [3]\n\n        $res27 = $r->jsonobjkeys('bike:1', '$');\n        echo json_encode($res27) . PHP_EOL;\n        // >>> [[\"model\",\"brand\",\"price\"]]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex69","language":"python","code":"res25 = r.json().set(\n    \"bike:1\", \"$\", {\"model\": \"Deimos\", \"brand\": \"Ergonom\", \"price\": 4972}\n)\nprint(res25)  # >>> True\n\nres26 = r.json().objlen(\"bike:1\", \"$\")\nprint(res26)  # >>> [3]\n\nres27 = r.json().objkeys(\"bike:1\", \"$\")\nprint(res27)  # >>> [['model', 'brand', 'price']]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex70","language":"ruby","code":"res25 = r.json_set('bike:1', '$', { 'model' => 'Deimos', 'brand' => 'Ergonom', 'price' => 4972 })\nputs res25 # >>> OK\n\nres26 = r.json_objlen('bike:1', '$')\np res26 # >>> [3]\n\nres27 = r.json_objkeys('bike:1', '$')\np res27 # >>> [[\"model\", \"brand\", \"price\"]]\n\n# raw: true returns the object as unparsed JSON text.\nres_raw5 = r.json_get('bike:1', '$', raw: true)\nputs res_raw5 # >>> [{\"model\":\"Deimos\",\"brand\":\"Ergonom\",\"price\":4972}]  (a JSON string)","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex71","language":"rust","code":"let res26: bool = r\n            .json_set(\n                \"bike:1\",\n                \"$\",\n                &json!({\"model\": \"Deimos\", \"brand\": \"Ergonom\", \"price\": 4972}),\n            )\n            .await\n            .expect(\"Failed to run JSON.SET\");\n        print_set_result(res26); // >>> OK\n\n        let res27: Value = r\n            .json_obj_len(\"bike:1\", \"$\")\n            .await\n            .expect(\"Failed to run JSON.OBJLEN\");\n        print_redis_value(&res27); // >>> [3]\n\n        let res28: Value = r\n            .json_obj_keys(\"bike:1\", \"$\")\n            .await\n            .expect(\"Failed to run JSON.OBJKEYS\");\n        print_redis_value(&res28); // >>> [[\"brand\",\"model\",\"price\"]]","section_id":"use-redis-with-json"},{"id":"use-redis-with-json-ex72","language":"rust","code":"let res26: bool = r\n            .json_set(\n                \"bike:1\",\n                \"$\",\n                &json!({\"model\": \"Deimos\", \"brand\": \"Ergonom\", \"price\": 4972}),\n            )\n            .expect(\"Failed to run JSON.SET\");\n        print_set_result(res26); // >>> OK\n\n        let res27: Value = r\n            .json_obj_len(\"bike:1\", \"$\")\n            .expect(\"Failed to run JSON.OBJLEN\");\n        print_redis_value(&res27); // >>> [3]\n\n        let res28: Value = r\n            .json_obj_keys(\"bike:1\", \"$\")\n            .expect(\"Failed to run JSON.OBJKEYS\");\n        print_redis_value(&res28); // >>> [[\"brand\",\"model\",\"price\"]]","section_id":"use-redis-with-json"},{"id":"format-cli-output-ex0","language":"bash","code":"$ redis-cli --raw\n> JSON.GET obj INDENT \"\\t\" NEWLINE \"\\n\" SPACE \" \" $\n[\n\t{\n\t\t\"name\": \"Leonard Cohen\",\n\t\t\"lastSeen\": 1478476800,\n\t\t\"loggedOut\": true\n\t}\n]","section_id":"format-cli-output"}]}
{"schema_version":2,"id":"develop/data-types/json/indexing_JSON","title":"Index/Search JSON documents","url":"https://redis.io/docs/latest/develop/data-types/json/indexing_json/","summary":"Combine Redis JSON and Redis Search to index and search JSON documents","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-10T15:45:45+01:00","page_type":"content","content_hash":"619d68833cd1ad250e603703e7eb76607d6403ea49272fb20fe6ddab35374af2","sections":[{"id":"content","title":"Content","role":"content","text":"In addition to storing JSON documents, you can also index them using the [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) feature. This enables full-text search capabilities and document retrieval based on their content.\n\nTo use these features, install [Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack).\n\nSee the [tutorial](https://redis.io/docs/latest/develop/ai/search-and-query/indexing) to learn how to search and query your JSON."}],"examples":[]}
{"schema_version":2,"id":"develop/data-types/json/path","title":"Path","url":"https://redis.io/docs/latest/develop/data-types/json/path/","summary":"Access specific elements within a JSON document","aliases":["/stack/json/path","/stack/json/path/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-10T15:45:45+01:00","page_type":"content","content_hash":"272b09be068c956ac905e7d250bb00172d1b7e40ae346bc6df56ffa5548793fd","sections":[{"id":"jsonpath-support","title":"JSONPath support","role":"content","text":"RedisJSON v2.0 introduced [JSONPath](http://goessner.net/articles/JsonPath/) support. It follows the syntax described by Goessner in his [article](http://goessner.net/articles/JsonPath/).\n\nA JSONPath query can resolve to several locations in a JSON document. In this case, the JSON commands apply the operation to every possible location. This is a major improvement over [legacy path](#legacy-path-syntax) queries, which only operate on the first path.\n\nNotice that the structure of the command response often differs when using JSONPath. See the [Commands](/commands/?group=json) page for more details.\n\nThe new syntax supports bracket notation, which allows the use of special characters like colon \":\" or whitespace in key names.\n\nIf you want to include double quotes in a query from the CLI, enclose the JSONPath within single quotes. For example:\n\n[code example]"},{"id":"jsonpath-syntax","title":"JSONPath syntax","role":"content","text":"The following JSONPath syntax table was adapted from Goessner's [path syntax comparison](https://goessner.net/articles/JsonPath/index.html#e2).\n\n| Syntax&nbsp;element | Description |\n|----------------|-------------|\n| $ | The root (outermost JSON element), starts the path. |\n| . or [] | Selects a child element. |\n| .. | Recursively descends through the JSON document. |\n| * | Wildcard, returns all elements. |\n| [] | Subscript operator, accesses an array element. |\n| [,] | Union, selects multiple elements. |\n| [start\\:end\\:step] | Array slice where *start*, *end*, and *step* are index values. You can omit values from the slice (for example, `[3:]`, `[:8:2]`) to use the default values: *start* defaults to the first index, *end* defaults to the last index, and *step* defaults to `1`. Use `[*]` or `[:]` to select all elements. |\n| ?() | Filters a JSON object or array. Supports comparison operators <nobr>(`==`, `!=`, `<`, `<=`, `>`, `>=`, `=~`)</nobr>, logical operators <nobr>(`&&`, `\\|\\|`, `!`)</nobr>, arithmetic operators <nobr>(`+`, `-`, `*`, `/`, `%`)</nobr>, membership operators <nobr>(`in`, `nin`)</nobr>, set-relation operators <nobr>(`subsetof`, `anyof`, `noneof`)</nobr>, the <nobr>(`size`/`sizeof`, `empty`)</nobr> operators, and parenthesis <nobr>(`(`, `)`)</nobr>. |\n| () | Script expression. |\n| @ | The current element, used in filter or script expressions. |\n| ~ | Returns the names of an object's members as a list of strings. |\n\nBeginning with Redis 8.10, the JSON data type supports a richer JSONPath syntax, with additional operators and functions:\n\n- [Projection expressions](#projection-expressions) at the top level of a JSONPath query\n- [`==` and `!=`](#comparing-array-and-object-literals) can now compare any literal, including array and object literals\n- Filter negation operator: [`!`](#negation-)\n- [`size`/`sizeof` and `empty`](#size-sizeof-and-empty) operators on string, array, object, and nodelist\n- [`in` and `nin`](#membership-in-and-nin) operators: membership test on an array and nodelist\n- [Operators on numbers](#arithmetic-operators): binary `-`, `+`, `*`, `/`, `%`, and unary `-` and `+`\n- Operator on object: [`~`](#get-keys-operator-)\n- [`length()`](#length) function on array, object, and string\n- Functions on number: [`abs()`, `ceiling()`, `floor()`](#abs-ceiling-and-floor)\n- Functions on string: [`match()`, `search()`](#match-and-search)\n- Strings concatenation with [`concat()`](#concat)\n- Functions on array: [`first()`, `last()`, `index()`](#first-last-and-index), [`append()`](#append)\n- Aggregation functions on array: [`min()`, `max()`, `avg()`, `sum()`, `stddev()`](#min-max-avg-sum-and-stddev)\n- Function on object: [`keys()`](#keys)\n- Function on nodelist: [`count()`](#count)\n- Function on nodelist with exactly one node: [`value()`](#value)\n- Relations functions on array and nodelist: [`subsetof()`, `anyof()`, `noneof()`](#set-relations-subsetof-anyof-and-noneof)\n\nThese operators can be used within a filter expression (`?()`). Functions can appear inside filter expressions and, when they return a value, as top-level [projection expressions](#projection-expressions). A function can be written in prefix form, `length($.arr)`, or in postfix (method) form, `$.arr.length()`. A path segment immediately followed by `(` is a method call, so `$.arr.length()` is a function call while `$.arr.length` is a reference to a field named `length`.\n\n> [!WARNING]\n> Beginning with Redis 8.10, two changes to path parsing may affect existing queries:\n>\n> - To access a field whose name contains a tilde (`~`), use bracket notation, for example `$[\"a~b\"]`. A tilde is no longer valid in a field name using dot notation.\n> - The words `in`, `nin`, `subsetof`, `anyof`, `noneof`, `size`, `sizeof`, and `empty` are now reserved as operators. To access a field with one of these names, use `$.size` or `$[\"size\"]`."},{"id":"evaluation-semantics","title":"Evaluation semantics","role":"content","text":"The following rules apply to the operators and functions described below:\n\n- **The \"Nothing\" result.** Any operand that cannot be evaluated — a non-numeric arithmetic operand, division or modulo by zero, an out-of-range index, a wrong argument count, a type mismatch, or an arithmetic overflow to a non-finite value — evaluates to *Nothing*. Inside a filter, a *Nothing* result makes the surrounding condition false, so the element is skipped. As a projection, it produces an empty reply (`[]`). Evaluation never returns an error and never fails the query for these cases.\n- **Number coercion.** The `==`, `!=`, `in`, `nin`, and set-relation operators treat integer and floating-point numbers with the same value as equal (for example, `1` equals `1.0`). The ordering operators (`<`, `<=`, `>`, `>=`) cannot compare against an array or object literal, so such a comparison never matches.\n- **Node lists match on \"any\".** When an operand selects more than one node (for example, `@.*`), a comparison or operator holds if *any* selected node satisfies it.\n- **Arithmetic operators must be surrounded by spaces.** `@.a + 1` is addition, but `@.a+1` is a reference to a field literally named `a+1`, because the field-name character set includes characters such as `+`, `-`, `/`, `%`, `$`, `^`, `:`, and `_`.\n- **Functions are arity-checked.** Calling a function with the wrong number of arguments produces *Nothing* rather than silently using a subset of the arguments."},{"id":"jsonpath-examples","title":"JSONPath examples","role":"content","text":"The following examples use this JSON document, which stores details about items in a store's inventory:\n\n[code example]\n\nFirst, create the JSON document in your database:\n\nSetup: Create a complex JSON document with nested objects and arrays to use in JSONPath examples\n\n**Commands:** JSON.SET\n\n**Complexity:**\n- JSON.SET: O(M+N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"access-examples","title":"Access examples","role":"content","text":"The following examples use the [`JSON.GET`](https://redis.io/docs/latest/commands/json.get) command to retrieve data from various paths in the JSON document.\n\nYou can use the wildcard operator `*` to return a list of all items in the inventory:\n\nWildcard queries: Use the * operator to retrieve all items in a collection when you need to access all elements at a specific level\n\n**Builds upon:** set_bikes\n\n**Commands:** JSON.GET\n\n**Complexity:**\n- JSON.GET: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nFor some queries, multiple paths can produce the same results. For example, the following paths return the names of all mountain bikes:\n\nArray element access: Use bracket notation and array subscripts to access specific array elements or all elements with [*] when you need to retrieve values from arrays\n\n**Difficulty:** Intermediate\n\n**Builds upon:** get_bikes\n\n**Commands:** JSON.GET\n\n**Complexity:**\n- JSON.GET: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nThe recursive descent operator `..` can retrieve a field from multiple sections of a JSON document. The following example returns the names of all inventory items:\n\nRecursive descent: Use the .. operator to search for a field at any depth in the JSON document when you need to find values across multiple nesting levels\n\n**Difficulty:** Intermediate\n\n**Builds upon:** get_mtnbikes\n\n**Commands:** JSON.GET\n\n**Complexity:**\n- JSON.GET: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nYou can use an array slice to select a range of elements from an array. This example returns the names of the first 2 mountain bikes:\n\nArray slicing: Use [start:end] syntax to select a range of array elements when you need to retrieve a subset of items from an array\n\n**Difficulty:** Intermediate\n\n**Builds upon:** get_mtnbikes\n\n**Commands:** JSON.GET\n\n**Complexity:**\n- JSON.GET: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nFilter expressions `?()` let you select JSON elements based on certain conditions. You can use comparison operators (`==`, `!=`, `<`, `<=`, `>`, `>=`, and starting with version v2.4.2, also `=~`), logical operators (`&&`, `||`), and parenthesis (`(`, `)`) within these expressions. A filter expression can be applied on an array or on an object, iterating over all the **elements** in the array or all the **values** in the object, retrieving only the ones that match the filter condition. \n\nPaths within the filter condition use the dot notation with either `@` to denote the current array element or the current object value, or `$` to denote the top-level element. For example, use `@.key_name` to refer to a nested value and `$.top_level_key_name` to refer to a top-level value.\n\nFrom version v2.4.2 onward, you can use the comparison operator `=~` to match a path of a string value on the left side against a regular expression pattern on the right side. For more information, see the [supported regular expression syntax docs](https://docs.rs/regex/latest/regex/#syntax).\n\nNon-string values do not match. A match can only occur when the left side is a path of a string value and the right side is either a hard-coded string, or a path of a string value. See [examples](#json-filter-examples) below.\n\nThe regex match is partial, meaning a regex pattern like `\"foo\"` matches a string such as `\"barefoots\"`.\nTo make the match exact, use the regex pattern `\"^foo$\"`.\n\nOther JSONPath engines may use regex patterns between slashes (for example, `/foo/`),\nand their match is exact. They can perform partial matches using a regex pattern such\nas `/.*foo.*/`."},{"id":"filter-examples","title":"Filter examples","role":"content","text":"In the following example, the filter only returns mountain bikes with a price less than 3000 and\na weight less than 10:\n\nFilter expressions: Use ?() with comparison and logical operators to select elements matching specific conditions when you need to query based on multiple criteria\n\n**Difficulty:** Advanced\n\n**Builds upon:** get_models\n\n**Commands:** JSON.GET\n\n**Complexity:**\n- JSON.GET: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nThis example filters the inventory for the model names of bikes made from alloy:\n\nEquality filters: Use == operator in filter expressions to select elements with specific field values when you need to find items matching exact criteria\n\n**Difficulty:** Advanced\n\n**Builds upon:** filter1\n\n**Commands:** JSON.GET\n\n**Complexity:**\n- JSON.GET: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nThis example, valid from version v2.4.2 onwards, filters only bikes whose material begins with\n\"al-\" using regex match. Note that this match is case-insensitive because of the prefix `(?i)` in\nthe regular expression pattern `\"(?i)al\"`:\n\nRegex filters: Use =~ operator with regular expressions in filter expressions to match patterns in string values when you need flexible pattern-based filtering\n\n**Difficulty:** Advanced\n\n**Builds upon:** filter2\n\n**Commands:** JSON.GET\n\n**Complexity:**\n- JSON.GET: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nYou can also specify a regex pattern using a property from the JSON object itself.\nFor example, we can add a string property named `regex_pat` to each mountain bike,\nwith the value `\"(?i)al\"` to match the material, as in the previous example. We\ncan then match `regex_pat` against the bike's material: \n\nDynamic regex filters: Use regex patterns stored in JSON properties to filter elements when you need to apply patterns defined within the document itself\n\n**Difficulty:** Advanced\n\n**Builds upon:** filter3\n\n**Commands:** JSON.SET, JSON.GET\n\n**Complexity:**\n- JSON.SET: O(M+N)\n- JSON.GET: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nBeginning with Redis 8.10, the JSONPath filter syntax supports the following operators.\n\n#### Negation `!`\n\nThe `!` operator negates a filter condition. It binds more tightly than the logical `&&` and `||` operators, so use parentheses to negate a compound condition. A double negation (`!!`) is equivalent to an existence test.\n\nFilter negation: Use the ! operator to invert a filter condition when you need to select elements that do not match\n\n**Difficulty:** Advanced\n\n**Commands:** JSON.SET, JSON.GET\n\n**Complexity:**\n- JSON.SET: O(M+N)\n- JSON.GET: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\n#### Comparing array and object literals\n\nThe `==` and `!=` operators can compare any JSON literal, including array and object literals, on either side of the operator. Comparison is by structural (deep) equality. A comparison between values of different types is always false.\n\nLiteral comparison: Use == and != to compare array and object literals by structural equality when you need to match a whole nested value\n\n**Difficulty:** Advanced\n\n**Commands:** JSON.SET, JSON.GET\n\n**Complexity:**\n- JSON.SET: O(M+N)\n- JSON.GET: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\n#### Arithmetic operators\n\nThe binary operators `+`, `-`, `*`, `/`, and `%` and the unary operators `-` and `+` operate on numbers. Precedence, from highest to lowest, is: unary `-`/`+`, then `*`/`/`/`%`, then `+`/`-`. Use parentheses to override precedence. Division always produces a floating-point result. Division or modulo by zero produces *Nothing*, so no element matches.\n\nRemember that arithmetic operators must be surrounded by spaces (see [Evaluation semantics](#evaluation-semantics)).\n\nArithmetic in filters: Use +, -, *, / and % inside a filter condition when you need to compute a value before comparing it\n\n**Difficulty:** Advanced\n\n**Commands:** JSON.SET, JSON.GET\n\n**Complexity:**\n- JSON.SET: O(M+N)\n- JSON.GET: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\n#### Membership: `in` and `nin`\n\n`value in array` matches when `value` is a member of `array`; `value nin array` is its strict negation. The right-hand side can be an array literal or a path to an array. Membership uses the same structural comparison and number coercion as `==`. If the right-hand side is not an array, `in` is false (so `nin` is true).\n\nMembership filters: Use in and nin to test whether a value is a member of an array when you need set-style matching\n\n**Difficulty:** Advanced\n\n**Commands:** JSON.SET, JSON.GET\n\n**Complexity:**\n- JSON.SET: O(M+N)\n- JSON.GET: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\n#### Set relations: `subsetof`, `anyof`, and `noneof`\n\nThese operators compare two arrays (each side can be an array literal or a path to an array):\n\n- `subsetof` matches when every element of the left array is a member of the right array. An empty array is a subset of any array.\n- `anyof` matches when the two arrays have a non-empty intersection. An empty array never matches.\n- `noneof` matches when the two arrays have an empty intersection. An empty array always matches.\n\nSet relation filters: Use subsetof, anyof, and noneof to compare two arrays when you need to match based on their overlap\n\n**Difficulty:** Advanced\n\n**Commands:** JSON.SET, JSON.GET\n\n**Complexity:**\n- JSON.SET: O(M+N)\n- JSON.GET: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\n#### `size` (`sizeof`) and `empty`\n\n`left sizeof n` matches when the size of `left` equals the integer `n`, where size is the number of characters in a string, the number of elements in an array, or the number of members in an object. A non-integer `n` is truncated toward zero; a non-numeric `n` never matches. `size` is an alias for `sizeof`.\n\n`left empty true` matches an empty string, array, or object; `left empty false` matches a non-empty one.\n\nSize and emptiness filters: Use sizeof and empty to filter by the size or emptiness of a string, array, or object\n\n**Difficulty:** Advanced\n\n**Commands:** JSON.SET, JSON.GET\n\n**Complexity:**\n- JSON.SET: O(M+N)\n- JSON.GET: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\n#### Get-keys operator `~`\n\nThe terminal `~` operator returns the member names of an object as a flat list of strings. Applied to the root (`$~`), it returns the top-level keys. Applied to a multiple-match receiver, it flattens the keys of every matched object. A non-object receiver contributes no keys.\n\nBecause `~` is terminal, expressions such as `$.obj~.x`, `$.obj~~`, and `$.obj.keys()~` are parse errors. For a composable alternative that can be chained with other functions, use the [`keys()`](#keys) function.\n\nGet-keys filter: Use the ~ operator to retrieve an object's member names as a list of strings\n\n**Difficulty:** Advanced\n\n**Commands:** JSON.SET, JSON.GET\n\n**Complexity:**\n- JSON.SET: O(M+N)\n- JSON.GET: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nFunctions can appear inside filter expressions and, when they return a value, as top-level [projection expressions](#projection-expressions). Beginning with Redis 8.10, JSONPath supports the following functions.\n\n#### `length()`\n\nReturns the number of characters in a string, elements in an array, or members in an object. Any other type produces *Nothing*.\n\nLength function: Use length() to get the number of characters in a string, elements in an array, or members in an object\n\n**Difficulty:** Advanced\n\n**Commands:** JSON.SET, JSON.GET\n\n**Complexity:**\n- JSON.SET: O(M+N)\n- JSON.GET: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\n#### `count()`\n\nReturns the number of nodes selected by a query. An absent path counts as `0`; a single node counts as `1`.\n\nCount function: Use count() to get the number of nodes selected by a query\n\n**Difficulty:** Advanced\n\n**Commands:** JSON.SET, JSON.GET\n\n**Complexity:**\n- JSON.SET: O(M+N)\n- JSON.GET: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\n#### `value()`\n\nReturns the value of a query that selects exactly one node. A query that selects zero or more than one node produces *Nothing*.\n\nValue function: Use value() to get the value of a query that selects exactly one node\n\n**Difficulty:** Advanced\n\n**Commands:** JSON.SET, JSON.GET\n\n**Complexity:**\n- JSON.SET: O(M+N)\n- JSON.GET: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\n#### `keys()`\n\nReturns the member names of an object as a list of strings, like the `~` operator. Unlike `~`, `keys()` is composable and can be chained with other functions.\n\nKeys function: Use keys() to get an object's member names as a composable, chainable list of strings\n\n**Difficulty:** Advanced\n\n**Commands:** JSON.SET, JSON.GET\n\n**Complexity:**\n- JSON.SET: O(M+N)\n- JSON.GET: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\n#### `match()` and `search()`\n\nBoth test a string against a regular expression pattern ([RFC 9485](https://datatracker.ietf.org/doc/rfc9485/) I-Regexp). `match()` requires the whole string to match (anchored), while `search()` matches any substring (the same behavior as the `=~` operator). An invalid pattern produces no match.\n\nRegex functions: Use match() for an anchored (whole-string) regular expression match and search() for a partial match\n\n**Difficulty:** Advanced\n\n**Commands:** JSON.SET, JSON.GET\n\n**Complexity:**\n- JSON.SET: O(M+N)\n- JSON.GET: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\n#### `concat()`\n\nConcatenates its string arguments into a single string. It requires at least one argument, and any non-string argument produces *Nothing*.\n\nConcat function: Use concat() to join string arguments into a single string for comparison in a filter\n\n**Difficulty:** Advanced\n\n**Commands:** JSON.SET, JSON.GET\n\n**Complexity:**\n- JSON.SET: O(M+N)\n- JSON.GET: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\n#### `abs()`, `ceiling()`, and `floor()`\n\nOperate on a number: `abs()` returns the absolute value, `ceiling()` rounds up to the nearest integer, and `floor()` rounds down. An integer argument stays an integer and a floating-point argument stays a float. A result that overflows the signed 64-bit integer range produces *Nothing*.\n\nNumeric functions: Use abs(), ceiling(), and floor() to transform a number before comparing it in a filter\n\n**Difficulty:** Advanced\n\n**Commands:** JSON.SET, JSON.GET\n\n**Complexity:**\n- JSON.SET: O(M+N)\n- JSON.GET: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\n#### `first()`, `last()`, and `index()`\n\n`first(array)` and `last(array)` return the first and last element of an array. `index(array, n)` returns the element at index `n`; a negative `n` counts from the end, a fractional `n` is truncated toward zero, and an out-of-range index produces *Nothing*.\n\nArray access functions: Use first(), last(), and index() to pick a single element out of an array before comparing it\n\n**Difficulty:** Advanced\n\n**Commands:** JSON.SET, JSON.GET\n\n**Complexity:**\n- JSON.SET: O(M+N)\n- JSON.GET: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\n#### `min()`, `max()`, `avg()`, `sum()`, and `stddev()`\n\nThese aggregation functions operate on an array of numbers. `stddev()` returns the *population* standard deviation (dividing by N). The functions are strict: an array that contains any non-numeric element, or an empty array, produces *Nothing* — elements are never silently skipped.\n\nAggregation functions: Use sum(), avg(), min(), max(), or stddev() to reduce an array of numbers to a single value in a filter\n\n**Difficulty:** Advanced\n\n**Commands:** JSON.SET, JSON.GET\n\n**Complexity:**\n- JSON.SET: O(M+N)\n- JSON.GET: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\n#### `append()`\n\n`append(value, ...)` returns the matched array with the given value or values added after its elements. It is a read-only query-time projection and does not modify the stored document — to mutate an array in place, use the [`JSON.ARRAPPEND`](https://redis.io/docs/latest/commands/json.arrappend) command instead. A multiple-value argument is added as a single element (it is not spread), and a *Nothing* argument makes the whole result *Nothing*.\n\nAppend function: Use append() as a read-only, query-time projection that adds a value after an array's elements without modifying the stored document\n\n**Difficulty:** Advanced\n\n**Commands:** JSON.SET, JSON.GET\n\n**Complexity:**\n- JSON.SET: O(M+N)\n- JSON.GET: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"update-examples","title":"Update examples","role":"content","text":"You can also use JSONPath queries when you want to update specific sections of a JSON document.\n\nFor example, you can pass a JSONPath to the [`JSON.SET`](https://redis.io/docs/latest/commands/json.set) command to update a specific field. This example changes the price of the first item in the headphones list:\n\nBulk updates: Use JSONPath with JSON.NUMINCRBY to update multiple numeric values across the document when you need to apply arithmetic operations to multiple fields\n\n**Difficulty:** Intermediate\n\n**Builds upon:** get_bikes\n\n**Commands:** JSON.GET, JSON.NUMINCRBY\n\n**Complexity:**\n- JSON.GET: O(N)\n- JSON.NUMINCRBY: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nYou can use filter expressions to update only JSON elements that match certain conditions. The following example sets the price of any bike to 1500 if its price is already less than 2000:\n\nConditional updates: Use filter expressions with JSON.SET to update only elements matching specific conditions when you need selective modifications\n\n**Difficulty:** Advanced\n\n**Builds upon:** filter1\n\n**Commands:** JSON.SET, JSON.GET\n\n**Complexity:**\n- JSON.SET: O(M+N)\n- JSON.GET: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nJSONPath queries also work with other JSON commands that accept a path as an argument. For example, you can add a new color option for a set of headphones with [`JSON.ARRAPPEND`](https://redis.io/docs/latest/commands/json.arrappend):\n\nArray updates with filters: Use JSON.ARRAPPEND with filter expressions to add elements to arrays matching conditions when you need to modify collections selectively\n\n**Difficulty:** Advanced\n\n**Builds upon:** update_filters1\n\n**Commands:** JSON.ARRAPPEND, JSON.GET\n\n**Complexity:**\n- JSON.ARRAPPEND: O(1)\n- JSON.GET: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"projection-expressions","title":"Projection expressions","role":"content","text":"The top level of a JSONPath query can be an expression that *computes* a value — arithmetic, a function call, a get-keys operator, and so on — rather than only selecting nodes from the document. A query that is a plain path (for example, `$.a.b`, `$..x`, or even a fully parenthesized path such as `($.a)`) still selects nodes; anything else (for example, `$.a + 1`, `-$.a`, `$.arr.length()`, `length($.arr)`, `$.obj~`, or `$.obj.keys()`) is a projection.\n\nA projection that evaluates to *Nothing* produces an empty reply (`[]`).\n\nProjection expressions: Use arithmetic, a function call, or the length of a path as a top-level query that computes a value instead of only selecting nodes\n\n**Difficulty:** Advanced\n\n**Commands:** JSON.SET, JSON.GET\n\n**Complexity:**\n- JSON.SET: O(M+N)\n- JSON.GET: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nWhen [`JSON.GET`](https://redis.io/docs/latest/commands/json.get) is given more than one path, projections and plain paths can be mixed, and each path becomes a key in the returned object. The order of keys in that object is not guaranteed.\n\nMulti-path queries: Pass more than one path to JSON.GET to have each path's result returned under its own key in a single reply\n\n**Difficulty:** Advanced\n\n**Commands:** JSON.GET\n\n**Complexity:**\n- JSON.GET: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\n[`JSON.MGET`](https://redis.io/docs/latest/commands/json.mget) evaluates the projection independently for each key. A missing key, or a per-key evaluation error, yields a null reply for that key rather than failing the whole request."},{"id":"legacy-path-syntax","title":"Legacy path syntax","role":"content","text":"RedisJSON v1 had the following path implementation. JSON v2 still supports this legacy path in addition to JSONPath.\n\nPaths always begin at the root of a Redis JSON value. The root is denoted by a period character (`.`). For paths that reference the root's children, it is optional to prefix the path with the root.\n\nRedis JSON supports both dot notation and bracket notation for object key access. The following paths refer to _headphones_, which is a child of _inventory_ under the root:\n\n*   `.inventory.headphones`\n*   `inventory[\"headphones\"]`\n*   `['inventory'][\"headphones\"]`\n\nTo access an array element, enclose its index within a pair of square brackets. The index is 0-based, with 0 being the first element of the array, 1 being the next element, and so on. You can use negative offsets to access elements starting from the end of the array. For example, -1 is the last element in the array, -2 is the second to last element, and so on."},{"id":"json-key-names-and-path-compatibility","title":"JSON key names and path compatibility","role":"content","text":"By definition, a JSON key can be any valid JSON string. Paths, on the other hand, are traditionally based on JavaScript's (and Java's) variable naming conventions.\n\nAlthough JSON can store objects that contain arbitrary key names, you can only use a legacy path to access these keys if they conform to these naming syntax rules:\n\n1.  Names must begin with a letter, a dollar sign (`$`), or an underscore (`_`) character\n2.  Names can contain letters, digits, dollar signs, and underscores\n3.  Names are case-sensitive"},{"id":"time-complexity-of-path-evaluation","title":"Time complexity of path evaluation","role":"content","text":"The time complexity of searching (navigating to) an element in the path is calculated from:\n\n1. Child level - every level along the path adds an additional search\n2. Key search - O(N)<sup>&#8224;</sup>, where N is the number of keys in the parent object\n3. Array search - O(1)\n\nThis means that the overall time complexity of searching a path is _O(N*M)_, where N is the depth and M is the number of parent object keys.\n\n<sup>&#8224;</sup> While this is acceptable for objects where N is small, access can be optimized for larger objects."}],"examples":[{"id":"jsonpath-support-ex0","language":"bash","code":"JSON.GET store '$.inventory[\"mountain_bikes\"]'","section_id":"jsonpath-support"},{"id":"jsonpath-examples-ex0","language":"json","code":"{\n    \"inventory\": {\n        \"mountain_bikes\": [\n            {\n                \"id\": \"bike:1\",\n                \"model\": \"Phoebe\",\n                \"description\": \"This is a mid-travel trail slayer that is a fantastic daily driver or one bike quiver. The Shimano Claris 8-speed groupset gives plenty of gear range to tackle hills and there\\u2019s room for mudguards and a rack too.  This is the bike for the rider who wants trail manners with low fuss ownership.\",\n                \"price\": 1920,\n                \"specs\": {\"material\": \"carbon\", \"weight\": 13.1},\n                \"colors\": [\"black\", \"silver\"],\n            },\n... output truncated for AI-facing Markdown ...\n                \"description\": \"A real joy to ride, this bike got very high scores in last years Bike of the year report. The carefully crafted 50-34 tooth chainset and 11-32 tooth cassette give an easy-on-the-legs bottom gear for climbing, and the high-quality Vittoria Zaffiro tires give balance and grip.It includes a low-step frame , our memory foam seat, bump-resistant shocks and conveniently placed thumb throttle. Put it all together and you get a bike that helps redefine what can be done for this price.\",\n                \"price\": 3941,\n                \"specs\": {\"material\": \"alloy\", \"weight\": 11.6},\n            },\n        ],\n    }\n}","section_id":"jsonpath-examples"},{"id":"jsonpath-examples-ex1","language":"plaintext","code":"> JSON.SET bikes:inventory $ '{ \"inventory\": { \"mountain_bikes\": [ { \"id\": \"bike:1\", \"model\": \"Phoebe\", \"description\": \"This is a mid-travel trail slayer that is a fantastic daily driver or one bike quiver. The Shimano Claris 8-speed groupset gives plenty of gear range to tackle hills and there\\'s room for mudguards and a rack too. This is the bike for the rider who wants trail manners with low fuss ownership.\", \"price\": 1920, \"specs\": {\"material\": \"carbon\", \"weight\": 13.1}, \"colors\": [\"black\", \"silver\"] }, { \"id\": \"bike:2\", \"model\": \"Quaoar\", \"description\": \"Redesigned for the 2020 model year, this bike impressed our testers and is the best all-around trail bike we\\'ve ever tested. The Shimano gear system effectively does away with an external cassette, so is super low maintenance in terms of wear and tear. All in all it\\'s an impressive package for the price, making it very competitive.\", \"price\": 2072, \"specs\": {\"material\": \"aluminium\", \"weight\": 7.9}, \"colors\": [\"black\", \"white\"] }, { \"id\": \"bike:3\", \"model\": \"Weywot\", \"description\": \"This bike gives kids aged six years and older a durable and uberlight mountain bike for their first experience on tracks and easy cruising through forests and fields. A set of powerful Shimano hydraulic disc brakes provide ample stopping ability. If you\\'re after a budget option, this is one of the best bikes you could get.\", \"price\": 3264, \"specs\": {\"material\": \"alloy\", \"weight\": 13.8} } ], \"commuter_bikes\": [ { \"id\": \"bike:4\", \"model\": \"Salacia\", \"description\": \"This bike is a great option for anyone who just wants a bike to get about on With a slick-shifting Claris gears from Shimano\\'s, this is a bike which doesn\\'t break the bank and delivers craved performance. It\\'s for the rider who wants both efficiency and capability.\", \"price\": 1475, \"specs\": {\"material\": \"aluminium\", \"weight\": 16.6}, \"colors\": [\"black\", \"silver\"] }, { \"id\": \"bike:5\", \"model\": \"Mimas\", \"description\": \"A real joy to ride, this bike got very high scores in last years Bike of the year report. The carefully crafted 50-34 tooth chainset and 11-32 tooth cassette give an easy-on-the-legs bottom gear for climbing, and the high-quality Vittoria Zaffiro tires give balance and grip.It includes a low-step frame , our memory foam seat, bump-resistant shocks and conveniently placed thumb throttle. Put it all together and you get a bike that helps redefine what can be done for this price.\", \"price\": 3941, \"specs\": {\"material\": \"alloy\", \"weight\": 11.6} } ] }}'\n... output truncated for AI-facing Markdown ...\n> JSON.SET bikes:inventory $ '{ \"inventory\": { \"mountain_bikes\": [ { \"id\": \"bike:1\", \"model\": \"Phoebe\", \"description\": \"This is a mid-travel trail slayer that is a fantastic daily driver or one bike quiver. The Shimano Claris 8-speed groupset gives plenty of gear range to tackle hills and there\\'s room for mudguards and a rack too. This is the bike for the rider who wants trail manners with low fuss ownership.\", \"price\": 1920, \"specs\": {\"material\": \"carbon\", \"weight\": 13.1}, \"colors\": [\"black\", \"silver\"] }, { \"id\": \"bike:2\", \"model\": \"Quaoar\", \"description\": \"Redesigned for the 2020 model year, this bike impressed our testers and is the best all-around trail bike we\\'ve ever tested. The Shimano gear system effectively does away with an external cassette, so is super low maintenance in terms of wear and tear. All in all it\\'s an impressive package for the price, making it very competitive.\", \"price\": 2072, \"specs\": {\"material\": \"aluminium\", \"weight\": 7.9}, \"colors\": [\"black\", \"white\"] }, { \"id\": \"bike:3\", \"model\": \"Weywot\", \"description\": \"This bike gives kids aged six years and older a durable and uberlight mountain bike for their first experience on tracks and easy cruising through forests and fields. A set of powerful Shimano hydraulic disc brakes provide ample stopping ability. If you\\'re after a budget option, this is one of the best bikes you could get.\", \"price\": 3264, \"specs\": {\"material\": \"alloy\", \"weight\": 13.8} } ], \"commuter_bikes\": [ { \"id\": \"bike:4\", \"model\": \"Salacia\", \"description\": \"This bike is a great option for anyone who just wants a bike to get about on With a slick-shifting Claris gears from Shimano\\'s, this is a bike which doesn\\'t break the bank and delivers craved performance. It\\'s for the rider who wants both efficiency and capability.\", \"price\": 1475, \"specs\": {\"material\": \"aluminium\", \"weight\": 16.6}, \"colors\": [\"black\", \"silver\"] }, { \"id\": \"bike:5\", \"model\": \"Mimas\", \"description\": \"A real joy to ride, this bike got very high scores in last years Bike of the year report. The carefully crafted 50-34 tooth chainset and 11-32 tooth cassette give an easy-on-the-legs bottom gear for climbing, and the high-quality Vittoria Zaffiro tires give balance and grip.It includes a low-step frame , our memory foam seat, bump-resistant shocks and conveniently placed thumb throttle. Put it all together and you get a bike that helps redefine what can be done for this price.\", \"price\": 3941, \"specs\": {\"material\": \"alloy\", \"weight\": 11.6} } ] }}'","section_id":"jsonpath-examples"},{"id":"jsonpath-examples-ex2","language":"csharp","code":"string inventoryJson = @\"\n{\n    \"\"inventory\"\": {\n        \"\"mountain_bikes\"\": [\n            {\n                \"\"id\"\": \"\"bike:1\"\",\n                \"\"model\"\": \"\"Phoebe\"\",\n                \"\"description\"\": \"\"This is a mid-travel trail slayer that is a fantastic daily driver or one bike quiver. The Shimano Claris 8-speed groupset gives plenty of gear range to tackle hills and there\\u2019s room for mudguards and a rack too.  This is the bike for the rider who wants trail manners with low fuss ownership.\"\",\n                \"\"price\"\": 1920,\n                \"\"specs\"\": {\"\"material\"\": \"\"carbon\"\", \"\"weight\"\": 13.1},\n                \"\"colors\"\": [\"\"black\"\", \"\"silver\"\"]\n            },\n            {\n                \"\"id\"\": \"\"bike:2\"\",\n                \"\"model\"\": \"\"Quaoar\"\",\n                \"\"description\"\": \"\"Redesigned for the 2020 model year, this bike impressed our testers and is the best all-around trail bike we've ever tested. The Shimano gear system effectively does away with an external cassette, so is super low maintenance in terms of wear and tear. All in all it's an impressive package for the price, making it very competitive.\"\",\n                \"\"price\"\": 2072,\n                \"\"specs\"\": {\"\"material\"\": \"\"aluminium\"\", \"\"weight\"\": 7.9},\n                \"\"colors\"\": [\"\"black\"\", \"\"white\"\"]\n            },\n            {\n                \"\"id\"\": \"\"bike:3\"\",\n                \"\"model\"\": \"\"Weywot\"\",\n                \"\"description\"\": \"\"This bike gives kids aged six years and older a durable and uberlight mountain bike for their first experience on tracks and easy cruising through forests and fields. A set of powerful Shimano hydraulic disc brakes provide ample stopping ability. If you're after a budget option, this is one of the best bikes you could get.\"\",\n                \"\"price\"\": 3264,\n                \"\"specs\"\": {\"\"material\"\": \"\"alloy\"\", \"\"weight\"\": 13.8}\n            }\n        ],\n        \"\"commuter_bikes\"\": [\n            {\n                \"\"id\"\": \"\"bike:4\"\",\n                \"\"model\"\": \"\"Salacia\"\",\n                \"\"description\"\": \"\"This bike is a great option for anyone who just wants a bike to get about on With a slick-shifting Claris gears from Shimano\\u2019s, this is a bike which doesn\\u2019t break the bank and delivers craved performance.  It\\u2019s for the rider who wants both efficiency and capability.\"\",\n                \"\"price\"\": 1475,\n                \"\"specs\"\": {\"\"material\"\": \"\"aluminium\"\", \"\"weight\"\": 16.6},\n                \"\"colors\"\": [\"\"black\"\", \"\"silver\"\"]\n            },\n            {\n                \"\"id\"\": \"\"bike:5\"\",\n                \"\"model\"\": \"\"Mimas\"\",\n                \"\"description\"\": \"\"A real joy to ride, this bike got very high scores in last years Bike of the year report. The carefully crafted 50-34 tooth chainset and 11-32 tooth cassette give an easy-on-the-legs bottom gear for climbing, and the high-quality Vittoria Zaffiro tires give balance and grip.It includes a low-step frame , our memory foam seat, bump-resistant shocks and conveniently placed thumb throttle. Put it all together and you get a bike that helps redefine what can be done for this price.\"\",\n                \"\"price\"\": 3941,\n                \"\"specs\"\": {\"\"material\"\": \"\"alloy\"\", \"\"weight\"\": 11.6}\n            }\n        ]\n    }\n}\";\n\n        bool res28 = db.JSON().Set(\"bikes:inventory\", \"$\", inventoryJson);\n        Console.WriteLine(res28);   // >>> True","section_id":"jsonpath-examples"},{"id":"jsonpath-examples-ex3","language":"go","code":"var inventory_json = map[string]interface{}{\n\t\t\"inventory\": map[string]interface{}{\n\t\t\t\"mountain_bikes\": []interface{}{\n\t\t\t\tmap[string]interface{}{\n\t\t\t\t\t\"id\":    \"bike:1\",\n\t\t\t\t\t\"model\": \"Phoebe\",\n\t\t\t\t\t\"description\": \"This is a mid-travel trail slayer that is a fantastic \" +\n\t\t\t\t\t\t\"daily driver or one bike quiver. The Shimano Claris 8-speed groupset \" +\n\t\t\t\t\t\t\"gives plenty of gear range to tackle hills and there\\u2019s room for \" +\n\t\t\t\t\t\t\"mudguards and a rack too.  This is the bike for the rider who wants \" +\n\t\t\t\t\t\t\"trail manners with low fuss ownership.\",\n\t\t\t\t\t\"price\":  1920,\n\t\t\t\t\t\"specs\":  map[string]interface{}{\"material\": \"carbon\", \"weight\": 13.1},\n\t\t\t\t\t\"colors\": []interface{}{\"black\", \"silver\"},\n\t\t\t\t},\n\t\t\t\tmap[string]interface{}{\n\t\t\t\t\t\"id\":    \"bike:2\",\n\t\t\t\t\t\"model\": \"Quaoar\",\n\t\t\t\t\t\"description\": \"Redesigned for the 2020 model year, this bike \" +\n\t\t\t\t\t\t\"impressed our testers and is the best all-around trail bike we've \" +\n\t\t\t\t\t\t\"ever tested. The Shimano gear system effectively does away with an \" +\n\t\t\t\t\t\t\"external cassette, so is super low maintenance in terms of wear \" +\n\t\t\t\t\t\t\"and tear. All in all it's an impressive package for the price, \" +\n\t\t\t\t\t\t\"making it very competitive.\",\n\t\t\t\t\t\"price\":  2072,\n\t\t\t\t\t\"specs\":  map[string]interface{}{\"material\": \"aluminium\", \"weight\": 7.9},\n\t\t\t\t\t\"colors\": []interface{}{\"black\", \"white\"},\n\t\t\t\t},\n\t\t\t\tmap[string]interface{}{\n\t\t\t\t\t\"id\":    \"bike:3\",\n\t\t\t\t\t\"model\": \"Weywot\",\n\t\t\t\t\t\"description\": \"This bike gives kids aged six years and older \" +\n\t\t\t\t\t\t\"a durable and uberlight mountain bike for their first experience \" +\n\t\t\t\t\t\t\"on tracks and easy cruising through forests and fields. A set of \" +\n\t\t\t\t\t\t\"powerful Shimano hydraulic disc brakes provide ample stopping \" +\n\t\t\t\t\t\t\"ability. If you're after a budget option, this is one of the best \" +\n\t\t\t\t\t\t\"bikes you could get.\",\n\t\t\t\t\t\"price\": 3264,\n\t\t\t\t\t\"specs\": map[string]interface{}{\"material\": \"alloy\", \"weight\": 13.8},\n\t\t\t\t},\n\t\t\t},\n\t\t\t\"commuter_bikes\": []interface{}{\n\t\t\t\tmap[string]interface{}{\n\t\t\t\t\t\"id\":    \"bike:4\",\n\t\t\t\t\t\"model\": \"Salacia\",\n\t\t\t\t\t\"description\": \"This bike is a great option for anyone who just \" +\n\t\t\t\t\t\t\"wants a bike to get about on With a slick-shifting Claris gears \" +\n\t\t\t\t\t\t\"from Shimano\\u2019s, this is a bike which doesn\\u2019t break the \" +\n\t\t\t\t\t\t\"bank and delivers craved performance.  It\\u2019s for the rider \" +\n\t\t\t\t\t\t\"who wants both efficiency and capability.\",\n\t\t\t\t\t\"price\":  1475,\n\t\t\t\t\t\"specs\":  map[string]interface{}{\"material\": \"aluminium\", \"weight\": 16.6},\n\t\t\t\t\t\"colors\": []interface{}{\"black\", \"silver\"},\n\t\t\t\t},\n\t\t\t\tmap[string]interface{}{\n\t\t\t\t\t\"id\":    \"bike:5\",\n\t\t\t\t\t\"model\": \"Mimas\",\n\t\t\t\t\t\"description\": \"A real joy to ride, this bike got very high \" +\n\t\t\t\t\t\t\"scores in last years Bike of the year report. The carefully \" +\n\t\t\t\t\t\t\"crafted 50-34 tooth chainset and 11-32 tooth cassette give an \" +\n\t\t\t\t\t\t\"easy-on-the-legs bottom gear for climbing, and the high-quality \" +\n\t\t\t\t\t\t\"Vittoria Zaffiro tires give balance and grip.It includes \" +\n\t\t\t\t\t\t\"a low-step frame , our memory foam seat, bump-resistant shocks and \" +\n\t\t\t\t\t\t\"conveniently placed thumb throttle. Put it all together and you \" +\n\t\t\t\t\t\t\"get a bike that helps redefine what can be done for this price.\",\n\t\t\t\t\t\"price\": 3941,\n\t\t\t\t\t\"specs\": map[string]interface{}{\"material\": \"alloy\", \"weight\": 11.6},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tres1, err := rdb.JSONSet(ctx, \"bikes:inventory\", \"$\", inventory_json).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> OK","section_id":"jsonpath-examples"},{"id":"jsonpath-examples-ex4","language":"java","code":"String inventory_json_str = \"{\" + \"    \\\"inventory\\\": {\" + \"        \\\"mountain_bikes\\\": [\" + \"            {\"\n                    + \"                \\\"id\\\": \\\"bike:1\\\",\" + \"                \\\"model\\\": \\\"Phoebe\\\",\"\n                    + \"                \\\"description\\\": \\\"This is a mid-travel trail slayer that is a \"\n                    + \"fantastic daily driver or one bike quiver. The Shimano Claris 8-speed groupset \"\n                    + \"gives plenty of gear range to tackle hills and there\\u2019s room for mudguards \"\n                    + \"and a rack too.  This is the bike for the rider who wants trail manners with \" + \"low fuss ownership.\\\",\"\n                    + \"                \\\"price\\\": 1920,\"\n                    + \"                \\\"specs\\\": {\\\"material\\\": \\\"carbon\\\", \\\"weight\\\": 13.1},\"\n                    + \"                \\\"colors\\\": [\\\"black\\\", \\\"silver\\\"]\" + \"            },\" + \"            {\"\n                    + \"                \\\"id\\\": \\\"bike:2\\\",\" + \"                \\\"model\\\": \\\"Quaoar\\\",\"\n                    + \"                \\\"description\\\": \\\"Redesigned for the 2020 model year, this \"\n                    + \"bike impressed our testers and is the best all-around trail bike we've ever \"\n                    + \"tested. The Shimano gear system effectively does away with an external cassette, \"\n                    + \"so is super low maintenance in terms of wear and tear. All in all it's an \"\n                    + \"impressive package for the price, making it very competitive.\\\",\" + \"                \\\"price\\\": 2072,\"\n                    + \"                \\\"specs\\\": {\\\"material\\\": \\\"aluminium\\\", \\\"weight\\\": 7.9},\"\n                    + \"                \\\"colors\\\": [\\\"black\\\", \\\"white\\\"]\" + \"            },\" + \"            {\"\n                    + \"                \\\"id\\\": \\\"bike:3\\\",\" + \"                \\\"model\\\": \\\"Weywot\\\",\"\n                    + \"                \\\"description\\\": \\\"This bike gives kids aged six years and older \"\n                    + \"a durable and uberlight mountain bike for their first experience on tracks and easy \"\n                    + \"cruising through forests and fields. A set of powerful Shimano hydraulic disc brakes \"\n                    + \"provide ample stopping ability. If you're after a budget option, this is one of the \"\n                    + \"best bikes you could get.\\\",\" + \"                \\\"price\\\": 3264,\"\n                    + \"                \\\"specs\\\": {\\\"material\\\": \\\"alloy\\\", \\\"weight\\\": 13.8}\" + \"            }\" + \"        ],\"\n                    + \"        \\\"commuter_bikes\\\": [\" + \"            {\" + \"                \\\"id\\\": \\\"bike:4\\\",\"\n                    + \"                \\\"model\\\": \\\"Salacia\\\",\"\n                    + \"                \\\"description\\\": \\\"This bike is a great option for anyone who just \"\n                    + \"wants a bike to get about on With a slick-shifting Claris gears from Shimano\\u2019s, \"\n                    + \"this is a bike which doesn\\u2019t break the bank and delivers craved performance.  \"\n                    + \"It\\u2019s for the rider who wants both efficiency and capability.\\\",\"\n                    + \"                \\\"price\\\": 1475,\"\n                    + \"                \\\"specs\\\": {\\\"material\\\": \\\"aluminium\\\", \\\"weight\\\": 16.6},\"\n                    + \"                \\\"colors\\\": [\\\"black\\\", \\\"silver\\\"]\" + \"            },\" + \"            {\"\n                    + \"                \\\"id\\\": \\\"bike:5\\\",\" + \"                \\\"model\\\": \\\"Mimas\\\",\"\n                    + \"                \\\"description\\\": \\\"A real joy to ride, this bike got very high scores \"\n                    + \"in last years Bike of the year report. The carefully crafted 50-34 tooth chainset \"\n                    + \"and 11-32 tooth cassette give an easy-on-the-legs bottom gear for climbing, and the \"\n                    + \"high-quality Vittoria Zaffiro tires give balance and grip.It includes a low-step \"\n                    + \"frame , our memory foam seat, bump-resistant shocks and conveniently placed thumb \"\n                    + \"throttle. Put it all together and you get a bike that helps redefine what can be \"\n                    + \"done for this price.\\\",\" + \"                \\\"price\\\": 3941,\"\n                    + \"                \\\"specs\\\": {\\\"material\\\": \\\"alloy\\\", \\\"weight\\\": 11.6}\" + \"            }\" + \"        ]\"\n                    + \"    }\" + \"}\";\n\n            Charset charset = Charset.forName(\"UTF-8\");\n            ByteBuffer inventory_json = charset.encode(inventory_json_str);\n\n            CompletableFuture<Void> setBikes = asyncCommands\n                    .jsonSet(\"bikes:inventory\", JsonPath.ROOT_PATH, parser.loadJsonValue(inventory_json))\n                    .thenAccept(System.out::println) // >>> OK\n                    .toCompletableFuture();","section_id":"jsonpath-examples"},{"id":"jsonpath-examples-ex5","language":"java","code":"String inventory_json_str = \"{\" + \"    \\\"inventory\\\": {\" + \"        \\\"mountain_bikes\\\": [\" + \"            {\"\n                    + \"                \\\"id\\\": \\\"bike:1\\\",\" + \"                \\\"model\\\": \\\"Phoebe\\\",\"\n                    + \"                \\\"description\\\": \\\"This is a mid-travel trail slayer that is a \"\n                    + \"fantastic daily driver or one bike quiver. The Shimano Claris 8-speed groupset \"\n                    + \"gives plenty of gear range to tackle hills and there\\u2019s room for mudguards \"\n                    + \"and a rack too.  This is the bike for the rider who wants trail manners with \" + \"low fuss ownership.\\\",\"\n                    + \"                \\\"price\\\": 1920,\"\n                    + \"                \\\"specs\\\": {\\\"material\\\": \\\"carbon\\\", \\\"weight\\\": 13.1},\"\n                    + \"                \\\"colors\\\": [\\\"black\\\", \\\"silver\\\"]\" + \"            },\" + \"            {\"\n                    + \"                \\\"id\\\": \\\"bike:2\\\",\" + \"                \\\"model\\\": \\\"Quaoar\\\",\"\n                    + \"                \\\"description\\\": \\\"Redesigned for the 2020 model year, this \"\n                    + \"bike impressed our testers and is the best all-around trail bike we've ever \"\n                    + \"tested. The Shimano gear system effectively does away with an external cassette, \"\n                    + \"so is super low maintenance in terms of wear and tear. All in all it's an \"\n                    + \"impressive package for the price, making it very competitive.\\\",\" + \"                \\\"price\\\": 2072,\"\n                    + \"                \\\"specs\\\": {\\\"material\\\": \\\"aluminium\\\", \\\"weight\\\": 7.9},\"\n                    + \"                \\\"colors\\\": [\\\"black\\\", \\\"white\\\"]\" + \"            },\" + \"            {\"\n                    + \"                \\\"id\\\": \\\"bike:3\\\",\" + \"                \\\"model\\\": \\\"Weywot\\\",\"\n                    + \"                \\\"description\\\": \\\"This bike gives kids aged six years and older \"\n                    + \"a durable and uberlight mountain bike for their first experience on tracks and easy \"\n                    + \"cruising through forests and fields. A set of powerful Shimano hydraulic disc brakes \"\n                    + \"provide ample stopping ability. If you're after a budget option, this is one of the \"\n                    + \"best bikes you could get.\\\",\" + \"                \\\"price\\\": 3264,\"\n                    + \"                \\\"specs\\\": {\\\"material\\\": \\\"alloy\\\", \\\"weight\\\": 13.8}\" + \"            }\" + \"        ],\"\n                    + \"        \\\"commuter_bikes\\\": [\" + \"            {\" + \"                \\\"id\\\": \\\"bike:4\\\",\"\n                    + \"                \\\"model\\\": \\\"Salacia\\\",\"\n                    + \"                \\\"description\\\": \\\"This bike is a great option for anyone who just \"\n                    + \"wants a bike to get about on With a slick-shifting Claris gears from Shimano\\u2019s, \"\n                    + \"this is a bike which doesn\\u2019t break the bank and delivers craved performance.  \"\n                    + \"It\\u2019s for the rider who wants both efficiency and capability.\\\",\"\n                    + \"                \\\"price\\\": 1475,\"\n                    + \"                \\\"specs\\\": {\\\"material\\\": \\\"aluminium\\\", \\\"weight\\\": 16.6},\"\n                    + \"                \\\"colors\\\": [\\\"black\\\", \\\"silver\\\"]\" + \"            },\" + \"            {\"\n                    + \"                \\\"id\\\": \\\"bike:5\\\",\" + \"                \\\"model\\\": \\\"Mimas\\\",\"\n                    + \"                \\\"description\\\": \\\"A real joy to ride, this bike got very high scores \"\n                    + \"in last years Bike of the year report. The carefully crafted 50-34 tooth chainset \"\n                    + \"and 11-32 tooth cassette give an easy-on-the-legs bottom gear for climbing, and the \"\n                    + \"high-quality Vittoria Zaffiro tires give balance and grip.It includes a low-step \"\n                    + \"frame , our memory foam seat, bump-resistant shocks and conveniently placed thumb \"\n                    + \"throttle. Put it all together and you get a bike that helps redefine what can be \"\n                    + \"done for this price.\\\",\" + \"                \\\"price\\\": 3941,\"\n                    + \"                \\\"specs\\\": {\\\"material\\\": \\\"alloy\\\", \\\"weight\\\": 11.6}\" + \"            }\" + \"        ]\"\n                    + \"    }\" + \"}\";\n\n            Charset charset = Charset.forName(\"UTF-8\");\n            ByteBuffer inventory_json = charset.encode(inventory_json_str);\n\n            Mono<Void> setBikes = reactiveCommands\n                    .jsonSet(\"bikes:inventory\", JsonPath.ROOT_PATH, parser.loadJsonValue(inventory_json))\n                    .doOnNext(System.out::println) // >>> OK\n                    .then();","section_id":"jsonpath-examples"},{"id":"jsonpath-examples-ex6","language":"java","code":"String inventory_json = \"{\"\n        + \"    \\\"inventory\\\": {\"\n        + \"        \\\"mountain_bikes\\\": [\"\n        + \"            {\"\n        + \"                \\\"id\\\": \\\"bike:1\\\",\"\n        + \"                \\\"model\\\": \\\"Phoebe\\\",\"\n        + \"                \\\"description\\\": \\\"This is a mid-travel trail slayer that is a \"\n        + \"fantastic daily driver or one bike quiver. The Shimano Claris 8-speed groupset \"\n        + \"gives plenty of gear range to tackle hills and there\\u2019s room for mudguards \"\n        + \"and a rack too.  This is the bike for the rider who wants trail manners with \"\n        + \"low fuss ownership.\\\",\"\n        + \"                \\\"price\\\": 1920,\"\n        + \"                \\\"specs\\\": {\\\"material\\\": \\\"carbon\\\", \\\"weight\\\": 13.1},\"\n        + \"                \\\"colors\\\": [\\\"black\\\", \\\"silver\\\"]\"\n        + \"            },\"\n        + \"            {\"\n        + \"                \\\"id\\\": \\\"bike:2\\\",\"\n        + \"                \\\"model\\\": \\\"Quaoar\\\",\"\n        + \"                \\\"description\\\": \\\"Redesigned for the 2020 model year, this \"\n        + \"bike impressed our testers and is the best all-around trail bike we've ever \"\n        + \"tested. The Shimano gear system effectively does away with an external cassette, \"\n        + \"so is super low maintenance in terms of wear and tear. All in all it's an \"\n        + \"impressive package for the price, making it very competitive.\\\",\"\n        + \"                \\\"price\\\": 2072,\"\n        + \"                \\\"specs\\\": {\\\"material\\\": \\\"aluminium\\\", \\\"weight\\\": 7.9},\"\n        + \"                \\\"colors\\\": [\\\"black\\\", \\\"white\\\"]\"\n        + \"            },\"\n        + \"            {\"\n        + \"                \\\"id\\\": \\\"bike:3\\\",\"\n        + \"                \\\"model\\\": \\\"Weywot\\\",\"\n        + \"                \\\"description\\\": \\\"This bike gives kids aged six years and older \"\n        + \"a durable and uberlight mountain bike for their first experience on tracks and easy \"\n        + \"cruising through forests and fields. A set of powerful Shimano hydraulic disc brakes \"\n        + \"provide ample stopping ability. If you're after a budget option, this is one of the \"\n        + \"best bikes you could get.\\\",\"\n        + \"                \\\"price\\\": 3264,\"\n        + \"                \\\"specs\\\": {\\\"material\\\": \\\"alloy\\\", \\\"weight\\\": 13.8}\"\n        + \"            }\"\n        + \"        ],\"\n        + \"        \\\"commuter_bikes\\\": [\"\n        + \"            {\"\n        + \"                \\\"id\\\": \\\"bike:4\\\",\"\n        + \"                \\\"model\\\": \\\"Salacia\\\",\"\n        + \"                \\\"description\\\": \\\"This bike is a great option for anyone who just \"\n        + \"wants a bike to get about on With a slick-shifting Claris gears from Shimano\\u2019s, \"\n        + \"this is a bike which doesn\\u2019t break the bank and delivers craved performance.  \"\n        + \"It\\u2019s for the rider who wants both efficiency and capability.\\\",\"\n        + \"                \\\"price\\\": 1475,\"\n        + \"                \\\"specs\\\": {\\\"material\\\": \\\"aluminium\\\", \\\"weight\\\": 16.6},\"\n        + \"                \\\"colors\\\": [\\\"black\\\", \\\"silver\\\"]\"\n        + \"            },\"\n        + \"            {\"\n        + \"                \\\"id\\\": \\\"bike:5\\\",\"\n        + \"                \\\"model\\\": \\\"Mimas\\\",\"\n        + \"                \\\"description\\\": \\\"A real joy to ride, this bike got very high scores \"\n        + \"in last years Bike of the year report. The carefully crafted 50-34 tooth chainset \"\n        + \"and 11-32 tooth cassette give an easy-on-the-legs bottom gear for climbing, and the \"\n        + \"high-quality Vittoria Zaffiro tires give balance and grip.It includes a low-step \"\n        + \"frame , our memory foam seat, bump-resistant shocks and conveniently placed thumb \"\n        + \"throttle. Put it all together and you get a bike that helps redefine what can be \"\n        + \"done for this price.\\\",\"\n        + \"                \\\"price\\\": 3941,\"\n        + \"                \\\"specs\\\": {\\\"material\\\": \\\"alloy\\\", \\\"weight\\\": 11.6}\"\n        + \"            }\"\n        + \"        ]\"\n        + \"    }\"\n        + \"}\";\n\n        String res28 = jedis.jsonSet(\"bikes:inventory\", new Path2(\"$\"), inventory_json);\n        System.out.println(res28);  // >>> OK","section_id":"jsonpath-examples"},{"id":"jsonpath-examples-ex7","language":"javascript","code":"const inventoryJSON = {\n  \"inventory\": {\n    \"mountain_bikes\": [{\n        \"id\": \"bike:1\",\n        \"model\": \"Phoebe\",\n        \"description\": \"This is a mid-travel trail slayer that is a fantastic daily driver or one bike quiver. The Shimano Claris 8-speed groupset gives plenty of gear range to tackle hills and there\\u2019s room for mudguards and a rack too.  This is the bike for the rider who wants trail manners with low fuss ownership.\",\n        \"price\": 1920,\n        \"specs\": {\n          \"material\": \"carbon\",\n          \"weight\": 13.1\n        },\n        \"colors\": [\"black\", \"silver\"],\n      },\n      {\n        \"id\": \"bike:2\",\n        \"model\": \"Quaoar\",\n        \"description\": \"Redesigned for the 2020 model year, this bike impressed our testers and is the best all-around trail bike we've ever tested. The Shimano gear system effectively does away with an external cassette, so is super low maintenance in terms of wear and teaawait client. All in all it's an impressive package for the price, making it very competitive.\",\n        \"price\": 2072,\n        \"specs\": {\n          \"material\": \"aluminium\",\n          \"weight\": 7.9\n        },\n        \"colors\": [\"black\", \"white\"],\n      },\n      {\n        \"id\": \"bike:3\",\n        \"model\": \"Weywot\",\n        \"description\": \"This bike gives kids aged six years and older a durable and uberlight mountain bike for their first experience on tracks and easy cruising through forests and fields. A set of powerful Shimano hydraulic disc brakes provide ample stopping ability. If you're after a budget option, this is one of the best bikes you could get.\",\n        \"price\": 3264,\n        \"specs\": {\n          \"material\": \"alloy\",\n          \"weight\": 13.8\n        },\n      },\n    ],\n    \"commuter_bikes\": [{\n        \"id\": \"bike:4\",\n        \"model\": \"Salacia\",\n        \"description\": \"This bike is a great option for anyone who just wants a bike to get about on With a slick-shifting Claris gears from Shimano\\u2019s, this is a bike which doesn\\u2019t break the bank and delivers craved performance.  It\\u2019s for the rider who wants both efficiency and capability.\",\n        \"price\": 1475,\n        \"specs\": {\n          \"material\": \"aluminium\",\n          \"weight\": 16.6\n        },\n        \"colors\": [\"black\", \"silver\"],\n      },\n      {\n        \"id\": \"bike:5\",\n        \"model\": \"Mimas\",\n        \"description\": \"A real joy to ride, this bike got very high scores in last years Bike of the year report. The carefully crafted 50-34 tooth chainset and 11-32 tooth cassette give an easy-on-the-legs bottom gear for climbing, and the high-quality Vittoria Zaffiro tires give balance and grip.It includes a low-step frame , our memory foam seat, bump-resistant shocks and conveniently placed thumb throttle. Put it all together and you get a bike that helps redefine what can be done for this price.\",\n        \"price\": 3941,\n        \"specs\": {\n          \"material\": \"alloy\",\n          \"weight\": 11.6\n        },\n      },\n    ],\n  }\n};\n\nconst res28 = await client.json.set(\"bikes:inventory\", \"$\", inventoryJSON);\nconsole.log(res28); //  OK","section_id":"jsonpath-examples"},{"id":"jsonpath-examples-ex8","language":"php","code":"$inventory = [\n            'inventory' => [\n                'mountain_bikes' => [\n                    [\n                        'id' => 'bike:1',\n                        'model' => 'Phoebe',\n                        'description' => 'This is a mid-travel trail slayer that is a fantastic daily driver or one bike quiver. The Shimano Claris 8-speed groupset gives plenty of gear range to tackle hills and there’s room for mudguards and a rack too.  This is the bike for the rider who wants trail manners with low fuss ownership.',\n                        'price' => 1920,\n                        'specs' => ['material' => 'carbon', 'weight' => 13.1],\n                        'colors' => ['black', 'silver'],\n                    ],\n                    [\n                        'id' => 'bike:2',\n                        'model' => 'Quaoar',\n                        'description' => \"Redesigned for the 2020 model year, this bike impressed our testers and is the best all-around trail bike we've ever tested. The Shimano gear system effectively does away with an external cassette, so is super low maintenance in terms of wear and tear. All in all it's an impressive package for the price, making it very competitive.\",\n                        'price' => 2072,\n                        'specs' => ['material' => 'aluminium', 'weight' => 7.9],\n                        'colors' => ['black', 'white'],\n                    ],\n                    [\n                        'id' => 'bike:3',\n                        'model' => 'Weywot',\n                        'description' => 'This bike gives kids aged six years and older a durable and uberlight mountain bike for their first experience on tracks and easy cruising through forests and fields. A set of powerful Shimano hydraulic disc brakes provide ample stopping ability. If you\\'re after a budget option, this is one of the best bikes you could get.',\n                        'price' => 3264,\n                        'specs' => ['material' => 'alloy', 'weight' => 13.8],\n                    ],\n                ],\n                'commuter_bikes' => [\n                    [\n                        'id' => 'bike:4',\n                        'model' => 'Salacia',\n                        'description' => 'This bike is a great option for anyone who just wants a bike to get about on With a slick-shifting Claris gears from Shimano’s, this is a bike which doesn’t break the bank and delivers craved performance.  It’s for the rider who wants both efficiency and capability.',\n                        'price' => 1475,\n                        'specs' => ['material' => 'aluminium', 'weight' => 16.6],\n                        'colors' => ['black', 'silver'],\n                    ],\n                    [\n                        'id' => 'bike:5',\n                        'model' => 'Mimas',\n                        'description' => 'A real joy to ride, this bike got very high scores in last years Bike of the year report. The carefully crafted 50-34 tooth chainset and 11-32 tooth cassette give an easy-on-the-legs bottom gear for climbing, and the high-quality Vittoria Zaffiro tires give balance and grip.It includes a low-step frame , our memory foam seat, bump-resistant shocks and conveniently placed thumb throttle. Put it all together and you get a bike that helps redefine what can be done for this price.',\n                        'price' => 3941,\n                        'specs' => ['material' => 'alloy', 'weight' => 11.6],\n                    ],\n                ],\n            ],\n        ];\n        $res1b = $r->jsonset('bikes:inventory', '$', json_encode($inventory, JSON_THROW_ON_ERROR));\n        echo $res1b . PHP_EOL;\n        // >>> OK","section_id":"jsonpath-examples"},{"id":"jsonpath-examples-ex9","language":"python","code":"inventory_json = {\n    \"inventory\": {\n        \"mountain_bikes\": [\n            {\n                \"id\": \"bike:1\",\n                \"model\": \"Phoebe\",\n                \"description\": \"This is a mid-travel trail slayer that is a fantastic \"\n                \"daily driver or one bike quiver. The Shimano Claris 8-speed groupset \"\n                \"gives plenty of gear range to tackle hills and there\\u2019s room for \"\n                \"mudguards and a rack too.  This is the bike for the rider who wants \"\n                \"trail manners with low fuss ownership.\",\n                \"price\": 1920,\n                \"specs\": {\"material\": \"carbon\", \"weight\": 13.1},\n                \"colors\": [\"black\", \"silver\"],\n            },\n            {\n                \"id\": \"bike:2\",\n                \"model\": \"Quaoar\",\n                \"description\": \"Redesigned for the 2020 model year, this bike \"\n                \"impressed our testers and is the best all-around trail bike we've \"\n                \"ever tested. The Shimano gear system effectively does away with an \"\n                \"external cassette, so is super low maintenance in terms of wear \"\n                \"and tear. All in all it's an impressive package for the price, \"\n                \"making it very competitive.\",\n                \"price\": 2072,\n                \"specs\": {\"material\": \"aluminium\", \"weight\": 7.9},\n                \"colors\": [\"black\", \"white\"],\n            },\n            {\n                \"id\": \"bike:3\",\n                \"model\": \"Weywot\",\n                \"description\": \"This bike gives kids aged six years and older \"\n                \"a durable and uberlight mountain bike for their first experience \"\n                \"on tracks and easy cruising through forests and fields. A set of \"\n                \"powerful Shimano hydraulic disc brakes provide ample stopping \"\n                \"ability. If you're after a budget option, this is one of the best \"\n                \"bikes you could get.\",\n                \"price\": 3264,\n                \"specs\": {\"material\": \"alloy\", \"weight\": 13.8},\n            },\n        ],\n        \"commuter_bikes\": [\n            {\n                \"id\": \"bike:4\",\n                \"model\": \"Salacia\",\n                \"description\": \"This bike is a great option for anyone who just \"\n                \"wants a bike to get about on With a slick-shifting Claris gears \"\n                \"from Shimano\\u2019s, this is a bike which doesn\\u2019t break the \"\n                \"bank and delivers craved performance.  It\\u2019s for the rider \"\n                \"who wants both efficiency and capability.\",\n                \"price\": 1475,\n                \"specs\": {\"material\": \"aluminium\", \"weight\": 16.6},\n                \"colors\": [\"black\", \"silver\"],\n            },\n            {\n                \"id\": \"bike:5\",\n                \"model\": \"Mimas\",\n                \"description\": \"A real joy to ride, this bike got very high \"\n                \"scores in last years Bike of the year report. The carefully \"\n                \"crafted 50-34 tooth chainset and 11-32 tooth cassette give an \"\n                \"easy-on-the-legs bottom gear for climbing, and the high-quality \"\n                \"Vittoria Zaffiro tires give balance and grip.It includes \"\n                \"a low-step frame , our memory foam seat, bump-resistant shocks and \"\n                \"conveniently placed thumb throttle. Put it all together and you \"\n                \"get a bike that helps redefine what can be done for this price.\",\n                \"price\": 3941,\n                \"specs\": {\"material\": \"alloy\", \"weight\": 11.6},\n            },\n        ],\n    }\n}\n\nres1 = r.json().set(\"bikes:inventory\", \"$\", inventory_json)\nprint(res1)  # >>> True","section_id":"jsonpath-examples"},{"id":"jsonpath-examples-ex10","language":"ruby","code":"inventory_json = {\n  'inventory' => {\n    'mountain_bikes' => [\n      {\n        'id' => 'bike:1',\n        'model' => 'Phoebe',\n        'description' => 'This is a mid-travel trail slayer that is a fantastic ' \\\n          'daily driver or one bike quiver. The Shimano Claris 8-speed groupset ' \\\n          'gives plenty of gear range to tackle hills and there’s room for ' \\\n          'mudguards and a rack too.  This is the bike for the rider who wants ' \\\n          'trail manners with low fuss ownership.',\n        'price' => 1920,\n        'specs' => { 'material' => 'carbon', 'weight' => 13.1 },\n        'colors' => %w[black silver]\n      },\n      {\n        'id' => 'bike:2',\n        'model' => 'Quaoar',\n        'description' => 'Redesigned for the 2020 model year, this bike ' \\\n          \"impressed our testers and is the best all-around trail bike we've \" \\\n          'ever tested. The Shimano gear system effectively does away with an ' \\\n          'external cassette, so is super low maintenance in terms of wear ' \\\n          \"and tear. All in all it's an impressive package for the price, \" \\\n          'making it very competitive.',\n        'price' => 2072,\n        'specs' => { 'material' => 'aluminium', 'weight' => 7.9 },\n        'colors' => %w[black white]\n      },\n      {\n        'id' => 'bike:3',\n        'model' => 'Weywot',\n        'description' => 'This bike gives kids aged six years and older ' \\\n          'a durable and uberlight mountain bike for their first experience ' \\\n          'on tracks and easy cruising through forests and fields. A set of ' \\\n          'powerful Shimano hydraulic disc brakes provide ample stopping ' \\\n          \"ability. If you're after a budget option, this is one of the best \" \\\n          'bikes you could get.',\n        'price' => 3264,\n        'specs' => { 'material' => 'alloy', 'weight' => 13.8 }\n      }\n    ],\n    'commuter_bikes' => [\n      {\n        'id' => 'bike:4',\n        'model' => 'Salacia',\n        'description' => 'This bike is a great option for anyone who just ' \\\n          'wants a bike to get about on With a slick-shifting Claris gears ' \\\n          'from Shimano’s, this is a bike which doesn’t break the ' \\\n          \"bank and delivers craved performance.  It's for the rider \" \\\n          'who wants both efficiency and capability.',\n        'price' => 1475,\n        'specs' => { 'material' => 'aluminium', 'weight' => 16.6 },\n        'colors' => %w[black silver]\n      },\n      {\n        'id' => 'bike:5',\n        'model' => 'Mimas',\n        'description' => 'A real joy to ride, this bike got very high ' \\\n          'scores in last years Bike of the year report. The carefully ' \\\n          'crafted 50-34 tooth chainset and 11-32 tooth cassette give an ' \\\n          'easy-on-the-legs bottom gear for climbing, and the high-quality ' \\\n          'Vittoria Zaffiro tires give balance and grip.It includes ' \\\n          'a low-step frame , our memory foam seat, bump-resistant shocks and ' \\\n          'conveniently placed thumb throttle. Put it all together and you ' \\\n          'get a bike that helps redefine what can be done for this price.',\n        'price' => 3941,\n        'specs' => { 'material' => 'alloy', 'weight' => 11.6 }\n      }\n    ]\n  }\n}\n\nres1 = r.json_set('bikes:inventory', '$', inventory_json)\nputs res1 # >>> OK","section_id":"jsonpath-examples"},{"id":"jsonpath-examples-ex11","language":"rust","code":"let res29: bool = r\n            .json_set(\"bikes:inventory\", \"$\", &inventory_json())\n            .await\n            .expect(\"Failed to run JSON.SET\");\n        print_set_result(res29); // >>> OK","section_id":"jsonpath-examples"},{"id":"jsonpath-examples-ex12","language":"rust","code":"let res29: bool = r\n            .json_set(\"bikes:inventory\", \"$\", &inventory_json())\n            .expect(\"Failed to run JSON.SET\");\n        print_set_result(res29); // >>> OK","section_id":"jsonpath-examples"},{"id":"access-examples-ex0","language":"plaintext","code":"> JSON.GET bikes:inventory $.inventory.*\n\"[[{\\\"id\\\":\\\"bike:1\\\",\\\"model\\\":\\\"Phoebe\\\",\\\"description\\\":\\\"This is a mid-travel trail slayer that is a fantastic daily driver or one bike quiver. The Shimano Claris 8-speed groupset gives plenty of gear range to tackle hills and there's room for mudguards and a rack too. This is the bike for the rider who wants trail manners with low fuss ownership.\\\",\\\"price\\\":1920,\\\"specs\\\":{\\\"material\\\":\\\"carbon\\\",\\\"weight\\\":13.1},\\\"colors\\\":[\\\"black\\\",\\\"silver\\\"]},{\\\"id\\\":\\\"bike:2\\\",\\\"model\\\":\\\"Quaoar\\\",\\\"description\\\":\\\"Redesigned for the 2020 model year, this bike impressed our testers and is the best all-around trail bike we've ever tested. The Shimano gear system effectively does away with an external cassette, so is super low maintenance in terms of wear and tear. All in all it's an impressive package for the price, making it very competitive.\\\",\\\"price\\\":2072,\\\"specs\\\":{\\\"material\\\":\\\"aluminium\\\",\\\"weight\\\":7.9},\\\"colors\\\":[\\\"black\\\",\\\"white\\\"]},{\\\"id\\\":\\\"bike:3\\\",\\\"model\\\":\\\"Weywot\\\",\\\"description\\\":\\\"This bike gives kids aged six years and older a durable and uberlight mountain bike for their first experience on tracks and easy cruising through forests and fields. A set of powerful Shimano hydraulic disc brakes provide ample stopping ability. If you're after a budget option, this is one of the best bikes you could get.\\\",\\\"price\\\":3264,\\\"specs\\\":{\\\"material\\\":\\\"alloy\\\",\\\"weight\\\":13.8}}],[{\\\"id\\\":\\\"bike:4\\\",\\\"model\\\":\\\"Salacia\\\",\\\"description\\\":\\\"This bike is a great option for anyone who just wants a bike to get about on With a slick-shifting Claris gears from Shimano's, this is a bike which doesn't break the bank and delivers craved performance. It's for the rider who wants both efficiency and capability.\\\",\\\"price\\\":1475,\\\"specs\\\":{\\\"material\\\":\\\"aluminium\\\",\\\"weight\\\":16.6},\\\"colors\\\":[\\\"black\\\",\\\"silver\\\"]},{\\\"id\\\":\\\"bike:5\\\",\\\"model\\\":\\\"Mimas\\\",\\\"description\\\":\\\"A real joy to ride, this bike got very high scores in last years Bike of the year report. The carefully crafted 50-34 tooth chainset and 11-32 tooth cassette give an easy-on-the-legs bottom gear for climbing, and the high-quality Vittoria Zaffiro tires give balance and grip.It includes a low-step frame , our memory foam seat, bump-resistant shocks and conveniently placed thumb throttle. Put it all together and you get a bike that helps redefine what can be done for this price.\\\",\\\"price\\\":3941,\\\"specs\\\":{\\\"material\\\":\\\"alloy\\\",\\\"weight\\\":11.6}}]]\"","section_id":"access-examples"},{"id":"access-examples-ex1","language":"csharp","code":"RedisResult res29 = db.JSON().Get(\"bikes:inventory\", path: \"$.inventory.*\");\n        Console.WriteLine(res29);   // >>> {[[{\"id\":\"bike:1\",\"model\":\"Phoebe\", ...","section_id":"access-examples"},{"id":"access-examples-ex2","language":"go","code":"res2, err := rdb.JSONGetWithArgs(ctx, \"bikes:inventory\",\n\t\t&redis.JSONGetArgs{Indent: \"  \", Newline: \"\\n\", Space: \" \"},\n\t\t\"$.inventory.*\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2)\n\t// >>>\n\t// [\n\t//   [\n\t//     {\n\t//       \"colors\": [\n\t//         \"black\",\n\t//         \"silver\"\n\t// ...","section_id":"access-examples"},{"id":"access-examples-ex3","language":"java","code":"CompletableFuture<Void> getBikes = asyncCommands.jsonGet(\"bikes:inventory\", JsonPath.of(\"$.inventory.*\"))\n                    .thenAccept(System.out::println)\n                    // >>> [[[{\"id\":\"bike:1\",\"model\":\"Phoebe\",...\n                    .toCompletableFuture();","section_id":"access-examples"},{"id":"access-examples-ex4","language":"java","code":"Mono<Void> getBikes = reactiveCommands.jsonGet(\"bikes:inventory\", JsonPath.of(\"$.inventory.*\")).collectList()\n                    .doOnNext(System.out::println) // >>> [[[{\"id\":\"bike:1\",\"model\":\"Phoebe\",...\n                    .then();","section_id":"access-examples"},{"id":"access-examples-ex5","language":"java","code":"Object res29 = jedis.jsonGet(\"bikes:inventory\", new Path2(\"$.inventory.*\"));\n        System.out.println(res29);\n        // >>> [[{\"specs\":{\"material\":\"carbon\",\"weight\":13.1},\"price\":1920, ...","section_id":"access-examples"},{"id":"access-examples-ex6","language":"javascript","code":"const res29 = await client.json.get(\"bikes:inventory\", {\n  path: \"$.inventory.*\"\n});\nconsole.log(res29);\n/*\n[\n  [\n    {\n      id: 'bike:1',\n      model: 'Phoebe',\n      description: 'This is a mid-travel trail slayer that is a fantastic daily driver or one bike quiver. The Shimano Claris 8-speed groupset gives plenty of gear range to tackle hills and there’s room for mudguards and a rack too.  This is the bike for the rider who wants trail manners with low fuss ownership.',\n      price: 1920,\n      specs: [Object],\n      colors: [Array]\n    },\n    {\n      id: 'bike:2',\n      model: 'Quaoar',\n      description: \"Redesigned for the 2020 model year, this bike impressed our testers and is the best all-around trail bike we've ever tested. The Shimano gear system effectively does away with an external cassette, so is super low maintenance in terms of wear and teaawait client. All in all it's an impressive package for the price, making it very competitive.\",\n      price: 2072,\n      specs: [Object],\n      colors: [Array]\n    },\n    {\n      id: 'bike:3',\n      model: 'Weywot',\n      description: \"This bike gives kids aged six years and older a durable and uberlight mountain bike for their first experience on tracks and easy cruising through forests and fields. A set of powerful Shimano hydraulic disc brakes provide ample stopping ability. If you're after a budget option, this is one of the best bikes you could get.\",\n      price: 3264,\n      specs: [Object]\n    }\n  ],\n  [\n    {\n      id: 'bike:4',\n      model: 'Salacia',\n      description: 'This bike is a great option for anyone who just wants a bike to get about on With a slick-shifting Claris gears from Shimano’s, this is a bike which doesn’t break the bank and delivers craved performance.  It’s for the rider who wants both efficiency and capability.',\n      price: 1475,\n      specs: [Object],\n      colors: [Array]\n    },\n    {\n      id: 'bike:5',\n      model: 'Mimas',\n      description: 'A real joy to ride, this bike got very high scores in last years Bike of the year report. The carefully crafted 50-34 tooth chainset and 11-32 tooth cassette give an easy-on-the-legs bottom gear for climbing, and the high-quality Vittoria Zaffiro tires give balance and grip.It includes a low-step frame , our memory foam seat, bump-resistant shocks and conveniently placed thumb throttle. Put it all together and you get a bike that helps redefine what can be done for this price.',\n      price: 3941,\n      specs: [Object]\n    }\n  ]\n]\n*/","section_id":"access-examples"},{"id":"access-examples-ex7","language":"php","code":"$res2b = $r->jsonget('bikes:inventory', '', '', '', '$.inventory.*');\n        echo $res2b . PHP_EOL;\n        // >>> [{'id': 'bike:1', 'model': 'Phoebe',","section_id":"access-examples"},{"id":"access-examples-ex8","language":"python","code":"res2 = r.json().get(\"bikes:inventory\", \"$.inventory.*\")\nprint(res2)\n# >>>    [[{'id': 'bike:1', 'model': 'Phoebe',\n# >>>       'description': 'This is a mid-travel trail slayer...","section_id":"access-examples"},{"id":"access-examples-ex9","language":"ruby","code":"res2 = r.json_get('bikes:inventory', '$.inventory.*')\np res2\n# >>> [[{\"id\"=>\"bike:1\", \"model\"=>\"Phoebe\",\n# >>>    \"description\"=>\"This is a mid-travel trail slayer...","section_id":"access-examples"},{"id":"access-examples-ex10","language":"rust","code":"let res30: String = r\n            .json_get(\"bikes:inventory\", \"$.inventory.*\")\n            .await\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{res30}\");\n        // >>> [[{\"id\":\"bike:1\",\"model\":\"Phoebe\",\"description\":\"This is a mid-travel trail slayer...","section_id":"access-examples"},{"id":"access-examples-ex11","language":"rust","code":"let res30: String = r\n            .json_get(\"bikes:inventory\", \"$.inventory.*\")\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{res30}\");\n        // >>> [[{\"id\":\"bike:1\",\"model\":\"Phoebe\",\"description\":\"This is a mid-travel trail slayer...","section_id":"access-examples"},{"id":"access-examples-ex12","language":"plaintext","code":"> JSON.GET bikes:inventory $.inventory.mountain_bikes[*].model\n\"[\\\"Phoebe\\\",\\\"Quaoar\\\",\\\"Weywot\\\"]\"\n> JSON.GET bikes:inventory '$.inventory[\"mountain_bikes\"][*].model'\n\"[\\\"Phoebe\\\",\\\"Quaoar\\\",\\\"Weywot\\\"]\"\n> JSON.GET bikes:inventory '$..mountain_bikes[*].model'\n\"[\\\"Phoebe\\\",\\\"Quaoar\\\",\\\"Weywot\\\"]\"","section_id":"access-examples"},{"id":"access-examples-ex13","language":"csharp","code":"RedisResult res30 = db.JSON().Get(\"bikes:inventory\", path: \"$.inventory.mountain_bikes[*].model\");\n        Console.WriteLine(res30);   // >>> [\"Phoebe\",\"Quaoar\",\"Weywot\"]\n\n        RedisResult res31 = db.JSON().Get(\"bikes:inventory\", path: \"$.inventory[\\\"mountain_bikes\\\"][*].model\");\n        Console.WriteLine(res31);   // >>> [\"Phoebe\",\"Quaoar\",\"Weywot\"]\n\n        RedisResult res32 = db.JSON().Get(\"bikes:inventory\", path: \"$..mountain_bikes[*].model\");\n        Console.WriteLine(res32);   // >>> [\"Phoebe\",\"Quaoar\",\"Weywot\"]","section_id":"access-examples"},{"id":"access-examples-ex14","language":"go","code":"res3, err := rdb.JSONGet(ctx, \"bikes:inventory\",\n\t\t\"$.inventory.mountain_bikes[*].model\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3)\n\t// >>> [\"Phoebe\",\"Quaoar\",\"Weywot\"]\n\n\tres4, err := rdb.JSONGet(ctx,\n\t\t\"bikes:inventory\", \"$.inventory[\\\"mountain_bikes\\\"][*].model\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4)\n\t// >>> [\"Phoebe\",\"Quaoar\",\"Weywot\"]\n\n\tres5, err := rdb.JSONGet(ctx,\n\t\t\"bikes:inventory\", \"$..mountain_bikes[*].model\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res5)\n\t// >>> [\"Phoebe\",\"Quaoar\",\"Weywot\"]","section_id":"access-examples"},{"id":"access-examples-ex15","language":"java","code":"CompletableFuture<Void> getMtnBikes = asyncCommands\n                    .jsonGet(\"bikes:inventory\", JsonPath.of(\"$.inventory.mountain_bikes[*].model\")).thenCompose(res19 -> {\n                        System.out.println(res19); // >>> [[\"Phoebe\",\"Quaoar\",\"Weywot\"]]\n                        return asyncCommands.jsonGet(\"bikes:inventory\",\n                                JsonPath.of(\"$.inventory[\\\"mountain_bikes\\\"][*].model\"));\n                    }).thenCompose(res20 -> {\n                        System.out.println(res20); // >>> [[\"Phoebe\",\"Quaoar\",\"Weywot\"]]\n                        return asyncCommands.jsonGet(\"bikes:inventory\", JsonPath.of(\"$..mountain_bikes[*].model\"));\n                    })\n                    .thenAccept(System.out::println)\n                    // >>> [[\"Phoebe\",\"Quaoar\",\"Weywot\"]]\n                    .toCompletableFuture();","section_id":"access-examples"},{"id":"access-examples-ex16","language":"java","code":"Mono<Void> getMtnBikes = reactiveCommands\n                    .jsonGet(\"bikes:inventory\", JsonPath.of(\"$.inventory.mountain_bikes[*].model\")).collectList()\n                    .doOnNext(res19 -> {\n                        System.out.println(res19); // >>> [[\"Phoebe\",\"Quaoar\",\"Weywot\"]]\n                    })\n                    .flatMap(res19 -> reactiveCommands\n                            .jsonGet(\"bikes:inventory\", JsonPath.of(\"$.inventory[\\\"mountain_bikes\\\"][*].model\")).collectList())\n                    .doOnNext(res20 -> {\n                        System.out.println(res20); // >>> [[\"Phoebe\",\"Quaoar\",\"Weywot\"]]\n                    })\n                    .flatMap(res20 -> reactiveCommands.jsonGet(\"bikes:inventory\", JsonPath.of(\"$..mountain_bikes[*].model\"))\n                            .collectList())\n                    .doOnNext(System.out::println) // >>> [[\"Phoebe\",\"Quaoar\",\"Weywot\"]]\n                    .then();","section_id":"access-examples"},{"id":"access-examples-ex17","language":"java","code":"Object res30 = jedis.jsonGet(\n            \"bikes:inventory\", new Path2(\"$.inventory.mountain_bikes[*].model\")\n        );\n        System.out.println(res30);  // >>> [\"Phoebe\",\"Quaoar\",\"Weywot\"]\n\n        Object res31 = jedis.jsonGet(\n            \"bikes:inventory\", new Path2(\"$.inventory[\\\"mountain_bikes\\\"][*].model\")\n        );\n        System.out.println(res31);  // >>> [\"Phoebe\",\"Quaoar\",\"Weywot\"]\n\n        Object res32 = jedis.jsonGet(\n            \"bikes:inventory\", new Path2(\"$..mountain_bikes[*].model\")\n        );\n        System.out.println(res32);  // >>> [\"Phoebe\",\"Quaoar\",\"Weywot\"]","section_id":"access-examples"},{"id":"access-examples-ex18","language":"javascript","code":"const res30 = await client.json.get(\"bikes:inventory\", {\n  path: \"$.inventory.mountain_bikes[*].model\"\n});\nconsole.log(res30); //  ['Phoebe', 'Quaoar', 'Weywot']\n\nconst res31 = await client.json.get(\"bikes:inventory\", {\n  path: '$.inventory[\"mountain_bikes\"][*].model'\n});\nconsole.log(res31); //  ['Phoebe', 'Quaoar', 'Weywot']\n\nconst res32 = await client.json.get(\"bikes:inventory\", {\n  path: \"$..mountain_bikes[*].model\"\n});\nconsole.log(res32); //  ['Phoebe', 'Quaoar', 'Weywot']","section_id":"access-examples"},{"id":"access-examples-ex19","language":"php","code":"$res3b = $r->jsonget('bikes:inventory', '', '', '', '$.inventory.mountain_bikes[*].model');\n        echo $res3b . PHP_EOL;\n        // >>> [\"Phoebe\",\"Quaoar\",\"Weywot\"]\n\n        $res4b = $r->jsonget('bikes:inventory', '', '', '', '$.inventory[\"mountain_bikes\"][*].model');\n        echo $res4b . PHP_EOL;\n        // >>> [\"Phoebe\",\"Quaoar\",\"Weywot\"]\n\n        $res5b = $r->jsonget('bikes:inventory', '', '', '', '$..mountain_bikes[*].model');\n        echo $res5b . PHP_EOL;\n        // >>> [\"Phoebe\",\"Quaoar\",\"Weywot\"]","section_id":"access-examples"},{"id":"access-examples-ex20","language":"python","code":"res3 = r.json().get(\"bikes:inventory\", \"$.inventory.mountain_bikes[*].model\")\nprint(res3)  # >>> [['Phoebe', 'Quaoar', 'Weywot']]\n\nres4 = r.json().get(\"bikes:inventory\", '$.inventory[\"mountain_bikes\"][*].model')\nprint(res4)  # >>> [['Phoebe', 'Quaoar', 'Weywot']]\n\nres5 = r.json().get(\"bikes:inventory\", \"$..mountain_bikes[*].model\")\nprint(res5)  # >>> [['Phoebe', 'Quaoar', 'Weywot']]","section_id":"access-examples"},{"id":"access-examples-ex21","language":"ruby","code":"res3 = r.json_get('bikes:inventory', '$.inventory.mountain_bikes[*].model')\np res3 # >>> [\"Phoebe\", \"Quaoar\", \"Weywot\"]\n\nres4 = r.json_get('bikes:inventory', '$.inventory[\"mountain_bikes\"][*].model')\np res4 # >>> [\"Phoebe\", \"Quaoar\", \"Weywot\"]\n\nres5 = r.json_get('bikes:inventory', '$..mountain_bikes[*].model')\np res5 # >>> [\"Phoebe\", \"Quaoar\", \"Weywot\"]","section_id":"access-examples"},{"id":"access-examples-ex22","language":"rust","code":"let res31: String = r\n            .json_get(\"bikes:inventory\", \"$.inventory.mountain_bikes[*].model\")\n            .await\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{res31}\"); // >>> [[\"Phoebe\",\"Quaoar\",\"Weywot\"]]\n\n        let res32: String = r\n            .json_get(\n                \"bikes:inventory\",\n                r#\"$.inventory[\"mountain_bikes\"][*].model\"#,\n            )\n            .await\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{res32}\"); // >>> [[\"Phoebe\",\"Quaoar\",\"Weywot\"]]\n\n        let res33: String = r\n            .json_get(\"bikes:inventory\", \"$..mountain_bikes[*].model\")\n            .await\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{res33}\"); // >>> [[\"Phoebe\",\"Quaoar\",\"Weywot\"]]","section_id":"access-examples"},{"id":"access-examples-ex23","language":"rust","code":"let res31: String = r\n            .json_get(\"bikes:inventory\", \"$.inventory.mountain_bikes[*].model\")\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{res31}\"); // >>> [[\"Phoebe\",\"Quaoar\",\"Weywot\"]]\n\n        let res32: String = r\n            .json_get(\n                \"bikes:inventory\",\n                r#\"$.inventory[\"mountain_bikes\"][*].model\"#,\n            )\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{res32}\"); // >>> [[\"Phoebe\",\"Quaoar\",\"Weywot\"]]\n\n        let res33: String = r\n            .json_get(\"bikes:inventory\", \"$..mountain_bikes[*].model\")\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{res33}\"); // >>> [[\"Phoebe\",\"Quaoar\",\"Weywot\"]]","section_id":"access-examples"},{"id":"access-examples-ex24","language":"plaintext","code":"> JSON.GET bikes:inventory $..model\n\"[\\\"Phoebe\\\",\\\"Quaoar\\\",\\\"Weywot\\\",\\\"Salacia\\\",\\\"Mimas\\\"]\"","section_id":"access-examples"},{"id":"access-examples-ex25","language":"csharp","code":"RedisResult res33 = db.JSON().Get(\"bikes:inventory\", path: \"$..model\");\n        Console.WriteLine(res33);   // >>> [\"Phoebe\",\"Quaoar\",\"Weywot\",\"Salacia\",\"Mimas\"]","section_id":"access-examples"},{"id":"access-examples-ex26","language":"go","code":"res6, err := rdb.JSONGet(ctx, \"bikes:inventory\", \"$..model\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res6) // >>> [\"Salacia\",\"Mimas\",\"Phoebe\",\"Quaoar\",\"Weywot\"]","section_id":"access-examples"},{"id":"access-examples-ex27","language":"java","code":"CompletableFuture<Void> getModels = asyncCommands.jsonGet(\"bikes:inventory\", JsonPath.of(\"$..model\"))\n                    .thenAccept(System.out::println)\n                    // >>> [[\"Phoebe\",\"Quaoar\",\"Weywot\",\"Salacia\",\"Mimas\"]]\n                    .toCompletableFuture();","section_id":"access-examples"},{"id":"access-examples-ex28","language":"java","code":"Mono<Void> getModels = reactiveCommands.jsonGet(\"bikes:inventory\", JsonPath.of(\"$..model\")).collectList()\n                    .doOnNext(System.out::println) // >>> [[\"Phoebe\",\"Quaoar\",\"Weywot\",\"Salacia\",\"Mimas\"]]\n                    .then();","section_id":"access-examples"},{"id":"access-examples-ex29","language":"java","code":"Object res33 = jedis.jsonGet(\"bikes:inventory\", new Path2(\"$..model\"));\n        System.out.println(res33);\n        // >>> [\"Phoebe\",\"Quaoar\",\"Weywot\",\"Salacia\",\"Mimas\"]","section_id":"access-examples"},{"id":"access-examples-ex30","language":"javascript","code":"const res33 = await client.json.get(\"bikes:inventory\", {\n  path: \"$..model\"\n});\nconsole.log(res33); //  ['Phoebe', 'Quaoar', 'Weywot', 'Salacia', 'Mimas']","section_id":"access-examples"},{"id":"access-examples-ex31","language":"php","code":"$res6b = $r->jsonget('bikes:inventory', '', '', '', '$..model');\n        echo $res6b . PHP_EOL;\n        // >>> [\"Phoebe\",\"Quaoar\",\"Weywot\",\"Salacia\",\"Mimas\"]","section_id":"access-examples"},{"id":"access-examples-ex32","language":"python","code":"res6 = r.json().get(\"bikes:inventory\", \"$..model\")\nprint(res6)  # >>> [['Phoebe', 'Quaoar', 'Weywot', 'Salacia', 'Mimas']]","section_id":"access-examples"},{"id":"access-examples-ex33","language":"ruby","code":"res6 = r.json_get('bikes:inventory', '$..model')\np res6 # >>> [\"Phoebe\", \"Quaoar\", \"Weywot\", \"Salacia\", \"Mimas\"]","section_id":"access-examples"},{"id":"access-examples-ex34","language":"rust","code":"let res34: String = r\n            .json_get(\"bikes:inventory\", \"$..model\")\n            .await\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{res34}\"); // >>> [[\"Phoebe\",\"Quaoar\",\"Weywot\",\"Salacia\",\"Mimas\"]]","section_id":"access-examples"},{"id":"access-examples-ex35","language":"rust","code":"let res34: String = r\n            .json_get(\"bikes:inventory\", \"$..model\")\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{res34}\"); // >>> [[\"Phoebe\",\"Quaoar\",\"Weywot\",\"Salacia\",\"Mimas\"]]","section_id":"access-examples"},{"id":"access-examples-ex36","language":"plaintext","code":"> JSON.GET bikes:inventory $..mountain_bikes[0:2].model\n\"[\\\"Phoebe\\\",\\\"Quaoar\\\"]\"","section_id":"access-examples"},{"id":"access-examples-ex37","language":"csharp","code":"RedisResult res34 = db.JSON().Get(\"bikes:inventory\", path: \"$..mountain_bikes[0:2].model\");\n        Console.WriteLine(res34);   // >>> [\"Phoebe\",\"Quaoar\"]","section_id":"access-examples"},{"id":"access-examples-ex38","language":"go","code":"res7, err := rdb.JSONGet(ctx, \"bikes:inventory\", \"$..mountain_bikes[0:2].model\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res7) // >>> [\"Phoebe\",\"Quaoar\"]","section_id":"access-examples"},{"id":"access-examples-ex39","language":"java","code":"CompletableFuture<Void> get2MtnBikes = asyncCommands\n                    .jsonGet(\"bikes:inventory\", JsonPath.of(\"$..mountain_bikes[0:2].model\"))\n                    .thenAccept(System.out::println)\n                    // >>> [[\"Phoebe\",\"Quaoar\"]]\n                    .toCompletableFuture();","section_id":"access-examples"},{"id":"access-examples-ex40","language":"java","code":"Mono<Void> get2MtnBikes = reactiveCommands.jsonGet(\"bikes:inventory\", JsonPath.of(\"$..mountain_bikes[0:2].model\"))\n                    .collectList()\n                    .doOnNext(System.out::println) // >>> [[\"Phoebe\",\"Quaoar\"]]\n                    .then();","section_id":"access-examples"},{"id":"access-examples-ex41","language":"java","code":"Object res34 = jedis.jsonGet(\n            \"bikes:inventory\", new Path2(\"$..mountain_bikes[0:2].model\")\n        );\n        System.out.println(res34);  // >>> [\"Phoebe\",\"Quaoar\"]","section_id":"access-examples"},{"id":"access-examples-ex42","language":"javascript","code":"const res34 = await client.json.get(\"bikes:inventory\", {\n  path: \"$..mountain_bikes[0:2].model\"\n});\nconsole.log(res34); //  ['Phoebe', 'Quaoar']","section_id":"access-examples"},{"id":"access-examples-ex43","language":"php","code":"$res7b = $r->jsonget('bikes:inventory', '', '', '', '$..mountain_bikes[0:2].model');\n        echo $res7b . PHP_EOL;\n        // >>> [\"Phoebe\",\"Quaoar\"]","section_id":"access-examples"},{"id":"access-examples-ex44","language":"python","code":"res7 = r.json().get(\"bikes:inventory\", \"$..mountain_bikes[0:2].model\")\nprint(res7)  # >>> [['Phoebe', 'Quaoar']]","section_id":"access-examples"},{"id":"access-examples-ex45","language":"ruby","code":"res7 = r.json_get('bikes:inventory', '$..mountain_bikes[0:2].model')\np res7 # >>> [\"Phoebe\", \"Quaoar\"]","section_id":"access-examples"},{"id":"access-examples-ex46","language":"rust","code":"let res35: String = r\n            .json_get(\"bikes:inventory\", \"$..mountain_bikes[0:2].model\")\n            .await\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{res35}\"); // >>> [[\"Phoebe\",\"Quaoar\"]]","section_id":"access-examples"},{"id":"access-examples-ex47","language":"rust","code":"let res35: String = r\n            .json_get(\"bikes:inventory\", \"$..mountain_bikes[0:2].model\")\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{res35}\"); // >>> [[\"Phoebe\",\"Quaoar\"]]","section_id":"access-examples"},{"id":"filter-examples-ex0","language":"plaintext","code":"> JSON.GET bikes:inventory '$..mountain_bikes[?(@.price < 3000 && @.specs.weight < 10)]'\n\"[{\\\"id\\\":\\\"bike:2\\\",\\\"model\\\":\\\"Quaoar\\\",\\\"description\\\":\\\"Redesigned for the 2020 model year, this bike impressed our testers and is the best all-around trail bike we've ever tested. The Shimano gear system effectively does away with an external cassette, so is super low maintenance in terms of wear and tear. All in all it's an impressive package for the price, making it very competitive.\\\",\\\"price\\\":2072,\\\"specs\\\":{\\\"material\\\":\\\"aluminium\\\",\\\"weight\\\":7.9},\\\"colors\\\":[\\\"black\\\",\\\"white\\\"]}]\"","section_id":"filter-examples"},{"id":"filter-examples-ex1","language":"csharp","code":"RedisResult res35 = db.JSON().Get(\n            \"bikes:inventory\",\n            path: \"$..mountain_bikes[?(@.price < 3000 && @.specs.weight < 10)]\"\n        );\n        Console.WriteLine(res35);\n        // >>> [{\"id\":\"bike:2\",\"model\":\"Quaoar\",\"description\":\"Redesigned for the 2020 model year...","section_id":"filter-examples"},{"id":"filter-examples-ex2","language":"go","code":"res8, err := rdb.JSONGetWithArgs(ctx, \"bikes:inventory\",\n\t\t&redis.JSONGetArgs{Indent: \"  \", Newline: \"\\n\", Space: \" \"},\n\t\t\"$..mountain_bikes[?(@.price < 3000 && @.specs.weight < 10)]\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res8)\n\t// >>>\n\t// [\n\t//   {\n\t//     \"colors\": [\n\t//       \"black\",\n\t//       \"white\"\n\t//     ],\n\t//     \"description\": \"Redesigned for the 2020 model year\n\t// ...","section_id":"filter-examples"},{"id":"filter-examples-ex3","language":"java","code":"CompletableFuture<Void> filter1 = asyncCommands\n                    .jsonGet(\"bikes:inventory\", JsonPath.of(\"$..mountain_bikes[?(@.price < 3000 && @.specs.weight < 10)]\"))\n                    .thenAccept(System.out::println)\n                    // >>> [[{\"id\":\"bike:2\",\"model\":\"Quaoar\",\"description\":...\n                    .toCompletableFuture();","section_id":"filter-examples"},{"id":"filter-examples-ex4","language":"java","code":"Mono<Void> filter1 = reactiveCommands\n                    .jsonGet(\"bikes:inventory\", JsonPath.of(\"$..mountain_bikes[?(@.price < 3000 && @.specs.weight < 10)]\"))\n                    .collectList()\n                    .doOnNext(System.out::println) // >>> [[{\"id\":\"bike:2\",\"model\":\"Quaoar\",\"description\":...\n                    .then();","section_id":"filter-examples"},{"id":"filter-examples-ex5","language":"java","code":"Object res35 = jedis.jsonGet(\n            \"bikes:inventory\",\n            new Path2(\"$..mountain_bikes[?(@.price < 3000 && @.specs.weight < 10)]\")\n        );\n        System.out.println(res35);\n        // >>> [{\"specs\":{\"material\":\"aluminium\",\"weight\":7.9},\"price\":2072,...","section_id":"filter-examples"},{"id":"filter-examples-ex6","language":"javascript","code":"const res35 = await client.json.get(\"bikes:inventory\", {\n  path: \"$..mountain_bikes[?(@.price < 3000 && @.specs.weight < 10)]\"\n});\nconsole.log(res35);\n/*\n[\n  {\n    id: 'bike:2',\n    model: 'Quaoar',\n    description: \"Redesigned for the 2020 model year, this bike impressed our testers and is the best all-around trail bike we've ever tested. The Shimano gear system effectively does away with an external cassette, so is super low maintenance in terms of wear and teaawait client. All in all it's an impressive package for the price, making it very competitive.\",\n    price: 2072,\n    specs: { material: 'aluminium', weight: 7.9 },\n    colors: [ 'black', 'white' ]\n  }\n]\n*/","section_id":"filter-examples"},{"id":"filter-examples-ex7","language":"php","code":"$res8b = $r->jsonget(\n            'bikes:inventory',\n            '',\n            '',\n            '',\n            '$..mountain_bikes[?(@.price < 3000 && @.specs.weight < 10)]'\n        );\n        echo $res8b . PHP_EOL;\n        // >>> [{\"id\":\"bike:2\",\"model\":\"Quaoar\",...}]","section_id":"filter-examples"},{"id":"filter-examples-ex8","language":"python","code":"res8 = r.json().get(\n    \"bikes:inventory\",\n    \"$..mountain_bikes[?(@.price < 3000 && @.specs.weight < 10)]\",\n)\nprint(res8)\n# >>> [{'id': 'bike:2', 'model': 'Quaoar',\n#           'description': \"Redesigned for the 2020 model year...","section_id":"filter-examples"},{"id":"filter-examples-ex9","language":"ruby","code":"res8 = r.json_get(\n  'bikes:inventory',\n  '$..mountain_bikes[?(@.price < 3000 && @.specs.weight < 10)]'\n)\np res8\n# >>> [{\"id\"=>\"bike:2\", \"model\"=>\"Quaoar\",\n# >>>   \"description\"=>\"Redesigned for the 2020 model year...","section_id":"filter-examples"},{"id":"filter-examples-ex10","language":"rust","code":"let res36: String = r\n            .json_get(\n                \"bikes:inventory\",\n                \"$..mountain_bikes[?(@.price < 3000 && @.specs.weight < 10)]\",\n            )\n            .await\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{res36}\");\n        // >>> [[{\"id\":\"bike:2\",\"model\":\"Quaoar\",\"description\":\"Redesigned for the 2020 model year...","section_id":"filter-examples"},{"id":"filter-examples-ex11","language":"rust","code":"let res36: String = r\n            .json_get(\n                \"bikes:inventory\",\n                \"$..mountain_bikes[?(@.price < 3000 && @.specs.weight < 10)]\",\n            )\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{res36}\");\n        // >>> [[{\"id\":\"bike:2\",\"model\":\"Quaoar\",\"description\":\"Redesigned for the 2020 model year...","section_id":"filter-examples"},{"id":"filter-examples-ex12","language":"plaintext","code":"> JSON.GET bikes:inventory '$..[?(@.specs.material == \"alloy\")].model'\n\"[\\\"Weywot\\\",\\\"Mimas\\\"]\"","section_id":"filter-examples"},{"id":"filter-examples-ex13","language":"csharp","code":"RedisResult res36 = db.JSON().Get(\n            \"bikes:inventory\",\n            path: \"$..[?(@.specs.material == 'alloy')].model\"\n        );\n        Console.WriteLine(res36);   // >>> [\"Weywot\",\"Mimas\"]","section_id":"filter-examples"},{"id":"filter-examples-ex14","language":"go","code":"res9, err := rdb.JSONGet(ctx,\n\t\t\"bikes:inventory\",\n\t\t\"$..[?(@.specs.material == 'alloy')].model\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res9) // >>> [\"Mimas\",\"Weywot\"]","section_id":"filter-examples"},{"id":"filter-examples-ex15","language":"java","code":"CompletableFuture<Void> filter2 = asyncCommands\n                    .jsonGet(\"bikes:inventory\", JsonPath.of(\"$..[?(@.specs.material == 'alloy')].model\"))\n                    .thenAccept(System.out::println)\n                    // >>> [[\"Weywot\",\"Mimas\"]]\n                    .toCompletableFuture();","section_id":"filter-examples"},{"id":"filter-examples-ex16","language":"java","code":"Mono<Void> filter2 = reactiveCommands\n                    .jsonGet(\"bikes:inventory\", JsonPath.of(\"$..[?(@.specs.material == 'alloy')].model\")).collectList()\n                    .doOnNext(System.out::println) // >>> [[\"Weywot\",\"Mimas\"]]\n                    .then();","section_id":"filter-examples"},{"id":"filter-examples-ex17","language":"java","code":"Object res36 = jedis.jsonGet(\n            \"bikes:inventory\", new Path2(\"$..[?(@.specs.material == 'alloy')].model\")\n        );\n        System.out.println(res36);  // >>> [\"Weywot\",\"Mimas\"]","section_id":"filter-examples"},{"id":"filter-examples-ex18","language":"javascript","code":"//  names of bikes made from an alloy\nconst res36 = await client.json.get(\"bikes:inventory\", {\n  path: \"$..[?(@.specs.material == 'alloy')].model\"\n});\nconsole.log(res36); //  ['Weywot', 'Mimas']","section_id":"filter-examples"},{"id":"filter-examples-ex19","language":"php","code":"$res9b = $r->jsonget('bikes:inventory', '', '', '', \"$..[?(@.specs.material == 'alloy')].model\");\n        echo $res9b . PHP_EOL;\n        // >>> [\"Weywot\",\"Mimas\"]","section_id":"filter-examples"},{"id":"filter-examples-ex20","language":"python","code":"res9 = r.json().get(\"bikes:inventory\", \"$..[?(@.specs.material == 'alloy')].model\")\nprint(res9)  # >>> ['Weywot', 'Mimas']","section_id":"filter-examples"},{"id":"filter-examples-ex21","language":"ruby","code":"res9 = r.json_get('bikes:inventory', \"$..[?(@.specs.material == 'alloy')].model\")\np res9 # >>> [\"Weywot\", \"Mimas\"]","section_id":"filter-examples"},{"id":"filter-examples-ex22","language":"rust","code":"let res37: String = r\n            .json_get(\n                \"bikes:inventory\",\n                \"$..[?(@.specs.material == 'alloy')].model\",\n            )\n            .await\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{res37}\"); // >>> [[\"Weywot\",\"Mimas\"]]","section_id":"filter-examples"},{"id":"filter-examples-ex23","language":"rust","code":"let res37: String = r\n            .json_get(\n                \"bikes:inventory\",\n                \"$..[?(@.specs.material == 'alloy')].model\",\n            )\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{res37}\"); // >>> [[\"Weywot\",\"Mimas\"]]","section_id":"filter-examples"},{"id":"filter-examples-ex24","language":"plaintext","code":"> JSON.GET bikes:inventory '$..[?(@.specs.material =~ \"(?i)al\")].model'\n\"[\\\"Quaoar\\\",\\\"Weywot\\\",\\\"Salacia\\\",\\\"Mimas\\\"]\"","section_id":"filter-examples"},{"id":"filter-examples-ex25","language":"csharp","code":"RedisResult res37 = db.JSON().Get(\n            \"bikes:inventory\",\n            path: \"$..[?(@.specs.material =~ '(?i)al')].model\"\n        );\n        Console.WriteLine(res37);   // >>> [\"Quaoar\",\"Weywot\",\"Salacia\",\"Mimas\"]","section_id":"filter-examples"},{"id":"filter-examples-ex26","language":"go","code":"res10, err := rdb.JSONGet(ctx,\n\t\t\"bikes:inventory\",\n\t\t\"$..[?(@.specs.material =~ '(?i)al')].model\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res10) // >>> [\"Salacia\",\"Mimas\",\"Quaoar\",\"Weywot\"]","section_id":"filter-examples"},{"id":"filter-examples-ex27","language":"java","code":"CompletableFuture<Void> filter3 = asyncCommands\n                    .jsonGet(\"bikes:inventory\", JsonPath.of(\"$..[?(@.specs.material =~ '(?i)al')].model\"))\n                    .thenAccept(System.out::println)\n                    // >>> [[\"Quaoar\",\"Weywot\",\"Salacia\",\"Mimas\"]]\n                    .toCompletableFuture();","section_id":"filter-examples"},{"id":"filter-examples-ex28","language":"java","code":"Mono<Void> filter3 = reactiveCommands\n                    .jsonGet(\"bikes:inventory\", JsonPath.of(\"$..[?(@.specs.material =~ '(?i)al')].model\")).collectList()\n                    .doOnNext(System.out::println) // >>> [[\"Quaoar\",\"Weywot\",\"Salacia\",\"Mimas\"]]\n                    .then();","section_id":"filter-examples"},{"id":"filter-examples-ex29","language":"java","code":"Object res37 = jedis.jsonGet(\n            \"bikes:inventory\", new Path2(\"$..[?(@.specs.material =~ '(?i)al')].model\")\n        );\n        System.out.println(res37);\n        // >>> [\"Quaoar\",\"Weywot\",\"Salacia\",\"Mimas\"]","section_id":"filter-examples"},{"id":"filter-examples-ex30","language":"javascript","code":"const res37 = await client.json.get(\"bikes:inventory\", {\n  path: \"$..[?(@.specs.material =~ '(?i)al')].model\"\n});\nconsole.log(res37); //  ['Quaoar', 'Weywot', 'Salacia', 'Mimas']","section_id":"filter-examples"},{"id":"filter-examples-ex31","language":"php","code":"$res10b = $r->jsonget('bikes:inventory', '', '', '', \"$..[?(@.specs.material =~ '(?i)al')].model\");\n        echo $res10b . PHP_EOL;\n        // >>> [\"Quaoar\",\"Weywot\",\"Salacia\",\"Mimas\"]","section_id":"filter-examples"},{"id":"filter-examples-ex32","language":"python","code":"res10 = r.json().get(\"bikes:inventory\", \"$..[?(@.specs.material =~ '(?i)al')].model\")\nprint(res10)  # >>> ['Quaoar', 'Weywot', 'Salacia', 'Mimas']","section_id":"filter-examples"},{"id":"filter-examples-ex33","language":"ruby","code":"res10 = r.json_get('bikes:inventory', \"$..[?(@.specs.material =~ '(?i)al')].model\")\np res10 # >>> [\"Quaoar\", \"Weywot\", \"Salacia\", \"Mimas\"]","section_id":"filter-examples"},{"id":"filter-examples-ex34","language":"rust","code":"let res38: String = r\n            .json_get(\n                \"bikes:inventory\",\n                \"$..[?(@.specs.material =~ '(?i)al')].model\",\n            )\n            .await\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{res38}\"); // >>> [[\"Quaoar\",\"Weywot\",\"Salacia\",\"Mimas\"]]","section_id":"filter-examples"},{"id":"filter-examples-ex35","language":"rust","code":"let res38: String = r\n            .json_get(\n                \"bikes:inventory\",\n                \"$..[?(@.specs.material =~ '(?i)al')].model\",\n            )\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{res38}\"); // >>> [[\"Quaoar\",\"Weywot\",\"Salacia\",\"Mimas\"]]","section_id":"filter-examples"},{"id":"filter-examples-ex36","language":"plaintext","code":"> JSON.SET bikes:inventory $.inventory.mountain_bikes[0].regex_pat '\"(?i)al\"'\nOK\n> JSON.SET bikes:inventory $.inventory.mountain_bikes[1].regex_pat '\"(?i)al\"'\nOK\n> JSON.SET bikes:inventory $.inventory.mountain_bikes[2].regex_pat '\"(?i)al\"'\nOK\n> JSON.GET bikes:inventory '$.inventory.mountain_bikes[?(@.specs.material =~ @.regex_pat)].model'\n\"[\\\"Quaoar\\\",\\\"Weywot\\\"]\"","section_id":"filter-examples"},{"id":"filter-examples-ex37","language":"csharp","code":"bool res38 = db.JSON().Set(\n            \"bikes:inventory\",\n            \"$.inventory.mountain_bikes[0].regex_pat\",\n            \"\\\"(?i)al\\\"\"\n        );\n        Console.WriteLine(res38);   // >>> True\n\n        bool res39 = db.JSON().Set(\n            \"bikes:inventory\",\n            \"$.inventory.mountain_bikes[1].regex_pat\",\n            \"\\\"(?i)al\\\"\"\n        );\n        Console.WriteLine(res39);   // >>> True\n\n        bool res40 = db.JSON().Set(\n            \"bikes:inventory\",\n            \"$.inventory.mountain_bikes[2].regex_pat\",\n            \"\\\"(?i)al\\\"\"\n        );\n        Console.WriteLine(res40);   // >>> True\n\n        RedisResult res41 = db.JSON().Get(\n            \"bikes:inventory\",\n            path: \"$.inventory.mountain_bikes[?(@.specs.material =~ @.regex_pat)].model\"\n        );\n        Console.WriteLine(res41);   // >>> [\"Quaoar\",\"Weywot\"]","section_id":"filter-examples"},{"id":"filter-examples-ex38","language":"go","code":"res11, err := rdb.JSONSet(ctx,\n\t\t\"bikes:inventory\",\n\t\t\"$.inventory.mountain_bikes[0].regex_pat\",\n\t\t\"\\\"(?i)al\\\"\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res11) // >>> OK\n\n\tres12, err := rdb.JSONSet(ctx,\n\t\t\"bikes:inventory\",\n\t\t\"$.inventory.mountain_bikes[1].regex_pat\",\n\t\t\"\\\"(?i)al\\\"\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res12) // >>> OK\n\n\tres13, err := rdb.JSONSet(ctx,\n\t\t\"bikes:inventory\",\n\t\t\"$.inventory.mountain_bikes[2].regex_pat\",\n\t\t\"\\\"(?i)al\\\"\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res13) // >>> OK\n\n\tres14, err := rdb.JSONGet(ctx,\n\t\t\"bikes:inventory\",\n\t\t\"$.inventory.mountain_bikes[?(@.specs.material =~ @.regex_pat)].model\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res14) // >>> [\"Quaoar\",\"Weywot\"]","section_id":"filter-examples"},{"id":"filter-examples-ex39","language":"java","code":"CompletableFuture<Void> filter4 = asyncCommands.jsonSet(\"bikes:inventory\",\n                    JsonPath.of(\"$.inventory.mountain_bikes[0].regex_pat\"), parser.createJsonValue(\"\\\"(?i)al\\\"\"))\n                    .thenCompose(r -> {\n                        System.out.println(r); // >>> OK\n\n                        return asyncCommands.jsonSet(\"bikes:inventory\", JsonPath.of(\"$.inventory.mountain_bikes[1].regex_pat\"),\n                                parser.createJsonValue(\"\\\"(?i)al\\\"\"));\n                    }).thenCompose(r -> {\n                        System.out.println(r); // >>> OK\n\n                        return asyncCommands.jsonSet(\"bikes:inventory\", JsonPath.of(\"$.inventory.mountain_bikes[2].regex_pat\"),\n                                parser.createJsonValue(\"\\\"(?i)al\\\"\"));\n                    }).thenCompose(res22 -> {\n                        System.out.println(res22); // >>> OK\n\n                        return asyncCommands.jsonGet(\"bikes:inventory\",\n                                JsonPath.of(\"$.inventory.mountain_bikes[?(@.specs.material =~ @.regex_pat)].model\"));\n                    })\n                    .thenAccept(System.out::println)\n                    // >>> [[\"Quaoar\",\"Weywot\"]]\n                    .toCompletableFuture();","section_id":"filter-examples"},{"id":"filter-examples-ex40","language":"java","code":"Mono<Void> filter4 = reactiveCommands.jsonSet(\"bikes:inventory\",\n                    JsonPath.of(\"$.inventory.mountain_bikes[0].regex_pat\"), parser.createJsonValue(\"\\\"(?i)al\\\"\"))\n                    .doOnNext(r -> {\n                        System.out.println(r); // >>> OK\n                    })\n                    .flatMap(r -> reactiveCommands.jsonSet(\"bikes:inventory\",\n                            JsonPath.of(\"$.inventory.mountain_bikes[1].regex_pat\"), parser.createJsonValue(\"\\\"(?i)al\\\"\")))\n                    .doOnNext(r -> {\n                        System.out.println(r); // >>> OK\n                    })\n                    .flatMap(r -> reactiveCommands.jsonSet(\"bikes:inventory\",\n                            JsonPath.of(\"$.inventory.mountain_bikes[2].regex_pat\"), parser.createJsonValue(\"\\\"(?i)al\\\"\")))\n                    .doOnNext(res22 -> {\n                        System.out.println(res22); // >>> OK\n                    })\n                    .flatMap(res22 -> reactiveCommands\n                            .jsonGet(\"bikes:inventory\",\n                                    JsonPath.of(\"$.inventory.mountain_bikes[?(@.specs.material =~ @.regex_pat)].model\"))\n                            .collectList())\n                    .doOnNext(System.out::println) // >>> [[\"Quaoar\",\"Weywot\"]]\n                    .then();","section_id":"filter-examples"},{"id":"filter-examples-ex41","language":"java","code":"jedis.jsonSet(\n            \"bikes:inventory\", new Path2(\"$.inventory.mountain_bikes[0].regex_pat\"),\n            \"\\\"(?i)al\\\"\"\n        );\n        jedis.jsonSet(\n            \"bikes:inventory\", new Path2(\"$.inventory.mountain_bikes[1].regex_pat\"),\n            \"\\\"(?i)al\\\"\"\n        );\n        jedis.jsonSet(\n            \"bikes:inventory\", new Path2(\"$.inventory.mountain_bikes[2].regex_pat\"),\n            \"\\\"(?i)al\\\"\"\n        );\n        \n        Object res38 = jedis.jsonGet(\n            \"bikes:inventory\",\n            new Path2(\"$.inventory.mountain_bikes[?(@.specs.material =~ @.regex_pat)].model\")\n        );\n        System.out.println(res38);  // >>> [\"Quaoar\",\"Weywot\"]","section_id":"filter-examples"},{"id":"filter-examples-ex42","language":"javascript","code":"const res37a = await client.json.set(\n  'bikes:inventory', \n  '$.inventory.mountain_bikes[0].regex_pat', \n  '(?i)al'\n);\n\nconst res37b = await client.json.set(\n  'bikes:inventory', \n  '$.inventory.mountain_bikes[1].regex_pat', \n  '(?i)al'\n);\n\nconst res37c = await client.json.set(\n  'bikes:inventory', \n  '$.inventory.mountain_bikes[2].regex_pat', \n  '(?i)al'\n);\n\nconst res37d = await client.json.get(\n  'bikes:inventory',\n  { path: '$.inventory.mountain_bikes[?(@.specs.material =~ @.regex_pat)].model' }\n);\nconsole.log(res37d); // ['Quaoar', 'Weywot']","section_id":"filter-examples"},{"id":"filter-examples-ex43","language":"php","code":"$r->jsonset('bikes:inventory', '$.inventory.mountain_bikes[0].regex_pat', '\"(?i)al\"');\n        $r->jsonset('bikes:inventory', '$.inventory.mountain_bikes[1].regex_pat', '\"(?i)al\"');\n        $r->jsonset('bikes:inventory', '$.inventory.mountain_bikes[2].regex_pat', '\"(?i)al\"');\n\n        $res14b = $r->jsonget(\n            'bikes:inventory',\n            '',\n            '',\n            '',\n            '$.inventory.mountain_bikes[?(@.specs.material =~ @.regex_pat)].model'\n        );\n        echo $res14b . PHP_EOL;\n        // >>> [\"Quaoar\",\"Weywot\"]","section_id":"filter-examples"},{"id":"filter-examples-ex44","language":"python","code":"res11 = r.json().set(\n    \"bikes:inventory\", \"$.inventory.mountain_bikes[0].regex_pat\", \"(?i)al\"\n)\nres12 = r.json().set(\n    \"bikes:inventory\", \"$.inventory.mountain_bikes[1].regex_pat\", \"(?i)al\"\n)\nres13 = r.json().set(\n    \"bikes:inventory\", \"$.inventory.mountain_bikes[2].regex_pat\", \"(?i)al\"\n)\n\nres14 = r.json().get(\n    \"bikes:inventory\",\n    \"$.inventory.mountain_bikes[?(@.specs.material =~ @.regex_pat)].model\",\n)\nprint(res14)  # >>> ['Quaoar', 'Weywot']","section_id":"filter-examples"},{"id":"filter-examples-ex45","language":"ruby","code":"res11 = r.json_set(\n  'bikes:inventory', '$.inventory.mountain_bikes[0].regex_pat', '(?i)al'\n)\nres12 = r.json_set(\n  'bikes:inventory', '$.inventory.mountain_bikes[1].regex_pat', '(?i)al'\n)\nres13 = r.json_set(\n  'bikes:inventory', '$.inventory.mountain_bikes[2].regex_pat', '(?i)al'\n)\n\nres14 = r.json_get(\n  'bikes:inventory',\n  '$.inventory.mountain_bikes[?(@.specs.material =~ @.regex_pat)].model'\n)\np res14 # >>> [\"Quaoar\", \"Weywot\"]","section_id":"filter-examples"},{"id":"filter-examples-ex46","language":"rust","code":"let _: bool = r\n            .json_set(\n                \"bikes:inventory\",\n                \"$.inventory.mountain_bikes[0].regex_pat\",\n                &json!(\"(?i)al\"),\n            )\n            .await\n            .expect(\"Failed to run JSON.SET\");\n        let _: bool = r\n            .json_set(\n                \"bikes:inventory\",\n                \"$.inventory.mountain_bikes[1].regex_pat\",\n                &json!(\"(?i)al\"),\n            )\n            .await\n            .expect(\"Failed to run JSON.SET\");\n        let _: bool = r\n            .json_set(\n                \"bikes:inventory\",\n                \"$.inventory.mountain_bikes[2].regex_pat\",\n                &json!(\"(?i)al\"),\n            )\n            .await\n            .expect(\"Failed to run JSON.SET\");\n\n        let res39: String = r\n            .json_get(\n                \"bikes:inventory\",\n                \"$.inventory.mountain_bikes[?(@.specs.material =~ @.regex_pat)].model\",\n            )\n            .await\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{res39}\"); // >>> [[\"Quaoar\",\"Weywot\"]]","section_id":"filter-examples"},{"id":"filter-examples-ex47","language":"rust","code":"let _: bool = r\n            .json_set(\n                \"bikes:inventory\",\n                \"$.inventory.mountain_bikes[0].regex_pat\",\n                &json!(\"(?i)al\"),\n            )\n            .expect(\"Failed to run JSON.SET\");\n        let _: bool = r\n            .json_set(\n                \"bikes:inventory\",\n                \"$.inventory.mountain_bikes[1].regex_pat\",\n                &json!(\"(?i)al\"),\n            )\n            .expect(\"Failed to run JSON.SET\");\n        let _: bool = r\n            .json_set(\n                \"bikes:inventory\",\n                \"$.inventory.mountain_bikes[2].regex_pat\",\n                &json!(\"(?i)al\"),\n            )\n            .expect(\"Failed to run JSON.SET\");\n\n        let res39: String = r\n            .json_get(\n                \"bikes:inventory\",\n                \"$.inventory.mountain_bikes[?(@.specs.material =~ @.regex_pat)].model\",\n            )\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{res39}\"); // >>> [[\"Quaoar\",\"Weywot\"]]","section_id":"filter-examples"},{"id":"filter-examples-ex48","language":"plaintext","code":"> JSON.SET doc $ '[{\"a\":1,\"b\":1},{\"b\":2},{\"a\":1},{\"c\":3}]'\nOK\n> JSON.GET doc '$[?!@.a]'\n\"[{\\\"b\\\":2},{\\\"c\\\":3}]\"\n> JSON.GET doc '$[?!(@.a==1)]'\n\"[{\\\"b\\\":2},{\\\"c\\\":3}]\"\n> JSON.GET doc '$[?!@.a && @.b]'\n\"[{\\\"b\\\":2}]\"","section_id":"filter-examples"},{"id":"filter-examples-ex49","language":"csharp","code":"bool res1 = db.JSON().Set(\"doc\", \"$\", \"[{\\\"a\\\":1,\\\"b\\\":1},{\\\"b\\\":2},{\\\"a\\\":1},{\\\"c\\\":3}]\");\n        Console.WriteLine(res1);    // >>> True\n\n        RedisResult res2 = db.JSON().Get(\"doc\", path: \"$[?!@.a]\");\n        Console.WriteLine(res2);    // >>> [{\"b\":2},{\"c\":3}]\n\n        RedisResult res3 = db.JSON().Get(\"doc\", path: \"$[?!(@.a==1)]\");\n        Console.WriteLine(res3);    // >>> [{\"b\":2},{\"c\":3}]\n\n        RedisResult res4 = db.JSON().Get(\"doc\", path: \"$[?!@.a && @.b]\");\n        Console.WriteLine(res4);    // >>> [{\"b\":2}]","section_id":"filter-examples"},{"id":"filter-examples-ex50","language":"go","code":"res1, err := rdb.JSONSet(ctx, \"doc\", \"$\",\n\t\t`[{\"a\":1,\"b\":1},{\"b\":2},{\"a\":1},{\"c\":3}]`,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> OK\n\n\tres2, err := rdb.JSONGet(ctx, \"doc\", `$[?!@.a]`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> [{\"b\":2},{\"c\":3}]\n\n\tres3, err := rdb.JSONGet(ctx, \"doc\", `$[?!(@.a==1)]`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3) // >>> [{\"b\":2},{\"c\":3}]\n\n\tres4, err := rdb.JSONGet(ctx, \"doc\", `$[?!@.a && @.b]`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4) // >>> [{\"b\":2}]","section_id":"filter-examples"},{"id":"filter-examples-ex51","language":"java","code":"CompletableFuture<Void> filterNegationExample = asyncCommands\n                    .jsonSet(\"doc\", JsonPath.ROOT_PATH,\n                            parser.createJsonValue(\"[{\\\"a\\\":1,\\\"b\\\":1},{\\\"b\\\":2},{\\\"a\\\":1},{\\\"c\\\":3}]\"))\n                    .thenCompose(res1 -> {\n                        System.out.println(res1); // >>> OK\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$[?!@.a]\"));\n                    }).thenCompose(res2 -> {\n                        System.out.println(res2); // >>> [[{\"b\":2},{\"c\":3}]]\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$[?!(@.a==1)]\"));\n                    }).thenCompose(res3 -> {\n                        System.out.println(res3); // >>> [[{\"b\":2},{\"c\":3}]]\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$[?!@.a && @.b]\"));\n                    }).thenAccept(res4 -> {\n                        System.out.println(res4); // >>> [[{\"b\":2}]]\n                    }).toCompletableFuture();","section_id":"filter-examples"},{"id":"filter-examples-ex52","language":"java","code":"Mono<Void> filterNegationExample = reactiveCommands\n                    .jsonSet(\"doc\", JsonPath.ROOT_PATH,\n                            parser.createJsonValue(\"[{\\\"a\\\":1,\\\"b\\\":1},{\\\"b\\\":2},{\\\"a\\\":1},{\\\"c\\\":3}]\"))\n                    .doOnNext(res1 -> {\n                        System.out.println(res1); // >>> OK\n                    })\n                    .flatMap(res1 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$[?!@.a]\")).collectList())\n                    .doOnNext(res2 -> {\n                        System.out.println(res2); // >>> [[{\"b\":2},{\"c\":3}]]\n                    })\n                    .flatMap(res2 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$[?!(@.a==1)]\")).collectList())\n                    .doOnNext(res3 -> {\n                        System.out.println(res3); // >>> [[{\"b\":2},{\"c\":3}]]\n                    })\n                    .flatMap(res3 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$[?!@.a && @.b]\")).collectList())\n                    .doOnNext(res4 -> {\n                        System.out.println(res4); // >>> [[{\"b\":2}]]\n                    })\n                    .then();","section_id":"filter-examples"},{"id":"filter-examples-ex53","language":"java","code":"String res1 = jedis.jsonSet(\"doc\", new Path2(\"$\"), \"[{\\\"a\\\":1,\\\"b\\\":1},{\\\"b\\\":2},{\\\"a\\\":1},{\\\"c\\\":3}]\");\n        System.out.println(res1);    // >>> OK\n\n        Object res2 = jedis.jsonGet(\"doc\", new Path2(\"$[?!@.a]\"));\n        System.out.println(res2);    // >>> [{\"b\":2},{\"c\":3}]\n\n        Object res3 = jedis.jsonGet(\"doc\", new Path2(\"$[?!(@.a==1)]\"));\n        System.out.println(res3);    // >>> [{\"b\":2},{\"c\":3}]\n\n        Object res4 = jedis.jsonGet(\"doc\", new Path2(\"$[?!@.a && @.b]\"));\n        System.out.println(res4);    // >>> [{\"b\":2}]","section_id":"filter-examples"},{"id":"filter-examples-ex54","language":"javascript","code":"const res1 = await client.json.set('doc', '$', [{ a: 1, b: 1 }, { b: 2 }, { a: 1 }, { c: 3 }]);\nconsole.log(res1); // >>> OK\n\nconst res2 = await client.json.get('doc', { path: '$[?!@.a]' });\nconsole.log(res2); // >>> [ { b: 2 }, { c: 3 } ]\n\nconst res3 = await client.json.get('doc', { path: '$[?!(@.a==1)]' });\nconsole.log(res3); // >>> [ { b: 2 }, { c: 3 } ]\n\nconst res4 = await client.json.get('doc', { path: '$[?!@.a && @.b]' });\nconsole.log(res4); // >>> [ { b: 2 } ]","section_id":"filter-examples"},{"id":"filter-examples-ex55","language":"php","code":"$res1 = $this->redis->jsonset('doc', '$', json_encode(\n            [[\"a\" => 1, \"b\" => 1], [\"b\" => 2], [\"a\" => 1], [\"c\" => 3]]\n        ));\n        echo $res1 . PHP_EOL; // >>> OK\n\n        $res2 = $this->redis->jsonget('doc', '', '', '', '$[?!@.a]');\n        echo $res2 . PHP_EOL; // >>> [{\"b\":2},{\"c\":3}]\n\n        $res3 = $this->redis->jsonget('doc', '', '', '', '$[?!(@.a==1)]');\n        echo $res3 . PHP_EOL; // >>> [{\"b\":2},{\"c\":3}]\n\n        $res4 = $this->redis->jsonget('doc', '', '', '', '$[?!@.a && @.b]');\n        echo $res4 . PHP_EOL; // >>> [{\"b\":2}]","section_id":"filter-examples"},{"id":"filter-examples-ex56","language":"python","code":"res1 = r.json().set(\"doc\", \"$\", [{\"a\": 1, \"b\": 1}, {\"b\": 2}, {\"a\": 1}, {\"c\": 3}])\nprint(res1)\n# >>> True\n\nres2 = r.json().get(\"doc\", \"$[?!@.a]\")\nprint(res2)\n# >>> [{'b': 2}, {'c': 3}]\n\nres3 = r.json().get(\"doc\", \"$[?!(@.a==1)]\")\nprint(res3)\n# >>> [{'b': 2}, {'c': 3}]\n\nres4 = r.json().get(\"doc\", \"$[?!@.a && @.b]\")\nprint(res4)\n# >>> [{'b': 2}]","section_id":"filter-examples"},{"id":"filter-examples-ex57","language":"ruby","code":"res1 = r.json_set('doc', '$', [{ 'a' => 1, 'b' => 1 }, { 'b' => 2 }, { 'a' => 1 }, { 'c' => 3 }])\nputs res1 # >>> OK\n\nres2 = r.json_get('doc', '$[?!@.a]')\np res2 # >>> [{\"b\"=>2}, {\"c\"=>3}]\n\nres3 = r.json_get('doc', '$[?!(@.a==1)]')\np res3 # >>> [{\"b\"=>2}, {\"c\"=>3}]\n\nres4 = r.json_get('doc', '$[?!@.a && @.b]')\np res4 # >>> [{\"b\"=>2}]","section_id":"filter-examples"},{"id":"filter-examples-ex58","language":"rust","code":"let _: bool = match r.json_set(\"doc\", \"$\", &json!([{\"a\":1,\"b\":1},{\"b\":2},{\"a\":1},{\"c\":3}])).await {\n            Ok(v) => v,\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        };\n\n        match r.json_get(\"doc\", \"$[?!@.a]\").await {\n            Ok(res1) => {\n                let res1: String = res1;\n                println!(\"{res1}\");    // >>> [{\"b\":2},{\"c\":3}]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$[?!(@.a==1)]\").await {\n            Ok(res2) => {\n                let res2: String = res2;\n                println!(\"{res2}\");    // >>> [{\"b\":2},{\"c\":3}]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$[?!@.a && @.b]\").await {\n            Ok(res3) => {\n                let res3: String = res3;\n                println!(\"{res3}\");    // >>> [{\"b\":2}]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }","section_id":"filter-examples"},{"id":"filter-examples-ex59","language":"rust","code":"match r.json_set(\"doc\", \"$\", &json!([{\"a\":1,\"b\":1},{\"b\":2},{\"a\":1},{\"c\":3}])) {\n            Ok(res1) => {\n                let res1: bool = res1;\n                println!(\"{}\", if res1 { \"OK\" } else { \"(nil)\" });    // >>> OK\n            },\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$[?!@.a]\") {\n            Ok(res2) => {\n                let res2: String = res2;\n                println!(\"{res2}\");    // >>> [{\"b\":2},{\"c\":3}]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$[?!(@.a==1)]\") {\n            Ok(res3) => {\n                let res3: String = res3;\n                println!(\"{res3}\");    // >>> [{\"b\":2},{\"c\":3}]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$[?!@.a && @.b]\") {\n            Ok(res4) => {\n                let res4: String = res4;\n                println!(\"{res4}\");    // >>> [{\"b\":2}]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }","section_id":"filter-examples"},{"id":"filter-examples-ex60","language":"plaintext","code":"> JSON.SET doc $ '{\"arrs\":[[1],[2],[1,2],[1,[2]]],\"objs\":[{\"x\":1},{\"x\":2},{\"y\":1}]}'\nOK\n> JSON.GET doc '$.arrs[?(@ == [1])]'\n\"[[1]]\"\n> JSON.GET doc '$.arrs[?(@ == [1,[2]])]'\n\"[[1,[2]]]\"\n> JSON.GET doc '$.objs[?(@ == {\"x\":1})]'\n\"[{\\\"x\\\":1}]\"","section_id":"filter-examples"},{"id":"filter-examples-ex61","language":"csharp","code":"bool res5 = db.JSON().Set(\n            \"doc\",\n            \"$\",\n            \"{\\\"arrs\\\":[[1],[2],[1,2],[1,[2]]],\\\"objs\\\":[{\\\"x\\\":1},{\\\"x\\\":2},{\\\"y\\\":1}]}\"\n        );\n        Console.WriteLine(res5);    // >>> True\n\n        RedisResult res6 = db.JSON().Get(\"doc\", path: \"$.arrs[?(@ == [1])]\");\n        Console.WriteLine(res6);    // >>> [[1]]\n\n        RedisResult res7 = db.JSON().Get(\"doc\", path: \"$.arrs[?(@ == [1,[2]])]\");\n        Console.WriteLine(res7);    // >>> [[1,[2]]]\n\n        RedisResult res8 = db.JSON().Get(\"doc\", path: \"$.objs[?(@ == {\\\"x\\\":1})]\");\n        Console.WriteLine(res8);    // >>> [{\"x\":1}]","section_id":"filter-examples"},{"id":"filter-examples-ex62","language":"go","code":"res1, err := rdb.JSONSet(ctx, \"doc\", \"$\",\n\t\t`{\"arrs\":[[1],[2],[1,2],[1,[2]]],\"objs\":[{\"x\":1},{\"x\":2},{\"y\":1}]}`,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> OK\n\n\tres2, err := rdb.JSONGet(ctx, \"doc\", `$.arrs[?(@ == [1])]`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> [[1]]\n\n\tres3, err := rdb.JSONGet(ctx, \"doc\", `$.arrs[?(@ == [1,[2]])]`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3) // >>> [[1,[2]]]\n\n\tres4, err := rdb.JSONGet(ctx, \"doc\", `$.objs[?(@ == {\"x\":1})]`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4) // >>> [{\"x\":1}]","section_id":"filter-examples"},{"id":"filter-examples-ex63","language":"java","code":"CompletableFuture<Void> filterLiteralEqExample = asyncCommands\n                    .jsonSet(\"doc\", JsonPath.ROOT_PATH,\n                            parser.createJsonValue(\n                                    \"{\\\"arrs\\\":[[1],[2],[1,2],[1,[2]]],\\\"objs\\\":[{\\\"x\\\":1},{\\\"x\\\":2},{\\\"y\\\":1}]}\"))\n                    .thenCompose(res1 -> {\n                        System.out.println(res1); // >>> OK\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$.arrs[?(@ == [1])]\"));\n                    }).thenCompose(res2 -> {\n                        System.out.println(res2); // >>> [[[1]]]\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$.arrs[?(@ == [1,[2]])]\"));\n                    }).thenCompose(res3 -> {\n                        System.out.println(res3); // >>> [[[1,[2]]]]\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$.objs[?(@ == {\\\"x\\\":1})]\"));\n                    }).thenAccept(res4 -> {\n                        System.out.println(res4); // >>> [[{\"x\":1}]]\n                    }).toCompletableFuture();","section_id":"filter-examples"},{"id":"filter-examples-ex64","language":"java","code":"Mono<Void> filterLiteralEqExample = reactiveCommands\n                    .jsonSet(\"doc\", JsonPath.ROOT_PATH,\n                            parser.createJsonValue(\n                                    \"{\\\"arrs\\\":[[1],[2],[1,2],[1,[2]]],\\\"objs\\\":[{\\\"x\\\":1},{\\\"x\\\":2},{\\\"y\\\":1}]}\"))\n                    .doOnNext(res1 -> {\n                        System.out.println(res1); // >>> OK\n                    })\n                    .flatMap(res1 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$.arrs[?(@ == [1])]\")).collectList())\n                    .doOnNext(res2 -> {\n                        System.out.println(res2); // >>> [[[1]]]\n                    })\n                    .flatMap(res2 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$.arrs[?(@ == [1,[2]])]\")).collectList())\n                    .doOnNext(res3 -> {\n                        System.out.println(res3); // >>> [[[1,[2]]]]\n                    })\n                    .flatMap(res3 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$.objs[?(@ == {\\\"x\\\":1})]\")).collectList())\n                    .doOnNext(res4 -> {\n                        System.out.println(res4); // >>> [[{\"x\":1}]]\n                    })\n                    .then();","section_id":"filter-examples"},{"id":"filter-examples-ex65","language":"java","code":"String res5 = jedis.jsonSet(\n            \"doc\",\n            new Path2(\"$\"),\n            \"{\\\"arrs\\\":[[1],[2],[1,2],[1,[2]]],\\\"objs\\\":[{\\\"x\\\":1},{\\\"x\\\":2},{\\\"y\\\":1}]}\"\n        );\n        System.out.println(res5);    // >>> OK\n\n        Object res6 = jedis.jsonGet(\"doc\", new Path2(\"$.arrs[?(@ == [1])]\"));\n        System.out.println(res6);    // >>> [[1]]\n\n        Object res7 = jedis.jsonGet(\"doc\", new Path2(\"$.arrs[?(@ == [1,[2]])]\"));\n        System.out.println(res7);    // >>> [[1,[2]]]\n\n        Object res8 = jedis.jsonGet(\"doc\", new Path2(\"$.objs[?(@ == {\\\"x\\\":1})]\"));\n        System.out.println(res8);    // >>> [{\"x\":1}]","section_id":"filter-examples"},{"id":"filter-examples-ex66","language":"javascript","code":"const res5 = await client.json.set('doc', '$', {\n  arrs: [[1], [2], [1, 2], [1, [2]]],\n  objs: [{ x: 1 }, { x: 2 }, { y: 1 }]\n});\nconsole.log(res5); // >>> OK\n\nconst res6 = await client.json.get('doc', { path: '$.arrs[?(@ == [1])]' });\nconsole.log(res6); // >>> [ [ 1 ] ]\n\nconst res7 = await client.json.get('doc', { path: '$.arrs[?(@ == [1,[2]])]' });\nconsole.log(res7); // >>> [ [ 1, [ 2 ] ] ]\n\nconst res8 = await client.json.get('doc', { path: '$.objs[?(@ == {\"x\":1})]' });\nconsole.log(res8); // >>> [ { x: 1 } ]","section_id":"filter-examples"},{"id":"filter-examples-ex67","language":"php","code":"$res5 = $this->redis->jsonset('doc', '$', json_encode([\n            \"arrs\" => [[1], [2], [1, 2], [1, [2]]],\n            \"objs\" => [[\"x\" => 1], [\"x\" => 2], [\"y\" => 1]],\n        ]));\n        echo $res5 . PHP_EOL; // >>> OK\n\n        $res6 = $this->redis->jsonget('doc', '', '', '', '$.arrs[?(@ == [1])]');\n        echo $res6 . PHP_EOL; // >>> [[1]]\n\n        $res7 = $this->redis->jsonget('doc', '', '', '', '$.arrs[?(@ == [1,[2]])]');\n        echo $res7 . PHP_EOL; // >>> [[1,[2]]]\n\n        $res8 = $this->redis->jsonget('doc', '', '', '', '$.objs[?(@ == {\"x\":1})]');\n        echo $res8 . PHP_EOL; // >>> [{\"x\":1}]","section_id":"filter-examples"},{"id":"filter-examples-ex68","language":"python","code":"res1 = r.json().set(\n    \"doc\",\n    \"$\",\n    {\"arrs\": [[1], [2], [1, 2], [1, [2]]], \"objs\": [{\"x\": 1}, {\"x\": 2}, {\"y\": 1}]},\n)\nprint(res1)\n# >>> True\n\nres2 = r.json().get(\"doc\", \"$.arrs[?(@ == [1])]\")\nprint(res2)\n# >>> [[1]]\n\nres3 = r.json().get(\"doc\", \"$.arrs[?(@ == [1,[2]])]\")\nprint(res3)\n# >>> [[1, [2]]]\n\nres4 = r.json().get(\"doc\", '$.objs[?(@ == {\"x\":1})]')\nprint(res4)\n# >>> [{'x': 1}]","section_id":"filter-examples"},{"id":"filter-examples-ex69","language":"ruby","code":"res1 = r.json_set(\n  'doc', '$',\n  { 'arrs' => [[1], [2], [1, 2], [1, [2]]], 'objs' => [{ 'x' => 1 }, { 'x' => 2 }, { 'y' => 1 }] }\n)\nputs res1 # >>> OK\n\nres2 = r.json_get('doc', '$.arrs[?(@ == [1])]')\np res2 # >>> [[1]]\n\nres3 = r.json_get('doc', '$.arrs[?(@ == [1,[2]])]')\np res3 # >>> [[1, [2]]]\n\nres4 = r.json_get('doc', '$.objs[?(@ == {\"x\":1})]')\np res4 # >>> [{\"x\"=>1}]","section_id":"filter-examples"},{"id":"filter-examples-ex70","language":"rust","code":"let _: bool = match r.json_set(\n            \"doc\",\n            \"$\",\n            &json!({\"arrs\":[[1],[2],[1,2],[1,[2]]],\"objs\":[{\"x\":1},{\"x\":2},{\"y\":1}]}),\n        ).await {\n            Ok(v) => v,\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        };\n\n        match r.json_get(\"doc\", \"$.arrs[?(@ == [1])]\").await {\n            Ok(res4) => {\n                let res4: String = res4;\n                println!(\"{res4}\");    // >>> [[1]]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.arrs[?(@ == [1,[2]])]\").await {\n            Ok(res5) => {\n                let res5: String = res5;\n                println!(\"{res5}\");    // >>> [[1,[2]]]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", r#\"$.objs[?(@ == {\"x\":1})]\"#).await {\n            Ok(res6) => {\n                let res6: String = res6;\n                println!(\"{res6}\");    // >>> [{\"x\":1}]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }","section_id":"filter-examples"},{"id":"filter-examples-ex71","language":"rust","code":"match r.json_set(\n            \"doc\",\n            \"$\",\n            &json!({\"arrs\":[[1],[2],[1,2],[1,[2]]],\"objs\":[{\"x\":1},{\"x\":2},{\"y\":1}]}),\n        ) {\n            Ok(res5) => {\n                let res5: bool = res5;\n                println!(\"{}\", if res5 { \"OK\" } else { \"(nil)\" });    // >>> OK\n            },\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.arrs[?(@ == [1])]\") {\n            Ok(res6) => {\n                let res6: String = res6;\n                println!(\"{res6}\");    // >>> [[1]]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.arrs[?(@ == [1,[2]])]\") {\n            Ok(res7) => {\n                let res7: String = res7;\n                println!(\"{res7}\");    // >>> [[1,[2]]]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", r#\"$.objs[?(@ == {\"x\":1})]\"#) {\n            Ok(res8) => {\n                let res8: String = res8;\n                println!(\"{res8}\");    // >>> [{\"x\":1}]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }","section_id":"filter-examples"},{"id":"filter-examples-ex72","language":"plaintext","code":"> JSON.SET doc $ '[{\"a\":2,\"b\":3},{\"a\":5,\"b\":2}]'\nOK\n> JSON.GET doc '$[?@.a + 1 == 3]'\n\"[{\\\"a\\\":2,\\\"b\\\":3}]\"\n> JSON.GET doc '$[?@.a + @.b * 2 == 8]'\n\"[{\\\"a\\\":2,\\\"b\\\":3}]\"\n> JSON.GET doc '$[?(@.a + @.b) * 2 == 10]'\n\"[{\\\"a\\\":2,\\\"b\\\":3}]\"","section_id":"filter-examples"},{"id":"filter-examples-ex73","language":"csharp","code":"bool res9 = db.JSON().Set(\"doc\", \"$\", \"[{\\\"a\\\":2,\\\"b\\\":3},{\\\"a\\\":5,\\\"b\\\":2}]\");\n        Console.WriteLine(res9);    // >>> True\n\n        RedisResult res10 = db.JSON().Get(\"doc\", path: \"$[?@.a + 1 == 3]\");\n        Console.WriteLine(res10);   // >>> [{\"a\":2,\"b\":3}]\n\n        RedisResult res11 = db.JSON().Get(\"doc\", path: \"$[?@.a + @.b * 2 == 8]\");\n        Console.WriteLine(res11);   // >>> [{\"a\":2,\"b\":3}]\n\n        RedisResult res12 = db.JSON().Get(\"doc\", path: \"$[?(@.a + @.b) * 2 == 10]\");\n        Console.WriteLine(res12);   // >>> [{\"a\":2,\"b\":3}]","section_id":"filter-examples"},{"id":"filter-examples-ex74","language":"go","code":"res1, err := rdb.JSONSet(ctx, \"doc\", \"$\",\n\t\t`[{\"a\":2,\"b\":3},{\"a\":5,\"b\":2}]`,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> OK\n\n\tres2, err := rdb.JSONGet(ctx, \"doc\", `$[?@.a + 1 == 3]`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> [{\"a\":2,\"b\":3}]\n\n\tres3, err := rdb.JSONGet(ctx, \"doc\", `$[?@.a + @.b * 2 == 8]`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3) // >>> [{\"a\":2,\"b\":3}]\n\n\tres4, err := rdb.JSONGet(ctx, \"doc\", `$[?(@.a + @.b) * 2 == 10]`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4) // >>> [{\"a\":2,\"b\":3}]","section_id":"filter-examples"},{"id":"filter-examples-ex75","language":"java","code":"CompletableFuture<Void> filterArithmeticExample = asyncCommands\n                    .jsonSet(\"doc\", JsonPath.ROOT_PATH,\n                            parser.createJsonValue(\"[{\\\"a\\\":2,\\\"b\\\":3},{\\\"a\\\":5,\\\"b\\\":2}]\"))\n                    .thenCompose(res1 -> {\n                        System.out.println(res1); // >>> OK\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$[?@.a + 1 == 3]\"));\n                    }).thenCompose(res2 -> {\n                        System.out.println(res2); // >>> [[{\"a\":2,\"b\":3}]]\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$[?@.a + @.b * 2 == 8]\"));\n                    }).thenCompose(res3 -> {\n                        System.out.println(res3); // >>> [[{\"a\":2,\"b\":3}]]\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$[?(@.a + @.b) * 2 == 10]\"));\n                    }).thenAccept(res4 -> {\n                        System.out.println(res4); // >>> [[{\"a\":2,\"b\":3}]]\n                    }).toCompletableFuture();","section_id":"filter-examples"},{"id":"filter-examples-ex76","language":"java","code":"Mono<Void> filterArithmeticExample = reactiveCommands\n                    .jsonSet(\"doc\", JsonPath.ROOT_PATH, parser.createJsonValue(\"[{\\\"a\\\":2,\\\"b\\\":3},{\\\"a\\\":5,\\\"b\\\":2}]\"))\n                    .doOnNext(res1 -> {\n                        System.out.println(res1); // >>> OK\n                    })\n                    .flatMap(res1 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$[?@.a + 1 == 3]\")).collectList())\n                    .doOnNext(res2 -> {\n                        System.out.println(res2); // >>> [[{\"a\":2,\"b\":3}]]\n                    })\n                    .flatMap(res2 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$[?@.a + @.b * 2 == 8]\")).collectList())\n                    .doOnNext(res3 -> {\n                        System.out.println(res3); // >>> [[{\"a\":2,\"b\":3}]]\n                    })\n                    .flatMap(res3 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$[?(@.a + @.b) * 2 == 10]\")).collectList())\n                    .doOnNext(res4 -> {\n                        System.out.println(res4); // >>> [[{\"a\":2,\"b\":3}]]\n                    })\n                    .then();","section_id":"filter-examples"},{"id":"filter-examples-ex77","language":"java","code":"String res9 = jedis.jsonSet(\"doc\", new Path2(\"$\"), \"[{\\\"a\\\":2,\\\"b\\\":3},{\\\"a\\\":5,\\\"b\\\":2}]\");\n        System.out.println(res9);    // >>> OK\n\n        Object res10 = jedis.jsonGet(\"doc\", new Path2(\"$[?@.a + 1 == 3]\"));\n        System.out.println(res10);    // >>> [{\"a\":2,\"b\":3}]\n\n        Object res11 = jedis.jsonGet(\"doc\", new Path2(\"$[?@.a + @.b * 2 == 8]\"));\n        System.out.println(res11);    // >>> [{\"a\":2,\"b\":3}]\n\n        Object res12 = jedis.jsonGet(\"doc\", new Path2(\"$[?(@.a + @.b) * 2 == 10]\"));\n        System.out.println(res12);    // >>> [{\"a\":2,\"b\":3}]","section_id":"filter-examples"},{"id":"filter-examples-ex78","language":"javascript","code":"const res9 = await client.json.set('doc', '$', [{ a: 2, b: 3 }, { a: 5, b: 2 }]);\nconsole.log(res9); // >>> OK\n\nconst res10 = await client.json.get('doc', { path: '$[?@.a + 1 == 3]' });\nconsole.log(res10); // >>> [ { a: 2, b: 3 } ]\n\nconst res11 = await client.json.get('doc', { path: '$[?@.a + @.b * 2 == 8]' });\nconsole.log(res11); // >>> [ { a: 2, b: 3 } ]\n\nconst res12 = await client.json.get('doc', { path: '$[?(@.a + @.b) * 2 == 10]' });\nconsole.log(res12); // >>> [ { a: 2, b: 3 } ]","section_id":"filter-examples"},{"id":"filter-examples-ex79","language":"php","code":"$res9 = $this->redis->jsonset('doc', '$', json_encode(\n            [[\"a\" => 2, \"b\" => 3], [\"a\" => 5, \"b\" => 2]]\n        ));\n        echo $res9 . PHP_EOL; // >>> OK\n\n        $res10 = $this->redis->jsonget('doc', '', '', '', '$[?@.a + 1 == 3]');\n        echo $res10 . PHP_EOL; // >>> [{\"a\":2,\"b\":3}]\n\n        $res11 = $this->redis->jsonget('doc', '', '', '', '$[?@.a + @.b * 2 == 8]');\n        echo $res11 . PHP_EOL; // >>> [{\"a\":2,\"b\":3}]\n\n        $res12 = $this->redis->jsonget('doc', '', '', '', '$[?(@.a + @.b) * 2 == 10]');\n        echo $res12 . PHP_EOL; // >>> [{\"a\":2,\"b\":3}]","section_id":"filter-examples"},{"id":"filter-examples-ex80","language":"python","code":"res1 = r.json().set(\"doc\", \"$\", [{\"a\": 2, \"b\": 3}, {\"a\": 5, \"b\": 2}])\nprint(res1)\n# >>> True\n\nres2 = r.json().get(\"doc\", \"$[?@.a + 1 == 3]\")\nprint(res2)\n# >>> [{'a': 2, 'b': 3}]\n\nres3 = r.json().get(\"doc\", \"$[?@.a + @.b * 2 == 8]\")\nprint(res3)\n# >>> [{'a': 2, 'b': 3}]\n\nres4 = r.json().get(\"doc\", \"$[?(@.a + @.b) * 2 == 10]\")\nprint(res4)\n# >>> [{'a': 2, 'b': 3}]","section_id":"filter-examples"},{"id":"filter-examples-ex81","language":"ruby","code":"res1 = r.json_set('doc', '$', [{ 'a' => 2, 'b' => 3 }, { 'a' => 5, 'b' => 2 }])\nputs res1 # >>> OK\n\nres2 = r.json_get('doc', '$[?@.a + 1 == 3]')\np res2 # >>> [{\"a\"=>2, \"b\"=>3}]\n\nres3 = r.json_get('doc', '$[?@.a + @.b * 2 == 8]')\np res3 # >>> [{\"a\"=>2, \"b\"=>3}]\n\nres4 = r.json_get('doc', '$[?(@.a + @.b) * 2 == 10]')\np res4 # >>> [{\"a\"=>2, \"b\"=>3}]","section_id":"filter-examples"},{"id":"filter-examples-ex82","language":"rust","code":"let _: bool = match r.json_set(\"doc\", \"$\", &json!([{\"a\":2,\"b\":3},{\"a\":5,\"b\":2}])).await {\n            Ok(v) => v,\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        };\n\n        match r.json_get(\"doc\", \"$[?@.a + 1 == 3]\").await {\n            Ok(res7) => {\n                let res7: String = res7;\n                println!(\"{res7}\");    // >>> [{\"a\":2,\"b\":3}]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$[?@.a + @.b * 2 == 8]\").await {\n            Ok(res8) => {\n                let res8: String = res8;\n                println!(\"{res8}\");    // >>> [{\"a\":2,\"b\":3}]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$[?(@.a + @.b) * 2 == 10]\").await {\n            Ok(res9) => {\n                let res9: String = res9;\n                println!(\"{res9}\");    // >>> [{\"a\":2,\"b\":3}]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }","section_id":"filter-examples"},{"id":"filter-examples-ex83","language":"rust","code":"match r.json_set(\"doc\", \"$\", &json!([{\"a\":2,\"b\":3},{\"a\":5,\"b\":2}])) {\n            Ok(res9) => {\n                let res9: bool = res9;\n                println!(\"{}\", if res9 { \"OK\" } else { \"(nil)\" });    // >>> OK\n            },\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$[?@.a + 1 == 3]\") {\n            Ok(res10) => {\n                let res10: String = res10;\n                println!(\"{res10}\");    // >>> [{\"a\":2,\"b\":3}]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$[?@.a + @.b * 2 == 8]\") {\n            Ok(res11) => {\n                let res11: String = res11;\n                println!(\"{res11}\");    // >>> [{\"a\":2,\"b\":3}]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$[?(@.a + @.b) * 2 == 10]\") {\n            Ok(res12) => {\n                let res12: String = res12;\n                println!(\"{res12}\");    // >>> [{\"a\":2,\"b\":3}]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }","section_id":"filter-examples"},{"id":"filter-examples-ex84","language":"plaintext","code":"> JSON.SET doc $ '{\"a\":[1,2,3,4],\"allow\":[2,3]}'\nOK\n> JSON.GET doc '$.a[?@ in [2,4]]'\n\"[2,4]\"\n> JSON.GET doc '$.a[?@ nin [2,4]]'\n\"[1,3]\"\n> JSON.GET doc '$.a[?@ in $.allow]'\n\"[2,3]\"","section_id":"filter-examples"},{"id":"filter-examples-ex85","language":"csharp","code":"bool res13 = db.JSON().Set(\"doc\", \"$\", \"{\\\"a\\\":[1,2,3,4],\\\"allow\\\":[2,3]}\");\n        Console.WriteLine(res13);   // >>> True\n\n        RedisResult res14 = db.JSON().Get(\"doc\", path: \"$.a[?@ in [2,4]]\");\n        Console.WriteLine(res14);   // >>> [2,4]\n\n        RedisResult res15 = db.JSON().Get(\"doc\", path: \"$.a[?@ nin [2,4]]\");\n        Console.WriteLine(res15);   // >>> [1,3]\n\n        RedisResult res16 = db.JSON().Get(\"doc\", path: \"$.a[?@ in $.allow]\");\n        Console.WriteLine(res16);   // >>> [2,3]","section_id":"filter-examples"},{"id":"filter-examples-ex86","language":"go","code":"res1, err := rdb.JSONSet(ctx, \"doc\", \"$\",\n\t\t`{\"a\":[1,2,3,4],\"allow\":[2,3]}`,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> OK\n\n\tres2, err := rdb.JSONGet(ctx, \"doc\", `$.a[?@ in [2,4]]`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> [2,4]\n\n\tres3, err := rdb.JSONGet(ctx, \"doc\", `$.a[?@ nin [2,4]]`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3) // >>> [1,3]\n\n\tres4, err := rdb.JSONGet(ctx, \"doc\", `$.a[?@ in $.allow]`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4) // >>> [2,3]","section_id":"filter-examples"},{"id":"filter-examples-ex87","language":"java","code":"CompletableFuture<Void> filterMembershipExample = asyncCommands\n                    .jsonSet(\"doc\", JsonPath.ROOT_PATH,\n                            parser.createJsonValue(\"{\\\"a\\\":[1,2,3,4],\\\"allow\\\":[2,3]}\"))\n                    .thenCompose(res1 -> {\n                        System.out.println(res1); // >>> OK\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?@ in [2,4]]\"));\n                    }).thenCompose(res2 -> {\n                        System.out.println(res2); // >>> [[2,4]]\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?@ nin [2,4]]\"));\n                    }).thenCompose(res3 -> {\n                        System.out.println(res3); // >>> [[1,3]]\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?@ in $.allow]\"));\n                    }).thenAccept(res4 -> {\n                        System.out.println(res4); // >>> [[2,3]]\n                    }).toCompletableFuture();","section_id":"filter-examples"},{"id":"filter-examples-ex88","language":"java","code":"Mono<Void> filterMembershipExample = reactiveCommands\n                    .jsonSet(\"doc\", JsonPath.ROOT_PATH, parser.createJsonValue(\"{\\\"a\\\":[1,2,3,4],\\\"allow\\\":[2,3]}\"))\n                    .doOnNext(res1 -> {\n                        System.out.println(res1); // >>> OK\n                    })\n                    .flatMap(res1 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?@ in [2,4]]\")).collectList())\n                    .doOnNext(res2 -> {\n                        System.out.println(res2); // >>> [[2,4]]\n                    })\n                    .flatMap(res2 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?@ nin [2,4]]\")).collectList())\n                    .doOnNext(res3 -> {\n                        System.out.println(res3); // >>> [[1,3]]\n                    })\n                    .flatMap(res3 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?@ in $.allow]\")).collectList())\n                    .doOnNext(res4 -> {\n                        System.out.println(res4); // >>> [[2,3]]\n                    })\n                    .then();","section_id":"filter-examples"},{"id":"filter-examples-ex89","language":"java","code":"String res13 = jedis.jsonSet(\"doc\", new Path2(\"$\"), \"{\\\"a\\\":[1,2,3,4],\\\"allow\\\":[2,3]}\");\n        System.out.println(res13);    // >>> OK\n\n        Object res14 = jedis.jsonGet(\"doc\", new Path2(\"$.a[?@ in [2,4]]\"));\n        System.out.println(res14);    // >>> [2,4]\n\n        Object res15 = jedis.jsonGet(\"doc\", new Path2(\"$.a[?@ nin [2,4]]\"));\n        System.out.println(res15);    // >>> [1,3]\n\n        Object res16 = jedis.jsonGet(\"doc\", new Path2(\"$.a[?@ in $.allow]\"));\n        System.out.println(res16);    // >>> [2,3]","section_id":"filter-examples"},{"id":"filter-examples-ex90","language":"javascript","code":"const res13 = await client.json.set('doc', '$', { a: [1, 2, 3, 4], allow: [2, 3] });\nconsole.log(res13); // >>> OK\n\nconst res14 = await client.json.get('doc', { path: '$.a[?@ in [2,4]]' });\nconsole.log(res14); // >>> [ 2, 4 ]\n\nconst res15 = await client.json.get('doc', { path: '$.a[?@ nin [2,4]]' });\nconsole.log(res15); // >>> [ 1, 3 ]\n\nconst res16 = await client.json.get('doc', { path: '$.a[?@ in $.allow]' });\nconsole.log(res16); // >>> [ 2, 3 ]","section_id":"filter-examples"},{"id":"filter-examples-ex91","language":"php","code":"$res13 = $this->redis->jsonset('doc', '$', json_encode(\n            [\"a\" => [1, 2, 3, 4], \"allow\" => [2, 3]]\n        ));\n        echo $res13 . PHP_EOL; // >>> OK\n\n        $res14 = $this->redis->jsonget('doc', '', '', '', '$.a[?@ in [2,4]]');\n        echo $res14 . PHP_EOL; // >>> [2,4]\n\n        $res15 = $this->redis->jsonget('doc', '', '', '', '$.a[?@ nin [2,4]]');\n        echo $res15 . PHP_EOL; // >>> [1,3]\n\n        $res16 = $this->redis->jsonget('doc', '', '', '', '$.a[?@ in $.allow]');\n        echo $res16 . PHP_EOL; // >>> [2,3]","section_id":"filter-examples"},{"id":"filter-examples-ex92","language":"python","code":"res1 = r.json().set(\"doc\", \"$\", {\"a\": [1, 2, 3, 4], \"allow\": [2, 3]})\nprint(res1)\n# >>> True\n\nres2 = r.json().get(\"doc\", \"$.a[?@ in [2,4]]\")\nprint(res2)\n# >>> [2, 4]\n\nres3 = r.json().get(\"doc\", \"$.a[?@ nin [2,4]]\")\nprint(res3)\n# >>> [1, 3]\n\nres4 = r.json().get(\"doc\", \"$.a[?@ in $.allow]\")\nprint(res4)\n# >>> [2, 3]","section_id":"filter-examples"},{"id":"filter-examples-ex93","language":"ruby","code":"res1 = r.json_set('doc', '$', { 'a' => [1, 2, 3, 4], 'allow' => [2, 3] })\nputs res1 # >>> OK\n\nres2 = r.json_get('doc', '$.a[?@ in [2,4]]')\np res2 # >>> [2, 4]\n\nres3 = r.json_get('doc', '$.a[?@ nin [2,4]]')\np res3 # >>> [1, 3]\n\nres4 = r.json_get('doc', '$.a[?@ in $.allow]')\np res4 # >>> [2, 3]","section_id":"filter-examples"},{"id":"filter-examples-ex94","language":"rust","code":"let _: bool = match r.json_set(\"doc\", \"$\", &json!({\"a\":[1,2,3,4],\"allow\":[2,3]})).await {\n            Ok(v) => v,\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        };\n\n        match r.json_get(\"doc\", \"$.a[?@ in [2,4]]\").await {\n            Ok(res10) => {\n                let res10: String = res10;\n                println!(\"{res10}\");    // >>> [2,4]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.a[?@ nin [2,4]]\").await {\n            Ok(res11) => {\n                let res11: String = res11;\n                println!(\"{res11}\");    // >>> [1,3]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.a[?@ in $.allow]\").await {\n            Ok(res12) => {\n                let res12: String = res12;\n                println!(\"{res12}\");    // >>> [2,3]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }","section_id":"filter-examples"},{"id":"filter-examples-ex95","language":"rust","code":"match r.json_set(\"doc\", \"$\", &json!({\"a\":[1,2,3,4],\"allow\":[2,3]})) {\n            Ok(res13) => {\n                let res13: bool = res13;\n                println!(\"{}\", if res13 { \"OK\" } else { \"(nil)\" });    // >>> OK\n            },\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.a[?@ in [2,4]]\") {\n            Ok(res14) => {\n                let res14: String = res14;\n                println!(\"{res14}\");    // >>> [2,4]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.a[?@ nin [2,4]]\") {\n            Ok(res15) => {\n                let res15: String = res15;\n                println!(\"{res15}\");    // >>> [1,3]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.a[?@ in $.allow]\") {\n            Ok(res16) => {\n                let res16: String = res16;\n                println!(\"{res16}\");    // >>> [2,3]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }","section_id":"filter-examples"},{"id":"filter-examples-ex96","language":"plaintext","code":"> JSON.SET doc $ '{\"a\":[[1,2],[1,5],[]]}'\nOK\n> JSON.GET doc '$.a[?@ subsetof [1,2,3]]'\n\"[[1,2],[]]\"\n> JSON.SET doc $ '{\"a\":[[1,9],[8,9],[]]}'\nOK\n> JSON.GET doc '$.a[?@ anyof [1,2,3]]'\n\"[[1,9]]\"\n> JSON.SET doc $ '{\"a\":[[4,5],[1,9],[]]}'\nOK\n> JSON.GET doc '$.a[?@ noneof [1,2,3]]'\n\"[[4,5],[]]\"","section_id":"filter-examples"},{"id":"filter-examples-ex97","language":"csharp","code":"bool res17 = db.JSON().Set(\"doc\", \"$\", \"{\\\"a\\\":[[1,2],[1,5],[]]}\");\n        Console.WriteLine(res17);   // >>> True\n\n        RedisResult res18 = db.JSON().Get(\"doc\", path: \"$.a[?@ subsetof [1,2,3]]\");\n        Console.WriteLine(res18);   // >>> [[1,2],[]]\n\n        bool res19 = db.JSON().Set(\"doc\", \"$\", \"{\\\"a\\\":[[1,9],[8,9],[]]}\");\n        Console.WriteLine(res19);   // >>> True\n\n        RedisResult res20 = db.JSON().Get(\"doc\", path: \"$.a[?@ anyof [1,2,3]]\");\n        Console.WriteLine(res20);   // >>> [[1,9]]\n\n        bool res21 = db.JSON().Set(\"doc\", \"$\", \"{\\\"a\\\":[[4,5],[1,9],[]]}\");\n        Console.WriteLine(res21);   // >>> True\n\n        RedisResult res22 = db.JSON().Get(\"doc\", path: \"$.a[?@ noneof [1,2,3]]\");\n        Console.WriteLine(res22);   // >>> [[4,5],[]]","section_id":"filter-examples"},{"id":"filter-examples-ex98","language":"go","code":"res1, err := rdb.JSONSet(ctx, \"doc\", \"$\",\n\t\t`{\"a\":[[1,2],[1,5],[]]}`,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> OK\n\n\tres2, err := rdb.JSONGet(ctx, \"doc\", `$.a[?@ subsetof [1,2,3]]`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> [[1,2],[]]\n\n\tres3, err := rdb.JSONSet(ctx, \"doc\", \"$\",\n\t\t`{\"a\":[[1,9],[8,9],[]]}`,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3) // >>> OK\n\n\tres4, err := rdb.JSONGet(ctx, \"doc\", `$.a[?@ anyof [1,2,3]]`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4) // >>> [[1,9]]\n\n\tres5, err := rdb.JSONSet(ctx, \"doc\", \"$\",\n\t\t`{\"a\":[[4,5],[1,9],[]]}`,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res5) // >>> OK\n\n\tres6, err := rdb.JSONGet(ctx, \"doc\", `$.a[?@ noneof [1,2,3]]`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res6) // >>> [[4,5],[]]","section_id":"filter-examples"},{"id":"filter-examples-ex99","language":"java","code":"CompletableFuture<Void> filterSetRelationsExample = asyncCommands\n                    .jsonSet(\"doc\", JsonPath.ROOT_PATH, parser.createJsonValue(\"{\\\"a\\\":[[1,2],[1,5],[]]}\"))\n                    .thenCompose(res1 -> {\n                        System.out.println(res1); // >>> OK\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?@ subsetof [1,2,3]]\"));\n                    }).thenCompose(res2 -> {\n                        System.out.println(res2); // >>> [[[1,2],[]]]\n                        return asyncCommands.jsonSet(\"doc\", JsonPath.ROOT_PATH,\n                                parser.createJsonValue(\"{\\\"a\\\":[[1,9],[8,9],[]]}\"));\n                    }).thenCompose(res3 -> {\n                        System.out.println(res3); // >>> OK\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?@ anyof [1,2,3]]\"));\n                    }).thenCompose(res4 -> {\n                        System.out.println(res4); // >>> [[[1,9]]]\n                        return asyncCommands.jsonSet(\"doc\", JsonPath.ROOT_PATH,\n                                parser.createJsonValue(\"{\\\"a\\\":[[4,5],[1,9],[]]}\"));\n                    }).thenCompose(res5 -> {\n                        System.out.println(res5); // >>> OK\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?@ noneof [1,2,3]]\"));\n                    }).thenAccept(res6 -> {\n                        System.out.println(res6); // >>> [[[4,5],[]]]\n                    }).toCompletableFuture();","section_id":"filter-examples"},{"id":"filter-examples-ex100","language":"java","code":"Mono<Void> filterSetRelationsExample = reactiveCommands\n                    .jsonSet(\"doc\", JsonPath.ROOT_PATH, parser.createJsonValue(\"{\\\"a\\\":[[1,2],[1,5],[]]}\"))\n                    .doOnNext(res1 -> {\n                        System.out.println(res1); // >>> OK\n                    })\n                    .flatMap(res1 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?@ subsetof [1,2,3]]\")).collectList())\n                    .doOnNext(res2 -> {\n                        System.out.println(res2); // >>> [[[1,2],[]]]\n                    })\n                    .flatMap(res2 -> reactiveCommands.jsonSet(\"doc\", JsonPath.ROOT_PATH,\n                            parser.createJsonValue(\"{\\\"a\\\":[[1,9],[8,9],[]]}\")))\n                    .doOnNext(res3 -> {\n                        System.out.println(res3); // >>> OK\n                    })\n                    .flatMap(res3 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?@ anyof [1,2,3]]\")).collectList())\n                    .doOnNext(res4 -> {\n                        System.out.println(res4); // >>> [[[1,9]]]\n                    })\n                    .flatMap(res4 -> reactiveCommands.jsonSet(\"doc\", JsonPath.ROOT_PATH,\n                            parser.createJsonValue(\"{\\\"a\\\":[[4,5],[1,9],[]]}\")))\n                    .doOnNext(res5 -> {\n                        System.out.println(res5); // >>> OK\n                    })\n                    .flatMap(res5 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?@ noneof [1,2,3]]\")).collectList())\n                    .doOnNext(res6 -> {\n                        System.out.println(res6); // >>> [[[4,5],[]]]\n                    })\n                    .then();","section_id":"filter-examples"},{"id":"filter-examples-ex101","language":"java","code":"String res17 = jedis.jsonSet(\"doc\", new Path2(\"$\"), \"{\\\"a\\\":[[1,2],[1,5],[]]}\");\n        System.out.println(res17);    // >>> OK\n\n        Object res18 = jedis.jsonGet(\"doc\", new Path2(\"$.a[?@ subsetof [1,2,3]]\"));\n        System.out.println(res18);    // >>> [[1,2],[]]\n\n        String res19 = jedis.jsonSet(\"doc\", new Path2(\"$\"), \"{\\\"a\\\":[[1,9],[8,9],[]]}\");\n        System.out.println(res19);    // >>> OK\n\n        Object res20 = jedis.jsonGet(\"doc\", new Path2(\"$.a[?@ anyof [1,2,3]]\"));\n        System.out.println(res20);    // >>> [[1,9]]\n\n        String res21 = jedis.jsonSet(\"doc\", new Path2(\"$\"), \"{\\\"a\\\":[[4,5],[1,9],[]]}\");\n        System.out.println(res21);    // >>> OK\n\n        Object res22 = jedis.jsonGet(\"doc\", new Path2(\"$.a[?@ noneof [1,2,3]]\"));\n        System.out.println(res22);    // >>> [[4,5],[]]","section_id":"filter-examples"},{"id":"filter-examples-ex102","language":"javascript","code":"const res17 = await client.json.set('doc', '$', { a: [[1, 2], [1, 5], []] });\nconsole.log(res17); // >>> OK\n\nconst res18 = await client.json.get('doc', { path: '$.a[?@ subsetof [1,2,3]]' });\nconsole.log(res18); // >>> [ [ 1, 2 ], [] ]\n\nconst res19 = await client.json.set('doc', '$', { a: [[1, 9], [8, 9], []] });\nconsole.log(res19); // >>> OK\n\nconst res20 = await client.json.get('doc', { path: '$.a[?@ anyof [1,2,3]]' });\nconsole.log(res20); // >>> [ [ 1, 9 ] ]\n\nconst res21 = await client.json.set('doc', '$', { a: [[4, 5], [1, 9], []] });\nconsole.log(res21); // >>> OK\n\nconst res22 = await client.json.get('doc', { path: '$.a[?@ noneof [1,2,3]]' });\nconsole.log(res22); // >>> [ [ 4, 5 ], [] ]","section_id":"filter-examples"},{"id":"filter-examples-ex103","language":"php","code":"$res17 = $this->redis->jsonset('doc', '$', json_encode(\n            [\"a\" => [[1, 2], [1, 5], []]]\n        ));\n        echo $res17 . PHP_EOL; // >>> OK\n\n        $res18 = $this->redis->jsonget('doc', '', '', '', '$.a[?@ subsetof [1,2,3]]');\n        echo $res18 . PHP_EOL; // >>> [[1,2],[]]\n\n        $res19 = $this->redis->jsonset('doc', '$', json_encode(\n            [\"a\" => [[1, 9], [8, 9], []]]\n        ));\n        echo $res19 . PHP_EOL; // >>> OK\n\n        $res20 = $this->redis->jsonget('doc', '', '', '', '$.a[?@ anyof [1,2,3]]');\n        echo $res20 . PHP_EOL; // >>> [[1,9]]\n\n        $res21 = $this->redis->jsonset('doc', '$', json_encode(\n            [\"a\" => [[4, 5], [1, 9], []]]\n        ));\n        echo $res21 . PHP_EOL; // >>> OK\n\n        $res22 = $this->redis->jsonget('doc', '', '', '', '$.a[?@ noneof [1,2,3]]');\n        echo $res22 . PHP_EOL; // >>> [[4,5],[]]","section_id":"filter-examples"},{"id":"filter-examples-ex104","language":"python","code":"res1 = r.json().set(\"doc\", \"$\", {\"a\": [[1, 2], [1, 5], []]})\nprint(res1)\n# >>> True\n\nres2 = r.json().get(\"doc\", \"$.a[?@ subsetof [1,2,3]]\")\nprint(res2)\n# >>> [[1, 2], []]\n\nres3 = r.json().set(\"doc\", \"$\", {\"a\": [[1, 9], [8, 9], []]})\nprint(res3)\n# >>> True\n\nres4 = r.json().get(\"doc\", \"$.a[?@ anyof [1,2,3]]\")\nprint(res4)\n# >>> [[1, 9]]\n\nres5 = r.json().set(\"doc\", \"$\", {\"a\": [[4, 5], [1, 9], []]})\nprint(res5)\n# >>> True\n\nres6 = r.json().get(\"doc\", \"$.a[?@ noneof [1,2,3]]\")\nprint(res6)\n# >>> [[4, 5], []]","section_id":"filter-examples"},{"id":"filter-examples-ex105","language":"ruby","code":"res1 = r.json_set('doc', '$', { 'a' => [[1, 2], [1, 5], []] })\nputs res1 # >>> OK\n\nres2 = r.json_get('doc', '$.a[?@ subsetof [1,2,3]]')\np res2 # >>> [[1, 2], []]\n\nres3 = r.json_set('doc', '$', { 'a' => [[1, 9], [8, 9], []] })\nputs res3 # >>> OK\n\nres4 = r.json_get('doc', '$.a[?@ anyof [1,2,3]]')\np res4 # >>> [[1, 9]]\n\nres5 = r.json_set('doc', '$', { 'a' => [[4, 5], [1, 9], []] })\nputs res5 # >>> OK\n\nres6 = r.json_get('doc', '$.a[?@ noneof [1,2,3]]')\np res6 # >>> [[4, 5], []]","section_id":"filter-examples"},{"id":"filter-examples-ex106","language":"rust","code":"let _: bool = match r.json_set(\"doc\", \"$\", &json!({\"a\":[[1,2],[1,5],[]]})).await {\n            Ok(v) => v,\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        };\n\n        match r.json_get(\"doc\", \"$.a[?@ subsetof [1,2,3]]\").await {\n            Ok(res13) => {\n                let res13: String = res13;\n                println!(\"{res13}\");    // >>> [[1,2],[]]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        let _: bool = match r.json_set(\"doc\", \"$\", &json!({\"a\":[[1,9],[8,9],[]]})).await {\n            Ok(v) => v,\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        };\n\n        match r.json_get(\"doc\", \"$.a[?@ anyof [1,2,3]]\").await {\n            Ok(res14) => {\n                let res14: String = res14;\n                println!(\"{res14}\");    // >>> [[1,9]]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        let _: bool = match r.json_set(\"doc\", \"$\", &json!({\"a\":[[4,5],[1,9],[]]})).await {\n            Ok(v) => v,\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        };\n\n        match r.json_get(\"doc\", \"$.a[?@ noneof [1,2,3]]\").await {\n            Ok(res15) => {\n                let res15: String = res15;\n                println!(\"{res15}\");    // >>> [[4,5],[]]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }","section_id":"filter-examples"},{"id":"filter-examples-ex107","language":"rust","code":"match r.json_set(\"doc\", \"$\", &json!({\"a\":[[1,2],[1,5],[]]})) {\n            Ok(res17) => {\n                let res17: bool = res17;\n                println!(\"{}\", if res17 { \"OK\" } else { \"(nil)\" });    // >>> OK\n            },\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.a[?@ subsetof [1,2,3]]\") {\n            Ok(res18) => {\n                let res18: String = res18;\n                println!(\"{res18}\");    // >>> [[1,2],[]]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_set(\"doc\", \"$\", &json!({\"a\":[[1,9],[8,9],[]]})) {\n            Ok(res19) => {\n                let res19: bool = res19;\n                println!(\"{}\", if res19 { \"OK\" } else { \"(nil)\" });    // >>> OK\n            },\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.a[?@ anyof [1,2,3]]\") {\n            Ok(res20) => {\n                let res20: String = res20;\n                println!(\"{res20}\");    // >>> [[1,9]]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_set(\"doc\", \"$\", &json!({\"a\":[[4,5],[1,9],[]]})) {\n            Ok(res21) => {\n                let res21: bool = res21;\n                println!(\"{}\", if res21 { \"OK\" } else { \"(nil)\" });    // >>> OK\n            },\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.a[?@ noneof [1,2,3]]\") {\n            Ok(res22) => {\n                let res22: String = res22;\n                println!(\"{res22}\");    // >>> [[4,5],[]]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }","section_id":"filter-examples"},{"id":"filter-examples-ex108","language":"plaintext","code":"> JSON.SET doc $ '{\"a\":[[4,5],[1],[7,8,9]]}'\nOK\n> JSON.GET doc '$.a[?@ sizeof 2]'\n\"[[4,5]]\"\n> JSON.SET doc $ '{\"a\":[[],[1],\"\",[2,3],{},{\"k\":1}]}'\nOK\n> JSON.GET doc '$.a[?@ empty true]'\n\"[[],\\\"\\\",{}]\"\n> JSON.GET doc '$.a[?@ empty false]'\n\"[[1],[2,3],{\\\"k\\\":1}]\"","section_id":"filter-examples"},{"id":"filter-examples-ex109","language":"csharp","code":"bool res23 = db.JSON().Set(\"doc\", \"$\", \"{\\\"a\\\":[[4,5],[1],[7,8,9]]}\");\n        Console.WriteLine(res23);   // >>> True\n\n        RedisResult res24 = db.JSON().Get(\"doc\", path: \"$.a[?@ sizeof 2]\");\n        Console.WriteLine(res24);   // >>> [[4,5]]\n\n        bool res25 = db.JSON().Set(\"doc\", \"$\", \"{\\\"a\\\":[[],[1],\\\"\\\",[2,3],{},{\\\"k\\\":1}]}\");\n        Console.WriteLine(res25);   // >>> True\n\n        RedisResult res26 = db.JSON().Get(\"doc\", path: \"$.a[?@ empty true]\");\n        Console.WriteLine(res26);   // >>> [[],\"\",{}]\n\n        RedisResult res27 = db.JSON().Get(\"doc\", path: \"$.a[?@ empty false]\");\n        Console.WriteLine(res27);   // >>> [[1],[2,3],{\"k\":1}]","section_id":"filter-examples"},{"id":"filter-examples-ex110","language":"go","code":"res1, err := rdb.JSONSet(ctx, \"doc\", \"$\",\n\t\t`{\"a\":[[4,5],[1],[7,8,9]]}`,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> OK\n\n\tres2, err := rdb.JSONGet(ctx, \"doc\", `$.a[?@ sizeof 2]`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> [[4,5]]\n\n\tres3, err := rdb.JSONSet(ctx, \"doc\", \"$\",\n\t\t`{\"a\":[[],[1],\"\",[2,3],{},{\"k\":1}]}`,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3) // >>> OK\n\n\tres4, err := rdb.JSONGet(ctx, \"doc\", `$.a[?@ empty true]`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4) // >>> [[],\"\",{}]\n\n\tres5, err := rdb.JSONGet(ctx, \"doc\", `$.a[?@ empty false]`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res5) // >>> [[1],[2,3],{\"k\":1}]","section_id":"filter-examples"},{"id":"filter-examples-ex111","language":"java","code":"CompletableFuture<Void> filterSizeEmptyExample = asyncCommands\n                    .jsonSet(\"doc\", JsonPath.ROOT_PATH, parser.createJsonValue(\"{\\\"a\\\":[[4,5],[1],[7,8,9]]}\"))\n                    .thenCompose(res1 -> {\n                        System.out.println(res1); // >>> OK\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?@ sizeof 2]\"));\n                    }).thenCompose(res2 -> {\n                        System.out.println(res2); // >>> [[[4,5]]]\n                        return asyncCommands.jsonSet(\"doc\", JsonPath.ROOT_PATH,\n                                parser.createJsonValue(\"{\\\"a\\\":[[],[1],\\\"\\\",[2,3],{},{\\\"k\\\":1}]}\"));\n                    }).thenCompose(res3 -> {\n                        System.out.println(res3); // >>> OK\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?@ empty true]\"));\n                    }).thenCompose(res4 -> {\n                        System.out.println(res4); // >>> [[[],\"\",{}]]\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?@ empty false]\"));\n                    }).thenAccept(res5 -> {\n                        System.out.println(res5); // >>> [[[1],[2,3],{\"k\":1}]]\n                    }).toCompletableFuture();","section_id":"filter-examples"},{"id":"filter-examples-ex112","language":"java","code":"Mono<Void> filterSizeEmptyExample = reactiveCommands\n                    .jsonSet(\"doc\", JsonPath.ROOT_PATH, parser.createJsonValue(\"{\\\"a\\\":[[4,5],[1],[7,8,9]]}\"))\n                    .doOnNext(res1 -> {\n                        System.out.println(res1); // >>> OK\n                    })\n                    .flatMap(res1 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?@ sizeof 2]\")).collectList())\n                    .doOnNext(res2 -> {\n                        System.out.println(res2); // >>> [[[4,5]]]\n                    })\n                    .flatMap(res2 -> reactiveCommands.jsonSet(\"doc\", JsonPath.ROOT_PATH,\n                            parser.createJsonValue(\"{\\\"a\\\":[[],[1],\\\"\\\",[2,3],{},{\\\"k\\\":1}]}\")))\n                    .doOnNext(res3 -> {\n                        System.out.println(res3); // >>> OK\n                    })\n                    .flatMap(res3 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?@ empty true]\")).collectList())\n                    .doOnNext(res4 -> {\n                        System.out.println(res4); // >>> [[[],\"\",{}]]\n                    })\n                    .flatMap(res4 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?@ empty false]\")).collectList())\n                    .doOnNext(res5 -> {\n                        System.out.println(res5); // >>> [[[1],[2,3],{\"k\":1}]]\n                    })\n                    .then();","section_id":"filter-examples"},{"id":"filter-examples-ex113","language":"java","code":"String res23 = jedis.jsonSet(\"doc\", new Path2(\"$\"), \"{\\\"a\\\":[[4,5],[1],[7,8,9]]}\");\n        System.out.println(res23);    // >>> OK\n\n        Object res24 = jedis.jsonGet(\"doc\", new Path2(\"$.a[?@ sizeof 2]\"));\n        System.out.println(res24);    // >>> [[4,5]]\n\n        String res25 = jedis.jsonSet(\"doc\", new Path2(\"$\"), \"{\\\"a\\\":[[],[1],\\\"\\\",[2,3],{},{\\\"k\\\":1}]}\");\n        System.out.println(res25);    // >>> OK\n\n        Object res26 = jedis.jsonGet(\"doc\", new Path2(\"$.a[?@ empty true]\"));\n        System.out.println(res26);    // >>> [[],\"\",{}]\n\n        Object res27 = jedis.jsonGet(\"doc\", new Path2(\"$.a[?@ empty false]\"));\n        System.out.println(res27);    // >>> [[1],[2,3],{\"k\":1}]","section_id":"filter-examples"},{"id":"filter-examples-ex114","language":"javascript","code":"const res23 = await client.json.set('doc', '$', { a: [[4, 5], [1], [7, 8, 9]] });\nconsole.log(res23); // >>> OK\n\nconst res24 = await client.json.get('doc', { path: '$.a[?@ sizeof 2]' });\nconsole.log(res24); // >>> [ [ 4, 5 ] ]\n\nconst res25 = await client.json.set('doc', '$', { a: [[], [1], '', [2, 3], {}, { k: 1 }] });\nconsole.log(res25); // >>> OK\n\nconst res26 = await client.json.get('doc', { path: '$.a[?@ empty true]' });\nconsole.log(res26); // >>> [ [], '', {} ]\n\nconst res27 = await client.json.get('doc', { path: '$.a[?@ empty false]' });\nconsole.log(res27); // >>> [ [ 1 ], [ 2, 3 ], { k: 1 } ]","section_id":"filter-examples"},{"id":"filter-examples-ex115","language":"php","code":"$res23 = $this->redis->jsonset('doc', '$', json_encode(\n            [\"a\" => [[4, 5], [1], [7, 8, 9]]]\n        ));\n        echo $res23 . PHP_EOL; // >>> OK\n\n        $res24 = $this->redis->jsonget('doc', '', '', '', '$.a[?@ sizeof 2]');\n        echo $res24 . PHP_EOL; // >>> [[4,5]]\n\n        $res25 = $this->redis->jsonset('doc', '$', json_encode(\n            [\"a\" => [[], [1], \"\", [2, 3], new \\stdClass(), [\"k\" => 1]]]\n        ));\n        echo $res25 . PHP_EOL; // >>> OK\n\n        $res26 = $this->redis->jsonget('doc', '', '', '', '$.a[?@ empty true]');\n        echo $res26 . PHP_EOL; // >>> [[],\"\",{}]\n\n        $res27 = $this->redis->jsonget('doc', '', '', '', '$.a[?@ empty false]');\n        echo $res27 . PHP_EOL; // >>> [[1],[2,3],{\"k\":1}]","section_id":"filter-examples"},{"id":"filter-examples-ex116","language":"python","code":"res1 = r.json().set(\"doc\", \"$\", {\"a\": [[4, 5], [1], [7, 8, 9]]})\nprint(res1)\n# >>> True\n\nres2 = r.json().get(\"doc\", \"$.a[?@ sizeof 2]\")\nprint(res2)\n# >>> [[4, 5]]\n\nres3 = r.json().set(\"doc\", \"$\", {\"a\": [[], [1], \"\", [2, 3], {}, {\"k\": 1}]})\nprint(res3)\n# >>> True\n\nres4 = r.json().get(\"doc\", \"$.a[?@ empty true]\")\nprint(res4)\n# >>> [[], '', {}]\n\nres5 = r.json().get(\"doc\", \"$.a[?@ empty false]\")\nprint(res5)\n# >>> [[1], [2, 3], {'k': 1}]","section_id":"filter-examples"},{"id":"filter-examples-ex117","language":"ruby","code":"res1 = r.json_set('doc', '$', { 'a' => [[4, 5], [1], [7, 8, 9]] })\nputs res1 # >>> OK\n\nres2 = r.json_get('doc', '$.a[?@ sizeof 2]')\np res2 # >>> [[4, 5]]\n\nres3 = r.json_set('doc', '$', { 'a' => [[], [1], '', [2, 3], {}, { 'k' => 1 }] })\nputs res3 # >>> OK\n\nres4 = r.json_get('doc', '$.a[?@ empty true]')\np res4 # >>> [[], \"\", {}]\n\nres5 = r.json_get('doc', '$.a[?@ empty false]')\np res5 # >>> [[1], [2, 3], {\"k\"=>1}]","section_id":"filter-examples"},{"id":"filter-examples-ex118","language":"rust","code":"let _: bool = match r.json_set(\"doc\", \"$\", &json!({\"a\":[[4,5],[1],[7,8,9]]})).await {\n            Ok(v) => v,\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        };\n\n        match r.json_get(\"doc\", \"$.a[?@ sizeof 2]\").await {\n            Ok(res16) => {\n                let res16: String = res16;\n                println!(\"{res16}\");    // >>> [[4,5]]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        let _: bool = match r.json_set(\"doc\", \"$\", &json!({\"a\":[[],[1],\"\",[2,3],{},{\"k\":1}]})).await {\n            Ok(v) => v,\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        };\n\n        match r.json_get(\"doc\", \"$.a[?@ empty true]\").await {\n            Ok(res17) => {\n                let res17: String = res17;\n                println!(\"{res17}\");    // >>> [[],\"\",{}]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.a[?@ empty false]\").await {\n            Ok(res18) => {\n                let res18: String = res18;\n                println!(\"{res18}\");    // >>> [[1],[2,3],{\"k\":1}]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }","section_id":"filter-examples"},{"id":"filter-examples-ex119","language":"rust","code":"match r.json_set(\"doc\", \"$\", &json!({\"a\":[[4,5],[1],[7,8,9]]})) {\n            Ok(res23) => {\n                let res23: bool = res23;\n                println!(\"{}\", if res23 { \"OK\" } else { \"(nil)\" });    // >>> OK\n            },\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.a[?@ sizeof 2]\") {\n            Ok(res24) => {\n                let res24: String = res24;\n                println!(\"{res24}\");    // >>> [[4,5]]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_set(\"doc\", \"$\", &json!({\"a\":[[],[1],\"\",[2,3],{},{\"k\":1}]})) {\n            Ok(res25) => {\n                let res25: bool = res25;\n                println!(\"{}\", if res25 { \"OK\" } else { \"(nil)\" });    // >>> OK\n            },\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.a[?@ empty true]\") {\n            Ok(res26) => {\n                let res26: String = res26;\n                println!(\"{res26}\");    // >>> [[],\"\",{}]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.a[?@ empty false]\") {\n            Ok(res27) => {\n                let res27: String = res27;\n                println!(\"{res27}\");    // >>> [[1],[2,3],{\"k\":1}]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }","section_id":"filter-examples"},{"id":"filter-examples-ex120","language":"plaintext","code":"> JSON.SET doc $ '{\"obj\":{\"x\":1,\"y\":2},\"books\":[{\"t\":\"a\"},{\"t\":\"b\"}]}'\nOK\n> JSON.GET doc '$.obj~'\n\"[\\\"x\\\",\\\"y\\\"]\"\n> JSON.GET doc '$~'\n\"[\\\"obj\\\",\\\"books\\\"]\"\n> JSON.GET doc '$.books~'\n\"[]\"","section_id":"filter-examples"},{"id":"filter-examples-ex121","language":"csharp","code":"bool res28 = db.JSON().Set(\n            \"doc\",\n            \"$\",\n            \"{\\\"obj\\\":{\\\"x\\\":1,\\\"y\\\":2},\\\"books\\\":[{\\\"t\\\":\\\"a\\\"},{\\\"t\\\":\\\"b\\\"}]}\"\n        );\n        Console.WriteLine(res28);   // >>> True\n\n        RedisResult res29 = db.JSON().Get(\"doc\", path: \"$.obj~\");\n        Console.WriteLine(res29);   // >>> [\"x\",\"y\"]\n\n        RedisResult res30 = db.JSON().Get(\"doc\", path: \"$~\");\n        Console.WriteLine(res30);   // >>> [\"obj\",\"books\"]\n\n        RedisResult res31 = db.JSON().Get(\"doc\", path: \"$.books~\");\n        Console.WriteLine(res31);   // >>> []","section_id":"filter-examples"},{"id":"filter-examples-ex122","language":"go","code":"res1, err := rdb.JSONSet(ctx, \"doc\", \"$\",\n\t\t`{\"obj\":{\"x\":1,\"y\":2},\"books\":[{\"t\":\"a\"},{\"t\":\"b\"}]}`,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> OK\n\n\tres2, err := rdb.JSONGet(ctx, \"doc\", `$.obj~`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> [\"x\",\"y\"]\n\n\tres3, err := rdb.JSONGet(ctx, \"doc\", `$~`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3) // >>> [\"obj\",\"books\"]\n\n\tres4, err := rdb.JSONGet(ctx, \"doc\", `$.books~`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4) // >>> []","section_id":"filter-examples"},{"id":"filter-examples-ex123","language":"java","code":"CompletableFuture<Void> filterGetkeysExample = asyncCommands\n                    .jsonSet(\"doc\", JsonPath.ROOT_PATH,\n                            parser.createJsonValue(\n                                    \"{\\\"obj\\\":{\\\"x\\\":1,\\\"y\\\":2},\\\"books\\\":[{\\\"t\\\":\\\"a\\\"},{\\\"t\\\":\\\"b\\\"}]}\"))\n                    .thenCompose(res1 -> {\n                        System.out.println(res1); // >>> OK\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$.obj~\"));\n                    }).thenCompose(res2 -> {\n                        System.out.println(res2); // >>> [[\"x\",\"y\"]]\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$~\"));\n                    }).thenCompose(res3 -> {\n                        System.out.println(res3); // >>> [[\"obj\",\"books\"]]\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$.books~\"));\n                    }).thenAccept(res4 -> {\n                        System.out.println(res4); // >>> [[]]\n                    }).toCompletableFuture();","section_id":"filter-examples"},{"id":"filter-examples-ex124","language":"java","code":"Mono<Void> filterGetkeysExample = reactiveCommands\n                    .jsonSet(\"doc\", JsonPath.ROOT_PATH,\n                            parser.createJsonValue(\"{\\\"obj\\\":{\\\"x\\\":1,\\\"y\\\":2},\\\"books\\\":[{\\\"t\\\":\\\"a\\\"},{\\\"t\\\":\\\"b\\\"}]}\"))\n                    .doOnNext(res1 -> {\n                        System.out.println(res1); // >>> OK\n                    })\n                    .flatMap(res1 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$.obj~\")).collectList())\n                    .doOnNext(res2 -> {\n                        System.out.println(res2); // >>> [[\"x\",\"y\"]]\n                    })\n                    .flatMap(res2 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$~\")).collectList())\n                    .doOnNext(res3 -> {\n                        System.out.println(res3); // >>> [[\"obj\",\"books\"]]\n                    })\n                    .flatMap(res3 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$.books~\")).collectList())\n                    .doOnNext(res4 -> {\n                        System.out.println(res4); // >>> [[]]\n                    })\n                    .then();","section_id":"filter-examples"},{"id":"filter-examples-ex125","language":"java","code":"String res28 = jedis.jsonSet(\n            \"doc\",\n            new Path2(\"$\"),\n            \"{\\\"obj\\\":{\\\"x\\\":1,\\\"y\\\":2},\\\"books\\\":[{\\\"t\\\":\\\"a\\\"},{\\\"t\\\":\\\"b\\\"}]}\"\n        );\n        System.out.println(res28);    // >>> OK\n\n        Object res29 = jedis.jsonGet(\"doc\", new Path2(\"$.obj~\"));\n        System.out.println(res29);    // >>> [\"x\",\"y\"]\n\n        Object res30 = jedis.jsonGet(\"doc\", new Path2(\"$~\"));\n        System.out.println(res30);    // >>> [\"obj\",\"books\"]\n\n        Object res31 = jedis.jsonGet(\"doc\", new Path2(\"$.books~\"));\n        System.out.println(res31);    // >>> []","section_id":"filter-examples"},{"id":"filter-examples-ex126","language":"javascript","code":"const res28 = await client.json.set('doc', '$', {\n  obj: { x: 1, y: 2 },\n  books: [{ t: 'a' }, { t: 'b' }]\n});\nconsole.log(res28); // >>> OK\n\nconst res29 = await client.json.get('doc', { path: '$.obj~' });\nconsole.log(res29); // >>> [ 'x', 'y' ]\n\nconst res30 = await client.json.get('doc', { path: '$~' });\nconsole.log(res30); // >>> [ 'obj', 'books' ]\n\nconst res31 = await client.json.get('doc', { path: '$.books~' });\nconsole.log(res31); // >>> []","section_id":"filter-examples"},{"id":"filter-examples-ex127","language":"php","code":"$res28 = $this->redis->jsonset('doc', '$', json_encode([\n            \"obj\" => [\"x\" => 1, \"y\" => 2],\n            \"books\" => [[\"t\" => \"a\"], [\"t\" => \"b\"]],\n        ]));\n        echo $res28 . PHP_EOL; // >>> OK\n\n        $res29 = $this->redis->jsonget('doc', '', '', '', '$.obj~');\n        echo $res29 . PHP_EOL; // >>> [\"x\",\"y\"]\n\n        $res30 = $this->redis->jsonget('doc', '', '', '', '$~');\n        echo $res30 . PHP_EOL; // >>> [\"obj\",\"books\"]\n\n        $res31 = $this->redis->jsonget('doc', '', '', '', '$.books~');\n        echo $res31 . PHP_EOL; // >>> []","section_id":"filter-examples"},{"id":"filter-examples-ex128","language":"python","code":"res1 = r.json().set(\"doc\", \"$\", {\"obj\": {\"x\": 1, \"y\": 2}, \"books\": [{\"t\": \"a\"}, {\"t\": \"b\"}]})\nprint(res1)\n# >>> True\n\nres2 = r.json().get(\"doc\", \"$.obj~\")\nprint(res2)\n# >>> ['x', 'y']\n\nres3 = r.json().get(\"doc\", \"$~\")\nprint(res3)\n# >>> ['obj', 'books']\n\nres4 = r.json().get(\"doc\", \"$.books~\")\nprint(res4)\n# >>> []","section_id":"filter-examples"},{"id":"filter-examples-ex129","language":"ruby","code":"res1 = r.json_set('doc', '$', { 'obj' => { 'x' => 1, 'y' => 2 }, 'books' => [{ 't' => 'a' }, { 't' => 'b' }] })\nputs res1 # >>> OK\n\nres2 = r.json_get('doc', '$.obj~')\np res2 # >>> [\"x\", \"y\"]\n\nres3 = r.json_get('doc', '$~')\np res3 # >>> [\"obj\", \"books\"]\n\nres4 = r.json_get('doc', '$.books~')\np res4 # >>> []","section_id":"filter-examples"},{"id":"filter-examples-ex130","language":"rust","code":"let _: bool = match r.json_set(\n            \"doc\",\n            \"$\",\n            &json!({\"obj\":{\"x\":1,\"y\":2},\"books\":[{\"t\":\"a\"},{\"t\":\"b\"}]}),\n        ).await {\n            Ok(v) => v,\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        };\n\n        match r.json_get(\"doc\", \"$.obj~\").await {\n            Ok(res19) => {\n                let res19: String = res19;\n                println!(\"{res19}\");    // >>> [\"x\",\"y\"]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$~\").await {\n            Ok(res20) => {\n                let res20: String = res20;\n                println!(\"{res20}\");    // >>> [\"obj\",\"books\"]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.books~\").await {\n            Ok(res21) => {\n                let res21: String = res21;\n                println!(\"{res21}\");    // >>> []\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }","section_id":"filter-examples"},{"id":"filter-examples-ex131","language":"rust","code":"match r.json_set(\n            \"doc\",\n            \"$\",\n            &json!({\"obj\":{\"x\":1,\"y\":2},\"books\":[{\"t\":\"a\"},{\"t\":\"b\"}]}),\n        ) {\n            Ok(res28) => {\n                let res28: bool = res28;\n                println!(\"{}\", if res28 { \"OK\" } else { \"(nil)\" });    // >>> OK\n            },\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.obj~\") {\n            Ok(res29) => {\n                let res29: String = res29;\n                println!(\"{res29}\");    // >>> [\"x\",\"y\"]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$~\") {\n            Ok(res30) => {\n                let res30: String = res30;\n                println!(\"{res30}\");    // >>> [\"obj\",\"books\"]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.books~\") {\n            Ok(res31) => {\n                let res31: String = res31;\n                println!(\"{res31}\");    // >>> []\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }","section_id":"filter-examples"},{"id":"filter-examples-ex132","language":"plaintext","code":"> JSON.SET doc $ '{\"a\":[[1,2,3],[1],\"abcd\",\"x\"]}'\nOK\n> JSON.GET doc '$.a[?length(@) > 2]'\n\"[[1,2,3],\\\"abcd\\\"]\"","section_id":"filter-examples"},{"id":"filter-examples-ex133","language":"csharp","code":"bool res32 = db.JSON().Set(\"doc\", \"$\", \"{\\\"a\\\":[[1,2,3],[1],\\\"abcd\\\",\\\"x\\\"]}\");\n        Console.WriteLine(res32);   // >>> True\n\n        RedisResult res33 = db.JSON().Get(\"doc\", path: \"$.a[?length(@) > 2]\");\n        Console.WriteLine(res33);   // >>> [[1,2,3],\"abcd\"]","section_id":"filter-examples"},{"id":"filter-examples-ex134","language":"go","code":"res1, err := rdb.JSONSet(ctx, \"doc\", \"$\",\n\t\t`{\"a\":[[1,2,3],[1],\"abcd\",\"x\"]}`,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> OK\n\n\tres2, err := rdb.JSONGet(ctx, \"doc\", `$.a[?length(@) > 2]`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> [[1,2,3],\"abcd\"]","section_id":"filter-examples"},{"id":"filter-examples-ex135","language":"java","code":"CompletableFuture<Void> funcLengthExample = asyncCommands\n                    .jsonSet(\"doc\", JsonPath.ROOT_PATH,\n                            parser.createJsonValue(\"{\\\"a\\\":[[1,2,3],[1],\\\"abcd\\\",\\\"x\\\"]}\"))\n                    .thenCompose(res1 -> {\n                        System.out.println(res1); // >>> OK\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?length(@) > 2]\"));\n                    }).thenAccept(res2 -> {\n                        System.out.println(res2); // >>> [[[1,2,3],\"abcd\"]]\n                    }).toCompletableFuture();","section_id":"filter-examples"},{"id":"filter-examples-ex136","language":"java","code":"Mono<Void> funcLengthExample = reactiveCommands\n                    .jsonSet(\"doc\", JsonPath.ROOT_PATH,\n                            parser.createJsonValue(\"{\\\"a\\\":[[1,2,3],[1],\\\"abcd\\\",\\\"x\\\"]}\"))\n                    .doOnNext(res1 -> {\n                        System.out.println(res1); // >>> OK\n                    })\n                    .flatMap(res1 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?length(@) > 2]\")).collectList())\n                    .doOnNext(res2 -> {\n                        System.out.println(res2); // >>> [[[1,2,3],\"abcd\"]]\n                    })\n                    .then();","section_id":"filter-examples"},{"id":"filter-examples-ex137","language":"java","code":"String res32 = jedis.jsonSet(\"doc\", new Path2(\"$\"), \"{\\\"a\\\":[[1,2,3],[1],\\\"abcd\\\",\\\"x\\\"]}\");\n        System.out.println(res32);    // >>> OK\n\n        Object res33 = jedis.jsonGet(\"doc\", new Path2(\"$.a[?length(@) > 2]\"));\n        System.out.println(res33);    // >>> [[1,2,3],\"abcd\"]","section_id":"filter-examples"},{"id":"filter-examples-ex138","language":"javascript","code":"const res32 = await client.json.set('doc', '$', { a: [[1, 2, 3], [1], 'abcd', 'x'] });\nconsole.log(res32); // >>> OK\n\nconst res33 = await client.json.get('doc', { path: '$.a[?length(@) > 2]' });\nconsole.log(res33); // >>> [ [ 1, 2, 3 ], 'abcd' ]","section_id":"filter-examples"},{"id":"filter-examples-ex139","language":"php","code":"$res32 = $this->redis->jsonset('doc', '$', json_encode(\n            [\"a\" => [[1, 2, 3], [1], \"abcd\", \"x\"]]\n        ));\n        echo $res32 . PHP_EOL; // >>> OK\n\n        $res33 = $this->redis->jsonget('doc', '', '', '', '$.a[?length(@) > 2]');\n        echo $res33 . PHP_EOL; // >>> [[1,2,3],\"abcd\"]","section_id":"filter-examples"},{"id":"filter-examples-ex140","language":"python","code":"res1 = r.json().set(\"doc\", \"$\", {\"a\": [[1, 2, 3], [1], \"abcd\", \"x\"]})\nprint(res1)\n# >>> True\n\nres2 = r.json().get(\"doc\", \"$.a[?length(@) > 2]\")\nprint(res2)\n# >>> [[1, 2, 3], 'abcd']","section_id":"filter-examples"},{"id":"filter-examples-ex141","language":"ruby","code":"res1 = r.json_set('doc', '$', { 'a' => [[1, 2, 3], [1], 'abcd', 'x'] })\nputs res1 # >>> OK\n\nres2 = r.json_get('doc', '$.a[?length(@) > 2]')\np res2 # >>> [[1, 2, 3], \"abcd\"]","section_id":"filter-examples"},{"id":"filter-examples-ex142","language":"rust","code":"let _: bool = match r.json_set(\"doc\", \"$\", &json!({\"a\":[[1,2,3],[1],\"abcd\",\"x\"]})).await {\n            Ok(v) => v,\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        };\n\n        match r.json_get(\"doc\", \"$.a[?length(@) > 2]\").await {\n            Ok(res1) => {\n                let res1: String = res1;\n                println!(\"{res1}\");    // >>> [[1,2,3],\"abcd\"]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }","section_id":"filter-examples"},{"id":"filter-examples-ex143","language":"rust","code":"match r.json_set(\"doc\", \"$\", &json!({\"a\":[[1,2,3],[1],\"abcd\",\"x\"]})) {\n            Ok(res32) => {\n                let res32: bool = res32;\n                println!(\"{}\", if res32 { \"OK\" } else { \"(nil)\" });    // >>> OK\n            },\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.a[?length(@) > 2]\") {\n            Ok(res33) => {\n                let res33: String = res33;\n                println!(\"{res33}\");    // >>> [[1,2,3],\"abcd\"]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }","section_id":"filter-examples"},{"id":"filter-examples-ex144","language":"plaintext","code":"> JSON.SET doc $ '[{\"a\":1,\"b\":2,\"c\":3},{\"a\":1}]'\nOK\n> JSON.GET doc '$[?count(@.*) == 3]'\n\"[{\\\"a\\\":1,\\\"b\\\":2,\\\"c\\\":3}]\"","section_id":"filter-examples"},{"id":"filter-examples-ex145","language":"csharp","code":"bool res34 = db.JSON().Set(\"doc\", \"$\", \"[{\\\"a\\\":1,\\\"b\\\":2,\\\"c\\\":3},{\\\"a\\\":1}]\");\n        Console.WriteLine(res34);   // >>> True\n\n        RedisResult res35 = db.JSON().Get(\"doc\", path: \"$[?count(@.*) == 3]\");\n        Console.WriteLine(res35);   // >>> [{\"a\":1,\"b\":2,\"c\":3}]","section_id":"filter-examples"},{"id":"filter-examples-ex146","language":"go","code":"res1, err := rdb.JSONSet(ctx, \"doc\", \"$\",\n\t\t`[{\"a\":1,\"b\":2,\"c\":3},{\"a\":1}]`,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> OK\n\n\tres2, err := rdb.JSONGet(ctx, \"doc\", `$[?count(@.*) == 3]`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> [{\"a\":1,\"b\":2,\"c\":3}]","section_id":"filter-examples"},{"id":"filter-examples-ex147","language":"java","code":"CompletableFuture<Void> funcCountExample = asyncCommands\n                    .jsonSet(\"doc\", JsonPath.ROOT_PATH,\n                            parser.createJsonValue(\"[{\\\"a\\\":1,\\\"b\\\":2,\\\"c\\\":3},{\\\"a\\\":1}]\"))\n                    .thenCompose(res1 -> {\n                        System.out.println(res1); // >>> OK\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$[?count(@.*) == 3]\"));\n                    }).thenAccept(res2 -> {\n                        System.out.println(res2); // >>> [[{\"a\":1,\"b\":2,\"c\":3}]]\n                    }).toCompletableFuture();","section_id":"filter-examples"},{"id":"filter-examples-ex148","language":"java","code":"Mono<Void> funcCountExample = reactiveCommands\n                    .jsonSet(\"doc\", JsonPath.ROOT_PATH, parser.createJsonValue(\"[{\\\"a\\\":1,\\\"b\\\":2,\\\"c\\\":3},{\\\"a\\\":1}]\"))\n                    .doOnNext(res1 -> {\n                        System.out.println(res1); // >>> OK\n                    })\n                    .flatMap(res1 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$[?count(@.*) == 3]\")).collectList())\n                    .doOnNext(res2 -> {\n                        System.out.println(res2); // >>> [[{\"a\":1,\"b\":2,\"c\":3}]]\n                    })\n                    .then();","section_id":"filter-examples"},{"id":"filter-examples-ex149","language":"java","code":"String res34 = jedis.jsonSet(\"doc\", new Path2(\"$\"), \"[{\\\"a\\\":1,\\\"b\\\":2,\\\"c\\\":3},{\\\"a\\\":1}]\");\n        System.out.println(res34);    // >>> OK\n\n        Object res35 = jedis.jsonGet(\"doc\", new Path2(\"$[?count(@.*) == 3]\"));\n        System.out.println(res35);    // >>> [{\"a\":1,\"b\":2,\"c\":3}]","section_id":"filter-examples"},{"id":"filter-examples-ex150","language":"javascript","code":"const res34 = await client.json.set('doc', '$', [{ a: 1, b: 2, c: 3 }, { a: 1 }]);\nconsole.log(res34); // >>> OK\n\nconst res35 = await client.json.get('doc', { path: '$[?count(@.*) == 3]' });\nconsole.log(res35); // >>> [ { a: 1, b: 2, c: 3 } ]","section_id":"filter-examples"},{"id":"filter-examples-ex151","language":"php","code":"$res34 = $this->redis->jsonset('doc', '$', json_encode(\n            [[\"a\" => 1, \"b\" => 2, \"c\" => 3], [\"a\" => 1]]\n        ));\n        echo $res34 . PHP_EOL; // >>> OK\n\n        $res35 = $this->redis->jsonget('doc', '', '', '', '$[?count(@.*) == 3]');\n        echo $res35 . PHP_EOL; // >>> [{\"a\":1,\"b\":2,\"c\":3}]","section_id":"filter-examples"},{"id":"filter-examples-ex152","language":"python","code":"res1 = r.json().set(\"doc\", \"$\", [{\"a\": 1, \"b\": 2, \"c\": 3}, {\"a\": 1}])\nprint(res1)\n# >>> True\n\nres2 = r.json().get(\"doc\", \"$[?count(@.*) == 3]\")\nprint(res2)\n# >>> [{'a': 1, 'b': 2, 'c': 3}]","section_id":"filter-examples"},{"id":"filter-examples-ex153","language":"ruby","code":"res1 = r.json_set('doc', '$', [{ 'a' => 1, 'b' => 2, 'c' => 3 }, { 'a' => 1 }])\nputs res1 # >>> OK\n\nres2 = r.json_get('doc', '$[?count(@.*) == 3]')\np res2 # >>> [{\"a\"=>1, \"b\"=>2, \"c\"=>3}]","section_id":"filter-examples"},{"id":"filter-examples-ex154","language":"rust","code":"let _: bool = match r.json_set(\"doc\", \"$\", &json!([{\"a\":1,\"b\":2,\"c\":3},{\"a\":1}])).await {\n            Ok(v) => v,\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        };\n\n        match r.json_get(\"doc\", \"$[?count(@.*) == 3]\").await {\n            Ok(res1) => {\n                let res1: String = res1;\n                println!(\"{res1}\");    // >>> [{\"a\":1,\"b\":2,\"c\":3}]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }","section_id":"filter-examples"},{"id":"filter-examples-ex155","language":"rust","code":"match r.json_set(\"doc\", \"$\", &json!([{\"a\":1,\"b\":2,\"c\":3},{\"a\":1}])) {\n            Ok(res34) => {\n                let res34: bool = res34;\n                println!(\"{}\", if res34 { \"OK\" } else { \"(nil)\" });    // >>> OK\n            },\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$[?count(@.*) == 3]\") {\n            Ok(res35) => {\n                let res35: String = res35;\n                println!(\"{res35}\");    // >>> [{\"a\":1,\"b\":2,\"c\":3}]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }","section_id":"filter-examples"},{"id":"filter-examples-ex156","language":"plaintext","code":"> JSON.SET doc $ '[{\"a\":1},{\"a\":2}]'\nOK\n> JSON.GET doc '$[?value(@.a) == 1]'\n\"[{\\\"a\\\":1}]\"","section_id":"filter-examples"},{"id":"filter-examples-ex157","language":"csharp","code":"bool res36 = db.JSON().Set(\"doc\", \"$\", \"[{\\\"a\\\":1},{\\\"a\\\":2}]\");\n        Console.WriteLine(res36);   // >>> True\n\n        RedisResult res37 = db.JSON().Get(\"doc\", path: \"$[?value(@.a) == 1]\");\n        Console.WriteLine(res37);   // >>> [{\"a\":1}]","section_id":"filter-examples"},{"id":"filter-examples-ex158","language":"go","code":"res1, err := rdb.JSONSet(ctx, \"doc\", \"$\",\n\t\t`[{\"a\":1},{\"a\":2}]`,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> OK\n\n\tres2, err := rdb.JSONGet(ctx, \"doc\", `$[?value(@.a) == 1]`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> [{\"a\":1}]","section_id":"filter-examples"},{"id":"filter-examples-ex159","language":"java","code":"CompletableFuture<Void> funcValueExample = asyncCommands\n                    .jsonSet(\"doc\", JsonPath.ROOT_PATH, parser.createJsonValue(\"[{\\\"a\\\":1},{\\\"a\\\":2}]\"))\n                    .thenCompose(res1 -> {\n                        System.out.println(res1); // >>> OK\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$[?value(@.a) == 1]\"));\n                    }).thenAccept(res2 -> {\n                        System.out.println(res2); // >>> [[{\"a\":1}]]\n                    }).toCompletableFuture();","section_id":"filter-examples"},{"id":"filter-examples-ex160","language":"java","code":"Mono<Void> funcValueExample = reactiveCommands\n                    .jsonSet(\"doc\", JsonPath.ROOT_PATH, parser.createJsonValue(\"[{\\\"a\\\":1},{\\\"a\\\":2}]\"))\n                    .doOnNext(res1 -> {\n                        System.out.println(res1); // >>> OK\n                    })\n                    .flatMap(res1 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$[?value(@.a) == 1]\")).collectList())\n                    .doOnNext(res2 -> {\n                        System.out.println(res2); // >>> [[{\"a\":1}]]\n                    })\n                    .then();","section_id":"filter-examples"},{"id":"filter-examples-ex161","language":"java","code":"String res36 = jedis.jsonSet(\"doc\", new Path2(\"$\"), \"[{\\\"a\\\":1},{\\\"a\\\":2}]\");\n        System.out.println(res36);    // >>> OK\n\n        Object res37 = jedis.jsonGet(\"doc\", new Path2(\"$[?value(@.a) == 1]\"));\n        System.out.println(res37);    // >>> [{\"a\":1}]","section_id":"filter-examples"},{"id":"filter-examples-ex162","language":"javascript","code":"const res36 = await client.json.set('doc', '$', [{ a: 1 }, { a: 2 }]);\nconsole.log(res36); // >>> OK\n\nconst res37 = await client.json.get('doc', { path: '$[?value(@.a) == 1]' });\nconsole.log(res37); // >>> [ { a: 1 } ]","section_id":"filter-examples"},{"id":"filter-examples-ex163","language":"php","code":"$res36 = $this->redis->jsonset('doc', '$', json_encode(\n            [[\"a\" => 1], [\"a\" => 2]]\n        ));\n        echo $res36 . PHP_EOL; // >>> OK\n\n        $res37 = $this->redis->jsonget('doc', '', '', '', '$[?value(@.a) == 1]');\n        echo $res37 . PHP_EOL; // >>> [{\"a\":1}]","section_id":"filter-examples"},{"id":"filter-examples-ex164","language":"python","code":"res1 = r.json().set(\"doc\", \"$\", [{\"a\": 1}, {\"a\": 2}])\nprint(res1)\n# >>> True\n\nres2 = r.json().get(\"doc\", \"$[?value(@.a) == 1]\")\nprint(res2)\n# >>> [{'a': 1}]","section_id":"filter-examples"},{"id":"filter-examples-ex165","language":"ruby","code":"res1 = r.json_set('doc', '$', [{ 'a' => 1 }, { 'a' => 2 }])\nputs res1 # >>> OK\n\nres2 = r.json_get('doc', '$[?value(@.a) == 1]')\np res2 # >>> [{\"a\"=>1}]","section_id":"filter-examples"},{"id":"filter-examples-ex166","language":"rust","code":"let _: bool = match r.json_set(\"doc\", \"$\", &json!([{\"a\":1},{\"a\":2}])).await {\n            Ok(v) => v,\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        };\n\n        match r.json_get(\"doc\", \"$[?value(@.a) == 1]\").await {\n            Ok(res1) => {\n                let res1: String = res1;\n                println!(\"{res1}\");    // >>> [{\"a\":1}]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }","section_id":"filter-examples"},{"id":"filter-examples-ex167","language":"rust","code":"match r.json_set(\"doc\", \"$\", &json!([{\"a\":1},{\"a\":2}])) {\n            Ok(res36) => {\n                let res36: bool = res36;\n                println!(\"{}\", if res36 { \"OK\" } else { \"(nil)\" });    // >>> OK\n            },\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$[?value(@.a) == 1]\") {\n            Ok(res37) => {\n                let res37: String = res37;\n                println!(\"{res37}\");    // >>> [{\"a\":1}]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }","section_id":"filter-examples"},{"id":"filter-examples-ex168","language":"plaintext","code":"> JSON.SET doc $ '{\"obj\":{\"x\":1,\"y\":2}}'\nOK\n> JSON.GET doc '$.obj.keys()'\n\"[\\\"x\\\",\\\"y\\\"]\"\n> JSON.GET doc '$.obj.keys().count()'\n\"[2]\"","section_id":"filter-examples"},{"id":"filter-examples-ex169","language":"csharp","code":"bool res38 = db.JSON().Set(\"doc\", \"$\", \"{\\\"obj\\\":{\\\"x\\\":1,\\\"y\\\":2}}\");\n        Console.WriteLine(res38);   // >>> True\n\n        RedisResult res39 = db.JSON().Get(\"doc\", path: \"$.obj.keys()\");\n        Console.WriteLine(res39);   // >>> [\"x\",\"y\"]\n\n        RedisResult res40 = db.JSON().Get(\"doc\", path: \"$.obj.keys().count()\");\n        Console.WriteLine(res40);   // >>> [2]","section_id":"filter-examples"},{"id":"filter-examples-ex170","language":"go","code":"res1, err := rdb.JSONSet(ctx, \"doc\", \"$\",\n\t\t`{\"obj\":{\"x\":1,\"y\":2}}`,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> OK\n\n\tres2, err := rdb.JSONGet(ctx, \"doc\", `$.obj.keys()`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> [\"x\",\"y\"]\n\n\tres3, err := rdb.JSONGet(ctx, \"doc\", `$.obj.keys().count()`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3) // >>> [2]","section_id":"filter-examples"},{"id":"filter-examples-ex171","language":"java","code":"CompletableFuture<Void> funcKeysExample = asyncCommands\n                    .jsonSet(\"doc\", JsonPath.ROOT_PATH, parser.createJsonValue(\"{\\\"obj\\\":{\\\"x\\\":1,\\\"y\\\":2}}\"))\n                    .thenCompose(res1 -> {\n                        System.out.println(res1); // >>> OK\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$.obj.keys()\"));\n                    }).thenCompose(res2 -> {\n                        System.out.println(res2); // >>> [[\"x\",\"y\"]]\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$.obj.keys().count()\"));\n                    }).thenAccept(res3 -> {\n                        System.out.println(res3); // >>> [[2]]\n                    }).toCompletableFuture();","section_id":"filter-examples"},{"id":"filter-examples-ex172","language":"java","code":"Mono<Void> funcKeysExample = reactiveCommands\n                    .jsonSet(\"doc\", JsonPath.ROOT_PATH, parser.createJsonValue(\"{\\\"obj\\\":{\\\"x\\\":1,\\\"y\\\":2}}\"))\n                    .doOnNext(res1 -> {\n                        System.out.println(res1); // >>> OK\n                    })\n                    .flatMap(res1 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$.obj.keys()\")).collectList())\n                    .doOnNext(res2 -> {\n                        System.out.println(res2); // >>> [[\"x\",\"y\"]]\n                    })\n                    .flatMap(res2 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$.obj.keys().count()\")).collectList())\n                    .doOnNext(res3 -> {\n                        System.out.println(res3); // >>> [[2]]\n                    })\n                    .then();","section_id":"filter-examples"},{"id":"filter-examples-ex173","language":"java","code":"String res38 = jedis.jsonSet(\"doc\", new Path2(\"$\"), \"{\\\"obj\\\":{\\\"x\\\":1,\\\"y\\\":2}}\");\n        System.out.println(res38);    // >>> OK\n\n        Object res39 = jedis.jsonGet(\"doc\", new Path2(\"$.obj.keys()\"));\n        System.out.println(res39);    // >>> [\"x\",\"y\"]\n\n        Object res40 = jedis.jsonGet(\"doc\", new Path2(\"$.obj.keys().count()\"));\n        System.out.println(res40);    // >>> [2]","section_id":"filter-examples"},{"id":"filter-examples-ex174","language":"javascript","code":"const res38 = await client.json.set('doc', '$', { obj: { x: 1, y: 2 } });\nconsole.log(res38); // >>> OK\n\nconst res39 = await client.json.get('doc', { path: '$.obj.keys()' });\nconsole.log(res39); // >>> [ 'x', 'y' ]\n\nconst res40 = await client.json.get('doc', { path: '$.obj.keys().count()' });\nconsole.log(res40); // >>> [ 2 ]","section_id":"filter-examples"},{"id":"filter-examples-ex175","language":"php","code":"$res38 = $this->redis->jsonset('doc', '$', json_encode(\n            [\"obj\" => [\"x\" => 1, \"y\" => 2]]\n        ));\n        echo $res38 . PHP_EOL; // >>> OK\n\n        $res39 = $this->redis->jsonget('doc', '', '', '', '$.obj.keys()');\n        echo $res39 . PHP_EOL; // >>> [\"x\",\"y\"]\n\n        $res40 = $this->redis->jsonget('doc', '', '', '', '$.obj.keys().count()');\n        echo $res40 . PHP_EOL; // >>> [2]","section_id":"filter-examples"},{"id":"filter-examples-ex176","language":"python","code":"res1 = r.json().set(\"doc\", \"$\", {\"obj\": {\"x\": 1, \"y\": 2}})\nprint(res1)\n# >>> True\n\nres2 = r.json().get(\"doc\", \"$.obj.keys()\")\nprint(res2)\n# >>> ['x', 'y']\n\nres3 = r.json().get(\"doc\", \"$.obj.keys().count()\")\nprint(res3)\n# >>> [2]","section_id":"filter-examples"},{"id":"filter-examples-ex177","language":"ruby","code":"res1 = r.json_set('doc', '$', { 'obj' => { 'x' => 1, 'y' => 2 } })\nputs res1 # >>> OK\n\nres2 = r.json_get('doc', '$.obj.keys()')\np res2 # >>> [\"x\", \"y\"]\n\nres3 = r.json_get('doc', '$.obj.keys().count()')\np res3 # >>> [2]","section_id":"filter-examples"},{"id":"filter-examples-ex178","language":"rust","code":"let _: bool = match r.json_set(\"doc\", \"$\", &json!({\"obj\":{\"x\":1,\"y\":2}})).await {\n            Ok(v) => v,\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        };\n\n        match r.json_get(\"doc\", \"$.obj.keys()\").await {\n            Ok(res1) => {\n                let res1: String = res1;\n                println!(\"{res1}\");    // >>> [\"x\",\"y\"]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.obj.keys().count()\").await {\n            Ok(res2) => {\n                let res2: String = res2;\n                println!(\"{res2}\");    // >>> [2]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }","section_id":"filter-examples"},{"id":"filter-examples-ex179","language":"rust","code":"match r.json_set(\"doc\", \"$\", &json!({\"obj\":{\"x\":1,\"y\":2}})) {\n            Ok(res38) => {\n                let res38: bool = res38;\n                println!(\"{}\", if res38 { \"OK\" } else { \"(nil)\" });    // >>> OK\n            },\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.obj.keys()\") {\n            Ok(res39) => {\n                let res39: String = res39;\n                println!(\"{res39}\");    // >>> [\"x\",\"y\"]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.obj.keys().count()\") {\n            Ok(res40) => {\n                let res40: String = res40;\n                println!(\"{res40}\");    // >>> [2]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }","section_id":"filter-examples"},{"id":"filter-examples-ex180","language":"plaintext","code":"> JSON.SET doc $ '{\"a\":[\"abc\",\"xabc\",\"a\",\"b\"]}'\nOK\n> JSON.GET doc '$.a[?match(@, \"a.*\")]'\n\"[\\\"abc\\\",\\\"a\\\"]\"\n> JSON.SET doc $ '{\"a\":[\"abc\",\"xyz\",\"b\"]}'\nOK\n> JSON.GET doc '$.a[?search(@, \"b\")]'\n\"[\\\"abc\\\",\\\"b\\\"]\"","section_id":"filter-examples"},{"id":"filter-examples-ex181","language":"csharp","code":"bool res41 = db.JSON().Set(\"doc\", \"$\", \"{\\\"a\\\":[\\\"abc\\\",\\\"xabc\\\",\\\"a\\\",\\\"b\\\"]}\");\n        Console.WriteLine(res41);   // >>> True\n\n        RedisResult res42 = db.JSON().Get(\"doc\", path: \"$.a[?match(@, \\\"a.*\\\")]\");\n        Console.WriteLine(res42);   // >>> [\"abc\",\"a\"]\n\n        bool res43 = db.JSON().Set(\"doc\", \"$\", \"{\\\"a\\\":[\\\"abc\\\",\\\"xyz\\\",\\\"b\\\"]}\");\n        Console.WriteLine(res43);   // >>> True\n\n        RedisResult res44 = db.JSON().Get(\"doc\", path: \"$.a[?search(@, \\\"b\\\")]\");\n        Console.WriteLine(res44);   // >>> [\"abc\",\"b\"]","section_id":"filter-examples"},{"id":"filter-examples-ex182","language":"go","code":"res1, err := rdb.JSONSet(ctx, \"doc\", \"$\",\n\t\t`{\"a\":[\"abc\",\"xabc\",\"a\",\"b\"]}`,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> OK\n\n\tres2, err := rdb.JSONGet(ctx, \"doc\", `$.a[?match(@, \"a.*\")]`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> [\"abc\",\"a\"]\n\n\tres3, err := rdb.JSONSet(ctx, \"doc\", \"$\",\n\t\t`{\"a\":[\"abc\",\"xyz\",\"b\"]}`,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3) // >>> OK\n\n\tres4, err := rdb.JSONGet(ctx, \"doc\", `$.a[?search(@, \"b\")]`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4) // >>> [\"abc\",\"b\"]","section_id":"filter-examples"},{"id":"filter-examples-ex183","language":"java","code":"CompletableFuture<Void> funcMatchSearchExample = asyncCommands\n                    .jsonSet(\"doc\", JsonPath.ROOT_PATH,\n                            parser.createJsonValue(\"{\\\"a\\\":[\\\"abc\\\",\\\"xabc\\\",\\\"a\\\",\\\"b\\\"]}\"))\n                    .thenCompose(res1 -> {\n                        System.out.println(res1); // >>> OK\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?match(@, \\\"a.*\\\")]\"));\n                    }).thenCompose(res2 -> {\n                        System.out.println(res2); // >>> [[\"abc\",\"a\"]]\n                        return asyncCommands.jsonSet(\"doc\", JsonPath.ROOT_PATH,\n                                parser.createJsonValue(\"{\\\"a\\\":[\\\"abc\\\",\\\"xyz\\\",\\\"b\\\"]}\"));\n                    }).thenCompose(res3 -> {\n                        System.out.println(res3); // >>> OK\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?search(@, \\\"b\\\")]\"));\n                    }).thenAccept(res4 -> {\n                        System.out.println(res4); // >>> [[\"abc\",\"b\"]]\n                    }).toCompletableFuture();","section_id":"filter-examples"},{"id":"filter-examples-ex184","language":"java","code":"Mono<Void> funcMatchSearchExample = reactiveCommands\n                    .jsonSet(\"doc\", JsonPath.ROOT_PATH, parser.createJsonValue(\"{\\\"a\\\":[\\\"abc\\\",\\\"xabc\\\",\\\"a\\\",\\\"b\\\"]}\"))\n                    .doOnNext(res1 -> {\n                        System.out.println(res1); // >>> OK\n                    })\n                    .flatMap(res1 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?match(@, \\\"a.*\\\")]\")).collectList())\n                    .doOnNext(res2 -> {\n                        System.out.println(res2); // >>> [[\"abc\",\"a\"]]\n                    })\n                    .flatMap(res2 -> reactiveCommands.jsonSet(\"doc\", JsonPath.ROOT_PATH,\n                            parser.createJsonValue(\"{\\\"a\\\":[\\\"abc\\\",\\\"xyz\\\",\\\"b\\\"]}\")))\n                    .doOnNext(res3 -> {\n                        System.out.println(res3); // >>> OK\n                    })\n                    .flatMap(res3 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?search(@, \\\"b\\\")]\")).collectList())\n                    .doOnNext(res4 -> {\n                        System.out.println(res4); // >>> [[\"abc\",\"b\"]]\n                    })\n                    .then();","section_id":"filter-examples"},{"id":"filter-examples-ex185","language":"java","code":"String res41 = jedis.jsonSet(\"doc\", new Path2(\"$\"), \"{\\\"a\\\":[\\\"abc\\\",\\\"xabc\\\",\\\"a\\\",\\\"b\\\"]}\");\n        System.out.println(res41);    // >>> OK\n\n        Object res42 = jedis.jsonGet(\"doc\", new Path2(\"$.a[?match(@, \\\"a.*\\\")]\"));\n        System.out.println(res42);    // >>> [\"abc\",\"a\"]\n\n        String res43 = jedis.jsonSet(\"doc\", new Path2(\"$\"), \"{\\\"a\\\":[\\\"abc\\\",\\\"xyz\\\",\\\"b\\\"]}\");\n        System.out.println(res43);    // >>> OK\n\n        Object res44 = jedis.jsonGet(\"doc\", new Path2(\"$.a[?search(@, \\\"b\\\")]\"));\n        System.out.println(res44);    // >>> [\"abc\",\"b\"]","section_id":"filter-examples"},{"id":"filter-examples-ex186","language":"javascript","code":"const res41 = await client.json.set('doc', '$', { a: ['abc', 'xabc', 'a', 'b'] });\nconsole.log(res41); // >>> OK\n\nconst res42 = await client.json.get('doc', { path: '$.a[?match(@, \"a.*\")]' });\nconsole.log(res42); // >>> [ 'abc', 'a' ]\n\nconst res43 = await client.json.set('doc', '$', { a: ['abc', 'xyz', 'b'] });\nconsole.log(res43); // >>> OK\n\nconst res44 = await client.json.get('doc', { path: '$.a[?search(@, \"b\")]' });\nconsole.log(res44); // >>> [ 'abc', 'b' ]","section_id":"filter-examples"},{"id":"filter-examples-ex187","language":"php","code":"$res41 = $this->redis->jsonset('doc', '$', json_encode(\n            [\"a\" => [\"abc\", \"xabc\", \"a\", \"b\"]]\n        ));\n        echo $res41 . PHP_EOL; // >>> OK\n\n        $res42 = $this->redis->jsonget('doc', '', '', '', '$.a[?match(@, \"a.*\")]');\n        echo $res42 . PHP_EOL; // >>> [\"abc\",\"a\"]\n\n        $res43 = $this->redis->jsonset('doc', '$', json_encode(\n            [\"a\" => [\"abc\", \"xyz\", \"b\"]]\n        ));\n        echo $res43 . PHP_EOL; // >>> OK\n\n        $res44 = $this->redis->jsonget('doc', '', '', '', '$.a[?search(@, \"b\")]');\n        echo $res44 . PHP_EOL; // >>> [\"abc\",\"b\"]","section_id":"filter-examples"},{"id":"filter-examples-ex188","language":"python","code":"res1 = r.json().set(\"doc\", \"$\", {\"a\": [\"abc\", \"xabc\", \"a\", \"b\"]})\nprint(res1)\n# >>> True\n\nres2 = r.json().get(\"doc\", '$.a[?match(@, \"a.*\")]')\nprint(res2)\n# >>> ['abc', 'a']\n\nres3 = r.json().set(\"doc\", \"$\", {\"a\": [\"abc\", \"xyz\", \"b\"]})\nprint(res3)\n# >>> True\n\nres4 = r.json().get(\"doc\", '$.a[?search(@, \"b\")]')\nprint(res4)\n# >>> ['abc', 'b']","section_id":"filter-examples"},{"id":"filter-examples-ex189","language":"ruby","code":"res1 = r.json_set('doc', '$', { 'a' => ['abc', 'xabc', 'a', 'b'] })\nputs res1 # >>> OK\n\nres2 = r.json_get('doc', '$.a[?match(@, \"a.*\")]')\np res2 # >>> [\"abc\", \"a\"]\n\nres3 = r.json_set('doc', '$', { 'a' => ['abc', 'xyz', 'b'] })\nputs res3 # >>> OK\n\nres4 = r.json_get('doc', '$.a[?search(@, \"b\")]')\np res4 # >>> [\"abc\", \"b\"]","section_id":"filter-examples"},{"id":"filter-examples-ex190","language":"rust","code":"let _: bool = match r.json_set(\"doc\", \"$\", &json!({\"a\":[\"abc\",\"xabc\",\"a\",\"b\"]})).await {\n            Ok(v) => v,\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        };\n\n        match r.json_get(\"doc\", r#\"$.a[?match(@, \"a.*\")]\"#).await {\n            Ok(res1) => {\n                let res1: String = res1;\n                println!(\"{res1}\");    // >>> [\"abc\",\"a\"]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        let _: bool = match r.json_set(\"doc\", \"$\", &json!({\"a\":[\"abc\",\"xyz\",\"b\"]})).await {\n            Ok(v) => v,\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        };\n\n        match r.json_get(\"doc\", r#\"$.a[?search(@, \"b\")]\"#).await {\n            Ok(res2) => {\n                let res2: String = res2;\n                println!(\"{res2}\");    // >>> [\"abc\",\"b\"]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }","section_id":"filter-examples"},{"id":"filter-examples-ex191","language":"rust","code":"match r.json_set(\"doc\", \"$\", &json!({\"a\":[\"abc\",\"xabc\",\"a\",\"b\"]})) {\n            Ok(res41) => {\n                let res41: bool = res41;\n                println!(\"{}\", if res41 { \"OK\" } else { \"(nil)\" });    // >>> OK\n            },\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", r#\"$.a[?match(@, \"a.*\")]\"#) {\n            Ok(res42) => {\n                let res42: String = res42;\n                println!(\"{res42}\");    // >>> [\"abc\",\"a\"]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_set(\"doc\", \"$\", &json!({\"a\":[\"abc\",\"xyz\",\"b\"]})) {\n            Ok(res43) => {\n                let res43: bool = res43;\n                println!(\"{}\", if res43 { \"OK\" } else { \"(nil)\" });    // >>> OK\n            },\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", r#\"$.a[?search(@, \"b\")]\"#) {\n            Ok(res44) => {\n                let res44: String = res44;\n                println!(\"{res44}\");    // >>> [\"abc\",\"b\"]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }","section_id":"filter-examples"},{"id":"filter-examples-ex192","language":"plaintext","code":"> JSON.SET doc $ '{\"a\":[{\"x\":\"a\",\"y\":\"b\"},{\"x\":\"a\",\"y\":\"c\"}]}'\nOK\n> JSON.GET doc '$.a[?concat(@.x, @.y) == \"ab\"]'\n\"[{\\\"x\\\":\\\"a\\\",\\\"y\\\":\\\"b\\\"}]\"","section_id":"filter-examples"},{"id":"filter-examples-ex193","language":"csharp","code":"bool res45 = db.JSON().Set(\"doc\", \"$\", \"{\\\"a\\\":[{\\\"x\\\":\\\"a\\\",\\\"y\\\":\\\"b\\\"},{\\\"x\\\":\\\"a\\\",\\\"y\\\":\\\"c\\\"}]}\");\n        Console.WriteLine(res45);   // >>> True\n\n        RedisResult res46 = db.JSON().Get(\"doc\", path: \"$.a[?concat(@.x, @.y) == \\\"ab\\\"]\");\n        Console.WriteLine(res46);   // >>> [{\"x\":\"a\",\"y\":\"b\"}]","section_id":"filter-examples"},{"id":"filter-examples-ex194","language":"go","code":"res1, err := rdb.JSONSet(ctx, \"doc\", \"$\",\n\t\t`{\"a\":[{\"x\":\"a\",\"y\":\"b\"},{\"x\":\"a\",\"y\":\"c\"}]}`,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> OK\n\n\tres2, err := rdb.JSONGet(ctx, \"doc\", `$.a[?concat(@.x, @.y) == \"ab\"]`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> [{\"x\":\"a\",\"y\":\"b\"}]","section_id":"filter-examples"},{"id":"filter-examples-ex195","language":"java","code":"CompletableFuture<Void> funcConcatExample = asyncCommands\n                    .jsonSet(\"doc\", JsonPath.ROOT_PATH,\n                            parser.createJsonValue(\"{\\\"a\\\":[{\\\"x\\\":\\\"a\\\",\\\"y\\\":\\\"b\\\"},{\\\"x\\\":\\\"a\\\",\\\"y\\\":\\\"c\\\"}]}\"))\n                    .thenCompose(res1 -> {\n                        System.out.println(res1); // >>> OK\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?concat(@.x, @.y) == \\\"ab\\\"]\"));\n                    }).thenAccept(res2 -> {\n                        System.out.println(res2); // >>> [[{\"x\":\"a\",\"y\":\"b\"}]]\n                    }).toCompletableFuture();","section_id":"filter-examples"},{"id":"filter-examples-ex196","language":"java","code":"Mono<Void> funcConcatExample = reactiveCommands\n                    .jsonSet(\"doc\", JsonPath.ROOT_PATH,\n                            parser.createJsonValue(\"{\\\"a\\\":[{\\\"x\\\":\\\"a\\\",\\\"y\\\":\\\"b\\\"},{\\\"x\\\":\\\"a\\\",\\\"y\\\":\\\"c\\\"}]}\"))\n                    .doOnNext(res1 -> {\n                        System.out.println(res1); // >>> OK\n                    })\n                    .flatMap(res1 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?concat(@.x, @.y) == \\\"ab\\\"]\"))\n                            .collectList())\n                    .doOnNext(res2 -> {\n                        System.out.println(res2); // >>> [[{\"x\":\"a\",\"y\":\"b\"}]]\n                    })\n                    .then();","section_id":"filter-examples"},{"id":"filter-examples-ex197","language":"java","code":"String res45 = jedis.jsonSet(\"doc\", new Path2(\"$\"), \"{\\\"a\\\":[{\\\"x\\\":\\\"a\\\",\\\"y\\\":\\\"b\\\"},{\\\"x\\\":\\\"a\\\",\\\"y\\\":\\\"c\\\"}]}\");\n        System.out.println(res45);    // >>> OK\n\n        Object res46 = jedis.jsonGet(\"doc\", new Path2(\"$.a[?concat(@.x, @.y) == \\\"ab\\\"]\"));\n        System.out.println(res46);    // >>> [{\"x\":\"a\",\"y\":\"b\"}]","section_id":"filter-examples"},{"id":"filter-examples-ex198","language":"javascript","code":"const res45 = await client.json.set('doc', '$', { a: [{ x: 'a', y: 'b' }, { x: 'a', y: 'c' }] });\nconsole.log(res45); // >>> OK\n\nconst res46 = await client.json.get('doc', { path: '$.a[?concat(@.x, @.y) == \"ab\"]' });\nconsole.log(res46); // >>> [ { x: 'a', y: 'b' } ]","section_id":"filter-examples"},{"id":"filter-examples-ex199","language":"php","code":"$res45 = $this->redis->jsonset('doc', '$', json_encode(\n            [\"a\" => [[\"x\" => \"a\", \"y\" => \"b\"], [\"x\" => \"a\", \"y\" => \"c\"]]]\n        ));\n        echo $res45 . PHP_EOL; // >>> OK\n\n        $res46 = $this->redis->jsonget('doc', '', '', '', '$.a[?concat(@.x, @.y) == \"ab\"]');\n        echo $res46 . PHP_EOL; // >>> [{\"x\":\"a\",\"y\":\"b\"}]","section_id":"filter-examples"},{"id":"filter-examples-ex200","language":"python","code":"res1 = r.json().set(\"doc\", \"$\", {\"a\": [{\"x\": \"a\", \"y\": \"b\"}, {\"x\": \"a\", \"y\": \"c\"}]})\nprint(res1)\n# >>> True\n\nres2 = r.json().get(\"doc\", '$.a[?concat(@.x, @.y) == \"ab\"]')\nprint(res2)\n# >>> [{'x': 'a', 'y': 'b'}]","section_id":"filter-examples"},{"id":"filter-examples-ex201","language":"ruby","code":"res1 = r.json_set('doc', '$', { 'a' => [{ 'x' => 'a', 'y' => 'b' }, { 'x' => 'a', 'y' => 'c' }] })\nputs res1 # >>> OK\n\nres2 = r.json_get('doc', '$.a[?concat(@.x, @.y) == \"ab\"]')\np res2 # >>> [{\"x\"=>\"a\", \"y\"=>\"b\"}]","section_id":"filter-examples"},{"id":"filter-examples-ex202","language":"rust","code":"let _: bool = match r.json_set(\n            \"doc\",\n            \"$\",\n            &json!({\"a\":[{\"x\":\"a\",\"y\":\"b\"},{\"x\":\"a\",\"y\":\"c\"}]}),\n        ).await {\n            Ok(v) => v,\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        };\n\n        match r.json_get(\"doc\", r#\"$.a[?concat(@.x, @.y) == \"ab\"]\"#).await {\n            Ok(res1) => {\n                let res1: String = res1;\n                println!(\"{res1}\");    // >>> [{\"x\":\"a\",\"y\":\"b\"}]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }","section_id":"filter-examples"},{"id":"filter-examples-ex203","language":"rust","code":"match r.json_set(\"doc\", \"$\", &json!({\"a\":[{\"x\":\"a\",\"y\":\"b\"},{\"x\":\"a\",\"y\":\"c\"}]})) {\n            Ok(res45) => {\n                let res45: bool = res45;\n                println!(\"{}\", if res45 { \"OK\" } else { \"(nil)\" });    // >>> OK\n            },\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", r#\"$.a[?concat(@.x, @.y) == \"ab\"]\"#) {\n            Ok(res46) => {\n                let res46: String = res46;\n                println!(\"{res46}\");    // >>> [{\"x\":\"a\",\"y\":\"b\"}]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }","section_id":"filter-examples"},{"id":"filter-examples-ex204","language":"plaintext","code":"> JSON.SET doc $ '{\"a\":[2.1,3.9,1.0]}'\nOK\n> JSON.GET doc '$.a[?ceiling(@) == 3]'\n\"[2.1]\"\n> JSON.SET doc $ '{\"a\":[2.1,2.9,3.5]}'\nOK\n> JSON.GET doc '$.a[?floor(@) == 2]'\n\"[2.1,2.9]\"\n> JSON.SET doc $ '{\"a\":[{\"n\":-5},{\"n\":5},{\"n\":-3}]}'\nOK\n> JSON.GET doc '$.a[?abs(@.n) == 5]'\n\"[{\\\"n\\\":-5},{\\\"n\\\":5}]\"","section_id":"filter-examples"},{"id":"filter-examples-ex205","language":"csharp","code":"bool res47 = db.JSON().Set(\"doc\", \"$\", \"{\\\"a\\\":[2.1,3.9,1.0]}\");\n        Console.WriteLine(res47);   // >>> True\n\n        RedisResult res48 = db.JSON().Get(\"doc\", path: \"$.a[?ceiling(@) == 3]\");\n        Console.WriteLine(res48);   // >>> [2.1]\n\n        bool res49 = db.JSON().Set(\"doc\", \"$\", \"{\\\"a\\\":[2.1,2.9,3.5]}\");\n        Console.WriteLine(res49);   // >>> True\n\n        RedisResult res50 = db.JSON().Get(\"doc\", path: \"$.a[?floor(@) == 2]\");\n        Console.WriteLine(res50);   // >>> [2.1,2.9]\n\n        bool res51 = db.JSON().Set(\"doc\", \"$\", \"{\\\"a\\\":[{\\\"n\\\":-5},{\\\"n\\\":5},{\\\"n\\\":-3}]}\");\n        Console.WriteLine(res51);   // >>> True\n\n        RedisResult res52 = db.JSON().Get(\"doc\", path: \"$.a[?abs(@.n) == 5]\");\n        Console.WriteLine(res52);   // >>> [{\"n\":-5},{\"n\":5}]","section_id":"filter-examples"},{"id":"filter-examples-ex206","language":"go","code":"res1, err := rdb.JSONSet(ctx, \"doc\", \"$\",\n\t\t`{\"a\":[2.1,3.9,1.0]}`,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> OK\n\n\tres2, err := rdb.JSONGet(ctx, \"doc\", `$.a[?ceiling(@) == 3]`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> [2.1]\n\n\tres3, err := rdb.JSONSet(ctx, \"doc\", \"$\",\n\t\t`{\"a\":[2.1,2.9,3.5]}`,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3) // >>> OK\n\n\tres4, err := rdb.JSONGet(ctx, \"doc\", `$.a[?floor(@) == 2]`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4) // >>> [2.1,2.9]\n\n\tres5, err := rdb.JSONSet(ctx, \"doc\", \"$\",\n\t\t`{\"a\":[{\"n\":-5},{\"n\":5},{\"n\":-3}]}`,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res5) // >>> OK\n\n\tres6, err := rdb.JSONGet(ctx, \"doc\", `$.a[?abs(@.n) == 5]`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res6) // >>> [{\"n\":-5},{\"n\":5}]","section_id":"filter-examples"},{"id":"filter-examples-ex207","language":"java","code":"CompletableFuture<Void> funcMathExample = asyncCommands\n                    .jsonSet(\"doc\", JsonPath.ROOT_PATH, parser.createJsonValue(\"{\\\"a\\\":[2.1,3.9,1.0]}\"))\n                    .thenCompose(res1 -> {\n                        System.out.println(res1); // >>> OK\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?ceiling(@) == 3]\"));\n                    }).thenCompose(res2 -> {\n                        System.out.println(res2); // >>> [[2.1]]\n                        return asyncCommands.jsonSet(\"doc\", JsonPath.ROOT_PATH,\n                                parser.createJsonValue(\"{\\\"a\\\":[2.1,2.9,3.5]}\"));\n                    }).thenCompose(res3 -> {\n                        System.out.println(res3); // >>> OK\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?floor(@) == 2]\"));\n                    }).thenCompose(res4 -> {\n                        System.out.println(res4); // >>> [[2.1,2.9]]\n                        return asyncCommands.jsonSet(\"doc\", JsonPath.ROOT_PATH,\n                                parser.createJsonValue(\"{\\\"a\\\":[{\\\"n\\\":-5},{\\\"n\\\":5},{\\\"n\\\":-3}]}\"));\n                    }).thenCompose(res5 -> {\n                        System.out.println(res5); // >>> OK\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?abs(@.n) == 5]\"));\n                    }).thenAccept(res6 -> {\n                        System.out.println(res6); // >>> [[{\"n\":-5},{\"n\":5}]]\n                    }).toCompletableFuture();","section_id":"filter-examples"},{"id":"filter-examples-ex208","language":"java","code":"Mono<Void> funcMathExample = reactiveCommands\n                    .jsonSet(\"doc\", JsonPath.ROOT_PATH, parser.createJsonValue(\"{\\\"a\\\":[2.1,3.9,1.0]}\"))\n                    .doOnNext(res1 -> {\n                        System.out.println(res1); // >>> OK\n                    })\n                    .flatMap(res1 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?ceiling(@) == 3]\")).collectList())\n                    .doOnNext(res2 -> {\n                        System.out.println(res2); // >>> [[2.1]]\n                    })\n                    .flatMap(res2 -> reactiveCommands.jsonSet(\"doc\", JsonPath.ROOT_PATH,\n                            parser.createJsonValue(\"{\\\"a\\\":[2.1,2.9,3.5]}\")))\n                    .doOnNext(res3 -> {\n                        System.out.println(res3); // >>> OK\n                    })\n                    .flatMap(res3 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?floor(@) == 2]\")).collectList())\n                    .doOnNext(res4 -> {\n                        System.out.println(res4); // >>> [[2.1,2.9]]\n                    })\n                    .flatMap(res4 -> reactiveCommands.jsonSet(\"doc\", JsonPath.ROOT_PATH,\n                            parser.createJsonValue(\"{\\\"a\\\":[{\\\"n\\\":-5},{\\\"n\\\":5},{\\\"n\\\":-3}]}\")))\n                    .doOnNext(res5 -> {\n                        System.out.println(res5); // >>> OK\n                    })\n                    .flatMap(res5 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?abs(@.n) == 5]\")).collectList())\n                    .doOnNext(res6 -> {\n                        System.out.println(res6); // >>> [[{\"n\":-5},{\"n\":5}]]\n                    })\n                    .then();","section_id":"filter-examples"},{"id":"filter-examples-ex209","language":"java","code":"String res47 = jedis.jsonSet(\"doc\", new Path2(\"$\"), \"{\\\"a\\\":[2.1,3.9,1.0]}\");\n        System.out.println(res47);    // >>> OK\n\n        Object res48 = jedis.jsonGet(\"doc\", new Path2(\"$.a[?ceiling(@) == 3]\"));\n        System.out.println(res48);    // >>> [2.1]\n\n        String res49 = jedis.jsonSet(\"doc\", new Path2(\"$\"), \"{\\\"a\\\":[2.1,2.9,3.5]}\");\n        System.out.println(res49);    // >>> OK\n\n        Object res50 = jedis.jsonGet(\"doc\", new Path2(\"$.a[?floor(@) == 2]\"));\n        System.out.println(res50);    // >>> [2.1,2.9]\n\n        String res51 = jedis.jsonSet(\"doc\", new Path2(\"$\"), \"{\\\"a\\\":[{\\\"n\\\":-5},{\\\"n\\\":5},{\\\"n\\\":-3}]}\");\n        System.out.println(res51);    // >>> OK\n\n        Object res52 = jedis.jsonGet(\"doc\", new Path2(\"$.a[?abs(@.n) == 5]\"));\n        System.out.println(res52);    // >>> [{\"n\":-5},{\"n\":5}]","section_id":"filter-examples"},{"id":"filter-examples-ex210","language":"javascript","code":"const res47 = await client.json.set('doc', '$', { a: [2.1, 3.9, 1.0] });\nconsole.log(res47); // >>> OK\n\nconst res48 = await client.json.get('doc', { path: '$.a[?ceiling(@) == 3]' });\nconsole.log(res48); // >>> [ 2.1 ]\n\nconst res49 = await client.json.set('doc', '$', { a: [2.1, 2.9, 3.5] });\nconsole.log(res49); // >>> OK\n\nconst res50 = await client.json.get('doc', { path: '$.a[?floor(@) == 2]' });\nconsole.log(res50); // >>> [ 2.1, 2.9 ]\n\nconst res51 = await client.json.set('doc', '$', { a: [{ n: -5 }, { n: 5 }, { n: -3 }] });\nconsole.log(res51); // >>> OK\n\nconst res52 = await client.json.get('doc', { path: '$.a[?abs(@.n) == 5]' });\nconsole.log(res52); // >>> [ { n: -5 }, { n: 5 } ]","section_id":"filter-examples"},{"id":"filter-examples-ex211","language":"php","code":"$res47 = $this->redis->jsonset('doc', '$', json_encode(\n            [\"a\" => [2.1, 3.9, 1.0]]\n        ));\n        echo $res47 . PHP_EOL; // >>> OK\n\n        $res48 = $this->redis->jsonget('doc', '', '', '', '$.a[?ceiling(@) == 3]');\n        echo $res48 . PHP_EOL; // >>> [2.1]\n\n        $res49 = $this->redis->jsonset('doc', '$', json_encode(\n            [\"a\" => [2.1, 2.9, 3.5]]\n        ));\n        echo $res49 . PHP_EOL; // >>> OK\n\n        $res50 = $this->redis->jsonget('doc', '', '', '', '$.a[?floor(@) == 2]');\n        echo $res50 . PHP_EOL; // >>> [2.1,2.9]\n\n        $res51 = $this->redis->jsonset('doc', '$', json_encode(\n            [\"a\" => [[\"n\" => -5], [\"n\" => 5], [\"n\" => -3]]]\n        ));\n        echo $res51 . PHP_EOL; // >>> OK\n\n        $res52 = $this->redis->jsonget('doc', '', '', '', '$.a[?abs(@.n) == 5]');\n        echo $res52 . PHP_EOL; // >>> [{\"n\":-5},{\"n\":5}]","section_id":"filter-examples"},{"id":"filter-examples-ex212","language":"python","code":"res1 = r.json().set(\"doc\", \"$\", {\"a\": [2.1, 3.9, 1.0]})\nprint(res1)\n# >>> True\n\nres2 = r.json().get(\"doc\", \"$.a[?ceiling(@) == 3]\")\nprint(res2)\n# >>> [2.1]\n\nres3 = r.json().set(\"doc\", \"$\", {\"a\": [2.1, 2.9, 3.5]})\nprint(res3)\n# >>> True\n\nres4 = r.json().get(\"doc\", \"$.a[?floor(@) == 2]\")\nprint(res4)\n# >>> [2.1, 2.9]\n\nres5 = r.json().set(\"doc\", \"$\", {\"a\": [{\"n\": -5}, {\"n\": 5}, {\"n\": -3}]})\nprint(res5)\n# >>> True\n\nres6 = r.json().get(\"doc\", \"$.a[?abs(@.n) == 5]\")\nprint(res6)\n# >>> [{'n': -5}, {'n': 5}]","section_id":"filter-examples"},{"id":"filter-examples-ex213","language":"ruby","code":"res1 = r.json_set('doc', '$', { 'a' => [2.1, 3.9, 1.0] })\nputs res1 # >>> OK\n\nres2 = r.json_get('doc', '$.a[?ceiling(@) == 3]')\np res2 # >>> [2.1]\n\nres3 = r.json_set('doc', '$', { 'a' => [2.1, 2.9, 3.5] })\nputs res3 # >>> OK\n\nres4 = r.json_get('doc', '$.a[?floor(@) == 2]')\np res4 # >>> [2.1, 2.9]\n\nres5 = r.json_set('doc', '$', { 'a' => [{ 'n' => -5 }, { 'n' => 5 }, { 'n' => -3 }] })\nputs res5 # >>> OK\n\nres6 = r.json_get('doc', '$.a[?abs(@.n) == 5]')\np res6 # >>> [{\"n\"=>-5}, {\"n\"=>5}]","section_id":"filter-examples"},{"id":"filter-examples-ex214","language":"rust","code":"let _: bool = match r.json_set(\"doc\", \"$\", &json!({\"a\":[2.1,3.9,1.0]})).await {\n            Ok(v) => v,\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        };\n\n        match r.json_get(\"doc\", \"$.a[?ceiling(@) == 3]\").await {\n            Ok(res1) => {\n                let res1: String = res1;\n                println!(\"{res1}\");    // >>> [2.1]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        let _: bool = match r.json_set(\"doc\", \"$\", &json!({\"a\":[2.1,2.9,3.5]})).await {\n            Ok(v) => v,\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        };\n\n        match r.json_get(\"doc\", \"$.a[?floor(@) == 2]\").await {\n            Ok(res2) => {\n                let res2: String = res2;\n                println!(\"{res2}\");    // >>> [2.1,2.9]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        let _: bool = match r.json_set(\n            \"doc\",\n            \"$\",\n            &json!({\"a\":[{\"n\":-5},{\"n\":5},{\"n\":-3}]}),\n        ).await {\n            Ok(v) => v,\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        };\n\n        match r.json_get(\"doc\", \"$.a[?abs(@.n) == 5]\").await {\n            Ok(res3) => {\n                let res3: String = res3;\n                println!(\"{res3}\");    // >>> [{\"n\":-5},{\"n\":5}]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }","section_id":"filter-examples"},{"id":"filter-examples-ex215","language":"rust","code":"match r.json_set(\"doc\", \"$\", &json!({\"a\":[2.1,3.9,1.0]})) {\n            Ok(res47) => {\n                let res47: bool = res47;\n                println!(\"{}\", if res47 { \"OK\" } else { \"(nil)\" });    // >>> OK\n            },\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.a[?ceiling(@) == 3]\") {\n            Ok(res48) => {\n                let res48: String = res48;\n                println!(\"{res48}\");    // >>> [2.1]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_set(\"doc\", \"$\", &json!({\"a\":[2.1,2.9,3.5]})) {\n            Ok(res49) => {\n                let res49: bool = res49;\n                println!(\"{}\", if res49 { \"OK\" } else { \"(nil)\" });    // >>> OK\n            },\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.a[?floor(@) == 2]\") {\n            Ok(res50) => {\n                let res50: String = res50;\n                println!(\"{res50}\");    // >>> [2.1,2.9]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_set(\"doc\", \"$\", &json!({\"a\":[{\"n\":-5},{\"n\":5},{\"n\":-3}]})) {\n            Ok(res51) => {\n                let res51: bool = res51;\n                println!(\"{}\", if res51 { \"OK\" } else { \"(nil)\" });    // >>> OK\n            },\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.a[?abs(@.n) == 5]\") {\n            Ok(res52) => {\n                let res52: String = res52;\n                println!(\"{res52}\");    // >>> [{\"n\":-5},{\"n\":5}]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }","section_id":"filter-examples"},{"id":"filter-examples-ex216","language":"plaintext","code":"> JSON.SET doc $ '{\"a\":[{\"n\":[1,2]},{\"n\":[9,8]}]}'\nOK\n> JSON.GET doc '$.a[?first(@.n) == 1]'\n\"[{\\\"n\\\":[1,2]}]\"\n> JSON.GET doc '$.a[?last(@.n) == 8]'\n\"[{\\\"n\\\":[9,8]}]\"\n> JSON.GET doc '$.a[?index(@.n, -1) == 2]'\n\"[{\\\"n\\\":[1,2]}]\"","section_id":"filter-examples"},{"id":"filter-examples-ex217","language":"csharp","code":"bool res53 = db.JSON().Set(\"doc\", \"$\", \"{\\\"a\\\":[{\\\"n\\\":[1,2]},{\\\"n\\\":[9,8]}]}\");\n        Console.WriteLine(res53);   // >>> True\n\n        RedisResult res54 = db.JSON().Get(\"doc\", path: \"$.a[?first(@.n) == 1]\");\n        Console.WriteLine(res54);   // >>> [{\"n\":[1,2]}]\n\n        RedisResult res55 = db.JSON().Get(\"doc\", path: \"$.a[?last(@.n) == 8]\");\n        Console.WriteLine(res55);   // >>> [{\"n\":[9,8]}]\n\n        RedisResult res56 = db.JSON().Get(\"doc\", path: \"$.a[?index(@.n, -1) == 2]\");\n        Console.WriteLine(res56);   // >>> [{\"n\":[1,2]}]","section_id":"filter-examples"},{"id":"filter-examples-ex218","language":"go","code":"res1, err := rdb.JSONSet(ctx, \"doc\", \"$\",\n\t\t`{\"a\":[{\"n\":[1,2]},{\"n\":[9,8]}]}`,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> OK\n\n\tres2, err := rdb.JSONGet(ctx, \"doc\", `$.a[?first(@.n) == 1]`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> [{\"n\":[1,2]}]\n\n\tres3, err := rdb.JSONGet(ctx, \"doc\", `$.a[?last(@.n) == 8]`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3) // >>> [{\"n\":[9,8]}]\n\n\tres4, err := rdb.JSONGet(ctx, \"doc\", `$.a[?index(@.n, -1) == 2]`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4) // >>> [{\"n\":[1,2]}]","section_id":"filter-examples"},{"id":"filter-examples-ex219","language":"java","code":"CompletableFuture<Void> funcArrayAccessExample = asyncCommands\n                    .jsonSet(\"doc\", JsonPath.ROOT_PATH,\n                            parser.createJsonValue(\"{\\\"a\\\":[{\\\"n\\\":[1,2]},{\\\"n\\\":[9,8]}]}\"))\n                    .thenCompose(res1 -> {\n                        System.out.println(res1); // >>> OK\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?first(@.n) == 1]\"));\n                    }).thenCompose(res2 -> {\n                        System.out.println(res2); // >>> [[{\"n\":[1,2]}]]\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?last(@.n) == 8]\"));\n                    }).thenCompose(res3 -> {\n                        System.out.println(res3); // >>> [[{\"n\":[9,8]}]]\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?index(@.n, -1) == 2]\"));\n                    }).thenAccept(res4 -> {\n                        System.out.println(res4); // >>> [[{\"n\":[1,2]}]]\n                    }).toCompletableFuture();","section_id":"filter-examples"},{"id":"filter-examples-ex220","language":"java","code":"Mono<Void> funcArrayAccessExample = reactiveCommands\n                    .jsonSet(\"doc\", JsonPath.ROOT_PATH, parser.createJsonValue(\"{\\\"a\\\":[{\\\"n\\\":[1,2]},{\\\"n\\\":[9,8]}]}\"))\n                    .doOnNext(res1 -> {\n                        System.out.println(res1); // >>> OK\n                    })\n                    .flatMap(res1 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?first(@.n) == 1]\")).collectList())\n                    .doOnNext(res2 -> {\n                        System.out.println(res2); // >>> [[{\"n\":[1,2]}]]\n                    })\n                    .flatMap(res2 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?last(@.n) == 8]\")).collectList())\n                    .doOnNext(res3 -> {\n                        System.out.println(res3); // >>> [[{\"n\":[9,8]}]]\n                    })\n                    .flatMap(res3 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?index(@.n, -1) == 2]\")).collectList())\n                    .doOnNext(res4 -> {\n                        System.out.println(res4); // >>> [[{\"n\":[1,2]}]]\n                    })\n                    .then();","section_id":"filter-examples"},{"id":"filter-examples-ex221","language":"java","code":"String res53 = jedis.jsonSet(\"doc\", new Path2(\"$\"), \"{\\\"a\\\":[{\\\"n\\\":[1,2]},{\\\"n\\\":[9,8]}]}\");\n        System.out.println(res53);    // >>> OK\n\n        Object res54 = jedis.jsonGet(\"doc\", new Path2(\"$.a[?first(@.n) == 1]\"));\n        System.out.println(res54);    // >>> [{\"n\":[1,2]}]\n\n        Object res55 = jedis.jsonGet(\"doc\", new Path2(\"$.a[?last(@.n) == 8]\"));\n        System.out.println(res55);    // >>> [{\"n\":[9,8]}]\n\n        Object res56 = jedis.jsonGet(\"doc\", new Path2(\"$.a[?index(@.n, -1) == 2]\"));\n        System.out.println(res56);    // >>> [{\"n\":[1,2]}]","section_id":"filter-examples"},{"id":"filter-examples-ex222","language":"javascript","code":"const res53 = await client.json.set('doc', '$', { a: [{ n: [1, 2] }, { n: [9, 8] }] });\nconsole.log(res53); // >>> OK\n\nconst res54 = await client.json.get('doc', { path: '$.a[?first(@.n) == 1]' });\nconsole.log(res54); // >>> [ { n: [ 1, 2 ] } ]\n\nconst res55 = await client.json.get('doc', { path: '$.a[?last(@.n) == 8]' });\nconsole.log(res55); // >>> [ { n: [ 9, 8 ] } ]\n\nconst res56 = await client.json.get('doc', { path: '$.a[?index(@.n, -1) == 2]' });\nconsole.log(res56); // >>> [ { n: [ 1, 2 ] } ]","section_id":"filter-examples"},{"id":"filter-examples-ex223","language":"php","code":"$res53 = $this->redis->jsonset('doc', '$', json_encode(\n            [\"a\" => [[\"n\" => [1, 2]], [\"n\" => [9, 8]]]]\n        ));\n        echo $res53 . PHP_EOL; // >>> OK\n\n        $res54 = $this->redis->jsonget('doc', '', '', '', '$.a[?first(@.n) == 1]');\n        echo $res54 . PHP_EOL; // >>> [{\"n\":[1,2]}]\n\n        $res55 = $this->redis->jsonget('doc', '', '', '', '$.a[?last(@.n) == 8]');\n        echo $res55 . PHP_EOL; // >>> [{\"n\":[9,8]}]\n\n        $res56 = $this->redis->jsonget('doc', '', '', '', '$.a[?index(@.n, -1) == 2]');\n        echo $res56 . PHP_EOL; // >>> [{\"n\":[1,2]}]","section_id":"filter-examples"},{"id":"filter-examples-ex224","language":"python","code":"res1 = r.json().set(\"doc\", \"$\", {\"a\": [{\"n\": [1, 2]}, {\"n\": [9, 8]}]})\nprint(res1)\n# >>> True\n\nres2 = r.json().get(\"doc\", \"$.a[?first(@.n) == 1]\")\nprint(res2)\n# >>> [{'n': [1, 2]}]\n\nres3 = r.json().get(\"doc\", \"$.a[?last(@.n) == 8]\")\nprint(res3)\n# >>> [{'n': [9, 8]}]\n\nres4 = r.json().get(\"doc\", \"$.a[?index(@.n, -1) == 2]\")\nprint(res4)\n# >>> [{'n': [1, 2]}]","section_id":"filter-examples"},{"id":"filter-examples-ex225","language":"ruby","code":"res1 = r.json_set('doc', '$', { 'a' => [{ 'n' => [1, 2] }, { 'n' => [9, 8] }] })\nputs res1 # >>> OK\n\nres2 = r.json_get('doc', '$.a[?first(@.n) == 1]')\np res2 # >>> [{\"n\"=>[1, 2]}]\n\nres3 = r.json_get('doc', '$.a[?last(@.n) == 8]')\np res3 # >>> [{\"n\"=>[9, 8]}]\n\nres4 = r.json_get('doc', '$.a[?index(@.n, -1) == 2]')\np res4 # >>> [{\"n\"=>[1, 2]}]","section_id":"filter-examples"},{"id":"filter-examples-ex226","language":"rust","code":"let _: bool = match r.json_set(\n            \"doc\",\n            \"$\",\n            &json!({\"a\":[{\"n\":[1,2]},{\"n\":[9,8]}]}),\n        ).await {\n            Ok(v) => v,\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        };\n\n        match r.json_get(\"doc\", \"$.a[?first(@.n) == 1]\").await {\n            Ok(res1) => {\n                let res1: String = res1;\n                println!(\"{res1}\");    // >>> [{\"n\":[1,2]}]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.a[?last(@.n) == 8]\").await {\n            Ok(res2) => {\n                let res2: String = res2;\n                println!(\"{res2}\");    // >>> [{\"n\":[9,8]}]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.a[?index(@.n, -1) == 2]\").await {\n            Ok(res3) => {\n                let res3: String = res3;\n                println!(\"{res3}\");    // >>> [{\"n\":[1,2]}]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }","section_id":"filter-examples"},{"id":"filter-examples-ex227","language":"rust","code":"match r.json_set(\"doc\", \"$\", &json!({\"a\":[{\"n\":[1,2]},{\"n\":[9,8]}]})) {\n            Ok(res53) => {\n                let res53: bool = res53;\n                println!(\"{}\", if res53 { \"OK\" } else { \"(nil)\" });    // >>> OK\n            },\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.a[?first(@.n) == 1]\") {\n            Ok(res54) => {\n                let res54: String = res54;\n                println!(\"{res54}\");    // >>> [{\"n\":[1,2]}]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.a[?last(@.n) == 8]\") {\n            Ok(res55) => {\n                let res55: String = res55;\n                println!(\"{res55}\");    // >>> [{\"n\":[9,8]}]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.a[?index(@.n, -1) == 2]\") {\n            Ok(res56) => {\n                let res56: String = res56;\n                println!(\"{res56}\");    // >>> [{\"n\":[1,2]}]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }","section_id":"filter-examples"},{"id":"filter-examples-ex228","language":"plaintext","code":"> JSON.SET doc $ '{\"a\":[{\"n\":[3,1,2]},{\"n\":[5,6]}]}'\nOK\n> JSON.GET doc '$.a[?sum(@.n) == 6]'\n\"[{\\\"n\\\":[3,1,2]}]\"\n> JSON.GET doc '$.a[?avg(@.n) == 2]'\n\"[{\\\"n\\\":[3,1,2]}]\"","section_id":"filter-examples"},{"id":"filter-examples-ex229","language":"csharp","code":"bool res57 = db.JSON().Set(\"doc\", \"$\", \"{\\\"a\\\":[{\\\"n\\\":[3,1,2]},{\\\"n\\\":[5,6]}]}\");\n        Console.WriteLine(res57);   // >>> True\n\n        RedisResult res58 = db.JSON().Get(\"doc\", path: \"$.a[?sum(@.n) == 6]\");\n        Console.WriteLine(res58);   // >>> [{\"n\":[3,1,2]}]\n\n        RedisResult res59 = db.JSON().Get(\"doc\", path: \"$.a[?avg(@.n) == 2]\");\n        Console.WriteLine(res59);   // >>> [{\"n\":[3,1,2]}]","section_id":"filter-examples"},{"id":"filter-examples-ex230","language":"go","code":"res1, err := rdb.JSONSet(ctx, \"doc\", \"$\",\n\t\t`{\"a\":[{\"n\":[3,1,2]},{\"n\":[5,6]}]}`,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> OK\n\n\tres2, err := rdb.JSONGet(ctx, \"doc\", `$.a[?sum(@.n) == 6]`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> [{\"n\":[3,1,2]}]\n\n\tres3, err := rdb.JSONGet(ctx, \"doc\", `$.a[?avg(@.n) == 2]`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3) // >>> [{\"n\":[3,1,2]}]","section_id":"filter-examples"},{"id":"filter-examples-ex231","language":"java","code":"CompletableFuture<Void> funcAggregateExample = asyncCommands\n                    .jsonSet(\"doc\", JsonPath.ROOT_PATH,\n                            parser.createJsonValue(\"{\\\"a\\\":[{\\\"n\\\":[3,1,2]},{\\\"n\\\":[5,6]}]}\"))\n                    .thenCompose(res1 -> {\n                        System.out.println(res1); // >>> OK\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?sum(@.n) == 6]\"));\n                    }).thenCompose(res2 -> {\n                        System.out.println(res2); // >>> [[{\"n\":[3,1,2]}]]\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?avg(@.n) == 2]\"));\n                    }).thenAccept(res3 -> {\n                        System.out.println(res3); // >>> [[{\"n\":[3,1,2]}]]\n                    }).toCompletableFuture();","section_id":"filter-examples"},{"id":"filter-examples-ex232","language":"java","code":"Mono<Void> funcAggregateExample = reactiveCommands\n                    .jsonSet(\"doc\", JsonPath.ROOT_PATH,\n                            parser.createJsonValue(\"{\\\"a\\\":[{\\\"n\\\":[3,1,2]},{\\\"n\\\":[5,6]}]}\"))\n                    .doOnNext(res1 -> {\n                        System.out.println(res1); // >>> OK\n                    })\n                    .flatMap(res1 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?sum(@.n) == 6]\")).collectList())\n                    .doOnNext(res2 -> {\n                        System.out.println(res2); // >>> [[{\"n\":[3,1,2]}]]\n                    })\n                    .flatMap(res2 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$.a[?avg(@.n) == 2]\")).collectList())\n                    .doOnNext(res3 -> {\n                        System.out.println(res3); // >>> [[{\"n\":[3,1,2]}]]\n                    })\n                    .then();","section_id":"filter-examples"},{"id":"filter-examples-ex233","language":"java","code":"String res57 = jedis.jsonSet(\"doc\", new Path2(\"$\"), \"{\\\"a\\\":[{\\\"n\\\":[3,1,2]},{\\\"n\\\":[5,6]}]}\");\n        System.out.println(res57);    // >>> OK\n\n        Object res58 = jedis.jsonGet(\"doc\", new Path2(\"$.a[?sum(@.n) == 6]\"));\n        System.out.println(res58);    // >>> [{\"n\":[3,1,2]}]\n\n        Object res59 = jedis.jsonGet(\"doc\", new Path2(\"$.a[?avg(@.n) == 2]\"));\n        System.out.println(res59);    // >>> [{\"n\":[3,1,2]}]","section_id":"filter-examples"},{"id":"filter-examples-ex234","language":"javascript","code":"const res57 = await client.json.set('doc', '$', { a: [{ n: [3, 1, 2] }, { n: [5, 6] }] });\nconsole.log(res57); // >>> OK\n\nconst res58 = await client.json.get('doc', { path: '$.a[?sum(@.n) == 6]' });\nconsole.log(res58); // >>> [ { n: [ 3, 1, 2 ] } ]\n\nconst res59 = await client.json.get('doc', { path: '$.a[?avg(@.n) == 2]' });\nconsole.log(res59); // >>> [ { n: [ 3, 1, 2 ] } ]","section_id":"filter-examples"},{"id":"filter-examples-ex235","language":"php","code":"$res57 = $this->redis->jsonset('doc', '$', json_encode(\n            [\"a\" => [[\"n\" => [3, 1, 2]], [\"n\" => [5, 6]]]]\n        ));\n        echo $res57 . PHP_EOL; // >>> OK\n\n        $res58 = $this->redis->jsonget('doc', '', '', '', '$.a[?sum(@.n) == 6]');\n        echo $res58 . PHP_EOL; // >>> [{\"n\":[3,1,2]}]\n\n        $res59 = $this->redis->jsonget('doc', '', '', '', '$.a[?avg(@.n) == 2]');\n        echo $res59 . PHP_EOL; // >>> [{\"n\":[3,1,2]}]","section_id":"filter-examples"},{"id":"filter-examples-ex236","language":"python","code":"res1 = r.json().set(\"doc\", \"$\", {\"a\": [{\"n\": [3, 1, 2]}, {\"n\": [5, 6]}]})\nprint(res1)\n# >>> True\n\nres2 = r.json().get(\"doc\", \"$.a[?sum(@.n) == 6]\")\nprint(res2)\n# >>> [{'n': [3, 1, 2]}]\n\nres3 = r.json().get(\"doc\", \"$.a[?avg(@.n) == 2]\")\nprint(res3)\n# >>> [{'n': [3, 1, 2]}]","section_id":"filter-examples"},{"id":"filter-examples-ex237","language":"ruby","code":"res1 = r.json_set('doc', '$', { 'a' => [{ 'n' => [3, 1, 2] }, { 'n' => [5, 6] }] })\nputs res1 # >>> OK\n\nres2 = r.json_get('doc', '$.a[?sum(@.n) == 6]')\np res2 # >>> [{\"n\"=>[3, 1, 2]}]\n\nres3 = r.json_get('doc', '$.a[?avg(@.n) == 2]')\np res3 # >>> [{\"n\"=>[3, 1, 2]}]","section_id":"filter-examples"},{"id":"filter-examples-ex238","language":"rust","code":"let _: bool = match r.json_set(\n            \"doc\",\n            \"$\",\n            &json!({\"a\":[{\"n\":[3,1,2]},{\"n\":[5,6]}]}),\n        ).await {\n            Ok(v) => v,\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        };\n\n        match r.json_get(\"doc\", \"$.a[?sum(@.n) == 6]\").await {\n            Ok(res1) => {\n                let res1: String = res1;\n                println!(\"{res1}\");    // >>> [{\"n\":[3,1,2]}]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.a[?avg(@.n) == 2]\").await {\n            Ok(res2) => {\n                let res2: String = res2;\n                println!(\"{res2}\");    // >>> [{\"n\":[3,1,2]}]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }","section_id":"filter-examples"},{"id":"filter-examples-ex239","language":"rust","code":"match r.json_set(\"doc\", \"$\", &json!({\"a\":[{\"n\":[3,1,2]},{\"n\":[5,6]}]})) {\n            Ok(res57) => {\n                let res57: bool = res57;\n                println!(\"{}\", if res57 { \"OK\" } else { \"(nil)\" });    // >>> OK\n            },\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.a[?sum(@.n) == 6]\") {\n            Ok(res58) => {\n                let res58: String = res58;\n                println!(\"{res58}\");    // >>> [{\"n\":[3,1,2]}]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.a[?avg(@.n) == 2]\") {\n            Ok(res59) => {\n                let res59: String = res59;\n                println!(\"{res59}\");    // >>> [{\"n\":[3,1,2]}]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }","section_id":"filter-examples"},{"id":"filter-examples-ex240","language":"plaintext","code":"> JSON.SET doc $ '{\"arr\":[1,2,3]}'\nOK\n> JSON.GET doc '$.arr.append(9)'\n\"[1,2,3,9]\"\n> JSON.SET doc $ '{\"books\":[{\"t\":\"a\",\"price\":30},{\"t\":\"b\",\"price\":5}]}'\nOK\n> JSON.GET doc '$.books[?(@.price >= 10)].append({\"t\":\"X\"})'\n\"[{\\\"t\\\":\\\"a\\\",\\\"price\\\":30},{\\\"t\\\":\\\"X\\\"}]\"","section_id":"filter-examples"},{"id":"filter-examples-ex241","language":"csharp","code":"bool res60 = db.JSON().Set(\"doc\", \"$\", \"{\\\"arr\\\":[1,2,3]}\");\n        Console.WriteLine(res60);   // >>> True\n\n        RedisResult res61 = db.JSON().Get(\"doc\", path: \"$.arr.append(9)\");\n        Console.WriteLine(res61);   // >>> [1,2,3,9]\n\n        bool res62 = db.JSON().Set(\"doc\", \"$\", \"{\\\"books\\\":[{\\\"t\\\":\\\"a\\\",\\\"price\\\":30},{\\\"t\\\":\\\"b\\\",\\\"price\\\":5}]}\");\n        Console.WriteLine(res62);   // >>> True\n\n        RedisResult res63 = db.JSON().Get(\"doc\", path: \"$.books[?(@.price >= 10)].append({\\\"t\\\":\\\"X\\\"})\");\n        Console.WriteLine(res63);   // >>> [{\"t\":\"a\",\"price\":30},{\"t\":\"X\"}]","section_id":"filter-examples"},{"id":"filter-examples-ex242","language":"go","code":"res1, err := rdb.JSONSet(ctx, \"doc\", \"$\",\n\t\t`{\"arr\":[1,2,3]}`,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> OK\n\n\tres2, err := rdb.JSONGet(ctx, \"doc\", `$.arr.append(9)`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> [1,2,3,9]\n\n\tres3, err := rdb.JSONSet(ctx, \"doc\", \"$\",\n\t\t`{\"books\":[{\"t\":\"a\",\"price\":30},{\"t\":\"b\",\"price\":5}]}`,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3) // >>> OK\n\n\tres4, err := rdb.JSONGet(ctx, \"doc\", `$.books[?(@.price >= 10)].append({\"t\":\"X\"})`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4) // >>> [{\"t\":\"a\",\"price\":30},{\"t\":\"X\"}]","section_id":"filter-examples"},{"id":"filter-examples-ex243","language":"java","code":"CompletableFuture<Void> funcAppendExample = asyncCommands\n                    .jsonSet(\"doc\", JsonPath.ROOT_PATH, parser.createJsonValue(\"{\\\"arr\\\":[1,2,3]}\"))\n                    .thenCompose(res1 -> {\n                        System.out.println(res1); // >>> OK\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$.arr.append(9)\"));\n                    }).thenCompose(res2 -> {\n                        System.out.println(res2); // >>> [[1,2,3,9]]\n                        return asyncCommands.jsonSet(\"doc\", JsonPath.ROOT_PATH,\n                                parser.createJsonValue(\n                                        \"{\\\"books\\\":[{\\\"t\\\":\\\"a\\\",\\\"price\\\":30},{\\\"t\\\":\\\"b\\\",\\\"price\\\":5}]}\"));\n                    }).thenCompose(res3 -> {\n                        System.out.println(res3); // >>> OK\n                        return asyncCommands.jsonGet(\"doc\",\n                                JsonPath.of(\"$.books[?(@.price >= 10)].append({\\\"t\\\":\\\"X\\\"})\"));\n                    }).thenAccept(res4 -> {\n                        System.out.println(res4); // >>> [[{\"t\":\"a\",\"price\":30},{\"t\":\"X\"}]]\n                    }).toCompletableFuture();","section_id":"filter-examples"},{"id":"filter-examples-ex244","language":"java","code":"Mono<Void> funcAppendExample = reactiveCommands\n                    .jsonSet(\"doc\", JsonPath.ROOT_PATH, parser.createJsonValue(\"{\\\"arr\\\":[1,2,3]}\"))\n                    .doOnNext(res1 -> {\n                        System.out.println(res1); // >>> OK\n                    })\n                    .flatMap(res1 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$.arr.append(9)\")).collectList())\n                    .doOnNext(res2 -> {\n                        System.out.println(res2); // >>> [[1,2,3,9]]\n                    })\n                    .flatMap(res2 -> reactiveCommands.jsonSet(\"doc\", JsonPath.ROOT_PATH,\n                            parser.createJsonValue(\n                                    \"{\\\"books\\\":[{\\\"t\\\":\\\"a\\\",\\\"price\\\":30},{\\\"t\\\":\\\"b\\\",\\\"price\\\":5}]}\")))\n                    .doOnNext(res3 -> {\n                        System.out.println(res3); // >>> OK\n                    })\n                    .flatMap(res3 -> reactiveCommands\n                            .jsonGet(\"doc\", JsonPath.of(\"$.books[?(@.price >= 10)].append({\\\"t\\\":\\\"X\\\"})\"))\n                            .collectList())\n                    .doOnNext(res4 -> {\n                        System.out.println(res4); // >>> [[{\"t\":\"a\",\"price\":30},{\"t\":\"X\"}]]\n                    })\n                    .then();","section_id":"filter-examples"},{"id":"filter-examples-ex245","language":"java","code":"String res60 = jedis.jsonSet(\"doc\", new Path2(\"$\"), \"{\\\"arr\\\":[1,2,3]}\");\n        System.out.println(res60);    // >>> OK\n\n        Object res61 = jedis.jsonGet(\"doc\", new Path2(\"$.arr.append(9)\"));\n        System.out.println(res61);    // >>> [1,2,3,9]\n\n        String res62 = jedis.jsonSet(\"doc\", new Path2(\"$\"), \"{\\\"books\\\":[{\\\"t\\\":\\\"a\\\",\\\"price\\\":30},{\\\"t\\\":\\\"b\\\",\\\"price\\\":5}]}\");\n        System.out.println(res62);    // >>> OK\n\n        Object res63 = jedis.jsonGet(\"doc\", new Path2(\"$.books[?(@.price >= 10)].append({\\\"t\\\":\\\"X\\\"})\"));\n        System.out.println(res63);    // >>> [{\"t\":\"a\",\"price\":30},{\"t\":\"X\"}]","section_id":"filter-examples"},{"id":"filter-examples-ex246","language":"javascript","code":"const res60 = await client.json.set('doc', '$', { arr: [1, 2, 3] });\nconsole.log(res60); // >>> OK\n\nconst res61 = await client.json.get('doc', { path: '$.arr.append(9)' });\nconsole.log(res61); // >>> [ 1, 2, 3, 9 ]\n\nconst res62 = await client.json.set('doc', '$', {\n  books: [{ t: 'a', price: 30 }, { t: 'b', price: 5 }]\n});\nconsole.log(res62); // >>> OK\n\nconst res63 = await client.json.get('doc', { path: '$.books[?(@.price >= 10)].append({\"t\":\"X\"})' });\nconsole.log(res63); // >>> [ { t: 'a', price: 30 }, { t: 'X' } ]","section_id":"filter-examples"},{"id":"filter-examples-ex247","language":"php","code":"$res60 = $this->redis->jsonset('doc', '$', json_encode(\n            [\"arr\" => [1, 2, 3]]\n        ));\n        echo $res60 . PHP_EOL; // >>> OK\n\n        $res61 = $this->redis->jsonget('doc', '', '', '', '$.arr.append(9)');\n        echo $res61 . PHP_EOL; // >>> [1,2,3,9]\n\n        $res62 = $this->redis->jsonset('doc', '$', json_encode(\n            [\"books\" => [[\"t\" => \"a\", \"price\" => 30], [\"t\" => \"b\", \"price\" => 5]]]\n        ));\n        echo $res62 . PHP_EOL; // >>> OK\n\n        $res63 = $this->redis->jsonget('doc', '', '', '', '$.books[?(@.price >= 10)].append({\"t\":\"X\"})');\n        echo $res63 . PHP_EOL; // >>> [{\"t\":\"a\",\"price\":30},{\"t\":\"X\"}]","section_id":"filter-examples"},{"id":"filter-examples-ex248","language":"python","code":"res1 = r.json().set(\"doc\", \"$\", {\"arr\": [1, 2, 3]})\nprint(res1)\n# >>> True\n\nres2 = r.json().get(\"doc\", \"$.arr.append(9)\")\nprint(res2)\n# >>> [1, 2, 3, 9]\n\nres3 = r.json().set(\"doc\", \"$\", {\"books\": [{\"t\": \"a\", \"price\": 30}, {\"t\": \"b\", \"price\": 5}]})\nprint(res3)\n# >>> True\n\nres4 = r.json().get(\"doc\", '$.books[?(@.price >= 10)].append({\"t\":\"X\"})')\nprint(res4)\n# >>> [{'t': 'a', 'price': 30}, {'t': 'X'}]","section_id":"filter-examples"},{"id":"filter-examples-ex249","language":"ruby","code":"res1 = r.json_set('doc', '$', { 'arr' => [1, 2, 3] })\nputs res1 # >>> OK\n\nres2 = r.json_get('doc', '$.arr.append(9)')\np res2 # >>> [1, 2, 3, 9]\n\nres3 = r.json_set('doc', '$', { 'books' => [{ 't' => 'a', 'price' => 30 }, { 't' => 'b', 'price' => 5 }] })\nputs res3 # >>> OK\n\nres4 = r.json_get('doc', '$.books[?(@.price >= 10)].append({\"t\":\"X\"})')\np res4 # >>> [{\"t\"=>\"a\", \"price\"=>30}, {\"t\"=>\"X\"}]","section_id":"filter-examples"},{"id":"filter-examples-ex250","language":"rust","code":"let _: bool = match r.json_set(\"doc\", \"$\", &json!({\"arr\":[1,2,3]})).await {\n            Ok(v) => v,\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        };\n\n        match r.json_get(\"doc\", \"$.arr.append(9)\").await {\n            Ok(res1) => {\n                let res1: String = res1;\n                println!(\"{res1}\");    // >>> [1,2,3,9]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        let _: bool = match r.json_set(\n            \"doc\",\n            \"$\",\n            &json!({\"books\":[{\"t\":\"a\",\"price\":30},{\"t\":\"b\",\"price\":5}]}),\n        ).await {\n            Ok(v) => v,\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        };\n\n        match r.json_get(\"doc\", r#\"$.books[?(@.price >= 10)].append({\"t\":\"X\"})\"#).await {\n            Ok(res2) => {\n                let res2: String = res2;\n                println!(\"{res2}\");    // >>> [{\"t\":\"a\",\"price\":30},{\"t\":\"X\"}]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }","section_id":"filter-examples"},{"id":"filter-examples-ex251","language":"rust","code":"match r.json_set(\"doc\", \"$\", &json!({\"arr\":[1,2,3]})) {\n            Ok(res60) => {\n                let res60: bool = res60;\n                println!(\"{}\", if res60 { \"OK\" } else { \"(nil)\" });    // >>> OK\n            },\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.arr.append(9)\") {\n            Ok(res61) => {\n                let res61: String = res61;\n                println!(\"{res61}\");    // >>> [1,2,3,9]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_set(\"doc\", \"$\", &json!({\"books\":[{\"t\":\"a\",\"price\":30},{\"t\":\"b\",\"price\":5}]})) {\n            Ok(res62) => {\n                let res62: bool = res62;\n                println!(\"{}\", if res62 { \"OK\" } else { \"(nil)\" });    // >>> OK\n            },\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", r#\"$.books[?(@.price >= 10)].append({\"t\":\"X\"})\"#) {\n            Ok(res63) => {\n                let res63: String = res63;\n                println!(\"{res63}\");    // >>> [{\"t\":\"a\",\"price\":30},{\"t\":\"X\"}]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }","section_id":"filter-examples"},{"id":"update-examples-ex0","language":"plaintext","code":"> JSON.GET bikes:inventory $..price\n\"[1920,2072,3264,1475,3941]\"\n> JSON.NUMINCRBY bikes:inventory $..price -100\n\"[1820,1972,3164,1375,3841]\"\n> JSON.NUMINCRBY bikes:inventory $..price 100\n\"[1920,2072,3264,1475,3941]\"","section_id":"update-examples"},{"id":"update-examples-ex1","language":"csharp","code":"RedisResult res42 = db.JSON().Get(\"bikes:inventory\", path: \"$..price\");\n        Console.WriteLine(res42);   // >>> [1920,2072,3264,1475,3941]\n\n        double?[] res43 = db.JSON().NumIncrby(\"bikes:inventory\", \"$..price\", -100);\n        Console.WriteLine(string.Join(\", \", res43));    // >>> 1820, 1972, 3164, 1375, 3841\n\n        double?[] res44 = db.JSON().NumIncrby(\"bikes:inventory\", \"$..price\", 100);\n        Console.WriteLine(string.Join(\", \", res44));    // >>> 1920, 2072, 3264, 1475, 3941","section_id":"update-examples"},{"id":"update-examples-ex2","language":"go","code":"res15, err := rdb.JSONGet(ctx, \"bikes:inventory\", \"$..price\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res15) // >>> [1475,3941,1920,2072,3264]\n\n\tres16, err := rdb.JSONNumIncrBy(ctx, \"bikes:inventory\", \"$..price\", -100).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res16) // >>> [1375,3841,1820,1972,3164]\n\n\tres17, err := rdb.JSONNumIncrBy(ctx, \"bikes:inventory\", \"$..price\", 100).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res17) // >>> [1475,3941,1920,2072,3264]","section_id":"update-examples"},{"id":"update-examples-ex3","language":"java","code":"CompletableFuture<Void> updateBikes = asyncCommands.jsonGet(\"bikes:inventory\", JsonPath.of(\"$..price\"))\n                    .thenCompose(r -> {\n                        System.out.println(r); // >>> [[1920,2072,3264,1475,3941]]\n\n                        return asyncCommands.jsonNumincrby(\"bikes:inventory\", JsonPath.of(\"$..price\"), -100);\n                    }).thenCompose(res23 -> {\n                        System.out.println(res23); // >>> [1820, 1972, 3164, 1375, 3841]\n\n                        return asyncCommands.jsonNumincrby(\"bikes:inventory\", JsonPath.of(\"$..price\"), 100);\n                    })\n                    .thenAccept(System.out::println)\n                    // >>> [1920, 2072, 3264, 1475, 3941]\n                    .toCompletableFuture();","section_id":"update-examples"},{"id":"update-examples-ex4","language":"java","code":"Mono<Void> updateBikes = reactiveCommands.jsonGet(\"bikes:inventory\", JsonPath.of(\"$..price\")).collectList()\n                    .doOnNext(r -> {\n                        System.out.println(r); // >>> [[1920,2072,3264,1475,3941]]\n                    })\n                    .flatMap(\n                            r -> reactiveCommands.jsonNumincrby(\"bikes:inventory\", JsonPath.of(\"$..price\"), -100).collectList())\n                    .doOnNext(res23 -> {\n                        System.out.println(res23); // >>> [1820, 1972, 3164, 1375, 3841]\n                    })\n                    .flatMap(res23 -> reactiveCommands.jsonNumincrby(\"bikes:inventory\", JsonPath.of(\"$..price\"), 100)\n                            .collectList())\n                    .doOnNext(System.out::println) // >>> [1920, 2072, 3264, 1475, 3941]\n                    .then();","section_id":"update-examples"},{"id":"update-examples-ex5","language":"java","code":"Object res39 = jedis.jsonGet(\"bikes:inventory\", new Path2(\"$..price\"));\n        System.out.println(res39);\n        // >>> [1920,2072,3264,1475,3941]\n\n        Object res40 = jedis.jsonNumIncrBy(\"bikes:inventory\", new Path2(\"$..price\"), -100);\n        System.out.println(res40);  // >>> [1820,1972,3164,1375,3841]\n\n        Object res41 = jedis.jsonNumIncrBy(\"bikes:inventory\", new Path2(\"$..price\"), 100);\n        System.out.println(res41);  // >>> [1920,2072,3264,1475,3941]","section_id":"update-examples"},{"id":"update-examples-ex6","language":"javascript","code":"const res38 = await client.json.get(\"bikes:inventory\", {\n  path: \"$..price\"\n});\nconsole.log(res38);  //  [1920, 2072, 3264, 1475, 3941]\n\nconst res39 = await client.json.numIncrBy(\"bikes:inventory\", \"$..price\", -100);\nconsole.log(res39);  //  [1820, 1972, 3164, 1375, 3841]\n\nconst res40 = await client.json.numIncrBy(\"bikes:inventory\", \"$..price\", 100);\nconsole.log(res40);  //  [1920, 2072, 3264, 1475, 3941]","section_id":"update-examples"},{"id":"update-examples-ex7","language":"php","code":"$res15b = $r->jsonget('bikes:inventory', '', '', '', '$..price');\n        echo $res15b . PHP_EOL;\n        // >>> [1920,2072,3264,1475,3941]\n\n        $res16b = $r->jsonnumincrby('bikes:inventory', '$..price', -100);\n        echo json_encode($res16b) . PHP_EOL;\n        // >>> [1820,1972,3164,1375,3841]\n\n        $res17b = $r->jsonnumincrby('bikes:inventory', '$..price', 100);\n        echo json_encode($res17b) . PHP_EOL;\n        // >>> [1920,2072,3264,1475,3941]","section_id":"update-examples"},{"id":"update-examples-ex8","language":"python","code":"res15 = r.json().get(\"bikes:inventory\", \"$..price\")\nprint(res15)  # >>> [1920, 2072, 3264, 1475, 3941]\n\nres16 = r.json().numincrby(\"bikes:inventory\", \"$..price\", -100)\nprint(res16)  # >>> [1820, 1972, 3164, 1375, 3841]\n\nres17 = r.json().numincrby(\"bikes:inventory\", \"$..price\", 100)\nprint(res17)  # >>> [1920, 2072, 3264, 1475, 3941]","section_id":"update-examples"},{"id":"update-examples-ex9","language":"ruby","code":"res15 = r.json_get('bikes:inventory', '$..price')\np res15 # >>> [1920, 2072, 3264, 1475, 3941]\n\nres16 = r.json_numincrby('bikes:inventory', '$..price', -100)\np res16 # >>> [1820, 1972, 3164, 1375, 3841]\n\nres17 = r.json_numincrby('bikes:inventory', '$..price', 100)\np res17 # >>> [1920, 2072, 3264, 1475, 3941]","section_id":"update-examples"},{"id":"update-examples-ex10","language":"rust","code":"let res40: String = r\n            .json_get(\"bikes:inventory\", \"$..price\")\n            .await\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{res40}\"); // >>> [1920,2072,3264,1475,3941]\n\n        let res41: String = cmd(\"JSON.NUMINCRBY\")\n            .arg(\"bikes:inventory\")\n            .arg(\"$..price\")\n            .arg(-100)\n            .query_async(&mut r)\n            .await\n            .expect(\"Failed to run JSON.NUMINCRBY\");\n        println!(\"{res41}\"); // >>> [1820,1972,3164,1375,3841]\n\n        let res42: String = cmd(\"JSON.NUMINCRBY\")\n            .arg(\"bikes:inventory\")\n            .arg(\"$..price\")\n            .arg(100)\n            .query_async(&mut r)\n            .await\n            .expect(\"Failed to run JSON.NUMINCRBY\");\n        println!(\"{res42}\"); // >>> [1920,2072,3264,1475,3941]","section_id":"update-examples"},{"id":"update-examples-ex11","language":"rust","code":"let res40: String = r\n            .json_get(\"bikes:inventory\", \"$..price\")\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{res40}\"); // >>> [1920,2072,3264,1475,3941]\n\n        let res41: String = cmd(\"JSON.NUMINCRBY\")\n            .arg(\"bikes:inventory\")\n            .arg(\"$..price\")\n            .arg(-100)\n            .query(&mut r)\n            .expect(\"Failed to run JSON.NUMINCRBY\");\n        println!(\"{res41}\"); // >>> [1820,1972,3164,1375,3841]\n\n        let res42: String = cmd(\"JSON.NUMINCRBY\")\n            .arg(\"bikes:inventory\")\n            .arg(\"$..price\")\n            .arg(100)\n            .query(&mut r)\n            .expect(\"Failed to run JSON.NUMINCRBY\");\n        println!(\"{res42}\"); // >>> [1920,2072,3264,1475,3941]","section_id":"update-examples"},{"id":"update-examples-ex12","language":"plaintext","code":"> JSON.SET bikes:inventory '$.inventory.*[?(@.price<2000)].price' 1500\nOK\n> JSON.GET bikes:inventory $..price\n\"[1500,2072,3264,1500,3941]\"","section_id":"update-examples"},{"id":"update-examples-ex13","language":"csharp","code":"bool res45 = db.JSON().Set(\n            \"bikes:inventory\",\n            \"$.inventory.*[?(@.price<2000)].price\",\n            1500\n        );\n        Console.WriteLine(res45);   // >>> True\n\n        RedisResult res46 = db.JSON().Get(\"bikes:inventory\", path: \"$..price\");\n        Console.WriteLine(res46);   // >>> [1500,2072,3264,1500,3941]","section_id":"update-examples"},{"id":"update-examples-ex14","language":"go","code":"res18, err := rdb.JSONSet(ctx,\n\t\t\"bikes:inventory\",\n\t\t\"$.inventory.*[?(@.price<2000)].price\",\n\t\t1500,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res18) // >>> OK\n\n\tres19, err := rdb.JSONGet(ctx, \"bikes:inventory\", \"$..price\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res19) // >>> [1500,3941,1500,2072,3264]","section_id":"update-examples"},{"id":"update-examples-ex15","language":"java","code":"CompletableFuture<Void> updateFilters1 = asyncCommands.jsonSet(\"bikes:inventory\",\n                    JsonPath.of(\"$.inventory.*[?(@.price<2000)].price\"), parser.createJsonValue(\"1500\")).thenCompose(r -> {\n                        System.out.println(r); // >>> OK\n\n                        return asyncCommands.jsonGet(\"bikes:inventory\", JsonPath.of(\"$..price\"));\n                    })\n                    .thenAccept(System.out::println)\n                    // >>> [[1500,2072,3264,1500,3941]]\n                    .toCompletableFuture();","section_id":"update-examples"},{"id":"update-examples-ex16","language":"java","code":"Mono<Void> updateFilters1 = reactiveCommands.jsonSet(\"bikes:inventory\",\n                    JsonPath.of(\"$.inventory.*[?(@.price<2000)].price\"), parser.createJsonValue(\"1500\")).doOnNext(r -> {\n                        System.out.println(r); // >>> OK\n                    }).flatMap(r -> reactiveCommands.jsonGet(\"bikes:inventory\", JsonPath.of(\"$..price\")).collectList())\n                    .doOnNext(System.out::println) // >>> [[1500,2072,3264,1500,3941]]\n                    .then();","section_id":"update-examples"},{"id":"update-examples-ex17","language":"java","code":"jedis.jsonSet(\"bikes:inventory\", new Path2(\"$.inventory.*[?(@.price<2000)].price\"), 1500);\n        Object res42 = jedis.jsonGet(\"bikes:inventory\", new Path2(\"$..price\"));\n        System.out.println(res42);  // >>> [1500,2072,3264,1500,3941]","section_id":"update-examples"},{"id":"update-examples-ex18","language":"javascript","code":"const res40a = await client.json.set(\n  'bikes:inventory', \n  '$.inventory.*[?(@.price<2000)].price', \n  1500\n);\n\n// Get all prices from the inventory\nconst res40b = await client.json.get(\n  'bikes:inventory',\n  { path: \"$..price\" }\n);\nconsole.log(res40b); // [1500, 2072, 3264, 1500, 3941]","section_id":"update-examples"},{"id":"update-examples-ex19","language":"php","code":"$res18b = $r->jsonset('bikes:inventory', '$.inventory.*[?(@.price<2000)].price', '1500');\n        $res19b = $r->jsonget('bikes:inventory', '', '', '', '$..price');\n        echo $res19b . PHP_EOL;\n        // >>> [1500,2072,3264,1500,3941]","section_id":"update-examples"},{"id":"update-examples-ex20","language":"python","code":"res18 = r.json().set(\"bikes:inventory\", \"$.inventory.*[?(@.price<2000)].price\", 1500)\nres19 = r.json().get(\"bikes:inventory\", \"$..price\")\nprint(res19)  # >>> [1500, 2072, 3264, 1500, 3941]","section_id":"update-examples"},{"id":"update-examples-ex21","language":"ruby","code":"res18 = r.json_set('bikes:inventory', '$.inventory.*[?(@.price<2000)].price', 1500)\nres19 = r.json_get('bikes:inventory', '$..price')\np res19 # >>> [1500, 2072, 3264, 1500, 3941]","section_id":"update-examples"},{"id":"update-examples-ex22","language":"rust","code":"let _: bool = r\n            .json_set(\n                \"bikes:inventory\",\n                \"$.inventory.*[?(@.price<2000)].price\",\n                &json!(1500),\n            )\n            .await\n            .expect(\"Failed to run JSON.SET\");\n\n        let res43: String = r\n            .json_get(\"bikes:inventory\", \"$..price\")\n            .await\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{res43}\"); // >>> [1500,2072,3264,1500,3941]","section_id":"update-examples"},{"id":"update-examples-ex23","language":"rust","code":"let _: bool = r\n            .json_set(\n                \"bikes:inventory\",\n                \"$.inventory.*[?(@.price<2000)].price\",\n                &json!(1500),\n            )\n            .expect(\"Failed to run JSON.SET\");\n\n        let res43: String = r\n            .json_get(\"bikes:inventory\", \"$..price\")\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{res43}\"); // >>> [1500,2072,3264,1500,3941]","section_id":"update-examples"},{"id":"update-examples-ex24","language":"plaintext","code":"> JSON.ARRAPPEND bikes:inventory '$.inventory.*[?(@.price<2000)].colors' '\"pink\"'\n1) (integer) 3\n2) (integer) 3\n> JSON.GET bikes:inventory $..[*].colors\n\"[[\\\"black\\\",\\\"silver\\\",\\\"pink\\\"],[\\\"black\\\",\\\"white\\\"],[\\\"black\\\",\\\"silver\\\",\\\"pink\\\"]]\"","section_id":"update-examples"},{"id":"update-examples-ex25","language":"csharp","code":"long?[] res47 = db.JSON().ArrAppend(\n            \"bikes:inventory\", \"$.inventory.*[?(@.price<2000)].colors\", \"pink\"\n        );\n        Console.WriteLine(string.Join(\", \", res47));    // >>> 3, 3\n\n        RedisResult res48 = db.JSON().Get(\"bikes:inventory\", path: \"$..[*].colors\");\n        Console.WriteLine(res48);   // >>> [[\"black\",\"silver\",\"pink\"],[\"black\",\"white\"],[\"black\",\"silver\",\"pink\"]]","section_id":"update-examples"},{"id":"update-examples-ex26","language":"go","code":"res20, err := rdb.JSONArrAppend(ctx,\n\t\t\"bikes:inventory\",\n\t\t\"$.inventory.*[?(@.price<2000)].colors\",\n\t\t\"\\\"pink\\\"\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res20) // >>> [3 3]\n\n\tres21, err := rdb.JSONGet(ctx, \"bikes:inventory\", \"$..[*].colors\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res21)\n\t// >>> [[\"black\",\"silver\",\"pink\"],[\"black\",\"silver\",\"pink\"],[\"black\",\"white\"]]","section_id":"update-examples"},{"id":"update-examples-ex27","language":"java","code":"CompletableFuture<Void> updateFilters2 = asyncCommands.jsonArrappend(\"bikes:inventory\",\n                    JsonPath.of(\"$.inventory.*[?(@.price<2000)].colors\"), parser.createJsonValue(\"\\\"pink\\\"\")).thenCompose(r -> {\n                        System.out.println(r); // >>> [3, 3]\n\n                        return asyncCommands.jsonGet(\"bikes:inventory\", JsonPath.of(\"$..[*].colors\"));\n                    })\n                    .thenAccept(System.out::println)\n                    // >>> [[[\"black\",\"silver\",\"pink\"],[\"black\",\"white\"],[\"black\",\"silver\",\"pink\"]]]\n                    .toCompletableFuture();","section_id":"update-examples"},{"id":"update-examples-ex28","language":"java","code":"Mono<Void> updateFilters2 = reactiveCommands.jsonArrappend(\"bikes:inventory\",\n                    JsonPath.of(\"$.inventory.*[?(@.price<2000)].colors\"), parser.createJsonValue(\"\\\"pink\\\"\")).collectList()\n                    .doOnNext(r -> {\n                        System.out.println(r); // >>> [3, 3]\n                    }).flatMap(r -> reactiveCommands.jsonGet(\"bikes:inventory\", JsonPath.of(\"$..[*].colors\")).collectList())\n                    .doOnNext(System.out::println) // >>>\n                                                   // [[[\"black\",\"silver\",\"pink\"],[\"black\",\"white\"],[\"black\",\"silver\",\"pink\"]]]\n                    .then();","section_id":"update-examples"},{"id":"update-examples-ex29","language":"java","code":"List<Long> res43 = jedis.jsonArrAppendWithEscape(\n            \"bikes:inventory\", new Path2(\"$.inventory.*[?(@.price<2000)].colors\"),\n            \"\\\"pink\\\"\"\n        );\n        System.out.println(res43);  // >>> [3, 3]\n\n        Object res44 = jedis.jsonGet(\"bikes:inventory\", new Path2(\"$..[*].colors\"));\n        System.out.println(res44);\n        // >>> [[\"black\",\"silver\",\"\\\"pink\\\"\"],[\"black\",\"white\"],[\"black\",\"silver\",\"\\\"pink\\\"\"]]","section_id":"update-examples"},{"id":"update-examples-ex30","language":"javascript","code":"const res41 = await client.json.arrAppend(\n    \"bikes:inventory\", \"$.inventory.*[?(@.price<2000)].colors\", \"pink\"\n);\nconsole.log(res41);  //  [3, 3]\n\nconst res42 = await client.json.get(\"bikes:inventory\", {\n  path: \"$..[*].colors\"\n});\nconsole.log(res42);  //  [['black', 'silver', 'pink'], ['black', 'white'], ['black', 'silver', 'pink']]","section_id":"update-examples"},{"id":"update-examples-ex31","language":"php","code":"$res20b = $r->jsonarrappend('bikes:inventory', '$.inventory.*[?(@.price<2000)].colors', '\"pink\"');\n        echo json_encode($res20b) . PHP_EOL;\n        // >>> [3,3]\n\n        $res21b = $r->jsonget('bikes:inventory', '', '', '', '$..[*].colors');\n        echo $res21b . PHP_EOL;\n        // >>> [[\"black\",\"silver\",\"pink\"],[\"black\",\"white\"],[\"black\",\"silver\",\"pink\"]]","section_id":"update-examples"},{"id":"update-examples-ex32","language":"python","code":"res20 = r.json().arrappend(\n    \"bikes:inventory\", \"$.inventory.*[?(@.price<2000)].colors\", \"pink\"\n)\nprint(res20)  # >>> [3, 3]\n\nres21 = r.json().get(\"bikes:inventory\", \"$..[*].colors\")\nprint(\n    res21\n)  # >>> [['black', 'silver', 'pink'], ['black', 'white'], ['black', 'silver', 'pink']]","section_id":"update-examples"},{"id":"update-examples-ex33","language":"ruby","code":"res20 = r.json_arrappend(\n  'bikes:inventory', '$.inventory.*[?(@.price<2000)].colors', 'pink'\n)\np res20 # >>> [3, 3]\n\nres21 = r.json_get('bikes:inventory', '$..[*].colors')\np res21\n# >>> [[\"black\", \"silver\", \"pink\"], [\"black\", \"white\"], [\"black\", \"silver\", \"pink\"]]","section_id":"update-examples"},{"id":"update-examples-ex34","language":"rust","code":"let res44: Value = cmd(\"JSON.ARRAPPEND\")\n            .arg(\"bikes:inventory\")\n            .arg(\"$.inventory.*[?(@.price<2000)].colors\")\n            .arg(\"\\\"pink\\\"\")\n            .query_async(&mut r)\n            .await\n            .expect(\"Failed to run JSON.ARRAPPEND\");\n        print_redis_value(&res44); // >>> [3,3]\n\n        let res45: String = r\n            .json_get(\"bikes:inventory\", \"$..[*].colors\")\n            .await\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{res45}\");\n        // >>> [[\"black\",\"silver\",\"pink\"],[\"black\",\"white\"],[\"black\",\"silver\",\"pink\"]]","section_id":"update-examples"},{"id":"update-examples-ex35","language":"rust","code":"let res44: Value = cmd(\"JSON.ARRAPPEND\")\n            .arg(\"bikes:inventory\")\n            .arg(\"$.inventory.*[?(@.price<2000)].colors\")\n            .arg(\"\\\"pink\\\"\")\n            .query(&mut r)\n            .expect(\"Failed to run JSON.ARRAPPEND\");\n        print_redis_value(&res44); // >>> [3,3]\n\n        let res45: String = r\n            .json_get(\"bikes:inventory\", \"$..[*].colors\")\n            .expect(\"Failed to run JSON.GET\");\n        println!(\"{res45}\");\n        // >>> [[\"black\",\"silver\",\"pink\"],[\"black\",\"white\"],[\"black\",\"silver\",\"pink\"]]","section_id":"update-examples"},{"id":"projection-expressions-ex0","language":"plaintext","code":"> JSON.SET doc $ '{\"a\":2,\"b\":4,\"arr\":[1,2,3]}'\nOK\n> JSON.GET doc '$.a + 1'\n\"[3]\"\n> JSON.GET doc '$.a * $.b'\n\"[8]\"\n> JSON.GET doc '($.a + $.b) / 2'\n\"[3.0]\"\n> JSON.GET doc '$.arr.length()'\n\"[3]\"\n> JSON.GET doc '$.a / 0'\n\"[]\"","section_id":"projection-expressions"},{"id":"projection-expressions-ex1","language":"csharp","code":"bool res64 = db.JSON().Set(\"doc\", \"$\", \"{\\\"a\\\":2,\\\"b\\\":4,\\\"arr\\\":[1,2,3]}\");\n        Console.WriteLine(res64);   // >>> True\n\n        RedisResult res65 = db.JSON().Get(\"doc\", path: \"$.a + 1\");\n        Console.WriteLine(res65);   // >>> [3]\n\n        RedisResult res66 = db.JSON().Get(\"doc\", path: \"$.a * $.b\");\n        Console.WriteLine(res66);   // >>> [8]\n\n        RedisResult res67 = db.JSON().Get(\"doc\", path: \"($.a + $.b) / 2\");\n        Console.WriteLine(res67);   // >>> [3.0]\n\n        RedisResult res68 = db.JSON().Get(\"doc\", path: \"$.arr.length()\");\n        Console.WriteLine(res68);   // >>> [3]\n\n        RedisResult res69 = db.JSON().Get(\"doc\", path: \"$.a / 0\");\n        Console.WriteLine(res69);   // >>> []","section_id":"projection-expressions"},{"id":"projection-expressions-ex2","language":"go","code":"res1, err := rdb.JSONSet(ctx, \"doc\", \"$\",\n\t\t`{\"a\":2,\"b\":4,\"arr\":[1,2,3]}`,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> OK\n\n\tres2, err := rdb.JSONGet(ctx, \"doc\", `$.a + 1`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> [3]\n\n\tres3, err := rdb.JSONGet(ctx, \"doc\", `$.a * $.b`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3) // >>> [8]\n\n\tres4, err := rdb.JSONGet(ctx, \"doc\", `($.a + $.b) / 2`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4) // >>> [3.0]\n\n\tres5, err := rdb.JSONGet(ctx, \"doc\", `$.arr.length()`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res5) // >>> [3]\n\n\tres6, err := rdb.JSONGet(ctx, \"doc\", `$.a / 0`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res6) // >>> []","section_id":"projection-expressions"},{"id":"projection-expressions-ex3","language":"java","code":"CompletableFuture<Void> projBasicExample = asyncCommands\n                    .jsonSet(\"doc\", JsonPath.ROOT_PATH,\n                            parser.createJsonValue(\"{\\\"a\\\":2,\\\"b\\\":4,\\\"arr\\\":[1,2,3]}\"))\n                    .thenCompose(res1 -> {\n                        System.out.println(res1); // >>> OK\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$.a + 1\"));\n                    }).thenCompose(res2 -> {\n                        System.out.println(res2); // >>> [[3]]\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$.a * $.b\"));\n                    }).thenCompose(res3 -> {\n                        System.out.println(res3); // >>> [[8]]\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"($.a + $.b) / 2\"));\n                    }).thenCompose(res4 -> {\n                        System.out.println(res4); // >>> [[3.0]]\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$.arr.length()\"));\n                    }).thenCompose(res5 -> {\n                        System.out.println(res5); // >>> [[3]]\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$.a / 0\"));\n                    }).thenAccept(res6 -> {\n                        System.out.println(res6); // >>> [[]]\n                    }).toCompletableFuture();","section_id":"projection-expressions"},{"id":"projection-expressions-ex4","language":"java","code":"Mono<Void> projBasicExample = reactiveCommands\n                    .jsonSet(\"doc\", JsonPath.ROOT_PATH, parser.createJsonValue(\"{\\\"a\\\":2,\\\"b\\\":4,\\\"arr\\\":[1,2,3]}\"))\n                    .doOnNext(res1 -> {\n                        System.out.println(res1); // >>> OK\n                    })\n                    .flatMap(res1 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$.a + 1\")).collectList())\n                    .doOnNext(res2 -> {\n                        System.out.println(res2); // >>> [[3]]\n                    })\n                    .flatMap(res2 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$.a * $.b\")).collectList())\n                    .doOnNext(res3 -> {\n                        System.out.println(res3); // >>> [[8]]\n                    })\n                    .flatMap(res3 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"($.a + $.b) / 2\")).collectList())\n                    .doOnNext(res4 -> {\n                        System.out.println(res4); // >>> [[3.0]]\n                    })\n                    .flatMap(res4 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$.arr.length()\")).collectList())\n                    .doOnNext(res5 -> {\n                        System.out.println(res5); // >>> [[3]]\n                    })\n                    .flatMap(res5 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$.a / 0\")).collectList())\n                    .doOnNext(res6 -> {\n                        System.out.println(res6); // >>> [[]]\n                    })\n                    .then();","section_id":"projection-expressions"},{"id":"projection-expressions-ex5","language":"java","code":"String res64 = jedis.jsonSet(\"doc\", new Path2(\"$\"), \"{\\\"a\\\":2,\\\"b\\\":4,\\\"arr\\\":[1,2,3]}\");\n        System.out.println(res64);    // >>> OK\n\n        Object res65 = jedis.jsonGet(\"doc\", new Path2(\"$.a + 1\"));\n        System.out.println(res65);    // >>> [3]\n\n        Object res66 = jedis.jsonGet(\"doc\", new Path2(\"$.a * $.b\"));\n        System.out.println(res66);    // >>> [8]\n\n        Object res67 = jedis.jsonGet(\"doc\", new Path(\"($.a + $.b) / 2\"));\n        System.out.println(res67);    // >>> [3.0]\n\n        Object res68 = jedis.jsonGet(\"doc\", new Path2(\"$.arr.length()\"));\n        System.out.println(res68);    // >>> [3]\n\n        Object res69 = jedis.jsonGet(\"doc\", new Path2(\"$.a / 0\"));\n        System.out.println(res69);    // >>> []","section_id":"projection-expressions"},{"id":"projection-expressions-ex6","language":"javascript","code":"const res64 = await client.json.set('doc', '$', { a: 2, b: 4, arr: [1, 2, 3] });\nconsole.log(res64); // >>> OK\n\nconst res65 = await client.json.get('doc', { path: '$.a + 1' });\nconsole.log(res65); // >>> [ 3 ]\n\nconst res66 = await client.json.get('doc', { path: '$.a * $.b' });\nconsole.log(res66); // >>> [ 8 ]\n\nconst res67 = await client.json.get('doc', { path: '($.a + $.b) / 2' });\nconsole.log(res67); // >>> [ 3 ]\n\nconst res68 = await client.json.get('doc', { path: '$.arr.length()' });\nconsole.log(res68); // >>> [ 3 ]\n\nconst res69 = await client.json.get('doc', { path: '$.a / 0' });\nconsole.log(res69); // >>> []","section_id":"projection-expressions"},{"id":"projection-expressions-ex7","language":"php","code":"$res64 = $this->redis->jsonset('doc', '$', json_encode(\n            [\"a\" => 2, \"b\" => 4, \"arr\" => [1, 2, 3]]\n        ));\n        echo $res64 . PHP_EOL; // >>> OK\n\n        $res65 = $this->redis->jsonget('doc', '', '', '', '$.a + 1');\n        echo $res65 . PHP_EOL; // >>> [3]\n\n        $res66 = $this->redis->jsonget('doc', '', '', '', '$.a * $.b');\n        echo $res66 . PHP_EOL; // >>> [8]\n\n        $res67 = $this->redis->jsonget('doc', '', '', '', '($.a + $.b) / 2');\n        echo $res67 . PHP_EOL; // >>> [3.0]\n\n        $res68 = $this->redis->jsonget('doc', '', '', '', '$.arr.length()');\n        echo $res68 . PHP_EOL; // >>> [3]\n\n        $res69 = $this->redis->jsonget('doc', '', '', '', '$.a / 0');\n        echo $res69 . PHP_EOL; // >>> []","section_id":"projection-expressions"},{"id":"projection-expressions-ex8","language":"python","code":"res1 = r.json().set(\"doc\", \"$\", {\"a\": 2, \"b\": 4, \"arr\": [1, 2, 3]})\nprint(res1)\n# >>> True\n\nres2 = r.json().get(\"doc\", \"$.a + 1\")\nprint(res2)\n# >>> [3]\n\nres3 = r.json().get(\"doc\", \"$.a * $.b\")\nprint(res3)\n# >>> [8]\n\nres4 = r.json().get(\"doc\", \"($.a + $.b) / 2\")\nprint(res4)\n# >>> [3.0]\n\nres5 = r.json().get(\"doc\", \"$.arr.length()\")\nprint(res5)\n# >>> [3]\n\nres6 = r.json().get(\"doc\", \"$.a / 0\")\nprint(res6)\n# >>> []","section_id":"projection-expressions"},{"id":"projection-expressions-ex9","language":"ruby","code":"res1 = r.json_set('doc', '$', { 'a' => 2, 'b' => 4, 'arr' => [1, 2, 3] })\nputs res1 # >>> OK\n\nres2 = r.json_get('doc', '$.a + 1')\np res2 # >>> [3]\n\nres3 = r.json_get('doc', '$.a * $.b')\np res3 # >>> [8]\n\nres4 = r.json_get('doc', '($.a + $.b) / 2')\np res4 # >>> [3.0]\n\nres5 = r.json_get('doc', '$.arr.length()')\np res5 # >>> [3]\n\nres6 = r.json_get('doc', '$.a / 0')\np res6 # >>> []","section_id":"projection-expressions"},{"id":"projection-expressions-ex10","language":"rust","code":"let _: bool = match r.json_set(\"doc\", \"$\", &json!({\"a\":2,\"b\":4,\"arr\":[1,2,3]})).await {\n            Ok(v) => v,\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        };\n\n        match r.json_get(\"doc\", \"$.a + 1\").await {\n            Ok(res1) => {\n                let res1: String = res1;\n                println!(\"{res1}\");    // >>> [3]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.a * $.b\").await {\n            Ok(res2) => {\n                let res2: String = res2;\n                println!(\"{res2}\");    // >>> [8]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"($.a + $.b) / 2\").await {\n            Ok(res3) => {\n                let res3: String = res3;\n                println!(\"{res3}\");    // >>> [3.0]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.arr.length()\").await {\n            Ok(res4) => {\n                let res4: String = res4;\n                println!(\"{res4}\");    // >>> [3]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.a / 0\").await {\n            Ok(res5) => {\n                let res5: String = res5;\n                println!(\"{res5}\");    // >>> []\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }","section_id":"projection-expressions"},{"id":"projection-expressions-ex11","language":"rust","code":"match r.json_set(\"doc\", \"$\", &json!({\"a\":2,\"b\":4,\"arr\":[1,2,3]})) {\n            Ok(res64) => {\n                let res64: bool = res64;\n                println!(\"{}\", if res64 { \"OK\" } else { \"(nil)\" });    // >>> OK\n            },\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.a + 1\") {\n            Ok(res65) => {\n                let res65: String = res65;\n                println!(\"{res65}\");    // >>> [3]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.a * $.b\") {\n            Ok(res66) => {\n                let res66: String = res66;\n                println!(\"{res66}\");    // >>> [8]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"($.a + $.b) / 2\") {\n            Ok(res67) => {\n                let res67: String = res67;\n                println!(\"{res67}\");    // >>> [3.0]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.arr.length()\") {\n            Ok(res68) => {\n                let res68: String = res68;\n                println!(\"{res68}\");    // >>> [3]\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }\n\n        match r.json_get(\"doc\", \"$.a / 0\") {\n            Ok(res69) => {\n                let res69: String = res69;\n                println!(\"{res69}\");    // >>> []\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }","section_id":"projection-expressions"},{"id":"projection-expressions-ex12","language":"plaintext","code":"> JSON.GET doc '$.a + 1' '$.b'\n\"{\\\"$.a + 1\\\":[3],\\\"$.b\\\":[4]}\"","section_id":"projection-expressions"},{"id":"projection-expressions-ex13","language":"csharp","code":"bool res70 = db.JSON().Set(\"doc\", \"$\", \"{\\\"a\\\":2,\\\"b\\\":4,\\\"arr\\\":[1,2,3]}\");\n        Console.WriteLine(res70);   // >>> True\n\n        RedisResult res71 = db.JSON().Get(\"doc\", paths: new[] { \"$.a + 1\", \"$.b\" });\n        Console.WriteLine(res71);   // >>> {\"$.a + 1\":[3],\"$.b\":[4]}","section_id":"projection-expressions"},{"id":"projection-expressions-ex14","language":"go","code":"res1, err := rdb.JSONSet(ctx, \"doc\", \"$\",\n\t\t`{\"a\":2,\"b\":4,\"arr\":[1,2,3]}`,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> OK\n\n\traw, err := rdb.JSONGet(ctx, \"doc\", `$.a + 1`, `$.b`).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// A multi-path JSON.GET reply is a JSON object whose key order is not\n\t// guaranteed, so decode it into a map rather than printing the raw\n\t// string. Go's fmt package prints map keys in sorted order, which keeps\n\t// this example's output deterministic regardless of the order Redis\n\t// returns the paths in.\n\tres2 := map[string]interface{}{}\n\n\tif err := json.Unmarshal([]byte(raw), &res2); err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> map[$.a + 1:[3] $.b:[4]]","section_id":"projection-expressions"},{"id":"projection-expressions-ex15","language":"java","code":"CompletableFuture<Void> projMultipathExample = asyncCommands\n                    .jsonSet(\"doc\", JsonPath.ROOT_PATH,\n                            parser.createJsonValue(\"{\\\"a\\\":2,\\\"b\\\":4,\\\"arr\\\":[1,2,3]}\"))\n                    .thenCompose(res7 -> {\n                        System.out.println(res7); // >>> OK\n                        return asyncCommands.jsonGet(\"doc\", JsonPath.of(\"$.a + 1\"), JsonPath.of(\"$.b\"));\n                    }).thenAccept(res8 -> {\n                        // The multi-path JSON.GET reply is a single JSON object keyed by path,\n                        // and Redis does not guarantee key order, so look up each path rather\n                        // than comparing the object's rendered string.\n                        JsonObject paths = res8.get(0).asJsonObject();\n                        System.out.println(paths); // >>> {\"$.a + 1\":[3],\"$.b\":[4]}\n                    }).toCompletableFuture();","section_id":"projection-expressions"},{"id":"projection-expressions-ex16","language":"java","code":"Mono<Void> projMultipathExample = reactiveCommands\n                    .jsonSet(\"doc\", JsonPath.ROOT_PATH, parser.createJsonValue(\"{\\\"a\\\":2,\\\"b\\\":4,\\\"arr\\\":[1,2,3]}\"))\n                    .doOnNext(res7 -> {\n                        System.out.println(res7); // >>> OK\n                    })\n                    .flatMap(res7 -> reactiveCommands.jsonGet(\"doc\", JsonPath.of(\"$.a + 1\"), JsonPath.of(\"$.b\"))\n                            .collectList())\n                    .doOnNext(res8 -> {\n                        System.out.println(res8); // >>> [{\"$.a + 1\":[3],\"$.b\":[4]}]\n                    })\n                    .then();","section_id":"projection-expressions"},{"id":"projection-expressions-ex17","language":"java","code":"String res70 = jedis.jsonSet(\"doc\", new Path2(\"$\"), \"{\\\"a\\\":2,\\\"b\\\":4,\\\"arr\\\":[1,2,3]}\");\n        System.out.println(res70);    // >>> OK\n\n        Object res71 = jedis.jsonGet(\"doc\", new Path2(\"$.a + 1\"), new Path2(\"$.b\"));\n        System.out.println(res71);    // >>> {\"$.a + 1\":[3],\"$.b\":[4]} (a JSON object; key order is not guaranteed)","section_id":"projection-expressions"},{"id":"projection-expressions-ex18","language":"javascript","code":"const res70 = await client.json.set('doc', '$', { a: 2, b: 4, arr: [1, 2, 3] });\nconsole.log(res70); // >>> OK\n\nconst res71 = await client.json.get('doc', { path: ['$.a + 1', '$.b'] });\nconsole.log(res71); // >>> { '$.a + 1': [ 3 ], '$.b': [ 4 ] }","section_id":"projection-expressions"},{"id":"projection-expressions-ex19","language":"php","code":"$res70 = $this->redis->jsonset('doc', '$', json_encode(\n            [\"a\" => 2, \"b\" => 4, \"arr\" => [1, 2, 3]]\n        ));\n        echo $res70 . PHP_EOL; // >>> OK\n\n        $res71 = $this->redis->jsonget('doc', '', '', '', '$.a + 1', '$.b');\n        // The reply is a JSON object keyed by path; key order is not guaranteed.\n        echo $res71 . PHP_EOL; // >>> {\"$.a + 1\":[3],\"$.b\":[4]} (key order not guaranteed)","section_id":"projection-expressions"},{"id":"projection-expressions-ex20","language":"python","code":"res7 = r.json().set(\"doc\", \"$\", {\"a\": 2, \"b\": 4, \"arr\": [1, 2, 3]})\nprint(res7)\n# >>> True\n\nres8 = r.json().get(\"doc\", \"$.a + 1\", \"$.b\")\nprint(res8)\n# >>> {'$.a + 1': [3], '$.b': [4]}","section_id":"projection-expressions"},{"id":"projection-expressions-ex21","language":"ruby","code":"res7 = r.json_set('doc', '$', { 'a' => 2, 'b' => 4, 'arr' => [1, 2, 3] })\nputs res7 # >>> OK\n\n# The reply's key order is not guaranteed, so don't rely on it.\nres8 = r.json_get('doc', '$.a + 1', '$.b')\np res8","section_id":"projection-expressions"},{"id":"projection-expressions-ex22","language":"rust","code":"let _: bool = match r.json_set(\"doc\", \"$\", &json!({\"a\":2,\"b\":4,\"arr\":[1,2,3]})).await {\n            Ok(v) => v,\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        };\n\n        match r.json_get(\"doc\", &[\"$.a + 1\", \"$.b\"]).await {\n            Ok(res6) => {\n                let res6: String = res6;\n                // The reply's key order is not guaranteed, so parse it into a\n                // Value and compare that structurally rather than relying on\n                // the raw string's key order.\n                println!(\"{res6}\");\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }","section_id":"projection-expressions"},{"id":"projection-expressions-ex23","language":"rust","code":"match r.json_set(\"doc\", \"$\", &json!({\"a\":2,\"b\":4,\"arr\":[1,2,3]})) {\n            Ok(res70) => {\n                let res70: bool = res70;\n                println!(\"{}\", if res70 { \"OK\" } else { \"(nil)\" });    // >>> OK\n            },\n            Err(e) => {\n                println!(\"Error setting doc: {e}\");\n                return;\n            }\n        }\n\n        // A multi-path JSON.GET replies with an object keyed by path, but Redis does not\n        // guarantee the key order in that reply, so this parses the JSON and compares it\n        // structurally (map equality, order-independent) instead of comparing raw strings.\n        match r.json_get::<_, _, String>(\"doc\", vec![\"$.a + 1\", \"$.b\"]) {\n            Ok(res71) => {\n                let parsed: serde_json::Value = serde_json::from_str(&res71).unwrap();\n                // Render with sorted keys purely for a deterministic, printable example —\n                // the actual reply's key order can differ from run to run.\n                let sorted: std::collections::BTreeMap<String, serde_json::Value> =\n                    serde_json::from_value(parsed.clone()).unwrap();\n                println!(\"{}\", serde_json::to_string(&sorted).unwrap());    // >>> {\"$.a + 1\":[3],\"$.b\":[4]}\n            },\n            Err(e) => {\n                println!(\"Error getting doc: {e}\");\n                return;\n            }\n        }","section_id":"projection-expressions"}]}
{"schema_version":2,"id":"develop/data-types/json/performance","title":"Performance","url":"https://redis.io/docs/latest/develop/data-types/json/performance/","summary":"Performance benchmarks","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-10T15:45:45+01:00","children":[],"page_type":"content","content_hash":"72601766a6922c0f268ba2c706e2cbfd69cb2df9da4ea1089a8c238f91f1df2c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"To get an early sense of what Redis JSON is capable of, you can test it with `redis-benchmark` just like\nany other Redis command. However, in order to have more control over the tests, we'll use a \na tool written in Go called _ReJSONBenchmark_ that we expect to release in the near future.\n\nThe following figures were obtained from an AWS EC2 c4.8xlarge instance that ran both the Redis\nserver as well the as the benchmarking tool. Connections to the server are via the networking stack.\nAll tests are non-pipelined.\n\n> NOTE: The results below are measured using the preview version of Redis JSON, which is still very much unoptimized."},{"id":"redis-json-baseline","title":"Redis JSON baseline","role":"content","text":""},{"id":"a-smallish-object","title":"A smallish object","role":"content","text":"We test a JSON value that, while purely synthetic, is interesting. The test subject is\n[/tests/files/pass-100.json](https://github.com/RedisLabsModules/redisjson/blob/master/tests/files/pass-100.json),\nwho weighs in at 380 bytes and is nested. We first test SETting it, then GETting it using several\ndifferent paths:\n\n![ReJSONBenchmark pass-100.json](images/bench_pass_100.png)\n\n![ReJSONBenchmark pass-100.json percentiles](images/bench_pass_100_p.png)"},{"id":"a-bigger-array","title":"A bigger array","role":"content","text":"Moving on to bigger values, we use the 1.4 kB array in\n[/tests/files/pass-jsonsl-1.json](https://github.com/RedisLabsModules/redisjson/blob/master/tests/files/pass-jsonsl-1.json):\n\n\n![ReJSONBenchmark pass-jsonsl-1.json](images/bench_pass_jsonsl_1.png)\n\n![ReJSONBenchmark pass-jsonsl-1.json percentiles](images/bench_pass_jsonsl_1_p.png)"},{"id":"a-largish-object","title":"A largish object","role":"content","text":"More of the same to wrap up, now we'll take on a behemoth of no less than 3.5 kB as given by\n[/tests/files/pass-json-parser-0000.json](https://github.com/RedisLabsModules/redisjson/blob/master/tests/files/pass-json-parser-0000.json):\n\n![ReJSONBenchmark pass-json-parser-0000.json](images/bench_pass_json_parser_0000.png)\n\n![ReJSONBenchmark pass-json-parser-0000.json percentiles](images/bench_pass_json_parser_0000_p.png)"},{"id":"number-operations","title":"Number operations","role":"content","text":"Last but not least, some adding and multiplying:\n\n![ReJSONBenchmark number operations](images/bench_numbers.png)\n\n![ReJSONBenchmark number operations percentiles](images/bench_numbers_p.png)"},{"id":"baseline","title":"Baseline","role":"content","text":"To establish a baseline, we'll use the Redis [`PING`](https://redis.io/docs/latest/commands/ping) command.\nFirst, lets see what `redis-benchmark` reports:\n\n[code example]\n\nReJSONBenchmark's concurrency is configurable, so we'll test a few settings to find a good one. Here\nare the results, which indicate that 16 workers yield the best throughput:\n\n![ReJSONBenchmark PING](images/bench_ping.png)\n\n![ReJSONBenchmark PING percentiles](images/bench_ping_p.png)\n\nNote how our benchmarking tool does slightly worse in PINGing - producing only 116K ops, compared to\n`redis-cli`'s 140K."},{"id":"the-empty-string","title":"The empty string","role":"content","text":"Another JSON benchmark is that of setting and getting an empty string - a value that's only two\nbytes long (i.e. `\"\"`). Granted, that's not very useful, but it teaches us something about the basic\nperformance of the module:\n\n![ReJSONBenchmark empty string](images/bench_empty_string.png)\n\n![ReJSONBenchmark empty string percentiles](images/bench_empty_string_p.png)"},{"id":"comparison-vs-server-side-lua-scripting","title":"Comparison vs. server-side Lua scripting","role":"content","text":"We compare the JSON performance of Redis Open Source with the Redis embedded Lua engine. For this purpose, we use the Lua\nscripts at [/benchmarks/lua](https://github.com/RedisLabsModules/redisjson/tree/master/benchmarks/lua).\nThese scripts provide JSON's GET and SET functionality on values stored in JSON or MessagePack\nformats. Each of the different operations (set root, get root, set path and get path) is executed\nwith each \"engine\" on objects of varying sizes."},{"id":"setting-and-getting-the-root","title":"Setting and getting the root","role":"configuration","text":"Storing raw JSON performs best in this test, but that isn't really surprising as all it does is\nserve unprocessed strings. While you can and should use Redis for caching opaque data, and JSON\n\"blobs\" are just one example, this does not allow any updates other than these of the entire value.\n\nA more meaningful comparison therefore is between JSON and the MessagePack variant, since both\nprocess the incoming JSON value before actually storing it. While the rates and latencies of these \ntwo behave in a very similar way, the absolute measurements suggest that Redis JSON's performance may be\nfurther improved.\n\n![VS. Lua set root](images/bench_lua_set_root.png)\n\n![VS. Lua set root latency](images/bench_lua_set_root_l.png)\n\n![VS. Lua get root](images/bench_lua_get_root.png)\n\n![VS. Lua get root latency](images/bench_lua_get_root_l.png)"},{"id":"setting-and-getting-parts-of-objects","title":"Setting and getting parts of objects","role":"configuration","text":"This test shows why Redis JSON exists. Not only does it outperform the Lua variants, it retains constant\nrates and latencies regardless the object's overall size. There's no magic here - JSON keeps the\nvalue deserialized so that accessing parts of it is a relatively inexpensive operation. In deep contrast\nare both raw JSON as well as MessagePack, which require decoding the entire object before anything can\nbe done with it (a process that becomes more expensive the larger the object is).\n\n![VS. Lua set path to scalar](images/bench_lua_set_path.png)\n\n![VS. Lua set path to scalar latency](images/bench_lua_set_path_l.png)\n\n![VS. Lua get scalar from path](images/bench_lua_get_path.png)\n\n![VS. Lua get scalar from path latency](images/bench_lua_get_path_l.png)"},{"id":"even-more-charts","title":"Even more charts","role":"content","text":"These charts are more of the same but independent for each file (value):\n\n![VS. Lua pass-100.json rate](images/bench_lua_pass_100.png)\n\n![VS. Lua pass-100.json average latency](images/bench_lua_pass_100_l.png)\n\n![VS. Lua pass-jsonsl-1.json rate](images/bench_lua_pass_jsonsl_1.png)\n\n![VS. Lua pass-jsonsl-1.json average latency](images/bench_lua_pass_jsonsl_1_l.png)\n\n![VS. Lua pass-json-parser-0000.json rate](images/bench_lua_pass_json_parser_0000.png)\n\n![VS. Lua pass-json-parser-0000.json latency](images/bench_lua_pass_json_parser_0000_l.png)\n\n![VS. Lua pass-jsonsl-yahoo2.json rate](images/bench_lua_pass_jsonsl_yahoo2.png)\n\n![VS. Lua pass-jsonsl-yahoo2.json latency](images/bench_lua_pass_jsonsl_yahoo2_l.png)\n\n![VS. Lua pass-jsonsl-yelp.json rate](images/bench_lua_pass_jsonsl_yelp.png)\n\n![VS. Lua pass-jsonsl-yelp.json latency](images/bench_lua_pass_jsonsl_yelp_l.png)"},{"id":"raw-results","title":"Raw results","role":"content","text":"The following are the raw results from the benchmark in CSV format."},{"id":"json-results","title":"JSON results","role":"content","text":"title,concurrency,rate,average latency,50.00%-tile,90.00%-tile,95.00%-tile,99.00%-tile,99.50%-tile,100.00%-tile\n    [ping],1,22128.12,0.04,0.04,0.04,0.05,0.05,0.05,1.83\n    [ping],2,54641.13,0.04,0.03,0.05,0.05,0.06,0.07,2.14\n    [ping],4,76000.18,0.05,0.05,0.07,0.07,0.09,0.10,2.10\n    [ping],8,106750.99,0.07,0.07,0.10,0.11,0.14,0.16,2.99\n    [ping],12,111297.33,0.11,0.10,0.15,0.16,0.20,0.22,6.81\n    [ping],16,116292.19,0.14,0.13,0.19,0.21,0.27,0.33,7.50\n    [ping],20,110622.82,0.18,0.17,0.24,0.27,0.38,0.47,12.21\n    [ping],24,107468.51,0.22,0.20,0.31,0.38,0.58,0.71,13.86\n    [ping],28,102827.35,0.27,0.25,0.38,0.44,0.66,0.79,12.87\n    [ping],32,105733.51,0.30,0.28,0.42,0.50,0.79,0.97,10.56\n    [ping],36,102046.43,0.35,0.33,0.48,0.56,0.90,1.13,14.66\n    ... output truncated for AI-facing Markdown ...\n    JSON.GET {key} ResultSet.totalResultsAvailable,16,91202.84,0.17,0.16,0.24,0.28,0.35,0.38,5.30\n    JSON.SET {key} . {pass-jsonsl-yelp.json size: 40 kB},16,1624.86,9.84,9.67,9.86,9.94,15.86,19.36,31.94\n    JSON.GET {key} .,16,442.55,36.08,35.62,37.78,38.14,55.23,81.33,88.40\n    JSON.SET {key} message.code 1,16,77677.25,0.20,0.19,0.28,0.33,0.42,0.45,11.07\n    JSON.GET {key} message.code,16,89206.61,0.18,0.16,0.25,0.28,0.36,0.39,8.60\n    [JSON.SET num . 0],16,84498.21,0.19,0.17,0.26,0.30,0.39,0.43,8.08\n    [JSON.NUMINCRBY num . 1],16,78640.20,0.20,0.18,0.28,0.33,0.44,0.48,11.05\n    [JSON.NUMMULTBY num . 2],16,77170.85,0.21,0.19,0.28,0.33,0.43,0.47,6.85"},{"id":"lua-using-cjson","title":"Lua using cjson","role":"content","text":"json-set-root.lua empty string,16,86817.84,0.18,0.17,0.26,0.31,0.39,0.42,9.36\n    json-get-root.lua,16,90795.08,0.17,0.16,0.25,0.28,0.36,0.39,8.75\n    json-set-root.lua pass-100.json,16,84190.26,0.19,0.17,0.27,0.30,0.38,0.41,12.00\n    json-get-root.lua,16,87170.45,0.18,0.17,0.26,0.29,0.38,0.45,9.81\n    json-get-path.lua sclr,16,54556.80,0.29,0.28,0.35,0.38,0.57,0.64,7.53\n    json-set-path.lua sclr 1,16,35907.30,0.44,0.42,0.53,0.67,0.93,1.00,8.57\n    json-get-path.lua sub_doc,16,51158.84,0.31,0.30,0.36,0.39,0.50,0.62,7.22\n    json-get-path.lua sub_doc sclr,16,51054.47,0.31,0.29,0.39,0.47,0.66,0.74,7.43\n    json-get-path.lua array_of_docs,16,39103.77,0.41,0.37,0.57,0.68,0.87,0.94,8.02\n    json-get-path.lua array_of_docs 1,16,45811.31,0.35,0.32,0.45,0.56,0.77,0.83,8.17\n    json-get-path.lua array_of_docs 1 sclr,16,47346.83,0.34,0.31,0.44,0.54,0.72,0.79,8.07\n    json-set-root.lua pass-jsonsl-1.json,16,82100.90,0.19,0.18,0.28,0.31,0.39,0.43,12.43\n    ... output truncated for AI-facing Markdown ...\n    json-set-root.lua pass-jsonsl-yahoo2.json,16,47544.65,0.33,0.31,0.41,0.47,0.59,0.64,10.52\n    json-get-root.lua,16,25369.92,0.63,0.57,0.91,1.05,1.37,1.56,9.95\n    json-set-path.lua ResultSet totalResultsAvailable 1,16,5077.32,3.15,3.09,3.20,3.24,5.12,6.26,14.98\n    json-get-path.lua ResultSet totalResultsAvailable,16,7652.56,2.09,2.05,2.13,2.17,3.23,3.95,9.65\n    json-set-root.lua pass-jsonsl-yelp.json,16,29575.20,0.54,0.52,0.64,0.75,0.94,1.00,12.66\n    json-get-root.lua,16,18424.29,0.87,0.84,1.25,1.40,1.82,1.95,7.35\n    json-set-path.lua message code 1,16,2251.07,7.10,6.98,7.14,7.22,11.00,12.79,21.14\n    json-get-path.lua message code,16,3380.72,4.73,4.44,5.03,6.82,10.28,11.06,14.93"},{"id":"lua-using-cmsgpack","title":"Lua using cmsgpack","role":"content","text":"msgpack-set-root.lua empty string,16,82592.66,0.19,0.18,0.27,0.31,0.38,0.42,10.18\n    msgpack-get-root.lua,16,89561.41,0.18,0.16,0.25,0.29,0.37,0.40,9.52\n    msgpack-set-root.lua pass-100.json,16,44326.47,0.36,0.34,0.43,0.54,0.78,0.86,6.45\n    msgpack-get-root.lua,16,41036.58,0.39,0.36,0.51,0.62,0.84,0.91,7.21\n    msgpack-get-path.lua sclr,16,55845.56,0.28,0.26,0.36,0.44,0.64,0.70,11.29\n    msgpack-set-path.lua sclr 1,16,43608.26,0.37,0.34,0.47,0.58,0.78,0.85,10.27\n    msgpack-get-path.lua sub_doc,16,50153.07,0.32,0.29,0.41,0.50,0.69,0.75,8.56\n    msgpack-get-path.lua sub_doc sclr,16,54016.35,0.29,0.27,0.38,0.46,0.62,0.67,6.38\n    msgpack-get-path.lua array_of_docs,16,45394.79,0.35,0.32,0.45,0.56,0.78,0.85,11.88\n    msgpack-get-path.lua array_of_docs 1,16,48336.48,0.33,0.30,0.42,0.52,0.71,0.76,7.69\n    msgpack-get-path.lua array_of_docs 1 sclr,16,53689.41,0.30,0.27,0.38,0.46,0.64,0.69,11.16\n    msgpack-set-root.lua pass-jsonsl-1.json,16,28956.94,0.55,0.51,0.65,0.82,1.17,1.26,8.39\n    ... output truncated for AI-facing Markdown ...\n    msgpack-set-root.lua pass-jsonsl-yahoo2.json,16,5597.60,2.85,2.81,2.89,2.94,4.57,5.59,10.19\n    msgpack-get-root.lua,16,6585.01,2.43,2.39,2.52,2.66,3.76,4.80,10.59\n    msgpack-set-path.lua ResultSet totalResultsAvailable 1,16,6666.95,2.40,2.35,2.43,2.47,3.78,4.59,12.08\n    msgpack-get-path.lua ResultSet totalResultsAvailable,16,10733.03,1.49,1.45,1.60,1.66,2.36,2.93,13.15\n    msgpack-set-root-lua pass-jsonsl-yelp.json,16,2291.53,6.97,6.87,7.01,7.12,10.54,12.89,21.75\n    msgpack-get-root.lua,16,2889.59,5.53,5.45,5.71,5.86,8.80,10.48,25.55\n    msgpack-set-path.lua message code 1,16,2847.85,5.61,5.44,5.56,6.01,10.58,11.90,16.91\n    msgpack-get-path.lua message code,16,5030.95,3.18,3.07,3.24,3.57,6.08,6.92,12.44"}],"examples":[{"id":"baseline-ex0","language":"plaintext","code":"~$ redis/src/redis-benchmark -n 1000000 ping\n====== ping ======\n  1000000 requests completed in 7.11 seconds\n  50 parallel clients\n  3 bytes payload\n  keep alive: 1\n\n99.99% <= 1 milliseconds\n100.00% <= 1 milliseconds\n140587.66 requests per second","section_id":"baseline"}]}
{"schema_version":2,"id":"develop/data-types/json/ram","title":"Redis JSON RAM Usage","url":"https://redis.io/docs/latest/develop/data-types/json/ram/","summary":"Debugging memory consumption","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-10T15:45:45+01:00","page_type":"content","content_hash":"6b990e7067067ddc376f042878c143e24f6a846f1c58783a561c2d9695431cea","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> Because of ongoing feature additions, improvements, and optimizations, JSON memory consumption may vary depending on the Redis version.\n> Redis 8 in Redis Open Source was used for the examples on this page.\n\nEvery key in Redis takes memory and requires at least the amount of RAM to store the key name, as\nwell as some per-key overhead that Redis uses. On top of that, the value in the key also requires\nRAM.\n\nRedis JSON stores JSON values as binary data after deserialization. This representation is often more\nexpensive, size-wise, than the serialized form. All JSON values occupy at least 8 bytes (on 64-bit architectures) because each is represented as a thin wrapper around a pointer. The type information is stored in the lower bits of the pointer, which are guaranteed to be zero due to alignment restrictions. This allows those bits to be repurposed to store some auxiliary data.\n\nFor some types of JSON values, 8 bytes is all that’s needed. Nulls and booleans don’t require any additional storage. Small integers are stored in static memory because they’re frequently used, so they also use only the initial 8 bytes. Similarly, empty strings, arrays, and objects don’t require any bookkeeping. Instead, they point to static instances of a _null_ string, array, or object. Here are some examples that use the [JSON.DEBUG MEMORY](https://redis.io/docs/latest/commands/json.debug-memory) command to report on memory consumption:\n\n[code example]\n\nThis RAM requirement is the same for all scalar values, but strings require additional space\ndepending on their length. For example, a 3-character string will use 3 additional bytes:\n\n[code example]\n\nIn the following four examples, each array requires 56 bytes. This breaks down as:\n- 8 bytes for the initial array value pointer\n- 16 bytes of metadata: 8 bytes for the allocated capacity and 8 bytes for the point-in-time size of the array\n- 32 bytes for the array. The initial capacity of an array is 4. Therefore, the calculation is `4 * 8` bytes\n\n[code example]\n\n[code example]\n\n[code example]\n\n[code example]\n\nOnce the current capacity is insufficient to fit a new value, the array reallocates to double its capacity. An array with 5 elements will have a capacity of 8, therefore consuming `8 + 16 + 8 * 8 = 88` bytes.\n\n[code example]\n\nBecause reallocation operations can be expensive, Redis grows JSON arrays geometrically rather than linearly. This approach spreads the cost across many insertions.\n\nThis table gives the size (in bytes) of a few of the test files from the [module repo](https://github.com/RedisJSON/RedisJSON/tree/master/tests/files), stored using\nJSON. The _MessagePack_ column is for reference purposes and reflects the length of the value when stored using [MessagePack](https://msgpack.org/index.html).\n\n| File                                    | File size | Redis JSON | MessagePack |\n| --------------------------------------- | --------- | ---------- | ----------- |\n| /tests/files/pass-100.json              | 381       | 1069       | 140         |\n| /tests/files/pass-jsonsl-1.json         | 1387      | 2190       | 757         |\n| /tests/files/pass-json-parser-0000.json | 3718      | 5469       | 2393        |\n| /tests/files/pass-jsonsl-yahoo2.json    | 22466     | 26901      | 16869       |\n| /tests/files/pass-jsonsl-yelp.json      | 46333     | 57513      | 35529       |\n\n> Note: In the current version, deleting values from containers **does not** free the container's\nallocated memory."},{"id":"json-string-reuse-mechanism","title":"JSON string reuse mechanism","role":"content","text":"Redis uses a global string reuse mechanism to reduce memory usage. When a string value appears multiple times, either within the same JSON document\nor across different documents on the same node, Redis stores only a single copy of that string and uses references to it.\nThis approach is especially efficient when many documents share similar structures.\n\nHowever, the `JSON.DEBUG MEMORY` command reports memory usage as if each string instance is stored independently, even when it's actually reused.\nFor example, the document `{\"foo\": [\"foo\", \"foo\"]}` reuses the string `\"foo\"` internally, but the reported memory usage counts the string three times: once for the key and once for each array element."}],"examples":[{"id":"overview-ex0","language":"plaintext","code":"127.0.0.1:6379> JSON.SET boolean . 'true'\nOK\n127.0.0.1:6379> JSON.DEBUG MEMORY boolean\n(integer) 8\n\n127.0.0.1:6379> JSON.SET null . null\nOK\n127.0.0.1:6379> JSON.DEBUG MEMORY null\n(integer) 8\n\n127.0.0.1:6379> JSON.SET emptystring . '\"\"'\nOK\n127.0.0.1:6379> JSON.DEBUG MEMORY emptystring\n(integer) 8\n\n127.0.0.1:6379> JSON.SET emptyarr . '[]'\nOK\n127.0.0.1:6379> JSON.DEBUG MEMORY emptyarr\n(integer) 8\n\n127.0.0.1:6379> JSON.SET emptyobj . '{}'\nOK\n127.0.0.1:6379> JSON.DEBUG MEMORY emptyobj\n(integer) 8","section_id":"overview"},{"id":"overview-ex1","language":"plaintext","code":"127.0.0.1:6379> JSON.SET foo . '\"bar\"'\nOK\n127.0.0.1:6379> JSON.DEBUG MEMORY foo\n(integer) 11","section_id":"overview"},{"id":"overview-ex2","language":"plaintext","code":"127.0.0.1:6379> JSON.SET arr . '[\"\"]'\nOK\n127.0.0.1:6379> JSON.DEBUG MEMORY arr\n(integer) 56","section_id":"overview"},{"id":"overview-ex3","language":"plaintext","code":"127.0.0.1:6379> JSON.SET arr . '[\"\", \"\"]'\nOK\n127.0.0.1:6379> JSON.DEBUG MEMORY arr\n(integer) 56","section_id":"overview"},{"id":"overview-ex4","language":"plaintext","code":"127.0.0.1:6379> JSON.SET arr . '[\"\", \"\", \"\"]'\nOK\n127.0.0.1:6379> JSON.DEBUG MEMORY arr\n(integer) 56","section_id":"overview"},{"id":"overview-ex5","language":"plaintext","code":"127.0.0.1:6379> JSON.SET arr . '[\"\", \"\", \"\", \"\"]'\nOK\n127.0.0.1:6379> JSON.DEBUG MEMORY arr\n(integer) 56","section_id":"overview"},{"id":"overview-ex6","language":"plaintext","code":"127.0.0.1:6379> JSON.SET arr . '[\"\", \"\", \"\", \"\", \"\"]'\nOK\n127.0.0.1:6379> JSON.DEBUG MEMORY arr\n(integer) 88","section_id":"overview"}]}
{"schema_version":2,"id":"develop/data-types/json/resp3","title":"Guide for migrating from RESP2 to RESP3 replies","url":"https://redis.io/docs/latest/develop/data-types/json/resp3/","summary":"JSON RESP2 to RESP3 replies reference for client developers","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2025-09-03T08:26:45-07:00","page_type":"content","content_hash":"c342517050a784083fe5a42f4a55591cc8608483a55eccc0f3b9e5a15b522c32","sections":[{"id":"overview","title":"Overview","role":"overview","text":"In RESP3, the default value of the optional path argument was changed from `.` to `$`. \nDue to this change, the replies of some commands have slightly changed. \nThis page provides a brief comparison between RESP2 and RESP3 responses for JSON commands to help developers in migrating their clients from RESP2 to RESP3."},{"id":"json-command-replies-comparison","title":"JSON command replies comparison","role":"content","text":"The types are described using a [\"TypeScript-like\" syntax](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html). `Array<a>` denotes an [array](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#arrays) where the type of elements is known, but the number of elements is not.\n\n| Command                                                                                                                                                 | RESP2                                                                                                                                                                                            | RESP3                                                                                                                                                                                                               |\n|---------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| All JSON commands                                                                                                                                       | **Default value of optional `path` argument**: `.`                                                                                                                                               | **Default value of optional `path` argument:** `$`                                                                                                                                                                  |\n| JSON.ARRAPPEND<br/>JSON.ARRINDEX<br/>JSON.ARRINSERT<br/>JSON.ARRLEN<br/>JSON.ARRTRIM<br/>JSON.OBJLEN<br/>JSON.STRAPPEND<br/>JSON.STRLEN<br/>JSON.TOGGLE | *`$`-based path argument:* <br/> Reply: Array\\<BulkString &#124; null> <br/><br/> *`.`-based path argument :*&nbsp;<br/> Reply: BulkString                                                       | *`$`-based path argument:*&nbsp; <br/> Reply: Array\\<number &#124; null> <br/><br/> *`.`-based path argument :* <br/> Reply: number                                                                                 |\n| JSON.GET                                                                                                                                                | Reply: JSON encoded string  <br/> Example: <br/> [code example]> JSON.TYPE k $```<br />```1) \"array\"``` <br/><br/> *`.`-based path argument :*&nbsp;<br/> Reply: BulkString | *`$`-based path argument:* <br/> Reply: Array\\<Array\\<BulkString\\>> <br/> Example: <br />```> JSON.TYPE k $```<br />```1) 1) \"array\"``` <br/><br/> *`.`-based path argument :*&nbsp;<br/> Reply: Array\\<BulkString> |"}],"examples":[{"id":"json-command-replies-comparison-ex0","language":"> JSON.SET k $ \"[1,2,3]\"```<br/> ```> JSON.GET k```<br/>```\"[1,2,3]\"```                                                                      | Reply: JSON encoded string with a top-level array  <br/> Example: <br/> ```> JSON.SET k $ \"[1,2,3]\"```<br/> ```> JSON.GET k```<br/>```\"[[1,2,3]]\"```                                                                |","code":"| JSON.NUMINCRBY<br/>JSON.NUMMULTBY                                                                                                                       | *`$`-based path argument:* <br/> Reply: JSON-encoded BulkString &#124; null <br/><br/> *`.`-based path argument :*&nbsp;<br/> Reply: BulkString &#124; null &#124; error                         | *`$`-based path argument:* <br/> Reply: Array\\<number &#124;  null> &#124; error <br/><br/> *`.`-based path argument :*&nbsp;<br/> Reply: number &#124;  null &#124; error                                          |\n| JSON.OBJKEYS                                                                                                                                            | *`$`-based path argument:* <br/> Reply: Array\\<Array\\<BulkString\\>> <br/><br/> *`.`-based path argument :*&nbsp;<br/> Reply: Array\\<BulkString>                                                  | *`$`-based path argument:* <br/> Reply: Array\\<Array\\<BulkString\\>> <br/><br/> *`.`-based path argument :*&nbsp;<br/> Reply: Array\\<BulkString>                                                                     |\n| JSON.TYPE                                                                                                                                               | *`$`-based path argument:* <br/> Reply: Array\\<BulkString\\> <br/> Example: <br />","section_id":"json-command-replies-comparison"}]}
{"schema_version":2,"id":"develop/data-types/json/use_cases","title":"Use cases","url":"https://redis.io/docs/latest/develop/data-types/json/use_cases/","summary":"JSON use cases","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-10T15:45:45+01:00","page_type":"content","content_hash":"4e9128183e253c147b511fdffcca7900b2a4eb5784499afd41b669bd797f7011","sections":[{"id":"content","title":"Content","role":"content","text":"You can of course use Redis native data structures to store JSON objects, and that's a common practice. For example, you can serialize JSON and save it in a Redis String.\n\nHowever, Redis JSON provides several benefits over this approach.\n\n**Access and retrieval of subvalues**\n\nWith JSON, you can get nested values without having to transmit the entire object over the network. Being able to access sub-objects can lead to greater efficiencies when you're storing large JSON objects in Redis.\n\n**Atomic partial updates**\n\nJSON allows you to atomically run operations like incrementing a value, adding, or removing elements from an array, append strings, and so on. To do the same with a serialized object, you have to retrieve and then reserialize the entire object, which can be expensive and also lack atomicity.\n\n**Indexing and querying**\n\nWhen you store JSON objects as Redis strings, there's no good way to query those objects. On the other hand, storing these objects as JSON lets you index and query them. This capability is provided by Redis Search.\n\nWith the `FPHA` option to the [`JSON.SET`](https://redis.io/docs/latest/commands/json.set) command, Redis provides the user with a flexible way to store floating point vectors for vector search."}],"examples":[]}
{"schema_version":2,"id":"develop/data-types/lists","title":"Redis lists","url":"https://redis.io/docs/latest/develop/data-types/lists/","summary":"Introduction to Redis lists","aliases":["/data-types/lists/","/manual/data-types/lists/","/develop/data-types/list/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-10T15:45:53+01:00","page_type":"content","content_hash":"02d3f8dbe371261e31635931e55b202d6caa4dfcfd1a6ac300755613e3eae1fb","sections":[{"id":"list-command-summary","title":"List command summary","role":"content","text":"**24 commands in this group:**\n\n[View all list commands](https://redis.io/commands/?group=list)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [BLMOVE](https://redis.io/commands/blmove/) | Pops an element from a list, pushes it to another list and returns it. Blocks until an element is available otherwise. Deletes the list if the last element was moved. | O(1) | 6.2.0 |\n| [BLMOVEM](https://redis.io/commands/blmovem/) | Moves up to (or exactly) a number of elements from one list to another and returns them. Blocks until the elements are available otherwise. Deletes the source list if it becomes empty. | O(N) where N is the number of elements moved. | 8.10.0 |\n| [BLMPOP](https://redis.io/commands/blmpop/) | Pops the first element from one of multiple lists. Blocks until an element is available otherwise. Deletes the list if the last element was popped. | O(N+M) where N is the number of provided keys a... | 7.0.0 |\n| [BLPOP](https://redis.io/commands/blpop/) | Removes and returns the first element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped. | O(N) where N is the number of provided keys. | 2.0.0 |\n| [BRPOP](https://redis.io/commands/brpop/) | Removes and returns the last element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped. | O(N) where N is the number of provided keys. | 2.0.0 |\n| [BRPOPLPUSH](https://redis.io/commands/brpoplpush/) | Pops an element from a list, pushes it to another list and returns it. Block until an element is available otherwise. Deletes the list if the last element was popped. | O(1) | 2.2.0 |\n| [LINDEX](https://redis.io/commands/lindex/) | Returns an element from a list by its index. | O(N) where N is the number of elements to trave... | 1.0.0 |\n| [LINSERT](https://redis.io/commands/linsert/) | Inserts an element before or after another element in a list. | O(N) where N is the number of elements to trave... | 2.2.0 |\n| [LLEN](https://redis.io/commands/llen/) | Returns the length of a list. | O(1) | 1.0.0 |\n| [LMOVE](https://redis.io/commands/lmove/) | Returns an element after popping it from one list and pushing it to another. Deletes the list if the last element was moved. | O(1) | 6.2.0 |\n\n#### List command summary (part 2)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [LMOVEM](https://redis.io/commands/lmovem/) | Moves up to (or exactly) a number of elements from one list to another and returns them. Deletes the source list if it becomes empty. | O(N) where N is the number of elements moved. | 8.10.0 |\n| [LMPOP](https://redis.io/commands/lmpop/) | Returns multiple elements from a list after removing them. Deletes the list if the last element was popped. | O(N+M) where N is the number of provided keys a... | 7.0.0 |\n| [LPOP](https://redis.io/commands/lpop/) | Returns the first elements in a list after removing it. Deletes the list if the last element was popped. | O(N) where N is the number of elements returned | 1.0.0 |\n| [LPOS](https://redis.io/commands/lpos/) | Returns the index of matching elements in a list. | O(N) where N is the number of elements in the l... | 6.0.6 |\n| [LPUSH](https://redis.io/commands/lpush/) | Prepends one or more elements to a list. Creates the key if it doesn't exist. | O(1) for each element added, so O(N) to add N e... | 1.0.0 |\n| [LPUSHX](https://redis.io/commands/lpushx/) | Prepends one or more elements to a list only when the list exists. | O(1) for each element added, so O(N) to add N e... | 2.2.0 |\n| [LRANGE](https://redis.io/commands/lrange/) | Returns a range of elements from a list. | O(S+N) where S is the distance of start offset ... | 1.0.0 |\n| [LREM](https://redis.io/commands/lrem/) | Removes elements from a list. Deletes the list if the last element was removed. | O(N+M) where N is the length of the list and M ... | 1.0.0 |\n| [LSET](https://redis.io/commands/lset/) | Sets the value of an element in a list by its index. | O(N) where N is the length of the list. Setting... | 1.0.0 |\n| [LTRIM](https://redis.io/commands/ltrim/) | Removes elements from both ends a list. Deletes the list if all elements were trimmed. | O(N) where N is the number of elements to be re... | 1.0.0 |\n\n#### List command summary (part 3)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [RPOP](https://redis.io/commands/rpop/) | Returns and removes the last elements of a list. Deletes the list if the last element was popped. | O(N) where N is the number of elements returned | 1.0.0 |\n| [RPOPLPUSH](https://redis.io/commands/rpoplpush/) | Returns the last element of a list after removing and pushing it to another list. Deletes the list if the last element was popped. | O(1) | 1.2.0 |\n| [RPUSH](https://redis.io/commands/rpush/) | Appends one or more elements to a list. Creates the key if it doesn't exist. | O(1) for each element added, so O(N) to add N e... | 1.0.0 |\n| [RPUSHX](https://redis.io/commands/rpushx/) | Appends an element to a list only when the list exists. | O(1) for each element added, so O(N) to add N e... | 2.2.0 |\n\n\n\nRedis lists are linked lists of string values.\nRedis lists are frequently used to:\n\n* Implement stacks and queues.\n* Build queue management for background worker systems."},{"id":"examples","title":"Examples","role":"example","text":"* Treat a list like a queue (first in, first out):\nQueue pattern: Use LPUSH to add items and RPOP to remove them in FIFO order when you need to process items in the order they were added\n\n**Builds upon:** lpush_rpush, lpop_rpop\n\n**Commands:** LPUSH, RPOP\n\n**Complexity:**\n- LPUSH: O(1)\n- RPOP: O(N)\n\n**Available in:** Redis CLI, C#, C#, Go, Java (Asynchronous - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\n* Treat a list like a stack (first in, last out):\nStack pattern: Use LPUSH to add items and LPOP to remove them in LIFO order when you need to process items in reverse order\n\n**Builds upon:** lpush_rpush\n\n**Commands:** LPUSH, LPOP\n\n**Complexity:**\n- LPUSH: O(1)\n- LPOP: O(N)\n\n**Available in:** Redis CLI, C#, C#, Go, Java (Asynchronous - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\n* Check the length of a list:\nFoundational: Get the number of elements in a list using LLEN to check list size\n\n**Commands:** LLEN\n\n**Complexity:**\n- LLEN: O(1)\n\n**Available in:** Redis CLI, C#, C#, Go, Java (Asynchronous - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\n* Atomically pop an element from one list and push to another:\nAtomic transfer: Use LMOVE to move elements between lists in a single operation when you need to transfer items without race conditions\n\n**Difficulty:** Intermediate\n\n**Builds upon:** lpush_rpush, lpop_rpop\n\n**Commands:** LPUSH, LMOVE, LRANGE\n\n**Complexity:**\n- LPUSH: O(1)\n- LMOVE: O(1)\n- LRANGE: O(S+N)\n\n**Available in:** Redis CLI, C#, C#, Go, Java (Asynchronous - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\n* To limit the length of a list you can call [`LTRIM`](https://redis.io/docs/latest/commands/ltrim):\nCapped lists: Use LTRIM to keep only a specific range of elements when you need to maintain a fixed-size list\n\n**Difficulty:** Intermediate\n\n**Builds upon:** lpush_rpush\n\n**Commands:** DEL, RPUSH, LTRIM, LRANGE\n\n**Complexity:**\n- DEL: O(N)\n- RPUSH: O(1)\n- LTRIM: O(N)\n- LRANGE: O(S+N)\n\n**Available in:** Redis CLI, C#, C#, Go, Java (Asynchronous - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"what-are-lists","title":"What are Lists?","role":"content","text":"To explain the List data type it's better to start with a little bit of theory,\nas the term *List* is often used in an improper way by information technology\nfolks. For instance \"Python Lists\" are not what the name may suggest (Linked\nLists), but rather Arrays (the same data type is called Array in\nRuby actually).\n\nFrom a very general point of view a List is just a sequence of ordered\nelements: 10,20,1,2,3 is a list. But the properties of a List implemented using\nan Array are very different from the properties of a List implemented using a\n*Linked List*.\n\nRedis lists are implemented via Linked Lists. This means that even if you have\nmillions of elements inside a list, the operation of adding a new element in\nthe head or in the tail of the list is performed *in constant time*. The speed of adding a\nnew element with the [`LPUSH`](https://redis.io/docs/latest/commands/lpush) command to the head of a list with ten\nelements is the same as adding an element to the head of list with 10\nmillion elements.\n\nWhat's the downside? Accessing an element *by index* is very fast in lists\nimplemented with an Array (constant time indexed access) and not so fast in\nlists implemented by linked lists (where the operation requires an amount of\nwork proportional to the index of the accessed element).\n\nRedis Lists are implemented with linked lists because for a database system it\nis crucial to be able to add elements to a very long list in a very fast way.\nAnother strong advantage, as you'll see in a moment, is that Redis Lists can be\ntaken at constant length in constant time.\n\nWhen fast access to the middle of a large collection of elements is important,\nthere is a different data structure that can be used, called sorted sets.\nSorted sets are covered in the [Sorted sets](https://redis.io/docs/latest/develop/data-types/sorted-sets) tutorial page."},{"id":"first-steps-with-redis-lists","title":"First steps with Redis Lists","role":"content","text":"The [`LPUSH`](https://redis.io/docs/latest/commands/lpush) command adds a new element into a list, on the\nleft (at the head), while the [`RPUSH`](https://redis.io/docs/latest/commands/rpush) command adds a new\nelement into a list, on the right (at the tail). Finally the\n[`LRANGE`](https://redis.io/docs/latest/commands/lrange) command extracts ranges of elements from lists:\n\nFoundational: Add elements to both ends of a list using LPUSH (left/head) and RPUSH (right/tail) to build ordered sequences\n\n**Commands:** DEL, RPUSH, LPUSH, LRANGE\n\n**Complexity:**\n- DEL: O(N)\n- RPUSH: O(1)\n- LPUSH: O(1)\n- LRANGE: O(S+N)\n\n**Available in:** Redis CLI, C#, C#, Go, Java (Asynchronous - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nNote that [`LRANGE`](https://redis.io/docs/latest/commands/lrange) takes two indexes, the first and the last\nelement of the range to return. Both the indexes can be negative, telling Redis\nto start counting from the end: so -1 is the last element, -2 is the\npenultimate element of the list, and so forth.\n\nAs you can see [`RPUSH`](https://redis.io/docs/latest/commands/rpush) appended the elements on the right of the list, while\nthe final [`LPUSH`](https://redis.io/docs/latest/commands/lpush) appended the element on the left.\n\nBoth commands are *variadic commands*, meaning that you are free to push\nmultiple elements into a list in a single call:\n\nVariadic operations: Add multiple elements in a single command using LPUSH or RPUSH when you need to reduce round trips to the server\n\n**Builds upon:** lpush_rpush\n\n**Commands:** DEL, RPUSH, LPUSH, LRANGE\n\n**Complexity:**\n- DEL: O(N)\n- RPUSH: O(1)\n- LPUSH: O(1)\n- LRANGE: O(S+N)\n\n**Available in:** Redis CLI, C#, C#, Go, Java (Asynchronous - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nAn important operation defined on Redis lists is the ability to *pop elements*.\nPopping elements is the operation of both retrieving the element from the list,\nand eliminating it from the list, at the same time. You can pop elements\nfrom left and right, similarly to how you can push elements in both sides\nof the list. We'll add three elements and pop three elements, so at the end of this\nsequence of commands the list is empty and there are no more elements to\npop:\n\nFoundational: Remove and retrieve elements from both ends of a list using LPOP (left/head) and RPOP (right/tail) to consume items\n\n**Commands:** DEL, RPUSH, RPOP, LPOP\n\n**Complexity:**\n- DEL: O(N)\n- RPUSH: O(1)\n- RPOP: O(N)\n- LPOP: O(N)\n\n**Available in:** Redis CLI, C#, C#, Go, Java (Asynchronous - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nRedis returned a NULL value to signal that there are no elements in the\nlist."},{"id":"common-use-cases-for-lists","title":"Common use cases for lists","role":"content","text":"Lists are useful for a number of tasks, two very representative use cases\nare the following:\n\n* Remember the latest updates posted by users into a social network.\n* Communication between processes, using a consumer-producer pattern where the producer pushes items into a list, and a consumer (usually a *worker*) consumes those items and executes actions. Redis has special list commands to make this use case both more reliable and efficient.\n\nFor example both the popular Ruby libraries [resque](https://github.com/resque/resque) and\n[sidekiq](https://github.com/mperham/sidekiq) use Redis lists under the hood in order to\nimplement background jobs.\n\nThe popular Twitter social network [takes the latest tweets](http://www.infoq.com/presentations/Real-Time-Delivery-Twitter)\nposted by users into Redis lists.\n\nTo describe a common use case step by step, imagine your home page shows the latest\nphotos published in a photo sharing social network and you want to speedup access.\n\n* Every time a user posts a new photo, we add its ID into a list with [`LPUSH`](https://redis.io/docs/latest/commands/lpush).\n* When users visit the home page, we use `LRANGE 0 9` in order to get the latest 10 posted items."},{"id":"capped-lists","title":"Capped lists","role":"content","text":"In many use cases we just want to use lists to store the *latest items*,\nwhatever they are: social network updates, logs, or anything else.\n\nRedis allows us to use lists as a capped collection, only remembering the latest\nN items and discarding all the oldest items using the [`LTRIM`](https://redis.io/docs/latest/commands/ltrim) command.\n\nThe [`LTRIM`](https://redis.io/docs/latest/commands/ltrim) command is similar to [`LRANGE`](https://redis.io/docs/latest/commands/lrange), but **instead of displaying the\nspecified range of elements** it sets this range as the new list value. All\nthe elements outside the given range are removed.\n\nFor example, if you're adding bikes on the end of a list of repairs, but only\nwant to worry about the 3 that have been on the list the longest:\n\nCapped lists: Use LTRIM with positive indexes to keep a range of elements from the beginning when you need to maintain a fixed-size list\n\n**Difficulty:** Intermediate\n\n**Builds upon:** lpush_rpush\n\n**Commands:** DEL, RPUSH, LTRIM, LRANGE\n\n**Complexity:**\n- DEL: O(N)\n- RPUSH: O(1)\n- LTRIM: O(N)\n- LRANGE: O(S+N)\n\n**Available in:** Redis CLI, C#, C#, Go, Java (Asynchronous - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nThe above [`LTRIM`](https://redis.io/docs/latest/commands/ltrim) command tells Redis to keep just list elements from index\n0 to 2, everything else will be discarded. This allows for a very simple but\nuseful pattern: doing a List push operation + a List trim operation together \nto add a new element and discard elements exceeding a limit. Using \n[`LTRIM`](https://redis.io/docs/latest/commands/ltrim) with negative indexes can then be used to keep only the 3 most recently added:\n\nCapped lists with negative indexes: Use LTRIM with negative indexes to keep the most recent elements when you need to maintain a fixed-size list of latest items\n\n**Difficulty:** Intermediate\n\n**Builds upon:** lpush_rpush\n\n**Commands:** DEL, RPUSH, LTRIM, LRANGE\n\n**Complexity:**\n- DEL: O(N)\n- RPUSH: O(1)\n- LTRIM: O(N)\n- LRANGE: O(S+N)\n\n**Available in:** Redis CLI, C#, C#, Go, Java (Asynchronous - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nThe above combination adds new elements and keeps only the 3\nnewest elements into the list. With [`LRANGE`](https://redis.io/docs/latest/commands/lrange) you can access the top items\nwithout any need to remember very old data.\n\nNote: while [`LRANGE`](https://redis.io/docs/latest/commands/lrange) is technically an O(N) command, accessing small ranges\ntowards the head or the tail of the list is a constant time operation."},{"id":"blocking-operations-on-lists","title":"Blocking operations on lists","role":"content","text":"Lists have a special feature that make them suitable to implement queues,\nand in general as a building block for inter process communication systems:\nblocking operations.\n\nImagine you want to push items into a list with one process, and use\na different process in order to actually do some kind of work with those\nitems. This is the usual producer / consumer setup, and can be implemented\nin the following simple way:\n\n* To push items into the list, producers call [`LPUSH`](https://redis.io/docs/latest/commands/lpush).\n* To extract / process items from the list, consumers call [`RPOP`](https://redis.io/docs/latest/commands/rpop).\n\nHowever it is possible that sometimes the list is empty and there is nothing\nto process, so [`RPOP`](https://redis.io/docs/latest/commands/rpop) just returns NULL. In this case a consumer is forced to wait\nsome time and retry again with [`RPOP`](https://redis.io/docs/latest/commands/rpop). This is called *polling*, and is not\na good idea in this context because it has several drawbacks:\n\n1. Forces Redis and clients to process useless commands (all the requests when the list is empty will get no actual work done, they'll just return NULL).\n2. Adds a delay to the processing of items, since after a worker receives a NULL, it waits some time. To make the delay smaller, we could wait less between calls to [`RPOP`](https://redis.io/docs/latest/commands/rpop), with the effect of amplifying problem number 1, i.e. more useless calls to Redis.\n\nSo Redis implements commands called [`BRPOP`](https://redis.io/docs/latest/commands/brpop) and [`BLPOP`](https://redis.io/docs/latest/commands/blpop) which are versions\nof [`RPOP`](https://redis.io/docs/latest/commands/rpop) and [`LPOP`](https://redis.io/docs/latest/commands/lpop) able to block if the list is empty: they'll return to\nthe caller only when a new element is added to the list, or when a user-specified\ntimeout is reached.\n\nThis is an example of a [`BRPOP`](https://redis.io/docs/latest/commands/brpop) call we could use in the worker:\n\nBlocking operations: Use BRPOP to wait for elements with a timeout when you need to implement producer-consumer patterns without polling\n\n**Difficulty:** Intermediate\n\n**Builds upon:** lpush_rpush, lpop_rpop\n\n**Commands:** DEL, RPUSH, BRPOP\n\n**Complexity:**\n- DEL: O(N)\n- RPUSH: O(1)\n- BRPOP: O(N)\n\n**Available in:** Redis CLI, C#, C#, Go, Java (Asynchronous - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nIt means: \"wait for elements in the list `bikes:repairs`, but return if after 1 second\nno element is available\".\n\nNote that you can use 0 as timeout to wait for elements forever, and you can\nalso specify multiple lists and not just one, in order to wait on multiple\nlists at the same time, and get notified when the first list receives an\nelement.\n\nA few things to note about [`BRPOP`](https://redis.io/docs/latest/commands/brpop):\n\n1. Clients are served in an ordered way: the first client that blocked waiting for a list, is served first when an element is pushed by some other client, and so forth.\n2. The return value is different compared to [`RPOP`](https://redis.io/docs/latest/commands/rpop): it is a two-element array since it also includes the name of the key, because [`BRPOP`](https://redis.io/docs/latest/commands/brpop) and [`BLPOP`](https://redis.io/docs/latest/commands/blpop) are able to block waiting for elements from multiple lists.\n3. If the timeout is reached, NULL is returned.\n\nThere are more things you should know about lists and blocking ops. We\nsuggest that you read more on the following:\n\n* It is possible to build safer queues or rotating queues using [`LMOVE`](https://redis.io/docs/latest/commands/lmove).\n* There is also a blocking variant of the command, called [`BLMOVE`](https://redis.io/docs/latest/commands/blmove)."},{"id":"automatic-creation-and-removal-of-keys","title":"Automatic creation and removal of keys","role":"content","text":"So far in our examples we never had to create empty lists before pushing\nelements, or removing empty lists when they no longer have elements inside.\nIt is Redis' responsibility to delete keys when lists are left empty, or to create\nan empty list if the key does not exist and we are trying to add elements\nto it, for example, with [`LPUSH`](https://redis.io/docs/latest/commands/lpush).\n\nThis is not specific to lists, it applies to all the Redis data types\ncomposed of multiple elements -- Streams, Sets, Sorted Sets and Hashes.\n\nBasically we can summarize the behavior with three rules:\n\n1. When we add an element to an aggregate data type, if the target key does not exist, an empty aggregate data type is created before adding the element.\n2. When we remove elements from an aggregate data type, if the value remains empty, the key is automatically destroyed. The Stream data type is the only exception to this rule.\n3. Calling a read-only command such as [`LLEN`](https://redis.io/docs/latest/commands/llen) (which returns the length of the list), or a write command removing elements, with an empty key, always produces the same result as if the key is holding an empty aggregate type of the type the command expects to find.\n\nExamples of rule 1:\n\nAutomatic creation: Redis automatically creates a list when you push to a non-existent key, simplifying initialization\n\n**Commands:** DEL, LPUSH\n\n**Complexity:**\n- DEL: O(N)\n- LPUSH: O(1)\n\n**Available in:** Redis CLI, C#, C#, Go, Java (Asynchronous - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nHowever we can't perform operations against the wrong type if the key exists:\n\nType safety: Redis prevents operations on wrong data types, returning WRONGTYPE error when you try to use list commands on non-list keys\n\n**Builds upon:** rule_1\n\n**Commands:** DEL, SET, TYPE, LPUSH\n\n**Complexity:**\n- DEL: O(N)\n- SET: O(1)\n- TYPE: O(1)\n- LPUSH: O(1)\n\n**Available in:** Redis CLI, C#, C#, Go, Java (Asynchronous - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nExample of rule 2:\n\nAutomatic removal: Redis automatically deletes empty lists, freeing memory when the last element is removed\n\n**Builds upon:** lpush_rpush, lpop_rpop\n\n**Commands:** DEL, LPUSH, EXISTS, LPOP\n\n**Complexity:**\n- DEL: O(N)\n- LPUSH: O(1)\n- EXISTS: O(N)\n- LPOP: O(N)\n\n**Available in:** Redis CLI, C#, C#, Go, Java (Asynchronous - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nThe key no longer exists after all the elements are popped.\n\nExample of rule 3:\n\nNil handling: Read-only commands on non-existent keys return empty results (0 or nil) instead of errors, treating them as empty lists\n\n**Commands:** DEL, LLEN, LPOP\n\n**Complexity:**\n- DEL: O(N)\n- LLEN: O(1)\n- LPOP: O(N)\n\n**Available in:** Redis CLI, C#, C#, Go, Java (Asynchronous - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"limits","title":"Limits","role":"limits","text":"The maximum length of a Redis list is 2^32 - 1 (4,294,967,295) elements."},{"id":"performance","title":"Performance","role":"performance","text":"List operations that access its head or tail are O(1), which means they're highly efficient.\nHowever, commands that manipulate elements within a list are usually O(n).\nExamples of these include [`LINDEX`](https://redis.io/docs/latest/commands/lindex), [`LINSERT`](https://redis.io/docs/latest/commands/linsert), and [`LSET`](https://redis.io/docs/latest/commands/lset).\nExercise caution when running these commands, mainly when operating on large lists."},{"id":"alternatives","title":"Alternatives","role":"content","text":"Consider [Redis streams](https://redis.io/docs/latest/develop/data-types/streams) as an alternative to lists when you need to store and process an indeterminate series of events."},{"id":"learn-more","title":"Learn more","role":"related","text":"* [Redis Lists Explained](https://www.youtube.com/watch?v=PB5SeOkkxQc) is a short, comprehensive video explainer on Redis lists.\n* [Redis University's RU101](https://university.redis.com/courses/ru101/) covers Redis lists in detail."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> LPUSH bikes:repairs bike:1\n(integer) 1\n> LPUSH bikes:repairs bike:2\n(integer) 2\n> RPOP bikes:repairs\n\"bike:1\"\n> RPOP bikes:repairs\n\"bike:2\"","section_id":"examples"},{"id":"examples-ex1","language":"csharp","code":"long res1 = db.ListLeftPush(\"bikes:repairs\", \"bike:1\");\n        Console.WriteLine(res1);    // >>> 1\n\n        long res2 = db.ListLeftPush(\"bikes:repairs\", \"bike:2\");\n        Console.WriteLine(res2);    // >>> 2\n\n        RedisValue res3 = db.ListRightPop(\"bikes:repairs\");\n        Console.WriteLine(res3);    // >>> \"bike:1\"\n\n        RedisValue res4 = db.ListRightPop(\"bikes:repairs\");\n        Console.WriteLine(res4); // >>> \"bike:2\"","section_id":"examples"},{"id":"examples-ex2","language":"csharp","code":"long res1 = db.ListLeftPush(\"bikes:repairs\", \"bike:1\");\n        Console.WriteLine(res1);    // >>> 1\n\n        long res2 = db.ListLeftPush(\"bikes:repairs\", \"bike:2\");\n        Console.WriteLine(res2);    // >>> 2\n\n        RedisValue res3 = db.ListRightPop(\"bikes:repairs\");\n        Console.WriteLine(res3);    // >>> \"bike:1\"\n\n        RedisValue res4 = db.ListRightPop(\"bikes:repairs\");\n        Console.WriteLine(res4); // >>> \"bike:2\"","section_id":"examples"},{"id":"examples-ex3","language":"csharp","code":"long res1 = db.ListLeftPush(\"bikes:repairs\", \"bike:1\");\n        Console.WriteLine(res1);    // >>> 1\n\n        long res2 = db.ListLeftPush(\"bikes:repairs\", \"bike:2\");\n        Console.WriteLine(res2);    // >>> 2\n\n        RedisValue res3 = db.ListRightPop(\"bikes:repairs\");\n        Console.WriteLine(res3);    // >>> \"bike:1\"\n\n        RedisValue res4 = db.ListRightPop(\"bikes:repairs\");\n        Console.WriteLine(res4); // >>> \"bike:2\"","section_id":"examples"},{"id":"examples-ex4","language":"csharp","code":"long res1 = db.ListLeftPush(\"bikes:repairs\", \"bike:1\");\n        Console.WriteLine(res1);    // >>> 1\n\n        long res2 = db.ListLeftPush(\"bikes:repairs\", \"bike:2\");\n        Console.WriteLine(res2);    // >>> 2\n\n        RedisValue res3 = db.ListRightPop(\"bikes:repairs\");\n        Console.WriteLine(res3);    // >>> \"bike:1\"\n\n        RedisValue res4 = db.ListRightPop(\"bikes:repairs\");\n        Console.WriteLine(res4); // >>> \"bike:2\"","section_id":"examples"},{"id":"examples-ex5","language":"go","code":"res1, err := rdb.LPush(ctx, \"bikes:repairs\", \"bike:1\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> 1\n\n\tres2, err := rdb.LPush(ctx, \"bikes:repairs\", \"bike:2\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> 2\n\n\tres3, err := rdb.RPop(ctx, \"bikes:repairs\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3) // >>> bike:1\n\n\tres4, err := rdb.RPop(ctx, \"bikes:repairs\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4) // >>> bike:2","section_id":"examples"},{"id":"examples-ex6","language":"java","code":"CompletableFuture<Void> queue = asyncCommands.lpush(\"bikes:repairs\", \"bike:1\").thenCompose(res1 -> {\n                System.out.println(res1); // >>> 1\n\n                return asyncCommands.lpush(\"bikes:repairs\", \"bike:2\");\n            }).thenCompose(res2 -> {\n                System.out.println(res2); // >>> 2\n\n                return asyncCommands.rpop(\"bikes:repairs\");\n            }).thenCompose(res3 -> {\n                System.out.println(res3); // >>> bike:1\n\n                return asyncCommands.rpop(\"bikes:repairs\");\n            })\n                    .thenAccept(System.out::println) // >>> bike:2\n                    .toCompletableFuture();","section_id":"examples"},{"id":"examples-ex7","language":"java","code":"long res1 = jedis.lpush(\"bikes:repairs\", \"bike:1\");\n        System.out.println(res1);  // >>> 1\n\n        long res2 = jedis.lpush(\"bikes:repairs\", \"bike:2\");\n        System.out.println(res2);  // >>> 2\n\n        String res3 = jedis.rpop(\"bikes:repairs\");\n        System.out.println(res3);  // >>> bike:1\n\n        String res4 = jedis.rpop(\"bikes:repairs\");\n        System.out.println(res4); // >>> bike:2","section_id":"examples"},{"id":"examples-ex8","language":"javascript","code":"const res1 = await client.lPush('bikes:repairs', 'bike:1');\nconsole.log(res1);  // 1\n\nconst res2 = await client.lPush('bikes:repairs', 'bike:2');\nconsole.log(res2);  // 2\n\nconst res3 = await client.rPop('bikes:repairs');\nconsole.log(res3);  // bike:1\n\nconst res4 = await client.rPop('bikes:repairs');\nconsole.log(res4);  // bike:2","section_id":"examples"},{"id":"examples-ex9","language":"php","code":"$res1 = $r->lpush('bikes:repairs', 'bike:1');\n        echo $res1 . PHP_EOL;\n        // >>> 1\n\n        $res2 = $r->lpush('bikes:repairs', 'bike:2');\n        echo $res2 . PHP_EOL;\n        // >>> 2\n\n        $res3 = $r->rpop('bikes:repairs');\n        echo $res3 . PHP_EOL;\n        // >>> bike:1\n\n        $res4 = $r->rpop('bikes:repairs');\n        echo $res4 . PHP_EOL;\n        // >>> bike:2","section_id":"examples"},{"id":"examples-ex10","language":"python","code":"res1 = r.lpush(\"bikes:repairs\", \"bike:1\")\nprint(res1)  # >>> 1\n\nres2 = r.lpush(\"bikes:repairs\", \"bike:2\")\nprint(res2)  # >>> 2\n\nres3 = r.rpop(\"bikes:repairs\")\nprint(res3)  # >>> bike:1\n\nres4 = r.rpop(\"bikes:repairs\")\nprint(res4)  # >>> bike:2","section_id":"examples"},{"id":"examples-ex11","language":"ruby","code":"res1 = r.lpush('bikes:repairs', 'bike:1')\nputs res1 # >>> 1\n\nres2 = r.lpush('bikes:repairs', 'bike:2')\nputs res2 # >>> 2\n\nres3 = r.rpop('bikes:repairs')\nputs res3 # >>> bike:1\n\nres4 = r.rpop('bikes:repairs')\nputs res4 # >>> bike:2","section_id":"examples"},{"id":"examples-ex12","language":"rust","code":"if let Ok(res) = r.lpush(\"bikes:repairs\", \"bike:1\").await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 1\n        }\n\n        if let Ok(res) = r.lpush(\"bikes:repairs\", \"bike:2\").await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 2\n        }\n\n        if let Ok(res) = r.rpop(\"bikes:repairs\", None).await {\n            let res: Option<String> = res;\n            print_optional_string(res.clone()); // >>> bike:1\n        }\n\n        if let Ok(res) = r.rpop(\"bikes:repairs\", None).await {\n            let res: Option<String> = res;\n            print_optional_string(res.clone()); // >>> bike:2\n        }","section_id":"examples"},{"id":"examples-ex13","language":"rust","code":"if let Ok(res) = r.lpush(\"bikes:repairs\", \"bike:1\") {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 1\n        }\n\n        if let Ok(res) = r.lpush(\"bikes:repairs\", \"bike:2\") {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 2\n        }\n\n        if let Ok(res) = r.rpop(\"bikes:repairs\", None) {\n            let res: Option<String> = res;\n            print_optional_string(res.clone()); // >>> bike:1\n        }\n\n        if let Ok(res) = r.rpop(\"bikes:repairs\", None) {\n            let res: Option<String> = res;\n            print_optional_string(res.clone()); // >>> bike:2\n        }","section_id":"examples"},{"id":"examples-ex14","language":"plaintext","code":"> LPUSH bikes:repairs bike:1\n(integer) 1\n> LPUSH bikes:repairs bike:2\n(integer) 2\n> LPOP bikes:repairs\n\"bike:2\"\n> LPOP bikes:repairs\n\"bike:1\"","section_id":"examples"},{"id":"examples-ex15","language":"csharp","code":"long res5 = db.ListLeftPush(\"bikes:repairs\", \"bike:1\");\n        Console.WriteLine(res5);    // >>> 1\n\n        long res6 = db.ListLeftPush(\"bikes:repairs\", \"bike:2\");\n        Console.WriteLine(res6);    // >>> 2\n\n        RedisValue res7 = db.ListLeftPop(\"bikes:repairs\");\n        Console.WriteLine(res7);    // >>> \"bike:2\"\n\n        RedisValue res8 = db.ListLeftPop(\"bikes:repairs\");\n        Console.WriteLine(res8);    // >>> \"bike:1\"","section_id":"examples"},{"id":"examples-ex16","language":"csharp","code":"long res5 = db.ListLeftPush(\"bikes:repairs\", \"bike:1\");\n        Console.WriteLine(res5);    // >>> 1\n\n        long res6 = db.ListLeftPush(\"bikes:repairs\", \"bike:2\");\n        Console.WriteLine(res6);    // >>> 2\n\n        RedisValue res7 = db.ListLeftPop(\"bikes:repairs\");\n        Console.WriteLine(res7);    // >>> \"bike:2\"\n\n        RedisValue res8 = db.ListLeftPop(\"bikes:repairs\");\n        Console.WriteLine(res8);    // >>> \"bike:1\"","section_id":"examples"},{"id":"examples-ex17","language":"csharp","code":"long res5 = db.ListLeftPush(\"bikes:repairs\", \"bike:1\");\n        Console.WriteLine(res5);    // >>> 1\n\n        long res6 = db.ListLeftPush(\"bikes:repairs\", \"bike:2\");\n        Console.WriteLine(res6);    // >>> 2\n\n        RedisValue res7 = db.ListLeftPop(\"bikes:repairs\");\n        Console.WriteLine(res7);    // >>> \"bike:2\"\n\n        RedisValue res8 = db.ListLeftPop(\"bikes:repairs\");\n        Console.WriteLine(res8);    // >>> \"bike:1\"","section_id":"examples"},{"id":"examples-ex18","language":"csharp","code":"long res5 = db.ListLeftPush(\"bikes:repairs\", \"bike:1\");\n        Console.WriteLine(res5);    // >>> 1\n\n        long res6 = db.ListLeftPush(\"bikes:repairs\", \"bike:2\");\n        Console.WriteLine(res6);    // >>> 2\n\n        RedisValue res7 = db.ListLeftPop(\"bikes:repairs\");\n        Console.WriteLine(res7);    // >>> \"bike:2\"\n\n        RedisValue res8 = db.ListLeftPop(\"bikes:repairs\");\n        Console.WriteLine(res8);    // >>> \"bike:1\"","section_id":"examples"},{"id":"examples-ex19","language":"go","code":"res5, err := rdb.LPush(ctx, \"bikes:repairs\", \"bike:1\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res5) // >>> 1\n\n\tres6, err := rdb.LPush(ctx, \"bikes:repairs\", \"bike:2\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res6) // >>> 2\n\n\tres7, err := rdb.LPop(ctx, \"bikes:repairs\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res7) // >>> bike:2\n\n\tres8, err := rdb.LPop(ctx, \"bikes:repairs\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res8) // >>> bike:1","section_id":"examples"},{"id":"examples-ex20","language":"java","code":"CompletableFuture<Void> stack = asyncCommands.lpush(\"bikes:repairs\", \"bike:1\").thenCompose(res4 -> {\n                System.out.println(res4); // >>> 1\n\n                return asyncCommands.lpush(\"bikes:repairs\", \"bike:2\");\n            }).thenCompose(res5 -> {\n                System.out.println(res5); // >>> 2\n\n                return asyncCommands.lpop(\"bikes:repairs\");\n            }).thenCompose(res6 -> {\n                System.out.println(res6); // >>> bike:2\n\n                return asyncCommands.lpop(\"bikes:repairs\");\n            })\n                    .thenAccept(System.out::println) // >>> bike:1\n                    .toCompletableFuture();","section_id":"examples"},{"id":"examples-ex21","language":"java","code":"long res5 = jedis.lpush(\"bikes:repairs\", \"bike:1\");\n        System.out.println(res5);  // >>> 1\n\n        long res6 = jedis.lpush(\"bikes:repairs\", \"bike:2\");\n        System.out.println(res6);  // >>> 2\n\n        String res7 = jedis.lpop(\"bikes:repairs\");\n        System.out.println(res7);  // >>> bike:2\n\n        String res8 = jedis.lpop(\"bikes:repairs\");\n        System.out.println(res8);  // >>> bike:1","section_id":"examples"},{"id":"examples-ex22","language":"javascript","code":"const res5 = await client.lPush('bikes:repairs', 'bike:1');\nconsole.log(res5);  // 1\n\nconst res6 = await client.lPush('bikes:repairs', 'bike:2');\nconsole.log(res6); // 2\n\nconst res7 = await client.lPop('bikes:repairs');\nconsole.log(res7);  // bike:2\n\nconst res8 = await client.lPop('bikes:repairs');\nconsole.log(res8);  // bike:1","section_id":"examples"},{"id":"examples-ex23","language":"php","code":"$res5 = $r->lpush('bikes:repairs', 'bike:1');\n        echo $res5 . PHP_EOL;\n        // >>> 1\n\n        $res6 = $r->lpush('bikes:repairs', 'bike:2');\n        echo $res6 . PHP_EOL;\n        // >>> 2\n\n        $res7 = $r->lpop('bikes:repairs');\n        echo $res7 . PHP_EOL;\n        // >>> bike:2\n\n        $res8 = $r->lpop('bikes:repairs');\n        echo $res8 . PHP_EOL;\n        // >>> bike:1","section_id":"examples"},{"id":"examples-ex24","language":"python","code":"res5 = r.lpush(\"bikes:repairs\", \"bike:1\")\nprint(res5)  # >>> 1\n\nres6 = r.lpush(\"bikes:repairs\", \"bike:2\")\nprint(res6)  # >>> 2\n\nres7 = r.lpop(\"bikes:repairs\")\nprint(res7)  # >>> bike:2\n\nres8 = r.lpop(\"bikes:repairs\")\nprint(res8)  # >>> bike:1","section_id":"examples"},{"id":"examples-ex25","language":"ruby","code":"res5 = r.lpush('bikes:repairs', 'bike:1')\nputs res5 # >>> 1\n\nres6 = r.lpush('bikes:repairs', 'bike:2')\nputs res6 # >>> 2\n\nres7 = r.lpop('bikes:repairs')\nputs res7 # >>> bike:2\n\nres8 = r.lpop('bikes:repairs')\nputs res8 # >>> bike:1","section_id":"examples"},{"id":"examples-ex26","language":"rust","code":"if let Ok(res) = r.lpush(\"bikes:repairs\", \"bike:1\").await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 1\n        }\n\n        if let Ok(res) = r.lpush(\"bikes:repairs\", \"bike:2\").await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 2\n        }\n\n        if let Ok(res) = r.lpop(\"bikes:repairs\", None).await {\n            let res: Option<String> = res;\n            print_optional_string(res.clone()); // >>> bike:2\n        }\n\n        if let Ok(res) = r.lpop(\"bikes:repairs\", None).await {\n            let res: Option<String> = res;\n            print_optional_string(res.clone()); // >>> bike:1\n        }","section_id":"examples"},{"id":"examples-ex27","language":"rust","code":"if let Ok(res) = r.lpush(\"bikes:repairs\", \"bike:1\") {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 1\n        }\n\n        if let Ok(res) = r.lpush(\"bikes:repairs\", \"bike:2\") {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 2\n        }\n\n        if let Ok(res) = r.lpop(\"bikes:repairs\", None) {\n            let res: Option<String> = res;\n            print_optional_string(res.clone()); // >>> bike:2\n        }\n\n        if let Ok(res) = r.lpop(\"bikes:repairs\", None) {\n            let res: Option<String> = res;\n            print_optional_string(res.clone()); // >>> bike:1\n        }","section_id":"examples"},{"id":"examples-ex28","language":"plaintext","code":"> LLEN bikes:repairs\n(integer) 0","section_id":"examples"},{"id":"examples-ex29","language":"csharp","code":"long res9 = db.ListLength(\"bikes:repairs\");\n        Console.WriteLine(res9);    // >>> 0","section_id":"examples"},{"id":"examples-ex30","language":"csharp","code":"long res9 = db.ListLength(\"bikes:repairs\");\n        Console.WriteLine(res9);    // >>> 0","section_id":"examples"},{"id":"examples-ex31","language":"csharp","code":"long res9 = db.ListLength(\"bikes:repairs\");\n        Console.WriteLine(res9);    // >>> 0","section_id":"examples"},{"id":"examples-ex32","language":"csharp","code":"long res9 = db.ListLength(\"bikes:repairs\");\n        Console.WriteLine(res9);    // >>> 0","section_id":"examples"},{"id":"examples-ex33","language":"go","code":"res9, err := rdb.LLen(ctx, \"bikes:repairs\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res9) // >>> 0","section_id":"examples"},{"id":"examples-ex34","language":"java","code":"CompletableFuture<Void> llen = asyncCommands.llen(\"bikes:repairs\")\n                    .thenAccept(System.out::println) // >>> 0\n                    .toCompletableFuture();","section_id":"examples"},{"id":"examples-ex35","language":"java","code":"long res9 = jedis.llen(\"bikes:repairs\");\n        System.out.println(res9);  // >>> 0","section_id":"examples"},{"id":"examples-ex36","language":"javascript","code":"const res9 = await client.lLen('bikes:repairs');\nconsole.log(res9); // 0","section_id":"examples"},{"id":"examples-ex37","language":"php","code":"$res9 = $r->llen('bikes:repairs');\n        echo $res9 . PHP_EOL;\n        // >>> 0","section_id":"examples"},{"id":"examples-ex38","language":"python","code":"res9 = r.llen(\"bikes:repairs\")\nprint(res9)  # >>> 0","section_id":"examples"},{"id":"examples-ex39","language":"ruby","code":"res9 = r.llen('bikes:repairs')\nputs res9 # >>> 0","section_id":"examples"},{"id":"examples-ex40","language":"rust","code":"if let Ok(res) = r.llen(\"bikes:repairs\").await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 0\n        }","section_id":"examples"},{"id":"examples-ex41","language":"rust","code":"if let Ok(res) = r.llen(\"bikes:repairs\") {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 0\n        }","section_id":"examples"},{"id":"examples-ex42","language":"plaintext","code":"> LPUSH bikes:repairs bike:1\n(integer) 1\n> LPUSH bikes:repairs bike:2\n(integer) 2\n> LMOVE bikes:repairs bikes:finished LEFT LEFT\n\"bike:2\"\n> LRANGE bikes:repairs 0 -1\n1) \"bike:1\"\n> LRANGE bikes:finished 0 -1\n1) \"bike:2\"","section_id":"examples"},{"id":"examples-ex43","language":"csharp","code":"long res10 = db.ListLeftPush(\"{bikes}:repairs\", \"bike:1\");\n        Console.WriteLine(res10);   // >>> 1\n\n        long res11 = db.ListLeftPush(\"{bikes}:repairs\", \"bike:2\");\n        Console.WriteLine(res11);   // >>> 2\n\n        RedisValue res12 = db.ListMove(\"{bikes}:repairs\", \"{bikes}:finished\", ListSide.Left, ListSide.Left);\n        Console.Write(res12);   // >>> \"bike:2\"\n\n        RedisValue[] res13 = db.ListRange(\"{bikes}:repairs\", 0, -1);\n        Console.WriteLine(string.Join(\", \", res13));    // >>> \"bike:1\"\n\n        RedisValue[] res14 = db.ListRange(\"{bikes}:finished\", 0, -1);\n        Console.WriteLine(string.Join(\", \", res14));    // >>> \"bike:2\"","section_id":"examples"},{"id":"examples-ex44","language":"csharp","code":"long res10 = db.ListLeftPush(\"{bikes}:repairs\", \"bike:1\");\n        Console.WriteLine(res10);   // >>> 1\n\n        long res11 = db.ListLeftPush(\"{bikes}:repairs\", \"bike:2\");\n        Console.WriteLine(res11);   // >>> 2\n\n        RedisValue res12 = db.ListMove(\"{bikes}:repairs\", \"{bikes}:finished\", ListSide.Left, ListSide.Left);\n        Console.Write(res12);   // >>> \"bike:2\"\n\n        RedisValue[] res13 = db.ListRange(\"{bikes}:repairs\", 0, -1);\n        Console.WriteLine(string.Join(\", \", res13));    // >>> \"bike:1\"\n\n        RedisValue[] res14 = db.ListRange(\"{bikes}:finished\", 0, -1);\n        Console.WriteLine(string.Join(\", \", res14));    // >>> \"bike:2\"","section_id":"examples"},{"id":"examples-ex45","language":"csharp","code":"long res10 = db.ListLeftPush(\"{bikes}:repairs\", \"bike:1\");\n        Console.WriteLine(res10);   // >>> 1\n\n        long res11 = db.ListLeftPush(\"{bikes}:repairs\", \"bike:2\");\n        Console.WriteLine(res11);   // >>> 2\n\n        RedisValue res12 = db.ListMove(\"{bikes}:repairs\", \"{bikes}:finished\", ListSide.Left, ListSide.Left);\n        Console.Write(res12);   // >>> \"bike:2\"\n\n        RedisValue[] res13 = db.ListRange(\"{bikes}:repairs\", 0, -1);\n        Console.WriteLine(string.Join(\", \", res13));    // >>> \"bike:1\"\n\n        RedisValue[] res14 = db.ListRange(\"{bikes}:finished\", 0, -1);\n        Console.WriteLine(string.Join(\", \", res14));    // >>> \"bike:2\"","section_id":"examples"},{"id":"examples-ex46","language":"csharp","code":"long res10 = db.ListLeftPush(\"{bikes}:repairs\", \"bike:1\");\n        Console.WriteLine(res10);   // >>> 1\n\n        long res11 = db.ListLeftPush(\"{bikes}:repairs\", \"bike:2\");\n        Console.WriteLine(res11);   // >>> 2\n\n        RedisValue res12 = db.ListMove(\"{bikes}:repairs\", \"{bikes}:finished\", ListSide.Left, ListSide.Left);\n        Console.Write(res12);   // >>> \"bike:2\"\n\n        RedisValue[] res13 = db.ListRange(\"{bikes}:repairs\", 0, -1);\n        Console.WriteLine(string.Join(\", \", res13));    // >>> \"bike:1\"\n\n        RedisValue[] res14 = db.ListRange(\"{bikes}:finished\", 0, -1);\n        Console.WriteLine(string.Join(\", \", res14));    // >>> \"bike:2\"","section_id":"examples"},{"id":"examples-ex47","language":"go","code":"res10, err := rdb.LPush(ctx, \"bikes:repairs\", \"bike:1\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res10) // >>> 1\n\n\tres11, err := rdb.LPush(ctx, \"bikes:repairs\", \"bike:2\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res11) // >>> 2\n\n\tres12, err := rdb.LMove(ctx, \"bikes:repairs\", \"bikes:finished\", \"LEFT\", \"LEFT\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res12) // >>> bike:2\n\n\tres13, err := rdb.LRange(ctx, \"bikes:repairs\", 0, -1).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res13) // >>> [bike:1]\n\n\tres14, err := rdb.LRange(ctx, \"bikes:finished\", 0, -1).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res14) // >>> [bike:2]","section_id":"examples"},{"id":"examples-ex48","language":"java","code":"CompletableFuture<Void> lmovelrange = asyncCommands.lpush(\"bikes:repairs\", \"bike:1\").thenCompose(res7 -> {\n                System.out.println(res7); // >>> 1\n\n                return asyncCommands.lpush(\"bikes:repairs\", \"bike:2\");\n            }).thenCompose(res8 -> {\n                System.out.println(res8); // >>> 2\n\n                return asyncCommands.lmove(\"bikes:repairs\", \"bikes:finished\", LMoveArgs.Builder.leftLeft());\n            }).thenCompose(res9 -> {\n                System.out.println(res9); // >>> bike:2\n\n                return asyncCommands.lrange(\"bikes:repairs\", 0, -1);\n            }).thenCompose(res10 -> {\n                System.out.println(res10); // >>> [bike:1]\n\n                return asyncCommands.lrange(\"bikes:finished\", 0, -1);\n            })\n                    .thenAccept(System.out::println) // >>> [bike:2]\n                    .toCompletableFuture();","section_id":"examples"},{"id":"examples-ex49","language":"java","code":"long res10 = jedis.lpush(\"bikes:repairs\", \"bike:1\");\n        System.out.println(res10);  // >>> 1\n\n        long res11 = jedis.lpush(\"bikes:repairs\", \"bike:2\");\n        System.out.println(res11);  // >>> 2\n\n        String res12 = jedis.lmove(\"bikes:repairs\", \"bikes:finished\", ListDirection.LEFT, ListDirection.LEFT);\n        System.out.println(res12);  // >>> bike:2\n\n        List<String> res13 = jedis.lrange(\"bikes:repairs\", 0, -1);\n        System.out.println(res13);  // >>> [bike:1]\n\n        List<String> res14 = jedis.lrange(\"bikes:finished\", 0, -1);\n        System.out.println(res14);  // >>> [bike:2]","section_id":"examples"},{"id":"examples-ex50","language":"javascript","code":"const res10 = await client.lPush('bikes:repairs', 'bike:1');\nconsole.log(res10);  // 1\n\nconst res11 = await client.lPush('bikes:repairs', 'bike:2');\nconsole.log(res11);  // 2\n\nconst res12 = await client.lMove('bikes:repairs', 'bikes:finished', 'LEFT', 'LEFT');\nconsole.log(res12);  // 'bike:2'\n\nconst res13 = await client.lRange('bikes:repairs', 0, -1);\nconsole.log(res13);  // ['bike:1']\n\nconst res14 = await client.lRange('bikes:finished', 0, -1);\nconsole.log(res14);  // ['bike:2']","section_id":"examples"},{"id":"examples-ex51","language":"php","code":"$res10 = $r->lpush('bikes:repairs', 'bike:1');\n        echo $res10 . PHP_EOL;\n        // >>> 1\n\n        $res11 = $r->lpush('bikes:repairs', 'bike:2');\n        echo $res11 . PHP_EOL;\n        // >>> 2\n\n        $res12 = $r->lmove('bikes:repairs', 'bikes:finished', 'LEFT', 'LEFT');\n        echo $res12 . PHP_EOL;\n        // >>> 'bike:2'\n\n        $res13 = $r->lrange('bikes:repairs', 0, -1);\n        echo json_encode($res13) . PHP_EOL;\n        // >>> ['bike:1']\n\n        $res14 = $r->lrange('bikes:finished', 0, -1);\n        echo json_encode($res14) . PHP_EOL;\n        // >>> ['bike:2']","section_id":"examples"},{"id":"examples-ex52","language":"python","code":"res10 = r.lpush(\"bikes:repairs\", \"bike:1\")\nprint(res10)  # >>> 1\n\nres11 = r.lpush(\"bikes:repairs\", \"bike:2\")\nprint(res11)  # >>> 2\n\nres12 = r.lmove(\"bikes:repairs\", \"bikes:finished\", \"LEFT\", \"LEFT\")\nprint(res12)  # >>> 'bike:2'\n\nres13 = r.lrange(\"bikes:repairs\", 0, -1)\nprint(res13)  # >>> ['bike:1']\n\nres14 = r.lrange(\"bikes:finished\", 0, -1)\nprint(res14)  # >>> ['bike:2']","section_id":"examples"},{"id":"examples-ex53","language":"ruby","code":"res10 = r.lpush('bikes:repairs', 'bike:1')\nputs res10 # >>> 1\n\nres11 = r.lpush('bikes:repairs', 'bike:2')\nputs res11 # >>> 2\n\nres12 = r.lmove('bikes:repairs', 'bikes:finished', 'LEFT', 'LEFT')\nputs res12 # >>> bike:2\n\nres13 = r.lrange('bikes:repairs', 0, -1)\nputs res13.inspect # >>> [\"bike:1\"]\n\nres14 = r.lrange('bikes:finished', 0, -1)\nputs res14.inspect # >>> [\"bike:2\"]","section_id":"examples"},{"id":"examples-ex54","language":"rust","code":"if let Ok(res) = r.lpush(\"bikes:repairs\", \"bike:1\").await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 1\n        }\n\n        if let Ok(res) = r.lpush(\"bikes:repairs\", \"bike:2\").await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 2\n        }\n\n        if let Ok(res) = r\n            .lmove(\n                \"bikes:repairs\",\n                \"bikes:finished\",\n                Direction::Left,\n                Direction::Left,\n            )\n            .await\n        {\n            let res: String = res;\n            println!(\"{res}\"); // >>> bike:2\n        }\n\n        if let Ok(res) = r.lrange(\"bikes:repairs\", 0, -1).await {\n            let res: Vec<String> = res;\n            println!(\"{res:?}\"); // >>> [\"bike:1\"]\n        }\n\n        if let Ok(res) = r.lrange(\"bikes:finished\", 0, -1).await {\n            let res: Vec<String> = res;\n            println!(\"{res:?}\"); // >>> [\"bike:2\"]\n        }","section_id":"examples"},{"id":"examples-ex55","language":"rust","code":"if let Ok(res) = r.lpush(\"bikes:repairs\", \"bike:1\") {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 1\n        }\n\n        if let Ok(res) = r.lpush(\"bikes:repairs\", \"bike:2\") {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 2\n        }\n\n        if let Ok(res) = r.lmove(\n            \"bikes:repairs\",\n            \"bikes:finished\",\n            Direction::Left,\n            Direction::Left,\n        ) {\n            let res: String = res;\n            println!(\"{res}\"); // >>> bike:2\n        }\n\n        if let Ok(res) = r.lrange(\"bikes:repairs\", 0, -1) {\n            let res: Vec<String> = res;\n            println!(\"{res:?}\"); // >>> [\"bike:1\"]\n        }\n\n        if let Ok(res) = r.lrange(\"bikes:finished\", 0, -1) {\n            let res: Vec<String> = res;\n            println!(\"{res:?}\"); // >>> [\"bike:2\"]\n        }","section_id":"examples"},{"id":"examples-ex56","language":"plaintext","code":"> DEL bikes:repairs\n(integer) 1\n> RPUSH bikes:repairs bike:1 bike:2 bike:3 bike:4 bike:5\n(integer) 5\n> LTRIM bikes:repairs 0 2\nOK\n> LRANGE bikes:repairs 0 -1\n1) \"bike:1\"\n2) \"bike:2\"\n3) \"bike:3\"","section_id":"examples"},{"id":"examples-ex57","language":"csharp","code":"long res49 = db.ListLeftPush(\"bikes:repairs\", [\"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\", \"bike:5\"]);\n        Console.WriteLine(res49);   // >>> 5\n\n        db.ListTrim(\"bikes:repairs\", 0, 2);\n        RedisValue[] res50 = db.ListRange(\"bikes:repairs\", 0, -1);\n        Console.WriteLine(string.Join(\", \", res50));    // >>> \"bike:5, bike:4, bike:3\"","section_id":"examples"},{"id":"examples-ex58","language":"csharp","code":"db.KeyDelete(\"bikes:repairs\");\n\n        long res49 = db.ListLeftPush(\"bikes:repairs\", [\"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\", \"bike:5\"]);\n        Console.WriteLine(res49);   // >>> 5\n\n        db.ListTrim(\"bikes:repairs\", 0, 2);\n        RedisValue[] res50 = db.ListRange(\"bikes:repairs\", 0, -1);\n        Console.WriteLine(string.Join(\", \", res50));    // >>> \"bike:5, bike:4, bike:3\"","section_id":"examples"},{"id":"examples-ex59","language":"csharp","code":"long res49 = db.ListLeftPush(\"bikes:repairs\", [\"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\", \"bike:5\"]);\n        Console.WriteLine(res49);   // >>> 5\n\n        db.ListTrim(\"bikes:repairs\", 0, 2);\n        RedisValue[] res50 = db.ListRange(\"bikes:repairs\", 0, -1);\n        Console.WriteLine(string.Join(\", \", res50));    // >>> \"bike:5, bike:4, bike:3\"","section_id":"examples"},{"id":"examples-ex60","language":"csharp","code":"db.KeyDelete(\"bikes:repairs\");\n\n        long res49 = db.ListLeftPush(\"bikes:repairs\", [\"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\", \"bike:5\"]);\n        Console.WriteLine(res49);   // >>> 5\n\n        db.ListTrim(\"bikes:repairs\", 0, 2);\n        RedisValue[] res50 = db.ListRange(\"bikes:repairs\", 0, -1);\n        Console.WriteLine(string.Join(\", \", res50));    // >>> \"bike:5, bike:4, bike:3\"","section_id":"examples"},{"id":"examples-ex61","language":"go","code":"rdb.Del(ctx, \"bikes:repairs\")\n\n\tres51, err := rdb.LPush(ctx, \"bikes:repairs\", \"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\", \"bike:5\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res51) // >>> 5\n\n\tres52, err := rdb.LTrim(ctx, \"bikes:repairs\", 0, 2).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res52) // >>> OK\n\n\tres53, err := rdb.LRange(ctx, \"bikes:repairs\", 0, -1).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res53) // >>> [bike:5 bike:4 bike:3]","section_id":"examples"},{"id":"examples-ex62","language":"java","code":"CompletableFuture<Void> ltrim1 = asyncCommands\n                    .lpush(\"bikes:repairs\", \"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\", \"bike:5\").thenCompose(res37 -> {\n                        System.out.println(res37); // >>> 5\n\n                        return asyncCommands.ltrim(\"bikes:repairs\", 0, 2);\n                    }).thenCompose(res38 -> {\n                        System.out.println(res38); // >>> OK\n\n                        return asyncCommands.lrange(\"bikes:repairs\", 0, -1);\n                    })\n                    .thenAccept(System.out::println) // >>> [bike:5, bike:4, bike:3]\n                    .toCompletableFuture();","section_id":"examples"},{"id":"examples-ex63","language":"java","code":"jedis.del(\"bikes:repairs\");\n\n        long res48 = jedis.lpush(\"bikes:repairs\", \"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\", \"bike:5\");\n        System.out.println(res48);  // >>> 5\n\n        String res49 = jedis.ltrim(\"bikes:repairs\", 0, 2);\n        System.out.println(res49);  // >>> OK\n\n        List<String> res50 = jedis.lrange(\"bikes:repairs\", 0, -1);\n        System.out.println(res50);  // >>> [bike:5, bike:4, bike:3]","section_id":"examples"},{"id":"examples-ex64","language":"javascript","code":"await client.del('bikes:repairs');\n\nconst res48 = await client.lPush(\n  'bikes:repairs', ['bike:1', 'bike:2', 'bike:3', 'bike:4', 'bike:5']\n);\nconsole.log(res48);  // 5\n\nconst res49 = await client.lTrim('bikes:repairs', 0, 2);\nconsole.log(res49);  // 'OK'\n\nconst res50 = await client.lRange('bikes:repairs', 0, -1);\nconsole.log(res50);  // ['bike:5', 'bike:4', 'bike:3']","section_id":"examples"},{"id":"examples-ex65","language":"php","code":"$res48 = $r->lpush('bikes:repairs', 'bike:1', 'bike:2', 'bike:3', 'bike:4', 'bike:5');\n        echo $res48 . PHP_EOL;\n        // >>> 5\n\n        $res49 = $r->ltrim('bikes:repairs', 0, 2);\n        echo $res49 . PHP_EOL;\n        // >>> True\n\n        $res50 = $r->lrange('bikes:repairs', 0, -1);\n        echo json_encode($res50) . PHP_EOL;\n        // >>> ['bike:5', 'bike:4', 'bike:3']","section_id":"examples"},{"id":"examples-ex66","language":"python","code":"r.delete(\"bikes:repairs\")\n\nres48 = r.lpush(\"bikes:repairs\", \"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\", \"bike:5\")\nprint(res48)  # >>> 5\n\nres49 = r.ltrim(\"bikes:repairs\", 0, 2)\nprint(res49)  # >>> True\n\nres50 = r.lrange(\"bikes:repairs\", 0, -1)\nprint(res50)  # >>> ['bike:5', 'bike:4', 'bike:3']","section_id":"examples"},{"id":"examples-ex67","language":"ruby","code":"r.del('bikes:repairs')\n\nres15 = r.rpush('bikes:repairs', ['bike:1', 'bike:2', 'bike:3', 'bike:4', 'bike:5'])\nputs res15 # >>> 5\n\nres16 = r.ltrim('bikes:repairs', 0, 2)\nputs res16 # >>> OK\n\nres17 = r.lrange('bikes:repairs', 0, -1)\nputs res17.inspect # >>> [\"bike:1\", \"bike:2\", \"bike:3\"]","section_id":"examples"},{"id":"examples-ex68","language":"rust","code":"if let Ok(res) = r.del(\"bikes:repairs\").await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 1\n        }\n\n        if let Ok(res) = r\n            .rpush(\n                \"bikes:repairs\",\n                &[\"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\", \"bike:5\"],\n            )\n            .await\n        {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 5\n        }\n\n        if let Ok(res) = r.ltrim(\"bikes:repairs\", 0, 2).await {\n            let res: () = res;\n            let _ = res;\n            println!(\"OK\"); // >>> OK\n        }\n\n        if let Ok(res) = r.lrange(\"bikes:repairs\", 0, -1).await {\n            let res: Vec<String> = res;\n            println!(\"{res:?}\"); // >>> [\"bike:1\", \"bike:2\", \"bike:3\"]\n        }","section_id":"examples"},{"id":"examples-ex69","language":"rust","code":"if let Ok(res) = r.del(\"bikes:repairs\") {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 1\n        }\n\n        if let Ok(res) = r.rpush(\"bikes:repairs\", &[\"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\", \"bike:5\"]) {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 5\n        }\n\n        if let Ok(res) = r.ltrim(\"bikes:repairs\", 0, 2) {\n            let res: () = res;\n            let _ = res;\n            println!(\"OK\"); // >>> OK\n        }\n\n        if let Ok(res) = r.lrange(\"bikes:repairs\", 0, -1) {\n            let res: Vec<String> = res;\n            println!(\"{res:?}\"); // >>> [\"bike:1\", \"bike:2\", \"bike:3\"]\n        }","section_id":"examples"},{"id":"first-steps-with-redis-lists-ex0","language":"plaintext","code":"> DEL bikes:repairs\n(integer) 1\n> RPUSH bikes:repairs bike:1\n(integer) 1\n> RPUSH bikes:repairs bike:2\n(integer) 2\n> LPUSH bikes:repairs bike:important_bike\n(integer) 3\n> LRANGE bikes:repairs 0 -1\n1) \"bike:important_bike\"\n2) \"bike:1\"\n3) \"bike:2\"","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex1","language":"csharp","code":"long res15 = db.ListRightPush(\"bikes:repairs\", \"bike:1\");\n        Console.WriteLine(res15);   // >>> 1\n\n        long res16 = db.ListRightPush(\"bikes:repairs\", \"bike:2\");\n        Console.WriteLine(res16);   // >>> 2\n\n        long res17 = db.ListLeftPush(\"bikes:repairs\", \"bike:important_bike\");\n        Console.WriteLine(res17);   // >>> 3\n\n        RedisValue[] res18 = db.ListRange(\"bikes:repairs\", 0, -1);\n        Console.WriteLine(string.Join(\", \", res18));    // >>> \"bike:important_bike, bike:1, bike:2\"","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex2","language":"csharp","code":"db.KeyDelete(\"bikes:repairs\");\n\n        long res15 = db.ListRightPush(\"bikes:repairs\", \"bike:1\");\n        Console.WriteLine(res15);   // >>> 1\n\n        long res16 = db.ListRightPush(\"bikes:repairs\", \"bike:2\");\n        Console.WriteLine(res16);   // >>> 2\n\n        long res17 = db.ListLeftPush(\"bikes:repairs\", \"bike:important_bike\");\n        Console.WriteLine(res17);   // >>> 3\n\n        RedisValue[] res18 = db.ListRange(\"bikes:repairs\", 0, -1);\n        Console.WriteLine(string.Join(\", \", res18));    // >>> \"bike:important_bike, bike:1, bike:2\"","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex3","language":"csharp","code":"long res15 = db.ListRightPush(\"bikes:repairs\", \"bike:1\");\n        Console.WriteLine(res15);   // >>> 1\n\n        long res16 = db.ListRightPush(\"bikes:repairs\", \"bike:2\");\n        Console.WriteLine(res16);   // >>> 2\n\n        long res17 = db.ListLeftPush(\"bikes:repairs\", \"bike:important_bike\");\n        Console.WriteLine(res17);   // >>> 3\n\n        RedisValue[] res18 = db.ListRange(\"bikes:repairs\", 0, -1);\n        Console.WriteLine(string.Join(\", \", res18));    // >>> \"bike:important_bike, bike:1, bike:2\"","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex4","language":"csharp","code":"db.KeyDelete(\"bikes:repairs\");\n\n        long res15 = db.ListRightPush(\"bikes:repairs\", \"bike:1\");\n        Console.WriteLine(res15);   // >>> 1\n\n        long res16 = db.ListRightPush(\"bikes:repairs\", \"bike:2\");\n        Console.WriteLine(res16);   // >>> 2\n\n        long res17 = db.ListLeftPush(\"bikes:repairs\", \"bike:important_bike\");\n        Console.WriteLine(res17);   // >>> 3\n\n        RedisValue[] res18 = db.ListRange(\"bikes:repairs\", 0, -1);\n        Console.WriteLine(string.Join(\", \", res18));    // >>> \"bike:important_bike, bike:1, bike:2\"","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex5","language":"go","code":"rdb.Del(ctx, \"bikes:repairs\")\n\n\tres15, err := rdb.RPush(ctx, \"bikes:repairs\", \"bike:1\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res15) // >>> 1\n\n\tres16, err := rdb.RPush(ctx, \"bikes:repairs\", \"bike:2\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res16) // >>> 2\n\n\tres17, err := rdb.LPush(ctx, \"bikes:repairs\", \"bike:important_bike\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res17) // >>> 3\n\n\tres18, err := rdb.LRange(ctx, \"bikes:repairs\", 0, -1).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res18) // >>> [bike:important_bike bike:1 bike:2]","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex6","language":"java","code":"CompletableFuture<Void> lpushrpush = asyncCommands.rpush(\"bikes:repairs\", \"bike:1\").thenCompose(res11 -> {\n                System.out.println(res11); // >>> 1\n\n                return asyncCommands.rpush(\"bikes:repairs\", \"bike:2\");\n            }).thenCompose(res12 -> {\n                System.out.println(res12); // >>> 2\n\n                return asyncCommands.lpush(\"bikes:repairs\", \"bike:important_bike\");\n            }).thenCompose(res13 -> {\n                System.out.println(res13); // >>> 3\n\n                return asyncCommands.lrange(\"bikes:repairs\", 0, -1);\n            })\n                    .thenAccept(System.out::println)\n                    // >>> [bike:important_bike, bike:1, bike:2]\n                    .toCompletableFuture();","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex7","language":"java","code":"jedis.del(\"bikes:repairs\");\n\n        long res15 = jedis.rpush(\"bikes:repairs\", \"bike:1\");\n        System.out.println(res15);  // >>> 1\n\n        long res16 = jedis.rpush(\"bikes:repairs\", \"bike:2\");\n        System.out.println(res16);  // >>> 2\n\n        long res17 = jedis.lpush(\"bikes:repairs\", \"bike:important_bike\");\n        System.out.println(res17);  // >>> 3\n\n        List<String> res18 = jedis.lrange(\"bikes:repairs\", 0, -1);\n        System.out.println(res18);  // >>> [bike:important_bike, bike:1, bike:2]","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex8","language":"javascript","code":"await client.del('bikes:repairs');\n\nconst res15 = await client.rPush('bikes:repairs', 'bike:1');\nconsole.log(res15);  // 1\n\nconst res16 = await client.rPush('bikes:repairs', 'bike:2');\nconsole.log(res16);  // 2\n\nconst res17 = await client.lPush('bikes:repairs', 'bike:important_bike');\nconsole.log(res17);  // 3\n\nconst res18 = await client.lRange('bikes:repairs', 0, -1);\nconsole.log(res18);  // ['bike:important_bike', 'bike:1', 'bike:2']","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex9","language":"php","code":"$r->del('bikes:repairs');\n        \n        $res15 = $r->rpush('bikes:repairs', 'bike:1');\n        echo $res15 . PHP_EOL;\n        // >>> 1\n\n        $res16 = $r->rpush('bikes:repairs', 'bike:2');\n        echo $res16 . PHP_EOL;\n        // >>> 2\n\n        $res17 = $r->lpush('bikes:repairs', 'bike:important_bike');\n        echo $res17 . PHP_EOL;\n        // >>> 3\n\n        $res18 = $r->lrange('bikes:repairs', 0, -1);\n        echo json_encode($res18) . PHP_EOL;\n        // >>> ['bike:important_bike', 'bike:1', 'bike:2']","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex10","language":"python","code":"r.delete(\"bikes:repairs\")\n\nres15 = r.rpush(\"bikes:repairs\", \"bike:1\")\nprint(res15)  # >>> 1\n\nres16 = r.rpush(\"bikes:repairs\", \"bike:2\")\nprint(res16)  # >>> 2\n\nres17 = r.lpush(\"bikes:repairs\", \"bike:important_bike\")\nprint(res17)  # >>> 3\n\nres18 = r.lrange(\"bikes:repairs\", 0, -1)\nprint(res18)  # >>> ['bike:important_bike', 'bike:1', 'bike:2']","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex11","language":"ruby","code":"r.del('bikes:repairs')\n\nres18 = r.rpush('bikes:repairs', 'bike:1')\nputs res18 # >>> 1\n\nres19 = r.rpush('bikes:repairs', 'bike:2')\nputs res19 # >>> 2\n\nres20 = r.lpush('bikes:repairs', 'bike:important_bike')\nputs res20 # >>> 3\n\nres21 = r.lrange('bikes:repairs', 0, -1)\nputs res21.inspect # >>> [\"bike:important_bike\", \"bike:1\", \"bike:2\"]","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex12","language":"rust","code":"let _: usize = r.del(\"bikes:repairs\").await.unwrap_or(0);\n\n        if let Ok(res) = r.rpush(\"bikes:repairs\", \"bike:1\").await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 1\n        }\n\n        if let Ok(res) = r.rpush(\"bikes:repairs\", \"bike:2\").await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 2\n        }\n\n        if let Ok(res) = r.lpush(\"bikes:repairs\", \"bike:important_bike\").await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 3\n        }\n\n        if let Ok(res) = r.lrange(\"bikes:repairs\", 0, -1).await {\n            let res: Vec<String> = res;\n            println!(\"{res:?}\"); // >>> [\"bike:important_bike\", \"bike:1\", \"bike:2\"]\n        }","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex13","language":"rust","code":"let _: usize = r.del(\"bikes:repairs\").unwrap_or(0);\n\n        if let Ok(res) = r.rpush(\"bikes:repairs\", \"bike:1\") {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 1\n        }\n\n        if let Ok(res) = r.rpush(\"bikes:repairs\", \"bike:2\") {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 2\n        }\n\n        if let Ok(res) = r.lpush(\"bikes:repairs\", \"bike:important_bike\") {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 3\n        }\n\n        if let Ok(res) = r.lrange(\"bikes:repairs\", 0, -1) {\n            let res: Vec<String> = res;\n            println!(\"{res:?}\"); // >>> [\"bike:important_bike\", \"bike:1\", \"bike:2\"]\n        }","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex14","language":"plaintext","code":"> DEL bikes:repairs\n(integer) 1\n> RPUSH bikes:repairs bike:1 bike:2 bike:3\n(integer) 3\n> LPUSH bikes:repairs bike:important_bike bike:very_important_bike\n> LRANGE bikes:repairs 0 -1\n1) \"bike:very_important_bike\"\n2) \"bike:important_bike\"\n3) \"bike:1\"\n4) \"bike:2\"\n5) \"bike:3\"","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex15","language":"csharp","code":"long res19 = db.ListRightPush(\"bikes:repairs\", [\"bike:1\", \"bike:2\", \"bike:3\"]);\n        Console.WriteLine(res19);   // >>> 3\n\n        long res20 = db.ListLeftPush(\"bikes:repairs\", [\"bike:important_bike\", \"bike:very_important_bike\"]);\n        Console.WriteLine(res20);   // >>> 5\n\n        RedisValue[] res21 = db.ListRange(\"bikes:repairs\", 0, -1);\n        Console.WriteLine(string.Join(\", \", res21));\n        // >>> \"bike:very_important_bike, bike:important_bike, bike:1, bike:2, bike:3\"","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex16","language":"csharp","code":"db.KeyDelete(\"bikes:repairs\");\n        \n        long res19 = db.ListRightPush(\"bikes:repairs\", [\"bike:1\", \"bike:2\", \"bike:3\"]);\n        Console.WriteLine(res19);   // >>> 3\n\n        long res20 = db.ListLeftPush(\"bikes:repairs\", [\"bike:important_bike\", \"bike:very_important_bike\"]);\n        Console.WriteLine(res20);   // >>> 5\n\n        RedisValue[] res21 = db.ListRange(\"bikes:repairs\", 0, -1);\n        Console.WriteLine(string.Join(\", \", res21));\n        // >>> \"bike:very_important_bike, bike:important_bike, bike:1, bike:2, bike:3\"","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex17","language":"csharp","code":"long res19 = db.ListRightPush(\"bikes:repairs\", [\"bike:1\", \"bike:2\", \"bike:3\"]);\n        Console.WriteLine(res19);   // >>> 3\n\n        long res20 = db.ListLeftPush(\"bikes:repairs\", [\"bike:important_bike\", \"bike:very_important_bike\"]);\n        Console.WriteLine(res20);   // >>> 5\n\n        RedisValue[] res21 = db.ListRange(\"bikes:repairs\", 0, -1);\n        Console.WriteLine(string.Join(\", \", res21));\n        // >>> \"bike:very_important_bike, bike:important_bike, bike:1, bike:2, bike:3\"","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex18","language":"csharp","code":"db.KeyDelete(\"bikes:repairs\");\n        \n        long res19 = db.ListRightPush(\"bikes:repairs\", [\"bike:1\", \"bike:2\", \"bike:3\"]);\n        Console.WriteLine(res19);   // >>> 3\n\n        long res20 = db.ListLeftPush(\"bikes:repairs\", [\"bike:important_bike\", \"bike:very_important_bike\"]);\n        Console.WriteLine(res20);   // >>> 5\n\n        RedisValue[] res21 = db.ListRange(\"bikes:repairs\", 0, -1);\n        Console.WriteLine(string.Join(\", \", res21));\n        // >>> \"bike:very_important_bike, bike:important_bike, bike:1, bike:2, bike:3\"","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex19","language":"go","code":"rdb.Del(ctx, \"bikes:repairs\")\n\n\tres19, err := rdb.RPush(ctx, \"bikes:repairs\", \"bike:1\", \"bike:2\", \"bike:3\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res19) // >>> 3\n\n\tres20, err := rdb.LPush(ctx, \"bikes:repairs\", \"bike:important_bike\", \"bike:very_important_bike\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res20) // >>> 5\n\n\tres21, err := rdb.LRange(ctx, \"bikes:repairs\", 0, -1).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res21) // >>> [bike:very_important_bike bike:important_bike bike:1 bike:2 bike:3]","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex20","language":"java","code":"CompletableFuture<Void> variadic = asyncCommands.rpush(\"bikes:repairs\", \"bike:1\", \"bike:2\", \"bike:3\")\n                    .thenCompose(res14 -> {\n                        System.out.println(res14); // >>> 3\n\n                        return asyncCommands.lpush(\"bikes:repairs\", \"bike:important_bike\", \"bike:very_important_bike\");\n                    }).thenCompose(res15 -> {\n                        System.out.println(res15); // >>> 5\n\n                        return asyncCommands.lrange(\"bikes:repairs\", 0, -1);\n                    })\n                    .thenAccept(System.out::println)\n                    // >>> [bike:very_important_bike, bike:important_bike, bike:1, bike:2, bike:3]\n                    .toCompletableFuture();","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex21","language":"java","code":"jedis.del(\"bikes:repairs\");\n        \n        long res19 = jedis.rpush(\"bikes:repairs\", \"bike:1\", \"bike:2\", \"bike:3\");\n        System.out.println(res19);  // >>> 3\n\n        long res20 = jedis.lpush(\"bikes:repairs\", \"bike:important_bike\", \"bike:very_important_bike\");\n        System.out.println(res20);  // >>> 5\n\n        List<String> res21 = jedis.lrange(\"bikes:repairs\", 0, -1);\n        System.out.println(res21);  // >>> [bike:very_important_bike, bike:important_bike, bike:1, bike:2, bike:3]","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex22","language":"javascript","code":"await client.del('bikes:repairs');\n\nconst res19 = await client.rPush('bikes:repairs', ['bike:1', 'bike:2', 'bike:3']);\nconsole.log(res19);  // 3\n\nconst res20 = await client.lPush(\n  'bikes:repairs', ['bike:important_bike', 'bike:very_important_bike']\n);\nconsole.log(res20);  // 5\n\nconst res21 = await client.lRange('bikes:repairs', 0, -1);\nconsole.log(res21);  // ['bike:very_important_bike', 'bike:important_bike', 'bike:1', 'bike:2', 'bike:3']","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex23","language":"php","code":"$r->del('bikes:repairs');\n\n        $res19 = $r->rpush('bikes:repairs', 'bike:1', 'bike:2', 'bike:3');\n        echo $res19 . PHP_EOL;\n        // >>> 3\n\n        $res20 = $r->lpush('bikes:repairs', 'bike:important_bike', 'bike:very_important_bike');\n        echo $res20 . PHP_EOL;\n        // >>> 5\n\n        $res21 = $r->lrange('bikes:repairs', 0, -1);\n        echo json_encode($res21) . PHP_EOL;\n        // >>> ['bike:very_important_bike', 'bike:important_bike', 'bike:1', ...","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex24","language":"python","code":"r.delete(\"bikes:repairs\")\n\nres19 = r.rpush(\"bikes:repairs\", \"bike:1\", \"bike:2\", \"bike:3\")\nprint(res19)  # >>> 3\n\nres20 = r.lpush(\"bikes:repairs\", \"bike:important_bike\", \"bike:very_important_bike\")\nprint(res20)  # >>> 5\n\nres21 = r.lrange(\"bikes:repairs\", 0, -1)\nprint(\n    res21\n)  # >>> ['bike:very_important_bike', 'bike:important_bike', 'bike:1', ...","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex25","language":"ruby","code":"r.del('bikes:repairs')\n\nres22 = r.rpush('bikes:repairs', ['bike:1', 'bike:2', 'bike:3'])\nputs res22 # >>> 3\n\nres23 = r.lpush('bikes:repairs', ['bike:important_bike', 'bike:very_important_bike'])\nputs res23 # >>> 5\n\nres24 = r.lrange('bikes:repairs', 0, -1)\nputs res24.inspect\n# >>> [\"bike:very_important_bike\", \"bike:important_bike\", \"bike:1\", \"bike:2\", \"bike:3\"]","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex26","language":"rust","code":"let _: usize = r.del(\"bikes:repairs\").await.unwrap_or(0);\n\n        if let Ok(res) = r.rpush(\"bikes:repairs\", &[\"bike:1\", \"bike:2\", \"bike:3\"]).await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 3\n        }\n\n        if let Ok(res) = r\n            .lpush(\"bikes:repairs\", &[\"bike:important_bike\", \"bike:very_important_bike\"])\n            .await\n        {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 5\n        }\n\n        if let Ok(res) = r.lrange(\"bikes:repairs\", 0, -1).await {\n            let res: Vec<String> = res;\n            println!(\"{res:?}\");\n            // >>> [\"bike:very_important_bike\", \"bike:important_bike\", \"bike:1\", \"bike:2\", \"bike:3\"]\n        }","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex27","language":"rust","code":"let _: usize = r.del(\"bikes:repairs\").unwrap_or(0);\n\n        if let Ok(res) = r.rpush(\"bikes:repairs\", &[\"bike:1\", \"bike:2\", \"bike:3\"]) {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 3\n        }\n\n        if let Ok(res) = r.lpush(\"bikes:repairs\", &[\"bike:important_bike\", \"bike:very_important_bike\"]) {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 5\n        }\n\n        if let Ok(res) = r.lrange(\"bikes:repairs\", 0, -1) {\n            let res: Vec<String> = res;\n            println!(\"{res:?}\");\n            // >>> [\"bike:very_important_bike\", \"bike:important_bike\", \"bike:1\", \"bike:2\", \"bike:3\"]\n        }","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex28","language":"plaintext","code":"> DEL bikes:repairs\n(integer) 1\n> RPUSH bikes:repairs bike:1 bike:2 bike:3\n(integer) 3\n> RPOP bikes:repairs\n\"bike:3\"\n> LPOP bikes:repairs\n\"bike:1\"\n> RPOP bikes:repairs\n\"bike:2\"\n> RPOP bikes:repairs\n(nil)","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex29","language":"csharp","code":"long res22 = db.ListRightPush(\"bikes:repairs\", [\"bike:1\", \"bike:2\", \"bike:3\"]);\n        Console.WriteLine(res22);   // >>> 3\n\n        RedisValue res23 = db.ListRightPop(\"bikes:repairs\");\n        Console.WriteLine(res23);   // >>> \"bike:3\"\n\n        RedisValue res24 = db.ListLeftPop(\"bikes:repairs\");\n        Console.WriteLine(res24);   // >>> \"bike:1\"\n\n        RedisValue res25 = db.ListRightPop(\"bikes:repairs\");\n        Console.WriteLine(res25);   // >>> \"bike:2\"\n\n        RedisValue res26 = db.ListRightPop(\"bikes:repairs\");\n        Console.WriteLine(res26);   // >>> <Empty string>","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex30","language":"csharp","code":"db.KeyDelete(\"bikes:repairs\");\n\n        long res22 = db.ListRightPush(\"bikes:repairs\", [\"bike:1\", \"bike:2\", \"bike:3\"]);\n        Console.WriteLine(res22);   // >>> 3\n\n        RedisValue res23 = db.ListRightPop(\"bikes:repairs\");\n        Console.WriteLine(res23);   // >>> \"bike:3\"\n\n        RedisValue res24 = db.ListLeftPop(\"bikes:repairs\");\n        Console.WriteLine(res24);   // >>> \"bike:1\"\n\n        RedisValue res25 = db.ListRightPop(\"bikes:repairs\");\n        Console.WriteLine(res25);   // >>> \"bike:2\"\n\n        RedisValue res26 = db.ListRightPop(\"bikes:repairs\");\n        Console.WriteLine(res26);   // >>> <Empty string>","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex31","language":"csharp","code":"long res22 = db.ListRightPush(\"bikes:repairs\", [\"bike:1\", \"bike:2\", \"bike:3\"]);\n        Console.WriteLine(res22);   // >>> 3\n\n        RedisValue res23 = db.ListRightPop(\"bikes:repairs\");\n        Console.WriteLine(res23);   // >>> \"bike:3\"\n\n        RedisValue res24 = db.ListLeftPop(\"bikes:repairs\");\n        Console.WriteLine(res24);   // >>> \"bike:1\"\n\n        RedisValue res25 = db.ListRightPop(\"bikes:repairs\");\n        Console.WriteLine(res25);   // >>> \"bike:2\"\n\n        RedisValue res26 = db.ListRightPop(\"bikes:repairs\");\n        Console.WriteLine(res26);   // >>> <Empty string>","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex32","language":"csharp","code":"db.KeyDelete(\"bikes:repairs\");\n\n        long res22 = db.ListRightPush(\"bikes:repairs\", [\"bike:1\", \"bike:2\", \"bike:3\"]);\n        Console.WriteLine(res22);   // >>> 3\n\n        RedisValue res23 = db.ListRightPop(\"bikes:repairs\");\n        Console.WriteLine(res23);   // >>> \"bike:3\"\n\n        RedisValue res24 = db.ListLeftPop(\"bikes:repairs\");\n        Console.WriteLine(res24);   // >>> \"bike:1\"\n\n        RedisValue res25 = db.ListRightPop(\"bikes:repairs\");\n        Console.WriteLine(res25);   // >>> \"bike:2\"\n\n        RedisValue res26 = db.ListRightPop(\"bikes:repairs\");\n        Console.WriteLine(res26);   // >>> <Empty string>","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex33","language":"go","code":"rdb.Del(ctx, \"bikes:repairs\")\n\n\tres22, err := rdb.RPush(ctx, \"bikes:repairs\", \"bike:1\", \"bike:2\", \"bike:3\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res22) // >>> 3\n\n\tres23, err := rdb.RPop(ctx, \"bikes:repairs\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res23) // >>> bike:3\n\n\tres24, err := rdb.LPop(ctx, \"bikes:repairs\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res24) // >>> bike:1\n\n\tres25, err := rdb.RPop(ctx, \"bikes:repairs\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res25) // >>> bike:2\n\n\tres26, err := rdb.RPop(ctx, \"bikes:repairs\").Result()\n\n\tif err != nil {\n\t\tfmt.Println(err) // >>> redis: nil\n\t}\n\n\tfmt.Println(res26) // >>> <empty string>","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex34","language":"java","code":"CompletableFuture<Void> lpoprpop = asyncCommands.rpush(\"bikes:repairs\", \"bike:1\", \"bike:2\", \"bike:3\")\n                    .thenCompose(res16 -> {\n                        System.out.println(res16); // >>> 3\n\n                        return asyncCommands.rpop(\"bikes:repairs\");\n                    }).thenCompose(res17 -> {\n                        System.out.println(res17); // >>> bike:3\n\n                        return asyncCommands.lpop(\"bikes:repairs\");\n                    }).thenCompose(res18 -> {\n                        System.out.println(res18); // >>> bike:1\n\n                        return asyncCommands.rpop(\"bikes:repairs\");\n                    }).thenCompose(res19 -> {\n                        System.out.println(res19); // >>> bike:2\n\n                        return asyncCommands.rpop(\"bikes:repairs\");\n                    })\n                    .thenAccept(System.out::println) // >>> null\n                    .toCompletableFuture();","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex35","language":"java","code":"jedis.del(\"bikes:repairs\");\n\n        long res22 = jedis.rpush(\"bikes:repairs\", \"bike:1\", \"bike:2\", \"bike:3\");\n        System.out.println(res22);  // >>> 3\n\n        String res23 = jedis.rpop(\"bikes:repairs\");\n        System.out.println(res23);  // >>> bike:3\n\n        String res24 = jedis.lpop(\"bikes:repairs\");\n        System.out.println(res24);  // >>> bike:1\n\n        String res25 = jedis.rpop(\"bikes:repairs\");\n        System.out.println(res25);  // >>> bike:2\n\n        String res26 = jedis.rpop(\"bikes:repairs\");\n        System.out.println(res26);  // >>> null","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex36","language":"javascript","code":"await client.del('bikes:repairs');\n\nconst res22 = await client.rPush('bikes:repairs', ['bike:1', 'bike:2', 'bike:3']);\nconsole.log(res22);  // 3\n\nconst res23 = await client.rPop('bikes:repairs');\nconsole.log(res23);  // 'bike:3'\n\nconst res24 = await client.lPop('bikes:repairs');\nconsole.log(res24);  // 'bike:1'\n\nconst res25 = await client.rPop('bikes:repairs');\nconsole.log(res25);  // 'bike:2'\n\nconst res26 = await client.rPop('bikes:repairs');\nconsole.log(res26);  // null","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex37","language":"php","code":"$r->del('bikes:repairs');\n        \n        $res22 = $r->rpush('bikes:repairs', 'bike:1', 'bike:2', 'bike:3');\n        echo $res22 . PHP_EOL;\n        // >>> 3\n\n        $res23 = $r->rpop('bikes:repairs');\n        echo $res23 . PHP_EOL;\n        // >>> 'bike:3'\n\n        $res24 = $r->lpop('bikes:repairs');\n        echo $res24 . PHP_EOL;\n        // >>> 'bike:1'\n\n        $res25 = $r->rpop('bikes:repairs');\n        echo $res25 . PHP_EOL;\n        // >>> 'bike:2'\n\n        $res26 = $r->rpop('bikes:repairs');\n        echo $res26 . PHP_EOL;\n        // >>> None","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex38","language":"python","code":"r.delete(\"bikes:repairs\")\n\nres22 = r.rpush(\"bikes:repairs\", \"bike:1\", \"bike:2\", \"bike:3\")\nprint(res22)  # >>> 3\n\nres23 = r.rpop(\"bikes:repairs\")\nprint(res23)  # >>> 'bike:3'\n\nres24 = r.lpop(\"bikes:repairs\")\nprint(res24)  # >>> 'bike:1'\n\nres25 = r.rpop(\"bikes:repairs\")\nprint(res25)  # >>> 'bike:2'\n\nres26 = r.rpop(\"bikes:repairs\")\nprint(res26)  # >>> None","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex39","language":"ruby","code":"r.del('bikes:repairs')\n\nres25 = r.rpush('bikes:repairs', ['bike:1', 'bike:2', 'bike:3'])\nputs res25 # >>> 3\n\nres26 = r.rpop('bikes:repairs')\nputs res26 # >>> bike:3\n\nres27 = r.lpop('bikes:repairs')\nputs res27 # >>> bike:1\n\nres28 = r.rpop('bikes:repairs')\nputs res28 # >>> bike:2\n\nres29 = r.rpop('bikes:repairs')\nputs res29.inspect # >>> nil","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex40","language":"rust","code":"let _: usize = r.del(\"bikes:repairs\").await.unwrap_or(0);\n\n        if let Ok(res) = r.rpush(\"bikes:repairs\", &[\"bike:1\", \"bike:2\", \"bike:3\"]).await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 3\n        }\n\n        if let Ok(res) = r.rpop(\"bikes:repairs\", None).await {\n            let res: Option<String> = res;\n            print_optional_string(res.clone()); // >>> bike:3\n        }\n\n        if let Ok(res) = r.lpop(\"bikes:repairs\", None).await {\n            let res: Option<String> = res;\n            print_optional_string(res.clone()); // >>> bike:1\n        }\n\n        if let Ok(res) = r.rpop(\"bikes:repairs\", None).await {\n            let res: Option<String> = res;\n            print_optional_string(res.clone()); // >>> bike:2\n        }\n\n        if let Ok(res) = r.rpop(\"bikes:repairs\", None).await {\n            let res: Option<String> = res;\n            print_optional_string(res.clone()); // >>> (nil)\n        }","section_id":"first-steps-with-redis-lists"},{"id":"first-steps-with-redis-lists-ex41","language":"rust","code":"let _: usize = r.del(\"bikes:repairs\").unwrap_or(0);\n\n        if let Ok(res) = r.rpush(\"bikes:repairs\", &[\"bike:1\", \"bike:2\", \"bike:3\"]) {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 3\n        }\n\n        if let Ok(res) = r.rpop(\"bikes:repairs\", None) {\n            let res: Option<String> = res;\n            print_optional_string(res.clone()); // >>> bike:3\n        }\n\n        if let Ok(res) = r.lpop(\"bikes:repairs\", None) {\n            let res: Option<String> = res;\n            print_optional_string(res.clone()); // >>> bike:1\n        }\n\n        if let Ok(res) = r.rpop(\"bikes:repairs\", None) {\n            let res: Option<String> = res;\n            print_optional_string(res.clone()); // >>> bike:2\n        }\n\n        if let Ok(res) = r.rpop(\"bikes:repairs\", None) {\n            let res: Option<String> = res;\n            print_optional_string(res.clone()); // >>> (nil)\n        }","section_id":"first-steps-with-redis-lists"},{"id":"capped-lists-ex0","language":"plaintext","code":"> DEL bikes:repairs\n(integer) 0\n> RPUSH bikes:repairs bike:1 bike:2 bike:3 bike:4 bike:5\n(integer) 5\n> LTRIM bikes:repairs 0 2\nOK\n> LRANGE bikes:repairs 0 -1\n1) \"bike:1\"\n2) \"bike:2\"\n3) \"bike:3\"","section_id":"capped-lists"},{"id":"capped-lists-ex1","language":"csharp","code":"long res27 = db.ListLeftPush(\"bikes:repairs\", [\"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\", \"bike:5\"]);\n        Console.WriteLine(res27);   // >>> 5\n\n        db.ListTrim(\"bikes:repairs\", 0, 2);\n        RedisValue[] res28 = db.ListRange(\"bikes:repairs\", 0, -1);\n        Console.WriteLine(string.Join(\", \", res28));    // \"bike:5, bike:4, bike:3\"","section_id":"capped-lists"},{"id":"capped-lists-ex2","language":"csharp","code":"long res27 = db.ListLeftPush(\"bikes:repairs\", [\"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\", \"bike:5\"]);\n        Console.WriteLine(res27);   // >>> 5\n\n        db.ListTrim(\"bikes:repairs\", 0, 2);\n        RedisValue[] res28 = db.ListRange(\"bikes:repairs\", 0, -1);\n        Console.WriteLine(string.Join(\", \", res28));    // \"bike:5, bike:4, bike:3\"","section_id":"capped-lists"},{"id":"capped-lists-ex3","language":"csharp","code":"long res27 = db.ListLeftPush(\"bikes:repairs\", [\"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\", \"bike:5\"]);\n        Console.WriteLine(res27);   // >>> 5\n\n        db.ListTrim(\"bikes:repairs\", 0, 2);\n        RedisValue[] res28 = db.ListRange(\"bikes:repairs\", 0, -1);\n        Console.WriteLine(string.Join(\", \", res28));    // \"bike:5, bike:4, bike:3\"","section_id":"capped-lists"},{"id":"capped-lists-ex4","language":"csharp","code":"long res27 = db.ListLeftPush(\"bikes:repairs\", [\"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\", \"bike:5\"]);\n        Console.WriteLine(res27);   // >>> 5\n\n        db.ListTrim(\"bikes:repairs\", 0, 2);\n        RedisValue[] res28 = db.ListRange(\"bikes:repairs\", 0, -1);\n        Console.WriteLine(string.Join(\", \", res28));    // \"bike:5, bike:4, bike:3\"","section_id":"capped-lists"},{"id":"capped-lists-ex5","language":"go","code":"rdb.Del(ctx, \"bikes:repairs\")\n\n\tres27, err := rdb.RPush(ctx, \"bikes:repairs\", \"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\", \"bike:5\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res27) // >>> 5\n\n\tres28, err := rdb.LTrim(ctx, \"bikes:repairs\", 0, 2).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res28) // >>> OK\n\n\tres29, err := rdb.LRange(ctx, \"bikes:repairs\", 0, -1).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res29) // >>> [bike:1 bike:2 bike:3]","section_id":"capped-lists"},{"id":"capped-lists-ex6","language":"java","code":"CompletableFuture<Void> ltrim = asyncCommands\n                    .lpush(\"bikes:repairs\", \"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\", \"bike:5\").thenCompose(res20 -> {\n                        System.out.println(res20); // >>> 5\n\n                        return asyncCommands.ltrim(\"bikes:repairs\", 0, 2);\n                    }).thenCompose(res21 -> {\n                        System.out.println(res21); // >>> OK\n\n                        return asyncCommands.lrange(\"bikes:repairs\", 0, -1);\n                    })\n                    .thenAccept(System.out::println)\n                    // >>> [bike:5, bike:4, bike:3]\n                    .toCompletableFuture();","section_id":"capped-lists"},{"id":"capped-lists-ex7","language":"java","code":"long res27 = jedis.rpush(\"bikes:repairs\", \"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\", \"bike:5\");\n        System.out.println(res27);  // >>> 5\n\n        String res28 = jedis.ltrim(\"bikes:repairs\", 0, 2);\n        System.out.println(res28);  // >>> OK\n\n        List<String> res29 = jedis.lrange(\"bikes:repairs\", 0, -1);\n        System.out.println(res29);  // >>> [bike:1, bike:2, bike:3]","section_id":"capped-lists"},{"id":"capped-lists-ex8","language":"javascript","code":"const res27 = await client.lPush(\n  'bikes:repairs', ['bike:1', 'bike:2', 'bike:3', 'bike:4', 'bike:5']\n);\nconsole.log(res27);  // 5\n\nconst res28 = await client.lTrim('bikes:repairs', 0, 2);\nconsole.log(res28);  // OK\n\nconst res29 = await client.lRange('bikes:repairs', 0, -1);\nconsole.log(res29);  // ['bike:5', 'bike:4', 'bike:3']","section_id":"capped-lists"},{"id":"capped-lists-ex9","language":"php","code":"$res27 = $r->rpush('bikes:repairs', 'bike:1', 'bike:2', 'bike:3', 'bike:4', 'bike:5');\n        echo $res27 . PHP_EOL;\n        // >>> 5\n\n        $res28 = $r->ltrim('bikes:repairs', 0, 2);\n        echo $res28 . PHP_EOL;\n        // >>> True\n\n        $res29 = $r->lrange('bikes:repairs', 0, -1);\n        echo json_encode($res29) . PHP_EOL;\n        // >>> ['bike:1', 'bike:2', 'bike:3']","section_id":"capped-lists"},{"id":"capped-lists-ex10","language":"python","code":"res27 = r.rpush(\"bikes:repairs\", \"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\", \"bike:5\")\nprint(res27)  # >>> 5\n\nres28 = r.ltrim(\"bikes:repairs\", 0, 2)\nprint(res28)  # >>> True\n\nres29 = r.lrange(\"bikes:repairs\", 0, -1)\nprint(res29)  # >>> ['bike:1', 'bike:2', 'bike:3']","section_id":"capped-lists"},{"id":"capped-lists-ex11","language":"ruby","code":"r.del('bikes:repairs')\n\nres30 = r.rpush('bikes:repairs', ['bike:1', 'bike:2', 'bike:3', 'bike:4', 'bike:5'])\nputs res30 # >>> 5\n\nres31 = r.ltrim('bikes:repairs', 0, 2)\nputs res31 # >>> OK\n\nres32 = r.lrange('bikes:repairs', 0, -1)\nputs res32.inspect # >>> [\"bike:1\", \"bike:2\", \"bike:3\"]","section_id":"capped-lists"},{"id":"capped-lists-ex12","language":"rust","code":"if let Ok(res) = r\n            .rpush(\n                \"bikes:repairs\",\n                &[\"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\", \"bike:5\"],\n            )\n            .await\n        {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 5\n        }\n\n        if let Ok(res) = r.ltrim(\"bikes:repairs\", 0, 2).await {\n            let res: () = res;\n            let _ = res;\n            println!(\"OK\"); // >>> OK\n        }\n\n        if let Ok(res) = r.lrange(\"bikes:repairs\", 0, -1).await {\n            let res: Vec<String> = res;\n            println!(\"{res:?}\"); // >>> [\"bike:1\", \"bike:2\", \"bike:3\"]\n        }","section_id":"capped-lists"},{"id":"capped-lists-ex13","language":"rust","code":"if let Ok(res) = r.rpush(\"bikes:repairs\", &[\"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\", \"bike:5\"]) {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 5\n        }\n\n        if let Ok(res) = r.ltrim(\"bikes:repairs\", 0, 2) {\n            let res: () = res;\n            let _ = res;\n            println!(\"OK\"); // >>> OK\n        }\n\n        if let Ok(res) = r.lrange(\"bikes:repairs\", 0, -1) {\n            let res: Vec<String> = res;\n            println!(\"{res:?}\"); // >>> [\"bike:1\", \"bike:2\", \"bike:3\"]\n        }","section_id":"capped-lists"},{"id":"capped-lists-ex14","language":"plaintext","code":"> DEL bikes:repairs\n(integer) 1\n> RPUSH bikes:repairs bike:1 bike:2 bike:3 bike:4 bike:5\n(integer) 5\n> LTRIM bikes:repairs -3 -1\nOK\n> LRANGE bikes:repairs 0 -1\n1) \"bike:3\"\n2) \"bike:4\"\n3) \"bike:5\"","section_id":"capped-lists"},{"id":"capped-lists-ex15","language":"csharp","code":"long res29 = db.ListRightPush(\"bikes:repairs\", [\"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\", \"bike:5\"]);\n        Console.WriteLine(res29);   // >>> 5\n\n        db.ListTrim(\"bikes:repairs\", -3, -1);\n        RedisValue[] res30 = db.ListRange(\"bikes:repairs\", 0, -1);\n        Console.WriteLine(string.Join(\", \", res30));    // >>> \"bike:3, bike:4, bike:5\"","section_id":"capped-lists"},{"id":"capped-lists-ex16","language":"csharp","code":"db.KeyDelete(\"bikes:repairs\");\n\n        long res29 = db.ListRightPush(\"bikes:repairs\", [\"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\", \"bike:5\"]);\n        Console.WriteLine(res29);   // >>> 5\n\n        db.ListTrim(\"bikes:repairs\", -3, -1);\n        RedisValue[] res30 = db.ListRange(\"bikes:repairs\", 0, -1);\n        Console.WriteLine(string.Join(\", \", res30));    // >>> \"bike:3, bike:4, bike:5\"","section_id":"capped-lists"},{"id":"capped-lists-ex17","language":"csharp","code":"long res29 = db.ListRightPush(\"bikes:repairs\", [\"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\", \"bike:5\"]);\n        Console.WriteLine(res29);   // >>> 5\n\n        db.ListTrim(\"bikes:repairs\", -3, -1);\n        RedisValue[] res30 = db.ListRange(\"bikes:repairs\", 0, -1);\n        Console.WriteLine(string.Join(\", \", res30));    // >>> \"bike:3, bike:4, bike:5\"","section_id":"capped-lists"},{"id":"capped-lists-ex18","language":"csharp","code":"db.KeyDelete(\"bikes:repairs\");\n\n        long res29 = db.ListRightPush(\"bikes:repairs\", [\"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\", \"bike:5\"]);\n        Console.WriteLine(res29);   // >>> 5\n\n        db.ListTrim(\"bikes:repairs\", -3, -1);\n        RedisValue[] res30 = db.ListRange(\"bikes:repairs\", 0, -1);\n        Console.WriteLine(string.Join(\", \", res30));    // >>> \"bike:3, bike:4, bike:5\"","section_id":"capped-lists"},{"id":"capped-lists-ex19","language":"go","code":"rdb.Del(ctx, \"bikes:repairs\")\n\n\tres30, err := rdb.RPush(ctx, \"bikes:repairs\", \"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\", \"bike:5\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res30) // >>> 5\n\n\tres31, err := rdb.LTrim(ctx, \"bikes:repairs\", -3, -1).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res31) // >>> OK\n\n\tres32, err := rdb.LRange(ctx, \"bikes:repairs\", 0, -1).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res32) // >>> [bike:3 bike:4 bike:5]","section_id":"capped-lists"},{"id":"capped-lists-ex20","language":"java","code":"CompletableFuture<Void> ltrimendoflist = asyncCommands\n                    .rpush(\"bikes:repairs\", \"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\", \"bike:5\").thenCompose(res22 -> {\n                        System.out.println(res22); // >>> 5\n\n                        return asyncCommands.ltrim(\"bikes:repairs\", -3, -1);\n                    }).thenCompose(res23 -> {\n                        System.out.println(res23); // >>> OK\n\n                        return asyncCommands.lrange(\"bikes:repairs\", 0, -1);\n                    })\n                    .thenAccept(System.out::println)\n                    // >>> [bike:3, bike:4, bike:5]\n                    .toCompletableFuture();","section_id":"capped-lists"},{"id":"capped-lists-ex21","language":"java","code":"jedis.del(\"bikes:repairs\");\n        \n        res27 = jedis.rpush(\"bikes:repairs\", \"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\", \"bike:5\");\n        System.out.println(res27);  // >>> 5\n\n        res28 = jedis.ltrim(\"bikes:repairs\", -3, -1);\n        System.out.println(res2);  // >>> OK\n\n        res29 = jedis.lrange(\"bikes:repairs\", 0, -1);\n        System.out.println(res29);  // >>> [bike:3, bike:4, bike:5]","section_id":"capped-lists"},{"id":"capped-lists-ex22","language":"javascript","code":"await client.del('bikes:repairs');\n\nconst res27eol = await client.rPush(\n  'bikes:repairs', ['bike:1', 'bike:2', 'bike:3', 'bike:4', 'bike:5']\n);\nconsole.log(res27eol);  // 5\n\nconst res28eol = await client.lTrim('bikes:repairs', -3, -1);\nconsole.log(res28eol);  // 'OK'\n\nconst res29eol = await client.lRange('bikes:repairs', 0, -1);\nconsole.log(res29eol);  // ['bike:3', 'bike:4', 'bike:5']","section_id":"capped-lists"},{"id":"capped-lists-ex23","language":"php","code":"$r->del('bikes:repairs');\n\n        $res27 = $r->rpush('bikes:repairs', 'bike:1', 'bike:2', 'bike:3', 'bike:4', 'bike:5');\n        echo $res27 . PHP_EOL;\n        // >>> 5\n\n        $res28 = $r->ltrim('bikes:repairs', -3, -1);\n        echo $res28 . PHP_EOL;\n        // >>> True\n\n        $res29 = $r->lrange('bikes:repairs', 0, -1);\n        echo json_encode($res29) . PHP_EOL;\n        // >>> ['bike:3', 'bike:4', 'bike:5']","section_id":"capped-lists"},{"id":"capped-lists-ex24","language":"python","code":"r.delete(\"bikes:repairs\")\n\nres27 = r.rpush(\"bikes:repairs\", \"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\", \"bike:5\")\nprint(res27)  # >>> 5\n\nres28 = r.ltrim(\"bikes:repairs\", -3, -1)\nprint(res28)  # >>> True\n\nres29 = r.lrange(\"bikes:repairs\", 0, -1)\nprint(res29)  # >>> ['bike:3', 'bike:4', 'bike:5']","section_id":"capped-lists"},{"id":"capped-lists-ex25","language":"ruby","code":"r.del('bikes:repairs')\n\nres33 = r.rpush('bikes:repairs', ['bike:1', 'bike:2', 'bike:3', 'bike:4', 'bike:5'])\nputs res33 # >>> 5\n\nres34 = r.ltrim('bikes:repairs', -3, -1)\nputs res34 # >>> OK\n\nres35 = r.lrange('bikes:repairs', 0, -1)\nputs res35.inspect # >>> [\"bike:3\", \"bike:4\", \"bike:5\"]","section_id":"capped-lists"},{"id":"capped-lists-ex26","language":"rust","code":"let _: usize = r.del(\"bikes:repairs\").await.unwrap_or(0);\n\n        if let Ok(res) = r\n            .rpush(\n                \"bikes:repairs\",\n                &[\"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\", \"bike:5\"],\n            )\n            .await\n        {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 5\n        }\n\n        if let Ok(res) = r.ltrim(\"bikes:repairs\", -3, -1).await {\n            let res: () = res;\n            let _ = res;\n            println!(\"OK\"); // >>> OK\n        }\n\n        if let Ok(res) = r.lrange(\"bikes:repairs\", 0, -1).await {\n            let res: Vec<String> = res;\n            println!(\"{res:?}\"); // >>> [\"bike:3\", \"bike:4\", \"bike:5\"]\n        }","section_id":"capped-lists"},{"id":"capped-lists-ex27","language":"rust","code":"let _: usize = r.del(\"bikes:repairs\").unwrap_or(0);\n\n        if let Ok(res) = r.rpush(\"bikes:repairs\", &[\"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\", \"bike:5\"]) {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 5\n        }\n\n        if let Ok(res) = r.ltrim(\"bikes:repairs\", -3, -1) {\n            let res: () = res;\n            let _ = res;\n            println!(\"OK\"); // >>> OK\n        }\n\n        if let Ok(res) = r.lrange(\"bikes:repairs\", 0, -1) {\n            let res: Vec<String> = res;\n            println!(\"{res:?}\"); // >>> [\"bike:3\", \"bike:4\", \"bike:5\"]\n        }","section_id":"capped-lists"},{"id":"blocking-operations-on-lists-ex0","language":"plaintext","code":"> DEL bikes:repairs\n(integer) 1\n> RPUSH bikes:repairs bike:1 bike:2\n(integer) 2\n> BRPOP bikes:repairs 1\n1) \"bikes:repairs\"\n2) \"bike:2\"\n> BRPOP bikes:repairs 1\n1) \"bikes:repairs\"\n2) \"bike:1\"\n> BRPOP bikes:repairs 1\n(nil)\n(2.01s)","section_id":"blocking-operations-on-lists"},{"id":"blocking-operations-on-lists-ex1","language":"csharp","code":"long res31 = db.ListRightPush(\"bikes:repairs\", [\"bike:1\", \"bike:2\"]);\n        Console.WriteLine(res31);   // >>> 2\n\n        Tuple<RedisKey, RedisValue>? res32 = db.BRPop([\"bikes:repairs\"], 1);\n\n        if (res32 != null)\n            Console.WriteLine($\"{res32.Item1} -> {res32.Item2}\"); // >>> \"bikes:repairs -> bike:2\"\n\n        Tuple<RedisKey, RedisValue>? res33 = db.BRPop([\"bikes:repairs\"], 1);\n\n        if (res33 != null)\n            Console.WriteLine($\"{res33.Item1} -> {res33.Item2}\"); // >>> \"bikes:repairs -> bike:1\"\n\n        Tuple<RedisKey, RedisValue>? res34 = db.BRPop([\"bikes:repairs\"], 1);\n        Console.WriteLine(res34);   // >>> \"Null\"","section_id":"blocking-operations-on-lists"},{"id":"blocking-operations-on-lists-ex2","language":"csharp","code":"db.KeyDelete(\"bikes:repairs\");\n\n        long res31 = db.ListRightPush(\"bikes:repairs\", [\"bike:1\", \"bike:2\"]);\n        Console.WriteLine(res31);   // >>> 2\n\n        Tuple<RedisKey, RedisValue>? res32 = db.BRPop([\"bikes:repairs\"], 1);\n\n        if (res32 != null)\n            Console.WriteLine($\"{res32.Item1} -> {res32.Item2}\"); // >>> \"bikes:repairs -> bike:2\"\n\n        Tuple<RedisKey, RedisValue>? res33 = db.BRPop([\"bikes:repairs\"], 1);\n\n        if (res33 != null)\n            Console.WriteLine($\"{res33.Item1} -> {res33.Item2}\"); // >>> \"bikes:repairs -> bike:1\"\n\n        Tuple<RedisKey, RedisValue>? res34 = db.BRPop([\"bikes:repairs\"], 1);\n        Console.WriteLine(res34);   // >>> \"Null\"","section_id":"blocking-operations-on-lists"},{"id":"blocking-operations-on-lists-ex3","language":"csharp","code":"long res31 = db.ListRightPush(\"bikes:repairs\", [\"bike:1\", \"bike:2\"]);\n        Console.WriteLine(res31);   // >>> 2\n\n        Tuple<RedisKey, RedisValue>? res32 = db.BRPop([\"bikes:repairs\"], 1);\n\n        if (res32 != null)\n            Console.WriteLine($\"{res32.Item1} -> {res32.Item2}\"); // >>> \"bikes:repairs -> bike:2\"\n\n        Tuple<RedisKey, RedisValue>? res33 = db.BRPop([\"bikes:repairs\"], 1);\n\n        if (res33 != null)\n            Console.WriteLine($\"{res33.Item1} -> {res33.Item2}\"); // >>> \"bikes:repairs -> bike:1\"\n\n        Tuple<RedisKey, RedisValue>? res34 = db.BRPop([\"bikes:repairs\"], 1);\n        Console.WriteLine(res34);   // >>> \"Null\"","section_id":"blocking-operations-on-lists"},{"id":"blocking-operations-on-lists-ex4","language":"csharp","code":"db.KeyDelete(\"bikes:repairs\");\n\n        long res31 = db.ListRightPush(\"bikes:repairs\", [\"bike:1\", \"bike:2\"]);\n        Console.WriteLine(res31);   // >>> 2\n\n        Tuple<RedisKey, RedisValue>? res32 = db.BRPop([\"bikes:repairs\"], 1);\n\n        if (res32 != null)\n            Console.WriteLine($\"{res32.Item1} -> {res32.Item2}\"); // >>> \"bikes:repairs -> bike:2\"\n\n        Tuple<RedisKey, RedisValue>? res33 = db.BRPop([\"bikes:repairs\"], 1);\n\n        if (res33 != null)\n            Console.WriteLine($\"{res33.Item1} -> {res33.Item2}\"); // >>> \"bikes:repairs -> bike:1\"\n\n        Tuple<RedisKey, RedisValue>? res34 = db.BRPop([\"bikes:repairs\"], 1);\n        Console.WriteLine(res34);   // >>> \"Null\"","section_id":"blocking-operations-on-lists"},{"id":"blocking-operations-on-lists-ex5","language":"go","code":"rdb.Del(ctx, \"bikes:repairs\")\n\n\tres33, err := rdb.RPush(ctx, \"bikes:repairs\", \"bike:1\", \"bike:2\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res33) // >>> 2\n\n\tres34, err := rdb.BRPop(ctx, 1, \"bikes:repairs\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res34) // >>> [bikes:repairs bike:2]\n\n\tres35, err := rdb.BRPop(ctx, 1, \"bikes:repairs\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res35) // >>> [bikes:repairs bike:1]\n\n\tres36, err := rdb.BRPop(ctx, 1, \"bikes:repairs\").Result()\n\n\tif err != nil {\n\t\tfmt.Println(err) // >>> redis: nil\n\t}\n\n\tfmt.Println(res36) // >>> []","section_id":"blocking-operations-on-lists"},{"id":"blocking-operations-on-lists-ex6","language":"java","code":"CompletableFuture<Void> brpop = asyncCommands.rpush(\"bikes:repairs\", \"bike:1\", \"bike:2\").thenCompose(res24 -> {\n                System.out.println(res24); // >>> 2\n\n                return asyncCommands.brpop(1, \"bikes:repairs\");\n            }).thenCompose(res25 -> {\n                System.out.println(res25);\n                // >>> KeyValue[bikes:repairs, bike:2]\n\n                return asyncCommands.brpop(1, \"bikes:repairs\");\n            }).thenCompose(res26 -> {\n                System.out.println(res26);\n                // >>> KeyValue[bikes:repairs, bike:1]\n\n                return asyncCommands.brpop(1, \"bikes:repairs\");\n            })\n                    .thenAccept(System.out::println) // >>> null\n                    .toCompletableFuture();","section_id":"blocking-operations-on-lists"},{"id":"blocking-operations-on-lists-ex7","language":"java","code":"jedis.del(\"bikes:repairs\");\n\n        long res31 = jedis.rpush(\"bikes:repairs\", \"bike:1\", \"bike:2\");\n        System.out.println(res31);  // >>> 2\n\n        List<String> res32 = jedis.brpop(1, \"bikes:repairs\");\n        System.out.println(res32);  // >>> (bikes:repairs, bike:2)\n\n        List<String>  res33 = jedis.brpop(1,\"bikes:repairs\");\n        System.out.println(res33);  // >>> (bikes:repairs, bike:1)\n\n        List<String>  res34 = jedis.brpop(1,\"bikes:repairs\");\n        System.out.println(res34);  // >>> null","section_id":"blocking-operations-on-lists"},{"id":"blocking-operations-on-lists-ex8","language":"javascript","code":"await client.del('bikes:repairs');\n\nconst res31 = await client.rPush('bikes:repairs', ['bike:1', 'bike:2']);\nconsole.log(res31);  // 2\n\nconst res32 = await client.brPop('bikes:repairs', 1);\nconsole.log(res32);  // { key: 'bikes:repairs', element: 'bike:2' }\n\nconst res33 = await client.brPop('bikes:repairs', 1);\nconsole.log(res33);  // { key: 'bikes:repairs', element: 'bike:1' }\n\nconst res34 = await client.brPop('bikes:repairs', 1);\nconsole.log(res34);  // null","section_id":"blocking-operations-on-lists"},{"id":"blocking-operations-on-lists-ex9","language":"php","code":"$r->del('bikes:repairs');\n\n        $res31 = $r->rpush('bikes:repairs', 'bike:1', 'bike:2');\n        echo $res31 . PHP_EOL;\n        // >>> 2\n\n        $res32 = $r->brpop('bikes:repairs', 1);\n        echo json_encode($res32) . PHP_EOL;\n        // >>> ['bikes:repairs', 'bike:2']\n\n        $res33 = $r->brpop('bikes:repairs', 1);\n        echo json_encode($res33) . PHP_EOL;\n        // >>> ['bikes:repairs', 'bike:1']\n\n        $res34 = $r->brpop('bikes:repairs', 1);\n        echo json_encode($res34) . PHP_EOL;\n        // >>> None","section_id":"blocking-operations-on-lists"},{"id":"blocking-operations-on-lists-ex10","language":"python","code":"r.delete(\"bikes:repairs\")\n\nres31 = r.rpush(\"bikes:repairs\", \"bike:1\", \"bike:2\")\nprint(res31)  # >>> 2\n\nres32 = r.brpop(\"bikes:repairs\", timeout=1)\nprint(res32)  # >>> ('bikes:repairs', 'bike:2')\n\nres33 = r.brpop(\"bikes:repairs\", timeout=1)\nprint(res33)  # >>> ('bikes:repairs', 'bike:1')\n\nres34 = r.brpop(\"bikes:repairs\", timeout=1)\nprint(res34)  # >>> None","section_id":"blocking-operations-on-lists"},{"id":"blocking-operations-on-lists-ex11","language":"ruby","code":"r.del('bikes:repairs')\n\nres36 = r.rpush('bikes:repairs', ['bike:1', 'bike:2'])\nputs res36 # >>> 2\n\nres37 = r.brpop('bikes:repairs', timeout: 1)\nputs res37.inspect # >>> [\"bikes:repairs\", \"bike:2\"]\n\nres38 = r.brpop('bikes:repairs', timeout: 1)\nputs res38.inspect # >>> [\"bikes:repairs\", \"bike:1\"]\n\nres39 = r.brpop('bikes:repairs', timeout: 1)\nputs res39.inspect # >>> nil","section_id":"blocking-operations-on-lists"},{"id":"blocking-operations-on-lists-ex12","language":"rust","code":"let _: usize = r.del(\"bikes:repairs\").await.unwrap_or(0);\n\n        if let Ok(res) = r.rpush(\"bikes:repairs\", &[\"bike:1\", \"bike:2\"]).await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 2\n        }\n\n        if let Ok(res) = r.brpop(\"bikes:repairs\", 1.0).await {\n            let res: Option<[String; 2]> = res;\n            println!(\"{res:?}\"); // >>> Some([\"bikes:repairs\", \"bike:2\"])\n        }\n\n        if let Ok(res) = r.brpop(\"bikes:repairs\", 1.0).await {\n            let res: Option<[String; 2]> = res;\n            println!(\"{res:?}\"); // >>> Some([\"bikes:repairs\", \"bike:1\"])\n        }\n\n        if let Ok(res) = r.brpop(\"bikes:repairs\", 1.0).await {\n            let res: Option<[String; 2]> = res;\n            println!(\"{res:?}\"); // >>> None\n        }","section_id":"blocking-operations-on-lists"},{"id":"blocking-operations-on-lists-ex13","language":"rust","code":"let _: usize = r.del(\"bikes:repairs\").unwrap_or(0);\n\n        if let Ok(res) = r.rpush(\"bikes:repairs\", &[\"bike:1\", \"bike:2\"]) {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 2\n        }\n\n        if let Ok(res) = r.brpop(\"bikes:repairs\", 1.0) {\n            let res: Option<[String; 2]> = res;\n            println!(\"{res:?}\"); // >>> Some([\"bikes:repairs\", \"bike:2\"])\n        }\n\n        if let Ok(res) = r.brpop(\"bikes:repairs\", 1.0) {\n            let res: Option<[String; 2]> = res;\n            println!(\"{res:?}\"); // >>> Some([\"bikes:repairs\", \"bike:1\"])\n        }\n\n        if let Ok(res) = r.brpop(\"bikes:repairs\", 1.0) {\n            let res: Option<[String; 2]> = res;\n            println!(\"{res:?}\"); // >>> None\n        }","section_id":"blocking-operations-on-lists"},{"id":"automatic-creation-and-removal-of-keys-ex0","language":"plaintext","code":"> DEL new_bikes\n(integer) 0\n> LPUSH new_bikes bike:1 bike:2 bike:3\n(integer) 3","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex1","language":"csharp","code":"bool res35 = db.KeyDelete(\"new_bikes\");\n        Console.WriteLine(res35);   // >>> False\n\n        long res36 = db.ListRightPush(\"new_bikes\", [\"bike:1\", \"bike:2\", \"bike:3\"]);\n        Console.WriteLine(res36);   // >>> 3","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex2","language":"csharp","code":"db.KeyDelete(\"new_bikes\");\n\n        long res36 = db.ListRightPush(\"new_bikes\", [\"bike:1\", \"bike:2\", \"bike:3\"]);\n        Console.WriteLine(res36);   // >>> 3","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex3","language":"csharp","code":"bool res35 = db.KeyDelete(\"new_bikes\");\n        Console.WriteLine(res35);   // >>> False\n\n        long res36 = db.ListRightPush(\"new_bikes\", [\"bike:1\", \"bike:2\", \"bike:3\"]);\n        Console.WriteLine(res36);   // >>> 3","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex4","language":"csharp","code":"db.KeyDelete(\"new_bikes\");\n\n        long res36 = db.ListRightPush(\"new_bikes\", [\"bike:1\", \"bike:2\", \"bike:3\"]);\n        Console.WriteLine(res36);   // >>> 3","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex5","language":"go","code":"res37, err := rdb.Del(ctx, \"new_bikes\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res37) // >>> 0\n\n\tres38, err := rdb.LPush(ctx, \"new_bikes\", \"bike:1\", \"bike:2\", \"bike:3\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res38) // >>> 3","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex6","language":"java","code":"CompletableFuture<Void> rule1 = asyncCommands.del(\"new_bikes\").thenCompose(res27 -> {\n                System.out.println(res27); // >>> 0\n\n                return asyncCommands.lpush(\"new_bikes\", \"bike:1\", \"bike:2\", \"bike:3\");\n            })\n                    .thenAccept(System.out::println) // >>> 3\n                    .toCompletableFuture();","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex7","language":"java","code":"jedis.del(\"new_bikes\");\n\n        long res36 = jedis.lpush(\"new_bikes\", \"bike:1\", \"bike:2\", \"bike:3\");\n        System.out.println(res36);  // >>> 3","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex8","language":"javascript","code":"const res35 = await client.del('new_bikes');\nconsole.log(res35); // 0\n\nconst res36 = await client.lPush('new_bikes', ['bike:1', 'bike:2', 'bike:3']);\nconsole.log(res36); // 3","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex9","language":"php","code":"$res35 = $r->del('new_bikes');\n        echo $res35 . PHP_EOL;\n        // >>> 0\n\n        $res36 = $r->lpush('new_bikes', 'bike:1', 'bike:2', 'bike:3');\n        echo $res36 . PHP_EOL;\n        // >>> 3","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex10","language":"python","code":"res35 = r.delete(\"new_bikes\")\nprint(res35)  # >>> 0\n\nres36 = r.lpush(\"new_bikes\", \"bike:1\", \"bike:2\", \"bike:3\")\nprint(res36)  # >>> 3","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex11","language":"ruby","code":"res40 = r.del('new_bikes')\nputs res40 # >>> 0\n\nres41 = r.lpush('new_bikes', ['bike:1', 'bike:2', 'bike:3'])\nputs res41 # >>> 3","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex12","language":"rust","code":"if let Ok(res) = r.del(\"new_bikes\").await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 0\n        }\n\n        if let Ok(res) = r.lpush(\"new_bikes\", &[\"bike:1\", \"bike:2\", \"bike:3\"]).await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 3\n        }","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex13","language":"rust","code":"if let Ok(res) = r.del(\"new_bikes\") {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 0\n        }\n\n        if let Ok(res) = r.lpush(\"new_bikes\", &[\"bike:1\", \"bike:2\", \"bike:3\"]) {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 3\n        }","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex14","language":"plaintext","code":"> DEL new_bikes\n(integer) 1\n> SET new_bikes bike:1\nOK\n> TYPE new_bikes\nstring\n> LPUSH new_bikes bike:2 bike:3\n(error) WRONGTYPE Operation against a key holding the wrong kind of value","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex15","language":"csharp","code":"bool res37 = db.StringSet(\"new_bikes\", \"bike:1\");\n        Console.WriteLine(res37);   // >>> True\n\n        RedisType res38 = db.KeyType(\"new_bikes\");\n        Console.WriteLine(res38);   // >>> RedisType.String\n\n        try\n        {\n            long res39 = db.ListRightPush(\"new_bikes\", [\"bike:2\", \"bike:3\"]);\n        }\n        catch (Exception e)\n        {\n            Console.WriteLine(e);\n        }","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex16","language":"csharp","code":"db.KeyDelete(\"new_bikes\");\n        \n        bool res37 = db.StringSet(\"new_bikes\", \"bike:1\");\n        Console.WriteLine(res37);   // >>> True\n\n        RedisType res38 = db.KeyType(\"new_bikes\");\n        Console.WriteLine(res38);   // >>> RedisType.String\n\n        try\n        {\n            long res39 = db.ListRightPush(\"new_bikes\", [\"bike:2\", \"bike:3\"]);\n        }\n        catch (Exception e)\n        {\n            Console.WriteLine(e);\n        }","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex17","language":"csharp","code":"bool res37 = db.StringSet(\"new_bikes\", \"bike:1\");\n        Console.WriteLine(res37);   // >>> True\n\n        RedisType res38 = db.KeyType(\"new_bikes\");\n        Console.WriteLine(res38);   // >>> RedisType.String\n\n        try\n        {\n            long res39 = db.ListRightPush(\"new_bikes\", [\"bike:2\", \"bike:3\"]);\n        }\n        catch (Exception e)\n        {\n            Console.WriteLine(e);\n        }","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex18","language":"csharp","code":"db.KeyDelete(\"new_bikes\");\n        \n        bool res37 = db.StringSet(\"new_bikes\", \"bike:1\");\n        Console.WriteLine(res37);   // >>> True\n\n        RedisType res38 = db.KeyType(\"new_bikes\");\n        Console.WriteLine(res38);   // >>> RedisType.String\n\n        try\n        {\n            long res39 = db.ListRightPush(\"new_bikes\", [\"bike:2\", \"bike:3\"]);\n        }\n        catch (Exception e)\n        {\n            Console.WriteLine(e);\n        }","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex19","language":"go","code":"rdb.Del(ctx, \"new_bikes\")\n\n\tres39, err := rdb.Set(ctx, \"new_bikes\", \"bike:1\", 0).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res39) // >>> OK\n\n\tres40, err := rdb.Type(ctx, \"new_bikes\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res40) // >>> string\n\n\tres41, err := rdb.LPush(ctx, \"new_bikes\", \"bike:2\", \"bike:3\").Result()\n\n\tif err != nil {\n\t\tfmt.Println(err)\n\t\t// >>> WRONGTYPE Operation against a key holding the wrong kind of value\n\t}\n\n\tfmt.Println(res41)","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex20","language":"java","code":"CompletableFuture<Void> rule11 = asyncCommands.set(\"new_bikes_string\", \"bike:1\").thenCompose(res28 -> {\n                System.out.println(res28); // >>> OK\n\n                return asyncCommands.type(\"new_bikes_string\");\n            }).thenCompose(res29 -> {\n                System.out.println(res29); // >>> string\n\n                return asyncCommands.lpush(\"new_bikes_string\", \"bike:2\", \"bike:3\");\n            }).handle((res, ex) -> {\n                if (ex == null) {\n                    return res;\n                } else {\n                    System.out.println(ex);\n                    // >>> java.util.concurrent.CompletionException:\n                    // >>> io.lettuce.core.RedisCommandExecutionException:\n                    // >>> WRONGTYPE Operation against a key holding the wrong\n                    // >>> kind of value\n\n                    return -1L;\n                }\n            })\n                    .thenAccept(System.out::println) // >>> -1\n                    .toCompletableFuture();","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex21","language":"java","code":"jedis.del(\"new_bikes\");\n        \n        String res37 = jedis.set(\"new_bikes\", \"bike:1\");\n        System.out.println(res37);  // >>> OK\n\n        String res38 = jedis.type(\"new_bikes\");\n        System.out.println(res38);  // >>> string\n\n        try {\n            long res39  = jedis.lpush(\"new_bikes\", \"bike:2\", \"bike:3\");\n        } catch (Exception e) {\n            e.printStackTrace();\n            // >>> redis.clients.jedis.exceptions.JedisDataException:\n            // >>> WRONGTYPE Operation against a key holding the wrong kind of value\n        }","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex22","language":"javascript","code":"await client.del('new_bikes');\n\nconst res37 = await client.set('new_bikes', 'bike:1');\nconsole.log(res37);  // 'OK'\n\nconst res38 = await client.type('new_bikes');\nconsole.log(res38);  // 'string'\n\ntry {\n  const res39 = await client.lPush('new_bikes', 'bike:2', 'bike:3');\n  // redis.exceptions.ResponseError:\n  // [SimpleError: WRONGTYPE Operation against a key holding the wrong kind of value]\n}\ncatch(e){\n  console.log(e);\n}","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex23","language":"php","code":"$r->del('new_bikes');\n\n        $res37 = $r->set('new_bikes', 'bike:1');\n        echo $res37 . PHP_EOL;\n        // >>> True\n\n        $res38 = $r->type('new_bikes');\n        echo $res38 . PHP_EOL;\n        // >>> 'string'\n\n        try {\n            $res39 = $r->lpush('new_bikes', 'bike:2', 'bike:3');\n            // >>> redis.exceptions.ResponseError:\n            // >>> WRONGTYPE Operation against a key holding the wrong kind of value\n        } catch (\\Predis\\Response\\ServerException $e) {\n            echo $e->getMessage() . PHP_EOL;\n        }","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex24","language":"python","code":"r.delete(\"new_bikes\")\n\nres37 = r.set(\"new_bikes\", \"bike:1\")\nprint(res37)  # >>> True\n\nres38 = r.type(\"new_bikes\")\nprint(res38)  # >>> 'string'\n\ntry:\n    res39 = r.lpush(\"new_bikes\", \"bike:2\", \"bike:3\")\n    # >>> redis.exceptions.ResponseError:\n    # >>> WRONGTYPE Operation against a key holding the wrong kind of value\nexcept redis.exceptions.ResponseError as e:\n    print(e)","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex25","language":"ruby","code":"r.del('new_bikes')\n\nres42 = r.set('new_bikes', 'bike:1')\nputs res42 # >>> OK\n\nres43 = r.type('new_bikes')\nputs res43 # >>> string\n\nwrong_type_error = nil\nbegin\n  r.lpush('new_bikes', ['bike:2', 'bike:3'])\nrescue Redis::CommandError => e\n  wrong_type_error = e\n  puts e.message # >>> WRONGTYPE Operation against a key holding the wrong kind of value\nend","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex26","language":"rust","code":"if let Ok(res) = r.del(\"new_bikes\").await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 1\n        }\n\n        if let Ok(res) = r.set(\"new_bikes\", \"bike:1\").await {\n            let res: String = res;\n            println!(\"{res}\"); // >>> OK\n        }\n\n        if let Ok(res) = r.key_type(\"new_bikes\").await {\n            let res: ValueType = res;\n            let res: String = res.into();\n            println!(\"{res}\"); // >>> string\n        }\n\n        let res: redis::RedisResult<usize> = r.lpush(\"new_bikes\", &[\"bike:2\", \"bike:3\"]).await;\n        match res {\n            Ok(_) => {}\n            Err(e) => {\n                let msg = e.to_string();\n                if let Some((_, tail)) = msg.split_once(\"WRONGTYPE \") {\n                    println!(\"WRONGTYPE {tail}\");\n                } else {\n                    println!(\"{msg}\");\n                }\n            }\n        }","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex27","language":"rust","code":"if let Ok(res) = r.del(\"new_bikes\") {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 1\n        }\n\n        if let Ok(res) = r.set(\"new_bikes\", \"bike:1\") {\n            let res: String = res;\n            println!(\"{res}\"); // >>> OK\n        }\n\n        if let Ok(res) = r.key_type(\"new_bikes\") {\n            let res: ValueType = res;\n            let res: String = res.into();\n            println!(\"{res}\"); // >>> string\n        }\n\n        let res: redis::RedisResult<usize> = r.lpush(\"new_bikes\", &[\"bike:2\", \"bike:3\"]);\n        match res {\n            Ok(_) => {}\n            Err(e) => {\n                let msg = e.to_string();\n                if let Some((_, tail)) = msg.split_once(\"WRONGTYPE \") {\n                    println!(\"WRONGTYPE {tail}\");\n                } else {\n                    println!(\"{msg}\");\n                }\n            }\n        }","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex28","language":"plaintext","code":"> DEL bikes:repairs\n(integer) 1\n> LPUSH bikes:repairs bike:1 bike:2 bike:3\n(integer) 3\n> EXISTS bikes:repairs\n(integer) 1\n> LPOP bikes:repairs\n\"bike:3\"\n> LPOP bikes:repairs\n\"bike:2\"\n> LPOP bikes:repairs\n\"bike:1\"\n> EXISTS bikes:repairs\n(integer) 0","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex29","language":"csharp","code":"long res40 = db.ListLeftPush(\"bikes:repairs\", [\"bike:1\", \"bike:2\", \"bike:3\"]);\n        Console.WriteLine(res40);   // >>> 3\n\n        bool res41 = db.KeyExists(\"bikes:repairs\");\n        Console.WriteLine(res41);   // >>> True\n\n        RedisValue res42 = db.ListLeftPop(\"bikes:repairs\");\n        Console.WriteLine(res42);   // >>> \"bike:3\"\n\n        RedisValue res43 = db.ListLeftPop(\"bikes:repairs\");\n        Console.WriteLine(res43);   // >>> \"bike:2\"\n\n        RedisValue res44 = db.ListLeftPop(\"bikes:repairs\");\n        Console.WriteLine(res44);   //  >>> \"bike:1\"\n\n        bool res45 = db.KeyExists(\"bikes:repairs\");\n        Console.WriteLine(res45);   // >>> False","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex30","language":"csharp","code":"db.KeyDelete(\"bikes:repairs\");\n\n        long res40 = db.ListLeftPush(\"bikes:repairs\", [\"bike:1\", \"bike:2\", \"bike:3\"]);\n        Console.WriteLine(res40);   // >>> 3\n\n        bool res41 = db.KeyExists(\"bikes:repairs\");\n        Console.WriteLine(res41);   // >>> True\n\n        RedisValue res42 = db.ListLeftPop(\"bikes:repairs\");\n        Console.WriteLine(res42);   // >>> \"bike:3\"\n\n        RedisValue res43 = db.ListLeftPop(\"bikes:repairs\");\n        Console.WriteLine(res43);   // >>> \"bike:2\"\n\n        RedisValue res44 = db.ListLeftPop(\"bikes:repairs\");\n        Console.WriteLine(res44);   //  >>> \"bike:1\"\n\n        bool res45 = db.KeyExists(\"bikes:repairs\");\n        Console.WriteLine(res45);   // >>> False","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex31","language":"csharp","code":"long res40 = db.ListLeftPush(\"bikes:repairs\", [\"bike:1\", \"bike:2\", \"bike:3\"]);\n        Console.WriteLine(res40);   // >>> 3\n\n        bool res41 = db.KeyExists(\"bikes:repairs\");\n        Console.WriteLine(res41);   // >>> True\n\n        RedisValue res42 = db.ListLeftPop(\"bikes:repairs\");\n        Console.WriteLine(res42);   // >>> \"bike:3\"\n\n        RedisValue res43 = db.ListLeftPop(\"bikes:repairs\");\n        Console.WriteLine(res43);   // >>> \"bike:2\"\n\n        RedisValue res44 = db.ListLeftPop(\"bikes:repairs\");\n        Console.WriteLine(res44);   //  >>> \"bike:1\"\n\n        bool res45 = db.KeyExists(\"bikes:repairs\");\n        Console.WriteLine(res45);   // >>> False","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex32","language":"csharp","code":"db.KeyDelete(\"bikes:repairs\");\n\n        long res40 = db.ListLeftPush(\"bikes:repairs\", [\"bike:1\", \"bike:2\", \"bike:3\"]);\n        Console.WriteLine(res40);   // >>> 3\n\n        bool res41 = db.KeyExists(\"bikes:repairs\");\n        Console.WriteLine(res41);   // >>> True\n\n        RedisValue res42 = db.ListLeftPop(\"bikes:repairs\");\n        Console.WriteLine(res42);   // >>> \"bike:3\"\n\n        RedisValue res43 = db.ListLeftPop(\"bikes:repairs\");\n        Console.WriteLine(res43);   // >>> \"bike:2\"\n\n        RedisValue res44 = db.ListLeftPop(\"bikes:repairs\");\n        Console.WriteLine(res44);   //  >>> \"bike:1\"\n\n        bool res45 = db.KeyExists(\"bikes:repairs\");\n        Console.WriteLine(res45);   // >>> False","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex33","language":"go","code":"rdb.Del(ctx, \"bikes:repairs\")\n\tres42, err := rdb.LPush(ctx, \"bikes:repairs\", \"bike:1\", \"bike:2\", \"bike:3\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res42) // >>> 3\n\n\tres43, err := rdb.Exists(ctx, \"bikes:repairs\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res43) // >>> 1\n\n\tres44, err := rdb.LPop(ctx, \"bikes:repairs\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res44) // >>> bike:3\n\n\tres45, err := rdb.LPop(ctx, \"bikes:repairs\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res45) // >>> bike:2\n\n\tres46, err := rdb.LPop(ctx, \"bikes:repairs\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res46) // >>> bike:1\n\n\tres47, err := rdb.Exists(ctx, \"bikes:repairs\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res47) // >>> 0","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex34","language":"java","code":"CompletableFuture<Void> rule2 = asyncCommands.lpush(\"bikes:repairs\", \"bike:1\", \"bike:2\", \"bike:3\")\n                    .thenCompose(res30 -> {\n                        System.out.println(res30); // >>> 3\n\n                        return asyncCommands.exists(\"bikes:repairs\");\n                    }).thenCompose(res31 -> {\n                        System.out.println(res31); // >>> 1\n\n                        return asyncCommands.lpop(\"bikes:repairs\");\n                    }).thenCompose(res32 -> {\n                        System.out.println(res32); // >>> bike:3\n\n                        return asyncCommands.lpop(\"bikes:repairs\");\n                    }).thenCompose(res33 -> {\n                        System.out.println(res33); // >>> bike:2\n\n                        return asyncCommands.lpop(\"bikes:repairs\");\n                    }).thenCompose(res34 -> {\n                        System.out.println(res34); // >>> bike:1\n\n                        return asyncCommands.exists(\"bikes:repairs\");\n                    })\n                    .thenAccept(System.out::println) // >>> 0\n                    .toCompletableFuture();","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex35","language":"java","code":"jedis.del(\"bikes:repairs\");\n\n        jedis.lpush(\"bikes:repairs\", \"bike:1\", \"bike:2\", \"bike:3\");\n        System.out.println(res36);  // >>> 3\n\n        boolean res40 = jedis.exists(\"bikes:repairs\");\n        System.out.println(res40);  // >>> true\n\n        String res41 = jedis.lpop(\"bikes:repairs\");\n        System.out.println(res41);  // >>> bike:3\n\n        String res42 = jedis.lpop(\"bikes:repairs\");\n        System.out.println(res42);  // >>> bike:2\n\n        String res43 = jedis.lpop(\"bikes:repairs\");\n        System.out.println(res43);  // >>> bike:1\n\n        boolean res44 = jedis.exists(\"bikes:repairs\");\n        System.out.println(res44);  // >>> false","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex36","language":"javascript","code":"await client.del('bikes:repairs');\n\nawait client.lPush('bikes:repairs', ['bike:1', 'bike:2', 'bike:3']);\nconsole.log(res36);  // 3\n\nconst res40 = await client.exists('bikes:repairs')\nconsole.log(res40);  // 1\n\nconst res41 = await client.lPop('bikes:repairs');\nconsole.log(res41);  // 'bike:3'\n\nconst res42 = await client.lPop('bikes:repairs');\nconsole.log(res42);  // 'bike:2'\n\nconst res43 = await client.lPop('bikes:repairs');\nconsole.log(res43);  // 'bike:1'\n\nconst res44 = await client.exists('bikes:repairs');\nconsole.log(res44);  // 0","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex37","language":"php","code":"$r->del('bikes:repairs');\n        $res36 = $r->lpush('bikes:repairs', 'bike:1', 'bike:2', 'bike:3');\n        echo $res36 . PHP_EOL;\n        // >>> 3\n\n        $res40 = $r->exists('bikes:repairs');\n        echo $res40 . PHP_EOL;\n        // >>> 1\n\n        $res41 = $r->lpop('bikes:repairs');\n        echo $res41 . PHP_EOL;\n        // >>> 'bike:3'\n\n        $res42 = $r->lpop('bikes:repairs');\n        echo $res42 . PHP_EOL;\n        // >>> 'bike:2'\n\n        $res43 = $r->lpop('bikes:repairs');\n        echo $res43 . PHP_EOL;\n        // >>> 'bike:1'\n\n        $res44 = $r->exists('bikes:repairs');\n        echo $res44 . PHP_EOL;\n        // >>> False","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex38","language":"python","code":"r.delete(\"bikes:repairs\")\n\nr.lpush(\"bikes:repairs\", \"bike:1\", \"bike:2\", \"bike:3\")\nprint(res36)  # >>> 3\n\nres40 = r.exists(\"bikes:repairs\")\nprint(res40)  # >>> 1\n\nres41 = r.lpop(\"bikes:repairs\")\nprint(res41)  # >>> 'bike:3'\n\nres42 = r.lpop(\"bikes:repairs\")\nprint(res42)  # >>> 'bike:2'\n\nres43 = r.lpop(\"bikes:repairs\")\nprint(res43)  # >>> 'bike:1'\n\nres44 = r.exists(\"bikes:repairs\")\nprint(res44)  # >>> False","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex39","language":"ruby","code":"r.del('bikes:repairs')\n\nres44 = r.lpush('bikes:repairs', ['bike:1', 'bike:2', 'bike:3'])\nputs res44 # >>> 3\n\nres45 = r.exists('bikes:repairs')\nputs res45 # >>> 1\n\nres46 = r.lpop('bikes:repairs')\nputs res46 # >>> bike:3\n\nres47 = r.lpop('bikes:repairs')\nputs res47 # >>> bike:2\n\nres48 = r.lpop('bikes:repairs')\nputs res48 # >>> bike:1\n\nres49 = r.exists('bikes:repairs')\nputs res49 # >>> 0","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex40","language":"rust","code":"let _: usize = r.rpush(\"bikes:repairs\", \"bike:placeholder\").await.unwrap_or(0);\n        if let Ok(res) = r.del(\"bikes:repairs\").await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 1\n        }\n\n        if let Ok(res) = r.lpush(\"bikes:repairs\", &[\"bike:1\", \"bike:2\", \"bike:3\"]).await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 3\n        }\n\n        if let Ok(res) = r.exists(\"bikes:repairs\").await {\n            let res: bool = res;\n            println!(\"{}\", i32::from(res)); // >>> 1\n        }\n\n        if let Ok(res) = r.lpop(\"bikes:repairs\", None).await {\n            let res: Option<String> = res;\n            print_optional_string(res.clone()); // >>> bike:3\n        }\n\n        if let Ok(res) = r.lpop(\"bikes:repairs\", None).await {\n            let res: Option<String> = res;\n            print_optional_string(res.clone()); // >>> bike:2\n        }\n\n        if let Ok(res) = r.lpop(\"bikes:repairs\", None).await {\n            let res: Option<String> = res;\n            print_optional_string(res.clone()); // >>> bike:1\n        }\n\n        if let Ok(res) = r.exists(\"bikes:repairs\").await {\n            let res: bool = res;\n            println!(\"{}\", i32::from(res)); // >>> 0\n        }","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex41","language":"rust","code":"let _: usize = r.rpush(\"bikes:repairs\", \"bike:placeholder\").unwrap_or(0);\n        if let Ok(res) = r.del(\"bikes:repairs\") {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 1\n        }\n\n        if let Ok(res) = r.lpush(\"bikes:repairs\", &[\"bike:1\", \"bike:2\", \"bike:3\"]) {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 3\n        }\n\n        if let Ok(res) = r.exists(\"bikes:repairs\") {\n            let res: bool = res;\n            println!(\"{}\", i32::from(res)); // >>> 1\n        }\n\n        if let Ok(res) = r.lpop(\"bikes:repairs\", None) {\n            let res: Option<String> = res;\n            print_optional_string(res.clone()); // >>> bike:3\n        }\n\n        if let Ok(res) = r.lpop(\"bikes:repairs\", None) {\n            let res: Option<String> = res;\n            print_optional_string(res.clone()); // >>> bike:2\n        }\n\n        if let Ok(res) = r.lpop(\"bikes:repairs\", None) {\n            let res: Option<String> = res;\n            print_optional_string(res.clone()); // >>> bike:1\n        }\n\n        if let Ok(res) = r.exists(\"bikes:repairs\") {\n            let res: bool = res;\n            println!(\"{}\", i32::from(res)); // >>> 0\n        }","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex42","language":"plaintext","code":"> DEL bikes:repairs\n(integer) 1\n> LLEN bikes:repairs\n(integer) 0\n> LPOP bikes:repairs\n(nil)","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex43","language":"csharp","code":"bool res46 = db.KeyDelete(\"bikes:repairs\");\n        Console.WriteLine(res46);   // >>> False\n\n        long res47 = db.ListLength(\"bikes:repairs\");\n        Console.WriteLine(res47);   // >>> 0\n\n        RedisValue res48 = db.ListLeftPop(\"bikes:repairs\");\n        Console.WriteLine(res48);   // >>> Null","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex44","language":"csharp","code":"db.KeyDelete(\"bikes:repairs\");\n\n        long res47 = db.ListLength(\"bikes:repairs\");\n        Console.WriteLine(res47);   // >>> 0\n\n        RedisValue res48 = db.ListLeftPop(\"bikes:repairs\");\n        Console.WriteLine(res48);   // >>> Null","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex45","language":"csharp","code":"bool res46 = db.KeyDelete(\"bikes:repairs\");\n        Console.WriteLine(res46);   // >>> False\n\n        long res47 = db.ListLength(\"bikes:repairs\");\n        Console.WriteLine(res47);   // >>> 0\n\n        RedisValue res48 = db.ListLeftPop(\"bikes:repairs\");\n        Console.WriteLine(res48);   // >>> Null","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex46","language":"csharp","code":"db.KeyDelete(\"bikes:repairs\");\n\n        long res47 = db.ListLength(\"bikes:repairs\");\n        Console.WriteLine(res47);   // >>> 0\n\n        RedisValue res48 = db.ListLeftPop(\"bikes:repairs\");\n        Console.WriteLine(res48);   // >>> Null","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex47","language":"go","code":"rdb.Del(ctx, \"bikes:repairs\")\n\n\tres48, err := rdb.Del(ctx, \"bikes:repairs\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res48) // >>> 0\n\n\tres49, err := rdb.LLen(ctx, \"bikes:repairs\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res49) // >>> 0\n\n\tres50, err := rdb.LPop(ctx, \"bikes:repairs\").Result()\n\n\tif err != nil {\n\t\tfmt.Println(err) // >>> redis: nil\n\t}\n\n\tfmt.Println(res50) // >>> <empty string>","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex48","language":"java","code":"CompletableFuture<Void> rule3 = asyncCommands.del(\"bikes:repairs\").thenCompose(res35 -> {\n                System.out.println(res35); // >>> 0\n\n                return asyncCommands.llen(\"bikes:repairs\");\n            }).thenCompose(res36 -> {\n                System.out.println(res36); // >>> 0\n\n                return asyncCommands.lpop(\"bikes:repairs\");\n            })\n                    .thenAccept(System.out::println) // >>> null\n                    .toCompletableFuture();","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex49","language":"java","code":"jedis.del(\"bikes:repairs\");\n\n        long res46 = jedis.llen(\"bikes:repairs\");\n        System.out.println(res46);  // >>> 0\n\n        String res47 = jedis.lpop(\"bikes:repairs\");\n        System.out.println(res47);  // >>> null","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex50","language":"javascript","code":"await client.del('bikes:repairs');\n\nconst res45 = await client.del('bikes:repairs');\nconsole.log(res45);  // 0\n\nconst res46 = await client.lLen('bikes:repairs');\nconsole.log(res46);  // 0\n\nconst res47 = await client.lPop('bikes:repairs');\nconsole.log(res47);  // null","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex51","language":"php","code":"$res45 = $r->del('bikes:repairs');\n        echo $res45 . PHP_EOL;\n        // >>> 0\n\n        $res46 = $r->llen('bikes:repairs');\n        echo $res46 . PHP_EOL;\n        // >>> 0\n\n        $res47 = $r->lpop('bikes:repairs');\n        echo $res47 . PHP_EOL;\n        // >>> None","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex52","language":"python","code":"r.delete(\"bikes:repairs\")\n\nres45 = r.delete(\"bikes:repairs\")\nprint(res45)  # >>> 0\n\nres46 = r.llen(\"bikes:repairs\")\nprint(res46)  # >>> 0\n\nres47 = r.lpop(\"bikes:repairs\")\nprint(res47)  # >>> None","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex53","language":"ruby","code":"r.del('bikes:repairs')\n\nres50 = r.del('bikes:repairs')\nputs res50 # >>> 0\n\nres51 = r.llen('bikes:repairs')\nputs res51 # >>> 0\n\nres52 = r.lpop('bikes:repairs')\nputs res52.inspect # >>> nil","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex54","language":"rust","code":"if let Ok(res) = r.del(\"bikes:repairs\").await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 0\n        }\n\n        if let Ok(res) = r.llen(\"bikes:repairs\").await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 0\n        }\n\n        if let Ok(res) = r.lpop(\"bikes:repairs\", None).await {\n            let res: Option<String> = res;\n            print_optional_string(res.clone()); // >>> (nil)\n        }","section_id":"automatic-creation-and-removal-of-keys"},{"id":"automatic-creation-and-removal-of-keys-ex55","language":"rust","code":"if let Ok(res) = r.del(\"bikes:repairs\") {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 0\n        }\n\n        if let Ok(res) = r.llen(\"bikes:repairs\") {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 0\n        }\n\n        if let Ok(res) = r.lpop(\"bikes:repairs\", None) {\n            let res: Option<String> = res;\n            print_optional_string(res.clone()); // >>> (nil)\n        }","section_id":"automatic-creation-and-removal-of-keys"}]}
{"schema_version":2,"id":"develop/data-types/probabilistic/bloom-filter","title":"Bloom filter","url":"https://redis.io/docs/latest/develop/data-types/probabilistic/bloom-filter/","summary":"Bloom filters are a probabilistic data structure that checks for presence of an item in a set","aliases":["/data-types/probabilistic/bloom-filter/","/data-types/bloom-filter/","/data-types/bloom-filters/","/manual/data-types/probabilistic/bloom-filter/","/manual/bloom-filter/","/manual/bloom-filters/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-04-21T16:00:33+01:00","page_type":"content","content_hash":"f996f12024b2a03b1451da93fbe871103028c0b671def6edddeb25851dece933","sections":[{"id":"bloom-filter-command-summary","title":"Bloom filter command summary","role":"content","text":"**11 commands in this group:**\n\n[View all bf commands](https://redis.io/commands/?group=bf)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [BF.ADD](https://redis.io/commands/bf.add/) | Adds an item to a Bloom Filter | O(k), where k is the number of hash functions u... | 1.0.0 |\n| [BF.CARD](https://redis.io/commands/bf.card/) | Returns the cardinality of a Bloom filter | O(1) | 2.4.4 |\n| [BF.DEBUG](https://redis.io/commands/bf.debug/) | Returns internal debug information about a Bloom Filter | O(n), where n is the number of sub-filters | 1.0.0 |\n| [BF.EXISTS](https://redis.io/commands/bf.exists/) | Checks whether an item exists in a Bloom Filter | O(k), where k is the number of hash functions u... | 1.0.0 |\n| [BF.INFO](https://redis.io/commands/bf.info/) | Returns information about a Bloom Filter | O(1) | 1.0.0 |\n| [BF.INSERT](https://redis.io/commands/bf.insert/) | Adds one or more items to a Bloom Filter. A filter will be created if it does not exist | O(k * n), where k is the number of hash functio... | 1.0.0 |\n| [BF.LOADCHUNK](https://redis.io/commands/bf.loadchunk/) | Restores a filter previously saved using SCANDUMP | O(n), where n is the capacity | 1.0.0 |\n| [BF.MADD](https://redis.io/commands/bf.madd/) | Adds one or more items to a Bloom Filter. A filter will be created if it does not exist | O(k * n), where k is the number of hash functio... | 1.0.0 |\n| [BF.MEXISTS](https://redis.io/commands/bf.mexists/) | Checks whether one or more items exist in a Bloom Filter | O(k * n), where k is the number of hash functio... | 1.0.0 |\n| [BF.RESERVE](https://redis.io/commands/bf.reserve/) | Creates a new Bloom Filter | O(1) | 1.0.0 |\n\n#### Bloom filter command summary (part 2)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [BF.SCANDUMP](https://redis.io/commands/bf.scandump/) | Begins an incremental save of the bloom filter | O(n), where n is the capacity | 1.0.0 |\n\n\n\nA Bloom filter is a probabilistic data structure in Redis Open Source that enables you to check if an element is present in a set using a very small memory space of a fixed size.\n\nInstead of storing all the items in a set, a Bloom Filter stores only the items' hashed representations, thus sacrificing some precision. The trade-off is that Bloom Filters are very space-efficient and fast.\n\nA Bloom filter can guarantee the absence of an item from a set, but it can only give an estimation about its presence. So when it responds that an item is not present in a set (a negative answer), you can be sure that indeed is the case. But one out of every N positive answers will be wrong. Even though it looks unusual at first glance, this kind of uncertainty still has its place in computer science. There are many cases out there where a negative answer will prevent more costly operations, for example checking if a username has been taken, if a credit card has been reported as stolen, if a user has already seen an ad and much more."},{"id":"use-cases","title":"Use cases","role":"content","text":"**Financial fraud detection (finance)**\n\nThis application answers the question, \"Has the user paid from this location before?\", thus checking for suspicious activity in their users' shopping habits.\n \nUse one Bloom filter per user, checked for every transaction. Provide an extremely fast response (local latency). Replicate in different regions in case the user moves. Prevent decreasing performance with scale.  \n\nUsing the Redis Bloom filter for this type of application provides these benefits: \n\n- Fast transaction completion \n- Decreased possibility for transaction to break in case of network partitions (connection needs to be kept open for a shorter time) \n- Extra layer of security for both credit card owners and retailers\n\nOther questions a Bloom filter can help answer in the finance industry are: \n\n- Has the user ever made purchases in this category of products/services? \n- Do I need to skip some security steps when the user is buying with a vetted online shop (big retailers like Amazon, Apple app store...)? \n- Has this credit card been reported as lost/stolen? An additional benefit of using a Bloom filter in the last case is that financial organizations can exchange their lists of stolen/blocked credit card numbers without revealing the numbers themselves. \n\n**Ad placement (retail, advertising)**\n\nThis application answers these questions: \n\n- Has the user already seen this ad? \n- Has the user already bought this product?\n\nUse a Bloom filter for every user, storing all bought products. The recommendation engine suggests a new product and checks if the product is in the user's Bloom filter. \n\n- If no, the ad is shown to the user and is added to the Bloom filter. \n- If yes, the process restarts and repeats until it finds a product that is not present in the filter. \n\nUsing the Redis Bloom filter for this type of application provides these benefits: \n\n- Cost efficient way to a customized near real-time experience \n- No need to invest in expensive infrastructure  \n\n**Check if a username is taken (SaaS, content publishing platforms)** \n\nThis application answers this question: Has this username/email/domain name/slug already been used?\n \nUse a Bloom filter for every username that has signed up. A new user types in the desired username. The app checks if the username exists in the Bloom filter. \n\n- If no, the user is created and the username is added to the Bloom filter. \n- If yes, the app can decide to either check the main database or reject the username. \n\nThe query time stays the same at scale. \n\nUsing the Redis Bloom filter for this type of application provides these benefits: \n\n- Very fast and efficient way to do a common operation \n- No need to invest in expensive infrastructure"},{"id":"example","title":"Example","role":"example","text":"Consider a bike manufacturer that makes a million different kinds of bikes and you'd like to avoid using a duplicate model name in new models. A Bloom filter can be used to detect duplicates. In the example that follows,  you'll create a filter with space for a million entries and with a 0.1% error rate. Add one model name and check if it exists. Then add multiple model names and check if they exist.\n\n\nBloom filter operations: Use BF.RESERVE to create a filter, BF.ADD and BF.MADD to add items, and BF.EXISTS and BF.MEXISTS to check membership when you need space-efficient probabilistic set membership testing\n\n**Difficulty:** Intermediate\n\n**Commands:** BF.RESERVE, BF.ADD, BF.EXISTS, BF.MADD, BF.MEXISTS\n\n**Complexity:**\n- BF.RESERVE: O(1)\n- BF.ADD: O(k)\n- BF.EXISTS: O(k)\n- BF.MADD: O(k * n)\n- BF.MEXISTS: O(k * n)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nNote: there is always a chance that even with just a few items, there could be a false positive, meaning an item could \"exist\" even though it has not been explicitly added to the Bloom filter. For a more in depth understanding of the probabilistic nature of a Bloom filter, check out the blog posts linked at the bottom of this page."},{"id":"reserving-bloom-filters","title":"Reserving Bloom filters","role":"content","text":"With the Redis Bloom filter, most of the sizing work is done for you: \n\n[code example]\n\n#### 1. False positives rate (`error_rate`)\nThe rate is a decimal value between 0 and 1. For example, for a desired false positive rate of 0.1% (1 in 1000), error_rate should be set to 0.001.\n\n#### 2. Expected capacity (`capacity`)\nThis is the number of items you expect having in your filter in total and is trivial when you have a static set but it becomes more challenging when your set grows over time. It's important to get the number right because if you **oversize** - you'll end up wasting memory. If you **undersize**, the filter will fill up and a new one will have to be stacked on top of it (sub-filter stacking). In the cases when a filter consists of multiple sub-filters stacked on top of each other latency for adds stays the same, but the latency for presence checks increases. The reason for this is the way the checks work: a regular check would first be performed on the top (latest) filter and if a negative answer is returned the next one is checked and so on. That's where the added latency comes from.\n\n#### 3. Scaling (`EXPANSION`)\nAdding an item to a Bloom filter never fails due to the data structure \"filling up\". Instead, the error rate starts to grow. To keep the error close to the one set on filter initialization, the Bloom filter will auto-scale, meaning, when capacity is reached, an additional sub-filter will be created.  \n The size of the new sub-filter is the size of the last sub-filter multiplied by `EXPANSION`. If the number of items to be stored in the filter is unknown, we recommend that you use an expansion of 2 or more to reduce the number of sub-filters. Otherwise, we recommend that you use an expansion of 1 to reduce memory consumption. The default expansion value is 2. \n \n The filter will keep adding more hash functions for every new sub-filter in order to keep your desired error rate. \n\nMaybe you're wondering \"Why would I create a smaller filter with a high expansion rate if I know I'm going to scale anyway?\"; the answer is: for cases where you need to keep many filters (let's say a filter per user, or per product) and most of them will stay small, but some with more activity will have to scale. \n\n#### 4. `NONSCALING`\nIf you know you're not going to scale use the `NONSCALING` flag because that way the filter will use one hash function less. Just remember that if you ever do reach the initially assigned capacity - your error rate will start to grow."},{"id":"total-size-of-a-bloom-filter","title":"Total size of a Bloom filter","role":"content","text":"The actual memory used by a Bloom filter is a function of the chosen error rate:\n\nThe optimal number of hash functions is `ceil(-ln(error_rate) / ln(2))`.\n\nThe required number of bits per item, given the desired `error_rate` and the optimal number of hash functions, is `-ln(error_rate) / ln(2)^2`. Hence, the required number of bits in the filter is `capacity * -ln(error_rate) / ln(2)^2`.\n\n* **1%**    error rate requires  7 hash functions and  9.585 bits per item.\n* **0.1%**  error rate requires 10 hash functions and 14.378 bits per item.\n* **0.01%** error rate requires 14 hash functions and 19.170 bits per item.\n\nJust as a comparison, when using a Redis set for membership testing the memory needed is:\n\n[code example]\n\nFor a set of IP addresses, for example, we would have around 40 bytes (320 bits) per item - considerably higher than the 19.170 bits we need for a Bloom filter with a 0.01% false positives rate."},{"id":"bloom-vs-cuckoo-filters","title":"Bloom vs. Cuckoo filters","role":"content","text":"Bloom filters typically exhibit better performance and scalability when inserting\nitems (so if you're often adding items to your dataset, then a Bloom filter may be ideal).\nCuckoo filters are quicker on check operations and also allow deletions."},{"id":"performance","title":"Performance","role":"performance","text":"Insertion in a Bloom filter is O(K), where `k` is the number of hash functions. \n\nChecking for an item is O(K) or O(K*n) for stacked filters, where n is the number of stacked filters."},{"id":"academic-sources","title":"Academic sources","role":"content","text":"- [Space/Time Trade-offs in Hash Coding with Allowable Errors](http://www.dragonwins.com/domains/getteched/bbc/literature/Bloom70.pdf) by Burton H. Bloom.\n- [Scalable Bloom Filters](https://gsd.di.uminho.pt/members/cbm/ps/dbloom.pdf)"},{"id":"references","title":"References","role":"related","text":""},{"id":"webinars","title":"Webinars","role":"content","text":"1. [Probabilistic Data Structures - The most useful thing in Redis you probably aren't using](https://youtu.be/dq-0xagF7v8?t=102)"},{"id":"blog-posts","title":"Blog posts","role":"content","text":"1. [RedisBloom Quick Start Tutorial](https://docs.redis.com/latest/modules/redisbloom/redisbloom-quickstart/)\n1. [Developing with Bloom Filters](https://redis.io/blog/bloom-filter/)\n1. [RedisBloom on Redis Software](https://redis.com/redis-enterprise/redis-bloom/)\n1. [Probably and No: Redis, RedisBloom, and Bloom Filters](https://redis.com/blog/redis-redisbloom-bloom-filters/)"}],"examples":[{"id":"example-ex0","language":"plaintext","code":"> BF.RESERVE bikes:models 0.001 1000000\nOK\n> BF.ADD bikes:models \"Smoky Mountain Striker\"\n(integer) 1\n> BF.EXISTS bikes:models \"Smoky Mountain Striker\"\n(integer) 1\n> BF.MADD bikes:models \"Rocky Mountain Racer\" \"Cloudy City Cruiser\" \"Windy City Wippet\"\n1) (integer) 1\n2) (integer) 1\n3) (integer) 1\n> BF.MEXISTS bikes:models \"Rocky Mountain Racer\" \"Cloudy City Cruiser\" \"Windy City Wippet\"\n1) (integer) 1\n2) (integer) 1\n3) (integer) 1","section_id":"example"},{"id":"example-ex1","language":"csharp","code":"bool res1 = db.BF().Reserve(\"bikes:models\", 0.01, 1000);\n        Console.WriteLine(res1);    // >>> True\n\n        bool res2 = db.BF().Add(\"bikes:models\", \"Smoky Mountain Striker\");\n        Console.WriteLine(res2);    // >>> True\n\n        bool res3 = db.BF().Exists(\"bikes:models\", \"Smoky Mountain Striker\");\n        Console.WriteLine(res3);    // >>> True\n\n        bool[] res4 = db.BF().MAdd(\"bikes:models\", new RedisValue[]{\n            \"Rocky Mountain Racer\",\n            \"Cloudy City Cruiser\",\n            \"Windy City Wippet\"\n        });\n        Console.WriteLine(string.Join(\", \", res4)); // >>> True, True, True\n\n        bool[] res5 = db.BF().MExists(\"bikes:models\", [\n            \"Rocky Mountain Racer\",\n            \"Cloudy City Cruiser\",\n            \"Windy City Wippet\"\n        ]);\n        Console.WriteLine(string.Join(\", \", res5)); // >>> True, True, True","section_id":"example"},{"id":"example-ex2","language":"go","code":"res1, err := rdb.BFReserve(ctx, \"bikes:models\", 0.01, 1000).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> OK\n\n\tres2, err := rdb.BFAdd(ctx, \"bikes:models\", \"Smoky Mountain Striker\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> true\n\n\tres3, err := rdb.BFExists(ctx, \"bikes:models\", \"Smoky Mountain Striker\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3) // >>> true\n\n\tres4, err := rdb.BFMAdd(ctx, \"bikes:models\",\n\t\t\"Rocky Mountain Racer\",\n\t\t\"Cloudy City Cruiser\",\n\t\t\"Windy City Wippet\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4) // >>> [true true true]\n\n\tres5, err := rdb.BFMExists(ctx, \"bikes:models\",\n\t\t\"Rocky Mountain Racer\",\n\t\t\"Cloudy City Cruiser\",\n\t\t\"Windy City Wippet\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res5) // >>> [true true true]","section_id":"example"},{"id":"example-ex3","language":"java","code":"CompletableFuture<Void> bloomExample = asyncCommands\n                    .bfReserve(\"bikes:models\", 0.01, 1000)\n                    .thenCompose(res1 -> {\n                        System.out.println(res1);\n                        // >>> OK\n                        return asyncCommands.bfAdd(\"bikes:models\", \"Smoky Mountain Striker\");\n                    })\n                    .thenCompose(res2 -> {\n                        System.out.println(res2);\n                        // >>> true\n                        return asyncCommands.bfExists(\"bikes:models\", \"Smoky Mountain Striker\");\n                    })\n                    .thenCompose(res3 -> {\n                        System.out.println(res3);\n                        // >>> true\n                        return asyncCommands.bfMAdd(\"bikes:models\",\n                                \"Rocky Mountain Racer\",\n                                \"Cloudy City Cruiser\",\n                                \"Windy City Wippet\");\n                    })\n                    .thenCompose(res4 -> {\n                        System.out.println(res4);\n                        // >>> [true, true, true]\n                        return asyncCommands.bfMExists(\"bikes:models\",\n                                \"Rocky Mountain Racer\",\n                                \"Cloudy City Cruiser\",\n                                \"Windy City Wippet\");\n                    })\n                    .thenAccept(res5 -> {\n                        System.out.println(res5);\n                        // >>> [true, true, true]\n                    })\n                    .toCompletableFuture();\n\n            bloomExample.join();","section_id":"example"},{"id":"example-ex4","language":"java","code":"Mono<Void> bloomExample = reactiveCommands\n                    .bfReserve(\"bikes:models\", 0.01, 1000)\n                    .doOnNext(res1 -> {\n                        System.out.println(res1);\n                        // >>> OK\n                    })\n                    .then(reactiveCommands.bfAdd(\"bikes:models\", \"Smoky Mountain Striker\"))\n                    .doOnNext(res2 -> {\n                        System.out.println(res2);\n                        // >>> true\n                    })\n                    .then(reactiveCommands.bfExists(\"bikes:models\", \"Smoky Mountain Striker\"))\n                    .doOnNext(res3 -> {\n                        System.out.println(res3);\n                        // >>> true\n                    })\n                    .thenMany(reactiveCommands.bfMAdd(\"bikes:models\",\n                            \"Rocky Mountain Racer\",\n                            \"Cloudy City Cruiser\",\n                            \"Windy City Wippet\"))\n                    .map(Value::getValue)\n                    .collectList()\n                    .doOnNext(res4 -> {\n                        System.out.println(res4);\n                        // >>> [true, true, true]\n                    })\n                    .thenMany(reactiveCommands.bfMExists(\"bikes:models\",\n                            \"Rocky Mountain Racer\",\n                            \"Cloudy City Cruiser\",\n                            \"Windy City Wippet\"))\n                    .collectList()\n                    .doOnNext(res5 -> {\n                        System.out.println(res5);\n                        // >>> [true, true, true]\n                    })\n                    .then();\n\n            bloomExample.block();","section_id":"example"},{"id":"example-ex5","language":"java","code":"String res1 = jedis.bfReserve(\"bikes:models\", 0.01, 1000);\n        System.out.println(res1); // >>> OK\n\n\n        boolean res2 = jedis.bfAdd(\"bikes:models\", \"Smoky Mountain Striker\");\n        System.out.println(res2); // >>> True\n\n        boolean res3 = jedis.bfExists(\"bikes:models\", \"Smoky Mountain Striker\");\n        System.out.println(res3); // >>> True\n\n        List<Boolean> res4 = jedis.bfMAdd(\"bikes:models\",\n                \"Rocky Mountain Racer\",\n                \"Cloudy City Cruiser\",\n                \"Windy City Wippet\");\n        System.out.println(res4); // >>> [True, True, True]\n\n        List<Boolean> res5 = jedis.bfMExists(\"bikes:models\",\n                \"Rocky Mountain Racer\",\n                \"Cloudy City Cruiser\",\n                \"Windy City Wippet\");\n        System.out.println(res5); // >>> [True, True, True]","section_id":"example"},{"id":"example-ex6","language":"javascript","code":"const res1 = await client.bf.reserve('bikes:models', 0.01, 1000);\nconsole.log(res1);  // >>> OK\n\nconst res2 = await client.bf.add('bikes:models', 'Smoky Mountain Striker');\nconsole.log(res2);  // >>> true\n\nconst res3 = await client.bf.exists('bikes:models', 'Smoky Mountain Striker');\nconsole.log(res3);  // >>> true\n\nconst res4 = await client.bf.mAdd('bikes:models', [\n  'Rocky Mountain Racer',\n  'Cloudy City Cruiser',\n  'Windy City Wippet'\n]);\nconsole.log(res4);  // >>> [true, true, true]\n\nconst res5 = await client.bf.mExists('bikes:models', [\n  'Rocky Mountain Racer',\n  'Cloudy City Cruiser',\n  'Windy City Wippet'\n]);\nconsole.log(res5);  // >>> [true, true, true]","section_id":"example"},{"id":"example-ex7","language":"php","code":"$res1 = $r->bfreserve('bikes:models', 0.01, 1000);\n        echo $res1 . PHP_EOL;\n        // >>> OK\n\n        $res2 = $r->bfadd('bikes:models', 'Smoky Mountain Striker');\n        echo $res2 . PHP_EOL;\n        // >>> 1\n\n        $res3 = $r->bfexists('bikes:models', 'Smoky Mountain Striker');\n        echo $res3 . PHP_EOL;\n        // >>> 1\n\n        $res4 = $r->bfmadd(\n            'bikes:models',\n            'Rocky Mountain Racer',\n            'Cloudy City Cruiser',\n            'Windy City Wippet'\n        );\n        echo json_encode($res4) . PHP_EOL;\n        // >>> [1,1,1]\n\n        $res5 = $r->bfmexists(\n            'bikes:models',\n            'Rocky Mountain Racer',\n            'Cloudy City Cruiser',\n            'Windy City Wippet'\n        );\n        echo json_encode($res5) . PHP_EOL;\n        // >>> [1,1,1]","section_id":"example"},{"id":"example-ex8","language":"python","code":"res1 = r.bf().reserve(\"bikes:models\", 0.01, 1000)\nprint(res1)  # >>> True\n\nres2 = r.bf().add(\"bikes:models\", \"Smoky Mountain Striker\")\nprint(res2)  # >>> True\n\nres3 = r.bf().exists(\"bikes:models\", \"Smoky Mountain Striker\")\nprint(res3)  # >>> True\n\nres4 = r.bf().madd(\n    \"bikes:models\",\n    \"Rocky Mountain Racer\",\n    \"Cloudy City Cruiser\",\n    \"Windy City Wippet\",\n)\nprint(res4)  # >>> [True, True, True]\n\nres5 = r.bf().mexists(\n    \"bikes:models\",\n    \"Rocky Mountain Racer\",\n    \"Cloudy City Cruiser\",\n    \"Windy City Wippet\",\n)\nprint(res5)  # >>> [True, True, True]","section_id":"example"},{"id":"example-ex9","language":"rust","code":"let _: () = r\n            .bf_reserve(\"bikes:models\", 0.001, 1_000_000)\n            .await\n            .expect(\"Failed to reserve Bloom filter\");\n        println!(\"OK\"); // >>> OK\n\n        let res1: bool = r\n            .bf_add(\"bikes:models\", \"Smoky Mountain Striker\")\n            .await\n            .expect(\"Failed to add item to Bloom filter\");\n        println!(\"{res1}\"); // >>> true\n\n        let res2: bool = r\n            .bf_exists(\"bikes:models\", \"Smoky Mountain Striker\")\n            .await\n            .expect(\"Failed to check item in Bloom filter\");\n        println!(\"{res2}\"); // >>> true\n\n        let res3: Vec<bool> = r\n            .bf_madd(\n                \"bikes:models\",\n                &[\n                    \"Rocky Mountain Racer\",\n                    \"Cloudy City Cruiser\",\n                    \"Windy City Wippet\",\n                ],\n            )\n            .await\n            .expect(\"Failed to add items to Bloom filter\");\n        println!(\"{res3:?}\"); // >>> [true, true, true]\n\n        let res4: Vec<bool> = r\n            .bf_mexists(\n                \"bikes:models\",\n                &[\n                    \"Rocky Mountain Racer\",\n                    \"Cloudy City Cruiser\",\n                    \"Windy City Wippet\",\n                ],\n            )\n            .await\n            .expect(\"Failed to check items in Bloom filter\");\n        println!(\"{res4:?}\"); // >>> [true, true, true]","section_id":"example"},{"id":"example-ex10","language":"rust","code":"let _: () = r\n            .bf_reserve(\"bikes:models\", 0.001, 1_000_000)\n            .expect(\"Failed to reserve Bloom filter\");\n        println!(\"OK\"); // >>> OK\n\n        let res1: bool = r\n            .bf_add(\"bikes:models\", \"Smoky Mountain Striker\")\n            .expect(\"Failed to add item to Bloom filter\");\n        println!(\"{res1}\"); // >>> true\n\n        let res2: bool = r\n            .bf_exists(\"bikes:models\", \"Smoky Mountain Striker\")\n            .expect(\"Failed to check item in Bloom filter\");\n        println!(\"{res2}\"); // >>> true\n\n        let res3: Vec<bool> = r\n            .bf_madd(\n                \"bikes:models\",\n                &[\n                    \"Rocky Mountain Racer\",\n                    \"Cloudy City Cruiser\",\n                    \"Windy City Wippet\",\n                ],\n            )\n            .expect(\"Failed to add items to Bloom filter\");\n        println!(\"{res3:?}\"); // >>> [true, true, true]\n\n        let res4: Vec<bool> = r\n            .bf_mexists(\n                \"bikes:models\",\n                &[\n                    \"Rocky Mountain Racer\",\n                    \"Cloudy City Cruiser\",\n                    \"Windy City Wippet\",\n                ],\n            )\n            .expect(\"Failed to check items in Bloom filter\");\n        println!(\"{res4:?}\"); // >>> [true, true, true]","section_id":"example"},{"id":"reserving-bloom-filters-ex0","language":"plaintext","code":"BF.RESERVE {key} {error_rate} {capacity} [EXPANSION expansion] [NONSCALING]","section_id":"reserving-bloom-filters"},{"id":"total-size-of-a-bloom-filter-ex0","language":"plaintext","code":"memory_with_sets = capacity*(192b + value)","section_id":"total-size-of-a-bloom-filter"}]}
{"schema_version":2,"id":"develop/data-types/probabilistic/configuration","title":"Configuration Parameters","url":"https://redis.io/docs/latest/develop/data-types/probabilistic/configuration/","summary":"Redis probabilistic data structures support multiple configuration parameters.","aliases":["/data-types/probabilistic/configuration/","/manual/data-types/probabilistic/configuration/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-10T15:45:40+01:00","page_type":"content","content_hash":"ceec300f0731d68be041f38a199856be2c70536eef2b5ecac86a0a168c48737c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> As of Redis 8 in Redis Open Source (Redis 8), configuration parameters for the probabilistic data structures are now set in the following ways:\n> * At load time via your `redis.conf` file.\n> * At run time (where applicable) using the [`CONFIG SET`](https://redis.io/docs/latest/commands/config-set) command.\n>\n> Also, Redis 8 persists probabilistic configuration parameters just like any other configuration parameters (e.g., using the [`CONFIG REWRITE`](https://redis.io/docs/latest/commands/config-rewrite) command)."},{"id":"redis-probabilistic-data-structure-configuration-parameters","title":"Redis probabilistic data structure configuration parameters","role":"content","text":"The following table summarizes which Bloom filter configuration parameters can be set at run-time, and compatibility with Redis Software and Redis Cloud.\n\n| Parameter name<br />(version < 8.0) | Parameter name<br />(version &#8805; 8.0) | Run-time | Redis<br />Software | Redis<br />Cloud |\n| :------- | :------- | :------- | :------- | :------- |\n| ERROR_RATE         | [bf-error-rate](#bf-error-rate)             | :white_check_mark: | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> |\n|                    | [bf-expansion-factor](#bf-expansion-factor) | :white_check_mark: |||\n| INITIAL_SIZE       | [bf-initial-size](#bf-initial-size)         | :white_check_mark: | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> |\n\nThe following table summarizes which Cuckoo filter configuration parameters can be set at run-time, and compatibility with Redis Software and Redis Cloud.\n\n| Parameter name<br />(version < 8.0) | Parameter name<br />(version &#8805; 8.0) | Run-time | Redis<br />Software | Redis<br />Cloud |\n| :------- | :------- | :------- | :------- | :------- |\n|                    | [cf-bucket-size](#cf-bucket-size)           | :white_check_mark: |||\n|                    | [cf-initial-size](#cf-initial-size)         | :white_check_mark: |||\n|                    | [cf-expansion-factor](#cf-expansion-factor) | :white_check_mark: |||\n| CF_MAX_EXPANSIONS  | [cf-max-expansions](#cf-max-expansions)     | :white_check_mark: | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> |\n|                    | [cf-max-iterations](#cf-max-iterations)     | :white_check_mark: |||\n\n> [!NOTE]\n> Parameter names for Redis Open Source versions < 8.0, while deprecated, will still be supported in Redis 8.\n\nSee also [Redis configuration](https://redis.io/docs/latest/operate/oss_and_stack/management/config).\n\n---\n\n> [!WARNING]\n> A filter should always be sized for the expected capacity and the desired error rate.\n> Using the `INSERT` family commands with the default values should be used in cases where many small filters exist and the expectation is most will remain at around the default sizes.\n> Not optimizing a filter for its intended use will result in degradation of performance and memory efficiency."},{"id":"default-parameters-for-bloom-filters","title":"Default parameters for Bloom filters","role":"content","text":""},{"id":"bf-error-rate","title":"bf-error-rate","role":"content","text":"Default false positive rate for Bloom filters.\n\nType: double\n\nValid range: `(0 .. 1)`. Though the valid range is `(0 .. 1)` (corresponding to `> 0%` to `< 100%` false positive rate), any value greater than `0.25` is treated as `0.25`.\n\nDefault: `0.01`"},{"id":"bf-expansion-factor","title":"bf-expansion-factor","role":"content","text":"Added in v8.0.0.\n\nExpansion factor for Bloom filters.\n\nType: integer\n\nValid range: `[0 .. 32768]`.\n\nDefault: `2`"},{"id":"bf-initial-size","title":"bf-initial-size","role":"content","text":"Initial capacity for Bloom filters.\n\nType: integer\n\nValid range: `[1 .. 1048576]`\n\nDefault: `100`"},{"id":"default-parameters-for-cuckoo-filters","title":"Default parameters for Cuckoo filters","role":"content","text":""},{"id":"cf-bucket-size","title":"cf-bucket-size","role":"content","text":"Added in v8.0.0.\n\nThe number of items in each Cuckoo filter bucket.\n\nType: integer\n\nValid range: `[1 .. 255]`\n\nDefault: `2`"},{"id":"cf-initial-size","title":"cf-initial-size","role":"content","text":"Added in v8.0.0.\n\nCuckoo filter initial capacity.\n\nType: integer\n\nValid range: `[2*cf-bucket-size .. 1048576]`\n\nDefault: `1024`"},{"id":"cf-expansion-factor","title":"cf-expansion-factor","role":"content","text":"Added in v8.0.0.\n\nExpansion factor for Cuckoo filters.\n\nType: integer\n\nValid range: `[0 .. 32768]`\n\nDefault: `1`"},{"id":"cf-max-expansions","title":"cf-max-expansions","role":"content","text":"The maximum number of expansions for Cuckoo filters.\n\nType: integer\n\nValid range: `[1 .. 65535]`\n\nDefault: `32`"},{"id":"cf-max-iterations","title":"cf-max-iterations","role":"content","text":"Added in v8.0.0\n\nThe maximum number of iterations for Cuckoo filters.\n\nType: integer\n\nValid range: `[1 .. 65535]`\n\nDefault: `20`"},{"id":"setting-configuration-parameters-on-module-load-deprecated","title":"Setting configuration parameters on module load (deprecated)","role":"configuration","text":"These methods are deprecated beginning with Redis 8.\n\nSetting configuration parameters at load-time is done by appending arguments after the `--loadmodule` argument when starting a server from the command line or after the `loadmodule` directive in a Redis config file. For example:\n\nIn [redis.conf](https://redis.io/docs/latest/operate/oss_and_stack/management/config):\n\n[code example]\n\nFrom the [Redis CLI](https://redis.io/docs/latest/develop/tools/cli), using the [MODULE LOAD](https://redis.io/docs/latest/commands/module-load) command:\n\n[code example]\n\nFrom the command line:\n\n[code example]"}],"examples":[{"id":"setting-configuration-parameters-on-module-load-deprecated-ex0","language":"sh","code":"loadmodule ./redisbloom.so [OPT VAL]...","section_id":"setting-configuration-parameters-on-module-load-deprecated"},{"id":"setting-configuration-parameters-on-module-load-deprecated-ex1","language":"plaintext","code":"127.0.0.6379> MODULE LOAD redisbloom.so [OPT VAL]...","section_id":"setting-configuration-parameters-on-module-load-deprecated"},{"id":"setting-configuration-parameters-on-module-load-deprecated-ex2","language":"sh","code":"$ redis-server --loadmodule ./redisbloom.so [OPT VAL]...","section_id":"setting-configuration-parameters-on-module-load-deprecated"}]}
{"schema_version":2,"id":"develop/data-types/probabilistic/count-min-sketch","title":"Count-min sketch","url":"https://redis.io/docs/latest/develop/data-types/probabilistic/count-min-sketch/","summary":"Count-min sketch is a probabilistic data structure that estimates the frequency of an element in a data stream.","aliases":["/data-types/probabilistic/count-min-sketch/","/manual/data-types/probabilistic/count-min-sketch/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-10T15:45:40+01:00","page_type":"content","content_hash":"46540d233775c850d9d6c66a46b492269ab958e7db61c521671d75d5be2f9dea","sections":[{"id":"count-min-sketch-command-summary","title":"Count-min sketch command summary","role":"content","text":"**6 commands in this group:**\n\n[View all cms commands](https://redis.io/commands/?group=cms)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [CMS.INCRBY](https://redis.io/commands/cms.incrby/) | Increases the count of one or more items by increment, which may be negative | O(n) where n is the number of items | 2.0.0 |\n| [CMS.INFO](https://redis.io/commands/cms.info/) | Returns information about a sketch | O(1) | 2.0.0 |\n| [CMS.INITBYDIM](https://redis.io/commands/cms.initbydim/) | Initializes a Count-Min Sketch to dimensions specified by user | O(1) | 2.0.0 |\n| [CMS.INITBYPROB](https://redis.io/commands/cms.initbyprob/) | Initializes a Count-Min Sketch to accommodate requested tolerances. | O(1) | 2.0.0 |\n| [CMS.MERGE](https://redis.io/commands/cms.merge/) | Merges several sketches into one sketch | O(n) where n is the number of sketches | 2.0.0 |\n| [CMS.QUERY](https://redis.io/commands/cms.query/) | Returns the count for one or more items in a sketch | O(n) where n is the number of items | 2.0.0 |\n\n\n\nCount-Min Sketch is a probabilistic data structure in Redis Open Source that can be used to estimate the frequency of events/elements in a stream of data.\n\nIt uses a sub-linear space at the expense of over-counting some events due to collisions. It consumes a stream of events/elements and keeps estimated counters of their frequency.\n\nIt is very important to know that the results coming from a Count-Min sketch lower than a certain threshold (determined by the error_rate) should be ignored and often even approximated to zero. So Count-Min sketch is indeed a data-structure for counting frequencies of elements in a stream, but it's only useful for higher counts. Very low counts should be ignored as noise."},{"id":"use-cases","title":"Use cases","role":"content","text":"**Products (retail, online shops)** \n\nThis application answers this question: What was the sales volume (on a certain day) for a product? \n\nUse one Count-Min sketch created per day (period). Every product sale goes into the CMS. The CMS give reasonably accurate results for the products that contribute the most toward the sales. Products with low percentage of the total sales are ignored."},{"id":"examples","title":"Examples","role":"example","text":"Assume you select an error rate of 0.1% (0.001) with a certainty of 99.8% (0.998). This means you have an error probability of 0.2% (0.002). Your sketch strives to keep the error within 0.1% of the total count of all elements you've added. There's a 0.2% chance the error might exceed this—like when an element below the threshold overlaps with one above it. When you add a few items to the CMS and evaluate their frequency, remember that in such a small sample, collisions are rare, as seen with other probabilistic data structures.\n\nCount-min sketch operations: Use CMS.INITBYPROB to create a sketch, CMS.INCRBY to increment element counts, CMS.QUERY to estimate frequencies, and CMS.INFO to inspect sketch properties when you need to estimate element frequencies in a data stream\n\n**Difficulty:** Intermediate\n\n**Commands:** CMS.INITBYPROB, CMS.INCRBY, CMS.QUERY, CMS.INFO\n\n**Complexity:**\n- CMS.INITBYPROB: O(1)\n- CMS.INCRBY: O(n)\n- CMS.QUERY: O(n)\n- CMS.INFO: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\n##### Example 1:\nIf we had a uniform distribution of 1000 elements where each has a count of around 500 the threshold would be 500: \n\n[code example]\n\nThis shows that a CMS is maybe not the best data structure to count frequency of a uniformly distributed stream.\nLet's try decreasing the error to 0.01%:\n\n[code example]\nThis threshold looks more acceptable already, but it means we will need a bigger sketch width `w = 2/error = 20 000` and consequently - more memory.\n\n##### Example 2:\nIn another example let's imagine a normal (gaussian) distribution where we have 1000 elements, out of which 800 will have a summed count of 400K (with an average count of 500) and 200 elements will have a much higher summed count of 1.6M (with an average count of 8000), making them the heavy hitters (elephant flow). The threshold after \"populating\" the sketch with all the 1000 elements would be:\n\n[code example]\n\nThis threshold seems to be sitting comfortably between the 2 average counts 500 and 8000 so the initial chosen error rate should be working well for this case."},{"id":"sizing","title":"Sizing","role":"content","text":"Even though the Count-Min sketch is similar to Bloom filter in many ways, its sizing is considerably more complex. The initialisation command receives only two sizing parameters, but you have to understand them thoroughly if you want to have a usable sketch.\n\n[code example]"},{"id":"1-error","title":"1. Error","role":"content","text":"The `error` parameter will determine the width `w` of your sketch and the probability will determine the number of hash functions (depth `d`). The error rate we choose will determine the threshold above which we can trust the result from the sketch. The correlation is:\n[code example]\nor\n[code example]\n\nwhere `total_count` is the sum of the count of all elements that can be obtained from the `count` key of the result of the [`CMS.INFO`](https://redis.io/docs/latest/commands/cms.info) command and is of course dynamic - it changes with every new increment in the sketch. At creation time you can approximate the `total_count` ratio as a product of the average count you'll be expecting in the sketch and the average number of elements.\n\nSince the threshold is a function of the total count in the filter it's very important to note that it will grow as  the count grows, but knowing the total count we can always dynamically calculate the threshold. If a result is below it - it can be discarded."},{"id":"2-probability","title":"2. Probability","role":"content","text":"`probability` in this data structure represents the chance of an element that has a count below the threshold to collide with elements that had a count above the threshold on all sketches/depths thus returning a min-count of a frequently occurring element instead of its own."},{"id":"performance","title":"Performance","role":"performance","text":"Adding, updating and querying for elements in a CMS has a time complexity O(1)."},{"id":"academic-sources","title":"Academic sources","role":"content","text":"- [An Improved Data Stream Summary: The Count-Min Sketch and its Applications](http://dimacs.rutgers.edu/~graham/pubs/papers/cm-full.pdf)"},{"id":"references","title":"References","role":"related","text":"- [Count-Min Sketch: The Art and Science of Estimating Stuff](https://redis.com/blog/count-min-sketch-the-art-and-science-of-estimating-stuff/)"}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> CMS.INITBYPROB bikes:profit 0.001 0.002\nOK\n> CMS.INCRBY bikes:profit \"Smokey Mountain Striker\" 100\n1) (integer) 100\n> CMS.INCRBY bikes:profit \"Rocky Mountain Racer\" 200 \"Cloudy City Cruiser\" 150\n1) (integer) 200\n2) (integer) 150\n> CMS.QUERY bikes:profit \"Smokey Mountain Striker\" \"Rocky Mountain Racer\" \"Cloudy City Cruiser\" \"Terrible Bike Name\"\n1) (integer) 100\n2) (integer) 200\n3) (integer) 150\n4) (integer) 0\n> CMS.INFO bikes:profit\n1) width\n2) (integer) 2000\n3) depth\n4) (integer) 9\n5) count\n6) (integer) 450","section_id":"examples"},{"id":"examples-ex1","language":"csharp","code":"bool res1 = db.CMS().InitByProb(\"bikes:profit\", 0.001, 0.002);\n        Console.WriteLine(res1);    // >>> True\n\n        long res2 = db.CMS().IncrBy(\"bikes:profit\", \"Smoky Mountain Striker\", 100);\n        Console.WriteLine(res2);    // >>> 100\n\n        long[] res3 = db.CMS().IncrBy(\"bikes:profit\",\n            [\n                new(\"Rocky Mountain Racer\", 200),\n                new(\"Cloudy City Cruiser\", 150)\n            ]\n        );\n        Console.WriteLine(string.Join(\", \", res3)); // >>> 200, 150\n\n        long[] res4 = db.CMS().Query(\"bikes:profit\", new RedisValue[] { \"Smoky Mountain Striker\" });\n        Console.WriteLine(string.Join(\", \", res4)); // >>> 100\n\n        CmsInformation res5 = db.CMS().Info(\"bikes:profit\");\n        Console.WriteLine($\"Width: {res5.Width}, Depth: {res5.Depth}, Count: {res5.Count}\");\n        // >>> Width: 2000, Depth: 9, Count: 450","section_id":"examples"},{"id":"examples-ex2","language":"go","code":"res1, err := rdb.CMSInitByProb(ctx, \"bikes:profit\", 0.001, 0.002).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> OK\n\n\tres2, err := rdb.CMSIncrBy(ctx, \"bikes:profit\",\n\t\t\"Smoky Mountain Striker\", 100,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> [100]\n\n\tres3, err := rdb.CMSIncrBy(ctx, \"bikes:profit\",\n\t\t\"Rocky Mountain Racer\", 200,\n\t\t\"Cloudy City Cruiser\", 150,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3) // >>> [200 150]\n\n\tres4, err := rdb.CMSQuery(ctx, \"bikes:profit\",\n\t\t\"Smoky Mountain Striker\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4) // >>> [100]\n\n\tres5, err := rdb.CMSInfo(ctx, \"bikes:profit\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Printf(\"Width: %v, Depth: %v, Count: %v\",\n\t\tres5.Width, res5.Depth, res5.Count)\n\t// >>> Width: 2000, Depth: 9, Count: 450","section_id":"examples"},{"id":"examples-ex3","language":"java","code":"CompletableFuture<Void> cmsExample = asyncCommands\n                    .cmsInitByProb(\"bikes:profit\", 0.001, 0.002)\n                    .thenCompose(res1 -> {\n                        System.out.println(res1);\n                        // >>> OK\n                        return asyncCommands.cmsIncrBy(\"bikes:profit\",\n                                IncrementPair.of(\"Smoky Mountain Striker\", 100L));\n                    })\n                    .thenCompose(res2 -> {\n                        System.out.println(res2);\n                        // >>> [100]\n                        return asyncCommands.cmsIncrBy(\"bikes:profit\",\n                                IncrementPair.of(\"Rocky Mountain Racer\", 200L),\n                                IncrementPair.of(\"Cloudy City Cruiser\", 150L));\n                    })\n                    .thenCompose(res3 -> {\n                        System.out.println(res3);\n                        // >>> [200, 150]\n                        return asyncCommands.cmsQuery(\"bikes:profit\", \"Smoky Mountain Striker\");\n                    })\n                    .thenCompose(res4 -> {\n                        System.out.println(res4);\n                        // >>> [100]\n                        return asyncCommands.cmsInfo(\"bikes:profit\");\n                    })\n                    .thenAccept(res5 -> {\n                        System.out.println(res5.getWidth() + \" \" + res5.getDepth() + \" \" + res5.getCount());\n                        // >>> 2000 9 450\n                    })\n                    .toCompletableFuture();\n\n            cmsExample.join();","section_id":"examples"},{"id":"examples-ex4","language":"java","code":"Mono<Void> cmsExample = reactiveCommands\n                    .cmsInitByProb(\"bikes:profit\", 0.001, 0.002)\n                    .doOnNext(res1 -> {\n                        System.out.println(res1);\n                        // >>> OK\n                    })\n                    .thenMany(reactiveCommands.cmsIncrBy(\"bikes:profit\",\n                            IncrementPair.of(\"Smoky Mountain Striker\", 100L)))\n                    .collectList()\n                    .doOnNext(res2 -> {\n                        System.out.println(res2);\n                        // >>> [100]\n                    })\n                    .thenMany(reactiveCommands.cmsIncrBy(\"bikes:profit\",\n                            IncrementPair.of(\"Rocky Mountain Racer\", 200L),\n                            IncrementPair.of(\"Cloudy City Cruiser\", 150L)))\n                    .collectList()\n                    .doOnNext(res3 -> {\n                        System.out.println(res3);\n                        // >>> [200, 150]\n                    })\n                    .thenMany(reactiveCommands.cmsQuery(\"bikes:profit\", \"Smoky Mountain Striker\"))\n                    .collectList()\n                    .doOnNext(res4 -> {\n                        System.out.println(res4);\n                        // >>> [100]\n                    })\n                    .then(reactiveCommands.cmsInfo(\"bikes:profit\"))\n                    .doOnNext(res5 -> {\n                        System.out.println(res5.getWidth() + \" \" + res5.getDepth() + \" \" + res5.getCount());\n                        // >>> 2000 9 450\n                    })\n                    .then();\n\n            cmsExample.block();","section_id":"examples"},{"id":"examples-ex5","language":"java","code":"String res1 = jedis.cmsInitByProb(\"bikes:profit\", 0.001d, 0.002d);\n    System.out.println(res1); // >>> OK\n\n    long res2 = jedis.cmsIncrBy(\"bikes:profit\", \"Smoky Mountain Striker\", 100L);\n    System.out.println(res2); // >>> 100\n\n    List<Long> res3 = jedis.cmsIncrBy(\"bikes:profit\", new HashMap<String, Long>() {{\n      put(\"Rocky Mountain Racer\", 200L);\n      put(\"Cloudy City Cruiser\", 150L);\n    }});\n    System.out.println(res3); // >>> [200, 150]\n\n    List<Long> res4 = jedis.cmsQuery(\"bikes:profit\", \"Smoky Mountain Striker\");\n    System.out.println(res4); // >>> [100]\n\n    Map<String, Object> res5 = jedis.cmsInfo(\"bikes:profit\");\n    System.out.println(res5.get(\"width\") + \" \" + res5.get(\"depth\") + \" \" + res5.get(\"count\")); // >>> 2000 9 450","section_id":"examples"},{"id":"examples-ex6","language":"javascript","code":"const res1 = await client.cms.initByProb('bikes:profit', 0.001, 0.002);\nconsole.log(res1);  // >>> OK\n\nconst res2 = await client.cms.incrBy('bikes:profit', {\n    item: 'Smoky Mountain Striker',\n    incrementBy: 100\n});\nconsole.log(res2);  // >>> [100]\n\nconst res3 = await client.cms.incrBy('bikes:profit', [\n  {\n    item: 'Rocky Mountain Racer',\n    incrementBy: 200\n  },\n  {\n    item: 'Cloudy City Cruiser',\n    incrementBy: 150\n  }\n]);\nconsole.log(res3);  // >>> [200, 150]\n\nconst res4 = await client.cms.query('bikes:profit', 'Smoky Mountain Striker');\nconsole.log(res4);  // >>> [100]\n\nconst res5 = await client.cms.info('bikes:profit');\nconsole.log(res5.width, res5.depth, res5.count);  // >>> 2000 9 450","section_id":"examples"},{"id":"examples-ex7","language":"php","code":"$res1 = $r->cmsinitbyprob('bikes:profit', 0.001, 0.002);\n        echo $res1 . PHP_EOL;\n        // >>> OK\n\n        $res2 = $r->cmsincrby('bikes:profit', 'Smoky Mountain Striker', 100);\n        echo json_encode($res2) . PHP_EOL;\n        // >>> [100]\n\n        $res3 = $r->cmsincrby(\n            'bikes:profit',\n            'Rocky Mountain Racer', 200,\n            'Cloudy City Cruiser', 150\n        );\n        echo json_encode($res3) . PHP_EOL;\n        // >>> [200,150]\n\n        $res4 = $r->cmsquery('bikes:profit', 'Smoky Mountain Striker');\n        echo json_encode($res4) . PHP_EOL;\n        // >>> [100]\n\n        $res5 = $r->cmsinfo('bikes:profit');\n        echo $res5['width'] . ' ' . $res5['depth'] . ' ' . $res5['count'] . PHP_EOL;\n        // >>> 2000 9 450","section_id":"examples"},{"id":"examples-ex8","language":"python","code":"res1 = r.cms().initbyprob(\"bikes:profit\", 0.001, 0.002)\nprint(res1)  # >>> True\n\nres2 = r.cms().incrby(\"bikes:profit\", [\"Smoky Mountain Striker\"], [100])\nprint(res2)  # >>> [100]\n\nres3 = r.cms().incrby(\n    \"bikes:profit\", [\"Rocky Mountain Racer\", \"Cloudy City Cruiser\"], [200, 150]\n)\nprint(res3)  # >>> [200, 150]\n\nres4 = r.cms().query(\"bikes:profit\", \"Smoky Mountain Striker\")\nprint(res4)  # >>> [100]\n\nres5 = r.cms().info(\"bikes:profit\")\nprint(res5.width, res5.depth, res5.count)  # >>> 2000 9 450","section_id":"examples"},{"id":"examples-ex9","language":"plaintext","code":"threshold = error * total_count  = 0.001 * (1000*500) = 500","section_id":"examples"},{"id":"examples-ex10","language":"plaintext","code":"threshold = error * total_count  = 0.0001 * (1000*500) = 100","section_id":"examples"},{"id":"examples-ex11","language":"plaintext","code":"threshold = error * total_count = 0.001 * 2M = 2000","section_id":"examples"},{"id":"sizing-ex0","language":"plaintext","code":"CMS.INITBYPROB key error probability","section_id":"sizing"},{"id":"1-error-ex0","language":"plaintext","code":"threshold = error * total_count","section_id":"1-error"},{"id":"1-error-ex1","language":"plaintext","code":"error = threshold/total_count","section_id":"1-error"}]}
{"schema_version":2,"id":"develop/data-types/probabilistic/cuckoo-filter","title":"Cuckoo filter","url":"https://redis.io/docs/latest/develop/data-types/probabilistic/cuckoo-filter/","summary":"Cuckoo filters are a probabilistic data structure that checks for presence of an element in a set","aliases":["/data-types/probabilistic/cuckoo-filter/","/manual/data-types/probabilistic/cuckoo-filter/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-10T15:45:40+01:00","page_type":"content","content_hash":"252e277f2cb9898c5e329df47e3d70d3bda1944ea2ce21b69b3a98d9158cfe0b","sections":[{"id":"cuckoo-filter-command-summary","title":"Cuckoo filter command summary","role":"content","text":"**13 commands in this group:**\n\n[View all cf commands](https://redis.io/commands/?group=cf)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [CF.ADD](https://redis.io/commands/cf.add/) | Adds an item to a Cuckoo Filter | O(k + i), where k is the number of sub-filters ... | 1.0.0 |\n| [CF.ADDNX](https://redis.io/commands/cf.addnx/) | Adds an item to a Cuckoo Filter if the item did not exist previously. | O(k + i), where k is the number of sub-filters ... | 1.0.0 |\n| [CF.COUNT](https://redis.io/commands/cf.count/) | Return the number of times an item might be in a Cuckoo Filter | O(k), where k is the number of sub-filters | 1.0.0 |\n| [CF.DEBUG](https://redis.io/commands/cf.debug/) | Returns internal debug information about a Cuckoo Filter | O(1) | 1.0.0 |\n| [CF.DEL](https://redis.io/commands/cf.del/) | Deletes an item from a Cuckoo Filter | O(k), where k is the number of sub-filters | 1.0.0 |\n| [CF.EXISTS](https://redis.io/commands/cf.exists/) | Checks whether one or more items exist in a Cuckoo Filter | O(k), where k is the number of sub-filters | 1.0.0 |\n| [CF.INFO](https://redis.io/commands/cf.info/) | Returns information about a Cuckoo Filter | O(1) | 1.0.0 |\n| [CF.INSERT](https://redis.io/commands/cf.insert/) | Adds one or more items to a Cuckoo Filter. A filter will be created if it does not exist | O(n * (k + i)), where n is the number of items,... | 1.0.0 |\n| [CF.INSERTNX](https://redis.io/commands/cf.insertnx/) | Adds one or more items to a Cuckoo Filter if the items did not exist previously. A filter will be created if it does not exist | O(n * (k + i)), where n is the number of items,... | 1.0.0 |\n| [CF.LOADCHUNK](https://redis.io/commands/cf.loadchunk/) | Restores a filter previously saved using SCANDUMP | O(n), where n is the capacity | 1.0.0 |\n\n#### Cuckoo filter command summary (part 2)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [CF.MEXISTS](https://redis.io/commands/cf.mexists/) | Checks whether one or more items exist in a Cuckoo Filter | O(k * n), where k is the number of sub-filters ... | 1.0.0 |\n| [CF.RESERVE](https://redis.io/commands/cf.reserve/) | Creates a new Cuckoo Filter | O(1) | 1.0.0 |\n| [CF.SCANDUMP](https://redis.io/commands/cf.scandump/) | Begins an incremental save of the bloom filter | O(n), where n is the capacity | 1.0.0 |\n\n\n\nA Cuckoo filter, just like a Bloom filter, is a probabilistic data structure in Redis Open Source that enables you to check if an element is present in a set in a very fast and space efficient way, while also allowing for deletions and showing better performance than Bloom in some scenarios.\n\nWhile the Bloom filter is a bit array with flipped bits at positions decided by the hash function, a Cuckoo filter is an array of buckets, storing fingerprints of the values in one of the buckets at positions decided by the two hash functions. A membership query for item `x` searches the possible buckets for the fingerprint of `x`, and returns true if an identical fingerprint is found. A cuckoo filter's fingerprint size will directly determine the false positive rate."},{"id":"use-cases","title":"Use cases","role":"content","text":"**Targeted ad campaigns (advertising, retail)** \n\nThis application answers this question: Has the user signed up for this campaign yet?\n\nUse a Cuckoo filter for every campaign, populated with targeted users' ids. On every visit, the user id is checked against one of the Cuckoo filters. \n\n- If yes, the user has not signed up for campaign. Show the ad.\n- If the user clicks ad and signs up, remove the user id from that Cuckoo filter. \n- If no, the user has signed up for that campaign. Try the next ad/Cuckoo filter. \n \n**Discount code/coupon validation (retail, online shops)** \n\nThis application answers this question: Has this discount code/coupon been used yet?\n\nUse a Cuckoo filter populated with all discount codes/coupons. On every try, the entered code is checked against the filter. \n\n- If no, the coupon is not valid. \n- If yes, the coupon can be valid. Check the main database. If valid, remove from Cuckoo filter as `used`.\n\nNote> In addition to these two cases, Cuckoo filters serve very well all the Bloom filter use cases."},{"id":"examples","title":"Examples","role":"example","text":"> You'll learn how to create an empty cuckoo filter with an initial capacity for 1,000 items, add items, check their existence, and remove them. Even though the [`CF.ADD`](https://redis.io/docs/latest/commands/cf.add) command can create a new filter if one isn't present, it might not be optimally sized for your needs. It's better to use the [`CF.RESERVE`](https://redis.io/docs/latest/commands/cf.reserve) command to set up a filter with your preferred capacity.\n\nCuckoo filter operations: Use CF.RESERVE to create a filter, CF.ADD to add items, CF.EXISTS to check membership, and CF.DEL to remove items when you need space-efficient probabilistic set membership testing with deletion support\n\n**Difficulty:** Intermediate\n\n**Commands:** CF.RESERVE, CF.ADD, CF.EXISTS, CF.DEL\n\n**Complexity:**\n- CF.RESERVE: O(1)\n- CF.ADD: O(k + i)\n- CF.EXISTS: O(k)\n- CF.DEL: O(k)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]"},{"id":"bloom-vs-cuckoo-filters","title":"Bloom vs. Cuckoo filters","role":"content","text":"Bloom filters typically exhibit better performance and scalability when inserting\nitems (so if you're often adding items to your dataset, then a Bloom filter may be ideal).\nCuckoo filters are quicker on check operations and also allow deletions."},{"id":"sizing-cuckoo-filters","title":"Sizing Cuckoo filters","role":"content","text":"These are the main parameters and features of a cuckoo filter:\n\n- `p` target false positive rate  \n- `f` fingerprint length in bits\n- `α` fill rate or load factor (0≤α≤1)\n- `b` number of entries per bucket\n- `m` number of buckets\n- `n` number of items\n- `C` average bits per item\n\nLet's start by remembering that a cuckoo filter bucket can have multiple entries (where each entry stores one fingerprint). If we end up having all entries occupied with a fingerprint then we won't have empty slots to save new elements and the filter will be declared full, that's why we should always maintain a certain percentage of our cuckoo filter free.  \nAs a result of this the \"real\" memory cost of an item should include that overhead in addition to the fingerprint size. If `α` is the load factor (fingerprint size / total filter size) and `f` is the number of bits in an entry the amortised space cost `f/α bits`.\n\nWhen you initialise a new filter you are asked to choose its capacity and bucket size. \n\n[code example]"},{"id":"choosing-the-capacity--capacity","title":"Choosing the capacity  (`capacity`)","role":"content","text":"The capacity of a Cuckoo filter is calculated as\n\n[code example]\nwhere `n` is the number of elements you expect to have in your filter, `f` is the fingerprint length in bits which is set to `8` and `α` is the fill factor. So in order to get your filter capacity you must first choose a fill factor. The fill factor will determine the density of your data and of course the memory. \nThe capacity will be rounded up to the next \"power of two (2<sup>n</sup>)\" number.\n\n> Please note that inserting repeated items in a cuckoo filter will try to add them multiple times causing your filter to fill up\n\nBecause of how Cuckoo Filters work, the filter is likely to declare itself full before capacity is reached and therefore fill rate will likely never reach 100%."},{"id":"choosing-the-bucket-size-bucketsize","title":"Choosing the bucket size (`BUCKETSIZE`)","role":"content","text":"Number of items in each bucket. A higher bucket size value improves the fill rate but also causes a higher error rate and slightly slower performance.\n\n[code example]\n\nWhen bucket size of 1 is used the fill rate is 55% and false positive error rate is 2/256 ≈ 0.78% **which is the minimal false positive rate you can achieve**. Larger buckets increase the error rate linearly but improve the fill rate of the filter. For example, a bucket size of 3 yields a 2.34% error rate and an 80% fill rate. Bucket size of 4 yields a 3.12% error rate and a 95% fill rate."},{"id":"choosing-the-scaling-factor-expansion","title":"Choosing the scaling factor (`EXPANSION`)","role":"content","text":"When the filter self-declares itself full, it will auto-expand by generating additional sub-filters at the cost of reduced performance and increased error rate. The new sub-filter is created with size of the previous sub-filter multiplied by `EXPANSION` (chosen on filter creation). Like bucket size, additional sub-filters grow the error rate linearly (the compound error is a sum of all subfilters' errors). The size of the new sub-filter is the size of the last sub-filter multiplied by expansion and this is something very important to keep in mind. If you know you'll have to scale at some point it's better to choose a higher expansion value. The default is [`cf-expansion-factor`](https://redis.io/docs/latest/develop/data-types/probabilistic/configuration#cf-expansion-factor).\n\nMaybe you're wondering \"Why would I create a smaller filter with a high expansion rate if I know I'm going to scale anyway?\"; the answer is: for cases where you need to keep many filters (let's say a filter per user, or per product) and most of them will stay small, but some with more activity will have to scale. \n\nThe expansion factor will be rounded up to the next \"power of two (2<sup>n</sup>)\" number."},{"id":"choosing-the-maximum-number-of-iterations-maxiterations","title":"Choosing the maximum number of iterations (`MAXITERATIONS`)","role":"content","text":"`MAXITERATIONS` dictates the number of attempts to find a slot for the incoming fingerprint. Once the filter gets full, a high MAXITERATIONS value will slow down insertions. The default value is [`cf-max-iterations`](https://redis.io/docs/latest/develop/data-types/probabilistic/configuration#cf-max-iterations)."},{"id":"interesting-facts","title":"Interesting facts:","role":"content","text":"- Unused capacity in prior sub-filters is automatically used when possible. \n- The filter can grow up to [`cf-max-expansions`](https://redis.io/docs/latest/develop/data-types/probabilistic/configuration#cf-max-expansions) times.\n- You can delete items to stay within filter limits instead of rebuilding\n- Adding the same element multiple times will create multiple entries, thus filling up your filter."},{"id":"performance","title":"Performance","role":"performance","text":"Adding an element to a Cuckoo filter has a time complexity of O(1).\n\nSimilarly, checking for an element and deleting an element also has a time complexity of O(1)."},{"id":"academic-sources","title":"Academic sources","role":"content","text":"- [Cuckoo Filter: Practically Better Than Bloom](https://www.cs.cmu.edu/~dga/papers/cuckoo-conext2014.pdf)"}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> CF.RESERVE bikes:models 1000\nOK\n> CF.ADD bikes:models \"Smoky Mountain Striker\"\n(integer) 1\n> CF.EXISTS bikes:models \"Smoky Mountain Striker\"\n(integer) 1\n> CF.EXISTS bikes:models \"Terrible Bike Name\"\n(integer) 0\n> CF.DEL bikes:models \"Smoky Mountain Striker\"\n(integer) 1","section_id":"examples"},{"id":"examples-ex1","language":"csharp","code":"bool res1 = db.CF().Reserve(\"bikes:models\", 1000000);\n        Console.WriteLine(res1);    // >>> True\n\n        bool res2 = db.CF().Add(\"bikes:models\", \"Smoky Mountain Striker\");\n        Console.WriteLine(res2);    // >>> True\n\n        bool res3 = db.CF().Exists(\"bikes:models\", \"Smoky Mountain Striker\");\n        Console.WriteLine(res3);    // >>> True\n\n        bool res4 = db.CF().Exists(\"bikes:models\", \"Terrible Bike Name\");\n        Console.WriteLine(res4);    // >>> False\n\n        bool res5 = db.CF().Del(\"bikes:models\", \"Smoky Mountain Striker\");\n        Console.WriteLine(res5);    // >>> True","section_id":"examples"},{"id":"examples-ex2","language":"go","code":"res1, err := rdb.CFReserve(ctx, \"bikes:models\", 1000000).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> OK\n\n\tres2, err := rdb.CFAdd(ctx, \"bikes:models\", \"Smoky Mountain Striker\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> true\n\n\tres3, err := rdb.CFExists(ctx, \"bikes:models\", \"Smoky Mountain Striker\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3) // >>> true\n\n\tres4, err := rdb.CFExists(ctx, \"bikes:models\", \"Terrible Bike Name\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4) // >>> false\n\n\tres5, err := rdb.CFDel(ctx, \"bikes:models\", \"Smoky Mountain Striker\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res5) // >>> true","section_id":"examples"},{"id":"examples-ex3","language":"java","code":"CompletableFuture<Void> cuckooExample = asyncCommands\n                    .cfReserve(\"bikes:models\", 1000000L)\n                    .thenCompose(res1 -> {\n                        System.out.println(res1);\n                        // >>> OK\n                        return asyncCommands.cfAdd(\"bikes:models\", \"Smoky Mountain Striker\");\n                    })\n                    .thenCompose(res2 -> {\n                        System.out.println(res2);\n                        // >>> true\n                        return asyncCommands.cfExists(\"bikes:models\", \"Smoky Mountain Striker\");\n                    })\n                    .thenCompose(res3 -> {\n                        System.out.println(res3);\n                        // >>> true\n                        return asyncCommands.cfExists(\"bikes:models\", \"Terrible Bike Name\");\n                    })\n                    .thenCompose(res4 -> {\n                        System.out.println(res4);\n                        // >>> false\n                        return asyncCommands.cfDel(\"bikes:models\", \"Smoky Mountain Striker\");\n                    })\n                    .thenAccept(res5 -> {\n                        System.out.println(res5);\n                        // >>> true\n                    })\n                    .toCompletableFuture();\n\n            cuckooExample.join();","section_id":"examples"},{"id":"examples-ex4","language":"java","code":"Mono<Void> cuckooExample = reactiveCommands\n                    .cfReserve(\"bikes:models\", 1000000L)\n                    .doOnNext(res1 -> {\n                        System.out.println(res1);\n                        // >>> OK\n                    })\n                    .then(reactiveCommands.cfAdd(\"bikes:models\", \"Smoky Mountain Striker\"))\n                    .doOnNext(res2 -> {\n                        System.out.println(res2);\n                        // >>> true\n                    })\n                    .then(reactiveCommands.cfExists(\"bikes:models\", \"Smoky Mountain Striker\"))\n                    .doOnNext(res3 -> {\n                        System.out.println(res3);\n                        // >>> true\n                    })\n                    .then(reactiveCommands.cfExists(\"bikes:models\", \"Terrible Bike Name\"))\n                    .doOnNext(res4 -> {\n                        System.out.println(res4);\n                        // >>> false\n                    })\n                    .then(reactiveCommands.cfDel(\"bikes:models\", \"Smoky Mountain Striker\"))\n                    .doOnNext(res5 -> {\n                        System.out.println(res5);\n                        // >>> true\n                    })\n                    .then();\n\n            cuckooExample.block();","section_id":"examples"},{"id":"examples-ex5","language":"java","code":"String res1 = jedis.cfReserve(\"bikes:models\", 1000000);\n        System.out.println(res1); // >>> OK\n\n\n        boolean res2 = jedis.cfAdd(\"bikes:models\", \"Smoky Mountain Striker\");\n        System.out.println(res2); // >>> True\n\n        boolean res3 = jedis.cfExists(\"bikes:models\", \"Smoky Mountain Striker\");\n        System.out.println(res3); // >>> True\n\n        boolean res4 = jedis.cfExists(\"bikes:models\", \"Terrible Bike Name\");\n        System.out.println(res4); // >>> False\n\n        boolean res5 = jedis.cfDel(\"bikes:models\", \"Smoky Mountain Striker\");\n        System.out.println(res5); // >>> True","section_id":"examples"},{"id":"examples-ex6","language":"javascript","code":"const res1 = await client.cf.reserve('bikes:models', 1000000);\nconsole.log(res1);  // >>> OK\n\nconst res2 = await client.cf.add('bikes:models', 'Smoky Mountain Striker');\nconsole.log(res2);  // >>> true\n\nconst res3 = await client.cf.exists('bikes:models', 'Smoky Mountain Striker');\nconsole.log(res3);  // >>> true\n\nconst res4 = await client.cf.exists('bikes:models', 'Terrible Bike Name');\nconsole.log(res4);  // >>> false\n\nconst res5 = await client.cf.del('bikes:models', 'Smoky Mountain Striker');\nconsole.log(res5);  // >>> true","section_id":"examples"},{"id":"examples-ex7","language":"php","code":"$res1 = $r->cfreserve('bikes:models', 1000000);\n        echo $res1 . PHP_EOL;\n        // >>> OK\n\n        $res2 = $r->cfadd('bikes:models', 'Smoky Mountain Striker');\n        echo $res2 . PHP_EOL;\n        // >>> 1\n\n        $res3 = $r->cfexists('bikes:models', 'Smoky Mountain Striker');\n        echo $res3 . PHP_EOL;\n        // >>> 1\n\n        $res4 = $r->cfexists('bikes:models', 'Terrible Bike Name');\n        echo $res4 . PHP_EOL;\n        // >>> 0\n\n        $res5 = $r->cfdel('bikes:models', 'Smoky Mountain Striker');\n        echo $res5 . PHP_EOL;\n        // >>> 1","section_id":"examples"},{"id":"examples-ex8","language":"python","code":"res1 = r.cf().reserve(\"bikes:models\", 1000000)\nprint(res1)  # >>> True\n\nres2 = r.cf().add(\"bikes:models\", \"Smoky Mountain Striker\")\nprint(res2)  # >>> 1\n\nres3 = r.cf().exists(\"bikes:models\", \"Smoky Mountain Striker\")\nprint(res3)  # >>> 1\n\nres4 = r.cf().exists(\"bikes:models\", \"Terrible Bike Name\")\nprint(res4)  # >>> 0\n\nres5 = r.cf().delete(\"bikes:models\", \"Smoky Mountain Striker\")\nprint(res5)  # >>> 1","section_id":"examples"},{"id":"sizing-cuckoo-filters-ex0","language":"plaintext","code":"CF.RESERVE {key} {capacity} [BUCKETSIZE bucketSize] [MAXITERATIONS maxIterations]\n[EXPANSION expansion]","section_id":"sizing-cuckoo-filters"},{"id":"choosing-the-capacity--capacity-ex0","language":"plaintext","code":"capacity = n*f/α","section_id":"choosing-the-capacity--capacity"},{"id":"choosing-the-bucket-size-bucketsize-ex0","language":"plaintext","code":"error_rate = (buckets * hash_functions)/2^fingerprint_size = (buckets*2)/256","section_id":"choosing-the-bucket-size-bucketsize"}]}
{"schema_version":2,"id":"develop/data-types/probabilistic/hyperloglogs","title":"HyperLogLog","url":"https://redis.io/docs/latest/develop/data-types/probabilistic/hyperloglogs/","summary":"HyperLogLog is a probabilistic data structure that estimates the cardinality of a set.","aliases":["/data-types/probabilistic/hyperloglogs/","/manual/data-types/probabilistic/hyperloglogs/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-10T15:45:40+01:00","page_type":"content","content_hash":"f77ec4729ca09623d7b09454417efdd17b54a5fe2ff852176cffa49e27928571","sections":[{"id":"hyperloglog-command-summary","title":"HyperLogLog command summary","role":"content","text":"**5 commands in this group:**\n\n[View all hyperloglog commands](https://redis.io/commands/?group=hyperloglog)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [PFADD](https://redis.io/commands/pfadd/) | Adds elements to a HyperLogLog key. Creates the key if it doesn't exist. | O(1) to add every element. | 2.8.9 |\n| [PFCOUNT](https://redis.io/commands/pfcount/) | Returns the approximated cardinality of the set(s) observed by the HyperLogLog key(s). | O(1) with a very small average constant time wh... | 2.8.9 |\n| [PFDEBUG](https://redis.io/commands/pfdebug/) | Internal commands for debugging HyperLogLog values. | N/A | 2.8.9 |\n| [PFMERGE](https://redis.io/commands/pfmerge/) | Merges one or more HyperLogLog values into a single key. | O(N) to merge N HyperLogLogs, but with high con... | 2.8.9 |\n| [PFSELFTEST](https://redis.io/commands/pfselftest/) | An internal command for testing HyperLogLog values. | N/A | 2.8.9 |\n\n\n\nHyperLogLog is a probabilistic data structure that estimates the cardinality of a set, trading perfect accuracy for efficient space utilization. The Redis implementation uses up to 12 KB of memory and provides a standard error rate of 0.81%.\n\nCounting unique items usually requires an amount of memory\nproportional to the number of items you want to count, because you need\nto remember the elements you have already seen in the past in order to avoid\ncounting them multiple times. However, a set of algorithms exist that trade \nmemory for precision: they return an estimated measure with a standard error, \nwhich, in the case of the Redis implementation for HyperLogLog, is less than 1%.\nThe magic of this algorithm is that you no longer need to use an amount of memory\nproportional to the number of items counted, and instead can use a\nconstant amount of memory; 12k bytes in the worst case, or a lot less if your\nHyperLogLog (We'll just call them HLL from now) has seen very few elements.\n\nHLLs in Redis, while technically a different data structure, are encoded\nas a Redis string, so you can call [`GET`](https://redis.io/docs/latest/commands/get) to serialize a HLL, and [`SET`](https://redis.io/docs/latest/commands/set)\nto deserialize it back to the server.\n\nConceptually the HLL API is like using Sets to do the same task. You would\n[`SADD`](https://redis.io/docs/latest/commands/sadd) every observed element into a set, and would use [`SCARD`](https://redis.io/docs/latest/commands/scard) to check the\nnumber of elements inside the set, which are unique since [`SADD`](https://redis.io/docs/latest/commands/sadd) will not\nre-add an existing element.\n\nWhile you don't really *add items* into an HLL, because the data structure\nonly contains a state that does not include actual elements, the API is the\nsame:\n\n* Every time you see a new element, you add it to the count with [`PFADD`](https://redis.io/docs/latest/commands/pfadd).\n* When you want to retrieve the current approximation of unique elements added using the [`PFADD`](https://redis.io/docs/latest/commands/pfadd) command, you can use the [`PFCOUNT`](https://redis.io/docs/latest/commands/pfcount) command. If you need to merge two different HLLs, the [`PFMERGE`](https://redis.io/docs/latest/commands/pfmerge) command is available. Since HLLs provide approximate counts of unique elements, the result of the merge will give you an approximation of the number of unique elements across both source HLLs.\n\nHyperLogLog operations: Use PFADD to add items to a HyperLogLog, PFCOUNT to estimate cardinality, and PFMERGE to combine HyperLogLogs when you need space-efficient cardinality estimation\n\n**Difficulty:** Intermediate\n\n**Commands:** PFADD, PFCOUNT, PFMERGE\n\n**Complexity:**\n- PFADD: O(1)\n- PFCOUNT: O(1)\n- PFMERGE: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nSome examples of use cases for this data structure is counting unique queries\nperformed by users in a search form every day, number of unique visitors to a web page and other similar cases.\n\nRedis is also able to perform the union of HLLs, please check the\n[full documentation](/commands?group=hyperloglog) for more information."},{"id":"use-cases","title":"Use cases","role":"content","text":"**Anonymous unique visits of a web page (SaaS, analytics tools)** \n\nThis application answers these questions: \n\n- How many unique visits has this page had on this day? \n- How many unique users have played this song? \n- How many unique users have viewed this video? \n\n> [!NOTE]\n>  \n> Storing the IP address or any other kind of personal identifier is against the law in some countries, which makes it impossible to get unique visitor statistics on your website.\n\nOne HyperLogLog is created per page (video/song) per period, and every IP/identifier is added to it on every visit."},{"id":"performance","title":"Performance","role":"performance","text":"Writing ([`PFADD`](https://redis.io/docs/latest/commands/pfadd)) to and reading from ([`PFCOUNT`](https://redis.io/docs/latest/commands/pfcount)) the HyperLogLog is done in constant time and space.\nMerging HLLs is O(n), where _n_ is the number of sketches."},{"id":"limits","title":"Limits","role":"limits","text":"The HyperLogLog can estimate the cardinality of sets with up to 18,446,744,073,709,551,616 (2^64) members."},{"id":"learn-more","title":"Learn more","role":"related","text":"* [Redis new data structure: the HyperLogLog](http://antirez.com/news/75) has a lot of details about the data structure and its implementation in Redis.\n* [Redis HyperLogLog Explained](https://www.youtube.com/watch?v=MunL8nnwscQ) shows you how to use Redis HyperLogLog data structures to build a traffic heat map."}],"examples":[{"id":"hyperloglog-command-summary-ex0","language":"plaintext","code":"> PFADD bikes Hyperion Deimos Phoebe Quaoar\n(integer) 1\n> PFCOUNT bikes\n(integer) 4\n> PFADD commuter_bikes Salacia Mimas Quaoar\n(integer) 1\n> PFMERGE all_bikes bikes commuter_bikes\nOK\n> PFCOUNT all_bikes\n(integer) 6","section_id":"hyperloglog-command-summary"},{"id":"hyperloglog-command-summary-ex1","language":"csharp","code":"bool res1 = db.HyperLogLogAdd(\"{bikes}\", [\"Hyperion\", \"Deimos\", \"Phoebe\", \"Quaoar\"]);\n        Console.WriteLine(res1);    // >>> True\n\n        long res2 = db.HyperLogLogLength(\"{bikes}\");\n        Console.WriteLine(res2);    // >>> 4\n\n        bool res3 = db.HyperLogLogAdd(\"commuter_{bikes}\", [\"Salacia\", \"Mimas\", \"Quaoar\"]);\n        Console.WriteLine(res3);    // >>> True\n\n        db.HyperLogLogMerge(\"all_{bikes}\", \"{bikes}\", \"commuter_{bikes}\");\n        long res4 = db.HyperLogLogLength(\"all_{bikes}\");\n        Console.WriteLine(res4);    // >>> 6","section_id":"hyperloglog-command-summary"},{"id":"hyperloglog-command-summary-ex2","language":"go","code":"res1, err := rdb.PFAdd(ctx, \"bikes\", \"Hyperion\", \"Deimos\", \"Phoebe\", \"Quaoar\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // 1\n\n\tres2, err := rdb.PFCount(ctx, \"bikes\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // 4\n\n\tres3, err := rdb.PFAdd(ctx, \"commuter_bikes\", \"Salacia\", \"Mimas\", \"Quaoar\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3) // 1\n\n\tres4, err := rdb.PFMerge(ctx, \"all_bikes\", \"bikes\", \"commuter_bikes\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4) // OK\n\n\tres5, err := rdb.PFCount(ctx, \"all_bikes\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res5) // 6","section_id":"hyperloglog-command-summary"},{"id":"hyperloglog-command-summary-ex3","language":"java","code":"CompletableFuture<Void> pfaddExample = asyncCommands\n                    .pfadd(\"bikes\", \"Hyperion\", \"Deimos\", \"Phoebe\", \"Quaoar\")\n                    .thenCompose(res1 -> {\n                        System.out.println(res1);\n                        // >>> 1\n                        return asyncCommands.pfcount(\"bikes\");\n                    })\n                    .thenCompose(res2 -> {\n                        System.out.println(res2);\n                        // >>> 4\n                        return asyncCommands.pfadd(\"commuter_bikes\", \"Salacia\", \"Mimas\", \"Quaoar\");\n                    })\n                    .thenCompose(res3 -> {\n                        System.out.println(res3);\n                        // >>> 1\n                        return asyncCommands.pfmerge(\"all_bikes\", \"bikes\", \"commuter_bikes\");\n                    })\n                    .thenCompose(res4 -> {\n                        System.out.println(res4);\n                        // >>> OK\n                        return asyncCommands.pfcount(\"all_bikes\");\n                    })\n                    .thenAccept(res5 -> {\n                        System.out.println(res5);\n                        // >>> 6\n                    })\n                    .toCompletableFuture();\n\n            pfaddExample.join();","section_id":"hyperloglog-command-summary"},{"id":"hyperloglog-command-summary-ex4","language":"java","code":"Mono<Void> pfaddExample = reactiveCommands\n                    .pfadd(\"bikes\", \"Hyperion\", \"Deimos\", \"Phoebe\", \"Quaoar\")\n                    .doOnNext(res1 -> {\n                        System.out.println(res1);\n                        // >>> 1\n                    })\n                    .then(reactiveCommands.pfcount(\"bikes\"))\n                    .doOnNext(res2 -> {\n                        System.out.println(res2);\n                        // >>> 4\n                    })\n                    .then(reactiveCommands.pfadd(\"commuter_bikes\", \"Salacia\", \"Mimas\", \"Quaoar\"))\n                    .doOnNext(res3 -> {\n                        System.out.println(res3);\n                        // >>> 1\n                    })\n                    .then(reactiveCommands.pfmerge(\"all_bikes\", \"bikes\", \"commuter_bikes\"))\n                    .doOnNext(res4 -> {\n                        System.out.println(res4);\n                        // >>> OK\n                    })\n                    .then(reactiveCommands.pfcount(\"all_bikes\"))\n                    .doOnNext(res5 -> {\n                        System.out.println(res5);\n                        // >>> 6\n                    })\n                    .then();\n\n            pfaddExample.block();","section_id":"hyperloglog-command-summary"},{"id":"hyperloglog-command-summary-ex5","language":"java","code":"long res1 = jedis.pfadd(\"bikes\", \"Hyperion\", \"Deimos\", \"Phoebe\", \"Quaoar\");\n        System.out.println(res1); // >>> 1\n\n        long res2 = jedis.pfcount(\"bikes\");\n        System.out.println(res2); // >>> 4\n\n        long res3 = jedis.pfadd(\"commuter_bikes\", \"Salacia\", \"Mimas\", \"Quaoar\");\n        System.out.println(res3); // >>> 1\n\n        String res4 = jedis.pfmerge(\"all_bikes\", \"bikes\", \"commuter_bikes\");\n        System.out.println(res4); // >>> OK\n\n\n        long res5 = jedis.pfcount(\"all_bikes\");\n        System.out.println(res5); // >>> 6","section_id":"hyperloglog-command-summary"},{"id":"hyperloglog-command-summary-ex6","language":"javascript","code":"const res1 = await client.pfAdd('bikes', ['Hyperion', 'Deimos', 'Phoebe', 'Quaoar']);\nconsole.log(res1);  // >>> 1\n\nconst res2 = await client.pfCount('bikes');\nconsole.log(res2);  // >>> 4\n\nconst res3 = await client.pfAdd('commuter_bikes', ['Salacia', 'Mimas', 'Quaoar']);\nconsole.log(res3);  // >>> 1\n\nconst res4 = await client.pfMerge('all_bikes', ['bikes', 'commuter_bikes']);\nconsole.log(res4);  // >>> OK\n\nconst res5 = await client.pfCount('all_bikes');\nconsole.log(res5);  // >>> 6","section_id":"hyperloglog-command-summary"},{"id":"hyperloglog-command-summary-ex7","language":"php","code":"$res1 = $r->pfadd('bikes', ['Hyperion', 'Deimos', 'Phoebe', 'Quaoar']);\n        echo $res1 . PHP_EOL;\n        // >>> 1\n\n        $res2 = $r->pfcount('bikes');\n        echo $res2 . PHP_EOL;\n        // >>> 4\n\n        $res3 = $r->pfadd('commuter_bikes', ['Salacia', 'Mimas', 'Quaoar']);\n        echo $res3 . PHP_EOL;\n        // >>> 1\n\n        $res4 = $r->pfmerge('all_bikes', 'bikes', 'commuter_bikes');\n        echo $res4 . PHP_EOL;\n        // >>> OK\n\n        $res5 = $r->pfcount('all_bikes');\n        echo $res5 . PHP_EOL;\n        // >>> 6","section_id":"hyperloglog-command-summary"},{"id":"hyperloglog-command-summary-ex8","language":"python","code":"res1 = r.pfadd(\"bikes\", \"Hyperion\", \"Deimos\", \"Phoebe\", \"Quaoar\")\nprint(res1)  # >>> 1\n\nres2 = r.pfcount(\"bikes\")\nprint(res2)  # >>> 4\n\nres3 = r.pfadd(\"commuter_bikes\", \"Salacia\", \"Mimas\", \"Quaoar\")\nprint(res3)  # >>> 1\n\nres4 = r.pfmerge(\"all_bikes\", \"bikes\", \"commuter_bikes\")\nprint(res4)  # >>> True\n\nres5 = r.pfcount(\"all_bikes\")\nprint(res5)  # >>> 6","section_id":"hyperloglog-command-summary"},{"id":"hyperloglog-command-summary-ex9","language":"ruby","code":"res1 = r.pfadd('bikes', ['Hyperion', 'Deimos', 'Phoebe', 'Quaoar'])\nputs res1 # >>> true\n\nres2 = r.pfcount('bikes')\nputs res2 # >>> 4\n\nres3 = r.pfadd('commuter_bikes', ['Salacia', 'Mimas', 'Quaoar'])\nputs res3 # >>> true\n\nres4 = r.pfmerge('all_bikes', 'bikes', 'commuter_bikes')\nputs res4 # >>> true\n\nres5 = r.pfcount('all_bikes')\nputs res5 # >>> 6","section_id":"hyperloglog-command-summary"},{"id":"hyperloglog-command-summary-ex10","language":"rust","code":"let res1: bool = r\n            .pfadd(\"bikes\", &[\"Hyperion\", \"Deimos\", \"Phoebe\", \"Quaoar\"])\n            .await\n            .expect(\"Failed to add bikes\");\n        println!(\"{res1}\"); // >>> true\n\n        let res2: usize = r.pfcount(\"bikes\").await.expect(\"Failed to count bikes\");\n        println!(\"{res2}\"); // >>> 4\n\n        let res3: bool = r\n            .pfadd(\"commuter_bikes\", &[\"Salacia\", \"Mimas\", \"Quaoar\"])\n            .await\n            .expect(\"Failed to add commuter bikes\");\n        println!(\"{res3}\"); // >>> true\n\n        let _: () = r\n            .pfmerge(\"all_bikes\", &[\"bikes\", \"commuter_bikes\"])\n            .await\n            .expect(\"Failed to merge HyperLogLogs\");\n        println!(\"OK\"); // >>> OK\n\n        let res5: usize = r\n            .pfcount(\"all_bikes\")\n            .await\n            .expect(\"Failed to count all bikes\");\n        println!(\"{res5}\"); // >>> 6","section_id":"hyperloglog-command-summary"},{"id":"hyperloglog-command-summary-ex11","language":"rust","code":"let res1: bool = r\n            .pfadd(\"bikes\", &[\"Hyperion\", \"Deimos\", \"Phoebe\", \"Quaoar\"])\n            .expect(\"Failed to add bikes\");\n        println!(\"{res1}\"); // >>> true\n\n        let res2: usize = r.pfcount(\"bikes\").expect(\"Failed to count bikes\");\n        println!(\"{res2}\"); // >>> 4\n\n        let res3: bool = r\n            .pfadd(\"commuter_bikes\", &[\"Salacia\", \"Mimas\", \"Quaoar\"])\n            .expect(\"Failed to add commuter bikes\");\n        println!(\"{res3}\"); // >>> true\n\n        let _: () = r\n            .pfmerge(\"all_bikes\", &[\"bikes\", \"commuter_bikes\"])\n            .expect(\"Failed to merge HyperLogLogs\");\n        println!(\"OK\"); // >>> OK\n\n        let res5: usize = r.pfcount(\"all_bikes\").expect(\"Failed to count all bikes\");\n        println!(\"{res5}\"); // >>> 6","section_id":"hyperloglog-command-summary"}]}
{"schema_version":2,"id":"develop/data-types/probabilistic","title":"Probabilistic","url":"https://redis.io/docs/latest/develop/data-types/probabilistic/","summary":"Probabilistic data structures in Redis","aliases":["/data-types/probabilistic/","/manual/data-types/probabilistic/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-10T15:45:40+01:00","children":[{"id":"develop/data-types/probabilistic/bloom-filter","summary":"Bloom filters are a probabilistic data structure that checks for presence of an item in a set","title":"Bloom filter","url":"https://redis.io/docs/latest/develop/data-types/probabilistic/bloom-filter/"},{"id":"develop/data-types/probabilistic/count-min-sketch","summary":"Count-min sketch is a probabilistic data structure that estimates the frequency of an element in a data stream.","title":"Count-min sketch","url":"https://redis.io/docs/latest/develop/data-types/probabilistic/count-min-sketch/"},{"id":"develop/data-types/probabilistic/cuckoo-filter","summary":"Cuckoo filters are a probabilistic data structure that checks for presence of an element in a set","title":"Cuckoo filter","url":"https://redis.io/docs/latest/develop/data-types/probabilistic/cuckoo-filter/"},{"id":"develop/data-types/probabilistic/hyperloglogs","summary":"HyperLogLog is a probabilistic data structure that estimates the cardinality of a set.","title":"HyperLogLog","url":"https://redis.io/docs/latest/develop/data-types/probabilistic/hyperloglogs/"},{"id":"develop/data-types/probabilistic/t-digest","summary":"t-digest is a probabilistic data structure that allows you to estimate the percentile of a data stream.","title":"t-digest","url":"https://redis.io/docs/latest/develop/data-types/probabilistic/t-digest/"},{"id":"develop/data-types/probabilistic/top-k","summary":"Top-K is a probabilistic data structure that allows you to find the most frequent items in a data stream.","title":"Top-K","url":"https://redis.io/docs/latest/develop/data-types/probabilistic/top-k/"},{"id":"develop/data-types/probabilistic/configuration","summary":"Redis probabilistic data structures support multiple configuration parameters.","title":"Configuration Parameters","url":"https://redis.io/docs/latest/develop/data-types/probabilistic/configuration/"}],"page_type":"content","content_hash":"99a8de5670545fdcd746774e3fcae1179dfe5bc875694b5916a80693680e8a22","sections":[{"id":"content","title":"Content","role":"content","text":"*Probabilistic data structures* give approximations of statistics such as\ncounts, frequencies, and rankings rather than precise values.\nThe advantage of using approximations is that they are adequate for\nmany common purposes but are much more efficient to calculate. They\nsometimes have other advantages too, such as obfuscating times, locations,\nand other sensitive data.\n\nProbabilistic data structures are available as part of Redis Open Source and they are available in Redis Software and Redis Cloud.\nSee\n[Install Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack) or\n[Install Redis Software](https://redis.io/docs/latest/operate/rs/installing-upgrading/install)\nfor full installation instructions."}],"examples":[]}
{"schema_version":2,"id":"develop/data-types/probabilistic/t-digest","title":"t-digest","url":"https://redis.io/docs/latest/develop/data-types/probabilistic/t-digest/","summary":"t-digest is a probabilistic data structure that allows you to estimate the percentile of a data stream.","aliases":["/data-types/probabilistic/t-digest/","/manual/data-types/probabilistic/t-digest/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-10T15:45:40+01:00","page_type":"content","content_hash":"223d71baa0f72c78c66b0b321195ff47e0033968025d9ba75379b906b15af72d","sections":[{"id":"t-digest-command-summary","title":"t-digest command summary","role":"content","text":"**14 commands in this group:**\n\n[View all tdigest commands](https://redis.io/commands/?group=tdigest)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [TDIGEST.ADD](https://redis.io/commands/tdigest.add/) | Adds one or more observations to a t-digest sketch | O(N), where N is the number of samples to add | 2.4.0 |\n| [TDIGEST.BYRANK](https://redis.io/commands/tdigest.byrank/) | Returns, for each input rank, an estimation of the value (floating-point) with that rank | O(N) where N is the number of ranks specified | 2.4.0 |\n| [TDIGEST.BYREVRANK](https://redis.io/commands/tdigest.byrevrank/) | Returns, for each input reverse rank, an estimation of the value (floating-point) with that reverse rank | O(N) where N is the number of reverse ranks spe... | 2.4.0 |\n| [TDIGEST.CDF](https://redis.io/commands/tdigest.cdf/) | Returns, for each input value, an estimation of the floating-point fraction of (observations smaller than the given value + half the observations equal to the given value). Multiple fractions can be retrieved in a single call. | O(N) where N is the number of values specified. | 2.4.0 |\n| [TDIGEST.CREATE](https://redis.io/commands/tdigest.create/) | Allocates memory and initializes a new t-digest sketch | O(1) | 2.4.0 |\n| [TDIGEST.INFO](https://redis.io/commands/tdigest.info/) | Returns information and statistics about a t-digest sketch | O(1) | 2.4.0 |\n| [TDIGEST.MAX](https://redis.io/commands/tdigest.max/) | Returns the maximum observation value from a t-digest sketch | O(1) | 2.4.0 |\n| [TDIGEST.MERGE](https://redis.io/commands/tdigest.merge/) | Merges multiple t-digest sketches into a single sketch | O(N*K), where N is the number of centroids and ... | 2.4.0 |\n| [TDIGEST.MIN](https://redis.io/commands/tdigest.min/) | Returns the minimum observation value from a t-digest sketch | O(1) | 2.4.0 |\n| [TDIGEST.QUANTILE](https://redis.io/commands/tdigest.quantile/) | Returns, for each input fraction, an estimation of the value (floating point) that is smaller than the given fraction of observations | O(N) where N is the number of quantiles specified. | 2.4.0 |\n\n#### t-digest command summary (part 2)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [TDIGEST.RANK](https://redis.io/commands/tdigest.rank/) | Returns, for each input value (floating-point), the estimated rank of the value (the number of observations in the sketch that are smaller than the value + half the number of observations that are equal to the value) | O(N) where N is the number of values specified. | 2.4.0 |\n| [TDIGEST.RESET](https://redis.io/commands/tdigest.reset/) | Resets a t-digest sketch: empty the sketch and re-initializes it. | O(1) | 2.4.0 |\n| [TDIGEST.REVRANK](https://redis.io/commands/tdigest.revrank/) | Returns, for each input value (floating-point), the estimated reverse rank of the value (the number of observations in the sketch that are larger than the value + half the number of observations that are equal to the value) | O(N) where N is the number of values specified. | 2.4.0 |\n| [TDIGEST.TRIMMED_MEAN](https://redis.io/commands/tdigest.trimmed_mean/) | Returns an estimation of the mean value from the sketch, excluding observation values outside the low and high cutoff quantiles | O(N) where N is the number of centroids | 2.4.0 |\n\n\n\nThe t-digest is a sketch data structure in Redis Open Source for estimating percentiles from a data stream or a large dataset using a compact sketch.\n\nIt can answer questions like:\n- Which fraction of the values in the data stream are smaller than a given value?\n- How many values in the data stream are smaller than a given value?\n- What's the highest value that's smaller than *p* percent of the values in the data stream? (what is the p-percentile value)?"},{"id":"what-is-t-digest","title":"What is t-digest?","role":"content","text":"t-digest is a data structure that will estimate a percentile point without having to store and order all the data points in a set. For example: to answer the question \"What's the average latency for 99% of my database operations\" we would have to store the average latency for every user, order the values, cut out the last 1% and only then find the average value of all the rest. This kind of process is costly not just in terms of the processing needed to order those values but also in terms of the space needed to store them. Those are precisely the problems t-digest solves.\n\nt-digest can also be used to estimate other values related to percentiles, like trimmed means.   \n\n> A **trimmed mean** is the mean value from the sketch, excluding observation values outside the low and high cutoff percentiles. For example, a 0.1 trimmed mean is the mean value of the sketch, excluding the lowest 10% and the highest 10% of the values."},{"id":"use-cases","title":"Use cases","role":"content","text":"**Hardware/software monitoring**\n\nYou measure your online server response latency, and you like to query:\n\n- What are the 50th, 90th, and 99th percentiles of the measured latencies?\n\n- Which fraction of the measured latencies are less than 25 milliseconds?\n\n- What is the mean latency, ignoring outliers? or What is the mean latency between the 10th and the 90th percentile?\n\n**Online gaming**\n\nMillions of people are playing a game on your online gaming platform, and you want to give the following information to each player?\n\n- Your score is better than x percent of the game sessions played.\n\n- There were about y game sessions where people scored larger than you.\n\n- To have a better score than 90% of the games played, your score should be z.\n\n**Network traffic monitoring**\n\nYou measure the IP packets transferred over your network each second and try to detect denial-of-service attacks by asking:\n\n- Does the number of packets in the last second exceed 99% of previously observed values?\n\n- How many packets do I expect to see under _normal_ network conditions? \n(Answer: between x and y, where x represents the 1st percentile and y represents the 99th percentile).\n\n**Predictive maintenance**\n\n- Was the measured parameter (noise level, current consumption, etc.) irregular? (not within the [1st percentile...99th percentile] range)?\n\n- To which values should I set my alerts?"},{"id":"examples","title":"Examples","role":"example","text":"In the following example, you'll create a t-digest with a compression of 100 and add items to it. The `COMPRESSION` argument is used to specify the tradeoff between accuracy and memory consumption. The default value is 100. Higher values mean more accuracy. Note: unlike some of the other probabilistic data structures, the [`TDIGEST.ADD`](https://redis.io/docs/latest/commands/tdigest.add) command will not create a new structure if the key does not exist.\n\nt-digest creation and data addition: Use TDIGEST.CREATE to initialize a sketch and TDIGEST.ADD to insert values when you need to build a percentile estimation data structure\n\n**Commands:** TDIGEST.CREATE, TDIGEST.ADD\n\n**Complexity:**\n- TDIGEST.CREATE: O(1)\n- TDIGEST.ADD: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\n\nYou can repeat calling [TDIGEST.ADD](https://redis.io/docs/latest/commands/tdigest.add) whenever new observations are available\n\n#### Estimating fractions or ranks by values\n\nAnother helpful feature in t-digest is CDF (definition of rank) which gives us the fraction of observations smaller or equal to a certain value. This command is very useful to answer questions like \"*What's the percentage of observations with a value lower or equal to X*\".\n\n>More precisely, [`TDIGEST.CDF`](https://redis.io/docs/latest/commands/tdigest.cdf) will return the estimated fraction of observations in the sketch that are smaller than X plus half the number of observations that are equal to X. We can also use the [`TDIGEST.RANK`](https://redis.io/docs/latest/commands/tdigest.rank) command, which is very similar. Instead of returning a fraction, it returns the ----estimated---- rank of a value. The [`TDIGEST.RANK`](https://redis.io/docs/latest/commands/tdigest.rank) command is also variadic, meaning you can use a single command to retrieve estimations for one or more values.\n\nHere's an example. Given a set of biker's ages, you can ask a question like \"What's the percentage of bike racers that are younger than 50 years?\"\n\nPercentile rank estimation: Use TDIGEST.CDF to estimate the fraction of values below a threshold and TDIGEST.RANK to estimate the rank of values when you need to determine percentile positions\n\n**Difficulty:** Intermediate\n\n**Builds upon:** tdig_start\n\n**Commands:** TDIGEST.CREATE, TDIGEST.ADD, TDIGEST.CDF, TDIGEST.RANK\n\n**Complexity:**\n- TDIGEST.CREATE: O(1)\n- TDIGEST.ADD: O(N)\n- TDIGEST.CDF: O(N)\n- TDIGEST.RANK: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\n\nAnd lastly, `TDIGEST.REVRANK key value...` is similar to [TDIGEST.RANK](https://redis.io/docs/latest/commands/tdigest.rank), but returns, for each input value, an estimation of the number of (observations larger than a given value + half the observations equal to the given value).\n\n\n#### Estimating values by fractions or ranks\n\n`TDIGEST.QUANTILE key fraction...` returns, for each input fraction, an estimation of the value (floating point) that is smaller than the given fraction of observations. `TDIGEST.BYRANK key rank...` returns, for each input rank, an estimation of the value (floating point) with that rank.\n\nQuantile and rank value estimation: Use TDIGEST.QUANTILE to find values at specific percentiles and TDIGEST.BYRANK to find values by rank when you need to retrieve percentile values from a sketch\n\n**Difficulty:** Intermediate\n\n**Builds upon:** tdig_cdf\n\n**Commands:** TDIGEST.QUANTILE, TDIGEST.BYRANK\n\n**Complexity:**\n- TDIGEST.QUANTILE: O(N)\n- TDIGEST.BYRANK: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\n`TDIGEST.BYREVRANK key rank...` returns, for each input **reverse rank**, an estimation of the **value** (floating point) with that reverse rank.\n\n#### Estimating trimmed mean\n\nUse `TDIGEST.TRIMMED_MEAN key lowFraction highFraction` to retrieve an estimation of the mean value between the specified fractions.\n\nThis is especially useful for calculating the average value ignoring outliers. For example - calculating the average value between the 20th percentile and the 80th percentile.\n\n#### Merging sketches\n\nSometimes it is useful to merge sketches. For example, suppose we measure latencies for 3 servers, and we want to calculate the 90%, 95%, and 99% latencies for all the servers combined.\n\n`TDIGEST.MERGE destKey numKeys sourceKey... [COMPRESSION compression] [OVERRIDE]` merges multiple sketches into a single sketch.\n\nIf `destKey` does not exist - a new sketch is created.\n\nIf `destKey` is an existing sketch, its values are merged with the values of the source keys. To override the destination key contents, use `OVERRIDE`.\n\n#### Retrieving sketch information\n\nUse [`TDIGEST.MIN`](https://redis.io/docs/latest/commands/tdigest.min) and [`TDIGEST.MAX`](https://redis.io/docs/latest/commands/tdigest.max) to retrieve the minimal and maximal values in the sketch, respectively.\n\nSketch metadata retrieval: Use TDIGEST.MIN and TDIGEST.MAX to retrieve the minimum and maximum values in a sketch when you need to inspect the bounds of your data\n\n**Builds upon:** tdig_cdf\n\n**Commands:** TDIGEST.MIN, TDIGEST.MAX\n\n**Complexity:**\n- TDIGEST.MIN: O(1)\n- TDIGEST.MAX: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nBoth return `nan` when the sketch is empty.\n\nBoth commands return accurate results and are equivalent to `TDIGEST.BYRANK racer_ages 0` and `TDIGEST.BYREVRANK racer_ages 0`, respectively.\n\nUse `TDIGEST.INFO racer_ages` to retrieve some additional information about the sketch.\n\n#### Resetting a sketch\n\nSketch reset: Use TDIGEST.RESET to clear all data from a sketch when you need to reuse a sketch for new data\n\n**Builds upon:** tdig_cdf\n\n**Commands:** TDIGEST.RESET\n\n**Complexity:**\n- TDIGEST.RESET: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]"},{"id":"academic-sources","title":"Academic sources","role":"content","text":"- [The _t_-digest: Efficient estimates of distributions](https://www.sciencedirect.com/science/article/pii/S2665963820300403)"},{"id":"references","title":"References","role":"related","text":"- [t-digest: A New Probabilistic Data Structure in Redis Stack](https://redis.com/blog/t-digest-in-redis-stack/)"}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> TDIGEST.CREATE bikes:sales COMPRESSION 100\nOK\n> TDIGEST.ADD bikes:sales 21\nOK\n> TDIGEST.ADD bikes:sales 150 95 75 34\nOK","section_id":"examples"},{"id":"examples-ex1","language":"csharp","code":"bool res1 = db.TDIGEST().Create(\"bikes:sales\", 100);\n        Console.WriteLine(res1);    // >>> True\n\n        bool res2 = db.TDIGEST().Add(\"bikes:sales\", 21);\n        Console.WriteLine(res2);    // >>> True\n\n        bool res3 = db.TDIGEST().Add(\"bikes:sales\", 150, 95, 75, 34);\n        Console.WriteLine(res3);    // >>> true","section_id":"examples"},{"id":"examples-ex2","language":"go","code":"res1, err := rdb.TDigestCreate(ctx, \"bikes:sales\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> OK\n\n\tres2, err := rdb.TDigestAdd(ctx, \"bikes:sales\", 21).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> OK\n\n\tres3, err := rdb.TDigestAdd(ctx, \"bikes:sales\",\n\t\t150, 95, 75, 34,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3) // >>> OK","section_id":"examples"},{"id":"examples-ex3","language":"java","code":"CompletableFuture<Void> tdigStartExample = asyncCommands\n                    .tdigestCreate(\"bikes:sales\", 100L)\n                    .thenCompose(res1 -> {\n                        System.out.println(res1);\n                        // >>> OK\n                        return asyncCommands.tdigestAdd(\"bikes:sales\", 21);\n                    })\n                    .thenCompose(res2 -> {\n                        System.out.println(res2);\n                        // >>> OK\n                        return asyncCommands.tdigestAdd(\"bikes:sales\", 150, 95, 75, 34);\n                    })\n                    .thenAccept(res3 -> {\n                        System.out.println(res3);\n                        // >>> OK\n                    })\n                    .toCompletableFuture();\n\n            tdigStartExample.join();","section_id":"examples"},{"id":"examples-ex4","language":"java","code":"String res1 = reactiveCommands.tdigestCreate(\"bikes:sales\", 100L).block();\n            System.out.println(res1);\n            // >>> OK\n\n            String res2 = reactiveCommands.tdigestAdd(\"bikes:sales\", 21).block();\n            System.out.println(res2);\n            // >>> OK\n\n            String res3 = reactiveCommands.tdigestAdd(\"bikes:sales\", 150, 95, 75, 34).block();\n            System.out.println(res3);\n            // >>> OK","section_id":"examples"},{"id":"examples-ex5","language":"java","code":"String res1 = jedis.tdigestCreate(\"bikes:sales\", 100);\n        System.out.println(res1); // >>> True\n\n        String res2 = jedis.tdigestAdd(\"bikes:sales\", 21);\n        System.out.println(res2); // >>> OK\n\n        String res3 = jedis.tdigestAdd(\"bikes:sales\", 150, 95, 75, 34);\n        System.out.println(res3); // >>> OK","section_id":"examples"},{"id":"examples-ex6","language":"javascript","code":"const res1 = await client.tDigest.create('bikes:sales', 100);\nconsole.log(res1);  // >>> OK\n\nconst res2 = await client.tDigest.add('bikes:sales', [21]);\nconsole.log(res2);  // >>> OK\n\nconst res3 = await client.tDigest.add('bikes:sales', [150, 95, 75, 34]);\nconsole.log(res3);  // >>> OK","section_id":"examples"},{"id":"examples-ex7","language":"php","code":"$res1 = $r->tdigestcreate('bikes:sales', 100);\n        echo $res1 . PHP_EOL;\n        // >>> OK\n\n        $res2 = $r->tdigestadd('bikes:sales', 21);\n        echo $res2 . PHP_EOL;\n        // >>> OK\n\n        $res3 = $r->tdigestadd('bikes:sales', 150, 95, 75, 34);\n        echo $res3 . PHP_EOL;\n        // >>> OK","section_id":"examples"},{"id":"examples-ex8","language":"python","code":"res1 = r.tdigest().create(\"bikes:sales\", 100)\nprint(res1)  # >>> True\n\nres2 = r.tdigest().add(\"bikes:sales\", [21])\nprint(res2)  # >>> OK\n\nres3 = r.tdigest().add(\"bikes:sales\", [150, 95, 75, 34])\nprint(res3)  # >>> OK","section_id":"examples"},{"id":"examples-ex9","language":"plaintext","code":"> TDIGEST.CREATE racer_ages\nOK\n> TDIGEST.ADD racer_ages 45.88 44.2 58.03 19.76 39.84 69.28 50.97 25.41 19.27 85.71 42.63\nOK\n> TDIGEST.CDF racer_ages 50\n1) \"0.6363636363636364\"\n> TDIGEST.RANK racer_ages 50\n1) (integer) 7\n> TDIGEST.RANK racer_ages 50 40\n1) (integer) 7\n2) (integer) 4","section_id":"examples"},{"id":"examples-ex10","language":"csharp","code":"bool res4 = db.TDIGEST().Create(\"racer_ages\");\n        Console.WriteLine(res4);    // >>> True\n\n        bool res5 = db.TDIGEST().Add(\"racer_ages\",\n            45.88,\n            44.2,\n            58.03,\n            19.76,\n            39.84,\n            69.28,\n            50.97,\n            25.41,\n            19.27,\n            85.71,\n            42.63\n        );\n        Console.WriteLine(res5);    // >>> True\n\n        long[] res6 = db.TDIGEST().Rank(\"racer_ages\", 50);\n        Console.WriteLine(string.Join(\", \", res6)); // >>> 7\n\n        long[] res7 = db.TDIGEST().Rank(\"racer_ages\", 50, 40);\n        Console.WriteLine(string.Join(\", \", res7)); // >>> 7, 4","section_id":"examples"},{"id":"examples-ex11","language":"go","code":"res4, err := rdb.TDigestCreate(ctx, \"racer_ages\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4) // >>> OK\n\n\tres5, err := rdb.TDigestAdd(ctx, \"racer_ages\",\n\t\t45.88, 44.2, 58.03, 19.76, 39.84, 69.28,\n\t\t50.97, 25.41, 19.27, 85.71, 42.63,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res5) // >>> OK\n\n\tres6, err := rdb.TDigestRank(ctx, \"racer_ages\", 50).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res6) // >>> [7]\n\n\tres7, err := rdb.TDigestRank(ctx, \"racer_ages\", 50, 40).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res7) // >>> [7 4]","section_id":"examples"},{"id":"examples-ex12","language":"java","code":"CompletableFuture<Void> tdigCdfExample = asyncCommands\n                    .tdigestCreate(\"racer_ages\")\n                    .thenCompose(res4 -> {\n                        System.out.println(res4);\n                        // >>> OK\n                        return asyncCommands.tdigestAdd(\"racer_ages\",\n                                45.88, 44.2, 58.03, 19.76, 39.84, 69.28,\n                                50.97, 25.41, 19.27, 85.71, 42.63);\n                    })\n                    .thenCompose(res5 -> {\n                        System.out.println(res5);\n                        // >>> OK\n                        return asyncCommands.tdigestRank(\"racer_ages\", 50);\n                    })\n                    .thenCompose(res6 -> {\n                        System.out.println(res6);\n                        // >>> [7]\n                        return asyncCommands.tdigestRank(\"racer_ages\", 50, 40);\n                    })\n                    .thenAccept(res7 -> {\n                        System.out.println(res7);\n                        // >>> [7, 4]\n                    })\n                    .toCompletableFuture();\n\n            tdigCdfExample.join();","section_id":"examples"},{"id":"examples-ex13","language":"java","code":"String res4 = reactiveCommands.tdigestCreate(\"racer_ages\").block();\n            System.out.println(res4);\n            // >>> OK\n\n            String res5 = reactiveCommands.tdigestAdd(\"racer_ages\",\n                    45.88, 44.2, 58.03, 19.76, 39.84, 69.28,\n                    50.97, 25.41, 19.27, 85.71, 42.63).block();\n            System.out.println(res5);\n            // >>> OK\n\n            List<Long> res6 = reactiveCommands.tdigestRank(\"racer_ages\", 50).collectList().block();\n            System.out.println(res6);\n            // >>> [7]\n\n            List<Long> res7 = reactiveCommands.tdigestRank(\"racer_ages\", 50, 40).collectList().block();\n            System.out.println(res7);\n            // >>> [7, 4]","section_id":"examples"},{"id":"examples-ex14","language":"java","code":"String res4 = jedis.tdigestCreate(\"racer_ages\");\n        System.out.println(res4); // >>> True\n\n        String res5 = jedis.tdigestAdd(\"racer_ages\", 45.88,\n                44.2,\n                58.03,\n                19.76,\n                39.84,\n                69.28,\n                50.97,\n                25.41,\n                19.27,\n                85.71,\n                42.63);\n        System.out.println(res5); // >>> OK\n\n        List<Long> res6 = jedis.tdigestRank(\"racer_ages\", 50);\n        System.out.println(res6); // >>> [7]\n\n        List<Long> res7 = jedis.tdigestRank(\"racer_ages\", 50, 40);\n        System.out.println(res7); // >>> [7, 4]","section_id":"examples"},{"id":"examples-ex15","language":"javascript","code":"const res4 = await client.tDigest.create('racer_ages');\nconsole.log(res4);  // >>> OK\n\nconst res5 = await client.tDigest.add('racer_ages', [\n  45.88, 44.2, 58.03, 19.76, 39.84, 69.28, 50.97, 25.41, 19.27, 85.71, 42.63\n]);\nconsole.log(res5);  // >>> OK\n\nconst res6 = await client.tDigest.rank('racer_ages', [50]);\nconsole.log(res6);  // >>> [7]\n\nconst res7 = await client.tDigest.rank('racer_ages', [50, 40]);\nconsole.log(res7);  // >>> [7, 4]","section_id":"examples"},{"id":"examples-ex16","language":"php","code":"$res4 = $r->tdigestcreate('racer_ages');\n        echo $res4 . PHP_EOL;\n        // >>> OK\n\n        $res5 = $r->tdigestadd(\n            'racer_ages',\n            45.88,\n            44.2,\n            58.03,\n            19.76,\n            39.84,\n            69.28,\n            50.97,\n            25.41,\n            19.27,\n            85.71,\n            42.63\n        );\n        echo $res5 . PHP_EOL;\n        // >>> OK\n\n        $res6 = $r->tdigestrank('racer_ages', 50);\n        echo json_encode($res6) . PHP_EOL;\n        // >>> [7]\n\n        $res7 = $r->tdigestrank('racer_ages', 50, 40);\n        echo json_encode($res7) . PHP_EOL;\n        // >>> [7,4]","section_id":"examples"},{"id":"examples-ex17","language":"python","code":"res4 = r.tdigest().create(\"racer_ages\")\nprint(res4)  # >>> True\n\nres5 = r.tdigest().add(\n    \"racer_ages\",\n    [\n        45.88,\n        44.2,\n        58.03,\n        19.76,\n        39.84,\n        69.28,\n        50.97,\n        25.41,\n        19.27,\n        85.71,\n        42.63,\n    ],\n)\nprint(res5)  # >>> OK\n\nres6 = r.tdigest().rank(\"racer_ages\", 50)\nprint(res6)  # >>> [7]\n\nres7 = r.tdigest().rank(\"racer_ages\", 50, 40)\nprint(res7)  # >>> [7, 4]","section_id":"examples"},{"id":"examples-ex18","language":"plaintext","code":"> TDIGEST.QUANTILE racer_ages .5\n1) \"44.2\"\n> TDIGEST.BYRANK racer_ages 4\n1) \"42.63\"","section_id":"examples"},{"id":"examples-ex19","language":"csharp","code":"double[] res8 = db.TDIGEST().Quantile(\"racer_ages\", 0.5); ;\n        Console.WriteLine(string.Join(\", \", res8)); // >>> 44.2\n\n        double[] res9 = db.TDIGEST().ByRank(\"racer_ages\", 4);\n        Console.WriteLine(string.Join(\", \", res9)); // >>> 42.63","section_id":"examples"},{"id":"examples-ex20","language":"go","code":"res8, err := rdb.TDigestQuantile(ctx, \"racer_ages\", 0.5).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res8) // >>> [44.2]\n\n\tres9, err := rdb.TDigestByRank(ctx, \"racer_ages\", 4).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res9) // >>> [42.63]","section_id":"examples"},{"id":"examples-ex21","language":"java","code":"CompletableFuture<Void> tdigQuantExample = asyncCommands\n                    .tdigestQuantile(\"racer_ages\", 0.5)\n                    .thenCompose(res8 -> {\n                        System.out.println(res8);\n                        // >>> [44.2]\n                        return asyncCommands.tdigestByRank(\"racer_ages\", 4L);\n                    })\n                    .thenAccept(res9 -> {\n                        System.out.println(res9);\n                        // >>> [42.63]\n                    })\n                    .toCompletableFuture();\n\n            tdigQuantExample.join();","section_id":"examples"},{"id":"examples-ex22","language":"java","code":"List<Double> res8 = reactiveCommands.tdigestQuantile(\"racer_ages\", 0.5).collectList().block();\n            System.out.println(res8);\n            // >>> [44.2]\n\n            List<Double> res9 = reactiveCommands.tdigestByRank(\"racer_ages\", 4L).collectList().block();\n            System.out.println(res9);\n            // >>> [42.63]","section_id":"examples"},{"id":"examples-ex23","language":"java","code":"List<Double> res8 = jedis.tdigestQuantile(\"racer_ages\", 0.5);\n        System.out.println(res8); // >>> [44.2]\n\n        List<Double> res9 = jedis.tdigestByRank(\"racer_ages\", 4);\n        System.out.println(res9); // >>> [42.63]","section_id":"examples"},{"id":"examples-ex24","language":"javascript","code":"const res8 = await client.tDigest.quantile('racer_ages', [0.5]);\nconsole.log(res8);  // >>> [44.2]\n\nconst res9 = await client.tDigest.byRank('racer_ages', [4]);\nconsole.log(res9);  // >>> [42.63]","section_id":"examples"},{"id":"examples-ex25","language":"php","code":"$res8 = $r->tdigestquantile('racer_ages', 0.5);\n        echo json_encode($res8) . PHP_EOL;\n        // >>> [\"44.2\"]\n\n        $res9 = $r->tdigestbyrank('racer_ages', 4);\n        echo json_encode($res9) . PHP_EOL;\n        // >>> [\"42.63\"]","section_id":"examples"},{"id":"examples-ex26","language":"python","code":"res8 = r.tdigest().quantile(\"racer_ages\", 0.5)\nprint(res8)  # >>> [44.2]\n\nres9 = r.tdigest().byrank(\"racer_ages\", 4)\nprint(res9)  # >>> [42.63]","section_id":"examples"},{"id":"examples-ex27","language":"plaintext","code":"> TDIGEST.MIN racer_ages\n\"19.27\"\n> TDIGEST.MAX racer_ages\n\"85.71\"","section_id":"examples"},{"id":"examples-ex28","language":"csharp","code":"double res10 = db.TDIGEST().Min(\"racer_ages\");\n        Console.WriteLine(res10);   // >>> 19.27\n\n        double res11 = db.TDIGEST().Max(\"racer_ages\");\n        Console.WriteLine(res11);   // >>> 85.71","section_id":"examples"},{"id":"examples-ex29","language":"go","code":"res10, err := rdb.TDigestMin(ctx, \"racer_ages\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res10) // >>> 19.27\n\n\tres11, err := rdb.TDigestMax(ctx, \"racer_ages\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res11) // >>> 85.71","section_id":"examples"},{"id":"examples-ex30","language":"java","code":"CompletableFuture<Void> tdigMinExample = asyncCommands\n                    .tdigestMin(\"racer_ages\")\n                    .thenCompose(res10 -> {\n                        System.out.println(res10);\n                        // >>> 19.27\n                        return asyncCommands.tdigestMax(\"racer_ages\");\n                    })\n                    .thenAccept(res11 -> {\n                        System.out.println(res11);\n                        // >>> 85.71\n                    })\n                    .toCompletableFuture();\n\n            tdigMinExample.join();","section_id":"examples"},{"id":"examples-ex31","language":"java","code":"Double res10 = reactiveCommands.tdigestMin(\"racer_ages\").block();\n            System.out.println(res10);\n            // >>> 19.27\n\n            Double res11 = reactiveCommands.tdigestMax(\"racer_ages\").block();\n            System.out.println(res11);\n            // >>> 85.71","section_id":"examples"},{"id":"examples-ex32","language":"java","code":"double res10 = jedis.tdigestMin(\"racer_ages\");\n        System.out.println(res10); // >>> 19.27\n\n        double res11 = jedis.tdigestMax(\"racer_ages\");\n        System.out.println(res11); // >>> 85.71","section_id":"examples"},{"id":"examples-ex33","language":"javascript","code":"const res10 = await client.tDigest.min('racer_ages');\nconsole.log(res10);  // >>> 19.27\n\nconst res11 = await client.tDigest.max('racer_ages');\nconsole.log(res11);  // >>> 85.71","section_id":"examples"},{"id":"examples-ex34","language":"php","code":"$res10 = $r->tdigestmin('racer_ages');\n        echo $res10 . PHP_EOL;\n        // >>> 19.27\n\n        $res11 = $r->tdigestmax('racer_ages');\n        echo $res11 . PHP_EOL;\n        // >>> 85.71","section_id":"examples"},{"id":"examples-ex35","language":"python","code":"res10 = r.tdigest().min(\"racer_ages\")\nprint(res10)  # >>> 19.27\n\nres11 = r.tdigest().max(\"racer_ages\")\nprint(res11)  # >>> 85.71","section_id":"examples"},{"id":"examples-ex36","language":"plaintext","code":"> TDIGEST.RESET racer_ages\nOK","section_id":"examples"},{"id":"examples-ex37","language":"csharp","code":"bool res12 = db.TDIGEST().Reset(\"racer_ages\");\n        Console.WriteLine(res12);   // >>> True","section_id":"examples"},{"id":"examples-ex38","language":"go","code":"res12, err := rdb.TDigestReset(ctx, \"racer_ages\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res12) // >>> OK","section_id":"examples"},{"id":"examples-ex39","language":"java","code":"CompletableFuture<Void> tdigResetExample = asyncCommands\n                    .tdigestReset(\"racer_ages\")\n                    .thenAccept(res12 -> {\n                        System.out.println(res12);\n                        // >>> OK\n                    })\n                    .toCompletableFuture();\n\n            tdigResetExample.join();","section_id":"examples"},{"id":"examples-ex40","language":"java","code":"String res12 = reactiveCommands.tdigestReset(\"racer_ages\").block();\n            System.out.println(res12);\n            // >>> OK","section_id":"examples"},{"id":"examples-ex41","language":"java","code":"String res12 = jedis.tdigestReset(\"racer_ages\");\n        System.out.println(res12); // >>> OK","section_id":"examples"},{"id":"examples-ex42","language":"javascript","code":"const res12 = await client.tDigest.reset('racer_ages');\nconsole.log(res12);  // >>> OK","section_id":"examples"},{"id":"examples-ex43","language":"php","code":"$res12 = $r->tdigestreset('racer_ages');\n        echo $res12 . PHP_EOL;\n        // >>> OK","section_id":"examples"},{"id":"examples-ex44","language":"python","code":"res12 = r.tdigest().reset(\"racer_ages\")\nprint(res12)  # >>> OK","section_id":"examples"}]}
{"schema_version":2,"id":"develop/data-types/probabilistic/top-k","title":"Top-K","url":"https://redis.io/docs/latest/develop/data-types/probabilistic/top-k/","summary":"Top-K is a probabilistic data structure that allows you to find the most frequent items in a data stream.","aliases":["/data-types/probabilistic/top-k/","/manual/data-types/probabilistic/top-k/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-10T15:45:40+01:00","page_type":"content","content_hash":"015e76e56d9b898f265af8a998a73d7c9b1760f26b2b379d43ea0ccae7892a98","sections":[{"id":"top-k-command-summary","title":"Top-K command summary","role":"content","text":"**7 commands in this group:**\n\n[View all topk commands](https://redis.io/commands/?group=topk)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [TOPK.ADD](https://redis.io/commands/topk.add/) | Adds an item to a Top-k sketch. Multiple items can be added at the same time. | O(n * k) where n is the number of items and k i... | 2.0.0 |\n| [TOPK.COUNT](https://redis.io/commands/topk.count/) | Return the count for one or more items are in a sketch | O(n) where n is the number of items | 2.0.0 |\n| [TOPK.INCRBY](https://redis.io/commands/topk.incrby/) | Increases the count of one or more items by increment | O(n * k * incr) where n is the number of items,... | 2.0.0 |\n| [TOPK.INFO](https://redis.io/commands/topk.info/) | Returns information about a sketch | O(1) | 2.0.0 |\n| [TOPK.LIST](https://redis.io/commands/topk.list/) | Return the full list of items in Top-K sketch. | O(k*log(k)) where k is the value of top-k | 2.0.0 |\n| [TOPK.QUERY](https://redis.io/commands/topk.query/) | Checks whether one or more items are in a sketch | O(n) where n is the number of items | 2.0.0 |\n| [TOPK.RESERVE](https://redis.io/commands/topk.reserve/) | Initializes a Top-K sketch with specified parameters | O(1) | 2.0.0 |\n\n\n\nTop K is a probabilistic data structure in Redis Open Source used to estimate the `K` highest-rank elements from a stream.\n\n\"Highest-rank\" in this case means \"elements with a highest number or score attached to them\", where the score can be a count of how many times the element has appeared in the stream - thus making the data structure perfect for finding the elements with the highest frequency in a stream.\nOne very common application is detecting network anomalies and DDoS attacks where Top K can answer the question: Is there a sudden increase in the flux of requests to the same address or from the same IP?\n \nThere is, indeed, some overlap with the functionality of Count-Min Sketch, but the two data structures have their differences and should be applied for different use cases. \n\nThe Redis Open Source implementation of Top-K is based on the [HeavyKeepers](https://www.usenix.org/conference/atc18/presentation/gong) algorithm presented by Junzhi Gong et al. It discards some older approaches like \"count-all\" and \"admit-all-count-some\" in favour of a \"**count-with-exponential-decay**\" strategy which is biased against mouse (small) flows and has a limited impact on elephant (large) flows. This implementation uses two data structures in tandem: a hash table that holds the probabilistic counts (much like the Count-Min Sketch), and a min heap that holds the `K` items with the highest counts. This ensures high accuracy with shorter execution times than previous probabilistic algorithms allowed, while keeping memory utilization to a fraction of what is typically required by a Sorted Set. It has the additional benefit of being able to get real time notifications when elements are added or removed from the Top K list."},{"id":"use-case","title":"Use case","role":"content","text":"**Trending hashtags (social media platforms, news distribution networks)** \n\nThis application answers these questions: \n\n- What are the K hashtags people have mentioned the most in the last X hours? \n- What are the K news with highest read/view count today? \n\nData flow is the incoming social media posts from which you parse out the different hashtags. \n\nThe [`TOPK.LIST`](https://redis.io/docs/latest/commands/topk.list) command has a time complexity of `O(K*log(k))` so if `K` is small, there is no need to keep a separate set or sorted set of all the hashtags. You can query directly from the Top K itself."},{"id":"example","title":"Example","role":"example","text":"This example will show you how to track key words used \"bike\" when shopping online; e.g., \"bike store\" and \"bike handlebars\". Proceed as follows.\n​\n* Use [`TOPK.RESERVE`](https://redis.io/docs/latest/commands/topk.reserve) to initialize a top K sketch with specific parameters. Note: the `width`, `depth`, and `decay_constant` parameters can be omitted, as they will be set to the default values 7, 8, and 0.9, respectively, if not present.\n​\n [code example]\n \n * Use [`TOPK.ADD`](https://redis.io/docs/latest/commands/topk.add) to add items to the sketch. As you can see, multiple items can be added at the same time. If an item is returned when adding additional items, it means that item was demoted out of the min heap of the top items, below it will mean the returned item is no longer in the top 5, otherwise `nil` is returned. This allows dynamic heavy-hitter detection of items being entered or expelled from top K list.\n​\nIn the example below, \"pedals\" displaces \"handlebars\", which is returned after \"pedals\" is added. Also note that the addition of both \"store\" and \"seat\" a second time don't return anything, as they're already in the top K.\n \n * Use [`TOPK.LIST`](https://redis.io/docs/latest/commands/topk.list) to list the items entered thus far.\n​\n * Use [`TOPK.QUERY`](https://redis.io/docs/latest/commands/topk.query) to see if an item is on the top K list. Just like [`TOPK.ADD`](https://redis.io/docs/latest/commands/topk.add) multiple items can be queried at the same time.\nTop-K operations: Use TOPK.RESERVE to initialize a sketch, TOPK.ADD to track item frequencies, TOPK.LIST to retrieve top items, and TOPK.QUERY to check membership when you need to identify the most frequent items in a data stream\n\n**Difficulty:** Intermediate\n\n**Commands:** TOPK.RESERVE, TOPK.ADD, TOPK.LIST, TOPK.QUERY\n\n**Complexity:**\n- TOPK.RESERVE: O(1)\n- TOPK.ADD: O(n * k)\n- TOPK.LIST: O(k*log(k)\n- TOPK.QUERY: O(n)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]"},{"id":"sizing","title":"Sizing","role":"content","text":"Choosing the size for a Top K sketch is relatively easy, because the only two parameters you need to set are a direct function of the number of elements (K) you want to keep in your list.\n\nIf you start by knowing your desired `k` you can easily derive the width and depth:\n\n[code example]\n\nFor the `decay_constant` you can use the value `0.9` which has been found as optimal in many cases, but you can experiment with different values and find what works best for your use case."},{"id":"performance","title":"Performance","role":"performance","text":"Insertion in a top-k has time complexity of O(K + depth) ≈ O(K) and lookup has time complexity of O(K), where K is the number of top elements to be kept in the list and depth is the number of hash functions used."},{"id":"academic-sources","title":"Academic sources","role":"content","text":"- [HeavyKeeper: An Accurate Algorithm for Finding Top-k Elephant Flows.](https://yangtonghome.github.io/uploads/HeavyKeeper_ToN.pdf)"},{"id":"references","title":"References","role":"related","text":"- [Meet Top-K: an Awesome Probabilistic Addition to Redis](https://redis.com/blog/meet-top-k-awesome-probabilistic-addition-redis/)"}],"examples":[{"id":"example-ex0","language":"plaintext","code":"> TOPK.RESERVE key k width depth decay_constant","section_id":"example"},{"id":"example-ex1","language":"plaintext","code":"> TOPK.RESERVE bikes:keywords 5 2000 7 0.925\nOK\n> TOPK.ADD bikes:keywords store seat handlebars handles pedals tires store seat\n1) (nil)\n2) (nil)\n3) (nil)\n4) (nil)\n5) (nil)\n6) \"handlebars\"\n7) (nil)\n8) (nil)\n> TOPK.LIST bikes:keywords\n1) \"store\"\n2) \"seat\"\n3) \"pedals\"\n4) \"tires\"\n5) \"handles\"\n> TOPK.QUERY bikes:keywords store handlebars\n1) (integer) 1\n2) (integer) 0","section_id":"example"},{"id":"example-ex2","language":"csharp","code":"bool res1 = db.TOPK().Reserve(\"bikes:keywords\", 5, 2000, 7, 0.925);\n        Console.WriteLine(res1);    // >>> True\n\n        RedisResult[]? res2 = db.TOPK().Add(\"bikes:keywords\",\n                \"store\",\n                \"seat\",\n                \"handlebars\",\n                \"handles\",\n                \"pedals\",\n                \"tires\",\n                \"store\",\n                \"seat\"\n        );\n\n        if (res2 is not null)\n        {\n            Console.WriteLine(string.Join(\", \", string.Join(\", \", res2.Select(r => $\"{(r.IsNull ? \"Null\" : r)}\"))));\n            // >>> Null, Null, Null, Null, Null, handlebars, Null, Null\n        }\n\n        RedisResult[] res3 = db.TOPK().List(\"bikes:keywords\");\n\n        if (res3 is not null)\n        {\n            Console.WriteLine(string.Join(\", \", string.Join(\", \", res3.Select(r => $\"{(r.IsNull ? \"Null\" : r)}\"))));\n            // >>> store, seat, pedals, tires, handles\n        }\n\n        bool[] res4 = db.TOPK().Query(\"bikes:keywords\", \"store\", \"handlebars\");\n        Console.WriteLine(string.Join(\", \", res4)); // >>> True, False","section_id":"example"},{"id":"example-ex3","language":"go","code":"res1, err := rdb.TopKReserve(ctx, \"bikes:keywords\", 5).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> OK\n\n\tres2, err := rdb.TopKAdd(ctx, \"bikes:keywords\",\n\t\t\"store\",\n\t\t\"seat\",\n\t\t\"handlebars\",\n\t\t\"handles\",\n\t\t\"pedals\",\n\t\t\"tires\",\n\t\t\"store\",\n\t\t\"seat\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> [     handlebars  ]\n\n\tres3, err := rdb.TopKList(ctx, \"bikes:keywords\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3) // [store seat pedals tires handles]\n\n\tres4, err := rdb.TopKQuery(ctx, \"bikes:keywords\", \"store\", \"handlebars\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4) // [true false]","section_id":"example"},{"id":"example-ex4","language":"java","code":"CompletableFuture<Void> topKExample = asyncCommands\n                    .topKReserve(\"bikes:keywords\", 5L,\n                            TopKReserveArgs.Builder.width(2000L).depth(7L).decay(0.925))\n                    .thenCompose(res1 -> {\n                        System.out.println(res1);\n                        // >>> OK\n                        return asyncCommands.topKAdd(\"bikes:keywords\",\n                                \"store\", \"seat\", \"handlebars\", \"handles\", \"pedals\",\n                                \"tires\", \"store\", \"seat\");\n                    })\n                    .thenCompose(res2 -> {\n                        System.out.println(res2);\n                        // >>> [null, null, null, null, null, handlebars, null, null]\n                        return asyncCommands.topKList(\"bikes:keywords\");\n                    })\n                    .thenCompose(res3 -> {\n                        System.out.println(res3);\n                        // >>> [store, seat, pedals, tires, handles]\n                        return asyncCommands.topKQuery(\"bikes:keywords\", \"store\", \"handlebars\");\n                    })\n                    .thenAccept(res4 -> {\n                        System.out.println(res4);\n                        // >>> [true, false]\n                    })\n                    .toCompletableFuture();\n\n            topKExample.join();","section_id":"example"},{"id":"example-ex5","language":"java","code":"Mono<Void> topKExample = reactiveCommands\n                    .topKReserve(\"bikes:keywords\", 5L,\n                            TopKReserveArgs.Builder.width(2000L).depth(7L).decay(0.925))\n                    .doOnNext(res1 -> {\n                        System.out.println(res1);\n                        // >>> OK\n                    })\n                    .thenMany(reactiveCommands.topKAdd(\"bikes:keywords\",\n                            \"store\", \"seat\", \"handlebars\", \"handles\", \"pedals\",\n                            \"tires\", \"store\", \"seat\"))\n                    .collectList()\n                    .doOnNext(res2 -> {\n                        System.out.println(res2);\n                        // >>> [Value.empty, Value.empty, Value.empty, Value.empty, Value.empty, Value[handlebars], Value.empty, Value.empty]\n                    })\n                    .thenMany(reactiveCommands.topKList(\"bikes:keywords\"))\n                    .collectList()\n                    .doOnNext(res3 -> {\n                        System.out.println(res3);\n                        // >>> [store, seat, pedals, tires, handles]\n                    })\n                    .thenMany(reactiveCommands.topKQuery(\"bikes:keywords\", \"store\", \"handlebars\"))\n                    .collectList()\n                    .doOnNext(res4 -> {\n                        System.out.println(res4);\n                        // >>> [true, false]\n                    })\n                    .then();\n\n            topKExample.block();","section_id":"example"},{"id":"example-ex6","language":"java","code":"String res1 = jedis.topkReserve(\"bikes:keywords\", 5L, 2000L, 7L, 0.925D);\n        System.out.println(res1); // >>> True\n\n        List<String> res2 = jedis.topkAdd(\"bikes:keywords\",\n                \"store\",\n                \"seat\",\n                \"handlebars\",\n                \"handles\",\n                \"pedals\",\n                \"tires\",\n                \"store\",\n                \"seat\");\n\n        System.out.println(res2); // >>> [None, None, None, None, None, 'handlebars', None, None]\n\n        List<String> res3 = jedis.topkList(\"bikes:keywords\");\n        System.out.println(res3); // >>> ['store', 'seat', 'pedals', 'tires', 'handles']\n\n        List<Boolean> res4 = jedis.topkQuery(\"bikes:keywords\", \"store\", \"handlebars\");\n        System.out.println(res4); // >>> [1, 0]","section_id":"example"},{"id":"example-ex7","language":"javascript","code":"const res1 = await client.topK.reserve('bikes:keywords', 5, {\n    width: 2000,\n    depth: 7,\n    decay: 0.925\n});\nconsole.log(res1);  // >>> OK\n\nconst res2 = await client.topK.add('bikes:keywords', [\n  'store',\n  'seat',\n  'handlebars',\n  'handles',\n  'pedals',\n  'tires',\n  'store',\n  'seat'\n]);\nconsole.log(res2);  // >>> [null, null, null, null, null, 'handlebars', null, null]\n\nconst res3 = await client.topK.list('bikes:keywords');\nconsole.log(res3);  // >>> ['store', 'seat', 'pedals', 'tires', 'handles']\n\nconst res4 = await client.topK.query('bikes:keywords', ['store', 'handlebars']);\nconsole.log(res4);  // >>> [true, false]","section_id":"example"},{"id":"example-ex8","language":"php","code":"$res1 = $r->topkreserve('bikes:keywords', 5, 2000, 7, 0.925);\n        echo $res1 . PHP_EOL;\n        // >>> OK\n\n        $res2 = $r->topkadd(\n            'bikes:keywords',\n            'store',\n            'seat',\n            'handlebars',\n            'handles',\n            'pedals',\n            'tires',\n            'store',\n            'seat',\n        );\n        echo json_encode($res2) . PHP_EOL;\n        // >>> [null,null,null,null,null,\"handlebars\",null,null]\n\n        $res3 = $r->topklist('bikes:keywords');\n        echo json_encode($res3) . PHP_EOL;\n        // >>> [\"store\",\"seat\",\"pedals\",\"tires\",\"handles\"]\n\n        $res4 = $r->topkquery('bikes:keywords', 'store', 'handlebars');\n        echo json_encode($res4) . PHP_EOL;\n        // >>> [1,0]","section_id":"example"},{"id":"example-ex9","language":"python","code":"\"\"\"\nCode samples for Top-K pages:\n    https://redis.io/docs/latest/develop/data-types/probabilistic/top-k/\n\"\"\"\n\nimport redis\n\nr = redis.Redis(decode_responses=True)\n\n\nres1 = r.topk().reserve(\"bikes:keywords\", 5, 2000, 7, 0.925)\nprint(res1)  # >>> True\n\nres2 = r.topk().add(\n    \"bikes:keywords\",\n    \"store\",\n    \"seat\",\n    \"handlebars\",\n    \"handles\",\n    \"pedals\",\n    \"tires\",\n    \"store\",\n    \"seat\",\n)\nprint(res2)  # >>> [None, None, None, None, None, 'handlebars', None, None]\n\nres3 = r.topk().list(\"bikes:keywords\")\nprint(res3)  # >>> ['store', 'seat', 'pedals', 'tires', 'handles']\n\nres4 = r.topk().query(\"bikes:keywords\", \"store\", \"handlebars\")\nprint(res4)  # >>> [1, 0]","section_id":"example"},{"id":"sizing-ex0","language":"plaintext","code":"width = k*log(k)\ndepth =  log(k)  # but a minimum of 5","section_id":"sizing"}]}
{"schema_version":2,"id":"develop/data-types/sets","title":"Redis sets","url":"https://redis.io/docs/latest/develop/data-types/sets/","summary":"Introduction to Redis sets","aliases":["/data-types/sets/","/manual/data-types/sets/","/develop/data-types/set/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-10T15:45:53+01:00","page_type":"content","content_hash":"6d1f55beaf117976d4af09f44e24a619bc950197ae9ed5346d15780bf0f4ead6","sections":[{"id":"set-command-summary","title":"Set command summary","role":"content","text":"**19 commands in this group:**\n\n[View all set commands](https://redis.io/commands/?group=set)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [SADD](https://redis.io/commands/sadd/) | Adds one or more members to a set. Creates the key if it doesn't exist. | O(1) for each element added, so O(N) to add N e... | 1.0.0 |\n| [SCARD](https://redis.io/commands/scard/) | Returns the number of members in a set. | O(1) | 1.0.0 |\n| [SDIFF](https://redis.io/commands/sdiff/) | Returns the difference of multiple sets. | O(N) where N is the total number of elements in... | 1.0.0 |\n| [SDIFFCARD](https://redis.io/commands/sdiffcard/) | Returns the number of members of the difference between the first set and all successive sets. | O(N) where N is the total number of elements in... | 8.10.0 |\n| [SDIFFSTORE](https://redis.io/commands/sdiffstore/) | Stores the difference of multiple sets in a key. | O(N) where N is the total number of elements in... | 1.0.0 |\n| [SINTER](https://redis.io/commands/sinter/) | Returns the intersect of multiple sets. | O(N*M) worst case where N is the cardinality of... | 1.0.0 |\n| [SINTERCARD](https://redis.io/commands/sintercard/) | Returns the number of members of the intersect of multiple sets. | O(N*M) worst case where N is the cardinality of... | 7.0.0 |\n| [SINTERSTORE](https://redis.io/commands/sinterstore/) | Stores the intersect of multiple sets in a key. | O(N*M) worst case where N is the cardinality of... | 1.0.0 |\n| [SISMEMBER](https://redis.io/commands/sismember/) | Determines whether a member belongs to a set. | O(1) | 1.0.0 |\n| [SMEMBERS](https://redis.io/commands/smembers/) | Returns all members of a set. | O(N) where N is the set cardinality. | 1.0.0 |\n\n#### Set command summary (part 2)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [SMISMEMBER](https://redis.io/commands/smismember/) | Determines whether multiple members belong to a set. | O(N) where N is the number of elements being ch... | 6.2.0 |\n| [SMOVE](https://redis.io/commands/smove/) | Moves a member from one set to another. | O(1) | 1.0.0 |\n| [SPOP](https://redis.io/commands/spop/) | Returns one or more random members from a set after removing them. Deletes the set if the last member was popped. | Without the count argument O(1), otherwise O(N)... | 1.0.0 |\n| [SRANDMEMBER](https://redis.io/commands/srandmember/) | Get one or multiple random members from a set | Without the count argument O(1), otherwise O(N)... | 1.0.0 |\n| [SREM](https://redis.io/commands/srem/) | Removes one or more members from a set. Deletes the set if the last member was removed. | O(N) where N is the number of members to be rem... | 1.0.0 |\n| [SSCAN](https://redis.io/commands/sscan/) | Iterates over members of a set. | O(1) for every call. O(N) for a complete iterat... | 2.8.0 |\n| [SUNION](https://redis.io/commands/sunion/) | Returns the union of multiple sets. | O(N) where N is the total number of elements in... | 1.0.0 |\n| [SUNIONCARD](https://redis.io/commands/sunioncard/) | Returns the number of members of the union of multiple sets. | O(N) where N is the total number of elements in... | 8.10.0 |\n| [SUNIONSTORE](https://redis.io/commands/sunionstore/) | Stores the union of multiple sets in a key. | O(N) where N is the total number of elements in... | 1.0.0 |\n\n\n\nA Redis set is an unordered collection of unique strings (members).\nYou can use Redis sets to efficiently:\n\n* Track unique items (e.g., track all unique IP addresses accessing a given blog post).\n* Represent relations (e.g., the set of all users with a given role).\n* Perform common set operations such as intersection, unions, and differences."},{"id":"examples","title":"Examples","role":"example","text":"* Store the sets of bikes racing in France and the USA. Note that\nif you add a member that already exists, it will be ignored.\nFoundational: Add members to a set using SADD when you need to store unique items (duplicates are silently ignored)\n\n**Difficulty:** Beginner\n\n**Commands:** SADD\n\n**Complexity:**\n- SADD: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\n* Check whether bike:1 or bike:2 are racing in the US.\nFoundational: Test set membership using SISMEMBER when you need O(1) lookups (much faster than checking a list)\n\n**Difficulty:** Beginner\n\n**Builds upon:** sadd\n\n**Commands:** SISMEMBER\n\n**Complexity:**\n- SISMEMBER: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\n* Which bikes are competing in both races?\nSet intersection: Find common members across multiple sets using SINTER when you need to identify shared items (e.g., users in multiple groups)\n\n**Difficulty:** Intermediate\n\n**Builds upon:** sadd\n\n**Commands:** SINTER\n\n**Complexity:**\n- SINTER: O(N*M)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\n* How many bikes are racing in France?\nFoundational: Get the cardinality of a set using SCARD when you need to count unique items efficiently\n\n**Difficulty:** Beginner\n\n**Builds upon:** sadd\n\n**Commands:** SCARD\n\n**Complexity:**\n- SCARD: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"tutorial","title":"Tutorial","role":"content","text":"The [`SADD`](https://redis.io/docs/latest/commands/sadd) command adds new elements to a set. It's also possible\nto do a number of other operations against sets like testing if a given element\nalready exists, performing the intersection, union or difference between\nmultiple sets, and so forth.\n\nFoundational: Retrieve all members from a set using SMEMBERS when you need the complete set (note: order is not guaranteed)\n\n**Difficulty:** Beginner\n\n**Builds upon:** sadd\n\n**Commands:** DEL, SADD, SMEMBERS\n\n**Complexity:**\n- DEL: O(N)\n- SADD: O(1)\n- SMEMBERS: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nHere I've added three elements to my set and told Redis to return all the\nelements. There is no order guarantee with a set. Redis is free to return the\nelements in any order at every call.\n\nRedis has commands to test for set membership. These commands can be used on single as well as multiple items:\n\nBatch membership checks: Test multiple items at once using SMISMEMBER when you need to reduce round trips to the server\n\n**Difficulty:** Intermediate\n\n**Builds upon:** sismember\n\n**Commands:** DEL, SADD, SISMEMBER, SMISMEMBER\n\n**Complexity:**\n- DEL: O(N)\n- SADD: O(1)\n- SISMEMBER: O(1)\n- SMISMEMBER: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nWe can also find the difference between two sets. For instance, we may want\nto know which bikes are racing in France but not in the USA:\n\nSet difference: Find members in one set but not in others using SDIFF when you need to exclude items (warning: argument order matters)\n\n**Difficulty:** Intermediate\n\n**Builds upon:** sadd\n\n**Commands:** DEL, SADD, SDIFF\n\n**Complexity:**\n- DEL: O(N)\n- SADD: O(1)\n- SDIFF: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nThere are other non trivial operations that are still easy to implement\nusing the right Redis commands. For instance we may want a list of all the\nbikes racing in France, the USA, and some other races. We can do this using\nthe [`SINTER`](https://redis.io/docs/latest/commands/sinter) command, which performs the intersection between different\nsets. In addition to intersection you can also perform\nunions, difference, and more. For example \nif we add a third race we can see some of these commands in action:\n\nAdvanced pattern: Combine SINTER, SUNION, and SDIFF to analyze complex relationships when you need to precompute set algebra across multiple datasets\n\n**Difficulty:** Advanced\n\n**Builds upon:** sinter, sdiff\n\n**Commands:** DEL, SADD, SINTER, SUNION, SDIFF\n\n**Complexity:**\n- DEL: O(N)\n- SADD: O(1)\n- SINTER: O(N*M)\n- SUNION: O(N)\n- SDIFF: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nYou'll note that the [`SDIFF`](https://redis.io/docs/latest/commands/sdiff) command returns an empty array when the\ndifference between all sets is empty. You'll also note that the order of sets\npassed to [`SDIFF`](https://redis.io/docs/latest/commands/sdiff) matters, since the difference is not commutative.\n\nWhen you want to remove items from a set, you can use the [`SREM`](https://redis.io/docs/latest/commands/srem) command to\nremove one or more items from a set, or you can use the [`SPOP`](https://redis.io/docs/latest/commands/spop) command to\nremove a random item from a set. You can also _return_ a random item from a\nset without removing it using the [`SRANDMEMBER`](https://redis.io/docs/latest/commands/srandmember) command:\n\nRemoval strategies: Use SREM for targeted removal, SPOP for random removal, or SRANDMEMBER to inspect without modifying when you need flexible deletion patterns\n\n**Difficulty:** Intermediate\n\n**Builds upon:** sadd\n\n**Commands:** DEL, SADD, SREM, SPOP, SMEMBERS, SRANDMEMBER\n\n**Complexity:**\n- DEL: O(N)\n- SADD: O(1)\n- SREM: O(N)\n- SMEMBERS: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"limits","title":"Limits","role":"limits","text":"The max size of a Redis set is 2^32 - 1 (4,294,967,295) members."},{"id":"performance","title":"Performance","role":"performance","text":"Most set operations, including adding, removing, and checking whether an item is a set member, are O(1).\nThis means that they're highly efficient.\nHowever, for large sets with hundreds of thousands of members or more, you should exercise caution when running the [`SMEMBERS`](https://redis.io/docs/latest/commands/smembers) command.\nThis command is O(n) and returns the entire set in a single response. \nAs an alternative, consider the [`SSCAN`](https://redis.io/docs/latest/commands/sscan), which lets you retrieve all members of a set iteratively."},{"id":"alternatives","title":"Alternatives","role":"content","text":"Sets membership checks on large datasets (or on streaming data) can use a lot of memory.\nIf you're concerned about memory usage and don't need perfect precision, consider a [Bloom filter or Cuckoo filter](https://redis.io/docs/latest/develop/data-types/probabilistic/bloom-filter) as an alternative to a set.\n\nRedis sets are frequently used as a kind of index.\nIf you need to index and query your data, consider the [JSON](https://redis.io/docs/latest/develop/data-types/json) data type and the [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) features."},{"id":"learn-more","title":"Learn more","role":"related","text":"* [Redis Sets Explained](https://www.youtube.com/watch?v=PKdCppSNTGQ) and [Redis Sets Elaborated](https://www.youtube.com/watch?v=aRw5ME_5kMY) are two short but thorough video explainers covering Redis sets.\n* [Redis University's RU101](https://university.redis.com/courses/ru101/) explores Redis sets in detail."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> SADD bikes:racing:france bike:1\n(integer) 1\n> SADD bikes:racing:france bike:1\n(integer) 0\n> SADD bikes:racing:france bike:2 bike:3\n(integer) 2\n> SADD bikes:racing:usa bike:1 bike:4\n(integer) 2","section_id":"examples"},{"id":"examples-ex1","language":"csharp","code":"long res1 = db.SetAdd(\"bikes:racing:france\", [\"bike:1\"]);\n        Console.WriteLine(res1);    // >>> 1\n\n        long res2 = db.SetAdd(\"bikes:racing:france\", [\"bike:1\"]);\n        Console.WriteLine(res2);    // >>> 0\n\n        long res3 = db.SetAdd(\"bikes:racing:france\", [\"bike:2\", \"bike:3\"]);\n        Console.WriteLine(res3);    // >>> 2\n\n        long res4 = db.SetAdd(\"bikes:racing:usa\", [\"bike:1\", \"bike:4\"]);\n        Console.WriteLine(res4);    // >>> 2","section_id":"examples"},{"id":"examples-ex2","language":"go","code":"res1, err := rdb.SAdd(ctx, \"bikes:racing:france\", \"bike:1\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> 1\n\n\tres2, err := rdb.SAdd(ctx, \"bikes:racing:france\", \"bike:1\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> 0\n\n\tres3, err := rdb.SAdd(ctx, \"bikes:racing:france\", \"bike:2\", \"bike:3\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3) // >>> 2\n\n\tres4, err := rdb.SAdd(ctx, \"bikes:racing:usa\", \"bike:1\", \"bike:4\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4) // >>> 2","section_id":"examples"},{"id":"examples-ex3","language":"java","code":"CompletableFuture<Void> sAdd = asyncCommands.sadd(\"bikes:racing:france\", \"bike:1\").thenCompose(res1 -> {\n                System.out.println(res1); // >>> 1\n                return asyncCommands.sadd(\"bikes:racing:france\", \"bike:1\");\n            }).thenCompose(res2 -> {\n                System.out.println(res2); // >>> 0\n                return asyncCommands.sadd(\"bikes:racing:france\", \"bike:2\", \"bike:3\");\n            }).thenCompose(res3 -> {\n                System.out.println(res3); // >>> 2\n                return asyncCommands.sadd(\"bikes:racing:usa\", \"bike:1\", \"bike:4\");\n            }).thenAccept(res4 -> {\n                System.out.println(res4); // >>> 2\n            }).toCompletableFuture();","section_id":"examples"},{"id":"examples-ex4","language":"java","code":"Mono<Void> sAdd = reactiveCommands.sadd(\"bikes:racing:france\", \"bike:1\").doOnNext(result -> {\n                System.out.println(result); // >>> 1\n            }).flatMap(v -> reactiveCommands.sadd(\"bikes:racing:france\", \"bike:1\")).doOnNext(result -> {\n                System.out.println(result); // >>> 0\n            }).flatMap(v -> reactiveCommands.sadd(\"bikes:racing:france\", \"bike:2\", \"bike:3\")).doOnNext(result -> {\n                System.out.println(result); // >>> 2\n            }).flatMap(v -> reactiveCommands.sadd(\"bikes:racing:usa\", \"bike:1\", \"bike:4\")).doOnNext(result -> {\n                System.out.println(result); // >>> 2\n            }).then();","section_id":"examples"},{"id":"examples-ex5","language":"java","code":"long res1 = jedis.sadd(\"bikes:racing:france\", \"bike:1\");\n        System.out.println(res1);  // >>> 1\n\n        long res2 = jedis.sadd(\"bikes:racing:france\", \"bike:1\");\n        System.out.println(res2);  // >>> 0\n\n        long res3 = jedis.sadd(\"bikes:racing:france\", \"bike:2\", \"bike:3\");\n        System.out.println(res3);  // >>> 2\n\n        long res4 = jedis.sadd(\"bikes:racing:usa\", \"bike:1\", \"bike:4\");\n        System.out.println(res4);  // >>> 2","section_id":"examples"},{"id":"examples-ex6","language":"javascript","code":"const res1 = await client.sAdd('bikes:racing:france', 'bike:1')\nconsole.log(res1)  // >>> 1\n\nconst res2 = await client.sAdd('bikes:racing:france', 'bike:1')\nconsole.log(res2)  // >>> 0\nconst res3 = await client.sAdd('bikes:racing:france', ['bike:2', 'bike:3'])\nconsole.log(res3)  // >>> 2\nconst res4 = await client.sAdd('bikes:racing:usa', ['bike:1', 'bike:4'])\nconsole.log(res4)  // >>> 2","section_id":"examples"},{"id":"examples-ex7","language":"php","code":"$res1 = $r->sadd('bikes:racing:france', ['bike:1']);\n        echo $res1 . PHP_EOL;\n        // >>> 1\n\n        $res2 = $r->sadd('bikes:racing:france', ['bike:1']);\n        echo $res2 . PHP_EOL;\n        // >>> 0\n\n        $res3 = $r->sadd('bikes:racing:france', ['bike:2', 'bike:3']);\n        echo $res3 . PHP_EOL;\n        // >>> 2\n\n        $res4 = $r->sadd('bikes:racing:usa', ['bike:1', 'bike:4']);\n        echo $res4 . PHP_EOL;\n        // >>> 2","section_id":"examples"},{"id":"examples-ex8","language":"python","code":"res1 = r.sadd(\"bikes:racing:france\", \"bike:1\")\nprint(res1)  # >>> 1\n\nres2 = r.sadd(\"bikes:racing:france\", \"bike:1\")\nprint(res2)  # >>> 0\n\nres3 = r.sadd(\"bikes:racing:france\", \"bike:2\", \"bike:3\")\nprint(res3)  # >>> 2\n\nres4 = r.sadd(\"bikes:racing:usa\", \"bike:1\", \"bike:4\")\nprint(res4)  # >>> 2","section_id":"examples"},{"id":"examples-ex9","language":"ruby","code":"res1 = r.sadd('bikes:racing:france', ['bike:1'])\nputs res1 # >>> 1\n\nres2 = r.sadd('bikes:racing:france', ['bike:1'])\nputs res2 # >>> 0\n\nres3 = r.sadd('bikes:racing:france', ['bike:2', 'bike:3'])\nputs res3 # >>> 2\n\nres4 = r.sadd('bikes:racing:usa', ['bike:1', 'bike:4'])\nputs res4 # >>> 2","section_id":"examples"},{"id":"examples-ex10","language":"rust","code":"if let Ok(res) = r.sadd(\"bikes:racing:france\", \"bike:1\").await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 1\n        }\n\n        if let Ok(res) = r.sadd(\"bikes:racing:france\", \"bike:1\").await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 0\n        }\n\n        if let Ok(res) = r.sadd(\"bikes:racing:france\", &[\"bike:2\", \"bike:3\"]).await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 2\n        }\n\n        if let Ok(res) = r.sadd(\"bikes:racing:usa\", &[\"bike:1\", \"bike:4\"]).await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 2\n        }","section_id":"examples"},{"id":"examples-ex11","language":"rust","code":"if let Ok(res) = r.sadd(\"bikes:racing:france\", \"bike:1\") {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 1\n        }\n\n        if let Ok(res) = r.sadd(\"bikes:racing:france\", \"bike:1\") {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 0\n        }\n\n        if let Ok(res) = r.sadd(\"bikes:racing:france\", &[\"bike:2\", \"bike:3\"]) {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 2\n        }\n\n        if let Ok(res) = r.sadd(\"bikes:racing:usa\", &[\"bike:1\", \"bike:4\"]) {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 2\n        }","section_id":"examples"},{"id":"examples-ex12","language":"plaintext","code":"> SISMEMBER bikes:racing:usa bike:1\n(integer) 1\n> SISMEMBER bikes:racing:usa bike:2\n(integer) 0","section_id":"examples"},{"id":"examples-ex13","language":"csharp","code":"bool res5 = db.SetContains(\"bikes:racing:france\", \"bike:1\");\n        Console.WriteLine(res5);    // >>> True\n\n        bool res6 = db.SetContains(\"bikes:racing:usa\", \"bike:2\");\n        Console.WriteLine(res6);    // >>> False","section_id":"examples"},{"id":"examples-ex14","language":"go","code":"res5, err := rdb.SIsMember(ctx, \"bikes:racing:usa\", \"bike:1\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res5) // >>> true\n\n\tres6, err := rdb.SIsMember(ctx, \"bikes:racing:usa\", \"bike:2\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res6) // >>> false","section_id":"examples"},{"id":"examples-ex15","language":"java","code":"CompletableFuture<Void> sIsMember = asyncCommands.sismember(\"bikes:racing:usa\", \"bike:1\").thenCompose(res5 -> {\n                System.out.println(res5); // >>> true\n                return asyncCommands.sismember(\"bikes:racing:usa\", \"bike:2\");\n            }).thenAccept(res6 -> {\n                System.out.println(res6); // >>> false\n            }).toCompletableFuture();","section_id":"examples"},{"id":"examples-ex16","language":"java","code":"Mono<Boolean> sIsMember1 = reactiveCommands.sismember(\"bikes:racing:usa\", \"bike:1\").doOnNext(result -> {\n                System.out.println(result); // >>> true\n            });\n\n            Mono<Boolean> sIsMember2 = reactiveCommands.sismember(\"bikes:racing:usa\", \"bike:2\").doOnNext(result -> {\n                System.out.println(result); // >>> false\n            });","section_id":"examples"},{"id":"examples-ex17","language":"java","code":"jedis.sadd(\"bikes:racing:france\", \"bike:1\", \"bike:2\", \"bike:3\");\n        jedis.sadd(\"bikes:racing:usa\", \"bike:1\", \"bike:4\");\n\n        boolean res5 = jedis.sismember(\"bikes:racing:usa\", \"bike:1\");\n        System.out.println(res5);  // >>> true\n\n        boolean res6 = jedis.sismember(\"bikes:racing:usa\", \"bike:2\");\n        System.out.println(res6);  // >>> false","section_id":"examples"},{"id":"examples-ex18","language":"javascript","code":"await client.del('bikes:racing:france')\nawait client.del('bikes:racing:usa')\nawait client.sAdd('bikes:racing:france', ['bike:1', 'bike:2', 'bike:3'])\nawait client.sAdd('bikes:racing:usa', ['bike:1', 'bike:4'])\nconst res5 = await client.sIsMember('bikes:racing:usa', 'bike:1')\nconsole.log(res5)  // >>> 1\n\nconst res6 = await client.sIsMember('bikes:racing:usa', 'bike:2')\nconsole.log(res6)  // >>> 0","section_id":"examples"},{"id":"examples-ex19","language":"php","code":"$res5 = $r->sismember('bikes:racing:usa', 'bike:1');\n        echo $res5 . PHP_EOL;\n        // >>> 1\n\n        $res6 = $r->sismember('bikes:racing:usa', 'bike:2');\n        echo $res6 . PHP_EOL;\n        // >>> 0","section_id":"examples"},{"id":"examples-ex20","language":"python","code":"r.sadd(\"bikes:racing:france\", \"bike:1\", \"bike:2\", \"bike:3\")\nr.sadd(\"bikes:racing:usa\", \"bike:1\", \"bike:4\")\nres5 = r.sismember(\"bikes:racing:usa\", \"bike:1\")\nprint(res5)  # >>> 1\n\nres6 = r.sismember(\"bikes:racing:usa\", \"bike:2\")\nprint(res6)  # >>> 0","section_id":"examples"},{"id":"examples-ex21","language":"ruby","code":"r.del('bikes:racing:france', 'bikes:racing:usa')\nr.sadd('bikes:racing:france', ['bike:1', 'bike:2', 'bike:3'])\nr.sadd('bikes:racing:usa', ['bike:1', 'bike:4'])\nres5 = r.sismember('bikes:racing:usa', 'bike:1')\nputs res5 # >>> true\n\nres6 = r.sismember('bikes:racing:usa', 'bike:2')\nputs res6 # >>> false","section_id":"examples"},{"id":"examples-ex22","language":"rust","code":"if let Ok(res) = r.sismember(\"bikes:racing:usa\", \"bike:1\").await {\n            let res: bool = res;\n            println!(\"{}\", i32::from(res)); // >>> 1\n        }\n\n        if let Ok(res) = r.sismember(\"bikes:racing:usa\", \"bike:2\").await {\n            let res: bool = res;\n            println!(\"{}\", i32::from(res)); // >>> 0\n        }","section_id":"examples"},{"id":"examples-ex23","language":"rust","code":"if let Ok(res) = r.sismember(\"bikes:racing:usa\", \"bike:1\") {\n            let res: bool = res;\n            println!(\"{}\", i32::from(res)); // >>> 1\n        }\n\n        if let Ok(res) = r.sismember(\"bikes:racing:usa\", \"bike:2\") {\n            let res: bool = res;\n            println!(\"{}\", i32::from(res)); // >>> 0\n        }","section_id":"examples"},{"id":"examples-ex24","language":"plaintext","code":"> SINTER bikes:racing:france bikes:racing:usa\n1) \"bike:1\"","section_id":"examples"},{"id":"examples-ex25","language":"csharp","code":"long res7 = db.SetAdd(\"{bikes:racing}:france\", [\"bike:1\", \"bike:2\", \"bike:3\"]);\n        long res8 = db.SetAdd(\"{bikes:racing}:usa\", [\"bike:1\", \"bike:4\"]);\n\n        RedisValue[] res9 = db.SetCombine(SetOperation.Intersect, [\"{bikes:racing}:france\", \"{bikes:racing}:usa\"]);\n        Console.WriteLine(string.Join(\", \", res9)); // >>> bike:1","section_id":"examples"},{"id":"examples-ex26","language":"go","code":"res7, err := rdb.SInter(ctx, \"bikes:racing:france\", \"bikes:racing:usa\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res7) // >>> [bike:1]","section_id":"examples"},{"id":"examples-ex27","language":"java","code":"CompletableFuture<Void> sInter = asyncCommands.sinter(\"bikes:racing:france\", \"bikes:racing:usa\")\n                    .thenAccept(res7 -> {\n                        System.out.println(res7); // >>> [bike:1]\n                    }).toCompletableFuture();","section_id":"examples"},{"id":"examples-ex28","language":"java","code":"Mono<List<String>> sInter = reactiveCommands.sinter(\"bikes:racing:france\", \"bikes:racing:usa\").collectList()\n                    .doOnNext(result -> {\n                        System.out.println(result); // >>> [bike:1]\n                    });","section_id":"examples"},{"id":"examples-ex29","language":"java","code":"jedis.sadd(\"bikes:racing:france\", \"bike:1\", \"bike:2\", \"bike:3\");\n        jedis.sadd(\"bikes:racing:usa\", \"bike:1\", \"bike:4\");\n\n        Set<String> res7 = jedis.sinter(\"bikes:racing:france\", \"bikes:racing:usa\");\n        System.out.println(res7);  // >>> [bike:1]","section_id":"examples"},{"id":"examples-ex30","language":"javascript","code":"await client.del('bikes:racing:france')\nawait client.del('bikes:racing:usa')\nawait client.sAdd('bikes:racing:france', ['bike:1', 'bike:2', 'bike:3'])\nawait client.sAdd('bikes:racing:usa', ['bike:1', 'bike:4'])\nconst res7 = await client.sInter(['bikes:racing:france', 'bikes:racing:usa'])\nconsole.log(res7)  // >>> ['bike:1']","section_id":"examples"},{"id":"examples-ex31","language":"php","code":"$res7 = $r->sinter(['bikes:racing:france', 'bikes:racing:usa']);\n        echo json_encode($res7) . PHP_EOL;\n        // >>> [\"bike:1\"]","section_id":"examples"},{"id":"examples-ex32","language":"python","code":"r.sadd(\"bikes:racing:france\", \"bike:1\", \"bike:2\", \"bike:3\")\nr.sadd(\"bikes:racing:usa\", \"bike:1\", \"bike:4\")\nres7 = r.sinter(\"bikes:racing:france\", \"bikes:racing:usa\")\nprint(res7)  # >>> {'bike:1'}","section_id":"examples"},{"id":"examples-ex33","language":"ruby","code":"r.del('bikes:racing:france', 'bikes:racing:usa')\nr.sadd('bikes:racing:france', ['bike:1', 'bike:2', 'bike:3'])\nr.sadd('bikes:racing:usa', ['bike:1', 'bike:4'])\nres7 = r.sinter('bikes:racing:france', 'bikes:racing:usa')\nputs res7.inspect # >>> [\"bike:1\"]","section_id":"examples"},{"id":"examples-ex34","language":"rust","code":"if let Ok(res) = r.sinter(&[\"bikes:racing:france\", \"bikes:racing:usa\"]).await {\n            let res: HashSet<String> = res;\n            let res = sorted_set(res);\n            println!(\"{res:?}\"); // >>> [\"bike:1\"]\n        }","section_id":"examples"},{"id":"examples-ex35","language":"rust","code":"if let Ok(res) = r.sinter(&[\"bikes:racing:france\", \"bikes:racing:usa\"]) {\n            let res: HashSet<String> = res;\n            let res = sorted_set(res);\n            println!(\"{res:?}\"); // >>> [\"bike:1\"]\n        }","section_id":"examples"},{"id":"examples-ex36","language":"plaintext","code":"> SCARD bikes:racing:france\n(integer) 3","section_id":"examples"},{"id":"examples-ex37","language":"csharp","code":"long res10 = db.SetAdd(\"bikes:racing:france\", [\"bike:1\", \"bike:2\", \"bike:3\"]);\n        long res11 = db.SetLength(\"bikes:racing:france\");\n        Console.WriteLine(res11);   // >>> 3","section_id":"examples"},{"id":"examples-ex38","language":"go","code":"res8, err := rdb.SCard(ctx, \"bikes:racing:france\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res8) // >>> 3","section_id":"examples"},{"id":"examples-ex39","language":"java","code":"CompletableFuture<Void> sCard = asyncCommands.scard(\"bikes:racing:france\").thenAccept(res8 -> {\n                System.out.println(res8); // >>> 3\n            }).toCompletableFuture();","section_id":"examples"},{"id":"examples-ex40","language":"java","code":"Mono<Long> sCard = reactiveCommands.scard(\"bikes:racing:france\").doOnNext(result -> {\n                System.out.println(result); // >>> 3\n            });","section_id":"examples"},{"id":"examples-ex41","language":"java","code":"jedis.sadd(\"bikes:racing:france\", \"bike:1\", \"bike:2\", \"bike:3\");\n\n        long res8 = jedis.scard(\"bikes:racing:france\");\n        System.out.println(res8);  // >>> 3","section_id":"examples"},{"id":"examples-ex42","language":"javascript","code":"await client.del('bikes:racing:france')\nawait client.sAdd('bikes:racing:france', ['bike:1', 'bike:2', 'bike:3'])\nconst res8 = await client.sCard('bikes:racing:france')\nconsole.log(res8)  // >>> 3","section_id":"examples"},{"id":"examples-ex43","language":"php","code":"$res8 = $r->scard('bikes:racing:france');\n        echo $res8 . PHP_EOL;\n        // >>> 3","section_id":"examples"},{"id":"examples-ex44","language":"python","code":"r.sadd(\"bikes:racing:france\", \"bike:1\", \"bike:2\", \"bike:3\")\nres8 = r.scard(\"bikes:racing:france\")\nprint(res8)  # >>> 3","section_id":"examples"},{"id":"examples-ex45","language":"ruby","code":"r.del('bikes:racing:france')\nr.sadd('bikes:racing:france', ['bike:1', 'bike:2', 'bike:3'])\nres8 = r.scard('bikes:racing:france')\nputs res8 # >>> 3","section_id":"examples"},{"id":"examples-ex46","language":"rust","code":"if let Ok(res) = r.scard(\"bikes:racing:france\").await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 3\n        }","section_id":"examples"},{"id":"examples-ex47","language":"rust","code":"if let Ok(res) = r.scard(\"bikes:racing:france\") {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 3\n        }","section_id":"examples"},{"id":"tutorial-ex0","language":"plaintext","code":"> DEL bikes:racing:france\n(integer) 1\n> SADD bikes:racing:france bike:1 bike:2 bike:3\n(integer) 3\n> SMEMBERS bikes:racing:france\n1) \"bike:3\"\n2) \"bike:1\"\n3) \"bike:2\"","section_id":"tutorial"},{"id":"tutorial-ex1","language":"csharp","code":"db.KeyDelete(\"bikes:racing:france\");\n\n        long res12 = db.SetAdd(\"bikes:racing:france\", [\"bike:1\", \"bike:2\", \"bike:3\"]);\n        RedisValue[] res13 = db.SetMembers(\"bikes:racing:france\");\n        Console.WriteLine(string.Join(\", \", res13));    // >>> bike:3, bike:2, bike:1","section_id":"tutorial"},{"id":"tutorial-ex2","language":"go","code":"rdb.Del(ctx, \"bikes:racing:france\")\n\n\tres9, err := rdb.SAdd(ctx, \"bikes:racing:france\", \"bike:1\", \"bike:2\", \"bike:3\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res9) // >>> 3\n\n\tres10, err := rdb.SMembers(ctx, \"bikes:racing:france\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Sort the strings in the slice to make sure the output is lexicographical\n\tsort.Strings(res10)\n\n\tfmt.Println(res10) // >>> [bike:1 bike:2 bike:3]","section_id":"tutorial"},{"id":"tutorial-ex3","language":"java","code":"CompletableFuture<Void> sAddSMembers = asyncCommands.sadd(\"bikes:racing:france\", \"bike:1\", \"bike:2\", \"bike:3\")\n                    .thenCompose(res9 -> {\n                        System.out.println(res9); // >>> 3\n                        return asyncCommands.smembers(\"bikes:racing:france\");\n                    }).thenAccept(res10 -> {\n                        System.out.println(res10); // >>> [bike:1, bike:2, bike:3]\n                    }).toCompletableFuture();","section_id":"tutorial"},{"id":"tutorial-ex4","language":"java","code":"Mono<Void> sAddSMembers = reactiveCommands.sadd(\"bikes:racing:france\", \"bike:1\", \"bike:2\", \"bike:3\")\n                    .doOnNext(result -> {\n                        System.out.println(result); // >>> 3\n                    }).flatMap(v -> reactiveCommands.smembers(\"bikes:racing:france\").collectList()).doOnNext(result -> {\n                        System.out.println(result); // >>> [bike:1, bike:2, bike:3]\n                    }).then();","section_id":"tutorial"},{"id":"tutorial-ex5","language":"java","code":"jedis.del(\"bikes:racing:france\");\n\n        long res9 = jedis.sadd(\"bikes:racing:france\", \"bike:1\", \"bike:2\", \"bike:3\");\n        System.out.println(res9);  // >>> 3\n\n        Set<String> res10 = jedis.smembers(\"bikes:racing:france\");\n        System.out.println(res10);  // >>> [bike:1, bike:2, bike:3]","section_id":"tutorial"},{"id":"tutorial-ex6","language":"javascript","code":"await client.del('bikes:racing:france')\n\nconst res9 = await client.sAdd('bikes:racing:france', ['bike:1', 'bike:2', 'bike:3'])\nconsole.log(res9)  // >>> 3\n\nconst res10 = await client.sMembers('bikes:racing:france')\nconsole.log(res10)  // >>> ['bike:1', 'bike:2', 'bike:3']","section_id":"tutorial"},{"id":"tutorial-ex7","language":"php","code":"$r->del('bikes:racing:france');\n\n        $res9 = $r->sadd('bikes:racing:france', ['bike:1', 'bike:2', 'bike:3']);\n        echo $res9 . PHP_EOL;\n        // >>> 3\n\n        $res10 = $r->smembers('bikes:racing:france');\n        sort($res10);\n        echo json_encode($res10) . PHP_EOL;\n        // >>> [\"bike:1\",\"bike:2\",\"bike:3\"]","section_id":"tutorial"},{"id":"tutorial-ex8","language":"python","code":"r.delete(\"bikes:racing:france\")\n\nres9 = r.sadd(\"bikes:racing:france\", \"bike:1\", \"bike:2\", \"bike:3\")\nprint(res9)  # >>> 3\n\nres10 = r.smembers(\"bikes:racing:france\")\nprint(res10)  # >>> {'bike:1', 'bike:2', 'bike:3'}","section_id":"tutorial"},{"id":"tutorial-ex9","language":"ruby","code":"r.del('bikes:racing:france')\n\nres9 = r.sadd('bikes:racing:france', ['bike:1', 'bike:2', 'bike:3'])\nputs res9 # >>> 3\n\nres10 = r.smembers('bikes:racing:france')\nputs res10.sort.inspect # >>> [\"bike:1\", \"bike:2\", \"bike:3\"]","section_id":"tutorial"},{"id":"tutorial-ex10","language":"rust","code":"let _: usize = r.del(\"bikes:racing:france\").await.unwrap_or(0);\n\n        if let Ok(res) = r.sadd(\"bikes:racing:france\", &[\"bike:1\", \"bike:2\", \"bike:3\"]).await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 3\n        }\n\n        if let Ok(res) = r.smembers(\"bikes:racing:france\").await {\n            let res: HashSet<String> = res;\n            let res = sorted_set(res);\n            println!(\"{res:?}\"); // >>> [\"bike:1\", \"bike:2\", \"bike:3\"]\n        }","section_id":"tutorial"},{"id":"tutorial-ex11","language":"rust","code":"let _: usize = r.del(\"bikes:racing:france\").unwrap_or(0);\n\n        if let Ok(res) = r.sadd(\"bikes:racing:france\", &[\"bike:1\", \"bike:2\", \"bike:3\"]) {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 3\n        }\n\n        if let Ok(res) = r.smembers(\"bikes:racing:france\") {\n            let res: HashSet<String> = res;\n            let res = sorted_set(res);\n            println!(\"{res:?}\"); // >>> [\"bike:1\", \"bike:2\", \"bike:3\"]\n        }","section_id":"tutorial"},{"id":"tutorial-ex12","language":"plaintext","code":"# Recreate the France racing set so this example runs on its own.\n> DEL bikes:racing:france\n(integer) 1\n> SADD bikes:racing:france bike:1 bike:2 bike:3\n(integer) 3\n> SISMEMBER bikes:racing:france bike:1\n(integer) 1\n> SMISMEMBER bikes:racing:france bike:2 bike:3 bike:4\n1) (integer) 1\n2) (integer) 1\n3) (integer) 0","section_id":"tutorial"},{"id":"tutorial-ex13","language":"csharp","code":"// Recreate the set so this example runs on its own.\n        db.KeyDelete(\"bikes:racing:france\");\n        db.SetAdd(\"bikes:racing:france\", [\"bike:1\", \"bike:2\", \"bike:3\"]);\n\n        bool res14 = db.SetContains(\"bikes:racing:france\", \"bike:1\");\n        Console.WriteLine(res14);   // >>> true\n\n        bool[] res15 = db.SetContains(\"bikes:racing:france\", [\"bike:2\", \"bike:3\", \"bike:4\"]);\n        Console.WriteLine(string.Join(\", \", res15));    // >>> True, True, False","section_id":"tutorial"},{"id":"tutorial-ex14","language":"go","code":"res11, err := rdb.SIsMember(ctx, \"bikes:racing:france\", \"bike:1\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res11) // >>> true\n\n\tres12, err := rdb.SMIsMember(ctx, \"bikes:racing:france\", \"bike:2\", \"bike:3\", \"bike:4\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res12) // >>> [true true false]","section_id":"tutorial"},{"id":"tutorial-ex15","language":"java","code":"// Recreate the set so this example runs on its own. The chain is\n            // ordered, so the DEL + SADD complete before the reads below.\n            CompletableFuture<Void> sMIsMember = asyncCommands.del(\"bikes:racing:france\")\n                    .thenCompose(d -> asyncCommands.sadd(\"bikes:racing:france\", \"bike:1\", \"bike:2\", \"bike:3\"))\n                    .thenCompose(a -> asyncCommands.sismember(\"bikes:racing:france\", \"bike:1\")).thenCompose(res11 -> {\n                        System.out.println(res11); // >>> true\n                        return asyncCommands.smismember(\"bikes:racing:france\", \"bike:2\", \"bike:3\", \"bike:4\");\n                    }).thenAccept(res12 -> {\n                        System.out.println(res12); // >>> [true, true, false]\n                    }).toCompletableFuture();","section_id":"tutorial"},{"id":"tutorial-ex16","language":"java","code":"// Recreate the set so this example runs on its own. Block it\n            // sequentially: a DEL inside the shared Mono.when() below would\n            // race the sibling reads on the same key.\n            reactiveCommands.del(\"bikes:racing:france\")\n                    .then(reactiveCommands.sadd(\"bikes:racing:france\", \"bike:1\", \"bike:2\", \"bike:3\"))\n                    .block();\n\n            Mono<Boolean> sIsMember3 = reactiveCommands.sismember(\"bikes:racing:france\", \"bike:1\").doOnNext(result -> {\n                System.out.println(result); // >>> true\n            });\n\n            Mono<List<Boolean>> sMIsMember = reactiveCommands.smismember(\"bikes:racing:france\", \"bike:2\", \"bike:3\", \"bike:4\")\n                    .collectList().doOnNext(result -> {\n                        System.out.println(result); // >>> [true, true, false]\n                    });","section_id":"tutorial"},{"id":"tutorial-ex17","language":"java","code":"// Recreate the set so this example runs on its own.\n        jedis.del(\"bikes:racing:france\");\n        jedis.sadd(\"bikes:racing:france\", \"bike:1\", \"bike:2\", \"bike:3\");\n\n        boolean res11 = jedis.sismember(\"bikes:racing:france\", \"bike:1\");\n        System.out.println(res11);  // >>> true\n\n        List<Boolean> res12 = jedis.smismember(\"bikes:racing:france\", \"bike:2\", \"bike:3\", \"bike:4\");\n        System.out.println(res12);  // >>> [true,true,false]","section_id":"tutorial"},{"id":"tutorial-ex18","language":"javascript","code":"// Recreate the set so this example runs on its own.\nawait client.del('bikes:racing:france')\nawait client.sAdd('bikes:racing:france', ['bike:1', 'bike:2', 'bike:3'])\n\nconst res11 = await client.sIsMember('bikes:racing:france', 'bike:1')\nconsole.log(res11)  // >>> 1\n\nconst res12 = await client.smIsMember('bikes:racing:france', ['bike:2', 'bike:3', 'bike:4'])\nconsole.log(res12)  // >>> [1, 1, 0]","section_id":"tutorial"},{"id":"tutorial-ex19","language":"php","code":"// Recreate the set so this example runs on its own.\n        $r->del('bikes:racing:france');\n        $r->sadd('bikes:racing:france', 'bike:1', 'bike:2', 'bike:3');\n\n        $res11 = $r->sismember('bikes:racing:france', 'bike:1');\n        echo $res11 . PHP_EOL;\n        // >>> 1\n\n        $res12 = $r->smismember('bikes:racing:france', 'bike:2', 'bike:3', 'bike:4');\n        echo json_encode($res12) . PHP_EOL;\n        // >>> [1,1,0]","section_id":"tutorial"},{"id":"tutorial-ex20","language":"python","code":"# Recreate the set so this example runs on its own.\nr.delete(\"bikes:racing:france\")\nr.sadd(\"bikes:racing:france\", \"bike:1\", \"bike:2\", \"bike:3\")\n\nres11 = r.sismember(\"bikes:racing:france\", \"bike:1\")\nprint(res11)  # >>> 1\n\nres12 = r.smismember(\"bikes:racing:france\", \"bike:2\", \"bike:3\", \"bike:4\")\nprint(res12)  # >>> [1, 1, 0]","section_id":"tutorial"},{"id":"tutorial-ex21","language":"ruby","code":"# Recreate the set so this example runs on its own.\nr.del('bikes:racing:france')\nr.sadd('bikes:racing:france', ['bike:1', 'bike:2', 'bike:3'])\n\nres11 = r.sismember('bikes:racing:france', 'bike:1')\nputs res11 # >>> true\n\nres12 = r.smismember('bikes:racing:france', 'bike:2', 'bike:3', 'bike:4')\nputs res12.inspect # >>> [true, true, false]","section_id":"tutorial"},{"id":"tutorial-ex22","language":"rust","code":"// Recreate the set so this example runs on its own.\n        let _: usize = r.del(\"bikes:racing:france\").await.unwrap_or(0);\n        let _: usize = r\n            .sadd(\"bikes:racing:france\", &[\"bike:1\", \"bike:2\", \"bike:3\"])\n            .await\n            .unwrap_or(0);\n\n        if let Ok(res) = r.sismember(\"bikes:racing:france\", \"bike:1\").await {\n            let res: bool = res;\n            println!(\"{}\", i32::from(res)); // >>> 1\n        }\n\n        if let Ok(res) = r\n            .smismember(\"bikes:racing:france\", &[\"bike:2\", \"bike:3\", \"bike:4\"])\n            .await\n        {\n            let res: Vec<bool> = res;\n            let res: Vec<i32> = res.into_iter().map(i32::from).collect();\n            println!(\"{res:?}\"); // >>> [1, 1, 0]\n        }","section_id":"tutorial"},{"id":"tutorial-ex23","language":"rust","code":"// Recreate the set so this example runs on its own.\n        let _: usize = r.del(\"bikes:racing:france\").unwrap_or(0);\n        let _: usize = r\n            .sadd(\"bikes:racing:france\", &[\"bike:1\", \"bike:2\", \"bike:3\"])\n            .unwrap_or(0);\n\n        if let Ok(res) = r.sismember(\"bikes:racing:france\", \"bike:1\") {\n            let res: bool = res;\n            println!(\"{}\", i32::from(res)); // >>> 1\n        }\n\n        if let Ok(res) = r.smismember(\"bikes:racing:france\", &[\"bike:2\", \"bike:3\", \"bike:4\"]) {\n            let res: Vec<bool> = res;\n            let res: Vec<i32> = res.into_iter().map(i32::from).collect();\n            println!(\"{res:?}\"); // >>> [1, 1, 0]\n        }","section_id":"tutorial"},{"id":"tutorial-ex24","language":"plaintext","code":"> DEL bikes:racing:usa\n(integer) 1\n> SADD bikes:racing:usa bike:1 bike:4\n(integer) 2\n> SDIFF bikes:racing:france bikes:racing:usa\n1) \"bike:3\"\n2) \"bike:2\"","section_id":"tutorial"},{"id":"tutorial-ex25","language":"csharp","code":"long res16 = db.SetAdd(\"{bikes:racing}:france\", [\"bike:1\", \"bike:2\", \"bike:3\"]);\n        long res17 = db.SetAdd(\"{bikes:racing}:usa\", [\"bike:1\", \"bike:4\"]);\n        RedisValue[] res18 = db.SetCombine(SetOperation.Difference, [\"{bikes:racing}:france\", \"{bikes:racing}:usa\"]);\n        Console.WriteLine(string.Join(\", \", res18));    // >>> bike:2, bike:3","section_id":"tutorial"},{"id":"tutorial-ex26","language":"go","code":"_, err := rdb.SAdd(ctx, \"bikes:racing:france\", \"bike:1\", \"bike:2\", \"bike:3\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t_, err = rdb.SAdd(ctx, \"bikes:racing:usa\", \"bike:1\", \"bike:4\").Result()\n\n\tres13, err := rdb.SDiff(ctx, \"bikes:racing:france\", \"bikes:racing:usa\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Sort the strings in the slice to make sure the output is lexicographical\n\tsort.Strings(res13)\n\n\tfmt.Println(res13) // >>> [bike:2 bike:3]","section_id":"tutorial"},{"id":"tutorial-ex27","language":"java","code":"CompletableFuture<Void> sDiff = asyncCommands.sdiff(\"bikes:racing:france\", \"bikes:racing:usa\")\n                    .thenAccept(res13 -> {\n                        System.out.println(res13); // >>> [bike:2, bike:3]\n                    }).toCompletableFuture();","section_id":"tutorial"},{"id":"tutorial-ex28","language":"java","code":"Mono<List<String>> sDiff = reactiveCommands.sdiff(\"bikes:racing:france\", \"bikes:racing:usa\").collectList()\n                    .doOnNext(result -> {\n                        System.out.println(result); // >>> [bike:2, bike:3]\n                    });","section_id":"tutorial"},{"id":"tutorial-ex29","language":"java","code":"jedis.sadd(\"bikes:racing:france\", \"bike:1\", \"bike:2\", \"bike:3\");\n        jedis.sadd(\"bikes:racing:usa\", \"bike:1\", \"bike:4\");\n\n        Set<String> res13 = jedis.sdiff(\"bikes:racing:france\", \"bikes:racing:usa\");\n        System.out.println(res13);  // >>> [bike:2, bike:3]","section_id":"tutorial"},{"id":"tutorial-ex30","language":"javascript","code":"await client.del('bikes:racing:france')\nawait client.del('bikes:racing:usa')\n\nawait client.sAdd('bikes:racing:france', ['bike:1', 'bike:2', 'bike:3'])\nawait client.sAdd('bikes:racing:usa', ['bike:1', 'bike:4'])\nconst res13 = await client.sDiff(['bikes:racing:france', 'bikes:racing:usa'])\nconsole.log(res13)  // >>> [ 'bike:2', 'bike:3' ]","section_id":"tutorial"},{"id":"tutorial-ex31","language":"php","code":"$r->sadd('bikes:racing:france', ['bike:1', 'bike:2', 'bike:3']);\n        $r->sadd('bikes:racing:usa', ['bike:1', 'bike:4']);\n\n        $res13 = $r->sdiff(['bikes:racing:france', 'bikes:racing:usa']);\n        sort($res13);\n        echo json_encode($res13) . PHP_EOL;\n        // >>> [\"bike:2\",\"bike:3\"]","section_id":"tutorial"},{"id":"tutorial-ex32","language":"python","code":"r.sadd(\"bikes:racing:france\", \"bike:1\", \"bike:2\", \"bike:3\")\nr.sadd(\"bikes:racing:usa\", \"bike:1\", \"bike:4\")\n\nres13 = r.sdiff(\"bikes:racing:france\", \"bikes:racing:usa\")\nprint(res13)  # >>> {'bike:2', 'bike:3'}","section_id":"tutorial"},{"id":"tutorial-ex33","language":"ruby","code":"r.sadd('bikes:racing:france', ['bike:1', 'bike:2', 'bike:3'])\nr.sadd('bikes:racing:usa', ['bike:1', 'bike:4'])\n\nres13 = r.sdiff('bikes:racing:france', 'bikes:racing:usa')\nputs res13.sort.inspect # >>> [\"bike:2\", \"bike:3\"]","section_id":"tutorial"},{"id":"tutorial-ex34","language":"rust","code":"let _: usize = r.del(\"bikes:racing:france\").await.unwrap_or(0);\n        let _: usize = r.del(\"bikes:racing:usa\").await.unwrap_or(0);\n        let _: usize = r\n            .sadd(\"bikes:racing:france\", &[\"bike:1\", \"bike:2\", \"bike:3\"])\n            .await\n            .unwrap_or(0);\n        let _: usize = r\n            .sadd(\"bikes:racing:usa\", &[\"bike:1\", \"bike:4\"])\n            .await\n            .unwrap_or(0);\n\n        if let Ok(res) = r.sdiff(&[\"bikes:racing:france\", \"bikes:racing:usa\"]).await {\n            let res: HashSet<String> = res;\n            let res = sorted_set(res);\n            println!(\"{res:?}\"); // >>> [\"bike:2\", \"bike:3\"]\n        }","section_id":"tutorial"},{"id":"tutorial-ex35","language":"rust","code":"let _: usize = r.del(\"bikes:racing:france\").unwrap_or(0);\n        let _: usize = r.del(\"bikes:racing:usa\").unwrap_or(0);\n        let _: usize = r.sadd(\"bikes:racing:france\", &[\"bike:1\", \"bike:2\", \"bike:3\"]).unwrap_or(0);\n        let _: usize = r.sadd(\"bikes:racing:usa\", &[\"bike:1\", \"bike:4\"]).unwrap_or(0);\n\n        if let Ok(res) = r.sdiff(&[\"bikes:racing:france\", \"bikes:racing:usa\"]) {\n            let res: HashSet<String> = res;\n            let res = sorted_set(res);\n            println!(\"{res:?}\"); // >>> [\"bike:2\", \"bike:3\"]\n        }","section_id":"tutorial"},{"id":"tutorial-ex36","language":"plaintext","code":"> DEL bikes:racing:france bikes:racing:usa bikes:racing:italy\n(integer) 2\n> SADD bikes:racing:france bike:1 bike:2 bike:3\n(integer) 3\n> SADD bikes:racing:usa bike:1 bike:4\n(integer) 2\n> SADD bikes:racing:italy bike:1 bike:2 bike:3 bike:4\n(integer) 4\n> SINTER bikes:racing:france bikes:racing:usa bikes:racing:italy\n1) \"bike:1\"\n> SUNION bikes:racing:france bikes:racing:usa bikes:racing:italy\n1) \"bike:2\"\n2) \"bike:1\"\n3) \"bike:4\"\n4) \"bike:3\"\n> SDIFF bikes:racing:france bikes:racing:usa bikes:racing:italy\n(empty array)\n> SDIFF bikes:racing:france bikes:racing:usa\n1) \"bike:3\"\n2) \"bike:2\"\n> SDIFF bikes:racing:usa bikes:racing:france\n1) \"bike:4\"","section_id":"tutorial"},{"id":"tutorial-ex37","language":"csharp","code":"db.KeyDelete(\"{bikes:racing}:france\");\n        db.KeyDelete(\"{bikes:racing}:usa\");\n        db.KeyDelete(\"{bikes:racing}:italy\");\n\n        long res19 = db.SetAdd(\"{bikes:racing}:france\", [\"bike:1\", \"bike:2\", \"bike:3\"]);\n        long res20 = db.SetAdd(\"{bikes:racing}:usa\", [\"bike:1\", \"bike:4\"]);\n        long res21 = db.SetAdd(\"{bikes:racing}:italy\", [\"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\"]);\n\n        RedisValue[] res22 = db.SetCombine(SetOperation.Intersect, [\"{bikes:racing}:france\", \"{bikes:racing}:usa\", \"{bikes:racing}:italy\"\n        ]);\n        Console.WriteLine(string.Join(\", \", res22));    // >>> bike:1\n\n        RedisValue[] res23 = db.SetCombine(SetOperation.Union, [\"{bikes:racing}:france\", \"{bikes:racing}:usa\", \"{bikes:racing}:italy\"\n        ]);\n        Console.WriteLine(string.Join(\", \", res23));    // >>> bike:1, bike:2, bike:3, bike:4\n\n        RedisValue[] res24 = db.SetCombine(SetOperation.Difference, [\"{bikes:racing}:france\", \"{bikes:racing}:usa\", \"{bikes:racing}:italy\"\n        ]);\n        Console.WriteLine(string.Join(\", \", res24));    // >>> <empty set>\n\n        RedisValue[] res25 = db.SetCombine(SetOperation.Difference, [\"{bikes:racing}:usa\", \"{bikes:racing}:france\"]);\n        Console.WriteLine(string.Join(\", \", res25));    // >>> bike:4\n\n        RedisValue[] res26 = db.SetCombine(SetOperation.Difference, [\"{bikes:racing}:france\", \"{bikes:racing}:usa\"]);\n        Console.WriteLine(string.Join(\", \", res26));    // >>> bike:2, bike:3","section_id":"tutorial"},{"id":"tutorial-ex38","language":"go","code":"rdb.Del(ctx, \"bikes:racing:france\")\n\trdb.Del(ctx, \"bikes:racing:usa\")\n\trdb.Del(ctx, \"bikes:racing:italy\")\n\n\t_, err := rdb.SAdd(ctx, \"bikes:racing:france\", \"bike:1\", \"bike:2\", \"bike:3\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t_, err = rdb.SAdd(ctx, \"bikes:racing:usa\", \"bike:1\", \"bike:4\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t_, err = rdb.SAdd(ctx, \"bikes:racing:italy\", \"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tres14, err := rdb.SInter(ctx, \"bikes:racing:france\", \"bikes:racing:usa\", \"bikes:racing:italy\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res14) // >>> [bike:1]\n\n\tres15, err := rdb.SUnion(ctx, \"bikes:racing:france\", \"bikes:racing:usa\", \"bikes:racing:italy\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Sort the strings in the slice to make sure the output is lexicographical\n\tsort.Strings(res15)\n\n\tfmt.Println(res15) // >>> [bike:1 bike:2 bike:3 bike:4]\n\n\tres16, err := rdb.SDiff(ctx, \"bikes:racing:france\", \"bikes:racing:usa\", \"bikes:racing:italy\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res16) // >>> []\n\n\tres17, err := rdb.SDiff(ctx, \"bikes:racing:usa\", \"bikes:racing:france\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res17) // >>> [bike:4]\n\n\tres18, err := rdb.SDiff(ctx, \"bikes:racing:france\", \"bikes:racing:usa\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Sort the strings in the slice to make sure the output is lexicographical\n\tsort.Strings(res18)\n\n\tfmt.Println(res18) // >>> [bike:2 bike:3]","section_id":"tutorial"},{"id":"tutorial-ex39","language":"java","code":"CompletableFuture<Void> multisets = asyncCommands.sadd(\"bikes:racing:france\", \"bike:1\", \"bike:2\", \"bike:3\")\n                    .thenCompose(res14 -> {\n                        System.out.println(res14); // >>> 0\n                        return asyncCommands.sadd(\"bikes:racing:usa\", \"bike:1\", \"bike:4\");\n                    }).thenCompose(res15 -> {\n                        System.out.println(res15); // >>> 0\n                        return asyncCommands.sadd(\"bikes:racing:italy\", \"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\");\n                    }).thenCompose(res16 -> {\n                        System.out.println(res16); // >>> 4\n                        return asyncCommands.sinter(\"bikes:racing:france\", \"bikes:racing:usa\", \"bikes:racing:italy\");\n                    }).thenCompose(res17 -> {\n                        System.out.println(res17); // >>> [bike:1]\n                        return asyncCommands.sunion(\"bikes:racing:france\", \"bikes:racing:usa\", \"bikes:racing:italy\");\n                    }).thenCompose(res18 -> {\n                        System.out.println(res18); // >>> [bike:1, bike:2, bike:3, bike:4]\n                        return asyncCommands.sdiff(\"bikes:racing:france\", \"bikes:racing:usa\", \"bikes:racing:italy\");\n                    }).thenCompose(res19 -> {\n                        System.out.println(res19); // >>> []\n                        return asyncCommands.sdiff(\"bikes:racing:usa\", \"bikes:racing:france\");\n                    }).thenCompose(res20 -> {\n                        System.out.println(res20); // >>> [bike:4]\n                        return asyncCommands.sdiff(\"bikes:racing:france\", \"bikes:racing:usa\");\n                    }).thenAccept(res21 -> {\n                        System.out.println(res21); // >>> [bike:2, bike:3]\n                    }).toCompletableFuture();","section_id":"tutorial"},{"id":"tutorial-ex40","language":"java","code":"Mono<Void> add3sets = reactiveCommands.sadd(\"bikes:racing:france\", \"bike:1\", \"bike:2\", \"bike:3\")\n                    .doOnNext(result -> {\n                        System.out.println(result); // >>> 0\n                    }).flatMap(v -> reactiveCommands.sadd(\"bikes:racing:usa\", \"bike:1\", \"bike:4\")).doOnNext(result -> {\n                        System.out.println(result); // >>> 0\n                    }).flatMap(v -> reactiveCommands.sadd(\"bikes:racing:italy\", \"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\"))\n                    .doOnNext(result -> {\n                        System.out.println(result); // >>> 4,\n                    }).then();\n            add3sets.block();\n\n            Mono<List<String>> multisets1 = reactiveCommands\n                    .sinter(\"bikes:racing:france\", \"bikes:racing:usa\", \"bikes:racing:italy\").collectList().doOnNext(result -> {\n                        System.out.println(result); // >>> [bike:1]\n                    });\n\n            Mono<List<String>> multisets2 = reactiveCommands\n                    .sunion(\"bikes:racing:france\", \"bikes:racing:usa\", \"bikes:racing:italy\").collectList().doOnNext(result -> {\n                        System.out.println(result); // >>> [bike:1, bike:2, bike:3, bike:4]\n                    });\n\n            Mono<List<String>> multisets3 = reactiveCommands\n                    .sdiff(\"bikes:racing:france\", \"bikes:racing:usa\", \"bikes:racing:italy\").collectList().doOnNext(result -> {\n                        System.out.println(result); // >>> []\n                    });\n\n            Mono<List<String>> multisets4 = reactiveCommands.sdiff(\"bikes:racing:usa\", \"bikes:racing:france\").collectList()\n                    .doOnNext(result -> {\n                        System.out.println(result); // >>> [bike:4]\n                    });\n\n            Mono<List<String>> multisets5 = reactiveCommands.sdiff(\"bikes:racing:france\", \"bikes:racing:usa\").collectList()\n                    .doOnNext(result -> {\n                        System.out.println(result); // >>> [bike:2, bike:3]\n                    });","section_id":"tutorial"},{"id":"tutorial-ex41","language":"java","code":"jedis.del(\"bikes:racing:france\");\n        jedis.del(\"bikes:racing:usa\");\n        jedis.del(\"bikes:racing:italy\");\n\n        jedis.sadd(\"bikes:racing:france\", \"bike:1\", \"bike:2\", \"bike:3\");\n        jedis.sadd(\"bikes:racing:usa\", \"bike:1\", \"bike:4\");\n        jedis.sadd(\"bikes:racing:italy\", \"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\");\n\n        Set<String> res14 = jedis.sinter(\"bikes:racing:france\", \"bikes:racing:usa\", \"bikes:racing:italy\");\n        System.out.println(res14);  // >>> [bike:1]\n\n        Set<String> res15 = jedis.sunion(\"bikes:racing:france\", \"bikes:racing:usa\", \"bikes:racing:italy\");\n        System.out.println(res15);  // >>> [bike:1, bike:2, bike:3, bike:4]\n\n        Set<String> res16 = jedis.sdiff(\"bikes:racing:france\", \"bikes:racing:usa\", \"bikes:racing:italy\");\n        System.out.println(res16);  // >>> []\n\n        Set<String> res17 = jedis.sdiff(\"bikes:racing:usa\", \"bikes:racing:france\");\n        System.out.println(res17);  // >>> [bike:4]\n\n        Set<String> res18 = jedis.sdiff(\"bikes:racing:france\", \"bikes:racing:usa\");\n        System.out.println(res18);  // >>> [bike:2, bike:3]","section_id":"tutorial"},{"id":"tutorial-ex42","language":"javascript","code":"await client.del('bikes:racing:france')\nawait client.del('bikes:racing:usa')\nawait client.del('bikes:racing:italy')\n\nawait client.sAdd('bikes:racing:france', ['bike:1', 'bike:2', 'bike:3'])\nawait client.sAdd('bikes:racing:usa', ['bike:1', 'bike:4'])\nawait client.sAdd('bikes:racing:italy', ['bike:1', 'bike:2', 'bike:3', 'bike:4'])\n\nconst res14 = await client.sInter(\n  ['bikes:racing:france', 'bikes:racing:usa', 'bikes:racing:italy']\n)\nconsole.log(res14)  // >>> ['bike:1']\n\nconst res15 = await client.sUnion(\n  ['bikes:racing:france', 'bikes:racing:usa', 'bikes:racing:italy']\n)\nconsole.log(res15)  // >>> ['bike:1', 'bike:2', 'bike:3', 'bike:4']\n\nconst res16 = await client.sDiff(['bikes:racing:france', 'bikes:racing:usa', 'bikes:racing:italy'])\nconsole.log(res16)  // >>> []\n\nconst res17 = await client.sDiff(['bikes:racing:usa', 'bikes:racing:france'])\nconsole.log(res17)  // >>> ['bike:4']\n\nconst res18 = await client.sDiff(['bikes:racing:france', 'bikes:racing:usa'])\nconsole.log(res18)  // >>> ['bike:2', 'bike:3']","section_id":"tutorial"},{"id":"tutorial-ex43","language":"php","code":"$r->del('bikes:racing:france');\n        $r->del('bikes:racing:usa');\n        $r->del('bikes:racing:italy');\n            \n        $r->sadd('bikes:racing:france', ['bike:1', 'bike:2', 'bike:3']);\n        $r->sadd('bikes:racing:usa', ['bike:1', 'bike:4']);\n        $r->sadd('bikes:racing:italy', ['bike:1', 'bike:2', 'bike:3', 'bike:4']);\n\n        $res14 = $r->sinter(['bikes:racing:france', 'bikes:racing:usa', 'bikes:racing:italy']);\n        echo json_encode($res14) . PHP_EOL;\n        // >>> [\"bike:1\"]\n\n        $res15 = $r->sunion(['bikes:racing:france', 'bikes:racing:usa', 'bikes:racing:italy']);\n        sort($res15);\n        echo json_encode($res15) . PHP_EOL;\n        // >>> [\"bike:1\",\"bike:2\",\"bike:3\",\"bike:4\"]\n\n        $res16 = $r->sdiff(['bikes:racing:france', 'bikes:racing:usa', 'bikes:racing:italy']);\n        echo json_encode($res16) . PHP_EOL;\n        // >>> []\n\n        $res17 = $r->sdiff(['bikes:racing:usa', 'bikes:racing:france']);\n        echo json_encode($res17) . PHP_EOL;\n        // >>> [\"bike:4\"]\n\n        $res18 = $r->sdiff(['bikes:racing:france', 'bikes:racing:usa']);\n        sort($res18);\n        echo json_encode($res18) . PHP_EOL;\n        // >>> [\"bike:2\",\"bike:3\"]","section_id":"tutorial"},{"id":"tutorial-ex44","language":"python","code":"r.delete(\"bikes:racing:france\")\nr.delete(\"bikes:racing:usa\")\nr.delete(\"bikes:racing:italy\")\n\nr.sadd(\"bikes:racing:france\", \"bike:1\", \"bike:2\", \"bike:3\")\nr.sadd(\"bikes:racing:usa\", \"bike:1\", \"bike:4\")\nr.sadd(\"bikes:racing:italy\", \"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\")\n\nres13 = r.sinter(\"bikes:racing:france\", \"bikes:racing:usa\", \"bikes:racing:italy\")\nprint(res13)  # >>> {'bike:1'}\n\nres14 = r.sunion(\"bikes:racing:france\", \"bikes:racing:usa\", \"bikes:racing:italy\")\nprint(res14)  # >>> {'bike:1', 'bike:2', 'bike:3', 'bike:4'}\n\nres15 = r.sdiff(\"bikes:racing:france\", \"bikes:racing:usa\", \"bikes:racing:italy\")\nprint(res15)  # >>> set()\n\nres16 = r.sdiff(\"bikes:racing:usa\", \"bikes:racing:france\")\nprint(res16)  # >>> {'bike:4'}\n\nres17 = r.sdiff(\"bikes:racing:france\", \"bikes:racing:usa\")\nprint(res17)  # >>> {'bike:2', 'bike:3'}","section_id":"tutorial"},{"id":"tutorial-ex45","language":"ruby","code":"r.del('bikes:racing:france', 'bikes:racing:usa', 'bikes:racing:italy')\n\nr.sadd('bikes:racing:france', ['bike:1', 'bike:2', 'bike:3'])\nr.sadd('bikes:racing:usa', ['bike:1', 'bike:4'])\nr.sadd('bikes:racing:italy', ['bike:1', 'bike:2', 'bike:3', 'bike:4'])\n\nres14 = r.sinter('bikes:racing:france', 'bikes:racing:usa', 'bikes:racing:italy')\nputs res14.inspect # >>> [\"bike:1\"]\n\nres15 = r.sunion('bikes:racing:france', 'bikes:racing:usa', 'bikes:racing:italy')\nputs res15.sort.inspect # >>> [\"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\"]\n\nres16 = r.sdiff('bikes:racing:france', 'bikes:racing:usa', 'bikes:racing:italy')\nputs res16.inspect # >>> []\n\nres17 = r.sdiff('bikes:racing:france', 'bikes:racing:usa')\nputs res17.sort.inspect # >>> [\"bike:2\", \"bike:3\"]\n\nres18 = r.sdiff('bikes:racing:usa', 'bikes:racing:france')\nputs res18.inspect # >>> [\"bike:4\"]","section_id":"tutorial"},{"id":"tutorial-ex46","language":"rust","code":"let _: usize = r\n            .del(&[\"bikes:racing:france\", \"bikes:racing:usa\", \"bikes:racing:italy\"])\n            .await\n            .unwrap_or(0);\n        let _: usize = r\n            .sadd(\"bikes:racing:france\", &[\"bike:1\", \"bike:2\", \"bike:3\"])\n            .await\n            .unwrap_or(0);\n        let _: usize = r\n            .sadd(\"bikes:racing:usa\", &[\"bike:1\", \"bike:4\"])\n            .await\n            .unwrap_or(0);\n        let _: usize = r\n            .sadd(\"bikes:racing:italy\", &[\"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\"])\n            .await\n            .unwrap_or(0);\n\n        if let Ok(res) = r\n            .sinter(&[\"bikes:racing:france\", \"bikes:racing:usa\", \"bikes:racing:italy\"])\n            .await\n        {\n            let res: HashSet<String> = res;\n            let res = sorted_set(res);\n            println!(\"{res:?}\"); // >>> [\"bike:1\"]\n        }\n\n        if let Ok(res) = r\n            .sunion(&[\"bikes:racing:france\", \"bikes:racing:usa\", \"bikes:racing:italy\"])\n            .await\n        {\n            let res: HashSet<String> = res;\n            let res = sorted_set(res);\n            println!(\"{res:?}\"); // >>> [\"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\"]\n        }\n\n        if let Ok(res) = r\n            .sdiff(&[\"bikes:racing:france\", \"bikes:racing:usa\", \"bikes:racing:italy\"])\n            .await\n        {\n            let res: HashSet<String> = res;\n            let res = sorted_set(res);\n            println!(\"{res:?}\"); // >>> []\n        }\n\n        if let Ok(res) = r.sdiff(&[\"bikes:racing:france\", \"bikes:racing:usa\"]).await {\n            let res: HashSet<String> = res;\n            let res = sorted_set(res);\n            println!(\"{res:?}\"); // >>> [\"bike:2\", \"bike:3\"]\n        }\n\n        if let Ok(res) = r.sdiff(&[\"bikes:racing:usa\", \"bikes:racing:france\"]).await {\n            let res: HashSet<String> = res;\n            let res = sorted_set(res);\n            println!(\"{res:?}\"); // >>> [\"bike:4\"]\n        }","section_id":"tutorial"},{"id":"tutorial-ex47","language":"rust","code":"let _: usize = r.del(&[\"bikes:racing:france\", \"bikes:racing:usa\", \"bikes:racing:italy\"]).unwrap_or(0);\n        let _: usize = r.sadd(\"bikes:racing:france\", &[\"bike:1\", \"bike:2\", \"bike:3\"]).unwrap_or(0);\n        let _: usize = r.sadd(\"bikes:racing:usa\", &[\"bike:1\", \"bike:4\"]).unwrap_or(0);\n        let _: usize = r.sadd(\"bikes:racing:italy\", &[\"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\"]).unwrap_or(0);\n\n        if let Ok(res) =\n            r.sinter(&[\"bikes:racing:france\", \"bikes:racing:usa\", \"bikes:racing:italy\"])\n        {\n            let res: HashSet<String> = res;\n            let res = sorted_set(res);\n            println!(\"{res:?}\"); // >>> [\"bike:1\"]\n        }\n\n        if let Ok(res) =\n            r.sunion(&[\"bikes:racing:france\", \"bikes:racing:usa\", \"bikes:racing:italy\"])\n        {\n            let res: HashSet<String> = res;\n            let res = sorted_set(res);\n            println!(\"{res:?}\"); // >>> [\"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\"]\n        }\n\n        if let Ok(res) =\n            r.sdiff(&[\"bikes:racing:france\", \"bikes:racing:usa\", \"bikes:racing:italy\"])\n        {\n            let res: HashSet<String> = res;\n            let res = sorted_set(res);\n            println!(\"{res:?}\"); // >>> []\n        }\n\n        if let Ok(res) = r.sdiff(&[\"bikes:racing:france\", \"bikes:racing:usa\"]) {\n            let res: HashSet<String> = res;\n            let res = sorted_set(res);\n            println!(\"{res:?}\"); // >>> [\"bike:2\", \"bike:3\"]\n        }\n\n        if let Ok(res) = r.sdiff(&[\"bikes:racing:usa\", \"bikes:racing:france\"]) {\n            let res: HashSet<String> = res;\n            let res = sorted_set(res);\n            println!(\"{res:?}\"); // >>> [\"bike:4\"]\n        }","section_id":"tutorial"},{"id":"tutorial-ex48","language":"plaintext","code":"> DEL bikes:racing:france\n(integer) 1\n> SADD bikes:racing:france bike:1 bike:2 bike:3 bike:4 bike:5\n(integer) 5\n> SREM bikes:racing:france bike:1\n(integer) 1\n> SPOP bikes:racing:france\n\"bike:3\"\n> SMEMBERS bikes:racing:france\n1) \"bike:2\"\n2) \"bike:4\"\n3) \"bike:5\"\n> SRANDMEMBER bikes:racing:france\n\"bike:2\"","section_id":"tutorial"},{"id":"tutorial-ex49","language":"csharp","code":"db.KeyDelete(\"bikes:racing:france\");\n\n        long res27 = db.SetAdd(\"bikes:racing:france\", [\"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\", \"bike:5\"]);\n\n        bool res28 = db.SetRemove(\"bikes:racing:france\", \"bike:1\");\n        Console.WriteLine(res28);   // >>> True\n\n        RedisValue res29 = db.SetPop(\"bikes:racing:france\");\n        Console.WriteLine(res29);   // >>> bike:3\n\n        RedisValue[] res30 = db.SetMembers(\"bikes:racing:france\");\n        Console.WriteLine(string.Join(\", \", res30));    // >>> bike:2, bike:4, bike:5\n\n        RedisValue res31 = db.SetRandomMember(\"bikes:racing:france\");\n        Console.WriteLine(res31);   // >>> bike:4","section_id":"tutorial"},{"id":"tutorial-ex50","language":"go","code":"rdb.Del(ctx, \"bikes:racing:france\")\n\n\t_, err := rdb.SAdd(ctx, \"bikes:racing:france\", \"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\", \"bike:5\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tres19, err := rdb.SRem(ctx, \"bikes:racing:france\", \"bike:1\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res19) // >>> 1\n\n\tres20, err := rdb.SPop(ctx, \"bikes:racing:france\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res20) // >>> <random element>\n\n\tres21, err := rdb.SMembers(ctx, \"bikes:racing:france\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res21) // >>> <remaining elements>\n\n\tres22, err := rdb.SRandMember(ctx, \"bikes:racing:france\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res22) // >>> <random element>","section_id":"tutorial"},{"id":"tutorial-ex51","language":"java","code":"CompletableFuture<Void> sRem = asyncCommands\n                    .sadd(\"bikes:racing:france\", \"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\", \"bike:5\").thenCompose(res22 -> {\n                        System.out.println(res22); // >>> 2\n                        return asyncCommands.srem(\"bikes:racing:france\", \"bike:1\");\n                    }).thenCompose(res23 -> {\n                        System.out.println(res23); // >>> 1\n                        return asyncCommands.spop(\"bikes:racing:france\");\n                    }).thenCompose(res24 -> {\n                        System.out.println(res24); // >>> bike:3 (for example)\n                        return asyncCommands.smembers(\"bikes:racing:france\");\n                    }).thenCompose(res25 -> {\n                        System.out.println(res25); // >>> [bike:2, bike:4, bike:5] (for example)\n                        return asyncCommands.srandmember(\"bikes:racing:france\");\n                    }).thenAccept(res26 -> {\n                        System.out.println(res26); // >>> bike:4 (for example)\n                    }).toCompletableFuture();","section_id":"tutorial"},{"id":"tutorial-ex52","language":"java","code":"Mono<Void> sRem = reactiveCommands.sadd(\"bikes:racing:france\", \"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\", \"bike:5\")\n                    .doOnNext(result -> {\n                        System.out.println(result); // >>> 2\n                    }).flatMap(v -> reactiveCommands.srem(\"bikes:racing:france\", \"bike:1\")).doOnNext(result -> {\n                        System.out.println(result); // >>> 1\n                    }).flatMap(v -> reactiveCommands.spop(\"bikes:racing:france\")).doOnNext(result -> {\n                        System.out.println(result); // >>> bike:3 (for example)\n                    }).flatMap(v -> reactiveCommands.smembers(\"bikes:racing:france\").collectList()).doOnNext(result -> {\n                        System.out.println(result); // >>> [bike:2, bike:4, bike:5] (for example)\n                    }).flatMap(v -> reactiveCommands.srandmember(\"bikes:racing:france\")).doOnNext(result -> {\n                        System.out.println(result); // >>> bike:4 (for example)\n                    }).then();","section_id":"tutorial"},{"id":"tutorial-ex53","language":"java","code":"jedis.del(\"bikes:racing:france\");\n        jedis.sadd(\"bikes:racing:france\", \"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\", \"bike:5\");\n\n        long res19 = jedis.srem(\"bikes:racing:france\", \"bike:1\");\n        System.out.println(res18);  // >>> 1\n\n        String res20 = jedis.spop(\"bikes:racing:france\");\n        System.out.println(res20);  // >>> bike:3\n\n        Set<String> res21 = jedis.smembers(\"bikes:racing:france\");\n        System.out.println(res21);  // >>> [bike:2, bike:4, bike:5]\n\n        String res22 = jedis.srandmember(\"bikes:racing:france\");\n        System.out.println(res22);  // >>> bike:4","section_id":"tutorial"},{"id":"tutorial-ex54","language":"javascript","code":"await client.del('bikes:racing:france')\n\nawait client.sAdd('bikes:racing:france', ['bike:1', 'bike:2', 'bike:3', 'bike:4', 'bike:5'])\n\nconst res19 = await client.sRem('bikes:racing:france', 'bike:1')\nconsole.log(res19)  // >>> 1\n\nconst res20 = await client.sPop('bikes:racing:france')\nconsole.log(res20)  // >>> bike:3 or other random value\n\nconst res21 = await client.sMembers('bikes:racing:france')\nconsole.log(res21)  // >>> ['bike:2', 'bike:4', 'bike:5']; depends on previous result\n\nconst res22 = await client.sRandMember('bikes:racing:france')\nconsole.log(res22)  // >>> bike:4 or other random value","section_id":"tutorial"},{"id":"tutorial-ex55","language":"php","code":"$r->del('bikes:racing:france');\n\n        $r->sadd('bikes:racing:france', ['bike:1', 'bike:2', 'bike:3', 'bike:4', 'bike:5']);\n\n        $res19 = $r->srem('bikes:racing:france', ['bike:1']);\n        echo $res19 . PHP_EOL;\n        // >>> 1\n\n        $res20 = $r->spop('bikes:racing:france');\n        echo $res20 . PHP_EOL;\n        // >>> bike:3 (for example)\n\n        $res21 = $r->smembers('bikes:racing:france');\n        sort($res21);\n        echo json_encode($res21) . PHP_EOL;\n        // >>> [\"bike:2\",\"bike:4\",\"bike:5\"]\n\n        $res22 = $r->srandmember('bikes:racing:france');\n        echo $res22 . PHP_EOL;\n        // >>> bike:4 (for example)","section_id":"tutorial"},{"id":"tutorial-ex56","language":"python","code":"r.delete(\"bikes:racing:france\")\n\nr.sadd(\"bikes:racing:france\", \"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\", \"bike:5\")\n\nres18 = r.srem(\"bikes:racing:france\", \"bike:1\")\nprint(res18)  # >>> 1\n\nres19 = r.spop(\"bikes:racing:france\")\nprint(res19)  # >>> bike:3\n\nres20 = r.smembers(\"bikes:racing:france\")\nprint(res20)  # >>> {'bike:2', 'bike:4', 'bike:5'}\n\nres21 = r.srandmember(\"bikes:racing:france\")\nprint(res21)  # >>> bike:4","section_id":"tutorial"},{"id":"tutorial-ex57","language":"ruby","code":"r.del('bikes:racing:france')\n\nr.sadd('bikes:racing:france', ['bike:1', 'bike:2', 'bike:3', 'bike:4', 'bike:5'])\n\nres19 = r.srem('bikes:racing:france', ['bike:1'])\nputs res19 # >>> 1\n\nres20 = r.spop('bikes:racing:france')\nputs res20 # >>> bike:3, for example\n\nres21 = r.smembers('bikes:racing:france')\nputs res21.sort.inspect # >>> Remaining members, in no particular order\n\nres22 = r.srandmember('bikes:racing:france')\nputs res22 # >>> bike:4, for example","section_id":"tutorial"},{"id":"tutorial-ex58","language":"rust","code":"let _: usize = r.del(\"bikes:racing:france\").await.unwrap_or(0);\n        let _: usize = r\n            .sadd(\n                \"bikes:racing:france\",\n                &[\"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\", \"bike:5\"],\n            )\n            .await\n            .unwrap_or(0);\n\n        if let Ok(res) = r.srem(\"bikes:racing:france\", \"bike:1\").await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 1\n        }\n\n        if let Ok(res) = r.spop(\"bikes:racing:france\").await {\n            let res: Option<String> = res;\n            match res {\n                Some(res) => println!(\"{res}\"), // >>> bike:3 (for example)\n                None => println!(\"(nil)\"),\n            }\n        }\n\n        if let Ok(res) = r.smembers(\"bikes:racing:france\").await {\n            let res: HashSet<String> = res;\n            let res = sorted_set(res);\n            println!(\"{res:?}\"); // >>> [\"bike:2\", \"bike:4\", \"bike:5\"] (for example)\n        }\n\n        if let Ok(res) = r.srandmember(\"bikes:racing:france\").await {\n            let res: Option<String> = res;\n            match res {\n                Some(res) => println!(\"{res}\"), // >>> bike:4 (for example)\n                None => println!(\"(nil)\"),\n            }\n        }","section_id":"tutorial"},{"id":"tutorial-ex59","language":"rust","code":"let _: usize = r.del(\"bikes:racing:france\").unwrap_or(0);\n        let _: usize = r\n            .sadd(\n                \"bikes:racing:france\",\n                &[\"bike:1\", \"bike:2\", \"bike:3\", \"bike:4\", \"bike:5\"],\n            )\n            .unwrap_or(0);\n\n        if let Ok(res) = r.srem(\"bikes:racing:france\", \"bike:1\") {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 1\n        }\n\n        if let Ok(res) = r.spop(\"bikes:racing:france\") {\n            let res: Option<String> = res;\n            match res {\n                Some(res) => println!(\"{res}\"), // >>> bike:3 (for example)\n                None => println!(\"(nil)\"),\n            }\n        }\n\n        if let Ok(res) = r.smembers(\"bikes:racing:france\") {\n            let res: HashSet<String> = res;\n            let res = sorted_set(res);\n            println!(\"{res:?}\"); // >>> [\"bike:2\", \"bike:4\", \"bike:5\"] (for example)\n        }\n\n        if let Ok(res) = r.srandmember(\"bikes:racing:france\") {\n            let res: Option<String> = res;\n            match res {\n                Some(res) => println!(\"{res}\"), // >>> bike:4 (for example)\n                None => println!(\"(nil)\"),\n            }\n        }","section_id":"tutorial"}]}
{"schema_version":2,"id":"develop/data-types/sorted-sets","title":"Redis sorted sets","url":"https://redis.io/docs/latest/develop/data-types/sorted-sets/","summary":"Introduction to Redis sorted sets","aliases":["/data-types/sorted-sets/","/manual/data-types/sorted-sets/","/develop/data-types/sorted-set/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-10T15:45:53+01:00","page_type":"content","content_hash":"15b503a15947f38193785e54152c529cf181cced16d8df2a424acc9af967b3be","sections":[{"id":"sorted-set-command-summary","title":"Sorted set command summary","role":"content","text":"**35 commands in this group:**\n\n[View all sorted-set commands](https://redis.io/commands/?group=sorted-set)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [BZMPOP](https://redis.io/commands/bzmpop/) | Removes and returns a member by score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped. | O(K) + O(M*log(N)) where K is the number of pro... | 7.0.0 |\n| [BZPOPMAX](https://redis.io/commands/bzpopmax/) | Removes and returns the member with the highest score from one or more sorted sets. Blocks until a member available otherwise.  Deletes the sorted set if the last element was popped. | O(log(N)) with N being the number of elements i... | 5.0.0 |\n| [BZPOPMIN](https://redis.io/commands/bzpopmin/) | Removes and returns the member with the lowest score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped. | O(log(N)) with N being the number of elements i... | 5.0.0 |\n| [ZADD](https://redis.io/commands/zadd/) | Adds one or more members to a sorted set, or updates their scores. Creates the key if it doesn't exist. | O(log(N)) for each item added, where N is the n... | 1.2.0 |\n| [ZCARD](https://redis.io/commands/zcard/) | Returns the number of members in a sorted set. | O(1) | 1.2.0 |\n| [ZCOUNT](https://redis.io/commands/zcount/) | Returns the count of members in a sorted set that have scores within a range. | O(log(N)) with N being the number of elements i... | 2.0.0 |\n| [ZDIFF](https://redis.io/commands/zdiff/) | Returns the difference between multiple sorted sets. | O(L + (N-K)log(N)) worst case where L is the to... | 6.2.0 |\n| [ZDIFFSTORE](https://redis.io/commands/zdiffstore/) | Stores the difference of multiple sorted sets in a key. | O(L + (N-K)log(N)) worst case where L is the to... | 6.2.0 |\n| [ZINCRBY](https://redis.io/commands/zincrby/) | Increments the score of a member in a sorted set. | O(log(N)) where N is the number of elements in ... | 1.2.0 |\n| [ZINTER](https://redis.io/commands/zinter/) | Returns the intersect of multiple sorted sets. | O(N*K)+O(M*log(M)) worst case with N being the ... | 6.2.0 |\n\n#### Sorted set command summary (part 2)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [ZINTERCARD](https://redis.io/commands/zintercard/) | Returns the number of members of the intersect of multiple sorted sets. | O(N*K) worst case with N being the smallest inp... | 7.0.0 |\n| [ZINTERSTORE](https://redis.io/commands/zinterstore/) | Stores the intersect of multiple sorted sets in a key. | O(N*K)+O(M*log(M)) worst case with N being the ... | 2.0.0 |\n| [ZLEXCOUNT](https://redis.io/commands/zlexcount/) | Returns the number of members in a sorted set within a lexicographical range. | O(log(N)) with N being the number of elements i... | 2.8.9 |\n| [ZMPOP](https://redis.io/commands/zmpop/) | Returns the highest- or lowest-scoring members from one or more sorted sets after removing them. Deletes the sorted set if the last member was popped. | O(K) + O(M*log(N)) where K is the number of pro... | 7.0.0 |\n| [ZMSCORE](https://redis.io/commands/zmscore/) | Returns the score of one or more members in a sorted set. | O(N) where N is the number of members being req... | 6.2.0 |\n| [ZPOPMAX](https://redis.io/commands/zpopmax/) | Returns the highest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped. | O(log(N)*M) with N being the number of elements... | 5.0.0 |\n| [ZPOPMIN](https://redis.io/commands/zpopmin/) | Returns the lowest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped. | O(log(N)*M) with N being the number of elements... | 5.0.0 |\n| [ZRANDMEMBER](https://redis.io/commands/zrandmember/) | Returns one or more random members from a sorted set. | O(N) where N is the number of members returned | 6.2.0 |\n| [ZRANGE](https://redis.io/commands/zrange/) | Returns members in a sorted set within a range of indexes. | O(log(N)+M) with N being the number of elements... | 1.2.0 |\n| [ZRANGEBYLEX](https://redis.io/commands/zrangebylex/) | Returns members in a sorted set within a lexicographical range. | O(log(N)+M) with N being the number of elements... | 2.8.9 |\n\n#### Sorted set command summary (part 3)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [ZRANGEBYSCORE](https://redis.io/commands/zrangebyscore/) | Returns members in a sorted set within a range of scores. | O(log(N)+M) with N being the number of elements... | 1.0.5 |\n| [ZRANGESTORE](https://redis.io/commands/zrangestore/) | Stores a range of members from sorted set in a key. | O(log(N)+M) with N being the number of elements... | 6.2.0 |\n| [ZRANK](https://redis.io/commands/zrank/) | Returns the index of a member in a sorted set ordered by ascending scores. | O(log(N)) | 2.0.0 |\n| [ZREM](https://redis.io/commands/zrem/) | Removes one or more members from a sorted set. Deletes the sorted set if all members were removed. | O(M*log(N)) with N being the number of elements... | 1.2.0 |\n| [ZREMRANGEBYLEX](https://redis.io/commands/zremrangebylex/) | Removes members in a sorted set within a lexicographical range. Deletes the sorted set if all members were removed. | O(log(N)+M) with N being the number of elements... | 2.8.9 |\n| [ZREMRANGEBYRANK](https://redis.io/commands/zremrangebyrank/) | Removes members in a sorted set within a range of indexes. Deletes the sorted set if all members were removed. | O(log(N)+M) with N being the number of elements... | 2.0.0 |\n| [ZREMRANGEBYSCORE](https://redis.io/commands/zremrangebyscore/) | Removes members in a sorted set within a range of scores. Deletes the sorted set if all members were removed. | O(log(N)+M) with N being the number of elements... | 1.2.0 |\n| [ZREVRANGE](https://redis.io/commands/zrevrange/) | Returns members in a sorted set within a range of indexes in reverse order. | O(log(N)+M) with N being the number of elements... | 1.2.0 |\n| [ZREVRANGEBYLEX](https://redis.io/commands/zrevrangebylex/) | Returns members in a sorted set within a lexicographical range in reverse order. | O(log(N)+M) with N being the number of elements... | 2.8.9 |\n| [ZREVRANGEBYSCORE](https://redis.io/commands/zrevrangebyscore/) | Returns members in a sorted set within a range of scores in reverse order. | O(log(N)+M) with N being the number of elements... | 2.2.0 |\n\n#### Sorted set command summary (part 4)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [ZREVRANK](https://redis.io/commands/zrevrank/) | Returns the index of a member in a sorted set ordered by descending scores. | O(log(N)) | 2.0.0 |\n| [ZSCAN](https://redis.io/commands/zscan/) | Iterates over members and scores of a sorted set. | O(1) for every call. O(N) for a complete iterat... | 2.8.0 |\n| [ZSCORE](https://redis.io/commands/zscore/) | Returns the score of a member in a sorted set. | O(1) | 1.2.0 |\n| [ZUNION](https://redis.io/commands/zunion/) | Returns the union of multiple sorted sets. | O(N)+O(M*log(M)) with N being the sum of the si... | 6.2.0 |\n| [ZUNIONSTORE](https://redis.io/commands/zunionstore/) | Stores the union of multiple sorted sets in a key. | O(N)+O(M log(M)) with N being the sum of the si... | 2.0.0 |\n\n\n\nA Redis sorted set is a collection of unique strings (members) ordered by an associated score.\nWhen more than one string has the same score, the strings are ordered lexicographically.\nSome use cases for sorted sets include:\n\n* Leaderboards. For example, you can use sorted sets to easily maintain  ordered lists of the highest scores in a massive online game.\n* Rate limiters. In particular, you can use a sorted set to build a sliding-window rate limiter to prevent excessive API requests.\n\nYou can think of sorted sets as a mix between a Set and\na Hash. Like sets, sorted sets are composed of unique, non-repeating\nstring elements, so in some sense a sorted set is a set as well.\n\nHowever while elements inside sets are not ordered, every element in\na sorted set is associated with a floating point value, called *the score*\n(this is why the type is also similar to a hash, since every element\nis mapped to a value).\n\nMoreover, elements in a sorted set are *taken in order* (so they are not\nordered on request, order is a peculiarity of the data structure used to\nrepresent sorted sets). They are ordered according to the following rule:\n\n* If B and A are two elements with a different score, then A > B if A.score is > B.score.\n* If B and A have exactly the same score, then A > B if the A string is lexicographically greater than the B string. B and A strings can't be equal since sorted sets only have unique elements.\n\nLet's start with a simple example, we'll add all our racers and the score they got in the first race:\n\nFoundational: Add members to a sorted set with their scores (creates new members or updates existing ones)\n\n**Commands:** ZADD\n\n**Complexity:**\n- ZADD: O(log(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\n\nAs you can see [`ZADD`](https://redis.io/docs/latest/commands/zadd) is similar to [`SADD`](https://redis.io/docs/latest/commands/sadd), but takes one additional argument\n(placed before the element to be added) which is the score.\n[`ZADD`](https://redis.io/docs/latest/commands/zadd) is also variadic, so you are free to specify multiple score-value\npairs, as shown in the example above.\n\nWith sorted sets it is trivial to return a list of racers sorted by their\nscore because actually *they are already sorted*.\n\nImplementation note: Sorted sets are implemented via a\ndual-ported data structure containing both a skip list and a hash table, so\nevery time we add an element Redis performs an O(log(N)) operation. That's\ngood, so when we ask for sorted elements, Redis does not have to do any work at\nall, it's already sorted. Note that the [`ZRANGE`](https://redis.io/docs/latest/commands/zrange) order is low to high, while the [`ZREVRANGE`](https://redis.io/docs/latest/commands/zrevrange) order is high to low:\n\nRetrieve members in ascending or descending order using ZRANGE and ZREVRANGE (no sorting needed, already ordered)\n\n**Builds upon:** zadd\n\n**Commands:** ZRANGE, ZREVRANGE\n\n**Complexity:**\n- ZRANGE: O(log(N)\n- ZREVRANGE: O(log(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nNote: 0 and -1 means from element index 0 to the last element (-1 works\nhere just as it does in the case of the [`LRANGE`](https://redis.io/docs/latest/commands/lrange) command).\n\nIt is possible to return scores as well, using the `WITHSCORES` argument:\n\nRetrieve members with their scores using WITHSCORES when you need both the member and its associated score value\n\n**Builds upon:** zrange\n\n**Commands:** ZRANGE\n\n**Complexity:**\n- ZRANGE: O(log(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"operating-on-ranges","title":"Operating on ranges","role":"content","text":"Sorted sets are more powerful than this. They can operate on ranges.\nLet's get all the racers with 10 or fewer points. We\nuse the [`ZRANGEBYSCORE`](https://redis.io/docs/latest/commands/zrangebyscore) command to do it:\n\nQuery by score range: Retrieve members within a score range using ZRANGEBYSCORE when you need to filter by numeric values\n\n**Difficulty:** Intermediate\n\n**Builds upon:** zadd\n\n**Commands:** ZRANGEBYSCORE\n\n**Complexity:**\n- ZRANGEBYSCORE: O(log(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nWe asked Redis to return all the elements with a score between negative\ninfinity and 10 (both extremes are included).\n\nTo remove an element we'd simply call [`ZREM`](https://redis.io/docs/latest/commands/zrem) with the racer's name.\nIt's also possible to remove ranges of elements. Let's remove racer Castilla along with all\nthe racers with strictly fewer than 10 points:\n\nRemove members using ZREM for individual elements or ZREMRANGEBYSCORE for score ranges when you need to delete data\n\n**Difficulty:** Intermediate\n\n**Builds upon:** zadd\n\n**Commands:** ZREM, ZREMRANGEBYSCORE, ZRANGE\n\n**Complexity:**\n- ZREM: O(M*log(N)\n- ZREMRANGEBYSCORE: O(log(N)\n- ZRANGE: O(log(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\n[`ZREMRANGEBYSCORE`](https://redis.io/docs/latest/commands/zremrangebyscore) is perhaps not the best command name,\nbut it can be very useful, and returns the number of removed elements.\n\nAnother extremely useful operation defined for sorted set elements\nis the get-rank operation. It is possible to ask what is the\nposition of an element in the set of ordered elements.\nThe [`ZREVRANK`](https://redis.io/docs/latest/commands/zrevrank) command is also available in order to get the rank, considering\nthe elements sorted in a descending way.\n\nGet member position: Use ZRANK and ZREVRANK to find a member's position in the sorted set (useful for leaderboards)\n\n**Difficulty:** Intermediate\n\n**Builds upon:** zadd\n\n**Commands:** DEL, ZADD, ZRANK, ZREVRANK\n\n**Complexity:**\n- DEL: O(N)\n- ZADD: O(log(N)\n- ZRANK: O(log(N)\n- ZREVRANK: O(log(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"lexicographical-scores","title":"Lexicographical scores","role":"content","text":"In version Redis 2.8, a new feature was introduced that allows\ngetting ranges lexicographically, assuming elements in a sorted set are all\ninserted with the same identical score (elements are compared with the C\n`memcmp` function, so it is guaranteed that there is no collation, and every\nRedis instance will reply with the same output).\n\nThe main commands to operate with lexicographical ranges are [`ZRANGEBYLEX`](https://redis.io/docs/latest/commands/zrangebylex),\n[`ZREVRANGEBYLEX`](https://redis.io/docs/latest/commands/zrevrangebylex), [`ZREMRANGEBYLEX`](https://redis.io/docs/latest/commands/zremrangebylex) and [`ZLEXCOUNT`](https://redis.io/docs/latest/commands/zlexcount).\n\nFor example, let's add again our list of famous racers, but this time\nusing a score of zero for all the elements. We'll see that because of the sorted sets ordering rules, they are already sorted lexicographically. Using [`ZRANGEBYLEX`](https://redis.io/docs/latest/commands/zrangebylex) we can ask for lexicographical ranges:\n\nLexicographical queries: Add members with identical scores and use ZRANGEBYLEX to query by string range (enables generic indexing)\n\n**Difficulty:** Intermediate\n\n**Builds upon:** zadd\n\n**Commands:** ZADD, ZRANGE, ZRANGEBYLEX\n\n**Complexity:**\n- ZADD: O(log(N)\n- ZRANGE: O(log(N)\n- ZRANGEBYLEX: O(log(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nRanges can be inclusive or exclusive (depending on the first character),\nalso string infinite and minus infinite are specified respectively with\nthe `+` and `-` strings. See the documentation for more information.\n\nThis feature is important because it allows us to use sorted sets as a generic\nindex. For example, if you want to index elements by a 128-bit unsigned\ninteger argument, all you need to do is to add elements into a sorted\nset with the same score (for example 0) but with a 16 byte prefix\nconsisting of **the 128 bit number in big endian**. Since numbers in big\nendian, when ordered lexicographically (in raw bytes order) are actually\nordered numerically as well, you can ask for ranges in the 128 bit space,\nand get the element's value discarding the prefix\n\nUpdating the score: leaderboards\n---\n\nJust a final note about sorted sets before switching to the next topic.\nSorted sets' scores can be updated at any time. Just calling [`ZADD`](https://redis.io/docs/latest/commands/zadd) against\nan element already included in the sorted set will update its score\n(and position) with O(log(N)) time complexity.  As such, sorted sets are suitable\nwhen there are tons of updates.\n\nBecause of this characteristic a common use case is leaderboards.\nThe typical application is a Facebook game where you combine the ability to\ntake users sorted by their high score, plus the get-rank operation, in order\nto show the top-N users, and the user rank in the leader board (e.g., \"you are\nthe #4932 best score here\")."},{"id":"examples","title":"Examples","role":"example","text":"* There are two ways we can use a sorted set to represent a leaderboard. If we know a racer's new score, we can update it directly via the [`ZADD`](https://redis.io/docs/latest/commands/zadd) command. However, if we want to add points to an existing score, we can use the [`ZINCRBY`](https://redis.io/docs/latest/commands/zincrby) command.\nPractical pattern: Use ZADD to set scores and ZINCRBY to increment them when you need to update leaderboards with atomic operations\n\n**Difficulty:** Intermediate\n\n**Builds upon:** zadd\n\n**Commands:** ZADD, ZINCRBY\n\n**Complexity:**\n- ZADD: O(log(N)\n- ZINCRBY: O(log(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nYou'll see that [`ZADD`](https://redis.io/docs/latest/commands/zadd) returns 0 when the member already exists (the score is updated), while [`ZINCRBY`](https://redis.io/docs/latest/commands/zincrby) returns the new score. The score for racer Henshaw went from 100, was changed to 150 with no regard for what score was there before, and then was incremented by 50 to 200."},{"id":"performance","title":"Performance","role":"performance","text":"Most sorted set operations are O(log(n)), where _n_ is the number of members.\n\nExercise some caution when running the [`ZRANGE`](https://redis.io/docs/latest/commands/zrange) command with large returns values (e.g., in the tens of thousands or more).\nThis command's time complexity is O(log(n) + m), where _m_ is the number of results returned."},{"id":"alternatives","title":"Alternatives","role":"content","text":"Redis sorted sets are sometimes used for indexing other Redis data structures.\nIf you need to index and query your data, consider the [JSON](https://redis.io/docs/latest/develop/data-types/json) data type and the [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) features."},{"id":"learn-more","title":"Learn more","role":"related","text":"* [Redis Sorted Sets Explained](https://www.youtube.com/watch?v=MUKlxdBQZ7g) is an entertaining introduction to sorted sets in Redis.\n* [Redis University's RU101](https://university.redis.com/courses/ru101/) explores Redis sorted sets in detail."}],"examples":[{"id":"sorted-set-command-summary-ex0","language":"plaintext","code":"> ZADD racer_scores 10 \"Norem\"\n(integer) 1\n> ZADD racer_scores 12 \"Castilla\"\n(integer) 1\n> ZADD racer_scores 8 \"Sam-Bodden\" 10 \"Royce\" 6 \"Ford\" 14 \"Prickett\"\n(integer) 4","section_id":"sorted-set-command-summary"},{"id":"sorted-set-command-summary-ex1","language":"csharp","code":"bool res1 = db.SortedSetAdd(\"racer_scores\", \"Norem\", 10);\n        Console.WriteLine(res1); // >>> True\n\n        bool res2 = db.SortedSetAdd(\"racer_scores\", \"Castilla\", 12);\n        Console.WriteLine(res2); // >>> True\n\n        long res3 = db.SortedSetAdd(\"racer_scores\", [\n            new(\"Sam-Bodden\", 8),\n            new(\"Royce\", 10),\n            new(\"Ford\", 6),\n            new(\"Prickett\", 14),\n            new(\"Castilla\", 12)\n        ]);\n        Console.WriteLine(res3); // >>> 4","section_id":"sorted-set-command-summary"},{"id":"sorted-set-command-summary-ex2","language":"go","code":"res1, err := rdb.ZAdd(ctx, \"racer_scores\",\n\t\tredis.Z{Member: \"Norem\", Score: 10},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> 1\n\n\tres2, err := rdb.ZAdd(ctx, \"racer_scores\",\n\t\tredis.Z{Member: \"Castilla\", Score: 12},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> 1\n\n\tres3, err := rdb.ZAdd(ctx, \"racer_scores\",\n\t\tredis.Z{Member: \"Norem\", Score: 10},\n\t\tredis.Z{Member: \"Sam-Bodden\", Score: 8},\n\t\tredis.Z{Member: \"Royce\", Score: 10},\n\t\tredis.Z{Member: \"Ford\", Score: 6},\n\t\tredis.Z{Member: \"Prickett\", Score: 14},\n\t\tredis.Z{Member: \"Castilla\", Score: 12},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3) // >>> 4","section_id":"sorted-set-command-summary"},{"id":"sorted-set-command-summary-ex3","language":"java","code":"CompletableFuture<Void> zadd = asyncCommands.zadd(\"racer_scores\", ScoredValue.just(10d, \"Norem\"))\n                    .thenCompose(res1 -> {\n                        System.out.println(res1); // >>> 1\n\n                        return asyncCommands.zadd(\"racer_scores\", ScoredValue.just(12d, \"Castilla\"));\n                    }).thenCompose(res2 -> {\n                        System.out.println(res2); // >>> 1\n\n                        return asyncCommands.zadd(\"racer_scores\", ScoredValue.just(8d, \"Sam-Bodden\"),\n                                ScoredValue.just(10d, \"Royce\"), ScoredValue.just(6d, \"Ford\"),\n                                ScoredValue.just(14d, \"Prickett\"));\n                    })\n                    .thenAccept(System.out::println) // >>> 4\n                    .toCompletableFuture();","section_id":"sorted-set-command-summary"},{"id":"sorted-set-command-summary-ex4","language":"java","code":"Mono<Void> zadd = reactiveCommands.zadd(\"racer_scores\", ScoredValue.just(10d, \"Norem\")).doOnNext(result -> {\n                System.out.println(result); // >>> 1\n            }).flatMap(v -> reactiveCommands.zadd(\"racer_scores\", ScoredValue.just(12d, \"Castilla\"))).doOnNext(result -> {\n                System.out.println(result); // >>> 1\n            }).flatMap(v -> reactiveCommands.zadd(\"racer_scores\", ScoredValue.just(8d, \"Sam-Bodden\"),\n                    ScoredValue.just(10d, \"Royce\"), ScoredValue.just(6d, \"Ford\"),\n                    ScoredValue.just(14d, \"Prickett\"))).doOnNext(result -> {\n                        System.out.println(result); // >>> 4\n                    }).then();","section_id":"sorted-set-command-summary"},{"id":"sorted-set-command-summary-ex5","language":"java","code":"long res1 = jedis.zadd(\"racer_scores\", 10d, \"Norem\");\n    System.out.println(res1); // >>> 1\n\n    long res2 = jedis.zadd(\"racer_scores\", 12d, \"Castilla\");\n    System.out.println(res2); // >>> 1\n\n    long res3 = jedis.zadd(\"racer_scores\", new HashMap<String,Double>() {{\n      put(\"Sam-Bodden\", 8d);\n      put(\"Royce\", 10d);\n      put(\"Ford\", 6d);\n      put(\"Prickett\", 14d);\n      put(\"Castilla\", 12d);\n    }});\n    System.out.println(res3); // >>> 4","section_id":"sorted-set-command-summary"},{"id":"sorted-set-command-summary-ex6","language":"javascript","code":"const res1 = await client.zAdd('racer_scores', { score: 10, value: 'Norem' });\nconsole.log(res1);  // >>> 1\n\nconst res2 = await client.zAdd('racer_scores', { score: 12, value: 'Castilla' });\nconsole.log(res2);  // >>> 1\n\nconst res3 = await client.zAdd('racer_scores', [\n  { score: 8, value: 'Sam-Bodden' },\n  { score: 10, value: 'Royce' },\n  { score: 6, value: 'Ford' },\n  { score: 14, value: 'Prickett' },\n  { score: 12, value: 'Castilla' }\n]);\nconsole.log(res3);  // >>> 4","section_id":"sorted-set-command-summary"},{"id":"sorted-set-command-summary-ex7","language":"php","code":"$res1 = $r->zadd('racer_scores', ['Norem' => 10]);\n        echo $res1 . PHP_EOL;\n        // >>> 1\n\n        $res2 = $r->zadd('racer_scores', ['Castilla' => 12]);\n        echo $res2 . PHP_EOL;\n        // >>> 1\n\n        $res3 = $r->zadd('racer_scores', [\n            'Sam-Bodden' => 8,\n            'Royce' => 10,\n            'Ford' => 6,\n            'Prickett' => 14,\n            'Castilla' => 12,\n        ]);\n        echo $res3 . PHP_EOL;\n        // >>> 4","section_id":"sorted-set-command-summary"},{"id":"sorted-set-command-summary-ex8","language":"python","code":"res1 = r.zadd(\"racer_scores\", {\"Norem\": 10})\nprint(res1)  # >>> 1\n\nres2 = r.zadd(\"racer_scores\", {\"Castilla\": 12})\nprint(res2)  # >>> 1\n\nres3 = r.zadd(\n    \"racer_scores\",\n    {\"Sam-Bodden\": 8, \"Royce\": 10, \"Ford\": 6, \"Prickett\": 14, \"Castilla\": 12},\n)\nprint(res3)  # >>> 4","section_id":"sorted-set-command-summary"},{"id":"sorted-set-command-summary-ex9","language":"ruby","code":"res1 = r.zadd('racer_scores', [[10, 'Norem']])\nputs res1 # >>> 1\n\nres2 = r.zadd('racer_scores', [[12, 'Castilla']])\nputs res2 # >>> 1\n\nres3 = r.zadd('racer_scores', [\n  [8, 'Sam-Bodden'],\n  [10, 'Royce'],\n  [6, 'Ford'],\n  [14, 'Prickett'],\n  [12, 'Castilla']\n])\nputs res3 # >>> 4","section_id":"sorted-set-command-summary"},{"id":"sorted-set-command-summary-ex10","language":"rust","code":"if let Ok(res) = r.zadd(\"racer_scores\", \"Norem\", 10).await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 1\n        }\n\n        if let Ok(res) = r.zadd(\"racer_scores\", \"Castilla\", 12).await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 1\n        }\n\n        if let Ok(res) = r\n            .zadd_multiple(\n                \"racer_scores\",\n                &[\n                    (8, \"Sam-Bodden\"),\n                    (10, \"Royce\"),\n                    (6, \"Ford\"),\n                    (14, \"Prickett\"),\n                    (12, \"Castilla\"),\n                ],\n            )\n            .await\n        {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 4\n        }","section_id":"sorted-set-command-summary"},{"id":"sorted-set-command-summary-ex11","language":"rust","code":"if let Ok(res) = r.zadd(\"racer_scores\", \"Norem\", 10) {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 1\n        }\n\n        if let Ok(res) = r.zadd(\"racer_scores\", \"Castilla\", 12) {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 1\n        }\n\n        if let Ok(res) = r.zadd_multiple(\n            \"racer_scores\",\n            &[\n                (8, \"Sam-Bodden\"),\n                (10, \"Royce\"),\n                (6, \"Ford\"),\n                (14, \"Prickett\"),\n                (12, \"Castilla\"),\n            ],\n        ) {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 4\n        }","section_id":"sorted-set-command-summary"},{"id":"sorted-set-command-summary-ex12","language":"plaintext","code":"> ZRANGE racer_scores 0 -1\n1) \"Ford\"\n2) \"Sam-Bodden\"\n3) \"Norem\"\n4) \"Royce\"\n5) \"Castilla\"\n6) \"Prickett\"\n> ZREVRANGE racer_scores 0 -1\n1) \"Prickett\"\n2) \"Castilla\"\n3) \"Royce\"\n4) \"Norem\"\n5) \"Sam-Bodden\"\n6) \"Ford\"","section_id":"sorted-set-command-summary"},{"id":"sorted-set-command-summary-ex13","language":"csharp","code":"RedisValue[] res4 = db.SortedSetRangeByRank(\"racer_scores\", 0, -1);\n        Console.WriteLine(string.Join(\", \", res4)); // >>> Ford, Sam-Bodden, Norem, Royce, Castilla, Prickett\n\n        RedisValue[] res5 = db.SortedSetRangeByRank(\"racer_scores\", 0, -1, Order.Descending);\n        Console.WriteLine(string.Join(\", \", res5)); // >>> Prickett, Castilla, Royce, Norem, Sam-Bodden, Ford","section_id":"sorted-set-command-summary"},{"id":"sorted-set-command-summary-ex14","language":"go","code":"res4, err := rdb.ZRange(ctx, \"racer_scores\", 0, -1).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4)\n\t// >>> [Ford Sam-Bodden Norem Royce Castilla Prickett]\n\n\tres5, err := rdb.ZRevRange(ctx, \"racer_scores\", 0, -1).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res5)\n\t// >>> [Prickett Castilla Royce Norem Sam-Bodden Ford]","section_id":"sorted-set-command-summary"},{"id":"sorted-set-command-summary-ex15","language":"java","code":"CompletableFuture<Void> zrange = asyncCommands.zrange(\"racer_scores\", 0, -1).thenCompose(res3 -> {\n                System.out.println(res3);\n                // >>> [Ford, Sam-Bodden, Norem, Royce, Castilla, Prickett]\n\n                return asyncCommands.zrevrange(\"racer_scores\", 0, -1);\n            })\n                    .thenAccept(System.out::println)\n                    // >>> [Prickett, Castilla, Royce, Norem, Sam-Bodden, Ford]\n                    .toCompletableFuture();","section_id":"sorted-set-command-summary"},{"id":"sorted-set-command-summary-ex16","language":"java","code":"Mono<Void> zrange = reactiveCommands.zrange(\"racer_scores\", 0, -1).collectList().doOnNext(result -> {\n                System.out.println(result);\n                // >>> [Ford, Sam-Bodden, Norem, Royce, Castilla, Prickett]\n            }).flatMap(v -> reactiveCommands.zrevrange(\"racer_scores\", 0, -1).collectList()).doOnNext(result -> {\n                System.out.println(result);\n                // >>> [Prickett, Castilla, Royce, Norem, Sam-Bodden, Ford]\n            }).then();","section_id":"sorted-set-command-summary"},{"id":"sorted-set-command-summary-ex17","language":"java","code":"List<String> res4 = jedis.zrange(\"racer_scores\", 0, -1);\n    System.out.println(res4); // >>> [Ford, Sam-Bodden, Norem, Royce, Castil, Castilla, Prickett]\n\n    List<String> res5 = jedis.zrevrange(\"racer_scores\", 0, -1);\n    System.out.println(res5); // >>> [Prickett, Castilla, Castil, Royce, Norem, Sam-Bodden, Ford]","section_id":"sorted-set-command-summary"},{"id":"sorted-set-command-summary-ex18","language":"javascript","code":"const res4 = await client.zRange('racer_scores', 0, -1);\nconsole.log(res4);  // >>> ['Ford', 'Sam-Bodden', 'Norem', 'Royce', 'Castilla', 'Prickett']","section_id":"sorted-set-command-summary"},{"id":"sorted-set-command-summary-ex19","language":"php","code":"$res4 = $r->zrange('racer_scores', 0, -1);\n        echo json_encode($res4) . PHP_EOL;\n        // >>> [\"Ford\",\"Sam-Bodden\",\"Norem\",\"Royce\",\"Castilla\",\"Prickett\"]\n\n        $res5 = $r->zrevrange('racer_scores', 0, -1);\n        echo json_encode($res5) . PHP_EOL;\n        // >>> [\"Prickett\",\"Castilla\",\"Royce\",\"Norem\",\"Sam-Bodden\",\"Ford\"]","section_id":"sorted-set-command-summary"},{"id":"sorted-set-command-summary-ex20","language":"python","code":"res4 = r.zrange(\"racer_scores\", 0, -1)\nprint(res4)  # >>> ['Ford', 'Sam-Bodden', 'Norem', 'Royce', 'Castilla', 'Prickett']\n\nres5 = r.zrevrange(\"racer_scores\", 0, -1)\nprint(res5)  # >>> ['Prickett', 'Castilla', 'Royce', 'Norem', 'Sam-Bodden', 'Ford']","section_id":"sorted-set-command-summary"},{"id":"sorted-set-command-summary-ex21","language":"ruby","code":"res4 = r.zrange('racer_scores', 0, -1)\nputs res4.inspect # >>> [\"Ford\", \"Sam-Bodden\", \"Norem\", \"Royce\", \"Castilla\", \"Prickett\"]\n\nres5 = r.zrevrange('racer_scores', 0, -1)\nputs res5.inspect # >>> [\"Prickett\", \"Castilla\", \"Royce\", \"Norem\", \"Sam-Bodden\", \"Ford\"]","section_id":"sorted-set-command-summary"},{"id":"sorted-set-command-summary-ex22","language":"rust","code":"if let Ok(res) = r.zrange(\"racer_scores\", 0, -1).await {\n            let res: Vec<String> = res;\n            println!(\"{res:?}\");\n            // >>> [\"Ford\", \"Sam-Bodden\", \"Norem\", \"Royce\", \"Castilla\", \"Prickett\"]\n        }\n\n        if let Ok(res) = r.zrevrange(\"racer_scores\", 0, -1).await {\n            let res: Vec<String> = res;\n            println!(\"{res:?}\");\n            // >>> [\"Prickett\", \"Castilla\", \"Royce\", \"Norem\", \"Sam-Bodden\", \"Ford\"]\n        }","section_id":"sorted-set-command-summary"},{"id":"sorted-set-command-summary-ex23","language":"rust","code":"if let Ok(res) = r.zrange(\"racer_scores\", 0, -1) {\n            let res: Vec<String> = res;\n            println!(\"{res:?}\");\n            // >>> [\"Ford\", \"Sam-Bodden\", \"Norem\", \"Royce\", \"Castilla\", \"Prickett\"]\n        }\n\n        if let Ok(res) = r.zrevrange(\"racer_scores\", 0, -1) {\n            let res: Vec<String> = res;\n            println!(\"{res:?}\");\n            // >>> [\"Prickett\", \"Castilla\", \"Royce\", \"Norem\", \"Sam-Bodden\", \"Ford\"]\n        }","section_id":"sorted-set-command-summary"},{"id":"sorted-set-command-summary-ex24","language":"plaintext","code":"> ZRANGE racer_scores 0 -1 withscores\n 1) \"Ford\"\n 2) \"6\"\n 3) \"Sam-Bodden\"\n 4) \"8\"\n 5) \"Norem\"\n 6) \"10\"\n 7) \"Royce\"\n 8) \"10\"\n 9) \"Castilla\"\n10) \"12\"\n11) \"Prickett\"\n12) \"14\"","section_id":"sorted-set-command-summary"},{"id":"sorted-set-command-summary-ex25","language":"csharp","code":"SortedSetEntry[] res6 = db.SortedSetRangeByRankWithScores(\"racer_scores\", 0, -1);\n        Console.WriteLine(string.Join(\", \", res6)); // >>> Ford: 6, Sam-Bodden: 8, Norem: 10, Royce: 10, Castilla: 12, Prickett: 14","section_id":"sorted-set-command-summary"},{"id":"sorted-set-command-summary-ex26","language":"go","code":"res6, err := rdb.ZRangeWithScores(ctx, \"racer_scores\", 0, -1).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res6)\n\t// >>> [{6 Ford} {8 Sam-Bodden} {10 Norem} {10 Royce} {12 Castilla} {14 Prickett}]","section_id":"sorted-set-command-summary"},{"id":"sorted-set-command-summary-ex27","language":"java","code":"CompletableFuture<Void> zrangeWithScores = asyncCommands.zrangeWithScores(\"racer_scores\", 0, -1)\n                    .thenAccept(System.out::println)\n                    // >>> [ScoredValue[6.000000, Ford], ScoredValue[8.000000, Sam-Bodden]...\n                    .toCompletableFuture();","section_id":"sorted-set-command-summary"},{"id":"sorted-set-command-summary-ex28","language":"java","code":"Mono<Void> zrangeWithScores = reactiveCommands.zrangeWithScores(\"racer_scores\", 0, -1).collectList()\n                    .doOnNext(result -> {\n                        System.out.println(result);\n                        // >>> [ScoredValue[6.000000, Ford], ScoredValue[8.000000, Sam-Bodden]...\n                    }).then();","section_id":"sorted-set-command-summary"},{"id":"sorted-set-command-summary-ex29","language":"java","code":"List<Tuple> res6 = jedis.zrangeWithScores(\"racer_scores\", 0, -1);\n    System.out.println(res6); // >>> [[Ford,6.0], [Sam-Bodden,8.0], [Norem,10.0], [Royce,10.0], [Castil,12.0], [Castilla,12.0], [Prickett,14.0]]","section_id":"sorted-set-command-summary"},{"id":"sorted-set-command-summary-ex30","language":"javascript","code":"const res6 = await client.zRangeWithScores('racer_scores', 0, -1);\nconsole.log(res6);\n// >>> [\n//       { value: 'Ford', score: 6 }, { value: 'Sam-Bodden', score: 8 },\n//       { value: 'Norem', score: 10 }, { value: 'Royce', score: 10 },\n//       { value: 'Castilla', score: 12 }, { value: 'Prickett', score: 14 }\n// ]","section_id":"sorted-set-command-summary"},{"id":"sorted-set-command-summary-ex31","language":"php","code":"$res6 = $r->zrange('racer_scores', 0, -1,[\n            'withscores' => true,\n        ]);\n        echo json_encode($res6) . PHP_EOL;\n        // >>> {\"Ford\":\"6\",\"Sam-Bodden\":\"8\",\"Norem\":\"10\",\"Royce\":\"10\",\"Castilla\":\"12\",\"Prickett\":\"14\"}","section_id":"sorted-set-command-summary"},{"id":"sorted-set-command-summary-ex32","language":"python","code":"res6 = r.zrange(\"racer_scores\", 0, -1, withscores=True)\nprint(\n    res6\n)\n# >>> [\n#       ('Ford', 6.0), ('Sam-Bodden', 8.0), ('Norem', 10.0), ('Royce', 10.0),\n#       ('Castilla', 12.0), ('Prickett', 14.0)\n# ]","section_id":"sorted-set-command-summary"},{"id":"sorted-set-command-summary-ex33","language":"ruby","code":"res6 = r.zrange('racer_scores', 0, -1, with_scores: true)\nputs res6.inspect\n# >>> [[\"Ford\", 6.0], [\"Sam-Bodden\", 8.0], [\"Norem\", 10.0], [\"Royce\", 10.0],\n#  [\"Castilla\", 12.0], [\"Prickett\", 14.0]]","section_id":"sorted-set-command-summary"},{"id":"sorted-set-command-summary-ex34","language":"rust","code":"if let Ok(res) = r.zrange_withscores(\"racer_scores\", 0, -1).await {\n            let res: Vec<(String, f64)> = res;\n            println!(\"{res:?}\");\n            // >>> [(\"Ford\", 6.0), (\"Sam-Bodden\", 8.0), (\"Norem\", 10.0), (\"Royce\", 10.0), (\"Castilla\", 12.0), (\"Prickett\", 14.0)]\n        }","section_id":"sorted-set-command-summary"},{"id":"sorted-set-command-summary-ex35","language":"rust","code":"if let Ok(res) = r.zrange_withscores(\"racer_scores\", 0, -1) {\n            let res: Vec<(String, f64)> = res;\n            println!(\"{res:?}\");\n            // >>> [(\"Ford\", 6.0), (\"Sam-Bodden\", 8.0), (\"Norem\", 10.0), (\"Royce\", 10.0), (\"Castilla\", 12.0), (\"Prickett\", 14.0)]\n        }","section_id":"sorted-set-command-summary"},{"id":"operating-on-ranges-ex0","language":"plaintext","code":"> ZRANGEBYSCORE racer_scores -inf 10\n1) \"Ford\"\n2) \"Sam-Bodden\"\n3) \"Norem\"\n4) \"Royce\"","section_id":"operating-on-ranges"},{"id":"operating-on-ranges-ex1","language":"csharp","code":"RedisValue[] res7 = db.SortedSetRangeByScore(\"racer_scores\", double.NegativeInfinity, 10);\n        Console.WriteLine(string.Join(\", \", res7)); // >>> Ford, Sam-Bodden, Norem, Royce","section_id":"operating-on-ranges"},{"id":"operating-on-ranges-ex2","language":"go","code":"res7, err := rdb.ZRangeByScore(ctx, \"racer_scores\",\n\t\t&redis.ZRangeBy{Min: \"-inf\", Max: \"10\"},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res7)\n\t// >>> [Ford Sam-Bodden Norem Royce]","section_id":"operating-on-ranges"},{"id":"operating-on-ranges-ex3","language":"java","code":"CompletableFuture<Void> zrangebyscore = asyncCommands\n                    .zrangebyscore(\"racer_scores\", Range.create(Double.NEGATIVE_INFINITY, 10))\n                    .thenAccept(System.out::println)\n                    // >>> [Ford, Sam-Bodden, Norem, Royce]\n                    .toCompletableFuture();","section_id":"operating-on-ranges"},{"id":"operating-on-ranges-ex4","language":"java","code":"Mono<Void> zrangebyscore = reactiveCommands\n                    .zrangebyscore(\"racer_scores\", Range.create(Double.NEGATIVE_INFINITY, 10)).collectList().doOnNext(result -> {\n                        System.out.println(result); // >>> [Ford, Sam-Bodden, Norem, Royce]\n                    }).then();","section_id":"operating-on-ranges"},{"id":"operating-on-ranges-ex5","language":"java","code":"List<String> res7 = jedis.zrangeByScore(\"racer_scores\", Double.NEGATIVE_INFINITY, 10d);\n    System.out.println(res7); // >>> [Ford, Sam-Bodden, Norem, Royce]","section_id":"operating-on-ranges"},{"id":"operating-on-ranges-ex6","language":"javascript","code":"const res7 = await client.zRangeByScore('racer_scores', '-inf', 10);\nconsole.log(res7);  // >>> ['Ford', 'Sam-Bodden', 'Norem', 'Royce']","section_id":"operating-on-ranges"},{"id":"operating-on-ranges-ex7","language":"php","code":"$res7 = $r->zrangebyscore('racer_scores', '-inf', 10);\n        echo json_encode($res7) . PHP_EOL;\n        // >>> [\"Ford\",\"Sam-Bodden\",\"Norem\",\"Royce\"]","section_id":"operating-on-ranges"},{"id":"operating-on-ranges-ex8","language":"python","code":"res7 = r.zrangebyscore(\"racer_scores\", \"-inf\", 10)\nprint(res7)  # >>> ['Ford', 'Sam-Bodden', 'Norem', 'Royce']","section_id":"operating-on-ranges"},{"id":"operating-on-ranges-ex9","language":"ruby","code":"res7 = r.zrangebyscore('racer_scores', '-inf', 10)\nputs res7.inspect # >>> [\"Ford\", \"Sam-Bodden\", \"Norem\", \"Royce\"]","section_id":"operating-on-ranges"},{"id":"operating-on-ranges-ex10","language":"rust","code":"if let Ok(res) = r.zrangebyscore(\"racer_scores\", \"-inf\", 10).await {\n            let res: Vec<String> = res;\n            println!(\"{res:?}\"); // >>> [\"Ford\", \"Sam-Bodden\", \"Norem\", \"Royce\"]\n        }","section_id":"operating-on-ranges"},{"id":"operating-on-ranges-ex11","language":"rust","code":"if let Ok(res) = r.zrangebyscore(\"racer_scores\", \"-inf\", 10) {\n            let res: Vec<String> = res;\n            println!(\"{res:?}\"); // >>> [\"Ford\", \"Sam-Bodden\", \"Norem\", \"Royce\"]\n        }","section_id":"operating-on-ranges"},{"id":"operating-on-ranges-ex12","language":"plaintext","code":"> ZREM racer_scores \"Castilla\"\n(integer) 1\n> ZREMRANGEBYSCORE racer_scores -inf 9\n(integer) 2\n> ZRANGE racer_scores 0 -1\n1) \"Norem\"\n2) \"Royce\"\n3) \"Prickett\"","section_id":"operating-on-ranges"},{"id":"operating-on-ranges-ex13","language":"csharp","code":"bool res8 = db.SortedSetRemove(\"racer_scores\", \"Castilla\");\n        Console.WriteLine(res8); // >>> True\n\n        long res9 = db.SortedSetRemoveRangeByScore(\"racer_scores\", double.NegativeInfinity, 9);\n        Console.WriteLine(res9); // >>> 2\n\n        RedisValue[] res10 = db.SortedSetRangeByRank(\"racer_scores\", 0, -1);\n        Console.WriteLine(string.Join(\", \", res10)); // >>> Norem, Royce, Prickett","section_id":"operating-on-ranges"},{"id":"operating-on-ranges-ex14","language":"go","code":"res8, err := rdb.ZRem(ctx, \"racer_scores\", \"Castilla\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res8) // >>> 1\n\n\tres9, err := rdb.ZRemRangeByScore(ctx, \"racer_scores\", \"-inf\", \"9\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res9) // >>> 2\n\n\tres10, err := rdb.ZRange(ctx, \"racer_scores\", 0, -1).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res10)\n\t// >>> [Norem Royce Prickett]","section_id":"operating-on-ranges"},{"id":"operating-on-ranges-ex15","language":"java","code":"CompletableFuture<Void> zremrangebyscore = asyncCommands.zrem(\"racer_scores\", \"Castilla\").thenCompose(res4 -> {\n                System.out.println(res4); // >>> 1\n\n                return asyncCommands.zremrangebyscore(\"racer_scores\", Range.create(Double.NEGATIVE_INFINITY, 9));\n            }).thenCompose(res5 -> {\n                System.out.println(res5); // >>> 2\n\n                return asyncCommands.zrange(\"racer_scores\", 0, -1);\n            })\n                    .thenAccept(System.out::println)\n                    // >>> [Norem, Royce, Prickett]\n                    .toCompletableFuture();","section_id":"operating-on-ranges"},{"id":"operating-on-ranges-ex16","language":"java","code":"Mono<Void> zremrangebyscore = reactiveCommands.zrem(\"racer_scores\", \"Castilla\").doOnNext(result -> {\n                System.out.println(result); // >>> 1\n            }).flatMap(v -> reactiveCommands.zremrangebyscore(\"racer_scores\", Range.create(Double.NEGATIVE_INFINITY, 9)))\n                    .doOnNext(result -> {\n                        System.out.println(result); // >>> 2\n                    }).flatMap(v -> reactiveCommands.zrange(\"racer_scores\", 0, -1).collectList()).doOnNext(result -> {\n                        System.out.println(result); // >>> [Norem, Royce, Prickett]\n                    }).then();","section_id":"operating-on-ranges"},{"id":"operating-on-ranges-ex17","language":"java","code":"long res8 = jedis.zrem(\"racer_scores\", \"Castilla\");\n    System.out.println(res8); // >>> 1\n\n    long res9 = jedis.zremrangeByScore(\"racer_scores\", Double.NEGATIVE_INFINITY, 9d);\n    System.out.println(res9); // >>> 2\n\n    List<String> res10 = jedis.zrange(\"racer_scores\", 0, -1);\n    System.out.println(res10); // >>> [Norem, Royce, Prickett]","section_id":"operating-on-ranges"},{"id":"operating-on-ranges-ex18","language":"javascript","code":"const res8 = await client.zRem('racer_scores', 'Castilla');\nconsole.log(res8);  // >>> 1\n\nconst res9 = await client.zRemRangeByScore('racer_scores', '-inf', 9);\nconsole.log(res9);  // >>> 2\n\n\nconst res10 = await client.zRange('racer_scores', 0, -1);\nconsole.log(res10);  // >>> ['Norem', 'Royce', 'Prickett']","section_id":"operating-on-ranges"},{"id":"operating-on-ranges-ex19","language":"php","code":"$res8 = $r->zrem('racer_scores', 'Castilla');\n        echo $res8 . PHP_EOL;\n        // >>> 1\n\n        $res9 = $r->zremrangebyscore('racer_scores', '-inf', 9);\n        echo $res9 . PHP_EOL;\n        // >>> 2\n\n        $res10 = $r->zrange('racer_scores', 0, -1);\n        echo json_encode($res10) . PHP_EOL;\n        // >>> [\"Norem\",\"Royce\",\"Prickett\"]","section_id":"operating-on-ranges"},{"id":"operating-on-ranges-ex20","language":"python","code":"res8 = r.zrem(\"racer_scores\", \"Castilla\")\nprint(res8)  # >>> 1\n\nres9 = r.zremrangebyscore(\"racer_scores\", \"-inf\", 9)\nprint(res9)  # >>> 2\n\nres10 = r.zrange(\"racer_scores\", 0, -1)\nprint(res10)  # >>> ['Norem', 'Royce', 'Prickett']","section_id":"operating-on-ranges"},{"id":"operating-on-ranges-ex21","language":"ruby","code":"res8 = r.zrem('racer_scores', ['Castilla'])\nputs res8 # >>> 1\n\nres9 = r.zremrangebyscore('racer_scores', '-inf', 9)\nputs res9 # >>> 2\n\nres10 = r.zrange('racer_scores', 0, -1)\nputs res10.inspect # >>> [\"Norem\", \"Royce\", \"Prickett\"]","section_id":"operating-on-ranges"},{"id":"operating-on-ranges-ex22","language":"rust","code":"if let Ok(res) = r.zrem(\"racer_scores\", \"Castilla\").await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 1\n        }\n\n        if let Ok(res) = r.zrembyscore(\"racer_scores\", \"-inf\", 9).await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 2\n        }\n\n        if let Ok(res) = r.zrange(\"racer_scores\", 0, -1).await {\n            let res: Vec<String> = res;\n            println!(\"{res:?}\"); // >>> [\"Norem\", \"Royce\", \"Prickett\"]\n        }","section_id":"operating-on-ranges"},{"id":"operating-on-ranges-ex23","language":"rust","code":"if let Ok(res) = r.zrem(\"racer_scores\", \"Castilla\") {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 1\n        }\n\n        if let Ok(res) = r.zrembyscore(\"racer_scores\", \"-inf\", 9) {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 2\n        }\n\n        if let Ok(res) = r.zrange(\"racer_scores\", 0, -1) {\n            let res: Vec<String> = res;\n            println!(\"{res:?}\"); // >>> [\"Norem\", \"Royce\", \"Prickett\"]\n        }","section_id":"operating-on-ranges"},{"id":"operating-on-ranges-ex24","language":"plaintext","code":"# Recreate the three remaining racers so this example runs on its own.\n> DEL racer_scores\n(integer) 1\n> ZADD racer_scores 10 \"Norem\" 10 \"Royce\" 14 \"Prickett\"\n(integer) 3\n> ZRANK racer_scores \"Norem\"\n(integer) 0\n> ZREVRANK racer_scores \"Norem\"\n(integer) 2","section_id":"operating-on-ranges"},{"id":"operating-on-ranges-ex25","language":"csharp","code":"// Recreate the three remaining racers so this example runs on its own.\n        db.KeyDelete(\"racer_scores\");\n        db.SortedSetAdd(\"racer_scores\", [\n            new(\"Norem\", 10),\n            new(\"Royce\", 10),\n            new(\"Prickett\", 14)\n        ]);\n\n        long? res11 = db.SortedSetRank(\"racer_scores\", \"Norem\");\n        Console.WriteLine(res11); // >>> 0\n\n        long? res12 = db.SortedSetRank(\"racer_scores\", \"Norem\", Order.Descending);\n        Console.WriteLine(res12); // >>> 2","section_id":"operating-on-ranges"},{"id":"operating-on-ranges-ex26","language":"go","code":"// Recreate the three remaining racers so this example runs on its own.\n\trdb.Del(ctx, \"racer_scores\")\n\trdb.ZAdd(ctx, \"racer_scores\",\n\t\tredis.Z{Member: \"Norem\", Score: 10},\n\t\tredis.Z{Member: \"Royce\", Score: 10},\n\t\tredis.Z{Member: \"Prickett\", Score: 14},\n\t)\n\n\tres11, err := rdb.ZRank(ctx, \"racer_scores\", \"Norem\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res11) // >>> 0\n\n\tres12, err := rdb.ZRevRank(ctx, \"racer_scores\", \"Norem\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res12) // >>> 2","section_id":"operating-on-ranges"},{"id":"operating-on-ranges-ex27","language":"java","code":"// Recreate the three remaining racers so this example runs on its own.\n            CompletableFuture<Void> zrank = asyncCommands.del(\"racer_scores\")\n                    .thenCompose(delRes -> asyncCommands.zadd(\"racer_scores\", ScoredValue.just(10d, \"Norem\"),\n                            ScoredValue.just(10d, \"Royce\"), ScoredValue.just(14d, \"Prickett\")))\n                    .thenCompose(addRes -> asyncCommands.zrank(\"racer_scores\", \"Norem\"))\n                    .thenCompose(res6 -> {\n                        System.out.println(res6); // >>> 0\n\n                        return asyncCommands.zrevrank(\"racer_scores\", \"Norem\");\n                    })\n                    .thenAccept(System.out::println) // >>> 2\n                    .toCompletableFuture();","section_id":"operating-on-ranges"},{"id":"operating-on-ranges-ex28","language":"java","code":"// Recreate the three remaining racers so this example runs on its own.\n            Mono<Void> zrank = reactiveCommands.del(\"racer_scores\")\n                    .flatMap(v -> reactiveCommands.zadd(\"racer_scores\", ScoredValue.just(10d, \"Norem\"),\n                            ScoredValue.just(10d, \"Royce\"), ScoredValue.just(14d, \"Prickett\")))\n                    .flatMap(v -> reactiveCommands.zrank(\"racer_scores\", \"Norem\")).doOnNext(result -> {\n                        System.out.println(result); // >>> 0\n                    }).flatMap(v -> reactiveCommands.zrevrank(\"racer_scores\", \"Norem\")).doOnNext(result -> {\n                        System.out.println(result); // >>> 2\n                    }).then();","section_id":"operating-on-ranges"},{"id":"operating-on-ranges-ex29","language":"java","code":"// Recreate the three remaining racers so this example runs on its own.\n    jedis.del(\"racer_scores\");\n    jedis.zadd(\"racer_scores\", new HashMap<String,Double>() {{\n      put(\"Norem\", 10d);\n      put(\"Royce\", 10d);\n      put(\"Prickett\", 14d);\n    }});\n\n    long res11 = jedis.zrank(\"racer_scores\", \"Norem\");\n    System.out.println(res11); // >>> 0\n\n    long res12 = jedis.zrevrank(\"racer_scores\", \"Norem\");\n    System.out.println(res12); // >>> 2","section_id":"operating-on-ranges"},{"id":"operating-on-ranges-ex30","language":"javascript","code":"// Recreate the three remaining racers so this example runs on its own.\nawait client.del('racer_scores');\nawait client.zAdd('racer_scores', [\n  { score: 10, value: 'Norem' },\n  { score: 10, value: 'Royce' },\n  { score: 14, value: 'Prickett' }\n]);\n\nconst res11 = await client.zRank('racer_scores', 'Norem');\nconsole.log(res11);  // >>> 0\n\nconst res12 = await client.zRevRank('racer_scores', 'Norem');\nconsole.log(res12);  // >>> 2","section_id":"operating-on-ranges"},{"id":"operating-on-ranges-ex31","language":"php","code":"// Recreate the three remaining racers so this example runs on its own.\n        $r->del('racer_scores');\n        $r->zadd('racer_scores', [\n            'Norem' => 10,\n            'Royce' => 10,\n            'Prickett' => 14,\n        ]);\n\n        $res11 = $r->zrank('racer_scores', 'Norem');\n        echo $res11 . PHP_EOL;\n        // >>> 0\n\n        $res12 = $r->zrevrank('racer_scores', 'Norem');\n        echo $res12 . PHP_EOL;\n        // >>> 2","section_id":"operating-on-ranges"},{"id":"operating-on-ranges-ex32","language":"python","code":"# Recreate the three remaining racers so this example runs on its own.\nr.delete(\"racer_scores\")\nr.zadd(\n    \"racer_scores\",\n    {\"Norem\": 10, \"Royce\": 10, \"Prickett\": 14},\n)\n\nres11 = r.zrank(\"racer_scores\", \"Norem\")\nprint(res11)  # >>> 0\n\nres12 = r.zrevrank(\"racer_scores\", \"Norem\")\nprint(res12)  # >>> 2","section_id":"operating-on-ranges"},{"id":"operating-on-ranges-ex33","language":"ruby","code":"# Recreate the three remaining racers so this example runs on its own.\nr.del('racer_scores')\nr.zadd('racer_scores', [[10, 'Norem'], [10, 'Royce'], [14, 'Prickett']])\n\nres11 = r.zrank('racer_scores', 'Norem')\nputs res11 # >>> 0\n\nres12 = r.zrevrank('racer_scores', 'Norem')\nputs res12 # >>> 2","section_id":"operating-on-ranges"},{"id":"operating-on-ranges-ex34","language":"rust","code":"// Recreate the three remaining racers so this example runs on its own.\n        let _: () = r.del(\"racer_scores\").await.expect(\"Failed to del\");\n        let _: usize = r\n            .zadd_multiple(\n                \"racer_scores\",\n                &[(10, \"Norem\"), (10, \"Royce\"), (14, \"Prickett\")],\n            )\n            .await\n            .expect(\"Failed to zadd\");\n\n        if let Ok(res) = r.zrank(\"racer_scores\", \"Norem\").await {\n            let res: Option<usize> = res;\n            if let Some(res) = res {\n                println!(\"{res}\"); // >>> 0\n            }\n        }\n\n        if let Ok(res) = r.zrevrank(\"racer_scores\", \"Norem\").await {\n            let res: Option<usize> = res;\n            if let Some(res) = res {\n                println!(\"{res}\"); // >>> 2\n            }\n        }","section_id":"operating-on-ranges"},{"id":"operating-on-ranges-ex35","language":"rust","code":"// Recreate the three remaining racers so this example runs on its own.\n        let _: () = r.del(\"racer_scores\").expect(\"Failed to del\");\n        let _: usize = r\n            .zadd_multiple(\n                \"racer_scores\",\n                &[(10, \"Norem\"), (10, \"Royce\"), (14, \"Prickett\")],\n            )\n            .expect(\"Failed to zadd\");\n\n        if let Ok(res) = r.zrank(\"racer_scores\", \"Norem\") {\n            let res: Option<usize> = res;\n            if let Some(res) = res {\n                println!(\"{res}\"); // >>> 0\n            }\n        }\n\n        if let Ok(res) = r.zrevrank(\"racer_scores\", \"Norem\") {\n            let res: Option<usize> = res;\n            if let Some(res) = res {\n                println!(\"{res}\"); // >>> 2\n            }\n        }","section_id":"operating-on-ranges"},{"id":"lexicographical-scores-ex0","language":"plaintext","code":"> ZADD racer_scores 0 \"Norem\" 0 \"Sam-Bodden\" 0 \"Royce\" 0 \"Castilla\" 0 \"Prickett\" 0 \"Ford\"\n(integer) 3\n> ZRANGE racer_scores 0 -1\n1) \"Castilla\"\n2) \"Ford\"\n3) \"Norem\"\n4) \"Prickett\"\n5) \"Royce\"\n6) \"Sam-Bodden\"\n> ZRANGEBYLEX racer_scores [A [L\n1) \"Castilla\"\n2) \"Ford\"","section_id":"lexicographical-scores"},{"id":"lexicographical-scores-ex1","language":"csharp","code":"long res13 = db.SortedSetAdd(\"racer_scores\", [\n            new(\"Norem\", 0),\n            new(\"Sam-Bodden\", 0),\n            new(\"Royce\", 0),\n            new(\"Ford\", 0),\n            new(\"Prickett\", 0),\n            new(\"Castilla\", 0)\n        ]);\n        Console.WriteLine(res13); // >>> 3\n\n        RedisValue[] res14 = db.SortedSetRangeByRank(\"racer_scores\", 0, -1);\n        Console.WriteLine(string.Join(\", \", res14)); // >>> Castilla, Ford, Norem, Pricket, Royce, Sam-Bodden\n\n        RedisValue[] res15 = db.SortedSetRangeByValue(\"racer_scores\", \"A\", \"L\", Exclude.None);\n        Console.WriteLine(string.Join(\", \", res15)); // >>> Castilla, Ford","section_id":"lexicographical-scores"},{"id":"lexicographical-scores-ex2","language":"go","code":"res13, err := rdb.ZAdd(ctx, \"racer_scores\",\n\t\tredis.Z{Member: \"Norem\", Score: 0},\n\t\tredis.Z{Member: \"Sam-Bodden\", Score: 0},\n\t\tredis.Z{Member: \"Royce\", Score: 0},\n\t\tredis.Z{Member: \"Ford\", Score: 0},\n\t\tredis.Z{Member: \"Prickett\", Score: 0},\n\t\tredis.Z{Member: \"Castilla\", Score: 0},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res13) // >>> 3\n\n\tres14, err := rdb.ZRange(ctx, \"racer_scores\", 0, -1).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res14)\n\t// >>> [Castilla Ford Norem Prickett Royce Sam-Bodden]\n\n\tres15, err := rdb.ZRangeByLex(ctx, \"racer_scores\", &redis.ZRangeBy{\n\t\tMin: \"[A\", Max: \"[L\",\n\t}).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res15) // >>> [Castilla Ford]","section_id":"lexicographical-scores"},{"id":"lexicographical-scores-ex3","language":"java","code":"CompletableFuture<Void> zaddLex = asyncCommands.zadd(\"racer_scores\", ScoredValue.just(0d, \"Norem\"),\n                    ScoredValue.just(0d, \"Sam-Bodden\"), ScoredValue.just(0d, \"Royce\"), ScoredValue.just(0d, \"Castilla\"),\n                    ScoredValue.just(0d, \"Prickett\"), ScoredValue.just(0d, \"Ford\")).thenCompose(res7 -> {\n                        System.out.println(res7); // >>> 3\n\n                        return asyncCommands.zrange(\"racer_scores\", 0, -1);\n                    }).thenCompose(res8 -> {\n                        System.out.println(res8);\n                        // >>> [Castilla, Ford, Norem, Prickett, Royce, Sam-Bodden]\n\n                        return asyncCommands.zrangebylex(\"racer_scores\", Range.create(\"A\", \"L\"));\n                    })\n                    .thenAccept(System.out::println)\n                    // >>> [Castilla, Ford]\n                    .toCompletableFuture();","section_id":"lexicographical-scores"},{"id":"lexicographical-scores-ex4","language":"java","code":"Mono<Void> zaddLex = reactiveCommands\n                    .zadd(\"racer_scores\", ScoredValue.just(0d, \"Norem\"), ScoredValue.just(0d, \"Sam-Bodden\"),\n                            ScoredValue.just(0d, \"Royce\"), ScoredValue.just(0d, \"Castilla\"),\n                            ScoredValue.just(0d, \"Prickett\"), ScoredValue.just(0d, \"Ford\"))\n                    .doOnNext(result -> {\n                        System.out.println(result); // >>> 3\n                    }).flatMap(v -> reactiveCommands.zrange(\"racer_scores\", 0, -1).collectList()).doOnNext(result -> {\n                        System.out.println(result);\n                        // >>> [Castilla, Ford, Norem, Prickett, Royce, Sam-Bodden]\n                    }).flatMap(v -> reactiveCommands.zrangebylex(\"racer_scores\", Range.create(\"A\", \"L\")).collectList())\n                    .doOnNext(result -> {\n                        System.out.println(result); // >>> [Castilla, Ford]\n                    }).then();","section_id":"lexicographical-scores"},{"id":"lexicographical-scores-ex5","language":"java","code":"long res13 = jedis.zadd(\"racer_scores\", new HashMap<String,Double>() {{\n      put(\"Norem\", 0d);\n      put(\"Sam-Bodden\", 0d);\n      put(\"Royce\", 0d);\n      put(\"Ford\", 0d);\n      put(\"Prickett\", 0d);\n      put(\"Castilla\", 0d);\n    }});\n    System.out.println(res13); // >>> 3\n\n    List<String> res14 = jedis.zrange(\"racer_scores\", 0, -1);\n    System.out.println(res14); // >>> [Castilla, Ford, Norem, Prickett, Royce, Sam-Bodden]\n\n    List<String> res15 = jedis.zrangeByLex(\"racer_scores\", \"[A\", \"[L\");\n    System.out.println(res15); // >>> [Castilla, Ford]","section_id":"lexicographical-scores"},{"id":"lexicographical-scores-ex6","language":"javascript","code":"const res13 = await client.zAdd('racer_scores', [\n  { score: 0, value: 'Norem' },\n  { score: 0, value: 'Sam-Bodden' },\n  { score: 0, value: 'Royce' },\n  { score: 0, value: 'Ford' },\n  { score: 0, value: 'Prickett' },\n  { score: 0, value: 'Castilla' }\n]);\nconsole.log(res13);  // >>> 3\n\n\nconst res14 = await client.zRange('racer_scores', 0, -1);\nconsole.log(res14);  // >>> ['Castilla', 'Ford', 'Norem', 'Prickett', 'Royce', 'Sam-Bodden']\n\nconst res15 = await client.zRangeByLex('racer_scores', '[A', '[L');\nconsole.log(res15);  // >>> ['Castilla', 'Ford']","section_id":"lexicographical-scores"},{"id":"lexicographical-scores-ex7","language":"php","code":"$res13 = $r->zadd('racer_scores', [\n            'Norem' => 0,\n            'Sam-Bodden' => 0,\n            'Royce' => 0,\n            'Ford' => 0,\n            'Prickett' => 0,\n            'Castilla' => 0,\n        ]);\n        echo $res13 . PHP_EOL;\n        // >>> 3\n\n        $res14 = $r->zrange('racer_scores', 0, -1);\n        echo json_encode($res14) . PHP_EOL;\n        // >>> [\"Castilla\",\"Ford\",\"Norem\",\"Prickett\",\"Royce\",\"Sam-Bodden\"]\n\n        $res15 = $r->zrangebylex('racer_scores', '[A', '[L');\n        echo json_encode($res15) . PHP_EOL;\n        // >>> [\"Castilla\",\"Ford\"]","section_id":"lexicographical-scores"},{"id":"lexicographical-scores-ex8","language":"python","code":"res13 = r.zadd(\n    \"racer_scores\",\n    {\n        \"Norem\": 0,\n        \"Sam-Bodden\": 0,\n        \"Royce\": 0,\n        \"Ford\": 0,\n        \"Prickett\": 0,\n        \"Castilla\": 0,\n    },\n)\nprint(res13)  # >>> 3\n\nres14 = r.zrange(\"racer_scores\", 0, -1)\nprint(res14)  # >>> ['Castilla', 'Ford', 'Norem', 'Prickett', 'Royce', 'Sam-Bodden']\n\nres15 = r.zrangebylex(\"racer_scores\", \"[A\", \"[L\")\nprint(res15)  # >>> ['Castilla', 'Ford']","section_id":"lexicographical-scores"},{"id":"lexicographical-scores-ex9","language":"ruby","code":"res13 = r.zadd('racer_scores', [\n  [0, 'Norem'],\n  [0, 'Sam-Bodden'],\n  [0, 'Royce'],\n  [0, 'Ford'],\n  [0, 'Prickett'],\n  [0, 'Castilla']\n])\nputs res13 # >>> 3\n\nres14 = r.zrange('racer_scores', 0, -1)\nputs res14.inspect # >>> [\"Castilla\", \"Ford\", \"Norem\", \"Prickett\", \"Royce\", \"Sam-Bodden\"]\n\nres15 = r.zrangebylex('racer_scores', '[A', '[L')\nputs res15.inspect # >>> [\"Castilla\", \"Ford\"]","section_id":"lexicographical-scores"},{"id":"lexicographical-scores-ex10","language":"rust","code":"if let Ok(res) = r\n            .zadd_multiple(\n                \"racer_scores\",\n                &[\n                    (0, \"Norem\"),\n                    (0, \"Sam-Bodden\"),\n                    (0, \"Royce\"),\n                    (0, \"Ford\"),\n                    (0, \"Prickett\"),\n                    (0, \"Castilla\"),\n                ],\n            )\n            .await\n        {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 3\n        }\n\n        if let Ok(res) = r.zrange(\"racer_scores\", 0, -1).await {\n            let res: Vec<String> = res;\n            println!(\"{res:?}\");\n            // >>> [\"Castilla\", \"Ford\", \"Norem\", \"Prickett\", \"Royce\", \"Sam-Bodden\"]\n        }\n\n        if let Ok(res) = r.zrangebylex(\"racer_scores\", \"[A\", \"[L\").await {\n            let res: Vec<String> = res;\n            println!(\"{res:?}\"); // >>> [\"Castilla\", \"Ford\"]\n        }","section_id":"lexicographical-scores"},{"id":"lexicographical-scores-ex11","language":"rust","code":"if let Ok(res) = r.zadd_multiple(\n            \"racer_scores\",\n            &[\n                (0, \"Norem\"),\n                (0, \"Sam-Bodden\"),\n                (0, \"Royce\"),\n                (0, \"Ford\"),\n                (0, \"Prickett\"),\n                (0, \"Castilla\"),\n            ],\n        ) {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 3\n        }\n\n        if let Ok(res) = r.zrange(\"racer_scores\", 0, -1) {\n            let res: Vec<String> = res;\n            println!(\"{res:?}\");\n            // >>> [\"Castilla\", \"Ford\", \"Norem\", \"Prickett\", \"Royce\", \"Sam-Bodden\"]\n        }\n\n        if let Ok(res) = r.zrangebylex(\"racer_scores\", \"[A\", \"[L\") {\n            let res: Vec<String> = res;\n            println!(\"{res:?}\"); // >>> [\"Castilla\", \"Ford\"]\n        }","section_id":"lexicographical-scores"},{"id":"examples-ex0","language":"plaintext","code":"> ZADD racer_scores 100 \"Wood\"\n(integer) 1\n> ZADD racer_scores 100 \"Henshaw\"\n(integer) 1\n> ZADD racer_scores 150 \"Henshaw\"\n(integer) 0\n> ZINCRBY racer_scores 50 \"Wood\"\n\"150\"\n> ZINCRBY racer_scores 50 \"Henshaw\"\n\"200\"","section_id":"examples"},{"id":"examples-ex1","language":"csharp","code":"bool res16 = db.SortedSetAdd(\"racer_scores\", \"Wood\", 100);\n        Console.WriteLine(res16); // >>> True\n\n        bool res17 = db.SortedSetAdd(\"racer_scores\", \"Henshaw\", 100);\n        Console.WriteLine(res17); // >>> True\n\n        bool res18 = db.SortedSetAdd(\"racer_scores\", \"Henshaw\", 150);\n        Console.WriteLine(res18); // >>> False\n\n        double res19 = db.SortedSetIncrement(\"racer_scores\", \"Wood\", 50);\n        Console.WriteLine(res19); // >>> 150.0\n\n        double res20 = db.SortedSetIncrement(\"racer_scores\", \"Henshaw\", 50);\n        Console.WriteLine(res20); // >>> 200.0","section_id":"examples"},{"id":"examples-ex2","language":"go","code":"res16, err := rdb.ZAdd(ctx, \"racer_scores\",\n\t\tredis.Z{Member: \"Wood\", Score: 100},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res16) // >>> 1\n\n\tres17, err := rdb.ZAdd(ctx, \"racer_scores\",\n\t\tredis.Z{Member: \"Henshaw\", Score: 100},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res17) // >>> 1\n\n\tres18, err := rdb.ZAdd(ctx, \"racer_scores\",\n\t\tredis.Z{Member: \"Henshaw\", Score: 150},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res18) // >>> 0\n\n\tres19, err := rdb.ZIncrBy(ctx, \"racer_scores\", 50, \"Wood\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res19) // >>> 150\n\n\tres20, err := rdb.ZIncrBy(ctx, \"racer_scores\", 50, \"Henshaw\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res20) // >>> 200","section_id":"examples"},{"id":"examples-ex3","language":"java","code":"CompletableFuture<Void> leaderboard = asyncCommands.zadd(\"racer_scores\", ScoredValue.just(100, \"Wood\"))\n                    .thenCompose(res9 -> {\n                        System.out.println(res9); // >>> 1\n\n                        return asyncCommands.zadd(\"racer_scores\", ScoredValue.just(100, \"Henshaw\"));\n                    }).thenCompose(res10 -> {\n                        System.out.println(res10); // >>> 1\n\n                        return asyncCommands.zadd(\"racer_scores\", ScoredValue.just(150, \"Henshaw\"));\n                    }).thenCompose(res11 -> {\n                        System.out.println(res11); // >>> 0\n\n                        return asyncCommands.zincrby(\"racer_scores\", 50, \"Wood\");\n                    }).thenCompose(res12 -> {\n                        System.out.println(res12); // >>> 150\n\n                        return asyncCommands.zincrby(\"racer_scores\", 50, \"Henshaw\");\n                    })\n                    .thenAccept(System.out::println) // >>> 200\n                    .toCompletableFuture();","section_id":"examples"},{"id":"examples-ex4","language":"java","code":"Mono<Void> leaderboard = reactiveCommands.zadd(\"racer_scores\", ScoredValue.just(100, \"Wood\"))\n                    .doOnNext(result -> {\n                        System.out.println(result); // >>> 1\n                    }).flatMap(v -> reactiveCommands.zadd(\"racer_scores\", ScoredValue.just(100, \"Henshaw\")))\n                    .doOnNext(result -> {\n                        System.out.println(result); // >>> 1\n                    }).flatMap(v -> reactiveCommands.zadd(\"racer_scores\", ScoredValue.just(150, \"Henshaw\")))\n                    .doOnNext(result -> {\n                        System.out.println(result); // >>> 0\n                    }).flatMap(v -> reactiveCommands.zincrby(\"racer_scores\", 50, \"Wood\")).doOnNext(result -> {\n                        System.out.println(result); // >>> 150\n                    }).flatMap(v -> reactiveCommands.zincrby(\"racer_scores\", 50, \"Henshaw\")).doOnNext(result -> {\n                        System.out.println(result); // >>> 200\n                    }).then();","section_id":"examples"},{"id":"examples-ex5","language":"java","code":"long res16 = jedis.zadd(\"racer_scores\", 100d, \"Wood\");\n    System.out.println(res16); // >>> 1\n\n    long res17 = jedis.zadd(\"racer_scores\", 100d, \"Henshaw\");\n    System.out.println(res17); // >>> 1\n\n    long res18 = jedis.zadd(\"racer_scores\", 150d, \"Henshaw\");\n    System.out.println(res18); // >>> 0\n\n    double res19 = jedis.zincrby(\"racer_scores\", 50d, \"Wood\");\n    System.out.println(res19); // >>> 150.0\n\n    double res20 = jedis.zincrby(\"racer_scores\", 50d, \"Henshaw\");\n    System.out.println(res20); // >>> 200.0","section_id":"examples"},{"id":"examples-ex6","language":"javascript","code":"const res16 = await client.zAdd('racer_scores', { score: 100, value: 'Wood' });\nconsole.log(res16);  // >>> 1\n\nconst res17 = await client.zAdd('racer_scores', { score: 100, value: 'Henshaw' });\nconsole.log(res17);  // >>> 1\n\nconst res18 = await client.zAdd('racer_scores', { score: 150, value: 'Henshaw' }, { nx: true });\nconsole.log(res18);  // >>> 0\n\nconst res19 = await client.zIncrBy('racer_scores', 50, 'Wood');\nconsole.log(res19);  // >>> 150.0\n\nconst res20 = await client.zIncrBy('racer_scores', 50, 'Henshaw');\nconsole.log(res20);  // >>> 200.0","section_id":"examples"},{"id":"examples-ex7","language":"php","code":"$res16 = $r->zadd('racer_scores', ['Wood' => 100]);\n        echo $res16 . PHP_EOL;\n        // >>> 1\n\n        $res17 = $r->zadd('racer_scores', ['Henshaw' => 100]);\n        echo $res17 . PHP_EOL;\n        // >>> 1\n\n        $res18 = $r->zadd('racer_scores', ['Henshaw' => 150]);\n        echo $res18 . PHP_EOL;\n        // >>> 0\n\n        $res19 = $r->zincrby('racer_scores', 50, 'Wood');\n        echo $res19 . PHP_EOL;\n        // >>> 150\n\n        $res20 = $r->zincrby('racer_scores', 50, 'Henshaw');\n        echo $res20 . PHP_EOL;\n        // >>> 200","section_id":"examples"},{"id":"examples-ex8","language":"python","code":"res16 = r.zadd(\"racer_scores\", {\"Wood\": 100})\nprint(res16)  # >>> 1\n\nres17 = r.zadd(\"racer_scores\", {\"Henshaw\": 100})\nprint(res17)  # >>> 1\n\nres18 = r.zadd(\"racer_scores\", {\"Henshaw\": 150})\nprint(res18)  # >>> 0\n\nres19 = r.zincrby(\"racer_scores\", 50, \"Wood\")\nprint(res19)  # >>> 150.0\n\nres20 = r.zincrby(\"racer_scores\", 50, \"Henshaw\")\nprint(res20)  # >>> 200.0","section_id":"examples"},{"id":"examples-ex9","language":"ruby","code":"res16 = r.zadd('racer_scores', [[100, 'Wood']])\nputs res16 # >>> 1\n\nres17 = r.zadd('racer_scores', [[100, 'Henshaw']])\nputs res17 # >>> 1\n\nres18 = r.zadd('racer_scores', [[150, 'Henshaw']])\nputs res18 # >>> 0\n\nres19 = r.zincrby('racer_scores', 50, 'Wood')\nputs res19 # >>> 150.0\n\nres20 = r.zincrby('racer_scores', 50, 'Henshaw')\nputs res20 # >>> 200.0","section_id":"examples"},{"id":"examples-ex10","language":"rust","code":"if let Ok(res) = r.zadd(\"racer_scores\", \"Wood\", 100).await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 1\n        }\n\n        if let Ok(res) = r.zadd(\"racer_scores\", \"Henshaw\", 100).await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 1\n        }\n\n        if let Ok(res) = r.zadd(\"racer_scores\", \"Henshaw\", 150).await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 0\n        }\n\n        if let Ok(res) = r.zincr(\"racer_scores\", \"Wood\", 50).await {\n            let res: f64 = res;\n            println!(\"{res}\"); // >>> 150\n        }\n\n        if let Ok(res) = r.zincr(\"racer_scores\", \"Henshaw\", 50).await {\n            let res: f64 = res;\n            println!(\"{res}\"); // >>> 200\n        }","section_id":"examples"},{"id":"examples-ex11","language":"rust","code":"if let Ok(res) = r.zadd(\"racer_scores\", \"Wood\", 100) {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 1\n        }\n\n        if let Ok(res) = r.zadd(\"racer_scores\", \"Henshaw\", 100) {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 1\n        }\n\n        if let Ok(res) = r.zadd(\"racer_scores\", \"Henshaw\", 150) {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 0\n        }\n\n        if let Ok(res) = r.zincr(\"racer_scores\", \"Wood\", 50) {\n            let res: f64 = res;\n            println!(\"{res}\"); // >>> 150\n        }\n\n        if let Ok(res) = r.zincr(\"racer_scores\", \"Henshaw\", 50) {\n            let res: f64 = res;\n            println!(\"{res}\"); // >>> 200\n        }","section_id":"examples"}]}
{"schema_version":2,"id":"develop/data-types/streams/idempotency","title":"Idempotent message processing","url":"https://redis.io/docs/latest/develop/data-types/streams/idempotency/","summary":"Idempotent message processing in Redis Streams","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-29T16:29:37+01:00","page_type":"content","content_hash":"adb2ec23491ba43f0eb6192e850ec704ee9388e0bb7fab5b7e8fae073cd61acf","sections":[{"id":"overview","title":"Overview","role":"overview","text":"In Redis 8.6, streams support idempotent message processing (at-most-once production) to prevent duplicate entries when using at-least-once delivery patterns. This feature enables reliable message submission with automatic deduplication.\n\nIdempotent message processing ensures that handling the same message multiple times produces the same system state as handling it once.\n\nBeginning with Redis 8.6, streams support idempotent message processing (at-most-once production) to prevent duplicate entries when producers resend messages.\n\nProducers may need to resend messages under two scenarios:\n\n1. Producer-Redis network issues (disconnection and reconnection).\n\n    If a disconnection occurs after the producer executes `XADD`, but before it receives the reply, the producer has no way of knowing if that message was delivered.\n\n1. The producer crashes and restarts.\n\n    If the producer crashes after calling `XADD` but before receiving the reply and marking a message as delivered, after a restart, the producer has no way of knowing if that message was delivered.\n\nIn both cases, to guarantee that the message is added to the stream, the producer must call `XADD` again with the same message. Without idempotent message processing, a retry may result in a message being delivered twice. With idempotent message processing, producers can guarantee at-most-once production even under such scenarios.\n\nA unique ID, called an idempotent ID or *iid*, is associated with each message that is added to a stream. \nThere are two ways to assign iids:\n\n1. Producers provide a unique iid for each message. An iid can be some identifier already associated with this message: a transaction ID, a counter, or a UUID.\n1. Redis generates an iid based on each message’s content.\n\nIf the same message is added to the stream more than once, the same iid would need to be provided by the producer.\nFor (1), this is the producer’s responsibility, and for (2), Redis will calculate the same iid, as long as the message content hasn’t changed."},{"id":"idempotency-modes","title":"Idempotency modes","role":"content","text":"Use the [`XADD`](https://redis.io/docs/latest/commands/xadd) command with idempotency parameters, `IDMP` or `IDMPAUTO`:\n\n[code example]"},{"id":"manual-mode-idmp","title":"Manual mode (`IDMP`)","role":"content","text":"Specify both producer ID (pid) and iid explicitly:\n\n[code example]\n\n- `pid`: Unique identifier for the message producer.\n- `iid`: Unique identifier for a specific message.\n- Performance: Faster processing (no hash calculation).\n- Control: Full control over ID generation and uniqueness."},{"id":"automatic-mode-idmpauto","title":"Automatic mode (`IDMPAUTO`)","role":"content","text":"Specify only the pid; Redis generates the iid from message content:\n\n[code example]\n\n- `pid`: Unique identifier for the message producer.\n- Automatic deduplication: Redis calculates an iid from field-value pairs.\n- Content-based: The same content produces the same iid.\n- Performance: Slightly slower due to hash calculation.\n\nFor both IDMP and IDMPAUTO, each producer application is required to use the same pid after it restarts.\n\nFor IDMP, each producer application is responsible for:\n\n- Providing a unique iid for each entry (either globally, or just for each pid).\n- Reusing the same (pid, iid) when resending a message (even after it restarts).\n\nHere's an illustration of how message processing in Redis Streams works with and without idempotent production:\n\n![Idempotent message processing in Redis Streams](https://redis.io/docs/latest/images/dev/stream/stream-idempotency.png)"},{"id":"stream-configuration","title":"Stream configuration","role":"content","text":"Configure idempotency settings for a stream using [`XCFGSET`](https://redis.io/docs/latest/commands/xcfgset):\n\n[code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"- `IDMP-DURATION`: How long (in seconds) to retain iids (1-86400 seconds, the default is 100).\n- `IDMP-MAXSIZE`: The maximum number of per-producer iids to track (1-10,000 entries, the default is 100)."},{"id":"expiration-behavior","title":"Expiration behavior","role":"content","text":"Idempotent IDs are removed when either condition is met:\n\n- Time-based: iids expire after the configured `IDMP-DURATION`.\n- Capacity-based: Oldest iids are evicted when `IDMP-MAXSIZE` is reached. Redis never keeps more than `IDMP-MAXSIZE` iids per pid. In other words, `IDMP-MAXSIZE` is *stronger* than `IDMP-DURATION`."},{"id":"determine-optimal-configuration-values","title":"Determine optimal configuration values","role":"content","text":"`IDMP-DURATION` is an operational guarantee: Redis will not discard a previously sent iid for the specified duration (unless reaching `IDMP-MAXSIZE` iids for that producer).\nIf a producer application crashes and stops sending messages to Redis, Redis will keep each iid for `IDMP-DURATION` seconds, after which they will be discarded.\nYou should know how long it may take your producer application to recover from a crash and start resending messages, so you should set `IDMP-DURATION` accordingly.\nIf `IDMP-DURATION` is set too high, Redis will waste memory by retaining iids for a longer duration than necessary.\n\n**Example**: if a producer crashes, it may take up to 1,000 seconds until it recovers and restarts sending messages. You should set `IDMP-DURATION` to 1000.\n\nWhen a producer application retrieves an `XADD` reply from Redis, it usually marks the message as *delivered* in a transaction database or log file. \nIf the application crashes, it needs to resend undelivered messages after recovering from the crash.\nSince a few messages may have not been marked as delivered as a result of the crash, the application will likely resend these messages.\nUsing iids will allow Redis to detect such duplicate messages and filter them.\nSetting `IDMP-MAXSIZE` correctly ensures that Redis retains a sufficient number of recent iids.\nIf `IDMP-MAXSIZE` is set too high, Redis will waste memory by retaining too many iids.\nUsually this number can be very small, and often, even *one* is enough.\nIf your application marks messages as delivered asynchronously, you should know how long it may take from the time it retrieved a `XADD` reply from Redis until the message is marked as delivered; this duration is called *mark-delay*. `IDMP-MAXSIZE` should be set to\n\n`mark-delay [in msec] * (messages / msec) + some margin.`\n\n**Example**: a producer is sending 1K msgs/sec (1 msg/msec), and takes up to 80 msec to mark each message as delivered, `IDMP-MAXSIZE` should be set to `1 * 80 + margin = 100` iids."},{"id":"producer-isolation","title":"Producer isolation","role":"content","text":"Each producer maintains independent idempotency tracking:\n\n[code example]\n\nProducers can use the same iid without conflicts, as long as long as the pids are different."},{"id":"monitoring","title":"Monitoring","role":"content","text":"Use [`XINFO STREAM`](https://redis.io/docs/latest/commands/xinfo-stream) to monitor idempotency metrics:\n\n[code example]\n\nReturns additional fields when idempotency is being used:\n\n- `idmp-duration`: Current duration setting.\n- `idmp-maxsize`: Current maxsize setting.\n- `pids-tracked`: The number of pids currently tracked in the stream\n- `iids-tracked`: Total number of iids currently tracked.\n- `iids-added`: Lifetime count of messages with idempotent IDs.\n- `iids-duplicates`: Lifetime count of duplicates prevented."},{"id":"best-practices","title":"Best practices","role":"content","text":""},{"id":"producer-id-selection","title":"Producer ID selection","role":"content","text":"Use globally unique, persistent producer IDs:\n\n- Recommended: Use short producer IDs to save memory and increase performance.\n- Persistence: Use the same producer ID after restarts to maintain idempotency tracking."},{"id":"configuration-tuning","title":"Configuration tuning","role":"configuration","text":"- Duration: Set based on your retry timeout patterns.\n- Maxsize: Balance memory usage with deduplication window needs.\n- Monitoring: Track `iids-duplicates` to verify deduplication effectiveness."},{"id":"error-handling","title":"Error handling","role":"errors","text":"Handle these error conditions:\n\n- `WRONGTYPE`: Key exists but is not a stream.\n- `ERR no such key`: Stream doesn't exist (when using NOMKSTREAM).\n- `ERR syntax error`: Invalid command syntax."},{"id":"performance-characteristics","title":"Performance characteristics","role":"performance","text":"Idempotency introduces minimal overhead:\n\n- Throughput: 2-5% reduction compared to standard XADD.\n- Memory: <1.5% additional memory usage.\n- Latency: Negligible impact on per-operation latency.\n\nManual mode (IDMP) is slightly faster than automatic mode (IDMPAUTO) since it avoids hash calculations."},{"id":"persistence","title":"Persistence","role":"content","text":"Idempotency tracking persists across Redis restarts:\n\n- RDB/AOF: All producer-idempotent ID pairs are saved.\n- Recovery: Tracking remains active after restart.\n- Configuration: `IDMP-DURATION` and `IDMP-MAXSIZE` settings persist.\n- Important: Executing `XCFGSET` with different `IDMP-DURATION` or `IDMP-MAXSIZE` values than the current values for a particular key clears its IDMP map."}],"examples":[{"id":"idempotency-modes-ex0","language":"plaintext","code":"XADD mystream IDMP producer-1 iid-1 * field value      # producer-1 (pid) and iid-1 (iid) are provided manually\nXADD mystream IDMPAUTO producer-2 * field value        # producer-2 (pid) is provided manually, Redis provides the iid","section_id":"idempotency-modes"},{"id":"manual-mode-idmp-ex0","language":"plaintext","code":"XADD mystream IDMP producer1 msg1 * field value","section_id":"manual-mode-idmp"},{"id":"automatic-mode-idmpauto-ex0","language":"plaintext","code":"XADD mystream IDMPAUTO producer1 * field value","section_id":"automatic-mode-idmpauto"},{"id":"stream-configuration-ex0","language":"plaintext","code":"XCFGSET mystream IDMP-DURATION 300 IDMP-MAXSIZE 1000","section_id":"stream-configuration"},{"id":"producer-isolation-ex0","language":"plaintext","code":"XADD mystream IDMP producer-1 iid-1 * field value    # producer-1 is tracking\nXADD mystream IDMP producer-2 iid-1 * field value    # producer-2 is tracking (independent)","section_id":"producer-isolation"},{"id":"monitoring-ex0","language":"plaintext","code":"XINFO STREAM mystream","section_id":"monitoring"}]}
{"schema_version":2,"id":"develop/data-types/streams","title":"Redis Streams","url":"https://redis.io/docs/latest/develop/data-types/streams/","summary":"Introduction to Redis streams","aliases":["/manual/data-types/streams/","/data-types/streams/","/develop/data-types/streams-tutorial/","/io/data-structures/streams/","/develop/data-types/stream/","/data-types/streams-tutorial/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-30T13:16:11-07:00","children":[{"id":"develop/data-types/streams/idempotency","summary":"Idempotent message processing in Redis Streams","title":"Idempotent message processing","url":"https://redis.io/docs/latest/develop/data-types/streams/idempotency/"}],"page_type":"content","content_hash":"99af51be4d090dd23d2c7499a5778e2eecad0a9e8e1e9d7747144b852e5a8e59","sections":[{"id":"stream-command-summary","title":"Stream command summary","role":"content","text":"**30 commands in this group:**\n\n[View all stream commands](https://redis.io/commands/?group=stream)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [XACK](https://redis.io/commands/xack/) | Returns the number of messages that were successfully acknowledged by the consumer group member of a stream. | O(1) for each message ID processed. | 5.0.0 |\n| [XACKDEL](https://redis.io/commands/xackdel/) | Acknowledges and deletes one or multiple messages for a stream consumer group. | O(1) for each message ID processed. | 8.2.0 |\n| [XADD](https://redis.io/commands/xadd/) | Appends a new message to a stream. Creates the key if it doesn't exist. | O(1) when adding a new entry, O(N) when trimmin... | 5.0.0 |\n| [XAUTOCLAIM](https://redis.io/commands/xautoclaim/) | Changes, or acquires, ownership of messages in a consumer group, as if the messages were delivered to as consumer group member. | O(1) if COUNT is small. | 6.2.0 |\n| [XCFGSET](https://redis.io/commands/xcfgset/) | Sets the IDMP configuration parameters for a stream. | O(1) | 8.6.0 |\n| [XCLAIM](https://redis.io/commands/xclaim/) | Changes, or acquires, ownership of a message in a consumer group, as if the message was delivered a consumer group member. | O(log N) with N being the number of messages in... | 5.0.0 |\n| [XDEL](https://redis.io/commands/xdel/) | Returns the number of messages after removing them from a stream. | O(1) for each single item to delete in the stre... | 5.0.0 |\n| [XDELEX](https://redis.io/commands/xdelex/) | Deletes one or multiple entries from the stream. | O(1) for each single item to delete in the stre... | 8.2.0 |\n| [XGROUP](https://redis.io/commands/xgroup/) | A container for consumer groups commands. | Depends on subcommand. | 5.0.0 |\n| [XGROUP CREATE](https://redis.io/commands/xgroup-create/) | Creates a consumer group. | O(1) | 5.0.0 |\n\n#### Stream command summary (part 2)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [XGROUP CREATECONSUMER](https://redis.io/commands/xgroup-createconsumer/) | Creates a consumer in a consumer group. | O(1) | 6.2.0 |\n| [XGROUP DELCONSUMER](https://redis.io/commands/xgroup-delconsumer/) | Deletes a consumer from a consumer group. | O(1) | 5.0.0 |\n| [XGROUP DESTROY](https://redis.io/commands/xgroup-destroy/) | Destroys a consumer group. | O(N) where N is the number of entries in the gr... | 5.0.0 |\n| [XGROUP HELP](https://redis.io/commands/xgroup-help/) | Returns helpful text about the different subcommands. | O(1) | 5.0.0 |\n| [XGROUP SETID](https://redis.io/commands/xgroup-setid/) | Sets the last-delivered ID of a consumer group. | O(1) | 5.0.0 |\n| [XIDMPRECORD](https://redis.io/commands/xidmprecord/) | An internal command for setting IDMP metadata on an existing stream message. | O(1) | 8.6.2 |\n| [XINFO](https://redis.io/commands/xinfo/) | A container for stream introspection commands. | Depends on subcommand. | 5.0.0 |\n| [XINFO CONSUMERS](https://redis.io/commands/xinfo-consumers/) | Returns a list of the consumers in a consumer group. | O(1) | 5.0.0 |\n| [XINFO GROUPS](https://redis.io/commands/xinfo-groups/) | Returns a list of the consumer groups of a stream. | O(1) | 5.0.0 |\n| [XINFO HELP](https://redis.io/commands/xinfo-help/) | Returns helpful text about the different subcommands. | O(1) | 5.0.0 |\n\n#### Stream command summary (part 3)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [XINFO STREAM](https://redis.io/commands/xinfo-stream/) | Returns information about a stream. | O(1) | 5.0.0 |\n| [XLEN](https://redis.io/commands/xlen/) | Return the number of messages in a stream. | O(1) | 5.0.0 |\n| [XNACK](https://redis.io/commands/xnack/) | Releases claimed messages back to the group's PEL without acknowledging them, making them available for re-delivery. | O(1) for each message ID processed. | 8.8.0 |\n| [XPENDING](https://redis.io/commands/xpending/) | Returns the information and entries from a stream consumer group's pending entries list. | O(N) with N being the number of elements return... | 5.0.0 |\n| [XRANGE](https://redis.io/commands/xrange/) | Returns the messages from a stream within a range of IDs. | O(N) with N being the number of elements being ... | 5.0.0 |\n| [XREAD](https://redis.io/commands/xread/) | Returns messages from multiple streams with IDs greater than the ones requested. Blocks until a message is available otherwise. | N/A | 5.0.0 |\n| [XREADGROUP](https://redis.io/commands/xreadgroup/) | Returns new or historical messages from a stream for a consumer in a group. Blocks until a message is available otherwise. | For each stream mentioned: O(M) with M being th... | 5.0.0 |\n| [XREVRANGE](https://redis.io/commands/xrevrange/) | Returns the messages from a stream within a range of IDs in reverse order. | O(N) with N being the number of elements return... | 5.0.0 |\n| [XSETID](https://redis.io/commands/xsetid/) | An internal command for replicating stream values. | O(1) | 5.0.0 |\n| [XTRIM](https://redis.io/commands/xtrim/) | Deletes messages from the beginning of a stream. | O(N), with N being the number of evicted entrie... | 5.0.0 |\n\n\n\nA Redis stream is a data structure that acts like an append-only log but also implements several operations to overcome some of the limits of a typical append-only log. These include random access in O(1) time and complex consumption strategies, such as consumer groups.\nYou can use streams to record and simultaneously syndicate events in real time.\nExamples of Redis stream use cases include:\n\n* Event sourcing (e.g., tracking user actions, clicks, etc.)\n* Sensor monitoring (e.g., readings from devices in the field)\n* Notifications (e.g., storing a record of each user's notifications in a separate stream)\n\nRedis generates a unique ID for each stream entry.\nYou can use these IDs to retrieve their associated entries later or to read and process all subsequent entries in the stream. Note that because these IDs are related to time, the ones shown here may vary and will be different from the IDs you see in your own Redis instance.\n\nRedis streams support several trimming strategies (to prevent streams from growing unbounded) and more than one consumption strategy (see [`XREAD`](https://redis.io/docs/latest/commands/xread), [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup), and [`XRANGE`](https://redis.io/docs/latest/commands/xrange)). Starting with Redis 8.2, the `XACKDEL`, `XDELEX`, `XADD`, and `XTRIM` commands provide fine-grained control over how stream operations interact with multiple consumer groups, simplifying the coordination of message processing across different applications.\n\nBeginning with Redis 8.6, Redis streams support idempotent message processing (at-most-once production) to prevent duplicate entries when using at-least-once delivery patterns. This feature enables reliable message submission with automatic deduplication. See [Idempotent Message Processing](https://redis.io/docs/latest/develop/data-types/streams/idempotency) for more information."},{"id":"examples","title":"Examples","role":"example","text":"* When our racers pass a checkpoint, we add a stream entry for each racer that includes the racer's name, speed, position, and location ID:\nFoundational: Add entries to a stream using XADD with auto-generated IDs (creates new entries with field-value pairs)\n\n**Commands:** XADD\n\n**Complexity:**\n- XADD: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\n* Read two stream entries starting at ID `1692632086370-0`:\nFoundational: Retrieve stream entries within a range of IDs using XRANGE when you need to access historical data\n\n**Commands:** XRANGE\n\n**Complexity:**\n- XRANGE: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\n* Read up to 100 new stream entries, starting at the end of the stream, and block for up to 300 ms if no entries are being written:\nUse XREAD with BLOCK to wait for new entries when you need to consume messages as they arrive\n\n**Difficulty:** Intermediate\n\n**Commands:** XREAD\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"performance","title":"Performance","role":"performance","text":"Adding an entry to a stream is O(1).\nAccessing any single entry is O(n), where _n_ is the length of the ID.\nSince stream IDs are typically short and of a fixed length, this effectively reduces to a constant time lookup.\nFor details on why, note that streams are implemented as [radix trees](https://en.wikipedia.org/wiki/Radix_tree).\n\nSimply put, Redis streams provide highly efficient inserts and reads.\nSee each command's time complexity for the details."},{"id":"streams-basics","title":"Streams basics","role":"content","text":"Streams are an append-only data structure. The fundamental write command, called [`XADD`](https://redis.io/docs/latest/commands/xadd), appends a new entry to the specified stream.\n\nEach stream entry consists of one or more field-value pairs, somewhat like a dictionary or a Redis hash:\n\nFoundational: Add a single entry to a stream with multiple field-value pairs using XADD\n\n**Commands:** XADD\n\n**Complexity:**\n- XADD: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nThe above call to the [`XADD`](https://redis.io/docs/latest/commands/xadd) command adds an entry `rider: Castilla, speed: 29.9, position: 1, location_id: 2` to the stream at key `race:france`, using an auto-generated entry ID, which is the one returned by the command, specifically `1692632147973-0`. It gets as its first argument the key name `race:france`, the second argument is the entry ID that identifies every entry inside a stream. However, in this case, we passed `*` because we want the server to generate a new ID for us. Every new ID will be monotonically increasing, so in more simple terms, every new entry added will have a higher ID compared to all the past entries. Auto-generation of IDs by the server is almost always what you want, and the reasons for specifying an ID explicitly are very rare. We'll talk more about this later. The fact that each Stream entry has an ID is another similarity with log files, where line numbers, or the byte offset inside the file, can be used in order to identify a given entry. Returning back at our [`XADD`](https://redis.io/docs/latest/commands/xadd) example, after the key name and ID, the next arguments are the field-value pairs composing our stream entry.\n\nIt is possible to get the number of items inside a Stream just using the [`XLEN`](https://redis.io/docs/latest/commands/xlen) command:\n\nFoundational: Get the total number of entries in a stream using XLEN\n\n**Commands:** XLEN\n\n**Complexity:**\n- XLEN: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"entry-ids","title":"Entry IDs","role":"content","text":"The entry ID returned by the [`XADD`](https://redis.io/docs/latest/commands/xadd) command, and identifying univocally each entry inside a given stream, is composed of two parts:\n\n[code example]\n\nThe milliseconds time part is actually the local time in the local Redis node generating the stream ID, however if the current milliseconds time happens to be smaller than the previous entry time, then the previous entry time is used instead, so if a clock jumps backward the monotonically incrementing ID property still holds. The sequence number is used for entries created in the same millisecond. Since the sequence number is 64 bit wide, in practical terms there is no limit to the number of entries that can be generated within the same millisecond.\n\nThe format of such IDs may look strange at first, and the gentle reader may wonder why the time is part of the ID. The reason is that Redis streams support range queries by ID. Because the ID is related to the time the entry is generated, this gives the ability to query for time ranges basically for free. We will see this soon while covering the [`XRANGE`](https://redis.io/docs/latest/commands/xrange) command.\n\nIf for some reason the user needs incremental IDs that are not related to time but are actually associated to another external system ID, as previously mentioned, the [`XADD`](https://redis.io/docs/latest/commands/xadd) command can take an explicit ID instead of the `*` wildcard ID that triggers auto-generation, like in the following examples:\n\nSpecify explicit stream entry IDs instead of auto-generated ones when you need to use external system IDs\n\n**Difficulty:** Advanced\n\n**Commands:** XADD\n\n**Complexity:**\n- XADD: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nNote that in this case, the minimum ID is 0-1 and that the command will not accept an ID equal or smaller than a previous one:\n\nUnderstand ID validation - XADD rejects IDs that are not monotonically increasing\n\n**Difficulty:** Advanced\n\n**Commands:** XADD\n\n**Complexity:**\n- XADD: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nIf you're running Redis 7 or later, you can also provide an explicit ID consisting of the milliseconds part only. In this case, the sequence portion of the ID will be automatically generated. To do this, use the syntax below:\n\nUse partial explicit IDs with XADD to specify milliseconds while letting Redis auto-generate the sequence number\n\n**Difficulty:** Intermediate\n\n**Commands:** XADD\n\n**Complexity:**\n- XADD: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"getting-data-from-streams","title":"Getting data from Streams","role":"content","text":"Now we are finally able to append entries in our stream via [`XADD`](https://redis.io/docs/latest/commands/xadd). However, while appending data to a stream is quite obvious, the way streams can be queried in order to extract data is not so obvious. If we continue with the analogy of the log file, one obvious way is to mimic what we normally do with the Unix command `tail -f`, that is, we may start to listen in order to get the new messages that are appended to the stream. Note that unlike the blocking list operations of Redis, where a given element will reach a single client which is blocking in a *pop style* operation like [`BLPOP`](https://redis.io/docs/latest/commands/blpop), with streams we want multiple consumers to see the new messages appended to the stream (the same way many `tail -f` processes can see what is added to a log). Using the traditional terminology we want the streams to be able to *fan out* messages to multiple clients.\n\nHowever, this is just one potential access mode. We could also see a stream in quite a different way: not as a messaging system, but as a *time series store*. In this case, maybe it's also useful to get the new messages appended, but another natural query mode is to get messages by ranges of time, or alternatively to iterate the messages using a cursor to incrementally check all the history. This is definitely another useful access mode.\n\nFinally, if we see a stream from the point of view of consumers, we may want to access the stream in yet another way, that is, as a stream of messages that can be partitioned to multiple consumers that are processing such messages, so that groups of consumers can only see a subset of the messages arriving in a single stream. In this way, it is possible to scale the message processing across different consumers, without single consumers having to process all the messages: each consumer will just get different messages to process. This is basically what Kafka (TM) does with consumer groups. Reading messages via consumer groups is yet another interesting mode of reading from a Redis Stream.\n\nRedis Streams support all three of the query modes described above via different commands. The next sections will show them all, starting from the simplest and most direct to use: range queries."},{"id":"querying-by-range-xrange-and-xrevrange","title":"Querying by range: XRANGE and XREVRANGE","role":"content","text":"To query the stream by range we are only required to specify two IDs, *start* and *end*. The range returned will include the elements having start or end as ID, so the range is inclusive. The two special IDs `-` and `+` respectively mean the smallest and the greatest ID possible.\n\nFoundational: Retrieve all entries in a stream using XRANGE with - and + special IDs\n\n**Commands:** XRANGE\n\n**Complexity:**\n- XRANGE: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nEach entry returned is an array of two items: the ID and the list of field-value pairs. We already said that the entry IDs have a relation with the time, because the part at the left of the `-` character is the Unix time in milliseconds of the local node that created the stream entry, at the moment the entry was created (however note that streams are replicated with fully specified [`XADD`](https://redis.io/docs/latest/commands/xadd) commands, so the replicas will have identical IDs to the master). This means that I could query a range of time using [`XRANGE`](https://redis.io/docs/latest/commands/xrange). In order to do so, however, I may want to omit the sequence part of the ID: if omitted, in the start of the range it will be assumed to be 0, while in the end part it will be assumed to be the maximum sequence number available. This way, querying using just two milliseconds Unix times, we get all the entries that were generated in that range of time, in an inclusive way. For instance, if I want to query a two milliseconds period I could use:\n\nQuery stream entries by time range using millisecond timestamps instead of full IDs\n\n**Difficulty:** Intermediate\n\n**Commands:** XRANGE\n\n**Complexity:**\n- XRANGE: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nI have only a single entry in this range. However in real data sets, I could query for ranges of hours, or there could be many items in just two milliseconds, and the result returned could be huge. For this reason, [`XRANGE`](https://redis.io/docs/latest/commands/xrange) supports an optional **COUNT** option at the end. By specifying a count, I can just get the first *N* items. If I want more, I can get the last ID returned, increment the sequence part by one, and query again. Let's see this in the following example. Let's assume that the stream `race:france` was populated with 4 items. To start my iteration, getting 2 items per command, I start with the full range, but with a count of 2.\n\nPractical pattern: Paginate stream entries using XRANGE with COUNT to retrieve results in batches\n\n**Difficulty:** Intermediate\n\n**Commands:** XRANGE\n\n**Complexity:**\n- XRANGE: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nTo continue the iteration with the next two items, I have to pick the last ID returned, that is `1692632094485-0`, and add the prefix `(` to it. The resulting exclusive range interval, that is `(1692632094485-0` in this case, can now be used as the new *start* argument for the next [`XRANGE`](https://redis.io/docs/latest/commands/xrange) call:\n\nPractical pattern: Continue pagination using exclusive range syntax with ( prefix to skip the last retrieved entry\n\n**Difficulty:** Intermediate\n\n**Commands:** XRANGE\n\n**Complexity:**\n- XRANGE: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nNow that we've retrieved 4 items out of a stream that only had 4 entries in it, if we try to retrieve more items, we'll get an empty array:\n\nPractical pattern: Handle empty results when pagination reaches the end of the stream\n\n**Difficulty:** Intermediate\n\n**Commands:** XRANGE\n\n**Complexity:**\n- XRANGE: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nSince [`XRANGE`](https://redis.io/docs/latest/commands/xrange) complexity is *O(log(N))* to seek, and then *O(M)* to return M elements, with a small count the command has a logarithmic time complexity, which means that each step of the iteration is fast. So [`XRANGE`](https://redis.io/docs/latest/commands/xrange) is also the de facto *streams iterator* and does not require an **XSCAN** command.\n\nThe command [`XREVRANGE`](https://redis.io/docs/latest/commands/xrevrange) is the equivalent of [`XRANGE`](https://redis.io/docs/latest/commands/xrange) but returning the elements in inverted order, so a practical use for [`XREVRANGE`](https://redis.io/docs/latest/commands/xrevrange) is to check what is the last item in a Stream:\n\nFoundational: Retrieve stream entries in reverse order using XREVRANGE when you need the most recent entries first\n\n**Commands:** XREVRANGE\n\n**Complexity:**\n- XREVRANGE: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nNote that the [`XREVRANGE`](https://redis.io/docs/latest/commands/xrevrange) command takes the *start* and *stop* arguments in reverse order."},{"id":"listening-for-new-items-with-xread","title":"Listening for new items with XREAD","role":"content","text":"When we do not want to access items by a range in a stream, usually what we want instead is to *subscribe* to new items arriving to the stream. This concept may appear related to Redis Pub/Sub, where you subscribe to a channel, or to Redis blocking lists, where you wait for a key to get new elements to fetch, but there are fundamental differences in the way you consume a stream:\n\n1. A stream can have multiple clients (consumers) waiting for data. Every new item, by default, will be delivered to *every consumer* that is waiting for data in a given stream. This behavior is different than blocking lists, where each consumer will get a different element. However, the ability to *fan out* to multiple consumers is similar to Pub/Sub.\n2. While in Pub/Sub messages are *fire and forget* and are never stored anyway, and while when using blocking lists, when a message is received by the client it is *popped* (effectively removed) from the list, streams work in a fundamentally different way. All the messages are appended in the stream indefinitely (unless the user explicitly asks to delete entries): different consumers will know what is a new message from its point of view by remembering the ID of the last message received.\n3. Streams Consumer Groups provide a level of control that Pub/Sub or blocking lists cannot achieve, with different groups for the same stream, explicit acknowledgment of processed items, ability to inspect the pending items, claiming of unprocessed messages, and coherent history visibility for each single client, that is only able to see its private past history of messages.\n\nThe command that provides the ability to listen for new messages arriving into a stream is called [`XREAD`](https://redis.io/docs/latest/commands/xread). It's a bit more complex than [`XRANGE`](https://redis.io/docs/latest/commands/xrange), so we'll start showing simple forms, and later the whole command layout will be provided.\n\nFoundational: Read entries from a stream starting from a specific ID using XREAD (non-blocking form)\n\n**Commands:** XREAD\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nThe above is the non-blocking form of [`XREAD`](https://redis.io/docs/latest/commands/xread). Note that the **COUNT** option is not mandatory, in fact the only mandatory option of the command is the **STREAMS** option, that specifies a list of keys together with the corresponding maximum ID already seen for each stream by the calling consumer, so that the command will provide the client only with messages with an ID greater than the one we specified.\n\nIn the above command we wrote `STREAMS race:france 0` so we want all the messages in the Stream `race:france` having an ID greater than `0-0`. As you can see in the example above, the command returns the key name, because actually it is possible to call this command with more than one key to read from different streams at the same time. I could write, for instance: `STREAMS race:france race:italy 0 0`. Note how after the **STREAMS** option we need to provide the key names, and later the IDs. For this reason, the **STREAMS** option must always be the last option.\nAny other options must come before the **STREAMS** option.\n\nApart from the fact that [`XREAD`](https://redis.io/docs/latest/commands/xread) can access multiple streams at once, and that we are able to specify the last ID we own to just get newer messages, in this simple form the command is not doing something so different compared to [`XRANGE`](https://redis.io/docs/latest/commands/xrange). However, the interesting part is that we can turn [`XREAD`](https://redis.io/docs/latest/commands/xread) into a *blocking command* easily, by specifying the **BLOCK** argument:\n\n[code example]\n\nNote that in the example above, other than removing **COUNT**, I specified the new **BLOCK** option with a timeout of 0 milliseconds (that means to never timeout). Moreover, instead of passing a normal ID for the stream `race:france` I passed the special ID `$`. This special ID means that [`XREAD`](https://redis.io/docs/latest/commands/xread) should use as last ID the maximum ID already stored in the stream `race:france`, so that we will receive only *new* messages, starting from the time we started listening. This is similar to the `tail -f` Unix command in some way.\n\nNote that when the **BLOCK** option is used, we do not have to use the special ID `$`. We can use any valid ID. If the command is able to serve our request immediately without blocking, it will do so, otherwise it will block. Normally if we want to consume the stream starting from new entries, we start with the ID `$`, and after that we continue using the ID of the last message received to make the next call, and so forth.\n\nThe blocking form of [`XREAD`](https://redis.io/docs/latest/commands/xread) is also able to listen to multiple Streams, just by specifying multiple key names. If the request can be served synchronously because there is at least one stream with elements greater than the corresponding ID we specified, it returns with the results. Otherwise, the command will block and will return the items of the first stream which gets new data (according to the specified ID).\n\nSimilarly to blocking list operations, blocking stream reads are *fair* from the point of view of clients waiting for data, since the semantics is FIFO style. The first client that blocked for a given stream will be the first to be unblocked when new items are available.\n\n[`XREAD`](https://redis.io/docs/latest/commands/xread) has no other options than **COUNT** and **BLOCK**, so it's a pretty basic command with a specific purpose to attach consumers to one or multiple streams. More powerful features to consume streams are available using the consumer groups API, however reading via consumer groups is implemented by a different command called [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup), covered in the next section of this guide."},{"id":"consumer-groups","title":"Consumer groups","role":"content","text":"When the task at hand is to consume the same stream from different clients, then [`XREAD`](https://redis.io/docs/latest/commands/xread) already offers a way to *fan-out* to N clients, potentially also using replicas in order to provide more read scalability. However in certain problems what we want to do is not to provide the same stream of messages to many clients, but to provide a *different subset* of messages from the same stream to many clients. An obvious case where this is useful is that of messages which are slow to process: the ability to have N different workers that will receive different parts of the stream allows us to scale message processing, by routing different messages to different workers that are ready to do more work.\n\nIn practical terms, if we imagine having three consumers C1, C2, C3, and a stream that contains the messages 1, 2, 3, 4, 5, 6, 7 then what we want is to serve the messages according to the following diagram:\n\n[code example]\n\nIn order to achieve this, Redis uses a concept called *consumer groups*. It is very important to understand that Redis consumer groups have nothing to do, from an implementation standpoint, with Kafka (TM) consumer groups. Yet they are similar in functionality, so I decided to keep Kafka's (TM) terminology, as it originally popularized this idea.\n\nA consumer group is like a *pseudo consumer* that gets data from a stream, and actually serves multiple consumers, providing certain guarantees:\n\n1. Each message is served to a different consumer so that it is not possible that the same message will be delivered to multiple consumers.\n2. Consumers are identified, within a consumer group, by a name, which is a case-sensitive string that the clients implementing consumers must choose. This means that even after a disconnect, the stream consumer group retains all the state, since the client will claim again to be the same consumer. However, this also means that it is up to the client to provide a unique identifier.\n3. Each consumer group has the concept of the *first ID never consumed* so that, when a consumer asks for new messages, it can provide just messages that were not previously delivered.\n4. Consuming a message, however, requires an explicit acknowledgment using a specific command. Redis interprets the acknowledgment as: this message was correctly processed so it can be evicted from the consumer group.\n5. A consumer group tracks all the messages that are currently pending, that is, messages that were delivered to some consumer of the consumer group, but are yet to be acknowledged as processed. Thanks to this feature, when accessing the message history of a stream, each consumer *will only see messages that were delivered to it*.\n\nIn a way, a consumer group can be imagined as some *amount of state* about a stream:\n\n[code example]\n\nIf you see this from this point of view, it is very simple to understand what a consumer group can do, how it is able to just provide consumers with their history of pending messages, and how consumers asking for new messages will just be served with message IDs greater than `last_delivered_id`. At the same time, if you look at the consumer group as an auxiliary data structure for Redis streams, it is obvious that a single stream can have multiple consumer groups, that have a different set of consumers. Actually, it is even possible for the same stream to have clients reading without consumer groups via [`XREAD`](https://redis.io/docs/latest/commands/xread), and clients reading via [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) in different consumer groups.\n\nNow it's time to zoom in to see the fundamental consumer group commands. They are the following:\n\n* [`XGROUP`](https://redis.io/docs/latest/commands/xgroup) is used in order to create, destroy and manage consumer groups.\n* [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) is used to read from a stream via a consumer group.\n* [`XACK`](https://redis.io/docs/latest/commands/xack) is the command that allows a consumer to mark a pending message as correctly processed.\n* [`XNACK`](https://redis.io/docs/latest/commands/xnack) is the command that allows a consumer to release pending messages back to the group without acknowledging them, making them immediately available for re-delivery to other consumers.\n* [`XACKDEL`](https://redis.io/docs/latest/commands/xackdel) combines acknowledgment and deletion in a single atomic operation with enhanced control over consumer group references."},{"id":"creating-a-consumer-group","title":"Creating a consumer group","role":"content","text":"Assuming I have a key `race:france` of type stream already existing, in order to create a consumer group I just need to do the following:\n\nFoundational: Create a consumer group for a stream using XGROUP CREATE to enable coordinated message consumption\n\n**Commands:** XGROUP\n\n**Complexity:**\n- XGROUP: Depends on subcommand\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nAs you can see in the command above when creating the consumer group we have to specify an ID, which in the example is just `$`. This is needed because the consumer group, among the other states, must have an idea about what message to serve next at the first consumer connecting, that is, what was the *last message ID* when the group was just created. If we provide `$` as we did, then only new messages arriving in the stream from now on will be provided to the consumers in the group. If we specify `0` instead the consumer group will consume *all* the messages in the stream history to start with. Of course, you can specify any other valid ID. What you know is that the consumer group will start delivering messages that are greater than the ID you specify. Because `$` means the current greatest ID in the stream, specifying `$` will have the effect of consuming only new messages.\n\n[`XGROUP CREATE`](https://redis.io/docs/latest/commands/xgroup-create) also supports creating the stream automatically, if it doesn't exist, using the optional `MKSTREAM` subcommand as the last argument:\n\nCreate a consumer group and stream atomically using XGROUP CREATE with MKSTREAM option\n\n**Difficulty:** Intermediate\n\n**Commands:** XGROUP\n\n**Complexity:**\n- XGROUP: Depends on subcommand\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nNow that the consumer group is created we can immediately try to read messages via the consumer group using the [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) command. We'll read from consumers, that we will call Alice and Bob, to see how the system will return different messages to Alice or Bob.\n\n[`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) is very similar to [`XREAD`](https://redis.io/docs/latest/commands/xread) and provides the same **BLOCK** option, otherwise it is a synchronous command. However there is a *mandatory* option that must be always specified, which is **GROUP** and has two arguments: the name of the consumer group, and the name of the consumer that is attempting to read. The option **COUNT** is also supported and is identical to the one in [`XREAD`](https://redis.io/docs/latest/commands/xread).\n\nWe'll add riders to the race:italy stream and try reading something using the consumer group:\nNote: *here rider is the field name, and the name is the associated value. Remember that stream items are small dictionaries.*\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\n[`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) replies are just like [`XREAD`](https://redis.io/docs/latest/commands/xread) replies. Note however the `GROUP <group-name> <consumer-name>` provided above. It states that I want to read from the stream using the consumer group `mygroup` and I'm the consumer `Alice`. Every time a consumer performs an operation with a consumer group, it must specify its name, uniquely identifying this consumer inside the group.\n\nThere is another very important detail in the command line above, after the mandatory **STREAMS** option the ID requested for the key `race:italy` is the special ID `>`. This special ID is only valid in the context of consumer groups, and it means: **messages never delivered to other consumers so far**.\n\nThis is almost always what you want, however it is also possible to specify a real ID, such as `0` or any other valid ID, in this case, however, what happens is that we request from [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) to just provide us with the **history of pending messages**, and in such case, will never see new messages in the group. So basically [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) has the following behavior based on the ID we specify:\n\n* If the ID is the special ID `>` then the command will return only new messages never delivered to other consumers so far, and as a side effect, will update the consumer group's *last ID*.\n* If the ID is any other valid numerical ID, then the command will let us access our *history of pending messages*. That is, the set of messages that were delivered to this specified consumer (identified by the provided name), and never acknowledged so far with [`XACK`](https://redis.io/docs/latest/commands/xack).\n\nWe can test this behavior immediately specifying an ID of 0, without any **COUNT** option: we'll just see the only pending message, that is, the one about Castilla:\n\nAccess pending message history using XREADGROUP with a specific ID to retrieve unacknowledged messages\n\n**Difficulty:** Intermediate\n\n**Commands:** XREADGROUP\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nHowever, if we acknowledge the message as processed, it will no longer be part of the pending messages history, so the system will no longer report anything:\n\nFoundational: Acknowledge processed messages using XACK to mark them as handled by a consumer\n\n**Commands:** XACK, XREADGROUP\n\n**Complexity:**\n- XACK: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nDon't worry if you yet don't know how [`XACK`](https://redis.io/docs/latest/commands/xack) works, the idea is just that processed messages are no longer part of the history that we can access.\n\nNow it's Bob's turn to read something:\n\nPractical pattern: Demonstrate consumer group load balancing where different consumers receive different messages from the same stream\n\n**Difficulty:** Intermediate\n\n**Commands:** XREADGROUP\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nBob asked for a maximum of two messages and is reading via the same group `mygroup`. So what happens is that Redis reports just *new* messages. As you can see the \"Castilla\" message is not delivered, since it was already delivered to Alice, so Bob gets Royce and Sam-Bodden and so forth.\n\nThis way Alice, Bob, and any other consumer in the group, are able to read different messages from the same stream, to read their history of yet to process messages, or to mark messages as processed. This allows creating different topologies and semantics for consuming messages from a stream.\n\nThere are a few things to keep in mind:\n\n* Consumers are auto-created the first time they are mentioned, no need for explicit creation.\n* Even with [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) you can read from multiple keys at the same time, however for this to work, you need to create a consumer group with the same name in every stream. This is not a common need, but it is worth mentioning that the feature is technically available.\n* [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) is a *write command* because even if it reads from the stream, the consumer group is modified as a side effect of reading, so it can only be called on master instances.\n\nAn example of a consumer implementation, using consumer groups, written in the Ruby language could be the following. The Ruby code is aimed to be readable by virtually any experienced programmer, even if they do not know Ruby:\n\n[code example]\n\nAs you can see the idea here is to start by consuming the history, that is, our list of pending messages. This is useful because the consumer may have crashed before, so in the event of a restart we want to re-read messages that were delivered to us without getting acknowledged. Note that we might process a message multiple times or one time (at least in the case of consumer failures, but there are also the limits of Redis persistence and replication involved, see the specific section about this topic).\n\nOnce the history was consumed, and we get an empty list of messages, we can switch to using the `>` special ID in order to consume new messages."},{"id":"recovering-from-permanent-failures","title":"Recovering from permanent failures","role":"content","text":"The example above allows us to write consumers that participate in the same consumer group, each taking a subset of messages to process, and when recovering from failures re-reading the pending messages that were delivered just to them. However in the real world consumers may permanently fail and never recover. What happens to the pending messages of the consumer that never recovers after stopping for any reason?\n\nRedis consumer groups offer a feature that is used in these situations in order to *claim* the pending messages of a given consumer so that such messages will change ownership and will be re-assigned to a different consumer. The feature is very explicit. A consumer has to inspect the list of pending messages, and will have to claim specific messages using a special command, otherwise the server will leave the messages pending forever and assigned to the old consumer. In this way different applications can choose if to use such a feature or not, and exactly how to use it.\n\nThe first step of this process is just a command that provides observability of pending entries in the consumer group and is called [`XPENDING`](https://redis.io/docs/latest/commands/xpending).\nThis is a read-only command which is always safe to call and will not change ownership of any message.\nIn its simplest form, the command is called with two arguments, which are the name of the stream and the name of the consumer group.\n\nView pending message summary for a consumer group using XPENDING to monitor unacknowledged messages\n\n**Difficulty:** Intermediate\n\n**Commands:** XPENDING\n\n**Complexity:**\n- XPENDING: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nWhen called in this way, the command outputs the total number of pending messages in the consumer group (two in this case), the lower and higher message ID among the pending messages, and finally a list of consumers and the number of pending messages they have.\nWe have only Bob with two pending messages because the single message that Alice requested was acknowledged using [`XACK`](https://redis.io/docs/latest/commands/xack).\n\nWe can ask for more information by giving more arguments to [`XPENDING`](https://redis.io/docs/latest/commands/xpending), because the full command signature is the following:\n\n[code example]\n\nBy providing a start and end ID (that can be just `-` and `+` as in [`XRANGE`](https://redis.io/docs/latest/commands/xrange)) and a count to control the amount of information returned by the command, we are able to know more about the pending messages. The optional final argument, the consumer name, is used if we want to limit the output to just messages pending for a given consumer, but won't use this feature in the following example.\n\nGet detailed pending message information including idle time and delivery count using XPENDING with range\n\n**Difficulty:** Advanced\n\n**Commands:** XPENDING\n\n**Complexity:**\n- XPENDING: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nNow we have the details for each message: the ID, the consumer name, the *idle time* in milliseconds, which is how many milliseconds have passed since the last time the message was delivered to some consumer, and finally the number of times that a given message was delivered.\nWe have two messages from Bob, and they are idle for 60000+ milliseconds, about a minute.\n\nNote that nobody prevents us from checking what the first message content was by just using [`XRANGE`](https://redis.io/docs/latest/commands/xrange).\n\nRetrieve the content of pending messages using XRANGE to inspect what needs to be processed\n\n**Difficulty:** Intermediate\n\n**Commands:** XRANGE\n\n**Complexity:**\n- XRANGE: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nWe have just to repeat the same ID twice in the arguments. Now that we have some ideas, Alice may decide that after 1 minute of not processing messages, Bob will probably not recover quickly, and it's time to *claim* such messages and resume the processing in place of Bob. To do so, we use the [`XCLAIM`](https://redis.io/docs/latest/commands/xclaim) command.\n\nThis command is very complex and full of options in its full form, since it is used for replication of consumer groups changes, but we'll use just the arguments that we need normally. In this case it is as simple as:\n\n[code example]\n\nBasically we say, for this specific key and group, I want that the message IDs specified will change ownership, and will be assigned to the specified consumer name `<consumer>`. However, we also provide a minimum idle time, so that the operation will only work if the idle time of the mentioned messages is greater than the specified idle time. This is useful because maybe two clients are retrying to claim a message at the same time:\n\n[code example]\n\nHowever, as a side effect, claiming a message will reset its idle time and will increment its number of deliveries counter, so the second client will fail claiming it. In this way we avoid trivial re-processing of messages (even if in the general case you cannot obtain exactly once processing).\n\nThis is the result of the command execution:\n\nClaim pending messages from another consumer using XCLAIM when a consumer fails to process messages\n\n**Difficulty:** Advanced\n\n**Commands:** XCLAIM\n\n**Complexity:**\n- XCLAIM: O(log N)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nThe message was successfully claimed by Alice, who can now process the message and acknowledge it, and move things forward even if the original consumer is not recovering.\n\nIt is clear from the example above that as a side effect of successfully claiming a given message, the [`XCLAIM`](https://redis.io/docs/latest/commands/xclaim) command also returns it. However this is not mandatory. The **JUSTID** option can be used in order to return just the IDs of the message successfully claimed. This is useful if you want to reduce the bandwidth used between the client and the server (and also the performance of the command) and you are not interested in the message because your consumer is implemented in a way that it will rescan the history of pending messages from time to time.\n\nClaiming may also be implemented by a separate process: one that just checks the list of pending messages, and assigns idle messages to consumers that appear to be active. Active consumers can be obtained using one of the observability features of Redis streams. This is the topic of the next section."},{"id":"automatic-claiming","title":"Automatic claiming","role":"content","text":"The [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) command, added in Redis 6.2, implements the claiming process that we've described above.\n[`XPENDING`](https://redis.io/docs/latest/commands/xpending) and [`XCLAIM`](https://redis.io/docs/latest/commands/xclaim) provide the basic building blocks for different types of recovery mechanisms.\nThis command optimizes the generic process by having Redis manage it and offers a simple solution for most recovery needs.\n\n[`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) identifies idle pending messages and transfers ownership of them to a consumer.\nThe command's signature looks like this:\n\n[code example]\n\nSo, in the example above, I could have used automatic claiming to claim a single message like this:\n\nPractical pattern: Automatically claim idle pending messages using XAUTOCLAIM for simplified consumer failure recovery\n\n**Difficulty:** Advanced\n\n**Commands:** XAUTOCLAIM\n\n**Complexity:**\n- XAUTOCLAIM: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nLike [`XCLAIM`](https://redis.io/docs/latest/commands/xclaim), the command replies with an array of the claimed messages, but it also returns a stream ID that allows iterating the pending entries.\nThe stream ID is a cursor, and I can use it in my next call to continue in claiming idle pending messages:\n\nContinue automatic claiming using the cursor returned by XAUTOCLAIM to iterate through pending messages\n\n**Difficulty:** Advanced\n\n**Commands:** XAUTOCLAIM\n\n**Complexity:**\n- XAUTOCLAIM: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nWhen [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) returns the \"0-0\" stream ID as a cursor, that means that it reached the end of the consumer group pending entries list.\nThat doesn't mean that there are no new idle pending messages, so the process continues by calling [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) from the beginning of the stream."},{"id":"claiming-and-the-delivery-counter","title":"Claiming and the delivery counter","role":"content","text":"The counter that you observe in the [`XPENDING`](https://redis.io/docs/latest/commands/xpending) output is the number of deliveries of each message. The counter is incremented in two ways: when a message is successfully claimed via [`XCLAIM`](https://redis.io/docs/latest/commands/xclaim) or when an [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) call is used in order to access the history of pending messages.\n\nWhen there are failures, it is normal that messages will be delivered multiple times, but eventually they usually get processed and acknowledged. However there might be a problem processing some specific message, because it is corrupted or crafted in a way that triggers a bug in the processing code. In such a case what happens is that consumers will continuously fail to process this particular message. Because we have the counter of the delivery attempts, we can use that counter to detect messages that for some reason are not processable. So once the deliveries counter reaches a given large number that you chose, it is probably wiser to put such messages in another stream and send a notification to the system administrator. This is basically the way that Redis Streams implements the *dead letter* concept."},{"id":"releasing-messages-back-to-the-group-xnack","title":"Releasing messages back to the group: XNACK","role":"content","text":"Starting with Redis 8.8, the [`XNACK`](https://redis.io/docs/latest/commands/xnack) command provides a way for consumers to explicitly release pending messages back to the group without acknowledging them. This is the opposite of claiming - instead of taking ownership of messages from other consumers, a consumer can release its own messages back to the group for immediate re-delivery.\n\nThis capability addresses several common scenarios:\n\n1. **Graceful shutdown**: When a consumer is shutting down, it can release all its pending messages so other consumers can pick them up immediately without waiting for idle timeouts.\n\n2. **Consumer-side failures**: When a consumer encounters consumer-side issues (like network connectivity problems or resource constraints), it can release messages it cannot process rather than letting them sit idle.\n\n3. **Resource management**: A consumer under resource pressure can release complex or large messages that it cannot handle, allowing other consumers with more resources to process them.\n\n4. **Poison message handling**: A consumer can mark messages as permanently failed when it detects invalid or malicious content.\n\nThe command provides three modes that control how the delivery counter is adjusted:\n\n- **SILENT**: Decrements the delivery counter, essentially \"undoing\" the delivery. Use this for graceful shutdowns or when the failure is unrelated to the message content.\n- **FAIL**: Keeps the delivery counter unchanged. Use this when the current consumer cannot handle the message but others might be able to.\n- **FATAL**: Sets the delivery counter to maximum value, marking the message as permanently failed for dead letter processing.\n\nReleased messages are placed in a special \"NACK zone\" at the head of the consumer group's PEL, ensuring they are prioritized for re-delivery over other idle pending messages. This makes recovery much faster than traditional claiming mechanisms that rely on idle timeouts.\n\n[code example]\n\nAfter NACKing, the message appears with an empty consumer in [`XPENDING`](https://redis.io/docs/latest/commands/xpending) output and becomes immediately available for claiming or consumption via [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) with the CLAIM option."},{"id":"working-with-multiple-consumer-groups","title":"Working with multiple consumer groups","role":"content","text":"Redis Streams can be associated with multiple consumer groups, where each entry is delivered to all the stream's consumer groups. Within each consumer group, consumers handle a portion of the entries collaboratively. This design enables different applications or services to process the same stream data independently.\n\nTraditionally, when a consumer processesed a message, it acknowledged it using the [`XACK`](https://redis.io/docs/latest/commands/xack) command, which removed the entry reference from the Pending Entries List (PEL) of that specific consumer group. However, the entry remained in the stream and in the PELs of other consumer groups until they also acknowledge it. Applications needed to implement complex logic to delete entries from the stream only after all consumer groups had acknowledged them. This coordination was challenging to implement correctly and efficiently."},{"id":"enhanced-deletion-control-in-redis-82","title":"Enhanced deletion control in Redis 8.2","role":"content","text":"Starting with Redis 8.2, several commands provide enhanced control over how entries are handled with respect to multiple consumer groups:\n\n* [`XADD`](https://redis.io/docs/latest/commands/xadd) with trimming options now supports `KEEPREF`, `DELREF`, and `ACKED` modes\n* [`XTRIM`](https://redis.io/docs/latest/commands/xtrim) supports the same reference handling options\n* [`XDELEX`](https://redis.io/docs/latest/commands/xdelex) provides fine-grained deletion control\n* [`XACKDEL`](https://redis.io/docs/latest/commands/xackdel) combines acknowledgment and deletion atomically\n\nThese options control how consumer group references are handled:\n\n- **KEEPREF** (default): Preserves existing references to entries in all consumer groups' PELs, maintaining backward compatibility\n- **DELREF**: Removes all references to entries from all consumer groups' PELs, effectively cleaning up all traces of the messages\n- **ACKED**: Only processes entries that have been acknowledged by all consumer groups\n\nThe `ACKED` option is particularly useful as it automates the complex logic of coordinating deletion across multiple consumer groups, ensuring entries are only removed when all groups have finished processing them."},{"id":"streams-observability","title":"Streams observability","role":"content","text":"Messaging systems that lack observability are very hard to work with. Not knowing who is consuming messages, what messages are pending, the set of consumer groups active in a given stream, makes everything opaque. For this reason, Redis Streams and consumer groups have different ways to observe what is happening. We already covered [`XPENDING`](https://redis.io/docs/latest/commands/xpending), which allows us to inspect the list of messages that are under processing at a given moment, together with their idle time and number of deliveries.\n\nHowever we may want to do more than that, and the [`XINFO`](https://redis.io/docs/latest/commands/xinfo) command is an observability interface that can be used with sub-commands in order to get information about streams or consumer groups.\n\nThis command uses subcommands in order to show different information about the status of the stream and its consumer groups. For instance **XINFO STREAM <key>** reports information about the stream itself.\n\nGet detailed stream information including length, encoding, and consumer groups using XINFO STREAM\n\n**Difficulty:** Intermediate\n\n**Commands:** XINFO\n\n**Complexity:**\n- XINFO: Depends on subcommand\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nThe output shows information about how the stream is encoded internally, and also shows the first and last message in the stream. Another piece of information available is the number of consumer groups associated with this stream. We can dig further asking for more information about the consumer groups.\n\nList all consumer groups for a stream using XINFO GROUPS to see group status and pending message counts\n\n**Difficulty:** Intermediate\n\n**Commands:** XINFO\n\n**Complexity:**\n- XINFO: Depends on subcommand\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nAs you can see in this and in the previous output, the [`XINFO`](https://redis.io/docs/latest/commands/xinfo) command outputs a sequence of field-value items. Because it is an observability command this allows the human user to immediately understand what information is reported, and allows the command to report more information in the future by adding more fields without breaking compatibility with older clients. Other commands that must be more bandwidth efficient, like [`XPENDING`](https://redis.io/docs/latest/commands/xpending), just report the information without the field names.\n\nThe output of the example above, where the **GROUPS** subcommand is used, should be clear observing the field names. We can check in more detail the state of a specific consumer group by checking the consumers that are registered in the group.\n\nGet detailed consumer information for a group using XINFO CONSUMERS to monitor individual consumer status\n\n**Difficulty:** Advanced\n\n**Commands:** XINFO\n\n**Complexity:**\n- XINFO: Depends on subcommand\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nIn case you do not remember the syntax of the command, just ask the command itself for help:\n\n[code example]"},{"id":"differences-with-kafka-tm-partitions","title":"Differences with Kafka (TM) partitions","role":"content","text":"Consumer groups in Redis streams may resemble in some way Kafka (TM) partitioning-based consumer groups, however note that Redis streams are, in practical terms, very different. The partitions are only *logical* and the messages are just put into a single Redis key, so the way the different clients are served is based on who is ready to process new messages, and not from which partition clients are reading. For instance, if the consumer C3 at some point fails permanently, Redis will continue to serve C1 and C2 all the new messages arriving, as if now there are only two *logical* partitions.\n\nSimilarly, if a given consumer is much faster at processing messages than the other consumers, this consumer will receive proportionally more messages in the same unit of time. This is possible since Redis tracks all the unacknowledged messages explicitly, and remembers who received which message and the ID of the first message never delivered to any consumer.\n\nHowever, this also means that in Redis if you really want to partition messages in the same stream into multiple Redis instances, you have to use multiple keys and some sharding system such as Redis Cluster or some other application-specific sharding system. A single Redis stream is not automatically partitioned to multiple instances.\n\nWe could say that schematically the following is true:\n\n* If you use 1 stream -> 1 consumer, you are processing messages in order.\n* If you use N streams with N consumers, so that only a given consumer hits a subset of the N streams, you can scale the above model of 1 stream -> 1 consumer.\n* If you use 1 stream -> N consumers, you are load balancing to N consumers, however in that case, messages about the same logical item may be consumed out of order, because a given consumer may process message 3 faster than another consumer is processing message 4.\n\nSo basically Kafka partitions are more similar to using N different Redis keys, while Redis consumer groups are a server-side load balancing system of messages from a given stream to N different consumers."},{"id":"capped-streams","title":"Capped Streams","role":"content","text":"Many applications do not want to collect data into a stream forever. Sometimes it is useful to have at maximum a given number of items inside a stream, other times once a given size is reached, it is useful to move data from Redis to a storage which is not in memory and not as fast but suited to store the history for, potentially, decades to come. Redis streams have some support for this. One is the **MAXLEN** option of the [`XADD`](https://redis.io/docs/latest/commands/xadd) command. This option is very simple to use:\n\nLimit stream size using MAXLEN option with XADD to automatically evict old entries and maintain constant memory usage\n\n**Difficulty:** Intermediate\n\n**Commands:** XADD, XLEN, XRANGE\n\n**Complexity:**\n- XADD: O(1)\n- XLEN: O(1)\n- XRANGE: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nUsing **MAXLEN** the old entries are automatically evicted when the specified length is reached, so that the stream is left at a constant size. There is currently no option to tell the stream to just retain items that are not older than a given period, because such command, in order to run consistently, would potentially block for a long time in order to evict items. Imagine for example what happens if there is an insertion spike, then a long pause, and another insertion, all with the same maximum time. The stream would block to evict the data that became too old during the pause. So it is up to the user to do some planning and understand what is the maximum stream length desired. Moreover, while the length of the stream is proportional to the memory used, trimming by time is less simple to control and anticipate: it depends on the insertion rate which often changes over time (and when it does not change, then to just trim by size is trivial).\n\nHowever trimming with **MAXLEN** can be expensive: streams are represented by macro nodes into a radix tree, in order to be very memory efficient. Altering the single macro node, consisting of a few tens of elements, is not optimal. So it's possible to use the command in the following special form:\n\n[code example]\n\nThe `~` argument between the **MAXLEN** option and the actual count means, I don't really need this to be exactly 1000 items. It can be 1000 or 1010 or 1030, just make sure to save at least 1000 items. With this argument, the trimming is performed only when we can remove a whole node. This makes it much more efficient, and it is usually what you want. You'll note here that the client libraries have various implementations of this. For example, the Python client defaults to approximate and has to be explicitly set to a true length.\n\nThere is also the [`XTRIM`](https://redis.io/docs/latest/commands/xtrim) command, which performs something very similar to what the **MAXLEN** option does above, except that it can be run by itself:\n\nTrim a stream to a maximum length using XTRIM MAXLEN to remove old entries\n\n**Difficulty:** Intermediate\n\n**Commands:** XTRIM\n\n**Complexity:**\n- XTRIM: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nOr, as for the [`XADD`](https://redis.io/docs/latest/commands/xadd) option:\n\nUse approximate trimming with XTRIM MAXLEN ~ for more efficient memory management\n\n**Difficulty:** Intermediate\n\n**Commands:** XTRIM\n\n**Complexity:**\n- XTRIM: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nHowever, [`XTRIM`](https://redis.io/docs/latest/commands/xtrim) is designed to accept different trimming strategies. Another trimming strategy is **MINID**, that evicts entries with IDs lower than the one specified.\n\nAs [`XTRIM`](https://redis.io/docs/latest/commands/xtrim) is an explicit command, the user is expected to know about the possible shortcomings of different trimming strategies."},{"id":"trimming-with-consumer-group-awareness","title":"Trimming with consumer group awareness","role":"content","text":"Starting with Redis 8.2, both [`XADD`](https://redis.io/docs/latest/commands/xadd) with trimming options and [`XTRIM`](https://redis.io/docs/latest/commands/xtrim) support enhanced control over how trimming interacts with consumer groups through the `KEEPREF`, `DELREF`, and `ACKED` options:\n\n[code example]\n\n- **KEEPREF** (default): Trims entries according to the strategy but preserves references in consumer group PELs\n- **DELREF**: Trims entries and removes all references from consumer group PELs\n- **ACKED**: Only trims entries that have been acknowledged by all consumer groups\n\nThe `ACKED` option is particularly useful for maintaining data integrity across multiple consumer groups, ensuring that entries are only removed when all groups have finished processing them.\n\nAnother useful eviction strategy that may be added to [`XTRIM`](https://redis.io/docs/latest/commands/xtrim) in the future, is to remove by a range of IDs to ease use of [`XRANGE`](https://redis.io/docs/latest/commands/xrange) and [`XTRIM`](https://redis.io/docs/latest/commands/xtrim) to move data from Redis to other storage systems if needed."},{"id":"special-ids-in-the-streams-api","title":"Special IDs in the streams API","role":"content","text":"You may have noticed that there are several special IDs that can be used in the Redis API. Here is a short recap, so that they can make more sense in the future.\n\nThe first two special IDs are `-` and `+`, and are used in range queries with the [`XRANGE`](https://redis.io/docs/latest/commands/xrange) command. Those two IDs respectively mean the smallest ID possible (that is basically `0-1`) and the greatest ID possible (that is `18446744073709551615-18446744073709551615`). As you can see it is a lot cleaner to write `-` and `+` instead of those numbers.\n\nThen there are APIs where we want to say, the ID of the item with the greatest ID inside the stream. This is what `$` means. So for instance if I want only new entries with [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) I use this ID to signify I already have all the existing entries, but not the new ones that will be inserted in the future. Similarly when I create or set the ID of a consumer group, I can set the last delivered item to `$` in order to just deliver new entries to the consumers in the group.\n\nAs you can see `$` does not mean `+`, they are two different things, as `+` is the greatest ID possible in every possible stream, while `$` is the greatest ID in a given stream containing given entries. Moreover APIs will usually only understand `+` or `$`, yet it was useful to avoid loading a given symbol with multiple meanings.\n\nAnother special ID is `>`, that is a special meaning only related to consumer groups and only when the [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) command is used. This special ID means that we want only entries that were never delivered to other consumers so far. So basically the `>` ID is the *last delivered ID* of a consumer group.\n\nFinally the special ID `*`, that can be used only with the [`XADD`](https://redis.io/docs/latest/commands/xadd) command, means to auto select an ID for us for the new entry.\n\nSo we have `-`, `+`, `$`, `>` and `*`, and all have a different meaning, and most of the time, can be used in different contexts."},{"id":"persistence-replication-and-message-safety","title":"Persistence, replication and message safety","role":"content","text":"A Stream, like any other Redis data structure, is asynchronously replicated to replicas and persisted into AOF and RDB files. However what may not be so obvious is that also the consumer groups full state is propagated to AOF, RDB and replicas, so if a message is pending in the master, also the replica will have the same information. Similarly, after a restart, the AOF will restore the consumer groups' state.\n\nHowever note that Redis streams and consumer groups are persisted and replicated using the Redis default replication, so:\n\n* AOF must be used with a strong fsync policy if persistence of messages is important in your application.\n* By default the asynchronous replication will not guarantee that [`XADD`](https://redis.io/docs/latest/commands/xadd) commands or consumer groups state changes are replicated: after a failover something can be missing depending on the ability of replicas to receive the data from the master.\n* The [`WAIT`](https://redis.io/docs/latest/commands/wait) command may be used in order to force the propagation of the changes to a set of replicas. However note that while this makes it very unlikely that data is lost, the Redis failover process as operated by Sentinel or Redis Cluster performs only a *best effort* check to failover to the replica which is the most updated, and under certain specific failure conditions may promote a replica that lacks some data.\n\nSo when designing an application using Redis streams and consumer groups, make sure to understand the semantical properties your application should have during failures, and configure things accordingly, evaluating whether it is safe enough for your use case."},{"id":"removing-single-items-from-a-stream","title":"Removing single items from a stream","role":"content","text":"Streams also have a special command for removing items from the middle of a stream, just by ID. Normally for an append only data structure this may look like an odd feature, but it is actually useful for applications involving, for instance, privacy regulations. The command is called [`XDEL`](https://redis.io/docs/latest/commands/xdel) and receives the name of the stream followed by the IDs to delete:\n\nDelete specific entries from a stream by ID using XDEL for privacy or data cleanup purposes\n\n**Difficulty:** Advanced\n\n**Commands:** XRANGE, XDEL\n\n**Complexity:**\n- XRANGE: O(N)\n- XDEL: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"enhanced-deletion-with-xdelex","title":"Enhanced deletion with XDELEX","role":"content","text":"Starting with Redis 8.2, the [`XDELEX`](https://redis.io/docs/latest/commands/xdelex) command provides enhanced control over entry deletion, particularly when working with consumer groups. Like other enhanced commands, it supports `KEEPREF`, `DELREF`, and `ACKED` options:\n\n[code example]\n\nThis allows you to delete entries only when they have been acknowledged by all consumer groups (`ACKED`), remove all consumer group references (`DELREF`), or preserve existing references (`KEEPREF`)."},{"id":"zero-length-streams","title":"Zero length streams","role":"content","text":"A difference between streams and other Redis data structures is that when the other data structures no longer have any elements, as a side effect of calling commands that remove elements, the key itself will be removed. So for instance, a sorted set will be completely removed when a call to [`ZREM`](https://redis.io/docs/latest/commands/zrem) will remove the last element in the sorted set. Streams, on the other hand, are allowed to stay at zero elements, both as a result of using a **MAXLEN** option with a count of zero ([`XADD`](https://redis.io/docs/latest/commands/xadd) and [`XTRIM`](https://redis.io/docs/latest/commands/xtrim) commands), or because [`XDEL`](https://redis.io/docs/latest/commands/xdel) was called.\n\nThe reason why such an asymmetry exists is because Streams may have associated consumer groups, and we do not want to lose the state that the consumer groups defined just because there are no longer any items in the stream. Currently the stream is not deleted even when it has no associated consumer groups."},{"id":"total-latency-of-consuming-a-message","title":"Total latency of consuming a message","role":"content","text":"Non blocking stream commands like [`XRANGE`](https://redis.io/docs/latest/commands/xrange) and [`XREAD`](https://redis.io/docs/latest/commands/xread) or [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) without the BLOCK option are served synchronously like any other Redis command, so to discuss latency of such commands is meaningless: it is more interesting to check the time complexity of the commands in the Redis documentation. It should be enough to say that stream commands are at least as fast as sorted set commands when extracting ranges, and that [`XADD`](https://redis.io/docs/latest/commands/xadd) is very fast and can easily insert from half a million to one million items per second in an average machine if pipelining is used.\n\nHowever latency becomes an interesting parameter if we want to understand the delay of processing a message, in the context of blocking consumers in a consumer group, from the moment the message is produced via [`XADD`](https://redis.io/docs/latest/commands/xadd), to the moment the message is obtained by the consumer because [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) returned with the message."},{"id":"how-serving-blocked-consumers-works","title":"How serving blocked consumers works","role":"content","text":"Before providing the results of performed tests, it is interesting to understand what model Redis uses in order to route stream messages (and in general actually how any blocking operation waiting for data is managed).\n\n* The blocked client is referenced in a hash table that maps keys for which there is at least one blocking consumer, to a list of consumers that are waiting for such key. This way, given a key that received data, we can resolve all the clients that are waiting for such data.\n* When a write happens, in this case when the [`XADD`](https://redis.io/docs/latest/commands/xadd) command is called, it calls the `signalKeyAsReady()` function. This function will put the key into a list of keys that need to be processed, because such keys may have new data for blocked consumers. Note that such *ready keys* will be processed later, so in the course of the same event loop cycle, it is possible that the key will receive other writes.\n* Finally, before returning into the event loop, the *ready keys* are finally processed. For each key the list of clients waiting for data is scanned, and if applicable, such clients will receive the new data that arrived. In the case of streams the data is the messages in the applicable range requested by the consumer.\n\nAs you can see, basically, before returning to the event loop both the client calling [`XADD`](https://redis.io/docs/latest/commands/xadd) and the clients blocked to consume messages, will have their reply in the output buffers, so the caller of [`XADD`](https://redis.io/docs/latest/commands/xadd) should receive the reply from Redis at about the same time the consumers will receive the new messages.\n\nThis model is *push-based*, since adding data to the consumers buffers will be performed directly by the action of calling [`XADD`](https://redis.io/docs/latest/commands/xadd), so the latency tends to be quite predictable."},{"id":"latency-tests-results","title":"Latency tests results","role":"content","text":"In order to check these latency characteristics a test was performed using multiple instances of Ruby programs pushing messages having as an additional field the computer millisecond time, and Ruby programs reading the messages from the consumer group and processing them. The message processing step consisted of comparing the current computer time with the message timestamp, in order to understand the total latency.\n\nResults obtained:\n\n[code example]\n\nSo 99.9% of requests have a latency <= 2 milliseconds, with the outliers that remain still very close to the average.\n\nAdding a few million unacknowledged messages to the stream does not change the gist of the benchmark, with most queries still processed with very short latency.\n\nA few remarks:\n\n* Here we processed up to 10k messages per iteration, this means that the `COUNT` parameter of [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) was set to 10000. This adds a lot of latency but is needed in order to allow the slow consumers to be able to keep with the message flow. So you can expect a real world latency that is a lot smaller.\n* The system used for this benchmark is very slow compared to today's standards."},{"id":"learn-more","title":"Learn more","role":"related","text":"* [Redis Streams Explained](https://www.youtube.com/watch?v=Z8qcpXyMAiA) is an entertaining introduction to streams in Redis.\n* [Redis University's RU202](https://university.redis.com/courses/ru202/) is a free, online course dedicated to Redis Streams."}],"examples":[{"id":"examples-ex0","language":"plaintext","code":"> XADD race:france * rider Castilla speed 30.2 position 1 location_id 1\n\"1692632086370-0\"\n> XADD race:france * rider Norem speed 28.8 position 3 location_id 1\n\"1692632094485-0\"\n> XADD race:france * rider Prickett speed 29.7 position 2 location_id 1\n\"1692632102976-0\"","section_id":"examples"},{"id":"examples-ex1","language":"csharp","code":"RedisValue res1 = db.StreamAdd(\n            \"race:france\",\n            [\n                new(\"rider\", \"Castilla\"),\n                new(\"speed\", 30.2),\n                new(\"position\", 1),\n                new(\"location_id\", 1)\n            ]\n        );\n        Console.WriteLine(res1);    // >>> 1712668482289-0\n\n        RedisValue res2 = db.StreamAdd(\n            \"race:france\",\n            [\n                new(\"rider\", \"Norem\"),\n                new(\"speed\", 28.8),\n                new(\"position\", 3),\n                new(\"location_id\", 1)\n            ]\n        );\n        Console.WriteLine(res2);    // >>> 1712668766534-1\n\n        RedisValue res3 = db.StreamAdd(\n            \"race:france\",\n            [\n                new(\"rider\", \"Prickett\"),\n                new(\"speed\", 29.7),\n                new(\"position\", 2),\n                new(\"location_id\", 1)\n            ]\n        );\n        Console.WriteLine(res3);    // >>> 1712669055705-0","section_id":"examples"},{"id":"examples-ex2","language":"go","code":"res1, err := rdb.XAdd(ctx, &redis.XAddArgs{\n\t\tStream: \"race:france\",\n\t\tValues: map[string]interface{}{\n\t\t\t\"rider\":       \"Castilla\",\n\t\t\t\"speed\":       30.2,\n\t\t\t\"position\":    1,\n\t\t\t\"location_id\": 1,\n\t\t},\n\t}).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// fmt.Println(res1) // >>> 1692632086370-0\n\n\tres2, err := rdb.XAdd(ctx, &redis.XAddArgs{\n\t\tStream: \"race:france\",\n\t\tValues: map[string]interface{}{\n\t\t\t\"rider\":       \"Norem\",\n\t\t\t\"speed\":       28.8,\n\t\t\t\"position\":    3,\n\t\t\t\"location_id\": 1,\n\t\t},\n\t}).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// fmt.PrintLn(res2) // >>> 1692632094485-0\n\n\tres3, err := rdb.XAdd(ctx, &redis.XAddArgs{\n\t\tStream: \"race:france\",\n\t\tValues: map[string]interface{}{\n\t\t\t\"rider\":       \"Prickett\",\n\t\t\t\"speed\":       29.7,\n\t\t\t\"position\":    2,\n\t\t\t\"location_id\": 1,\n\t\t},\n\t}).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// fmt.Println(res3) // >>> 1692632102976-0","section_id":"examples"},{"id":"examples-ex3","language":"java","code":"StreamEntryID res1 = jedis.xadd(\"race:france\",new HashMap<String,String>(){{put(\"rider\",\"Castilla\");put(\"speed\",\"30.2\");put(\"position\",\"1\");put(\"location_id\",\"1\");}} , XAddParams.xAddParams());\n\n    System.out.println(res1); // >>> 1701760582225-0\n\n    StreamEntryID res2 = jedis.xadd(\"race:france\",new HashMap<String,String>(){{put(\"rider\",\"Norem\");put(\"speed\",\"28.8\");put(\"position\",\"3\");put(\"location_id\",\"1\");}} , XAddParams.xAddParams());\n\n    System.out.println(res2); // >>> 1701760582225-1\n\n    StreamEntryID res3 = jedis.xadd(\"race:france\",new HashMap<String,String>(){{put(\"rider\",\"Prickett\");put(\"speed\",\"29.7\");put(\"position\",\"2\");put(\"location_id\",\"1\");}} , XAddParams.xAddParams());\n\n    System.out.println(res3); // >>> 1701760582226-0","section_id":"examples"},{"id":"examples-ex4","language":"javascript","code":"const res1 = await client.xAdd(\n  'race:france', '*', {\n    'rider': 'Castilla',\n    'speed': '30.2',\n    'position': '1',\n    'location_id': '1'\n  }\n);\nconsole.log(res1); // >>> 1700073067968-0 N.B. actual values will differ from these examples\n\nconst res2 = await client.xAdd(\n  'race:france', '*', {\n    'rider': 'Norem',\n    'speed': '28.8',\n    'position': '3',\n    'location_id': '1'\n  },\n);\nconsole.log(res2); // >>> 1692629594113-0\n\nconst res3 = await client.xAdd(\n  'race:france', '*', {\n    'rider': 'Prickett',\n    'speed': '29.7',\n    'position': '2',\n    'location_id': '1'\n  },\n);\nconsole.log(res3); // >>> 1692629613374-0","section_id":"examples"},{"id":"examples-ex5","language":"python","code":"res1 = r.xadd(\n    \"race:france\",\n    {\"rider\": \"Castilla\", \"speed\": 30.2, \"position\": 1, \"location_id\": 1},\n)\nprint(res1)  # >>> 1692629576966-0\n\nres2 = r.xadd(\n    \"race:france\",\n    {\"rider\": \"Norem\", \"speed\": 28.8, \"position\": 3, \"location_id\": 1},\n)\nprint(res2)  # >>> 1692629594113-0\n\nres3 = r.xadd(\n    \"race:france\",\n    {\"rider\": \"Prickett\", \"speed\": 29.7, \"position\": 2, \"location_id\": 1},\n)\nprint(res3)  # >>> 1692629613374-0","section_id":"examples"},{"id":"examples-ex6","language":"ruby","code":"res1 = r.xadd('race:france', {\n  'rider' => 'Castilla',\n  'speed' => 30.2,\n  'position' => 1,\n  'location_id' => 1\n})\nputs res1 # >>> 1692632086370-0, for example\n\nres2 = r.xadd('race:france', {\n  'rider' => 'Norem',\n  'speed' => 28.8,\n  'position' => 3,\n  'location_id' => 1\n})\nputs res2 # >>> 1692632094485-0, for example\n\nres3 = r.xadd('race:france', {\n  'rider' => 'Prickett',\n  'speed' => 29.7,\n  'position' => 2,\n  'location_id' => 1\n})\nputs res3 # >>> 1692632102976-0, for example","section_id":"examples"},{"id":"examples-ex7","language":"rust","code":"let res1: Option<String> = r\n            .xadd(\n                \"race:france\",\n                \"*\",\n                &[\n                    (\"rider\", \"Castilla\"),\n                    (\"speed\", \"30.2\"),\n                    (\"position\", \"1\"),\n                    (\"location_id\", \"1\"),\n                ],\n            )\n            .await\n            .expect(\"xadd 1\");\n        let res1 = res1.expect(\"missing stream id\");\n        println!(\"{res1}\"); // >>> 1692632086370-0\n\n        let res2: Option<String> = r\n            .xadd(\n                \"race:france\",\n                \"*\",\n                &[\n                    (\"rider\", \"Norem\"),\n                    (\"speed\", \"28.8\"),\n                    (\"position\", \"3\"),\n                    (\"location_id\", \"1\"),\n                ],\n            )\n            .await\n            .expect(\"xadd 2\");\n        let res2 = res2.expect(\"missing stream id\");\n        println!(\"{res2}\"); // >>> 1692632094485-0\n\n        let res3: Option<String> = r\n            .xadd(\n                \"race:france\",\n                \"*\",\n                &[\n                    (\"rider\", \"Prickett\"),\n                    (\"speed\", \"29.7\"),\n                    (\"position\", \"2\"),\n                    (\"location_id\", \"1\"),\n                ],\n            )\n            .await\n            .expect(\"xadd 3\");\n        let res3 = res3.expect(\"missing stream id\");\n        println!(\"{res3}\"); // >>> 1692632102976-0","section_id":"examples"},{"id":"examples-ex8","language":"rust","code":"let res1 = {\n            let res: Option<String> = r\n                .xadd(\n                    \"race:france\",\n                    \"*\",\n                    &[\n                        (\"rider\", \"Castilla\"),\n                        (\"speed\", \"30.2\"),\n                        (\"position\", \"1\"),\n                        (\"location_id\", \"1\"),\n                    ],\n                )\n                .expect(\"xadd 1\");\n            res.expect(\"missing stream id\")\n        };\n        println!(\"{res1}\"); // >>> 1692632086370-0\n\n        let res2 = {\n            let res: Option<String> = r\n                .xadd(\n                    \"race:france\",\n                    \"*\",\n                    &[\n                        (\"rider\", \"Norem\"),\n                        (\"speed\", \"28.8\"),\n                        (\"position\", \"3\"),\n                        (\"location_id\", \"1\"),\n                    ],\n                )\n                .expect(\"xadd 2\");\n            res.expect(\"missing stream id\")\n        };\n        println!(\"{res2}\"); // >>> 1692632094485-0\n\n        let res3 = {\n            let res: Option<String> = r\n                .xadd(\n                    \"race:france\",\n                    \"*\",\n                    &[\n                        (\"rider\", \"Prickett\"),\n                        (\"speed\", \"29.7\"),\n                        (\"position\", \"2\"),\n                        (\"location_id\", \"1\"),\n                    ],\n                )\n                .expect(\"xadd 3\");\n            res.expect(\"missing stream id\")\n        };\n        println!(\"{res3}\"); // >>> 1692632102976-0","section_id":"examples"},{"id":"examples-ex9","language":"plaintext","code":"> XRANGE race:france 1692632086370-0 + COUNT 2\n1) 1) \"1692632086370-0\"\n   2) 1) \"rider\"\n      2) \"Castilla\"\n      3) \"speed\"\n      4) \"30.2\"\n      5) \"position\"\n      6) \"1\"\n      7) \"location_id\"\n      8) \"1\"\n2) 1) \"1692632094485-0\"\n   2) 1) \"rider\"\n      2) \"Norem\"\n      3) \"speed\"\n      4) \"28.8\"\n      5) \"position\"\n      6) \"3\"\n      7) \"location_id\"\n      8) \"1\"","section_id":"examples"},{"id":"examples-ex10","language":"csharp","code":"StreamEntry[] res4 = db.StreamRange(\"race:france\", \"1712668482289-0\", \"+\", 2);\n\n        foreach (StreamEntry entry in res4)\n        {\n            Console.WriteLine($\"{entry.Id}: [{string.Join(\", \", entry.Values.Select(b => $\"{b.Name}: {b.Value}\"))}]\");\n        }\n\n        // >>> 1712668482289-0: [rider: Castilla, speed: 30.199999999999999, position: 1, location_id: 1]\n        // >>> 1712668766534-1: [rider: Norem, speed: 28.800000000000001, position: 3, location_id: 1]","section_id":"examples"},{"id":"examples-ex11","language":"go","code":"res4, err := rdb.XRangeN(ctx, \"race:france\", \"1691765278160-0\", \"+\", 2).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4)\n\t// >>> [{1692632086370-0 map[location_id:1 position:1 rider:Castilla...","section_id":"examples"},{"id":"examples-ex12","language":"java","code":"List<StreamEntry> res4 = jedis.xrange(\"race:france\",\"1701760582225-0\",\"+\",2);\n\n    System.out.println(res4); // >>> [1701760841292-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701760841292-1 {rider=Norem, speed=28.8, location_id=1, position=3}]","section_id":"examples"},{"id":"examples-ex13","language":"javascript","code":"const res4 = await client.xRange('race:france', '1691765278160-0', '+', {COUNT: 2});\nconsole.log(res4); // >>> [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }, { id: '1692629594113-0', message: { rider: 'Norem', speed: '28.8', position: '3', location_id: '1' } }]","section_id":"examples"},{"id":"examples-ex14","language":"python","code":"res4 = r.xrange(\"race:france\", \"1691765278160-0\", \"+\", 2)\nprint(\n    res4\n)  # >>> [\n#   ('1692629576966-0',\n#       {'rider': 'Castilla', 'speed': '30.2', 'position': '1', 'location_id': '1'}\n#   ),\n#   ('1692629594113-0',\n#       {'rider': 'Norem', 'speed': '28.8', 'position': '3', 'location_id': '1'}\n#   )\n# ]","section_id":"examples"},{"id":"examples-ex15","language":"ruby","code":"r.del('race:france')\nr.xadd('race:france', {\n  'rider' => 'Castilla',\n  'speed' => '30.2',\n  'position' => '1',\n  'location_id' => '1'\n}, id: '1692632086370-0')\nr.xadd('race:france', {\n  'rider' => 'Norem',\n  'speed' => '28.8',\n  'position' => '3',\n  'location_id' => '1'\n}, id: '1692632094485-0')\nr.xadd('race:france', {\n  'rider' => 'Prickett',\n  'speed' => '29.7',\n  'position' => '2',\n  'location_id' => '1'\n}, id: '1692632102976-0')\nr.xadd('race:france', {\n  'rider' => 'Castilla',\n  'speed' => '29.9',\n  'position' => '1',\n  'location_id' => '2'\n}, id: '1692632147973-0')\nres4 = r.xrange('race:france', '1692632086370-0', '+', count: 2)\nputs res4.inspect\n# >>> [[\"1692632086370-0\", {\"rider\" => \"Castilla\", \"speed\" => \"30.2\", \"position\" => \"1\", \"location_id\" => \"1\"}],\n#  [\"1692632094485-0\", {\"rider\" => \"Norem\", \"speed\" => \"28.8\", \"position\" => \"3\", \"location_id\" => \"1\"}]]","section_id":"examples"},{"id":"examples-ex16","language":"rust","code":"if let Ok(res) = r.xrange_count(\"race:france\", \"1692632086370-0\", \"+\", 2).await {\n            let res: StreamRangeReply = res;\n            let view: Vec<_> = res\n                .ids\n                .iter()\n                .map(|entry| {\n                    (\n                        entry.id.clone(),\n                        vec![\n                            (\"rider\".to_string(), entry.get::<String>(\"rider\").expect(\"missing rider\")),\n                            (\"speed\".to_string(), entry.get::<String>(\"speed\").expect(\"missing speed\")),\n                            (\"position\".to_string(), entry.get::<String>(\"position\").expect(\"missing position\")),\n                            (\n                                \"location_id\".to_string(),\n                                entry.get::<String>(\"location_id\").expect(\"missing location_id\"),\n                            ),\n                        ],\n                    )\n                })\n                .collect();\n            println!(\"{view:?}\");\n            // >>> [(\"1692632086370-0\", [(\"rider\", \"Castilla\"), (\"speed\", \"30.2\"), (\"position\", \"1\"), (\"location_id\", \"1\")]), (\"1692632094485-0\", [(\"rider\", \"Norem\"), (\"speed\", \"28.8\"), (\"position\", \"3\"), (\"location_id\", \"1\")])]\n        }","section_id":"examples"},{"id":"examples-ex17","language":"rust","code":"if let Ok(res) = r.xrange_count(\"race:france\", \"1692632086370-0\", \"+\", 2) {\n            let res: StreamRangeReply = res;\n            let view: Vec<_> = res\n                .ids\n                .iter()\n                .map(|entry| {\n                    (\n                        entry.id.clone(),\n                        vec![\n                            (\"rider\".to_string(), entry.get::<String>(\"rider\").expect(\"missing rider\")),\n                            (\"speed\".to_string(), entry.get::<String>(\"speed\").expect(\"missing speed\")),\n                            (\"position\".to_string(), entry.get::<String>(\"position\").expect(\"missing position\")),\n                            (\n                                \"location_id\".to_string(),\n                                entry.get::<String>(\"location_id\").expect(\"missing location_id\"),\n                            ),\n                        ],\n                    )\n                })\n                .collect();\n            println!(\"{view:?}\");\n            // >>> [(\"1692632086370-0\", [(\"rider\", \"Castilla\"), (\"speed\", \"30.2\"), (\"position\", \"1\"), (\"location_id\", \"1\")]), (\"1692632094485-0\", [(\"rider\", \"Norem\"), (\"speed\", \"28.8\"), (\"position\", \"3\"), (\"location_id\", \"1\")])]\n        }","section_id":"examples"},{"id":"examples-ex18","language":"plaintext","code":"> XREAD COUNT 100 BLOCK 300 STREAMS race:france $\n(nil)","section_id":"examples"},{"id":"examples-ex19","language":"csharp","code":"StreamEntry[] res5 = db.StreamRead(\"race:france\", 0, 100);\n\n        foreach (StreamEntry entry in res4)\n        {\n            Console.WriteLine($\"{entry.Id}: [{string.Join(\", \", entry.Values.Select(b => $\"{b.Name}: {b.Value}\"))}]\");\n        }\n\n        // >>> 1712668482289-0: [rider: Castilla, speed: 30.199999999999999, position: 1, location_id: 1]\n        // >>> 1712668766534-1: [rider: Norem, speed: 28.800000000000001, position: 3, location_id: 1]\n        // >>> 1712669055705-0: [rider: Prickett, speed: 29.699999999999999, position: 2, location_id: 1]","section_id":"examples"},{"id":"examples-ex20","language":"go","code":"res5, err := rdb.XRead(ctx, &redis.XReadArgs{\n\t\tStreams: []string{\"race:france\", \"0\"},\n\t\tCount:   100,\n\t\tBlock:   300,\n\t}).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res5)\n\t// >>> // [{race:france [{1692632086370-0 map[location_id:1 position:1...","section_id":"examples"},{"id":"examples-ex21","language":"java","code":"List<Map.Entry<String, List<StreamEntry>>> res5= jedis.xread(XReadParams.xReadParams().block(300).count(100),new HashMap<String,StreamEntryID>(){{put(\"race:france\",new StreamEntryID());}});\n    System.out.println(\n      res5\n    ); // >>> [race:france=[1701761996660-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701761996661-0 {rider=Norem, speed=28.8, location_id=1, position=3}, 1701761996661-1 {rider=Prickett, speed=29.7, location_id=1, position=2}]]","section_id":"examples"},{"id":"examples-ex22","language":"javascript","code":"const res5 = await client.xRead({\n  key: 'race:france',\n  id: '0-0'\n}, {\n  COUNT: 100,\n  BLOCK: 300\n});\nconsole.log(res5); // >>> [{ name: 'race:france', messages: [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }, { id: '1692629594113-0', message: { rider: 'Norem', speed: '28.8', position: '3', location_id: '1' } }, { id: '1692629613374-0', message: { rider: 'Prickett', speed: '29.7', position: '2', location_id: '1' } }] }]","section_id":"examples"},{"id":"examples-ex23","language":"python","code":"res5 = r.xread(streams={\"race:france\": 0}, count=100, block=300)\nprint(\n    res5\n)\n# >>> [\n#   ['race:france',\n#       [('1692629576966-0',\n#           {'rider': 'Castilla', 'speed': '30.2', 'position': '1', 'location_id': '1'}\n#       ),\n#       ('1692629594113-0',\n#           {'rider': 'Norem', 'speed': '28.8', 'position': '3', 'location_id': '1'}\n#       ),\n#       ('1692629613374-0',\n#           {'rider': 'Prickett', 'speed': '29.7', 'position': '2', 'location_id': '1'}\n#       )]\n# ]\n# ]","section_id":"examples"},{"id":"examples-ex24","language":"ruby","code":"r.del('race:france')\nr.xadd('race:france', {'rider' => 'Castilla'}, id: '1692632086370-0')\nres5 = r.xread(['race:france'], ['$'], count: 100, block: 300)\nputs res5.inspect # >>> {}","section_id":"examples"},{"id":"examples-ex25","language":"rust","code":"let opts = StreamReadOptions::default().count(100).block(300);\n        if let Ok(res) = r.xread_options(&[\"race:france\"], &[\"$\"], &opts).await {\n            let res: Option<StreamReadReply> = res;\n            println!(\"{res:?}\"); // >>> None\n        }","section_id":"examples"},{"id":"examples-ex26","language":"rust","code":"let opts = StreamReadOptions::default().count(100).block(300);\n        if let Ok(res) = r.xread_options(&[\"race:france\"], &[\"$\"], &opts) {\n            let res: Option<StreamReadReply> = res;\n            println!(\"{res:?}\"); // >>> None\n        }","section_id":"examples"},{"id":"streams-basics-ex0","language":"plaintext","code":"> XADD race:france * rider Castilla speed 29.9 position 1 location_id 2\n\"1692632147973-0\"","section_id":"streams-basics"},{"id":"streams-basics-ex1","language":"csharp","code":"RedisValue res6 = db.StreamAdd(\n            \"race:france\",\n            [\n                new(\"rider\", \"Castilla\"),\n                new(\"speed\", 29.9),\n                new(\"position\", 1),\n                new(\"location_id\", 2)\n            ]\n        );\n\n        Console.WriteLine(res6);    // >>> 1712675674750-0","section_id":"streams-basics"},{"id":"streams-basics-ex2","language":"go","code":"res6, err := rdb.XAdd(ctx, &redis.XAddArgs{\n\t\tStream: \"race:france\",\n\t\tValues: map[string]interface{}{\n\t\t\t\"rider\":       \"Castilla\",\n\t\t\t\"speed\":       29.9,\n\t\t\t\"position\":    1,\n\t\t\t\"location_id\": 2,\n\t\t},\n\t}).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t//fmt.Println(res6) // >>> 1692632147973-0","section_id":"streams-basics"},{"id":"streams-basics-ex3","language":"java","code":"StreamEntryID res6 = jedis.xadd(\"race:france\",new HashMap<String,String>(){{put(\"rider\",\"Castilla\");put(\"speed\",\"29.9\");put(\"position\",\"2\");put(\"location_id\",\"1\");}} , XAddParams.xAddParams());\n    System.out.println(res6); // >>> 1701762285679-0","section_id":"streams-basics"},{"id":"streams-basics-ex4","language":"javascript","code":"const res6 = await client.xAdd(\n  'race:france', '*', {\n    'rider': 'Castilla',\n    'speed': '29.9',\n    'position': '1',\n    'location_id': '2'\n  }\n);\nconsole.log(res6); // >>> 1692629676124-0","section_id":"streams-basics"},{"id":"streams-basics-ex5","language":"python","code":"res6 = r.xadd(\n    \"race:france\",\n    {\"rider\": \"Castilla\", \"speed\": 29.9, \"position\": 1, \"location_id\": 2},\n)\nprint(res6)  # >>> 1692629676124-0","section_id":"streams-basics"},{"id":"streams-basics-ex6","language":"ruby","code":"res6 = r.xadd('race:france', {\n  'rider' => 'Castilla',\n  'speed' => 29.9,\n  'position' => 1,\n  'location_id' => 2\n})\nputs res6 # >>> 1692632147973-0, for example","section_id":"streams-basics"},{"id":"streams-basics-ex7","language":"rust","code":"if let Ok(res) = r\n            .xadd(\n                \"race:france\",\n                \"*\",\n                &[\n                    (\"rider\", \"Castilla\"),\n                    (\"speed\", \"29.9\"),\n                    (\"position\", \"1\"),\n                    (\"location_id\", \"2\"),\n                ],\n            )\n            .await\n        {\n            let res: Option<String> = res;\n            let res = res.expect(\"missing stream id\");\n            println!(\"{res}\"); // >>> 1692632147973-0\n        }","section_id":"streams-basics"},{"id":"streams-basics-ex8","language":"rust","code":"if let Ok(res) = r.xadd(\n            \"race:france\",\n            \"*\",\n            &[\n                (\"rider\", \"Castilla\"),\n                (\"speed\", \"29.9\"),\n                (\"position\", \"1\"),\n                (\"location_id\", \"2\"),\n            ],\n        ) {\n            let res: Option<String> = res;\n            let res = res.expect(\"missing stream id\");\n            println!(\"{res}\"); // >>> 1692632147973-0\n        }","section_id":"streams-basics"},{"id":"streams-basics-ex9","language":"plaintext","code":"> XLEN race:france\n(integer) 4","section_id":"streams-basics"},{"id":"streams-basics-ex10","language":"csharp","code":"long res7 = db.StreamLength(\"race:france\");\n        Console.WriteLine(res7);    // >>> 4","section_id":"streams-basics"},{"id":"streams-basics-ex11","language":"go","code":"res7, err := rdb.XLen(ctx, \"race:france\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res7) // >>> 4","section_id":"streams-basics"},{"id":"streams-basics-ex12","language":"java","code":"long res7 = jedis.xlen(\"race:france\");\n    System.out.println(res7); // >>> 4","section_id":"streams-basics"},{"id":"streams-basics-ex13","language":"javascript","code":"const res7 = await client.xLen('race:france');\nconsole.log(res7); // >>> 4","section_id":"streams-basics"},{"id":"streams-basics-ex14","language":"python","code":"res7 = r.xlen(\"race:france\")\nprint(res7)  # >>> 4","section_id":"streams-basics"},{"id":"streams-basics-ex15","language":"ruby","code":"r.del('race:france')\nr.xadd('race:france', {'rider' => 'Castilla'}, id: '1692632086370-0')\nr.xadd('race:france', {'rider' => 'Norem'}, id: '1692632094485-0')\nr.xadd('race:france', {'rider' => 'Prickett'}, id: '1692632102976-0')\nr.xadd('race:france', {'rider' => 'Castilla'}, id: '1692632147973-0')\nres7 = r.xlen('race:france')\nputs res7 # >>> 4","section_id":"streams-basics"},{"id":"streams-basics-ex16","language":"rust","code":"add_france_fixed(&mut r).await;\n        if let Ok(res) = r.xlen(\"race:france\").await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 4\n        }","section_id":"streams-basics"},{"id":"streams-basics-ex17","language":"rust","code":"if let Ok(res) = r.xlen(\"race:france\") {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 4\n        }","section_id":"streams-basics"},{"id":"entry-ids-ex0","language":"plaintext","code":"<millisecondsTime>-<sequenceNumber>","section_id":"entry-ids"},{"id":"entry-ids-ex1","language":"plaintext","code":"> XADD race:usa 0-1 racer Castilla\n0-1\n> XADD race:usa 0-2 racer Norem\n0-2","section_id":"entry-ids"},{"id":"entry-ids-ex2","language":"csharp","code":"RedisValue res8 = db.StreamAdd(\n            \"race:usa\",\n            [\n                new(\"racer\", \"Castilla\")\n            ],\n            \"0-1\"\n        );\n        Console.WriteLine(res8);    // >>> 0-1\n\n        RedisValue res9 = db.StreamAdd(\n            \"race:usa\",\n            [\n                new(\"racer\", \"Norem\")\n            ],\n            \"0-2\"\n        );\n        Console.WriteLine(res9);    // >>> 0-2","section_id":"entry-ids"},{"id":"entry-ids-ex3","language":"go","code":"res8, err := rdb.XAdd(ctx, &redis.XAddArgs{\n\t\tStream: \"race:usa\",\n\t\tValues: map[string]interface{}{\n\t\t\t\"racer\": \"Castilla\",\n\t\t},\n\t\tID: \"0-1\",\n\t}).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res8) // >>> 0-1\n\n\tres9, err := rdb.XAdd(ctx, &redis.XAddArgs{\n\t\tStream: \"race:usa\",\n\t\tValues: map[string]interface{}{\n\t\t\t\"racer\": \"Norem\",\n\t\t},\n\t\tID: \"0-2\",\n\t}).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res9) // >>> 0-2","section_id":"entry-ids"},{"id":"entry-ids-ex4","language":"java","code":"StreamEntryID res8 = jedis.xadd(\"race:usa\", new HashMap<String,String>(){{put(\"racer\",\"Castilla\");}},XAddParams.xAddParams().id(\"0-1\"));\n    System.out.println(res8); // >>> 0-1\n\n    StreamEntryID res9 = jedis.xadd(\"race:usa\", new HashMap<String,String>(){{put(\"racer\",\"Norem\");}},XAddParams.xAddParams().id(\"0-2\"));\n    System.out.println(res9); // >>> 0-2","section_id":"entry-ids"},{"id":"entry-ids-ex5","language":"javascript","code":"const res8 = await client.xAdd('race:usa', '0-1', {\n  'racer': 'Castilla'\n});\nconsole.log(res8); // >>> 0-1\n\nconst res9 = await client.xAdd('race:usa', '0-2', {\n  'racer': 'Norem'\n});\nconsole.log(res9); // >>> 0-2","section_id":"entry-ids"},{"id":"entry-ids-ex6","language":"python","code":"res8 = r.xadd(\"race:usa\", {\"racer\": \"Castilla\"}, id=\"0-1\")\nprint(res8)  # >>> 0-1\n\nres9 = r.xadd(\"race:usa\", {\"racer\": \"Norem\"}, id=\"0-2\")\nprint(res9)  # >>> 0-2","section_id":"entry-ids"},{"id":"entry-ids-ex7","language":"ruby","code":"r.del('race:usa')\nres8 = r.xadd('race:usa', {'racer' => 'Castilla'}, id: '0-1')\nputs res8 # >>> 0-1\n\nres9 = r.xadd('race:usa', {'racer' => 'Norem'}, id: '0-2')\nputs res9 # >>> 0-2","section_id":"entry-ids"},{"id":"entry-ids-ex8","language":"rust","code":"delete_keys(&mut r, &[\"race:usa\"]).await;\n        if let Ok(res) = r.xadd(\"race:usa\", \"0-1\", &[(\"racer\", \"Castilla\")]).await {\n            let res: Option<String> = res;\n            let res = res.expect(\"missing stream id\");\n            println!(\"{res}\"); // >>> 0-1\n        }\n        if let Ok(res) = r.xadd(\"race:usa\", \"0-2\", &[(\"racer\", \"Norem\")]).await {\n            let res: Option<String> = res;\n            let res = res.expect(\"missing stream id\");\n            println!(\"{res}\"); // >>> 0-2\n        }","section_id":"entry-ids"},{"id":"entry-ids-ex9","language":"rust","code":"if let Ok(res) = r.xadd(\"race:usa\", \"0-1\", &[(\"racer\", \"Castilla\")]) {\n            let res: Option<String> = res;\n            let res = res.expect(\"missing stream id\");\n            println!(\"{res}\"); // >>> 0-1\n        }\n\n        if let Ok(res) = r.xadd(\"race:usa\", \"0-2\", &[(\"racer\", \"Norem\")]) {\n            let res: Option<String> = res;\n            let res = res.expect(\"missing stream id\");\n            println!(\"{res}\"); // >>> 0-2\n        }","section_id":"entry-ids"},{"id":"entry-ids-ex10","language":"plaintext","code":"> XADD race:usa 0-1 racer Prickett\n(error) ERR The ID specified in XADD is equal or smaller than the target stream top item","section_id":"entry-ids"},{"id":"entry-ids-ex11","language":"csharp","code":"try\n        {\n            RedisValue res10 = db.StreamAdd(\n                \"race:usa\",\n                [\n                    new(\"racer\", \"Prickett\")\n                ],\n                \"0-1\"\n            );\n        }\n        catch (RedisServerException ex)\n        {\n            Console.WriteLine(ex);  // >>> ERR The ID specified in XADD is equal or smaller than the target stream top item\n        }","section_id":"entry-ids"},{"id":"entry-ids-ex12","language":"go","code":"res10, err := rdb.XAdd(ctx, &redis.XAddArgs{\n\t\tValues: map[string]interface{}{\n\t\t\t\"racer\": \"Prickett\",\n\t\t},\n\t\tID: \"0-1\",\n\t}).Result()\n\n\tif err != nil {\n\t\t// fmt.Println(err)\n\t\t// >>> ERR The ID specified in XADD is equal or smaller than the target stream top item\n\t}","section_id":"entry-ids"},{"id":"entry-ids-ex13","language":"java","code":"try {\n      StreamEntryID res10 = jedis.xadd(\"race:usa\", new HashMap<String,String>(){{put(\"racer\",\"Prickett\");}},XAddParams.xAddParams().id(\"0-1\"));\n      System.out.println(res10); // >>> 0-1\n    }\n    catch (JedisDataException e){\n      System.out.println(e); // >>> ERR The ID specified in XADD is equal or smaller than the target stream top item\n    }","section_id":"entry-ids"},{"id":"entry-ids-ex14","language":"javascript","code":"try {\n  const res10 = await client.xAdd('race:usa', '0-1', {\n    'racer': 'Prickett'\n  });\n  console.log(res10); // >>> 0-1\n} catch (error) {\n  console.error(error); // >>> [SimpleError: ERR The ID specified in XADD is equal or smaller than the target stream top item]\n}","section_id":"entry-ids"},{"id":"entry-ids-ex15","language":"python","code":"try:\n    res10 = r.xadd(\"race:usa\", {\"racer\": \"Prickett\"}, id=\"0-1\")\n    print(res10)  # >>> 0-1\nexcept redis.exceptions.ResponseError as e:\n    print(e)  # >>> WRONGID","section_id":"entry-ids"},{"id":"entry-ids-ex16","language":"ruby","code":"begin\n  r.xadd('race:usa', {'racer' => 'Prickett'}, id: '0-1')\nrescue Redis::CommandError => e\n  puts e.message\n  # >>> ERR The ID specified in XADD is equal or smaller than the target stream top item\nend","section_id":"entry-ids"},{"id":"entry-ids-ex17","language":"rust","code":"let res: redis::RedisResult<Option<String>> =\n            r.xadd(\"race:usa\", \"0-1\", &[(\"racer\", \"Prickett\")]).await;\n        match res {\n            Ok(_) => {}\n            Err(e) => {\n                let msg = e.to_string();\n                println!(\"{msg}\");\n                // >>> An error was signalled by the server - ResponseError: The ID specified in XADD is equal or smaller than the target stream top item\n            }\n        }","section_id":"entry-ids"},{"id":"entry-ids-ex18","language":"rust","code":"let res: redis::RedisResult<Option<String>> =\n            r.xadd(\"race:usa\", \"0-1\", &[(\"racer\", \"Prickett\")]);\n        match res {\n            Ok(_) => {}\n            Err(e) => {\n                let msg = e.to_string();\n                println!(\"{msg}\");\n                // >>> An error was signalled by the server - ResponseError: The ID specified in XADD is equal or smaller than the target stream top item\n            }\n        }","section_id":"entry-ids"},{"id":"entry-ids-ex19","language":"plaintext","code":"> XADD race:usa 0-* racer Prickett\n0-3","section_id":"entry-ids"},{"id":"entry-ids-ex20","language":"csharp","code":"RedisValue res11 = \"\";\n        Version version = muxer.GetServer(\"localhost:6379\").Version;\n        if (version.Major >= 7)\n        {\n            res11 = db.StreamAdd(\n                \"race:usa\",\n                [\n                    new(\"rider\", \"Norem\")\n                ],\n                \"0-*\"\n            );\n\n            Console.WriteLine(res11);   // >>> \"0-3\"\n        }","section_id":"entry-ids"},{"id":"entry-ids-ex21","language":"go","code":"res11, err := rdb.XAdd(ctx, &redis.XAddArgs{\n\t\tStream: \"race:usa\",\n\t\tValues: map[string]interface{}{\n\t\t\t\"racer\": \"Prickett\",\n\t\t},\n\t\tID: \"0-*\",\n\t}).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res11) // >>> 0-3","section_id":"entry-ids"},{"id":"entry-ids-ex22","language":"java","code":"StreamEntryID res11 = jedis.xadd(\"race:usa\", new HashMap<String,String>(){{put(\"racer\",\"Norem\");}},XAddParams.xAddParams().id(\"0-*\"));\n    System.out.println(res11);","section_id":"entry-ids"},{"id":"entry-ids-ex23","language":"javascript","code":"const res11a = await client.xAdd('race:usa', '0-*', { racer: 'Norem' });\nconsole.log(res11a); // >>> 0-3","section_id":"entry-ids"},{"id":"entry-ids-ex24","language":"python","code":"# Not yet implemented","section_id":"entry-ids"},{"id":"entry-ids-ex25","language":"ruby","code":"r.del('race:usa')\nr.xadd('race:usa', {'racer' => 'Castilla'}, id: '0-1')\nr.xadd('race:usa', {'racer' => 'Norem'}, id: '0-2')\nres10 = r.xadd('race:usa', {'racer' => 'Prickett'}, id: '0-*')\nputs res10 # >>> 0-3","section_id":"entry-ids"},{"id":"entry-ids-ex26","language":"rust","code":"seed_usa_fixed(&mut r).await;\n        if let Ok(res) = r.xadd(\"race:usa\", \"0-*\", &[(\"racer\", \"Prickett\")]).await {\n            let res: Option<String> = res;\n            let res = res.expect(\"missing stream id\");\n            println!(\"{res}\"); // >>> 0-3\n        }","section_id":"entry-ids"},{"id":"entry-ids-ex27","language":"rust","code":"if let Ok(res) = r.xadd(\"race:usa\", \"0-*\", &[(\"racer\", \"Prickett\")]) {\n            let res: Option<String> = res;\n            let res = res.expect(\"missing stream id\");\n            println!(\"{res}\"); // >>> 0-3\n        }","section_id":"entry-ids"},{"id":"querying-by-range-xrange-and-xrevrange-ex0","language":"plaintext","code":"> XRANGE race:france - +\n1) 1) \"1692632086370-0\"\n   2) 1) \"rider\"\n      2) \"Castilla\"\n      3) \"speed\"\n      4) \"30.2\"\n      5) \"position\"\n      6) \"1\"\n      7) \"location_id\"\n      8) \"1\"\n... output truncated for AI-facing Markdown ...\n   2) 1) \"rider\"\n      2) \"Castilla\"\n      3) \"speed\"\n      4) \"29.9\"\n      5) \"position\"\n      6) \"1\"\n      7) \"location_id\"\n      8) \"2\"","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex1","language":"csharp","code":"StreamEntry[] res12 = db.StreamRange(\"race:france\", \"-\", \"+\");\n\n        foreach (StreamEntry entry in res12)\n        {\n            Console.WriteLine($\"{entry.Id}: [{string.Join(\", \", entry.Values.Select(b => $\"{b.Name}: {b.Value}\"))}]\");\n        }\n        // >>> 1712668482289-0: [rider: Castilla, speed: 30.199999999999999, position: 1, location_id: 1]\n        // >>> 1712668766534-1: [rider: Norem, speed: 28.800000000000001, position: 3, location_id: 1]\n        // >>> 1712669055705-0: [rider: Prickett, speed: 29.699999999999999, position: 2, location_id: 1]\n        // >>> 1712675674750-0: [rider: Castilla, speed: 29.899999999999999, position: 1, location_id: 2]","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex2","language":"go","code":"res12, err := rdb.XRange(ctx, \"race:france\", \"-\", \"+\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res12)\n\t// >>> [{1692632086370-0 map[location_id:1 position:1 rider:Castilla...","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex3","language":"java","code":"List<StreamEntry> res12 = jedis.xrange(\"race:france\",\"-\",\"+\");\n    System.out.println(\n      res12\n    ); // >>> [1701764734160-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701764734160-1 {rider=Norem, speed=28.8, location_id=1, position=3}, 1701764734161-0 {rider=Prickett, speed=29.7, location_id=1, position=2}, 1701764734162-0 {rider=Castilla, speed=29.9, location_id=1, position=2}]","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex4","language":"javascript","code":"const res11 = await client.xRange('race:france', '-', '+');\nconsole.log(res11); // >>> [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }, { id: '1692629594113-0', message: { rider: 'Norem', speed: '28.8', position: '3', location_id: '1' } }, { id: '1692629613374-0', message: { rider: 'Prickett', speed: '29.7', position: '2', location_id: '1' } }, { id: '1692629676124-0', message: { rider: 'Castilla', speed: '29.9', position: '1', location_id: '2' } }]","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex5","language":"python","code":"res11 = r.xrange(\"race:france\", \"-\", \"+\")\nprint(\n    res11\n)\n# >>> [\n#   ('1692629576966-0',\n#       {'rider': 'Castilla', 'speed': '30.2', 'position': '1', 'location_id': '1'}\n#   ),\n#   ('1692629594113-0',\n#       {'rider': 'Norem', 'speed': '28.8', 'position': '3', 'location_id': '1'}\n#   ),\n#   ('1692629613374-0',\n#       {'rider': 'Prickett', 'speed': '29.7', 'position': '2', 'location_id': '1'}\n#   ),\n#   ('1692629676124-0',\n#       {'rider': 'Castilla', 'speed': '29.9', 'position': '1', 'location_id': '2'}\n#   )\n# ]","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex6","language":"ruby","code":"r.del('race:france')\nr.xadd('race:france', {'rider' => 'Castilla', 'speed' => '30.2', 'position' => '1', 'location_id' => '1'}, id: '1692632086370-0')\nr.xadd('race:france', {'rider' => 'Norem', 'speed' => '28.8', 'position' => '3', 'location_id' => '1'}, id: '1692632094485-0')\nr.xadd('race:france', {'rider' => 'Prickett', 'speed' => '29.7', 'position' => '2', 'location_id' => '1'}, id: '1692632102976-0')\nr.xadd('race:france', {'rider' => 'Castilla', 'speed' => '29.9', 'position' => '1', 'location_id' => '2'}, id: '1692632147973-0')\nres11 = r.xrange('race:france', '-', '+')\nputs res11.inspect\n# >>> [[\"1692632086370-0\", {\"rider\" => \"Castilla\", \"speed\" => \"30.2\", \"position\" => \"1\", \"location_id\" => \"1\"}],\n#  [\"1692632094485-0\", {\"rider\" => \"Norem\", \"speed\" => \"28.8\", \"position\" => \"3\", \"location_id\" => \"1\"}],\n#  [\"1692632102976-0\", {\"rider\" => \"Prickett\", \"speed\" => \"29.7\", \"position\" => \"2\", \"location_id\" => \"1\"}],\n#  [\"1692632147973-0\", {\"rider\" => \"Castilla\", \"speed\" => \"29.9\", \"position\" => \"1\", \"location_id\" => \"2\"}]]","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex7","language":"rust","code":"add_france_fixed(&mut r).await;\n        if let Ok(res) = r.xrange_all(\"race:france\").await {\n            let res: StreamRangeReply = res;\n            let view: Vec<_> = res\n                .ids\n                .iter()\n                .map(|entry| {\n                    (\n                        entry.id.clone(),\n                        vec![\n                            (\"rider\".to_string(), entry.get::<String>(\"rider\").expect(\"missing rider\")),\n                            (\"speed\".to_string(), entry.get::<String>(\"speed\").expect(\"missing speed\")),\n                            (\"position\".to_string(), entry.get::<String>(\"position\").expect(\"missing position\")),\n                            (\n                                \"location_id\".to_string(),\n                                entry.get::<String>(\"location_id\").expect(\"missing location_id\"),\n                            ),\n                        ],\n                    )\n                })\n                .collect();\n            println!(\"{view:?}\");\n            // >>> [(\"1692632086370-0\", [(\"rider\", \"Castilla\"), (\"speed\", \"30.2\"), (\"position\", \"1\"), (\"location_id\", \"1\")]), (\"1692632094485-0\", [(\"rider\", \"Norem\"), (\"speed\", \"28.8\"), (\"position\", \"3\"), (\"location_id\", \"1\")]), (\"1692632102976-0\", [(\"rider\", \"Prickett\"), (\"speed\", \"29.7\"), (\"position\", \"2\"), (\"location_id\", \"1\")]), (\"1692632147973-0\", [(\"rider\", \"Castilla\"), (\"speed\", \"29.9\"), (\"position\", \"1\"), (\"location_id\", \"2\")])]\n        }","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex8","language":"rust","code":"if let Ok(res) = r.xrange_all(\"race:france\") {\n            let res: StreamRangeReply = res;\n            let view: Vec<_> = res\n                .ids\n                .iter()\n                .map(|entry| {\n                    (\n                        entry.id.clone(),\n                        vec![\n                            (\"rider\".to_string(), entry.get::<String>(\"rider\").expect(\"missing rider\")),\n                            (\"speed\".to_string(), entry.get::<String>(\"speed\").expect(\"missing speed\")),\n                            (\"position\".to_string(), entry.get::<String>(\"position\").expect(\"missing position\")),\n                            (\n                                \"location_id\".to_string(),\n                                entry.get::<String>(\"location_id\").expect(\"missing location_id\"),\n                            ),\n                        ],\n                    )\n                })\n                .collect();\n            println!(\"{view:?}\");\n            // >>> [(\"1692632086370-0\", [(\"rider\", \"Castilla\"), (\"speed\", \"30.2\"), (\"position\", \"1\"), (\"location_id\", \"1\")]), (\"1692632094485-0\", [(\"rider\", \"Norem\"), (\"speed\", \"28.8\"), (\"position\", \"3\"), (\"location_id\", \"1\")]), (\"1692632102976-0\", [(\"rider\", \"Prickett\"), (\"speed\", \"29.7\"), (\"position\", \"2\"), (\"location_id\", \"1\")]), (\"1692632147973-0\", [(\"rider\", \"Castilla\"), (\"speed\", \"29.9\"), (\"position\", \"1\"), (\"location_id\", \"2\")])]\n        }","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex9","language":"plaintext","code":"> XRANGE race:france 1692632086369 1692632086371\n1) 1) \"1692632086370-0\"\n   2) 1) \"rider\"\n      2) \"Castilla\"\n      3) \"speed\"\n      4) \"30.2\"\n      5) \"position\"\n      6) \"1\"\n      7) \"location_id\"\n      8) \"1\"","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex10","language":"csharp","code":"StreamEntry[] res13 = db.StreamRange(\"race:france\", 1712668482289, 1712668482291);\n\n        foreach (StreamEntry entry in res13)\n        {\n            Console.WriteLine($\"{entry.Id}: [{string.Join(\", \", entry.Values.Select(b => $\"{b.Name}: {b.Value}\"))}]\");\n        }\n        // >>> 1712668482289-0: [rider: Castilla, speed: 30.199999999999999, position: 1, location_id: 1]","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex11","language":"go","code":"res13, err := rdb.XRange(ctx, \"race:france\",\n\t\t\"1692632086369\", \"1692632086371\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res13)\n\t// >>> [{1692632086370-0 map[location_id:1 position:1 rider:Castilla speed:30.2] 0 0}]","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex12","language":"java","code":"List<StreamEntry> res13 = jedis.xrange(\"race:france\",String.valueOf(System.currentTimeMillis()-1000),String.valueOf(System.currentTimeMillis()+1000));\n    System.out.println(\n      res13\n    ); // >>> [1701764734160-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701764734160-1 {rider=Norem, speed=28.8, location_id=1, position=3}, 1701764734161-0 {rider=Prickett, speed=29.7, location_id=1, position=2}, 1701764734162-0 {rider=Castilla, speed=29.9, location_id=1, position=2}]","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex13","language":"javascript","code":"const res12 = await client.xRange('race:france', '1692629576965', '1692629576967');\nconsole.log(res12); // >>> [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }]","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex14","language":"python","code":"res12 = r.xrange(\"race:france\", 1692629576965, 1692629576967)\nprint(\n    res12\n)\n# >>> [\n#       ('1692629576966-0',\n#           {'rider': 'Castilla', 'speed': '30.2', 'position': '1', 'location_id': '1'}\n#       )\n# ]","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex15","language":"ruby","code":"r.del('race:france')\nr.xadd('race:france', {'rider' => 'Castilla', 'speed' => '30.2', 'position' => '1', 'location_id' => '1'}, id: '1692632086370-0')\nr.xadd('race:france', {'rider' => 'Norem', 'speed' => '28.8', 'position' => '3', 'location_id' => '1'}, id: '1692632094485-0')\nres12 = r.xrange('race:france', '1692632086369', '1692632086371')\nputs res12.inspect\n# >>> [[\"1692632086370-0\", {\"rider\" => \"Castilla\", \"speed\" => \"30.2\", \"position\" => \"1\", \"location_id\" => \"1\"}]]","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex16","language":"rust","code":"add_france_fixed(&mut r).await;\n        if let Ok(res) = r.xrange(\"race:france\", \"1692632086369\", \"1692632086371\").await {\n            let res: StreamRangeReply = res;\n            let view: Vec<_> = res\n                .ids\n                .iter()\n                .map(|entry| {\n                    (\n                        entry.id.clone(),\n                        vec![\n                            (\"rider\".to_string(), entry.get::<String>(\"rider\").expect(\"missing rider\")),\n                            (\"speed\".to_string(), entry.get::<String>(\"speed\").expect(\"missing speed\")),\n                            (\"position\".to_string(), entry.get::<String>(\"position\").expect(\"missing position\")),\n                            (\n                                \"location_id\".to_string(),\n                                entry.get::<String>(\"location_id\").expect(\"missing location_id\"),\n                            ),\n                        ],\n                    )\n                })\n                .collect();\n            println!(\"{view:?}\");\n            // >>> [(\"1692632086370-0\", [(\"rider\", \"Castilla\"), (\"speed\", \"30.2\"), (\"position\", \"1\"), (\"location_id\", \"1\")])]\n        }","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex17","language":"rust","code":"if let Ok(res) = r.xrange(\"race:france\", \"1692632086369\", \"1692632086371\") {\n            let res: StreamRangeReply = res;\n            let view: Vec<_> = res\n                .ids\n                .iter()\n                .map(|entry| {\n                    (\n                        entry.id.clone(),\n                        vec![\n                            (\"rider\".to_string(), entry.get::<String>(\"rider\").expect(\"missing rider\")),\n                            (\"speed\".to_string(), entry.get::<String>(\"speed\").expect(\"missing speed\")),\n                            (\"position\".to_string(), entry.get::<String>(\"position\").expect(\"missing position\")),\n                            (\n                                \"location_id\".to_string(),\n                                entry.get::<String>(\"location_id\").expect(\"missing location_id\"),\n                            ),\n                        ],\n                    )\n                })\n                .collect();\n            println!(\"{view:?}\");\n            // >>> [(\"1692632086370-0\", [(\"rider\", \"Castilla\"), (\"speed\", \"30.2\"), (\"position\", \"1\"), (\"location_id\", \"1\")])]\n        }","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex18","language":"plaintext","code":"> XRANGE race:france - + COUNT 2\n1) 1) \"1692632086370-0\"\n   2) 1) \"rider\"\n      2) \"Castilla\"\n      3) \"speed\"\n      4) \"30.2\"\n      5) \"position\"\n      6) \"1\"\n      7) \"location_id\"\n      8) \"1\"\n2) 1) \"1692632094485-0\"\n   2) 1) \"rider\"\n      2) \"Norem\"\n      3) \"speed\"\n      4) \"28.8\"\n      5) \"position\"\n      6) \"3\"\n      7) \"location_id\"\n      8) \"1\"","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex19","language":"csharp","code":"StreamEntry[] res14 = db.StreamRange(\"race:france\", \"-\", \"+\", 2);\n\n        foreach (StreamEntry entry in res14)\n        {\n            Console.WriteLine($\"{entry.Id}: [{string.Join(\", \", entry.Values.Select(b => $\"{b.Name}: {b.Value}\"))}]\");\n        }\n        // >>> 1712668482289-0: [rider: Castilla, speed: 30.199999999999999, position: 1, location_id: 1]\n        // >>> 1712668766534-1: [rider: Norem, speed: 28.800000000000001, position: 3, location_id: 1]","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex20","language":"go","code":"res14, err := rdb.XRangeN(ctx, \"race:france\", \"-\", \"+\", 2).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res14)\n\t// >>> [{1692632086370-0 map[location_id:1 position:1 rider:Castilla speed:30.2] 0 0} {1692632094485-0 map[location_id:1 position:3 rider:Norem speed:28.8] 0 0}]","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex21","language":"java","code":"List<StreamEntry> res14 = jedis.xrange(\"race:france\",\"-\",\"+\",2);\n    System.out.println(res14); // >>> [1701764887638-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701764887638-1 {rider=Norem, speed=28.8, location_id=1, position=3}]","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex22","language":"javascript","code":"const res13 = await client.xRange('race:france', '-', '+', {COUNT: 2});\nconsole.log(res13); // >>> [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }, { id: '1692629594113-0', message: { rider: 'Norem', speed: '28.8', position: '3', location_id: '1' } }]","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex23","language":"python","code":"res13 = r.xrange(\"race:france\", \"-\", \"+\", 2)\nprint(\n    res13\n)\n# >>> [\n#   ('1692629576966-0',\n#       {'rider': 'Castilla', 'speed': '30.2', 'position': '1', 'location_id': '1'}\n#   ),\n#   ('1692629594113-0',\n#       {'rider': 'Norem', 'speed': '28.8', 'position': '3', 'location_id': '1'}\n#   )\n# ]","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex24","language":"ruby","code":"r.del('race:france')\nr.xadd('race:france', {'rider' => 'Castilla', 'speed' => '30.2', 'position' => '1', 'location_id' => '1'}, id: '1692632086370-0')\nr.xadd('race:france', {'rider' => 'Norem', 'speed' => '28.8', 'position' => '3', 'location_id' => '1'}, id: '1692632094485-0')\nr.xadd('race:france', {'rider' => 'Prickett', 'speed' => '29.7', 'position' => '2', 'location_id' => '1'}, id: '1692632102976-0')\nr.xadd('race:france', {'rider' => 'Castilla', 'speed' => '29.9', 'position' => '1', 'location_id' => '2'}, id: '1692632147973-0')\nres13 = r.xrange('race:france', '-', '+', count: 2)\nputs res13.inspect\n# >>> [[\"1692632086370-0\", {\"rider\" => \"Castilla\", \"speed\" => \"30.2\", \"position\" => \"1\", \"location_id\" => \"1\"}],\n#  [\"1692632094485-0\", {\"rider\" => \"Norem\", \"speed\" => \"28.8\", \"position\" => \"3\", \"location_id\" => \"1\"}]]","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex25","language":"rust","code":"add_france_fixed(&mut r).await;\n        if let Ok(res) = r.xrange_count(\"race:france\", \"-\", \"+\", 2).await {\n            let res: StreamRangeReply = res;\n            let view: Vec<_> = res\n                .ids\n                .iter()\n                .map(|entry| {\n                    (\n                        entry.id.clone(),\n                        vec![\n                            (\"rider\".to_string(), entry.get::<String>(\"rider\").expect(\"missing rider\")),\n                            (\"speed\".to_string(), entry.get::<String>(\"speed\").expect(\"missing speed\")),\n                            (\"position\".to_string(), entry.get::<String>(\"position\").expect(\"missing position\")),\n                            (\n                                \"location_id\".to_string(),\n                                entry.get::<String>(\"location_id\").expect(\"missing location_id\"),\n                            ),\n                        ],\n                    )\n                })\n                .collect();\n            println!(\"{view:?}\");\n            // >>> [(\"1692632086370-0\", [(\"rider\", \"Castilla\"), (\"speed\", \"30.2\"), (\"position\", \"1\"), (\"location_id\", \"1\")]), (\"1692632094485-0\", [(\"rider\", \"Norem\"), (\"speed\", \"28.8\"), (\"position\", \"3\"), (\"location_id\", \"1\")])]\n        }","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex26","language":"rust","code":"if let Ok(res) = r.xrange_count(\"race:france\", \"-\", \"+\", 2) {\n            let res: StreamRangeReply = res;\n            let view: Vec<_> = res\n                .ids\n                .iter()\n                .map(|entry| {\n                    (\n                        entry.id.clone(),\n                        vec![\n                            (\"rider\".to_string(), entry.get::<String>(\"rider\").expect(\"missing rider\")),\n                            (\"speed\".to_string(), entry.get::<String>(\"speed\").expect(\"missing speed\")),\n                            (\"position\".to_string(), entry.get::<String>(\"position\").expect(\"missing position\")),\n                            (\n                                \"location_id\".to_string(),\n                                entry.get::<String>(\"location_id\").expect(\"missing location_id\"),\n                            ),\n                        ],\n                    )\n                })\n                .collect();\n            println!(\"{view:?}\");\n            // >>> [(\"1692632086370-0\", [(\"rider\", \"Castilla\"), (\"speed\", \"30.2\"), (\"position\", \"1\"), (\"location_id\", \"1\")]), (\"1692632094485-0\", [(\"rider\", \"Norem\"), (\"speed\", \"28.8\"), (\"position\", \"3\"), (\"location_id\", \"1\")])]\n        }","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex27","language":"plaintext","code":"> XRANGE race:france (1692632094485-0 + COUNT 2\n1) 1) \"1692632102976-0\"\n   2) 1) \"rider\"\n      2) \"Prickett\"\n      3) \"speed\"\n      4) \"29.7\"\n      5) \"position\"\n      6) \"2\"\n      7) \"location_id\"\n      8) \"1\"\n2) 1) \"1692632147973-0\"\n   2) 1) \"rider\"\n      2) \"Castilla\"\n      3) \"speed\"\n      4) \"29.9\"\n      5) \"position\"\n      6) \"1\"\n      7) \"location_id\"\n      8) \"2\"","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex28","language":"csharp","code":"StreamEntry[] res15 = db.StreamRange(\"race:france\", \"(1712668766534-1\", \"+\", 2);\n\n        foreach (StreamEntry entry in res15)\n        {\n            Console.WriteLine($\"{entry.Id}: [{string.Join(\", \", entry.Values.Select(b => $\"{b.Name}: {b.Value}\"))}]\");\n        }\n        // >>> 1712669055705-0: [rider: Prickett, speed: 29.699999999999999, position: 2, location_id: 1]\n        // >>> 1712675674750-0: [rider: Castilla, speed: 29.899999999999999, position: 1, location_id: 2]","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex29","language":"go","code":"res15, err := rdb.XRangeN(ctx, \"race:france\",\n\t\t\"(1692632094485-0\", \"+\", 2,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res15)\n\t// >>> [{1692632102976-0 map[location_id:1 position:2 rider:Prickett speed:29.7] 0 0} {1692632147973-0 map[location_id:2 position:1 rider:Castilla speed:29.9] 0 0}]","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex30","language":"java","code":"List<StreamEntry> res15 = jedis.xrange(\"race:france\",String.valueOf(System.currentTimeMillis()-1000)+\"-0\",\"+\",2);\n    System.out.println(res15); // >>> [1701764887638-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701764887638-1 {rider=Norem, speed=28.8, location_id=1, position=3}]","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex31","language":"javascript","code":"const res14 = await client.xRange('race:france', '(1692629594113-0', '+', {COUNT: 2});\nconsole.log(res14); // >>> [{ id: '1692629613374-0', message: { rider: 'Prickett', speed: '29.7', position: '2', location_id: '1' } }, { id: '1692629676124-0', message: { rider: 'Castilla', speed: '29.9', position: '1', location_id: '2' } }]","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex32","language":"python","code":"res14 = r.xrange(\"race:france\", \"(1692629594113-0\", \"+\", 2)\nprint(\n    res14\n)\n# >>> [\n#   ('1692629613374-0',\n#       {'rider': 'Prickett', 'speed': '29.7', 'position': '2', 'location_id': '1'}\n#   ),\n#   ('1692629676124-0',\n#       {'rider': 'Castilla', 'speed': '29.9', 'position': '1', 'location_id': '2'}\n#   )\n# ]","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex33","language":"ruby","code":"r.del('race:france')\nr.xadd('race:france', {'rider' => 'Castilla', 'speed' => '30.2', 'position' => '1', 'location_id' => '1'}, id: '1692632086370-0')\nr.xadd('race:france', {'rider' => 'Norem', 'speed' => '28.8', 'position' => '3', 'location_id' => '1'}, id: '1692632094485-0')\nr.xadd('race:france', {'rider' => 'Prickett', 'speed' => '29.7', 'position' => '2', 'location_id' => '1'}, id: '1692632102976-0')\nr.xadd('race:france', {'rider' => 'Castilla', 'speed' => '29.9', 'position' => '1', 'location_id' => '2'}, id: '1692632147973-0')\nres14 = r.xrange('race:france', '(1692632094485-0', '+', count: 2)\nputs res14.inspect\n# >>> [[\"1692632102976-0\", {\"rider\" => \"Prickett\", \"speed\" => \"29.7\", \"position\" => \"2\", \"location_id\" => \"1\"}],\n#  [\"1692632147973-0\", {\"rider\" => \"Castilla\", \"speed\" => \"29.9\", \"position\" => \"1\", \"location_id\" => \"2\"}]]","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex34","language":"rust","code":"add_france_fixed(&mut r).await;\n        if let Ok(res) = r.xrange_count(\"race:france\", \"(1692632094485-0\", \"+\", 2).await {\n            let res: StreamRangeReply = res;\n            let view: Vec<_> = res\n                .ids\n                .iter()\n                .map(|entry| {\n                    (\n                        entry.id.clone(),\n                        vec![\n                            (\"rider\".to_string(), entry.get::<String>(\"rider\").expect(\"missing rider\")),\n                            (\"speed\".to_string(), entry.get::<String>(\"speed\").expect(\"missing speed\")),\n                            (\"position\".to_string(), entry.get::<String>(\"position\").expect(\"missing position\")),\n                            (\n                                \"location_id\".to_string(),\n                                entry.get::<String>(\"location_id\").expect(\"missing location_id\"),\n                            ),\n                        ],\n                    )\n                })\n                .collect();\n            println!(\"{view:?}\");\n            // >>> [(\"1692632102976-0\", [(\"rider\", \"Prickett\"), (\"speed\", \"29.7\"), (\"position\", \"2\"), (\"location_id\", \"1\")]), (\"1692632147973-0\", [(\"rider\", \"Castilla\"), (\"speed\", \"29.9\"), (\"position\", \"1\"), (\"location_id\", \"2\")])]\n        }","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex35","language":"rust","code":"if let Ok(res) = r.xrange_count(\"race:france\", \"(1692632094485-0\", \"+\", 2) {\n            let res: StreamRangeReply = res;\n            let view: Vec<_> = res\n                .ids\n                .iter()\n                .map(|entry| {\n                    (\n                        entry.id.clone(),\n                        vec![\n                            (\"rider\".to_string(), entry.get::<String>(\"rider\").expect(\"missing rider\")),\n                            (\"speed\".to_string(), entry.get::<String>(\"speed\").expect(\"missing speed\")),\n                            (\"position\".to_string(), entry.get::<String>(\"position\").expect(\"missing position\")),\n                            (\n                                \"location_id\".to_string(),\n                                entry.get::<String>(\"location_id\").expect(\"missing location_id\"),\n                            ),\n                        ],\n                    )\n                })\n                .collect();\n            println!(\"{view:?}\");\n            // >>> [(\"1692632102976-0\", [(\"rider\", \"Prickett\"), (\"speed\", \"29.7\"), (\"position\", \"2\"), (\"location_id\", \"1\")]), (\"1692632147973-0\", [(\"rider\", \"Castilla\"), (\"speed\", \"29.9\"), (\"position\", \"1\"), (\"location_id\", \"2\")])]\n        }","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex36","language":"plaintext","code":"> XRANGE race:france (1692632147973-0 + COUNT 2\n(empty array)","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex37","language":"csharp","code":"StreamEntry[] res16 = db.StreamRange(\"race:france\", \"(1712675674750-0\", \"+\", 2);\n\n        foreach (StreamEntry entry in res16)\n        {\n            Console.WriteLine($\"{entry.Id}: [{string.Join(\", \", entry.Values.Select(b => $\"{b.Name}: {b.Value}\"))}]\");\n        }\n        // >>> <empty array>","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex38","language":"go","code":"res16, err := rdb.XRangeN(ctx, \"race:france\",\n\t\t\"(1692632147973-0\", \"+\", 2,\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res16)\n\t// >>> []","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex39","language":"java","code":"List<StreamEntry> res16 = jedis.xrange(\"race:france\",String.valueOf(System.currentTimeMillis()+1000)+\"-0\",\"+\",2);\n    System.out.println(res16); // >>> []","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex40","language":"javascript","code":"const res15 = await client.xRange('race:france', '(1692629676124-0', '+', {COUNT: 2});\nconsole.log(res15); // >>> []","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex41","language":"python","code":"res15 = r.xrange(\"race:france\", \"(1692629676124-0\", \"+\", 2)\nprint(res15)  # >>> []","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex42","language":"ruby","code":"res15 = r.xrange('race:france', '(1692632147973-0', '+', count: 2)\nputs res15.inspect # >>> []","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex43","language":"rust","code":"add_france_fixed(&mut r).await;\n        if let Ok(res) = r.xrange_count(\"race:france\", \"(1692632147973-0\", \"+\", 2).await {\n            let res: StreamRangeReply = res;\n            let view: Vec<_> = res\n                .ids\n                .iter()\n                .map(|entry| {\n                    (\n                        entry.id.clone(),\n                        vec![\n                            (\"rider\".to_string(), entry.get::<String>(\"rider\").expect(\"missing rider\")),\n                            (\"speed\".to_string(), entry.get::<String>(\"speed\").expect(\"missing speed\")),\n                            (\"position\".to_string(), entry.get::<String>(\"position\").expect(\"missing position\")),\n                            (\n                                \"location_id\".to_string(),\n                                entry.get::<String>(\"location_id\").expect(\"missing location_id\"),\n                            ),\n                        ],\n                    )\n                })\n                .collect();\n            println!(\"{view:?}\"); // >>> []\n        }","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex44","language":"rust","code":"if let Ok(res) = r.xrange_count(\"race:france\", \"(1692632147973-0\", \"+\", 2) {\n            let res: StreamRangeReply = res;\n            let view: Vec<_> = res\n                .ids\n                .iter()\n                .map(|entry| {\n                    (\n                        entry.id.clone(),\n                        vec![\n                            (\"rider\".to_string(), entry.get::<String>(\"rider\").expect(\"missing rider\")),\n                            (\"speed\".to_string(), entry.get::<String>(\"speed\").expect(\"missing speed\")),\n                            (\"position\".to_string(), entry.get::<String>(\"position\").expect(\"missing position\")),\n                            (\n                                \"location_id\".to_string(),\n                                entry.get::<String>(\"location_id\").expect(\"missing location_id\"),\n                            ),\n                        ],\n                    )\n                })\n                .collect();\n            println!(\"{view:?}\"); // >>> []\n        }","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex45","language":"plaintext","code":"> XREVRANGE race:france + - COUNT 1\n1) 1) \"1692632147973-0\"\n   2) 1) \"rider\"\n      2) \"Castilla\"\n      3) \"speed\"\n      4) \"29.9\"\n      5) \"position\"\n      6) \"1\"\n      7) \"location_id\"\n      8) \"2\"","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex46","language":"csharp","code":"StreamEntry[] res17 = db.StreamRange(\"race:france\", \"+\", \"-\", 1, Order.Descending);\n\n        foreach (StreamEntry entry in res17)\n        {\n            Console.WriteLine($\"{entry.Id}: [{string.Join(\", \", entry.Values.Select(b => $\"{b.Name}: {b.Value}\"))}]\");\n        }\n        // >>> 1712675674750-0: [rider: Castilla, speed: 29.899999999999999, position: 1, location_id: 2]","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex47","language":"go","code":"res17, err := rdb.XRevRangeN(ctx, \"race:france\", \"+\", \"-\", 1).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res17)\n\t// >>> [{1692632147973-0 map[location_id:2 position:1 rider:Castilla speed:29.9] 0 0}]","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex48","language":"java","code":"List<StreamEntry> res17 = jedis.xrevrange(\"race:france\",\"+\",\"-\",1);\n    System.out.println(res17); // >>> [1701765218592-0 {rider=Castilla, speed=29.9, location_id=1, position=2}]","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex49","language":"javascript","code":"const res16 = await client.xRevRange('race:france', '+', '-', {COUNT: 1});\nconsole.log(\n  res16\n); // >>> [{ id: '1692629676124-0', message: { rider: 'Castilla', speed: '29.9', position: '1', location_id: '2' } }]","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex50","language":"python","code":"res16 = r.xrevrange(\"race:france\", \"+\", \"-\", 1)\nprint(\n    res16\n)\n# >>> [\n#       ('1692629676124-0',\n#           {'rider': 'Castilla', 'speed': '29.9', 'position': '1', 'location_id': '2'}\n#       )\n# ]","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex51","language":"ruby","code":"res16 = r.xrevrange('race:france', '+', '-', count: 1)\nputs res16.inspect\n# >>> [[\"1692632147973-0\", {\"rider\" => \"Castilla\", \"speed\" => \"29.9\", \"position\" => \"1\", \"location_id\" => \"2\"}]]","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex52","language":"rust","code":"add_france_fixed(&mut r).await;\n        if let Ok(res) = r.xrevrange_count(\"race:france\", \"+\", \"-\", 1).await {\n            let res: StreamRangeReply = res;\n            let view: Vec<_> = res\n                .ids\n                .iter()\n                .map(|entry| {\n                    (\n                        entry.id.clone(),\n                        vec![\n                            (\"rider\".to_string(), entry.get::<String>(\"rider\").expect(\"missing rider\")),\n                            (\"speed\".to_string(), entry.get::<String>(\"speed\").expect(\"missing speed\")),\n                            (\"position\".to_string(), entry.get::<String>(\"position\").expect(\"missing position\")),\n                            (\n                                \"location_id\".to_string(),\n                                entry.get::<String>(\"location_id\").expect(\"missing location_id\"),\n                            ),\n                        ],\n                    )\n                })\n                .collect();\n            println!(\"{view:?}\");\n            // >>> [(\"1692632147973-0\", [(\"rider\", \"Castilla\"), (\"speed\", \"29.9\"), (\"position\", \"1\"), (\"location_id\", \"2\")])]\n        }","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"querying-by-range-xrange-and-xrevrange-ex53","language":"rust","code":"if let Ok(res) = r.xrevrange_count(\"race:france\", \"+\", \"-\", 1) {\n            let res: StreamRangeReply = res;\n            let view: Vec<_> = res\n                .ids\n                .iter()\n                .map(|entry| {\n                    (\n                        entry.id.clone(),\n                        vec![\n                            (\"rider\".to_string(), entry.get::<String>(\"rider\").expect(\"missing rider\")),\n                            (\"speed\".to_string(), entry.get::<String>(\"speed\").expect(\"missing speed\")),\n                            (\"position\".to_string(), entry.get::<String>(\"position\").expect(\"missing position\")),\n                            (\n                                \"location_id\".to_string(),\n                                entry.get::<String>(\"location_id\").expect(\"missing location_id\"),\n                            ),\n                        ],\n                    )\n                })\n                .collect();\n            println!(\"{view:?}\");\n            // >>> [(\"1692632147973-0\", [(\"rider\", \"Castilla\"), (\"speed\", \"29.9\"), (\"position\", \"1\"), (\"location_id\", \"2\")])]\n        }","section_id":"querying-by-range-xrange-and-xrevrange"},{"id":"listening-for-new-items-with-xread-ex0","language":"plaintext","code":"> XREAD COUNT 2 STREAMS race:france 0\n1) 1) \"race:france\"\n   2) 1) 1) \"1692632086370-0\"\n         2) 1) \"rider\"\n            2) \"Castilla\"\n            3) \"speed\"\n            4) \"30.2\"\n            5) \"position\"\n            6) \"1\"\n            7) \"location_id\"\n... output truncated for AI-facing Markdown ...\n         2) 1) \"rider\"\n            2) \"Norem\"\n            3) \"speed\"\n            4) \"28.8\"\n            5) \"position\"\n            6) \"3\"\n            7) \"location_id\"\n            8) \"1\"","section_id":"listening-for-new-items-with-xread"},{"id":"listening-for-new-items-with-xread-ex1","language":"csharp","code":"StreamEntry[] res18 = db.StreamRead(\"race:france\", 0, 2);\n\n        foreach (StreamEntry entry in res18)\n        {\n            Console.WriteLine($\"{entry.Id}: [{string.Join(\", \", entry.Values.Select(b => $\"{b.Name}: {b.Value}\"))}]\");\n        }\n        // >>> 1712668482289-0: [rider: Castilla, speed: 30.199999999999999, position: 1, location_id: 1]\n        // >>> 1712668766534-1: [rider: Norem, speed: 28.800000000000001, position: 3, location_id: 1]","section_id":"listening-for-new-items-with-xread"},{"id":"listening-for-new-items-with-xread-ex2","language":"go","code":"res18, err := rdb.XRead(ctx, &redis.XReadArgs{\n\t\tStreams: []string{\"race:france\", \"0\"},\n\t\tCount:   2,\n\t}).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res18)\n\t// >>> [{race:france [{1692632086370-0 map[location_id:1 position:1 rider:Castilla speed:30.2] 0 0} {1692632094485-0 map[location_id:1 position:3 rider:Norem speed:28.8] 0 0}]}]","section_id":"listening-for-new-items-with-xread"},{"id":"listening-for-new-items-with-xread-ex3","language":"java","code":"List<Map.Entry<String, List<StreamEntry>>> res18= jedis.xread(XReadParams.xReadParams().count(2),new HashMap<String,StreamEntryID>(){{put(\"race:france\",new StreamEntryID());}});\n    System.out.println(\n      res18\n    ); // >>> [race:france=[1701765384638-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701765384638-1 {rider=Norem, speed=28.8, location_id=1, position=3}]]","section_id":"listening-for-new-items-with-xread"},{"id":"listening-for-new-items-with-xread-ex4","language":"javascript","code":"const res17 = await client.xRead({\n  key: 'race:france',\n  id: '0-0'\n}, {\n  COUNT: 2\n});\nconsole.log(res17); // >>> [{ name: 'race:france', messages: [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }, { id: '1692629594113-0', message: { rider: 'Norem', speed: '28.8', position: '3', location_id: '1' } }] }]","section_id":"listening-for-new-items-with-xread"},{"id":"listening-for-new-items-with-xread-ex5","language":"python","code":"res17 = r.xread(streams={\"race:france\": 0}, count=2)\nprint(\n    res17\n)\n# >>> [\n#       ['race:france', [\n#       ('1692629576966-0',\n#           {'rider': 'Castilla', 'speed': '30.2', 'position': '1', 'location_id': '1'}\n#       ),\n#       ('1692629594113-0',\n#           {'rider': 'Norem', 'speed': '28.8', 'position': '3', 'location_id': '1'}\n#       )\n#       ]\n#       ]\n#   ]","section_id":"listening-for-new-items-with-xread"},{"id":"listening-for-new-items-with-xread-ex6","language":"ruby","code":"res17 = r.xread(['race:france'], ['0'], count: 2)\nputs res17.inspect\n# >>> {\"race:france\"=>[[\"1692632086370-0\", {\"rider\" => \"Castilla\", \"speed\" => \"30.2\", \"position\" => \"1\", \"location_id\" => \"1\"}],\n#                  [\"1692632094485-0\", {\"rider\" => \"Norem\", \"speed\" => \"28.8\", \"position\" => \"3\", \"location_id\" => \"1\"}]]}","section_id":"listening-for-new-items-with-xread"},{"id":"listening-for-new-items-with-xread-ex7","language":"rust","code":"add_france_fixed(&mut r).await;\n        let opts = StreamReadOptions::default().count(2);\n        if let Ok(res) = r.xread_options(&[\"race:france\"], &[\"0\"], &opts).await {\n            let res: Option<StreamReadReply> = res;\n            let view: Vec<_> = res\n                .expect(\"xread should return data\")\n                .keys\n                .iter()\n                .map(|stream| {\n                    (\n                        stream.key.clone(),\n                        stream\n                            .ids\n                            .iter()\n                            .map(|entry| {\n                                (\n                                    entry.id.clone(),\n                                    vec![\n                                        (\"rider\".to_string(), entry.get::<String>(\"rider\").expect(\"missing rider\")),\n                                        (\"speed\".to_string(), entry.get::<String>(\"speed\").expect(\"missing speed\")),\n                                        (\"position\".to_string(), entry.get::<String>(\"position\").expect(\"missing position\")),\n                                        (\n                                            \"location_id\".to_string(),\n                                            entry.get::<String>(\"location_id\").expect(\"missing location_id\"),\n                                        ),\n                                    ],\n                                )\n                            })\n                            .collect::<Vec<_>>(),\n                    )\n                })\n                .collect();\n            println!(\"{view:?}\");\n            // >>> [(\"race:france\", [(\"1692632086370-0\", [(\"rider\", \"Castilla\"), (\"speed\", \"30.2\"), (\"position\", \"1\"), (\"location_id\", \"1\")]), (\"1692632094485-0\", [(\"rider\", \"Norem\"), (\"speed\", \"28.8\"), (\"position\", \"3\"), (\"location_id\", \"1\")])])]\n        }","section_id":"listening-for-new-items-with-xread"},{"id":"listening-for-new-items-with-xread-ex8","language":"rust","code":"let opts = StreamReadOptions::default().count(2);\n        if let Ok(res) = r.xread_options(&[\"race:france\"], &[\"0\"], &opts) {\n            let res: Option<StreamReadReply> = res;\n            let reply = res.expect(\"xread should return data\");\n            let view: Vec<_> = reply\n                .keys\n                .iter()\n                .map(|stream| {\n                    (\n                        stream.key.clone(),\n                        stream\n                            .ids\n                            .iter()\n                            .map(|entry| {\n                                (\n                                    entry.id.clone(),\n                                    vec![\n                                        (\"rider\".to_string(), entry.get::<String>(\"rider\").expect(\"missing rider\")),\n                                        (\"speed\".to_string(), entry.get::<String>(\"speed\").expect(\"missing speed\")),\n                                        (\"position\".to_string(), entry.get::<String>(\"position\").expect(\"missing position\")),\n                                        (\n                                            \"location_id\".to_string(),\n                                            entry.get::<String>(\"location_id\").expect(\"missing location_id\"),\n                                        ),\n                                    ],\n                                )\n                            })\n                            .collect::<Vec<_>>(),\n                    )\n                })\n                .collect();\n            println!(\"{view:?}\");\n            // >>> [(\"race:france\", [(\"1692632086370-0\", [(\"rider\", \"Castilla\"), (\"speed\", \"30.2\"), (\"position\", \"1\"), (\"location_id\", \"1\")]), (\"1692632094485-0\", [(\"rider\", \"Norem\"), (\"speed\", \"28.8\"), (\"position\", \"3\"), (\"location_id\", \"1\")])])]\n        }","section_id":"listening-for-new-items-with-xread"},{"id":"listening-for-new-items-with-xread-ex9","language":"plaintext","code":"> XREAD BLOCK 0 STREAMS race:france $","section_id":"listening-for-new-items-with-xread"},{"id":"consumer-groups-ex0","language":"plaintext","code":"1 -> C1\n2 -> C2\n3 -> C3\n4 -> C1\n5 -> C2\n6 -> C3\n7 -> C1","section_id":"consumer-groups"},{"id":"consumer-groups-ex1","language":"plaintext","code":"+----------------------------------------+\n| consumer_group_name: mygroup           |\n| consumer_group_stream: somekey         |\n| last_delivered_id: 1292309234234-92    |\n|                                        |\n| consumers:                             |\n|    \"consumer-1\" with pending messages  |\n|       1292309234234-4                  |\n|       1292309234232-8                  |\n|    \"consumer-42\" with pending messages |\n|       ... (and so forth)               |\n+----------------------------------------+","section_id":"consumer-groups"},{"id":"creating-a-consumer-group-ex0","language":"plaintext","code":"> XGROUP CREATE race:france france_riders $\nOK","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex1","language":"csharp","code":"bool res19 = db.StreamCreateConsumerGroup(\"race:france\", \"france_riders\", \"$\");\n        Console.WriteLine(res19);   // >>> true","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex2","language":"go","code":"res19, err := rdb.XGroupCreate(ctx, \"race:france\", \"france_riders\", \"$\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res19) // >>> OK","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex3","language":"java","code":"String res19 = jedis.xgroupCreate(\"race:france\",\"france_riders\",StreamEntryID.LAST_ENTRY,false);\n    System.out.println(res19); // >>> OK","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex4","language":"javascript","code":"const res18 = await client.xGroupCreate('race:france', 'france_riders', '$');\nconsole.log(res18); // >>> OK","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex5","language":"python","code":"res18 = r.xgroup_create(\"race:france\", \"france_riders\", \"$\")\nprint(res18)  # >>> True","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex6","language":"ruby","code":"r.del('race:france')\nr.xadd('race:france', {'rider' => 'Castilla'}, id: '1692632086370-0')\nres18 = r.xgroup(:create, 'race:france', 'france_riders', '$')\nputs res18 # >>> OK","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex7","language":"rust","code":"add_france_fixed(&mut r).await;\n        if let Ok(res) = r.xgroup_create(\"race:france\", \"france_riders\", \"$\").await {\n            let res: () = res;\n            let _ = res;\n            println!(\"OK\"); // >>> OK\n        }","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex8","language":"rust","code":"if let Ok(res) = r.xgroup_create(\"race:france\", \"france_riders\", \"$\") {\n            let res: () = res;\n            let _ = res;\n            println!(\"OK\"); // >>> OK\n        }","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex9","language":"plaintext","code":"> XGROUP CREATE race:italy italy_riders $ MKSTREAM\nOK","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex10","language":"csharp","code":"bool res20 = db.StreamCreateConsumerGroup(\"race:italy\", \"italy_riders\", \"$\", true);\n        Console.WriteLine(res20);   // >>> true","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex11","language":"go","code":"res20, err := rdb.XGroupCreateMkStream(ctx,\n\t\t\"race:italy\", \"italy_riders\", \"$\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res20) // >>> OK","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex12","language":"java","code":"String res20 = jedis.xgroupCreate(\"race:italy\",\"italy_riders\",StreamEntryID.LAST_ENTRY,true);\n    System.out.println(res20); // >>> OK","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex13","language":"javascript","code":"const res19 = await client.xGroupCreate('race:italy', 'italy_riders', '$', {\n  MKSTREAM: true\n});\nconsole.log(res19); // >>> OK","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex14","language":"python","code":"res19 = r.xgroup_create(\"race:italy\", \"italy_riders\", \"$\", mkstream=True)\nprint(res19)  # >>> True","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex15","language":"ruby","code":"r.del('race:italy')\nres19 = r.xgroup(:create, 'race:italy', 'italy_riders', '$', mkstream: true)\nputs res19 # >>> OK","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex16","language":"rust","code":"delete_keys(&mut r, &[\"race:italy\"]).await;\n        if let Ok(res) = r.xgroup_create_mkstream(\"race:italy\", \"italy_riders\", \"$\").await {\n            let res: () = res;\n            let _ = res;\n            println!(\"OK\"); // >>> OK\n        }","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex17","language":"rust","code":"if let Ok(res) = r.xgroup_create_mkstream(\"race:italy\", \"italy_riders\", \"$\") {\n            let res: () = res;\n            let _ = res;\n            println!(\"OK\"); // >>> OK\n        }","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex18","language":"plaintext","code":"special ID\" try_it=\"false\" runnable=\"false\" >}}\n> XADD race:italy * rider Castilla\n\"1692632639151-0\"\n> XADD race:italy * rider Royce\n\"1692632647899-0\"\n> XADD race:italy * rider Sam-Bodden\n\"1692632662819-0\"\n> XADD race:italy * rider Prickett\n\"1692632670501-0\"\n> XADD race:italy * rider Norem\n\"1692632678249-0\"\n> XREADGROUP GROUP italy_riders Alice COUNT 1 STREAMS race:italy >\n1) 1) \"race:italy\"\n   2) 1) 1) \"1692632639151-0\"\n         2) 1) \"rider\"\n            2) \"Castilla\"","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex19","language":"csharp","code":"RedisValue groupRes = db.StreamAdd(\n            \"race:italy\",\n            [new(\"rider\", \"Castilla\")]\n        ); // 1712744323758-0\n\n        groupRes = db.StreamAdd(\n            \"race:italy\",\n            [new(\"rider\", \"Royce\")]\n        ); // 1712744358384-0\n\n        groupRes = db.StreamAdd(\n            \"race:italy\",\n            [new(\"rider\", \"Sam-Bodden\")]\n        ); // 1712744379676-0\n\n        groupRes = db.StreamAdd(\n            \"race:italy\",\n            [new(\"rider\", \"Prickett\")]\n        ); // 1712744399401-0\n\n        groupRes = db.StreamAdd(\n            \"race:italy\",\n            [new(\"rider\", \"Norem\")]\n        ); // 1712744413117-0\n\n        StreamEntry[] res21 = db.StreamReadGroup(\"race:italy\", \"italy_riders\", \"Alice\", \">\", 1);\n\n        foreach (StreamEntry entry in res21)\n        {\n            Console.WriteLine($\"{entry.Id}: [{string.Join(\", \", entry.Values.Select(b => $\"{b.Name}: {b.Value}\"))}]\");\n        }\n        // >>> 1712744323758-0: [rider: Castilla]","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex20","language":"go","code":"_, err = rdb.XAdd(ctx, &redis.XAddArgs{\n\t\tStream: \"race:italy\",\n\t\tValues: map[string]interface{}{\"rider\": \"Castilla\"},\n\t}).Result()\n\t// >>> 1692632639151-0\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t_, err = rdb.XAdd(ctx, &redis.XAddArgs{\n\t\tStream: \"race:italy\",\n\t\tValues: map[string]interface{}{\"rider\": \"Royce\"},\n\t}).Result()\n\t// >>> 1692632647899-0\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t_, err = rdb.XAdd(ctx, &redis.XAddArgs{\n\t\tStream: \"race:italy\",\n\t\tValues: map[string]interface{}{\"rider\": \"Sam-Bodden\"},\n\t}).Result()\n\t// >>> 1692632662819-0\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t_, err = rdb.XAdd(ctx, &redis.XAddArgs{\n\t\tStream: \"race:italy\",\n\t\tValues: map[string]interface{}{\"rider\": \"Prickett\"},\n\t}).Result()\n\t// >>> 1692632670501-0\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t_, err = rdb.XAdd(ctx, &redis.XAddArgs{\n\t\tStream: \"race:italy\",\n\t\tValues: map[string]interface{}{\"rider\": \"Norem\"},\n\t}).Result()\n\t// >>> 1692632678249-0\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// fmt.Println(res25)\n\n\tres21, err := rdb.XReadGroup(ctx, &redis.XReadGroupArgs{\n\t\tStreams:  []string{\"race:italy\", \">\"},\n\t\tGroup:    \"italy_riders\",\n\t\tConsumer: \"Alice\",\n\t\tCount:    1,\n\t}).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// fmt.Println(res21)\n\t// >>> [{race:italy [{1692632639151-0 map[rider:Castilla] 0 0}]}]","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex21","language":"java","code":"StreamEntryID id1 = jedis.xadd(\"race:italy\", new HashMap<String,String>(){{put(\"rider\",\"Castilaa\");}},XAddParams.xAddParams());\n    StreamEntryID id2 = jedis.xadd(\"race:italy\", new HashMap<String,String>(){{put(\"rider\",\"Royce\");}},XAddParams.xAddParams());\n    StreamEntryID id3 = jedis.xadd(\"race:italy\", new HashMap<String,String>(){{put(\"rider\",\"Sam-Bodden\");}},XAddParams.xAddParams());\n    StreamEntryID id4 = jedis.xadd(\"race:italy\", new HashMap<String,String>(){{put(\"rider\",\"Prickett\");}},XAddParams.xAddParams());\n    StreamEntryID id5 = jedis.xadd(\"race:italy\", new HashMap<String,String>(){{put(\"rider\",\"Norem\");}},XAddParams.xAddParams());\n\n    List<Map.Entry<String, List<StreamEntry>>> res21 = jedis.xreadGroup(\"italy_riders\",\"Alice\", XReadGroupParams.xReadGroupParams().count(1),new HashMap<String,StreamEntryID>(){{put(\"race:italy\",StreamEntryID.UNRECEIVED_ENTRY);}});\n    System.out.println(res21); // >>> [race:italy=[1701766299006-0 {rider=Castilaa}]]","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex22","language":"javascript","code":"await client.xAdd('race:italy', '*', {\n  'rider': 'Castilla'\n});\nawait client.xAdd('race:italy', '*', {\n  'rider': 'Royce'\n});\nawait client.xAdd('race:italy', '*', {\n  'rider': 'Sam-Bodden'\n});\nawait client.xAdd('race:italy', '*', {\n  'rider': 'Prickett'\n});\nawait client.xAdd('race:italy', '*', {\n  'rider': 'Norem'\n});\n\nconst res20 = await client.xReadGroup(\n  'italy_riders',\n  'Alice', {\n    key: 'race:italy',\n    id: '>'\n  }, {\n    COUNT: 1\n  }\n);\nconsole.log(res20); // >>> [{ name: 'race:italy', messages: [{ id: '1692629925771-0', message: { rider: 'Castilla' } }] }]","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex23","language":"python","code":"r.xadd(\"race:italy\", {\"rider\": \"Castilla\"})\nr.xadd(\"race:italy\", {\"rider\": \"Royce\"})\nr.xadd(\"race:italy\", {\"rider\": \"Sam-Bodden\"})\nr.xadd(\"race:italy\", {\"rider\": \"Prickett\"})\nr.xadd(\"race:italy\", {\"rider\": \"Norem\"})\n\nres20 = r.xreadgroup(\n    streams={\"race:italy\": \">\"},\n    consumername=\"Alice\",\n    groupname=\"italy_riders\",\n    count=1,\n)\nprint(res20)  # >>> [['race:italy', [('1692629925771-0', {'rider': 'Castilla'})]]]","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex24","language":"ruby","code":"r.del('race:italy')\nr.xgroup(:create, 'race:italy', 'italy_riders', '$', mkstream: true)\nr.xadd('race:italy', {'rider' => 'Castilla'}, id: '1692632639151-0')\nr.xadd('race:italy', {'rider' => 'Royce'}, id: '1692632647899-0')\nr.xadd('race:italy', {'rider' => 'Sam-Bodden'}, id: '1692632662819-0')\nr.xadd('race:italy', {'rider' => 'Prickett'}, id: '1692632670501-0')\nr.xadd('race:italy', {'rider' => 'Norem'}, id: '1692632678249-0')\n\nres20 = r.xreadgroup('italy_riders', 'Alice', ['race:italy'], ['>'], count: 1)\nputs res20.inspect\n# >>> {\"race:italy\"=>[[\"1692632639151-0\", {\"rider\" => \"Castilla\"}]]}","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex25","language":"rust","code":"let italy_1: Option<String> = r\n            .xadd(\"race:italy\", \"1692632639151-0\", &[(\"rider\", \"Castilla\")])\n            .await\n            .expect(\"italy1\");\n        let italy_1 = italy_1.expect(\"missing stream id\");\n        println!(\"{italy_1}\"); // >>> 1692632639151-0\n        let italy_2: Option<String> = r\n            .xadd(\"race:italy\", \"1692632647899-0\", &[(\"rider\", \"Royce\")])\n            .await\n            .expect(\"italy2\");\n        let italy_2 = italy_2.expect(\"missing stream id\");\n        println!(\"{italy_2}\"); // >>> 1692632647899-0\n        let italy_3: Option<String> = r\n            .xadd(\"race:italy\", \"1692632662819-0\", &[(\"rider\", \"Sam-Bodden\")])\n            .await\n            .expect(\"italy3\");\n        let italy_3 = italy_3.expect(\"missing stream id\");\n        println!(\"{italy_3}\"); // >>> 1692632662819-0\n        let italy_4: Option<String> = r\n            .xadd(\"race:italy\", \"1692632670501-0\", &[(\"rider\", \"Prickett\")])\n            .await\n            .expect(\"italy4\");\n        let italy_4 = italy_4.expect(\"missing stream id\");\n        println!(\"{italy_4}\"); // >>> 1692632670501-0\n        let italy_5: Option<String> = r\n            .xadd(\"race:italy\", \"1692632678249-0\", &[(\"rider\", \"Norem\")])\n            .await\n            .expect(\"italy5\");\n        let italy_5 = italy_5.expect(\"missing stream id\");\n        println!(\"{italy_5}\"); // >>> 1692632678249-0\n        let opts = StreamReadOptions::default().group(\"italy_riders\", \"Alice\").count(1);\n        if let Ok(res) = r.xread_options(&[\"race:italy\"], &[\">\"], &opts).await {\n            let res: Option<StreamReadReply> = res;\n            let view: Vec<_> = res\n                .expect(\"xgroup read should return data\")\n                .keys\n                .iter()\n                .map(|stream| {\n                    (\n                        stream.key.clone(),\n                        stream\n                            .ids\n                            .iter()\n                            .map(|entry| {\n                                (\n                                    entry.id.clone(),\n                                    vec![(\n                                        \"rider\".to_string(),\n                                        entry.get::<String>(\"rider\").expect(\"missing rider\"),\n                                    )],\n                                )\n                            })\n                            .collect::<Vec<_>>(),\n                    )\n                })\n                .collect();\n            println!(\"{view:?}\");\n            // >>> [(\"race:italy\", [(\"1692632639151-0\", [(\"rider\", \"Castilla\")])])]\n        }","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex26","language":"rust","code":"let italy_1: Option<String> = r\n            .xadd(\"race:italy\", \"1692632639151-0\", &[(\"rider\", \"Castilla\")])\n            .expect(\"italy1\");\n        let italy_1 = italy_1.expect(\"missing stream id\");\n        println!(\"{italy_1}\"); // >>> 1692632639151-0\n        let italy_2: Option<String> = r\n            .xadd(\"race:italy\", \"1692632647899-0\", &[(\"rider\", \"Royce\")])\n            .expect(\"italy2\");\n        let italy_2 = italy_2.expect(\"missing stream id\");\n        println!(\"{italy_2}\"); // >>> 1692632647899-0\n        let italy_3: Option<String> = r\n            .xadd(\"race:italy\", \"1692632662819-0\", &[(\"rider\", \"Sam-Bodden\")])\n            .expect(\"italy3\");\n        let italy_3 = italy_3.expect(\"missing stream id\");\n        println!(\"{italy_3}\"); // >>> 1692632662819-0\n        let italy_4: Option<String> = r\n            .xadd(\"race:italy\", \"1692632670501-0\", &[(\"rider\", \"Prickett\")])\n            .expect(\"italy4\");\n        let italy_4 = italy_4.expect(\"missing stream id\");\n        println!(\"{italy_4}\"); // >>> 1692632670501-0\n        let italy_5: Option<String> = r\n            .xadd(\"race:italy\", \"1692632678249-0\", &[(\"rider\", \"Norem\")])\n            .expect(\"italy5\");\n        let italy_5 = italy_5.expect(\"missing stream id\");\n        println!(\"{italy_5}\"); // >>> 1692632678249-0\n\n        let opts = StreamReadOptions::default().group(\"italy_riders\", \"Alice\").count(1);\n        if let Ok(res) = r.xread_options(&[\"race:italy\"], &[\">\"], &opts) {\n            let res: Option<StreamReadReply> = res;\n            let view: Vec<_> = res\n                .expect(\"xgroup read should return data\")\n                .keys\n                .iter()\n                .map(|stream| {\n                    (\n                        stream.key.clone(),\n                        stream\n                            .ids\n                            .iter()\n                            .map(|entry| {\n                                (\n                                    entry.id.clone(),\n                                    vec![(\n                                        \"rider\".to_string(),\n                                        entry.get::<String>(\"rider\").expect(\"missing rider\"),\n                                    )],\n                                )\n                            })\n                            .collect::<Vec<_>>(),\n                    )\n                })\n                .collect();\n            println!(\"{view:?}\");\n            // >>> [(\"race:italy\", [(\"1692632639151-0\", [(\"rider\", \"Castilla\")])])]\n        }","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex27","language":"plaintext","code":"> XREADGROUP GROUP italy_riders Alice STREAMS race:italy 0\n1) 1) \"race:italy\"\n   2) 1) 1) \"1692632639151-0\"\n         2) 1) \"rider\"\n            2) \"Castilla\"","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex28","language":"csharp","code":"StreamEntry[] res22 = db.StreamReadGroup(\"race:italy\", \"italy_riders\", \"Alice\", \"0\");\n\n        foreach (StreamEntry entry in res22)\n        {\n            Console.WriteLine($\"{entry.Id}: [{string.Join(\", \", entry.Values.Select(b => $\"{b.Name}: {b.Value}\"))}]\");\n            // >>> 1712744323758-0: [rider: Castilla]\n        }","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex29","language":"go","code":"res22, err := rdb.XReadGroup(ctx, &redis.XReadGroupArgs{\n\t\tStreams:  []string{\"race:italy\", \"0\"},\n\t\tGroup:    \"italy_riders\",\n\t\tConsumer: \"Alice\",\n\t}).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res22)\n\t// >>> [{race:italy [{1692632639151-0 map[rider:Castilla] 0 0}]}]","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex30","language":"java","code":"List<Map.Entry<String, List<StreamEntry>>> res22 = jedis.xreadGroup(\"italy_riders\",\"Alice\", XReadGroupParams.xReadGroupParams().count(1),new HashMap<String,StreamEntryID>(){{put(\"race:italy\",new StreamEntryID());}});\n    System.out.println(res22); // >>> [race:italy=[1701766299006-0 {rider=Castilaa}]]","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex31","language":"javascript","code":"const res21 = await client.xReadGroup(\n  'italy_riders',\n  'Alice', {\n    key: 'race:italy',\n    id: '0'\n  }, {\n    COUNT: 1\n  }\n);\nconsole.log(res21); // >>> [{ name: 'race:italy', messages: [{ id: '1692629925771-0', message: { rider: 'Castilla' } }] }]","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex32","language":"python","code":"res21 = r.xreadgroup(\n    streams={\"race:italy\": 0},\n    consumername=\"Alice\",\n    groupname=\"italy_riders\",\n    count=1,\n)\nprint(res21)  # >>> [['race:italy', [('1692629925771-0', {'rider': 'Castilla'})]]]","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex33","language":"ruby","code":"res21 = r.xreadgroup('italy_riders', 'Alice', ['race:italy'], ['0'], count: 1)\nputs res21.inspect\n# >>> {\"race:italy\"=>[[\"1692632639151-0\", {\"rider\" => \"Castilla\"}]]}","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex34","language":"rust","code":"let opts = StreamReadOptions::default().group(\"italy_riders\", \"Alice\");\n        if let Ok(res) = r.xread_options(&[\"race:italy\"], &[\"0\"], &opts).await {\n            let res: Option<StreamReadReply> = res;\n            let view: Vec<_> = res\n                .expect(\"xgroup history\")\n                .keys\n                .iter()\n                .map(|stream| {\n                    (\n                        stream.key.clone(),\n                        stream\n                            .ids\n                            .iter()\n                            .map(|entry| {\n                                (\n                                    entry.id.clone(),\n                                    vec![(\n                                        \"rider\".to_string(),\n                                        entry.get::<String>(\"rider\").expect(\"missing rider\"),\n                                    )],\n                                )\n                            })\n                            .collect::<Vec<_>>(),\n                    )\n                })\n                .collect();\n            println!(\"{view:?}\");\n            // >>> [(\"race:italy\", [(\"1692632639151-0\", [(\"rider\", \"Castilla\")])])]\n        }","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex35","language":"rust","code":"let opts = StreamReadOptions::default().group(\"italy_riders\", \"Alice\");\n        if let Ok(res) = r.xread_options(&[\"race:italy\"], &[\"0\"], &opts) {\n            let res: Option<StreamReadReply> = res;\n            let view: Vec<_> = res\n                .expect(\"xgroup history\")\n                .keys\n                .iter()\n                .map(|stream| {\n                    (\n                        stream.key.clone(),\n                        stream\n                            .ids\n                            .iter()\n                            .map(|entry| {\n                                (\n                                    entry.id.clone(),\n                                    vec![(\n                                        \"rider\".to_string(),\n                                        entry.get::<String>(\"rider\").expect(\"missing rider\"),\n                                    )],\n                                )\n                            })\n                            .collect::<Vec<_>>(),\n                    )\n                })\n                .collect();\n            println!(\"{view:?}\");\n            // >>> [(\"race:italy\", [(\"1692632639151-0\", [(\"rider\", \"Castilla\")])])]\n        }","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex36","language":"plaintext","code":"> XACK race:italy italy_riders 1692632639151-0\n(integer) 1\n> XREADGROUP GROUP italy_riders Alice STREAMS race:italy 0\n1) 1) \"race:italy\"\n   2) (empty array)","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex37","language":"csharp","code":"long res23 = db.StreamAcknowledge(\"race:italy\", \"italy_riders\", \"1712744323758-0\");\n        Console.WriteLine(res23);   // >>> 1\n\n        StreamEntry[] res24 = db.StreamReadGroup(\"race:italy\", \"italy_riders\", \"Alice\", \"0\");\n\n        foreach (StreamEntry entry in res24)\n        {\n            Console.WriteLine($\"{entry.Id}: [{string.Join(\", \", entry.Values.Select(b => $\"{b.Name}: {b.Value}\"))}]\");\n        }\n        // >>> <empty array>","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex38","language":"go","code":"res23, err := rdb.XAck(ctx,\n\t\t\"race:italy\", \"italy_riders\", \"1692632639151-0\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res23) // >>> 1\n\n\tres24, err := rdb.XReadGroup(ctx, &redis.XReadGroupArgs{\n\t\tStreams:  []string{\"race:italy\", \"0\"},\n\t\tGroup:    \"italy_riders\",\n\t\tConsumer: \"Alice\",\n\t}).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res24)\n\t// >>> [{race:italy []}]","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex39","language":"java","code":"long res23 = jedis.xack(\"race:italy\",\"italy_riders\",id1);\n    System.out.println(res23); // >>> 1\n\n    List<Map.Entry<String, List<StreamEntry>>> res24 = jedis.xreadGroup(\"italy_riders\",\"Alice\", XReadGroupParams.xReadGroupParams().count(1),new HashMap<String,StreamEntryID>(){{put(\"race:italy\",new StreamEntryID());}});\n    System.out.println(res24); // >>> [race:italy=[]]","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex40","language":"javascript","code":"const res22 = await client.xAck('race:italy', 'italy_riders', '1692629925771-0')\nconsole.log(res22); // >>> 1\n\nconst res23 = await client.xReadGroup(\n  'italy_riders',\n  'Alice', {\n    key: 'race:italy',\n    id: '0'\n  }, {\n    COUNT: 1\n  }\n);\nconsole.log(res23); // >>> [{ name: 'race:italy', messages: [] }]","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex41","language":"python","code":"res22 = r.xack(\"race:italy\", \"italy_riders\", \"1692629925771-0\")\nprint(res22)  # >>> 1\n\nres23 = r.xreadgroup(\n    streams={\"race:italy\": 0},\n    consumername=\"Alice\",\n    groupname=\"italy_riders\",\n    count=1,\n)\nprint(res23)  # >>> [['race:italy', []]]","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex42","language":"ruby","code":"res22 = r.xack('race:italy', 'italy_riders', '1692632639151-0')\nputs res22 # >>> 1\n\nres23 = r.xreadgroup('italy_riders', 'Alice', ['race:italy'], ['0'])\nputs res23.inspect\n# >>> {\"race:italy\"=>[]}","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex43","language":"rust","code":"if let Ok(res) = r.xack(\"race:italy\", \"italy_riders\", &[\"1692632639151-0\"]).await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 1\n        }\n        let opts = StreamReadOptions::default().group(\"italy_riders\", \"Alice\");\n        if let Ok(res) = r.xread_options(&[\"race:italy\"], &[\"0\"], &opts).await {\n            let res: Option<StreamReadReply> = res;\n            let view: Vec<_> = res\n                .expect(\"xgroup history\")\n                .keys\n                .iter()\n                .map(|stream| {\n                    (\n                        stream.key.clone(),\n                        stream\n                            .ids\n                            .iter()\n                            .map(|entry| {\n                                (\n                                    entry.id.clone(),\n                                    vec![(\n                                        \"rider\".to_string(),\n                                        entry.get::<String>(\"rider\").expect(\"missing rider\"),\n                                    )],\n                                )\n                            })\n                            .collect::<Vec<_>>(),\n                    )\n                })\n                .collect();\n            println!(\"{view:?}\"); // >>> [(\"race:italy\", [])]\n        }","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex44","language":"rust","code":"if let Ok(res) = r.xack(\"race:italy\", \"italy_riders\", &[\"1692632639151-0\"]) {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 1\n        }\n\n        let opts = StreamReadOptions::default().group(\"italy_riders\", \"Alice\");\n        if let Ok(res) = r.xread_options(&[\"race:italy\"], &[\"0\"], &opts) {\n            let res: Option<StreamReadReply> = res;\n            let view: Vec<_> = res\n                .expect(\"xgroup history\")\n                .keys\n                .iter()\n                .map(|stream| {\n                    (\n                        stream.key.clone(),\n                        stream\n                            .ids\n                            .iter()\n                            .map(|entry| {\n                                (\n                                    entry.id.clone(),\n                                    vec![(\n                                        \"rider\".to_string(),\n                                        entry.get::<String>(\"rider\").expect(\"missing rider\"),\n                                    )],\n                                )\n                            })\n                            .collect::<Vec<_>>(),\n                    )\n                })\n                .collect();\n            println!(\"{view:?}\"); // >>> [(\"race:italy\", [])]\n        }","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex45","language":"plaintext","code":"> XREADGROUP GROUP italy_riders Bob COUNT 2 STREAMS race:italy >\n1) 1) \"race:italy\"\n   2) 1) 1) \"1692632647899-0\"\n         2) 1) \"rider\"\n            2) \"Royce\"\n      2) 1) \"1692632662819-0\"\n         2) 1) \"rider\"\n            2) \"Sam-Bodden\"","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex46","language":"csharp","code":"StreamEntry[] res25 = db.StreamReadGroup(\"race:italy\", \"italy_riders\", \"Bob\", \">\", 2);\n\n        foreach (StreamEntry entry in res25)\n        {\n            Console.WriteLine($\"{entry.Id}: [{string.Join(\", \", entry.Values.Select(b => $\"{b.Name}: {b.Value}\"))}]\");\n        }\n        // >>> 1712744358384-0: [rider: Royce]\n        // >>> 1712744379676-0: [rider: Sam-Bodden]","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex47","language":"go","code":"res25, err := rdb.XReadGroup(ctx, &redis.XReadGroupArgs{\n\t\tStreams:  []string{\"race:italy\", \">\"},\n\t\tGroup:    \"italy_riders\",\n\t\tConsumer: \"Bob\",\n\t\tCount:    2,\n\t}).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res25)\n\t// >>> [{race:italy [{1692632647899-0 map[rider:Royce] 0 0} {1692632662819-0 map[rider:Sam-Bodden] 0 0}]}]","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex48","language":"java","code":"List<Map.Entry<String, List<StreamEntry>>> res25 = jedis.xreadGroup(\"italy_riders\",\"Bob\", XReadGroupParams.xReadGroupParams().count(2),new HashMap<String,StreamEntryID>(){{put(\"race:italy\",StreamEntryID.UNRECEIVED_ENTRY);}});\n    System.out.println(res25); // >>> [race:italy=[1701767632261-1 {rider=Royce}, 1701767632262-0 {rider=Sam-Bodden}]]","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex49","language":"javascript","code":"const res24 = await client.xReadGroup(\n  'italy_riders',\n  'Bob', {\n    key: 'race:italy',\n    id: '>'\n  }, {\n    COUNT: 2\n  }\n);\nconsole.log(res24); // >>> [{ name: 'race:italy', messages: [{ id: '1692629925789-0', message: { rider: 'Royce' } }, { id: '1692629925790-0', message: { rider: 'Sam-Bodden' } }] }]","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex50","language":"python","code":"res24 = r.xreadgroup(\n    streams={\"race:italy\": \">\"},\n    consumername=\"Bob\",\n    groupname=\"italy_riders\",\n    count=2,\n)\nprint(\n    res24\n)\n# >>> [\n#       ['race:italy', [\n#           ('1692629925789-0',\n#               {'rider': 'Royce'}\n#           ),\n#           ('1692629925790-0',\n#               {'rider': 'Sam-Bodden'}\n#           )\n#       ]\n#       ]\n# ]","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex51","language":"ruby","code":"res24 = r.xreadgroup('italy_riders', 'Bob', ['race:italy'], ['>'], count: 2)\nputs res24.inspect\n# >>> {\"race:italy\"=>[[\"1692632647899-0\", {\"rider\" => \"Royce\"}],\n#                 [\"1692632662819-0\", {\"rider\" => \"Sam-Bodden\"}]]}","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex52","language":"rust","code":"let opts = StreamReadOptions::default().group(\"italy_riders\", \"Bob\").count(2);\n        if let Ok(res) = r.xread_options(&[\"race:italy\"], &[\">\"], &opts).await {\n            let res: Option<StreamReadReply> = res;\n            let view: Vec<_> = res\n                .expect(\"bob should receive data\")\n                .keys\n                .iter()\n                .map(|stream| {\n                    (\n                        stream.key.clone(),\n                        stream\n                            .ids\n                            .iter()\n                            .map(|entry| {\n                                (\n                                    entry.id.clone(),\n                                    vec![(\n                                        \"rider\".to_string(),\n                                        entry.get::<String>(\"rider\").expect(\"missing rider\"),\n                                    )],\n                                )\n                            })\n                            .collect::<Vec<_>>(),\n                    )\n                })\n                .collect();\n            println!(\"{view:?}\");\n            // >>> [(\"race:italy\", [(\"1692632647899-0\", [(\"rider\", \"Royce\")]), (\"1692632662819-0\", [(\"rider\", \"Sam-Bodden\")])])]\n        }","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex53","language":"rust","code":"let opts = StreamReadOptions::default().group(\"italy_riders\", \"Bob\").count(2);\n        if let Ok(res) = r.xread_options(&[\"race:italy\"], &[\">\"], &opts) {\n            let res: Option<StreamReadReply> = res;\n            let view: Vec<_> = res\n                .expect(\"bob should receive data\")\n                .keys\n                .iter()\n                .map(|stream| {\n                    (\n                        stream.key.clone(),\n                        stream\n                            .ids\n                            .iter()\n                            .map(|entry| {\n                                (\n                                    entry.id.clone(),\n                                    vec![(\n                                        \"rider\".to_string(),\n                                        entry.get::<String>(\"rider\").expect(\"missing rider\"),\n                                    )],\n                                )\n                            })\n                            .collect::<Vec<_>>(),\n                    )\n                })\n                .collect();\n            println!(\"{view:?}\");\n            // >>> [(\"race:italy\", [(\"1692632647899-0\", [(\"rider\", \"Royce\")]), (\"1692632662819-0\", [(\"rider\", \"Sam-Bodden\")])])]\n        }","section_id":"creating-a-consumer-group"},{"id":"creating-a-consumer-group-ex54","language":"ruby","code":"require 'redis'\n\nif ARGV.length == 0\n    puts \"Please specify a consumer name\"\n    exit 1\nend\n\nConsumerName = ARGV[0]\nGroupName = \"mygroup\"\nr = Redis.new\n\ndef process_message(id,msg)\n    puts \"[#{ConsumerName}] #{id} = #{msg.inspect}\"\nend\n\n$lastid = '0-0'\n\nputs \"Consumer #{ConsumerName} starting...\"\ncheck_backlog = true\nwhile true\n    # Pick the ID based on the iteration: the first time we want to\n    # read our pending messages, in case we crashed and are recovering.\n    # Once we consumed our history, we can start getting new messages.\n    if check_backlog\n        myid = $lastid\n    else\n        myid = '>'\n    end\n\n    items = r.xreadgroup('GROUP',GroupName,ConsumerName,'BLOCK','2000','COUNT','10','STREAMS',:my_stream_key,myid)\n\n    if items == nil\n        puts \"Timeout!\"\n        next\n    end\n\n    # If we receive an empty reply, it means we were consuming our history\n    # and that the history is now empty. Let's start to consume new messages.\n    check_backlog = false if items[0][1].length == 0\n\n    items[0][1].each{|i|\n        id,fields = i\n\n        # Process the message\n        process_message(id,fields)\n\n        # Acknowledge the message as processed\n        r.xack(:my_stream_key,GroupName,id)\n\n        $lastid = id\n    }\nend","section_id":"creating-a-consumer-group"},{"id":"recovering-from-permanent-failures-ex0","language":"plaintext","code":"> XPENDING race:italy italy_riders\n1) (integer) 2\n2) \"1692632647899-0\"\n3) \"1692632662819-0\"\n4) 1) 1) \"Bob\"\n      2) \"2\"","section_id":"recovering-from-permanent-failures"},{"id":"recovering-from-permanent-failures-ex1","language":"csharp","code":"StreamPendingInfo res26 = db.StreamPending(\"race:italy\", \"italy_riders\");\n        Console.WriteLine($\"pending: {res26.PendingMessageCount}, min: {res26.LowestPendingMessageId}, max: {res26.HighestPendingMessageId}, consumers:[{string.Join(\", \", res26.Consumers.Select(c => $\"{c.Name}: {c.PendingMessageCount}\"))}]\");\n        // >>> pending: 2, min: 1712747506906-0, max: 1712747506907-0, consumers:[name: Bob, pending:2]","section_id":"recovering-from-permanent-failures"},{"id":"recovering-from-permanent-failures-ex2","language":"go","code":"res26, err := rdb.XPending(ctx, \"race:italy\", \"italy_riders\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res26)\n\t// >>> &{2 1692632647899-0 1692632662819-0 map[Bob:2]}","section_id":"recovering-from-permanent-failures"},{"id":"recovering-from-permanent-failures-ex3","language":"java","code":"StreamPendingSummary res26 = jedis.xpending(\"race:italy\",\"italy_riders\");\n    System.out.println(res26.getConsumerMessageCount()); // >>> {Bob=2}","section_id":"recovering-from-permanent-failures"},{"id":"recovering-from-permanent-failures-ex4","language":"javascript","code":"const res25 = await client.xPending('race:italy', 'italy_riders');\nconsole.log(res25); // >>> {'pending': 2, 'firstId': '1692629925789-0', 'lastId': '1692629925790-0', 'consumers': [{'name': 'Bob', 'deliveriesCounter': 2}]}","section_id":"recovering-from-permanent-failures"},{"id":"recovering-from-permanent-failures-ex5","language":"python","code":"res25 = r.xpending(\"race:italy\", \"italy_riders\")\nprint(\n    res25\n)\n# >>> {\n#       'pending': 2, 'min': '1692629925789-0', 'max': '1692629925790-0',\n#       'consumers': [{'name': 'Bob', 'pending': 2}]\n# }","section_id":"recovering-from-permanent-failures"},{"id":"recovering-from-permanent-failures-ex6","language":"ruby","code":"res25 = r.xpending('race:italy', 'italy_riders')\nputs res25.inspect\n# >>> {\"size\"=>2, \"min_entry_id\" => \"1692632647899-0\", \"max_entry_id\" => \"1692632662819-0\", \"consumers\"=>{\"Bob\" => \"2\"}}","section_id":"recovering-from-permanent-failures"},{"id":"recovering-from-permanent-failures-ex7","language":"rust","code":"if let Ok(res) = r.xpending(\"race:italy\", \"italy_riders\").await {\n            let res: StreamPendingReply = res;\n            let view = match res {\n                StreamPendingReply::Empty => None,\n                StreamPendingReply::Data(data) => Some((\n                    data.count,\n                    data.start_id.clone(),\n                    data.end_id.clone(),\n                    data.consumers\n                        .iter()\n                        .map(|consumer| (consumer.name.clone(), consumer.pending))\n                        .collect::<Vec<_>>(),\n                )),\n            }\n            .expect(\"pending summary\");\n            println!(\"{view:?}\");\n            // >>> (2, \"1692632647899-0\", \"1692632662819-0\", [(\"Bob\", 2)])\n        }","section_id":"recovering-from-permanent-failures"},{"id":"recovering-from-permanent-failures-ex8","language":"rust","code":"if let Ok(res) = r.xpending(\"race:italy\", \"italy_riders\") {\n            let res: StreamPendingReply = res;\n            let view = match res {\n                StreamPendingReply::Empty => None,\n                StreamPendingReply::Data(data) => Some((\n                    data.count,\n                    data.start_id.clone(),\n                    data.end_id.clone(),\n                    data.consumers\n                        .iter()\n                        .map(|consumer| (consumer.name.clone(), consumer.pending))\n                        .collect::<Vec<_>>(),\n                )),\n            }\n            .expect(\"pending summary\");\n            println!(\"{view:?}\");\n            // >>> (2, \"1692632647899-0\", \"1692632662819-0\", [(\"Bob\", 2)])\n        }","section_id":"recovering-from-permanent-failures"},{"id":"recovering-from-permanent-failures-ex9","language":"plaintext","code":"XPENDING <key> <groupname> [[IDLE <min-idle-time>] <start-id> <end-id> <count> [<consumer-name>]]","section_id":"recovering-from-permanent-failures"},{"id":"recovering-from-permanent-failures-ex10","language":"plaintext","code":"> XPENDING race:italy italy_riders - + 10\n1) 1) \"1692632647899-0\"\n   2) \"Bob\"\n   3) (integer) 74642\n   4) (integer) 1\n2) 1) \"1692632662819-0\"\n   2) \"Bob\"\n   3) (integer) 74642\n   4) (integer) 1","section_id":"recovering-from-permanent-failures"},{"id":"recovering-from-permanent-failures-ex11","language":"csharp","code":"StreamPendingMessageInfo[] res27 = db.StreamPendingMessages(\n            \"race:italy\", \"italy_riders\", 10, \"\", \"-\", \"+\"\n        );\n\n        foreach (StreamPendingMessageInfo info in res27)\n        {\n            Console.WriteLine($\"message_id: {info.MessageId}, consumer: {info.ConsumerName}, time_since_delivered: {info.IdleTimeInMilliseconds}, times_delivered: {info.DeliveryCount}\");\n        }\n        // >>> message_id: min: 1712747506906-0, consumer: Bob, time_since_delivered: 31084, times_delivered: 1\n        // >>> message_id: min: 1712747506907-0, consumer: Bob, time_since_delivered: 31084, times_delivered: 1","section_id":"recovering-from-permanent-failures"},{"id":"recovering-from-permanent-failures-ex12","language":"go","code":"res27, err := rdb.XPendingExt(ctx, &redis.XPendingExtArgs{\n\t\tStream: \"race:italy\",\n\t\tGroup:  \"italy_riders\",\n\t\tStart:  \"-\",\n\t\tEnd:    \"+\",\n\t\tCount:  10,\n\t}).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// fmt.Println(res27)\n\t// >>> [{1692632647899-0 Bob 0s 1} {1692632662819-0 Bob 0s 1}]","section_id":"recovering-from-permanent-failures"},{"id":"recovering-from-permanent-failures-ex13","language":"java","code":"List<StreamPendingEntry> res27 = jedis.xpending(\"race:italy\",\"italy_riders\",XPendingParams.xPendingParams().start(StreamEntryID.MINIMUM_ID).end(StreamEntryID.MAXIMUM_ID).count(10));\n    System.out.println(res27); // >>> [1701768567412-1 Bob idle:0 times:1, 1701768567412-2 Bob idle:0 times:1]","section_id":"recovering-from-permanent-failures"},{"id":"recovering-from-permanent-failures-ex14","language":"javascript","code":"const res26 = await client.xPendingRange('race:italy', 'italy_riders', '-', '+', 10);\nconsole.log(res26); // >>> [{'id': '1692629925789-0', 'consumer': 'Bob', 'millisecondsSinceLastDelivery': 31084, 'deliveriesCounter:': 1}, {'id': '1692629925790-0', 'consumer': 'Bob', 'millisecondsSinceLastDelivery': 31084, 'deliveriesCounter': 1}]","section_id":"recovering-from-permanent-failures"},{"id":"recovering-from-permanent-failures-ex15","language":"python","code":"res26 = r.xpending_range(\"race:italy\", \"italy_riders\", \"-\", \"+\", 10)\nprint(\n    res26\n)\n# >>> [\n#       {\n#           'message_id': '1692629925789-0', 'consumer': 'Bob',\n#           'time_since_delivered': 31084, 'times_delivered': 1\n#       },\n#       {\n#           'message_id': '1692629925790-0', 'consumer': 'Bob',\n#           'time_since_delivered': 31084, 'times_delivered': 1\n#       }\n# ]","section_id":"recovering-from-permanent-failures"},{"id":"recovering-from-permanent-failures-ex16","language":"ruby","code":"res26 = r.xpending('race:italy', 'italy_riders', '-', '+', 10)\nputs res26.inspect","section_id":"recovering-from-permanent-failures"},{"id":"recovering-from-permanent-failures-ex17","language":"rust","code":"if let Ok(res) = r.xpending_count(\"race:italy\", \"italy_riders\", \"-\", \"+\", 10).await {\n            let res: StreamPendingCountReply = res;\n            let view: Vec<_> = res\n                .ids\n                .iter()\n                .map(|entry| {\n                    (\n                        entry.id.clone(),\n                        entry.consumer.clone(),\n                        entry.last_delivered_ms,\n                        entry.times_delivered,\n                    )\n                })\n                .collect();\n            println!(\"{view:?}\");\n            // >>> [(\"1692632647899-0\", \"Bob\", 5, 1), (\"1692632662819-0\", \"Bob\", 5, 1)]\n        }","section_id":"recovering-from-permanent-failures"},{"id":"recovering-from-permanent-failures-ex18","language":"rust","code":"if let Ok(res) = r.xpending_count(\"race:italy\", \"italy_riders\", \"-\", \"+\", 10) {\n            let res: StreamPendingCountReply = res;\n            let view: Vec<_> = res\n                .ids\n                .iter()\n                .map(|entry| {\n                    (\n                        entry.id.clone(),\n                        entry.consumer.clone(),\n                        entry.last_delivered_ms,\n                        entry.times_delivered,\n                    )\n                })\n                .collect();\n            println!(\"{view:?}\");\n            // >>> [(\"1692632647899-0\", \"Bob\", 5, 1), (\"1692632662819-0\", \"Bob\", 5, 1)]\n        }","section_id":"recovering-from-permanent-failures"},{"id":"recovering-from-permanent-failures-ex19","language":"plaintext","code":"> XRANGE race:italy 1692632647899-0 1692632647899-0\n1) 1) \"1692632647899-0\"\n   2) 1) \"rider\"\n      2) \"Royce\"","section_id":"recovering-from-permanent-failures"},{"id":"recovering-from-permanent-failures-ex20","language":"csharp","code":"StreamEntry[] res28 = db.StreamRange(\"race:italy\", \"1712744358384-0\", \"1712744358384-0\");\n\n        foreach (StreamEntry entry in res28)\n        {\n            Console.WriteLine($\"{entry.Id}: [{string.Join(\", \", entry.Values.Select(b => $\"{b.Name}: {b.Value}\"))}]\");\n        }\n        // >>> 1712744358384-0: [rider: Royce]","section_id":"recovering-from-permanent-failures"},{"id":"recovering-from-permanent-failures-ex21","language":"go","code":"res28, err := rdb.XRange(ctx, \"race:italy\",\n\t\t\"1692632647899-0\", \"1692632647899-0\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res28) // >>> [{1692632647899-0 map[rider:Royce] 0 0}]","section_id":"recovering-from-permanent-failures"},{"id":"recovering-from-permanent-failures-ex22","language":"java","code":"List<StreamEntry> res28 = jedis.xrange(\"race:italy\",id2.toString(),id2.toString());\n    System.out.println(res28); // >>> [1701768744819-1 {rider=Royce}]","section_id":"recovering-from-permanent-failures"},{"id":"recovering-from-permanent-failures-ex23","language":"javascript","code":"const res27 = await client.xRange('race:italy', '1692629925789-0', '1692629925789-0');\nconsole.log(res27); // >>> [{ id: '1692629925789-0', message: { rider: 'Royce' } }]","section_id":"recovering-from-permanent-failures"},{"id":"recovering-from-permanent-failures-ex24","language":"python","code":"res27 = r.xrange(\"race:italy\", \"1692629925789-0\", \"1692629925789-0\")\nprint(res27)  # >>> [('1692629925789-0', {'rider': 'Royce'})]","section_id":"recovering-from-permanent-failures"},{"id":"recovering-from-permanent-failures-ex25","language":"ruby","code":"res27 = r.xrange('race:italy', '1692632647899-0', '1692632647899-0')\nputs res27.inspect\n# >>> [[\"1692632647899-0\", {\"rider\" => \"Royce\"}]]","section_id":"recovering-from-permanent-failures"},{"id":"recovering-from-permanent-failures-ex26","language":"rust","code":"if let Ok(res) = r.xrange(\"race:italy\", \"1692632647899-0\", \"1692632647899-0\").await {\n            let res: StreamRangeReply = res;\n            let view: Vec<_> = res\n                .ids\n                .iter()\n                .map(|entry| {\n                    (\n                        entry.id.clone(),\n                        vec![(\n                            \"rider\".to_string(),\n                            entry.get::<String>(\"rider\").expect(\"missing rider\"),\n                        )],\n                    )\n                })\n                .collect();\n            println!(\"{view:?}\"); // >>> [(\"1692632647899-0\", [(\"rider\", \"Royce\")])]\n        }","section_id":"recovering-from-permanent-failures"},{"id":"recovering-from-permanent-failures-ex27","language":"rust","code":"if let Ok(res) = r.xrange(\"race:italy\", \"1692632647899-0\", \"1692632647899-0\") {\n            let res: StreamRangeReply = res;\n            let view: Vec<_> = res\n                .ids\n                .iter()\n                .map(|entry| {\n                    (\n                        entry.id.clone(),\n                        vec![(\n                            \"rider\".to_string(),\n                            entry.get::<String>(\"rider\").expect(\"missing rider\"),\n                        )],\n                    )\n                })\n                .collect();\n            println!(\"{view:?}\"); // >>> [(\"1692632647899-0\", [(\"rider\", \"Royce\")])]\n        }","section_id":"recovering-from-permanent-failures"},{"id":"recovering-from-permanent-failures-ex28","language":"plaintext","code":"XCLAIM <key> <group> <consumer> <min-idle-time> <ID-1> <ID-2> ... <ID-N>","section_id":"recovering-from-permanent-failures"},{"id":"recovering-from-permanent-failures-ex29","language":"plaintext","code":"Client 1: XCLAIM race:italy italy_riders Alice 60000 1692632647899-0\nClient 2: XCLAIM race:italy italy_riders Lora 60000 1692632647899-0","section_id":"recovering-from-permanent-failures"},{"id":"recovering-from-permanent-failures-ex30","language":"plaintext","code":"> XCLAIM race:italy italy_riders Alice 60000 1692632647899-0\n1) 1) \"1692632647899-0\"\n   2) 1) \"rider\"\n      2) \"Royce\"","section_id":"recovering-from-permanent-failures"},{"id":"recovering-from-permanent-failures-ex31","language":"csharp","code":"StreamEntry[] res29 = db.StreamClaim(\n            \"race:italy\", \"italy_riders\", \"Alice\", 60000, [1712744358384 - 0]\n        );\n\n        foreach (StreamEntry entry in res29)\n        {\n            Console.WriteLine($\"{entry.Id}: [{string.Join(\", \", entry.Values.Select(b => $\"{b.Name}: {b.Value}\"))}]\");\n        }\n        // >>> 1712744358384-0: [rider: Royce]","section_id":"recovering-from-permanent-failures"},{"id":"recovering-from-permanent-failures-ex32","language":"go","code":"res29, err := rdb.XClaim(ctx, &redis.XClaimArgs{\n\t\tStream:   \"race:italy\",\n\t\tGroup:    \"italy_riders\",\n\t\tConsumer: \"Alice\",\n\t\tMinIdle:  0,\n\t\tMessages: []string{\"1692632647899-0\"},\n\t}).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res29)","section_id":"recovering-from-permanent-failures"},{"id":"recovering-from-permanent-failures-ex33","language":"java","code":"List<StreamEntry> res29 = jedis.xclaim(\"race:italy\",\"italy_riders\",\"Alice\", 0L, XClaimParams.xClaimParams().time(60000),id2);\n    System.out.println(res29); // >>> [1701769004195-1 {rider=Royce}]","section_id":"recovering-from-permanent-failures"},{"id":"recovering-from-permanent-failures-ex34","language":"javascript","code":"const res28 = await client.xClaim(\n  'race:italy', 'italy_riders', 'Alice', 60000, ['1692629925789-0']\n);\nconsole.log(res28); // >>> [{ id: '1692629925789-0', message: { rider: 'Royce' } }]","section_id":"recovering-from-permanent-failures"},{"id":"recovering-from-permanent-failures-ex35","language":"python","code":"res28 = r.xclaim(\"race:italy\", \"italy_riders\", \"Alice\", 60000, [\"1692629925789-0\"])\nprint(res28)  # >>> [('1692629925789-0', {'rider': 'Royce'})]","section_id":"recovering-from-permanent-failures"},{"id":"recovering-from-permanent-failures-ex36","language":"ruby","code":"res28 = r.xclaim('race:italy', 'italy_riders', 'Alice', 0, '1692632647899-0')\nputs res28.inspect\n# >>> [[\"1692632647899-0\", {\"rider\" => \"Royce\"}]]","section_id":"recovering-from-permanent-failures"},{"id":"recovering-from-permanent-failures-ex37","language":"rust","code":"if let Ok(res) = r\n            .xclaim(\"race:italy\", \"italy_riders\", \"Alice\", 1, &[\"1692632647899-0\"])\n            .await\n        {\n            let res: redis::streams::StreamClaimReply = res;\n            let view: Vec<_> = res\n                .ids\n                .iter()\n                .map(|entry| {\n                    (\n                        entry.id.clone(),\n                        vec![(\n                            \"rider\".to_string(),\n                            entry.get::<String>(\"rider\").expect(\"missing rider\"),\n                        )],\n                    )\n                })\n                .collect();\n            println!(\"{view:?}\"); // >>> [(\"1692632647899-0\", [(\"rider\", \"Royce\")])]\n        }","section_id":"recovering-from-permanent-failures"},{"id":"recovering-from-permanent-failures-ex38","language":"rust","code":"if let Ok(res) = r.xclaim(\"race:italy\", \"italy_riders\", \"Alice\", 1, &[\"1692632647899-0\"]) {\n            let res: redis::streams::StreamClaimReply = res;\n            let view: Vec<_> = res\n                .ids\n                .iter()\n                .map(|entry| {\n                    (\n                        entry.id.clone(),\n                        vec![(\n                            \"rider\".to_string(),\n                            entry.get::<String>(\"rider\").expect(\"missing rider\"),\n                        )],\n                    )\n                })\n                .collect();\n            println!(\"{view:?}\"); // >>> [(\"1692632647899-0\", [(\"rider\", \"Royce\")])]\n        }","section_id":"recovering-from-permanent-failures"},{"id":"automatic-claiming-ex0","language":"plaintext","code":"XAUTOCLAIM <key> <group> <consumer> <min-idle-time> <start> [COUNT count] [JUSTID]","section_id":"automatic-claiming"},{"id":"automatic-claiming-ex1","language":"plaintext","code":"> XAUTOCLAIM race:italy italy_riders Alice 60000 0-0 COUNT 1\n1) \"0-0\"\n2) 1) 1) \"1692632662819-0\"\n      2) 1) \"rider\"\n         2) \"Sam-Bodden\"","section_id":"automatic-claiming"},{"id":"automatic-claiming-ex2","language":"csharp","code":"StreamAutoClaimResult res30 = db.StreamAutoClaim(\n            \"race:italy\", \"italy_riders\", \"Alice\", 1, \"0-0\", 1\n        );\n\n        Console.WriteLine($\"{res30.NextStartId}, ({string.Join(\", \", res30.ClaimedEntries.Select(entry => $\"{entry.Id}: [{string.Join(\", \", entry.Values.Select(b => $\"{b.Name}: {b.Value}\"))}]\"))})\");\n        // >>> 1712744379676-0, (1712744358384-0: [rider: Royce])","section_id":"automatic-claiming"},{"id":"automatic-claiming-ex3","language":"go","code":"res30, res30a, err := rdb.XAutoClaim(ctx, &redis.XAutoClaimArgs{\n\t\tStream:   \"race:italy\",\n\t\tGroup:    \"italy_riders\",\n\t\tConsumer: \"Alice\",\n\t\tStart:    \"0-0\",\n\t\tCount:    1,\n\t}).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res30)  // >>> [{1692632647899-0 map[rider:Royce] 0 0}]\n\tfmt.Println(res30a) // >>> 1692632662819-0","section_id":"automatic-claiming"},{"id":"automatic-claiming-ex4","language":"java","code":"Map.Entry<StreamEntryID, List<StreamEntry>> res30 = jedis.xautoclaim(\"race:italy\",\"italy_riders\",\"Alice\",1L,new StreamEntryID(\"0-0\"),XAutoClaimParams.xAutoClaimParams().count(1));\n    System.out.println(res30); // >>> [1701769266831-2=[1701769266831-1 {rider=Royce}]","section_id":"automatic-claiming"},{"id":"automatic-claiming-ex5","language":"javascript","code":"const res29 = await client.xAutoClaim('race:italy', 'italy_riders', 'Alice', 1, '0-0', {\n  COUNT: 1\n});\nconsole.log(res29); // >>> { nextId: '1692629925790-0', messages: [{ id: '1692629925789-0', message: { rider: 'Royce' } }], deletedMessages: [] }","section_id":"automatic-claiming"},{"id":"automatic-claiming-ex6","language":"python","code":"res29 = r.xautoclaim(\"race:italy\", \"italy_riders\", \"Alice\", 1, \"0-0\", 1)\nprint(res29)  # >>> ['1692629925790-0', [('1692629925789-0', {'rider': 'Royce'})]]","section_id":"automatic-claiming"},{"id":"automatic-claiming-ex7","language":"ruby","code":"res29 = r.xautoclaim('race:italy', 'italy_riders', 'Alice', 0, '0-0', count: 1)\nputs res29.inspect\n# >>> {\"next\" => \"1692632662819-0\", \"entries\"=>[[\"1692632647899-0\", {\"rider\" => \"Royce\"}]]}","section_id":"automatic-claiming"},{"id":"automatic-claiming-ex8","language":"rust","code":"let opts = StreamAutoClaimOptions::default().count(1);\n        if let Ok(res) = r\n            .xautoclaim_options(\"race:italy\", \"italy_riders\", \"Alice\", 1, \"0-0\", opts)\n            .await\n        {\n            let res: redis::streams::StreamAutoClaimReply = res;\n            let claimed: Vec<_> = res\n                .claimed\n                .iter()\n                .map(|entry| {\n                    (\n                        entry.id.clone(),\n                        vec![(\n                            \"rider\".to_string(),\n                            entry.get::<String>(\"rider\").expect(\"missing rider\"),\n                        )],\n                    )\n                })\n                .collect();\n            println!(\"{:?}\", (res.next_stream_id.clone(), &claimed));\n            // >>> (\"1692632662819-0\", [(\"1692632647899-0\", [(\"rider\", \"Royce\")])])\n        }","section_id":"automatic-claiming"},{"id":"automatic-claiming-ex9","language":"rust","code":"let opts = StreamAutoClaimOptions::default().count(1);\n        if let Ok(res) = r.xautoclaim_options(\"race:italy\", \"italy_riders\", \"Alice\", 1, \"0-0\", opts) {\n            let res: redis::streams::StreamAutoClaimReply = res;\n            let claimed: Vec<_> = res\n                .claimed\n                .iter()\n                .map(|entry| {\n                    (\n                        entry.id.clone(),\n                        vec![(\n                            \"rider\".to_string(),\n                            entry.get::<String>(\"rider\").expect(\"missing rider\"),\n                        )],\n                    )\n                })\n                .collect();\n            println!(\"{:?}\", (res.next_stream_id.clone(), &claimed));\n            // >>> (\"1692632662819-0\", [(\"1692632647899-0\", [(\"rider\", \"Royce\")])])\n        }","section_id":"automatic-claiming"},{"id":"automatic-claiming-ex10","language":"plaintext","code":"> XAUTOCLAIM race:italy italy_riders Lora 60000 (1692632662819-0 COUNT 1\n1) \"1692632662819-0\"\n2) 1) 1) \"1692632647899-0\"\n      2) 1) \"rider\"\n         2) \"Royce\"","section_id":"automatic-claiming"},{"id":"automatic-claiming-ex11","language":"csharp","code":"StreamAutoClaimResult res31 = db.StreamAutoClaim(\n            \"race:italy\", \"italy_riders\", \"Alice\", 1, \"(1712744358384-0\", 1\n        );\n\n        Console.WriteLine($\"{res31.NextStartId}, ({string.Join(\", \", res31.ClaimedEntries.Select(entry => $\"{entry.Id}: [{string.Join(\", \", entry.Values.Select(b => $\"{b.Name}: {b.Value}\"))}]\"))})\");\n        // >>> 0-0, (1712744379676-0: [rider: Sam-Bodden])","section_id":"automatic-claiming"},{"id":"automatic-claiming-ex12","language":"go","code":"res31, res31a, err := rdb.XAutoClaim(ctx, &redis.XAutoClaimArgs{\n\t\tStream:   \"race:italy\",\n\t\tGroup:    \"italy_riders\",\n\t\tConsumer: \"Lora\",\n\t\tStart:    \"(1692632662819-0\",\n\t\tCount:    1,\n\t}).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res31)  // >>> []\n\tfmt.Println(res31a) // >>> 0-0","section_id":"automatic-claiming"},{"id":"automatic-claiming-ex13","language":"java","code":"Map.Entry<StreamEntryID, List<StreamEntry>> res31 = jedis.xautoclaim(\"race:italy\",\"italy_riders\",\"Alice\",1L,new StreamEntryID(id2.toString()),XAutoClaimParams.xAutoClaimParams().count(1));\n    System.out.println(res31); // >>> [0-0=[1701769605847-2 {rider=Sam-Bodden}]","section_id":"automatic-claiming"},{"id":"automatic-claiming-ex14","language":"javascript","code":"const res30 = await client.xAutoClaim(\n  'race:italy', 'italy_riders', 'Alice', 1, '(1692629925789-0',\n  {\n    COUNT: 1\n  }\n);\nconsole.log(res30); // >>> { nextId: '0-0', messages: [{ id: '1692629925790-0', message: { rider: 'Sam-Bodden' } }], deletedMessages: [] }","section_id":"automatic-claiming"},{"id":"automatic-claiming-ex15","language":"python","code":"res30 = r.xautoclaim(\"race:italy\", \"italy_riders\", \"Alice\", 1, \"(1692629925789-0\", 1)\nprint(res30)  # >>> ['0-0', [('1692629925790-0', {'rider': 'Sam-Bodden'})]]","section_id":"automatic-claiming"},{"id":"automatic-claiming-ex16","language":"ruby","code":"res30 = r.xautoclaim('race:italy', 'italy_riders', 'Lora', 0, res29['next'], count: 1)\nputs res30.inspect\n# >>> {\"next\" => \"0-0\", \"entries\"=>[[\"1692632662819-0\", {\"rider\" => \"Sam-Bodden\"}]]}","section_id":"automatic-claiming"},{"id":"automatic-claiming-ex17","language":"rust","code":"let next_opts = StreamAutoClaimOptions::default().count(1);\n        if let Ok(res) = r\n            .xautoclaim_options(\n                \"race:italy\",\n                \"italy_riders\",\n                \"Lora\",\n                1,\n                \"(1692632647899-0\",\n                next_opts,\n            )\n            .await\n        {\n            let res: redis::streams::StreamAutoClaimReply = res;\n            let claimed: Vec<_> = res\n                .claimed\n                .iter()\n                .map(|entry| {\n                    (\n                        entry.id.clone(),\n                        vec![(\n                            \"rider\".to_string(),\n                            entry.get::<String>(\"rider\").expect(\"missing rider\"),\n                        )],\n                    )\n                })\n                .collect();\n            println!(\"{:?}\", (res.next_stream_id.clone(), &claimed));\n            // >>> (\"0-0\", [(\"1692632662819-0\", [(\"rider\", \"Sam-Bodden\")])])\n        }","section_id":"automatic-claiming"},{"id":"automatic-claiming-ex18","language":"rust","code":"let next_opts = StreamAutoClaimOptions::default().count(1);\n        if let Ok(res) = r.xautoclaim_options(\n            \"race:italy\",\n            \"italy_riders\",\n            \"Lora\",\n            1,\n            \"(1692632647899-0\",\n            next_opts,\n        ) {\n            let res: redis::streams::StreamAutoClaimReply = res;\n            let claimed: Vec<_> = res\n                .claimed\n                .iter()\n                .map(|entry| {\n                    (\n                        entry.id.clone(),\n                        vec![(\n                            \"rider\".to_string(),\n                            entry.get::<String>(\"rider\").expect(\"missing rider\"),\n                        )],\n                    )\n                })\n                .collect();\n            println!(\"{:?}\", (res.next_stream_id.clone(), &claimed));\n            // >>> (\"0-0\", [(\"1692632662819-0\", [(\"rider\", \"Sam-Bodden\")])])\n        }","section_id":"automatic-claiming"},{"id":"releasing-messages-back-to-the-group-xnack-ex0","language":"plaintext","code":"> XNACK mystream mygroup FAIL IDS 1 1526569498055-0\n(integer) 1","section_id":"releasing-messages-back-to-the-group-xnack"},{"id":"streams-observability-ex0","language":"plaintext","code":"> XINFO STREAM race:italy\n 1) \"length\"\n 2) (integer) 5\n 3) \"radix-tree-keys\"\n 4) (integer) 1\n 5) \"radix-tree-nodes\"\n 6) (integer) 2\n 7) \"last-generated-id\"\n 8) \"1692632678249-0\"\n 9) \"groups\"\n10) (integer) 1\n11) \"first-entry\"\n12) 1) \"1692632639151-0\"\n    2) 1) \"rider\"\n       2) \"Castilla\"\n13) \"last-entry\"\n14) 1) \"1692632678249-0\"\n    2) 1) \"rider\"\n       2) \"Norem\"","section_id":"streams-observability"},{"id":"streams-observability-ex1","language":"csharp","code":"StreamInfo res32 = db.StreamInfo(\"race:italy\");\n        Console.WriteLine($\"length: {res32.Length}, radix-tree-keys: {res32.RadixTreeKeys}, radix-tree-nodes: {res32.RadixTreeNodes}, last-generated-id: {res32.LastGeneratedId}, first-entry: {$\"{res32.FirstEntry.Id}: [{string.Join(\", \", res32.FirstEntry.Values.Select(b => $\"{b.Name}: {b.Value}\"))}]\"}, last-entry: {$\"{res32.LastEntry.Id}: [{string.Join(\", \", res32.LastEntry.Values.Select(b => $\"{b.Name}: {b.Value}\"))}]\"}\");\n        // >>> length: 5, radix-tree-keys: 1, radix-tree-nodes: 2, last-generated-id: 1712756762686-1, first-entry: 1712756762685-0: [rider: Castilla], last-entry: 1712756762686-1: [rider: Norem]","section_id":"streams-observability"},{"id":"streams-observability-ex2","language":"go","code":"res33, err := rdb.XInfoStream(ctx, \"race:italy\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res33.Length)\n\t// >>> 5\n\tfmt.Println(res33.FirstEntry)\n\t// >>> {1692632639151-0 map[rider:Castilla] 0 0}","section_id":"streams-observability"},{"id":"streams-observability-ex3","language":"java","code":"StreamInfo res32 = jedis.xinfoStream(\"race:italy\");\n    System.out.println(\n      res32.getStreamInfo()\n    ); // >>> {radix-tree-keys=1, radix-tree-nodes=2, entries-added=5, length=5, groups=1, max-deleted-entry-id=0-0, first-entry=1701769637612-0 {rider=Castilaa}, last-generated-id=1701769637612-4, last-entry=1701769637612-4 {rider=Norem}, recorded-first-entry-id=1701769637612-0}","section_id":"streams-observability"},{"id":"streams-observability-ex4","language":"javascript","code":"const res31 = await client.xInfoStream('race:italy');\nconsole.log(res31); // >>> { length: 5, 'radix-tree-keys': 1, 'radix-tree-nodes': 2, 'last-generated-id': '1692629926436-0', 'max-deleted-entry-id': '0-0', 'entries-added': 5, 'recorded-first-entry-id': '1692629925771-0', groups: 1, 'first-entry': { id: '1692629925771-0', message: { rider: 'Castilla' } }, 'last-entry': { id: '1692629926436-0', message: { rider: 'Norem' } } }","section_id":"streams-observability"},{"id":"streams-observability-ex5","language":"python","code":"res31 = r.xinfo_stream(\"race:italy\")\nprint(\n    res31\n)\n# >>> {\n#       'length': 5, 'radix-tree-keys': 1, 'radix-tree-nodes': 2,\n#       'last-generated-id': '1692629926436-0', 'groups': 1,\n#       'first-entry': ('1692629925771-0', {'rider': 'Castilla'}),\n#       'last-entry': ('1692629926436-0', {'rider': 'Norem'})\n# }","section_id":"streams-observability"},{"id":"streams-observability-ex6","language":"ruby","code":"res31 = r.xinfo(:stream, 'race:italy')\nputs res31.inspect","section_id":"streams-observability"},{"id":"streams-observability-ex7","language":"rust","code":"if let Ok(res) = r.xinfo_stream(\"race:italy\").await {\n            let res: StreamInfoStreamReply = res;\n            let view = (\n                res.length,\n                res.radix_tree_keys,\n                res.groups,\n                res.last_generated_id.clone(),\n                res.first_entry.id.clone(),\n                res.last_entry.id.clone(),\n            );\n            println!(\"{view:?}\");\n            // >>> (5, 1, 1, \"1692632678249-0\", \"1692632639151-0\", \"1692632678249-0\")\n        }","section_id":"streams-observability"},{"id":"streams-observability-ex8","language":"rust","code":"if let Ok(res) = r.xinfo_stream(\"race:italy\") {\n            let res: StreamInfoStreamReply = res;\n            let view = (\n                res.length,\n                res.radix_tree_keys,\n                res.groups,\n                res.last_generated_id.clone(),\n                res.first_entry.id.clone(),\n                res.last_entry.id.clone(),\n            );\n            println!(\"{view:?}\");\n            // >>> (5, 1, 1, \"1692632678249-0\", \"1692632639151-0\", \"1692632678249-0\")\n        }","section_id":"streams-observability"},{"id":"streams-observability-ex9","language":"plaintext","code":"> XINFO GROUPS race:italy\n1) 1) \"name\"\n   2) \"italy_riders\"\n   3) \"consumers\"\n   4) (integer) 3\n   5) \"pending\"\n   6) (integer) 2\n   7) \"last-delivered-id\"\n   8) \"1692632662819-0\"","section_id":"streams-observability"},{"id":"streams-observability-ex10","language":"csharp","code":"StreamGroupInfo[] res33 = db.StreamGroupInfo(\"race:italy\");\n\n        foreach (StreamGroupInfo info in res33)\n        {\n            Console.WriteLine($\"name: {info.Name}, consumers: {info.ConsumerCount}, pending: {info.PendingMessageCount}, last-delivered-id: {info.LastDeliveredId}\");\n        }\n        // >>> name: italy_riders, consumers: 2, pending: 2, last-delivered-id: 1712757192730-2","section_id":"streams-observability"},{"id":"streams-observability-ex11","language":"go","code":"res34, err := rdb.XInfoGroups(ctx, \"race:italy\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res34)\n\t// >>> [{italy_riders 3 2 1692632662819-0 3 2}]","section_id":"streams-observability"},{"id":"streams-observability-ex12","language":"java","code":"List<StreamGroupInfo> res33 = jedis.xinfoGroups(\"race:italy\");\n    for (StreamGroupInfo a : res33){\n      System.out.println(\n        a.getGroupInfo()\n      ); // >>> {last-delivered-id=1701770253659-0, lag=2, pending=2, name=italy_riders, consumers=2, entries-read=3}\n    }","section_id":"streams-observability"},{"id":"streams-observability-ex13","language":"javascript","code":"const res32 = await client.xInfoGroups('race:italy');\nconsole.log(res32); // >>> [{ name: 'italy_riders', consumers: 2, pending: 3, 'last-delivered-id': '1692629925790-0', 'entries-read': 3, lag: 2 }]","section_id":"streams-observability"},{"id":"streams-observability-ex14","language":"python","code":"res32 = r.xinfo_groups(\"race:italy\")\nprint(\n    res32\n)\n# >>> [\n#       {\n#           'name': 'italy_riders', 'consumers': 2, 'pending': 2,\n#           'last-delivered-id': '1692629925790-0'\n#       }\n# ]","section_id":"streams-observability"},{"id":"streams-observability-ex15","language":"ruby","code":"res32 = r.xinfo(:groups, 'race:italy')\nputs res32.inspect","section_id":"streams-observability"},{"id":"streams-observability-ex16","language":"rust","code":"if let Ok(res) = r.xinfo_groups(\"race:italy\").await {\n            let res: StreamInfoGroupsReply = res;\n            let view: Vec<_> = res\n                .groups\n                .iter()\n                .map(|group| {\n                    (\n                        group.name.clone(),\n                        group.consumers,\n                        group.pending,\n                        group.last_delivered_id.clone(),\n                    )\n                })\n                .collect();\n            println!(\"{view:?}\");\n            // >>> [(\"italy_riders\", 3, 2, \"1692632662819-0\")]\n        }","section_id":"streams-observability"},{"id":"streams-observability-ex17","language":"rust","code":"if let Ok(res) = r.xinfo_groups(\"race:italy\") {\n            let res: StreamInfoGroupsReply = res;\n            let view: Vec<_> = res\n                .groups\n                .iter()\n                .map(|group| {\n                    (\n                        group.name.clone(),\n                        group.consumers,\n                        group.pending,\n                        group.last_delivered_id.clone(),\n                    )\n                })\n                .collect();\n            println!(\"{view:?}\");\n            // >>> [(\"italy_riders\", 3, 2, \"1692632662819-0\")]\n        }","section_id":"streams-observability"},{"id":"streams-observability-ex18","language":"plaintext","code":"> XINFO CONSUMERS race:italy italy_riders\n1) 1) \"name\"\n   2) \"Alice\"\n   3) \"pending\"\n   4) (integer) 1\n   5) \"idle\"\n   6) (integer) 177546\n2) 1) \"name\"\n   2) \"Bob\"\n   3) \"pending\"\n   4) (integer) 0\n   5) \"idle\"\n   6) (integer) 424686\n3) 1) \"name\"\n   2) \"Lora\"\n   3) \"pending\"\n   4) (integer) 1\n   5) \"idle\"\n   6) (integer) 72241","section_id":"streams-observability"},{"id":"streams-observability-ex19","language":"csharp","code":"StreamConsumerInfo[] res34 = db.StreamConsumerInfo(\"race:italy\", \"italy_riders\");\n\n        foreach (StreamConsumerInfo info in res34)\n        {\n            Console.WriteLine($\"name: {info.Name}, pending: {info.PendingMessageCount}, idle: {info.IdleTimeInMilliseconds}\");\n        }\n        // >>> name: Alice, pending: 1, idle: 7717\n        // >>> name: Bob, pending: 0, idle: 7722","section_id":"streams-observability"},{"id":"streams-observability-ex20","language":"go","code":"res35, err := rdb.XInfoConsumers(ctx, \"race:italy\", \"italy_riders\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// fmt.Println(res35)\n\t// >>> [{Alice 1 1ms 1ms} {Bob 1 2ms 2ms} {Lora 0 1ms -1ms}]","section_id":"streams-observability"},{"id":"streams-observability-ex21","language":"java","code":"List<StreamConsumersInfo> res34 = jedis.xinfoConsumers(\"race:italy\",\"italy_riders\");\n    for (StreamConsumerInfo a : res34){\n      System.out.println(\n        a.getConsumerInfo()\n      ); // {inactive=1, idle=1, pending=1, name=Alice} , {inactive=3, idle=3, pending=1, name=Bob}\n    }","section_id":"streams-observability"},{"id":"streams-observability-ex22","language":"javascript","code":"const res33 = await client.xInfoConsumers('race:italy', 'italy_riders');\nconsole.log(res33); // >>> [{ name: 'Alice', pending: 3, idle: 170582, inactive: 170582 }, { name: 'Bob', pending: 0, idle: 489404, inactive: 489404 }]","section_id":"streams-observability"},{"id":"streams-observability-ex23","language":"python","code":"res33 = r.xinfo_consumers(\"race:italy\", \"italy_riders\")\nprint(\n    res33\n)\n# >>> [\n#       {'name': 'Alice', 'pending': 2, 'idle': 199332},\n#       {'name': 'Bob', 'pending': 0, 'idle': 489170}\n# ]","section_id":"streams-observability"},{"id":"streams-observability-ex24","language":"ruby","code":"res33 = r.xinfo(:consumers, 'race:italy', 'italy_riders')\nputs res33.inspect","section_id":"streams-observability"},{"id":"streams-observability-ex25","language":"rust","code":"if let Ok(res) = r.xinfo_consumers(\"race:italy\", \"italy_riders\").await {\n            let res: StreamInfoConsumersReply = res;\n            let mut view: Vec<_> = res\n                .consumers\n                .iter()\n                .map(|consumer| (consumer.name.clone(), consumer.pending, consumer.idle))\n                .collect();\n            view.sort_by(|a, b| a.0.cmp(&b.0));\n            println!(\"{view:?}\");\n            // >>> [(\"Alice\", 1, 5), (\"Bob\", 0, 5), (\"Lora\", 1, 5)]\n        }","section_id":"streams-observability"},{"id":"streams-observability-ex26","language":"rust","code":"if let Ok(res) = r.xinfo_consumers(\"race:italy\", \"italy_riders\") {\n            let res: StreamInfoConsumersReply = res;\n            let mut view: Vec<_> = res\n                .consumers\n                .iter()\n                .map(|consumer| (consumer.name.clone(), consumer.pending, consumer.idle))\n                .collect();\n            view.sort_by(|a, b| a.0.cmp(&b.0));\n            println!(\"{view:?}\");\n            // >>> [(\"Alice\", 1, 5), (\"Bob\", 0, 5), (\"Lora\", 1, 5)]\n        }","section_id":"streams-observability"},{"id":"streams-observability-ex27","language":"plaintext","code":"> XINFO HELP\n1) XINFO <subcommand> [<arg> [value] [opt] ...]. Subcommands are:\n2) CONSUMERS <key> <groupname>\n3)     Show consumers of <groupname>.\n4) GROUPS <key>\n5)     Show the stream consumer groups.\n6) STREAM <key> [FULL [COUNT <count>]\n7)     Show information about the stream.\n8) HELP\n9)     Prints this help.","section_id":"streams-observability"},{"id":"capped-streams-ex0","language":"plaintext","code":"> XADD race:italy MAXLEN 2 * rider Jones\n\"1692633189161-0\"\n> XADD race:italy MAXLEN 2 * rider Wood\n\"1692633198206-0\"\n> XADD race:italy MAXLEN 2 * rider Henshaw\n\"1692633208557-0\"\n> XLEN race:italy\n(integer) 2\n> XRANGE race:italy - +\n1) 1) \"1692633198206-0\"\n   2) 1) \"rider\"\n      2) \"Wood\"\n2) 1) \"1692633208557-0\"\n   2) 1) \"rider\"\n      2) \"Henshaw\"","section_id":"capped-streams"},{"id":"capped-streams-ex1","language":"csharp","code":"db.StreamAdd(\n            \"race:italy\", [new(\"rider\", \"Jones\")], null, 2, true\n        );\n\n        db.StreamAdd(\n            \"race:italy\", [new(\"rider\", \"Wood\")], null, 2, true\n        );\n\n        db.StreamAdd(\n            \"race:italy\", [new(\"rider\", \"Henshaw\")], null, 2, true\n        );\n\n        long res35 = db.StreamLength(\"race:italy\");\n        Console.WriteLine(res35); // >>> 8\n\n        StreamEntry[] res36 = db.StreamRange(\"race:italy\", \"-\", \"+\");\n\n        foreach (StreamEntry entry in res36)\n        {\n            Console.WriteLine($\"{entry.Id}: [{string.Join(\", \", entry.Values.Select(b => $\"{b.Name}: {b.Value}\"))}]\");\n        }\n        // >>> 1712758336128-0: [rider: Castilla]\n        // >>> 1712758336128-1: [rider: Royce]\n        // >>> 1712758336128-2: [rider: Sam-Bodden]\n        // >>> 1712758336129-0: [rider: Prickett]\n        // >>> 1712758336139-0: [rider: Norem]\n        // >>> 1712758340854-0: [rider: Jones]\n        // >>> 1712758341645-0: [rider: Wood]\n        // >>> 1712758342134-0: [rider: Henshaw]\n\n        db.StreamAdd(\n            \"race:italy\", [new(\"rider\", \"Smith\")], null, 2, false\n        );\n\n        StreamEntry[] res37 = db.StreamRange(\"race:italy\", \"-\", \"+\");\n\n        foreach (StreamEntry entry in res37)\n        {\n            Console.WriteLine($\"{entry.Id}: [{string.Join(\", \", entry.Values.Select(b => $\"{b.Name}: {b.Value}\"))}]\");\n        }\n        // 1712758746476-1: [rider: Henshaw]\n        // 1712758746477-0: [rider: Smith]","section_id":"capped-streams"},{"id":"capped-streams-ex2","language":"go","code":"_, err = rdb.XAdd(ctx, &redis.XAddArgs{\n\t\tStream: \"race:italy\",\n\t\tMaxLen: 2,\n\t\tValues: map[string]interface{}{\"rider\": \"Jones\"},\n\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t_, err = rdb.XAdd(ctx, &redis.XAddArgs{\n\t\tStream: \"race:italy\",\n\t\tMaxLen: 2,\n\t\tValues: map[string]interface{}{\"rider\": \"Wood\"},\n\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t_, err = rdb.XAdd(ctx, &redis.XAddArgs{\n\t\tStream: \"race:italy\",\n\t\tMaxLen: 2,\n\t\tValues: map[string]interface{}{\"rider\": \"Henshaw\"},\n\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tres36, err := rdb.XLen(ctx, \"race:italy\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res36) // >>> 2\n\n\tres37, err := rdb.XRange(ctx, \"race:italy\", \"-\", \"+\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// fmt.Println(res37)\n\t// >>> [{1726649529170-1 map[rider:Wood] 0 0} {1726649529171-0 map[rider:Henshaw] 0 0}]","section_id":"capped-streams"},{"id":"capped-streams-ex3","language":"java","code":"jedis.xadd(\"race:italy\", new HashMap<String,String>(){{put(\"rider\",\"Jones\");}},XAddParams.xAddParams().maxLen(10));\n    jedis.xadd(\"race:italy\", new HashMap<String,String>(){{put(\"rider\",\"Wood\");}},XAddParams.xAddParams().maxLen(10));\n    jedis.xadd(\"race:italy\", new HashMap<String,String>(){{put(\"rider\",\"Henshaw\");}},XAddParams.xAddParams().maxLen(10));\n    long res35 = jedis.xlen(\"race:italy\");\n    System.out.println(res35); // >>> 8\n\n    List<StreamEntry> res36 = jedis.xrange(\"race:italy\",\"-\",\"+\");\n    System.out.println(res36); // >>> [1701771219852-0 {rider=Castilaa}, 1701771219852-1 {rider=Royce}, 1701771219853-0 {rider=Sam-Bodden}, 1701771219853-1 {rider=Prickett}, 1701771219853-2 {rider=Norem}, 1701771219858-0 {rider=Jones}, 1701771219858-1 {rider=Wood}, 1701771219859-0 {rider=Henshaw}]\n\n    StreamEntryID id6 = jedis.xadd(\"race:italy\", new HashMap<String,String>(){{put(\"rider\",\"Smith\");}},XAddParams.xAddParams().maxLen(2));\n\n    List<StreamEntry> res37 = jedis.xrange(\"race:italy\",\"-\",\"+\");\n    System.out.println(res37); // >>> [1701771067332-1 {rider=Henshaw}, 1701771067332-2 {rider=Smith}]","section_id":"capped-streams"},{"id":"capped-streams-ex4","language":"javascript","code":"await client.xAdd('race:italy', '*', {\n  'rider': 'Jones'\n}, {\n  TRIM: {\n    strategy: 'MAXLEN',\n    strategyModifier: '~',\n    threshold: 2\n  }\n});\nawait client.xAdd('race:italy', '*', {\n  'rider': 'Wood'\n}, {\n  TRIM: {\n    strategy: 'MAXLEN',\n    strategyModifier: '~',\n    threshold: 2\n  }\n});\nawait client.xAdd('race:italy', '*', {\n  'rider': 'Henshaw'\n}, {\n  TRIM: {\n    strategy: 'MAXLEN',\n    strategyModifier: '~',\n    threshold: 2\n  }\n});\n\nconst res34 = await client.xLen('race:italy');\nconsole.log(res34); // >>> 8\n\nconst res35 = await client.xRange('race:italy', '-', '+');\nconsole.log(res35); // >>> [{ id: '1692629925771-0', message: { rider: 'Castilla' } }, { id: '1692629925789-0', message: { rider: 'Royce' } }, { id: '1692629925790-0', message: { rider: 'Sam-Bodden' } }, { id: '1692629925791-0', message: { rider: 'Prickett' } }, { id: '1692629926436-0', message: { rider: 'Norem' } }, { id: '1692630612602-0', message: { rider: 'Jones' } }, { id: '1692630641947-0', message: { rider: 'Wood' } }, { id: '1692630648281-0', message: { rider: 'Henshaw' } }]\n\nawait client.xAdd('race:italy', '*', {\n  'rider': 'Smith'\n}, {\n  TRIM: {\n    strategy: 'MAXLEN',\n    strategyModifier: '=',\n    threshold: 2\n  }\n});\n\nconst res36 = await client.xRange('race:italy', '-', '+');\nconsole.log(res36); // >>> [{ id: '1692630648281-0', message: { rider: 'Henshaw' } }, { id: '1692631018238-0', message: { rider: 'Smith' } }]","section_id":"capped-streams"},{"id":"capped-streams-ex5","language":"python","code":"r.xadd(\"race:italy\", {\"rider\": \"Jones\"}, maxlen=2)\nr.xadd(\"race:italy\", {\"rider\": \"Wood\"}, maxlen=2)\nr.xadd(\"race:italy\", {\"rider\": \"Henshaw\"}, maxlen=2)\n\nres34 = r.xlen(\"race:italy\")\nprint(res34)  # >>> 8\n\nres35 = r.xrange(\"race:italy\", \"-\", \"+\")\nprint(\n    res35\n)\n# >>> [\n#       ('1692629925771-0', {'rider': 'Castilla'}),\n#       ('1692629925789-0', {'rider': 'Royce'}),\n#       ('1692629925790-0', {'rider': 'Sam-Bodden'}),\n#       ('1692629925791-0', {'rider': 'Prickett'}),\n#       ('1692629926436-0', {'rider': 'Norem'}),\n#       ('1692630612602-0', {'rider': 'Jones'}),\n#       ('1692630641947-0', {'rider': 'Wood'}),\n#       ('1692630648281-0', {'rider': 'Henshaw'})\n# ]\n\nr.xadd(\"race:italy\", {\"rider\": \"Smith\"}, maxlen=2, approximate=False)\n\nres36 = r.xrange(\"race:italy\", \"-\", \"+\")\nprint(\n    res36\n)\n# >>> [\n#       ('1692630648281-0', {'rider': 'Henshaw'}),\n#       ('1692631018238-0', {'rider': 'Smith'})\n# ]","section_id":"capped-streams"},{"id":"capped-streams-ex6","language":"ruby","code":"r.del('race:italy')\nr.xadd('race:italy', {'rider' => 'Castilla'}, id: '1692632639151-0')\nr.xadd('race:italy', {'rider' => 'Royce'}, id: '1692632647899-0')\nr.xadd('race:italy', {'rider' => 'Sam-Bodden'}, id: '1692632662819-0')\nr.xadd('race:italy', {'rider' => 'Prickett'}, id: '1692632670501-0')\nr.xadd('race:italy', {'rider' => 'Norem'}, id: '1692632678249-0')\nr.xadd('race:italy', {'rider' => 'Jones'}, id: '1692633189161-0', maxlen: 2)\nr.xadd('race:italy', {'rider' => 'Wood'}, id: '1692633198206-0', maxlen: 2)\nr.xadd('race:italy', {'rider' => 'Henshaw'}, id: '1692633208557-0', maxlen: 2)\n\nres34 = r.xlen('race:italy')\nputs res34 # >>> 2\n\nres35 = r.xrange('race:italy', '-', '+')\nputs res35.inspect\n# >>> [[\"1692633198206-0\", {\"rider\" => \"Wood\"}], [\"1692633208557-0\", {\"rider\" => \"Henshaw\"}]]","section_id":"capped-streams"},{"id":"capped-streams-ex7","language":"rust","code":"delete_keys(&mut r, &[\"race:italy\"]).await;\n        let max1: Option<String> = r\n            .xadd_maxlen(\"race:italy\", StreamMaxlen::Equals(2), \"1-0\", &[(\"rider\", \"Jones\")])\n            .await\n            .expect(\"maxlen add 1\");\n        let max1 = max1.expect(\"missing stream id\");\n        println!(\"{max1}\"); // >>> 1-0\n        let max2: Option<String> = r\n            .xadd_maxlen(\"race:italy\", StreamMaxlen::Equals(2), \"2-0\", &[(\"rider\", \"Wood\")])\n            .await\n            .expect(\"maxlen add 2\");\n        let max2 = max2.expect(\"missing stream id\");\n        println!(\"{max2}\"); // >>> 2-0\n        let max3: Option<String> = r\n            .xadd_maxlen(\"race:italy\", StreamMaxlen::Equals(2), \"3-0\", &[(\"rider\", \"Henshaw\")])\n            .await\n            .expect(\"maxlen add 3\");\n        let max3 = max3.expect(\"missing stream id\");\n        println!(\"{max3}\"); // >>> 3-0\n        if let Ok(res) = r.xlen(\"race:italy\").await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 2\n        }\n        if let Ok(res) = r.xrange_all(\"race:italy\").await {\n            let res: StreamRangeReply = res;\n            let view: Vec<_> = res\n                .ids\n                .iter()\n                .map(|entry| {\n                    (\n                        entry.id.clone(),\n                        vec![(\n                            \"rider\".to_string(),\n                            entry.get::<String>(\"rider\").expect(\"missing rider\"),\n                        )],\n                    )\n                })\n                .collect();\n            println!(\"{view:?}\");\n            // >>> [(\"2-0\", [(\"rider\", \"Wood\")]), (\"3-0\", [(\"rider\", \"Henshaw\")])]\n        }","section_id":"capped-streams"},{"id":"capped-streams-ex8","language":"rust","code":"delete_keys(&mut r, &[\"race:italy\"]);\n        let max1: Option<String> = r\n            .xadd_maxlen(\"race:italy\", StreamMaxlen::Equals(2), \"1-0\", &[(\"rider\", \"Jones\")])\n            .expect(\"maxlen add 1\");\n        let max1 = max1.expect(\"missing stream id\");\n        println!(\"{max1}\"); // >>> 1-0\n        let max2: Option<String> = r\n            .xadd_maxlen(\"race:italy\", StreamMaxlen::Equals(2), \"2-0\", &[(\"rider\", \"Wood\")])\n            .expect(\"maxlen add 2\");\n        let max2 = max2.expect(\"missing stream id\");\n        println!(\"{max2}\"); // >>> 2-0\n        let max3: Option<String> = r\n            .xadd_maxlen(\"race:italy\", StreamMaxlen::Equals(2), \"3-0\", &[(\"rider\", \"Henshaw\")])\n            .expect(\"maxlen add 3\");\n        let max3 = max3.expect(\"missing stream id\");\n        println!(\"{max3}\"); // >>> 3-0\n\n        if let Ok(res) = r.xlen(\"race:italy\") {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 2\n        }\n\n        if let Ok(res) = r.xrange_all(\"race:italy\") {\n            let res: StreamRangeReply = res;\n            let view: Vec<_> = res\n                .ids\n                .iter()\n                .map(|entry| {\n                    (\n                        entry.id.clone(),\n                        vec![(\n                            \"rider\".to_string(),\n                            entry.get::<String>(\"rider\").expect(\"missing rider\"),\n                        )],\n                    )\n                })\n                .collect();\n            println!(\"{view:?}\");\n            // >>> [(\"2-0\", [(\"rider\", \"Wood\")]), (\"3-0\", [(\"rider\", \"Henshaw\")])]\n        }","section_id":"capped-streams"},{"id":"capped-streams-ex9","language":"plaintext","code":"XADD race:italy MAXLEN ~ 1000 * ... entry fields here ...","section_id":"capped-streams"},{"id":"capped-streams-ex10","language":"plaintext","code":"> XTRIM race:italy MAXLEN 10\n(integer) 0","section_id":"capped-streams"},{"id":"capped-streams-ex11","language":"csharp","code":"long res38 = db.StreamTrim(\"race:italy\", 10, false);\n        Console.WriteLine(res38);   // >>> 0","section_id":"capped-streams"},{"id":"capped-streams-ex12","language":"go","code":"res38, err := rdb.XTrimMaxLen(ctx, \"race:italy\", 10).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res38) // >>> 0","section_id":"capped-streams"},{"id":"capped-streams-ex13","language":"java","code":"long res38 = jedis.xtrim(\"race:italy\",XTrimParams.xTrimParams().maxLen(10).exactTrimming());\n    System.out.println(res38); /// >>> 0","section_id":"capped-streams"},{"id":"capped-streams-ex14","language":"javascript","code":"const res37 = await client.xTrim('race:italy', 'MAXLEN', 10, {\n  strategyModifier: '=',\n});\nconsole.log(res37); // >>> 0","section_id":"capped-streams"},{"id":"capped-streams-ex15","language":"python","code":"res37 = r.xtrim(\"race:italy\", maxlen=10, approximate=False)\nprint(res37)  # >>> 0","section_id":"capped-streams"},{"id":"capped-streams-ex16","language":"ruby","code":"res36 = r.xtrim('race:italy', 10, approximate: false)\nputs res36 # >>> 0","section_id":"capped-streams"},{"id":"capped-streams-ex17","language":"rust","code":"delete_keys(&mut r, &[\"race:italy\"]).await;\n        let _: Option<String> = r.xadd(\"race:italy\", \"1-0\", &[(\"rider\", \"Wood\")]).await.expect(\"trim seed 1\");\n        let _: Option<String> = r.xadd(\"race:italy\", \"2-0\", &[(\"rider\", \"Henshaw\")]).await.expect(\"trim seed 2\");\n        if let Ok(res) = r.xtrim(\"race:italy\", StreamMaxlen::Equals(10)).await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 0\n        }","section_id":"capped-streams"},{"id":"capped-streams-ex18","language":"rust","code":"delete_keys(&mut r, &[\"race:italy\"]);\n        let _: Option<String> = r.xadd(\"race:italy\", \"1-0\", &[(\"rider\", \"Wood\")]).expect(\"trim seed 1\");\n        let _: Option<String> = r.xadd(\"race:italy\", \"2-0\", &[(\"rider\", \"Henshaw\")]).expect(\"trim seed 2\");\n        if let Ok(res) = r.xtrim(\"race:italy\", StreamMaxlen::Equals(10)) {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 0\n        }","section_id":"capped-streams"},{"id":"capped-streams-ex19","language":"plaintext","code":"> XTRIM mystream MAXLEN ~ 10\n(integer) 0","section_id":"capped-streams"},{"id":"capped-streams-ex20","language":"csharp","code":"long res39 = db.StreamTrim(\"race:italy\", 10, true);\n        Console.WriteLine(res39);   // >>> 0","section_id":"capped-streams"},{"id":"capped-streams-ex21","language":"go","code":"res39, err := rdb.XTrimMaxLenApprox(ctx, \"race:italy\", 10, 20).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res39) // >>> 0","section_id":"capped-streams"},{"id":"capped-streams-ex22","language":"java","code":"long res39 = jedis.xtrim(\"race:italy\",XTrimParams.xTrimParams().maxLen(10));\n    System.out.println(res39); /// >>> 0","section_id":"capped-streams"},{"id":"capped-streams-ex23","language":"javascript","code":"const res38 = await client.xTrim('race:italy', \"MAXLEN\", 10);\nconsole.log(res38); // >>> 0","section_id":"capped-streams"},{"id":"capped-streams-ex24","language":"python","code":"res38 = r.xtrim(\"race:italy\", maxlen=10)\nprint(res38)  # >>> 0","section_id":"capped-streams"},{"id":"capped-streams-ex25","language":"ruby","code":"r.del('mystream')\n1.upto(10) do |n|\n  r.xadd('mystream', {'field' => 'value'}, id: \"#{n}-0\")\nend\nres37 = r.xtrim('mystream', 10, approximate: true)\nputs res37 # >>> 0","section_id":"capped-streams"},{"id":"capped-streams-ex26","language":"rust","code":"seed_trim_stream(&mut r).await;\n        if let Ok(res) = r\n            .xtrim_options(\n                \"mystream\",\n                &StreamTrimOptions::maxlen(StreamTrimmingMode::Approx, 10),\n            )\n            .await\n        {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 0\n        }","section_id":"capped-streams"},{"id":"capped-streams-ex27","language":"rust","code":"seed_trim_stream(&mut r);\n        if let Ok(res) = r.xtrim_options(\n            \"mystream\",\n            &StreamTrimOptions::maxlen(StreamTrimmingMode::Approx, 10),\n        ) {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 0\n        }","section_id":"capped-streams"},{"id":"trimming-with-consumer-group-awareness-ex0","language":"plaintext","code":"XADD mystream KEEPREF MAXLEN 1000 * field value\nXTRIM mystream ACKED MAXLEN 1000","section_id":"trimming-with-consumer-group-awareness"},{"id":"removing-single-items-from-a-stream-ex0","language":"plaintext","code":"> XRANGE race:italy - + COUNT 2\n1) 1) \"1692633198206-0\"\n   2) 1) \"rider\"\n      2) \"Wood\"\n2) 1) \"1692633208557-0\"\n   2) 1) \"rider\"\n      2) \"Henshaw\"\n> XDEL race:italy 1692633208557-0\n(integer) 1\n> XRANGE race:italy - + COUNT 2\n1) 1) \"1692633198206-0\"\n   2) 1) \"rider\"\n      2) \"Wood\"","section_id":"removing-single-items-from-a-stream"},{"id":"removing-single-items-from-a-stream-ex1","language":"csharp","code":"StreamEntry[] res40 = db.StreamRange(\"race:italy\", \"-\", \"+\");\n\n        foreach (StreamEntry entry in res40)\n        {\n            Console.WriteLine($\"{entry.Id}: [{string.Join(\", \", entry.Values.Select(b => $\"{b.Name}: {b.Value}\"))}]\");\n        }\n        // >>> 1712759694003-0: [rider: Henshaw]\n        // >>> 1712759694003-1: [rider: Smith]\n\n        long res41 = db.StreamDelete(\"race:italy\", [\"1712759694003-1\"]);\n        Console.WriteLine(res41);   // >>> 1\n\n        StreamEntry[] res42 = db.StreamRange(\"race:italy\", \"-\", \"+\");\n\n        foreach (StreamEntry entry in res42)\n        {\n            Console.WriteLine($\"{entry.Id}: [{string.Join(\", \", entry.Values.Select(b => $\"{b.Name}: {b.Value}\"))}]\");\n\n        }\n        // >>> 1712759694003-0: [rider: Henshaw]","section_id":"removing-single-items-from-a-stream"},{"id":"removing-single-items-from-a-stream-ex2","language":"go","code":"res39, err := rdb.XRangeN(ctx, \"race:italy\", \"-\", \"+\", 2).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res39)\n\t// >>> [{1692633198206-0 map[rider:Wood] 0 0} {1692633208557-0 map[rider:Henshaw] 0 0}]\n\n\tres40, err := rdb.XDel(ctx, \"race:italy\", \"1692633208557-0\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res40) // 1\n\n\tres41, err := rdb.XRangeN(ctx, \"race:italy\", \"-\", \"+\", 2).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res41)\n\t// >>> [{1692633198206-0 map[rider:Wood] 0 0}]","section_id":"removing-single-items-from-a-stream"},{"id":"removing-single-items-from-a-stream-ex3","language":"java","code":"List<StreamEntry> res40 = jedis.xrange(\"race:italy\",\"-\",\"+\");\n    System.out.println(res40); // >>> [1701771356428-2 {rider=Henshaw}, 1701771356429-0 {rider=Smith}]\n\n    long res41 = jedis.xdel(\"race:italy\",id6);\n    System.out.println(res41); // >>> 1\n\n    List<StreamEntry> res42 = jedis.xrange(\"race:italy\",\"-\",\"+\");\n    System.out.println(res42); // >>> [1701771517639-1 {rider=Henshaw}]","section_id":"removing-single-items-from-a-stream"},{"id":"removing-single-items-from-a-stream-ex4","language":"javascript","code":"const res39 = await client.xRange('race:italy', '-', '+');\nconsole.log(res39); // >>> [{ id: '1692630648281-0', message: { rider: 'Henshaw' } }, { id: '1692631018238-0', message: { rider: 'Smith' } }]\n\nconst res40 = await client.xDel('race:italy', '1692631018238-0');\nconsole.log(res40); // >>> 1\n\nconst res41 = await client.xRange('race:italy', '-', '+');\nconsole.log(res41); // >>> [{ id: '1692630648281-0', message: { rider: 'Henshaw' } }]","section_id":"removing-single-items-from-a-stream"},{"id":"removing-single-items-from-a-stream-ex5","language":"python","code":"res39 = r.xrange(\"race:italy\", \"-\", \"+\")\nprint(\n    res39\n)\n# >>> [\n#       ('1692630648281-0', {'rider': 'Henshaw'}),\n#       ('1692631018238-0', {'rider': 'Smith'})\n# ]\n\nres40 = r.xdel(\"race:italy\", \"1692631018238-0\")\nprint(res40)  # >>> 1\n\nres41 = r.xrange(\"race:italy\", \"-\", \"+\")\nprint(res41)  # >>> [('1692630648281-0', {'rider': 'Henshaw'})]","section_id":"removing-single-items-from-a-stream"},{"id":"removing-single-items-from-a-stream-ex6","language":"ruby","code":"r.del('race:italy')\nr.xadd('race:italy', {'rider' => 'Wood'}, id: '1692633198206-0')\nr.xadd('race:italy', {'rider' => 'Henshaw'}, id: '1692633208557-0')\nres38 = r.xrange('race:italy', '-', '+', count: 2)\nputs res38.inspect\n# >>> [[\"1692633198206-0\", {\"rider\" => \"Wood\"}], [\"1692633208557-0\", {\"rider\" => \"Henshaw\"}]]\n\nres39 = r.xdel('race:italy', '1692633208557-0')\nputs res39 # >>> 1\n\nres40 = r.xrange('race:italy', '-', '+', count: 2)\nputs res40.inspect\n# >>> [[\"1692633198206-0\", {\"rider\" => \"Wood\"}]]","section_id":"removing-single-items-from-a-stream"},{"id":"removing-single-items-from-a-stream-ex7","language":"rust","code":"delete_keys(&mut r, &[\"race:italy\"]).await;\n        let _: Option<String> = r.xadd(\"race:italy\", \"2-0\", &[(\"rider\", \"Wood\")]).await.expect(\"xdel seed 1\");\n        let _: Option<String> = r.xadd(\"race:italy\", \"3-0\", &[(\"rider\", \"Henshaw\")]).await.expect(\"xdel seed 2\");\n        if let Ok(res) = r.xrange_count(\"race:italy\", \"-\", \"+\", 2).await {\n            let res: StreamRangeReply = res;\n            let view: Vec<_> = res\n                .ids\n                .iter()\n                .map(|entry| {\n                    (\n                        entry.id.clone(),\n                        vec![(\n                            \"rider\".to_string(),\n                            entry.get::<String>(\"rider\").expect(\"missing rider\"),\n                        )],\n                    )\n                })\n                .collect();\n            println!(\"{view:?}\"); // >>> [(\"2-0\", [(\"rider\", \"Wood\")]), (\"3-0\", [(\"rider\", \"Henshaw\")])]\n        }\n        if let Ok(res) = r.xdel(\"race:italy\", &[\"3-0\"]).await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 1\n        }\n        if let Ok(res) = r.xrange_count(\"race:italy\", \"-\", \"+\", 2).await {\n            let res: StreamRangeReply = res;\n            let view: Vec<_> = res\n                .ids\n                .iter()\n                .map(|entry| {\n                    (\n                        entry.id.clone(),\n                        vec![(\n                            \"rider\".to_string(),\n                            entry.get::<String>(\"rider\").expect(\"missing rider\"),\n                        )],\n                    )\n                })\n                .collect();\n            println!(\"{view:?}\"); // >>> [(\"2-0\", [(\"rider\", \"Wood\")])]\n        }","section_id":"removing-single-items-from-a-stream"},{"id":"removing-single-items-from-a-stream-ex8","language":"rust","code":"delete_keys(&mut r, &[\"race:italy\"]);\n        let _: Option<String> = r.xadd(\"race:italy\", \"2-0\", &[(\"rider\", \"Wood\")]).expect(\"xdel seed 1\");\n        let _: Option<String> = r.xadd(\"race:italy\", \"3-0\", &[(\"rider\", \"Henshaw\")]).expect(\"xdel seed 2\");\n        if let Ok(res) = r.xrange_count(\"race:italy\", \"-\", \"+\", 2) {\n            let res: StreamRangeReply = res;\n            let view: Vec<_> = res\n                .ids\n                .iter()\n                .map(|entry| {\n                    (\n                        entry.id.clone(),\n                        vec![(\n                            \"rider\".to_string(),\n                            entry.get::<String>(\"rider\").expect(\"missing rider\"),\n                        )],\n                    )\n                })\n                .collect();\n            println!(\"{view:?}\"); // >>> [(\"2-0\", [(\"rider\", \"Wood\")]), (\"3-0\", [(\"rider\", \"Henshaw\")])]\n        }\n\n        if let Ok(res) = r.xdel(\"race:italy\", &[\"3-0\"]) {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 1\n        }\n\n        if let Ok(res) = r.xrange_count(\"race:italy\", \"-\", \"+\", 2) {\n            let res: StreamRangeReply = res;\n            let view: Vec<_> = res\n                .ids\n                .iter()\n                .map(|entry| {\n                    (\n                        entry.id.clone(),\n                        vec![(\n                            \"rider\".to_string(),\n                            entry.get::<String>(\"rider\").expect(\"missing rider\"),\n                        )],\n                    )\n                })\n                .collect();\n            println!(\"{view:?}\"); // >>> [(\"2-0\", [(\"rider\", \"Wood\")])]\n        }","section_id":"removing-single-items-from-a-stream"},{"id":"enhanced-deletion-with-xdelex-ex0","language":"plaintext","code":"XDELEX mystream ACKED IDS 2 1692633198206-0 1692633208557-0","section_id":"enhanced-deletion-with-xdelex"},{"id":"latency-tests-results-ex0","language":"plaintext","code":"Processed between 0 and 1 ms -> 74.11%\nProcessed between 1 and 2 ms -> 25.80%\nProcessed between 2 and 3 ms -> 0.06%\nProcessed between 3 and 4 ms -> 0.01%\nProcessed between 4 and 5 ms -> 0.02%","section_id":"latency-tests-results"}]}
{"schema_version":2,"id":"develop/data-types/strings/bitfields","title":"Redis bitfields","url":"https://redis.io/docs/latest/develop/data-types/strings/bitfields/","summary":"Introduction to Redis bitfields","aliases":["/data-types/bitfields/","/manual/data-types/bitfields/","/develop/data-types/bitfields/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-10T15:45:58+01:00","page_type":"content","content_hash":"2882b3f22d089935aa07765c22b8b92054f581321b8dc728f9e176693f2eccb8","sections":[{"id":"bitmapbitfield-command-summary","title":"Bitmap/bitfield command summary","role":"content","text":"**7 commands in this group:**\n\n[View all bitmap commands](https://redis.io/commands/?group=bitmap)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [BITCOUNT](https://redis.io/commands/bitcount/) | Counts the number of set bits (population counting) in a string. | O(N) | 2.6.0 |\n| [BITFIELD](https://redis.io/commands/bitfield/) | Performs arbitrary bitfield integer operations on strings. | O(1) for each subcommand specified | 3.2.0 |\n| [BITFIELD_RO](https://redis.io/commands/bitfield_ro/) | Performs arbitrary read-only bitfield integer operations on strings. | O(1) for each subcommand specified | 6.0.0 |\n| [BITOP](https://redis.io/commands/bitop/) | Performs bitwise operations on multiple strings, and stores the result. | O(N) | 2.6.0 |\n| [BITPOS](https://redis.io/commands/bitpos/) | Finds the first set (1) or clear (0) bit in a string. | O(N) | 2.8.7 |\n| [GETBIT](https://redis.io/commands/getbit/) | Returns a bit value by offset. | O(1) | 2.2.0 |\n| [SETBIT](https://redis.io/commands/setbit/) | Sets or clears the bit at offset of the string value. Creates the key if it doesn't exist. | O(1) | 2.2.0 |\n\n\n\nRedis bitfields let you set, increment, and get integer values of arbitrary bit length.\nFor example, you can operate on anything from unsigned 1-bit integers to signed 63-bit integers.\n\nThese values are stored using binary-encoded Redis strings.\nBitfields support atomic read, write and increment operations, making them a good choice for managing counters and similar numerical values."},{"id":"example","title":"Example","role":"example","text":"Suppose you want to maintain two metrics for various bicycles: the current price and the number of owners over time. You can represent these counters with a 32-bit wide bitfield for each bike.\n\n* Bike 1 initially costs 1,000 (counter in offset 0) and has never had an owner. After being sold, it's now considered used and the price instantly drops to reflect its new condition, and it now has an owner (offset 1). After quite some time, the bike becomes a classic. The original owner sells it for a profit, so the price goes up and the number of owners does as well.Finally, you can look at the bike's current price and number of owners.\n\nBitfield operations: Use BITFIELD to atomically set, increment, and get integer values of arbitrary bit length when you need to manage multiple counters efficiently\n\n**Difficulty:** Intermediate\n\n**Commands:** BITFIELD\n\n**Complexity:**\n- BITFIELD: O(1)\n\n**Available in:** Redis CLI, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]"},{"id":"performance","title":"Performance","role":"performance","text":"[`BITFIELD`](https://redis.io/docs/latest/commands/bitfield) is O(n), where _n_ is the number of counters accessed."}],"examples":[{"id":"example-ex0","language":"plaintext","code":"> BITFIELD bike:1:stats SET u32 #0 1000\n1) (integer) 0\n> BITFIELD bike:1:stats INCRBY u32 #0 -50 INCRBY u32 #1 1\n1) (integer) 950\n2) (integer) 1\n> BITFIELD bike:1:stats INCRBY u32 #0 500 INCRBY u32 #1 1\n1) (integer) 1450\n2) (integer) 2\n> BITFIELD bike:1:stats GET u32 #0 GET u32 #1\n1) (integer) 1450\n2) (integer) 2","section_id":"example"},{"id":"example-ex1","language":"go","code":"res1, err := rdb.BitField(ctx, \"bike:1:stats\",\n\t\t\"set\", \"u32\", \"#0\", \"1000\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> [0]\n\n\tres2, err := rdb.BitField(ctx,\n\t\t\"bike:1:stats\",\n\t\t\"incrby\", \"u32\", \"#0\", \"-50\",\n\t\t\"incrby\", \"u32\", \"#1\", \"1\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> [950 1]\n\n\tres3, err := rdb.BitField(ctx,\n\t\t\"bike:1:stats\",\n\t\t\"incrby\", \"u32\", \"#0\", \"500\",\n\t\t\"incrby\", \"u32\", \"#1\", \"1\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3) // >>> [1450 2]\n\n\tres4, err := rdb.BitField(ctx, \"bike:1:stats\",\n\t\t\"get\", \"u32\", \"#0\",\n\t\t\"get\", \"u32\", \"#1\",\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4) // >>> [1450 2]","section_id":"example"},{"id":"example-ex2","language":"java","code":"List<Long> res1 = jedis.bitfield(\"bike:1:stats\", \"SET\", \"u32\", \"#0\", \"1000\");\n        System.out.println(res1);   // >>> [0]\n\n        List<Long> res2 = jedis.bitfield(\"bike:1:stats\", \"INCRBY\", \"u32\", \"#0\", \"-50\", \"INCRBY\", \"u32\", \"#1\", \"1\");\n        System.out.println(res2);   // >>> [950, 1]\n\n        List<Long> res3 = jedis.bitfield(\"bike:1:stats\", \"INCRBY\", \"u32\", \"#0\", \"500\", \"INCRBY\", \"u32\", \"#1\", \"1\");\n        System.out.println(res3);   // >>> [1450, 2]\n\n        List<Long> res4 = jedis.bitfield(\"bike:1:stats\", \"GET\", \"u32\", \"#0\", \"GET\", \"u32\", \"#1\");\n        System.out.println(res4);   // >>> [1450, 2]","section_id":"example"},{"id":"example-ex3","language":"javascript","code":"let res1 = await client.bitField(\"bike:1:stats\", [{\n  operation: 'SET',\n  encoding: 'u32',\n  offset: '#0',\n  value: 1000\n}]);\nconsole.log(res1);  // >>> [0]\n\nlet res2 = await client.bitField('bike:1:stats', [\n  {\n    operation: 'INCRBY',\n    encoding: 'u32',\n    offset: '#0',\n    increment: -50\n  },\n  {\n    operation: 'INCRBY',\n    encoding: 'u32',\n    offset: '#1',\n    increment: 1\n  }\n]);\nconsole.log(res2);  // >>> [950, 1]\n\nlet res3 = await client.bitField('bike:1:stats', [\n  {\n    operation: 'INCRBY',\n    encoding: 'u32',\n    offset: '#0',\n    increment: 500\n  },\n  {\n    operation: 'INCRBY',\n    encoding: 'u32',\n    offset: '#1',\n    increment: 1\n  }\n]);\nconsole.log(res3);  // >>> [1450, 2]\n\nlet res4 = await client.bitField('bike:1:stats', [\n  {\n    operation: 'GET',\n    encoding: 'u32',\n    offset: '#0'\n  },\n  {\n    operation: 'GET',\n    encoding: 'u32',\n    offset: '#1'\n  }\n]);\nconsole.log(res4);  // >>> [1450, 2]","section_id":"example"},{"id":"example-ex4","language":"python","code":"bf = r.bitfield(\"bike:1:stats\")\nres1 = bf.set(\"u32\", \"#0\", 1000).execute()\nprint(res1)  # >>> [0]\n\nres2 = bf.incrby(\"u32\", \"#0\", -50).incrby(\"u32\", \"#1\", 1).execute()\nprint(res2)  # >>> [950, 1]\n\nres3 = bf.incrby(\"u32\", \"#0\", 500).incrby(\"u32\", \"#1\", 1).execute()\nprint(res3)  # >>> [1450, 2]\n\nres4 = bf.get(\"u32\", \"#0\").get(\"u32\", \"#1\").execute()\nprint(res4)  # >>> [1450, 2]","section_id":"example"}]}
{"schema_version":2,"id":"develop/data-types/strings/bitmaps","title":"Redis bitmaps","url":"https://redis.io/docs/latest/develop/data-types/strings/bitmaps/","summary":"Introduction to Redis bitmaps","aliases":["/data-types/bitmaps/","/manual/data-types/bitmaps/","/develop/data-types/bitmaps/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-29T16:29:37+01:00","page_type":"content","content_hash":"de4dc324890700b63f6d805e3f78237703e983af5add0f0d0835fac8c1ce82e8","sections":[{"id":"bitmapbitfield-command-summary","title":"Bitmap/bitfield command summary","role":"content","text":"**7 commands in this group:**\n\n[View all bitmap commands](https://redis.io/commands/?group=bitmap)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [BITCOUNT](https://redis.io/commands/bitcount/) | Counts the number of set bits (population counting) in a string. | O(N) | 2.6.0 |\n| [BITFIELD](https://redis.io/commands/bitfield/) | Performs arbitrary bitfield integer operations on strings. | O(1) for each subcommand specified | 3.2.0 |\n| [BITFIELD_RO](https://redis.io/commands/bitfield_ro/) | Performs arbitrary read-only bitfield integer operations on strings. | O(1) for each subcommand specified | 6.0.0 |\n| [BITOP](https://redis.io/commands/bitop/) | Performs bitwise operations on multiple strings, and stores the result. | O(N) | 2.6.0 |\n| [BITPOS](https://redis.io/commands/bitpos/) | Finds the first set (1) or clear (0) bit in a string. | O(N) | 2.8.7 |\n| [GETBIT](https://redis.io/commands/getbit/) | Returns a bit value by offset. | O(1) | 2.2.0 |\n| [SETBIT](https://redis.io/commands/setbit/) | Sets or clears the bit at offset of the string value. Creates the key if it doesn't exist. | O(1) | 2.2.0 |\n\n\n\nBitmaps are not an actual data type, but a set of bit-oriented operations\ndefined on the String type which is treated like a bit vector.\nSince strings are binary safe blobs and their maximum length is 512 MB,\nthey are suitable to set up to 2^32 different bits.\n\nYou can perform bitwise operations on one or more strings.\nSome examples of bitmap use cases include:\n\n* Efficient set representations for cases where the members of a set correspond to the integers 0-N.\n* Object permissions, where each bit represents a particular permission, similar to the way that file systems store permissions."},{"id":"example","title":"Example","role":"example","text":"Suppose you have 1000 cyclists racing through the country-side, with sensors on their bikes labeled 0-999.\nYou want to quickly determine whether a given sensor has pinged a tracking server within the hour to check in on a rider. \n\nYou can represent this scenario using a bitmap whose key references the current hour.\n\n* Rider 123 pings the server on January 1, 2024 within the 00:00 hour. You can then confirm that rider 123 pinged the server. You can also check to see if rider 456 has pinged the server for that same hour.\n\nFoundational: Set and get individual bits using SETBIT and GETBIT to track binary states\n\n**Commands:** SETBIT, GETBIT\n\n**Complexity:**\n- SETBIT: O(1)\n- GETBIT: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]"},{"id":"bit-operations","title":"Bit Operations","role":"content","text":"Bit operations are divided into two groups: constant-time single bit\noperations, like setting a bit to 1 or 0, or getting its value, and\noperations on groups of bits, for example counting the number of set\nbits in a given range of bits (e.g., population counting).\n\nOne of the biggest advantages of bitmaps is that they often provide\nextreme space savings when storing information. For example in a system\nwhere different users are represented by incremental user IDs, it is possible\nto remember a single bit information (for example, knowing whether\na user wants to receive a newsletter) of 4 billion users using just 512 MB of memory.\n\nThe [`SETBIT`](https://redis.io/docs/latest/commands/setbit) command takes as its first argument the bit number, and as its second\nargument the value to set the bit to, which is 1 or 0. The command\nautomatically enlarges the string if the addressed bit is outside the\ncurrent string length.\n\n[`GETBIT`](https://redis.io/docs/latest/commands/getbit) just returns the value of the bit at the specified index.\nOut of range bits (addressing a bit that is outside the length of the string\nstored into the target key) are always considered to be zero.\n\nThere are three commands operating on group of bits:\n\n1. [`BITOP`](https://redis.io/docs/latest/commands/bitop) performs bit-wise operations between different strings. The provided operators are `AND`, `OR`, `XOR`, `NOT`, `DIFF`, `DIFF1`, `ANDOR`, and `ONE`.\n2. [`BITCOUNT`](https://redis.io/docs/latest/commands/bitcount) performs population counting, reporting the number of bits set to 1.\n3. [`BITPOS`](https://redis.io/docs/latest/commands/bitpos) finds the first bit having the specified value of 0 or 1.\n\nBoth [`BITPOS`](https://redis.io/docs/latest/commands/bitpos) and [`BITCOUNT`](https://redis.io/docs/latest/commands/bitcount) are able to operate with byte ranges of the\nstring, instead of running for the whole length of the string. We can trivially see the number of bits that have been set in a bitmap.\n\nBit counting: Use BITCOUNT to count the number of set bits in a bitmap when you need to get population counts\n\n**Builds upon:** ping\n\n**Commands:** BITCOUNT\n\n**Complexity:**\n- BITCOUNT: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nFor example imagine you want to know the longest streak of daily visits of\nyour web site users. You start counting days starting from zero, that is the\nday you made your web site public, and set a bit with [`SETBIT`](https://redis.io/docs/latest/commands/setbit) every time\nthe user visits the web site. As a bit index you simply take the current unix\ntime, subtract the initial offset, and divide by the number of seconds in a day\n(normally, 3600\\*24).\n\nThis way for each user you have a small string containing the visit\ninformation for each day. With [`BITCOUNT`](https://redis.io/docs/latest/commands/bitcount) it is possible to easily get\nthe number of days a given user visited the web site, while with\na few [`BITPOS`](https://redis.io/docs/latest/commands/bitpos) calls, or simply fetching and analyzing the bitmap client-side,\nit is possible to easily compute the longest streak."},{"id":"bitwise-operations","title":"Bitwise operations","role":"content","text":"The [`BITOP`](https://redis.io/docs/latest/commands/bitop) command performs bitwise\noperations over two or more source keys, storing the result in a destination key.\n\nThe examples below show the available operations using three keys: `A` (with bit pattern\n`11011000`), `B` (`00011001`), and `C` (`01101100`).\n\n![Bitop setup](https://redis.io/docs/latest/images/dev/bitmap/BitopSetup.svg)\n\nNumbering the bits from left to right, starting at zero, the following `SETBIT` commands \nwill create these bitmaps:\n\nSetup for bitwise operations: Create multiple bitmaps using SETBIT to prepare for demonstrating bitwise operations\n\n**Difficulty:** Intermediate\n\n**Builds upon:** ping\n\n**Commands:** SETBIT, GET\n\n**Complexity:**\n- SETBIT: O(1)\n- GET: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\n#### `AND`\n\nSet a bit in the destination key to 1 only if it is set in all the source keys.\n\n![Bitop AND](https://redis.io/docs/latest/images/dev/bitmap/BitopAnd.svg)\n\nAND operation: Use BITOP AND to find bits set in all source bitmaps when you need to find common bits across multiple sets\n\n**Difficulty:** Intermediate\n\n**Builds upon:** bitop_setup\n\n**Commands:** BITOP, GET\n\n**Complexity:**\n- BITOP: O(N)\n- GET: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\n#### `OR`\nSet a bit in the destination key to 1 if it is set in at least one of the source keys.\n\n![Bitop OR](https://redis.io/docs/latest/images/dev/bitmap/BitopOr.svg)\n\nOR operation: Use BITOP OR to find bits set in at least one source bitmap when you need to combine multiple sets\n\n**Difficulty:** Intermediate\n\n**Builds upon:** bitop_setup\n\n**Commands:** BITOP, GET\n\n**Complexity:**\n- BITOP: O(N)\n- GET: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\n#### `XOR`\n\nFor two source keys, set a bit in the destination key to 1 if the value of the bit is \ndifferent in the two keys. For three or more source keys, the result of XORing the first two \nkeys is then XORed with the next key, and so forth.\n\n![Bitop XOR](https://redis.io/docs/latest/images/dev/bitmap/BitopXor.svg)\n\nXOR operation: Use BITOP XOR to find bits that differ between bitmaps when you need to identify differences\n\n**Difficulty:** Intermediate\n\n**Builds upon:** bitop_setup\n\n**Commands:** BITOP, GET\n\n**Complexity:**\n- BITOP: O(N)\n- GET: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\n#### `NOT`\n\nSet a bit in the destination key to 1 if it is not set in the source key (this\nis the only unary operator).\n\n![Bitop NOT](https://redis.io/docs/latest/images/dev/bitmap/BitopNot.svg)\n\nNOT operation: Use BITOP NOT to invert all bits in a bitmap when you need to negate a set\n\n**Difficulty:** Intermediate\n\n**Builds upon:** bitop_setup\n\n**Commands:** BITOP, GET\n\n**Complexity:**\n- BITOP: O(N)\n- GET: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\n#### `DIFF`\n\nSet a bit in the destination key to 1 if it is set in the first source key, but not in any \nof the other source keys.\n\n![Bitop DIFF](https://redis.io/docs/latest/images/dev/bitmap/BitopDiff.svg)\n\nDIFF operation: Use BITOP DIFF to find bits set in the first bitmap but not in others when you need set difference\n\n**Difficulty:** Advanced\n\n**Builds upon:** bitop_setup\n\n**Commands:** BITOP, GET\n\n**Complexity:**\n- BITOP: O(N)\n- GET: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\n#### `DIFF1`\n\nSet a bit in the destination key to 1 if it is not set in the first source key, \nbut set in at least one of the other source keys.\n\n![Bitop DIFF1](https://redis.io/docs/latest/images/dev/bitmap/BitopDiff1.svg)\n\nDIFF1 operation: Use BITOP DIFF1 to find bits not in the first bitmap but in at least one other when you need inverse difference\n\n**Difficulty:** Advanced\n\n**Builds upon:** bitop_setup\n\n**Commands:** BITOP, GET\n\n**Complexity:**\n- BITOP: O(N)\n- GET: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\n#### `ANDOR`\n\nSet a bit in the destination key to 1 if it is set in the first source key and also in at least one of the other source keys.\n\n![Bitop ANDOR](https://redis.io/docs/latest/images/dev/bitmap/BitopAndOr.svg)\n\nANDOR operation: Use BITOP ANDOR to find bits in the first bitmap and at least one other when you need intersection with union\n\n**Difficulty:** Advanced\n\n**Builds upon:** bitop_setup\n\n**Commands:** BITOP, GET\n\n**Complexity:**\n- BITOP: O(N)\n- GET: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\n#### `ONE`\n\nSet a bit in the destination key to 1 if it is set in exactly one of the source keys.\n\n![Bitop ONE](https://redis.io/docs/latest/images/dev/bitmap/BitopOne.svg)\n\nONE operation: Use BITOP ONE to find bits set in exactly one bitmap when you need exclusive membership\n\n**Difficulty:** Advanced\n\n**Builds upon:** bitop_setup\n\n**Commands:** BITOP, GET\n\n**Complexity:**\n- BITOP: O(N)\n- GET: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]"},{"id":"split-bitmaps-into-multiple-keys","title":"Split bitmaps into multiple keys","role":"content","text":"Bitmaps are trivial to split into multiple keys, for example for\nthe sake of sharding the data set and because in general it is better to\navoid working with huge keys. To split a bitmap across different keys\ninstead of setting all the bits into a key, a trivial strategy is just\nto store M bits per key and obtain the key name with `bit-number/M` and\nthe Nth bit to address inside the key with `bit-number MOD M`."},{"id":"performance","title":"Performance","role":"performance","text":"[`SETBIT`](https://redis.io/docs/latest/commands/setbit) and [`GETBIT`](https://redis.io/docs/latest/commands/getbit) are O(1).\n[`BITOP`](https://redis.io/docs/latest/commands/bitop) is O(n), where _n_ is the length of the longest string in the comparison."},{"id":"learn-more","title":"Learn more","role":"related","text":"* [Redis Bitmaps Explained](https://www.youtube.com/watch?v=oj8LdJQjhJo) teaches you how to use bitmaps for map exploration in an online game. \n* [Redis University's RU101](https://university.redis.com/courses/ru101/) covers Redis bitmaps in detail."}],"examples":[{"id":"example-ex0","language":"plaintext","code":"> SETBIT pings:2024-01-01-00:00 123 1\n(integer) 0\n> GETBIT pings:2024-01-01-00:00 123\n1\n> GETBIT pings:2024-01-01-00:00 456\n0","section_id":"example"},{"id":"example-ex1","language":"csharp","code":"bool res1 = db.StringSetBit(\"pings:2024-01-01-00:00\", 123, true);\n        Console.WriteLine(res1);    // >>> 0\n\n        bool res2 = db.StringGetBit(\"pings:2024-01-01-00:00\", 123);\n        Console.WriteLine(res2);    // >>> True\n\n        bool res3 = db.StringGetBit(\"pings:2024-01-01-00:00\", 456);\n        Console.WriteLine(res3);    // >>> False","section_id":"example"},{"id":"example-ex2","language":"go","code":"res1, err := rdb.SetBit(ctx, \"pings:2024-01-01-00:00\", 123, 1).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> 0\n\n\tres2, err := rdb.GetBit(ctx, \"pings:2024-01-01-00:00\", 123).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> 1\n\n\tres3, err := rdb.GetBit(ctx, \"pings:2024-01-01-00:00\", 456).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3) // >>> 0","section_id":"example"},{"id":"example-ex3","language":"java","code":"CompletableFuture<Void> ping = async.setbit(\"pings:2024-01-01-00:00\", 123, 1).thenCompose(res1 -> {\n                System.out.println(res1); // >>> 0\n                return async.getbit(\"pings:2024-01-01-00:00\", 123);\n            }).thenCompose(res2 -> {\n                System.out.println(res2); // >>> 1\n                return async.getbit(\"pings:2024-01-01-00:00\", 456);\n            }).thenAccept(res3 -> {\n                System.out.println(res3); // >>> 0\n            }).toCompletableFuture();","section_id":"example"},{"id":"example-ex4","language":"java","code":"Mono<Void> ping = reactive.setbit(\"pings:2024-01-01-00:00\", 123, 1).doOnNext(res1 -> {\n                System.out.println(res1); // >>> 0\n            }).flatMap(v -> reactive.getbit(\"pings:2024-01-01-00:00\", 123)).doOnNext(res2 -> {\n                System.out.println(res2); // >>> 1\n            }).flatMap(v -> reactive.getbit(\"pings:2024-01-01-00:00\", 456)).doOnNext(res3 -> {\n                System.out.println(res3); // >>> 0\n            }).then();","section_id":"example"},{"id":"example-ex5","language":"java","code":"boolean res1 = jedis.setbit(\"pings:2024-01-01-00:00\", 123, true);\n        System.out.println(res1); // >>> false\n\n        boolean res2 = jedis.getbit(\"pings:2024-01-01-00:00\", 123);\n        System.out.println(res2); // >>> true\n\n        boolean res3 = jedis.getbit(\"pings:2024-01-01-00:00\", 456);\n        System.out.println(res3); // >>> false","section_id":"example"},{"id":"example-ex6","language":"javascript","code":"const res1 = await client.setBit(\"pings:2024-01-01-00:00\", 123, 1)\nconsole.log(res1)  // >>> 0\n\nconst res2 = await client.getBit(\"pings:2024-01-01-00:00\", 123)\nconsole.log(res2)  // >>> 1\n\nconst res3 = await client.getBit(\"pings:2024-01-01-00:00\", 456)\nconsole.log(res3)  // >>> 0","section_id":"example"},{"id":"example-ex7","language":"php","code":"$res1 = $r->setbit('pings:2024-01-01-00:00', 123, 1);\n        echo $res1 . PHP_EOL;\n        // >>> 0\n\n        $res2 = $r->getbit('pings:2024-01-01-00:00', 123);\n        echo $res2 . PHP_EOL;\n        // >>> 1\n\n        $res3 = $r->getbit('pings:2024-01-01-00:00', 456);\n        echo $res3 . PHP_EOL;\n        // >>> 0","section_id":"example"},{"id":"example-ex8","language":"python","code":"res1 = r.setbit(\"pings:2024-01-01-00:00\", 123, 1)\nprint(res1)  # >>> 0\n\nres2 = r.getbit(\"pings:2024-01-01-00:00\", 123)\nprint(res2)  # >>> 1\n\nres3 = r.getbit(\"pings:2024-01-01-00:00\", 456)\nprint(res3)  # >>> 0","section_id":"example"},{"id":"bit-operations-ex0","language":"plaintext","code":"> BITCOUNT pings:2024-01-01-00:00\n(integer) 1","section_id":"bit-operations"},{"id":"bit-operations-ex1","language":"csharp","code":"bool res4 = db.StringSetBit(\"pings:2024-01-01-00:00\", 123, true);\n        long res5 = db.StringBitCount(\"pings:2024-01-01-00:00\");\n        Console.WriteLine(res5);    // >>> 1","section_id":"bit-operations"},{"id":"bit-operations-ex2","language":"go","code":"res4, err := rdb.BitCount(ctx, \"pings:2024-01-01-00:00\",\n\t\t&redis.BitCount{\n\t\t\tStart: 0,\n\t\t\tEnd:   456,\n\t\t}).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4) // >>> 1","section_id":"bit-operations"},{"id":"bit-operations-ex3","language":"java","code":"CompletableFuture<Void> bitcount = async.bitcount(\"pings:2024-01-01-00:00\").thenAccept(res4 -> {\n                System.out.println(res4); // >>> 1\n            }).toCompletableFuture();","section_id":"bit-operations"},{"id":"bit-operations-ex4","language":"java","code":"Mono<Void> bitcount = reactive.bitcount(\"pings:2024-01-01-00:00\").doOnNext(res4 -> {\n                System.out.println(res4); // >>> 1\n            }).then();","section_id":"bit-operations"},{"id":"bit-operations-ex5","language":"java","code":"long res4 = jedis.bitcount(\"pings:2024-01-01-00:00\");\n        System.out.println(res4); // >>> 1","section_id":"bit-operations"},{"id":"bit-operations-ex6","language":"javascript","code":"await client.setBit(\"pings:2024-01-01-00:00\", 123, 1)\nconst res4 = await client.bitCount(\"pings:2024-01-01-00:00\")\nconsole.log(res4)  // >>> 1","section_id":"bit-operations"},{"id":"bit-operations-ex7","language":"php","code":"// Ensure the bit is set\n        $r->setbit('pings:2024-01-01-00:00', 123, 1);\n        $res4 = $r->bitcount('pings:2024-01-01-00:00');\n        echo $res4 . PHP_EOL;\n        // >>> 1","section_id":"bit-operations"},{"id":"bit-operations-ex8","language":"python","code":"r.setbit(\"pings:2024-01-01-00:00\", 123, 1)\nres4 = r.bitcount(\"pings:2024-01-01-00:00\")\nprint(res4)  # >>> 1","section_id":"bit-operations"},{"id":"bitwise-operations-ex0","language":"plaintext","code":"> SETBIT A 0 1\n(integer) 0\n> SETBIT A 1 1\n(integer) 0\n> SETBIT A 3 1\n(integer) 0\n> SETBIT A 4 1\n(integer) 0\n> GET A\n\"\\xd8\"\n# Hex value: 0xd8 = 0b11011000\n\n> SETBIT B 3 1\n(integer) 0\n> SETBIT B 4 1\n(integer) 0\n> SETBIT B 7 1\n(integer) 0\n> GET B\n\"\\x19\"\n# Hex value: 0x19 = 0b00011001\n\n> SETBIT C 1 1\n(integer) 0\n> SETBIT C 2 1\n(integer) 0\n> SETBIT C 4 1\n(integer) 0\n> SETBIT C 5 1\n(integer) 0\n> GET C\n\"l\"\n# ASCII \"l\" = hex 0x6c = 0b01101100","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex1","language":"csharp","code":"// Bitwise operations are not supported in NRedisStack.","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex2","language":"go","code":"rdb.SetBit(ctx, \"A\", 0, 1)\n\trdb.SetBit(ctx, \"A\", 1, 1)\n\trdb.SetBit(ctx, \"A\", 3, 1)\n\trdb.SetBit(ctx, \"A\", 4, 1)\n\tba, _ := rdb.Get(ctx, \"A\").Bytes()\n\tfmt.Printf(\"%08b\\n\", ba[0])\n\t// >>> 11011000\n\n\trdb.SetBit(ctx, \"B\", 3, 1)\n\trdb.SetBit(ctx, \"B\", 4, 1)\n\trdb.SetBit(ctx, \"B\", 7, 1)\n\tbb, _ := rdb.Get(ctx, \"B\").Bytes()\n\tfmt.Printf(\"%08b\\n\", bb[0])\n\t// >>> 00011001\n\n\trdb.SetBit(ctx, \"C\", 1, 1)\n\trdb.SetBit(ctx, \"C\", 2, 1)\n\trdb.SetBit(ctx, \"C\", 4, 1)\n\trdb.SetBit(ctx, \"C\", 5, 1)\n\tbc, _ := rdb.Get(ctx, \"C\").Bytes()\n\tfmt.Printf(\"%08b\\n\", bc[0])\n\t// >>> 01101100","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex3","language":"java","code":"CompletableFuture<Void> setup = async.setbit(\"A\", 0, 1).thenCompose(v -> async.setbit(\"A\", 1, 1))\n                    .thenCompose(v -> async.setbit(\"A\", 3, 1)).thenCompose(v -> async.setbit(\"A\", 4, 1))\n                    .thenCompose(v -> async.setbit(\"B\", 3, 1)).thenCompose(v -> async.setbit(\"B\", 4, 1))\n                    .thenCompose(v -> async.setbit(\"B\", 7, 1)).thenCompose(v -> async.setbit(\"C\", 1, 1))\n                    .thenCompose(v -> async.setbit(\"C\", 2, 1)).thenCompose(v -> async.setbit(\"C\", 4, 1))\n                    .thenCompose(v -> async.setbit(\"C\", 5, 1)).thenCompose(v -> asyncBytes.get(\"A\".getBytes()))\n                    .thenApply(res -> {\n                        byte b = (res != null && res.length > 0) ? res[0] : 0;\n                        String bitsA = String.format(\"%8s\", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');\n                        System.out.println(bitsA); // >>> 11011000\n                        return bitsA;\n                    }).thenCompose(v -> asyncBytes.get(\"B\".getBytes())).thenApply(res -> {\n                        byte b = (res != null && res.length > 0) ? res[0] : 0;\n                        String bitsB = String.format(\"%8s\", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');\n                        System.out.println(bitsB); // >>> 00011001\n                        return bitsB;\n                    })\n                    // Print C\n                    .thenCompose(v -> asyncBytes.get(\"C\".getBytes())).thenAccept(res -> {\n                        byte b = (res != null && res.length > 0) ? res[0] : 0;\n                        String bitsC = String.format(\"%8s\", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');\n                        System.out.println(bitsC); // >>> 01101100\n                    }).toCompletableFuture();","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex4","language":"java","code":"Mono<Void> setup = reactive.setbit(\"A\", 0, 1).then(reactive.setbit(\"A\", 1, 1)).then(reactive.setbit(\"A\", 3, 1))\n                    .then(reactive.setbit(\"A\", 4, 1)).then(reactive.setbit(\"B\", 3, 1)).then(reactive.setbit(\"B\", 4, 1))\n                    .then(reactive.setbit(\"B\", 7, 1)).then(reactive.setbit(\"C\", 1, 1)).then(reactive.setbit(\"C\", 2, 1))\n                    .then(reactive.setbit(\"C\", 4, 1)).then(reactive.setbit(\"C\", 5, 1)).then(reactiveBytes.get(\"A\".getBytes()))\n                    .doOnNext(res -> {\n                        byte b = (res != null && res.length > 0) ? res[0] : 0;\n                        String bitsA = String.format(\"%8s\", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');\n                        System.out.println(bitsA); // >>> 11011000\n                    }).then(reactiveBytes.get(\"B\".getBytes())).doOnNext(res -> {\n                        byte b = (res != null && res.length > 0) ? res[0] : 0;\n                        String bitsB = String.format(\"%8s\", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');\n                        System.out.println(bitsB); // >>> 00011001\n                    }).then(reactiveBytes.get(\"C\".getBytes())).doOnNext(res -> {\n                        byte b = (res != null && res.length > 0) ? res[0] : 0;\n                        String bitsC = String.format(\"%8s\", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');\n                        System.out.println(bitsC); // >>> 01101100\n                    }).then();","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex5","language":"java","code":"jedis.setbit(\"A\", 0, true);\n        jedis.setbit(\"A\", 1, true);\n        jedis.setbit(\"A\", 3, true);\n        jedis.setbit(\"A\", 4, true);\n\n        byte[] res5 = jedis.get(\"A\".getBytes());\n        System.out.println(String.format(\"%8s\", Integer.toBinaryString(res5[0] & 0xFF)).replace(' ', '0'));\n        // >>> 11011000\n\n        jedis.setbit(\"B\", 3, true);\n        jedis.setbit(\"B\", 4, true);\n        jedis.setbit(\"B\", 7, true);\n\n        byte[] res6 = jedis.get(\"B\".getBytes());\n        System.out.println(String.format(\"%8s\", Integer.toBinaryString(res6[0] & 0xFF)).replace(' ', '0'));\n        // >>> 00011001\n\n        jedis.setbit(\"C\", 1, true);\n        jedis.setbit(\"C\", 2, true);\n        jedis.setbit(\"C\", 4, true);\n        jedis.setbit(\"C\", 5, true);\n\n        byte[] res7 = jedis.get(\"C\".getBytes());\n        System.out.println(String.format(\"%8s\", Integer.toBinaryString(res7[0] & 0xFF)).replace(' ', '0'));\n        // >>> 01101100","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex6","language":"javascript","code":"await client.setBit(\"A\", 0, 1)\nawait client.setBit(\"A\", 1, 1)\nawait client.setBit(\"A\", 3, 1)\nawait client.setBit(\"A\", 4, 1)\n\nconst res5 = await client.get(\"A\")\nconsole.log(res5.readUInt8(0).toString(2).padStart(8, '0'))\n// >>> 11011000\n\nawait client.setBit(\"B\", 3, 1)\nawait client.setBit(\"B\", 4, 1)\nawait client.setBit(\"B\", 7, 1)\n\nconst res6 = await client.get(\"B\")\nconsole.log(res6.readUInt8(0).toString(2).padStart(8, '0'))\n// >>> 00011001\n\nawait client.setBit(\"C\", 1, 1)\nawait client.setBit(\"C\", 2, 1)\nawait client.setBit(\"C\", 4, 1)\nawait client.setBit(\"C\", 5, 1)\n\nconst res7 = await client.get(\"C\")\nconsole.log(res7.readUInt8(0).toString(2).padStart(8, '0'))\n// >>> 01101100","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex7","language":"php","code":"$r->setbit('A', 0, 1);\n        $r->setbit('A', 1, 1);\n        $r->setbit('A', 3, 1);\n        $r->setbit('A', 4, 1);\n\n        $res5 = $r->get('A');\n        echo str_pad(decbin(ord($res5)), 8, '0', STR_PAD_LEFT) . PHP_EOL;\n        // >>> 11011000\n\n        $r->setbit('B', 3, 1);\n        $r->setbit('B', 4, 1);\n        $r->setbit('B', 7, 1);\n\n        $res6 = $r->get('B');\n        echo str_pad(decbin(ord($res6)), 8, '0', STR_PAD_LEFT) . PHP_EOL;\n        // >>> 00011001\n\n        $r->setbit('C', 1, 1);\n        $r->setbit('C', 2, 1);\n        $r->setbit('C', 4, 1);\n        $r->setbit('C', 5, 1);\n\n        $res7 = $r->get('C');\n        echo str_pad(decbin(ord($res7)), 8, '0', STR_PAD_LEFT) . PHP_EOL;\n        // >>> 01101100","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex8","language":"python","code":"r.setbit(\"A\", 0, 1)\nr.setbit(\"A\", 1, 1)\nr.setbit(\"A\", 3, 1)\nr.setbit(\"A\", 4, 1)\n\nres5 = r.get(\"A\")\nprint(\"{:08b}\".format(int.from_bytes(res5, \"big\")))\n# >>> 11011000\n\nr.setbit(\"B\", 3, 1)\nr.setbit(\"B\", 4, 1)\nr.setbit(\"B\", 7, 1)\n\nres6 = r.get(\"B\")\nprint(\"{:08b}\".format(int.from_bytes(res6, \"big\")))\n# >>> 00011001\n\nr.setbit(\"C\", 1, 1)\nr.setbit(\"C\", 2, 1)\nr.setbit(\"C\", 4, 1)\nr.setbit(\"C\", 5, 1)\n\nres7 = r.get(\"C\")\nprint(\"{:08b}\".format(int.from_bytes(res7, \"big\")))\n# >>> 01101100","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex9","language":"plaintext","code":"> BITOP AND R A B C\n(integer) 1\n> GET R\n\"\\b\"\n# ASCII \"\\b\" (backspace) = hex 0x08 = 0b00001000","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex10","language":"csharp","code":"// Bitwise operations are not supported in NRedisStack.","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex11","language":"go","code":"rdb.BitOpAnd(ctx, \"R\", \"A\", \"B\", \"C\")\n\tbr, _ := rdb.Get(ctx, \"R\").Bytes()\n\tfmt.Printf(\"%08b\\n\", br[0])\n\t// >>> 00001000","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex12","language":"java","code":"CompletableFuture<Void> andOp = async.bitopAnd(\"R\", \"A\", \"B\", \"C\")\n                    .thenCompose(len -> asyncBytes.get(\"R\".getBytes())).thenAccept(res -> {\n                        byte b = (res != null && res.length > 0) ? res[0] : 0;\n                        String bits = String.format(\"%8s\", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');\n                        System.out.println(bits); // >>> 00001000\n                    }).toCompletableFuture();","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex13","language":"java","code":"Mono<Void> andOp = reactive.bitopAnd(\"R\", \"A\", \"B\", \"C\").then(reactiveBytes.get(\"R\".getBytes())).doOnNext(res -> {\n                byte b = (res != null && res.length > 0) ? res[0] : 0;\n                String bits = String.format(\"%8s\", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');\n                System.out.println(bits); // >>> 00001000\n            }).then();","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex14","language":"java","code":"jedis.bitop(BitOP.AND, \"R\", \"A\", \"B\", \"C\");\n        byte[] res8 = jedis.get(\"R\".getBytes());\n        System.out.println(String.format(\"%8s\", Integer.toBinaryString(res8[0] & 0xFF)).replace(' ', '0'));\n        // >>> 00001000","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex15","language":"javascript","code":"await client.bitOp(\"AND\", \"R\", [\"A\", \"B\", \"C\"])\nconst res8 = await client.get(\"R\")\nconsole.log(res8.readUInt8(0).toString(2).padStart(8, '0'))\n// >>> 00001000","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex16","language":"php","code":"$r->bitop('AND', 'R', 'A', 'B', 'C');\n        $res8 = $r->get('R');\n        echo str_pad(decbin(ord($res8)), 8, '0', STR_PAD_LEFT) . PHP_EOL;\n        // >>> 00001000","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex17","language":"python","code":"r.bitop(\"AND\", \"R\", \"A\", \"B\", \"C\")\nres8 = r.get(\"R\")\nprint(\"{:08b}\".format(int.from_bytes(res8, \"big\")))\n# >>> 00001000","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex18","language":"plaintext","code":"> BITOP OR R A B C\n(integer) 1\n> GET R\n\"\\xfd\"\n# Hex value: 0xfd = 0b11111101","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex19","language":"csharp","code":"// Bitwise operations are not supported in NRedisStack.","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex20","language":"go","code":"rdb.BitOpOr(ctx, \"R\", \"A\", \"B\", \"C\")\n\tbr, _ = rdb.Get(ctx, \"R\").Bytes()\n\tfmt.Printf(\"%08b\\n\", br[0])\n\t// >>> 11111101","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex21","language":"java","code":"CompletableFuture<Void> orOp = async.bitopOr(\"R\", \"A\", \"B\", \"C\").thenCompose(len -> asyncBytes.get(\"R\".getBytes()))\n                    .thenAccept(res -> {\n                        byte b = (res != null && res.length > 0) ? res[0] : 0;\n                        String bits = String.format(\"%8s\", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');\n                        System.out.println(bits); // >>> 11111101\n                    }).toCompletableFuture();","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex22","language":"java","code":"Mono<Void> orOp = reactive.bitopOr(\"R\", \"A\", \"B\", \"C\").then(reactiveBytes.get(\"R\".getBytes())).doOnNext(res -> {\n                byte b = (res != null && res.length > 0) ? res[0] : 0;\n                String bits = String.format(\"%8s\", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');\n                System.out.println(bits); // >>> 11111101\n            }).then();","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex23","language":"java","code":"jedis.bitop(BitOP.OR, \"R\", \"A\", \"B\", \"C\");\n        byte[] res9 = jedis.get(\"R\".getBytes());\n        System.out.println(String.format(\"%8s\", Integer.toBinaryString(res9[0] & 0xFF)).replace(' ', '0'));\n        // >>> 11111101","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex24","language":"javascript","code":"await client.bitOp(\"OR\", \"R\", [\"A\", \"B\", \"C\"])\nconst res9 = await client.get(\"R\")\nconsole.log(res9.readUInt8(0).toString(2).padStart(8, '0'))\n// >>> 11111101","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex25","language":"php","code":"$r->bitop('OR', 'R', 'A', 'B', 'C');\n        $res9 = $r->get('R');\n        echo str_pad(decbin(ord($res9)), 8, '0', STR_PAD_LEFT) . PHP_EOL;\n        // >>> 11111101","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex26","language":"python","code":"r.bitop(\"OR\", \"R\", \"A\", \"B\", \"C\")\nres9 = r.get(\"R\")\nprint(\"{:08b}\".format(int.from_bytes(res9, \"big\")))\n# >>> 11111101","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex27","language":"plaintext","code":"> BITOP XOR R A B\n(integer) 1\n> GET R\n\"\\xc1\"\n# Hex value: 0xc1 = 0b11000001","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex28","language":"csharp","code":"// Bitwise operations are not supported in NRedisStack.","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex29","language":"go","code":"rdb.BitOpXor(ctx, \"R\", \"A\", \"B\")\n\tbr, _ = rdb.Get(ctx, \"R\").Bytes()\n\tfmt.Printf(\"%08b\\n\", br[0])\n\t// >>> 11000001","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex30","language":"java","code":"CompletableFuture<Void> xorOp = async.bitopXor(\"R\", \"A\", \"B\").thenCompose(len -> asyncBytes.get(\"R\".getBytes()))\n                    .thenAccept(res -> {\n                        byte b = (res != null && res.length > 0) ? res[0] : 0;\n                        String bits = String.format(\"%8s\", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');\n                        System.out.println(bits); // >>> 11000001\n                    }).toCompletableFuture();","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex31","language":"java","code":"Mono<Void> xorOp = reactive.bitopXor(\"R\", \"A\", \"B\").then(reactiveBytes.get(\"R\".getBytes())).doOnNext(res -> {\n                byte b = (res != null && res.length > 0) ? res[0] : 0;\n                String bits = String.format(\"%8s\", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');\n                System.out.println(bits); // >>> 11000001\n            }).then();","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex32","language":"java","code":"jedis.bitop(BitOP.XOR, \"R\", \"A\", \"B\");\n        byte[] res10 = jedis.get(\"R\".getBytes());\n        System.out.println(String.format(\"%8s\", Integer.toBinaryString(res10[0] & 0xFF)).replace(' ', '0'));\n        // >>> 11000001","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex33","language":"javascript","code":"await client.bitOp(\"XOR\", \"R\", [\"A\", \"B\"]) // XOR uses two keys here\nconst res10 = await client.get(\"R\")\nconsole.log(res10.readUInt8(0).toString(2).padStart(8, '0'))\n// >>> 11000001","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex34","language":"php","code":"$r->bitop('XOR', 'R', 'A', 'B');\n        $res10 = $r->get('R');\n        echo str_pad(decbin(ord($res10)), 8, '0', STR_PAD_LEFT) . PHP_EOL;\n        // >>> 11000001","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex35","language":"python","code":"r.bitop(\"XOR\", \"R\", \"A\", \"B\")\nres10 = r.get(\"R\")\nprint(\"{:08b}\".format(int.from_bytes(res10, \"big\")))\n# >>> 11000001","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex36","language":"plaintext","code":"> BITOP NOT R A\n(integer) 1\n> GET R\n\"'\"\n# ASCII \"'\" (single quote) = hex 0x27 = 0b00100111","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex37","language":"csharp","code":"// Bitwise operations are not supported in NRedisStack.","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex38","language":"go","code":"rdb.BitOpNot(ctx, \"R\", \"A\")\n\tbr, _ = rdb.Get(ctx, \"R\").Bytes()\n\tfmt.Printf(\"%08b\\n\", br[0])\n\t// >>> 00100111","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex39","language":"java","code":"CompletableFuture<Void> notOp = async.bitopNot(\"R\", \"A\").thenCompose(len -> asyncBytes.get(\"R\".getBytes()))\n                    .thenAccept(res -> {\n                        byte b = (res != null && res.length > 0) ? res[0] : 0;\n                        String bits = String.format(\"%8s\", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');\n                        System.out.println(bits); // >>> 00100111\n                    }).toCompletableFuture();","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex40","language":"java","code":"Mono<Void> notOp = reactive.bitopNot(\"R\", \"A\").then(reactiveBytes.get(\"R\".getBytes())).doOnNext(res -> {\n                byte b = (res != null && res.length > 0) ? res[0] : 0;\n                String bits = String.format(\"%8s\", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');\n                System.out.println(bits); // >>> 00100111\n            }).then();","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex41","language":"java","code":"jedis.bitop(BitOP.NOT, \"R\", \"A\");\n        byte[] res11 = jedis.get(\"R\".getBytes());\n        System.out.println(String.format(\"%8s\", Integer.toBinaryString(res11[0] & 0xFF)).replace(' ', '0'));\n        // >>> 00100111","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex42","language":"javascript","code":"await client.bitOp(\"NOT\", \"R\", \"A\")\nconst res11 = await client.get(\"R\")\nconsole.log(res11.readUInt8(0).toString(2).padStart(8, '0'))\n// >>> 00100111","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex43","language":"php","code":"$r->bitop('NOT', 'R', 'A');\n        $res11 = $r->get('R');\n        echo str_pad(decbin(ord($res11)), 8, '0', STR_PAD_LEFT) . PHP_EOL;\n        // >>> 00100111","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex44","language":"python","code":"r.bitop(\"NOT\", \"R\", \"A\")\nres11 = r.get(\"R\")\nprint(\"{:08b}\".format(int.from_bytes(res11, \"big\")))\n# >>> 00100111","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex45","language":"plaintext","code":"> BITOP DIFF R A B C\n(integer) 1\n> GET R\n\"\\x80\"\n# Hex value: 0x80 = 0b10000000","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex46","language":"csharp","code":"// Bitwise operations are not supported in NRedisStack.","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex47","language":"go","code":"rdb.BitOpDiff(ctx, \"R\", \"A\", \"B\", \"C\")\n\tbr, _ = rdb.Get(ctx, \"R\").Bytes()\n\tfmt.Printf(\"%08b\\n\", br[0])\n\t// >>> 10000000","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex48","language":"java","code":"CompletableFuture<Void> diffOp = async.bitopDiff(\"R\", \"A\", \"B\", \"C\")\n                    .thenCompose(len -> asyncBytes.get(\"R\".getBytes())).thenAccept(res -> {\n                        byte b = (res != null && res.length > 0) ? res[0] : 0;\n                        String bits = String.format(\"%8s\", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');\n                        System.out.println(bits); // >>> 10000000\n                    }).toCompletableFuture();","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex49","language":"java","code":"Mono<Void> diffOp = reactive.bitopDiff(\"R\", \"A\", \"B\", \"C\").then(reactiveBytes.get(\"R\".getBytes())).doOnNext(res -> {\n                byte b = (res != null && res.length > 0) ? res[0] : 0;\n                String bits = String.format(\"%8s\", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');\n                System.out.println(bits); // >>> 10000000\n            }).then();","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex50","language":"java","code":"jedis.bitop(BitOP.DIFF, \"R\", \"A\", \"B\", \"C\");\n        byte[] res12 = jedis.get(\"R\".getBytes());\n        System.out.println(String.format(\"%8s\", Integer.toBinaryString(res12[0] & 0xFF)).replace(' ', '0'));\n        // >>> 10000000","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex51","language":"javascript","code":"await client.bitOp(\"DIFF\", \"R\", [\"A\", \"B\", \"C\"])\nconst res12 = await client.get(\"R\")\nconsole.log(res12.readUInt8(0).toString(2).padStart(8, '0'))\n// >>> 10000000","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex52","language":"php","code":"$r->bitop('DIFF', 'R', 'A', 'B', 'C');\n        $res12 = $r->get('R');\n        echo str_pad(decbin(ord($res12)), 8, '0', STR_PAD_LEFT) . PHP_EOL;\n        // >>> 10000000","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex53","language":"python","code":"r.bitop(\"DIFF\", \"R\", \"A\", \"B\", \"C\")\nres12 = r.get(\"R\")\nprint(\"{:08b}\".format(int.from_bytes(res12, \"big\")))\n# >>> 10000000","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex54","language":"plaintext","code":"> BITOP DIFF1 R A B C\n(integer) 1\n> GET R\n\"%\"\n# ASCII \"%\" (percent) = hex 0x25 = 0b00100101","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex55","language":"csharp","code":"// Bitwise operations are not supported in NRedisStack.","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex56","language":"go","code":"rdb.BitOpDiff1(ctx, \"R\", \"A\", \"B\", \"C\")\n\tbr, _ = rdb.Get(ctx, \"R\").Bytes()\n\tfmt.Printf(\"%08b\\n\", br[0])\n\t// >>> 00100101","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex57","language":"java","code":"CompletableFuture<Void> diff1Op = async.bitopDiff1(\"R\", \"A\", \"B\", \"C\")\n                    .thenCompose(len -> asyncBytes.get(\"R\".getBytes())).thenAccept(res -> {\n                        byte b = (res != null && res.length > 0) ? res[0] : 0;\n                        String bits = String.format(\"%8s\", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');\n                        System.out.println(bits); // >>> 00100101\n                    }).toCompletableFuture();","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex58","language":"java","code":"Mono<Void> diff1Op = reactive.bitopDiff1(\"R\", \"A\", \"B\", \"C\").then(reactiveBytes.get(\"R\".getBytes()))\n                    .doOnNext(res -> {\n                        byte b = (res != null && res.length > 0) ? res[0] : 0;\n                        String bits = String.format(\"%8s\", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');\n                        System.out.println(bits); // >>> 00100101\n                    }).then();","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex59","language":"java","code":"jedis.bitop(BitOP.DIFF1, \"R\", \"A\", \"B\", \"C\");\n        byte[] res13 = jedis.get(\"R\".getBytes());\n        System.out.println(String.format(\"%8s\", Integer.toBinaryString(res13[0] & 0xFF)).replace(' ', '0'));\n        // >>> 00100101","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex60","language":"javascript","code":"await client.bitOp(\"DIFF1\", \"R\", [\"A\", \"B\", \"C\"])\nconst res13 = await client.get(\"R\")\nconsole.log(res13.readUInt8(0).toString(2).padStart(8, '0'))\n// >>> 00100101","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex61","language":"php","code":"$r->bitop('DIFF1', 'R', 'A', 'B', 'C');\n        $res13 = $r->get('R');\n        echo str_pad(decbin(ord($res13)), 8, '0', STR_PAD_LEFT) . PHP_EOL;\n        // >>> 00100101","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex62","language":"python","code":"r.bitop(\"DIFF1\", \"R\", \"A\", \"B\", \"C\")\nres13 = r.get(\"R\")\nprint(\"{:08b}\".format(int.from_bytes(res13, \"big\")))\n# >>> 00100101","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex63","language":"plaintext","code":"> BITOP ANDOR R A B C\n(integer) 1\n> GET R\n\"X\"\n# ASCII \"X\" = hex 0x58 = 0b01011000","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex64","language":"csharp","code":"// Bitwise operations are not supported in NRedisStack.","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex65","language":"go","code":"rdb.BitOpAndOr(ctx, \"R\", \"A\", \"B\", \"C\")\n\tbr, _ = rdb.Get(ctx, \"R\").Bytes()\n\tfmt.Printf(\"%08b\\n\", br[0])\n\t// >>> 01011000","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex66","language":"java","code":"CompletableFuture<Void> andorOp = async.bitopAndor(\"R\", \"A\", \"B\", \"C\")\n                    .thenCompose(len -> asyncBytes.get(\"R\".getBytes())).thenAccept(res -> {\n                        byte b = (res != null && res.length > 0) ? res[0] : 0;\n                        String bits = String.format(\"%8s\", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');\n                        System.out.println(bits); // >>> 01011000\n                    }).toCompletableFuture();","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex67","language":"java","code":"Mono<Void> andorOp = reactive.bitopAndor(\"R\", \"A\", \"B\", \"C\").then(reactiveBytes.get(\"R\".getBytes()))\n                    .doOnNext(res -> {\n                        byte b = (res != null && res.length > 0) ? res[0] : 0;\n                        String bits = String.format(\"%8s\", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');\n                        System.out.println(bits); // >>> 01011000\n                    }).then();","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex68","language":"java","code":"jedis.bitop(BitOP.ANDOR, \"R\", \"A\", \"B\", \"C\");\n        byte[] res14 = jedis.get(\"R\".getBytes());\n        System.out.println(String.format(\"%8s\", Integer.toBinaryString(res14[0] & 0xFF)).replace(' ', '0'));\n        // >>> 01011000","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex69","language":"javascript","code":"await client.bitOp(\"ANDOR\", \"R\", [\"A\", \"B\", \"C\"])\nconst res14 = await client.get(\"R\")\nconsole.log(res14.readUInt8(0).toString(2).padStart(8, '0'))\n// >>> 01011000","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex70","language":"php","code":"$r->bitop('ANDOR', 'R', 'A', 'B', 'C');\n        $res14 = $r->get('R');\n        echo str_pad(decbin(ord($res14)), 8, '0', STR_PAD_LEFT) . PHP_EOL;\n        // >>> 01011000","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex71","language":"python","code":"r.bitop(\"ANDOR\", \"R\", \"A\", \"B\", \"C\")\nres14 = r.get(\"R\")\nprint(\"{:08b}\".format(int.from_bytes(res14, \"big\")))\n# >>> 01011000","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex72","language":"plaintext","code":"> BITOP ONE R A B C\n(integer) 1\n> GET R\n\"\\xa5\"\n# Hex value: 0xa5 = 0b10100101","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex73","language":"csharp","code":"// Bitwise operations are not supported in NRedisStack.","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex74","language":"go","code":"rdb.BitOpOne(ctx, \"R\", \"A\", \"B\", \"C\")\n\tbr, _ = rdb.Get(ctx, \"R\").Bytes()\n\tfmt.Printf(\"%08b\\n\", br[0])\n\t// >>> 10100101","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex75","language":"java","code":"CompletableFuture<Void> oneOp = async.bitopOne(\"R\", \"A\", \"B\", \"C\")\n                    .thenCompose(len -> asyncBytes.get(\"R\".getBytes())).thenAccept(res -> {\n                        byte b = (res != null && res.length > 0) ? res[0] : 0;\n                        String bits = String.format(\"%8s\", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');\n                        System.out.println(bits); // >>> 10100101\n                    }).toCompletableFuture();","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex76","language":"java","code":"Mono<Void> oneOp = reactive.bitopOne(\"R\", \"A\", \"B\", \"C\").then(reactiveBytes.get(\"R\".getBytes())).doOnNext(res -> {\n                byte b = (res != null && res.length > 0) ? res[0] : 0;\n                String bits = String.format(\"%8s\", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');\n                System.out.println(bits); // >>> 10100101\n            }).then();","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex77","language":"java","code":"jedis.bitop(BitOP.ONE, \"R\", \"A\", \"B\", \"C\");\n        byte[] res15 = jedis.get(\"R\".getBytes());\n        System.out.println(String.format(\"%8s\", Integer.toBinaryString(res15[0] & 0xFF)).replace(' ', '0'));\n        // >>> 10100101","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex78","language":"javascript","code":"await client.bitOp(\"ONE\", \"R\", [\"A\", \"B\", \"C\"])\nconst res15 = await client.get(\"R\")\nconsole.log(res15.readUInt8(0).toString(2).padStart(8, '0'))\n// >>> 10100101","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex79","language":"php","code":"$r->bitop('ONE', 'R', 'A', 'B', 'C');\n        $res15 = $r->get('R');\n        echo str_pad(decbin(ord($res15)), 8, '0', STR_PAD_LEFT) . PHP_EOL;\n        // >>> 10100101","section_id":"bitwise-operations"},{"id":"bitwise-operations-ex80","language":"python","code":"r.bitop(\"ONE\", \"R\", \"A\", \"B\", \"C\")\nres15 = r.get(\"R\")\nprint(\"{:08b}\".format(int.from_bytes(res15, \"big\")))\n# >>> 10100101","section_id":"bitwise-operations"}]}
{"schema_version":2,"id":"develop/data-types/strings","title":"Redis Strings","url":"https://redis.io/docs/latest/develop/data-types/strings/","summary":"Introduction to Redis strings","aliases":["/data-types/strings/","/manual/data-types/strings/","/develop/data-types/string/"],"tags":["docs","develop","stack","oss","rs","rc","kubernetes","clients"],"last_updated":"2026-10-12T11:44:13+01:00","children":[{"id":"develop/data-types/strings/bitfields","summary":"Introduction to Redis bitfields","title":"Redis bitfields","url":"https://redis.io/docs/latest/develop/data-types/strings/bitfields/"},{"id":"develop/data-types/strings/bitmaps","summary":"Introduction to Redis bitmaps","title":"Redis bitmaps","url":"https://redis.io/docs/latest/develop/data-types/strings/bitmaps/"}],"page_type":"content","content_hash":"c0d482c4d1be20a4d92f3f7ee04673bdfb33ced1eeaf25b3fd3ff2956575eaa7","sections":[{"id":"string-command-summary","title":"String command summary","role":"content","text":"**26 commands in this group:**\n\n[View all string commands](https://redis.io/commands/?group=string)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [APPEND](https://redis.io/commands/append/) | Appends a string to the value of a key. Creates the key if it doesn't exist. | O(1). The amortized time complexity is O(1) ass... | 2.0.0 |\n| [DECR](https://redis.io/commands/decr/) | Decrements the integer value of a key by one. Uses 0 as initial value if the key doesn't exist. | O(1) | 1.0.0 |\n| [DECRBY](https://redis.io/commands/decrby/) | Decrements a number from the integer value of a key. Uses 0 as initial value if the key doesn't exist. | O(1) | 1.0.0 |\n| [DELEX](https://redis.io/commands/delex/) | Conditionally removes the specified key based on value or digest comparison. | O(1) for IFEQ/IFNE, O(N) for IFDEQ/IFDNE where ... | 8.4.0 |\n| [DIGEST](https://redis.io/commands/digest/) | Returns the XXH3 hash of a string value. | O(N) where N is the length of the string value. | 8.4.0 |\n| [GET](https://redis.io/commands/get/) | Returns the string value of a key. | O(1) | 1.0.0 |\n| [GETDEL](https://redis.io/commands/getdel/) | Returns the string value of a key after deleting the key. | O(1) | 6.2.0 |\n| [GETEX](https://redis.io/commands/getex/) | Returns the string value of a key after setting its expiration time. | O(1) | 6.2.0 |\n| [GETRANGE](https://redis.io/commands/getrange/) | Returns a substring of the string stored at a key. | O(N) where N is the length of the returned stri... | 2.4.0 |\n| [GETSET](https://redis.io/commands/getset/) | Returns the previous string value of a key after setting it to a new value. | O(1) | 1.0.0 |\n\n#### String command summary (part 2)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [INCR](https://redis.io/commands/incr/) | Increments the integer value of a key by one. Uses 0 as initial value if the key doesn't exist. | O(1) | 1.0.0 |\n| [INCRBY](https://redis.io/commands/incrby/) | Increments the integer value of a key by a number. Uses 0 as initial value if the key doesn't exist. | O(1) | 1.0.0 |\n| [INCRBYFLOAT](https://redis.io/commands/incrbyfloat/) | Increment the floating point value of a key by a number. Uses 0 as initial value if the key doesn't exist. | O(1) | 2.6.0 |\n| [INCREX](https://redis.io/commands/increx/) | Increments the numeric value of a key by a number and sets its expiration time. Uses 0 as initial value if the key doesn't exist. | O(1) | 8.8.0 |\n| [LCS](https://redis.io/commands/lcs/) | Finds the longest common substring. | O(N*M) where N and M are the lengths of s1 and ... | 7.0.0 |\n| [MGET](https://redis.io/commands/mget/) | Atomically returns the string values of one or more keys. | O(N) where N is the number of keys to retrieve. | 1.0.0 |\n| [MSET](https://redis.io/commands/mset/) | Atomically creates or modifies the string values of one or more keys. | O(N) where N is the number of keys to set. | 1.0.1 |\n| [MSETEX](https://redis.io/commands/msetex/) | 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. | O(N) where N is the number of keys to set. | 8.4.0 |\n| [MSETNX](https://redis.io/commands/msetnx/) | Atomically modifies the string values of one or more keys only when all keys don't exist. | O(N) where N is the number of keys to set. | 1.0.1 |\n| [PSETEX](https://redis.io/commands/psetex/) | Sets both string value and expiration time in milliseconds of a key. The key is created if it doesn't exist. | O(1) | 2.6.0 |\n\n#### String command summary (part 3)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [SET](https://redis.io/commands/set/) | Sets the string value of a key, ignoring its type. The key is created if it doesn't exist. | O(1) | 1.0.0 |\n| [SETEX](https://redis.io/commands/setex/) | Sets the string value and expiration time of a key. Creates the key if it doesn't exist. | O(1) | 2.0.0 |\n| [SETNX](https://redis.io/commands/setnx/) | Set the string value of a key only when the key doesn't exist. | O(1) | 1.0.0 |\n| [SETRANGE](https://redis.io/commands/setrange/) | Overwrites a part of a string value with another by an offset. Creates the key if it doesn't exist. | O(1), not counting the time taken to copy the n... | 2.2.0 |\n| [STRLEN](https://redis.io/commands/strlen/) | Returns the length of a string value. | O(1) | 2.2.0 |\n| [SUBSTR](https://redis.io/commands/substr/) | Returns a substring from a string value. | O(N) where N is the length of the returned stri... | 1.0.0 |\n\n\n\nRedis strings store sequences of bytes, including text, serialized objects, and binary arrays.\nAs such, strings are the simplest type of value you can associate with a Redis key.\nThey're often used for caching, but they support additional functionality that lets you implement counters and perform bitwise operations, too.\n\nSince Redis keys are strings, when we use the string type as a value too,\nwe are mapping a string to another string. The string data type is useful\nfor a number of use cases, like caching HTML fragments or pages.\n\nFoundational: Set and retrieve string values using SET and GET (overwrites existing values)\n\n**Commands:** SET, GET\n\n**Complexity:**\n- SET: O(1)\n- GET: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nAs you can see using the [`SET`](https://redis.io/docs/latest/commands/set) and the [`GET`](https://redis.io/docs/latest/commands/get) commands are the way we set\nand retrieve a string value. Note that [`SET`](https://redis.io/docs/latest/commands/set) will replace any existing value\nalready stored into the key, in the case that the key already exists, even if\nthe key is associated with a non-string value. So [`SET`](https://redis.io/docs/latest/commands/set) performs an assignment.\n\nValues can be strings (including binary data) of every kind, for instance you\ncan store a jpeg image inside a value. A value can't be bigger than 512 MB.\n\nThe [`SET`](https://redis.io/docs/latest/commands/set) command has interesting options that are provided as additional\narguments. For example, I may ask [`SET`](https://redis.io/docs/latest/commands/set) to fail if the key already exists,\nor the opposite, that it only succeed if the key already exists:\n\nConditional SET operations: Use NX and XX options to set a key only if it doesn't exist yet, or only if it already exists\n\n**Difficulty:** Intermediate\n\n**Builds upon:** set_get\n\n**Commands:** SET\n\n**Complexity:**\n- SET: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nStarting with Redis 8.4, [`SET`](https://redis.io/docs/latest/commands/set) can also check the key's current value before it writes.\nWith the `IFEQ` option, `SET` succeeds only if the current value equals the one you pass.\nThis lets a client update a value it read earlier without overwriting a change that another client made in the meantime, a pattern known as compare-and-swap (CAS).\nThe [`DELEX`](https://redis.io/docs/latest/commands/delex) command applies the same check to deletion (compare-and-delete):\n\nCompare-and-swap and compare-and-delete: Use the IFEQ option with SET and DELEX to update or delete a key only if its value hasn't changed since you read it\n\n**Difficulty:** Intermediate\n\n**Builds upon:** setnx_xx\n\n**Commands:** SET, GET, DELEX\n\n**Complexity:**\n- SET: O(1)\n- GET: O(1)\n- DELEX: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nThe `IFNE` option reverses the check, and the `IFDEQ` and `IFDNE` options compare a hash digest of the value instead of the value itself.\nUse [`DIGEST`](https://redis.io/docs/latest/commands/digest) to get the digest of a key's value.\n\nThere are a number of other commands for operating on strings. For example\nthe [`GETSET`](https://redis.io/docs/latest/commands/getset) command sets a key to a new value, returning the old value as the\nresult. You can use this command, for example, if you have a\nsystem that increments a Redis key using [`INCR`](https://redis.io/docs/latest/commands/incr)\nevery time your web site receives a new visitor. You may want to collect this\ninformation once every hour, without losing a single increment.\nYou can [`GETSET`](https://redis.io/docs/latest/commands/getset) the key, assigning it the new value of \"0\" and reading the\nold value back.\n\nThe ability to set or retrieve the value of multiple keys in a single\ncommand is also useful for reduced latency. For this reason there are\nthe [`MSET`](https://redis.io/docs/latest/commands/mset) and [`MGET`](https://redis.io/docs/latest/commands/mget) commands:\n\nSet and retrieve multiple values using MSET and MGET when you need to reduce round trips to the server\n\n**Builds upon:** set_get\n\n**Commands:** MSET, MGET\n\n**Complexity:**\n- MSET: O(N)\n- MGET: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nWhen [`MGET`](https://redis.io/docs/latest/commands/mget) is used, Redis returns an array of values."},{"id":"strings-as-counters","title":"Strings as counters","role":"content","text":"Even if strings are the basic values of Redis, there are interesting operations\nyou can perform with them. For instance, one is atomic increment:\n\nAtomic counters: Increment string values using INCR and INCRBY when you need thread-safe operations (initializes to 0 if key doesn't exist)\n\n**Builds upon:** set_get\n\n**Commands:** SET, INCR, INCRBY\n\n**Complexity:**\n- SET: O(1)\n- INCR: O(1)\n- INCRBY: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nThe [`INCR`](https://redis.io/docs/latest/commands/incr) command parses the string value as an integer,\nincrements it by one, and finally sets the obtained value as the new value.\nThere are other similar commands like [`INCRBY`](https://redis.io/docs/latest/commands/incrby),\n[`DECR`](https://redis.io/docs/latest/commands/decr) and [`DECRBY`](https://redis.io/docs/latest/commands/decrby). Internally it's\nalways the same command, acting in a slightly different way.\n\nWhat does it mean that INCR is atomic?\nThat even multiple clients issuing INCR against\nthe same key will never enter into a race condition. For instance, it will never\nhappen that client 1 reads \"10\", client 2 reads \"10\" at the same time, both\nincrement to 11, and set the new value to 11. The final value will always be\n12 and the read-increment-set operation is performed while all the other\nclients are not executing a command at the same time."},{"id":"limits","title":"Limits","role":"limits","text":"By default, a single Redis string can be a maximum of 512 MB."},{"id":"bitwise-and-bitfield-operations","title":"Bitwise and bitfield operations","role":"content","text":"To perform bitwise operations on a string, see the [bitmaps data type](https://redis.io/docs/latest/develop/data-types/strings/bitmaps) docs. To store and manipulate integer values within a string, see the [bitfields data type](https://redis.io/docs/latest/develop/data-types/strings/bitfields) docs."},{"id":"performance","title":"Performance","role":"performance","text":"Most string operations are O(1), which means they're highly efficient.\nHowever, be careful with the [`SUBSTR`](https://redis.io/docs/latest/commands/substr), [`GETRANGE`](https://redis.io/docs/latest/commands/getrange), and [`SETRANGE`](https://redis.io/docs/latest/commands/setrange) commands, which can be O(n).\nThese random-access string commands may cause performance issues when dealing with large strings."},{"id":"alternatives","title":"Alternatives","role":"content","text":"If you're storing structured data as a serialized string, you may also want to consider Redis [hashes](https://redis.io/docs/latest/develop/data-types/hashes) or [JSON](https://redis.io/docs/latest/develop/data-types/json)."},{"id":"learn-more","title":"Learn more","role":"related","text":"* [Redis Strings Explained](https://www.youtube.com/watch?v=7CUt4yWeRQE) is a short, comprehensive video explainer on Redis strings.\n* [Redis University's RU101](https://university.redis.com/courses/ru101/) covers Redis strings in detail."}],"examples":[{"id":"string-command-summary-ex0","language":"plaintext","code":"> SET bike:1 Deimos\nOK\n> GET bike:1\n\"Deimos\"","section_id":"string-command-summary"},{"id":"string-command-summary-ex1","language":"csharp","code":"var res1 = db.StringSet(\"bike:1\", \"Deimos\");\n        Console.WriteLine(res1); // true\n        var res2 = db.StringGet(\"bike:1\");\n        Console.WriteLine(res2); // Deimos","section_id":"string-command-summary"},{"id":"string-command-summary-ex2","language":"go","code":"res1, err := rdb.Set(ctx, \"bike:1\", \"Deimos\", 0).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> OK\n\n\tres2, err := rdb.Get(ctx, \"bike:1\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> Deimos","section_id":"string-command-summary"},{"id":"string-command-summary-ex3","language":"java","code":"CompletableFuture<Void> setAndGet = asyncCommands.set(\"bike:1\", \"Deimos\").thenCompose(v -> {\n                System.out.println(v); // >>> OK\n                return asyncCommands.get(\"bike:1\");\n            })\n                    .thenAccept(System.out::println) // >>> Deimos\n                    .toCompletableFuture();","section_id":"string-command-summary"},{"id":"string-command-summary-ex4","language":"java","code":"Mono<Void> setAndGet = reactiveCommands.set(\"bike:1\", \"Deimos\").doOnNext(v -> {\n                System.out.println(v); // OK\n            }).flatMap(v -> reactiveCommands.get(\"bike:1\")).doOnNext(res -> {\n                System.out.println(res); // Deimos\n            }).then();","section_id":"string-command-summary"},{"id":"string-command-summary-ex5","language":"java","code":"String res1 = jedis.set(\"bike:1\", \"Deimos\");\n      System.out.println(res1); // OK\n      String res2 = jedis.get(\"bike:1\");\n      System.out.println(res2); // Deimos","section_id":"string-command-summary"},{"id":"string-command-summary-ex6","language":"javascript","code":"const res1 = await client.set(\"bike:1\", \"Deimos\");\nconsole.log(res1);  // OK\nconst res2 = await client.get(\"bike:1\");\nconsole.log(res2);  // Deimos","section_id":"string-command-summary"},{"id":"string-command-summary-ex7","language":"php","code":"$res1 = $r->set('bike:1', 'Deimos');\n        echo \"$res1\" . PHP_EOL;\n        // >>> OK\n\n        $res2 = $r->get('bike:1');\n        echo \"$res2\" . PHP_EOL;\n        // >>> Deimos","section_id":"string-command-summary"},{"id":"string-command-summary-ex8","language":"python","code":"res1 = r.set(\"bike:1\", \"Deimos\")\nprint(res1)  # True\nres2 = r.get(\"bike:1\")\nprint(res2)  # Deimos","section_id":"string-command-summary"},{"id":"string-command-summary-ex9","language":"ruby","code":"res1 = r.set('bike:1', 'Deimos')\nputs res1 # >>> OK\n\nres2 = r.get('bike:1')\nputs res2 # >>> Deimos","section_id":"string-command-summary"},{"id":"string-command-summary-ex10","language":"rust","code":"if let Ok(res) = r.set(\"bike:1\", \"Deimos\").await {\n            let res: String = res;\n            println!(\"{res}\");    // >>> OK\n        }\n\n        match r.get(\"bike:1\").await {\n            Ok(res) => {\n                let res: String = res;\n                println!(\"{res}\");   // >>> Deimos\n            },\n            Err(e) => {\n                println!(\"Error getting foo: {e}\");\n                return;\n            }\n        };","section_id":"string-command-summary"},{"id":"string-command-summary-ex11","language":"rust","code":"if let Ok(res) = r.set(\"bike:1\", \"Deimos\") {\n            let res: String = res;\n            println!(\"{res}\");    // >>> OK\n        }\n\n        match r.get(\"bike:1\") {\n            Ok(res) => {\n                let res: String = res;\n                println!(\"{res}\");   // >>> Deimos\n            },\n            Err(e) => {\n                println!(\"Error getting bike:1: {e}\");\n                return;\n            }\n        };","section_id":"string-command-summary"},{"id":"string-command-summary-ex12","language":"plaintext","code":"# Recreate the bike:1 key so this example runs on its own.\n> SET bike:1 Deimos\nOK\n> SET bike:1 bike NX\n(nil)\n> SET bike:1 bike XX\nOK","section_id":"string-command-summary"},{"id":"string-command-summary-ex13","language":"csharp","code":"// Recreate the bike:1 key so this example runs on its own.\n        db.StringSet(\"bike:1\", \"Deimos\");\n\n        var res3 = db.StringSet(\"bike:1\", \"bike\", when: When.NotExists);\n        Console.WriteLine(res3); // false\n        Console.WriteLine(db.StringGet(\"bike:1\"));\n        var res4 = db.StringSet(\"bike:1\", \"bike\", when: When.Exists);\n        Console.WriteLine(res4); // true","section_id":"string-command-summary"},{"id":"string-command-summary-ex14","language":"go","code":"// Recreate the bike:1 key so this example runs on its own.\n\trdb.Set(ctx, \"bike:1\", \"Deimos\", 0)\n\n\tres3, err := rdb.SetNX(ctx, \"bike:1\", \"bike\", 0).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3) // >>> false\n\n\tres4, err := rdb.Get(ctx, \"bike:1\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4) // >>> Deimos\n\n\tres5, err := rdb.SetXX(ctx, \"bike:1\", \"bike\", 0).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res5) // >>> OK","section_id":"string-command-summary"},{"id":"string-command-summary-ex15","language":"java","code":"// Recreate the bike:1 key so this example runs on its own.\n            CompletableFuture<Void> setnx = asyncCommands.set(\"bike:1\", \"Deimos\")\n                    .thenCompose(setup -> asyncCommands.setnx(\"bike:1\", \"bike\")).thenCompose(v -> {\n                        System.out.println(v); // >>> false (because key already exists)\n                        return asyncCommands.get(\"bike:1\");\n                    })\n                    .thenAccept(System.out::println) // >>> Deimos (value is unchanged)\n                    .toCompletableFuture();\n            setnx.join();\n\n            // set the value to \"bike\" if it already exists\n            CompletableFuture<Void> setxx = asyncCommands.set(\"bike:1\", \"bike\", SetArgs.Builder.xx())\n                    .thenAccept(System.out::println) // >>> OK\n                    .toCompletableFuture();\n            setxx.join();","section_id":"string-command-summary"},{"id":"string-command-summary-ex16","language":"java","code":"// Recreate the bike:1 key so this example runs on its own.\n            Mono<Void> setnx = reactiveCommands.set(\"bike:1\", \"Deimos\")\n                    .flatMap(setup -> reactiveCommands.setnx(\"bike:1\", \"bike\")).doOnNext(v -> {\n                        System.out.println(v); // false (because key already exists)\n                    }).flatMap(v -> reactiveCommands.get(\"bike:1\")).doOnNext(res -> {\n                        System.out.println(res); // Deimos (value is unchanged)\n                    }).then();\n\n            Mono<Void> setxx = reactiveCommands.set(\"bike:1\", \"bike\", SetArgs.Builder.xx()).doOnNext(res -> {\n                System.out.println(res); // OK\n            }).then();","section_id":"string-command-summary"},{"id":"string-command-summary-ex17","language":"java","code":"// Recreate the bike:1 key so this example runs on its own.\n      jedis.set(\"bike:1\", \"Deimos\");\n\n      Long res3 = jedis.setnx(\"bike:1\", \"bike\");\n      System.out.println(res3); // 0 (because key already exists)\n      System.out.println(jedis.get(\"bike:1\")); // Deimos (value is unchanged)\n      String res4 = jedis.set(\"bike:1\", \"bike\", SetParams.setParams().xx()); // set the value to \"bike\" if it\n      // already\n      // exists\n      System.out.println(res4); // OK","section_id":"string-command-summary"},{"id":"string-command-summary-ex18","language":"javascript","code":"// Recreate the bike:1 key so this example runs on its own.\nawait client.set(\"bike:1\", \"Deimos\");\n\nconst res3 = await client.set(\"bike:1\", \"bike\", {'NX': true});\nconsole.log(res3);  // null\nconsole.log(await client.get(\"bike:1\"));  // Deimos\nconst res4 = await client.set(\"bike:1\", \"bike\", {'XX': true});\nconsole.log(res4);  // OK","section_id":"string-command-summary"},{"id":"string-command-summary-ex19","language":"php","code":"// Recreate the bike:1 key so this example runs on its own.\n        $r->set('bike:1', 'Deimos');\n\n        $res3 = $r->set('bike:1', 'bike', 'nx');\n        echo var_export($res3, true) . PHP_EOL;\n        // >>> NULL\n        \n        echo $r->get('bike:1') . PHP_EOL;\n        // >>> Deimos\n\n        $res4 = $r->set('bike:1', 'bike', 'xx');\n        echo \"$res4\" . PHP_EOL;\n        // >>> OK","section_id":"string-command-summary"},{"id":"string-command-summary-ex20","language":"python","code":"# Recreate the bike:1 key so this example runs on its own.\nr.set(\"bike:1\", \"Deimos\")\n\nres3 = r.set(\"bike:1\", \"bike\", nx=True)\nprint(res3)  # None\nprint(r.get(\"bike:1\"))  # Deimos\nres4 = r.set(\"bike:1\", \"bike\", xx=True)\nprint(res4)  # True","section_id":"string-command-summary"},{"id":"string-command-summary-ex21","language":"ruby","code":"# Recreate the bike:1 key so this example runs on its own.\nr.set('bike:1', 'Deimos')\n\nres3 = r.set('bike:1', 'bike', nx: true)\nputs res3 # >>> false\n\nputs r.get('bike:1') # >>> Deimos\n\nres4 = r.set('bike:1', 'bike', xx: true)\nputs res4 # >>> true","section_id":"string-command-summary"},{"id":"string-command-summary-ex22","language":"rust","code":"// Recreate the bike:1 key so this example runs on its own.\n        let _: () = r.set(\"bike:1\", \"Deimos\").await.expect(\"Failed to set\");\n\n        if let Ok(res) = r.set_options(\"bike:1\", \"bike\", redis::SetOptions::default().conditional_set(ExistenceCheck::NX)).await {\n            let res: bool = res;\n            println!(\"{res}\");    // >>> false\n        }\n\n        match r.get(\"bike:1\").await {\n            Ok(res) => {\n                let res: String = res;\n                println!(\"{res}\");   // >>> Deimos\n            },\n            Err(e) => {\n                println!(\"Error getting foo: {e}\");\n                return;\n            }\n        };\n\n        if let Ok(res) = r.set_options(\"bike:1\", \"bike\", redis::SetOptions::default().conditional_set(ExistenceCheck::XX)).await {\n            let res: String = res;\n            println!(\"{res}\");    // >>> OK\n        }\n\n        match r.get(\"bike:1\").await {\n            Ok(res) => {\n                let res: String = res;\n                println!(\"{res}\");   // >>> bike\n            },\n            Err(e) => {\n                println!(\"Error getting foo: {e}\");\n                return;\n            }\n        };","section_id":"string-command-summary"},{"id":"string-command-summary-ex23","language":"rust","code":"// Recreate the bike:1 key so this example runs on its own.\n        let _: () = r.set(\"bike:1\", \"Deimos\").expect(\"Failed to set\");\n\n        if let Ok(res) = r.set_options(\"bike:1\", \"bike\", redis::SetOptions::default().conditional_set(ExistenceCheck::NX)) {\n            let res: bool = res;\n            println!(\"{res}\");    // >>> false\n        }\n\n        match r.get(\"bike:1\") {\n            Ok(res) => {\n                let res: String = res;\n                println!(\"{res}\");   // >>> Deimos\n            },\n            Err(e) => {\n                println!(\"Error getting bike:1: {e}\");\n                return;\n            }\n        };\n\n        if let Ok(res) = r.set_options(\"bike:1\", \"bike\", redis::SetOptions::default().conditional_set(ExistenceCheck::XX)) {\n            let res: String = res;\n            println!(\"{res}\");    // >>> OK\n        }\n        \n        match r.get(\"bike:1\") {\n            Ok(res) => {\n                let res: String = res;\n                println!(\"{res}\");   // >>> bike\n            },\n            Err(e) => {\n                println!(\"Error getting bike:1: {e}\");\n                return;\n            }\n        };","section_id":"string-command-summary"},{"id":"string-command-summary-ex24","language":"plaintext","code":"> SET bike:1 Deimos\nOK\n> SET bike:1 Ares IFEQ Deimos\nOK\n> SET bike:1 Zeus IFEQ Deimos\n(nil)\n> GET bike:1\n\"Ares\"\n> DELEX bike:1 IFEQ Deimos\n(integer) 0\n> DELEX bike:1 IFEQ Ares\n(integer) 1","section_id":"string-command-summary"},{"id":"string-command-summary-ex25","language":"csharp","code":"db.StringSet(\"bike:1\", \"Deimos\");\n\n        var res9 = db.StringSet(\"bike:1\", \"Ares\", when: ValueCondition.Equal(\"Deimos\"));\n        Console.WriteLine(res9); // True\n        var res10 = db.StringSet(\"bike:1\", \"Zeus\", when: ValueCondition.Equal(\"Deimos\"));\n        Console.WriteLine(res10); // False\n        Console.WriteLine(db.StringGet(\"bike:1\")); // Ares\n\n        var res11 = db.StringDelete(\"bike:1\", ValueCondition.Equal(\"Deimos\"));\n        Console.WriteLine(res11); // False\n        var res12 = db.StringDelete(\"bike:1\", ValueCondition.Equal(\"Ares\"));\n        Console.WriteLine(res12); // True","section_id":"string-command-summary"},{"id":"string-command-summary-ex26","language":"go","code":"rdb.Set(ctx, \"bike:1\", \"Deimos\", 0)\n\n\tres11, err := rdb.SetIFEQ(ctx, \"bike:1\", \"Ares\", \"Deimos\", 0).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res11) // >>> OK\n\n\t// The value no longer matches, so SetIFEQ returns redis.Nil.\n\t_, err = rdb.SetIFEQ(ctx, \"bike:1\", \"Zeus\", \"Deimos\", 0).Result()\n\n\tif err != nil && err != redis.Nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(err == redis.Nil) // >>> true\n\n\tres12, err := rdb.Get(ctx, \"bike:1\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res12) // >>> Ares\n\n\tres13, err := rdb.DelExArgs(ctx, \"bike:1\", redis.DelExArgs{\n\t\tMode: \"IFEQ\", MatchValue: \"Deimos\",\n\t}).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res13) // >>> 0\n\n\tres14, err := rdb.DelExArgs(ctx, \"bike:1\", redis.DelExArgs{\n\t\tMode: \"IFEQ\", MatchValue: \"Ares\",\n\t}).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res14) // >>> 1","section_id":"string-command-summary"},{"id":"string-command-summary-ex27","language":"java","code":"CompletableFuture<Void> casCad = asyncCommands.set(\"bike:1\", \"Deimos\")\n                    .thenCompose(setup -> asyncCommands.set(\"bike:1\", \"Ares\",\n                            SetArgs.Builder.compareCondition(CompareCondition.valueEq(\"Deimos\"))))\n                    .thenCompose(v -> {\n                        System.out.println(v); // >>> OK\n                        return asyncCommands.set(\"bike:1\", \"Zeus\",\n                                SetArgs.Builder.compareCondition(CompareCondition.valueEq(\"Deimos\")));\n                    }).thenCompose(v -> {\n                        System.out.println(v); // >>> null\n                        return asyncCommands.get(\"bike:1\");\n                    }).thenCompose(v -> {\n                        System.out.println(v); // >>> Ares\n                        return asyncCommands.delex(\"bike:1\", CompareCondition.valueEq(\"Deimos\"));\n                    }).thenCompose(v -> {\n                        System.out.println(v); // >>> 0\n                        return asyncCommands.delex(\"bike:1\", CompareCondition.valueEq(\"Ares\"));\n                    })\n                    .thenAccept(System.out::println) // >>> 1\n                    .toCompletableFuture();","section_id":"string-command-summary"},{"id":"string-command-summary-ex28","language":"java","code":"Mono<Void> casCad = reactiveCommands.set(\"bike:1\", \"Deimos\")\n                    .flatMap(setup -> reactiveCommands.set(\"bike:1\", \"Ares\",\n                            SetArgs.Builder.compareCondition(CompareCondition.valueEq(\"Deimos\"))))\n                    .doOnNext(res -> {\n                        System.out.println(res); // OK\n                    })\n                    // A failed condition returns an empty Mono, so check for an element.\n                    .flatMap(v -> reactiveCommands.set(\"bike:1\", \"Zeus\",\n                            SetArgs.Builder.compareCondition(CompareCondition.valueEq(\"Deimos\"))).hasElement())\n                    .doOnNext(res -> {\n                        System.out.println(res); // false\n                    }).flatMap(v -> reactiveCommands.get(\"bike:1\")).doOnNext(res -> {\n                        System.out.println(res); // Ares\n                    }).flatMap(v -> reactiveCommands.delex(\"bike:1\", CompareCondition.valueEq(\"Deimos\")))\n                    .doOnNext(res -> {\n                        System.out.println(res); // 0\n                    }).flatMap(v -> reactiveCommands.delex(\"bike:1\", CompareCondition.valueEq(\"Ares\")))\n                    .doOnNext(res -> {\n                        System.out.println(res); // 1\n                    }).then();","section_id":"string-command-summary"},{"id":"string-command-summary-ex29","language":"java","code":"jedis.set(\"bike:1\", \"Deimos\");\n\n      String res9 = jedis.set(\"bike:1\", \"Ares\",\n          SetParams.setParams().condition(CompareCondition.valueEq(\"Deimos\")));\n      System.out.println(res9); // OK\n      String res10 = jedis.set(\"bike:1\", \"Zeus\",\n          SetParams.setParams().condition(CompareCondition.valueEq(\"Deimos\")));\n      System.out.println(res10); // null\n      System.out.println(jedis.get(\"bike:1\")); // Ares\n\n      long res11 = jedis.delex(\"bike:1\", CompareCondition.valueEq(\"Deimos\"));\n      System.out.println(res11); // 0\n      long res12 = jedis.delex(\"bike:1\", CompareCondition.valueEq(\"Ares\"));\n      System.out.println(res12); // 1","section_id":"string-command-summary"},{"id":"string-command-summary-ex30","language":"javascript","code":"await client.set(\"bike:1\", \"Deimos\");\n\nconst res9 = await client.set(\"bike:1\", \"Ares\", {\n  condition: 'IFEQ', matchValue: \"Deimos\"\n});\nconsole.log(res9);  // OK\nconst res10 = await client.set(\"bike:1\", \"Zeus\", {\n  condition: 'IFEQ', matchValue: \"Deimos\"\n});\nconsole.log(res10);  // null\nconsole.log(await client.get(\"bike:1\"));  // Ares\n\nconst res11 = await client.delEx(\"bike:1\", {\n  condition: 'IFEQ', matchValue: \"Deimos\"\n});\nconsole.log(res11);  // 0\nconst res12 = await client.delEx(\"bike:1\", {\n  condition: 'IFEQ', matchValue: \"Ares\"\n});\nconsole.log(res12);  // 1","section_id":"string-command-summary"},{"id":"string-command-summary-ex31","language":"php","code":"$r->set('bike:1', 'Deimos');\n\n        $res9 = $r->set('bike:1', 'Ares', null, null, 'IFEQ', 'Deimos');\n        echo \"$res9\" . PHP_EOL;\n        // >>> OK\n\n        $res10 = $r->set('bike:1', 'Zeus', null, null, 'IFEQ', 'Deimos');\n        echo var_export($res10, true) . PHP_EOL;\n        // >>> NULL\n\n        echo $r->get('bike:1') . PHP_EOL;\n        // >>> Ares\n\n        $res11 = $r->delex('bike:1', 'IFEQ', 'Deimos');\n        echo \"$res11\" . PHP_EOL;\n        // >>> 0\n\n        $res12 = $r->delex('bike:1', 'IFEQ', 'Ares');\n        echo \"$res12\" . PHP_EOL;\n        // >>> 1","section_id":"string-command-summary"},{"id":"string-command-summary-ex32","language":"python","code":"r.set(\"bike:1\", \"Deimos\")\n\nres9 = r.set(\"bike:1\", \"Ares\", ifeq=\"Deimos\")\nprint(res9)  # True\nres10 = r.set(\"bike:1\", \"Zeus\", ifeq=\"Deimos\")\nprint(res10)  # None\nprint(r.get(\"bike:1\"))  # Ares\n\nres11 = r.delex(\"bike:1\", ifeq=\"Deimos\")\nprint(res11)  # 0\nres12 = r.delex(\"bike:1\", ifeq=\"Ares\")\nprint(res12)  # 1","section_id":"string-command-summary"},{"id":"string-command-summary-ex33","language":"rust","code":"let _: () = r.set(\"bike:1\", \"Deimos\").await.expect(\"Failed to set\");\n\n        let res: Option<String> = r\n            .set_options(\"bike:1\", \"Ares\", redis::SetOptions::default().value_comparison(ValueComparison::ifeq(\"Deimos\")))\n            .await.expect(\"Failed to set\");\n        println!(\"{res:?}\");    // >>> Some(\"OK\")\n\n        let res: Option<String> = r\n            .set_options(\"bike:1\", \"Zeus\", redis::SetOptions::default().value_comparison(ValueComparison::ifeq(\"Deimos\")))\n            .await.expect(\"Failed to set\");\n        println!(\"{res:?}\");    // >>> None\n\n        let res: String = r.get(\"bike:1\").await.expect(\"Failed to get\");\n        println!(\"{res}\");   // >>> Ares\n\n        let res: usize = r.del_ex(\"bike:1\", ValueComparison::ifeq(\"Deimos\")).await.expect(\"Failed to delete\");\n        println!(\"{res}\");    // >>> 0\n\n        let res: usize = r.del_ex(\"bike:1\", ValueComparison::ifeq(\"Ares\")).await.expect(\"Failed to delete\");\n        println!(\"{res}\");    // >>> 1","section_id":"string-command-summary"},{"id":"string-command-summary-ex34","language":"rust","code":"let _: () = r.set(\"bike:1\", \"Deimos\").expect(\"Failed to set\");\n\n        let res: Option<String> = r\n            .set_options(\"bike:1\", \"Ares\", redis::SetOptions::default().value_comparison(ValueComparison::ifeq(\"Deimos\")))\n            .expect(\"Failed to set\");\n        println!(\"{res:?}\");    // >>> Some(\"OK\")\n\n        let res: Option<String> = r\n            .set_options(\"bike:1\", \"Zeus\", redis::SetOptions::default().value_comparison(ValueComparison::ifeq(\"Deimos\")))\n            .expect(\"Failed to set\");\n        println!(\"{res:?}\");    // >>> None\n\n        let res: String = r.get(\"bike:1\").expect(\"Failed to get\");\n        println!(\"{res}\");   // >>> Ares\n\n        let res: usize = r.del_ex(\"bike:1\", ValueComparison::ifeq(\"Deimos\")).expect(\"Failed to delete\");\n        println!(\"{res}\");    // >>> 0\n\n        let res: usize = r.del_ex(\"bike:1\", ValueComparison::ifeq(\"Ares\")).expect(\"Failed to delete\");\n        println!(\"{res}\");    // >>> 1","section_id":"string-command-summary"},{"id":"string-command-summary-ex35","language":"plaintext","code":"> MSET bike:1 \"Deimos\" bike:2 \"Ares\" bike:3 \"Vanth\"\nOK\n> MGET bike:1 bike:2 bike:3\n1) \"Deimos\"\n2) \"Ares\"\n3) \"Vanth\"","section_id":"string-command-summary"},{"id":"string-command-summary-ex36","language":"csharp","code":"var res5 = db.StringSet([\n            new (\"bike:1\", \"Deimos\"), new(\"bike:2\", \"Ares\"), new(\"bike:3\", \"Vanth\")\n        ]);\n        Console.WriteLine(res5);\n        var res6 = db.StringGet([\"bike:1\", \"bike:2\", \"bike:3\"]);\n        Console.WriteLine(string.Join(\", \", res6));","section_id":"string-command-summary"},{"id":"string-command-summary-ex37","language":"go","code":"res6, err := rdb.MSet(ctx, \"bike:1\", \"Deimos\", \"bike:2\", \"Ares\", \"bike:3\", \"Vanth\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res6) // >>> OK\n\n\tres7, err := rdb.MGet(ctx, \"bike:1\", \"bike:2\", \"bike:3\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res7) // >>> [Deimos Ares Vanth]","section_id":"string-command-summary"},{"id":"string-command-summary-ex38","language":"java","code":"Map<String, String> bikeMap = new HashMap<>();\n            bikeMap.put(\"bike:1\", \"Deimos\");\n            bikeMap.put(\"bike:2\", \"Ares\");\n            bikeMap.put(\"bike:3\", \"Vanth\");\n\n            CompletableFuture<Void> mset = asyncCommands.mset(bikeMap).thenCompose(v -> {\n                System.out.println(v); // >>> OK\n                return asyncCommands.mget(\"bike:1\", \"bike:2\", \"bike:3\");\n            })\n                    .thenAccept(System.out::println)\n                    // >>> [KeyValue[bike:1, Deimos], KeyValue[bike:2, Ares], KeyValue[bike:3,\n                    // Vanth]]\n                    .toCompletableFuture();","section_id":"string-command-summary"},{"id":"string-command-summary-ex39","language":"java","code":"Map<String, String> bikeMap = new HashMap<>();\n            bikeMap.put(\"bike:1\", \"Deimos\");\n            bikeMap.put(\"bike:2\", \"Ares\");\n            bikeMap.put(\"bike:3\", \"Vanth\");\n\n            Mono<Void> mset = reactiveCommands.mset(bikeMap).doOnNext(System.out::println) // OK\n                    .flatMap(v -> reactiveCommands.mget(\"bike:1\", \"bike:2\", \"bike:3\").collectList()).doOnNext(res -> {\n                        System.out.println(res); // [KeyValue[bike:1, Deimos], KeyValue[bike:2, Ares], KeyValue[bike:3, Vanth]]\n                    }).then();","section_id":"string-command-summary"},{"id":"string-command-summary-ex40","language":"java","code":"String res5 = jedis.mset(\"bike:1\", \"Deimos\", \"bike:2\", \"Ares\", \"bike:3\", \"Vanth\");\n      System.out.println(res5); // OK\n      List<String> res6 = jedis.mget(\"bike:1\", \"bike:2\", \"bike:3\");\n      System.out.println(res6); // [Deimos, Ares, Vanth]","section_id":"string-command-summary"},{"id":"string-command-summary-ex41","language":"javascript","code":"const res5 = await client.mSet([\n  [\"bike:1\", \"Deimos\"],\n  [\"bike:2\", \"Ares\"],\n  [\"bike:3\", \"Vanth\"]\n]);\n\nconsole.log(res5);  // OK\nconst res6 = await client.mGet([\"bike:1\", \"bike:2\", \"bike:3\"]);\nconsole.log(res6);  // ['Deimos', 'Ares', 'Vanth']","section_id":"string-command-summary"},{"id":"string-command-summary-ex42","language":"php","code":"$res5 = $r->mset([\n            'bike:1' => 'Deimos', 'bike:2' => 'Ares', 'bike:3' => 'Vanth'\n        ]);\n        echo \"$res5\" . PHP_EOL;\n        // >>> OK\n\n        $res6 = $r->mget(['bike:1', 'bike:2', 'bike:3']);\n        echo json_encode($res6) . PHP_EOL;\n        // >>> [\"Deimos\",\"Ares\",\"Vanth\"]","section_id":"string-command-summary"},{"id":"string-command-summary-ex43","language":"python","code":"res5 = r.mset({\"bike:1\": \"Deimos\", \"bike:2\": \"Ares\", \"bike:3\": \"Vanth\"})\nprint(res5)  # True\nres6 = r.mget([\"bike:1\", \"bike:2\", \"bike:3\"])\nprint(res6)  # ['Deimos', 'Ares', 'Vanth']","section_id":"string-command-summary"},{"id":"string-command-summary-ex44","language":"ruby","code":"res5 = r.mset('bike:1', 'Deimos', 'bike:2', 'Ares', 'bike:3', 'Vanth')\nputs res5 # >>> OK\n\nres6 = r.mget('bike:1', 'bike:2', 'bike:3')\nputs res6.inspect # >>> [\"Deimos\", \"Ares\", \"Vanth\"]","section_id":"string-command-summary"},{"id":"string-command-summary-ex45","language":"rust","code":"if let Ok(res) = r.mset(&[(\"bike:1\", \"Deimos\"), (\"bike:2\", \"Ares\"), (\"bike:3\", \"Vanth\")]).await {\n            let res: String = res;\n            println!(\"{res}\");    // >>> OK\n        }\n\n        match r.mget(&[\"bike:1\", \"bike:2\", \"bike:3\"]).await {\n            Ok(res) => {\n                let res: Vec<String> = res;\n                println!(\"{res:?}\");   // >>> [\"Deimos\", \"Ares\", \"Vanth\"]\n            },\n            Err(e) => {\n                println!(\"Error getting foo: {e}\");\n                return;\n            }\n        };","section_id":"string-command-summary"},{"id":"string-command-summary-ex46","language":"rust","code":"if let Ok(res) = r.mset(&[(\"bike:1\", \"Deimos\"), (\"bike:2\", \"Ares\"), (\"bike:3\", \"Vanth\")]) {\n            let res: String = res;\n            println!(\"{res}\");    // >>> OK\n        }\n\n        match r.mget(&[\"bike:1\", \"bike:2\", \"bike:3\"]) {\n            Ok(res) => {\n                let res: Vec<String> = res;\n                println!(\"{res:?}\");   // >>> [\"Deimos\", \"Ares\", \"Vanth\"]\n            },\n            Err(e) => {\n                println!(\"Error getting values: {e}\");\n                return;\n            }\n        };","section_id":"string-command-summary"},{"id":"strings-as-counters-ex0","language":"plaintext","code":"> SET total_crashes 0\nOK\n> INCR total_crashes\n(integer) 1\n> INCRBY total_crashes 10\n(integer) 11","section_id":"strings-as-counters"},{"id":"strings-as-counters-ex1","language":"csharp","code":"db.StringSet(\"total_crashes\", 0);\n        var res7 = db.StringIncrement(\"total_crashes\");\n        Console.WriteLine(res7); // 1\n        var res8 = db.StringIncrement(\"total_crashes\", 10);\n        Console.WriteLine(res8);","section_id":"strings-as-counters"},{"id":"strings-as-counters-ex2","language":"go","code":"res8, err := rdb.Set(ctx, \"total_crashes\", \"0\", 0).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res8) // >>> OK\n\n\tres9, err := rdb.Incr(ctx, \"total_crashes\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res9) // >>> 1\n\n\tres10, err := rdb.IncrBy(ctx, \"total_crashes\", 10).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res10) // >>> 11","section_id":"strings-as-counters"},{"id":"strings-as-counters-ex3","language":"java","code":"CompletableFuture<Void> incrby = asyncCommands.set(\"total_crashes\", \"0\")\n                    .thenCompose(v -> asyncCommands.incr(\"total_crashes\")).thenCompose(v -> {\n                        System.out.println(v); // >>> 1\n                        return asyncCommands.incrby(\"total_crashes\", 10);\n                    })\n                    .thenAccept(System.out::println) // >>> 11\n                    .toCompletableFuture();","section_id":"strings-as-counters"},{"id":"strings-as-counters-ex4","language":"java","code":"Mono<Void> incrby = reactiveCommands.set(\"total_crashes\", \"0\").flatMap(v -> reactiveCommands.incr(\"total_crashes\"))\n                    .doOnNext(v -> {\n                        System.out.println(v); // 1\n                    }).flatMap(v -> reactiveCommands.incrby(\"total_crashes\", 10)).doOnNext(res -> {\n                        System.out.println(res); // 11\n                    }).then();","section_id":"strings-as-counters"},{"id":"strings-as-counters-ex5","language":"java","code":"jedis.set(\"total_crashes\", \"0\");\n      Long res7 = jedis.incr(\"total_crashes\");\n      System.out.println(res7); // 1\n      Long res8 = jedis.incrBy(\"total_crashes\", 10);\n      System.out.println(res8); // 11","section_id":"strings-as-counters"},{"id":"strings-as-counters-ex6","language":"javascript","code":"await client.set(\"total_crashes\", 0);\nconst res7 = await client.incr(\"total_crashes\");\nconsole.log(res7); // 1\nconst res8 = await client.incrBy(\"total_crashes\", 10);\nconsole.log(res8); // 11","section_id":"strings-as-counters"},{"id":"strings-as-counters-ex7","language":"php","code":"$r->set('total_crashes', 0);\n        $res7 = $r->incr('total_crashes');\n        echo \"$res7\" . PHP_EOL;\n        // >>> 1\n\n        $res8 = $r->incrby('total_crashes', 10);\n        echo \"$res8\" . PHP_EOL;\n        // >>> 11","section_id":"strings-as-counters"},{"id":"strings-as-counters-ex8","language":"python","code":"r.set(\"total_crashes\", 0)\nres7 = r.incr(\"total_crashes\")\nprint(res7)  # 1\nres8 = r.incrby(\"total_crashes\", 10)\nprint(res8)  # 11","section_id":"strings-as-counters"},{"id":"strings-as-counters-ex9","language":"ruby","code":"r.set('total_crashes', 0)\n\nres7 = r.incr('total_crashes')\nputs res7 # >>> 1\n\nres8 = r.incrby('total_crashes', 10)\nputs res8 # >>> 11","section_id":"strings-as-counters"},{"id":"strings-as-counters-ex10","language":"rust","code":"if let Ok(res) = r.set(\"total_crashes\", 0).await {\n            let res: String = res;\n            println!(\"{res}\");    // >>> OK\n        }\n\n        if let Ok(res) = r.incr(\"total_crashes\", 1).await {\n            let res: i32 = res;\n            println!(\"{res}\");    // >>> 1\n        }\n\n        if let Ok(res) = r.incr(\"total_crashes\", 10).await {\n            let res: i32 = res;\n            println!(\"{res}\");    // >>> 11\n        }","section_id":"strings-as-counters"},{"id":"strings-as-counters-ex11","language":"rust","code":"if let Ok(res) = r.set(\"total_crashes\", 0) {\n            let res: String = res;\n            println!(\"{res}\");    // >>> OK\n        }\n\n        if let Ok(res) = r.incr(\"total_crashes\", 1) {\n            let res: i32 = res;\n            println!(\"{res}\");    // >>> 1\n        }\n\n        if let Ok(res) = r.incr(\"total_crashes\", 10) {\n            let res: i32 = res;\n            println!(\"{res}\");    // >>> 11\n        }","section_id":"strings-as-counters"}]}
{"schema_version":2,"id":"develop/data-types/timeseries/configuration","title":"Configuration Parameters","url":"https://redis.io/docs/latest/develop/data-types/timeseries/configuration/","summary":"Redis time series support multiple configuration parameters.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-10T15:45:58+01:00","page_type":"content","content_hash":"7ab27550e55e1f371b5b92e24537e49d129970e83030d26478b1d8bfe13d56c9","sections":[{"id":"redis-open-source---set-configuration-parameters","title":"Redis Open Source - set configuration parameters","role":"overview","text":"Before Redis 8 in Redis Open Source (version 8.0), all time series configuration parameters are load-time parameters.\nUse one of the following methods to set the values of load-time configuration parameters:\n\n- Pass them as command-line arguments following the `loadmodule` argument when starting `redis-server`:\n\n  `redis-server --loadmodule ./{modulename}.so [OPT VAL]...`\n\n- Add them as arguments to the `loadmodule` directive in your configuration file (for example, `redis.conf`):\n\n  `loadmodule ./{modulename}.so [OPT VAL]...`\n\n- Use the `MODULE LOAD path [arg [arg ...]]` command.\n\n- Use the `MODULE LOADEX path [CONFIG name value [CONFIG name value ...]] [ARGS args [args ....]]` command.\n\nStarting with Redis 8.0, most time series configuration parameters are runtime parameters.\nWhile you can set runtime parameters at load time, using the Redis `CONFIG` command is easier and works the same way as with Redis runtime configuration parameters.\n\nThis means:\n\n- `CONFIG SET parameter value [parameter value ...] `\n\n  Set one or more configuration parameters.\n\n- `CONFIG GET parameter [parameter ...]`\n\n  Read the current value of one of more parameters.\n\n- `CONFIG REWRITE`\n\n  Rewrite your Redis configuration file (for example, the `redis.conf` file) to reflect the configuration changes.\n\nStarting with Redis 8.0, you can specify time series configuration parameters directly in your Redis configuration file the same way you would for Redis configuration parameters.\n\nOnce a value is set with `CONFIG SET` or added manually to your configuration file, it will overwrite values set with `--loadmodule`, `loadmodule`, `MODULE LOAD`, or `MODULE LOADEX`.\n\nIn a cluster, you must run `CONFIG SET` and `CONFIG REWRITE` on each node separately.\n\nIn Redis 8.0, new names for the time series configuration parameters were introduced to align the naming with the Redis configuration parameters.\nYou must use the new names when using the `CONFIG` command.\n\nSee also [Redis configuration](https://redis.io/docs/latest/operate/oss_and_stack/management/config)."},{"id":"time-series-configuration-parameters","title":"Time series configuration parameters","role":"content","text":"| Parameter name<br />(version < 8.0) | Parameter name<br />(version &#8805; 8.0) | Run-time | Redis<br />Software | Redis<br />Cloud |\n| :------- | :------- | :------- | :------- | :------- |\n| CHUNK_SIZE_BYTES     | [ts-chunk-size-bytes](#chunk_size_bytes--ts-chunk-size-bytes)    | :white_check_mark:   | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> |\n| COMPACTION_POLICY    | [ts-compaction-policy](#compaction_policy--ts-compaction-policy) | :white_check_mark:   | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> |\n| DUPLICATE_POLICY     | [ts-duplicate-policy](#duplicate_policy--ts-duplicate-policy)    | :white_check_mark:   | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> |\n| RETENTION_POLICY     | [ts-retention-policy](#retention_policy--ts-retention-policy)    | :white_check_mark:   | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> |\n| ENCODING             | [ts-encoding](#encoding--ts-encoding)                            | :white_check_mark:   | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> |\n| IGNORE_MAX_TIME_DIFF | [ts-ignore-max-time-diff](#ignore_max_time_diff--ts-ignore-max-time-diff-and-ignore_max_val_diff--ts-ignore-max-val-diff) | :white_check_mark:   |||\n| IGNORE_MAX_VAL_DIFF  | [ts-ignore-max-val-diff](#ignore_max_time_diff--ts-ignore-max-time-diff-and-ignore_max_val_diff--ts-ignore-max-val-diff)  | :white_check_mark:   |||\n| NUM_THREADS          | [ts-num-threads](#num_threads--ts-num-threads)                   | :white_large_square: | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Not Supported\">&#x274c; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> |\n| [OSS_GLOBAL_PASSWORD](#oss_global_password)  | Deprecated in v8.0                       | :white_check_mark:   |||\n\n---"},{"id":"chunk_size_bytes--ts-chunk-size-bytes","title":"CHUNK_SIZE_BYTES / ts-chunk-size-bytes","role":"content","text":"The initial allocation size, in bytes, for the data part of each new chunk. Actual chunks may consume more memory.\nChanging this value does not affect existing chunks.\n\nType: integer\n\nValid range: `[48 .. 1048576]`; must be a multiple of 8\n\n#### Precedence order\n\nBecause the chunk size can be provided at different levels, the actual precedence of the chunk size is:\n\n1. Key-level policy, as set with [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create)'s and [`TS.ALTER`](https://redis.io/docs/latest/commands/ts.alter)'s `CHUNK_SIZE` optional argument.\n1. The `ts-chunk-size-bytes` configuration parameter.\n1. The hard-coded default: `4096`\n\n#### Example\n\nSet the default chunk size to 1024 bytes:\n\nVersion < 8.0:\n\n[code example]\n\nVersion >= 8.0:\n\n[code example]"},{"id":"compaction_policy--ts-compaction-policy","title":"COMPACTION_POLICY / ts-compaction-policy","role":"content","text":"Default compaction rules for newly created keys with [`TS.ADD`](https://redis.io/docs/latest/commands/ts.add), [`TS.INCRBY`](https://redis.io/docs/latest/commands/ts.incrby), and  [`TS.DECRBY`](https://redis.io/docs/latest/commands/ts.decrby).\n\nType: string\n\nNote that this configuration parameter does not affect keys you create with [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create). To understand why, consider the following scenario: Suppose you define a default compaction policy but then want to manually create an additional compaction rule (using [`TS.CREATERULE`](https://redis.io/docs/latest/commands/ts.createrule)), which requires you to first create an empty destination key (using `TS.CREATE`). This approach creates a problem: the default compaction policy would cause Redis to automatically create undesired compactions for the destination key.\n\nEach rule is separated by a semicolon (`;`), the rule consists of multiple fields that are separated by a colon (`:`):\n\n* Aggregation type: One of the following:\n\n  | Aggregator | Description                                                      |\n  | ---------- | ---------------------------------------------------------------- |\n  | `avg`      | Arithmetic mean of all values                                    |\n  | `sum`      | Sum of all values                                                |\n  | `min`      | Minimum value                                                    |\n  | `max`      | Maximum value                                                    |\n  | `range`    | Difference between the highest and the lowest value              |\n  | `count`    | Number of values                                                 |\n  | `first`    | The value with the lowest timestamp in the bucket                |\n  | `last`     | The value with the highest timestamp in the bucket               |\n  | `std.p`    | Population standard deviation of the values                      |\n  | `std.s`    | Sample standard deviation of the values                          |\n  | `var.p`    | Population variance of the values                                |\n  | `var.s`    | Sample variance of the values                                    |\n  | `twa`      | Time-weighted average of all values (since v1.8)                 |\n\n* Duration of each time bucket - number and the time representation (Example for one minute: `1M`, `60s`, or `60000m`)\n\n    * m - millisecond\n    * s - seconds\n    * M - minute\n    * h - hour\n    * d - day\n\n* Retention time - number and the time representation (Example for one minute: `1M`, `60s`, or `60000m`)\n\n    * m - millisecond\n    * s - seconds\n    * M - minute\n    * h - hour\n    * d - day\n    \n  `0m`, `0s`, `0M`, `0h`, or `0d` means no expiration.\n\n* (Since v1.8):\n\n  Optional: Time bucket alignment - number and the time representation (Example for one minute: `1M`, `60s`, or `60000m`)\n\n    * m - millisecond\n    * s - seconds\n    * M - minute\n    * h - hour\n    * d - day\n\n  Ensure that there is a bucket that starts at exactly _alignTimestamp_ after the Epoch and align all other buckets accordingly. Default value: 0 (aligned with the Epoch). Example: if _bucketDuration_ is 24 hours, setting _alignTimestamp_ to `6h` (6 hours after the Epoch) will ensure that each bucket’s timeframe is [06:00 .. 06:00).\n\n> [!WARNING]\n> In a clustered environment, if you set this configuration parameter, you must use [hash tags](https://redis.io/docs/latest/operate/oss_and_stack/reference/cluster-spec#hash-tags) for all time series key names. This ensures that Redis will create each compaction in the same hash slot as its source key. If you don't, the system may fail to compact the data without displaying any error messages.\n\nWhen a compaction policy is defined, compaction rules are created automatically for newly created time series, and the compaction key name would be:\n  \n* If the time bucket alignment is 0:\n\n   _key_agg_dur_ where _key_ is the key of the source time series, _agg_ is the aggregator (in uppercase), and _dur_ is the bucket duration in milliseconds. Example: `key_SUM_60000`.\n     \n* If the time bucket alignment is not 0:\n\n   _key_agg_dur_aln_ where _key_ is the key of the source time series, _agg_ is the aggregator (in uppercase), _dur_ is the bucket duration in milliseconds, and _aln_ is the time bucket alignment in milliseconds. Example: `key_SUM_60000_1000`.\n\n#### Precedence order\n\n1. The `ts-compaction-policy` configuration parameter.\n1. No compaction rules.\n\n#### Example rules\n\n- `max:1M:1h` - Aggregate using `max` over one-minute windows and retain the last hour\n- `twa:1d:0m:360M` - Aggregate daily [06:00 .. 06:00) using `twa`; no expiration\n\n#### Example\n\nSet a compaction policy composed of 5 compaction rules:\n\nVersion < 8.0:\n\n[code example]\n\nVersion >= 8.0:\n\n[code example]"},{"id":"duplicate_policy--ts-duplicate-policy","title":"DUPLICATE_POLICY / ts-duplicate-policy","role":"content","text":"The default policy for handling insertion ([`TS.ADD`](https://redis.io/docs/latest/commands/ts.add) and [`TS.MADD`](https://redis.io/docs/latest/commands/ts.madd)) of multiple samples with identical timestamps, with one of the following values:\n\n  | policy     | description                                                      |\n  | ---------- | ---------------------------------------------------------------- |\n  | `BLOCK`    | Ignore any newly reported value and reply with an error          |\n  | `FIRST`    | Ignore any newly reported value                                  |\n  | `LAST`     | Override with the newly reported value                           |\n  | `MIN`      | Only override if the value is lower than the existing value      |\n  | `MAX`      | Only override if the value is higher than the existing value     |\n  | `SUM`      | If a previous sample exists, add the new sample to it so that the updated value is equal to (previous + new). If no previous sample exists, set the updated value equal to the new value. |\n\nThe default value is applied to each new time series upon its creation.\n\nType: string\n\n#### Precedence order\n\nBecause the duplication policy can be provided at different levels, the actual precedence of the duplication policy is:\n\n1. [`TS.ADD`](https://redis.io/docs/latest/commands/ts.add)'s `ON_DUPLICATE_POLICY` optional argument.\n1. Key-level policy, as set with [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create)'s and [`TS.ALTER`](https://redis.io/docs/latest/commands/ts.alter)'s `DUPLICATE_POLICY` optional argument.\n1. The `ts-duplicate-policy` configuration parameter.\n1. The hard-coded default: `BLOCK`"},{"id":"retention_policy--ts-retention-policy","title":"RETENTION_POLICY / ts-retention-policy","role":"content","text":"The default retention period, in milliseconds, for newly created keys.\n\nThe retention period is the maximum age of samples compared to the highest reported timestamp, per key. Samples are expired based solely on the difference between their timestamps and the timestamps passed to subsequent [`TS.ADD`](https://redis.io/docs/latest/commands/ts.add), [`TS.MADD`](https://redis.io/docs/latest/commands/ts.madd), [`TS.INCRBY`](https://redis.io/docs/latest/commands/ts.incrby), and [`TS.DECRBY`](https://redis.io/docs/latest/commands/ts.decrby) calls.\n\nType: integer\n\nValid range: `[0 .. 9,223,372,036,854,775,807]`\n\nThe value `0` means no expiration.\n\nWhen both `COMPACTION_POLICY` / `ts-compaction-policy` and `RETENTION_POLICY` / `ts-retention-policy` are specified, the retention of newly created compactions is according to the retention time specified in `COMPACTION_POLICY` / `ts-compaction-policy`.\n\n#### Precedence order\n\nBecause the retention can be provided at different levels, the actual precedence of the retention is:\n\n1. Key-level retention, as set with [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create)'s and [`TS.ALTER`](https://redis.io/docs/latest/commands/ts.alter)'s `RETENTION` optional argument.\n1. The `ts-retention-policy` configuration parameter.\n1. No retention.\n\n#### Example\n\nSet the default retention to 300 days:\n\nVersion < 8.0:\n\n[code example]\n\nVersion >= 8.0:\n\n[code example]"},{"id":"encoding--ts-encoding","title":"ENCODING / ts-encoding","role":"content","text":"Note: Before v1.6 this configuration parameter was named `CHUNK_TYPE`.\n\nDefault chunk encoding for automatically created compactions when [ts-compaction-policy](#ts-compaction-policy) is configured.\n\nType: string\n\nValid values: `COMPRESSED`, `UNCOMPRESSED`\n\n#### Precedence order\n\n1. The `ts-encoding` configuration parameter.\n1. The hard-coded default: `COMPRESSED`\n\n#### Example\n\nSet the default encoding to `UNCOMPRESSED`:\n\nVersion < 8.0:\n\n[code example]\n\nVersion >= 8.0:\n\n[code example]"},{"id":"ignore_max_time_diff--ts-ignore-max-time-diff-and-ignore_max_val_diff--ts-ignore-max-val-diff","title":"IGNORE_MAX_TIME_DIFF / ts-ignore-max-time-diff and IGNORE_MAX_VAL_DIFF / ts-ignore-max-val-diff","role":"content","text":"Default values for newly created keys.\n\nTypes:\n- `ts-ignore-max-time-diff`: integer\n- `ts-ignore-max-val-diff`: double\n\nValid ranges:\n- `ts-ignore-max-time-diff`: `[0 .. 9,223,372,036,854,775,807]`\n- `ts-ignore-max-val-diff`: `[0 .. 1.7976931348623157e+308]`\n\nMany sensors report data periodically. Often, the difference between the measured value and the previous measured value is negligible and related to random noise or to measurement accuracy limitations. In such situations it may be preferable not to add the new measurement to the time series.\n\nA new sample is considered a duplicate and is ignored if the following conditions are met:\n\n1. The time series is not a compaction.\n1. The time series' `ts-duplicate-policy` is `LAST`.\n1. The sample is added in-order (`timestamp ≥ max_timestamp`).\n1. The difference of the current timestamp from the previous timestamp (`timestamp - max_timestamp`) is less than or equal to `ts-ignore-max-time-diff`.\n1. The absolute value difference of the current value from the value at the previous maximum timestamp (`abs(value - value_at_max_timestamp`) is less than or equal to `ts-ignore-max-val-diff`.\n\nwhere `max_timestamp` is the timestamp of the sample with the largest timestamp in the time series, and `value_at_max_timestamp` is the value at `max_timestamp`.\n\n#### Precedence order\n\n1. The `ts-ignore-max-time-diff` and `ts-ignore-max-val-diff` configuration parameters.\n1. The hard-coded defaults: `0` and `0.0`.\n\n#### Example\n\nVersion < 8.0:\n\n[code example]\n\nVersion >= 8.0:\n\n[code example]"},{"id":"num_threads--ts-num-threads","title":"NUM_THREADS / ts-num-threads","role":"content","text":"The maximum number of per-shard threads for cross-key queries when using cluster mode ([`TS.MRANGE`](https://redis.io/docs/latest/commands/ts.mrange), [`TS.MREVRANGE`](https://redis.io/docs/latest/commands/ts.mrevrange), [`TS.MGET`](https://redis.io/docs/latest/commands/ts.mget), and [`TS.QUERYINDEX`](https://redis.io/docs/latest/commands/ts.queryindex)). The value must be equal to or greater than `1`. Note that increasing this value may either increase or decrease the performance!\n\nType: integer\n\nValid range: `[1..16]`\n\nRedis Open Source default: `3`\n\nRedis Software default: Set by plan, and automatically updates when you change your plan.\n\nRedis Cloud defaults:\n- Flexible & Annual: Set by plan\n- Free & Fixed: `1`\n\n#### Example\n\nVersion < 8.0:\n\n[code example]\n\nVersion >= 8.0:\n\n[code example]"},{"id":"oss_global_password","title":"OSS_GLOBAL_PASSWORD","role":"content","text":"Prior to version 8.0, when using time series in a cluster, you had to set the `OSS_GLOBAL_PASSWORD` configuration parameter on all cluster nodes. As of version 8.0, Redis no longer uses this parameter and ignores it if present. Redis now uses a new shared secret mechanism to send internal commands between cluster nodes."}],"examples":[{"id":"chunk_size_bytes--ts-chunk-size-bytes-ex0","language":"plaintext","code":"$ redis-server --loadmodule ./redistimeseries.so CHUNK_SIZE_BYTES 1024","section_id":"chunk_size_bytes--ts-chunk-size-bytes"},{"id":"chunk_size_bytes--ts-chunk-size-bytes-ex1","language":"plaintext","code":"redis> CONFIG SET ts-chunk-size-bytes 1024","section_id":"chunk_size_bytes--ts-chunk-size-bytes"},{"id":"compaction_policy--ts-compaction-policy-ex0","language":"plaintext","code":"$ redis-server --loadmodule ./redistimeseries.so COMPACTION_POLICY max:1m:1h;min:10s:5d:10d;last:5M:10m;avg:2h:10d;avg:3d:100d","section_id":"compaction_policy--ts-compaction-policy"},{"id":"compaction_policy--ts-compaction-policy-ex1","language":"plaintext","code":"redis> CONFIG SET ts-compaction-policy max:1m:1h;min:10s:5d:10d;last:5M:10m;avg:2h:10d;avg:3d:100d","section_id":"compaction_policy--ts-compaction-policy"},{"id":"retention_policy--ts-retention-policy-ex0","language":"plaintext","code":"$ redis-server --loadmodule ./redistimeseries.so RETENTION_POLICY 25920000000","section_id":"retention_policy--ts-retention-policy"},{"id":"retention_policy--ts-retention-policy-ex1","language":"plaintext","code":"redis> CONFIG SET ts-retention-policy 25920000000","section_id":"retention_policy--ts-retention-policy"},{"id":"encoding--ts-encoding-ex0","language":"plaintext","code":"$ redis-server --loadmodule ./redistimeseries.so ENCODING UNCOMPRESSED","section_id":"encoding--ts-encoding"},{"id":"encoding--ts-encoding-ex1","language":"plaintext","code":"redis> CONFIG SET ts-encoding UNCOMPRESSED","section_id":"encoding--ts-encoding"},{"id":"ignore_max_time_diff--ts-ignore-max-time-diff-and-ignore_max_val_diff--ts-ignore-max-val-diff-ex0","language":"plaintext","code":"$ redis-server --loadmodule ./redistimeseries.so IGNORE_MAX_TIME_DIFF 10 IGNORE_MAX_VAL_DIFF 0.1","section_id":"ignore_max_time_diff--ts-ignore-max-time-diff-and-ignore_max_val_diff--ts-ignore-max-val-diff"},{"id":"ignore_max_time_diff--ts-ignore-max-time-diff-and-ignore_max_val_diff--ts-ignore-max-val-diff-ex1","language":"plaintext","code":"redis> CONFIG SET ts-ignore-max-time-diff 10 ts-ignore-max-val-diff 0.1","section_id":"ignore_max_time_diff--ts-ignore-max-time-diff-and-ignore_max_val_diff--ts-ignore-max-val-diff"},{"id":"num_threads--ts-num-threads-ex0","language":"plaintext","code":"$ redis-server --loadmodule ./redistimeseries.so NUM_THREADS 3","section_id":"num_threads--ts-num-threads"},{"id":"num_threads--ts-num-threads-ex1","language":"plaintext","code":"redis> redis-server --loadmodule ./redistimeseries.so ts-num-threads 3","section_id":"num_threads--ts-num-threads"}]}
{"schema_version":2,"id":"develop/data-types/timeseries","title":"Time series","url":"https://redis.io/docs/latest/develop/data-types/timeseries/","summary":"Ingest and query time series data with Redis","aliases":["/develop/data-types/timeseries/quickstart","/develop/data-types/timeseries/quickstart/","/develop/data-types/timeseries/clients","/develop/data-types/timeseries/clients/","/develop/data-types/timeseries/development","/develop/data-types/timeseries/development/","/data-types/timeseries/","/manual/data-types/timeseries/","/stack/timeseries/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-30T13:16:11-07:00","children":[{"id":"develop/data-types/timeseries/configuration","summary":"Redis time series support multiple configuration parameters.","title":"Configuration Parameters","url":"https://redis.io/docs/latest/develop/data-types/timeseries/configuration/"},{"id":"develop/data-types/timeseries/out-of-order_performance_considerations","summary":"Out-of-order / backfilled ingestion performance considerations","title":"Out-of-order / backfilled ingestion performance considerations","url":"https://redis.io/docs/latest/develop/data-types/timeseries/out-of-order_performance_considerations/"},{"id":"develop/data-types/timeseries/use_cases","summary":"Time series use cases","title":"Use cases","url":"https://redis.io/docs/latest/develop/data-types/timeseries/use_cases/"}],"page_type":"content","content_hash":"9b9d823a2058b07174ea8a54c9dbee8d0a67a522a2fe30eee35a038d83105858","sections":[{"id":"time-series-command-summary","title":"Time series command summary","role":"content","text":"**21 commands in this group:**\n\n[View all timeseries commands](https://redis.io/commands/?group=timeseries)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [TS.ADD](https://redis.io/commands/ts.add/) | Append a sample to a time series | O(M) where M is the number of compaction rules ... | 1.0.0 |\n| [TS.ALTER](https://redis.io/commands/ts.alter/) | Update the retention, chunk size, duplicate policy, and labels of an existing time series | O(N) where N is the number of labels requested ... | 1.0.0 |\n| [TS.CREATE](https://redis.io/commands/ts.create/) | Create a new time series | O(1) | 1.0.0 |\n| [TS.CREATERULE](https://redis.io/commands/ts.createrule/) | Create a compaction rule | O(1) | 1.0.0 |\n| [TS.DECRBY](https://redis.io/commands/ts.decrby/) | Decrease the value of the latest sample | O(M) when M is the amount of compaction rules o... | 1.0.0 |\n| [TS.DEL](https://redis.io/commands/ts.del/) | Delete all samples between two timestamps for a given time series | O(N) where N is the number of data points that ... | 1.6.0 |\n| [TS.DELETERULE](https://redis.io/commands/ts.deleterule/) | Delete a compaction rule | O(1) | 1.0.0 |\n| [TS.GET](https://redis.io/commands/ts.get/) | Get the sample with the highest timestamp from a given time series | O(1) | 1.0.0 |\n| [TS.INCRBY](https://redis.io/commands/ts.incrby/) | Increase the value of the latest sample | O(M) when M is the amount of compaction rules o... | 1.0.0 |\n| [TS.INFO](https://redis.io/commands/ts.info/) | Returns information and statistics for a time series | O(1) | 1.0.0 |\n\n#### Time series command summary (part 2)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [TS.MADD](https://redis.io/commands/ts.madd/) | Append new samples to one or more time series | O(N*M) when N is the amount of series updated a... | 1.0.0 |\n| [TS.MGET](https://redis.io/commands/ts.mget/) | Get the sample with the highest timestamp from each time series matching a specific filter | O(n) where n is the number of time-series that ... | 1.0.0 |\n| [TS.MRANGE](https://redis.io/commands/ts.mrange/) | Query a range across multiple time series by filters in forward direction | O(n/m+k) where n = Number of data points, m = C... | 1.0.0 |\n| [TS.MREVRANGE](https://redis.io/commands/ts.mrevrange/) | Query a range across multiple time series by filters in reverse direction | O(n/m+k) where n = Number of data points, m = C... | 1.4.0 |\n| [TS.NRANGE](https://redis.io/commands/ts.nrange/) | Query a range across multiple time series in forward direction, returning the results pivoted by timestamp (one value column per key) | O(numkeys*(n/m+k)) where n = Number of samples,... | 8.10.0 |\n| [TS.NREVRANGE](https://redis.io/commands/ts.nrevrange/) | Query a range across multiple time series in reverse direction, returning the results pivoted by timestamp (one value column per key) | O(numkeys*(n/m+k)) where n = Number of samples,... | 8.10.0 |\n| [TS.QUERYINDEX](https://redis.io/commands/ts.queryindex/) | Get all time series keys matching a filter list | O(n) where n is the number of time-series that ... | 1.0.0 |\n| [TS.QUERYLABELS](https://redis.io/commands/ts.querylabels/) | Get all label names, or all values of a given label, for time series matching a filter list, or all series | O(n) where n is the number of time-series that ... | 8.10.0 |\n| [TS.RANGE](https://redis.io/commands/ts.range/) | Query a range in forward direction | O(n/m+k) where n = Number of data points, m = C... | 1.0.0 |\n| [TS.READ](https://redis.io/commands/ts.read/) | Read: return up to max_count samples with timestamp >= timestamp. With BLOCK, waits up to milliseconds ms until at least min_count qualifying samples exist | O(log(n)+k) where n is the number of samples in... | 8.10.0 |\n\n#### Time series command summary (part 3)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [TS.REVRANGE](https://redis.io/commands/ts.revrange/) | Query a range in reverse direction | O(n/m+k) where n = Number of data points, m = C... | 1.4.0 |\n\n\n\n[![Discord](https://img.shields.io/discord/697882427875393627?style=flat-square)](https://discord.gg/KExRgMb)\n[![Github](https://img.shields.io/static/v1?label=&message=repository&color=5961FF&logo=github)](https://github.com/RedisTimeSeries/RedisTimeSeries/)\n\nThe Redis time series data type lets you store real-valued data points\nalong with the time they were collected. You can combine the values from a selection\nof time series and query them by time or value range. You can also compute\naggregators of the data over periods of time and create new time series\nfrom the results. When you create a time series, you can specify a maximum\nretention period for the data, relative to the last reported timestamp, to\nprevent the time series from growing indefinitely.\n\nStarting from Redis 8.6, time series support NaN (Not a Number) values, which allow you to represent missing or invalid measurements while maintaining the temporal structure of your data.\n\nTime series support very fast reads and writes, making them ideal for\napplications such as:\n\n- Instrument data logging\n- System performance metrics\n- Financial market data\n- Internet of Things (IoT) sensor data\n- Smart metering\n- Quality of service (QoS) monitoring\n\nRedis time series are available in Redis Open Source, Redis Software, and Redis Cloud.\nSee\n[Install Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack) or\n[Install Redis Software](https://redis.io/docs/latest/operate/rs/installing-upgrading/install)\nfor full installation instructions."},{"id":"create-a-time-series","title":"Create a time series","role":"content","text":"You can create a new empty time series with the [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create)\ncommand, specifying a key name. Alternatively, if you use [`TS.ADD`](https://redis.io/docs/latest/commands/ts.add)\nto add data to a time series key that does not exist, it is automatically created (see\n[Adding data points](#adding-data-points) below for more information about `TS.ADD`).\n\nFoundational: Use TS.CREATE to initialize a new time series key\n\n**Difficulty:** Beginner\n\n**Commands:** TS.CREATE, TYPE, TS.INFO\n\n**Complexity:**\n- TS.CREATE: O(1)\n- TYPE: O(1)\n- TS.INFO: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nThe timestamp for each data point is a 64-bit integer value. The value\nrepresents a Unix timestamp, measured in milliseconds since the\n[Unix epoch](https://en.wikipedia.org/wiki/Unix_time).\nWhen you create a time series, you can specify a maximum retention period for the\ndata, relative to the last reported timestamp. A retention period of zero means\nthe data does not expire.\n\nData retention: Use TS.ADD with RETENTION option to automatically expire old data points based on time since the last update\n\n**Difficulty:** Intermediate\n\n**Builds upon:** create\n\n**Commands:** TS.ADD, TS.INFO\n\n**Complexity:**\n- TS.ADD: O(M)\n- TS.INFO: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nYou can also add one or more *labels* to a time series when you create it. Labels\nare name-value pairs where both the name and value are strings. You can use\nthe names and values to select subsets of all the available time series\nfor queries and aggregations.\n\nLabeling: Add metadata labels to time series using LABELS option when you need to organize and filter series by attributes like location or sensor type\n\n**Difficulty:** Beginner\n\n**Builds upon:** create\n\n**Commands:** TS.ADD, TS.INFO\n\n**Complexity:**\n- TS.ADD: O(M)\n- TS.INFO: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]"},{"id":"add-data-points","title":"Add data points","role":"content","text":"You can add individual data points with [`TS.ADD`](https://redis.io/docs/latest/commands/ts.add),\nbut you can also use [`TS.MADD`](https://redis.io/docs/latest/commands/ts.madd) to add multiple data\npoints to one or more time series in a single command. (Note that unlike `TS.ADD`, `TS.MADD`\ndoesn't create any new time series if you specify keys that don't exist.) The return value\nis an array containing the number of samples in each time series after the operation.\nIf you use the `*` character as the timestamp, Redis will record the current\nUnix time, as reported by the server's clock.\n\nBatch operations: Add multiple data points to one or more time series using TS.MADD when you need to reduce round trips to the server\n\n**Difficulty:** Beginner\n\n**Builds upon:** create\n\n**Commands:** DEL, TS.CREATE, TS.ADD, TS.MADD\n\n**Complexity:**\n- DEL: O(N)\n- TS.CREATE: O(1)\n- TS.ADD: O(M)\n- TS.MADD: O(N*M)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]"},{"id":"query-data-points","title":"Query data points","role":"content","text":"Use [`TS.GET`](https://redis.io/docs/latest/commands/ts.get) to retrieve the data point\nwith the highest timestamp in a time series. This returns both the timestamp and the value.\n\nFoundational: Use TS.GET to get the latest value and timestamp\n\n**Difficulty:** Beginner\n\n**Builds upon:** madd\n\n**Commands:** DEL, TS.ADD, TS.GET\n\n**Complexity:**\n- DEL: O(N)\n- TS.ADD: O(M)\n- TS.GET: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nUse [`TS.RANGE`](https://redis.io/docs/latest/commands/ts.range) to retrieve data points\nfrom a time series that fall within a given timestamp range. The range is inclusive,\nmeaning that samples whose timestamp equals the start or end of the range are included.\nYou can use `-` and `+` as the start and end of the range, respectively, to\nindicate the minimum and maximum timestamps in the series. The response is\nan array of timestamp-value pairs returned in ascending order by timestamp.\nIf you want the results in descending order, use [`TS.REVRANGE`](https://redis.io/docs/latest/commands/ts.revrange) with the same parameters.\n\nRange queries: Retrieve data points within a timestamp range using TS.RANGE (ascending) or TS.REVRANGE (descending) when you need to analyze historical data\n\n**Difficulty:** Intermediate\n\n**Builds upon:** madd\n\n**Commands:** TS.CREATE, TS.MADD, TS.RANGE, TS.REVRANGE\n\n**Complexity:**\n- TS.CREATE: O(1)\n- TS.MADD: O(N*M)\n- TS.RANGE: O(n/m+k)\n- TS.REVRANGE: O(n/m+k)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nBoth `TS.RANGE` and `TS.REVRANGE` also let you filter results. Specify\na list of timestamps to include only samples with those exact timestamps\nin the results (you must still specify timestamp range parameters if you\nuse this option). Specify a minimum and maximum value to include only\nsamples within that range. The value range is inclusive and you can\nuse the same value for the minimum and maximum to filter for a single value.\n\nFiltering results: Use FILTER_BY_TS and FILTER_BY_VALUE options with range queries when you need to select specific timestamps or value ranges\n\n**Difficulty:** Intermediate\n\n**Builds upon:** range\n\n**Commands:** DEL, TS.CREATE, TS.MADD, TS.RANGE, TS.REVRANGE\n\n**Complexity:**\n- DEL: O(N)\n- TS.CREATE: O(1)\n- TS.MADD: O(N*M)\n- TS.RANGE: O(n/m+k)\n- TS.REVRANGE: O(n/m+k)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]"},{"id":"query-multiple-time-series","title":"Query multiple time series","role":"content","text":"The `TS.GET`, `TS.RANGE`, and `TS.REVRANGE` commands also have\ncorresponding\n[`TS.MGET`](https://redis.io/docs/latest/commands/ts.mget),\n[`TS.MRANGE`](https://redis.io/docs/latest/commands/ts.mrange), and\n[`TS.MREVRANGE`](https://redis.io/docs/latest/commands/ts.mrevrange) versions that\noperate on multiple time series. `TS.MGET` returns the data point with the highest\ntimestamp from each time series, while `TS.MRANGE` and `TS.MREVRANGE`\nreturn data points from a range of timestamps in each time series.\n\nThe parameters are mostly the same except that the multiple time series\ncommands don't take a key name as the first parameter. Instead, you\nspecify a filter expression to include only time series with\nspecific labels. (See [Creating a time series](#creating-a-time-series)\nabove to learn how to add labels to a time series.) The filter expressions\nuse a simple syntax that lets you include or exclude time series based on\nthe presence or value of a label. See the description in the\n[`TS.MGET`](https://redis.io/docs/latest/commands/ts.mget#required-arguments) command reference\nfor details of the filter syntax. You can also request that\ndata points be returned with all their labels or with a selected subset of them.\n\nMulti-series queries: Use TS.MGET, TS.MRANGE, and TS.MREVRANGE with label filters when you need to query multiple time series based on label criteria\n\n**Difficulty:** Advanced\n\n**Builds upon:** create_labels\n\n**Commands:** TS.CREATE, TS.MADD, TS.MGET, TS.MRANGE, TS.MREVRANGE\n\n**Complexity:**\n- TS.CREATE: O(1)\n- TS.MADD: O(N*M)\n- TS.MGET: O(n)\n- TS.MRANGE: O(n/m+k)\n- TS.MREVRANGE: O(n/m+k)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]"},{"id":"aggregation","title":"Aggregation","role":"content","text":"A time series can become large if samples are added very frequently. Instead\nof dealing with individual samples, it is sometimes useful to split the full\ntime range of the series into equal-sized \"buckets\" and represent each\nbucket by an aggregate value, such as the average or maximum value.\n\nFor example, if you expect to collect more than one billion data points in a day, you could aggregate the data using buckets of one minute. Since each bucket is represented by a single value, this reduces \nthe dataset size to 1,440 data points (24 hours x 60 minutes = 1,440 minutes).\n\nThe range query commands let you specify one or more aggregators and bucket size.\nThe available aggregators are:\n\n| aggregator   | Description                                                     |\n| ------------ | --------------------------------------------------------------- |\n| `avg`        | Arithmetic mean of all non-NaN values                           |\n| `sum`        | Sum of all non-NaN values                                       |\n| `min`        | Minimum non-NaN value                                           |\n| `max`        | Maximum non-NaN value                                           |\n| `range`      | Difference between the maximum and the minimum non-NaN values   |\n| `count`      | Number of non-NaN values                                        |\n| `countNaN`   | Number of NaN values (since Redis 8.6)                          |\n| `countAll`   | Number of values, including NaN and non-NaN (since Redis 8.6)   |\n| `first`      | The non-NaN value with the lowest timestamp in the bucket       |\n| `last`       | The non-NaN value with the highest timestamp in the bucket      |\n| `std.p`      | Population standard deviation of the non-NaN values             |\n| `std.s`      | Sample standard deviation of the non-NaN values                 |\n| `var.p`      | Population variance of the non-NaN values                       |\n| `var.s`      | Sample variance of the non-NaN values                           |\n| `twa`        | Time-weighted average over the bucket's timeframe (ignores NaN values) (since RedisTimeSeries 1.8) |\n\nFor example, the example below shows an aggregation with the `avg` aggregator over all\nfive data points in the `rg:2` time series. The bucket size is 2ms, so there are three\naggregated values with only one value used to calculate the average for the last bucket.\n\nAggregation: Use AGGREGATION option with range queries to compute statistics such as avg, sum, min, and max over time buckets when you need to reduce large datasets\n\n**Difficulty:** Intermediate\n\n**Builds upon:** madd\n\n**Commands:** DEL, TS.CREATE, TS.MADD, TS.RANGE\n\n**Complexity:**\n- DEL: O(N)\n- TS.CREATE: O(1)\n- TS.MADD: O(N*M)\n- TS.RANGE: O(n/m+k)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nTo use multiple aggregators in a single query, separate the aggregators with comma characters as in the following example.\n\n[code example]\n\n<note><b>NaN Handling (Redis 8.6+):</b> Starting from Redis 8.6, all existing aggregators ignore NaN values when computing results. For example, if a bucket contains values [1.0, NaN, 3.0], the `avg` aggregator will return 2.0 (average of 1.0 and 3.0), and the `count` aggregator will return 2. Use the new `countNaN` and `countAll` aggregators to count NaN values and total values respectively.</note>"},{"id":"bucket-alignment","title":"Bucket alignment","role":"content","text":"The sequence of buckets has a reference timestamp, which is the timestamp where\nthe first bucket in the sequence starts. By default, the reference timestamp is zero.\nFor example, the following commands create a time series and apply a `min` aggregator\nwith a bucket size of 25 milliseconds at the default zero alignment.\n\nBucket alignment: Use AGGREGATION with default zero alignment to group data into fixed-size time buckets when you need consistent time-based aggregations\n\n**Difficulty:** Intermediate\n\n**Builds upon:** agg\n\n**Commands:** TS.CREATE, TS.MADD, TS.RANGE\n\n**Complexity:**\n- TS.CREATE: O(1)\n- TS.MADD: O(N*M)\n- TS.RANGE: O(n/m+k)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nThe diagram below shows the aggregation buckets and their alignment to the reference timestamp\nat time zero.\n\n[code example]\n\nYou can also align the buckets to the start or end of the query range. For example, the following command aligns the buckets to the start of the query range at time 10.\n\nCustom alignment: Use ALIGN option with aggregations to align buckets to query range start/end when you need aggregations relative to specific time boundaries\n\n**Difficulty:** Advanced\n\n**Builds upon:** agg_bucket\n\n**Commands:** DEL, TS.CREATE, TS.MADD, TS.RANGE\n\n**Complexity:**\n- DEL: O(N)\n- TS.CREATE: O(1)\n- TS.MADD: O(N*M)\n- TS.RANGE: O(n/m+k)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nThe diagram below shows this arrangement of buckets.\n\n[code example]"},{"id":"aggregation-across-timeseries","title":"Aggregation across timeseries","role":"content","text":"By default, the results from\n[`TS.MRANGE`](https://redis.io/docs/latest/commands/ts.mrange) and\n[`TS.MREVRANGE`](https://redis.io/docs/latest/commands/ts.mrevrange) are grouped by time series. However, you can use the `GROUPBY` and `REDUCE` options to group them by label and apply an aggregation over elements\nthat have the same timestamp and the same label value (this feature is available from RedisTimeSeries v1.6 onwards).\n\nFor example, the following commands create four time series, two for the UK and two for the US, and add some data points. The first `TS.MRANGE` command groups the results by country and applies a `max` aggregation to find the maximum sample value in each country at each timestamp. The second `TS.MRANGE` command uses the same grouping, but applies an `avg` aggregation.\n\nCross-series aggregation: Use GROUPBY and REDUCE with TS.MRANGE to aggregate data across multiple time series by label when you need to compute statistics across groups\n\n**Difficulty:** Advanced\n\n**Builds upon:** agg, create_labels\n\n**Commands:** TS.CREATE, TS.MADD, TS.MRANGE\n\n**Complexity:**\n- TS.CREATE: O(1)\n- TS.MADD: O(N*M)\n- TS.MRANGE: O(n/m+k)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]"},{"id":"nan-values","title":"NaN Values","role":"content","text":"Starting from Redis 8.6, time series support NaN (Not a Number) values. This feature allows you to insert measurements that represent missing, invalid, or unknown data while preserving the temporal structure of your time series."},{"id":"use-cases-for-nan-values","title":"Use Cases for NaN Values","role":"content","text":"NaN values are useful in scenarios where you need to distinguish between:\n- **Missing data**: A timestamp where no measurement was taken\n- **Invalid data**: A timestamp where a measurement was attempted but failed (for example, sensor malfunction)\n- **Unknown data**: A timestamp where the measurement value is semantically \"unknown\" and may be filled in later"},{"id":"nan-behavior","title":"NaN Behavior","role":"content","text":"- **Adding NaN values**: Use [`TS.ADD`](https://redis.io/docs/latest/commands/ts.add) and [`TS.MADD`](https://redis.io/docs/latest/commands/ts.madd) to insert NaN values\n- **Querying NaN values**: All raw measurement queries ([`TS.GET`](https://redis.io/docs/latest/commands/ts.get), [`TS.RANGE`](https://redis.io/docs/latest/commands/ts.range), etc.) include NaN values in results\n- **Aggregation with NaN**: All existing aggregators except `countNaN` and `countAll` ignore NaN values. Use `countNaN` and `countAll` to count NaN and total values\n- **Increment/Decrement**: [`TS.INCRBY`](https://redis.io/docs/latest/commands/ts.incrby) and [`TS.DECRBY`](https://redis.io/docs/latest/commands/ts.decrby) return errors when the current value or operand is NaN\n- **Duplicate policies**: Special handling for `MIN`, `MAX`, and `SUM` policies when mixing NaN and non-NaN values\n- **Filtering**: [`FILTER_BY_VALUE`](https://redis.io/docs/latest/commands/ts.range#filter_by_value-min-max-since-redistimeseries-v16) parameters cannot be NaN values\n- **Ignore duplicates**: NaN values are never considered duplicates when using `IGNORE` parameters\n\n[code example]"},{"id":"compaction","title":"Compaction","role":"content","text":"[Aggregation](#aggregation) queries let you extract the important information from a large data set\ninto a smaller, more manageable set. If you are continually adding new data to a\ntime series as it is generated, you may need to run the same aggregation\nregularly on the latest data. Instead of running the query manually\neach time, you can add a *compaction rule* to a time series to compute an\naggregation incrementally on data as it arrives. The values from the\naggregation buckets are stored in a separate time series, leaving the original\nseries unchanged.\n\nUse [`TS.CREATERULE`](https://redis.io/docs/latest/commands/ts.createrule) to create a\ncompaction rule, specifying the source and destination time series keys, an\naggregator, and the bucket duration. Note that the destination time\nseries must already exist when you create the rule and also that the compaction will\nonly process data that is added to the source series after you create the rule.\n\nFor example, you could use the commands below to create a time series along with a\ncompaction rule to find the minimum reading in each period of 3ms.\n\nCompaction rules: Use TS.CREATERULE to automatically aggregate data into a destination time series when you need to maintain pre-computed aggregations\n\n**Difficulty:** Advanced\n\n**Builds upon:** create\n\n**Commands:** TS.CREATE, TS.CREATERULE, TS.INFO\n\n**Complexity:**\n- TS.CREATE: O(1)\n- TS.CREATERULE: O(1)\n- TS.INFO: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nAdding data points within the first 3ms (the first bucket) doesn't\nproduce any data in the compacted series. However, when you add data for\ntime 4 (in the second bucket), the compaction rule computes the minimum\nvalue for the first bucket and adds it to the compacted series.\n\nCompaction behavior: Understand how compaction rules process data incrementally, computing aggregates for completed buckets when new data arrives\n\n**Difficulty:** Intermediate\n\n**Builds upon:** create_compaction\n\n**Commands:** DEL, TS.CREATE, TS.CREATERULE, TS.MADD, TS.RANGE, TS.ADD\n\n**Complexity:**\n- DEL: O(N)\n- TS.CREATE: O(1)\n- TS.CREATERULE: O(1)\n- TS.MADD: O(N*M)\n- TS.RANGE: O(n/m+k)\n- TS.ADD: O(M)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nThe general strategy is that the rule does not add data to the\ncompaction for the latest bucket in the source series, but will add and\nupdate the compacted data for any previous buckets. This reflects the\ntypical usage pattern of adding data samples sequentially in real time\n(an aggregate value typically isn't correct until its bucket period is over).\nBut note that earlier buckets are not \"closed\" when you add data to a later\nbucket. If you add or [delete](#deleting-data-points) data in a bucket before\nthe latest one, the compaction rule will still update the compacted data for\nthat bucket."},{"id":"delete-data-points","title":"Delete data points","role":"content","text":"Use [`TS.DEL`](https://redis.io/docs/latest/commands/ts.del) to delete data points\nthat fall within a given timestamp range. The range is inclusive, meaning that\nsamples whose timestamp equals the start or end of the range are deleted.\nIf you want to delete a single timestamp, use it as both the start and end of the range.\n\nDeleting data: Use TS.DEL to remove data points within a timestamp range when you need to clean up or correct historical data\n\n**Difficulty:** Beginner\n\n**Builds upon:** create\n\n**Commands:** DEL, TS.ADD, TS.INFO, TS.DEL\n\n**Complexity:**\n- DEL: O(N)\n- TS.ADD: O(M)\n- TS.INFO: O(1)\n- TS.DEL: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]"},{"id":"use-time-series-with-other-metrics-tools","title":"Use time series with other metrics tools","role":"content","text":"In the [RedisTimeSeries](https://github.com/RedisTimeSeries) GitHub organization, you can\nfind projects that help you integrate RedisTimeSeries with other tools, including:\n\n1. [Prometheus](https://github.com/RedisTimeSeries/prometheus-redistimeseries-adapter), a read/write adapter to use RedisTimeSeries as the backend database.\n2. [Grafana 7.1+](https://github.com/RedisTimeSeries/grafana-redis-datasource), using the [Redis Data Source](https://redislabs.com/blog/introducing-the-redis-data-source-plug-in-for-grafana/).\n3. [Telegraf](https://github.com/influxdata/telegraf). Download the plugin from [InfluxData](https://portal.influxdata.com/downloads/). \n4. StatsD, Graphite exports using graphite protocol."},{"id":"more-information","title":"More information","role":"content","text":"The other pages in this section describe RedisTimeSeries concepts in more detail.\nSee also the [time series command reference](/commands/?group=timeseries)."}],"examples":[{"id":"create-a-time-series-ex0","language":"plaintext","code":"> TS.CREATE thermometer:1\nOK\n> TYPE thermometer:1\nTSDB-TYPE\n> TS.INFO thermometer:1\n 1) totalSamples\n 2) (integer) 0\n 3) memoryUsage\n 4) (integer) 4504\n 5) firstTimestamp\n 6) (integer) 0\n 7) lastTimestamp\n 8) (integer) 0\n 9) retentionTime\n10) (integer) 0\n11) chunkCount\n12) (integer) 1\n13) chunkSize\n14) (integer) 4096\n15) chunkType\n16) compressed\n17) duplicatePolicy\n18) block\n19) labels\n20) (empty array)\n21) sourceKey\n22) (nil)\n23) rules\n24) (empty array)\n25) ignoreMaxTimeDiff\n26) (integer) 0\n27) ignoreMaxValDiff\n28) \"0\"","section_id":"create-a-time-series"},{"id":"create-a-time-series-ex1","language":"csharp","code":"bool res1 = db.TS().Create(\n            \"thermometer:1\",\n            new TsCreateParamsBuilder().build()\n        );\n        Console.WriteLine(res1); // >>> True\n\n        RedisType res2 = db.KeyType(\"thermometer:1\");\n        Console.WriteLine(res2); // >>> TSDB-TYPE\n\n        TimeSeriesInformation res3 = db.TS().Info(\"thermometer:1\");\n        Console.WriteLine(res3.TotalSamples); // >>> 0","section_id":"create-a-time-series"},{"id":"create-a-time-series-ex2","language":"go","code":"res1, err := rdb.TSCreate(ctx, \"thermometer:1\").Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> OK\n\n\tres2, err := rdb.Type(ctx, \"thermometer:1\").Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> TSDB-TYPE\n\n\tres3, err := rdb.TSInfo(ctx, \"thermometer:1\").Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3[\"totalSamples\"]) // >>> 0","section_id":"create-a-time-series"},{"id":"create-a-time-series-ex3","language":"java","code":"String res1 = jedis.tsCreate(\"thermometer:1\");\n        System.out.println(res1); // >>> OK\n\n        String res2 = jedis.type(\"thermometer:1\");\n        System.out.println(res2); // >>> TSDB-TYPE\n\n        TSInfo res3 = jedis.tsInfo(\"thermometer:1\");\n        System.out.println(res3.getProperty(\"totalSamples\")); // >>> 0","section_id":"create-a-time-series"},{"id":"create-a-time-series-ex4","language":"javascript","code":"const res1 = await client.ts.create('thermometer:1');\nconsole.log(res1); // >>> OK\n\nconst res2 = await client.type('thermometer:1');\nconsole.log(res2); // >>> TSDB-TYPE\n\nconst res3 = await client.ts.info('thermometer:1');\nconsole.log(res3);\n// >>> { rules: [], ... totalSamples: 0, ...","section_id":"create-a-time-series"},{"id":"create-a-time-series-ex5","language":"python","code":"res1 = r.ts().create(\"thermometer:1\")\nprint(res1)  # >>> True\n\nres2 = r.type(\"thermometer:1\")\nprint(res2)  # >>> TSDB-TYPE\n\nres3 = r.ts().info(\"thermometer:1\")\nprint(res3)\n# >>> {'rules': [], ... 'total_samples': 0, ...","section_id":"create-a-time-series"},{"id":"create-a-time-series-ex6","language":"plaintext","code":"# Create a new time series with a first value of 10.8 (Celsius), recorded at time 1, with a retention period of 100ms.\n> TS.ADD thermometer:2 1 10.8 RETENTION 100\n(integer) 1\n> TS.INFO thermometer:2\n 1) totalSamples\n 2) (integer) 1\n 3) memoryUsage\n 4) (integer) 4504\n 5) firstTimestamp\n 6) (integer) 1\n 7) lastTimestamp\n 8) (integer) 1\n 9) retentionTime\n10) (integer) 100\n11) chunkCount\n12) (integer) 1\n13) chunkSize\n14) (integer) 4096\n15) chunkType\n16) compressed\n17) duplicatePolicy\n18) block\n19) labels\n20) (empty array)\n21) sourceKey\n22) (nil)\n23) rules\n24) (empty array)\n25) ignoreMaxTimeDiff\n26) (integer) 0\n27) ignoreMaxValDiff\n28) \"0\"","section_id":"create-a-time-series"},{"id":"create-a-time-series-ex7","language":"csharp","code":"long res4 = db.TS().Add(\n            \"thermometer:2\",\n            new TsAddParamsBuilder()\n                .AddTimestamp(1)\n                .AddValue(10.8)\n                .AddRetentionTime(100)\n                .build()\n        );\n        Console.WriteLine(res4); // >>> 1\n\n        TimeSeriesInformation res5 = db.TS().Info(\"thermometer:2\");\n        Console.WriteLine(res5.RetentionTime);\n        // >>> 100","section_id":"create-a-time-series"},{"id":"create-a-time-series-ex8","language":"go","code":"res4, err := rdb.TSAddWithArgs(\n\t\tctx,\n\t\t\"thermometer:2\",\n\t\t1,\n\t\t10.8,\n\t\t&redis.TSOptions{\n\t\t\tRetention: 100,\n\t\t},\n\t).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4) // >>> 1\n\n\tres5, err := rdb.TSInfo(ctx, \"thermometer:2\").Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res5[\"retentionTime\"]) // >>> 100","section_id":"create-a-time-series"},{"id":"create-a-time-series-ex9","language":"java","code":"long res4 = jedis.tsAdd(\"thermometer:2\", 1L, 10.8, \n            TSCreateParams.createParams().retention(100));\n        System.out.println(res4); // >>> 1\n\n        TSInfo res5 = jedis.tsInfo(\"thermometer:2\");\n        System.out.println(res5.getProperty(\"retentionTime\")); // >>> 100","section_id":"create-a-time-series"},{"id":"create-a-time-series-ex10","language":"javascript","code":"const res4 = await client.ts.add('thermometer:2', 1, 10.8, { RETENTION: 100 });\nconsole.log(res4); // >>> 1\n\nconst res5 = await client.ts.info('thermometer:2');\nconsole.log(res5);\n// >>> { rules: [], ... retentionTime: 100, ...","section_id":"create-a-time-series"},{"id":"create-a-time-series-ex11","language":"python","code":"res4 = r.ts().add(\"thermometer:2\", 1, 10.8, retention_msecs=100)\nprint(res4)  # >>> 1\n\nres5 = r.ts().info(\"thermometer:2\")\nprint(res5)\n# >>> {'rules': [], ... 'retention_msecs': 100, ...","section_id":"create-a-time-series"},{"id":"create-a-time-series-ex12","language":"plaintext","code":"> TS.ADD thermometer:3 1 10.4 LABELS location UK type Mercury\n(integer) 1\n> TS.INFO thermometer:3\n 1) totalSamples\n 2) (integer) 1\n 3) memoryUsage\n 4) (integer) 5472\n 5) firstTimestamp\n 6) (integer) 1\n 7) lastTimestamp\n 8) (integer) 1\n 9) retentionTime\n10) (integer) 0\n11) chunkCount\n12) (integer) 1\n13) chunkSize\n14) (integer) 4096\n15) chunkType\n16) compressed\n17) duplicatePolicy\n18) block\n19) labels\n20) 1) 1) \"location\"\n       2) \"UK\"\n    2) 1) \"type\"\n       2) \"Mercury\"\n21) sourceKey\n22) (nil)\n23) rules\n24) (empty array)\n25) ignoreMaxTimeDiff\n26) (integer) 0\n27) ignoreMaxValDiff\n28) \"0\"","section_id":"create-a-time-series"},{"id":"create-a-time-series-ex13","language":"csharp","code":"var labels = new List<TimeSeriesLabel>\n        {\n            new(\"location\", \"UK\"),\n            new(\"type\", \"Mercury\")\n        };\n        long res6 = db.TS().Add(\n            \"thermometer:3\",\n            new TsAddParamsBuilder()\n                .AddTimestamp(1)\n                .AddValue(10.4)\n                .AddLabels(labels)\n                .build()\n        );\n        Console.WriteLine(res6); // >>> 1\n\n        TimeSeriesInformation res7 = db.TS().Info(\"thermometer:3\");\n        Console.WriteLine(\n            $\"Labels: {string.Join(\", \", res7.Labels!.Select(l => $\"{l.Key}={l.Value}\"))}\"\n        );\n        // >>> Labels: location=UK, type=Mercury","section_id":"create-a-time-series"},{"id":"create-a-time-series-ex14","language":"go","code":"res6, err := rdb.TSAddWithArgs(\n\t\tctx,\n\t\t\"thermometer:3\",\n\t\t1,\n\t\t10.4,\n\t\t&redis.TSOptions{\n\t\t\tLabels: map[string]string{\n\t\t\t\t\"location\": \"UK\",\n\t\t\t\t\"type\":     \"Mercury\",\n\t\t\t},\n\t\t},\n\t).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res6) // >>> 1\n\n\tres7, err := rdb.TSInfo(ctx, \"thermometer:3\").Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res7[\"labels\"])\n\t// >>> map[location:UK type:Mercury]","section_id":"create-a-time-series"},{"id":"create-a-time-series-ex15","language":"java","code":"Map<String, String> labels = new HashMap<>();\n        labels.put(\"location\", \"UK\");\n        labels.put(\"type\", \"Mercury\");\n        \n        long res6 = jedis.tsAdd(\"thermometer:3\", 1L, 10.4,\n            TSCreateParams.createParams().labels(labels));\n        System.out.println(res6); // >>> 1\n\n        TSInfo res7 = jedis.tsInfo(\"thermometer:3\");\n        System.out.println(\"Labels: \" + res7.getLabels());\n        // >>> Labels: {location=UK, type=Mercury}","section_id":"create-a-time-series"},{"id":"create-a-time-series-ex16","language":"javascript","code":"const res6 = await client.ts.add('thermometer:3', 1, 10.4, {\n  LABELS: { location: 'UK', type: 'Mercury' }\n});\nconsole.log(res6); // >>> 1\n\nconst res7 = await client.ts.info('thermometer:3');\nconsole.log(res7);\n// >>> { labels: [{ name: 'location', value: 'UK' }, { name: 'type', value: 'Mercury' }], ... }","section_id":"create-a-time-series"},{"id":"create-a-time-series-ex17","language":"python","code":"res6 = r.ts().create(\n    \"thermometer:3\", 1, 10.4,\n    labels={\"location\": \"UK\", \"type\": \"Mercury\"}\n)\nprint(res6)  # >>> 1\n\nres7 = r.ts().info(\"thermometer:3\")\nprint(res7)\n# >>> {'rules': [], ... 'labels': {'location': 'UK', 'type': 'Mercury'}, ...","section_id":"create-a-time-series"},{"id":"add-data-points-ex0","language":"plaintext","code":"# Recreate the thermometer series so this example runs on its own.\n> DEL thermometer:1 thermometer:2\n(integer) 2\n> TS.CREATE thermometer:1\nOK\n> TS.ADD thermometer:2 1 10.8\n(integer) 1\n> TS.MADD thermometer:1 1 9.2 thermometer:1 2 9.9 thermometer:2 2 10.3\n1) (integer) 1\n2) (integer) 2\n3) (integer) 2","section_id":"add-data-points"},{"id":"add-data-points-ex1","language":"csharp","code":"// Recreate the thermometer series so this example runs on its own.\n        db.KeyDelete(new RedisKey[] { \"thermometer:1\", \"thermometer:2\" });\n        db.TS().Create(\"thermometer:1\", new TsCreateParamsBuilder().build());\n        db.TS().Add(\"thermometer:2\", 1, 10.8);\n\n        var sequence = new List<(string, TimeStamp, double)>\n        {\n            (\"thermometer:1\", 1, 9.2),\n            (\"thermometer:1\", 2, 9.9),\n            (\"thermometer:2\", 2, 10.3)\n        };\n        IReadOnlyList<TimeStamp> res8 = db.TS().MAdd(sequence);\n        Console.WriteLine($\"[{string.Join(\", \", res8.Select(t => t.Value))}]\");\n        // >>> [1, 2, 2]","section_id":"add-data-points"},{"id":"add-data-points-ex2","language":"go","code":"// Recreate the thermometer series so this example runs on its own.\n\trdb.Del(ctx, \"thermometer:1\", \"thermometer:2\")\n\trdb.TSCreate(ctx, \"thermometer:1\")\n\trdb.TSAdd(ctx, \"thermometer:2\", 1, 10.8)\n\n\tres1, err := rdb.TSMAdd(ctx, [][]interface{}{\n\t\t{\"thermometer:1\", 1, 9.2},\n\t\t{\"thermometer:1\", 2, 9.9},\n\t\t{\"thermometer:2\", 2, 10.3},\n\t}).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> [1 2 2]","section_id":"add-data-points"},{"id":"add-data-points-ex3","language":"java","code":"// Recreate the thermometer series so this example runs on its own.\n        jedis.del(\"thermometer:1\", \"thermometer:2\");\n        jedis.tsCreate(\"thermometer:1\");\n        jedis.tsAdd(\"thermometer:2\", 1L, 10.8);\n\n        List<Long> res8 = jedis.tsMAdd(\n            new AbstractMap.SimpleEntry<>(\"thermometer:1\", new TSElement(1L, 9.2)),\n            new AbstractMap.SimpleEntry<>(\"thermometer:1\", new TSElement(2L, 9.9)),\n            new AbstractMap.SimpleEntry<>(\"thermometer:2\", new TSElement(2L, 10.3))\n        );\n        System.out.println(res8); // >>> [1, 2, 2]","section_id":"add-data-points"},{"id":"add-data-points-ex4","language":"javascript","code":"// Recreate the thermometer series so this example runs on its own.\nawait client.del(['thermometer:1', 'thermometer:2']);\nawait client.ts.create('thermometer:1');\nawait client.ts.add('thermometer:2', 1, 10.8);\n\nconst res8 = await client.ts.mAdd([\n  { key: 'thermometer:1', timestamp: 1, value: 9.2 },\n  { key: 'thermometer:1', timestamp: 2, value: 9.9 },\n  { key: 'thermometer:2', timestamp: 2, value: 10.3 }\n]);\nconsole.log(res8); // >>> [1, 2, 2]","section_id":"add-data-points"},{"id":"add-data-points-ex5","language":"python","code":"# Recreate the thermometer series so this example runs on its own.\nr.delete(\"thermometer:1\", \"thermometer:2\")\nr.ts().create(\"thermometer:1\")\nr.ts().add(\"thermometer:2\", 1, 10.8)\n\nres8 = r.ts().madd([\n    (\"thermometer:1\", 1, 9.2),\n    (\"thermometer:1\", 2, 9.9),\n    (\"thermometer:2\", 2, 10.3)\n])\nprint(res8)  # >>> [1, 2, 2]","section_id":"add-data-points"},{"id":"query-data-points-ex0","language":"plaintext","code":"# Recreate the thermometer:2 series so this example runs on its own.\n> DEL thermometer:2\n(integer) 1\n> TS.ADD thermometer:2 1 10.8\n(integer) 1\n> TS.ADD thermometer:2 2 10.3\n(integer) 2\n# The last recorded temperature for thermometer:2\n# was 10.3 at time 2ms.\n> TS.GET thermometer:2\n1) (integer) 2\n2) 10.3","section_id":"query-data-points"},{"id":"query-data-points-ex1","language":"csharp","code":"// Recreate the thermometer:2 series so this example runs on its own.\n        db.KeyDelete(\"thermometer:2\");\n        db.TS().Add(\"thermometer:2\", 1, 10.8);\n        db.TS().Add(\"thermometer:2\", 2, 10.3);\n        // The last recorded temperature for thermometer:2\n        // was 10.3 at time 2.\n        TimeSeriesTuple? res9 = db.TS().Get(\"thermometer:2\");\n        Console.WriteLine($\"({res9!.Time.Value}, {res9.Val})\");\n        // >>> (2, 10.3)","section_id":"query-data-points"},{"id":"query-data-points-ex2","language":"go","code":"// Recreate the thermometer:2 series so this example runs on its own.\n\trdb.Del(ctx, \"thermometer:2\")\n\trdb.TSAdd(ctx, \"thermometer:2\", 1, 10.8)\n\trdb.TSAdd(ctx, \"thermometer:2\", 2, 10.3)\n\t// The last recorded temperature for thermometer:2\n\t// was 10.3 at time 2.\n\tres2, err := rdb.TSGet(ctx, \"thermometer:2\").Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2)\n\t// >>> {2 10.3}","section_id":"query-data-points"},{"id":"query-data-points-ex3","language":"java","code":"// Recreate the thermometer:2 series so this example runs on its own.\n        jedis.del(\"thermometer:2\");\n        jedis.tsAdd(\"thermometer:2\", 1L, 10.8);\n        jedis.tsAdd(\"thermometer:2\", 2L, 10.3);\n        // The last recorded temperature for thermometer:2\n        // was 10.3 at time 2.\n        TSElement res9 = jedis.tsGet(\"thermometer:2\");\n        System.out.println(\"(\" + res9.getTimestamp() + \", \" + res9.getValue() + \")\");\n        // >>> (2, 10.3)","section_id":"query-data-points"},{"id":"query-data-points-ex4","language":"javascript","code":"// Recreate the thermometer:2 series so this example runs on its own.\nawait client.del('thermometer:2');\nawait client.ts.add('thermometer:2', 1, 10.8);\nawait client.ts.add('thermometer:2', 2, 10.3);\n// The last recorded temperature for thermometer:2\n// was 10.3 at time 2.\nconst res9 = await client.ts.get('thermometer:2');\nconsole.log(res9); // >>> { timestamp: 2, value: 10.3 }","section_id":"query-data-points"},{"id":"query-data-points-ex5","language":"python","code":"# Recreate the thermometer:2 series so this example runs on its own.\nr.delete(\"thermometer:2\")\nr.ts().add(\"thermometer:2\", 1, 10.8)\nr.ts().add(\"thermometer:2\", 2, 10.3)\n# The last recorded temperature for thermometer:2\n# was 10.3 at time 2.\nres9 = r.ts().get(\"thermometer:2\")\nprint(res9)  # >>> (2, 10.3)","section_id":"query-data-points"},{"id":"query-data-points-ex6","language":"plaintext","code":"# Add 5 data points to a time series named \"rg:1\".\n> TS.CREATE rg:1\nOK\n> TS.MADD rg:1 0 18 rg:1 1 14 rg:1 2 22 rg:1 3 18 rg:1 4 24\n1) (integer) 0\n2) (integer) 1\n3) (integer) 2\n4) (integer) 3\n5) (integer) 4\n\n# Retrieve all the data points in ascending order.\n> TS.RANGE rg:1 - +\n1) 1) (integer) 0\n   2) 18\n2) 1) (integer) 1\n   2) 14\n3) 1) (integer) 2\n   2) 22\n4) 1) (integer) 3\n   2) 18\n5) 1) (integer) 4\n   2) 24\n\n# Retrieve data points up to time 1 (inclusive).\n> TS.RANGE rg:1 - 1\n1) 1) (integer) 0\n   2) 18\n2) 1) (integer) 1\n   2) 14\n\n# Retrieve data points from time 3 onwards.\n> TS.RANGE rg:1 3 +\n1) 1) (integer) 3\n   2) 18\n2) 1) (integer) 4\n   2) 24\n\n# Retrieve all the data points in descending order.\n> TS.REVRANGE rg:1 - +\n1) 1) (integer) 4\n   2) 24\n2) 1) (integer) 3\n   2) 18\n3) 1) (integer) 2\n   2) 22\n4) 1) (integer) 1\n   2) 14\n5) 1) (integer) 0\n   2) 18\n\n# Retrieve data points up to time 1 (inclusive), but\n# return them in descending order.\n> TS.REVRANGE rg:1 - 1\n1) 1) (integer) 1\n   2) 14\n2) 1) (integer) 0\n   2) 18","section_id":"query-data-points"},{"id":"query-data-points-ex7","language":"csharp","code":"// Add 5 data points to a time series named \"rg:1\".\n        bool res10 = db.TS().Create(\n            \"rg:1\",\n            new TsCreateParamsBuilder().build()\n        );\n        Console.WriteLine(res10); // >>> True\n\n        var sequence2 = new List<(string, TimeStamp, double)>\n        {\n            (\"rg:1\", 0, 18),\n            (\"rg:1\", 1, 14),\n            (\"rg:1\", 2, 22),\n            (\"rg:1\", 3, 18),\n            (\"rg:1\", 4, 24)\n        };\n        IReadOnlyList<TimeStamp> res11 = db.TS().MAdd(sequence2);\n        Console.WriteLine(\n            $\"[{string.Join(\", \", res11.Select(t => t.Value))}]\"\n        );\n        // >>> [0, 1, 2, 3, 4]\n\n        // Retrieve all the data points in ascending order.\n        IReadOnlyList<TimeSeriesTuple> res12 = db.TS().Range(\"rg:1\", \"-\", \"+\");\n        Console.WriteLine(\n            $\"[{string.Join(\", \", res12.Select(t => $\"({t.Time.Value}, {t.Val})\"))}]\"\n        );\n        // >>> [(0, 18), (1, 14), (2, 22), (3, 18), (4, 24)]\n\n        // Retrieve data points up to time 1 (inclusive).\n        IReadOnlyList<TimeSeriesTuple> res13 = db.TS().Range(\"rg:1\", \"-\", 1);\n        Console.WriteLine(\n            $\"[{string.Join(\", \", res13.Select(t => $\"({t.Time.Value}, {t.Val})\"))}]\"\n        );\n        // >>> [(0, 18), (1, 14)]\n\n        // Retrieve data points from time 3 onwards.\n        IReadOnlyList<TimeSeriesTuple> res14 = db.TS().Range(\"rg:1\", 3, \"+\");\n        Console.WriteLine(\n            $\"[{string.Join(\", \", res14.Select(t => $\"({t.Time.Value}, {t.Val})\"))}]\"\n        );\n        // >>> [(3, 18), (4, 24)]\n\n        // Retrieve all the data points in descending order.\n        IReadOnlyList<TimeSeriesTuple> res15 = db.TS().RevRange(\"rg:1\", \"-\", \"+\");\n        Console.WriteLine(\n            $\"[{string.Join(\", \", res15.Select(t => $\"({t.Time.Value}, {t.Val})\"))}]\"\n        );\n        // >>> [(4, 24), (3, 18), (2, 22), (1, 14), (0, 18)]\n\n        // Retrieve data points up to time 1 (inclusive), but return them\n        // in descending order.\n        IReadOnlyList<TimeSeriesTuple> res16 = db.TS().RevRange(\"rg:1\", \"-\", 1);\n        Console.WriteLine(\n            $\"[{string.Join(\", \", res16.Select(t => $\"({t.Time.Value}, {t.Val})\"))}]\"\n        );\n        // >>> [(1, 14), (0, 18)]","section_id":"query-data-points"},{"id":"query-data-points-ex8","language":"go","code":"// Add 5 data points to a time series named \"rg:1\".\n\tres1, err := rdb.TSCreate(ctx, \"rg:1\").Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> OK\n\n\tres2, err := rdb.TSMAdd(ctx, [][]interface{}{\n\t\t{\"rg:1\", 0, 18},\n\t\t{\"rg:1\", 1, 14},\n\t\t{\"rg:1\", 2, 22},\n\t\t{\"rg:1\", 3, 18},\n\t\t{\"rg:1\", 4, 24},\n\t}).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> [0 1 2 3 4]\n\n\t// Retrieve all the data points in ascending order.\n\t// Note: use 0 and `math.MaxInt64` instead of - and +\n\t// to denote the minimum and maximum possible timestamps.\n\tres3, err := rdb.TSRange(ctx, \"rg:1\", 0, math.MaxInt64).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3)\n\t// >>> [{0 18} {1 14} {2 22} {3 18} {4 24}]\n\n\t// Retrieve data points up to time 1 (inclusive).\n\tres4, err := rdb.TSRange(ctx, \"rg:1\", 0, 1).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4)\n\t// >>> [{0 18} {1 14}]\n\n\t// Retrieve data points from time 3 onwards.\n\tres5, err := rdb.TSRange(ctx, \"rg:1\", 3, math.MaxInt64).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res5)\n\t// >>> [{3 18} {4 24}]\n\n\t// Retrieve all the data points in descending order.\n\tres6, err := rdb.TSRevRange(ctx, \"rg:1\", 0, math.MaxInt64).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res6)\n\t// >>> [{4 24} {3 18} {2 22} {1 14} {0 18}]\n\n\t// Retrieve data points up to time 1 (inclusive), but return them\n\t// in descending order.\n\tres7, err := rdb.TSRevRange(ctx, \"rg:1\", 0, 1).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res7)\n\t// >>> [{1 14} {0 18}]","section_id":"query-data-points"},{"id":"query-data-points-ex9","language":"java","code":"// Add 5 data points to a time series named \"rg:1\"\n        String res10 = jedis.tsCreate(\"rg:1\");\n        System.out.println(res10); // >>> OK\n\n        List<Long> res11 = jedis.tsMAdd(\n            new AbstractMap.SimpleEntry<>(\"rg:1\", new TSElement(0L, 18.0)),\n            new AbstractMap.SimpleEntry<>(\"rg:1\", new TSElement(1L, 14.0)),\n            new AbstractMap.SimpleEntry<>(\"rg:1\", new TSElement(2L, 22.0)),\n            new AbstractMap.SimpleEntry<>(\"rg:1\", new TSElement(3L, 18.0)),\n            new AbstractMap.SimpleEntry<>(\"rg:1\", new TSElement(4L, 24.0))\n        );\n        System.out.println(res11); // >>> [0, 1, 2, 3, 4]\n\n        // Retrieve all the data points in ascending order\n        List<TSElement> res12 = jedis.tsRange(\"rg:1\", 0L, 4L);\n        System.out.println(res12);\n        // >>> [(0:18.0), (1:14.0), (2:22.0), (3:18.0), (4:24.0)]\n\n        // Retrieve data points up to time 1 (inclusive)\n        List<TSElement> res13 = jedis.tsRange(\"rg:1\", 0L, 1L);\n        System.out.println(res13);\n        // >>> [(0:18.0), (1:14.0)]\n\n        // Retrieve data points from time 3 onwards\n        List<TSElement> res14 = jedis.tsRange(\"rg:1\", 3L, 4L);\n        System.out.println(res14);\n        // >>> [(3:18.0), (4:24.0)]\n\n        // Retrieve all the data points in descending order\n        List<TSElement> res15 = jedis.tsRevRange(\"rg:1\", 0L, 4L);\n        System.out.println(res15);\n        // >>> [(4:24.0), (3:18.0), (2:22.0), (1:14.0), (0:18.0)]\n\n        // Retrieve data points up to time 1 (inclusive), in descending order\n        List<TSElement> res16 = jedis.tsRevRange(\"rg:1\", 0L, 1L);\n        System.out.println(res16);\n        // >>> [(1:14.0), (0:18.0)]","section_id":"query-data-points"},{"id":"query-data-points-ex10","language":"javascript","code":"// Add 5 data points to a time series named \"rg:1\".\nconst res10 = await client.ts.create('rg:1');\nconsole.log(res10); // >>> OK\n\nconst res11 = await client.ts.mAdd([\n  { key: 'rg:1', timestamp: 0, value: 18 },\n  { key: 'rg:1', timestamp: 1, value: 14 },\n  { key: 'rg:1', timestamp: 2, value: 22 },\n  { key: 'rg:1', timestamp: 3, value: 18 },\n  { key: 'rg:1', timestamp: 4, value: 24 }\n]);\nconsole.log(res11); // >>> [0, 1, 2, 3, 4]\n\n// Retrieve all the data points in ascending order.\nconst res12 = await client.ts.range('rg:1', '-', '+');\nconsole.log(res12);\n// >>> [{ timestamp: 0, value: 18 }, { timestamp: 1, value: 14 }, ...]\n\n// Retrieve data points up to time 1 (inclusive).\nconst res13 = await client.ts.range('rg:1', '-', 1);\nconsole.log(res13);\n// >>> [{ timestamp: 0, value: 18 }, { timestamp: 1, value: 14 }]\n\n// Retrieve data points from time 3 onwards.\nconst res14 = await client.ts.range('rg:1', 3, '+');\nconsole.log(res14);\n// >>> [{ timestamp: 3, value: 18 }, { timestamp: 4, value: 24 }]\n\n// Retrieve all the data points in descending order.\nconst res15 = await client.ts.revRange('rg:1', '-', '+');\nconsole.log(res15);\n// >>> [{ timestamp: 4, value: 24 }, { timestamp: 3, value: 18 }, ...]\n\n// Retrieve data points up to time 1 (inclusive), but return them\n// in descending order.\nconst res16 = await client.ts.revRange('rg:1', '-', 1);\nconsole.log(res16);\n// >>> [{ timestamp: 1, value: 14 }, { timestamp: 0, value: 18 }]","section_id":"query-data-points"},{"id":"query-data-points-ex11","language":"python","code":"# Add 5 data points to a time series named \"rg:1\".\nres10 = r.ts().create(\"rg:1\")\nprint(res10)  # >>> True\n\nres11 = r.ts().madd([\n        (\"rg:1\", 0, 18),\n        (\"rg:1\", 1, 14),\n        (\"rg:1\", 2, 22),\n        (\"rg:1\", 3, 18),\n        (\"rg:1\", 4, 24),\n])\nprint(res11)  # >>> [0, 1, 2, 3, 4]\n\n# Retrieve all the data points in ascending order.\nres12 = r.ts().range(\"rg:1\", \"-\", \"+\")\nprint(res12)  # >>> [(0, 18.0), (1, 14.0), (2, 22.0), (3, 18.0), (4, 24.0)]\n\n# Retrieve data points up to time 1 (inclusive).\nres13 = r.ts().range(\"rg:1\", \"-\", 1)\nprint(res13)  # >>> [(0, 18.0), (1, 14.0)]\n\n# Retrieve data points from time 3 onwards.\nres14 = r.ts().range(\"rg:1\", 3, \"+\")\nprint(res14)  # >>> [(3, 18.0), (4, 24.0)]\n\n# Retrieve all the data points in descending order.\nres15 = r.ts().revrange(\"rg:1\", \"-\", \"+\")\nprint(res15)  # >>> [(4, 24.0), (3, 18.0), (2, 22.0), (1, 14.0), (0, 18.0)]\n\n# Retrieve data points up to time 1 (inclusive), but return them\n# in descending order.\nres16 = r.ts().revrange(\"rg:1\", \"-\", 1)\nprint(res16)  # >>> [(1, 14.0), (0, 18.0)]","section_id":"query-data-points"},{"id":"query-data-points-ex12","language":"plaintext","code":"# Recreate the rg:1 series so this example runs on its own.\n> DEL rg:1\n(integer) 1\n> TS.CREATE rg:1\nOK\n> TS.MADD rg:1 0 18 rg:1 1 14 rg:1 2 22 rg:1 3 18 rg:1 4 24\n1) (integer) 0\n2) (integer) 1\n3) (integer) 2\n4) (integer) 3\n5) (integer) 4\n> TS.RANGE rg:1 - + FILTER_BY_TS 0 2 4\n1) 1) (integer) 0\n   2) 18\n2) 1) (integer) 2\n   2) 22\n3) 1) (integer) 4\n   2) 24\n> TS.REVRANGE rg:1 - + FILTER_BY_TS 0 2 4 FILTER_BY_VALUE 20 25\n1) 1) (integer) 4\n   2) 24\n2) 1) (integer) 2\n   2) 22\n> TS.REVRANGE rg:1 - + FILTER_BY_TS 0 2 4 FILTER_BY_VALUE 22 22\n1) 1) (integer) 2\n   2) 22","section_id":"query-data-points"},{"id":"query-data-points-ex13","language":"csharp","code":"// Recreate the rg:1 series so this example runs on its own.\n        db.KeyDelete(\"rg:1\");\n        db.TS().Create(\"rg:1\", new TsCreateParamsBuilder().build());\n        db.TS().MAdd(new List<(string, TimeStamp, double)>\n        {\n            (\"rg:1\", 0, 18),\n            (\"rg:1\", 1, 14),\n            (\"rg:1\", 2, 22),\n            (\"rg:1\", 3, 18),\n            (\"rg:1\", 4, 24)\n        });\n\n        var filterByTs = new List<TimeStamp> { 0, 2, 4 };\n        IReadOnlyList<TimeSeriesTuple> res17 = db.TS().Range(\n            \"rg:1\", \"-\", \"+\", filterByTs: filterByTs\n        );\n        Console.WriteLine(\n            $\"[{string.Join(\", \", res17.Select(t => $\"({t.Time.Value}, {t.Val})\"))}]\"\n        );\n        // >>> [(0, 18), (2, 22), (4, 24)]\n\n        IReadOnlyList<TimeSeriesTuple> res18 = db.TS().RevRange(\n            \"rg:1\", \"-\", \"+\",\n            filterByTs: filterByTs,\n            filterByValue: (20, 25)\n        );\n        Console.WriteLine(\n            $\"[{string.Join(\", \", res18.Select(t => $\"({t.Time.Value}, {t.Val})\"))}]\"\n        );\n        // >>> [(4, 24), (2, 22)]\n\n        IReadOnlyList<TimeSeriesTuple> res19 = db.TS().RevRange(\n            \"rg:1\", \"-\", \"+\",\n            filterByTs: filterByTs,\n            filterByValue: (22, 22),\n            count: 1\n        );\n        Console.WriteLine(\n            $\"[{string.Join(\", \", res19.Select(t => $\"({t.Time.Value}, {t.Val})\"))}]\"\n        );\n        // >>> [(2, 22)]","section_id":"query-data-points"},{"id":"query-data-points-ex14","language":"go","code":"// Recreate the rg:1 series so this example runs on its own.\n\trdb.Del(ctx, \"rg:1\")\n\trdb.TSCreate(ctx, \"rg:1\")\n\trdb.TSMAdd(ctx, [][]interface{}{\n\t\t{\"rg:1\", 0, 18},\n\t\t{\"rg:1\", 1, 14},\n\t\t{\"rg:1\", 2, 22},\n\t\t{\"rg:1\", 3, 18},\n\t\t{\"rg:1\", 4, 24},\n\t})\n\n\tres8, err := rdb.TSRangeWithArgs(\n\t\tctx,\n\t\t\"rg:1\",\n\t\t0,\n\t\tmath.MaxInt64,\n\t\t&redis.TSRangeOptions{\n\t\t\tFilterByTS: []int{0, 2, 4},\n\t\t},\n\t).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res8) // >>> [{0 18} {2 22} {4 24}]\n\n\tres9, err := rdb.TSRevRangeWithArgs(\n\t\tctx,\n\t\t\"rg:1\",\n\t\t0,\n\t\tmath.MaxInt64,\n\t\t&redis.TSRevRangeOptions{\n\t\t\tFilterByTS:    []int{0, 2, 4},\n\t\t\tFilterByValue: []int{20, 25},\n\t\t},\n\t).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res9) // >>> [{4 24} {2 22}]\n\n\tres10, err := rdb.TSRevRangeWithArgs(\n\t\tctx,\n\t\t\"rg:1\",\n\t\t0,\n\t\tmath.MaxInt64,\n\t\t&redis.TSRevRangeOptions{\n\t\t\tFilterByTS:    []int{0, 2, 4},\n\t\t\tFilterByValue: []int{22, 22},\n\t\t},\n\t).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res10) // >>> [{2 22}]","section_id":"query-data-points"},{"id":"query-data-points-ex15","language":"java","code":"// Recreate the rg:1 series so this example runs on its own.\n        jedis.del(\"rg:1\");\n        jedis.tsCreate(\"rg:1\");\n        jedis.tsMAdd(\n            new AbstractMap.SimpleEntry<>(\"rg:1\", new TSElement(0L, 18.0)),\n            new AbstractMap.SimpleEntry<>(\"rg:1\", new TSElement(1L, 14.0)),\n            new AbstractMap.SimpleEntry<>(\"rg:1\", new TSElement(2L, 22.0)),\n            new AbstractMap.SimpleEntry<>(\"rg:1\", new TSElement(3L, 18.0)),\n            new AbstractMap.SimpleEntry<>(\"rg:1\", new TSElement(4L, 24.0))\n        );\n\n        List<TSElement> res17 = jedis.tsRange(\"rg:1\",  \n            TSRangeParams.rangeParams()\n                .fromTimestamp(0L)\n                .toTimestamp(4L)\n                .filterByTS(0L, 2L, 4L)\n        );\n        System.out.println(res17);\n        // >>> [(0:18.0), (2:22.0), (4:24.0)]\n\n        List<TSElement> res18 = jedis.tsRevRange(\"rg:1\",\n            TSRangeParams.rangeParams()\n                .fromTimestamp(0L)\n                .toTimestamp(4L)\n                .filterByTS(0L, 2L, 4L)\n                .filterByValues(20.0, 25.0)\n        );\n        System.out.println(res18);\n        // >>> [(4:24.0), (2:22.0)]\n\n        List<TSElement> res19 = jedis.tsRevRange(\"rg:1\",\n            TSRangeParams.rangeParams()\n                .fromTimestamp(0L)\n                .toTimestamp(4L)\n                .filterByTS(0L, 2L, 4L)\n                .filterByValues(22.0, 22.0)\n                .count(1)\n        );\n        System.out.println(res19);\n        // >>> [(2:22.0)]","section_id":"query-data-points"},{"id":"query-data-points-ex16","language":"javascript","code":"// Recreate the rg:1 series so this example runs on its own.\nawait client.del('rg:1');\nawait client.ts.create('rg:1');\nawait client.ts.mAdd([\n  { key: 'rg:1', timestamp: 0, value: 18 },\n  { key: 'rg:1', timestamp: 1, value: 14 },\n  { key: 'rg:1', timestamp: 2, value: 22 },\n  { key: 'rg:1', timestamp: 3, value: 18 },\n  { key: 'rg:1', timestamp: 4, value: 24 }\n]);\n\nconst res17 = await client.ts.range('rg:1', '-', '+', {\n  FILTER_BY_TS: [0, 2, 4]\n});\nconsole.log(res17);\n// >>> [{ timestamp: 0, value: 18 }, { timestamp: 2, value: 22 }, { timestamp: 4, value: 24 }]\n\nconst res18 = await client.ts.revRange('rg:1', '-', '+', {\n  FILTER_BY_TS: [0, 2, 4],\n  FILTER_BY_VALUE: { min: 20, max: 25 }\n});\nconsole.log(res18);\n// >>> [{ timestamp: 4, value: 24 }, { timestamp: 2, value: 22 }]\n\nconst res19 = await client.ts.revRange('rg:1', '-', '+', {\n  FILTER_BY_TS: [0, 2, 4],\n  FILTER_BY_VALUE: { min: 22, max: 22 },\n  COUNT: 1\n});\nconsole.log(res19);\n// >>> [{ timestamp: 2, value: 22 }]","section_id":"query-data-points"},{"id":"query-data-points-ex17","language":"python","code":"# Recreate the rg:1 series so this example runs on its own.\nr.delete(\"rg:1\")\nr.ts().create(\"rg:1\")\nr.ts().madd([\n    (\"rg:1\", 0, 18),\n    (\"rg:1\", 1, 14),\n    (\"rg:1\", 2, 22),\n    (\"rg:1\", 3, 18),\n    (\"rg:1\", 4, 24),\n])\n\nres17 = r.ts().range(\"rg:1\", \"-\", \"+\", filter_by_ts=[0, 2, 4])\nprint(res17)  # >>> [(0, 18.0), (2, 22.0), (4, 24.0)]\n\nres18 = r.ts().revrange(\n    \"rg:1\", \"-\", \"+\",\n    filter_by_ts=[0, 2, 4],\n    filter_by_min_value=20,\n    filter_by_max_value=25,\n)\nprint(res18)  # >>> [(4, 24.0), (2, 22.0)]\n\nres19 = r.ts().revrange(\n    \"rg:1\", \"-\", \"+\",\n    filter_by_ts=[0, 2, 4],\n    filter_by_min_value=22,\n    filter_by_max_value=22,\n    count=1,\n)\nprint(res19)  # >>> [(2, 22.0)]","section_id":"query-data-points"},{"id":"query-multiple-time-series-ex0","language":"plaintext","code":"# Create three new \"rg:\" time series (two in the US\n# and one in the UK, with different units) and add some\n# data points.\n> TS.CREATE rg:2 LABELS location us unit cm\nOK\n> TS.CREATE rg:3 LABELS location us unit in\nOK\n> TS.CREATE rg:4 LABELS location uk unit mm\nOK\n> TS.MADD rg:2 0 1.8 rg:3 0 0.9 rg:4 0 25\n1) (integer) 0\n2) (integer) 0\n3) (integer) 0\n> TS.MADD rg:2 1 2.1 rg:3 1 0.77 rg:4 1 18\n1) (integer) 1\n2) (integer) 1\n3) (integer) 1\n> TS.MADD rg:2 2 2.3 rg:3 2 1.1 rg:4 2 21\n1) (integer) 2\n2) (integer) 2\n3) (integer) 2\n> TS.MADD rg:2 3 1.9 rg:3 3 0.81 rg:4 3 19\n1) (integer) 3\n2) (integer) 3\n3) (integer) 3\n> TS.MADD rg:2 4 1.78 rg:3 4 0.74 rg:4 4 23\n1) (integer) 4\n2) (integer) 4\n3) (integer) 4\n\n# Retrieve the last data point from each US time series. If\n# you don't specify any labels, an empty array is returned\n# for the labels.\n> TS.MGET FILTER location=us\n1) 1) \"rg:2\"\n   2) (empty array)\n   3) 1) (integer) 4\n      2) 1.78\n2) 1) \"rg:3\"\n   2) (empty array)\n   3) 1) (integer) 4\n      2) 7.4E-1\n\n# Retrieve the same data points, but include the `unit`\n# label in the results.\n> TS.MGET SELECTED_LABELS unit FILTER location=us\n1) 1) \"rg:2\"\n   2) 1) 1) \"unit\"\n         2) \"cm\"\n   3) 1) (integer) 4\n      2) 1.78\n2) 1) \"rg:3\"\n   2) 1) 1) \"unit\"\n         2) \"in\"\n   3) 1) (integer) 4\n      2) 7.4E-1\n\n# Retrieve data points up to time 2 (inclusive) from all\n# time series that use millimeters as the unit. Include all\n# labels in the results.\n> TS.MRANGE - 2 WITHLABELS FILTER unit=mm\n1) 1) \"rg:4\"\n   2) 1) 1) \"location\"\n         2) \"uk\"\n      2) 1) \"unit\"\n         2) \"mm\"\n   3) 1) 1) (integer) 0\n         2) 25\n      2) 1) (integer) 1\n         2) 18\n      3) 1) (integer) 2\n         2) 21\n\n# Retrieve data points from time 1 to time 3 (inclusive) from\n# all time series that use centimeters or millimeters as the unit,\n# but only return the `location` label. Return the results\n# in descending order of timestamp.\n> TS.MREVRANGE 1 3 SELECTED_LABELS location FILTER unit=(cm,mm)\n1) 1) \"rg:2\"\n   2) 1) 1) \"location\"\n         2) \"us\"\n   3) 1) 1) (integer) 3\n         2) 1.9\n      2) 1) (integer) 2\n         2) 2.3\n      3) 1) (integer) 1\n         2) 2.1\n2) 1) \"rg:4\"\n   2) 1) 1) \"location\"\n         2) \"uk\"\n   3) 1) 1) (integer) 3\n         2) 19\n      2) 1) (integer) 2\n         2) 21\n      3) 1) (integer) 1\n         2) 18","section_id":"query-multiple-time-series"},{"id":"query-multiple-time-series-ex1","language":"csharp","code":"// Create three new \"rg:\" time series (two in the US\n        // and one in the UK, with different units) and add some\n        // data points.\n        var labels2 = new List<TimeSeriesLabel>\n        {\n            new(\"location\", \"us\"),\n            new(\"unit\", \"cm\")\n        };\n        bool res20 = db.TS().Create(\n            \"rg:2\",\n            new TsCreateParamsBuilder()\n                .AddLabels(labels2)\n                .build()\n        );\n        Console.WriteLine(res20); // >>> True\n\n        var labels3 = new List<TimeSeriesLabel>\n        {\n            new(\"location\", \"us\"),\n            new(\"unit\", \"in\")\n        };\n\n        bool res21 = db.TS().Create(\n            \"rg:3\",\n            new TsCreateParamsBuilder()\n                .AddLabels(labels3)\n                .build()\n        );\n        Console.WriteLine(res21); // >>> True\n\n        var labels4 = new List<TimeSeriesLabel>\n        {\n            new(\"location\", \"uk\"),\n            new(\"unit\", \"mm\")\n        };\n        bool res22 = db.TS().Create(\n            \"rg:4\",\n            new TsCreateParamsBuilder()\n                .AddLabels(labels4)\n                .build()\n        );\n        Console.WriteLine(res22); // >>> True\n\n        var sequence3 = new List<(string, TimeStamp, double)>\n        {\n            (\"rg:2\", 0, 1.8),\n            (\"rg:3\", 0, 0.9),\n            (\"rg:4\", 0, 25)\n        };\n        IReadOnlyList<TimeStamp> res23 = db.TS().MAdd(sequence3);\n        Console.WriteLine(\n            $\"[{string.Join(\", \", res23.Select(t => t.Value))}]\"\n        );\n        // >>> [0, 0, 0]\n\n        var sequence4 = new List<(string, TimeStamp, double)>\n        {\n            (\"rg:2\", 1, 2.1),\n            (\"rg:3\", 1, 0.77),\n            (\"rg:4\", 1, 18)\n        };\n\n        IReadOnlyList<TimeStamp> res24 = db.TS().MAdd(sequence4);\n        Console.WriteLine(\n            $\"[{string.Join(\", \", res24.Select(t => t.Value))}]\"\n        );\n        // >>> [1, 1, 1]\n\n        var sequence5 = new List<(string, TimeStamp, double)>\n        {\n            (\"rg:2\", 2, 2.3),\n            (\"rg:3\", 2, 1.1),\n            (\"rg:4\", 2, 21)\n        };\n\n        IReadOnlyList<TimeStamp> res25 = db.TS().MAdd(sequence5);\n        Console.WriteLine(\n            $\"[{string.Join(\", \", res25.Select(t => t.Value))}]\"\n        );\n        // >>> [2, 2, 2]\n\n        var sequence6 = new List<(string, TimeStamp, double)>\n        {\n            (\"rg:2\", 3, 1.9),\n            (\"rg:3\", 3, 0.81),\n            (\"rg:4\", 3, 19)\n        };\n\n        IReadOnlyList<TimeStamp> res26 = db.TS().MAdd(sequence6);\n        Console.WriteLine(\n            $\"[{string.Join(\", \", res26.Select(t => t.Value))}]\"\n        );\n        // >>> [3, 3, 3]\n\n        var sequence7 = new List<(string, TimeStamp, double)>\n        {\n            (\"rg:2\", 4, 1.78),\n            (\"rg:3\", 4, 0.74),\n            (\"rg:4\", 4, 23)\n        };\n        IReadOnlyList<TimeStamp> res27 = db.TS().MAdd(sequence7);\n        Console.WriteLine(\n            $\"[{string.Join(\", \", res27.Select(t => t.Value))}]\"\n        );\n        // >>> [4, 4, 4]\n\n        // Retrieve the last data point from each US time series. If\n        // you don't specify any labels, an empty array is returned\n        // for the labels.\n        var filters = new List<string> { \"location=us\" };\n        var res28 = db.TS().MGet(filters);\n        Console.WriteLine(res28.Count); // >>> 2\n\n        foreach (var (key, labelsResult, value) in res28)\n        {\n            Console.WriteLine($\"{key}: ({value.Time.Value}, {value.Val})\");\n        }\n        // >>> rg:2: (4, 1.78)\n        // >>> rg:3: (4, 0.74)\n\n        // Retrieve the same data points, but include the `unit`\n        // label in the results.\n        var selectUnitLabel = new List<string> { \"unit\" };\n\n        var res29 = db.TS().MGet(\n            filters,\n            selectedLabels: selectUnitLabel\n        );\n        Console.WriteLine(res29.Count); // >>> 2\n\n        foreach (var (key, labelsResult, value) in res29)\n        {\n            var unitLabel = labelsResult.FirstOrDefault(l => l.Key == \"unit\");\n            Console.WriteLine($\"{key} (unit: {unitLabel?.Value}): ({value.Time.Value}, {value.Val})\");\n        }\n        // >>> rg:2 (unit: cm): (4, 1.78)\n        // >>> rg:3 (unit: in): (4, 0.74)\n\n        // Retrieve data points up to time 2 (inclusive) from all\n        // time series that use millimeters as the unit. Include all\n        // labels in the results.\n        var mmFilters = new List<string> { \"unit=mm\" };\n\n        IReadOnlyList<\n            (string, IReadOnlyList<TimeSeriesLabel>, IReadOnlyList<TimeSeriesTuple>)\n        > res30 = db.TS().MRange(\n            \"-\", 2, mmFilters, withLabels: true\n        );\n        Console.WriteLine(res30.Count); // >>> 1\n\n        foreach (var (key, labelsResult, values) in res30)\n        {\n            Console.WriteLine($\"{key}:\");\n            Console.WriteLine($\"  Labels: ({string.Join(\", \", labelsResult.Select(l => $\"{l.Key}={l.Value}\"))})\");\n            Console.WriteLine($\"  Values: [{string.Join(\", \", values.Select(t => $\"({t.Time.Value}, {t.Val})\"))}]\");\n        }\n        // >>> rg:4:\n        // >>>   Labels:location=uk,unit=mm\n        // >>>   Values: [(1, 23), (2, 21), (3, 19)]\n\n        // Retrieve data points from time 1 to time 3 (inclusive) from\n        // all time series that use centimeters or millimeters as the unit,\n        // but only return the `location` label. Return the results\n        // in descending order of timestamp.\n        var cmMmFilters = new List<string> { \"unit=(cm,mm)\" };\n        var locationLabels = new List<string> { \"location\" };\n        IReadOnlyList<\n            (string, IReadOnlyList<TimeSeriesLabel>, IReadOnlyList<TimeSeriesTuple>)\n        > res31 = db.TS().MRevRange(\n            1, 3, cmMmFilters, selectLabels: locationLabels\n        );\n        Console.WriteLine(res31.Count); // >>> 2\n\n        foreach (var (key, labelsResult, values) in res31)\n        {\n            var locationLabel = labelsResult.FirstOrDefault(l => l.Key == \"location\");\n            Console.WriteLine($\"{key} (location: {locationLabel?.Value})\");\n            Console.WriteLine($\"  Values: [{string.Join(\", \", values.Select(t => $\"({t.Time.Value}, {t.Val})\"))}]\");\n        }\n        // >>> rg:4 (location: uk)\n        // >>>   Values: [(3, 19), (2, 21), (1, 23)]\n        // >>> rg:2 (location: us)\n        // >>>   Values: [(3, 2.3), (2, 2.1), (1, 1.8)]","section_id":"query-multiple-time-series"},{"id":"query-multiple-time-series-ex2","language":"go","code":"// Create three new \"rg:\" time series (two in the US\n\t// and one in the UK, with different units) and add some\n\t// data points.\n\tres20, err := rdb.TSCreateWithArgs(ctx, \"rg:2\", &redis.TSOptions{\n\t\tLabels: map[string]string{\"location\": \"us\", \"unit\": \"cm\"},\n\t}).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res20) // >>> OK\n\n\tres21, err := rdb.TSCreateWithArgs(ctx, \"rg:3\", &redis.TSOptions{\n\t\tLabels: map[string]string{\"location\": \"us\", \"unit\": \"in\"},\n\t}).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res21) // >>> OK\n\n\tres22, err := rdb.TSCreateWithArgs(ctx, \"rg:4\", &redis.TSOptions{\n\t\tLabels: map[string]string{\"location\": \"uk\", \"unit\": \"mm\"},\n\t}).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res22) // >>> OK\n\n\tres23, err := rdb.TSMAdd(ctx, [][]interface{}{\n\t\t{\"rg:2\", 0, 1.8},\n\t\t{\"rg:3\", 0, 0.9},\n\t\t{\"rg:4\", 0, 25},\n\t}).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res23) // >>> [0 0 0]\n\n\tres24, err := rdb.TSMAdd(ctx, [][]interface{}{\n\t\t{\"rg:2\", 1, 2.1},\n\t\t{\"rg:3\", 1, 0.77},\n\t\t{\"rg:4\", 1, 18},\n\t}).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res24) // >>> [1 1 1]\n\n\tres25, err := rdb.TSMAdd(ctx, [][]interface{}{\n\t\t{\"rg:2\", 2, 2.3},\n\t\t{\"rg:3\", 2, 1.1},\n\t\t{\"rg:4\", 2, 21},\n\t}).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res25) // >>> [2 2 2]\n\n\tres26, err := rdb.TSMAdd(ctx, [][]interface{}{\n\t\t{\"rg:2\", 3, 1.9},\n\t\t{\"rg:3\", 3, 0.81},\n\t\t{\"rg:4\", 3, 19},\n\t}).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res26) // >>> [3 3 3]\n\n\tres27, err := rdb.TSMAdd(ctx, [][]interface{}{\n\t\t{\"rg:2\", 4, 1.78},\n\t\t{\"rg:3\", 4, 0.74},\n\t\t{\"rg:4\", 4, 23},\n\t}).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res27) // >>> [4 4 4]\n\n\t// Retrieve the last data point from each US time series.\n\tres28, err := rdb.TSMGet(ctx, []string{\"location=us\"}).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tres28Keys := slices.Collect(maps.Keys(res28))\n\tsort.Strings(res28Keys)\n\n\tfor _, k := range res28Keys {\n\t\tlabels := res28[k][0].(map[interface{}]interface{})\n\n\t\tlabelKeys := make([]string, 0, len(labels))\n\n\t\tfor lk := range labels {\n\t\t\tlabelKeys = append(labelKeys, lk.(string))\n\t\t}\n\n\t\tsort.Strings(labelKeys)\n\n\t\tfmt.Printf(\"%v:\\n\", k)\n\n\t\tfor _, lk := range labelKeys {\n\t\t\tfmt.Printf(\"  %v: %v\\n\", lk, labels[lk])\n\t\t}\n\n\t\tfmt.Printf(\"  %v\\n\", res28[k][1])\n\t}\n\t// >>> rg:2:\n\t// >>>   {4 1.78}\n\t// >>> rg:3:\n\t// >>>   {4 0.74}\n\n\t// Retrieve the same data points, but include the `unit`\n\t// label in the results.\n\tres29, err := rdb.TSMGetWithArgs(\n\t\tctx,\n\t\t[]string{\"location=us\"},\n\t\t&redis.TSMGetOptions{\n\t\t\tSelectedLabels: []interface{}{\"unit\"},\n\t\t},\n\t).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tres29Keys := slices.Collect(maps.Keys(res29))\n\tsort.Strings(res29Keys)\n\n\tfor _, k := range res29Keys {\n\t\tlabels := res29[k][0].(map[interface{}]interface{})\n\n\t\tlabelKeys := make([]string, 0, len(labels))\n\n\t\tfor lk := range labels {\n\t\t\tlabelKeys = append(labelKeys, lk.(string))\n\t\t}\n\n\t\tsort.Strings(labelKeys)\n\n\t\tfmt.Printf(\"%v:\\n\", k)\n\n\t\tfor _, lk := range labelKeys {\n\t\t\tfmt.Printf(\"  %v: %v\\n\", lk, labels[lk])\n\t\t}\n\n\t\tfmt.Printf(\"  %v\\n\", res29[k][1])\n\t}\n\n\t// >>> rg:2:\n\t// >>>   unit: cm\n\t// >>>   [4 1.78]\n\t// >>> rg:3:\n\t// >>>   unit: in\n\t// >>>   [4 0.74]\n\n\t// Retrieve data points up to time 2 (inclusive) from all\n\t// time series that use millimeters as the unit. Include all\n\t// labels in the results.\n\t// Note that the `aggregators` field is empty if you don't\n\t// specify any aggregators.\n\tres30, err := rdb.TSMRangeWithArgs(\n\t\tctx,\n\t\t0,\n\t\t2,\n\t\t[]string{\"unit=mm\"},\n\t\t&redis.TSMRangeOptions{\n\t\t\tWithLabels: true,\n\t\t},\n\t).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tres30Keys := slices.Collect(maps.Keys(res30))\n\tsort.Strings(res30Keys)\n\n\tfor _, k := range res30Keys {\n\t\tlabels := res30[k][0].(map[interface{}]interface{})\n\t\tlabelKeys := make([]string, 0, len(labels))\n\n\t\tfor lk := range labels {\n\t\t\tlabelKeys = append(labelKeys, lk.(string))\n\t\t}\n\n\t\tsort.Strings(labelKeys)\n\n\t\tfmt.Printf(\"%v:\\n\", k)\n\n\t\tfor _, lk := range labelKeys {\n\t\t\tfmt.Printf(\"  %v: %v\\n\", lk, labels[lk])\n\t\t}\n\n\t\tfmt.Printf(\"  Aggregators: %v\\n\", res30[k][1])\n\t\tfmt.Printf(\"  %v\\n\", res30[k][2])\n\t}\n\t// >>> rg:4:\n\t// >>>   location: uk\n\t// >>>   unit: mm\n\t// >>>   Aggregators: map[aggregators:[]]\n\t// >>>   [{0 25} {1 18} {2 21}]\n\n\t// Retrieve data points from time 1 to time 3 (inclusive) from\n\t// all time series that use centimeters or millimeters as the unit,\n\t// but only return the `location` label. Return the results\n\t// in descending order of timestamp.\n\tres31, err := rdb.TSMRevRangeWithArgs(\n\t\tctx,\n\t\t1,\n\t\t3,\n\t\t[]string{\"unit=(cm,mm)\"},\n\t\t&redis.TSMRevRangeOptions{\n\t\t\tSelectedLabels: []interface{}{\"location\"},\n\t\t},\n\t).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tres31Keys := slices.Collect(maps.Keys(res31))\n\tsort.Strings(res31Keys)\n\n\tfor _, k := range res31Keys {\n\t\tlabels := res31[k][0].(map[interface{}]interface{})\n\t\tlabelKeys := make([]string, 0, len(labels))\n\n\t\tfor lk := range labels {\n\t\t\tlabelKeys = append(labelKeys, lk.(string))\n\t\t}\n\n\t\tsort.Strings(labelKeys)\n\n\t\tfmt.Printf(\"%v:\\n\", k)\n\n\t\tfor _, lk := range labelKeys {\n\t\t\tfmt.Printf(\"  %v: %v\\n\", lk, labels[lk])\n\t\t}\n\n\t\tfmt.Printf(\"  Aggregators: %v\\n\", res31[k][1])\n\t\tfmt.Printf(\"  %v\\n\", res31[k][2])\n\t}\n\t// >>> rg:2:\n\t// >>>   location: us\n\t// >>>   Aggregators: map[aggregators:[]]\n\t// >>>   [{3 1.9} {2 2.3} {1 2.1}]\n\t// >>> rg:4:\n\t// >>>   location: uk\n\t// >>>   Aggregators: map[aggregators:[]]\n\t// >>>   [{3 19} {2 21} {1 18}]","section_id":"query-multiple-time-series"},{"id":"query-multiple-time-series-ex3","language":"java","code":"// Create three new \"rg:\" time series (two in the US\n        // and one in the UK, with different units) and add some\n        // data points.\n        Map<String, String> usLabels1 = new HashMap<>();\n        usLabels1.put(\"location\", \"us\");\n        usLabels1.put(\"unit\", \"cm\");\n        \n        Map<String, String> usLabels2 = new HashMap<>();\n        usLabels2.put(\"location\", \"us\");\n        usLabels2.put(\"unit\", \"in\");\n        \n        Map<String, String> ukLabels = new HashMap<>();\n        ukLabels.put(\"location\", \"uk\");\n        ukLabels.put(\"unit\", \"mm\");\n\n        String res20 = jedis.tsCreate(\"rg:2\",\n            TSCreateParams.createParams().labels(usLabels1));\n        System.out.println(res20); // >>> OK\n\n        String res21 = jedis.tsCreate(\"rg:3\",\n            TSCreateParams.createParams().labels(usLabels2));\n        System.out.println(res21); // >>> OK\n\n        String res22 = jedis.tsCreate(\"rg:4\",\n            TSCreateParams.createParams().labels(ukLabels));\n        System.out.println(res22); // >>> OK\n\n        List<Long> res23 = jedis.tsMAdd(\n            new AbstractMap.SimpleEntry<>(\"rg:2\", new TSElement(0L, 1.8)),\n            new AbstractMap.SimpleEntry<>(\"rg:3\", new TSElement(0L, 0.9)),\n            new AbstractMap.SimpleEntry<>(\"rg:4\", new TSElement(0L, 25.0))\n        );\n        System.out.println(res23); // >>> [0, 0, 0]\n\n        List<Long> res24 = jedis.tsMAdd(\n            new AbstractMap.SimpleEntry<>(\"rg:2\", new TSElement(1L, 2.1)),\n            new AbstractMap.SimpleEntry<>(\"rg:3\", new TSElement(1L, 0.77)),\n            new AbstractMap.SimpleEntry<>(\"rg:4\", new TSElement(1L, 18.0))\n        );\n        System.out.println(res24); // >>> [1, 1, 1]\n\n        List<Long> res25 = jedis.tsMAdd(\n            new AbstractMap.SimpleEntry<>(\"rg:2\", new TSElement(2L, 2.3)),\n            new AbstractMap.SimpleEntry<>(\"rg:3\", new TSElement(2L, 1.1)),\n            new AbstractMap.SimpleEntry<>(\"rg:4\", new TSElement(2L, 21.0))\n        );\n        System.out.println(res25); // >>> [2, 2, 2]\n        \n        List<Long> res26 = jedis.tsMAdd(\n            new AbstractMap.SimpleEntry<>(\"rg:2\", new TSElement(3L, 1.9)),\n            new AbstractMap.SimpleEntry<>(\"rg:3\", new TSElement(3L, 0.81)),\n            new AbstractMap.SimpleEntry<>(\"rg:4\", new TSElement(3L, 19.0))\n        );\n        System.out.println(res26); // >>> [3, 3, 3]\n\n        List<Long> res27 = jedis.tsMAdd(\n            new AbstractMap.SimpleEntry<>(\"rg:2\", new TSElement(4L, 1.78)),\n            new AbstractMap.SimpleEntry<>(\"rg:3\", new TSElement(4L, 0.74)),\n            new AbstractMap.SimpleEntry<>(\"rg:4\", new TSElement(4L, 23.0))\n        );\n        System.out.println(res27); // >>> [4, 4, 4]\n\n        // Retrieve the last data point from each US time series.\n        Map<String, TSMGetElement> res28 = jedis.tsMGet(\n            TSMGetParams.multiGetParams().latest(),\n             \"location=us\"\n        );\n        System.out.println(res28);\n        // >>> {rg:2=TSMGetElement{key=rg:2, labels={}, element=(4:1.78)}...\n\n        // Retrieve the same data points, but include the `unit`\n        // label in the results.\n        Map<String, TSMGetElement> res29 = jedis.tsMGet(\n            TSMGetParams.multiGetParams().selectedLabels(\"unit\"), \n            \"location=us\"\n        );\n        System.out.println(res29);\n        // >>> {rg:2=TSMGetElement{key=rg:2, labels={unit=cm}, element=(4:1.78)}...\n\n        // Retrieve data points up to time 2 (inclusive) from all\n        // time series that use millimeters as the unit. Include all\n        // labels in the results.\n        Map<String, TSMRangeElements> res30 = jedis.tsMRange(\n            TSMRangeParams.multiRangeParams(0L, 2L)\n                .withLabels()\n                .filter(\"unit=mm\")\n        );\n        System.out.println(res30);\n        // >>> {rg:4=TSMRangeElements{key=rg:4, labels={location=uk, unit=mm}, value=[(0:25.0), (1:18.0), (2:21.0)]}}\n\n        // Retrieve data points from time 1 to time 3 (inclusive) from\n        // all time series that use centimeters or millimeters as the unit,\n        // but only return the `location` label. Return the results\n        // in descending order of timestamp.\n        Map<String, TSMRangeElements> res31 = jedis.tsMRevRange(\n            TSMRangeParams.multiRangeParams(1L, 3L)\n                .selectedLabels(\"location\")\n                .filter(\"unit=(cm,mm)\")\n        );\n        System.out.println(res31);\n        // >>> {rg:2=TSMRangeElements{key=rg:2, labels={location=us, unit=cm}, value=[(1:2.1)...","section_id":"query-multiple-time-series"},{"id":"query-multiple-time-series-ex4","language":"javascript","code":"// Create three new \"rg:\" time series (two in the US\n// and one in the UK, with different units) and add some\n// data points.\nconst res20 = await client.ts.create('rg:2', {\n  LABELS: { location: 'us', unit: 'cm' }\n});\nconsole.log(res20); // >>> OK\n\nconst res21 = await client.ts.create('rg:3', {\n  LABELS: { location: 'us', unit: 'in' }\n});\nconsole.log(res21); // >>> OK\n\nconst res22 = await client.ts.create('rg:4', {\n  LABELS: { location: 'uk', unit: 'mm' }\n});\nconsole.log(res22); // >>> OK\n\nconst res23 = await client.ts.mAdd([\n  { key: 'rg:2', timestamp: 0, value: 1.8 },\n  { key: 'rg:3', timestamp: 0, value: 0.9 },\n  { key: 'rg:4', timestamp: 0, value: 25 }\n]);\nconsole.log(res23); // >>> [0, 0, 0]\n\nconst res24 = await client.ts.mAdd([\n  { key: 'rg:2', timestamp: 1, value: 2.1 },\n  { key: 'rg:3', timestamp: 1, value: 0.77 },\n  { key: 'rg:4', timestamp: 1, value: 18 }\n]);\nconsole.log(res24); // >>> [1, 1, 1]\n\nconst res25 = await client.ts.mAdd([\n  { key: 'rg:2', timestamp: 2, value: 2.3 },\n  { key: 'rg:3', timestamp: 2, value: 1.1 },\n  { key: 'rg:4', timestamp: 2, value: 21 }\n]);\nconsole.log(res25); // >>> [2, 2, 2]\n\nconst res26 = await client.ts.mAdd([\n  { key: 'rg:2', timestamp: 3, value: 1.9 },\n  { key: 'rg:3', timestamp: 3, value: 0.81 },\n  { key: 'rg:4', timestamp: 3, value: 19 }\n]);\nconsole.log(res26); // >>> [3, 3, 3]\n\nconst res27 = await client.ts.mAdd([\n  { key: 'rg:2', timestamp: 4, value: 1.78 },\n  { key: 'rg:3', timestamp: 4, value: 0.74 },\n  { key: 'rg:4', timestamp: 4, value: 23 }\n]);\nconsole.log(res27); // >>> [4, 4, 4]\n\n// Retrieve the last data point from each US time series.\nconst res28 = await client.ts.mGet(['location=us']);\nconsole.log(res28);\n// >>> { \"rg:2\": { sample: { timestamp: 4, value: 1.78 } }, \"rg:3\": { sample: { timestamp: 4, value: 0.74 } } }\n\n// Retrieve the same data points, but include the `unit`\n// label in the results.\nconst res29 = await client.ts.mGetSelectedLabels(['location=us'], ['unit']);\nconsole.log(res29);\n// >>> { \"rg:2\": { labels: { unit: 'cm' }, sample: { timestamp: 4, value: 1.78 } }, \"rg:3\": { labels: { unit: 'in' }, sample: { timestamp: 4, value: 0.74 } } }\n\n// Retrieve data points up to time 2 (inclusive) from all\n// time series that use millimeters as the unit. Include all\n// labels in the results.\nconst res30 = await client.ts.mRangeWithLabels('-', 2, 'unit=mm');\nconsole.log(res30);\n// >>> { \"rg:4\": { labels: { location: 'uk', unit: 'mm' }, samples: [\n//   { timestamp: 0, value: 25 },\n//   { timestamp: 1, value: 18 },\n//   { timestamp: 2, value: 21 }\n// ] } }\n\n// Retrieve data points from time 1 to time 3 (inclusive) from\n// all time series that use centimeters or millimeters as the unit,\n// but only return the `location` label. Return the results\n// in descending order of timestamp.\nconst res31 = await client.ts.mRevRangeSelectedLabels(\n  1, 3,\n  ['location'],\n  ['unit=(cm,mm)']\n);\nconsole.log(res31);\n// >>> { \"rg:2\": { labels: { location: 'us' }, samples: [\n//   { timestamp: 3, value: 1.9 },\n//   { timestamp: 2, value: 2.3 },\n//   { timestamp: 1, value: 2.1 }\n// ] }, \"rg:4\": { labels: { location: 'uk' }, samples: [\n//   { timestamp: 3, value: 19 },\n//   { timestamp: 2, value: 21 },\n//   { timestamp: 1, value: 18 }\n// ] } }","section_id":"query-multiple-time-series"},{"id":"query-multiple-time-series-ex5","language":"python","code":"# Create three new \"rg:\" time series (two in the US\n# and one in the UK, with different units) and add some\n# data points.\nres20 = r.ts().create(\n    \"rg:2\",\n    labels={\"location\": \"us\", \"unit\": \"cm\"},\n)\nprint(res20)  # >>> True\n\nres21 = r.ts().create(\n    \"rg:3\",\n    labels={\"location\": \"us\", \"unit\": \"in\"},\n)\nprint(res21)  # >>> True\n\nres22 = r.ts().create(\n    \"rg:4\",\n    labels={\"location\": \"uk\", \"unit\": \"mm\"},\n)\nprint(res22)  # >>> True\n\nres23 = r.ts().madd([\n        (\"rg:2\", 0, 1.8),\n        (\"rg:3\", 0, 0.9),\n        (\"rg:4\", 0, 25),\n])\nprint(res23)  # >>> [0, 0, 0]\n\nres24 = r.ts().madd([\n        (\"rg:2\", 1, 2.1),\n        (\"rg:3\", 1, 0.77),\n        (\"rg:4\", 1, 18),\n])\nprint(res24)  # >>> [1, 1, 1]\n\nres25 = r.ts().madd([\n        (\"rg:2\", 2, 2.3),\n        (\"rg:3\", 2, 1.1),\n        (\"rg:4\", 2, 21),\n])\nprint(res25)  # >>> [2, 2, 2]\n\nres26 = r.ts().madd([\n        (\"rg:2\", 3, 1.9),\n        (\"rg:3\", 3, 0.81),\n        (\"rg:4\", 3, 19),\n])\nprint(res26)  # >>> [3, 3, 3]\n\nres27 = r.ts().madd([\n        (\"rg:2\", 4, 1.78),\n        (\"rg:3\", 4, 0.74),\n        (\"rg:4\", 4, 23),\n])\nprint(res27)  # >>> [4, 4, 4]\n\n# Retrieve the last data point from each US time series. If\n# you don't specify any labels, an empty array is returned\n# for the labels.\nres28 = r.ts().mget([\"location=us\"])\nprint(res28)  # >>> [{'rg:2': [{}, 4, 1.78]}, {'rg:3': [{}, 4, 0.74]}]\n\n# Retrieve the same data points, but include the `unit`\n# label in the results.\nres29 = r.ts().mget([\"location=us\"], select_labels=[\"unit\"])\nprint(res29)\n# >>> [{'rg:2': [{'unit': 'cm'}, 4, 1.78]}, {'rg:3': [{'unit': 'in'}, 4, 0.74]}]\n\n# Retrieve data points up to time 2 (inclusive) from all\n# time series that use millimeters as the unit. Include all\n# labels in the results.\nres30 = r.ts().mrange(\n    \"-\", 2, filters=[\"unit=mm\"], with_labels=True\n)\nprint(res30)\n# >>> [{'rg:4': [{'location': 'uk', 'unit': 'mm'}, [(0, 25.4),...\n\n# Retrieve data points from time 1 to time 3 (inclusive) from\n# all time series that use centimeters or millimeters as the unit,\n# but only return the `location` label. Return the results\n# in descending order of timestamp.\nres31 = r.ts().mrevrange(\n    1, 3, filters=[\"unit=(cm,mm)\"], select_labels=[\"location\"]\n)\nprint(res31)\n# >>> [[{'location': 'uk'}, (3, 19.0), (2, 21.0), (1, 18.0)],...","section_id":"query-multiple-time-series"},{"id":"aggregation-ex0","language":"plaintext","code":"# Recreate the rg:2 series so this example runs on its own.\n> DEL rg:2\n(integer) 1\n> TS.CREATE rg:2\nOK\n> TS.MADD rg:2 0 1.8 rg:2 1 2.1 rg:2 2 2.3 rg:2 3 1.9 rg:2 4 1.78\n1) (integer) 0\n2) (integer) 1\n3) (integer) 2\n4) (integer) 3\n5) (integer) 4\n> TS.RANGE rg:2 - + AGGREGATION avg 2\n1) 1) (integer) 0\n   2) 1.9500000000000002\n2) 1) (integer) 2\n   2) 2.0999999999999996\n3) 1) (integer) 4\n   2) 1.78","section_id":"aggregation"},{"id":"aggregation-ex1","language":"csharp","code":"// Recreate the rg:2 series so this example runs on its own.\n        db.KeyDelete(\"rg:2\");\n        db.TS().Create(\"rg:2\", new TsCreateParamsBuilder().build());\n        db.TS().MAdd(new List<(string, TimeStamp, double)>\n        {\n            (\"rg:2\", 0, 1.8),\n            (\"rg:2\", 1, 2.1),\n            (\"rg:2\", 2, 2.3),\n            (\"rg:2\", 3, 1.9),\n            (\"rg:2\", 4, 1.78)\n        });\n\n        IReadOnlyList<TimeSeriesTuple> res32 = db.TS().Range(\n            \"rg:2\", \"-\", \"+\",\n            aggregation: TsAggregation.Avg,\n            timeBucket: 2\n        );\n        Console.WriteLine($\"[{string.Join(\", \", res32.Select(t => $\"({t.Time.Value}, {t.Val})\"))}]\");\n        // >>> [(0, 1.95), (2, 2.1), (4, 1.78)]","section_id":"aggregation"},{"id":"aggregation-ex2","language":"go","code":"// Recreate the rg:2 series so this example runs on its own.\n\trdb.Del(ctx, \"rg:2\")\n\trdb.TSCreate(ctx, \"rg:2\")\n\trdb.TSMAdd(ctx, [][]interface{}{\n\t\t{\"rg:2\", 0, 1.8},\n\t\t{\"rg:2\", 1, 2.1},\n\t\t{\"rg:2\", 2, 2.3},\n\t\t{\"rg:2\", 3, 1.9},\n\t\t{\"rg:2\", 4, 1.78},\n\t})\n\n\tres32, err := rdb.TSRangeWithArgs(\n\t\tctx,\n\t\t\"rg:2\",\n\t\t0,\n\t\tmath.MaxInt64,\n\t\t&redis.TSRangeOptions{\n\t\t\tAggregator:     redis.Avg,\n\t\t\tBucketDuration: 2,\n\t\t},\n\t).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res32)\n\t// >>> [{0 1.9500000000000002} {2 2.0999999999999996} {4 1.78}]","section_id":"aggregation"},{"id":"aggregation-ex3","language":"java","code":"// Recreate the rg:2 series so this example runs on its own.\n        jedis.del(\"rg:2\");\n        jedis.tsCreate(\"rg:2\");\n        jedis.tsMAdd(\n            new AbstractMap.SimpleEntry<>(\"rg:2\", new TSElement(0L, 1.8)),\n            new AbstractMap.SimpleEntry<>(\"rg:2\", new TSElement(1L, 2.1)),\n            new AbstractMap.SimpleEntry<>(\"rg:2\", new TSElement(2L, 2.3)),\n            new AbstractMap.SimpleEntry<>(\"rg:2\", new TSElement(3L, 1.9)),\n            new AbstractMap.SimpleEntry<>(\"rg:2\", new TSElement(4L, 1.78))\n        );\n\n        List<TSElement> res32 = jedis.tsRange(\"rg:2\",\n            TSRangeParams.rangeParams()\n                .fromTimestamp(0L)\n                .toTimestamp(4L)\n                .aggregation(AggregationType.AVG, 2)\n        );\n        System.out.println(res32);\n        // >>> [(0:1.9500000000000002), (2:2.0999999999999996), (4:1.78)]","section_id":"aggregation"},{"id":"aggregation-ex4","language":"javascript","code":"// Recreate the rg:2 series so this example runs on its own.\nawait client.del('rg:2');\nawait client.ts.create('rg:2');\nawait client.ts.mAdd([\n  { key: 'rg:2', timestamp: 0, value: 1.8 },\n  { key: 'rg:2', timestamp: 1, value: 2.1 },\n  { key: 'rg:2', timestamp: 2, value: 2.3 },\n  { key: 'rg:2', timestamp: 3, value: 1.9 },\n  { key: 'rg:2', timestamp: 4, value: 1.78 }\n]);\n\nconst res32 = await client.ts.range('rg:2', '-', '+', {\n  AGGREGATION: {\n    type: TIME_SERIES_AGGREGATION_TYPE.AVG,\n    timeBucket: 2\n  }\n});\nconsole.log(res32);\n// >>> [{ timestamp: 0, value: 1.9500000000000002 },{ timestamp: 2, value: 2.0999999999999996 }, { timestamp: 4, value: 1.78 }]","section_id":"aggregation"},{"id":"aggregation-ex5","language":"python","code":"# Recreate the rg:2 series so this example runs on its own.\nr.delete(\"rg:2\")\nr.ts().create(\"rg:2\")\nr.ts().madd([\n    (\"rg:2\", 0, 1.8),\n    (\"rg:2\", 1, 2.1),\n    (\"rg:2\", 2, 2.3),\n    (\"rg:2\", 3, 1.9),\n    (\"rg:2\", 4, 1.78),\n])\n\nres32 = r.ts().range(\n    \"rg:2\", \"-\", \"+\",\n    aggregation_type=\"avg\",\n    bucket_size_msec=2\n)\nprint(res32)\n# >>> [(0, 1.9500000000000002), (2, 2.0999999999999996), (4, 1.78)]","section_id":"aggregation"},{"id":"aggregation-ex6","language":"bash","code":"TS.RANGE rg:2 - + AGGREGATION min,avg,max 2","section_id":"aggregation"},{"id":"bucket-alignment-ex0","language":"plaintext","code":"> TS.CREATE sensor3\nOK\n> TS.MADD sensor3 10 1000 sensor3 20 2000 sensor3 30 3000 sensor3 40 4000 sensor3 50 5000 sensor3 60 6000 sensor3 70 7000\n1) (integer) 10\n2) (integer) 20\n3) (integer) 30\n4) (integer) 40\n5) (integer) 50\n6) (integer) 60\n7) (integer) 70\n> TS.RANGE sensor3 10 70 AGGREGATION min 25\n1) 1) (integer) 0\n   2) 1000\n2) 1) (integer) 25\n   2) 3000\n3) 1) (integer) 50\n   2) 5000","section_id":"bucket-alignment"},{"id":"bucket-alignment-ex1","language":"csharp","code":"bool res33 = db.TS().Create(\n            \"sensor3\",\n            new TsCreateParamsBuilder()\n                .build()\n        );\n        Console.WriteLine(res33); // >>> True\n\n        var sensorSequence = new List<(string, TimeStamp, double)>\n        {\n            (\"sensor3\", 10, 1000),\n            (\"sensor3\", 20, 2000),\n            (\"sensor3\", 30, 3000),\n            (\"sensor3\", 40, 4000),\n            (\"sensor3\", 50, 5000),\n            (\"sensor3\", 60, 6000),\n            (\"sensor3\", 70, 7000)\n        };\n        IReadOnlyList<TimeStamp> res34 = db.TS().MAdd(sensorSequence);\n        Console.WriteLine($\"[{string.Join(\", \", res34.Select(t => t.Value))}]\");\n        // >>> [10, 20, 30, 40, 50, 60, 70]\n\n        IReadOnlyList<TimeSeriesTuple> res35 = db.TS().Range(\n            \"sensor3\", 10, 70,\n            aggregation: TsAggregation.Min,\n            timeBucket: 25\n        );\n        Console.WriteLine($\"[{string.Join(\", \", res35.Select(t => $\"({t.Time.Value}, {t.Val})\"))}]\");\n        // >>> [(0, 1000), (25, 3000), (50, 5000)]","section_id":"bucket-alignment"},{"id":"bucket-alignment-ex2","language":"go","code":"res1, err := rdb.TSCreate(ctx, \"sensor3\").Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> OK\n\n\tres2, err := rdb.TSMAdd(ctx, [][]interface{}{\n\t\t{\"sensor3\", 10, 1000},\n\t\t{\"sensor3\", 20, 2000},\n\t\t{\"sensor3\", 30, 3000},\n\t\t{\"sensor3\", 40, 4000},\n\t\t{\"sensor3\", 50, 5000},\n\t\t{\"sensor3\", 60, 6000},\n\t\t{\"sensor3\", 70, 7000},\n\t}).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> [10 20 30 40 50 60 70]\n\n\tres3, err := rdb.TSRangeWithArgs(\n\t\tctx,\n\t\t\"sensor3\",\n\t\t10,\n\t\t70,\n\t\t&redis.TSRangeOptions{\n\t\t\tAggregator:     redis.Min,\n\t\t\tBucketDuration: 25,\n\t\t},\n\t).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3) // >>> [{0 1000} {25 3000} {50 5000}]","section_id":"bucket-alignment"},{"id":"bucket-alignment-ex3","language":"java","code":"String res33 = jedis.tsCreate(\"sensor3\");\n        System.out.println(res33); // >>> OK\n\n        List<Long> res34 = jedis.tsMAdd(\n            new AbstractMap.SimpleEntry<>(\"sensor3\", new TSElement(10L, 1000.0)),\n            new AbstractMap.SimpleEntry<>(\"sensor3\", new TSElement(20L, 2000.0)),\n            new AbstractMap.SimpleEntry<>(\"sensor3\", new TSElement(30L, 3000.0)),\n            new AbstractMap.SimpleEntry<>(\"sensor3\", new TSElement(40L, 4000.0)),\n            new AbstractMap.SimpleEntry<>(\"sensor3\", new TSElement(50L, 5000.0)),\n            new AbstractMap.SimpleEntry<>(\"sensor3\", new TSElement(60L, 6000.0)),\n            new AbstractMap.SimpleEntry<>(\"sensor3\", new TSElement(70L, 7000.0))\n        );\n        System.out.println(res34); // >>> [10, 20, 30, 40, 50, 60, 70]\n\n        List<TSElement> res35 = jedis.tsRange(\"sensor3\",\n            TSRangeParams.rangeParams()\n                .fromTimestamp(10L)\n                .toTimestamp(70L)\n                .aggregation(AggregationType.MIN, 25)\n        );\n        System.out.println(res35);\n        // >>> [(0:1000.0), (25:3000.0), (50:5000.0)]","section_id":"bucket-alignment"},{"id":"bucket-alignment-ex4","language":"javascript","code":"const res33 = await client.ts.create('sensor3');\nconsole.log(res33); // >>> OK\n\nconst res34 = await client.ts.mAdd([\n  { key: 'sensor3', timestamp: 10, value: 1000 },\n  { key: 'sensor3', timestamp: 20, value: 2000 },\n  { key: 'sensor3', timestamp: 30, value: 3000 },\n  { key: 'sensor3', timestamp: 40, value: 4000 },\n  { key: 'sensor3', timestamp: 50, value: 5000 },\n  { key: 'sensor3', timestamp: 60, value: 6000 },\n  { key: 'sensor3', timestamp: 70, value: 7000 }\n]);\nconsole.log(res34); // >>> [10, 20, 30, 40, 50, 60, 70]\n\nconst res35 = await client.ts.range('sensor3', 10, 70, {\n  AGGREGATION: {\n    type: TIME_SERIES_AGGREGATION_TYPE.MIN,\n    timeBucket: 25\n  }\n});\nconsole.log(res35);\n// >>> [{ timestamp: 0, value: 1000 }, { timestamp: 25, value: 3000 }, { timestamp: 50, value: 5000 }]","section_id":"bucket-alignment"},{"id":"bucket-alignment-ex5","language":"python","code":"res33 = r.ts().create(\"sensor3\")\nprint(res33)  # >>> True\n\nres34 = r.ts().madd([\n    (\"sensor3\", 10, 1000),\n    (\"sensor3\", 20, 2000),\n    (\"sensor3\", 30, 3000),\n    (\"sensor3\", 40, 4000),\n    (\"sensor3\", 50, 5000),\n    (\"sensor3\", 60, 6000),\n    (\"sensor3\", 70, 7000),\n])\nprint(res34)  # >>> [10, 20, 30, 40, 50, 60, 70]\n\nres35 = r.ts().range(\n    \"sensor3\", 10, 70,\n    aggregation_type=\"min\",\n    bucket_size_msec=25\n)\nprint(res35)\n# >>> [(0, 1000.0), (25, 3000.0), (50, 5000.0)]","section_id":"bucket-alignment"},{"id":"bucket-alignment-ex6","language":"plaintext","code":"Value:        |      (1000)     (2000)     (3000)     (4000)     (5000)     (6000)     (7000)\nTimestamp:    |-------|10|-------|20|-------|30|-------|40|-------|50|-------|60|-------|70|--->\n\nBucket(25ms): |_________________________||_________________________||___________________________|\n                           V                          V                           V\n                  min(1000, 2000)=1000      min(3000, 4000)=3000     min(5000, 6000, 7000)=5000","section_id":"bucket-alignment"},{"id":"bucket-alignment-ex7","language":"plaintext","code":"# Recreate the sensor3 series so this example runs on its own.\n> DEL sensor3\n(integer) 1\n> TS.CREATE sensor3\nOK\n> TS.MADD sensor3 10 1000 sensor3 20 2000 sensor3 30 3000 sensor3 40 4000 sensor3 50 5000 sensor3 60 6000 sensor3 70 7000\n1) (integer) 10\n2) (integer) 20\n3) (integer) 30\n4) (integer) 40\n5) (integer) 50\n6) (integer) 60\n7) (integer) 70\n> TS.RANGE sensor3 10 70 AGGREGATION min 25 ALIGN start\n1) 1) (integer) 10\n   2) 1000\n2) 1) (integer) 35\n   2) 4000\n3) 1) (integer) 60\n   2) 6000","section_id":"bucket-alignment"},{"id":"bucket-alignment-ex8","language":"csharp","code":"// Recreate the sensor3 series so this example runs on its own.\n        db.KeyDelete(\"sensor3\");\n        db.TS().Create(\"sensor3\", new TsCreateParamsBuilder().build());\n        db.TS().MAdd(new List<(string, TimeStamp, double)>\n        {\n            (\"sensor3\", 10, 1000),\n            (\"sensor3\", 20, 2000),\n            (\"sensor3\", 30, 3000),\n            (\"sensor3\", 40, 4000),\n            (\"sensor3\", 50, 5000),\n            (\"sensor3\", 60, 6000),\n            (\"sensor3\", 70, 7000)\n        });\n\n        IReadOnlyList<TimeSeriesTuple> res36 = db.TS().Range(\n            \"sensor3\", 10, 70,\n            aggregation: TsAggregation.Min,\n            timeBucket: 25,\n            align: \"-\"\n        );\n        Console.WriteLine(\n            $\"[{string.Join(\", \", res36.Select(t => $\"({t.Time.Value}, {t.Val})\"))}]\"\n        );\n        // >>> [(10, 1000), (35, 4000), (60, 6000)]","section_id":"bucket-alignment"},{"id":"bucket-alignment-ex9","language":"go","code":"// Recreate the sensor3 series so this example runs on its own.\n\trdb.Del(ctx, \"sensor3\")\n\trdb.TSCreate(ctx, \"sensor3\")\n\trdb.TSMAdd(ctx, [][]interface{}{\n\t\t{\"sensor3\", 10, 1000},\n\t\t{\"sensor3\", 20, 2000},\n\t\t{\"sensor3\", 30, 3000},\n\t\t{\"sensor3\", 40, 4000},\n\t\t{\"sensor3\", 50, 5000},\n\t\t{\"sensor3\", 60, 6000},\n\t\t{\"sensor3\", 70, 7000},\n\t})\n\n\tres4, err := rdb.TSRangeWithArgs(\n\t\tctx,\n\t\t\"sensor3\",\n\t\t10,\n\t\t70,\n\t\t&redis.TSRangeOptions{\n\t\t\tAggregator:     redis.Min,\n\t\t\tBucketDuration: 25,\n\t\t\tAlign:          \"START\",\n\t\t},\n\t).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4) // >>> [{10 1000} {35 4000} {60 6000}]","section_id":"bucket-alignment"},{"id":"bucket-alignment-ex10","language":"java","code":"// Recreate the sensor3 series so this example runs on its own.\n        jedis.del(\"sensor3\");\n        jedis.tsCreate(\"sensor3\");\n        jedis.tsMAdd(\n            new AbstractMap.SimpleEntry<>(\"sensor3\", new TSElement(10L, 1000.0)),\n            new AbstractMap.SimpleEntry<>(\"sensor3\", new TSElement(20L, 2000.0)),\n            new AbstractMap.SimpleEntry<>(\"sensor3\", new TSElement(30L, 3000.0)),\n            new AbstractMap.SimpleEntry<>(\"sensor3\", new TSElement(40L, 4000.0)),\n            new AbstractMap.SimpleEntry<>(\"sensor3\", new TSElement(50L, 5000.0)),\n            new AbstractMap.SimpleEntry<>(\"sensor3\", new TSElement(60L, 6000.0)),\n            new AbstractMap.SimpleEntry<>(\"sensor3\", new TSElement(70L, 7000.0))\n        );\n\n        List<TSElement> res36 = jedis.tsRange(\"sensor3\",\n            TSRangeParams.rangeParams()\n                .fromTimestamp(10L)\n                .toTimestamp(70L)\n                .aggregation(AggregationType.MIN, 25)\n                .alignStart()\n        );\n        System.out.println(res36);\n        // >>> [(10:1000.0), (35:4000.0), (60:6000.0)]","section_id":"bucket-alignment"},{"id":"bucket-alignment-ex11","language":"javascript","code":"// Recreate the sensor3 series so this example runs on its own.\nawait client.del('sensor3');\nawait client.ts.create('sensor3');\nawait client.ts.mAdd([\n  { key: 'sensor3', timestamp: 10, value: 1000 },\n  { key: 'sensor3', timestamp: 20, value: 2000 },\n  { key: 'sensor3', timestamp: 30, value: 3000 },\n  { key: 'sensor3', timestamp: 40, value: 4000 },\n  { key: 'sensor3', timestamp: 50, value: 5000 },\n  { key: 'sensor3', timestamp: 60, value: 6000 },\n  { key: 'sensor3', timestamp: 70, value: 7000 }\n]);\n\nconst res36 = await client.ts.range('sensor3', 10, 70, {\n  AGGREGATION: {\n    type: TIME_SERIES_AGGREGATION_TYPE.MIN,\n    timeBucket: 25\n  },\n  ALIGN: 'START'\n});\nconsole.log(res36);\n// >>> [{ timestamp: 10, value: 1000 }, { timestamp: 35, value: 4000 }, { timestamp: 60, value: 6000 }]","section_id":"bucket-alignment"},{"id":"bucket-alignment-ex12","language":"python","code":"# Recreate the sensor3 series so this example runs on its own.\nr.delete(\"sensor3\")\nr.ts().create(\"sensor3\")\nr.ts().madd([\n    (\"sensor3\", 10, 1000),\n    (\"sensor3\", 20, 2000),\n    (\"sensor3\", 30, 3000),\n    (\"sensor3\", 40, 4000),\n    (\"sensor3\", 50, 5000),\n    (\"sensor3\", 60, 6000),\n    (\"sensor3\", 70, 7000),\n])\n\nres36 = r.ts().range(\n    \"sensor3\", 10, 70,\n    aggregation_type=\"min\",\n    bucket_size_msec=25,\n    align=\"START\"\n)\nprint(res36)\n# >>> [(10, 1000.0), (35, 4000.0), (60, 6000.0)]","section_id":"bucket-alignment"},{"id":"bucket-alignment-ex13","language":"plaintext","code":"Value:        |      (1000)     (2000)     (3000)     (4000)     (5000)     (6000)     (7000)\nTimestamp:    |-------|10|-------|20|-------|30|-------|40|-------|50|-------|60|-------|70|--->\n\nBucket(25ms):          |__________________________||_________________________||___________________________|\n                                    V                          V                           V\n                        min(1000, 2000, 3000)=1000      min(4000, 5000)=4000     min(6000, 7000)=6000","section_id":"bucket-alignment"},{"id":"aggregation-across-timeseries-ex0","language":"plaintext","code":"> TS.CREATE wind:1 LABELS country uk\nOK\n> TS.CREATE wind:2 LABELS country uk\nOK\n> TS.CREATE wind:3 LABELS country us\nOK\n> TS.CREATE wind:4 LABELS country us\nOK\n> TS.MADD wind:1 1 12 wind:2 1 18 wind:3 1 5 wind:4 1 20\n1) (integer) 1\n2) (integer) 1\n3) (integer) 1\n4) (integer) 1\n> TS.MADD wind:1 2 14 wind:2 2 21 wind:3 2 4 wind:4 2 25\n1) (integer) 2\n2) (integer) 2\n3) (integer) 2\n4) (integer) 2\n> TS.MADD wind:1 3 10 wind:2 3 24 wind:3 3 8 wind:4 3 18\n1) (integer) 3\n2) (integer) 3\n3) (integer) 3\n4) (integer) 3\n\n# The result pairs contain the timestamp and the maximum sample value\n# for the country at that timestamp.\n> TS.MRANGE - + FILTER country=(us,uk) GROUPBY country REDUCE max\n1) 1) \"country=uk\"\n   2) (empty array)\n   3) 1) 1) (integer) 1\n         2) 18\n      2) 1) (integer) 2\n         2) 21\n      3) 1) (integer) 3\n         2) 24\n2) 1) \"country=us\"\n   2) (empty array)\n   3) 1) 1) (integer) 1\n         2) 20\n      2) 1) (integer) 2\n         2) 25\n      3) 1) (integer) 3\n         2) 18\n\n# The result pairs contain the timestamp and the average sample value\n# for the country at that timestamp.\n> TS.MRANGE - + FILTER country=(us,uk) GROUPBY country REDUCE avg\n1) 1) \"country=uk\"\n   2) (empty array)\n   3) 1) 1) (integer) 1\n         2) 15\n      2) 1) (integer) 2\n         2) 17.5\n      3) 1) (integer) 3\n         2) 17\n2) 1) \"country=us\"\n   2) (empty array)\n   3) 1) 1) (integer) 1\n         2) 12.5\n      2) 1) (integer) 2\n         2) 14.5\n      3) 1) (integer) 3\n         2) 13","section_id":"aggregation-across-timeseries"},{"id":"aggregation-across-timeseries-ex1","language":"csharp","code":"var ukLabels = new List<TimeSeriesLabel> { new(\"country\", \"uk\") };\n\n        bool res37 = db.TS().Create(\n            \"wind:1\",\n            new TsCreateParamsBuilder()\n                .AddLabels(ukLabels)\n                .build()\n        );\n        Console.WriteLine(res37); // >>> True\n\n        bool res38 = db.TS().Create(\n            \"wind:2\",\n            new TsCreateParamsBuilder()\n                .AddLabels(ukLabels)\n                .build()\n        );\n        Console.WriteLine(res38); // >>> True\n\n        var usLabels = new List<TimeSeriesLabel> { new(\"country\", \"us\") };\n        bool res39 = db.TS().Create(\n            \"wind:3\",\n            new TsCreateParamsBuilder()\n                .AddLabels(usLabels)\n                .build()\n        );\n        Console.WriteLine(res39); // >>> True\n\n        bool res40 = db.TS().Create(\n            \"wind:4\",\n            new TsCreateParamsBuilder()\n                .AddLabels(usLabels)\n                .build()\n        );\n        Console.WriteLine(res40); // >>> True\n\n        var windSequence1 = new List<(string, TimeStamp, double)>\n        {\n            (\"wind:1\", 1, 12),\n            (\"wind:2\", 1, 18),\n            (\"wind:3\", 1, 5),\n            (\"wind:4\", 1, 20)\n        };\n        IReadOnlyList<TimeStamp> res41 = db.TS().MAdd(windSequence1);\n        Console.WriteLine(\n            $\"[{string.Join(\", \", res41.Select(t => t.Value))}]\"\n        );\n        // >>> [1, 1, 1, 1]\n\n        var windSequence2 = new List<(string, TimeStamp, double)>\n        {\n            (\"wind:1\", 2, 14),\n            (\"wind:2\", 2, 21),\n            (\"wind:3\", 2, 4),\n            (\"wind:4\", 2, 25)\n        };\n        IReadOnlyList<TimeStamp> res42 = db.TS().MAdd(windSequence2);\n        Console.WriteLine(\n            $\"[{string.Join(\", \", res42.Select(t => t.Value))}]\"\n        );\n        // >>> [2, 2, 2, 2]\n\n        var windSequence3 = new List<(string, TimeStamp, double)>\n        {\n            (\"wind:1\", 3, 10),\n            (\"wind:2\", 3, 24),\n            (\"wind:3\", 3, 8),\n            (\"wind:4\", 3, 18)\n        };\n        IReadOnlyList<TimeStamp> res43 = db.TS().MAdd(windSequence3);\n        Console.WriteLine(\n            $\"[{string.Join(\", \", res43.Select(t => t.Value))}]\"\n        );\n        // >>> [3, 3, 3, 3]\n\n        // The result pairs contain the timestamp and the maximum sample value\n        // for the country at that timestamp.\n        var countryFilters = new List<string> { \"country=(us,uk)\" };\n        IReadOnlyList<\n            (string, IReadOnlyList<TimeSeriesLabel>, IReadOnlyList<TimeSeriesTuple>)\n        > res44 = db.TS().MRange(\n            \"-\", \"+\",\n            countryFilters,\n            groupbyTuple: (\"country\", TsReduce.Max)\n        );\n        Console.WriteLine(res44.Count); // >>> 2\n\n        foreach (var (key, labelsResult, values) in res44)\n        {\n            Console.WriteLine($\"{key}:\");\n            Console.WriteLine($\"  Values: [{string.Join(\", \", values.Select(t => $\"({t.Time.Value}, {t.Val})\"))}]\");\n        }\n        // >>> country=uk\n        // >>>   Values: [(1, 18), (2, 21), (3, 24)]\n        // >>> country=us\n        // >>>   Values: [(1, 20), (2, 25), (3, 18)]\n\n        // The result pairs contain the timestamp and the average sample value\n        // for the country at that timestamp.\n        IReadOnlyList<\n            (string, IReadOnlyList<TimeSeriesLabel>, IReadOnlyList<TimeSeriesTuple>)\n        > res45 = db.TS().MRange(\n            \"-\", \"+\",\n            countryFilters,\n            groupbyTuple: (\"country\", TsReduce.Avg)\n        );\n        Console.WriteLine(res45.Count); // >>> 2\n\n        foreach (var (key, labelsResult, values) in res45)\n        {\n            Console.WriteLine($\"{key}:\");\n            Console.WriteLine($\"  Values: [{string.Join(\", \", values.Select(t => $\"({t.Time.Value}, {t.Val})\"))}]\");\n        }\n        // >>>   country=uk\n        // >>>      Values: [(1, 14), (2, 18), (3, 10)]\n        // >>>   country=us\n        // >>>      Values: [(1, 16), (2, 22), (3, 14)]","section_id":"aggregation-across-timeseries"},{"id":"aggregation-across-timeseries-ex2","language":"go","code":"res37, err := rdb.TSCreateWithArgs(ctx, \"wind:1\", &redis.TSOptions{\n\t\tLabels: map[string]string{\"country\": \"uk\"},\n\t}).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res37) // >>> OK\n\n\tres38, err := rdb.TSCreateWithArgs(ctx, \"wind:2\", &redis.TSOptions{\n\t\tLabels: map[string]string{\"country\": \"uk\"},\n\t}).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res38) // >>> OK\n\n\tres39, err := rdb.TSCreateWithArgs(ctx, \"wind:3\", &redis.TSOptions{\n\t\tLabels: map[string]string{\"country\": \"us\"},\n\t}).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res39) // >>> OK\n\n\tres40, err := rdb.TSCreateWithArgs(ctx, \"wind:4\", &redis.TSOptions{\n\t\tLabels: map[string]string{\"country\": \"us\"},\n\t}).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res40) // >>> OK\n\n\tres41, err := rdb.TSMAdd(ctx, [][]interface{}{\n\t\t{\"wind:1\", 1, 12},\n\t\t{\"wind:2\", 1, 18},\n\t\t{\"wind:3\", 1, 5},\n\t\t{\"wind:4\", 1, 20},\n\t}).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res41) // >>> [1 1 1 1]\n\n\tres42, err := rdb.TSMAdd(ctx, [][]interface{}{\n\t\t{\"wind:1\", 2, 14},\n\t\t{\"wind:2\", 2, 21},\n\t\t{\"wind:3\", 2, 4},\n\t\t{\"wind:4\", 2, 25},\n\t}).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res42) // >>> [2 2 2 2]\n\n\tres43, err := rdb.TSMAdd(ctx, [][]interface{}{\n\t\t{\"wind:1\", 3, 10},\n\t\t{\"wind:2\", 3, 24},\n\t\t{\"wind:3\", 3, 8},\n\t\t{\"wind:4\", 3, 18},\n\t}).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res43) // >>> [3 3 3 3]\n\n\t// The result pairs contain the timestamp and the maximum sample value\n\t// for the country at that timestamp.\n\tres44, err := rdb.TSMRangeWithArgs(\n\t\tctx,\n\t\t0,\n\t\tmath.MaxInt64,\n\t\t[]string{\"country=(us,uk)\"},\n\t\t&redis.TSMRangeOptions{\n\t\t\tGroupByLabel: \"country\",\n\t\t\tReducer:      \"max\",\n\t\t},\n\t).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tres44Keys := slices.Collect(maps.Keys(res44))\n\tsort.Strings(res44Keys)\n\n\tfor _, k := range res44Keys {\n\t\tlabels := res44[k][0].(map[interface{}]interface{})\n\t\tlabelKeys := make([]string, 0, len(labels))\n\n\t\tfor lk := range labels {\n\t\t\tlabelKeys = append(labelKeys, lk.(string))\n\t\t}\n\n\t\tsort.Strings(labelKeys)\n\n\t\tfmt.Printf(\"%v:\\n\", k)\n\n\t\tfor _, lk := range labelKeys {\n\t\t\tfmt.Printf(\"  %v: %v\\n\", lk, labels[lk])\n\t\t}\n\n\t\tfmt.Printf(\"  %v\\n\", res44[k][1])\n\t\tfmt.Printf(\"  %v\\n\", res44[k][2])\n\t\tfmt.Printf(\"  %v\\n\", res44[k][3])\n\t}\n\t// >>> country=uk:\n\t// >>>   map[reducers:[max]]\n\t// >>>   map[sources:[wind:1 wind:2]]\n\t// >>>   [[1 18] [2 21] [3 24]]\n\t// >>> country=us:\n\t// >>>   map[reducers:[max]]\n\t// >>>   map[sources:[wind:3 wind:4]]\n\t// >>>   [[1 20] [2 25] [3 18]]\n\n\t// The result pairs contain the timestamp and the average sample value\n\t// for the country at that timestamp.\n\tres45, err := rdb.TSMRangeWithArgs(\n\t\tctx,\n\t\t0,\n\t\tmath.MaxInt64,\n\t\t[]string{\"country=(us,uk)\"},\n\t\t&redis.TSMRangeOptions{\n\t\t\tGroupByLabel: \"country\",\n\t\t\tReducer:      \"avg\",\n\t\t},\n\t).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tres45Keys := slices.Collect(maps.Keys(res45))\n\tsort.Strings(res45Keys)\n\n\tfor _, k := range res45Keys {\n\t\tlabels := res45[k][0].(map[interface{}]interface{})\n\t\tlabelKeys := make([]string, 0, len(labels))\n\n\t\tfor lk := range labels {\n\t\t\tlabelKeys = append(labelKeys, lk.(string))\n\t\t}\n\n\t\tsort.Strings(labelKeys)\n\n\t\tfmt.Printf(\"%v:\\n\", k)\n\n\t\tfor _, lk := range labelKeys {\n\t\t\tfmt.Printf(\"  %v: %v\\n\", lk, labels[lk])\n\t\t}\n\n\t\tfmt.Printf(\"  %v\\n\", res45[k][1])\n\t\tfmt.Printf(\"  %v\\n\", res45[k][2])\n\t\tfmt.Printf(\"  %v\\n\", res45[k][3])\n\t}\n\t// >>> country=uk:\n\t// >>>   map[reducers:[avg]]\n\t// >>>   map[sources:[wind:1 wind:2]]\n\t// >>>   [[1 15] [2 17.5] [3 17]]\n\t// >>> country=us:\n\t// >>>   map[reducers:[avg]]\n\t// >>>   map[sources:[wind:3 wind:4]]\n\t// >>>   [[1 12.5] [2 14.5] [3 13]]","section_id":"aggregation-across-timeseries"},{"id":"aggregation-across-timeseries-ex3","language":"java","code":"Map<String, String> ukCountry = new HashMap<>();\n        ukCountry.put(\"country\", \"uk\");\n        \n        Map<String, String> usCountry = new HashMap<>();\n        usCountry.put(\"country\", \"us\");\n\n        jedis.tsCreate(\"wind:1\", TSCreateParams.createParams().labels(ukCountry));\n        jedis.tsCreate(\"wind:2\", TSCreateParams.createParams().labels(ukCountry));\n        jedis.tsCreate(\"wind:3\", TSCreateParams.createParams().labels(usCountry));\n        jedis.tsCreate(\"wind:4\", TSCreateParams.createParams().labels(usCountry));\n\n        jedis.tsMAdd(\n            new AbstractMap.SimpleEntry<>(\"wind:1\", new TSElement(0L, 10.0)),\n            new AbstractMap.SimpleEntry<>(\"wind:2\", new TSElement(0L, 12.0)),\n            new AbstractMap.SimpleEntry<>(\"wind:3\", new TSElement(0L, 8.0)),\n            new AbstractMap.SimpleEntry<>(\"wind:4\", new TSElement(0L, 15.0))\n        );\n\n        jedis.tsMAdd(\n            new AbstractMap.SimpleEntry<>(\"wind:1\", new TSElement(1L, 11.0)),\n            new AbstractMap.SimpleEntry<>(\"wind:2\", new TSElement(1L, 13.0)),\n            new AbstractMap.SimpleEntry<>(\"wind:3\", new TSElement(1L, 9.0)),\n            new AbstractMap.SimpleEntry<>(\"wind:4\", new TSElement(1L, 16.0))\n        );\n\n        jedis.tsMAdd(\n            new AbstractMap.SimpleEntry<>(\"wind:1\", new TSElement(2L, 9.0)),\n            new AbstractMap.SimpleEntry<>(\"wind:2\", new TSElement(2L, 11.0)),\n            new AbstractMap.SimpleEntry<>(\"wind:3\", new TSElement(2L, 7.0)),\n            new AbstractMap.SimpleEntry<>(\"wind:4\", new TSElement(2L, 14.0))\n        );\n\n        jedis.tsMAdd(\n            new AbstractMap.SimpleEntry<>(\"wind:1\", new TSElement(3L, 12.0)),\n            new AbstractMap.SimpleEntry<>(\"wind:2\", new TSElement(3L, 14.0)),\n            new AbstractMap.SimpleEntry<>(\"wind:3\", new TSElement(3L, 10.0)),\n            new AbstractMap.SimpleEntry<>(\"wind:4\", new TSElement(3L, 17.0))\n        );\n\n        jedis.tsMAdd(\n            new AbstractMap.SimpleEntry<>(\"wind:1\", new TSElement(4L, 8.0)),\n            new AbstractMap.SimpleEntry<>(\"wind:2\", new TSElement(4L, 10.0)),\n            new AbstractMap.SimpleEntry<>(\"wind:3\", new TSElement(4L, 6.0)),\n            new AbstractMap.SimpleEntry<>(\"wind:4\", new TSElement(4L, 13.0))\n        );\n\n        // Group by country with max reduction\n        Map<String, TSMRangeElements> res44 = jedis.tsMRange(\n            TSMRangeParams.multiRangeParams(0L, 4L)\n                .filter(\"country=(us,uk)\")\n                .groupBy(\"country\", \"max\"));\n        System.out.println(res44);\n        // >>> {country=uk=TSMRangeElements{key=country=uk, labels={}, value=[(0:12.0)...\n\n        // Group by country with avg reduction\n        Map<String, TSMRangeElements> res45 = jedis.tsMRange(\n            TSMRangeParams.multiRangeParams(0L, 4L)\n                .filter(\"country=(us,uk)\")\n                .groupBy(\"country\", \"avg\"));\n        System.out.println(res45);\n        // >>> {country=uk=TSMRangeElements{key=country=uk, labels={}, value=[(0:11.0)...","section_id":"aggregation-across-timeseries"},{"id":"aggregation-across-timeseries-ex4","language":"javascript","code":"const res37 = await client.ts.create('wind:1', {\n  LABELS: { country: 'uk' }\n});\nconsole.log(res37); // >>> OK\n\nconst res38 = await client.ts.create('wind:2', {\n  LABELS: { country: 'uk' }\n});\nconsole.log(res38); // >>> OK\n\nconst res39 = await client.ts.create('wind:3', {\n  LABELS: { country: 'us' }\n});\nconsole.log(res39); // >>> OK\n\nconst res40 = await client.ts.create('wind:4', {\n  LABELS: { country: 'us' }\n});\nconsole.log(res40); // >>> OK\n\nconst res41 = await client.ts.mAdd([\n  { key: 'wind:1', timestamp: 1, value: 12 },\n  { key: 'wind:2', timestamp: 1, value: 18 },\n  { key: 'wind:3', timestamp: 1, value: 5 },\n  { key: 'wind:4', timestamp: 1, value: 20 }\n]);\nconsole.log(res41); // >>> [1, 1, 1, 1]\n\nconst res42 = await client.ts.mAdd([\n  { key: 'wind:1', timestamp: 2, value: 14 },\n  { key: 'wind:2', timestamp: 2, value: 21 },\n  { key: 'wind:3', timestamp: 2, value: 4 },\n  { key: 'wind:4', timestamp: 2, value: 25 }\n]);\nconsole.log(res42); // >>> [2, 2, 2, 2]\n\nconst res43 = await client.ts.mAdd([\n  { key: 'wind:1', timestamp: 3, value: 10 },\n  { key: 'wind:2', timestamp: 3, value: 24 },\n  { key: 'wind:3', timestamp: 3, value: 8 },\n  { key: 'wind:4', timestamp: 3, value: 18 }\n]);\nconsole.log(res43); // >>> [3, 3, 3, 3]\n\n// The result pairs contain the timestamp and the maximum sample value\n// for the country at that timestamp.\nconst res44 = await client.ts.mRangeGroupBy(\n  '-', '+', ['country=(us,uk)'],\n  {label: 'country', REDUCE: TIME_SERIES_REDUCERS.MAX}\n);\nconsole.log(res44);\n// >>> { \"country=uk\": { samples: [\n//   { timestamp: 1, value: 18 },\n//   { timestamp: 2, value: 21 },\n//   { timestamp: 3, value: 24 }\n// ] }, \"country=us\": { samples: [\n//   { timestamp: 1, value: 20 },\n//   { timestamp: 2, value: 25 },\n//   { timestamp: 3, value: 18 }\n// ] } }\n\n// The result pairs contain the timestamp and the average sample value\n// for the country at that timestamp.\nconst res45 = await client.ts.mRangeGroupBy(\n  '-', '+', ['country=(us,uk)'],\n  { label: 'country', REDUCE: TIME_SERIES_REDUCERS.AVG}\n);\nconsole.log(res45);\n// >>> {\n// \"country=uk\": {\n//   samples: [{ timestamp: 1, value: 15 }, { timestamp: 2, value: 17.5 }, { timestamp: 3, value: 17 }]\n// },\n// \"country=us\": {\n//   samples: [{ timestamp: 1, value: 12.5 }, { timestamp: 2, value: 14.5 }, { timestamp: 3, value: 13 }]\n// }\n// }","section_id":"aggregation-across-timeseries"},{"id":"aggregation-across-timeseries-ex5","language":"python","code":"res37 = r.ts().create(\n    \"wind:1\",\n    labels={\"country\": \"uk\"}\n)\nprint(res37)  # >>> True\n\nres38 = r.ts().create(\n    \"wind:2\",\n    labels={\"country\": \"uk\"}\n)\nprint(res38)  # >>> True\n\nres39 = r.ts().create(\n    \"wind:3\",\n    labels={\"country\": \"us\"}\n)\nprint(res39)  # >>> True\n\nres40 = r.ts().create(\n    \"wind:4\",\n    labels={\"country\": \"us\"}\n)\nprint(res40)  # >>> True\n\nres41 = r.ts().madd([\n        (\"wind:1\", 1, 12),\n        (\"wind:2\", 1, 18),\n        (\"wind:3\", 1, 5),\n        (\"wind:4\", 1, 20),\n])\nprint(res41)  # >>> [1, 1, 1, 1]\n\nres42 = r.ts().madd([\n        (\"wind:1\", 2, 14),\n        (\"wind:2\", 2, 21),\n        (\"wind:3\", 2, 4),\n        (\"wind:4\", 2, 25),\n])\nprint(res42)  # >>> [2, 2, 2, 2]\n\nres43 = r.ts().madd([\n        (\"wind:1\", 3, 10),\n        (\"wind:2\", 3, 24),\n        (\"wind:3\", 3, 8),\n        (\"wind:4\", 3, 18),\n])\nprint(res43)  # >>> [3, 3, 3, 3]\n\n# The result pairs contain the timestamp and the maximum sample value\n# for the country at that timestamp.\nres44 = r.ts().mrange(\n    \"-\", \"+\",\n    filters=[\"country=(us,uk)\"],\n    groupby=\"country\",\n    reduce=\"max\"\n)\nprint(res44)\n# >>> [{'country=uk': [{}, [(1, 18.0), (2, 21.0), (3, 24.0)]]}, ...\n\n# The result pairs contain the timestamp and the average sample value\n# for the country at that timestamp.\nres45 = r.ts().mrange(\n    \"-\", \"+\",\n    filters=[\"country=(us,uk)\"],\n    groupby=\"country\",\n    reduce=\"avg\"\n)\nprint(res45)\n# >>> [{'country=uk': [{}, [(1, 15.0), (2, 17.5), (3, 17.0)]]}, ...","section_id":"aggregation-across-timeseries"},{"id":"nan-behavior-ex0","language":"bash","code":"# Create a temperature sensor time series\n> TS.CREATE sensor:temp LABELS type temperature location outdoor\n\n# Add normal readings and NaN for sensor malfunction\n> TS.MADD sensor:temp 1000 25.5 sensor:temp 1010 26.1 sensor:temp 1020 NaN sensor:temp 1030 24.8\n1) (integer) 1000\n2) (integer) 1010\n3) (integer) 1020\n4) (integer) 1030\n\n# Query all values - NaN is included in raw results\n> TS.RANGE sensor:temp - +\n1) 1) (integer) 1000\n   2) 25.5\n2) 1) (integer) 1010\n   2) 26.1\n3) 1) (integer) 1020\n   2) NaN\n4) 1) (integer) 1030\n   2) 24.8\n\n# Aggregate ignores NaN - average of 25.5, 26.1, 24.8 = 25.47\n> TS.RANGE sensor:temp - + AGGREGATION avg 1000\n1) 1) (integer) 1000\n   2) 25.466666666666665\n\n# Count non-NaN values\n> TS.RANGE sensor:temp - + AGGREGATION count 1000\n1) 1) (integer) 1000\n   2) 3\n\n# Count NaN values\n> TS.RANGE sensor:temp - + AGGREGATION countNaN 1000\n1) 1) (integer) 1000\n   2) 1\n\n# Count all values\n> TS.RANGE sensor:temp - + AGGREGATION countAll 1000\n1) 1) (integer) 1000\n   2) 4","section_id":"nan-behavior"},{"id":"compaction-ex0","language":"plaintext","code":"# The source time series.\n> TS.CREATE hyg:1\nOK\n# The destination time series for the compacted data.\n> TS.CREATE hyg:compacted\nOK\n# The compaction rule.\n> TS.CREATERULE hyg:1 hyg:compacted AGGREGATION min 3\nOK\n> TS.INFO hyg:1\n 1) totalSamples\n 2) (integer) 0\n 3) memoryUsage\n 4) (integer) 4592\n 5) firstTimestamp\n 6) (integer) 0\n 7) lastTimestamp\n 8) (integer) 0\n 9) retentionTime\n10) (integer) 0\n11) chunkCount\n12) (integer) 1\n13) chunkSize\n14) (integer) 4096\n15) chunkType\n16) compressed\n17) duplicatePolicy\n18) block\n19) labels\n20) (empty array)\n21) sourceKey\n22) (nil)\n23) rules\n24) 1) 1) \"hyg:compacted\"\n       2) (integer) 3\n       3) MIN\n       4) (integer) 0\n25) ignoreMaxTimeDiff\n26) (integer) 0\n27) ignoreMaxValDiff\n28) \"0\"","section_id":"compaction"},{"id":"compaction-ex1","language":"csharp","code":"bool res46 = db.TS().Create(\n            \"hyg:1\",\n            new TsCreateParamsBuilder().build()\n        );\n        Console.WriteLine(res46); // >>> True\n\n        bool res47 = db.TS().Create(\n            \"hyg:compacted\",\n            new TsCreateParamsBuilder().build()\n        );\n        Console.WriteLine(res47); // >>> True\n\n        var compactionRule = new TimeSeriesRule(\"hyg:compacted\", 3, TsAggregation.Min);\n        bool res48 = db.TS().CreateRule(\"hyg:1\", compactionRule);\n        Console.WriteLine(res48); // >>> True\n\n        TimeSeriesInformation res49 = db.TS().Info(\"hyg:1\");\n        Console.WriteLine(res49.Rules!.Count);\n        // >>> 1\n\n        TimeSeriesInformation res50 = db.TS().Info(\"hyg:compacted\");\n        Console.WriteLine(res50.SourceKey);\n        // >>> hyg:1","section_id":"compaction"},{"id":"compaction-ex2","language":"go","code":"res45, err := rdb.TSCreate(ctx, \"hyg:1\").Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res45) // >>> OK\n\n\tres46, err := rdb.TSCreate(ctx, \"hyg:compacted\").Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res46) // >>> OK\n\n\tres47, err := rdb.TSCreateRule(\n\t\tctx, \"hyg:1\", \"hyg:compacted\", redis.Min, 3,\n\t).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res47) // >>> OK\n\n\tres48, err := rdb.TSInfo(ctx, \"hyg:1\").Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res48[\"rules\"]) // >>> [[hyg:compacted 3 MIN 0]]\n\n\tres49, err := rdb.TSInfo(ctx, \"hyg:compacted\").Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res49[\"sourceKey\"]) // >>> hyg:1","section_id":"compaction"},{"id":"compaction-ex3","language":"java","code":"String res46 = jedis.tsCreate(\"hyg:1\");\n        System.out.println(res46); // >>> OK\n\n        String res47 = jedis.tsCreate(\"hyg:compacted\");\n        System.out.println(res47); // >>> OK\n\n        String res48 = jedis.tsCreateRule(\"hyg:1\", \"hyg:compacted\", AggregationType.MIN, 3);\n        System.out.println(res48); // >>> OK\n\n        TSInfo res49 = jedis.tsInfo(\"hyg:1\");\n        System.out.println(\"Rules: \" + res49.getProperty(\"rules\"));\n        // >>> Rules: [{compactionKey=hyg:compacted, bucketDuration=3, aggregationType=MIN, alignmentTimestamp=0}]\n\n        TSInfo res50 = jedis.tsInfo(\"hyg:compacted\");\n        System.out.println(\"Source key: \" + res50.getProperty(\"sourceKey\"));\n        // >>> Source key: hyg:1","section_id":"compaction"},{"id":"compaction-ex4","language":"javascript","code":"const res46 = await client.ts.create('hyg:1');\nconsole.log(res46); // >>> OK\n\nconst res47 = await client.ts.create('hyg:compacted');\nconsole.log(res47); // >>> OK\n\nconst res48 = await client.ts.createRule('hyg:1', 'hyg:compacted', TIME_SERIES_AGGREGATION_TYPE.MIN, 3);\nconsole.log(res48); // >>> OK\n\nconst res49 = await client.ts.info('hyg:1');\nconsole.log(res49.rules);\n// >>> [{ aggregationType: 'MIN', key: 'hyg:compacted', timeBucket: 3}]\n\nconst res50 = await client.ts.info('hyg:compacted');\nconsole.log(res50.sourceKey); // >>> 'hyg:1'","section_id":"compaction"},{"id":"compaction-ex5","language":"python","code":"res45 = r.ts().create(\"hyg:1\")\nprint(res45)  # >>> True\n\nres46 = r.ts().create(\"hyg:compacted\")\nprint(res46)  # >>> True\n\nres47 = r.ts().createrule(\"hyg:1\", \"hyg:compacted\", \"min\", 3)\nprint(res47)  # >>> True\n\nres48 = r.ts().info(\"hyg:1\")\nprint(res48.rules)\n# >>> [['hyg:compacted', 3, 'MIN', 0]]\n\nres49 = r.ts().info(\"hyg:compacted\")\nprint(res49.source_key)  # >>> 'hyg:1'","section_id":"compaction"},{"id":"compaction-ex6","language":"plaintext","code":"# Recreate the compaction rule so this example runs on its own.\n> DEL hyg:1 hyg:compacted\n(integer) 2\n> TS.CREATE hyg:1\nOK\n> TS.CREATE hyg:compacted\nOK\n> TS.CREATERULE hyg:1 hyg:compacted AGGREGATION min 3\nOK\n> TS.MADD hyg:1 0 75 hyg:1 1 77 hyg:1 2 78\n1) (integer) 0\n2) (integer) 1\n3) (integer) 2\n> ts.range hyg:compacted - +\n(empty array)\n> TS.ADD hyg:1 3 79\n(integer) 3\n> ts.range hyg:compacted - +\n1) 1) (integer) 0\n   2) 75","section_id":"compaction"},{"id":"compaction-ex7","language":"csharp","code":"// Recreate the compaction rule so this example runs on its own.\n        db.KeyDelete(new RedisKey[] { \"hyg:1\", \"hyg:compacted\" });\n        db.TS().Create(\"hyg:1\", new TsCreateParamsBuilder().build());\n        db.TS().Create(\"hyg:compacted\", new TsCreateParamsBuilder().build());\n        db.TS().CreateRule(\"hyg:1\", new TimeSeriesRule(\"hyg:compacted\", 3, TsAggregation.Min));\n\n        var hygSequence1 = new List<(string, TimeStamp, double)>\n        {\n            (\"hyg:1\", 0, 75),\n            (\"hyg:1\", 1, 77),\n            (\"hyg:1\", 2, 78)\n        };\n        IReadOnlyList<TimeStamp> res51 = db.TS().MAdd(hygSequence1);\n        Console.WriteLine($\"[{string.Join(\", \", res51.Select(t => t.Value))}]\");\n        // >>> [0, 1, 2]\n\n        IReadOnlyList<TimeSeriesTuple> res52 = db.TS().Range(\"hyg:compacted\", \"-\", \"+\");\n        Console.WriteLine(res52.Count); // >>> 0\n\n        TimeStamp res53 = db.TS().Add(\n            \"hyg:1\",\n            new TsAddParamsBuilder()\n                .AddTimestamp(3)\n                .AddValue(79)\n                .build()\n        );\n        Console.WriteLine(res53.Value); // >>> 3\n\n        IReadOnlyList<TimeSeriesTuple> res54 = db.TS().Range(\"hyg:compacted\", \"-\", \"+\");\n        Console.WriteLine(\n            $\"[{string.Join(\", \", res54.Select(t => $\"({t.Time.Value}, {t.Val})\"))}]\"\n        );\n        // >>> [(0, 75)]","section_id":"compaction"},{"id":"compaction-ex8","language":"go","code":"// Recreate the compaction rule so this example runs on its own.\n\trdb.Del(ctx, \"hyg:1\", \"hyg:compacted\")\n\trdb.TSCreate(ctx, \"hyg:1\")\n\trdb.TSCreate(ctx, \"hyg:compacted\")\n\trdb.TSCreateRule(ctx, \"hyg:1\", \"hyg:compacted\", redis.Min, 3)\n\n\tres50, err := rdb.TSMAdd(ctx, [][]interface{}{\n\t\t{\"hyg:1\", 0, 75},\n\t\t{\"hyg:1\", 1, 77},\n\t\t{\"hyg:1\", 2, 78},\n\t}).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res50) // >>> [0 1 2]\n\n\tres51, err := rdb.TSRange(\n\t\tctx, \"hyg:compacted\", 0, math.MaxInt64,\n\t).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res51) // >>> []\n\n\tres52, err := rdb.TSAdd(ctx, \"hyg:1\", 3, 79).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res52) // >>> 3\n\n\tres53, err := rdb.TSRange(\n\t\tctx, \"hyg:compacted\", 0, math.MaxInt64,\n\t).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res53) // >>> [{0 75}]","section_id":"compaction"},{"id":"compaction-ex9","language":"java","code":"// Recreate the compaction rule so this example runs on its own.\n        jedis.del(\"hyg:1\", \"hyg:compacted\");\n        jedis.tsCreate(\"hyg:1\");\n        jedis.tsCreate(\"hyg:compacted\");\n        jedis.tsCreateRule(\"hyg:1\", \"hyg:compacted\", AggregationType.MIN, 3);\n\n        List<Long> res51 = jedis.tsMAdd(\n            new AbstractMap.SimpleEntry<>(\"hyg:1\", new TSElement(0L, 75.0)),\n            new AbstractMap.SimpleEntry<>(\"hyg:1\", new TSElement(1L, 77.0)),\n            new AbstractMap.SimpleEntry<>(\"hyg:1\", new TSElement(2L, 78.0))\n        );\n        System.out.println(res51); // >>> [0, 1, 2]\n\n        List<TSElement> res52 = jedis.tsRange(\"hyg:compacted\", 0L, 10L);\n        System.out.println(res52); // >>> []\n\n        long res53 = jedis.tsAdd(\"hyg:1\", 3L, 79.0);\n        System.out.println(res53); // >>> 3\n\n        List<TSElement> res54 = jedis.tsRange(\"hyg:compacted\", 0L, 10L);\n        System.out.println(res54); // >>> [(0:75.0)]","section_id":"compaction"},{"id":"compaction-ex10","language":"javascript","code":"// Recreate the compaction rule so this example runs on its own.\nawait client.del(['hyg:1', 'hyg:compacted']);\nawait client.ts.create('hyg:1');\nawait client.ts.create('hyg:compacted');\nawait client.ts.createRule('hyg:1', 'hyg:compacted', TIME_SERIES_AGGREGATION_TYPE.MIN, 3);\n\nconst res51 = await client.ts.mAdd([\n  { key: 'hyg:1', timestamp: 0, value: 75 },\n  { key: 'hyg:1', timestamp: 1, value: 77 },\n  { key: 'hyg:1', timestamp: 2, value: 78 }\n]);\nconsole.log(res51); // >>> [0, 1, 2]\n\nconst res52 = await client.ts.range('hyg:compacted', '-', '+');\nconsole.log(res52); // >>> []\n\nconst res53 = await client.ts.add('hyg:1', 3, 79);\nconsole.log(res53); // >>> 3\n\nconst res54 = await client.ts.range('hyg:compacted', '-', '+');\nconsole.log(res54); // >>> [{ timestamp: 0, value: 75 }]","section_id":"compaction"},{"id":"compaction-ex11","language":"python","code":"# Recreate the compaction rule so this example runs on its own.\nr.delete(\"hyg:1\", \"hyg:compacted\")\nr.ts().create(\"hyg:1\")\nr.ts().create(\"hyg:compacted\")\nr.ts().createrule(\"hyg:1\", \"hyg:compacted\", \"min\", 3)\n\nres50 = r.ts().madd([\n    (\"hyg:1\", 0, 75),\n    (\"hyg:1\", 1, 77),\n    (\"hyg:1\", 2, 78),\n])\nprint(res50)  # >>> [0, 1, 2]\n\nres51 = r.ts().range(\"hyg:compacted\", \"-\", \"+\")\nprint(res51)  # >>> []\n\nres52 = r.ts().add(\"hyg:1\", 3, 79)\nprint(res52)  # >>> 3\n\nres53 = r.ts().range(\"hyg:compacted\", \"-\", \"+\")\nprint(res53)  # >>> [(0, 75.0)]","section_id":"compaction"},{"id":"delete-data-points-ex0","language":"plaintext","code":"# Recreate the thermometer:1 series so this example runs on its own.\n> DEL thermometer:1\n(integer) 1\n> TS.ADD thermometer:1 1 9.2\n(integer) 1\n> TS.ADD thermometer:1 2 9.9\n(integer) 2\n> TS.INFO thermometer:1\n 1) totalSamples\n 2) (integer) 2\n 3) memoryUsage\n 4) (integer) 4504\n 5) firstTimestamp\n 6) (integer) 1\n 7) lastTimestamp\n 8) (integer) 2\n 9) retentionTime\n10) (integer) 0\n11) chunkCount\n12) (integer) 1\n13) chunkSize\n14) (integer) 4096\n15) chunkType\n16) compressed\n17) duplicatePolicy\n18) block\n19) labels\n20) (empty array)\n21) sourceKey\n22) (nil)\n23) rules\n24) (empty array)\n25) ignoreMaxTimeDiff\n26) (integer) 0\n27) ignoreMaxValDiff\n28) \"0\"\n> TS.ADD thermometer:1 3 9.7\n(integer) 3\n> TS.INFO thermometer:1\n 1) totalSamples\n 2) (integer) 3\n 3) memoryUsage\n 4) (integer) 4504\n 5) firstTimestamp\n 6) (integer) 1\n 7) lastTimestamp\n 8) (integer) 3\n 9) retentionTime\n10) (integer) 0\n11) chunkCount\n12) (integer) 1\n13) chunkSize\n14) (integer) 4096\n15) chunkType\n16) compressed\n17) duplicatePolicy\n18) block\n19) labels\n20) (empty array)\n21) sourceKey\n22) (nil)\n23) rules\n24) (empty array)\n25) ignoreMaxTimeDiff\n26) (integer) 0\n27) ignoreMaxValDiff\n28) \"0\"\n> TS.DEL thermometer:1 1 2\n(integer) 2\n> TS.INFO thermometer:1\n 1) totalSamples\n 2) (integer) 1\n 3) memoryUsage\n 4) (integer) 4504\n 5) firstTimestamp\n 6) (integer) 3\n 7) lastTimestamp\n 8) (integer) 3\n 9) retentionTime\n10) (integer) 0\n11) chunkCount\n12) (integer) 1\n13) chunkSize\n14) (integer) 4096\n15) chunkType\n16) compressed\n17) duplicatePolicy\n18) block\n19) labels\n20) (empty array)\n21) sourceKey\n22) (nil)\n23) rules\n24) (empty array)\n25) ignoreMaxTimeDiff\n26) (integer) 0\n27) ignoreMaxValDiff\n28) \"0\"\n> TS.DEL thermometer:1 3 3\n(integer) 1\n> TS.INFO thermometer:1\n 1) totalSamples\n 2) (integer) 0\n 3) memoryUsage\n 4) (integer) 4504\n 5) firstTimestamp\n 6) (integer) 0\n 7) lastTimestamp\n 8) (integer) 0\n 9) retentionTime\n10) (integer) 0\n11) chunkCount\n12) (integer) 1\n13) chunkSize\n14) (integer) 4096\n15) chunkType\n16) compressed\n17) duplicatePolicy\n18) block\n19) labels\n20) (empty array)\n21) sourceKey\n22) (nil)\n23) rules\n24) (empty array)\n25) ignoreMaxTimeDiff\n26) (integer) 0\n27) ignoreMaxValDiff\n28) \"0\"","section_id":"delete-data-points"},{"id":"delete-data-points-ex1","language":"csharp","code":"// Recreate the thermometer:1 series so this example runs on its own.\n        db.KeyDelete(\"thermometer:1\");\n        db.TS().Add(\"thermometer:1\", 1, 9.2);\n        db.TS().Add(\"thermometer:1\", 2, 9.9);\n\n        TimeSeriesInformation res55 = db.TS().Info(\"thermometer:1\");\n        Console.WriteLine(res55.TotalSamples); // >>> 2\n        Console.WriteLine(res55.FirstTimeStamp!); // >>> 1\n        Console.WriteLine(res55.LastTimeStamp!); // >>> 2\n\n        TimeStamp res56 = db.TS().Add(\n            \"thermometer:1\",\n            new TsAddParamsBuilder()\n                .AddTimestamp(3)\n                .AddValue(9.7)\n                .build()\n        );\n        Console.WriteLine(res56.Value); // >>> 3\n\n        TimeSeriesInformation res57 = db.TS().Info(\"thermometer:1\");\n        Console.WriteLine(res57.TotalSamples); // >>> 3\n        Console.WriteLine(res57.FirstTimeStamp!); // >>> 1\n        Console.WriteLine(res57.LastTimeStamp!); // >>> 3\n\n        long res58 = db.TS().Del(\"thermometer:1\", 1, 2);\n        Console.WriteLine(res58); // >>> 2\n\n        TimeSeriesInformation res59 = db.TS().Info(\"thermometer:1\");\n        Console.WriteLine(res59.TotalSamples); // >>> 1\n        Console.WriteLine(res59.FirstTimeStamp!); // >>> 3\n        Console.WriteLine(res59.LastTimeStamp!); // >>> 3\n\n        long res60 = db.TS().Del(\"thermometer:1\", 3, 3);\n        Console.WriteLine(res60); // >>> 1\n\n        TimeSeriesInformation res61 = db.TS().Info(\"thermometer:1\");\n        Console.WriteLine(res61.TotalSamples); // >>> 0","section_id":"delete-data-points"},{"id":"delete-data-points-ex2","language":"go","code":"// Recreate the thermometer:1 series so this example runs on its own.\n\trdb.Del(ctx, \"thermometer:1\")\n\trdb.TSAdd(ctx, \"thermometer:1\", 1, 9.2)\n\trdb.TSAdd(ctx, \"thermometer:1\", 2, 9.9)\n\n\tres54, err := rdb.TSInfo(ctx, \"thermometer:1\").Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res54[\"totalSamples\"])   // >>> 2\n\tfmt.Println(res54[\"firstTimestamp\"]) // >>> 1\n\tfmt.Println(res54[\"lastTimestamp\"])  // >>> 2\n\n\tres55, err := rdb.TSAdd(ctx, \"thermometer:1\", 3, 9.7).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res55) // >>> 3\n\n\tres56, err := rdb.TSInfo(ctx, \"thermometer:1\").Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res56[\"totalSamples\"])   // >>> 3\n\tfmt.Println(res56[\"firstTimestamp\"]) // >>> 1\n\tfmt.Println(res56[\"lastTimestamp\"])  // >>> 3\n\n\tres57, err := rdb.TSDel(ctx, \"thermometer:1\", 1, 2).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res57) // >>> 2\n\n\tres58, err := rdb.TSInfo(ctx, \"thermometer:1\").Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res58[\"totalSamples\"])   // >>> 1\n\tfmt.Println(res58[\"firstTimestamp\"]) // >>> 3\n\tfmt.Println(res58[\"lastTimestamp\"])  // >>> 3\n\n\tres59, err := rdb.TSDel(ctx, \"thermometer:1\", 3, 3).Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res59) // >>> 1\n\n\tres60, err := rdb.TSInfo(ctx, \"thermometer:1\").Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res60[\"totalSamples\"]) // >>> 0","section_id":"delete-data-points"},{"id":"delete-data-points-ex3","language":"java","code":"// Recreate the thermometer:1 series so this example runs on its own.\n        jedis.del(\"thermometer:1\");\n        jedis.tsAdd(\"thermometer:1\", 1L, 9.2);\n        jedis.tsAdd(\"thermometer:1\", 2L, 9.9);\n\n        TSInfo res55 = jedis.tsInfo(\"thermometer:1\");\n        System.out.println(res55.getProperty(\"totalSamples\")); // >>> 2\n        System.out.println(res55.getProperty(\"firstTimestamp\")); // >>> 1\n        System.out.println(res55.getProperty(\"lastTimestamp\")); // >>> 2\n\n        long res56 = jedis.tsAdd(\"thermometer:1\", 3L, 9.7);\n        System.out.println(res56); // >>> 3\n\n        TSInfo res57 = jedis.tsInfo(\"thermometer:1\");\n        System.out.println(res57.getProperty(\"totalSamples\")); // >>> 3\n\n        long res58 = jedis.tsDel(\"thermometer:1\", 1L, 2L);\n        System.out.println(res58); // >>> 2\n\n        TSInfo res59 = jedis.tsInfo(\"thermometer:1\");\n        System.out.println(res59.getProperty(\"totalSamples\")); // >>> 1\n\n        long res60 = jedis.tsDel(\"thermometer:1\", 3L, 3L);\n        System.out.println(res60); // >>> 1\n\n        TSInfo res61 = jedis.tsInfo(\"thermometer:1\");\n        System.out.println(res61.getProperty(\"totalSamples\")); // >>> 0","section_id":"delete-data-points"},{"id":"delete-data-points-ex4","language":"javascript","code":"// Recreate the thermometer:1 series so this example runs on its own.\nawait client.del('thermometer:1');\nawait client.ts.add('thermometer:1', 1, 9.2);\nawait client.ts.add('thermometer:1', 2, 9.9);\n\nconst res55 = await client.ts.info('thermometer:1');\nconsole.log(res55.totalSamples); // >>> 2\nconsole.log(res55.firstTimestamp); // >>> 1\nconsole.log(res55.lastTimestamp); // >>> 2\n\nconst res56 = await client.ts.add('thermometer:1', 3, 9.7);\nconsole.log(res56); // >>> 3\n\nconst res57 = await client.ts.info('thermometer:1');\nconsole.log(res57.totalSamples); // >>> 3\nconsole.log(res57.firstTimestamp); // >>> 1\nconsole.log(res57.lastTimestamp); // >>> 3\n\nconst res58 = await client.ts.del('thermometer:1', 1, 2);\nconsole.log(res58); // >>> 2\n\nconst res59 = await client.ts.info('thermometer:1');\nconsole.log(res59.totalSamples); // >>> 1\nconsole.log(res59.firstTimestamp); // >>> 3\nconsole.log(res59.lastTimestamp); // >>> 3\n\nconst res60 = await client.ts.del('thermometer:1', 3, 3);\nconsole.log(res60); // >>> 1\n\nconst res61 = await client.ts.info('thermometer:1');\nconsole.log(res61.totalSamples); // >>> 0","section_id":"delete-data-points"},{"id":"delete-data-points-ex5","language":"python","code":"# Recreate the thermometer:1 series so this example runs on its own.\nr.delete(\"thermometer:1\")\nr.ts().add(\"thermometer:1\", 1, 9.2)\nr.ts().add(\"thermometer:1\", 2, 9.9)\n\nres54 = r.ts().info(\"thermometer:1\")\nprint(res54.total_samples)  # >>> 2\nprint(res54.first_timestamp)  # >>> 1\nprint(res54.last_timestamp)  # >>> 2\n\nres55 = r.ts().add(\"thermometer:1\", 3, 9.7)\nprint(res55)  # >>> 3\n\nres56 = r.ts().info(\"thermometer:1\")\nprint(res56.total_samples)  # >>> 3\nprint(res56.first_timestamp)  # >>> 1\nprint(res56.last_timestamp)  # >>> 3\n\nres57 = r.ts().delete(\"thermometer:1\", 1, 2)\nprint(res57)  # >>> 2\n\nres58 = r.ts().info(\"thermometer:1\")\nprint(res58.total_samples)  # >>> 1\nprint(res58.first_timestamp)  # >>> 3\nprint(res58.last_timestamp)  # >>> 3\n\nres59 = r.ts().delete(\"thermometer:1\", 3, 3)\nprint(res59)  # >>> 1\n\nres60 = r.ts().info(\"thermometer:1\")\nprint(res60.total_samples)  # >>> 0","section_id":"delete-data-points"}]}
{"schema_version":2,"id":"develop/data-types/timeseries/out-of-order_performance_considerations","title":"Out-of-order / backfilled ingestion performance considerations","url":"https://redis.io/docs/latest/develop/data-types/timeseries/out-of-order_performance_considerations/","summary":"Out-of-order / backfilled ingestion performance considerations","aliases":["/develop/data-types/timeseries/reference/out-of-order_performance_considerations","/develop/data-types/timeseries/reference/out-of-order_performance_considerations/","/develop/data-types/timeseries/reference/","/develop/data-types/timeseries/reference"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-29T16:29:37+01:00","page_type":"content","content_hash":"81d376effaa39f8a56c07e2be18678d7ce3542779d9098e1011f2b50d9ff0545","sections":[{"id":"overview","title":"Overview","role":"overview","text":"When an older timestamp is inserted into a time series, the chunk of memory corresponding to the new sample’s time frame will potentially have to be retrieved from the main memory (you can read more about these chunks [here](https://redislabs.com/blog/redistimeseries-ga-making-4th-dimension-truly-immersive/)). When this chunk is a compressed chunk, it will also have to be decoded before we can insert/update to it. These are memory-intensive—and in the case of decoding, compute-intensive—operations that will influence the overall achievable ingestion rate. \n\n\nIngest performance is critical for us, which pushed  us to assess and be transparent about the impact of the out-of-order backfilled ratio on our overall high-performance TSDB.\n\n\nTo do so, we created a Go benchmark client that enabled us to control key factors that dictate overall system performance, like the out-of-order ratio, the compression of the series, the number of concurrent clients used, and command pipelining. For the full benchmark-driver configuration details and parameters, please refer to this [GitHub link](https://github.com/RedisTimeSeries/redistimeseries-ooo-benchmark).\n\n\nFurthermore, all benchmark variations were run on Amazon Web Services instances, provisioned through our benchmark-testing infrastructure. Both the benchmarking client and database servers were running on separate c5.9xlarge instances. The tests were executed on a single-shard setup, with RedisTimeSeries version 1.4.\n\n\nBelow you can see the correlation between achievable ops/sec and out-of-order ratio for both compressed and uncompressed chunks."},{"id":"compressed-chunks-out-of-orderbackfilled-impact-analysis","title":"Compressed chunks out-of-order/backfilled impact analysis","role":"content","text":"With compressed chunks, given that a single out-of-order datapoint implies the full decompression from double delta of the entire chunk, you should expect higher overheads in out-of-order writes. \n\nAs a rule of thumb, to increase out-of-order compressed performance, reduce the chunk size as much as possible. Smaller chunks imply less computation on double-delta decompression and thus less overall impact, with the drawback of smaller compression ratio.\n\nThe graphs and tables below make these key points:\n\n- If the database receives 1% of out-of-order samples with our current default chunk size in bytes (4096) the overall impact on the ingestion rate should be 10%.\n\n- At larger out-of-order percentages, like 5%, 10%, or even 25%, the overall impact should be between 35% to 75% fewer ops/sec. At this level of out-of-order percentages, you should really consider reducing the chunk size.\n\n- We've observed a maximum 95% drop in the achievable ops/sec even at 99% out-of-order ingestion. (Again, reducing the chunk size can cut the impact in half.)\n\n![compressed-overall-ops-sec-vs-out-of-order-percentage](https://redis.io/docs/latest/images/timeseries/compressed-overall-ops-sec-vs-out-of-order-percentage.webp)\n\n![compressed-overall-p50-lat-vs-out-of-order-percentage](https://redis.io/docs/latest/images/timeseries/compressed-overall-p50-lat-vs-out-of-order-percentage.webp)\n\n![compressed-out-of-order-overhead-table](https://redis.io/docs/latest/images/timeseries/compressed-out-of-order-overhead-table.webp)"},{"id":"uncompressed-chunks-out-of-orderbackfilled-impact-analysis","title":"Uncompressed chunks out-of-order/backfilled impact analysis","role":"content","text":"As visible on the charts and tables below, the chunk size does not affect the overall out-of-order impact on ingestion (meaning that if I have a chunk size of 256 bytes and a chunk size of 4096 bytes, the expected impact that out-of-order ingestion is the same—as it should be). \nApart from that, we can observe the following key take-aways:\n\n- If the database receives 1% of out-of-order samples, the overall impact in ingestion rate should be low or even unmeasurable.\n\n- At higher out-of-order percentages, like  5%, 10%, or even 25%, the overall impact should be 5% to 19% fewer ops/sec.\n\n- We've observed a maximum 45% drop in the achievable ops/sec, even at 99% out-of-order ingestion.\n\n![uncompressed-overall-ops-sec-vs-out-of-order-percentage](https://redis.io/docs/latest/images/timeseries/uncompressed-overall-ops-sec-vs-out-of-order-percentage.webp)\n\n![uncompressed-overall-p50-lat-vs-out-of-order-percentage](https://redis.io/docs/latest/images/timeseries/uncompressed-overall-p50-lat-vs-out-of-order-percentage.webp)\n\n![uncompressed-out-of-order-overhead-table](https://redis.io/docs/latest/images/timeseries/uncompressed-out-of-order-overhead-table.webp)"}],"examples":[]}
{"schema_version":2,"id":"develop/data-types/timeseries/use_cases","title":"Use cases","url":"https://redis.io/docs/latest/develop/data-types/timeseries/use_cases/","summary":"Time series use cases","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2024-01-19T18:04:55+01:00","page_type":"content","content_hash":"5bb048da169099cce9a89cad7895804705d0f6493f53c12045a43e8ccb022a17","sections":[{"id":"content","title":"Content","role":"content","text":"**Monitoring (data center)**\n\nModern data centers have a lot of moving pieces, such as infrastructure (servers and networks) and software systems (applications and services) that need to be monitored around the clock.\n\nRedis Time Series allows you to plan for new resources upfront, optimize the utilization of existing resources, reconstruct the circumstances that led to outages, and identify application performance issues by analyzing and reporting on the following metrics:\n\n- Maximum CPU utilization per server\n- Maximum network latency between two services\n- Average IO bandwidth utilization of a storage system\n- 99th percentile of the response time of a specific application outages\n\n**Weather analysis (environment)**\n\nRedis Time Series can be used to track environmental measurements such as the number of daily sunshine hours and hourly rainfall depth, over a period of many years. Seasonally, you can measure average rainfall depth, average daily temperature, and the maximum number of sunny hours per day, for example. Watch the increase of the maximum daily temperature over the years. Predict the expected temperature and rainfall depth in a specific location for a particular week of the year.\n\nMultiple time series can be collected, each for a different location. By utilizing secondary indexes, measurements can be aggregated over given geographical regions (e.g., minimal and maximal daily temperature in Europe) or over locations with specific attributes (e.g., average rainfall depth in mountainous regions).\n\nExample metrics include: \n\n- Rain (cm)\n- Temperature (C)\n- Sunny periods (h)\n\n**Analysis of the atmosphere (environment)**\n\nThe atmospheric concentration of CO2 is more important than ever before. Use TimeSeries to track average, maximum and minimum CO2 level per season and average yearly CO2 over the last decades. Example metrics include:\n\n- Concentration of CO2 (ppm)\n- Location\n\n**Flight data recording (sensor data and IoT)**\n\nPlanes have a multitude of sensors. This sensor data is stored in a black box and also shared with external systems. TimeSeries can help you reconstruct the sequence of events over time, optimize operations and maintenance intervals, improve safety, and provide feedback to the equipment manufacturers about the part quality. Example metrics include:\n\n- Altitude\n- Flight path\n- Engine temperature\n- Level of vibrations\n- Pressure\n\n**Ship logbooks (sensor data and IoT)**\n\nIt's very common to keep track of ship voyages via (digital) logbooks. Use TimeSeries to calculate optimal routes using these metrics:\n\n- Wind (km/h)\n- Ocean conditions (classes)\n- Speed (knots)\n- Location (long, lat)\n\n**Connected car (sensor data and IoT)**\n\nModern cars are exposing several metrics via a standard interface. Use TimeSeries to correlate average fuel consumption with the tire pressure, figure out how long to keep a car in the fleet, determine optimal maintenance intervals, and calculate tax savings by type of the road (taxable vs. nontaxable roads). Example metrics include:\n\n- Acceleration\n- Location (long, lat)\n- Fuel level (liter)\n- Distances (km)\n- Speed (km/h)\n- Tire pressure\n- Distance until next maintenance check\n\n**Smart metering (sensor data and IoT)**\n\nModern houses and facilities gather details about energy consumption/production. Use Redis Time Series to aggregate billing based on monthly consumption. Optimize the network by redirecting the energy delivery relative to the fluctuations in need. Provide recommendations on how to improve the energy consumption behavior. Example metrics include:\n\n- Consumption per location\n- Produced amount of electrical energy per location\n\n**Quality of service (telecom)**\n\nMobile phone usage is increasing, producing a natural growth that just correlates to the increasing number of cellphones. However, there might also be spikes that correlate with specific events (for example, more messages around world championships). \n\nTelecom providers need to ensure that they are providing the necessary infrastructure to deliver the right quality of service. This includes using mini towers for short-term peaks. Use TimeSeries to correlate traffic peaks to specific events, load balance traffic over several towers or mini towers, and predictively plan the infrastructure. Metrics include the amount of traffic per tower.\n\n**Stock trading (finance)**\n\nStock trading is highly automated today. Algorithms, and not just human beings, are trading, from the amount of bids and asks for the trading of a stock to the extreme volumes of trades per second (millions of ops per second). Computer-driven trading requires millisecond response times. It's necessary to keep a lot of data points within a very short period of time (for example, price fluctuations per second within a minute). In addition, the long-term history needs to be kept to make statements about trends or for regulatory purposes.  \n\nUse Redis Time Series to identify correlations between the trading behavior and other events (for example, social network posts). Discover a developing market. Detect anomalies to discover insider trades. Example metrics include:\n\n- Exact time and order of a trade by itself\n- Type of the event (trade/bid)\n- The stock price"}],"examples":[]}
{"schema_version":2,"id":"develop/data-types/vector-sets/filtered-search","title":"Filter expressions","url":"https://redis.io/docs/latest/develop/data-types/vector-sets/filtered-search/","summary":"Use filter expressions to refine vector similarity results with Redis vector sets","tags":["docs","develop","stack","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-10T15:45:48+01:00","page_type":"content","content_hash":"d525bb6409485f363a25872a3d9e8d5254a85002a53dc6f1dd4a4abc0002e9b8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Filtered search lets you combine vector similarity search with scalar filtering. You can associate JSON attributes with elements in a vector set, and then filter results using those attributes during [`VSIM`](https://redis.io/docs/latest/commands/vsim) queries.\n\nThis allows queries such as:\n\n[code example]"},{"id":"assigning-attributes","title":"Assigning attributes","role":"content","text":"You can associate attributes when adding a new vector using the `SETATTR` argument:\n\n[code example]\n\nOr update them later with the [`VSETATTR`](https://redis.io/docs/latest/commands/vsetattr) command:\n\n[code example]\n\nYou can retrieve attributes with the [`VGETATTR`](https://redis.io/docs/latest/commands/vgetattr) command:\n\n[code example]"},{"id":"filtering-during-similarity-search","title":"Filtering during similarity search","role":"content","text":"To filter by attributes, pass the `FILTER` option to the [`VSIM`](https://redis.io/docs/latest/commands/vsim) command:\n\n[code example]\n\nThis returns only elements that match both the vector similarity and the filter expression."},{"id":"expression-syntax","title":"Expression syntax","role":"content","text":"Expressions support familiar JavaScript-like syntax:\n\n- Arithmetic: `+`, `-`, `*`, `/`, `%`, `**`\n- Comparison: `==`, `!=`, `>`, `<`, `>=`, `<=`\n- Logical: `and`, `or`, `not` (or `&&`, `||`, `!`)\n- Containment: `in`\n- Grouping: Parentheses `()`\n\nUse dot notation to access attribute fields, for example, `.year`, `.rating`.\n\n> Only top-level fields are supported (for example, `.genre`, but not `.movie.genre`)."},{"id":"supported-data-types","title":"Supported data types","role":"compatibility","text":"- Numbers\n- Strings\n- Booleans (converted to 1 or 0)\n- Arrays (for `in`)\n\nIf a field is missing or invalid, the element is skipped without error."},{"id":"filter-ef","title":"FILTER-EF","role":"content","text":"The `FILTER-EF` option controls how many candidate nodes the engine inspects to find enough filtered results. The defaults is `COUNT * 100`.\n\n[code example]\n\n- Use a higher value for rare filters.\n- Use `FILTER-EF 0` to scan as many as needed to fulfill the request.\n- The engine will stop early if enough high-quality results are found."},{"id":"examples","title":"Examples","role":"example","text":"[code example]"},{"id":"tips","title":"Tips","role":"content","text":"- Missing attributes are treated as non-matching.\n- Use `FILTER-EF` to tune recall vs performance.\n- Combine multiple attributes for fine-grained filtering."},{"id":"see-also","title":"See also","role":"related","text":"- [VSIM](https://redis.io/docs/latest/commands/vsim)\n- [VADD](https://redis.io/docs/latest/commands/vadd)\n- [VSETATTR](https://redis.io/docs/latest/commands/vsetattr)\n- [VGETATTR](https://redis.io/docs/latest/commands/vgetattr)"}],"examples":[{"id":"overview-ex0","language":"bash","code":"VSIM movies VALUES 3 0.5 0.8 0.2 FILTER '.year >= 1980 and .rating > 7'","section_id":"overview"},{"id":"assigning-attributes-ex0","language":"bash","code":"VADD vset VALUES 3 1 1 1 a SETATTR '{\"year\": 1950}'","section_id":"assigning-attributes"},{"id":"assigning-attributes-ex1","language":"bash","code":"VSETATTR vset a '{\"year\": 1960}'","section_id":"assigning-attributes"},{"id":"assigning-attributes-ex2","language":"bash","code":"VGETATTR vset a","section_id":"assigning-attributes"},{"id":"filtering-during-similarity-search-ex0","language":"bash","code":"VSIM vset VALUES 3 0 0 0 FILTER '.year > 1950'","section_id":"filtering-during-similarity-search"},{"id":"filter-ef-ex0","language":"bash","code":"VSIM vset VALUES 3 0 0 0 COUNT 10 FILTER '.year > 2000' FILTER-EF 500","section_id":"filter-ef"},{"id":"examples-ex0","language":"bash","code":"# Filter by year range\nVSIM movies VALUES 3 0.5 0.8 0.2 FILTER '.year >= 1980 and .year < 1990'\n\n# Filter by genre and rating\nVSIM movies VALUES 3 0.5 0.8 0.2 FILTER '.genre == \"action\" and .rating > 8.0'\n\n# Use IN with array\nVSIM movies VALUES 3 0.5 0.8 0.2 FILTER '.director in [\"Spielberg\", \"Nolan\"]'\n\n# Math and logic\nVSIM movies VALUES 3 0.5 0.8 0.2 FILTER '(.year - 2000) ** 2 < 100 and .rating / 2 > 4'","section_id":"examples"}]}
{"schema_version":2,"id":"develop/data-types/vector-sets","title":"Redis vector sets","url":"https://redis.io/docs/latest/develop/data-types/vector-sets/","summary":"Introduction to Redis vector sets","aliases":["/data-types/vector-sets/","/manual/data-types/vector-sets/","/develop/data-types/vector-set/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-29T16:29:37+01:00","children":[{"id":"develop/data-types/vector-sets/filtered-search","summary":"Use filter expressions to refine vector similarity results with Redis vector sets","title":"Filter expressions","url":"https://redis.io/docs/latest/develop/data-types/vector-sets/filtered-search/"},{"id":"develop/data-types/vector-sets/performance","summary":"Learn how Redis vector sets behave under load and how to optimize for speed and recall","title":"Performance","url":"https://redis.io/docs/latest/develop/data-types/vector-sets/performance/"},{"id":"develop/data-types/vector-sets/scalability","summary":"Scale Redis vector sets to handle larger data sets and workloads","title":"Scalability","url":"https://redis.io/docs/latest/develop/data-types/vector-sets/scalability/"},{"id":"develop/data-types/vector-sets/memory","summary":"Learn how to optimize memory consumption in Redis vector sets","title":"Memory optimization","url":"https://redis.io/docs/latest/develop/data-types/vector-sets/memory/"},{"id":"develop/data-types/vector-sets/troubleshooting","summary":"Diagnose and debug issues when working with Redis vector sets","title":"Troubleshooting","url":"https://redis.io/docs/latest/develop/data-types/vector-sets/troubleshooting/"}],"page_type":"content","content_hash":"25761a4ea23b1eea1af844a32d95bc5fad5813d3fa6c3bc518b7e14bab34e2cc","sections":[{"id":"vector-set-command-summary","title":"Vector set command summary","role":"content","text":"**13 commands in this group:**\n\n[View all vector_set commands](https://redis.io/commands/?group=vector_set)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [VADD](https://redis.io/commands/vadd/) | Add one or more elements to a vector set, or update its vector if it already exists | N/A | 8.0.0 |\n| [VCARD](https://redis.io/commands/vcard/) | Return the number of elements in a vector set | N/A | 8.0.0 |\n| [VDIM](https://redis.io/commands/vdim/) | Return the dimension of vectors in the vector set | N/A | 8.0.0 |\n| [VEMB](https://redis.io/commands/vemb/) | Return the vector associated with an element | N/A | 8.0.0 |\n| [VGETATTR](https://redis.io/commands/vgetattr/) | Retrieve the JSON attributes of elements | N/A | 8.0.0 |\n| [VINFO](https://redis.io/commands/vinfo/) | Return information about a vector set | N/A | 8.0.0 |\n| [VISMEMBER](https://redis.io/commands/vismember/) | Check if an element exists in a vector set | N/A | 8.2.0 |\n| [VLINKS](https://redis.io/commands/vlinks/) | Return the neighbors of an element at each layer in the HNSW graph | N/A | 8.0.0 |\n| [VRANDMEMBER](https://redis.io/commands/vrandmember/) | Return one or multiple random members from a vector set | N/A | 8.0.0 |\n| [VRANGE](https://redis.io/commands/vrange/) | Return vector set elements in a lex range | N/A | 8.4.0 |\n\n#### Vector set command summary (part 2)\n\n| Command | Summary | Complexity | Since |\n|---------|---------|------------|-------|\n| [VREM](https://redis.io/commands/vrem/) | Remove an element from a vector set | N/A | 8.0.0 |\n| [VSETATTR](https://redis.io/commands/vsetattr/) | Associate or remove the JSON attributes of elements | N/A | 8.0.0 |\n| [VSIM](https://redis.io/commands/vsim/) | Return elements by vector similarity | N/A | 8.0.0 |\n\n\n\nVector sets are a data type similar to sorted sets, but instead of a score, vector set elements have a string representation of a vector.\nVector sets allow you to add items to a set, and then either:\n\n* retrieve a subset of items that are the most similar to a specified vector, or\n* retrieve a subset of items that are the most similar to the vector of an element that is already part of the vector set.\n\nVector sets also provide for optional [filtered search](https://redis.io/docs/latest/develop/data-types/vector-sets/filtered-search). You can associate attributes with all or some elements in a vector set, and then use the `FILTER` option of the [`VSIM`](https://redis.io/docs/latest/commands/vsim) command to retrieve items similar to a given vector while applying simple mathematical filters to those attributes. Here's a sample filter: `\".year > 1950\"`."},{"id":"endianness-considerations-for-fp32-format","title":"Endianness considerations for FP32 format","role":"content","text":"When using the FP32 blob format with vector set commands like [`VADD`](https://redis.io/docs/latest/commands/vadd) and [`VSIM`](https://redis.io/docs/latest/commands/vsim), the binary data must be encoded in little-endian byte order. This is important for cross-platform compatibility, as some ARM variants and other architectures may use different endianness.\n\nIf your platform uses big-endian or mixed-endian encoding, you have two options:\n- Manually convert the byte order to little-endian before passing the blob to Redis.\n- Use the `VALUES` syntax instead, which accepts floating-point numbers as strings and is platform-independent."},{"id":"examples","title":"Examples","role":"example","text":"The following examples give an overview of how to use vector sets. For clarity,\nwe will use a set of two-dimensional vectors that represent points in the\nCartesian coordinate plane. However, in real use cases, the vectors will typically\nrepresent *text embeddings* and have hundreds of dimensions. See\n[Redis for AI](https://redis.io/docs/latest/develop/ai) for more information about using text\nembeddings.\n\nThe points we will use are A: (1.0, 1.0), B: (-1.0, -1.0), C: (-1.0, 1.0), D: (1.0. -1.0), and\nE: (1.0, 0), shown in the diagram below.\n\n![Example points on the coordinate plane.](https://redis.io/docs/latest/images/vecsets/VecSetExamplePoints.drawio.svg)"},{"id":"basic-operations","title":"Basic operations","role":"content","text":"Start by adding the point vectors to a set called `points` using\n[`VADD`](https://redis.io/docs/latest/commands/vadd). This also creates the vector set object.\nThe [`TYPE`](https://redis.io/docs/latest/commands/type) command returns a type of `vectorset`\nfor this object.\n\nFoundational: Use VADD to create a new vector set and populate it with vectors\n\n**Commands:** VADD, TYPE\n\n**Complexity:**\n- TYPE: O(1)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\n\nGet the number of elements in the set (also known as the *cardinality* of the set)\nusing [`VCARD`](https://redis.io/docs/latest/commands/vcard) and the number of dimensions of\nthe vectors using [`VDIM`](https://redis.io/docs/latest/commands/vdim):\n\nMetadata retrieval: Use VCARD to get the number of elements and VDIM to get vector dimensions when you need to inspect vector set properties\n\n**Builds upon:** vadd\n\n**Commands:** VCARD, VDIM\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nGet the coordinate values from the elements using [`VEMB`](https://redis.io/docs/latest/commands/vemb).\nNote that the values will not typically be the exact values you supplied when you added\nthe vector because\n[quantization](https://redis.io/docs/latest/develop/data-types/vector-sets/performance#quantization-effects)\nis applied to improve performance.\n\nVector retrieval: Use VEMB to retrieve the approximate vector values of elements when you need to inspect the actual vector data stored in the set\n\n**Builds upon:** vadd\n\n**Commands:** VEMB\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nRemove an unwanted element with [`VREM`](https://redis.io/docs/latest/commands/vrem)\n\nElement removal: Use VREM to delete elements from a vector set when you need to remove vectors from the collection\n\n**Builds upon:** vadd\n\n**Commands:** VADD, VCARD, VREM\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nSet and retrieve an element's JSON attribute data using\n[`VSETATTR`](https://redis.io/docs/latest/commands/vsetattr)\nand [`VGETATTR`](https://redis.io/docs/latest/commands/vgetattr). You can also pass an empty string\nto `VSETATTR` to delete the attribute data:\n\nAttribute management: Use VSETATTR to store JSON attributes on elements and VGETATTR to retrieve them when you need to associate metadata with vectors\n\n**Builds upon:** vadd\n\n**Commands:** VSETATTR, VGETATTR\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"vector-similarity-search","title":"Vector similarity search","role":"content","text":"Use [`VSIM`](https://redis.io/docs/latest/commands/vsim) to rank the points in order of their vector distance from a sample point:\n\nSimilarity search: Use VSIM to find elements most similar to a query vector when you need to perform vector similarity searches\n\n**Difficulty:** Intermediate\n\n**Builds upon:** vadd\n\n**Commands:** VSIM\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nFind the four elements that are closest to point A and show their distance \"scores\":\n\nSimilarity with options: Use VSIM with ELE, WITHSCORES, and COUNT options to find similar elements with scores and limits when you need detailed similarity results\n\n**Difficulty:** Intermediate\n\n**Builds upon:** vadd\n\n**Commands:** VSIM\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nAdd some JSON attributes and use\n[filter expressions](https://redis.io/docs/latest/develop/data-types/vector-sets/filtered-search)\nto include them in the search:\n\nFiltered similarity search: Use VSIM with FILTER option to apply attribute-based conditions to similarity results when you need to combine vector similarity with attribute filtering\n\n**Difficulty:** Advanced\n\n**Builds upon:** vadd\n\n**Commands:** VSETATTR, VSIM\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n&nbsp;\n> [!NOTE] Try it out\n> Experiment with vector set commands interactively in the [Redis playground](https://redis.io/try/sandbox) — no installation required."},{"id":"more-information","title":"More information","role":"content","text":"See the other pages in this section to learn more about the features\nand performance parameters of vector sets."}],"examples":[{"id":"basic-operations-ex0","language":"plaintext","code":"> VADD points VALUES 2 1.0 1.0 pt:A\n(integer) 1\n> VADD points VALUES 2 -1.0 -1.0 pt:B\n(integer) 1\n> VADD points VALUES 2 -1.0 1.0 pt:C\n(integer) 1\n> VADD points VALUES 2 1.0 -1.0 pt:D\n(integer) 1\n> VADD points VALUES 2 1.0 0 pt:E\n(integer) 1\n> TYPE points\nvectorset","section_id":"basic-operations"},{"id":"basic-operations-ex1","language":"csharp","code":"bool r1 = db.VectorSetAdd(\"points\", VectorSetAddRequest.Member(\"pt:A\", new float[] { 1f, 1f }, null));\n        Console.WriteLine(r1); // >>> True\n\n        bool r2 = db.VectorSetAdd(\"points\", VectorSetAddRequest.Member(\"pt:B\", new float[] { -1f, -1f }, null));\n        Console.WriteLine(r2); // >>> True\n\n        bool r3 = db.VectorSetAdd(\"points\", VectorSetAddRequest.Member(\"pt:C\", new float[] { -1f, 1f }, null));\n        Console.WriteLine(r3); // >>> True\n\n        bool r4 = db.VectorSetAdd(\"points\", VectorSetAddRequest.Member(\"pt:D\", new float[] { 1f, -1f }, null));\n        Console.WriteLine(r4); // >>> True\n\n        bool r5 = db.VectorSetAdd(\"points\", VectorSetAddRequest.Member(\"pt:E\", new float[] { 1f, 0f }, null));\n        Console.WriteLine(r5); // >>> True","section_id":"basic-operations"},{"id":"basic-operations-ex2","language":"go","code":"res1, err := rdb.VAdd(ctx, \"points\", \"pt:A\",\n\t\t&redis.VectorValues{Val: []float64{1.0, 1.0}},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> true\n\n\tres2, err := rdb.VAdd(ctx, \"points\", \"pt:B\",\n\t\t&redis.VectorValues{Val: []float64{-1.0, -1.0}},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> true\n\n\tres3, err := rdb.VAdd(ctx, \"points\", \"pt:C\",\n\t\t&redis.VectorValues{Val: []float64{-1.0, 1.0}},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3) // >>> true\n\n\tres4, err := rdb.VAdd(ctx, \"points\", \"pt:D\",\n\t\t&redis.VectorValues{Val: []float64{1.0, -1.0}},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4) // >>> true\n\n\tres5, err := rdb.VAdd(ctx, \"points\", \"pt:E\",\n\t\t&redis.VectorValues{Val: []float64{1.0, 0.0}},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res5) // >>> true\n\n\tres6, err := rdb.Type(ctx, \"points\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res6) // >>> vectorset","section_id":"basic-operations"},{"id":"basic-operations-ex3","language":"java","code":"CompletableFuture<Boolean> addPointA = asyncCommands.vadd(\"points\", \"pt:A\", 1.0, 1.0).thenApply(result -> {\n                System.out.println(result); // >>> true\n                return result;\n            }).toCompletableFuture();\n\n            CompletableFuture<Boolean> addPointB = asyncCommands.vadd(\"points\", \"pt:B\", -1.0, -1.0).thenApply(result -> {\n                System.out.println(result); // >>> true\n                return result;\n            }).toCompletableFuture();\n\n            CompletableFuture<Boolean> addPointC = asyncCommands.vadd(\"points\", \"pt:C\", -1.0, 1.0).thenApply(result -> {\n                System.out.println(result); // >>> true\n                return result;\n            }).toCompletableFuture();\n\n            CompletableFuture<Boolean> addPointD = asyncCommands.vadd(\"points\", \"pt:D\", 1.0, -1.0).thenApply(result -> {\n                System.out.println(result); // >>> true\n                return result;\n            }).toCompletableFuture();\n\n            CompletableFuture<Boolean> addPointE = asyncCommands.vadd(\"points\", \"pt:E\", 1.0, 0.0).thenApply(result -> {\n                System.out.println(result); // >>> true\n                return result;\n            }).toCompletableFuture();\n\n            // Chain checkDataType after all vadd operations complete\n            CompletableFuture<Void> vaddOperations = CompletableFuture\n                    .allOf(addPointA, addPointB, addPointC, addPointD, addPointE)\n                    .thenCompose(ignored -> asyncCommands.type(\"points\")).thenAccept(result -> {\n                        System.out.println(result); // >>> vectorset\n                    }).toCompletableFuture();","section_id":"basic-operations"},{"id":"basic-operations-ex4","language":"java","code":"Mono<Boolean> addPointA = reactiveCommands.vadd(\"points\", \"pt:A\", 1.0, 1.0).doOnNext(result -> {\n                System.out.println(result); // >>> true\n            });\n\n            Mono<Boolean> addPointB = reactiveCommands.vadd(\"points\", \"pt:B\", -1.0, -1.0).doOnNext(result -> {\n                System.out.println(result); // >>> true\n            });\n\n            Mono<Boolean> addPointC = reactiveCommands.vadd(\"points\", \"pt:C\", -1.0, 1.0).doOnNext(result -> {\n                System.out.println(result); // >>> true\n            });\n\n            Mono<Boolean> addPointD = reactiveCommands.vadd(\"points\", \"pt:D\", 1.0, -1.0).doOnNext(result -> {\n                System.out.println(result); // >>> true\n            });\n\n            Mono<Boolean> addPointE = reactiveCommands.vadd(\"points\", \"pt:E\", 1.0, 0.0).doOnNext(result -> {\n                System.out.println(result); // >>> true\n            });\n\n            Mono<Void> vaddOperations = Mono.when(addPointA, addPointB, addPointC, addPointD, addPointE)\n                    .then(reactiveCommands.type(\"points\")).doOnNext(result -> {\n                        System.out.println(result); // >>> vectorset\n                    }).then();","section_id":"basic-operations"},{"id":"basic-operations-ex5","language":"java","code":"boolean res1 = jedis.vadd(\"points\", new float[] { 1.0f, 1.0f }, \"pt:A\");\n      System.out.println(res1); // >>> true\n\n      boolean res2 = jedis.vadd(\"points\", new float[] { -1.0f, -1.0f }, \"pt:B\");\n      System.out.println(res2); // >>> true\n\n      boolean res3 = jedis.vadd(\"points\", new float[] { -1.0f, 1.0f }, \"pt:C\");\n      System.out.println(res3); // >>> true\n\n      boolean res4 = jedis.vadd(\"points\", new float[] { 1.0f, -1.0f }, \"pt:D\");\n      System.out.println(res4); // >>> true\n\n      boolean res5 = jedis.vadd(\"points\", new float[] { 1.0f, 0.0f }, \"pt:E\");\n      System.out.println(res5); // >>> true\n\n      String res6 = jedis.type(\"points\");\n      System.out.println(res6); // >>> vectorset","section_id":"basic-operations"},{"id":"basic-operations-ex6","language":"javascript","code":"const res1 = await client.vAdd(\"points\", [1.0, 1.0], \"pt:A\");\nconsole.log(res1);  // >>> true\n\nconst res2 = await client.vAdd(\"points\", [-1.0, -1.0], \"pt:B\");\nconsole.log(res2);  // >>> true\n\nconst res3 = await client.vAdd(\"points\", [-1.0, 1.0], \"pt:C\");\nconsole.log(res3);  // >>> true\n\nconst res4 = await client.vAdd(\"points\", [1.0, -1.0], \"pt:D\");\nconsole.log(res4);  // >>> true\n\nconst res5 = await client.vAdd(\"points\", [1.0, 0], \"pt:E\");\nconsole.log(res5);  // >>> true\n\nconst res6 = await client.type(\"points\");\nconsole.log(res6);  // >>> vectorset","section_id":"basic-operations"},{"id":"basic-operations-ex7","language":"php","code":"$res1 = $r->vadd('points', [1.0, 1.0], 'pt:A');\n        echo $res1 . PHP_EOL;\n        // >>> 1\n\n        $res2 = $r->vadd('points', [-1.0, -1.0], 'pt:B');\n        echo $res2 . PHP_EOL;\n        // >>> 1\n\n        $res3 = $r->vadd('points', [-1.0, 1.0], 'pt:C');\n        echo $res3 . PHP_EOL;\n        // >>> 1\n\n        $res4 = $r->vadd('points', [1.0, -1.0], 'pt:D');\n        echo $res4 . PHP_EOL;\n        // >>> 1\n\n        $res5 = $r->vadd('points', [1.0, 0.0], 'pt:E');\n        echo $res5 . PHP_EOL;\n        // >>> 1\n\n        $res6 = $r->type('points');\n        echo $res6 . PHP_EOL;\n        // >>> vectorset","section_id":"basic-operations"},{"id":"basic-operations-ex8","language":"python","code":"res1 = r.vset().vadd(\"points\", [1.0, 1.0], \"pt:A\")\nprint(res1)  # >>> 1\n\nres2 = r.vset().vadd(\"points\", [-1.0, -1.0], \"pt:B\")\nprint(res2)  # >>> 1\n\nres3 = r.vset().vadd(\"points\", [-1.0, 1.0], \"pt:C\")\nprint(res3)  # >>> 1\n\nres4 = r.vset().vadd(\"points\", [1.0, -1.0], \"pt:D\")\nprint(res4)  # >>> 1\n\nres5 = r.vset().vadd(\"points\", [1.0, 0], \"pt:E\")\nprint(res5)  # >>> 1\n\nres6 = r.type(\"points\")\nprint(res6)  # >>> vectorset","section_id":"basic-operations"},{"id":"basic-operations-ex9","language":"rust","code":"if let Ok(res) = r\n            .vadd(\n                \"points\",\n                VectorAddInput::Values(EmbeddingInput::Float64(&[1.0, 1.0])),\n                \"pt:A\",\n            )\n            .await\n        {\n            let res: bool = res;\n            println!(\"{res}\"); // >>> true\n        }\n\n        if let Ok(res) = r\n            .vadd(\n                \"points\",\n                VectorAddInput::Values(EmbeddingInput::Float64(&[-1.0, -1.0])),\n                \"pt:B\",\n            )\n            .await\n        {\n            let res: bool = res;\n            println!(\"{res}\"); // >>> true\n        }\n\n        if let Ok(res) = r\n            .vadd(\n                \"points\",\n                VectorAddInput::Values(EmbeddingInput::Float64(&[-1.0, 1.0])),\n                \"pt:C\",\n            )\n            .await\n        {\n            let res: bool = res;\n            println!(\"{res}\"); // >>> true\n        }\n\n        if let Ok(res) = r\n            .vadd(\n                \"points\",\n                VectorAddInput::Values(EmbeddingInput::Float64(&[1.0, -1.0])),\n                \"pt:D\",\n            )\n            .await\n        {\n            let res: bool = res;\n            println!(\"{res}\"); // >>> true\n        }\n\n        if let Ok(res) = r\n            .vadd(\n                \"points\",\n                VectorAddInput::Values(EmbeddingInput::Float64(&[1.0, 0.0])),\n                \"pt:E\",\n            )\n            .await\n        {\n            let res: bool = res;\n            println!(\"{res}\"); // >>> true\n        }\n\n        let res: String = cmd(\"TYPE\")\n            .arg(\"points\")\n            .query_async(&mut r)\n            .await\n            .expect(\"TYPE points should succeed\");\n        println!(\"{res}\"); // >>> vectorset","section_id":"basic-operations"},{"id":"basic-operations-ex10","language":"rust","code":"if let Ok(res) = r.vadd(\n            \"points\",\n            VectorAddInput::Values(EmbeddingInput::Float64(&[1.0, 1.0])),\n            \"pt:A\",\n        ) {\n            let res: bool = res;\n            println!(\"{res}\"); // >>> true\n        }\n\n        if let Ok(res) = r.vadd(\n            \"points\",\n            VectorAddInput::Values(EmbeddingInput::Float64(&[-1.0, -1.0])),\n            \"pt:B\",\n        ) {\n            let res: bool = res;\n            println!(\"{res}\"); // >>> true\n        }\n\n        if let Ok(res) = r.vadd(\n            \"points\",\n            VectorAddInput::Values(EmbeddingInput::Float64(&[-1.0, 1.0])),\n            \"pt:C\",\n        ) {\n            let res: bool = res;\n            println!(\"{res}\"); // >>> true\n        }\n\n        if let Ok(res) = r.vadd(\n            \"points\",\n            VectorAddInput::Values(EmbeddingInput::Float64(&[1.0, -1.0])),\n            \"pt:D\",\n        ) {\n            let res: bool = res;\n            println!(\"{res}\"); // >>> true\n        }\n\n        if let Ok(res) = r.vadd(\n            \"points\",\n            VectorAddInput::Values(EmbeddingInput::Float64(&[1.0, 0.0])),\n            \"pt:E\",\n        ) {\n            let res: bool = res;\n            println!(\"{res}\"); // >>> true\n        }\n\n        let res: String = cmd(\"TYPE\")\n            .arg(\"points\")\n            .query(&mut r)\n            .expect(\"TYPE points should succeed\");\n        println!(\"{res}\"); // >>> vectorset","section_id":"basic-operations"},{"id":"basic-operations-ex11","language":"plaintext","code":"> VCARD points\n(integer) 5\n> VDIM points\n(integer) 2","section_id":"basic-operations"},{"id":"basic-operations-ex12","language":"csharp","code":"long card = db.VectorSetLength(\"points\");\n        Console.WriteLine(card); // >>> 5\n\n        int dim = db.VectorSetDimension(\"points\");\n        Console.WriteLine(dim); // >>> 2","section_id":"basic-operations"},{"id":"basic-operations-ex13","language":"go","code":"res7, err := rdb.VCard(ctx, \"points\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res7) // >>> 5\n\n\tres8, err := rdb.VDim(ctx, \"points\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res8) // >>> 2","section_id":"basic-operations"},{"id":"basic-operations-ex14","language":"java","code":"CompletableFuture<Void> getCardinality = asyncCommands.vcard(\"points\").thenAccept(result -> {\n                System.out.println(result); // >>> 5\n            }).toCompletableFuture();\n\n            CompletableFuture<Void> getDimensions = asyncCommands.vdim(\"points\").thenAccept(result -> {\n                System.out.println(result); // >>> 2\n            }).toCompletableFuture();","section_id":"basic-operations"},{"id":"basic-operations-ex15","language":"java","code":"Mono<Long> getCardinality = reactiveCommands.vcard(\"points\").doOnNext(result -> {\n                System.out.println(result); // >>> 5\n            });\n\n            Mono<Long> getDimensions = reactiveCommands.vdim(\"points\").doOnNext(result -> {\n                System.out.println(result); // >>> 2\n            });","section_id":"basic-operations"},{"id":"basic-operations-ex16","language":"java","code":"long res7 = jedis.vcard(\"points\");\n      System.out.println(res7); // >>> 5\n\n      long res8 = jedis.vdim(\"points\");\n      System.out.println(res8); // >>> 2","section_id":"basic-operations"},{"id":"basic-operations-ex17","language":"javascript","code":"const res7 = await client.vCard(\"points\");\nconsole.log(res7);  // >>> 5\n\nconst res8 = await client.vDim(\"points\");\nconsole.log(res8);  // >>> 2","section_id":"basic-operations"},{"id":"basic-operations-ex18","language":"php","code":"$res7 = $r->vcard('points');\n        echo $res7 . PHP_EOL;\n        // >>> 5\n\n        $res8 = $r->vdim('points');\n        echo $res8 . PHP_EOL;\n        // >>> 2","section_id":"basic-operations"},{"id":"basic-operations-ex19","language":"python","code":"res7 = r.vset().vcard(\"points\")\nprint(res7)  # >>> 5\n\nres8 = r.vset().vdim(\"points\")\nprint(res8)  # >>> 2","section_id":"basic-operations"},{"id":"basic-operations-ex20","language":"rust","code":"if let Ok(res) = r.vcard(\"points\").await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 5\n        }\n\n        if let Ok(res) = r.vdim(\"points\").await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 2\n        }","section_id":"basic-operations"},{"id":"basic-operations-ex21","language":"rust","code":"if let Ok(res) = r.vcard(\"points\") {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 5\n        }\n\n        if let Ok(res) = r.vdim(\"points\") {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 2\n        }","section_id":"basic-operations"},{"id":"basic-operations-ex22","language":"plaintext","code":"> VEMB points pt:A\n1) \"0.9999999403953552\"\n2) \"0.9999999403953552\"\n> VEMB points pt:B\n1) \"-0.9999999403953552\"\n2) \"-0.9999999403953552\"\n> VEMB points pt:C\n1) \"-0.9999999403953552\"\n2) \"0.9999999403953552\"\n> VEMB points pt:D\n1) \"0.9999999403953552\"\n2) \"-0.9999999403953552\"\n> VEMB points pt:E\n1) \"1\"\n2) \"0\"","section_id":"basic-operations"},{"id":"basic-operations-ex23","language":"csharp","code":"using (Lease<float>? eA = db.VectorSetGetApproximateVector(\"points\", \"pt:A\"))\n        {\n            Span<float> a = eA!.Span;\n            Console.WriteLine($\"[{a[0]}, {a[1]}]\"); // >>> [0.9999999403953552, 0.9999999403953552]\n        }\n        using (Lease<float>? eB = db.VectorSetGetApproximateVector(\"points\", \"pt:B\"))\n        {\n            Span<float> b = eB!.Span;\n            Console.WriteLine($\"[{b[0]}, {b[1]}]\"); // >>> [-0.9999999403953552, -0.9999999403953552]\n        }\n        using (Lease<float>? eC = db.VectorSetGetApproximateVector(\"points\", \"pt:C\"))\n        {\n            Span<float> c = eC!.Span;\n            Console.WriteLine($\"[{c[0]}, {c[1]}]\"); // >>> [-0.9999999403953552, 0.9999999403953552]\n        }\n        using (Lease<float>? eD = db.VectorSetGetApproximateVector(\"points\", \"pt:D\"))\n        {\n            Span<float> d = eD!.Span;\n            Console.WriteLine($\"[{d[0]}, {d[1]}]\"); // >>> [0.9999999403953552, -0.9999999403953552]\n        }\n        using (Lease<float>? eE = db.VectorSetGetApproximateVector(\"points\", \"pt:E\"))\n        {\n            Span<float> e = eE!.Span;\n            Console.WriteLine($\"[{e[0]}, {e[1]}]\"); // >>> [1, 0]\n        }","section_id":"basic-operations"},{"id":"basic-operations-ex24","language":"go","code":"res9, err := rdb.VEmb(ctx, \"points\", \"pt:A\", false).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res9) // >>> [0.9999999403953552 0.9999999403953552]\n\n\tres10, err := rdb.VEmb(ctx, \"points\", \"pt:B\", false).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res10) // >>> [-0.9999999403953552 -0.9999999403953552]\n\n\tres11, err := rdb.VEmb(ctx, \"points\", \"pt:C\", false).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res11) // >>> [-0.9999999403953552 0.9999999403953552]\n\n\tres12, err := rdb.VEmb(ctx, \"points\", \"pt:D\", false).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res12) // >>> [0.9999999403953552 -0.9999999403953552]\n\n\tres13, err := rdb.VEmb(ctx, \"points\", \"pt:E\", false).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res13) // >>> [1 0]","section_id":"basic-operations"},{"id":"basic-operations-ex25","language":"java","code":"CompletableFuture<Void> getEmbeddingA = asyncCommands.vemb(\"points\", \"pt:A\").thenAccept(result -> {\n                System.out.println(result); // >>> [0.9999999403953552, 0.9999999403953552]\n            }).toCompletableFuture();\n\n            CompletableFuture<Void> getEmbeddingB = asyncCommands.vemb(\"points\", \"pt:B\").thenAccept(result -> {\n                System.out.println(result); // >>> [-0.9999999403953552, -0.9999999403953552]\n            }).toCompletableFuture();\n\n            CompletableFuture<Void> getEmbeddingC = asyncCommands.vemb(\"points\", \"pt:C\").thenAccept(result -> {\n                System.out.println(result); // >>> [-0.9999999403953552, 0.9999999403953552]\n            }).toCompletableFuture();\n\n            CompletableFuture<Void> getEmbeddingD = asyncCommands.vemb(\"points\", \"pt:D\").thenAccept(result -> {\n                System.out.println(result); // >>> [0.9999999403953552, -0.9999999403953552]\n            }).toCompletableFuture();\n\n            CompletableFuture<Void> getEmbeddingE = asyncCommands.vemb(\"points\", \"pt:E\").thenAccept(result -> {\n                System.out.println(result); // >>> [1.0, 0.0]\n            }).toCompletableFuture();","section_id":"basic-operations"},{"id":"basic-operations-ex26","language":"java","code":"Mono<java.util.List<Double>> getEmbeddingA = reactiveCommands.vemb(\"points\", \"pt:A\").collectList()\n                    .doOnNext(result -> {\n                        System.out.println(result); // >>> [0.9999999403953552, 0.9999999403953552]\n                    });\n\n            Mono<java.util.List<Double>> getEmbeddingB = reactiveCommands.vemb(\"points\", \"pt:B\").collectList()\n                    .doOnNext(result -> {\n                        System.out.println(result); // >>> [-0.9999999403953552, -0.9999999403953552]\n                    });\n\n            Mono<java.util.List<Double>> getEmbeddingC = reactiveCommands.vemb(\"points\", \"pt:C\").collectList()\n                    .doOnNext(result -> {\n                        System.out.println(result); // >>> [-0.9999999403953552, 0.9999999403953552]\n                    });\n\n            Mono<java.util.List<Double>> getEmbeddingD = reactiveCommands.vemb(\"points\", \"pt:D\").collectList()\n                    .doOnNext(result -> {\n                        System.out.println(result); // >>> [0.9999999403953552, -0.9999999403953552]\n                    });\n\n            Mono<java.util.List<Double>> getEmbeddingE = reactiveCommands.vemb(\"points\", \"pt:E\").collectList()\n                    .doOnNext(result -> {\n                        System.out.println(result); // >>> [1.0, 0.0]\n                    });","section_id":"basic-operations"},{"id":"basic-operations-ex27","language":"java","code":"List<Double> res9 = jedis.vemb(\"points\", \"pt:A\");\n      System.out.println(res9); // >>> [0.9999999..., 0.9999999...]\n\n      List<Double> res10 = jedis.vemb(\"points\", \"pt:B\");\n      System.out.println(res10); // >>> [-0.9999999..., -0.9999999...]\n\n      List<Double> res11 = jedis.vemb(\"points\", \"pt:C\");\n      System.out.println(res11); // >>> [-0.9999999..., 0.9999999...]\n\n      List<Double> res12 = jedis.vemb(\"points\", \"pt:D\");\n      System.out.println(res12); // >>> [0.9999999..., -0.9999999...]\n\n      List<Double> res13 = jedis.vemb(\"points\", \"pt:E\");\n      System.out.println(res13); // >>> [1, 0]","section_id":"basic-operations"},{"id":"basic-operations-ex28","language":"javascript","code":"const res9 = await client.vEmb(\"points\", \"pt:A\");\nconsole.log(res9);  // >>> [0.9999999403953552, 0.9999999403953552]\n\nconst res10 = await client.vEmb(\"points\", \"pt:B\");\nconsole.log(res10);  // >>> [-0.9999999403953552, -0.9999999403953552]\n\nconst res11 = await client.vEmb(\"points\", \"pt:C\");\nconsole.log(res11);  // >>> [-0.9999999403953552, 0.9999999403953552]\n\nconst res12 = await client.vEmb(\"points\", \"pt:D\");\nconsole.log(res12);  // >>> [0.9999999403953552, -0.9999999403953552]\n\nconst res13 = await client.vEmb(\"points\", \"pt:E\");\nconsole.log(res13);  // >>> [1, 0]","section_id":"basic-operations"},{"id":"basic-operations-ex29","language":"php","code":"$res9 = $r->vemb('points', 'pt:A');\n        echo json_encode($res9) . PHP_EOL;\n        // >>> [0.9999999403953552, 0.9999999403953552]\n\n        $res10 = $r->vemb('points', 'pt:B');\n        echo json_encode($res10) . PHP_EOL;\n        // >>> [-0.9999999403953552, -0.9999999403953552]\n\n        $res11 = $r->vemb('points', 'pt:C');\n        echo json_encode($res11) . PHP_EOL;\n        // >>> [-0.9999999403953552, 0.9999999403953552]\n\n        $res12 = $r->vemb('points', 'pt:D');\n        echo json_encode($res12) . PHP_EOL;\n        // >>> [0.9999999403953552, -0.9999999403953552]\n\n        $res13 = $r->vemb('points', 'pt:E');\n        echo json_encode($res13) . PHP_EOL;\n        // >>> [1,0]","section_id":"basic-operations"},{"id":"basic-operations-ex30","language":"python","code":"res9 = r.vset().vemb(\"points\", \"pt:A\")\nprint(res9)  # >>> [0.9999999403953552, 0.9999999403953552]\n\nres10 = r.vset().vemb(\"points\", \"pt:B\")\nprint(res10)  # >>> [-0.9999999403953552, -0.9999999403953552]\n\nres11 = r.vset().vemb(\"points\", \"pt:C\")\nprint(res11)  # >>> [-0.9999999403953552, 0.9999999403953552]\n\nres12 = r.vset().vemb(\"points\", \"pt:D\")\nprint(res12)  # >>> [0.9999999403953552, -0.9999999403953552]\n\nres13 = r.vset().vemb(\"points\", \"pt:E\")\nprint(res13)  # >>> [1, 0]","section_id":"basic-operations"},{"id":"basic-operations-ex31","language":"rust","code":"if let Ok(res) = r.vemb(\"points\", \"pt:A\").await {\n            let res: Vec<f64> = res;\n            println!(\"{res:?}\");\n            // >>> [0.9999999403953552, 0.9999999403953552]\n        }\n\n        if let Ok(res) = r.vemb(\"points\", \"pt:B\").await {\n            let res: Vec<f64> = res;\n            println!(\"{res:?}\");\n            // >>> [-0.9999999403953552, -0.9999999403953552]\n        }\n\n        if let Ok(res) = r.vemb(\"points\", \"pt:C\").await {\n            let res: Vec<f64> = res;\n            println!(\"{res:?}\");\n            // >>> [-0.9999999403953552, 0.9999999403953552]\n        }\n\n        if let Ok(res) = r.vemb(\"points\", \"pt:D\").await {\n            let res: Vec<f64> = res;\n            println!(\"{res:?}\");\n            // >>> [0.9999999403953552, -0.9999999403953552]\n        }\n\n        if let Ok(res) = r.vemb(\"points\", \"pt:E\").await {\n            let res: Vec<f64> = res;\n            println!(\"{res:?}\"); // >>> [1.0, 0.0]\n        }","section_id":"basic-operations"},{"id":"basic-operations-ex32","language":"rust","code":"if let Ok(res) = r.vemb(\"points\", \"pt:A\") {\n            let res: Vec<f64> = res;\n            println!(\"{res:?}\");\n            // >>> [0.9999999403953552, 0.9999999403953552]\n        }\n\n        if let Ok(res) = r.vemb(\"points\", \"pt:B\") {\n            let res: Vec<f64> = res;\n            println!(\"{res:?}\");\n            // >>> [-0.9999999403953552, -0.9999999403953552]\n        }\n\n        if let Ok(res) = r.vemb(\"points\", \"pt:C\") {\n            let res: Vec<f64> = res;\n            println!(\"{res:?}\");\n            // >>> [-0.9999999403953552, 0.9999999403953552]\n        }\n\n        if let Ok(res) = r.vemb(\"points\", \"pt:D\") {\n            let res: Vec<f64> = res;\n            println!(\"{res:?}\");\n            // >>> [0.9999999403953552, -0.9999999403953552]\n        }\n\n        if let Ok(res) = r.vemb(\"points\", \"pt:E\") {\n            let res: Vec<f64> = res;\n            println!(\"{res:?}\"); // >>> [1.0, 0.0]\n        }","section_id":"basic-operations"},{"id":"basic-operations-ex33","language":"plaintext","code":"> VADD points VALUES 2 0 0 pt:F\n(integer) 1\n> VCARD points\n(integer) 6\n> VREM points pt:F\n(integer) 1\n> VCARD points\n(integer) 5","section_id":"basic-operations"},{"id":"basic-operations-ex34","language":"csharp","code":"bool addF = db.VectorSetAdd(\"points\", VectorSetAddRequest.Member(\"pt:F\", new float[] { 0f, 0f }, null));\n        Console.WriteLine(addF); // >>> True\n\n        long card1 = db.VectorSetLength(\"points\");\n        Console.WriteLine(card1); // >>> 6\n\n        bool remF = db.VectorSetRemove(\"points\", \"pt:F\");\n        Console.WriteLine(remF); // >>> True\n\n        long card2 = db.VectorSetLength(\"points\");\n        Console.WriteLine(card2); // >>> 5","section_id":"basic-operations"},{"id":"basic-operations-ex35","language":"go","code":"res18, err := rdb.VAdd(ctx, \"points\", \"pt:F\",\n\t\t&redis.VectorValues{Val: []float64{0.0, 0.0}},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res18) // >>> true\n\n\tres19, err := rdb.VCard(ctx, \"points\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res19) // >>> 6\n\n\tres20, err := rdb.VRem(ctx, \"points\", \"pt:F\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res20) // >>> true\n\n\tres21, err := rdb.VCard(ctx, \"points\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res21) // >>> 5","section_id":"basic-operations"},{"id":"basic-operations-ex36","language":"java","code":"CompletableFuture<Void> addTempPointF = asyncCommands.vadd(\"points\", \"pt:F\", 0.0, 0.0).thenAccept(result -> {\n                System.out.println(result); // >>> true\n            }).toCompletableFuture();\n\n            CompletableFuture<Void> checkCardinalityBefore = asyncCommands.vcard(\"points\").thenAccept(result -> {\n                System.out.println(result); // >>> 6\n            }).toCompletableFuture();\n\n            CompletableFuture<Void> removePointF = asyncCommands.vrem(\"points\", \"pt:F\").thenAccept(result -> {\n                System.out.println(result); // >>> true\n            }).toCompletableFuture();\n\n            CompletableFuture<Void> checkCardinalityAfter = asyncCommands.vcard(\"points\").thenAccept(result -> {\n                System.out.println(result); // >>> 5\n            }).toCompletableFuture();","section_id":"basic-operations"},{"id":"basic-operations-ex37","language":"java","code":"Mono<Boolean> addTempPointF = reactiveCommands.vadd(\"points\", \"pt:F\", 0.0, 0.0).doOnNext(result -> {\n                System.out.println(result); // >>> true\n            });\n\n            Mono<Long> checkCardinalityBefore = reactiveCommands.vcard(\"points\").doOnNext(result -> {\n                System.out.println(result); // >>> 6\n            });\n\n            Mono<Boolean> removePointF = reactiveCommands.vrem(\"points\", \"pt:F\").doOnNext(result -> {\n                System.out.println(result); // >>> true\n            });\n\n            Mono<Long> checkCardinalityAfter = reactiveCommands.vcard(\"points\").doOnNext(result -> {\n                System.out.println(result); // >>> 5\n            });","section_id":"basic-operations"},{"id":"basic-operations-ex38","language":"java","code":"boolean res18 = jedis.vadd(\"points\", new float[] { 0.0f, 0.0f }, \"pt:F\");\n      System.out.println(res18); // >>> true\n\n      long res19 = jedis.vcard(\"points\");\n      System.out.println(res19); // >>> 6\n\n      boolean res20 = jedis.vrem(\"points\", \"pt:F\");\n      System.out.println(res20); // >>> true\n\n      long res21 = jedis.vcard(\"points\");\n      System.out.println(res21); // >>> 5","section_id":"basic-operations"},{"id":"basic-operations-ex39","language":"javascript","code":"const res18 = await client.vAdd(\"points\", [0, 0], \"pt:F\");\nconsole.log(res18);  // >>> true\n\nconst res19 = await client.vCard(\"points\");\nconsole.log(res19);  // >>> 6\n\nconst res20 = await client.vRem(\"points\", \"pt:F\");\nconsole.log(res20);  // >>> true\n\nconst res21 = await client.vCard(\"points\");\nconsole.log(res21);  // >>> 5","section_id":"basic-operations"},{"id":"basic-operations-ex40","language":"php","code":"$res18 = $r->vadd('points', [0, 0], 'pt:F');\n        echo $res18 . PHP_EOL;\n        // >>> 1\n\n        $res19 = $r->vcard('points');\n        echo $res19 . PHP_EOL;\n        // >>> 6\n\n        $res20 = $r->vrem('points', 'pt:F');\n        echo $res20 . PHP_EOL;\n        // >>> 1\n\n        $res21 = $r->vcard('points');\n        echo $res21 . PHP_EOL;\n        // >>> 5","section_id":"basic-operations"},{"id":"basic-operations-ex41","language":"python","code":"res18 = r.vset().vadd(\"points\", [0, 0], \"pt:F\")\nprint(res18)  # >>> 1\n\nres19 = r.vset().vcard(\"points\")\nprint(res19)  # >>> 6\n\nres20 = r.vset().vrem(\"points\", \"pt:F\")\nprint(res20)  # >>> 1\n\nres21 = r.vset().vcard(\"points\")\nprint(res21)  # >>> 5","section_id":"basic-operations"},{"id":"basic-operations-ex42","language":"rust","code":"if let Ok(res) = r\n            .vadd(\n                \"points\",\n                VectorAddInput::Values(EmbeddingInput::String(&[\"0\", \"0\"])),\n                \"pt:F\",\n            )\n            .await\n        {\n            let res: bool = res;\n            println!(\"{res}\"); // >>> true\n        }\n\n        if let Ok(res) = r.vcard(\"points\").await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 6\n        }\n\n        if let Ok(res) = r.vrem(\"points\", \"pt:F\").await {\n            let res: bool = res;\n            println!(\"{res}\"); // >>> true\n        }\n\n        if let Ok(res) = r.vcard(\"points\").await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 5\n        }","section_id":"basic-operations"},{"id":"basic-operations-ex43","language":"rust","code":"if let Ok(res) = r.vadd(\n            \"points\",\n            VectorAddInput::Values(EmbeddingInput::String(&[\"0\", \"0\"])),\n            \"pt:F\",\n        ) {\n            let res: bool = res;\n            println!(\"{res}\"); // >>> true\n        }\n\n        if let Ok(res) = r.vcard(\"points\") {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 6\n        }\n\n        if let Ok(res) = r.vrem(\"points\", \"pt:F\") {\n            let res: bool = res;\n            println!(\"{res}\"); // >>> true\n        }\n\n        if let Ok(res) = r.vcard(\"points\") {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 5\n        }","section_id":"basic-operations"},{"id":"basic-operations-ex44","language":"plaintext","code":"> VSETATTR points pt:A \"{\\\"name\\\": \\\"Point A\\\", \\\"description\\\": \\\"First point added\\\"}\"\n(integer) 1\n> VGETATTR points pt:A\n\"{\\\"name\\\": \\\"Point A\\\", \\\"description\\\": \\\"First point added\\\"}\"\n> VSETATTR points pt:A \"\"\n(integer) 1\n> VGETATTR points pt:A\n(nil)","section_id":"basic-operations"},{"id":"basic-operations-ex45","language":"csharp","code":"string attrJson = \"{\\\"name\\\":\\\"Point A\\\",\\\"description\\\":\\\"First point added\\\"}\";\n        bool setAttr1 = db.VectorSetSetAttributesJson(\"points\", \"pt:A\", attrJson);\n        Console.WriteLine(setAttr1); // >>> True\n\n        string? getAttr1 = db.VectorSetGetAttributesJson(\"points\", \"pt:A\");\n        Console.WriteLine(getAttr1); // >>> {\"name\":\"Point A\",\"description\":\"First point added\"}\n\n        bool clearAttr = db.VectorSetSetAttributesJson(\"points\", \"pt:A\", \"\");\n        Console.WriteLine(clearAttr); // >>> True\n\n        string? getAttr2 = db.VectorSetGetAttributesJson(\"points\", \"pt:A\");\n        Console.WriteLine(getAttr2 is null ? \"None\" : getAttr2); // >>> None","section_id":"basic-operations"},{"id":"basic-operations-ex46","language":"go","code":"attrs := map[string]interface{}{\n\t\t\"name\":        \"Point A\",\n\t\t\"description\": \"First point added\",\n\t}\n\n\tres14, err := rdb.VSetAttr(ctx, \"points\", \"pt:A\", attrs).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res14) // >>> true\n\n\tres15, err := rdb.VGetAttr(ctx, \"points\", \"pt:A\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res15)\n\t// >>> {\"description\":\"First point added\",\"name\":\"Point A\"}\n\n\tres16, err := rdb.VClearAttributes(ctx, \"points\", \"pt:A\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res16) // >>> true\n\n\t// `VGetAttr()` returns an error if the attribute doesn't exist.\n\t_, err = rdb.VGetAttr(ctx, \"points\", \"pt:A\").Result()\n\n\tif err != nil {\n\t\tfmt.Println(err)\n\t}","section_id":"basic-operations"},{"id":"basic-operations-ex47","language":"java","code":"CompletableFuture<Void> setAttributeA = asyncCommands\n                    .vsetattr(\"points\", \"pt:A\", \"{\\\"name\\\": \\\"Point A\\\", \\\"description\\\": \\\"First point added\\\"}\")\n                    .thenAccept(result -> {\n                        System.out.println(result); // >>> true\n                    }).toCompletableFuture();\n\n            CompletableFuture<Void> getAttributeA = asyncCommands.vgetattr(\"points\", \"pt:A\").thenAccept(result -> {\n                System.out.println(result); // >>> {\"name\": \"Point A\", \"description\": \"First point added\"}\n            }).toCompletableFuture();\n\n            CompletableFuture<Void> clearAttributeA = asyncCommands.vsetattr(\"points\", \"pt:A\", \"\").thenAccept(result -> {\n                System.out.println(result); // >>> true\n            }).toCompletableFuture();\n\n            CompletableFuture<Void> verifyAttributeCleared = asyncCommands.vgetattr(\"points\", \"pt:A\").thenAccept(result -> {\n                System.out.println(result); // >>> null\n            }).toCompletableFuture();","section_id":"basic-operations"},{"id":"basic-operations-ex48","language":"java","code":"Mono<Boolean> setAttributeA = reactiveCommands\n                    .vsetattr(\"points\", \"pt:A\", \"{\\\"name\\\": \\\"Point A\\\", \\\"description\\\": \\\"First point added\\\"}\")\n                    .doOnNext(result -> {\n                        System.out.println(result); // >>> true\n                    });\n\n            Mono<String> getAttributeA = reactiveCommands.vgetattr(\"points\", \"pt:A\").doOnNext(result -> {\n                System.out.println(result); // >>> {\"name\": \"Point A\", \"description\": \"First point added\"}\n            });\n\n            Mono<Boolean> clearAttributeA = reactiveCommands.vsetattr(\"points\", \"pt:A\", \"\").doOnNext(result -> {\n                System.out.println(result); // >>> true\n            });\n\n            Mono<String> verifyAttributeCleared = reactiveCommands.vgetattr(\"points\", \"pt:A\").doOnNext(result -> {\n                System.out.println(result); // >>> null\n            });","section_id":"basic-operations"},{"id":"basic-operations-ex49","language":"java","code":"boolean res14 = jedis.vsetattr(\"points\", \"pt:A\",\n        \"{\\\"name\\\":\\\"Point A\\\",\\\"description\\\":\\\"First point added\\\"}\");\n      System.out.println(res14); // >>> true\n\n      String res15 = jedis.vgetattr(\"points\", \"pt:A\");\n      System.out.println(res15);\n      // >>> {\"name\":\"Point A\",\"description\":\"First point added\"}\n\n      boolean res16 = jedis.vsetattr(\"points\", \"pt:A\", \"\");\n      System.out.println(res16); // >>> true\n\n      String res17 = jedis.vgetattr(\"points\", \"pt:A\");\n      System.out.println(res17); // >>> null","section_id":"basic-operations"},{"id":"basic-operations-ex50","language":"javascript","code":"const res14 = await client.vSetAttr(\"points\", \"pt:A\", {\n  name: \"Point A\",\n  description: \"First point added\"\n});\nconsole.log(res14);  // >>> true\n\nconst res15 = await client.vGetAttr(\"points\", \"pt:A\");\nconsole.log(res15);\n// >>> {name: 'Point A', description: 'First point added'}\n\nconst res16 = await client.vSetAttr(\"points\", \"pt:A\", \"\");\nconsole.log(res16);  // >>> true\n\nconst res17 = await client.vGetAttr(\"points\", \"pt:A\");\nconsole.log(res17);  // >>> null","section_id":"basic-operations"},{"id":"basic-operations-ex51","language":"php","code":"$res14 = $r->vsetattr('points', 'pt:A', '{\n            \"name\": \"Point A\",\n            \"description\": \"First point added\"\n        }');\n        echo $res14 . PHP_EOL;\n        // >>> 1\n\n        $res15 = $r->vgetattr('points', 'pt:A');\n        echo json_encode($res15) . PHP_EOL;\n        // >>> {\"name\":\"Point A\",\"description\":\"First point added\"}\n\n        $res16 = $r->vsetattr('points', 'pt:A', '');\n        echo $res16 . PHP_EOL;\n        // >>> 1\n\n        $res17 = $r->vgetattr('points', 'pt:A');\n        echo json_encode($res17) . PHP_EOL;\n        // >>> null","section_id":"basic-operations"},{"id":"basic-operations-ex52","language":"python","code":"res14 = r.vset().vsetattr(\"points\", \"pt:A\", {\n    \"name\": \"Point A\",\n    \"description\": \"First point added\"\n})\nprint(res14)  # >>> 1\n\nres15 = r.vset().vgetattr(\"points\", \"pt:A\")\nprint(res15)\n# >>> {'name': 'Point A', 'description': 'First point added'}\n\nres16 = r.vset().vsetattr(\"points\", \"pt:A\", \"\")\nprint(res16)  # >>> 1\n\nres17 = r.vset().vgetattr(\"points\", \"pt:A\")\nprint(res17)  # >>> None","section_id":"basic-operations"},{"id":"basic-operations-ex53","language":"rust","code":"let res: bool = cmd(\"VSETATTR\")\n            .arg(\"points\")\n            .arg(\"pt:A\")\n            .arg(\"{\\\"name\\\":\\\"Point A\\\",\\\"description\\\":\\\"First point added\\\"}\")\n            .query_async(&mut r)\n            .await\n            .expect(\"VSETATTR should succeed\");\n        println!(\"{res}\"); // >>> true\n\n        if let Ok(res) = r.vgetattr(\"points\", \"pt:A\").await {\n            let res: Option<String> = res;\n            println!(\n                \"{}\",\n                res.clone().unwrap_or_else(|| \"None\".to_string())\n            );\n            // >>> {\"name\":\"Point A\",\"description\":\"First point added\"}\n        }\n\n        let res: bool = cmd(\"VSETATTR\")\n            .arg(\"points\")\n            .arg(\"pt:A\")\n            .arg(\"\")\n            .query_async(&mut r)\n            .await\n            .expect(\"VSETATTR clear should succeed\");\n        println!(\"{res}\"); // >>> true\n\n        if let Ok(res) = r.vgetattr(\"points\", \"pt:A\").await {\n            let res: Option<String> = res;\n            println!(\n                \"{}\",\n                res.clone().unwrap_or_else(|| \"None\".to_string())\n            ); // >>> None\n        }","section_id":"basic-operations"},{"id":"basic-operations-ex54","language":"rust","code":"let res: bool = cmd(\"VSETATTR\")\n            .arg(\"points\")\n            .arg(\"pt:A\")\n            .arg(\"{\\\"name\\\":\\\"Point A\\\",\\\"description\\\":\\\"First point added\\\"}\")\n            .query(&mut r)\n            .expect(\"VSETATTR should succeed\");\n        println!(\"{res}\"); // >>> true\n\n        if let Ok(res) = r.vgetattr(\"points\", \"pt:A\") {\n            let res: Option<String> = res;\n            println!(\n                \"{}\",\n                res.clone().unwrap_or_else(|| \"None\".to_string())\n            );\n            // >>> {\"name\":\"Point A\",\"description\":\"First point added\"}\n        }\n\n        let res: bool = cmd(\"VSETATTR\")\n            .arg(\"points\")\n            .arg(\"pt:A\")\n            .arg(\"\")\n            .query(&mut r)\n            .expect(\"VSETATTR clear should succeed\");\n        println!(\"{res}\"); // >>> true\n\n        if let Ok(res) = r.vgetattr(\"points\", \"pt:A\") {\n            let res: Option<String> = res;\n            println!(\n                \"{}\",\n                res.clone().unwrap_or_else(|| \"None\".to_string())\n            ); // >>> None\n        }","section_id":"basic-operations"},{"id":"vector-similarity-search-ex0","language":"plaintext","code":"> VSIM points values 2 0.9 0.1\n1) \"pt:E\"\n2) \"pt:A\"\n3) \"pt:D\"\n4) \"pt:C\"\n5) \"pt:B\"","section_id":"vector-similarity-search"},{"id":"vector-similarity-search-ex1","language":"csharp","code":"VectorSetSimilaritySearchRequest qBasic = VectorSetSimilaritySearchRequest.ByVector(new float[] { 0.9f, 0.1f });\n        using (Lease<VectorSetSimilaritySearchResult>? res = db.VectorSetSimilaritySearch(\"points\", qBasic))\n        {\n            VectorSetSimilaritySearchResult[] items = res!.Span.ToArray();\n            string[] ordered = items.Select(x => (string?)x.Member).Where(s => s is not null).Select(s => s!).ToArray();\n            Console.WriteLine(\"[\" + string.Join(\", \", ordered.Select(s => $\"'{s}'\")) + \"]\");\n            // >>> ['pt:E', 'pt:A', 'pt:D', 'pt:C', 'pt:B']\n        }","section_id":"vector-similarity-search"},{"id":"vector-similarity-search-ex2","language":"go","code":"res22, err := rdb.VSim(ctx, \"points\",\n\t\t&redis.VectorValues{Val: []float64{0.9, 0.1}},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res22) // >>> [pt:E pt:A pt:D pt:C pt:B]","section_id":"vector-similarity-search"},{"id":"vector-similarity-search-ex3","language":"java","code":"CompletableFuture<Void> basicSimilaritySearch = asyncCommands.vsim(\"points\", 0.9, 0.1).thenAccept(result -> {\n                System.out.println(result); // >>> [pt:E, pt:A, pt:D, pt:C, pt:B]\n            }).toCompletableFuture();","section_id":"vector-similarity-search"},{"id":"vector-similarity-search-ex4","language":"java","code":"Mono<java.util.List<String>> basicSimilaritySearch = reactiveCommands.vsim(\"points\", 0.9, 0.1).collectList()\n                    .doOnNext(result -> {\n                        System.out.println(result); // >>> [pt:E, pt:A, pt:D, pt:C, pt:B]\n                    });","section_id":"vector-similarity-search"},{"id":"vector-similarity-search-ex5","language":"java","code":"List<String> res22 = jedis.vsim(\"points\", new float[] { 0.9f, 0.1f });\n      System.out.println(res22);\n      // >>> [\"pt:E\", \"pt:A\", \"pt:D\", \"pt:C\", \"pt:B\"]","section_id":"vector-similarity-search"},{"id":"vector-similarity-search-ex6","language":"javascript","code":"const res22 = await client.vSim(\"points\", [0.9, 0.1]);\nconsole.log(res22);\n// >>> ['pt:E', 'pt:A', 'pt:D', 'pt:C', 'pt:B']","section_id":"vector-similarity-search"},{"id":"vector-similarity-search-ex7","language":"php","code":"$res22 = $r->vsim('points', [0.9, 0.1]);\n        echo json_encode($res22) . PHP_EOL;\n        // >>> [\"pt:E\",\"pt:A\",\"pt:D\",\"pt:C\",\"pt:B\"]","section_id":"vector-similarity-search"},{"id":"vector-similarity-search-ex8","language":"python","code":"res22 = r.vset().vsim(\"points\", [0.9, 0.1])\nprint(res22)\n# >>> ['pt:E', 'pt:A', 'pt:D', 'pt:C', 'pt:B']","section_id":"vector-similarity-search"},{"id":"vector-similarity-search-ex9","language":"rust","code":"if let Ok(res) = r\n            .vsim(\n                \"points\",\n                VectorSimilaritySearchInput::Values(EmbeddingInput::Float64(&[0.9, 0.1])),\n            )\n            .await\n        {\n            let res: Value = res;\n            if let Value::Array(items) = res {\n                let view: Vec<String> = items\n                    .into_iter()\n                    .map(|item| match item {\n                        Value::BulkString(bytes) => String::from_utf8(bytes).expect(\"utf8\"),\n                        Value::SimpleString(s) => s,\n                        other => panic!(\"Unexpected VSIM item: {other:?}\"),\n                    })\n                    .collect();\n                println!(\"{view:?}\");\n                // >>> [\"pt:E\", \"pt:A\", \"pt:D\", \"pt:C\", \"pt:B\"]\n            }\n        }","section_id":"vector-similarity-search"},{"id":"vector-similarity-search-ex10","language":"rust","code":"if let Ok(res) = r.vsim(\n            \"points\",\n            VectorSimilaritySearchInput::Values(EmbeddingInput::Float64(&[0.9, 0.1])),\n        ) {\n            let res: Value = res;\n            if let Value::Array(items) = res {\n                let view: Vec<String> = items\n                    .into_iter()\n                    .map(|item| match item {\n                        Value::BulkString(bytes) => String::from_utf8(bytes).expect(\"utf8\"),\n                        Value::SimpleString(s) => s,\n                        other => panic!(\"Unexpected VSIM item: {other:?}\"),\n                    })\n                    .collect();\n                println!(\"{view:?}\");\n                // >>> [\"pt:E\", \"pt:A\", \"pt:D\", \"pt:C\", \"pt:B\"]\n            }\n        }","section_id":"vector-similarity-search"},{"id":"vector-similarity-search-ex11","language":"plaintext","code":"> VSIM points ELE pt:A WITHSCORES COUNT 4\n1) \"pt:A\"\n2) \"1\"\n3) \"pt:E\"\n4) \"0.8535534143447876\"\n5) \"pt:C\"\n6) \"0.5\"\n7) \"pt:D\"\n8) \"0.5\"","section_id":"vector-similarity-search"},{"id":"vector-similarity-search-ex12","language":"csharp","code":"VectorSetSimilaritySearchRequest qOpts = VectorSetSimilaritySearchRequest.ByMember(\"pt:A\");\n        qOpts.WithScores = true;\n        qOpts.Count = 4;\n        using (Lease<VectorSetSimilaritySearchResult>? res = db.VectorSetSimilaritySearch(\"points\", qOpts))\n        {\n            VectorSetSimilaritySearchResult[] items = res!.Span.ToArray();\n            Dictionary<string, double> dict = items\n                .Select(i => new { Key = (string?)i.Member, i.Score })\n                .Where(x => x.Key is not null)\n                .ToDictionary(x => x.Key!, x => x.Score);\n            Console.WriteLine(\"{\" + string.Join(\", \", dict.Select(kv => $\"'{kv.Key}': {kv.Value}\")) + \"}\");\n            // >>> {'pt:A': 1.0, 'pt:E': 0.8535534143447876, 'pt:D': 0.5, 'pt:C': 0.5}\n        }","section_id":"vector-similarity-search"},{"id":"vector-similarity-search-ex13","language":"go","code":"res23, err := rdb.VSimWithArgsWithScores(\n\t\tctx,\n\t\t\"points\",\n\t\t&redis.VectorRef{Name: \"pt:A\"},\n\t\t&redis.VSimArgs{Count: 4},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tsort.Slice(res23, func(i, j int) bool {\n\t\treturn res23[i].Name < res23[j].Name\n\t})\n\n\tfmt.Println(res23)\n\t// >>> [{pt:A 1} {pt:C 0.5} {pt:D 0.5} {pt:E 0.8535534143447876}]","section_id":"vector-similarity-search"},{"id":"vector-similarity-search-ex14","language":"java","code":"VSimArgs vsimArgs = new VSimArgs();\n            vsimArgs.count(4L);\n            CompletableFuture<Void> similaritySearchWithScore = asyncCommands.vsimWithScore(\"points\", vsimArgs, \"pt:A\")\n                    .thenAccept(result -> {\n                        System.out.println(result); // >>> {pt:A=1.0, pt:E=0.8535534143447876, pt:D=0.5, pt:C=0.5}\n                    }).toCompletableFuture();","section_id":"vector-similarity-search"},{"id":"vector-similarity-search-ex15","language":"java","code":"VSimArgs vsimArgs = new VSimArgs();\n            vsimArgs.count(4L);\n            Mono<java.util.Map<String, Double>> similaritySearchWithScore = reactiveCommands\n                    .vsimWithScore(\"points\", vsimArgs, \"pt:A\").doOnNext(result -> {\n                        System.out.println(result); // >>> {pt:A=1.0, pt:E=0.8535534143447876, pt:D=0.5, pt:C=0.5}\n                    });","section_id":"vector-similarity-search"},{"id":"vector-similarity-search-ex16","language":"java","code":"Map<String, Double> res23 = jedis.vsimByElementWithScores(\"points\", \"pt:A\",\n        new VSimParams().count(4));\n      System.out.println(res23);\n      // >>> {pt:A=1.0, pt:E≈0.85355, pt:D=0.5, pt:C=0.5}","section_id":"vector-similarity-search"},{"id":"vector-similarity-search-ex17","language":"javascript","code":"const res23 = await client.vSimWithScores(\"points\", \"pt:A\", { COUNT: 4 });\nconsole.log(res23);\n// >>> {pt:A: 1.0, pt:E: 0.8535534143447876, pt:D: 0.5, pt:C: 0.5}","section_id":"vector-similarity-search"},{"id":"vector-similarity-search-ex18","language":"php","code":"// Returns an array of elements with their scores:\n        //      ['pt:A' => 1.0, 'pt:E' => 0.8535534143447876,...\n        $res23 = $r->vsim(\n            'points', 'pt:A', true,true, 4,\n        );\n        echo json_encode($res23) . PHP_EOL;\n        // >>> {'pt:A': 1.0, 'pt:E': 0.8535534143447876, 'pt:D': 0.5, 'pt:C': 0.5}","section_id":"vector-similarity-search"},{"id":"vector-similarity-search-ex19","language":"python","code":"res23 = r.vset().vsim(\n    \"points\", \"pt:A\",\n    with_scores=True,\n    count=4\n)\nprint(res23)\n# >>> {'pt:A': 1.0, 'pt:E': 0.8535534143447876, 'pt:D': 0.5, 'pt:C': 0.5}","section_id":"vector-similarity-search"},{"id":"vector-similarity-search-ex20","language":"rust","code":"let opts = VSimOptions::default().set_with_scores(true).set_count(4);\n        if let Ok(res) = r\n            .vsim_options(\"points\", VectorSimilaritySearchInput::Element(\"pt:A\"), &opts)\n            .await\n        {\n            let res: Value = res;\n            let mut view: Vec<(String, f64)> = match res {\n                Value::Array(items) => items\n                    .chunks(2)\n                    .map(|chunk| {\n                        let member = match &chunk[0] {\n                            Value::BulkString(bytes) => {\n                                String::from_utf8(bytes.clone()).expect(\"utf8\")\n                            }\n                            Value::SimpleString(s) => s.clone(),\n                            other => panic!(\"Unexpected member format: {other:?}\"),\n                        };\n                        let score = match &chunk[1] {\n                            Value::BulkString(bytes) => String::from_utf8(bytes.clone())\n                                .expect(\"utf8\")\n                                .parse::<f64>()\n                                .expect(\"score\"),\n                            Value::SimpleString(s) => s.parse::<f64>().expect(\"score\"),\n                            Value::Double(v) => *v,\n                            Value::Int(v) => *v as f64,\n                            other => panic!(\"Unexpected score format: {other:?}\"),\n                        };\n                        (member, score)\n                    })\n                    .collect(),\n                Value::Map(items) => items\n                    .into_iter()\n                    .map(|(member, score)| {\n                        let member = match member {\n                            Value::BulkString(bytes) => String::from_utf8(bytes).expect(\"utf8\"),\n                            Value::SimpleString(s) => s,\n                            other => panic!(\"Unexpected member format: {other:?}\"),\n                        };\n                        let score = match score {\n                            Value::BulkString(bytes) => String::from_utf8(bytes)\n                                .expect(\"utf8\")\n                                .parse::<f64>()\n                                .expect(\"score\"),\n                            Value::SimpleString(s) => s.parse::<f64>().expect(\"score\"),\n                            Value::Double(v) => v,\n                            Value::Int(v) => v as f64,\n                            other => panic!(\"Unexpected score format: {other:?}\"),\n                        };\n                        (member, score)\n                    })\n                    .collect(),\n                other => panic!(\"Unexpected VSIM WITHSCORES response: {other:?}\"),\n            };\n            view.sort_by(|a, b| {\n                b.1.partial_cmp(&a.1)\n                    .unwrap_or(std::cmp::Ordering::Equal)\n                    .then_with(|| b.0.cmp(&a.0))\n            });\n            println!(\"{view:?}\");\n            // >>> [(\"pt:A\", 1.0), (\"pt:E\", 0.8535534143447876), (\"pt:D\", 0.5), (\"pt:C\", 0.5)]\n        }","section_id":"vector-similarity-search"},{"id":"vector-similarity-search-ex21","language":"rust","code":"let opts = VSimOptions::default().set_with_scores(true).set_count(4);\n        if let Ok(res) =\n            r.vsim_options(\"points\", VectorSimilaritySearchInput::Element(\"pt:A\"), &opts)\n        {\n            let res: Value = res;\n            let mut view: Vec<(String, f64)> = match res {\n                Value::Array(items) => items\n                    .chunks(2)\n                    .map(|chunk| {\n                        let member = match &chunk[0] {\n                            Value::BulkString(bytes) => {\n                                String::from_utf8(bytes.clone()).expect(\"utf8\")\n                            }\n                            Value::SimpleString(s) => s.clone(),\n                            other => panic!(\"Unexpected member format: {other:?}\"),\n                        };\n                        let score = match &chunk[1] {\n                            Value::BulkString(bytes) => String::from_utf8(bytes.clone())\n                                .expect(\"utf8\")\n                                .parse::<f64>()\n                                .expect(\"score\"),\n                            Value::SimpleString(s) => s.parse::<f64>().expect(\"score\"),\n                            Value::Double(v) => *v,\n                            Value::Int(v) => *v as f64,\n                            other => panic!(\"Unexpected score format: {other:?}\"),\n                        };\n                        (member, score)\n                    })\n                    .collect(),\n                Value::Map(items) => items\n                    .into_iter()\n                    .map(|(member, score)| {\n                        let member = match member {\n                            Value::BulkString(bytes) => String::from_utf8(bytes).expect(\"utf8\"),\n                            Value::SimpleString(s) => s,\n                            other => panic!(\"Unexpected member format: {other:?}\"),\n                        };\n                        let score = match score {\n                            Value::BulkString(bytes) => String::from_utf8(bytes)\n                                .expect(\"utf8\")\n                                .parse::<f64>()\n                                .expect(\"score\"),\n                            Value::SimpleString(s) => s.parse::<f64>().expect(\"score\"),\n                            Value::Double(v) => v,\n                            Value::Int(v) => v as f64,\n                            other => panic!(\"Unexpected score format: {other:?}\"),\n                        };\n                        (member, score)\n                    })\n                    .collect(),\n                other => panic!(\"Unexpected VSIM WITHSCORES response: {other:?}\"),\n            };\n            view.sort_by(|a, b| {\n                b.1.partial_cmp(&a.1)\n                    .unwrap_or(std::cmp::Ordering::Equal)\n                    .then_with(|| b.0.cmp(&a.0))\n            });\n            println!(\"{view:?}\");\n            // >>> [(\"pt:A\", 1.0), (\"pt:E\", 0.8535534143447876), (\"pt:D\", 0.5), (\"pt:C\", 0.5)]\n        }","section_id":"vector-similarity-search"},{"id":"vector-similarity-search-ex22","language":"plaintext","code":"> VSETATTR points pt:A \"{\\\"size\\\":\\\"large\\\",\\\"price\\\": 18.99}\"\n(integer) 1\n> VSETATTR points pt:B \"{\\\"size\\\":\\\"large\\\",\\\"price\\\": 35.99}\"\n(integer) 1\n> VSETATTR points pt:C \"{\\\"size\\\":\\\"large\\\",\\\"price\\\": 25.99}\"\n(integer) 1\n> VSETATTR points pt:D \"{\\\"size\\\":\\\"small\\\",\\\"price\\\": 21.00}\"\n(integer) 1\n> VSETATTR points pt:E \"{\\\"size\\\":\\\"small\\\",\\\"price\\\": 17.75}\"\n(integer) 1\n\n# Return elements in order of distance from point A whose\n# `size` attribute is `large`.\n> VSIM points ELE pt:A FILTER '.size == \"large\"'\n1) \"pt:A\"\n2) \"pt:C\"\n3) \"pt:B\"\n\n# Return elements in order of distance from point A whose size is\n# `large` and whose price is greater than 20.00.\n> VSIM points ELE pt:A FILTER '.size == \"large\" && .price > 20.00'\n1) \"pt:C\"\n2) \"pt:B\"","section_id":"vector-similarity-search"},{"id":"vector-similarity-search-ex23","language":"csharp","code":"bool okA = db.VectorSetSetAttributesJson(\"points\", \"pt:A\", \"{\\\"size\\\":\\\"large\\\",\\\"price\\\":18.99}\");\n        Console.WriteLine(okA); // >>> True\n        bool okB = db.VectorSetSetAttributesJson(\"points\", \"pt:B\", \"{\\\"size\\\":\\\"large\\\",\\\"price\\\":35.99}\");\n        Console.WriteLine(okB); // >>> True\n        bool okC = db.VectorSetSetAttributesJson(\"points\", \"pt:C\", \"{\\\"size\\\":\\\"large\\\",\\\"price\\\":25.99}\");\n        Console.WriteLine(okC); // >>> True\n        bool okD = db.VectorSetSetAttributesJson(\"points\", \"pt:D\", \"{\\\"size\\\":\\\"small\\\",\\\"price\\\":21.00}\");\n        Console.WriteLine(okD); // >>> True\n        bool okE = db.VectorSetSetAttributesJson(\"points\", \"pt:E\", \"{\\\"size\\\":\\\"small\\\",\\\"price\\\":17.75}\");\n        Console.WriteLine(okE); // >>> True\n\n        VectorSetSimilaritySearchRequest qFilt1 = VectorSetSimilaritySearchRequest.ByMember(\"pt:A\");\n        qFilt1.FilterExpression = \".size == \\\"large\\\"\";\n        using (Lease<VectorSetSimilaritySearchResult>? res = db.VectorSetSimilaritySearch(\"points\", qFilt1))\n        {\n            string[] ids = res!.Span.ToArray()\n                .Select(i => (string?)i.Member)\n                .Where(s => s is not null)\n                .Select(s => s!)\n                .ToArray();\n            Console.WriteLine(\"[\" + string.Join(\", \", ids.Select(s => $\"'{s}'\")) + \"]\");\n            // >>> ['pt:A', 'pt:C', 'pt:B']\n\n        }\n\n        VectorSetSimilaritySearchRequest qFilt2 = VectorSetSimilaritySearchRequest.ByMember(\"pt:A\");\n        qFilt2.FilterExpression = \".size == \\\"large\\\" && .price > 20.00\";\n        using (Lease<VectorSetSimilaritySearchResult>? res = db.VectorSetSimilaritySearch(\"points\", qFilt2))\n        {\n            string[] ids = res!.Span.ToArray()\n                .Select(i => (string?)i.Member)\n                .Where(s => s is not null)\n                .Select(s => s!)\n                .ToArray();\n            Console.WriteLine(\"[\" + string.Join(\", \", ids.Select(s => $\"'{s}'\")) + \"]\");\n            // >>> ['pt:C', 'pt:B']\n        }","section_id":"vector-similarity-search"},{"id":"vector-similarity-search-ex24","language":"go","code":"// Set attributes for filtering\n\tres24, err := rdb.VSetAttr(ctx, \"points\", \"pt:A\",\n\t\tmap[string]interface{}{\n\t\t\t\"size\":  \"large\",\n\t\t\t\"price\": 18.99,\n\t\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res24) // >>> true\n\n\tres25, err := rdb.VSetAttr(ctx, \"points\", \"pt:B\",\n\t\tmap[string]interface{}{\n\t\t\t\"size\":  \"large\",\n\t\t\t\"price\": 35.99,\n\t\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res25) // >>> true\n\n\tres26, err := rdb.VSetAttr(ctx, \"points\", \"pt:C\",\n\t\tmap[string]interface{}{\n\t\t\t\"size\":  \"large\",\n\t\t\t\"price\": 25.99,\n\t\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res26) // >>> true\n\n\tres27, err := rdb.VSetAttr(ctx, \"points\", \"pt:D\",\n\t\tmap[string]interface{}{\n\t\t\t\"size\":  \"small\",\n\t\t\t\"price\": 21.00,\n\t\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res27) // >>> true\n\n\tres28, err := rdb.VSetAttr(ctx, \"points\", \"pt:E\",\n\t\tmap[string]interface{}{\n\t\t\t\"size\":  \"small\",\n\t\t\t\"price\": 17.75,\n\t\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res28) // >>> true\n\n\t// Return elements in order of distance from point A whose\n\t// `size` attribute is `large`.\n\tres29, err := rdb.VSimWithArgs(ctx, \"points\",\n\t\t&redis.VectorRef{Name: \"pt:A\"},\n\t\t&redis.VSimArgs{Filter: `.size == \"large\"`},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res29) // >>> [pt:A pt:C pt:B]\n\n\t// Return elements in order of distance from point A whose size is\n\t// `large` and whose price is greater than 20.00.\n\tres30, err := rdb.VSimWithArgs(ctx, \"points\",\n\t\t&redis.VectorRef{Name: \"pt:A\"},\n\t\t&redis.VSimArgs{Filter: `.size == \"large\" && .price > 20.00`},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res30) // >>> [pt:C pt:B]\n\n\tres31, err := rdb.VIsMember(ctx, \"points\", \"pt:A\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res31) // >>> true\n\n\tres32, err := rdb.VIsMember(ctx, \"points\", \"pt:W\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res32) // >>> false\n\n\tres33, err := rdb.VSimWithArgsWithAttribs(ctx, \"points\",\n\t\t&redis.VectorRef{Name: \"pt:A\"},\n\t\t&redis.VSimArgs{Count: 1, Truth: true},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res33[0].Name, *res33[0].Attribs) // >>> pt:A {\"price\":18.99,\"size\":\"large\"}\n\n\tres34, err := rdb.VSimWithArgsWithScoresWithAttribs(ctx, \"points\",\n\t\t&redis.VectorRef{Name: \"pt:A\"},\n\t\t&redis.VSimArgs{Count: 1, Truth: true},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res34[0].Name, res34[0].Score, *res34[0].Attribs) // >>> pt:A 1 {\"price\":18.99,\"size\":\"large\"}","section_id":"vector-similarity-search"},{"id":"vector-similarity-search-ex25","language":"java","code":"CompletableFuture<Void> filteredSimilaritySearch = asyncCommands\n                    .vsetattr(\"points\", \"pt:A\", \"{\\\"size\\\": \\\"large\\\", \\\"price\\\": 18.99}\").thenCompose(result -> {\n                        System.out.println(result); // >>> true\n                        return asyncCommands.vsetattr(\"points\", \"pt:B\", \"{\\\"size\\\": \\\"large\\\", \\\"price\\\": 35.99}\");\n                    }).thenCompose(result -> {\n                        System.out.println(result); // >>> true\n                        return asyncCommands.vsetattr(\"points\", \"pt:C\", \"{\\\"size\\\": \\\"large\\\", \\\"price\\\": 25.99}\");\n                    }).thenCompose(result -> {\n                        System.out.println(result); // >>> true\n                        return asyncCommands.vsetattr(\"points\", \"pt:D\", \"{\\\"size\\\": \\\"small\\\", \\\"price\\\": 21.00}\");\n                    }).thenCompose(result -> {\n                        System.out.println(result); // >>> true\n                        return asyncCommands.vsetattr(\"points\", \"pt:E\", \"{\\\"size\\\": \\\"small\\\", \\\"price\\\": 17.75}\");\n                    }).thenCompose(result -> {\n                        System.out.println(result); // >>> true\n\n                        // Return elements in order of distance from point A whose size attribute is large.\n                        VSimArgs filterArgs = new VSimArgs();\n                        filterArgs.filter(\".size == \\\"large\\\"\");\n                        return asyncCommands.vsim(\"points\", filterArgs, \"pt:A\");\n                    }).thenCompose(result -> {\n                        System.out.println(result); // >>> [pt:A, pt:C, pt:B]\n\n                        // Return elements in order of distance from point A whose size is large and price > 20.00.\n                        VSimArgs filterArgs2 = new VSimArgs();\n                        filterArgs2.filter(\".size == \\\"large\\\" && .price > 20.00\");\n                        return asyncCommands.vsim(\"points\", filterArgs2, \"pt:A\");\n                    }).thenAccept(result -> {\n                        System.out.println(result); // >>> [pt:C, pt:B]\n                    }).toCompletableFuture();","section_id":"vector-similarity-search"},{"id":"vector-similarity-search-ex26","language":"java","code":"Mono<Void> filteredSimilaritySearch = reactiveCommands\n                    .vsetattr(\"points\", \"pt:A\", \"{\\\"size\\\": \\\"large\\\", \\\"price\\\": 18.99}\").doOnNext(result -> {\n                        System.out.println(result); // >>> true\n                    }).flatMap(result -> reactiveCommands.vsetattr(\"points\", \"pt:B\", \"{\\\"size\\\": \\\"large\\\", \\\"price\\\": 35.99}\"))\n                    .doOnNext(result -> {\n                        System.out.println(result); // >>> true\n                    }).flatMap(result -> reactiveCommands.vsetattr(\"points\", \"pt:C\", \"{\\\"size\\\": \\\"large\\\", \\\"price\\\": 25.99}\"))\n                    .doOnNext(result -> {\n                        System.out.println(result); // >>> true\n                    }).flatMap(result -> reactiveCommands.vsetattr(\"points\", \"pt:D\", \"{\\\"size\\\": \\\"small\\\", \\\"price\\\": 21.00}\"))\n                    .doOnNext(result -> {\n                        System.out.println(result); // >>> true\n                    }).flatMap(result -> reactiveCommands.vsetattr(\"points\", \"pt:E\", \"{\\\"size\\\": \\\"small\\\", \\\"price\\\": 17.75}\"))\n                    .doOnNext(result -> {\n                        System.out.println(result); // >>> true\n                    }).flatMap(result -> {\n                        // Return elements in order of distance from point A whose size attribute is large.\n                        VSimArgs filterArgs = new VSimArgs();\n                        filterArgs.filter(\".size == \\\"large\\\"\");\n                        return reactiveCommands.vsim(\"points\", filterArgs, \"pt:A\").collectList();\n                    }).doOnNext(result -> {\n                        System.out.println(result); // >>> [pt:A, pt:C, pt:B]\n                    }).flatMap(result -> {\n                        // Return elements in order of distance from point A whose size is large and price > 20.00.\n                        VSimArgs filterArgs2 = new VSimArgs();\n                        filterArgs2.filter(\".size == \\\"large\\\" && .price > 20.00\");\n                        return reactiveCommands.vsim(\"points\", filterArgs2, \"pt:A\").collectList();\n                    }).doOnNext(result -> {\n                        System.out.println(result); // >>> [pt:C, pt:B]\n                    }).then();","section_id":"vector-similarity-search"},{"id":"vector-similarity-search-ex27","language":"java","code":"boolean res24 = jedis.vsetattr(\"points\", \"pt:A\", \"{\\\"size\\\":\\\"large\\\",\\\"price\\\":18.99}\");\n      System.out.println(res24); // >>> true\n\n      boolean res25 = jedis.vsetattr(\"points\", \"pt:B\", \"{\\\"size\\\":\\\"large\\\",\\\"price\\\":35.99}\");\n      System.out.println(res25); // >>> true\n\n      boolean res26 = jedis.vsetattr(\"points\", \"pt:C\", \"{\\\"size\\\":\\\"large\\\",\\\"price\\\":25.99}\");\n      System.out.println(res26); // >>> true\n\n      boolean res27 = jedis.vsetattr(\"points\", \"pt:D\", \"{\\\"size\\\":\\\"small\\\",\\\"price\\\":21.00}\");\n      System.out.println(res27); // >>> true\n\n      boolean res28 = jedis.vsetattr(\"points\", \"pt:E\", \"{\\\"size\\\":\\\"small\\\",\\\"price\\\":17.75}\");\n      System.out.println(res28); // >>> true\n\n      List<String> res29 = jedis.vsimByElement(\"points\", \"pt:A\",\n        new VSimParams().filter(\".size == \\\"large\\\"\"));\n      System.out.println(res29); // >>> [\"pt:A\", \"pt:C\", \"pt:B\"]\n\n      List<String> res30 = jedis.vsimByElement(\"points\", \"pt:A\",\n        new VSimParams().filter(\".size == \\\"large\\\" && .price > 20.00\"));\n      System.out.println(res30); // >>> [\"pt:C\", \"pt:B\"]","section_id":"vector-similarity-search"},{"id":"vector-similarity-search-ex28","language":"javascript","code":"const res24 = await client.vSetAttr(\"points\", \"pt:A\", {\n  size: \"large\",\n  price: 18.99\n});\nconsole.log(res24);  // >>> true\n\nconst res25 = await client.vSetAttr(\"points\", \"pt:B\", {\n  size: \"large\",\n  price: 35.99\n});\nconsole.log(res25);  // >>> true\n\nconst res26 = await client.vSetAttr(\"points\", \"pt:C\", {\n  size: \"large\",\n  price: 25.99\n});\nconsole.log(res26);  // >>> true\n\nconst res27 = await client.vSetAttr(\"points\", \"pt:D\", {\n  size: \"small\",\n  price: 21.00\n});\nconsole.log(res27);  // >>> true\n\nconst res28 = await client.vSetAttr(\"points\", \"pt:E\", {\n  size: \"small\",\n  price: 17.75\n});\nconsole.log(res28);  // >>> true\n\n// Return elements in order of distance from point A whose\n// `size` attribute is `large`.\nconst res29 = await client.vSim(\"points\", \"pt:A\", {\n  FILTER: '.size == \"large\"'\n});\nconsole.log(res29);  // >>> ['pt:A', 'pt:C', 'pt:B']\n\n// Return elements in order of distance from point A whose size is\n// `large` and whose price is greater than 20.00.\nconst res30 = await client.vSim(\"points\", \"pt:A\", {\n  FILTER: '.size == \"large\" && .price > 20.00'\n});\nconsole.log(res30);  // >>> ['pt:C', 'pt:B']","section_id":"vector-similarity-search"},{"id":"vector-similarity-search-ex29","language":"php","code":"$res24 = $r->vsetattr('points', 'pt:A', [\n            'size' => 'large',\n            'price' => 18.99\n        ]);\n        echo $res24 . PHP_EOL;\n        // >>> 1\n\n        $res25 = $r->vsetattr('points', 'pt:B', [\n            'size' => 'large',\n            'price' => 35.99\n        ]);\n        echo $res25 . PHP_EOL;\n        // >>> 1\n\n        $res26 = $r->vsetattr('points', 'pt:C', [\n            'size' => 'large',\n            'price' => 25.99\n        ]);\n        echo $res26 . PHP_EOL;\n        // >>> 1\n\n        $res27 = $r->vsetattr('points', 'pt:D', [\n            'size' => 'small',\n            'price' => 21.00\n        ]);\n        echo $res27 . PHP_EOL;\n        // >>> 1\n\n        $res28 = $r->vsetattr('points', 'pt:E', [\n            'size' => 'small',\n            'price' => 17.75\n        ]);\n        echo $res28 . PHP_EOL;\n        // >>> 1\n\n        // Return elements in order of distance from point A whose\n        // `size` attribute is `large`.\n        $res29 = $r->vsim(\n            'points', 'pt:A',true, null, null, null, null,\n            '.size == \"large\"'\n        );\n        echo json_encode($res29) . PHP_EOL;\n        // >>> [\"pt:A\",\"pt:C\",\"pt:B\"]\n\n        // Return elements in order of distance from point A whose size is\n        // `large` and whose price is greater than 20.00.\n        $res30 = $r->vsim(\n            'points', 'pt:A',true, null, null, null, null,\n            '.size == \"large\" && .price > 20.00'\n        );\n        echo json_encode($res30) . PHP_EOL;\n        // >>> [\"pt:C\",\"pt:B\"]","section_id":"vector-similarity-search"},{"id":"vector-similarity-search-ex30","language":"python","code":"res24 = r.vset().vsetattr(\"points\", \"pt:A\", {\n    \"size\": \"large\",\n    \"price\": 18.99\n})\nprint(res24)  # >>> 1\n\nres25 = r.vset().vsetattr(\"points\", \"pt:B\", {\n    \"size\": \"large\",\n    \"price\": 35.99\n})\nprint(res25)  # >>> 1\n\nres26 = r.vset().vsetattr(\"points\", \"pt:C\", {\n    \"size\": \"large\",\n    \"price\": 25.99\n})\nprint(res26)  # >>> 1\n\nres27 = r.vset().vsetattr(\"points\", \"pt:D\", {\n    \"size\": \"small\",\n    \"price\": 21.00\n})\nprint(res27)  # >>> 1\n\nres28 = r.vset().vsetattr(\"points\", \"pt:E\", {\n    \"size\": \"small\",\n    \"price\": 17.75\n})\nprint(res28)  # >>> 1\n\n# Return elements in order of distance from point A whose\n# `size` attribute is `large`.\nres29 = r.vset().vsim(\n    \"points\", \"pt:A\",\n    filter='.size == \"large\"'\n)\nprint(res29)  # >>> ['pt:A', 'pt:C', 'pt:B']\n\n# Return elements in order of distance from point A whose size is\n# `large` and whose price is greater than 20.00.\nres30 = r.vset().vsim(\n    \"points\", \"pt:A\",\n    filter='.size == \"large\" && .price > 20.00'\n)\nprint(res30)  # >>> ['pt:C', 'pt:B']","section_id":"vector-similarity-search"},{"id":"vector-similarity-search-ex31","language":"rust","code":"let res: bool = cmd(\"VSETATTR\")\n            .arg(\"points\")\n            .arg(\"pt:A\")\n            .arg(\"{\\\"size\\\":\\\"large\\\",\\\"price\\\":18.99}\")\n            .query_async(&mut r)\n            .await\n            .expect(\"VSETATTR A should succeed\");\n        println!(\"{res}\"); // >>> true\n\n        let res: bool = cmd(\"VSETATTR\")\n            .arg(\"points\")\n            .arg(\"pt:B\")\n            .arg(\"{\\\"size\\\":\\\"large\\\",\\\"price\\\":35.99}\")\n            .query_async(&mut r)\n            .await\n            .expect(\"VSETATTR B should succeed\");\n        println!(\"{res}\"); // >>> true\n\n        let res: bool = cmd(\"VSETATTR\")\n            .arg(\"points\")\n            .arg(\"pt:C\")\n            .arg(\"{\\\"size\\\":\\\"large\\\",\\\"price\\\":25.99}\")\n            .query_async(&mut r)\n            .await\n            .expect(\"VSETATTR C should succeed\");\n        println!(\"{res}\"); // >>> true\n\n        let res: bool = cmd(\"VSETATTR\")\n            .arg(\"points\")\n            .arg(\"pt:D\")\n            .arg(\"{\\\"size\\\":\\\"small\\\",\\\"price\\\":21.00}\")\n            .query_async(&mut r)\n            .await\n            .expect(\"VSETATTR D should succeed\");\n        println!(\"{res}\"); // >>> true\n\n        let res: bool = cmd(\"VSETATTR\")\n            .arg(\"points\")\n            .arg(\"pt:E\")\n            .arg(\"{\\\"size\\\":\\\"small\\\",\\\"price\\\":17.75}\")\n            .query_async(&mut r)\n            .await\n            .expect(\"VSETATTR E should succeed\");\n        println!(\"{res}\"); // >>> true\n\n        let opts = VSimOptions::default().set_filter_expression(\".size == \\\"large\\\"\");\n        if let Ok(res) = r\n            .vsim_options(\"points\", VectorSimilaritySearchInput::Element(\"pt:A\"), &opts)\n            .await\n        {\n            let res: Value = res;\n            if let Value::Array(items) = res {\n                let view: Vec<String> = items\n                    .into_iter()\n                    .map(|item| match item {\n                        Value::BulkString(bytes) => String::from_utf8(bytes).expect(\"utf8\"),\n                        Value::SimpleString(s) => s,\n                        other => panic!(\"Unexpected VSIM item: {other:?}\"),\n                    })\n                    .collect();\n                println!(\"{view:?}\");\n                // >>> [\"pt:A\", \"pt:C\", \"pt:B\"]\n            }\n        }\n\n        let opts = VSimOptions::default()\n            .set_filter_expression(\".size == \\\"large\\\" && .price > 20.00\");\n        if let Ok(res) = r\n            .vsim_options(\"points\", VectorSimilaritySearchInput::Element(\"pt:A\"), &opts)\n            .await\n        {\n            let res: Value = res;\n            if let Value::Array(items) = res {\n                let view: Vec<String> = items\n                    .into_iter()\n                    .map(|item| match item {\n                        Value::BulkString(bytes) => String::from_utf8(bytes).expect(\"utf8\"),\n                        Value::SimpleString(s) => s,\n                        other => panic!(\"Unexpected VSIM item: {other:?}\"),\n                    })\n                    .collect();\n                println!(\"{view:?}\");\n                // >>> [\"pt:C\", \"pt:B\"]\n            }\n        }","section_id":"vector-similarity-search"},{"id":"vector-similarity-search-ex32","language":"rust","code":"let res: bool = cmd(\"VSETATTR\")\n            .arg(\"points\")\n            .arg(\"pt:A\")\n            .arg(\"{\\\"size\\\":\\\"large\\\",\\\"price\\\":18.99}\")\n            .query(&mut r)\n            .expect(\"VSETATTR A should succeed\");\n        println!(\"{res}\"); // >>> true\n\n        let res: bool = cmd(\"VSETATTR\")\n            .arg(\"points\")\n            .arg(\"pt:B\")\n            .arg(\"{\\\"size\\\":\\\"large\\\",\\\"price\\\":35.99}\")\n            .query(&mut r)\n            .expect(\"VSETATTR B should succeed\");\n        println!(\"{res}\"); // >>> true\n\n        let res: bool = cmd(\"VSETATTR\")\n            .arg(\"points\")\n            .arg(\"pt:C\")\n            .arg(\"{\\\"size\\\":\\\"large\\\",\\\"price\\\":25.99}\")\n            .query(&mut r)\n            .expect(\"VSETATTR C should succeed\");\n        println!(\"{res}\"); // >>> true\n\n        let res: bool = cmd(\"VSETATTR\")\n            .arg(\"points\")\n            .arg(\"pt:D\")\n            .arg(\"{\\\"size\\\":\\\"small\\\",\\\"price\\\":21.00}\")\n            .query(&mut r)\n            .expect(\"VSETATTR D should succeed\");\n        println!(\"{res}\"); // >>> true\n\n        let res: bool = cmd(\"VSETATTR\")\n            .arg(\"points\")\n            .arg(\"pt:E\")\n            .arg(\"{\\\"size\\\":\\\"small\\\",\\\"price\\\":17.75}\")\n            .query(&mut r)\n            .expect(\"VSETATTR E should succeed\");\n        println!(\"{res}\"); // >>> true\n\n        let opts = VSimOptions::default().set_filter_expression(\".size == \\\"large\\\"\");\n        if let Ok(res) =\n            r.vsim_options(\"points\", VectorSimilaritySearchInput::Element(\"pt:A\"), &opts)\n        {\n            let res: Value = res;\n            if let Value::Array(items) = res {\n                let view: Vec<String> = items\n                    .into_iter()\n                    .map(|item| match item {\n                        Value::BulkString(bytes) => String::from_utf8(bytes).expect(\"utf8\"),\n                        Value::SimpleString(s) => s,\n                        other => panic!(\"Unexpected VSIM item: {other:?}\"),\n                    })\n                    .collect();\n                println!(\"{view:?}\");\n                // >>> [\"pt:A\", \"pt:C\", \"pt:B\"]\n            }\n        }\n\n        let opts = VSimOptions::default()\n            .set_filter_expression(\".size == \\\"large\\\" && .price > 20.00\");\n        if let Ok(res) =\n            r.vsim_options(\"points\", VectorSimilaritySearchInput::Element(\"pt:A\"), &opts)\n        {\n            let res: Value = res;\n            if let Value::Array(items) = res {\n                let view: Vec<String> = items\n                    .into_iter()\n                    .map(|item| match item {\n                        Value::BulkString(bytes) => String::from_utf8(bytes).expect(\"utf8\"),\n                        Value::SimpleString(s) => s,\n                        other => panic!(\"Unexpected VSIM item: {other:?}\"),\n                    })\n                    .collect();\n                println!(\"{view:?}\");\n                // >>> [\"pt:C\", \"pt:B\"]\n            }\n        }","section_id":"vector-similarity-search"}]}
{"schema_version":2,"id":"develop/data-types/vector-sets/memory","title":"Memory optimization","url":"https://redis.io/docs/latest/develop/data-types/vector-sets/memory/","summary":"Learn how to optimize memory consumption in Redis vector sets","tags":["docs","develop","stack","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-10T15:45:48+01:00","page_type":"content","content_hash":"3344f2d173b588d271f5f98df9702c4d43a02bc45f8d40aa399a1b7a886492da","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis vector sets are efficient, but vector similarity indexing and graph traversal require memory tradeoffs. This guide helps you manage memory use through quantization, graph tuning, and attribute choices."},{"id":"quantization-modes","title":"Quantization modes","role":"content","text":"Vector sets support three quantization levels:\n\n| Mode       | Memory usage  | Recall | Notes                           |\n|------------|---------------|--------|---------------------------------|\n| `Q8`       | 4x smaller    | High   | Default, fast and accurate      |\n| `BIN`      | 32x smaller   | Lower  | Fastest, best for coarse search |\n| `NOQUANT`  | Full size     | Highest| Best precision, slowest         |\n\nUse `Q8` unless your use case demands either ultra-precision (use `NOQUANT`) or ultra-efficiency (use `BIN`)."},{"id":"graph-structure-memory","title":"Graph structure memory","role":"content","text":"HNSW graphs store multiple connections per node. Each node:\n\n- Has an average of `M * 2 + M * 0.33` pointers (default M = 16).\n- Stores pointers using 8 bytes each.\n- Allocates ~1.33 layers per node.\n\n> A single node with M = 64 may consume ~1 KB in links alone.\n\nTo reduce memory:\n\n- Lower `M` to shrink per-node connections.\n- Avoid unnecessarily large values for `M` unless recall needs to be improved."},{"id":"attribute-and-label-size","title":"Attribute and label size","role":"content","text":"Each node stores:\n\n- A string label (element name)\n- Optional JSON attribute string\n\nTips:\n\n- Use short, fixed-length strings for labels.\n- Keep attribute JSON minimal and flat. For example, use `{\"year\":2020}` instead of nested data."},{"id":"vector-dimension","title":"Vector dimension","role":"content","text":"High-dimensional vectors increase storage:\n\n- 300 components at `FP32` = 1200 bytes/vector\n- 300 components at `Q8` = 300 bytes/vector\n\nYou can reduce this using the `REDUCE` option during [`VADD`](https://redis.io/docs/latest/commands/vadd), which applies [random projection](https://en.wikipedia.org/wiki/Random_projection):\n\nDimension reduction: Use the REDUCE option with VADD to apply random projection and reduce vector dimensions when you need to optimize memory usage while maintaining search quality\n\n**Difficulty:** Advanced\n\n**Commands:** VDIM\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nThis projects a 300-dimensional vector into 100 dimensions, reducing size and improving speed at the cost of some recall."},{"id":"summary","title":"Summary","role":"content","text":"| Strategy            | Effect                                   |\n|---------------------|------------------------------------------|\n| Use `Q8`            | Best tradeoff for most use cases         |\n| Use `BIN`           | Minimal memory, fastest search           |\n| Lower `M`           | Shrinks HNSW link graph size             |\n| Reduce dimensions   | Cuts memory per vector                   |\n| Minimize JSON       | Smaller attributes, less memory per node |"},{"id":"see-also","title":"See also","role":"related","text":"- [Performance](https://redis.io/docs/latest/develop/data-types/vector-sets/performance)\n- [Scalability](https://redis.io/docs/latest/develop/data-types/vector-sets/scalability)"}],"examples":[{"id":"vector-dimension-ex0","language":"plaintext","code":">VADD setNotReduced VALUES 300 ... element\n(integer) 1\n> VDIM setNotReduced\n(integer) 300\n\n>VADD setReduced REDUCE 100 VALUES 300 ... element\n(integer) 1\n> VDIM setReduced\n(integer) 100","section_id":"vector-dimension"},{"id":"vector-dimension-ex1","language":"csharp","code":"float[] values = Enumerable.Range(0, 300).Select(x => (float)(x / 299.0)).ToArray();\n        bool addedNotReduced = db.VectorSetAdd(\"setNotReduced\", VectorSetAddRequest.Member(\"element\", values, null));\n        Console.WriteLine(addedNotReduced); // >>> True\n        Console.WriteLine(db.VectorSetDimension(\"setNotReduced\")); // >>> 300\n\n        VectorSetAddRequest addReduced = VectorSetAddRequest.Member(\"element\", values, null);\n        addReduced.ReducedDimensions = 100;\n        bool addedReduced = db.VectorSetAdd(\"setReduced\", addReduced);\n        Console.WriteLine(addedReduced); // >>> True\n        Console.WriteLine(db.VectorSetDimension(\"setReduced\")); // >>> 100","section_id":"vector-dimension"},{"id":"vector-dimension-ex2","language":"go","code":"// Create a vector with 300 dimensions\n\tvalues := make([]float64, 300)\n\n\tfor i := 0; i < 300; i++ {\n\t\tvalues[i] = float64(i) / 299\n\t}\n\n\tvecLarge := &redis.VectorValues{Val: values}\n\n\t// Add without reduction\n\tres1, err := rdb.VAdd(ctx, \"setNotReduced\", \"element\", vecLarge).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> true\n\n\tdim1, err := rdb.VDim(ctx, \"setNotReduced\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Printf(\"Dimension without reduction: %d\\n\", dim1)\n\t// >>> Dimension without reduction: 300\n\n\t// Add with reduction to 100 dimensions\n\tres2, err := rdb.VAddWithArgs(ctx, \"setReduced\", \"element\", vecLarge,\n\t\t&redis.VAddArgs{\n\t\t\tReduce: 100,\n\t\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> true\n\n\tdim2, err := rdb.VDim(ctx, \"setReduced\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Printf(\"Dimension after reduction: %d\\n\", dim2)\n\t// >>> Dimension after reduction: 100","section_id":"vector-dimension"},{"id":"vector-dimension-ex3","language":"java","code":"// Create a list of 300 arbitrary values.\n            Double[] values = new Double[300];\n            for (int i = 0; i < 300; i++) {\n                values[i] = (double) i / 299;\n            }\n\n            CompletableFuture<Void> dimensionalityReductionOperations = asyncCommands.vadd(\"setNotReduced\", \"element\", values)\n                    .thenCompose(result -> {\n                        System.out.println(result); // >>> true\n                        return asyncCommands.vdim(\"setNotReduced\");\n                    }).thenCompose(result -> {\n                        System.out.println(result); // >>> 300\n                        return asyncCommands.vadd(\"setReduced\", 100, \"element\", values);\n                    }).thenCompose(result -> {\n                        System.out.println(result); // >>> true\n                        return asyncCommands.vdim(\"setReduced\");\n                    }).thenAccept(result -> {\n                        System.out.println(result); // >>> 100\n                    }).toCompletableFuture();","section_id":"vector-dimension"},{"id":"vector-dimension-ex4","language":"java","code":"// Create a list of 300 arbitrary values.\n            Double[] values = new Double[300];\n            for (int i = 0; i < 300; i++) {\n                values[i] = (double) i / 299;\n            }\n\n            Mono<Void> dimensionalityReductionOperations = reactiveCommands.vadd(\"setNotReduced\", \"element\", values)\n                    .doOnNext(result -> {\n                        System.out.println(result); // >>> true\n                    }).flatMap(result -> reactiveCommands.vdim(\"setNotReduced\")).doOnNext(result -> {\n                        System.out.println(result); // >>> 300\n                    }).flatMap(result -> reactiveCommands.vadd(\"setReduced\", 100, \"element\", values)).doOnNext(result -> {\n                        System.out.println(result); // >>> true\n                    }).flatMap(result -> reactiveCommands.vdim(\"setReduced\")).doOnNext(result -> {\n                        System.out.println(result); // >>> 100\n                    }).then();","section_id":"vector-dimension"},{"id":"vector-dimension-ex5","language":"java","code":"float[] values = new float[300];\n      for (int i = 0; i < 300; i++)\n        values[i] = i / 299.0f;\n\n      boolean res37 = jedis.vadd(\"setNotReduced\", values, \"element\");\n      System.out.println(res37); // >>> true\n\n      long res38 = jedis.vdim(\"setNotReduced\");\n      System.out.println(res38); // >>> 300\n\n      boolean res39 = jedis.vadd(\"setReduced\", values, \"element\", 100, new VAddParams());\n      System.out.println(res39); // >>> true\n\n      long res40 = jedis.vdim(\"setReduced\");\n      System.out.println(res40); // >>> 100","section_id":"vector-dimension"},{"id":"vector-dimension-ex6","language":"javascript","code":"// Create a list of 300 arbitrary values.\nconst values = Array.from({length: 300}, (_, x) => x / 299);\n\nconst res37 = await client.vAdd(\"setNotReduced\", values, \"element\");\nconsole.log(res37);  // >>> true\n\nconst res38 = await client.vDim(\"setNotReduced\");\nconsole.log(res38);  // >>> 300\n\nconst res39 = await client.vAdd(\"setReduced\", values, \"element\", {\n  REDUCE: 100\n});\nconsole.log(res39);  // >>> true\n\nconst res40 = await client.vDim(\"setReduced\");\nconsole.log(res40);  // >>> 100","section_id":"vector-dimension"},{"id":"vector-dimension-ex7","language":"php","code":"$values = array();\n\n        for ($i = 0; $i < 300; $i++) {\n            $values[] = $i / 299.0;\n        }\n\n        $res37 = $r->vadd('setNotReduced', $values, 'element');\n        echo $res37 . PHP_EOL;\n        // >>> 1\n\n        $res38 = $r->vdim('setNotReduced');\n        echo $res38 . PHP_EOL;\n        // >>> 300\n\n        $res39 = $r->vadd('setReduced', $values, 'element', 100);\n        echo $res39 . PHP_EOL;\n        // >>> 1\n\n        $res40 = $r->vdim('setReduced');\n        echo $res40 . PHP_EOL;\n        // >>> 100","section_id":"vector-dimension"},{"id":"vector-dimension-ex8","language":"python","code":"# Create a list of 300 arbitrary values.\nvalues = [x / 299 for x in range(300)]\n\nres37 = r.vset().vadd(\n    \"setNotReduced\",\n    values,\n    \"element\"\n)\nprint(res37)  # >>> 1\n\nres38 = r.vset().vdim(\"setNotReduced\")\nprint(res38)  # >>> 300\n\nres39 = r.vset().vadd(\n    \"setReduced\",\n    values,\n    \"element\",\n    reduce_dim=100\n)\nprint(res39)  # >>> 1\n\nres40 = r.vset().vdim(\"setReduced\")  # >>> 100\nprint(res40)","section_id":"vector-dimension"},{"id":"vector-dimension-ex9","language":"rust","code":"let values: Vec<f64> = (0..300).map(|i| i as f64 / 299.0).collect();\n\n        if let Ok(res) = r\n            .vadd(\n                \"setNotReduced\",\n                VectorAddInput::Values(EmbeddingInput::Float64(&values)),\n                \"element\",\n            )\n            .await\n        {\n            let res: bool = res;\n            println!(\"{res}\"); // >>> true\n        }\n\n        if let Ok(res) = r.vdim(\"setNotReduced\").await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 300\n        }\n\n        let opts = VAddOptions::default().set_reduction_dimension(100);\n        if let Ok(res) = r\n            .vadd_options(\n                \"setReduced\",\n                VectorAddInput::Values(EmbeddingInput::Float64(&values)),\n                \"element\",\n                &opts,\n            )\n            .await\n        {\n            let res: bool = res;\n            println!(\"{res}\"); // >>> true\n        }\n\n        if let Ok(res) = r.vdim(\"setReduced\").await {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 100\n        }","section_id":"vector-dimension"},{"id":"vector-dimension-ex10","language":"rust","code":"let values: Vec<f64> = (0..300).map(|i| i as f64 / 299.0).collect();\n\n        if let Ok(res) = r.vadd(\n            \"setNotReduced\",\n            VectorAddInput::Values(EmbeddingInput::Float64(&values)),\n            \"element\",\n        ) {\n            let res: bool = res;\n            println!(\"{res}\"); // >>> true\n        }\n\n        if let Ok(res) = r.vdim(\"setNotReduced\") {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 300\n        }\n\n        let opts = VAddOptions::default().set_reduction_dimension(100);\n        if let Ok(res) = r.vadd_options(\n            \"setReduced\",\n            VectorAddInput::Values(EmbeddingInput::Float64(&values)),\n            \"element\",\n            &opts,\n        ) {\n            let res: bool = res;\n            println!(\"{res}\"); // >>> true\n        }\n\n        if let Ok(res) = r.vdim(\"setReduced\") {\n            let res: usize = res;\n            println!(\"{res}\"); // >>> 100\n        }","section_id":"vector-dimension"}]}
{"schema_version":2,"id":"develop/data-types/vector-sets/performance","title":"Performance","url":"https://redis.io/docs/latest/develop/data-types/vector-sets/performance/","summary":"Learn how Redis vector sets behave under load and how to optimize for speed and recall","tags":["docs","develop","stack","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-10T15:45:48+01:00","page_type":"content","content_hash":"9e7846da95960d9a0274310860427f3c50bc55eb8539ec608c310b6810ed87f7","sections":[{"id":"query-performance","title":"Query performance","role":"content","text":"Vector similarity queries using the [`VSIM`](https://redis.io/docs/latest/commands/vsim) are threaded by default. Redis uses up to 32 threads to process these queries in parallel.\n\n- `VSIM` performance scales nearly linearly with available CPU cores.\n- Expect ~50,000 similarity queries per second for a 3M-item set with 300-dim vectors using int8 quantization.\n- Performance depends heavily on the `EF` parameter:\n  - Higher `EF` improves recall, but slows down search.\n  - Lower `EF` returns faster results with reduced accuracy."},{"id":"insertion-performance","title":"Insertion performance","role":"content","text":"Inserting vectors with the [`VADD`](https://redis.io/docs/latest/commands/vadd) command is more computationally expensive than querying:\n\n- Insertion is single-threaded by default.\n- Use the `CAS` option to offload candidate graph search to a background thread.\n- Expect a few thousand insertions per second on a single node."},{"id":"quantization-effects","title":"Quantization effects","role":"content","text":"Quantization greatly impacts both speed and memory:\n\n- `Q8` (default): 4x smaller than `FP32`, high recall, high speed\n- `BIN` (binary): 32x smaller than `FP32`, lower recall, fastest search\n- `NOQUANT` (`FP32`): Full precision, slower performance, highest memory use\n\nUse the quantization mode that best fits your tradeoff between precision and efficiency.\nThe examples below show how the different modes affect a simple vector.\nNote that even with `NOQUANT` mode, the values change slightly,\ndue to floating point rounding.\n\nQuantization modes: Compare Q8, NOQUANT, and BIN quantization modes using VADD and VEMB to understand the tradeoffs between precision, memory, and performance\n\n**Difficulty:** Advanced\n\n**Commands:** VADD, VEMB\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]"},{"id":"deletion-performance","title":"Deletion performance","role":"content","text":"Deleting large vector sets using the [`DEL`](https://redis.io/docs/latest/commands/del) can cause latency spikes:\n\n- Redis must unlink and restructure many graph nodes.\n- Latency is most noticeable when deleting millions of elements."},{"id":"save-and-load-performance","title":"Save and load performance","role":"content","text":"Vector sets save and load the full HNSW graph structure:\n\n- When reloading from disk is fast and there's no need to rebuild the graph.\n\nExample: A 3M vector set with 300 components loads in ~15 seconds."},{"id":"summary-of-tuning-tips","title":"Summary of tuning tips","role":"content","text":"| Factor     | Effect on performance               | Tip                                            |\n|------------|-------------------------------------|------------------------------------------------|\n| `EF`       | Slower queries but higher recall    | Start low (for example, 200) and tune upward           |\n| `M`        | More memory per node, better recall | Use defaults unless recall is too low          |\n| Quant type | Binary is fastest, `FP32` is slowest| Use `Q8` or `BIN` unless full precision needed |\n| `CAS`      | Faster insertions with threading    | Use when high write throughput is needed       |"},{"id":"see-also","title":"See also","role":"related","text":"- [Memory usage](https://redis.io/docs/latest/develop/data-types/vector-sets/memory)\n- [Scalability](https://redis.io/docs/latest/develop/data-types/vector-sets/scalability)\n- [Filtered search](https://redis.io/docs/latest/develop/data-types/vector-sets/filtered-search)"}],"examples":[{"id":"quantization-effects-ex0","language":"plaintext","code":"> VADD quantSetQ8 VALUES 2 1.262185 1.958231 quantElement Q8\n(integer) 1\n> VEMB quantSetQ8 quantElement\n1) \"1.2643694877624512\"\n2) \"1.958230972290039\"\n\n> VADD quantSetNoQ VALUES 2 1.262185 1.958231 quantElement NOQUANT\n(integer) 1\n> VEMB quantSetNoQ quantElement\n1) \"1.262184977531433\"\n2) \"1.958230972290039\"\n\n> VADD quantSetBin VALUES 2 1.262185 1.958231 quantElement BIN\n(integer) 1\n> VEMB quantSetBin quantElement\n1) \"1\"\n2) \"1\"","section_id":"quantization-effects"},{"id":"quantization-effects-ex1","language":"csharp","code":"VectorSetAddRequest addInt8 = VectorSetAddRequest.Member(\"quantElement\", new float[] { 1.262185f, 1.958231f }, null);\n        addInt8.Quantization = VectorSetQuantization.Int8;\n        bool q8Added = db.VectorSetAdd(\"quantSetQ8\", addInt8);\n        Console.WriteLine(q8Added); // >>> True\n        using (Lease<float>? eInt8 = db.VectorSetGetApproximateVector(\"quantSetQ8\", \"quantElement\"))\n        {\n            Span<float> v = eInt8!.Span;\n            Console.WriteLine($\"Q8: [{v[0]}, {v[1]}]\");\n            // >>> Q8: [1.2643694877624512, 1.958230972290039]\n        }\n\n        VectorSetAddRequest addNone = VectorSetAddRequest.Member(\"quantElement\", new float[] { 1.262185f, 1.958231f }, null);\n        addNone.Quantization = VectorSetQuantization.None;\n        bool noQuantAdded = db.VectorSetAdd(\"quantSetNoQ\", addNone);\n        Console.WriteLine(noQuantAdded); // >>> True\n        using (Lease<float>? eNone = db.VectorSetGetApproximateVector(\"quantSetNoQ\", \"quantElement\"))\n        {\n            Span<float> v = eNone!.Span;\n            Console.WriteLine($\"NOQUANT: [{v[0]}, {v[1]}]\");\n            // >>> NOQUANT: [1.262184977531433, 1.958230972290039]\n        }\n\n        VectorSetAddRequest addBinary = VectorSetAddRequest.Member(\"quantElement\", new float[] { 1.262185f, 1.958231f }, null);\n        addBinary.Quantization = VectorSetQuantization.Binary;\n        bool binAdded = db.VectorSetAdd(\"quantSetBin\", addBinary);\n        Console.WriteLine(binAdded); // >>> True\n        using (Lease<float>? eBinary = db.VectorSetGetApproximateVector(\"quantSetBin\", \"quantElement\"))\n        {\n            Span<float> v = eBinary!.Span;\n            Console.WriteLine($\"BIN: [{v[0]}, {v[1]}]\");\n            // >>> BIN: [1, 1]\n        }","section_id":"quantization-effects"},{"id":"quantization-effects-ex2","language":"go","code":"// Add with Q8 quantization\n\tvecQ := &redis.VectorValues{Val: []float64{1.262185, 1.958231}}\n\n\tres1, err := rdb.VAddWithArgs(ctx, \"quantSetQ8\", \"quantElement\", vecQ,\n\t\t&redis.VAddArgs{\n\t\t\tQ8: true,\n\t\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> true\n\n\tembQ8, err := rdb.VEmb(ctx, \"quantSetQ8\", \"quantElement\", false).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Printf(\"Q8 embedding: %v\\n\", embQ8)\n\t// >>> Q8 embedding: [1.2621850967407227 1.9582309722900391]\n\n\t// Add with NOQUANT option\n\tres2, err := rdb.VAddWithArgs(ctx, \"quantSetNoQ\", \"quantElement\", vecQ,\n\t\t&redis.VAddArgs{\n\t\t\tNoQuant: true,\n\t\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> true\n\n\tembNoQ, err := rdb.VEmb(ctx, \"quantSetNoQ\", \"quantElement\", false).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Printf(\"NOQUANT embedding: %v\\n\", embNoQ)\n\t// >>> NOQUANT embedding: [1.262185 1.958231]\n\n\t// Add with BIN quantization\n\tres3, err := rdb.VAddWithArgs(ctx, \"quantSetBin\", \"quantElement\", vecQ,\n\t\t&redis.VAddArgs{\n\t\t\tBin: true,\n\t\t},\n\t).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3) // >>> true\n\n\tembBin, err := rdb.VEmb(ctx, \"quantSetBin\", \"quantElement\", false).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Printf(\"BIN embedding: %v\\n\", embBin)\n\t// >>> BIN embedding: [1 1]","section_id":"quantization-effects"},{"id":"quantization-effects-ex3","language":"java","code":"VAddArgs q8Args = VAddArgs.Builder.quantizationType(QuantizationType.Q8);\n            CompletableFuture<Void> quantizationOperations = asyncCommands\n                    .vadd(\"quantSetQ8\", \"quantElement\", q8Args, 1.262185, 1.958231).thenCompose(result -> {\n                        System.out.println(result); // >>> true\n                        return asyncCommands.vemb(\"quantSetQ8\", \"quantElement\");\n                    }).thenCompose(result -> {\n                        System.out.println(\"Q8: \" + result); // >>> Q8: [1.2643694877624512, 1.958230972290039]\n\n                        VAddArgs noQuantArgs = VAddArgs.Builder.quantizationType(QuantizationType.NO_QUANTIZATION);\n                        return asyncCommands.vadd(\"quantSetNoQ\", \"quantElement\", noQuantArgs, 1.262185, 1.958231);\n                    }).thenCompose(result -> {\n                        System.out.println(result); // >>> true\n                        return asyncCommands.vemb(\"quantSetNoQ\", \"quantElement\");\n                    }).thenCompose(result -> {\n                        System.out.println(\"NOQUANT: \" + result); // >>> NOQUANT: [1.262184977531433, 1.958230972290039]\n\n                        VAddArgs binArgs = VAddArgs.Builder.quantizationType(QuantizationType.BINARY);\n                        return asyncCommands.vadd(\"quantSetBin\", \"quantElement\", binArgs, 1.262185, 1.958231);\n                    }).thenCompose(result -> {\n                        System.out.println(result); // >>> true\n                        return asyncCommands.vemb(\"quantSetBin\", \"quantElement\");\n                    }).thenAccept(result -> {\n                        System.out.println(\"BIN: \" + result); // >>> BIN: [1.0, 1.0]\n                    }).toCompletableFuture();","section_id":"quantization-effects"},{"id":"quantization-effects-ex4","language":"java","code":"VAddArgs q8Args = VAddArgs.Builder.quantizationType(QuantizationType.Q8);\n            Mono<Void> quantizationOperations = reactiveCommands.vadd(\"quantSetQ8\", \"quantElement\", q8Args, 1.262185, 1.958231)\n                    .doOnNext(result -> {\n                        System.out.println(result); // >>> true\n                    }).flatMap(result -> reactiveCommands.vemb(\"quantSetQ8\", \"quantElement\").collectList()).doOnNext(result -> {\n                        System.out.println(\"Q8: \" + result); // >>> Q8: [1.2643694877624512, 1.958230972290039]\n                    }).flatMap(result -> {\n                        VAddArgs noQuantArgs = VAddArgs.Builder.quantizationType(QuantizationType.NO_QUANTIZATION);\n                        return reactiveCommands.vadd(\"quantSetNoQ\", \"quantElement\", noQuantArgs, 1.262185, 1.958231);\n                    }).doOnNext(result -> {\n                        System.out.println(result); // >>> true\n                    }).flatMap(result -> reactiveCommands.vemb(\"quantSetNoQ\", \"quantElement\").collectList())\n                    .doOnNext(result -> {\n                        System.out.println(\"NOQUANT: \" + result); // >>> NOQUANT: [1.262184977531433, 1.958230972290039]\n                    }).flatMap(result -> {\n                        VAddArgs binArgs = VAddArgs.Builder.quantizationType(QuantizationType.BINARY);\n                        return reactiveCommands.vadd(\"quantSetBin\", \"quantElement\", binArgs, 1.262185, 1.958231);\n                    }).doOnNext(result -> {\n                        System.out.println(result); // >>> true\n                    }).flatMap(result -> reactiveCommands.vemb(\"quantSetBin\", \"quantElement\").collectList())\n                    .doOnNext(result -> {\n                        System.out.println(\"BIN: \" + result); // >>> BIN: [1.0, 1.0]\n                    }).then();","section_id":"quantization-effects"},{"id":"quantization-effects-ex5","language":"java","code":"boolean res31 = jedis.vadd(\"quantSetQ8\", new float[] { 1.262185f, 1.958231f }, \"quantElement\",\n        new VAddParams().q8());\n      System.out.println(res31); // >>> true\n\n      List<Double> res32 = jedis.vemb(\"quantSetQ8\", \"quantElement\");\n      System.out.println(\"Q8: \" + res32);\n      // >>> Q8: [~1.264, ~1.958]\n\n      boolean res33 = jedis.vadd(\"quantSetNoQ\", new float[] { 1.262185f, 1.958231f },\n        \"quantElement\", new VAddParams().noQuant());\n      System.out.println(res33); // >>> true\n\n      List<Double> res34 = jedis.vemb(\"quantSetNoQ\", \"quantElement\");\n      System.out.println(\"NOQUANT: \" + res34);\n      // >>> NOQUANT: [~1.262185, ~1.958231]\n\n      boolean res35 = jedis.vadd(\"quantSetBin\", new float[] { 1.262185f, 1.958231f },\n        \"quantElement\", new VAddParams().bin());\n      System.out.println(res35); // >>> true\n\n      List<Double> res36 = jedis.vemb(\"quantSetBin\", \"quantElement\");\n      System.out.println(\"BIN: \" + res36);\n      // >>> BIN: [1, 1]","section_id":"quantization-effects"},{"id":"quantization-effects-ex6","language":"javascript","code":"const res31 = await client.vAdd(\"quantSetQ8\", [1.262185, 1.958231], \"quantElement\", {\n  QUANT: 'Q8'\n});\nconsole.log(res31);  // >>> true\n\nconst res32 = await client.vEmb(\"quantSetQ8\", \"quantElement\");\nconsole.log(`Q8: ${res32}`);\n// >>> Q8: [1.2643694877624512, 1.958230972290039]\n\nconst res33 = await client.vAdd(\"quantSetNoQ\", [1.262185, 1.958231], \"quantElement\", {\n  QUANT: 'NOQUANT'\n});\nconsole.log(res33);  // >>> true\n\nconst res34 = await client.vEmb(\"quantSetNoQ\", \"quantElement\");\nconsole.log(`NOQUANT: ${res34}`);\n// >>> NOQUANT: [1.262184977531433, 1.958230972290039]\n\nconst res35 = await client.vAdd(\"quantSetBin\", [1.262185, 1.958231], \"quantElement\", {\n  QUANT: 'BIN'\n});\nconsole.log(res35);  // >>> true\n\nconst res36 = await client.vEmb(\"quantSetBin\", \"quantElement\");\nconsole.log(`BIN: ${res36}`);\n// >>> BIN: [1, 1]","section_id":"quantization-effects"},{"id":"quantization-effects-ex7","language":"php","code":"$res31 = $r->vadd('quantSetQ8', [1.262185, 1.958231], 'quantElement', null, false,\n            Predis\\Command\\Redis\\VADD::QUANT_Q8\n        );\n        echo $res31 . PHP_EOL;\n        // >>> 1\n\n        $res32 = $r->vemb('quantSetQ8', 'quantElement');\n        echo json_encode($res32) . PHP_EOL;\n        // >>> [1.2643694877624512, 1.958230972290039]\n\n        $res33 = $r->vadd('quantSetNoQ', [1.262185, 1.958231], 'quantElement', null, false,\n            Predis\\Command\\Redis\\VADD::QUANT_NOQUANT\n        );\n        echo $res33 . PHP_EOL;\n        // >>> 1\n\n        $res34 = $r->vemb('quantSetNoQ', 'quantElement');\n        echo json_encode($res34) . PHP_EOL;\n        // >>> [1.262184977531433, 1.958230972290039]\n\n        $res35 = $r->vadd('quantSetBin', [1.262185, 1.958231], 'quantElement', null, false,\n            Predis\\Command\\Redis\\VADD::QUANT_BIN\n        );\n        echo $res35 . PHP_EOL;\n        // >>> 1\n\n        $res36 = $r->vemb('quantSetBin', 'quantElement');\n        echo json_encode($res36) . PHP_EOL;\n        // >>> [1, 1]","section_id":"quantization-effects"},{"id":"quantization-effects-ex8","language":"python","code":"# Import `QuantizationOptions` enum using:\n#\n# from redis.commands.vectorset.commands import (\n#   QuantizationOptions\n# )\nres31 = r.vset().vadd(\n    \"quantSetQ8\", [1.262185, 1.958231],\n    \"quantElement\",\n    quantization=QuantizationOptions.Q8\n)\nprint(res31)  # >>> 1\n\nres32 = r.vset().vemb(\"quantSetQ8\", \"quantElement\")\nprint(f\"Q8: {res32}\")\n# >>> Q8: [1.2643694877624512, 1.958230972290039]\n\nres33 = r.vset().vadd(\n    \"quantSetNoQ\", [1.262185, 1.958231],\n    \"quantElement\",\n    quantization=QuantizationOptions.NOQUANT\n)\nprint(res33)  # >>> 1\n\nres34 = r.vset().vemb(\"quantSetNoQ\", \"quantElement\")\nprint(f\"NOQUANT: {res34}\")\n# >>> NOQUANT: [1.262184977531433, 1.958230972290039]\n\nres35 = r.vset().vadd(\n    \"quantSetBin\", [1.262185, 1.958231],\n    \"quantElement\",\n    quantization=QuantizationOptions.BIN\n)\nprint(res35)  # >>> 1\n\nres36 = r.vset().vemb(\"quantSetBin\", \"quantElement\")\nprint(f\"BIN: {res36}\")\n# >>> BIN: [1, 1]","section_id":"quantization-effects"},{"id":"quantization-effects-ex9","language":"rust","code":"let opts = VAddOptions::default().set_quantization(VectorQuantization::Q8);\n        if let Ok(res) = r\n            .vadd_options(\n                \"quantSetQ8\",\n                VectorAddInput::Values(EmbeddingInput::Float64(&[1.262185, 1.958231])),\n                \"quantElement\",\n                &opts,\n            )\n            .await\n        {\n            let res: bool = res;\n            println!(\"{res}\"); // >>> true\n        }\n\n        if let Ok(res) = r.vemb(\"quantSetQ8\", \"quantElement\").await {\n            let res: Vec<f64> = res;\n            println!(\"{res:?}\");\n            // >>> [1.2643694877624512, 1.958230972290039]\n        }\n\n        let opts = VAddOptions::default().set_quantization(VectorQuantization::NoQuant);\n        if let Ok(res) = r\n            .vadd_options(\n                \"quantSetNoQ\",\n                VectorAddInput::Values(EmbeddingInput::Float64(&[1.262185, 1.958231])),\n                \"quantElement\",\n                &opts,\n            )\n            .await\n        {\n            let res: bool = res;\n            println!(\"{res}\"); // >>> true\n        }\n\n        if let Ok(res) = r.vemb(\"quantSetNoQ\", \"quantElement\").await {\n            let res: Vec<f64> = res;\n            println!(\"{res:?}\");\n            // >>> [1.262184977531433, 1.958230972290039]\n        }\n\n        let opts = VAddOptions::default().set_quantization(VectorQuantization::Bin);\n        if let Ok(res) = r\n            .vadd_options(\n                \"quantSetBin\",\n                VectorAddInput::Values(EmbeddingInput::Float64(&[1.262185, 1.958231])),\n                \"quantElement\",\n                &opts,\n            )\n            .await\n        {\n            let res: bool = res;\n            println!(\"{res}\"); // >>> true\n        }\n\n        if let Ok(res) = r.vemb(\"quantSetBin\", \"quantElement\").await {\n            let res: Vec<f64> = res;\n            println!(\"{res:?}\"); // >>> [1.0, 1.0]\n        }","section_id":"quantization-effects"},{"id":"quantization-effects-ex10","language":"rust","code":"let opts = VAddOptions::default().set_quantization(VectorQuantization::Q8);\n        if let Ok(res) = r.vadd_options(\n            \"quantSetQ8\",\n            VectorAddInput::Values(EmbeddingInput::Float64(&[1.262185, 1.958231])),\n            \"quantElement\",\n            &opts,\n        ) {\n            let res: bool = res;\n            println!(\"{res}\"); // >>> true\n        }\n\n        if let Ok(res) = r.vemb(\"quantSetQ8\", \"quantElement\") {\n            let res: Vec<f64> = res;\n            println!(\"{res:?}\");\n            // >>> [1.2643694877624512, 1.958230972290039]\n        }\n\n        let opts = VAddOptions::default().set_quantization(VectorQuantization::NoQuant);\n        if let Ok(res) = r.vadd_options(\n            \"quantSetNoQ\",\n            VectorAddInput::Values(EmbeddingInput::Float64(&[1.262185, 1.958231])),\n            \"quantElement\",\n            &opts,\n        ) {\n            let res: bool = res;\n            println!(\"{res}\"); // >>> true\n        }\n\n        if let Ok(res) = r.vemb(\"quantSetNoQ\", \"quantElement\") {\n            let res: Vec<f64> = res;\n            println!(\"{res:?}\");\n            // >>> [1.262184977531433, 1.958230972290039]\n        }\n\n        let opts = VAddOptions::default().set_quantization(VectorQuantization::Bin);\n        if let Ok(res) = r.vadd_options(\n            \"quantSetBin\",\n            VectorAddInput::Values(EmbeddingInput::Float64(&[1.262185, 1.958231])),\n            \"quantElement\",\n            &opts,\n        ) {\n            let res: bool = res;\n            println!(\"{res}\"); // >>> true\n        }\n\n        if let Ok(res) = r.vemb(\"quantSetBin\", \"quantElement\") {\n            let res: Vec<f64> = res;\n            println!(\"{res:?}\"); // >>> [1.0, 1.0]\n        }","section_id":"quantization-effects"}]}
{"schema_version":2,"id":"develop/data-types/vector-sets/scalability","title":"Scalability","url":"https://redis.io/docs/latest/develop/data-types/vector-sets/scalability/","summary":"Scale Redis vector sets to handle larger data sets and workloads","tags":["docs","develop","stack","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-10T15:45:48+01:00","page_type":"content","content_hash":"15acb8de723c17b6cfda6eb1c9911383163a95aa36bbcf097fc2fc0177965252","sections":[{"id":"multi-instance-scalability","title":"Multi-instance scalability","role":"overview","text":"Vector sets can scale horizontally by sharding your data across multiple Redis instances. This is done by partitioning the dataset manually across keys and nodes."},{"id":"example-strategy","title":"Example strategy","role":"example","text":"You can shard data using a consistent hash:\n\n[code example]\n\nThen add elements into different keys:\n\n[code example]\n\nTo run a similarity search across all shards, send [`VSIM`](https://redis.io/docs/latest/commands/vsim) commands to each key and then merge the results client-side:\n\n[code example]\n\nThen combine and sort the results by score."},{"id":"key-properties","title":"Key properties","role":"content","text":"- Write operations ([`VADD`](https://redis.io/docs/latest/commands/vadd), [`VREM`](https://redis.io/docs/latest/commands/vrem)) scale linearly—you can insert in parallel across instances.\n- Read operations ([`VSIM`](https://redis.io/docs/latest/commands/vsim)) do not scale linearly—you must query all shards for a full result set.\n- Smaller vector sets yield faster queries, so distributing them helps reduce query time per node.\n- Merging results client-side keeps logic simple and doesn't add server-side overhead."},{"id":"availability-benefits","title":"Availability benefits","role":"content","text":"This sharding model also improves fault tolerance:\n\n- If one instance is down, you can still retrieve partial results from others.\n- Use timeouts and partial fallbacks to increase resilience."},{"id":"latency-considerations","title":"Latency considerations","role":"content","text":"To avoid additive latency across N instances:\n\n- Send queries to all shards in parallel.\n- Wait for the slowest response.\n\nThis makes total latency close to the worst-case shard time, not the sum of all times."},{"id":"summary","title":"Summary","role":"content","text":"| Goal                      | Approach                                          |\n|---------------------------|---------------------------------------------------|\n| Scale inserts             | Split data across keys and instances              |\n| Scale reads               | Query all shards and merge results                |\n| High availability         | Accept partial results when some shards fail      |\n| Maintain performance      | Use smaller shards for faster per-node traversal  |"},{"id":"see-also","title":"See also","role":"related","text":"- [Performance](https://redis.io/docs/latest/develop/data-types/vector-sets/performance)\n- [Filtered search](https://redis.io/docs/latest/develop/data-types/vector-sets/filtered-search)\n- [Memory usage](https://redis.io/docs/latest/develop/data-types/vector-sets/memory)"}],"examples":[{"id":"example-strategy-ex0","language":"python","code":"key_index = crc32(item) % 3\nkey = f\"vset:{key_index}\"","section_id":"example-strategy"},{"id":"example-strategy-ex1","language":"bash","code":"VADD vset:0 VALUES 3 0.1 0.2 0.3 item1\nVADD vset:1 VALUES 3 0.4 0.5 0.6 item2","section_id":"example-strategy"},{"id":"example-strategy-ex2","language":"bash","code":"VSIM vset:0 VALUES ... WITHSCORES\nVSIM vset:1 VALUES ... WITHSCORES\nVSIM vset:2 VALUES ... WITHSCORES","section_id":"example-strategy"}]}
{"schema_version":2,"id":"develop/data-types/vector-sets/troubleshooting","title":"Troubleshooting","url":"https://redis.io/docs/latest/develop/data-types/vector-sets/troubleshooting/","summary":"Diagnose and debug issues when working with Redis vector sets","tags":["docs","develop","stack","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-10T15:45:48+01:00","page_type":"content","content_hash":"268c8953dd43d8c9d9c2fe7a76797fbed2b30461afc9882f6d6e505ea62289fe","sections":[{"id":"common-challenges","title":"Common challenges","role":"overview","text":"Vector sets are approximate by design. That makes debugging trickier than with exact match queries. This section helps you understand issues with recall, filtering, and graph structure."},{"id":"low-recall-or-missing-results","title":"Low recall or missing results","role":"content","text":"If [`VSIM`](https://redis.io/docs/latest/commands/vsim) doesn't return expected items:\n\n- Increase the `EF` parameter:\n\n  [code example]\n\n- Check quantization mode. Binary quantization (`BIN`) trades accuracy for speed.\n- Use `TRUTH` to compare results against a linear scan:\n\n  [code example]\n\n  This gives you the most accurate results for validation, but it's slow."},{"id":"filtering-issues","title":"Filtering issues","role":"content","text":"Filters silently exclude items if:\n\n- A field is missing from the element’s attributes\n- The JSON is invalid\n- A type doesn’t match the expression (for example, `.rating > 8` when `.rating` is a string)\n\nTry retrieving the attributes with [`VGETATTR`](https://redis.io/docs/latest/commands/vgetattr):\n\n[code example]\n\nDouble-check field names, JSON validity, and value types."},{"id":"unexpected-memory-usage","title":"Unexpected memory usage","role":"content","text":"Memory issues may arise from:\n\n- Large vectors (use `REDUCE` to project down)\n- High `M` values inflating link graphs\n- Large or deeply nested JSON attributes\n- Storing raw `FP32` vectors (`NOQUANT`)\n\nUse default `Q8` quantization and compact attributes to save space."},{"id":"inspecting-the-graph","title":"Inspecting the graph","role":"content","text":"Use [`VLINKS`](https://redis.io/docs/latest/commands/vlinks) to examine a node’s connections:\n\n[code example]\n\n- Helps you verify whether isolated or weakly connected nodes exist.\n- Useful for explaining poor recall."},{"id":"deletion-spikes","title":"Deletion spikes","role":"content","text":"Large sets deleted using the `DEL` command can briefly spike latency as Redis reclaims memory and rebuilds HNSW linkages."},{"id":"replication-quirks","title":"Replication quirks","role":"content","text":"- `VADD` with `REDUCE` does not replicate the random projection matrix.\n- Replicas will produce different projected vectors for the same inputs.\n\nThis doesn't affect similarity searches but does affect [`VEMB`](https://redis.io/docs/latest/commands/vemb) output."},{"id":"summary","title":"Summary","role":"content","text":"| Symptom                          | Try this                                                  |\n|----------------------------------|-----------------------------------------------------------|\n| Poor recall                      | Use higher `EF`, check quantization, use `TRUTH`          |\n| Filters exclude too much         | Validate attributes with `VGETATTR`, simplify expressions |\n| Memory spikes                    | Use `REDUCE`, `Q8`, smaller `M`, compact JSON             |\n| Replication mismatch with REDUCE | Avoid relying on projected vectors from replicas          |"},{"id":"see-also","title":"See also","role":"related","text":"- [Filtered Search](https://redis.io/docs/latest/develop/data-types/vector-sets/filtered-search)\n- [Memory Usage](https://redis.io/docs/latest/develop/data-types/vector-sets/memory)\n- [Performance](https://redis.io/docs/latest/develop/data-types/vector-sets/performance)"}],"examples":[{"id":"low-recall-or-missing-results-ex0","language":"bash","code":"VSIM myset VALUES 3 ... COUNT 10 EF 1000","section_id":"low-recall-or-missing-results"},{"id":"low-recall-or-missing-results-ex1","language":"bash","code":"VSIM myset VALUES 3 ... COUNT 10 TRUTH","section_id":"low-recall-or-missing-results"},{"id":"filtering-issues-ex0","language":"bash","code":"VGETATTR myset myelement","section_id":"filtering-issues"},{"id":"inspecting-the-graph-ex0","language":"bash","code":"VLINKS myset myelement WITHSCORES","section_id":"inspecting-the-graph"}]}
{"schema_version":2,"id":"develop/get-started/data-store","title":"Redis as an in-memory data structure store quick start guide","url":"https://redis.io/docs/latest/develop/get-started/data-store/","summary":"Understand how to use basic Redis data types","aliases":["/get-started/data-store/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-29T16:29:37+01:00","page_type":"content","content_hash":"bd63c4df0a837ab16309c88a333a140ebe489498c1110734d6b6e74eae7eb995","sections":[{"id":"setup","title":"Setup","role":"setup","text":"The easiest way to get started with Redis is to use Redis Cloud:\n\n1. Create a [free account](https://redis.com/try-free?utm_source=redisio&utm_medium=referral&utm_campaign=2023-09-try_free&utm_content=cu-redis_cloud_users).\n\n    ![](https://redis.io/docs/latest/images/dev/free-cloud-db.png)\n\n2. Follow the instructions to create a free database.\n\nYou can alternatively follow the [installation guides](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack) to install Redis on your local machine."},{"id":"connect","title":"Connect","role":"content","text":"The first step is to connect to Redis. You can find further details about the connection options in this documentation site's [Tools section](https://redis.io/docs/latest/develop/tools). The following example shows how to connect to a Redis server that runs on localhost (`-h 127.0.0.1`) and listens on the default port (`-p 6379`): \n\nFoundational: Connect to a Redis server\n\n**Difficulty:** Beginner\n\n**Commands:** REDIS-CLI\n\n**Available in:** Redis CLI, C#, Go, Java (Synchronous - Jedis), JavaScript (Node.js), Python\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n<br/>\n\n&nbsp;\n\n> [!TIP]\n> You can copy and paste the connection details from the Redis Cloud database configuration page. Here is an example connection string of a Cloud database that is hosted in the AWS region `us-east-1` and listens on port 16379: `redis-16379.c283.us-east-1-4.ec2.cloud.redislabs.com:16379`. The connection string has the format `host:port`. You must also copy and paste the username and password of your Cloud database and then either pass the credentials to your client or use the [AUTH command](https://redis.io/docs/latest/commands/auth) after the connection is established."},{"id":"store-and-retrieve-data","title":"Store and retrieve data","role":"content","text":"Redis stands for Remote Dictionary Server. You can use the same data types as in your local programming environment but on the server side within Redis.\n\nSimilar to byte arrays, Redis strings store sequences of bytes, including text, serialized objects, counter values, and binary arrays. The following example shows you how to set and get a string value:\n\nFoundational: Set and retrieve string values using SET and GET commands\n\n**Difficulty:** Beginner\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nHashes are the equivalent of dictionaries (dicts or hash maps). Among other things, you can use hashes to represent plain objects and to store groupings of counters. The following example explains how to set and access field values of an object:\n\nFoundational: Store and retrieve hash data structures using HSET to set multiple fields, HGET to retrieve individual fields, and HGETALL to retrieve all fields at once\n\n**Difficulty:** Beginner\n\n**Commands:** HSET, HGET, HGETALL\n\n**Complexity:**\n- HSET: O(1)\n- HGET: O(1)\n- HGETALL: O(N)\n\n**Available in:** Redis CLI, C#, Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (Asynchronous), Rust (Synchronous)\n\n##### Redis CLI\n\n[code example]\n\n##### C#\n\n[code example]\n\n##### Go\n\n[code example]\n\n##### Java (Asynchronous - Lettuce)\n\n[code example]\n\n##### Java (Reactive - Lettuce)\n\n[code example]\n\n##### Java (Synchronous - Jedis)\n\n[code example]\n\n##### JavaScript (Node.js)\n\n[code example]\n\n##### PHP\n\n[code example]\n\n##### Python\n\n[code example]\n\n##### Ruby\n\n[code example]\n\n##### Rust (Asynchronous)\n\n[code example]\n\n##### Rust (Synchronous)\n\n[code example]\n\n\n\nYou can get a complete overview of available data types in this documentation site's [data types section](https://redis.io/docs/latest/develop/data-types). Each data type has commands allowing you to manipulate or retrieve data. The [commands reference](/commands/) provides a sophisticated explanation."},{"id":"scan-the-keyspace","title":"Scan the keyspace","role":"content","text":"Each item within Redis has a unique key. All items live within the Redis [keyspace](https://redis.io/docs/latest/develop/using-commands/keyspace). You can scan the Redis keyspace via the [SCAN command](https://redis.io/docs/latest/commands/scan). Here is an example that scans for the first 100 keys that have the prefix `bike:`:\n\n[code example]\n\n[SCAN](https://redis.io/docs/latest/commands/scan) returns a cursor position, allowing you to scan iteratively for the next batch of keys until you reach the cursor value 0."},{"id":"next-steps","title":"Next steps","role":"content","text":"You can address more use cases with Redis by reading these additional quick start guides:\n\n* [Redis as a document database](https://redis.io/docs/latest/develop/get-started/search-tutorial)\n* [Redis as a vector database](https://redis.io/docs/latest/develop/get-started/search-tutorial/vector-search)"},{"id":"continue-learning-with-redis-university","title":"Continue learning with Redis University","role":"content","text":""}],"examples":[{"id":"connect-ex0","language":"plaintext","code":"> redis-cli -h 127.0.0.1 -p 6379","section_id":"connect"},{"id":"connect-ex1","language":"csharp","code":"var muxer = ConnectionMultiplexer.Connect(\"localhost:6379\");\n        var db = muxer.GetDatabase();\n        var ft = db.FT();\n        var json = db.JSON();","section_id":"connect"},{"id":"connect-ex2","language":"go","code":"ctx := context.Background()\n\n\trdb := redis.NewClient(&redis.Options{\n\t\tAddr:     \"localhost:6379\",\n\t\tPassword: \"\", // no password docs\n\t\tDB:       0,  // use default DB\n\t\tProtocol: 2,\n\t})","section_id":"connect"},{"id":"connect-ex3","language":"java","code":"RedisClient jedis = RedisClient.create(\"localhost\", 6379);","section_id":"connect"},{"id":"connect-ex4","language":"javascript","code":"const client = createClient();\nclient.on('error', err => console.log('Redis Client Error', err));\n\nawait client.connect();","section_id":"connect"},{"id":"connect-ex5","language":"python","code":"r = redis.Redis(host=\"localhost\", port=6379, db=0, decode_responses=True)","section_id":"connect"},{"id":"store-and-retrieve-data-ex0","language":"plaintext","code":"> SET bike:1 \"Process 134\"\n> GET bike:1","section_id":"store-and-retrieve-data"},{"id":"store-and-retrieve-data-ex1","language":"csharp","code":"using NRedisStack.Tests;\nusing StackExchange.Redis;\n\npublic class SetGetExample\n{\n    public void Run()\n    {\n        var muxer = ConnectionMultiplexer.Connect(\"localhost:6379\");\n        var db = muxer.GetDatabase();\n\n        bool status = db.StringSet(\"bike:1\", \"Process 134\");\n\n        if (status)\n            Console.WriteLine(\"Successfully added a bike.\");\n\n        var value = db.StringGet(\"bike:1\");\n\n        if (value.HasValue)\n            Console.WriteLine(\"The name of the bike is: \" + value + \".\");\n\n    }\n}","section_id":"store-and-retrieve-data"},{"id":"store-and-retrieve-data-ex2","language":"go","code":"package example_commands_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/redis/go-redis/v9\"\n)\n\nfunc ExampleClient_Set_and_get() {\n\tctx := context.Background()\n\n\trdb := redis.NewClient(&redis.Options{\n\t\tAddr:     \"localhost:6379\",\n\t\tPassword: \"\", // no password docs\n\t\tDB:       0,  // use default DB\n\t})\n\n\n\n\terr := rdb.Set(ctx, \"bike:1\", \"Process 134\", 0).Err()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(\"OK\")\n\n\tvalue, err := rdb.Get(ctx, \"bike:1\").Result()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tfmt.Printf(\"The name of the bike is %s\", value)\n\n}","section_id":"store-and-retrieve-data"},{"id":"store-and-retrieve-data-ex3","language":"java","code":"package io.redis.examples.async;\n\nimport io.lettuce.core.RedisClient;\nimport io.lettuce.core.api.async.RedisAsyncCommands;\nimport io.lettuce.core.api.StatefulRedisConnection;\n\nimport java.util.concurrent.CompletableFuture;\n\npublic class SetGetExample {\n\n    public void run() {\n        RedisClient redisClient = RedisClient.create(\"redis://localhost:6379\");\n\n        try (StatefulRedisConnection<String, String> connection = redisClient.connect()) {\n            RedisAsyncCommands<String, String> asyncCommands = connection.async();\n\n\n            CompletableFuture<Void> setGetExample = asyncCommands.set(\"bike:1\", \"Process 134\")\n                    .thenCompose(res1 -> {\n                        System.out.println(res1); // >>> OK\n                        return asyncCommands.get(\"bike:1\");\n                    }).thenAccept(res2 -> {\n                        System.out.println(res2); // >>> Process 134\n                    }).toCompletableFuture();\n\n            setGetExample.join();\n        } finally {\n            redisClient.shutdown();\n        }\n    }\n}","section_id":"store-and-retrieve-data"},{"id":"store-and-retrieve-data-ex4","language":"java","code":"package io.redis.examples.reactive;\n\nimport io.lettuce.core.RedisClient;\nimport io.lettuce.core.api.reactive.RedisReactiveCommands;\nimport io.lettuce.core.api.StatefulRedisConnection;\n\nimport reactor.core.publisher.Mono;\n\npublic class SetGetExample {\n\n    public void run() {\n        RedisClient redisClient = RedisClient.create(\"redis://localhost:6379\");\n\n        try (StatefulRedisConnection<String, String> connection = redisClient.connect()) {\n            RedisReactiveCommands<String, String> reactiveCommands = connection.reactive();\n\n\n            Mono<Void> setGetExample = reactiveCommands.set(\"bike:1\", \"Process 134\")\n                    .doOnNext(res1 -> {\n                        System.out.println(res1); // >>> OK\n                    })\n                    .then(reactiveCommands.get(\"bike:1\"))\n                    .doOnNext(res2 -> {\n                        System.out.println(res2); // >>> Process 134\n                    })\n                    .then();\n\n            setGetExample.block();\n        } finally {\n            redisClient.shutdown();\n        }\n    }\n}","section_id":"store-and-retrieve-data"},{"id":"store-and-retrieve-data-ex5","language":"java","code":"package io.redis.examples;\n\nimport redis.clients.jedis.RedisClient;\n\n\npublic class SetGetExample {\n\n  public void run() {\n\n    RedisClient jedis = RedisClient.create(\"redis://localhost:6379\");\n\n    String status = jedis.set(\"bike:1\", \"Process 134\");\n\n    if (\"OK\".equals(status)) System.out.println(\"Successfully added a bike.\");\n\n    String value = jedis.get(\"bike:1\");\n\n    if (value != null) System.out.println(\"The name of the bike is: \" + value + \".\");\n\n\n    jedis.close();\n  }\n}","section_id":"store-and-retrieve-data"},{"id":"store-and-retrieve-data-ex6","language":"javascript","code":"import { createClient } from 'redis';\n\nconst client = createClient();\n\nclient.on('error', err => console.log('Redis Client Error', err));\n\nawait client.connect().catch(console.error);\n\nawait client.set('bike:1', 'Process 134');\nconst value = await client.get('bike:1');\nconsole.log(value);\n// returns 'Process 134'\n\nawait client.close();","section_id":"store-and-retrieve-data"},{"id":"store-and-retrieve-data-ex7","language":"javascript","code":"import assert from 'node:assert';\nimport { Redis } from 'ioredis';\n\nconst redis = new Redis();\n\n\nconst res1 = await redis.set('bike:1', 'Process 134');\nconsole.log(res1); // >>> OK\n\nconst res2 = await redis.get('bike:1');\nconsole.log(res2); // >>> Process 134\n\n\nredis.disconnect();","section_id":"store-and-retrieve-data"},{"id":"store-and-retrieve-data-ex8","language":"javascript","code":"import { createClient } from 'redis';\n\nconst client = createClient();\n\nclient.on('error', err => console.log('Redis Client Error', err));\n\nawait client.connect().catch(console.error);\n\nawait client.set('bike:1', 'Process 134');\nconst value = await client.get('bike:1');\nconsole.log(value);\n// returns 'Process 134'\n\nawait client.close();","section_id":"store-and-retrieve-data"},{"id":"store-and-retrieve-data-ex9","language":"javascript","code":"import assert from 'node:assert';\nimport { Redis } from 'ioredis';\n\nconst redis = new Redis();\n\n\nconst res1 = await redis.set('bike:1', 'Process 134');\nconsole.log(res1); // >>> OK\n\nconst res2 = await redis.get('bike:1');\nconsole.log(res2); // >>> Process 134\n\n\nredis.disconnect();","section_id":"store-and-retrieve-data"},{"id":"store-and-retrieve-data-ex10","language":"php","code":"<?php\nuse Predis\\Client as PredisClient;\n\nclass SetGetTest\n{\n    public function testSetGet() {\n        $r = new PredisClient([\n            'scheme'   => 'tcp',\n            'host'     => '127.0.0.1',\n            'port'     => 6379,\n            'password' => '',\n            'database' => 0,\n        ]);\n\n\n        $res1 = $r->set('bike:1', 'Process 134');\n        echo $res1 . PHP_EOL; // >>> OK\n\n        $res2 = $r->get('bike:1');\n        echo $res2 . PHP_EOL; // >>> Process 134\n\n    }\n}","section_id":"store-and-retrieve-data"},{"id":"store-and-retrieve-data-ex11","language":"python","code":"\"\"\"\nCode samples for data structure store quickstart pages:\n    https://redis.io/docs/latest/develop/get-started/data-store/\n\"\"\"\n\nimport redis\n\nr = redis.Redis(host=\"localhost\", port=6379, db=0, decode_responses=True)\n\nres = r.set(\"bike:1\", \"Process 134\")\nprint(res)\n# >>> True\n\nres = r.get(\"bike:1\")\nprint(res)\n# >>> \"Process 134\"","section_id":"store-and-retrieve-data"},{"id":"store-and-retrieve-data-ex12","language":"ruby","code":"require 'redis'\n\nr = Redis.new\n\n\nres1 = r.set('bike:1', 'Process 134')\nputs res1 # >>> OK\n\nres2 = r.get('bike:1')\nputs res2 # >>> Process 134","section_id":"store-and-retrieve-data"},{"id":"store-and-retrieve-data-ex13","language":"rust","code":"mod set_and_get_tests {\n    use redis::AsyncCommands;\n\n    async fn run() {\n        let mut r = match redis::Client::open(\"redis://127.0.0.1\") {\n            Ok(client) => match client.get_multiplexed_async_connection().await {\n                Ok(conn) => conn,\n                Err(e) => {\n                    println!(\"Failed to connect to Redis: {e}\");\n                    return;\n                }\n            },\n            Err(e) => {\n                println!(\"Failed to create Redis client: {e}\");\n                return;\n            }\n        };\n\n\n        if let Ok(res1) = r.set(\"bike:1\", \"Process 134\").await {\n            let res1: String = res1;\n            println!(\"{res1}\"); // >>> OK\n        }\n\n        if let Ok(res2) = r.get(\"bike:1\").await {\n            let res2: String = res2;\n            println!(\"{res2}\"); // >>> Process 134\n        }\n\n    }\n}","section_id":"store-and-retrieve-data"},{"id":"store-and-retrieve-data-ex14","language":"rust","code":"mod set_and_get_tests {\n    use redis::Commands;\n\n    fn run() {\n        let mut r = match redis::Client::open(\"redis://127.0.0.1\") {\n            Ok(client) => match client.get_connection() {\n                Ok(conn) => conn,\n                Err(e) => {\n                    println!(\"Failed to connect to Redis: {e}\");\n                    return;\n                }\n            },\n            Err(e) => {\n                println!(\"Failed to create Redis client: {e}\");\n                return;\n            }\n        };\n\n\n        if let Ok(res1) = r.set(\"bike:1\", \"Process 134\") {\n            let res1: String = res1;\n            println!(\"{res1}\"); // >>> OK\n        }\n\n        if let Ok(res2) = r.get(\"bike:1\") {\n            let res2: String = res2;\n            println!(\"{res2}\"); // >>> Process 134\n        }\n\n    }\n}","section_id":"store-and-retrieve-data"},{"id":"store-and-retrieve-data-ex15","language":"plaintext","code":"> HSET bike:2 model Deimos brand Ergonom type 'Enduro bikes' price 4972\n(integer) 4\n> HGET bike:2 model\n\"Deimos\"\n> HGET bike:2 price\n\"4972\"\n> HGETALL bike:2\n1) \"model\"\n2) \"Deimos\"\n3) \"brand\"\n4) \"Ergonom\"\n5) \"type\"\n6) \"Enduro bikes\"\n7) \"price\"\n8) \"4972\"","section_id":"store-and-retrieve-data"},{"id":"store-and-retrieve-data-ex16","language":"csharp","code":"db.HashSet(\"bike:1\", [\n            new(\"model\", \"Deimos\"),\n            new(\"brand\", \"Ergonom\"),\n            new(\"type\", \"Enduro bikes\"),\n            new(\"price\", 4972)\n        ]);\n\n        Console.WriteLine(\"Hash Created\");\n        // Hash Created\n\n        var model = db.HashGet(\"bike:1\", \"model\");\n        Console.WriteLine($\"Model: {model}\");\n        // Model: Deimos\n\n        var price = db.HashGet(\"bike:1\", \"price\");\n        Console.WriteLine($\"Price: {price}\");\n        // Price: 4972\n\n        var bike = db.HashGetAll(\"bike:1\");\n        Console.WriteLine(\"bike:1\");\n        Console.WriteLine(string.Join(\"\\n\", bike.Select(b => $\"{b.Name}: {b.Value}\")));\n        // Bike:1:\n        // model: Deimos\n        // brand: Ergonom\n        // type: Enduro bikes\n        // price: 4972","section_id":"store-and-retrieve-data"},{"id":"store-and-retrieve-data-ex17","language":"go","code":"hashFields := []string{\n\t\t\"model\", \"Deimos\",\n\t\t\"brand\", \"Ergonom\",\n\t\t\"type\", \"Enduro bikes\",\n\t\t\"price\", \"4972\",\n\t}\n\n\tres1, err := rdb.HSet(ctx, \"bike:1\", hashFields).Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res1) // >>> 4\n\n\tres2, err := rdb.HGet(ctx, \"bike:1\", \"model\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res2) // >>> Deimos\n\n\tres3, err := rdb.HGet(ctx, \"bike:1\", \"price\").Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res3) // >>> 4972\n\n\tcmdReturn := rdb.HGetAll(ctx, \"bike:1\")\n\tres4, err := cmdReturn.Result()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(res4)\n\t// >>> map[brand:Ergonom model:Deimos price:4972 type:Enduro bikes]\n\n\ttype BikeInfo struct {\n\t\tModel string `redis:\"model\"`\n\t\tBrand string `redis:\"brand\"`\n\t\tType  string `redis:\"type\"`\n\t\tPrice int    `redis:\"price\"`\n\t}\n\n\tvar res4a BikeInfo\n\n\tif err := cmdReturn.Scan(&res4a); err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Printf(\"Model: %v, Brand: %v, Type: %v, Price: $%v\\n\",\n\t\tres4a.Model, res4a.Brand, res4a.Type, res4a.Price)\n\t// >>> Model: Deimos, Brand: Ergonom, Type: Enduro bikes, Price: $4972","section_id":"store-and-retrieve-data"},{"id":"store-and-retrieve-data-ex18","language":"java","code":"Map<String, String> bike1 = new HashMap<>();\n            bike1.put(\"model\", \"Deimos\");\n            bike1.put(\"brand\", \"Ergonom\");\n            bike1.put(\"type\", \"Enduro bikes\");\n            bike1.put(\"price\", \"4972\");\n\n            CompletableFuture<Void> setGetAll = asyncCommands.hset(\"bike:1\", bike1).thenCompose(res1 -> {\n                System.out.println(res1); // >>> 4\n                return asyncCommands.hget(\"bike:1\", \"model\");\n            }).thenCompose(res2 -> {\n                System.out.println(res2); // >>> Deimos\n                return asyncCommands.hget(\"bike:1\", \"price\");\n            }).thenCompose(res3 -> {\n                System.out.println(res3); // >>> 4972\n                return asyncCommands.hgetall(\"bike:1\");\n            })\n                    .thenAccept(System.out::println)\n                    // >>> {type=Enduro bikes, brand=Ergonom, price=4972, model=Deimos}\n                    .toCompletableFuture();","section_id":"store-and-retrieve-data"},{"id":"store-and-retrieve-data-ex19","language":"java","code":"Map<String, String> bike1 = new HashMap<>();\n            bike1.put(\"model\", \"Deimos\");\n            bike1.put(\"brand\", \"Ergonom\");\n            bike1.put(\"type\", \"Enduro bikes\");\n            bike1.put(\"price\", \"4972\");\n\n            Mono<Long> setGetAll = reactiveCommands.hset(\"bike:1\", bike1).doOnNext(result -> {\n                System.out.println(result); // >>> 4\n            });\n\n            setGetAll.block();\n\n            Mono<String> getModel = reactiveCommands.hget(\"bike:1\", \"model\").doOnNext(result -> {\n                System.out.println(result); // >>> Deimos\n            });\n\n            Mono<String> getPrice = reactiveCommands.hget(\"bike:1\", \"price\").doOnNext(result -> {\n                System.out.println(result); // >>> 4972\n            });\n\n            Mono<List<KeyValue<String, String>>> getAll = reactiveCommands.hgetall(\"bike:1\").collectList().doOnNext(result -> {\n                System.out.println(result);\n                // >>> [KeyValue[type, Enduro bikes], KeyValue[brand, Ergonom],\n                // KeyValue[price, 4972], KeyValue[model, Deimos]]\n            });","section_id":"store-and-retrieve-data"},{"id":"store-and-retrieve-data-ex20","language":"java","code":"Map<String, String> bike1 = new HashMap<>();\n      bike1.put(\"model\", \"Deimos\");\n      bike1.put(\"brand\", \"Ergonom\");\n      bike1.put(\"type\", \"Enduro bikes\");\n      bike1.put(\"price\", \"4972\");\n\n      Long res1 = jedis.hset(\"bike:1\", bike1);\n      System.out.println(res1); // 4\n\n      String res2 = jedis.hget(\"bike:1\", \"model\");\n      System.out.println(res2); // Deimos\n\n      String res3 = jedis.hget(\"bike:1\", \"price\");\n      System.out.println(res3); // 4972\n\n      Map<String, String> res4 = jedis.hgetAll(\"bike:1\");\n      System.out.println(res4); // {type=Enduro bikes, brand=Ergonom, price=4972, model=Deimos}","section_id":"store-and-retrieve-data"},{"id":"store-and-retrieve-data-ex21","language":"javascript","code":"const res1 = await client.hSet(\n  'bike:1',\n  {\n    'model': 'Deimos',\n    'brand': 'Ergonom',\n    'type': 'Enduro bikes',\n    'price': 4972,\n  }\n)\nconsole.log(res1) // 4\n\nconst res2 = await client.hGet('bike:1', 'model')\nconsole.log(res2)  // 'Deimos'\n\nconst res3 = await client.hGet('bike:1', 'price')\nconsole.log(res3)  // '4972'\n\nconst res4 = await client.hGetAll('bike:1')\nconsole.log(res4)  \n/*\n{\n  brand: 'Ergonom',\n  model: 'Deimos',\n  price: '4972',\n  type: 'Enduro bikes'\n}\n*/","section_id":"store-and-retrieve-data"},{"id":"store-and-retrieve-data-ex22","language":"php","code":"$res1 = $r->hmset('bike:1', [\n            'model' => 'Deimos',\n            'brand' => 'Ergonom',\n            'type' => 'Enduro bikes',\n            'price' => 4972,\n        ]);\n\n        echo $res1 . PHP_EOL;\n        // >>> 4\n\n        $res2 = $r->hget('bike:1', 'model');\n        echo $res2 . PHP_EOL;\n        // >>> Deimos\n\n        $res3 = $r->hget('bike:1', 'price');\n        echo $res3 . PHP_EOL;\n        // >>> 4972\n\n        $res4 = $r->hgetall('bike:1');\n        echo json_encode($res3) . PHP_EOL;\n        // >>> {\"name\":\"Deimos\",\"brand\":\"Ergonom\",\"type\":\"Enduro bikes\",\"price\":\"4972\"}","section_id":"store-and-retrieve-data"},{"id":"store-and-retrieve-data-ex23","language":"python","code":"res1 = r.hset(\n    \"bike:1\",\n    mapping={\n        \"model\": \"Deimos\",\n        \"brand\": \"Ergonom\",\n        \"type\": \"Enduro bikes\",\n        \"price\": 4972,\n    },\n)\nprint(res1)\n# >>> 4\n\nres2 = r.hget(\"bike:1\", \"model\")\nprint(res2)\n# >>> 'Deimos'\n\nres3 = r.hget(\"bike:1\", \"price\")\nprint(res3)\n# >>> '4972'\n\nres4 = r.hgetall(\"bike:1\")\nprint(res4)\n# >>> {'model': 'Deimos', 'brand': 'Ergonom', 'type': 'Enduro bikes', 'price': '4972'}","section_id":"store-and-retrieve-data"},{"id":"store-and-retrieve-data-ex24","language":"ruby","code":"res1 = r.hset('bike:1', {\n  'model' => 'Deimos',\n  'brand' => 'Ergonom',\n  'type' => 'Enduro bikes',\n  'price' => 4972\n})\nputs res1 # >>> 4\n\nres2 = r.hget('bike:1', 'model')\nputs res2 # >>> Deimos\n\nres3 = r.hget('bike:1', 'price')\nputs res3 # >>> 4972\n\nres4 = r.hgetall('bike:1')\nputs res4.inspect\n# >>> {\"model\" => \"Deimos\", \"brand\" => \"Ergonom\", \"type\" => \"Enduro bikes\", \"price\" => \"4972\"}","section_id":"store-and-retrieve-data"},{"id":"store-and-retrieve-data-ex25","language":"rust","code":"let hash_fields = [\n            (\"model\", \"Deimos\"),\n            (\"brand\", \"Ergonom\"),\n            (\"type\", \"Enduro bikes\"),\n            (\"price\", \"4972\"),\n        ];\n\n        if let Ok(res) = r.hset_multiple(\"bike:1\", &hash_fields).await {\n            let res: String = res;\n            println!(\"{res}\");    // >>> OK\n        }\n\n        match r.hget(\"bike:1\", \"model\").await {\n            Ok(res) => {\n                let res: String = res;\n                println!(\"{res}\");   // >>> Deimos\n            },\n            Err(e) => {\n                println!(\"Error getting bike:1 model: {e}\");\n                return;\n            }\n        };\n\n        match r.hget(\"bike:1\", \"price\").await {\n            Ok(res) => {\n                let res: String = res;\n                println!(\"{res}\");   // >>> 4972\n            },\n            Err(e) => {\n                println!(\"Error getting bike:1 price: {e}\");\n                return;\n            }\n        };\n\n        match r.hgetall(\"bike:1\").await {\n            Ok(res) => {\n                let res: Vec<(String, String)> = res;\n                println!(\"{res:?}\");\n                // >>> [(\"model\", \"Deimos\"), (\"brand\", \"Ergonom\"), (\"type\", \"Enduro bikes\"), (\"price\", \"4972\")]\n            },\n            Err(e) => {\n                println!(\"Error getting bike:1: {e}\");\n                return;\n            }\n        };","section_id":"store-and-retrieve-data"},{"id":"store-and-retrieve-data-ex26","language":"rust","code":"let hash_fields = [\n            (\"model\", \"Deimos\"),\n            (\"brand\", \"Ergonom\"),\n            (\"type\", \"Enduro bikes\"),\n            (\"price\", \"4972\"),\n        ];\n\n        if let Ok(res) = r.hset_multiple(\"bike:1\", &hash_fields) {\n            let res: String = res;\n            println!(\"{res}\");    // >>> OK\n        }\n\n        match r.hget(\"bike:1\", \"model\") {\n            Ok(res) => {\n                let res: String = res;\n                println!(\"{res}\");   // >>> Deimos\n            },\n            Err(e) => {\n                println!(\"Error getting bike:1 model: {e}\");\n                return;\n            }\n        };\n\n        match r.hget(\"bike:1\", \"price\") {\n            Ok(res) => {\n                let res: String = res;\n                println!(\"{res}\");   // >>> 4972\n            },\n            Err(e) => {\n                println!(\"Error getting bike:1 price: {e}\");\n                return;\n            }\n        };\n\n        match r.hgetall(\"bike:1\") {\n            Ok(res) => {\n                let res: Vec<(String, String)> = res;\n                println!(\"{res:?}\");\n                // >>> [(\"model\", \"Deimos\"), (\"brand\", \"Ergonom\"), (\"type\", \"Enduro bikes\"), (\"price\", \"4972\")]\n            },\n            Err(e) => {\n                println!(\"Error getting bike:1: {e}\");\n                return;\n            }\n        };","section_id":"store-and-retrieve-data"},{"id":"scan-the-keyspace-ex0","language":"plaintext","code":"SCAN 0 MATCH \"bike:*\" COUNT 100","section_id":"scan-the-keyspace"}]}
{"schema_version":2,"id":"develop/get-started/faq","title":"Redis FAQ","url":"https://redis.io/docs/latest/develop/get-started/faq/","summary":"Commonly asked questions when getting started with Redis","aliases":["/get-started/faq/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:36:02+01:00","page_type":"content","content_hash":"66752170c5193d254a714725003c1244cf60ba513444f1fd8f7af8b092e1baf5","sections":[{"id":"how-is-redis-different-from-other-key-value-stores","title":"How is Redis different from other key-value stores?","role":"overview","text":"* Redis has a different evolution path in the key-value DBs where values can contain more complex data types, with atomic operations defined on those data types. Redis data types are closely related to fundamental data structures and are exposed to the programmer as such, without additional abstraction layers.\n* Redis is an in-memory but persistent on disk database, so it represents a different trade off where very high write and read speed is achieved with the limitation of data sets that can't be larger than memory. Another advantage of\nin-memory databases is that the memory representation of complex data structures\nis much simpler to manipulate compared to the same data structures on disk, so\nRedis can do a lot with little internal complexity. At the same time the\ntwo on-disk storage formats (RDB and AOF) don't need to be suitable for random\naccess, so they are compact and always generated in an append-only fashion\n(Even the AOF log rotation is an append-only operation, since the new version\nis generated from the copy of data in memory). However this design also involves\ndifferent challenges compared to traditional on-disk stores. Being the main data\nrepresentation on memory, Redis operations must be carefully handled to make sure\nthere is always an updated version of the data set on disk."},{"id":"whats-the-redis-memory-footprint","title":"What's the Redis memory footprint?","role":"content","text":"To give you a few examples (all obtained using 64-bit instances):\n\n* An empty instance uses ~ 3MB of memory.\n* 1 Million small Keys -> String Value pairs use ~ 85MB of memory.\n* 1 Million Keys -> Hash value, representing an object with 5 fields, use ~ 160 MB of memory.\n\nTesting your use case is trivial. Use the `redis-benchmark` utility to generate random data sets then check the space used with the `INFO memory` command.\n\n64-bit systems will use considerably more memory than 32-bit systems to store the same keys, especially if the keys and values are small. This is because pointers take 8 bytes in 64-bit systems. But of course the advantage is that you can\nhave a lot of memory in 64-bit systems, so in order to run large Redis servers a 64-bit system is more or less required. The alternative is sharding."},{"id":"why-does-redis-keep-its-entire-dataset-in-memory","title":"Why does Redis keep its entire dataset in memory?","role":"content","text":"In the past the Redis developers experimented with Virtual Memory and other systems in order to allow larger than RAM datasets, but after all we are very happy if we can do one thing well: data served from memory, disk used for storage. So for now there are no plans to create an on disk backend for Redis. Most of what\nRedis is, after all, a direct result of its current design.\n\nIf your real problem is not the total RAM needed, but the fact that you need\nto split your data set into multiple Redis instances, please read the\n[partitioning page](https://redis.io/docs/latest/operate/oss_and_stack/management/scaling) in this documentation for more info.\n\nRedis Ltd., the company sponsoring Redis development, has developed a\n\"Redis on Flash\" solution that uses a mixed RAM/flash approach for\nlarger data sets with a biased access pattern. You may check their offering\nfor more information, however this feature is not part of the Redis Open Source\ncode base."},{"id":"can-you-use-redis-with-a-disk-based-database","title":"Can you use Redis with a disk-based database?","role":"content","text":"Yes, a common design pattern involves taking very write-heavy small data\nin Redis (and data you need the Redis data structures to model your problem\nin an efficient way), and big *blobs* of data into an SQL or eventually\nconsistent on-disk database. Similarly sometimes Redis is used in order to\ntake in memory another copy of a subset of the same data stored in the on-disk\ndatabase. This may look similar to caching, but actually is a more advanced model\nsince normally the Redis dataset is updated together with the on-disk DB dataset,\nand not refreshed on cache misses."},{"id":"how-can-i-reduce-redis-overall-memory-usage","title":"How can I reduce Redis' overall memory usage?","role":"content","text":"A good practice is to consider memory consumption when mapping your logical data model to the physical data model within Redis. These considerations include using specific data types, key patterns, and normalization.\n\nBeyond data modeling, there is more info in the [Memory Optimization page](https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/memory-optimization)."},{"id":"what-happens-if-redis-runs-out-of-memory","title":"What happens if Redis runs out of memory?","role":"content","text":"Redis has built-in protections allowing the users to set a max limit on memory\nusage, using the `maxmemory` option in the configuration file to put a limit\nto the memory Redis can use. If this limit is reached, Redis will start to reply\nwith an error to write commands (but will continue to accept read-only\ncommands).\n\nYou can also configure Redis to evict keys when the max memory limit\nis reached. See the [eviction policy docs](https://redis.io/docs/latest/develop/reference/eviction) for more information on this."},{"id":"background-saving-fails-with-a-fork-error-on-linux","title":"Background saving fails with a fork() error on Linux?","role":"content","text":"Short answer: `echo 1 > /proc/sys/vm/overcommit_memory` :)\n\nAnd now the long one:\n\nThe Redis background saving schema relies on the copy-on-write semantic of the `fork` system call in\nmodern operating systems: Redis forks (creates a child process) that is an\nexact copy of the parent. The child process dumps the DB on disk and finally\nexits. In theory the child should use as much memory as the parent being a\ncopy, but actually thanks to the copy-on-write semantic implemented by most\nmodern operating systems the parent and child process will _share_ the common\nmemory pages. A page will be duplicated only when it changes in the child or in\nthe parent. Since in theory all the pages may change while the child process is\nsaving, Linux can't tell in advance how much memory the child will take, so if\nthe `overcommit_memory` setting is set to zero the fork will fail unless there is\nas much free RAM as required to really duplicate all the parent memory pages.\nIf you have a Redis dataset of 3 GB and just 2 GB of free\nmemory it will fail.\n\nSetting `overcommit_memory` to 1 tells Linux to relax and perform the fork in a\nmore optimistic allocation fashion, and this is indeed what you want for Redis.\n\nYou can refer to the [proc(5)](http://man7.org/linux/man-pages/man5/proc.5.html) man page for explanations of the\navailable values."},{"id":"are-redis-on-disk-snapshots-atomic","title":"Are Redis on-disk snapshots atomic?","role":"content","text":"Yes, the Redis background saving process is always forked when the server is\noutside of the execution of a command, so every command reported to be atomic\nin RAM is also atomic from the point of view of the disk snapshot."},{"id":"how-can-redis-use-multiple-cpus-or-cores","title":"How can Redis use multiple CPUs or cores?","role":"content","text":"It's not very frequent that CPU becomes your bottleneck with Redis, as usually Redis is either memory or network bound.\nFor instance, when using pipelining a Redis instance running on an average Linux system can deliver 1 million requests per second, so if your application mainly uses O(N) or O(log(N)) commands, it is hardly going to use too much CPU.\n\nHowever, to maximize CPU usage you can start multiple instances of Redis in\nthe same box and treat them as different servers. At some point a single\nbox may not be enough anyway, so if you want to use multiple CPUs you can\nstart thinking of some way to shard earlier.\n\nYou can find more information about using multiple Redis instances in the [Partitioning page](https://redis.io/docs/latest/operate/oss_and_stack/management/scaling).\n\nAs of version 4.0, Redis has started implementing threaded actions. For now this is limited to deleting objects in the background and blocking commands implemented via Redis modules. For subsequent releases, the plan is to make Redis more and more threaded."},{"id":"what-is-the-maximum-number-of-keys-a-single-redis-instance-can-hold-what-is-the-maximum-number-of-elements-in-a-hash-list-set-and-sorted-set","title":"What is the maximum number of keys a single Redis instance can hold? What is the maximum number of elements in a Hash, List, Set, and Sorted Set?","role":"content","text":"Redis can handle up to 2^32 keys, and was tested in practice to\nhandle at least 250 million keys per instance.\n\nEvery hash, list, set, and sorted set, can hold 2^32 elements.\n\nIn other words your limit is likely the available memory in your system."},{"id":"why-does-my-replica-have-a-different-number-of-keys-its-master-instance","title":"Why does my replica have a different number of keys its master instance?","role":"content","text":"If you use keys with limited time to live (Redis expires) this is normal behavior. This is what happens:\n\n* The primary generates an RDB file on the first synchronization with the replica.\n* The RDB file will not include keys already expired in the primary but which are still in memory.\n* These keys are still in the memory of the Redis primary, even if logically expired. They'll be considered non-existent, and their memory will be reclaimed later, either incrementally or explicitly on access. While these keys are not logically part of the dataset, they are accounted for in the [`INFO`](https://redis.io/docs/latest/commands/info) output and in the [`DBSIZE`](https://redis.io/docs/latest/commands/dbsize) command.\n* When the replica reads the RDB file generated by the primary, this set of keys will not be loaded.\n\nBecause of this, it's common for users with many expired keys to see fewer keys in the replicas. However, logically, the primary and replica will have the same content."},{"id":"where-does-the-name-redis-come-from","title":"Where does the name \"Redis\" come from?","role":"content","text":"Redis is an acronym that stands for **RE**mote **DI**ctionary **S**erver."},{"id":"why-did-salvatore-sanfilippo-start-the-redis-project","title":"Why did Salvatore Sanfilippo start the Redis project?","role":"content","text":"Salvatore originally created Redis to scale [LLOOGG](https://github.com/antirez/lloogg), a real-time log analysis tool. But after getting the basic Redis server working, he decided to share the work with other people and turn Redis into an open source project."},{"id":"how-is-redis-pronounced","title":"How is Redis pronounced?","role":"content","text":"\"Redis\" (/ˈrɛd-ɪs/) is pronounced like the word \"red\" plus the word \"kiss\" without the \"k\"."}],"examples":[]}
{"schema_version":2,"id":"develop/get-started","title":"Quick starts","url":"https://redis.io/docs/latest/develop/get-started/","summary":"Redis quick start guides","aliases":["/getting-started/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:36:02+01:00","children":[{"id":"develop/get-started/data-store","summary":"Understand how to use basic Redis data types","title":"Redis as an in-memory data structure store quick start guide","url":"https://redis.io/docs/latest/develop/get-started/data-store/"},{"id":"develop/get-started/search-tutorial","summary":"A guided, end-to-end tour of Redis Search, from data modeling to vector and hybrid search.","title":"Redis Search tutorial","url":"https://redis.io/docs/latest/develop/get-started/search-tutorial/"},{"id":"develop/get-started/rag","summary":"Understand how to use Redis for RAG use cases","title":"RAG with Redis","url":"https://redis.io/docs/latest/develop/get-started/rag/"},{"id":"develop/get-started/redis-in-ai","summary":"Understand key benefits of using Redis for AI.","title":"Redis for GenAI apps","url":"https://redis.io/docs/latest/develop/get-started/redis-in-ai/"},{"id":"develop/get-started/faq","summary":"Commonly asked questions when getting started with Redis","title":"Redis FAQ","url":"https://redis.io/docs/latest/develop/get-started/faq/"}],"page_type":"content","content_hash":"e7810ed9ae178fec0867bd5ed9a9fa9b40b0b1548d2f6cbe1f3b27b3ae9711d5","sections":[{"id":"content","title":"Content","role":"content","text":"Redis can be used as a database, cache, streaming engine, message broker, and more. To get started, follow one of these guides:\n\n1. [Data structure store](https://redis.io/docs/latest/develop/get-started/data-store) &mdash; get to know the core Redis data types as an in-memory data structure store.\n2. [Redis Search tutorial](https://redis.io/docs/latest/develop/get-started/search-tutorial) &mdash; a guided, end-to-end walkthrough of search and query for developers new to Redis: data modeling (hashes versus JSON), indexing, searching and filtering with `FT.SEARCH`, aggregation with `FT.AGGREGATE`, and vector and hybrid search.\n\nYou can find answers to frequently asked questions in the [FAQ](https://redis.io/docs/latest/develop/get-started/faq)."}],"examples":[]}
{"schema_version":2,"id":"develop/get-started/rag","title":"RAG with Redis","url":"https://redis.io/docs/latest/develop/get-started/rag/","summary":"Understand how to use Redis for RAG use cases","aliases":["/develop/ai/rag/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:36:02+01:00","page_type":"content","content_hash":"9421ad54bff54f75ce9255a114bfe6283e1bfe9762dac4b5607bdcfe9e90a94a","sections":[{"id":"what-is-retrieval-augmented-generation-rag","title":"What is Retrieval Augmented Generation (RAG)?","role":"overview","text":"Large Language Models (LLMs) generate human-like text but are limited by the data they were trained on. RAG enhances LLMs by integrating them with external, domain-specific data stored in a Redis [vector database](https://redis.io/docs/latest/develop/get-started/search-tutorial/vector-search).\n\nRAG involves three main steps:\n\n- **Retrieve**: Fetch relevant information from Redis using vector search and filters based on the user query.\n- **Augment**: Create a prompt for the LLM, including the user query, relevant context, and additional instructions.\n- **Generate**: Return the response generated by the LLM to the user.\n\nRAG enables LLMs to use real-time information, improving the accuracy and relevance of generated content.\nRedis is ideal for RAG due to its speed, versatility, and familiarity.\n\n> [!TIP]\n> To generate a complete RAG agent with document ingestion, hybrid retrieval, and citations, open the [AI agent builder](https://redis.io/docs/latest/develop/ai/agent-builder) and choose the **Knowledge Assistant** template."},{"id":"the-role-of-redis-in-rag","title":"The role of Redis in RAG","role":"content","text":"Redis provides a robust platform for managing real-time data. It supports the storage and retrieval of vectors, essential for handling large-scale, unstructured data and performing similarity searches. Key features and components of Redis that make it suitable for RAG include:\n\n1. **Vector database**: Stores and indexes vector embeddings that semantically represent unstructured data.\n1. **Semantic cache**: Caches frequently asked questions (FAQs) in a RAG pipeline. Using vector search, Redis retrieves similar previously answered questions, reducing LLM inference costs and latency.\n1. **LLM session manager**: Stores conversation history between an LLM and a user. Redis fetches recent and relevant portions of the chat history to provide context, improving the quality and accuracy of responses.\n1. **High performance and scalability**: Known for its [low latency and high throughput](https://redis.io/blog/benchmarking-results-for-vector-databases/), Redis is ideal for RAG systems and AI agents requiring rapid data retrieval and generation."},{"id":"build-a-rag-application-with-redis","title":"Build a RAG Application with Redis","role":"content","text":"To build a RAG application with Redis, follow these general steps:\n\n1. **Set up Redis**: Start by setting up a Redis instance and configuring it to handle vector data.\n\n1. **Use a Framework**:\n    1. **Redis Vector Library (RedisVL)**: [RedisVL](https://redis.io/docs/latest/integrate/redisvl/) enhances the development of generative AI applications by efficiently managing vectors and metadata. It allows for storage of vector embeddings and facilitates fast similarity searches, crucial for retrieving relevant information in RAG.\n    1. **Popular AI frameworks**: Redis integrates seamlessly with various AI frameworks and tools. For instance, combining Redis with [LangChain](https://python.langchain.com/v0.2/docs/integrations/vectorstores/redis/) or [LlamaIndex](https://developers.llamaindex.ai/python/examples/vector_stores/redisindexdemo/), libraries for building language models, enables developers to create sophisticated RAG pipelines. These integrations support efficient data management and building real-time LLM chains.\n    1. **Spring AI and Redis**: Using [Spring AI with Redis](https://redis.io/blog/building-a-rag-application-with-redis-and-spring-ai/) simplifies building RAG applications. Spring AI provides a structured approach to integrating AI capabilities into applications, while Redis handles data management, ensuring the RAG pipeline is efficient and scalable.\n\n1. **Embed and store data**: Convert your data into vector embeddings using a suitable model (e.g., BERT, GPT). Store these embeddings in Redis, where they can be quickly retrieved based on vector searches.\n\n1. **Integrate with a generative model**: Use a generative AI model that can leverage the retrieved data. The model will use the vectors stored in Redis to augment its generation process, ensuring the output is informed by relevant, up-to-date information.\n\n1. **Query and generate**: Implement the query logic to retrieve relevant vectors from Redis based on the input prompt. Feed these vectors into the generative model to produce augmented outputs."},{"id":"benefits-of-using-redis-for-rag","title":"Benefits of Using Redis for RAG","role":"content","text":"- **Efficiency**: The in-memory data store of Redis ensures that retrieval operations are performed with minimal latency.\n- **Scalability**: Redis scales horizontally, seamlessly handling growing volumes of data and queries.\n- **Flexibility**: Redis supports a variety of data structures and integrates with AI frameworks.\n\nIn summary, Redis offers a powerful and efficient platform for implementing RAG. Its vector management capabilities, high performance, and seamless integration with AI frameworks make it an ideal choice for enhancing generative AI applications with real-time data retrieval."},{"id":"resources","title":"Resources","role":"content","text":"- [RAG defined](https://redis.io/glossary/retrieval-augmented-generation/).\n- [RAG overview](https://redis.io/kb/doc/2ok7xd1drq/how-to-perform-retrieval-augmented-generation-rag-with-redis).\n- [Redis Vector Library (RedisVL)](https://redis.io/docs/latest/integrate/redisvl/) and [introductory article](https://redis.io/blog/introducing-the-redis-vector-library-for-enhancing-genai-development/).\n- [RAG with Redis and SpringAI](https://redis.io/blog/building-a-rag-application-with-redis-and-spring-ai/)\n- [Build a multimodal RAG app with LangChain and Redis](https://redis.io/blog/explore-the-new-multimodal-rag-template-from-langchain-and-redis/)\n- [Get hands-on with advanced Redis AI Recipes](https://github.com/redis-developer/redis-ai-resources)"},{"id":"continue-learning-with-redis-university","title":"Continue learning with Redis University","role":"content","text":""}],"examples":[]}
{"schema_version":2,"id":"develop/get-started/redis-in-ai","title":"Redis for GenAI apps","url":"https://redis.io/docs/latest/develop/get-started/redis-in-ai/","summary":"Understand key benefits of using Redis for AI.","aliases":["/develop/ai/genai-apps/"],"tags":["docs","develop"],"last_updated":"2026-09-11T15:36:02+01:00","page_type":"content","content_hash":"8289111e583758769cc8e543bbd3e71c568a1f1d4f771bd32f270510455274f3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis enables high-performance, scalable, and reliable data management, making it a key component for GenAI apps, chatbots, and AI agents. By leveraging Redis for fast data retrieval, caching, and vector search capabilities, you can enhance AI-powered interactions, reduce latency, and improve user experience.\n\nRedis excels in storing and indexing vector embeddings that semantically represent unstructured data. With vector search, Redis retrieves similar questions and relevant data, lowering LLM inference costs and latency. It fetches pertinent portions of chat history, enriching context for more accurate and relevant responses. These features make Redis an ideal choice for RAG systems and GenAI apps requiring fast data access."},{"id":"key-benefits-of-redis-in-genai-apps","title":"Key Benefits of Redis in GenAI Apps","role":"content","text":"- **Performance**: low-latency data access enables real-time interactions critical for AI-driven applications.\n- **Scalability**: designed to handle numerous concurrent connections, Redis is perfect for high-demand GenAI apps.\n- **Caching**: efficiently stores frequently accessed data and responses, reducing primary database load and accelerating response times.\n- **Session Management**: in-memory data structures simplify managing session states in conversational AI scenarios.\n- **Flexibility**: Redis supports diverse data structures (for example, strings, hashes, lists, sets), allowing tailored solutions for GenAI apps.\n\n[RedisVL](https://redis.io/docs/latest/develop/ai/redisvl) is a Python library with an integrated CLI, offering seamless integration with Redis to enhance GenAI applications.\n\n---"},{"id":"redis-use-cases-in-genai-apps","title":"Redis Use Cases in GenAI Apps","role":"content","text":"Explore how Redis optimizes various GenAI applications through specific use cases, tutorials, and demo code repositories."},{"id":"optimizing-ai-agent-performance","title":"Optimizing AI Agent Performance","role":"content","text":"Redis improves session persistence and caching for conversational agents managing high interaction volumes. See the [Flowise Conversational Agent with Redis](https://redis.io/learn/howtos/solutions/flowise/conversational-agent) tutorial and demo for implementation details."},{"id":"chatbot-development-and-management","title":"Chatbot Development and Management","role":"content","text":"Redis supports chatbot platforms by enabling:\n\n- **Caching**: enhances bot responsiveness.\n- **Session Management**: tracks conversation states for seamless interactions.\n- **Scalability**: handles high-traffic bot usage.\n\nLearn how to build a GenAI chatbot with Redis through the [LangChain and Redis tutorial](https://redis.io/learn/howtos/solutions/vector/gen-ai-chatbot). For customer engagement platforms integrating human support with chatbots, Redis ensures rapid access to frequently used data. Check out the tutorial on [AI-Powered Video Q&A Applications](https://redis.io/learn/howtos/solutions/vector/ai-qa-videos-langchain-redis-openai-google)."},{"id":"integrating-ml-frameworks-with-redis","title":"Integrating ML Frameworks with Redis","role":"content","text":"Machine learning frameworks leverage Redis for:\n\n- **Message Queuing**: ensures smooth communication between components.\n- **State Management**: tracks conversation states for real-time interactions.\n\nRefer to [Semantic Image-Based Queries Using LangChain and Redis](https://redis.io/learn/howtos/solutions/vector/image-summary-search) for a detailed guide. To expand your knowledge, enroll in the [Redis as a Vector Database course](https://redis.io/university/courses/ru402/), where you'll learn about integrations with tools like LangChain, LlamaIndex, FeatureForm, Amazon Bedrock, and AzureOpenAI."},{"id":"advancing-natural-language-processing","title":"Advancing Natural Language Processing","role":"content","text":"Redis enhances natural language understanding by:\n\n- **Session Management**: tracks user interactions for seamless conversational experiences.\n- **Caching**: reduces latency for frequent queries.\n\nSee the [Streaming LLM Output Using Redis Streams](https://redis.io/learn/howtos/solutions/streams/streaming-llm-output) tutorial for an in-depth walkthrough.\n\nRedis is a powerful tool to elevate your GenAI applications, enabling them to deliver superior performance, scalability, and user satisfaction."},{"id":"resources","title":"Resources","role":"content","text":"Check out the [Redis for AI](https://redis.io/docs/latest/develop/ai) documentation for getting started guides, concepts, ecosystem integrations, examples, and Python notebooks."},{"id":"continue-learning-with-redis-university","title":"Continue learning with Redis University","role":"content","text":""}],"examples":[]}
{"schema_version":2,"id":"develop/get-started/search-tutorial/aggregation","title":"Aggregate your data","url":"https://redis.io/docs/latest/develop/get-started/search-tutorial/aggregation/","summary":"Use FT.AGGREGATE to group, summarize, and transform your data with GROUPBY, REDUCE, and APPLY.","aliases":["/get-started/search-tutorial/aggregation/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:36:02+01:00","page_type":"content","content_hash":"f95f0bc409334c56921bc03078bc828027e65404d89d05ab71bbb5b03a57c2f7","sections":[{"id":"count-documents-per-group","title":"Count documents per group","role":"content","text":"The most common aggregation is a grouped count. This groups every product by `category` and counts how many fall into each. `REDUCE COUNT 0` counts the documents in each group, and `AS count` names the result:\n\nGrouped count: Use GROUPBY with REDUCE COUNT to count documents in each group\n\n**Difficulty:** Beginner\n\n**Commands:** FT.AGGREGATE\n\n**Complexity:**\n- FT.AGGREGATE: O(1)\n\n**Available in:** Redis CLI, Python\n\n##### Redis CLI\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nThe `\"*\"` after the index name is a query expression, exactly like in `FT.SEARCH`. Here it means \"aggregate over all documents\", but you could narrow the input first, for example `@price:[0 100]` to aggregate only the cheaper products. `GROUPBY 1 @category` reads as \"group by one field: `category`\"."},{"id":"compute-an-average-per-group","title":"Compute an average per group","role":"content","text":"Swap `COUNT` for a different reducer to compute other summaries. This calculates the average price in each category and sorts the groups from most to least expensive with `SORTBY`:\n\nGrouped average: Use REDUCE AVG to average a numeric field per group, then order groups with SORTBY\n\n**Difficulty:** Intermediate\n\n**Commands:** FT.AGGREGATE\n\n**Complexity:**\n- FT.AGGREGATE: O(1)\n\n**Available in:** Redis CLI, Python\n\n##### Redis CLI\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\n`REDUCE AVG 1 @price` reads as \"apply the AVG reducer to one field: `price`\". The `SORTBY 2 @avg_price DESC` clause sorts by the computed `avg_price` value; the `2` is the number of arguments that follow (`@avg_price` and `DESC`). Other reducers include `SUM`, `MIN`, `MAX`, and `COUNT_DISTINCT`; see the [aggregation reference](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/aggregations) for the full list."},{"id":"calculate-new-values-with-apply","title":"Calculate new values with APPLY","role":"content","text":"`APPLY` evaluates an expression against each record and adds the result as a new field. This takes the Audio products, loads their name and price, and computes a 10%-off `sale_price`:\n\nCalculated field: Use APPLY to derive a new value (a discounted price) from an existing field\n\n**Difficulty:** Intermediate\n\n**Commands:** FT.AGGREGATE\n\n**Complexity:**\n- FT.AGGREGATE: O(1)\n\n**Available in:** Redis CLI, Python\n\n##### Redis CLI\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nThe `LOAD 2 name price` clause pulls those two fields into the pipeline so the expression can use them and so they appear in the output. `APPLY` does not group anything; it transforms each record in place."},{"id":"build-a-pipeline","title":"Build a pipeline","role":"content","text":"The real power of `FT.AGGREGATE` is chaining these steps. This finds the average rating per brand and returns the highest-rated brands first &mdash; a simple \"best brands\" leaderboard:\n\nPipeline: Combine GROUPBY, REDUCE, and SORTBY to rank brands by average rating\n\n**Difficulty:** Intermediate\n\n**Commands:** FT.AGGREGATE\n\n**Complexity:**\n- FT.AGGREGATE: O(1)\n\n**Available in:** Redis CLI, Python\n\n##### Redis CLI\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\n(The output is truncated; eight brands are returned in all.) You can keep extending the pipeline &mdash; apply multiple reducers under one `GROUPBY`, chain a second `GROUPBY`, add `FILTER` and `LIMIT` steps, and more. See the [aggregation queries](https://redis.io/docs/latest/develop/ai/search-and-query/query/aggregation) guide for deeper examples.\n\n> [!NOTE] Try it in Redis Insight\n> Aggregation results are tabular by nature, so they are especially easy to read in the [Redis Insight Search workspace](https://redis.io/docs/latest/develop/tools/insight/search-workspace). Paste any `FT.AGGREGATE` command from this page into the query editor to see each group as a row."},{"id":"next-steps","title":"Next steps","role":"content","text":"You can now find, filter, and summarize structured data. The final step goes beyond keywords and exact values to search by *meaning*. Continue to [vector and hybrid search](https://redis.io/docs/latest/develop/get-started/search-tutorial/vector-search)."}],"examples":[{"id":"count-documents-per-group-ex0","language":"plaintext","code":"> FT.AGGREGATE idx:catalog \"*\" GROUPBY 1 @category REDUCE COUNT 0 AS count\n1) (integer) 6\n2) 1) \"category\"\n   2) \"Audio\"\n   3) \"count\"\n   4) \"3\"\n3) 1) \"category\"\n   2) \"Computers\"\n   3) \"count\"\n   4) \"2\"\n4) 1) \"category\"\n   2) \"Accessories\"\n   3) \"count\"\n   4) \"2\"\n5) 1) \"category\"\n   2) \"Home\"\n   3) \"count\"\n   4) \"2\"\n6) 1) \"category\"\n   2) \"Wearables\"\n   3) \"count\"\n   4) \"2\"\n7) 1) \"category\"\n   2) \"Cameras\"\n   3) \"count\"\n   4) \"1\"","section_id":"count-documents-per-group"},{"id":"count-documents-per-group-ex1","language":"python","code":"req = aggregations.AggregateRequest(\"*\").group_by(\n    \"@category\", reducers.count().alias(\"count\")\n)\nres = index.aggregate(req).rows\nprint(res)\n# >>> [['category', 'Audio', 'count', '3'], ['category', 'Computers', 'count', '2'], ...]","section_id":"count-documents-per-group"},{"id":"compute-an-average-per-group-ex0","language":"plaintext","code":"> FT.AGGREGATE idx:catalog \"*\" GROUPBY 1 @category REDUCE AVG 1 @price AS avg_price SORTBY 2 @avg_price DESC\n1) (integer) 6\n2) 1) \"category\"\n   2) \"Computers\"\n   3) \"avg_price\"\n   4) \"864.495\"\n3) 1) \"category\"\n   2) \"Cameras\"\n   3) \"avg_price\"\n   4) \"299\"\n4) 1) \"category\"\n   2) \"Wearables\"\n   3) \"avg_price\"\n   4) \"164.495\"\n5) 1) \"category\"\n   2) \"Audio\"\n   3) \"avg_price\"\n   4) \"139.826666667\"\n6) 1) \"category\"\n   2) \"Accessories\"\n   3) \"avg_price\"\n   4) \"89.495\"\n7) 1) \"category\"\n   2) \"Home\"\n   3) \"avg_price\"\n   4) \"86.995\"","section_id":"compute-an-average-per-group"},{"id":"compute-an-average-per-group-ex1","language":"python","code":"req = (\n    aggregations.AggregateRequest(\"*\")\n    .group_by(\"@category\", reducers.avg(\"@price\").alias(\"avg_price\"))\n    .sort_by(aggregations.Desc(\"@avg_price\"))\n)\nres = index.aggregate(req).rows\nprint(res)\n# >>> [['category', 'Computers', 'avg_price', '864.495'], ...]","section_id":"compute-an-average-per-group"},{"id":"calculate-new-values-with-apply-ex0","language":"plaintext","code":"> FT.AGGREGATE idx:catalog \"@category:{Audio}\" LOAD 2 name price APPLY \"@price - (@price * 0.1)\" AS sale_price\n1) (integer) 3\n2) 1) \"name\"\n   2) \"Aurora AcousticPro Headphones\"\n   3) \"price\"\n   4) \"199.99\"\n   5) \"sale_price\"\n   6) \"179.991\"\n3) 1) \"name\"\n   2) \"Sonus Boom Portable Speaker\"\n   3) \"price\"\n   4) \"129.5\"\n   5) \"sale_price\"\n   6) \"116.55\"\n4) 1) \"name\"\n   2) \"Aurora BudsMini Earbuds\"\n   3) \"price\"\n   4) \"89.99\"\n   5) \"sale_price\"\n   6) \"80.991\"","section_id":"calculate-new-values-with-apply"},{"id":"calculate-new-values-with-apply-ex1","language":"python","code":"req = (\n    aggregations.AggregateRequest(\"@category:{Audio}\")\n    .load(\"name\", \"price\")\n    .apply(sale_price=\"@price - (@price * 0.1)\")\n)\nres = index.aggregate(req).rows\nprint(res)\n# >>> [['name', 'Aurora AcousticPro Headphones', 'price', '199.99', 'sale_price', '179.991'], ...]","section_id":"calculate-new-values-with-apply"},{"id":"build-a-pipeline-ex0","language":"plaintext","code":"> FT.AGGREGATE idx:catalog \"*\" GROUPBY 1 @brand REDUCE AVG 1 @rating AS avg_rating SORTBY 2 @avg_rating DESC\n1) (integer) 8\n2) 1) \"brand\"\n   2) \"Clackr\"\n   3) \"avg_rating\"\n   4) \"4.8\"\n3) 1) \"brand\"\n   2) \"Pixma\"\n   3) \"avg_rating\"\n   4) \"4.55\"\n4) 1) \"brand\"\n   2) \"Sonus\"\n   3) \"avg_rating\"\n   4) \"4.5\"\n5) 1) \"brand\"\n   2) \"Aurora\"\n   3) \"avg_rating\"\n   4) \"4.45\"","section_id":"build-a-pipeline"},{"id":"build-a-pipeline-ex1","language":"python","code":"req = (\n    aggregations.AggregateRequest(\"*\")\n    .group_by(\"@brand\", reducers.avg(\"@rating\").alias(\"avg_rating\"))\n    .sort_by(aggregations.Desc(\"@avg_rating\"))\n)\nres = index.aggregate(req).rows\nprint(res)\n# >>> [['brand', 'Clackr', 'avg_rating', '4.8'], ['brand', 'Pixma', 'avg_rating', '4.55'], ...]","section_id":"build-a-pipeline"}]}
{"schema_version":2,"id":"develop/get-started/search-tutorial/data-modeling","title":"Data modeling for search","url":"https://redis.io/docs/latest/develop/get-started/search-tutorial/data-modeling/","summary":"Learn how to store records in Redis so they can be searched, and when to choose hashes versus JSON documents.","aliases":["/get-started/search-tutorial/data-modeling/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:36:02+01:00","page_type":"content","content_hash":"5109d77beea0f4126d81b144b9a0d11654f1e7dc90b2a59fe0189d4b516d0f79","sections":[{"id":"a-record-as-a-hash","title":"A record as a hash","role":"content","text":"A [hash](https://redis.io/docs/latest/develop/data-types/hashes) stores a flat set of field-value pairs under a single key. It is the simplest way to represent a record and maps neatly onto a row of fields:\n\nFoundational: Store a record as a hash with HSET when your data is a flat set of fields\n\n**Difficulty:** Beginner\n\n**Commands:** HSET\n\n**Complexity:**\n- HSET: O(1)\n\n**Available in:** Redis CLI, Python\n\n##### Redis CLI\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nHashes are compact and fast, but they are **flat**: every value is a string or number. There is no natural place to put a nested object (like a `specs` sub-record) or a list of values (like multiple `features`) without flattening or encoding it yourself."},{"id":"a-record-as-a-json-document","title":"A record as a JSON document","role":"content","text":"The [JSON](https://redis.io/docs/latest/develop/data-types/json) data type stores a full JSON document under a key. It can represent nested objects and arrays directly, which matches how application data usually looks:\n\nFoundational: Store a record as a JSON document with JSON.SET when your data has nested objects or arrays\n\n**Difficulty:** Beginner\n\n**Commands:** JSON.SET\n\n**Complexity:**\n- JSON.SET: O(M+N)\n\n**Available in:** Redis CLI, Python\n\n##### Redis CLI\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nNotice that `features` is a real array and `specs` is a real nested object. You did not have to flatten them."},{"id":"which-should-you-use","title":"Which should you use?","role":"content","text":"Both hashes and JSON documents can be indexed and searched by Redis Search, so you can search either one. Use this as a guide:\n\n| Choose **hashes** when... | Choose **JSON** when... |\n| --- | --- |\n| Your records are flat (no nesting). | Your records have nested objects or arrays. |\n| You want the smallest possible memory footprint. | You want your stored shape to match your application objects. |\n| You frequently update individual fields. | You want to read, update, or index nested paths directly. |\n\nFor this tutorial, the catalog records have arrays (`features`) and a nested object (`specs`), so **we will use JSON documents** for the rest of the tutorial. If you are coming from a background where every record is a flat row, JSON is also a gentle way to keep your existing object shapes.\n\n> [!NOTE]\n> This is a modeling choice, not a limitation. The indexing and query commands you will learn (`FT.CREATE`, `FT.SEARCH`, `FT.AGGREGATE`) work with both hashes and JSON. The main practical difference shows up when indexing arrays, which you will see on the [next page](https://redis.io/docs/latest/develop/get-started/search-tutorial/indexing)."},{"id":"load-the-dataset","title":"Load the dataset","role":"content","text":"Now load the full catalog of 12 products. Each product is stored as a JSON document under a key with the prefix `product:`. The key prefix matters: in the next step you will tell Redis to index every key that starts with `product:`.\n\nFoundational: Load the tutorial dataset as JSON documents under a shared key prefix using JSON.SET\n\n**Difficulty:** Beginner\n\n**Commands:** JSON.SET\n\n**Complexity:**\n- JSON.SET: O(M+N)\n\n**Available in:** Redis CLI, Python\n\n##### Redis CLI\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nYou can read any single document back by its key with [JSON.GET](https://redis.io/docs/latest/commands/json.get):\n\nFoundational: Read one JSON document back by its key with JSON.GET\n\n**Difficulty:** Beginner\n\n**Commands:** JSON.GET\n\n**Complexity:**\n- JSON.GET: O(N)\n\n**Available in:** Redis CLI, Python\n\n##### Redis CLI\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nAt this point the data is in Redis, but you can only fetch it one key at a time. To *search* across all products, you need an index."},{"id":"next-steps","title":"Next steps","role":"content","text":"Continue to [creating an index](https://redis.io/docs/latest/develop/get-started/search-tutorial/indexing) to make this data searchable."}],"examples":[{"id":"a-record-as-a-hash-ex0","language":"plaintext","code":"> HSET hproduct:1 name \"Aurora AcousticPro Headphones\" brand \"Aurora\" category \"Audio\" price 199.99 rating 4.6\n(integer) 5","section_id":"a-record-as-a-hash"},{"id":"a-record-as-a-hash-ex1","language":"python","code":"r.hset(\n    \"product:1\",\n    mapping={\n        \"name\": \"Aurora AcousticPro Headphones\",\n        \"brand\": \"Aurora\",\n        \"category\": \"Audio\",\n        \"price\": 199.99,\n        \"rating\": 4.6,\n    },\n)\n# >>> 5","section_id":"a-record-as-a-hash"},{"id":"a-record-as-a-json-document-ex0","language":"plaintext","code":"> JSON.SET product:1 $ '{\"name\":\"Aurora AcousticPro Headphones\",\"brand\":\"Aurora\",\"category\":\"Audio\",\"price\":199.99,\"rating\":4.6,\"features\":[\"wireless\",\"noise-cancelling\",\"bluetooth\"],\"specs\":{\"color\":\"midnight black\",\"weight_grams\":268}}'","section_id":"a-record-as-a-json-document"},{"id":"a-record-as-a-json-document-ex1","language":"python","code":"r.json().set(\n    \"product:1\",\n    Path.root_path(),\n    {\n        \"name\": \"Aurora AcousticPro Headphones\",\n        \"brand\": \"Aurora\",\n        \"category\": \"Audio\",\n        \"price\": 199.99,\n        \"rating\": 4.6,\n        \"features\": [\"wireless\", \"noise-cancelling\", \"bluetooth\"],\n        \"specs\": {\"color\": \"midnight black\", \"weight_grams\": 268},\n    },\n)\n# >>> True","section_id":"a-record-as-a-json-document"},{"id":"load-the-dataset-ex0","language":"plaintext","code":"> JSON.SET product:2 $ '{\"name\":\"Aurora BudsMini Earbuds\",\"brand\":\"Aurora\",\"category\":\"Audio\",\"description\":\"Tiny true-wireless earbuds with a secure in-ear fit and sweat resistance for workouts. The compact charging case slips into a pocket and delivers three full recharges on the go.\",\"price\":89.99,\"rating\":4.3,\"review_count\":942,\"stock\":130,\"release_year\":2023,\"features\":[\"wireless\",\"bluetooth\",\"in-ear\",\"water-resistant\"],\"specs\":{\"color\":\"pearl white\",\"weight_grams\":5,\"warranty_years\":1}}'\n... output truncated for AI-facing Markdown ...\n> JSON.SET product:12 $ '{\"name\":\"Vista Action Cam 4K\",\"brand\":\"Vista\",\"category\":\"Cameras\",\"description\":\"A pocket-sized action camera that shoots stabilized 4K video and is waterproof without a case. Mount it on a helmet or bike and capture your adventures in sharp detail.\",\"price\":299.0,\"rating\":4.3,\"review_count\":455,\"stock\":33,\"release_year\":2023,\"features\":[\"camera\",\"4k\",\"waterproof\",\"wifi\"],\"specs\":{\"color\":\"black\",\"weight_grams\":128,\"warranty_years\":1}}'","section_id":"load-the-dataset"},{"id":"load-the-dataset-ex1","language":"python","code":"catalog = [\n    {\n        \"name\": \"Aurora AcousticPro Headphones\",\n        \"brand\": \"Aurora\",\n        \"category\": \"Audio\",\n        \"description\": (\n            \"Over-ear wireless headphones with active noise cancelling and a \"\n            \"40-hour battery. Plush memory-foam earcups and a lightweight frame \"\n            \"make them comfortable for all-day listening, whether you are \"\n            \"commuting, working, or relaxing at home.\"\n        ),\n        \"price\": 199.99,\n        \"rating\": 4.6,\n        \"review_count\": 1284,\n        \"stock\": 42,\n        \"release_year\": 2024,\n        \"features\": [\"wireless\", \"noise-cancelling\", \"bluetooth\", \"over-ear\"],\n        \"specs\": {\"color\": \"midnight black\", \"weight_grams\": 268, \"warranty_years\": 2},\n    },\n    {\n        \"name\": \"Aurora BudsMini Earbuds\",\n        \"brand\": \"Aurora\",\n        \"category\": \"Audio\",\n        \"description\": (\n            \"Tiny true-wireless earbuds with a secure in-ear fit and sweat \"\n            \"resistance for workouts. The compact charging case slips into a \"\n            \"pocket and delivers three full recharges on the go.\"\n        ),\n        \"price\": 89.99,\n        \"rating\": 4.3,\n        \"review_count\": 942,\n        \"stock\": 130,\n        \"release_year\": 2023,\n        \"features\": [\"wireless\", \"bluetooth\", \"in-ear\", \"water-resistant\"],\n        \"specs\": {\"color\": \"pearl white\", \"weight_grams\": 5, \"warranty_years\": 1},\n    },\n    {\n        \"name\": \"Sonus Boom Portable Speaker\",\n        \"brand\": \"Sonus\",\n        \"category\": \"Audio\",\n        \"description\": (\n            \"A rugged portable Bluetooth speaker with deep bass and a waterproof \"\n            \"shell. Toss it in a bag for the beach or a campsite and enjoy \"\n            \"room-filling sound for up to 20 hours per charge.\"\n        ),\n        \"price\": 129.5,\n        \"rating\": 4.5,\n        \"review_count\": 512,\n        \"stock\": 64,\n        \"release_year\": 2024,\n        \"features\": [\"wireless\", \"bluetooth\", \"portable\", \"waterproof\"],\n        \"specs\": {\"color\": \"slate gray\", \"weight_grams\": 540, \"warranty_years\": 1},\n    },\n    {\n        \"name\": \"Pixma Vortex 15 Laptop\",\n        \"brand\": \"Pixma\",\n        \"category\": \"Computers\",\n        \"description\": (\n            \"A thin-and-light 15-inch laptop with a fast multi-core processor, \"\n            \"16 GB of memory, and a speedy solid-state drive. The backlit keyboard \"\n            \"and bright display make it a capable companion for work and study.\"\n        ),\n        \"price\": 1399.0,\n        \"rating\": 4.7,\n        \"review_count\": 318,\n        \"stock\": 18,\n        \"release_year\": 2024,\n        \"features\": [\"laptop\", \"ssd\", \"backlit-keyboard\", \"lightweight\"],\n        \"specs\": {\"color\": \"space silver\", \"weight_grams\": 1600, \"warranty_years\": 2},\n    },\n    {\n        \"name\": \"Pixma UltraView 27 Monitor\",\n        \"brand\": \"Pixma\",\n        \"category\": \"Computers\",\n        \"description\": (\n            \"A 27-inch 4K monitor with an IPS panel for accurate colors and wide \"\n            \"viewing angles. A single USB-C cable carries video and power, keeping \"\n            \"your desk tidy.\"\n        ),\n        \"price\": 329.99,\n        \"rating\": 4.4,\n        \"review_count\": 221,\n        \"stock\": 27,\n        \"release_year\": 2023,\n        \"features\": [\"monitor\", \"4k\", \"ips\", \"usb-c\"],\n        \"specs\": {\"color\": \"black\", \"weight_grams\": 5200, \"warranty_years\": 3},\n    },\n    {\n        \"name\": \"Clackr Mechanical Keyboard\",\n        \"brand\": \"Clackr\",\n        \"category\": \"Accessories\",\n        \"description\": (\n            \"A compact mechanical keyboard with tactile switches, per-key RGB \"\n            \"lighting, and wireless connectivity. Hot-swappable switches let you \"\n            \"tune the typing feel without soldering.\"\n        ),\n        \"price\": 119.0,\n        \"rating\": 4.8,\n        \"review_count\": 1502,\n        \"stock\": 88,\n        \"release_year\": 2024,\n        \"features\": [\"keyboard\", \"mechanical\", \"rgb\", \"wireless\"],\n        \"specs\": {\"color\": \"graphite\", \"weight_grams\": 720, \"warranty_years\": 2},\n    },\n    {\n        \"name\": \"Glide Pro Wireless Mouse\",\n        \"brand\": \"Glide\",\n        \"category\": \"Accessories\",\n        \"description\": (\n            \"An ergonomic wireless mouse with a high-precision sensor and a \"\n            \"contoured shape that reduces wrist strain. A single charge lasts for \"\n            \"weeks of everyday use.\"\n        ),\n        \"price\": 59.99,\n        \"rating\": 4.2,\n        \"review_count\": 869,\n        \"stock\": 150,\n        \"release_year\": 2022,\n        \"features\": [\"mouse\", \"wireless\", \"ergonomic\"],\n        \"specs\": {\"color\": \"charcoal\", \"weight_grams\": 98, \"warranty_years\": 1},\n    },\n    {\n        \"name\": \"Pulse Series 6 Smartwatch\",\n        \"brand\": \"Pulse\",\n        \"category\": \"Wearables\",\n        \"description\": (\n            \"A sleek smartwatch with built-in GPS, continuous heart-rate \"\n            \"monitoring, and water resistance for swimming. Track workouts, sleep, \"\n            \"and notifications from your wrist.\"\n        ),\n        \"price\": 249.0,\n        \"rating\": 4.5,\n        \"review_count\": 1733,\n        \"stock\": 51,\n        \"release_year\": 2024,\n        \"features\": [\"smartwatch\", \"gps\", \"heart-rate\", \"water-resistant\"],\n        \"specs\": {\"color\": \"rose gold\", \"weight_grams\": 38, \"warranty_years\": 1},\n    },\n    {\n        \"name\": \"Pulse Band Fitness Tracker\",\n        \"brand\": \"Pulse\",\n        \"category\": \"Wearables\",\n        \"description\": (\n            \"A lightweight fitness band that tracks steps, heart rate, and sleep \"\n            \"stages. The slim screen shows daily progress and the battery lasts a \"\n            \"full week between charges.\"\n        ),\n        \"price\": 79.99,\n        \"rating\": 4.1,\n        \"review_count\": 2210,\n        \"stock\": 200,\n        \"release_year\": 2023,\n        \"features\": [\"fitness-tracker\", \"heart-rate\", \"sleep-tracking\"],\n        \"specs\": {\"color\": \"ocean blue\", \"weight_grams\": 24, \"warranty_years\": 1},\n    },\n    {\n        \"name\": \"Lumi Glow Smart Bulb\",\n        \"brand\": \"Lumi\",\n        \"category\": \"Home\",\n        \"description\": (\n            \"A color-changing smart bulb that connects over Wi-Fi and works with \"\n            \"voice assistants. Dim it for movie night or set a warm white for \"\n            \"reading, all from your phone.\"\n        ),\n        \"price\": 24.99,\n        \"rating\": 4.0,\n        \"review_count\": 640,\n        \"stock\": 320,\n        \"release_year\": 2022,\n        \"features\": [\"smart-home\", \"wifi\", \"dimmable\", \"color\"],\n        \"specs\": {\"color\": \"white\", \"weight_grams\": 70, \"warranty_years\": 2},\n    },\n    {\n        \"name\": \"Lumi Climate Smart Thermostat\",\n        \"brand\": \"Lumi\",\n        \"category\": \"Home\",\n        \"description\": (\n            \"A learning smart thermostat that adjusts heating and cooling to your \"\n            \"routine and helps lower energy bills. The crisp display and Wi-Fi app \"\n            \"make scheduling effortless.\"\n        ),\n        \"price\": 149.0,\n        \"rating\": 4.6,\n        \"review_count\": 388,\n        \"stock\": 75,\n        \"release_year\": 2024,\n        \"features\": [\"smart-home\", \"wifi\", \"energy-saving\"],\n        \"specs\": {\"color\": \"white\", \"weight_grams\": 210, \"warranty_years\": 3},\n    },\n    {\n        \"name\": \"Vista Action Cam 4K\",\n        \"brand\": \"Vista\",\n        \"category\": \"Cameras\",\n        \"description\": (\n            \"A pocket-sized action camera that shoots stabilized 4K video and is \"\n            \"waterproof without a case. Mount it on a helmet or bike and capture \"\n            \"your adventures in sharp detail.\"\n        ),\n        \"price\": 299.0,\n        \"rating\": 4.3,\n        \"review_count\": 455,\n        \"stock\": 33,\n        \"release_year\": 2023,\n        \"features\": [\"camera\", \"4k\", \"waterproof\", \"wifi\"],\n        \"specs\": {\"color\": \"black\", \"weight_grams\": 128, \"warranty_years\": 1},\n    },\n]\n\nfor product_id, product in enumerate(catalog, start=1):\n    r.json().set(f\"product:{product_id}\", Path.root_path(), product)","section_id":"load-the-dataset"},{"id":"load-the-dataset-ex2","language":"plaintext","code":"> JSON.GET product:1 $.name\n\"[\\\"Aurora AcousticPro Headphones\\\"]\"","section_id":"load-the-dataset"},{"id":"load-the-dataset-ex3","language":"python","code":"res = r.json().get(\"product:1\", \"$.name\")\nprint(res)  # >>> ['Aurora AcousticPro Headphones']","section_id":"load-the-dataset"}]}
{"schema_version":2,"id":"develop/get-started/search-tutorial","title":"Redis Search tutorial","url":"https://redis.io/docs/latest/develop/get-started/search-tutorial/","summary":"A guided, end-to-end tour of Redis Search, from data modeling to vector and hybrid search.","aliases":["/get-started/search-tutorial/","/get-started/document-database/","/develop/get-started/document-database/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:36:02+01:00","children":[{"id":"develop/get-started/search-tutorial/data-modeling","summary":"Learn how to store records in Redis so they can be searched, and when to choose hashes versus JSON documents.","title":"Data modeling for search","url":"https://redis.io/docs/latest/develop/get-started/search-tutorial/data-modeling/"},{"id":"develop/get-started/search-tutorial/indexing","summary":"Create a search index over your JSON documents with FT.CREATE, choose the right field types, and understand how Redis indexes arrays.","title":"Create an index","url":"https://redis.io/docs/latest/develop/get-started/search-tutorial/indexing/"},{"id":"develop/get-started/search-tutorial/search","summary":"Use FT.SEARCH to find documents by full text, tags, and numeric ranges, and to return only the fields you want.","title":"Search and filter your data","url":"https://redis.io/docs/latest/develop/get-started/search-tutorial/search/"},{"id":"develop/get-started/search-tutorial/aggregation","summary":"Use FT.AGGREGATE to group, summarize, and transform your data with GROUPBY, REDUCE, and APPLY.","title":"Aggregate your data","url":"https://redis.io/docs/latest/develop/get-started/search-tutorial/aggregation/"},{"id":"develop/get-started/search-tutorial/vector-search","summary":"Search by meaning with vector embeddings, run KNN queries with FT.SEARCH, and combine keywords with semantic similarity using FT.HYBRID.","title":"Vector and hybrid search","url":"https://redis.io/docs/latest/develop/get-started/search-tutorial/vector-search/"}],"page_type":"content","content_hash":"cf4b590c98bd4f011590c430a13cd5aa15da6400840fdf22bbd183385323296d","sections":[{"id":"the-example-a-product-catalog","title":"The example: a product catalog","role":"content","text":"Throughout the tutorial you will work with a small **online store catalog**. Each record is a product with a name, brand, category, free-text description, price, customer rating, and other attributes:\n\n[code example]\n\nThis kind of data is a good fit for search because people want to query it in many different ways: by keyword (\"wireless headphones\"), by filter (under $100, in the Audio category), by summary (average price per category), and increasingly by meaning (\"something for listening to music on a run\")."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"You need a running Redis instance that includes Redis Search and the JSON data type. The easiest options are:\n\n- **Redis Cloud** &mdash; create a [free account](https://redis.io/try-free/). A free database comes with all the Redis Open Source features, including Redis Search and JSON.\n- **Local install** &mdash; follow the [install guide](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack) to run Redis Open Source on your own machine.\n\n> [!NOTE]\n> The vector and hybrid search examples in the [last step](https://redis.io/docs/latest/develop/get-started/search-tutorial/vector-search) use features that require Redis 8.8 or later. The earlier steps work on any recent version of Redis with Redis Search."},{"id":"choose-your-tool","title":"Choose your tool","role":"content","text":"You can follow along using whichever tool you prefer. Every example in this tutorial is shown for each of them:\n\n- **`redis-cli`** &mdash; the command-line client included with Redis. It is the quickest way to try commands and see raw results. This is the default tab in every code example.\n- **[Redis Insight](https://redis.io/docs/latest/develop/tools/insight)** &mdash; a free graphical tool for Redis. Its [Search workspace](https://redis.io/docs/latest/develop/tools/insight/search-workspace) lets you browse indexes and run full-text, vector, and hybrid queries in a schema-aware editor. If you prefer to see your data and results visually, this is a great choice.\n- **A client library** &mdash; for real applications you will use Redis from your programming language of choice. Each example includes tabs for languages such as Python and Node.js.\n\nThroughout the tutorial, look for **\"Try it in Redis Insight\"** tips that show how to run the same query in the graphical editor."},{"id":"connect","title":"Connect","role":"content","text":"First, connect to your Redis database. The following example connects with `redis-cli` to a server running on `localhost` (`-h 127.0.0.1`) and listening on the default port (`-p 6379`):\n\n    redis-cli -h 127.0.0.1 -p 6379\n\nFoundational: Connect to a Redis server with redis-cli using host and port parameters\n\n**Difficulty:** Beginner\n\n**Available in:** Python\n\n##### Python\n\n[code example]\n\n\n\n<br/>\n\n&nbsp;\n\n> [!TIP]\n> If you are using Redis Cloud, copy the connection details from your database's configuration page. A Cloud connection string has the form `host:port`, for example `redis-16379.c283.us-east-1-4.ec2.cloud.redislabs.com:16379`. You also need the database username and password, which you can pass to your client or supply with the [AUTH command](https://redis.io/docs/latest/commands/auth) after connecting."},{"id":"next-steps","title":"Next steps","role":"content","text":"Ready to begin? Start with [data modeling](https://redis.io/docs/latest/develop/get-started/search-tutorial/data-modeling) to learn how to store your records so Redis can search them."}],"examples":[{"id":"the-example-a-product-catalog-ex0","language":"json","code":"{\n  \"name\": \"Aurora AcousticPro Headphones\",\n  \"brand\": \"Aurora\",\n  \"category\": \"Audio\",\n  \"description\": \"Over-ear wireless headphones with active noise cancelling ...\",\n  \"price\": 199.99,\n  \"rating\": 4.6,\n  \"review_count\": 1284,\n  \"stock\": 42,\n  \"release_year\": 2024,\n  \"features\": [\"wireless\", \"noise-cancelling\", \"bluetooth\", \"over-ear\"]\n}","section_id":"the-example-a-product-catalog"},{"id":"connect-ex0","language":"python","code":"r = redis.Redis(host=\"localhost\", port=6379, db=0, decode_responses=True)","section_id":"connect"}]}
{"schema_version":2,"id":"develop/get-started/search-tutorial/indexing","title":"Create an index","url":"https://redis.io/docs/latest/develop/get-started/search-tutorial/indexing/","summary":"Create a search index over your JSON documents with FT.CREATE, choose the right field types, and understand how Redis indexes arrays.","aliases":["/get-started/search-tutorial/indexing/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:36:02+01:00","page_type":"content","content_hash":"8ffad55a7401d52ee31908c9a73a91c57c5809abe6ec60b792656433be4b1b61","sections":[{"id":"what-an-index-does","title":"What an index does","role":"content","text":"When you create an index, you give Redis three things:\n\n1. **What to index** &mdash; which keys belong to the index, selected by a key prefix (here, `product:`).\n2. **The data type** &mdash; whether those keys hold hashes (`ON HASH`) or JSON documents (`ON JSON`).\n3. **The schema** &mdash; which fields to index, the path to each one, and what type each field is.\n\nOnce the index exists, Redis keeps it up to date automatically. Any `product:` document you add or change after creating the index is indexed immediately, and the documents you loaded earlier are indexed right away."},{"id":"field-types","title":"Field types","role":"content","text":"Redis Search has a few core field types. Choosing the right one for each field determines how you can query it:\n\n| Field type | Use it for | Example query |\n| --- | --- | --- |\n| `TEXT` | Human language you want to search by words and partial matches. | find products whose description contains *wireless* |\n| `TAG` | Exact-value labels and categories you filter on as a whole. | find products where category is exactly *Audio* |\n| `NUMERIC` | Numbers you filter by range or sort by. | find products priced between 0 and 100 |\n| `VECTOR` | Embeddings for similarity search (covered in the [last step](https://redis.io/docs/latest/develop/get-started/search-tutorial/vector-search)). | find products similar in meaning to a query |\n\nFor the catalog, a good mapping is: `name` and `description` are `TEXT` (you want word search), `brand` and `category` are `TAG` (exact labels), and `price`, `rating`, `review_count`, `stock`, and `release_year` are `NUMERIC`. The `features` field is a list of exact labels, so it is also a `TAG`."},{"id":"create-the-index","title":"Create the index","role":"content","text":"<details><summary>Reload products data</summary>\nFoundational: Load the tutorial dataset as JSON documents under a shared key prefix using JSON.SET\n\n**Difficulty:** Beginner\n\n**Commands:** JSON.SET\n\n**Complexity:**\n- JSON.SET: O(M+N)\n\n**Available in:** Redis CLI, Python\n\n##### Redis CLI\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\n</details>\n\nUse [FT.CREATE](https://redis.io/docs/latest/commands/ft.create) to define the index. Because the data is JSON, each field is identified by a [JSONPath](https://redis.io/docs/latest/develop/data-types/json/path) expression, and `AS` gives it a short alias to use in queries:\n\nFoundational: Create an index over JSON documents with FT.CREATE, mapping JSONPaths to TEXT, TAG, and NUMERIC fields\n\n**Difficulty:** Beginner\n\n**Commands:** FT.CREATE\n\n**Complexity:**\n- FT.CREATE: O(K)\n\n**Available in:** Redis CLI, Python\n\n##### Redis CLI\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nA few things to notice:\n\n- **`PREFIX 1 product:`** means \"index every key that starts with `product:`\". The `1` is the number of prefixes that follow.\n- **`AS name`, `AS price`, ...** define the alias you use in queries (`@name`, `@price`). Without an alias, you would have to write the full JSONPath in every query.\n- **`SORTABLE`** on a field lets you sort results by it efficiently. Add it to fields you expect to sort by, such as `price` and `rating`.\n- **`$.features[*]`** ends in `[*]`, which matters for arrays. More on that next.\n\nYou only create an index once. If you make a mistake, remove it with [FT.DROPINDEX](https://redis.io/docs/latest/commands/ft.dropindex) (this deletes the index, not your documents) and create it again."},{"id":"indexing-arrays-the--you-should-not-forget","title":"Indexing arrays: the `[*]` you should not forget","role":"content","text":"The `features` field is a JSON array like `[\"wireless\", \"bluetooth\", \"waterproof\"]`. To index each element as its own tag, the JSONPath ends in `[*]`:\n\n[code example]\n\nThis is the single most common point of confusion when indexing JSON, so it is worth understanding:\n\n- **With `$.features[*]`**, Redis indexes `wireless`, `bluetooth`, and `waterproof` as three separate tags. A query for `@features:{waterproof}` matches the document.\n- **With `$.features`** (no `[*]`) on a JSON array, the behavior is not what you want for filtering element-by-element.\n\n> [!NOTE]\n> This behavior differs between hashes and JSON, which trips up many newcomers. In a **hash**, a `TAG` field splits on commas by default, so `\"wireless,bluetooth\"` becomes two tags automatically. In **JSON**, there is no automatic splitting: index array elements with `[*]`, or if you store a comma-separated string, add `SEPARATOR \",\"` to the field definition. For the full explanation, see [Index JSON arrays as TAG](https://redis.io/docs/latest/develop/ai/search-and-query/indexing#index-json-arrays-as-tag)."},{"id":"check-the-index","title":"Check the index","role":"content","text":"After creating the index, you can confirm it picked up your documents. [FT.INFO](https://redis.io/docs/latest/commands/ft.info) reports details about an index, including how many documents it contains:\n\nFoundational: Inspect an index with FT.INFO to confirm it exists and see how many documents it contains\n\n**Difficulty:** Beginner\n\n**Commands:** FT.INFO\n\n**Complexity:**\n- FT.INFO: O(1)\n\n**Available in:** Redis CLI, Python\n\n##### Redis CLI\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nLook for `num_docs` in the output; it should be `12`, one for each product you loaded.\n\n> [!NOTE] Try it in Redis Insight\n> In the [Redis Insight Search workspace](https://redis.io/docs/latest/develop/tools/insight/search-workspace), your new `idx:catalog` index appears in the list of indexes. Selecting it shows the schema you just defined &mdash; the fields, their types, and their aliases &mdash; without having to read the raw `FT.INFO` output."},{"id":"next-steps","title":"Next steps","role":"content","text":"Your data is indexed. Continue to [searching and filtering](https://redis.io/docs/latest/develop/get-started/search-tutorial/search) to start asking questions of it."}],"examples":[{"id":"create-the-index-ex0","language":"plaintext","code":"> JSON.SET product:1 $ '{\"name\":\"Aurora AcousticPro Headphones\",\"brand\":\"Aurora\",\"category\":\"Audio\",\"price\":199.99,\"rating\":4.6,\"features\":[\"wireless\",\"noise-cancelling\",\"bluetooth\"],\"specs\":{\"color\":\"midnight black\",\"weight_grams\":268}}'\n> JSON.SET product:2 $ '{\"name\":\"Aurora BudsMini Earbuds\",\"brand\":\"Aurora\",\"category\":\"Audio\",\"description\":\"Tiny true-wireless earbuds with a secure in-ear fit and sweat resistance for workouts. The compact charging case slips into a pocket and delivers three full recharges on the go.\",\"price\":89.99,\"rating\":4.3,\"review_count\":942,\"stock\":130,\"release_year\":2023,\"features\":[\"wireless\",\"bluetooth\",\"in-ear\",\"water-resistant\"],\"specs\":{\"color\":\"pearl white\",\"weight_grams\":5,\"warranty_years\":1}}'\n... output truncated for AI-facing Markdown ...\n> JSON.SET product:12 $ '{\"name\":\"Vista Action Cam 4K\",\"brand\":\"Vista\",\"category\":\"Cameras\",\"description\":\"A pocket-sized action camera that shoots stabilized 4K video and is waterproof without a case. Mount it on a helmet or bike and capture your adventures in sharp detail.\",\"price\":299.0,\"rating\":4.3,\"review_count\":455,\"stock\":33,\"release_year\":2023,\"features\":[\"camera\",\"4k\",\"waterproof\",\"wifi\"],\"specs\":{\"color\":\"black\",\"weight_grams\":128,\"warranty_years\":1}}'","section_id":"create-the-index"},{"id":"create-the-index-ex1","language":"python","code":"catalog = [\n    {\n        \"name\": \"Aurora AcousticPro Headphones\",\n        \"brand\": \"Aurora\",\n        \"category\": \"Audio\",\n        \"description\": (\n            \"Over-ear wireless headphones with active noise cancelling and a \"\n            \"40-hour battery. Plush memory-foam earcups and a lightweight frame \"\n            \"make them comfortable for all-day listening, whether you are \"\n            \"commuting, working, or relaxing at home.\"\n        ),\n        \"price\": 199.99,\n        \"rating\": 4.6,\n        \"review_count\": 1284,\n        \"stock\": 42,\n        \"release_year\": 2024,\n        \"features\": [\"wireless\", \"noise-cancelling\", \"bluetooth\", \"over-ear\"],\n        \"specs\": {\"color\": \"midnight black\", \"weight_grams\": 268, \"warranty_years\": 2},\n    },\n    {\n        \"name\": \"Aurora BudsMini Earbuds\",\n        \"brand\": \"Aurora\",\n        \"category\": \"Audio\",\n        \"description\": (\n            \"Tiny true-wireless earbuds with a secure in-ear fit and sweat \"\n            \"resistance for workouts. The compact charging case slips into a \"\n            \"pocket and delivers three full recharges on the go.\"\n        ),\n        \"price\": 89.99,\n        \"rating\": 4.3,\n        \"review_count\": 942,\n        \"stock\": 130,\n        \"release_year\": 2023,\n        \"features\": [\"wireless\", \"bluetooth\", \"in-ear\", \"water-resistant\"],\n        \"specs\": {\"color\": \"pearl white\", \"weight_grams\": 5, \"warranty_years\": 1},\n    },\n    {\n        \"name\": \"Sonus Boom Portable Speaker\",\n        \"brand\": \"Sonus\",\n        \"category\": \"Audio\",\n        \"description\": (\n            \"A rugged portable Bluetooth speaker with deep bass and a waterproof \"\n            \"shell. Toss it in a bag for the beach or a campsite and enjoy \"\n            \"room-filling sound for up to 20 hours per charge.\"\n        ),\n        \"price\": 129.5,\n        \"rating\": 4.5,\n        \"review_count\": 512,\n        \"stock\": 64,\n        \"release_year\": 2024,\n        \"features\": [\"wireless\", \"bluetooth\", \"portable\", \"waterproof\"],\n        \"specs\": {\"color\": \"slate gray\", \"weight_grams\": 540, \"warranty_years\": 1},\n    },\n    {\n        \"name\": \"Pixma Vortex 15 Laptop\",\n        \"brand\": \"Pixma\",\n        \"category\": \"Computers\",\n        \"description\": (\n            \"A thin-and-light 15-inch laptop with a fast multi-core processor, \"\n            \"16 GB of memory, and a speedy solid-state drive. The backlit keyboard \"\n            \"and bright display make it a capable companion for work and study.\"\n        ),\n        \"price\": 1399.0,\n        \"rating\": 4.7,\n        \"review_count\": 318,\n        \"stock\": 18,\n        \"release_year\": 2024,\n        \"features\": [\"laptop\", \"ssd\", \"backlit-keyboard\", \"lightweight\"],\n        \"specs\": {\"color\": \"space silver\", \"weight_grams\": 1600, \"warranty_years\": 2},\n    },\n    {\n        \"name\": \"Pixma UltraView 27 Monitor\",\n        \"brand\": \"Pixma\",\n        \"category\": \"Computers\",\n        \"description\": (\n            \"A 27-inch 4K monitor with an IPS panel for accurate colors and wide \"\n            \"viewing angles. A single USB-C cable carries video and power, keeping \"\n            \"your desk tidy.\"\n        ),\n        \"price\": 329.99,\n        \"rating\": 4.4,\n        \"review_count\": 221,\n        \"stock\": 27,\n        \"release_year\": 2023,\n        \"features\": [\"monitor\", \"4k\", \"ips\", \"usb-c\"],\n        \"specs\": {\"color\": \"black\", \"weight_grams\": 5200, \"warranty_years\": 3},\n    },\n    {\n        \"name\": \"Clackr Mechanical Keyboard\",\n        \"brand\": \"Clackr\",\n        \"category\": \"Accessories\",\n        \"description\": (\n            \"A compact mechanical keyboard with tactile switches, per-key RGB \"\n            \"lighting, and wireless connectivity. Hot-swappable switches let you \"\n            \"tune the typing feel without soldering.\"\n        ),\n        \"price\": 119.0,\n        \"rating\": 4.8,\n        \"review_count\": 1502,\n        \"stock\": 88,\n        \"release_year\": 2024,\n        \"features\": [\"keyboard\", \"mechanical\", \"rgb\", \"wireless\"],\n        \"specs\": {\"color\": \"graphite\", \"weight_grams\": 720, \"warranty_years\": 2},\n    },\n    {\n        \"name\": \"Glide Pro Wireless Mouse\",\n        \"brand\": \"Glide\",\n        \"category\": \"Accessories\",\n        \"description\": (\n            \"An ergonomic wireless mouse with a high-precision sensor and a \"\n            \"contoured shape that reduces wrist strain. A single charge lasts for \"\n            \"weeks of everyday use.\"\n        ),\n        \"price\": 59.99,\n        \"rating\": 4.2,\n        \"review_count\": 869,\n        \"stock\": 150,\n        \"release_year\": 2022,\n        \"features\": [\"mouse\", \"wireless\", \"ergonomic\"],\n        \"specs\": {\"color\": \"charcoal\", \"weight_grams\": 98, \"warranty_years\": 1},\n    },\n    {\n        \"name\": \"Pulse Series 6 Smartwatch\",\n        \"brand\": \"Pulse\",\n        \"category\": \"Wearables\",\n        \"description\": (\n            \"A sleek smartwatch with built-in GPS, continuous heart-rate \"\n            \"monitoring, and water resistance for swimming. Track workouts, sleep, \"\n            \"and notifications from your wrist.\"\n        ),\n        \"price\": 249.0,\n        \"rating\": 4.5,\n        \"review_count\": 1733,\n        \"stock\": 51,\n        \"release_year\": 2024,\n        \"features\": [\"smartwatch\", \"gps\", \"heart-rate\", \"water-resistant\"],\n        \"specs\": {\"color\": \"rose gold\", \"weight_grams\": 38, \"warranty_years\": 1},\n    },\n    {\n        \"name\": \"Pulse Band Fitness Tracker\",\n        \"brand\": \"Pulse\",\n        \"category\": \"Wearables\",\n        \"description\": (\n            \"A lightweight fitness band that tracks steps, heart rate, and sleep \"\n            \"stages. The slim screen shows daily progress and the battery lasts a \"\n            \"full week between charges.\"\n        ),\n        \"price\": 79.99,\n        \"rating\": 4.1,\n        \"review_count\": 2210,\n        \"stock\": 200,\n        \"release_year\": 2023,\n        \"features\": [\"fitness-tracker\", \"heart-rate\", \"sleep-tracking\"],\n        \"specs\": {\"color\": \"ocean blue\", \"weight_grams\": 24, \"warranty_years\": 1},\n    },\n    {\n        \"name\": \"Lumi Glow Smart Bulb\",\n        \"brand\": \"Lumi\",\n        \"category\": \"Home\",\n        \"description\": (\n            \"A color-changing smart bulb that connects over Wi-Fi and works with \"\n            \"voice assistants. Dim it for movie night or set a warm white for \"\n            \"reading, all from your phone.\"\n        ),\n        \"price\": 24.99,\n        \"rating\": 4.0,\n        \"review_count\": 640,\n        \"stock\": 320,\n        \"release_year\": 2022,\n        \"features\": [\"smart-home\", \"wifi\", \"dimmable\", \"color\"],\n        \"specs\": {\"color\": \"white\", \"weight_grams\": 70, \"warranty_years\": 2},\n    },\n    {\n        \"name\": \"Lumi Climate Smart Thermostat\",\n        \"brand\": \"Lumi\",\n        \"category\": \"Home\",\n        \"description\": (\n            \"A learning smart thermostat that adjusts heating and cooling to your \"\n            \"routine and helps lower energy bills. The crisp display and Wi-Fi app \"\n            \"make scheduling effortless.\"\n        ),\n        \"price\": 149.0,\n        \"rating\": 4.6,\n        \"review_count\": 388,\n        \"stock\": 75,\n        \"release_year\": 2024,\n        \"features\": [\"smart-home\", \"wifi\", \"energy-saving\"],\n        \"specs\": {\"color\": \"white\", \"weight_grams\": 210, \"warranty_years\": 3},\n    },\n    {\n        \"name\": \"Vista Action Cam 4K\",\n        \"brand\": \"Vista\",\n        \"category\": \"Cameras\",\n        \"description\": (\n            \"A pocket-sized action camera that shoots stabilized 4K video and is \"\n            \"waterproof without a case. Mount it on a helmet or bike and capture \"\n            \"your adventures in sharp detail.\"\n        ),\n        \"price\": 299.0,\n        \"rating\": 4.3,\n        \"review_count\": 455,\n        \"stock\": 33,\n        \"release_year\": 2023,\n        \"features\": [\"camera\", \"4k\", \"waterproof\", \"wifi\"],\n        \"specs\": {\"color\": \"black\", \"weight_grams\": 128, \"warranty_years\": 1},\n    },\n]\n\nfor product_id, product in enumerate(catalog, start=1):\n    r.json().set(f\"product:{product_id}\", Path.root_path(), product)","section_id":"create-the-index"},{"id":"create-the-index-ex2","language":"plaintext","code":"> FT.CREATE idx:catalog ON JSON PREFIX 1 product: SCHEMA $.name AS name TEXT $.brand AS brand TAG SORTABLE $.category AS category TAG $.description AS description TEXT $.price AS price NUMERIC SORTABLE $.rating AS rating NUMERIC SORTABLE $.review_count AS review_count NUMERIC $.stock AS stock NUMERIC $.release_year AS release_year NUMERIC SORTABLE $.features[*] AS features TAG","section_id":"create-the-index"},{"id":"create-the-index-ex3","language":"python","code":"schema = (\n    TextField(\"$.name\", as_name=\"name\"),\n    TagField(\"$.brand\", as_name=\"brand\", sortable=True),\n    TagField(\"$.category\", as_name=\"category\"),\n    TextField(\"$.description\", as_name=\"description\"),\n    NumericField(\"$.price\", as_name=\"price\", sortable=True),\n    NumericField(\"$.rating\", as_name=\"rating\", sortable=True),\n    NumericField(\"$.review_count\", as_name=\"review_count\"),\n    NumericField(\"$.stock\", as_name=\"stock\"),\n    NumericField(\"$.release_year\", as_name=\"release_year\", sortable=True),\n    TagField(\"$.features[*]\", as_name=\"features\"),\n)\nindex = r.ft(\"idx:catalog\")\nindex.create_index(\n    schema,\n    definition=IndexDefinition(prefix=[\"product:\"], index_type=IndexType.JSON),\n)","section_id":"create-the-index"},{"id":"indexing-arrays-the--you-should-not-forget-ex0","language":"plaintext","code":"$.features[*] AS features TAG","section_id":"indexing-arrays-the--you-should-not-forget"},{"id":"check-the-index-ex0","language":"plaintext","code":"> FT.INFO idx:catalog","section_id":"check-the-index"},{"id":"check-the-index-ex1","language":"python","code":"info = r.ft(\"idx:catalog\").info()\nprint(\"Documents indexed:\", info[\"num_docs\"])  # >>> Documents indexed: 12","section_id":"check-the-index"}]}
{"schema_version":2,"id":"develop/get-started/search-tutorial/search","title":"Search and filter your data","url":"https://redis.io/docs/latest/develop/get-started/search-tutorial/search/","summary":"Use FT.SEARCH to find documents by full text, tags, and numeric ranges, and to return only the fields you want.","aliases":["/get-started/search-tutorial/search/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:36:02+01:00","page_type":"content","content_hash":"a7a8cbd0f8fbc3e041eb0f88c466d708ad84dd0a487f74a24fb65d3e25b6f22f","sections":[{"id":"return-everything","title":"Return everything","role":"returns","text":"The `*` query matches every document. Use it to confirm the index is working. The first line of the result is the total number of matches:\n\nFoundational: Match every document with the wildcard query to confirm the index works\n\n**Difficulty:** Beginner\n\n**Commands:** FT.SEARCH\n\n**Complexity:**\n- FT.SEARCH: O(N)\n\n**Available in:** Redis CLI, Python\n\n##### Redis CLI\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nThe `LIMIT 0 0` clause asks for zero documents, so you get just the count. By default `FT.SEARCH` returns the full document for each match, which is verbose. The rest of this page uses `RETURN` to keep the output readable."},{"id":"full-text-search","title":"Full-text search","role":"content","text":"Fields you indexed as `TEXT` support full-text search: matching by word, regardless of position or surrounding text. To search a specific field, prefix the term with `@fieldname:`.\n\nThis finds products whose `name` contains the word *headphones*:\n\nFull-text search: Match a word within a TEXT field using the @field:term syntax\n\n**Difficulty:** Beginner\n\n**Commands:** FT.SEARCH\n\n**Complexity:**\n- FT.SEARCH: O(N)\n\n**Available in:** Redis CLI, Python\n\n##### Redis CLI\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nFull-text matching is case-insensitive and word-based, so `headphones` matches `Headphones`. You can also search across all `TEXT` fields at once by leaving off the `@field:` prefix; for example, `FT.SEARCH idx:catalog \"wireless\"` matches any product with *wireless* in its name or description."},{"id":"match-an-exact-phrase","title":"Match an exact phrase","role":"content","text":"To match an exact phrase &mdash; several words that must appear together and in order &mdash; wrap the phrase in escaped double quotes. This finds products whose description contains the phrase *noise cancelling*:\n\nExact phrase: Match an ordered, contiguous phrase in a TEXT field by wrapping it in escaped double quotes\n\n**Difficulty:** Intermediate\n\n**Commands:** FT.SEARCH\n\n**Complexity:**\n- FT.SEARCH: O(N)\n\n**Available in:** Redis CLI, Python\n\n##### Redis CLI\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nWithout the quotes, `@description:noise cancelling` matches the two words independently: both must be present, but they can appear anywhere in the field and in any order. The quotes are what require them to sit together as a phrase."},{"id":"filter-by-tag","title":"Filter by tag","role":"content","text":"Fields you indexed as `TAG` match on exact values. Tag values go inside curly braces: `@field:{value}`.\n\nThis finds every product in the `Audio` category and returns the name and price of each:\n\nTag filter: Match an exact TAG value using the @field:{value} syntax\n\n**Difficulty:** Beginner\n\n**Commands:** FT.SEARCH\n\n**Complexity:**\n- FT.SEARCH: O(N)\n\n**Available in:** Redis CLI, Python\n\n##### Redis CLI\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nBecause `features` was indexed as a multi-value tag (the `[*]` from the [previous step](https://redis.io/docs/latest/develop/get-started/search-tutorial/indexing)), the same syntax filters on individual list elements. This finds every `waterproof` product:\n\nTag filter on arrays: Match a single element of a multi-value TAG field\n\n**Difficulty:** Beginner\n\n**Commands:** FT.SEARCH\n\n**Complexity:**\n- FT.SEARCH: O(N)\n\n**Available in:** Redis CLI, Python\n\n##### Redis CLI\n\n[code example]\n\n##### Python\n\n[code example]"},{"id":"filter-by-numeric-range","title":"Filter by numeric range","role":"content","text":"Fields you indexed as `NUMERIC` match on ranges, written as `@field:[min max]`. This finds products priced at $100 or less, sorted from cheapest to most expensive with `SORTBY`:\n\nNumeric range: Match a NUMERIC field within [min max] and order results with SORTBY\n\n**Difficulty:** Beginner\n\n**Commands:** FT.SEARCH\n\n**Complexity:**\n- FT.SEARCH: O(N)\n\n**Available in:** Redis CLI, Python\n\n##### Redis CLI\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nUse `-inf` and `+inf` for open-ended ranges. For example, `@price:[1000 +inf]` matches everything priced $1000 or more."},{"id":"combine-conditions","title":"Combine conditions","role":"content","text":"Real questions usually combine several conditions. Listing expressions one after another means **AND**: every condition must match. This finds Audio products that also cost $100 or less:\n\nCombined query: AND multiple conditions by listing them together (tag plus numeric range)\n\n**Difficulty:** Intermediate\n\n**Commands:** FT.SEARCH\n\n**Complexity:**\n- FT.SEARCH: O(N)\n\n**Available in:** Redis CLI, Python\n\n##### Redis CLI\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nOnly the BudsMini Earbuds satisfy both conditions. You can also express OR with `|` and negation with `-`. See [Combined queries](https://redis.io/docs/latest/develop/ai/search-and-query/query/combined) for the full set of operators."},{"id":"projection-return-only-what-you-need","title":"Projection: return only what you need","role":"content","text":"You have already been using `RETURN` to pick fields. It is worth calling out on its own, because returning only the fields you need keeps responses small and fast:\n\n- `RETURN 2 name price` returns just those two fields.\n- Without `RETURN`, the full document comes back for every match.\n- `LIMIT <offset> <count>` controls how many results you get and is the basis for pagination. By default, `FT.SEARCH` returns the first 10 matches.\n\nThis returns the three most expensive products, newest pricing first, with only their name and price:\n\nProjection and paging: Return only chosen fields, sort, and page results with RETURN, SORTBY, and LIMIT\n\n**Difficulty:** Intermediate\n\n**Commands:** FT.SEARCH\n\n**Complexity:**\n- FT.SEARCH: O(N)\n\n**Available in:** Redis CLI, Python\n\n##### Redis CLI\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nThe total is still `12` (the count of all matches), but only three documents are returned because of `LIMIT 0 3`.\n\n> [!NOTE] Try it in Redis Insight\n> The [Redis Insight Search workspace](https://redis.io/docs/latest/develop/tools/insight/search-workspace) has a query editor that understands your index schema. As you type `@`, it suggests field names and tag values, and it renders results as a table instead of the numbered list you see in `redis-cli`. It is a comfortable place to experiment with the queries on this page."},{"id":"next-steps","title":"Next steps","role":"content","text":"`FT.SEARCH` finds and returns documents. When you need to *summarize* across many documents &mdash; counts, averages, totals per group &mdash; you use a different command. Continue to [aggregation](https://redis.io/docs/latest/develop/get-started/search-tutorial/aggregation)."}],"examples":[{"id":"return-everything-ex0","language":"plaintext","code":"> FT.SEARCH idx:catalog \"*\" LIMIT 0 0\n1) (integer) 12","section_id":"return-everything"},{"id":"return-everything-ex1","language":"python","code":"res = index.search(Query(\"*\").paging(0, 0))\nprint(\"Total products:\", res.total)  # >>> Total products: 12","section_id":"return-everything"},{"id":"full-text-search-ex0","language":"plaintext","code":"> FT.SEARCH idx:catalog \"@name:headphones\" RETURN 1 name\n1) (integer) 1\n2) \"product:1\"\n3) 1) \"name\"\n   2) \"Aurora AcousticPro Headphones\"","section_id":"full-text-search"},{"id":"full-text-search-ex1","language":"python","code":"res = index.search(Query(\"@name:headphones\").return_field(\"name\"))\nprint(res.docs)\n# >>> [Document {'id': 'product:1', ... 'name': 'Aurora AcousticPro Headphones'}]","section_id":"full-text-search"},{"id":"match-an-exact-phrase-ex0","language":"plaintext","code":"> FT.SEARCH idx:catalog \"@description:\\\"noise cancelling\\\"\" RETURN 1 name\n1) (integer) 1\n2) \"product:1\"\n3) 1) \"name\"\n   2) \"Aurora AcousticPro Headphones\"","section_id":"match-an-exact-phrase"},{"id":"match-an-exact-phrase-ex1","language":"python","code":"res = index.search(Query('@description:\"noise cancelling\"').return_field(\"name\"))\nprint(res.total, [d.name for d in res.docs])\n# >>> 1 ['Aurora AcousticPro Headphones']","section_id":"match-an-exact-phrase"},{"id":"filter-by-tag-ex0","language":"plaintext","code":"> FT.SEARCH idx:catalog \"@category:{Audio}\" RETURN 2 name price\n1) (integer) 3\n2) \"product:2\"\n3) 1) \"name\"\n   2) \"Aurora BudsMini Earbuds\"\n   3) \"price\"\n   4) \"89.99\"\n4) \"product:3\"\n5) 1) \"name\"\n   2) \"Sonus Boom Portable Speaker\"\n   3) \"price\"\n   4) \"129.5\"\n6) \"product:1\"\n7) 1) \"name\"\n   2) \"Aurora AcousticPro Headphones\"\n   3) \"price\"\n   4) \"199.99\"","section_id":"filter-by-tag"},{"id":"filter-by-tag-ex1","language":"python","code":"res = index.search(Query(\"@category:{Audio}\").return_fields(\"name\", \"price\"))\nprint(res.total, [d.name for d in res.docs])\n# >>> 3 ['Aurora BudsMini Earbuds', 'Sonus Boom Portable Speaker', ...]","section_id":"filter-by-tag"},{"id":"filter-by-tag-ex2","language":"plaintext","code":"> FT.SEARCH idx:catalog \"@features:{waterproof}\" RETURN 1 name\n1) (integer) 2\n2) \"product:3\"\n3) 1) \"name\"\n   2) \"Sonus Boom Portable Speaker\"\n4) \"product:12\"\n5) 1) \"name\"\n   2) \"Vista Action Cam 4K\"","section_id":"filter-by-tag"},{"id":"filter-by-tag-ex3","language":"python","code":"res = index.search(Query(\"@features:{waterproof}\").return_field(\"name\"))\nprint(res.total, [d.name for d in res.docs])\n# >>> 2 ['Sonus Boom Portable Speaker', 'Vista Action Cam 4K']","section_id":"filter-by-tag"},{"id":"filter-by-numeric-range-ex0","language":"plaintext","code":"> FT.SEARCH idx:catalog \"@price:[0 100]\" SORTBY price ASC RETURN 2 name price\n1) (integer) 4\n2) \"product:10\"\n3) 1) \"price\"\n   2) \"24.99\"\n   3) \"name\"\n   4) \"Lumi Glow Smart Bulb\"\n4) \"product:7\"\n5) 1) \"price\"\n   2) \"59.99\"\n   3) \"name\"\n   4) \"Glide Pro Wireless Mouse\"\n6) \"product:9\"\n7) 1) \"price\"\n   2) \"79.99\"\n   3) \"name\"\n   4) \"Pulse Band Fitness Tracker\"\n8) \"product:2\"\n9) 1) \"price\"\n   2) \"89.99\"\n   3) \"name\"\n   4) \"Aurora BudsMini Earbuds\"","section_id":"filter-by-numeric-range"},{"id":"filter-by-numeric-range-ex1","language":"python","code":"res = index.search(\n    Query(\"@price:[0 100]\").sort_by(\"price\", asc=True).return_fields(\"name\", \"price\")\n)\nprint([(d.name, d.price) for d in res.docs])\n# >>> [('Lumi Glow Smart Bulb', '24.99'), ('Glide Pro Wireless Mouse', '59.99'), ...]","section_id":"filter-by-numeric-range"},{"id":"combine-conditions-ex0","language":"plaintext","code":"> FT.SEARCH idx:catalog \"@category:{Audio} @price:[0 100]\" RETURN 2 name price\n1) (integer) 1\n2) \"product:2\"\n3) 1) \"name\"\n   2) \"Aurora BudsMini Earbuds\"\n   3) \"price\"\n   4) \"89.99\"","section_id":"combine-conditions"},{"id":"combine-conditions-ex1","language":"python","code":"res = index.search(\n    Query(\"@category:{Audio} @price:[0 100]\").return_fields(\"name\", \"price\")\n)\nprint(res.total, [d.name for d in res.docs])\n# >>> 1 ['Aurora BudsMini Earbuds']","section_id":"combine-conditions"},{"id":"projection-return-only-what-you-need-ex0","language":"plaintext","code":"> FT.SEARCH idx:catalog \"*\" SORTBY price DESC RETURN 2 name price LIMIT 0 3\n1) (integer) 12\n2) \"product:4\"\n3) 1) \"price\"\n   2) \"1399\"\n   3) \"name\"\n   4) \"Pixma Vortex 15 Laptop\"\n4) \"product:5\"\n5) 1) \"price\"\n   2) \"329.99\"\n   3) \"name\"\n   4) \"Pixma UltraView 27 Monitor\"\n6) \"product:12\"\n7) 1) \"price\"\n   2) \"299\"\n   3) \"name\"\n   4) \"Vista Action Cam 4K\"","section_id":"projection-return-only-what-you-need"},{"id":"projection-return-only-what-you-need-ex1","language":"python","code":"res = index.search(\n    Query(\"*\").sort_by(\"price\", asc=False).return_fields(\"name\", \"price\").paging(0, 3)\n)\nprint([(d.name, d.price) for d in res.docs])\n# >>> [('Pixma Vortex 15 Laptop', '1399'), ('Pixma UltraView 27 Monitor', '329.99'), ...]","section_id":"projection-return-only-what-you-need"}]}
{"schema_version":2,"id":"develop/get-started/search-tutorial/vector-search","title":"Vector and hybrid search","url":"https://redis.io/docs/latest/develop/get-started/search-tutorial/vector-search/","summary":"Search by meaning with vector embeddings, run KNN queries with FT.SEARCH, and combine keywords with semantic similarity using FT.HYBRID.","aliases":["/get-started/search-tutorial/vector-search/","/get-started/vector-database/","/develop/get-started/vector-database/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:36:02+01:00","page_type":"content","content_hash":"aba1cf94fd29ba37083dd51ee60f14329f4fde33de43156095ea0d028f906885","sections":[{"id":"how-vector-search-works","title":"How vector search works","role":"content","text":"A machine learning **embedding model** turns a piece of text into a list of numbers, called a **vector**, that captures its meaning. Texts with similar meanings produce vectors that are close together in space. To search by meaning, you:\n\n1. Generate an embedding for each product (here, from its description) and store it on the document.\n2. Add a `VECTOR` field to the index so Redis can search those embeddings.\n3. At query time, embed the search phrase and ask Redis for the products whose vectors are nearest to it.\n\n\"Nearest\" is measured by a **distance metric**. This tutorial uses cosine distance, where a smaller distance means more similar."},{"id":"generate-and-store-embeddings","title":"Generate and store embeddings","role":"content","text":"Embeddings come from a model, so this step uses a client library rather than `redis-cli`. The example below uses the Python [SentenceTransformers](https://www.sbert.net/) framework to embed each product description and store the result on the document under `$.embedding`. The model used here produces 768-dimensional vectors.\n\n[code example]\n\nRedis can store vectors in either hashes or JSON documents. Because this tutorial uses JSON, each embedding is stored as a JSON array of numbers, so every product now has an `embedding` field alongside its other attributes."},{"id":"add-a-vector-field-to-the-index","title":"Add a vector field to the index","role":"content","text":"<details><summary>Reload products data and re-create index</summary>\n\nredis> FT.CREATE idx:catalog ON JSON PREFIX 1 product: SCHEMA $.name AS name TEXT $.brand AS brand TAG SORTABLE $.category AS category TAG $.description AS description TEXT $.price AS price NUMERIC SORTABLE $.rating AS rating NUMERIC SORTABLE $.review_count AS review_count NUMERIC $.stock AS stock NUMERIC $.release_year AS release_year NUMERIC SORTABLE $.features[*] AS features TAG\nOK\nredis> JSON.SET product:1 $ '{\"name\":\"Aurora AcousticPro Headphones\",\"brand\":\"Aurora\",\"category\":\"Audio\",\"price\":199.99,\"rating\":4.6,\"features\":[\"wireless\",\"noise-cancelling\",\"bluetooth\"],\"specs\":{\"color\":\"midnight black\",\"weight_grams\":268}}'\nOK\nredis> JSON.SET product:2 $ '{\"name\":\"Aurora BudsMini Earbuds\",\"brand\":\"Aurora\",\"category\":\"Audio\",\"description\":\"Tiny true-wireless earbuds with a secure in-ear fit and sweat resistance for workouts. The compact charging case slips into a pocket and delivers three full recharges on the go.\",\"price\":89.99,\"rating\":4.3,\"review_count\":942,\"stock\":130,\"release_year\":2023,\"features\":[\"wireless\",\"bluetooth\",\"in-ear\",\"water-resistant\"],\"specs\":{\"color\":\"pearl white\",\"weight_grams\":5,\"warranty_years\":1}}'\nOK\nredis> JSON.SET product:3 $ '{\"name\":\"Sonus Boom Portable Speaker\",\"brand\":\"Sonus\",\"category\":\"Audio\",\"description\":\"A rugged portable Bluetooth speaker with deep bass and a waterproof shell. Toss it in a bag for the beach or a campsite and enjoy room-filling sound for up to 20 hours per charge.\",\"price\":129.5,\"rating\":4.5,\"review_count\":512,\"stock\":64,\"release_year\":2024,\"features\":[\"wireless\",\"bluetooth\",\"portable\",\"waterproof\"],\"specs\":{\"color\":\"slate gray\",\"weight_grams\":540,\"warranty_years\":1}}'\nOK\nredis> JSON.SET product:4 $ '{\"name\":\"Pixma Vortex 15 Laptop\",\"brand\":\"Pixma\",\"category\":\"Computers\",\"description\":\"A thin-and-light 15-inch laptop with a fast multi-core processor, 16 GB of memory, and a speedy solid-state drive. The backlit keyboard and bright display make it a capable companion for work and study.\",\"price\":1399.0,\"rating\":4.7,\"review_count\":318,\"stock\":18,\"release_year\":2024,\"features\":[\"laptop\",\"ssd\",\"backlit-keyboard\",\"lightweight\"],\"specs\":{\"color\":\"space silver\",\"weight_grams\":1600,\"warranty_years\":2}}'\nOK\nredis> JSON.SET product:5 $ '{\"name\":\"Pixma UltraView 27 Monitor\",\"brand\":\"Pixma\",\"category\":\"Computers\",\"description\":\"A 27-inch 4K monitor with an IPS panel for accurate colors and wide viewing angles. A single USB-C cable carries video and power, keeping your desk tidy.\",\"price\":329.99,\"rating\":4.4,\"review_count\":221,\"stock\":27,\"release_year\":2023,\"features\":[\"monitor\",\"4k\",\"ips\",\"usb-c\"],\"specs\":{\"color\":\"black\",\"weight_grams\":5200,\"warranty_years\":3}}'\nOK\nredis> JSON.SET product:6 $ '{\"name\":\"Clackr Mechanical Keyboard\",\"brand\":\"Clackr\",\"category\":\"Accessories\",\"description\":\"A compact mechanical keyboard with tactile switches, per-key RGB lighting, and wireless connectivity. Hot-swappable switches let you tune the typing feel without soldering.\",\"price\":119.0,\"rating\":4.8,\"review_count\":1502,\"stock\":88,\"release_year\":2024,\"features\":[\"keyboard\",\"mechanical\",\"rgb\",\"wireless\"],\"specs\":{\"color\":\"graphite\",\"weight_grams\":720,\"warranty_years\":2}}'\nOK\nredis> JSON.SET product:7 $ '{\"name\":\"Glide Pro Wireless Mouse\",\"brand\":\"Glide\",\"category\":\"Accessories\",\"description\":\"An ergonomic wireless mouse with a high-precision sensor and a contoured shape that reduces wrist strain. A single charge lasts for weeks of everyday use.\",\"price\":59.99,\"rating\":4.2,\"review_count\":869,\"stock\":150,\"release_year\":2022,\"features\":[\"mouse\",\"wireless\",\"ergonomic\"],\"specs\":{\"color\":\"charcoal\",\"weight_grams\":98,\"warranty_years\":1}}'\nOK\nredis> JSON.SET product:8 $ '{\"name\":\"Pulse Series 6 Smartwatch\",\"brand\":\"Pulse\",\"category\":\"Wearables\",\"description\":\"A sleek smartwatch with built-in GPS, continuous heart-rate monitoring, and water resistance for swimming. Track workouts, sleep, and notifications from your wrist.\",\"price\":249.0,\"rating\":4.5,\"review_count\":1733,\"stock\":51,\"release_year\":2024,\"features\":[\"smartwatch\",\"gps\",\"heart-rate\",\"water-resistant\"],\"specs\":{\"color\":\"rose gold\",\"weight_grams\":38,\"warranty_years\":1}}'\nOK\nredis> JSON.SET product:9 $ '{\"name\":\"Pulse Band Fitness Tracker\",\"brand\":\"Pulse\",\"category\":\"Wearables\",\"description\":\"A lightweight fitness band that tracks steps, heart rate, and sleep stages. The slim screen shows daily progress and the battery lasts a full week between charges.\",\"price\":79.99,\"rating\":4.1,\"review_count\":2210,\"stock\":200,\"release_year\":2023,\"features\":[\"fitness-tracker\",\"heart-rate\",\"sleep-tracking\"],\"specs\":{\"color\":\"ocean blue\",\"weight_grams\":24,\"warranty_years\":1}}'\nOK\nredis> JSON.SET product:10 $ '{\"name\":\"Lumi Glow Smart Bulb\",\"brand\":\"Lumi\",\"category\":\"Home\",\"description\":\"A color-changing smart bulb that connects over Wi-Fi and works with voice assistants. Dim it for movie night or set a warm white for reading, all from your phone.\",\"price\":24.99,\"rating\":4.0,\"review_count\":640,\"stock\":320,\"release_year\":2022,\"features\":[\"smart-home\",\"wifi\",\"dimmable\",\"color\"],\"specs\":{\"color\":\"white\",\"weight_grams\":70,\"warranty_years\":2}}'\nOK\nredis> JSON.SET product:11 $ '{\"name\":\"Lumi Climate Smart Thermostat\",\"brand\":\"Lumi\",\"category\":\"Home\",\"description\":\"A learning smart thermostat that adjusts heating and cooling to your routine and helps lower energy bills. The crisp display and Wi-Fi app make scheduling effortless.\",\"price\":149.0,\"rating\":4.6,\"review_count\":388,\"stock\":75,\"release_year\":2024,\"features\":[\"smart-home\",\"wifi\",\"energy-saving\"],\"specs\":{\"color\":\"white\",\"weight_grams\":210,\"warranty_years\":3}}'\nOK\nredis> JSON.SET product:12 $ '{\"name\":\"Vista Action Cam 4K\",\"brand\":\"Vista\",\"category\":\"Cameras\",\"description\":\"A pocket-sized action camera that shoots stabilized 4K video and is waterproof without a case. Mount it on a helmet or bike and capture your adventures in sharp detail.\",\"price\":299.0,\"rating\":4.3,\"review_count\":455,\"stock\":33,\"release_year\":2023,\"features\":[\"camera\",\"4k\",\"waterproof\",\"wifi\"],\"specs\":{\"color\":\"black\",\"weight_grams\":128,\"warranty_years\":1}}'\nOK\n\n\n</details>\n\nThe index you created earlier does not know about the new `embedding` field. Recreate it to include a `VECTOR` field. Dropping the index does not delete your documents, and the embeddings you just stored are indexed as soon as the new index is created:\n\nFoundational: Recreate the index with a VECTOR field so embeddings can be searched\n\n**Difficulty:** Intermediate\n\n**Commands:** FT.DROPINDEX, FT.CREATE\n\n**Complexity:**\n- FT.DROPINDEX: O(1)\n- FT.CREATE: O(K)\n\n**Available in:** Redis CLI, Python\n\n##### Redis CLI\n\n[code example]\n\n##### Python\n\n[code example]\n\n\n\nThe vector field definition reads: index `$.embedding` as a `VECTOR` field using the `FLAT` algorithm, with `6` attributes following &mdash; `TYPE FLOAT32`, `DIM 768` (the model's dimension), and `DISTANCE_METRIC COSINE`. `FLAT` does an exact search and is a good default for small datasets; for large datasets you would choose `HNSW`. For all the options, see the [vector search concepts](https://redis.io/docs/latest/develop/ai/search-and-query/vectors) page."},{"id":"k-nearest-neighbors-knn","title":"K-nearest neighbors (KNN)","role":"content","text":"A KNN query asks for the `k` products whose embeddings are closest to a query vector. You embed the search phrase with the *same* model, then pass the resulting vector to `FT.SEARCH`:\n\n[code example]\n\nHere is what each part does:\n\n- **`(*)`** is a pre-filter that runs *before* the vector search. `(*)` means \"consider all products\". You can put any query here to restrict the candidates (shown next).\n- **`=>[KNN 3 @embedding $query_vector AS score]`** asks for the 3 nearest neighbors in the `embedding` field, naming each result's distance `score`.\n- **`PARAMS 2 query_vector \"...\"`** supplies the query vector's binary value. The `2` means two arguments follow: the parameter name and its value.\n- **`SORTBY score ASC`** orders results closest-first, and **`DIALECT 2`** selects the query dialect that vector search requires.\n\n> [!NOTE]\n> The query vector's binary value is long, so it is shortened in the example above. In a real application your client library builds it for you from the model's output, as in the [embedding step](#generate-and-store-embeddings) above.\n\nFor a phrase like \"*portable music for the outdoors*\", this returns the products whose descriptions are closest in meaning &mdash; the portable speaker and the earbuds rank highly &mdash; even though they share no specific keyword with the query."},{"id":"pre-filter-the-candidates","title":"Pre-filter the candidates","role":"content","text":"The pre-filter is where vector search meets the filtering you already know. Replace `(*)` with any `FT.SEARCH` query to search for similar products *within a subset*. This finds the 3 nearest products **among Audio products only**:\n\n[code example]"},{"id":"hybrid-search","title":"Hybrid search","role":"content","text":"Keyword search and vector search each have strengths. Keyword search is precise when the user knows the exact term; vector search is forgiving when they describe what they want in their own words. **Hybrid search** runs both at once and fuses the results, giving you the best of each.\n\nThe [FT.HYBRID](https://redis.io/docs/latest/commands/ft.hybrid) command takes a `SEARCH` clause (a full-text query, exactly like `FT.SEARCH`) and a `VSIM` clause (a vector similarity query), and combines their rankings. This searches for the keyword *wireless* and, at the same time, for products semantically similar to the query vector (here, an embedding of \"*wireless headphones for listening to music*\"):\n\n[code example]\n\nAs with the KNN examples, the query vector's binary value is shortened above; your client library builds it from the model's output.\n\nThe result blends two rankings: products that literally mention *wireless* and products whose meaning is closest to the query vector. For this query, the wireless headphones and earbuds come out on top &mdash; they satisfy both the keyword and the meaning &mdash; followed by other wireless items and the nearest semantic matches such as the portable speaker.\n\nBy default, `FT.HYBRID` fuses the two rankings with a method called Reciprocal Rank Fusion. You can tune the balance with a `COMBINE` clause, and add `FILTER`, `LOAD`, `APPLY`, and `SORTBY` steps just as you would in an aggregation. See the [FT.HYBRID](https://redis.io/docs/latest/commands/ft.hybrid) reference for the full syntax.\n\n> [!NOTE] Try it in Redis Insight\n> The [Redis Insight Search workspace](https://redis.io/docs/latest/develop/tools/insight/search-workspace) is built for exactly this kind of work. Its welcome screen introduces full-text, vector, and hybrid search, it can load a ready-made vector dataset, and its editor handles the vector parameters for you &mdash; a much friendlier way to experiment with vector and hybrid queries than pasting binary blobs into `redis-cli`."},{"id":"what-you-have-learned","title":"What you have learned","role":"content","text":"Congratulations &mdash; you have gone from an empty database to running hybrid semantic search. Along the way you:\n\n1. Modeled records as JSON documents and learned when hashes fit better.\n2. Created an index and chose `TEXT`, `TAG`, and `NUMERIC` field types.\n3. Searched, filtered, and projected with `FT.SEARCH`.\n4. Grouped and summarized data with `FT.AGGREGATE`.\n5. Searched by meaning with vector KNN and combined it with keywords using `FT.HYBRID`."},{"id":"where-to-go-next","title":"Where to go next","role":"content","text":"- **Go deeper on querying** &mdash; the [query documentation](https://redis.io/docs/latest/develop/ai/search-and-query/query) covers fuzzy matching, geospatial queries, scoring, and more.\n- **Tune your vectors** &mdash; [vector search concepts](https://redis.io/docs/latest/develop/ai/search-and-query/vectors) explains the `FLAT` and `HNSW` index types, vector range queries, and how to choose between them.\n- **Use a vector-native Python library** &mdash; [RedisVL](https://redis.io/docs/latest/develop/clients/redis-vl) provides a higher-level API for building vector search and AI applications on Redis.\n- **Build an AI application** &mdash; see how Redis powers retrieval-augmented generation in the [RAG quick start](https://redis.io/docs/latest/develop/get-started/rag) and [Redis for AI](https://redis.io/docs/latest/develop/ai).\n- **See also** &mdash; if you need standalone similarity search without a full search index, Redis also offers the [vector sets](https://redis.io/docs/latest/develop/data-types/vector-sets) data type."}],"examples":[{"id":"generate-and-store-embeddings-ex0","language":"python","code":"from redis import Redis\nfrom sentence_transformers import SentenceTransformer\n\nr = Redis(host=\"localhost\", port=6379, decode_responses=True)\nembedder = SentenceTransformer(\"msmarco-distilbert-base-v4\")  # 768-dimensional vectors\n\n# Embed each product's description and store it on the document.\nfor key in r.scan_iter(match=\"product:*\"):\n    description = r.json().get(key, \"$.description\")[0]\n    embedding = embedder.encode(description).astype(\"float32\").tolist()\n    r.json().set(key, \"$.embedding\", embedding)","section_id":"generate-and-store-embeddings"},{"id":"add-a-vector-field-to-the-index-ex0","language":"plaintext","code":"> FT.DROPINDEX idx:catalog\nOK\n> FT.CREATE idx:catalog ON JSON PREFIX 1 product: SCHEMA $.name AS name TEXT $.brand AS brand TAG SORTABLE $.category AS category TAG $.description AS description TEXT $.price AS price NUMERIC SORTABLE $.rating AS rating NUMERIC SORTABLE $.features[*] AS features TAG $.embedding AS embedding VECTOR FLAT 6 TYPE FLOAT32 DIM 768 DISTANCE_METRIC COSINE\nOK","section_id":"add-a-vector-field-to-the-index"},{"id":"add-a-vector-field-to-the-index-ex1","language":"python","code":"r.ft(\"idx:catalog\").dropindex()\nschema = (\n    TextField(\"$.name\", as_name=\"name\"),\n    TagField(\"$.brand\", as_name=\"brand\", sortable=True),\n    TagField(\"$.category\", as_name=\"category\"),\n    TextField(\"$.description\", as_name=\"description\"),\n    NumericField(\"$.price\", as_name=\"price\", sortable=True),\n    NumericField(\"$.rating\", as_name=\"rating\", sortable=True),\n    TagField(\"$.features[*]\", as_name=\"features\"),\n    VectorField(\n        \"$.embedding\",\n        \"FLAT\",\n        {\"TYPE\": \"FLOAT32\", \"DIM\": 768, \"DISTANCE_METRIC\": \"COSINE\"},\n        as_name=\"embedding\",\n    ),\n)\nindex = r.ft(\"idx:catalog\")\nindex.create_index(\n    schema,\n    definition=IndexDefinition(prefix=[\"product:\"], index_type=IndexType.JSON),\n)","section_id":"add-a-vector-field-to-the-index"},{"id":"k-nearest-neighbors-knn-ex0","language":"plaintext","code":"FT.SEARCH idx:catalog \"(*)=>[KNN 3 @embedding $query_vector AS score]\" PARAMS 2 query_vector \"\\x9a\\x99\\x19\\x3f...\" SORTBY score ASC RETURN 2 score name DIALECT 2","section_id":"k-nearest-neighbors-knn"},{"id":"pre-filter-the-candidates-ex0","language":"plaintext","code":"FT.SEARCH idx:catalog \"(@category:{Audio})=>[KNN 3 @embedding $query_vector AS score]\" PARAMS 2 query_vector \"\\x9a\\x99\\x19\\x3f...\" SORTBY score ASC RETURN 2 score name DIALECT 2","section_id":"pre-filter-the-candidates"},{"id":"hybrid-search-ex0","language":"plaintext","code":"FT.HYBRID idx:catalog SEARCH \"wireless\" VSIM @embedding $query_vector KNN 2 K 5 LOAD 1 @name PARAMS 2 query_vector \"\\x9a\\x99\\x19\\x3f...\"","section_id":"hybrid-search"}]}
{"schema_version":2,"id":"develop","title":"Develop with Redis","url":"https://redis.io/docs/latest/develop/","summary":"Learn how to develop with Redis","tags":[],"last_updated":"2026-09-30T09:44:02-07:00","children":[{"id":"develop/setup","summary":"Get Redis running and connect your first application, however you deploy it.","title":"Get started","url":"https://redis.io/docs/latest/develop/setup/"},{"id":"develop/whats-new","summary":"High-level description of important updates to the Develop section","title":"What's new?","url":"https://redis.io/docs/latest/develop/whats-new/"},{"id":"develop/get-started","summary":"Redis quick start guides","title":"Quick starts","url":"https://redis.io/docs/latest/develop/get-started/"},{"id":"develop/tools","summary":"Tools to interact with a Redis server","title":"Client tools","url":"https://redis.io/docs/latest/develop/tools/"},{"id":"develop/clients","summary":"Connect your application to a Redis database and try an example","title":"Connect with Redis client API libraries","url":"https://redis.io/docs/latest/develop/clients/"},{"id":"develop/using-commands","summary":"Learn how to interact with Redis using commands.","title":"Using Redis commands","url":"https://redis.io/docs/latest/develop/using-commands/"},{"id":"develop/data-types","summary":"Overview of data types supported by Redis","title":"Redis data types","url":"https://redis.io/docs/latest/develop/data-types/"},{"id":"develop/ai","summary":"An overview of Redis for AI and search documentation, including vector search, AI agents, and the Context Engine (Redis Iris) managed services.","title":"Redis for AI and search","url":"https://redis.io/docs/latest/develop/ai/"},{"id":"develop/programmability","summary":"Extending Redis with Lua and Redis Functions","title":"Redis programmability","url":"https://redis.io/docs/latest/develop/programmability/"},{"id":"develop/use-cases","summary":"Learn how to implement common use cases with Redis","title":"Redis use cases","url":"https://redis.io/docs/latest/develop/use-cases/"},{"id":"develop/pubsub","summary":"How to use pub/sub channels in Redis","title":"Redis Pub/sub","url":"https://redis.io/docs/latest/develop/pubsub/"},{"id":"develop/reference","summary":"Specifications and protocols","title":"Redis reference","url":"https://redis.io/docs/latest/develop/reference/"}],"page_type":"content","content_hash":"e78446a342299f2a5cc7ce7f60013ff3d722f0fd2aaeb791d01dae7b1cebdd18","sections":[{"id":"content","title":"Content","role":"content","text":"Get a Redis server running in minutes with a free trial of\n[Redis Cloud](https://redis.io/docs/latest/operate/rc), or install\n[Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack) locally\non your machine. Check out the <a href=\"https://hub.docker.com/_/redis\">Redis Docker Hub</a> for the latest release. Then, explore Redis with your favorite\n[programming language](https://redis.io/docs/latest/develop/clients)\nor analyze and manage your database with our\n[UI tools](https://redis.io/docs/latest/develop/tools):\n\n<div class=\"grid grid-cols-1 md:grid-cols-3 gap-6 my-8\">\n  <div class=\"flex flex-col gap-2 h-full relative transition text-redis-ink-900 hover:text-red-900 focus-within:text-red-900 bg-white hover:bg-red-50/50 focus-within:bg-red-50/50 border border-redis-pen-800 focus-within:ring-red-200 focus-within:ring-[3px] focus-within:outline-none bg-clip-padding rounded-md group\">\n    <a class=\"absolute inset-0 z-0 outline-0\" href=\"https://redis.io/docs/latest/develop/clients/redis-py\"><span class=\"sr-only\">Read more</span></a>\n    <div class=\"relative z-10 pointer-events-none\">\n      <div class=\"flex flex-row items-center gap-2 uppercase font-mono text-xs border-b border-redis-pen-800 px-6 py-2\">\n        \n        <span>Python</span>\n      </div>\n      <p class=\"text-sm px-6 pt-2 my-0 text-redis-pen-600 font-geist\">Examples:</p>\n      <ul class=\"text-sm px-6 pt-1 pb-3 my-0 text-redis-pen-600 font-geist space-y-1 [&>li]:my-0\">\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/clients/redis-py\">Get started</a></li>\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/clients/redis-py/queryjson\">Document search</a></li>\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/clients/redis-py/vecsearch\">Vector search</a></li>\n      </ul>\n    </div>\n  </div>\n  <div class=\"flex flex-col gap-2 h-full relative transition text-redis-ink-900 hover:text-red-900 focus-within:text-red-900 bg-white hover:bg-red-50/50 focus-within:bg-red-50/50 border border-redis-pen-800 focus-within:ring-red-200 focus-within:ring-[3px] focus-within:outline-none bg-clip-padding rounded-md group\">\n    <a class=\"absolute inset-0 z-0 outline-0\" href=\"https://redis.io/docs/latest/develop/clients/dotnet\"><span class=\"sr-only\">Read more</span></a>\n    <div class=\"relative z-10 pointer-events-none\">\n      <div class=\"flex flex-row items-center gap-2 uppercase font-mono text-xs border-b border-redis-pen-800 px-6 py-2\">\n        \n        <span>C#/.NET</span>\n      </div>\n      <p class=\"text-sm px-6 pt-2 my-0 text-redis-pen-600 font-geist\">Examples:</p>\n      <ul class=\"text-sm px-6 pt-1 pb-3 my-0 text-redis-pen-600 font-geist space-y-1 [&>li]:my-0\">\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/clients/dotnet\">Get started</a></li>\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/clients/dotnet/nredisstack/queryjson\">Document search</a></li>\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/clients/dotnet/nredisstack/vecsearch\">Vector search</a></li>\n      </ul>\n    </div>\n  </div>\n  <div class=\"flex flex-col gap-2 h-full relative transition text-redis-ink-900 hover:text-red-900 focus-within:text-red-900 bg-white hover:bg-red-50/50 focus-within:bg-red-50/50 border border-redis-pen-800 focus-within:ring-red-200 focus-within:ring-[3px] focus-within:outline-none bg-clip-padding rounded-md group\">\n    <a class=\"absolute inset-0 z-0 outline-0\" href=\"https://redis.io/docs/latest/develop/clients/nodejs\"><span class=\"sr-only\">Read more</span></a>\n    <div class=\"relative z-10 pointer-events-none\">\n      <div class=\"flex flex-row items-center gap-2 uppercase font-mono text-xs border-b border-redis-pen-800 px-6 py-2\">\n        \n        <span>Node.js</span>\n      </div>\n      <p class=\"text-sm px-6 pt-2 my-0 text-redis-pen-600 font-geist\">Examples:</p>\n      <ul class=\"text-sm px-6 pt-1 pb-3 my-0 text-redis-pen-600 font-geist space-y-1 [&>li]:my-0\">\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/clients/nodejs\">Get started</a></li>\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/clients/nodejs/queryjson\">Document search</a></li>\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/clients/nodejs/vecsearch\">Vector search</a></li>\n      </ul>\n    </div>\n  </div>\n  <div class=\"flex flex-col gap-2 h-full relative transition text-redis-ink-900 hover:text-red-900 focus-within:text-red-900 bg-white hover:bg-red-50/50 focus-within:bg-red-50/50 border border-redis-pen-800 focus-within:ring-red-200 focus-within:ring-[3px] focus-within:outline-none bg-clip-padding rounded-md group\">\n    <a class=\"absolute inset-0 z-0 outline-0\" href=\"https://redis.io/docs/latest/develop/clients/jedis\"><span class=\"sr-only\">Read more</span></a>\n    <div class=\"relative z-10 pointer-events-none\">\n      <div class=\"flex flex-row items-center gap-2 uppercase font-mono text-xs border-b border-redis-pen-800 px-6 py-2\">\n        \n        <span>Java (Jedis)</span>\n      </div>\n      <p class=\"text-sm px-6 pt-2 my-0 text-redis-pen-600 font-geist\">Examples:</p>\n      <ul class=\"text-sm px-6 pt-1 pb-3 my-0 text-redis-pen-600 font-geist space-y-1 [&>li]:my-0\">\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/clients/jedis\">Get started</a></li>\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/clients/jedis/queryjson\">Document search</a></li>\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/clients/jedis/vecsearch\">Vector search</a></li>\n      </ul>\n    </div>\n  </div>\n  <div class=\"flex flex-col gap-2 h-full relative transition text-redis-ink-900 hover:text-red-900 focus-within:text-red-900 bg-white hover:bg-red-50/50 focus-within:bg-red-50/50 border border-redis-pen-800 focus-within:ring-red-200 focus-within:ring-[3px] focus-within:outline-none bg-clip-padding rounded-md group\">\n    <a class=\"absolute inset-0 z-0 outline-0\" href=\"https://redis.io/docs/latest/develop/clients/lettuce\"><span class=\"sr-only\">Read more</span></a>\n    <div class=\"relative z-10 pointer-events-none\">\n      <div class=\"flex flex-row items-center gap-2 uppercase font-mono text-xs border-b border-redis-pen-800 px-6 py-2\">\n        \n        <span>Java (Lettuce)</span>\n      </div>\n      <p class=\"text-sm px-6 pt-2 my-0 text-redis-pen-600 font-geist\">Examples:</p>\n      <ul class=\"text-sm px-6 pt-1 pb-3 my-0 text-redis-pen-600 font-geist space-y-1 [&>li]:my-0\">\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/clients/lettuce\">Get started</a></li>\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/clients/lettuce/queryjson\">Document search</a></li>\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/clients/lettuce/vecsearch\">Vector search</a></li>\n      </ul>\n    </div>\n  </div>\n  <div class=\"flex flex-col gap-2 h-full relative transition text-redis-ink-900 hover:text-red-900 focus-within:text-red-900 bg-white hover:bg-red-50/50 focus-within:bg-red-50/50 border border-redis-pen-800 focus-within:ring-red-200 focus-within:ring-[3px] focus-within:outline-none bg-clip-padding rounded-md group\">\n    <a class=\"absolute inset-0 z-0 outline-0\" href=\"https://redis.io/docs/latest/develop/clients/go\"><span class=\"sr-only\">Read more</span></a>\n    <div class=\"relative z-10 pointer-events-none\">\n      <div class=\"flex flex-row items-center gap-2 uppercase font-mono text-xs border-b border-redis-pen-800 px-6 py-2\">\n        \n        <span>Go</span>\n      </div>\n      <p class=\"text-sm px-6 pt-2 my-0 text-redis-pen-600 font-geist\">Examples:</p>\n      <ul class=\"text-sm px-6 pt-1 pb-3 my-0 text-redis-pen-600 font-geist space-y-1 [&>li]:my-0\">\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/clients/go\">Get started</a></li>\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/clients/go/queryjson\">Document search</a></li>\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/clients/go/vecsearch\">Vector search</a></li>\n      </ul>\n    </div>\n  </div>\n  <div class=\"flex flex-col gap-2 h-full relative transition text-redis-ink-900 hover:text-red-900 focus-within:text-red-900 bg-white hover:bg-red-50/50 focus-within:bg-red-50/50 border border-redis-pen-800 focus-within:ring-red-200 focus-within:ring-[3px] focus-within:outline-none bg-clip-padding rounded-md group\">\n    <a class=\"absolute inset-0 z-0 outline-0\" href=\"https://redis.io/docs/latest/develop/clients/php\"><span class=\"sr-only\">Read more</span></a>\n    <div class=\"relative z-10 pointer-events-none\">\n      <div class=\"flex flex-row items-center gap-2 uppercase font-mono text-xs border-b border-redis-pen-800 px-6 py-2\">\n        \n        <span>PHP</span>\n      </div>\n      <p class=\"text-sm px-6 pt-2 my-0 text-redis-pen-600 font-geist\">Examples:</p>\n      <ul class=\"text-sm px-6 pt-1 pb-3 my-0 text-redis-pen-600 font-geist space-y-1 [&>li]:my-0\">\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/clients/php\">Get started</a></li>\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/clients/php/queryjson\">Document search</a></li>\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/clients/php/vecsearch\">Vector search</a></li>\n      </ul>\n    </div>\n  </div>\n  <div class=\"flex flex-col gap-2 h-full relative transition text-redis-ink-900 hover:text-red-900 focus-within:text-red-900 bg-white hover:bg-red-50/50 focus-within:bg-red-50/50 border border-redis-pen-800 focus-within:ring-red-200 focus-within:ring-[3px] focus-within:outline-none bg-clip-padding rounded-md group\">\n    <a class=\"absolute inset-0 z-0 outline-0\" href=\"https://redis.io/docs/latest/develop/clients/ioredis\"><span class=\"sr-only\">Read more</span></a>\n    <div class=\"relative z-10 pointer-events-none\">\n      <div class=\"flex flex-row items-center gap-2 uppercase font-mono text-xs border-b border-redis-pen-800 px-6 py-2\">\n        \n        <span>JavaScript (ioredis)</span>\n      </div>\n      <p class=\"text-sm px-6 pt-2 my-0 text-redis-pen-600 font-geist\">Examples:</p>\n      <ul class=\"text-sm px-6 pt-1 pb-3 my-0 text-redis-pen-600 font-geist space-y-1 [&>li]:my-0\">\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/clients/ioredis\">Get started</a></li>\n      </ul>\n    </div>\n  </div>\n  <div class=\"flex flex-col gap-2 h-full relative transition text-redis-ink-900 hover:text-red-900 focus-within:text-red-900 bg-white hover:bg-red-50/50 focus-within:bg-red-50/50 border border-redis-pen-800 focus-within:ring-red-200 focus-within:ring-[3px] focus-within:outline-none bg-clip-padding rounded-md group\">\n    <a class=\"absolute inset-0 z-0 outline-0\" href=\"https://redis.io/docs/latest/develop/clients/rust\"><span class=\"sr-only\">Read more</span></a>\n    <div class=\"relative z-10 pointer-events-none\">\n      <div class=\"flex flex-row items-center gap-2 uppercase font-mono text-xs border-b border-redis-pen-800 px-6 py-2\">\n        \n        <span>Rust</span>\n      </div>\n      <p class=\"text-sm px-6 pt-2 my-0 text-redis-pen-600 font-geist\">Examples:</p>\n      <ul class=\"text-sm px-6 pt-1 pb-3 my-0 text-redis-pen-600 font-geist space-y-1 [&>li]:my-0\">\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/clients/rust\">Get started</a></li>\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/clients/rust/json\">Document search</a></li>\n      </ul>\n    </div>\n  </div>\n  <div class=\"flex flex-col gap-2 h-full relative transition text-redis-ink-900 hover:text-red-900 focus-within:text-red-900 bg-white hover:bg-red-50/50 focus-within:bg-red-50/50 border border-redis-pen-800 focus-within:ring-red-200 focus-within:ring-[3px] focus-within:outline-none bg-clip-padding rounded-md group\">\n    <a class=\"absolute inset-0 z-0 outline-0\" href=\"https://redis.io/docs/latest/develop/clients/hiredis\"><span class=\"sr-only\">Read more</span></a>\n    <div class=\"relative z-10 pointer-events-none\">\n      <div class=\"flex flex-row items-center gap-2 uppercase font-mono text-xs border-b border-redis-pen-800 px-6 py-2\">\n        \n        <span>C</span>\n      </div>\n      <p class=\"text-sm px-6 pt-2 my-0 text-redis-pen-600 font-geist\">Examples:</p>\n      <ul class=\"text-sm px-6 pt-1 pb-3 my-0 text-redis-pen-600 font-geist space-y-1 [&>li]:my-0\">\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/clients/hiredis\">Get started</a></li>\n      </ul>\n    </div>\n  </div>\n  <div class=\"flex flex-col gap-2 h-full relative transition text-redis-ink-900 hover:text-red-900 focus-within:text-red-900 bg-white hover:bg-red-50/50 focus-within:bg-red-50/50 border border-redis-pen-800 focus-within:ring-red-200 focus-within:ring-[3px] focus-within:outline-none bg-clip-padding rounded-md group\">\n    <a class=\"absolute inset-0 z-0 outline-0\" href=\"https://redis.io/docs/latest/develop/clients/ruby\"><span class=\"sr-only\">Read more</span></a>\n    <div class=\"relative z-10 pointer-events-none\">\n      <div class=\"flex flex-row items-center gap-2 uppercase font-mono text-xs border-b border-redis-pen-800 px-6 py-2\">\n        \n        <span>Ruby</span>\n      </div>\n      <p class=\"text-sm px-6 pt-2 my-0 text-redis-pen-600 font-geist\">Examples:</p>\n      <ul class=\"text-sm px-6 pt-1 pb-3 my-0 text-redis-pen-600 font-geist space-y-1 [&>li]:my-0\">\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/clients/ruby\">Get started</a></li>\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/clients/ruby/queryjson\">Document search</a></li>\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/clients/ruby/vecsearch\">Vector search</a></li>\n      </ul>\n    </div>\n  </div>\n</div>\n\n<div class=\"flex flex-col gap-4 my-6\">\n  <div class=\"relative transition text-redis-ink-900 hover:text-red-900 focus-within:text-red-900 bg-white hover:bg-red-50/50 focus-within:bg-red-50/50 border border-redis-pen-800 focus-within:ring-red-200 focus-within:ring-[3px] focus-within:outline-none bg-clip-padding rounded-md group\">\n    <a class=\"absolute inset-0 z-0 outline-0\" href=\"https://redis.io/docs/latest/develop/tools/insight\"><span class=\"sr-only\">Read more</span></a>\n    <div class=\"relative z-10 pointer-events-none\">\n      <div class=\"flex flex-row items-center gap-2 uppercase font-mono text-xs border-b border-redis-pen-800 px-6 py-2\">\n        \n        <span>Redis Insight</span>\n      </div>\n      <p class=\"text-sm px-6 pt-3 my-0 text-redis-pen-600 font-geist\">Visual client tool for creating, managing, and analyzing Redis databases.</p>\n      <ol class=\"flex flex-wrap items-center gap-x-2 gap-y-2 list-none m-0 px-6 py-4 text-sm text-redis-pen-600 font-geist\">\n        <li class=\"flex items-center gap-2 my-0 pl-0\"><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/downloads/#insight\">Download</a><span aria-hidden=\"true\">→</span></li>\n        <li class=\"flex items-center gap-2 my-0 pl-0\"><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/tools/insight\">Use</a></li>\n      </ol>\n    </div>\n  </div>\n  <div class=\"relative transition text-redis-ink-900 hover:text-red-900 focus-within:text-red-900 bg-white hover:bg-red-50/50 focus-within:bg-red-50/50 border border-redis-pen-800 focus-within:ring-red-200 focus-within:ring-[3px] focus-within:outline-none bg-clip-padding rounded-md group\">\n    <a class=\"absolute inset-0 z-0 outline-0\" href=\"https://redis.io/docs/latest/develop/tools/redis-for-vscode\"><span class=\"sr-only\">Read more</span></a>\n    <div class=\"relative z-10 pointer-events-none\">\n      <div class=\"flex flex-row items-center gap-2 uppercase font-mono text-xs border-b border-redis-pen-800 px-6 py-2\">\n        \n        <span>Redis for VS Code</span>\n      </div>\n      <p class=\"text-sm px-6 pt-3 my-0 text-redis-pen-600 font-geist\">VS Code extension for creating, managing, and analyzing Redis databases.</p>\n      <ol class=\"flex flex-wrap items-center gap-x-2 gap-y-2 list-none m-0 px-6 py-4 text-sm text-redis-pen-600 font-geist\">\n        <li class=\"flex items-center gap-2 my-0 pl-0\"><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://marketplace.visualstudio.com/items?itemName=Redis.redis-for-vscode\">Install</a><span aria-hidden=\"true\">→</span></li>\n        <li class=\"flex items-center gap-2 my-0 pl-0\"><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/tools/redis-for-vscode\">Use</a></li>\n      </ol>\n    </div>\n  </div>\n</div>\n\n<div class=\"grid grid-cols-1 md:grid-cols-3 gap-6 my-8\">\n  <div class=\"flex flex-col gap-2 h-full min-h-40 relative transition text-redis-ink-900 hover:text-red-900 focus-within:text-red-900 bg-white hover:bg-red-50/50 focus-within:bg-red-50/50 border border-redis-pen-800 focus-within:ring-red-200 focus-within:ring-[3px] focus-within:outline-none bg-clip-padding rounded-md group\">\n    <a class=\"absolute inset-0 z-0 outline-0\" href=\"https://redis.io/docs/latest/develop/get-started\"><span class=\"sr-only\">Read more</span></a>\n    <div class=\"relative z-10 pointer-events-none\">\n      <div class=\"flex flex-row items-center gap-2 uppercase font-mono text-xs border-b border-redis-pen-800 px-6 py-2\">\n        \n        <span>Quick start</span>\n      </div>\n      <ul class=\"text-sm px-6 pt-2 pb-4 text-redis-pen-600 font-geist space-y-1\">\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/get-started/search-tutorial/vector-search\">Vector database</a></li>\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/get-started/search-tutorial\">Document store</a></li>\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/get-started/data-store\">Data structure store</a></li>\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/get-started/rag\">RAG with Redis</a></li>\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/get-started/redis-in-ai\">GenAI</a></li>\n      </ul>\n    </div>\n  </div>\n  <div class=\"flex flex-col gap-2 h-full min-h-40 relative transition text-redis-ink-900 hover:text-red-900 focus-within:text-red-900 bg-white hover:bg-red-50/50 focus-within:bg-red-50/50 border border-redis-pen-800 focus-within:ring-red-200 focus-within:ring-[3px] focus-within:outline-none bg-clip-padding rounded-md group\">\n    <a class=\"absolute inset-0 z-0 outline-0\" href=\"https://redis.io/docs/latest/develop/data-types\"><span class=\"sr-only\">Read more</span></a>\n    <div class=\"relative z-10 pointer-events-none\">\n      <div class=\"flex flex-row items-center gap-2 uppercase font-mono text-xs border-b border-redis-pen-800 px-6 py-2\">\n        \n        <span>Data types</span>\n      </div>\n      <ul class=\"text-sm px-6 pt-2 pb-4 text-redis-pen-600 font-geist space-y-1\">\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/data-types/strings\">String</a></li>\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/data-types/json\">JSON</a></li>\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/data-types/hashes\">Hash</a></li>\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/data-types/vector-sets\">Vector set</a></li>\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/data-types/probabilistic\">Probabilistic types</a></li>\n      </ul>\n    </div>\n  </div>\n  <div class=\"flex flex-col gap-2 h-full min-h-40 relative transition text-redis-ink-900 hover:text-red-900 focus-within:text-red-900 bg-white hover:bg-red-50/50 focus-within:bg-red-50/50 border border-redis-pen-800 focus-within:ring-red-200 focus-within:ring-[3px] focus-within:outline-none bg-clip-padding rounded-md group\">\n    <a class=\"absolute inset-0 z-0 outline-0\" href=\"https://redis.io/docs/latest/develop/ai/search-and-query\"><span class=\"sr-only\">Read more</span></a>\n    <div class=\"relative z-10 pointer-events-none\">\n      <div class=\"flex flex-row items-center gap-2 uppercase font-mono text-xs border-b border-redis-pen-800 px-6 py-2\">\n        \n        <span>Redis Search</span>\n      </div>\n      <ul class=\"text-sm px-6 pt-2 pb-4 text-redis-pen-600 font-geist space-y-1\">\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/ai/search-and-query\">Get started</a></li>\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/ai/search-and-query/indexing/field-and-type-options\">Schema field types</a></li>\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/ai/search-and-query/indexing\">Indexing</a></li>\n        <li><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/ai/search-and-query/query\">Querying</a></li>\n      </ul>\n    </div>\n  </div>\n</div>"}],"examples":[]}
{"schema_version":2,"id":"develop/programmability/eval-intro","title":"Scripting with Lua","url":"https://redis.io/docs/latest/develop/programmability/eval-intro/","summary":"Executing Lua in Redis","aliases":["/develop/interact/programmability/eval-intro","/interact/programmability/eval-intro/","/manual/programmability/eval-intro/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-30T13:16:11-07:00","page_type":"content","content_hash":"c55dfd50b05c25cc3a431a574660221c7274dbff6e5cfb59d7df20bd401924ec","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis lets users upload and execute Lua scripts on the server.\nScripts can employ programmatic control structures and use most of the [commands](/commands) while executing to access the database.\nBecause scripts execute in the server, reading and writing data from scripts is very efficient.\n\nRedis guarantees the script's atomic execution.\nWhile executing the script, all server activities are blocked during its entire runtime.\nThese semantics mean that all of the script's effects either have yet to happen or had already happened.\n\nScripting offers several properties that can be valuable in many cases.\nThese include:\n\n* Providing locality by executing logic where data lives. Data locality reduces overall latency and saves networking resources.\n* Blocking semantics that ensure the script's atomic execution.\n* Enabling the composition of simple capabilities that are either missing from Redis or are too niche to be a part of it.\n\nLua lets you run part of your application logic inside Redis.\nSuch scripts can perform conditional updates across multiple keys, possibly combining several different data types atomically.\n\nScripts are executed in Redis by an embedded execution engine.\nPresently, Redis supports a single scripting engine, the [Lua 5.1](https://www.lua.org/) interpreter.\nPlease refer to the [Redis Lua API Reference](https://redis.io/docs/latest/develop/programmability/lua-api) page for complete documentation.\n\nAlthough the server executes them, Eval scripts are regarded as a part of the client-side application, which is why they're not named, versioned, or persisted.\nSo all scripts may need to be reloaded by the application at any time if missing (after a server restart, fail-over to a replica, etc.).\nAs of version 7.0, [Redis Functions](https://redis.io/docs/latest/develop/programmability/functions-intro) offer an alternative approach to programmability which allow the server itself to be extended with additional programmed logic."},{"id":"getting-started","title":"Getting started","role":"setup","text":"We'll start scripting with Redis by using the [`EVAL`](https://redis.io/docs/latest/commands/eval) command.\n\nHere's our first example:\n\n[code example]\n\nIn this example, [`EVAL`](https://redis.io/docs/latest/commands/eval) takes two arguments.\nThe first argument is a string that consists of the script's Lua source code.\nThe script doesn't need to include any definitions of Lua function.\nIt is just a Lua program that will run in the Redis engine's context.\n\nThe second argument is the number of arguments that follow the script's body, starting from the third argument, representing Redis key names.\nIn this example, we used the value _0_ because we didn't provide the script with any arguments, whether the names of keys or not."},{"id":"script-parameterization","title":"Script parameterization","role":"content","text":"It is possible, although highly ill-advised, to have the application dynamically generate script source code per its needs.\nFor example, the application could send these two entirely different, but at the same time perfectly identical scripts:\n\n[code example]\n\nAlthough this mode of operation isn't blocked by Redis, it is an anti-pattern due to script cache considerations (more on the topic below).\nInstead of having your application generate subtle variations of the same scripts, you can parametrize them and pass any arguments needed for to execute them.\n\nThe following example demonstrates how to achieve the same effects as above, but via parameterization:\n\n[code example]\n\nAt this point, it is essential to understand the distinction Redis makes between input arguments that are names of keys and those that aren't.\n\nWhile key names in Redis are just strings, unlike any other string values, these represent keys in the database.\nThe name of a key is a fundamental concept in Redis and is the basis for operating the Redis Cluster.\n\n**Important:**\nto ensure the correct execution of scripts, both in standalone and clustered deployments, all names of keys that a script accesses must be explicitly provided as input key arguments.\nThe script **should only** access keys whose names are given as input arguments.\nScripts **should never** access keys with programmatically-generated names or based on the contents of data structures stored in the database.\n\nAny input to the function that isn't the name of a key is a regular input argument.\n\nIn the example above, both _Hello_ and _Parameterization!_ regular input arguments for the script.\nBecause the script doesn't touch any keys, we use the numerical argument _0_ to specify there are no key name arguments.\nThe execution context makes arguments available to the script through [_KEYS_](https://redis.io/docs/latest/develop/programmability/lua-api#the-keys-global-variable) and [_ARGV_](https://redis.io/docs/latest/develop/programmability/lua-api#the-argv-global-variable) global runtime variables.\nThe _KEYS_ table is pre-populated with all key name arguments provided to the script before its execution, whereas the _ARGV_ table serves a similar purpose but for regular arguments.\n\nThe following attempts to demonstrate the distribution of input arguments between the scripts _KEYS_ and _ARGV_ runtime global variables:\n\n\n[code example]\n\n**Note:**\nas can been seen above, Lua's table arrays are returned as [RESP2 array replies](https://redis.io/docs/latest/develop/reference/protocol-spec#resp-arrays), so it is likely that your client's library will convert it to the native array data type in your programming language.\nPlease refer to the rules that govern [data type conversion](https://redis.io/docs/latest/develop/programmability/lua-api#data-type-conversion) for more pertinent information."},{"id":"interacting-with-redis-from-a-script","title":"Interacting with Redis from a script","role":"content","text":"It is possible to call Redis commands from a Lua script either via [`redis.call()`](https://redis.io/docs/latest/develop/programmability/lua-api#redis.call) or [`redis.pcall()`](https://redis.io/docs/latest/develop/programmability/lua-api#redis.pcall).\n\nThe two are nearly identical.\nBoth execute a Redis command along with its provided arguments, if these represent a well-formed command.\nHowever, the difference between the two functions lies in the manner in which runtime errors (such as syntax errors, for example) are handled.\nErrors raised from calling `redis.call()` function are returned directly to the client that had executed it.\nConversely, errors encountered when calling the `redis.pcall()` function are returned to the script's execution context instead for possible handling.\n\nFor example, consider the following:\n\n[code example]\nThe above script accepts one key name and one value as its input arguments.\nWhen executed, the script calls the [`SET`](https://redis.io/docs/latest/commands/set) command to set the input key, _foo_, with the string value \"bar\"."},{"id":"script-cache","title":"Script cache","role":"content","text":"Until this point, we've used the [`EVAL`](https://redis.io/docs/latest/commands/eval) command to run our script.\n\nWhenever we call [`EVAL`](https://redis.io/docs/latest/commands/eval), we also include the script's source code with the request.\nRepeatedly calling [`EVAL`](https://redis.io/docs/latest/commands/eval) to execute the same set of parameterized scripts, wastes both network bandwidth and also has some overheads in Redis.\nNaturally, saving on network and compute resources is key, so, instead, Redis provides a caching mechanism for scripts.\n\nEvery script you execute with [`EVAL`](https://redis.io/docs/latest/commands/eval) is stored in a dedicated cache that the server keeps.\nThe cache's contents are organized by the scripts' SHA1 digest sums, so the SHA1 digest sum of a script uniquely identifies it in the cache.\nYou can verify this behavior by running [`EVAL`](https://redis.io/docs/latest/commands/eval) and calling [`INFO`](https://redis.io/docs/latest/commands/info) afterward.\nYou'll notice that the _used_memory_scripts_eval_ and _number_of_cached_scripts_ metrics grow with every new script that's executed.\n\nAs mentioned above, dynamically-generated scripts are an anti-pattern.\nGenerating scripts during the application's runtime may, and probably will, exhaust the host's memory resources for caching them.\nInstead, scripts should be as generic as possible and provide customized execution via their arguments.\n\nA script is loaded to the server's cache by calling the [`SCRIPT LOAD`](https://redis.io/docs/latest/commands/script-load) command and providing its source code.\nThe server doesn't execute the script, but instead just compiles and loads it to the server's cache.\nOnce loaded, you can execute the cached script with the SHA1 digest returned from the server.\n\nHere's an example of loading and then executing a cached script:\n\n[code example]"},{"id":"cache-volatility","title":"Cache volatility","role":"content","text":"The Redis script cache is **always volatile**.\nIt isn't considered as a part of the database and is **not persisted**.\nThe cache may be cleared when the server restarts, during fail-over when a replica assumes the master role, or explicitly by [`SCRIPT FLUSH`](https://redis.io/docs/latest/commands/script-flush).\nThat means that cached scripts are ephemeral, and the cache's contents can be lost at any time.\n\nApplications that use scripts should always call [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) to execute them.\nThe server returns an error if the script's SHA1 digest is not in the cache.\nFor example:\n\n[code example]\n\nIn this case, the application should first load it with [`SCRIPT LOAD`](https://redis.io/docs/latest/commands/script-load) and then call [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) once more to run the cached script by its SHA1 sum.\nMost of Redis' clients already provide utility APIs for doing that automatically.\nPlease consult your client's documentation regarding the specific details."},{"id":"evalsha-in-the-context-of-pipelining","title":"`EVALSHA` in the context of pipelining","role":"content","text":"Special care should be given executing [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) in the context of a [pipelined request](https://redis.io/docs/latest/develop/using-commands/pipelining).\nThe commands in a pipelined request run in the order they are sent, but other clients' commands may be interleaved for execution between these.\nBecause of that, the `NOSCRIPT` error can return from a pipelined request but can't be handled.\n\nTherefore, a client library's implementation should revert to using plain [`EVAL`](https://redis.io/docs/latest/commands/eval) of parameterized in the context of a pipeline."},{"id":"script-cache-semantics","title":"Script cache semantics","role":"content","text":"During normal operation, an application's scripts are meant to stay indefinitely in the cache (that is, until the server is restarted or the cache being flushed).\nThe underlying reasoning is that the script cache contents of a well-written application are unlikely to grow continuously.\nEven large applications that use hundreds of cached scripts shouldn't be an issue in terms of cache memory usage. \n\nThe only way to flush the script cache is by explicitly calling the [`SCRIPT FLUSH`](https://redis.io/docs/latest/commands/script-flush) command.\nRunning the command will _completely flush_ the scripts cache, removing all the scripts executed so far.\nTypically, this is only needed when the instance is going to be instantiated for another customer or application in a cloud environment.\n\nAlso, as already mentioned, restarting a Redis instance flushes the non-persistent script cache.\nHowever, from the point of view of the Redis client, there are only two ways to make sure that a Redis instance was not restarted between two different commands:\n\n* The connection we have with the server is persistent and was never closed so far.\n* The client explicitly checks the `run_id` field in the [`INFO`](https://redis.io/docs/latest/commands/info) command to ensure the server was not restarted and is still the same process.\n\nPractically speaking, it is much simpler for the client to assume that in the context of a given connection, cached scripts are guaranteed to be there unless the administrator explicitly invoked the [`SCRIPT FLUSH`](https://redis.io/docs/latest/commands/script-flush) command.\nThe fact that the user can count on Redis to retain cached scripts is semantically helpful in the context of pipelining."},{"id":"the-script-command","title":"The `SCRIPT` command","role":"content","text":"The Redis [`SCRIPT`](https://redis.io/docs/latest/commands/script) provides several ways for controlling the scripting subsystem.\nThese are:\n\n* [`SCRIPT FLUSH`](https://redis.io/docs/latest/commands/script-flush): this command is the only way to force Redis to flush the scripts cache.\n  It is most useful in environments where the same Redis instance is reassigned to different uses.\n  It is also helpful for testing client libraries' implementations of the scripting feature.\n\n* [`SCRIPT EXISTS`](https://redis.io/docs/latest/commands/script-exists): given one or more SHA1 digests as arguments, this command returns an array of _1_'s and _0_'s.\n  _1_ means the specific SHA1 is recognized as a script already present in the scripting cache. _0_'s meaning is that a script with this SHA1 wasn't loaded before (or at least never since the latest call to [`SCRIPT FLUSH`](https://redis.io/docs/latest/commands/script-flush)).\n\n* `SCRIPT LOAD script`: this command registers the specified script in the Redis script cache. \n  It is a useful command in all the contexts where we want to ensure that [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) doesn't not fail (for instance, in a pipeline or when called from a [`MULTI`](https://redis.io/docs/latest/commands/multi)/[`EXEC`](https://redis.io/docs/latest/commands/exec) [transaction](https://redis.io/docs/latest/develop/using-commands/transactions), without the need to execute the script.\n\n* [`SCRIPT KILL`](https://redis.io/docs/latest/commands/script-kill): this command is the only way to interrupt a long-running script (a.k.a slow script), short of shutting down the server.\n  A script is deemed as slow once its execution's duration exceeds the configured [maximum execution time](https://redis.io/docs/latest/develop/programmability#maximum-execution-time) threshold.\n  The [`SCRIPT KILL`](https://redis.io/docs/latest/commands/script-kill) command can be used only with scripts that did not modify the dataset during their execution (since stopping a read-only script does not violate the scripting engine's guaranteed atomicity).\n\n* [`SCRIPT DEBUG`](https://redis.io/docs/latest/commands/script-debug): controls use of the built-in [Redis Lua scripts debugger](https://redis.io/docs/latest/develop/programmability/lua-debugging)."},{"id":"script-replication","title":"Script replication","role":"content","text":"In standalone deployments, a single Redis instance called _master_ manages the entire database.\nA [clustered deployment](https://redis.io/docs/latest/operate/oss_and_stack/management/scaling) has at least three masters managing the sharded database.\nRedis uses [replication](https://redis.io/docs/latest/operate/oss_and_stack/management/replication) to maintain one or more replicas, or exact copies, for any given master.\n\nBecause scripts can modify the data, Redis ensures all write operations performed by a script are also sent to replicas to maintain consistency.\nThere are two conceptual approaches when it comes to script replication:\n\n1. Verbatim replication: the master sends the script's source code to the replicas.\n   Replicas then execute the script and apply the write effects.\n   This mode can save on replication bandwidth in cases where short scripts generate many commands (for example, a _for_ loop).\n   However, this replication mode means that replicas redo the same work done by the master, which is wasteful.\n   More importantly, it also requires [all write scripts to be deterministic](#scripts-with-deterministic-writes).\n1. Effects replication: only the script's data-modifying commands are replicated.\n   Replicas then run the commands without executing any scripts.\n   While potentially lengthier in terms of network traffic, this replication mode is deterministic by definition and therefore doesn't require special consideration.\n\nVerbatim script replication was the only mode supported until Redis 3.2, in which effects replication was added.\nThe _lua-replicate-commands_ configuration directive and [`redis.replicate_commands()`](https://redis.io/docs/latest/develop/programmability/lua-api#redis.replicate_commands) Lua API can be used to enable it.\n\nIn Redis 5.0, effects replication became the default mode.\nAs of Redis 7.0, verbatim replication is no longer supported."},{"id":"replicating-commands-instead-of-scripts","title":"Replicating commands instead of scripts","role":"content","text":"Starting with Redis 3.2, it is possible to select an alternative replication method.\nInstead of replicating whole scripts, we can replicate the write commands generated by the script.\nWe call this **script effects replication**.\n\n**Note:**\nstarting with Redis 5.0, script effects replication is the default mode and does not need to be explicitly enabled.\n\nIn this replication mode, while Lua scripts are executed, Redis collects all the commands executed by the Lua scripting engine that actually modify the dataset.\nWhen the script execution finishes, the sequence of commands that the script generated are wrapped into a [`MULTI`](https://redis.io/docs/latest/commands/multi)/[`EXEC`](https://redis.io/docs/latest/commands/exec) [transaction](https://redis.io/docs/latest/develop/using-commands/transactions) and are sent to the replicas and AOF.\n\nThis is useful in several ways depending on the use case:\n\n* When the script is slow to compute, but the effects can be summarized by a few write commands, it is a shame to re-compute the script on the replicas or when reloading the AOF.\n  In this case, it is much better to replicate just the effects of the script.\n* When script effects replication is enabled, the restrictions on non-deterministic functions are removed.\n  You can, for example, use the [`TIME`](https://redis.io/docs/latest/commands/time) or [`SRANDMEMBER`](https://redis.io/docs/latest/commands/srandmember) commands inside your scripts freely at any place.\n* The Lua PRNG in this mode is seeded randomly on every call.\n\nUnless already enabled by the server's configuration or defaults (before Redis 7.0), you need to issue the following Lua command before the script performs a write:\n\n[code example]\n\nThe [`redis.replicate_commands()`](https://redis.io/docs/latest/develop/programmability/lua-api#redis.replicate_commands) function returns _true) if script effects replication was enabled;\notherwise, if the function was called after the script already called a write command,\nit returns _false_, and normal whole script replication is used.\n\nThis function is deprecated as of Redis 7.0, and while you can still call it, it will always succeed."},{"id":"scripts-with-deterministic-writes","title":"Scripts with deterministic writes","role":"content","text":"**Note:**\nStarting with Redis 5.0, script replication is by default effect-based rather than verbatim.\nIn Redis 7.0, verbatim script replication had been removed entirely.\nThe following section only applies to versions lower than Redis 7.0 when not using effect-based script replication.\n\nAn important part of scripting is writing scripts that only change the database in a deterministic way.\nScripts executed in a Redis instance are, by default until version 5.0, propagated to replicas and to the AOF file by sending the script itself -- not the resulting commands.\nSince the script will be re-run on the remote host (or when reloading the AOF file), its changes to the database must be reproducible.\n\nThe reason for sending the script is that it is often much faster than sending the multiple commands that the script generates.\nIf the client is sending many scripts to the master, converting the scripts into individual commands for the replica / AOF would result in too much bandwidth for the replication link or the Append Only File (and also too much CPU since dispatching a command received via the network is a lot more work for Redis compared to dispatching a command invoked by Lua scripts).\n\nNormally replicating scripts instead of the effects of the scripts makes sense, however not in all the cases.\nSo starting with Redis 3.2, the scripting engine is able to, alternatively, replicate the sequence of write commands resulting from the script execution, instead of replication the script itself.\n\nIn this section, we'll assume that scripts are replicated verbatim by sending the whole script.\nLet's call this replication mode **verbatim scripts replication**.\n\nThe main drawback with the *whole scripts replication* approach is that scripts are required to have the following property:\nthe script **always must** execute the same Redis _write_ commands with the same arguments given the same input data set.\nOperations performed by the script can't depend on any hidden (non-explicit) information or state that may change as the script execution proceeds or between different executions of the script.\nNor can it depend on any external input from I/O devices.\n\nActs such as using the system time, calling Redis commands that return random values (e.g., [`RANDOMKEY`](https://redis.io/docs/latest/commands/randomkey)), or using Lua's random number generator, could result in scripts that will not evaluate consistently.\n\nTo enforce the deterministic behavior of scripts, Redis does the following:\n\n* Lua does not export commands to access the system time or other external states.\n* Redis will block the script with an error if a script calls a Redis command able to alter the data set **after** a Redis _random_ command like [`RANDOMKEY`](https://redis.io/docs/latest/commands/randomkey), [`SRANDMEMBER`](https://redis.io/docs/latest/commands/srandmember), [`TIME`](https://redis.io/docs/latest/commands/time).\n  That means that read-only scripts that don't modify the dataset can call those commands.\n  Note that a _random command_ does not necessarily mean a command that uses random numbers: any non-deterministic command is considered as a random command (the best example in this regard is the [`TIME`](https://redis.io/docs/latest/commands/time) command).\n* In Redis version 4.0, commands that may return elements in random order, such as [`SMEMBERS`](https://redis.io/docs/latest/commands/smembers) (because Redis Sets are _unordered_), exhibit a different behavior when called from Lua,\nand undergo a silent lexicographical sorting filter before returning data to Lua scripts.\n  So `redis.call(\"SMEMBERS\",KEYS[1])` will always return the Set elements in the same order, while the same command invoked by normal clients may return different results even if the key contains exactly the same elements.\n  However, starting with Redis 5.0, this ordering is no longer performed because replicating effects circumvents this type of non-determinism.\n  In general, even when developing for Redis 4.0, never assume that certain commands in Lua will be ordered, but instead rely on the documentation of the original command you call to see the properties it provides.\n* Lua's pseudo-random number generation function `math.random` is modified and always uses the same seed for every execution.\n  This means that calling [`math.random`](https://redis.io/docs/latest/develop/programmability/lua-api#runtime-libraries) will always generate the same sequence of numbers every time a script is executed (unless `math.randomseed` is used).\n\nAll that said, you can still use commands that write and random behavior with a simple trick.\nImagine that you want to write a Redis script that will populate a list with N random integers.\n\nThe initial implementation in Ruby could look like this:\n\n[code example]\n\nEvery time this code runs, the resulting list will have exactly the\nfollowing elements:\n\n[code example]\n\nTo make the script both deterministic and still have it produce different random elements,\nwe can add an extra argument to the script that's the seed to Lua's pseudo-random number generator.\nThe new script is as follows:\n\n[code example]\n\nWhat we are doing here is sending the seed of the PRNG as one of the arguments.\nThe script output will always be the same given the same arguments (our requirement) but we are changing one of the arguments at every invocation,\ngenerating the random seed client-side.\nThe seed will be propagated as one of the arguments both in the replication link and in the Append Only File,\nguaranteeing that the same changes will be generated when the AOF is reloaded or when the replica processes the script.\n\nNote: an important part of this behavior is that the PRNG that Redis implements as `math.random` and `math.randomseed` is guaranteed to have the same output regardless of the architecture of the system running Redis.\n32-bit, 64-bit, big-endian and little-endian systems will all produce the same output."},{"id":"debugging-eval-scripts","title":"Debugging Eval scripts","role":"content","text":"Starting with Redis 3.2, Redis has support for native Lua debugging.\nThe Redis Lua debugger is a remote debugger consisting of a server, which is Redis itself, and a client, which is by default [`redis-cli`](https://redis.io/docs/latest/develop/tools/cli).\n\nThe Lua debugger is described in the [Lua scripts debugging](https://redis.io/docs/latest/develop/programmability/lua-debugging) section of the Redis documentation."},{"id":"execution-under-low-memory-conditions","title":"Execution under low memory conditions","role":"content","text":"When memory usage in Redis exceeds the `maxmemory` limit, the first write command encountered in the script that uses additional memory will cause the script to abort (unless [`redis.pcall`](https://redis.io/docs/latest/develop/programmability/lua-api#redis.pcall) was used).\n\nHowever, an exception to the above is when the script's first write command does not use additional memory, as is the case with  (for example, [`DEL`](https://redis.io/docs/latest/commands/del) and [`LREM`](https://redis.io/docs/latest/commands/lrem)).\nIn this case, Redis will allow all commands in the script to run to ensure atomicity.\nIf subsequent writes in the script consume additional memory, Redis' memory usage can exceed the threshold set by the `maxmemory` configuration directive.\n\nAnother scenario in which a script can cause memory usage to cross the `maxmemory` threshold is when the execution begins when Redis is slightly below `maxmemory`, so the script's first write command is allowed.\nAs the script executes, subsequent write commands consume more memory leading to the server using more RAM than the configured `maxmemory` directive.\n\nIn those scenarios, you should consider setting the `maxmemory-policy` configuration directive to any values other than `noeviction`.\nIn addition, Lua scripts should be as fast as possible so that eviction can kick in between executions.\n\nNote that you can change this behaviour by using [flags](#eval-flags)"},{"id":"eval-flags","title":"Eval flags","role":"content","text":"Normally, when you run an Eval script, the server does not know how it accesses the database.\nBy default, Redis assumes that all scripts read and write data.\nHowever, starting with Redis 7.0, there's a way to declare flags when creating a script in order to tell Redis how it should behave.\n\nThe way to do that is by using a Shebang statement on the first line of the script like so:\n\n[code example]\n\nNote that as soon as Redis sees the `#!` comment, it'll treat the script as if it declares flags, even if no flags are defined,\nit still has a different set of defaults compared to a script without a `#!` line.\n\nAnother difference is that scripts without `#!` can run commands that access keys belonging to different cluster hash slots, but ones with `#!` inherit the default flags, so they cannot.\n\nPlease refer to [Script flags](https://redis.io/docs/latest/develop/programmability/lua-api#script_flags) to learn about the various scripts and the defaults."}],"examples":[{"id":"getting-started-ex0","language":"plaintext","code":"> EVAL \"return 'Hello, scripting!'\" 0\n\"Hello, scripting!\"","section_id":"getting-started"},{"id":"script-parameterization-ex0","language":"plaintext","code":"redis> EVAL \"return 'Hello'\" 0\n\"Hello\"\nredis> EVAL \"return 'Scripting!'\" 0\n\"Scripting!\"","section_id":"script-parameterization"},{"id":"script-parameterization-ex1","language":"plaintext","code":"redis> EVAL \"return ARGV[1]\" 0 Hello\n\"Hello\"\nredis> EVAL \"return ARGV[1]\" 0 Parameterization!\n\"Parameterization!\"","section_id":"script-parameterization"},{"id":"script-parameterization-ex2","language":"plaintext","code":"redis> EVAL \"return { KEYS[1], KEYS[2], ARGV[1], ARGV[2], ARGV[3] }\" 2 key1 key2 arg1 arg2 arg3\n1) \"key1\"\n2) \"key2\"\n3) \"arg1\"\n4) \"arg2\"\n5) \"arg3\"","section_id":"script-parameterization"},{"id":"interacting-with-redis-from-a-script-ex0","language":"plaintext","code":"> EVAL \"return redis.call('SET', KEYS[1], ARGV[1])\" 1 foo bar\nOK","section_id":"interacting-with-redis-from-a-script"},{"id":"script-cache-ex0","language":"plaintext","code":"redis> SCRIPT LOAD \"return 'Immabe a cached script'\"\n\"c664a3bf70bd1d45c4284ffebb65a6f2299bfc9f\"\nredis> EVALSHA c664a3bf70bd1d45c4284ffebb65a6f2299bfc9f 0\n\"Immabe a cached script\"","section_id":"script-cache"},{"id":"cache-volatility-ex0","language":"plaintext","code":"redis> EVALSHA ffffffffffffffffffffffffffffffffffffffff 0\n(error) NOSCRIPT No matching script","section_id":"cache-volatility"},{"id":"replicating-commands-instead-of-scripts-ex0","language":"lua","code":"redis.replicate_commands()","section_id":"replicating-commands-instead-of-scripts"},{"id":"scripts-with-deterministic-writes-ex0","language":"plaintext","code":"require 'rubygems'\nrequire 'redis'\n\nr = Redis.new\n\nRandomPushScript = <<EOF\n    local i = tonumber(ARGV[1])\n    local res\n    while (i > 0) do\n        res = redis.call('LPUSH',KEYS[1],math.random())\n        i = i-1\n    end\n    return res\nEOF\n\nr.del(:mylist)\nputs r.eval(RandomPushScript,[:mylist],[10,rand(2**32)])","section_id":"scripts-with-deterministic-writes"},{"id":"scripts-with-deterministic-writes-ex1","language":"plaintext","code":"redis> LRANGE mylist 0 -1\n 1) \"0.74509509873814\"\n 2) \"0.87390407681181\"\n 3) \"0.36876626981831\"\n 4) \"0.6921941534114\"\n 5) \"0.7857992587545\"\n 6) \"0.57730350670279\"\n 7) \"0.87046522734243\"\n 8) \"0.09637165539729\"\n 9) \"0.74990198051087\"\n10) \"0.17082803611217\"","section_id":"scripts-with-deterministic-writes"},{"id":"scripts-with-deterministic-writes-ex2","language":"plaintext","code":"RandomPushScript = <<EOF\n    local i = tonumber(ARGV[1])\n    local res\n    math.randomseed(tonumber(ARGV[2]))\n    while (i > 0) do\n        res = redis.call('LPUSH',KEYS[1],math.random())\n        i = i-1\n    end\n    return res\nEOF\n\nr.del(:mylist)\nputs r.eval(RandomPushScript,1,:mylist,10,rand(2**32))","section_id":"scripts-with-deterministic-writes"},{"id":"eval-flags-ex0","language":"plaintext","code":"#!lua flags=no-writes,allow-stale\nlocal x = redis.call('get','x')\nreturn x","section_id":"eval-flags"}]}
{"schema_version":2,"id":"develop/programmability/functions-intro","title":"Redis functions","url":"https://redis.io/docs/latest/develop/programmability/functions-intro/","summary":"Scripting with Redis 7 and beyond","aliases":["/develop/interact/programmability/functions-intro","/interact/programmability/functions-intro/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T14:32:43+01:00","page_type":"content","content_hash":"f6af769ad19d5b507fc3cae85870d9b4144d5f782fcaa75bd935b79eec3ddfe2","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Functions is an API for managing code to be executed on the server. This feature, which became available in Redis 7, supersedes the use of [EVAL](https://redis.io/docs/latest/develop/programmability/eval-intro) in prior versions of Redis."},{"id":"prologue-or-whats-wrong-with-eval-scripts","title":"Prologue (or, what's wrong with Eval Scripts?)","role":"content","text":"Prior versions of Redis made scripting available only via the [`EVAL`](https://redis.io/docs/latest/commands/eval) command, which allows a Lua script to be sent for execution by the server.\nThe core use cases for [Eval Scripts](https://redis.io/docs/latest/develop/programmability/eval-intro) is executing part of your application logic inside Redis, efficiently and atomically.\nSuch script can perform conditional updates across multiple keys, possibly combining several different data types.\n\nUsing [`EVAL`](https://redis.io/docs/latest/commands/eval) requires that the application sends the entire script for execution every time.\nBecause this results in network and script compilation overheads, Redis provides an optimization in the form of the [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) command. By first calling [`SCRIPT LOAD`](https://redis.io/docs/latest/commands/script-load) to obtain the script's SHA1, the application can invoke it repeatedly afterward with its digest alone.\n\nBy design, Redis only caches the loaded scripts.\nThat means that the script cache can become lost at any time, such as after calling [`SCRIPT FLUSH`](https://redis.io/docs/latest/commands/script-flush), after restarting the server, or when failing over to a replica.\nThe application is responsible for reloading scripts during runtime if any are missing.\nThe underlying assumption is that scripts are a part of the application and not maintained by the Redis server.\n\nThis approach suits many light-weight scripting use cases, but introduces several difficulties once an application becomes complex and relies more heavily on scripting, namely:\n\n1. All client application instances must maintain a copy of all scripts. That means having some mechanism that applies script updates to all of the application's instances.\n1. Calling cached scripts within the context of a [transaction](https://redis.io/docs/latest/develop/using-commands/transactions) increases the probability of the transaction failing because of a missing script. Being more likely to fail makes using cached scripts as building blocks of workflows less attractive.\n1. SHA1 digests are meaningless, making debugging the system extremely hard (e.g., in a [`MONITOR`](https://redis.io/docs/latest/commands/monitor) session).\n1. When used naively, [`EVAL`](https://redis.io/docs/latest/commands/eval) promotes an anti-pattern in which scripts the client application renders verbatim scripts instead of responsibly using the [`KEYS` and `ARGV` Lua APIs](https://redis.io/docs/latest/develop/programmability/lua-api#runtime-globals).\n1. Because they are ephemeral, a script can't call another script. This makes sharing and reusing code between scripts nearly impossible, short of client-side preprocessing (see the first point).\n\nTo address these needs while avoiding breaking changes to already-established and well-liked ephemeral scripts, Redis v7.0 introduces Redis Functions."},{"id":"what-are-redis-functions","title":"What are Redis Functions?","role":"content","text":"Redis functions are an evolutionary step from ephemeral scripting.\n\nFunctions provide the same core functionality as scripts but are first-class software artifacts of the database.\nRedis manages functions as an integral part of the database and ensures their availability via data persistence and replication.\nBecause functions are part of the database and therefore declared before use, applications aren't required to load them during runtime nor risk aborted transactions.\nAn application that uses functions depends only on their APIs rather than on the embedded script logic in the database.\n\nWhereas ephemeral scripts are considered a part of the application's domain, functions extend the database server itself with user-provided logic.\nThey can be used to expose a richer API composed of core Redis commands, similar to modules, developed once, loaded at startup, and used repeatedly by various applications / clients.\nEvery function has a unique user-defined name, making it much easier to call and trace its execution.\n\nThe design of Redis Functions also attempts to demarcate between the programming language used for writing functions and their management by the server.\nLua, the only language interpreter that Redis presently support as an embedded execution engine, is meant to be simple and easy to learn.\nHowever, the choice of Lua as a language still presents many Redis users with a challenge.\n\nThe Redis Functions feature makes no assumptions about the implementation's language.\nAn execution engine that is part of the definition of the function handles running it.\nAn engine can theoretically execute functions in any language as long as it respects several rules (such as the ability to terminate an executing function).\n\nPresently, as noted above, Redis ships with a single embedded [Lua 5.1](https://redis.io/docs/latest/develop/programmability/lua-api) engine.\nThere are plans to support additional engines in the future.\nRedis functions can use all of Lua's available capabilities to ephemeral scripts,\nwith the only exception being the [Redis Lua scripts debugger](https://redis.io/docs/latest/develop/programmability/lua-debugging).\n\nFunctions also simplify development by enabling code sharing.\nEvery function belongs to a single library, and any given library can consist of multiple functions.\nThe library's contents are immutable, and selective updates of its functions aren't allowed.\nInstead, libraries are updated as a whole with all of their functions together in one operation.\nThis allows calling functions from other functions within the same library, or sharing code between functions by using a common code in library-internal methods, that can also take language native arguments.\n\nFunctions are intended to better support the use case of maintaining a consistent view for data entities through a logical schema, as mentioned above.\nAs such, functions are stored alongside the data itself.\nFunctions are also persisted to the AOF file and replicated from master to replicas, so they are as durable as the data itself.\nWhen Redis is used as an ephemeral cache, additional mechanisms (described below) are required to make functions more durable.\n\nLike all other operations in Redis, the execution of a function is atomic.\nA function's execution blocks all server activities during its entire time, similarly to the semantics of [transactions](https://redis.io/docs/latest/develop/using-commands/transactions).\nThese semantics mean that all of the script's effects either have yet to happen or had already happened.\nThe blocking semantics of an executed function apply to all connected clients at all times.\nBecause running a function blocks the Redis server, functions are meant to finish executing quickly, so you should avoid using long-running functions."},{"id":"loading-libraries-and-functions","title":"Loading libraries and functions","role":"content","text":"Let's explore Redis Functions via some tangible examples and Lua snippets.\n\nAt this point, if you're unfamiliar with Lua in general and specifically in Redis, you may benefit from reviewing some of the examples in [Introduction to Eval Scripts](https://redis.io/docs/latest/develop/programmability/eval-intro) and [Lua API](https://redis.io/docs/latest/develop/programmability/lua-api) pages for a better grasp of the language.\n\nEvery Redis function belongs to a single library that's loaded to Redis.\nLoading a library to the database is done with the [`FUNCTION LOAD`](https://redis.io/docs/latest/commands/function-load) command.\nThe command gets the library payload as input,\nthe library payload must start with Shebang statement that provides a metadata about the library (like the engine to use and the library name).\nThe Shebang format is:\n[code example]\n\nLet's try loading an empty library:\n\n[code example]\n\nThe error is expected, as there are no functions in the loaded library. Every library needs to include at least one registered function to load successfully.\nA registered function is named and acts as an entry point to the library.\nWhen the target execution engine handles the [`FUNCTION LOAD`](https://redis.io/docs/latest/commands/function-load) command, it registers the library's functions.\n\nThe Lua engine compiles and evaluates the library source code when loaded, and expects functions to be registered by calling the `redis.register_function()` API.\n\nThe following snippet demonstrates a simple library registering a single function named _knockknock_, returning a string reply:\n\n[code example]\n\nIn the example above, we provide two arguments about the function to Lua's `redis.register_function()` API: its registered name and a callback.\n\nWe can load our library and use [`FCALL`](https://redis.io/docs/latest/commands/fcall) to call the registered function:\n\n[code example]\n\nNotice that the [`FUNCTION LOAD`](https://redis.io/docs/latest/commands/function-load) command returns the name of the loaded library, this name can later be used [`FUNCTION LIST`](https://redis.io/docs/latest/commands/function-list) and [`FUNCTION DELETE`](https://redis.io/docs/latest/commands/function-delete).\n\nWe've provided [`FCALL`](https://redis.io/docs/latest/commands/fcall) with two arguments: the function's registered name and the numeric value `0`. This numeric value indicates the number of key names that follow it (the same way [`EVAL`](https://redis.io/docs/latest/commands/eval) and [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) work).\n\nWe'll explain immediately how key names and additional arguments are available to the function. As this simple example doesn't involve keys, we simply use 0 for now."},{"id":"input-keys-and-regular-arguments","title":"Input keys and regular arguments","role":"content","text":"Before we move to the following example, it is vital to understand the distinction Redis makes between arguments that are names of keys and those that aren't.\n\nWhile key names in Redis are just strings, unlike any other string values, these represent keys in the database.\nThe name of a key is a fundamental concept in Redis and is the basis for operating the Redis Cluster.\n\n**Important:**\nTo ensure the correct execution of Redis Functions, both in standalone and clustered deployments, all names of keys that a function accesses must be explicitly provided as input key arguments.\n\nAny input to the function that isn't the name of a key is a regular input argument.\n\nNow, let's pretend that our application stores some of its data in Redis Hashes.\nWe want an [`HSET`](https://redis.io/docs/latest/commands/hset)-like way to set and update fields in said Hashes and store the last modification time in a new field named `_last_modified_`.\nWe can implement a function to do all that.\n\nOur function will call [`TIME`](https://redis.io/docs/latest/commands/time) to get the server's clock reading and update the target Hash with the new fields' values and the modification's timestamp.\nThe function we'll implement accepts the following input arguments: the Hash's key name and the field-value pairs to update.\n\nThe Lua API for Redis Functions makes these inputs accessible as the first and second arguments to the function's callback.\nThe callback's first argument is a Lua table populated with all key names inputs to the function.\nSimilarly, the callback's second argument consists of all regular arguments.\n\nThe following is a possible implementation for our function and its library registration:\n\n[code example]\n\nIf we create a new file named _mylib.lua_ that consists of the library's definition, we can load it like so (without stripping the source code of helpful whitespaces):\n\n[code example]\n\nWe've added the `REPLACE` modifier to the call to [`FUNCTION LOAD`](https://redis.io/docs/latest/commands/function-load) to tell Redis that we want to overwrite the existing library definition.\nOtherwise, we would have gotten an error from Redis complaining that the library already exists.\n\nNow that the library's updated code is loaded to Redis, we can proceed and call our function:\n\n[code example]\n\nIn this case, we had invoked [`FCALL`](https://redis.io/docs/latest/commands/fcall) with _1_ as the number of key name arguments.\nThat means that the function's first input argument is a name of a key (and is therefore included in the callback's `keys` table).\nAfter that first argument, all following input arguments are considered regular arguments and constitute the `args` table passed to the callback as its second argument."},{"id":"expanding-the-library","title":"Expanding the library","role":"content","text":"We can add more functions to our library to benefit our application.\nThe additional metadata field we've added to the Hash shouldn't be included in responses when accessing the Hash's data.\nOn the other hand, we do want to provide the means to obtain the modification timestamp for a given Hash key.\n\nWe'll add two new functions to our library to accomplish these objectives:\n\n1. The `my_hgetall` Redis Function will return all fields and their respective values from a given Hash key name, excluding the metadata (i.e., the `_last_modified_` field).\n1. The `my_hlastmodified` Redis Function will return the modification timestamp for a given Hash key name.\n\nThe library's source code could look something like the following:\n\n[code example]\n\nWhile all of the above should be straightforward, note that the `my_hgetall` also calls [`redis.setresp(3)`](https://redis.io/docs/latest/develop/programmability/lua-api#redis.setresp).\nThat means that the function expects [RESP3](https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md) replies after calling `redis.call()`, which, unlike the default RESP2 protocol, provides dictionary (associative arrays) replies.\nDoing so allows the function to delete (or set to `nil` as is the case with Lua tables) specific fields from the reply, and in our case, the `_last_modified_` field.\n\nAssuming you've saved the library's implementation in the _mylib.lua_ file, you can replace it with:\n\n[code example]\n\nOnce loaded, you can call the library's functions with [`FCALL`](https://redis.io/docs/latest/commands/fcall):\n\n[code example]\n\nYou can also get the library's details with the [`FUNCTION LIST`](https://redis.io/docs/latest/commands/function-list) command:\n\n[code example]\n\nYou can see that it is easy to update our library with new capabilities."},{"id":"reusing-code-in-the-library","title":"Reusing code in the library","role":"content","text":"On top of bundling functions together into database-managed software artifacts, libraries also facilitate code sharing.\nWe can add to our library an error handling helper function called from other functions.\nThe helper function `check_keys()` verifies that the input _keys_ table has a single key.\nUpon success it returns `nil`, otherwise it returns an [error reply](https://redis.io/docs/latest/develop/programmability/lua-api#redis.error_reply).\n\nThe updated library's source code would be:\n\n[code example]\n\nAfter you've replaced the library in Redis with the above, you can immediately try out the new error handling mechanism:\n\n[code example]\n\nAnd your Redis log file should have lines in it that are similar to:\n\n[code example]"},{"id":"functions-in-cluster","title":"Functions in cluster","role":"content","text":"As noted above, Redis automatically handles propagation of loaded functions to replicas.\nIn a Redis Cluster, it is also necessary to load functions to all cluster nodes. This is not handled automatically by Redis Cluster, and needs to be handled by the cluster administrator (like module loading, configuration setting, etc.).\n\nAs one of the goals of functions is to live separately from the client application, this should not be part of the Redis client library responsibilities. Instead, `redis-cli --cluster-only-masters --cluster call host:port FUNCTION LOAD ...` can be used to execute the load command on all master nodes.\n\nAlso, note that `redis-cli --cluster add-node` automatically takes care to propagate the loaded functions from one of the existing nodes to the new node."},{"id":"functions-and-ephemeral-redis-instances","title":"Functions and ephemeral Redis instances","role":"content","text":"In some cases there may be a need to start a fresh Redis server with a set of functions pre-loaded. Common reasons for that could be:\n\n* Starting Redis in a new environment\n* Re-starting an ephemeral (cache-only) Redis, that uses functions\n\nIn such cases, we need to make sure that the pre-loaded functions are available before Redis accepts inbound user connections and commands.\n\nTo do that, it is possible to use `redis-cli --functions-rdb` to extract the functions from an existing server. This generates an RDB file that can be loaded by Redis at startup."},{"id":"function-flags","title":"Function flags","role":"content","text":"Redis needs to have some information about how a function is going to behave when executed, in order to properly enforce resource usage policies and maintain data consistency.\n\nFor example, Redis needs to know that a certain function is read-only before permitting it to execute using [`FCALL_RO`](https://redis.io/docs/latest/commands/fcall_ro) on a read-only replica.\n\nBy default, Redis assumes that all functions may perform arbitrary read or write operations. Function Flags make it possible to declare more specific function behavior at the time of registration. Let's see how this works.\n\nIn our previous example, we defined two functions that only read data. We can try executing them using [`FCALL_RO`](https://redis.io/docs/latest/commands/fcall_ro) against a read-only replica.\n\n[code example]\n\nRedis returns this error because a function can, in theory, perform both read and write operations on the database.\nAs a safeguard and by default, Redis assumes that the function does both, so it blocks its execution.\nThe server will reply with this error in the following cases:\n\n1. Executing a function with [`FCALL`](https://redis.io/docs/latest/commands/fcall) against a read-only replica.\n2. Using [`FCALL_RO`](https://redis.io/docs/latest/commands/fcall_ro) to execute a function.\n3. A disk error was detected (Redis is unable to persist so it rejects writes).\n\nIn these cases, you can add the `no-writes` flag to the function's registration, disable the safeguard and allow them to run.\nTo register a function with flags use the [named arguments](https://redis.io/docs/latest/develop/programmability/lua-api#redis.register_function_named_args) variant of `redis.register_function`.\n\nThe updated registration code snippet from the library looks like this:\n\n[code example]\n\nOnce we've replaced the library, Redis allows running both `my_hgetall` and `my_hlastmodified` with [`FCALL_RO`](https://redis.io/docs/latest/commands/fcall_ro) against a read-only replica:\n\n[code example]\n\nFor the complete documentation flags, please refer to [Script flags](https://redis.io/docs/latest/develop/programmability/lua-api#script_flags)."}],"examples":[{"id":"loading-libraries-and-functions-ex0","language":"plaintext","code":"#!<engine name> name=<library name>","section_id":"loading-libraries-and-functions"},{"id":"loading-libraries-and-functions-ex1","language":"plaintext","code":"redis> FUNCTION LOAD \"#!lua name=mylib\\n\"\n(error) ERR No functions registered","section_id":"loading-libraries-and-functions"},{"id":"loading-libraries-and-functions-ex2","language":"lua","code":"#!lua name=mylib\nredis.register_function(\n  'knockknock',\n  function() return 'Who\\'s there?' end\n)","section_id":"loading-libraries-and-functions"},{"id":"loading-libraries-and-functions-ex3","language":"plaintext","code":"redis> FUNCTION LOAD \"#!lua name=mylib\\nredis.register_function('knockknock', function() return 'Who\\\\'s there?' end)\"\nmylib\nredis> FCALL knockknock 0\n\"Who's there?\"","section_id":"loading-libraries-and-functions"},{"id":"input-keys-and-regular-arguments-ex0","language":"lua","code":"#!lua name=mylib\n\nlocal function my_hset(keys, args)\n  local hash = keys[1]\n  local time = redis.call('TIME')[1]\n  return redis.call('HSET', hash, '_last_modified_', time, unpack(args))\nend\n\nredis.register_function('my_hset', my_hset)","section_id":"input-keys-and-regular-arguments"},{"id":"input-keys-and-regular-arguments-ex1","language":"bash","code":"$ cat mylib.lua | redis-cli -x FUNCTION LOAD REPLACE","section_id":"input-keys-and-regular-arguments"},{"id":"input-keys-and-regular-arguments-ex2","language":"plaintext","code":"redis> FCALL my_hset 1 myhash myfield \"some value\" another_field \"another value\"\n(integer) 3\nredis> HGETALL myhash\n1) \"_last_modified_\"\n2) \"1640772721\"\n3) \"myfield\"\n4) \"some value\"\n5) \"another_field\"\n6) \"another value\"","section_id":"input-keys-and-regular-arguments"},{"id":"expanding-the-library-ex0","language":"lua","code":"#!lua name=mylib\n\nlocal function my_hset(keys, args)\n  local hash = keys[1]\n  local time = redis.call('TIME')[1]\n  return redis.call('HSET', hash, '_last_modified_', time, unpack(args))\nend\n\nlocal function my_hgetall(keys, args)\n  redis.setresp(3)\n  local hash = keys[1]\n  local res = redis.call('HGETALL', hash)\n  res['map']['_last_modified_'] = nil\n  return res\nend\n\nlocal function my_hlastmodified(keys, args)\n  local hash = keys[1]\n  return redis.call('HGET', hash, '_last_modified_')\nend\n\nredis.register_function('my_hset', my_hset)\nredis.register_function('my_hgetall', my_hgetall)\nredis.register_function('my_hlastmodified', my_hlastmodified)","section_id":"expanding-the-library"},{"id":"expanding-the-library-ex1","language":"bash","code":"$ cat mylib.lua | redis-cli -x FUNCTION LOAD REPLACE","section_id":"expanding-the-library"},{"id":"expanding-the-library-ex2","language":"plaintext","code":"redis> FCALL my_hgetall 1 myhash\n1) \"myfield\"\n2) \"some value\"\n3) \"another_field\"\n4) \"another value\"\nredis> FCALL my_hlastmodified 1 myhash\n\"1640772721\"","section_id":"expanding-the-library"},{"id":"expanding-the-library-ex3","language":"plaintext","code":"redis> FUNCTION LIST\n1) 1) \"library_name\"\n   2) \"mylib\"\n   3) \"engine\"\n   4) \"LUA\"\n   5) \"functions\"\n   6) 1) 1) \"name\"\n         2) \"my_hset\"\n         3) \"description\"\n         4) (nil)\n         5) \"flags\"\n         6) (empty array)\n      2) 1) \"name\"\n         2) \"my_hgetall\"\n         3) \"description\"\n         4) (nil)\n         5) \"flags\"\n         6) (empty array)\n      3) 1) \"name\"\n         2) \"my_hlastmodified\"\n         3) \"description\"\n         4) (nil)\n         5) \"flags\"\n         6) (empty array)","section_id":"expanding-the-library"},{"id":"reusing-code-in-the-library-ex0","language":"lua","code":"#!lua name=mylib\n\nlocal function check_keys(keys)\n  local error = nil\n  local nkeys = table.getn(keys)\n  if nkeys == 0 then\n    error = 'Hash key name not provided'\n  elseif nkeys > 1 then\n    error = 'Only one key name is allowed'\n  end\n\n  if error ~= nil then\n    redis.log(redis.LOG_WARNING, error);\n    return redis.error_reply(error)\n  end\n  return nil\nend\n\nlocal function my_hset(keys, args)\n  local error = check_keys(keys)\n  if error ~= nil then\n    return error\n  end\n\n  local hash = keys[1]\n  local time = redis.call('TIME')[1]\n  return redis.call('HSET', hash, '_last_modified_', time, unpack(args))\nend\n\nlocal function my_hgetall(keys, args)\n  local error = check_keys(keys)\n  if error ~= nil then\n    return error\n  end\n\n  redis.setresp(3)\n  local hash = keys[1]\n  local res = redis.call('HGETALL', hash)\n  res['map']['_last_modified_'] = nil\n  return res\nend\n\nlocal function my_hlastmodified(keys, args)\n  local error = check_keys(keys)\n  if error ~= nil then\n    return error\n  end\n\n  local hash = keys[1]\n  return redis.call('HGET', keys[1], '_last_modified_')\nend\n\nredis.register_function('my_hset', my_hset)\nredis.register_function('my_hgetall', my_hgetall)\nredis.register_function('my_hlastmodified', my_hlastmodified)","section_id":"reusing-code-in-the-library"},{"id":"reusing-code-in-the-library-ex1","language":"plaintext","code":"127.0.0.1:6379> FCALL my_hset 0 myhash nope nope\n(error) Hash key name not provided\n127.0.0.1:6379> FCALL my_hgetall 2 myhash anotherone\n(error) Only one key name is allowed","section_id":"reusing-code-in-the-library"},{"id":"reusing-code-in-the-library-ex2","language":"plaintext","code":"...\n20075:M 1 Jan 2022 16:53:57.688 # Hash key name not provided\n20075:M 1 Jan 2022 16:54:01.309 # Only one key name is allowed","section_id":"reusing-code-in-the-library"},{"id":"function-flags-ex0","language":"plaintext","code":"redis > FCALL_RO my_hgetall 1 myhash\n(error) ERR Can not execute a function with write flag using fcall_ro.","section_id":"function-flags"},{"id":"function-flags-ex1","language":"lua","code":"redis.register_function('my_hset', my_hset)\nredis.register_function{\n  function_name='my_hgetall',\n  callback=my_hgetall,\n  flags={ 'no-writes' }\n}\nredis.register_function{\n  function_name='my_hlastmodified',\n  callback=my_hlastmodified,\n  flags={ 'no-writes' }\n}","section_id":"function-flags"},{"id":"function-flags-ex2","language":"plaintext","code":"redis> FCALL_RO my_hgetall 1 myhash\n1) \"myfield\"\n2) \"some value\"\n3) \"another_field\"\n4) \"another value\"\nredis> FCALL_RO my_hlastmodified 1 myhash\n\"1640772721\"","section_id":"function-flags"}]}
{"schema_version":2,"id":"develop/programmability","title":"Redis programmability","url":"https://redis.io/docs/latest/develop/programmability/","summary":"Extending Redis with Lua and Redis Functions","aliases":["/develop/interact/programmability","/develop/interact/programmability/","/manual/programmability/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T14:32:43+01:00","children":[{"id":"develop/programmability/functions-intro","summary":"Scripting with Redis 7 and beyond","title":"Redis functions","url":"https://redis.io/docs/latest/develop/programmability/functions-intro/"},{"id":"develop/programmability/eval-intro","summary":"Executing Lua in Redis","title":"Scripting with Lua","url":"https://redis.io/docs/latest/develop/programmability/eval-intro/"},{"id":"develop/programmability/lua-api","summary":"Executing Lua in Redis","title":"Redis Lua API reference","url":"https://redis.io/docs/latest/develop/programmability/lua-api/"},{"id":"develop/programmability/lua-debugging","summary":"How to use the built-in Lua debugger","title":"Debugging Lua scripts in Redis","url":"https://redis.io/docs/latest/develop/programmability/lua-debugging/"}],"page_type":"content","content_hash":"89306d513596a3e20439e1eb252a4eaa51069483bd962d3ccaffb73fff5ab0b2","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis provides a programming interface that lets you execute custom scripts on the server itself. In Redis 7 and beyond, you can use [Redis Functions](https://redis.io/docs/latest/develop/programmability/functions-intro) to manage and run your scripts. In Redis 6.2 and below, you use [Lua scripting with the EVAL command](https://redis.io/docs/latest/develop/programmability/eval-intro) to program the server."},{"id":"background","title":"Background","role":"content","text":"Redis is, by [definition](https://github.com/redis/redis/blob/3eaa2cdc44a9b0742f0695f44911b92547995836/MANIFESTO#L7), a _\"domain-specific language for abstract data types\"_.\nThe language that Redis speaks consists of its [commands](/commands).\nMost the commands specialize at manipulating core [data types](https://redis.io/docs/latest/develop/data-types) in different ways.\nIn many cases, these commands provide all the functionality that a developer requires for managing application data in Redis.\n\nThe term **programmability** in Redis means having the ability to execute arbitrary user-defined logic by the server.\nWe refer to such pieces of logic as **scripts**.\nIn our case, scripts enable processing the data where it lives, a.k.a _data locality_.\nFurthermore, the responsible embedding of programmatic workflows in the Redis server can help in reducing network traffic and improving overall performance.\nDevelopers can use this capability for implementing robust, application-specific APIs.\nSuch APIs can encapsulate business logic and maintain a data model across multiple keys and different data structures.\n\nUser scripts are executed in Redis by an embedded, sandboxed scripting engine.\nPresently, Redis supports a single scripting engine, the [Lua 5.1](https://www.lua.org/) interpreter.\n\nPlease refer to the [Redis Lua API Reference](https://redis.io/docs/latest/develop/programmability/lua-api) page for complete documentation."},{"id":"running-scripts","title":"Running scripts","role":"content","text":"Redis provides two means for running scripts.\n\nFirstly, and ever since Redis 2.6.0, the [`EVAL`](https://redis.io/docs/latest/commands/eval) command enables running server-side scripts.\nEval scripts provide a quick and straightforward way to have Redis run your scripts ad-hoc.\nHowever, using them means that the scripted logic is a part of your application (not an extension of the Redis server).\nEvery applicative instance that runs a script must have the script's source code readily available for loading at any time.\nThat is because scripts are only cached by the server and are volatile.\nAs your application grows, this approach can become harder to develop and maintain.\n\nSecondly, added in v7.0, Redis Functions are essentially scripts that are first-class database elements.\nAs such, functions decouple scripting from application logic and enable independent development, testing, and deployment of scripts.\nTo use functions, they need to be loaded first, and then they are available for use by all connected clients.\nIn this case, loading a function to the database becomes an administrative deployment task (such as loading a Redis module, for example), which separates the script from the application.\n\nPlease refer to the following pages for more information:\n\n* [Redis Eval Scripts](https://redis.io/docs/latest/develop/programmability/eval-intro)\n* [Redis Functions](https://redis.io/docs/latest/develop/programmability/functions-intro)\n\nWhen running a script or a function, Redis guarantees its atomic execution.\nThe script's execution blocks all server activities during its entire time, similarly to the semantics of [transactions](https://redis.io/docs/latest/develop/using-commands/transactions).\nThese semantics mean that all of the script's effects either have yet to happen or had already happened.\nThe blocking semantics of an executed script apply to all connected clients at all times.\n\nNote that the potential downside of this blocking approach is that executing slow scripts is not a good idea.\nIt is not hard to create fast scripts because scripting's overhead is very low.\nHowever, if you intend to use a slow script in your application, be aware that all other clients are blocked and can't execute any command while it is running."},{"id":"read-only-scripts","title":"Read-only scripts","role":"content","text":"A read-only script is a script that only executes commands that don't modify any keys within Redis.\nRead-only scripts can be executed either by adding the `no-writes` [flag](https://redis.io/docs/latest/develop/programmability/lua-api#script_flags) to the script or by executing the script with one of the read-only script command variants: [`EVAL_RO`](https://redis.io/docs/latest/commands/eval_ro), [`EVALSHA_RO`](https://redis.io/docs/latest/commands/evalsha_ro), or [`FCALL_RO`](https://redis.io/docs/latest/commands/fcall_ro).\nThey have the following properties:\n\n* They can always be executed on replicas.\n* They can always be killed by the [`SCRIPT KILL`](https://redis.io/docs/latest/commands/script-kill) command. \n* They never fail with OOM error when redis is over the memory limit.\n* They are not blocked during write pauses, such as those that occur during coordinated failovers.\n* They cannot execute any command that may modify the data set.\n* Currently [`PUBLISH`](https://redis.io/docs/latest/commands/publish), [`SPUBLISH`](https://redis.io/docs/latest/commands/spublish) and [`PFCOUNT`](https://redis.io/docs/latest/commands/pfcount) are also considered write commands in scripts, because they could attempt to propagate commands to replicas and AOF file.\n\nIn addition to the benefits provided by all read-only scripts, the read-only script commands have the following advantages:\n\n* They can be used to configure an ACL user to only be able to execute read-only scripts.\n* Many clients also better support routing the read-only script commands to replicas for applications that want to use replicas for read scaling.\n\n#### Read-only script history\n\nRead-only scripts and read-only script commands were introduced in Redis 7.0\n\n* Before Redis 7.0.1 [`PUBLISH`](https://redis.io/docs/latest/commands/publish), [`SPUBLISH`](https://redis.io/docs/latest/commands/spublish) and [`PFCOUNT`](https://redis.io/docs/latest/commands/pfcount) were not considered write commands in scripts\n* Before Redis 7.0.1 the `no-writes` [flag](https://redis.io/docs/latest/develop/programmability/lua-api#script_flags) did not imply `allow-oom`\n* Before Redis 7.0.1 the `no-writes` flag did not permit the script to run during write pauses.\n\n\nThe recommended approach is to use the standard scripting commands with the `no-writes` flag unless you need one of the previously mentioned features."},{"id":"sandboxed-script-context","title":"Sandboxed script context","role":"content","text":"Redis places the engine that executes user scripts inside a sandbox.\nThe sandbox attempts to prevent accidental misuse and reduce potential threats from the server's environment.\n\nScripts should never try to access the Redis server's underlying host systems, such as the file system, network, or attempt to perform any other system call other than those supported by the API.\n\nScripts should operate solely on data stored in Redis and data provided as arguments to their execution."},{"id":"maximum-execution-time","title":"Maximum execution time","role":"content","text":"Scripts are subject to a maximum execution time (set by default to five seconds).\nThis default timeout is enormous since a script usually runs in less than a millisecond.\nThe limit is in place to handle accidental infinite loops created during development.\n\nIt is possible to modify the maximum time a script can be executed with millisecond precision,\neither via `redis.conf` or by using the [`CONFIG SET`](https://redis.io/docs/latest/commands/config-set) command.\nThe configuration parameter affecting max execution time is called `busy-reply-threshold`.\n\nWhen a script reaches the timeout threshold, it isn't terminated by Redis automatically.\nDoing so would violate the contract between Redis and the scripting engine that ensures that scripts are atomic.\nInterrupting the execution of a script has the potential of leaving the dataset with half-written changes.\n\nTherefore, when a script executes longer than the configured timeout, the following happens:\n\n* Redis logs that a script is running for too long.\n* It starts accepting commands again from other clients but will reply with a BUSY error to all the clients sending normal commands. The only commands allowed in this state are [`SCRIPT KILL`](https://redis.io/docs/latest/commands/script-kill), [`FUNCTION KILL`](https://redis.io/docs/latest/commands/function-kill), and `SHUTDOWN NOSAVE`.\n* It is possible to terminate a script that only executes read-only commands using the [`SCRIPT KILL`](https://redis.io/docs/latest/commands/script-kill) and [`FUNCTION KILL`](https://redis.io/docs/latest/commands/function-kill) commands. These commands do not violate the scripting semantic as no data was written to the dataset by the script yet.\n* If the script had already performed even a single write operation, the only command allowed is `SHUTDOWN NOSAVE` that stops the server without saving the current data set on disk (basically, the server is aborted)."}],"examples":[]}
{"schema_version":2,"id":"develop/programmability/lua-api","title":"Redis Lua API reference","url":"https://redis.io/docs/latest/develop/programmability/lua-api/","summary":"Executing Lua in Redis","aliases":["/develop/interact/programmability/lua-api"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T14:32:43+01:00","page_type":"content","content_hash":"32998e8216343277239b51418981e48bba2149a763b93ae92f8ddc5010e950c9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis includes an embedded [Lua 5.1](https://www.lua.org/) interpreter.\nThe interpreter runs user-defined [ephemeral scripts](https://redis.io/docs/latest/develop/programmability/eval-intro) and [functions](https://redis.io/docs/latest/develop/programmability/functions-intro). Scripts run in a sandboxed context and can only access specific Lua packages. This page describes the packages and APIs available inside the execution's context."},{"id":"sandbox-context","title":"Sandbox context","role":"content","text":"The sandboxed Lua context attempts to prevent accidental misuse and reduce potential threats from the server's environment.\n\nScripts should never try to access the Redis server's underlying host systems.\nThat includes the file system, network, and any other attempt to perform a system call other than those supported by the API.\n\nScripts should operate solely on data stored in Redis and data provided as arguments to their execution."},{"id":"global-variables-and-functions","title":"Global variables and functions","role":"content","text":"The sandboxed Lua execution context blocks the declaration of global variables and functions.\nThe blocking of global variables is in place to ensure that scripts and functions don't attempt to maintain any runtime context other than the data stored in Redis.\nIn the (somewhat uncommon) use case that a context needs to be maintain between executions,\nyou should store the context in Redis' keyspace.\n\nRedis will return a \"Script attempted to create global variable 'my_global_variable\" error when trying to execute the following snippet:\n\n[code example]\n\nAnd similarly for the following global function declaration:\n\n[code example]\n\nYou'll also get a similar error when your script attempts to access any global variables that are undefined in the runtime's context:\n\n[code example]\n\nInstead, all variable and function definitions are required to be declared as local.\nTo do so, you'll need to prepend the [_local_](https://www.lua.org/manual/5.1/manual.html#2.4.7) keyword to your declarations.\nFor example, the following snippet will be considered perfectly valid by Redis:\n\n[code example]\n\n**Note:**\nthe sandbox attempts to prevent the use of globals.\nUsing Lua's debugging functionality or other approaches such as altering the meta table used for implementing the globals' protection to circumvent the sandbox isn't hard.\nHowever, it is difficult to circumvent the protection by accident.\nIf the user messes with the Lua global state, the consistency of AOF and replication can't be guaranteed.\nIn other words, just don't do it."},{"id":"imported-lua-modules","title":"Imported Lua modules","role":"content","text":"Using imported Lua modules is not supported inside the sandboxed execution context.\nThe sandboxed execution context prevents the loading modules by disabling Lua's [`require` function](https://www.lua.org/pil/8.1.html).\n\nThe only libraries that Redis ships with and that you can use in scripts are listed under the [Runtime libraries](#runtime-libraries) section."},{"id":"runtime-globals","title":"Runtime globals","role":"content","text":"While the sandbox prevents users from declaring globals, the execution context is pre-populated with several of these."},{"id":"the-_redis_-singleton","title":"The _redis_ singleton","role":"content","text":"The _redis_ singleton is an object instance that's accessible from all scripts.\nIt provides the API to interact with Redis from scripts.\nIts description follows [below](#redis_object)."},{"id":"the-keys-global-variable","title":"The _KEYS_ global variable","role":"content","text":"* Since version: 2.6.0\n* Available in scripts: yes\n* Available in functions: no\n\n**Important:**\nto ensure the correct execution of scripts, both in standalone and clustered deployments, all names of keys that a function accesses must be explicitly provided as input key arguments.\nThe script **should only** access keys whose names are given as input arguments.\nScripts **should never** access keys with programmatically-generated names or based on the contents of data structures stored in the database.\n\nThe _KEYS_ global variable is available only for [ephemeral scripts](https://redis.io/docs/latest/develop/programmability/eval-intro).\nIt is pre-populated with all key name input arguments."},{"id":"the-argv-global-variable","title":"The _ARGV_ global variable","role":"content","text":"* Since version: 2.6.0\n* Available in scripts: yes\n* Available in functions: no\n\nThe _ARGV_ global variable is available only in [ephemeral scripts](https://redis.io/docs/latest/develop/programmability/eval-intro).\nIt is pre-populated with all regular input arguments."},{"id":"redis_object","title":"_redis_ object","role":"content","text":"* Since version: 2.6.0\n* Available in scripts: yes\n* Available in functions: yes\n\nThe Redis Lua execution context always provides a singleton instance of an object named _redis_.\nThe _redis_ instance enables the script to interact with the Redis server that's running it.\nFollowing is the API provided by the _redis_ object instance."},{"id":"redis.call","title":"`redis.call(command [,arg...])`","role":"content","text":"* Since version: 2.6.0\n* Available in scripts: yes\n* Available in functions: yes\n\nThe `redis.call()` function calls a given Redis command and returns its reply.\nIts inputs are the command and arguments, and once called, it executes the command in Redis and returns the reply.\n\nFor example, we can call the [`ECHO`](https://redis.io/docs/latest/commands/echo) command from a script and return its reply like so:\n\n[code example]\n\nIf and when `redis.call()` triggers a runtime exception, the raw exception is raised back to the user as an error, automatically.\nTherefore, attempting to execute the following ephemeral script will fail and generate a runtime exception because [`ECHO`](https://redis.io/docs/latest/commands/echo) accepts exactly one argument:\n\n[code example]\n\nNote that the call can fail due to various reasons, see [Execution under low memory conditions](https://redis.io/docs/latest/develop/programmability/eval-intro#execution-under-low-memory-conditions) and [Script flags](#script_flags)\n\nTo handle Redis runtime errors use `redis.pcall()` instead."},{"id":"redis.pcall","title":"`redis.pcall(command [,arg...])`","role":"content","text":"* Since version: 2.6.0\n* Available in scripts: yes\n* Available in functions: yes\n\nThis function enables handling runtime errors raised by the Redis server.\nThe `redis.pcall()` function  behaves exactly like [`redis.call()`](#redis.call), except that it:\n\n* Always returns a reply.\n* Never throws a runtime exception, and returns in its stead a [`redis.error_reply`](#redis.error_reply) in case that a runtime exception is thrown by the server.\n\nThe following demonstrates how to use `redis.pcall()` to intercept and handle runtime exceptions from within the context of an ephemeral script.\n\n[code example]\n\nEvaluating this script with more than one argument will return:\n\n[code example]"},{"id":"redis.error_reply","title":"`redis.error_reply(x)`","role":"content","text":"* Since version: 2.6.0\n* Available in scripts: yes\n* Available in functions: yes\n\nThis is a helper function that returns an [error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors).\nThe helper accepts a single string argument and returns a Lua table with the _err_ field set to that string.\n\nThe outcome of the following code is that _error1_ and _error2_ are identical for all intents and purposes:\n\n[code example]\n\nTherefore, both forms are valid as means for returning an error reply from scripts:\n\n[code example]\n\nFor returning Redis status replies refer to [`redis.status_reply()`](#redis.status_reply).\nRefer to the [Data type conversion](#data-type-conversion) for returning other response types.\n\n**Note:**\nBy convention, Redis uses the first word of an error string as a unique error code for specific errors or `ERR` for general-purpose errors.\nScripts are advised to follow this convention, as shown in the example above, but this is not mandatory."},{"id":"redis.status_reply","title":"`redis.status_reply(x)`","role":"content","text":"* Since version: 2.6.0\n* Available in scripts: yes\n* Available in functions: yes\n\nThis is a helper function that returns a [simple string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings).\n\"OK\" is an example of a standard Redis status reply.\nThe Lua API represents status replies as tables with a single field, _ok_, set with a simple status string.\n\nThe outcome of the following code is that _status1_ and _status2_ are identical for all intents and purposes:\n\n[code example]\n\nTherefore, both forms are valid as means for returning status replies from scripts:\n\n[code example]\n\nFor returning Redis error replies refer to [`redis.error_reply()`](#redis.error_reply).\nRefer to the [Data type conversion](#data-type-conversion) for returning other response types."},{"id":"redis.sha1hex","title":"`redis.sha1hex(x)`","role":"content","text":"* Since version: 2.6.0\n* Available in scripts: yes\n* Available in functions: yes\n\nThis function returns the SHA1 hexadecimal digest of its single string argument.\n\nYou can, for example, obtain the empty string's SHA1 digest:\n\n[code example]"},{"id":"redis.log","title":"`redis.log(level, message)`","role":"content","text":"* Since version: 2.6.0\n* Available in scripts: yes\n* Available in functions: yes\n\nThis function writes to the Redis server log.\n\nIt expects two input arguments: the log level and a message.\nThe message is a string to write to the log file.\nLog level can be on of these:\n\n* `redis.LOG_DEBUG`\n* `redis.LOG_VERBOSE`\n* `redis.LOG_NOTICE`\n* `redis.LOG_WARNING`\n\nThese levels map to the server's log levels.\nThe log only records messages equal or greater in level than the server's `loglevel` configuration directive.\n\nThe following snippet:\n\n[code example]\n\nwill produce a line similar to the following in your server's log:\n\n[code example]"},{"id":"redis.setresp","title":"`redis.setresp(x)`","role":"content","text":"* Since version: 6.0.0\n* Available in scripts: yes\n* Available in functions: yes\n\nThis function allows the executing script to switch between [Redis Serialization Protocol (RESP)](https://redis.io/docs/latest/develop/reference/protocol-spec) versions for the replies returned by [`redis.call()`](#redis.call) and [`redis.pcall()`](#redis.pcall).\nIt expects a single numerical argument as the protocol's version.\nThe default protocol version is _2_, but it can be switched to version _3_.\n\nHere's an example of switching to RESP3 replies:\n\n[code example]\n\nPlease refer to the [Data type conversion](#data-type-conversion) for more information about type conversions."},{"id":"redis.set_repl","title":"`redis.set_repl(x)`","role":"content","text":"* Since version: 3.2.0\n* Available in scripts: yes\n* Available in functions: no\n\n**Note:**\nthis feature is only available when script effects replication is employed.\nCalling it when using verbatim script replication will result in an error.\nAs of Redis version 2.6.0, scripts were replicated verbatim, meaning that the scripts' source code was sent for execution by replicas and stored in the AOF.\nAn alternative replication mode added in version 3.2.0 allows replicating only the scripts' effects.\nAs of Redis version 7.0, script replication is no longer supported, and the only replication mode available is script effects replication.\n\n**Warning:**\nthis is an advanced feature. Misuse can cause damage by violating the contract that binds the Redis master, its replicas, and AOF contents to hold the same logical content.\n\nThis function allows a script to assert control over how its effects are propagated to replicas and the AOF afterward.\nA script's effects are the Redis write commands that it calls.\n\nBy default, all write commands that a script executes are replicated.\nSometimes, however, better control over this behavior can be helpful.\nThis can be the case, for example, when storing intermediate values in the master alone.\n\nConsider a script that intersects two sets and stores the result in a temporary key with [`SUNIONSTORE`](https://redis.io/docs/latest/commands/sunionstore).\nIt then picks five random elements ([`SRANDMEMBER`](https://redis.io/docs/latest/commands/srandmember)) from the intersection and stores ([`SADD`](https://redis.io/docs/latest/commands/sadd)) them in another set.\nFinally, before returning, it deletes the temporary key that stores the intersection of the two source sets.\n\nIn this case, only the new set with its five randomly-chosen elements needs to be replicated.\nReplicating the [`SUNIONSTORE`](https://redis.io/docs/latest/commands/sunionstore) command and the [`DEL`](https://redis.io/docs/latest/commands/del)ition of the temporary key is unnecessary and wasteful.\n\nThe `redis.set_repl()` function instructs the server how to treat subsequent write commands in terms of replication.\nIt accepts a single input argument that only be one of the following:\n\n* `redis.REPL_ALL`: replicates the effects to the AOF and replicas.\n* `redis.REPL_AOF`: replicates the effects to the AOF alone.\n* `redis.REPL_REPLICA`: replicates the effects to the replicas alone.\n* `redis.REPL_SLAVE`: same as `REPL_REPLICA`, maintained for backward compatibility.\n* `redis.REPL_NONE`: disables effect replication entirely.\n\nBy default, the scripting engine is initialized to the `redis.REPL_ALL` setting when a script begins its execution.\nYou can call the `redis.set_repl()` function at any time during the script's execution to switch between the different replication modes.\n\nA simple example follows:\n\n[code example]\n\nIf you run this script by calling `EVAL \"...\" 3 A B C 1 2 3`, the result will be that only the keys _A_ and _C_ are created on the replicas and AOF."},{"id":"redis.replicate_commands","title":"`redis.replicate_commands()`","role":"content","text":"* Since version: 3.2.0\n* Until version: 7.0.0\n* Available in scripts: yes\n* Available in functions: no\n\nThis function switches the script's replication mode from verbatim replication to effects replication.\nYou can use it to override the default verbatim script replication mode used by Redis until version 7.0.\n\n**Note:**\nas of Redis v7.0, verbatim script replication is no longer supported.\nThe default, and only script replication mode supported, is script effects' replication.\nFor more information, please refer to [`Replicating commands instead of scripts`](https://redis.io/docs/latest/develop/programmability/eval-intro#replicating-commands-instead-of-scripts)"},{"id":"redis.breakpoint","title":"`redis.breakpoint()`","role":"content","text":"* Since version: 3.2.0\n* Available in scripts: yes\n* Available in functions: no\n\nThis function triggers a breakpoint when using the [Redis Lua debugger](https://redis.io/docs/latest/develop/programmability/lua-debugging)."},{"id":"redis.debug","title":"`redis.debug(x)`","role":"content","text":"* Since version: 3.2.0\n* Available in scripts: yes\n* Available in functions: no\n\nThis function prints its argument in the [Redis Lua debugger](https://redis.io/docs/latest/develop/programmability/lua-debugging) console."},{"id":"redis.acl_check_cmd","title":"`redis.acl_check_cmd(command [,arg...])`","role":"content","text":"* Since version: 7.0.0\n* Available in scripts: yes\n* Available in functions: yes\n\nThis function is used for checking if the current user running the script has [ACL](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl) permissions to execute the given command with the given arguments.\n\nThe return value is a boolean `true` in case the current user has permissions to execute the command (via a call to [redis.call](#redis.call) or [redis.pcall](#redis.pcall)) or `false` in case they don't.\n\nThe function will raise an error if the passed command or its arguments are invalid."},{"id":"redis.register_function","title":"`redis.register_function`","role":"content","text":"* Since version: 7.0.0\n* Available in scripts: no\n* Available in functions: yes\n\nThis function is only available from the context of the [`FUNCTION LOAD`](https://redis.io/docs/latest/commands/function-load) command.\nWhen called, it registers a function to the loaded library.\nThe function can be called either with positional or named arguments.\n\n#### positional arguments: `redis.register_function(name, callback)` {#redis.register_function_pos_args}\nThe first argument to `redis.register_function` is a Lua string representing the function name.\nThe second argument to `redis.register_function` is a Lua function.\n\nUsage example:\n\n[code example]\n\n#### Named arguments:  `redis.register_function{function_name=name, callback=callback, flags={flag1, flag2, ..}, description=description}` {#redis.register_function_named_args}\n\nThe named arguments variant accepts the following arguments:\n\n* _function\\_name_: the function's name.\n* _callback_: the function's callback.\n* _flags_: an array of strings, each a function flag (optional).\n* _description_: function's description (optional).\n\nBoth _function\\_name_ and _callback_ are mandatory.\n\nUsage example:\n\n[code example]\n\n#### Script flags {#script_flags}\n\n**Important:**\nUse script flags with care, which may negatively impact if misused.\nNote that the default for Eval scripts are different than the default for functions that are mentioned below, see [Eval Flags](https://redis.io/docs/latest/develop/programmability/eval-intro#eval-flags)\n\nWhen you register a function or load an Eval script, the server does not know how it accesses the database.\nBy default, Redis assumes that all scripts read and write data.\nThis results in the following behavior:\n\n1. They can read and write data.\n1. They can run in cluster mode, and are not able to run commands accessing keys of different hash slots.\n1. Execution against a stale replica is denied to avoid inconsistent reads.\n1. Execution under low memory is denied to avoid exceeding the configured threshold.\n\nYou can use the following flags and instruct the server to treat the scripts' execution differently:\n\n* `no-writes`: this flag indicates that the script only reads data but never writes.\n\n    By default, Redis will deny the execution of flagged scripts (Functions and Eval scripts with [shebang](https://redis.io/docs/latest/develop/programmability/eval-intro#eval-flags)) against read-only replicas, as they may attempt to perform writes.\n    Similarly, the server will not allow calling scripts with [`FCALL_RO`](https://redis.io/docs/latest/commands/fcall_ro) / [`EVAL_RO`](https://redis.io/docs/latest/commands/eval_ro).\n    Lastly, when data persistence is at risk due to a disk error, execution is blocked as well.\n\n    Using this flag allows executing the script:\n    1. With [`FCALL_RO`](https://redis.io/docs/latest/commands/fcall_ro) / [`EVAL_RO`](https://redis.io/docs/latest/commands/eval_ro)\n    2. On read-only replicas.\n    3. Even if there's a disk error (Redis is unable to persist so it rejects writes).\n    4. When over the memory limit since it implies the script doesn't increase memory consumption (see `allow-oom` below)\n\n    However, note that the server will return an error if the script attempts to call a write command.\n    Also note that currently [`PUBLISH`](https://redis.io/docs/latest/commands/publish), [`SPUBLISH`](https://redis.io/docs/latest/commands/spublish) and [`PFCOUNT`](https://redis.io/docs/latest/commands/pfcount) are also considered write commands in scripts, because they could attempt to propagate commands to replicas and AOF file.\n\n    For more information please refer to [Read-only scripts](https://redis.io/docs/latest/develop/programmability#read-only_scripts)\n\n* `allow-oom`: use this flag to allow a script to execute when the server is out of memory (OOM).\n\n    Unless used, Redis will deny the execution of flagged scripts (Functions and Eval scripts with [shebang](https://redis.io/docs/latest/develop/programmability/eval-intro#eval-flags)) when in an OOM state.\n    Furthermore, when you use this flag, the script can call any Redis command, including commands that aren't usually allowed in this state.\n    Specifying `no-writes` or using [`FCALL_RO`](https://redis.io/docs/latest/commands/fcall_ro) / [`EVAL_RO`](https://redis.io/docs/latest/commands/eval_ro) also implies the script can run in OOM state (without specifying `allow-oom`)\n\n* `allow-stale`: a flag that enables running the flagged scripts (Functions and Eval scripts with [shebang](https://redis.io/docs/latest/develop/programmability/eval-intro#eval-flags)) against a stale replica when the `replica-serve-stale-data` config is set to `no` .\n\n    Redis can be set to prevent data consistency problems from using old data by having stale replicas return a runtime error.\n    For scripts that do not access the data, this flag can be set to allow stale Redis replicas to run the script.\n    Note however that the script will still be unable to execute any command that accesses stale data.\n\n* `no-cluster`: the flag causes the script to return an error in Redis cluster mode.\n\n    Redis allows scripts to be executed both in standalone and cluster modes.\n    Setting this flag prevents executing the script against nodes in the cluster.\n\n* `allow-cross-slot-keys`: The flag that allows a script to access keys from multiple slots.\n\n    Redis typically prevents any single command from accessing keys that hash to multiple slots.\n    This flag allows scripts to break this rule and access keys within the script that access multiple slots.\n    Declared keys to the script are still always required to hash to a single slot.\n    Accessing keys from multiple slots is discouraged as applications should be designed to only access keys from a single slot at a time, allowing slots to move between Redis servers.\n    \n    This flag has no effect when cluster mode is disabled.\n\nPlease refer to [Function Flags](https://redis.io/docs/latest/develop/programmability/functions-intro#function-flags) and [Eval Flags](https://redis.io/docs/latest/develop/programmability/eval-intro#eval-flags) for a detailed example."},{"id":"redis.redis_version","title":"`redis.REDIS_VERSION`","role":"content","text":"* Since version: 7.0.0\n* Available in scripts: yes\n* Available in functions: yes\n\nReturns the current Redis server version as a Lua string.\nThe reply's format is `MM.mm.PP`, where:\n\n* **MM:** is the major version.\n* **mm:** is the minor version.\n* **PP:** is the patch level."},{"id":"redis.redis_version_num","title":"`redis.REDIS_VERSION_NUM`","role":"content","text":"* Since version: 7.0.0\n* Available in scripts: yes\n* Available in functions: yes\n\nReturns the current Redis server version as a number.\nThe reply is a hexadecimal value structured as `0x00MMmmPP`, where:\n\n* **MM:** is the major version.\n* **mm:** is the minor version.\n* **PP:** is the patch level."},{"id":"data-type-conversion","title":"Data type conversion","role":"content","text":"Unless a runtime exception is raised, `redis.call()` and `redis.pcall()` return the reply from the executed command to the Lua script.\nRedis' replies from these functions are converted automatically into Lua's native data types.\n\nSimilarly, when a Lua script returns a reply with the `return` keyword,\nthat reply is automatically converted to Redis' protocol.\n\nPut differently; there's a one-to-one mapping between Redis' replies and Lua's data types and a one-to-one mapping between Lua's data types and the [Redis Protocol](https://redis.io/docs/latest/develop/reference/protocol-spec) data types.\nThe underlying design is such that if a Redis type is converted into a Lua type and converted back into a Redis type, the result is the same as the initial value.\n\nType conversion from Redis protocol replies (i.e., the replies from `redis.call()` and `redis.pcall()`) to Lua data types depends on the Redis Serialization Protocol version used by the script.\nThe default protocol version during script executions is RESP2.\nThe script may switch the replies' protocol versions by calling the `redis.setresp()` function.\n\nType conversion from a script's returned Lua data type depends on the user's choice of protocol (see the [`HELLO`](https://redis.io/docs/latest/commands/hello) command).\n\nThe following sections describe the type conversion rules between Lua and Redis per the protocol's version."},{"id":"resp2-to-lua-type-conversion","title":"RESP2 to Lua type conversion","role":"content","text":"The following type conversion rules apply to the execution's context by default as well as after calling `redis.setresp(2)`:\n\n* [RESP2 integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) -> Lua number\n* [RESP2 bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) -> Lua string\n* [RESP2 array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) -> Lua table (may have other Redis data types nested)\n* [RESP2 status reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings) -> Lua table with a single _ok_ field containing the status string\n* [RESP2 error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors) -> Lua table with a single _err_ field containing the error string\n* [RESP2 null bulk reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings) and [RESP2 null multi-bulk reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) -> Lua false boolean type"},{"id":"lua-to-resp2-type-conversion","title":"Lua to RESP2 type conversion","role":"content","text":"The following type conversion rules apply by default as well as after the user had called `HELLO 2`:\n\n* Lua number -> [RESP2 integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) (the number is converted into an integer)\n* Lua string -> [RESP2 bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings)\n* Lua table (indexed, non-associative array) -> [RESP2 array reply](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays) (truncated at the first Lua `nil` value encountered in the table, if any)\n* Lua table with a single _ok_ field -> [RESP2 status reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-strings)\n* Lua table with a single _err_ field -> [RESP2 error reply](https://redis.io/docs/latest/develop/reference/protocol-spec#simple-errors)\n* Lua boolean false -> [RESP2 null bulk reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-strings)\n\nThere is an additional Lua-to-Redis conversion rule that has no corresponding Redis-to-Lua conversion rule:\n\n* Lua Boolean `true` -> [RESP2 integer reply](https://redis.io/docs/latest/develop/reference/protocol-spec#integers) with value of 1.\n\nThere are three additional rules to note about converting Lua to Redis data types:\n\n* Lua has a single numerical type, Lua numbers. \n  There is no distinction between integers and floats.\n  So we always convert Lua numbers into integer replies, removing the decimal part of the number, if any.\n  **If you want to return a Lua float, it should be returned as a string**,\n  exactly like Redis itself does (see, for instance, the [`ZSCORE`](https://redis.io/docs/latest/commands/zscore) command).\n* There's [no simple way to have nils inside Lua arrays](http://www.lua.org/pil/19.1.html) due \n  to Lua's table semantics.\n  Therefore, when Redis converts a Lua array to RESP, the conversion stops when it encounters a Lua `nil` value.\n* When a Lua table is an associative array that contains keys and their respective values, the converted Redis reply will **not** include them.\n\nLua to RESP2 type conversion examples:\n\n[code example]\n\nThe last example demonstrates receiving and returning the exact return value of `redis.call()` (or `redis.pcall()`) in Lua as it would be returned if the command had been called directly.\n\nThe following example shows how floats and arrays that cont nils and keys are handled:\n\n[code example]\n\nAs you can see, the float value of _3.333_ gets converted to an integer _3_, the _somekey_ key and its value are omitted, and the string \"bar\" isn't returned as there is a `nil` value that precedes it."},{"id":"resp3-to-lua-type-conversion","title":"RESP3 to Lua type conversion","role":"content","text":"[RESP3](https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md) is a newer version of the [Redis Serialization Protocol](https://redis.io/docs/latest/develop/reference/protocol-spec).\nIt is available as an opt-in choice as of Redis v6.0.\n\nAn executing script may call the [`redis.setresp`](#redis.setresp) function during its execution and switch the protocol version that's used for returning replies from Redis' commands (that can be invoked via [`redis.call()`](#redis.call) or [`redis.pcall()`](#redis.pcall)).\n\nOnce Redis' replies are in RESP3 protocol, all of the [RESP2 to Lua conversion](#resp2-to-lua-type-conversion) rules apply, with the following additions:\n\n* [RESP3 map reply](https://redis.io/docs/latest/develop/reference/protocol-spec#maps) -> Lua table with a single _map_ field containing a Lua table representing the fields and values of the map.\n* [RESP set reply](https://redis.io/docs/latest/develop/reference/protocol-spec#sets) -> Lua table with a single _set_ field containing a Lua table representing the elements of the set as fields, each with the Lua Boolean value of `true`.\n* [RESP3 null](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls) -> Lua `nil`.\n* [RESP3 true reply](https://redis.io/docs/latest/develop/reference/protocol-spec#booleans) -> Lua true boolean value.\n* [RESP3 false reply](https://redis.io/docs/latest/develop/reference/protocol-spec#booleans) -> Lua false boolean value.\n* [RESP3 double reply](https://redis.io/docs/latest/develop/reference/protocol-spec#doubles) -> Lua table with a single _double_ field containing a Lua number representing the double value.\n* [RESP3 big number reply](https://redis.io/docs/latest/develop/reference/protocol-spec#big-numbers) -> Lua table with a single _big_number_ field containing a Lua string representing the big number value.\n* [Redis verbatim string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#verbatim-strings) -> Lua table with a single _verbatim_string_ field containing a Lua table with two fields, _string_ and _format_, representing the verbatim string and its format, respectively.\n\n**Note:**\nthe RESP3 [big number](https://redis.io/docs/latest/develop/reference/protocol-spec#big-numbers) and [verbatim strings](https://redis.io/docs/latest/develop/reference/protocol-spec#verbatim-strings) replies are only supported as of Redis v7.0 and greater. \nAlso, presently, RESP3's [attributes](https://redis.io/docs/latest/develop/reference/protocol-spec#attributes), [streamed strings](https://redis.io/docs/latest/develop/reference/protocol-spec#streamed-strings) and [streamed aggregated data types](https://redis.io/docs/latest/develop/reference/protocol-spec#streamed-aggregated-data-types) are not supported by the Redis Lua API."},{"id":"lua-to-resp3-type-conversion","title":"Lua to RESP3 type conversion","role":"content","text":"Regardless of the script's choice of protocol version set for replies with the [`redis.setresp()` function] when it calls `redis.call()` or `redis.pcall()`, the user may opt-in to using RESP3 (with the `HELLO 3` command) for the connection.\nAlthough the default protocol for incoming client connections is RESP2, the script should honor the user's preference and return adequately-typed RESP3 replies, so the following rules apply on top of those specified in the [Lua to RESP2 type conversion](#lua-to-resp2-type-conversion) section when that is the case.\n\n* Lua Boolean -> [RESP3 Boolean reply](https://redis.io/docs/latest/develop/reference/protocol-spec#booleans) (note that this is a change compared to the RESP2, in which returning a Boolean Lua `true` returned the number 1 to the Redis client, and returning a `false` used to return a `null`.\n* Lua table with a single _map_ field set to an associative Lua table -> [RESP3 map reply](https://redis.io/docs/latest/develop/reference/protocol-spec#maps).\n* Lua table with a single _set_ field set to an associative Lua table -> [RESP3 set reply](https://redis.io/docs/latest/develop/reference/protocol-spec#sets). Values can be set to anything and are discarded anyway.\n* Lua table with a single _double_ field to an associative Lua table -> [RESP3 double reply](https://redis.io/docs/latest/develop/reference/protocol-spec#doubles).\n* Lua nil -> [RESP3 null](https://redis.io/docs/latest/develop/reference/protocol-spec#nulls).\n\nHowever, if the connection is set use the RESP2 protocol, and even if the script replies with RESP3-typed responses, Redis will automatically perform a RESP3 to RESP2 conversion of the reply as is the case for regular commands.\nThat means, for example, that returning the RESP3 map type to a RESP2 connection will result in the reply being converted to a flat RESP2 array that consists of alternating field names and their values, rather than a RESP3 map."},{"id":"additional-notes-about-scripting","title":"Additional notes about scripting","role":"content","text":""},{"id":"using-select-inside-scripts","title":"Using `SELECT` inside scripts","role":"content","text":"You can call the [`SELECT`](https://redis.io/docs/latest/commands/select) command from your Lua scripts, like you can with any normal client connection.\nHowever, one subtle aspect of the behavior changed between Redis versions 2.8.11 and 2.8.12.\nPrior to Redis version 2.8.12, the database selected by the Lua script was *set as the current database* for the client connection that had called it.\nAs of Redis version 2.8.12, the database selected by the Lua script only affects the execution context of the script, and does not modify the database that's selected by the client calling the script.\nThis semantic change between patch level releases was required since the old behavior was inherently incompatible with Redis' replication and introduced bugs."},{"id":"runtime-libraries","title":"Runtime libraries","role":"content","text":"The Redis Lua runtime context always comes with several pre-imported libraries.\n\nThe following [standard Lua libraries](https://www.lua.org/manual/5.1/manual.html#5) are available to use:\n\n* The [_String Manipulation (string)_ library](https://www.lua.org/manual/5.1/manual.html#5.4)\n* The [_Table Manipulation (table)_ library](https://www.lua.org/manual/5.1/manual.html#5.5)\n* The [_Mathematical Functions (math)_ library](https://www.lua.org/manual/5.1/manual.html#5.6)\n* The [_Operating System Facilities (os)_ library](#os-library)\n\nIn addition, the following external libraries are loaded and accessible to scripts:\n\n* The [_struct_ library](#struct-library)\n* The [_cjson_ library](#cjson-library)\n* The [_cmsgpack_ library](#cmsgpack-library)\n* The [_bitop_ library](#bitop-library)"},{"id":"os-library","title":"_os_ library","role":"content","text":"* Since version: 7.4\n* Available in scripts: yes\n* Available in functions: yes\n\n_os_ provides a set of functions for dealing with date, time, and system commands.\nMore details can be found in the [Operating System Facilities](https://www.lua.org/manual/5.1/manual.html#5.8).\nNote that for sandbox security, currently only the following os functions is exposed:\n\n* `os.clock()`"},{"id":"struct-library","title":"_struct_ library","role":"content","text":"* Since version: 2.6.0\n* Available in scripts: yes\n* Available in functions: yes\n\n_struct_ is a library for packing and unpacking C-like structures in Lua.\nIt provides the following functions:\n\n* [`struct.pack()`](#struct.pack)\n* [`struct.unpack()`](#struct.unpack)\n* [`struct.size()`](#struct.size)\n\nAll of _struct_'s functions expect their first argument to be a [format string](#struct-formats).\n\n#### _struct_ formats {#struct-formats}\n\nThe following are valid format strings for _struct_'s functions:\n\n* `>`: big endian\n* `<`: little endian\n* `![num]`: alignment\n* `x`: padding\n* `b/B`: signed/unsigned byte\n* `h/H`: signed/unsigned short\n* `l/L`: signed/unsigned long\n* `T`: size_t\n* `i/In`: signed/unsigned integer with size _n_ (defaults to the size of int)\n* `cn`: sequence of _n_ chars (from/to a string); when packing, n == 0 means the\n  whole string; when unpacking, n == 0 means use the previously read number as\n  the string's length.\n* `s`: zero-terminated string\n* `f`: float\n* `d`: double\n* ` ` (space): ignored\n\n#### `struct.pack(x)` {#struct.pack}\n\nThis function returns a struct-encoded string from values.\nIt accepts a [_struct_ format string](#struct-formats) as its first argument, followed by the values that are to be encoded.\n\nUsage example:\n\n[code example]\n\n#### `struct.unpack(x)` {#struct.unpack}\n\nThis function returns the decoded values from a struct.\nIt accepts a [_struct_ format string](#struct-formats) as its first argument, followed by encoded struct's string.\n\nUsage example:\n\n[code example]\n\n#### `struct.size(x)` {#struct.size}\n\nThis function returns the size, in bytes, of a struct.\nIt accepts a [_struct_ format string](#struct-formats) as its only argument.\n\nUsage example:\n\n[code example]"},{"id":"cjson-library","title":"_cjson_ library","role":"content","text":"* Since version: 2.6.0\n* Available in scripts: yes\n* Available in functions: yes\n\nThe _cjson_ library provides fast [JSON](https://json.org) encoding and decoding from Lua.\nIt provides these functions.\n\n#### `cjson.encode(x)` {#cjson.encode}\n\nThis function returns a JSON-encoded string for the Lua data type provided as its argument.\n\nUsage example:\n\n[code example]\n\n#### `cjson.decode(x)` {#cjson.decode()}\n\nThis function returns a Lua data type from the JSON-encoded string provided as its argument.\n\nUsage example:\n\n[code example]"},{"id":"cmsgpack-library","title":"_cmsgpack_ library","role":"content","text":"* Since version: 2.6.0\n* Available in scripts: yes\n* Available in functions: yes\n\nThe _cmsgpack_ library provides fast [MessagePack](https://msgpack.org/index.html) encoding and decoding from Lua.\nIt provides these functions.\n\n#### `cmsgpack.pack(x)` {#cmsgpack.pack()}\n\nThis function returns the packed string encoding of the Lua data type it is given as an argument.\n\nUsage example:\n\n[code example]\n\n#### `cmsgpack.unpack(x)` {#cmsgpack.unpack()}\n\nThis function returns the unpacked values from decoding its input string argument.\n\nUsage example:\n\n[code example]"},{"id":"bitop-library","title":"_bit_ library","role":"content","text":"* Since version: 2.8.18\n* Available in scripts: yes\n* Available in functions: yes\n\nThe _bit_ library provides bitwise operations on numbers.\nIts documentation resides at [Lua BitOp documentation](http://bitop.luajit.org/api.html)\nIt provides the following functions.\n\n#### `bit.tobit(x)` {#bit.tobit()}\n\nNormalizes a number to the numeric range for bit operations and returns it.\n\nUsage example:\n\n[code example]\n\n#### `bit.tohex(x [,n])` {#bit.tohex()}\n\nConverts its first argument to a hex string. The number of hex digits is given by the absolute value of the optional second argument.\n\nUsage example:\n\n[code example]\n\n#### `bit.bnot(x)` {#bit.bnot()}\n\nReturns the bitwise **not** of its argument.\n\n#### `bit.bnot(x)` `bit.bor(x1 [,x2...])`, `bit.band(x1 [,x2...])` and `bit.bxor(x1 [,x2...])` {#bit.ops}\n\nReturns either the bitwise **or**, bitwise **and**, or bitwise **xor** of all of its arguments.\nNote that more than two arguments are allowed.\n\nUsage example:\n\n[code example]\n\n#### `bit.lshift(x, n)`, `bit.rshift(x, n)` and `bit.arshift(x, n)` {#bit.shifts}\n\nReturns either the bitwise logical **left-shift**, bitwise logical **right-shift**, or bitwise **arithmetic right-shift** of its first argument by the number of bits given by the second argument.\n\n#### `bit.rol(x, n)` and `bit.ror(x, n)` {#bit.ro}\n\nReturns either the bitwise **left rotation**, or bitwise **right rotation** of its first argument by the number of bits given by the second argument.\nBits shifted out on one side are shifted back in on the other side.\n\n#### `bit.bswap(x)` {#bit.bswap()}\n\nSwaps the bytes of its argument and returns it.\nThis can be used to convert little-endian 32-bit numbers to big-endian 32-bit numbers and vice versa."}],"examples":[{"id":"global-variables-and-functions-ex0","language":"lua","code":"my_global_variable = 'some value'","section_id":"global-variables-and-functions"},{"id":"global-variables-and-functions-ex1","language":"lua","code":"function my_global_function()\n  -- Do something amazing\nend","section_id":"global-variables-and-functions"},{"id":"global-variables-and-functions-ex2","language":"lua","code":"-- The following will surely raise an error\nreturn an_undefined_global_variable","section_id":"global-variables-and-functions"},{"id":"global-variables-and-functions-ex3","language":"lua","code":"local my_local_variable = 'some value'\n\nlocal function my_local_function()\n  -- Do something else, but equally amazing\nend","section_id":"global-variables-and-functions"},{"id":"redis.call-ex0","language":"lua","code":"return redis.call('ECHO', 'Echo, echo... eco... o...')","section_id":"redis.call"},{"id":"redis.call-ex1","language":"lua","code":"redis> EVAL \"return redis.call('ECHO', 'Echo,', 'echo... ', 'eco... ', 'o...')\" 0\n(error) ERR Wrong number of args calling Redis command from script script: b0345693f4b77517a711221050e76d24ae60b7f7, on @user_script:1.","section_id":"redis.call"},{"id":"redis.pcall-ex0","language":"lua","code":"local reply = redis.pcall('ECHO', unpack(ARGV))\nif reply['err'] ~= nil then\n  -- Handle the error sometime, but for now just log it\n  redis.log(redis.LOG_WARNING, reply['err'])\n  reply['err'] = 'ERR Something is wrong, but no worries, everything is under control'\nend\nreturn reply","section_id":"redis.pcall"},{"id":"redis.pcall-ex1","language":"plaintext","code":"redis> EVAL \"...\" 0 hello world\n(error) ERR Something is wrong, but no worries, everything is under control","section_id":"redis.pcall"},{"id":"redis.error_reply-ex0","language":"lua","code":"local text = 'ERR My very special error'\nlocal reply1 = { err = text }\nlocal reply2 = redis.error_reply(text)","section_id":"redis.error_reply"},{"id":"redis.error_reply-ex1","language":"plaintext","code":"redis> EVAL \"return { err = 'ERR My very special table error' }\" 0\n(error) ERR My very special table error\nredis> EVAL \"return redis.error_reply('ERR My very special reply error')\" 0\n(error) ERR My very special reply error","section_id":"redis.error_reply"},{"id":"redis.status_reply-ex0","language":"lua","code":"local text = 'Frosty'\nlocal status1 = { ok = text }\nlocal status2 = redis.status_reply(text)","section_id":"redis.status_reply"},{"id":"redis.status_reply-ex1","language":"plaintext","code":"redis> EVAL \"return { ok = 'TICK' }\" 0\nTICK\nredis> EVAL \"return redis.status_reply('TOCK')\" 0\nTOCK","section_id":"redis.status_reply"},{"id":"redis.sha1hex-ex0","language":"plaintext","code":"redis> EVAL \"return redis.sha1hex('')\" 0\n\"da39a3ee5e6b4b0d3255bfef95601890afd80709\"","section_id":"redis.sha1hex"},{"id":"redis.log-ex0","language":"lua","code":"redis.log(redis.LOG_WARNING, 'Something is terribly wrong')","section_id":"redis.log"},{"id":"redis.log-ex1","language":"plaintext","code":"[32343] 22 Mar 15:21:39 # Something is terribly wrong","section_id":"redis.log"},{"id":"redis.setresp-ex0","language":"lua","code":"redis.setresp(3)","section_id":"redis.setresp"},{"id":"redis.set_repl-ex0","language":"lua","code":"redis.replicate_commands() -- Enable effects replication in versions lower than Redis v7.0\nredis.call('SET', KEYS[1], ARGV[1])\nredis.set_repl(redis.REPL_NONE)\nredis.call('SET', KEYS[2], ARGV[2])\nredis.set_repl(redis.REPL_ALL)\nredis.call('SET', KEYS[3], ARGV[3])","section_id":"redis.set_repl"},{"id":"redis.register_function-ex0","language":"plaintext","code":"redis> FUNCTION LOAD \"#!lua name=mylib\\n redis.register_function('noop', function() end)\"","section_id":"redis.register_function"},{"id":"redis.register_function-ex1","language":"plaintext","code":"redis> FUNCTION LOAD \"#!lua name=mylib\\n redis.register_function{function_name='noop', callback=function() end, flags={ 'no-writes' }, description='Does nothing'}\"","section_id":"redis.register_function"},{"id":"lua-to-resp2-type-conversion-ex0","language":"plaintext","code":"redis> EVAL \"return 10\" 0\n(integer) 10\n\nredis> EVAL \"return { 1, 2, { 3, 'Hello World!' } }\" 0\n1) (integer) 1\n2) (integer) 2\n3) 1) (integer) 3\n   1) \"Hello World!\"\n\nredis> EVAL \"return redis.call('get','foo')\" 0\n\"bar\"","section_id":"lua-to-resp2-type-conversion"},{"id":"lua-to-resp2-type-conversion-ex1","language":"plaintext","code":"redis> EVAL \"return { 1, 2, 3.3333, somekey = 'somevalue', 'foo', nil , 'bar' }\" 0\n1) (integer) 1\n2) (integer) 2\n3) (integer) 3\n4) \"foo\"","section_id":"lua-to-resp2-type-conversion"},{"id":"struct-library-ex0","language":"plaintext","code":"redis> EVAL \"return struct.pack('HH', 1, 2)\" 0\n\"\\x01\\x00\\x02\\x00\"","section_id":"struct-library"},{"id":"struct-library-ex1","language":"plaintext","code":"redis> EVAL \"return { struct.unpack('HH', ARGV[1]) }\" 0 \"\\x01\\x00\\x02\\x00\"\n1) (integer) 1\n2) (integer) 2\n3) (integer) 5","section_id":"struct-library"},{"id":"struct-library-ex2","language":"plaintext","code":"redis> EVAL \"return struct.size('HH')\" 0\n(integer) 4","section_id":"struct-library"},{"id":"cjson-library-ex0","language":"plaintext","code":"redis> EVAL \"return cjson.encode({ ['foo'] = 'bar' })\" 0\n\"{\\\"foo\\\":\\\"bar\\\"}\"","section_id":"cjson-library"},{"id":"cjson-library-ex1","language":"plaintext","code":"redis> EVAL \"return cjson.decode(ARGV[1])['foo']\" 0 '{\"foo\":\"bar\"}'\n\"bar\"","section_id":"cjson-library"},{"id":"cmsgpack-library-ex0","language":"plaintext","code":"redis> EVAL \"return cmsgpack.pack({'foo', 'bar', 'baz'})\" 0\n\"\\x93\\xa3foo\\xa3bar\\xa3baz\"","section_id":"cmsgpack-library"},{"id":"cmsgpack-library-ex1","language":"plaintext","code":"redis> EVAL \"return cmsgpack.unpack(ARGV[1])\" 0 \"\\x93\\xa3foo\\xa3bar\\xa3baz\"\n1) \"foo\"\n2) \"bar\"\n3) \"baz\"","section_id":"cmsgpack-library"},{"id":"bitop-library-ex0","language":"plaintext","code":"redis> EVAL 'return bit.tobit(1)' 0\n(integer) 1","section_id":"bitop-library"},{"id":"bitop-library-ex1","language":"plaintext","code":"redis> EVAL 'return bit.tohex(422342)' 0\n\"000671c6\"","section_id":"bitop-library"},{"id":"bitop-library-ex2","language":"plaintext","code":"redis> EVAL 'return bit.bor(1,2,4,8,16,32,64,128)' 0\n(integer) 255","section_id":"bitop-library"}]}
{"schema_version":2,"id":"develop/programmability/lua-debugging","title":"Debugging Lua scripts in Redis","url":"https://redis.io/docs/latest/develop/programmability/lua-debugging/","summary":"How to use the built-in Lua debugger","aliases":["/develop/interact/programmability/lua-debugging"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T14:32:43+01:00","page_type":"content","content_hash":"2cd4188b6fca0fe4e808f27161438d11b08c7edf0d2cf8cb0a16fa22bf34a7df","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Starting with version 3.2 Redis includes a complete Lua debugger, that can be\nused in order to make the task of writing complex Redis scripts much simpler.\n\nThe Redis Lua debugger, codenamed LDB, has the following important features:\n\n* It uses a server-client model, so it's a remote debugger.\nThe Redis server acts as the debugging server, while the default client is `redis-cli`. \nHowever other clients can be developed by following the simple protocol implemented by the server.\n* By default every new debugging session is a forked session.\nIt means that while the Redis Lua script is being debugged, the server does not block and is usable for development or in order to execute multiple debugging sessions in parallel.\nThis also means that changes are **rolled back** after the script debugging session finished, so that's possible to restart a new debugging session again, using exactly the same Redis data set as the previous debugging session.\n* An alternative synchronous (non forked) debugging model is available on demand, so that changes to the dataset can be retained.\nIn this mode the server blocks for the time the debugging session is active.\n* Support for step by step execution.\n* Support for static and dynamic breakpoints.\n* Support from logging the debugged script into the debugger console.\n* Inspection of Lua variables.\n* Tracing of Redis commands executed by the script.\n* Pretty printing of Redis and Lua values.\n* Infinite loops and long execution detection, which simulates a breakpoint."},{"id":"quick-start","title":"Quick start","role":"content","text":"A simple way to get started with the Lua debugger is to watch this video\nintroduction:\n\n<iframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/IMvRfStaoyM\" frameborder=\"0\" allowfullscreen></iframe>\n\n> Important Note:  please make sure to avoid debugging Lua scripts using your Redis production server.\nUse a development server instead.\nAlso note that using the synchronous debugging mode (which is NOT the default) results in the Redis server blocking for all the time the debugging session lasts.\n\nTo start a new debugging session using `redis-cli` do the following:\n\n1. Create your script in some file with your preferred editor. Let's assume you are editing your Redis Lua script located at `/tmp/script.lua`.\n2. Start a debugging session with:\n\n    ./redis-cli --ldb --eval /tmp/script.lua\n\nNote that with the `--eval` option of `redis-cli` you can pass key names and arguments to the script, separated by a comma, like in the following example:\n\n[code example]\n\nYou'll enter a special mode where `redis-cli` no longer accepts its normal\ncommands, but instead prints a help screen and passes the unmodified debugging\ncommands directly to Redis.\n\nThe only commands which are not passed to the Redis debugger are:\n\n* `quit` -- this will terminate the debugging session.\nIt's like removing all the breakpoints and using the `continue` debugging command.\nMoreover the command will exit from `redis-cli`.\n* `restart` -- the debugging session will restart from scratch, **reloading the new version of the script from the file**.\nSo a normal debugging cycle involves modifying the script after some debugging, and calling `restart` in order to start debugging again with the new script changes.\n* `help` -- this command is passed to the Redis Lua debugger, that will print a list of commands like the following:\n\n[code example]\n\nNote that when you start the debugger it will start in **stepping mode**.\nIt will stop at the first line of the script that actually does something before executing it.\n\nFrom this point you usually call `step` in order to execute the line and go to the next line.\nWhile you step Redis will show all the commands executed by the server like in the following example:\n\n[code example]\n\nThe `<redis>` and `<reply>` lines show the command executed by the line just\nexecuted, and the reply from the server. Note that this happens only in stepping mode.\nIf you use `continue` in order to execute the script till the next breakpoint, commands will not be dumped on the screen to prevent too much output."},{"id":"termination-of-the-debugging-session","title":"Termination of the debugging session","role":"content","text":"When the scripts terminates naturally, the debugging session ends and\n`redis-cli` returns in its normal non-debugging mode. You can restart the\nsession using the `restart` command as usual.\n\nAnother way to stop a debugging session is just interrupting `redis-cli`\nmanually by pressing `Ctrl+C`. Note that also any event breaking the\nconnection between `redis-cli` and the `redis-server` will interrupt the\ndebugging session.\n\nAll the forked debugging sessions are terminated when the server is shut\ndown."},{"id":"abbreviating-debugging-commands","title":"Abbreviating debugging commands","role":"content","text":"Debugging can be a very repetitive task. For this reason every Redis\ndebugger command starts with a different character, and you can use the single\ninitial character in order to refer to the command.\n\nSo for example instead of typing `step` you can just type `s`."},{"id":"breakpoints","title":"Breakpoints","role":"content","text":"Adding and removing breakpoints is trivial as described in the online help.\nJust use `b 1 2 3 4` to add a breakpoint in line 1, 2, 3, 4.\nThe command `b 0` removes all the breakpoints. Selected breakpoints can be\nremoved using as argument the line where the breakpoint we want to remove is, but prefixed by a minus sign. \nSo for example `b -3` removes the breakpoint from line 3.\n\nNote that adding breakpoints to lines that Lua never executes, like declaration of local variables or comments, will not work.\nThe breakpoint will be added but since this part of the script will never be executed, the program will never stop."},{"id":"dynamic-breakpoints","title":"Dynamic breakpoints","role":"content","text":"Using the `breakpoint` command it is possible to add breakpoints into specific\nlines. However sometimes we want to stop the execution of the program only\nwhen something special happens. In order to do so, you can use the\n`redis.breakpoint()` function inside your Lua script. When called it simulates\na breakpoint in the next line that will be executed.\n\n[code example]\nThis feature is extremely useful when debugging, so that we can avoid\ncontinuing the script execution manually multiple times until a given condition\nis encountered."},{"id":"synchronous-mode","title":"Synchronous mode","role":"content","text":"As explained previously, but default LDB uses forked sessions with rollback\nof all the data changes operated by the script while it has being debugged.\nDeterminism is usually a good thing to have during debugging, so that successive\ndebugging sessions can be started without having to reset the database content\nto its original state.\n\nHowever for tracking certain bugs, you may want to retain the changes performed\nto the key space by each debugging session. When this is a good idea you\nshould start the debugger using a special option, `ldb-sync-mode`, in `redis-cli`.\n\n[code example]\n\n> Note: Redis server will be unreachable during the debugging session in this mode, so use with care.\n\nIn this special mode, the `abort` command can stop the script half-way taking the changes operated to the dataset.\nNote that this is different compared to ending the debugging session normally. \nIf you just interrupt `redis-cli` the script will be fully executed and then the session terminated.\nInstead with `abort` you can interrupt the script execution in the middle and start a new debugging session if needed."},{"id":"logging-from-scripts","title":"Logging from scripts","role":"content","text":"The `redis.debug()` command is a powerful debugging facility that can be\ncalled inside the Redis Lua script in order to log things into the debug\nconsole:\n\n[code example]\n\nIf the script is executed outside of a debugging session, `redis.debug()` has no effects at all.\nNote that the function accepts multiple arguments, that are separated by a comma and a space in the output.\n\nTables and nested tables are displayed correctly in order to make values simple to observe for the programmer debugging the script."},{"id":"inspecting-the-program-state-with-print-and-eval","title":"Inspecting the program state with `print` and `eval`","role":"content","text":"While the `redis.debug()` function can be used in order to print values\ndirectly from within the Lua script, often it is useful to observe the local\nvariables of a program while stepping or when stopped into a breakpoint.\n\nThe `print` command does just that, and performs lookup in the call frames\nstarting from the current one back to the previous ones, up to top-level.\nThis means that even if we are into a nested function inside a Lua script,\nwe can still use `print foo` to look at the value of `foo` in the context\nof the calling function. When called without a variable name, `print` will\nprint all variables and their respective values.\n\nThe `eval` command executes small pieces of Lua scripts **outside the context of the current call frame** (evaluating inside the context of the current call frame is not possible with the current Lua internals).\nHowever you can use this command in order to test Lua functions.\n\n[code example]"},{"id":"debugging-clients","title":"Debugging clients","role":"content","text":"LDB uses the client-server model where the Redis server acts as a debugging server that communicates using [RESP](https://redis.io/docs/latest/develop/reference/protocol-spec). While `redis-cli` is the default debug client, any client can be used for debugging as long as it meets one of the following conditions:\n\n1. The client provides a native interface for setting the debug mode and controlling the debug session.\n2. The client provides an interface for sending arbitrary commands over RESP.\n3. The client allows sending raw messages to the Redis server.\n\nFor example, the [Redis plugin](https://redis.com/blog/zerobrane-studio-plugin-for-redis-lua-scripts) for [ZeroBrane Studio](http://studio.zerobrane.com/) integrates with LDB using [redis-lua](https://github.com/nrk/redis-lua). The following Lua code is a simplified example of how the plugin achieves that:\n\n[code example]"}],"examples":[{"id":"quick-start-ex0","language":"plaintext","code":"./redis-cli --ldb --eval /tmp/script.lua mykey somekey , arg1 arg2","section_id":"quick-start"},{"id":"quick-start-ex1","language":"plaintext","code":"lua debugger> help\nRedis Lua debugger help:\n[h]elp               Show this help.\n[s]tep               Run current line and stop again.\n[n]ext               Alias for step.\n[c]ontinue           Run till next breakpoint.\n[l]ist               List source code around current line.\n[l]ist [line]        List source code around [line].\n                     line = 0 means: current position.\n[l]ist [line] [ctx]  In this form [ctx] specifies how many lines\n                     to show before/after [line].\n[w]hole              List all source code. Alias for 'list 1 1000000'.\n[p]rint              Show all the local variables.\n[p]rint <var>        Show the value of the specified variable.\n                     Can also show global vars KEYS and ARGV.\n[b]reak              Show all breakpoints.\n[b]reak <line>       Add a breakpoint to the specified line.\n[b]reak -<line>      Remove breakpoint from the specified line.\n[b]reak 0            Remove all breakpoints.\n[t]race              Show a backtrace.\n[e]val <code>        Execute some Lua code (in a different callframe).\n[r]edis <cmd>        Execute a Redis command.\n[m]axlen [len]       Trim logged Redis replies and Lua var dumps to len.\n                     Specifying zero as <len> means unlimited.\n[a]bort              Stop the execution of the script. In sync\n                     mode dataset changes will be retained.\n\nDebugger functions you can call from Lua scripts:\nredis.debug()        Produce logs in the debugger console.\nredis.breakpoint()   Stop execution as if there was a breakpoint in the\n                     next line of code.","section_id":"quick-start"},{"id":"quick-start-ex2","language":"plaintext","code":"* Stopped at 1, stop reason = step over\n-> 1   redis.call('ping')\nlua debugger> step\n<redis> ping\n<reply> \"+PONG\"\n* Stopped at 2, stop reason = step over","section_id":"quick-start"},{"id":"dynamic-breakpoints-ex0","language":"plaintext","code":"if counter > 10 then redis.breakpoint() end","section_id":"dynamic-breakpoints"},{"id":"synchronous-mode-ex0","language":"plaintext","code":"./redis-cli --ldb-sync-mode --eval /tmp/script.lua","section_id":"synchronous-mode"},{"id":"logging-from-scripts-ex0","language":"plaintext","code":"lua debugger> list\n-> 1   local a = {1,2,3}\n   2   local b = false\n   3   redis.debug(a,b)\nlua debugger> continue\n<debug> line 3: {1; 2; 3}, false","section_id":"logging-from-scripts"},{"id":"inspecting-the-program-state-with-print-and-eval-ex0","language":"plaintext","code":"lua debugger> e redis.sha1hex('foo')\n<retval> \"0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33\"","section_id":"inspecting-the-program-state-with-print-and-eval"},{"id":"debugging-clients-ex0","language":"Lua","code":"local redis = require 'redis'\n\n-- add LDB's Continue command\nredis.commands['ldbcontinue'] = redis.command('C')\n\n-- script to be debugged\nlocal script = [[\n  local x, y = tonumber(ARGV[1]), tonumber(ARGV[2])\n  local result = x * y\n  return result\n]]\n\nlocal client = redis.connect('127.0.0.1', 6379)\nclient:script(\"DEBUG\", \"YES\")\nprint(unpack(client:eval(script, 0, 6, 9)))\nclient:ldbcontinue()","section_id":"debugging-clients"}]}
{"schema_version":2,"id":"develop/pubsub","title":"Redis Pub/sub","url":"https://redis.io/docs/latest/develop/pubsub/","summary":"How to use pub/sub channels in Redis","aliases":["/develop/interact/pubsub","/develop/interact/pubsub/","/interact/pubsub/","/manual/pubsub/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-30T15:53:52-07:00","children":[{"id":"develop/pubsub/keyspace-notifications","summary":"Monitor changes to Redis keys and values in real time","title":"Redis keyspace notifications","url":"https://redis.io/docs/latest/develop/pubsub/keyspace-notifications/"},{"id":"develop/pubsub/subkeyspace-notifications","summary":"Monitor changes to individual subkeys in real time","title":"Redis subkey notifications","url":"https://redis.io/docs/latest/develop/pubsub/subkeyspace-notifications/"}],"page_type":"content","content_hash":"bb64592fd8b5052af3813699bf161b609d5b08ba3586ac7f2418308be0a4fa2e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[`SUBSCRIBE`](https://redis.io/docs/latest/commands/subscribe), [`UNSUBSCRIBE`](https://redis.io/docs/latest/commands/unsubscribe) and [`PUBLISH`](https://redis.io/docs/latest/commands/publish) implement the [Publish/Subscribe messaging paradigm](http://en.wikipedia.org/wiki/Publish/subscribe) where (citing Wikipedia) senders (publishers) are not programmed to send their messages to specific receivers (subscribers).\nRather, published messages are categorized into channels, without knowledge of what (if any) subscribers there may be.\nSubscribers express interest in one or more channels and only receive messages that are of interest, without knowledge of what (if any) publishers there are.\nThis decoupling of publishers and subscribers allows for greater scalability and a more dynamic network topology.\n\nFor instance, to subscribe to channels \"channel11\" and \"ch:00\" the client issues a [`SUBSCRIBE`](https://redis.io/docs/latest/commands/subscribe) providing the names of the channels:\n\n[code example]\n\nMessages sent by other clients to these channels will be pushed by Redis to all the subscribed clients.\nSubscribers receive the messages in the order that the messages are published.\n\nA client subscribed to one or more channels shouldn't issue commands, although it can [`SUBSCRIBE`](https://redis.io/docs/latest/commands/subscribe) and [`UNSUBSCRIBE`](https://redis.io/docs/latest/commands/unsubscribe) to and from other channels.\nThe replies to subscription and unsubscribing operations are sent in the form of messages so that the client can just read a coherent stream of messages where the first element indicates the type of message.\nThe commands that are allowed in the context of a subscribed RESP2 client are:\n\n* [`PING`](https://redis.io/docs/latest/commands/ping)\n* [`PSUBSCRIBE`](https://redis.io/docs/latest/commands/psubscribe)\n* [`PUNSUBSCRIBE`](https://redis.io/docs/latest/commands/punsubscribe)\n* [`QUIT`](https://redis.io/docs/latest/commands/quit)\n* [`RESET`](https://redis.io/docs/latest/commands/reset)\n* [`SSUBSCRIBE`](https://redis.io/docs/latest/commands/ssubscribe)\n* [`SUBSCRIBE`](https://redis.io/docs/latest/commands/subscribe)\n* [`SUNSUBSCRIBE`](https://redis.io/docs/latest/commands/sunsubscribe)\n* [`UNSUBSCRIBE`](https://redis.io/docs/latest/commands/unsubscribe)\n\nHowever, if RESP3 is used (see [`HELLO`](https://redis.io/docs/latest/commands/hello)), a client can issue any commands while in the subscribed state.\n\nPlease note that when using `redis-cli`, in subscribed mode commands such as [`UNSUBSCRIBE`](https://redis.io/docs/latest/commands/unsubscribe) and [`PUNSUBSCRIBE`](https://redis.io/docs/latest/commands/punsubscribe) cannot be used because `redis-cli` will not accept any commands and can only quit the mode with `Ctrl-C`."},{"id":"delivery-semantics","title":"Delivery semantics","role":"content","text":"Redis' Pub/Sub exhibits _at-most-once_ message delivery semantics.\nAs the name suggests, it means that a message will be delivered once if at all.\nOnce the message is sent by the Redis server, there's no chance of it being sent again.\nIf the subscriber is unable to handle the message (for example, due to an error or a network disconnect) the message is forever lost.\n\nIf your application requires stronger delivery guarantees, you may want to learn about [Redis Streams](https://redis.io/docs/latest/develop/data-types/streams).\nMessages in streams are persisted, and support both _at-most-once_ as well as _at-least-once_ delivery semantics."},{"id":"format-of-pushed-messages","title":"Format of pushed messages","role":"content","text":"A message is an [array-reply](https://redis.io/docs/latest/develop/reference/protocol-spec#array-reply) with three elements.\n\nThe first element is the kind of message:\n\n* `subscribe`: means that we successfully subscribed to the channel given as the second element in the reply.\n  The third argument represents the number of channels we are currently subscribed to.\n\n* `unsubscribe`: means that we successfully unsubscribed from the channel given as second element in the reply.\n  The third argument represents the number of channels we are currently subscribed to.\n  When the last argument is zero, we are no longer subscribed to any channel, and the client can issue any kind of Redis command as we are outside the Pub/Sub state.\n\n* `message`: it is a message received as a result of a [`PUBLISH`](https://redis.io/docs/latest/commands/publish) command issued by another client.\n  The second element is the name of the originating channel, and the third argument is the actual message payload."},{"id":"database--scoping","title":"Database & Scoping","role":"content","text":"Pub/Sub has no relation to the key space.\nIt was made to not interfere with it on any level, including database numbers.\n\nPublishing on db 10, will be heard by a subscriber on db 1.\n\nIf you need scoping of some kind, prefix the channels with the name of the environment (test, staging, production...)."},{"id":"wire-protocol-example","title":"Wire protocol example","role":"content","text":"In this example, client A subscribes to two channels, client B publishes to one of them, and then client A unsubscribes from both:\n\n![Client A subscribes to channels first and second, client B publishes Hello to second, and client A unsubscribes from both](https://redis.io/docs/latest/images/dev/pubsub/pubsub-wire-sequence.svg)\n\nThe same exchange on the wire, starting with client A's subscription:\n\n[code example]\n\nAt this point, from another client we issue a [`PUBLISH`](https://redis.io/docs/latest/commands/publish) operation against the channel named `second`:\n\n[code example]\n\nThis is what the first client receives:\n\n[code example]\n\nNow the client unsubscribes itself from all the channels using the [`UNSUBSCRIBE`](https://redis.io/docs/latest/commands/unsubscribe) command without additional arguments:\n\n[code example]"},{"id":"pattern-matching-subscriptions","title":"Pattern-matching subscriptions","role":"content","text":"The Redis Pub/Sub implementation supports pattern matching.\nClients may subscribe to glob-style patterns to receive all the messages sent to channel names matching a given pattern.\n\nFor instance:\n\n[code example]\n\nWill receive all the messages sent to the channel `news.art.figurative`, `news.music.jazz`, etc.\nAll the glob-style patterns are valid, so multiple wildcards are supported.\n\n[code example]\n\nWill then unsubscribe the client from that pattern.\nNo other subscriptions will be affected by this call.\n\nMessages received as a result of pattern matching are sent in a different format:\n\n* The type of the message is `pmessage`: it is a message received as a result from a [`PUBLISH`](https://redis.io/docs/latest/commands/publish) command issued by another client, matching a pattern-matching subscription. \n  The second element is the original pattern matched, the third element is the name of the originating channel, and the last element is the actual message payload.\n\nSimilarly to [`SUBSCRIBE`](https://redis.io/docs/latest/commands/subscribe) and [`UNSUBSCRIBE`](https://redis.io/docs/latest/commands/unsubscribe), [`PSUBSCRIBE`](https://redis.io/docs/latest/commands/psubscribe) and [`PUNSUBSCRIBE`](https://redis.io/docs/latest/commands/punsubscribe) commands are acknowledged by the system sending a message of type `psubscribe` and `punsubscribe` using the same format as the `subscribe` and `unsubscribe` message format."},{"id":"messages-matching-both-a-pattern-and-a-channel-subscription","title":"Messages matching both a pattern and a channel subscription","role":"content","text":"A client may receive a single message multiple times if it's subscribed to multiple patterns matching a published message, or if it is subscribed to both patterns and channels matching the message. \nThis is shown by the following example:\n\n[code example]\n\nIn the above example, if a message is sent to channel `foo`, the client will receive two messages: one of type `message` and one of type `pmessage`.\n\n![One PUBLISH to foo reaches a client subscribed to both foo and f* as a message and a pmessage](https://redis.io/docs/latest/images/dev/pubsub/pubsub-pattern-and-channel.svg)"},{"id":"the-meaning-of-the-subscription-count-with-pattern-matching","title":"The meaning of the subscription count with pattern matching","role":"content","text":"In `subscribe`, `unsubscribe`, `psubscribe` and `punsubscribe` message types, the last argument is the count of subscriptions still active. \nThis number is the total number of channels and patterns the client is still subscribed to. \nSo the client will exit the Pub/Sub state only when this count drops to zero as a result of unsubscribing from all the channels and patterns."},{"id":"sharded-pubsub","title":"Sharded Pub/Sub","role":"content","text":"From Redis 7.0, sharded Pub/Sub is introduced in which shard channels are assigned to slots by the same algorithm used to assign keys to slots. \nA shard message must be sent to a node that owns the slot the shard channel is hashed to. \nThe cluster makes sure the published shard messages are forwarded to all nodes in the shard, so clients can subscribe to a shard channel by connecting to either the master responsible for the slot, or to any of its replicas.\n[`SSUBSCRIBE`](https://redis.io/docs/latest/commands/ssubscribe), [`SUNSUBSCRIBE`](https://redis.io/docs/latest/commands/sunsubscribe) and [`SPUBLISH`](https://redis.io/docs/latest/commands/spublish) are used to implement sharded Pub/Sub.\n\nSharded Pub/Sub helps to scale the usage of Pub/Sub in cluster mode. \nIt restricts the propagation of messages to be within the shard of a cluster. \nHence, the amount of data passing through the cluster bus is limited in comparison to global Pub/Sub where each message propagates to each node in the cluster.\nThis allows users to horizontally scale the Pub/Sub usage by adding more shards."},{"id":"programming-example","title":"Programming example","role":"content","text":"Pieter Noordhuis provided a great example using EventMachine and Redis to create [a multi user high performance web chat](https://gist.github.com/pietern/348262)."},{"id":"client-library-implementation-hints","title":"Client library implementation hints","role":"content","text":"Because all the messages received contain the original subscription causing the message delivery (the channel in the case of message type, and the original pattern in the case of pmessage type) client libraries may bind the original subscription to callbacks (that can be anonymous functions, blocks, function pointers), using a hash table.\n\nWhen a message is received an O(1) lookup can be done to deliver the message to the registered callback."}],"examples":[{"id":"overview-ex0","language":"bash","code":"SUBSCRIBE channel11 ch:00","section_id":"overview"},{"id":"wire-protocol-example-ex0","language":"plaintext","code":"SUBSCRIBE first second\n*3\n$9\nsubscribe\n$5\nfirst\n:1\n*3\n$9\nsubscribe\n$6\nsecond\n:2","section_id":"wire-protocol-example"},{"id":"wire-protocol-example-ex1","language":"plaintext","code":"> PUBLISH second Hello","section_id":"wire-protocol-example"},{"id":"wire-protocol-example-ex2","language":"plaintext","code":"*3\n$7\nmessage\n$6\nsecond\n$5\nHello","section_id":"wire-protocol-example"},{"id":"wire-protocol-example-ex3","language":"plaintext","code":"UNSUBSCRIBE\n*3\n$11\nunsubscribe\n$6\nsecond\n:1\n*3\n$11\nunsubscribe\n$5\nfirst\n:0","section_id":"wire-protocol-example"},{"id":"pattern-matching-subscriptions-ex0","language":"plaintext","code":"PSUBSCRIBE news.*","section_id":"pattern-matching-subscriptions"},{"id":"pattern-matching-subscriptions-ex1","language":"plaintext","code":"PUNSUBSCRIBE news.*","section_id":"pattern-matching-subscriptions"},{"id":"messages-matching-both-a-pattern-and-a-channel-subscription-ex0","language":"plaintext","code":"SUBSCRIBE foo\nPSUBSCRIBE f*","section_id":"messages-matching-both-a-pattern-and-a-channel-subscription"}]}
{"schema_version":2,"id":"develop/pubsub/keyspace-notifications","title":"Redis keyspace notifications","url":"https://redis.io/docs/latest/develop/pubsub/keyspace-notifications/","summary":"Monitor changes to Redis keys and values in real time","aliases":["/develop/use/keyspace-notifications","/develop/use/keyspace-notifications/","/manual/keyspace-notifications/","/develop/reference/keyspace-notifications/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T14:32:43+01:00","page_type":"content","content_hash":"4a0c893a771c0a8dc1bf209e8ab1e946af212d57c7793e50efcf984c3f280cb3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Keyspace notifications allow clients to subscribe to Pub/Sub channels in order\nto receive events affecting the Redis data set in some way.\n\nExamples of events that can be received are:\n\n* All the commands affecting a given key.\n* All the keys receiving an LPUSH operation.\n* All the keys expiring in the database 0.\n\nNote: Redis Pub/Sub is *fire and forget*; that is, if your Pub/Sub client disconnects,\nand reconnects later, all the events delivered during the time the client was\ndisconnected are lost."},{"id":"type-of-events","title":"Type of events","role":"content","text":"Keyspace notifications are implemented by sending two distinct types of events\nfor every operation affecting the Redis data space. For instance a [`DEL`](https://redis.io/docs/latest/commands/del)\noperation targeting the key named `mykey` in database `0` will trigger\nthe delivering of two messages, exactly equivalent to the following two\n[`PUBLISH`](https://redis.io/docs/latest/commands/publish) commands:\n\n    PUBLISH __keyspace@0__:mykey del\n    PUBLISH __keyevent@0__:del mykey\n\nThe first channel listens to all the events targeting\nthe key `mykey` and the other channel listens only to `del` operation\nevents on the key `mykey`\n\nThe first kind of event, with `keyspace` prefix in the channel is called\na **Key-space notification**, while the second, with the `keyevent` prefix,\nis called a **Key-event notification**.\n\nIn the previous example a `del` event was generated for the key `mykey` resulting\nin two messages:\n\n* The Key-space channel receives as message the name of the event.\n* The Key-event channel receives as message the name of the key.\n\nIt is possible to enable only one kind of notification in order to deliver\njust the subset of events we are interested in."},{"id":"configuration","title":"Configuration","role":"configuration","text":"By default keyspace event notifications are disabled because while not\nvery sensible the feature uses some CPU power. Notifications are enabled\nusing the `notify-keyspace-events` of redis.conf or via the **CONFIG SET**.\n\nSetting the parameter to the empty string disables notifications.\nIn order to enable the feature a non-empty string is used, composed of multiple\ncharacters, where every character has a special meaning according to the\nfollowing table:\n\n    K     Keyspace events, published with __keyspace@<db>__ prefix.\n    E     Keyevent events, published with __keyevent@<db>__ prefix.\n    g     Generic commands (non-type specific) like DEL, EXPIRE, RENAME, ...\n    $     String commands\n    l     List commands\n    s     Set commands\n    h     Hash commands\n    z     Sorted set commands\n    t     Stream commands\n    a     Array commands\n    d     Module key type events\n    x     Expired events (events generated every time a key expires)\n    e     Evicted events (events generated when a key is evicted for maxmemory)\n    m     Key miss events generated when a key that doesn't exist is accessed (Note: not included in the 'A' class)\n    n     New key events generated whenever a new key is created (Note: not included in the 'A' class)\n    o     Overwritten events generated every time a key is overwritten (Note: not included in the 'A' class)\n    c     Type-changed events generated every time a key's type changes (Note: not included in the 'A' class)\n    A     Alias for \"g$lshztdxea\", so that the \"AKE\" string means all the events except \"m\", \"n\", \"o\" and \"c\".\n\nAt least `K` or `E` should be present in the string, otherwise no event\nwill be delivered regardless of the rest of the string.\n\nFor instance to enable just Key-space events for lists, the configuration\nparameter must be set to `Kl`, and so forth.\n\nYou can use the string `KEA` to enable most types of events."},{"id":"events-generated-by-different-commands","title":"Events generated by different commands","role":"content","text":"Different commands generate different kind of events according to the following list.\n* [`APPEND`](https://redis.io/docs/latest/commands/append) generates an `append` event.\n* [`COPY`](https://redis.io/docs/latest/commands/copy) generates a `copy_to` event.\n* [`DEL`](https://redis.io/docs/latest/commands/del) generates a `del` event for every deleted key.\n* [`EXPIRE`](https://redis.io/docs/latest/commands/expire) and all its variants ([`PEXPIRE`](https://redis.io/docs/latest/commands/pexpire), [`EXPIREAT`](https://redis.io/docs/latest/commands/expireat), [`PEXPIREAT`](https://redis.io/docs/latest/commands/pexpireat)) generate an `expire` event when called with a positive timeout (or a future timestamp). Note that when these commands are called with a negative timeout value or timestamp in the past, the key is deleted and only a `del` event is generated instead.\n* [`HDEL`](https://redis.io/docs/latest/commands/hdel) generates a single `hdel` event, and an additional `del` event if the resulting hash is empty and the key is removed.\n* [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) and all its variants ([`HEXPIREAT`](https://redis.io/docs/latest/commands/hpexpireat), [`HPEXPIRE`](https://redis.io/docs/latest/commands/hpexpire), [`HPEXPIREAT`](https://redis.io/docs/latest/commands/hpexpireat)) generate `hexpired` events. Furthermore, `hexpired` events are generated when fields expire.\n* [`HINCRBYFLOAT`](https://redis.io/docs/latest/commands/hincrbyfloat) generates an `hincrbyfloat` event.\n* [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby) generates an `hincrby` event.\n* [`HPERSIST`](https://redis.io/docs/latest/commands/hpersist) generates an `hpersist` event.\n* [`HSET`](https://redis.io/docs/latest/commands/hset), [`HSETNX`](https://redis.io/docs/latest/commands/hsetnx) and [`HMSET`](https://redis.io/docs/latest/commands/hmset) all generate a single `hset` event.\n* [`INCRBYFLOAT`](https://redis.io/docs/latest/commands/incrbyfloat) generates an `incrbyfloat` events.\n* [`INCR`](https://redis.io/docs/latest/commands/incr), [`DECR`](https://redis.io/docs/latest/commands/decr), [`INCRBY`](https://redis.io/docs/latest/commands/incrby), [`DECRBY`](https://redis.io/docs/latest/commands/decrby) commands all generate `incrby` events.\n* [`INCREX`](https://redis.io/docs/latest/commands/increx) generates an `incrby` event (or `incrbyfloat` when used with `BYFLOAT`). Additionally, an `expire` event is generated when `EX`/`PX`/`EXAT`/`PXAT` is used, a `persist` event when `PERSIST` is used, or a `del` event if the specified expiration is already in the past and the key is removed.\n* [`LINSERT`](https://redis.io/docs/latest/commands/linsert) generates an `linsert` event.\n* [`LMOVE`](https://redis.io/docs/latest/commands/lmove) and [`BLMOVE`](https://redis.io/docs/latest/commands/blmove) generate an `lpush`/`rpush` event (depending on the whereto argument) and an `lpop`/`rpop` event (depending on the wherefrom argument). In both cases the order is guaranteed (the `lpush`/`rpush` event will always be delivered before the `lpop`/`rpop` event). Additionally a `del` event will be generated if the resulting list is zero length and the key is removed.\n* [`LPOP`](https://redis.io/docs/latest/commands/lpop) generates an `lpop` event. Additionally a `del` event is generated if the key is removed because the last element from the list was popped.\n* [`LPUSH`](https://redis.io/docs/latest/commands/lpush) and [`LPUSHX`](https://redis.io/docs/latest/commands/lpushx) generates a single `lpush` event, even in the variadic case.\n* [`LREM`](https://redis.io/docs/latest/commands/lrem) generates an `lrem` event, and additionally a `del` event if the resulting list is empty and the key is removed.\n* [`LSET`](https://redis.io/docs/latest/commands/lset) generates an `lset` event.\n* [`LTRIM`](https://redis.io/docs/latest/commands/ltrim) generates an `ltrim` event, and additionally a `del` event if the resulting list is empty and the key is removed.\n* [`MIGRATE`](https://redis.io/docs/latest/commands/migrate) generates a `del` event if the source key is removed.\n* [`MOVE`](https://redis.io/docs/latest/commands/move) generates two events, a `move_from` event for the source key, and a `move_to` event for the destination key.\n* [`MSET`](https://redis.io/docs/latest/commands/mset) generates a separate `set` event for every key.\n* [`PERSIST`](https://redis.io/docs/latest/commands/persist) generates a `persist` event if the expiry time associated with key has been successfully deleted.\n* [`RENAME`](https://redis.io/docs/latest/commands/rename) generates two events, a `rename_from` event for the source key, and a `rename_to` event for the destination key.\n* [`RESTORE`](https://redis.io/docs/latest/commands/restore) generates a `restore` event for the key.\n* [`RPOPLPUSH`](https://redis.io/docs/latest/commands/rpoplpush) and [`BRPOPLPUSH`](https://redis.io/docs/latest/commands/brpoplpush) generate an `lpush` event and an `rpop` event. In both cases the order is guaranteed (the `lpush` event will always be delivered before the `rpop` event). Additionally a `del` event will be generated if the resulting list is zero length and the key is removed.\n* [`RPOP`](https://redis.io/docs/latest/commands/rpop) generates an `rpop` event. Additionally a `del` event is generated if the key is removed because the last element from the list was popped.\n* [`RPUSH`](https://redis.io/docs/latest/commands/rpush) and [`RPUSHX`](https://redis.io/docs/latest/commands/rpushx) generates a single `rpush` event, even in the variadic case.\n* [`SADD`](https://redis.io/docs/latest/commands/sadd) generates a single `sadd` event, even in the variadic case.\n* [`SETRANGE`](https://redis.io/docs/latest/commands/setrange) generates a `setrange` event.\n* [`SET`](https://redis.io/docs/latest/commands/set) and all its variants ([`SETEX`](https://redis.io/docs/latest/commands/setex), [`SETNX`](https://redis.io/docs/latest/commands/setnx),[`GETSET`](https://redis.io/docs/latest/commands/getset)) generate `set` events. However [`SETEX`](https://redis.io/docs/latest/commands/setex) will also generate an `expire` events.\n* [`SINTERSTORE`](https://redis.io/docs/latest/commands/sinterstore), [`SUNIONSTORE`](https://redis.io/docs/latest/commands/sunionstore), [`SDIFFSTORE`](https://redis.io/docs/latest/commands/sdiffstore) generate `sinterstore`, `sunionstore`, `sdiffstore` events respectively. In the special case the resulting set is empty, and the key where the result is stored already exists, a `del` event is generated since the key is removed.\n* [`SMOVE`](https://redis.io/docs/latest/commands/smove) generates an `srem` event for the source key, and an `sadd` event for the destination key.\n* [`SORT`](https://redis.io/docs/latest/commands/sort) generates a `sortstore` event when `STORE` is used to set a new key. If the resulting list is empty, and the `STORE` option is used, and there was already an existing key with that name, the result is that the key is deleted, so a `del` event is generated in this condition.\n* [`SPOP`](https://redis.io/docs/latest/commands/spop) generates an `spop` event, and an additional `del` event if the resulting set is empty and the key is removed.\n* [`SREM`](https://redis.io/docs/latest/commands/srem) generates a single `srem` event, and an additional `del` event if the resulting set is empty and the key is removed.\n* [`XADD`](https://redis.io/docs/latest/commands/xadd) generates an `xadd` event, possibly followed an `xtrim` event when used with the `MAXLEN` subcommand.\n* [`XDEL`](https://redis.io/docs/latest/commands/xdel) generates a single `xdel` event even when multiple entries are deleted.\n* [`XGROUP CREATECONSUMER`](https://redis.io/docs/latest/commands/xgroup-createconsumer) generates an `xgroup-createconsumer` event.\n* [`XGROUP CREATE`](https://redis.io/docs/latest/commands/xgroup-create) generates an `xgroup-create` event.\n* [`XGROUP DELCONSUMER`](https://redis.io/docs/latest/commands/xgroup-delconsumer) generates an `xgroup-delconsumer` event.\n* [`XGROUP DESTROY`](https://redis.io/docs/latest/commands/xgroup-destroy) generates an `xgroup-destroy` event.\n* [`XGROUP SETID`](https://redis.io/docs/latest/commands/xgroup-setid) generates an `xgroup-setid` event.\n* [`XSETID`](https://redis.io/docs/latest/commands/xsetid) generates an `xsetid` event.\n* [`XTRIM`](https://redis.io/docs/latest/commands/xtrim) generates an `xtrim` event.\n* [`ZADD`](https://redis.io/docs/latest/commands/zadd) generates a single `zadd` event even when multiple elements are added.\n* [`ZDIFFSTORE`](https://redis.io/docs/latest/commands/zdiffstore), [`ZINTERSTORE`](https://redis.io/docs/latest/commands/zinterstore) and [`ZUNIONSTORE`](https://redis.io/docs/latest/commands/zunionstore) respectively generate `zdiffstore`, `zinterstore` and `zunionstore` events. In the special case the resulting sorted set is empty, and the key where the result is stored already exists, a `del` event is generated since the key is removed.\n* [`ZINCRBY`](https://redis.io/docs/latest/commands/zincrby) generates a `zincr` event.\n* [`ZREMRANGEBYRANK`](https://redis.io/docs/latest/commands/zremrangebyrank) generates a single `zrembyrank` event. When the resulting sorted set is empty and the key is generated, an additional `del` event is generated.\n* [`ZREMRANGEBYSCORE`](https://redis.io/docs/latest/commands/zremrangebyscore) generates a single `zrembyscore` event. When the resulting sorted set is empty and the key is generated, an additional `del` event is generated.\n* [`ZREM`](https://redis.io/docs/latest/commands/zrem) generates a single `zrem` event even when multiple elements are deleted. When the resulting sorted set is empty and the key is generated, an additional `del` event is generated.\n* Every time a key with a time to live associated is removed from the data set because it expired, an `expired` event is generated.\n* Every time a key is evicted from the data set in order to free memory as a result of the `maxmemory` policy, an `evicted` event is generated.\n* Every time a new key is added to the data set, a `new` event is generated.\n* Every time a key is being overwritten, an `overwritten` event is generated.\n* Every time a key's type changes, a `type_changed` event is generated.\n\n**IMPORTANT** all the commands generate events only if the target key is really modified. For instance an [`SREM`](https://redis.io/docs/latest/commands/srem) deleting a non-existing element from a Set will not actually change the value of the key, so no event will be generated.\n\nIf in doubt about how events are generated for a given command, the simplest\nthing to do is to watch yourself:\n\n    $ redis-cli config set notify-keyspace-events KEA\n    $ redis-cli --csv psubscribe '__key*__:*'\n    Reading messages... (press Ctrl-C to quit)\n    \"psubscribe\",\"__key*__:*\",1\n\nAt this point use `redis-cli` in another terminal to send commands to the\nRedis server and watch the events generated:\n\n    \"pmessage\",\"__key*__:*\",\"__keyspace@0__:foo\",\"set\"\n    \"pmessage\",\"__key*__:*\",\"__keyevent@0__:set\",\"foo\"\n    ..."},{"id":"timing-of-expired-events","title":"Timing of expired events","role":"content","text":"Keys with a time to live associated are expired by Redis in two ways:\n\n* When the key is accessed by a command and is found to be expired.\n* Via a background system that looks for expired keys in the background, incrementally, in order to be able to also collect keys that are never accessed.\n\nThe `expired` events are generated when a key is accessed and is found to be expired by one of the above systems, as a result there are no guarantees that the Redis server will be able to generate the `expired` event at the time the key time to live reaches the value of zero.\n\nIf no command targets the key constantly, and there are many keys with a TTL associated, there can be a significant delay between the time the key time to live drops to zero, and the time the `expired` event is generated.\n\nExpired (`expired`) events are generated when the Redis server deletes the key and not when the time to live theoretically reaches the value of zero."},{"id":"events-in-a-cluster","title":"Events in a cluster","role":"content","text":"Every node of a Redis cluster generates events about its own subset of the keyspace as described above. However, unlike regular Pub/Sub communication in a cluster, events' notifications **are not** broadcasted to all nodes. Put differently, keyspace events are node-specific. This means that to receive all keyspace events of a cluster, clients need to subscribe to each of the nodes."},{"id":"see-also","title":"See also","role":"related","text":"See the [subkey notifications](https://redis.io/docs/latest/develop/pubsub/subkeyspace-notifications) page for information about subkey notifications (for example, individual fields of a hash key).\n\n@history\n\n*   `>= 6.0`: Key miss events were added.\n*   `>= 7.0`: Event type `new` added"}],"examples":[]}
{"schema_version":2,"id":"develop/pubsub/subkeyspace-notifications","title":"Redis subkey notifications","url":"https://redis.io/docs/latest/develop/pubsub/subkeyspace-notifications/","summary":"Monitor changes to individual subkeys in real time","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T14:32:43+01:00","page_type":"content","content_hash":"6eae3bcb7d4647261bba9c0add1604c8429b7a390276dd80e22268a6ccfedadb","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Subkey notifications, added in Redis 8.8, extend Redis's existing [keyspace notification](https://redis.io/docs/latest/develop/pubsub/keyspace-notifications) system to include the key, the subkey (for example, the field for hashes, the path for JSON documents, and the element for arrays), and the event type.\n\nWith standard keyspace notifications, when a hash field is modified via [`HSET`](https://redis.io/docs/latest/commands/hset), [`HDEL`](https://redis.io/docs/latest/commands/hdel), or [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire), the subscriber receives the key name and the event type but not which specific fields were affected. Subkey notifications solve this by carrying the affected field names in the message payload.\n\nSubkey notifications are delivered through Pub/Sub channels and are independent of the standard keyspace/keyevent notification channels. Enabling subkey notifications does **not** implicitly enable standard keyspace notifications, and vice versa.\n\nNote: Redis Pub/Sub is *fire and forget*. If your Pub/Sub client disconnects and reconnects later, all events delivered during the disconnection period are lost."},{"id":"notification-channels","title":"Notification channels","role":"content","text":"Four new channel types are available, each suited to a different subscription pattern:\n\n| Channel format | Payload |\n|---|---|\n| `__subkeyspace@<db>__:<key>` | `<event>\\|<len>:<subkey>[,...]` |\n| `__subkeyevent@<db>__:<event>` | `<key_len>:<key>\\|<len>:<subkey>[,...]` |\n| `__subkeyspaceitem@<db>__:<key>\\n<subkey>` | `<event>` |\n| `__subkeyspaceevent@<db>__:<event>\\|<key>` | `<len>:<subkey>[,...]` |\n\n**Design rationale:**\n\n- **Subkeyspace** (`__subkeyspace@<db>__:<key>`): Subscribe to a specific key; each message contains an event type and subkey names.\n- **Subkeyevent** (`__subkeyevent@<db>__:<event>`): Subscribe to a specific event type; each message contains a key name and subkey names.\n- **Subkeyspaceitem** (`__subkeyspaceitem@<db>__:<key>\\n<subkey>`): Subscribe to a specific key and subkey name combination; each message contains an event type.\n- **Subkeyspaceevent** (`__subkeyspaceevent@<db>__:<event>|<key>`): Subscribe to a specific event and key combination; each message contains subkey names.\n\nSubkeys in the payload are encoded in a length-prefixed format (`<len>:<subkey>`) to support binary-safe subkey names that may contain delimiter characters.\n\n**Safeguards:**\n\n- Events whose name contains `|` are skipped for the `__subkeyspace` and `__subkeyspaceevent` channels to avoid parsing ambiguity.\n- Keys containing `\\n` are skipped for the `__subkeyspaceitem` channel because newline is the key/subkey separator.\n- Subkey events are only published when at least one subkey is present."},{"id":"configuration","title":"Configuration","role":"configuration","text":"Subkey notifications are controlled by the existing `notify-keyspace-events` configuration string. Four new flag characters are added:\n\n    S     Subkeyspace events, published with __subkeyspace@<db>__ prefix.\n    T     Subkeyevent events, published with __subkeyevent@<db>__ prefix.\n    I     Subkeyspaceitem events, published with __subkeyspaceitem@<db>__ prefix.\n    V     Subkeyspaceevent events, published with __subkeyspaceevent@<db>__ prefix.\n    h     Hash commands.\n\nThese flags are **independent** from the existing key-level flags (`K`, `E`, and so on). You may enable any combination. For example, to enable only the subkeyspace and subkeyevent channels:\n\n    $ redis-cli config set notify-keyspace-events ST\n\nTo enable all four subkey channel types:\n\n    $ redis-cli config set notify-keyspace-events STIV"},{"id":"commands-emitting-subkey-notifications","title":"Commands emitting subkey notifications","role":"syntax","text":"The following commands emit subkey notifications. Currently, only hash commands are supported; support for additional data types is planned for future releases.\n\n| Command | Event | Subkeys included |\n|---|---|---|\n| [`HSET`](https://redis.io/docs/latest/commands/hset) / [`HMSET`](https://redis.io/docs/latest/commands/hmset) | `hset` | All fields being set |\n| [`HSETNX`](https://redis.io/docs/latest/commands/hsetnx) | `hset` | The field (only if it was set) |\n| [`HDEL`](https://redis.io/docs/latest/commands/hdel) | `hdel` | All fields deleted |\n| [`HGETDEL`](https://redis.io/docs/latest/commands/hgetdel) | `hdel` / `hexpired` | Deleted or lazily expired fields |\n| [`HGETEX`](https://redis.io/docs/latest/commands/hgetex) | `hexpire` / `hpersist` / `hdel` / `hexpired` | Affected fields per event type |\n| [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby) | `hincrby` | The field |\n| [`HINCRBYFLOAT`](https://redis.io/docs/latest/commands/hincrbyfloat) | `hincrbyfloat` | The field |\n| [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) / [`HPEXPIRE`](https://redis.io/docs/latest/commands/hpexpire) / [`HEXPIREAT`](https://redis.io/docs/latest/commands/hexpireat) / [`HPEXPIREAT`](https://redis.io/docs/latest/commands/hpexpireat) | `hexpire` | Fields whose TTLs were updated |\n| [`HPERSIST`](https://redis.io/docs/latest/commands/hpersist) | `hpersist` | Fields that were persisted |\n| [`HSETEX`](https://redis.io/docs/latest/commands/hsetex) | `hset` / `hdel` / `hexpire` / `hexpired` | Affected fields per event type |\n| Subkey expiration (active or lazy) | `hexpired` | All expired fields, batched into a single notification |"},{"id":"watching-events-in-real-time","title":"Watching events in real time","role":"content","text":"To observe subkey notifications, enable the desired channel types and use `redis-cli` to subscribe with a glob pattern:\n\n    $ redis-cli config set notify-keyspace-events ST\n    $ redis-cli --csv psubscribe '__subkey*'\n    Reading messages... (press Ctrl-C to quit)\n    \"psubscribe\",\"__subkey*\",1\n\nThen, in another terminal, run a command:\n\n    $ redis-cli hset myhash field1 val1 field2 val2\n\nYou will see messages similar to the following:\n\n    \"pmessage\",\"__subkey*\",\"__subkeyspace@0__:myhash\",\"hset|7:field1,7:field2\"\n    \"pmessage\",\"__subkey*\",\"__subkeyevent@0__:hset\",\"5:myhash|7:field1,7:field2\""}],"examples":[]}
{"schema_version":2,"id":"develop/reference/client-side-caching","title":"Client-side caching reference","url":"https://redis.io/docs/latest/develop/reference/client-side-caching/","summary":"Server-assisted, client-side caching in Redis","aliases":["/develop/use/client-side-caching/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:49:27+01:00","page_type":"content","content_hash":"fdf82f74fe3d10b221ae5ff24d2716ea845061d1ade176823fa05c472777e247","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This document is intended as an in-depth reference for\n> client-side caching. See\n> [Client-side caching introduction](https://redis.io/docs/latest/develop/clients/client-side-caching)\n> for general usage guidelines.\n\nClient-side caching is a technique used to create high performance services.\nIt exploits the memory available on application servers, servers that are\nusually distinct computers compared to the database nodes, to store some subset\nof the database information directly in the application side.\n\nNormally when data is required, the application servers ask the database about\nsuch information, like in the following diagram:\n\n\n    +-------------+                                +----------+\n    |             | ------- GET user:1234 -------> |          |\n    | Application |                                | Database |\n    |             | <---- username = Alice ------- |          |\n    +-------------+                                +----------+\n\nWhen client-side caching is used, the application will store the reply of\npopular queries directly inside the application memory, so that it can\nreuse such replies later, without contacting the database again:\n\n    +-------------+                                +----------+\n    |             |                                |          |\n    | Application |       ( No chat needed )       | Database |\n    |             |                                |          |\n    +-------------+                                +----------+\n    | Local cache |\n    |             |\n    | user:1234 = |\n    | username    |\n    | Alice       |\n    +-------------+\n\nWhile the application memory used for the local cache may not be very big,\nthe time needed in order to access the local computer memory is orders of\nmagnitude smaller compared to accessing a networked service like a database.\nSince often the same small percentage of data are accessed frequently,\nthis pattern can greatly reduce the latency for the application to get data\nand, at the same time, the load in the database side.\n\nMoreover there are many datasets where items change very infrequently.\nFor instance, most user posts in a social network are either immutable or\nrarely edited by the user. Adding to this the fact that usually a small\npercentage of the posts are very popular, either because a small set of users\nhave a lot of followers and/or because recent posts have a lot more\nvisibility, it is clear why such a pattern can be very useful.\n\nUsually the two key advantages of client-side caching are:\n\n1. Data is available with a very small latency.\n2. The database system receives less queries, allowing it to serve the same dataset with a smaller number of nodes."},{"id":"there-are-two-hard-problems-in-computer-science","title":"There are two hard problems in computer science...","role":"content","text":"A problem with the above pattern is how to invalidate the information that\nthe application is holding, in order to avoid presenting stale data to the\nuser. For example after the application above locally cached the information\nfor user:1234, Alice may update her username to Flora. Yet the application\nmay continue to serve the old username for user:1234.\n\nSometimes, depending on the exact application we are modeling, this isn't a\nbig deal, so the client will just use a fixed maximum \"time to live\" for the\ncached information. Once a given amount of time has elapsed, the information\nwill no longer be considered valid. More complex patterns, when using Redis,\nleverage the Pub/Sub system in order to send invalidation messages to\nlistening clients. This can be made to work but is tricky and costly from\nthe point of view of the bandwidth used, because often such patterns involve\nsending the invalidation messages to every client in the application, even\nif certain clients may not have any copy of the invalidated data. Moreover\nevery application query altering the data requires to use the [`PUBLISH`](https://redis.io/docs/latest/commands/publish)\ncommand, costing the database more CPU time to process this command.\n\nRegardless of what schema is used, there is a simple fact: many very large\napplications implement some form of client-side caching, because it is the\nnext logical step to having a fast store or a fast cache server. For this\nreason Redis 6 implements direct support for client-side caching, in order\nto make this pattern much simpler to implement, more accessible, reliable,\nand efficient."},{"id":"the-redis-implementation-of-client-side-caching","title":"The Redis implementation of client-side caching","role":"content","text":"The Redis client-side caching support is called _Tracking_, and has two modes:\n\n* In the default mode, the server remembers what keys a given client accessed, and sends invalidation messages when the same keys are modified. This costs memory in the server side, but sends invalidation messages only for the set of keys that the client might have in memory.\n* In the _broadcasting_ mode, the server does not attempt to remember what keys a given client accessed, so this mode costs no memory at all in the server side. Instead clients subscribe to key prefixes such as `object:` or `user:`, and receive a notification message every time a key matching a subscribed prefix is touched.\n\nTo recap, for now let's forget for a moment about the broadcasting mode, to\nfocus on the first mode. We'll describe broadcasting in more detail later.\n\n1. Clients can enable tracking if they want. Connections start without tracking enabled.\n2. When tracking is enabled, the server remembers what keys each client requested during the connection lifetime (by sending read commands about such keys).\n3. When a key is modified by some client, or is evicted because it has an associated expire time, or evicted because of a _maxmemory_ policy, all the clients with tracking enabled that may have the key cached, are notified with an _invalidation message_.\n4. When clients receive invalidation messages, they are required to remove the corresponding keys, in order to avoid serving stale data.\n\nThis is an example of the protocol:\n\n* Client 1 `->` Server: CLIENT TRACKING ON\n* Client 1 `->` Server: GET foo\n* (The server remembers that Client 1 may have the key \"foo\" cached)\n* (Client 1 may remember the value of \"foo\" inside its local memory)\n* Client 2 `->` Server: SET foo SomeOtherValue\n* Server `->` Client 1: INVALIDATE \"foo\"\n\nThis looks great superficially, but if you imagine 10k connected clients all\nasking for millions of keys over long living connection, the server ends up\nstoring too much information. For this reason Redis uses two key ideas in\norder to limit the amount of memory used server-side and the CPU cost of\nhandling the data structures implementing the feature:\n\n* The server remembers the list of clients that may have cached a given key in a single global table. This table is called the **Invalidation Table**. The invalidation table can contain a maximum number of entries. If a new key is inserted, the server may evict an older entry by pretending that such key was modified (even if it was not), and sending an invalidation message to the clients. Doing so, it can reclaim the memory used for this key, even if this will force the clients having a local copy of the key to evict it.\n* Inside the invalidation table we don't really need to store pointers to clients' structures, that would force a garbage collection procedure when the client disconnects: instead what we do is just store client IDs (each Redis client has a unique numerical ID). If a client disconnects, the information will be incrementally garbage collected as caching slots are invalidated.\n* There is a single keys namespace, not divided by database numbers. So if a client is caching the key `foo` in database 2, and some other client changes the value of the key `foo` in database 3, an invalidation message will still be sent. This way we can ignore database numbers reducing both the memory usage and the implementation complexity."},{"id":"two-connections-mode","title":"Two connections mode","role":"content","text":"Using the new version of the Redis protocol, RESP3, supported by Redis 6, it is possible to run the data queries and receive the invalidation messages in the same connection. However many client implementations may prefer to implement client-side caching using two separated connections: one for data, and one for invalidation messages. For this reason when a client enables tracking, it can specify to redirect the invalidation messages to another connection by specifying the \"client ID\" of a different connection. Many data connections can redirect invalidation messages to the same connection, this is useful for clients implementing connection pooling. The two connections model is the only one that is also supported for RESP2 (which lacks the ability to multiplex different kind of information in the same connection).\n\nHere's an example of a complete session using the Redis protocol in the old RESP2 mode involving the following steps: enabling tracking redirecting to another connection, asking for a key, and getting an invalidation message once the key gets modified.\n\nTo start, the client opens a first connection that will be used for invalidations, requests the connection ID, and subscribes via Pub/Sub to the special channel that is used to get invalidation messages when in RESP2 modes (remember that RESP2 is the usual Redis protocol, and not the more advanced protocol that you can use, optionally, with Redis 6 using the [`HELLO`](https://redis.io/docs/latest/commands/hello) command):\n\n[code example]\n\nNow we can enable tracking from the data connection:\n\n[code example]\n\nThe client may decide to cache `\"foo\" => \"bar\"` in the local memory.\n\nA different client will now modify the value of the \"foo\" key:\n\n[code example]\n\nAs a result, the invalidations connection will receive a message that invalidates the specified key.\n\n[code example]\nThe client will check if there are cached keys in this caching slot, and will evict the information that is no longer valid.\n\nNote that the third element of the Pub/Sub message is not a single key but\nis a Redis array with just a single element. Since we send an array, if there\nare groups of keys to invalidate, we can do that in a single message.\nIn case of a flush ([`FLUSHALL`](https://redis.io/docs/latest/commands/flushall) or [`FLUSHDB`](https://redis.io/docs/latest/commands/flushdb)), a `null` message will be sent.\n\nA very important thing to understand about client-side caching used with\nRESP2 and a Pub/Sub connection in order to read the invalidation messages,\nis that using Pub/Sub is entirely a trick **in order to reuse old client\nimplementations**, but actually the message is not really sent to a channel\nand received by all the clients subscribed to it. Only the connection we\nspecified in the `REDIRECT` argument of the [`CLIENT`](https://redis.io/docs/latest/commands/client) command will actually\nreceive the Pub/Sub message, making the feature a lot more scalable.\n\nWhen RESP3 is used instead, invalidation messages are sent (either in the\nsame connection, or in the secondary connection when redirection is used)\nas `push` messages (read the RESP3 specification for more information)."},{"id":"what-tracking-tracks","title":"What tracking tracks","role":"content","text":"As you can see clients do not need, by default, to tell the server what keys\nthey are caching. Every key that is mentioned in the context of a read-only\ncommand is tracked by the server, because it *could be cached*.\n\nThis has the obvious advantage of not requiring the client to tell the server\nwhat it is caching. Moreover in many clients implementations, this is what\nyou want, because a good solution could be to just cache everything that is not\nalready cached, using a first-in first-out approach: we may want to cache a\nfixed number of objects, every new data we retrieve, we could cache it,\ndiscarding the oldest cached object. More advanced implementations may instead\ndrop the least used object or alike.\n\nNote that anyway if there is write traffic on the server, caching slots\nwill get invalidated during the course of the time. In general when the\nserver assumes that what we get we also cache, we are making a tradeoff:\n\n1. It is more efficient when the client tends to cache many things with a policy that welcomes new objects.\n2. The server will be forced to retain more data about the client keys.\n3. The client will receive useless invalidation messages about objects it did not cache.\n\nSo there is an alternative described in the next section."},{"id":"opt-in-and-opt-out-caching","title":"Opt-in and Opt-out caching","role":"content","text":""},{"id":"opt-in","title":"Opt-in","role":"content","text":"Clients implementations may want to cache only selected keys, and communicate\nexplicitly to the server what they'll cache and what they will not. This will\nrequire more bandwidth when caching new objects, but at the same time reduces\nthe amount of data that the server has to remember and the amount of\ninvalidation messages received by the client.\n\nIn order to do this, tracking must be enabled using the OPTIN option:\n\n    CLIENT TRACKING ON REDIRECT 1234 OPTIN\n\nIn this mode, by default, keys mentioned in read queries *are not supposed to be cached*, instead when a client wants to cache something, it must send a special command immediately before the actual command to retrieve the data:\n\n    CLIENT CACHING YES\n    +OK\n    GET foo\n    \"bar\"\n\nThe `CACHING` command affects the command executed immediately after it.\nHowever, in case the next command is [`MULTI`](https://redis.io/docs/latest/commands/multi), all the commands in the\ntransaction will be tracked. Similarly, in case of Lua scripts, all the\ncommands executed by the script will be tracked."},{"id":"opt-out","title":"Opt-out","role":"content","text":"Opt-out caching allows clients to automatically cache keys locally without explicitly opting in for each key.\nThis approach ensures that all keys are cached by default unless specified otherwise.\nOpt-out caching can simplify the implementation of client-side caching by reducing the need for explicit commands to enable caching for individual keys.\n\nTracking must be enabled using the OPTOUT option to enable opt-out caching:\n\n    CLIENT TRACKING ON OPTOUT\n\nIf you want to exclude a specific key from being tracked and cached, use the CLIENT UNTRACKING command:\n\n    CLIENT UNTRACKING key"},{"id":"broadcasting-mode","title":"Broadcasting mode","role":"content","text":"So far we described the first client-side caching model that Redis implements.\nThere is another one, called broadcasting, that sees the problem from the\npoint of view of a different tradeoff, does not consume any memory on the\nserver side, but instead sends more invalidation messages to clients.\nIn this mode we have the following main behaviors:\n\n* Clients enable client-side caching using the `BCAST` option, specifying one or more prefixes using the `PREFIX` option. For instance: `CLIENT TRACKING on REDIRECT 10 BCAST PREFIX object: PREFIX user:`. If no prefix is specified at all, the prefix is assumed to be the empty string, so the client will receive invalidation messages for every key that gets modified. Instead if one or more prefixes are used, only keys matching one of the specified prefixes will be sent in the invalidation messages.\n* The server does not store anything in the invalidation table. Instead it uses a different **Prefixes Table**, where each prefix is associated to a list of clients.\n* No two prefixes can track overlapping parts of the keyspace. For instance, having the prefix \"foo\" and \"foob\" would not be allowed, since they would both trigger an invalidation for the key \"foobar\". However, just using the prefix \"foo\" is sufficient.\n* Every time a key matching any of the prefixes is modified, all the clients subscribed to that prefix, will receive the invalidation message.\n* The server will consume CPU proportional to the number of registered prefixes. If you have just a few, it is hard to see any difference. With a big number of prefixes the CPU cost can become quite large.\n* In this mode the server can perform the optimization of creating a single reply for all the clients subscribed to a given prefix, and send the same reply to all. This helps to lower the CPU usage."},{"id":"the-noloop-option","title":"The NOLOOP option","role":"content","text":"By default client-side tracking will send invalidation messages to the\nclient that modified the key. Sometimes clients want this, since they\nimplement very basic logic that does not involve automatically caching\nwrites locally. However, more advanced clients may want to cache even the\nwrites they are doing in the local in-memory table. In such case receiving\nan invalidation message immediately after the write is a problem, since it\nwill force the client to evict the value it just cached.\n\nIn this case it is possible to use the `NOLOOP` option: it works both\nin normal and broadcasting mode. Using this option, clients are able to\ntell the server they don't want to receive invalidation messages for keys\nthat they modified.\n\nWith tracking in the default mode, the server removes the key from the\ninvalidation table when the key is modified. If the connection that modified\nthe key is using `NOLOOP`, Redis suppresses the invalidation message to that\nconnection, but the key is still no longer tracked for that connection after\nthe write. To receive future invalidations for the same key, the connection\nmust read the key again so that Redis can track it again."},{"id":"avoiding-race-conditions","title":"Avoiding race conditions","role":"content","text":"When implementing client-side caching redirecting the invalidation messages\nto a different connection, you should be aware that there is a possible\nrace condition. See the following example interaction, where we'll call\nthe data connection \"D\" and the invalidation connection \"I\":\n\n    [D] client -> server: GET foo\n    [I] server -> client: Invalidate foo (somebody else touched it)\n    [D] server -> client: \"bar\" (the reply of \"GET foo\")\n\nAs you can see, because the reply to the GET was slower to reach the\nclient, we received the invalidation message before the actual data that\nis already no longer valid. So we'll keep serving a stale version of the\nfoo key. To avoid this problem, it is a good idea to populate the cache\nwhen we send the command with a placeholder:\n\n    Client cache: set the local copy of \"foo\" to \"caching-in-progress\"\n    [D] client-> server: GET foo.\n    [I] server -> client: Invalidate foo (somebody else touched it)\n    Client cache: delete \"foo\" from the local cache.\n    [D] server -> client: \"bar\" (the reply of \"GET foo\")\n    Client cache: don't set \"bar\" since the entry for \"foo\" is missing.\n\nSuch a race condition is not possible when using a single connection for both\ndata and invalidation messages, since the order of the messages is always known\nin that case."},{"id":"what-to-do-when-losing-connection-with-the-server","title":"What to do when losing connection with the server","role":"content","text":"Similarly, if we lost the connection with the socket we use in order to\nget the invalidation messages, we may end with stale data. In order to avoid\nthis problem, we need to do the following things:\n\n1. Make sure that if the connection is lost, the local cache is flushed.\n2. Both when using RESP2 with Pub/Sub, or RESP3, ping the invalidation channel periodically (you can send PING commands even when the connection is in Pub/Sub mode!). If the connection looks broken and we are not able to receive ping backs, after a maximum amount of time, close the connection and flush the cache."},{"id":"what-to-cache","title":"What to cache","role":"content","text":"Clients may want to run internal statistics about the number of times\na given cached key was actually served in a request, to understand in the\nfuture what is good to cache. In general:\n\n* We don't want to cache many keys that change continuously.\n* We don't want to cache many keys that are requested very rarely.\n* We want to cache keys that are requested often and change at a reasonable rate. For an example of key not changing at a reasonable rate, think of a global counter that is continuously [`INCR`](https://redis.io/docs/latest/commands/incr)emented.\n\nHowever simpler clients may just evict data using some random sampling just\nremembering the last time a given cached value was served, trying to evict\nkeys that were not served recently."},{"id":"other-hints-for-implementing-client-libraries","title":"Other hints for implementing client libraries","role":"content","text":"* Handling TTLs: make sure you also request the key TTL and set the TTL in the local cache if you want to support caching keys with a TTL.\n* Putting a max TTL on every key is a good idea, even if it has no TTL. This protects against bugs or connection issues that would make the client have old data in the local copy.\n* Limiting the amount of memory used by clients is absolutely needed. There must be a way to evict old keys when new ones are added."},{"id":"limiting-the-amount-of-memory-used-by-redis","title":"Limiting the amount of memory used by Redis","role":"limits","text":"Be sure to configure a suitable value for the maximum number of keys remembered by Redis or alternatively use the BCAST mode that consumes no memory at all on the Redis side. Note that the memory consumed by Redis when BCAST is not used, is proportional both to the number of keys tracked and the number of clients requesting such keys."}],"examples":[{"id":"two-connections-mode-ex0","language":"plaintext","code":"(Connection 1 -- used for invalidations)\nCLIENT ID\n:4\nSUBSCRIBE __redis__:invalidate\n*3\n$9\nsubscribe\n$20\n__redis__:invalidate\n:1","section_id":"two-connections-mode"},{"id":"two-connections-mode-ex1","language":"plaintext","code":"(Connection 2 -- data connection)\nCLIENT TRACKING on REDIRECT 4\n+OK\n\nGET foo\n$3\nbar","section_id":"two-connections-mode"},{"id":"two-connections-mode-ex2","language":"plaintext","code":"(Some other unrelated connection)\nSET foo bar\n+OK","section_id":"two-connections-mode"},{"id":"two-connections-mode-ex3","language":"plaintext","code":"(Connection 1 -- used for invalidations)\n*3\n$7\nmessage\n$20\n__redis__:invalidate\n*1\n$3\nfoo","section_id":"two-connections-mode"}]}
{"schema_version":2,"id":"develop/reference/clients","title":"Redis client handling","url":"https://redis.io/docs/latest/develop/reference/clients/","summary":"How the Redis server manages client connections","aliases":["/reference/clients/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:49:27+01:00","page_type":"content","content_hash":"70877070c3637ce0f43a0e1384832363ecdea0607c86413b63f48091bbf7c82f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This document provides information about how Redis handles clients at the network layer level: connections, timeouts, buffers, and other similar topics are covered here.\n\nThe information contained in this document is **only applicable to Redis version 2.6 or greater**."},{"id":"accepting-client-connections","title":"Accepting Client Connections","role":"content","text":"Redis accepts clients connections on the configured TCP port and on the Unix socket if enabled. When a new client connection is accepted the following operations are performed:\n\n* The client socket is put in the non-blocking state since Redis uses multiplexing and non-blocking I/O.\n* The `TCP_NODELAY` option is set in order to ensure that there are no delays to the connection.\n* A *readable* file event is created so that Redis is able to collect the client queries as soon as new data is available to read on the socket.\n\nAfter the client is initialized, Redis checks if it is already at the limit\nconfigured for the number of simultaneous clients (configured using the `maxclients` configuration directive, see the next section of this document for further information).\n\nWhen Redis can't accept a new client connection because the maximum number of clients\nhas been reached, it tries to send an error to the client in order to\nmake it aware of this condition, closing the connection immediately.\nThe error message will reach the client even if the connection is\nclosed immediately by Redis because the new socket output buffer is usually\nbig enough to contain the error, so the kernel will handle transmission\nof the error."},{"id":"what-order-are-client-requests-served-in","title":"What Order are Client Requests Served In?","role":"content","text":"The order is determined by a combination of the client socket file descriptor\nnumber and order in which the kernel reports events, so the order should be \nconsidered as unspecified.\n\nHowever, Redis does the following two things when serving clients:\n\n* It only performs a single `read()` system call every time there is something new to read from the client socket. This ensures that if we have multiple clients connected, and a few send queries at a high rate, other clients are not penalized and will not experience latency issues.\n* However once new data is read from a client, all the queries contained in the current buffers are processed sequentially. This improves locality and does not need iterating a second time to see if there are clients that need some processing time."},{"id":"maximum-concurrent-connected-clients","title":"Maximum Concurrent Connected Clients","role":"content","text":"In Redis 2.4 there was a hard-coded limit for the maximum number of clients\nthat could be handled simultaneously.\n\nIn Redis 2.6 and newer, this limit is configurable using the `maxclients` directive in `redis.conf`. The default is 10,000 clients.\n\nHowever, Redis checks with the kernel what the maximum number of file\ndescriptors that we are able to open is (the *soft limit* is checked). If the\nlimit is less than the maximum number of clients we want to handle, plus\n32 (that is the number of file descriptors Redis reserves for internal uses),\nthen the maximum number of clients is updated to match the number\nof clients it is *really able to handle* under the current operating system\nlimit.\n\nWhen `maxclients` is set to a number greater than Redis can support, a message is logged at startup:\n\n[code example]\n\nWhen Redis is configured in order to handle a specific number of clients it\nis a good idea to make sure that the operating system limit for the maximum\nnumber of file descriptors per process is also set accordingly.\n\nUnder Linux these limits can be set both in the current session and as a\nsystem-wide setting with the following commands:\n\n* `ulimit -Sn 100000 # This will only work if hard limit is big enough.`\n* `sysctl -w fs.file-max=100000`"},{"id":"output-buffer-limits","title":"Output Buffer Limits","role":"returns","text":"Redis needs to handle a variable-length output buffer for every client, since\na command can produce a large amount of data that needs to be transferred to the\nclient.\n\nHowever it is possible that a client sends more commands producing more output\nto serve at a faster rate than that which Redis can send the existing output to the\nclient. This is especially true with Pub/Sub clients in case a client is not\nable to process new messages fast enough.\n\nBoth conditions will cause the client output buffer to grow and consume\nmore and more memory. For this reason by default Redis sets limits to the\noutput buffer size for different kind of clients. When the limit is reached\nthe client connection is closed and the event logged in the Redis log file.\n\nThere are two kind of limits Redis uses:\n\n* The **hard limit** is a fixed limit that when reached will make Redis close the client connection as soon as possible.\n* The **soft limit** instead is a limit that depends on the time, for instance a soft limit of 32 megabytes per 10 seconds means that if the client has an output buffer bigger than 32 megabytes for, continuously, 10 seconds, the connection gets closed.\n\nDifferent kind of clients have different default limits:\n\n* **Normal clients** have a default limit of 0, that means, no limit at all, because most normal clients use blocking implementations sending a single command and waiting for the reply to be completely read before sending the next command, so it is always not desirable to close the connection in case of a normal client.\n* **Pub/Sub clients** have a default hard limit of 32 megabytes and a soft limit of 8 megabytes per 60 seconds.\n* **Replicas** have a default hard limit of 256 megabytes and a soft limit of 64 megabyte per 60 seconds.\n\nIt is possible to change the limit at runtime using the [`CONFIG SET`](https://redis.io/docs/latest/commands/config-set) command or in a permanent way using the Redis configuration file `redis.conf`. See the example `redis.conf` in the Redis distribution for more information about how to set the limit."},{"id":"query-buffer-hard-limit","title":"Query Buffer Hard Limit","role":"content","text":"Every client is also subject to a query buffer limit. This is a non-configurable hard limit that will close the connection when the client query buffer (that is the buffer we use to accumulate commands from the client) reaches 1 GB, and is actually only an extreme limit to avoid a server crash in case of client or server software bugs."},{"id":"client-eviction","title":"Client Eviction","role":"content","text":"Redis is built to handle a very large number of client connections.\nClient connections tend to consume memory, and when there are many of them, the aggregate memory consumption can be extremely high, leading to data eviction or out-of-memory errors.\nThese cases can be mitigated to an extent using [output buffer limits](#output-buffer-limits), but Redis allows us a more robust configuration to limit the aggregate memory used by all clients' connections.\n\n\nThis mechanism is called **client eviction**, and it's essentially a safety mechanism that will disconnect clients once the aggregate memory usage of all clients is above a threshold.\nThe mechanism first attempts to disconnect clients that use the most memory.\nIt disconnects the minimal number of clients needed to return below the `maxmemory-clients` threshold.\n\n`maxmemory-clients` defines the maximum aggregate memory usage of all clients connected to Redis.\nThe aggregation takes into account all the memory used by the client connections: the [query buffer](#query-buffer-hard-limit), the output buffer, and other intermediate buffers.\n\nNote that replica and master connections aren't affected by the client eviction mechanism. Therefore, such connections are never evicted.\n\n`maxmemory-clients` can be set permanently in the configuration file (`redis.conf`) or via the [`CONFIG SET`](https://redis.io/docs/latest/commands/config-set) command.\nThis setting can either be 0 (meaning no limit), a size in bytes (possibly with `mb`/`gb` suffix),\nor a percentage of `maxmemory` by using the `%` suffix (e.g. setting it to `10%` would mean 10% of the `maxmemory` configuration).\n\nThe default setting is 0, meaning client eviction is turned off by default.\nHowever, for any large production deployment, it is highly recommended to configure some non-zero `maxmemory-clients` value.\nA value `5%`, for example, can be a good place to start.\n\nIt is possible to flag a specific client connection to be excluded from the client eviction mechanism.\nThis is useful for control path connections.\nIf, for example, you have an application that monitors the server via the [`INFO`](https://redis.io/docs/latest/commands/info) command and alerts you in case of a problem, you might want to make sure this connection isn't evicted.\nYou can do so using the following command (from the relevant client's connection):\n\n[`CLIENT NO-EVICT`](https://redis.io/docs/latest/commands/client-no-evict) `on`\n\nAnd you can revert that with:\n\n[`CLIENT NO-EVICT`](https://redis.io/docs/latest/commands/client-no-evict) `off`\n\nFor more information and an example refer to the `maxmemory-clients` section in the default `redis.conf` file.\n\nClient eviction is available from Redis 7.0."},{"id":"client-timeouts","title":"Client Timeouts","role":"content","text":"By default recent versions of Redis don't close the connection with the client\nif the client is idle for many seconds: the connection will remain open forever.\n\nHowever if you don't like this behavior, you can configure a timeout, so that\nif the client is idle for more than the specified number of seconds, the client connection will be closed.\n\nYou can configure this limit via `redis.conf` or simply using `CONFIG SET timeout <value>`.\n\nNote that the timeout only applies to normal clients and it **does not apply to Pub/Sub clients**, since a Pub/Sub connection is a *push style* connection so a client that is idle is the norm.\n\nEven if by default connections are not subject to timeout, there are two conditions when it makes sense to set a timeout:\n\n* Mission critical applications where a bug in the client software may saturate the Redis server with idle connections, causing service disruption.\n* As a debugging mechanism in order to be able to connect with the server if a bug in the client software saturates the server with idle connections, making it impossible to interact with the server.\n\nTimeouts are not to be considered very precise: Redis avoids setting timer events or running O(N) algorithms in order to check idle clients, so the check is performed incrementally from time to time. This means that it is possible that while the timeout is set to 10 seconds, the client connection will be closed, for instance, after 12 seconds if many clients are connected at the same time."},{"id":"the-client-command","title":"The CLIENT Command","role":"content","text":"The Redis [`CLIENT`](https://redis.io/docs/latest/commands/client) command allows you to inspect the state of every connected client, to kill a specific client, and to name connections. It is a very powerful debugging tool if you use Redis at scale.\n\n[`CLIENT LIST`](https://redis.io/docs/latest/commands/client-list) is used in order to obtain a list of connected clients and their state:\n\n[code example]\n\nIn the above example two clients are connected to the Redis server. Let's look at what some of the data returned represents:\n\n* **addr**: The client address, that is, the client IP and the remote port number it used to connect with the Redis server.\n* **fd**: The client socket file descriptor number.\n* **name**: The client name as set by [`CLIENT SETNAME`](https://redis.io/docs/latest/commands/client-setname).\n* **age**: The number of seconds the connection existed for.\n* **idle**: The number of seconds the connection is idle.\n* **flags**: The kind of client (N means normal client, check the [full list of flags](https://redis.io/docs/latest/commands/client-list)).\n* **omem**: The amount of memory used by the client for the output buffer.\n* **cmd**: The last executed command.\n\nSee the [`CLIENT LIST`](https://redis.io/docs/latest/commands/client-list) documentation for the full listing of fields and their purpose.\n\nOnce you have the list of clients, you can close a client's connection using the [`CLIENT KILL`](https://redis.io/docs/latest/commands/client-kill) command, specifying the client address as its argument.\n\nThe commands [`CLIENT SETNAME`](https://redis.io/docs/latest/commands/client-setname) and [`CLIENT GETNAME`](https://redis.io/docs/latest/commands/client-getname) can be used to set and get the connection name. Starting with Redis 4.0, the client name is shown in the\n[`SLOWLOG`](https://redis.io/docs/latest/commands/slowlog) output, to help identify clients that create latency issues."},{"id":"tcp-keepalive","title":"TCP keepalive","role":"content","text":"From version 3.2 onwards, Redis has TCP keepalive (`SO_KEEPALIVE` socket option) enabled by default and set to about 300 seconds. This option is useful in order to detect dead peers (clients that cannot be reached even if they look connected). Moreover, if there is network equipment between clients and servers that need to see some traffic in order to take the connection open, the option will prevent unexpected connection closed events."}],"examples":[{"id":"maximum-concurrent-connected-clients-ex0","language":"plaintext","code":"$ ./redis-server --maxclients 100000\n[41422] 23 Jan 11:28:33.179 # Unable to set the max number of files limit to 100032 (Invalid argument), setting the max clients configuration to 10112.","section_id":"maximum-concurrent-connected-clients"},{"id":"the-client-command-ex0","language":"plaintext","code":"redis 127.0.0.1:6379> client list\naddr=127.0.0.1:52555 fd=5 name= age=855 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=client\naddr=127.0.0.1:52787 fd=6 name= age=6 idle=5 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=ping","section_id":"the-client-command"}]}
{"schema_version":2,"id":"develop/reference/command-arguments","title":"Redis command arguments","url":"https://redis.io/docs/latest/develop/reference/command-arguments/","summary":"How Redis commands expose their documentation programmatically","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:49:27+01:00","page_type":"content","content_hash":"5bf1fdad31ec63f098f08db9884d073e1957d73c3f4a1ccbed15c568beeac5ba","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The [`COMMAND DOCS`](https://redis.io/docs/latest/commands/command-docs) command returns documentation-focused information about available Redis commands.\nThe map reply that the command returns includes the _arguments_ key.\nThis key stores an array that describes the command's arguments.\n\nEvery element in the _arguments_ array is a map with the following fields:\n\n* **name:** the argument's name, always present.\n  The name of an argument is given for identification purposes alone.\n  It isn't displayed during the command's syntax rendering.\n  The same name can appear more than once in the entire argument tree, but it is unique compared to other sibling arguments' names.\n  This allows obtaining a unique identifier for each argument (the concatenation of all names in the path from the root to any argument).\n* **display_text:** the argument's display string, present in arguments that have a displayable representation (all arguments that aren't oneof/block).\n  This is the string used in the command's syntax rendering.\n* **type:** the argument's type, always present.\n  An argument must have one of the following types:\n  - **string:** a string argument.\n  - **integer:** an integer argument.\n  - **double:** a double-precision argument.\n  - **key:** a string that represents the name of a key.\n  - **pattern:** a string that represents a glob-like pattern.\n  - **unix-time:** an integer that represents a Unix timestamp.\n  - **pure-token:** an argument is a token, meaning a reserved keyword, which may or may not be provided. \n    Not to be confused with free-text user input.\n  - **oneof**: the argument is a container for nested arguments.\n    This type enables choice among several nested arguments (see the [`XADD`](https://redis.io/docs/latest/commands/xadd) example below).\n  - **block:** the argument is a container for nested arguments.\n    This type enables grouping arguments and applying a property (such as _optional_) to all (see the [`XADD`](https://redis.io/docs/latest/commands/xadd) example below).\n* **key_spec_index:** this value is available for every argument of the _key_ type.\n  It is a 0-based index of the specification in the command's [key specifications](https://redis.io/docs/latest/develop/reference/key-specs) that corresponds to the argument.\n* **token**: a constant literal that precedes the argument (user input) itself.\n* **summary:** a short description of the argument.\n* **since:** the debut Redis version of the argument (or for module commands, the module version).\n* **deprecated_since:** the Redis version that deprecated the command (or for module commands, the module version).\n* **flags:** an array of argument flags.\n  Possible flags are:\n  - **optional**: denotes that the argument is optional (for example, the _GET_ clause of the  [`SET`](https://redis.io/docs/latest/commands/set) command).\n  - **multiple**: denotes that the argument may be repeated (such as the _key_ argument of [`DEL`](https://redis.io/docs/latest/commands/del)).\n  - **multiple-token:** denotes the possible repetition of the argument with its preceding token (see [`SORT`](https://redis.io/docs/latest/commands/sort)'s `GET pattern` clause).\n* **value:** the argument's value.\n  For arguments types other than _oneof_ and _block_, this is a string that describes the value in the command's syntax.\n  For the _oneof_ and _block_ types, this is an array of nested arguments, each being a map as described in this section."},{"id":"example","title":"Example","role":"example","text":"The trimming clause of [`XADD`](https://redis.io/docs/latest/commands/xadd), i.e., `[MAXLEN|MINID [=|~] threshold [LIMIT count]]`, is represented at the top-level as _block_-typed argument.\n\nIt consists of four nested arguments:\n\n1. **trimming strategy:** this nested argument has an _oneof_ type with two nested arguments.\n  Each of the nested arguments, _MAXLEN_ and _MINID_, is typed as _pure-token_.\n2. **trimming operator:** this nested argument is an optional _oneof_ type with two nested arguments.\n  Each of the nested arguments, _=_ and _~_, is a _pure-token_.\n3. **threshold:** this nested argument is a _string_.\n4. **count:** this nested argument is an optional _integer_ with a _token_ (_LIMIT_).\n\nHere's [`XADD`](https://redis.io/docs/latest/commands/xadd)'s arguments array:\n\n[code example]"}],"examples":[{"id":"example-ex0","language":"plaintext","code":"1) 1) \"name\"\n   2) \"key\"\n   3) \"type\"\n   4) \"key\"\n   5) \"value\"\n   6) \"key\"\n2)  1) \"name\"\n    2) \"nomkstream\"\n    3) \"type\"\n    4) \"pure-token\"\n    5) \"token\"\n    6) \"NOMKSTREAM\"\n    7) \"since\"\n    8) \"6.2\"\n    9) \"flags\"\n   10) 1) optional\n3) 1) \"name\"\n   2) \"trim\"\n   3) \"type\"\n   4) \"block\"\n   5) \"flags\"\n   6) 1) optional\n   7) \"value\"\n   8) 1) 1) \"name\"\n         2) \"strategy\"\n         3) \"type\"\n         4) \"oneof\"\n         5) \"value\"\n         6) 1) 1) \"name\"\n               2) \"maxlen\"\n               3) \"type\"\n               4) \"pure-token\"\n               5) \"token\"\n               6) \"MAXLEN\"\n            2) 1) \"name\"\n               2) \"minid\"\n               3) \"type\"\n               4) \"pure-token\"\n               5) \"token\"\n               6) \"MINID\"\n               7) \"since\"\n               8) \"6.2\"\n      2) 1) \"name\"\n         2) \"operator\"\n         3) \"type\"\n         4) \"oneof\"\n         5) \"flags\"\n         6) 1) optional\n         7) \"value\"\n         8) 1) 1) \"name\"\n               2) \"equal\"\n               3) \"type\"\n               4) \"pure-token\"\n               5) \"token\"\n               6) \"=\"\n            2) 1) \"name\"\n               2) \"approximately\"\n               3) \"type\"\n               4) \"pure-token\"\n               5) \"token\"\n               6) \"~\"\n      3) 1) \"name\"\n         2) \"threshold\"\n         3) \"type\"\n         4) \"string\"\n         5) \"value\"\n         6) \"threshold\"\n      4)  1) \"name\"\n          2) \"count\"\n          3) \"type\"\n          4) \"integer\"\n          5) \"token\"\n          6) \"LIMIT\"\n          7) \"since\"\n          8) \"6.2\"\n          9) \"flags\"\n         10) 1) optional\n         11) \"value\"\n         12) \"count\"\n4) 1) \"name\"\n   2) \"id_or_auto\"\n   3) \"type\"\n   4) \"oneof\"\n   5) \"value\"\n   6) 1) 1) \"name\"\n         2) \"auto_id\"\n         3) \"type\"\n         4) \"pure-token\"\n         5) \"token\"\n         6) \"*\"\n      2) 1) \"name\"\n         2) \"id\"\n         3) \"type\"\n         4) \"string\"\n         5) \"value\"\n         6) \"id\"\n5) 1) \"name\"\n   2) \"field_value\"\n   3) \"type\"\n   4) \"block\"\n   5) \"flags\"\n   6) 1) multiple\n   7) \"value\"\n   8) 1) 1) \"name\"\n         2) \"field\"\n         3) \"type\"\n         4) \"string\"\n         5) \"value\"\n         6) \"field\"\n      2) 1) \"name\"\n         2) \"value\"\n         3) \"type\"\n         4) \"string\"\n         5) \"value\"\n         6) \"value\"","section_id":"example"}]}
{"schema_version":2,"id":"develop/reference/command-tips","title":"Redis command tips","url":"https://redis.io/docs/latest/develop/reference/command-tips/","summary":"Get additional information about a command","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:49:27+01:00","page_type":"content","content_hash":"c3cc9a58318efc68ac295d128024886ce3b3d9245e974020596f4b8acc0d0eac","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Command tips are an array of strings.\nThese provide Redis clients with additional information about the command.\nThe information can instruct Redis Cluster clients as to how the command should be executed and its output processed in a clustered deployment.\n\nUnlike the command's flags (see the 3rd element of [`COMMAND`](https://redis.io/docs/latest/commands/command)'s reply), which are strictly internal to the server's operation, tips don't serve any purpose other than being reported to clients.\n\nCommand tips are arbitrary strings.\nHowever, the following sections describe proposed tips and demonstrate the conventions they are likely to adhere to."},{"id":"nondeterministic_output","title":"nondeterministic_output","role":"content","text":"This tip indicates that the command's output isn't deterministic.\nThat means that calls to the command may yield different results with the same arguments and data.\nThat difference could be the result of the command's random nature (e.g., [`RANDOMKEY`](https://redis.io/docs/latest/commands/randomkey) and [`SPOP`](https://redis.io/docs/latest/commands/spop)); the call's timing (e.g., [`TTL`](https://redis.io/docs/latest/commands/ttl)); or generic differences that relate to the server's state (e.g., [`INFO`](https://redis.io/docs/latest/commands/info) and [`CLIENT LIST`](https://redis.io/docs/latest/commands/client-list)).\n\n**Note:**\nPrior to Redis 7.0, this tip was the _random_ command flag."},{"id":"nondeterministic_output_order","title":"nondeterministic_output_order","role":"content","text":"The existence of this tip indicates that the command's output is deterministic, but its ordering is random (e.g., [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) and [`SMEMBERS`](https://redis.io/docs/latest/commands/smembers)).\n\n**Note:**\nPrior to Redis 7.0, this tip was the _sort_\\__for_\\__script_ flag."},{"id":"request_policy","title":"request_policy","role":"content","text":"This tip can help clients determine the shards to send the command in clustering mode.\nThe default behavior a client should implement for commands without the _request_policy_ tip is as follows:\n\n1. The command doesn't accept key name arguments: the client can execute the command on an arbitrary shard.\n1. For commands that accept one or more key name arguments: the client should route the command to a single shard, as determined by the hash slot of the input keys.\n\nIn cases where the client should adopt a behavior different than the default, the _request_policy_ tip can be one of:\n\n- **all_nodes:** the client should execute the command on all nodes - masters and replicas alike.\n  An example is the [`CONFIG SET`](https://redis.io/docs/latest/commands/config-set) command. \n  This tip is in-use by commands that don't accept key name arguments.\n  The command operates atomically per shard.\n* **all_shards:** the client should execute the command on all master shards (e.g., the [`DBSIZE`](https://redis.io/docs/latest/commands/dbsize) command).\n  This tip is in-use by commands that don't accept key name arguments.\n  The command operates atomically per shard.\n- **multi_shard:** the client should execute the command on several shards.\n  The client should split the inputs according to the hash slots of its input key name arguments.\n  For example, the command `DEL {foo} {foo}1 bar` should be split to `DEL {foo} {foo}1` and `DEL bar`.\n  If the keys are hashed to more than a single slot, the command must be split even if all the slots are managed by the same shard.\n  Examples for such commands include [`MSET`](https://redis.io/docs/latest/commands/mset), [`MGET`](https://redis.io/docs/latest/commands/mget) and [`DEL`](https://redis.io/docs/latest/commands/del).\n  However, note that [`SUNIONSTORE`](https://redis.io/docs/latest/commands/sunionstore) isn't considered as _multi_shard_ because all of its keys must belong to the same hash slot.\n- **special:** indicates a non-trivial form of the client's request policy, such as the [`SCAN`](https://redis.io/docs/latest/commands/scan) command."},{"id":"response_policy","title":"response_policy","role":"returns","text":"This tip can help clients determine the aggregate they need to compute from the replies of multiple shards in a cluster.\nThe default behavior for commands without a _request_policy_ tip only applies to replies with of nested types (i.e., an array, a set, or a map).\nThe client's implementation for the default behavior should be as follows:\n\n1. The command doesn't accept key name arguments: the client can aggregate all replies within a single nested data structure.\nFor example, the array replies we get from calling [`KEYS`](https://redis.io/docs/latest/commands/keys) against all shards.\nThese should be packed in a single in no particular order.\n1. For commands that accept one or more key name arguments: the client needs to retain the same order of replies as the input key names.\nFor example, [`MGET`](https://redis.io/docs/latest/commands/mget)'s aggregated reply.\n\nThe _response_policy_ tip is set for commands that reply with scalar data types, or when it's expected that clients implement a non-default aggregate.\nThis tip can be one of:\n\n* **one_succeeded:** the clients should return success if at least one shard didn't reply with an error.\n  The client should reply with the first non-error reply it obtains.\n  If all shards return an error, the client can reply with any one of these.\n  For example, consider a [`SCRIPT KILL`](https://redis.io/docs/latest/commands/script-kill) command that's sent to all shards.\n  Although the script should be loaded in all of the cluster's shards, the [`SCRIPT KILL`](https://redis.io/docs/latest/commands/script-kill) will typically run only on one at a given time.\n* **all_succeeded:** the client should return successfully only if there are no error replies.\n  Even a single error reply should disqualify the aggregate and be returned.\n  Otherwise, the client should return one of the non-error replies.\n  As an example, consider the [`CONFIG SET`](https://redis.io/docs/latest/commands/config-set), [`SCRIPT FLUSH`](https://redis.io/docs/latest/commands/script-flush) and [`SCRIPT LOAD`](https://redis.io/docs/latest/commands/script-load) commands.\n* **agg_logical_and:** the client should return the result of a logical _AND_ operation on all replies (only applies to integer replies, usually from commands that return either _0_ or _1_).\n  Consider the [`SCRIPT EXISTS`](https://redis.io/docs/latest/commands/script-exists) command as an example.\n  It returns an array of _0_'s and _1_'s that denote the existence of its given SHA1 sums in the script cache.\n  The aggregated response should be _1_ only when all shards had reported that a given script SHA1 sum is in their respective cache.\n* **agg_logical_or:** the client should return the result of a logical _OR_ operation on all replies (only applies to integer replies, usually from commands that return either _0_ or _1_).\n* **agg_min:** the client should return the minimal value from the replies (only applies to numerical replies).\n  The aggregate reply from a cluster-wide [`WAIT`](https://redis.io/docs/latest/commands/wait) command, for example, should be the minimal value (number of synchronized replicas) from all shards.\n* **agg_max:** the client should return the maximal value from the replies (only applies to numerical replies).\n* **agg_sum:** the client should return the sum of replies (only applies to numerical replies).\n  Example: [`DBSIZE`](https://redis.io/docs/latest/commands/dbsize).\n* **special:** this type of tip indicates a non-trivial form of reply policy.\n  [`INFO`](https://redis.io/docs/latest/commands/info) is an excellent example of that."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"example-ex0","language":"plaintext","code":"redis> command info ping\n1)  1) \"ping\"\n    2) (integer) -1\n    3) 1) fast\n    4) (integer) 0\n    5) (integer) 0\n    6) (integer) 0\n    7) 1) @fast\n       2) @connection\n    8) 1) \"request_policy:all_shards\"\n       2) \"response_policy:all_succeeded\"\n    9) (empty array)\n   10) (empty array)","section_id":"example"}]}
{"schema_version":2,"id":"develop/reference/eviction/index","title":"Key eviction","url":"https://redis.io/docs/latest/develop/reference/eviction/","summary":"Overview of Redis key eviction policies (LRU, LFU, etc.)","aliases":["/manual/eviction/","/reference/eviction/","/develop/reference/eviction/index/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:49:27+01:00","page_type":"content","content_hash":"0c3389beebdc352bc656cd445867bb3b5ad56fa83bde85f2c3386afb357f776d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis is commonly used as a cache to speed up read accesses to a slower server\nor database. Since cache entries are copies of persistently-stored data, it\nis usually safe to evict them when the cache runs out of memory (they can be\ncached again in the future if necessary).\n\nRedis lets you specify an eviction policy to evict keys automatically\nwhen the size of the cache exceeds a set memory limit. Whenever a client\nruns a new command that adds more data to the cache, Redis checks the memory usage.\nIf it is greater than the limit, Redis evicts keys according to the chosen\neviction policy until the total memory used is back below the limit.\n\nNote that when a command adds a lot of data to the cache (for example, a big set\nintersection stored into a new key), this might temporarily exceed the limit by\na large amount.\n\nThe sections below explain how to [configure the memory limit](#maxmem) for the cache\nand also describe the available [eviction policies](#eviction-policies) and when to\nuse them."},{"id":"maxmem","title":"Using the `maxmemory` configuration directive","role":"content","text":"The `maxmemory` configuration directive specifies\nthe maximum amount of memory to use for the cache data. You can\nset `maxmemory` with the [`redis.conf`](https://github.com/redis/redis/blob/7.4.0/redis.conf)\nfile at startup time. For example, to configure a memory limit of 100 megabytes,\nyou can use the following directive inside `redis.conf`:\n\n[code example]\n\nYou can also use [`CONFIG SET`](https://redis.io/docs/latest/commands/config-set) to\nset `maxmemory` at runtime using [`redis-cli`](https://redis.io/docs/latest/develop/tools/cli):\n\n[code example]\n\nSet `maxmemory` to zero to specify that you don't want to limit the memory\nfor the dataset. This is the default behavior for 64-bit systems, while 32-bit\nsystems use an implicit memory limit of 3GB.\n\nWhen the size of your cache exceeds the limit set by `maxmemory`, Redis will\nenforce your chosen [eviction policy](#eviction-policies) to prevent any\nfurther growth of the cache."},{"id":"setting-maxmemory-for-a-replicated-or-persisted-instance","title":"Setting `maxmemory` for a replicated or persisted instance","role":"configuration","text":"If you are using\n[replication](https://redis.io/docs/latest/operate/rs/databases/durability-ha/replication)\nor [persistence](https://redis.io/docs/latest/operate/rs/databases/configure/database-persistence)\nfor a server, Redis will use some RAM as a buffer to store the set of updates waiting\nto be written to the replicas or AOF files.\nThe memory used by this buffer is not included in the total that\nis compared to `maxmemory` to see if eviction is required.\n\nThis is because the key evictions themselves generate updates that must be added\nto the buffer. If the updates were counted among the used\nmemory then in some circumstances, the memory saved by\nevicting keys would be immediately used up by the update data added to the buffer.\nThis, in turn, would trigger even more evictions and the resulting feedback loop\ncould evict many items from the cache unnecessarily.\n\nIf you are using replication or persistence, we recommend that you set\n`maxmemory` to leave a little RAM free to store the buffers. Note that this is not\nnecessary for the `noeviction` policy (see [the section below](#eviction-policies)\nfor more information about eviction policies).\n\nThe [`INFO`](https://redis.io/docs/latest/commands/info) command returns a\n`mem_not_counted_for_evict` value in the `memory` section (you can use\nthe `INFO memory` option to see just this section). This is the amount of\nmemory currently used by the buffers. Although the exact amount will vary,\nyou can use it to estimate how much to subtract from the total available RAM\nbefore setting `maxmemory`."},{"id":"eviction-policies","title":"Eviction policies","role":"content","text":"Use the `maxmemory-policy` configuration directive to select the eviction\npolicy you want to use when the limit set by `maxmemory` is reached.\n\nThe following policies are available:\n\n-   `noeviction`: Keys are not evicted but the server will return an error\n    when you try to execute commands that cache new data. If your database uses replication\n    then this condition only applies to the primary database. Note that commands that only\n    read existing data still work as normal.\n-   `allkeys-lru`: Evict the [least recently used](#apx-lru) (LRU) keys.\n-   `allkeys-lrm`: Evict the [least recently modified](#lrm-eviction) (LRM) keys.\n-   `allkeys-lfu`: Evict the [least frequently used](#lfu-eviction) (LFU) keys.\n-   `allkeys-random`: Evict keys at random.\n-   `volatile-lru`: Evict the least recently used keys that have an associated expiration (TTL).\n-   `volatile-lrm`: Evict the least recently modified keys that have an associated expiration (TTL).\n-   `volatile-lfu`: Evict the least frequently used keys that have an associated expiration (TTL).\n-   `volatile-random`: Evicts random keys that have an associated expiration (TTL).\n-   `volatile-ttl`: Evict keys with an associated expiration (TTL) that have the shortest remaining TTL value.\n\nThe `volatile-xxx` policies behave like `noeviction` if no keys have an associated expiration.\n\nYou should choose an eviction policy that fits the way your app\naccesses keys. You may be able to predict the access pattern in advance\nbut you can also use information from the `INFO` command at runtime to\ncheck or improve your choice of policy (see\n[Using the `INFO` command](#using-the-info-command) below for more information).\n\nAs a rule of thumb:\n\n-   Use `allkeys-lru` when you expect that a subset of elements will be accessed far\n    more often than the rest. This is a very common case according to the\n    [Pareto principle](https://en.wikipedia.org/wiki/Pareto_principle), so\n    `allkeys-lru` is a good default option if you have no reason to prefer any others.\n-   Use `allkeys-lrm` when you want to preserve frequently read data but evict data\n    that hasn't been modified recently. This is useful for read-heavy workloads where\n    you want to distinguish between data that's actively being updated versus data\n    that's only being read.\n-   Use `allkeys-random` when you expect all keys to be accessed with roughly equal\n    frequency. An example of this is when your app reads data items in a repeating cycle.\n-   Use `volatile-ttl` if your code can estimate which keys are good candidates for eviction\n    and assign short TTLs to them. Note also that if you make good use of\n    key expiration, then you are less likely to run into the cache memory limit because keys\n    will often expire before they need to be evicted.\n\nThe `volatile-lru`, `volatile-lrm`, and `volatile-random` policies are mainly useful when you want to use\na single Redis instance for both caching and for a set of persistent keys. However,\nyou should consider running two separate Redis instances in a case like this, if possible.\n\nAlso note that setting an `expire` value for a key costs memory, so a\npolicy like `allkeys-lru` is more memory efficient since it doesn't need an\n`expire` value to operate."},{"id":"using-the-info-command","title":"Using the `INFO` command","role":"content","text":"The [`INFO`](https://redis.io/docs/latest/commands/info) command provides several pieces\nof data that are useful for checking the performance of your cache. In particular,\nthe `INFO stats` section includes two important entries, `keyspace_hits` (the number of\ntimes keys were successfully found in the cache) and `keyspace_misses` (the number\nof times a key was requested but was not in the cache). The calculation below gives\nthe percentage of attempted accesses that were satisfied from the cache:\n\n[code example]\n\nCheck that this is roughly equal to what you would expect for your app\n(naturally, a higher percentage indicates better cache performance).\n\n> [!NOTE]\n>  When the [`EXISTS`](https://redis.io/docs/latest/commands/exists)\n> command reports that a key is absent then this is counted as a keyspace miss.\n\nIf the percentage of hits is lower than expected, then this might\nmean you are not using the best eviction policy. For example, if\nyou believe that a small subset of \"hot\" data (that will easily fit into the\ncache) should account for about 75% of accesses, you could reasonably\nexpect the percentage of keyspace hits to be around 75%. If the actual\npercentage is lower, check the value of `evicted_keys` (also returned by\n`INFO stats`). A high proportion of evictions would suggest that the\nwrong keys are being evicted too often by your chosen policy\n(so `allkeys-lru` might be a good option here). If the\nvalue of `evicted_keys` is low and you are using key expiration, check\n`expired_keys` to see how many keys have expired. If this number is high,\nyou might be using a TTL that is too low or you are choosing the wrong\nkeys to expire and this is causing keys to disappear from the cache\nbefore they should.\n\nOther useful pieces of information returned by `INFO` include:\n\n-   `used_memory_dataset`: (`memory` section) The amount of memory used for\n    cached data. If this is greater than `maxmemory`, then the difference\n    is the amount by which `maxmemory` has been exceeded.\n-   `current_eviction_exceeded_time`: (`stats` section) The time since\n    the cache last started to exceed `maxmemory`.\n-   `commandstats` section: Among other things, this reports the number of\n    times each command issued to the server has been rejected. If you are\n    using `noeviction` or one of the `volatile_xxx` policies, you can use\n    this to find which commands are being stopped by the `maxmemory` limit\n    and how often it is happening."},{"id":"apx-lru","title":"Approximated LRU algorithm","role":"content","text":"The Redis LRU algorithm uses an approximation of the least recently used\nkeys rather than calculating them exactly. It samples a small number of keys\nat random and then evicts the ones with the longest time since last access.\n\nFrom Redis 3.0 onwards, the algorithm also tracks a pool of good\ncandidates for eviction. This improves the performance of the algorithm, making\nit a close approximation to a true LRU algorithm.\n\n(Redis Open Source only) You can tune the performance of the algorithm by changing the number of samples to check\nbefore every eviction with the `maxmemory-samples` configuration directive:\n\n[code example]\n\nSee these pages for information on eviction policies in: (1) [Redis Software](https://redis.io/docs/latest/operate/rs/databases/memory-performance/eviction-policy) and (2) [Redis Cloud](https://redis.io/docs/latest/operate/rc/databases/configuration/data-eviction-policies).\n\nThe reason Redis does not use a true LRU implementation is because it\ncosts more memory. However, the approximation is virtually equivalent for an\napplication using Redis. This figure compares\nthe LRU approximation used by Redis with true LRU.\n\n![LRU comparison](lru_comparison.png)\n\nThe test to generate the above graphs filled a Redis server with a given number of keys. The keys were accessed from the first to the last. The first keys are the best candidates for eviction using an LRU algorithm. Later more 50% of keys are added, in order to force half of the old keys to be evicted.\n\nYou can see three kind of dots in the graphs, forming three distinct bands.\n\n* The light gray band are objects that were evicted.\n* The gray band are objects that were not evicted.\n* The green band are objects that were added.\n\nIn a theoretical LRU implementation we expect that, among the old keys, the first half will be expired. The Redis LRU algorithm will instead only *probabilistically* expire the older keys.\n\nAs you can see Redis 3.0 does a better job with 5 samples compared to Redis 2.8, however most objects that are among the latest accessed are still retained by Redis 2.8. Using a sample size of 10 in Redis 3.0 the approximation is very close to the theoretical performance of Redis 3.0.\n\nNote that LRU is just a model to predict how likely a given key will be accessed in the future. Moreover, if your data access pattern closely\nresembles the power law, most of the accesses will be in the set of keys\nthe LRU approximated algorithm can handle well.\n\nIn simulations we found that using a power law access pattern, the difference between true LRU and Redis approximation were minimal or non-existent.\n\nHowever you can raise the sample size to 10 at the cost of some additional CPU\nusage to closely approximate true LRU, and check if this makes a\ndifference in your cache misses rate.\n\nTo experiment in production with different values for the sample size by using\nthe `CONFIG SET maxmemory-samples <count>` command, is very simple."},{"id":"lfu-eviction","title":"LFU eviction","role":"content","text":"Starting with Redis 4.0, the [Least Frequently Used eviction mode](http://antirez.com/news/109) is available. This mode may work better (provide a better\nhits/misses ratio) in certain cases. In LFU mode, Redis will try to track\nthe frequency of access of items, so the ones used rarely are evicted. This means\nthe keys used often have a higher chance of remaining in memory.\n\nTo configure the LFU mode, the following policies are available:\n\n* `volatile-lfu` Evict using approximated LFU among the keys with an expire set.\n* `allkeys-lfu` Evict any key using approximated LFU.\n\nLFU is approximated like LRU: it uses a probabilistic counter, called a [Morris counter](https://en.wikipedia.org/wiki/Approximate_counting_algorithm) to estimate the object access frequency using just a few bits per object, combined with a decay period so that the counter is reduced over time. At some point we no longer want to consider keys as frequently accessed, even if they were in the past, so that the algorithm can adapt to a shift in the access pattern.\n\nThat information is sampled similarly to what happens for LRU (as explained in the previous section of this documentation) to select a candidate for eviction.\n\nHowever unlike LRU, LFU has certain tunable parameters: for example, how fast\nshould a frequent item lower in rank if it gets no longer accessed? It is also possible to tune the Morris counters range to better adapt the algorithm to specific use cases.\n\nBy default Redis is configured to:\n\n* Saturate the counter at, around, one million requests.\n* Decay the counter every one minute.\n\nThose should be reasonable values and were tested experimentally, but the user may want to play with these configuration settings to pick optimal values.\n\nInstructions about how to tune these parameters can be found inside the example `redis.conf` file in the source distribution. Briefly, they are:\n\n[code example]\n\nThe decay time is the obvious one, it is the amount of minutes a counter should be decayed, when sampled and found to be older than that value. A special value of `0` means: we will never decay the counter.\n\nThe counter *logarithm factor* changes how many hits are needed to saturate the frequency counter, which is just in the range 0-255. The higher the factor, the more accesses are needed to reach the maximum. The lower the factor, the better is the resolution of the counter for low accesses, according to the following table:\n\n[code example]\n\nSo basically the factor is a trade off between better distinguishing items with low accesses VS distinguishing items with high accesses. More information is available in the example `redis.conf` file."},{"id":"lrm-eviction","title":"LRM eviction","role":"content","text":"Starting with Redis 8.6, the Least Recently Modified (LRM) eviction policy is available. LRM is similar to LRU but only updates the timestamp on write operations, not read operations. This makes it useful for evicting keys that haven't been modified recently, regardless of how frequently they are read.\n\nThe key difference between LRU and LRM is:\n\n- **LRU (Least Recently Used)**: Updates the access timestamp on both read and write operations\n- **LRM (Least Recently Modified)**: Updates the access timestamp only on write operations\n\nThis distinction makes LRM particularly useful in scenarios where:\n\n- Your application has a clear distinction between read-heavy and write-heavy workloads\n- You want to evict stale data that hasn't been updated, regardless of read activity\n\nTo configure LRM eviction, the following policies are available:\n\n* `volatile-lrm` Evict using LRM among the keys with an associated expiration (TTL).\n* `allkeys-lrm` Evict any key using LRM.\n\nLike LRU, LRM uses an approximation algorithm that samples a small number of keys at random and evicts the ones with the longest time since last modification. The same `maxmemory-samples` configuration directive that affects LRU performance also applies to LRM."}],"examples":[{"id":"maxmem-ex0","language":"plaintext","code":"maxmemory 100mb","section_id":"maxmem"},{"id":"maxmem-ex1","language":"bash","code":"> CONFIG SET maxmemory 100mb","section_id":"maxmem"},{"id":"using-the-info-command-ex0","language":"plaintext","code":"keyspace_hits / (keyspace_hits + keyspace_misses) * 100","section_id":"using-the-info-command"},{"id":"apx-lru-ex0","language":"plaintext","code":"maxmemory-samples 5","section_id":"apx-lru"},{"id":"lfu-eviction-ex0","language":"plaintext","code":"lfu-log-factor 10\nlfu-decay-time 1","section_id":"lfu-eviction"},{"id":"lfu-eviction-ex1","language":"plaintext","code":"+--------+------------+------------+------------+------------+------------+\n| factor | 100 hits   | 1000 hits  | 100K hits  | 1M hits    | 10M hits   |\n+--------+------------+------------+------------+------------+------------+\n| 0      | 104        | 255        | 255        | 255        | 255        |\n+--------+------------+------------+------------+------------+------------+\n| 1      | 18         | 49         | 255        | 255        | 255        |\n+--------+------------+------------+------------+------------+------------+\n| 10     | 10         | 18         | 142        | 255        | 255        |\n+--------+------------+------------+------------+------------+------------+\n| 100    | 8          | 11         | 49         | 143        | 255        |\n+--------+------------+------------+------------+------------+------------+","section_id":"lfu-eviction"}]}
{"schema_version":2,"id":"develop/reference/gopher","title":"Redis and the Gopher protocol","url":"https://redis.io/docs/latest/develop/reference/gopher/","summary":"The Redis Gopher protocol implementation","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2024-01-19T18:04:55+01:00","page_type":"content","content_hash":"3f03142c5b62888f1c2ab9af5644e68f149efdec4b43e474cbe7307bcebb34ee","sections":[{"id":"overview","title":"Overview","role":"overview","text":"** Note: Support for Gopher was removed in Redis 7.0 **\n\nRedis contains an implementation of the Gopher protocol, as specified in\nthe [RFC 1436](https://www.ietf.org/rfc/rfc1436.txt).\n\nThe Gopher protocol was very popular in the late '90s. It is an alternative\nto the web, and the implementation both server and client side is so simple\nthat the Redis server has just 100 lines of code in order to implement this\nsupport.\n\nWhat do you do with Gopher nowadays? Well Gopher never *really* died, and\nlately there is a movement in order for the Gopher more hierarchical content\ncomposed of just plain text documents to be resurrected. Some want a simpler\ninternet, others believe that the mainstream internet became too much\ncontrolled, and it's cool to create an alternative space for people that\nwant a bit of fresh air.\n\nAnyway, for the 10th birthday of the Redis, we gave it the Gopher protocol\nas a gift."},{"id":"how-it-works","title":"How it works","role":"content","text":"The Redis Gopher support uses the inline protocol of Redis, and specifically\ntwo kind of inline requests that were anyway illegal: an empty request\nor any request that starts with \"/\" (there are no Redis commands starting\nwith such a slash). Normal RESP2/RESP3 requests are completely out of the\npath of the Gopher protocol implementation and are served as usually as well.\n\nIf you open a connection to Redis when Gopher is enabled and send it\na string like \"/foo\", if there is a key named \"/foo\" it is served via the\nGopher protocol.\n\nIn order to create a real Gopher \"hole\" (the name of a Gopher site in Gopher\ntalking), you likely need a script such as the one in [https://github.com/antirez/gopher2redis](https://github.com/antirez/gopher2redis)."},{"id":"security-warning","title":"SECURITY WARNING","role":"security","text":"If you plan to put Redis on the internet in a publicly accessible address\nto server Gopher pages **make sure to set a password** to the instance.\nOnce a password is set:\n\n1. The Gopher server (when enabled, not by default) will kill serve content via Gopher.\n2. However other commands cannot be called before the client will authenticate.\n\nSo use the `requirepass` option to protect your instance.\n\nTo enable Gopher support use the following configuration line.\n\n    gopher-enabled yes\n\nAccessing keys that are not strings or do not exit will generate\nan error in Gopher protocol format."}],"examples":[]}
{"schema_version":2,"id":"develop/reference","title":"Redis reference","url":"https://redis.io/docs/latest/develop/reference/","summary":"Specifications and protocols","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2024-01-19T18:04:55+01:00","children":[{"id":"develop/reference/command-tips","summary":"Get additional information about a command","title":"Redis command tips","url":"https://redis.io/docs/latest/develop/reference/command-tips/"},{"id":"develop/reference/client-side-caching","summary":"Server-assisted, client-side caching in Redis","title":"Client-side caching reference","url":"https://redis.io/docs/latest/develop/reference/client-side-caching/"},{"id":"develop/reference/modules","summary":"Introduction to writing Redis modules","title":"Redis modules API","url":"https://redis.io/docs/latest/develop/reference/modules/"},{"id":"develop/reference/sentinel-clients","summary":"How to build clients for Redis Sentinel","title":"Sentinel client spec","url":"https://redis.io/docs/latest/develop/reference/sentinel-clients/"},{"id":"develop/reference/key-specs","summary":"What are command key specification and how to use them in your client","title":"Command key specifications","url":"https://redis.io/docs/latest/develop/reference/key-specs/"},{"id":"develop/reference/protocol-spec","summary":"Redis serialization protocol (RESP) is the wire protocol that clients implement","title":"Redis serialization protocol specification","url":"https://redis.io/docs/latest/develop/reference/protocol-spec/"},{"id":"develop/reference/clients","summary":"How the Redis server manages client connections","title":"Redis client handling","url":"https://redis.io/docs/latest/develop/reference/clients/"},{"id":"develop/reference/eviction/index","summary":"Overview of Redis key eviction policies (LRU, LFU, etc.)","title":"Key eviction","url":"https://redis.io/docs/latest/develop/reference/eviction/"},{"id":"develop/reference/command-arguments","summary":"How Redis commands expose their documentation programmatically","title":"Redis command arguments","url":"https://redis.io/docs/latest/develop/reference/command-arguments/"},{"id":"develop/reference/gopher","summary":"The Redis Gopher protocol implementation","title":"Redis and the Gopher protocol","url":"https://redis.io/docs/latest/develop/reference/gopher/"}],"page_type":"index","sections":[],"examples":[]}
{"schema_version":2,"id":"develop/reference/key-specs","title":"Command key specifications","url":"https://redis.io/docs/latest/develop/reference/key-specs/","summary":"What are command key specification and how to use them in your client","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:49:27+01:00","page_type":"content","content_hash":"641e5c19db8f37153728c20ea5075cdbc9bd9a756dcb5d55ccd7fbea45c52038","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Many of the commands in Redis accept key names as input arguments.\nThe 9th element in the reply of [`COMMAND`](https://redis.io/docs/latest/commands/command) (and [`COMMAND INFO`](https://redis.io/docs/latest/commands/command-info)) is an array that consists of the command's key specifications.\n\nA _key specification_ describes a rule for extracting the names of one or more keys from the arguments of a given command.\nKey specifications provide a robust and flexible mechanism, compared to the _first key_, _last key_ and _step_ scheme employed until Redis 7.0.\nBefore introducing these specifications, Redis clients had no trivial programmatic means to extract key names for all commands.\n\nCluster-aware Redis clients had to have the keys' extraction logic hard-coded in the cases of commands such as [`EVAL`](https://redis.io/docs/latest/commands/eval) and [`ZUNIONSTORE`](https://redis.io/docs/latest/commands/zunionstore) that rely on a _numkeys_ argument or [`SORT`](https://redis.io/docs/latest/commands/sort) and its many clauses.\nAlternatively, the [`COMMAND GETKEYS`](https://redis.io/docs/latest/commands/command-getkeys) can be used to achieve a similar extraction effect but at a higher latency.\n\nA Redis client isn't obligated to support key specifications.\nIt can continue using the legacy _first key_, _last key_ and _step_ scheme along with the [_movablekeys_ flag](https://redis.io/docs/latest/commands/command#flags) that remain unchanged.\n\nHowever, a Redis client that implements key specifications support can consolidate most of its keys' extraction logic.\nEven if the client encounters an unfamiliar type of key specification, it can always revert to the [`COMMAND GETKEYS`](https://redis.io/docs/latest/commands/command-getkeys) command.\n\nThat said, most cluster-aware clients only require a single key name to perform correct command routing, so it is possible that although a command features one unfamiliar specification, its other specification may still be usable by the client.\n\nKey specifications are maps with the following keys:\n\n1. **begin_search:**: the starting index for keys' extraction.\n2. **find_keys:** the rule for identifying the keys relative to the BS.\n3. **notes**: notes about this key spec, if there are any.\n4. **flags**: indicate the type of data access."},{"id":"begin_search","title":"begin_search","role":"content","text":"The _begin\\_search_ value of a specification informs the client of the extraction's beginning.\nThe value is a map.\nThere are three types of `begin_search`:\n\n1. **index:** key name arguments begin at a constant index.\n2. **keyword:** key names start after a specific keyword (token).\n3. **unknown:** an unknown type of specification - see the [incomplete flag section](#incomplete) for more details."},{"id":"index","title":"index","role":"content","text":"The _index_ type of `begin_search` indicates that input keys appear at a constant index.\nIt is a map under the _spec_ key with a single key:\n\n1. **index:** the 0-based index from which the client should start extracting key names."},{"id":"keyword","title":"keyword","role":"content","text":"The _keyword_ type of `begin_search` means a literal token precedes key name arguments.\nIt is a map under the _spec_ with two keys:\n\n1. **keyword:** the keyword (token) that marks the beginning of key name arguments.\n2. **startfrom:** an index to the arguments array from which the client should begin searching. \n  This can be a negative value, which means the search should start from the end of the arguments' array, in reverse order.\n  For example, _-2_'s meaning is to search reverse from the penultimate argument.\n\nMore examples of the _keyword_ search type include:\n\n* [`SET`](https://redis.io/docs/latest/commands/set) has a `begin_search` specification of type _index_ with a value of _1_.\n* [`XREAD`](https://redis.io/docs/latest/commands/xread) has a `begin_search` specification of type _keyword_ with the values _\"STREAMS\"_ and _1_ as _keyword_ and _startfrom_, respectively.\n* [`MIGRATE`](https://redis.io/docs/latest/commands/migrate) has a _start_search_ specification of type _keyword_ with the values of _\"KEYS\"_ and _-2_."},{"id":"find_keys","title":"find_keys","role":"content","text":"The `find_keys` value of a key specification tells the client how to continue the search for key names.\n`find_keys` has three possible types:\n\n1. **range:** keys stop at a specific index or relative to the last argument.\n2. **keynum:** an additional argument specifies the number of input keys.\n3. **unknown:** an unknown type of specification - see the [incomplete flag section](#incomplete) for more details."},{"id":"range","title":"range","role":"content","text":"The _range_ type of `find_keys` is a map under the _spec_ key with three keys:\n\n1. **lastkey:** the index, relative to `begin_search`, of the last key argument.\n  This can be a negative value, in which case it isn't relative.\n  For example, _-1_ indicates to keep extracting keys until the last argument, _-2_ until one before the last, and so on.\n2. **keystep:** the number of arguments that should be skipped, after finding a key, to find the next one.\n3. **limit:** if _lastkey_ is has the value of _-1_, we use the _limit_ to stop the search by a factor.\n  _0_ and _1_ mean no limit.\n  _2_ means half of the remaining arguments, 3 means a third, and so on."},{"id":"keynum","title":"keynum","role":"content","text":"The _keynum_ type of `find_keys` is a map under the _spec_ key with three keys:\n\n* **keynumidx:** the index, relative to `begin_search`, of the argument containing the number of keys.\n* **firstkey:** the index, relative to `begin_search`, of the first key.\n  This is usually the next argument after _keynumidx_, and its value, in this case, is greater by one.\n* **keystep:** Tthe number of arguments that should be skipped, after finding a key, to find the next one.\n\nExamples:\n\n* The [`SET`](https://redis.io/docs/latest/commands/set) command has a _range_ of _0_, _1_ and _0_.\n* The [`MSET`](https://redis.io/docs/latest/commands/mset) command has a _range_ of _-1_, _2_ and _0_.\n* The [`XREAD`](https://redis.io/docs/latest/commands/xread) command has a _range_ of _-1_, _1_ and _2_.\n* The [`ZUNION`](https://redis.io/docs/latest/commands/zunion) command has a _start_search_ type _index_ with the value _1_, and `find_keys` of type _keynum_ with values of _0_, _1_ and _1_.\n\n**Note:**\nthis isn't a perfect solution as the module writers can come up with anything.\nHowever, this mechanism should allow the extraction of key name arguments for the vast majority of commands."},{"id":"notes","title":"notes","role":"content","text":"Notes about non-obvious key specs considerations, if applicable."},{"id":"flags","title":"flags","role":"parameters","text":"A key specification can have additional flags that provide more details about the key.\nThese flags are divided into three groups, as described below."},{"id":"access-type-flags","title":"Access type flags","role":"content","text":"The following flags declare the type of access the command uses to a key's value or its metadata.\nA key's metadata includes LRU/LFU counters, type, and cardinality.\nThese flags do not relate to the reply sent back to the client.\n\nEvery key specification has precisely one of the following flags:\n\n* **RW:** the read-write flag.\n  The command modifies the data stored in the value of the key or its metadata.\n  This flag marks every operation that isn't distinctly a delete, an overwrite, or read-only.\n* **RO:** the read-only flag.\n  The command only reads the value of the key (although it doesn't necessarily return it).\n* **OW:** the overwrite flag.\n  The command overwrites the data stored in the value of the key.\n* **RM:** the remove flag.\n  The command deletes the key."},{"id":"logical-operation-flags","title":"Logical operation flags","role":"content","text":"The following flags declare the type of operations performed on the data stored as the key's value and its TTL (if any), not the metadata.\nThese flags describe the logical operation that the command executes on data, driven by the input arguments.\nThe flags do not relate to modifying or returning metadata (such as a key's type, cardinality, or existence).\n\nEvery key specification may include the following flag:\n\n* **access:** the access flag.\n  This flag indicates that the command returns, copies, or somehow uses the user's data that's stored in the key.\n\nIn addition, the specification may include precisely one of the following:\n\n* **update:** the update flag.\n  The command updates the data stored in the key's value.\n  The new value may depend on the old value.\n  This flag marks every operation that isn't distinctly an insert or a delete.\n* **insert:** the insert flag.\n  The command only adds data to the value; existing data isn't modified or deleted.\n* **delete:** the delete flag.\n  The command explicitly deletes data from the value stored at the key."},{"id":"miscellaneous-flags","title":"Miscellaneous flags","role":"content","text":"Key specifications may have the following flags:\n\n* **not_key:** this flag indicates that the specified argument isn't a key.\n  This argument is treated the same as a key when computing which slot a command should be assigned to for Redis cluster. \n  For all other purposes this argument should not be considered a key.\n* **incomplete:** this flag is explained below.\n* **variable_flags:** this flag is explained below."},{"id":"incomplete","title":"incomplete","role":"content","text":"Some commands feature exotic approaches when it comes to specifying their keys, which makes extraction difficult.\nConsider, for example, what would happen with a call to [`MIGRATE`](https://redis.io/docs/latest/commands/migrate) that includes the literal string _\"KEYS\"_ as an argument to its _AUTH_ clause.\nOur key specifications would miss the mark, and extraction would begin at the wrong index.\n\nThus, we recognize that key specifications are incomplete and may fail to extract all keys.\nHowever, we assure that even incomplete specifications never yield the wrong names of keys, providing that the command is syntactically correct.\n\nIn the case of [`MIGRATE`](https://redis.io/docs/latest/commands/migrate), the search begins at the end (_startfrom_ has the value of _-1_).\nIf and when we encounter a key named _\"KEYS\"_, we'll only extract the subset of the key name arguments after it.\nThat's why [`MIGRATE`](https://redis.io/docs/latest/commands/migrate) has the _incomplete_ flag in its key specification.\n\nAnother case of incompleteness is the [`SORT`](https://redis.io/docs/latest/commands/sort) command.\nHere, the `begin_search` and `find_keys` are of type _unknown_.\nThe client should revert to calling the [`COMMAND GETKEYS`](https://redis.io/docs/latest/commands/command-getkeys) command to extract key names from the arguments, short of implementing it natively.\nThe difficulty arises, for example, because the string _\"STORE\"_ is both a keyword (token) and a valid literal argument for [`SORT`](https://redis.io/docs/latest/commands/sort).\n\n**Note:**\nthe only commands with _incomplete_ key specifications are [`SORT`](https://redis.io/docs/latest/commands/sort) and [`MIGRATE`](https://redis.io/docs/latest/commands/migrate).\nWe don't expect the addition of such commands in the future."},{"id":"variable_flags","title":"variable_flags","role":"content","text":"In some commands, the flags for the same key name argument can depend on other arguments.\nFor example, consider the [`SET`](https://redis.io/docs/latest/commands/set) command and its optional  _GET_ argument.\nWithout the _GET_ argument, [`SET`](https://redis.io/docs/latest/commands/set) is write-only, but it becomes a read and write command with it.\nWhen this flag is present, it means that the key specification flags cover all possible options, but the effective flags depend on other arguments."},{"id":"examples","title":"Examples","role":"example","text":""},{"id":"set-key-specifications","title":"SET key specifications","role":"content","text":"[code example]"},{"id":"zunion-key-specifications","title":"ZUNION key specifications","role":"content","text":"[code example]"}],"examples":[{"id":"set-key-specifications-ex0","language":"plaintext","code":"1) 1) \"flags\"\n     2) 1) RW\n        2) access\n        3) update\n     3) \"begin_search\"\n     4) 1) \"type\"\n        2) \"index\"\n        3) \"spec\"\n        4) 1) \"index\"\n           2) (integer) 1\n     5) \"find_keys\"\n     6) 1) \"type\"\n        2) \"range\"\n        3) \"spec\"\n        4) 1) \"lastkey\"\n           2) (integer) 0\n           3) \"keystep\"\n           4) (integer) 1\n           5) \"limit\"\n           6) (integer) 0","section_id":"set-key-specifications"},{"id":"zunion-key-specifications-ex0","language":"plaintext","code":"1) 1) \"flags\"\n     2) 1) RO\n        2) access\n     3) \"begin_search\"\n     4) 1) \"type\"\n        2) \"index\"\n        3) \"spec\"\n        4) 1) \"index\"\n           2) (integer) 1\n     5) \"find_keys\"\n     6) 1) \"type\"\n        2) \"keynum\"\n        3) \"spec\"\n        4) 1) \"keynumidx\"\n           2) (integer) 0\n           3) \"firstkey\"\n           4) (integer) 1\n           5) \"keystep\"\n           6) (integer) 1","section_id":"zunion-key-specifications"}]}
{"schema_version":2,"id":"develop/reference/modules","title":"Redis modules API","url":"https://redis.io/docs/latest/develop/reference/modules/","summary":"Introduction to writing Redis modules","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:49:27+01:00","children":[{"id":"develop/reference/modules/modules-api-ref","summary":"Reference for the Redis Modules API","title":"Modules API reference","url":"https://redis.io/docs/latest/develop/reference/modules/modules-api-ref/"},{"id":"develop/reference/modules/modules-blocking-ops","summary":"How to implement blocking commands in a Redis module","title":"Redis modules and blocking commands","url":"https://redis.io/docs/latest/develop/reference/modules/modules-blocking-ops/"},{"id":"develop/reference/modules/modules-native-types","summary":"How to use native types in a Redis module","title":"Modules API for native types","url":"https://redis.io/docs/latest/develop/reference/modules/modules-native-types/"}],"page_type":"content","content_hash":"d98fc456b0862d2a2b497ee46d47c90e1cb905a585b2144808978a44cfe496d4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The modules documentation is composed of the following pages:\n\n* Introduction to Redis modules (this file). An overview about Redis Modules system and API. It's a good idea to start your reading here.\n* [Implementing native data types](https://redis.io/docs/latest/develop/reference/modules/modules-native-types) covers the implementation of native data types into modules.\n* [Blocking operations](https://redis.io/docs/latest/develop/reference/modules/modules-blocking-ops) shows how to write blocking commands that will not reply immediately, but will block the client, without blocking the Redis server, and will provide a reply whenever will be possible.\n* [Redis modules API reference](https://redis.io/docs/latest/develop/reference/modules/modules-api-ref) is generated from module.c top comments of RedisModule functions. It is a good reference in order to understand how each function works.\n\nRedis modules make it possible to extend Redis functionality using external\nmodules, rapidly implementing new Redis commands with features\nsimilar to what can be done inside the core itself.\n\nRedis modules are dynamic libraries that can be loaded into Redis at\nstartup, or using the [`MODULE LOAD`](https://redis.io/docs/latest/commands/module-load) command. Redis exports a C API, in the\nform of a single C header file called `redismodule.h`. Modules are meant\nto be written in C, however it will be possible to use C++ or other languages\nthat have C binding functionalities.\n\nModules are designed in order to be loaded into different versions of Redis,\nso a given module does not need to be designed, or recompiled, in order to\nrun with a specific version of Redis. For this reason, the module will\nregister to the Redis core using a specific API version. The current API\nversion is \"1\".\n\nThis document is about an alpha version of Redis modules. API, functionalities\nand other details may change in the future."},{"id":"loading-modules","title":"Loading modules","role":"content","text":"In order to test the module you are developing, you can load the module\nusing the following `redis.conf` configuration directive:\n\n    loadmodule /path/to/mymodule.so\n\nIt is also possible to load a module at runtime using the following command:\n\n    MODULE LOAD /path/to/mymodule.so\n\nIn order to list all loaded modules, use:\n\n    MODULE LIST\n\nFinally, you can unload (and later reload if you wish) a module using the\nfollowing command:\n\n    MODULE UNLOAD mymodule\n\nNote that `mymodule` above is not the filename without the `.so` suffix, but\ninstead, the name the module used to register itself into the Redis core.\nThe name can be obtained using [`MODULE LIST`](https://redis.io/docs/latest/commands/module-list). However it is good practice\nthat the filename of the dynamic library is the same as the name the module\nuses to register itself into the Redis core."},{"id":"the-simplest-module-you-can-write","title":"The simplest module you can write","role":"content","text":"In order to show the different parts of a module, here we'll show a very\nsimple module that implements a command that outputs a random number.\n\n    #include \"redismodule.h\"\n    #include <stdlib.h>\n\n    int HelloworldRand_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n        RedisModule_ReplyWithLongLong(ctx,rand());\n        return REDISMODULE_OK;\n    }\n\n    int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n        if (RedisModule_Init(ctx,\"helloworld\",1,REDISMODULE_APIVER_1)\n            == REDISMODULE_ERR) return REDISMODULE_ERR;\n\n        if (RedisModule_CreateCommand(ctx,\"helloworld.rand\",\n            HelloworldRand_RedisCommand, \"fast random\",\n            0, 0, 0) == REDISMODULE_ERR)\n            return REDISMODULE_ERR;\n\n        return REDISMODULE_OK;\n    }\n\nThe example module has two functions. One implements a command called\nHELLOWORLD.RAND. This function is specific of that module. However the\nother function called `RedisModule_OnLoad()` must be present in each\nRedis module. It is the entry point for the module to be initialized,\nregister its commands, and potentially other private data structures\nit uses.\n\nNote that it is a good idea for modules to call commands with the\nname of the module followed by a dot, and finally the command name,\nlike in the case of `HELLOWORLD.RAND`. This way it is less likely to\nhave collisions.\n\nNote that if different modules have colliding commands, they'll not be\nable to work in Redis at the same time, since the function\n`RedisModule_CreateCommand` will fail in one of the modules, so the module\nloading will abort returning an error condition."},{"id":"module-initialization","title":"Module initialization","role":"content","text":"The above example shows the usage of the function `RedisModule_Init()`.\nIt should be the first function called by the module `OnLoad` function.\nThe following is the function prototype:\n\n    int RedisModule_Init(RedisModuleCtx *ctx, const char *modulename,\n                         int module_version, int api_version);\n\nThe `Init` function announces the Redis core that the module has a given\nname, its version (that is reported by [`MODULE LIST`](https://redis.io/docs/latest/commands/module-list)), and that is willing\nto use a specific version of the API.\n\nIf the API version is wrong, the name is already taken, or there are other\nsimilar errors, the function will return `REDISMODULE_ERR`, and the module\n`OnLoad` function should return ASAP with an error.\n\nBefore the `Init` function is called, no other API function can be called,\notherwise the module will segfault and the Redis instance will crash.\n\nThe second function called, `RedisModule_CreateCommand`, is used in order\nto register commands into the Redis core. The following is the prototype:\n\n    int RedisModule_CreateCommand(RedisModuleCtx *ctx, const char *name,\n                                  RedisModuleCmdFunc cmdfunc, const char *strflags,\n                                  int firstkey, int lastkey, int keystep);\n\nAs you can see, most Redis modules API calls all take as first argument\nthe `context` of the module, so that they have a reference to the module\ncalling it, to the command and client executing a given command, and so forth.\n\nTo create a new command, the above function needs the context, the command's\nname, a pointer to the function implementing the command, the command's flags\nand the positions of key names in the command's arguments.\n\nThe function that implements the command must have the following prototype:\n\n    int mycommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);\n\nThe command function arguments are just the context, that will be passed\nto all the other API calls, the command argument vector, and total number\nof arguments, as passed by the user.\n\nAs you can see, the arguments are provided as pointers to a specific data\ntype, the `RedisModuleString`. This is an opaque data type you have API\nfunctions to access and use, direct access to its fields is never needed.\n\nZooming into the example command implementation, we can find another call:\n\n    int RedisModule_ReplyWithLongLong(RedisModuleCtx *ctx, long long integer);\n\nThis function returns an integer to the client that invoked the command,\nexactly like other Redis commands do, like for example [`INCR`](https://redis.io/docs/latest/commands/incr) or [`SCARD`](https://redis.io/docs/latest/commands/scard)."},{"id":"module-cleanup","title":"Module cleanup","role":"content","text":"In most cases, there is no need for special cleanup.\nWhen a module is unloaded, Redis will automatically unregister commands and\nunsubscribe from notifications.\nHowever in the case where a module contains some persistent memory or\nconfiguration, a module may include an optional `RedisModule_OnUnload`\nfunction.\nIf a module provides this function, it will be invoked during the module unload\nprocess.\nThe following is the function prototype:\n\n    int RedisModule_OnUnload(RedisModuleCtx *ctx);\n\nThe `OnUnload` function may prevent module unloading by returning\n`REDISMODULE_ERR`.\nOtherwise, `REDISMODULE_OK` should be returned."},{"id":"setup-and-dependencies-of-a-redis-module","title":"Setup and dependencies of a Redis module","role":"setup","text":"Redis modules don't depend on Redis or some other library, nor they\nneed to be compiled with a specific `redismodule.h` file. In order\nto create a new module, just copy a recent version of `redismodule.h`\nin your source tree, link all the libraries you want, and create\na dynamic library having the `RedisModule_OnLoad()` function symbol\nexported.\n\nThe module will be able to load into different versions of Redis.\n\nA module can be designed to support both newer and older Redis versions where certain API functions are not available in all versions.\nIf an API function is not implemented in the currently running Redis version, the function pointer is set to NULL.\nThis allows the module to check if a function exists before using it:\n\n    if (RedisModule_SetCommandInfo != NULL) {\n        RedisModule_SetCommandInfo(cmd, &info);\n    }\n\nIn recent versions of `redismodule.h`, a convenience macro `RMAPI_FUNC_SUPPORTED(funcname)` is defined.\nUsing the macro or just comparing with NULL is a matter of personal preference."},{"id":"passing-configuration-parameters-to-redis-modules","title":"Passing configuration parameters to Redis modules","role":"content","text":"When the module is loaded with the [`MODULE LOAD`](https://redis.io/docs/latest/commands/module-load) command, or using the\n`loadmodule` directive in the `redis.conf` file, the user is able to pass\nconfiguration parameters to the module by adding arguments after the module\nfile name:\n\n    loadmodule mymodule.so foo bar 1234\n\nIn the above example the strings `foo`, `bar` and `1234` will be passed\nto the module `OnLoad()` function in the `argv` argument as an array\nof RedisModuleString pointers. The number of arguments passed is into `argc`.\n\nThe way you can access those strings will be explained in the rest of this\ndocument. Normally the module will store the module configuration parameters\nin some `static` global variable that can be accessed module wide, so that\nthe configuration can change the behavior of different commands."},{"id":"working-with-redismodulestring-objects","title":"Working with RedisModuleString objects","role":"content","text":"The command argument vector `argv` passed to module commands, and the\nreturn value of other module APIs functions, are of type `RedisModuleString`.\n\nUsually you directly pass module strings to other API calls, however sometimes\nyou may need to directly access the string object.\n\nThere are a few functions in order to work with string objects:\n\n    const char *RedisModule_StringPtrLen(RedisModuleString *string, size_t *len);\n\nThe above function accesses a string by returning its pointer and setting its\nlength in `len`.\nYou should never write to a string object pointer, as you can see from the\n`const` pointer qualifier.\n\nHowever, if you want, you can create new string objects using the following\nAPI:\n\n    RedisModuleString *RedisModule_CreateString(RedisModuleCtx *ctx, const char *ptr, size_t len);\n\nThe string returned by the above command must be freed using a corresponding\ncall to `RedisModule_FreeString()`:\n\n    void RedisModule_FreeString(RedisModuleString *str);\n\nHowever if you want to avoid having to free strings, the automatic memory\nmanagement, covered later in this document, can be a good alternative, by\ndoing it for you.\n\nNote that the strings provided via the argument vector `argv` never need\nto be freed. You only need to free new strings you create, or new strings\nreturned by other APIs, where it is specified that the returned string must\nbe freed."},{"id":"creating-strings-from-numbers-or-parsing-strings-as-numbers","title":"Creating strings from numbers or parsing strings as numbers","role":"content","text":"Creating a new string from an integer is a very common operation, so there\nis a function to do this:\n\n    RedisModuleString *mystr = RedisModule_CreateStringFromLongLong(ctx,10);\n\nSimilarly in order to parse a string as a number:\n\n    long long myval;\n    if (RedisModule_StringToLongLong(ctx,argv[1],&myval) == REDISMODULE_OK) {\n        /* Do something with 'myval' */\n    }"},{"id":"accessing-redis-keys-from-modules","title":"Accessing Redis keys from modules","role":"content","text":"Most Redis modules, in order to be useful, have to interact with the Redis\ndata space (this is not always true, for example an ID generator may\nnever touch Redis keys). Redis modules have two different APIs in order to\naccess the Redis data space, one is a low level API that provides very\nfast access and a set of functions to manipulate Redis data structures.\nThe other API is more high level, and allows to call Redis commands and\nfetch the result, similarly to how Lua scripts access Redis.\n\nThe high level API is also useful in order to access Redis functionalities\nthat are not available as APIs.\n\nIn general modules developers should prefer the low level API, because commands\nimplemented using the low level API run at a speed comparable to the speed\nof native Redis commands. However there are definitely use cases for the\nhigher level API. For example often the bottleneck could be processing the\ndata and not accessing it.\n\nAlso note that sometimes using the low level API is not harder compared to\nthe higher level one."},{"id":"calling-redis-commands","title":"Calling Redis commands","role":"content","text":"The high level API to access Redis is the sum of the `RedisModule_Call()`\nfunction, together with the functions needed in order to access the\nreply object returned by `Call()`.\n\n`RedisModule_Call` uses a special calling convention, with a format specifier\nthat is used to specify what kind of objects you are passing as arguments\nto the function.\n\nRedis commands are invoked just using a command name and a list of arguments.\nHowever when calling commands, the arguments may originate from different\nkind of strings: null-terminated C strings, RedisModuleString objects as\nreceived from the `argv` parameter in the command implementation, binary\nsafe C buffers with a pointer and a length, and so forth.\n\nFor example if I want to call [`INCRBY`](https://redis.io/docs/latest/commands/incrby) using a first argument (the key)\na string received in the argument vector `argv`, which is an array\nof RedisModuleString object pointers, and a C string representing the\nnumber \"10\" as second argument (the increment), I'll use the following\nfunction call:\n\n    RedisModuleCallReply *reply;\n    reply = RedisModule_Call(ctx,\"INCRBY\",\"sc\",argv[1],\"10\");\n\nThe first argument is the context, and the second is always a null terminated\nC string with the command name. The third argument is the format specifier\nwhere each character corresponds to the type of the arguments that will follow.\nIn the above case `\"sc\"` means a RedisModuleString object, and a null\nterminated C string. The other arguments are just the two arguments as\nspecified. In fact `argv[1]` is a RedisModuleString and `\"10\"` is a null\nterminated C string.\n\nThis is the full list of format specifiers:\n\n* **c** -- Null terminated C string pointer.\n* **b** -- C buffer, two arguments needed: C string pointer and `size_t` length.\n* **s** -- RedisModuleString as received in `argv` or by other Redis module APIs returning a RedisModuleString object.\n* **l** -- Long long integer.\n* **v** -- Array of RedisModuleString objects.\n* **!** -- This modifier just tells the function to replicate the command to replicas and AOF. It is ignored from the point of view of arguments parsing.\n* **A** -- This modifier, when `!` is given, tells to suppress AOF propagation: the command will be propagated only to replicas.\n* **R** -- This modifier, when `!` is given, tells to suppress replicas propagation: the command will be propagated only to the AOF if enabled.\n\nThe function returns a `RedisModuleCallReply` object on success, on\nerror NULL is returned.\n\nNULL is returned when the command name is invalid, the format specifier uses\ncharacters that are not recognized, or when the command is called with the\nwrong number of arguments. In the above cases the `errno` var is set to `EINVAL`. NULL is also returned when, in an instance with Cluster enabled, the target\nkeys are about non local hash slots. In this case `errno` is set to `EPERM`."},{"id":"working-with-redismodulecallreply-objects","title":"Working with RedisModuleCallReply objects.","role":"content","text":"`RedisModuleCall` returns reply objects that can be accessed using the\n`RedisModule_CallReply*` family of functions.\n\nIn order to obtain the type or reply (corresponding to one of the data types\nsupported by the Redis protocol), the function `RedisModule_CallReplyType()`\nis used:\n\n    reply = RedisModule_Call(ctx,\"INCRBY\",\"sc\",argv[1],\"10\");\n    if (RedisModule_CallReplyType(reply) == REDISMODULE_REPLY_INTEGER) {\n        long long myval = RedisModule_CallReplyInteger(reply);\n        /* Do something with myval. */\n    }\n\nValid reply types are:\n\n* `REDISMODULE_REPLY_STRING` Bulk string or status replies.\n* `REDISMODULE_REPLY_ERROR` Errors.\n* `REDISMODULE_REPLY_INTEGER` Signed 64 bit integers.\n* `REDISMODULE_REPLY_ARRAY` Array of replies.\n* `REDISMODULE_REPLY_NULL` NULL reply.\n\nStrings, errors and arrays have an associated length. For strings and errors\nthe length corresponds to the length of the string. For arrays the length\nis the number of elements. To obtain the reply length the following function\nis used:\n\n    size_t reply_len = RedisModule_CallReplyLength(reply);\n\nIn order to obtain the value of an integer reply, the following function is used, as already shown in the example above:\n\n    long long reply_integer_val = RedisModule_CallReplyInteger(reply);\n\nCalled with a reply object of the wrong type, the above function always\nreturns `LLONG_MIN`.\n\nSub elements of array replies are accessed this way:\n\n    RedisModuleCallReply *subreply;\n    subreply = RedisModule_CallReplyArrayElement(reply,idx);\n\nThe above function returns NULL if you try to access out of range elements.\n\nStrings and errors (which are like strings but with a different type) can\nbe accessed using in the following way, making sure to never write to\nthe resulting pointer (that is returned as a `const` pointer so that\nmisusing must be pretty explicit):\n\n    size_t len;\n    char *ptr = RedisModule_CallReplyStringPtr(reply,&len);\n\nIf the reply type is not a string or an error, NULL is returned.\n\nRedisCallReply objects are not the same as module string objects\n(RedisModuleString types). However sometimes you may need to pass replies\nof type string or integer, to API functions expecting a module string.\n\nWhen this is the case, you may want to evaluate if using the low level\nAPI could be a simpler way to implement your command, or you can use\nthe following function in order to create a new string object from a\ncall reply of type string, error or integer:\n\n    RedisModuleString *mystr = RedisModule_CreateStringFromCallReply(myreply);\n\nIf the reply is not of the right type, NULL is returned.\nThe returned string object should be released with `RedisModule_FreeString()`\nas usually, or by enabling automatic memory management (see corresponding\nsection)."},{"id":"releasing-call-reply-objects","title":"Releasing call reply objects","role":"content","text":"Reply objects must be freed using `RedisModule_FreeCallReply`. For arrays,\nyou need to free only the top level reply, not the nested replies.\nCurrently the module implementation provides a protection in order to avoid\ncrashing if you free a nested reply object for error, however this feature\nis not guaranteed to be here forever, so should not be considered part\nof the API.\n\nIf you use automatic memory management (explained later in this document)\nyou don't need to free replies (but you still could if you wish to release\nmemory ASAP)."},{"id":"returning-values-from-redis-commands","title":"Returning values from Redis commands","role":"returns","text":"Like normal Redis commands, new commands implemented via modules must be\nable to return values to the caller. The API exports a set of functions for\nthis goal, in order to return the usual types of the Redis protocol, and\narrays of such types as elements. Also errors can be returned with any\nerror string and code (the error code is the initial uppercase letters in\nthe error message, like the \"BUSY\" string in the \"BUSY the sever is busy\" error\nmessage).\n\nAll the functions to send a reply to the client are called\n`RedisModule_ReplyWith<something>`.\n\nTo return an error, use:\n\n    RedisModule_ReplyWithError(RedisModuleCtx *ctx, const char *err);\n\nThere is a predefined error string for key of wrong type errors:\n\n    REDISMODULE_ERRORMSG_WRONGTYPE\n\nExample usage:\n\n    RedisModule_ReplyWithError(ctx,\"ERR invalid arguments\");\n\nWe already saw how to reply with a `long long` in the examples above:\n\n    RedisModule_ReplyWithLongLong(ctx,12345);\n\nTo reply with a simple string, that can't contain binary values or newlines,\n(so it's suitable to send small words, like \"OK\") we use:\n\n    RedisModule_ReplyWithSimpleString(ctx,\"OK\");\n\nIt's possible to reply with \"bulk strings\" that are binary safe, using\ntwo different functions:\n\n    int RedisModule_ReplyWithStringBuffer(RedisModuleCtx *ctx, const char *buf, size_t len);\n\n    int RedisModule_ReplyWithString(RedisModuleCtx *ctx, RedisModuleString *str);\n\nThe first function gets a C pointer and length. The second a RedisModuleString\nobject. Use one or the other depending on the source type you have at hand.\n\nIn order to reply with an array, you just need to use a function to emit the\narray length, followed by as many calls to the above functions as the number\nof elements of the array are:\n\n    RedisModule_ReplyWithArray(ctx,2);\n    RedisModule_ReplyWithStringBuffer(ctx,\"age\",3);\n    RedisModule_ReplyWithLongLong(ctx,22);\n\nTo return nested arrays is easy, your nested array element just uses another\ncall to `RedisModule_ReplyWithArray()` followed by the calls to emit the\nsub array elements."},{"id":"returning-arrays-with-dynamic-length","title":"Returning arrays with dynamic length","role":"returns","text":"Sometimes it is not possible to know beforehand the number of items of\nan array. As an example, think of a Redis module implementing a FACTOR\ncommand that given a number outputs the prime factors. Instead of\nfactorializing the number, storing the prime factors into an array, and\nlater produce the command reply, a better solution is to start an array\nreply where the length is not known, and set it later. This is accomplished\nwith a special argument to `RedisModule_ReplyWithArray()`:\n\n    RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_LEN);\n\nThe above call starts an array reply so we can use other `ReplyWith` calls\nin order to produce the array items. Finally in order to set the length,\nuse the following call:\n\n    RedisModule_ReplySetArrayLength(ctx, number_of_items);\n\nIn the case of the FACTOR command, this translates to some code similar\nto this:\n\n    RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_LEN);\n    number_of_factors = 0;\n    while(still_factors) {\n        RedisModule_ReplyWithLongLong(ctx, some_factor);\n        number_of_factors++;\n    }\n    RedisModule_ReplySetArrayLength(ctx, number_of_factors);\n\nAnother common use case for this feature is iterating over the arrays of\nsome collection and only returning the ones passing some kind of filtering.\n\nIt is possible to have multiple nested arrays with postponed reply.\nEach call to `SetArray()` will set the length of the latest corresponding\ncall to `ReplyWithArray()`:\n\n    RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_LEN);\n    ... generate 100 elements ...\n    RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_LEN);\n    ... generate 10 elements ...\n    RedisModule_ReplySetArrayLength(ctx, 10);\n    RedisModule_ReplySetArrayLength(ctx, 100);\n\nThis creates a 100 items array having as last element a 10 items array."},{"id":"arity-and-type-checks","title":"Arity and type checks","role":"content","text":"Often commands need to check that the number of arguments and type of the key\nis correct. In order to report a wrong arity, there is a specific function\ncalled `RedisModule_WrongArity()`. The usage is trivial:\n\n    if (argc != 2) return RedisModule_WrongArity(ctx);\n\nChecking for the wrong type involves opening the key and checking the type:\n\n    RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],\n        REDISMODULE_READ|REDISMODULE_WRITE);\n\n    int keytype = RedisModule_KeyType(key);\n    if (keytype != REDISMODULE_KEYTYPE_STRING &&\n        keytype != REDISMODULE_KEYTYPE_EMPTY)\n    {\n        RedisModule_CloseKey(key);\n        return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);\n    }\n\nNote that you often want to proceed with a command both if the key\nis of the expected type, or if it's empty."},{"id":"low-level-access-to-keys","title":"Low level access to keys","role":"content","text":"Low level access to keys allow to perform operations on value objects associated\nto keys directly, with a speed similar to what Redis uses internally to\nimplement the built-in commands.\n\nOnce a key is opened, a key pointer is returned that will be used with all the\nother low level API calls in order to perform operations on the key or its\nassociated value.\n\nBecause the API is meant to be very fast, it cannot do too many run-time\nchecks, so the user must be aware of certain rules to follow:\n\n* Opening the same key multiple times where at least one instance is opened for writing, is undefined and may lead to crashes.\n* While a key is open, it should only be accessed via the low level key API. For example opening a key, then calling DEL on the same key using the `RedisModule_Call()` API will result into a crash. However it is safe to open a key, perform some operation with the low level API, closing it, then using other APIs to manage the same key, and later opening it again to do some more work.\n\nIn order to open a key the `RedisModule_OpenKey` function is used. It returns\na key pointer, that we'll use with all the next calls to access and modify\nthe value:\n\n    RedisModuleKey *key;\n    key = RedisModule_OpenKey(ctx,argv[1],REDISMODULE_READ);\n\nThe second argument is the key name, that must be a `RedisModuleString` object.\nThe third argument is the mode: `REDISMODULE_READ` or `REDISMODULE_WRITE`.\nIt is possible to use `|` to bitwise OR the two modes to open the key in\nboth modes. Currently a key opened for writing can also be accessed for reading\nbut this is to be considered an implementation detail. The right mode should\nbe used in sane modules.\n\nYou can open non existing keys for writing, since the keys will be created\nwhen an attempt to write to the key is performed. However when opening keys\njust for reading, `RedisModule_OpenKey` will return NULL if the key does not\nexist.\n\nOnce you are done using a key, you can close it with:\n\n    RedisModule_CloseKey(key);\n\nNote that if automatic memory management is enabled, you are not forced to\nclose keys. When the module function returns, Redis will take care to close\nall the keys which are still open."},{"id":"getting-the-key-type","title":"Getting the key type","role":"content","text":"In order to obtain the value of a key, use the `RedisModule_KeyType()` function:\n\n    int keytype = RedisModule_KeyType(key);\n\nIt returns one of the following values:\n\n    REDISMODULE_KEYTYPE_EMPTY\n    REDISMODULE_KEYTYPE_STRING\n    REDISMODULE_KEYTYPE_LIST\n    REDISMODULE_KEYTYPE_HASH\n    REDISMODULE_KEYTYPE_SET\n    REDISMODULE_KEYTYPE_ZSET\n\nThe above are just the usual Redis key types, with the addition of an empty\ntype, that signals the key pointer is associated with an empty key that\ndoes not yet exists."},{"id":"creating-new-keys","title":"Creating new keys","role":"content","text":"To create a new key, open it for writing and then write to it using one\nof the key writing functions. Example:\n\n    RedisModuleKey *key;\n    key = RedisModule_OpenKey(ctx,argv[1],REDISMODULE_WRITE);\n    if (RedisModule_KeyType(key) == REDISMODULE_KEYTYPE_EMPTY) {\n        RedisModule_StringSet(key,argv[2]);\n    }"},{"id":"deleting-keys","title":"Deleting keys","role":"content","text":"Just use:\n\n    RedisModule_DeleteKey(key);\n\nThe function returns `REDISMODULE_ERR` if the key is not open for writing.\nNote that after a key gets deleted, it is setup in order to be targeted\nby new key commands. For example `RedisModule_KeyType()` will return it is\nan empty key, and writing to it will create a new key, possibly of another\ntype (depending on the API used)."},{"id":"managing-key-expires-ttls","title":"Managing key expires (TTLs)","role":"content","text":"To control key expires two functions are provided, that are able to set,\nmodify, get, and unset the time to live associated with a key.\n\nOne function is used in order to query the current expire of an open key:\n\n    mstime_t RedisModule_GetExpire(RedisModuleKey *key);\n\nThe function returns the time to live of the key in milliseconds, or\n`REDISMODULE_NO_EXPIRE` as a special value to signal the key has no associated\nexpire or does not exist at all (you can differentiate the two cases checking\nif the key type is `REDISMODULE_KEYTYPE_EMPTY`).\n\nIn order to change the expire of a key the following function is used instead:\n\n    int RedisModule_SetExpire(RedisModuleKey *key, mstime_t expire);\n\nWhen called on a non existing key, `REDISMODULE_ERR` is returned, because\nthe function can only associate expires to existing open keys (non existing\nopen keys are only useful in order to create new values with data type\nspecific write operations).\n\nAgain the `expire` time is specified in milliseconds. If the key has currently\nno expire, a new expire is set. If the key already have an expire, it is\nreplaced with the new value.\n\nIf the key has an expire, and the special value `REDISMODULE_NO_EXPIRE` is\nused as a new expire, the expire is removed, similarly to the Redis\n[`PERSIST`](https://redis.io/docs/latest/commands/persist) command. In case the key was already persistent, no operation is\nperformed."},{"id":"obtaining-the-length-of-values","title":"Obtaining the length of values","role":"content","text":"There is a single function in order to retrieve the length of the value\nassociated to an open key. The returned length is value-specific, and is\nthe string length for strings, and the number of elements for the aggregated\ndata types (how many elements there is in a list, set, sorted set, hash).\n\n    size_t len = RedisModule_ValueLength(key);\n\nIf the key does not exist, 0 is returned by the function:"},{"id":"string-type-api","title":"String type API","role":"content","text":"Setting a new string value, like the Redis [`SET`](https://redis.io/docs/latest/commands/set) command does, is performed\nusing:\n\n    int RedisModule_StringSet(RedisModuleKey *key, RedisModuleString *str);\n\nThe function works exactly like the Redis [`SET`](https://redis.io/docs/latest/commands/set) command itself, that is, if\nthere is a prior value (of any type) it will be deleted.\n\nAccessing existing string values is performed using DMA (direct memory\naccess) for speed. The API will return a pointer and a length, so that's\npossible to access and, if needed, modify the string directly.\n\n    size_t len, j;\n    char *myptr = RedisModule_StringDMA(key,&len,REDISMODULE_WRITE);\n    for (j = 0; j < len; j++) myptr[j] = 'A';\n\nIn the above example we write directly on the string. Note that if you want\nto write, you must be sure to ask for `WRITE` mode.\n\nDMA pointers are only valid if no other operations are performed with the key\nbefore using the pointer, after the DMA call.\n\nSometimes when we want to manipulate strings directly, we need to change\ntheir size as well. For this scope, the `RedisModule_StringTruncate` function\nis used. Example:\n\n    RedisModule_StringTruncate(mykey,1024);\n\nThe function truncates, or enlarges the string as needed, padding it with\nzero bytes if the previous length is smaller than the new length we request.\nIf the string does not exist since `key` is associated to an open empty key,\na string value is created and associated to the key.\n\nNote that every time `StringTruncate()` is called, we need to re-obtain\nthe DMA pointer again, since the old may be invalid."},{"id":"list-type-api","title":"List type API","role":"content","text":"It's possible to push and pop values from list values:\n\n    int RedisModule_ListPush(RedisModuleKey *key, int where, RedisModuleString *ele);\n    RedisModuleString *RedisModule_ListPop(RedisModuleKey *key, int where);\n\nIn both the APIs the `where` argument specifies if to push or pop from tail\nor head, using the following macros:\n\n    REDISMODULE_LIST_HEAD\n    REDISMODULE_LIST_TAIL\n\nElements returned by `RedisModule_ListPop()` are like strings created with\n`RedisModule_CreateString()`, they must be released with\n`RedisModule_FreeString()` or by enabling automatic memory management."},{"id":"set-type-api","title":"Set type API","role":"content","text":"Work in progress."},{"id":"sorted-set-type-api","title":"Sorted set type API","role":"content","text":"Documentation missing, please refer to the top comments inside `module.c`\nfor the following functions:\n\n* `RedisModule_ZsetAdd`\n* `RedisModule_ZsetIncrby`\n* `RedisModule_ZsetScore`\n* `RedisModule_ZsetRem`\n\nAnd for the sorted set iterator:\n\n* `RedisModule_ZsetRangeStop`\n* `RedisModule_ZsetFirstInScoreRange`\n* `RedisModule_ZsetLastInScoreRange`\n* `RedisModule_ZsetFirstInLexRange`\n* `RedisModule_ZsetLastInLexRange`\n* `RedisModule_ZsetRangeCurrentElement`\n* `RedisModule_ZsetRangeNext`\n* `RedisModule_ZsetRangePrev`\n* `RedisModule_ZsetRangeEndReached`"},{"id":"hash-type-api","title":"Hash type API","role":"content","text":"Documentation missing, please refer to the top comments inside `module.c`\nfor the following functions:\n\n* `RedisModule_HashSet`\n* `RedisModule_HashGet`"},{"id":"iterating-aggregated-values","title":"Iterating aggregated values","role":"content","text":"Work in progress."},{"id":"replicating-commands","title":"Replicating commands","role":"content","text":"If you want to use module commands exactly like normal Redis commands, in the\ncontext of replicated Redis instances, or using the AOF file for persistence,\nit is important for module commands to handle their replication in a consistent\nway.\n\nWhen using the higher level APIs to invoke commands, replication happens\nautomatically if you use the \"!\" modifier in the format string of\n`RedisModule_Call()` as in the following example:\n\n    reply = RedisModule_Call(ctx,\"INCRBY\",\"!sc\",argv[1],\"10\");\n\nAs you can see the format specifier is `\"!sc\"`. The bang is not parsed as a\nformat specifier, but it internally flags the command as \"must replicate\".\n\nIf you use the above programming style, there are no problems.\nHowever sometimes things are more complex than that, and you use the low level\nAPI. In this case, if there are no side effects in the command execution, and\nit consistently always performs the same work, what is possible to do is to\nreplicate the command verbatim as the user executed it. To do that, you just\nneed to call the following function:\n\n    RedisModule_ReplicateVerbatim(ctx);\n\nWhen you use the above API, you should not use any other replication function\nsince they are not guaranteed to mix well.\n\nHowever this is not the only option. It's also possible to exactly tell\nRedis what commands to replicate as the effect of the command execution, using\nan API similar to `RedisModule_Call()` but that instead of calling the command\nsends it to the AOF / replicas stream. Example:\n\n    RedisModule_Replicate(ctx,\"INCRBY\",\"cl\",\"foo\",my_increment);\n\nIt's possible to call `RedisModule_Replicate` multiple times, and each\nwill emit a command. All the sequence emitted is wrapped between a\n`MULTI/EXEC` transaction, so that the AOF and replication effects are the\nsame as executing a single command.\n\nNote that before Redis version 7.0, `Call()` replication and `Replicate()` replication have a rule,\nin case you want to mix both forms of replication (not necessarily a good\nidea if there are simpler approaches). Commands replicated with `Call()`\nare always the first emitted in the final `MULTI/EXEC` block, while all\nthe commands emitted with `Replicate()` will follow."},{"id":"automatic-memory-management","title":"Automatic memory management","role":"content","text":"Normally when writing programs in the C language, programmers need to manage\nmemory manually. This is why the Redis modules API has functions to release\nstrings, close open keys, free replies, and so forth.\n\nHowever given that commands are executed in a contained environment and\nwith a set of strict APIs, Redis is able to provide automatic memory management\nto modules, at the cost of some performance (most of the time, a very low\ncost).\n\nWhen automatic memory management is enabled:\n\n1. You don't need to close open keys.\n2. You don't need to free replies.\n3. You don't need to free RedisModuleString objects.\n\nHowever you can still do it, if you want. For example, automatic memory\nmanagement may be active, but inside a loop allocating a lot of strings,\nyou may still want to free strings no longer used.\n\nIn order to enable automatic memory management, just call the following\nfunction at the start of the command implementation:\n\n    RedisModule_AutoMemory(ctx);\n\nAutomatic memory management is usually the way to go, however experienced\nC programmers may not use it in order to gain some speed and memory usage\nbenefit."},{"id":"allocating-memory-into-modules","title":"Allocating memory into modules","role":"content","text":"Normal C programs use `malloc()` and `free()` in order to allocate and\nrelease memory dynamically. While in Redis modules the use of malloc is\nnot technically forbidden, it is a lot better to use the Redis Modules\nspecific functions, that are exact replacements for `malloc`, `free`,\n`realloc` and `strdup`. These functions are:\n\n    void *RedisModule_Alloc(size_t bytes);\n    void* RedisModule_Realloc(void *ptr, size_t bytes);\n    void RedisModule_Free(void *ptr);\n    void RedisModule_Calloc(size_t nmemb, size_t size);\n    char *RedisModule_Strdup(const char *str);\n\nThey work exactly like their `libc` equivalent calls, however they use\nthe same allocator Redis uses, and the memory allocated using these\nfunctions is reported by the [`INFO`](https://redis.io/docs/latest/commands/info) command in the memory section, is\naccounted when enforcing the `maxmemory` policy, and in general is\na first citizen of the Redis executable. On the contrary, the method\nallocated inside modules with libc `malloc()` is transparent to Redis.\n\nAnother reason to use the modules functions in order to allocate memory\nis that, when creating native data types inside modules, the RDB loading\nfunctions can return deserialized strings (from the RDB file) directly\nas `RedisModule_Alloc()` allocations, so they can be used directly to\npopulate data structures after loading, instead of having to copy them\nto the data structure."},{"id":"pool-allocator","title":"Pool allocator","role":"content","text":"Sometimes in commands implementations, it is required to perform many\nsmall allocations that will be not retained at the end of the command\nexecution, but are just functional to execute the command itself.\n\nThis work can be more easily accomplished using the Redis pool allocator:\n\n    void *RedisModule_PoolAlloc(RedisModuleCtx *ctx, size_t bytes);\n\nIt works similarly to `malloc()`, and returns memory aligned to the\nnext power of two of greater or equal to `bytes` (for a maximum alignment\nof 8 bytes). However it allocates memory in blocks, so it the overhead\nof the allocations is small, and more important, the memory allocated\nis automatically released when the command returns.\n\nSo in general short living allocations are a good candidates for the pool\nallocator."},{"id":"writing-commands-compatible-with-redis-cluster","title":"Writing commands compatible with Redis Cluster","role":"content","text":"Documentation missing, please check the following functions inside `module.c`:\n\n    RedisModule_IsKeysPositionRequest(ctx);\n    RedisModule_KeyAtPos(ctx,pos);"}],"examples":[]}
{"schema_version":2,"id":"develop/reference/modules/modules-api-ref","title":"Modules API reference","url":"https://redis.io/docs/latest/develop/reference/modules/modules-api-ref/","summary":"Reference for the Redis Modules API","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-07-30T08:54:30-07:00","page_type":"content","content_hash":"4cd87f0e4099c6156703fe6a3bba69347194784b4c357c4e5aab3839add4e78f","sections":[{"id":"sections","title":"Sections","role":"overview","text":"* [Heap allocation raw functions](#section-heap-allocation-raw-functions)\n* [Commands API](#section-commands-api)\n* [Module information and time measurement](#section-module-information-and-time-measurement)\n* [Automatic memory management for modules](#section-automatic-memory-management-for-modules)\n* [String objects APIs](#section-string-objects-apis)\n* [Reply APIs](#section-reply-apis)\n* [Commands replication API](#section-commands-replication-api)\n* [DB and Key APIs – Generic API](#section-db-and-key-apis-generic-api)\n* [Key API for String type](#section-key-api-for-string-type)\n* [Key API for List type](#section-key-api-for-list-type)\n* [Key API for Sorted Set type](#section-key-api-for-sorted-set-type)\n* [Key API for Sorted Set iterator](#section-key-api-for-sorted-set-iterator)\n* [Key API for Hash type](#section-key-api-for-hash-type)\n* [Key API for Stream type](#section-key-api-for-stream-type)\n* [Calling Redis commands from modules](#section-calling-redis-commands-from-modules)\n* [Modules data types](#section-modules-data-types)\n* [RDB loading and saving functions](#section-rdb-loading-and-saving-functions)\n* [Key digest API (DEBUG DIGEST interface for modules types)](#section-key-digest-api-debug-digest-interface-for-modules-types)\n* [AOF API for modules data types](#section-aof-api-for-modules-data-types)\n* [IO context handling](#section-io-context-handling)\n* [Logging](#section-logging)\n* [Blocking clients from modules](#section-blocking-clients-from-modules)\n* [Thread Safe Contexts](#section-thread-safe-contexts)\n* [Module Keyspace Notifications API](#section-module-keyspace-notifications-api)\n* [Modules Cluster API](#section-modules-cluster-api)\n* [Modules Timers API](#section-modules-timers-api)\n* [Modules EventLoop API](#section-modules-eventloop-api)\n* [Modules ACL API](#section-modules-acl-api)\n* [Modules Dictionary API](#section-modules-dictionary-api)\n* [Modules Info fields](#section-modules-info-fields)\n* [Modules utility APIs](#section-modules-utility-apis)\n* [Modules API exporting / importing](#section-modules-api-exporting-importing)\n* [Module Command Filter API](#section-module-command-filter-api)\n* [Scanning keyspace and hashes](#section-scanning-keyspace-and-hashes)\n* [Module fork API](#section-module-fork-api)\n* [Server hooks implementation](#section-server-hooks-implementation)\n* [Module Configurations API](#section-module-configurations-api)\n* [RDB load/save API](#section-rdb-load-save-api)\n* [Config access API](#section-config-access-api)\n* [Key eviction API](#section-key-eviction-api)\n* [Miscellaneous APIs](#section-miscellaneous-apis)\n* [Defrag API](#section-defrag-api)\n* [Function index](#section-function-index)\n\n<span id=\"section-heap-allocation-raw-functions\"></span>"},{"id":"heap-allocation-raw-functions","title":"Heap allocation raw functions","role":"content","text":"Memory allocated with these functions are taken into account by Redis key\neviction algorithms and are reported in Redis memory usage information.\n\n<span id=\"RedisModule_Alloc\"></span>"},{"id":"redismodule_alloc","title":"`RedisModule_Alloc`","role":"content","text":"void *RedisModule_Alloc(size_t bytes);\n\n**Available since:** 4.0.0\n\nUse like `malloc()`. Memory allocated with this function is reported in\nRedis INFO memory, used for keys eviction according to maxmemory settings\nand in general is taken into account as memory allocated by Redis.\nYou should avoid using `malloc()`.\nThis function panics if unable to allocate enough memory.\n\n<span id=\"RedisModule_TryAlloc\"></span>"},{"id":"redismodule_tryalloc","title":"`RedisModule_TryAlloc`","role":"content","text":"void *RedisModule_TryAlloc(size_t bytes);\n\n**Available since:** 7.0.0\n\nSimilar to [`RedisModule_Alloc`](#RedisModule_Alloc), but returns NULL in case of allocation failure, instead\nof panicking.\n\n<span id=\"RedisModule_Calloc\"></span>"},{"id":"redismodule_calloc","title":"`RedisModule_Calloc`","role":"content","text":"void *RedisModule_Calloc(size_t nmemb, size_t size);\n\n**Available since:** 4.0.0\n\nUse like `calloc()`. Memory allocated with this function is reported in\nRedis INFO memory, used for keys eviction according to maxmemory settings\nand in general is taken into account as memory allocated by Redis.\nYou should avoid using `calloc()` directly.\n\n<span id=\"RedisModule_TryCalloc\"></span>"},{"id":"redismodule_trycalloc","title":"`RedisModule_TryCalloc`","role":"content","text":"void *RedisModule_TryCalloc(size_t nmemb, size_t size);\n\n**Available since:** 7.4.0\n\nSimilar to [`RedisModule_Calloc`](#RedisModule_Calloc), but returns NULL in case of allocation failure, instead\nof panicking.\n\n<span id=\"RedisModule_Realloc\"></span>"},{"id":"redismodule_realloc","title":"`RedisModule_Realloc`","role":"content","text":"void* RedisModule_Realloc(void *ptr, size_t bytes);\n\n**Available since:** 4.0.0\n\nUse like `realloc()` for memory obtained with [`RedisModule_Alloc()`](#RedisModule_Alloc).\n\n<span id=\"RedisModule_TryRealloc\"></span>"},{"id":"redismodule_tryrealloc","title":"`RedisModule_TryRealloc`","role":"content","text":"void *RedisModule_TryRealloc(void *ptr, size_t bytes);\n\n**Available since:** 7.4.0\n\nSimilar to [`RedisModule_Realloc`](#RedisModule_Realloc), but returns NULL in case of allocation failure,\ninstead of panicking.\n\n<span id=\"RedisModule_Free\"></span>"},{"id":"redismodule_free","title":"`RedisModule_Free`","role":"content","text":"void RedisModule_Free(void *ptr);\n\n**Available since:** 4.0.0\n\nUse like `free()` for memory obtained by [`RedisModule_Alloc()`](#RedisModule_Alloc) and\n[`RedisModule_Realloc()`](#RedisModule_Realloc). However you should never try to free with\n[`RedisModule_Free()`](#RedisModule_Free) memory allocated with `malloc()` inside your module.\n\n<span id=\"RedisModule_Strdup\"></span>"},{"id":"redismodule_strdup","title":"`RedisModule_Strdup`","role":"content","text":"char *RedisModule_Strdup(const char *str);\n\n**Available since:** 4.0.0\n\nLike `strdup()` but returns memory allocated with [`RedisModule_Alloc()`](#RedisModule_Alloc).\n\n<span id=\"RedisModule_PoolAlloc\"></span>"},{"id":"redismodule_poolalloc","title":"`RedisModule_PoolAlloc`","role":"content","text":"void *RedisModule_PoolAlloc(RedisModuleCtx *ctx, size_t bytes);\n\n**Available since:** 4.0.0\n\nReturn heap allocated memory that will be freed automatically when the\nmodule callback function returns. Mostly suitable for small allocations\nthat are short living and must be released when the callback returns\nanyway. The returned memory is aligned to the architecture word size\nif at least word size bytes are requested, otherwise it is just\naligned to the next power of two, so for example a 3 bytes request is\n4 bytes aligned while a 2 bytes request is 2 bytes aligned.\n\nThere is no realloc style function since when this is needed to use the\npool allocator is not a good idea.\n\nThe function returns NULL if `bytes` is 0.\n\n<span id=\"section-commands-api\"></span>"},{"id":"commands-api","title":"Commands API","role":"syntax","text":"These functions are used to implement custom Redis commands.\n\nFor examples, see [https://redis.io/docs/latest/develop/reference/modules/](https://redis.io/docs/latest/develop/reference/modules/).\n\n<span id=\"RedisModule_IsKeysPositionRequest\"></span>"},{"id":"redismodule_iskeyspositionrequest","title":"`RedisModule_IsKeysPositionRequest`","role":"content","text":"int RedisModule_IsKeysPositionRequest(RedisModuleCtx *ctx);\n\n**Available since:** 4.0.0\n\nReturn non-zero if a module command, that was declared with the\nflag \"getkeys-api\", is called in a special way to get the keys positions\nand not to get executed. Otherwise zero is returned.\n\n<span id=\"RedisModule_KeyAtPosWithFlags\"></span>"},{"id":"redismodule_keyatposwithflags","title":"`RedisModule_KeyAtPosWithFlags`","role":"content","text":"void RedisModule_KeyAtPosWithFlags(RedisModuleCtx *ctx, int pos, int flags);\n\n**Available since:** 7.0.0\n\nWhen a module command is called in order to obtain the position of\nkeys, since it was flagged as \"getkeys-api\" during the registration,\nthe command implementation checks for this special call using the\n[`RedisModule_IsKeysPositionRequest()`](#RedisModule_IsKeysPositionRequest) API and uses this function in\norder to report keys.\n\nThe supported flags are the ones used by [`RedisModule_SetCommandInfo`](#RedisModule_SetCommandInfo), see `REDISMODULE_CMD_KEY_`*.\n\n\nThe following is an example of how it could be used:\n\n    if (RedisModule_IsKeysPositionRequest(ctx)) {\n        RedisModule_KeyAtPosWithFlags(ctx, 2, REDISMODULE_CMD_KEY_RO | REDISMODULE_CMD_KEY_ACCESS);\n        RedisModule_KeyAtPosWithFlags(ctx, 1, REDISMODULE_CMD_KEY_RW | REDISMODULE_CMD_KEY_UPDATE | REDISMODULE_CMD_KEY_ACCESS);\n    }\n\n Note: in the example above the get keys API could have been handled by key-specs (preferred).\n Implementing the getkeys-api is required only when is it not possible to declare key-specs that cover all keys.\n\n<span id=\"RedisModule_KeyAtPos\"></span>"},{"id":"redismodule_keyatpos","title":"`RedisModule_KeyAtPos`","role":"content","text":"void RedisModule_KeyAtPos(RedisModuleCtx *ctx, int pos);\n\n**Available since:** 4.0.0\n\nThis API existed before [`RedisModule_KeyAtPosWithFlags`](#RedisModule_KeyAtPosWithFlags) was added, now deprecated and\ncan be used for compatibility with older versions, before key-specs and flags\nwere introduced.\n\n<span id=\"RedisModule_IsChannelsPositionRequest\"></span>"},{"id":"redismodule_ischannelspositionrequest","title":"`RedisModule_IsChannelsPositionRequest`","role":"content","text":"int RedisModule_IsChannelsPositionRequest(RedisModuleCtx *ctx);\n\n**Available since:** 7.0.0\n\nReturn non-zero if a module command, that was declared with the\nflag \"getchannels-api\", is called in a special way to get the channel positions\nand not to get executed. Otherwise zero is returned.\n\n<span id=\"RedisModule_ChannelAtPosWithFlags\"></span>"},{"id":"redismodule_channelatposwithflags","title":"`RedisModule_ChannelAtPosWithFlags`","role":"content","text":"void RedisModule_ChannelAtPosWithFlags(RedisModuleCtx *ctx,\n                                           int pos,\n                                           int flags);\n\n**Available since:** 7.0.0\n\nWhen a module command is called in order to obtain the position of\nchannels, since it was flagged as \"getchannels-api\" during the\nregistration, the command implementation checks for this special call\nusing the [`RedisModule_IsChannelsPositionRequest()`](#RedisModule_IsChannelsPositionRequest) API and uses this\nfunction in order to report the channels.\n\nThe supported flags are:\n* `REDISMODULE_CMD_CHANNEL_SUBSCRIBE`: This command will subscribe to the channel.\n* `REDISMODULE_CMD_CHANNEL_UNSUBSCRIBE`: This command will unsubscribe from this channel.\n* `REDISMODULE_CMD_CHANNEL_PUBLISH`: This command will publish to this channel.\n* `REDISMODULE_CMD_CHANNEL_PATTERN`: Instead of acting on a specific channel, will act on any \n                                   channel specified by the pattern. This is the same access\n                                   used by the PSUBSCRIBE and PUNSUBSCRIBE commands available \n                                   in Redis. Not intended to be used with PUBLISH permissions.\n\nThe following is an example of how it could be used:\n\n    if (RedisModule_IsChannelsPositionRequest(ctx)) {\n        RedisModule_ChannelAtPosWithFlags(ctx, 1, REDISMODULE_CMD_CHANNEL_SUBSCRIBE | REDISMODULE_CMD_CHANNEL_PATTERN);\n        RedisModule_ChannelAtPosWithFlags(ctx, 1, REDISMODULE_CMD_CHANNEL_PUBLISH);\n    }\n\nNote: One usage of declaring channels is for evaluating ACL permissions. In this context,\nunsubscribing is always allowed, so commands will only be checked against subscribe and\npublish permissions. This is preferred over using [`RedisModule_ACLCheckChannelPermissions`](#RedisModule_ACLCheckChannelPermissions), since\nit allows the ACLs to be checked before the command is executed.\n\n<span id=\"RedisModule_CreateCommand\"></span>"},{"id":"redismodule_createcommand","title":"`RedisModule_CreateCommand`","role":"content","text":"int RedisModule_CreateCommand(RedisModuleCtx *ctx,\n                                  const char *name,\n                                  RedisModuleCmdFunc cmdfunc,\n                                  const char *strflags,\n                                  int firstkey,\n                                  int lastkey,\n                                  int keystep);\n\n**Available since:** 4.0.0\n\nRegister a new command in the Redis server, that will be handled by\ncalling the function pointer 'cmdfunc' using the RedisModule calling\nconvention.\n\nThe function returns `REDISMODULE_ERR` in these cases:\n- If creation of module command is called outside the `RedisModule_OnLoad`.\n- The specified command is already busy.\n- The command name contains some chars that are not allowed.\n- A set of invalid flags were passed.\n\nOtherwise `REDISMODULE_OK` is returned and the new command is registered.\n\nThis function must be called during the initialization of the module\ninside the `RedisModule_OnLoad()` function. Calling this function outside\nof the initialization function is not defined.\n\nThe command function type is the following:\n\n     int MyCommand_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);\n\nAnd is supposed to always return `REDISMODULE_OK`.\n\nThe set of flags 'strflags' specify the behavior of the command, and should\nbe passed as a C string composed of space separated words, like for\nexample \"write deny-oom\". The set of flags are:\n\n* **\"write\"**:     The command may modify the data set (it may also read\n                   from it).\n* **\"readonly\"**:  The command returns data from keys but never writes.\n* **\"admin\"**:     The command is an administrative command (may change\n                   replication or perform similar tasks).\n* **\"deny-oom\"**:  The command may use additional memory and should be\n                   denied during out of memory conditions.\n* **\"deny-script\"**:   Don't allow this command in Lua scripts.\n* **\"allow-loading\"**: Allow this command while the server is loading data.\n                       Only commands not interacting with the data set\n                       should be allowed to run in this mode. If not sure\n                       don't use this flag.\n* **\"pubsub\"**:    The command publishes things on Pub/Sub channels.\n* **\"random\"**:    The command may have different outputs even starting\n                   from the same input arguments and key values.\n                   Starting from Redis 7.0 this flag has been deprecated.\n                   Declaring a command as \"random\" can be done using\n                   command tips, see https://redis.io/docs/latest/develop/reference/command-tips/.\n* **\"allow-stale\"**: The command is allowed to run on slaves that don't\n                     serve stale data. Don't use if you don't know what\n                     this means.\n* **\"no-monitor\"**: Don't propagate the command on monitor. Use this if\n                    the command has sensitive data among the arguments.\n* **\"no-slowlog\"**: Don't log this command in the slowlog. Use this if\n                    the command has sensitive data among the arguments.\n* **\"fast\"**:      The command time complexity is not greater\n                   than O(log(N)) where N is the size of the collection or\n                   anything else representing the normal scalability\n                   issue with the command.\n* **\"getkeys-api\"**: The command implements the interface to return\n                     the arguments that are keys. Used when start/stop/step\n                     is not enough because of the command syntax.\n* **\"no-cluster\"**: The command should not register in Redis Cluster\n                    since is not designed to work with it because, for\n                    example, is unable to report the position of the\n                    keys, programmatically creates key names, or any\n                    other reason.\n* **\"no-auth\"**:    This command can be run by an un-authenticated client.\n                    Normally this is used by a command that is used\n                    to authenticate a client.\n* **\"may-replicate\"**: This command may generate replication traffic, even\n                       though it's not a write command.\n* **\"no-mandatory-keys\"**: All the keys this command may take are optional\n* **\"blocking\"**: The command has the potential to block the client.\n* **\"allow-busy\"**: Permit the command while the server is blocked either by\n                    a script or by a slow module command, see\n                    RedisModule_Yield.\n* **\"getchannels-api\"**: The command implements the interface to return\n                         the arguments that are channels.\n* **\"internal\"**: Internal command, one that should not be exposed to the user connections.\n                  For example, module commands that are called by the modules,\n                  commands that do not perform ACL validations (relying on earlier checks)\n* **\"touches-arbitrary-keys\"**: This command may modify arbitrary keys (i.e. not provided via argv).\n                  This flag is used so we don't wrap the replicated commands with MULTI/EXEC.\n\nThe last three parameters specify which arguments of the new command are\nRedis keys. See [https://redis.io/commands/command](https://redis.io/commands/command) for more information.\n\n* `firstkey`: One-based index of the first argument that's a key.\n              Position 0 is always the command name itself.\n              0 for commands with no keys.\n* `lastkey`:  One-based index of the last argument that's a key.\n              Negative numbers refer to counting backwards from the last\n              argument (-1 means the last argument provided)\n              0 for commands with no keys.\n* `keystep`:  Step between first and last key indexes.\n              0 for commands with no keys.\n\nThis information is used by ACL, Cluster and the `COMMAND` command.\n\nNOTE: The scheme described above serves a limited purpose and can\nonly be used to find keys that exist at constant indices.\nFor non-trivial key arguments, you may pass 0,0,0 and use\n[`RedisModule_SetCommandInfo`](#RedisModule_SetCommandInfo) to set key specs using a more advanced scheme and use\n[`RedisModule_SetCommandACLCategories`](#RedisModule_SetCommandACLCategories) to set Redis ACL categories of the commands.\n\n<span id=\"RedisModule_GetCommand\"></span>"},{"id":"redismodule_getcommand","title":"`RedisModule_GetCommand`","role":"content","text":"RedisModuleCommand *RedisModule_GetCommand(RedisModuleCtx *ctx,\n                                               const char *name);\n\n**Available since:** 7.0.0\n\nGet an opaque structure, representing a module command, by command name.\nThis structure is used in some of the command-related APIs.\n\nNULL is returned in case of the following errors:\n\n* Command not found\n* The command is not a module command\n* The command doesn't belong to the calling module\n\n<span id=\"RedisModule_CreateSubcommand\"></span>"},{"id":"redismodule_createsubcommand","title":"`RedisModule_CreateSubcommand`","role":"content","text":"int RedisModule_CreateSubcommand(RedisModuleCommand *parent,\n                                     const char *name,\n                                     RedisModuleCmdFunc cmdfunc,\n                                     const char *strflags,\n                                     int firstkey,\n                                     int lastkey,\n                                     int keystep);\n\n**Available since:** 7.0.0\n\nVery similar to [`RedisModule_CreateCommand`](#RedisModule_CreateCommand) except that it is used to create\na subcommand, associated with another, container, command.\n\nExample: If a module has a configuration command, MODULE.CONFIG, then\nGET and SET should be individual subcommands, while MODULE.CONFIG is\na command, but should not be registered with a valid `funcptr`:\n\n     if (RedisModule_CreateCommand(ctx,\"module.config\",NULL,\"\",0,0,0) == REDISMODULE_ERR)\n         return REDISMODULE_ERR;\n\n     RedisModuleCommand *parent = RedisModule_GetCommand(ctx,,\"module.config\");\n\n     if (RedisModule_CreateSubcommand(parent,\"set\",cmd_config_set,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\n     if (RedisModule_CreateSubcommand(parent,\"get\",cmd_config_get,\"\",0,0,0) == REDISMODULE_ERR)\n        return REDISMODULE_ERR;\n\nReturns `REDISMODULE_OK` on success and `REDISMODULE_ERR` in case of the following errors:\n\n* Error while parsing `strflags`\n* Command is marked as `no-cluster` but cluster mode is enabled\n* `parent` is already a subcommand (we do not allow more than one level of command nesting)\n* `parent` is a command with an implementation (`RedisModuleCmdFunc`) (A parent command should be a pure container of subcommands)\n* `parent` already has a subcommand called `name`\n* Creating a subcommand is called outside of `RedisModule_OnLoad`.\n\n<span id=\"RedisModule_AddACLCategory\"></span>"},{"id":"redismodule_addaclcategory","title":"`RedisModule_AddACLCategory`","role":"content","text":"int RedisModule_AddACLCategory(RedisModuleCtx *ctx, const char *name);\n\n**Available since:** 7.4.0\n\n[`RedisModule_AddACLCategory`](#RedisModule_AddACLCategory) can be used to add new ACL command categories. Category names\ncan only contain alphanumeric characters, underscores, or dashes. Categories can only be added\nduring the `RedisModule_OnLoad` function. Once a category has been added, it can not be removed. \nAny module can register a command to any added categories using [`RedisModule_SetCommandACLCategories`](#RedisModule_SetCommandACLCategories).\n\nReturns:\n- `REDISMODULE_OK` on successfully adding the new ACL category. \n- `REDISMODULE_ERR` on failure.\n\nOn error the errno is set to:\n- EINVAL if the name contains invalid characters.\n- EBUSY if the category name already exists.\n- ENOMEM if the number of categories reached the max limit of 64 categories.\n\n<span id=\"RedisModule_SetCommandACLCategories\"></span>"},{"id":"redismodule_setcommandaclcategories","title":"`RedisModule_SetCommandACLCategories`","role":"content","text":"int RedisModule_SetCommandACLCategories(RedisModuleCommand *command,\n                                            const char *aclflags);\n\n**Available since:** 7.2.0\n\n[`RedisModule_SetCommandACLCategories`](#RedisModule_SetCommandACLCategories) can be used to set ACL categories to module\ncommands and subcommands. The set of ACL categories should be passed as\na space separated C string 'aclflags'.\n\nExample, the acl flags 'write slow' marks the command as part of the write and \nslow ACL categories.\n\nOn success `REDISMODULE_OK` is returned. On error `REDISMODULE_ERR` is returned.\n\nThis function can only be called during the `RedisModule_OnLoad` function. If called\noutside of this function, an error is returned.\n\n<span id=\"RedisModule_SetCommandInfo\"></span>"},{"id":"redismodule_setcommandinfo","title":"`RedisModule_SetCommandInfo`","role":"content","text":"int RedisModule_SetCommandInfo(RedisModuleCommand *command,\n                                   const RedisModuleCommandInfo *info);\n\n**Available since:** 7.0.0\n\nSet additional command information.\n\nAffects the output of `COMMAND`, `COMMAND INFO` and `COMMAND DOCS`, Cluster,\nACL and is used to filter commands with the wrong number of arguments before\nthe call reaches the module code.\n\nThis function can be called after creating a command using [`RedisModule_CreateCommand`](#RedisModule_CreateCommand)\nand fetching the command pointer using [`RedisModule_GetCommand`](#RedisModule_GetCommand). The information can\nonly be set once for each command and has the following structure:\n\n    typedef struct RedisModuleCommandInfo {\n        const RedisModuleCommandInfoVersion *version;\n        const char *summary;\n        const char *complexity;\n        const char *since;\n        RedisModuleCommandHistoryEntry *history;\n        const char *tips;\n        int arity;\n        RedisModuleCommandKeySpec *key_specs;\n        RedisModuleCommandArg *args;\n    } RedisModuleCommandInfo;\n\nAll fields except `version` are optional. Explanation of the fields:\n\n- `version`: This field enables compatibility with different Redis versions.\n  Always set this field to `REDISMODULE_COMMAND_INFO_VERSION`.\n\n- `summary`: A short description of the command (optional).\n\n- `complexity`: Complexity description (optional).\n\n- `since`: The version where the command was introduced (optional).\n  Note: The version specified should be the module's, not Redis version.\n\n- `history`: An array of `RedisModuleCommandHistoryEntry` (optional), which is\n  a struct with the following fields:\n\n        const char *since;\n        const char *changes;\n\n    `since` is a version string and `changes` is a string describing the\n    changes. The array is terminated by a zeroed entry, i.e. an entry with\n    both strings set to NULL.\n\n- `tips`: A string of space-separated tips regarding this command, meant for\n  clients and proxies. See [https://redis.io/docs/latest/develop/reference/command-tips/](https://redis.io/docs/latest/develop/reference/command-tips/).\n\n- `arity`: Number of arguments, including the command name itself. A positive\n  number specifies an exact number of arguments and a negative number\n  specifies a minimum number of arguments, so use -N to say >= N. Redis\n  validates a call before passing it to a module, so this can replace an\n  arity check inside the module command implementation. A value of 0 (or an\n  omitted arity field) is equivalent to -2 if the command has sub commands\n  and -1 otherwise.\n\n- `key_specs`: An array of `RedisModuleCommandKeySpec`, terminated by an\n  element memset to zero. This is a scheme that tries to describe the\n  positions of key arguments better than the old [`RedisModule_CreateCommand`](#RedisModule_CreateCommand) arguments\n  `firstkey`, `lastkey`, `keystep` and is needed if those three are not\n  enough to describe the key positions. There are two steps to retrieve key\n  positions: *begin search* (BS) in which index should find the first key and\n  *find keys* (FK) which, relative to the output of BS, describes how can we\n  will which arguments are keys. Additionally, there are key specific flags.\n\n    Key-specs cause the triplet (firstkey, lastkey, keystep) given in\n    RedisModule_CreateCommand to be recomputed, but it is still useful to provide\n    these three parameters in RedisModule_CreateCommand, to better support old Redis\n    versions where RedisModule_SetCommandInfo is not available.\n\n    Note that key-specs don't fully replace the \"getkeys-api\" (see\n    RedisModule_CreateCommand, RedisModule_IsKeysPositionRequest and RedisModule_KeyAtPosWithFlags) so\n    it may be a good idea to supply both key-specs and implement the\n    getkeys-api.\n\n    A key-spec has the following structure:\n\n        typedef struct RedisModuleCommandKeySpec {\n            const char *notes;\n            uint64_t flags;\n            RedisModuleKeySpecBeginSearchType begin_search_type;\n            union {\n                struct {\n                    int pos;\n                } index;\n                struct {\n                    const char *keyword;\n                    int startfrom;\n                } keyword;\n            } bs;\n            RedisModuleKeySpecFindKeysType find_keys_type;\n            union {\n                struct {\n                    int lastkey;\n                    int keystep;\n                    int limit;\n                } range;\n                struct {\n                    int keynumidx;\n                    int firstkey;\n                    int keystep;\n                } keynum;\n            } fk;\n        } RedisModuleCommandKeySpec;\n\n    Explanation of the fields of RedisModuleCommandKeySpec:\n\n    * `notes`: Optional notes or clarifications about this key spec.\n\n    * `flags`: A bitwise or of key-spec flags described below.\n\n    * `begin_search_type`: This describes how the first key is discovered.\n      There are two ways to determine the first key:\n\n        * `REDISMODULE_KSPEC_BS_UNKNOWN`: There is no way to tell where the\n          key args start.\n        * `REDISMODULE_KSPEC_BS_INDEX`: Key args start at a constant index.\n        * `REDISMODULE_KSPEC_BS_KEYWORD`: Key args start just after a\n          specific keyword.\n\n    * `bs`: This is a union in which the `index` or `keyword` branch is used\n      depending on the value of the `begin_search_type` field.\n\n        * `bs.index.pos`: The index from which we start the search for keys.\n          (`REDISMODULE_KSPEC_BS_INDEX` only.)\n\n        * `bs.keyword.keyword`: The keyword (string) that indicates the\n          beginning of key arguments. (`REDISMODULE_KSPEC_BS_KEYWORD` only.)\n\n        * `bs.keyword.startfrom`: An index in argv from which to start\n          searching. Can be negative, which means start search from the end,\n          in reverse. Example: -2 means to start in reverse from the\n          penultimate argument. (`REDISMODULE_KSPEC_BS_KEYWORD` only.)\n\n    * `find_keys_type`: After the \"begin search\", this describes which\n      arguments are keys. The strategies are:\n\n        * `REDISMODULE_KSPEC_BS_UNKNOWN`: There is no way to tell where the\n          key args are located.\n        * `REDISMODULE_KSPEC_FK_RANGE`: Keys end at a specific index (or\n          relative to the last argument).\n        * `REDISMODULE_KSPEC_FK_KEYNUM`: There's an argument that contains\n          the number of key args somewhere before the keys themselves.\n\n      `find_keys_type` and `fk` can be omitted if this keyspec describes\n      exactly one key.\n\n    * `fk`: This is a union in which the `range` or `keynum` branch is used\n      depending on the value of the `find_keys_type` field.\n\n        * `fk.range` (for `REDISMODULE_KSPEC_FK_RANGE`): A struct with the\n          following fields:\n\n            * `lastkey`: Index of the last key relative to the result of the\n              begin search step. Can be negative, in which case it's not\n              relative. -1 indicates the last argument, -2 one before the\n              last and so on.\n\n            * `keystep`: How many arguments should we skip after finding a\n              key, in order to find the next one?\n\n            * `limit`: If `lastkey` is -1, we use `limit` to stop the search\n              by a factor. 0 and 1 mean no limit. 2 means 1/2 of the\n              remaining args, 3 means 1/3, and so on.\n\n        * `fk.keynum` (for `REDISMODULE_KSPEC_FK_KEYNUM`): A struct with the\n          following fields:\n\n            * `keynumidx`: Index of the argument containing the number of\n              keys to come, relative to the result of the begin search step.\n\n            * `firstkey`: Index of the fist key relative to the result of the\n              begin search step. (Usually it's just after `keynumidx`, in\n              which case it should be set to `keynumidx + 1`.)\n\n            * `keystep`: How many arguments should we skip after finding a\n              key, in order to find the next one?\n\n    Key-spec flags:\n\n    The first four refer to what the command actually does with the *value or\n    metadata of the key*, and not necessarily the user data or how it affects\n    it. Each key-spec may must have exactly one of these. Any operation\n    that's not distinctly deletion, overwrite or read-only would be marked as\n    RW.\n\n    * `REDISMODULE_CMD_KEY_RO`: Read-Only. Reads the value of the key, but\n      doesn't necessarily return it.\n\n    * `REDISMODULE_CMD_KEY_RW`: Read-Write. Modifies the data stored in the\n      value of the key or its metadata.\n\n    * `REDISMODULE_CMD_KEY_OW`: Overwrite. Overwrites the data stored in the\n      value of the key.\n\n    * `REDISMODULE_CMD_KEY_RM`: Deletes the key.\n\n    The next four refer to *user data inside the value of the key*, not the\n    metadata like LRU, type, cardinality. It refers to the logical operation\n    on the user's data (actual input strings or TTL), being\n    used/returned/copied/changed. It doesn't refer to modification or\n    returning of metadata (like type, count, presence of data). ACCESS can be\n    combined with one of the write operations INSERT, DELETE or UPDATE. Any\n    write that's not an INSERT or a DELETE would be UPDATE.\n\n    * `REDISMODULE_CMD_KEY_ACCESS`: Returns, copies or uses the user data\n      from the value of the key.\n\n    * `REDISMODULE_CMD_KEY_UPDATE`: Updates data to the value, new value may\n      depend on the old value.\n\n    * `REDISMODULE_CMD_KEY_INSERT`: Adds data to the value with no chance of\n      modification or deletion of existing data.\n\n    * `REDISMODULE_CMD_KEY_DELETE`: Explicitly deletes some content from the\n      value of the key.\n\n    Other flags:\n\n    * `REDISMODULE_CMD_KEY_NOT_KEY`: The key is not actually a key, but \n      should be routed in cluster mode as if it was a key.\n\n    * `REDISMODULE_CMD_KEY_INCOMPLETE`: The keyspec might not point out all\n      the keys it should cover.\n\n    * `REDISMODULE_CMD_KEY_VARIABLE_FLAGS`: Some keys might have different\n      flags depending on arguments.\n\n- `args`: An array of `RedisModuleCommandArg`, terminated by an element memset\n  to zero. `RedisModuleCommandArg` is a structure with at the fields described\n  below.\n\n        typedef struct RedisModuleCommandArg {\n            const char *name;\n            RedisModuleCommandArgType type;\n            int key_spec_index;\n            const char *token;\n            const char *summary;\n            const char *since;\n            int flags;\n            struct RedisModuleCommandArg *subargs;\n        } RedisModuleCommandArg;\n\n    Explanation of the fields:\n\n    * `name`: Name of the argument.\n\n    * `type`: The type of the argument. See below for details. The types\n      `REDISMODULE_ARG_TYPE_ONEOF` and `REDISMODULE_ARG_TYPE_BLOCK` require\n      an argument to have sub-arguments, i.e. `subargs`.\n\n    * `key_spec_index`: If the `type` is `REDISMODULE_ARG_TYPE_KEY` you must\n      provide the index of the key-spec associated with this argument. See\n      `key_specs` above. If the argument is not a key, you may specify -1.\n\n    * `token`: The token preceding the argument (optional). Example: the\n      argument `seconds` in `SET` has a token `EX`. If the argument consists\n      of only a token (for example `NX` in `SET`) the type should be\n      `REDISMODULE_ARG_TYPE_PURE_TOKEN` and `value` should be NULL.\n\n    * `summary`: A short description of the argument (optional).\n\n    * `since`: The first version which included this argument (optional).\n\n    * `flags`: A bitwise or of the macros `REDISMODULE_CMD_ARG_*`. See below.\n\n    * `value`: The display-value of the argument. This string is what should\n      be displayed when creating the command syntax from the output of\n      `COMMAND`. If `token` is not NULL, it should also be displayed.\n\n    Explanation of `RedisModuleCommandArgType`:\n\n    * `REDISMODULE_ARG_TYPE_STRING`: String argument.\n    * `REDISMODULE_ARG_TYPE_INTEGER`: Integer argument.\n    * `REDISMODULE_ARG_TYPE_DOUBLE`: Double-precision float argument.\n    * `REDISMODULE_ARG_TYPE_KEY`: String argument representing a keyname.\n    * `REDISMODULE_ARG_TYPE_PATTERN`: String, but regex pattern.\n    * `REDISMODULE_ARG_TYPE_UNIX_TIME`: Integer, but Unix timestamp.\n    * `REDISMODULE_ARG_TYPE_PURE_TOKEN`: Argument doesn't have a placeholder.\n      It's just a token without a value. Example: the `KEEPTTL` option of the\n      `SET` command.\n    * `REDISMODULE_ARG_TYPE_ONEOF`: Used when the user can choose only one of\n      a few sub-arguments. Requires `subargs`. Example: the `NX` and `XX`\n      options of `SET`.\n    * `REDISMODULE_ARG_TYPE_BLOCK`: Used when one wants to group together\n      several sub-arguments, usually to apply something on all of them, like\n      making the entire group \"optional\". Requires `subargs`. Example: the\n      `LIMIT offset count` parameters in `ZRANGE`.\n\n    Explanation of the command argument flags:\n\n    * `REDISMODULE_CMD_ARG_OPTIONAL`: The argument is optional (like GET in\n      the SET command).\n    * `REDISMODULE_CMD_ARG_MULTIPLE`: The argument may repeat itself (like\n      key in DEL).\n    * `REDISMODULE_CMD_ARG_MULTIPLE_TOKEN`: The argument may repeat itself,\n      and so does its token (like `GET pattern` in SORT).\n\nOn success `REDISMODULE_OK` is returned. On error `REDISMODULE_ERR` is returned\nand `errno` is set to EINVAL if invalid info was provided or EEXIST if info\nhas already been set. If the info is invalid, a warning is logged explaining\nwhich part of the info is invalid and why.\n\n<span id=\"section-module-information-and-time-measurement\"></span>"},{"id":"module-information-and-time-measurement","title":"Module information and time measurement","role":"content","text":"<span id=\"RedisModule_IsModuleNameBusy\"></span>"},{"id":"redismodule_ismodulenamebusy","title":"`RedisModule_IsModuleNameBusy`","role":"content","text":"int RedisModule_IsModuleNameBusy(const char *name);\n\n**Available since:** 4.0.3\n\nReturn non-zero if the module name is busy.\nOtherwise zero is returned.\n\n<span id=\"RedisModule_Milliseconds\"></span>"},{"id":"redismodule_milliseconds","title":"`RedisModule_Milliseconds`","role":"content","text":"mstime_t RedisModule_Milliseconds(void);\n\n**Available since:** 4.0.0\n\nReturn the current UNIX time in milliseconds.\n\n<span id=\"RedisModule_MonotonicMicroseconds\"></span>"},{"id":"redismodule_monotonicmicroseconds","title":"`RedisModule_MonotonicMicroseconds`","role":"content","text":"uint64_t RedisModule_MonotonicMicroseconds(void);\n\n**Available since:** 7.0.0\n\nReturn counter of micro-seconds relative to an arbitrary point in time.\n\n<span id=\"RedisModule_Microseconds\"></span>"},{"id":"redismodule_microseconds","title":"`RedisModule_Microseconds`","role":"content","text":"ustime_t RedisModule_Microseconds(void);\n\n**Available since:** 7.2.0\n\nReturn the current UNIX time in microseconds\n\n<span id=\"RedisModule_CachedMicroseconds\"></span>"},{"id":"redismodule_cachedmicroseconds","title":"`RedisModule_CachedMicroseconds`","role":"content","text":"ustime_t RedisModule_CachedMicroseconds(void);\n\n**Available since:** 7.2.0\n\nReturn the cached UNIX time in microseconds.\nIt is updated in the server cron job and before executing a command.\nIt is useful for complex call stacks, such as a command causing a\nkey space notification, causing a module to execute a [`RedisModule_Call`](#RedisModule_Call),\ncausing another notification, etc.\nIt makes sense that all this callbacks would use the same clock.\n\n<span id=\"RedisModule_BlockedClientMeasureTimeStart\"></span>"},{"id":"redismodule_blockedclientmeasuretimestart","title":"`RedisModule_BlockedClientMeasureTimeStart`","role":"content","text":"int RedisModule_BlockedClientMeasureTimeStart(RedisModuleBlockedClient *bc);\n\n**Available since:** 6.2.0\n\nMark a point in time that will be used as the start time to calculate\nthe elapsed execution time when [`RedisModule_BlockedClientMeasureTimeEnd()`](#RedisModule_BlockedClientMeasureTimeEnd) is called.\nWithin the same command, you can call multiple times\n[`RedisModule_BlockedClientMeasureTimeStart()`](#RedisModule_BlockedClientMeasureTimeStart) and [`RedisModule_BlockedClientMeasureTimeEnd()`](#RedisModule_BlockedClientMeasureTimeEnd)\nto accumulate independent time intervals to the background duration.\nThis method always return `REDISMODULE_OK`.\n\nThis function is not thread safe, If used in module thread and blocked callback (possibly main thread)\nsimultaneously, it's recommended to protect them with lock owned by caller instead of GIL.\n\n<span id=\"RedisModule_BlockedClientMeasureTimeEnd\"></span>"},{"id":"redismodule_blockedclientmeasuretimeend","title":"`RedisModule_BlockedClientMeasureTimeEnd`","role":"content","text":"int RedisModule_BlockedClientMeasureTimeEnd(RedisModuleBlockedClient *bc);\n\n**Available since:** 6.2.0\n\nMark a point in time that will be used as the end time\nto calculate the elapsed execution time.\nOn success `REDISMODULE_OK` is returned.\nThis method only returns `REDISMODULE_ERR` if no start time was\npreviously defined ( meaning [`RedisModule_BlockedClientMeasureTimeStart`](#RedisModule_BlockedClientMeasureTimeStart) was not called ).\n\nThis function is not thread safe, If used in module thread and blocked callback (possibly main thread)\nsimultaneously, it's recommended to protect them with lock owned by caller instead of GIL.\n\n<span id=\"RedisModule_Yield\"></span>"},{"id":"redismodule_yield","title":"`RedisModule_Yield`","role":"content","text":"void RedisModule_Yield(RedisModuleCtx *ctx, int flags, const char *busy_reply);\n\n**Available since:** 7.0.0\n\nThis API allows modules to let Redis process background tasks, and some\ncommands during long blocking execution of a module command.\nThe module can call this API periodically.\nThe flags is a bit mask of these:\n\n- `REDISMODULE_YIELD_FLAG_NONE`: No special flags, can perform some background\n                                 operations, but not process client commands.\n- `REDISMODULE_YIELD_FLAG_CLIENTS`: Redis can also process client commands.\n\nThe `busy_reply` argument is optional, and can be used to control the verbose\nerror string after the `-BUSY` error code.\n\nWhen the `REDISMODULE_YIELD_FLAG_CLIENTS` is used, Redis will only start\nprocessing client commands after the time defined by the\n`busy-reply-threshold` config, in which case Redis will start rejecting most\ncommands with `-BUSY` error, but allow the ones marked with the `allow-busy`\nflag to be executed.\nThis API can also be used in thread safe context (while locked), and during\nloading (in the `rdb_load` callback, in which case it'll reject commands with\nthe -LOADING error)\n\n<span id=\"RedisModule_SetModuleOptions\"></span>"},{"id":"redismodule_setmoduleoptions","title":"`RedisModule_SetModuleOptions`","role":"content","text":"void RedisModule_SetModuleOptions(RedisModuleCtx *ctx, int options);\n\n**Available since:** 6.0.0\n\nSet flags defining capabilities or behavior bit flags.\n\n`REDISMODULE_OPTIONS_HANDLE_IO_ERRORS`:\nGenerally, modules don't need to bother with this, as the process will just\nterminate if a read error happens, however, setting this flag would allow\nrepl-diskless-load to work if enabled.\nThe module should use [`RedisModule_IsIOError`](#RedisModule_IsIOError) after reads, before using the\ndata that was read, and in case of error, propagate it upwards, and also be\nable to release the partially populated value and all it's allocations.\n\n`REDISMODULE_OPTION_NO_IMPLICIT_SIGNAL_MODIFIED`:\nSee [`RedisModule_SignalModifiedKey()`](#RedisModule_SignalModifiedKey).\n\n`REDISMODULE_OPTIONS_HANDLE_REPL_ASYNC_LOAD`:\nSetting this flag indicates module awareness of diskless async replication (repl-diskless-load=swapdb)\nand that redis could be serving reads during replication instead of blocking with LOADING status.\n\n`REDISMODULE_OPTIONS_ALLOW_NESTED_KEYSPACE_NOTIFICATIONS`:\nDeclare that the module wants to get nested key-space notifications.\nBy default, Redis will not fire key-space notifications that happened inside\na key-space notification callback. This flag allows to change this behavior\nand fire nested key-space notifications. Notice: if enabled, the module\nshould protected itself from infinite recursion.\n\n<span id=\"RedisModule_SignalModifiedKey\"></span>"},{"id":"redismodule_signalmodifiedkey","title":"`RedisModule_SignalModifiedKey`","role":"content","text":"int RedisModule_SignalModifiedKey(RedisModuleCtx *ctx,\n                                      RedisModuleString *keyname);\n\n**Available since:** 6.0.0\n\nSignals that the key is modified from user's perspective (i.e. invalidate WATCH\nand client side caching).\n\nThis is done automatically when a key opened for writing is closed, unless\nthe option `REDISMODULE_OPTION_NO_IMPLICIT_SIGNAL_MODIFIED` has been set using\n[`RedisModule_SetModuleOptions()`](#RedisModule_SetModuleOptions).\n\n<span id=\"section-automatic-memory-management-for-modules\"></span>"},{"id":"automatic-memory-management-for-modules","title":"Automatic memory management for modules","role":"content","text":"<span id=\"RedisModule_AutoMemory\"></span>"},{"id":"redismodule_automemory","title":"`RedisModule_AutoMemory`","role":"content","text":"void RedisModule_AutoMemory(RedisModuleCtx *ctx);\n\n**Available since:** 4.0.0\n\nEnable automatic memory management.\n\nThe function must be called as the first function of a command implementation\nthat wants to use automatic memory.\n\nWhen enabled, automatic memory management tracks and automatically frees\nkeys, call replies and Redis string objects once the command returns. In most\ncases this eliminates the need of calling the following functions:\n\n1. [`RedisModule_CloseKey()`](#RedisModule_CloseKey)\n2. [`RedisModule_FreeCallReply()`](#RedisModule_FreeCallReply)\n3. [`RedisModule_FreeString()`](#RedisModule_FreeString)\n\nThese functions can still be used with automatic memory management enabled,\nto optimize loops that make numerous allocations for example.\n\n<span id=\"section-string-objects-apis\"></span>"},{"id":"string-objects-apis","title":"String objects APIs","role":"content","text":"<span id=\"RedisModule_CreateString\"></span>"},{"id":"redismodule_createstring","title":"`RedisModule_CreateString`","role":"content","text":"RedisModuleString *RedisModule_CreateString(RedisModuleCtx *ctx,\n                                                const char *ptr,\n                                                size_t len);\n\n**Available since:** 4.0.0\n\nCreate a new module string object. The returned string must be freed\nwith [`RedisModule_FreeString()`](#RedisModule_FreeString), unless automatic memory is enabled.\n\nThe string is created by copying the `len` bytes starting\nat `ptr`. No reference is retained to the passed buffer.\n\nThe module context 'ctx' is optional and may be NULL if you want to create\na string out of the context scope. However in that case, the automatic\nmemory management will not be available, and the string memory must be\nmanaged manually.\n\n<span id=\"RedisModule_CreateStringPrintf\"></span>"},{"id":"redismodule_createstringprintf","title":"`RedisModule_CreateStringPrintf`","role":"content","text":"RedisModuleString *RedisModule_CreateStringPrintf(RedisModuleCtx *ctx,\n                                                      const char *fmt,\n                                                      ...);\n\n**Available since:** 4.0.0\n\nCreate a new module string object from a printf format and arguments.\nThe returned string must be freed with [`RedisModule_FreeString()`](#RedisModule_FreeString), unless\nautomatic memory is enabled.\n\nThe string is created using the sds formatter function `sdscatvprintf()`.\n\nThe passed context 'ctx' may be NULL if necessary, see the\n[`RedisModule_CreateString()`](#RedisModule_CreateString) documentation for more info.\n\n<span id=\"RedisModule_CreateStringFromLongLong\"></span>"},{"id":"redismodule_createstringfromlonglong","title":"`RedisModule_CreateStringFromLongLong`","role":"content","text":"RedisModuleString *RedisModule_CreateStringFromLongLong(RedisModuleCtx *ctx,\n                                                            long long ll);\n\n**Available since:** 4.0.0\n\nLike [`RedisModule_CreateString()`](#RedisModule_CreateString), but creates a string starting from a `long long`\ninteger instead of taking a buffer and its length.\n\nThe returned string must be released with [`RedisModule_FreeString()`](#RedisModule_FreeString) or by\nenabling automatic memory management.\n\nThe passed context 'ctx' may be NULL if necessary, see the\n[`RedisModule_CreateString()`](#RedisModule_CreateString) documentation for more info.\n\n<span id=\"RedisModule_CreateStringFromULongLong\"></span>"},{"id":"redismodule_createstringfromulonglong","title":"`RedisModule_CreateStringFromULongLong`","role":"content","text":"RedisModuleString *RedisModule_CreateStringFromULongLong(RedisModuleCtx *ctx,\n                                                             unsigned long long ull);\n\n**Available since:** 7.0.3\n\nLike [`RedisModule_CreateString()`](#RedisModule_CreateString), but creates a string starting from a `unsigned long long`\ninteger instead of taking a buffer and its length.\n\nThe returned string must be released with [`RedisModule_FreeString()`](#RedisModule_FreeString) or by\nenabling automatic memory management.\n\nThe passed context 'ctx' may be NULL if necessary, see the\n[`RedisModule_CreateString()`](#RedisModule_CreateString) documentation for more info.\n\n<span id=\"RedisModule_CreateStringFromDouble\"></span>"},{"id":"redismodule_createstringfromdouble","title":"`RedisModule_CreateStringFromDouble`","role":"content","text":"RedisModuleString *RedisModule_CreateStringFromDouble(RedisModuleCtx *ctx,\n                                                          double d);\n\n**Available since:** 6.0.0\n\nLike [`RedisModule_CreateString()`](#RedisModule_CreateString), but creates a string starting from a double\ninstead of taking a buffer and its length.\n\nThe returned string must be released with [`RedisModule_FreeString()`](#RedisModule_FreeString) or by\nenabling automatic memory management.\n\n<span id=\"RedisModule_CreateStringFromLongDouble\"></span>"},{"id":"redismodule_createstringfromlongdouble","title":"`RedisModule_CreateStringFromLongDouble`","role":"content","text":"RedisModuleString *RedisModule_CreateStringFromLongDouble(RedisModuleCtx *ctx,\n                                                              long double ld,\n                                                              int humanfriendly);\n\n**Available since:** 6.0.0\n\nLike [`RedisModule_CreateString()`](#RedisModule_CreateString), but creates a string starting from a long\ndouble.\n\nThe returned string must be released with [`RedisModule_FreeString()`](#RedisModule_FreeString) or by\nenabling automatic memory management.\n\nThe passed context 'ctx' may be NULL if necessary, see the\n[`RedisModule_CreateString()`](#RedisModule_CreateString) documentation for more info.\n\n<span id=\"RedisModule_CreateStringFromString\"></span>"},{"id":"redismodule_createstringfromstring","title":"`RedisModule_CreateStringFromString`","role":"content","text":"RedisModuleString *RedisModule_CreateStringFromString(RedisModuleCtx *ctx,\n                                                          const RedisModuleString *str);\n\n**Available since:** 4.0.0\n\nLike [`RedisModule_CreateString()`](#RedisModule_CreateString), but creates a string starting from another\n`RedisModuleString`.\n\nThe returned string must be released with [`RedisModule_FreeString()`](#RedisModule_FreeString) or by\nenabling automatic memory management.\n\nThe passed context 'ctx' may be NULL if necessary, see the\n[`RedisModule_CreateString()`](#RedisModule_CreateString) documentation for more info.\n\n<span id=\"RedisModule_CreateStringFromStreamID\"></span>"},{"id":"redismodule_createstringfromstreamid","title":"`RedisModule_CreateStringFromStreamID`","role":"content","text":"RedisModuleString *RedisModule_CreateStringFromStreamID(RedisModuleCtx *ctx,\n                                                            const RedisModuleStreamID *id);\n\n**Available since:** 6.2.0\n\nCreates a string from a stream ID. The returned string must be released with\n[`RedisModule_FreeString()`](#RedisModule_FreeString), unless automatic memory is enabled.\n\nThe passed context `ctx` may be NULL if necessary. See the\n[`RedisModule_CreateString()`](#RedisModule_CreateString) documentation for more info.\n\n<span id=\"RedisModule_FreeString\"></span>"},{"id":"redismodule_freestring","title":"`RedisModule_FreeString`","role":"content","text":"void RedisModule_FreeString(RedisModuleCtx *ctx, RedisModuleString *str);\n\n**Available since:** 4.0.0\n\nFree a module string object obtained with one of the Redis modules API calls\nthat return new string objects.\n\nIt is possible to call this function even when automatic memory management\nis enabled. In that case the string will be released ASAP and removed\nfrom the pool of string to release at the end.\n\nIf the string was created with a NULL context 'ctx', it is also possible to\npass ctx as NULL when releasing the string (but passing a context will not\ncreate any issue). Strings created with a context should be freed also passing\nthe context, so if you want to free a string out of context later, make sure\nto create it using a NULL context.\n\nThis API is not thread safe, access to these retained strings (if they originated\nfrom a client command arguments) must be done with GIL locked.\n\n<span id=\"RedisModule_RetainString\"></span>"},{"id":"redismodule_retainstring","title":"`RedisModule_RetainString`","role":"content","text":"void RedisModule_RetainString(RedisModuleCtx *ctx, RedisModuleString *str);\n\n**Available since:** 4.0.0\n\nEvery call to this function, will make the string 'str' requiring\nan additional call to [`RedisModule_FreeString()`](#RedisModule_FreeString) in order to really\nfree the string. Note that the automatic freeing of the string obtained\nenabling modules automatic memory management counts for one\n[`RedisModule_FreeString()`](#RedisModule_FreeString) call (it is just executed automatically).\n\nNormally you want to call this function when, at the same time\nthe following conditions are true:\n\n1. You have automatic memory management enabled.\n2. You want to create string objects.\n3. Those string objects you create need to live *after* the callback\n   function(for example a command implementation) creating them returns.\n\nUsually you want this in order to store the created string object\ninto your own data structure, for example when implementing a new data\ntype.\n\nNote that when memory management is turned off, you don't need\nany call to RetainString() since creating a string will always result\ninto a string that lives after the callback function returns, if\nno FreeString() call is performed.\n\nIt is possible to call this function with a NULL context.\n\nWhen strings are going to be retained for an extended duration, it is good\npractice to also call [`RedisModule_TrimStringAllocation()`](#RedisModule_TrimStringAllocation) in order to\noptimize memory usage.\n\nThreaded modules that reference retained strings from other threads *must*\nexplicitly trim the allocation as soon as the string is retained. Not doing\nso may result with automatic trimming which is not thread safe.\n\nThis API is not thread safe, access to these retained strings (if they originated\nfrom a client command arguments) must be done with GIL locked.\n\n<span id=\"RedisModule_HoldString\"></span>"},{"id":"redismodule_holdstring","title":"`RedisModule_HoldString`","role":"content","text":"RedisModuleString* RedisModule_HoldString(RedisModuleCtx *ctx,\n                                              RedisModuleString *str);\n\n**Available since:** 6.0.7\n\n\nThis function can be used instead of [`RedisModule_RetainString()`](#RedisModule_RetainString).\nThe main difference between the two is that this function will always\nsucceed, whereas [`RedisModule_RetainString()`](#RedisModule_RetainString) may fail because of an\nassertion.\n\nThe function returns a pointer to `RedisModuleString`, which is owned\nby the caller. It requires a call to [`RedisModule_FreeString()`](#RedisModule_FreeString) to free\nthe string when automatic memory management is disabled for the context.\nWhen automatic memory management is enabled, you can either call\n[`RedisModule_FreeString()`](#RedisModule_FreeString) or let the automation free it.\n\nThis function is more efficient than [`RedisModule_CreateStringFromString()`](#RedisModule_CreateStringFromString)\nbecause whenever possible, it avoids copying the underlying\n`RedisModuleString`. The disadvantage of using this function is that it\nmight not be possible to use [`RedisModule_StringAppendBuffer()`](#RedisModule_StringAppendBuffer) on the\nreturned `RedisModuleString`.\n\nIt is possible to call this function with a NULL context.\n\nWhen strings are going to be held for an extended duration, it is good\npractice to also call [`RedisModule_TrimStringAllocation()`](#RedisModule_TrimStringAllocation) in order to\noptimize memory usage.\n\nThreaded modules that reference held strings from other threads *must*\nexplicitly trim the allocation as soon as the string is held. Not doing\nso may result with automatic trimming which is not thread safe.\n\nThis API is not thread safe, access to these retained strings (if they originated\nfrom a client command arguments) must be done with GIL locked.\n\n<span id=\"RedisModule_StringPtrLen\"></span>"},{"id":"redismodule_stringptrlen","title":"`RedisModule_StringPtrLen`","role":"content","text":"const char *RedisModule_StringPtrLen(const RedisModuleString *str,\n                                         size_t *len);\n\n**Available since:** 4.0.0\n\nGiven a string module object, this function returns the string pointer\nand length of the string. The returned pointer and length should only\nbe used for read only accesses and never modified.\n\n<span id=\"RedisModule_StringToLongLong\"></span>"},{"id":"redismodule_stringtolonglong","title":"`RedisModule_StringToLongLong`","role":"content","text":"int RedisModule_StringToLongLong(const RedisModuleString *str, long long *ll);\n\n**Available since:** 4.0.0\n\nConvert the string into a `long long` integer, storing it at `*ll`.\nReturns `REDISMODULE_OK` on success. If the string can't be parsed\nas a valid, strict `long long` (no spaces before/after), `REDISMODULE_ERR`\nis returned.\n\n<span id=\"RedisModule_StringToULongLong\"></span>"},{"id":"redismodule_stringtoulonglong","title":"`RedisModule_StringToULongLong`","role":"content","text":"int RedisModule_StringToULongLong(const RedisModuleString *str,\n                                      unsigned long long *ull);\n\n**Available since:** 7.0.3\n\nConvert the string into a `unsigned long long` integer, storing it at `*ull`.\nReturns `REDISMODULE_OK` on success. If the string can't be parsed\nas a valid, strict `unsigned long long` (no spaces before/after), `REDISMODULE_ERR`\nis returned.\n\n<span id=\"RedisModule_StringToDouble\"></span>"},{"id":"redismodule_stringtodouble","title":"`RedisModule_StringToDouble`","role":"content","text":"int RedisModule_StringToDouble(const RedisModuleString *str, double *d);\n\n**Available since:** 4.0.0\n\nConvert the string into a double, storing it at `*d`.\nReturns `REDISMODULE_OK` on success or `REDISMODULE_ERR` if the string is\nnot a valid string representation of a double value.\n\n<span id=\"RedisModule_StringToLongDouble\"></span>"},{"id":"redismodule_stringtolongdouble","title":"`RedisModule_StringToLongDouble`","role":"content","text":"int RedisModule_StringToLongDouble(const RedisModuleString *str,\n                                       long double *ld);\n\n**Available since:** 6.0.0\n\nConvert the string into a long double, storing it at `*ld`.\nReturns `REDISMODULE_OK` on success or `REDISMODULE_ERR` if the string is\nnot a valid string representation of a double value.\n\n<span id=\"RedisModule_StringToStreamID\"></span>"},{"id":"redismodule_stringtostreamid","title":"`RedisModule_StringToStreamID`","role":"content","text":"int RedisModule_StringToStreamID(const RedisModuleString *str,\n                                     RedisModuleStreamID *id);\n\n**Available since:** 6.2.0\n\nConvert the string into a stream ID, storing it at `*id`.\nReturns `REDISMODULE_OK` on success and returns `REDISMODULE_ERR` if the string\nis not a valid string representation of a stream ID. The special IDs \"+\" and\n\"-\" are allowed.\n\n<span id=\"RedisModule_StringCompare\"></span>"},{"id":"redismodule_stringcompare","title":"`RedisModule_StringCompare`","role":"content","text":"int RedisModule_StringCompare(const RedisModuleString *a,\n                                  const RedisModuleString *b);\n\n**Available since:** 4.0.0\n\nCompare two string objects, returning -1, 0 or 1 respectively if\na < b, a == b, a > b. Strings are compared byte by byte as two\nbinary blobs without any encoding care / collation attempt.\n\n<span id=\"RedisModule_StringAppendBuffer\"></span>"},{"id":"redismodule_stringappendbuffer","title":"`RedisModule_StringAppendBuffer`","role":"content","text":"int RedisModule_StringAppendBuffer(RedisModuleCtx *ctx,\n                                       RedisModuleString *str,\n                                       const char *buf,\n                                       size_t len);\n\n**Available since:** 4.0.0\n\nAppend the specified buffer to the string 'str'. The string must be a\nstring created by the user that is referenced only a single time, otherwise\n`REDISMODULE_ERR` is returned and the operation is not performed.\n\n<span id=\"RedisModule_TrimStringAllocation\"></span>"},{"id":"redismodule_trimstringallocation","title":"`RedisModule_TrimStringAllocation`","role":"content","text":"void RedisModule_TrimStringAllocation(RedisModuleString *str);\n\n**Available since:** 7.0.0\n\nTrim possible excess memory allocated for a `RedisModuleString`.\n\nSometimes a `RedisModuleString` may have more memory allocated for\nit than required, typically for argv arguments that were constructed\nfrom network buffers. This function optimizes such strings by reallocating\ntheir memory, which is useful for strings that are not short lived but\nretained for an extended duration.\n\nThis operation is *not thread safe* and should only be called when\nno concurrent access to the string is guaranteed. Using it for an argv\nstring in a module command before the string is potentially available\nto other threads is generally safe.\n\nCurrently, Redis may also automatically trim retained strings when a\nmodule command returns. However, doing this explicitly should still be\na preferred option:\n\n1. Future versions of Redis may abandon auto-trimming.\n2. Auto-trimming as currently implemented is *not thread safe*.\n   A background thread manipulating a recently retained string may end up\n   in a race condition with the auto-trim, which could result with\n   data corruption.\n\n<span id=\"section-reply-apis\"></span>"},{"id":"reply-apis","title":"Reply APIs","role":"content","text":"These functions are used for sending replies to the client.\n\nMost functions always return `REDISMODULE_OK` so you can use it with\n'return' in order to return from the command implementation with:\n\n    if (... some condition ...)\n        return RedisModule_ReplyWithLongLong(ctx,mycount);"},{"id":"reply-with-collection-functions","title":"Reply with collection functions","role":"content","text":"After starting a collection reply, the module must make calls to other\n`ReplyWith*` style functions in order to emit the elements of the collection.\nCollection types include: Array, Map, Set and Attribute.\n\nWhen producing collections with a number of elements that is not known\nbeforehand, the function can be called with a special flag\n`REDISMODULE_POSTPONED_LEN` (`REDISMODULE_POSTPONED_ARRAY_LEN` in the past),\nand the actual number of elements can be later set with `RedisModule_ReplySet`*Length()\ncall (which will set the latest \"open\" count if there are multiple ones).\n\n<span id=\"RedisModule_WrongArity\"></span>"},{"id":"redismodule_wrongarity","title":"`RedisModule_WrongArity`","role":"content","text":"int RedisModule_WrongArity(RedisModuleCtx *ctx);\n\n**Available since:** 4.0.0\n\nSend an error about the number of arguments given to the command,\nciting the command name in the error message. Returns `REDISMODULE_OK`.\n\nExample:\n\n    if (argc != 3) return RedisModule_WrongArity(ctx);\n\n<span id=\"RedisModule_ReplyWithLongLong\"></span>"},{"id":"redismodule_replywithlonglong","title":"`RedisModule_ReplyWithLongLong`","role":"content","text":"int RedisModule_ReplyWithLongLong(RedisModuleCtx *ctx, long long ll);\n\n**Available since:** 4.0.0\n\nSend an integer reply to the client, with the specified `long long` value.\nThe function always returns `REDISMODULE_OK`.\n\n<span id=\"RedisModule_ReplyWithError\"></span>"},{"id":"redismodule_replywitherror","title":"`RedisModule_ReplyWithError`","role":"content","text":"int RedisModule_ReplyWithError(RedisModuleCtx *ctx, const char *err);\n\n**Available since:** 4.0.0\n\nReply with the error 'err'.\n\nNote that 'err' must contain all the error, including\nthe initial error code. The function only provides the initial \"-\", so\nthe usage is, for example:\n\n    RedisModule_ReplyWithError(ctx,\"ERR Wrong Type\");\n\nand not just:\n\n    RedisModule_ReplyWithError(ctx,\"Wrong Type\");\n\nThe function always returns `REDISMODULE_OK`.\n\n<span id=\"RedisModule_ReplyWithErrorFormat\"></span>"},{"id":"redismodule_replywitherrorformat","title":"`RedisModule_ReplyWithErrorFormat`","role":"content","text":"int RedisModule_ReplyWithErrorFormat(RedisModuleCtx *ctx,\n                                         const char *fmt,\n                                         ...);\n\n**Available since:** 7.2.0\n\nReply with the error create from a printf format and arguments.\n\nNote that 'fmt' must contain all the error, including\nthe initial error code. The function only provides the initial \"-\", so\nthe usage is, for example:\n\n    RedisModule_ReplyWithErrorFormat(ctx,\"ERR Wrong Type: %s\",type);\n\nand not just:\n\n    RedisModule_ReplyWithErrorFormat(ctx,\"Wrong Type: %s\",type);\n\nThe function always returns `REDISMODULE_OK`.\n\n<span id=\"RedisModule_ReplyWithSimpleString\"></span>"},{"id":"redismodule_replywithsimplestring","title":"`RedisModule_ReplyWithSimpleString`","role":"content","text":"int RedisModule_ReplyWithSimpleString(RedisModuleCtx *ctx, const char *msg);\n\n**Available since:** 4.0.0\n\nReply with a simple string (`+... \\r\\n` in RESP protocol). This replies\nare suitable only when sending a small non-binary string with small\noverhead, like \"OK\" or similar replies.\n\nThe function always returns `REDISMODULE_OK`.\n\n<span id=\"RedisModule_ReplyWithArray\"></span>"},{"id":"redismodule_replywitharray","title":"`RedisModule_ReplyWithArray`","role":"content","text":"int RedisModule_ReplyWithArray(RedisModuleCtx *ctx, long len);\n\n**Available since:** 4.0.0\n\nReply with an array type of 'len' elements.\n\nAfter starting an array reply, the module must make `len` calls to other\n`ReplyWith*` style functions in order to emit the elements of the array.\nSee Reply APIs section for more details.\n\nUse [`RedisModule_ReplySetArrayLength()`](#RedisModule_ReplySetArrayLength) to set deferred length.\n\nThe function always returns `REDISMODULE_OK`.\n\n<span id=\"RedisModule_ReplyWithMap\"></span>"},{"id":"redismodule_replywithmap","title":"`RedisModule_ReplyWithMap`","role":"content","text":"int RedisModule_ReplyWithMap(RedisModuleCtx *ctx, long len);\n\n**Available since:** 7.0.0\n\nReply with a RESP3 Map type of 'len' pairs.\nVisit [https://github.com/antirez/RESP3/blob/master/spec.md](https://github.com/antirez/RESP3/blob/master/spec.md) for more info about RESP3.\n\nAfter starting a map reply, the module must make `len*2` calls to other\n`ReplyWith*` style functions in order to emit the elements of the map.\nSee Reply APIs section for more details.\n\nIf the connected client is using RESP2, the reply will be converted to a flat\narray.\n\nUse [`RedisModule_ReplySetMapLength()`](#RedisModule_ReplySetMapLength) to set deferred length.\n\nThe function always returns `REDISMODULE_OK`.\n\n<span id=\"RedisModule_ReplyWithSet\"></span>"},{"id":"redismodule_replywithset","title":"`RedisModule_ReplyWithSet`","role":"content","text":"int RedisModule_ReplyWithSet(RedisModuleCtx *ctx, long len);\n\n**Available since:** 7.0.0\n\nReply with a RESP3 Set type of 'len' elements.\nVisit [https://github.com/antirez/RESP3/blob/master/spec.md](https://github.com/antirez/RESP3/blob/master/spec.md) for more info about RESP3.\n\nAfter starting a set reply, the module must make `len` calls to other\n`ReplyWith*` style functions in order to emit the elements of the set.\nSee Reply APIs section for more details.\n\nIf the connected client is using RESP2, the reply will be converted to an\narray type.\n\nUse [`RedisModule_ReplySetSetLength()`](#RedisModule_ReplySetSetLength) to set deferred length.\n\nThe function always returns `REDISMODULE_OK`.\n\n<span id=\"RedisModule_ReplyWithAttribute\"></span>"},{"id":"redismodule_replywithattribute","title":"`RedisModule_ReplyWithAttribute`","role":"content","text":"int RedisModule_ReplyWithAttribute(RedisModuleCtx *ctx, long len);\n\n**Available since:** 7.0.0\n\nAdd attributes (metadata) to the reply. Should be done before adding the\nactual reply. see [https://github.com/antirez/RESP3/blob/master/spec.md](https://github.com/antirez/RESP3/blob/master/spec.md)#attribute-type\n\nAfter starting an attribute's reply, the module must make `len*2` calls to other\n`ReplyWith*` style functions in order to emit the elements of the attribute map.\nSee Reply APIs section for more details.\n\nUse [`RedisModule_ReplySetAttributeLength()`](#RedisModule_ReplySetAttributeLength) to set deferred length.\n\nNot supported by RESP2 and will return `REDISMODULE_ERR`, otherwise\nthe function always returns `REDISMODULE_OK`.\n\n<span id=\"RedisModule_ReplyWithNullArray\"></span>"},{"id":"redismodule_replywithnullarray","title":"`RedisModule_ReplyWithNullArray`","role":"content","text":"int RedisModule_ReplyWithNullArray(RedisModuleCtx *ctx);\n\n**Available since:** 6.0.0\n\nReply to the client with a null array, simply null in RESP3,\nnull array in RESP2.\n\nNote: In RESP3 there's no difference between Null reply and\nNullArray reply, so to prevent ambiguity it's better to avoid\nusing this API and use [`RedisModule_ReplyWithNull`](#RedisModule_ReplyWithNull) instead.\n\nThe function always returns `REDISMODULE_OK`.\n\n<span id=\"RedisModule_ReplyWithEmptyArray\"></span>"},{"id":"redismodule_replywithemptyarray","title":"`RedisModule_ReplyWithEmptyArray`","role":"content","text":"int RedisModule_ReplyWithEmptyArray(RedisModuleCtx *ctx);\n\n**Available since:** 6.0.0\n\nReply to the client with an empty array.\n\nThe function always returns `REDISMODULE_OK`.\n\n<span id=\"RedisModule_ReplySetArrayLength\"></span>"},{"id":"redismodule_replysetarraylength","title":"`RedisModule_ReplySetArrayLength`","role":"content","text":"void RedisModule_ReplySetArrayLength(RedisModuleCtx *ctx, long len);\n\n**Available since:** 4.0.0\n\nWhen [`RedisModule_ReplyWithArray()`](#RedisModule_ReplyWithArray) is used with the argument\n`REDISMODULE_POSTPONED_LEN`, because we don't know beforehand the number\nof items we are going to output as elements of the array, this function\nwill take care to set the array length.\n\nSince it is possible to have multiple array replies pending with unknown\nlength, this function guarantees to always set the latest array length\nthat was created in a postponed way.\n\nFor example in order to output an array like [1,[10,20,30]] we\ncould write:\n\n     RedisModule_ReplyWithArray(ctx,REDISMODULE_POSTPONED_LEN);\n     RedisModule_ReplyWithLongLong(ctx,1);\n     RedisModule_ReplyWithArray(ctx,REDISMODULE_POSTPONED_LEN);\n     RedisModule_ReplyWithLongLong(ctx,10);\n     RedisModule_ReplyWithLongLong(ctx,20);\n     RedisModule_ReplyWithLongLong(ctx,30);\n     RedisModule_ReplySetArrayLength(ctx,3); // Set len of 10,20,30 array.\n     RedisModule_ReplySetArrayLength(ctx,2); // Set len of top array\n\nNote that in the above example there is no reason to postpone the array\nlength, since we produce a fixed number of elements, but in the practice\nthe code may use an iterator or other ways of creating the output so\nthat is not easy to calculate in advance the number of elements.\n\n<span id=\"RedisModule_ReplySetMapLength\"></span>"},{"id":"redismodule_replysetmaplength","title":"`RedisModule_ReplySetMapLength`","role":"content","text":"void RedisModule_ReplySetMapLength(RedisModuleCtx *ctx, long len);\n\n**Available since:** 7.0.0\n\nVery similar to [`RedisModule_ReplySetArrayLength`](#RedisModule_ReplySetArrayLength) except `len` should\nexactly half of the number of `ReplyWith*` functions called in the\ncontext of the map.\nVisit [https://github.com/antirez/RESP3/blob/master/spec.md](https://github.com/antirez/RESP3/blob/master/spec.md) for more info about RESP3.\n\n<span id=\"RedisModule_ReplySetSetLength\"></span>"},{"id":"redismodule_replysetsetlength","title":"`RedisModule_ReplySetSetLength`","role":"content","text":"void RedisModule_ReplySetSetLength(RedisModuleCtx *ctx, long len);\n\n**Available since:** 7.0.0\n\nVery similar to [`RedisModule_ReplySetArrayLength`](#RedisModule_ReplySetArrayLength)\nVisit [https://github.com/antirez/RESP3/blob/master/spec.md](https://github.com/antirez/RESP3/blob/master/spec.md) for more info about RESP3.\n\n<span id=\"RedisModule_ReplySetAttributeLength\"></span>"},{"id":"redismodule_replysetattributelength","title":"`RedisModule_ReplySetAttributeLength`","role":"content","text":"void RedisModule_ReplySetAttributeLength(RedisModuleCtx *ctx, long len);\n\n**Available since:** 7.0.0\n\nVery similar to [`RedisModule_ReplySetMapLength`](#RedisModule_ReplySetMapLength)\nVisit [https://github.com/antirez/RESP3/blob/master/spec.md](https://github.com/antirez/RESP3/blob/master/spec.md) for more info about RESP3.\n\nMust not be called if [`RedisModule_ReplyWithAttribute`](#RedisModule_ReplyWithAttribute) returned an error.\n\n<span id=\"RedisModule_ReplyWithStringBuffer\"></span>"},{"id":"redismodule_replywithstringbuffer","title":"`RedisModule_ReplyWithStringBuffer`","role":"content","text":"int RedisModule_ReplyWithStringBuffer(RedisModuleCtx *ctx,\n                                          const char *buf,\n                                          size_t len);\n\n**Available since:** 4.0.0\n\nReply with a bulk string, taking in input a C buffer pointer and length.\n\nThe function always returns `REDISMODULE_OK`.\n\n<span id=\"RedisModule_ReplyWithCString\"></span>"},{"id":"redismodule_replywithcstring","title":"`RedisModule_ReplyWithCString`","role":"content","text":"int RedisModule_ReplyWithCString(RedisModuleCtx *ctx, const char *buf);\n\n**Available since:** 5.0.6\n\nReply with a bulk string, taking in input a C buffer pointer that is\nassumed to be null-terminated.\n\nThe function always returns `REDISMODULE_OK`.\n\n<span id=\"RedisModule_ReplyWithString\"></span>"},{"id":"redismodule_replywithstring","title":"`RedisModule_ReplyWithString`","role":"content","text":"int RedisModule_ReplyWithString(RedisModuleCtx *ctx, RedisModuleString *str);\n\n**Available since:** 4.0.0\n\nReply with a bulk string, taking in input a `RedisModuleString` object.\n\nThe function always returns `REDISMODULE_OK`.\n\n<span id=\"RedisModule_ReplyWithEmptyString\"></span>"},{"id":"redismodule_replywithemptystring","title":"`RedisModule_ReplyWithEmptyString`","role":"content","text":"int RedisModule_ReplyWithEmptyString(RedisModuleCtx *ctx);\n\n**Available since:** 6.0.0\n\nReply with an empty string.\n\nThe function always returns `REDISMODULE_OK`.\n\n<span id=\"RedisModule_ReplyWithVerbatimStringType\"></span>"},{"id":"redismodule_replywithverbatimstringtype","title":"`RedisModule_ReplyWithVerbatimStringType`","role":"content","text":"int RedisModule_ReplyWithVerbatimStringType(RedisModuleCtx *ctx,\n                                                const char *buf,\n                                                size_t len,\n                                                const char *ext);\n\n**Available since:** 7.0.0\n\nReply with a binary safe string, which should not be escaped or filtered\ntaking in input a C buffer pointer, length and a 3 character type/extension.\n\nThe function always returns `REDISMODULE_OK`.\n\n<span id=\"RedisModule_ReplyWithVerbatimString\"></span>"},{"id":"redismodule_replywithverbatimstring","title":"`RedisModule_ReplyWithVerbatimString`","role":"content","text":"int RedisModule_ReplyWithVerbatimString(RedisModuleCtx *ctx,\n                                            const char *buf,\n                                            size_t len);\n\n**Available since:** 6.0.0\n\nReply with a binary safe string, which should not be escaped or filtered\ntaking in input a C buffer pointer and length.\n\nThe function always returns `REDISMODULE_OK`.\n\n<span id=\"RedisModule_ReplyWithNull\"></span>"},{"id":"redismodule_replywithnull","title":"`RedisModule_ReplyWithNull`","role":"content","text":"int RedisModule_ReplyWithNull(RedisModuleCtx *ctx);\n\n**Available since:** 4.0.0\n\nReply to the client with a NULL.\n\nThe function always returns `REDISMODULE_OK`.\n\n<span id=\"RedisModule_ReplyWithBool\"></span>"},{"id":"redismodule_replywithbool","title":"`RedisModule_ReplyWithBool`","role":"content","text":"int RedisModule_ReplyWithBool(RedisModuleCtx *ctx, int b);\n\n**Available since:** 7.0.0\n\nReply with a RESP3 Boolean type.\nVisit [https://github.com/antirez/RESP3/blob/master/spec.md](https://github.com/antirez/RESP3/blob/master/spec.md) for more info about RESP3.\n\nIn RESP3, this is boolean type\nIn RESP2, it's a string response of \"1\" and \"0\" for true and false respectively.\n\nThe function always returns `REDISMODULE_OK`.\n\n<span id=\"RedisModule_ReplyWithCallReply\"></span>"},{"id":"redismodule_replywithcallreply","title":"`RedisModule_ReplyWithCallReply`","role":"content","text":"int RedisModule_ReplyWithCallReply(RedisModuleCtx *ctx,\n                                       RedisModuleCallReply *reply);\n\n**Available since:** 4.0.0\n\nReply exactly what a Redis command returned us with [`RedisModule_Call()`](#RedisModule_Call).\nThis function is useful when we use [`RedisModule_Call()`](#RedisModule_Call) in order to\nexecute some command, as we want to reply to the client exactly the\nsame reply we obtained by the command.\n\nReturn:\n- `REDISMODULE_OK` on success.\n- `REDISMODULE_ERR` if the given reply is in RESP3 format but the client expects RESP2.\n  In case of an error, it's the module writer responsibility to translate the reply\n  to RESP2 (or handle it differently by returning an error). Notice that for\n  module writer convenience, it is possible to pass `0` as a parameter to the fmt\n  argument of [`RedisModule_Call`](#RedisModule_Call) so that the `RedisModuleCallReply` will return in the same\n  protocol (RESP2 or RESP3) as set in the current client's context.\n\n<span id=\"RedisModule_ReplyWithDouble\"></span>"},{"id":"redismodule_replywithdouble","title":"`RedisModule_ReplyWithDouble`","role":"content","text":"int RedisModule_ReplyWithDouble(RedisModuleCtx *ctx, double d);\n\n**Available since:** 4.0.0\n\nReply with a RESP3 Double type.\nVisit [https://github.com/antirez/RESP3/blob/master/spec.md](https://github.com/antirez/RESP3/blob/master/spec.md) for more info about RESP3.\n\nSend a string reply obtained converting the double 'd' into a bulk string.\nThis function is basically equivalent to converting a double into\na string into a C buffer, and then calling the function\n[`RedisModule_ReplyWithStringBuffer()`](#RedisModule_ReplyWithStringBuffer) with the buffer and length.\n\nIn RESP3 the string is tagged as a double, while in RESP2 it's just a plain string \nthat the user will have to parse.\n\nThe function always returns `REDISMODULE_OK`.\n\n<span id=\"RedisModule_ReplyWithBigNumber\"></span>"},{"id":"redismodule_replywithbignumber","title":"`RedisModule_ReplyWithBigNumber`","role":"content","text":"int RedisModule_ReplyWithBigNumber(RedisModuleCtx *ctx,\n                                       const char *bignum,\n                                       size_t len);\n\n**Available since:** 7.0.0\n\nReply with a RESP3 BigNumber type.\nVisit [https://github.com/antirez/RESP3/blob/master/spec.md](https://github.com/antirez/RESP3/blob/master/spec.md) for more info about RESP3.\n\nIn RESP3, this is a string of length `len` that is tagged as a BigNumber, \nhowever, it's up to the caller to ensure that it's a valid BigNumber.\nIn RESP2, this is just a plain bulk string response.\n\nThe function always returns `REDISMODULE_OK`.\n\n<span id=\"RedisModule_ReplyWithLongDouble\"></span>"},{"id":"redismodule_replywithlongdouble","title":"`RedisModule_ReplyWithLongDouble`","role":"content","text":"int RedisModule_ReplyWithLongDouble(RedisModuleCtx *ctx, long double ld);\n\n**Available since:** 6.0.0\n\nSend a string reply obtained converting the long double 'ld' into a bulk\nstring. This function is basically equivalent to converting a long double\ninto a string into a C buffer, and then calling the function\n[`RedisModule_ReplyWithStringBuffer()`](#RedisModule_ReplyWithStringBuffer) with the buffer and length.\nThe double string uses human readable formatting (see\n`addReplyHumanLongDouble` in networking.c).\n\nThe function always returns `REDISMODULE_OK`.\n\n<span id=\"section-commands-replication-api\"></span>"},{"id":"commands-replication-api","title":"Commands replication API","role":"syntax","text":"<span id=\"RedisModule_Replicate\"></span>"},{"id":"redismodule_replicate","title":"`RedisModule_Replicate`","role":"content","text":"int RedisModule_Replicate(RedisModuleCtx *ctx,\n                              const char *cmdname,\n                              const char *fmt,\n                              ...);\n\n**Available since:** 4.0.0\n\nReplicate the specified command and arguments to slaves and AOF, as effect\nof execution of the calling command implementation.\n\nThe replicated commands are always wrapped into the MULTI/EXEC that\ncontains all the commands replicated in a given module command\nexecution, in the order they were executed.\n\nModules should try to use one interface or the other.\n\nThis command follows exactly the same interface of [`RedisModule_Call()`](#RedisModule_Call),\nso a set of format specifiers must be passed, followed by arguments\nmatching the provided format specifiers.\n\nPlease refer to [`RedisModule_Call()`](#RedisModule_Call) for more information.\n\nUsing the special \"A\" and \"R\" modifiers, the caller can exclude either\nthe AOF or the replicas from the propagation of the specified command.\nOtherwise, by default, the command will be propagated in both channels.\n\n#### Note about calling this function from a thread safe context:\n\nNormally when you call this function from the callback implementing a\nmodule command, or any other callback provided by the Redis Module API,\nRedis will accumulate all the calls to this function in the context of\nthe callback, and will propagate all the commands wrapped in a MULTI/EXEC\ntransaction. However when calling this function from a threaded safe context\nthat can live an undefined amount of time, and can be locked/unlocked in\nat will, it is important to note that this API is not thread-safe and\nmust be executed while holding the GIL.\n\n#### Return value\n\nThe command returns `REDISMODULE_ERR` if the format specifiers are invalid\nor the command name does not belong to a known command.\n\n<span id=\"RedisModule_ReplicateVerbatim\"></span>"},{"id":"redismodule_replicateverbatim","title":"`RedisModule_ReplicateVerbatim`","role":"content","text":"int RedisModule_ReplicateVerbatim(RedisModuleCtx *ctx);\n\n**Available since:** 4.0.0\n\nThis function will replicate the command exactly as it was invoked\nby the client. Note that the replicated commands are always wrapped\ninto the MULTI/EXEC that contains all the commands replicated in a\ngiven module command execution, in the order they were executed.\n\nBasically this form of replication is useful when you want to propagate\nthe command to the slaves and AOF file exactly as it was called, since\nthe command can just be re-executed to deterministically re-create the\nnew state starting from the old one.\n\nIt is important to note that this API is not thread-safe and\nmust be executed while holding the GIL.\n\nThe function always returns `REDISMODULE_OK`.\n\n<span id=\"section-db-and-key-apis-generic-api\"></span>"},{"id":"db-and-key-apis--generic-api","title":"DB and Key APIs – Generic API","role":"content","text":"<span id=\"RedisModule_GetClientId\"></span>"},{"id":"redismodule_getclientid","title":"`RedisModule_GetClientId`","role":"content","text":"unsigned long long RedisModule_GetClientId(RedisModuleCtx *ctx);\n\n**Available since:** 4.0.0\n\nReturn the ID of the current client calling the currently active module\ncommand. The returned ID has a few guarantees:\n\n1. The ID is different for each different client, so if the same client\n   executes a module command multiple times, it can be recognized as\n   having the same ID, otherwise the ID will be different.\n2. The ID increases monotonically. Clients connecting to the server later\n   are guaranteed to get IDs greater than any past ID previously seen.\n\nValid IDs are from 1 to 2^64 - 1. If 0 is returned it means there is no way\nto fetch the ID in the context the function was currently called.\n\nAfter obtaining the ID, it is possible to check if the command execution\nis actually happening in the context of AOF loading, using this macro:\n\n     if (RedisModule_IsAOFClient(RedisModule_GetClientId(ctx)) {\n         // Handle it differently.\n     }\n\n<span id=\"RedisModule_GetClientUserNameById\"></span>"},{"id":"redismodule_getclientusernamebyid","title":"`RedisModule_GetClientUserNameById`","role":"content","text":"RedisModuleString *RedisModule_GetClientUserNameById(RedisModuleCtx *ctx,\n                                                         uint64_t id);\n\n**Available since:** 6.2.1\n\nReturn the ACL user name used by the client with the specified client ID.\nClient ID can be obtained with [`RedisModule_GetClientId()`](#RedisModule_GetClientId) API. If the client does not\nexist, NULL is returned and errno is set to ENOENT. If the client isn't\nusing an ACL user, NULL is returned and errno is set to ENOTSUP\n\n<span id=\"RedisModule_GetClientInfoById\"></span>"},{"id":"redismodule_getclientinfobyid","title":"`RedisModule_GetClientInfoById`","role":"content","text":"int RedisModule_GetClientInfoById(void *ci, uint64_t id);\n\n**Available since:** 6.0.0\n\nReturn information about the client with the specified ID (that was\npreviously obtained via the [`RedisModule_GetClientId()`](#RedisModule_GetClientId) API). If the\nclient exists, `REDISMODULE_OK` is returned, otherwise `REDISMODULE_ERR`\nis returned.\n\nWhen the client exist and the `ci` pointer is not NULL, but points to\na structure of type `RedisModuleClientInfoV`1, previously initialized with\nthe correct `REDISMODULE_CLIENTINFO_INITIALIZER_V1`, the structure is populated\nwith the following fields:\n\n     uint64_t flags;         // REDISMODULE_CLIENTINFO_FLAG_*\n     uint64_t id;            // Client ID\n     char addr[46];          // IPv4 or IPv6 address.\n     uint16_t port;          // TCP port.\n     uint16_t db;            // Selected DB.\n\nNote: the client ID is useless in the context of this call, since we\n      already know, however the same structure could be used in other\n      contexts where we don't know the client ID, yet the same structure\n      is returned.\n\nWith flags having the following meaning:\n\n    REDISMODULE_CLIENTINFO_FLAG_SSL          Client using SSL connection.\n    REDISMODULE_CLIENTINFO_FLAG_PUBSUB       Client in Pub/Sub mode.\n    REDISMODULE_CLIENTINFO_FLAG_BLOCKED      Client blocked in command.\n    REDISMODULE_CLIENTINFO_FLAG_TRACKING     Client with keys tracking on.\n    REDISMODULE_CLIENTINFO_FLAG_UNIXSOCKET   Client using unix domain socket.\n    REDISMODULE_CLIENTINFO_FLAG_MULTI        Client in MULTI state.\n\nHowever passing NULL is a way to just check if the client exists in case\nwe are not interested in any additional information.\n\nThis is the correct usage when we want the client info structure\nreturned:\n\n     RedisModuleClientInfo ci = REDISMODULE_CLIENTINFO_INITIALIZER;\n     int retval = RedisModule_GetClientInfoById(&ci,client_id);\n     if (retval == REDISMODULE_OK) {\n         printf(\"Address: %s\\n\", ci.addr);\n     }\n\n<span id=\"RedisModule_GetClientNameById\"></span>"},{"id":"redismodule_getclientnamebyid","title":"`RedisModule_GetClientNameById`","role":"content","text":"RedisModuleString *RedisModule_GetClientNameById(RedisModuleCtx *ctx,\n                                                     uint64_t id);\n\n**Available since:** 7.0.3\n\nReturns the name of the client connection with the given ID.\n\nIf the client ID does not exist or if the client has no name associated with\nit, NULL is returned.\n\n<span id=\"RedisModule_SetClientNameById\"></span>"},{"id":"redismodule_setclientnamebyid","title":"`RedisModule_SetClientNameById`","role":"content","text":"int RedisModule_SetClientNameById(uint64_t id, RedisModuleString *name);\n\n**Available since:** 7.0.3\n\nSets the name of the client with the given ID. This is equivalent to the client calling\n`CLIENT SETNAME name`.\n\nReturns `REDISMODULE_OK` on success. On failure, `REDISMODULE_ERR` is returned\nand errno is set as follows:\n\n- ENOENT if the client does not exist\n- EINVAL if the name contains invalid characters\n\n<span id=\"RedisModule_PublishMessage\"></span>"},{"id":"redismodule_publishmessage","title":"`RedisModule_PublishMessage`","role":"content","text":"int RedisModule_PublishMessage(RedisModuleCtx *ctx,\n                                   RedisModuleString *channel,\n                                   RedisModuleString *message);\n\n**Available since:** 6.0.0\n\nPublish a message to subscribers (see PUBLISH command).\n\n<span id=\"RedisModule_PublishMessageShard\"></span>"},{"id":"redismodule_publishmessageshard","title":"`RedisModule_PublishMessageShard`","role":"content","text":"int RedisModule_PublishMessageShard(RedisModuleCtx *ctx,\n                                        RedisModuleString *channel,\n                                        RedisModuleString *message);\n\n**Available since:** 7.0.0\n\nPublish a message to shard-subscribers (see SPUBLISH command).\n\n<span id=\"RedisModule_GetSelectedDb\"></span>"},{"id":"redismodule_getselecteddb","title":"`RedisModule_GetSelectedDb`","role":"content","text":"int RedisModule_GetSelectedDb(RedisModuleCtx *ctx);\n\n**Available since:** 4.0.0\n\nReturn the currently selected DB.\n\n<span id=\"RedisModule_GetContextFlags\"></span>"},{"id":"redismodule_getcontextflags","title":"`RedisModule_GetContextFlags`","role":"content","text":"int RedisModule_GetContextFlags(RedisModuleCtx *ctx);\n\n**Available since:** 4.0.3\n\nReturn the current context's flags. The flags provide information on the\ncurrent request context (whether the client is a Lua script or in a MULTI),\nand about the Redis instance in general, i.e replication and persistence.\n\nIt is possible to call this function even with a NULL context, however\nin this case the following flags will not be reported:\n\n * LUA, MULTI, REPLICATED, DIRTY (see below for more info).\n\nAvailable flags and their meaning:\n\n * `REDISMODULE_CTX_FLAGS_LUA`: The command is running in a Lua script\n\n * `REDISMODULE_CTX_FLAGS_MULTI`: The command is running inside a transaction\n\n * `REDISMODULE_CTX_FLAGS_REPLICATED`: The command was sent over the replication\n   link by the MASTER\n\n * `REDISMODULE_CTX_FLAGS_MASTER`: The Redis instance is a master\n\n * `REDISMODULE_CTX_FLAGS_SLAVE`: The Redis instance is a slave\n\n * `REDISMODULE_CTX_FLAGS_READONLY`: The Redis instance is read-only\n\n * `REDISMODULE_CTX_FLAGS_CLUSTER`: The Redis instance is in cluster mode\n\n * `REDISMODULE_CTX_FLAGS_AOF`: The Redis instance has AOF enabled\n\n * `REDISMODULE_CTX_FLAGS_RDB`: The instance has RDB enabled\n\n * `REDISMODULE_CTX_FLAGS_MAXMEMORY`:  The instance has Maxmemory set\n\n * `REDISMODULE_CTX_FLAGS_EVICT`:  Maxmemory is set and has an eviction\n   policy that may delete keys\n\n * `REDISMODULE_CTX_FLAGS_OOM`: Redis is out of memory according to the\n   maxmemory setting.\n\n * `REDISMODULE_CTX_FLAGS_OOM_WARNING`: Less than 25% of memory remains before\n                                      reaching the maxmemory level.\n\n * `REDISMODULE_CTX_FLAGS_LOADING`: Server is loading RDB/AOF\n\n * `REDISMODULE_CTX_FLAGS_REPLICA_IS_STALE`: No active link with the master.\n\n * `REDISMODULE_CTX_FLAGS_REPLICA_IS_CONNECTING`: The replica is trying to\n                                                connect with the master.\n\n * `REDISMODULE_CTX_FLAGS_REPLICA_IS_TRANSFERRING`: Master -> Replica RDB\n                                                  transfer is in progress.\n\n * `REDISMODULE_CTX_FLAGS_REPLICA_IS_ONLINE`: The replica has an active link\n                                            with its master. This is the\n                                            contrary of STALE state.\n\n * `REDISMODULE_CTX_FLAGS_ACTIVE_CHILD`: There is currently some background\n                                       process active (RDB, AUX or module).\n\n * `REDISMODULE_CTX_FLAGS_MULTI_DIRTY`: The next EXEC will fail due to dirty\n                                      CAS (touched keys).\n\n * `REDISMODULE_CTX_FLAGS_IS_CHILD`: Redis is currently running inside\n                                   background child process.\n\n * `REDISMODULE_CTX_FLAGS_RESP3`: Indicate the that client attached to this\n                                context is using RESP3.\n\n * `REDISMODULE_CTX_FLAGS_SERVER_STARTUP`: The Redis instance is starting\n\n * `REDISMODULE_CTX_FLAGS_DEBUG_ENABLED`: Debug commands are enabled for this\n                                        context.\n * `REDISMODULE_CTX_FLAGS_TRIM_IN_PROGRESS`: Trim is in progress due to slot\n                                           migration.\n\n<span id=\"RedisModule_AvoidReplicaTraffic\"></span>"},{"id":"redismodule_avoidreplicatraffic","title":"`RedisModule_AvoidReplicaTraffic`","role":"content","text":"int RedisModule_AvoidReplicaTraffic(void);\n\n**Available since:** 6.0.0\n\nReturns true if a client sent the CLIENT PAUSE command to the server or\nif Redis Cluster does a manual failover, pausing the clients.\nThis is needed when we have a master with replicas, and want to write,\nwithout adding further data to the replication channel, that the replicas\nreplication offset, match the one of the master. When this happens, it is\nsafe to failover the master without data loss.\n\nHowever modules may generate traffic by calling [`RedisModule_Call()`](#RedisModule_Call) with\nthe \"!\" flag, or by calling [`RedisModule_Replicate()`](#RedisModule_Replicate), in a context outside\ncommands execution, for instance in timeout callbacks, threads safe\ncontexts, and so forth. When modules will generate too much traffic, it\nwill be hard for the master and replicas offset to match, because there\nis more data to send in the replication channel.\n\nSo modules may want to try to avoid very heavy background work that has\nthe effect of creating data to the replication channel, when this function\nreturns true. This is mostly useful for modules that have background\ngarbage collection tasks, or that do writes and replicate such writes\nperiodically in timer callbacks or other periodic callbacks.\n\n<span id=\"RedisModule_SelectDb\"></span>"},{"id":"redismodule_selectdb","title":"`RedisModule_SelectDb`","role":"content","text":"int RedisModule_SelectDb(RedisModuleCtx *ctx, int newid);\n\n**Available since:** 4.0.0\n\nChange the currently selected DB. Returns an error if the id\nis out of range.\n\nNote that the client will retain the currently selected DB even after\nthe Redis command implemented by the module calling this function\nreturns.\n\nIf the module command wishes to change something in a different DB and\nreturns back to the original one, it should call [`RedisModule_GetSelectedDb()`](#RedisModule_GetSelectedDb)\nbefore in order to restore the old DB number before returning.\n\n<span id=\"RedisModule_KeyExists\"></span>"},{"id":"redismodule_keyexists","title":"`RedisModule_KeyExists`","role":"content","text":"int RedisModule_KeyExists(RedisModuleCtx *ctx, robj *keyname);\n\n**Available since:** 7.0.0\n\nCheck if a key exists, without affecting its last access time.\n\nThis is equivalent to calling [`RedisModule_OpenKey`](#RedisModule_OpenKey) with the mode `REDISMODULE_READ` |\n`REDISMODULE_OPEN_KEY_NOTOUCH`, then checking if NULL was returned and, if not,\ncalling [`RedisModule_CloseKey`](#RedisModule_CloseKey) on the opened key.\n\n<span id=\"RedisModule_OpenKey\"></span>"},{"id":"redismodule_openkey","title":"`RedisModule_OpenKey`","role":"content","text":"RedisModuleKey *RedisModule_OpenKey(RedisModuleCtx *ctx,\n                                        robj *keyname,\n                                        int mode);\n\n**Available since:** 4.0.0\n\nReturn a handle representing a Redis key, so that it is possible\nto call other APIs with the key handle as argument to perform\noperations on the key.\n\nThe return value is the handle representing the key, that must be\nclosed with [`RedisModule_CloseKey()`](#RedisModule_CloseKey).\n\nIf the key does not exist and `REDISMODULE_WRITE` mode is requested, the handle\nis still returned, since it is possible to perform operations on\na yet not existing key (that will be created, for example, after\na list push operation). If the mode is just `REDISMODULE_READ` instead, and the\nkey does not exist, NULL is returned. However it is still safe to\ncall [`RedisModule_CloseKey()`](#RedisModule_CloseKey) and [`RedisModule_KeyType()`](#RedisModule_KeyType) on a NULL\nvalue.\n\nExtra flags that can be pass to the API under the mode argument:\n* `REDISMODULE_OPEN_KEY_NOTOUCH` - Avoid touching the LRU/LFU of the key when opened.\n* `REDISMODULE_OPEN_KEY_NONOTIFY` - Don't trigger keyspace event on key misses.\n* `REDISMODULE_OPEN_KEY_NOSTATS` - Don't update keyspace hits/misses counters.\n* `REDISMODULE_OPEN_KEY_NOEXPIRE` - Avoid deleting lazy expired keys.\n* `REDISMODULE_OPEN_KEY_NOEFFECTS` - Avoid any effects from fetching the key.\n* `REDISMODULE_OPEN_KEY_ACCESS_EXPIRED` - Access expired keys that have not yet been deleted\n\n<span id=\"RedisModule_GetOpenKeyModesAll\"></span>"},{"id":"redismodule_getopenkeymodesall","title":"`RedisModule_GetOpenKeyModesAll`","role":"content","text":"int RedisModule_GetOpenKeyModesAll(void);\n\n**Available since:** 7.2.0\n\n\nReturns the full OpenKey modes mask, using the return value\nthe module can check if a certain set of OpenKey modes are supported\nby the redis server version in use.\nExample:\n\n       int supportedMode = RedisModule_GetOpenKeyModesAll();\n       if (supportedMode & REDISMODULE_OPEN_KEY_NOTOUCH) {\n             // REDISMODULE_OPEN_KEY_NOTOUCH is supported\n       } else{\n             // REDISMODULE_OPEN_KEY_NOTOUCH is not supported\n       }\n\n<span id=\"RedisModule_CloseKey\"></span>"},{"id":"redismodule_closekey","title":"`RedisModule_CloseKey`","role":"content","text":"void RedisModule_CloseKey(RedisModuleKey *key);\n\n**Available since:** 4.0.0\n\nClose a key handle.\n\n<span id=\"RedisModule_KeyType\"></span>"},{"id":"redismodule_keytype","title":"`RedisModule_KeyType`","role":"content","text":"int RedisModule_KeyType(RedisModuleKey *key);\n\n**Available since:** 4.0.0\n\nReturn the type of the key. If the key pointer is NULL then\n`REDISMODULE_KEYTYPE_EMPTY` is returned.\n\n<span id=\"RedisModule_ValueLength\"></span>"},{"id":"redismodule_valuelength","title":"`RedisModule_ValueLength`","role":"content","text":"size_t RedisModule_ValueLength(RedisModuleKey *key);\n\n**Available since:** 4.0.0\n\nReturn the length of the value associated with the key.\nFor strings this is the length of the string. For all the other types\nis the number of elements (just counting keys for hashes).\n\nIf the key pointer is NULL or the key is empty, zero is returned.\n\n<span id=\"RedisModule_DeleteKey\"></span>"},{"id":"redismodule_deletekey","title":"`RedisModule_DeleteKey`","role":"content","text":"int RedisModule_DeleteKey(RedisModuleKey *key);\n\n**Available since:** 4.0.0\n\nIf the key is open for writing, remove it, and setup the key to\naccept new writes as an empty key (that will be created on demand).\nOn success `REDISMODULE_OK` is returned. If the key is not open for\nwriting `REDISMODULE_ERR` is returned.\n\n<span id=\"RedisModule_UnlinkKey\"></span>"},{"id":"redismodule_unlinkkey","title":"`RedisModule_UnlinkKey`","role":"content","text":"int RedisModule_UnlinkKey(RedisModuleKey *key);\n\n**Available since:** 4.0.7\n\nIf the key is open for writing, unlink it (that is delete it in a\nnon-blocking way, not reclaiming memory immediately) and setup the key to\naccept new writes as an empty key (that will be created on demand).\nOn success `REDISMODULE_OK` is returned. If the key is not open for\nwriting `REDISMODULE_ERR` is returned.\n\n<span id=\"RedisModule_GetExpire\"></span>"},{"id":"redismodule_getexpire","title":"`RedisModule_GetExpire`","role":"content","text":"mstime_t RedisModule_GetExpire(RedisModuleKey *key);\n\n**Available since:** 4.0.0\n\nReturn the key expire value, as milliseconds of remaining TTL.\nIf no TTL is associated with the key or if the key is empty,\n`REDISMODULE_NO_EXPIRE` is returned.\n\n<span id=\"RedisModule_SetExpire\"></span>"},{"id":"redismodule_setexpire","title":"`RedisModule_SetExpire`","role":"content","text":"int RedisModule_SetExpire(RedisModuleKey *key, mstime_t expire);\n\n**Available since:** 4.0.0\n\nSet a new expire for the key. If the special expire\n`REDISMODULE_NO_EXPIRE` is set, the expire is cancelled if there was\none (the same as the PERSIST command).\n\nNote that the expire must be provided as a positive integer representing\nthe number of milliseconds of TTL the key should have.\n\nThe function returns `REDISMODULE_OK` on success or `REDISMODULE_ERR` if\nthe key was not open for writing or is an empty key.\n\n<span id=\"RedisModule_GetAbsExpire\"></span>"},{"id":"redismodule_getabsexpire","title":"`RedisModule_GetAbsExpire`","role":"content","text":"mstime_t RedisModule_GetAbsExpire(RedisModuleKey *key);\n\n**Available since:** 6.2.2\n\nReturn the key expire value, as absolute Unix timestamp.\nIf no TTL is associated with the key or if the key is empty,\n`REDISMODULE_NO_EXPIRE` is returned.\n\n<span id=\"RedisModule_SetAbsExpire\"></span>"},{"id":"redismodule_setabsexpire","title":"`RedisModule_SetAbsExpire`","role":"content","text":"int RedisModule_SetAbsExpire(RedisModuleKey *key, mstime_t expire);\n\n**Available since:** 6.2.2\n\nSet a new expire for the key. If the special expire\n`REDISMODULE_NO_EXPIRE` is set, the expire is cancelled if there was\none (the same as the PERSIST command).\n\nNote that the expire must be provided as a positive integer representing\nthe absolute Unix timestamp the key should have.\n\nThe function returns `REDISMODULE_OK` on success or `REDISMODULE_ERR` if\nthe key was not open for writing or is an empty key.\n\n<span id=\"RedisModule_CreateKeyMetaClass\"></span>"},{"id":"redismodule_createkeymetaclass","title":"`RedisModule_CreateKeyMetaClass`","role":"content","text":"RedisModuleKeyMetaClassId RedisModule_CreateKeyMetaClass(RedisModuleCtx *ctx,;\n\n**Available since:** 8.6.0\n\nRegister a new key metadata class exported by the module.\n\nKey metadata allows modules to attach up to 8 bytes of metadata to any Redis key,\nregardless of the key's type. This metadata persists across key operations like\nCOPY, RENAME, MOVE, and can be saved/loaded from RDB files.\n\nThe parameters are the following:\n\n* **metaname**: A 9 characters metadata class name that MUST be unique in the Redis\n  Modules ecosystem. Use the charset A-Z a-z 0-9, plus the two \"-_\" characters.\n  A good idea is to use, for example `<metaname>-<vendor>`. For example\n  \"idx-RediSearch\" may mean \"Index metadata by RediSearch module\". To use both\n  lower case and upper case letters helps in order to prevent collisions.\n\n* **metaver**: Encoding version, which is the version of the serialization\n  that a module used in order to persist metadata. As long as the \"metaname\"\n  matches, the RDB loading will be dispatched to the metadata class callbacks\n  whatever 'metaver' is used, however the module can understand if\n  the encoding it must load is of an older version of the module.\n  For example the module \"idx-RediSearch\" initially used metaver=0. Later\n  after an upgrade, it started to serialize metadata in a different format\n  and to register the class with metaver=1. However this module may\n  still load old data produced by an older version if the `rdb_load`\n  callback is able to check the metaver value and act accordingly.\n  The metaver must be a positive value between 0 and 1023.\n\n* **confPtr** is a pointer to a `RedisModuleKeyMetaClassConfig` structure\n  that should be populated with the configuration and callbacks, like in\n  the following example:\n\n        RedisModuleKeyMetaClassConfig config = {\n            .version = REDISMODULE_KEY_META_VERSION,\n            .flags = 1 << REDISMODULE_META_ALLOW_IGNORE,\n            .reset_value = 0,\n            .copy = myMeta_CopyCallback,\n            .rename = myMeta_RenameCallback,\n            .move = myMeta_MoveCallback,\n            .unlink = myMeta_UnlinkCallback,\n            .free = myMeta_FreeCallback,\n            .rdb_load = myMeta_RDBLoadCallback,\n            .rdb_save = myMeta_RDBSaveCallback,\n            .aof_rewrite = myMeta_AOFRewriteCallback,\n            .defrag = myMeta_DefragCallback,\n            .mem_usage = myMeta_MemUsageCallback,\n            .free_effort = myMeta_FreeEffortCallback\n        }\n\n  Redis does NOT take ownership of the config structure itself. The `confPtr` \n  parameter only needs to remain valid during the [`RedisModule_CreateKeyMetaClass()`](#RedisModule_CreateKeyMetaClass) call \n  and can be freed immediately after.\n\n* **version**: Module must set it to `REDISMODULE_KEY_META_VERSION`. This field is\n  bumped when new fields are added; Redis keeps backward compatibility in\n  [`RedisModule_CreateKeyMetaClass()`](#RedisModule_CreateKeyMetaClass).\n\n* **flags**: Currently supports `REDISMODULE_META_ALLOW_IGNORE` (value 0).\n  When set, metadata will be silently ignored during RDB load if the module\n  is not available or if `rdb_load` callback is NULL. Otherwise, RDB loading\n  will fail if metadata is encountered but cannot be loaded.\n\n* **reset_value**: The value to which metadata should be reset when it is being\n  \"removed\" from a key. Typically 0, but can be any 8-byte value. This is\n  especially relevant when metadata is a pointer/handler to external resources.\n\n  IMPORTANT GUARANTEE: Redis only invokes callbacks when meta != `reset_value`.\n\n* **copy**: A callback function pointer for COPY command (optional).\n  - Return 1 to attach `meta` to the new key, or 0 to skip attaching metadata.\n  - If NULL, metadata is ignored during copy.\n  - The `meta` value may be modified in-place to produce a different value\n    for the new key.\n\n* **rename**: A callback function pointer for RENAME command (optional).\n  - If NULL, then metadata is kept during rename.\n  - The `meta` value may be modified in-place to produce a different value\n    for the new key.\n\n* **move**: A callback function pointer for MOVE command (optional).\n  - Return 1 to keep metadata, 0 to drop.\n  - If NULL, then metadata is kept during move.\n  - The `meta` value may be modified in-place to produce a different value\n    for the new key.\n\n* **unlink**: A callback function pointer for unlink operations (optional).\n  - If not provided, then metadata is ignored during unlink.\n  - Indication that key may soon be freed by background thread.\n  - Pointer to meta is provided for modification. If the metadata holds a pointer\n    or handle to resources and you free them here, you should set `*meta=reset_value`\n    to prevent the free callback from being invoked (Redis skips callbacks when\n    meta == reset_value, see reset_value documentation above).\n\n* **free**: A callback function pointer for cleanup (optional).\n  Invoked when a key with this metadata is deleted/overwritten/expired,\n  or when Redis needs to release per-key metadata during lifecycle operations.\n  The module should free any external allocation referenced by `meta`\n  if it uses the 8 bytes as a handle/pointer.\n  This callback may run in a background thread and is not protected by GIL.\n  It also might be called during RDB loading if the load fails after some\n  metadata has been successfully loaded. In this case, keyname will be NULL\n  since the key hasn't been created yet.\n\n* **rdb_load**: A callback function pointer for RDB loading (optional).\n  - Called during RDB loading when metadata for this class is encountered.\n  - Behavior when NULL:\n    > If rdb_load is NULL AND REDISMODULE_META_ALLOW_IGNORE flag is set,\n      the metadata will be silently ignored during RDB load.\n    > If rdb_load is NULL AND the flag is NOT set, RDB loading will fail\n      if metadata for this class is encountered.\n  - Behavior when class is not registered:\n    > If the class was saved with REDISMODULE_META_ALLOW_IGNORE flag but\n      is not registered at load time, the metadata will be silently ignored.\n    > Otherwise, RDB loading will fail.\n  - Callback responsibilities:\n    > Read custom serialized data from `rdb` using RedisModule_Load*() APIs\n    > Deserialize and reconstruct the 8-byte metadata value\n    > Write the final 8-byte value into `*meta`\n    > Return appropriate status code (see below)\n    > Database ID can be derived from `rdb` if needed. The associated key\n      will be loaded immediately after this callback returns.\n  - Parameters:\n    > rdb: RDB I/O context (use RedisModule_Load*() functions to read data)\n    > meta: Pointer to 8-byte metadata slot (write your deserialized value here)\n    > encver: Encoding version (the metadata class version at save time)\n  - Return values:\n    > 1: Attach value `*meta` to the key (success)\n    > 0: Ignore/skip metadata (don't attach, but continue loading - not an error)\n    > -1: Error - abort RDB load (e.g., invalid data, version incompatibility)\n           Module MUST clean up any allocated metadata before returning -1.\n\n* **rdb_save**: A callback function pointer for RDB saving (optional).\n  - If set to NULL, Redis will not save metadata to RDB.\n  - Callback should write data using RDB assisting functions: `RedisModule_Save*()`.\n\n* **aof_rewrite**: A callback function pointer for AOF rewrite (optional).\n  Called during AOF rewrite to emit commands that reconstruct the metadata.\n  IMPORTANT: For AOF/RDB persistence to work correctly, metadata classes must be\n  registered in `RedisModule_OnLoad()` so they are available when loading persisted\n  data on server startup.\n\n* **defrag**: A callback function pointer for active defragmentation (optional).\n  If the metadata contains pointers, this callback should defragment them.\n\n* **mem_usage**: A callback function pointer for MEMORY USAGE command (optional).\n  Should return the memory used by the metadata in bytes.\n\n* **free_effort**: A callback function pointer for lazy free (optional).\n  Should return the complexity of freeing the metadata to determine if\n  lazy free should be used.\n\nNote: the metadata class name \"AAAAAAAAA\" is reserved and produces an error.\n\nIf [`RedisModule_CreateKeyMetaClass()`](#RedisModule_CreateKeyMetaClass) is called outside of `RedisModule_OnLoad()` function\nand outside of server startup, there is already a metadata class registered\nwith the same name, or if the metadata class name or metaver is invalid,\na negative value is returned.\nOtherwise the new metadata class is registered into Redis, and a reference of\ntype `RedisModuleKeyMetaClassId` is returned: the caller of the function should store\nthis reference into a global variable to make future use of it in the\nmodules metadata API, since a single module may register multiple metadata classes.\nExample code fragment:\n\n     static RedisModuleKeyMetaClassId IndexMetaClass;\n\n     int RedisModule_OnLoad(RedisModuleCtx *ctx) {\n         // some code here ...\n         IndexMetaClass = RedisModule_CreateKeyMetaClass(...);\n     }\n\n<span id=\"RedisModule_ReleaseKeyMetaClass\"></span>"},{"id":"redismodule_releasekeymetaclass","title":"`RedisModule_ReleaseKeyMetaClass`","role":"content","text":"int RedisModule_ReleaseKeyMetaClass(RedisModuleKeyMetaClassId id);\n\n**Available since:** 8.6.0\n\nRelease a class by its ID. Returns 1 on success, 0 on failure.\n\n<span id=\"RedisModule_SetKeyMeta\"></span>"},{"id":"redismodule_setkeymeta","title":"`RedisModule_SetKeyMeta`","role":"content","text":"int RedisModule_SetKeyMeta(RedisModuleKeyMetaClassId id,\n                               RedisModuleKey *key,\n                               uint64_t metadata);\n\n**Available since:** 8.6.0\n\nSet metadata of class id on an opened key. If metadata is already attached,\nit will be overwritten. The caller is responsible for retrieving and freeing\nany existing pointer-based metadata before setting a new value.\n\n<span id=\"RedisModule_GetKeyMeta\"></span>"},{"id":"redismodule_getkeymeta","title":"`RedisModule_GetKeyMeta`","role":"content","text":"int RedisModule_GetKeyMeta(RedisModuleKeyMetaClassId id,\n                               RedisModuleKey *key,\n                               uint64_t *metadata);\n\n**Available since:** 8.6.0\n\nGet metadata of class id from an opened key.\n\n<span id=\"RedisModule_ResetDataset\"></span>"},{"id":"redismodule_resetdataset","title":"`RedisModule_ResetDataset`","role":"content","text":"void RedisModule_ResetDataset(int restart_aof, int async);\n\n**Available since:** 6.0.0\n\nPerforms similar operation to FLUSHALL, and optionally start a new AOF file (if enabled)\nIf `restart_aof` is true, you must make sure the command that triggered this call is not\npropagated to the AOF file.\nWhen async is set to true, db contents will be freed by a background thread.\n\n<span id=\"RedisModule_DbSize\"></span>"},{"id":"redismodule_dbsize","title":"`RedisModule_DbSize`","role":"content","text":"unsigned long long RedisModule_DbSize(RedisModuleCtx *ctx);\n\n**Available since:** 6.0.0\n\nReturns the number of keys in the current db.\n\n<span id=\"RedisModule_RandomKey\"></span>"},{"id":"redismodule_randomkey","title":"`RedisModule_RandomKey`","role":"content","text":"RedisModuleString *RedisModule_RandomKey(RedisModuleCtx *ctx);\n\n**Available since:** 6.0.0\n\nReturns a name of a random key, or NULL if current db is empty.\n\n<span id=\"RedisModule_GetKeyNameFromOptCtx\"></span>"},{"id":"redismodule_getkeynamefromoptctx","title":"`RedisModule_GetKeyNameFromOptCtx`","role":"content","text":"const RedisModuleString *RedisModule_GetKeyNameFromOptCtx(RedisModuleKeyOptCtx *ctx);\n\n**Available since:** 7.0.0\n\nReturns the name of the key currently being processed.\n\n<span id=\"RedisModule_GetToKeyNameFromOptCtx\"></span>"},{"id":"redismodule_gettokeynamefromoptctx","title":"`RedisModule_GetToKeyNameFromOptCtx`","role":"content","text":"const RedisModuleString *RedisModule_GetToKeyNameFromOptCtx(RedisModuleKeyOptCtx *ctx);\n\n**Available since:** 7.0.0\n\nReturns the name of the target key currently being processed.\n\n<span id=\"RedisModule_GetDbIdFromOptCtx\"></span>"},{"id":"redismodule_getdbidfromoptctx","title":"`RedisModule_GetDbIdFromOptCtx`","role":"content","text":"int RedisModule_GetDbIdFromOptCtx(RedisModuleKeyOptCtx *ctx);\n\n**Available since:** 7.0.0\n\nReturns the dbid currently being processed.\n\n<span id=\"RedisModule_GetToDbIdFromOptCtx\"></span>"},{"id":"redismodule_gettodbidfromoptctx","title":"`RedisModule_GetToDbIdFromOptCtx`","role":"content","text":"int RedisModule_GetToDbIdFromOptCtx(RedisModuleKeyOptCtx *ctx);\n\n**Available since:** 7.0.0\n\nReturns the target dbid currently being processed.\n\n<span id=\"section-key-api-for-string-type\"></span>"},{"id":"key-api-for-string-type","title":"Key API for String type","role":"content","text":"See also [`RedisModule_ValueLength()`](#RedisModule_ValueLength), which returns the length of a string.\n\n<span id=\"RedisModule_StringSet\"></span>"},{"id":"redismodule_stringset","title":"`RedisModule_StringSet`","role":"content","text":"int RedisModule_StringSet(RedisModuleKey *key, RedisModuleString *str);\n\n**Available since:** 4.0.0\n\nIf the key is open for writing, set the specified string 'str' as the\nvalue of the key, deleting the old value if any.\nOn success `REDISMODULE_OK` is returned. If the key is not open for\nwriting or there is an active iterator, `REDISMODULE_ERR` is returned.\n\n<span id=\"RedisModule_StringDMA\"></span>"},{"id":"redismodule_stringdma","title":"`RedisModule_StringDMA`","role":"content","text":"char *RedisModule_StringDMA(RedisModuleKey *key, size_t *len, int mode);\n\n**Available since:** 4.0.0\n\nPrepare the key associated string value for DMA access, and returns\na pointer and size (by reference), that the user can use to read or\nmodify the string in-place accessing it directly via pointer.\n\nThe 'mode' is composed by bitwise OR-ing the following flags:\n\n    REDISMODULE_READ -- Read access\n    REDISMODULE_WRITE -- Write access\n\nIf the DMA is not requested for writing, the pointer returned should\nonly be accessed in a read-only fashion.\n\nOn error (wrong type) NULL is returned.\n\nDMA access rules:\n\n1. No other key writing function should be called since the moment\nthe pointer is obtained, for all the time we want to use DMA access\nto read or modify the string.\n\n2. Each time [`RedisModule_StringTruncate()`](#RedisModule_StringTruncate) is called, to continue with the DMA\naccess, [`RedisModule_StringDMA()`](#RedisModule_StringDMA) should be called again to re-obtain\na new pointer and length.\n\n3. If the returned pointer is not NULL, but the length is zero, no\nbyte can be touched (the string is empty, or the key itself is empty)\nso a [`RedisModule_StringTruncate()`](#RedisModule_StringTruncate) call should be used if there is to enlarge\nthe string, and later call StringDMA() again to get the pointer.\n\n<span id=\"RedisModule_StringTruncate\"></span>"},{"id":"redismodule_stringtruncate","title":"`RedisModule_StringTruncate`","role":"content","text":"int RedisModule_StringTruncate(RedisModuleKey *key, size_t newlen);\n\n**Available since:** 4.0.0\n\nIf the key is open for writing and is of string type, resize it, padding\nwith zero bytes if the new length is greater than the old one.\n\nAfter this call, [`RedisModule_StringDMA()`](#RedisModule_StringDMA) must be called again to continue\nDMA access with the new pointer.\n\nThe function returns `REDISMODULE_OK` on success, and `REDISMODULE_ERR` on\nerror, that is, the key is not open for writing, is not a string\nor resizing for more than 512 MB is requested.\n\nIf the key is empty, a string key is created with the new string value\nunless the new length value requested is zero.\n\n<span id=\"section-key-api-for-list-type\"></span>"},{"id":"key-api-for-list-type","title":"Key API for List type","role":"content","text":"Many of the list functions access elements by index. Since a list is in\nessence a doubly-linked list, accessing elements by index is generally an\nO(N) operation. However, if elements are accessed sequentially or with\nindices close together, the functions are optimized to seek the index from\nthe previous index, rather than seeking from the ends of the list.\n\nThis enables iteration to be done efficiently using a simple for loop:\n\n    long n = RedisModule_ValueLength(key);\n    for (long i = 0; i < n; i++) {\n        RedisModuleString *elem = RedisModule_ListGet(key, i);\n        // Do stuff...\n    }\n\nNote that after modifying a list using [`RedisModule_ListPop`](#RedisModule_ListPop), [`RedisModule_ListSet`](#RedisModule_ListSet) or\n[`RedisModule_ListInsert`](#RedisModule_ListInsert), the internal iterator is invalidated so the next operation\nwill require a linear seek.\n\nModifying a list in any another way, for example using [`RedisModule_Call()`](#RedisModule_Call), while a key\nis open will confuse the internal iterator and may cause trouble if the key\nis used after such modifications. The key must be reopened in this case.\n\nSee also [`RedisModule_ValueLength()`](#RedisModule_ValueLength), which returns the length of a list.\n\n<span id=\"RedisModule_ListPush\"></span>"},{"id":"redismodule_listpush","title":"`RedisModule_ListPush`","role":"content","text":"int RedisModule_ListPush(RedisModuleKey *key,\n                             int where,\n                             RedisModuleString *ele);\n\n**Available since:** 4.0.0\n\nPush an element into a list, on head or tail depending on 'where' argument\n(`REDISMODULE_LIST_HEAD` or `REDISMODULE_LIST_TAIL`). If the key refers to an\nempty key opened for writing, the key is created. On success, `REDISMODULE_OK`\nis returned. On failure, `REDISMODULE_ERR` is returned and `errno` is set as\nfollows:\n\n- EINVAL if key or ele is NULL.\n- ENOTSUP if the key is of another type than list.\n- EBADF if the key is not opened for writing.\n\nNote: Before Redis 7.0, `errno` was not set by this function.\n\n<span id=\"RedisModule_ListPop\"></span>"},{"id":"redismodule_listpop","title":"`RedisModule_ListPop`","role":"content","text":"RedisModuleString *RedisModule_ListPop(RedisModuleKey *key, int where);\n\n**Available since:** 4.0.0\n\nPop an element from the list, and returns it as a module string object\nthat the user should be free with [`RedisModule_FreeString()`](#RedisModule_FreeString) or by enabling\nautomatic memory. The `where` argument specifies if the element should be\npopped from the beginning or the end of the list (`REDISMODULE_LIST_HEAD` or\n`REDISMODULE_LIST_TAIL`). On failure, the command returns NULL and sets\n`errno` as follows:\n\n- EINVAL if key is NULL.\n- ENOTSUP if the key is empty or of another type than list.\n- EBADF if the key is not opened for writing.\n\nNote: Before Redis 7.0, `errno` was not set by this function.\n\n<span id=\"RedisModule_ListGet\"></span>"},{"id":"redismodule_listget","title":"`RedisModule_ListGet`","role":"content","text":"RedisModuleString *RedisModule_ListGet(RedisModuleKey *key, long index);\n\n**Available since:** 7.0.0\n\nReturns the element at index `index` in the list stored at `key`, like the\nLINDEX command. The element should be free'd using [`RedisModule_FreeString()`](#RedisModule_FreeString) or using\nautomatic memory management.\n\nThe index is zero-based, so 0 means the first element, 1 the second element\nand so on. Negative indices can be used to designate elements starting at the\ntail of the list. Here, -1 means the last element, -2 means the penultimate\nand so forth.\n\nWhen no value is found at the given key and index, NULL is returned and\n`errno` is set as follows:\n\n- EINVAL if key is NULL.\n- ENOTSUP if the key is not a list.\n- EBADF if the key is not opened for reading.\n- EDOM if the index is not a valid index in the list.\n\n<span id=\"RedisModule_ListSet\"></span>"},{"id":"redismodule_listset","title":"`RedisModule_ListSet`","role":"content","text":"int RedisModule_ListSet(RedisModuleKey *key,\n                            long index,\n                            RedisModuleString *value);\n\n**Available since:** 7.0.0\n\nReplaces the element at index `index` in the list stored at `key`.\n\nThe index is zero-based, so 0 means the first element, 1 the second element\nand so on. Negative indices can be used to designate elements starting at the\ntail of the list. Here, -1 means the last element, -2 means the penultimate\nand so forth.\n\nOn success, `REDISMODULE_OK` is returned. On failure, `REDISMODULE_ERR` is\nreturned and `errno` is set as follows:\n\n- EINVAL if key or value is NULL.\n- ENOTSUP if the key is not a list.\n- EBADF if the key is not opened for writing.\n- EDOM if the index is not a valid index in the list.\n\n<span id=\"RedisModule_ListInsert\"></span>"},{"id":"redismodule_listinsert","title":"`RedisModule_ListInsert`","role":"content","text":"int RedisModule_ListInsert(RedisModuleKey *key,\n                               long index,\n                               RedisModuleString *value);\n\n**Available since:** 7.0.0\n\nInserts an element at the given index.\n\nThe index is zero-based, so 0 means the first element, 1 the second element\nand so on. Negative indices can be used to designate elements starting at the\ntail of the list. Here, -1 means the last element, -2 means the penultimate\nand so forth. The index is the element's index after inserting it.\n\nOn success, `REDISMODULE_OK` is returned. On failure, `REDISMODULE_ERR` is\nreturned and `errno` is set as follows:\n\n- EINVAL if key or value is NULL.\n- ENOTSUP if the key of another type than list.\n- EBADF if the key is not opened for writing.\n- EDOM if the index is not a valid index in the list.\n\n<span id=\"RedisModule_ListDelete\"></span>"},{"id":"redismodule_listdelete","title":"`RedisModule_ListDelete`","role":"content","text":"int RedisModule_ListDelete(RedisModuleKey *key, long index);\n\n**Available since:** 7.0.0\n\nRemoves an element at the given index. The index is 0-based. A negative index\ncan also be used, counting from the end of the list.\n\nOn success, `REDISMODULE_OK` is returned. On failure, `REDISMODULE_ERR` is\nreturned and `errno` is set as follows:\n\n- EINVAL if key or value is NULL.\n- ENOTSUP if the key is not a list.\n- EBADF if the key is not opened for writing.\n- EDOM if the index is not a valid index in the list.\n\n<span id=\"section-key-api-for-sorted-set-type\"></span>"},{"id":"key-api-for-sorted-set-type","title":"Key API for Sorted Set type","role":"content","text":"See also [`RedisModule_ValueLength()`](#RedisModule_ValueLength), which returns the length of a sorted set.\n\n<span id=\"RedisModule_ZsetAdd\"></span>"},{"id":"redismodule_zsetadd","title":"`RedisModule_ZsetAdd`","role":"content","text":"int RedisModule_ZsetAdd(RedisModuleKey *key,\n                            double score,\n                            RedisModuleString *ele,\n                            int *flagsptr);\n\n**Available since:** 4.0.0\n\nAdd a new element into a sorted set, with the specified 'score'.\nIf the element already exists, the score is updated.\n\nA new sorted set is created at value if the key is an empty open key\nsetup for writing.\n\nAdditional flags can be passed to the function via a pointer, the flags\nare both used to receive input and to communicate state when the function\nreturns. 'flagsptr' can be NULL if no special flags are used.\n\nThe input flags are:\n\n    REDISMODULE_ZADD_XX: Element must already exist. Do nothing otherwise.\n    REDISMODULE_ZADD_NX: Element must not exist. Do nothing otherwise.\n    REDISMODULE_ZADD_GT: If element exists, new score must be greater than the current score. \n                         Do nothing otherwise. Can optionally be combined with XX.\n    REDISMODULE_ZADD_LT: If element exists, new score must be less than the current score.\n                         Do nothing otherwise. Can optionally be combined with XX.\n\nThe output flags are:\n\n    REDISMODULE_ZADD_ADDED: The new element was added to the sorted set.\n    REDISMODULE_ZADD_UPDATED: The score of the element was updated.\n    REDISMODULE_ZADD_NOP: No operation was performed because XX or NX flags.\n\nOn success the function returns `REDISMODULE_OK`. On the following errors\n`REDISMODULE_ERR` is returned:\n\n* The key was not opened for writing.\n* The key is of the wrong type.\n* 'score' double value is not a number (NaN).\n\n<span id=\"RedisModule_ZsetIncrby\"></span>"},{"id":"redismodule_zsetincrby","title":"`RedisModule_ZsetIncrby`","role":"content","text":"int RedisModule_ZsetIncrby(RedisModuleKey *key,\n                               double score,\n                               RedisModuleString *ele,\n                               int *flagsptr,\n                               double *newscore);\n\n**Available since:** 4.0.0\n\nThis function works exactly like [`RedisModule_ZsetAdd()`](#RedisModule_ZsetAdd), but instead of setting\na new score, the score of the existing element is incremented, or if the\nelement does not already exist, it is added assuming the old score was\nzero.\n\nThe input and output flags, and the return value, have the same exact\nmeaning, with the only difference that this function will return\n`REDISMODULE_ERR` even when 'score' is a valid double number, but adding it\nto the existing score results into a NaN (not a number) condition.\n\nThis function has an additional field 'newscore', if not NULL is filled\nwith the new score of the element after the increment, if no error\nis returned.\n\n<span id=\"RedisModule_ZsetRem\"></span>"},{"id":"redismodule_zsetrem","title":"`RedisModule_ZsetRem`","role":"content","text":"int RedisModule_ZsetRem(RedisModuleKey *key,\n                            RedisModuleString *ele,\n                            int *deleted);\n\n**Available since:** 4.0.0\n\nRemove the specified element from the sorted set.\nThe function returns `REDISMODULE_OK` on success, and `REDISMODULE_ERR`\non one of the following conditions:\n\n* The key was not opened for writing.\n* The key is of the wrong type.\n\nThe return value does NOT indicate the fact the element was really\nremoved (since it existed) or not, just if the function was executed\nwith success.\n\nIn order to know if the element was removed, the additional argument\n'deleted' must be passed, that populates the integer by reference\nsetting it to 1 or 0 depending on the outcome of the operation.\nThe 'deleted' argument can be NULL if the caller is not interested\nto know if the element was really removed.\n\nEmpty keys will be handled correctly by doing nothing.\n\n<span id=\"RedisModule_ZsetScore\"></span>"},{"id":"redismodule_zsetscore","title":"`RedisModule_ZsetScore`","role":"content","text":"int RedisModule_ZsetScore(RedisModuleKey *key,\n                              RedisModuleString *ele,\n                              double *score);\n\n**Available since:** 4.0.0\n\nOn success retrieve the double score associated at the sorted set element\n'ele' and returns `REDISMODULE_OK`. Otherwise `REDISMODULE_ERR` is returned\nto signal one of the following conditions:\n\n* There is no such element 'ele' in the sorted set.\n* The key is not a sorted set.\n* The key is an open empty key.\n\n<span id=\"section-key-api-for-sorted-set-iterator\"></span>"},{"id":"key-api-for-sorted-set-iterator","title":"Key API for Sorted Set iterator","role":"content","text":"<span id=\"RedisModule_ZsetRangeStop\"></span>"},{"id":"redismodule_zsetrangestop","title":"`RedisModule_ZsetRangeStop`","role":"content","text":"void RedisModule_ZsetRangeStop(RedisModuleKey *key);\n\n**Available since:** 4.0.0\n\nStop a sorted set iteration.\n\n<span id=\"RedisModule_ZsetRangeEndReached\"></span>"},{"id":"redismodule_zsetrangeendreached","title":"`RedisModule_ZsetRangeEndReached`","role":"content","text":"int RedisModule_ZsetRangeEndReached(RedisModuleKey *key);\n\n**Available since:** 4.0.0\n\nReturn the \"End of range\" flag value to signal the end of the iteration.\n\n<span id=\"RedisModule_ZsetFirstInScoreRange\"></span>"},{"id":"redismodule_zsetfirstinscorerange","title":"`RedisModule_ZsetFirstInScoreRange`","role":"content","text":"int RedisModule_ZsetFirstInScoreRange(RedisModuleKey *key,\n                                          double min,\n                                          double max,\n                                          int minex,\n                                          int maxex);\n\n**Available since:** 4.0.0\n\nSetup a sorted set iterator seeking the first element in the specified\nrange. Returns `REDISMODULE_OK` if the iterator was correctly initialized\notherwise `REDISMODULE_ERR` is returned in the following conditions:\n\n1. The value stored at key is not a sorted set or the key is empty.\n\nThe range is specified according to the two double values 'min' and 'max'.\nBoth can be infinite using the following two macros:\n\n* `REDISMODULE_POSITIVE_INFINITE` for positive infinite value\n* `REDISMODULE_NEGATIVE_INFINITE` for negative infinite value\n\n'minex' and 'maxex' parameters, if true, respectively setup a range\nwhere the min and max value are exclusive (not included) instead of\ninclusive.\n\n<span id=\"RedisModule_ZsetLastInScoreRange\"></span>"},{"id":"redismodule_zsetlastinscorerange","title":"`RedisModule_ZsetLastInScoreRange`","role":"content","text":"int RedisModule_ZsetLastInScoreRange(RedisModuleKey *key,\n                                         double min,\n                                         double max,\n                                         int minex,\n                                         int maxex);\n\n**Available since:** 4.0.0\n\nExactly like [`RedisModule_ZsetFirstInScoreRange()`](#RedisModule_ZsetFirstInScoreRange) but the last element of\nthe range is selected for the start of the iteration instead.\n\n<span id=\"RedisModule_ZsetFirstInLexRange\"></span>"},{"id":"redismodule_zsetfirstinlexrange","title":"`RedisModule_ZsetFirstInLexRange`","role":"content","text":"int RedisModule_ZsetFirstInLexRange(RedisModuleKey *key,\n                                        RedisModuleString *min,\n                                        RedisModuleString *max);\n\n**Available since:** 4.0.0\n\nSetup a sorted set iterator seeking the first element in the specified\nlexicographical range. Returns `REDISMODULE_OK` if the iterator was correctly\ninitialized otherwise `REDISMODULE_ERR` is returned in the\nfollowing conditions:\n\n1. The value stored at key is not a sorted set or the key is empty.\n2. The lexicographical range 'min' and 'max' format is invalid.\n\n'min' and 'max' should be provided as two `RedisModuleString` objects\nin the same format as the parameters passed to the ZRANGEBYLEX command.\nThe function does not take ownership of the objects, so they can be released\nASAP after the iterator is setup.\n\n<span id=\"RedisModule_ZsetLastInLexRange\"></span>"},{"id":"redismodule_zsetlastinlexrange","title":"`RedisModule_ZsetLastInLexRange`","role":"content","text":"int RedisModule_ZsetLastInLexRange(RedisModuleKey *key,\n                                       RedisModuleString *min,\n                                       RedisModuleString *max);\n\n**Available since:** 4.0.0\n\nExactly like [`RedisModule_ZsetFirstInLexRange()`](#RedisModule_ZsetFirstInLexRange) but the last element of\nthe range is selected for the start of the iteration instead.\n\n<span id=\"RedisModule_ZsetRangeCurrentElement\"></span>"},{"id":"redismodule_zsetrangecurrentelement","title":"`RedisModule_ZsetRangeCurrentElement`","role":"content","text":"RedisModuleString *RedisModule_ZsetRangeCurrentElement(RedisModuleKey *key,\n                                                           double *score);\n\n**Available since:** 4.0.0\n\nReturn the current sorted set element of an active sorted set iterator\nor NULL if the range specified in the iterator does not include any\nelement.\n\n<span id=\"RedisModule_ZsetRangeNext\"></span>"},{"id":"redismodule_zsetrangenext","title":"`RedisModule_ZsetRangeNext`","role":"content","text":"int RedisModule_ZsetRangeNext(RedisModuleKey *key);\n\n**Available since:** 4.0.0\n\nGo to the next element of the sorted set iterator. Returns 1 if there was\na next element, 0 if we are already at the latest element or the range\ndoes not include any item at all.\n\n<span id=\"RedisModule_ZsetRangePrev\"></span>"},{"id":"redismodule_zsetrangeprev","title":"`RedisModule_ZsetRangePrev`","role":"content","text":"int RedisModule_ZsetRangePrev(RedisModuleKey *key);\n\n**Available since:** 4.0.0\n\nGo to the previous element of the sorted set iterator. Returns 1 if there was\na previous element, 0 if we are already at the first element or the range\ndoes not include any item at all.\n\n<span id=\"section-key-api-for-hash-type\"></span>"},{"id":"key-api-for-hash-type","title":"Key API for Hash type","role":"content","text":"See also [`RedisModule_ValueLength()`](#RedisModule_ValueLength), which returns the number of fields in a hash.\n\n<span id=\"RedisModule_HashSet\"></span>"},{"id":"redismodule_hashset","title":"`RedisModule_HashSet`","role":"content","text":"int RedisModule_HashSet(RedisModuleKey *key, int flags, ...);\n\n**Available since:** 4.0.0\n\nSet the field of the specified hash field to the specified value.\nIf the key is an empty key open for writing, it is created with an empty\nhash value, in order to set the specified field.\n\nThe function is variadic and the user must specify pairs of field\nnames and values, both as `RedisModuleString` pointers (unless the\nCFIELD option is set, see later). At the end of the field/value-ptr pairs,\nNULL must be specified as last argument to signal the end of the arguments\nin the variadic function.\n\nExample to set the hash argv[1] to the value argv[2]:\n\n     RedisModule_HashSet(key,REDISMODULE_HASH_NONE,argv[1],argv[2],NULL);\n\nThe function can also be used in order to delete fields (if they exist)\nby setting them to the specified value of `REDISMODULE_HASH_DELETE`:\n\n     RedisModule_HashSet(key,REDISMODULE_HASH_NONE,argv[1],\n                         REDISMODULE_HASH_DELETE,NULL);\n\nThe behavior of the command changes with the specified flags, that can be\nset to `REDISMODULE_HASH_NONE` if no special behavior is needed.\n\n    REDISMODULE_HASH_NX: The operation is performed only if the field was not\n                         already existing in the hash.\n    REDISMODULE_HASH_XX: The operation is performed only if the field was\n                         already existing, so that a new value could be\n                         associated to an existing filed, but no new fields\n                         are created.\n    REDISMODULE_HASH_CFIELDS: The field names passed are null terminated C\n                              strings instead of RedisModuleString objects.\n    REDISMODULE_HASH_COUNT_ALL: Include the number of inserted fields in the\n                                returned number, in addition to the number of\n                                updated and deleted fields. (Added in Redis\n                                6.2.)\n\nUnless NX is specified, the command overwrites the old field value with\nthe new one.\n\nWhen using `REDISMODULE_HASH_CFIELDS`, field names are reported using\nnormal C strings, so for example to delete the field \"foo\" the following\ncode can be used:\n\n     RedisModule_HashSet(key,REDISMODULE_HASH_CFIELDS,\"foo\",\n                         REDISMODULE_HASH_DELETE,NULL);\n\nReturn value:\n\nThe number of fields existing in the hash prior to the call, which have been\nupdated (its old value has been replaced by a new value) or deleted. If the\nflag `REDISMODULE_HASH_COUNT_ALL` is set, inserted fields not previously\nexisting in the hash are also counted.\n\nIf the return value is zero, `errno` is set (since Redis 6.2) as follows:\n\n- EINVAL if any unknown flags are set or if key is NULL.\n- ENOTSUP if the key is associated with a non Hash value.\n- EBADF if the key was not opened for writing.\n- ENOENT if no fields were counted as described under Return value above.\n  This is not actually an error. The return value can be zero if all fields\n  were just created and the `COUNT_ALL` flag was unset, or if changes were held\n  back due to the NX and XX flags.\n\nNOTICE: The return value semantics of this function are very different\nbetween Redis 6.2 and older versions. Modules that use it should determine\nthe Redis version and handle it accordingly.\n\n<span id=\"RedisModule_HashGet\"></span>"},{"id":"redismodule_hashget","title":"`RedisModule_HashGet`","role":"content","text":"int RedisModule_HashGet(RedisModuleKey *key, int flags, ...);\n\n**Available since:** 4.0.0\n\nGet fields from a hash value. This function is called using a variable\nnumber of arguments, alternating a field name (as a `RedisModuleString`\npointer) with a pointer to a `RedisModuleString` pointer, that is set to the\nvalue of the field if the field exists, or NULL if the field does not exist.\nAt the end of the field/value-ptr pairs, NULL must be specified as last\nargument to signal the end of the arguments in the variadic function.\n\nThis is an example usage:\n\n     RedisModuleString *first, *second;\n     RedisModule_HashGet(mykey,REDISMODULE_HASH_NONE,argv[1],&first,\n                         argv[2],&second,NULL);\n\nAs with [`RedisModule_HashSet()`](#RedisModule_HashSet) the behavior of the command can be specified\npassing flags different than `REDISMODULE_HASH_NONE`:\n\n`REDISMODULE_HASH_CFIELDS`: field names as null terminated C strings.\n\n`REDISMODULE_HASH_EXISTS`: instead of setting the value of the field\nexpecting a `RedisModuleString` pointer to pointer, the function just\nreports if the field exists or not and expects an integer pointer\nas the second element of each pair.\n\n`REDISMODULE_HASH_EXPIRE_TIME`: retrieves the expiration time of a field in the hash.\nThe function expects a `mstime_t` pointer as the second element of each pair.\nIf the field does not exist or has no expiration, the value is set to \n`REDISMODULE_NO_EXPIRE`. This flag must not be used with `REDISMODULE_HASH_EXISTS`.\n\nExample of `REDISMODULE_HASH_CFIELDS`:\n\n     RedisModuleString *username, *hashedpass;\n     RedisModule_HashGet(mykey,REDISMODULE_HASH_CFIELDS,\"username\",&username,\"hp\",&hashedpass, NULL);\n\nExample of `REDISMODULE_HASH_EXISTS`:\n\n     int exists;\n     RedisModule_HashGet(mykey,REDISMODULE_HASH_EXISTS,\"username\",&exists,NULL);\n\nExample of `REDISMODULE_HASH_EXPIRE_TIME`:\n\n     mstime_t hpExpireTime; \n     RedisModule_HashGet(mykey,REDISMODULE_HASH_EXPIRE_TIME,\"hp\",&hpExpireTime,NULL);\n     \nThe function returns `REDISMODULE_OK` on success and `REDISMODULE_ERR` if\nthe key is not a hash value.\n\nMemory management:\n\nThe returned `RedisModuleString` objects should be released with\n[`RedisModule_FreeString()`](#RedisModule_FreeString), or by enabling automatic memory management.\n\n<span id=\"RedisModule_HashFieldMinExpire\"></span>"},{"id":"redismodule_hashfieldminexpire","title":"`RedisModule_HashFieldMinExpire`","role":"content","text":"mstime_t RedisModule_HashFieldMinExpire(RedisModuleKey *key);\n\n**Available since:** 8.0.0\n\n\nRetrieves the minimum expiration time of fields in a hash.\n\nReturn:\n  - The minimum expiration time (in milliseconds) of the hash fields if at\n    least one field has an expiration set.\n  - `REDISMODULE_NO_EXPIRE` if no fields have an expiration set or if the key\n    is not a hash.\n\n<span id=\"section-key-api-for-stream-type\"></span>"},{"id":"key-api-for-stream-type","title":"Key API for Stream type","role":"content","text":"For an introduction to streams, see [https://redis.io/docs/latest/develop/data-types/streams/](https://redis.io/docs/latest/develop/data-types/streams/).\n\nThe type `RedisModuleStreamID`, which is used in stream functions, is a struct\nwith two 64-bit fields and is defined as\n\n    typedef struct RedisModuleStreamID {\n        uint64_t ms;\n        uint64_t seq;\n    } RedisModuleStreamID;\n\nSee also [`RedisModule_ValueLength()`](#RedisModule_ValueLength), which returns the length of a stream, and the\nconversion functions [`RedisModule_StringToStreamID()`](#RedisModule_StringToStreamID) and [`RedisModule_CreateStringFromStreamID()`](#RedisModule_CreateStringFromStreamID).\n\n<span id=\"RedisModule_StreamAdd\"></span>"},{"id":"redismodule_streamadd","title":"`RedisModule_StreamAdd`","role":"content","text":"int RedisModule_StreamAdd(RedisModuleKey *key,\n                              int flags,\n                              RedisModuleStreamID *id,\n                              RedisModuleString **argv,\n                              long numfields);\n\n**Available since:** 6.2.0\n\nAdds an entry to a stream. Like XADD without trimming.\n\n- `key`: The key where the stream is (or will be) stored\n- `flags`: A bit field of\n  - `REDISMODULE_STREAM_ADD_AUTOID`: Assign a stream ID automatically, like\n    `*` in the XADD command.\n- `id`: If the `AUTOID` flag is set, this is where the assigned ID is\n  returned. Can be NULL if `AUTOID` is set, if you don't care to receive the\n  ID. If `AUTOID` is not set, this is the requested ID.\n- `argv`: A pointer to an array of size `numfields * 2` containing the\n  fields and values.\n- `numfields`: The number of field-value pairs in `argv`.\n\nReturns `REDISMODULE_OK` if an entry has been added. On failure,\n`REDISMODULE_ERR` is returned and `errno` is set as follows:\n\n- EINVAL if called with invalid arguments\n- ENOTSUP if the key refers to a value of a type other than stream\n- EBADF if the key was not opened for writing\n- EDOM if the given ID was 0-0 or not greater than all other IDs in the\n  stream (only if the AUTOID flag is unset)\n- EFBIG if the stream has reached the last possible ID\n- ERANGE if the elements are too large to be stored.\n\n<span id=\"RedisModule_StreamDelete\"></span>"},{"id":"redismodule_streamdelete","title":"`RedisModule_StreamDelete`","role":"content","text":"int RedisModule_StreamDelete(RedisModuleKey *key, RedisModuleStreamID *id);\n\n**Available since:** 6.2.0\n\nDeletes an entry from a stream.\n\n- `key`: A key opened for writing, with no stream iterator started.\n- `id`: The stream ID of the entry to delete.\n\nReturns `REDISMODULE_OK` on success. On failure, `REDISMODULE_ERR` is returned\nand `errno` is set as follows:\n\n- EINVAL if called with invalid arguments\n- ENOTSUP if the key refers to a value of a type other than stream or if the\n  key is empty\n- EBADF if the key was not opened for writing or if a stream iterator is\n  associated with the key\n- ENOENT if no entry with the given stream ID exists\n\nSee also [`RedisModule_StreamIteratorDelete()`](#RedisModule_StreamIteratorDelete) for deleting the current entry while\niterating using a stream iterator.\n\n<span id=\"RedisModule_StreamIteratorStart\"></span>"},{"id":"redismodule_streamiteratorstart","title":"`RedisModule_StreamIteratorStart`","role":"content","text":"int RedisModule_StreamIteratorStart(RedisModuleKey *key,\n                                        int flags,\n                                        RedisModuleStreamID *start,\n                                        RedisModuleStreamID *end);\n\n**Available since:** 6.2.0\n\nSets up a stream iterator.\n\n- `key`: The stream key opened for reading using [`RedisModule_OpenKey()`](#RedisModule_OpenKey).\n- `flags`:\n  - `REDISMODULE_STREAM_ITERATOR_EXCLUSIVE`: Don't include `start` and `end`\n    in the iterated range.\n  - `REDISMODULE_STREAM_ITERATOR_REVERSE`: Iterate in reverse order, starting\n    from the `end` of the range.\n- `start`: The lower bound of the range. Use NULL for the beginning of the\n  stream.\n- `end`: The upper bound of the range. Use NULL for the end of the stream.\n\nReturns `REDISMODULE_OK` on success. On failure, `REDISMODULE_ERR` is returned\nand `errno` is set as follows:\n\n- EINVAL if called with invalid arguments\n- ENOTSUP if the key refers to a value of a type other than stream or if the\n  key is empty\n- EBADF if the key was not opened for writing or if a stream iterator is\n  already associated with the key\n- EDOM if `start` or `end` is outside the valid range\n\nReturns `REDISMODULE_OK` on success and `REDISMODULE_ERR` if the key doesn't\nrefer to a stream or if invalid arguments were given.\n\nThe stream IDs are retrieved using [`RedisModule_StreamIteratorNextID()`](#RedisModule_StreamIteratorNextID) and\nfor each stream ID, the fields and values are retrieved using\n[`RedisModule_StreamIteratorNextField()`](#RedisModule_StreamIteratorNextField). The iterator is freed by calling\n[`RedisModule_StreamIteratorStop()`](#RedisModule_StreamIteratorStop).\n\nExample (error handling omitted):\n\n    RedisModule_StreamIteratorStart(key, 0, startid_ptr, endid_ptr);\n    RedisModuleStreamID id;\n    long numfields;\n    while (RedisModule_StreamIteratorNextID(key, &id, &numfields) ==\n           REDISMODULE_OK) {\n        RedisModuleString *field, *value;\n        while (RedisModule_StreamIteratorNextField(key, &field, &value) ==\n               REDISMODULE_OK) {\n            //\n            // ... Do stuff ...\n            //\n            RedisModule_FreeString(ctx, field);\n            RedisModule_FreeString(ctx, value);\n        }\n    }\n    RedisModule_StreamIteratorStop(key);\n\n<span id=\"RedisModule_StreamIteratorStop\"></span>"},{"id":"redismodule_streamiteratorstop","title":"`RedisModule_StreamIteratorStop`","role":"content","text":"int RedisModule_StreamIteratorStop(RedisModuleKey *key);\n\n**Available since:** 6.2.0\n\nStops a stream iterator created using [`RedisModule_StreamIteratorStart()`](#RedisModule_StreamIteratorStart) and\nreclaims its memory.\n\nReturns `REDISMODULE_OK` on success. On failure, `REDISMODULE_ERR` is returned\nand `errno` is set as follows:\n\n- EINVAL if called with a NULL key\n- ENOTSUP if the key refers to a value of a type other than stream or if the\n  key is empty\n- EBADF if the key was not opened for writing or if no stream iterator is\n  associated with the key\n\n<span id=\"RedisModule_StreamIteratorNextID\"></span>"},{"id":"redismodule_streamiteratornextid","title":"`RedisModule_StreamIteratorNextID`","role":"content","text":"int RedisModule_StreamIteratorNextID(RedisModuleKey *key,\n                                         RedisModuleStreamID *id,\n                                         long *numfields);\n\n**Available since:** 6.2.0\n\nFinds the next stream entry and returns its stream ID and the number of\nfields.\n\n- `key`: Key for which a stream iterator has been started using\n  [`RedisModule_StreamIteratorStart()`](#RedisModule_StreamIteratorStart).\n- `id`: The stream ID returned. NULL if you don't care.\n- `numfields`: The number of fields in the found stream entry. NULL if you\n  don't care.\n\nReturns `REDISMODULE_OK` and sets `*id` and `*numfields` if an entry was found.\nOn failure, `REDISMODULE_ERR` is returned and `errno` is set as follows:\n\n- EINVAL if called with a NULL key\n- ENOTSUP if the key refers to a value of a type other than stream or if the\n  key is empty\n- EBADF if no stream iterator is associated with the key\n- ENOENT if there are no more entries in the range of the iterator\n\nIn practice, if [`RedisModule_StreamIteratorNextID()`](#RedisModule_StreamIteratorNextID) is called after a successful call\nto [`RedisModule_StreamIteratorStart()`](#RedisModule_StreamIteratorStart) and with the same key, it is safe to assume that\nan `REDISMODULE_ERR` return value means that there are no more entries.\n\nUse [`RedisModule_StreamIteratorNextField()`](#RedisModule_StreamIteratorNextField) to retrieve the fields and values.\nSee the example at [`RedisModule_StreamIteratorStart()`](#RedisModule_StreamIteratorStart).\n\n<span id=\"RedisModule_StreamIteratorNextField\"></span>"},{"id":"redismodule_streamiteratornextfield","title":"`RedisModule_StreamIteratorNextField`","role":"content","text":"int RedisModule_StreamIteratorNextField(RedisModuleKey *key,\n                                            RedisModuleString **field_ptr,\n                                            RedisModuleString **value_ptr);\n\n**Available since:** 6.2.0\n\nRetrieves the next field of the current stream ID and its corresponding value\nin a stream iteration. This function should be called repeatedly after calling\n[`RedisModule_StreamIteratorNextID()`](#RedisModule_StreamIteratorNextID) to fetch each field-value pair.\n\n- `key`: Key where a stream iterator has been started.\n- `field_ptr`: This is where the field is returned.\n- `value_ptr`: This is where the value is returned.\n\nReturns `REDISMODULE_OK` and points `*field_ptr` and `*value_ptr` to freshly\nallocated `RedisModuleString` objects. The string objects are freed\nautomatically when the callback finishes if automatic memory is enabled. On\nfailure, `REDISMODULE_ERR` is returned and `errno` is set as follows:\n\n- EINVAL if called with a NULL key\n- ENOTSUP if the key refers to a value of a type other than stream or if the\n  key is empty\n- EBADF if no stream iterator is associated with the key\n- ENOENT if there are no more fields in the current stream entry\n\nIn practice, if [`RedisModule_StreamIteratorNextField()`](#RedisModule_StreamIteratorNextField) is called after a successful\ncall to [`RedisModule_StreamIteratorNextID()`](#RedisModule_StreamIteratorNextID) and with the same key, it is safe to assume\nthat an `REDISMODULE_ERR` return value means that there are no more fields.\n\nSee the example at [`RedisModule_StreamIteratorStart()`](#RedisModule_StreamIteratorStart).\n\n<span id=\"RedisModule_StreamIteratorDelete\"></span>"},{"id":"redismodule_streamiteratordelete","title":"`RedisModule_StreamIteratorDelete`","role":"content","text":"int RedisModule_StreamIteratorDelete(RedisModuleKey *key);\n\n**Available since:** 6.2.0\n\nDeletes the current stream entry while iterating.\n\nThis function can be called after [`RedisModule_StreamIteratorNextID()`](#RedisModule_StreamIteratorNextID) or after any\ncalls to [`RedisModule_StreamIteratorNextField()`](#RedisModule_StreamIteratorNextField).\n\nReturns `REDISMODULE_OK` on success. On failure, `REDISMODULE_ERR` is returned\nand `errno` is set as follows:\n\n- EINVAL if key is NULL\n- ENOTSUP if the key is empty or is of another type than stream\n- EBADF if the key is not opened for writing, if no iterator has been started\n- ENOENT if the iterator has no current stream entry\n\n<span id=\"RedisModule_StreamTrimByLength\"></span>"},{"id":"redismodule_streamtrimbylength","title":"`RedisModule_StreamTrimByLength`","role":"content","text":"long long RedisModule_StreamTrimByLength(RedisModuleKey *key,\n                                             int flags,\n                                             long long length);\n\n**Available since:** 6.2.0\n\nTrim a stream by length, similar to XTRIM with MAXLEN.\n\n- `key`: Key opened for writing.\n- `flags`: A bitfield of\n  - `REDISMODULE_STREAM_TRIM_APPROX`: Trim less if it improves performance,\n    like XTRIM with `~`.\n- `length`: The number of stream entries to keep after trimming.\n\nReturns the number of entries deleted. On failure, a negative value is\nreturned and `errno` is set as follows:\n\n- EINVAL if called with invalid arguments\n- ENOTSUP if the key is empty or of a type other than stream\n- EBADF if the key is not opened for writing\n\n<span id=\"RedisModule_StreamTrimByID\"></span>"},{"id":"redismodule_streamtrimbyid","title":"`RedisModule_StreamTrimByID`","role":"content","text":"long long RedisModule_StreamTrimByID(RedisModuleKey *key,\n                                         int flags,\n                                         RedisModuleStreamID *id);\n\n**Available since:** 6.2.0\n\nTrim a stream by ID, similar to XTRIM with MINID.\n\n- `key`: Key opened for writing.\n- `flags`: A bitfield of\n  - `REDISMODULE_STREAM_TRIM_APPROX`: Trim less if it improves performance,\n    like XTRIM with `~`.\n- `id`: The smallest stream ID to keep after trimming.\n\nReturns the number of entries deleted. On failure, a negative value is\nreturned and `errno` is set as follows:\n\n- EINVAL if called with invalid arguments\n- ENOTSUP if the key is empty or of a type other than stream\n- EBADF if the key is not opened for writing\n\n<span id=\"section-calling-redis-commands-from-modules\"></span>"},{"id":"calling-redis-commands-from-modules","title":"Calling Redis commands from modules","role":"content","text":"[`RedisModule_Call()`](#RedisModule_Call) sends a command to Redis. The remaining functions handle the reply.\n\n<span id=\"RedisModule_FreeCallReply\"></span>"},{"id":"redismodule_freecallreply","title":"`RedisModule_FreeCallReply`","role":"content","text":"void RedisModule_FreeCallReply(RedisModuleCallReply *reply);\n\n**Available since:** 4.0.0\n\nFree a Call reply and all the nested replies it contains if it's an\narray.\n\n<span id=\"RedisModule_CallReplyType\"></span>"},{"id":"redismodule_callreplytype","title":"`RedisModule_CallReplyType`","role":"content","text":"int RedisModule_CallReplyType(RedisModuleCallReply *reply);\n\n**Available since:** 4.0.0\n\nReturn the reply type as one of the following:\n\n- `REDISMODULE_REPLY_UNKNOWN`\n- `REDISMODULE_REPLY_STRING`\n- `REDISMODULE_REPLY_ERROR`\n- `REDISMODULE_REPLY_INTEGER`\n- `REDISMODULE_REPLY_ARRAY`\n- `REDISMODULE_REPLY_NULL`\n- `REDISMODULE_REPLY_MAP`\n- `REDISMODULE_REPLY_SET`\n- `REDISMODULE_REPLY_BOOL`\n- `REDISMODULE_REPLY_DOUBLE`\n- `REDISMODULE_REPLY_BIG_NUMBER`\n- `REDISMODULE_REPLY_VERBATIM_STRING`\n- `REDISMODULE_REPLY_ATTRIBUTE`\n- `REDISMODULE_REPLY_PROMISE`\n\n<span id=\"RedisModule_CallReplyLength\"></span>"},{"id":"redismodule_callreplylength","title":"`RedisModule_CallReplyLength`","role":"content","text":"size_t RedisModule_CallReplyLength(RedisModuleCallReply *reply);\n\n**Available since:** 4.0.0\n\nReturn the reply type length, where applicable.\n\n<span id=\"RedisModule_CallReplyArrayElement\"></span>"},{"id":"redismodule_callreplyarrayelement","title":"`RedisModule_CallReplyArrayElement`","role":"content","text":"RedisModuleCallReply *RedisModule_CallReplyArrayElement(RedisModuleCallReply *reply,\n                                                            size_t idx);\n\n**Available since:** 4.0.0\n\nReturn the 'idx'-th nested call reply element of an array reply, or NULL\nif the reply type is wrong or the index is out of range.\n\n<span id=\"RedisModule_CallReplyInteger\"></span>"},{"id":"redismodule_callreplyinteger","title":"`RedisModule_CallReplyInteger`","role":"content","text":"long long RedisModule_CallReplyInteger(RedisModuleCallReply *reply);\n\n**Available since:** 4.0.0\n\nReturn the `long long` of an integer reply.\n\n<span id=\"RedisModule_CallReplyDouble\"></span>"},{"id":"redismodule_callreplydouble","title":"`RedisModule_CallReplyDouble`","role":"content","text":"double RedisModule_CallReplyDouble(RedisModuleCallReply *reply);\n\n**Available since:** 7.0.0\n\nReturn the double value of a double reply.\n\n<span id=\"RedisModule_CallReplyBigNumber\"></span>"},{"id":"redismodule_callreplybignumber","title":"`RedisModule_CallReplyBigNumber`","role":"content","text":"const char *RedisModule_CallReplyBigNumber(RedisModuleCallReply *reply,\n                                               size_t *len);\n\n**Available since:** 7.0.0\n\nReturn the big number value of a big number reply.\n\n<span id=\"RedisModule_CallReplyVerbatim\"></span>"},{"id":"redismodule_callreplyverbatim","title":"`RedisModule_CallReplyVerbatim`","role":"content","text":"const char *RedisModule_CallReplyVerbatim(RedisModuleCallReply *reply,\n                                              size_t *len,\n                                              const char **format);\n\n**Available since:** 7.0.0\n\nReturn the value of a verbatim string reply,\nAn optional output argument can be given to get verbatim reply format.\n\n<span id=\"RedisModule_CallReplyBool\"></span>"},{"id":"redismodule_callreplybool","title":"`RedisModule_CallReplyBool`","role":"content","text":"int RedisModule_CallReplyBool(RedisModuleCallReply *reply);\n\n**Available since:** 7.0.0\n\nReturn the Boolean value of a Boolean reply.\n\n<span id=\"RedisModule_CallReplySetElement\"></span>"},{"id":"redismodule_callreplysetelement","title":"`RedisModule_CallReplySetElement`","role":"content","text":"RedisModuleCallReply *RedisModule_CallReplySetElement(RedisModuleCallReply *reply,\n                                                          size_t idx);\n\n**Available since:** 7.0.0\n\nReturn the 'idx'-th nested call reply element of a set reply, or NULL\nif the reply type is wrong or the index is out of range.\n\n<span id=\"RedisModule_CallReplyMapElement\"></span>"},{"id":"redismodule_callreplymapelement","title":"`RedisModule_CallReplyMapElement`","role":"content","text":"int RedisModule_CallReplyMapElement(RedisModuleCallReply *reply,\n                                        size_t idx,\n                                        RedisModuleCallReply **key,\n                                        RedisModuleCallReply **val);\n\n**Available since:** 7.0.0\n\nRetrieve the 'idx'-th key and value of a map reply.\n\nReturns:\n- `REDISMODULE_OK` on success.\n- `REDISMODULE_ERR` if idx out of range or if the reply type is wrong.\n\nThe `key` and `value` arguments are used to return by reference, and may be\nNULL if not required.\n\n<span id=\"RedisModule_CallReplyAttribute\"></span>"},{"id":"redismodule_callreplyattribute","title":"`RedisModule_CallReplyAttribute`","role":"content","text":"RedisModuleCallReply *RedisModule_CallReplyAttribute(RedisModuleCallReply *reply);\n\n**Available since:** 7.0.0\n\nReturn the attribute of the given reply, or NULL if no attribute exists.\n\n<span id=\"RedisModule_CallReplyAttributeElement\"></span>"},{"id":"redismodule_callreplyattributeelement","title":"`RedisModule_CallReplyAttributeElement`","role":"content","text":"int RedisModule_CallReplyAttributeElement(RedisModuleCallReply *reply,\n                                              size_t idx,\n                                              RedisModuleCallReply **key,\n                                              RedisModuleCallReply **val);\n\n**Available since:** 7.0.0\n\nRetrieve the 'idx'-th key and value of an attribute reply.\n\nReturns:\n- `REDISMODULE_OK` on success.\n- `REDISMODULE_ERR` if idx out of range or if the reply type is wrong.\n\nThe `key` and `value` arguments are used to return by reference, and may be\nNULL if not required.\n\n<span id=\"RedisModule_CallReplyPromiseSetUnblockHandler\"></span>"},{"id":"redismodule_callreplypromisesetunblockhandler","title":"`RedisModule_CallReplyPromiseSetUnblockHandler`","role":"content","text":"void RedisModule_CallReplyPromiseSetUnblockHandler(RedisModuleCallReply *reply,\n                                                       RedisModuleOnUnblocked on_unblock,\n                                                       void *private_data);\n\n**Available since:** 7.2.0\n\nSet unblock handler (callback and private data) on the given promise `RedisModuleCallReply`.\nThe given reply must be of promise type (`REDISMODULE_REPLY_PROMISE`).\n\n<span id=\"RedisModule_CallReplyPromiseAbort\"></span>"},{"id":"redismodule_callreplypromiseabort","title":"`RedisModule_CallReplyPromiseAbort`","role":"content","text":"int RedisModule_CallReplyPromiseAbort(RedisModuleCallReply *reply,\n                                          void **private_data);\n\n**Available since:** 7.2.0\n\nAbort the execution of a given promise `RedisModuleCallReply`.\nreturn `REDMODULE_OK` in case the abort was done successfully and `REDISMODULE_ERR`\nif its not possible to abort the execution (execution already finished).\nIn case the execution was aborted (`REDMODULE_OK` was returned), the `private_data` out parameter\nwill be set with the value of the private data that was given on '[`RedisModule_CallReplyPromiseSetUnblockHandler`](#RedisModule_CallReplyPromiseSetUnblockHandler)'\nso the caller will be able to release the private data.\n\nIf the execution was aborted successfully, it is promised that the unblock handler will not be called.\nThat said, it is possible that the abort operation will successes but the operation will still continue.\nThis can happened if, for example, a module implements some blocking command and does not respect the\ndisconnect callback. For pure Redis commands this can not happened.\n\n<span id=\"RedisModule_CallReplyStringPtr\"></span>"},{"id":"redismodule_callreplystringptr","title":"`RedisModule_CallReplyStringPtr`","role":"content","text":"const char *RedisModule_CallReplyStringPtr(RedisModuleCallReply *reply,\n                                               size_t *len);\n\n**Available since:** 4.0.0\n\nReturn the pointer and length of a string or error reply.\n\n<span id=\"RedisModule_CreateStringFromCallReply\"></span>"},{"id":"redismodule_createstringfromcallreply","title":"`RedisModule_CreateStringFromCallReply`","role":"content","text":"RedisModuleString *RedisModule_CreateStringFromCallReply(RedisModuleCallReply *reply);\n\n**Available since:** 4.0.0\n\nReturn a new string object from a call reply of type string, error or\ninteger. Otherwise (wrong reply type) return NULL.\n\n<span id=\"RedisModule_SetContextUser\"></span>"},{"id":"redismodule_setcontextuser","title":"`RedisModule_SetContextUser`","role":"content","text":"void RedisModule_SetContextUser(RedisModuleCtx *ctx,\n                                    const RedisModuleUser *user);\n\n**Available since:** 7.0.6\n\nModifies the user that [`RedisModule_Call`](#RedisModule_Call) will use (e.g. for ACL checks)\n\n<span id=\"RedisModule_GetContextUser\"></span>"},{"id":"redismodule_getcontextuser","title":"`RedisModule_GetContextUser`","role":"content","text":"const RedisModuleUser *RedisModule_GetContextUser(RedisModuleCtx *ctx);\n\n**Available since:** 8.8.0\n\nReturns the user associated with the context via [`RedisModule_SetContextUser`](#RedisModule_SetContextUser).\nReturns NULL if no user was set on the context.\nThe returned pointer is borrowed from the context — do NOT free it.\n\n<span id=\"RedisModule_Call\"></span>"},{"id":"redismodule_call","title":"`RedisModule_Call`","role":"content","text":"RedisModuleCallReply *RedisModule_Call(RedisModuleCtx *ctx,\n                                           const char *cmdname,\n                                           const char *fmt,\n                                           ...);\n\n**Available since:** 4.0.0\n\nExported API to call any Redis command from modules.\n\n* **cmdname**: The Redis command to call.\n* **fmt**: A format specifier string for the command's arguments. Each\n  of the arguments should be specified by a valid type specification. The\n  format specifier can also contain the modifiers `!`, `A`, `3` and `R` which\n  don't have a corresponding argument.\n\n    * `b` -- The argument is a buffer and is immediately followed by another\n             argument that is the buffer's length.\n    * `c` -- The argument is a pointer to a plain C string (null-terminated).\n    * `l` -- The argument is a `long long` integer.\n    * `s` -- The argument is a RedisModuleString.\n    * `v` -- The argument(s) is a vector of RedisModuleString.\n    * `!` -- Sends the Redis command and its arguments to replicas and AOF.\n    * `A` -- Suppress AOF propagation, send only to replicas (requires `!`).\n    * `R` -- Suppress replicas propagation, send only to AOF (requires `!`).\n    * `3` -- Return a RESP3 reply. This will change the command reply.\n             e.g., HGETALL returns a map instead of a flat array.\n    * `0` -- Return the reply in auto mode, i.e. the reply format will be the\n             same as the client attached to the given RedisModuleCtx. This will\n             probably used when you want to pass the reply directly to the client.\n    * `C` -- Run a command as the user attached to the context.\n             User is either attached automatically via the client that directly\n             issued the command and created the context or via RedisModule_SetContextUser.\n             If the context is not directly created by an issued command (such as a\n             background context and no user was set on it via RedisModule_SetContextUser,\n             RedisModule_Call will fail.\n             Checks if the command can be executed according to ACL rules and causes\n             the command to run as the determined user, so that any future user\n             dependent activity, such as ACL checks within scripts will proceed as\n             expected.\n             Otherwise, the command will run as the Redis unrestricted user.\n             Upon sending a command from an internal connection, this flag is\n             ignored and the command will run as the Redis unrestricted user.\n    * `S` -- Run the command in a script mode, this means that it will raise\n             an error if a command which are not allowed inside a script\n             (flagged with the `deny-script` flag) is invoked (like SHUTDOWN).\n             In addition, on script mode, write commands are not allowed if there are\n             not enough good replicas (as configured with `min-replicas-to-write`)\n             or when the server is unable to persist to the disk.\n    * `W` -- Do not allow to run any write command (flagged with the `write` flag).\n    * `M` -- Do not allow `deny-oom` flagged commands when over the memory limit.\n    * `E` -- Return error as RedisModuleCallReply. If there is an error before\n             invoking the command, the error is returned using errno mechanism.\n             This flag allows to get the error also as an error CallReply with\n             relevant error message.\n    * 'D' -- A \"Dry Run\" mode. Return before executing the underlying call().\n             If everything succeeded, it will return with a NULL, otherwise it will\n             return with a CallReply object denoting the error, as if it was called with\n             the 'E' code.\n    * 'K' -- Allow running blocking commands. If enabled and the command gets blocked, a\n             special REDISMODULE_REPLY_PROMISE will be returned. This reply type\n             indicates that the command was blocked and the reply will be given asynchronously.\n             The module can use this reply object to set a handler which will be called when\n             the command gets unblocked using RedisModule_CallReplyPromiseSetUnblockHandler.\n             The handler must be set immediately after the command invocation (without releasing\n             the Redis lock in between). If the handler is not set, the blocking command will\n             still continue its execution but the reply will be ignored (fire and forget),\n             notice that this is dangerous in case of role change, as explained below.\n             The module can use RedisModule_CallReplyPromiseAbort to abort the command invocation\n             if it was not yet finished (see RedisModule_CallReplyPromiseAbort documentation for more\n             details). It is also the module's responsibility to abort the execution on role change, either by using\n             server event (to get notified when the instance becomes a replica) or relying on the disconnect\n             callback of the original client. Failing to do so can result in a write operation on a replica.\n             Unlike other call replies, promise call reply **must** be freed while the Redis GIL is locked.\n             Notice that on unblocking, the only promise is that the unblock handler will be called,\n             If the blocking RedisModule_Call caused the module to also block some real client (using RedisModule_BlockClient),\n             it is the module responsibility to unblock this client on the unblock handler.\n             On the unblock handler it is only allowed to perform the following:\n             * Calling additional Redis commands using RedisModule_Call\n             * Open keys using RedisModule_OpenKey\n             * Replicate data to the replica or AOF\n\n             Specifically, it is not allowed to call any Redis module API which are client related such as:\n             * RedisModule_Reply* API's\n             * RedisModule_BlockClient\n             * RedisModule_GetCurrentUserName\n\n* **...**: The actual arguments to the Redis command.\n\nOn success a `RedisModuleCallReply` object is returned, otherwise\nNULL is returned and errno is set to the following values:\n\n* EBADF: wrong format specifier.\n* EINVAL: wrong command arity, or the command was issued from within a\n          per-key post-notification callback (RedisModule_AddPostNotificationJobForKey),\n          where commands are not allowed.\n* ENOENT: command does not exist.\n* EPERM: operation in Cluster instance with key in non local slot.\n* EROFS: operation in Cluster instance when a write command is sent\n         in a readonly state.\n* ENETDOWN: operation in Cluster instance when cluster is down.\n* ENOTSUP: No ACL user for the specified module context\n* EACCES: Command cannot be executed, according to ACL rules\n* ENOSPC: Write or deny-oom command is not allowed\n* ESPIPE: Command not allowed on script mode\n\nExample code fragment:\n\n     reply = RedisModule_Call(ctx,\"INCRBY\",\"sc\",argv[1],\"10\");\n     if (RedisModule_CallReplyType(reply) == REDISMODULE_REPLY_INTEGER) {\n       long long myval = RedisModule_CallReplyInteger(reply);\n       // Do something with myval.\n     }\n\nThis API is documented here: [https://redis.io/docs/latest/develop/reference/modules/](https://redis.io/docs/latest/develop/reference/modules/)\n\n<span id=\"RedisModule_CallReplyProto\"></span>"},{"id":"redismodule_callreplyproto","title":"`RedisModule_CallReplyProto`","role":"content","text":"const char *RedisModule_CallReplyProto(RedisModuleCallReply *reply,\n                                           size_t *len);\n\n**Available since:** 4.0.0\n\nReturn a pointer, and a length, to the protocol returned by the command\nthat returned the reply object.\n\n<span id=\"section-modules-data-types\"></span>"},{"id":"modules-data-types","title":"Modules data types","role":"content","text":"When String DMA or using existing data structures is not enough, it is\npossible to create new data types from scratch and export them to\nRedis. The module must provide a set of callbacks for handling the\nnew values exported (for example in order to provide RDB saving/loading,\nAOF rewrite, and so forth). In this section we define this API.\n\n<span id=\"RedisModule_CreateDataType\"></span>"},{"id":"redismodule_createdatatype","title":"`RedisModule_CreateDataType`","role":"content","text":"moduleType *RedisModule_CreateDataType(RedisModuleCtx *ctx,\n                                           const char *name,\n                                           int encver,\n                                           void *typemethods_ptr);\n\n**Available since:** 4.0.0\n\nRegister a new data type exported by the module. The parameters are the\nfollowing. Please for in depth documentation check the modules API\ndocumentation, especially [https://redis.io/docs/latest/develop/reference/modules/modules-native-types/](https://redis.io/docs/latest/develop/reference/modules/modules-native-types/).\n\n* **name**: A 9 characters data type name that MUST be unique in the Redis\n  Modules ecosystem. Be creative... and there will be no collisions. Use\n  the charset A-Z a-z 9-0, plus the two \"-_\" characters. A good\n  idea is to use, for example `<typename>-<vendor>`. For example\n  \"tree-AntZ\" may mean \"Tree data structure by @antirez\". To use both\n  lower case and upper case letters helps in order to prevent collisions.\n* **encver**: Encoding version, which is, the version of the serialization\n  that a module used in order to persist data. As long as the \"name\"\n  matches, the RDB loading will be dispatched to the type callbacks\n  whatever 'encver' is used, however the module can understand if\n  the encoding it must load are of an older version of the module.\n  For example the module \"tree-AntZ\" initially used encver=0. Later\n  after an upgrade, it started to serialize data in a different format\n  and to register the type with encver=1. However this module may\n  still load old data produced by an older version if the `rdb_load`\n  callback is able to check the encver value and act accordingly.\n  The encver must be a positive value between 0 and 1023.\n\n* **typemethods_ptr** is a pointer to a `RedisModuleTypeMethods` structure\n  that should be populated with the methods callbacks and structure\n  version, like in the following example:\n\n        RedisModuleTypeMethods tm = {\n            .version = REDISMODULE_TYPE_METHOD_VERSION,\n            .rdb_load = myType_RDBLoadCallBack,\n            .rdb_save = myType_RDBSaveCallBack,\n            .aof_rewrite = myType_AOFRewriteCallBack,\n            .free = myType_FreeCallBack,\n\n            // Optional fields\n            .digest = myType_DigestCallBack,\n            .mem_usage = myType_MemUsageCallBack,\n            .aux_load = myType_AuxRDBLoadCallBack,\n            .aux_save = myType_AuxRDBSaveCallBack,\n            .free_effort = myType_FreeEffortCallBack,\n            .unlink = myType_UnlinkCallBack,\n            .copy = myType_CopyCallback,\n            .defrag = myType_DefragCallback\n\n            // Enhanced optional fields\n            .mem_usage2 = myType_MemUsageCallBack2,\n            .free_effort2 = myType_FreeEffortCallBack2,\n            .unlink2 = myType_UnlinkCallBack2,\n            .copy2 = myType_CopyCallback2,\n        }\n\n* **rdb_load**: A callback function pointer that loads data from RDB files.\n* **rdb_save**: A callback function pointer that saves data to RDB files.\n* **aof_rewrite**: A callback function pointer that rewrites data as commands.\n* **digest**: A callback function pointer that is used for `DEBUG DIGEST`.\n* **free**: A callback function pointer that can free a type value.\n* **aux_save**: A callback function pointer that saves out of keyspace data to RDB files.\n  'when' argument is either `REDISMODULE_AUX_BEFORE_RDB` or `REDISMODULE_AUX_AFTER_RDB`.\n* **aux_load**: A callback function pointer that loads out of keyspace data from RDB files.\n  Similar to `aux_save`, returns `REDISMODULE_OK` on success, and ERR otherwise.\n* **free_effort**: A callback function pointer that used to determine whether the module's\n  memory needs to be lazy reclaimed. The module should return the complexity involved by\n  freeing the value. for example: how many pointers are gonna be freed. Note that if it \n  returns 0, we'll always do an async free.\n* **unlink**: A callback function pointer that used to notifies the module that the key has \n  been removed from the DB by redis, and may soon be freed by a background thread. Note that \n  it won't be called on FLUSHALL/FLUSHDB (both sync and async), and the module can use the \n  `RedisModuleEvent_FlushDB` to hook into that.\n* **copy**: A callback function pointer that is used to make a copy of the specified key.\n  The module is expected to perform a deep copy of the specified value and return it.\n  In addition, hints about the names of the source and destination keys is provided.\n  A NULL return value is considered an error and the copy operation fails.\n  Note: if the target key exists and is being overwritten, the copy callback will be\n  called first, followed by a free callback to the value that is being replaced.\n\n* **defrag**: A callback function pointer that is used to request the module to defrag\n  a key. The module should then iterate pointers and call the relevant `RedisModule_Defrag*()`\n  functions to defragment pointers or complex types. The module should continue\n  iterating as long as [`RedisModule_DefragShouldStop()`](#RedisModule_DefragShouldStop) returns a zero value, and return a\n  zero value if finished or non-zero value if more work is left to be done. If more work\n  needs to be done, [`RedisModule_DefragCursorSet()`](#RedisModule_DefragCursorSet) and [`RedisModule_DefragCursorGet()`](#RedisModule_DefragCursorGet) can be used to track\n  this work across different calls.\n  Normally, the defrag mechanism invokes the callback without a time limit, so\n  [`RedisModule_DefragShouldStop()`](#RedisModule_DefragShouldStop) always returns zero. The \"late defrag\" mechanism which has\n  a time limit and provides cursor support is used only for keys that are determined\n  to have significant internal complexity. To determine this, the defrag mechanism\n  uses the `free_effort` callback and the 'active-defrag-max-scan-fields' config directive.\n  NOTE: The value is passed as a `void**` and the function is expected to update the\n  pointer if the top-level value pointer is defragmented and consequently changes.\n\n* **mem_usage2**: Similar to `mem_usage`, but provides the `RedisModuleKeyOptCtx` parameter\n  so that meta information such as key name and db id can be obtained, and\n  the `sample_size` for size estimation (see MEMORY USAGE command).\n* **free_effort2**: Similar to `free_effort`, but provides the `RedisModuleKeyOptCtx` parameter\n  so that meta information such as key name and db id can be obtained.\n* **unlink2**: Similar to `unlink`, but provides the `RedisModuleKeyOptCtx` parameter\n  so that meta information such as key name and db id can be obtained.\n* **copy2**: Similar to `copy`, but provides the `RedisModuleKeyOptCtx` parameter\n  so that meta information such as key names and db ids can be obtained.\n* **aux_save2**: Similar to `aux_save`, but with small semantic change, if the module\n  saves nothing on this callback then no data about this aux field will be written to the\n  RDB and it will be possible to load the RDB even if the module is not loaded.\n\nNote: the module name \"AAAAAAAAA\" is reserved and produces an error, it\nhappens to be pretty lame as well.\n\nIf [`RedisModule_CreateDataType()`](#RedisModule_CreateDataType) is called outside of `RedisModule_OnLoad()` function,\nthere is already a module registering a type with the same name,\nor if the module name or encver is invalid, NULL is returned.\nOtherwise the new type is registered into Redis, and a reference of\ntype `RedisModuleType` is returned: the caller of the function should store\nthis reference into a global variable to make future use of it in the\nmodules type API, since a single module may register multiple types.\nExample code fragment:\n\n     static RedisModuleType *BalancedTreeType;\n\n     int RedisModule_OnLoad(RedisModuleCtx *ctx) {\n         // some code here ...\n         BalancedTreeType = RedisModule_CreateDataType(...);\n     }\n\n<span id=\"RedisModule_ModuleTypeSetValue\"></span>"},{"id":"redismodule_moduletypesetvalue","title":"`RedisModule_ModuleTypeSetValue`","role":"content","text":"int RedisModule_ModuleTypeSetValue(RedisModuleKey *key,\n                                       moduleType *mt,\n                                       void *value);\n\n**Available since:** 4.0.0\n\nIf the key is open for writing, set the specified module type object\nas the value of the key, deleting the old value if any.\nOn success `REDISMODULE_OK` is returned. If the key is not open for\nwriting or there is an active iterator, `REDISMODULE_ERR` is returned.\n\n<span id=\"RedisModule_ModuleTypeGetType\"></span>"},{"id":"redismodule_moduletypegettype","title":"`RedisModule_ModuleTypeGetType`","role":"content","text":"moduleType *RedisModule_ModuleTypeGetType(RedisModuleKey *key);\n\n**Available since:** 4.0.0\n\nAssuming [`RedisModule_KeyType()`](#RedisModule_KeyType) returned `REDISMODULE_KEYTYPE_MODULE` on\nthe key, returns the module type pointer of the value stored at key.\n\nIf the key is NULL, is not associated with a module type, or is empty,\nthen NULL is returned instead.\n\n<span id=\"RedisModule_ModuleTypeGetValue\"></span>"},{"id":"redismodule_moduletypegetvalue","title":"`RedisModule_ModuleTypeGetValue`","role":"content","text":"void *RedisModule_ModuleTypeGetValue(RedisModuleKey *key);\n\n**Available since:** 4.0.0\n\nAssuming [`RedisModule_KeyType()`](#RedisModule_KeyType) returned `REDISMODULE_KEYTYPE_MODULE` on\nthe key, returns the module type low-level value stored at key, as\nit was set by the user via [`RedisModule_ModuleTypeSetValue()`](#RedisModule_ModuleTypeSetValue).\n\nIf the key is NULL, is not associated with a module type, or is empty,\nthen NULL is returned instead.\n\n<span id=\"section-rdb-loading-and-saving-functions\"></span>"},{"id":"rdb-loading-and-saving-functions","title":"RDB loading and saving functions","role":"content","text":"<span id=\"RedisModule_IsIOError\"></span>"},{"id":"redismodule_isioerror","title":"`RedisModule_IsIOError`","role":"content","text":"int RedisModule_IsIOError(RedisModuleIO *io);\n\n**Available since:** 6.0.0\n\nReturns true if any previous IO API failed.\nfor `Load*` APIs the `REDISMODULE_OPTIONS_HANDLE_IO_ERRORS` flag must be set with\n[`RedisModule_SetModuleOptions`](#RedisModule_SetModuleOptions) first.\n\n<span id=\"RedisModule_SaveUnsigned\"></span>"},{"id":"redismodule_saveunsigned","title":"`RedisModule_SaveUnsigned`","role":"content","text":"void RedisModule_SaveUnsigned(RedisModuleIO *io, uint64_t value);\n\n**Available since:** 4.0.0\n\nSave an unsigned 64 bit value into the RDB file. This function should only\nbe called in the context of the `rdb_save` method of modules implementing new\ndata types.\n\n<span id=\"RedisModule_LoadUnsigned\"></span>"},{"id":"redismodule_loadunsigned","title":"`RedisModule_LoadUnsigned`","role":"content","text":"uint64_t RedisModule_LoadUnsigned(RedisModuleIO *io);\n\n**Available since:** 4.0.0\n\nLoad an unsigned 64 bit value from the RDB file. This function should only\nbe called in the context of the `rdb_load` method of modules implementing\nnew data types.\n\n<span id=\"RedisModule_SaveSigned\"></span>"},{"id":"redismodule_savesigned","title":"`RedisModule_SaveSigned`","role":"content","text":"void RedisModule_SaveSigned(RedisModuleIO *io, int64_t value);\n\n**Available since:** 4.0.0\n\nLike [`RedisModule_SaveUnsigned()`](#RedisModule_SaveUnsigned) but for signed 64 bit values.\n\n<span id=\"RedisModule_LoadSigned\"></span>"},{"id":"redismodule_loadsigned","title":"`RedisModule_LoadSigned`","role":"content","text":"int64_t RedisModule_LoadSigned(RedisModuleIO *io);\n\n**Available since:** 4.0.0\n\nLike [`RedisModule_LoadUnsigned()`](#RedisModule_LoadUnsigned) but for signed 64 bit values.\n\n<span id=\"RedisModule_SaveString\"></span>"},{"id":"redismodule_savestring","title":"`RedisModule_SaveString`","role":"content","text":"void RedisModule_SaveString(RedisModuleIO *io, RedisModuleString *s);\n\n**Available since:** 4.0.0\n\nIn the context of the `rdb_save` method of a module type, saves a\nstring into the RDB file taking as input a `RedisModuleString`.\n\nThe string can be later loaded with [`RedisModule_LoadString()`](#RedisModule_LoadString) or\nother Load family functions expecting a serialized string inside\nthe RDB file.\n\n<span id=\"RedisModule_SaveStringBuffer\"></span>"},{"id":"redismodule_savestringbuffer","title":"`RedisModule_SaveStringBuffer`","role":"content","text":"void RedisModule_SaveStringBuffer(RedisModuleIO *io,\n                                      const char *str,\n                                      size_t len);\n\n**Available since:** 4.0.0\n\nLike [`RedisModule_SaveString()`](#RedisModule_SaveString) but takes a raw C pointer and length\nas input.\n\n<span id=\"RedisModule_LoadString\"></span>"},{"id":"redismodule_loadstring","title":"`RedisModule_LoadString`","role":"content","text":"RedisModuleString *RedisModule_LoadString(RedisModuleIO *io);\n\n**Available since:** 4.0.0\n\nIn the context of the `rdb_load` method of a module data type, loads a string\nfrom the RDB file, that was previously saved with [`RedisModule_SaveString()`](#RedisModule_SaveString)\nfunctions family.\n\nThe returned string is a newly allocated `RedisModuleString` object, and\nthe user should at some point free it with a call to [`RedisModule_FreeString()`](#RedisModule_FreeString).\n\nIf the data structure does not store strings as `RedisModuleString` objects,\nthe similar function [`RedisModule_LoadStringBuffer()`](#RedisModule_LoadStringBuffer) could be used instead.\n\n<span id=\"RedisModule_LoadStringBuffer\"></span>"},{"id":"redismodule_loadstringbuffer","title":"`RedisModule_LoadStringBuffer`","role":"content","text":"char *RedisModule_LoadStringBuffer(RedisModuleIO *io, size_t *lenptr);\n\n**Available since:** 4.0.0\n\nLike [`RedisModule_LoadString()`](#RedisModule_LoadString) but returns a heap allocated string that\nwas allocated with [`RedisModule_Alloc()`](#RedisModule_Alloc), and can be resized or freed with\n[`RedisModule_Realloc()`](#RedisModule_Realloc) or [`RedisModule_Free()`](#RedisModule_Free).\n\nThe size of the string is stored at '*lenptr' if not NULL.\nThe returned string is not automatically NULL terminated, it is loaded\nexactly as it was stored inside the RDB file.\n\n<span id=\"RedisModule_SaveDouble\"></span>"},{"id":"redismodule_savedouble","title":"`RedisModule_SaveDouble`","role":"content","text":"void RedisModule_SaveDouble(RedisModuleIO *io, double value);\n\n**Available since:** 4.0.0\n\nIn the context of the `rdb_save` method of a module data type, saves a double\nvalue to the RDB file. The double can be a valid number, a NaN or infinity.\nIt is possible to load back the value with [`RedisModule_LoadDouble()`](#RedisModule_LoadDouble).\n\n<span id=\"RedisModule_LoadDouble\"></span>"},{"id":"redismodule_loaddouble","title":"`RedisModule_LoadDouble`","role":"content","text":"double RedisModule_LoadDouble(RedisModuleIO *io);\n\n**Available since:** 4.0.0\n\nIn the context of the `rdb_save` method of a module data type, loads back the\ndouble value saved by [`RedisModule_SaveDouble()`](#RedisModule_SaveDouble).\n\n<span id=\"RedisModule_SaveFloat\"></span>"},{"id":"redismodule_savefloat","title":"`RedisModule_SaveFloat`","role":"content","text":"void RedisModule_SaveFloat(RedisModuleIO *io, float value);\n\n**Available since:** 4.0.0\n\nIn the context of the `rdb_save` method of a module data type, saves a float\nvalue to the RDB file. The float can be a valid number, a NaN or infinity.\nIt is possible to load back the value with [`RedisModule_LoadFloat()`](#RedisModule_LoadFloat).\n\n<span id=\"RedisModule_LoadFloat\"></span>"},{"id":"redismodule_loadfloat","title":"`RedisModule_LoadFloat`","role":"content","text":"float RedisModule_LoadFloat(RedisModuleIO *io);\n\n**Available since:** 4.0.0\n\nIn the context of the `rdb_save` method of a module data type, loads back the\nfloat value saved by [`RedisModule_SaveFloat()`](#RedisModule_SaveFloat).\n\n<span id=\"RedisModule_SaveLongDouble\"></span>"},{"id":"redismodule_savelongdouble","title":"`RedisModule_SaveLongDouble`","role":"content","text":"void RedisModule_SaveLongDouble(RedisModuleIO *io, long double value);\n\n**Available since:** 6.0.0\n\nIn the context of the `rdb_save` method of a module data type, saves a long double\nvalue to the RDB file. The double can be a valid number, a NaN or infinity.\nIt is possible to load back the value with [`RedisModule_LoadLongDouble()`](#RedisModule_LoadLongDouble).\n\n<span id=\"RedisModule_LoadLongDouble\"></span>"},{"id":"redismodule_loadlongdouble","title":"`RedisModule_LoadLongDouble`","role":"content","text":"long double RedisModule_LoadLongDouble(RedisModuleIO *io);\n\n**Available since:** 6.0.0\n\nIn the context of the `rdb_save` method of a module data type, loads back the\nlong double value saved by [`RedisModule_SaveLongDouble()`](#RedisModule_SaveLongDouble).\n\n<span id=\"section-key-digest-api-debug-digest-interface-for-modules-types\"></span>"},{"id":"key-digest-api-debug-digest-interface-for-modules-types","title":"Key digest API (DEBUG DIGEST interface for modules types)","role":"content","text":"<span id=\"RedisModule_DigestAddStringBuffer\"></span>"},{"id":"redismodule_digestaddstringbuffer","title":"`RedisModule_DigestAddStringBuffer`","role":"content","text":"void RedisModule_DigestAddStringBuffer(RedisModuleDigest *md,\n                                           const char *ele,\n                                           size_t len);\n\n**Available since:** 4.0.0\n\nAdd a new element to the digest. This function can be called multiple times\none element after the other, for all the elements that constitute a given\ndata structure. The function call must be followed by the call to\n[`RedisModule_DigestEndSequence`](#RedisModule_DigestEndSequence) eventually, when all the elements that are\nalways in a given order are added. See the Redis Modules data types\ndocumentation for more info. However this is a quick example that uses Redis\ndata types as an example.\n\nTo add a sequence of unordered elements (for example in the case of a Redis\nSet), the pattern to use is:\n\n    foreach element {\n        AddElement(element);\n        EndSequence();\n    }\n\nBecause Sets are not ordered, so every element added has a position that\ndoes not depend from the other. However if instead our elements are\nordered in pairs, like field-value pairs of a Hash, then one should\nuse:\n\n    foreach key,value {\n        AddElement(key);\n        AddElement(value);\n        EndSequence();\n    }\n\nBecause the key and value will be always in the above order, while instead\nthe single key-value pairs, can appear in any position into a Redis hash.\n\nA list of ordered elements would be implemented with:\n\n    foreach element {\n        AddElement(element);\n    }\n    EndSequence();\n\n<span id=\"RedisModule_DigestAddLongLong\"></span>"},{"id":"redismodule_digestaddlonglong","title":"`RedisModule_DigestAddLongLong`","role":"content","text":"void RedisModule_DigestAddLongLong(RedisModuleDigest *md, long long ll);\n\n**Available since:** 4.0.0\n\nLike [`RedisModule_DigestAddStringBuffer()`](#RedisModule_DigestAddStringBuffer) but takes a `long long` as input\nthat gets converted into a string before adding it to the digest.\n\n<span id=\"RedisModule_DigestEndSequence\"></span>"},{"id":"redismodule_digestendsequence","title":"`RedisModule_DigestEndSequence`","role":"content","text":"void RedisModule_DigestEndSequence(RedisModuleDigest *md);\n\n**Available since:** 4.0.0\n\nSee the documentation for `RedisModule_DigestAddElement()`.\n\n<span id=\"RedisModule_LoadDataTypeFromStringEncver\"></span>"},{"id":"redismodule_loaddatatypefromstringencver","title":"`RedisModule_LoadDataTypeFromStringEncver`","role":"content","text":"void *RedisModule_LoadDataTypeFromStringEncver(const RedisModuleString *str,\n                                                   const moduleType *mt,\n                                                   int encver);\n\n**Available since:** 7.0.0\n\nDecode a serialized representation of a module data type 'mt', in a specific encoding version 'encver'\nfrom string 'str' and return a newly allocated value, or NULL if decoding failed.\n\nThis call basically reuses the '`rdb_load`' callback which module data types\nimplement in order to allow a module to arbitrarily serialize/de-serialize\nkeys, similar to how the Redis 'DUMP' and 'RESTORE' commands are implemented.\n\nModules should generally use the `REDISMODULE_OPTIONS_HANDLE_IO_ERRORS` flag and\nmake sure the de-serialization code properly checks and handles IO errors\n(freeing allocated buffers and returning a NULL).\n\nIf this is NOT done, Redis will handle corrupted (or just truncated) serialized\ndata by producing an error message and terminating the process.\n\n<span id=\"RedisModule_LoadDataTypeFromString\"></span>"},{"id":"redismodule_loaddatatypefromstring","title":"`RedisModule_LoadDataTypeFromString`","role":"content","text":"void *RedisModule_LoadDataTypeFromString(const RedisModuleString *str,\n                                             const moduleType *mt);\n\n**Available since:** 6.0.0\n\nSimilar to [`RedisModule_LoadDataTypeFromStringEncver`](#RedisModule_LoadDataTypeFromStringEncver), original version of the API, kept\nfor backward compatibility.\n\n<span id=\"RedisModule_SaveDataTypeToString\"></span>"},{"id":"redismodule_savedatatypetostring","title":"`RedisModule_SaveDataTypeToString`","role":"content","text":"RedisModuleString *RedisModule_SaveDataTypeToString(RedisModuleCtx *ctx,\n                                                        void *data,\n                                                        const moduleType *mt);\n\n**Available since:** 6.0.0\n\nEncode a module data type 'mt' value 'data' into serialized form, and return it\nas a newly allocated `RedisModuleString`.\n\nThis call basically reuses the '`rdb_save`' callback which module data types\nimplement in order to allow a module to arbitrarily serialize/de-serialize\nkeys, similar to how the Redis 'DUMP' and 'RESTORE' commands are implemented.\n\n<span id=\"RedisModule_GetKeyNameFromDigest\"></span>"},{"id":"redismodule_getkeynamefromdigest","title":"`RedisModule_GetKeyNameFromDigest`","role":"content","text":"const RedisModuleString *RedisModule_GetKeyNameFromDigest(RedisModuleDigest *dig);\n\n**Available since:** 7.0.0\n\nReturns the name of the key currently being processed.\n\n<span id=\"RedisModule_GetDbIdFromDigest\"></span>"},{"id":"redismodule_getdbidfromdigest","title":"`RedisModule_GetDbIdFromDigest`","role":"content","text":"int RedisModule_GetDbIdFromDigest(RedisModuleDigest *dig);\n\n**Available since:** 7.0.0\n\nReturns the database id of the key currently being processed.\n\n<span id=\"section-aof-api-for-modules-data-types\"></span>"},{"id":"aof-api-for-modules-data-types","title":"AOF API for modules data types","role":"content","text":"<span id=\"RedisModule_EmitAOF\"></span>"},{"id":"redismodule_emitaof","title":"`RedisModule_EmitAOF`","role":"content","text":"void RedisModule_EmitAOF(RedisModuleIO *io,\n                             const char *cmdname,\n                             const char *fmt,\n                             ...);\n\n**Available since:** 4.0.0\n\nEmits a command into the AOF during the AOF rewriting process. This function\nis only called in the context of the `aof_rewrite` method of data types exported\nby a module. The command works exactly like [`RedisModule_Call()`](#RedisModule_Call) in the way\nthe parameters are passed, but it does not return anything as the error\nhandling is performed by Redis itself.\n\n<span id=\"section-io-context-handling\"></span>"},{"id":"io-context-handling","title":"IO context handling","role":"content","text":"<span id=\"RedisModule_GetKeyNameFromIO\"></span>"},{"id":"redismodule_getkeynamefromio","title":"`RedisModule_GetKeyNameFromIO`","role":"content","text":"const RedisModuleString *RedisModule_GetKeyNameFromIO(RedisModuleIO *io);\n\n**Available since:** 5.0.5\n\nReturns the name of the key currently being processed.\nThere is no guarantee that the key name is always available, so this may return NULL.\n\n<span id=\"RedisModule_GetKeyNameFromModuleKey\"></span>"},{"id":"redismodule_getkeynamefrommodulekey","title":"`RedisModule_GetKeyNameFromModuleKey`","role":"content","text":"const RedisModuleString *RedisModule_GetKeyNameFromModuleKey(RedisModuleKey *key);\n\n**Available since:** 6.0.0\n\nReturns a `RedisModuleString` with the name of the key from `RedisModuleKey`.\n\n<span id=\"RedisModule_GetDbIdFromModuleKey\"></span>"},{"id":"redismodule_getdbidfrommodulekey","title":"`RedisModule_GetDbIdFromModuleKey`","role":"content","text":"int RedisModule_GetDbIdFromModuleKey(RedisModuleKey *key);\n\n**Available since:** 7.0.0\n\nReturns a database id of the key from `RedisModuleKey`.\n\n<span id=\"RedisModule_GetDbIdFromIO\"></span>"},{"id":"redismodule_getdbidfromio","title":"`RedisModule_GetDbIdFromIO`","role":"content","text":"int RedisModule_GetDbIdFromIO(RedisModuleIO *io);\n\n**Available since:** 7.0.0\n\nReturns the database id of the key currently being processed.\nThere is no guarantee that this info is always available, so this may return -1.\n\n<span id=\"section-logging\"></span>"},{"id":"logging","title":"Logging","role":"content","text":"<span id=\"RedisModule_Log\"></span>"},{"id":"redismodule_log","title":"`RedisModule_Log`","role":"content","text":"void RedisModule_Log(RedisModuleCtx *ctx,\n                         const char *levelstr,\n                         const char *fmt,\n                         ...);\n\n**Available since:** 4.0.0\n\nProduces a log message to the standard Redis log, the format accepts\nprintf-alike specifiers, while level is a string describing the log\nlevel to use when emitting the log, and must be one of the following:\n\n* \"debug\" (`REDISMODULE_LOGLEVEL_DEBUG`)\n* \"verbose\" (`REDISMODULE_LOGLEVEL_VERBOSE`)\n* \"notice\" (`REDISMODULE_LOGLEVEL_NOTICE`)\n* \"warning\" (`REDISMODULE_LOGLEVEL_WARNING`)\n\nIf the specified log level is invalid, verbose is used by default.\nThere is a fixed limit to the length of the log line this function is able\nto emit, this limit is not specified but is guaranteed to be more than\na few lines of text.\n\nThe ctx argument may be NULL if cannot be provided in the context of the\ncaller for instance threads or callbacks, in which case a generic \"module\"\nwill be used instead of the module name.\n\n<span id=\"RedisModule_LogIOError\"></span>"},{"id":"redismodule_logioerror","title":"`RedisModule_LogIOError`","role":"content","text":"void RedisModule_LogIOError(RedisModuleIO *io,\n                                const char *levelstr,\n                                const char *fmt,\n                                ...);\n\n**Available since:** 4.0.0\n\nLog errors from RDB / AOF serialization callbacks.\n\nThis function should be used when a callback is returning a critical\nerror to the caller since cannot load or save the data for some\ncritical reason.\n\n<span id=\"RedisModule__Assert\"></span>"},{"id":"redismodule__assert","title":"`RedisModule__Assert`","role":"content","text":"void RedisModule__Assert(const char *estr, const char *file, int line);\n\n**Available since:** 6.0.0\n\nRedis-like assert function.\n\nThe macro `RedisModule_Assert(expression)` is recommended, rather than\ncalling this function directly.\n\nA failed assertion will shut down the server and produce logging information\nthat looks identical to information generated by Redis itself.\n\n<span id=\"RedisModule_LatencyAddSample\"></span>"},{"id":"redismodule_latencyaddsample","title":"`RedisModule_LatencyAddSample`","role":"content","text":"void RedisModule_LatencyAddSample(const char *event, mstime_t latency);\n\n**Available since:** 6.0.0\n\nAllows adding event to the latency monitor to be observed by the LATENCY\ncommand. The call is skipped if the latency is smaller than the configured\nlatency-monitor-threshold.\n\n<span id=\"section-blocking-clients-from-modules\"></span>"},{"id":"blocking-clients-from-modules","title":"Blocking clients from modules","role":"content","text":"For a guide about blocking commands in modules, see\n[https://redis.io/docs/latest/develop/reference/modules/modules-blocking-ops/](https://redis.io/docs/latest/develop/reference/modules/modules-blocking-ops/).\n\n<span id=\"RedisModule_RegisterAuthCallback\"></span>"},{"id":"redismodule_registerauthcallback","title":"`RedisModule_RegisterAuthCallback`","role":"content","text":"void RedisModule_RegisterAuthCallback(RedisModuleCtx *ctx,\n                                          RedisModuleAuthCallback cb);\n\n**Available since:** 7.2.0\n\nThis API registers a callback to execute in addition to normal password based authentication.\nMultiple callbacks can be registered across different modules. When a Module is unloaded, all the\nauth callbacks registered by it are unregistered.\nThe callbacks are attempted (in the order of most recently registered first) when the AUTH/HELLO\n(with AUTH field provided) commands are called.\nThe callbacks will be called with a module context along with a username and a password, and are\nexpected to take one of the following actions:\n(1) Authenticate - Use the `RedisModule_AuthenticateClient`* API and return `REDISMODULE_AUTH_HANDLED`.\nThis will immediately end the auth chain as successful and add the OK reply.\n(2) Deny Authentication - Return `REDISMODULE_AUTH_HANDLED` without authenticating or blocking the\nclient. Optionally, `err` can be set to a custom error message and `err` will be automatically\nfreed by the server.\nThis will immediately end the auth chain as unsuccessful and add the ERR reply.\n(3) Block a client on authentication - Use the [`RedisModule_BlockClientOnAuth`](#RedisModule_BlockClientOnAuth) API and return\n`REDISMODULE_AUTH_HANDLED`. Here, the client will be blocked until the [`RedisModule_UnblockClient`](#RedisModule_UnblockClient) API is used\nwhich will trigger the auth reply callback (provided through the [`RedisModule_BlockClientOnAuth`](#RedisModule_BlockClientOnAuth)).\nIn this reply callback, the Module should authenticate, deny or skip handling authentication.\n(4) Skip handling Authentication - Return `REDISMODULE_AUTH_NOT_HANDLED` without blocking the\nclient. This will allow the engine to attempt the next module auth callback.\nIf none of the callbacks authenticate or deny auth, then password based auth is attempted and\nwill authenticate or add failure logs and reply to the clients accordingly.\n\nNote: If a client is disconnected while it was in the middle of blocking module auth, that\noccurrence of the AUTH or HELLO command will not be tracked in the INFO command stats.\n\nThe following is an example of how non-blocking module based authentication can be used:\n\n     int auth_cb(RedisModuleCtx *ctx, RedisModuleString *username, RedisModuleString *password, RedisModuleString **err) {\n         const char *user = RedisModule_StringPtrLen(username, NULL);\n         const char *pwd = RedisModule_StringPtrLen(password, NULL);\n         if (!strcmp(user,\"foo\") && !strcmp(pwd,\"valid_password\")) {\n             RedisModule_AuthenticateClientWithACLUser(ctx, \"foo\", 3, NULL, NULL, NULL);\n             return REDISMODULE_AUTH_HANDLED;\n         }\n\n         else if (!strcmp(user,\"foo\") && !strcmp(pwd,\"wrong_password\")) {\n             RedisModuleString *log = RedisModule_CreateString(ctx, \"Module Auth\", 11);\n             RedisModule_ACLAddLogEntryByUserName(ctx, username, log, REDISMODULE_ACL_LOG_AUTH);\n             RedisModule_FreeString(ctx, log);\n             const char *err_msg = \"Auth denied by Misc Module.\";\n             *err = RedisModule_CreateString(ctx, err_msg, strlen(err_msg));\n             return REDISMODULE_AUTH_HANDLED;\n         }\n         return REDISMODULE_AUTH_NOT_HANDLED;\n      }\n\n     int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {\n         if (RedisModule_Init(ctx,\"authmodule\",1,REDISMODULE_APIVER_1)== REDISMODULE_ERR)\n             return REDISMODULE_ERR;\n         RedisModule_RegisterAuthCallback(ctx, auth_cb);\n         return REDISMODULE_OK;\n     }\n\n<span id=\"RedisModule_BlockClient\"></span>"},{"id":"redismodule_blockclient","title":"`RedisModule_BlockClient`","role":"content","text":"RedisModuleBlockedClient *RedisModule_BlockClient(RedisModuleCtx *ctx,\n                                                      RedisModuleCmdFunc reply_callback,\n                                                      ;\n\n**Available since:** 4.0.0\n\nBlock a client in the context of a blocking command, returning a handle\nwhich will be used, later, in order to unblock the client with a call to\n[`RedisModule_UnblockClient()`](#RedisModule_UnblockClient). The arguments specify callback functions\nand a timeout after which the client is unblocked.\n\nThe callbacks are called in the following contexts:\n\n    reply_callback:   called after a successful RedisModule_UnblockClient()\n                      call in order to reply to the client and unblock it.\n\n    timeout_callback: called when the timeout is reached or if `CLIENT UNBLOCK`\n                      is invoked, in order to send an error to the client.\n\n    free_privdata:    called in order to free the private data that is passed\n                      by RedisModule_UnblockClient() call.\n\nNote: [`RedisModule_UnblockClient`](#RedisModule_UnblockClient) should be called for every blocked client,\n      even if client was killed, timed-out or disconnected. Failing to do so\n      will result in memory leaks.\n\nThere are some cases where [`RedisModule_BlockClient()`](#RedisModule_BlockClient) cannot be used:\n\n1. If the client is a Lua script.\n2. If the client is executing a MULTI block.\n\nIn these cases, a call to [`RedisModule_BlockClient()`](#RedisModule_BlockClient) will **not** block the\nclient, but instead produce a specific error reply.\n\nA module that registers a `timeout_callback` function can also be unblocked\nusing the `CLIENT UNBLOCK` command, which will trigger the timeout callback.\nIf a callback function is not registered, then the blocked client will be\ntreated as if it is not in a blocked state and `CLIENT UNBLOCK` will return\na zero value.\n\nMeasuring background time: By default the time spent in the blocked command\nis not account for the total command duration. To include such time you should\nuse [`RedisModule_BlockedClientMeasureTimeStart()`](#RedisModule_BlockedClientMeasureTimeStart) and [`RedisModule_BlockedClientMeasureTimeEnd()`](#RedisModule_BlockedClientMeasureTimeEnd) one,\nor multiple times within the blocking command background work.\n\n<span id=\"RedisModule_BlockClientOnAuth\"></span>"},{"id":"redismodule_blockclientonauth","title":"`RedisModule_BlockClientOnAuth`","role":"content","text":"RedisModuleBlockedClient *RedisModule_BlockClientOnAuth(RedisModuleCtx *ctx,\n                                                            RedisModuleAuthCallback reply_callback,\n                                                            ;\n\n**Available since:** 7.2.0\n\nBlock the current client for module authentication in the background. If module auth is not in\nprogress on the client, the API returns NULL. Otherwise, the client is blocked and the `RedisModule_BlockedClient`\nis returned similar to the [`RedisModule_BlockClient`](#RedisModule_BlockClient) API.\nNote: Only use this API from the context of a module auth callback.\n\n<span id=\"RedisModule_BlockClientGetPrivateData\"></span>"},{"id":"redismodule_blockclientgetprivatedata","title":"`RedisModule_BlockClientGetPrivateData`","role":"content","text":"void *RedisModule_BlockClientGetPrivateData(RedisModuleBlockedClient *blocked_client);\n\n**Available since:** 7.2.0\n\nGet the private data that was previusely set on a blocked client\n\n<span id=\"RedisModule_BlockClientSetPrivateData\"></span>"},{"id":"redismodule_blockclientsetprivatedata","title":"`RedisModule_BlockClientSetPrivateData`","role":"content","text":"void RedisModule_BlockClientSetPrivateData(RedisModuleBlockedClient *blocked_client,\n                                               void *private_data);\n\n**Available since:** 7.2.0\n\nSet private data on a blocked client\n\n<span id=\"RedisModule_BlockClientOnKeys\"></span>"},{"id":"redismodule_blockclientonkeys","title":"`RedisModule_BlockClientOnKeys`","role":"content","text":"RedisModuleBlockedClient *RedisModule_BlockClientOnKeys(RedisModuleCtx *ctx,\n                                                            RedisModuleCmdFunc reply_callback,\n                                                            ;\n\n**Available since:** 6.0.0\n\nThis call is similar to [`RedisModule_BlockClient()`](#RedisModule_BlockClient), however in this case we\ndon't just block the client, but also ask Redis to unblock it automatically\nonce certain keys become \"ready\", that is, contain more data.\n\nBasically this is similar to what a typical Redis command usually does,\nlike BLPOP or BZPOPMAX: the client blocks if it cannot be served ASAP,\nand later when the key receives new data (a list push for instance), the\nclient is unblocked and served.\n\nHowever in the case of this module API, when the client is unblocked?\n\n1. If you block on a key of a type that has blocking operations associated,\n   like a list, a sorted set, a stream, and so forth, the client may be\n   unblocked once the relevant key is targeted by an operation that normally\n   unblocks the native blocking operations for that type. So if we block\n   on a list key, an RPUSH command may unblock our client and so forth.\n2. If you are implementing your native data type, or if you want to add new\n   unblocking conditions in addition to \"1\", you can call the modules API\n   [`RedisModule_SignalKeyAsReady()`](#RedisModule_SignalKeyAsReady).\n\nAnyway we can't be sure if the client should be unblocked just because the\nkey is signaled as ready: for instance a successive operation may change the\nkey, or a client in queue before this one can be served, modifying the key\nas well and making it empty again. So when a client is blocked with\n[`RedisModule_BlockClientOnKeys()`](#RedisModule_BlockClientOnKeys) the reply callback is not called after\n[`RedisModule_UnblockClient()`](#RedisModule_UnblockClient) is called, but every time a key is signaled as ready:\nif the reply callback can serve the client, it returns `REDISMODULE_OK`\nand the client is unblocked, otherwise it will return `REDISMODULE_ERR`\nand we'll try again later.\n\nThe reply callback can access the key that was signaled as ready by\ncalling the API [`RedisModule_GetBlockedClientReadyKey()`](#RedisModule_GetBlockedClientReadyKey), that returns\njust the string name of the key as a `RedisModuleString` object.\n\nThanks to this system we can setup complex blocking scenarios, like\nunblocking a client only if a list contains at least 5 items or other\nmore fancy logics.\n\nNote that another difference with [`RedisModule_BlockClient()`](#RedisModule_BlockClient), is that here\nwe pass the private data directly when blocking the client: it will\nbe accessible later in the reply callback. Normally when blocking with\n[`RedisModule_BlockClient()`](#RedisModule_BlockClient) the private data to reply to the client is\npassed when calling [`RedisModule_UnblockClient()`](#RedisModule_UnblockClient) but here the unblocking\nis performed by Redis itself, so we need to have some private data before\nhand. The private data is used to store any information about the specific\nunblocking operation that you are implementing. Such information will be\nfreed using the `free_privdata` callback provided by the user.\n\nHowever the reply callback will be able to access the argument vector of\nthe command, so the private data is often not needed.\n\nNote: Under normal circumstances [`RedisModule_UnblockClient`](#RedisModule_UnblockClient) should not be\n      called for clients that are blocked on keys (Either the key will\n      become ready or a timeout will occur). If for some reason you do want\n      to call RedisModule_UnblockClient it is possible, but it must NOT be\n      called from module threads and the client will be handled as if it\n      timed out (You must implement the timeout callback in that case).\n\n<span id=\"RedisModule_BlockClientOnKeysWithFlags\"></span>"},{"id":"redismodule_blockclientonkeyswithflags","title":"`RedisModule_BlockClientOnKeysWithFlags`","role":"content","text":"RedisModuleBlockedClient *RedisModule_BlockClientOnKeysWithFlags(RedisModuleCtx *ctx,\n                                                                     RedisModuleCmdFunc reply_callback,\n                                                                     ;\n\n**Available since:** 7.2.0\n\nSame as [`RedisModule_BlockClientOnKeys`](#RedisModule_BlockClientOnKeys), but can take `REDISMODULE_BLOCK_`* flags\nCan be either `REDISMODULE_BLOCK_UNBLOCK_DEFAULT`, which means default behavior (same\nas calling [`RedisModule_BlockClientOnKeys`](#RedisModule_BlockClientOnKeys))\n\nThe flags is a bit mask of these:\n\n- `REDISMODULE_BLOCK_UNBLOCK_DELETED`: The clients should to be awakened in case any of `keys` are deleted.\n                                       Mostly useful for commands that require the key to exist (like XREADGROUP)\n\n<span id=\"RedisModule_SignalKeyAsReady\"></span>"},{"id":"redismodule_signalkeyasready","title":"`RedisModule_SignalKeyAsReady`","role":"content","text":"void RedisModule_SignalKeyAsReady(RedisModuleCtx *ctx, RedisModuleString *key);\n\n**Available since:** 6.0.0\n\nThis function is used in order to potentially unblock a client blocked\non keys with [`RedisModule_BlockClientOnKeys()`](#RedisModule_BlockClientOnKeys). When this function is called,\nall the clients blocked for this key will get their `reply_callback` called.\n\n<span id=\"RedisModule_UnblockClient\"></span>"},{"id":"redismodule_unblockclient","title":"`RedisModule_UnblockClient`","role":"content","text":"int RedisModule_UnblockClient(RedisModuleBlockedClient *bc, void *privdata);\n\n**Available since:** 4.0.0\n\nUnblock a client blocked by `RedisModule_BlockedClient`. This will trigger\nthe reply callbacks to be called in order to reply to the client.\nThe 'privdata' argument will be accessible by the reply callback, so\nthe caller of this function can pass any value that is needed in order to\nactually reply to the client.\n\nA common usage for 'privdata' is a thread that computes something that\nneeds to be passed to the client, included but not limited some slow\nto compute reply or some reply obtained via networking.\n\nNote 1: this function can be called from threads spawned by the module when\nthe client was blocked using [`RedisModule_BlockClient()`](#RedisModule_BlockClient).\n\nNote 2: when we unblock a client that is blocked for keys using the API\n[`RedisModule_BlockClientOnKeys()`](#RedisModule_BlockClientOnKeys), the privdata argument here is not used.\nUnblocking a client that was blocked for keys using this API will still\nrequire the client to get some reply, so the function will use the\n\"timeout\" handler in order to do so (The privdata provided in\n[`RedisModule_BlockClientOnKeys()`](#RedisModule_BlockClientOnKeys) is accessible from the timeout\ncallback via [`RedisModule_GetBlockedClientPrivateData`](#RedisModule_GetBlockedClientPrivateData)).\n\n<span id=\"RedisModule_AbortBlock\"></span>"},{"id":"redismodule_abortblock","title":"`RedisModule_AbortBlock`","role":"content","text":"int RedisModule_AbortBlock(RedisModuleBlockedClient *bc);\n\n**Available since:** 4.0.0\n\nAbort a blocked client blocking operation: the client will be unblocked\nwithout firing any callback.\n\n<span id=\"RedisModule_SetDisconnectCallback\"></span>"},{"id":"redismodule_setdisconnectcallback","title":"`RedisModule_SetDisconnectCallback`","role":"content","text":"void RedisModule_SetDisconnectCallback(RedisModuleBlockedClient *bc,\n                                           RedisModuleDisconnectFunc callback);\n\n**Available since:** 5.0.0\n\nSet a callback that will be called if a blocked client disconnects\nbefore the module has a chance to call [`RedisModule_UnblockClient()`](#RedisModule_UnblockClient)\n\nUsually what you want to do there, is to cleanup your module state\nso that you can call [`RedisModule_UnblockClient()`](#RedisModule_UnblockClient) safely, otherwise\nthe client will remain blocked forever if the timeout is large.\n\nNotes:\n\n1. It is not safe to call Reply* family functions here, it is also\n   useless since the client is gone.\n\n2. This callback is not called if the client disconnects because of\n   a timeout. In such a case, the client is unblocked automatically\n   and the timeout callback is called.\n\n<span id=\"RedisModule_IsBlockedReplyRequest\"></span>"},{"id":"redismodule_isblockedreplyrequest","title":"`RedisModule_IsBlockedReplyRequest`","role":"content","text":"int RedisModule_IsBlockedReplyRequest(RedisModuleCtx *ctx);\n\n**Available since:** 4.0.0\n\nReturn non-zero if a module command was called in order to fill the\nreply for a blocked client.\n\n<span id=\"RedisModule_IsBlockedTimeoutRequest\"></span>"},{"id":"redismodule_isblockedtimeoutrequest","title":"`RedisModule_IsBlockedTimeoutRequest`","role":"content","text":"int RedisModule_IsBlockedTimeoutRequest(RedisModuleCtx *ctx);\n\n**Available since:** 4.0.0\n\nReturn non-zero if a module command was called in order to fill the\nreply for a blocked client that timed out.\n\n<span id=\"RedisModule_GetBlockedClientPrivateData\"></span>"},{"id":"redismodule_getblockedclientprivatedata","title":"`RedisModule_GetBlockedClientPrivateData`","role":"content","text":"void *RedisModule_GetBlockedClientPrivateData(RedisModuleCtx *ctx);\n\n**Available since:** 4.0.0\n\nGet the private data set by [`RedisModule_UnblockClient()`](#RedisModule_UnblockClient)\n\n<span id=\"RedisModule_GetBlockedClientReadyKey\"></span>"},{"id":"redismodule_getblockedclientreadykey","title":"`RedisModule_GetBlockedClientReadyKey`","role":"content","text":"RedisModuleString *RedisModule_GetBlockedClientReadyKey(RedisModuleCtx *ctx);\n\n**Available since:** 6.0.0\n\nGet the key that is ready when the reply callback is called in the context\nof a client blocked by [`RedisModule_BlockClientOnKeys()`](#RedisModule_BlockClientOnKeys).\n\n<span id=\"RedisModule_GetBlockedClientHandle\"></span>"},{"id":"redismodule_getblockedclienthandle","title":"`RedisModule_GetBlockedClientHandle`","role":"content","text":"RedisModuleBlockedClient *RedisModule_GetBlockedClientHandle(RedisModuleCtx *ctx);\n\n**Available since:** 5.0.0\n\nGet the blocked client associated with a given context.\nThis is useful in the reply and timeout callbacks of blocked clients,\nbefore sometimes the module has the blocked client handle references\naround, and wants to cleanup it.\n\n<span id=\"RedisModule_BlockedClientDisconnected\"></span>"},{"id":"redismodule_blockedclientdisconnected","title":"`RedisModule_BlockedClientDisconnected`","role":"content","text":"int RedisModule_BlockedClientDisconnected(RedisModuleCtx *ctx);\n\n**Available since:** 5.0.0\n\nReturn true if when the free callback of a blocked client is called,\nthe reason for the client to be unblocked is that it disconnected\nwhile it was blocked.\n\n<span id=\"section-thread-safe-contexts\"></span>"},{"id":"thread-safe-contexts","title":"Thread Safe Contexts","role":"content","text":"<span id=\"RedisModule_GetThreadSafeContext\"></span>"},{"id":"redismodule_getthreadsafecontext","title":"`RedisModule_GetThreadSafeContext`","role":"content","text":"RedisModuleCtx *RedisModule_GetThreadSafeContext(RedisModuleBlockedClient *bc);\n\n**Available since:** 4.0.0\n\nReturn a context which can be used inside threads to make Redis context\ncalls with certain modules APIs. If 'bc' is not NULL then the module will\nbe bound to a blocked client, and it will be possible to use the\n`RedisModule_Reply*` family of functions to accumulate a reply for when the\nclient will be unblocked. Otherwise the thread safe context will be\ndetached by a specific client.\n\nTo call non-reply APIs, the thread safe context must be prepared with:\n\n    RedisModule_ThreadSafeContextLock(ctx);\n    ... make your call here ...\n    RedisModule_ThreadSafeContextUnlock(ctx);\n\nThis is not needed when using `RedisModule_Reply*` functions, assuming\nthat a blocked client was used when the context was created, otherwise\nno `RedisModule_Reply`* call should be made at all.\n\nNOTE: If you're creating a detached thread safe context (bc is NULL),\nconsider using [`RedisModule_GetDetachedThreadSafeContext`](#RedisModule_GetDetachedThreadSafeContext) which will also retain\nthe module ID and thus be more useful for logging.\n\n<span id=\"RedisModule_GetDetachedThreadSafeContext\"></span>"},{"id":"redismodule_getdetachedthreadsafecontext","title":"`RedisModule_GetDetachedThreadSafeContext`","role":"content","text":"RedisModuleCtx *RedisModule_GetDetachedThreadSafeContext(RedisModuleCtx *ctx);\n\n**Available since:** 6.0.9\n\nReturn a detached thread safe context that is not associated with any\nspecific blocked client, but is associated with the module's context.\n\nThis is useful for modules that wish to hold a global context over\na long term, for purposes such as logging.\n\n<span id=\"RedisModule_FreeThreadSafeContext\"></span>"},{"id":"redismodule_freethreadsafecontext","title":"`RedisModule_FreeThreadSafeContext`","role":"content","text":"void RedisModule_FreeThreadSafeContext(RedisModuleCtx *ctx);\n\n**Available since:** 4.0.0\n\nRelease a thread safe context.\n\n<span id=\"RedisModule_ThreadSafeContextLock\"></span>"},{"id":"redismodule_threadsafecontextlock","title":"`RedisModule_ThreadSafeContextLock`","role":"content","text":"void RedisModule_ThreadSafeContextLock(RedisModuleCtx *ctx);\n\n**Available since:** 4.0.0\n\nAcquire the server lock before executing a thread safe API call.\nThis is not needed for `RedisModule_Reply*` calls when there is\na blocked client connected to the thread safe context.\n\n<span id=\"RedisModule_ThreadSafeContextTryLock\"></span>"},{"id":"redismodule_threadsafecontexttrylock","title":"`RedisModule_ThreadSafeContextTryLock`","role":"content","text":"int RedisModule_ThreadSafeContextTryLock(RedisModuleCtx *ctx);\n\n**Available since:** 6.0.8\n\nSimilar to [`RedisModule_ThreadSafeContextLock`](#RedisModule_ThreadSafeContextLock) but this function\nwould not block if the server lock is already acquired.\n\nIf successful (lock acquired) `REDISMODULE_OK` is returned,\notherwise `REDISMODULE_ERR` is returned and errno is set\naccordingly.\n\n<span id=\"RedisModule_ThreadSafeContextUnlock\"></span>"},{"id":"redismodule_threadsafecontextunlock","title":"`RedisModule_ThreadSafeContextUnlock`","role":"content","text":"void RedisModule_ThreadSafeContextUnlock(RedisModuleCtx *ctx);\n\n**Available since:** 4.0.0\n\nRelease the server lock after a thread safe API call was executed.\n\n<span id=\"section-module-keyspace-notifications-api\"></span>"},{"id":"module-keyspace-notifications-api","title":"Module Keyspace Notifications API","role":"content","text":"<span id=\"RedisModule_SubscribeToKeyspaceEvents\"></span>"},{"id":"redismodule_subscribetokeyspaceevents","title":"`RedisModule_SubscribeToKeyspaceEvents`","role":"content","text":"int RedisModule_SubscribeToKeyspaceEvents(RedisModuleCtx *ctx,\n                                              int types,\n                                              RedisModuleNotificationFunc callback);\n\n**Available since:** 4.0.9\n\nSubscribe to keyspace notifications. This is a low-level version of the\nkeyspace-notifications API. A module can register callbacks to be notified\nwhen keyspace events occur.\n\nNotification events are filtered by their type (string events, set events,\netc), and the subscriber callback receives only events that match a specific\nmask of event types.\n\nWhen subscribing to notifications with [`RedisModule_SubscribeToKeyspaceEvents`](#RedisModule_SubscribeToKeyspaceEvents)\nthe module must provide an event type-mask, denoting the events the subscriber\nis interested in. This can be an ORed mask of any of the following flags:\n\n - `REDISMODULE_NOTIFY_GENERIC`: Generic commands like DEL, EXPIRE, RENAME\n - `REDISMODULE_NOTIFY_STRING`: String events\n - `REDISMODULE_NOTIFY_LIST`: List events\n - `REDISMODULE_NOTIFY_SET`: Set events\n - `REDISMODULE_NOTIFY_HASH`: Hash events\n - `REDISMODULE_NOTIFY_ZSET`: Sorted Set events\n - `REDISMODULE_NOTIFY_EXPIRED`: Expiration events\n - `REDISMODULE_NOTIFY_EVICTED`: Eviction events\n - `REDISMODULE_NOTIFY_STREAM`: Stream events\n - `REDISMODULE_NOTIFY_MODULE`: Module types events\n - `REDISMODULE_NOTIFY_KEYMISS`: Key-miss events\n                               Notice, key-miss event is the only type\n                               of event that is fired from within a read command.\n                               Performing RedisModule_Call with a write command from within\n                               this notification is wrong and discourage. It will\n                               cause the read command that trigger the event to be\n                               replicated to the AOF/Replica.\n\n - `REDISMODULE_NOTIFY_NEW`: New key notification\n - `REDISMODULE_NOTIFY_OVERWRITTEN`: Overwritten events\n - `REDISMODULE_NOTIFY_TYPE_CHANGED`: Type-changed events\n - `REDISMODULE_NOTIFY_KEY_TRIMMED`: Key trimmed events after a slot migration operation\n - `REDISMODULE_NOTIFY_RATE_LIMIT`: Rate limit event\n - `REDISMODULE_NOTIFY_ALL`: All events (Excluding `REDISMODULE_NOTIFY_KEYMISS`,\n                           REDISMODULE_NOTIFY_NEW, REDISMODULE_NOTIFY_OVERWRITTEN,\n                           REDISMODULE_NOTIFY_TYPE_CHANGED, REDISMODULE_NOTIFY_KEY_TRIMMED\n                           and REDISMODULE_NOTIFY_RATE_LIMIT)\n - `REDISMODULE_NOTIFY_LOADED`: A special notification available only for modules,\n                              indicates that the key was loaded from persistence.\n                              Notice, when this event fires, the given key\n                              can not be retained, use RedisModule_CreateStringFromString\n                              instead.\n\nWe do not distinguish between key events and keyspace events, and it is up\nto the module to filter the actions taken based on the key.\n\nThe subscriber signature is:\n\n    int (*RedisModuleNotificationFunc) (RedisModuleCtx *ctx, int type,\n                                        const char *event,\n                                        RedisModuleString *key);\n\n`type` is the event type bit, that must match the mask given at registration\ntime. The event string is the actual command being executed, and key is the\nrelevant Redis key.\n\nNotification callback gets executed with a redis context that can not be\nused to send anything to the client, and has the db number where the event\noccurred as its selected db number.\n\nNotice that it is not necessary to enable notifications in redis.conf for\nmodule notifications to work.\n\nWarning: the notification callbacks are performed in a synchronous manner,\nso notification callbacks must to be fast, or they would slow Redis down.\nIf you need to take long actions, use threads to offload them.\n\nMoreover, the fact that the notification is executed synchronously means\nthat the notification code will be executed in the middle on Redis logic\n(commands logic, eviction, expire). Changing the key space while the logic\nruns is dangerous and discouraged. In order to react to key space events with\nwrite actions, please refer to [`RedisModule_AddPostNotificationJob`](#RedisModule_AddPostNotificationJob).\n\nSee [https://redis.io/docs/latest/develop/use/keyspace-notifications/](https://redis.io/docs/latest/develop/use/keyspace-notifications/) for more information.\n\n<span id=\"RedisModule_UnsubscribeFromKeyspaceEvents\"></span>"},{"id":"redismodule_unsubscribefromkeyspaceevents","title":"`RedisModule_UnsubscribeFromKeyspaceEvents`","role":"content","text":"int RedisModule_UnsubscribeFromKeyspaceEvents(RedisModuleCtx *ctx,\n                                                  int types,\n                                                  RedisModuleNotificationFunc callback);\n\n**Available since:** 8.2.0\n\n\n[`RedisModule_UnsubscribeFromKeyspaceEvents`](#RedisModule_UnsubscribeFromKeyspaceEvents) - Unregister a module's callback from keyspace notifications for specific event types.\n\nThis function removes a previously registered subscription identified by both the event mask and the callback function.\nIt is useful to reduce performance overhead when the module no longer requires notifications for certain events.\n\nParameters:\n - ctx: The `RedisModuleCtx` associated with the calling module.\n - types: The event mask representing the keyspace notification types to unsubscribe from.\n - callback: The callback function pointer that was originally registered for these events.\n\nReturns:\n - `REDISMODULE_OK` on successful removal of the subscription.\n - `REDISMODULE_ERR` if no matching subscription was found or if invalid parameters were provided.\n\n<span id=\"RedisModule_SubscribeToKeyspaceEventsWithSubkeys\"></span>"},{"id":"redismodule_subscribetokeyspaceeventswithsubkeys","title":"`RedisModule_SubscribeToKeyspaceEventsWithSubkeys`","role":"content","text":"int RedisModule_SubscribeToKeyspaceEventsWithSubkeys(RedisModuleCtx *ctx,\n                                                         int types,\n                                                         int flags,\n                                                         RedisModuleNotificationWithSubkeysFunc callback);\n\n**Available since:** 8.8.0\n\nSubscribe to keyspace notifications with subkey information.\n\nThis is the extended version of [`RedisModule_SubscribeToKeyspaceEvents`](#RedisModule_SubscribeToKeyspaceEvents). When subkeys\nare available, the `subkeys` array and `count` are passed to the callback.\n`subkeys` contains only the names of affected subkeys (values are not included),\nand `count` is the number of elements. The array may contain duplicates when\nthe same subkey appears more than once in a command (e.g. HSET key f1 v1 f1 v2\nproduces subkeys=[\"f1\",\"f1\"], count=2). When no subkeys are present, `subkeys`\nwill be NULL and `count` will be 0. Whether events without subkeys are delivered\ndepends on the `flags` parameter (see below).\n\n`types` is a bit mask of event types the module is interested in\n(using the same `REDISMODULE_NOTIFY_`* flags as [`RedisModule_SubscribeToKeyspaceEvents`](#RedisModule_SubscribeToKeyspaceEvents)).\n\n`flags` controls delivery filtering:\n - `REDISMODULE_NOTIFY_FLAG_NONE`: The callback is invoked for all matching\n   events regardless of whether subkeys are present, so a separate\n   [`RedisModule_SubscribeToKeyspaceEvents`](#RedisModule_SubscribeToKeyspaceEvents) registration can be omitted.\n - `REDISMODULE_NOTIFY_FLAG_SUBKEYS_REQUIRED`: The callback is only invoked\n   when subkeys are not empty. Events without subkey information (e.g. SET,\n   EXPIRE, DEL) are skipped.\n\nThe callback signature is:\n  void callback(`RedisModuleCtx` *ctx, int type, const char *event,\n                RedisModuleString *key, RedisModuleString **subkeys, int count);\n\nThe subkeys array and its contents are only valid during the callback.\nThe underlying objects may be stack-allocated or temporary, so\n[`RedisModule_RetainString`](#RedisModule_RetainString) must NOT be used on them. To keep a subkey beyond\nthe callback (e.g. in a [`RedisModule_AddPostNotificationJob`](#RedisModule_AddPostNotificationJob) callback), use\n[`RedisModule_HoldString`](#RedisModule_HoldString) (which handles static objects by copying) or\n[`RedisModule_CreateStringFromString`](#RedisModule_CreateStringFromString) to make a deep copy before returning.\n\n<span id=\"RedisModule_UnsubscribeFromKeyspaceEventsWithSubkeys\"></span>"},{"id":"redismodule_unsubscribefromkeyspaceeventswithsubkeys","title":"`RedisModule_UnsubscribeFromKeyspaceEventsWithSubkeys`","role":"content","text":"int RedisModule_UnsubscribeFromKeyspaceEventsWithSubkeys(RedisModuleCtx *ctx,\n                                                             int types,\n                                                             int flags,\n                                                             RedisModuleNotificationWithSubkeysFunc callback);\n\n**Available since:** 8.8.0\n\nUnregister a module's callback from keyspace notifications with subkeys\nfor specific event types.\n\nThis function removes a previously registered subscription identified by\nthe event mask, delivery flags, and the callback function.\n\nParameters:\n - ctx: The `RedisModuleCtx` associated with the calling module.\n - types: The event mask representing the notification types to unsubscribe from.\n - flags: The delivery flags that were used during registration.\n - callback: The callback function pointer that was originally registered.\n\nReturns:\n - `REDISMODULE_OK` on successful removal of the subscription.\n - `REDISMODULE_ERR` if no matching subscription was found.\n\n<span id=\"RedisModule_AddPostNotificationJob\"></span>"},{"id":"redismodule_addpostnotificationjob","title":"`RedisModule_AddPostNotificationJob`","role":"content","text":"int RedisModule_AddPostNotificationJob(RedisModuleCtx *ctx,\n                                           RedisModulePostNotifyJobFunc callback,\n                                           void *privdata,\n                                           void (*free_privdata)(void*));\n\n**Available since:** 7.2.0\n\nWhen running inside a key space notification callback, it is dangerous and highly discouraged to perform any write\noperation (See [`RedisModule_SubscribeToKeyspaceEvents`](#RedisModule_SubscribeToKeyspaceEvents)). In order to still perform write actions in this scenario,\nRedis provides [`RedisModule_AddPostNotificationJob`](#RedisModule_AddPostNotificationJob) API. The API allows to register a job callback which Redis will call\nwhen the following condition are promised to be fulfilled:\n1. It is safe to perform any write operation.\n2. The job will be called atomically along side the key space notification.\n\nNotice, one job might trigger key space notifications that will trigger more jobs.\nThis raises a concerns of entering an infinite loops, we consider infinite loops\nas a logical bug that need to be fixed in the module, an attempt to protect against\ninfinite loops by halting the execution could result in violation of the feature correctness\nand so Redis will make no attempt to protect the module from infinite loops.\n\n'`free_pd`' can be NULL and in such case will not be used.\n\nReturn `REDISMODULE_OK` on success and `REDISMODULE_ERR` if was called while loading data from disk (AOF or RDB) or\nif the instance is a readonly replica.\n\n<span id=\"RedisModule_AddPostNotificationJobForKey\"></span>"},{"id":"redismodule_addpostnotificationjobforkey","title":"`RedisModule_AddPostNotificationJobForKey`","role":"content","text":"int RedisModule_AddPostNotificationJobForKey(RedisModuleCtx *ctx,\n                                                 RedisModulePostNotifyJobPerKeyFunc callback,\n                                                 RedisModuleString *key,\n                                                 void *privdata,\n                                                 void (*free_privdata)(void*));\n\n**Available since:** unreleased\n\nSibling of [`RedisModule_AddPostNotificationJob`](#RedisModule_AddPostNotificationJob) that fires per-key. May only be\ncalled from within a keyspace-notification handler (the single-key context\nthe API is scoped to). Each call binds the job to the caller-supplied\n`key`, so multi-key commands such as MSET (which emit one notification per\nkey) may register one job per affected key.\n\nFiring schedule (this is what distinguishes the per-key API from\n[`RedisModule_AddPostNotificationJob`](#RedisModule_AddPostNotificationJob) — choosing this API opts into all of it):\n - At the tail of every `call()`, so per-key effects are observable between\n   MULTI/EXEC and script (EVAL/FCALL) sub-commands.\n - During AOF replay, at the tail of every replayed command (single commands\n   and each sub-command of MULTI/EXEC), so per-key state is rebuilt on reload.\n - At the end of the outermost execution unit, for jobs registered outside a\n   sub-command loop (e.g. by a standalone command).\nThe firing happens off the universal afterCommand() hot path — via explicit\ndrains in execCommand (multi.c), scriptCall (script.c), and AOF replay — so\nstandalone commands that don't use the feature pay nothing.\n\nJobs fire in submission order. `key` must be a valid `RedisModuleString`; the\nimplementation takes its own reference and the caller retains ownership of\nits own reference. `free_pd` may be NULL.\n\nCross-phase contract:\n - The callback MUST only touch non-replicated, non-AOF-persisted state,\n   such as module-attached key metadata via [`RedisModule_SetKeyMeta`](#RedisModule_SetKeyMeta) /\n   [`RedisModule_GetKeyMeta`](#RedisModule_GetKeyMeta). The same callback fires on the master, on every\n   replica receiving master-propagated commands, and during AOF replay;\n   each instance maintains its own per-key state independently and they\n   converge because they all run the same callback over the same KSN\n   stream.\n - Touching the keyspace from inside the callback (e.g.\n   `RedisModule_Call(..., \"!...\")`) is a contract violation: on AOF replay it\n   amplifies the AOF; on a replica it diverges the replica from its\n   master. The runtime enforces this — [`RedisModule_Call`](#RedisModule_Call) is refused (returns NULL\n   with errno == EINVAL and a warning logged, or a `-ERR` call-reply when\n   `CALL_REPLIES_AS_ERRORS` is requested) while a per-key callback runs.\n - Firing a keyspace notification from inside the callback\n   ([`RedisModule_NotifyKeyspaceEvent`](#RedisModule_NotifyKeyspaceEvent) / [`RedisModule_NotifyKeyspaceEventWithSubkeys`](#RedisModule_NotifyKeyspaceEventWithSubkeys)) is\n   likewise refused (returns `REDISMODULE_ERR` with a warning logged): a\n   nested notification could enqueue further per-key jobs mid-drain, so the\n   callback is kept free of side effects on the post-notification queue.\n\nReturn `REDISMODULE_OK` on success and `REDISMODULE_ERR` if called outside a\nkeyspace-notification handler. The API is permitted on read-only replicas\nduring AOF replay, so per-key state stays continuously in sync with\nthe keyspace events the instance observes.\n\n<span id=\"RedisModule_GetNotifyKeyspaceEvents\"></span>"},{"id":"redismodule_getnotifykeyspaceevents","title":"`RedisModule_GetNotifyKeyspaceEvents`","role":"content","text":"int RedisModule_GetNotifyKeyspaceEvents(void);\n\n**Available since:** 6.0.0\n\nGet the configured bitmap of notify-keyspace-events (Could be used\nfor additional filtering in `RedisModuleNotificationFunc`)\n\n<span id=\"RedisModule_NotifyKeyspaceEvent\"></span>"},{"id":"redismodule_notifykeyspaceevent","title":"`RedisModule_NotifyKeyspaceEvent`","role":"content","text":"int RedisModule_NotifyKeyspaceEvent(RedisModuleCtx *ctx,\n                                        int type,\n                                        const char *event,\n                                        RedisModuleString *key);\n\n**Available since:** 6.0.0\n\nExpose notifyKeyspaceEvent to modules\n\n<span id=\"RedisModule_NotifyKeyspaceEventWithSubkeys\"></span>"},{"id":"redismodule_notifykeyspaceeventwithsubkeys","title":"`RedisModule_NotifyKeyspaceEventWithSubkeys`","role":"content","text":"int RedisModule_NotifyKeyspaceEventWithSubkeys(RedisModuleCtx *ctx,\n                                                   int type,\n                                                   const char *event,\n                                                   ;\n\n**Available since:** 8.8.0\n\nLike [`RedisModule_NotifyKeyspaceEvent`](#RedisModule_NotifyKeyspaceEvent), but also triggers subkey-level notifications\nwhen subkeys are provided. Both key-level (keyspace/keyevent) and\nsubkey-level (subkeyspace/subkeyevent/subkeyspaceitem/subkeyspaceevent)\nchannels are published to, depending on the server configuration.\n\nThis is the extended version of [`RedisModule_NotifyKeyspaceEvent`](#RedisModule_NotifyKeyspaceEvent) and can actually\nreplace it. When called with subkeys=NULL and count=0, it behaves\nidentically to [`RedisModule_NotifyKeyspaceEvent`](#RedisModule_NotifyKeyspaceEvent).\n\n<span id=\"section-modules-cluster-api\"></span>"},{"id":"modules-cluster-api","title":"Modules Cluster API","role":"content","text":"<span id=\"RedisModule_RegisterClusterMessageReceiver\"></span>"},{"id":"redismodule_registerclustermessagereceiver","title":"`RedisModule_RegisterClusterMessageReceiver`","role":"content","text":"void RedisModule_RegisterClusterMessageReceiver(RedisModuleCtx *ctx,\n                                                    uint8_t type,\n                                                    RedisModuleClusterMessageReceiver callback);\n\n**Available since:** 5.0.0\n\nRegister a callback receiver for cluster messages of type 'type'. If there\nwas already a registered callback, this will replace the callback function\nwith the one provided, otherwise if the callback is set to NULL and there\nis already a callback for this function, the callback is unregistered\n(so this API call is also used in order to delete the receiver).\n\n<span id=\"RedisModule_SendClusterMessage\"></span>"},{"id":"redismodule_sendclustermessage","title":"`RedisModule_SendClusterMessage`","role":"content","text":"int RedisModule_SendClusterMessage(RedisModuleCtx *ctx,\n                                       const char *target_id,\n                                       uint8_t type,\n                                       const char *msg,\n                                       uint32_t len);\n\n**Available since:** 5.0.0\n\nSend a message to all the nodes in the cluster if `target` is NULL, otherwise\nat the specified target, which is a `REDISMODULE_NODE_ID_LEN` bytes node ID, as\nreturned by the receiver callback or by the nodes iteration functions.\n\nThe function returns `REDISMODULE_OK` if the message was successfully sent,\notherwise if the node is not connected or such node ID does not map to any\nknown cluster node, `REDISMODULE_ERR` is returned.\n\n<span id=\"RedisModule_GetClusterNodesList\"></span>"},{"id":"redismodule_getclusternodeslist","title":"`RedisModule_GetClusterNodesList`","role":"content","text":"char **RedisModule_GetClusterNodesList(RedisModuleCtx *ctx, size_t *numnodes);\n\n**Available since:** 5.0.0\n\nReturn an array of string pointers, each string pointer points to a cluster\nnode ID of exactly `REDISMODULE_NODE_ID_LEN` bytes (without any null term).\nThe number of returned node IDs is stored into `*numnodes`.\nHowever if this function is called by a module not running an a Redis\ninstance with Redis Cluster enabled, NULL is returned instead.\n\nThe IDs returned can be used with [`RedisModule_GetClusterNodeInfo()`](#RedisModule_GetClusterNodeInfo) in order\nto get more information about single node.\n\nThe array returned by this function must be freed using the function\n[`RedisModule_FreeClusterNodesList()`](#RedisModule_FreeClusterNodesList).\n\nExample:\n\n    size_t count, j;\n    char **ids = RedisModule_GetClusterNodesList(ctx,&count);\n    for (j = 0; j < count; j++) {\n        RedisModule_Log(ctx,\"notice\",\"Node %.*s\",\n            REDISMODULE_NODE_ID_LEN,ids[j]);\n    }\n    RedisModule_FreeClusterNodesList(ids);\n\n<span id=\"RedisModule_FreeClusterNodesList\"></span>"},{"id":"redismodule_freeclusternodeslist","title":"`RedisModule_FreeClusterNodesList`","role":"content","text":"void RedisModule_FreeClusterNodesList(char **ids);\n\n**Available since:** 5.0.0\n\nFree the node list obtained with [`RedisModule_GetClusterNodesList`](#RedisModule_GetClusterNodesList).\n\n<span id=\"RedisModule_GetMyClusterID\"></span>"},{"id":"redismodule_getmyclusterid","title":"`RedisModule_GetMyClusterID`","role":"content","text":"const char *RedisModule_GetMyClusterID(void);\n\n**Available since:** 5.0.0\n\nReturn this node ID (`REDISMODULE_CLUSTER_ID_LEN` bytes) or NULL if the cluster\nis disabled.\n\n<span id=\"RedisModule_GetClusterSize\"></span>"},{"id":"redismodule_getclustersize","title":"`RedisModule_GetClusterSize`","role":"content","text":"size_t RedisModule_GetClusterSize(void);\n\n**Available since:** 5.0.0\n\nReturn the number of nodes in the cluster, regardless of their state\n(handshake, noaddress, ...) so that the number of active nodes may actually\nbe smaller, but not greater than this number. If the instance is not in\ncluster mode, zero is returned.\n\n<span id=\"RedisModule_GetClusterNodeInfo\"></span>"},{"id":"redismodule_getclusternodeinfo","title":"`RedisModule_GetClusterNodeInfo`","role":"content","text":"int RedisModule_GetClusterNodeInfo(RedisModuleCtx *ctx,\n                                       const char *id,\n                                       char *ip,\n                                       char *master_id,\n                                       int *port,\n                                       int *flags);\n\n**Available since:** 5.0.0\n\nPopulate the specified info for the node having as ID the specified 'id',\nthen returns `REDISMODULE_OK`. Otherwise if the format of node ID is invalid\nor the node ID does not exist from the POV of this local node, `REDISMODULE_ERR`\nis returned.\n\nThe arguments `ip`, `master_id`, `port` and `flags` can be NULL in case we don't\nneed to populate back certain info. If an `ip` and/or `master_id` (only populated\nif the instance is a slave) are specified, they point to buffers holding\nat least INET6_ADDRSTRLEN (46) and `REDISMODULE_NODE_ID_LEN` bytes, respectively.\nThe strings written back as `ip` and `master_id` are not null terminated.\n\nThe list of flags reported is the following:\n\n* `REDISMODULE_NODE_MYSELF`:       This node\n* `REDISMODULE_NODE_MASTER`:       The node is a master\n* `REDISMODULE_NODE_SLAVE`:        The node is a replica\n* `REDISMODULE_NODE_PFAIL`:        We see the node as failing\n* `REDISMODULE_NODE_FAIL`:         The cluster agrees the node is failing\n* `REDISMODULE_NODE_NOFAILOVER`:   The slave is configured to never failover\n\n<span id=\"RedisModule_GetClusterNodeSlotRanges\"></span>"},{"id":"redismodule_getclusternodeslotranges","title":"`RedisModule_GetClusterNodeSlotRanges`","role":"content","text":"RedisModuleSlotRangeArray *RedisModule_GetClusterNodeSlotRanges(RedisModuleCtx *ctx,\n                                                                    const char *nodeid);\n\n**Available since:** unreleased\n\nReturns the slot ranges owned by the cluster node identified by `nodeid`.\n\nAn optional `ctx` can be provided to enable auto-memory management.\nAn empty array is returned if cluster mode is disabled (no cluster nodes\nexist) or if no node matches `nodeid`.\nIf the node is a replica, the slot ranges of its master are returned.\n\nThe returned array must be freed with [`RedisModule_ClusterFreeSlotRanges()`](#RedisModule_ClusterFreeSlotRanges).\n\n<span id=\"RedisModule_SetClusterFlags\"></span>"},{"id":"redismodule_setclusterflags","title":"`RedisModule_SetClusterFlags`","role":"content","text":"void RedisModule_SetClusterFlags(RedisModuleCtx *ctx, uint64_t flags);\n\n**Available since:** 5.0.0\n\nSet Redis Cluster flags in order to change the normal behavior of\nRedis Cluster, especially with the goal of disabling certain functions.\nThis is useful for modules that use the Cluster API in order to create\na different distributed system, but still want to use the Redis Cluster\nmessage bus. Flags that can be set:\n\n* `CLUSTER_MODULE_FLAG_NO_FAILOVER`\n* `CLUSTER_MODULE_FLAG_NO_REDIRECTION`\n\nWith the following effects:\n\n* `NO_FAILOVER`: prevent Redis Cluster slaves from failing over a dead master.\n               Also disables the replica migration feature.\n\n* `NO_REDIRECTION`: Every node will accept any key, without trying to perform\n                  partitioning according to the Redis Cluster algorithm.\n                  Slots information will still be propagated across the\n                  cluster, but without effect.\n\n<span id=\"RedisModule_ClusterDisableTrim\"></span>"},{"id":"redismodule_clusterdisabletrim","title":"`RedisModule_ClusterDisableTrim`","role":"content","text":"int RedisModule_ClusterDisableTrim(RedisModuleCtx *ctx);\n\n**Available since:** 8.4.1\n\n[`RedisModule_ClusterDisableTrim`](#RedisModule_ClusterDisableTrim) allows a module to temporarily prevent slot trimming\nafter a slot migration. This is useful when the module has asynchronous\noperations that rely on keys in migrating slots, which would be trimmed.\n\nThe module must call [`RedisModule_ClusterEnableTrim`](#RedisModule_ClusterEnableTrim) once it has completed those\noperations to re-enable trimming.\n\nTrimming uses a reference counter: every call to [`RedisModule_ClusterDisableTrim`](#RedisModule_ClusterDisableTrim)\nincrements the counter, and every [`RedisModule_ClusterEnableTrim`](#RedisModule_ClusterEnableTrim) call decrements it.\nTrimming remains disabled as long as the counter is greater than zero.\n\nDisable automatic slot trimming.\n\n<span id=\"RedisModule_ClusterEnableTrim\"></span>"},{"id":"redismodule_clusterenabletrim","title":"`RedisModule_ClusterEnableTrim`","role":"content","text":"int RedisModule_ClusterEnableTrim(RedisModuleCtx *ctx);\n\n**Available since:** 8.4.1\n\nEnable automatic slot trimming. See also comments on [`RedisModule_ClusterDisableTrim`](#RedisModule_ClusterDisableTrim).\n\n<span id=\"RedisModule_ClusterKeySlot\"></span>"},{"id":"redismodule_clusterkeyslot","title":"`RedisModule_ClusterKeySlot`","role":"content","text":"unsigned int RedisModule_ClusterKeySlot(RedisModuleString *key);\n\n**Available since:** 7.4.0\n\nReturns the cluster slot of a key, similar to the `CLUSTER KEYSLOT` command.\nThis function works even if cluster mode is not enabled.\n\n<span id=\"RedisModule_ClusterKeySlotC\"></span>"},{"id":"redismodule_clusterkeyslotc","title":"`RedisModule_ClusterKeySlotC`","role":"content","text":"unsigned int RedisModule_ClusterKeySlotC(const char *keystr, size_t keylen);\n\n**Available since:** 8.4.0\n\nLike [`RedisModule_ClusterKeySlot`](#RedisModule_ClusterKeySlot), but gets a char pointer and a length.\nReturns the cluster slot of a key, similar to the `CLUSTER KEYSLOT` command.\nThis function works even if cluster mode is not enabled.\n\n<span id=\"RedisModule_ClusterCanonicalKeyNameInSlot\"></span>"},{"id":"redismodule_clustercanonicalkeynameinslot","title":"`RedisModule_ClusterCanonicalKeyNameInSlot`","role":"content","text":"const char *RedisModule_ClusterCanonicalKeyNameInSlot(unsigned int slot);\n\n**Available since:** 7.4.0\n\nReturns a short string that can be used as a key or as a hash tag in a key,\nsuch that the key maps to the given cluster slot. Returns NULL if slot is not\na valid slot.\n\n<span id=\"RedisModule_ClusterCanAccessKeysInSlot\"></span>"},{"id":"redismodule_clustercanaccesskeysinslot","title":"`RedisModule_ClusterCanAccessKeysInSlot`","role":"content","text":"int RedisModule_ClusterCanAccessKeysInSlot(int slot);\n\n**Available since:** 8.4.0\n\nReturns 1 if keys in the specified slot can be accessed by this node, 0 otherwise.\n\nThis function returns 1 in the following cases:\n- The slot is owned by this node or by its master if this node is a replica\n- The slot is being imported under the old slot migration approach (CLUSTER SETSLOT <slot> IMPORTING ..)\n- Not in cluster mode (all slots are accessible)\n\nReturns 0 for:\n- Invalid slot numbers (< 0 or >= 16384)\n- Slots owned by other nodes\n\n<span id=\"RedisModule_ClusterPropagateForSlotMigration\"></span>"},{"id":"redismodule_clusterpropagateforslotmigration","title":"`RedisModule_ClusterPropagateForSlotMigration`","role":"content","text":"int RedisModule_ClusterPropagateForSlotMigration(RedisModuleCtx *ctx,\n                                                     const char *cmdname,\n                                                     const char *fmt,\n                                                     ...);\n\n**Available since:** 8.4.0\n\nPropagate commands along with slot migration.\n\nThis function allows modules to add commands that will be sent to the\ndestination node as part of the slot migration. It should only be called\nduring one of the following events:\n\n* `REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_MIGRATE_MODULE_PROPAGATE`:\n  the commands are delivered before the actual slot data migration begins.\n  This event is fired in the fork child process just before slot snapshot\n  delivery begins.\n* `REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_MIGRATE_MODULE_PROPAGATE_END`:\n  the commands are delivered at the very end of the migration, just before\n  the STREAM-EOF is sent to the destination. This event is fired in the main\n  process while the slot writes are paused.\n\nThis function can be called multiple times within the same event to\nreplicate multiple commands.\n\nOn success `REDISMODULE_OK` is returned, otherwise\n`REDISMODULE_ERR` is returned and errno is set to the following values:\n\n* EINVAL: function arguments or format specifiers are invalid.\n* EBADF: not called in the correct context, e.g. not called in one of the\n         events listed above.\n* ENOENT: command does not exist.\n* ENOTSUP: command is cross-slot.\n* ERANGE: command contains keys that are not within the migrating slot range.\n\n<span id=\"RedisModule_ClusterGetLocalSlotRanges\"></span>"},{"id":"redismodule_clustergetlocalslotranges","title":"`RedisModule_ClusterGetLocalSlotRanges`","role":"content","text":"RedisModuleSlotRangeArray *RedisModule_ClusterGetLocalSlotRanges(RedisModuleCtx *ctx);\n\n**Available since:** 8.4.0\n\nReturns the locally owned slot ranges for the node.\n\nAn optional `ctx` can be provided to enable auto-memory management.\nIf cluster mode is disabled, the array will include all slots (0–16383).\nIf the node is a replica, the slot ranges of its master are returned.\n\nThe returned array must be freed with [`RedisModule_ClusterFreeSlotRanges()`](#RedisModule_ClusterFreeSlotRanges).\n\n<span id=\"RedisModule_ClusterFreeSlotRanges\"></span>"},{"id":"redismodule_clusterfreeslotranges","title":"`RedisModule_ClusterFreeSlotRanges`","role":"content","text":"void RedisModule_ClusterFreeSlotRanges(RedisModuleCtx *ctx,\n                                           RedisModuleSlotRangeArray *slots);\n\n**Available since:** 8.4.0\n\nFrees a slot range array returned by [`RedisModule_ClusterGetLocalSlotRanges()`](#RedisModule_ClusterGetLocalSlotRanges).\nPass the `ctx` pointer only if the array was created with a context.\n\n<span id=\"section-modules-timers-api\"></span>"},{"id":"modules-timers-api","title":"Modules Timers API","role":"content","text":"Module timers are a high precision \"green timers\" abstraction where\nevery module can register even millions of timers without problems, even if\nthe actual event loop will just have a single timer that is used to awake the\nmodule timers subsystem in order to process the next event.\n\nAll the timers are stored into a radix tree, ordered by expire time, when\nthe main Redis event loop timer callback is called, we try to process all\nthe timers already expired one after the other. Then we re-enter the event\nloop registering a timer that will expire when the next to process module\ntimer will expire.\n\nEvery time the list of active timers drops to zero, we unregister the\nmain event loop timer, so that there is no overhead when such feature is\nnot used.\n\n<span id=\"RedisModule_CreateTimer\"></span>"},{"id":"redismodule_createtimer","title":"`RedisModule_CreateTimer`","role":"content","text":"RedisModuleTimerID RedisModule_CreateTimer(RedisModuleCtx *ctx,\n                                               mstime_t period,\n                                               RedisModuleTimerProc callback,\n                                               void *data);\n\n**Available since:** 5.0.0\n\nCreate a new timer that will fire after `period` milliseconds, and will call\nthe specified function using `data` as argument. The returned timer ID can be\nused to get information from the timer or to stop it before it fires.\nNote that for the common use case of a repeating timer (Re-registration\nof the timer inside the `RedisModuleTimerProc` callback) it matters when\nthis API is called:\nIf it is called at the beginning of 'callback' it means\nthe event will triggered every 'period'.\nIf it is called at the end of 'callback' it means\nthere will 'period' milliseconds gaps between events.\n(If the time it takes to execute 'callback' is negligible the two\nstatements above mean the same)\n\n<span id=\"RedisModule_StopTimer\"></span>"},{"id":"redismodule_stoptimer","title":"`RedisModule_StopTimer`","role":"content","text":"int RedisModule_StopTimer(RedisModuleCtx *ctx,\n                              RedisModuleTimerID id,\n                              void **data);\n\n**Available since:** 5.0.0\n\nStop a timer, returns `REDISMODULE_OK` if the timer was found, belonged to the\ncalling module, and was stopped, otherwise `REDISMODULE_ERR` is returned.\nIf not NULL, the data pointer is set to the value of the data argument when\nthe timer was created.\n\n<span id=\"RedisModule_GetTimerInfo\"></span>"},{"id":"redismodule_gettimerinfo","title":"`RedisModule_GetTimerInfo`","role":"content","text":"int RedisModule_GetTimerInfo(RedisModuleCtx *ctx,\n                                 RedisModuleTimerID id,\n                                 uint64_t *remaining,\n                                 void **data);\n\n**Available since:** 5.0.0\n\nObtain information about a timer: its remaining time before firing\n(in milliseconds), and the private data pointer associated with the timer.\nIf the timer specified does not exist or belongs to a different module\nno information is returned and the function returns `REDISMODULE_ERR`, otherwise\n`REDISMODULE_OK` is returned. The arguments remaining or data can be NULL if\nthe caller does not need certain information.\n\n<span id=\"section-modules-eventloop-api\"></span>"},{"id":"modules-eventloop-api","title":"Modules EventLoop API","role":"content","text":"<span id=\"RedisModule_EventLoopAdd\"></span>"},{"id":"redismodule_eventloopadd","title":"`RedisModule_EventLoopAdd`","role":"content","text":"int RedisModule_EventLoopAdd(int fd,\n                                 int mask,\n                                 RedisModuleEventLoopFunc func,\n                                 void *user_data);\n\n**Available since:** 7.0.0\n\nAdd a pipe / socket event to the event loop.\n\n* `mask` must be one of the following values:\n\n    * `REDISMODULE_EVENTLOOP_READABLE`\n    * `REDISMODULE_EVENTLOOP_WRITABLE`\n    * `REDISMODULE_EVENTLOOP_READABLE | REDISMODULE_EVENTLOOP_WRITABLE`\n\nOn success `REDISMODULE_OK` is returned, otherwise\n`REDISMODULE_ERR` is returned and errno is set to the following values:\n\n* ERANGE: `fd` is negative or higher than `maxclients` Redis config.\n* EINVAL: `callback` is NULL or `mask` value is invalid.\n\n`errno` might take other values in case of an internal error.\n\nExample:\n\n    void onReadable(int fd, void *user_data, int mask) {\n        char buf[32];\n        int bytes = read(fd,buf,sizeof(buf));\n        printf(\"Read %d bytes \\n\", bytes);\n    }\n    RedisModule_EventLoopAdd(fd, REDISMODULE_EVENTLOOP_READABLE, onReadable, NULL);\n\n<span id=\"RedisModule_EventLoopDel\"></span>"},{"id":"redismodule_eventloopdel","title":"`RedisModule_EventLoopDel`","role":"content","text":"int RedisModule_EventLoopDel(int fd, int mask);\n\n**Available since:** 7.0.0\n\nDelete a pipe / socket event from the event loop.\n\n* `mask` must be one of the following values:\n\n    * `REDISMODULE_EVENTLOOP_READABLE`\n    * `REDISMODULE_EVENTLOOP_WRITABLE`\n    * `REDISMODULE_EVENTLOOP_READABLE | REDISMODULE_EVENTLOOP_WRITABLE`\n\nOn success `REDISMODULE_OK` is returned, otherwise\n`REDISMODULE_ERR` is returned and errno is set to the following values:\n\n* ERANGE: `fd` is negative or higher than `maxclients` Redis config.\n* EINVAL: `mask` value is invalid.\n\n<span id=\"RedisModule_EventLoopAddOneShot\"></span>"},{"id":"redismodule_eventloopaddoneshot","title":"`RedisModule_EventLoopAddOneShot`","role":"content","text":"int RedisModule_EventLoopAddOneShot(RedisModuleEventLoopOneShotFunc func,\n                                        void *user_data);\n\n**Available since:** 7.0.0\n\nThis function can be called from other threads to trigger callback on Redis\nmain thread. On success `REDISMODULE_OK` is returned. If `func` is NULL\n`REDISMODULE_ERR` is returned and errno is set to EINVAL.\n\n<span id=\"section-modules-acl-api\"></span>"},{"id":"modules-acl-api","title":"Modules ACL API","role":"content","text":"Implements a hook into the authentication and authorization within Redis.\n\n<span id=\"RedisModule_CreateModuleUser\"></span>"},{"id":"redismodule_createmoduleuser","title":"`RedisModule_CreateModuleUser`","role":"content","text":"RedisModuleUser *RedisModule_CreateModuleUser(const char *name);\n\n**Available since:** 6.0.0\n\nCreates a Redis ACL user that the module can use to authenticate a client.\nAfter obtaining the user, the module should set what such user can do\nusing the `RedisModule_SetUserACL()` function. Once configured, the user\ncan be used in order to authenticate a connection, with the specified\nACL rules, using the `RedisModule_AuthClientWithUser()` function.\n\nNote that:\n\n* Users created here are not listed by the ACL command.\n* Users created here are not checked for duplicated name, so it's up to\n  the module calling this function to take care of not creating users\n  with the same name.\n* The created user can be used to authenticate multiple Redis connections.\n\nThe caller can later free the user using the function\n[`RedisModule_FreeModuleUser()`](#RedisModule_FreeModuleUser). When this function is called, if there are\nstill clients authenticated with this user, they are disconnected.\nThe function to free the user should only be used when the caller really\nwants to invalidate the user to define a new one with different\ncapabilities.\n\n<span id=\"RedisModule_FreeModuleUser\"></span>"},{"id":"redismodule_freemoduleuser","title":"`RedisModule_FreeModuleUser`","role":"content","text":"int RedisModule_FreeModuleUser(RedisModuleUser *user);\n\n**Available since:** 6.0.0\n\nFrees a given user and disconnects all of the clients that have been\nauthenticated with it. See [`RedisModule_CreateModuleUser`](#RedisModule_CreateModuleUser) for detailed usage.\n\n<span id=\"RedisModule_SetModuleUserACL\"></span>"},{"id":"redismodule_setmoduleuseracl","title":"`RedisModule_SetModuleUserACL`","role":"content","text":"int RedisModule_SetModuleUserACL(RedisModuleUser *user, const char* acl);\n\n**Available since:** 6.0.0\n\nSets the permissions of a user created through the redis module\ninterface. The syntax is the same as ACL SETUSER, so refer to the\ndocumentation in acl.c for more information. See [`RedisModule_CreateModuleUser`](#RedisModule_CreateModuleUser)\nfor detailed usage.\n\nReturns `REDISMODULE_OK` on success and `REDISMODULE_ERR` on failure\nand will set an errno describing why the operation failed.\n\n<span id=\"RedisModule_SetModuleUserACLString\"></span>"},{"id":"redismodule_setmoduleuseraclstring","title":"`RedisModule_SetModuleUserACLString`","role":"content","text":"int RedisModule_SetModuleUserACLString(RedisModuleCtx *ctx,\n                                           RedisModuleUser *user,\n                                           const char *acl,\n                                           RedisModuleString **error);\n\n**Available since:** 7.0.6\n\nSets the permission of a user with a complete ACL string, such as one\nwould use on the redis ACL SETUSER command line API. This differs from\n[`RedisModule_SetModuleUserACL`](#RedisModule_SetModuleUserACL), which only takes single ACL operations at a time.\n\nReturns `REDISMODULE_OK` on success and `REDISMODULE_ERR` on failure\nif a `RedisModuleString` is provided in error, a string describing the error\nwill be returned\n\n<span id=\"RedisModule_GetModuleUserACLString\"></span>"},{"id":"redismodule_getmoduleuseraclstring","title":"`RedisModule_GetModuleUserACLString`","role":"content","text":"RedisModuleString *RedisModule_GetModuleUserACLString(RedisModuleUser *user);\n\n**Available since:** 7.0.6\n\nGet the ACL string for a given user\nReturns a `RedisModuleString`\n\n<span id=\"RedisModule_GetCurrentUserName\"></span>"},{"id":"redismodule_getcurrentusername","title":"`RedisModule_GetCurrentUserName`","role":"content","text":"RedisModuleString *RedisModule_GetCurrentUserName(RedisModuleCtx *ctx);\n\n**Available since:** 7.0.0\n\nRetrieve the user name of the client connection behind the current context.\nThe user name can be used later, in order to get a `RedisModuleUser`.\nSee more information in [`RedisModule_GetModuleUserFromUserName`](#RedisModule_GetModuleUserFromUserName).\n\nThe returned string must be released with [`RedisModule_FreeString()`](#RedisModule_FreeString) or by\nenabling automatic memory management.\n\n<span id=\"RedisModule_GetModuleUserFromUserName\"></span>"},{"id":"redismodule_getmoduleuserfromusername","title":"`RedisModule_GetModuleUserFromUserName`","role":"content","text":"RedisModuleUser *RedisModule_GetModuleUserFromUserName(RedisModuleString *name);\n\n**Available since:** 7.0.0\n\nA `RedisModuleUser` can be used to check if command, key or channel can be executed or\naccessed according to the ACLs rules associated with that user.\nWhen a Module wants to do ACL checks on a general ACL user (not created by [`RedisModule_CreateModuleUser`](#RedisModule_CreateModuleUser)),\nit can get the `RedisModuleUser` from this API, based on the user name retrieved by [`RedisModule_GetCurrentUserName`](#RedisModule_GetCurrentUserName).\n\nSince a general ACL user can be deleted at any time, this `RedisModuleUser` should be used only in the context\nwhere this function was called. In order to do ACL checks out of that context, the Module can store the user name,\nand call this API at any other context.\n\nReturns NULL if the user is disabled or the user does not exist.\nThe caller should later free the user using the function [`RedisModule_FreeModuleUser()`](#RedisModule_FreeModuleUser).\n\n<span id=\"RedisModule_ACLCheckCommandPermissions\"></span>"},{"id":"redismodule_aclcheckcommandpermissions","title":"`RedisModule_ACLCheckCommandPermissions`","role":"content","text":"int RedisModule_ACLCheckCommandPermissions(RedisModuleUser *user,\n                                               RedisModuleString **argv,\n                                               int argc);\n\n**Available since:** 7.0.0\n\nChecks if the command can be executed by the user, according to the ACLs associated with it.\n\nOn success a `REDISMODULE_OK` is returned, otherwise\n`REDISMODULE_ERR` is returned and errno is set to the following values:\n\n* ENOENT: Specified command does not exist.\n* EINVAL: Invalid number of arguments for the specified command.\n* EACCES: Command cannot be executed, according to ACL rules\n\n<span id=\"RedisModule_ACLCheckKeyPermissions\"></span>"},{"id":"redismodule_aclcheckkeypermissions","title":"`RedisModule_ACLCheckKeyPermissions`","role":"content","text":"int RedisModule_ACLCheckKeyPermissions(RedisModuleUser *user,\n                                           RedisModuleString *key,\n                                           int flags);\n\n**Available since:** 7.0.0\n\nCheck if the key can be accessed by the user according to the ACLs attached to the user\nand the flags representing the key access. The flags are the same that are used in the\nkeyspec for logical operations. These flags are documented in [`RedisModule_SetCommandInfo`](#RedisModule_SetCommandInfo) as\nthe `REDISMODULE_CMD_KEY_ACCESS`, `REDISMODULE_CMD_KEY_UPDATE`, `REDISMODULE_CMD_KEY_INSERT`,\nand `REDISMODULE_CMD_KEY_DELETE` flags.\n\nIf no flags are supplied, the user is still required to have some access to the key for\nthis command to return successfully.\n\nIf the user is able to access the key then `REDISMODULE_OK` is returned, otherwise\n`REDISMODULE_ERR` is returned and errno is set to one of the following values:\n\n* EINVAL: The provided flags are invalid.\n* EACCESS: The user does not have permission to access the key.\n\n<span id=\"RedisModule_ACLCheckKeyPrefixPermissions\"></span>"},{"id":"redismodule_aclcheckkeyprefixpermissions","title":"`RedisModule_ACLCheckKeyPrefixPermissions`","role":"content","text":"int RedisModule_ACLCheckKeyPrefixPermissions(RedisModuleUser *user,\n                                                 RedisModuleString *prefix,\n                                                 int flags);\n\n**Available since:** 8.0.0\n\nCheck if the user can access keys matching the given key prefix according to the ACLs \nattached to the user and the flags representing key access. The flags are the same that \nare used in the keyspec for logical operations. These flags are documented in \n[`RedisModule_SetCommandInfo`](#RedisModule_SetCommandInfo) as the `REDISMODULE_CMD_KEY_ACCESS`, \n`REDISMODULE_CMD_KEY_UPDATE`, `REDISMODULE_CMD_KEY_INSERT`, and `REDISMODULE_CMD_KEY_DELETE` flags.\n\nIf no flags are supplied, the user is still required to have some access to keys matching \nthe prefix for this command to return successfully.\n\nIf the user is able to access keys matching the prefix, then `REDISMODULE_OK` is returned.\nOtherwise, `REDISMODULE_ERR` is returned and errno is set to one of the following values:\n\n* EINVAL: The provided flags are invalid.\n* EACCES: The user does not have permission to access keys matching the prefix.\n\n<span id=\"RedisModule_ACLCheckChannelPermissions\"></span>"},{"id":"redismodule_aclcheckchannelpermissions","title":"`RedisModule_ACLCheckChannelPermissions`","role":"content","text":"int RedisModule_ACLCheckChannelPermissions(RedisModuleUser *user,\n                                               RedisModuleString *ch,\n                                               int flags);\n\n**Available since:** 7.0.0\n\nCheck if the pubsub channel can be accessed by the user based off of the given\naccess flags. See [`RedisModule_ChannelAtPosWithFlags`](#RedisModule_ChannelAtPosWithFlags) for more information about the\npossible flags that can be passed in.\n\nIf the user is able to access the pubsub channel then `REDISMODULE_OK` is returned, otherwise\n`REDISMODULE_ERR` is returned and errno is set to one of the following values:\n\n* EINVAL: The provided flags are invalid.\n* EACCESS: The user does not have permission to access the pubsub channel.\n\n<span id=\"RedisModule_ACLAddLogEntry\"></span>"},{"id":"redismodule_acladdlogentry","title":"`RedisModule_ACLAddLogEntry`","role":"content","text":"int RedisModule_ACLAddLogEntry(RedisModuleCtx *ctx,\n                                   RedisModuleUser *user,\n                                   RedisModuleString *object,\n                                   RedisModuleACLLogEntryReason reason);\n\n**Available since:** 7.0.0\n\nAdds a new entry in the ACL log.\nReturns `REDISMODULE_OK` on success and `REDISMODULE_ERR` on error.\n\nFor more information about ACL log, please refer to [https://redis.io/commands/acl-log](https://redis.io/commands/acl-log)\n\n<span id=\"RedisModule_ACLAddLogEntryByUserName\"></span>"},{"id":"redismodule_acladdlogentrybyusername","title":"`RedisModule_ACLAddLogEntryByUserName`","role":"content","text":"int RedisModule_ACLAddLogEntryByUserName(RedisModuleCtx *ctx,\n                                             RedisModuleString *username,\n                                             RedisModuleString *object,\n                                             RedisModuleACLLogEntryReason reason);\n\n**Available since:** 7.2.0\n\nAdds a new entry in the ACL log with the `username` `RedisModuleString` provided.\nReturns `REDISMODULE_OK` on success and `REDISMODULE_ERR` on error.\n\nFor more information about ACL log, please refer to [https://redis.io/commands/acl-log](https://redis.io/commands/acl-log)\n\n<span id=\"RedisModule_AuthenticateClientWithUser\"></span>"},{"id":"redismodule_authenticateclientwithuser","title":"`RedisModule_AuthenticateClientWithUser`","role":"content","text":"int RedisModule_AuthenticateClientWithUser(RedisModuleCtx *ctx,\n                                               RedisModuleUser *module_user,\n                                               RedisModuleUserChangedFunc callback,\n                                               void *privdata,\n                                               uint64_t *client_id);\n\n**Available since:** 6.0.0\n\nAuthenticate the current context's user with the provided redis acl user.\nReturns `REDISMODULE_ERR` if the user is disabled.\n\nSee authenticateClientWithUser for information about callback, `client_id`,\nand general usage for authentication.\n\n<span id=\"RedisModule_AuthenticateClientWithACLUser\"></span>"},{"id":"redismodule_authenticateclientwithacluser","title":"`RedisModule_AuthenticateClientWithACLUser`","role":"content","text":"int RedisModule_AuthenticateClientWithACLUser(RedisModuleCtx *ctx,\n                                                  const char *name,\n                                                  size_t len,\n                                                  RedisModuleUserChangedFunc callback,\n                                                  void *privdata,\n                                                  uint64_t *client_id);\n\n**Available since:** 6.0.0\n\nAuthenticate the current context's user with the provided redis acl user.\nReturns `REDISMODULE_ERR` if the user is disabled or the user does not exist.\n\nSee authenticateClientWithUser for information about callback, `client_id`,\nand general usage for authentication.\n\n<span id=\"RedisModule_DeauthenticateAndCloseClient\"></span>"},{"id":"redismodule_deauthenticateandcloseclient","title":"`RedisModule_DeauthenticateAndCloseClient`","role":"content","text":"int RedisModule_DeauthenticateAndCloseClient(RedisModuleCtx *ctx,\n                                                 uint64_t client_id);\n\n**Available since:** 6.0.0\n\nDeauthenticate and close the client. The client resources will not be\nimmediately freed, but will be cleaned up in a background job. This is\nthe recommended way to deauthenticate a client since most clients can't\nhandle users becoming deauthenticated. Returns `REDISMODULE_ERR` when the\nclient doesn't exist and `REDISMODULE_OK` when the operation was successful.\n\nThe client ID is returned from the [`RedisModule_AuthenticateClientWithUser`](#RedisModule_AuthenticateClientWithUser) and\n[`RedisModule_AuthenticateClientWithACLUser`](#RedisModule_AuthenticateClientWithACLUser) APIs, but can be obtained through\nthe CLIENT api or through server events.\n\nThis function is not thread safe, and must be executed within the context\nof a command or thread safe context.\n\n<span id=\"RedisModule_RedactClientCommandArgument\"></span>"},{"id":"redismodule_redactclientcommandargument","title":"`RedisModule_RedactClientCommandArgument`","role":"content","text":"int RedisModule_RedactClientCommandArgument(RedisModuleCtx *ctx, int pos);\n\n**Available since:** 7.0.0\n\nRedact the client command argument specified at the given position. Redacted arguments \nare obfuscated in user facing commands such as SLOWLOG or MONITOR, as well as\nnever being written to server logs. This command may be called multiple times on the\nsame position.\n\nNote that the command name, position 0, can not be redacted. \n\nReturns `REDISMODULE_OK` if the argument was redacted and `REDISMODULE_ERR` if there \nwas an invalid parameter passed in or the position is outside the client \nargument range.\n\n<span id=\"RedisModule_GetClientCertificate\"></span>"},{"id":"redismodule_getclientcertificate","title":"`RedisModule_GetClientCertificate`","role":"content","text":"RedisModuleString *RedisModule_GetClientCertificate(RedisModuleCtx *ctx,\n                                                        uint64_t client_id);\n\n**Available since:** 6.0.9\n\nReturn the X.509 client-side certificate used by the client to authenticate\nthis connection.\n\nThe return value is an allocated `RedisModuleString` that is a X.509 certificate\nencoded in PEM (Base64) format. It should be freed (or auto-freed) by the caller.\n\nA NULL value is returned in the following conditions:\n\n- Connection ID does not exist\n- Connection is not a TLS connection\n- Connection is a TLS connection but no client certificate was used\n\n<span id=\"section-modules-dictionary-api\"></span>"},{"id":"modules-dictionary-api","title":"Modules Dictionary API","role":"content","text":"Implements a sorted dictionary (actually backed by a radix tree) with\nthe usual get / set / del / num-items API, together with an iterator\ncapable of going back and forth.\n\n<span id=\"RedisModule_CreateDict\"></span>"},{"id":"redismodule_createdict","title":"`RedisModule_CreateDict`","role":"content","text":"RedisModuleDict *RedisModule_CreateDict(RedisModuleCtx *ctx);\n\n**Available since:** 5.0.0\n\nCreate a new dictionary. The 'ctx' pointer can be the current module context\nor NULL, depending on what you want. Please follow the following rules:\n\n1. Use a NULL context if you plan to retain a reference to this dictionary\n   that will survive the time of the module callback where you created it.\n2. Use a NULL context if no context is available at the time you are creating\n   the dictionary (of course...).\n3. However use the current callback context as 'ctx' argument if the\n   dictionary time to live is just limited to the callback scope. In this\n   case, if enabled, you can enjoy the automatic memory management that will\n   reclaim the dictionary memory, as well as the strings returned by the\n   Next / Prev dictionary iterator calls.\n\n<span id=\"RedisModule_FreeDict\"></span>"},{"id":"redismodule_freedict","title":"`RedisModule_FreeDict`","role":"content","text":"void RedisModule_FreeDict(RedisModuleCtx *ctx, RedisModuleDict *d);\n\n**Available since:** 5.0.0\n\nFree a dictionary created with [`RedisModule_CreateDict()`](#RedisModule_CreateDict). You need to pass the\ncontext pointer 'ctx' only if the dictionary was created using the\ncontext instead of passing NULL.\n\n<span id=\"RedisModule_DictSize\"></span>"},{"id":"redismodule_dictsize","title":"`RedisModule_DictSize`","role":"content","text":"uint64_t RedisModule_DictSize(RedisModuleDict *d);\n\n**Available since:** 5.0.0\n\nReturn the size of the dictionary (number of keys).\n\n<span id=\"RedisModule_DictSetC\"></span>"},{"id":"redismodule_dictsetc","title":"`RedisModule_DictSetC`","role":"content","text":"int RedisModule_DictSetC(RedisModuleDict *d,\n                             void *key,\n                             size_t keylen,\n                             void *ptr);\n\n**Available since:** 5.0.0\n\nStore the specified key into the dictionary, setting its value to the\npointer 'ptr'. If the key was added with success, since it did not\nalready exist, `REDISMODULE_OK` is returned. Otherwise if the key already\nexists the function returns `REDISMODULE_ERR`.\n\n<span id=\"RedisModule_DictReplaceC\"></span>"},{"id":"redismodule_dictreplacec","title":"`RedisModule_DictReplaceC`","role":"content","text":"int RedisModule_DictReplaceC(RedisModuleDict *d,\n                                 void *key,\n                                 size_t keylen,\n                                 void *ptr);\n\n**Available since:** 5.0.0\n\nLike [`RedisModule_DictSetC()`](#RedisModule_DictSetC) but will replace the key with the new\nvalue if the key already exists.\n\n<span id=\"RedisModule_DictSet\"></span>"},{"id":"redismodule_dictset","title":"`RedisModule_DictSet`","role":"content","text":"int RedisModule_DictSet(RedisModuleDict *d, RedisModuleString *key, void *ptr);\n\n**Available since:** 5.0.0\n\nLike [`RedisModule_DictSetC()`](#RedisModule_DictSetC) but takes the key as a `RedisModuleString`.\n\n<span id=\"RedisModule_DictReplace\"></span>"},{"id":"redismodule_dictreplace","title":"`RedisModule_DictReplace`","role":"content","text":"int RedisModule_DictReplace(RedisModuleDict *d,\n                                RedisModuleString *key,\n                                void *ptr);\n\n**Available since:** 5.0.0\n\nLike [`RedisModule_DictReplaceC()`](#RedisModule_DictReplaceC) but takes the key as a `RedisModuleString`.\n\n<span id=\"RedisModule_DictGetC\"></span>"},{"id":"redismodule_dictgetc","title":"`RedisModule_DictGetC`","role":"content","text":"void *RedisModule_DictGetC(RedisModuleDict *d,\n                               void *key,\n                               size_t keylen,\n                               int *nokey);\n\n**Available since:** 5.0.0\n\nReturn the value stored at the specified key. The function returns NULL\nboth in the case the key does not exist, or if you actually stored\nNULL at key. So, optionally, if the 'nokey' pointer is not NULL, it will\nbe set by reference to 1 if the key does not exist, or to 0 if the key\nexists.\n\n<span id=\"RedisModule_DictGet\"></span>"},{"id":"redismodule_dictget","title":"`RedisModule_DictGet`","role":"content","text":"void *RedisModule_DictGet(RedisModuleDict *d,\n                              RedisModuleString *key,\n                              int *nokey);\n\n**Available since:** 5.0.0\n\nLike [`RedisModule_DictGetC()`](#RedisModule_DictGetC) but takes the key as a `RedisModuleString`.\n\n<span id=\"RedisModule_DictDelC\"></span>"},{"id":"redismodule_dictdelc","title":"`RedisModule_DictDelC`","role":"content","text":"int RedisModule_DictDelC(RedisModuleDict *d,\n                             void *key,\n                             size_t keylen,\n                             void *oldval);\n\n**Available since:** 5.0.0\n\nRemove the specified key from the dictionary, returning `REDISMODULE_OK` if\nthe key was found and deleted, or `REDISMODULE_ERR` if instead there was\nno such key in the dictionary. When the operation is successful, if\n'oldval' is not NULL, then '*oldval' is set to the value stored at the\nkey before it was deleted. Using this feature it is possible to get\na pointer to the value (for instance in order to release it), without\nhaving to call [`RedisModule_DictGet()`](#RedisModule_DictGet) before deleting the key.\n\n<span id=\"RedisModule_DictDel\"></span>"},{"id":"redismodule_dictdel","title":"`RedisModule_DictDel`","role":"content","text":"int RedisModule_DictDel(RedisModuleDict *d,\n                            RedisModuleString *key,\n                            void *oldval);\n\n**Available since:** 5.0.0\n\nLike [`RedisModule_DictDelC()`](#RedisModule_DictDelC) but gets the key as a `RedisModuleString`.\n\n<span id=\"RedisModule_DictIteratorStartC\"></span>"},{"id":"redismodule_dictiteratorstartc","title":"`RedisModule_DictIteratorStartC`","role":"content","text":"RedisModuleDictIter *RedisModule_DictIteratorStartC(RedisModuleDict *d,\n                                                        const char *op,\n                                                        void *key,\n                                                        size_t keylen);\n\n**Available since:** 5.0.0\n\nReturn an iterator, setup in order to start iterating from the specified\nkey by applying the operator 'op', which is just a string specifying the\ncomparison operator to use in order to seek the first element. The\noperators available are:\n\n* `^`   – Seek the first (lexicographically smaller) key.\n* `$`   – Seek the last  (lexicographically bigger) key.\n* `>`   – Seek the first element greater than the specified key.\n* `>=`  – Seek the first element greater or equal than the specified key.\n* `<`   – Seek the first element smaller than the specified key.\n* `<=`  – Seek the first element smaller or equal than the specified key.\n* `==`  – Seek the first element matching exactly the specified key.\n\nNote that for `^` and `$` the passed key is not used, and the user may\njust pass NULL with a length of 0.\n\nIf the element to start the iteration cannot be seeked based on the\nkey and operator passed, [`RedisModule_DictNext()`](#RedisModule_DictNext) / Prev() will just return\n`REDISMODULE_ERR` at the first call, otherwise they'll produce elements.\n\n<span id=\"RedisModule_DictIteratorStart\"></span>"},{"id":"redismodule_dictiteratorstart","title":"`RedisModule_DictIteratorStart`","role":"content","text":"RedisModuleDictIter *RedisModule_DictIteratorStart(RedisModuleDict *d,\n                                                       const char *op,\n                                                       RedisModuleString *key);\n\n**Available since:** 5.0.0\n\nExactly like [`RedisModule_DictIteratorStartC`](#RedisModule_DictIteratorStartC), but the key is passed as a\n`RedisModuleString`.\n\n<span id=\"RedisModule_DictIteratorStop\"></span>"},{"id":"redismodule_dictiteratorstop","title":"`RedisModule_DictIteratorStop`","role":"content","text":"void RedisModule_DictIteratorStop(RedisModuleDictIter *di);\n\n**Available since:** 5.0.0\n\nRelease the iterator created with [`RedisModule_DictIteratorStart()`](#RedisModule_DictIteratorStart). This call\nis mandatory otherwise a memory leak is introduced in the module.\n\n<span id=\"RedisModule_DictIteratorReseekC\"></span>"},{"id":"redismodule_dictiteratorreseekc","title":"`RedisModule_DictIteratorReseekC`","role":"content","text":"int RedisModule_DictIteratorReseekC(RedisModuleDictIter *di,\n                                        const char *op,\n                                        void *key,\n                                        size_t keylen);\n\n**Available since:** 5.0.0\n\nAfter its creation with [`RedisModule_DictIteratorStart()`](#RedisModule_DictIteratorStart), it is possible to\nchange the currently selected element of the iterator by using this\nAPI call. The result based on the operator and key is exactly like\nthe function [`RedisModule_DictIteratorStart()`](#RedisModule_DictIteratorStart), however in this case the\nreturn value is just `REDISMODULE_OK` in case the seeked element was found,\nor `REDISMODULE_ERR` in case it was not possible to seek the specified\nelement. It is possible to reseek an iterator as many times as you want.\n\n<span id=\"RedisModule_DictIteratorReseek\"></span>"},{"id":"redismodule_dictiteratorreseek","title":"`RedisModule_DictIteratorReseek`","role":"content","text":"int RedisModule_DictIteratorReseek(RedisModuleDictIter *di,\n                                       const char *op,\n                                       RedisModuleString *key);\n\n**Available since:** 5.0.0\n\nLike [`RedisModule_DictIteratorReseekC()`](#RedisModule_DictIteratorReseekC) but takes the key as a\n`RedisModuleString`.\n\n<span id=\"RedisModule_DictNextC\"></span>"},{"id":"redismodule_dictnextc","title":"`RedisModule_DictNextC`","role":"content","text":"void *RedisModule_DictNextC(RedisModuleDictIter *di,\n                                size_t *keylen,\n                                void **dataptr);\n\n**Available since:** 5.0.0\n\nReturn the current item of the dictionary iterator `di` and steps to the\nnext element. If the iterator already yield the last element and there\nare no other elements to return, NULL is returned, otherwise a pointer\nto a string representing the key is provided, and the `*keylen` length\nis set by reference (if keylen is not NULL). The `*dataptr`, if not NULL\nis set to the value of the pointer stored at the returned key as auxiliary\ndata (as set by the [`RedisModule_DictSet`](#RedisModule_DictSet) API).\n\nUsage example:\n\n     ... create the iterator here ...\n     char *key;\n     void *data;\n     while((key = RedisModule_DictNextC(iter,&keylen,&data)) != NULL) {\n         printf(\"%.*s %p\\n\", (int)keylen, key, data);\n     }\n\nThe returned pointer is of type void because sometimes it makes sense\nto cast it to a `char*` sometimes to an unsigned `char*` depending on the\nfact it contains or not binary data, so this API ends being more\ncomfortable to use.\n\nThe validity of the returned pointer is until the next call to the\nnext/prev iterator step. Also the pointer is no longer valid once the\niterator is released.\n\n<span id=\"RedisModule_DictPrevC\"></span>"},{"id":"redismodule_dictprevc","title":"`RedisModule_DictPrevC`","role":"content","text":"void *RedisModule_DictPrevC(RedisModuleDictIter *di,\n                                size_t *keylen,\n                                void **dataptr);\n\n**Available since:** 5.0.0\n\nThis function is exactly like [`RedisModule_DictNext()`](#RedisModule_DictNext) but after returning\nthe currently selected element in the iterator, it selects the previous\nelement (lexicographically smaller) instead of the next one.\n\n<span id=\"RedisModule_DictNext\"></span>"},{"id":"redismodule_dictnext","title":"`RedisModule_DictNext`","role":"content","text":"RedisModuleString *RedisModule_DictNext(RedisModuleCtx *ctx,\n                                            RedisModuleDictIter *di,\n                                            void **dataptr);\n\n**Available since:** 5.0.0\n\nLike `RedisModuleNextC()`, but instead of returning an internally allocated\nbuffer and key length, it returns directly a module string object allocated\nin the specified context 'ctx' (that may be NULL exactly like for the main\nAPI [`RedisModule_CreateString`](#RedisModule_CreateString)).\n\nThe returned string object should be deallocated after use, either manually\nor by using a context that has automatic memory management active.\n\n<span id=\"RedisModule_DictPrev\"></span>"},{"id":"redismodule_dictprev","title":"`RedisModule_DictPrev`","role":"content","text":"RedisModuleString *RedisModule_DictPrev(RedisModuleCtx *ctx,\n                                            RedisModuleDictIter *di,\n                                            void **dataptr);\n\n**Available since:** 5.0.0\n\nLike [`RedisModule_DictNext()`](#RedisModule_DictNext) but after returning the currently selected\nelement in the iterator, it selects the previous element (lexicographically\nsmaller) instead of the next one.\n\n<span id=\"RedisModule_DictCompareC\"></span>"},{"id":"redismodule_dictcomparec","title":"`RedisModule_DictCompareC`","role":"content","text":"int RedisModule_DictCompareC(RedisModuleDictIter *di,\n                                 const char *op,\n                                 void *key,\n                                 size_t keylen);\n\n**Available since:** 5.0.0\n\nCompare the element currently pointed by the iterator to the specified\nelement given by key/keylen, according to the operator 'op' (the set of\nvalid operators are the same valid for [`RedisModule_DictIteratorStart`](#RedisModule_DictIteratorStart)).\nIf the comparison is successful the command returns `REDISMODULE_OK`\notherwise `REDISMODULE_ERR` is returned.\n\nThis is useful when we want to just emit a lexicographical range, so\nin the loop, as we iterate elements, we can also check if we are still\non range.\n\nThe function return `REDISMODULE_ERR` if the iterator reached the\nend of elements condition as well.\n\n<span id=\"RedisModule_DictCompare\"></span>"},{"id":"redismodule_dictcompare","title":"`RedisModule_DictCompare`","role":"content","text":"int RedisModule_DictCompare(RedisModuleDictIter *di,\n                                const char *op,\n                                RedisModuleString *key);\n\n**Available since:** 5.0.0\n\nLike [`RedisModule_DictCompareC`](#RedisModule_DictCompareC) but gets the key to compare with the current\niterator key as a `RedisModuleString`.\n\n<span id=\"section-modules-info-fields\"></span>"},{"id":"modules-info-fields","title":"Modules Info fields","role":"content","text":"<span id=\"RedisModule_InfoAddSection\"></span>"},{"id":"redismodule_infoaddsection","title":"`RedisModule_InfoAddSection`","role":"content","text":"int RedisModule_InfoAddSection(RedisModuleInfoCtx *ctx, const char *name);\n\n**Available since:** 6.0.0\n\nUsed to start a new section, before adding any fields. the section name will\nbe prefixed by `<modulename>_` and must only include A-Z,a-z,0-9.\nNULL or empty string indicates the default section (only `<modulename>`) is used.\nWhen return value is `REDISMODULE_ERR`, the section should and will be skipped.\n\n<span id=\"RedisModule_InfoBeginDictField\"></span>"},{"id":"redismodule_infobegindictfield","title":"`RedisModule_InfoBeginDictField`","role":"content","text":"int RedisModule_InfoBeginDictField(RedisModuleInfoCtx *ctx, const char *name);\n\n**Available since:** 6.0.0\n\nStarts a dict field, similar to the ones in INFO KEYSPACE. Use normal\n`RedisModule_InfoAddField`* functions to add the items to this field, and\nterminate with [`RedisModule_InfoEndDictField`](#RedisModule_InfoEndDictField).\n\n<span id=\"RedisModule_InfoEndDictField\"></span>"},{"id":"redismodule_infoenddictfield","title":"`RedisModule_InfoEndDictField`","role":"content","text":"int RedisModule_InfoEndDictField(RedisModuleInfoCtx *ctx);\n\n**Available since:** 6.0.0\n\nEnds a dict field, see [`RedisModule_InfoBeginDictField`](#RedisModule_InfoBeginDictField)\n\n<span id=\"RedisModule_InfoAddFieldString\"></span>"},{"id":"redismodule_infoaddfieldstring","title":"`RedisModule_InfoAddFieldString`","role":"content","text":"int RedisModule_InfoAddFieldString(RedisModuleInfoCtx *ctx,\n                                       const char *field,\n                                       RedisModuleString *value);\n\n**Available since:** 6.0.0\n\nUsed by `RedisModuleInfoFunc` to add info fields.\nEach field will be automatically prefixed by `<modulename>_`.\nField names or values must not include `\\r\\n` or `:`.\n\n<span id=\"RedisModule_InfoAddFieldCString\"></span>"},{"id":"redismodule_infoaddfieldcstring","title":"`RedisModule_InfoAddFieldCString`","role":"content","text":"int RedisModule_InfoAddFieldCString(RedisModuleInfoCtx *ctx,\n                                        const char *field,\n                                        const char *value);\n\n**Available since:** 6.0.0\n\nSee [`RedisModule_InfoAddFieldString()`](#RedisModule_InfoAddFieldString).\n\n<span id=\"RedisModule_InfoAddFieldDouble\"></span>"},{"id":"redismodule_infoaddfielddouble","title":"`RedisModule_InfoAddFieldDouble`","role":"content","text":"int RedisModule_InfoAddFieldDouble(RedisModuleInfoCtx *ctx,\n                                       const char *field,\n                                       double value);\n\n**Available since:** 6.0.0\n\nSee [`RedisModule_InfoAddFieldString()`](#RedisModule_InfoAddFieldString).\n\n<span id=\"RedisModule_InfoAddFieldLongLong\"></span>"},{"id":"redismodule_infoaddfieldlonglong","title":"`RedisModule_InfoAddFieldLongLong`","role":"content","text":"int RedisModule_InfoAddFieldLongLong(RedisModuleInfoCtx *ctx,\n                                         const char *field,\n                                         long long value);\n\n**Available since:** 6.0.0\n\nSee [`RedisModule_InfoAddFieldString()`](#RedisModule_InfoAddFieldString).\n\n<span id=\"RedisModule_InfoAddFieldULongLong\"></span>"},{"id":"redismodule_infoaddfieldulonglong","title":"`RedisModule_InfoAddFieldULongLong`","role":"content","text":"int RedisModule_InfoAddFieldULongLong(RedisModuleInfoCtx *ctx,\n                                          const char *field,\n                                          unsigned long long value);\n\n**Available since:** 6.0.0\n\nSee [`RedisModule_InfoAddFieldString()`](#RedisModule_InfoAddFieldString).\n\n<span id=\"RedisModule_RegisterInfoFunc\"></span>"},{"id":"redismodule_registerinfofunc","title":"`RedisModule_RegisterInfoFunc`","role":"content","text":"int RedisModule_RegisterInfoFunc(RedisModuleCtx *ctx, RedisModuleInfoFunc cb);\n\n**Available since:** 6.0.0\n\nRegisters callback for the INFO command. The callback should add INFO fields\nby calling the `RedisModule_InfoAddField*()` functions.\n\n<span id=\"RedisModule_GetServerInfo\"></span>"},{"id":"redismodule_getserverinfo","title":"`RedisModule_GetServerInfo`","role":"content","text":"RedisModuleServerInfoData *RedisModule_GetServerInfo(RedisModuleCtx *ctx,\n                                                         const char *section);\n\n**Available since:** 6.0.0\n\nGet information about the server similar to the one that returns from the\nINFO command. This function takes an optional 'section' argument that may\nbe NULL. The return value holds the output and can be used with\n[`RedisModule_ServerInfoGetField`](#RedisModule_ServerInfoGetField) and alike to get the individual fields.\nWhen done, it needs to be freed with [`RedisModule_FreeServerInfo`](#RedisModule_FreeServerInfo) or with the\nautomatic memory management mechanism if enabled.\n\n<span id=\"RedisModule_FreeServerInfo\"></span>"},{"id":"redismodule_freeserverinfo","title":"`RedisModule_FreeServerInfo`","role":"content","text":"void RedisModule_FreeServerInfo(RedisModuleCtx *ctx,\n                                    RedisModuleServerInfoData *data);\n\n**Available since:** 6.0.0\n\nFree data created with [`RedisModule_GetServerInfo()`](#RedisModule_GetServerInfo). You need to pass the\ncontext pointer 'ctx' only if the dictionary was created using the\ncontext instead of passing NULL.\n\n<span id=\"RedisModule_ServerInfoGetField\"></span>"},{"id":"redismodule_serverinfogetfield","title":"`RedisModule_ServerInfoGetField`","role":"content","text":"RedisModuleString *RedisModule_ServerInfoGetField(RedisModuleCtx *ctx,\n                                                      RedisModuleServerInfoData *data,\n                                                      const char* field);\n\n**Available since:** 6.0.0\n\nGet the value of a field from data collected with [`RedisModule_GetServerInfo()`](#RedisModule_GetServerInfo). You\nneed to pass the context pointer 'ctx' only if you want to use auto memory\nmechanism to release the returned string. Return value will be NULL if the\nfield was not found.\n\n<span id=\"RedisModule_ServerInfoGetFieldC\"></span>"},{"id":"redismodule_serverinfogetfieldc","title":"`RedisModule_ServerInfoGetFieldC`","role":"content","text":"const char *RedisModule_ServerInfoGetFieldC(RedisModuleServerInfoData *data,\n                                                const char* field);\n\n**Available since:** 6.0.0\n\nSimilar to [`RedisModule_ServerInfoGetField`](#RedisModule_ServerInfoGetField), but returns a char* which should not be freed but the caller.\n\n<span id=\"RedisModule_ServerInfoGetFieldSigned\"></span>"},{"id":"redismodule_serverinfogetfieldsigned","title":"`RedisModule_ServerInfoGetFieldSigned`","role":"content","text":"long long RedisModule_ServerInfoGetFieldSigned(RedisModuleServerInfoData *data,\n                                                   const char* field,\n                                                   int *out_err);\n\n**Available since:** 6.0.0\n\nGet the value of a field from data collected with [`RedisModule_GetServerInfo()`](#RedisModule_GetServerInfo). If the\nfield is not found, or is not numerical or out of range, return value will be\n0, and the optional `out_err` argument will be set to `REDISMODULE_ERR`.\n\n<span id=\"RedisModule_ServerInfoGetFieldUnsigned\"></span>"},{"id":"redismodule_serverinfogetfieldunsigned","title":"`RedisModule_ServerInfoGetFieldUnsigned`","role":"content","text":"unsigned long long RedisModule_ServerInfoGetFieldUnsigned(RedisModuleServerInfoData *data,\n                                                              const char* field,\n                                                              int *out_err);\n\n**Available since:** 6.0.0\n\nGet the value of a field from data collected with [`RedisModule_GetServerInfo()`](#RedisModule_GetServerInfo). If the\nfield is not found, or is not numerical or out of range, return value will be\n0, and the optional `out_err` argument will be set to `REDISMODULE_ERR`.\n\n<span id=\"RedisModule_ServerInfoGetFieldDouble\"></span>"},{"id":"redismodule_serverinfogetfielddouble","title":"`RedisModule_ServerInfoGetFieldDouble`","role":"content","text":"double RedisModule_ServerInfoGetFieldDouble(RedisModuleServerInfoData *data,\n                                                const char* field,\n                                                int *out_err);\n\n**Available since:** 6.0.0\n\nGet the value of a field from data collected with [`RedisModule_GetServerInfo()`](#RedisModule_GetServerInfo). If the\nfield is not found, or is not a double, return value will be 0, and the\noptional `out_err` argument will be set to `REDISMODULE_ERR`.\n\n<span id=\"section-modules-utility-apis\"></span>"},{"id":"modules-utility-apis","title":"Modules utility APIs","role":"content","text":"<span id=\"RedisModule_GetRandomBytes\"></span>"},{"id":"redismodule_getrandombytes","title":"`RedisModule_GetRandomBytes`","role":"content","text":"void RedisModule_GetRandomBytes(unsigned char *dst, size_t len);\n\n**Available since:** 5.0.0\n\nReturn random bytes using SHA1 in counter mode with a /dev/urandom\ninitialized seed. This function is fast so can be used to generate\nmany bytes without any effect on the operating system entropy pool.\nCurrently this function is not thread safe.\n\n<span id=\"RedisModule_GetRandomHexChars\"></span>"},{"id":"redismodule_getrandomhexchars","title":"`RedisModule_GetRandomHexChars`","role":"content","text":"void RedisModule_GetRandomHexChars(char *dst, size_t len);\n\n**Available since:** 5.0.0\n\nLike [`RedisModule_GetRandomBytes()`](#RedisModule_GetRandomBytes) but instead of setting the string to\nrandom bytes the string is set to random characters in the in the\nhex charset [0-9a-f].\n\n<span id=\"section-modules-api-exporting-importing\"></span>"},{"id":"modules-api-exporting--importing","title":"Modules API exporting / importing","role":"content","text":"<span id=\"RedisModule_ExportSharedAPI\"></span>"},{"id":"redismodule_exportsharedapi","title":"`RedisModule_ExportSharedAPI`","role":"content","text":"int RedisModule_ExportSharedAPI(RedisModuleCtx *ctx,\n                                    const char *apiname,\n                                    void *func);\n\n**Available since:** 5.0.4\n\nThis function is called by a module in order to export some API with a\ngiven name. Other modules will be able to use this API by calling the\nsymmetrical function [`RedisModule_GetSharedAPI()`](#RedisModule_GetSharedAPI) and casting the return value to\nthe right function pointer.\n\nThe function will return `REDISMODULE_OK` if the name is not already taken,\notherwise `REDISMODULE_ERR` will be returned and no operation will be\nperformed.\n\nIMPORTANT: the apiname argument should be a string literal with static\nlifetime. The API relies on the fact that it will always be valid in\nthe future.\n\n<span id=\"RedisModule_GetSharedAPI\"></span>"},{"id":"redismodule_getsharedapi","title":"`RedisModule_GetSharedAPI`","role":"content","text":"void *RedisModule_GetSharedAPI(RedisModuleCtx *ctx, const char *apiname);\n\n**Available since:** 5.0.4\n\nRequest an exported API pointer. The return value is just a void pointer\nthat the caller of this function will be required to cast to the right\nfunction pointer, so this is a private contract between modules.\n\nIf the requested API is not available then NULL is returned. Because\nmodules can be loaded at different times with different order, this\nfunction calls should be put inside some module generic API registering\nstep, that is called every time a module attempts to execute a\ncommand that requires external APIs: if some API cannot be resolved, the\ncommand should return an error.\n\nHere is an example:\n\n    int ... myCommandImplementation(void) {\n       if (getExternalAPIs() == 0) {\n            reply with an error here if we cannot have the APIs\n       }\n       // Use the API:\n       myFunctionPointer(foo);\n    }\n\nAnd the function registerAPI() is:\n\n    int getExternalAPIs(void) {\n        static int api_loaded = 0;\n        if (api_loaded != 0) return 1; // APIs already resolved.\n\n        myFunctionPointer = RedisModule_GetSharedAPI(\"...\");\n        if (myFunctionPointer == NULL) return 0;\n\n        return 1;\n    }\n\n<span id=\"section-module-command-filter-api\"></span>"},{"id":"module-command-filter-api","title":"Module Command Filter API","role":"content","text":"<span id=\"RedisModule_RegisterCommandFilter\"></span>"},{"id":"redismodule_registercommandfilter","title":"`RedisModule_RegisterCommandFilter`","role":"content","text":"RedisModuleCommandFilter *RedisModule_RegisterCommandFilter(RedisModuleCtx *ctx,\n                                                                RedisModuleCommandFilterFunc callback,\n                                                                int flags);\n\n**Available since:** 5.0.5\n\nRegister a new command filter function.\n\nCommand filtering makes it possible for modules to extend Redis by plugging\ninto the execution flow of all commands.\n\nA registered filter gets called before Redis executes *any* command.  This\nincludes both core Redis commands and commands registered by any module.  The\nfilter applies in all execution paths including:\n\n1. Invocation by a client.\n2. Invocation through [`RedisModule_Call()`](#RedisModule_Call) by any module.\n3. Invocation through Lua `redis.call()`.\n4. Replication of a command from a master.\n\nThe filter executes in a special filter context, which is different and more\nlimited than a `RedisModuleCtx`.  Because the filter affects any command, it\nmust be implemented in a very efficient way to reduce the performance impact\non Redis.  All Redis Module API calls that require a valid context (such as\n[`RedisModule_Call()`](#RedisModule_Call), [`RedisModule_OpenKey()`](#RedisModule_OpenKey), etc.) are not supported in a\nfilter context.\n\nThe `RedisModuleCommandFilterCtx` can be used to inspect or modify the\nexecuted command and its arguments.  As the filter executes before Redis\nbegins processing the command, any change will affect the way the command is\nprocessed.  For example, a module can override Redis commands this way:\n\n1. Register a `MODULE.SET` command which implements an extended version of\n   the Redis `SET` command.\n2. Register a command filter which detects invocation of `SET` on a specific\n   pattern of keys.  Once detected, the filter will replace the first\n   argument from `SET` to `MODULE.SET`.\n3. When filter execution is complete, Redis considers the new command name\n   and therefore executes the module's own command.\n\nNote that in the above use case, if `MODULE.SET` itself uses\n[`RedisModule_Call()`](#RedisModule_Call) the filter will be applied on that call as well.  If\nthat is not desired, the `REDISMODULE_CMDFILTER_NOSELF` flag can be set when\nregistering the filter.\n\nThe `REDISMODULE_CMDFILTER_NOSELF` flag prevents execution flows that\noriginate from the module's own [`RedisModule_Call()`](#RedisModule_Call) from reaching the filter.  This\nflag is effective for all execution flows, including nested ones, as long as\nthe execution begins from the module's command context or a thread-safe\ncontext that is associated with a blocking command.\n\nDetached thread-safe contexts are *not* associated with the module and cannot\nbe protected by this flag.\n\nIf multiple filters are registered (by the same or different modules), they\nare executed in the order of registration.\n\n<span id=\"RedisModule_UnregisterCommandFilter\"></span>"},{"id":"redismodule_unregistercommandfilter","title":"`RedisModule_UnregisterCommandFilter`","role":"content","text":"int RedisModule_UnregisterCommandFilter(RedisModuleCtx *ctx,\n                                            RedisModuleCommandFilter *filter);\n\n**Available since:** 5.0.5\n\nUnregister a command filter.\n\n<span id=\"RedisModule_CommandFilterArgsCount\"></span>"},{"id":"redismodule_commandfilterargscount","title":"`RedisModule_CommandFilterArgsCount`","role":"content","text":"int RedisModule_CommandFilterArgsCount(RedisModuleCommandFilterCtx *fctx);\n\n**Available since:** 5.0.5\n\nReturn the number of arguments a filtered command has.  The number of\narguments include the command itself.\n\n<span id=\"RedisModule_CommandFilterArgGet\"></span>"},{"id":"redismodule_commandfilterargget","title":"`RedisModule_CommandFilterArgGet`","role":"content","text":"RedisModuleString *RedisModule_CommandFilterArgGet(RedisModuleCommandFilterCtx *fctx,\n                                                       int pos);\n\n**Available since:** 5.0.5\n\nReturn the specified command argument.  The first argument (position 0) is\nthe command itself, and the rest are user-provided args.\n\n<span id=\"RedisModule_CommandFilterArgInsert\"></span>"},{"id":"redismodule_commandfilterarginsert","title":"`RedisModule_CommandFilterArgInsert`","role":"content","text":"int RedisModule_CommandFilterArgInsert(RedisModuleCommandFilterCtx *fctx,\n                                           int pos,\n                                           RedisModuleString *arg);\n\n**Available since:** 5.0.5\n\nModify the filtered command by inserting a new argument at the specified\nposition.  The specified `RedisModuleString` argument may be used by Redis\nafter the filter context is destroyed, so it must not be auto-memory\nallocated, freed or used elsewhere.\n\n<span id=\"RedisModule_CommandFilterArgReplace\"></span>"},{"id":"redismodule_commandfilterargreplace","title":"`RedisModule_CommandFilterArgReplace`","role":"content","text":"int RedisModule_CommandFilterArgReplace(RedisModuleCommandFilterCtx *fctx,\n                                            int pos,\n                                            RedisModuleString *arg);\n\n**Available since:** 5.0.5\n\nModify the filtered command by replacing an existing argument with a new one.\nThe specified `RedisModuleString` argument may be used by Redis after the\nfilter context is destroyed, so it must not be auto-memory allocated, freed\nor used elsewhere.\n\n<span id=\"RedisModule_CommandFilterArgDelete\"></span>"},{"id":"redismodule_commandfilterargdelete","title":"`RedisModule_CommandFilterArgDelete`","role":"content","text":"int RedisModule_CommandFilterArgDelete(RedisModuleCommandFilterCtx *fctx,\n                                           int pos);\n\n**Available since:** 5.0.5\n\nModify the filtered command by deleting an argument at the specified\nposition.\n\n<span id=\"RedisModule_CommandFilterGetClientId\"></span>"},{"id":"redismodule_commandfiltergetclientid","title":"`RedisModule_CommandFilterGetClientId`","role":"content","text":"unsigned long long RedisModule_CommandFilterGetClientId(RedisModuleCommandFilterCtx *fctx);\n\n**Available since:** 7.2.0\n\nGet Client ID for client that issued the command we are filtering\n\n<span id=\"RedisModule_MallocSize\"></span>"},{"id":"redismodule_mallocsize","title":"`RedisModule_MallocSize`","role":"content","text":"size_t RedisModule_MallocSize(void* ptr);\n\n**Available since:** 6.0.0\n\nFor a given pointer allocated via [`RedisModule_Alloc()`](#RedisModule_Alloc) or\n[`RedisModule_Realloc()`](#RedisModule_Realloc), return the amount of memory allocated for it.\nNote that this may be different (larger) than the memory we allocated\nwith the allocation calls, since sometimes the underlying allocator\nwill allocate more memory.\n\n<span id=\"RedisModule_MallocUsableSize\"></span>"},{"id":"redismodule_mallocusablesize","title":"`RedisModule_MallocUsableSize`","role":"content","text":"size_t RedisModule_MallocUsableSize(void *ptr);\n\n**Available since:** 7.0.1\n\nSimilar to [`RedisModule_MallocSize`](#RedisModule_MallocSize), the difference is that [`RedisModule_MallocUsableSize`](#RedisModule_MallocUsableSize)\nreturns the usable size of memory by the module.\n\n<span id=\"RedisModule_MallocSizeString\"></span>"},{"id":"redismodule_mallocsizestring","title":"`RedisModule_MallocSizeString`","role":"content","text":"size_t RedisModule_MallocSizeString(RedisModuleString* str);\n\n**Available since:** 7.0.0\n\nSame as [`RedisModule_MallocSize`](#RedisModule_MallocSize), except it works on `RedisModuleString` pointers.\n\n<span id=\"RedisModule_MallocSizeDict\"></span>"},{"id":"redismodule_mallocsizedict","title":"`RedisModule_MallocSizeDict`","role":"content","text":"size_t RedisModule_MallocSizeDict(RedisModuleDict* dict);\n\n**Available since:** 7.0.0\n\nSame as [`RedisModule_MallocSize`](#RedisModule_MallocSize), except it works on `RedisModuleDict` pointers.\nNote that the returned value is only the overhead of the underlying structures,\nit does not include the allocation size of the keys and values.\n\n<span id=\"RedisModule_GetUsedMemoryRatio\"></span>"},{"id":"redismodule_getusedmemoryratio","title":"`RedisModule_GetUsedMemoryRatio`","role":"content","text":"float RedisModule_GetUsedMemoryRatio(void);\n\n**Available since:** 6.0.0\n\nReturn the a number between 0 to 1 indicating the amount of memory\ncurrently used, relative to the Redis \"maxmemory\" configuration.\n\n* 0 - No memory limit configured.\n* Between 0 and 1 - The percentage of the memory used normalized in 0-1 range.\n* Exactly 1 - Memory limit reached.\n* Greater 1 - More memory used than the configured limit.\n\n<span id=\"section-scanning-keyspace-and-hashes\"></span>"},{"id":"scanning-keyspace-and-hashes","title":"Scanning keyspace and hashes","role":"content","text":"<span id=\"RedisModule_ScanCursorCreate\"></span>"},{"id":"redismodule_scancursorcreate","title":"`RedisModule_ScanCursorCreate`","role":"content","text":"RedisModuleScanCursor *RedisModule_ScanCursorCreate(void);\n\n**Available since:** 6.0.0\n\nCreate a new cursor to be used with [`RedisModule_Scan`](#RedisModule_Scan)\n\n<span id=\"RedisModule_ScanCursorRestart\"></span>"},{"id":"redismodule_scancursorrestart","title":"`RedisModule_ScanCursorRestart`","role":"content","text":"void RedisModule_ScanCursorRestart(RedisModuleScanCursor *cursor);\n\n**Available since:** 6.0.0\n\nRestart an existing cursor. The keys will be rescanned.\n\n<span id=\"RedisModule_ScanCursorDestroy\"></span>"},{"id":"redismodule_scancursordestroy","title":"`RedisModule_ScanCursorDestroy`","role":"content","text":"void RedisModule_ScanCursorDestroy(RedisModuleScanCursor *cursor);\n\n**Available since:** 6.0.0\n\nDestroy the cursor struct.\n\n<span id=\"RedisModule_Scan\"></span>"},{"id":"redismodule_scan","title":"`RedisModule_Scan`","role":"content","text":"int RedisModule_Scan(RedisModuleCtx *ctx,\n                         RedisModuleScanCursor *cursor,\n                         RedisModuleScanCB fn,\n                         void *privdata);\n\n**Available since:** 6.0.0\n\nScan API that allows a module to scan all the keys and value in\nthe selected db.\n\nCallback for scan implementation.\n\n    void scan_callback(RedisModuleCtx *ctx, RedisModuleString *keyname,\n                       RedisModuleKey *key, void *privdata);\n\n- `ctx`: the redis module context provided to for the scan.\n- `keyname`: owned by the caller and need to be retained if used after this\n  function.\n- `key`: holds info on the key and value, it is provided as best effort, in\n  some cases it might be NULL, in which case the user should (can) use\n  [`RedisModule_OpenKey()`](#RedisModule_OpenKey) (and CloseKey too).\n  when it is provided, it is owned by the caller and will be free when the\n  callback returns.\n- `privdata`: the user data provided to [`RedisModule_Scan()`](#RedisModule_Scan).\n\nThe way it should be used:\n\n     RedisModuleScanCursor *c = RedisModule_ScanCursorCreate();\n     while(RedisModule_Scan(ctx, c, callback, privateData));\n     RedisModule_ScanCursorDestroy(c);\n\nIt is also possible to use this API from another thread while the lock\nis acquired during the actual call to [`RedisModule_Scan`](#RedisModule_Scan):\n\n     RedisModuleScanCursor *c = RedisModule_ScanCursorCreate();\n     RedisModule_ThreadSafeContextLock(ctx);\n     while(RedisModule_Scan(ctx, c, callback, privateData)){\n         RedisModule_ThreadSafeContextUnlock(ctx);\n         // do some background job\n         RedisModule_ThreadSafeContextLock(ctx);\n     }\n     RedisModule_ScanCursorDestroy(c);\n\nThe function will return 1 if there are more elements to scan and\n0 otherwise, possibly setting errno if the call failed.\n\nIt is also possible to restart an existing cursor using [`RedisModule_ScanCursorRestart`](#RedisModule_ScanCursorRestart).\n\nIMPORTANT: This API is very similar to the Redis SCAN command from the\npoint of view of the guarantees it provides. This means that the API\nmay report duplicated keys, but guarantees to report at least one time\nevery key that was there from the start to the end of the scanning process.\n\nNOTE: If you do database changes within the callback, you should be aware\nthat the internal state of the database may change. For instance it is safe\nto delete or modify the current key, but may not be safe to delete any\nother key.\nMoreover playing with the Redis keyspace while iterating may have the\neffect of returning more duplicates. A safe pattern is to store the keys\nnames you want to modify elsewhere, and perform the actions on the keys\nlater when the iteration is complete. However this can cost a lot of\nmemory, so it may make sense to just operate on the current key when\npossible during the iteration, given that this is safe.\n\n<span id=\"RedisModule_ScanKey\"></span>"},{"id":"redismodule_scankey","title":"`RedisModule_ScanKey`","role":"content","text":"int RedisModule_ScanKey(RedisModuleKey *key,\n                            RedisModuleScanCursor *cursor,\n                            RedisModuleScanKeyCB fn,\n                            void *privdata);\n\n**Available since:** 6.0.0\n\nScan api that allows a module to scan the elements in a hash, set or sorted set key\n\nCallback for scan implementation.\n\n    void scan_callback(RedisModuleKey *key, RedisModuleString* field, RedisModuleString* value, void *privdata);\n\n- key - the redis key context provided to for the scan.\n- field - field name, owned by the caller and need to be retained if used\n  after this function.\n- value - value string or NULL for set type, owned by the caller and need to\n  be retained if used after this function.\n- privdata - the user data provided to [`RedisModule_ScanKey`](#RedisModule_ScanKey).\n\nThe way it should be used:\n\n     RedisModuleScanCursor *c = RedisModule_ScanCursorCreate();\n     RedisModuleKey *key = RedisModule_OpenKey(...);\n     while(RedisModule_ScanKey(key, c, callback, privateData));\n     RedisModule_CloseKey(key);\n     RedisModule_ScanCursorDestroy(c);\n\nIt is also possible to use this API from another thread while the lock is acquired during\nthe actual call to [`RedisModule_ScanKey`](#RedisModule_ScanKey), and re-opening the key each time:\n\n     RedisModuleScanCursor *c = RedisModule_ScanCursorCreate();\n     RedisModule_ThreadSafeContextLock(ctx);\n     RedisModuleKey *key = RedisModule_OpenKey(...);\n     while(RedisModule_ScanKey(ctx, c, callback, privateData)){\n         RedisModule_CloseKey(key);\n         RedisModule_ThreadSafeContextUnlock(ctx);\n         // do some background job\n         RedisModule_ThreadSafeContextLock(ctx);\n         key = RedisModule_OpenKey(...);\n     }\n     RedisModule_CloseKey(key);\n     RedisModule_ScanCursorDestroy(c);\n\nThe function will return 1 if there are more elements to scan and 0 otherwise,\npossibly setting errno if the call failed.\nIt is also possible to restart an existing cursor using [`RedisModule_ScanCursorRestart`](#RedisModule_ScanCursorRestart).\n\nNOTE: The scan may return an element more than once and you must not  modify\nthe key within the callback.\n\n<span id=\"section-module-fork-api\"></span>"},{"id":"module-fork-api","title":"Module fork API","role":"content","text":"<span id=\"RedisModule_Fork\"></span>"},{"id":"redismodule_fork","title":"`RedisModule_Fork`","role":"content","text":"int RedisModule_Fork(RedisModuleForkDoneHandler cb, void *user_data);\n\n**Available since:** 6.0.0\n\nCreate a background child process with the current frozen snapshot of the\nmain process where you can do some processing in the background without\naffecting / freezing the traffic and no need for threads and GIL locking.\nNote that Redis allows for only one concurrent fork.\nWhen the child wants to exit, it should call [`RedisModule_ExitFromChild`](#RedisModule_ExitFromChild).\nIf the parent wants to kill the child it should call [`RedisModule_KillForkChild`](#RedisModule_KillForkChild)\nThe done handler callback will be executed on the parent process when the\nchild existed (but not when killed)\nReturn: -1 on failure, on success the parent process will get a positive PID\nof the child, and the child process will get 0.\n\n<span id=\"RedisModule_SendChildHeartbeat\"></span>"},{"id":"redismodule_sendchildheartbeat","title":"`RedisModule_SendChildHeartbeat`","role":"content","text":"void RedisModule_SendChildHeartbeat(double progress);\n\n**Available since:** 6.2.0\n\nThe module is advised to call this function from the fork child once in a while,\nso that it can report progress and COW memory to the parent which will be\nreported in INFO.\nThe `progress` argument should between 0 and 1, or -1 when not available.\n\n<span id=\"RedisModule_ExitFromChild\"></span>"},{"id":"redismodule_exitfromchild","title":"`RedisModule_ExitFromChild`","role":"content","text":"int RedisModule_ExitFromChild(int retcode);\n\n**Available since:** 6.0.0\n\nCall from the child process when you want to terminate it.\nretcode will be provided to the done handler executed on the parent process.\n\n<span id=\"RedisModule_KillForkChild\"></span>"},{"id":"redismodule_killforkchild","title":"`RedisModule_KillForkChild`","role":"content","text":"int RedisModule_KillForkChild(int child_pid);\n\n**Available since:** 6.0.0\n\nCan be used to kill the forked child process from the parent process.\n`child_pid` would be the return value of [`RedisModule_Fork`](#RedisModule_Fork).\n\n<span id=\"section-server-hooks-implementation\"></span>"},{"id":"server-hooks-implementation","title":"Server hooks implementation","role":"content","text":"<span id=\"RedisModule_SubscribeToServerEvent\"></span>"},{"id":"redismodule_subscribetoserverevent","title":"`RedisModule_SubscribeToServerEvent`","role":"content","text":"int RedisModule_SubscribeToServerEvent(RedisModuleCtx *ctx,\n                                           RedisModuleEvent event,\n                                           RedisModuleEventCallback callback);\n\n**Available since:** 6.0.0\n\nRegister to be notified, via a callback, when the specified server event\nhappens. The callback is called with the event as argument, and an additional\nargument which is a void pointer and should be cased to a specific type\nthat is event-specific (but many events will just use NULL since they do not\nhave additional information to pass to the callback).\n\nIf the callback is NULL and there was a previous subscription, the module\nwill be unsubscribed. If there was a previous subscription and the callback\nis not null, the old callback will be replaced with the new one.\n\nThe callback must be of this type:\n\n    int (*RedisModuleEventCallback)(RedisModuleCtx *ctx,\n                                    RedisModuleEvent eid,\n                                    uint64_t subevent,\n                                    void *data);\n\nThe 'ctx' is a normal Redis module context that the callback can use in\norder to call other modules APIs. The 'eid' is the event itself, this\nis only useful in the case the module subscribed to multiple events: using\nthe 'id' field of this structure it is possible to check if the event\nis one of the events we registered with this callback. The 'subevent' field\ndepends on the event that fired.\n\nFinally the 'data' pointer may be populated, only for certain events, with\nmore relevant data.\n\nHere is a list of events you can use as 'eid' and related sub events:\n\n* `RedisModuleEvent_ReplicationRoleChanged`:\n\n    This event is called when the instance switches from master\n    to replica or the other way around, however the event is\n    also called when the replica remains a replica but starts to\n    replicate with a different master.\n\n    The following sub events are available:\n\n    * `REDISMODULE_SUBEVENT_REPLROLECHANGED_NOW_MASTER`\n    * `REDISMODULE_SUBEVENT_REPLROLECHANGED_NOW_REPLICA`\n\n    The 'data' field can be casted by the callback to a\n    `RedisModuleReplicationInfo` structure with the following fields:\n\n        int master; // true if master, false if replica\n        char *masterhost; // master instance hostname for NOW_REPLICA\n        int masterport; // master instance port for NOW_REPLICA\n        char *replid1; // Main replication ID\n        char *replid2; // Secondary replication ID\n        uint64_t repl1_offset; // Main replication offset\n        uint64_t repl2_offset; // Offset of replid2 validity\n\n* `RedisModuleEvent_Persistence`\n\n    This event is called when RDB saving or AOF rewriting starts\n    and ends. The following sub events are available:\n\n    * `REDISMODULE_SUBEVENT_PERSISTENCE_RDB_START`\n    * `REDISMODULE_SUBEVENT_PERSISTENCE_AOF_START`\n    * `REDISMODULE_SUBEVENT_PERSISTENCE_SYNC_RDB_START`\n    * `REDISMODULE_SUBEVENT_PERSISTENCE_SYNC_AOF_START`\n    * `REDISMODULE_SUBEVENT_PERSISTENCE_ENDED`\n    * `REDISMODULE_SUBEVENT_PERSISTENCE_FAILED`\n\n    The above events are triggered not just when the user calls the\n    relevant commands like BGSAVE, but also when a saving operation\n    or AOF rewriting occurs because of internal server triggers.\n    The SYNC_RDB_START sub events are happening in the foreground due to\n    SAVE command, FLUSHALL, or server shutdown, and the other RDB and\n    AOF sub events are executed in a background fork child, so any\n    action the module takes can only affect the generated AOF or RDB,\n    but will not be reflected in the parent process and affect connected\n    clients and commands. Also note that the AOF_START sub event may end\n    up saving RDB content in case of an AOF with rdb-preamble.\n\n* `RedisModuleEvent_FlushDB`\n\n    The FLUSHALL, FLUSHDB or an internal flush (for instance\n    because of replication, after the replica synchronization)\n    happened. The following sub events are available:\n\n    * `REDISMODULE_SUBEVENT_FLUSHDB_START`\n    * `REDISMODULE_SUBEVENT_FLUSHDB_END`\n\n    The data pointer can be casted to a RedisModuleFlushInfo\n    structure with the following fields:\n\n        int32_t async;  // True if the flush is done in a thread.\n                        // See for instance FLUSHALL ASYNC.\n                        // In this case the END callback is invoked\n                        // immediately after the database is put\n                        // in the free list of the thread.\n        int32_t dbnum;  // Flushed database number, -1 for all the DBs\n                        // in the case of the FLUSHALL operation.\n\n    The start event is called *before* the operation is initiated, thus\n    allowing the callback to call DBSIZE or other operation on the\n    yet-to-free keyspace.\n\n* `RedisModuleEvent_Loading`\n\n    Called on loading operations: at startup when the server is\n    started, but also after a first synchronization when the\n    replica is loading the RDB file from the master.\n    The following sub events are available:\n\n    * `REDISMODULE_SUBEVENT_LOADING_RDB_START`\n    * `REDISMODULE_SUBEVENT_LOADING_AOF_START`\n    * `REDISMODULE_SUBEVENT_LOADING_REPL_START`\n    * `REDISMODULE_SUBEVENT_LOADING_ENDED`\n    * `REDISMODULE_SUBEVENT_LOADING_FAILED`\n\n    Note that AOF loading may start with an RDB data in case of\n    rdb-preamble, in which case you'll only receive an AOF_START event.\n\n* `RedisModuleEvent_ClientChange`\n\n    Called when a client connects or disconnects.\n    The data pointer can be casted to a RedisModuleClientInfo\n    structure, documented in RedisModule_GetClientInfoById().\n    The following sub events are available:\n\n    * `REDISMODULE_SUBEVENT_CLIENT_CHANGE_CONNECTED`\n    * `REDISMODULE_SUBEVENT_CLIENT_CHANGE_DISCONNECTED`\n\n* `RedisModuleEvent_Shutdown`\n\n    The server is shutting down. No subevents are available.\n\n* `RedisModuleEvent_ReplicaChange`\n\n    This event is called when the instance (that can be both a\n    master or a replica) get a new online replica, or lose a\n    replica since it gets disconnected.\n    The following sub events are available:\n\n    * `REDISMODULE_SUBEVENT_REPLICA_CHANGE_ONLINE`\n    * `REDISMODULE_SUBEVENT_REPLICA_CHANGE_OFFLINE`\n\n    No additional information is available so far: future versions\n    of Redis will have an API in order to enumerate the replicas\n    connected and their state.\n\n* `RedisModuleEvent_CronLoop`\n\n    This event is called every time Redis calls the serverCron()\n    function in order to do certain bookkeeping. Modules that are\n    required to do operations from time to time may use this callback.\n    Normally Redis calls this function 10 times per second, but\n    this changes depending on the \"hz\" configuration.\n    No sub events are available.\n\n    The data pointer can be casted to a RedisModuleCronLoop\n    structure with the following fields:\n\n        int32_t hz;  // Approximate number of events per second.\n\n* `RedisModuleEvent_MasterLinkChange`\n\n    This is called for replicas in order to notify when the\n    replication link becomes functional (up) with our master,\n    or when it goes down. Note that the link is not considered\n    up when we just connected to the master, but only if the\n    replication is happening correctly.\n    The following sub events are available:\n\n    * `REDISMODULE_SUBEVENT_MASTER_LINK_UP`\n    * `REDISMODULE_SUBEVENT_MASTER_LINK_DOWN`\n\n* `RedisModuleEvent_ModuleChange`\n\n    This event is called when a new module is loaded or one is unloaded.\n    The following sub events are available:\n\n    * `REDISMODULE_SUBEVENT_MODULE_LOADED`\n    * `REDISMODULE_SUBEVENT_MODULE_UNLOADED`\n\n    The data pointer can be casted to a RedisModuleModuleChange\n    structure with the following fields:\n\n        const char* module_name;  // Name of module loaded or unloaded.\n        int32_t module_version;  // Module version.\n\n* `RedisModuleEvent_LoadingProgress`\n\n    This event is called repeatedly called while an RDB or AOF file\n    is being loaded.\n    The following sub events are available:\n\n    * `REDISMODULE_SUBEVENT_LOADING_PROGRESS_RDB`\n    * `REDISMODULE_SUBEVENT_LOADING_PROGRESS_AOF`\n\n    The data pointer can be casted to a RedisModuleLoadingProgress\n    structure with the following fields:\n\n        int32_t hz;  // Approximate number of events per second.\n        int32_t progress;  // Approximate progress between 0 and 1024,\n                           // or -1 if unknown.\n\n* `RedisModuleEvent_SwapDB`\n\n    This event is called when a SWAPDB command has been successfully\n    Executed.\n    For this event call currently there is no subevents available.\n\n    The data pointer can be casted to a RedisModuleSwapDbInfo\n    structure with the following fields:\n\n        int32_t dbnum_first;    // Swap Db first dbnum\n        int32_t dbnum_second;   // Swap Db second dbnum\n\n* `RedisModuleEvent_ReplBackup`\n\n    WARNING: Replication Backup events are deprecated since Redis 7.0 and are never fired.\n    See RedisModuleEvent_ReplAsyncLoad for understanding how Async Replication Loading events\n    are now triggered when repl-diskless-load is set to swapdb.\n\n    Called when repl-diskless-load config is set to swapdb,\n    And redis needs to backup the current database for the\n    possibility to be restored later. A module with global data and\n    maybe with aux_load and aux_save callbacks may need to use this\n    notification to backup / restore / discard its globals.\n    The following sub events are available:\n\n    * `REDISMODULE_SUBEVENT_REPL_BACKUP_CREATE`\n    * `REDISMODULE_SUBEVENT_REPL_BACKUP_RESTORE`\n    * `REDISMODULE_SUBEVENT_REPL_BACKUP_DISCARD`\n\n* `RedisModuleEvent_ReplAsyncLoad`\n\n    Called when repl-diskless-load config is set to swapdb and a replication with a master of same\n    data set history (matching replication ID) occurs.\n    In which case redis serves current data set while loading new database in memory from socket.\n    Modules must have declared they support this mechanism in order to activate it, through\n    REDISMODULE_OPTIONS_HANDLE_REPL_ASYNC_LOAD flag.\n    The following sub events are available:\n\n    * `REDISMODULE_SUBEVENT_REPL_ASYNC_LOAD_STARTED`\n    * `REDISMODULE_SUBEVENT_REPL_ASYNC_LOAD_ABORTED`\n    * `REDISMODULE_SUBEVENT_REPL_ASYNC_LOAD_COMPLETED`\n\n* `RedisModuleEvent_ForkChild`\n\n    Called when a fork child (AOFRW, RDBSAVE, module fork...) is born/dies\n    The following sub events are available:\n\n    * `REDISMODULE_SUBEVENT_FORK_CHILD_BORN`\n    * `REDISMODULE_SUBEVENT_FORK_CHILD_DIED`\n\n* `RedisModuleEvent_EventLoop`\n\n    Called on each event loop iteration, once just before the event loop goes\n    to sleep or just after it wakes up.\n    The following sub events are available:\n\n    * `REDISMODULE_SUBEVENT_EVENTLOOP_BEFORE_SLEEP`\n    * `REDISMODULE_SUBEVENT_EVENTLOOP_AFTER_SLEEP`\n\n* `RedisModule_Event_Config`\n\n    Called when a configuration event happens\n    The following sub events are available:\n\n    * `REDISMODULE_SUBEVENT_CONFIG_CHANGE`\n\n    The data pointer can be casted to a RedisModuleConfigChange\n    structure with the following fields:\n\n        const char **config_names; // An array of C string pointers containing the\n                                   // name of each modified configuration item \n        uint32_t num_changes;      // The number of elements in the config_names array\n\n* `RedisModule_Event_Key`\n\n    Called when a key is removed from the keyspace. We can't modify any key in\n    the event.\n    The following sub events are available:\n\n    * `REDISMODULE_SUBEVENT_KEY_DELETED`\n    * `REDISMODULE_SUBEVENT_KEY_EXPIRED`\n    * `REDISMODULE_SUBEVENT_KEY_EVICTED`\n    * `REDISMODULE_SUBEVENT_KEY_OVERWRITTEN`\n\n    The data pointer can be casted to a RedisModuleKeyInfo\n    structure with the following fields:\n\n        RedisModuleKey *key;    // Key name\n\n* `RedisModuleEvent_ClusterSlotMigration`\n\n    Called when an atomic slot migration (ASM) event happens.\n    IMPORT events are triggered on the destination side of a slot migration\n    operation. These notifications let modules prepare for the upcoming\n    ownership change, observe successful completion once the cluster config\n    reflects the new owner, or detect a failure in which case slot ownership\n    remains with the source.\n\n    Similarly, MIGRATE events triggered on the source side of a slot\n    migration operation to let modules prepare for the ownership change and\n    observe the completion of the slot migration. MIGRATE_MODULE_PROPAGATE\n    event is triggered in the fork just before snapshot delivery; modules may\n    use it to enqueue commands that will be delivered first. The\n    MIGRATE_MODULE_PROPAGATE_END event is triggered in the main process at the\n    very end of the migration, just before the STREAM-EOF is sent; modules may\n    use it to enqueue commands that will be delivered last. See\n    RedisModule_ClusterPropagateForSlotMigration() for details.\n\n    * `REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_IMPORT_STARTED`\n    * `REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_IMPORT_FAILED`\n    * `REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_IMPORT_COMPLETED`\n    * `REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_MIGRATE_STARTED`\n    * `REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_MIGRATE_FAILED`\n    * `REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_MIGRATE_COMPLETED`\n    * `REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_MIGRATE_MODULE_PROPAGATE`\n    * `REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_MIGRATE_MODULE_PROPAGATE_END`\n\n    The data pointer can be casted to a RedisModuleClusterSlotMigrationInfo\n    structure with the following fields:\n\n        char source_node_id[REDISMODULE_NODE_ID_LEN + 1];\n        char destination_node_id[REDISMODULE_NODE_ID_LEN + 1];\n        const char *task_id;               // Task ID\n        RedisModuleSlotRangeArray *slots;  // Slot ranges\n\n* `RedisModuleEvent_ClusterSlotMigrationTrim`\n\n    Called when trimming keys after a slot migration. Fires on the source\n    after a successful migration to clean up migrated keys, or on the\n    destination after a failed import to discard partial imports. Two methods\n    are supported. In the first method, keys are deleted in a background\n    thread; this is reported via the TRIM_BACKGROUND event. In the second\n    method, Redis performs incremental deletions on the main thread via the\n    cron loop to avoid stalls; this is reported via the TRIM_STARTED and\n    TRIM_COMPLETED events. Each deletion emits REDISMODULE_NOTIFY_KEY_TRIMMED\n    so modules can react to individual key deletions. Redis selects the\n    method automatically: background by default; switches to main thread\n    trimming when a module subscribes to REDISMODULE_NOTIFY_KEY_TRIMMED.\n\n    The following sub events are available:\n\n    * `REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_TRIM_STARTED`\n    * `REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_TRIM_COMPLETED`\n    * `REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_TRIM_BACKGROUND`\n\n    The data pointer can be casted to a RedisModuleClusterSlotMigrationTrimInfo\n    structure with the following fields:\n\n        RedisModuleSlotRangeArray *slots;  // Slot ranges\n\n* `RedisModuleEvent_ClusterTopologyChange`\n\n    Called, in cluster mode, when the topology of the cluster changes: when the\n    cluster first becomes ready, when slot ownership or node membership changes\n    (resharding, a node joining or leaving), and when a primary changes (a\n    failover or a replica being promoted). It lets modules that route to other\n    shards (e.g. via slot ranges) refresh their cached view of the cluster\n    without polling or requiring an explicit command. Fires are debounced to at\n    most one per event loop iteration, so a single reshuffle touching many slots\n    yields one event.\n\n    This event has no meaningful sub event (it is always fired with subevent 0).\n\n    The data pointer can be casted to a RedisModuleClusterTopologyChangeInfo\n    structure. Its `change_flags` field is a bitmask of the reasons that\n    contributed to this (possibly debounced) notification:\n\n    * `REDISMODULE_CLUSTER_TOPOLOGY_CHANGE_FLAG_SLOT`:\n        Slot ownership changed.\n    * `REDISMODULE_CLUSTER_TOPOLOGY_CHANGE_FLAG_ROLE`:\n        A node changed its primary/replica role (e.g. a failover).\n    * `REDISMODULE_CLUSTER_TOPOLOGY_CHANGE_FLAG_STATE`:\n        The cluster OK/FAIL state changed (this also covers the cluster\n        first becoming ready at startup).\n    * `REDISMODULE_CLUSTER_TOPOLOGY_CHANGE_FLAG_NODE`:\n        A node joined or left the cluster, or an existing node's address\n        (ip/port) changed.\n\n    More than one bit may be set. Beyond the change reasons, the module reads\n    whatever else it needs about the new topology via the cluster info APIs\n    (e.g. `CLUSTER SLOTS`, RedisModule_GetClusterNodesList /\n    RedisModule_GetClusterNodeInfo / RedisModule_GetClusterSize).\n\nThe function returns `REDISMODULE_OK` if the module was successfully subscribed\nfor the specified event. If the API is called from a wrong context or unsupported event\nis given then `REDISMODULE_ERR` is returned.\n\n<span id=\"RedisModule_IsSubEventSupported\"></span>"},{"id":"redismodule_issubeventsupported","title":"`RedisModule_IsSubEventSupported`","role":"content","text":"int RedisModule_IsSubEventSupported(RedisModuleEvent event, int64_t subevent);\n\n**Available since:** 6.0.9\n\n\nFor a given server event and subevent, return zero if the\nsubevent is not supported and non-zero otherwise.\n\n<span id=\"section-module-configurations-api\"></span>"},{"id":"module-configurations-api","title":"Module Configurations API","role":"content","text":"<span id=\"RedisModule_RegisterStringConfig\"></span>"},{"id":"redismodule_registerstringconfig","title":"`RedisModule_RegisterStringConfig`","role":"content","text":"int RedisModule_RegisterStringConfig(RedisModuleCtx *ctx,\n                                         const char *name,\n                                         const char *default_val,\n                                         unsigned int flags,\n                                         RedisModuleConfigGetStringFunc getfn,\n                                         RedisModuleConfigSetStringFunc setfn,\n                                         RedisModuleConfigApplyFunc applyfn,\n                                         void *privdata);\n\n**Available since:** 7.0.0\n\nCreate a string config that Redis users can interact with via the Redis config file,\n`CONFIG SET`, `CONFIG GET`, and `CONFIG REWRITE` commands.\n\nThe actual config value is owned by the module, and the `getfn`, `setfn` and optional\n`applyfn` callbacks that are provided to Redis in order to access or manipulate the\nvalue. The `getfn` callback retrieves the value from the module, while the `setfn`\ncallback provides a value to be stored into the module config.\nThe optional `applyfn` callback is called after a `CONFIG SET` command modified one or\nmore configs using the `setfn` callback and can be used to atomically apply a config\nafter several configs were changed together.\nIf there are multiple configs with `applyfn` callbacks set by a single `CONFIG SET`\ncommand, they will be deduplicated if their `applyfn` function and `privdata` pointers\nare identical, and the callback will only be run once.\nBoth the `setfn` and `applyfn` can return an error if the provided value is invalid or\ncannot be used.\nThe config also declares a type for the value that is validated by Redis and\nprovided to the module. The config system provides the following types:\n\n* Redis String: Binary safe string data.\n* Enum: One of a finite number of string tokens, provided during registration.\n* Numeric: 64 bit signed integer, which also supports min and max values.\n* Bool: Yes or no value.\n\nThe `setfn` callback is expected to return `REDISMODULE_OK` when the value is successfully\napplied. It can also return `REDISMODULE_ERR` if the value can't be applied, and the\n*err pointer can be set with a `RedisModuleString` error message to provide to the client.\nThis `RedisModuleString` will be freed by redis after returning from the set callback.\n\nAll configs are registered with a name, a type, a default value, private data that is made\navailable in the callbacks, as well as several flags that modify the behavior of the config.\nThe name must only contain alphanumeric characters or dashes. The supported flags are:\n\n* `REDISMODULE_CONFIG_DEFAULT`: The default flags for a config. This creates a config that can be modified after startup.\n* `REDISMODULE_CONFIG_IMMUTABLE`: This config can only be provided loading time.\n* `REDISMODULE_CONFIG_SENSITIVE`: The value stored in this config is redacted from all logging.\n* `REDISMODULE_CONFIG_HIDDEN`: The name is hidden from `CONFIG GET` with pattern matching.\n* `REDISMODULE_CONFIG_PROTECTED`: This config will be only be modifiable based off the value of enable-protected-configs.\n* `REDISMODULE_CONFIG_DENY_LOADING`: This config is not modifiable while the server is loading data.\n* `REDISMODULE_CONFIG_MEMORY`: For numeric configs, this config will convert data unit notations into their byte equivalent.\n* `REDISMODULE_CONFIG_BITFLAGS`: For enum configs, this config will allow multiple entries to be combined as bit flags.\n\nDefault values are used on startup to set the value if it is not provided via the config file\nor command line. Default values are also used to compare to on a config rewrite.\n\nNotes:\n\n 1. On string config sets that the string passed to the set callback will be freed after execution and the module must retain it.\n 2. On string config gets the string will not be consumed and will be valid after execution.\n\nExample implementation:\n\n    RedisModuleString *strval;\n    int adjustable = 1;\n    RedisModuleString *getStringConfigCommand(const char *name, void *privdata) {\n        return strval;\n    }\n\n    int setStringConfigCommand(const char *name, RedisModuleString *new, void *privdata, RedisModuleString **err) {\n       if (adjustable) {\n           RedisModule_Free(strval);\n           RedisModule_RetainString(NULL, new);\n           strval = new;\n           return REDISMODULE_OK;\n       }\n       *err = RedisModule_CreateString(NULL, \"Not adjustable.\", 15);\n       return REDISMODULE_ERR;\n    }\n    ...\n    RedisModule_RegisterStringConfig(ctx, \"string\", NULL, REDISMODULE_CONFIG_DEFAULT, getStringConfigCommand, setStringConfigCommand, NULL, NULL);\n\nIf the registration fails, `REDISMODULE_ERR` is returned and one of the following\nerrno is set:\n* EBUSY: Registering the Config outside of `RedisModule_OnLoad`.\n* EINVAL: The provided flags are invalid for the registration or the name of the config contains invalid characters.\n* EALREADY: The provided configuration name is already used.\n\n<span id=\"RedisModule_RegisterBoolConfig\"></span>"},{"id":"redismodule_registerboolconfig","title":"`RedisModule_RegisterBoolConfig`","role":"content","text":"int RedisModule_RegisterBoolConfig(RedisModuleCtx *ctx,\n                                       const char *name,\n                                       int default_val,\n                                       unsigned int flags,\n                                       RedisModuleConfigGetBoolFunc getfn,\n                                       RedisModuleConfigSetBoolFunc setfn,\n                                       RedisModuleConfigApplyFunc applyfn,\n                                       void *privdata);\n\n**Available since:** 7.0.0\n\nCreate a bool config that server clients can interact with via the \n`CONFIG SET`, `CONFIG GET`, and `CONFIG REWRITE` commands. See \n[`RedisModule_RegisterStringConfig`](#RedisModule_RegisterStringConfig) for detailed information about configs.\n\n<span id=\"RedisModule_RegisterEnumConfig\"></span>"},{"id":"redismodule_registerenumconfig","title":"`RedisModule_RegisterEnumConfig`","role":"content","text":"int RedisModule_RegisterEnumConfig(RedisModuleCtx *ctx,\n                                       const char *name,\n                                       int default_val,\n                                       unsigned int flags,\n                                       const char **enum_values,\n                                       const int *int_values,\n                                       int num_enum_vals,\n                                       RedisModuleConfigGetEnumFunc getfn,\n                                       RedisModuleConfigSetEnumFunc setfn,\n                                       RedisModuleConfigApplyFunc applyfn,\n                                       void *privdata);\n\n**Available since:** 7.0.0\n\n\nCreate an enum config that server clients can interact with via the \n`CONFIG SET`, `CONFIG GET`, and `CONFIG REWRITE` commands. \nEnum configs are a set of string tokens to corresponding integer values, where \nthe string value is exposed to Redis clients but the value passed Redis and the\nmodule is the integer value. These values are defined in `enum_values`, an array\nof null-terminated c strings, and `int_vals`, an array of enum values who has an\nindex partner in `enum_values`.\nExample Implementation:\n     const char *enum_vals[3] = {\"first\", \"second\", \"third\"};\n     const int int_vals[3] = {0, 2, 4};\n     int enum_val = 0;\n\n     int getEnumConfigCommand(const char *name, void *privdata) {\n         return enum_val;\n     }\n      \n     int setEnumConfigCommand(const char *name, int val, void *privdata, const char **err) {\n         enum_val = val;\n         return REDISMODULE_OK;\n     }\n     ...\n     RedisModule_RegisterEnumConfig(ctx, \"enum\", 0, REDISMODULE_CONFIG_DEFAULT, enum_vals, int_vals, 3, getEnumConfigCommand, setEnumConfigCommand, NULL, NULL);\n\nNote that you can use `REDISMODULE_CONFIG_BITFLAGS` so that multiple enum string\ncan be combined into one integer as bit flags, in which case you may want to\nsort your enums so that the preferred combinations are present first.\n\nSee [`RedisModule_RegisterStringConfig`](#RedisModule_RegisterStringConfig) for detailed general information about configs.\n\n<span id=\"RedisModule_RegisterNumericConfig\"></span>"},{"id":"redismodule_registernumericconfig","title":"`RedisModule_RegisterNumericConfig`","role":"content","text":"int RedisModule_RegisterNumericConfig(RedisModuleCtx *ctx,\n                                          const char *name,\n                                          long long default_val,\n                                          unsigned int flags,\n                                          long long min,\n                                          long long max,\n                                          RedisModuleConfigGetNumericFunc getfn,\n                                          RedisModuleConfigSetNumericFunc setfn,\n                                          RedisModuleConfigApplyFunc applyfn,\n                                          void *privdata);\n\n**Available since:** 7.0.0\n\n\nCreate an integer config that server clients can interact with via the \n`CONFIG SET`, `CONFIG GET`, and `CONFIG REWRITE` commands. See \n[`RedisModule_RegisterStringConfig`](#RedisModule_RegisterStringConfig) for detailed information about configs.\n\n<span id=\"RedisModule_LoadDefaultConfigs\"></span>"},{"id":"redismodule_loaddefaultconfigs","title":"`RedisModule_LoadDefaultConfigs`","role":"content","text":"int RedisModule_LoadDefaultConfigs(RedisModuleCtx *ctx);\n\n**Available since:** 8.0.0\n\nApplies all default configurations for the parameters the module registered.\nOnly call this function if the module would like to make changes to the\nconfiguration values before the actual values are applied by [`RedisModule_LoadConfigs`](#RedisModule_LoadConfigs).\nOtherwise it's sufficient to call [`RedisModule_LoadConfigs`](#RedisModule_LoadConfigs), it should already set the default values if needed.\nThis makes it possible to distinguish between default values and user provided values and apply other changes between setting the defaults and the user values.\nThis will return `REDISMODULE_ERR` if it is called:\n1. outside `RedisModule_OnLoad`\n2. more than once\n3. after the [`RedisModule_LoadConfigs`](#RedisModule_LoadConfigs) call\n\n<span id=\"RedisModule_LoadConfigs\"></span>"},{"id":"redismodule_loadconfigs","title":"`RedisModule_LoadConfigs`","role":"content","text":"int RedisModule_LoadConfigs(RedisModuleCtx *ctx);\n\n**Available since:** 7.0.0\n\nApplies all pending configurations on the module load. This should be called\nafter all of the configurations have been registered for the module inside of `RedisModule_OnLoad`.\nThis will return `REDISMODULE_ERR` if it is called outside `RedisModule_OnLoad`.\nThis API needs to be called when configurations are provided in either `MODULE LOADEX`\nor provided as startup arguments.\n\n<span id=\"section-rdb-load-save-api\"></span>"},{"id":"rdb-loadsave-api","title":"RDB load/save API","role":"content","text":"<span id=\"RedisModule_RdbStreamCreateFromFile\"></span>"},{"id":"redismodule_rdbstreamcreatefromfile","title":"`RedisModule_RdbStreamCreateFromFile`","role":"content","text":"RedisModuleRdbStream *RedisModule_RdbStreamCreateFromFile(const char *filename);\n\n**Available since:** 7.2.0\n\nCreate a stream object to save/load RDB to/from a file.\n\nThis function returns a pointer to `RedisModuleRdbStream` which is owned\nby the caller. It requires a call to [`RedisModule_RdbStreamFree()`](#RedisModule_RdbStreamFree) to free\nthe object.\n\n<span id=\"RedisModule_RdbStreamFree\"></span>"},{"id":"redismodule_rdbstreamfree","title":"`RedisModule_RdbStreamFree`","role":"content","text":"void RedisModule_RdbStreamFree(RedisModuleRdbStream *stream);\n\n**Available since:** 7.2.0\n\nRelease an RDB stream object.\n\n<span id=\"RedisModule_RdbLoad\"></span>"},{"id":"redismodule_rdbload","title":"`RedisModule_RdbLoad`","role":"content","text":"int RedisModule_RdbLoad(RedisModuleCtx *ctx,\n                            RedisModuleRdbStream *stream,\n                            int flags);\n\n**Available since:** 7.2.0\n\nLoad RDB file from the `stream`. Dataset will be cleared first and then RDB\nfile will be loaded.\n\n`flags` must be zero. This parameter is for future use.\n\nOn success `REDISMODULE_OK` is returned, otherwise `REDISMODULE_ERR` is returned\nand errno is set accordingly.\n\nExample:\n\n    RedisModuleRdbStream *s = RedisModule_RdbStreamCreateFromFile(\"exp.rdb\");\n    RedisModule_RdbLoad(ctx, s, 0);\n    RedisModule_RdbStreamFree(s);\n\n<span id=\"RedisModule_RdbSave\"></span>"},{"id":"redismodule_rdbsave","title":"`RedisModule_RdbSave`","role":"content","text":"int RedisModule_RdbSave(RedisModuleCtx *ctx,\n                            RedisModuleRdbStream *stream,\n                            int flags);\n\n**Available since:** 7.2.0\n\nSave dataset to the RDB stream.\n\n`flags` must be zero. This parameter is for future use.\n\nOn success `REDISMODULE_OK` is returned, otherwise `REDISMODULE_ERR` is returned\nand errno is set accordingly.\n\nExample:\n\n    RedisModuleRdbStream *s = RedisModule_RdbStreamCreateFromFile(\"exp.rdb\");\n    RedisModule_RdbSave(ctx, s, 0);\n    RedisModule_RdbStreamFree(s);\n\n<span id=\"RedisModule_GetInternalSecret\"></span>"},{"id":"redismodule_getinternalsecret","title":"`RedisModule_GetInternalSecret`","role":"content","text":"const char* RedisModule_GetInternalSecret(RedisModuleCtx *ctx, size_t *len);\n\n**Available since:** 8.0.0\n\nReturns the internal secret of the cluster.\nShould be used to authenticate as an internal connection to a node in the\ncluster, and by that gain the permissions to execute internal commands.\n\n<span id=\"section-config-access-api\"></span>"},{"id":"config-access-api","title":"Config access API","role":"content","text":"<span id=\"RedisModule_ConfigIteratorCreate\"></span>"},{"id":"redismodule_configiteratorcreate","title":"`RedisModule_ConfigIteratorCreate`","role":"content","text":"RedisModuleConfigIterator *RedisModule_ConfigIteratorCreate(RedisModuleCtx *ctx,\n                                                                const char *pattern);\n\n**Available since:** 8.2.0\n\nGet an iterator to all configs.\nOptional `ctx` can be provided if use of auto-memory is desired.\nOptional `pattern` can be provided to filter configs by name. If `pattern` is\nNULL all configs will be returned.\n\nThe returned iterator can be used to iterate over all configs using\n[`RedisModule_ConfigIteratorNext()`](#RedisModule_ConfigIteratorNext).\n\nExample usage:\n[code example]\n\nReturns a pointer to `RedisModuleConfigIterator`. Unless auto-memory is enabled\nthe caller is responsible for freeing the iterator using\n[`RedisModule_ConfigIteratorRelease()`](#RedisModule_ConfigIteratorRelease).\n\n<span id=\"RedisModule_ConfigIteratorRelease\"></span>"},{"id":"redismodule_configiteratorrelease","title":"`RedisModule_ConfigIteratorRelease`","role":"content","text":"void RedisModule_ConfigIteratorRelease(RedisModuleCtx *ctx,\n                                           RedisModuleConfigIterator *iter);\n\n**Available since:** 8.2.0\n\nRelease the iterator returned by [`RedisModule_ConfigIteratorCreate()`](#RedisModule_ConfigIteratorCreate). If auto-memory\nis enabled and manual release is needed one must pass the same `RedisModuleCtx`\nthat was used to create the iterator.\n\n<span id=\"RedisModule_ConfigGetType\"></span>"},{"id":"redismodule_configgettype","title":"`RedisModule_ConfigGetType`","role":"content","text":"int RedisModule_ConfigGetType(const char *name, RedisModuleConfigType *res);\n\n**Available since:** 8.2.0\n\nGet the type of a config as `RedisModuleConfigType`. One may use this  in order\nto get or set the values of the config with the appropriate function if the\ngeneric [`RedisModule_ConfigGet`](#RedisModule_ConfigGet) and [`RedisModule_ConfigSet`](#RedisModule_ConfigSet) APIs are performing\npoorly.\n\nIntended usage of this function is when iteration over the configs is\nperformed. See [`RedisModule_ConfigIteratorNext()`](#RedisModule_ConfigIteratorNext) for example usage. If setting\nor getting individual configs one can check the config type by hand in\nredis.conf (or via other sources if config is added by a module) and use the\nappropriate function without the need to call this function.\n\nExplanation of config types:\n - `REDISMODULE_CONFIG_TYPE_BOOL`: Config is a boolean. One can use `RedisModule_Config`(Get/Set)Bool\n - `REDISMODULE_CONFIG_TYPE_NUMERIC`: Config is a numeric value. One can use `RedisModule_Config`(Get/Set)Numeric\n - `REDISMODULE_CONFIG_TYPE_STRING`: Config is a string. One can use the generic `RedisModule_Config`(Get/Set)\n - `REDISMODULE_CONFIG_TYPE_ENUM`: Config is an enum. One can use `RedisModule_Config`(Get/Set)Enum\n\nIf a config with the given name exists `res` is populated with its type, else\n`REDISMODULE_ERR` is returned.\n\n<span id=\"RedisModule_ConfigIteratorNext\"></span>"},{"id":"redismodule_configiteratornext","title":"`RedisModule_ConfigIteratorNext`","role":"content","text":"const char *RedisModule_ConfigIteratorNext(RedisModuleConfigIterator *iter);\n\n**Available since:** 8.2.0\n\nGo to the next element of the config iterator.\n\nReturns the name of the next config, or NULL if there are no more configs.\nReturned string is non-owning and thus should not be freed.\nIf a pattern was provided when creating the iterator, only configs matching\nthe pattern will be returned.\n\nSee [`RedisModule_ConfigIteratorCreate()`](#RedisModule_ConfigIteratorCreate) for example usage.\n\n<span id=\"RedisModule_ConfigGet\"></span>"},{"id":"redismodule_configget","title":"`RedisModule_ConfigGet`","role":"content","text":"int RedisModule_ConfigGet(RedisModuleCtx *ctx,\n                              const char *name,\n                              RedisModuleString **res);\n\n**Available since:** 8.2.0\n\nGet the value of a config as a string. This function can be used to get the\nvalue of any config, regardless of its type.\n\nThe string is allocated by the module and must be freed by the caller unless\nauto memory is enabled.\n\nIf the config does not exist, `REDISMODULE_ERR` is returned, else `REDISMODULE_OK`\nis returned and `res` is populated with the value.\n\n<span id=\"RedisModule_ConfigGetBool\"></span>"},{"id":"redismodule_configgetbool","title":"`RedisModule_ConfigGetBool`","role":"content","text":"int RedisModule_ConfigGetBool(RedisModuleCtx *ctx, const char *name, int *res);\n\n**Available since:** 8.2.0\n\nGet the value of a bool config.\n\nIf the config does not exist or is not a bool config, `REDISMODULE_ERR` is\nreturned, else `REDISMODULE_OK` is returned and `res` is populated with the\nvalue.\n\n<span id=\"RedisModule_ConfigGetEnum\"></span>"},{"id":"redismodule_configgetenum","title":"`RedisModule_ConfigGetEnum`","role":"content","text":"int RedisModule_ConfigGetEnum(RedisModuleCtx *ctx,\n                                  const char *name,\n                                  RedisModuleString **res);\n\n**Available since:** 8.2.0\n\nGet the value of an enum config.\n\nIf the config does not exist or is not an enum config, `REDISMODULE_ERR` is\nreturned, else `REDISMODULE_OK` is returned and `res` is populated with the value.\nIf the config has multiple arguments they are returned as a space-separated\nstring.\n\n<span id=\"RedisModule_ConfigGetNumeric\"></span>"},{"id":"redismodule_configgetnumeric","title":"`RedisModule_ConfigGetNumeric`","role":"content","text":"int RedisModule_ConfigGetNumeric(RedisModuleCtx *ctx,\n                                     const char *name,\n                                     long long *res);\n\n**Available since:** 8.2.0\n\nGet the value of a numeric config.\n\nIf the config does not exist or is not a numeric config, `REDISMODULE_ERR` is\nreturned, else `REDISMODULE_OK` is returned and `res` is populated with the\nvalue.\n\n<span id=\"RedisModule_ConfigSet\"></span>"},{"id":"redismodule_configset","title":"`RedisModule_ConfigSet`","role":"content","text":"int RedisModule_ConfigSet(RedisModuleCtx *ctx,\n                              const char *name,\n                              RedisModuleString *value,\n                              RedisModuleString **err);\n\n**Available since:** 8.2.0\n\nSet the value of a config.\n\nThis function can be used to set the value of any config, regardless of its\ntype. If the config is multi-argument, the value must be a space-separated\nstring.\n\nIf the value failed to be set `REDISMODULE_ERR` will be returned and if `err`\nis not NULL, it will be populated with an error message.\n\n<span id=\"RedisModule_ConfigSetBool\"></span>"},{"id":"redismodule_configsetbool","title":"`RedisModule_ConfigSetBool`","role":"content","text":"int RedisModule_ConfigSetBool(RedisModuleCtx *ctx,\n                                  const char *name,\n                                  int value,\n                                  RedisModuleString **err);\n\n**Available since:** 8.2.0\n\nSet the value of a bool config.\n\nSee [`RedisModule_ConfigSet`](#RedisModule_ConfigSet) for return value.\n\n<span id=\"RedisModule_ConfigSetEnum\"></span>"},{"id":"redismodule_configsetenum","title":"`RedisModule_ConfigSetEnum`","role":"content","text":"int RedisModule_ConfigSetEnum(RedisModuleCtx *ctx,\n                                  const char *name,\n                                  RedisModuleString *value,\n                                  RedisModuleString **err);\n\n**Available since:** 8.2.0\n\nSet the value of an enum config.\n\nIf the config is multi-argument the value parameter must be a space-separated\nstring.\n\nSee [`RedisModule_ConfigSet`](#RedisModule_ConfigSet) for return value.\n\n<span id=\"RedisModule_ConfigSetNumeric\"></span>"},{"id":"redismodule_configsetnumeric","title":"`RedisModule_ConfigSetNumeric`","role":"content","text":"int RedisModule_ConfigSetNumeric(RedisModuleCtx *ctx,\n                                     const char *name,\n                                     long long value,\n                                     RedisModuleString **err);\n\n**Available since:** 8.2.0\n\nSet the value of a numeric config.\nIf the value passed is meant to be a percentage, it should be passed as a\nnegative value.\nFor unsigned configs pass the value and cast to (long long) - internal type\nchecks will handle it.\n\nSee [`RedisModule_ConfigSet`](#RedisModule_ConfigSet) for return value.\n\n<span id=\"section-key-eviction-api\"></span>"},{"id":"key-eviction-api","title":"Key eviction API","role":"content","text":"<span id=\"RedisModule_SetLRU\"></span>"},{"id":"redismodule_setlru","title":"`RedisModule_SetLRU`","role":"content","text":"int RedisModule_SetLRU(RedisModuleKey *key, mstime_t lru_idle);\n\n**Available since:** 6.0.0\n\nSet the key last access time for LRU based eviction. not relevant if the\nservers's maxmemory policy is LFU based. Value is idle time in milliseconds.\nreturns `REDISMODULE_OK` if the LRU was updated, `REDISMODULE_ERR` otherwise.\n\n<span id=\"RedisModule_GetLRU\"></span>"},{"id":"redismodule_getlru","title":"`RedisModule_GetLRU`","role":"content","text":"int RedisModule_GetLRU(RedisModuleKey *key, mstime_t *lru_idle);\n\n**Available since:** 6.0.0\n\nGets the key last access time.\nValue is idletime in milliseconds or -1 if the server's eviction policy is\nLFU based.\nreturns `REDISMODULE_OK` if when key is valid.\n\n<span id=\"RedisModule_SetLFU\"></span>"},{"id":"redismodule_setlfu","title":"`RedisModule_SetLFU`","role":"content","text":"int RedisModule_SetLFU(RedisModuleKey *key, long long lfu_freq);\n\n**Available since:** 6.0.0\n\nSet the key access frequency. only relevant if the server's maxmemory policy\nis LFU based.\nThe frequency is a logarithmic counter that provides an indication of\nthe access frequencyonly (must be <= 255).\nreturns `REDISMODULE_OK` if the LFU was updated, `REDISMODULE_ERR` otherwise.\n\n<span id=\"RedisModule_GetLFU\"></span>"},{"id":"redismodule_getlfu","title":"`RedisModule_GetLFU`","role":"content","text":"int RedisModule_GetLFU(RedisModuleKey *key, long long *lfu_freq);\n\n**Available since:** 6.0.0\n\nGets the key access frequency or -1 if the server's eviction policy is not\nLFU based.\nreturns `REDISMODULE_OK` if when key is valid.\n\n<span id=\"section-miscellaneous-apis\"></span>"},{"id":"miscellaneous-apis","title":"Miscellaneous APIs","role":"content","text":"<span id=\"RedisModule_GetModuleOptionsAll\"></span>"},{"id":"redismodule_getmoduleoptionsall","title":"`RedisModule_GetModuleOptionsAll`","role":"content","text":"int RedisModule_GetModuleOptionsAll(void);\n\n**Available since:** 7.2.0\n\n\nReturns the full module options flags mask, using the return value\nthe module can check if a certain set of module options are supported\nby the redis server version in use.\nExample:\n\n       int supportedFlags = RedisModule_GetModuleOptionsAll();\n       if (supportedFlags & REDISMODULE_OPTIONS_ALLOW_NESTED_KEYSPACE_NOTIFICATIONS) {\n             // REDISMODULE_OPTIONS_ALLOW_NESTED_KEYSPACE_NOTIFICATIONS is supported\n       } else{\n             // REDISMODULE_OPTIONS_ALLOW_NESTED_KEYSPACE_NOTIFICATIONS is not supported\n       }\n\n<span id=\"RedisModule_GetContextFlagsAll\"></span>"},{"id":"redismodule_getcontextflagsall","title":"`RedisModule_GetContextFlagsAll`","role":"content","text":"int RedisModule_GetContextFlagsAll(void);\n\n**Available since:** 6.0.9\n\n\nReturns the full ContextFlags mask, using the return value\nthe module can check if a certain set of flags are supported\nby the redis server version in use.\nExample:\n\n       int supportedFlags = RedisModule_GetContextFlagsAll();\n       if (supportedFlags & REDISMODULE_CTX_FLAGS_MULTI) {\n             // REDISMODULE_CTX_FLAGS_MULTI is supported\n       } else{\n             // REDISMODULE_CTX_FLAGS_MULTI is not supported\n       }\n\n<span id=\"RedisModule_GetKeyspaceNotificationFlagsAll\"></span>"},{"id":"redismodule_getkeyspacenotificationflagsall","title":"`RedisModule_GetKeyspaceNotificationFlagsAll`","role":"content","text":"int RedisModule_GetKeyspaceNotificationFlagsAll(void);\n\n**Available since:** 6.0.9\n\n\nReturns the full KeyspaceNotification mask, using the return value\nthe module can check if a certain set of flags are supported\nby the redis server version in use.\nExample:\n\n       int supportedFlags = RedisModule_GetKeyspaceNotificationFlagsAll();\n       if (supportedFlags & REDISMODULE_NOTIFY_LOADED) {\n             // REDISMODULE_NOTIFY_LOADED is supported\n       } else{\n             // REDISMODULE_NOTIFY_LOADED is not supported\n       }\n\n<span id=\"RedisModule_GetServerVersion\"></span>"},{"id":"redismodule_getserverversion","title":"`RedisModule_GetServerVersion`","role":"content","text":"int RedisModule_GetServerVersion(void);\n\n**Available since:** 6.0.9\n\n\nReturn the redis version in format of 0x00MMmmpp.\nExample for 6.0.7 the return value will be 0x00060007.\n\n<span id=\"RedisModule_GetTypeMethodVersion\"></span>"},{"id":"redismodule_gettypemethodversion","title":"`RedisModule_GetTypeMethodVersion`","role":"content","text":"int RedisModule_GetTypeMethodVersion(void);\n\n**Available since:** 6.2.0\n\n\nReturn the current redis-server runtime value of `REDISMODULE_TYPE_METHOD_VERSION`.\nYou can use that when calling [`RedisModule_CreateDataType`](#RedisModule_CreateDataType) to know which fields of\n`RedisModuleTypeMethods` are gonna be supported and which will be ignored.\n\n<span id=\"RedisModule_ModuleTypeReplaceValue\"></span>"},{"id":"redismodule_moduletypereplacevalue","title":"`RedisModule_ModuleTypeReplaceValue`","role":"content","text":"int RedisModule_ModuleTypeReplaceValue(RedisModuleKey *key,\n                                           moduleType *mt,\n                                           void *new_value,\n                                           void **old_value);\n\n**Available since:** 6.0.0\n\nReplace the value assigned to a module type.\n\nThe key must be open for writing, have an existing value, and have a moduleType\nthat matches the one specified by the caller.\n\nUnlike [`RedisModule_ModuleTypeSetValue()`](#RedisModule_ModuleTypeSetValue) which will free the old value, this function\nsimply swaps the old value with the new value.\n\nThe function returns `REDISMODULE_OK` on success, `REDISMODULE_ERR` on errors\nsuch as:\n\n1. Key is not opened for writing.\n2. Key is not a module data type key.\n3. Key is a module datatype other than 'mt'.\n\nIf `old_value` is non-NULL, the old value is returned by reference.\n\n<span id=\"RedisModule_GetCommandKeysWithFlags\"></span>"},{"id":"redismodule_getcommandkeyswithflags","title":"`RedisModule_GetCommandKeysWithFlags`","role":"content","text":"int *RedisModule_GetCommandKeysWithFlags(RedisModuleCtx *ctx,\n                                             RedisModuleString **argv,\n                                             int argc,\n                                             int *num_keys,\n                                             int **out_flags);\n\n**Available since:** 7.0.0\n\nFor a specified command, parse its arguments and return an array that\ncontains the indexes of all key name arguments. This function is\nessentially a more efficient way to do `COMMAND GETKEYS`.\n\nThe `out_flags` argument is optional, and can be set to NULL.\nWhen provided it is filled with `REDISMODULE_CMD_KEY_` flags in matching\nindexes with the key indexes of the returned array.\n\nA NULL return value indicates the specified command has no keys, or\nan error condition. Error conditions are indicated by setting errno\nas follows:\n\n* ENOENT: Specified command does not exist.\n* EINVAL: Invalid command arity specified.\n\nNOTE: The returned array is not a Redis Module object so it does not\nget automatically freed even when auto-memory is used. The caller\nmust explicitly call [`RedisModule_Free()`](#RedisModule_Free) to free it, same as the `out_flags` pointer if\nused.\n\n<span id=\"RedisModule_GetCommandKeys\"></span>"},{"id":"redismodule_getcommandkeys","title":"`RedisModule_GetCommandKeys`","role":"content","text":"int *RedisModule_GetCommandKeys(RedisModuleCtx *ctx,\n                                    RedisModuleString **argv,\n                                    int argc,\n                                    int *num_keys);\n\n**Available since:** 6.0.9\n\nIdentical to [`RedisModule_GetCommandKeysWithFlags`](#RedisModule_GetCommandKeysWithFlags) when flags are not needed.\n\n<span id=\"RedisModule_GetCurrentCommandName\"></span>"},{"id":"redismodule_getcurrentcommandname","title":"`RedisModule_GetCurrentCommandName`","role":"content","text":"const char *RedisModule_GetCurrentCommandName(RedisModuleCtx *ctx);\n\n**Available since:** 6.2.5\n\nReturn the name of the command currently running\n\n<span id=\"section-defrag-api\"></span>"},{"id":"defrag-api","title":"Defrag API","role":"content","text":"<span id=\"RedisModule_RegisterDefragFunc\"></span>"},{"id":"redismodule_registerdefragfunc","title":"`RedisModule_RegisterDefragFunc`","role":"content","text":"int RedisModule_RegisterDefragFunc(RedisModuleCtx *ctx,\n                                       RedisModuleDefragFunc cb);\n\n**Available since:** 6.2.0\n\nRegister a defrag callback for global data, i.e. anything that the module\nmay allocate that is not tied to a specific data type.\n\n<span id=\"RedisModule_RegisterDefragFunc2\"></span>"},{"id":"redismodule_registerdefragfunc2","title":"`RedisModule_RegisterDefragFunc2`","role":"content","text":"int RedisModule_RegisterDefragFunc2(RedisModuleCtx *ctx,\n                                        RedisModuleDefragFunc2 cb);\n\n**Available since:** 8.0.0\n\nRegister a defrag callback for global data, i.e. anything that the module\nmay allocate that is not tied to a specific data type.\nThis is a more advanced version of [`RedisModule_RegisterDefragFunc`](#RedisModule_RegisterDefragFunc), in that it takes\na callbacks that has a return value, and can use [`RedisModule_DefragShouldStop`](#RedisModule_DefragShouldStop)\nin and indicate that it should be called again later, or is it done (returned 0).\n\n<span id=\"RedisModule_RegisterDefragCallbacks\"></span>"},{"id":"redismodule_registerdefragcallbacks","title":"`RedisModule_RegisterDefragCallbacks`","role":"content","text":"int RedisModule_RegisterDefragCallbacks(RedisModuleCtx *ctx,\n                                            RedisModuleDefragFunc start,\n                                            RedisModuleDefragFunc end);\n\n**Available since:** 8.0.0\n\nRegister a defrag callbacks that will be called when defrag operation starts and ends.\n\nThe callbacks are the same as [`RedisModule_RegisterDefragFunc`](#RedisModule_RegisterDefragFunc) but the user\ncan also assume the callbacks are called when the defrag operation starts and ends.\n\n<span id=\"RedisModule_DefragShouldStop\"></span>"},{"id":"redismodule_defragshouldstop","title":"`RedisModule_DefragShouldStop`","role":"content","text":"int RedisModule_DefragShouldStop(RedisModuleDefragCtx *ctx);\n\n**Available since:** 6.2.0\n\nWhen the data type defrag callback iterates complex structures, this\nfunction should be called periodically. A zero (false) return\nindicates the callback may continue its work. A non-zero value (true)\nindicates it should stop.\n\nWhen stopped, the callback may use [`RedisModule_DefragCursorSet()`](#RedisModule_DefragCursorSet) to store its\nposition so it can later use [`RedisModule_DefragCursorGet()`](#RedisModule_DefragCursorGet) to resume defragging.\n\nWhen stopped and more work is left to be done, the callback should\nreturn 1. Otherwise, it should return 0.\n\n<span id=\"RedisModule_DefragCursorSet\"></span>"},{"id":"redismodule_defragcursorset","title":"`RedisModule_DefragCursorSet`","role":"content","text":"int RedisModule_DefragCursorSet(RedisModuleDefragCtx *ctx,\n                                    unsigned long cursor);\n\n**Available since:** 6.2.0\n\nStore an arbitrary cursor value for future re-use.\n\nThis should only be called if [`RedisModule_DefragShouldStop()`](#RedisModule_DefragShouldStop) has returned a non-zero\nvalue and the defrag callback is about to exit without fully iterating its\ndata type.\n\nThis behavior is reserved to cases where late defrag is performed. Late\ndefrag is selected for keys that implement the `free_effort` callback and\nreturn a `free_effort` value that is larger than the defrag\n'active-defrag-max-scan-fields' configuration directive.\n\nSmaller keys, keys that do not implement `free_effort` or the global\ndefrag callback are not called in late-defrag mode. In those cases, a\ncall to this function will return `REDISMODULE_ERR`.\n\nThe cursor may be used by the module to represent some progress into the\nmodule's data type. Modules may also store additional cursor-related\ninformation locally and use the cursor as a flag that indicates when\ntraversal of a new key begins. This is possible because the API makes\na guarantee that concurrent defragmentation of multiple keys will\nnot be performed.\n\n<span id=\"RedisModule_DefragCursorGet\"></span>"},{"id":"redismodule_defragcursorget","title":"`RedisModule_DefragCursorGet`","role":"content","text":"int RedisModule_DefragCursorGet(RedisModuleDefragCtx *ctx,\n                                    unsigned long *cursor);\n\n**Available since:** 6.2.0\n\nFetch a cursor value that has been previously stored using [`RedisModule_DefragCursorSet()`](#RedisModule_DefragCursorSet).\n\nIf not called for a late defrag operation, `REDISMODULE_ERR` will be returned and\nthe cursor should be ignored. See [`RedisModule_DefragCursorSet()`](#RedisModule_DefragCursorSet) for more details on\ndefrag cursors.\n\n<span id=\"RedisModule_DefragAlloc\"></span>"},{"id":"redismodule_defragalloc","title":"`RedisModule_DefragAlloc`","role":"content","text":"void *RedisModule_DefragAlloc(RedisModuleDefragCtx *ctx, void *ptr);\n\n**Available since:** 6.2.0\n\nDefrag a memory allocation previously allocated by [`RedisModule_Alloc`](#RedisModule_Alloc), [`RedisModule_Calloc`](#RedisModule_Calloc), etc.\nThe defragmentation process involves allocating a new memory block and copying\nthe contents to it, like `realloc()`.\n\nIf defragmentation was not necessary, NULL is returned and the operation has\nno other effect.\n\nIf a non-NULL value is returned, the caller should use the new pointer instead\nof the old one and update any reference to the old pointer, which must not\nbe used again.\n\n<span id=\"RedisModule_DefragAllocRaw\"></span>"},{"id":"redismodule_defragallocraw","title":"`RedisModule_DefragAllocRaw`","role":"content","text":"void *RedisModule_DefragAllocRaw(RedisModuleDefragCtx *ctx, size_t size);\n\n**Available since:** 8.0.0\n\nAllocate memory for defrag purposes\n\nOn the common cases user simply want to reallocate a pointer with a single\nowner. For such usecase [`RedisModule_DefragAlloc`](#RedisModule_DefragAlloc) is enough. But on some usecases the user\nmight want to replace a pointer with multiple owners in different keys.\nIn such case, an in place replacement can not work because the other key still\nkeep a pointer to the old value. \n\n[`RedisModule_DefragAllocRaw`](#RedisModule_DefragAllocRaw) and [`RedisModule_DefragFreeRaw`](#RedisModule_DefragFreeRaw) allows to control when the memory\nfor defrag purposes will be allocated and when it will be freed,\nallow to support more complex defrag usecases.\n\n<span id=\"RedisModule_DefragFreeRaw\"></span>"},{"id":"redismodule_defragfreeraw","title":"`RedisModule_DefragFreeRaw`","role":"content","text":"void RedisModule_DefragFreeRaw(RedisModuleDefragCtx *ctx, void *ptr);\n\n**Available since:** 8.0.0\n\nFree memory for defrag purposes\n\nSee [`RedisModule_DefragAllocRaw`](#RedisModule_DefragAllocRaw) for more information.\n\n<span id=\"RedisModule_DefragRedisModuleString\"></span>"},{"id":"redismodule_defragredismodulestring","title":"`RedisModule_DefragRedisModuleString`","role":"content","text":"RedisModuleString *RedisModule_DefragRedisModuleString(RedisModuleDefragCtx *ctx,\n                                                           RedisModuleString *str);\n\n**Available since:** 6.2.0\n\nDefrag a `RedisModuleString` previously allocated by [`RedisModule_Alloc`](#RedisModule_Alloc), [`RedisModule_Calloc`](#RedisModule_Calloc), etc.\nSee [`RedisModule_DefragAlloc()`](#RedisModule_DefragAlloc) for more information on how the defragmentation process\nworks.\n\nNOTE: It is only possible to defrag strings that have a single reference.\nTypically this means strings retained with [`RedisModule_RetainString`](#RedisModule_RetainString) or [`RedisModule_HoldString`](#RedisModule_HoldString)\nmay not be defragmentable. One exception is command argvs which, if retained\nby the module, will end up with a single reference (because the reference\non the Redis side is dropped as soon as the command callback returns).\n\n<span id=\"RedisModule_DefragRedisModuleDict\"></span>"},{"id":"redismodule_defragredismoduledict","title":"`RedisModule_DefragRedisModuleDict`","role":"content","text":"RedisModuleDict *RedisModule_DefragRedisModuleDict(RedisModuleDefragCtx *ctx,\n                                                       RedisModuleDict *dict,\n                                                       RedisModuleDefragDictValueCallback valueCB,\n                                                       RedisModuleString **seekTo);\n\n**Available since:** 8.0.0\n\nDefragment a Redis Module Dictionary by scanning its contents and calling a value\ncallback for each value.\n\nThe callback gets the current value in the dict, and should update newptr to the new pointer,\nif the value was re-allocated to a different address. The callback also gets the key name just as a reference.\nThe callback returns 0 when defrag is complete for this node, 1 when node needs more work.\n\nThe API can work incrementally by accepting a seek position to continue from, and\nreturning the next position to seek to on the next call (or return NULL when the iteration is completed).\n\nThis API returns a new dict if it was re-allocated to a new address (will only\nbe attempted when *seekTo is NULL on entry).\n\n<span id=\"RedisModule_GetKeyNameFromDefragCtx\"></span>"},{"id":"redismodule_getkeynamefromdefragctx","title":"`RedisModule_GetKeyNameFromDefragCtx`","role":"content","text":"const RedisModuleString *RedisModule_GetKeyNameFromDefragCtx(RedisModuleDefragCtx *ctx);\n\n**Available since:** 7.0.0\n\nReturns the name of the key currently being processed.\nThere is no guarantee that the key name is always available, so this may return NULL.\n\n<span id=\"RedisModule_GetDbIdFromDefragCtx\"></span>"},{"id":"redismodule_getdbidfromdefragctx","title":"`RedisModule_GetDbIdFromDefragCtx`","role":"content","text":"int RedisModule_GetDbIdFromDefragCtx(RedisModuleDefragCtx *ctx);\n\n**Available since:** 7.0.0\n\nReturns the database id of the key currently being processed.\nThere is no guarantee that this info is always available, so this may return -1.\n\n<span id=\"section-function-index\"></span>"},{"id":"function-index","title":"Function index","role":"content","text":"* [`RedisModule_ACLAddLogEntry`](#RedisModule_ACLAddLogEntry)\n* [`RedisModule_ACLAddLogEntryByUserName`](#RedisModule_ACLAddLogEntryByUserName)\n* [`RedisModule_ACLCheckChannelPermissions`](#RedisModule_ACLCheckChannelPermissions)\n* [`RedisModule_ACLCheckCommandPermissions`](#RedisModule_ACLCheckCommandPermissions)\n* [`RedisModule_ACLCheckKeyPermissions`](#RedisModule_ACLCheckKeyPermissions)\n* [`RedisModule_ACLCheckKeyPrefixPermissions`](#RedisModule_ACLCheckKeyPrefixPermissions)\n* [`RedisModule_AbortBlock`](#RedisModule_AbortBlock)\n* [`RedisModule_AddACLCategory`](#RedisModule_AddACLCategory)\n* [`RedisModule_AddPostNotificationJob`](#RedisModule_AddPostNotificationJob)\n* [`RedisModule_AddPostNotificationJobForKey`](#RedisModule_AddPostNotificationJobForKey)\n* [`RedisModule_Alloc`](#RedisModule_Alloc)\n* [`RedisModule_AuthenticateClientWithACLUser`](#RedisModule_AuthenticateClientWithACLUser)\n* [`RedisModule_AuthenticateClientWithUser`](#RedisModule_AuthenticateClientWithUser)\n* [`RedisModule_AutoMemory`](#RedisModule_AutoMemory)\n* [`RedisModule_AvoidReplicaTraffic`](#RedisModule_AvoidReplicaTraffic)\n* [`RedisModule_BlockClient`](#RedisModule_BlockClient)\n* [`RedisModule_BlockClientGetPrivateData`](#RedisModule_BlockClientGetPrivateData)\n* [`RedisModule_BlockClientOnAuth`](#RedisModule_BlockClientOnAuth)\n* [`RedisModule_BlockClientOnKeys`](#RedisModule_BlockClientOnKeys)\n* [`RedisModule_BlockClientOnKeysWithFlags`](#RedisModule_BlockClientOnKeysWithFlags)\n* [`RedisModule_BlockClientSetPrivateData`](#RedisModule_BlockClientSetPrivateData)\n* [`RedisModule_BlockedClientDisconnected`](#RedisModule_BlockedClientDisconnected)\n* [`RedisModule_BlockedClientMeasureTimeEnd`](#RedisModule_BlockedClientMeasureTimeEnd)\n* [`RedisModule_BlockedClientMeasureTimeStart`](#RedisModule_BlockedClientMeasureTimeStart)\n* [`RedisModule_CachedMicroseconds`](#RedisModule_CachedMicroseconds)\n* [`RedisModule_Call`](#RedisModule_Call)\n* [`RedisModule_CallReplyArrayElement`](#RedisModule_CallReplyArrayElement)\n* [`RedisModule_CallReplyAttribute`](#RedisModule_CallReplyAttribute)\n* [`RedisModule_CallReplyAttributeElement`](#RedisModule_CallReplyAttributeElement)\n* [`RedisModule_CallReplyBigNumber`](#RedisModule_CallReplyBigNumber)\n* [`RedisModule_CallReplyBool`](#RedisModule_CallReplyBool)\n* [`RedisModule_CallReplyDouble`](#RedisModule_CallReplyDouble)\n* [`RedisModule_CallReplyInteger`](#RedisModule_CallReplyInteger)\n* [`RedisModule_CallReplyLength`](#RedisModule_CallReplyLength)\n* [`RedisModule_CallReplyMapElement`](#RedisModule_CallReplyMapElement)\n* [`RedisModule_CallReplyPromiseAbort`](#RedisModule_CallReplyPromiseAbort)\n* [`RedisModule_CallReplyPromiseSetUnblockHandler`](#RedisModule_CallReplyPromiseSetUnblockHandler)\n* [`RedisModule_CallReplyProto`](#RedisModule_CallReplyProto)\n* [`RedisModule_CallReplySetElement`](#RedisModule_CallReplySetElement)\n* [`RedisModule_CallReplyStringPtr`](#RedisModule_CallReplyStringPtr)\n* [`RedisModule_CallReplyType`](#RedisModule_CallReplyType)\n* [`RedisModule_CallReplyVerbatim`](#RedisModule_CallReplyVerbatim)\n* [`RedisModule_Calloc`](#RedisModule_Calloc)\n* [`RedisModule_ChannelAtPosWithFlags`](#RedisModule_ChannelAtPosWithFlags)\n* [`RedisModule_CloseKey`](#RedisModule_CloseKey)\n* [`RedisModule_ClusterCanAccessKeysInSlot`](#RedisModule_ClusterCanAccessKeysInSlot)\n* [`RedisModule_ClusterCanonicalKeyNameInSlot`](#RedisModule_ClusterCanonicalKeyNameInSlot)\n* [`RedisModule_ClusterDisableTrim`](#RedisModule_ClusterDisableTrim)\n* [`RedisModule_ClusterEnableTrim`](#RedisModule_ClusterEnableTrim)\n* [`RedisModule_ClusterFreeSlotRanges`](#RedisModule_ClusterFreeSlotRanges)\n* [`RedisModule_ClusterGetLocalSlotRanges`](#RedisModule_ClusterGetLocalSlotRanges)\n* [`RedisModule_ClusterKeySlot`](#RedisModule_ClusterKeySlot)\n* [`RedisModule_ClusterKeySlotC`](#RedisModule_ClusterKeySlotC)\n* [`RedisModule_ClusterPropagateForSlotMigration`](#RedisModule_ClusterPropagateForSlotMigration)\n* [`RedisModule_CommandFilterArgDelete`](#RedisModule_CommandFilterArgDelete)\n* [`RedisModule_CommandFilterArgGet`](#RedisModule_CommandFilterArgGet)\n* [`RedisModule_CommandFilterArgInsert`](#RedisModule_CommandFilterArgInsert)\n* [`RedisModule_CommandFilterArgReplace`](#RedisModule_CommandFilterArgReplace)\n* [`RedisModule_CommandFilterArgsCount`](#RedisModule_CommandFilterArgsCount)\n* [`RedisModule_CommandFilterGetClientId`](#RedisModule_CommandFilterGetClientId)\n* [`RedisModule_ConfigGet`](#RedisModule_ConfigGet)\n* [`RedisModule_ConfigGetBool`](#RedisModule_ConfigGetBool)\n* [`RedisModule_ConfigGetEnum`](#RedisModule_ConfigGetEnum)\n* [`RedisModule_ConfigGetNumeric`](#RedisModule_ConfigGetNumeric)\n* [`RedisModule_ConfigGetType`](#RedisModule_ConfigGetType)\n* [`RedisModule_ConfigIteratorCreate`](#RedisModule_ConfigIteratorCreate)\n* [`RedisModule_ConfigIteratorNext`](#RedisModule_ConfigIteratorNext)\n* [`RedisModule_ConfigIteratorRelease`](#RedisModule_ConfigIteratorRelease)\n* [`RedisModule_ConfigSet`](#RedisModule_ConfigSet)\n* [`RedisModule_ConfigSetBool`](#RedisModule_ConfigSetBool)\n* [`RedisModule_ConfigSetEnum`](#RedisModule_ConfigSetEnum)\n* [`RedisModule_ConfigSetNumeric`](#RedisModule_ConfigSetNumeric)\n* [`RedisModule_CreateCommand`](#RedisModule_CreateCommand)\n* [`RedisModule_CreateDataType`](#RedisModule_CreateDataType)\n* [`RedisModule_CreateDict`](#RedisModule_CreateDict)\n* [`RedisModule_CreateKeyMetaClass`](#RedisModule_CreateKeyMetaClass)\n* [`RedisModule_CreateModuleUser`](#RedisModule_CreateModuleUser)\n* [`RedisModule_CreateString`](#RedisModule_CreateString)\n* [`RedisModule_CreateStringFromCallReply`](#RedisModule_CreateStringFromCallReply)\n* [`RedisModule_CreateStringFromDouble`](#RedisModule_CreateStringFromDouble)\n* [`RedisModule_CreateStringFromLongDouble`](#RedisModule_CreateStringFromLongDouble)\n* [`RedisModule_CreateStringFromLongLong`](#RedisModule_CreateStringFromLongLong)\n* [`RedisModule_CreateStringFromStreamID`](#RedisModule_CreateStringFromStreamID)\n* [`RedisModule_CreateStringFromString`](#RedisModule_CreateStringFromString)\n* [`RedisModule_CreateStringFromULongLong`](#RedisModule_CreateStringFromULongLong)\n* [`RedisModule_CreateStringPrintf`](#RedisModule_CreateStringPrintf)\n* [`RedisModule_CreateSubcommand`](#RedisModule_CreateSubcommand)\n* [`RedisModule_CreateTimer`](#RedisModule_CreateTimer)\n* [`RedisModule_DbSize`](#RedisModule_DbSize)\n* [`RedisModule_DeauthenticateAndCloseClient`](#RedisModule_DeauthenticateAndCloseClient)\n* [`RedisModule_DefragAlloc`](#RedisModule_DefragAlloc)\n* [`RedisModule_DefragAllocRaw`](#RedisModule_DefragAllocRaw)\n* [`RedisModule_DefragCursorGet`](#RedisModule_DefragCursorGet)\n* [`RedisModule_DefragCursorSet`](#RedisModule_DefragCursorSet)\n* [`RedisModule_DefragFreeRaw`](#RedisModule_DefragFreeRaw)\n* [`RedisModule_DefragRedisModuleDict`](#RedisModule_DefragRedisModuleDict)\n* [`RedisModule_DefragRedisModuleString`](#RedisModule_DefragRedisModuleString)\n* [`RedisModule_DefragShouldStop`](#RedisModule_DefragShouldStop)\n* [`RedisModule_DeleteKey`](#RedisModule_DeleteKey)\n* [`RedisModule_DictCompare`](#RedisModule_DictCompare)\n* [`RedisModule_DictCompareC`](#RedisModule_DictCompareC)\n* [`RedisModule_DictDel`](#RedisModule_DictDel)\n* [`RedisModule_DictDelC`](#RedisModule_DictDelC)\n* [`RedisModule_DictGet`](#RedisModule_DictGet)\n* [`RedisModule_DictGetC`](#RedisModule_DictGetC)\n* [`RedisModule_DictIteratorReseek`](#RedisModule_DictIteratorReseek)\n* [`RedisModule_DictIteratorReseekC`](#RedisModule_DictIteratorReseekC)\n* [`RedisModule_DictIteratorStart`](#RedisModule_DictIteratorStart)\n* [`RedisModule_DictIteratorStartC`](#RedisModule_DictIteratorStartC)\n* [`RedisModule_DictIteratorStop`](#RedisModule_DictIteratorStop)\n* [`RedisModule_DictNext`](#RedisModule_DictNext)\n* [`RedisModule_DictNextC`](#RedisModule_DictNextC)\n* [`RedisModule_DictPrev`](#RedisModule_DictPrev)\n* [`RedisModule_DictPrevC`](#RedisModule_DictPrevC)\n* [`RedisModule_DictReplace`](#RedisModule_DictReplace)\n* [`RedisModule_DictReplaceC`](#RedisModule_DictReplaceC)\n* [`RedisModule_DictSet`](#RedisModule_DictSet)\n* [`RedisModule_DictSetC`](#RedisModule_DictSetC)\n* [`RedisModule_DictSize`](#RedisModule_DictSize)\n* [`RedisModule_DigestAddLongLong`](#RedisModule_DigestAddLongLong)\n* [`RedisModule_DigestAddStringBuffer`](#RedisModule_DigestAddStringBuffer)\n* [`RedisModule_DigestEndSequence`](#RedisModule_DigestEndSequence)\n* [`RedisModule_EmitAOF`](#RedisModule_EmitAOF)\n* [`RedisModule_EventLoopAdd`](#RedisModule_EventLoopAdd)\n* [`RedisModule_EventLoopAddOneShot`](#RedisModule_EventLoopAddOneShot)\n* [`RedisModule_EventLoopDel`](#RedisModule_EventLoopDel)\n* [`RedisModule_ExitFromChild`](#RedisModule_ExitFromChild)\n* [`RedisModule_ExportSharedAPI`](#RedisModule_ExportSharedAPI)\n* [`RedisModule_Fork`](#RedisModule_Fork)\n* [`RedisModule_Free`](#RedisModule_Free)\n* [`RedisModule_FreeCallReply`](#RedisModule_FreeCallReply)\n* [`RedisModule_FreeClusterNodesList`](#RedisModule_FreeClusterNodesList)\n* [`RedisModule_FreeDict`](#RedisModule_FreeDict)\n* [`RedisModule_FreeModuleUser`](#RedisModule_FreeModuleUser)\n* [`RedisModule_FreeServerInfo`](#RedisModule_FreeServerInfo)\n* [`RedisModule_FreeString`](#RedisModule_FreeString)\n* [`RedisModule_FreeThreadSafeContext`](#RedisModule_FreeThreadSafeContext)\n* [`RedisModule_GetAbsExpire`](#RedisModule_GetAbsExpire)\n* [`RedisModule_GetBlockedClientHandle`](#RedisModule_GetBlockedClientHandle)\n* [`RedisModule_GetBlockedClientPrivateData`](#RedisModule_GetBlockedClientPrivateData)\n* [`RedisModule_GetBlockedClientReadyKey`](#RedisModule_GetBlockedClientReadyKey)\n* [`RedisModule_GetClientCertificate`](#RedisModule_GetClientCertificate)\n* [`RedisModule_GetClientId`](#RedisModule_GetClientId)\n* [`RedisModule_GetClientInfoById`](#RedisModule_GetClientInfoById)\n* [`RedisModule_GetClientNameById`](#RedisModule_GetClientNameById)\n* [`RedisModule_GetClientUserNameById`](#RedisModule_GetClientUserNameById)\n* [`RedisModule_GetClusterNodeInfo`](#RedisModule_GetClusterNodeInfo)\n* [`RedisModule_GetClusterNodeSlotRanges`](#RedisModule_GetClusterNodeSlotRanges)\n* [`RedisModule_GetClusterNodesList`](#RedisModule_GetClusterNodesList)\n* [`RedisModule_GetClusterSize`](#RedisModule_GetClusterSize)\n* [`RedisModule_GetCommand`](#RedisModule_GetCommand)\n* [`RedisModule_GetCommandKeys`](#RedisModule_GetCommandKeys)\n* [`RedisModule_GetCommandKeysWithFlags`](#RedisModule_GetCommandKeysWithFlags)\n* [`RedisModule_GetContextFlags`](#RedisModule_GetContextFlags)\n* [`RedisModule_GetContextFlagsAll`](#RedisModule_GetContextFlagsAll)\n* [`RedisModule_GetContextUser`](#RedisModule_GetContextUser)\n* [`RedisModule_GetCurrentCommandName`](#RedisModule_GetCurrentCommandName)\n* [`RedisModule_GetCurrentUserName`](#RedisModule_GetCurrentUserName)\n* [`RedisModule_GetDbIdFromDefragCtx`](#RedisModule_GetDbIdFromDefragCtx)\n* [`RedisModule_GetDbIdFromDigest`](#RedisModule_GetDbIdFromDigest)\n* [`RedisModule_GetDbIdFromIO`](#RedisModule_GetDbIdFromIO)\n* [`RedisModule_GetDbIdFromModuleKey`](#RedisModule_GetDbIdFromModuleKey)\n* [`RedisModule_GetDbIdFromOptCtx`](#RedisModule_GetDbIdFromOptCtx)\n* [`RedisModule_GetDetachedThreadSafeContext`](#RedisModule_GetDetachedThreadSafeContext)\n* [`RedisModule_GetExpire`](#RedisModule_GetExpire)\n* [`RedisModule_GetInternalSecret`](#RedisModule_GetInternalSecret)\n* [`RedisModule_GetKeyMeta`](#RedisModule_GetKeyMeta)\n* [`RedisModule_GetKeyNameFromDefragCtx`](#RedisModule_GetKeyNameFromDefragCtx)\n* [`RedisModule_GetKeyNameFromDigest`](#RedisModule_GetKeyNameFromDigest)\n* [`RedisModule_GetKeyNameFromIO`](#RedisModule_GetKeyNameFromIO)\n* [`RedisModule_GetKeyNameFromModuleKey`](#RedisModule_GetKeyNameFromModuleKey)\n* [`RedisModule_GetKeyNameFromOptCtx`](#RedisModule_GetKeyNameFromOptCtx)\n* [`RedisModule_GetKeyspaceNotificationFlagsAll`](#RedisModule_GetKeyspaceNotificationFlagsAll)\n* [`RedisModule_GetLFU`](#RedisModule_GetLFU)\n* [`RedisModule_GetLRU`](#RedisModule_GetLRU)\n* [`RedisModule_GetModuleOptionsAll`](#RedisModule_GetModuleOptionsAll)\n* [`RedisModule_GetModuleUserACLString`](#RedisModule_GetModuleUserACLString)\n* [`RedisModule_GetModuleUserFromUserName`](#RedisModule_GetModuleUserFromUserName)\n* [`RedisModule_GetMyClusterID`](#RedisModule_GetMyClusterID)\n* [`RedisModule_GetNotifyKeyspaceEvents`](#RedisModule_GetNotifyKeyspaceEvents)\n* [`RedisModule_GetOpenKeyModesAll`](#RedisModule_GetOpenKeyModesAll)\n* [`RedisModule_GetRandomBytes`](#RedisModule_GetRandomBytes)\n* [`RedisModule_GetRandomHexChars`](#RedisModule_GetRandomHexChars)\n* [`RedisModule_GetSelectedDb`](#RedisModule_GetSelectedDb)\n* [`RedisModule_GetServerInfo`](#RedisModule_GetServerInfo)\n* [`RedisModule_GetServerVersion`](#RedisModule_GetServerVersion)\n* [`RedisModule_GetSharedAPI`](#RedisModule_GetSharedAPI)\n* [`RedisModule_GetThreadSafeContext`](#RedisModule_GetThreadSafeContext)\n* [`RedisModule_GetTimerInfo`](#RedisModule_GetTimerInfo)\n* [`RedisModule_GetToDbIdFromOptCtx`](#RedisModule_GetToDbIdFromOptCtx)\n* [`RedisModule_GetToKeyNameFromOptCtx`](#RedisModule_GetToKeyNameFromOptCtx)\n* [`RedisModule_GetTypeMethodVersion`](#RedisModule_GetTypeMethodVersion)\n* [`RedisModule_GetUsedMemoryRatio`](#RedisModule_GetUsedMemoryRatio)\n* [`RedisModule_HashFieldMinExpire`](#RedisModule_HashFieldMinExpire)\n* [`RedisModule_HashGet`](#RedisModule_HashGet)\n* [`RedisModule_HashSet`](#RedisModule_HashSet)\n* [`RedisModule_HoldString`](#RedisModule_HoldString)\n* [`RedisModule_InfoAddFieldCString`](#RedisModule_InfoAddFieldCString)\n* [`RedisModule_InfoAddFieldDouble`](#RedisModule_InfoAddFieldDouble)\n* [`RedisModule_InfoAddFieldLongLong`](#RedisModule_InfoAddFieldLongLong)\n* [`RedisModule_InfoAddFieldString`](#RedisModule_InfoAddFieldString)\n* [`RedisModule_InfoAddFieldULongLong`](#RedisModule_InfoAddFieldULongLong)\n* [`RedisModule_InfoAddSection`](#RedisModule_InfoAddSection)\n* [`RedisModule_InfoBeginDictField`](#RedisModule_InfoBeginDictField)\n* [`RedisModule_InfoEndDictField`](#RedisModule_InfoEndDictField)\n* [`RedisModule_IsBlockedReplyRequest`](#RedisModule_IsBlockedReplyRequest)\n* [`RedisModule_IsBlockedTimeoutRequest`](#RedisModule_IsBlockedTimeoutRequest)\n* [`RedisModule_IsChannelsPositionRequest`](#RedisModule_IsChannelsPositionRequest)\n* [`RedisModule_IsIOError`](#RedisModule_IsIOError)\n* [`RedisModule_IsKeysPositionRequest`](#RedisModule_IsKeysPositionRequest)\n* [`RedisModule_IsModuleNameBusy`](#RedisModule_IsModuleNameBusy)\n* [`RedisModule_IsSubEventSupported`](#RedisModule_IsSubEventSupported)\n* [`RedisModule_KeyAtPos`](#RedisModule_KeyAtPos)\n* [`RedisModule_KeyAtPosWithFlags`](#RedisModule_KeyAtPosWithFlags)\n* [`RedisModule_KeyExists`](#RedisModule_KeyExists)\n* [`RedisModule_KeyType`](#RedisModule_KeyType)\n* [`RedisModule_KillForkChild`](#RedisModule_KillForkChild)\n* [`RedisModule_LatencyAddSample`](#RedisModule_LatencyAddSample)\n* [`RedisModule_ListDelete`](#RedisModule_ListDelete)\n* [`RedisModule_ListGet`](#RedisModule_ListGet)\n* [`RedisModule_ListInsert`](#RedisModule_ListInsert)\n* [`RedisModule_ListPop`](#RedisModule_ListPop)\n* [`RedisModule_ListPush`](#RedisModule_ListPush)\n* [`RedisModule_ListSet`](#RedisModule_ListSet)\n* [`RedisModule_LoadConfigs`](#RedisModule_LoadConfigs)\n* [`RedisModule_LoadDataTypeFromString`](#RedisModule_LoadDataTypeFromString)\n* [`RedisModule_LoadDataTypeFromStringEncver`](#RedisModule_LoadDataTypeFromStringEncver)\n* [`RedisModule_LoadDefaultConfigs`](#RedisModule_LoadDefaultConfigs)\n* [`RedisModule_LoadDouble`](#RedisModule_LoadDouble)\n* [`RedisModule_LoadFloat`](#RedisModule_LoadFloat)\n* [`RedisModule_LoadLongDouble`](#RedisModule_LoadLongDouble)\n* [`RedisModule_LoadSigned`](#RedisModule_LoadSigned)\n* [`RedisModule_LoadString`](#RedisModule_LoadString)\n* [`RedisModule_LoadStringBuffer`](#RedisModule_LoadStringBuffer)\n* [`RedisModule_LoadUnsigned`](#RedisModule_LoadUnsigned)\n* [`RedisModule_Log`](#RedisModule_Log)\n* [`RedisModule_LogIOError`](#RedisModule_LogIOError)\n* [`RedisModule_MallocSize`](#RedisModule_MallocSize)\n* [`RedisModule_MallocSizeDict`](#RedisModule_MallocSizeDict)\n* [`RedisModule_MallocSizeString`](#RedisModule_MallocSizeString)\n* [`RedisModule_MallocUsableSize`](#RedisModule_MallocUsableSize)\n* [`RedisModule_Microseconds`](#RedisModule_Microseconds)\n* [`RedisModule_Milliseconds`](#RedisModule_Milliseconds)\n* [`RedisModule_ModuleTypeGetType`](#RedisModule_ModuleTypeGetType)\n* [`RedisModule_ModuleTypeGetValue`](#RedisModule_ModuleTypeGetValue)\n* [`RedisModule_ModuleTypeReplaceValue`](#RedisModule_ModuleTypeReplaceValue)\n* [`RedisModule_ModuleTypeSetValue`](#RedisModule_ModuleTypeSetValue)\n* [`RedisModule_MonotonicMicroseconds`](#RedisModule_MonotonicMicroseconds)\n* [`RedisModule_NotifyKeyspaceEvent`](#RedisModule_NotifyKeyspaceEvent)\n* [`RedisModule_NotifyKeyspaceEventWithSubkeys`](#RedisModule_NotifyKeyspaceEventWithSubkeys)\n* [`RedisModule_OpenKey`](#RedisModule_OpenKey)\n* [`RedisModule_PoolAlloc`](#RedisModule_PoolAlloc)\n* [`RedisModule_PublishMessage`](#RedisModule_PublishMessage)\n* [`RedisModule_PublishMessageShard`](#RedisModule_PublishMessageShard)\n* [`RedisModule_RandomKey`](#RedisModule_RandomKey)\n* [`RedisModule_RdbLoad`](#RedisModule_RdbLoad)\n* [`RedisModule_RdbSave`](#RedisModule_RdbSave)\n* [`RedisModule_RdbStreamCreateFromFile`](#RedisModule_RdbStreamCreateFromFile)\n* [`RedisModule_RdbStreamFree`](#RedisModule_RdbStreamFree)\n* [`RedisModule_Realloc`](#RedisModule_Realloc)\n* [`RedisModule_RedactClientCommandArgument`](#RedisModule_RedactClientCommandArgument)\n* [`RedisModule_RegisterAuthCallback`](#RedisModule_RegisterAuthCallback)\n* [`RedisModule_RegisterBoolConfig`](#RedisModule_RegisterBoolConfig)\n* [`RedisModule_RegisterClusterMessageReceiver`](#RedisModule_RegisterClusterMessageReceiver)\n* [`RedisModule_RegisterCommandFilter`](#RedisModule_RegisterCommandFilter)\n* [`RedisModule_RegisterDefragCallbacks`](#RedisModule_RegisterDefragCallbacks)\n* [`RedisModule_RegisterDefragFunc`](#RedisModule_RegisterDefragFunc)\n* [`RedisModule_RegisterDefragFunc2`](#RedisModule_RegisterDefragFunc2)\n* [`RedisModule_RegisterEnumConfig`](#RedisModule_RegisterEnumConfig)\n* [`RedisModule_RegisterInfoFunc`](#RedisModule_RegisterInfoFunc)\n* [`RedisModule_RegisterNumericConfig`](#RedisModule_RegisterNumericConfig)\n* [`RedisModule_RegisterStringConfig`](#RedisModule_RegisterStringConfig)\n* [`RedisModule_ReleaseKeyMetaClass`](#RedisModule_ReleaseKeyMetaClass)\n* [`RedisModule_Replicate`](#RedisModule_Replicate)\n* [`RedisModule_ReplicateVerbatim`](#RedisModule_ReplicateVerbatim)\n* [`RedisModule_ReplySetArrayLength`](#RedisModule_ReplySetArrayLength)\n* [`RedisModule_ReplySetAttributeLength`](#RedisModule_ReplySetAttributeLength)\n* [`RedisModule_ReplySetMapLength`](#RedisModule_ReplySetMapLength)\n* [`RedisModule_ReplySetSetLength`](#RedisModule_ReplySetSetLength)\n* [`RedisModule_ReplyWithArray`](#RedisModule_ReplyWithArray)\n* [`RedisModule_ReplyWithAttribute`](#RedisModule_ReplyWithAttribute)\n* [`RedisModule_ReplyWithBigNumber`](#RedisModule_ReplyWithBigNumber)\n* [`RedisModule_ReplyWithBool`](#RedisModule_ReplyWithBool)\n* [`RedisModule_ReplyWithCString`](#RedisModule_ReplyWithCString)\n* [`RedisModule_ReplyWithCallReply`](#RedisModule_ReplyWithCallReply)\n* [`RedisModule_ReplyWithDouble`](#RedisModule_ReplyWithDouble)\n* [`RedisModule_ReplyWithEmptyArray`](#RedisModule_ReplyWithEmptyArray)\n* [`RedisModule_ReplyWithEmptyString`](#RedisModule_ReplyWithEmptyString)\n* [`RedisModule_ReplyWithError`](#RedisModule_ReplyWithError)\n* [`RedisModule_ReplyWithErrorFormat`](#RedisModule_ReplyWithErrorFormat)\n* [`RedisModule_ReplyWithLongDouble`](#RedisModule_ReplyWithLongDouble)\n* [`RedisModule_ReplyWithLongLong`](#RedisModule_ReplyWithLongLong)\n* [`RedisModule_ReplyWithMap`](#RedisModule_ReplyWithMap)\n* [`RedisModule_ReplyWithNull`](#RedisModule_ReplyWithNull)\n* [`RedisModule_ReplyWithNullArray`](#RedisModule_ReplyWithNullArray)\n* [`RedisModule_ReplyWithSet`](#RedisModule_ReplyWithSet)\n* [`RedisModule_ReplyWithSimpleString`](#RedisModule_ReplyWithSimpleString)\n* [`RedisModule_ReplyWithString`](#RedisModule_ReplyWithString)\n* [`RedisModule_ReplyWithStringBuffer`](#RedisModule_ReplyWithStringBuffer)\n* [`RedisModule_ReplyWithVerbatimString`](#RedisModule_ReplyWithVerbatimString)\n* [`RedisModule_ReplyWithVerbatimStringType`](#RedisModule_ReplyWithVerbatimStringType)\n* [`RedisModule_ResetDataset`](#RedisModule_ResetDataset)\n* [`RedisModule_RetainString`](#RedisModule_RetainString)\n* [`RedisModule_SaveDataTypeToString`](#RedisModule_SaveDataTypeToString)\n* [`RedisModule_SaveDouble`](#RedisModule_SaveDouble)\n* [`RedisModule_SaveFloat`](#RedisModule_SaveFloat)\n* [`RedisModule_SaveLongDouble`](#RedisModule_SaveLongDouble)\n* [`RedisModule_SaveSigned`](#RedisModule_SaveSigned)\n* [`RedisModule_SaveString`](#RedisModule_SaveString)\n* [`RedisModule_SaveStringBuffer`](#RedisModule_SaveStringBuffer)\n* [`RedisModule_SaveUnsigned`](#RedisModule_SaveUnsigned)\n* [`RedisModule_Scan`](#RedisModule_Scan)\n* [`RedisModule_ScanCursorCreate`](#RedisModule_ScanCursorCreate)\n* [`RedisModule_ScanCursorDestroy`](#RedisModule_ScanCursorDestroy)\n* [`RedisModule_ScanCursorRestart`](#RedisModule_ScanCursorRestart)\n* [`RedisModule_ScanKey`](#RedisModule_ScanKey)\n* [`RedisModule_SelectDb`](#RedisModule_SelectDb)\n* [`RedisModule_SendChildHeartbeat`](#RedisModule_SendChildHeartbeat)\n* [`RedisModule_SendClusterMessage`](#RedisModule_SendClusterMessage)\n* [`RedisModule_ServerInfoGetField`](#RedisModule_ServerInfoGetField)\n* [`RedisModule_ServerInfoGetFieldC`](#RedisModule_ServerInfoGetFieldC)\n* [`RedisModule_ServerInfoGetFieldDouble`](#RedisModule_ServerInfoGetFieldDouble)\n* [`RedisModule_ServerInfoGetFieldSigned`](#RedisModule_ServerInfoGetFieldSigned)\n* [`RedisModule_ServerInfoGetFieldUnsigned`](#RedisModule_ServerInfoGetFieldUnsigned)\n* [`RedisModule_SetAbsExpire`](#RedisModule_SetAbsExpire)\n* [`RedisModule_SetClientNameById`](#RedisModule_SetClientNameById)\n* [`RedisModule_SetClusterFlags`](#RedisModule_SetClusterFlags)\n* [`RedisModule_SetCommandACLCategories`](#RedisModule_SetCommandACLCategories)\n* [`RedisModule_SetCommandInfo`](#RedisModule_SetCommandInfo)\n* [`RedisModule_SetContextUser`](#RedisModule_SetContextUser)\n* [`RedisModule_SetDisconnectCallback`](#RedisModule_SetDisconnectCallback)\n* [`RedisModule_SetExpire`](#RedisModule_SetExpire)\n* [`RedisModule_SetKeyMeta`](#RedisModule_SetKeyMeta)\n* [`RedisModule_SetLFU`](#RedisModule_SetLFU)\n* [`RedisModule_SetLRU`](#RedisModule_SetLRU)\n* [`RedisModule_SetModuleOptions`](#RedisModule_SetModuleOptions)\n* [`RedisModule_SetModuleUserACL`](#RedisModule_SetModuleUserACL)\n* [`RedisModule_SetModuleUserACLString`](#RedisModule_SetModuleUserACLString)\n* [`RedisModule_SignalKeyAsReady`](#RedisModule_SignalKeyAsReady)\n* [`RedisModule_SignalModifiedKey`](#RedisModule_SignalModifiedKey)\n* [`RedisModule_StopTimer`](#RedisModule_StopTimer)\n* [`RedisModule_Strdup`](#RedisModule_Strdup)\n* [`RedisModule_StreamAdd`](#RedisModule_StreamAdd)\n* [`RedisModule_StreamDelete`](#RedisModule_StreamDelete)\n* [`RedisModule_StreamIteratorDelete`](#RedisModule_StreamIteratorDelete)\n* [`RedisModule_StreamIteratorNextField`](#RedisModule_StreamIteratorNextField)\n* [`RedisModule_StreamIteratorNextID`](#RedisModule_StreamIteratorNextID)\n* [`RedisModule_StreamIteratorStart`](#RedisModule_StreamIteratorStart)\n* [`RedisModule_StreamIteratorStop`](#RedisModule_StreamIteratorStop)\n* [`RedisModule_StreamTrimByID`](#RedisModule_StreamTrimByID)\n* [`RedisModule_StreamTrimByLength`](#RedisModule_StreamTrimByLength)\n* [`RedisModule_StringAppendBuffer`](#RedisModule_StringAppendBuffer)\n* [`RedisModule_StringCompare`](#RedisModule_StringCompare)\n* [`RedisModule_StringDMA`](#RedisModule_StringDMA)\n* [`RedisModule_StringPtrLen`](#RedisModule_StringPtrLen)\n* [`RedisModule_StringSet`](#RedisModule_StringSet)\n* [`RedisModule_StringToDouble`](#RedisModule_StringToDouble)\n* [`RedisModule_StringToLongDouble`](#RedisModule_StringToLongDouble)\n* [`RedisModule_StringToLongLong`](#RedisModule_StringToLongLong)\n* [`RedisModule_StringToStreamID`](#RedisModule_StringToStreamID)\n* [`RedisModule_StringToULongLong`](#RedisModule_StringToULongLong)\n* [`RedisModule_StringTruncate`](#RedisModule_StringTruncate)\n* [`RedisModule_SubscribeToKeyspaceEvents`](#RedisModule_SubscribeToKeyspaceEvents)\n* [`RedisModule_SubscribeToKeyspaceEventsWithSubkeys`](#RedisModule_SubscribeToKeyspaceEventsWithSubkeys)\n* [`RedisModule_SubscribeToServerEvent`](#RedisModule_SubscribeToServerEvent)\n* [`RedisModule_ThreadSafeContextLock`](#RedisModule_ThreadSafeContextLock)\n* [`RedisModule_ThreadSafeContextTryLock`](#RedisModule_ThreadSafeContextTryLock)\n* [`RedisModule_ThreadSafeContextUnlock`](#RedisModule_ThreadSafeContextUnlock)\n* [`RedisModule_TrimStringAllocation`](#RedisModule_TrimStringAllocation)\n* [`RedisModule_TryAlloc`](#RedisModule_TryAlloc)\n* [`RedisModule_TryCalloc`](#RedisModule_TryCalloc)\n* [`RedisModule_TryRealloc`](#RedisModule_TryRealloc)\n* [`RedisModule_UnblockClient`](#RedisModule_UnblockClient)\n* [`RedisModule_UnlinkKey`](#RedisModule_UnlinkKey)\n* [`RedisModule_UnregisterCommandFilter`](#RedisModule_UnregisterCommandFilter)\n* [`RedisModule_UnsubscribeFromKeyspaceEvents`](#RedisModule_UnsubscribeFromKeyspaceEvents)\n* [`RedisModule_UnsubscribeFromKeyspaceEventsWithSubkeys`](#RedisModule_UnsubscribeFromKeyspaceEventsWithSubkeys)\n* [`RedisModule_ValueLength`](#RedisModule_ValueLength)\n* [`RedisModule_WrongArity`](#RedisModule_WrongArity)\n* [`RedisModule_Yield`](#RedisModule_Yield)\n* [`RedisModule_ZsetAdd`](#RedisModule_ZsetAdd)\n* [`RedisModule_ZsetFirstInLexRange`](#RedisModule_ZsetFirstInLexRange)\n* [`RedisModule_ZsetFirstInScoreRange`](#RedisModule_ZsetFirstInScoreRange)\n* [`RedisModule_ZsetIncrby`](#RedisModule_ZsetIncrby)\n* [`RedisModule_ZsetLastInLexRange`](#RedisModule_ZsetLastInLexRange)\n* [`RedisModule_ZsetLastInScoreRange`](#RedisModule_ZsetLastInScoreRange)\n* [`RedisModule_ZsetRangeCurrentElement`](#RedisModule_ZsetRangeCurrentElement)\n* [`RedisModule_ZsetRangeEndReached`](#RedisModule_ZsetRangeEndReached)\n* [`RedisModule_ZsetRangeNext`](#RedisModule_ZsetRangeNext)\n* [`RedisModule_ZsetRangePrev`](#RedisModule_ZsetRangePrev)\n* [`RedisModule_ZsetRangeStop`](#RedisModule_ZsetRangeStop)\n* [`RedisModule_ZsetRem`](#RedisModule_ZsetRem)\n* [`RedisModule_ZsetScore`](#RedisModule_ZsetScore)\n* [`RedisModule__Assert`](#RedisModule__Assert)"}],"examples":[{"id":"redismodule_configiteratorcreate-ex0","language":"plaintext","code":"// Below is same as [`RedisModule_ConfigIteratorCreate`](#RedisModule_ConfigIteratorCreate)(ctx, NULL)\n`RedisModuleConfigIterator` *iter = [`RedisModule_ConfigIteratorCreate`](#RedisModule_ConfigIteratorCreate)(ctx, \"*\");\nconst char *config_name = NULL;\nwhile ((`config_name` = [`RedisModule_ConfigIteratorNext`](#RedisModule_ConfigIteratorNext)(iter)) != NULL) {\n    RedisModuleString *value = NULL;\n    if (RedisModule_ConfigGet(ctx, config_name, &value) == REDISMODULE_OK) {\n        // Do something with `value`...\n        RedisModule_FreeString(ctx, value);\n    }\n}\n[`RedisModule_ConfigIteratorRelease`](#RedisModule_ConfigIteratorRelease)(ctx, iter);\n\n// Or optionally one can check the type to get the config value directly\n// via the appropriate API in case performance is of consideration\niter = [`RedisModule_ConfigIteratorCreate`](#RedisModule_ConfigIteratorCreate)(ctx, \"*\");\nwhile ((`config_name` = [`RedisModule_ConfigIteratorNext`](#RedisModule_ConfigIteratorNext)(iter)) != NULL) {\n    RedisModuleConfigType type = RedisModule_ConfigGetType(config_name);\n    if (type == REDISMODULE_CONFIG_TYPE_STRING) {\n        RedisModuleString *value;\n        RedisModule_ConfigGet(ctx, config_name, &value);\n        // Do something with `value`...\n        RedisModule_FreeString(ctx, value);\n    } if (type == REDISMODULE_CONFIG_TYPE_NUMERIC) {\n        long long value;\n        RedisModule_ConfigGetNumeric(ctx, config_name, &value);\n        // Do something with `value`...\n    } else if (type == REDISMODULE_CONFIG_TYPE_BOOL) {\n        int value;\n        RedisModule_ConfigGetBool(ctx, config_name, &value);\n        // Do something with `value`...\n    } else if (type == REDISMODULE_CONFIG_TYPE_ENUM) {\n        RedisModuleString *value;\n        RedisModule_ConfigGetEnum(ctx, config_name, &value);\n        // Do something with `value`...\n        RedisModule_Free(value);\n    }\n}\n[`RedisModule_ConfigIteratorRelease`](#RedisModule_ConfigIteratorRelease)(ctx, iter);","section_id":"redismodule_configiteratorcreate"}]}
{"schema_version":2,"id":"develop/reference/modules/modules-blocking-ops","title":"Redis modules and blocking commands","url":"https://redis.io/docs/latest/develop/reference/modules/modules-blocking-ops/","summary":"How to implement blocking commands in a Redis module","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:49:27+01:00","page_type":"content","content_hash":"1149872acd3ebc9f60bc64ca633e34bf8798851811aabe376243398271f4c968","sections":[{"id":"content","title":"Content","role":"content","text":"Redis has a few blocking commands among the built-in set of commands.\nOne of the most used is [`BLPOP`](https://redis.io/docs/latest/commands/blpop) (or the symmetric [`BRPOP`](https://redis.io/docs/latest/commands/brpop)) which blocks\nwaiting for elements arriving in a list.\n\nThe interesting fact about blocking commands is that they do not block\nthe whole server, but just the client calling them. Usually the reason to\nblock is that we expect some external event to happen: this can be\nsome change in the Redis data structures like in the [`BLPOP`](https://redis.io/docs/latest/commands/blpop) case, a\nlong computation happening in a thread, to receive some data from the\nnetwork, and so forth.\n\nRedis modules have the ability to implement blocking commands as well,\nthis documentation shows how the API works and describes a few patterns\nthat can be used in order to model blocking commands.\n\n\nHow blocking and resuming works.\n---\n\n_Note: You may want to check the `helloblock.c` example in the Redis source tree\ninside the `src/modules` directory, for a simple to understand example\non how the blocking API is applied._\n\nIn Redis modules, commands are implemented by callback functions that\nare invoked by the Redis core when the specific command is called\nby the user. Normally the callback terminates its execution sending\nsome reply to the client. Using the following function instead, the\nfunction implementing the module command may request that the client\nis put into the blocked state:\n\n    RedisModuleBlockedClient *RedisModule_BlockClient(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(void*), long long timeout_ms);\n\nThe function returns a `RedisModuleBlockedClient` object, which is later\nused in order to unblock the client. The arguments have the following\nmeaning:\n\n* `ctx` is the command execution context as usually in the rest of the API.\n* `reply_callback` is the callback, having the same prototype of a normal command function, that is called when the client is unblocked in order to return a reply to the client.\n* `timeout_callback` is the callback, having the same prototype of a normal command function that is called when the client reached the `ms` timeout.\n* `free_privdata` is the callback that is called in order to free the private data. Private data is a pointer to some data that is passed between the API used to unblock the client, to the callback that will send the reply to the client. We'll see how this mechanism works later in this document.\n* `ms` is the timeout in milliseconds. When the timeout is reached, the timeout callback is called and the client is automatically aborted.\n\nOnce a client is blocked, it can be unblocked with the following API:\n\n    int RedisModule_UnblockClient(RedisModuleBlockedClient *bc, void *privdata);\n\nThe function takes as argument the blocked client object returned by\nthe previous call to `RedisModule_BlockClient()`, and unblock the client.\nImmediately before the client gets unblocked, the `reply_callback` function\nspecified when the client was blocked is called: this function will\nhave access to the `privdata` pointer used here.\n\nIMPORTANT: The above function is thread safe, and can be called from within\na thread doing some work in order to implement the command that blocked\nthe client.\n\nThe `privdata` data will be freed automatically using the `free_privdata`\ncallback when the client is unblocked. This is useful **since the reply\ncallback may never be called** in case the client timeouts or disconnects\nfrom the server, so it's important that it's up to an external function\nto have the responsibility to free the data passed if needed.\n\nTo better understand how the API works, we can imagine writing a command\nthat blocks a client for one second, and then send as reply \"Hello!\".\n\nNote: arity checks and other non important things are not implemented\nint his command, in order to take the example simple.\n\n    int Example_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv,\n                             int argc)\n    {\n        RedisModuleBlockedClient *bc =\n            RedisModule_BlockClient(ctx,reply_func,timeout_func,NULL,0);\n\n        pthread_t tid;\n        pthread_create(&tid,NULL,threadmain,bc);\n\n        return REDISMODULE_OK;\n    }\n\n    void *threadmain(void *arg) {\n        RedisModuleBlockedClient *bc = arg;\n\n        sleep(1); /* Wait one second and unblock. */\n        RedisModule_UnblockClient(bc,NULL);\n    }\n\nThe above command blocks the client ASAP, spawning a thread that will\nwait a second and will unblock the client. Let's check the reply and\ntimeout callbacks, which are in our case very similar, since they\njust reply the client with a different reply type.\n\n    int reply_func(RedisModuleCtx *ctx, RedisModuleString **argv,\n                   int argc)\n    {\n        return RedisModule_ReplyWithSimpleString(ctx,\"Hello!\");\n    }\n\n    int timeout_func(RedisModuleCtx *ctx, RedisModuleString **argv,\n                   int argc)\n    {\n        return RedisModule_ReplyWithNull(ctx);\n    }\n\nThe reply callback just sends the \"Hello!\" string to the client.\nThe important bit here is that the reply callback is called when the\nclient is unblocked from the thread.\n\nThe timeout command returns `NULL`, as it often happens with actual\nRedis blocking commands timing out.\n\nPassing reply data when unblocking\n---\n\nThe above example is simple to understand but lacks an important\nreal world aspect of an actual blocking command implementation: often\nthe reply function will need to know what to reply to the client,\nand this information is often provided as the client is unblocked.\n\nWe could modify the above example so that the thread generates a\nrandom number after waiting one second. You can think at it as an\nactually expansive operation of some kind. Then this random number\ncan be passed to the reply function so that we return it to the command\ncaller. In order to make this working, we modify the functions as follow:\n\n    void *threadmain(void *arg) {\n        RedisModuleBlockedClient *bc = arg;\n\n        sleep(1); /* Wait one second and unblock. */\n\n        long *mynumber = RedisModule_Alloc(sizeof(long));\n        *mynumber = rand();\n        RedisModule_UnblockClient(bc,mynumber);\n    }\n\nAs you can see, now the unblocking call is passing some private data,\nthat is the `mynumber` pointer, to the reply callback. In order to\nobtain this private data, the reply callback will use the following\nfunction:\n\n    void *RedisModule_GetBlockedClientPrivateData(RedisModuleCtx *ctx);\n\nSo our reply callback is modified like that:\n\n    int reply_func(RedisModuleCtx *ctx, RedisModuleString **argv,\n                   int argc)\n    {\n        long *mynumber = RedisModule_GetBlockedClientPrivateData(ctx);\n        /* IMPORTANT: don't free mynumber here, but in the\n         * free privdata callback. */\n        return RedisModule_ReplyWithLongLong(ctx,mynumber);\n    }\n\nNote that we also need to pass a `free_privdata` function when blocking\nthe client with `RedisModule_BlockClient()`, since the allocated\nlong value must be freed. Our callback will look like the following:\n\n    void free_privdata(void *privdata) {\n        RedisModule_Free(privdata);\n    }\n\nNOTE: It is important to stress that the private data is best freed in the\n`free_privdata` callback because the reply function may not be called\nif the client disconnects or timeout.\n\nAlso note that the private data is also accessible from the timeout\ncallback, always using the `GetBlockedClientPrivateData()` API.\n\nAborting the blocking of a client\n---\n\nOne problem that sometimes arises is that we need to allocate resources\nin order to implement the non blocking command. So we block the client,\nthen, for example, try to create a thread, but the thread creation function\nreturns an error. What to do in such a condition in order to recover? We\ndon't want to take the client blocked, nor we want to call `UnblockClient()`\nbecause this will trigger the reply callback to be called.\n\nIn this case the best thing to do is to use the following function:\n\n    int RedisModule_AbortBlock(RedisModuleBlockedClient *bc);\n\nPractically this is how to use it:\n\n    int Example_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv,\n                             int argc)\n    {\n        RedisModuleBlockedClient *bc =\n            RedisModule_BlockClient(ctx,reply_func,timeout_func,NULL,0);\n\n        pthread_t tid;\n        if (pthread_create(&tid,NULL,threadmain,bc) != 0) {\n            RedisModule_AbortBlock(bc);\n            RedisModule_ReplyWithError(ctx,\"Sorry can't create a thread\");\n        }\n\n        return REDISMODULE_OK;\n    }\n\nThe client will be unblocked but the reply callback will not be called.\n\nImplementing the command, reply and timeout callback using a single function\n---\n\nThe following functions can be used in order to implement the reply and\ncallback with the same function that implements the primary command\nfunction:\n\n    int RedisModule_IsBlockedReplyRequest(RedisModuleCtx *ctx);\n    int RedisModule_IsBlockedTimeoutRequest(RedisModuleCtx *ctx);\n\nSo I could rewrite the example command without using a separated\nreply and timeout callback:\n\n    int Example_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv,\n                             int argc)\n    {\n        if (RedisModule_IsBlockedReplyRequest(ctx)) {\n            long *mynumber = RedisModule_GetBlockedClientPrivateData(ctx);\n            return RedisModule_ReplyWithLongLong(ctx,mynumber);\n        } else if (RedisModule_IsBlockedTimeoutRequest) {\n            return RedisModule_ReplyWithNull(ctx);\n        }\n\n        RedisModuleBlockedClient *bc =\n            RedisModule_BlockClient(ctx,reply_func,timeout_func,NULL,0);\n\n        pthread_t tid;\n        if (pthread_create(&tid,NULL,threadmain,bc) != 0) {\n            RedisModule_AbortBlock(bc);\n            RedisModule_ReplyWithError(ctx,\"Sorry can't create a thread\");\n        }\n\n        return REDISMODULE_OK;\n    }\n\nFunctionally is the same but there are people that will prefer the less\nverbose implementation that concentrates most of the command logic in a\nsingle function.\n\nWorking on copies of data inside a thread\n---\n\nAn interesting pattern in order to work with threads implementing the\nslow part of a command, is to work with a copy of the data, so that\nwhile some operation is performed in a key, the user continues to see\nthe old version. However when the thread terminated its work, the\nrepresentations are swapped and the new, processed version, is used.\n\nAn example of this approach is the\n[Neural Redis module](https://github.com/antirez/neural-redis)\nwhere neural networks are trained in different threads while the\nuser can still execute and inspect their older versions.\n\nFuture work\n---\n\nAn API is work in progress right now in order to allow Redis modules APIs\nto be called in a safe way from threads, so that the threaded command\ncan access the data space and do incremental operations.\n\nThere is no ETA for this feature but it may appear in the course of the\nRedis 4.0 release at some point."}],"examples":[]}
{"schema_version":2,"id":"develop/reference/modules/modules-native-types","title":"Modules API for native types","url":"https://redis.io/docs/latest/develop/reference/modules/modules-native-types/","summary":"How to use native types in a Redis module","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:49:27+01:00","page_type":"content","content_hash":"1b3c56daa26d83c9e2d81c964ca62e84823856ceafc98a0ece6ed3ec16333a07","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis modules can access Redis built-in data structures both at high level,\nby calling Redis commands, and at low level, by manipulating the data structures\ndirectly.\n\nBy using these capabilities in order to build new abstractions on top of existing\nRedis data structures, or by using strings DMA in order to encode modules\ndata structures into Redis strings, it is possible to create modules that\n*feel like* they are exporting new data types. However, for more complex\nproblems, this is not enough, and the implementation of new data structures\ninside the module is needed.\n\nWe call the ability of Redis modules to implement new data structures that\nfeel like native Redis ones **native types support**. This document describes\nthe API exported by the Redis modules system in order to create new data\nstructures and handle the serialization in RDB files, the rewriting process\nin AOF, the type reporting via the [`TYPE`](https://redis.io/docs/latest/commands/type) command, and so forth."},{"id":"overview-of-native-types","title":"Overview of native types","role":"overview","text":"A module exporting a native type is composed of the following main parts:\n\n* The implementation of some kind of new data structure and of commands operating on the new data structure.\n* A set of callbacks that handle: RDB saving, RDB loading, AOF rewriting, releasing of a value associated with a key, calculation of a value digest (hash) to be used with the `DEBUG DIGEST` command.\n* A 9 characters name that is unique to each module native data type.\n* An encoding version, used to persist into RDB files a module-specific data version, so that a module will be able to load older representations from RDB files.\n\nWhile to handle RDB loading, saving and AOF rewriting may look complex as a first glance, the modules API provide very high level function for handling all this, without requiring the user to handle read/write errors, so in practical terms, writing a new data structure for Redis is a simple task.\n\nA **very easy** to understand but complete example of native type implementation\nis available inside the Redis distribution in the `/modules/hellotype.c` file.\nThe reader is encouraged to read the documentation by looking at this example\nimplementation to see how things are applied in the practice."},{"id":"register-a-new-data-type","title":"Register a new data type","role":"content","text":"In order to register a new native type into the Redis core, the module needs\nto declare a global variable that will hold a reference to the data type.\nThe API to register the data type will return a data type reference that will\nbe stored in the global variable.\n\n    static RedisModuleType *MyType;\n    #define MYTYPE_ENCODING_VERSION 0\n\n    int RedisModule_OnLoad(RedisModuleCtx *ctx) {\n\tRedisModuleTypeMethods tm = {\n\t    .version = REDISMODULE_TYPE_METHOD_VERSION,\n\t    .rdb_load = MyTypeRDBLoad,\n\t    .rdb_save = MyTypeRDBSave,\n\t    .aof_rewrite = MyTypeAOFRewrite,\n\t    .free = MyTypeFree\n\t};\n\n        MyType = RedisModule_CreateDataType(ctx, \"MyType-AZ\",\n\t\tMYTYPE_ENCODING_VERSION, &tm);\n        if (MyType == NULL) return REDISMODULE_ERR;\n    }\n\nAs you can see from the example above, a single API call is needed in order to\nregister the new type. However a number of function pointers are passed as\narguments. Certain are optionals while some are mandatory. The above set\nof methods *must* be passed, while `.digest` and `.mem_usage` are optional\nand are currently not actually supported by the modules internals, so for\nnow you can just ignore them.\n\nThe `ctx` argument is the context that we receive in the `OnLoad` function.\nThe type `name` is a 9 character name in the character set that includes\nfrom `A-Z`, `a-z`, `0-9`, plus the underscore `_` and minus `-` characters.\n\nNote that **this name must be unique** for each data type in the Redis\necosystem, so be creative, use both lower-case and upper case if it makes\nsense, and try to use the convention of mixing the type name with the name\nof the author of the module, to create a 9 character unique name.\n\n**NOTE:** It is very important that the name is exactly 9 chars or the\nregistration of the type will fail. Read more to understand why.\n\nFor example if I'm building a *b-tree* data structure and my name is *antirez*\nI'll call my type **btree1-az**. The name, converted to a 64 bit integer,\nis stored inside the RDB file when saving the type, and will be used when the\nRDB data is loaded in order to resolve what module can load the data. If Redis\nfinds no matching module, the integer is converted back to a name in order to\nprovide some clue to the user about what module is missing in order to load\nthe data.\n\nThe type name is also used as a reply for the [`TYPE`](https://redis.io/docs/latest/commands/type) command when called\nwith a key holding the registered type.\n\nThe `encver` argument is the encoding version used by the module to store data\ninside the RDB file. For example I can start with an encoding version of 0,\nbut later when I release version 2.0 of my module, I can switch encoding to\nsomething better. The new module will register with an encoding version of 1,\nso when it saves new RDB files, the new version will be stored on disk. However\nwhen loading RDB files, the module `rdb_load` method will be called even if\nthere is data found for a different encoding version (and the encoding version\nis passed as argument to `rdb_load`), so that the module can still load old\nRDB files.\n\nThe last argument is a structure used in order to pass the type methods to the\nregistration function: `rdb_load`, `rdb_save`, `aof_rewrite`, `digest` and\n`free` and `mem_usage` are all callbacks with the following prototypes and uses:\n\n    typedef void *(*RedisModuleTypeLoadFunc)(RedisModuleIO *rdb, int encver);\n    typedef void (*RedisModuleTypeSaveFunc)(RedisModuleIO *rdb, void *value);\n    typedef void (*RedisModuleTypeRewriteFunc)(RedisModuleIO *aof, RedisModuleString *key, void *value);\n    typedef size_t (*RedisModuleTypeMemUsageFunc)(void *value);\n    typedef void (*RedisModuleTypeDigestFunc)(RedisModuleDigest *digest, void *value);\n    typedef void (*RedisModuleTypeFreeFunc)(void *value);\n\n* `rdb_load` is called when loading data from the RDB file. It loads data in the same format as `rdb_save` produces.\n* `rdb_save` is called when saving data to the RDB file.\n* `aof_rewrite` is called when the AOF is being rewritten, and the module needs to tell Redis what is the sequence of commands to recreate the content of a given key.\n* `digest` is called when `DEBUG DIGEST` is executed and a key holding this module type is found. Currently this is not yet implemented so the function ca be left empty.\n* `mem_usage` is called when the [`MEMORY`](https://redis.io/docs/latest/commands/memory) command asks for the total memory consumed by a specific key, and is used in order to get the amount of bytes used by the module value.\n* `free` is called when a key with the module native type is deleted via [`DEL`](https://redis.io/docs/latest/commands/del) or in any other mean, in order to let the module reclaim the memory associated with such a value."},{"id":"why-module-types-require-nine-character-names","title":"Why module types require nine character names","role":"content","text":"When Redis persists to RDB files, modules specific data types require to\nbe persisted as well. Now RDB files are sequences of key-value pairs\nlike the following:\n\n    [1 byte type] [key] [a type specific value]\n\nThe 1 byte type identifies strings, lists, sets, and so forth. In the case\nof modules data, it is set to a special value of `module data`, but of\ncourse this is not enough, we need the information needed to link a specific\nvalue with a specific module type that is able to load and handle it.\n\nSo when we save a `type specific value` about a module, we prefix it with\na 64 bit integer. 64 bits is large enough to store the information needed\nin order to lookup the module that can handle that specific type, but is\nshort enough that we can prefix each module value we store inside the RDB\nwithout making the final RDB file too big. At the same time, this solution\nof prefixing the value with a 64 bit *signature* does not require to do\nstrange things like defining in the RDB header a list of modules specific\ntypes. Everything is pretty simple.\n\nSo, what you can store in 64 bits in order to identify a given module in\na reliable way? Well if you build a character set of 64 symbols, you can\neasily store 9 characters of 6 bits, and you are left with 10 bits, that\nare used in order to store the *encoding version* of the type, so that\nthe same type can evolve in the future and provide a different and more\nefficient or updated serialization format for RDB files.\n\nSo the 64 bit prefix stored before each module value is like the following:\n\n    6|6|6|6|6|6|6|6|6|10\n\nThe first 9 elements are 6-bits characters, the final 10 bits is the\nencoding version.\n\nWhen the RDB file is loaded back, it reads the 64 bit value, masks the final\n10 bits, and searches for a matching module in the modules types cache.\nWhen a matching one is found, the method to load the RDB file value is called\nwith the 10 bits encoding version as argument, so that the module knows\nwhat version of the data layout to load, if it can support multiple versions.\n\nNow the interesting thing about all this is that, if instead the module type\ncannot be resolved, since there is no loaded module having this signature,\nwe can convert back the 64 bit value into a 9 characters name, and print\nan error to the user that includes the module type name! So that she or he\nimmediately realizes what's wrong."},{"id":"set-and-get-keys","title":"Set and get keys","role":"content","text":"After registering our new data type in the `RedisModule_OnLoad()` function,\nwe also need to be able to set Redis keys having as value our native type.\n\nThis normally happens in the context of commands that write data to a key.\nThe native types API allow to set and get keys to module native data types,\nand to test if a given key is already associated to a value of a specific data\ntype.\n\nThe API uses the normal modules `RedisModule_OpenKey()` low level key access\ninterface in order to deal with this. This is an example of setting a\nnative type private data structure to a Redis key:\n\n    RedisModuleKey *key = RedisModule_OpenKey(ctx,keyname,REDISMODULE_WRITE);\n    struct some_private_struct *data = createMyDataStructure();\n    RedisModule_ModuleTypeSetValue(key,MyType,data);\n\nThe function `RedisModule_ModuleTypeSetValue()` is used with a key handle open\nfor writing, and gets three arguments: the key handle, the reference to the\nnative type, as obtained during the type registration, and finally a `void*`\npointer that contains the private data implementing the module native type.\n\nNote that Redis has no clues at all about what your data contains. It will\njust call the callbacks you provided during the method registration in order\nto perform operations on the type.\n\nSimilarly we can retrieve the private data from a key using this function:\n\n    struct some_private_struct *data;\n    data = RedisModule_ModuleTypeGetValue(key);\n\nWe can also test for a key to have our native type as value:\n\n    if (RedisModule_ModuleTypeGetType(key) == MyType) {\n        /* ... do something ... */\n    }\n\nHowever for the calls to do the right thing, we need to check if the key\nis empty, if it contains a value of the right kind, and so forth. So\nthe idiomatic code to implement a command writing to our native type\nis along these lines:\n\n    RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],\n        REDISMODULE_READ|REDISMODULE_WRITE);\n    int type = RedisModule_KeyType(key);\n    if (type != REDISMODULE_KEYTYPE_EMPTY &&\n        RedisModule_ModuleTypeGetType(key) != MyType)\n    {\n        return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);\n    }\n\nThen if we successfully verified the key is not of the wrong type, and\nwe are going to write to it, we usually want to create a new data structure if\nthe key is empty, or retrieve the reference to the value associated to the\nkey if there is already one:\n\n    /* Create an empty value object if the key is currently empty. */\n    struct some_private_struct *data;\n    if (type == REDISMODULE_KEYTYPE_EMPTY) {\n        data = createMyDataStructure();\n        RedisModule_ModuleTypeSetValue(key,MyTyke,data);\n    } else {\n        data = RedisModule_ModuleTypeGetValue(key);\n    }\n    /* Do something with 'data'... */"},{"id":"free-method","title":"Free method","role":"content","text":"As already mentioned, when Redis needs to free a key holding a native type\nvalue, it needs help from the module in order to release the memory. This\nis the reason why we pass a `free` callback during the type registration:\n\n    typedef void (*RedisModuleTypeFreeFunc)(void *value);\n\nA trivial implementation of the free method can be something like this,\nassuming our data structure is composed of a single allocation:\n\n    void MyTypeFreeCallback(void *value) {\n        RedisModule_Free(value);\n    }\n\nHowever a more real world one will call some function that performs a more\ncomplex memory reclaiming, by casting the void pointer to some structure\nand freeing all the resources composing the value."},{"id":"rdb-load-and-save-methods","title":"RDB load and save methods","role":"content","text":"The RDB saving and loading callbacks need to create (and load back) a\nrepresentation of the data type on disk. Redis offers a high level API\nthat can automatically store inside the RDB file the following types:\n\n* Unsigned 64 bit integers.\n* Signed 64 bit integers.\n* Doubles.\n* Strings.\n\nIt is up to the module to find a viable representation using the above base\ntypes. However note that while the integer and double values are stored\nand loaded in an architecture and *endianness* agnostic way, if you use\nthe raw string saving API to, for example, save a structure on disk, you\nhave to care those details yourself.\n\nThis is the list of functions performing RDB saving and loading:\n\n    void RedisModule_SaveUnsigned(RedisModuleIO *io, uint64_t value);\n    uint64_t RedisModule_LoadUnsigned(RedisModuleIO *io);\n    void RedisModule_SaveSigned(RedisModuleIO *io, int64_t value);\n    int64_t RedisModule_LoadSigned(RedisModuleIO *io);\n    void RedisModule_SaveString(RedisModuleIO *io, RedisModuleString *s);\n    void RedisModule_SaveStringBuffer(RedisModuleIO *io, const char *str, size_t len);\n    RedisModuleString *RedisModule_LoadString(RedisModuleIO *io);\n    char *RedisModule_LoadStringBuffer(RedisModuleIO *io, size_t *lenptr);\n    void RedisModule_SaveDouble(RedisModuleIO *io, double value);\n    double RedisModule_LoadDouble(RedisModuleIO *io);\n\nThe functions don't require any error checking from the module, that can\nalways assume calls succeed.\n\nAs an example, imagine I've a native type that implements an array of\ndouble values, with the following structure:\n\n    struct double_array {\n        size_t count;\n        double *values;\n    };\n\nMy `rdb_save` method may look like the following:\n\n    void DoubleArrayRDBSave(RedisModuleIO *io, void *ptr) {\n        struct dobule_array *da = ptr;\n        RedisModule_SaveUnsigned(io,da->count);\n        for (size_t j = 0; j < da->count; j++)\n            RedisModule_SaveDouble(io,da->values[j]);\n    }\n\nWhat we did was to store the number of elements followed by each double\nvalue. So when later we'll have to load the structure in the `rdb_load`\nmethod we'll do something like this:\n\n    void *DoubleArrayRDBLoad(RedisModuleIO *io, int encver) {\n        if (encver != DOUBLE_ARRAY_ENC_VER) {\n            /* We should actually log an error here, or try to implement\n               the ability to load older versions of our data structure. */\n            return NULL;\n        }\n\n        struct double_array *da;\n        da = RedisModule_Alloc(sizeof(*da));\n        da->count = RedisModule_LoadUnsigned(io);\n        da->values = RedisModule_Alloc(da->count * sizeof(double));\n        for (size_t j = 0; j < da->count; j++)\n            da->values[j] = RedisModule_LoadDouble(io);\n        return da;\n    }\n\nThe load callback just reconstruct back the data structure from the data\nwe stored in the RDB file.\n\nNote that while there is no error handling on the API that writes and reads\nfrom disk, still the load callback can return NULL on errors in case what\nit reads does not look correct. Redis will just panic in that case."},{"id":"aof-rewriting","title":"AOF rewriting","role":"content","text":"void RedisModule_EmitAOF(RedisModuleIO *io, const char *cmdname, const char *fmt, ...);"},{"id":"allocate-memory","title":"Allocate memory","role":"content","text":"Modules data types should try to use `RedisModule_Alloc()` functions family\nin order to allocate, reallocate and release heap memory used to implement the native data structures (see the other Redis Modules documentation for detailed information).\n\nThis is not just useful in order for Redis to be able to account for the memory used by the module, but there are also more advantages:\n\n* Redis uses the `jemalloc` allocator, that often prevents fragmentation problems that could be caused by using the libc allocator.\n* When loading strings from the RDB file, the native types API is able to return strings allocated directly with `RedisModule_Alloc()`, so that the module can directly link this memory into the data structure representation, avoiding a useless copy of the data.\n\nEven if you are using external libraries implementing your data structures, the\nallocation functions provided by the module API is exactly compatible with\n`malloc()`, `realloc()`, `free()` and `strdup()`, so converting the libraries\nin order to use these functions should be trivial.\n\nIn case you have an external library that uses libc `malloc()`, and you want\nto avoid replacing manually all the calls with the Redis Modules API calls,\nan approach could be to use simple macros in order to replace the libc calls\nwith the Redis API calls. Something like this could work:\n\n    #define malloc RedisModule_Alloc\n    #define realloc RedisModule_Realloc\n    #define free RedisModule_Free\n    #define strdup RedisModule_Strdup\n\nHowever take in mind that mixing libc calls with Redis API calls will result\ninto troubles and crashes, so if you replace calls using macros, you need to\nmake sure that all the calls are correctly replaced, and that the code with\nthe substituted calls will never, for example, attempt to call\n`RedisModule_Free()` with a pointer allocated using libc `malloc()`."}],"examples":[]}
{"schema_version":2,"id":"develop/reference/protocol-spec","title":"Redis serialization protocol specification","url":"https://redis.io/docs/latest/develop/reference/protocol-spec/","summary":"Redis serialization protocol (RESP) is the wire protocol that clients implement","aliases":["/reference/protocol-spec/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-29T18:39:36+01:00","page_type":"content","content_hash":"26374fe764d88d4b40f025fc76d700ae3ab880d558bb0588b709a57b246ca026","sections":[{"id":"overview","title":"Overview","role":"overview","text":"To communicate with the Redis server, Redis clients use a protocol called Redis Serialization Protocol (RESP).\nWhile the protocol was designed specifically for Redis, you can use it for other client-server software projects.\n\nRESP is a compromise among the following considerations:\n\n* Simple to implement.\n* Fast to parse.\n* Human readable.\n\nRESP can serialize different data types including integers, strings, and arrays.\nIt also features an error-specific type.\nA client sends a request to the Redis server as an array of strings.\nThe array's contents are the command and its arguments that the server should execute.\nThe server's reply type is command-specific.\n\nRESP is binary-safe and uses prefixed length to transfer bulk data so it does not require processing bulk data transferred from one process to another.\n\nRESP is the protocol you should implement in your Redis client.\n\n> [!NOTE]\n> The protocol outlined here is used only for client-server communication.\n> [Redis Cluster](https://redis.io/docs/latest/operate/oss_and_stack/reference/cluster-spec) uses a different binary protocol for exchanging messages between nodes."},{"id":"resp-versions","title":"RESP versions","role":"content","text":"Support for the first version of the RESP protocol was introduced in Redis 1.2.\nUsing RESP with Redis 1.2 was optional and had mainly served the purpose of working the kinks out of the protocol.\n\nIn Redis 2.0, the protocol's next version, a.k.a RESP2, became the standard communication method for clients with the Redis server.\n\n[RESP3](https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md) is mostly a superset of RESP2 that mainly aims to make a client author's life a little bit easier.\nRedis 6.0 introduced experimental opt-in support of RESP3's features (excluding streaming strings and streaming aggregates).\nIn addition, the introduction of the [`HELLO`](https://redis.io/docs/latest/commands/hello) command allows clients to handshake and upgrade the connection's protocol version (see [Client handshake](#client-handshake)).\n\nFrom Redis version 7 and forward, both RESP2 and RESP3 clients can invoke all core commands.\nHowever, commands may return differently typed replies for different protocol versions.\nEach command has descriptions of RESP2 and RESP3 return values that you can reference.\n\nFuture versions of Redis may change the default protocol version, but it is unlikely that RESP2 will become entirely deprecated.\nIt is possible, however, that new features in upcoming versions will require the use of RESP3."},{"id":"network-layer","title":"Network layer","role":"content","text":"A client connects to a Redis server by creating a TCP connection to its port (the default is 6379).\n\nWhile RESP is technically non-TCP specific, the protocol is used exclusively with TCP connections (or equivalent stream-oriented connections like Unix sockets) in the context of Redis."},{"id":"request-response-model","title":"Request-Response model","role":"content","text":"The Redis server accepts commands composed of different arguments.\nThen, the server processes the command and sends the reply back to the client.\n\nThis is the simplest model possible; however, there are some exceptions:\n\n* Redis requests can be [pipelined](#multiple-commands-and-pipelining).\n  Pipelining enables clients to send multiple commands at once and wait for replies later.\n* When a RESP2 connection subscribes to a [Pub/Sub](https://redis.io/docs/latest/develop/pubsub) channel, the protocol changes semantics and becomes a *push* protocol.\n  The client no longer requires sending commands because the server will automatically send new messages to the client (for the channels the client is subscribed to) as soon as they are received.\n* The [`MONITOR`](https://redis.io/docs/latest/commands/monitor) command.\n  Invoking the [`MONITOR`](https://redis.io/docs/latest/commands/monitor) command switches the connection to an ad-hoc push mode.\n  The protocol of this mode is not specified but is obvious to parse.\n* [Protected mode](https://redis.io/docs/latest/operate/oss_and_stack/management/security#protected-mode).\n  Connections opened from a non-loopback address to a Redis while in protected mode are denied and terminated by the server.\n  Before terminating the connection, Redis unconditionally sends a `-DENIED` reply, regardless of whether the client writes to the socket.\n* The [RESP3 Push type](#resp3-pushes).\n  As the name suggests, a push type allows the server to send out-of-band data to the connection.\n  The server may push data at any time, and the data isn't necessarily related to specific commands executed by the client.\n  \nExcluding these exceptions, the Redis protocol is a simple request-response protocol."},{"id":"resp-protocol-description","title":"RESP protocol description","role":"content","text":"RESP is essentially a serialization protocol that supports several data types.\nIn RESP, the first byte of data determines its type.\n\nRedis generally uses RESP as a [request-response](#request-response-model) protocol in the following way:\n\n* Clients send commands to a Redis server as an [array](#arrays) of [bulk strings](#bulk-strings).\n  The first (and sometimes also the second) bulk string in the array is the command's name.\n  Subsequent elements of the array are the arguments for the command.\n* The server replies with a RESP type.\n  The reply's type is determined by the command's implementation and possibly by the client's protocol version.\n\nRESP is a binary protocol that uses control sequences encoded in standard ASCII.\nThe `A` character, for example, is encoded with the binary byte of value 65.\nSimilarly, the characters CR (`\\r`), LF (`\\n`) and SP (` `) have binary byte values of 13, 10 and 32, respectively.\n\nThe `\\r\\n` (CRLF) is the protocol's _terminator_, which **always** separates its parts.\n\nThe first byte in an RESP-serialized payload always identifies its type.\nSubsequent bytes constitute the type's contents.\n\nWe categorize every RESP data type as either _simple_, _bulk_ or _aggregate_.\n\nSimple types are similar to scalars in programming languages that represent plain literal values. Booleans and Integers are such examples.\n\nRESP strings are either _simple_ or _bulk_.\nSimple strings never contain carriage return (`\\r`) or line feed (`\\n`) characters.\nBulk strings can contain any binary data and may also be referred to as _binary_ or _blob_.\nNote that bulk strings may be further encoded and decoded, e.g. with a wide multi-byte encoding, by the client.\n\nAggregates, such as Arrays and Maps, can have varying numbers of sub-elements and nesting levels.\n\nThe following table summarizes the RESP data types that Redis supports:\n\n| RESP data type | Minimal protocol version | Category | First byte |\n| --- | --- | --- | --- |\n| [Simple strings](#simple-strings) | RESP2 | Simple | `+` |\n| [Simple Errors](#simple-errors) | RESP2 | Simple | `-` |\n| [Integers](#integers) | RESP2 | Simple | `:` |\n| [Bulk strings](#bulk-strings) | RESP2 | Bulk | `$` |\n| [Null bulk strings](#null-bulk-strings) | RESP2 | Bulk | `$-1\\r\\n` |\n| [Arrays](#arrays) | RESP2 | Aggregate | `*` |\n| [Nulls](#nulls) | RESP3 | Simple | `_` |\n| [Booleans](#booleans) | RESP3 | Simple | `#` |\n| [Doubles](#doubles) | RESP3 | Simple | `,` |\n| [Big numbers](#big-numbers) | RESP3 | Simple | `(` |\n| [Bulk errors](#bulk-errors) | RESP3 | Bulk | `!` |\n| [Verbatim strings](#verbatim-strings) | RESP3 | Bulk | `=` |\n| [Maps](#maps) | RESP3 | Aggregate | `%` |\n| [Attributes](#attributes) | RESP3 | Aggregate | <code>&#124;</code> |\n| [Sets](#sets) | RESP3 | Aggregate | `~` |\n| [Pushes](#pushes) | RESP3 | Aggregate | `>` |\n\nRESP3 also defines two _streamed_ encodings for payloads whose length isn't known when the transfer begins: [streamed strings](#streamed-strings) and [streamed aggregated data types](#streamed-aggregated-data-types).\nThese are alternative encodings of the types in the table rather than types of their own, so the table doesn't list them.\n\n<a name=\"simple-string-reply\"></a>"},{"id":"simple-strings","title":"Simple strings","role":"content","text":"Simple strings are encoded as a plus (`+`) character, followed by a string.\nThe string mustn't contain a CR (`\\r`) or LF (`\\n`) character and is terminated by CRLF (i.e., `\\r\\n`).\n\nSimple strings transmit short, non-binary strings with minimal overhead.\nFor example, many Redis commands reply with just \"OK\" on success.\nThe encoding of this Simple String is the following 5 bytes:\n\n    +OK\\r\\n\n\nWhen Redis replies with a simple string, a client library should return to the caller a string value composed of the first character after the `+` up to the end of the string, excluding the final CRLF bytes.\n\nTo send binary strings, use [bulk strings](#bulk-strings) instead.\n\n<a name=\"error-reply\"></a>"},{"id":"simple-errors","title":"Simple errors","role":"content","text":"RESP has specific data types for errors.\nSimple errors, or simply just errors, are similar to [simple strings](#simple-strings), but their first character is the minus (`-`) character.\nThe difference between simple strings and errors in RESP is that clients should treat errors as exceptions, whereas the string encoded in the error type is the error message itself.\n\nThe basic format is:\n\n    -Error message\\r\\n\n\nRedis replies with an error only when something goes wrong, for example, when you try to operate against the wrong data type, or when the command does not exist.\nThe client should raise an exception when it receives an Error reply.\n\nThe following are examples of error replies:\n\n    -ERR unknown command 'asdf'\n    -WRONGTYPE Operation against a key holding the wrong kind of value\n\nThe first upper-case word after the `-`, up to the first space or newline, represents the kind of error returned.\nThis word is called an _error prefix_.\nNote that the error prefix is a convention used by Redis rather than part of the RESP error type.\n\nFor example, in Redis, `ERR` is a generic error, whereas `WRONGTYPE` is a more specific error that implies that the client attempted an operation against the wrong data type.\nThe error prefix allows the client to understand the type of error returned by the server without checking the exact error message.\n\nA client implementation can return different types of exceptions for various errors, or provide a generic way for trapping errors by directly providing the error name to the caller as a string.\n\nHowever, such a feature should not be considered vital as it is rarely useful. \nAlso, simpler client implementations can return a generic error value, such as `false`.\n\n<a name=\"integer-reply\"></a>"},{"id":"integers","title":"Integers","role":"content","text":"This type is a CRLF-terminated string that represents a signed, base-10, 64-bit integer.\n\nRESP encodes integers in the following way:\n\n    :[<+|->]<value>\\r\\n\n\n* The colon (`:`) as the first byte.\n* An optional plus (`+`) or minus (`-`) as the sign.\n* One or more decimal digits (`0`..`9`) as the integer's unsigned, base-10 value.\n* The CRLF terminator.\n\nFor example, `:0\\r\\n` and `:1000\\r\\n` are integer replies (of zero and one thousand, respectively).\n\nMany Redis commands return RESP integers, including [`INCR`](https://redis.io/docs/latest/commands/incr), [`LLEN`](https://redis.io/docs/latest/commands/llen), and [`LASTSAVE`](https://redis.io/docs/latest/commands/lastsave).\nAn integer, by itself, has no special meaning other than in the context of the command that returned it.\nFor example, it is an incremental number for [`INCR`](https://redis.io/docs/latest/commands/incr), a UNIX timestamp for [`LASTSAVE`](https://redis.io/docs/latest/commands/lastsave), and so forth.\nHowever, the returned integer is guaranteed to be in the range of a signed 64-bit integer.\n\nIn some cases, integers can represent true and false Boolean values.\nFor instance, [`SISMEMBER`](https://redis.io/docs/latest/commands/sismember) returns 1 for true and 0 for false.\n\nOther commands, including [`SADD`](https://redis.io/docs/latest/commands/sadd), [`SREM`](https://redis.io/docs/latest/commands/srem), and [`SETNX`](https://redis.io/docs/latest/commands/setnx), return 1 when the data changes and 0 otherwise.\n\n<a name=\"bulk-string-reply\"></a>"},{"id":"bulk-strings","title":"Bulk strings","role":"content","text":"A bulk string represents a single binary string.\nThe string can be of any size, but by default, Redis limits it to 512 MB (see the `proto-max-bulk-len` configuration directive).\n\nRESP encodes bulk strings in the following way:\n\n    $<length>\\r\\n<data>\\r\\n\n\n* The dollar sign (`$`) as the first byte.\n* One or more decimal digits (`0`..`9`) as the string's length, in bytes, as an unsigned, base-10 value.\n* The CRLF terminator.\n* The data.\n* A final CRLF.\n\nSo the string \"hello\" is encoded as follows:\n\n    $5\\r\\nhello\\r\\n\n\nThe empty string's encoding is:\n\n    $0\\r\\n\\r\\n\n\n<a name=\"nil-reply\"></a>"},{"id":"null-bulk-strings","title":"Null bulk strings","role":"content","text":"Whereas RESP3 has a dedicated data type for [null values](#nulls), RESP2 has no such type.\nInstead, due to historical reasons, the representation of null values in RESP2 is via predetermined forms of the [bulk strings](#bulk-strings) and [arrays](#arrays) types.\n\nThe null bulk string represents a non-existing value.\nThe [`GET`](https://redis.io/docs/latest/commands/get) command returns the Null Bulk String when the target key doesn't exist.\n\nIt is encoded as a bulk string with the length of negative one (-1), like so:\n\n    $-1\\r\\n\n\nA Redis client should return a nil object when the server replies with a null bulk string rather than the empty string.\nFor example, a Ruby library should return `nil` while a C library should return `NULL` (or set a special flag in the reply object).\n\n<a name=\"array-reply\"></a>"},{"id":"arrays","title":"Arrays","role":"content","text":"Clients send commands to the Redis server as RESP arrays.\nSimilarly, some Redis commands that return collections of elements use arrays as their replies. \nAn example is the [`LRANGE`](https://redis.io/docs/latest/commands/lrange) command that returns elements of a list.\n\nRESP Arrays' encoding uses the following format:\n\n    *<number-of-elements>\\r\\n<element-1>...<element-n>\n\n* An asterisk (`*`) as the first byte.\n* One or more decimal digits (`0`..`9`) as the number of elements in the array as an unsigned, base-10 value.\n* The CRLF terminator.\n* An additional RESP type for every element of the array.\n\nSo an empty Array is just the following:\n\n    *0\\r\\n\n\nWhereas the encoding of an array consisting of the two bulk strings \"hello\" and \"world\" is:\n\n    *2\\r\\n$5\\r\\nhello\\r\\n$5\\r\\nworld\\r\\n\n\nAs you can see, after the `*<count>CRLF` part prefixing the array, the other data types that compose the array are concatenated one after the other.\nFor example, an Array of three integers is encoded as follows:\n\n    *3\\r\\n:1\\r\\n:2\\r\\n:3\\r\\n\n\nArrays can contain mixed data types.\nFor instance, the following encoding is of a list of four integers and a bulk string:\n\n    *5\\r\\n\n    :1\\r\\n\n    :2\\r\\n\n    :3\\r\\n\n    :4\\r\\n\n    $5\\r\\n\n    hello\\r\\n\n\n(The raw RESP encoding is split into multiple lines for readability).\n\nThe first line the server sent is `*5\\r\\n`.\nThis numeric value tells the client that five reply types are about to follow it.\nThen, every successive reply constitutes an element in the array.\n\nAll of the aggregate RESP types support nesting.\nFor example, a nested array of two arrays is encoded as follows:\n\n    *2\\r\\n\n    *3\\r\\n\n    :1\\r\\n\n    :2\\r\\n\n    :3\\r\\n\n    *2\\r\\n\n    +Hello\\r\\n\n    -World\\r\\n\n\n(The raw RESP encoding is split into multiple lines for readability).\n\nThe above encodes a two-element array.\nThe first element is an array that, in turn, contains three integers (1, 2, 3).\nThe second element is another array containing a simple string and an error.\n\n> [!NOTE] Multi bulk reply\n> In some places, the RESP Array type may be referred to as _multi bulk_.\n> The two are the same.\n\n<a name=\"nil-array-reply\"></a>\n\n#### Null arrays\nWhereas RESP3 has a dedicated data type for [null values](#nulls), RESP2 has no such type. Instead, due to historical reasons, the representation of null values in RESP2 is via predetermined forms of the [Bulk Strings](#bulk-strings) and [arrays](#arrays) types.\n\nNull arrays exist as an alternative way of representing a null value.\nFor instance, when the [`BLPOP`](https://redis.io/docs/latest/commands/blpop) command times out, it returns a null array.\n\nThe encoding of a null array is that of an array with the length of -1, i.e.:\n\n    *-1\\r\\n\n\nWhen Redis replies with a null array, the client should return a null object rather than an empty array.\nThis is necessary to distinguish between an empty list and a different condition (for instance, the timeout condition of the [`BLPOP`](https://redis.io/docs/latest/commands/blpop) command).\n\n#### Null elements in arrays\nSingle elements of an array may be [null bulk string](#null-bulk-strings).\nThis is used in Redis replies to signal that these elements are missing and not empty strings. This can happen, for example, with the [`SORT`](https://redis.io/docs/latest/commands/sort) command when used with the `GET pattern` option\nif the specified key is missing.\n\nHere's an example of an array reply containing a null element:\n\n    *3\\r\\n\n    $5\\r\\n\n    hello\\r\\n\n    $-1\\r\\n\n    $5\\r\\n\n    world\\r\\n\n\nAbove, the second element is null.\nThe client library should return to its caller something like this:\n\n    [\"hello\",nil,\"world\"]\n\n<a name=\"null-reply\"></a>"},{"id":"nulls","title":"Nulls","role":"content","text":"The null data type represents non-existent values.\n\nNulls' encoding is the underscore (`_`) character, followed by the CRLF terminator (`\\r\\n`).\nHere's Null's raw RESP encoding:\n\n    _\\r\\n\n\n> [!NOTE] Null Bulk String, Null Arrays and Nulls\n> Due to historical reasons, RESP2 features two specially crafted values for representing null values of bulk strings and arrays.\n> This duality has always been a redundancy that added zero semantical value to the protocol itself.\n>\n> The null type, introduced in RESP3, aims to fix this wrong.\n\n<a name=\"boolean-reply\">"},{"id":"booleans","title":"Booleans","role":"content","text":"RESP booleans are encoded as follows:\n\n    #<t|f>\\r\\n\n\n* The octothorpe character (`#`) as the first byte.\n* A `t` character for true values, or an `f` character for false ones.\n* The CRLF terminator.\n\n<a name=\"double-reply\"></a>"},{"id":"doubles","title":"Doubles","role":"content","text":"The Double RESP type encodes a double-precision floating point value.\nDoubles are encoded as follows:\n\n    ,[<+|->]<integral>[.<fractional>][<E|e>[sign]<exponent>]\\r\\n\n\n* The comma character (`,`) as the first byte.\n* An optional plus (`+`) or minus (`-`) as the sign.\n* One or more decimal digits (`0`..`9`) as an unsigned, base-10 integral value.\n* An optional dot (`.`), followed by one or more decimal digits (`0`..`9`) as an unsigned, base-10 fractional value.\n* An optional capital or lowercase letter E (`E` or `e`), followed by an optional plus (`+`) or minus (`-`) as the exponent's sign, ending with one or more decimal digits (`0`..`9`) as an unsigned, base-10 exponent value.\n* The CRLF terminator.\n\nHere's the encoding of the number 1.23:\n\n    ,1.23\\r\\n\n\nBecause the fractional part is optional, the integer value of ten (10) can, therefore, be RESP-encoded both as an integer as well as a double:\n\n    :10\\r\\n\n    ,10\\r\\n\n\nIn such cases, the Redis client should return native integer and double values, respectively, providing that these types are supported by the language of its implementation.\n\nThe positive infinity, negative infinity and NaN values are encoded as follows:\n\n    ,inf\\r\\n\n    ,-inf\\r\\n\n    ,nan\\r\\n\n\n<a name=\"big-number-reply\"></a>"},{"id":"big-numbers","title":"Big numbers","role":"content","text":"This type can encode integer values outside the range of signed 64-bit integers.\n\nBig numbers use the following encoding:\n\n    ([+|-]<number>\\r\\n\n\n* The left parenthesis character (`(`) as the first byte.\n* An optional plus (`+`) or minus (`-`) as the sign.\n* One or more decimal digits (`0`..`9`) as an unsigned, base-10 value.\n* The CRLF terminator.\n\nExample:\n\n    (3492890328409238509324850943850943825024385\\r\\n\n\nBig numbers can be positive or negative but can't include fractionals.\nClient libraries written in languages with a big number type should return a big number.\nWhen big numbers aren't supported, the client should return a string and, when possible, signal to the caller that the reply is a big integer (depending on the API used by the client library).\n\n<a name=\"bulk-error-reply\"></a>"},{"id":"bulk-errors","title":"Bulk errors","role":"content","text":"This type combines the purpose of [simple errors](#simple-errors) with the expressive power of [bulk strings](#bulk-strings).\n\nIt is encoded as:\n\n    !<length>\\r\\n<error>\\r\\n\n\n* An exclamation mark (`!`) as the first byte.\n* One or more decimal digits (`0`..`9`) as the error's length, in bytes, as an unsigned, base-10 value.\n* The CRLF terminator.\n* The error itself.\n* A final CRLF.\n\nAs a convention, the error begins with an uppercase (space-delimited) word that conveys the error message.\n\nFor instance, the error \"SYNTAX invalid syntax\" is represented by the following protocol encoding:\n\n    !21\\r\\n\n    SYNTAX invalid syntax\\r\\n\n\n(The raw RESP encoding is split into multiple lines for readability).\n\n<a name=\"verbatim-string-reply\">"},{"id":"verbatim-strings","title":"Verbatim strings","role":"content","text":"This type is similar to the [bulk string](#bulk-strings), with the addition of providing a hint about the data's encoding.\n\nA verbatim string's RESP encoding is as follows:\n\n    =<length>\\r\\n<encoding>:<data>\\r\\n\n\n* An equal sign (`=`) as the first byte.\n* One or more decimal digits (`0`..`9`) as the string's total length, in bytes, as an unsigned, base-10 value.\n* The CRLF terminator.\n* Exactly three (3) bytes represent the data's encoding.\n* The colon (`:`) character separates the encoding and data.\n* The data.\n* A final CRLF.\n\nExample:\n\n    =15\\r\\n\n    txt:Some string\\r\\n\n\n(The raw RESP encoding is split into multiple lines for readability).\n\nSome client libraries may ignore the difference between this type and the string type and return a native string in both cases.\nHowever, interactive clients, such as command line interfaces (e.g., [`redis-cli`](https://redis.io/docs/latest/develop/tools/cli)), can use this type and know that their output should be presented to the human user as is and without quoting the string.\n\nFor example, the Redis command [`INFO`](https://redis.io/docs/latest/commands/info) outputs a report that includes newlines.\nWhen using RESP3, `redis-cli` displays it correctly because it is sent as a Verbatim String reply (with its three bytes being \"txt\").\nWhen using RESP2, however, the `redis-cli` is hard-coded to look for the [`INFO`](https://redis.io/docs/latest/commands/info) command to ensure its correct display to the user.\n\n<a name=\"map-reply\"></a>"},{"id":"maps","title":"Maps","role":"content","text":"The RESP map encodes a collection of key-value tuples, i.e., a dictionary or a hash.\n\nIt is encoded as follows:\n\n    %<number-of-entries>\\r\\n<key-1><value-1>...<key-n><value-n>\n\n* A percent character (`%`) as the first byte.\n* One or more decimal digits (`0`..`9`) as the number of entries, or key-value tuples, in the map as an unsigned, base-10 value.\n* The CRLF terminator.\n* Two additional RESP types for every key and value in the map.\n\nFor example, the following JSON object:\n\n    {\n        \"first\": 1,\n        \"second\": 2\n    }\n\nCan be encoded in RESP like so:\n\n    %2\\r\\n\n    +first\\r\\n\n    :1\\r\\n\n    +second\\r\\n\n    :2\\r\\n\n\n(The raw RESP encoding is split into multiple lines for readability).\n\nBoth map keys and values can be any of RESP's types.\n\nRedis clients should return the idiomatic dictionary type that their language provides.\nHowever, low-level programming languages (such as C, for example) will likely return an array along with type information that indicates to the caller that it is a dictionary.\n\n> [!NOTE] Map pattern in RESP2\n> RESP2 doesn't have a map type.\n> A map in RESP2 is represented by a flat array containing the keys and the values.\n> The first element is a key, followed by the corresponding value, then the next key and so on, like this:\n> `key1, value1, key2, value2, ...`.\n\n<a name=\"attribute-reply\"></a>"},{"id":"attributes","title":"Attributes","role":"content","text":"The attribute type is exactly like the Map type, but instead of a `%` character as the first byte, the `|` character is used. Attributes describe a dictionary exactly like the Map type. However the client should not consider such a dictionary part of the reply, but as auxiliary data that augments the reply.\n\nNote: in the examples below, indentation is shown only for clarity; the additional whitespace would not be part of a real reply.\n\nFor example, newer versions of Redis may include the ability to report the popularity of keys for every executed command. The reply to the command `MGET a b` may be the following:\n\n    |1\\r\\n\n        +key-popularity\\r\\n\n        %2\\r\\n\n            $1\\r\\n\n            a\\r\\n\n            ,0.1923\\r\\n\n            $1\\r\\n\n            b\\r\\n\n            ,0.0012\\r\\n\n    *2\\r\\n\n        :2039123\\r\\n\n        :9543892\\r\\n\n\nThe actual reply to `MGET` is just the two item array `[2039123, 9543892]`. The returned attributes specify the popularity, or frequency of requests, given as floating point numbers ranging from `0.0` to `1.0`, of the keys mentioned in the original command. Note: the actual implementation in Redis may differ.\n\nWhen a client reads a reply and encounters an attribute type, it should read the attribute, and continue reading the reply. The attribute reply should be accumulated separately, and the user should have a way to access such attributes. For instance, if we imagine a session in an higher level language, something like this could happen:\n\n[code example]\n\nAttributes can appear anywhere before a valid part of the protocol identifying a given type, and supply information only about the part of the reply that immediately follows. For example:\n\n    *3\\r\\n\n        :1\\r\\n\n        :2\\r\\n\n        |1\\r\\n\n            +ttl\\r\\n\n            :3600\\r\\n\n        :3\\r\\n\n\nIn the above example the third element of the array has associated auxiliary information of `{ttl:3600}`. Note that it's not up to the client library to interpret the attributes, but it should pass them to the caller in a sensible way.\n\n<a name=\"set-reply\"></a>"},{"id":"sets","title":"Sets","role":"content","text":"Sets are somewhat like [Arrays](#arrays) but are unordered and should only contain unique elements.\n\nRESP set's encoding is:\n\n    ~<number-of-elements>\\r\\n<element-1>...<element-n>\n\n* A tilde (`~`) as the first byte.\n* One or more decimal digits (`0`..`9`) as the number of elements in the set as an unsigned, base-10 value.\n* The CRLF terminator.\n* An additional RESP type for every element of the Set.\n\nClients should return the native set type if it is available in their programming language.\nAlternatively, in the absence of a native set type, an array coupled with type information can be used (in C, for example).\n\n<a name=\"push-event\"></a>"},{"id":"pushes","title":"Pushes","role":"content","text":"RESP's pushes contain out-of-band data.\nThey are an exception to the protocol's request-response model and provide a generic _push mode_ for connections.\n\nPush events are encoded similarly to [arrays](#arrays), differing only in their first byte:\n\n    ><number-of-elements>\\r\\n<element-1>...<element-n>\n\n* A greater-than sign (`>`) as the first byte.\n* One or more decimal digits (`0`..`9`) as the number of elements in the message as an unsigned, base-10 value.\n* The CRLF terminator.\n* An additional RESP type for every element of the push event.\n\nPushed data may precede or follow any of RESP's data types but never inside them.\nThat means a client won't find push data in the middle of a map reply, for example.\nIt also means that pushed data may appear before or after a command's reply, as well as by itself (without calling any command).\n\nClients should react to pushes by invoking a callback that implements their handling of the pushed data."},{"id":"streamed-strings","title":"Streamed strings","role":"content","text":"A streamed string is a [bulk string](#bulk-strings) whose total length isn't known when the transfer begins.\nInstead of a prefixed length, the sender transfers the payload as a series of chunks.\n\nA streamed string's RESP encoding is as follows:\n\n    $?\\r\\n;<chunk-1-length>\\r\\n<chunk-1-data>\\r\\n...;0\\r\\n\n\n* A dollar sign (`$`) as the first byte, followed by a question mark (`?`) in place of the length.\n* The CRLF terminator.\n* One or more chunks. Each chunk starts with a semicolon (`;`), followed by the chunk's length in bytes as an unsigned, base-10 value, the CRLF terminator, the chunk's data, and a final CRLF.\n* A zero-length chunk (`;0\\r\\n`) to mark the end of the string.\n\nExample:\n\n    $?\\r\\n\n    ;5\\r\\n\n    Hello\\r\\n\n    ;6\\r\\n\n     world\\r\\n\n    ;0\\r\\n\n\n(The raw RESP encoding is split into multiple lines for readability).\n\nThe chunks concatenate to the string's value, so the example encodes `Hello world`.\n\nRedis doesn't emit streamed strings, because its RESP3 support excludes streamed types (see [RESP versions](#resp-versions)).\nThe [RESP3 specification](https://github.com/redis/redis-specifications/blob/1252427cdbc497f66a7f8550c6b5f2f35367dc92/protocol/RESP3.md#streamed-strings) permits modules to use the encoding, so a client that implements RESP3 in full should be able to parse it."},{"id":"streamed-aggregated-data-types","title":"Streamed aggregated data types","role":"content","text":"[Arrays](#arrays), [sets](#sets), and [maps](#maps) can also be streamed when the number of elements isn't known when the transfer begins.\nA streamed aggregate replaces its number of elements with a question mark (`?`) and marks its end with a dedicated terminator.\n\nA streamed aggregate's RESP encoding is as follows:\n\n    <first-byte>?\\r\\n<element-1>...<element-n>.\\r\\n\n\n* The aggregate's usual first byte: an asterisk (`*`) for an array, a tilde (`~`) for a set, or a percent sign (`%`) for a map.\n* A question mark (`?`) in place of the number of elements.\n* The CRLF terminator.\n* An additional RESP type for every element of the aggregate.\n* A period (`.`) followed by the CRLF terminator to mark the end of the aggregate.\n\nExample:\n\n    *?\\r\\n\n    :1\\r\\n\n    :2\\r\\n\n    :3\\r\\n\n    .\\r\\n\n\n(The raw RESP encoding is split into multiple lines for readability).\n\nA streamed map's elements are field-value pairs, as in a regular [map](#maps), so a streamed map must contain an even number of elements.\n\nAs with [streamed strings](#streamed-strings), Redis doesn't emit streamed aggregates."},{"id":"client-handshake","title":"Client handshake","role":"content","text":"New RESP connections should begin the session by calling the [`HELLO`](https://redis.io/docs/latest/commands/hello) command.\nThis practice accomplishes two things:\n\n1. It allows servers to be backward compatible with RESP2 versions.\n  This is needed in Redis to make the transition to version 3 of the protocol gentler.\n2. The [`HELLO`](https://redis.io/docs/latest/commands/hello) command returns information about the server and the protocol that the client can use for different goals.\n\nThe [`HELLO`](https://redis.io/docs/latest/commands/hello) command has the following high-level syntax:\n\n    HELLO <protocol-version> [optional-arguments]\n\nThe first argument of the command is the protocol version we want the connection to be set.\nBy default, the connection starts in RESP2 mode.\nIf we specify a connection version that is too big and unsupported by the server, it should reply with a `-NOPROTO` error. Example:\n\n    Client: HELLO 4\n    Server: -NOPROTO sorry, this protocol version is not supported.\n    \nAt that point, the client may retry with a lower protocol version.\n\nSimilarly, the client can easily detect a server that is only able to speak RESP2:\n\n    Client: HELLO 3\n    Server: -ERR unknown command 'HELLO'\n\nThe client can then proceed and use RESP2 to communicate with the server.\n\nNote that even if the protocol's version is supported, the [`HELLO`](https://redis.io/docs/latest/commands/hello) command may return an error, perform no action and remain in RESP2 mode. \nFor example, when used with invalid authentication credentials in the command's optional `AUTH` clause:\n\n    Client: HELLO 3 AUTH default mypassword\n    Server: -ERR invalid password\n    (the connection remains in RESP2 mode)\n\nA successful reply to the [`HELLO`](https://redis.io/docs/latest/commands/hello) command is a map reply.\nThe information in the reply is partly server-dependent, but certain fields are mandatory for all the RESP3 implementations:\n* **server**: \"redis\" (or other software name).\n* **version**: the server's version.\n* **proto**: the highest supported version of the RESP protocol.\n\nIn Redis' RESP3 implementation, the following fields are also emitted:\n\n* **id**: the connection's identifier (ID).\n* **mode**: \"standalone\", \"sentinel\" or \"cluster\".\n* **role**: \"master\" or \"replica\".\n* **modules**: list of loaded modules as an Array of Bulk Strings."},{"id":"sending-commands-to-a-redis-server","title":"Sending commands to a Redis server","role":"content","text":"Now that you are familiar with the RESP serialization format, you can use it to help write a Redis client library.\nWe can further specify how the interaction between the client and the server works:\n\n* A client sends the Redis server an [array](#arrays) consisting of only bulk strings.\n* A Redis server replies to clients, sending any valid RESP data type as a reply.\n\nSo, for example, a typical interaction could be the following.\n\nThe client sends the command `LLEN mylist` to get the length of the list stored at the key _mylist_.\nThen the server replies with an [integer](#integers) reply as in the following example (`C:` is the client, `S:` the server).\n\n    C: *2\\r\\n\n    C: $4\\r\\n\n    C: LLEN\\r\\n\n    C: $6\\r\\n\n    C: mylist\\r\\n\n\n    S: :48293\\r\\n\n\nAs usual, we separate different parts of the protocol with newlines for simplicity, but the actual interaction is the client sending `*2\\r\\n$4\\r\\nLLEN\\r\\n$6\\r\\nmylist\\r\\n` as a whole."},{"id":"multiple-commands-and-pipelining","title":"Multiple commands and pipelining","role":"content","text":"A client can use the same connection to issue multiple commands.\nPipelining is supported, so multiple commands can be sent with a single write operation by the client.\nThe client can skip reading replies and continue to send the commands one after the other.\nAll the replies can be read at the end.\n\nFor more information, see [Pipelining](https://redis.io/docs/latest/develop/using-commands/pipelining)."},{"id":"inline-commands","title":"Inline commands","role":"content","text":"Sometimes you may need to send a command to the Redis server but only have `telnet` available.\nWhile the Redis protocol is simple to implement, it is not ideal for interactive sessions, and `redis-cli` may not always be available.\nFor this reason, Redis also accepts commands in the _inline command_ format.\n\nThe following example demonstrates a server/client exchange using an inline command (the server chat starts with `S:`, the client chat with `C:`):\n\n    C: PING\n    S: +PONG\n\nHere's another example of an inline command where the server returns an integer:\n\n    C: EXISTS somekey\n    S: :0\n\nBasically, to issue an inline command, you write space-separated arguments in a telnet session.\nSince no command starts with `*` (the identifying byte of RESP Arrays), Redis detects this condition and parses your command inline."},{"id":"high-performance-parser-for-the-redis-protocol","title":"High-performance parser for the Redis protocol","role":"content","text":"While the Redis protocol is human-readable and easy to implement, its implementation can exhibit performance similar to that of a binary protocol.\n\nRESP uses prefixed lengths to transfer bulk data.\nThat makes scanning the payload for special characters unnecessary (unlike parsing JSON, for example).\nFor the same reason, quoting and escaping the payload isn't needed.\n\nReading the length of aggregate types (for example, bulk strings or arrays) can be processed with code that performs a single operation per character while at the same time scanning for the CR character.\n\nExample (in C):\n\n[code example]\n\nAfter the first CR is identified, it can be skipped along with the following LF without further processing.\nThen, the bulk data can be read with a single read operation that doesn't inspect the payload in any way.\nFinally, the remaining CR and LF characters are discarded without additional processing.\n\nWhile comparable in performance to a binary protocol, the Redis protocol is significantly more straightforward to implement in most high-level languages, reducing the number of bugs in client software."},{"id":"tips-for-redis-client-authors","title":"Tips for Redis client authors","role":"content","text":"* For testing purposes, use [Lua's type conversions](https://redis.io/docs/latest/develop/programmability/lua-api#lua-to-resp3-type-conversion) to have Redis reply with any RESP2/RESP3 needed.\n  As an example, a RESP3 double can be generated like so:\n  [code example]"}],"examples":[{"id":"attributes-ex0","language":"python","code":"> r = Redis.new\n#<Redis client>\n> r.mget(\"a\",\"b\")\n#<Redis reply>\n> r\n[2039123,9543892]\n> r.attribs\n{:key-popularity => {:a => 0.1923, :b => 0.0012}}","section_id":"attributes"},{"id":"high-performance-parser-for-the-redis-protocol-ex0","language":"c","code":"#include <stdio.h>\n\nint main(void) {\n    unsigned char *p = \"$123\\r\\n\";\n    int len = 0;\n\n    p++;\n    while(*p != '\\r') {\n        len = (len*10)+(*p - '0');\n        p++;\n    }\n\n    /* Now p points at '\\r', and the length is in len. */\n    printf(\"%d\\n\", len);\n    return 0;\n}","section_id":"high-performance-parser-for-the-redis-protocol"},{"id":"tips-for-redis-client-authors-ex0","language":"plaintext","code":"EVAL \"return { double = tonumber(ARGV[1]) }\" 0 1e0","section_id":"tips-for-redis-client-authors"}]}
{"schema_version":2,"id":"develop/reference/sentinel-clients","title":"Sentinel client spec","url":"https://redis.io/docs/latest/develop/reference/sentinel-clients/","summary":"How to build clients for Redis Sentinel","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:49:27+01:00","page_type":"content","content_hash":"68a16320fb68c85019a3e8cc9fbbbe49cd1b92d8e2289ccafc48b853d24cafbb","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Sentinel is a monitoring solution for Redis instances that handles\nautomatic failover of Redis masters and service discovery (who is the current\nmaster for a given group of instances?). Since Sentinel is both responsible\nfor reconfiguring instances during failovers, and providing configurations to\nclients connecting to Redis masters or replicas, clients are required to have\nexplicit support for Redis Sentinel.\n\nThis document is targeted at Redis clients developers that want to support Sentinel in their clients implementation with the following goals:\n\n* Automatic configuration of clients via Sentinel.\n* Improved safety of Redis Sentinel automatic failover.\n\nFor details about how Redis Sentinel works, please check the [Redis Documentation](https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel), as this document only contains information needed for Redis client developers, and it is expected that readers are familiar with the way Redis Sentinel works."},{"id":"redis-service-discovery-via-sentinel","title":"Redis service discovery via Sentinel","role":"content","text":"Redis Sentinel identifies every master with a name like \"stats\" or \"cache\".\nEvery name actually identifies a *group of instances*, composed of a master\nand a variable number of replicas.\n\nThe address of the Redis master that is used for a specific purpose inside a network may change after events like an automatic failover, a manually triggered failover (for instance in order to upgrade a Redis instance), and other reasons.\n\nNormally Redis clients have some kind of hard-coded configuration that specifies the address of a Redis master instance within a network as IP address and port number. However if the master address changes, manual intervention in every client is needed.\n\nA Redis client supporting Sentinel can automatically discover the address of a Redis master from the master name using Redis Sentinel. So instead of a hard coded IP address and port, a client supporting Sentinel should optionally be able to take as input:\n\n* A list of ip:port pairs pointing to known Sentinel instances.\n* The name of the service, like \"cache\" or \"timelines\".\n\nThis is the procedure a client should follow in order to obtain the master address starting from the list of Sentinels and the service name."},{"id":"step-1-connect-to-the-first-sentinel","title":"Step 1: connect to the first Sentinel","role":"content","text":"The client should iterate the list of Sentinel addresses. For every address it should try to connect to the Sentinel, using a short timeout (in the order of a few hundreds of milliseconds). On errors or timeouts the next Sentinel address should be tried.\n\nIf all the Sentinel addresses were tried without success, an error should be returned to the client.\n\nThe first Sentinel replying to the client request should be put at the start of the list, so that at the next reconnection, we'll try first the Sentinel that was reachable in the previous connection attempt, minimizing latency."},{"id":"step-2-ask-for-the-master-address","title":"Step 2: ask for the master address","role":"content","text":"Once a connection with a Sentinel is established, the client should retry to execute the following command on the Sentinel:\n\n    SENTINEL get-master-addr-by-name master-name\n\nWhere *master-name* should be replaced with the actual service name specified by the user.\n\nThe result from this call can be one of the following two replies:\n\n* An ip:port pair.\n* A null reply. This means Sentinel does not know this master.\n\nIf an ip:port pair is received, this address should be used to connect to the Redis master. Otherwise if a null reply is received, the client should try the next Sentinel in the list."},{"id":"step-3-call-the-role-command-in-the-target-instance","title":"Step 3: call the ROLE command in the target instance","role":"content","text":"Once the client discovered the address of the master instance, it should\nattempt a connection with the master, and call the [`ROLE`](https://redis.io/docs/latest/commands/role) command in order\nto verify the role of the instance is actually a master.\n\nIf the [`ROLE`](https://redis.io/docs/latest/commands/role) commands is not available (it was introduced in Redis 2.8.12), a client may resort to the `INFO replication` command parsing the `role:` field of the output.\n\nIf the instance is not a master as expected, the client should wait a short amount of time (a few hundreds of milliseconds) and should try again starting from Step 1."},{"id":"handle-reconnections","title":"Handle reconnections","role":"content","text":"Once the service name is resolved into the master address and a connection is established with the Redis master instance, every time a reconnection is needed, the client should resolve again the address using Sentinels restarting from Step 1. For instance Sentinel should contacted again the following cases:\n\n* If the client reconnects after a timeout or socket error.\n* If the client reconnects because it was explicitly closed or reconnected by the user.\n\nIn the above cases and any other case where the client lost the connection with the Redis server, the client should resolve the master address again."},{"id":"sentinel-failover-disconnection","title":"Sentinel failover disconnection","role":"content","text":"Starting with Redis 2.8.12, when Redis Sentinel changes the configuration of\nan instance, for example promoting a replica to a master, demoting a master to\nreplicate to the new master after a failover, or simply changing the master\naddress of a stale replica instance, it sends a `CLIENT KILL type normal`\ncommand to the instance in order to make sure all the clients are disconnected\nfrom the reconfigured instance. This will force clients to resolve the master\naddress again.\n\nIf the client will contact a Sentinel with yet not updated information, the verification of the Redis instance role via the [`ROLE`](https://redis.io/docs/latest/commands/role) command will fail, allowing the client to detect that the contacted Sentinel provided stale information, and will try again.\n\nNote: it is possible that a stale master returns online at the same time a client contacts a stale Sentinel instance, so the client may connect with a stale master, and yet the ROLE output will match. However when the master is back again Sentinel will try to demote it to replica, triggering a new disconnection. The same reasoning applies to connecting to stale replicas that will get reconfigured to replicate with a different master."},{"id":"connect-to-replicas","title":"Connect to replicas","role":"content","text":"Sometimes clients are interested to connect to replicas, for example in order to scale read requests. This protocol supports connecting to replicas by modifying step 2 slightly. Instead of calling the following command:\n\n    SENTINEL get-master-addr-by-name master-name\n\nThe clients should call instead:\n\n    SENTINEL replicas master-name\n\nIn order to retrieve a list of replica instances.\n\nSymmetrically the client should verify with the [`ROLE`](https://redis.io/docs/latest/commands/role) command that the\ninstance is actually a replica, in order to avoid scaling read queries with\nthe master."},{"id":"connection-pools","title":"Connection pools","role":"content","text":"For clients implementing connection pools, on reconnection of a single connection, the Sentinel should be contacted again, and in case of a master address change all the existing connections should be closed and connected to the new address."},{"id":"error-reporting","title":"Error reporting","role":"errors","text":"The client should correctly return the information to the user in case of errors. Specifically:\n\n* If no Sentinel can be contacted (so that the client was never able to get the reply to `SENTINEL get-master-addr-by-name`), an error that clearly states that Redis Sentinel is unreachable should be returned.\n* If all the Sentinels in the pool replied with a null reply, the user should be informed with an error that Sentinels don't know this master name."},{"id":"sentinels-list-automatic-refresh","title":"Sentinels list automatic refresh","role":"content","text":"Optionally once a successful reply to `get-master-addr-by-name` is received, a client may update its internal list of Sentinel nodes following this procedure:\n\n* Obtain a list of other Sentinels for this master using the command `SENTINEL sentinels <master-name>`.\n* Add every ip:port pair not already existing in our list at the end of the list.\n\nIt is not needed for a client to be able to make the list persistent updating its own configuration. The ability to upgrade the in-memory representation of the list of Sentinels can be already useful to improve reliability."},{"id":"subscribe-to-sentinel-events-to-improve-responsiveness","title":"Subscribe to Sentinel events to improve responsiveness","role":"content","text":"The [Sentinel documentation](https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel) shows how clients can connect to\nSentinel instances using Pub/Sub in order to subscribe to changes in the\nRedis instances configurations.\n\nThis mechanism can be used in order to speedup the reconfiguration of clients,\nthat is, clients may listen to Pub/Sub in order to know when a configuration\nchange happened in order to run the three steps protocol explained in this\ndocument in order to resolve the new Redis master (or replica) address.\n\nHowever update messages received via Pub/Sub should not substitute the\nabove procedure, since there is no guarantee that a client is able to\nreceive all the update messages."},{"id":"additional-information","title":"Additional information","role":"content","text":"For additional information or to discuss specific aspects of this guidelines, please drop a message to the [Redis Google Group](https://groups.google.com/group/redis-db)."}],"examples":[]}
{"schema_version":2,"id":"develop/setup/build-with-an-agent","title":"Build with an agent","url":"https://redis.io/docs/latest/develop/setup/build-with-an-agent/","summary":"Connect AI agents to Redis documentation and give them Redis-specific skills.","aliases":["/develop/getting-started/build-with-an-agent/"],"tags":["docs","getting started"],"last_updated":"2026-10-01T14:49:09Z","page_type":"content","content_hash":"e26e392dfa5eed674ab3f1e70aa2bb7823e36c96a9f5f8f7941dca2254dd6d6d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis publishes two agent-facing tools that work alongside the client libraries and MCP servers covered elsewhere in these docs:\n\n- [Redis Docs MCP](#redis-docs-mcp) — a hosted server that lets any MCP-compatible agent search, fetch, and ask questions against the Redis documentation itself.\n- [Agent Skills](#agent-skills) — packaged instructions that teach a coding agent Redis-specific patterns (data modeling, connections, search, security, and more)."},{"id":"redis-docs-mcp","title":"Redis Docs MCP","role":"content","text":"[`redis.io/mcp`](https://redis.io/mcp) is a public, read-only [Model Context Protocol](https://modelcontextprotocol.io/introduction) server that serves the Redis documentation to any MCP client. It requires no API key or sign-up.\n\n> [!NOTE]\n> This is a different server from the [Redis MCP server](https://redis.io/docs/latest/integrate/redis-mcp) (`mcp-redis`). `mcp-redis` is a self-hosted server that connects an agent to *your own* Redis database, so it can run commands against your data. The Redis Docs MCP server has no access to any Redis instance except Redis's own documentation index."},{"id":"connect-a-client","title":"Connect a client","role":"content","text":"Add the server to your MCP client's configuration:\n\n[code example]"},{"id":"agent-skills","title":"Agent skills","role":"content","text":"[`redis/agent-skills`](https://github.com/redis/agent-skills) is Redis's published collection of [Agent Skills](https://agentskills.io/) — packaged instructions and resources that extend a coding agent's knowledge of Redis-specific patterns. Once installed, an agent automatically applies the relevant skill when it detects a matching task; you don't invoke them manually.\n\n| Skill | Covers |\n|-------|--------|\n| `redis-core` | Data structures, key naming strategies, memory management, TTLs, and when to use JSON versus Hash |\n| `redis-connections` | Connection pooling, multiplexing, pipelining, and client-side caching |\n| `redis-search` | Full-text search schemas, aggregation queries, and vector similarity (HNSW/FLAT) for RAG pipelines |\n| `redis-semantic-cache` | Caching LLM responses with LangCache and configurable similarity thresholds |\n| `redis-clustering` | Cluster operations using hash tags and replica reads |\n| `redis-security` | Authentication, TLS encryption, ACL policies, and network security |\n| `redis-observability` | Monitoring with `INFO`, `SLOWLOG` analysis, and Redis Insight |\n| `iris-development` | Agent Memory provisioning and long-term memory search |"},{"id":"install","title":"Install","role":"setup","text":"Install the full collection with the [Agent Skills CLI](https://agentskills.io/):\n\n[code example]\n\nOr install it as a plugin in your agent tool of choice:\n\n- **Claude Code**: `/plugin marketplace add redis/agent-skills`\n- **Cursor**: `/add-plugin redis`\n- **ChatGPT/Codex**: use the package under [`plugins/redis-development/`](https://github.com/redis/agent-skills/tree/main/plugins/redis-development) in the repository"},{"id":"see-also","title":"See also","role":"related","text":"- [Redis MCP server](https://redis.io/docs/latest/integrate/redis-mcp) — connect an agent to your own Redis database\n- [Redis client libraries](https://redis.io/docs/latest/develop/clients)\n- [Redis for AI agents](https://redis.io/docs/latest/develop/get-started/redis-in-ai)"}],"examples":[{"id":"connect-a-client-ex0","language":"json","code":"{\n  \"mcpServers\": {\n    \"redis-docs\": {\n      \"url\": \"https://redis.io/mcp\"\n    }\n  }\n}","section_id":"connect-a-client"},{"id":"install-ex0","language":"sh","code":"npx skills add redis/agent-skills","section_id":"install"}]}
{"schema_version":2,"id":"develop/setup","title":"Get started","url":"https://redis.io/docs/latest/develop/setup/","summary":"Get Redis running and connect your first application, however you deploy it.","aliases":["/develop/getting-started/"],"tags":[],"last_updated":"2026-10-01T14:49:09Z","children":[{"id":"develop/setup/build-with-an-agent","summary":"Connect AI agents to Redis documentation and give them Redis-specific skills.","title":"Build with an agent","url":"https://redis.io/docs/latest/develop/setup/build-with-an-agent/"}],"page_type":"content","content_hash":"8944cfd530c4a2917ea4194203d43c7ef52aca64e1bb608123b4e1e12bf44f6e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis can be used as a database, cache, streaming engine, message broker, context engine, feature platform, and more.\nIn this guide, you'll learn how to create a Redis deployment in Redis Open Source, Redis Cloud, or Redis Software. Then, you'll learn how to create an application that connects to your deployment."},{"id":"create-a-redis-deployment","title":"Create a Redis deployment","role":"content","text":"This section shows how to set up a Redis deployment and connect to it with `redis-cli`.\n\n1. Install dependencies\n\n   **Redis Open Source:**\n\n- [Docker](https://docs.docker.com/get-docker/) installed, to run the Docker quick start in step 2. To install without Docker, see [Install Redis on Linux, macOS, or from source](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack) for its own system requirements.\n\n**Redis Cloud:**\n\n- A web browser, to sign up and manage your database in the [Redis Cloud console](https://cloud.redis.io).\n   - `redis-cli` installed locally, to connect to your database in step 3:\n\n     [code example]\n\n     See [Install redis-cli](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/install-redis-cli) for more information.\n\n**Redis Software:**\n\n- [Docker](https://docs.docker.com/get-docker/) installed, to run the Docker quick start in step 2. To install without Docker, see the [Redis Software on Linux quick start](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/redis-enterprise-software-quickstart) for its own system requirements.\n   - A command-line HTTP client such as `curl`, if you want to create a database with the REST API in step 2.\n\n\n\n2. Set up a fully-featured deployment\n\n   **Redis Open Source:**\n\n[Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack) is the free, self-managed core Redis server. The fastest way to run it is with Docker:\n\n   [code example]\n\n   Starting the server creates your database — there's no separate create step. See [Run Redis on Docker](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/docker) for the full procedure, or [Install Redis on Linux, macOS, or from source](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack) to install without Docker.\n\n**Redis Cloud:**\n\n[Sign up and create your database](https://redis.io/try-free/). Signing up creates a free 30 MB database for you.\n\n   To create additional or different types of databases from the console, see:\n\n   - [Create an Essentials database](https://redis.io/docs/latest/operate/rc/databases/create-database/create-essentials-database) — a cost-efficient, fully managed database for low-throughput workloads, training, and prototyping.\n   - [Create a Pro database](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new) — a dedicated, \"pay as you go\" database for production workloads that need higher throughput, larger datasets, and advanced features like Active-Active and clustering.\n\n**Redis Software:**\n\n[Redis Software](https://redis.io/docs/latest/operate/rs) is a self-managed Redis cluster you install and run on your own infrastructure. The fastest way to try it is with Docker:\n\n   [code example]\n\n   See the [Redis Software on Docker quick start](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) for the full procedure, or the [Redis Software on Linux quick start](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/redis-enterprise-software-quickstart) to install without Docker.\n\n   You can create databases with Redis Software using the console or the REST API. To create a database with the REST API:\n\n   [code example]\n\n   See [Create a database](https://redis.io/docs/latest/operate/rs/databases/create) for the full procedure, including the console method and additional configuration fields.\n\n\n\n3. Connect to your deployment\n\n   **Redis Open Source:**\n\nIf you started Redis with the `docker run` command from step 2 then you can connect to the database with the following command:\n\n   [code example]\n\n   See [Redis CLI](https://redis.io/docs/latest/develop/tools/cli) for the full procedure, including non-Docker installs and [Redis Insight](https://redis.io/docs/latest/develop/tools/insight) as a GUI alternative to the CLI.\n\n**Redis Cloud:**\n\nGet your database's host, port, and password from the connection wizard in the [Redis Cloud console](https://cloud.redis.io), then connect with `redis-cli`:\n\n   [code example]\n\n   See [Connect to a Redis Cloud database](https://redis.io/docs/latest/operate/rc/databases/connect) for the full procedure, including Redis Insight and client library options.\n\n**Redis Software:**\n\nConnect to your Redis Software database using the following command:\n\n   [code example]\n\n   See [Connect to a Redis Software database](https://redis.io/docs/latest/operate/rs/databases/connect) for the full procedure and for further options, including Redis Insight and client library connections.\n\n\n\nCongratulations! You have successfully set up your Redis deployment and connected to it.\n\nIn the next section, you'll learn how to create an application that connects to your deployment and interacts with data."},{"id":"create-your-first-redis-application","title":"Create your first Redis application","role":"content","text":"To connect to your Redis deployment in an application, you can use one of the official [Redis client libraries](https://redis.io/docs/latest/develop/clients).\n\n1. Initialize your application\n\n   **Python:**\n\nCreate a project directory and [install redis-py](https://redis.io/docs/latest/develop/clients/redis-py#install):\n\n   [code example]\n\n**Node.js:**\n\nCreate a project directory and [install node-redis](https://redis.io/docs/latest/develop/clients/nodejs#install):\n\n   [code example]\n\n**Java:**\n\nAdd [Jedis](https://redis.io/docs/latest/develop/clients/jedis) as a dependency in your Maven project's `pom.xml`:\n\n   [code example]\n\n**Go:**\n\nCreate a project directory and [install go-redis](https://redis.io/docs/latest/develop/clients/go):\n\n   [code example]\n\n**C#:**\n\nCreate a project directory and [install StackExchange.Redis](https://redis.io/docs/latest/develop/clients/dotnet):\n\n   [code example]\n\n**Ruby:**\n\nCreate a project directory and [install redis-rb](https://redis.io/docs/latest/develop/clients/ruby#install):\n\n   [code example]\n\n**C:**\n\n[Build and install hiredis](https://redis.io/docs/latest/develop/clients/hiredis#build-and-install) from source:\n\n   [code example]\n\n**Rust:**\n\nCreate a project directory and [add the `redis` crate](https://redis.io/docs/latest/develop/clients/rust#install) as a dependency in `Cargo.toml`:\n\n   [code example]\n\n**PHP:**\n\nUse [Composer](https://redis.io/docs/latest/develop/clients/php#install) to install Predis:\n\n   [code example]\n\n**RedisVL:**\n\nCreate a project directory and [install RedisVL](https://redis.io/docs/latest/develop/ai/redisvl/install):\n\n   [code example]\n\n\n\n2. Create your application\n\n   **Python:**\n\nCopy the following code into `app.py`. This connects to your database and sets and retrieves a value.\n\n   [code example]\n\n**Node.js:**\n\nCopy the following code into `index.js`. This connects to your database and sets and retrieves a value.\n\n   [code example]\n\n**Java:**\n\nCopy the following code into `Main.java`. This connects to your database and sets and retrieves a value.\n\n   [code example]\n\n**Go:**\n\nCopy the following code into `main.go`. This connects to your database and sets and retrieves a value.\n\n   [code example]\n\n**C#:**\n\nCopy the following code into `Program.cs`. This connects to your database and sets and retrieves a value.\n\n   [code example]\n\n**Ruby:**\n\nCopy the following code into `app.rb`. This connects to your database and sets and retrieves a value.\n\n   [code example]\n\n**C:**\n\nCopy the following code into `main.c`. This connects to your database and sets and retrieves a value.\n\n   [code example]\n\n**Rust:**\n\nCopy the following code into `src/main.rs`. This connects to your database and sets and retrieves a value.\n\n   [code example]\n\n**PHP:**\n\nCopy the following code into `app.php`. This connects to your database and sets and retrieves a value.\n\n   [code example]\n\n**RedisVL:**\n\nRedisVL is a vector-search library, not a general key-value client — it works with index schemas rather than simple `SET`/`GET` commands. Copy the following code into `app.py` to define a schema and create an index:\n\n   [code example]\n\n   See the [RedisVL getting started guide](https://redis.io/docs/latest/develop/ai/redisvl/user_guide/getting_started) for the full procedure, including loading data and running vector searches.\n\n\n\n3. Add your connection string\n\n   The examples in step 2 connect to `localhost:6379`, which works as is if you're running Redis Open Source locally. Otherwise, replace it with your deployment's host, port, and password from the [Redis Cloud connection wizard](https://cloud.redis.io) or the [Redis Software connection details](https://redis.io/docs/latest/operate/rs/databases/connect).\n\n   **Python:**\n\n[code example]\n\n**Node.js:**\n\n[code example]\n\n**Java:**\n\n[code example]\n\n**Go:**\n\n[code example]\n\n**C#:**\n\n[code example]\n\n**Ruby:**\n\n[code example]\n\n**C:**\n\n`hiredis` connects by host and port, then authenticates with a separate command:\n\n   [code example]\n\n**Rust:**\n\n[code example]\n\n**PHP:**\n\n[code example]\n\n**RedisVL:**\n\n[code example]\n\n\n\n4. Run your application\n\n   **Python:**\n\n[code example]\n\n**Node.js:**\n\n[code example]\n\n**Java:**\n\n[code example]\n\n**Go:**\n\n[code example]\n\n**C#:**\n\n[code example]\n\n**Ruby:**\n\n[code example]\n\n**C:**\n\n[code example]\n\n**Rust:**\n\n[code example]\n\n**PHP:**\n\n[code example]\n\n**RedisVL:**\n\n[code example]\n\n\n\n   Each program prints `Process 134` — the value you stored and then retrieved. (RedisVL's example instead confirms the index was created.)\n\n5. Explore a use case\n\n   Each of the following quick starts shows a complete example application for a specific use case:\n\n   - [Data structure store](https://redis.io/docs/latest/develop/get-started/data-store)\n   - [Document database](https://redis.io/docs/latest/develop/get-started/search-tutorial)\n   - [Vector database](https://redis.io/docs/latest/develop/get-started/search-tutorial/vector-search)\n\n   Refer to the [Redis use cases](https://redis.io/docs/latest/develop/use-cases) page for a complete set of use cases with code examples in your programming language."},{"id":"next-steps","title":"Next steps","role":"content","text":"To learn more about developing with Redis, see the following resources:\n\n- [Explore all client libraries](https://redis.io/docs/latest/develop/clients)\n- [Redis products overview](https://redis.io/docs/latest/operate)\n- [Develop with Redis](https://redis.io/docs/latest/develop)"}],"examples":[{"id":"create-a-redis-deployment-ex0","language":"sh","code":"curl -fsSL https://packages.redis.io/redis-cli/install.sh | sh","section_id":"create-a-redis-deployment"},{"id":"create-a-redis-deployment-ex1","language":"sh","code":"docker run -d --name redis -p 6379:6379 redis:latest","section_id":"create-a-redis-deployment"},{"id":"create-a-redis-deployment-ex2","language":"sh","code":"docker run -d --cap-add sys_resource --name RE -p 8443:8443 -p 9443:9443 -p 12000:12000 redislabs/redis","section_id":"create-a-redis-deployment"},{"id":"create-a-redis-deployment-ex3","language":"sh","code":"POST https://<host>:<port>/v1/bdbs\n   {\n       \"name\": \"test-database\",\n       \"type\": \"redis\",\n       \"memory_size\": 1073741824,\n       // Additional fields\n   }","section_id":"create-a-redis-deployment"},{"id":"create-a-redis-deployment-ex4","language":"sh","code":"docker exec -it redis redis-cli","section_id":"create-a-redis-deployment"},{"id":"create-a-redis-deployment-ex5","language":"sh","code":"redis-cli -h <hostname> -p <portnumber> -a <password>","section_id":"create-a-redis-deployment"},{"id":"create-a-redis-deployment-ex6","language":"sh","code":"docker exec -it <container_name_or_ID> redis-cli -h <host_or_IP> -p <port>","section_id":"create-a-redis-deployment"},{"id":"create-your-first-redis-application-ex0","language":"sh","code":"mkdir python-quickstart\n   cd python-quickstart\n   pip install redis","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex1","language":"sh","code":"mkdir node-quickstart\n   cd node-quickstart\n   npm init -y\n   npm install redis","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex2","language":"xml","code":"<dependency>\n       <groupId>redis.clients</groupId>\n       <artifactId>jedis</artifactId>\n       <version>7.2.0</version>\n   </dependency>","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex3","language":"sh","code":"mkdir go-quickstart\n   cd go-quickstart\n   go mod init go-quickstart\n   go get github.com/redis/go-redis/v9","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex4","language":"sh","code":"mkdir csharp-quickstart\n   cd csharp-quickstart\n   dotnet new console\n   dotnet add package StackExchange.Redis","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex5","language":"sh","code":"mkdir ruby-quickstart\n   cd ruby-quickstart\n   gem install redis","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex6","language":"sh","code":"git clone https://github.com/redis/hiredis.git\n   cd hiredis\n   make\n   sudo make install","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex7","language":"toml","code":"[dependencies]\n   redis = \"1.0.4\"","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex8","language":"sh","code":"composer require predis/predis","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex9","language":"sh","code":"mkdir redisvl-quickstart\n   cd redisvl-quickstart\n   pip install redisvl","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex10","language":"python","code":"import redis\n\n   r = redis.Redis(host='localhost', port=6379, decode_responses=True)\n\n   r.set('bike:1', 'Process 134')\n   print(r.get('bike:1'))","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex11","language":"js","code":"import { createClient } from 'redis';\n\n   const client = createClient();\n   client.on('error', err => console.log('Redis Client Error', err));\n   await client.connect();\n\n   await client.set('bike:1', 'Process 134');\n   const value = await client.get('bike:1');\n   console.log(value);","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex12","language":"java","code":"package org.example;\n   import redis.clients.jedis.RedisClient;\n\n   public class Main {\n       public static void main(String[] args) {\n           RedisClient jedis = RedisClient.create(\"redis://localhost:6379\");\n\n           String res1 = jedis.set(\"bike:1\", \"Process 134\");\n           System.out.println(res1);\n\n           String res2 = jedis.get(\"bike:1\");\n           System.out.println(res2);\n\n           jedis.close();\n       }\n   }","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex13","language":"go","code":"package main\n\n   import (\n       \"context\"\n       \"fmt\"\n       \"github.com/redis/go-redis/v9\"\n   )\n\n   func main() {\n       ctx := context.Background()\n       client := redis.NewClient(&redis.Options{\n           Addr: \"localhost:6379\",\n       })\n\n       if err := client.Set(ctx, \"bike:1\", \"Process 134\", 0).Err(); err != nil {\n           panic(err)\n       }\n\n       val, err := client.Get(ctx, \"bike:1\").Result()\n       if err != nil {\n           panic(err)\n       }\n       fmt.Println(val)\n   }","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex14","language":"csharp","code":"using StackExchange.Redis;\n\n   ConnectionMultiplexer redis = ConnectionMultiplexer.Connect(\"localhost:6379\");\n   IDatabase db = redis.GetDatabase();\n\n   db.StringSet(\"bike:1\", \"Process 134\");\n   Console.WriteLine(db.StringGet(\"bike:1\"));","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex15","language":"ruby","code":"require 'redis'\n\n   r = Redis.new\n\n   r.set('bike:1', 'Process 134')\n   puts r.get('bike:1')","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex16","language":"c","code":"#include <stdio.h>\n   #include <stdlib.h>\n   #include <hiredis/hiredis.h>\n\n   int main() {\n       redisContext *c = redisConnect(\"127.0.0.1\", 6379);\n       if (c == NULL || c->err) {\n           printf(\"Connection error\\n\");\n           exit(1);\n       }\n\n       redisReply *reply = redisCommand(c, \"SET bike:1 %s\", \"Process 134\");\n       printf(\"%s\\n\", reply->str);\n       freeReplyObject(reply);\n\n       reply = redisCommand(c, \"GET bike:1\");\n       printf(\"%s\\n\", reply->str);\n       freeReplyObject(reply);\n\n       redisFree(c);\n       return 0;\n   }","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex17","language":"rust","code":"use redis::Commands;\n\n   fn main() -> redis::RedisResult<()> {\n       let client = redis::Client::open(\"redis://127.0.0.1:6379\")?;\n       let mut con = client.get_connection()?;\n\n       con.set(\"bike:1\", \"Process 134\")?;\n       let value: String = con.get(\"bike:1\")?;\n       println!(\"{value}\");\n\n       Ok(())\n   }","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex18","language":"php","code":"<?php\n   require 'vendor/autoload.php';\n\n   use Predis\\Client;\n\n   $client = new Client([\n       'scheme' => 'tcp',\n       'host'   => '127.0.0.1',\n       'port'   => 6379,\n   ]);\n\n   $client->set('bike:1', 'Process 134');\n   echo $client->get('bike:1') . PHP_EOL;","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex19","language":"python","code":"from redisvl.index import SearchIndex\n\n   schema = {\n       \"index\": {\"name\": \"bikes\", \"prefix\": \"bike\"},\n       \"fields\": [\n           {\"name\": \"model\", \"type\": \"text\"},\n           {\"name\": \"price\", \"type\": \"numeric\"},\n       ],\n   }\n\n   index = SearchIndex.from_dict(schema, redis_url=\"redis://localhost:6379\")\n   index.create(overwrite=True)","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex20","language":"python","code":"r = redis.from_url(\"redis://default:<password>@<host>:<port>\")","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex21","language":"js","code":"const client = createClient({\n     url: 'redis://default:<password>@<host>:<port>'\n   });","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex22","language":"java","code":"RedisClient jedis = RedisClient.create(\"redis://default:<password>@<host>:<port>\");","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex23","language":"go","code":"opt, err := redis.ParseURL(\"redis://default:<password>@<host>:<port>\")\n   if err != nil {\n       panic(err)\n   }\n   client := redis.NewClient(opt)","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex24","language":"csharp","code":"ConfigurationOptions conf = new ConfigurationOptions {\n       EndPoints = { \"<host>:<port>\" },\n       User = \"default\",\n       Password = \"<password>\"\n   };\n   ConnectionMultiplexer redis = ConnectionMultiplexer.Connect(conf);","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex25","language":"ruby","code":"r = Redis.new(url: \"redis://default:<password>@<host>:<port>\")","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex26","language":"c","code":"redisContext *c = redisConnect(\"<host>\", <port>);\n   redisReply *reply = redisCommand(c, \"AUTH %s\", \"<password>\");\n   freeReplyObject(reply);","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex27","language":"rust","code":"let client = redis::Client::open(\"redis://default:<password>@<host>:<port>\")?;","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex28","language":"php","code":"$client = new Client([\n       'scheme'   => 'tcp',\n       'host'     => '<host>',\n       'port'     => '<port>',\n       'password' => '<password>',\n   ]);","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex29","language":"python","code":"index = SearchIndex.from_dict(schema, redis_url=\"redis://default:<password>@<host>:<port>\")","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex30","language":"sh","code":"python app.py","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex31","language":"sh","code":"node index.js","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex32","language":"sh","code":"mvn compile exec:java -Dexec.mainClass=\"org.example.Main\"","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex33","language":"sh","code":"go run main.go","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex34","language":"sh","code":"dotnet run","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex35","language":"sh","code":"ruby app.rb","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex36","language":"sh","code":"cc main.c -L/usr/local/lib -lhiredis -o app\n   ./app","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex37","language":"sh","code":"cargo run","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex38","language":"sh","code":"php app.php","section_id":"create-your-first-redis-application"},{"id":"create-your-first-redis-application-ex39","language":"sh","code":"python app.py","section_id":"create-your-first-redis-application"}]}
{"schema_version":2,"id":"develop/tools/cli","title":"Redis CLI","url":"https://redis.io/docs/latest/develop/tools/cli/","summary":"Overview of redis-cli, the Redis command line interface","aliases":["/develop/connect/cli","/connect/cli/","/manual/cli/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T15:49:15+01:00","page_type":"content","content_hash":"e439d052a972c3d5104869b779774f95222015dd74b27589bdcf603c93b31c86","sections":[{"id":"overview","title":"Overview","role":"overview","text":"In interactive mode, `redis-cli` has basic line editing capabilities to provide a familiar typing experience.\n\nTo launch the program in special modes, you can use several options, including:\n\n* Simulate a replica and print the replication stream it receives from the primary.\n* Check the latency of a Redis server and display statistics. \n* Request ASCII-art spectrogram of latency samples and frequencies.\n\nThis topic covers the different aspects of `redis-cli`, starting from the simplest and ending with the more advanced features."},{"id":"install-redis-cli","title":"Install `redis-cli`","role":"setup","text":"You have several options for installing or using `redis-cli`. The easiest method is to install the standalone `redis-cli` binary for Linux or macOS. See the [Install redis-cli](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/install-redis-cli) page for more information.\n\nOther methods include:\n\n- [Install Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack). The `redis-cli` utility is installed as part of each installation method.\n- [Build Redis from source](https://redis.io/docs/latest/operate/oss_and_stack/install/build-stack). Instead of building everything, you can just run the following command:\n\n    `$ make redis-cli`.\n    \n    The `redis-cli` utility will be built in the `/path/to/redis-source/src` directory as `/path/to/redis-source/src/redis-cli`.\n\nIf you prefer not to install Redis, you can also run `redis-cli` in Docker. See the [Run `redis-cli` using Docker](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/docker#connect-with-redis-cli) page for instructions."},{"id":"command-line-usage","title":"Command line usage","role":"syntax","text":"To run a Redis command and return a standard output at the terminal, include the command to execute as separate arguments of `redis-cli`:\n\n    $ redis-cli INCR mycounter\n    (integer) 7\n\nThe reply of the command is \"7\". Since Redis replies are typed (strings, arrays, integers, nil, errors, etc.), you see the type of the reply between parentheses. This additional information may not be ideal when the output of `redis-cli` must be used as input of another command or redirected into a file.\n\n`redis-cli` only shows additional information for human readability when it detects the standard output is a tty, or terminal. For all other outputs it will auto-enable the *raw output mode*, as in the following example:\n\n    $ redis-cli INCR mycounter > /tmp/output.txt\n    $ cat /tmp/output.txt\n    8\n\nNote that `(integer)` is omitted from the output because `redis-cli` detects\nthe output is no longer written to the terminal. You can force raw output\neven on the terminal with the `--raw` option:\n\n    $ redis-cli --raw INCR mycounter\n    9\n\nYou can force human readable output when writing to a file or in\npipe to other commands by using `--no-raw`.\n\nFor complete command line usage, see [below](#usage)."},{"id":"string-quoting-and-escaping","title":"String quoting and escaping","role":"content","text":"When `redis-cli` parses a command, whitespace characters automatically delimit the arguments.\nIn interactive mode, a newline sends the command for parsing and execution.\nTo input string values that contain whitespaces or non-printable characters, you can use quoted and escaped strings.\n\nQuoted string values are enclosed in double (`\"`) or single (`'`) quotation marks.\nEscape sequences are used to put nonprintable characters in character and string literals.\n\nAn escape sequence contains a backslash (`\\`) symbol followed by one of the escape sequence characters.\n\nDoubly-quoted strings support the following escape sequences:\n\n* `\\\"` - double-quote\n* `\\n` - newline\n* `\\r` - carriage return\n* `\\t` - horizontal tab\n* `\\b` - backspace\n* `\\a` - alert\n* `\\\\` - backslash\n* `\\xhh` - any ASCII character represented by a hexadecimal number (_hh_)\n\nSingle quotes assume the string is literal, and allow only the following escape sequences:\n* `\\'` - single quote\n* `\\\\` - backslash\n\nFor example, to return `Hello World` on two lines:\n\n[code example]\n\nWhen you input strings that contain single or double quotes, as you might in passwords, for example, escape the string, like so: \n\n[code example]"},{"id":"host-port-password-and-database","title":"Host, port, password, and database","role":"content","text":"By default, `redis-cli` connects to the server at the address 127.0.0.1 with port 6379.\nYou can change the port using several command line options. To specify a different host name or an IP address, use the `-h` option. In order to set a different port, use `-p`.\n\n    $ redis-cli -h redis15.localnet.org -p 6390 PING\n    PONG\n\nIf your instance is password protected, the `-a <password>` option will\nperform authentication saving the need of explicitly using the [`AUTH`](https://redis.io/docs/latest/commands/auth) command:\n\n    $ redis-cli -a myUnguessablePazzzzzword123 PING\n    PONG\n\n**NOTE:** For security reasons, provide the password to `redis-cli` automatically via the\n`REDISCLI_AUTH` environment variable.\n\nFinally, it's possible to send a command that operates on a database number\nother than the default number zero by using the `-n <dbnum>` option:\n\n    $ redis-cli FLUSHALL\n    OK\n    $ redis-cli -n 1 INCR a\n    (integer) 1\n    $ redis-cli -n 1 INCR a\n    (integer) 2\n    $ redis-cli -n 2 INCR a\n    (integer) 1\n\nSome or all of this information can also be provided by using the `-u <uri>`\noption and the URI pattern `redis://user:password@host:port/dbnum`:\n\n    $ redis-cli -u redis://LJenkins:p%40ssw0rd@redis-16379.hosted.com:16379/0 PING\n    PONG\n\n**NOTE:**\nUser, password and dbnum are optional.\nFor authentication without a username, use username `default`.\nFor TLS, use the scheme `rediss`.\n\nYou can use the `-4` or `-6` argument to set a preference for IPv4 or IPv6, respectively, for DNS lookups."},{"id":"ssltls","title":"SSL/TLS","role":"content","text":"By default, `redis-cli` uses a plain TCP connection to connect to Redis.\nYou may enable SSL/TLS using the `--tls` option, along with `--cacert` or\n`--cacertdir` to configure a trusted root certificate bundle or directory.\n\nIf the target server requires authentication using a client side certificate,\nyou can specify a certificate and a corresponding private key using `--cert` and\n`--key`."},{"id":"get-input-from-other-programs","title":"Get input from other programs","role":"content","text":"There are two ways you can use `redis-cli` in order to receive input from other\ncommands via the standard input. One is to use the target payload as the last argument\nfrom *stdin*. For example, in order to set the Redis key `net_services`\nto the content of the file `/etc/services` from a local file system, use the `-x`\noption:\n\n    $ redis-cli -x SET net_services < /etc/services\n    OK\n    $ redis-cli GETRANGE net_services 0 50\n    \"#\\n# Network services, Internet style\\n#\\n# Note that \"\n\nIn the first line of the above session, `redis-cli` was executed with the `-x` option and a file was redirected to the CLI's\nstandard input as the value to satisfy the `SET net_services` command phrase. This is useful for scripting.\n\nA different approach is to feed `redis-cli` a sequence of commands written in a\ntext file:\n\n    $ cat /tmp/commands.txt\n    SET item:3374 100\n    INCR item:3374\n    APPEND item:3374 xxx\n    GET item:3374\n    $ cat /tmp/commands.txt | redis-cli\n    OK\n    (integer) 101\n    (integer) 6\n    \"101xxx\"\n\nAll the commands in `commands.txt` are executed consecutively by\n`redis-cli` as if they were typed by the user in interactive mode. Strings can be\nquoted inside the file if needed, so that it's possible to have single\narguments with spaces, newlines, or other special characters:\n\n    $ cat /tmp/commands.txt\n    SET arg_example \"This is a single argument\"\n    STRLEN arg_example\n    $ cat /tmp/commands.txt | redis-cli\n    OK\n    (integer) 25"},{"id":"continuously-run-the-same-command","title":"Continuously run the same command","role":"content","text":"It is possible to execute a single command a specified number of times\nwith a user-selected pause between executions. This is useful in\ndifferent contexts - for example when we want to continuously monitor some\nkey content or [`INFO`](https://redis.io/docs/latest/commands/info) field output, or when we want to simulate some\nrecurring write event, such as pushing a new item into a list every 5 seconds.\n\nThis feature is controlled by two options: `-r <count>` and `-i <delay>`.\nThe `-r` option states how many times to run a command and `-i` sets\nthe delay between the different command calls in seconds (with the ability\nto specify values such as 0.1 to represent 100 milliseconds).\n\nBy default the interval (or delay) is set to 0, so commands are just executed\nASAP:\n\n    $ redis-cli -r 5 INCR counter_value\n    (integer) 1\n    (integer) 2\n    (integer) 3\n    (integer) 4\n    (integer) 5\n\nTo run the same command indefinitely, use `-1` as the count value.\nTo monitor over time the RSS memory size it's possible to use the following command:\n\n    $ redis-cli -r -1 -i 1 INFO | grep rss_human\n    used_memory_rss_human:2.71M\n    used_memory_rss_human:2.73M\n    used_memory_rss_human:2.73M\n    used_memory_rss_human:2.73M\n    ... a new line will be printed each second ..."},{"id":"mass-insertion-of-data-using-redis-cli","title":"Mass insertion of data using `redis-cli`","role":"content","text":"Mass insertion using `redis-cli` is covered in a separate page as it is a\nworthwhile topic itself. Please refer to our [mass insertion guide](https://redis.io/docs/latest/develop/clients/patterns/bulk-loading)."},{"id":"csv-output","title":"CSV output","role":"content","text":"A CSV (Comma Separated Values) output feature exists within `redis-cli` to export data from Redis to an external program.  \n\n    $ redis-cli LPUSH mylist a b c d\n    (integer) 4\n    $ redis-cli --csv LRANGE mylist 0 -1\n    \"d\",\"c\",\"b\",\"a\"\n\nNote that the `--csv` flag will only work on a single command, not the entirety of a DB as an export."},{"id":"run-lua-scripts","title":"Run Lua scripts","role":"content","text":"The `redis-cli` has extensive support for using the debugging facility\nof Lua scripting, available with Redis 3.2 onwards. For this feature, refer to the [Redis Lua debugger documentation](https://redis.io/docs/latest/develop/programmability/lua-debugging).\n\nEven without using the debugger, `redis-cli` can be used to\nrun scripts from a file as an argument:\n\n    $ cat /tmp/script.lua\n    return redis.call('SET',KEYS[1],ARGV[1])\n    $ redis-cli --eval /tmp/script.lua location:hastings:temp , 23\n    OK\n\nThe Redis [`EVAL`](https://redis.io/docs/latest/commands/eval) command takes the list of keys the script uses, and the\nother non key arguments, as different arrays. When calling [`EVAL`](https://redis.io/docs/latest/commands/eval) you\nprovide the number of keys as a number. \n\nWhen calling `redis-cli` with the `--eval` option above, there is no need to specify the number of keys\nexplicitly. Instead it uses the convention of separating keys and arguments\nwith a comma. This is why in the above call you see `location:hastings:temp , 23` as arguments.\n\nSo `location:hastings:temp` will populate the [`KEYS`](https://redis.io/docs/latest/commands/keys) array, and `23` the `ARGV` array.\n\nThe `--eval` option is useful when writing simple scripts. For more\ncomplex work, the Lua debugger is recommended. It is possible to mix the two approaches, since the debugger can also execute scripts from an external file."},{"id":"interactive-mode","title":"Interactive mode","role":"content","text":"We have explored how to use the Redis CLI as a command line program.\nThis is useful for scripts and certain types of testing, however most\npeople will spend the majority of time in `redis-cli` using its interactive\nmode.\n\nIn interactive mode the user types Redis commands at the prompt. The command\nis sent to the server, processed, and the reply is parsed back and rendered\ninto a simpler form to read.\n\nNothing special is needed for running the `redis-cli` in interactive mode -\njust execute it without any arguments\n\n    $ redis-cli\n    127.0.0.1:6379> PING\n    PONG\n\nThe string `127.0.0.1:6379>` is the prompt. It displays the connected Redis server instance's hostname and port.\n\nThe prompt updates as the connected server changes or when operating on a database different from the database number zero:\n\n    127.0.0.1:6379> SELECT 2\n    OK\n    127.0.0.1:6379[2]> DBSIZE\n    (integer) 1\n    127.0.0.1:6379[2]> SELECT 0\n    OK\n    127.0.0.1:6379> DBSIZE\n    (integer) 503"},{"id":"handle-connections-and-reconnections","title":"Handle connections and reconnections","role":"content","text":"Using the `CONNECT` command in interactive mode makes it possible to connect\nto a different instance, by specifying the *hostname* and *port* we want\nto connect to:\n\n    127.0.0.1:6379> CONNECT metal 6379\n    metal:6379> PING\n    PONG\n\nAs you can see the prompt changes accordingly when connecting to a different server instance.\nIf a connection is attempted to an instance that is unreachable, the `redis-cli` goes into disconnected\nmode and attempts to reconnect with each new command:\n\n    127.0.0.1:6379> CONNECT 127.0.0.1 9999\n    Could not connect to Redis at 127.0.0.1:9999: Connection refused\n    not connected> PING\n    Could not connect to Redis at 127.0.0.1:9999: Connection refused\n    not connected> PING\n    Could not connect to Redis at 127.0.0.1:9999: Connection refused\n\nGenerally after a disconnection is detected, `redis-cli` always attempts to\nreconnect transparently; if the attempt fails, it shows the error and\nenters the disconnected state. The following is an example of disconnection\nand reconnection:\n\n    127.0.0.1:6379> INFO SERVER\n    Could not connect to Redis at 127.0.0.1:6379: Connection refused\n    not connected> PING\n    PONG\n    127.0.0.1:6379> \n    (now we are connected again)\n\nWhen a reconnection is performed, `redis-cli` automatically re-selects the\nlast database number selected. However, all other states about the\nconnection is lost, such as within a MULTI/EXEC transaction:\n\n    $ redis-cli\n    127.0.0.1:6379> MULTI\n    OK\n    127.0.0.1:6379> PING\n    QUEUED\n\n    ( here the server is manually restarted )\n\n    127.0.0.1:6379> EXEC\n    (error) ERR EXEC without MULTI\n\nThis is usually not an issue when using the `redis-cli` in interactive mode for\ntesting, but this limitation should be known.\n\nUse the `-t <timeout>` option to specify server timeout in seconds."},{"id":"editing-history-completion-and-hints","title":"Editing, history, completion and hints","role":"content","text":"Because `redis-cli` uses the\n[linenoise line editing library](http://github.com/antirez/linenoise), it\nalways has line editing capabilities, without depending on `libreadline` or\nother optional libraries.\n\nCommand execution history can be accessed in order to avoid retyping commands by pressing the arrow keys (up and down).\nThe history is preserved between restarts of the CLI, in a file named\n`.rediscli_history` inside the user home directory, as specified\nby the `HOME` environment variable. It is possible to use a different\nhistory filename by setting the `REDISCLI_HISTFILE` environment variable,\nand disable it by setting it to `/dev/null`.\n\nThe `redis-cli` client is also able to perform command-name completion by pressing the TAB\nkey, as in the following example:\n\n    127.0.0.1:6379> Z<TAB>\n    127.0.0.1:6379> ZADD<TAB>\n    127.0.0.1:6379> ZCARD<TAB>\n\nOnce Redis command name has been entered at the prompt, the `redis-cli` will display\nsyntax hints. Like command history, this behavior can be turned on and off via the `redis-cli` preferences.\n\nReverse history searches, such as `CTRL-R` in terminals, is supported.\n\nStarting with Redis 8.8, `redis-cli` supports word-jump navigation (`Alt/Option + ←/→`, `Ctrl + ←/→`), which makes\nediting long commands much more efficient."},{"id":"preferences","title":"Preferences","role":"content","text":"There are two ways to customize `redis-cli` behavior. The file `.redisclirc`\nin the home directory is loaded by the CLI on startup. You can override the\nfile's default location by setting the `REDISCLI_RCFILE` environment variable to\nan alternative path. Preferences can also be set during a CLI session, in which \ncase they will last only the duration of the session.\n\nTo set preferences, use the special `:set` command. The following preferences\ncan be set, either by typing the command in the CLI or adding it to the\n`.redisclirc` file:\n\n* `:set hints` - enables syntax hints\n* `:set nohints` - disables syntax hints"},{"id":"run-the-same-command-n-times","title":"Run the same command N times","role":"content","text":"It is possible to run the same command multiple times in interactive mode by prefixing the command\nname by a number:\n\n    127.0.0.1:6379> 5 INCR mycounter\n    (integer) 1\n    (integer) 2\n    (integer) 3\n    (integer) 4\n    (integer) 5"},{"id":"show-online-help-for-redis-commands","title":"Show online help for Redis commands","role":"content","text":"`redis-cli` provides online help for most Redis [commands](/commands), using the `HELP` command. The command can be used\nin two forms:\n\n* `HELP @<category>` shows all the commands about a given category. The\ncategories are: \n    - `@generic`\n    - `@string`\n    - `@list`\n    - `@set`\n    - `@sorted_set`\n    - `@hash`\n    - `@pubsub`\n    - `@transactions`\n    - `@connection`\n    - `@server`\n    - `@scripting`\n    - `@hyperloglog`\n    - `@cluster`\n    - `@geo`\n    - `@stream`\n* `HELP <commandname>` shows specific help for the command given as argument.\n\nFor example in order to show help for the [`PFADD`](https://redis.io/docs/latest/commands/pfadd) command, use:\n\n    127.0.0.1:6379> HELP PFADD\n\n    PFADD key element [element ...]\n    summary: Adds the specified elements to the specified HyperLogLog.\n    since: 2.8.9\n\nNote that `HELP` supports TAB completion as well."},{"id":"clear-the-terminal-screen","title":"Clear the terminal screen","role":"content","text":"Using the `CLEAR` command in interactive mode clears the terminal's screen."},{"id":"special-modes-of-operation","title":"Special modes of operation","role":"content","text":"So far we saw two main modes of `redis-cli`.\n\n* Command line execution of Redis commands.\n* Interactive \"REPL\" usage.\n\nThe CLI performs other auxiliary tasks related to Redis that\nare explained in the next sections:\n\n* Monitoring tool to show continuous stats about a Redis server.\n* Scanning a Redis database for very large keys.\n* Key space scanner with pattern matching.\n* Acting as a [Pub/Sub](https://redis.io/docs/latest/develop/pubsub) client to subscribe to channels.\n* Monitoring the commands executed into a Redis instance.\n* Checking the [latency](https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/latency) of a Redis server in different ways.\n* Checking the scheduler latency of the local computer.\n* Transferring RDB backups from a remote Redis server locally.\n* Acting as a Redis replica for showing what a replica receives.\n* Simulating [LRU](https://redis.io/docs/latest/develop/reference/eviction) workloads for showing stats about keys hits.\n* A client for the Lua debugger."},{"id":"continuous-stats-mode","title":"Continuous stats mode","role":"content","text":"Continuous stats mode is probably one of the lesser known yet very useful features of `redis-cli` to monitor Redis instances in real time. To enable this mode, the `--stat` option is used.\nThe output is very clear about the behavior of the CLI in this mode:\n\n    $ redis-cli --stat\n    ------- data ------ --------------------- load -------------------- - child -\n    keys       mem      clients blocked requests            connections\n    506        1015.00K 1       0       24 (+0)             7\n    506        1015.00K 1       0       25 (+1)             7\n    506        3.40M    51      0       60461 (+60436)      57\n    506        3.40M    51      0       146425 (+85964)     107\n    507        3.40M    51      0       233844 (+87419)     157\n    507        3.40M    51      0       321715 (+87871)     207\n    508        3.40M    51      0       408642 (+86927)     257\n    508        3.40M    51      0       497038 (+88396)     257\n\nIn this mode a new line is printed every second with useful information and differences of request values between old data points. Memory usage, client connection counts, and various other statistics about the connected Redis database can be easily understood with this auxiliary `redis-cli` tool.\n\nThe `-i <interval>` option in this case works as a modifier in order to\nchange the frequency at which new lines are emitted. The default is one\nsecond."},{"id":"scan-for-big-keys-and-memory-usage","title":"Scan for big keys and memory usage","role":"content","text":""},{"id":"big-keys","title":"Big keys","role":"content","text":"In this special mode, `redis-cli` works as a key space analyzer. It scans the\ndataset for big keys, but also provides information about the data types\nthat the data set consists of. This mode is enabled with the `--bigkeys` option,\nand produces verbose output:\n\n    $ redis-cli --bigkeys\n\n    # Scanning the entire keyspace to find biggest keys as well as\n    # average sizes per key type.  You can use -i 0.1 to sleep 0.1 sec\n    # per 100 SCAN commands (not usually needed).\n\n    100.00% ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||\n    Keys sampled: 55\n\n    -------- summary -------\n\n    Total key length in bytes is 495 (avg len 9.00)\n    ... output truncated for AI-facing Markdown ...\n    1 hashs with 3 fields (01.82% of keys, avg size 3.00)\n    3 streams with 8 entries (05.45% of keys, avg size 2.67)\n    2 TDIS-TYPEs with 0 ? (03.64% of keys, avg size 0.00)\n    1 TopK-TYPEs with 0 ? (01.82% of keys, avg size 0.00)\n    2 sets with 5 members (03.64% of keys, avg size 2.50)\n    1 CMSk-TYPEs with 0 ? (01.82% of keys, avg size 0.00)\n    2 zsets with 11 members (03.64% of keys, avg size 5.50)\n    25 ReJSON-RLs with 0 ? (45.45% of keys, avg size 0.00)\n\nIn the first part of the output, each new key larger than the previous larger\nkey (of the same type) encountered is reported. The summary section\nprovides general stats about the data inside the Redis instance.\n\nThe program uses the [`SCAN`](https://redis.io/docs/latest/commands/scan) command, so it can be executed against a busy\nserver without impacting the operations, however the `-i` option can be\nused in order to throttle the scanning process of the specified fraction\nof second for each [`SCAN`](https://redis.io/docs/latest/commands/scan) command. \n\nFor example, `-i 0.01` will slow down the program execution considerably, but will also reduce the load on the server\nto a negligible amount.\n\nNote that the summary also reports in a cleaner form the biggest keys found\nfor each time. The initial output is just to provide some interesting info\nASAP if running against a very large data set.\n\nThe `--bigkeys` option now works on cluster replicas."},{"id":"memory-usage","title":"Memory usage","role":"content","text":"Similar to the `--bigkeys` option, `--memkeys` allows you to scan the entire keyspace to find biggest keys as well as\nthe average sizes per key type.\n\n    $ redis-cli --memkeys\n\n    # Scanning the entire keyspace to find biggest keys as well as\n    # average sizes per key type.  You can use -i 0.1 to sleep 0.1 sec\n    # per 100 SCAN commands (not usually needed).\n\n    100.00% ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||\n    Keys sampled: 55\n\n    -------- summary -------\n\n    Total key length in bytes is 495 (avg len 9.00)\n    ... output truncated for AI-facing Markdown ...\n    1 hashs with 104 bytes (01.82% of keys, avg size 104.00)\n    3 streams with 16960 bytes (05.45% of keys, avg size 5653.33)\n    2 TDIS-TYPEs with 19648 bytes (03.64% of keys, avg size 9824.00)\n    1 TopK-TYPEs with 114256 bytes (01.82% of keys, avg size 114256.00)\n    2 sets with 208 bytes (03.64% of keys, avg size 104.00)\n    1 CMSk-TYPEs with 144056 bytes (01.82% of keys, avg size 144056.00)\n    2 zsets with 304 bytes (03.64% of keys, avg size 152.00)\n    25 ReJSON-RLs with 15748 bytes (45.45% of keys, avg size 629.92)\n\nThe `--memkeys` option now works on cluster replicas."},{"id":"combine---bigkeys-and---memkeys","title":"Combine `--bigkeys` and `--memkeys`","role":"content","text":"You can use the `--keystats` and `--keystats-samples` options to combine `--memkeys` and `--bigkeys` with additional distribution data.\n\n    $ redis-cli --keystats\n\n    # Scanning the entire keyspace to find the biggest keys and distribution information.\n    # Use -i 0.1 to sleep 0.1 sec per 100 SCAN commands (not usually needed).\n    # Use --cursor <n> to start the scan at the cursor <n> (usually after a Ctrl-C).\n    # Use --top <n> to display <n> top key sizes (default is 10).\n    # Ctrl-C to stop the scan.\n\n    100.00% ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||\n    Keys sampled: 55\n    Keys size:    1.30M\n\n    --- Top 10 key sizes ---\n      1    1.00M MBbloomCF  \"bikes:models\"\n    ... output truncated for AI-facing Markdown ...\n    --------- ------------ ------- -------- -------- ------------------ -----------\n    list                 1   1.82%     104B     104B            1 items        1.00\n    string              16  29.09%    1.33K      85B               149B          9B\n    MBbloomCF            1   1.82%    1.00M    1.00M                 -           - \n    hash                 1   1.82%     104B     104B           3 fields        3.00\n    stream               3   5.45%   16.56K    5.52K          8 entries        2.67\n    TDIS-TYPE            2   3.64%   19.19K    9.59K                 -           - \n    TopK-TYPE            1   1.82%  111.58K  111.58K                 -           - \n    set                  2   3.64%     208B     104B          5 members        2.50\n    CMSk-TYPE            1   1.82%  140.68K  140.68K                 -           - \n    zset                 2   3.64%     304B     152B         11 members        5.50\n    ReJSON-RL           25  45.45%   15.38K     629B                 -           -"},{"id":"get-a-list-of-keys","title":"Get a list of keys","role":"content","text":"It is also possible to scan the key space, again in a way that does not\nblock the Redis server (which does happen when you use a command\nlike `KEYS *`), and print all the key names, or filter them for specific\npatterns. This mode, like the `--bigkeys` option, uses the [`SCAN`](https://redis.io/docs/latest/commands/scan) command,\nso keys may be reported multiple times if the dataset is changing, but no\nkey would ever be missing, if that key was present since the start of the\niteration. Because of the command that it uses this option is called `--scan`.\n\n    $ redis-cli --scan | head -10\n    key-419\n    key-71\n    key-236\n    key-50\n    key-38\n    key-458\n    key-453\n    key-499\n    key-446\n    key-371\n\nNote that `head -10` is used in order to print only the first ten lines of the\noutput.\n\nScanning is able to use the underlying pattern matching capability of\nthe [`SCAN`](https://redis.io/docs/latest/commands/scan) command with the `--pattern` option.\n\n    $ redis-cli --scan --pattern '*-11*'\n    key-114\n    key-117\n    key-118\n    key-113\n    key-115\n    key-112\n    key-119\n    key-11\n    key-111\n    key-110\n    key-116\n\nPiping the output through the `wc` command can be used to count specific\nkind of objects, by key name:\n\n    $ redis-cli --scan --pattern 'user:*' | wc -l\n    3829433\n\nYou can use `-i 0.01` to add a delay between calls to the [`SCAN`](https://redis.io/docs/latest/commands/scan) command.\nThis will make the command slower but will significantly reduce load on the server."},{"id":"pubsub-mode","title":"Pub/sub mode","role":"content","text":"The CLI is able to publish messages in Redis Pub/Sub channels using\nthe [`PUBLISH`](https://redis.io/docs/latest/commands/publish) command. Subscribing to channels in order to receive\nmessages is different - the terminal is blocked and waits for\nmessages, so this is implemented as a special mode in `redis-cli`. Unlike\nother special modes this mode is not enabled by using a special option,\nbut simply by using the [`SUBSCRIBE`](https://redis.io/docs/latest/commands/subscribe) or [`PSUBSCRIBE`](https://redis.io/docs/latest/commands/psubscribe) command, which are available in\ninteractive or command mode:\n\n    $ redis-cli PSUBSCRIBE '*'\n    Reading messages... (press Ctrl-C to quit)\n    1) \"PSUBSCRIBE\"\n    2) \"*\"\n    3) (integer) 1\n\nThe *reading messages* message shows that we entered Pub/Sub mode.\nWhen another client publishes some message in some channel, such as with the command `redis-cli PUBLISH mychannel mymessage`, the CLI in Pub/Sub mode will show something such as:\n\n    1) \"pmessage\"\n    2) \"*\"\n    3) \"mychannel\"\n    4) \"mymessage\"\n\nThis is very useful for debugging Pub/Sub issues.\nTo exit the Pub/Sub mode just process `CTRL-C`."},{"id":"monitor-commands-executed-in-redis","title":"Monitor commands executed in Redis","role":"content","text":"Similarly to the Pub/Sub mode, the monitoring mode is entered automatically\nonce you use the [`MONITOR`](https://redis.io/docs/latest/commands/monitor) command. All commands received by the active Redis instance will be printed to the standard output:\n\n    $ redis-cli MONITOR\n    OK\n    1460100081.165665 [0 127.0.0.1:51706] \"set\" \"shipment:8000736522714:status\" \"sorting\"\n    1460100083.053365 [0 127.0.0.1:51707] \"get\" \"shipment:8000736522714:status\"\n\nNote that it is possible to pipe the output, so you can monitor\nfor specific patterns using tools such as `grep`."},{"id":"monitor-the-latency-of-redis-instances","title":"Monitor the latency of Redis instances","role":"content","text":"Redis is often used in contexts where latency is very critical. Latency\ninvolves multiple moving parts within the application, from the client library\nto the network stack, to the Redis instance itself.\n\nThe `redis-cli` has multiple facilities for studying the latency of a Redis\ninstance and understanding the latency's maximum, average and distribution.\n\nThe basic latency-checking tool is the `--latency` option. Using this\noption the CLI runs a loop where the [`PING`](https://redis.io/docs/latest/commands/ping) command is sent to the Redis\ninstance and the time to receive a reply is measured. This happens 100\ntimes per second, and stats are updated in a real time in the console:\n\n    $ redis-cli --latency\n    min: 0, max: 1, avg: 0.19 (427 samples)\n\nThe stats are provided in milliseconds. Usually, the average latency of\na very fast instance tends to be overestimated a bit because of the\nlatency due to the kernel scheduler of the system running `redis-cli`\nitself, so the average latency of 0.19 above may easily be 0.01 or less.\nHowever this is usually not a big problem, since most developers are interested in\nevents of a few milliseconds or more.\n\nSometimes it is useful to study how the maximum and average latencies\nevolve during time. The `--latency-history` option is used for that\npurpose: it works exactly like `--latency`, but every 15 seconds (by\ndefault) a new sampling session is started from scratch:\n\n    $ redis-cli --latency-history\n    min: 0, max: 1, avg: 0.14 (1314 samples) -- 15.01 seconds range\n    min: 0, max: 1, avg: 0.18 (1299 samples) -- 15.00 seconds range\n    min: 0, max: 1, avg: 0.20 (113 samples)^C\n\nSampling sessions' length can be changed with the `-i <interval>` option.\n\nThe most advanced latency study tool, but also the most complex to\ninterpret for non-experienced users, is the ability to use color terminals\nto show a spectrum of latencies. You'll see a colored output that indicates the\ndifferent percentages of samples, and different ASCII characters that indicate\ndifferent latency figures. This mode is enabled using the `--latency-dist`\noption:\n\n    $ redis-cli --latency-dist\n    (output not displayed, requires a color terminal, try it!)\n\nThere is another pretty unusual latency tool implemented inside `redis-cli`.\nIt does not check the latency of a Redis instance, but the latency of the\ncomputer running `redis-cli`. This latency is intrinsic to the kernel scheduler, \nthe hypervisor in case of virtualized instances, and so forth.\n\nRedis calls it *intrinsic latency* because it's mostly opaque to the programmer.\nIf the Redis instance has high latency regardless of all the obvious things\nthat may be the source cause, it's worth to check what's the best your system\ncan do by running `redis-cli` in this special mode directly in the system you\nare running Redis servers on.\n\nBy measuring the intrinsic latency, you know that this is the baseline,\nand Redis cannot outdo your system. In order to run the CLI\nin this mode, use the `--intrinsic-latency <test-time>`. Note that the test time is in seconds and dictates how long the test should run.\n\n    $ ./redis-cli --intrinsic-latency 5\n    Max latency so far: 1 microseconds.\n    Max latency so far: 7 microseconds.\n    Max latency so far: 9 microseconds.\n    Max latency so far: 11 microseconds.\n    Max latency so far: 13 microseconds.\n    Max latency so far: 15 microseconds.\n    Max latency so far: 34 microseconds.\n    Max latency so far: 82 microseconds.\n    Max latency so far: 586 microseconds.\n    Max latency so far: 739 microseconds.\n\n    65433042 total runs (avg latency: 0.0764 microseconds / 764.14 nanoseconds per run).\n    Worst run took 9671x longer than the average latency.\n\nIMPORTANT: this command must be executed on the computer that runs the Redis server instance, not on a different host. It does not connect to a Redis instance and performs the test locally.\n\nIn the above case, the system cannot do better than 739 microseconds of worst\ncase latency, so one can expect certain queries to occasionally run less than 1 millisecond."},{"id":"remote-backups-of-rdb-files","title":"Remote backups of RDB files","role":"content","text":"During a Redis replication's first synchronization, the primary and the replica\nexchange the whole data set in the form of an RDB file. This feature is exploited\nby `redis-cli` in order to provide a remote backup facility that allows a\ntransfer of an RDB file from any Redis instance to the local computer running\n`redis-cli`. To use this mode, call the CLI with the `--rdb <dest-filename>`\noption:\n\n    $ redis-cli --rdb /tmp/dump.rdb\n    SYNC sent to master, writing 13256 bytes to '/tmp/dump.rdb'\n    Transfer finished with success.\n\nThis is a simple but effective way to ensure disaster recovery\nRDB backups exist of your Redis instance. When using this options in\nscripts or `cron` jobs, make sure to check the return value of the command.\nIf it is non zero, an error occurred as in the following example:\n\n    $ redis-cli --rdb /tmp/dump.rdb\n    SYNC with master failed: -ERR Can't SYNC while not connected with my master\n    $ echo $?\n    1"},{"id":"replica-mode","title":"Replica mode","role":"content","text":"The replica mode of the CLI is an advanced feature useful for\nRedis developers and for debugging operations.\nIt allows for the inspection of the content a primary sends to its replicas in the replication\nstream in order to propagate the writes to its replicas. The option\nname is simply `--replica`. The following is a working example:\n\n    $ redis-cli --replica\n    SYNC with master, discarding 13256 bytes of bulk transfer...\n    SYNC done. Logging commands from master.\n    \"PING\"\n    \"SELECT\",\"0\"\n    \"SET\",\"last_name\",\"Enigk\"\n    \"PING\"\n    \"INCR\",\"mycounter\"\n\nThe command begins by discarding the RDB file of the first synchronization\nand then logs each command received in CSV format.\n\nIf you think some of the commands are not replicated correctly in your replicas\nthis is a good way to check what's happening, and also useful information\nin order to improve the bug report."},{"id":"perform-an-lru-simulation","title":"Perform an LRU simulation","role":"content","text":"Redis is often used as a cache with [LRU eviction](https://redis.io/docs/latest/develop/reference/eviction).\nDepending on the number of keys and the amount of memory allocated for the\ncache (specified via the `maxmemory` directive), the amount of cache hits\nand misses will change. Sometimes, simulating the rate of hits is very\nuseful to correctly provision your cache.\n\nThe `redis-cli` has a special mode where it performs a simulation of GET and SET\noperations, using an 80-20% power law distribution in the requests pattern.\nThis means that 20% of keys will be requested 80% of times, which is a\ncommon distribution in caching scenarios.\n\nTheoretically, given the distribution of the requests and the Redis memory\noverhead, it should be possible to compute the hit rate analytically\nwith a mathematical formula. However, Redis can be configured with\ndifferent LRU settings (number of samples) and LRU's implementation, which\nis approximated in Redis, changes a lot between different versions. Similarly\nthe amount of memory per key may change between versions. That is why this\ntool was built: its main motivation was for testing the quality of Redis' LRU\nimplementation, but now is also useful for testing how a given version\nbehaves with the settings originally intended for deployment.\n\nTo use this mode, specify the amount of keys in the test and configure a sensible `maxmemory` setting as a first attempt.\n\nIMPORTANT NOTE: Configuring the `maxmemory` setting in the Redis configuration\nis crucial: if there is no cap to the maximum memory usage, the hit will\neventually be 100% since all the keys can be stored in memory. If too many keys are specified with maximum memory, eventually all of the computer RAM will be used. It is also needed to configure an appropriate\n*maxmemory policy*; most of the time `allkeys-lru` is selected.\n\nIn the following example there is a configured a memory limit of 100MB and an LRU\nsimulation using 10 million keys.\n\nWARNING: the test uses pipelining and will stress the server, don't use it\nwith production instances.\n\n    $ ./redis-cli --lru-test 10000000\n    156000 Gets/sec | Hits: 4552 (2.92%) | Misses: 151448 (97.08%)\n    153750 Gets/sec | Hits: 12906 (8.39%) | Misses: 140844 (91.61%)\n    159250 Gets/sec | Hits: 21811 (13.70%) | Misses: 137439 (86.30%)\n    151000 Gets/sec | Hits: 27615 (18.29%) | Misses: 123385 (81.71%)\n    145000 Gets/sec | Hits: 32791 (22.61%) | Misses: 112209 (77.39%)\n    157750 Gets/sec | Hits: 42178 (26.74%) | Misses: 115572 (73.26%)\n    154500 Gets/sec | Hits: 47418 (30.69%) | Misses: 107082 (69.31%)\n    151250 Gets/sec | Hits: 51636 (34.14%) | Misses: 99614 (65.86%)\n\nThe program shows stats every second. In the first seconds the cache starts to be populated. The misses rate later stabilizes into the actual figure that can be expected:\n\n    120750 Gets/sec | Hits: 48774 (40.39%) | Misses: 71976 (59.61%)\n    122500 Gets/sec | Hits: 49052 (40.04%) | Misses: 73448 (59.96%)\n    127000 Gets/sec | Hits: 50870 (40.06%) | Misses: 76130 (59.94%)\n    124250 Gets/sec | Hits: 50147 (40.36%) | Misses: 74103 (59.64%)\n\nA miss rate of 59% may not be acceptable for certain use cases;\n100MB of memory is not enough. Observe an example using a half gigabyte of memory. After several\nminutes the output stabilizes to the following figures:\n\n    140000 Gets/sec | Hits: 135376 (96.70%) | Misses: 4624 (3.30%)\n    141250 Gets/sec | Hits: 136523 (96.65%) | Misses: 4727 (3.35%)\n    140250 Gets/sec | Hits: 135457 (96.58%) | Misses: 4793 (3.42%)\n    140500 Gets/sec | Hits: 135947 (96.76%) | Misses: 4553 (3.24%)\n\nWith 500MB there is sufficient space for the key quantity (10 million) and distribution (80-20 style)."},{"id":"usage","title":"Usage","role":"syntax","text":"[code example]\n\nOptions:\n\n- `-h <hostname>`: Server hostname (default: 127.0.0.1).\n- `-p <port>`: Server port (default: 6379).\n- `-t <timeout>`: Server connection timeout in seconds (decimals allowed). Default timeout is 0, meaning no limit, depending on the OS.\n- `-s <socket>`: Server socket (overrides hostname and port).\n- `-a <password>`: Password to use when connecting to the server. You can also use the REDISCLI_AUTH environment variable to pass this password more safely (if both are used, this argument takes precedence).\n- `--user <username>`: Used to send ACL style 'AUTH username pass'. Needs -a.\n- `--pass <password>`: Alias of -a for consistency with the new --user option.\n- `--askpass`: Force user to input password with mask from STDIN. If this argument is used, '-a' and REDISCLI_AUTH environment variable will be ignored.\n- `-u <uri>`: Server URI on format redis://user:password@host:port/dbnum User, password and dbnum are optional. For authentication without a username, use username 'default'. For TLS, use the scheme 'rediss'.\n- `-r <repeat>`: Execute specified command N times.\n- `-i <interval>`: When -r is used, waits `<interval>` seconds per command. It is possible to specify sub-second times like -i 0.1. This interval is also used in --scan and --stat per cycle. and in --bigkeys, --memkeys, --keystats, and --hotkeys per 100 cycles.\n- `-n <db>`: Database number.\n- `--name <name>`: Set the client name.\n- `-2`: Start session in RESP2 protocol mode.\n- `-3`: Start session in RESP3 protocol mode.\n- `-x`: Read last argument from STDIN (see example below).\n- `-X`: Read `<tag>` argument from STDIN (see example below).\n- `-d <delimiter>`: Delimiter between response bulks for raw formatting (default: \\n).\n- `-D <delimiter>`: Delimiter between responses for raw formatting (default: \\n).\n- `-c`: Enable cluster mode (follow -ASK and -MOVED redirections).\n- `-e`: Return exit error code when command execution fails.\n- `-4`: Prefer IPv4 over IPv6 on DNS lookup.\n- `-6`: Prefer IPv6 over IPv4 on DNS lookup.\n- `--raw`: Use raw formatting for replies (default when STDOUT is not a tty).\n- `--no-raw`: Force formatted output even when STDOUT is not a tty.\n- `--quoted-input`: Force input to be handled as quoted strings.\n- `--csv`: Output in CSV format.\n- `--json`: Output in JSON format (default RESP3, use -2 if you want to use with RESP2).\n- `--quoted-json`: Same as --json, but produce ASCII-safe quoted strings, not Unicode.\n- `--show-pushes <yn>`: Whether to print RESP3 PUSH messages.  Enabled by default when STDOUT is a tty but can be overridden with --show-pushes no.\n- `--stat`: Print rolling stats about server: mem, clients, ...\n- `--latency`: Enter a special mode continuously sampling latency. Stats (min/max/avg) are reported in milliseconds with sub-millisecond precision. If you use this mode in an interactive session it runs forever displaying real-time stats. Otherwise if --raw or --csv is specified, or if you redirect the output to a non TTY, it samples the latency for 1 second (you can use -i to change the interval), then produces a single output and exits.\n- `--latency-history`: Like --latency but tracking latency changes over time. Default time interval is 15 sec. Change it using -i.\n- `--latency-percentiles <p1,p2,...>`: Also report the given latency percentiles in milliseconds (e.g. 50,99,99.9) in --latency and --latency-history modes. Resolution is limited by the sample count: a percentile finer than 100/samples resolves to the maximum, so use a longer interval (-i) for meaningful high percentiles.\n- `--latency-dist`: Shows latency as a spectrum, requires xterm 256 colors. Default time interval is 1 sec. Change it using -i.\n- `--vset-recall <key>`: Enable VSIM recall test mode for the specified key (that must be a vector set). Random vectors are created mixing components from other elements. A VSIM is then executed and checked against ground truth.\n- `--vset-recall-count <count>`: How many top elements to fetch per query.\n- `--vset-recall-ef <ef>`: HSNW EF (search effort) to use. Default 500.\n- `--vset-recall-ele <count>`: Number of elements used to compose query vectors Default 1.\n- `--lru-test <keys>`: Simulate a cache workload with an 80-20 distribution.\n- `--replica`: Simulate a replica showing commands received from the master.\n- `--rdb <filename>`: Transfer an RDB dump from remote server to local file. Use filename of \"-\" to write to stdout.\n- `--functions-rdb <filename>`: Like --rdb but only get the functions (not the keys) when getting the RDB dump file.\n- `--pipe`: Transfer raw Redis protocol from stdin to server.\n- `--pipe-timeout <n>`: In --pipe mode, abort with error if after sending all data. no reply is received within `<n>` seconds. Default timeout: 30. Use 0 to wait forever.\n- `--bigkeys`: Sample Redis keys looking for keys with many elements (complexity).\n- `--memkeys`: Sample Redis keys looking for keys consuming a lot of memory.\n- `--memkeys-samples <n>`: Sample Redis keys looking for keys consuming a lot of memory. And define number of key elements to sample\n- `--keystats`: Sample Redis keys looking for keys memory size and length (combine bigkeys and memkeys).\n- `--keystats-samples <n>`: Sample Redis keys looking for keys memory size and length. And define number of key elements to sample (only for memory usage).\n- `--cursor <n>`: Start the scan at the cursor `<n>` (usually after a Ctrl-C). Optionally used with --keystats and --keystats-samples.\n- `--top <n>`: To display `<n>` top key sizes (default: 10). Optionally used with --keystats and --keystats-samples.\n- `--hotkeys`: Sample Redis keys looking for hot keys. only works when maxmemory-policy is *lfu.\n- `--scan`: List all keys using the SCAN command.\n- `--pattern <pat>`: Keys pattern when using the --scan, --bigkeys, --memkeys, --keystats or --hotkeys options (default: *).\n- `--count <count>`: Count option when using the --scan, --bigkeys, --memkeys, --keystats or --hotkeys (default: 10).\n- `--quoted-pattern <pat>`: Same as --pattern, but the specified string can be quoted, in order to pass an otherwise non binary-safe string.\n- `--intrinsic-latency <sec>`: Run a test to measure intrinsic system latency. The test will run for the specified amount of seconds.\n- `--eval <file>`: Send an EVAL command using the Lua script at `<file>`.\n- `--ldb`: Used with --eval enable the Redis Lua debugger.\n- `--ldb-sync-mode`: Like --ldb but uses the synchronous Lua debugger, in this mode the server is blocked and script changes are not rolled back from the server memory.\n- `--cluster <command> [args...] [opts...]`: Cluster Manager command and arguments (see below).\n- `--verbose`: Verbose mode.\n- `--no-auth-warning`: Don't show warning message when using password on command line interface.\n- `--help`: Output this help and exit.\n- `--version`: Output version and exit.\n\n#### Cluster Manager Commands\n\nUse --cluster help to list all available cluster manager commands.\n\n\n#### Examples\n\n- `redis-cli -u redis://default:PASSWORD@localhost:6379/0`\n- `cat /etc/passwd | redis-cli -x set mypasswd`\n- `redis-cli -D \"\" --raw dump key > key.dump && redis-cli -X dump_tag restore key2 0 dump_tag replace < key.dump`\n- `redis-cli -r 100 lpush mylist x`\n- `redis-cli -r 100 -i 1 info | grep used_memory_human:`\n- `redis-cli --quoted-input set '\"null-\\x00-separated\"' value`\n- `redis-cli --eval myscript.lua key1 key2 , arg1 arg2 arg3`\n- `redis-cli --scan --pattern '*:12345*'`\n- `redis-cli --scan --pattern '*:12345*' --count 100`\n(Note: when using --eval the comma separates KEYS[] from ARGV[] items)\n\nWhen no command is given, redis-cli starts in interactive mode. Type \"help\" in interactive mode for information on available commands and settings."}],"examples":[{"id":"string-quoting-and-escaping-ex0","language":"plaintext","code":"127.0.0.1:6379> SET mykey \"Hello\\nWorld\"\nOK\n127.0.0.1:6379> GET mykey\nHello\nWorld","section_id":"string-quoting-and-escaping"},{"id":"string-quoting-and-escaping-ex1","language":"plaintext","code":"127.0.0.1:6379> AUTH some_admin_user \">^8T>6Na{u|jp>+v\\\"55\\@_;OU(OR]7mbAYGqsfyu48(j'%hQH7;v*f1H${*gD(Se'\"","section_id":"string-quoting-and-escaping"},{"id":"usage-ex0","language":"text","code":"Usage: redis-cli [OPTIONS] [cmd [arg [arg ...]]]","section_id":"usage"}]}
{"schema_version":2,"id":"develop/tools","title":"Client tools","url":"https://redis.io/docs/latest/develop/tools/","summary":"Tools to interact with a Redis server","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-28T06:39:28-07:00","children":[{"id":"develop/tools/cli","summary":"Overview of redis-cli, the Redis command line interface","title":"Redis CLI","url":"https://redis.io/docs/latest/develop/tools/cli/"},{"id":"develop/tools/insight","summary":"Visualize and optimize Redis data, connect to RDI, and more.","title":"Redis Insight","url":"https://redis.io/docs/latest/develop/tools/insight/"},{"id":"develop/tools/redis-for-vscode","summary":"Connect to Redis from Visual Studio Code.","title":"Redis for VS Code","url":"https://redis.io/docs/latest/develop/tools/redis-for-vscode/"}],"page_type":"content","content_hash":"1931e322fbaf714b02e7a789fb398eedbdf5cd80fd2de5b3a5bde5f325b69a6f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can use several tools to connect to a Redis server, to\nmanage it and interact with the data:\n\n* The [`redis-cli`](#redis-command-line-interface-cli) command line tool\n* [Redis Insight](#redis-insight) (a graphical user interface tool)\n* The Redis [VSCode extension](#redis-vscode-extension)\n* [`redisctl`](#redisctl) (a unified CLI for managing Redis Cloud and Redis Software)\n* [Third-party tools](#third-party-tools) maintained by the community"},{"id":"redis-command-line-interface-cli","title":"Redis command line interface (CLI)","role":"content","text":"The [Redis command line interface](https://redis.io/docs/latest/develop/tools/cli) (also known as `redis-cli`) is a terminal program that sends commands to and reads replies from the Redis server. It has the following two main modes:\n\n1. An interactive Read Eval Print Loop (REPL) mode where the user types Redis commands and receives replies.\n2. A command mode where `redis-cli` is executed with additional arguments, and the reply is printed to the standard output."},{"id":"redis-insight","title":"Redis Insight","role":"content","text":"[Redis Insight](https://redis.io/docs/latest/develop/tools/insight) combines a graphical user interface with Redis CLI to let you work with any Redis deployment. You can visually browse and interact with data, take advantage of diagnostic tools, learn by example, and much more. Best of all, Redis Insight is free.\n\n[Download Redis Insight](https://redis.io/downloads/#insight)."},{"id":"redis-vscode-extension","title":"Redis VSCode extension","role":"content","text":"[Redis for VS Code](https://redis.io/docs/latest/develop/tools/redis-for-vscode)\nis an extension that allows you to connect to your Redis databases from within Microsoft Visual Studio Code. After connecting to a database, you can view, add, modify, and delete keys, and interact with your Redis databases using a Redis Insight like UI and also a built-in CLI interface."},{"id":"redisctl","title":"redisctl","role":"content","text":"[`redisctl`](https://github.com/redis/redisctl) is a unified command-line tool for managing Redis Cloud and Redis Software from your terminal. It provides complete API coverage for both platforms — including subscriptions, databases, VPC peering, ACLs, clusters, and users — without needing custom scripts. It also includes an MCP server component that exposes management operations to AI assistants.\n\nInstall via Homebrew, Cargo, or download a binary release from the [GitHub repository](https://github.com/redis/redisctl)."},{"id":"third-party-tools","title":"Third-party tools","role":"content","text":"The tools above are maintained by Redis. The tools below are maintained by their authors, who are responsible for supporting them."},{"id":"visualeaf","title":"VisuaLeaf","role":"content","text":"[VisuaLeaf](https://visualeaf.com/database/redis/) is a desktop database GUI for macOS, Windows, and Linux, with a free tier and paid plans. It presents Redis in the same interface, and the same connection list, as the MongoDB and SQL databases a team already runs, so a cache and the application database behind it can be inspected in one place. The Redis support is built on [Lettuce](https://github.com/redis/lettuce) and provides:\n\n* A command console and script editor that sends any command through a generic dispatch path — including module commands such as `JSON.GET` and `FT.SEARCH` — with command help, saved scripts, batch runs where each command's reply is shown separately, and command history.\n* A key browser that discovers keys with `SCAN` (never `KEYS`), groups them by prefix on a delimiter you choose, and filters by type or value. Large values are paged rather than fetched whole: lists and sorted sets by index window, hashes and sets by `HSCAN` / `SSCAN` cursor. TTLs are shown and editable, including per-field hash TTLs on servers that support `HTTL`.\n* Server, slow command, and cluster views built from `INFO`, `SLOWLOG GET`, and `CLUSTER INFO` / `CLUSTER NODES`, plus keyspace statistics tallied from a bounded `SCAN` sample with an explicit cap.\n* Security management: ACL users and permissions over the data-plane `ACL` commands, `requirepass` changes, and user management on Redis Enterprise and Redis Cloud through their REST APIs.\n* Per-key JSON export and restore for string, hash, list, set, and sorted set keys, preserving TTL. RDB parsing is out of scope.\n\nIt connects to standalone servers, Cluster deployments via seed nodes, and Sentinel via a master name and sentinel nodes, over plain TCP or TLS (`rediss://`, with optional client certificates), directly or through an SSH tunnel. Reads can be routed to replicas. Redis-compatible servers — Valkey, KeyDB, ElastiCache, Azure Cache — connect the same way. Commands carry the privileges of the user you connect as, so read-only access has to come from a Redis ACL rather than from the tool.\n\n[Download for free on their website](https://visualeaf.com/database/redis/)"},{"id":"libredb-studio","title":"LibreDB Studio","role":"content","text":"[LibreDB Studio](https://github.com/libredb/libredb-studio) is an MIT-licensed, self-hosted database GUI that runs in the browser and is deployed next to the databases it connects to, as a container, a Helm chart, or an npm package. It presents Redis in the same interface as the other engines a team runs, so a cache and the application database behind it can be inspected in one place. The Redis support is built on [`ioredis`](https://github.com/redis/ioredis) and provides:\n\n* A command console that sends any command through a generic dispatch path, including module commands such as `JSON.GET`.\n* A key browser that groups keys by prefix using `SCAN` over a bounded sample of the keyspace, rather than `KEYS`.\n* Server, client, and slow command views built from `INFO`, `CLIENT LIST`, and `SLOWLOG GET`.\n\nIt connects to a single standalone node over an unencrypted connection: Cluster, Sentinel, and TLS are not supported. Commands carry the privileges of the user you connect as, so read-only access has to come from a Redis ACL rather than from the tool."},{"id":"beekeeper-studio","title":"Beekeeper Studio","role":"content","text":"[Beekeeper Studio](https://www.beekeeperstudio.io/db/redis-client/) is a free, open-source database manager that supports a range of databases like Redis, MongoDB, PostgreSQL, MySQL and more.\nBeekeeper Studio is a desktop app, and is available for Linux, macOS, and Windows with full feature parity across all operating systems. \n\nRedis support includes\n\n* A command console with autocomplete, history, saved commands, and multi-tab editing, plus an AI assistant that can turn natural-language questions into Redis commands.\n* A spreadsheet-style key browser for browsing, filtering, and editing values, including JSON documents via the ReJSON module.\n* One-click export of query results to CSV, Excel, or JSON.\n\nIt connects to a Redis server directly or over an SSH tunnel, with TLS/SSL supported, and authenticates with either a password or a username and password against Redis 6+ ACLs.\n\n[Download for free on their website](https://www.beekeeperstudio.io/db/redis-client/)"},{"id":"tabularis","title":"Tabularis","role":"content","text":"[Tabularis](https://github.com/TabularisDB/tabularis) is an open-source desktop SQL workspace for Windows, macOS, and Linux, licensed under Apache 2.0. PostgreSQL, MySQL/MariaDB, and SQLite ship built in; Redis is added as a plugin installed from inside the app (Settings > Available Plugins), so Redis keys appear in the same explorer, data grid, and editor as the relational databases a team already opens there. The Redis plugin, written in Rust, provides:\n\n* A key browser that lists every key with its type, a value preview, and TTL, plus virtual tables that group keys by prefix.\n* Type-specific tables for hashes, lists, sets, sorted sets, and streams, with inline insert, update, and delete from the data grid.\n* Server statistics parsed from `INFO` (version, memory, clients, keyspace) and a list of active Pub/Sub channels with subscriber counts.\n\nIt connects to a single node over TCP or TLS, authenticating with a password or a username and password against Redis 6+ ACLs, and lets you pick logical databases 0 to 15 from the connection form. Cluster and Sentinel discovery are not implemented. A second, read-only Redis plugin written in Go is also available from the same plugin registry.\n\n[Download Tabularis](https://github.com/TabularisDB/tabularis/releases) · [Redis plugin (Rust)](https://github.com/nicholas-papachriston/tabularis-redis-plugin) · [Redis plugin (Go)](https://github.com/gzamboni/tabularis-redis-plugin-go)"}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/copilot-faq","title":"Redis Copilot FAQ","url":"https://redis.io/docs/latest/develop/tools/insight/copilot-faq/","summary":"","aliases":["/develop/connect/insight/copilot-faq"],"tags":["docs","operate","redisinsight"],"last_updated":"2024-11-11T16:18:49Z","page_type":"content","content_hash":"99145ac599efd912b6310e1397c5ea0ef8b2a3593f3609232bf9dc4b32f4b5db","sections":[{"id":"general-questions","title":"General questions","role":"overview","text":""},{"id":"what-is-redis-copilot","title":"What is Redis Copilot?","role":"content","text":"Redis Copilot is an AI-powered developer assistant that helps you learn about Redis, explore your Redis data, and build search queries in a conversational manner.  It is available in our flagship visual GUI developer tool, Redis Insight, as well as within the Redis public documentation (general chatbot)."},{"id":"how-does-redis-copilot-work","title":"How does Redis Copilot work?","role":"content","text":"Redis Copilot is powered by the [OpenAI API](https://platform.openai.com/docs/overview) platform. When it needs to provide context-aware assistance, such as within the **my data** chat in Redis Insight, Redis Copilot will use data from your connected database. Some of that data, such as indexing schemas and sample keys, may be transmitted to the Redis Copilot backend and OpenAI for processing."},{"id":"what-kind-of-tasks-can-redis-copilot-perform","title":"What kind of tasks can Redis Copilot perform?","role":"content","text":"Currently, Redis Copilot provides two primary features: a general chatbot and a context-aware data chatbot.\n\n**General chatbot**: the knowledge-based chatbot serves as an interactive and dynamic documentation interface to simplify the learning process. You can ask specific questions about Redis commands, concepts, and products, and get responses on the fly. The general chatbot is also available in our public docs.\n\n**My data chatbot**: the context-aware chatbot available in Redis Insight lets you construct search queries using everyday language rather than requiring specific programming syntax. This feature lets you query and explore data easily and interactively without extensive technical knowledge."},{"id":"how-do-i-get-started-with-redis-copilot-in-redis-insight","title":"How do I get started with Redis Copilot in Redis Insight?","role":"content","text":"The Redis Copilot instance within our public documentation is free for anyone to use and is available now to answer general questions about Redis.\n\nThe Redis Copilot instance that is embedded in Redis Insight is gradually being rolled out to the user base. Once you are granted access to Redis Copilot in the app, you need to sign in with your Redis Cloud account before you can start using it. If you don’t have an account with Redis Cloud yet, it will be automatically created when you sign in at no extra cost to you."},{"id":"data-and-privacy","title":"Data and Privacy","role":"content","text":""},{"id":"what-data-does-redis-copilot-have-access-to-in-my-database","title":"What data does Redis Copilot have access to in my database?","role":"content","text":"Redis Copilot will have access to any relevant data stored in your database to provide context-aware assistance.\nHowever, this only impacts the **my data** chat in Redis Insight."},{"id":"will-openai-use-my-data-to-train-their-models","title":"Will OpenAI use my data to train their models?","role":"content","text":"OpenAI states that the data provided via OpenAI API is not used for training. Please see the [OpenAI API data privacy page](https://openai.com/api-data-privacy) for the latest information."},{"id":"what-are-the-redis-copilot-terms","title":"What are the Redis Copilot terms?","role":"content","text":"Redis Copilot terms apply to your use of or access to Redis Copilot. They set out what you can expect from Redis Copilot concerning its capabilities and limitations and how Redis Copilot handles your data."},{"id":"feedback","title":"Feedback","role":"content","text":""},{"id":"how-can-i-provide-feedback-or-report-a-bug","title":"How can I provide feedback or report a bug?","role":"content","text":"Redis Copilot is released as Beta in Redis Insight. We welcome your feedback and bug reports. You can submit them through the feedback form available in the [Redis Insight GitHub repository](https://github.com/RedisInsight/RedisInsight)."}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/debugging","title":"Redis Insight debugging information","url":"https://redis.io/docs/latest/develop/tools/insight/debugging/","summary":"Redis Insight debugging information","aliases":["/develop/connect/insight/debugging"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2025-09-03T08:26:45-07:00","page_type":"content","content_hash":"2e27c2ae5227813d8778c55e93f8abdc5f69dab3cd3bf901ce278db723e553af","sections":[{"id":"overview","title":"Overview","role":"overview","text":"If you are experiencing errors or other issues when using Redis Insight, follow the steps below to learn more about the errors and to identify root cause."},{"id":"connection-issues","title":"Connection issues","role":"content","text":"If you experience connection issues, try these steps."},{"id":"1-launch-redis-insight-in-debug-mode","title":"1. Launch Redis Insight in debug mode","role":"content","text":"Run the following command to launch Redis Insight in debug mode to investigate connection issues:\n\n* **Windows**:\n\n    `cmd /C \"set DEBUG=ioredis* && \".\\Redis Insight.exe\"\"`\n\n* **macOS** (from the Applications folder):\n\n    `DEBUG=ioredis* open \"Redis Insight.app\"`\n\n* **Linux**:\n\n    `DEBUG=ioredis* \"redis insight\"`"},{"id":"2-investigate-logs","title":"2. Investigate logs","role":"content","text":"You can review the Redis Insight log files (files with a `.log` extension) to get detailed information about system issues.\nThese are the locations on supported platforms:\n\n- **Docker**: In the `/data/logs` directory *inside the container*.\n- **macOS**: In the `/Users/<your-username>/.redis-insight` directory.\n- **Windows**: In the `C:\\Users\\<your-username>\\.redis-insight` directory.\n- **Linux**: In the `/home/<your-username>/.redis-insight` directory."},{"id":"other-issues","title":"Other issues","role":"content","text":""},{"id":"to-debug-issues-other-than-connectivity","title":"To debug issues other than connectivity","role":"content","text":"* **Windows**:\n\n    `cmd /C \"set DEBUG=* && \".\\Redis Insight.exe\"\"`\n\n* **macOS** (from the Applications folder):\n\n    `DEBUG=* open \"Redis Insight.app\"`\n\n* **Linux**:\n\n    `DEBUG=* \"redis insight\"`"},{"id":"get-detailed-redis-insight-logs","title":"Get detailed Redis Insight logs","role":"content","text":"* **Windows**:\n\n    `cmd /C \"set STDOUT_LOGGER=true && set LOG_LEVEL=debug && set LOGGER_OMIT_DATA=false && \".\\Redis Insight.exe\"\"`\n\n* **macOS** (from the Applications folder):\n\n    `LOG_LEVEL=debug LOGGER_OMIT_DATA=false open \"Redis Insight.app\"`\n\n* **Linux**:\n\n    `LOG_LEVEL=debug LOGGER_OMIT_DATA=false \"redis insight\"`\n\n    Note: if you use LOGGER_OMIT_DATA=false, logs may contain sensitive data."},{"id":"to-log-everything","title":"To log everything","role":"content","text":"* **Windows**:\n\n    `cmd /C \"set STDOUT_LOGGER=true && set LOG_LEVEL=debug && set LOGGER_OMIT_DATA=false && set DEBUG=* && \".\\Redis Insight.exe\"\"`\n\n* **macOS** (from the Applications folder):\n\n    `LOG_LEVEL=debug LOGGER_OMIT_DATA=false DEBUG=* open \"Redis Insight.app\"`\n\n* **Linux**:\n\n    `LOG_LEVEL=debug LOGGER_OMIT_DATA=false DEBUG=* \"redis insight\"`\n\n    Note: if you use LOGGER_OMIT_DATA=false or DEBUG=*, logs may contain sensitive data."}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight","title":"Redis Insight","url":"https://redis.io/docs/latest/develop/tools/insight/","summary":"Visualize and optimize Redis data, connect to RDI, and more.","aliases":["/develop/connect/insight","/ui/insight/","/connect/insight/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-29T11:55:24-07:00","children":[{"id":"develop/tools/insight/copilot-faq","summary":"","title":"Redis Copilot FAQ","url":"https://redis.io/docs/latest/develop/tools/insight/copilot-faq/"},{"id":"develop/tools/insight/rdi-connector","summary":"Connect to RDI from Redis Insight, configure pipelines, and more.","title":"RDI in Redis Insight","url":"https://redis.io/docs/latest/develop/tools/insight/rdi-connector/"},{"id":"develop/tools/insight/search-workspace","summary":"Create and manage search indexes, build and save queries, and explore your data in the Redis Insight Search workspace.","title":"Search workspace","url":"https://redis.io/docs/latest/develop/tools/insight/search-workspace/"},{"id":"develop/tools/insight/insight-stream-consumer","summary":"Learn how to manage streams and consumer groups in Redis Insight","title":"Manage streams and consumer groups in Redis Insight","url":"https://redis.io/docs/latest/develop/tools/insight/insight-stream-consumer/"},{"id":"develop/tools/insight/debugging","summary":"Redis Insight debugging information","title":"Redis Insight debugging information","url":"https://redis.io/docs/latest/develop/tools/insight/debugging/"},{"id":"develop/tools/insight/release-notes","summary":"","title":"Redis Insight release notes","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/"}],"page_type":"content","content_hash":"256dfa1f89fa450cfca0233f0515c1f9fbffd2fd00f58e2089fac48e017a6b5c","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"[![Discord](https://img.shields.io/discord/697882427875393627?style=flat-square)](https://discord.gg/QUkjSsk)\n[![Github](https://img.shields.io/static/v1?label=&message=repository&color=5961FF&logo=github)](https://github.com/redisinsight/redisinsight/)\n\nRedis Insight is a powerful tool for visualizing and optimizing data in Redis, making real-time application development easier and more fun than ever before. Redis Insight lets you do both GUI- and CLI-based interactions in a fully-featured desktop GUI client."},{"id":"installation-and-release-notes","title":"Installation and release notes","role":"setup","text":"<div class=\"grid grid-cols-1 md:grid-cols-3 gap-6 my-8\">\n  \n  \n  \n</div>"},{"id":"overview","title":"Overview","role":"overview","text":""},{"id":"connection-management","title":"Connection management","role":"content","text":"* Automatically discover and add your local Redis databases (that use standalone connection type and do not require authentication).\n* Discover your databases in Redis Software Cluster and databases with Flexible plans in Redis Cloud.\n* Use a form to enter your connection details and add any Redis database running anywhere (including Redis Open Source cluster or sentinel).\n* Connect to a Redis Data Integration (RDI) management plane, create, test, and deploy RDI pipelines, and view RDI statistics.\n\n![The databases screen](https://redis.io/docs/latest/images/ri/ri-databases.png)\n\n> [!NOTE]\n> When you add a Redis database for a particular user using the `username` and `password` fields, that user must be able to run the `INFO` command. See the [access control list (ACL) documentation](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl) for more information."},{"id":"connect-to-azure-managed-redis-with-ease","title":"Connect to Azure Managed Redis with ease","role":"content","text":"Automatically discover databases across subscriptions and connect using Microsoft Entra ID (OAuth) with passwordless authentication and background token refresh. Redis Insight supports both Azure Managed Redis and Azure Cache for Redis tiers, with:\n\n- Auto-discovery of subscriptions and databases\n- One-click import and connection\n- Multi-account support for switching between Azure accounts\n- Improved, user-friendly error handling\n\n> [!NOTE]\n> This feature requires Azure-side configuration. Please coordinate with your Azure administrator and follow [the setup guide](https://github.com/redis/RedisInsight/blob/main/docs/azure-setup.md) to configure the necessary permissions."},{"id":"redis-copilot","title":"Redis Copilot","role":"content","text":"Redis Copilot is an AI-powered developer assistant that helps you learn about Redis, explore your Redis data, and build search queries in a conversational manner. It is available in Redis Insight as well as within the Redis public documentation.\n\nCurrently, Redis Copilot provides two primary features: a general chatbot and a context-aware data chatbot.\n\n**General chatbot**: the knowledge-based chatbot serves as an interactive and dynamic documentation interface to simplify the learning process. You can ask specific questions about Redis commands, concepts, and products, and get responses on the fly. The general chatbot is also available in our public docs.\n\n**My data chatbot**: the context-aware chatbot available in Redis Insight lets you construct search queries using everyday language rather than requiring specific programming syntax. This feature lets you query and explore data easily and interactively without extensive technical knowledge.\n\nBefore you can use Redis Copilot, you must first sign in and accept the terms of use. Click on the Redis Copilot icon in the top right corner of the Redis Insight window to sign in and accept the terms of use.\n\n![The Redis Copilot icon](https://redis.io/docs/latest/images/ri/ri-redis-copilot-icon.png)\n\n![The Redis Copilot sign in screen](https://redis.io/docs/latest/images/ri/ri-redis-copilot-signin.png)\n\nHere's an example of using Redis Copilot to search data using a simple, natural language prompt.\n\n![An example of using Redis Copilot to search data](https://redis.io/docs/latest/images/ri/ri-redis-copilot-query.png)\n\nSee the [Redis Insight Copilot FAQ](https://redis.io/docs/latest/develop/tools/insight/copilot-faq) for more information."},{"id":"rdi-in-redis-insight","title":"RDI in Redis Insight","role":"content","text":"Redis Insight includes Redis Data Integration (RDI) connectivity, which allows you to connect to an RDI management plane, and create, test, and deploy RDI pipelines. Read more about this feature [here](https://redis.io/docs/latest/develop/tools/insight/rdi-connector)."},{"id":"browser","title":"Browser","role":"content","text":"Browse, filter and visualize your key-value Redis data structures.\n* [CRUD](https://en.wikipedia.org/wiki/Create,_read,_update_and_delete) support for lists, hashes, strings, sets, sorted sets, streams, arrays, and vector sets.\n* CRUD support for [JSON](https://redis.io/docs/latest/develop/data-types/json).\n* Group keys according to their namespaces.\n\n* View, validate, and manage your key values in a human-readable format using formatters that prettify and highlight data in different formats (for example, Unicode, JSON, MessagePack, HEX, and ASCII) in the Browser tool.\n\n  ![The Browser tool](https://redis.io/docs/latest/images/ri/ri-browser.png)"},{"id":"profiler","title":"Profiler","role":"content","text":"Analyze every command sent to Redis in real time. To use the profiler, click  **Profiler** at the bottom left of the screen. It should reveal the profiler window, and there you can start the profiler by clicking on **Start Profiler**.\n\n![The Profiler tool](https://redis.io/docs/latest/images/ri/ri-profiler.png)"},{"id":"cli","title":"CLI","role":"content","text":"The CLI is accessible at any time within the application. To use the CLI, click  **>_ CLI** at the bottom left of the screen. It should reveal the CLI window, and there you can start typing Redis [commands](/commands).\n\nThe CLI includes the following features:\n\n* Employs integrated help to deliver intuitive assistance.\n* Use together with a convenient command helper that lets you search and read on Redis commands.\n\n![The CLI tool](https://redis.io/docs/latest/images/ri/ri-cli.png)"},{"id":"workbench","title":"Workbench","role":"content","text":"Workbench is an advanced command line interface with intelligent command auto-complete and complex data visualization support.\n\n* Built-in guides: you can conveniently discover Redis and Redis Open Source features using the built-in guides.\n* Command auto-complete support for all features in Redis and Redis Open Source.\n* Advanced, schema-aware auto-complete for Redis Search, which provides for faster query building with context-sensitive suggestions that recognize indexes, schemas, and fields based on your current query. Start typing any Redis Search command in to try this feature. See below for an example of an in-progress `FT.SEARCH` command.\n\n![An example of an in-progress FT.SEARCH command](https://redis.io/docs/latest/images/ri/ri-workbench.png)\n\nWorkbench also includes:\n\n* Visualizations of your indexes, queries, and aggregations.\n* Visualizations of your [time series](https://redis.io/docs/latest/develop/data-types/timeseries) data.\n\n![Visualizations of time series data](https://redis.io/docs/latest/images/ri/ri-workbench-timeseries.png)"},{"id":"tools","title":"Tools","role":"content","text":""},{"id":"database-analysis","title":"Database analysis","role":"content","text":"Use the database analysis tool to optimize the performance and memory usage of your Redis database. Check data type distribution and memory allocation and review the summary of key expiration time and memory to be freed over time. Inspect the top keys and namespaces sorted by consumed memory or key length and count of keys, respectively. Capture and track the changes in your database by viewing historical analysis reports. Next figure shows a sample database analysis report.\n\n> [!NOTE]\n> The database analysis tool will only analyze up to 10,000 keys. If more than 10,000 keys are present, the tool will attempt to use extrapolation in its analysis.\n\n![The database analysis tool](https://redis.io/docs/latest/images/ri/ri-analysis.png)"},{"id":"redis-streams-support","title":"Redis Streams support","role":"content","text":"Create and manage streams by adding, removing, and filtering entries per timestamp. To see and work with new entries, enable and customize the automatic refresh rate.\n\nView and manage the list of consumer groups. See existing consumers in a given consumer name as well as the last messages delivered to them. Inspect the list of pending messages, explicitly acknowledge the processed items, or claim unprocessed messages via Redis Insight.\n\n![Redis Streams support](https://redis.io/docs/latest/images/ri/ri-streams.png)"},{"id":"search-workspace","title":"Search workspace","role":"content","text":"The dedicated **Search** workspace lets you work with [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) from a single page: browse the search indexes in your database, create indexes from sample or existing data, build and run queries with a schema-aware editor that includes Profile and Explain actions, and save queries to a reusable Query Library. You can also move between the Browser and Search workspaces to make data searchable and view the indexes associated with a key.\n\nRead more about this feature [here](https://redis.io/docs/latest/develop/tools/insight/search-workspace).\n\n![The Search workspace](https://redis.io/docs/latest/images/ri/ri-search-indexes-list.png)"},{"id":"bulk-actions","title":"Bulk actions","role":"content","text":"Easily and quickly delete multiple keys of the same type and/or with the same key name pattern in bulk. To do so, in the List or Tree view, set filters per key type or key names and open the Bulk Actions section. The section displays a summary of all the keys with the expected number of keys that will be deleted based on the set filters.\n\nWhen the bulk deletion is completed, Redis Insight displays the results of this operation with the number of keys processed and the time taken to delete the keys in bulk.\nUse bulk deletion to optimize the usage of your database based on the results from the Redis database analysis.\n\n![Bulk actions](https://redis.io/docs/latest/images/ri/ri-bulk-actions.png)"},{"id":"slow-log","title":"Slow Log","role":"content","text":"The Slow Log tool displays the list of logs captured by the SLOWLOG command to analyze all commands that exceed a specified runtime, which helps with troubleshooting performance issues. Specify both the runtime and the maximum length of Slowlog (which are server configurations) to configure the list of commands logged and set the auto-refresh interval to automatically update the list of commands displayed.\n\n![Slow Log](https://redis.io/docs/latest/images/ri/ri-slow-log.png)"},{"id":"plugins","title":"Plugins","role":"content","text":"With Redis Insight you can now also extend the core functionality by building your own data visualizations. See our [plugin documentation](https://github.com/Redis-Insight/Redis-Insight/wiki/Plugin-Documentation) for more information."},{"id":"telemetry","title":"Telemetry","role":"content","text":"Redis Insight includes an opt-in telemetry system. This help us improve the developer experience of the app. We value your privacy; all collected data is anonymized."},{"id":"log-files","title":"Log files","role":"content","text":"You can review the Redis Insight log files (files with a `.log` extension) to get detailed information about system issues.\nThese are the locations on supported platforms:\n\n- **Docker**: In the `/data/logs` directory *inside the container*.\n- **Mac**: In the `/Users/<your-username>/.redis-insight` directory.\n- **Windows**: In the `C:\\Users\\<your-username>\\.redis-insight` directory.\n- **Linux**: In the `/home/<your-username>/.redis-insight` directory.\n\n> [!NOTE]\n> You can install Redis Insight on operating systems that are not officially supported, but it may not behave as expected."},{"id":"redis-insight-api-only-for-docker","title":"Redis Insight API (only for Docker)","role":"content","text":"If you are running Redis Insight from [Docker](https://redis.io/docs/latest/operate/redisinsight/install/install-on-docker),\nyou can access the API from `http://localhost:5540/api/docs`."},{"id":"feedback","title":"Feedback","role":"content","text":"To provide your feedback, [open a ticket in our Redis Insight repository](https://github.com/Redis-Insight/Redis-Insight/issues/new)."},{"id":"license","title":"License","role":"content","text":"Redis Insight is licensed under [SSPL](https://github.com/Redis-Insight/Redis-Insight/blob/main/LICENSE) license."}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/insight-stream-consumer","title":"Manage streams and consumer groups in Redis Insight","url":"https://redis.io/docs/latest/develop/tools/insight/insight-stream-consumer/","summary":"Learn how to manage streams and consumer groups in Redis Insight","aliases":["/develop/connect/insight/tutorials/insight-stream-consumer","/develop/tools/insight/tutorials/insight-stream-consumer"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-29T16:29:37+01:00","page_type":"content","content_hash":"5cab90d7874a2f7b155316ed9b8902490dec575bb92f3d1436630c1005ab0781","sections":[{"id":"overview","title":"Overview","role":"overview","text":"A _stream_ is an append-only log file. \nWhen you add data to it, you cannot change it. \nThat may seem like a disadvantage; however, a stream serves as a log or single source of truth. \nIt can also be used as a buffer between processes that work at different speeds and do not need to know about each other. \nFor more conceptual information about streams, see [Redis Streams](https://redis.io/docs/latest/develop/data-types/streams). \n\nIn this topic, you will learn how to add and work with streams as well as consumer groups in Redis Insight.\n\nHere's a stream that models temperature and humidity sensors. Processes interacting with the stream perform one of two roles: _consumer_ and _producer_. \nThe point of a stream is that it's not going to end, so you cannot capture whole datasets and do some processing on them. \n \nIn this stream, sensors are considered _producers_, which broadcast data. \nA _consumer_ reads from the stream and does some work on it. \nFor example, if the temperature is above a certain threshold, it puts a message out to turn on the air conditioner in that unit or notify the maintenance.\n\n![A stream that models temperature and humidity sensors.](https://redis.io/docs/latest/images/ri/stream.png)\n\nIt is possible to have multiple consumers doing different jobs, one measuring humidity, and another taking temperature measurements over periods of time. \nRedis stores a copy of the entire dataset in memory, which is a finite resource.\nTo avoid runaway data, streams can be trimmed when you add something to them. \nWhen adding to a stream with [`XADD`](https://redis.io/docs/latest/commands/xadd), you can optionally specify that the stream should be trimmed to a specific or approximate number of the newest entries, or to only include entries whose ID is higher than the ID specified.\nYou can also manage the storage required for streaming data using key expiry.  For example, by writing each day's data to its own stream in Redis and expiring each stream's key after a period of time, say a week.\nAn ID can be any number, but each new entry in the stream must have an ID whose value is higher than the last ID added to the stream."},{"id":"adding-new-entries","title":"Adding new entries","role":"content","text":"Use [`XADD`](https://redis.io/docs/latest/commands/xadd) with `*` for the ID to have Redis automatically generate a new ID for you consisting of a millisecond precision timestamp, a dash and a sequence number.  For example `1656416957625-0`.  Then supply the field names and values to store in the new stream entry.\n\nThere are a couple of ways of retrieving things. You can retrieve entries by time range or you could ask for everything that's happened since a timestamp or ID that you specify. Using a single command you can ask for anything from 10:30 until 11:15 am on a given day."},{"id":"consumer-groups","title":"Consumer groups","role":"content","text":"A more realistic use case would be a system with many temperature sensors whose data Redis puts in a stream, records the time they arrive, and orders them. \n\n![A stream that models temperature and humidity sensors.](https://redis.io/docs/latest/images/ri/consumer.png)\n\nOn the right side we have two consumers that read the stream. One of them is alerting if the temperature is over a certain number and texting the maintenance crew that they need to do something, and the other is a data warehouse that is taking the data and putting it into a database. \n\nThey run independently of each other. \nUp in the right, we have another sort of task. \nLet's assume that alerting and data warehouse are really fast. \nYou get a message whether the temperature is larger than a specific value, which might take a millisecond. \nAnd alerting can keep up with the data flow. \nOne way you can scale consumers is _consumer groups_, which allows multiple instances of the same consumer or same code to work as a team to process the stream."},{"id":"managing-streams-in-redis-insight","title":"Managing streams in Redis Insight","role":"content","text":"You can add a stream in Redis Insight in two ways: create a new stream or add to an existing stream.\n\nTo create a stream, start by selecting the key type (stream). \nYou cannot set time to live (TTL) because it cannot be put on a message in a stream; it can only be done on a Redis key. Name the stream _mystream_. \nThen, set the *Entry ID* to `*` to default to timestamp. \nIf you have your own ID generation strategy, enter the next ID from your sequence. Remember that the ID must be higher than the ID of any other entry in the stream.\n \nThen, enter fields and values using + to add more than one (for example, name and location). \nNow you have a stream that appears in the **Streams** view and you can continue adding fields and values to it.\n\nRedis Insight runs read commands for you so you can see the stream entries in the **Streams** view. \nAnd the **Consumer Groups** view shows each consumers in a given consumer group and the last time Redis allocated a message, what the ID of it was and how many times that process has happened, and whether a consumer has you have told Redis that you are finished working with that task using the [`XACK`](https://redis.io/docs/latest/commands/xack) command."},{"id":"monitor-temperature-and-humidity-from-sensors-in-redis-insight","title":"Monitor temperature and humidity from sensors in Redis Insight","role":"content","text":"This example shows how to bring an existing stream into Redis Insight and work with it."},{"id":"setup","title":"Setup","role":"setup","text":"1. Install [Redis Insight](https://redis.com/redis-enterprise/redis-insight/?_ga=2.48624486.1318387955.1655817244-1963545967.1655260674#insight-form).\n2. Download and install [Node.js](https://nodejs.org/en/download/) (LTS version).\n3. Install [Redis](https://redis.io/docs/latest/operate/oss_and_stack/install). In Docker, check that Redis is running locally on the default port 6379 (with no password set). \n4. Clone the [code repository](https://github.com/redis-developer/introducing-redis-talk) for this example. \nSee the [README](https://github.com/redis-developer/introducing-redis-talk/tree/main/streams) for more information about this example and installation tips.\n5. On your command-line, navigate to the folder containing the code repository and install the Node.js package manager (npm). \n\n \n npm install"},{"id":"run-the-producer","title":"Run the producer","role":"content","text":"To start the producer, which will add a new entry to the stream every few seconds, enter:\n\n\nnpm run producer\n\n> streams@1.0.0 producer\n> node producer.js\n\nStarting producer...\nAdding reading for location: 62, temperature: 40.3, humidity: 36.5\nAdded as 1632771056648-0\nAdding reading for location: 96, temperature: 15.4, humidity: 70\nAdded as 1632771059039-0\n...\n\n\nThe producer runs indefinitely. \nSelect `Ctrl+C` to stop it. \nYou can start multiple instances of the producer if you want to add entries to the stream faster."},{"id":"run-the-consumer","title":"Run the consumer","role":"content","text":"To start the consumer, which reads from the stream every few seconds, enter:\n\n\nnpm run consumer\n\n> streams@1.0.0 consumer\n> node consumer.js\n\nStarting consumer...\nResuming from ID 1632744741693-0\nReading stream...\nReceived entry 1632771056648-0:\n[ 'location', '62', 'temp', '40.3', 'humidity', '36.5' ]\nFinished working with entry 1632771056648-0\nReading stream...\nReceived entry 1632771059039-0:\n[ 'location', '96', 'temp', '15.4', 'humidity', '70' ]\n\n\nThe consumer stores the last entry ID that it read in a Redis string at the key `consumer:lastid`. It uses this string to pick up from where it left off after it is restarted. Try this out by stopping it with `Ctrl+C` and restarting it.\n\nOnce the consumer has processed every entry in the stream, it will wait indefinitely for instances of the producer to add more:\n\n\nReading stream...\nNo new entries since entry 1632771060229-0.\nReading stream...\nNo new entries since entry 1632771060229-0.\nReading stream...\n\n\nStop it using `Ctrl+C`."},{"id":"run-a-consumer-group","title":"Run a consumer group","role":"content","text":"A consumer group consists of multiple consumer instances working together. Redis manages allocation of entries read from the stream to members of a consumer group. A consumer in a group will receive a subset of the entries, with the group as a whole receiving all of them. When working in a consumer group, a consumer process must acknowledge receipt/processing of each entry.\n\nUsing multiple terminal windows, start three instances of the consumer group consumer, giving each a unique name:\n\n\nnpm run consumergroup consumer1\n\n> streams@1.0.0 consumergroup\n> node consumer_group.js -- \"consumer1\"\n\nStarting consumer consumer1...\nConsumer group temphumidity_consumers exists, not created.\nReading stream...\nReceived entry 1632771059039-0:\n[ 'location', '96', 'temp', '15.4', 'humidity', '70' ]\nAcknowledged processing of entry 1632771059039-0.\nReading stream...\n\n\nIn a second terminal:\n\n\nnpm run consumergroup consumer2\n\n\nAnd in a third:\n\n\nnpm run consumergroup consumer3\n\n\nThe consumers will run indefinitely, waiting for new messages to be added to the stream by a producer instance when they have collectively consumed the entire stream. \nNote that in this model, each consumer instance does not receive all of the entries from the stream, but the three members of the group each receive a subset."},{"id":"view-the-stream-in-redis-insight","title":"View the stream in Redis Insight","role":"content","text":"1. Launch Redis Insight.\n2. Select `localhost:6379`\n3. Select **STREAM**. Optionally, select full screen from the upper right corner to expand the view.\n\n![The Streams view in Redis Insight.](https://redis.io/docs/latest/images/ri/ri-streams-cg.png)\n\nYou can now toggle between **Stream** and **Consumer Groups** views to see your data. \nAs mentioned earlier in this topic, a stream is an append-only log so you can't modify the contents of an entry, but you can delete an entire entry. \nA case when that's useful is in the event of a so-called _poison-pill message_ that can cause consumers to crash. You can physically remove such messages in the **Streams** view or use the [`XDEL`](https://redis.io/docs/latest/commands/xdel) command at the command-line interface (CLI).\n\nYou can continue interacting with your stream at the CLI. For example, to get the current length of a stream, use the [`XLEN`](https://redis.io/docs/latest/commands/xlen) command:\n\n\nXLEN ingest:temphumidity\n\n\nUse streams for auditing and processing events in banking, gaming, supply chain, IoT, social media, and so on."},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Redis Streams](https://redis.io/docs/latest/develop/data-types/streams)\n- [Introducing Redis Streams with Redis Insight, node.js, and Python](https://www.youtube.com/watch?v=q2UOkQmIo9Q) (video)"}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/rdi-connector","title":"RDI in Redis Insight","url":"https://redis.io/docs/latest/develop/tools/insight/rdi-connector/","summary":"Connect to RDI from Redis Insight, configure pipelines, and more.","aliases":["/develop/connect/insight/rdi-connector"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-29T16:29:37+01:00","page_type":"content","content_hash":"d9f6ef4b5a486134473f8ce3254a2d50aa0e32de814784360abd90e57ecafcb4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Data Integration (RDI) and its [ingest pipeline capability](https://redis.io/docs/latest/integrate/redis-data-integration) is an end-to-end solution for mirroring your application's primary database in Redis. RDI employs a capture data change mechanism and a stream processor to map and transform source data such as relational tables into fast Redis data structures that match your use cases.\nYou can read more about RDI's ingest architecture [on these pages](https://redis.io/docs/latest/integrate/redis-data-integration/architecture).\n\nAs of version `2.54.0`, Redis Insight includes RDI connectivity, which allows you to connect to [RDI management planes](https://redis.io/docs/latest/integrate/redis-data-integration/architecture#how-rdi-is-deployed), create, test, and deploy [RDI pipelines](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines), and view RDI statistics."},{"id":"connect","title":"Connect","role":"content","text":"Open Redis Insight and then click on the **Redis Data Integration** tab, which looks like this:\n\n![Redis Data Integration tab](https://redis.io/docs/latest/images/ri/ri-rdi-main.png)\n\nNext, click on **Let's connect to RDI**, which will open the **Add RDI Endpoint** dialog.\n\n![Connect to RDI](https://redis.io/docs/latest/images/ri/ri-rdi-add-ep-2.png)\n\nEnter your RDI server details into the dialog and then click **Add Endpoint**. The **RDI Alias** field can be any name you choose and it will be used as the primary name in the **RDI Instances** list view. The **URL** field string needs to begin with \"https://\", followed by the IP address or hostname of your RDI server. You'll receive notification if your connection is successful.\n\n![RDI endpoint added](https://redis.io/docs/latest/images/ri/ri-rdi-add-ep-3.png)"},{"id":"create-test-and-deploy-rdi-pipelines","title":"Create, test, and deploy RDI pipelines","role":"content","text":"Begin by clicking the alias of your newly configured RDI endpoint in the **RDI Instances** view (for example, **Test connection** in the above image), which will reveal the **Pipeline Management** view:\n\n![Start with your pipeline](https://redis.io/docs/latest/images/ri/ri-rdi-pl-main.png)\n\nThe vertical ellipses (\"kebab\") menu, which is adjacent to the **Deploy** button, contains the following options:\n\n- **Download deployed pipeline** - Download an existing pipeline from your RDI configuration.\n- **Import pipeline from ZIP file** - Upload YAML pipeline files from your local computer in ZIP format.\n- **Save pipeline to ZIP file** - Save YAML pipeline files to your local computer in ZIP format.\n\n![Vertical ellipses (kebab) menu](https://redis.io/docs/latest/images/ri/ri-rdi-pl-ellipses-menu.png)\n\nRedis Insight will automatically download the RDI pipeline content from your server when the connection is opened for the first time."},{"id":"download-a-deployed-pipeline-from-your-rdi-configuration","title":"Download a deployed pipeline from your RDI configuration","role":"content","text":"Select the **Download deployed pipeline** option from the kebab menu to download a previously defined pipeline from your RDI configuration. The downloaded pipeline will be displayed in the **Pipeline management** pane. As shown below, each pipeline consists of a configuration file (`config.yaml`) and zero or more `job` YAML files; both shown in the leftmost panel. The configuration file will be displayed in the rightmost editor panel.\n\n![Start with your pipeline](https://redis.io/docs/latest/images/ri/ri-rdi-pl-main.png)"},{"id":"import-a-pipeline-from-your-local-machine","title":"Import a pipeline from your local machine","role":"content","text":"Select the **Import pipeline from ZIP file** option from the kebab menu to upload your configuration and job YAML files from your local computer. The files must be stored in a ZIP file that has the following structure.\n\n[code example]\n\nThe `config.yaml` file, your configuration YAML file, is required. The `jobs` directory can be empty, as job pipelines are not required, but the empty directory must exist in the ZIP file. Otherwise, the `jobs` folder might contain one or more job YAML files."},{"id":"create-a-new-configuration-file-using-the-built-in-editor","title":"Create a new configuration file using the built-in editor","role":"content","text":"If you wish to create a new pipeline from scratch, first delete any existing YAML code from the editor panel. If you're familiar with the structure of a configuration file, you can begin editing. If you're not familiar, you can insert a database template using the **Insert template** button menu, which has the following options:\n\n![Insert template menu](https://redis.io/docs/latest/images/ri/ri-rdi-pl-template-menu.png)\n\nAfter you make your selections and click **Apply**, Redis Insight will populate the editor window with an appropriate template.\n\nThe **Insert template** menu is only available when the editor panel is empty.\n\nSee the [RDI documentation](https://redis.io/docs/latest/integrate/redis-data-integration/reference/config-yaml-reference) for information about required fields."},{"id":"test-your-target-database-connection","title":"Test your target database connection","role":"content","text":"After you've created your **Target database configuration**, you can test the connection using the **Test Connection** button in the bottom right of the editor pane. A new panel will open to the right containing the test results.\n\n![Test connection](https://redis.io/docs/latest/images/ri/ri-rdi-pl-test-cnx.png)"},{"id":"create-a-new-transformation-job-file-using-the-built-in-editor","title":"Create a new transformation job file using the built-in editor","role":"content","text":"In the **Pipeline Management** pane, click the `+` next to the **Transform and Validate** folder label and enter a name for the new transformation job.\n\n![New job](https://redis.io/docs/latest/images/ri/ri-rdi-pl-add-job.png)\n\nNext, click the job name you just created. You can either begin manually editing your transformation job or you can select **Insert template**, which will populate a sample, non-functional job template in the editor.\n\nThe [RDI documentation](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples) has several examples of transformation jobs that can help get you started. Note: RDI uses a very specific YAML format for job files. See [here](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines#job-files) for more information."},{"id":"use-the-built-in-editors","title":"Use the built-in editors","role":"content","text":"The Redis Insight pipeline file editors are context-aware. They provide auto-completion, syntax highlighting, and error detection for:\n\n- YAML files in the configuration and job file editors\n- JMESPath and SQL function snippets in a dedicated editor. To open the JMESPath and SQL editor, click the **SQL and JMESPathEditor** button as shown below. A new editor panel will open in the lower half of the screen. At the bottom-left of that panel, you select either **SQLite functions** or **JMESPath**.\n\n![SQL and JMESPath editors](https://redis.io/docs/latest/images/ri/ri-rdi-pl-addl-editors.png)\n\nHere's a [reference](https://redis.io/docs/latest/integrate/redis-data-integration/reference/jmespath-custom-functions) to the supported JMESPath extension functions and expressions that you can use in your job files.\n\n> [!WARNING]\n> Any changes you make in the editors will be lost if you exit Redis Insight without saving your work. To save any changes you made to your pipeline files, deploy them to your RDI server (see below) or download the modified files as a ZIP file to your local computer. Redis Insight will prepend a green circle on unsaved/undeployed files. Redis Insight will also show errors (if present) using an exclamation point icon.\n\n![Unsaved pipeline](https://redis.io/docs/latest/images/ri/ri-rdi-pl-unsaved.png)"},{"id":"dry-run-transformation-job-pipelines","title":"Dry run transformation job pipelines","role":"content","text":"After you've created a transformation job pipeline, you can execute a dry run on the RDI server. To do that, click on **Dry Run** in the lower right side of the editor panel. A new **Test transformation logic** panel will open to the side. There are two vertically-stacked panes: **Input** and a lower panel that has two tabs: **Transformation output** and **Job output**. In the **Input** section, enter JSON data that will trigger the transformation and then click **Dry run**. Any results will be displayed in the lower section.\n\n![Dry run](https://redis.io/docs/latest/images/ri/ri-rdi-pl-dry-run.png)\n\nThere are two tabs in the lower section:\n\n1. **Transformation output** - this is where you'll see JSON output from your dry run.\n1. **Job output** - this is where you'll see the Redis commands that would have been run in a real scenario."},{"id":"deploy-pipelines-and-add-target-db-to-redis-insight","title":"Deploy pipelines and add target DB to Redis Insight","role":"content","text":"If you're satisfied with your configuration and transformation job pipelines, you can deploy them to the RDI management plane. Click the **Deploy** button to proceed.\n\n![Deploy](https://redis.io/docs/latest/images/ri/ri-rdi-deploy.png)\n\nAfter your pipelines have been deployed, you can add the RDI target Redis database defined in your `config.yaml` file to Redis Insight.\nDoing so will allow you to monitor key creation from your RDI pipeline over time."},{"id":"view-rdi-analytics","title":"View RDI analytics","role":"content","text":"You can view various analytics (statistics) for your RDI deployment. To do so, click  the **Analytics** tab button.\n\n![RDI analytics (statistics)](https://redis.io/docs/latest/images/ri/ri-rdi-analytics-view.png)\n\nAnalytics results are refreshed in an interval of your choosing. You can set the refresh interval by clicking on the arrow to the right of the **Auto refresh** control. You can either set the **Refresh rate** in seconds by clicking on the pencil control, or disable refresh altogether using the **Auto Refresh** slider control.\n\n![RDI analytics refresh](https://redis.io/docs/latest/images/ri/ri-rdi-refresh.png)"}],"examples":[{"id":"import-a-pipeline-from-your-local-machine-ex0","language":"plaintext","code":".\n├── config.yaml\n└── jobs\n    └── bbb.yaml","section_id":"import-a-pipeline-from-your-local-machine"}]}
{"schema_version":2,"id":"develop/tools/insight/release-notes","title":"Redis Insight release notes","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/","summary":"","aliases":["/develop/connect/insight/release-notes"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2024-11-11T16:18:49Z","children":[{"id":"develop/tools/insight/release-notes/v.3.8.0","summary":"Redis Insight v3.8.0","title":"Redis Insight v3.8.0, July 2026","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.3.8.0/"},{"id":"develop/tools/insight/release-notes/v.3.6.0","summary":"Redis Insight v3.6.0","title":"Redis Insight v3.6.0, June 2026","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.3.6.0/"},{"id":"develop/tools/insight/release-notes/v.3.4.2","summary":"Redis Insight v3.4.2","title":"Redis Insight v3.4.2, April 2026","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.3.4.2/"},{"id":"develop/tools/insight/release-notes/v.3.4.1","summary":"Redis Insight v3.4.1","title":"Redis Insight v3.4.1, April 2026","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.3.4.1/"},{"id":"develop/tools/insight/release-notes/v.3.2.0","summary":"Redis Insight v3.2.0","title":"Redis Insight v3.2.0, February 2026","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.3.2.0/"},{"id":"develop/tools/insight/release-notes/v.3.0.3","summary":"Redis Insight v3.0.3","title":"Redis Insight v3.0.3, February 2026","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.3.0.3/"},{"id":"develop/tools/insight/release-notes/v.3.0.2","summary":"Redis Insight v3.0.2","title":"Redis Insight v3.0.2, January 2026","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.3.0.2/"},{"id":"develop/tools/insight/release-notes/v.3.0.0","summary":"Redis Insight v3.0.0","title":"Redis Insight v3.0.0, November 2025","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.3.0.0/"},{"id":"develop/tools/insight/release-notes/v.2.70.1","summary":"Redis Insight v2.70.1","title":"Redis Insight v2.70.1, July 2025","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.70.1/"},{"id":"develop/tools/insight/release-notes/v.2.70.0","summary":"Redis Insight v2.70","title":"Redis Insight v2.70.0, May 2025","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.70.0/"},{"id":"develop/tools/insight/release-notes/v.2.68.0","summary":"Redis Insight v2.68","title":"Redis Insight v2.68.0, April 2025","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.68.0/"},{"id":"develop/tools/insight/release-notes/v.2.66.0","summary":"Redis Insight v2.66","title":"Redis Insight v2.66.0, January 2025","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.66.0/"},{"id":"develop/tools/insight/release-notes/v.2.64.1","summary":"Redis Insight v2.64.1","title":"Redis Insight v2.64.1, December 2024","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.64.1/"},{"id":"develop/tools/insight/release-notes/v.2.64.0","summary":"Redis Insight v2.64","title":"Redis Insight v2.64.0, December 2024","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.64.0/"},{"id":"develop/tools/insight/release-notes/v.2.62.0","summary":"Redis Insight v2.62","title":"Redis Insight v2.62.0, November 2024","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.62.0/"},{"id":"develop/tools/insight/release-notes/v.2.60.0","summary":"Redis Insight v2.60","title":"Redis Insight v2.60.0, October 2024","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.60.0/"},{"id":"develop/tools/insight/release-notes/v.2.58.0","summary":"Redis Insight v2.58","title":"Redis Insight v2.58.0, October 2024","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.58.0/"},{"id":"develop/tools/insight/release-notes/v.2.56.0","summary":"Redis Insight v2.56","title":"Redis Insight v2.56.0, September 2024","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.56.0/"},{"id":"develop/tools/insight/release-notes/v.2.54.0","summary":"Redis Insight v2.54","title":"Redis Insight v2.54.0, August 2024","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.54.0/"},{"id":"develop/tools/insight/release-notes/v.2.52.0","summary":"Redis Insight v2.52","title":"Redis Insight v2.52.0, June 2024","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.52.0/"},{"id":"develop/tools/insight/release-notes/v.2.50.0","summary":"Redis Insight v2.50","title":"Redis Insight v2.50.0, May 2024","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.50.0/"},{"id":"develop/tools/insight/release-notes/v.2.48.0","summary":"Redis Insight v2.48","title":"Redis Insight v2.48.0, April 2024","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.48.0/"},{"id":"develop/tools/insight/release-notes/v.2.46.0","summary":"RedisInsight v2.46","title":"RedisInsight v2.46.0, March 2024","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.46.0/"},{"id":"develop/tools/insight/release-notes/v.2.44.0","summary":"RedisInsight v2.44","title":"RedisInsight v2.44.0, February 2024","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.44.0/"},{"id":"develop/tools/insight/release-notes/v.2.42.0","summary":"RedisInsight v2.42","title":"RedisInsight v2.42.0, January 2024","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.42.0/"},{"id":"develop/tools/insight/release-notes/v.2.40.0","summary":"RedisInsight v2.40","title":"RedisInsight v2.40.0, December 2023","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.40.0/"},{"id":"develop/tools/insight/release-notes/v.2.38.0","summary":"RedisInsight v2.38","title":"RedisInsight v2.38.0, November 2023","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.38.0/"},{"id":"develop/tools/insight/release-notes/v.2.36.0","summary":"RedisInsight v2.36","title":"RedisInsight v2.36.0, October 2023","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.36.0/"},{"id":"develop/tools/insight/release-notes/v.2.34.0","summary":"RedisInsight v2.34","title":"RedisInsight v2.34.0, September 2023","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.34.0/"},{"id":"develop/tools/insight/release-notes/v.2.32.0","summary":"RedisInsight v2.32","title":"RedisInsight v2.32.0, August 2023","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.32.0/"},{"id":"develop/tools/insight/release-notes/v.2.30.0","summary":"RedisInsight v2.30","title":"RedisInsight v2.30.0, July 2023","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.30.0/"},{"id":"develop/tools/insight/release-notes/v2.28.0","summary":"RedisInsight v2.28","title":"RedisInsight v2.28.0, June 2023","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v2.28.0/"},{"id":"develop/tools/insight/release-notes/v.2.26.0","summary":"RedisInsight v2.26","title":"RedisInsight v2.26.0, May 2023","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.26.0/"},{"id":"develop/tools/insight/release-notes/v.2.24.0","summary":"RedisInsight v2.24","title":"RedisInsight v2.24.0, April 2023","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.24.0/"},{"id":"develop/tools/insight/release-notes/v.2.22.1","summary":"RedisInsight v2.22.1","title":"RedisInsight v2.22.1, March 2023","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.22.1/"},{"id":"develop/tools/insight/release-notes/v.2.20.0","summary":"RedisInsight v2.20.0","title":"RedisInsight v2.20.0, February 2023","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.20.0/"},{"id":"develop/tools/insight/release-notes/v.2.18.0","summary":"RedisInsight v2.18.0","title":"RedisInsight v2.18.0, January 2023","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.18.0/"},{"id":"develop/tools/insight/release-notes/v.2.16.0","summary":"RedisInsight v2.16.0","title":"RedisInsight v2.16.0, December 2022","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.16.0/"},{"id":"develop/tools/insight/release-notes/v.2.14.0","summary":"RedisInsight v2.14.0","title":"RedisInsight v2.14.0, November 2022","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.14.0/"},{"id":"develop/tools/insight/release-notes/v2.12.0","summary":"RedisInsight v2.12.0","title":"RedisInsight v2.12.0, October 2022","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v2.12.0/"},{"id":"develop/tools/insight/release-notes/v1.14.0","summary":"RedisInsight v1.14.0","title":"RedisInsight v1.14, may 2023","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.14.0/"},{"id":"develop/tools/insight/release-notes/v2.10.0","summary":"RedisInsight v2.10.0","title":"RedisInsight v2.10.0, September 2022","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v2.10.0/"},{"id":"develop/tools/insight/release-notes/v2.8.0","summary":"RedisInsight v2.8.0","title":"RedisInsight v2.8.0, August 2022","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v2.8.0/"},{"id":"develop/tools/insight/release-notes/v1.13.0","summary":"RedisInsight v1.13.0","title":"RedisInsight v1.13, Aug 2022","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.13.0/"},{"id":"develop/tools/insight/release-notes/v.2.6.0","summary":"RedisInsight v2.6.0","title":"RedisInsight v2.6.0, July 2022","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.6.0/"},{"id":"develop/tools/insight/release-notes/v.2.4.0","summary":"RedisInsight v2.4.0","title":"RedisInsight v2.4.0, June 2022","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.4.0/"},{"id":"develop/tools/insight/release-notes/v2.2.0","summary":"RedisInsight v2.2.0","title":"RedisInsight v2.2.0, May 2022","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v2.2.0/"},{"id":"develop/tools/insight/release-notes/v1.12.0","summary":"RedisInsight v1.12.0","title":"RedisInsight v1.12, May 2022","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.12.0/"},{"id":"develop/tools/insight/release-notes/v2.0","summary":"RedisInsight v2.0.2","title":"RedisInsight v2.0, Nov 2021","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v2.0/"},{"id":"develop/tools/insight/release-notes/v1.11.0","summary":"RedisInsight v1.11.0","title":"RedisInsight v1.11, Oct 2021","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.11.0/"},{"id":"develop/tools/insight/release-notes/v1.10.0","summary":"RedisInsight v1.10.0","title":"RedisInsight v1.10, March 2021","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.10.0/"},{"id":"develop/tools/insight/release-notes/v1.9.0","summary":"RedisInsight v1.9.0","title":"RedisInsight v1.9, January 2021","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.9.0/"},{"id":"develop/tools/insight/release-notes/v1.8.0","summary":"RedisInsight v1.8.0","title":"RedisInsight v1.8, November 2020","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.8.0/"},{"id":"develop/tools/insight/release-notes/v1.7.0","summary":"RediSearch 2.0 support and stability improvements","title":"RedisInsight v1.7, September 2020","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.7.0/"},{"id":"develop/tools/insight/release-notes/v1.6.0","summary":"Rootless Docker container, Copy keys in Browser and Stream UX improvements","title":"RedisInsight v1.6, June 2020","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.6.0/"},{"id":"develop/tools/insight/release-notes/v1.5.0","summary":"New tool for RedisGears, Multi-line query builder and improved suppport of Redis 6 ACLs","title":"RedisInsight v1.5, May 2020","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.5.0/"},{"id":"develop/tools/insight/release-notes/v1.4.0","summary":"Redis 6 ACLs support, improved CLI and full screen support in Graph, TimeSeries and RedisSearch","title":"RedisInsight v1.4, April 2020","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.4.0/"},{"id":"develop/tools/insight/release-notes/v1.3.0","summary":"Auto-discovery of Redis Cloud databases, visualising paths in RedisGraph","title":"RedisInsight v1.3, March 2020","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.3.0/"},{"id":"develop/tools/insight/release-notes/v1.2.0","summary":"TLS Client side authentication support and stability improvements","title":"RedisInsight v1.2, January 2020","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.2.0/"},{"id":"develop/tools/insight/release-notes/v1.1.0","summary":"Stability improvements and other fixes","title":"RedisInsight v1.1, December 2019","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.1.0/"},{"id":"develop/tools/insight/release-notes/v1.0.0","summary":"The initial release after Redis acquired RDBTools","title":"RedisInsight v1.0, November 2019","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.0.0/"}],"page_type":"content","content_hash":"4a23b01a1eedeedaa487ec7951bd174d908c4609e7cd949e19a0db506a8e7135","sections":[{"id":"content","title":"Content","role":"content","text":"Here are the most recent changes for Redis Insight:\n\n\n| Version (Date) | Release notes |\n| --- | --- |\n| v3.8.0 (July 2026) | [Redis Insight v3.8.0, July 2026](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.3.8.0/) |\n| v3.6.0 (June 2026) | [Redis Insight v3.6.0, June 2026](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.3.6.0/) |\n| v3.4.2 (April 2026) | [Redis Insight v3.4.2, April 2026](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.3.4.2/) |\n| v3.4.1 (April 2026) | [Redis Insight v3.4.1, April 2026](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.3.4.1/) |\n| v3.2.0 (February 2026) | [Redis Insight v3.2.0, February 2026](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.3.2.0/) |\n| v3.0.3 (February 2026) | [Redis Insight v3.0.3, February 2026](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.3.0.3/) |\n| v3.0.2 (January 2026) | [Redis Insight v3.0.2, January 2026](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.3.0.2/) |\n| v3.0.0 (November 2025) | [Redis Insight v3.0.0, November 2025](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.3.0.0/) |\n| v2.70.1 (July 2025) | [Redis Insight v2.70.1, July 2025](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.70.1/) |\n| v2.70.0 (May 2025) | [Redis Insight v2.70.0, May 2025](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.70.0/) |\n| v2.68.0 (April 2025) | [Redis Insight v2.68.0, April 2025](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.68.0/) |\n| v2.66.0 (January 2025) | [Redis Insight v2.66.0, January 2025](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.66.0/) |\n| v2.64.1 (December 2024) | [Redis Insight v2.64.1, December 2024](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.64.1/) |\n| v2.64.0 (December 2024) | [Redis Insight v2.64.0, December 2024](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.64.0/) |\n| v2.62.0 (November 2024) | [Redis Insight v2.62.0, November 2024](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.62.0/) |\n| v2.60.0 (October 2024) | [Redis Insight v2.60.0, October 2024](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.60.0/) |\n| v2.58.0 (October 2024) | [Redis Insight v2.58.0, October 2024](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.58.0/) |\n| v2.56.0 (September 2024) | [Redis Insight v2.56.0, September 2024](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.56.0/) |\n| v2.54.0 (August 2024) | [Redis Insight v2.54.0, August 2024](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.54.0/) |\n| v2.52.0 (June 2024) | [Redis Insight v2.52.0, June 2024](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.52.0/) |\n| v2.50.0 (May 2024) | [Redis Insight v2.50.0, May 2024](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.50.0/) |\n| v2.48.0 (April 2024) | [Redis Insight v2.48.0, April 2024](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.48.0/) |\n| v2.46.0 (March 2024) | [RedisInsight v2.46.0, March 2024](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.46.0/) |\n| v2.44.0 (February 2024) | [RedisInsight v2.44.0, February 2024](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.44.0/) |\n| v2.42.0 (January 2024) | [RedisInsight v2.42.0, January 2024](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.42.0/) |\n| v2.40.0 (December 2023) | [RedisInsight v2.40.0, December 2023](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.40.0/) |\n| v2.38.0 (November 2023) | [RedisInsight v2.38.0, November 2023](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.38.0/) |\n| v2.36.0 (October 2023) | [RedisInsight v2.36.0, October 2023](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.36.0/) |\n| v2.34.0 (September 2023) | [RedisInsight v2.34.0, September 2023](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.34.0/) |\n| v2.32.0 (August 2023) | [RedisInsight v2.32.0, August 2023](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.32.0/) |\n| v2.30.0 (July 2023) | [RedisInsight v2.30.0, July 2023](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.30.0/) |\n| v2.28.0 (June 2023) | [RedisInsight v2.28.0, June 2023](https://redis.io/docs/latest/develop/tools/insight/release-notes/v2.28.0/) |\n| v2.26.0 (May 2023) | [RedisInsight v2.26.0, May 2023](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.26.0/) |\n| v2.24.0 (Apr 2023) | [RedisInsight v2.24.0, April 2023](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.24.0/) |\n| v2.22.1 (Mar 2023) | [RedisInsight v2.22.1, March 2023](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.22.1/) |\n| v2.20.0 (Feb 2023) | [RedisInsight v2.20.0, February 2023](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.20.0/) |\n| v2.18.0 (Jan 2023) | [RedisInsight v2.18.0, January 2023](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.18.0/) |\n| v2.16.0 (Dec 2022) | [RedisInsight v2.16.0, December 2022](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.16.0/) |\n| v2.14.0 (Nov 2022) | [RedisInsight v2.14.0, November 2022](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.14.0/) |\n| v2.12.0 (Oct 2022) | [RedisInsight v2.12.0, October 2022](https://redis.io/docs/latest/develop/tools/insight/release-notes/v2.12.0/) |\n| v1.14 (May 2023) | [RedisInsight v1.14, may 2023](https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.14.0/) |\n| v2.10.0 (Sept 2022) | [RedisInsight v2.10.0, September 2022](https://redis.io/docs/latest/develop/tools/insight/release-notes/v2.10.0/) |\n| v2.8.0 (Aug 2022) | [RedisInsight v2.8.0, August 2022](https://redis.io/docs/latest/develop/tools/insight/release-notes/v2.8.0/) |\n| v1.13 (Aug 2022) | [RedisInsight v1.13, Aug 2022](https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.13.0/) |\n| v2.6.0 (July 2022) | [RedisInsight v2.6.0, July 2022](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.6.0/) |\n| v2.4.0 (June 2022) | [RedisInsight v2.4.0, June 2022](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.4.0/) |\n| v2.2.0 (May 2022) | [RedisInsight v2.2.0, May 2022](https://redis.io/docs/latest/develop/tools/insight/release-notes/v2.2.0/) |\n| v1.12 (May 2022) | [RedisInsight v1.12, May 2022](https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.12.0/) |\n| v2.0 (Nov 2021) | [RedisInsight v2.0, Nov 2021](https://redis.io/docs/latest/develop/tools/insight/release-notes/v2.0/) |\n| v1.11 (Oct 2021) | [RedisInsight v1.11, Oct 2021](https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.11.0/) |\n| v1.10 (Mar 2021) | [RedisInsight v1.10, March 2021](https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.10.0/) |\n| v1.9 (Jan 2021) | [RedisInsight v1.9, January 2021](https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.9.0/) |\n| v1.8 (Nov 2020) | [RedisInsight v1.8, November 2020](https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.8.0/) |\n| v1.7 (Sep 2020) | [RedisInsight v1.7, September 2020](https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.7.0/) |\n| v1.6 (June 2020) | [RedisInsight v1.6, June 2020](https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.6.0/) |\n| v1.5 (May 2020) | [RedisInsight v1.5, May 2020](https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.5.0/) |\n| v1.4 (Apr 2020) | [RedisInsight v1.4, April 2020](https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.4.0/) |\n| v1.3 (Mar 2020) | [RedisInsight v1.3, March 2020](https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.3.0/) |\n| v1.2 (Jan 2020) | [RedisInsight v1.2, January 2020](https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.2.0/) |\n| v1.1 (Dec 2019) | [RedisInsight v1.1, December 2019](https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.1.0/) |\n| v1.0 (Nov 2019) | [RedisInsight v1.0, November 2019](https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.0.0/) |"}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.2.14.0","title":"RedisInsight v2.14.0, November 2022","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.14.0/","summary":"RedisInsight v2.14.0","aliases":["/develop/connect/insight/release-notes/v.2.14.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"08c776fde51913796e9e3fa3326d8147aaa84474f5232a1c16a545974ab3df04","sections":[{"id":"2140-november-2022","title":"2.14.0 (November 2022)","role":"overview","text":"This is the General Availability (GA) release of RedisInsight 2.14."},{"id":"highlights","title":"Highlights","role":"content","text":"- Support for [search capabilities](https://redis.io/docs/stack/search/) in Browser: Create secondary index via dedicated form, run queries and full-text search in Browser or Tree views\n- Ability to resize the column width of key values when displaying hashes, lists, and sorted sets\n- Command processing time displayed as part of the result in Workbench"},{"id":"details","title":"Details","role":"content","text":"**Features and improvements**\n- [#1345](https://github.com/RedisInsight/RedisInsight/pull/1345), [#1346](https://github.com/RedisInsight/RedisInsight/pull/1346), [#1376](https://github.com/RedisInsight/RedisInsight/pull/1376) Added support for [search capabilities](https://redis.io/docs/stack/search/) in Browser tool. Create secondary index of your data using a dedicated form. Conveniently run your queries and full-text search against the preselected index and display results in Browser or Tree views. \n- [#1385](https://github.com/RedisInsight/RedisInsight/pull/1385) Resize the column width of key values when displaying hashes, lists, and sorted sets\n- [#1354](https://github.com/RedisInsight/RedisInsight/pull/1407) Do not scroll to the end of results when double-clicking a command output in CLI\n- [#1347](https://github.com/RedisInsight/RedisInsight/pull/1347) Display command processing time as part of the result in Workbench (time taken to process the command by both RedisInsight backend and Redis)\n- [#1351](https://github.com/RedisInsight/RedisInsight/pull/1351) Display the namespaces section in the Database analysis report when no namespaces were found"}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.2.16.0","title":"RedisInsight v2.16.0, December 2022","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.16.0/","summary":"RedisInsight v2.16.0","aliases":["/develop/connect/insight/release-notes/v.2.16.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"65e3b97e6e0751831467226ef65eb932c7a032db941dfb4bf5a519d07d93a415","sections":[{"id":"2160-december-2022","title":"2.16.0 (December 2022)","role":"overview","text":"This is the General Availability (GA) release of RedisInsight 2.16."},{"id":"highlights","title":"Highlights","role":"content","text":"- Bulk import database connections from a file\n- Navigation enhancements for the Tree view\n- Pre-populated host, port, and database alias in the form when adding a new Redis database"},{"id":"details","title":"Details","role":"content","text":"**Features and improvements**\n- [#1492](https://github.com/RedisInsight/RedisInsight/pull/1492), [#1497](https://github.com/RedisInsight/RedisInsight/pull/1497), [#1500](https://github.com/RedisInsight/RedisInsight/pull/1500), [#1502](https://github.com/RedisInsight/RedisInsight/pull/1502) Migrate your database connections from other Redis GUIs, including RESP.app, with the new feature to bulk import database connections from a file.\n- [#1506](https://github.com/RedisInsight/RedisInsight/pull/1506) Pre-populated host (127.0.0.1), port (6379, or 26379 for [Sentinel](https://redis.io/docs/management/sentinel/) connection type), and database alias in the form when adding a new Redis database\n- [#1473](https://github.com/RedisInsight/RedisInsight/pull/1473) **Browser** view is renamed **List** view to avoid confusion with the Browser tool\n- [#1464](https://github.com/RedisInsight/RedisInsight/pull/1464) Navigation enhancements for the Tree view, covering cases when filters are applied, the list of keys is refreshed or the view is switched to the Tree view\n- [#1481](https://github.com/RedisInsight/RedisInsight/pull/1481), [#1482](https://github.com/RedisInsight/RedisInsight/pull/1482), [#1489](https://github.com/RedisInsight/RedisInsight/pull/1489) Indication of new database connections that have been manually added, auto-discovered or imported, but not opened yet\n- [#1499](https://github.com/RedisInsight/RedisInsight/pull/1499) Display values of [JSON](https://redis.io/docs/stack/json/) keys when [JSON.DEBUG MEMORY](https://redis.io/commands/json.debug-memory/) is not available\n\n**Bugs**\n- [#1514](https://github.com/RedisInsight/RedisInsight/pull/1514) Scan the database even when the [DBSIZE](https://redis.io/commands/dbsize/) returns 0"}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.2.18.0","title":"RedisInsight v2.18.0, January 2023","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.18.0/","summary":"RedisInsight v2.18.0","aliases":["/develop/connect/insight/release-notes/v.2.18.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"2e1584cc788a6896267890400c5319b33c4eab421b23d2b450c904c788a8ecf0","sections":[{"id":"2180-january-2023","title":"2.18.0 (January 2023)","role":"overview","text":"This is the General Availability (GA) release of RedisInsight 2.18."},{"id":"highlights","title":"Highlights","role":"content","text":"- Support for SSH tunnel to connect to your Redis database\n- Ability to switch between database indexes while connected to your database\n- Recommendations on how to optimize the usage of your database"},{"id":"details","title":"Details","role":"content","text":"**Features and improvements**\n- [#1567](https://github.com/RedisInsight/RedisInsight/pull/1567), [#1576](https://github.com/RedisInsight/RedisInsight/pull/1576), [#1577](https://github.com/RedisInsight/RedisInsight/pull/1577) Connect to your Redis database via SSH tunnel using a password or private key in PEM format.\n- [#1540](https://github.com/RedisInsight/RedisInsight/pull/1540), [#1608](https://github.com/RedisInsight/RedisInsight/pull/1608) Switch between database indexes while connected to your database in Browser, Workbench, and Database Analysis.\n- [#1457](https://github.com/RedisInsight/RedisInsight/pull/1457), [#1465](https://github.com/RedisInsight/RedisInsight/pull/1465), [#1590](https://github.com/RedisInsight/RedisInsight/pull/1590) Run Database Analysis to generate recommendations on how to save memory and optimize the usage of your database. These recommendations are based on industry standards and Redis best practices. Upvote or downvote recommendations in terms of their usefulness. \n- [#1598](https://github.com/RedisInsight/RedisInsight/pull/1598) Check and highlight the [JSON](https://redis.io/docs/stack/json/) syntax using new [Monaco Editor](https://microsoft.github.io/monaco-editor/).\n- [#1583](https://github.com/RedisInsight/RedisInsight/pull/1583) Click a pencil icon to make changes to database aliases.\n- [#1579](https://github.com/RedisInsight/RedisInsight/pull/1579) Increase the database password length limitation to 10,000."}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.2.20.0","title":"RedisInsight v2.20.0, February 2023","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.20.0/","summary":"RedisInsight v2.20.0","aliases":["/develop/connect/insight/release-notes/v.2.20.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"11dec196a185d019ba50fc8f3bd34720b00d6ce2f9f310c61ecb9655fc1dc64b","sections":[{"id":"2200-february-2023","title":"2.20.0 (February 2023)","role":"overview","text":"This is the General Availability (GA) release of RedisInsight 2.20."},{"id":"highlights","title":"Highlights","role":"content","text":"- Visualizations of [search](https://redis.io/docs/stack/search/) and [graph](https://redis.io/docs/stack/graph/) execution plans in Workbench\n- Guided walkthrough of RedisInsight tools and capabilities for new users\n- Bulk export database connections to a file\n- Upload values of [RedisJSON](https://redis.io/docs/stack/json/) from a file for new keys in Browser\n- Visualizations of [CLIENT LIST](https://redis.io/commands/client-list/) in Workbench\n- See filters previously used in Browser"},{"id":"details","title":"Details","role":"content","text":"**Features and improvements**\n- [#1629](https://github.com/RedisInsight/RedisInsight/pull/1629), [#1739](https://github.com/RedisInsight/RedisInsight/pull/1739), [#1740](https://github.com/RedisInsight/RedisInsight/pull/1740), [#1781](https://github.com/RedisInsight/RedisInsight/pull/1781) Investigate and optimize your [search](https://redis.io/docs/stack/search/) and [graph](https://redis.io/docs/stack/graph/) queries with new visualizations of execution plans in Workbench. Visualizations are supported for [FT.EXPLAIN](https://redis.io/commands/ft.explain/),[FT.PROFILE](https://redis.io/commands/ft.profile/), [GRAPH.EXPLAIN](https://redis.io/commands/graph.explain/), and [GRAPH.PROFILE](https://redis.io/commands/graph.profile/).\n- [#1698](https://github.com/RedisInsight/RedisInsight/pull/1698) Explore RedisInsight's tools and capabilities with a new walkthrough when you start RedisInsight for the first time.\n- [#1631](https://github.com/RedisInsight/RedisInsight/pull/1631), [#1632](https://github.com/RedisInsight/RedisInsight/pull/1632) Migrate your database connections to another RedisInsight instance by performing a bulk export of database connections to a file.\n- [#1741](https://github.com/RedisInsight/RedisInsight/pull/1741) Upload [RedisJSON](https://redis.io/docs/stack/json/) values from a file for new keys in Browser.\n- [#1653](https://github.com/RedisInsight/RedisInsight/pull/1653) Analyze client connections using new Workbench visualizations for [CLIENT LIST](https://redis.io/commands/client-list/).\n- [#1625](https://github.com/RedisInsight/RedisInsight/pull/1625) Quickly set filters previously used in Browser by selecting them from the list of recently used filters.\n- [#1713](https://github.com/RedisInsight/RedisInsight/pull/1713) See your new Redis keys added in Browser without a need to refresh the list of keys.\n- [#1681](https://github.com/RedisInsight/RedisInsight/pull/1681), [#1692](https://github.com/RedisInsight/RedisInsight/pull/1692), [#1693](https://github.com/RedisInsight/RedisInsight/pull/1693) Avoid the timeout connection errors by configuring the connection timeout for databases added manually via host and port.\n- [#1696](https://github.com/RedisInsight/RedisInsight/pull/1696), [#1703](https://github.com/RedisInsight/RedisInsight/pull/1703) Test the database connection before adding the database.\n- [#1689](https://github.com/RedisInsight/RedisInsight/pull/1689) Update the port of an existing database connection instead of adding a new one.\n- [#1731](https://github.com/RedisInsight/RedisInsight/pull/1731) Use database indexes based on [INFO keyspace](https://redis.io/commands/info/).\n\n**Bugs**\n- [#1678](https://github.com/RedisInsight/RedisInsight/pull/1678) Prevent crashes when SSH is set up on Linux.\n- [#1697](https://github.com/RedisInsight/RedisInsight/pull/1697) Prevent crashes when working with [Redis streams](https://redis.io/docs/data-types/streams) with large IDs."}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.2.22.1","title":"RedisInsight v2.22.1, March 2023","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.22.1/","summary":"RedisInsight v2.22.1","aliases":["/develop/connect/insight/release-notes/v.2.22.1/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"e76fffacb18d42a543068dc2ed8058587e78b6e951a3e3badbef86b26070a496","sections":[{"id":"2221-march-2023","title":"2.22.1 (March 2023)","role":"overview","text":"This is the General Availability (GA) release of RedisInsight 2.22."},{"id":"highlights","title":"Highlights","role":"content","text":"- Share your Redis expertise with your team and the wider community by building custom RedisInsight tutorials. Use our [instructions](https://github.com/RedisInsight/Tutorials) to describe your implementations of Redis for other users to follow and interact with in the context of a connected Redis database\n- Take a quick tour of RedisInsight to discover how it can enhance your development experience when building with Redis or Redis Stack\n- Select from a list of supported decompression formats to view your data in a human-readable format"},{"id":"details","title":"Details","role":"content","text":"**Features and improvements**\n- [#1782](https://github.com/RedisInsight/RedisInsight/pull/1782), [#1813](https://github.com/RedisInsight/RedisInsight/pull/1813) Share your Redis expertise with your team and the wider community by building custom RedisInsight tutorials. The tutorials use markdown and are easy to write. They are an ideal way to describe practical implementations of Redis so users can follow and interact with commands in the context of an already connected Redis database. Check out these [instructions](https://github.com/RedisInsight/Tutorials) to start creating your own tutorials. Let the community discover your content by labeling your GitHub repository with [redis-tutorials](https://github.com/topics/redis-tutorials)\n- [#1834](https://github.com/RedisInsight/RedisInsight/pull/1834) Take a quick tour of RedisInsight to discover how it can enhance your development experience. To start the tour, in the left-side navigation, open the Help Center (above the Settings icon), reset the onboarding and open the Browser page\n- [#1742](https://github.com/RedisInsight/RedisInsight/pull/1742), [#1753](https://github.com/RedisInsight/RedisInsight/pull/1753), [#1755](https://github.com/RedisInsight/RedisInsight/pull/1755), [#1762](https://github.com/RedisInsight/RedisInsight/pull/1762) Configure one of the following data decompression formats when adding a database connection to view your data in a human-readable format: GZIP, LZ4, ZSTD, SNAPPY\n- [#1787](https://github.com/RedisInsight/RedisInsight/pull/1787) Added UX improvements to the search by values of keys feature in Browser: Enable the search box after the index is selected\n\n**Bugs**\n- [#1808](https://github.com/RedisInsight/RedisInsight/pull/1808) Prevent errors when running Docker RedisInsight on Safari Version 16.2\n- [#1835](https://github.com/RedisInsight/RedisInsight/pull/1835) Display total memory and total keys for replicas in Sentinel"}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.2.24.0","title":"RedisInsight v2.24.0, April 2023","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.24.0/","summary":"RedisInsight v2.24","aliases":["/develop/connect/insight/release-notes/v.2.24.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"6514929e5cc7906f1da7e2b3bac4e01b8a977204ded4f8439663fb764c5819a0","sections":[{"id":"224-april-2023","title":"2.24 (April 2023)","role":"overview","text":"This is the General Availability (GA) release of RedisInsight 2.24."},{"id":"highlights","title":"Highlights","role":"content","text":"- Bulk data upload: Upload the list of Redis commands from a text file using the new bulk action in the Browser tool. Use the bulk data upload with custom RedisInsight tutorials to quickly load your sample dataset.\n- Support for images in custom tutorials: showcase your Redis expertise with your team and the wider community by building shareable RedisInsight tutorials.\n- JSON formatter support for the [JSON.GET](https://redis.io/commands/json.get/), [JSON.MGET](https://redis.io/commands/json.mget/), and [GET](https://redis.io/commands/get/) command output in Workbench.\n- Added Brotli and PHP GZcompress to the list of supported decompression formats to view your data in a human-readable format."},{"id":"details","title":"Details","role":"content","text":"**Features and improvements**\n- [#1930](https://github.com/RedisInsight/RedisInsight/pull/1930), [#1961](https://github.com/RedisInsight/RedisInsight/pull/1961) Upload the list of Redis commands from a text file using the new bulk action in the Browser tool. Use the bulk data upload with custom RedisInsight tutorials to quickly load your sample dataset.\n- [#1936](https://github.com/RedisInsight/RedisInsight/pull/1936), [#1939](https://github.com/RedisInsight/RedisInsight/pull/1939) Added support for images in custom tutorials, available in Workbench. Showcase your Redis expertise with your team and the wider community by building shareable tutorials. Use markdown syntax described in our [instructions](https://github.com/RedisInsight/Tutorials) to build tutorials.\n- [#1946](https://github.com/RedisInsight/RedisInsight/pull/1946) See the output of [JSON.GET](https://redis.io/commands/json.get/), [JSON.MGET](https://redis.io/commands/json.mget/), and [GET](https://redis.io/commands/get/) formatted as JSON in Workbench.\n- [#1876](https://github.com/RedisInsight/RedisInsight/pull/1876) Ability to directly delete a key in the Browser list of keys without having to view its values.\n- [#1889](https://github.com/RedisInsight/RedisInsight/pull/1889), [#1900](https://github.com/RedisInsight/RedisInsight/pull/1900) Added Brotli and PHP GZcompress to the list of supported decompression formats to view your data in a human-readable format. Decompression format is configurable when adding a database connection.\n- [#1886](https://github.com/RedisInsight/RedisInsight/pull/1886) Enhanced command syntax in CLI, Workbench, and Command Helper to align with [command documentation](https://redis.io/commands/).\n- [#1975](https://github.com/RedisInsight/RedisInsight/pull/1975) [Renamed](https://github.com/RedisInsight/RedisInsight/issues/1902) the \"Display On System Tray\" to \"Show in Menu Bar\" on macOS.\n\n**Bugs**\n- [#1990](https://github.com/RedisInsight/RedisInsight/pull/1990) Keep the previously specified SNI parameters when a database connection is edited.\n- [#1999](https://github.com/RedisInsight/RedisInsight/pull/1999) Keep the previously set database index when a database connection is edited."}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.2.26.0","title":"RedisInsight v2.26.0, May 2023","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.26.0/","summary":"RedisInsight v2.26","aliases":["/develop/connect/insight/release-notes/v.2.26.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"523a0d6203c970531c3af2b7306e330ad777d5cf8566d6be5cc88c5b2df46d14","sections":[{"id":"226-may-2023","title":"2.26 (May 2023)","role":"overview","text":"This is the General Availability (GA) release of RedisInsight 2.26."},{"id":"highlights","title":"Highlights","role":"content","text":"- Introducing Insights (Beta): a new right-side panel that displays contextualised database recommendations for optimizing performance and memory usage. The list of recommendations gets updated as you interact with your database. Check out the paired-up tutorials to learn about the recommended feature and vote to provide feedback. This functionality is being rolled out gradually to the user base.\n- Support for bulk data upload in custom tutorials: quickly upload sample datasets from your custom RedisInsight tutorials to share your Redis expertise with your team and the wider community."},{"id":"details","title":"Details","role":"content","text":"**Features and improvements**\n- [#1847](https://github.com/RedisInsight/RedisInsight/pull/1847), [#1901](https://github.com/RedisInsight/RedisInsight/pull/1901), [#1957](https://github.com/RedisInsight/RedisInsight/pull/1957), [#1972](https://github.com/RedisInsight/RedisInsight/pull/1972) Launching Insights (Beta): a new right-side panel that displays contextualised database recommendations for optimizing performance and memory usage. The list of recommendations gets updated in real-time as you interact with your database taking into account database configuration, user actions and accessed data. Consult the paired-up tutorials to learn more about the recommended feature. This functionality is being rolled out gradually to the user base in order to allow the RedisInsight team to learn and adjust the recommendations. Provide feedback directly in the app or the [GitHub repository](https://github.com/RedisInsight/RedisInsight/issues). \n- [#2019](https://github.com/RedisInsight/RedisInsight/pull/2019) Quickly upload sample data sets in bulk from your custom RedisInsight tutorials to share your Redis expertise with your team and the wider community. Use a text file with the list of Redis commands and follow our simple [instructions](https://github.com/RedisInsight/Tutorials) to include example data sets in your custom RedisInsight tutorials\n- [#2010](https://github.com/RedisInsight/RedisInsight/pull/2010), [#2012](https://github.com/RedisInsight/RedisInsight/pull/2012), [#2013](https://github.com/RedisInsight/RedisInsight/pull/2013) Optimized the logic when filtering per data type in Browser to avoid unnecessary [TYPE](https://redis.io/commands/type/) commands\n\n**Bugs**\n- [#2014](https://github.com/RedisInsight/RedisInsight/pull/2014) Display the actual command processing time in Workbench when results are grouped"}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.2.30.0","title":"RedisInsight v2.30.0, July 2023","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.30.0/","summary":"RedisInsight v2.30","aliases":["/develop/connect/insight/release-notes/v.2.30.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"a729a7e312aa52bc1d199dcec93b31a5630f0b61644653804e058cbe00a61ca3","sections":[{"id":"230-july-2023","title":"2.30 (July 2023)","role":"overview","text":"This is the General Availability (GA) release of RedisInsight 2.30."},{"id":"highlights","title":"Highlights","role":"content","text":"Introducing support for [triggers and functions](https://github.com/RedisGears/RedisGears/) that bring application logic closer to your data and give Redis powerful features for event-driven data processing"},{"id":"details","title":"Details","role":"content","text":"**Features and improvements**\n\n[#2247](https://github.com/RedisInsight/RedisInsight/pull/2247), [#2249](https://github.com/RedisInsight/RedisInsight/pull/2249), [#2273](https://github.com/RedisInsight/RedisInsight/pull/2273), [#2279](https://github.com/RedisInsight/RedisInsight/pull/2279) Support for [triggers and functions](https://github.com/RedisGears/RedisGears/) that add the capability to execute server-side functions triggered by events or data operations to:\n - Speed up applications by running the application logic where the data lives\n - Eliminate the need to maintain the same code across different applications by moving application functionality inside the Redis database\n - Maintain consistent data when applications react to any keyspace change\n - Improve code resiliency by backing up and replicating triggers and functions along with the database\n\nTriggers and functions work with a JavaScript engine, which lets you take advantage of JavaScript’s vast ecosystem of libraries and frameworks and modern, expressive syntax."}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.2.32.0","title":"RedisInsight v2.32.0, August 2023","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.32.0/","summary":"RedisInsight v2.32","aliases":["/develop/connect/insight/release-notes/v.2.32.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"2608e5b6e0b48626b49ad30b22d0ba17fc00c3eab2381f0fb506ff6da2f11c3c","sections":[{"id":"232-august-2023","title":"2.32 (August 2023)","role":"overview","text":"This is the General Availability (GA) release of RedisInsight 2.32."},{"id":"highlights","title":"Highlights","role":"content","text":"- Easily provision a free database to use with the RedisInsight interactive tutorials to learn, among others, how to leverage Vector Similarity Search for your AI use cases or discover the power of the native JSON data structure supporting structured querying and full-text search. Take advantage of the in-app social sign-up to [Redis Cloud](https://redis.com/comparisons/oss-vs-enterprise/?utm_source=redisinsight&utm_medium=rel_notes&utm_campaign=2_32) to quickly provision a free database with [Redis Stack’s capabilities](https://redis.io/docs/about/about-stack/?utm_source=redisinsight&utm_medium=rel_notes&utm_campaign=2_32). Try the [latest 7.2](https://redis.com/blog/introducing-redis-7-2/?utm_source=redisinsight&utm_medium=rel_notes&utm_campaign=2_32) release which delivers the new [Triggers and Functions](https://redis.com/blog/introducing-triggers-and-functions/?utm_source=redisinsight&utm_medium=rel_notes&utm_campaign=2_32) feature, allowing you to execute server-side functions written in JavaScript that are either triggered by a keyspace change, by a stream entry arrival, or by explicitly calling them, empowering developers to build and maintain real-time applications by moving business logic closer to the data, ensuring a lower latency whilst delivering the best developer experience.\n- Select a custom installation directory on Windows OS for when multi-user access to the app is required."},{"id":"details","title":"Details","role":"content","text":"**Features and improvements**\n \n- [#2270](https://github.com/RedisInsight/RedisInsight/pull/2270), [#2271](https://github.com/RedisInsight/RedisInsight/pull/2271), [#2437](https://github.com/RedisInsight/RedisInsight/pull/2437) Added the ability to quickly provision a free [Redis Cloud](https://redis.com/comparisons/oss-vs-enterprise/?utm_source=redisinsight&utm_medium=rel_notes&utm_campaign=2_32) database via in-app social signup (Google or GitHub). Use the database with the RedisInsight interactive tutorials or try the [latest 7.2](https://redis.com/blog/introducing-redis-7-2/?utm_source=redisinsight&utm_medium=rel_notes&utm_campaign=2_32) release which delivers the new [Triggers and Functions](https://redis.com/blog/introducing-triggers-and-functions/?utm_source=redisinsight&utm_medium=rel_notes&utm_campaign=2_32) feature. To quickly create and automatically add a free Redis Cloud database to RedisInsight, click the \"Try Redis Cloud\" banner in the list of database connections page and follow the steps.\n- [#2455](https://github.com/RedisInsight/RedisInsight/pull/2455) Select a custom installation directory on Windows OS\n- [#2373](https://github.com/RedisInsight/RedisInsight/pull/2373), [#2387](https://github.com/RedisInsight/RedisInsight/pull/2387) Delete all command results in Workbench at once\n- [#2458](https://github.com/RedisInsight/RedisInsight/pull/2458) Added in-app hints in Browser and Workbench to get started with RedisInsight interactive tutorials\n- [#2422](https://github.com/RedisInsight/RedisInsight/pull/2422) Ignore the empty lines in files when uploading data in bulk\n- [#2470](https://github.com/RedisInsight/RedisInsight/pull/2470) Preset the header containing the engine, the API version and the library name in the JavaScript file when creating a new library within the [Triggers and Functions](https://redis.com/blog/introducing-triggers-and-functions/?utm_source=redisinsight&utm_medium=main&utm_campaign=main) tool"}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.2.34.0","title":"RedisInsight v2.34.0, September 2023","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.34.0/","summary":"RedisInsight v2.34","aliases":["/develop/connect/insight/release-notes/v.2.34.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"6336f2a5366f85a5dcc968162ff8c4a4c213bd40f385511d31ed246816e811f5","sections":[{"id":"234-september-2023","title":"2.34 (September 2023)","role":"overview","text":"This is the General Availability (GA) release of RedisInsight 2.34."},{"id":"highlights","title":"Highlights","role":"content","text":"- UX improvements to simplify the in-app provisioning and usage of a free [Redis Cloud](https://redis.com/comparisons/oss-vs-enterprise/?utm_source=redisinsight&utm_medium=rel_notes&utm_campaign=2_34) database with RedisInsight interactive tutorials. This will allow you to learn easily, among other things, how to leverage the native JSON data structure supporting structured querying and full-text search, including vector similarity search for your AI use cases\n- Ability to refresh the list of [search indexes](https://redis.io/docs/interact/search-and-query/?utm_source=redisinsight&utm_medium=main&utm_campaign=main) displayed in Browser\n- Set the color theme to follow the local system preferences"},{"id":"details","title":"Details","role":"content","text":"**Features and improvements** \n- [#2585](https://github.com/RedisInsight/RedisInsight/pull/2585) UX improvements to simplify the in-app provisioning and usage of a free [Redis Cloud](https://redis.com/comparisons/oss-vs-enterprise/?utm_source=redisinsight&utm_medium=rel_notes&utm_campaign=2_34) database with RedisInsight interactive tutorials. To provision a new database, click the \"Try Redis Cloud\" banner in the list of database connections page and follow the steps\n- [#2606](https://github.com/RedisInsight/RedisInsight/pull/2606) Ability to refresh the list of [search indexes](https://redis.io/docs/interact/search-and-query/?utm_source=redisinsight&utm_medium=main&utm_campaign=main) displayed in Browser\n- [#2593](https://github.com/RedisInsight/RedisInsight/pull/2593) UX optimizations to improve the back navigation to the list of databases, including for small resolutions\n- [#2599](https://github.com/RedisInsight/RedisInsight/pull/2599) Added an option to set the color theme to follow the local system preferences\n- [#2563](https://github.com/RedisInsight/RedisInsight/pull/2563) Load a new library from the Functions tab within the [Triggers and Functions](https://redis.com/blog/introducing-triggers-and-functions/?utm_source=redisinsight&utm_medium=main&utm_campaign=main) tool\n- [#2496](https://github.com/RedisInsight/RedisInsight/pull/2496) Set milliseconds as a default unit in Slow Log\n\n**Bugs**\n- [#2587](https://github.com/RedisInsight/RedisInsight/pull/2587) Display detailed [errors](https://github.com/RedisInsight/RedisInsight/issues/2562) in transactions run via CLI or Workbench"}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.2.36.0","title":"RedisInsight v2.36.0, October 2023","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.36.0/","summary":"RedisInsight v2.36","aliases":["/develop/connect/insight/release-notes/v.2.36.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"b2d904c33fd2d469f704e75b63bc53824640f4ac1664b9c44f0cf59f4095e744","sections":[{"id":"236-october-2023","title":"2.36 (October 2023)","role":"overview","text":"This is the General Availability (GA) release of RedisInsight 2.36."},{"id":"highlights","title":"Highlights","role":"content","text":"- Optimizations for efficient handling of big [Redis strings](https://redis.io/docs/data-types/strings/): choose to either view the string value for up to a maximum of 5,000 characters or download the data fully as a file if it exceeds the limit\n- Improved security measurement to no longer display in plain text existing database passwords, SSH passwords, passphrases, and private keys"},{"id":"details","title":"Details","role":"content","text":"**Features and improvements**\n- [#2685](https://github.com/RedisInsight/RedisInsight/pull/2685), [#2686](https://github.com/RedisInsight/RedisInsight/pull/2686) Added optimizations for working with big [Redis strings](https://redis.io/docs/data-types/strings/). Users can now choose to either view the data up to a maximum of 5,000 characters or download it in a file fully if it exceeds the limit.\n- [#2647](https://github.com/RedisInsight/RedisInsight/pull/2647) Improved security measurement to no longer expose the existing database passwords, SSH passwords, passphrases, and private keys in plain text\n- [#2631](https://github.com/RedisInsight/RedisInsight/pull/2631) Added proactive notification to restart the application when a new version becomes available\n- [#2705](https://github.com/RedisInsight/RedisInsight/pull/2705) Basic support in the [search index](https://redis.io/docs/interact/search-and-query/) creation form (in Browser) to enable [geo polygon](https://redis.io/commands/ft.create/#:~:text=Vector%20Fields.-,GEOSHAPE,-%2D%20Allows%20polygon%20queries) search\n- [#2681](https://github.com/RedisInsight/RedisInsight/pull/2681) Updated the Pickle formatter to [support](https://github.com/RedisInsight/RedisInsight/issues/2260) Pickle protocol 5\n \n**Bugs**\n- [#2675](https://github.com/RedisInsight/RedisInsight/pull/2675) Show the \"Scan more\" control until the cursor returned by the server is 0 to fix [cases](https://github.com/RedisInsight/RedisInsight/issues/2618) when not all keys are displayed."}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.2.38.0","title":"RedisInsight v2.38.0, November 2023","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.38.0/","summary":"RedisInsight v2.38","aliases":["/develop/connect/insight/release-notes/v.2.38.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"450bdc77f565f70b6dad8b3f62408c963a0af401c5856f8985173438c3631821","sections":[{"id":"238-november-2023","title":"2.38 (November 2023)","role":"overview","text":"This is the General Availability (GA) release of RedisInsight 2.38."},{"id":"highlights","title":"Highlights","role":"content","text":"- Major UX improvements and space optimization for a cleaner and more organized Tree view, ensuring easier namespace navigation and faster key browsing. Additionally, in Tree view, you can now sort your Redis key names alphabetically.\n- Renamed the application from RedisInsight v2 to simply RedisInsight"},{"id":"details","title":"Details","role":"content","text":"**Features and improvements**\n\n- [#2706](https://github.com/RedisInsight/RedisInsight/pull/2706), [#2783](https://github.com/RedisInsight/RedisInsight/pull/2783) Major UX improvements and space optimization for a cleaner and more organized Tree view. This includes consolidating the display of namespaces and keys in a dedicated section and omitting namespace information from key names in the list of keys. In addition, the Tree view introduces a new option to alphabetically sort Redis key names.\n- [#2751](https://github.com/RedisInsight/RedisInsight/pull/2751) Renamed the application from RedisInsight v2 to simply RedisInsight\n- [#2799](https://github.com/RedisInsight/RedisInsight/pull/2799) Automatically make three retries to establish or re-establish a database connection if an error occurs\n\n**Bugs**\n- [#2793](https://github.com/RedisInsight/RedisInsight/pull/2793) [Do not require](https://github.com/RedisInsight/RedisInsight/issues/2765) an SSH password or passphrase\n- [#2794](https://github.com/RedisInsight/RedisInsight/pull/2794) Prevent [potential crashes](https://github.com/RedisInsight/RedisInsight/issues/2763) caused by using parentheses in usernames on the Windows operating system\n- [#2797](https://github.com/RedisInsight/RedisInsight/pull/2797) Avoid initiating a bulk deletion or Profiler after the operating system resumes from sleep mode"}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.2.4.0","title":"RedisInsight v2.4.0, June 2022","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.4.0/","summary":"RedisInsight v2.4.0","aliases":["/develop/connect/insight/release-notes/v.2.4.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"5f160b37dade357b644e3fb2b3338f76f3440f335d5f2c627f74acfc672b9f94","sections":[{"id":"240-june-2022","title":"2.4.0 (June 2022)","role":"overview","text":"This is the General Availability (GA) release of RedisInsight 2.4.0"},{"id":"headlines","title":"Headlines:","role":"content","text":"- Pub/Sub: Added support for [Redis pub/sub](https://redis.io/docs/manual/pubsub/) enabling subscription to channels and posting messages to channels.\n- Consumer groups: Added support for [streams consumer groups](https://redis.io/docs/manual/data-types/streams/#consumer-groups) enabling provision of different subsets of messages from the same stream to many clients for inspection and processing.\n- Database search: Search the list of databases added to RedisInsight to quickly find the required database."},{"id":"details","title":"Details","role":"content","text":"**Features and improvements:**\n- [#760](https://github.com/RedisInsight/RedisInsight/pull/760), [#737](https://github.com/RedisInsight/RedisInsight/pull/737), [#773](https://github.com/RedisInsight/RedisInsight/pull/773) Added support for [Redis pub/sub](https://redis.io/docs/manual/pubsub/) enabling subscription to channels and posting messages to channels. Currently does not support sharded channels.\n- [#717](https://github.com/RedisInsight/RedisInsight/pull/717), [#683](https://github.com/RedisInsight/RedisInsight/pull/683), [#684](https://github.com/RedisInsight/RedisInsight/pull/684), [#688](https://github.com/RedisInsight/RedisInsight/pull/688), [#720](https://github.com/RedisInsight/RedisInsight/pull/720), Added support for [streams consumer groups](https://redis.io/docs/manual/data-types/streams/#consumer-groups) to manage different groups and consumers for the same stream, explicit acknowledgment of processed items, ability to inspect the pending items, claiming of unprocessed messages, and coherent history visibility for each single client.\n- [#754](https://github.com/RedisInsight/RedisInsight/pull/754) New **All Relationship** toggle for RedisGraph visualizations in **Workbench**. Enable it to see all relationships between your nodes.\n- [#788](https://github.com/RedisInsight/RedisInsight/pull/788) Quickly search the list of databases added to RedisInsight per database alias, host:port, or the last connection to find the database needed.\n- [#788](https://github.com/RedisInsight/RedisInsight/pull/788) Overview displays the number of keys per the logical database connected if this number is not equal to the total number in the database.\n\n**Bugs Fixed:**\n- [#774](https://github.com/RedisInsight/RedisInsight/pull/774) Fixed cases when not all parameters are received in Overview.\n- [#810](https://github.com/RedisInsight/RedisInsight/pull/810) Display several streams values with the same timestamp."}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.2.40.0","title":"RedisInsight v2.40.0, December 2023","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.40.0/","summary":"RedisInsight v2.40","aliases":["/develop/connect/insight/release-notes/v.2.40.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"6cc132bff89d2f2fd3caf097894f91fdf9a9c3648bbc2c50365e12a5ee26bd8d","sections":[{"id":"240-december-2023","title":"2.40 (December 2023)","role":"overview","text":"This is the General Availability (GA) release of RedisInsight 2.40."},{"id":"highlights","title":"Highlights","role":"content","text":"- To simplify in-app provisioning of a free [Redis Cloud](https://redis.com/comparisons/oss-vs-enterprise/?utm_source=redisinsight&utm_medium=rel_notes&utm_campaign=2_40) database to use with RedisInsight interactive tutorials, the recommended cloud vendor and region are now preselected \n- Optimizations when uploading large text files with the list of Redis commands, available under bulk actions in Browser"},{"id":"details","title":"Details","role":"content","text":"**Features and improvements**\n- [#2879](https://github.com/RedisInsight/RedisInsight/pull/2879) UX improvements to simplify in-app provisioning of a free [Redis Cloud](https://redis.com/comparisons/oss-vs-enterprise/?utm_source=redisinsight&utm_medium=rel_notes&utm_campaign=2_40) database. Create a new database with a preselected cloud vendor and region by using the recommended sign-up settings. You can manage your database by signing in to the [Redis Cloud console](https://cloud.redis.io/#/databases?utm_source=redisinsight&utm_medium=rel_notes&utm_campaign=2_40)\n- [#2851](https://github.com/RedisInsight/RedisInsight/pull/2851) See plan, cloud vendor, and region details after successfully provisioning your free [Redis Cloud](https://redis.com/comparisons/oss-vs-enterprise/?utm_source=redisinsight&utm_medium=rel_notes&utm_campaign=2_40) database\n- [#2882](https://github.com/RedisInsight/RedisInsight/pull/2882) Optimizations when uploading large text files with the list of Redis commands, available under bulk actions in Browser\n- [#2808](https://github.com/RedisInsight/RedisInsight/pull/2808) Enhanced security measurement to no longer display existing passwords for Redis Sentinel in plain text\n- [#2875](https://github.com/RedisInsight/RedisInsight/pull/2875) Increased performance when resizing the key list and key details in the Tree view, ensuring a smoother user experience\n- [#2866](https://github.com/RedisInsight/RedisInsight/pull/2866) Support for hyphens in [node host names](https://github.com/RedisInsight/RedisInsight/issues/2865)\n\n**Bugs**\n- [#2868](https://github.com/RedisInsight/RedisInsight/pull/2868) Prevent [unintentional data overwrites](https://github.com/RedisInsight/RedisInsight/issues/2791) by disabling both manual and automatic refreshing of key values while editing in the Browser"}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.2.42.0","title":"RedisInsight v2.42.0, January 2024","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.42.0/","summary":"RedisInsight v2.42","aliases":["/develop/connect/insight/release-notes/v.2.42.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"3e519594f8fcd5abccc5e34466d619e44aece438d561bc7a226ae5caa5ebaa83","sections":[{"id":"242-january-2024","title":"2.42 (January 2024)","role":"overview","text":"This is the General Availability (GA) release of RedisInsight 2.42."},{"id":"highlights","title":"Highlights","role":"content","text":"- Introducing a new dedicated developer enablement area! Explore Redis capabilities and learn how to use the native JSON data structure for structured querying and full-text search, including vector similarity search for AI use cases, and more. Browse the tutorials offline or use the in-app provisioning of a free Redis Cloud database to try them interactively.\n- RedisInsight is now available on Docker. Check out our [Docker repository](https://hub.docker.com/repository/docker/redis/redisinsight/general) if that’s your preferred platform."},{"id":"details","title":"Details","role":"content","text":"**Features and improvements**\n- [#2724](https://github.com/RedisInsight/RedisInsight/pull/2724), [#2752](https://github.com/RedisInsight/RedisInsight/pull/2752), [#2965](https://github.com/RedisInsight/RedisInsight/pull/2965) Introducing a dedicated developer enablement area. Dive into interactive tutorials and level up your Redis game even without a database connected. Start exploring tutorials by clicking on the \"Insights\" button located in the top-right corner. Because interactive tutorials can alter data in your database, avoid running them in a production environment. For an optimal tutorial experience, create a free [Redis Cloud](https://redis.com/try-free/?utm_source=redisinsight&utm_medium=main&utm_campaign=redisinsight_release_notes) database.\n- [#2972](https://github.com/RedisInsight/RedisInsight/pull/2972), [#2811](https://github.com/RedisInsight/RedisInsight/pull/2811) The long-awaited Docker build is now available. Check out our [Docker repository](https://hub.docker.com/repository/docker/redis/redisinsight/general) if that’s your preferred platform.\n- [#2857](https://github.com/RedisInsight/RedisInsight/pull/2857) Empty Browser and Workbench pages are aligned with the new interactive tutorials.\n- [#2940](https://github.com/RedisInsight/RedisInsight/pull/2940) Recommendations have been renamed to Tips.\n- [#2970](https://github.com/RedisInsight/RedisInsight/pull/2970) A critical vulnerability has been fixed."}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.2.44.0","title":"RedisInsight v2.44.0, February 2024","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.44.0/","summary":"RedisInsight v2.44","aliases":["/develop/connect/insight/release-notes/v.2.44.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"ed9d5ba43848a9b058624f44d8a4bfe76cfe21cb27e000655bdf44f185356d55","sections":[{"id":"244-february-2024","title":"2.44 (February 2024)","role":"overview","text":"This is the General Availability (GA) release of RedisInsight 2.44."},{"id":"highlights","title":"Highlights","role":"content","text":"- Added support for SSH tunneling for clustered databases, unblocking some users who want to migrate from RESP.app to RedisInsight.\n- UX optimizations in the Browser layout to make it easier to leverage [search and query](https://redis.io/docs/interact/search-and-query/?utm_source=redisinsight&utm_medium=main&utm_campaign=redisinsight_release_notes) indexes."},{"id":"details","title":"Details","role":"content","text":"**Features and improvements**\n- [#2711](https://github.com/RedisInsight/RedisInsight/pull/2711), [#3040](https://github.com/RedisInsight/RedisInsight/pull/3040) Connect to your clustered Redis database via SSH tunnel using a password or private key in PEM format.\n- [#3030](https://github.com/RedisInsight/RedisInsight/pull/3030), [#3070](https://github.com/RedisInsight/RedisInsight/pull/3070) UX optimizations in the Browser layout to enlarge the \"Filter by Key\" input field in the Browser and optimize the display of long [search and query](https://redis.io/docs/interact/search-and-query/?utm_source=redisinsight&utm_medium=main&utm_campaign=redisinsight_release_notes) indexes.\n- [#3033](https://github.com/RedisInsight/RedisInsight/pull/3033), [#3036](https://github.com/RedisInsight/RedisInsight/pull/3036) Various improvements for custom [tutorials](https://github.com/RedisInsight/Tutorials), including visual highlighting of Redis code blocks and strengthening security measures for bulk data uploads by providing an option to download and preview the list of commands for upload.\n- [#3010](https://github.com/RedisInsight/RedisInsight/pull/3010) Enhancements to prevent authentication errors caused by [certain special characters](https://github.com/RedisInsight/RedisInsight/issues/3019) in database passwords. \n\n**Bugs**\n- [#3029](https://github.com/RedisInsight/RedisInsight/pull/3029) A fix for cases when autofill [prevents](https://github.com/RedisInsight/RedisInsight/issues/3026) the form to auto-discover Redis Software Cluster database from being submitted."}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.2.46.0","title":"RedisInsight v2.46.0, March 2024","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.46.0/","summary":"RedisInsight v2.46","aliases":["/develop/connect/insight/release-notes/v.2.46.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"7374d6b58766928414132c939ecc1c13e148936f070cb6d639d03c06062c6166","sections":[{"id":"246-march-2024","title":"2.46 (March 2024)","role":"overview","text":"This is the General Availability (GA) release of RedisInsight 2.46."},{"id":"highlights","title":"Highlights","role":"content","text":"- New formatters for 32-bit and 64-bit vector embeddings for a more human-readable representation in the Browser\n- Cleaner layout on the main page with quick access to JSON and search & query tutorials and Redis Cloud in-app sign-up"},{"id":"details","title":"Details","role":"content","text":"**Features and improvements**\n- [#2843](https://github.com/RedisInsight/RedisInsight/pull/2843), [#3185](https://github.com/RedisInsight/RedisInsight/pull/3185) Adding new formatters for 32-bit and 64-bit vector embeddings to visualize them as arrays in Browser for a simpler and more intuitive representation.\n- [#3069](https://github.com/RedisInsight/RedisInsight/pull/3069) UX enhancements in the database list page for an improved user experience, leading to a cleaner layout and easier navigation.\n- [#3151](https://github.com/RedisInsight/RedisInsight/pull/3151) Launch RedisInsight with the previously used window size.\n\n**Bugs**\n- [#3152](https://github.com/RedisInsight/RedisInsight/pull/3152), [#3156](https://github.com/RedisInsight/RedisInsight/pull/3156) A fix to [support the * wildcard](https://github.com/RedisInsight/RedisInsight/issues/3146) in Stream IDs.\n- [#3174](https://github.com/RedisInsight/RedisInsight/pull/3174) Display invalid JSONs as unformatted values when a JSON view is set in Workbench results."}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.2.48.0","title":"Redis Insight v2.48.0, April 2024","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.48.0/","summary":"Redis Insight v2.48","aliases":["/develop/connect/insight/release-notes/v.2.48.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"c5cdb606ac00dcd6ed5657a3998d30b57feecd197a41a63a4dca8684bf3e7fac","sections":[{"id":"248-april-2024","title":"2.48 (April 2024)","role":"overview","text":"This is the General Availability (GA) release of Redis Insight 2.48."},{"id":"highlights","title":"Highlights","role":"content","text":"- New look, equally fast.\n- Learn Redis faster by uploading sample data and a concise tutorial for empty databases.\n- Enhance the security and scalability when running Redis Insight on Docker behind a proxy by adding support for the static proxy subpath."},{"id":"details","title":"Details","role":"content","text":"**Features and improvements**\n- [#3233](https://github.com/RedisInsight/RedisInsight/pull/3233) New look, equally fast. We've refreshed our Redis Insight app to align with our new brand look.\n- [#3224](https://github.com/RedisInsight/RedisInsight/pull/3224) Jumpstart your Redis journey by uploading sample data with JSON and basic data structures for empty databases. To upload the sample data, navigate to the Browser screen for your empty database and initiate the upload process with just a click.\n- [#2711](https://github.com/RedisInsight/RedisInsight/pull/2711) Enhance the security and scalability by running Redis Insight on Docker [behind a proxy](https://github.com/RedisInsight/RedisInsight-reverse-proxy) using the newly added support for the static proxy subpath. Use the `RIPROXYPATH` environment variable to configure the subpath proxy path."}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.2.50.0","title":"Redis Insight v2.50.0, May 2024","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.50.0/","summary":"Redis Insight v2.50","aliases":["/develop/connect/insight/release-notes/v.2.50.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"ae5f32fb8298f77e4acb7351319da7f1ba78a16ed96291c139b462cbfca5a6cc","sections":[{"id":"250-may-2024","title":"2.50 (May 2024)","role":"overview","text":"This is the General Availability (GA) release of Redis Insight 2.50."},{"id":"highlights","title":"Highlights","role":"content","text":"- New tutorial exploring several common Redis use cases with paired-up sample data that will get you started quicker with your empty database.\n- Performance and UX enhancements for the JSON data structure for smoother data rendering and interaction in the Browser."},{"id":"details","title":"Details","role":"content","text":"**Features and improvements**\n- [#3402](https://github.com/RedisInsight/RedisInsight/pull/3402) New tutorial exploring several common Redis use cases with paired-up sample data that will get you started quicker with your empty database.\n- [#3251](https://github.com/RedisInsight/RedisInsight/pull/3251) UX enhancements for the JSON data structure in the Browser to prevent collapsing the entire structure when updating a JSON value. Includes performance optimizations for loading JSON documents containing numerous objects.\n- [#3161](https://github.com/RedisInsight/RedisInsight/pull/3161), [#3171](https://github.com/RedisInsight/RedisInsight/pull/3171) Added a quick access button to sign in to your Redis Cloud account from anywhere within Redis Insight, to import existing databases or create a new account with a free database. Integration with your Redis Cloud account is currently available only in the desktop Redis Insight version.\n- [#3349](https://github.com/RedisInsight/RedisInsight/pull/3349) Changed the sorting order in the Tree view to lexicographical."}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.2.52.0","title":"Redis Insight v2.52.0, June 2024","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.52.0/","summary":"Redis Insight v2.52","aliases":["/develop/connect/insight/release-notes/v.2.52.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"8d57e007f379898166db7fba18c1202230ff425db5c8cc349f529c61a23034b3","sections":[{"id":"252-june-2024","title":"2.52 (June 2024)","role":"overview","text":"This is the General Availability (GA) release of Redis Insight 2.52."},{"id":"highlights","title":"Highlights","role":"content","text":"- Redis Insight now supports [setting expiration for individual hash fields](https://redis.io/docs/latest/develop/data-types/hashes/?utm_source=redisinsight&utm_medium=release_notes&utm_campaign=2.52#field-expiration), a highly requested feature available in the [first release candidate of Redis 7.4](https://github.com/redis-stack/redis-stack/releases/tag/v7.4.0-rc1)\n- Learn how to leverage Redis for Retrieval Augmented Generation (RAG) use cases via a new built-in Redis Insight tutorial"},{"id":"details","title":"Details","role":"content","text":"**Features and improvements**\n- [#3470](https://github.com/RedisInsight/RedisInsight/pull/3470) Redis Insight now supports [setting expiration for individual hash fields](https://redis.io/docs/latest/develop/data-types/hashes/?utm_source=redisinsight&utm_medium=release_notes&utm_campaign=2.52#field-expiration) through intuitive Browser controls. The hash field expiration is a highly requested feature available in the [first release candidate of Redis 7.4](https://github.com/redis-stack/redis-stack/releases/tag/v7.4.0-rc1).\n- [#60](https://github.com/RedisInsight/Tutorials/pull/60) Redis, with its high performance and versatile data structures, is an excellent choice for implementing Retrieval Augmented Generation (RAG). Our new built-in tutorial provides an overview of how Redis can be leveraged in a RAG use case. To get started, open the \"Insights\" panel in the top right corner and try the new tutorial.\n- [#3447](https://github.com/RedisInsight/RedisInsight/pull/3447), [#3483](https://github.com/RedisInsight/RedisInsight/pull/3483) UX optimizations for displaying the values of keys in the Browser. The new layout includes controls for editing key values that appear only when you hover over them, optimizing the use of space and providing a cleaner interface.\n- [#3231](https://github.com/RedisInsight/RedisInsight/pull/3231) Support for applying the JSON formatting in Browser for values of keys with float numbers that contain 10 or more decimal places.\n- [#3492](https://github.com/RedisInsight/RedisInsight/pull/3492) Increased the slot refresh timeout to 5000 milliseconds to enhance connection stability for clustered databases. This adjustment helps avoid scenarios where a connection is terminated before the acknowledgment of a successful connection establishment is received.\n\n**Bugs**\n- [#3490](https://github.com/RedisInsight/RedisInsight/pull/3490) Fix for an issue related to adding a JSON field to a key that already contains many fields."}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.2.54.0","title":"Redis Insight v2.54.0, August 2024","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.54.0/","summary":"Redis Insight v2.54","aliases":["/develop/connect/insight/release-notes/v.2.54.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"34fc3b4ca6aa596b05cb6c2429bc12e7c36bc5a5284e579db205533a9422c94a","sections":[{"id":"254-august-2024","title":"2.54 (August 2024)","role":"overview","text":"This is the General Availability (GA) release of Redis Insight 2.54."},{"id":"highlights","title":"Highlights","role":"content","text":"Support for [Redis Data Integration (RDI)](https://redis.io/data-integration/?utm_source=redisinsight&utm_medium=repository&utm_campaign=release_notes) - a powerful tool designed to seamlessly synchronize data from your existing database to Redis in near real-time. RDI establishes a data streaming pipeline that mirrors data from your existing database to Redis Software, so if a record is added or updated, those changes automatically flow into Redis. This no-code solution enables seamless data integration and faster data access so you can build real-time apps at any scale. And now you can seamlessly create, validate, deploy, and monitor your data pipelines directly from Redis Insight."},{"id":"details","title":"Details","role":"content","text":"**Features and improvements**\n- [#2839](https://github.com/RedisInsight/RedisInsight/pull/2839), [#2853](https://github.com/RedisInsight/RedisInsight/pull/2853), [#3101](https://github.com/RedisInsight/RedisInsight/pull/3101) Redis Insight now comes with the support for [Redis Data Integration (RDI)](https://redis.io/data-integration/?utm_source=redisinsight&utm_medium=repository&utm_campaign=release_notes) - a powerful tool designed to seamlessly synchronize data from your existing database to Redis in near real-time. RDI establishes a data streaming pipeline that mirrors data from your existing database to Redis Software, so if a record is added or updated, those changes automatically flow into Redis. This no-code solution enables seamless data integration and faster data access so you can build real-time apps at any scale. Use RDI version 1.2.7 or later to seamlessly create, validate, deploy, and monitor your data pipelines within Redis Insight. To get started, switch to the \"Redis Data Integration\" tab on the page with the list of Redis databases and add your RDI endpoint to Redis Insight.\n\n**Bugs**\n- [#3577](https://github.com/RedisInsight/RedisInsight/pull/3577) Show [information about OSS cluster](https://github.com/RedisInsight/RedisInsight/issues/3157) when connected using TLS.\n- [#3575](https://github.com/RedisInsight/RedisInsight/pull/3575) Return [results instead of an empty list](https://github.com/RedisInsight/RedisInsight/issues/3465) for commands written in lowercase.\n- [#3613](https://github.com/RedisInsight/RedisInsight/pull/3613) Prevent repetitive buffer overflow by avoiding the resending of unfulfilled commands."}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.2.56.0","title":"Redis Insight v2.56.0, September 2024","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.56.0/","summary":"Redis Insight v2.56","aliases":["/develop/connect/insight/release-notes/v.2.56.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"9b8e965e312e4160fb5541a6b31654bbe2dbfbeaa8f4431c6854603cdede84e7","sections":[{"id":"256-september-2024","title":"2.56 (September 2024)","role":"overview","text":"This is the General Availability (GA) release of Redis Insight 2.56."},{"id":"highlights","title":"Highlights","role":"content","text":"- Seamlessly sign in to your Redis Cloud account using the new [SAML single sign-on](https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso/) feature, now available alongside existing social logins via Google and GitHub. This integration lets you connect to all your Redis Cloud databases in several clicks.\n- Start your Redis journey faster with a sample data set automatically loaded for new free Redis Cloud databases created directly within Redis Insight.\n- Focus on what matters most:\n  - Hide or show [TTL for individual hash fields](https://redis.io/docs/latest/develop/data-types/hashes/?utm_source=redisinsight&utm_medium=release_notes&utm_campaign=2.52#field-expiration) to create a cleaner, more efficient workspace.\n  - Enhanced vector data representation with updated 32-bit and 64-bit vector formatters in the Browser.\n  - UX optimizations to make it easier and more intuitive to connect to your [Redis Data Integration (RDI)](https://redis.io/data-integration/?utm_source=redisinsight&utm_medium=repository&utm_campaign=release_notes) instance within Redis Insight."},{"id":"details","title":"Details","role":"content","text":"**Features and improvements**\n- [#3727](https://github.com/RedisInsight/RedisInsight/pull/3727) Seamlessly sign in to your Redis Cloud account using the new [SAML single sign-on](https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso/) feature, now available alongside existing social logins via Google and GitHub. This integration lets you connect to all your Redis Cloud databases in several clicks. Before setting up SAML in Redis Cloud, you must first [verify domain ownership](https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso/?utm_source=redisinsight&utm_medium=repository&utm_campaign=release_notes) for any domains associated with your SAML setup. Note that integration with Redis Cloud is currently available only in the desktop version of Redis Insight.\n- [#3659](https://github.com/RedisInsight/RedisInsight/pull/3659) Start your Redis journey faster with a sample data set automatically loaded for new free Redis Cloud databases created directly within Redis Insight. This feature ensures a smoother setup process, allowing you to dive into your data immediately.\n- [#3624](https://github.com/RedisInsight/RedisInsight/pull/3624) The ability to hide or show [TTL for individual hash fields](https://redis.io/docs/latest/develop/data-types/hashes/?utm_source=redisinsight&utm_medium=release_notes&utm_campaign=2.52#field-expiration) to create a cleaner, more efficient workspace. This optimization complements the highly requested hash field expiration feature introduced in the [first release candidate of Redis 7.4](https://github.com/redis-stack/redis-stack/releases/tag/v7.4.0-v0).\n- [#3701](https://github.com/RedisInsight/RedisInsight/pull/3701) Enhanced vector data representation with updated 32-bit and 64-bit vector formatters in the Browser. These changes ensure that vector formatters are applied only to data containing unprintable values when converted to UTF-8, providing a clearer and more accurate view of your data.\n- [#3714](https://github.com/RedisInsight/RedisInsight/pull/3714) UX optimizations to make it easier and more intuitive to connect to your [Redis Data Integration (RDI)](https://redis.io/data-integration/?utm_source=redisinsight&utm_medium=repository&utm_campaign=release_notes) instance within Redis Insight.\n- [#3665](https://github.com/RedisInsight/RedisInsight/pull/3665) A new timestamp formatter in the Browser to improve data readability. This formatter converts timestamps in hash fields to a human-readable format, making it easier to interpret results, validate and optimize queries, and inspect indexed data when using [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query/?utm_source=redisinsight&utm_medium=repository&utm_campaign=release_notes).\n- [#3730](https://github.com/RedisInsight/RedisInsight/pull/3730) Date and time format customization to make the data more intuitive in Redis Insight. This flexibility helps match your local time zone or standardize it to UTC for better alignment in time-critical operations across global teams."}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.2.58.0","title":"Redis Insight v2.58.0, October 2024","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.58.0/","summary":"Redis Insight v2.58","aliases":["/develop/connect/insight/release-notes/v.2.58.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"825821fc355611e32a52e511a933eaf8cabda36bf5513a9a7d92e54d469d2066","sections":[{"id":"258-october-2024","title":"2.58 (October 2024)","role":"overview","text":"This is the General Availability (GA) release of Redis Insight 2.58."},{"id":"highlights","title":"Highlights","role":"content","text":"- Added functionality to start, stop, and reset [Redis Data Integration](https://redis.io/data-integration/?utm_source=redisinsight&utm_medium=repository&utm_campaign=release_notes) pipelines directly in the app, simplifying management and enhancing control\n- Introduced support for subscribing to specific Pub/Sub channel - a [highly requested feature](https://github.com/RedisInsight/RedisInsight/issues/1671)\n- Ability to delete previously added CA and Client certificates to keep them updated"},{"id":"details","title":"Details","role":"content","text":"**Features and improvements**\n- [#3843](https://github.com/RedisInsight/RedisInsight/pull/3843) Redis Insight now supports starting, stopping, and resetting [Redis Data Integration](https://redis.io/data-integration/?utm_source=redisinsight&utm_medium=repository&utm_campaign=release_notes) (RDI) pipelines. Use RDI version 1.2.9 or later to seamlessly stop or resume processing new data. You can also reset an RDI pipeline to take a new snapshot of the data, process it, and continue tracking changes. To get started, navigate to the \"Redis Data Integration\" tab on the database list page and add or connect to your RDI endpoint.\n- [#3832](https://github.com/RedisInsight/RedisInsight/pull/3832) Added support for a [highly requested feature](https://github.com/RedisInsight/RedisInsight/issues/1671) to subscribe to specific Pub/Sub channels. On the Pub/Sub page, you can now subscribe to multiple channels or patterns by entering them as a space delimited list.\n- [#3796](https://github.com/RedisInsight/RedisInsight/pull/3796) Ability to delete previously added CA and Client certificates to keep them up-to-date.\n\n**Bugs**\n- [#3840](https://github.com/RedisInsight/RedisInsight/pull/3840) [Saved](https://github.com/RedisInsight/RedisInsight/issues/3833) SNI and SSH connection information for newly added database connections.\n- [#3828](https://github.com/RedisInsight/RedisInsight/pull/3828) Fixed an issue to [display multiple hash fields](https://github.com/RedisInsight/RedisInsight/issues/3826) when expanding a hash value."}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.2.6.0","title":"RedisInsight v2.6.0, July 2022","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.6.0/","summary":"RedisInsight v2.6.0","aliases":["/develop/connect/insight/release-notes/v.2.6.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"d0564a3d47bf6cfe5c36798abbc6ab74e0a277117701075eae810c751459fc95","sections":[{"id":"260-july-2022","title":"2.6.0 (July 2022)","role":"overview","text":"This is the General Availability (GA) release of RedisInsight 2.6.0"},{"id":"headlines","title":"Headlines:","role":"content","text":"- Bulk actions: Delete the keys in bulk based on the filters set in Browser or Tree view\n- Multiline support for key values in Browser and Tree View: Click the key value to see it in full\n- [Pipeline](https://redis.io/docs/manual/pipelining/) support in Workbench: Batch Redis commands in Workbench to optimize round-trip times\n- In-app notifications: Receive messages about important changes, updates, or announcements inside the application. Notifications are always available in the Notification center, and can be displayed with or without preview."},{"id":"details","title":"Details","role":"content","text":"**Features and improvements:**\n- [#890](https://github.com/RedisInsight/RedisInsight/pull/890), [#883](https://github.com/RedisInsight/RedisInsight/pull/883), [#875](https://github.com/RedisInsight/RedisInsight/pull/875) Delete keys in bulk from your Redis database in Browser and Tree view based on filters you set by key name or data type.\n- [#878](https://github.com/RedisInsight/RedisInsight/pull/878) Multiline support for key values in Browser and Tree View: Select the truncated value to expand the row and see the full value, select again to collapse it.\n- [#837](https://github.com/RedisInsight/RedisInsight/pull/837), [#838](https://github.com/RedisInsight/RedisInsight/pull/838) Added [pipeline](https://redis.io/docs/manual/pipelining/) support for commands run in Workbench to optimize round-trip times. Default number of commands sent in a pipeline is 5, and is configurable in Settings > Advanced. \n- [#862](https://github.com/RedisInsight/RedisInsight/pull/862), [#840](https://github.com/RedisInsight/RedisInsight/pull/840) Added in-app notifications to inform you about any important changes, updates, or announcements. Notifications are always available in the Notification center, and can be displayed with or without preview.\n- [#830](https://github.com/RedisInsight/RedisInsight/pull/830) To more easily explore and work with stream data, always display stream entry ID and controls to remove the Stream entry regardless of the number of fields.\n- [#928](https://github.com/RedisInsight/RedisInsight/pull/928) Remember the sorting on the list of databases.\n\n**Bugs fixed:**\n- [#932](https://github.com/RedisInsight/RedisInsight/pull/932) Refresh the JSON value in Browser/Tree view."}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.2.60.0","title":"Redis Insight v2.60.0, October 2024","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.60.0/","summary":"Redis Insight v2.60","aliases":["/develop/connect/insight/release-notes/v.2.60.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"13484943c15a298b64ab887f70d8d141b434dc65604c77c8ae060c9d28badb77","sections":[{"id":"260-october-2024","title":"2.60 (October 2024)","role":"overview","text":"This is the General Availability (GA) release of Redis Insight 2.60."},{"id":"highlights","title":"Highlights","role":"content","text":"- Advanced and schema-aware command auto-complete for [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query/?utm_source=redisinsight&utm_medium=main&utm_campaign=release_notes) is now available in Workbench, enabling faster and more accurate query building with smart suggestions for indexes, schemas, and expressions.\n- Support for adding multiple elements to the head or tail of lists, for both new or existing keys.\n- Multiple UI enhancements for clarity and ease of use when editing Redis Data Integration (RDI) jobs."},{"id":"details","title":"Details","role":"content","text":"**Features and improvements**\n- [#3553](https://github.com/RedisInsight/RedisInsight/pull/3553), [#3647](https://github.com/RedisInsight/RedisInsight/pull/3647), [#3669](https://github.com/RedisInsight/RedisInsight/pull/3669) Advanced, schema-aware auto-complete for [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query/?utm_source=redisinsight&utm_medium=main&utm_campaign=release_notes) in Workbench. Enjoy faster query building with context-sensitive suggestions that recognize indexes, schemas, and fields based on your current query. Start typing any [Redis Search](https://redis.io/docs/latest/commands/?group=search) command in Workbench to try this feature. \n- [#3891](https://github.com/RedisInsight/RedisInsight/pull/3891) Allows to easily push multiple elements to the head or tail of list data types, whether creating new or updating existing lists.\n- [#3891](https://github.com/RedisInsight/RedisInsight/pull/3891) UX/UI enhancements to provide more details about Redis Data Integration (RDI) job transformation and output results in the dry-run section.\n- [#3981](https://github.com/RedisInsight/RedisInsight/pull/3981) Removes confirmation prompts for template insertions in Redis Data Integration jobs, simplifying a workflow.\n- [#3827](https://github.com/RedisInsight/RedisInsight/pull/3827) Provides easy-to-understand metrics of network input/output by automatically converting units in Browser Overview.\n- [#3982](https://github.com/RedisInsight/RedisInsight/pull/3982), [#3975](https://github.com/RedisInsight/RedisInsight/pull/3975), [#3941](https://github.com/RedisInsight/RedisInsight/pull/3941) Various vulnerabilities have been fixed."}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.2.62.0","title":"Redis Insight v2.62.0, November 2024","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.62.0/","summary":"Redis Insight v2.62","tags":[],"last_updated":"2024-12-02T15:45:00+01:00","page_type":"content","content_hash":"7ce55335afc9ae075de115e682967158955f346b8dc9012416aded994f839a69","sections":[{"id":"262-november-2024","title":"2.62 (November 2024)","role":"overview","text":"This is the General Availability (GA) release of Redis Insight 2.62."},{"id":"highlights","title":"Highlights","role":"content","text":"- Support for multiple key name delimiters in Tree View, allowing more flexible browsing for databases with diverse key structures.\n- Remain authenticated to [Redis Copilot](https://redis.io/docs/latest/develop/tools/insight/?utm_source=redisinsight&utm_medium=main&utm_campaign=tutorials#:~:text=for%20more%20information.-,Redis%20Copilot,-Redis%20Copilot%20is), even after reopening Redis Insight, for seamless and uninterrupted access with daily use."},{"id":"details","title":"Details","role":"content","text":"**Features and improvements**\n- [#4090](https://github.com/RedisInsight/RedisInsight/pull/4090) Added support for multiple key name delimiters in Tree View, enabling more flexible browsing of databases with varied key name patterns.\n- [#3957](https://github.com/RedisInsight/RedisInsight/pull/3957) Remain authenticated to [Redis Copilot](https://redis.io/docs/latest/develop/tools/insight/?utm_source=redisinsight&utm_medium=main&utm_campaign=tutorials#:~:text=for%20more%20information.-,Redis%20Copilot,-Redis%20Copilot%20is), even after reopening Redis Insight, for seamless and uninterrupted access with daily use.\n- [#3988](https://github.com/RedisInsight/RedisInsight/pull/3988), [#4059](https://github.com/RedisInsight/RedisInsight/pull/4059) Enhanced both the Java and PHP serialized formatters: the Java formatter now supports date and time data, while the PHP formatter includes UTF-8 encoding for better handling of special characters and multi-language data.\n- [#4081](https://github.com/RedisInsight/RedisInsight/pull/4081) Introduced a unique theme key name with a proxy path prefix to prevent conflicts when multiple instances run on the same origin.\n- [#2970](https://github.com/RedisInsight/RedisInsight/pull/4107) Upgraded to Electron 33.2.0 for enhanced security and compatibility with modern web standards.\n\n**Bugs**\n- [#4089](https://github.com/RedisInsight/RedisInsight/pull/4089) Resolved an issue where large integers in JSON keys were being rounded, ensuring data integrity.\n\n**SHA-256 Checksums**\n| Package | SHA-256 |\n|--|--|\n| Windows | ibZ5kn0GSdrbnfHRWC1lDdKozn6YllcGIrDhmLEnt2K1rjgjL2kGKvbtfq9QEkumgGwk2a9zTjr0u5zztGHriQ== |\n| Linux AppImage | bM6lbyeAHFX/f0sBehu9a9ifHsDvX8o/2qn91sdtyiRcIU+f31+Ch7K4NI4v226rgj6LvkFDWDNq6VQ4pyLAPA== |\n| Linux Debian| ilD86T/+8gEgrZg8MS8Niv/8g54FPeEn1nZrUI6DA7KTl3owqzqD0npb8fdAdL6YtSRbSBUK2fXPQ6GRXWZ/GA== |\n| MacOS Intel | pSy3CvRfVIT3O7BXUPMUoONRaZCOA1965tF9T19gZ1NnUn9YkjWlNXdniQHZ4ALKbpC2q62ygt39xF6O52LxAw== |\n| MacOS Apple silicon | uoz6I6MO4/j8UJo7eNje3dz4rx1KKj6mum/vXb2882fYPD/lK1cG0Q0OZu/lbxuk0xgzXfWv0MhMTIVVV+EADg== |"}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.2.64.0","title":"Redis Insight v2.64.0, December 2024","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.64.0/","summary":"Redis Insight v2.64","tags":[],"last_updated":"2024-12-19T15:27:39+01:00","page_type":"content","content_hash":"2f7b49cc95a711edf33991f147a6c5e03cb1d6e729ad1bad1c20c9a7934de137","sections":[{"id":"264-december-2024","title":"2.64 (December 2024)","role":"overview","text":"This is the General Availability (GA) release of Redis Insight 2.64."},{"id":"highlights","title":"Highlights","role":"content","text":"- Improved the database connections list and simplified the connection form, delivering a cleaner, more intuitive UI for improved focus.\n- New in-app reminders to prevent your free Redis Cloud database from being deleted due to inactivity, ensuring you can make the most of it to test your ideas."},{"id":"details","title":"Details","role":"content","text":"**Features and improvements**\n- [#4088](https://github.com/RedisInsight/RedisInsight/pull/4088), [#4078](https://github.com/RedisInsight/RedisInsight/pull/4078), [#4094](https://github.com/RedisInsight/RedisInsight/pull/4094) Improved the database connections list and simplified the connection form, delivering a cleaner, more intuitive UI for improved focus.\n- [#4189](https://github.com/RedisInsight/RedisInsight/pull/4189), [#4191](https://github.com/RedisInsight/RedisInsight/pull/4191) New in-app reminders notify you before your free Redis Cloud database is deleted due to inactivity, ensuring you can make the most of it to test your ideas.\n- [#4204](https://github.com/RedisInsight/RedisInsight/pull/4204), [#4196](https://github.com/RedisInsight/RedisInsight/pull/4196), [#4202](https://github.com/RedisInsight/RedisInsight/pull/4202) Various vulnerabilities have been fixed.\n\n**Bugs**\n- [#4194](https://github.com/RedisInsight/RedisInsight/pull/4194) Resolved an [issue](https://github.com/RedisInsight/RedisInsight/issues/4186) where modifying a JSON value inadvertently converted strings into numbers.\n\n**SHA-256 Checksums**\n| Package | SHA-256 |\n|--|--|\n| Windows | iYZbKsFtz/Ua4qeBdeHIRtZRiA1I50R3yY1t3VUD2cn94EpZLR5Xz3lK3yRxA85PxJaHjrWljyGliZv0OX0KBg== |\n| Linux AppImage | ToEFW8wVLI8oFoc/puzf2Cwoff8gBTsIxEsGjQRZq5D5BgrE3muxtuEQto3J2RiRbadGAZx6AZPh75WVJ0DKRw== |\n| Linux Debian| /k6jgfzDSRJ0yWmbtxpD5WG2i9wGUZ4r2AexDz6rUOLyZMqQPJUKEKuonprFvHZp+PUW/EtSWc436IFykBVmsQ== |\n| MacOS Intel | PrbRc+ju0UKxr4huP7Xl9Sq0fH0XaxUtydW86rAYepEAADUADsAYV2lB8gO7Ohs9ukJ7mXBEU7OJWRqJGLhxHg== |\n| MacOS Apple silicon | E6kTbnkoW3eji/v7WVrnwqlEKk444+hxiFqt56r8J+zAHhmX9dlNd7y37xdJlQ82FZ9QOIIMsN5Z0N+bgRisuw== |"}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.2.64.1","title":"Redis Insight v2.64.1, December 2024","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.64.1/","summary":"Redis Insight v2.64.1","tags":[],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"3369724cff647fe285515303e6dd7438578ed418fb094d61f676cae1957000d1","sections":[{"id":"2641-december-2024","title":"2.64.1 (December 2024)","role":"overview","text":"This is a maintenance release for Redis Insight 2.64.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!"},{"id":"details","title":"Details","role":"content","text":"- [#4236](https://github.com/RedisInsight/RedisInsight/pull/4236) Reverts the change to use JSONPath ($) by default rather than (.). These changes could cause issues with shards in Redis Software Active-Active databases.\n\n**SHA-256 Checksums**\n| Package | SHA-256 |\n|--|--|\n| Windows | hIK4qrC50Gd4jZnpHnwRIIVyDWtOfvfFID9nv8xfdcDgf4LvJcGLa9zVYkbfvwUv+aEaaBCohJJZMIGFC6iYHQ== |\n| Linux AppImage | ll999oWjvKppawlYBPN6phGNa+mDiWmefIvkbQNAd7JPZFbHTYuLFWMWo4F1NrnZlr6vnPF6awbu7ubbiZL0HA== |\n| Linux Debian| 4MKHfmmapfhxXUln0X+rpFXzm2dH6IPj2BIwlNRPQDGhpQ5flzOtLlV1iNGm9xqennZUv+hx+cVQodzPIj8FTw== |\n| MacOS Intel | 5FkllEVCbD9M1fYww7N6XT3Qknl5tWrkHKWQWGhjkUiR/nZ89u+A84UzynB5H/lzBCFwUWJidfGJ4akrX2J7Hg== |\n| MacOS Apple silicon | 2gWxZqGlAo0RyQKa0h8puyXMkIg1vF/Gobd9vS9DNWZMr3aYJojALx6f7pfknBoL7MDmZI29Mohtx4mnQPbjGQ== |"}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.2.66.0","title":"Redis Insight v2.66.0, January 2025","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.66.0/","summary":"Redis Insight v2.66","tags":[],"last_updated":"2025-01-31T15:15:53+01:00","page_type":"content","content_hash":"bae733eb433d4e5622b11ff533fa77a07173908d3a7681ea1a6fa55e136c4143","sections":[{"id":"266-january-2025","title":"2.66 (January 2025)","role":"overview","text":"This is the General Availability (GA) release of Redis Insight 2.66."},{"id":"highlights","title":"Highlights","role":"content","text":"- Switch between Redis databases and [Redis Data Integration](https://redis.io/data-integration/?utm_source=redisinsight&utm_medium=repository&utm_campaign=release_notes) (RDI) instances without returning to the database or RDI endpoint list.\n- Improved performance in Browser when handling nested JSON data, along with the option to hide key size and TTL for a more efficient navigation."},{"id":"details","title":"Details","role":"content","text":"**Features and improvements**\n- [#4258](https://github.com/RedisInsight/RedisInsight/pull/4258) Improved navigation allows seamless switching between Redis databases and [Redis Data Integration](https://redis.io/data-integration/?utm_source=redisinsight&utm_medium=repository&utm_campaign=release_notes) (RDI) instances without returning to the database or endpoint list.\n- [#4315](https://github.com/RedisInsight/RedisInsight/pull/4315) Improved performance when working with nested JSON data types in Browser.\n- [#4290](https://github.com/RedisInsight/RedisInsight/pull/4290) Added an option to hide key size and TTL in Browser to optimize space. Hiding key size can also help avoid performance issues when working with large keys.\n- [#4268](https://github.com/RedisInsight/RedisInsight/pull/4268) Enhanced UX for adding Redis databases, now displaying information in multiple tabs.\n- [#4228](https://github.com/RedisInsight/RedisInsight/pull/4228) Added the ability to customize the refresh interval or stop refreshing database overview metrics, allowing to change the frequency or avoid seeing the `INFO` command in Profiler.\n- [#4255](https://github.com/RedisInsight/RedisInsight/pull/4255) Updated Brotli decompression to use brotli-wasm.\n\n**Bugs**\n- [#4304](https://github.com/RedisInsight/RedisInsight/pull/4304) Resolved the [application startup error](https://github.com/RedisInsight/RedisInsight/issues/3871) on Ubuntu 24.04 caused by a space in the application name.\n\n**SHA-256 Checksums**\n| Package | SHA-256 |\n|--|--|\n| Windows | Bjxu9UFPpWhz29prFqRsKDNlF4LZaTUJgAvBBI/FNQ9rBncFGGOb5m59wY3dXIYAG6+VB6F9U9ylffv31IDszw== |\n| Linux AppImage | T6y4xd4BVs1skNAOWkFpWkcov0288qIh2dXHo7gofDw99ow6phV3LzcaasHLT5F+TdlbfcjB8aGVJMx1qEIaBw== |\n| Linux Debian| WaMsSd6qKvw5x6ALLLPTnFoWMX/qVZafVJ3SJAUr8IYoGksnNlU1huUr9q/ftlwP00y2zYac7EZBbl2Z/bOppQ== |\n| MacOS Intel | 4x7vG7nTt3s4+kQ6WSuhrtigRa2XZ9Q6UiR1WCb/vROAL/X5GjmFvv4jBPIxZC1w6Z46pNzS+IhLcI4oHVSyAw== |\n| MacOS Apple silicon | x8wlgNy4dadKV3tcn8uJh/ksvEMnBYaCPvlxlfwcMjNwXNoSBQ+tK6kgJSONGgLVUqtQe1pfTbzqBsqkEAxquw== |"}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.2.68.0","title":"Redis Insight v2.68.0, April 2025","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.68.0/","summary":"Redis Insight v2.68","tags":[],"last_updated":"2025-04-02T18:35:22+03:00","page_type":"content","content_hash":"0069ebeb95dc9aa7228835bfc47f7f43254c5dea7edc2ff4c0f6f63def3a2205","sections":[{"id":"268-april-2025","title":"2.68 (April 2025)","role":"overview","text":"This is the General Availability (GA) release of Redis Insight 2.68."},{"id":"highlights","title":"Highlights","role":"content","text":"- You can now test the connectivity to your source database when setting up a [Redis Data Integration](https://redis.io/docs/latest/integrate/redis-data-integration/) (RDI) data pipeline in Redis Insight. This will help ensure that RDI can connect to the source database and keep your Redis cache updated with changes from the source database.\n- Configure database connections via environment variables or a JSON file, allowing for centralized and efficient configuration management. This is specifically useful for automated deployments."},{"id":"details","title":"Details","role":"content","text":"**Features and improvements**\n- [#4368](https://github.com/RedisInsight/RedisInsight/pull/4368), [#4389](https://github.com/RedisInsight/RedisInsight/pull/4389) You can now test the connectivity to your source database when setting up a [Redis Data Integration](https://redis.io/docs/latest/integrate/redis-data-integration/) (RDI) data pipeline in Redis Insight. This will help ensure that RDI can connect to the source database and keep your Redis cache updated with changes from the source database.\n- [#308](https://github.com/redislabsdev/RedisInsight-Cloud/pull/308) Configure database connections via environment variables or a JSON file, allowing for centralized and efficient configuration management. This is specifically useful for automated deployments. See [here](https://redis.io/docs/latest/operate/redisinsight/configuration/) for more details.\n- [#4428](https://github.com/RedisInsight/RedisInsight/pull/4428) Added an environment variable to disable the ability to manage database connections (adding, editing, or deleting) in Redis Insight. This provides enhanced security and configuration control in scenarios where preventing changes to database connections is necessary. See [here](https://redis.io/docs/latest/operate/redisinsight/configuration/) for more details.\n- [#4377](https://github.com/RedisInsight/RedisInsight/pull/4377), [#4383](https://github.com/RedisInsight/RedisInsight/pull/4383) Allows connecting to databases without requiring credentials for dangerous commands. In this mode, certain features, such as database statistics, are hidden.\n- [#4427](https://github.com/RedisInsight/RedisInsight/pull/4427) Added the ability to download a file containing all keys deleted through bulk actions, which helps in tracking changes.\n- [#4335](https://github.com/RedisInsight/RedisInsight/pull/4335) [Redis Data Integration](https://redis.io/docs/latest/integrate/redis-data-integration/) deployment errors are now stored in a file instead of being displayed in error messages, improving space efficiency.\n- [#4374](https://github.com/RedisInsight/RedisInsight/pull/4374) Improved connection errors for clustered databases by adding detailed information to help with troubleshooting.\n- [#4358](https://github.com/RedisInsight/RedisInsight/pull/4358) Added a setting to manually enforce standalone mode for clustered database connections instead of automatic clustered mode.\n- [#4418](https://github.com/RedisInsight/RedisInsight/pull/4418) An ability to see key names in HEX format, useful for non-ASCII characters or debugging. To switch from Unicode to HEX, open the \"Decompression & Formatters\" tab while adding or editing a database connection.\n- [#4401](https://github.com/RedisInsight/RedisInsight/pull/4401) Added an option to close key details for unsupported data types in the Browser to free up space.\n- [#4296](https://github.com/RedisInsight/RedisInsight/pull/4296) When working with JSON data types, Redis Insight now uses [JSONPath ($) syntax](https://redis.io/docs/latest/develop/data-types/json/path/).\n\n**SHA-256 Checksums**\n| Package | SHA-256 |\n|--|--|\n| Windows | 50YAPT59n2cLQu+P7kvc+kT+FxnW67pV53F1xz/C1IfgjmycgWpemycosbooQdLvXWPK4GLgk/NOnoZMI/15Lg== |\n| Linux AppImage | QbI7V8jCCVPum4jdd1W8CEOqT+iFzwUTrt9tVbu9Kpv81Pub27aIJve3kWDdXWyvxHPUlUOsBHIo/uHIzdFJPw== |\n| Linux Debian| V0/W8RclF6q0uT6uBR/lDNMt+OXqm7xmkSYf9vd8xCe4mGWUQBHiACX/aIgWs8l3Na5AQCNSJLrHnDXWiDD9Fw== |\n| MacOS Intel | j3bdEX0rvxPGBUMZ6hD9aD+C/WTR1bOZT+lekJqujkDnRSPMZS5syGfkd1cQfx8QSnM10qYBO4RCH0Ew0m3g0A== |\n| MacOS Apple silicon | iKOsvtOLOMcAvlbxL1LJI+45DgJxc+VIe9mVdoJZaNtMPCTAdxBX07GcvVVGfJOE8MdomsKrN8S2yYek7L6YLQ== |"}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.2.70.0","title":"Redis Insight v2.70.0, May 2025","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.70.0/","summary":"Redis Insight v2.70","tags":[],"last_updated":"2025-09-03T08:26:45-07:00","page_type":"content","content_hash":"d45ada640a1eb2caa8a9308649054ce82da1a30de76f28a0dc6dc8f9192522fe","sections":[{"id":"270-may-2025","title":"2.70 (May 2025)","role":"overview","text":"This is the General Availability (GA) release of Redis Insight 2.70."},{"id":"highlights","title":"Highlights","role":"content","text":"- Added support for tagging database connections, making it easier to organize and quickly find what you need. Tags can be created locally in Redis Insight or imported from [Redis Cloud](https://redis.io/cloud/) or [Redis Software](https://redis.io/software/).\n- Introduced highly requested improvements for working with JSON, including full-value editing and the ability to search within a JSON key."},{"id":"details","title":"Details","role":"content","text":"**Features and improvements**\n- [#4476](https://github.com/RedisInsight/RedisInsight/pull/4476), [#4457](https://github.com/RedisInsight/RedisInsight/pull/4457) Added support for database tags using key-value pairs to help categorize and filter database connections. Tags can be created locally in Redis Insight or imported from [Redis Cloud](https://redis.io/cloud/) or [Redis Software](https://redis.io/software/).\n- [#4457](https://github.com/RedisInsight/RedisInsight/pull/4457) Added the ability to hide columns in the database list to optimize screen space.\n- [#4522](https://github.com/RedisInsight/RedisInsight/pull/4522), [#4527](https://github.com/RedisInsight/RedisInsight/pull/4527), [#4483](https://github.com/RedisInsight/RedisInsight/pull/4483) Introduced highly requested improvements ([#2128](https://github.com/RedisInsight/RedisInsight/issues/2128), [#1686](https://github.com/RedisInsight/RedisInsight/issues/1686), [#4424](https://github.com/RedisInsight/RedisInsight/issues/4424)) for working with the JSON data type, including the ability to edit the entire JSON value and search within a JSON using native Ctrl+F/Cmd+F, powered by [Monaco Editor](https://microsoft.github.io/monaco-editor/).\n- [#4483](https://github.com/RedisInsight/RedisInsight/pull/4483) Added a confirmation prompt when attempting to create a duplicate key in a JSON object, preventing silent overwrites.\n\n**Bugs**\n- [#4514](https://github.com/RedisInsight/RedisInsight/pull/4514) Fixed an issue ([#4478](https://github.com/RedisInsight/RedisInsight/issues/4478)) where accessing a JSON key could result in a \"Cannot read properties of undefined (reading '0')\" error.\n\n**SHA-256 Checksums**\n| Package | SHA-256 |\n|--|--|\n| Windows | AMAdQy5TsKc/76OAH4NdJ+abe/ogRdBGTPmVmAMgCG3O74hGZ0Q5h53HMaGzaOpTM+bEiRqTfi/i/iHgTNRmDA== |\n| Linux AppImage | 6E1d++C6C4M8P/onVHrdsC1CwsdQ1dYysLedbIhsgmsxR9t37LA+mdxIWFOCi0Z/XZqox/kGb/EjPxRIyF2apw== |\n| Linux Debian| 3opZq01Di5DCBEX1tYaO59dHbHmeG2928RrIZAcJKiZnGNLNbOeg3AuXalkYuzb3i/kIBLeEKQIwwzW7GFw0VA== |\n| Linux RPM | bC7FpokqgG7/gMqHQRy4pB6U1leu5pWXAcS0jPfUveQHfrbth2zcNlCCIiZ7QW4eUbxeObmDK8dfiL3GojSBLg== |\n| MacOS Intel | gLlxX8kt26QpO0Wa0xjA5+dvW+ZbayJPh/BJ6r4PsRO/l7t7plNNI7RtJKPO4kc39x6FqJ1ipT5poQQQn5lPhQ== |\n| MacOS Apple silicon | V/yQCCf0sndGYgXNkjkKo7FrJwknnCDwKqBg+uUdKjpSwDaDa8zobSbREJ7rDdSMjj6F6eCedYPmhalfe0VZbw== |"}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.2.70.1","title":"Redis Insight v2.70.1, July 2025","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.70.1/","summary":"Redis Insight v2.70.1","tags":[],"last_updated":"2025-07-15T15:12:37+02:00","page_type":"content","content_hash":"c10ec06449a90e7481fc10aa8f37304cbcd44343cca2b2a250194f332b3560ed","sections":[{"id":"2701-july-2025","title":"2.70.1 (July 2025)","role":"overview","text":"This is a maintenance release for Redis Insight 2.70.\n\nUpdate urgency: `LOW` No need to upgrade unless there are new features you want to use.\n\n**Bug fixes**\n- [#4635](https://github.com/redis/RedisInsight/pull/4635) Redis Data Integration metrics are now shown even when the pipeline status is not running.\n- [#4644](https://github.com/redis/RedisInsight/pull/4644) Resolved [layout issues](https://github.com/redis/RedisInsight/issues/4637) in the bulk import summary view.\n- [#4569](https://github.com/redis/RedisInsight/pull/4569) Fixed an [issue](https://github.com/redis/RedisInsight/issues/3416) with a setting to manually enforce standalone mode for clustered database connections instead of automatic clustered mode.\n- Various security enhancements, including vulnerability fixes and dependency updates.\n\n**SHA-256 Checksums**\n| Package | SHA-256 |\n|--|--|\n| Windows | yg+7OLp+KdWgPXwVXWsk26zODO3PoMMhFzNdDQy8FSy1R+lHZAqA42JBA9JNFHUtalZy1k/I3tyZ71TUeSsn+w== |\n| Linux AppImage | hx5XhNIyhna6nYW7wXFjj2XdaDvaq+Fro7qr+iae6cYuY7IO5sH2+oOVVva07yAno02zwxslmNuDkiRWcc5rGw== |\n| Linux Debian| pSwGCXGpxihwymQJOWcbA2Bq97tnSPtnXoIBQ0uut0bwc1SfisHML526a/2ypbyr53TzD0Ut73B+DvWHeRTSbA== |\n| Linux RPM | gxbPo/8+1TkGf8E92BSRHn1I2DtG1PD5Go70WJ0ahRNYoT0FuXBBaERQk3ymwbB7fEcM1CVkwCpUum+iBECUQA== |\n| MacOS Intel | RXPsIECk8jInadX+FGvoM5H49E5mHu1jo6Pq+24AN97ymCIaXIVRIduTHw3sdCYTWJqgLkvjoHKT/IqlR3oayg== |\n| MacOS Apple silicon | EWjwyeGQq8sCeF7syv5LCkWVwXfNbgWYxvkrbAxd8QZmNYrAdl0866k2QGBxIQ3UtJsyePiLhVTJR2qAk6oYzw== |"}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.3.0.0","title":"Redis Insight v3.0.0, November 2025","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.3.0.0/","summary":"Redis Insight v3.0.0","tags":[],"last_updated":"2025-12-03T17:52:36+02:00","page_type":"content","content_hash":"2916ae34e27c45039cd64c84e9f30101ef054a447b8dc85ff79f5a0f5aa2f98b","sections":[{"id":"300-november-2025","title":"3.0.0 (November 2025)","role":"overview","text":"This is the General Availability (GA) release of Redis Insight 3.0.0, a major version upgrade that introduces a new UI experience, a new navigation architecture, and foundational improvements that set the stage for faster, more powerful developer workflows from now on."},{"id":"headlines","title":"Headlines","role":"content","text":"- Refreshed UI with modernized visuals and a more intuitive workflow, making it easier for developers to inspect keys, debug data issues, and move quickly between tasks. The updated design also aligns Redis Insight with the Redis Cloud experience for a unified visual model.\n- New top-level navigation that replaces the left sidebar, making high-usage tools like Browser and Workbench easier to find and access. This layout sets the foundation for improved workflow-driven enhancements."},{"id":"details","title":"Details","role":"content","text":"- Refreshed UI with modernized visuals and a more intuitive workflow, making it easier for developers to inspect keys, debug data issues, and move quickly between tasks.\n- [#5190](https://github.com/redis/RedisInsight/pull/4777) Introduced a new navigation menu that improves discoverability and reduces clicks for common developer workflows."},{"id":"bugs","title":"Bugs","role":"content","text":"- [#5218](https://github.com/redis/RedisInsight/issues/5218) RDI job name field not loading when reopening a job (Redis Data Integration 1.14+).\n- [#4927](https://github.com/redis/RedisInsight/issues/4927) Database list search now properly filters not-connected databases.\n- [#5190](https://github.com/redis/RedisInsight/issues/5190) Electron version bump resolves a MacOS 26 compatibility issue.\n\n**SHA-256 Checksums**\n\n| Package             | SHA-256                                                                                  |\n| ------------------- | ---------------------------------------------------------------------------------------- |\n| Windows             | uv0RwVZt5xIBbFWepOgss0Zdiz8/TuyEcUKYBnzaVPPkIfpxHFpV2LaJ+MNHuQoJETObNQtGcSGtqL/o09pMJQ== |\n| Linux AppImage      | IgQoAayGvrvEHCqix+4GsWt5+nHA5ROJ0vjSdg6+NggTPwfOpP6pHuGIvMlQP2pXjdeynaDPeWY5d+hQWpYtNA== |\n| Linux Debian        | nD27nc7EO0OaML9McebDwrLwp1nfDPwZx9OWQvseomwgvDU0OWbjib5DL31CVqFBE6YNQedYMTatiSF5qmymlw== |\n| Linux RPM           | GzzN86sAmQiikfbfV4k3Hc6mV+9DZZAkvDLqDOs6swYwgcjNmDmjwf89PGUp3BokugPwfHuVAmOfSa3khxSFvA== |\n| MacOS Intel         | zux1c8wOy69USAE88GGaOxGuvsCQxTN6/HlYMpWVgevWe9SFCxrFePb5bD4f7QZoDN0v958aVDiqF3bEYvGwLg== |\n| MacOS Apple silicon | F6YFC+6Quyl7SQMvlyscKq7WXlF2Wkmuf+q6qL6wU/wmzLKa1tfrP0anbtyEl9eGQESpUCZ5bUQo4LB4FBiw6A== |"}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.3.0.2","title":"Redis Insight v3.0.2, January 2026","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.3.0.2/","summary":"Redis Insight v3.0.2","tags":[],"last_updated":"2026-01-08T16:35:15+02:00","page_type":"content","content_hash":"285c8874a48bc988a2ca1109698b519bfa7170dcb76f95d07ee479a6ee480c86","sections":[{"id":"302-january-2026","title":"3.0.2 (January 2026)","role":"overview","text":"This maintenance patch release includes critical and non-critical bug fixes for Redis Insight 3.0.0."},{"id":"bug-fixes","title":"Bug fixes","role":"content","text":"- [#5317](https://github.com/redis/RedisInsight/issues/5317) SQLite package ‘not found’ error on macOS x64 builds.\n- [#5310](https://github.com/redis/RedisInsight/issues/5310) Inaccurately reported CPU metrics when Redis I/O threads or cluster shards push CPU usage above 100%.\n- [#5190](https://github.com/redis/RedisInsight/issues/5190) Delimiter options not persisted on database switch.\n\n**SHA-512 Checksums**\n\n| Package             | SHA-512                                                                                  |\n| ------------------- | ---------------------------------------------------------------------------------------- |\n| Windows             | 4wqbGo0XWd1iXmRMjVtDWCloUzNMWYq0rb13efWv8+odOTv0yHL2Qtxr7nGG4IFzETnL5BCVaoXPp8OkwGzZPA== |\n| Linux AppImage      | bzOdG+/srFdZ9hvWamftwwE+4l4H2/7UuHPb9Xg/G/tyLB1HlTso3/0B1GtWALz807uqDxJavd3GAKwLKOdtSw== |\n| Linux Debian        | 0XrzscKQzrRlKRRuQu0AYdwoK/30HP9e9OsuRFjoOVe4Triw3tgXj1/N5a7JGAqECy4BBMhI6ODCHzKEiEmHsw== |\n| Linux RPM           | grPoYw+/ZPMlNC13muASRqebIHjel+LaqggyVW37eFqHhVo70yEtoxiNogVXOj7y7UDKUG2O0qfDYpgBqlkWoQ== |\n| MacOS Intel         | Ta6PEtDBtjrV+Ut2ArQEaGHB/KYU1OvR6LNIMMTfOgHLssIv1l1YjlcMI0cZJj/zcgD1JtPdIyOe9k7vNXht5Q== |\n| MacOS Apple silicon | aAKmBHK+pBGm2yRSQmdzx2Eno9N237kPi/HmqQ/X+Iv2iWa3zFdw8QDfL28m13+ROslJDbzpHSfZue8fOew5Xg== |"}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.3.0.3","title":"Redis Insight v3.0.3, February 2026","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.3.0.3/","summary":"Redis Insight v3.0.3","tags":[],"last_updated":"2026-02-17T17:01:54+02:00","page_type":"content","content_hash":"846aa599c07031aab6b855834c204fa79ca017f3d72d420e3b8d64fe0b8ef7c9","sections":[{"id":"303-february-2026","title":"3.0.3 (February 2026)","role":"overview","text":"This release includes new features, improvements, and bug fixes for Redis Insight."},{"id":"details","title":"Details","role":"content","text":"- Redis Data Integration (RDI) now uses API v2 when available, showing detailed pipeline status, component statuses, and richer metrics on the Statistics page.\n- RDI pipeline **Start** and **Stop** buttons now correctly reflect component status for newer RDI versions (for example, `flink-collector-source`).\n- LZ4 decompression support for MessagePack-CSharp format, so Redis Insight can correctly display LZ4-compressed MessagePack values.\n- API now returns `4xx` (for example, `400`) for client-side `FT.CREATE` errors instead of `5xx`, improving error handling and monitoring.\n- Bulk delete from **Tree View**: you can delete all keys under a folder or namespace via a delete action on the folder, which opens the **Bulk actions** panel with the pattern pre-filled.\n- **Tutorials**: the open tutorial now shows an open-folder icon instead of the book icon.\n- **String key editor**: the edit area uses the full available space for easier editing.\n- **Copilot**: links in responses no longer overflow the message and insights containers.\n- Docker and store releases are enabled again for this version."},{"id":"bug-fixes","title":"Bug fixes","role":"content","text":"- [#4658](https://github.com/redis/RedisInsight/issues/4658) Implemented fallback to `DEL` when `UNLINK` is unavailable, restoring compatibility with Redis 3.2.12 and similar versions.\n- [#5383](https://github.com/redis/RedisInsight/issues/5383) Fixed default database sorting order so the most recently used databases appear at the top of the list.\n- [#5381](https://github.com/redis/RedisInsight/issues/5381) Restored missing Pub/Sub functionality: message clear option, full message display with line wrapping, and descending chronological order (most recent messages first).\n- [#5382](https://github.com/redis/RedisInsight/issues/5382) Implemented bulk delete for folders in **Tree View**; delete action on a folder opens **Bulk Actions** with the folder pattern pre-filled.\n\n**SHA-512 Checksums**\n\n| Package             | SHA-512                                                                                  |\n| ------------------- | ---------------------------------------------------------------------------------------- |\n| Windows             | +IkHnTZw0saEKT5xYfWFrBEhGOUfc0tP5DygmnpB6r7uFUw42HyATopYr6WsYQrmzAzlhYxgcG61v125WfXR8g== |\n| Linux AppImage      | /OugHCsutRHzqaxe3ppS6N5XkpFarZMc+RTro5owjclv+FvfV6gV+NZzEmzB9bUJgHhu5ImB7/2RbKGexXh/Hw== |\n| Linux Debian        | CSsxeuwhPwQTCOjuiJDQQZZEWB9wyoERrlGP3Pj8qAyDvk9We0DdpMyLintYwaYPjhyK5KDKHMpu1UEZ/GOh4w== |\n| Linux RPM           | Xq0BEuSIsGR/4+QqsSWc/eAawstWZPQqSIj1rT6HqOTJTdp2ovZNZSz85trTMalxf+WJAEmaId3DKHkubEycZQ== |\n| MacOS Intel         | FNdl0wn2J5bfKTv9Et6LTtIdNPOIj3WLvh8hrh/VwrA153KHgZxplyLoX6bvazpnyVptSz5vhQVBwP5yYWbYFQ== |\n| MacOS Apple silicon | jgMyLuRFl2suJo/m3cjwrR1I5v5AMEQIY7Xwj0SihTdhlPzM0WYvSHbZyQUoVyx+tE+tnt1CwV/hQIQpRjVZEA== |"}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.3.2.0","title":"Redis Insight v3.2.0, February 2026","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.3.2.0/","summary":"Redis Insight v3.2.0","tags":[],"last_updated":"2026-02-27T16:20:07+02:00","page_type":"content","content_hash":"4737696610c6d14bd068fdfc6db188e181cbe8e3925dfa8587bbbe2ba134328c","sections":[{"id":"320-february-2026","title":"3.2.0 (February 2026)","role":"overview","text":"This is the General Availability (GA) release of Redis Insight 3.2.0, which includes new features, build updates, and bug fixes."},{"id":"headlines","title":"Headlines","role":"content","text":"Connect to Azure Managed Redis with ease. Auto-discover databases across subscriptions with one-click import and connect using Entra ID and Azure passwordless (OAuth) authentication. To get started, follow the [Azure setup guide](https://github.com/RedisInsight/RedisInsight/blob/main/docs/azure-setup.md) to configure the required permissions."},{"id":"details","title":"Details","role":"content","text":"- Added support for Azure Managed Redis and Azure Cache for Redis tiers. This support includes auto-discovery of databases across subscriptions with one-click import, Microsoft Entra ID (OAuth) authentication with automatic background token refresh, and multi-account support to switch easily between different Azure accounts.\n- Simplified the build process by removing the Webpack dependency. Vite is used now for both development and production builds."},{"id":"bug-fixes","title":"Bug fixes","role":"content","text":"- https://github.com/redis/RedisInsight/pull/5504 Fixed critical security vulnerabilities CVE-2025-55130 (Node.js) and CVE-2025-15467 (OpenSSL) by upgrading the Node.js version and the Alpine base image in Docker.\n\n**SHA-512 Checksums**\n\n| Package             | SHA-512                                                                                  |\n| ------------------- | ---------------------------------------------------------------------------------------- |\n| Windows             | 4sgqVLCjqEmg3N9kAQUZXu1ORln9/RJaQazRK0GLJP9PdCoE57DvdLIQ0NWyo2Y7gKaciWnbYSALSYy1aEaWKA== |\n| Linux AppImage      | D3yFi8AX4nax/Tf9zighm592PVT3Gh6aQ07uABWOrBdp6gA1ENIleypQzrgOqpGyJ3NXMmNjgzmQCdCGHZUL3g== |\n| Linux Debian        | fuCXfh7tlUXQ6gvf/j4mPYG0qH0q94hcIV550ZTyymfSog8CCsxtKJAsySY0mIc+zSBV1nfFfK7qXwKeEmULsw== |\n| Linux RPM           | IdxUbf5M2c2nXd8huWhPxD+V331zsjLQ/T9T5KqkTNw+gPXsjCxPEX99Y6WuaWFrLF4iGDHZarByVqFYOIWnFg== |\n| MacOS Intel         | 6FUSdjAZeqYHg7U+MU8vY4icEwxiA0t14xi8WKh6VVAIf2ZVzjmFt2c4dHqrZuL5moAUJflhOgcZmp5t0xiJpQ== |\n| MacOS Apple silicon | tgtdVOsdph+l3rtq1N83qBwr4Lktz/hkPHpDdFzp0JRSe17Qer04mw8GibL9tyf8eVhJsUSF6dzGovGIq9Mv2A== |"}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.3.4.1","title":"Redis Insight v3.4.1, April 2026","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.3.4.1/","summary":"Redis Insight v3.4.1","tags":[],"last_updated":"2026-04-20T16:29:04+03:00","page_type":"content","content_hash":"3a32dea6cee6c376a369d30573c811ff03028f6f8408dd72f2ddc38da829c604","sections":[{"id":"341-april-2026","title":"3.4.1 (April 2026)","role":"overview","text":"This is the General Availability (GA) release of Redis Insight 3.4.1, which includes new features, enhancements and bug fixes."},{"id":"headlines","title":"Headlines","role":"content","text":"- New dedicated Search workspace with full index lifecycle support: create indexes from sample data or existing user data, query indexed data with an assisted editor, and save queries to a Query Library for reuse.\n- Enhancements for Azure integration, including Entra ID Docker support, Access Key authentication, and a manual connection form."},{"id":"details","title":"Details","role":"content","text":"- New dedicated Search page with index listing, index creation, a query editor with Profile and Explain actions, and a Query Library for saving and reusing queries.\n- Create indexes from sample data to explore how Redis Search works, or from existing user data with automatic field detection and index type configuration.\n- Navigate between Browser and Search workspaces: review the indexed data in the Browser, view indexed keys and their associated indexes, and create indexes directly from the Browser key list.\n- Azure Entra ID authentication support for Docker deployments. [To get started, follow the Azure Docker setup guide.](https://github.com/redis/RedisInsight/blob/main/docs/azure-docker-setup.md)\n- Added a manual connection form for Azure Redis databases, allowing users to configure or override autodiscovery connection details.\n- Added Access Key authentication support for Azure Redis databases as an alternative to Entra ID.\n- Added client-side column sorting for the Browser key list, allowing users to sort by Key, TTL, or Size directly from the Columns popover.\n- [#5637](https://github.com/redis/RedisInsight/issues/5637) Added Linux ARM64 release support with AppImage, deb, and rpm packages."},{"id":"bug-fixes","title":"Bug fixes","role":"content","text":"- [#5394](https://github.com/redis/RedisInsight/issues/5394) Fixed startup error on first install caused by missing `.redis-insight/logs` directory when the parent directory does not exist yet.\n- [#5515](https://github.com/redis/RedisInsight/issues/5515) Added Redis Insight version display in the Web UI Settings, making it easier to verify deployments when running via Docker.\n- [#5540](https://github.com/redis/RedisInsight/issues/5540) Fixed incorrect deserialization of DateTimeOffset values in MessagePack objects.\n- [#5412](https://github.com/redis/RedisInsight/issues/5412) Fixed JSON error when viewing keys containing 'constructor' in the key name.\n- [#5608](https://github.com/redis/RedisInsight/issues/5608) Fixed Workbench view-type dropdown crash when external plugins are loaded.\n- [#5587](https://github.com/redis/RedisInsight/issues/5587) Workbench output settings now persist between commands, so users no longer need to reconfigure their preferred view each time.\n\n**Full Changelog**: https://github.com/redis/RedisInsight/compare/3.2.0...3.4.1\n\n**SHA-512 Checksums**\n\n| Package                | SHA-512                                                                                  |\n| ---------------------- | ---------------------------------------------------------------------------------------- |\n| Windows                | WxS/WgDKWjVg44o6DOVhn5AORBGeuHJEM291vOomJGdA/dzUDumGrRUkUVTVHmJkbRIAMYu6bSJhSQxS8kRZTQ== |\n| Linux AppImage (x64)   | T/FzvvjMjC8zypDZ9xZ/g1wyuVv4G4WKaZVdqhe9P4qa5jaSeBlGS4ZSwCBRQKOS5QtFJrJ+GoKLOKCNIlZqwg== |\n| Linux AppImage (arm64) | ttYKN20quNVhbzoPNpd50UUnCfiJuz6K/dfQIhqI+nNU1w8sOKA+XbL+YGG7jf0VpHZg5ew0zPaDgk86M6vSRw== |\n| Linux Debian (x64)     | hv0ztyHWkk27zum8fp/oIz32Nz/K1Y5V+H5GDiYqrPM7itQDvhezNOaLPWIG3T7+GbmtShK6C+INMbmUY0A8JQ== |\n| Linux Debian (arm64)   | aosn4p641n/31g9ZDy2eaCt+2X7grA9V15D5oKESIFSEljZTsJmaBdJUCR2FDecRnhDrv17N7gsyLrMPsv821Q== |\n| Linux RPM (x64)        | o+YlHT/QzkoIpMaF7gRMNr7JSPqGq2qGMN/MSd6Yz7fbneqTudrsaKIw5Hxa7+kYsJorrGd6CNb3+wLAQKz5aQ== |\n| Linux RPM (arm64)      | Oz7pFhiLEvWkwUwJx0UeOnD4u/BXzIGzsdcdjYcy3K4mx4or+xQ2rvTGWiR9FBrknsOMBFsuwA3VNsPQXoRCdg== |\n| MacOS Intel            | /IrFmPoRLBm2nXLcuyM8JA7shxguEuaAddxc8doPBPGT3ZFGrZxfome8hgegDG23cEifRjF9CYdkes6VCGuzgA== |\n| MacOS Apple silicon    | ReqnQrNTSEVcJnMXc+P849ki5jDvxTvxa1Rm1BIOviig9gEcJBN3WEs9YF8SAQQbbAZ7pdYR+UeMk96gQvkcDQ== |"}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.3.4.2","title":"Redis Insight v3.4.2, April 2026","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.3.4.2/","summary":"Redis Insight v3.4.2","tags":[],"last_updated":"2026-04-22T18:04:36+03:00","page_type":"content","content_hash":"e34ef66b621579c3c8049a76b98a6c127a2eb660850505e8b5c9858cc53ff9dc","sections":[{"id":"342-april-2026","title":"3.4.2 (April 2026)","role":"overview","text":"This is a maintenance release of Redis Insight."},{"id":"bug-fixes","title":"Bug fixes","role":"content","text":"- Fixed issues with the Windows auto-update, so the new version is delivered automatically the next time the app is started.\n\n**SHA-512 Checksums**\n\n| Package                | SHA-512                                                                                  |\n| ---------------------- | ---------------------------------------------------------------------------------------- |\n| Windows                | 1ZqmAteq3AWF2e0C1i3L/yfrSwjhVkE/xLidDrQkJg0Ubqx6kmfCLRUakbibEEO5LBJLiXIVfOsCEraaCQWylg== |\n| Linux AppImage (x64)   | xqXQVzmXhUFM7LYfe9uFw8YSjhIawZelHPvnNHCIXWpnDOAfUhZg2fZf4nh25jamS3BX3cmhJbdK7u1nls3YMg== |\n| Linux AppImage (arm64) | ByesLGHezaiVSy2G+DCLXyF7NxgUSB4Jvprhytq6ALlujyKmleX0OBwyHk9FkJbJjgheU3LlrUYbAU6mrTg58A== |\n| Linux Debian (x64)     | NiEOjj9+bblBAgh4pBYbYWKENhEpV7nTKUNnJTzZRKTCC37A+WIAhCiPDWN0iSQmeXmDc89r5G1+eMjQtggR+A== |\n| Linux Debian (arm64)   | W4WKv+tsbvOSfqJM74wtMqiTbZPNZt22nr9UcIT320nvEHJLBGx35Qk7xsxuZk5OlSPggHUxoxdSqsieDoRjxw== |\n| Linux RPM (x64)        | nkcPvtcDdW4MGbRefXXBpwwJKRkXgfnbqBRdg4Arh+jonR1C37lufH/GZ94Mzwq7yNuiYbbzgwiH2pz3/9krqg== |\n| Linux RPM (arm64)      | dbcdqwYxARaOij1Zy/3qZzAdT1CSlZldYzvtPWPtLRVaVsP8Z/m6YrVl7rnxeaELLqx5GmeUv/Frg0Gjtrfcgg== |\n| MacOS Intel            | hDA2KACZ4ZqkcQoyFaoDtvsDmKelxyZsjAi5E1qdN0j7Zj7900XMGCnN4gsZhy1sCYaS3iUcYx5s313bHrhy/A== |\n| MacOS Apple silicon    | ibQxGJ1axmTsZPMbIvt2ZMpb3vQUuu7vrDIGNXQE6EEDxEmoMfneMW04hEoWuv5Eb+SODm29tIT2HWvD5yCl+A== |"}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.3.6.0","title":"Redis Insight v3.6.0, June 2026","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.3.6.0/","summary":"Redis Insight v3.6.0","tags":[],"last_updated":"2026-06-15T16:19:36+03:00","page_type":"content","content_hash":"23b2078f45dd069bded8ec4811399dc7c5b7fa739ea08e31c498ea3ea1bf416d","sections":[{"id":"360-june-2026","title":"3.6.0 (June 2026)","role":"overview","text":"This is the General Availability (GA) release of Redis Insight 3.6.0, which includes new features, improvements, and bug fixes."},{"id":"headlines","title":"Headlines","role":"content","text":"- Full support for **Vector Sets**, the new Redis 8 vector-native data type: create Vector Sets manually or from a bundled sample dataset, add elements to existing sets, and run similarity search end-to-end from Redis Insight.\n- New **Dev vs Production** database mode with safety guardrails: classify databases by environment, surface clear visual indicators, and require type-to-confirm for destructive actions on production."},{"id":"details","title":"Details","role":"content","text":"- **Vector Sets** are now a supported key type: create new Vector Sets from the Add Key panel — manually with per-element attributes (FP32 or string-array vectors) or in one click via the bundled `vec2word` sample dataset.\n- Manage elements from the key details view: inline `VADD` panel for adding, dynamic attribute columns in the element list, and a details drawer with vector copy/download and JSON attribute editing.\n- Run similarity search on Vector Sets by raw vector or element name, with attribute-aware filter autocompletion, a toggleable command preview, and a configurable results table with pinned Rank/Similarity columns plus per-element Actions (View, Find similar, Delete).\n- New **Dev vs Production** database mode: classify each database as **Production**, **Development**, or **Unspecified** in the connection form, with a PROD badge on production databases and a Development label on development databases.\n- Type-to-Confirm modal required on production for high-risk flows: Browser bulk delete, dangerous CLI/Workbench commands, and Profiler start. Run command and bulk import are disabled inside tutorials on production.\n- Added a \"Skip confirmations on non-production databases\" global setting, a per-command session-skip option, and database-environment tagging on command, bulk action, and profiler telemetry events.\n- New **Geodata Workbench** plugin renders Redis `GEO` command results as an interactive map, density heatmap, or details card, auto-selected per command.\n- Added Copy and Download value actions to `JSON` keys in the Browser, plus a Copy value action on `String` keys that respects the active view format.\n- Settings now shows the build commit SHA next to the app version (e.g. `Redis Insight v3.6.0 (a1b2c3d)`) to make it easier to verify which build is deployed.\n- **Breaking Change / Deprecation Notice**: Custom tutorials in Workbench have been deprecated and are scheduled for removal in the next major version. The \"MY TUTORIALS\" section is now hidden by default, but existing users can temporarily restore the previous behavior by setting `RI_CUSTOM_TUTORIALS_ENABLED=true`. We recommend migrating away from custom tutorials before the next major release."},{"id":"bug-fixes","title":"Bug fixes","role":"content","text":"- [#5835](https://github.com/redis/RedisInsight/issues/5835) Fixed missing scrollbar on the database list when the viewport height was too small to fit all rows.\n- [#5847](https://github.com/redis/RedisInsight/issues/5847) Fixed missing scrollbar in tables across the app at constrained heights, restoring scrollability after the 3.4.2 regression.\n\n**Full Changelog**: https://github.com/redis/RedisInsight/compare/3.4.2...3.6.0\n\n**SHA-512 Checksums**\n\n| Package                | SHA-512                                                                                  |\n| ---------------------- | ---------------------------------------------------------------------------------------- |\n| Windows                | vSOB8uYMGN5pFEwAVgXaUHmx+SLcfh4YVbfGrRW7NZhv467LvzSw3UQAI2Zj6smKQ5ZVkWX+WH9NZOn69owDYQ== |\n| Linux AppImage (x64)   | X8BOPIebctOtF+jd64Nj2Kjl1sAQaI+3AC8VQrJpqE0cNmoJUWOCuiSzDUE/exmG53glJVb0DQ4b2m4s9War2Q== |\n| Linux AppImage (arm64) | IRlVhj6LMNNiKJbGb4qGmQqrbZCBRWNU9tdTVHMA7XvRnbPEdHLBSZYnSLqoK1BqKc8ARHRWNBOXh6xxvNA0gg== |\n| Linux Debian (x64)     | Ak+w3K5Gc1Nj5CLpij2FDyj71tdg4q3a65+AhHwWnoRGlOtUc/QrSkycVpwR8Jz8W6ufgdVGLO7hWmwGfXuPuQ== |\n| Linux Debian (arm64)   | H4wYhkrkRDmUSNkH3/HtAItJzZTEkELL3N++xMmNNP6KpYF33rjNtRdm/VI4y+aPsv/P2wGjX/+SMdSZwwSt9A== |\n| Linux RPM (x64)        | CLaA5IH1D+fLz11Engm0/YExo3Jdobq/oXBrWst4wzW4Yb5P0ciJyL+p0PpkvSc4iT/mygmEolsjgB8zPdS7yA== |\n| Linux RPM (arm64)      | m5ul96vl+06sQ52PHdBjlS6h6KpQpCwdlECkcQdbUk8i+NYWMG2DxpHc7LayrYSs9E2RhTcLBpgcaoNxnOq7yQ== |\n| MacOS Intel            | q+rA5WH9sJolbDTK64J8HaNfaDYxEzhCP8MTCq/JRuyxgGNvQck25h3wC+w7omzl2VfCcXNH2wSBPAGKZUqV/A== |\n| MacOS Apple silicon    | sRwjNWWKyVJ26SHHIX6ENAnT1bs+XEtXC2cyzwYQXiS7iDz0rgKUd6bmtqpToBWLZK9GhU6g9VLU+73b8m54pw== |"}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v.3.8.0","title":"Redis Insight v3.8.0, July 2026","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v.3.8.0/","summary":"Redis Insight v3.8.0","tags":[],"last_updated":"2026-07-21T16:48:25+03:00","page_type":"content","content_hash":"9c5ed790adbb9320fb3c079bcb03aa3ee0c0dc2907af48de88aaadc38e19a920","sections":[{"id":"380-july-2026","title":"3.8.0 (July 2026)","role":"overview","text":"This is the General Availability (GA) release of Redis Insight 3.8.0, which includes new features, improvements, and bug fixes."},{"id":"headlines","title":"Headlines","role":"content","text":"- Full support for the new **Array** data type in Redis 8.8: create Arrays manually or from bundled sample datasets, then browse, search, aggregate, edit, and delete elements end-to-end from Redis Insight.\n- A new **What's New** popup highlights the top features of each release right after you upgrade, with per-version browsing and recommended try-it actions; re-open it anytime from Help.\n- Choose between **IPv4 and IPv6** when connecting to a database, giving explicit control in environments where one protocol doesn't resolve correctly."},{"id":"details","title":"Details","role":"content","text":"- **Arrays** are now a supported key type: create them from the Add Key panel — manually in contiguous or sparse mode, or in one click from one of three bundled sample datasets.\n- Manage Array elements from the key details view: add or append elements, edit values inline or in a multiline Monaco editor, and delete by single element, multi-select, or index range — with a toggleable command preview across all Array forms.\n- Search within Arrays using multi-predicate queries with AND/OR logic and a nearby-elements context band, and run aggregations over elements from the dedicated Aggregate tab. Integer values above 2^53 keep exact precision end to end.\n- View values as **Markdown**: a new value format renders stored values as formatted Markdown for every key type.\n- \"Redis Query Engine\" is now called \"Redis Search\" throughout the app.\n- Host and port are now editable for non-managed database connections, so address changes no longer require re-adding the database.\n- Added a **Copy diagnostics** button in Settings that copies your app version, OS, and build details, ready to paste into a GitHub issue or support request.\n- [#6047](https://github.com/redis/RedisInsight/issues/6047) Azure Entra ID sign-in now supports multi-tenant setups, fixing authentication with Azure Managed Redis when your account's tenant differs from the resource tenant.\n\n**Full Changelog**: https://github.com/redis/RedisInsight/compare/3.6.0...3.8.0\n\n**SHA-512 Checksums**\n\n| Package                | SHA-512                                                                                  |\n| ---------------------- | ---------------------------------------------------------------------------------------- |\n| Windows                | 1TZoW4C0AcRtl/j0cOVpageDZ9jhASyOjtKs2UY7MRjzwdOZ7mPhgGHWe2lAJCN/4uysDn9ucA68WRjXTSoLOg== |\n| Linux AppImage (x64)   | FC/+B7uKlGAMjgA9stAOlHiKhKrPjfTDMX3q6VbIriGa3O+fdz3ENidYO7AFDsHE+1jL/iuAQk/wqu6Ma3hb6A== |\n| Linux AppImage (arm64) | 4RZwtrlFwE2//8bErH8SAhltQWvHe+dDSP8bxz5wvqmfC6Mf52UWp6gQeR7VAejmKEiEvaHTjtgz5ICMwFXzOw== |\n| Linux Debian (x64)     | Se2a4S+Hge60Rl+5GJ8Mz4Z5KHkP2OONcVXeKvrimYmDRItShMZ+8FPLZxdwtq3Qt18BJ/rD2YWglV3MuCFQEw== |\n| Linux Debian (arm64)   | hZ2Tl2gI15tRPPUOK6CyEXFz1Je8qbnHZ/M0bzBgmBWeUwNWHZ4h0tkOzw1SWXSDr3mgKNwf1XFptzcQu196FA== |\n| Linux RPM (x64)        | 1FTnmHi8sSFLjkbDu6cUu69Ere7zXFhQTsNDeoYJKxuyTMO+nusZu5XZLeCJU2NGUIozEh8hlBDGOb0ZVM2HPw== |\n| Linux RPM (arm64)      | vI1Vvl0xgSwBE3F0Z7xtaytwf6QhSrbWYF9/o6p35M+MLQJtieBDCdXuZC8bRg/x9Luw3EgE5XxrOnWxlb7Yow== |\n| MacOS Intel            | 0dmaYw+VzK6DWSD5XDvSSvmkrTWvnyN7o2kf6TQRu1842pLNaDho8rZivf0xxV+CKWTRd82b0BnSe8CD6ePc1A== |\n| MacOS Apple silicon    | l4fC6xeDeZYYLMIVAMOoTyGy+R9DXcnlAktjgi6gNwVUgCN9BxnMvshRxcFTbeO+zrGtbcAfoO7/TDxl8FAq2g== |"}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v1.0.0","title":"RedisInsight v1.0, November 2019","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.0.0/","summary":"The initial release after Redis acquired RDBTools","aliases":["/develop/connect/insight/release-notes/v1.0.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"7955152dfa482beda921f599cf28c9ae3670eb127e01303dca4bdfd3d20ee919","sections":[{"id":"redisinsight-v100-release-notes-november-2019","title":"RedisInsight v1.0.0 release notes (November 2019)","role":"overview","text":"This is the initial release after [Redis acquired RDBTools](https://www.redislabs.com/blog/redisinsight-gui/)."}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v1.1.0","title":"RedisInsight v1.1, December 2019","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.1.0/","summary":"Stability improvements and other fixes","aliases":["/develop/connect/insight/release-notes/v1.1.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"f0b1ddfa9ccd4f21532c75e00ca7f73682951a788ebd4e7c15ae3458fcb2b29c","sections":[{"id":"redisinsight-v110-release-notes-december-2019","title":"RedisInsight v1.1.0 release notes (December 2019)","role":"overview","text":""},{"id":"headlines","title":"Headlines","role":"content","text":"- This release improves overall stability and provides fixes for issues found after the previous release."},{"id":"details","title":"Details","role":"content","text":"- Minor Enhancements:\n    - Core:\n        - Enable mouse wheel support inside the `querycard`.\n    - Browser:\n        - Enable enter key press for adding keys in browser\n    - RediSearch:\n        - Disable HIGHLIGHT markup in JSON view.\n    - Browser:\n        - Improve error message when database is unreachable\n        - Add a reload/refresh button to refresh the value of a key\n        - Enable enter key press for adding keys in browser\n        - Improve error message for unsupported value types\n- Bug Fixes:\n    - RedisGraph:\n        - Fix initial node placement in the view.\n        - Fix initial zoom with respect to the number of nodes in the result.\n    - Other minor fixes."}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v1.10.0","title":"RedisInsight v1.10, March 2021","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.10.0/","summary":"RedisInsight v1.10.0","aliases":["/develop/connect/insight/release-notes/v1.10.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"4394443793fb3fd825920d9a9351546565a175e6aef24bd5f67d33e47e08f1d4","sections":[{"id":"1101-april-2021","title":"1.10.1 (April 2021)","role":"overview","text":"This is the maintenance release of RedisInsight 1.10 (v1.10.1)!"},{"id":"fixes","title":"Fixes:","role":"content","text":"- Core:\n  - Fixed a bug where launching RedisInsight on macOS mojave (10.14.6) would log out the user.\n  - Fixed two major container vulnerability. (CVE-2021-24031, CVE-2021-24032 and CVE-2020-36242)\n  - Select existing installation path on upgrades in Windows.\n- CLI:\n  - Added support for RAW mode (`--raw` in `redis-cli`).\n- Browser:\n  - Fixed a bug where a number in redis string datatype is treated as a JSON .\n  -  RedisJSON - Distinguish between empty and collapsed objects/arrays.\n- Streams:\n  - Added ability to configure auto refresh interval.\n- RedisTimeseries:\n  - Charts now support milliseconds.\n  - Added ability to configure auto refresh interval.\n- RedisGraph:\n  - Properly detect module in Redis Software\n  - Large queries that are truncated in the query card is provided with a tooltip that displays the query on hover.\n  - Properly render boolean data types in the objects.\n- Bulk actions:\n  - Fixed a bug where preview returns duplicate dry run commands."},{"id":"1100-march-2021","title":"1.10.0 (March 2021)","role":"content","text":"This is the General Availability (GA) Release of RedisInsight 1.10 (v1.10.0)!"},{"id":"headlines","title":"Headlines:","role":"content","text":"- Improvements to the way the Browser tool displays \"special\" strings.\n- UX improvements to the RedisGraph tool.\n- Ability to configure the slowlog threshold from within the Slowlog tool."},{"id":"full-details","title":"Full Details:","role":"content","text":"- Overview:\n    - The connection details of the Redis database are now displayed.\n    - A message is displayed to indicate a cluster with no replicas instead of an empty table.\n    - Fixed a bug where the memory usage chart would display an incorrect graph when the memory usage changes rapidly.\n- Browser:\n    - Pretty-print \"special\" strings (like JSON, Java serialized object, Python pickle objects, etc.) once the entire value is loaded.\n    - Pretty-print \"special\" strings (like JSON, Java serialized object, Python pickle objects, etc.) inside container types like Hashes, Sets and Sorted Sets.\n    - Allow sorting the members of a sorted set by score.\n    - Refresh button for the key list.\n    - Delete a key by pressing the \"Delete\" key.\n    - All keys are now visible by default, i.e, the data type filters are disabled by default.\n    - Fixed bug where switching logical databases did not work correctly sometimes.\n    - Fixed bug where adding a field to a hash with an empty value crashes the UI.\n    - Fixed bug where setting TTL to -1 does not effectively delete the key.\n    - Added tooltip explaining how to use the logical database selector along with a submit button.\n- Streams:\n    - Refresh button for the list of streams.\n- RedisGraph:\n    - Node size is now dependent on the number of direct relationships.\n    - Added support for pasting the full `GRAPH.QUERY` command into the query textbox.\n- RediSearch:\n    - Fixed bug where the application fails to execute queries on indices starting with/enclosed within single-quotes.\n- Bulk Actions:\n    - Improved support for operations on a large number of keys.\n- Slowlog:\n    - Allow configuring the slowlog threshold from within the tool for non-cluster databases."}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v1.11.0","title":"RedisInsight v1.11, Oct 2021","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.11.0/","summary":"RedisInsight v1.11.0","aliases":["/develop/connect/insight/release-notes/v1.11.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"ac2238fb2260f8eb5ccd611546e362b1be75aa14478f9ec7461cdcce55e9fe7d","sections":[{"id":"1111-january-2022","title":"1.11.1 (January 2022)","role":"overview","text":"This is the maintenance release of RedisInsight 1.11 (v1.11.1)!"},{"id":"fixes","title":"Fixes:","role":"content","text":"- Core:\n  - Fixed a warning about `urllib` version deprecation.\n  - ACL errors are now show in pretty format while in edit database screen.\n  - Fixed unnecessary warning about segment when there's no internet connection.\n- RediSearch:\n  - Fix index tool support for `v2.2.5`.\n- Bulk actions:\n  - Added support for cross-slot bulk action execution.\n  - Fixed a bug where there's a failure when a malformed UTF-8 characters are present in the key.\n- Memory Analysis:\n  - Added support for `quicklist2` data type.\n- Cluster Management:\n  - Generic errors are also displayed in the tool. This is helpful when connected to a vendor provided redis with custom exceptions."},{"id":"1110","title":"1.11.0","role":"content","text":"This is the General Availability Release of RedisInsight 1.11 (v1.11.0)!"},{"id":"headlines","title":"Headlines:","role":"content","text":"- Added beta support for [RedisAI](https://oss.redis.com/redisai/)\n- Fixed the issue with empty fields for Hash objects."},{"id":"full-details","title":"Full Details:","role":"content","text":"- Core:\n  - Fixed a bug where editing cluster returns error.\n  - Fixed broken redis links.\n- Browser\n  - Check Hash value for `emptiness`\n- RedisGraph:\n  - Added support for point datatype.\n  - Fixed a bug where returning relationships without their respective nodes leads to infinite loading\n- RediSearch:\n  - Fixed a bug where a malformed unicode string in redisearch doesn't produce results.\n- RedisTimeseries:\n  - Added support for `TS.REVRANGE` and `TS.MREVRANGE` commands."}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v1.12.0","title":"RedisInsight v1.12, May 2022","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.12.0/","summary":"RedisInsight v1.12.0","aliases":["/develop/connect/insight/release-notes/v1.12.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"b349f7103eac4fd0d6980eb07dbdd1a64f2214943fe22342811b6f4383564775","sections":[{"id":"1121-july-2022","title":"1.12.1 (July 2022)","role":"overview","text":"This is the maintenance release of RedisInsight 1.12 (v1.12.1)!"},{"id":"critical-bug-fix","title":"Critical Bug Fix:","role":"content","text":"- Core:\n  - When you add or remove a Redis Software Software or Redis Cloud database in RedisInsight v1 that has the RediSearch module loaded, all hashes within that database are deleted."},{"id":"fixes","title":"Fixes:","role":"content","text":"- Core:\n  - Added curl command to container.\n  - Fixed container vulnerabilities (CVE-2022-1292, CVE-2022-2068).\n- RediSearch:\n  - Fixed index info to report correct information for RediSearch v2.\n- Profiling:\n  - Added support for profiling module commands.\n  - Added support for viewing information of clients that use IPv6 addresses.\n- RedisGraph:\n  - Added support for `RO_QUERY` only mode. RedisGraph now responds to the `RO_QUERY` command."},{"id":"1120-may-2022","title":"1.12.0 (May 2022)","role":"content","text":"This is the General Availability Release of RedisInsight 1.12 (v1.12.0)!"},{"id":"headlines","title":"Headlines:","role":"content","text":"- [Authenticate database users](https://docs.redis.com/latest/ri/using-redisinsight/auth-database/): Ask for database username and password\n- Support for `GRAPH.RO_QUERY` command in RedisGraph tool.\n- Support for variable CPU in RedisAI tool."},{"id":"full-details","title":"Full Details","role":"content","text":""},{"id":"core","title":"Core","role":"content","text":"- [Authenticate database users](https://docs.redis.com/latest/ri/using-redisinsight/auth-database/): Ask for database username and password\n  - If enabled, each time a user attempts to open a database previously added to RedisInsight, a form to enter username and password is displayed. This form displays also if a user is idle for a configurable amount of time.\n- Fix major container vulnerabilities.\n- Decrease Docker image size by discarding unnecessary contents.\n- Streams\n  - Fix slowdown and crash while loading large streams data.\n  - Use UTC time for stream id timestamp.\n- Graph\n  - Allow scanning for more keys.\n  - Add support for `GRAPH.RO_QUERY` command.\n- Browser\n  - Fix **Delete key** dialog box that displays when no key is selected.\n- RedisAI\n  - Add support for variable CPU number."}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v1.13.0","title":"RedisInsight v1.13, Aug 2022","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.13.0/","summary":"RedisInsight v1.13.0","aliases":["/develop/connect/insight/release-notes/v1.13.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"b938668f9c12cc5d726bcd0cc05885d48d1d087d45d0027e0620c3f916aeca5d","sections":[{"id":"1131-november-2022","title":"1.13.1 (November 2022)","role":"overview","text":"This is the maintenance release of RedisInsight 1.13 (v1.13.1)."},{"id":"fixes","title":"Fixes:","role":"content","text":"- Core:\n  - Fixed container vulnerabilities.\n  - Prevented healthcheck API from overloading RedisInsight DB. Earlier, a separate session was created for each healthcheck hit, which overloaded the database with too many session tokens. Now, healtcheck API doesn't create any session tokens.\n  - Get Sentinel host using IP field.\n- Memory Analysis:\n  - Added support for `hashlistpack`, `zsetlistpack`, `quicklist2` and `streamlistpack2`, encoding types."},{"id":"1130-august-2022","title":"1.13.0 (August 2022)","role":"content","text":"This is the General Availability Release of RedisInsight 1.13 (v1.13.0)."},{"id":"headlines","title":"Headlines","role":"content","text":"- Subpath Proxy Support"},{"id":"details","title":"Details","role":"content","text":""},{"id":"core","title":"Core","role":"content","text":"- Subpath Proxy support: RedisInsight can now be proxied behind a subpath\n- Added trusted origins environment variable to set trusted origins\n- Fixed major container vulnerabilities\n- Added proxy notification that displays when such an environment is found"},{"id":"redisearch","title":"RediSearch","role":"content","text":"- Fixed index information"},{"id":"profiler","title":"Profiler","role":"content","text":"- Added support for IPv6 clients"},{"id":"memory-analyzer","title":"Memory Analyzer","role":"content","text":"- Fixed Lua recommendation"}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v1.14.0","title":"RedisInsight v1.14, may 2023","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.14.0/","summary":"RedisInsight v1.14.0","aliases":["/develop/connect/insight/release-notes/v1.14.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"994345eeb411aefe45b5827f2b5d65e40d5729a65afbb133820c09112012b0b5","sections":[{"id":"1140-may-2023","title":"1.14.0 (May 2023)","role":"overview","text":"RedisInsight version 1.X was retired on April 30, 2023, and will no longer be supported.\nTo continue using the best RedisInsight features and capabilities, download the latest RedisInsight version 2.Y from our [website](https://redis.com/redis-enterprise/redis-insight/) or install it from an app store.\n\nThis is the maintenance release of RedisInsight 1.14 (v1.14.0)."},{"id":"headlines","title":"Headlines","role":"content","text":"- Export connections to RedisInsight v2."},{"id":"details","title":"Details","role":"content","text":""},{"id":"core","title":"Core","role":"content","text":"- Added support for exporting database connections to easily migrate them to RedisInsight v2 by bulk exporting to a file.\n  - Fixed Prompt verification bug for Sentinel instances."},{"id":"memory-analysis","title":"Memory analysis","role":"content","text":"- Added support for `setlistpack` and `streamlistpack3` Redis 7 encoding types parsing."}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v1.2.0","title":"RedisInsight v1.2, January 2020","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.2.0/","summary":"TLS Client side authentication support and stability improvements","aliases":["/develop/connect/insight/release-notes/v1.2.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"6598a10253f35d5929832d645bef1248265a1e208daa4affed439f13d9cd18ee","sections":[{"id":"redisinsight-v122-release-notes","title":"RedisInsight v1.2.2 release notes","role":"overview","text":"Update urgency: Medium\n\nThis is a maintenance release for version 1.2."},{"id":"details","title":"Details","role":"content","text":"- Bug Fixes:\n    - Core:\n        - This releases fixes the possible __false positive__ malware issues flagged by certain antivirus vendors introduced by [pyinstaller](https://github.com/pyinstaller/pyinstaller/issues/4633) which was reported on [reddit](https://www.reddit.com/r/redis/comments/f1qapz/redisinsight_cotains_malware/)."},{"id":"redisinsight-v121-release-notes","title":"RedisInsight v1.2.1 release notes","role":"content","text":"Update urgency: Medium\n\nThis is a maintenance release for version 1.2."},{"id":"details-1","title":"Details","role":"content","text":"- Enhancements:\n    - Core:\n        - Upgrade notifications: When you open RedisInsight, a notification is shown if a new version is available.\n    - RediSearch:\n        - Support for [RediSearch 1.6](https://github.com/RediSearch/RediSearch/releases/tag/v1.6.7).\n- Minor Bug Fixes:\n    - RedisTimeSeries:\n        - Time was interpreted as seconds instead of milliseconds as ([Issue 332](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/332))"},{"id":"redisinsight-v120-release-notes","title":"RedisInsight v1.2.0 release notes","role":"content","text":""},{"id":"headlines","title":"Headlines","role":"content","text":"- This release improves overall stability and provides fixes for issues found after the previous release.\n- Added support for Client side TLS authentication.\n- Resolved bug which caused blank pages at startup."},{"id":"details-2","title":"Details","role":"content","text":"- New features:\n    - Core:\n        - Added support for Redis databases that require TLS client authentication (as in Redis Software)\n    - RedisTimeseries:\n        - Initial `auto-updating` functionality when the query's end timestamp is `+`\n- Minor Enhancements:\n    - Core:\n        - Check whether the port is available before starting.\n        - Made `localhost` the default host instead of `0.0.0.0`.\n        - Improved logging during startup.\n    - RedisGraph:\n        - Fixed height of query cards.\n    - RediSearch:\n        - Add support for zero-length and whitespace-only index names.\n- Bug Fixes:\n    - Core:\n        - Moved server to another thread instead of a separate process.\n        In certain situations, the server process was being orphaned after the main process died. This resulted in a several issues, of which the \"blank page issue\" was the most common. Now that the server process is in a thread instead of a process, the server is not left running when the process exits.\n    - RediSearch:\n        - Fixed several bugs in the display of summarized results in the table view.\n    - Browser:\n        - Better handling of unsupported values - link to other tools that support it or show better error message.\n        - Fixed UI issues when the screen size is varied to provide better responsiveness."}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v1.3.0","title":"RedisInsight v1.3, March 2020","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.3.0/","summary":"Auto-discovery of Redis Cloud databases, visualising paths in RedisGraph","aliases":["/develop/connect/insight/release-notes/v1.3.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"aff2a73e93619f0d9da3ef7b68f1d5aa72a9e1d75edad0d3dd0f283fb7593a7b","sections":[{"id":"redisinsight-v131-release-notes-april-2020","title":"RedisInsight v1.3.1 release notes (April 2020)","role":"overview","text":"This is the maintenance release of RedisInsight 1.3 (v1.3.1).\n\nUpdate urgency: Medium"},{"id":"headlines","title":"Headlines","role":"content","text":"- Fixed support for connecting to Redis database on TLS-enabled hosts with SNI enforcement."},{"id":"details","title":"Details","role":"content","text":"- Bug Fixes:\n    - Core:\n        - Fixed support for connecting to Redis database on TLS-enabled hosts with SNI enforcement.\n    - Memory Analysis\n        - Fixed wrong display of table columns in Memory Analyzer view.\n    - Browser\n        - Fixed bug where the TTL on string and RedisJSON keys was being reset on edit.\n    - Configuration:\n        - Fixed freezing/flashing on refreshing configuration.\n    - CLI\n        - Fixed minor visual bug in inline command documentation.\n    - Security\n        - Updated frontend dependencies that had developed security vulnerabilities."},{"id":"redisinsight-v130-release-notes-march-2020","title":"RedisInsight v1.3.0 release notes (March 2020)","role":"content","text":"This is the General Availability release of RedisInsight 1.3 (v1.3.0)!"},{"id":"headlines-1","title":"Headlines","role":"content","text":"- The Windows installer is now signed with a Microsoft Authenticode certificate.\n- Auto-Discovery of databases for Redis Cloud Pro.\n- Visualising paths in RedisGraph"},{"id":"details-1","title":"Details","role":"content","text":"- Features:\n    - Security:\n        - The Windows installer now signed with a Microsoft Authenticode certificate\n    - Core:\n        - Auto-Discovery for Redis Cloud Pro: Redis Cloud Pro subscribers can automatically add\n          their cloud databases with just a few clicks\n        - Support for editing the connection details of an added database\n        - Better support for Sentinel-monitored databases with different passwords for the sentinel instance(s) and database\n        - UI improvements to the add database form\n        - Added a button in the top-right menu to reach the online documentation with one click\n    - RedisGraph:\n        - Added support for visualising queries that use [path functions](https://oss.redislabs.com/redisgraph/commands/#path-functions)\n    - Memory Analysis:\n        - Added support for [virtual hosted-style](https://docs.aws.amazon.com/AmazonS3/latest/dev/VirtualHosting.html#virtual-hosted-style-access) S3 paths for Offline Analysis\n    - Browser:\n        - Added tooltip to make it easier to view the name of long keys\n\n- Bug Fixes:\n    - Core:\n        - Fixed fonts that were being loaded from the Internet, causing jarring visual changes on slow connections\n    - RedisGraph:\n        - Improved rendering of Array records\n        - Removed `GRAPH.EXPLAIN` calls for now until we have execution plan visualisation"}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v1.4.0","title":"RedisInsight v1.4, April 2020","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.4.0/","summary":"Redis 6 ACLs support, improved CLI and full screen support in Graph, TimeSeries and RedisSearch","aliases":["/develop/connect/insight/release-notes/v1.4.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"bb73ab24c464dcdcbbc550d41cb9a0a5695362ea808fb6db81ce06a5be9cdf24","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is the General Availability Release of RedisInsight 1.4 (v1.4.0)!"},{"id":"headlines","title":"Headlines","role":"content","text":"- Support for Redis 6, Redis Software 6 and ACLs\n- Improve CLI capabilities with removed command restrictions\n- Full screen support in Graph, TimeSeries and RediSearch"},{"id":"full-details","title":"Full details:","role":"content","text":"- Features\n    - Core:\n        - Added support for Redis 6 + RE6 and authentication using ACL\n        - Added Full screen support for Graph, TimeSeries and RediSearch.  \n        - Improved UI consistency (colors and styles) in Graph and Timeseries\n    - CLI:\n        - Removed the command restrictions, unless a command is specifically blacklisted.\n        - Command responses are displayed in exactly the same way as in `redis-cli`\n    - RedisGraph:\n        - Optimized performances when getting nodes relationships (edges) from user's queries\n    - Stream:\n        - Improved UX when defining the timing range of events to be filtered\n\n- Bug Fixes:\n    - Core:\n        - Fixed issue when connecting to Redis Software with a password using a special character\n    - Stream:\n        - Fixed ability to properly visualize all events"},{"id":"known-issues","title":"Known issues","role":"content","text":"- Core:\n    - Authentication to Redis 6 OSS in cluster mode is not supported yet\n- CLI:\n    - Blocking commands are not supported\n    - Commands which return non-standard streaming responses are not supported: `MONITOR, SUBSCRIBE, PSUBSCRIBE, SYNC, PSYNC, SCRIPT DEBUG`"}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v1.5.0","title":"RedisInsight v1.5, May 2020","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.5.0/","summary":"New tool for RedisGears, Multi-line query builder and improved suppport of Redis 6 ACLs","aliases":["/develop/connect/insight/release-notes/v1.5.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"538ffab42ccd158fd59e48fcdb7f7dbe30f69364533125b42ad3527fffc9cacf","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is the General Availability Release of RedisInsight 1.5 (v1.5.0)!"},{"id":"headlines","title":"Headlines","role":"content","text":"- Added beta support for [RedisGears module](https://oss.redislabs.com/redisgears/)\n- Added multi-line query editing for RediSearch, RedisGraph and Timeseries\n- Improved support of Redis 6 ACLs"},{"id":"full-details","title":"Full details:","role":"content","text":"- Features\n    - Core:\n        - Improved support for Redis 6 managing ACL permissions for each different capabilities\n    - Gears:\n        - Beta support for [Redis Gears module](https://oss.redislabs.com/redisgears/)\n            - Explore the latest executed functions and analyze the results or errors\n            - Manage registered functions and get execution summary\n            - Code, build and execute functions\n    - RediSearch:\n        - Multi-line for building queries\n    - RedisGraph:\n        - Multi-line for building queries\n    - Timeseries:\n        - Multi-line for building queries\n\n- Bug Fixes:\n    - Configuration:\n        - Fixed issue not showing the list of modules\n    - Search:\n        - Fixed issue preventing users to see all documents matching a search query\n        - Fixed issue with retrieving the search indexes in case of large database"}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v1.6.0","title":"RedisInsight v1.6, June 2020","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.6.0/","summary":"Rootless Docker container, Copy keys in Browser and Stream UX improvements","aliases":["/develop/connect/insight/release-notes/v1.6.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"09405682a818abbe886f19563a585b948c66aadc7180efc37dbc7a904311d807","sections":[{"id":"163-july-2020","title":"1.6.3 (July 2020)","role":"overview","text":"Maintenance release for RedisInsight 1.6 including bug fixes and enhancements."},{"id":"headlines","title":"Headlines:","role":"content","text":"- Mac application is now getting notarized which simplifies the installation process on OS X\n- Fixed the resize of the keys explorer allowing to see long keys\n- Fixed filtering of keys in the browser with the filter capability in the browser"},{"id":"full-details","title":"Full details:","role":"content","text":"Enhancements and bug fixes\n  - Core:\n    - Mac application is properly signed and notarized on Apple services.\n  - Browser:\n    - Fixed resizing the key explorer allows to see more characters of long keys.\n    - Asynchronous loading of keys in large databases (discovered keys are actionable while search continues).\n    - Improved performance for exact key searches (when search pattern is not using *).\n    - Improved UI showing the progress of scanning the keys in the database.\n    - Fixed filtering the keys by data structures could be  to wrong.\n    - Fixed behavior of EXISTS and TYPE command when those have ACL restrictions.\n    - Fixed behavior when keys matching filters have ACL restrictions.\n    - Improved \"Stop Scan\" button behavior to respond immediately.\n    - Added visual indicator to show that by default, the browser is filtering out inner keys from modules."},{"id":"162-30-june-2020","title":"1.6.2 (30 June 2020)","role":"content","text":"Maintenance release for RedisInsight 1.6 including bug fixes and enhancements."},{"id":"headlines-1","title":"Headlines:","role":"content","text":"- Performance improvements to Profiler tool for TLS-enabled databases.\n- Bugfix: Feedback button was not visible."},{"id":"full-details-1","title":"Full details:","role":"content","text":"- Enhancements and bug fixes\n  - Core:\n      - Bugfix: Feedback button was not visible.\n  - Profiler:\n      - The native code implementation of the profiling logic was updated to add full support for TLS connections to Redis.\n  - Graph:\n      - Updated to use a newer version of the Ogma graph visualization library.\n  - Analytics:\n      - Bugfix: Report the OS/platform correctly."},{"id":"161-24-june-2020","title":"1.6.1 (24 June 2020)","role":"content","text":"Maintenance release for RedisInsight 1.6 including bug fixes and enhancements."},{"id":"headlines-2","title":"Headlines:","role":"content","text":"- Improved support for Redis 6 ACLs with Cluster and Sentinel databases\n- Added support for Redis Cluster in RedisGraph tool\n- UX improvements for RedisGraph tool\n- Enriched captured usage events"},{"id":"full-details-2","title":"Full details:","role":"content","text":"- Enhancements and bug fixes\n  - Core:\n    - Improved support for Redis 6 ACLs with Cluster and Sentinel databases\n    - Added events capturing usage of RedisInsight\n  - Graph:\n    - Add support for Redis Cluster\n    - Added option to configure labels to be displayed in graph's nodes (right-click on the node)\n    - Added ability to submit query with 'ctrl + enter' in single line mode\n  - TimeSeries:\n    - Added ability to submit query with 'ctrl + enter' in single line mode\n  - RediSearch:\n    - Added ability to submit query with 'ctrl + enter' in single line mode\n    - Better handling of long index names in index selector dropdown\n    - Fixed bug with pagination on queries with whitespace in the query string\n  - Gears:\n    - Added button to remove an execution from executed functions list\n    - Added option to get rid of the warning message when executing a function \n    - Fixed error message when it's impossible to visualize the graph\n  - Streams:\n    - Added persistence of user's selected stream columns when switching pages"},{"id":"160-11-june-2020","title":"1.6.0 (11 June 2020)","role":"content","text":"This is the General Availability Release of RedisInsight 1.6!"},{"id":"headlines-3","title":"Headlines:","role":"content","text":"- RedisInsight docker container is now rootless being compliant with best practices for containers\n- The Browser gets improved to allow quick copy of keys and resizing of the key explorer\n- Streams is now allowing to sort entries by timestamp, active/unactive live streaming of entries and keep persisted user's selection of fields to save context when switching between streams or other tools of RedisInsight. \n- New telemetry system allowing to capture tools usage and updated privacy settings"},{"id":"full-details-3","title":"Full details:","role":"content","text":"- Features\n  - Core:\n    - Improved docker container by making it rootless\n    - Added visual indicator to show configured user when connecting to Redis 6 using ACLs\n    - Improved navigation to application's settings\n  - Browser:\n    - Added ability to resize the Key explorer panel\n    - Added options to easily copy keys\n    - Added ability to filter out the inner keys\n  - Streams: \n    - Added persisting selected fields to be displayed to save context when switching to another stream or tool of RedisInsight \n    - Added ability to sort entries \"ascending\" or \"descending\" based on the timestamp\n    - Added ability to active/unactive the live streaming of events \n    - Updated timestamp font family for consistency\n  - CLI:\n    - Added ACL commands hints and summary info in CLI\n\n- Bug Fixes:\n  - Core:\n    - Fixed issue fetching data from Redis Cloud and replica enabled\n  - Browser:\n    - Fixed issue not shown field named \"key\" in hash keys\n    - Fixed wrong number of database's keys being displayed\n    - Fixed error when trying to view a Java serialized object\n  - Stream:\n    - Fixed issue with live streaming of entries\n    - Fixed UI when no entries are present in a stream\n  - Bulk Actions:\n    - Fixed responsiveness of the UI\n  - RedisGears:\n    - Fixed focus on editor and display of requirements"}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v1.7.0","title":"RedisInsight v1.7, September 2020","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.7.0/","summary":"RediSearch 2.0 support and stability improvements","aliases":["/develop/connect/insight/release-notes/v1.7.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"046969f963752693bf87b2684d6b9635ea898fe43d6da80051803a79b3eff0ea","sections":[{"id":"171-october-2020","title":"1.7.1 (October 2020)","role":"overview","text":"Maintenance release for RedisInsight 1.7 including bug fixes and enhancements."},{"id":"headlines","title":"Headlines:","role":"content","text":"- Core:\n    - New public health-check API to make monitoring deployments easier.\n    - Display progress information during memory analysis."},{"id":"full-details","title":"Full details:","role":"content","text":"Enhancements and bug fixes\n- Core:\n    - Fixed support for TLS in Redis Cluster databases.\n    - Application name is properly capitalized on MacOSX.\n    - Fixed update notifications on Docker - Now links to Docker Hub page and provides instructions for updating.\n- Memory Analysis:\n    - Information about the current stage of analysis is now displayed while the analysis runs.\n    - Fixed issue with running Memory Analysis on MacOSX (related to system OpenSSL libraries).\n- Browser:\n    - Visual improvements to key details view to improve the experience working with long key names.\n- CLI:\n    - Improvements for Redis Cluster databases - Controls to target specific nodes, all nodes, only masters/replicas, etc.\n- Streams:\n    - Fixed consumer groups functionality on Redis Cluster databases.\n- Telemetry:\n    - Report specific modules even when the `MODULE LIST` command is not available."},{"id":"170-september-2020","title":"1.7.0 (September 2020)","role":"content","text":""},{"id":"headlines-1","title":"Headlines:","role":"content","text":"- Support for [RediSearch 2.0](https://redislabs.com/blog/introducing-redisearch-2-0/)"},{"id":"full-details-1","title":"Full Details:","role":"content","text":"- Core:\n    - Added explanation of the supported subscription types for Redis Cloud database auto-discovery.\n    - Fixed a bug where upgrading from some previous versions would give an error on startup.\n    - Use a non-root group by default for the RedisInsight Docker container.\n- Memory Analysis:\n    - Improved UI for offline analysis via RDB file stored in S3.\n    - Fixed bug where using RDB stored in S3 sub-folder would fail.\n- Browser:\n    - Improved support for searching members of large collections (hashes, sets and sorted sets).\n- Streams:\n    - Improved UX for the handle to resize key selector.\n- RediSearch:\n    - Fixed support for Redis Cloud Essentials databases.\n- RedisGraph:\n    - Fixed an issue where localstorage is filled with unnecessary data.\n- Analytics:\n    - Reporting the subscription type for auto-discovered Redis Cloud databases."}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v1.8.0","title":"RedisInsight v1.8, November 2020","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.8.0/","summary":"RedisInsight v1.8.0","aliases":["/develop/connect/insight/release-notes/v1.8.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"4d29d18cc860360ecf51b23109f817696c6769a4dd1cd639cd2e062a803c8a5a","sections":[{"id":"183-january-2021","title":"1.8.3 (January 2021)","role":"overview","text":"This is a maintenance release of RedisInsight 1.8 (v1.8.3)!\n\nThis fixes the crash on MacOS Big Sur (11.1) for the MacOS package.\nRedisInsight is supported on Mac hardware with Intel chips, but not for Mac hardware with the Apple M1 (ARM) chip."},{"id":"182-24-december-2020","title":"1.8.2 (24 December 2020)","role":"content","text":"This is the maintenance release of RedisInsight 1.8 (v1.8.2)!"},{"id":"fixes","title":"Fixes:","role":"content","text":"- Browser:\n    - Improved handling of large strings, lists, hashes, sets and sorted sets.\n    - Better error message when loading a key's value fails.\n    - More robust handling of Java serialized objects - malformed Java serialized objects are now displayed as binary strings.\n    - Increased the default width of the key selector.\n- Memory Analysis:\n    - Fixed crash on databases with modules that store auxiliary module data in the RDB (RediSearch 2, RedisGears, etc.).\n    - Fix integer overflow which results in the size of large keys not being reported properly.\n    - Add Streams statistics to the charts shown in the Memory Analysis Overview tool.\n    - Better error message when online analysis fails due to both the `SYNC` and `DUMP` commands being unavailable."},{"id":"181-17-december-2020","title":"1.8.1 (17 December 2020)","role":"content","text":"Maintenance release for RedisInsight 1.8 including bug fixes and enhancements."},{"id":"important-note","title":"Important Note:","role":"content","text":"We'd love to learn more how you are using RedisInsight. Now we have a user survey in the application, but you can also get to it [here](https://www.surveymonkey.com/r/ZZVR2ZG)."},{"id":"fixes-1","title":"Fixes:","role":"content","text":"- Core:\n    - Fixed placeholder page for modules which was not appearing on desktop packagings of RedisInsight.\n    - Fixed error in handling disconnection to databases on desktop packaging of RedisInsight.\n    - Fixed auto-fill-on-URL-paste when adding Redis databases.\n    - Fixed add database form: scroll to error message if adding a database fails.\n    - Fixed typo in Settings page.\n- CLI:\n    - Fixed on the repeat command option which was not properly cleared when the command was changed."},{"id":"180-november-2020","title":"1.8.0 (November 2020)","role":"content","text":"This is the General Availability Release of RedisInsight 1.8 (v1.8.0)!"},{"id":"important-note-1","title":"Important Note:","role":"content","text":"We'd love to learn more how you are using RedisInsight. We introduce a user survey, you'll see it in the application, but it's happening [here](https://www.surveymonkey.com/r/ZZVR2ZG)."},{"id":"headlines","title":"Headlines:","role":"content","text":"- Guided experience to get started with RedisInsight, add a database and experiment with modules\n- CLI now supports \"help\" commands and lets you repeat commands\n- Ability to provide your CA Certificate and skip-verify option for TLS authentications\n- Ability to display RediSearch indices summary\n- Adding a database is getting simpler by auto-filling all database information from the Redis connection URL\n- New environment variables for configuring HOST, PORT and application LOG LEVEL\n- Support for RedisJSON on Redis Cluster"},{"id":"full-details","title":"Full Details:","role":"content","text":"- Core:\n    - New welcome page when no databases are configured\n    - New information pages for each modules, when they are not configured\n    - New environment variables for configuring HOST, PORT and application LOG LEVEL\n    - Ability to add a CA Certificate and skip-verify for TLS authentications\n    - Added auto-filling database details from Redis Connection URL\n- Browser:\n    - Added error message when trying to visualize large keys\n    - Fixed issue when copying key with \" character\n- RedisGraph, RediSearch, RedisTimeseries:\n    - Added ability to copy commands with a button click\n- RedisSearch:\n    - Display the selected Index's Summary\n- RedisGraph:\n    - Added ability to persist nodes display choices between queries\n- ReJSON:\n    - Support for RedisJSON on Redis Cluster\n- ClientList:\n    - Sort clients by type\n- CLI:\n    - Added support for the `help` command in CLI\n    - Added ability to repeat commands multiple times with increments\n    - Added the ability to close the Hint window\n    - Added stream command assists\n- Profiler:\n    - Fixed issue with TLS databases"}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v1.9.0","title":"RedisInsight v1.9, January 2021","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.9.0/","summary":"RedisInsight v1.9.0","aliases":["/develop/connect/insight/release-notes/v1.9.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"6f59bd061a60869d2cb23d8f026df32bfd12da9c4d51066916f027a36478d987","sections":[{"id":"190-january-2021","title":"1.9.0 (January 2021)","role":"overview","text":"This is the General Availability Release of RedisInsight 1.9 (v1.9.0)!"},{"id":"headlines","title":"Headlines:","role":"content","text":"- RedisGraph tools is getting improved with better UX and new interactions capabilities\n- Adding the ability to configure a database, by just using its direct URL to auto fill all required fields\n- CLI is now providing ability to configure your favorite key-bindings between Emacs or Vim"},{"id":"full-details","title":"Full Details:","role":"content","text":"- Core:\n    - Support for configuration of Redis where the number of databases goes over the default 16.\n    - Ability to add a Redis database using a shareable URL.\n- RedisGraph\n    - UX improvements for large queries: fixed long pause while results are being rendered.\n    - Made various improvements to interactions with the graph visualization:\n    - Selected node's size increased to make it easier to distinguish.\n    - Zoom via mouse wheel.\n    - Double click to zoom in.\n    - Double right-click to zoom out.\n    - Keyboard shortcuts to zoom.\n    - Center on node on fetching direct neighbours.\n    - Halo masking indirect edges on selected node.\n    - Button to reset view: center entire graph.\n    - Button to center on the selected node.\n    - New zoom buttons\n- CLI\n    - Basic navigation key-bindings for Emacs and Vim.\n    - UX improvements: the inputs and other controls are now disabled and a message is shown while the command is executing."}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v2.0","title":"RedisInsight v2.0, Nov 2021","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v2.0/","summary":"RedisInsight v2.0.2","aliases":["/develop/connect/insight/release-notes/v2.0/"],"tags":[],"last_updated":"2026-09-11T15:49:15+01:00","page_type":"content","content_hash":"62f8858916ab6ea46e31a40278d3089f1abbfa904fccfbf1cd8dea8c9a5f3255","sections":[{"id":"206-april-2022","title":"2.0.6 (April 2022)","role":"overview","text":"This is the General Availability (GA) release of RedisInsight 2.0.6"},{"id":"headlines","title":"Headlines:","role":"content","text":"- SNI support - added SNI support to indicate a hostname in the TLS handshake\n- Save Profiler logs into a file - now you can save and download Profiler logs into a .TXT file\n- Customize delimiters in Tree view - added support for custom delimiters in Tree view\n- Support for node grouping and pulsing in RedisGraph visualizations in Workbench"},{"id":"details","title":"Details","role":"content","text":"**Features and improvements:**\n- [#548](https://github.com/RedisInsight/RedisInsight/pull/548), [#542](https://github.com/RedisInsight/RedisInsight/pull/542) Added SNI support - use the \"Add Database Manually\" form to see the new \"Use SNI\" option under the TLS section to specify the server name and connect to your Redis Database\n- [#521](https://github.com/RedisInsight/RedisInsight/pull/521/files) Added an option to save Profiler logs. Enable saving before starting the Profiler to save the logs into a .TXT file and download it to analyze them outside of the application\n- [#496](https://github.com/RedisInsight/RedisInsight/pull/496) Now you can specify your own delimiters to work with namespaces in the Tree view, default delimiter is colon (':')\n- [#473](https://github.com/RedisInsight/RedisInsight/pull/473) Added a link to GitHub repository of RedisInsight to quickly find and access it - you can see the icon below the \"Settings\"\n- [#586](https://github.com/RedisInsight/RedisInsight/pull/586) Added support for node grouping and pulsing in the visualisations for RedisGraph in Workbench\n- [#455](https://github.com/RedisInsight/RedisInsight/pull/455) Limited the movement of the special editor with Cypher highlights to Workbench Editor area (to work with it, just type in RedisGraph commands in Workbench)\n- [#462](https://github.com/RedisInsight/RedisInsight/pull/462) Provided additional information about database indexes in the form to add a database using host and port\n- [#489](https://github.com/RedisInsight/RedisInsight/pull/489) Reworked user experience with filters per key type and key name in Browser \n- [#535](https://github.com/RedisInsight/RedisInsight/pull/535) Added highlights of timestamps and improved text wrapping in Profiler"},{"id":"bug-fixes","title":"Bug fixes:","role":"content","text":"- [#581](https://github.com/RedisInsight/RedisInsight/pull/581) Fixed the issue with displaying keys in multi-shard databases\n- [#576](https://github.com/RedisInsight/RedisInsight/pull/576) Fixed encoding in Workbench"},{"id":"205-march-2022-ga","title":"2.0.5 (March 2022, GA)","role":"content","text":"This is the General Availability (GA) release of RedisInsight 2.0."},{"id":"headlines-1","title":"Headlines","role":"content","text":"* **Tree view** - A new view of the keys in Browser, which automatically groups keys scanned in your database into folders based on key namespaces. Now you can navigate through and analyze your list of keys quicker by opening only folders with namespaces you want.\n* **Support for Apple M1 (arm64)** - You can download it [here](https://redis.com/redis-enterprise/redis-insight/#insight-form).\n* **Added auto-discovery of local databases** - RedisInsight will automatically find and add your local databases when you open the application for the first time.\n* **A dedicated Editor for Cypher syntax** - Workbench supports autocomplete and highlighting of Cypher syntax for RedisGraph queries."},{"id":"details-1","title":"Details","role":"content","text":"- You can switch to the Tree view in Browser to see all the keys grouped into folders according to their namespaces. Note that we use the colon (:) as a default separator, and it is not customizable yet.\n- Added support for Apple M1 (arm64).\n- Added a mechanism to auto-discover local databases based on the following parameters:\n  - The mechanism only triggers when you open the application for the first time.\n  - The database has standalone connection type.\n  - The database uses the default username and requires no password or TLS certificates.\n- Added new built-in guides in Workbench for additional capabilities.\n- Added tutorials in Workbench for Redis Stack databases that describe common use cases for Redis capabilities.\n- Added a new dedicated Editor to Workbench with support for Cypher syntax autocomplete and highlighting. Use the \"Shift+Space\" shortcut inside of the quotes for your query to open the dedicated Editor.\n- Show modules uploaded to databases in the list of databases.\n- Added support for returning to the previous command in Workbench Editor. Use arrow up when your cursor is at the beginning of the first row to return to the previous command. Note: there is no support for the reverse direction yet, so use it with caution.\n\nIf you installed RedisInsight-preview before, this folder will still exist at the following path:\n* For MacOs: <usersHomeDir>/.redisinsight-preview\n* For Windows: C:/Users/{Username}/.redisinsight-preview\n* For Linux: <usersHomeDir>/.redisinsight-preview"},{"id":"204-february-2022","title":"2.0.4 (February 2022)","role":"content","text":"This is the maintenance release of RedisInsight Preview 2.0 (v2.0.4)!"},{"id":"headlines-2","title":"Headlines","role":"content","text":"- Fixes to the issues found\n- Profiler\n  - Added RedisInsight Profiler, which uses the MONITOR command to analyze every command sent to the redis instance in real-time.\n- Workbench:\n  - Added support for RedisGears and RedisBloom on the intelligent Redis command auto-complete.\n  - Keep command results previously received in the Workbench.\n  - Support for repeating commands.\n- CLI:\n  - Added support for RedisGears and RedisBloom on the intelligent Redis command auto-complete.\n  - Support for repeating commands.\n- Command Helper:\n  - Added information about RedisGears and RedisBloom Redis commands."},{"id":"details-2","title":"Details","role":"content","text":"- Profiler\n  - Added RedisInsight Profiler, which uses the MONITOR command to analyze every command sent to the redis instance in real-time. Note: Running the MONITOR command is dangerous to the performance of your production server, so run it reasonably and remember to stop the Profiler. \n- Workbench:\n  - Added support for RedisGears and RedisBloom on the intelligent Redis command auto-complete, so the list of similar commands and their arguments are displayed when you start typing any RedisGears or RedisBloom commands.\n  - Keep command results (up to 1MB) previously received in the Workbench, so they are available even after you restart the application. \n  - Connect Workbench to the database index selected when adding a database.\n  - To repeat any command in Workbench, just enter any integer and then a Redis command with arguments.\n- CLI:\n  - Added support for RedisGears and RedisBloom on the intelligent Redis command auto-complete, so hints with arguments are displayed when you enter any RedisGears or RedisBloom commands\n  - CLI is by default connected to the database index selected when adding a database. Added displaying of the database index connected.\n  - To repeat any command in CLI, just enter any integer and then a Redis command with arguments.\n- Command Helper:\n  - Added information about RedisGears and RedisBloom Redis commands.\n- Core:\n  - Fixed an issue with displaying parameter values in the Overview when no information is received for these parameters."},{"id":"203-december-2021","title":"2.0.3 (December 2021)","role":"content","text":"This is the maintenance release of RedisInsight Preview 2.0 (v2.0.3)."},{"id":"headlines-3","title":"Headlines","role":"content","text":"- Workbench:\n  - Added indications of commands\n  - New hints with the list of command arguments\n  - Reworked navigation for the built-in guides\n- Help Center:\n  - Added a page with list of supported keyboard shortcuts\n- Core:\n  - Uncoupled Command Helper from CLI\n  - Renamed `ZSET` to Sorted Set"},{"id":"details-3","title":"Details","role":"content","text":"- Browser:\n  - Changed the format of TTL in the list of keys\n- CLI:\n  - Fixed a bug with `FT.CREATE` command that rendered the window blank\n- Workbench:\n  - Fixed a bug to avoid executing the Redis command one more time when the view of results is changed\n  - Added a new information message when there are no results to display\n  - Added indications of commands (currently, not clickable) in Editor area to point out the lines where commands start\n  - Added new hints in Editor to display the list of command arguments with the following keyboard shortcuts:\n    - Ctrl+Shift+Space for Windows and Linux\n    - ⌘ ⇧ Space for Mac\n  - Added support for remembering the state (expanded or collapsed) for left side panel in Workbench\n  - Reworked navigation for the built-in guides\n  - Changed icons for default and custom plugins\n- Command Helper:\n  - Changed titles of command groups to make them consistent with redis.io\n- Help Center:\n  - Added a page with supported keyboard shortcuts\n- Core:\n  - Reworked logic to open CLI and Command Helper, added an option to open Command Helper without a need to open CLI\n  - Changed fonts and colors across the application to enhance readability\n  - Renamed `ZSET` to Sorted Set\n  - Added description of RedisGears and RedisBloom commands to hints in CLI, Command Helper, and Workbench\n  - Added support for automatic updates to the list of commands and their description in CLI, Command Helper, and Workbench"},{"id":"202-november-2021","title":"2.0.2 (November 2021)","role":"content","text":"This is the public preview release of RedisInsight 2.0 (v2.0.2).\n\nRedisInsight 2.0 is a complete product rewrite based on a new tech stack. This version contains a number of must-have and most-used capabilities from previous releases, plus a number of differentiators and delights.\n\nRedisInsight-preview 2.0 can be installed along with the current GA (1.11) version of RedisInsight with no issues."},{"id":"headlines-4","title":"Headlines","role":"content","text":"- Developed using a new tech stack based on Electron, Elastic UI, and Monaco Editor\n- Introducing Workbench - advanced command line interface with intelligent command auto-complete and complex data visualizations\n- Ability to write and render your own data visualizations within Workbench\n- Built-in click-through Redis guides available\n- Support for Light and Dark themes\n- Enhanced user experience with Browser"},{"id":"details-4","title":"Details","role":"content","text":"- Core:\n  - Enhanced user experience with the list of databases:\n    - View, sort and edit databases added\n    - Multiple deletion of databases\n  - Ability to connect to Redis Standalone, Redis Cluster and Redis Sentinel\n  - Auto discovery of databases managed by Redis Software, Redis Cloud (Flexible), and Redis Sentinel\n  - Support for Redis OSS Cluster API\n  - Support for TLS connection\n  - Works with Microsoft Azure (official support upcoming)\n- Workbench:\n  - Advanced command-line interface that lets you run commands against your Redis server \n  - Workbench editor allows comments, multi-line formatting and multi-command execution\n  - Intelligent Redis command auto-complete and syntax highlighting with support for [RediSearch](https://oss.redis.com/redisearch/), [RedisJSON](https://oss.redis.com/redisjson/), [RedisGraph](https://oss.redis.com/redisgraph/), [RedisTimeSeries](https://oss.redis.com/redistimeseries/), [RedisGears](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions), [RedisAI](https://oss.redis.com/redisai/), [RedisBloom](https://oss.redis.com/redisbloom/)\n  - Allows rendering custom data visualization per Redis command using externally developed plugins\n- Browser:\n  - Browse, filter and visualize key-value Redis data structures\n    - Visual cues per data type\n    - Quick view of size and ttl in the main browser view\n    - Ability to filter by pattern and/or data type\n    - Ability to change the number of keys to scan through during filtering\n  - CRUD support for Lists, Hashes, Strings, Sets, Sorted Sets\n    - Search within the data structure (except for Strings)\n  - CRUD support for [RedisJSON](https://oss.redis.com/redisjson/)\n- Database overview:\n  - A number of metrics always on display within the database workspace\n    - Metrics updated every 5 second\n    - CPU, number of keys, commands/sec, network input, network output, total memory, number of connected clients\n  - Enabled modules per Redis server listed\n- CLI:\n  - Command-line interface with enhanced type-ahead command help\n  - Embedded command helper where you can filter and search for Redis commands\n- RediSearch:\n  - Tabular visualizations within Workbench of [RediSearch](https://oss.redis.com/redisearch/) index queries and aggregations (support for FT.INFO, FT.SEARCH and FT.AGGREGATE)\n- Custom plugins:\n  - Ability to build your own visualization plugins to be rendered within Workbench\n  - [Documentation](https://github.com/redisinsight/redisinsight) on how to develop custom plugins and a reference example are provided\n- Built-in guides:\n  - Built-in click-through guides for Redis capabilities\n  - Added a guide on Document Capabilities within Redis\n- User interface (UI):\n  - Light/dark themes available\n  - Colour palette adjusted to the highest level of [Web content accessibility guidelines](https://www.w3.org/WAI/standards-guidelines/wcag/)\n- Data encryption:\n  - Optional ability to encrypt sensitive data such as connection certificates and passwords"}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v2.10.0","title":"RedisInsight v2.10.0, September 2022","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v2.10.0/","summary":"RedisInsight v2.10.0","aliases":["/develop/connect/insight/release-notes/v2.10.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"d1fb7b1b682e814402b38d4119430a897b167f42aece83fd47727a1a44aef47e","sections":[{"id":"2100-september-2022","title":"2.10.0 (September 2022)","role":"overview","text":"This is the General Availability (GA) release of RedisInsight 2.10."},{"id":"highlights","title":"Highlights","role":"content","text":"- Formatters: Additional support for values of keys with Protobuf, Binary, PHP unserialize (view and edit serialized PHP values as JSON), and Java serialized objects, save formatters selected when viewing other keys\n- New overview for cluster databases displays memory and key allocation as well as database information per shards\n- Configure Workbench to persist the Editor after commands have been run and group the results\n- Complete an optional user survey"},{"id":"details","title":"Details","role":"content","text":"**Features and improvements**\n- [#1159](https://github.com/RedisInsight/RedisInsight/pull/1159), [#1160](https://github.com/RedisInsight/RedisInsight/pull/1160), [#1068](https://github.com/RedisInsight/RedisInsight/pull/1068), [#1071](https://github.com/RedisInsight/RedisInsight/pull/1071), [#1095](https://github.com/RedisInsight/RedisInsight/pull/1095), [#1097](https://github.com/RedisInsight/RedisInsight/pull/1097), [#1098](https://github.com/RedisInsight/RedisInsight/pull/1098) A dedicated **Analysis Tools** page displays memory and key allocation in cluster databases as well as database information per shards\n- [#1017](https://github.com/RedisInsight/RedisInsight/pull/1017), [#1025](https://github.com/RedisInsight/RedisInsight/pull/1025), [#1029](https://github.com/RedisInsight/RedisInsight/pull/1029), [#1059](https://github.com/RedisInsight/RedisInsight/pull/1059), [#1092](https://github.com/RedisInsight/RedisInsight/pull/1092) Added support for additional data formats in Browser/Tree view, including Protobuf, Binary, Pickle, PHP unserialize (view and edit serialized PHP values as JSON), and Java serialized objects\n- [#1130](https://github.com/RedisInsight/RedisInsight/pull/1130) Save formatters selected when viewing other keys\n- [#1177](https://github.com/RedisInsight/RedisInsight/pull/1177) Add a validation when edited value is not valid in the selected format in Browser/Tree view\n- [#1048](https://github.com/RedisInsight/RedisInsight/pull/1048) Configure Workbench to persist the Editor after commands have been run\n- [#1119](https://github.com/RedisInsight/RedisInsight/pull/1119) Pipeline mode configuration for Workbench moved to Settings > Workbench\n- [#1149](https://github.com/RedisInsight/RedisInsight/pull/1149) Save Workbench space by grouping results \n- [#1162](https://github.com/RedisInsight/RedisInsight/pull/1162) Complete an optional user survey\n- [#1037](https://github.com/RedisInsight/RedisInsight/pull/1037) Added tooltip to display long fields in [Redis Streams](https://redis.io/docs/data-types/streams/) \n- [#1202](https://github.com/RedisInsight/RedisInsight/pull/1202) Removed format validations from the admin username in the Redis Software Cluster autodiscovery process\n\n**Bugs**\n- [#1180](https://github.com/RedisInsight/RedisInsight/pull/1180) Fix to display full values for truncated TTL in munutes\n- [#1197](https://github.com/RedisInsight/RedisInsight/pull/1197) Workbench is now available even when encryption failed\n- [#1176](https://github.com/RedisInsight/RedisInsight/pull/1176) Save the refresh value in Browser/Tree view\n- [#1101](https://github.com/RedisInsight/RedisInsight/pull/1101) Fixed an issue when key names are not displayed"}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v2.12.0","title":"RedisInsight v2.12.0, October 2022","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v2.12.0/","summary":"RedisInsight v2.12.0","aliases":["/develop/connect/insight/release-notes/v2.12.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"2e8e5a5513eb70c126eec514fa87fa3d6815c8c003e7a6fc9bdc64ade8558302","sections":[{"id":"2120-october-2022","title":"2.12.0 (October 2022)","role":"overview","text":"This is the General Availability (GA) release of RedisInsight 2.12."},{"id":"highlights","title":"Highlights","role":"content","text":"- Database Analysis: Get insights and optimize the usage and performance of your Redis or Redis Stack based on the overview of the memory and data type distribution, big or complicated keys, and namespaces used\n- Faster initial loading of the list of keys in Browser and Tree views\n- Performance optimizations for large results in Workbench"},{"id":"details","title":"Details","role":"content","text":"**Features and improvements**\n- [#1207](https://github.com/RedisInsight/RedisInsight/pull/1207), [#1222](https://github.com/RedisInsight/RedisInsight/pull/1222), [#1295](https://github.com/RedisInsight/RedisInsight/pull/1295), [#1159](https://github.com/RedisInsight/RedisInsight/pull/1159), [#1231](https://github.com/RedisInsight/RedisInsight/pull/1231), [#1155](https://github.com/RedisInsight/RedisInsight/pull/1155) Get insights and optimize the usage and performance of your Redis or Redis Stack with Database Analysis. Navigate to Analysis Tools and scan up to 10,000 keys in the database to see the summary of memory allocation and the number of keys per Redis data type, memory likely to be freed over time, top 15 key namespaces, and the biggest keys found. You can extrapolate results based on the total number of keys or see the exact results for the number of keys scanned. \n- [#1280](https://github.com/RedisInsight/RedisInsight/pull/1280) Speed up the initial load of the key list in Browser and Tree views\n- [#1285](https://github.com/RedisInsight/RedisInsight/pull/1285) Support for infinite floating point numbers in sorted sets\n- [#1290](https://github.com/RedisInsight/RedisInsight/pull/1290) Performance optimizations to process large results of Redis commands in Workbench\n\n**Bugs**\n- [#1293](https://github.com/RedisInsight/RedisInsight/pull/1293) Fixed Workbench visualizations in Redis Stack"}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v2.2.0","title":"RedisInsight v2.2.0, May 2022","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v2.2.0/","summary":"RedisInsight v2.2.0","aliases":["/develop/connect/insight/release-notes/v2.2.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"dcdae0fc3bb480639e9827a268ed5e38c2c0a9660801e35c09d80d3d234a4dd6","sections":[{"id":"220-may-2022","title":"2.2.0 (May 2022)","role":"overview","text":"This is the General Availability (GA) release of RedisInsight 2.2.0"},{"id":"headlines","title":"Headlines:","role":"content","text":"- SlowLog: New tool based on results of the [Slowlog](https://redis.io/commands/slowlog/) command to analyze slow operations in Redis instances\n- Streams: Added support for [Redis Streams](https://redis.io/docs/manual/data-types/streams/)\n- Open the list of keys or key details in full screen\n- Automatically refresh the list of keys and key values with a timer"},{"id":"details","title":"Details","role":"content","text":"**Features and improvements:**\n- [#621](https://github.com/RedisInsight/RedisInsight/pull/621) , [#645](https://github.com/RedisInsight/RedisInsight/pull/645) , [#649](https://github.com/RedisInsight/RedisInsight/pull/649) Added SlowLog, a tool that displays the list of logs captured by the [Slowlog](https://redis.io/commands/slowlog/) command to analyze all commands that exceed a specified runtime, which helps in troubleshooting performance issues. Specify both the runtime and the maximum length of SlowLog (which are server configurations) to configure the list of commands logged and set the auto-refresh interval to automatically update the list of commands displayed.\n- [#597](https://github.com/RedisInsight/RedisInsight/pull/597) , [#598](https://github.com/RedisInsight/RedisInsight/pull/598), [#601](https://github.com/RedisInsight/RedisInsight/pull/601) , [#603](https://github.com/RedisInsight/RedisInsight/pull/603) , [#608](https://github.com/RedisInsight/RedisInsight/pull/608) , [#613](https://github.com/RedisInsight/RedisInsight/pull/613) , [#614](https://github.com/RedisInsight/RedisInsight/pull/614) , [#632](https://github.com/RedisInsight/RedisInsight/pull/632) Support for [Redis Streams](https://redis.io/docs/manual/data-types/streams/), including creation and deletion of Streams, addition and deletion of entries, and filtration of entries per timestamp. [Consumer groups](https://redis.io/docs/manual/data-types/streams/#consumer-groups) will be added in a future release.\n- [#643](https://github.com/RedisInsight/RedisInsight/pull/643) List of keys or key details are supported in full screen mode. To open the key list in full screen, close the key details. To open key details in full screen, use the new **Full Screen** control in key details section.\n- [#633](https://github.com/RedisInsight/RedisInsight/pull/633) Automatically refresh the list of keys and key values with a timer. To do so, enable the **Auto Refresh** mode by clicking the control next to the **Refresh** button and set the refresh rate.\n- [#634](https://github.com/RedisInsight/RedisInsight/pull/634) Removed the max value limitation in the **Database Index** field of the form for adding a new database.\n\n**Bugs Fixed:**\n- [#656](https://github.com/RedisInsight/RedisInsight/pull/656) Binary key names will not trigger errors in databases with enabled OSS Cluster API. Data type, TTL, and size of such keys are displayed in the list of keys in all Redis instances. Key details are currently not available."}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v2.28.0","title":"RedisInsight v2.28.0, June 2023","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v2.28.0/","summary":"RedisInsight v2.28","aliases":["/develop/connect/insight/release-notes/v2.28.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"ba753743349e3f0e66c36ac26be0025f4d01c4526488c1b6a06d9ed09c09c715","sections":[{"id":"228-june-2023","title":"2.28 (June 2023)","role":"overview","text":"This is the General Availability (GA) release of RedisInsight 2.28."},{"id":"highlights","title":"Highlights","role":"content","text":"- Quickly and conveniently add [Redis Cloud](https://redis.com/redis-enterprise-cloud/overview/) databases that belong to fixed subscriptions using the auto-discovery tool\n- UX optimizations in Browser for an improved experience when performing [full-text search and queries](https://redis.io/docs/stack/search/), filtering, and bulk actions.\n- Support for a monospaced font in [JSON](https://redis.io/docs/stack/json/) key types"},{"id":"details","title":"Details","role":"content","text":"**Features and improvements**\n- [#2198](https://github.com/RedisInsight/RedisInsight/pull/2198), [#2207](https://github.com/RedisInsight/RedisInsight/pull/2207) Automatically discover and add [Redis Cloud](https://redis.com/redis-enterprise-cloud/overview/) databases that belong to fixed subscriptions using the `Redis Cloud` option on the form to auto-discover databases.\n- [#2146](https://github.com/RedisInsight/RedisInsight/pull/2146),[#2161](https://github.com/RedisInsight/RedisInsight/pull/2161) Added UX optimizations in the Browser layout to improve the experience when performing [full-text search and queries](https://redis.io/docs/stack/search/), filtering, and bulk actions.\n- [#2200](https://github.com/RedisInsight/RedisInsight/pull/2200) Changed to a monospaced font in [JSON](https://redis.io/docs/stack/json/) key types, and JSON formatters in Browser and Workbench.\n- [#2204](https://github.com/RedisInsight/RedisInsight/pull/2204) Re-bind default RedisInsight port from 5001 to 5530 to avoid conflicts with other applications\n- [#2120](https://github.com/RedisInsight/RedisInsight/pull/2120) Added the ability to investigate the commands processed by Redis or Redis Stack server without the need to stop Profiler when a new record appears.\n- [#2186](https://github.com/RedisInsight/RedisInsight/pull/2186) Unified the RedisInsight icon with other macOS applications.\n\n\n**Bugs**\n- [#2154](https://github.com/RedisInsight/RedisInsight/pull/2154) Display `(integer) 0` instead of `nil` in [ZRANK](https://redis.io/commands/zrank/) results in Workbench"}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/release-notes/v2.8.0","title":"RedisInsight v2.8.0, August 2022","url":"https://redis.io/docs/latest/develop/tools/insight/release-notes/v2.8.0/","summary":"RedisInsight v2.8.0","aliases":["/develop/connect/insight/release-notes/v2.8.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"b0551a3ffeab2ffb45a0ee4e2cecd6e7793c61f845d4b1b642e120021b0eefe1","sections":[{"id":"280-august-2022","title":"2.8.0 (August 2022)","role":"overview","text":"This is the General Availability (GA) release of RedisInsight 2.8.0"},{"id":"headlines","title":"Headlines","role":"content","text":"- Formatters: See formatted key values in JSON, MessagePack, Hex, Unicode, or ASCII in Browser/Tree view for an improved experience of working with different data formats. \n- Clone existing database connection or connection details: Clone the database connection you previously added to have the same one, but with a different database index, username, or password.\n- Raw mode in Workbench: Added support for the raw mode in Workbench results"},{"id":"details","title":"Details","role":"content","text":"**Features and improvements**\n- [#978](https://github.com/RedisInsight/RedisInsight/pull/978), [#959](https://github.com/RedisInsight/RedisInsight/pull/959), [#984](https://github.com/RedisInsight/RedisInsight/pull/984), [#996](https://github.com/RedisInsight/RedisInsight/pull/996), [#992](https://github.com/RedisInsight/RedisInsight/pull/992), [#1030](https://github.com/RedisInsight/RedisInsight/pull/1030) Added support for different data formats in Browser/Tree view, including JSON, MessagePack, Hex, and ASCII. If selected, formatter is applied to the entire key value and is available for any data type supported in Browser/Tree view. If any non-printable characters are detected, data editing is available only in Workbench and CLI to avoid data loss.\n- [#955](https://github.com/RedisInsight/RedisInsight/pull/965) Quickly clone a database connection to have the same one, but with a different database index, username, or password. Open a database connection in the edit mode, request to clone it, and make the changes needed. The original database connection remains the same.\n- [#1012](https://github.com/RedisInsight/RedisInsight/pull/1012) Added support for the raw mode (--raw) in Workbench. Enable it to see the results of commands executed in the raw mode.\n- [#987](https://github.com/RedisInsight/RedisInsight/pull/987) [DBSIZE](https://redis.io/commands/dbsize/) command is no longer required to connect to a database.\n- [#971](https://github.com/RedisInsight/RedisInsight/pull/971) Updated icon for the [RediSearch](https://redis.io/docs/stack/search/) Light module.\n- [#1011](https://github.com/RedisInsight/RedisInsight/pull/1011) Enhanced navigation in the Command Helper allowing you to return to previous actions.\n\n**Bugs fixed**\n- [#1009](https://github.com/RedisInsight/RedisInsight/pull/1009) Fixed an [error](https://github.com/RedisInsight/RedisInsight/issues/804) on automatic discovery of Sentinel databases.\n- [#978](https://github.com/RedisInsight/RedisInsight/pull/978) Work with [non-printable characters](https://github.com/RedisInsight/RedisInsight/issues/873). To avoid data loss, when non-printable characters are detected, key and value are editable in Workbench and CLI only."}],"examples":[]}
{"schema_version":2,"id":"develop/tools/insight/search-workspace","title":"Search workspace","url":"https://redis.io/docs/latest/develop/tools/insight/search-workspace/","summary":"Create and manage search indexes, build and save queries, and explore your data in the Redis Insight Search workspace.","aliases":["/develop/connect/insight/search-workspace"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-29T16:29:37+01:00","page_type":"content","content_hash":"6e333501ac41ecbccdb78f186d1d419e3902b05f5f001414f6f2c1683eca8ef2","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The **Search** workspace in Redis Insight is a dedicated space for working with [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query). From a single page you can browse the search indexes in your database, create new indexes from sample or existing data, build and run queries with a schema-aware editor, and save queries to a reusable library.\n\nTo open the workspace, select **Search** in the menu at the top of the screen.\n\n![The Search workspace showing the list of indexes](https://redis.io/docs/latest/images/ri/ri-search-indexes-list.png)"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"The Search workspace requires Redis Search. When you open the workspace, Redis Insight checks the connected database and shows a message instead of the workspace if it is not available:\n\n- **Redis Search is not available for this database** &mdash; the connected database does not include Redis Search. The engine is available in Redis Open Source, Redis Software, and Redis Cloud.\n- **Redis Search 2.0+ required** &mdash; the Search workspace requires Redis Search 2.0 or later (included with Redis 6 and later). Older query engine versions are not compatible with the commands used here."},{"id":"get-started","title":"Get started","role":"content","text":"The first time you open the Search workspace for a database that has no search indexes, Redis Insight shows a welcome screen that introduces full-text, vector, and hybrid search. From here you can get started in one of two ways:\n\n- **Try with sample data** &mdash; load a ready-made dataset and its index so you can explore search straight away. See [Create an index from sample data](#from-sample-data).\n- **Use data from my database** &mdash; build an index over keys that already exist in your database. See [Create an index from existing data](#from-existing-data).\n\n![The Search workspace welcome screen](https://redis.io/docs/latest/images/ri/ri-search-welcome.png)\n\nIf your database contains at least one search index, the Search workspace opens to the index list instead."},{"id":"search-indexes-list","title":"Search indexes list","role":"content","text":"The Search workspace lists the [search indexes](https://redis.io/docs/latest/develop/ai/search-and-query/indexing) defined in the connected database.\n\nThe list shows the following information for each index. Several columns include an info button that explains the column when selected.\n\n| Column | Description |\n|--------|-------------|\n| **Index name** | The name of the index. |\n| **Index prefix** | The key prefix that the index matches. Keys whose names start with this prefix are automatically indexed. |\n| **Index types** | The field types used in the index schema (for example, TEXT, TAG, NUMERIC, and VECTOR). |\n| **Docs** | The number of documents currently indexed. |\n| **Records** | The total number of indexed field-value pairs across all documents. (A document with five fields counts as five records.) |\n| **Terms** | The number of unique words extracted from `TEXT` fields for full-text search. |\n| **Fields** | The total number of fields defined in the index schema. |\n\nThe last column (unlabeled) holds the row actions:\n\n- A **query** button that opens the index in the [query editor](#query-an-index).\n- An ellipsis (**...**) menu with:\n  - **View index** &mdash; open the query page for the index with its details side panel showing.\n  - **Browse dataset** &mdash; switch to the Browse workspace to review the keys that the index covers.\n  - **Delete** &mdash; drop the index."},{"id":"create-an-index","title":"Create an index","role":"content","text":"Select **+ Create search index** to create a new index. You can create an index from sample data or from data that already exists in your database.\n\n![The Create search index menu](https://redis.io/docs/latest/images/ri/ri-search-create-menu.png)"},{"id":"from-sample-data","title":"From sample data","role":"content","text":"Choose **Use sample data** to load a ready-made dataset and the index that goes with it. This is the quickest way to explore how Redis Search works without preparing your own data.\n\nSelect one of the bundled datasets:\n\n- **E-commerce Discovery** &mdash; product data for exploring discovery use cases that match intent rather than just text.\n- **Content recommendations** &mdash; content data for discovering items by theme or plot.\n\n![The sample data selection modal](https://redis.io/docs/latest/images/ri/ri-search-sample-data.png)\n\nFrom here you can:\n\n- Select **See index definition** to review the schema that will be created before you commit to it.\n- Select **Start querying** to load the data, create the index, and open the query editor right away."},{"id":"from-existing-data","title":"From existing data","role":"content","text":"Choose **Use existing data** to build an index over keys that already exist in your database. Indexing is available for hash and JSON data structures.\n\nRedis Insight opens the create-index page with a key browser on the left. Select a key to get started: Redis Insight uses it to generate a suggested indexing schema. The index then covers **all keys that share the same prefix**, not just the key you selected.\n\nAfter you select a key, Redis Insight inspects it and automatically detects its fields, presenting the suggested schema on the right. You can edit the index name (shown as **Define search index: `<name>`**) using the **pencil** icon next to it, and edit the **Index prefix** to control which keys the index matches.\n\nThe schema is shown in a table with the following columns:\n\n- A **selection** checkbox &mdash; select or clear a field to include or exclude it from the index.\n- **Field name** &mdash; the detected field.\n- **Field sample value** &mdash; an example value taken from the key.\n- **Suggested indexing type** &mdash; the field type Redis Insight recommends.\n- A **pencil** icon &mdash; edit the field's type and options.\n\n![Creating an index from existing data with automatic field detection](https://redis.io/docs/latest/images/ri/ri-search-create-existing.png)\n\n> [!NOTE]\n> The first time you create an index from existing data, Redis Insight shows a short guided tour of the page. Select **Skip tour** to dismiss it, or **Next** to step through it.\n\n&nbsp;\n\n> [!NOTE]\n> Nested JSON objects and arrays cannot be indexed directly. If Redis Insight detects one, it removes the field and shows a warning that explains why.\n\n#### Configure field types\n\nTo add a field, select **+ Add field**; to change a detected field, select its **pencil** icon. In the field dialog, choose the index type and configure its options:\n\n- **Text** &mdash; full-text searchable fields, with options for weight and phonetic matching (English, French, Portuguese, or Spanish).\n- **Tag** &mdash; exact-match fields for filtering and faceting.\n- **Numeric** &mdash; numeric fields for range filtering.\n- **Geo** &mdash; geospatial fields for location filtering.\n- **Vector** &mdash; vector fields for similarity search, with options for the indexing algorithm (`FLAT` or `HNSW`), vector type (`FLOAT32`, `FLOAT64`, `FLOAT16`, or `BFLOAT16`), distance metric (`L2`, `IP`, or `COSINE`), the number of dimensions, and (for `HNSW`) algorithm-specific tuning parameters.\n\n![Configuring a field type](https://redis.io/docs/latest/images/ri/ri-search-field-type.png)\n\n#### Table view and command view\n\nThe create-index page provides two views of your index definition:\n\n- **Table view** &mdash; the visual schema editor described above.\n- **Command view** &mdash; the equivalent [`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create) command that Redis Insight will run.\n\n![The generated FT.CREATE command in command view](https://redis.io/docs/latest/images/ri/ri-search-command-view.png)\n\nWhen you are satisfied with the schema, select **Create index**. Redis Insight confirms when the index is created and your data becomes searchable."},{"id":"query-an-index","title":"Query an index","role":"content","text":"When you open an index, Redis Insight shows the query page. At the top of the page, an index selector (shown as **Indexes / `<index name>`**) lets you switch between the indexes in your database, and a **View index** button opens a side panel with the full index definition and statistics.\n\n![The index details side panel](https://redis.io/docs/latest/images/ri/ri-search-index-info.png)\n\nThe rest of the page is split into two resizable panes: the query editor on top and the results below. The editor has two tabs, **Query editor** and **Query library**.\n\n![The Search query editor with results](https://redis.io/docs/latest/images/ri/ri-search-run-query.png)"},{"id":"query-editor","title":"Query editor","role":"content","text":"The **Query editor** tab lets you write and run [Redis Query Engine commands](https://redis.io/docs/latest/develop/ai/search-and-query/query) (such as `FT.SEARCH` and `FT.AGGREGATE`) against the selected index, with syntax highlighting and schema-aware autocomplete. Start typing `FT.` to see available search commands, index names, and fields based on your current query.\n\nThe action bar at the bottom of the editor pane provides the following actions:\n\n- **Explain** &mdash; show the execution plan for the query (using [`FT.EXPLAIN`](https://redis.io/docs/latest/commands/ft.explain)) so you can understand how it will run. Available for `FT.SEARCH` and `FT.AGGREGATE` queries.\n- **Profile** &mdash; profile the query (using [`FT.PROFILE`](https://redis.io/docs/latest/commands/ft.profile)) to see where time is spent and identify bottlenecks. Available for `FT.SEARCH` and `FT.AGGREGATE` queries.\n- **Save** &mdash; save the current query to the Query library for reuse.\n- **Run** &mdash; run the query and view the results in the lower pane.\n\nFor example, the following query returns the first 10 documents priced under 2000:\n\n[code example]\n\nSelect **Explain** to see how the query runs:\n\n![The execution plan returned by Explain](https://redis.io/docs/latest/images/ri/ri-search-explain-query.png)\n\nSelect **Profile** to see where time is spent:\n\n![The profile returned by Profile](https://redis.io/docs/latest/images/ri/ri-search-profile-query.png)"},{"id":"results","title":"Results","role":"returns","text":"Each command you run appears in the results pane below the editor. You can switch between a **Text** and a **Table** view of the results, and re-run, profile, or explain a command directly from its result using the actions on the right.\n\n![Switching between Text and Table views of the results](https://redis.io/docs/latest/images/ri/ri-search-results-view-button.png)\n\n![Per-result Profile and Explain actions](https://redis.io/docs/latest/images/ri/ri-search-results-actions-button.png)"},{"id":"query-library","title":"Query library","role":"content","text":"The **Query library** tab keeps saved and sample queries together so you can reuse them quickly. Use the search field at the top to filter the list. From the library you can load a query into the editor, run it directly, or delete it.\n\nTo save your own query, write it in the **Query editor** tab, select **Save**, and give it a name. It then appears in the library alongside the built-in sample queries.\n\n![The Query library](https://redis.io/docs/latest/images/ri/ri-search-query-library.png)"},{"id":"navigate-between-the-browse-and-search-workspaces","title":"Navigate between the Browse and Search workspaces","role":"content","text":"The [Browse](https://redis.io/docs/latest/develop/tools/insight#browser) and Search workspaces are connected so you can move between your raw data and your indexes."},{"id":"make-data-searchable-from-the-browse-workspace","title":"Make data searchable from the Browse workspace","role":"content","text":"When you view a hash or JSON key in the Browse workspace that is not yet indexed, select **Make searchable** to create an index for it. A **Make this data searchable** dialog explains that all keys sharing the key's prefix will be included. Select **Continue** to go to the Search workspace, where you can review and adjust the schema before creating the index, as described in [Create an index from existing data](#from-existing-data).\n\n![The Make searchable button in the Browse workspace](https://redis.io/docs/latest/images/ri/ri-search-make-searchable.png)"},{"id":"view-the-index-for-a-key","title":"View the index for a key","role":"content","text":"When you view a key that is already indexed, select **View index** to jump to that index in the Search workspace. If the key belongs to more than one index, choose the index you want from the menu.\n\n![The View index button in the Browse workspace](https://redis.io/docs/latest/images/ri/ri-search-view-index.png)"}],"examples":[{"id":"query-editor-ex0","language":"plaintext","code":"FT.SEARCH idx:bikes_vss \"@price:[0 2000]\" LIMIT 0 10","section_id":"query-editor"}]}
{"schema_version":2,"id":"develop/tools/redis-for-vscode","title":"Redis for VS Code","url":"https://redis.io/docs/latest/develop/tools/redis-for-vscode/","summary":"Connect to Redis from Visual Studio Code.","aliases":["/develop/connect/redis-for-vscode"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-29T16:29:37+01:00","children":[{"id":"develop/tools/redis-for-vscode/release-notes","summary":"","title":"Redis for VS Code release notes","url":"https://redis.io/docs/latest/develop/tools/redis-for-vscode/release-notes/"}],"page_type":"content","content_hash":"cfa24f08cfc3668172b1795d68b17be298c098ea95234521193e79d07fcc1963","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis for VS Code is an extension that allows you to connect to your Redis databases from within Microsoft Visual Studio Code.\nAfter connecting to a database, you can view, add, modify, and delete keys, and interact with your Redis databases using a Redis Insight like UI and also a built-in CLI interface.\nThe following data types are supported:\n\n- [Hash](https://redis.io/docs/latest/develop/data-types/hashes)\n- [List](https://redis.io/docs/latest/develop/data-types/lists)\n- [Set](https://redis.io/docs/latest/develop/data-types/sets)\n- [Sorted Set](https://redis.io/docs/latest/develop/data-types/sorted-sets)\n- [String](https://redis.io/docs/latest/develop/data-types/strings)\n- [JSON](https://redis.io/docs/latest/develop/data-types/json)"},{"id":"install-the-redis-for-vs-code-extension","title":"Install the Redis for VS Code extension","role":"setup","text":"Open VS Code and click on the **Extensions** menu button. In the **Search Extensions in Marketplace** field, type \"Redis for VS Code\" and press the `enter` or `return` key. There may be more than one option shown, so be sure to click on the extension published by Redis. The correct extension is shown below. Click on the **Install** to install the extension.\n\n![](https://redis.io/docs/latest/images/dev/connect/vscode/vscode-install1.png)\n\nOnce installed, check the **Auto Update** button to allow VS Code to install future revisions of the extension automatically.\n\n![](https://redis.io/docs/latest/images/dev/connect/vscode/vscode-install2.png)\n\nAfter installing the extension, your VS Code menu will look similar to the following.\n\n![](https://redis.io/docs/latest/images/dev/connect/vscode/vscode-menu.png)"},{"id":"connect-db","title":"Connect to Redis databases","role":"content","text":"Click on the Redis mark (the cursive **R**) in the VS Code menu to begin connecting a Redis database to VS Code. If you do not currently have access to a Redis database, consider giving Redis Cloud a try. [It's free](https://redis.io/try-free/).\n\n![](https://redis.io/docs/latest/images/dev/connect/vscode/vscode-initial.png)\n\nClick on the **+ Connect database** button. A dialog will display in the main pane. In the image shown below, all the options have been checked to show the available details for each connection. These connection details are similar to those accessible from [`redis-cli`](https://redis.io/docs/latest/develop/tools/cli).\n\n> [!NOTE]\n> In the first release of Redis for VS Code, there is no way to change the logical database after you have selected it. If you need to connect to a different logical database, you need to add a separate database connection.\n\n![](https://redis.io/docs/latest/images/dev/connect/vscode/vscode-add-menu.png)\n\nAfter filling out the necessary fields, click on the **Add Redis database** button. The pane on the left side, where you would normally see the Explorer view, shows your database connections.\n\n![](https://redis.io/docs/latest/images/dev/connect/vscode/vscode-cnx-view.png)\n\n> [!NOTE]\n> Local databases, excluding OSS cluster databases, with default usernames and no passwords will automatically be added to your list of database connections."},{"id":"connection-tools","title":"Connection tools","role":"content","text":"Several tools are displayed for each open connection.\n\n![](https://redis.io/docs/latest/images/dev/connect/vscode/vscode-cnx-tools.png)\n\nLeft to right, they are:\n\n- Refresh connection, which retrieves fresh data from the connected Redis database.\n- Edit connection, which shows a dialog similar to the one described in [Connect to Redis Databases](#connect-db) above.\n- Delete connection.\n- Open CLI. See [CLI tool](#cli) below for more information.\n- Sort keys, either ascending or descending.\n- Filter keys by key name or pattern, and by key type.\n- Add a new key by type: Hash, List, Set, Sorted Set, String, or JSON."},{"id":"key-view","title":"Key view","role":"content","text":"Here's what you'll see when there are no keys in your database (the image on the left) and when keys are present (the image on the right).\n\n![](https://redis.io/docs/latest/images/dev/connect/vscode/vscode-key-view-w-wo-keys.png)\n\nRedis for VS Code will automatically group the keys based on the one available setting, **Delimiter to separate namespaces**, which you can view by clicking on the gear icon in the top-right of the left side pane. Click on the current value to change it. The default setting is the colon (`:`) character.\n\n![](https://redis.io/docs/latest/images/dev/connect/vscode/vscode-settings.png)\n\nClick on a key to display its contents.\n\n![](https://redis.io/docs/latest/images/dev/connect/vscode/vscode-key-view.png)"},{"id":"key-editing-tools","title":"Key editing tools","role":"content","text":"There are several editing tools that you can use to edit key data. Each data type has its own editing capabilities. The following examples show edits to JSON data. Note that changes to keys are immediately written to the server.\n\n- **Rename**. Click on the key name field to change the name.\n\n![](https://redis.io/docs/latest/images/dev/connect/vscode/vscode-edit-name.png)\n\n- **Set time-to-live (TTL)**. Click on the **TTL** field to set the duration in seconds.\n\n![](https://redis.io/docs/latest/images/dev/connect/vscode/vscode-edit-ttl.png)\n\n- **Delete**. Click on the trash can icons to delete the entire key (highlighted in red) or portions of a key (highlighted in yellow).\n\n![](https://redis.io/docs/latest/images/dev/connect/vscode/vscode-edit-del.png)\n\n- **Add to key**. Click on the `+` button next to the closing bracket (shown highlighted in green above) to add a new component to a key. \n\n![](https://redis.io/docs/latest/images/dev/connect/vscode/vscode-edit-add.png)\n\n- **Refresh**. Click on the refresh icon (the circular arrow) to retrieve fresh data from the server. In the examples below, refresh was clicked (the image on the left) and the key now has a new field called \"test\" that was added by another Redis client (the image on the right).\n\n![](https://redis.io/docs/latest/images/dev/connect/vscode/vscode-recycle-before-after.png)\n\nFor strings, hashes, lists, sets, and sorted sets, the extension supports numerous value formatters (highlighted in red in the image below). They are:\n\n- Unicode\n- ASCII\n- Binary (blob)\n- HEX\n- JSON\n- Msgpack\n- Pickle\n- Protobuf\n- PHP serialized\n- Java serialized\n- 32-bit vector\n- 64-bit vector\n\n![](https://redis.io/docs/latest/images/dev/connect/vscode/vscode-edit-value-formatters.png)\n\nAlso for Hash keys, you can set per-field TTLs (highlighted in yellow in the image above), a new feature added to Redis Open Source 7.4."},{"id":"cli","title":"CLI tool","role":"content","text":"The connection tool with the boxed `>_` icon opens a Redis CLI window in the **REDIS CLI** tab at the bottom of the primary pane.\n\n![](https://redis.io/docs/latest/images/dev/connect/vscode/vscode-cli.png)\n\nThe CLI interface works just like the [`redis-cli`](https://redis.io/docs/latest/develop/tools/cli) command."}],"examples":[]}
{"schema_version":2,"id":"develop/tools/redis-for-vscode/release-notes","title":"Redis for VS Code release notes","url":"https://redis.io/docs/latest/develop/tools/redis-for-vscode/release-notes/","summary":"","aliases":["/develop/connect/redis-for-vscode/release-notes/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-08-10T10:40:34+01:00","children":[{"id":"develop/tools/redis-for-vscode/release-notes/v1.4.0","summary":"Redis for VS Code v1.4","title":"Redis for VS Code v1.4.0, May 2025","url":"https://redis.io/docs/latest/develop/tools/redis-for-vscode/release-notes/v1.4.0/"},{"id":"develop/tools/redis-for-vscode/release-notes/v1.2.0","summary":"Redis for VS Code v1.2","title":"Redis for VS Code v1.2.0, December 2024","url":"https://redis.io/docs/latest/develop/tools/redis-for-vscode/release-notes/v1.2.0/"},{"id":"develop/tools/redis-for-vscode/release-notes/v1.0.0","summary":"Redis for VS Code v1.0","title":"Redis for VS Code v1.0.0, September 2024","url":"https://redis.io/docs/latest/develop/tools/redis-for-vscode/release-notes/v1.0.0/"}],"page_type":"content","content_hash":"72046387720510684e81caea306f4bb0baa1047a38572fa760910d33ab800c6a","sections":[{"id":"content","title":"Content","role":"content","text":"Here are the most recent changes for Redis for VS Code:\n\n\n| Version (Date) | Release notes |\n| --- | --- |\n| v1.4.0 (May 2025) | [Redis for VS Code v1.4.0, May 2025](https://redis.io/docs/latest/develop/tools/redis-for-vscode/release-notes/v1.4.0/) |\n| v1.2.0 (December 2024) | [Redis for VS Code v1.2.0, December 2024](https://redis.io/docs/latest/develop/tools/redis-for-vscode/release-notes/v1.2.0/) |\n| v1.0.0 (September 2024) | [Redis for VS Code v1.0.0, September 2024](https://redis.io/docs/latest/develop/tools/redis-for-vscode/release-notes/v1.0.0/) |"}],"examples":[]}
{"schema_version":2,"id":"develop/tools/redis-for-vscode/release-notes/v1.0.0","title":"Redis for VS Code v1.0.0, September 2024","url":"https://redis.io/docs/latest/develop/tools/redis-for-vscode/release-notes/v1.0.0/","summary":"Redis for VS Code v1.0","aliases":["/develop/connect/redis-for-vscode/release-notes/v1.0.0/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"ed088322670fe084afbec46d81d67236caa3f90e9fb822b2b3bc996201a864e9","sections":[{"id":"100-september-2024","title":"1.0.0 (September 2024)","role":"overview","text":"This is the first release of Redis for VS Code.\n\nRedis for VS Code is the official Visual Studio Code extension that provides an intuitive and efficient GUI for Redis databases, developed by Redis."},{"id":"headlines","title":"Headlines","role":"content","text":"* Universal Redis Support: Connect to any Redis instance, including Redis Open Source, Redis Cloud, Redis Software, and Redis on Azure Cache.\n\n* Advanced Connectivity: Supports TLS certificates and SSH tunnels, with an option for automatic data decompression for GZIP, SNAPPY, Brotli, and more.\n\n* Data types: Supports strings, hashes, lists, sets, sorted sets, and JSON.\n\n* Human-readable data representation: Offers formatters like ASCII, JSON, Binary, Hex, 32-bit, and 64-bit vectors, and others.\n\n* Integrated Redis CLI: Leverage Redis CLI with syntax preview as you type commands."},{"id":"details","title":"Details","role":"content","text":"- Database connections:\n\n  - Connect to any Redis instance, including Redis Open Source, Redis Cloud, Redis Software, and Redis on Azure Cache.\n\n  - View, edit, and manage your Redis database connections.\n\n  - Supports TLS connections and SSH tunnels for secure access.\n\n  - Automatically handle data compressed with GZIP, LZ4, SNAPPY, ZSTD, Brotli, or PHP GZCompress.\n\n  - Choose and work with a specific logical database within your Redis instance.\n\n- Redis data structures:\n\n  - Use an intuitive tree view interface to browse, filter, and visualize Redis key-value data structures.\n\n  -  Perform create, read, update, and delete operations on the following Redis data types:\n\n    - Strings\n\n    - Hashes\n\n    - Lists\n\n    - Sets\n\n    - Sorted sets\n\n    - JSON\n\n- View your data in multiple human-readable formats, including Unicode, ASCII, Binary, HEX, JSON, Msgpack, Pickle, Protobuf, PHP serialized, Java serialized, and Vector (32 and 64-bit).\n\n - Sort by key names and apply filters by pattern or data type for quick and precise data access.\n\n - Conduct detailed searches within fields in hashes, indexes in lists, and members in sets and sorted sets.\n\n- Redis CLI:\n\n  - Access a built-in Redis CLI with improved type-ahead command suggestions, helping you execute commands accurately and efficiently."},{"id":"get-started-with-redis-for-vs-code","title":"Get started with Redis for VS Code","role":"content","text":"This repository contains the source code for the Redis for VS Code extension.\n\nInstall the extension from the [Visual Studio Code Marketplace](https://marketplace.visualstudio.com/items?itemName=redis.redis-for-vscode) to use it."}],"examples":[]}
{"schema_version":2,"id":"develop/tools/redis-for-vscode/release-notes/v1.2.0","title":"Redis for VS Code v1.2.0, December 2024","url":"https://redis.io/docs/latest/develop/tools/redis-for-vscode/release-notes/v1.2.0/","summary":"Redis for VS Code v1.2","tags":[],"last_updated":"2024-12-20T18:32:42+01:00","page_type":"content","content_hash":"8873f600d7cff6f3ac0d3ac2177f79ced33062a101a033f9b8e3a3d5dc9dc071","sections":[{"id":"120-december-2024","title":"1.2.0 (December 2024)","role":"overview","text":"This is the General Availability (GA) release of Redis for VS Code 1.2."},{"id":"headlines","title":"Headlines","role":"content","text":"* Work with keys across multiple database indexes, which are automatically discovered and displayed in the database list.\n* Support for adding multiple elements to the head or tail of Redis lists, for both new and existing keys.\n* Auto-refresh the list of keys and key values with a customizable timer.\n* Delete and update previously added CA and client certificates to keep them updated."},{"id":"details","title":"Details","role":"content","text":"- [#223](https://github.com/RedisInsight/Redis-for-VS-Code/pull/223) Work with keys across multiple database indexes. Database indexes with keys are automatically discovered and displayed in the database list. \n- [#207](https://github.com/RedisInsight/Redis-for-VS-Code/pull/207) Support for adding multiple elements to the head or tail of Redis lists for new and existing key.\n- [#226](https://github.com/RedisInsight/Redis-for-VS-Code/pull/226) Auto-refresh the list of keys and key values with a customizable timer. To do so, enable the Auto-refresh mode by clicking the control next to the Refresh button and setting the refresh rate.\n- [#224](https://github.com/RedisInsight/Redis-for-VS-Code/pull/224) Ability to delete previously added CA and Client certificates to keep them up-to-date.\n- [#224](https://github.com/RedisInsight/Redis-for-VS-Code/pull/224) Enhanced both the Java and PHP serialized formatters: the Java formatter now supports date and time data, while the PHP formatter includes UTF-8 encoding for better handling of special characters and multi-language data.\n- [#224](https://github.com/RedisInsight/Redis-for-VS-Code/pull/224) Keep databases and the list of keys [expanded](https://github.com/RedisInsight/Redis-for-VS-Code/issues/217) after navigating away.\n- [#226](https://github.com/RedisInsight/Redis-for-VS-Code/pull/226) New users can optionally encrypt sensitive data, such as connection certificates and passwords. Existing users can enable encryption by deleting the ~/.redis-for-vscode/redisinsight.db file and re-adding their database connections.\n\n**Bugs**\n- [#224](https://github.com/RedisInsight/Redis-for-VS-Code/pull/224) Resolved an issue where large integers in JSON keys were being rounded, ensuring data integrity.\n- [#224](https://github.com/RedisInsight/Redis-for-VS-Code/pull/224) Saved SNI and SSH connection information for newly added database connections.\n- [#224](https://github.com/RedisInsight/Redis-for-VS-Code/pull/224) Fixed an issue to display multiple hash fields when expanding a hash value."},{"id":"get-started-with-redis-for-vs-code","title":"Get started with Redis for VS Code","role":"content","text":"Install the extension from the [Visual Studio Code Marketplace](https://marketplace.visualstudio.com/items?itemName=redis.redis-for-vscode) to use it."}],"examples":[]}
{"schema_version":2,"id":"develop/tools/redis-for-vscode/release-notes/v1.4.0","title":"Redis for VS Code v1.4.0, May 2025","url":"https://redis.io/docs/latest/develop/tools/redis-for-vscode/release-notes/v1.4.0/","summary":"Redis for VS Code v1.4","tags":[],"last_updated":"2025-07-30T17:32:17+02:00","page_type":"content","content_hash":"0aee4e89a00eec1b8a6936291d9fbe9a1c225bfcdac666c0e548fb3114f8502c","sections":[{"id":"140-may-2025","title":"1.4.0 (May 2025)","role":"overview","text":"This is the General Availability (GA) release of Redis for VS Code 1.4."},{"id":"headlines","title":"Headlines","role":"content","text":"- Easily provision a free Redis Cloud database directly from within Redis for VS Code, great for prototyping AI workloads like semantic search, chat memory, and embedding-based retrieval, among many other Redis use cases, all without leaving your dev environment.\n- Ability to encrypt connection certificates, passwords, and other sensitive data for all users.\n- Support for multiple key name delimiters, making it easier to browse databases with varied key structures."},{"id":"details","title":"Details","role":"content","text":"- [#252](https://github.com/RedisInsight/Redis-for-VS-Code/pull/252), [#257](https://github.com/RedisInsight/Redis-for-VS-Code/pull/257) Easily provision a free [Redis Cloud](https://redis.io/cloud/) database directly from within Redis for VS Code, great for prototyping AI workloads like semantic search, chat memory, and embedding-based retrieval, among many other Redis use cases, all without leaving your dev environment.\n- [#255](https://github.com/RedisInsight/Redis-for-VS-Code/pull/255) Added the ability to encrypt connection certificates, passwords, and other sensitive data for all users.\n- [#247](https://github.com/RedisInsight/Redis-for-VS-Code/pull/247) Support for multiple key name delimiters, making it easier to browse databases with varied key structures."},{"id":"get-started-with-redis-for-vs-code","title":"Get started with Redis for VS Code","role":"content","text":"Install the extension from the [Visual Studio Code Marketplace](https://marketplace.visualstudio.com/items?itemName=redis.redis-for-vscode) to use it."}],"examples":[]}
{"schema_version":2,"id":"develop/use-cases/cache-aside/dotnet","title":"Redis cache-aside with StackExchange.Redis","url":"https://redis.io/docs/latest/develop/use-cases/cache-aside/dotnet/","summary":"Implement a Redis cache-aside layer in C# with StackExchange.Redis","aliases":["/develop/use-cases/cache-aside/stackexchange.redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:50:30+01:00","children":[],"page_type":"content","content_hash":"2c60ad0cd53b048406624474c81d0707e91527ca7f131c4dc4caecca443eeaf1","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis cache-aside layer in C# with [StackExchange.Redis](https://seredis.dev/). It includes a small local web server built on ASP.NET Core's minimal API so you can see cache hits, misses, invalidation on write, and stampede protection in action."},{"id":"overview","title":"Overview","role":"overview","text":"Cache-aside is one of the most common Redis use cases for read-heavy applications. Instead of querying the primary database on every request, the application checks Redis first and only falls back to the primary on a miss. The result is written back to Redis with a TTL so the next read is served from memory.\n\nThat gives you:\n\n* Sub-millisecond reads for the hot working set\n* Bounded staleness — every entry expires within a known window\n* Reduced primary database load proportional to hit rate\n* Field-level updates without re-serializing the full record\n* Protection against cache stampedes when popular keys expire under load\n\nIn this example, each cached product is stored as a Redis hash under a key like `cache:product:{id}`. The hash holds the product fields (`id`, `name`, `price_cents`, `stock`) and the key has a TTL so stale data is bounded automatically."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow on every read looks like this:\n\n1. The application calls `cache.Get(productId, primary.Read)`\n2. The helper runs `HGETALL` against `cache:product:{id}`\n3. On a hit, the cached hash is returned directly\n4. On a miss, the helper acquires a Lua-backed single-flight lock and invokes the loader to fetch from the primary\n5. The helper writes the result back to Redis with `HSET` plus `EXPIRE` and releases the lock\n6. Concurrent callers that fail to acquire the lock wait briefly for the cache to populate, then return that value instead of issuing their own primary read\n\nOn a write, the application updates the primary and then deletes the cache key, so the next read repopulates from the new source value."},{"id":"the-cache-aside-helper","title":"The cache-aside helper","role":"content","text":"The `RedisCache` class wraps the cache-aside operations\n([source](RedisCache.cs)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each cached product is stored in a Redis hash:\n\n[code example]\n\nThe implementation uses:\n\n* [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) to read the cached record\n* [`HSET`](https://redis.io/docs/latest/commands/hset) plus [`EXPIRE`](https://redis.io/docs/latest/commands/expire) to repopulate after a miss\n* [`DEL`](https://redis.io/docs/latest/commands/del) to invalidate on writes\n* [`TTL`](https://redis.io/docs/latest/commands/ttl) to surface remaining staleness in the demo UI\n* [`EVAL`](https://redis.io/docs/latest/commands/eval) for the Lua single-flight lock that prevents stampedes\n* StackExchange.Redis transactions with `Condition.KeyExists` for the conditional field update path"},{"id":"cache-aside-reads","title":"Cache-aside reads","role":"content","text":"The `Get()` method runs `HGETALL` on the cache key first. On a hit it returns the cached hash and increments the hit counter. On a miss, it delegates to a single-flight loader:\n\n[code example]\n\nThe returned record includes the measured Redis round-trip time so the demo UI can show the latency difference between a hit and a miss."},{"id":"stampede-protection-with-a-lua-lock","title":"Stampede protection with a Lua lock","role":"content","text":"When a popular key expires, every concurrent reader observes the miss at the same instant. Without coordination, all of them would query the primary and overwrite the cache redundantly — a *cache stampede*.\n\nThe helper uses a tiny Lua script to acquire a short-lived lock atomically. Only the caller that wins the `SET NX` becomes the primary loader; the rest poll the cache briefly and return the value the lock holder writes:\n\n[code example]\n\nA second script releases the lock only if the caller still owns it, so a lock that timed out and was re-acquired by someone else cannot be released by mistake:\n\n[code example]\n\nThe C# side runs both scripts via `IDatabase.ScriptEvaluate` on every miss:\n\n[code example]\n\nThe unique `token` per caller is what makes the release script safe — only the caller that actually holds the lock can release it."},{"id":"invalidation-on-write","title":"Invalidation on write","role":"content","text":"When a write hits the primary, the application invalidates the cache key. The next read pulls fresh data from the primary:\n\n[code example]\n\nThis is the simplest and safest pattern: never try to keep the cache and primary in sync directly, just delete the cache entry and let the next read repopulate it."},{"id":"field-level-updates","title":"Field-level updates","role":"content","text":"Because each record is stored as a hash, the cache helper can also update a single field in place without re-serializing the full record. The update only writes if the entry is already cached, so a partial record can never appear in Redis:\n\n[code example]\n\nStackExchange.Redis transactions wrap WATCH/MULTI/EXEC into a clean conditional API: the transaction commits atomically only if the cache key still exists, which prevents creating a partial record after an invalidation has raced in.\n\nThis is useful for hot fields that change more often than the rest of the record (a stock counter, a view count) and would otherwise force a full reload."},{"id":"hitmiss-accounting","title":"Hit/miss accounting","role":"content","text":"The helper keeps in-process counters for hits, misses, and stampedes that were suppressed by the single-flight lock. The demo UI surfaces these so you can see the cache absorbing load:\n\n[code example]\n\nIn production you would emit these as `IMetricsRoot` counters or push them into your observability stack rather than holding them as `long`s in process memory."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before running the demo, make sure that:\n\n* Redis is running and accessible. By default, the demo connects to `localhost:6379`.\n* .NET 8 SDK is installed.\n* The `StackExchange.Redis` NuGet package is referenced (already declared in the project file).\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`, or set `REDIS_HOST` / `REDIS_PORT` in the environment."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":"A local demo server is included to show the cache-aside layer in action\n([source](Program.cs)):\n\n[code example]\n\nThe demo server uses ASP.NET Core's minimal API for HTTP handling and `Task.Run` for the stampede test:\n\n* [Minimal APIs](https://learn.microsoft.com/aspnet/core/fundamentals/minimal-apis) for the web server\n* [`System.Threading.Tasks`](https://learn.microsoft.com/dotnet/api/system.threading.tasks) for the stampede test\n* [`System.Security.Cryptography.RandomNumberGenerator`](https://learn.microsoft.com/dotnet/api/system.security.cryptography.randomnumbergenerator) for the per-caller lock token\n\nIt exposes a small interactive page where you can:\n\n* Read a product through the cache and see whether it was a hit or a miss\n* Compare the measured Redis round-trip against the simulated primary read latency\n* Watch the cache TTL count down between requests\n* Update a field on the primary and see the cache invalidate automatically\n* Run a stampede test that fires many concurrent reads at a freshly-invalidated key and confirms only one of them reaches the primary\n* Reset the hit/miss counters at any time\n\nAfter starting the server, visit `http://localhost:8080`."},{"id":"threadpool-sizing-for-the-stampede-test","title":"ThreadPool sizing for the stampede test","role":"content","text":"`Program.cs` calls `ThreadPool.SetMinThreads(64, 64)` at startup. The demo's stampede test queues up to 50 `Task.Run` work items at once, and the default ThreadPool grows at roughly two threads per second under load — that growth ramp is slow enough that some of the queued readers can time out polling for the cache and fall back to a second primary read.\n\nRaising the ThreadPool floor is a property of *this synchronous demo* rather than the cache pattern itself. A production cache-aside helper would more naturally be `async` (`await Task.Delay`, `HashGetAllAsync`, `ScriptEvaluateAsync`) and would not block ThreadPool threads while polling."},{"id":"the-mock-primary-store","title":"The mock primary store","role":"content","text":"To make the demo self-contained, the example includes a `MockPrimaryStore` that stands in for a slow disk-backed database\n([source](MockPrimaryStore.cs)):\n\n[code example]\n\nEvery call to `Read()` sleeps for `ReadLatencyMs` so the difference between a cache hit and a miss is obvious in the UI. The store also tracks the total number of primary reads, which the stampede test uses to confirm that single-flight is working — for N concurrent readers against a cold key, you should see exactly one primary read.\n\nIn a real application this would be replaced by an Entity Framework query, an HTTP call to a downstream service, or any other slow-but-authoritative source."},{"id":"production-usage","title":"Production usage","role":"content","text":"This guide uses a deliberately small local demo so you can focus on the cache-aside pattern. In production, you will usually want to harden several aspects of it."},{"id":"choose-a-ttl-that-matches-your-staleness-tolerance","title":"Choose a TTL that matches your staleness tolerance","role":"content","text":"The TTL is the upper bound on how long a stale value can be served. Shorter TTLs mean lower hit rates and more primary load; longer TTLs mean higher hit rates and more stale reads between writes. Pick the value that matches your business tolerance for stale data, and combine it with explicit invalidation on writes for the cases where you cannot tolerate any staleness."},{"id":"invalidate-dont-try-to-keep-the-cache-in-sync","title":"Invalidate, don't try to keep the cache in sync","role":"content","text":"When the underlying record changes, delete the cache key rather than rewriting it. Cache-aside is robust precisely because it never assumes the cache holds the latest value — the next read always re-fetches from the primary on a miss."},{"id":"use-the-async-api-on-the-request-path","title":"Use the async API on the request path","role":"content","text":"The demo uses synchronous calls (`HashGetAll`, `ScriptEvaluate`, `Thread.Sleep`) to keep the helper compact. In production, prefer `HashGetAllAsync`, `ScriptEvaluateAsync`, and `await Task.Delay` so request-handling threads return to the ThreadPool while the loader is in flight. The cache-aside structure is identical — just propagate `await`s through the call chain."},{"id":"handle-missing-records-explicitly","title":"Handle missing records explicitly","role":"content","text":"In this demo, a missing record returns `null` and nothing is cached. In a real system you may want to cache \"not found\" sentinels with a short TTL to absorb load from probing for non-existent IDs, while making sure the sentinel TTL is shorter than the positive cache entry so a newly-created record becomes visible quickly."},{"id":"tune-the-single-flight-lock-ttl","title":"Tune the single-flight lock TTL","role":"content","text":"The lock TTL needs to be longer than the worst-case primary read latency so a slow loader does not lose the lock midway. The unique token in `RELEASE_LOCK_SCRIPT` ensures the original caller does not delete someone else's lock if its lock has expired."},{"id":"namespace-cache-keys-in-shared-redis-deployments","title":"Namespace cache keys in shared Redis deployments","role":"content","text":"If multiple applications share a Redis deployment, prefix cache keys with the application name (`cache:billing:product:{id}`) so different services cannot clobber each other's entries."},{"id":"inspect-cached-entries-directly-in-redis","title":"Inspect cached entries directly in Redis","role":"content","text":"When testing or troubleshooting, inspect the stored cache key directly to confirm the application is writing the fields and TTL you expect:\n\n[code example]"},{"id":"learn-more","title":"Learn more","role":"related","text":"* [StackExchange.Redis docs](https://seredis.dev/) - Install and use the .NET Redis client\n* [SET command](https://redis.io/docs/latest/commands/set) - Set a string with TTL options (`EX`, `PX`, `NX`)\n* [HSET command](https://redis.io/docs/latest/commands/hset) - Write hash fields\n* [HGETALL command](https://redis.io/docs/latest/commands/hgetall) - Read every field of a hash\n* [EXPIRE command](https://redis.io/docs/latest/commands/expire) - Set key expiration in seconds\n* [DEL command](https://redis.io/docs/latest/commands/del) - Delete a key on invalidation\n* [Lua scripting](https://redis.io/docs/latest/develop/programmability/eval-intro) - Atomic single-flight locks and stampede mitigation"}],"examples":[{"id":"the-cache-aside-helper-ex0","language":"csharp","code":"using StackExchange.Redis;\nusing CacheAsideDemo;\n\nvar redis = ConnectionMultiplexer.Connect(\"localhost:6379\");\nvar primary = new MockPrimaryStore(readLatencyMs: 150);\nvar cache = new RedisCache(redis.GetDatabase(), ttl: 30);\n\n// Read through the cache.\nvar result = cache.Get(\"p-001\", primary.Read);\nConsole.WriteLine($\"hit={result.Hit} latency={result.RedisLatencyMs:F2}ms\");\n\n// Update a single field without rewriting the whole record.\ncache.UpdateField(\"p-001\", \"stock\", \"41\");\n\n// Invalidate the cache key on a write to the primary.\nprimary.UpdateField(\"p-001\", \"price_cents\", \"699\");\ncache.Invalidate(\"p-001\");","section_id":"the-cache-aside-helper"},{"id":"data-model-ex0","language":"text","code":"cache:product:p-001\n  id          = p-001\n  name        = Sourdough Loaf\n  price_cents = 650\n  stock       = 42","section_id":"data-model"},{"id":"cache-aside-reads-ex0","language":"csharp","code":"public Result Get(string entityId, Func<string, Dictionary<string, string>?> loader)\n{\n    var cacheKey = CacheKey(entityId);\n    var sw = System.Diagnostics.Stopwatch.StartNew();\n    var entries = _db.HashGetAll(cacheKey);\n    sw.Stop();\n    var redisLatencyMs = sw.Elapsed.TotalMilliseconds;\n\n    if (entries.Length > 0)\n    {\n        Interlocked.Increment(ref _hits);\n        return new Result(ToDict(entries), Hit: true, redisLatencyMs);\n    }\n\n    Interlocked.Increment(ref _misses);\n    var record = LoadWithSingleFlight(entityId, loader);\n    return new Result(record, Hit: false, redisLatencyMs);\n}","section_id":"cache-aside-reads"},{"id":"stampede-protection-with-a-lua-lock-ex0","language":"lua","code":"-- Acquire a short-lived lock with SET NX PX. Returns 1 on acquire, 0 otherwise.\nif redis.call('SET', KEYS[1], ARGV[1], 'NX', 'PX', ARGV[2]) then\n    return 1\nend\nreturn 0","section_id":"stampede-protection-with-a-lua-lock"},{"id":"stampede-protection-with-a-lua-lock-ex1","language":"lua","code":"if redis.call('GET', KEYS[1]) == ARGV[1] then\n    return redis.call('DEL', KEYS[1])\nend\nreturn 0","section_id":"stampede-protection-with-a-lua-lock"},{"id":"stampede-protection-with-a-lua-lock-ex2","language":"csharp","code":"private Dictionary<string, string>? LoadWithSingleFlight(\n    string entityId,\n    Func<string, Dictionary<string, string>?> loader)\n{\n    var cacheKey = CacheKey(entityId);\n    var lockKey = LockKey(entityId);\n    var token = RandomToken();\n\n    var acquired = (long) _db.ScriptEvaluate(\n        AcquireLockScript,\n        new RedisKey[] { lockKey },\n        new RedisValue[] { token, _lockTtlMs.ToString() });\n\n    if (acquired == 1)\n    {\n        try\n        {\n            var record = loader(entityId);\n            if (record is null) return null;\n            var batch = _db.CreateBatch();\n            _ = batch.KeyDeleteAsync(cacheKey);\n            _ = batch.HashSetAsync(cacheKey,\n                record.Select(p => new HashEntry(p.Key, p.Value)).ToArray());\n            _ = batch.KeyExpireAsync(cacheKey, TimeSpan.FromSeconds(_ttl));\n            batch.Execute();\n            return record;\n        }\n        finally\n        {\n            _db.ScriptEvaluate(ReleaseLockScript,\n                new RedisKey[] { lockKey },\n                new RedisValue[] { token });\n        }\n    }\n\n    Interlocked.Increment(ref _stampedesSuppressed);\n    var deadline = Environment.TickCount + _lockTtlMs;\n    while (Environment.TickCount < deadline)\n    {\n        Thread.Sleep(_waitPollMs);\n        var entries = _db.HashGetAll(cacheKey);\n        if (entries.Length > 0) return ToDict(entries);\n    }\n    return loader(entityId);\n}","section_id":"stampede-protection-with-a-lua-lock"},{"id":"invalidation-on-write-ex0","language":"csharp","code":"public bool Invalidate(string entityId)\n{\n    return _db.KeyDelete(CacheKey(entityId));\n}","section_id":"invalidation-on-write"},{"id":"field-level-updates-ex0","language":"csharp","code":"public bool UpdateField(string entityId, string field, string value)\n{\n    var cacheKey = CacheKey(entityId);\n    while (true)\n    {\n        var tx = _db.CreateTransaction();\n        tx.AddCondition(Condition.KeyExists(cacheKey));\n        _ = tx.HashSetAsync(cacheKey, field, value);\n        _ = tx.KeyExpireAsync(cacheKey, TimeSpan.FromSeconds(_ttl));\n        if (tx.Execute()) return true;\n        if (!_db.KeyExists(cacheKey)) return false;\n    }\n}","section_id":"field-level-updates"},{"id":"hitmiss-accounting-ex0","language":"csharp","code":"public Dictionary<string, object> Stats()\n{\n    var hits = Interlocked.Read(ref _hits);\n    var misses = Interlocked.Read(ref _misses);\n    var stampedes = Interlocked.Read(ref _stampedesSuppressed);\n    var total = hits + misses;\n    var hitRatePct = total == 0 ? 0.0 : Math.Round(100.0 * hits / total, 1);\n    return new Dictionary<string, object>\n    {\n        [\"hits\"] = hits,\n        [\"misses\"] = misses,\n        [\"stampedes_suppressed\"] = stampedes,\n        [\"hit_rate_pct\"] = hitRatePct,\n    };\n}","section_id":"hitmiss-accounting"},{"id":"running-the-demo-ex0","language":"bash","code":"dotnet run --project CacheAsideDemo.csproj -- --port 8080 --redis-host localhost --redis-port 6379","section_id":"running-the-demo"},{"id":"the-mock-primary-store-ex0","language":"csharp","code":"public Dictionary<string, string>? Read(string id)\n{\n    Thread.Sleep(ReadLatencyMs);\n    Interlocked.Increment(ref _reads);\n    // ...\n}","section_id":"the-mock-primary-store"},{"id":"inspect-cached-entries-directly-in-redis-ex0","language":"bash","code":"redis-cli HGETALL cache:product:p-001\nredis-cli TTL cache:product:p-001","section_id":"inspect-cached-entries-directly-in-redis"}]}
{"schema_version":2,"id":"develop/use-cases/cache-aside/go","title":"Redis cache-aside with Go","url":"https://redis.io/docs/latest/develop/use-cases/cache-aside/go/","summary":"Implement a Redis cache-aside layer in Go with go-redis","aliases":["/develop/use-cases/cache-aside/go-redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:50:30+01:00","children":[],"page_type":"content","content_hash":"8dc5c916d3d3f1bd15aa4a1504f560152bf2d13288b429af38d5545d3e912733","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis cache-aside layer in Go with the [`go-redis`](https://redis.io/docs/latest/develop/clients/go) client library. It includes a small local web server built with Go's standard `net/http` package so you can see cache hits, misses, invalidation on write, and stampede protection in action."},{"id":"overview","title":"Overview","role":"overview","text":"Cache-aside is one of the most common Redis use cases for read-heavy applications. Instead of querying the primary database on every request, the application checks Redis first and only falls back to the primary on a miss. The result is written back to Redis with a TTL so the next read is served from memory.\n\nThat gives you:\n\n* Sub-millisecond reads for the hot working set\n* Bounded staleness — every entry expires within a known window\n* Reduced primary database load proportional to hit rate\n* Field-level updates without re-serializing the full record\n* Protection against cache stampedes when popular keys expire under load\n\nIn this example, each cached product is stored as a Redis hash under a key like `cache:product:{id}`. The hash holds the product fields (`id`, `name`, `price_cents`, `stock`) and the key has a TTL so stale data is bounded automatically."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow on every read looks like this:\n\n1. The application calls `cache.Get(ctx, productID, loader)`\n2. The helper runs `HGETALL` against `cache:product:{id}`\n3. On a hit, the cached hash is returned directly\n4. On a miss, the helper acquires a Lua-backed single-flight lock and calls `loader(ctx, productID)` to read the primary\n5. The helper writes the result back to Redis with `HSET` plus `EXPIRE` and releases the lock\n6. Concurrent goroutines that fail to acquire the lock wait briefly for the cache to populate, then return that value instead of issuing their own primary read\n\nOn a write, the application updates the primary and then deletes the cache key, so the next read repopulates from the new source value."},{"id":"the-cache-aside-helper","title":"The cache-aside helper","role":"content","text":"The `RedisCache` type wraps the cache-aside operations\n([source](cache.go)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each cached product is stored in a Redis hash:\n\n[code example]\n\nThe implementation uses:\n\n* [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) to read the cached record\n* [`HSET`](https://redis.io/docs/latest/commands/hset) plus [`EXPIRE`](https://redis.io/docs/latest/commands/expire) to repopulate after a miss\n* [`DEL`](https://redis.io/docs/latest/commands/del) to invalidate on writes\n* [`TTL`](https://redis.io/docs/latest/commands/ttl) to surface remaining staleness in the demo UI\n* [`EVAL`](https://redis.io/docs/latest/commands/eval) for the Lua single-flight lock that prevents stampedes\n* [`WATCH`](https://redis.io/docs/latest/commands/watch)/[`MULTI`](https://redis.io/docs/latest/commands/multi)/[`EXEC`](https://redis.io/docs/latest/commands/exec) for the conditional field update path"},{"id":"cache-aside-reads","title":"Cache-aside reads","role":"content","text":"The `Get()` method runs `HGETALL` on the cache key first. On a hit it returns the cached hash and records the hit in an in-process counter. On a miss, it delegates to a single-flight loader:\n\n[code example]\n\nThe returned `Result` includes the measured Redis round-trip time so the demo UI can show the latency difference between a hit and a miss."},{"id":"stampede-protection-with-a-lua-lock","title":"Stampede protection with a Lua lock","role":"content","text":"When a popular key expires, every concurrent reader observes the miss at the same instant. Without coordination, all of them would query the primary and overwrite the cache redundantly — a *cache stampede*.\n\nThe helper uses a tiny Lua script to acquire a short-lived lock atomically. Only the goroutine that wins the `SET NX` becomes the primary loader; the rest poll the cache briefly and return the value the lock holder writes:\n\n[code example]\n\nA second script releases the lock only if the caller still owns it, so a lock that timed out and was re-acquired by someone else cannot be released by mistake:\n\n[code example]\n\nThe Go side wraps both scripts in `redis.NewScript` and runs them on every miss:\n\n[code example]\n\nThe unique `token` per caller is what makes the release script safe — only the goroutine that actually holds the lock can release it."},{"id":"invalidation-on-write","title":"Invalidation on write","role":"content","text":"When a write hits the primary, the application invalidates the cache key. The next read pulls fresh data from the primary:\n\n[code example]\n\nThis is the simplest and safest pattern: never try to keep the cache and primary in sync directly, just delete the cache entry and let the next read repopulate it."},{"id":"field-level-updates","title":"Field-level updates","role":"content","text":"Because each record is stored as a hash, the cache helper can also update a single field in place without re-serializing the full record. The update only writes if the entry is already cached, so a partial record can never appear in Redis:\n\n[code example]\n\nThis is useful for hot fields that change more often than the rest of the record (a stock counter, a view count) and would otherwise force a full reload."},{"id":"hitmiss-accounting","title":"Hit/miss accounting","role":"content","text":"The helper keeps in-process counters for hits, misses, and stampedes that were suppressed by the single-flight lock. The demo UI surfaces these so you can see the cache absorbing load:\n\n[code example]\n\nIn production you would emit these as Prometheus counters or push them into your metrics pipeline rather than holding them in process memory."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before running the demo, make sure that:\n\n* Redis is running and accessible. By default, the demo connects to `localhost:6379`.\n* Go 1.23 or later is installed.\n* The `go-redis` module is available (see the `go.mod` file in this directory).\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":"A local demo server is included to show the cache-aside layer in action\n([source](demo_server.go)).\n\nCreate a `main.go` file in the same directory:\n\n[code example]\n\nThen run:\n\n[code example]\n\nThe demo server uses only Go's standard library for HTTP handling and concurrency:\n\n* [`net/http`](https://pkg.go.dev/net/http) for the web server\n* [`sync`](https://pkg.go.dev/sync) for the stampede test goroutine pool\n* [`crypto/rand`](https://pkg.go.dev/crypto/rand) for the per-caller lock token\n\nIt exposes a small interactive page where you can:\n\n* Read a product through the cache and see whether it was a hit or a miss\n* Compare the measured Redis round-trip against the simulated primary read latency\n* Watch the cache TTL count down between requests\n* Update a field on the primary and see the cache invalidate automatically\n* Run a stampede test that fires many concurrent reads at a freshly-invalidated key and confirms only one of them reaches the primary\n* Reset the hit/miss counters at any time\n\nAfter starting the server, visit `http://localhost:8080`."},{"id":"the-mock-primary-store","title":"The mock primary store","role":"content","text":"To make the demo self-contained, the example includes a `MockPrimaryStore` that stands in for a slow disk-backed database\n([source](primary.go)):\n\n[code example]\n\nEvery call to `Read()` sleeps for `ReadLatency` so the difference between a cache hit and a miss is obvious in the UI. The store also tracks the total number of primary reads, which the stampede test uses to confirm that single-flight is working — for N concurrent readers against a cold key, you should see exactly one primary read.\n\nIn a real application this would be replaced by a SQL query, an HTTP call to a downstream service, or any other slow-but-authoritative source."},{"id":"production-usage","title":"Production usage","role":"content","text":"This guide uses a deliberately small local demo so you can focus on the cache-aside pattern. In production, you will usually want to harden several aspects of it."},{"id":"choose-a-ttl-that-matches-your-staleness-tolerance","title":"Choose a TTL that matches your staleness tolerance","role":"content","text":"The TTL is the upper bound on how long a stale value can be served. Shorter TTLs mean lower hit rates and more primary load; longer TTLs mean higher hit rates and more stale reads between writes. Pick the value that matches your business tolerance for stale data, and combine it with explicit invalidation on writes for the cases where you cannot tolerate any staleness."},{"id":"invalidate-dont-try-to-keep-the-cache-in-sync","title":"Invalidate, don't try to keep the cache in sync","role":"content","text":"When the underlying record changes, delete the cache key rather than rewriting it. Cache-aside is robust precisely because it never assumes the cache holds the latest value — the next read always re-fetches from the primary on a miss."},{"id":"handle-missing-records-explicitly","title":"Handle missing records explicitly","role":"content","text":"In this demo, a missing record returns `nil` and nothing is cached. In a real system you may want to cache \"not found\" sentinels with a short TTL to absorb load from probing for non-existent IDs, while making sure the sentinel TTL is shorter than the positive cache entry so a newly-created record becomes visible quickly."},{"id":"tune-the-single-flight-lock-ttl","title":"Tune the single-flight lock TTL","role":"content","text":"The lock TTL needs to be longer than the worst-case primary read latency so a slow loader does not lose the lock midway. The unique token in `RELEASE_LOCK_SCRIPT` ensures the original caller does not delete someone else's lock if its lock has expired."},{"id":"namespace-cache-keys-in-shared-redis-deployments","title":"Namespace cache keys in shared Redis deployments","role":"content","text":"If multiple applications share a Redis deployment, prefix cache keys with the application name (`cache:billing:product:{id}`) so different services cannot clobber each other's entries."},{"id":"inspect-cached-entries-directly-in-redis","title":"Inspect cached entries directly in Redis","role":"content","text":"When testing or troubleshooting, inspect the stored cache key directly to confirm the application is writing the fields and TTL you expect:\n\n[code example]"},{"id":"learn-more","title":"Learn more","role":"related","text":"* [go-redis guide](https://redis.io/docs/latest/develop/clients/go) - Install and use the Go Redis client\n* [SET command](https://redis.io/docs/latest/commands/set) - Set a string with TTL options (`EX`, `PX`, `NX`)\n* [HSET command](https://redis.io/docs/latest/commands/hset) - Write hash fields\n* [HGETALL command](https://redis.io/docs/latest/commands/hgetall) - Read every field of a hash\n* [EXPIRE command](https://redis.io/docs/latest/commands/expire) - Set key expiration in seconds\n* [DEL command](https://redis.io/docs/latest/commands/del) - Delete a key on invalidation\n* [Lua scripting](https://redis.io/docs/latest/develop/programmability/eval-intro) - Atomic single-flight locks and stampede mitigation"}],"examples":[{"id":"the-cache-aside-helper-ex0","language":"go","code":"package main\n\nimport (\n    \"context\"\n    \"log\"\n    \"time\"\n\n    \"github.com/redis/go-redis/v9\"\n\n    \"cacheaside\"\n)\n\nfunc main() {\n    client := redis.NewClient(&redis.Options{Addr: \"localhost:6379\"})\n    primary := cacheaside.NewMockPrimaryStore(150 * time.Millisecond)\n    cache, err := cacheaside.New(cacheaside.Config{\n        Client: client,\n        TTL:    30 * time.Second,\n    })\n    if err != nil {\n        log.Fatal(err)\n    }\n    ctx := context.Background()\n\n    // Read through the cache.\n    res, _ := cache.Get(ctx, \"p-001\", primary.Read)\n    log.Printf(\"hit=%v latency=%.2fms record=%v\", res.Hit, res.RedisLatencyMs, res.Record)\n\n    // Update a single field without rewriting the whole record.\n    _, _ = cache.UpdateField(ctx, \"p-001\", \"stock\", \"41\")\n\n    // Invalidate the cache key on a write to the primary.\n    primary.UpdateField(\"p-001\", \"price_cents\", \"699\")\n    _, _ = cache.Invalidate(ctx, \"p-001\")\n}","section_id":"the-cache-aside-helper"},{"id":"data-model-ex0","language":"text","code":"cache:product:p-001\n  id          = p-001\n  name        = Sourdough Loaf\n  price_cents = 650\n  stock       = 42","section_id":"data-model"},{"id":"cache-aside-reads-ex0","language":"go","code":"func (c *RedisCache) Get(ctx context.Context, id string, loader Loader) (Result, error) {\n    cacheKey := c.cacheKey(id)\n\n    start := time.Now()\n    cached, err := c.client.HGetAll(ctx, cacheKey).Result()\n    redisMs := float64(time.Since(start).Microseconds()) / 1000.0\n    if err != nil {\n        return Result{RedisLatencyMs: redisMs}, err\n    }\n\n    if len(cached) > 0 {\n        c.recordHit()\n        return Result{Record: cached, Hit: true, RedisLatencyMs: redisMs}, nil\n    }\n\n    c.recordMiss()\n    record, err := c.loadWithSingleFlight(ctx, id, loader)\n    return Result{Record: record, Hit: false, RedisLatencyMs: redisMs}, err\n}","section_id":"cache-aside-reads"},{"id":"stampede-protection-with-a-lua-lock-ex0","language":"lua","code":"-- Acquire a short-lived lock with SET NX PX. Returns 1 on acquire, 0 otherwise.\nif redis.call('SET', KEYS[1], ARGV[1], 'NX', 'PX', ARGV[2]) then\n    return 1\nend\nreturn 0","section_id":"stampede-protection-with-a-lua-lock"},{"id":"stampede-protection-with-a-lua-lock-ex1","language":"lua","code":"if redis.call('GET', KEYS[1]) == ARGV[1] then\n    return redis.call('DEL', KEYS[1])\nend\nreturn 0","section_id":"stampede-protection-with-a-lua-lock"},{"id":"stampede-protection-with-a-lua-lock-ex2","language":"go","code":"func (c *RedisCache) loadWithSingleFlight(ctx context.Context, id string, loader Loader) (map[string]string, error) {\n    cacheKey := c.cacheKey(id)\n    lockKey := c.lockKey(id)\n    tokenBytes := make([]byte, 8)\n    if _, err := rand.Read(tokenBytes); err != nil {\n        return nil, err\n    }\n    token := hex.EncodeToString(tokenBytes)\n\n    acquired, err := c.acquireScript.Run(ctx, c.client,\n        []string{lockKey},\n        token,\n        c.lockTTL.Milliseconds(),\n    ).Int()\n    if err != nil {\n        return nil, err\n    }\n\n    if acquired == 1 {\n        defer c.releaseScript.Run(ctx, c.client, []string{lockKey}, token)\n        record, err := loader(ctx, id)\n        if err != nil || record == nil {\n            return record, err\n        }\n        pipe := c.client.TxPipeline()\n        pipe.Del(ctx, cacheKey)\n        // ...HSet fields... and pipe.Expire(ctx, cacheKey, c.ttl)\n        _, err = pipe.Exec(ctx)\n        return record, err\n    }\n\n    c.recordStampedeSuppressed()\n    deadline := time.Now().Add(c.lockTTL)\n    for time.Now().Before(deadline) {\n        select {\n        case <-ctx.Done():\n            return nil, ctx.Err()\n        case <-time.After(c.waitPoll):\n        }\n        if cached, err := c.client.HGetAll(ctx, cacheKey).Result(); err == nil && len(cached) > 0 {\n            return cached, nil\n        }\n    }\n    return loader(ctx, id)\n}","section_id":"stampede-protection-with-a-lua-lock"},{"id":"invalidation-on-write-ex0","language":"go","code":"func (c *RedisCache) Invalidate(ctx context.Context, id string) (bool, error) {\n    n, err := c.client.Del(ctx, c.cacheKey(id)).Result()\n    return n == 1, err\n}","section_id":"invalidation-on-write"},{"id":"field-level-updates-ex0","language":"go","code":"func (c *RedisCache) UpdateField(ctx context.Context, id, field, value string) (bool, error) {\n    cacheKey := c.cacheKey(id)\n    for {\n        var ok bool\n        err := c.client.Watch(ctx, func(tx *redis.Tx) error {\n            exists, err := tx.Exists(ctx, cacheKey).Result()\n            if err != nil {\n                return err\n            }\n            if exists == 0 {\n                ok = false\n                return nil\n            }\n            _, err = tx.TxPipelined(ctx, func(pipe redis.Pipeliner) error {\n                pipe.HSet(ctx, cacheKey, field, value)\n                pipe.Expire(ctx, cacheKey, c.ttl)\n                return nil\n            })\n            ok = err == nil\n            return err\n        }, cacheKey)\n        if errors.Is(err, redis.TxFailedErr) {\n            continue\n        }\n        return ok, err\n    }\n}","section_id":"field-level-updates"},{"id":"hitmiss-accounting-ex0","language":"go","code":"func (c *RedisCache) Stats() map[string]any {\n    c.mu.Lock()\n    defer c.mu.Unlock()\n    total := c.hits + c.misses\n    hitRate := 0.0\n    if total > 0 {\n        hitRate = float64(int(1000.0*float64(c.hits)/float64(total))) / 10.0\n    }\n    return map[string]any{\n        \"hits\":                 c.hits,\n        \"misses\":               c.misses,\n        \"stampedes_suppressed\": c.stampedesSuppressed,\n        \"hit_rate_pct\":         hitRate,\n    }\n}","section_id":"hitmiss-accounting"},{"id":"running-the-demo-ex0","language":"go","code":"package main\n\nimport \"cacheaside\"\n\nfunc main() { cacheaside.RunDemoServer() }","section_id":"running-the-demo"},{"id":"running-the-demo-ex1","language":"bash","code":"go build -o demo ./...\n./demo --port 8080 --redis-host localhost --redis-port 6379","section_id":"running-the-demo"},{"id":"the-mock-primary-store-ex0","language":"go","code":"func (m *MockPrimaryStore) Read(ctx context.Context, id string) (map[string]string, error) {\n    select {\n    case <-ctx.Done():\n        return nil, ctx.Err()\n    case <-time.After(m.ReadLatency):\n    }\n    // ...\n}","section_id":"the-mock-primary-store"},{"id":"inspect-cached-entries-directly-in-redis-ex0","language":"bash","code":"redis-cli HGETALL cache:product:p-001\nredis-cli TTL cache:product:p-001","section_id":"inspect-cached-entries-directly-in-redis"}]}
{"schema_version":2,"id":"develop/use-cases/cache-aside","title":"Redis cache-aside","url":"https://redis.io/docs/latest/develop/use-cases/cache-aside/","summary":"Cache database reads in Redis with TTL-bounded staleness.","tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:50:30+01:00","children":[{"id":"develop/use-cases/cache-aside/redis-py","summary":"Implement a Redis cache-aside layer in Python with redis-py","title":"Redis cache-aside with redis-py","url":"https://redis.io/docs/latest/develop/use-cases/cache-aside/redis-py/"},{"id":"develop/use-cases/cache-aside/nodejs","summary":"Implement a Redis cache-aside layer in Node.js with node-redis","title":"Redis cache-aside with node-redis","url":"https://redis.io/docs/latest/develop/use-cases/cache-aside/nodejs/"},{"id":"develop/use-cases/cache-aside/go","summary":"Implement a Redis cache-aside layer in Go with go-redis","title":"Redis cache-aside with Go","url":"https://redis.io/docs/latest/develop/use-cases/cache-aside/go/"},{"id":"develop/use-cases/cache-aside/java-jedis","summary":"Implement a Redis cache-aside layer in Java with Jedis","title":"Redis cache-aside with Jedis","url":"https://redis.io/docs/latest/develop/use-cases/cache-aside/java-jedis/"},{"id":"develop/use-cases/cache-aside/java-lettuce","summary":"Implement a Redis cache-aside layer in Java with Lettuce","title":"Redis cache-aside with Lettuce","url":"https://redis.io/docs/latest/develop/use-cases/cache-aside/java-lettuce/"},{"id":"develop/use-cases/cache-aside/dotnet","summary":"Implement a Redis cache-aside layer in C# with StackExchange.Redis","title":"Redis cache-aside with StackExchange.Redis","url":"https://redis.io/docs/latest/develop/use-cases/cache-aside/dotnet/"},{"id":"develop/use-cases/cache-aside/php","summary":"Implement a Redis cache-aside layer in PHP with Predis","title":"Redis cache-aside with Predis","url":"https://redis.io/docs/latest/develop/use-cases/cache-aside/php/"},{"id":"develop/use-cases/cache-aside/ruby","summary":"Implement a Redis cache-aside layer in Ruby with redis-rb","title":"Redis cache-aside with redis-rb","url":"https://redis.io/docs/latest/develop/use-cases/cache-aside/ruby/"},{"id":"develop/use-cases/cache-aside/rust","summary":"Implement a Redis cache-aside layer in Rust with redis-rs","title":"Redis cache-aside with redis-rs","url":"https://redis.io/docs/latest/develop/use-cases/cache-aside/rust/"}],"page_type":"content","content_hash":"73246e2f01032f6f1fc9ad287e79db6ae19d1728f6432a5da854bf51f9f44a54","sections":[{"id":"when-to-use-redis-cache-aside","title":"When to use Redis cache-aside","role":"overview","text":"Use Redis cache-aside when you need to serve repeated reads at sub-millisecond latency without overloading your primary database, caching only the data that is actually requested."},{"id":"why-the-problem-is-hard","title":"Why the problem is hard","role":"content","text":"Read-heavy workloads (product catalogs, user profiles, API responses) hit the same records thousands of times between updates. Primary databases optimized for durability pay the full cost of a disk-based lookup on every request; as traffic grows, P95 latency degrades, connection pools exhaust, and brief spikes cascade into broader service degradation.\n\nAn in-process cache solves this for a single instance but breaks across multiple stateless services: each instance warms independently, duplicates memory, and cannot be invalidated consistently. When a popular cached key expires under high concurrency, dozens of processes simultaneously query the database for the same record — a cache stampede that amplifies the exact load spike caching was supposed to prevent.\n\nThis use case is distinct from write-through or write-behind caching, where the cache mirrors the full dataset on every write. It is also distinct from semantic caching, which matches LLM responses by embedding similarity rather than exact key lookup."},{"id":"what-you-can-expect-from-a-redis-solution","title":"What you can expect from a Redis solution","role":"content","text":"You can:\n\n-   Keep P95 read latency under 5 ms for cached entities at peak traffic.\n-   Reduce primary database load proportionally to hit rate without provisioning read replicas.\n-   Cache only actively requested data, keeping memory bounded to the working set.\n-   Invalidate on write so stale data windows stay within a configured bound.\n-   Survive popular-key expiration under load without stampeding the database.\n-   Update individual fields in a cached entity without deserializing and rewriting the full object."},{"id":"how-redis-supports-the-solution","title":"How Redis supports the solution","role":"content","text":"In practice, each cached entity is stored under a key like `cache:{entity}:{id}`, with a TTL that bounds how long stale data can be served. The application reads from Redis on every request, falls back to the primary on a miss, and writes the result back to Redis. On updates, the application writes the primary and invalidates the cache key.\n\nRedis provides the following features that make it a good fit for cache-aside:\n\n-   [`GET`](https://redis.io/docs/latest/commands/get) and\n    [`SET`](https://redis.io/docs/latest/commands/set) with per-key\n    [`EX`/`PX`](https://redis.io/docs/latest/commands/set) TTL so every entry has a bounded\n    staleness window, and [`DEL`](https://redis.io/docs/latest/commands/del) on write for explicit\n    invalidation — keeping stale data confined to a known, configurable bound.\n-   [Hashes](https://redis.io/docs/latest/develop/data-types/hashes)\n    ([`HSET`](https://redis.io/docs/latest/commands/hset),\n    [`HGET`](https://redis.io/docs/latest/commands/hget)) and native\n    [JSON](https://redis.io/docs/latest/develop/data-types/json) path access\n    ([`JSON.SET`](https://redis.io/docs/latest/commands/json.set),\n    [`JSON.GET`](https://redis.io/docs/latest/commands/json.get)) for structured and\n    partial-field caching, avoiding full-object re-serialization on every read or update — a real\n    gap in string-only stores like Memcached.\n-   [Lua scripting](https://redis.io/docs/latest/develop/programmability/eval-intro) with\n    [`EVAL`](https://redis.io/docs/latest/commands/eval) for atomic stampede mitigation: mutex locks\n    or probabilistic early refresh execute in a single atomic step with no external locking.\n-   [`TTL`](https://redis.io/docs/latest/commands/ttl) for monitoring remaining staleness on any cached\n    key without recomputing.\n-   Sub-millisecond reads from memory, so the cache check adds negligible overhead on the\n    request path. If Redis is already in the stack for sessions, rate limiting, or queues,\n    cache-aside runs on the same instance at zero marginal infrastructure cost."},{"id":"ecosystem","title":"Ecosystem","role":"content","text":"The following libraries and frameworks provide Redis-backed cache-aside integrations:\n\n-   **Java**:\n    [Spring Data Redis (`@Cacheable`/`@CacheEvict`)](https://docs.spring.io/spring-data/redis/reference/redis/redis-cache.html),\n    [Redis Smart Cache](https://github.com/redis-field-engineering/redis-smart-cache)\n    (transparent JDBC-layer cache-aside)\n-   **Python**: [Flask-Caching](https://flask-caching.readthedocs.io/)\n    (Redis backend),\n    [Django cache framework](https://docs.djangoproject.com/en/stable/topics/cache/)\n    (with [redis-py](https://redis.readthedocs.io/))\n-   **Node.js**: [node-redis](https://github.com/redis/node-redis),\n    [ioredis](https://github.com/redis/ioredis) (wrap route handlers with a cache-aside helper)\n-   **API gateways**:\n    [Kong Redis response caching](https://docs.konghq.com/hub/kong-inc/proxy-cache-advanced/)\n    (configurable TTL per route)"},{"id":"code-examples-to-build-your-own-redis-cache-aside","title":"Code examples to build your own Redis cache-aside","role":"example","text":"The following guides show how to build a simple Redis-backed cache-aside layer in front\nof a slow primary store. Each guide includes a runnable interactive demo for each of the\nfollowing client libraries:\n\n* [redis-py (Python)](https://redis.io/docs/latest/develop/use-cases/cache-aside/redis-py)\n* [node-redis (Node.js)](https://redis.io/docs/latest/develop/use-cases/cache-aside/nodejs)\n* [go-redis (Go)](https://redis.io/docs/latest/develop/use-cases/cache-aside/go)\n* [Jedis (Java)](https://redis.io/docs/latest/develop/use-cases/cache-aside/java-jedis)\n* [Lettuce (Java)](https://redis.io/docs/latest/develop/use-cases/cache-aside/java-lettuce)\n* [StackExchange.Redis (C#)](https://redis.io/docs/latest/develop/use-cases/cache-aside/dotnet)\n* [Predis (PHP)](https://redis.io/docs/latest/develop/use-cases/cache-aside/php)\n* [redis-rb (Ruby)](https://redis.io/docs/latest/develop/use-cases/cache-aside/ruby)\n* [redis-rs (Rust)](https://redis.io/docs/latest/develop/use-cases/cache-aside/rust)"}],"examples":[]}
{"schema_version":2,"id":"develop/use-cases/cache-aside/java-jedis","title":"Redis cache-aside with Jedis","url":"https://redis.io/docs/latest/develop/use-cases/cache-aside/java-jedis/","summary":"Implement a Redis cache-aside layer in Java with Jedis","aliases":["/develop/use-cases/cache-aside/java","/develop/use-cases/cache-aside/jedis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:50:30+01:00","children":[],"page_type":"content","content_hash":"e576cf67e88eb602e7a13b8729ef8eb7127129f3e870cf514055ac86267ee95d","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis cache-aside layer in Java with the [Jedis](https://redis.io/docs/latest/develop/clients/jedis) client library. It includes a small local web server built on the JDK's `com.sun.net.httpserver` so you can see cache hits, misses, invalidation on write, and stampede protection in action."},{"id":"overview","title":"Overview","role":"overview","text":"Cache-aside is one of the most common Redis use cases for read-heavy applications. Instead of querying the primary database on every request, the application checks Redis first and only falls back to the primary on a miss. The result is written back to Redis with a TTL so the next read is served from memory.\n\nThat gives you:\n\n* Sub-millisecond reads for the hot working set\n* Bounded staleness — every entry expires within a known window\n* Reduced primary database load proportional to hit rate\n* Field-level updates without re-serializing the full record\n* Protection against cache stampedes when popular keys expire under load\n\nIn this example, each cached product is stored as a Redis hash under a key like `cache:product:{id}`. The hash holds the product fields (`id`, `name`, `price_cents`, `stock`) and the key has a TTL so stale data is bounded automatically."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow on every read looks like this:\n\n1. The application calls `cache.get(productId, primary::read)`\n2. The helper runs `HGETALL` against `cache:product:{id}`\n3. On a hit, the cached hash is returned directly\n4. On a miss, the helper acquires a Lua-backed single-flight lock and invokes the loader to fetch from the primary\n5. The helper writes the result back to Redis with `HSET` plus `EXPIRE` and releases the lock\n6. Concurrent threads that fail to acquire the lock wait briefly for the cache to populate, then return that value instead of issuing their own primary read\n\nOn a write, the application updates the primary and then deletes the cache key, so the next read repopulates from the new source value."},{"id":"the-cache-aside-helper","title":"The cache-aside helper","role":"content","text":"The `RedisCache` class wraps the cache-aside operations\n([source](RedisCache.java)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each cached product is stored in a Redis hash:\n\n[code example]\n\nThe implementation uses:\n\n* [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) to read the cached record\n* [`HSET`](https://redis.io/docs/latest/commands/hset) plus [`EXPIRE`](https://redis.io/docs/latest/commands/expire) to repopulate after a miss\n* [`DEL`](https://redis.io/docs/latest/commands/del) to invalidate on writes\n* [`TTL`](https://redis.io/docs/latest/commands/ttl) to surface remaining staleness in the demo UI\n* [`EVAL`](https://redis.io/docs/latest/commands/eval) for the Lua single-flight lock that prevents stampedes\n* [`WATCH`](https://redis.io/docs/latest/commands/watch)/[`MULTI`](https://redis.io/docs/latest/commands/multi)/[`EXEC`](https://redis.io/docs/latest/commands/exec) for the conditional field update path"},{"id":"cache-aside-reads","title":"Cache-aside reads","role":"content","text":"The `get()` method runs `HGETALL` on the cache key first. On a hit it returns the cached hash and increments the in-process hit counter. On a miss, it delegates to a single-flight loader:\n\n[code example]\n\nThe returned `Result` includes the measured Redis round-trip time so the demo UI can show the latency difference between a hit and a miss."},{"id":"stampede-protection-with-a-lua-lock","title":"Stampede protection with a Lua lock","role":"content","text":"When a popular key expires, every concurrent reader observes the miss at the same instant. Without coordination, all of them would query the primary and overwrite the cache redundantly — a *cache stampede*.\n\nThe helper uses a tiny Lua script to acquire a short-lived lock atomically. Only the thread that wins the `SET NX` becomes the primary loader; the rest poll the cache briefly and return the value the lock holder writes:\n\n[code example]\n\nA second script releases the lock only if the caller still owns it, so a lock that timed out and was re-acquired by someone else cannot be released by mistake:\n\n[code example]\n\nThe Java side runs both scripts via `eval()` on every miss:\n\n[code example]\n\nThe unique `token` per caller is what makes the release script safe — only the thread that actually holds the lock can release it."},{"id":"invalidation-on-write","title":"Invalidation on write","role":"content","text":"When a write hits the primary, the application invalidates the cache key. The next read pulls fresh data from the primary:\n\n[code example]\n\nThis is the simplest and safest pattern: never try to keep the cache and primary in sync directly, just delete the cache entry and let the next read repopulate it."},{"id":"field-level-updates","title":"Field-level updates","role":"content","text":"Because each record is stored as a hash, the cache helper can also update a single field in place without re-serializing the full record. The update only writes if the entry is already cached, so a partial record can never appear in Redis:\n\n[code example]\n\nThis is useful for hot fields that change more often than the rest of the record (a stock counter, a view count) and would otherwise force a full reload."},{"id":"hitmiss-accounting","title":"Hit/miss accounting","role":"content","text":"The helper keeps in-process counters for hits, misses, and stampedes that were suppressed by the single-flight lock. The demo UI surfaces these so you can see the cache absorbing load:\n\n[code example]\n\nIn production you would emit these as Micrometer counters or push them into your metrics pipeline rather than holding them as `AtomicLong`s in process memory."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before running the demo, make sure that:\n\n* Redis is running and accessible. By default, the demo connects to `localhost:6379`.\n* JDK 11 or later is installed.\n* The Jedis JAR is on your classpath. Get it from\n  [Maven Central](https://repo1.maven.org/maven2/redis/clients/jedis/),\n  or via Maven/Gradle in a project setup.\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":"A local demo server is included to show the cache-aside layer in action\n([source](DemoServer.java)):\n\n[code example]\n\nThe demo server uses standard Java libraries for HTTP handling and concurrency:\n\n* [`com.sun.net.httpserver.HttpServer`](https://docs.oracle.com/en/java/javase/21/docs/api/jdk.httpserver/com/sun/net/httpserver/HttpServer.html) for the web server\n* [`java.util.concurrent`](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/package-summary.html) for the stampede test thread pool\n* [`java.security.SecureRandom`](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/security/SecureRandom.html) for the per-caller lock token\n\nIt exposes a small interactive page where you can:\n\n* Read a product through the cache and see whether it was a hit or a miss\n* Compare the measured Redis round-trip against the simulated primary read latency\n* Watch the cache TTL count down between requests\n* Update a field on the primary and see the cache invalidate automatically\n* Run a stampede test that fires many concurrent reads at a freshly-invalidated key and confirms only one of them reaches the primary\n* Reset the hit/miss counters at any time\n\nAfter starting the server, visit `http://localhost:8080`."},{"id":"the-mock-primary-store","title":"The mock primary store","role":"content","text":"To make the demo self-contained, the example includes a `MockPrimaryStore` that stands in for a slow disk-backed database\n([source](MockPrimaryStore.java)):\n\n[code example]\n\nEvery call to `read()` sleeps for `readLatencyMs` so the difference between a cache hit and a miss is obvious in the UI. The store also tracks the total number of primary reads, which the stampede test uses to confirm that single-flight is working — for N concurrent readers against a cold key, you should see exactly one primary read.\n\nIn a real application this would be replaced by a JDBC query, an HTTP call to a downstream service, or any other slow-but-authoritative source."},{"id":"production-usage","title":"Production usage","role":"content","text":"This guide uses a deliberately small local demo so you can focus on the cache-aside pattern. In production, you will usually want to harden several aspects of it."},{"id":"choose-a-ttl-that-matches-your-staleness-tolerance","title":"Choose a TTL that matches your staleness tolerance","role":"content","text":"The TTL is the upper bound on how long a stale value can be served. Shorter TTLs mean lower hit rates and more primary load; longer TTLs mean higher hit rates and more stale reads between writes. Pick the value that matches your business tolerance for stale data, and combine it with explicit invalidation on writes for the cases where you cannot tolerate any staleness."},{"id":"invalidate-dont-try-to-keep-the-cache-in-sync","title":"Invalidate, don't try to keep the cache in sync","role":"content","text":"When the underlying record changes, delete the cache key rather than rewriting it. Cache-aside is robust precisely because it never assumes the cache holds the latest value — the next read always re-fetches from the primary on a miss."},{"id":"handle-missing-records-explicitly","title":"Handle missing records explicitly","role":"content","text":"In this demo, a missing record returns `null` and nothing is cached. In a real system you may want to cache \"not found\" sentinels with a short TTL to absorb load from probing for non-existent IDs, while making sure the sentinel TTL is shorter than the positive cache entry so a newly-created record becomes visible quickly."},{"id":"tune-the-single-flight-lock-ttl","title":"Tune the single-flight lock TTL","role":"content","text":"The lock TTL needs to be longer than the worst-case primary read latency so a slow loader does not lose the lock midway. The unique token in `RELEASE_LOCK_SCRIPT` ensures the original caller does not delete someone else's lock if its lock has expired."},{"id":"tune-the-jedispool","title":"Tune the JedisPool","role":"content","text":"The demo uses a default `JedisPoolConfig`. In production, set `setMaxTotal()` and `setMaxIdle()` to match your concurrency profile and server-side `maxclients`. Wrap pool acquisition in try-with-resources so connections are returned even on exceptions."},{"id":"namespace-cache-keys-in-shared-redis-deployments","title":"Namespace cache keys in shared Redis deployments","role":"content","text":"If multiple applications share a Redis deployment, prefix cache keys with the application name (`cache:billing:product:{id}`) so different services cannot clobber each other's entries."},{"id":"inspect-cached-entries-directly-in-redis","title":"Inspect cached entries directly in Redis","role":"content","text":"When testing or troubleshooting, inspect the stored cache key directly to confirm the application is writing the fields and TTL you expect:\n\n[code example]"},{"id":"learn-more","title":"Learn more","role":"related","text":"* [Jedis guide](https://redis.io/docs/latest/develop/clients/jedis) - Install and use the Jedis Redis client\n* [SET command](https://redis.io/docs/latest/commands/set) - Set a string with TTL options (`EX`, `PX`, `NX`)\n* [HSET command](https://redis.io/docs/latest/commands/hset) - Write hash fields\n* [HGETALL command](https://redis.io/docs/latest/commands/hgetall) - Read every field of a hash\n* [EXPIRE command](https://redis.io/docs/latest/commands/expire) - Set key expiration in seconds\n* [DEL command](https://redis.io/docs/latest/commands/del) - Delete a key on invalidation\n* [Lua scripting](https://redis.io/docs/latest/develop/programmability/eval-intro) - Atomic single-flight locks and stampede mitigation"}],"examples":[{"id":"the-cache-aside-helper-ex0","language":"java","code":"import redis.clients.jedis.JedisPool;\nimport redis.clients.jedis.JedisPoolConfig;\n\nJedisPool pool = new JedisPool(new JedisPoolConfig(), \"localhost\", 6379);\nMockPrimaryStore primary = new MockPrimaryStore(150);\nRedisCache cache = new RedisCache(pool);\n\n// Read through the cache.\nRedisCache.Result result = cache.get(\"p-001\", primary::read);\nSystem.out.printf(\"hit=%s latency=%.2fms record=%s%n\",\n    result.hit, result.redisLatencyMs, result.record);\n\n// Update a single field without rewriting the whole record.\ncache.updateField(\"p-001\", \"stock\", \"41\");\n\n// Invalidate the cache key on a write to the primary.\nprimary.updateField(\"p-001\", \"price_cents\", \"699\");\ncache.invalidate(\"p-001\");","section_id":"the-cache-aside-helper"},{"id":"data-model-ex0","language":"text","code":"cache:product:p-001\n  id          = p-001\n  name        = Sourdough Loaf\n  price_cents = 650\n  stock       = 42","section_id":"data-model"},{"id":"cache-aside-reads-ex0","language":"java","code":"public Result get(String entityId, Function<String, Map<String, String>> loader) {\n    String cacheKey = cacheKey(entityId);\n    Map<String, String> cached;\n    long startedNs = System.nanoTime();\n\n    try (Jedis jedis = pool.getResource()) {\n        cached = jedis.hgetAll(cacheKey);\n    }\n    double redisLatencyMs = (System.nanoTime() - startedNs) / 1_000_000.0;\n\n    if (cached != null && !cached.isEmpty()) {\n        hits.incrementAndGet();\n        return new Result(cached, true, redisLatencyMs);\n    }\n\n    misses.incrementAndGet();\n    Map<String, String> record = loadWithSingleFlight(entityId, loader);\n    return new Result(record, false, redisLatencyMs);\n}","section_id":"cache-aside-reads"},{"id":"stampede-protection-with-a-lua-lock-ex0","language":"lua","code":"-- Acquire a short-lived lock with SET NX PX. Returns 1 on acquire, 0 otherwise.\nif redis.call('SET', KEYS[1], ARGV[1], 'NX', 'PX', ARGV[2]) then\n    return 1\nend\nreturn 0","section_id":"stampede-protection-with-a-lua-lock"},{"id":"stampede-protection-with-a-lua-lock-ex1","language":"lua","code":"if redis.call('GET', KEYS[1]) == ARGV[1] then\n    return redis.call('DEL', KEYS[1])\nend\nreturn 0","section_id":"stampede-protection-with-a-lua-lock"},{"id":"stampede-protection-with-a-lua-lock-ex2","language":"java","code":"private Map<String, String> loadWithSingleFlight(\n        String entityId,\n        Function<String, Map<String, String>> loader) {\n    String cacheKey = cacheKey(entityId);\n    String lockKey = lockKey(entityId);\n    String token = randomToken();\n\n    try (Jedis jedis = pool.getResource()) {\n        Object acquired = jedis.eval(\n                ACQUIRE_LOCK_SCRIPT,\n                List.of(lockKey),\n                List.of(token, Integer.toString(lockTtlMs)));\n        if (asInt(acquired) == 1) {\n            try {\n                Map<String, String> record = loader.apply(entityId);\n                if (record == null) return null;\n                Transaction tx = jedis.multi();\n                tx.del(cacheKey);\n                tx.hset(cacheKey, record);\n                tx.expire(cacheKey, ttl);\n                tx.exec();\n                return record;\n            } finally {\n                jedis.eval(RELEASE_LOCK_SCRIPT, List.of(lockKey), List.of(token));\n            }\n        }\n    }\n\n    stampedesSuppressed.incrementAndGet();\n    long deadline = System.nanoTime() + (long) lockTtlMs * 1_000_000L;\n    while (System.nanoTime() < deadline) {\n        Thread.sleep(waitPollMs);\n        try (Jedis jedis = pool.getResource()) {\n            Map<String, String> cached = jedis.hgetAll(cacheKey);\n            if (cached != null && !cached.isEmpty()) return cached;\n        }\n    }\n    return loader.apply(entityId);\n}","section_id":"stampede-protection-with-a-lua-lock"},{"id":"invalidation-on-write-ex0","language":"java","code":"public boolean invalidate(String entityId) {\n    try (Jedis jedis = pool.getResource()) {\n        return jedis.del(cacheKey(entityId)) == 1L;\n    }\n}","section_id":"invalidation-on-write"},{"id":"field-level-updates-ex0","language":"java","code":"public boolean updateField(String entityId, String field, String value) {\n    String cacheKey = cacheKey(entityId);\n    while (true) {\n        try (Jedis jedis = pool.getResource()) {\n            jedis.watch(cacheKey);\n            if (!jedis.exists(cacheKey)) {\n                jedis.unwatch();\n                return false;\n            }\n            Transaction tx = jedis.multi();\n            tx.hset(cacheKey, field, value);\n            tx.expire(cacheKey, ttl);\n            List<Object> result = tx.exec();\n            if (result == null) continue; // WATCH detected a change — retry.\n            return true;\n        }\n    }\n}","section_id":"field-level-updates"},{"id":"hitmiss-accounting-ex0","language":"java","code":"public Map<String, Object> stats() {\n    long h = hits.get();\n    long m = misses.get();\n    long total = h + m;\n    double hitRate = total == 0 ? 0.0 : ((long) (1000.0 * h / total)) / 10.0;\n    Map<String, Object> stats = new HashMap<>();\n    stats.put(\"hits\", h);\n    stats.put(\"misses\", m);\n    stats.put(\"stampedes_suppressed\", stampedesSuppressed.get());\n    stats.put(\"hit_rate_pct\", hitRate);\n    return stats;\n}","section_id":"hitmiss-accounting"},{"id":"running-the-demo-ex0","language":"bash","code":"javac -cp jedis-5.0.0.jar:commons-pool2-2.12.1.jar:slf4j-api-1.7.36.jar:gson-2.9.1.jar:json-20230618.jar \\\n    RedisCache.java MockPrimaryStore.java DemoServer.java\n\njava -cp .:jedis-5.0.0.jar:commons-pool2-2.12.1.jar:slf4j-api-1.7.36.jar:gson-2.9.1.jar:json-20230618.jar \\\n    DemoServer --port 8080 --redis-host localhost --redis-port 6379","section_id":"running-the-demo"},{"id":"the-mock-primary-store-ex0","language":"java","code":"public Map<String, String> read(String id) {\n    try {\n        Thread.sleep(readLatencyMs);\n    } catch (InterruptedException e) {\n        Thread.currentThread().interrupt();\n        return null;\n    }\n    // ...\n}","section_id":"the-mock-primary-store"},{"id":"inspect-cached-entries-directly-in-redis-ex0","language":"bash","code":"redis-cli HGETALL cache:product:p-001\nredis-cli TTL cache:product:p-001","section_id":"inspect-cached-entries-directly-in-redis"}]}
{"schema_version":2,"id":"develop/use-cases/cache-aside/java-lettuce","title":"Redis cache-aside with Lettuce","url":"https://redis.io/docs/latest/develop/use-cases/cache-aside/java-lettuce/","summary":"Implement a Redis cache-aside layer in Java with Lettuce","aliases":["/develop/use-cases/cache-aside/lettuce"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:50:30+01:00","children":[],"page_type":"content","content_hash":"48dee351990ce95775e53fbfb555a606d00826851a650177f0cc8400accdf6e0","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis cache-aside layer in Java with the [Lettuce](https://redis.io/docs/latest/develop/clients/lettuce) client library. It includes a small local web server built on the JDK's `com.sun.net.httpserver` so you can see cache hits, misses, invalidation on write, and stampede protection in action."},{"id":"overview","title":"Overview","role":"overview","text":"Cache-aside is one of the most common Redis use cases for read-heavy applications. Instead of querying the primary database on every request, the application checks Redis first and only falls back to the primary on a miss. The result is written back to Redis with a TTL so the next read is served from memory.\n\nThat gives you:\n\n* Sub-millisecond reads for the hot working set\n* Bounded staleness — every entry expires within a known window\n* Reduced primary database load proportional to hit rate\n* Field-level updates without re-serializing the full record\n* Protection against cache stampedes when popular keys expire under load\n\nIn this example, each cached product is stored as a Redis hash under a key like `cache:product:{id}`. The hash holds the product fields (`id`, `name`, `price_cents`, `stock`) and the key has a TTL so stale data is bounded automatically.\n\nThis guide uses Lettuce's synchronous command API (`StatefulRedisConnection.sync()`). Lettuce also provides asynchronous (`async()`) and reactive (`reactive()`) APIs over the same connection — the cache-aside pattern adapts cleanly to either, but synchronous code keeps the helper compact and easy to follow."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow on every read looks like this:\n\n1. The application calls `cache.get(productId, primary::read)`\n2. The helper runs `HGETALL` against `cache:product:{id}`\n3. On a hit, the cached hash is returned directly\n4. On a miss, the helper acquires a Lua-backed single-flight lock and invokes the loader to fetch from the primary\n5. The helper writes the result back to Redis with `HSET` plus `EXPIRE` and releases the lock\n6. Concurrent threads that fail to acquire the lock wait briefly for the cache to populate, then return that value instead of issuing their own primary read\n\nOn a write, the application updates the primary and then deletes the cache key, so the next read repopulates from the new source value."},{"id":"the-cache-aside-helper","title":"The cache-aside helper","role":"content","text":"The `RedisCache` class wraps the cache-aside operations\n([source](RedisCache.java)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each cached product is stored in a Redis hash:\n\n[code example]\n\nThe implementation uses:\n\n* [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) to read the cached record\n* [`HSET`](https://redis.io/docs/latest/commands/hset) plus [`EXPIRE`](https://redis.io/docs/latest/commands/expire) to repopulate after a miss\n* [`DEL`](https://redis.io/docs/latest/commands/del) to invalidate on writes\n* [`TTL`](https://redis.io/docs/latest/commands/ttl) to surface remaining staleness in the demo UI\n* [`EVAL`](https://redis.io/docs/latest/commands/eval) for the Lua single-flight lock that prevents stampedes\n* [`WATCH`](https://redis.io/docs/latest/commands/watch)/[`MULTI`](https://redis.io/docs/latest/commands/multi)/[`EXEC`](https://redis.io/docs/latest/commands/exec) for the conditional field update path"},{"id":"cache-aside-reads","title":"Cache-aside reads","role":"content","text":"The `get()` method runs `HGETALL` on the cache key first. On a hit it returns the cached hash and increments the hit counter. On a miss, it delegates to a single-flight loader:\n\n[code example]\n\nThe returned `Result` includes the measured Redis round-trip time so the demo UI can show the latency difference between a hit and a miss."},{"id":"stampede-protection-with-a-lua-lock","title":"Stampede protection with a Lua lock","role":"content","text":"When a popular key expires, every concurrent reader observes the miss at the same instant. Without coordination, all of them would query the primary and overwrite the cache redundantly — a *cache stampede*.\n\nThe helper uses a tiny Lua script to acquire a short-lived lock atomically. Only the thread that wins the `SET NX` becomes the primary loader; the rest poll the cache briefly and return the value the lock holder writes:\n\n[code example]\n\nA second script releases the lock only if the caller still owns it, so a lock that timed out and was re-acquired by someone else cannot be released by mistake:\n\n[code example]\n\nThe Java side runs both scripts via `eval()` on every miss:\n\n[code example]\n\nThe unique `token` per caller is what makes the release script safe — only the thread that actually holds the lock can release it."},{"id":"invalidation-on-write","title":"Invalidation on write","role":"content","text":"When a write hits the primary, the application invalidates the cache key. The next read pulls fresh data from the primary:\n\n[code example]\n\nThis is the simplest and safest pattern: never try to keep the cache and primary in sync directly, just delete the cache entry and let the next read repopulate it."},{"id":"field-level-updates","title":"Field-level updates","role":"content","text":"Because each record is stored as a hash, the cache helper can also update a single field in place without re-serializing the full record. The update only writes if the entry is already cached, so a partial record can never appear in Redis:\n\n[code example]\n\nA subtle Lettuce-specific point: a single `StatefulRedisConnection` is thread-safe for individual command calls, but `WATCH`/`MULTI`/`EXEC` is connection-scoped state. Two threads issuing transactions over the same connection at the same time would interleave — commands meant for one transaction would end up queued inside another. The demo shares one connection across HTTP handlers, so `txLock` serializes every transaction block: both the field-level update above *and* the cache-miss repopulate inside `loadWithSingleFlight`. In production you would hand each transactional caller its own connection from a pool (see [Production usage](#production-usage)) and drop the lock entirely."},{"id":"hitmiss-accounting","title":"Hit/miss accounting","role":"content","text":"The helper keeps in-process counters for hits, misses, and stampedes that were suppressed by the single-flight lock. The demo UI surfaces these so you can see the cache absorbing load:\n\n[code example]\n\nIn production you would emit these as Micrometer counters or push them into your metrics pipeline rather than holding them as `AtomicLong`s in process memory."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before running the demo, make sure that:\n\n* Redis is running and accessible. By default, the demo connects to `localhost:6379`.\n* JDK 11 or later is installed.\n* The Lettuce JAR (and its Netty + Reactor dependencies) is on your classpath.\n  Get them from\n  [Maven Central](https://repo1.maven.org/maven2/io/lettuce/lettuce-core/),\n  or via Maven/Gradle in a project setup.\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":"A local demo server is included to show the cache-aside layer in action\n([source](DemoServer.java)):\n\n[code example]\n\n(Where `lib/` contains `lettuce-core`, `reactor-core`, `reactive-streams`, and the relevant Netty jars.)\n\nThe demo server uses standard Java libraries for HTTP handling and concurrency:\n\n* [`com.sun.net.httpserver.HttpServer`](https://docs.oracle.com/en/java/javase/21/docs/api/jdk.httpserver/com/sun/net/httpserver/HttpServer.html) for the web server\n* [`java.util.concurrent`](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/package-summary.html) for the stampede test thread pool\n* [`java.security.SecureRandom`](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/security/SecureRandom.html) for the per-caller lock token\n\nIt exposes a small interactive page where you can:\n\n* Read a product through the cache and see whether it was a hit or a miss\n* Compare the measured Redis round-trip against the simulated primary read latency\n* Watch the cache TTL count down between requests\n* Update a field on the primary and see the cache invalidate automatically\n* Run a stampede test that fires many concurrent reads at a freshly-invalidated key and confirms only one of them reaches the primary\n* Reset the hit/miss counters at any time\n\nAfter starting the server, visit `http://localhost:8080`."},{"id":"the-mock-primary-store","title":"The mock primary store","role":"content","text":"To make the demo self-contained, the example includes a `MockPrimaryStore` that stands in for a slow disk-backed database\n([source](MockPrimaryStore.java)):\n\n[code example]\n\nEvery call to `read()` sleeps for `readLatencyMs` so the difference between a cache hit and a miss is obvious in the UI. The store also tracks the total number of primary reads, which the stampede test uses to confirm that single-flight is working — for N concurrent readers against a cold key, you should see exactly one primary read.\n\nIn a real application this would be replaced by a JDBC query, an HTTP call to a downstream service, or any other slow-but-authoritative source."},{"id":"production-usage","title":"Production usage","role":"content","text":"This guide uses a deliberately small local demo so you can focus on the cache-aside pattern. In production, you will usually want to harden several aspects of it."},{"id":"choose-a-ttl-that-matches-your-staleness-tolerance","title":"Choose a TTL that matches your staleness tolerance","role":"content","text":"The TTL is the upper bound on how long a stale value can be served. Shorter TTLs mean lower hit rates and more primary load; longer TTLs mean higher hit rates and more stale reads between writes. Pick the value that matches your business tolerance for stale data, and combine it with explicit invalidation on writes for the cases where you cannot tolerate any staleness."},{"id":"invalidate-dont-try-to-keep-the-cache-in-sync","title":"Invalidate, don't try to keep the cache in sync","role":"content","text":"When the underlying record changes, delete the cache key rather than rewriting it. Cache-aside is robust precisely because it never assumes the cache holds the latest value — the next read always re-fetches from the primary on a miss."},{"id":"use-a-connection-pool-for-transactions","title":"Use a connection pool for transactions","role":"content","text":"The demo shares a single `StatefulRedisConnection` across threads and serializes WATCH/MULTI/EXEC blocks with an in-process lock. In production, use [`ConnectionPoolSupport`](https://github.com/redis/lettuce/wiki/Connection-Pooling) so that each transactional caller gets its own connection. You can drop the application-level lock once each transaction has its own dedicated connection."},{"id":"handle-missing-records-explicitly","title":"Handle missing records explicitly","role":"content","text":"In this demo, a missing record returns `null` and nothing is cached. In a real system you may want to cache \"not found\" sentinels with a short TTL to absorb load from probing for non-existent IDs, while making sure the sentinel TTL is shorter than the positive cache entry so a newly-created record becomes visible quickly."},{"id":"tune-the-single-flight-lock-ttl","title":"Tune the single-flight lock TTL","role":"content","text":"The lock TTL needs to be longer than the worst-case primary read latency so a slow loader does not lose the lock midway. The unique token in `RELEASE_LOCK_SCRIPT` ensures the original caller does not delete someone else's lock if its lock has expired."},{"id":"consider-the-async-or-reactive-apis","title":"Consider the async or reactive APIs","role":"content","text":"For high-throughput or event-driven applications, Lettuce's `async()` (`CompletionStage`-based) or `reactive()` (Project Reactor) APIs let you keep request-handling threads free while the loader is in flight. The cache-aside structure is identical — replace the synchronous `hgetall` / `eval` / `multi` calls with their async counterparts and chain them together."},{"id":"namespace-cache-keys-in-shared-redis-deployments","title":"Namespace cache keys in shared Redis deployments","role":"content","text":"If multiple applications share a Redis deployment, prefix cache keys with the application name (`cache:billing:product:{id}`) so different services cannot clobber each other's entries."},{"id":"inspect-cached-entries-directly-in-redis","title":"Inspect cached entries directly in Redis","role":"content","text":"When testing or troubleshooting, inspect the stored cache key directly to confirm the application is writing the fields and TTL you expect:\n\n[code example]"},{"id":"learn-more","title":"Learn more","role":"related","text":"* [Lettuce guide](https://redis.io/docs/latest/develop/clients/lettuce) - Install and use the Lettuce Redis client\n* [SET command](https://redis.io/docs/latest/commands/set) - Set a string with TTL options (`EX`, `PX`, `NX`)\n* [HSET command](https://redis.io/docs/latest/commands/hset) - Write hash fields\n* [HGETALL command](https://redis.io/docs/latest/commands/hgetall) - Read every field of a hash\n* [EXPIRE command](https://redis.io/docs/latest/commands/expire) - Set key expiration in seconds\n* [DEL command](https://redis.io/docs/latest/commands/del) - Delete a key on invalidation\n* [Lua scripting](https://redis.io/docs/latest/develop/programmability/eval-intro) - Atomic single-flight locks and stampede mitigation"}],"examples":[{"id":"the-cache-aside-helper-ex0","language":"java","code":"import io.lettuce.core.RedisClient;\nimport io.lettuce.core.RedisURI;\nimport io.lettuce.core.api.StatefulRedisConnection;\n\nRedisClient client = RedisClient.create(\n    RedisURI.builder().withHost(\"localhost\").withPort(6379).build());\nStatefulRedisConnection<String, String> connection = client.connect();\n\nMockPrimaryStore primary = new MockPrimaryStore(150);\nRedisCache cache = new RedisCache(connection);\n\n// Read through the cache.\nRedisCache.Result result = cache.get(\"p-001\", primary::read);\nSystem.out.printf(\"hit=%s latency=%.2fms record=%s%n\",\n    result.hit, result.redisLatencyMs, result.record);\n\n// Update a single field without rewriting the whole record.\ncache.updateField(\"p-001\", \"stock\", \"41\");\n\n// Invalidate the cache key on a write to the primary.\nprimary.updateField(\"p-001\", \"price_cents\", \"699\");\ncache.invalidate(\"p-001\");","section_id":"the-cache-aside-helper"},{"id":"data-model-ex0","language":"text","code":"cache:product:p-001\n  id          = p-001\n  name        = Sourdough Loaf\n  price_cents = 650\n  stock       = 42","section_id":"data-model"},{"id":"cache-aside-reads-ex0","language":"java","code":"public Result get(String entityId, Function<String, Map<String, String>> loader) {\n    RedisCommands<String, String> sync = connection.sync();\n    String cacheKey = cacheKey(entityId);\n\n    long startedNs = System.nanoTime();\n    Map<String, String> cached = sync.hgetall(cacheKey);\n    double redisLatencyMs = (System.nanoTime() - startedNs) / 1_000_000.0;\n\n    if (cached != null && !cached.isEmpty()) {\n        hits.incrementAndGet();\n        return new Result(cached, true, redisLatencyMs);\n    }\n\n    misses.incrementAndGet();\n    Map<String, String> record = loadWithSingleFlight(entityId, loader);\n    return new Result(record, false, redisLatencyMs);\n}","section_id":"cache-aside-reads"},{"id":"stampede-protection-with-a-lua-lock-ex0","language":"lua","code":"-- Acquire a short-lived lock with SET NX PX. Returns 1 on acquire, 0 otherwise.\nif redis.call('SET', KEYS[1], ARGV[1], 'NX', 'PX', ARGV[2]) then\n    return 1\nend\nreturn 0","section_id":"stampede-protection-with-a-lua-lock"},{"id":"stampede-protection-with-a-lua-lock-ex1","language":"lua","code":"if redis.call('GET', KEYS[1]) == ARGV[1] then\n    return redis.call('DEL', KEYS[1])\nend\nreturn 0","section_id":"stampede-protection-with-a-lua-lock"},{"id":"stampede-protection-with-a-lua-lock-ex2","language":"java","code":"private Map<String, String> loadWithSingleFlight(\n        String entityId,\n        Function<String, Map<String, String>> loader) {\n    RedisCommands<String, String> sync = connection.sync();\n    String cacheKey = cacheKey(entityId);\n    String lockKey = lockKey(entityId);\n    String token = randomToken();\n\n    Long acquired = sync.eval(\n            ACQUIRE_LOCK_SCRIPT,\n            ScriptOutputType.INTEGER,\n            new String[]{lockKey},\n            token,\n            Integer.toString(lockTtlMs));\n    if (acquired != null && acquired == 1L) {\n        try {\n            Map<String, String> record = loader.apply(entityId);\n            if (record == null) return null;\n            txLock.lock();\n            try {\n                sync.multi();\n                sync.del(cacheKey);\n                sync.hset(cacheKey, record);\n                sync.expire(cacheKey, ttl);\n                sync.exec();\n            } finally {\n                txLock.unlock();\n            }\n            return record;\n        } finally {\n            sync.eval(RELEASE_LOCK_SCRIPT, ScriptOutputType.INTEGER,\n                new String[]{lockKey}, token);\n        }\n    }\n\n    stampedesSuppressed.incrementAndGet();\n    long deadline = System.nanoTime() + (long) lockTtlMs * 1_000_000L;\n    while (System.nanoTime() < deadline) {\n        Thread.sleep(waitPollMs);\n        Map<String, String> cached = sync.hgetall(cacheKey);\n        if (cached != null && !cached.isEmpty()) return cached;\n    }\n    return loader.apply(entityId);\n}","section_id":"stampede-protection-with-a-lua-lock"},{"id":"invalidation-on-write-ex0","language":"java","code":"public boolean invalidate(String entityId) {\n    return connection.sync().del(cacheKey(entityId)) == 1L;\n}","section_id":"invalidation-on-write"},{"id":"field-level-updates-ex0","language":"java","code":"public boolean updateField(String entityId, String field, String value) {\n    RedisCommands<String, String> sync = connection.sync();\n    String cacheKey = cacheKey(entityId);\n    txLock.lock();\n    try {\n        while (true) {\n            sync.watch(cacheKey);\n            if (sync.exists(cacheKey) == 0L) {\n                sync.unwatch();\n                return false;\n            }\n            sync.multi();\n            sync.hset(cacheKey, field, value);\n            sync.expire(cacheKey, ttl);\n            TransactionResult result = sync.exec();\n            if (result.wasDiscarded()) continue; // WATCH detected a change — retry.\n            return true;\n        }\n    } finally {\n        txLock.unlock();\n    }\n}","section_id":"field-level-updates"},{"id":"hitmiss-accounting-ex0","language":"java","code":"public Map<String, Object> stats() {\n    long h = hits.get();\n    long m = misses.get();\n    long total = h + m;\n    double hitRate = total == 0 ? 0.0 : ((long) (1000.0 * h / total)) / 10.0;\n    Map<String, Object> stats = new HashMap<>();\n    stats.put(\"hits\", h);\n    stats.put(\"misses\", m);\n    stats.put(\"stampedes_suppressed\", stampedesSuppressed.get());\n    stats.put(\"hit_rate_pct\", hitRate);\n    return stats;\n}","section_id":"hitmiss-accounting"},{"id":"running-the-demo-ex0","language":"bash","code":"javac -cp 'lib/*' RedisCache.java MockPrimaryStore.java DemoServer.java\njava -cp '.:lib/*' DemoServer --port 8080 --redis-host localhost --redis-port 6379","section_id":"running-the-demo"},{"id":"the-mock-primary-store-ex0","language":"java","code":"public Map<String, String> read(String id) {\n    try {\n        Thread.sleep(readLatencyMs);\n    } catch (InterruptedException e) {\n        Thread.currentThread().interrupt();\n        return null;\n    }\n    // ...\n}","section_id":"the-mock-primary-store"},{"id":"inspect-cached-entries-directly-in-redis-ex0","language":"bash","code":"redis-cli HGETALL cache:product:p-001\nredis-cli TTL cache:product:p-001","section_id":"inspect-cached-entries-directly-in-redis"}]}
{"schema_version":2,"id":"develop/use-cases/cache-aside/nodejs","title":"Redis cache-aside with node-redis","url":"https://redis.io/docs/latest/develop/use-cases/cache-aside/nodejs/","summary":"Implement a Redis cache-aside layer in Node.js with node-redis","aliases":["/develop/use-cases/cache-aside/node-redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:50:30+01:00","children":[],"page_type":"content","content_hash":"b6400069e7c8fb7454ce964d94fbfd435210015873e43b160164569501d652da","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis cache-aside layer in Node.js with [`node-redis`](https://redis.io/docs/latest/develop/clients/nodejs). It includes a small local web server built with Node's standard `http` module so you can see cache hits, misses, invalidation on write, and stampede protection in action."},{"id":"overview","title":"Overview","role":"overview","text":"Cache-aside is one of the most common Redis use cases for read-heavy applications. Instead of querying the primary database on every request, the application checks Redis first and only falls back to the primary on a miss. The result is written back to Redis with a TTL so the next read is served from memory.\n\nThat gives you:\n\n* Sub-millisecond reads for the hot working set\n* Bounded staleness — every entry expires within a known window\n* Reduced primary database load proportional to hit rate\n* Field-level updates without re-serializing the full record\n* Protection against cache stampedes when popular keys expire under load\n\nIn this example, each cached product is stored as a Redis hash under a key like `cache:product:{id}`. The hash holds the product fields (`id`, `name`, `price_cents`, `stock`) and the key has a TTL so stale data is bounded automatically."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow on every read looks like this:\n\n1. The application calls `cache.get(productId, loader)`\n2. The helper runs `HGETALL` against `cache:product:{id}`\n3. On a hit, the cached hash is returned directly\n4. On a miss, the helper acquires a Lua-backed single-flight lock and calls `loader(productId)` to read the primary\n5. The helper writes the result back to Redis with `HSET` plus `EXPIRE` and releases the lock\n6. Concurrent callers that fail to acquire the lock wait briefly for the cache to populate, then return that value instead of issuing their own primary read\n\nOn a write, the application updates the primary and then deletes the cache key, so the next read repopulates from the new source value."},{"id":"the-cache-aside-helper","title":"The cache-aside helper","role":"content","text":"The `RedisCache` class wraps the cache-aside operations\n([source](cache.js)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each cached product is stored in a Redis hash:\n\n[code example]\n\nThe implementation uses:\n\n* [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) to read the cached record\n* [`HSET`](https://redis.io/docs/latest/commands/hset) plus [`EXPIRE`](https://redis.io/docs/latest/commands/expire) to repopulate after a miss\n* [`DEL`](https://redis.io/docs/latest/commands/del) to invalidate on writes\n* [`TTL`](https://redis.io/docs/latest/commands/ttl) to surface remaining staleness in the demo UI\n* [`EVAL`](https://redis.io/docs/latest/commands/eval) for the Lua single-flight lock that prevents stampedes\n* [`WATCH`](https://redis.io/docs/latest/commands/watch)/[`MULTI`](https://redis.io/docs/latest/commands/multi)/[`EXEC`](https://redis.io/docs/latest/commands/exec) for the conditional field update path"},{"id":"cache-aside-reads","title":"Cache-aside reads","role":"content","text":"The `get()` method runs `HGETALL` on the cache key first. On a hit it returns the cached hash and records the hit in an in-process counter. On a miss, it delegates to a single-flight loader:\n\n[code example]\n\nThe return object includes the measured Redis round-trip time so the demo UI can show the latency difference between a hit and a miss."},{"id":"stampede-protection-with-a-lua-lock","title":"Stampede protection with a Lua lock","role":"content","text":"When a popular key expires, every concurrent reader observes the miss at the same instant. Without coordination, all of them would query the primary and overwrite the cache redundantly — a *cache stampede*.\n\nThe helper uses a tiny Lua script to acquire a short-lived lock atomically. Only the caller that wins the `SET NX` becomes the primary loader; the rest poll the cache briefly and return the value the lock holder writes:\n\n[code example]\n\nA second script releases the lock only if the caller still owns it, so a lock that timed out and was re-acquired by someone else cannot be released by mistake:\n\n[code example]\n\nThe Node.js side runs both scripts via `EVAL` on every miss:\n\n[code example]\n\nThe unique `token` per caller is what makes the release script safe — only the caller that actually holds the lock can release it."},{"id":"invalidation-on-write","title":"Invalidation on write","role":"content","text":"When a write hits the primary, the application invalidates the cache key. The next read pulls fresh data from the primary:\n\n[code example]\n\nThis is the simplest and safest pattern: never try to keep the cache and primary in sync directly, just delete the cache entry and let the next read repopulate it."},{"id":"field-level-updates","title":"Field-level updates","role":"content","text":"Because each record is stored as a hash, the cache helper can also update a single field in place without re-serializing the full record. The update only writes if the entry is already cached, so a partial record can never appear in Redis:\n\n[code example]\n\nThis is useful for hot fields that change more often than the rest of the record (a stock counter, a view count) and would otherwise force a full reload."},{"id":"hitmiss-accounting","title":"Hit/miss accounting","role":"content","text":"The helper keeps in-process counters for hits, misses, and stampedes that were suppressed by the single-flight lock. The demo UI surfaces these so you can see the cache absorbing load:\n\n[code example]\n\nIn production you would emit these as Prometheus counters or push them into your metrics pipeline rather than holding them in process memory."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before running the demo, make sure that:\n\n* Redis is running and accessible. By default, the demo connects to `localhost:6379`.\n* Node.js 18 or later is installed.\n* The `redis` package is installed:\n\n[code example]\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":"A local demo server is included to show the cache-aside layer in action\n([source](demoServer.js)):\n\n[code example]\n\nThe demo server uses only Node's built-in modules for HTTP handling and concurrency:\n\n* [`http`](https://nodejs.org/api/http.html) for the web server\n* [`url`](https://nodejs.org/api/url.html) for query and form parsing\n* [`crypto`](https://nodejs.org/api/crypto.html) for the per-caller lock token\n\nIt exposes a small interactive page where you can:\n\n* Read a product through the cache and see whether it was a hit or a miss\n* Compare the measured Redis round-trip against the simulated primary read latency\n* Watch the cache TTL count down between requests\n* Update a field on the primary and see the cache invalidate automatically\n* Run a stampede test that fires many concurrent reads at a freshly-invalidated key and confirms only one of them reaches the primary\n* Reset the hit/miss counters at any time\n\nAfter starting the server, visit `http://localhost:8080`."},{"id":"the-mock-primary-store","title":"The mock primary store","role":"content","text":"To make the demo self-contained, the example includes a `MockPrimaryStore` that stands in for a slow disk-backed database\n([source](primary.js)):\n\n[code example]\n\nEvery call to `read()` sleeps for `readLatencyMs` so the difference between a cache hit and a miss is obvious in the UI. The store also tracks the total number of primary reads, which the stampede test uses to confirm that single-flight is working — for N concurrent readers against a cold key, you should see exactly one primary read.\n\nIn a real application this would be replaced by a SQL query, an HTTP call to a downstream service, or any other slow-but-authoritative source."},{"id":"production-usage","title":"Production usage","role":"content","text":"This guide uses a deliberately small local demo so you can focus on the cache-aside pattern. In production, you will usually want to harden several aspects of it."},{"id":"choose-a-ttl-that-matches-your-staleness-tolerance","title":"Choose a TTL that matches your staleness tolerance","role":"content","text":"The TTL is the upper bound on how long a stale value can be served. Shorter TTLs mean lower hit rates and more primary load; longer TTLs mean higher hit rates and more stale reads between writes. Pick the value that matches your business tolerance for stale data, and combine it with explicit invalidation on writes for the cases where you cannot tolerate any staleness."},{"id":"invalidate-dont-try-to-keep-the-cache-in-sync","title":"Invalidate, don't try to keep the cache in sync","role":"content","text":"When the underlying record changes, delete the cache key rather than rewriting it. Cache-aside is robust precisely because it never assumes the cache holds the latest value — the next read always re-fetches from the primary on a miss."},{"id":"handle-missing-records-explicitly","title":"Handle missing records explicitly","role":"content","text":"In this demo, a missing record returns `null` and nothing is cached. In a real system you may want to cache \"not found\" sentinels with a short TTL to absorb load from probing for non-existent IDs, while making sure the sentinel is shorter than the positive cache entry so a newly-created record becomes visible quickly."},{"id":"tune-the-single-flight-lock-ttl","title":"Tune the single-flight lock TTL","role":"content","text":"The lock TTL needs to be longer than the worst-case primary read latency so a slow loader does not lose the lock midway. The unique token in `RELEASE_LOCK_SCRIPT` ensures the original caller does not delete someone else's lock if its lock has expired."},{"id":"namespace-cache-keys-in-shared-redis-deployments","title":"Namespace cache keys in shared Redis deployments","role":"content","text":"If multiple applications share a Redis deployment, prefix cache keys with the application name (`cache:billing:product:{id}`) so different services cannot clobber each other's entries."},{"id":"inspect-cached-entries-directly-in-redis","title":"Inspect cached entries directly in Redis","role":"content","text":"When testing or troubleshooting, inspect the stored cache key directly to confirm the application is writing the fields and TTL you expect:\n\n[code example]"},{"id":"learn-more","title":"Learn more","role":"related","text":"* [node-redis guide](https://redis.io/docs/latest/develop/clients/nodejs) - Install and use the Node.js Redis client\n* [SET command](https://redis.io/docs/latest/commands/set) - Set a string with TTL options (`EX`, `PX`, `NX`)\n* [HSET command](https://redis.io/docs/latest/commands/hset) - Write hash fields\n* [HGETALL command](https://redis.io/docs/latest/commands/hgetall) - Read every field of a hash\n* [EXPIRE command](https://redis.io/docs/latest/commands/expire) - Set key expiration in seconds\n* [DEL command](https://redis.io/docs/latest/commands/del) - Delete a key on invalidation\n* [Lua scripting](https://redis.io/docs/latest/develop/programmability/eval-intro) - Atomic single-flight locks and stampede mitigation"}],"examples":[{"id":"the-cache-aside-helper-ex0","language":"javascript","code":"const { createClient } = require(\"redis\");\nconst { RedisCache } = require(\"./cache\");\nconst { MockPrimaryStore } = require(\"./primary\");\n\nconst client = createClient({ socket: { host: \"localhost\", port: 6379 } });\nawait client.connect();\n\nconst primary = new MockPrimaryStore({ readLatencyMs: 150 });\nconst cache = new RedisCache({ redisClient: client, ttl: 30 });\n\n// Read through the cache.\nconst { record, hit, redisLatencyMs } = await cache.get(\"p-001\", (id) => primary.read(id));\n\n// Update a single field without rewriting the whole record.\nawait cache.updateField(\"p-001\", \"stock\", \"41\");\n\n// Invalidate the cache key on a write to the primary.\nprimary.updateField(\"p-001\", \"price_cents\", \"699\");\nawait cache.invalidate(\"p-001\");","section_id":"the-cache-aside-helper"},{"id":"data-model-ex0","language":"text","code":"cache:product:p-001\n  id          = p-001\n  name        = Sourdough Loaf\n  price_cents = 650\n  stock       = 42","section_id":"data-model"},{"id":"cache-aside-reads-ex0","language":"javascript","code":"async get(entityId, loader) {\n  const cacheKey = this._cacheKey(entityId);\n\n  const started = process.hrtime.bigint();\n  const cached = await this.redis.hGetAll(cacheKey);\n  const redisLatencyMs = Number(process.hrtime.bigint() - started) / 1e6;\n\n  if (cached && Object.keys(cached).length > 0) {\n    this._stats.hits += 1;\n    return { record: cached, hit: true, redisLatencyMs };\n  }\n\n  this._stats.misses += 1;\n  const record = await this._loadWithSingleFlight(entityId, loader);\n  return { record, hit: false, redisLatencyMs };\n}","section_id":"cache-aside-reads"},{"id":"stampede-protection-with-a-lua-lock-ex0","language":"lua","code":"-- Acquire a short-lived lock with SET NX PX. Returns 1 on acquire, 0 otherwise.\nif redis.call('SET', KEYS[1], ARGV[1], 'NX', 'PX', ARGV[2]) then\n    return 1\nend\nreturn 0","section_id":"stampede-protection-with-a-lua-lock"},{"id":"stampede-protection-with-a-lua-lock-ex1","language":"lua","code":"if redis.call('GET', KEYS[1]) == ARGV[1] then\n    return redis.call('DEL', KEYS[1])\nend\nreturn 0","section_id":"stampede-protection-with-a-lua-lock"},{"id":"stampede-protection-with-a-lua-lock-ex2","language":"javascript","code":"async _loadWithSingleFlight(entityId, loader) {\n  const cacheKey = this._cacheKey(entityId);\n  const lockKey = this._lockKey(entityId);\n  const token = randomBytes(8).toString(\"hex\");\n\n  const acquired = await this.redis.eval(ACQUIRE_LOCK_SCRIPT, {\n    keys: [lockKey],\n    arguments: [token, String(this.lockTtlMs)],\n  });\n\n  if (acquired === 1) {\n    try {\n      const record = await loader(entityId);\n      if (record == null) return null;\n      const multi = this.redis.multi();\n      multi.del(cacheKey);\n      multi.hSet(cacheKey, record);\n      multi.expire(cacheKey, this.ttl);\n      await multi.exec();\n      return record;\n    } finally {\n      await this.redis.eval(RELEASE_LOCK_SCRIPT, {\n        keys: [lockKey],\n        arguments: [token],\n      });\n    }\n  }\n\n  this._stats.stampedesSuppressed += 1;\n  const deadline = Date.now() + this.lockTtlMs;\n  while (Date.now() < deadline) {\n    await new Promise((resolve) => setTimeout(resolve, this.waitPollMs));\n    const cached = await this.redis.hGetAll(cacheKey);\n    if (cached && Object.keys(cached).length > 0) return cached;\n  }\n  return loader(entityId);\n}","section_id":"stampede-protection-with-a-lua-lock"},{"id":"invalidation-on-write-ex0","language":"javascript","code":"async invalidate(entityId) {\n  const deleted = await this.redis.del(this._cacheKey(entityId));\n  return deleted === 1;\n}","section_id":"invalidation-on-write"},{"id":"field-level-updates-ex0","language":"javascript","code":"async updateField(entityId, field, value) {\n  const cacheKey = this._cacheKey(entityId);\n  while (true) {\n    await this.redis.watch(cacheKey);\n    const exists = await this.redis.exists(cacheKey);\n    if (!exists) {\n      await this.redis.unwatch();\n      return false;\n    }\n    const result = await this.redis\n      .multi()\n      .hSet(cacheKey, field, String(value))\n      .expire(cacheKey, this.ttl)\n      .exec();\n    if (result === null) continue;\n    return true;\n  }\n}","section_id":"field-level-updates"},{"id":"hitmiss-accounting-ex0","language":"javascript","code":"stats() {\n  const total = this._stats.hits + this._stats.misses;\n  const hitRate = total > 0 ? Math.round((1000 * this._stats.hits) / total) / 10 : 0;\n  return {\n    hits: this._stats.hits,\n    misses: this._stats.misses,\n    stampedes_suppressed: this._stats.stampedesSuppressed,\n    hit_rate_pct: hitRate,\n  };\n}","section_id":"hitmiss-accounting"},{"id":"prerequisites-ex0","language":"bash","code":"npm install redis","section_id":"prerequisites"},{"id":"running-the-demo-ex0","language":"bash","code":"node demoServer.js","section_id":"running-the-demo"},{"id":"the-mock-primary-store-ex0","language":"javascript","code":"class MockPrimaryStore {\n  constructor({ readLatencyMs = 150 } = {}) {\n    this.readLatencyMs = readLatencyMs;\n    ...\n  }\n\n  async read(id) {\n    await new Promise((r) => setTimeout(r, this.readLatencyMs));\n    ...\n  }\n}","section_id":"the-mock-primary-store"},{"id":"inspect-cached-entries-directly-in-redis-ex0","language":"bash","code":"redis-cli HGETALL cache:product:p-001\nredis-cli TTL cache:product:p-001","section_id":"inspect-cached-entries-directly-in-redis"}]}
{"schema_version":2,"id":"develop/use-cases/cache-aside/php","title":"Redis cache-aside with Predis","url":"https://redis.io/docs/latest/develop/use-cases/cache-aside/php/","summary":"Implement a Redis cache-aside layer in PHP with Predis","aliases":["/develop/use-cases/cache-aside/predis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:50:30+01:00","children":[],"page_type":"content","content_hash":"a81ee8c19a91b9e6b735cd1c9f1a74c26bb5ea6a580a6414fcd65e4abc8a2650","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis cache-aside layer in PHP with the [Predis](https://github.com/predis/predis) client library. It includes a small local web server built on PHP's built-in development server so you can see cache hits, misses, invalidation on write, and stampede protection in action."},{"id":"overview","title":"Overview","role":"overview","text":"Cache-aside is one of the most common Redis use cases for read-heavy applications. Instead of querying the primary database on every request, the application checks Redis first and only falls back to the primary on a miss. The result is written back to Redis with a TTL so the next read is served from memory.\n\nThat gives you:\n\n* Sub-millisecond reads for the hot working set\n* Bounded staleness — every entry expires within a known window\n* Reduced primary database load proportional to hit rate\n* Field-level updates without re-serializing the full record\n* Protection against cache stampedes when popular keys expire under load\n\nIn this example, each cached product is stored as a Redis hash under a key like `cache:product:{id}`. The hash holds the product fields (`id`, `name`, `price_cents`, `stock`) and the key has a TTL so stale data is bounded automatically."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow on every read looks like this:\n\n1. The application calls `$cache->get($productId, fn($id) => $primary->read($id))`\n2. The helper runs `HGETALL` against `cache:product:{id}`\n3. On a hit, the cached hash is returned directly\n4. On a miss, the helper acquires a Lua-backed single-flight lock and invokes the loader to fetch from the primary\n5. The helper writes the result back to Redis with `HSET` plus `EXPIRE` and releases the lock\n6. Concurrent worker processes that fail to acquire the lock wait briefly for the cache to populate, then return that value instead of issuing their own primary read\n\nOn a write, the application updates the primary and then deletes the cache key, so the next read repopulates from the new source value."},{"id":"state-across-stateless-requests","title":"State across stateless requests","role":"content","text":"PHP requests do not share process memory, so the demo keeps everything that needs to persist between requests in Redis itself:\n\n* Hit, miss, and stampede counters live in the `demo:cache_stats` hash, incremented atomically with `HINCRBY`.\n* The mock primary's read counter and per-record state live under `demo:primary:*` keys.\n\nThis is a cache-aside pattern detail rather than a Redis one — the same helper would use process-local counters in a long-running PHP-FPM or Roadrunner deployment, but the public method surface stays the same."},{"id":"the-cache-aside-helper","title":"The cache-aside helper","role":"content","text":"The `RedisCache` class wraps the cache-aside operations\n([source](Cache.php)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each cached product is stored in a Redis hash:\n\n[code example]\n\nThe implementation uses:\n\n* [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) to read the cached record\n* [`HMSET`](https://redis.io/docs/latest/commands/hmset) plus [`EXPIRE`](https://redis.io/docs/latest/commands/expire) to repopulate after a miss\n* [`DEL`](https://redis.io/docs/latest/commands/del) to invalidate on writes\n* [`TTL`](https://redis.io/docs/latest/commands/ttl) to surface remaining staleness in the demo UI\n* [`EVAL`](https://redis.io/docs/latest/commands/eval) for the Lua single-flight lock that prevents stampedes\n* [`WATCH`](https://redis.io/docs/latest/commands/watch)/[`MULTI`](https://redis.io/docs/latest/commands/multi)/[`EXEC`](https://redis.io/docs/latest/commands/exec) for the conditional field update path\n* [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby) for the cross-request hit/miss counters"},{"id":"cache-aside-reads","title":"Cache-aside reads","role":"content","text":"The `get()` method runs `HGETALL` on the cache key first. On a hit it returns the cached hash and increments the hit counter. On a miss, it delegates to a single-flight loader:\n\n[code example]\n\nThe returned array includes the measured Redis round-trip time so the demo UI can show the latency difference between a hit and a miss."},{"id":"stampede-protection-with-a-lua-lock","title":"Stampede protection with a Lua lock","role":"content","text":"When a popular key expires, every concurrent reader observes the miss at the same instant. Without coordination, all of them would query the primary and overwrite the cache redundantly — a *cache stampede*.\n\nThe helper uses a tiny Lua script to acquire a short-lived lock atomically. Only the caller that wins the `SET NX` becomes the primary loader; the rest poll the cache briefly and return the value the lock holder writes:\n\n[code example]\n\nA second script releases the lock only if the caller still owns it, so a lock that timed out and was re-acquired by someone else cannot be released by mistake:\n\n[code example]\n\nThe PHP side runs both scripts via `EVAL` on every miss:\n\n[code example]\n\nThe unique `token` per caller is what makes the release script safe — only the caller that actually holds the lock can release it."},{"id":"invalidation-on-write","title":"Invalidation on write","role":"content","text":"When a write hits the primary, the application invalidates the cache key. The next read pulls fresh data from the primary:\n\n[code example]\n\nThis is the simplest and safest pattern: never try to keep the cache and primary in sync directly, just delete the cache entry and let the next read repopulate it."},{"id":"field-level-updates","title":"Field-level updates","role":"content","text":"Because each record is stored as a hash, the cache helper can also update a single field in place without re-serializing the full record. The update only writes if the entry is already cached, so a partial record can never appear in Redis:\n\n[code example]\n\nThis is useful for hot fields that change more often than the rest of the record (a stock counter, a view count) and would otherwise force a full reload."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before running the demo, make sure that:\n\n* Redis is running and accessible. By default, the demo connects to `localhost:6379`.\n* PHP 8.1 or later is installed.\n* The Predis library is installed via Composer:\n\n[code example]\n\nIf your Redis server is running elsewhere, set `REDIS_HOST` and `REDIS_PORT` in the environment before starting the demo."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":"A local demo server is included to show the cache-aside layer in action\n([source](demo_server.php)):\n\n[code example]\n\nThe demo server uses PHP's built-in development server plus a separate worker script (`stampede_worker.php`) for the concurrent stampede test. The PHP CLI server is single-threaded, so the demo launches independent PHP processes via `proc_open` to get genuine parallelism for that test.\n\nIt exposes a small interactive page where you can:\n\n* Read a product through the cache and see whether it was a hit or a miss\n* Compare the measured Redis round-trip against the simulated primary read latency\n* Watch the cache TTL count down between requests\n* Update a field on the primary and see the cache invalidate automatically\n* Run a stampede test that spawns N concurrent worker processes against a freshly-invalidated key and confirms only one of them reaches the primary\n* Reset the hit/miss counters at any time\n\nAfter starting the server, visit `http://localhost:8080`."},{"id":"the-mock-primary-store","title":"The mock primary store","role":"content","text":"To make the demo self-contained, the example includes a `MockPrimaryStore` that stands in for a slow disk-backed database\n([source](Primary.php)):\n\n[code example]\n\nEvery call to `read()` sleeps for `$readLatencyMs` so the difference between a cache hit and a miss is obvious in the UI. Records are seeded into Redis under `demo:primary:product:{id}` so updates and the read counter survive between requests.\n\nFor the stampede test, an N-worker burst against a cold key should produce exactly one increment of `demo:primary:reads`, confirming that single-flight is working.\n\nIn a real application this would be replaced by a SQL query, an HTTP call to a downstream service, or any other slow-but-authoritative source."},{"id":"production-usage","title":"Production usage","role":"content","text":"This guide uses a deliberately small local demo so you can focus on the cache-aside pattern. In production, you will usually want to harden several aspects of it."},{"id":"choose-a-ttl-that-matches-your-staleness-tolerance","title":"Choose a TTL that matches your staleness tolerance","role":"content","text":"The TTL is the upper bound on how long a stale value can be served. Shorter TTLs mean lower hit rates and more primary load; longer TTLs mean higher hit rates and more stale reads between writes. Pick the value that matches your business tolerance for stale data, and combine it with explicit invalidation on writes for the cases where you cannot tolerate any staleness."},{"id":"invalidate-dont-try-to-keep-the-cache-in-sync","title":"Invalidate, don't try to keep the cache in sync","role":"content","text":"When the underlying record changes, delete the cache key rather than rewriting it. Cache-aside is robust precisely because it never assumes the cache holds the latest value — the next read always re-fetches from the primary on a miss."},{"id":"handle-missing-records-explicitly","title":"Handle missing records explicitly","role":"content","text":"In this demo, a missing record returns `null` and nothing is cached. In a real system you may want to cache \"not found\" sentinels with a short TTL to absorb load from probing for non-existent IDs, while making sure the sentinel TTL is shorter than the positive cache entry so a newly-created record becomes visible quickly."},{"id":"tune-the-single-flight-lock-ttl","title":"Tune the single-flight lock TTL","role":"content","text":"The lock TTL needs to be longer than the worst-case primary read latency so a slow loader does not lose the lock midway. The unique token in `RELEASE_LOCK_SCRIPT` ensures the original caller does not delete someone else's lock if its lock has expired."},{"id":"use-a-long-running-php-runtime-in-production","title":"Use a long-running PHP runtime in production","role":"content","text":"The PHP built-in server used here is fine for local development, but in production you will typically run PHP under PHP-FPM, Apache mod_php, FrankenPHP, or Roadrunner. The cache-aside helper code is unchanged, but you can move the hit/miss counters into shared memory (APCu, Roadrunner KV) instead of Redis if you want to avoid the per-request increment."},{"id":"namespace-cache-keys-in-shared-redis-deployments","title":"Namespace cache keys in shared Redis deployments","role":"content","text":"If multiple applications share a Redis deployment, prefix cache keys with the application name (`cache:billing:product:{id}`) so different services cannot clobber each other's entries."},{"id":"inspect-cached-entries-directly-in-redis","title":"Inspect cached entries directly in Redis","role":"content","text":"When testing or troubleshooting, inspect the stored cache key directly to confirm the application is writing the fields and TTL you expect:\n\n[code example]"},{"id":"learn-more","title":"Learn more","role":"related","text":"* [Predis on GitHub](https://github.com/predis/predis) - Install and use the PHP Redis client\n* [SET command](https://redis.io/docs/latest/commands/set) - Set a string with TTL options (`EX`, `PX`, `NX`)\n* [HSET command](https://redis.io/docs/latest/commands/hset) - Write hash fields\n* [HGETALL command](https://redis.io/docs/latest/commands/hgetall) - Read every field of a hash\n* [EXPIRE command](https://redis.io/docs/latest/commands/expire) - Set key expiration in seconds\n* [DEL command](https://redis.io/docs/latest/commands/del) - Delete a key on invalidation\n* [Lua scripting](https://redis.io/docs/latest/develop/programmability/eval-intro) - Atomic single-flight locks and stampede mitigation"}],"examples":[{"id":"the-cache-aside-helper-ex0","language":"php","code":"require_once 'vendor/autoload.php';\nrequire_once 'Cache.php';\nrequire_once 'Primary.php';\n\nuse Predis\\Client as PredisClient;\n\n$redis = new PredisClient(['host' => 'localhost', 'port' => 6379]);\n$primary = new MockPrimaryStore($redis, readLatencyMs: 150);\n$cache = new RedisCache($redis, ttl: 30);\n\n// Read through the cache.\n$result = $cache->get('p-001', fn(string $id) => $primary->read($id));\necho \"hit={$result['hit']} latency={$result['redis_latency_ms']} ms\\n\";\n\n// Update a single field without rewriting the whole record.\n$cache->updateField('p-001', 'stock', '41');\n\n// Invalidate the cache key on a write to the primary.\n$primary->updateField('p-001', 'price_cents', '699');\n$cache->invalidate('p-001');","section_id":"the-cache-aside-helper"},{"id":"data-model-ex0","language":"text","code":"cache:product:p-001\n  id          = p-001\n  name        = Sourdough Loaf\n  price_cents = 650\n  stock       = 42","section_id":"data-model"},{"id":"cache-aside-reads-ex0","language":"php","code":"public function get(string $entityId, callable $loader): array\n{\n    $cacheKey = $this->cacheKey($entityId);\n\n    $started = self::monotonicMs();\n    $cached = $this->redis->hgetall($cacheKey);\n    $redisLatencyMs = self::monotonicMs() - $started;\n\n    if (is_array($cached) && count($cached) > 0) {\n        $this->recordHit();\n        return ['record' => $cached, 'hit' => true, 'redis_latency_ms' => $redisLatencyMs];\n    }\n\n    $this->recordMiss();\n    $record = $this->loadWithSingleFlight($entityId, $loader);\n    return ['record' => $record, 'hit' => false, 'redis_latency_ms' => $redisLatencyMs];\n}","section_id":"cache-aside-reads"},{"id":"stampede-protection-with-a-lua-lock-ex0","language":"lua","code":"-- Acquire a short-lived lock with SET NX PX. Returns 1 on acquire, 0 otherwise.\nif redis.call('SET', KEYS[1], ARGV[1], 'NX', 'PX', ARGV[2]) then\n    return 1\nend\nreturn 0","section_id":"stampede-protection-with-a-lua-lock"},{"id":"stampede-protection-with-a-lua-lock-ex1","language":"lua","code":"if redis.call('GET', KEYS[1]) == ARGV[1] then\n    return redis.call('DEL', KEYS[1])\nend\nreturn 0","section_id":"stampede-protection-with-a-lua-lock"},{"id":"stampede-protection-with-a-lua-lock-ex2","language":"php","code":"private function loadWithSingleFlight(string $entityId, callable $loader): ?array\n{\n    $cacheKey = $this->cacheKey($entityId);\n    $lockKey = $this->lockKey($entityId);\n    $token = bin2hex(random_bytes(8));\n\n    $acquired = (int) $this->redis->eval(\n        self::ACQUIRE_LOCK_SCRIPT, 1, $lockKey, $token, (string) $this->lockTtlMs\n    );\n\n    if ($acquired === 1) {\n        try {\n            $record = $loader($entityId);\n            if ($record === null) return null;\n            $this->redis->transaction(function ($pipe) use ($cacheKey, $record): void {\n                $pipe->del([$cacheKey]);\n                $pipe->hmset($cacheKey, $record);\n                $pipe->expire($cacheKey, $this->ttl);\n            });\n            return $record;\n        } finally {\n            $this->redis->eval(self::RELEASE_LOCK_SCRIPT, 1, $lockKey, $token);\n        }\n    }\n\n    $this->recordStampedeSuppressed();\n    $deadline = self::monotonicMs() + $this->lockTtlMs;\n    while (self::monotonicMs() < $deadline) {\n        usleep($this->waitPollMs * 1000);\n        $cached = $this->redis->hgetall($cacheKey);\n        if (is_array($cached) && count($cached) > 0) return $cached;\n    }\n    return $loader($entityId);\n}","section_id":"stampede-protection-with-a-lua-lock"},{"id":"invalidation-on-write-ex0","language":"php","code":"public function invalidate(string $entityId): bool\n{\n    return ((int) $this->redis->del([$this->cacheKey($entityId)])) === 1;\n}","section_id":"invalidation-on-write"},{"id":"field-level-updates-ex0","language":"php","code":"public function updateField(string $entityId, string $field, string $value): bool\n{\n    $cacheKey = $this->cacheKey($entityId);\n    while (true) {\n        $this->redis->watch($cacheKey);\n        if ((int) $this->redis->exists($cacheKey) === 0) {\n            $this->redis->unwatch();\n            return false;\n        }\n        $result = $this->redis->transaction(function ($pipe) use ($cacheKey, $field, $value): void {\n            $pipe->hset($cacheKey, $field, $value);\n            $pipe->expire($cacheKey, $this->ttl);\n        });\n        if ($result !== null) {\n            return true;\n        }\n        // null means WATCH detected a change — retry.\n    }\n}","section_id":"field-level-updates"},{"id":"prerequisites-ex0","language":"bash","code":"composer require predis/predis","section_id":"prerequisites"},{"id":"running-the-demo-ex0","language":"bash","code":"php -S localhost:8080 demo_server.php","section_id":"running-the-demo"},{"id":"the-mock-primary-store-ex0","language":"php","code":"public function read(string $id): ?array\n{\n    usleep($this->readLatencyMs * 1000);\n    $this->redis->incr(self::READS_KEY);\n    $record = $this->redis->hgetall(self::RECORDS_KEY_PREFIX . $id);\n    return is_array($record) && count($record) > 0 ? $record : null;\n}","section_id":"the-mock-primary-store"},{"id":"inspect-cached-entries-directly-in-redis-ex0","language":"bash","code":"redis-cli HGETALL cache:product:p-001\nredis-cli TTL cache:product:p-001","section_id":"inspect-cached-entries-directly-in-redis"}]}
{"schema_version":2,"id":"develop/use-cases/cache-aside/redis-py","title":"Redis cache-aside with redis-py","url":"https://redis.io/docs/latest/develop/use-cases/cache-aside/redis-py/","summary":"Implement a Redis cache-aside layer in Python with redis-py","aliases":["/develop/use-cases/cache-aside/python"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:50:30+01:00","children":[],"page_type":"content","content_hash":"a4298348904653323c46c7bcac246b49615db5af2fc89a82191ab1c8c9773c14","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis cache-aside layer in Python with [`redis-py`](https://redis.io/docs/latest/develop/clients/redis-py). It includes a small local web server built with the Python standard library so you can see cache hits, misses, invalidation on write, and stampede protection in action."},{"id":"overview","title":"Overview","role":"overview","text":"Cache-aside is one of the most common Redis use cases for read-heavy applications. Instead of querying the primary database on every request, the application checks Redis first and only falls back to the primary on a miss. The result is written back to Redis with a TTL so the next read is served from memory.\n\nThat gives you:\n\n* Sub-millisecond reads for the hot working set\n* Bounded staleness — every entry expires within a known window\n* Reduced primary database load proportional to hit rate\n* Field-level updates without re-serializing the full record\n* Protection against cache stampedes when popular keys expire under load\n\nIn this example, each cached product is stored as a Redis hash under a key like `cache:product:{id}`. The hash holds the product fields (`id`, `name`, `price_cents`, `stock`) and the key has a TTL so stale data is bounded automatically."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow on every read looks like this:\n\n1. The application calls `cache.get(product_id, loader)`\n2. The helper runs `HGETALL` against `cache:product:{id}`\n3. On a hit, the cached hash is returned directly\n4. On a miss, the helper acquires a Lua-backed single-flight lock and calls `loader(product_id)` to read the primary\n5. The helper writes the result back to Redis with `HSET` plus `EXPIRE` and releases the lock\n6. Concurrent callers that fail to acquire the lock wait briefly for the cache to populate, then return that value instead of issuing their own primary read\n\nOn a write, the application updates the primary and then deletes the cache key, so the next read repopulates from the new source value."},{"id":"the-cache-aside-helper","title":"The cache-aside helper","role":"content","text":"The `RedisCache` class wraps the cache-aside operations\n([source](cache.py)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each cached product is stored in a Redis hash:\n\n[code example]\n\nThe implementation uses:\n\n* [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) to read the cached record\n* [`HSET`](https://redis.io/docs/latest/commands/hset) plus [`EXPIRE`](https://redis.io/docs/latest/commands/expire) to repopulate after a miss\n* [`DEL`](https://redis.io/docs/latest/commands/del) to invalidate on writes\n* [`TTL`](https://redis.io/docs/latest/commands/ttl) to surface remaining staleness in the demo UI\n* [`EVAL`](https://redis.io/docs/latest/commands/eval) for the Lua single-flight lock that prevents stampedes\n* [`WATCH`](https://redis.io/docs/latest/commands/watch)/[`MULTI`](https://redis.io/docs/latest/commands/multi)/[`EXEC`](https://redis.io/docs/latest/commands/exec) for the conditional field update path"},{"id":"cache-aside-reads","title":"Cache-aside reads","role":"content","text":"The `get()` method runs `HGETALL` on the cache key first. On a hit it returns the cached hash and records the hit in an in-process counter. On a miss, it delegates to a single-flight loader:\n\n[code example]\n\nThe return tuple includes the measured Redis round-trip time so the demo UI can show the latency difference between a hit and a miss."},{"id":"stampede-protection-with-a-lua-lock","title":"Stampede protection with a Lua lock","role":"content","text":"When a popular key expires, every concurrent reader observes the miss at the same instant. Without coordination, all of them would query the primary and overwrite the cache redundantly — a *cache stampede*.\n\nThe helper uses a tiny Lua script to acquire a short-lived lock atomically. Only the caller that wins the `SET NX` becomes the primary loader; the rest poll the cache briefly and return the value the lock holder writes:\n\n[code example]\n\nA second script releases the lock only if the caller still owns it, so a lock that timed out and was re-acquired by someone else cannot be released by mistake:\n\n[code example]\n\nThe Python side wires both scripts up through `register_script` and runs them on every miss:\n\n[code example]\n\nThe unique `token` per caller is what makes the release script safe — only the caller that actually holds the lock can release it."},{"id":"invalidation-on-write","title":"Invalidation on write","role":"content","text":"When a write hits the primary, the application invalidates the cache key. The next read pulls fresh data from the primary:\n\n[code example]\n\nThis is the simplest and safest pattern: never try to keep the cache and primary in sync directly, just delete the cache entry and let the next read repopulate it."},{"id":"field-level-updates","title":"Field-level updates","role":"content","text":"Because each record is stored as a hash, the cache helper can also update a single field in place without re-serializing the full record. The update only writes if the entry is already cached, so a partial record can never appear in Redis:\n\n[code example]\n\nThis is useful for hot fields that change more often than the rest of the record (a stock counter, a view count) and would otherwise force a full reload."},{"id":"hitmiss-accounting","title":"Hit/miss accounting","role":"content","text":"The helper keeps in-process counters for hits, misses, and stampedes that were suppressed by the single-flight lock. The demo UI surfaces these so you can see the cache absorbing load:\n\n[code example]\n\nIn production you would emit these as Prometheus counters or push them into your metrics pipeline rather than holding them in process memory."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before running the demo, make sure that:\n\n* Redis is running and accessible. By default, the demo connects to `localhost:6379`.\n* The `redis` Python package is installed:\n\n[code example]\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":"A local demo server is included to show the cache-aside layer in action\n([source](demo_server.py)):\n\n[code example]\n\nThe demo server uses only Python standard library features for HTTP handling and concurrency:\n\n* [`http.server`](https://docs.python.org/3/library/http.server.html) for the web server\n* [`urllib.parse`](https://docs.python.org/3/library/urllib.parse.html) for query and form decoding\n* [`concurrent.futures`](https://docs.python.org/3/library/concurrent.futures.html) for the stampede test thread pool\n\nIt exposes a small interactive page where you can:\n\n* Read a product through the cache and see whether it was a hit or a miss\n* Compare the measured Redis round-trip against the simulated primary read latency\n* Watch the cache TTL count down between requests\n* Update a field on the primary and see the cache invalidate automatically\n* Run a stampede test that fires many concurrent reads at a freshly-invalidated key and confirms only one of them reaches the primary\n* Reset the hit/miss counters at any time\n\nAfter starting the server, visit `http://localhost:8080`."},{"id":"the-mock-primary-store","title":"The mock primary store","role":"content","text":"To make the demo self-contained, the example includes a `MockPrimaryStore` that stands in for a slow disk-backed database\n([source](primary.py)):\n\n[code example]\n\nEvery call to `read()` sleeps for `read_latency_ms` so the difference between a cache hit and a miss is obvious in the UI. The store also tracks the total number of primary reads, which the stampede test uses to confirm that single-flight is working — for N concurrent readers against a cold key, you should see exactly one primary read.\n\nIn a real application this would be replaced by a SQL query, an HTTP call to a downstream service, or any other slow-but-authoritative source."},{"id":"production-usage","title":"Production usage","role":"content","text":"This guide uses a deliberately small local demo so you can focus on the cache-aside pattern. In production, you will usually want to harden several aspects of it."},{"id":"choose-a-ttl-that-matches-your-staleness-tolerance","title":"Choose a TTL that matches your staleness tolerance","role":"content","text":"The TTL is the upper bound on how long a stale value can be served. Shorter TTLs mean lower hit rates and more primary load; longer TTLs mean higher hit rates and more stale reads between writes. Pick the value that matches your business tolerance for stale data, and combine it with explicit invalidation on writes for the cases where you cannot tolerate any staleness."},{"id":"invalidate-dont-try-to-keep-the-cache-in-sync","title":"Invalidate, don't try to keep the cache in sync","role":"content","text":"When the underlying record changes, delete the cache key rather than rewriting it. Cache-aside is robust precisely because it never assumes the cache holds the latest value — the next read always re-fetches from the primary on a miss. Trying to keep the cache and primary in sync from the application opens the door to subtle ordering bugs."},{"id":"handle-missing-records-explicitly","title":"Handle missing records explicitly","role":"content","text":"In this demo, a missing record returns `None` and nothing is cached. In a real system you may want to cache \"not found\" sentinels with a short TTL to absorb load from probing for non-existent IDs, while making sure the sentinel is shorter than the positive cache entry so a newly-created record becomes visible quickly."},{"id":"tune-the-single-flight-lock-ttl","title":"Tune the single-flight lock TTL","role":"content","text":"The lock TTL needs to be longer than the worst-case primary read latency so a slow loader does not lose the lock midway. If the loader exceeds the lock TTL, the lock can be re-acquired by another caller — the unique token in `RELEASE_LOCK_SCRIPT` ensures the original caller does not delete someone else's lock, but it can still cause a redundant primary read."},{"id":"namespace-cache-keys-in-shared-redis-deployments","title":"Namespace cache keys in shared Redis deployments","role":"content","text":"If multiple applications share a Redis deployment, prefix cache keys with the application name (`cache:billing:product:{id}`) so different services cannot clobber each other's entries."},{"id":"inspect-cached-entries-directly-in-redis","title":"Inspect cached entries directly in Redis","role":"content","text":"When testing or troubleshooting, inspect the stored cache key directly to confirm the application is writing the fields and TTL you expect:\n\n[code example]"},{"id":"learn-more","title":"Learn more","role":"related","text":"* [redis-py guide](https://redis.io/docs/latest/develop/clients/redis-py) - Install and use the Python Redis client\n* [SET command](https://redis.io/docs/latest/commands/set) - Set a string with TTL options (`EX`, `PX`, `NX`)\n* [HSET command](https://redis.io/docs/latest/commands/hset) - Write hash fields\n* [HGETALL command](https://redis.io/docs/latest/commands/hgetall) - Read every field of a hash\n* [EXPIRE command](https://redis.io/docs/latest/commands/expire) - Set key expiration in seconds\n* [DEL command](https://redis.io/docs/latest/commands/del) - Delete a key on invalidation\n* [Lua scripting](https://redis.io/docs/latest/develop/programmability/eval-intro) - Atomic single-flight locks and stampede mitigation"}],"examples":[{"id":"the-cache-aside-helper-ex0","language":"python","code":"import redis\nfrom cache import RedisCache\nfrom primary import MockPrimaryStore\n\nr = redis.Redis(host=\"localhost\", port=6379, decode_responses=True)\nprimary = MockPrimaryStore(read_latency_ms=150)\ncache = RedisCache(redis_client=r, ttl=30)\n\n# Read through the cache.\nrecord, hit, redis_ms = cache.get(\"p-001\", primary.read)\n\n# Update a single field without rewriting the whole record.\ncache.update_field(\"p-001\", \"stock\", \"41\")\n\n# Invalidate the cache key on a write to the primary.\nprimary.update_field(\"p-001\", \"price_cents\", \"699\")\ncache.invalidate(\"p-001\")","section_id":"the-cache-aside-helper"},{"id":"data-model-ex0","language":"text","code":"cache:product:p-001\n  id          = p-001\n  name        = Sourdough Loaf\n  price_cents = 650\n  stock       = 42","section_id":"data-model"},{"id":"cache-aside-reads-ex0","language":"python","code":"def get(\n    self,\n    entity_id: str,\n    loader: Callable[[str], Optional[dict[str, str]]],\n) -> tuple[Optional[dict[str, str]], bool, float]:\n    cache_key = self._cache_key(entity_id)\n\n    started = time.perf_counter()\n    cached = self.redis.hgetall(cache_key)\n    redis_latency_ms = (time.perf_counter() - started) * 1000.0\n\n    if cached:\n        with self._stats_lock:\n            self._hits += 1\n        return cached, True, redis_latency_ms\n\n    with self._stats_lock:\n        self._misses += 1\n\n    return self._load_with_single_flight(entity_id, loader), False, redis_latency_ms","section_id":"cache-aside-reads"},{"id":"stampede-protection-with-a-lua-lock-ex0","language":"lua","code":"-- Acquire a short-lived lock with SET NX PX. Returns 1 on acquire, 0 otherwise.\nif redis.call('SET', KEYS[1], ARGV[1], 'NX', 'PX', ARGV[2]) then\n    return 1\nend\nreturn 0","section_id":"stampede-protection-with-a-lua-lock"},{"id":"stampede-protection-with-a-lua-lock-ex1","language":"lua","code":"if redis.call('GET', KEYS[1]) == ARGV[1] then\n    return redis.call('DEL', KEYS[1])\nend\nreturn 0","section_id":"stampede-protection-with-a-lua-lock"},{"id":"stampede-protection-with-a-lua-lock-ex2","language":"python","code":"def _load_with_single_flight(\n    self,\n    entity_id: str,\n    loader: Callable[[str], Optional[dict[str, str]]],\n) -> Optional[dict[str, str]]:\n    cache_key = self._cache_key(entity_id)\n    lock_key = self._lock_key(entity_id)\n    token = secrets.token_hex(8)\n\n    acquired = self._acquire_lock(\n        keys=[lock_key],\n        args=[token, self.lock_ttl_ms],\n    )\n\n    if acquired:\n        try:\n            record = loader(entity_id)\n            if record is None:\n                return None\n            pipe = self.redis.pipeline()\n            pipe.delete(cache_key)\n            pipe.hset(cache_key, mapping=record)\n            pipe.expire(cache_key, self.ttl)\n            pipe.execute()\n            return record\n        finally:\n            self._release_lock(keys=[lock_key], args=[token])\n\n    # Another caller is loading. Poll briefly for the cache to populate.\n    with self._stats_lock:\n        self._stampedes_suppressed += 1\n\n    deadline = time.monotonic() + (self.lock_ttl_ms / 1000.0)\n    while time.monotonic() < deadline:\n        time.sleep(self.wait_poll_ms / 1000.0)\n        cached = self.redis.hgetall(cache_key)\n        if cached:\n            return cached\n\n    return loader(entity_id)","section_id":"stampede-protection-with-a-lua-lock"},{"id":"invalidation-on-write-ex0","language":"python","code":"def invalidate(self, entity_id: str) -> bool:\n    return self.redis.delete(self._cache_key(entity_id)) == 1","section_id":"invalidation-on-write"},{"id":"field-level-updates-ex0","language":"python","code":"def update_field(self, entity_id: str, field: str, value: str) -> bool:\n    cache_key = self._cache_key(entity_id)\n    with self.redis.pipeline() as pipe:\n        while True:\n            try:\n                pipe.watch(cache_key)\n                if not pipe.exists(cache_key):\n                    pipe.unwatch()\n                    return False\n                pipe.multi()\n                pipe.hset(cache_key, field, value)\n                pipe.expire(cache_key, self.ttl)\n                pipe.execute()\n                return True\n            except redis.WatchError:\n                continue","section_id":"field-level-updates"},{"id":"hitmiss-accounting-ex0","language":"python","code":"def stats(self) -> dict[str, int]:\n    with self._stats_lock:\n        total = self._hits + self._misses\n        hit_rate = round(100.0 * self._hits / total, 1) if total else 0.0\n        return {\n            \"hits\": self._hits,\n            \"misses\": self._misses,\n            \"stampedes_suppressed\": self._stampedes_suppressed,\n            \"hit_rate_pct\": hit_rate,\n        }","section_id":"hitmiss-accounting"},{"id":"prerequisites-ex0","language":"bash","code":"pip install redis","section_id":"prerequisites"},{"id":"running-the-demo-ex0","language":"bash","code":"python demo_server.py","section_id":"running-the-demo"},{"id":"the-mock-primary-store-ex0","language":"python","code":"class MockPrimaryStore:\n    def __init__(self, read_latency_ms: int = 150) -> None:\n        self.read_latency_ms = read_latency_ms\n        ...\n\n    def read(self, product_id: str) -> Optional[dict[str, str]]:\n        time.sleep(self.read_latency_ms / 1000.0)\n        ...","section_id":"the-mock-primary-store"},{"id":"inspect-cached-entries-directly-in-redis-ex0","language":"bash","code":"redis-cli HGETALL cache:product:p-001\nredis-cli TTL cache:product:p-001","section_id":"inspect-cached-entries-directly-in-redis"}]}
{"schema_version":2,"id":"develop/use-cases/cache-aside/ruby","title":"Redis cache-aside with redis-rb","url":"https://redis.io/docs/latest/develop/use-cases/cache-aside/ruby/","summary":"Implement a Redis cache-aside layer in Ruby with redis-rb","aliases":["/develop/use-cases/cache-aside/redis-rb"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:50:30+01:00","children":[],"page_type":"content","content_hash":"1daf66c8766c958f93b99adf7be9e126b943ae7dce5e767dbeddc3553d0f41bc","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis cache-aside layer in Ruby with the [`redis-rb`](https://redis.io/docs/latest/develop/clients/ruby) gem. It includes a small local web server built on Ruby's standard `webrick` library so you can see cache hits, misses, invalidation on write, and stampede protection in action."},{"id":"overview","title":"Overview","role":"overview","text":"Cache-aside is one of the most common Redis use cases for read-heavy applications. Instead of querying the primary database on every request, the application checks Redis first and only falls back to the primary on a miss. The result is written back to Redis with a TTL so the next read is served from memory.\n\nThat gives you:\n\n* Sub-millisecond reads for the hot working set\n* Bounded staleness — every entry expires within a known window\n* Reduced primary database load proportional to hit rate\n* Field-level updates without re-serializing the full record\n* Protection against cache stampedes when popular keys expire under load\n\nIn this example, each cached product is stored as a Redis hash under a key like `cache:product:{id}`. The hash holds the product fields (`id`, `name`, `price_cents`, `stock`) and the key has a TTL so stale data is bounded automatically."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow on every read looks like this:\n\n1. The application calls `cache.get(product_id) { |id| primary.read(id) }`\n2. The helper runs `HGETALL` against `cache:product:{id}`\n3. On a hit, the cached hash is returned directly\n4. On a miss, the helper acquires a Lua-backed single-flight lock and yields the loader to fetch from the primary\n5. The helper writes the result back to Redis with `HSET` plus `EXPIRE` and releases the lock\n6. Concurrent threads that fail to acquire the lock wait briefly for the cache to populate, then return that value instead of issuing their own primary read\n\nOn a write, the application updates the primary and then deletes the cache key, so the next read repopulates from the new source value."},{"id":"the-cache-aside-helper","title":"The cache-aside helper","role":"content","text":"The `RedisCache` class wraps the cache-aside operations\n([source](cache.rb)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each cached product is stored in a Redis hash:\n\n[code example]\n\nThe implementation uses:\n\n* [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) to read the cached record\n* [`HSET`](https://redis.io/docs/latest/commands/hset) plus [`EXPIRE`](https://redis.io/docs/latest/commands/expire) to repopulate after a miss\n* [`DEL`](https://redis.io/docs/latest/commands/del) to invalidate on writes\n* [`TTL`](https://redis.io/docs/latest/commands/ttl) to surface remaining staleness in the demo UI\n* [`EVAL`](https://redis.io/docs/latest/commands/eval) for the Lua single-flight lock that prevents stampedes\n* [`WATCH`](https://redis.io/docs/latest/commands/watch)/[`MULTI`](https://redis.io/docs/latest/commands/multi)/[`EXEC`](https://redis.io/docs/latest/commands/exec) for the conditional field update path"},{"id":"cache-aside-reads","title":"Cache-aside reads","role":"content","text":"The `get` method runs `HGETALL` on the cache key first. On a hit it returns the cached hash and records the hit in an in-process counter. On a miss, it delegates to a single-flight loader:\n\n[code example]\n\nThe returned hash includes the measured Redis round-trip time so the demo UI can show the latency difference between a hit and a miss."},{"id":"stampede-protection-with-a-lua-lock","title":"Stampede protection with a Lua lock","role":"content","text":"When a popular key expires, every concurrent reader observes the miss at the same instant. Without coordination, all of them would query the primary and overwrite the cache redundantly — a *cache stampede*.\n\nThe helper uses a tiny Lua script to acquire a short-lived lock atomically. Only the thread that wins the `SET NX` becomes the primary loader; the rest poll the cache briefly and return the value the lock holder writes:\n\n[code example]\n\nA second script releases the lock only if the caller still owns it, so a lock that timed out and was re-acquired by someone else cannot be released by mistake:\n\n[code example]\n\nThe Ruby side runs both scripts via `EVAL` on every miss:\n\n[code example]\n\nThe unique `token` per caller is what makes the release script safe — only the thread that actually holds the lock can release it."},{"id":"invalidation-on-write","title":"Invalidation on write","role":"content","text":"When a write hits the primary, the application invalidates the cache key. The next read pulls fresh data from the primary:\n\n[code example]\n\nThis is the simplest and safest pattern: never try to keep the cache and primary in sync directly, just delete the cache entry and let the next read repopulate it."},{"id":"field-level-updates","title":"Field-level updates","role":"content","text":"Because each record is stored as a hash, the cache helper can also update a single field in place without re-serializing the full record. The update only writes if the entry is already cached, so a partial record can never appear in Redis:\n\n[code example]\n\nThis is useful for hot fields that change more often than the rest of the record (a stock counter, a view count) and would otherwise force a full reload."},{"id":"hitmiss-accounting","title":"Hit/miss accounting","role":"content","text":"The helper keeps in-process counters for hits, misses, and stampedes that were suppressed by the single-flight lock. The demo UI surfaces these so you can see the cache absorbing load:\n\n[code example]\n\nIn production you would emit these as Prometheus counters or push them into your metrics pipeline rather than holding them in process memory."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before running the demo, make sure that:\n\n* Redis is running and accessible. By default, the demo connects to `localhost:6379`.\n* The `redis` gem is installed:\n\n[code example]\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":"A local demo server is included to show the cache-aside layer in action\n([source](demo_server.rb)):\n\n[code example]\n\nThe demo server uses Ruby's standard library for HTTP handling and concurrency:\n\n* [`webrick`](https://github.com/ruby/webrick) for the multi-threaded web server\n* [`uri`](https://docs.ruby-lang.org/en/master/URI.html) for query and form decoding\n* [`securerandom`](https://docs.ruby-lang.org/en/master/SecureRandom.html) for the per-caller lock token\n\nIt exposes a small interactive page where you can:\n\n* Read a product through the cache and see whether it was a hit or a miss\n* Compare the measured Redis round-trip against the simulated primary read latency\n* Watch the cache TTL count down between requests\n* Update a field on the primary and see the cache invalidate automatically\n* Run a stampede test that fires many concurrent reads at a freshly-invalidated key and confirms only one of them reaches the primary\n* Reset the hit/miss counters at any time\n\nAfter starting the server, visit `http://localhost:8080`."},{"id":"the-mock-primary-store","title":"The mock primary store","role":"content","text":"To make the demo self-contained, the example includes a `MockPrimaryStore` that stands in for a slow disk-backed database\n([source](primary.rb)):\n\n[code example]\n\nEvery call to `read` sleeps for `read_latency_ms` so the difference between a cache hit and a miss is obvious in the UI. The store also tracks the total number of primary reads, which the stampede test uses to confirm that single-flight is working — for N concurrent readers against a cold key, you should see exactly one primary read.\n\nIn a real application this would be replaced by an ActiveRecord query, an HTTP call to a downstream service, or any other slow-but-authoritative source."},{"id":"production-usage","title":"Production usage","role":"content","text":"This guide uses a deliberately small local demo so you can focus on the cache-aside pattern. In production, you will usually want to harden several aspects of it."},{"id":"choose-a-ttl-that-matches-your-staleness-tolerance","title":"Choose a TTL that matches your staleness tolerance","role":"content","text":"The TTL is the upper bound on how long a stale value can be served. Shorter TTLs mean lower hit rates and more primary load; longer TTLs mean higher hit rates and more stale reads between writes. Pick the value that matches your business tolerance for stale data, and combine it with explicit invalidation on writes for the cases where you cannot tolerate any staleness."},{"id":"invalidate-dont-try-to-keep-the-cache-in-sync","title":"Invalidate, don't try to keep the cache in sync","role":"content","text":"When the underlying record changes, delete the cache key rather than rewriting it. Cache-aside is robust precisely because it never assumes the cache holds the latest value — the next read always re-fetches from the primary on a miss."},{"id":"handle-missing-records-explicitly","title":"Handle missing records explicitly","role":"content","text":"In this demo, a missing record returns `nil` and nothing is cached. In a real system you may want to cache \"not found\" sentinels with a short TTL to absorb load from probing for non-existent IDs, while making sure the sentinel TTL is shorter than the positive cache entry so a newly-created record becomes visible quickly."},{"id":"tune-the-single-flight-lock-ttl","title":"Tune the single-flight lock TTL","role":"content","text":"The lock TTL needs to be longer than the worst-case primary read latency so a slow loader does not lose the lock midway. The unique token in `RELEASE_LOCK_SCRIPT` ensures the original caller does not delete someone else's lock if its lock has expired."},{"id":"use-a-connection-pool-under-high-concurrency","title":"Use a connection pool under high concurrency","role":"content","text":"The demo shares a single `Redis` client across WEBrick request threads. For high-concurrency production workloads, use a connection pool such as [`connection_pool`](https://github.com/mperham/connection_pool) so that concurrent threads do not contend on a single TCP connection."},{"id":"namespace-cache-keys-in-shared-redis-deployments","title":"Namespace cache keys in shared Redis deployments","role":"content","text":"If multiple applications share a Redis deployment, prefix cache keys with the application name (`cache:billing:product:{id}`) so different services cannot clobber each other's entries."},{"id":"inspect-cached-entries-directly-in-redis","title":"Inspect cached entries directly in Redis","role":"content","text":"When testing or troubleshooting, inspect the stored cache key directly to confirm the application is writing the fields and TTL you expect:\n\n[code example]"},{"id":"learn-more","title":"Learn more","role":"related","text":"* [redis-rb guide](https://redis.io/docs/latest/develop/clients/ruby) - Install and use the Ruby Redis client\n* [SET command](https://redis.io/docs/latest/commands/set) - Set a string with TTL options (`EX`, `PX`, `NX`)\n* [HSET command](https://redis.io/docs/latest/commands/hset) - Write hash fields\n* [HGETALL command](https://redis.io/docs/latest/commands/hgetall) - Read every field of a hash\n* [EXPIRE command](https://redis.io/docs/latest/commands/expire) - Set key expiration in seconds\n* [DEL command](https://redis.io/docs/latest/commands/del) - Delete a key on invalidation\n* [Lua scripting](https://redis.io/docs/latest/develop/programmability/eval-intro) - Atomic single-flight locks and stampede mitigation"}],"examples":[{"id":"the-cache-aside-helper-ex0","language":"ruby","code":"require \"redis\"\nrequire_relative \"cache\"\nrequire_relative \"primary\"\n\nredis = Redis.new(host: \"localhost\", port: 6379)\nprimary = MockPrimaryStore.new(read_latency_ms: 150)\ncache = RedisCache.new(redis: redis, ttl: 30)\n\n# Read through the cache.\nresult = cache.get(\"p-001\") { |id| primary.read(id) }\nputs \"hit=#{result[:hit]} latency=#{result[:redis_latency_ms]} ms record=#{result[:record]}\"\n\n# Update a single field without rewriting the whole record.\ncache.update_field(\"p-001\", \"stock\", \"41\")\n\n# Invalidate the cache key on a write to the primary.\nprimary.update_field(\"p-001\", \"price_cents\", \"699\")\ncache.invalidate(\"p-001\")","section_id":"the-cache-aside-helper"},{"id":"data-model-ex0","language":"text","code":"cache:product:p-001\n  id          = p-001\n  name        = Sourdough Loaf\n  price_cents = 650\n  stock       = 42","section_id":"data-model"},{"id":"cache-aside-reads-ex0","language":"ruby","code":"def get(entity_id, &loader)\n  cache_key = cache_key(entity_id)\n\n  started = monotonic_ms\n  cached = @redis.hgetall(cache_key)\n  redis_latency_ms = monotonic_ms - started\n\n  if cached && !cached.empty?\n    record_hit\n    return { record: cached, hit: true, redis_latency_ms: redis_latency_ms }\n  end\n\n  record_miss\n  record = load_with_single_flight(entity_id, &loader)\n  { record: record, hit: false, redis_latency_ms: redis_latency_ms }\nend","section_id":"cache-aside-reads"},{"id":"stampede-protection-with-a-lua-lock-ex0","language":"lua","code":"-- Acquire a short-lived lock with SET NX PX. Returns 1 on acquire, 0 otherwise.\nif redis.call('SET', KEYS[1], ARGV[1], 'NX', 'PX', ARGV[2]) then\n    return 1\nend\nreturn 0","section_id":"stampede-protection-with-a-lua-lock"},{"id":"stampede-protection-with-a-lua-lock-ex1","language":"lua","code":"if redis.call('GET', KEYS[1]) == ARGV[1] then\n    return redis.call('DEL', KEYS[1])\nend\nreturn 0","section_id":"stampede-protection-with-a-lua-lock"},{"id":"stampede-protection-with-a-lua-lock-ex2","language":"ruby","code":"def load_with_single_flight(entity_id, &loader)\n  cache_key = cache_key(entity_id)\n  lock_key = lock_key(entity_id)\n  token = SecureRandom.hex(8)\n\n  acquired = @redis.eval(ACQUIRE_LOCK_SCRIPT, keys: [lock_key], argv: [token, @lock_ttl_ms.to_s])\n\n  if acquired == 1\n    begin\n      record = loader.call(entity_id)\n      return nil if record.nil?\n      @redis.multi do |pipe|\n        pipe.del(cache_key)\n        pipe.hset(cache_key, record)\n        pipe.expire(cache_key, @ttl)\n      end\n      return record\n    ensure\n      @redis.eval(RELEASE_LOCK_SCRIPT, keys: [lock_key], argv: [token])\n    end\n  end\n\n  record_stampede_suppressed\n  deadline = monotonic_ms + @lock_ttl_ms\n  while monotonic_ms < deadline\n    sleep(@wait_poll_ms / 1000.0)\n    cached = @redis.hgetall(cache_key)\n    return cached if cached && !cached.empty?\n  end\n  loader.call(entity_id)\nend","section_id":"stampede-protection-with-a-lua-lock"},{"id":"invalidation-on-write-ex0","language":"ruby","code":"def invalidate(entity_id)\n  @redis.del(cache_key(entity_id)) == 1\nend","section_id":"invalidation-on-write"},{"id":"field-level-updates-ex0","language":"ruby","code":"def update_field(entity_id, field, value)\n  cache_key = cache_key(entity_id)\n  loop do\n    @redis.watch(cache_key)\n    unless @redis.exists?(cache_key)\n      @redis.unwatch\n      return false\n    end\n    result = @redis.multi do |pipe|\n      pipe.hset(cache_key, field, value.to_s)\n      pipe.expire(cache_key, @ttl)\n    end\n    return true if result\n    # nil result means WATCH detected a change — retry.\n  end\nend","section_id":"field-level-updates"},{"id":"hitmiss-accounting-ex0","language":"ruby","code":"def stats\n  @stats_mutex.synchronize do\n    total = @stats[:hits] + @stats[:misses]\n    hit_rate = total.zero? ? 0.0 : (1000 * @stats[:hits] / total).to_f / 10\n    {\n      \"hits\" => @stats[:hits],\n      \"misses\" => @stats[:misses],\n      \"stampedes_suppressed\" => @stats[:stampedes_suppressed],\n      \"hit_rate_pct\" => hit_rate,\n    }\n  end\nend","section_id":"hitmiss-accounting"},{"id":"prerequisites-ex0","language":"bash","code":"gem install redis webrick","section_id":"prerequisites"},{"id":"running-the-demo-ex0","language":"bash","code":"ruby demo_server.rb","section_id":"running-the-demo"},{"id":"the-mock-primary-store-ex0","language":"ruby","code":"class MockPrimaryStore\n  def initialize(read_latency_ms: 150)\n    @read_latency_ms = read_latency_ms\n    # ...\n  end\n\n  def read(id)\n    sleep(@read_latency_ms / 1000.0)\n    # ...\n  end\nend","section_id":"the-mock-primary-store"},{"id":"inspect-cached-entries-directly-in-redis-ex0","language":"bash","code":"redis-cli HGETALL cache:product:p-001\nredis-cli TTL cache:product:p-001","section_id":"inspect-cached-entries-directly-in-redis"}]}
{"schema_version":2,"id":"develop/use-cases/cache-aside/rust","title":"Redis cache-aside with redis-rs","url":"https://redis.io/docs/latest/develop/use-cases/cache-aside/rust/","summary":"Implement a Redis cache-aside layer in Rust with redis-rs","aliases":["/develop/use-cases/cache-aside/redis-rs"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:50:30+01:00","children":[],"page_type":"content","content_hash":"7f45959b016a7cb81122f181b7a8b9d8528d5d6d03a28e5f49495b99b64b4812","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis cache-aside layer in Rust with the [redis](https://crates.io/crates/redis) crate (redis-rs). It includes a small local web server built on [axum](https://github.com/tokio-rs/axum) so you can see cache hits, misses, invalidation on write, and stampede protection in action."},{"id":"overview","title":"Overview","role":"overview","text":"Cache-aside is one of the most common Redis use cases for read-heavy applications. Instead of querying the primary database on every request, the application checks Redis first and only falls back to the primary on a miss. The result is written back to Redis with a TTL so the next read is served from memory.\n\nThat gives you:\n\n* Sub-millisecond reads for the hot working set\n* Bounded staleness — every entry expires within a known window\n* Reduced primary database load proportional to hit rate\n* Field-level updates without re-serializing the full record\n* Protection against cache stampedes when popular keys expire under load\n\nIn this example, each cached product is stored as a Redis hash under a key like `cache:product:{id}`. The hash holds the product fields (`id`, `name`, `price_cents`, `stock`) and the key has a TTL so stale data is bounded automatically."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow on every read looks like this:\n\n1. The application calls `cache.get(id, |key| async { primary.read(&key).await })`\n2. The helper runs `HGETALL` against `cache:product:{id}`\n3. On a hit, the cached hash is returned directly\n4. On a miss, the helper acquires a Lua-backed single-flight lock and awaits the loader to fetch from the primary\n5. The helper writes the result back to Redis with `HSET` plus `EXPIRE` and releases the lock\n6. Concurrent tasks that fail to acquire the lock wait briefly for the cache to populate, then return that value instead of issuing their own primary read\n\nOn a write, the application updates the primary and then deletes the cache key, so the next read repopulates from the new source value."},{"id":"the-cache-aside-helper","title":"The cache-aside helper","role":"content","text":"The `RedisCache` struct wraps the cache-aside operations\n([source](cache.rs)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each cached product is stored in a Redis hash:\n\n[code example]\n\nThe implementation uses:\n\n* [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) to read the cached record\n* [`HSET`](https://redis.io/docs/latest/commands/hset) plus [`EXPIRE`](https://redis.io/docs/latest/commands/expire) to repopulate after a miss\n* [`DEL`](https://redis.io/docs/latest/commands/del) to invalidate on writes\n* [`TTL`](https://redis.io/docs/latest/commands/ttl) to surface remaining staleness in the demo UI\n* [`EVAL`](https://redis.io/docs/latest/commands/eval) for the Lua single-flight lock that prevents stampedes\n* [`WATCH`](https://redis.io/docs/latest/commands/watch)/[`MULTI`](https://redis.io/docs/latest/commands/multi)/[`EXEC`](https://redis.io/docs/latest/commands/exec) for the conditional field update path"},{"id":"cache-aside-reads","title":"Cache-aside reads","role":"content","text":"The `get()` method runs `HGETALL` on the cache key first. On a hit it returns the cached hash and increments the hit counter. On a miss, it delegates to a single-flight loader:\n\n[code example]\n\nThe returned `CacheResult` includes the measured Redis round-trip time so the demo UI can show the latency difference between a hit and a miss."},{"id":"stampede-protection-with-a-lua-lock","title":"Stampede protection with a Lua lock","role":"content","text":"When a popular key expires, every concurrent reader observes the miss at the same instant. Without coordination, all of them would query the primary and overwrite the cache redundantly — a *cache stampede*.\n\nThe helper uses a tiny Lua script to acquire a short-lived lock atomically. Only the task that wins the `SET NX` becomes the primary loader; the rest poll the cache briefly and return the value the lock holder writes:\n\n[code example]\n\nA second script releases the lock only if the caller still owns it, so a lock that timed out and was re-acquired by someone else cannot be released by mistake:\n\n[code example]\n\nThe Rust side wraps both scripts in `redis::Script` and runs them on every miss:\n\n[code example]\n\nThe unique `token` per caller is what makes the release script safe — only the task that actually holds the lock can release it."},{"id":"invalidation-on-write","title":"Invalidation on write","role":"content","text":"When a write hits the primary, the application invalidates the cache key. The next read pulls fresh data from the primary:\n\n[code example]\n\nThis is the simplest and safest pattern: never try to keep the cache and primary in sync directly, just delete the cache entry and let the next read repopulate it."},{"id":"field-level-updates","title":"Field-level updates","role":"content","text":"Because each record is stored as a hash, the cache helper can also update a single field in place without re-serializing the full record. The update only writes if the entry is already cached, so a partial record can never appear in Redis:\n\n[code example]\n\nThis is useful for hot fields that change more often than the rest of the record (a stock counter, a view count) and would otherwise force a full reload."},{"id":"hitmiss-accounting","title":"Hit/miss accounting","role":"content","text":"The helper keeps in-process counters for hits, misses, and stampedes that were suppressed by the single-flight lock. The demo UI surfaces these so you can see the cache absorbing load:\n\n[code example]\n\nIn production you would emit these as `metrics`/`prometheus` counters or push them into your observability stack rather than holding them as `AtomicU64`s in process memory."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before running the demo, make sure that:\n\n* Redis is running and accessible. By default, the demo connects to `localhost:6379`.\n* The Rust toolchain is installed (rustup, cargo).\n* Dependencies are declared in `Cargo.toml`:\n\n[code example]\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":"A local demo server is included to show the cache-aside layer in action\n([source](demo_server.rs)):\n\n[code example]\n\nThe demo server uses Tokio's async runtime plus axum for HTTP handling:\n\n* [`tokio`](https://crates.io/crates/tokio) for the async runtime\n* [`axum`](https://crates.io/crates/axum) for the web server\n* [`redis::aio::ConnectionManager`](https://docs.rs/redis/latest/redis/aio/struct.ConnectionManager.html) for shared multiplexed connections\n* [`redis::Script`](https://docs.rs/redis/latest/redis/struct.Script.html) for the Lua single-flight scripts\n\nIt exposes a small interactive page where you can:\n\n* Read a product through the cache and see whether it was a hit or a miss\n* Compare the measured Redis round-trip against the simulated primary read latency\n* Watch the cache TTL count down between requests\n* Update a field on the primary and see the cache invalidate automatically\n* Run a stampede test that fires many concurrent reads at a freshly-invalidated key and confirms only one of them reaches the primary\n* Reset the hit/miss counters at any time\n\nAfter starting the server, visit `http://localhost:8080`."},{"id":"the-mock-primary-store","title":"The mock primary store","role":"content","text":"To make the demo self-contained, the example includes a `MockPrimaryStore` that stands in for a slow disk-backed database\n([source](primary.rs)):\n\n[code example]\n\nEvery call to `read()` sleeps for `read_latency_ms` so the difference between a cache hit and a miss is obvious in the UI. The store also tracks the total number of primary reads, which the stampede test uses to confirm that single-flight is working — for N concurrent readers against a cold key, you should see exactly one primary read.\n\nIn a real application this would be replaced by a SQLx query, an HTTP call to a downstream service, or any other slow-but-authoritative source."},{"id":"production-usage","title":"Production usage","role":"content","text":"This guide uses a deliberately small local demo so you can focus on the cache-aside pattern. In production, you will usually want to harden several aspects of it."},{"id":"choose-a-ttl-that-matches-your-staleness-tolerance","title":"Choose a TTL that matches your staleness tolerance","role":"content","text":"The TTL is the upper bound on how long a stale value can be served. Shorter TTLs mean lower hit rates and more primary load; longer TTLs mean higher hit rates and more stale reads between writes. Pick the value that matches your business tolerance for stale data, and combine it with explicit invalidation on writes for the cases where you cannot tolerate any staleness."},{"id":"invalidate-dont-try-to-keep-the-cache-in-sync","title":"Invalidate, don't try to keep the cache in sync","role":"content","text":"When the underlying record changes, delete the cache key rather than rewriting it. Cache-aside is robust precisely because it never assumes the cache holds the latest value — the next read always re-fetches from the primary on a miss."},{"id":"handle-missing-records-explicitly","title":"Handle missing records explicitly","role":"content","text":"In this demo, a missing record returns `None` and nothing is cached. In a real system you may want to cache \"not found\" sentinels with a short TTL to absorb load from probing for non-existent IDs, while making sure the sentinel TTL is shorter than the positive cache entry so a newly-created record becomes visible quickly."},{"id":"tune-the-single-flight-lock-ttl","title":"Tune the single-flight lock TTL","role":"content","text":"The lock TTL needs to be longer than the worst-case primary read latency so a slow loader does not lose the lock midway. The unique token in `RELEASE_LOCK_SCRIPT` ensures the original caller does not delete someone else's lock if its lock has expired."},{"id":"connectionmanager-vs-multiplexedconnection","title":"`ConnectionManager` vs `MultiplexedConnection`","role":"content","text":"The demo uses `redis::aio::ConnectionManager`, which transparently reconnects on transient failures and is cheap to clone for use across many concurrent tokio tasks. For very high-throughput workloads you may also want to look at `bb8-redis` or `deadpool-redis` for explicit pooling."},{"id":"namespace-cache-keys-in-shared-redis-deployments","title":"Namespace cache keys in shared Redis deployments","role":"content","text":"If multiple applications share a Redis deployment, prefix cache keys with the application name (`cache:billing:product:{id}`) so different services cannot clobber each other's entries."},{"id":"inspect-cached-entries-directly-in-redis","title":"Inspect cached entries directly in Redis","role":"content","text":"When testing or troubleshooting, inspect the stored cache key directly to confirm the application is writing the fields and TTL you expect:\n\n[code example]"},{"id":"learn-more","title":"Learn more","role":"related","text":"* [redis crate on crates.io](https://crates.io/crates/redis) - Install and use the Rust Redis client\n* [SET command](https://redis.io/docs/latest/commands/set) - Set a string with TTL options (`EX`, `PX`, `NX`)\n* [HSET command](https://redis.io/docs/latest/commands/hset) - Write hash fields\n* [HGETALL command](https://redis.io/docs/latest/commands/hgetall) - Read every field of a hash\n* [EXPIRE command](https://redis.io/docs/latest/commands/expire) - Set key expiration in seconds\n* [DEL command](https://redis.io/docs/latest/commands/del) - Delete a key on invalidation\n* [Lua scripting](https://redis.io/docs/latest/develop/programmability/eval-intro) - Atomic single-flight locks and stampede mitigation"}],"examples":[{"id":"the-cache-aside-helper-ex0","language":"rust","code":"use redis::aio::ConnectionManager;\nuse redis::Client;\nuse std::sync::Arc;\n\nmod cache;\nmod primary;\n\nuse cache::{CacheConfig, RedisCache};\nuse primary::MockPrimaryStore;\n\n#[tokio::main]\nasync fn main() -> redis::RedisResult<()> {\n    let client = Client::open(\"redis://localhost:6379/\")?;\n    let conn = ConnectionManager::new(client).await?;\n    let primary = Arc::new(MockPrimaryStore::new(150));\n    let cache = RedisCache::new(conn, CacheConfig::default());\n\n    // Read through the cache.\n    let primary_clone = primary.clone();\n    let result = cache\n        .get(\"p-001\", |key| {\n            let p = primary_clone.clone();\n            async move { p.read(&key).await }\n        })\n        .await?;\n    println!(\"hit={} latency={:.2}ms\", result.hit, result.redis_latency_ms);\n\n    // Update a single field without rewriting the whole record.\n    cache.update_field(\"p-001\", \"stock\", \"41\").await?;\n\n    // Invalidate the cache key on a write to the primary.\n    primary.update_field(\"p-001\", \"price_cents\", \"699\");\n    cache.invalidate(\"p-001\").await?;\n    Ok(())\n}","section_id":"the-cache-aside-helper"},{"id":"data-model-ex0","language":"text","code":"cache:product:p-001\n  id          = p-001\n  name        = Sourdough Loaf\n  price_cents = 650\n  stock       = 42","section_id":"data-model"},{"id":"cache-aside-reads-ex0","language":"rust","code":"pub async fn get<F, Fut>(&self, id: &str, loader: F) -> RedisResult<CacheResult>\nwhere\n    F: Fn(String) -> Fut,\n    Fut: Future<Output = Option<HashMap<String, String>>>,\n{\n    let cache_key = self.cache_key(id);\n    let mut conn = self.conn.clone();\n\n    let started = Instant::now();\n    let cached: HashMap<String, String> = conn.hgetall(&cache_key).await?;\n    let redis_latency_ms = started.elapsed().as_secs_f64() * 1000.0;\n\n    if !cached.is_empty() {\n        self.stats.hits.fetch_add(1, Ordering::Relaxed);\n        return Ok(CacheResult { record: Some(cached), hit: true, redis_latency_ms });\n    }\n\n    self.stats.misses.fetch_add(1, Ordering::Relaxed);\n    let record = self.load_with_single_flight(id, &loader).await?;\n    Ok(CacheResult { record, hit: false, redis_latency_ms })\n}","section_id":"cache-aside-reads"},{"id":"stampede-protection-with-a-lua-lock-ex0","language":"lua","code":"-- Acquire a short-lived lock with SET NX PX. Returns 1 on acquire, 0 otherwise.\nif redis.call('SET', KEYS[1], ARGV[1], 'NX', 'PX', ARGV[2]) then\n    return 1\nend\nreturn 0","section_id":"stampede-protection-with-a-lua-lock"},{"id":"stampede-protection-with-a-lua-lock-ex1","language":"lua","code":"if redis.call('GET', KEYS[1]) == ARGV[1] then\n    return redis.call('DEL', KEYS[1])\nend\nreturn 0","section_id":"stampede-protection-with-a-lua-lock"},{"id":"stampede-protection-with-a-lua-lock-ex2","language":"rust","code":"async fn load_with_single_flight<F, Fut>(\n    &self,\n    id: &str,\n    loader: &F,\n) -> RedisResult<Option<HashMap<String, String>>>\nwhere\n    F: Fn(String) -> Fut,\n    Fut: Future<Output = Option<HashMap<String, String>>>,\n{\n    let cache_key = self.cache_key(id);\n    let lock_key = self.lock_key(id);\n    let token = random_token();\n\n    let mut conn = self.conn.clone();\n    let acquired: i64 = self\n        .acquire_script\n        .key(&lock_key)\n        .arg(token.as_str())\n        .arg(self.cfg.lock_ttl_ms.to_string())\n        .invoke_async(&mut conn)\n        .await?;\n\n    if acquired == 1 {\n        let result = self.populate_after_lock(id, loader, &cache_key).await;\n        let _ = self\n            .release_script\n            .key(&lock_key)\n            .arg(token.as_str())\n            .invoke_async::<_, i64>(&mut conn)\n            .await;\n        return result;\n    }\n\n    self.stats.stampedes_suppressed.fetch_add(1, Ordering::Relaxed);\n    let deadline = Instant::now() + Duration::from_millis(self.cfg.lock_ttl_ms);\n    while Instant::now() < deadline {\n        tokio::time::sleep(Duration::from_millis(self.cfg.wait_poll_ms)).await;\n        let cached: HashMap<String, String> = conn.hgetall(&cache_key).await?;\n        if !cached.is_empty() {\n            return Ok(Some(cached));\n        }\n    }\n    Ok(loader(id.to_string()).await)\n}","section_id":"stampede-protection-with-a-lua-lock"},{"id":"invalidation-on-write-ex0","language":"rust","code":"pub async fn invalidate(&self, id: &str) -> RedisResult<bool> {\n    let mut conn = self.conn.clone();\n    let n: i64 = conn.del(self.cache_key(id)).await?;\n    Ok(n == 1)\n}","section_id":"invalidation-on-write"},{"id":"field-level-updates-ex0","language":"rust","code":"pub async fn update_field(&self, id: &str, field: &str, value: &str) -> RedisResult<bool> {\n    let cache_key = self.cache_key(id);\n    let mut conn = self.conn.clone();\n    loop {\n        redis::cmd(\"WATCH\").arg(&cache_key).query_async::<_, ()>(&mut conn).await?;\n        let exists: i64 = conn.exists(&cache_key).await?;\n        if exists == 0 {\n            redis::cmd(\"UNWATCH\").query_async::<_, ()>(&mut conn).await?;\n            return Ok(false);\n        }\n        let result: Option<((),)> = redis::pipe()\n            .atomic()\n            .hset(&cache_key, field, value)\n            .ignore()\n            .expire(&cache_key, self.cfg.ttl as i64)\n            .query_async(&mut conn)\n            .await?;\n        if result.is_some() {\n            return Ok(true);\n        }\n        // EXEC returned nil — WATCH detected a change. Retry.\n    }\n}","section_id":"field-level-updates"},{"id":"hitmiss-accounting-ex0","language":"rust","code":"pub fn stats(&self) -> serde_json::Value {\n    let hits = self.stats.hits.load(Ordering::Relaxed);\n    let misses = self.stats.misses.load(Ordering::Relaxed);\n    let stampedes = self.stats.stampedes_suppressed.load(Ordering::Relaxed);\n    let total = hits + misses;\n    let hit_rate_pct = if total == 0 {\n        0.0\n    } else {\n        ((1000 * hits / total) as f64) / 10.0\n    };\n    serde_json::json!({\n        \"hits\": hits,\n        \"misses\": misses,\n        \"stampedes_suppressed\": stampedes,\n        \"hit_rate_pct\": hit_rate_pct,\n    })\n}","section_id":"hitmiss-accounting"},{"id":"prerequisites-ex0","language":"toml","code":"[dependencies]\nredis = { version = \"0.24\", features = [\"tokio-comp\", \"aio\", \"connection-manager\"] }\ntokio = { version = \"1\", features = [\"full\"] }\naxum = \"0.7\"\nserde = { version = \"1.0\", features = [\"derive\"] }\nserde_json = \"1.0\"\nrand = \"0.8\"","section_id":"prerequisites"},{"id":"running-the-demo-ex0","language":"bash","code":"cargo run --release -- --port 8080 --redis-host localhost --redis-port 6379","section_id":"running-the-demo"},{"id":"the-mock-primary-store-ex0","language":"rust","code":"pub async fn read(&self, id: &str) -> Option<HashMap<String, String>> {\n    tokio::time::sleep(Duration::from_millis(self.read_latency_ms)).await;\n    self.reads.fetch_add(1, Ordering::Relaxed);\n    let map = self.records.lock().unwrap();\n    map.get(id).cloned()\n}","section_id":"the-mock-primary-store"},{"id":"inspect-cached-entries-directly-in-redis-ex0","language":"bash","code":"redis-cli HGETALL cache:product:p-001\nredis-cli TTL cache:product:p-001","section_id":"inspect-cached-entries-directly-in-redis"}]}
{"schema_version":2,"id":"develop/use-cases/feature-store/dotnet","title":"Redis feature store with StackExchange.Redis","url":"https://redis.io/docs/latest/develop/use-cases/feature-store/dotnet/","summary":"Build a Redis-backed online feature store in .NET with StackExchange.Redis","aliases":["/develop/use-cases/feature-store/stackexchange.redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:50:30+01:00","children":[],"page_type":"content","content_hash":"1bbac03225f59f5b8432df5d6489ae3e5d8c1c5c791ae45c5c07abce22d1e373","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a small Redis-backed online feature store\nin .NET with [StackExchange.Redis](https://redis.io/docs/latest/develop/clients/dotnet).\nThe demo runs on top of ASP.NET Core's minimal-API web framework so you can\nbulk-load a batch of users with a key-level TTL, run a streaming worker that\noverwrites real-time features with per-field TTL, retrieve any subset of\nfeatures for one user under 2 ms, and pipeline `HMGET` across a hundred\nusers for batch scoring."},{"id":"overview","title":"Overview","role":"overview","text":"Each entity (here, a user) is one Redis\n[Hash](https://redis.io/docs/latest/develop/data-types/hashes) at a deterministic key —\n`fs:user:{id}`. The hash holds every feature for that entity as one field per\nfeature: batch-materialized aggregates (refreshed once a day) alongside\nstreaming-updated signals (refreshed every few seconds). One\n[`HMGET`](https://redis.io/docs/latest/commands/hmget) returns whichever subset the\nmodel needs in one network round trip.\n\nTwo TTL layers solve the *mixed staleness* problem without an\napplication-side cleaner:\n\n* A **key-level** [`EXPIRE`](https://redis.io/docs/latest/commands/expire) aligned with\n  the batch materialization cycle (24 hours in the demo). If the batch\n  refresher fails, the whole entity disappears at the next cycle and\n  inference sees a missing entity — which the model handler can detect and\n  fall back on — rather than silently outdated values.\n* A **per-field** [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) (Redis 7.4+)\n  on each streaming feature gives that field its own shorter expiry,\n  independent of the rest of the hash. If the streaming pipeline stops\n  updating a feature, the field self-cleans while the batch fields stay\n  populated.\n\nThat gives you:\n\n* A single round trip for retrieval — any subset of features for one entity\n  in one [`HMGET`](https://redis.io/docs/latest/commands/hmget).\n* Sub-millisecond hot path. The Redis-side work is microseconds; in practice\n  the bottleneck is the network round trip plus the model's own\n  feature-prep.\n* Pipelined batch scoring — one round trip for `N` users at once.\n* Independent freshness per feature, expressed as a server-side TTL rather\n  than as application logic.\n* Self-cleanup on pipeline failure: a stalled batch refresher lets entities\n  expire on schedule, and a stalled streaming worker lets each affected\n  field expire on its own timer."},{"id":"how-stackexchangeredis-fits-the-demo","title":"How StackExchange.Redis fits the demo","role":"content","text":"Three client facts shape the helper:\n\n* **`ConnectionMultiplexer` is a single, shared, thread-safe object.** One\n  instance serves the whole process — every HTTP handler in the ASP.NET\n  Core thread pool and the streaming worker pull an `IDatabase` from the\n  same multiplexer with `mux.GetDatabase()`. There is no pool to manage and\n  no per-call connection borrow.\n* **`IBatch` is the canonical pipelining handle.** `db.CreateBatch()`\n  returns a builder; you call the async methods to queue commands (each\n  returns a `Task<T>` that completes when the batch is flushed), then\n  `batch.Execute()` ships the lot in one round trip. The pattern is \"fire\n  all the async methods, *then* call Execute, *then* await the Tasks.\"\n* **Per-field TTL is typed.** StackExchange.Redis 2.8+ exposes\n  `IDatabase.HashFieldExpireAsync` (returns `ExpireResult[]` — an enum\n  whose values map 1:1 to Redis's HEXPIRE return codes) and\n  `IDatabase.HashFieldGetTimeToLiveAsync` (returns `long[]` in\n  milliseconds). The demo pins 2.13.17.\n\nIn this example, the batch features describe a user's longer-term shape\n(`country_iso`, `risk_segment`, `account_age_days`, `tx_count_7d`,\n`avg_amount_30d`, `chargeback_count_180d`) and are bulk-loaded by the\n`BuildFeatures` static class. The streaming features describe what the user\nis doing right now (`last_login_ts`, `last_device_id`, `tx_count_5m`,\n`failed_logins_15m`, `session_country`) and are written by a `StreamingWorker`\nbackground task. The HTTP handlers in `Program.cs` read any subset of those\nfeatures through `FeatureStore`'s helper class."},{"id":"how-it-works","title":"How it works","role":"content","text":"There are three paths: a **batch path** that bulk-loads features once per\nmaterialization cycle, a **streaming path** that updates real-time features\nas events arrive, and an **inference path** that reads features on the\nrequest side."},{"id":"batch-path-per-materialization-cycle","title":"Batch path (per materialization cycle)","role":"content","text":"1. The batch job calls `BuildFeatures.SynthesizeUsers(N, seed)` (in\n   production, the equivalent computation lives in an offline pipeline\n   against the warehouse). The result is\n   `Dictionary<string, IReadOnlyDictionary<string, object>>` keyed by user\n   ID.\n2. `store.BulkLoadAsync(rows, ttlSeconds)` queues one\n   [`HSET`](https://redis.io/docs/latest/commands/hset) plus one\n   [`EXPIRE`](https://redis.io/docs/latest/commands/expire) per user on an `IBatch`,\n   calls `batch.Execute()` to ship the whole thing in one round trip, then\n   `Task.WhenAll` waits for every per-command reply."},{"id":"streaming-path-per-event","title":"Streaming path (per event)","role":"content","text":"When a user does something (login, transaction, page view) the streaming\nlayer computes whatever real-time signals fall out of that event and\ncalls `store.UpdateStreamingAsync(userId, fields, ttlSeconds)`. That queues:\n\n1. An [`HSET`](https://redis.io/docs/latest/commands/hset) writing the new field values.\n   Redis is single-threaded per shard, so this is atomic against any\n   concurrent batch write on the same hash — no version columns, no locks.\n2. An [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) over exactly the\n   fields that were written, with the streaming TTL. Each streaming field\n   carries its own per-field expiry independent of the rest of the hash.\n   Stop the worker and these fields drop out one by one as their TTLs\n   elapse, while the batch fields remain populated under the longer\n   key-level TTL."},{"id":"inference-path-per-request","title":"Inference path (per request)","role":"content","text":"1. The model server picks the feature subset it needs (the schema is owned\n   by the model, not the store).\n2. It calls `store.GetFeaturesAsync(userId, names)`, which is one\n   [`HMGET`](https://redis.io/docs/latest/commands/hmget). StackExchange.Redis returns\n   the values in the same order as the requested fields, with\n   `RedisValue.Null` for any field that doesn't exist (or has expired).\n3. For batch inference, the model server calls\n   `store.BatchGetFeaturesAsync(userIds, names)`, which pipelines one\n   [`HMGET`](https://redis.io/docs/latest/commands/hmget) per user across all `N`\n   users in a single network round trip via `IBatch`."},{"id":"project-layout","title":"Project layout","role":"content","text":"The csproj sits at the project root with every C# source file next to it,\nmirroring every other client demo in this use case:\n\n[code example]\n\nBuild and run with `dotnet run -c Release`. The `--mode build-features`\nflag short-circuits to the CLI builder before the HTTP server starts up."},{"id":"the-feature-store-helper","title":"The feature-store helper","role":"content","text":"The `FeatureStore` class wraps the read/write paths\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/feature-store/dotnet/FeatureStore.cs)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each user is one Redis Hash. Every value is stored as a string — Redis hash\nfields are bytes on the wire, so `FeatureStore.EncodeValue` renders\nbooleans as `\"true\"` / `\"false\"` and uses `Object.ToString()` (with\n`InvariantCulture` for doubles, so a `92.40` doesn't become `\"92,40\"` in\nlocales that use a comma decimal separator). The model server is responsible\nfor parsing back to the right type, the same way it would when reading any\nserialized feature store.\n\n[code example]"},{"id":"bulk-loading-batch-features","title":"Bulk-loading batch features","role":"content","text":"`BulkLoadAsync` queues one `HSET` and one `EXPIRE` per user through an\n`IBatch`, then `Execute()` ships the whole batch in one round trip.\n\n[code example]\n\nTwo things worth noticing:\n\n1. **Call the async methods *before* `Execute()`.** They don't run anything\n   yet — they just queue the command and return a `Task` that completes\n   when the batch is flushed. Order matters: a `batch.HashSetAsync(...)`\n   after `batch.Execute()` is just a regular async call against the\n   underlying database (and will fail because the local `IBatch` is now\n   spent).\n2. **`Task.WhenAll(tasks)` after `Execute()`** is how you wait for the\n   server to acknowledge the whole batch. Skipping it would leak any\n   per-command errors (a malformed `EXPIRE`, for example) into the next\n   call instead of the batch.\n\nIn production, the equivalent of this script runs as an offline pipeline\n(a Spark or Feast `materialize` job) that reads from the warehouse and\nwrites into Redis. The\n[Feast `RedisOnlineStore`](https://docs.feast.dev/reference/online-stores/redis)\nprovider does exactly this under the hood; the in-house\n[Redis Feature Form](https://redis.io/docs/latest/develop/ai/featureform) integration\ncovers the materialize + serve path end-to-end."},{"id":"streaming-writes-with-per-field-ttl","title":"Streaming writes with per-field TTL","role":"content","text":"`UpdateStreamingAsync` is the linchpin of the mixed-staleness story:\n\n[code example]\n\n[`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) sets the TTL on\n*individual* hash fields, not on the whole key. The two commands are\nqueued under one `IBatch` so Redis runs them in pipeline order: the\n`HSET` first creates or overwrites the fields, then `HEXPIRE` attaches a\nTTL to each of those same fields. `HashFieldExpireAsync` returns one\n`ExpireResult` per field:\n\n* `ExpireResult.Success` (= Redis code `1`): TTL set / updated.\n* `ExpireResult.Due` (= `2`): the expiry was 0 or in the past, so Redis\n  deleted the field instead of applying a TTL.\n* `ExpireResult.ConditionNotMet` (= `0`): an `NX | XX | GT | LT`\n  conditional flag was specified and not met (we never use one here).\n* `ExpireResult.NoSuchField` (= `-2`): no such field, or no such key.\n\nWe always follow `HSET` with `HEXPIRE` so any code other than `Success`\nmeans the per-field TTL invariant didn't hold — the helper throws an\n`InvalidOperationException` rather than silently leaving a streaming\nfield with no expiry attached.\n\nIf a streaming pipeline stops, the streaming fields drop out one by one\nas their per-field TTLs elapse. `FieldTtlsSecondsAsync` (which wraps\n`HashFieldGetTimeToLiveAsync`) lets the model side inspect the\nremaining TTL on any field. Note that the StackExchange.Redis return is\nin **milliseconds** — the helper divides by 1000 to match the\n`TTL` / `HTTL` second-based convention used by every other client in\nthis use case (and `redis-cli`).\n\n> **HEXPIRE requires Redis 7.4 or later.** `HEXPIRE` and the field-level\n> TTL commands (`HTTL`, `HPERSIST`, `HEXPIREAT`, `HPEXPIRE`,\n> `HPEXPIREAT`, `HPTTL`, `HEXPIRETIME`, `HPEXPIRETIME`) were added in\n> Redis 7.4. StackExchange.Redis 2.8 was the first release with the\n> typed bindings; the demo pins 2.13.17."},{"id":"inference-reads-with-hmget","title":"Inference reads with HMGET","role":"content","text":"`GetFeaturesAsync` is one `HMGET`:\n\n[code example]\n\n`db.HashGetAsync(key, RedisValue[] fields)` issues `HMGET` and returns\na `RedisValue[]` aligned with the input order. Missing fields come back\nas `RedisValue.Null` (which `IsNull` detects); the helper drops them\nfrom the result dict so the caller sees only the features that actually\nexist on the hash."},{"id":"batch-scoring-with-pipelined-hmget","title":"Batch scoring with pipelined HMGET","role":"content","text":"For batch inference, the same `HMGET` shape pipelines across users\nthrough one `IBatch`:\n\n[code example]\n\nOne round trip for the whole batch. The demo returns a 30-user batch in\n~2 ms against a local Redis after the first-call JIT/connection warm-up.\n\nA Redis Cluster is different: an `IBatch` is bound to one shard,\nbecause all queued commands ship through one connection to one node.\nFor batch reads on a cluster, the\n[StackExchange.Redis cluster client](https://redis.io/docs/latest/develop/clients/dotnet/connect)\nroutes non-batched `HashGetAsync` calls to the right shard\nautomatically — fan out parallel calls with `Task.WhenAll` and the\nmultiplexer handles per-shard routing. For tighter control, group\nentity IDs by hash slot ahead of time and use one `CreateBatch` per\nshard's connection in parallel. A hash tag like `fs:user:{vip}:u0001`\nforces a known set of keys onto the same shard so one batch can cover\nthem all."},{"id":"the-streaming-worker","title":"The streaming worker","role":"content","text":"`StreamingWorker.cs` is the demo's stand-in for whatever Flink, Kafka\nStreams, or bespoke service computes the real-time features\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/feature-store/dotnet/StreamingWorker.cs)).\nIt runs as a background `Task` next to the demo server so the UI can\nstart, pause, and resume it.\n\n[code example]\n\nThe same pre-flight `_tickInFlight` + `finally`-clear pattern as every\nother client in this use case closes the pause/in-flight race: a reset\nthat's about to `DEL` every key calls `worker.Pause()` to stop *future*\nticks *and* `await worker.WaitForIdleAsync()` to flush a mid-flight tick\nbefore issuing the DEL sweep.\n\nPausing the worker is what shows off the mixed-staleness behavior: leave\nit paused for longer than `StreamingTtlSeconds` and the streaming fields\ndisappear from every user's hash one by one, while the batch fields\nremain under the longer key-level `EXPIRE`. The demo's\n`Pause / resume` button lets you see this happen in real time."},{"id":"the-batch-builder","title":"The batch builder","role":"content","text":"`BuildFeatures.cs` is the demo's nightly materializer\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/feature-store/dotnet/BuildFeatures.cs)).\nIt generates synthetic feature rows and calls `store.BulkLoadAsync`\nonce. The synthesis itself is not the point — in a real deployment the\nequivalent code reads from the offline store (Snowflake, BigQuery,\nIceberg) and writes the resulting hashes into Redis.\n\nRun the builder on its own (independently of the demo server) to\npopulate Redis from the command line:\n\n[code example]\n\nThat writes 500 users at `fs:user:*` with a one-hour key-level TTL,\nwhich is how a typical operator would pre-seed a feature store from the\ncommand line when debugging."},{"id":"the-interactive-demo","title":"The interactive demo","role":"content","text":"`Program.cs` runs the ASP.NET Core minimal-API server on port 8091. The\nHTML page lets you:\n\n* **Bulk-load** any number of users (default 200) with a configurable\n  key-level TTL.\n* See the **store state**: user count, batch / streaming TTLs,\n  cumulative read/write counters.\n* See the **streaming worker** status and **pause or resume** it.\n* Run an **inference read** for any user with a chosen feature subset,\n  and see the value, the per-field TTL, and the read latency.\n* Run **batch scoring** with a pipelined `HMGET` across `N` users.\n* **Inspect** any user's full hash with field-level TTLs and the\n  key-level TTL.\n\nThe server holds one `FeatureStore`, one `StreamingWorker`, and one\n`ConnectionMultiplexer` for the lifetime of the process. Every handler\nin the ASP.NET Core thread pool and the streaming worker share that\nmultiplexer — StackExchange.Redis handles the per-call multiplexing\nacross the underlying socket. Endpoints:\n\n| Endpoint                  | What it does                                                                        |\n|---------------------------|-------------------------------------------------------------------------------------|\n| `GET  /state`             | User count, TTL config, stats counters, worker status.                              |\n| `POST /bulk-load`         | Pipelined `HSET` + `EXPIRE` over N synthetic users with a chosen TTL.               |\n| `POST /worker/toggle`     | Pause / resume the streaming worker.                                                |\n| `POST /read`              | `HMGET` a chosen feature subset for one user; report latency and per-field TTLs.    |\n| `POST /batch-read`        | Pipeline `HMGET` across N users; report total latency and per-entity field counts.  |\n| `GET  /inspect`           | `HGETALL` + `HTTL` for one user; full hash view with per-field TTLs.                |\n| `POST /reset`             | Drop every user under the key prefix (used by the demo's reset button).             |"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* **Redis 7.4 or later.** [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire)\n  and [`HTTL`](https://redis.io/docs/latest/commands/httl) were added in Redis 7.4;\n  the demo relies on per-field TTL for the mixed-staleness story.\n* **.NET 8 SDK or later.**\n* **StackExchange.Redis 2.8 or later.** The demo's csproj pins 2.13.17.\n  Typed bindings for the field-TTL commands ship from 2.8.\n\nThe connection multiplexer is opened with `AllowAdmin = true` because\nthe demo uses `IServer.Keys()` (SCAN under the hood) to populate UI\ndropdowns and to power the reset path. In a production read/write\nservice you would not enable `AllowAdmin`; instead, maintain an external\nindex of user IDs (a small Redis Set, say, keyed by tenant) and read it\nto discover entities. The demo's `SCAN` use is purely a UI convenience.\n\nIf your Redis server is running elsewhere, start the demo with\n`--redis-uri host:port`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo lives in a small csproj under\n[`feature-store/dotnet`](https://github.com/redis/docs/tree/main/content/develop/use-cases/feature-store/dotnet).\nClone the repo or copy the directory:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From the project directory:\n\n[code example]\n\nYou should see:\n\n[code example]\n\nOpen [http://127.0.0.1:8091](http://127.0.0.1:8091). Useful things to try:\n\n* Pick a user and click **Read features** with a mixed batch/streaming\n  subset — you'll see batch fields with no per-field TTL (covered by the\n  key-level TTL) and streaming fields with a positive per-field TTL.\n* Click **Pipeline HMGET** with `count=100` to see the latency of a\n  100-user batch read.\n* Click **Pause / resume** on the streaming worker and leave it paused\n  for ~5 minutes (or restart the server with\n  `--streaming-ttl-seconds 30` to make it visible in seconds). Re-run\n  **Read features** on any user and watch the streaming fields\n  disappear while the batch fields stay.\n* Click **Inspect** on a user to see the full hash with field-level\n  TTLs.\n* Click **Reset** to drop every user and start over."},{"id":"production-usage","title":"Production usage","role":"content","text":"The guidance below focuses on the production concerns specific to\nrunning a feature store on Redis. For the generic\nStackExchange.Redis production checklist —\n[`ConfigurationOptions`](https://redis.io/docs/latest/develop/clients/dotnet/connect)\ntuning, AUTH/ACL, retry/backoff, multiplexer lifetime, and exception\nhandling — see the\n[StackExchange.Redis production usage guide](https://redis.io/docs/latest/develop/clients/dotnet/produsage).\nFor TLS specifically, follow the\n[connect-with-TLS recipe](https://redis.io/docs/latest/develop/clients/dotnet/connect#connect-to-your-production-redis-with-tls).\nThe feature-store demo runs against `localhost` with the defaults; a real\ndeployment should harden the client first."},{"id":"adopting-the-helper-outside-aspnet-core","title":"Adopting the helper outside ASP.NET Core","role":"content","text":"`FeatureStore.cs` omits `.ConfigureAwait(false)` on its `await` calls\nbecause ASP.NET Core 8 has no synchronization context — every `await`\nresumes on a thread-pool thread, so the flag is a no-op and just\nclutters the source. If you copy the helper into a context that *does*\nhave a synchronization context (a Windows Forms or WPF app, classic\nASP.NET, a Xamarin or MAUI UI thread, or a library that needs to play\nnicely with any consumer) add `.ConfigureAwait(false)` after every\n`await` to avoid deadlocking the UI thread on the resumption."},{"id":"pick-the-batch-ttl-to-outlast-a-failed-refresher","title":"Pick the batch TTL to outlast a failed refresher","role":"content","text":"The whole-entity `EXPIRE` is your safety net against silent staleness\nfrom a broken batch pipeline. Set it longer than your worst-case batch\noutage so a single missed run doesn't take the feature store offline,\nbut short enough that a sustained outage causes loud failures (missing\nentities) rather than quiet ones (yesterday's features being scored as\ntoday's). The standard choice is one cycle of \"expected refresh\ninterval × 2\" — for a daily batch, 48 hours; for a 6-hour batch, 12\nhours.\n\nThe same logic applies to the per-field streaming TTL: a few times the\nexpected update interval so a slow-but-alive streaming worker doesn't\nchurn features needlessly, but short enough that a stalled worker\ncauses visible freshness failures."},{"id":"co-locate-the-online-store-with-serving-not-with-training","title":"Co-locate the online store with serving, not with training","role":"content","text":"The online store's hash representation does *not* have to match the\nschema in your offline store. The batch materialization step is your\nchance to flatten joins, encode categoricals, and project to whatever\nshape the model server wants — so the request path is exactly one\n`HMGET` and zero transforms.\n\nThe training pipeline reads from the offline store with its own\nschema; the serving pipeline reads from Redis with the flattened\nserving schema. Keeping those two pipelines as the same code path is\nwhat prevents training-serving skew."},{"id":"pipeline-batch-reads-across-shards","title":"Pipeline batch reads across shards","role":"content","text":"On a single Redis instance, an `IBatch` of `HMGET`s across `N` users is\none round trip. A Redis Cluster is different: an `IBatch` is bound to\none shard, so on a cluster you need to either fan out the per-user\n`HashGetAsync` calls with `Task.WhenAll` (the multiplexer routes each\none to the right shard) or group entity IDs by hash slot and create\none `IBatch` per shard's connection in parallel.\n\nA hash tag like `fs:user:{vip}:u0001` forces a known set of keys onto\nthe same shard so one `IBatch` can cover them all in a single round\ntrip."},{"id":"make-hexpire-part-of-every-streaming-write","title":"Make HEXPIRE part of every streaming write","role":"content","text":"The single biggest correctness lever in this design is that the\nstreaming write applies `HEXPIRE` *every time*. If a streaming worker\nwrites a field without renewing its TTL, the field carries whatever\nexpiry was there before — possibly none, possibly stale — and the\nmixed-staleness invariant breaks. Keep the `HSET` and `HEXPIRE` in the\nsame `IBatch` (or, even safer, in the same\n[Lua script](https://redis.io/docs/latest/develop/programmability/eval-intro) if\nyou don't trust the call site)."},{"id":"avoid-hgetall-on-the-request-path","title":"Avoid HGETALL on the request path","role":"content","text":"`HGETALL` reads every field on the hash, including ones the model\ndoesn't need. With dozens of features per entity, that is wasted\nserialization work on the server and wasted bandwidth on the wire.\nAlways specify the field list explicitly with `HashGetAsync(key, RedisValue[])`\nin the model server.\n\nThe exception is debugging and feature-set discovery, where you\ngenuinely want the full hash. The demo's \"Inspect\" button uses\n`HashGetAllAsync` for exactly this reason."},{"id":"inspect-the-store-directly-with-redis-cli","title":"Inspect the store directly with redis-cli","role":"content","text":"When testing or troubleshooting, the cli tells you everything:\n\n[code example]\n\nA streaming field that returns `-2` from `HTTL` doesn't exist on the\nhash (either it was never written, or it expired); `-1` means the\nfield has no TTL set (and is therefore covered only by the key-level\n`EXPIRE`); any positive value is the remaining TTL in seconds."},{"id":"learn-more","title":"Learn more","role":"related","text":"This example uses the following Redis commands:\n\n* [`HSET`](https://redis.io/docs/latest/commands/hset) to write a feature or a\n  whole feature row in one call.\n* [`HMGET`](https://redis.io/docs/latest/commands/hmget) to retrieve any subset of\n  features for one entity in one round trip.\n* [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) for debugging and\n  feature-set discovery.\n* [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) and\n  [`HTTL`](https://redis.io/docs/latest/commands/httl) for per-field TTL on\n  streaming features (Redis 7.4+).\n* [`EXPIRE`](https://redis.io/docs/latest/commands/expire) and\n  [`TTL`](https://redis.io/docs/latest/commands/ttl) for the whole-entity TTL\n  aligned with the batch materialization cycle.\n* Pipelined `HMGET` across many entities for batch scoring with one\n  network round trip — see\n  [transactions and pipelining](https://redis.io/docs/latest/develop/clients/dotnet/transpipe).\n\nSee the [StackExchange.Redis documentation](https://redis.io/docs/latest/develop/clients/dotnet)\nfor the full client reference, and the\n[Hashes overview](https://redis.io/docs/latest/develop/data-types/hashes) for the\ndeeper conceptual model."}],"examples":[{"id":"project-layout-ex0","language":"text","code":"feature-store/dotnet/\n├── FeatureStoreDemo.csproj\n├── Program.cs              — main() + ASP.NET Core minimal-API routes\n├── FeatureStore.cs         — FeatureStore class + EncodeValue + Stats record\n├── BuildFeatures.cs        — SynthesizeUsers + RunCliAsync\n├── StreamingWorker.cs      — background-task worker\n└── HtmlTemplate.cs         — inlined HTML page (C# 11 raw string literal)","section_id":"project-layout"},{"id":"the-feature-store-helper-ex0","language":"csharp","code":"using StackExchange.Redis;\nusing FeatureStoreDemo;\n\nvar muxOptions = ConfigurationOptions.Parse(\"localhost:6379\");\nmuxOptions.AllowAdmin = true;   // needed for SCAN via IServer.Keys()\nvar mux = await ConnectionMultiplexer.ConnectAsync(muxOptions);\n\nvar store = new FeatureStore(\n    mux,\n    \"fs:user:\",\n    batchTtlSeconds: 24 * 60 * 60,    // whole-entity TTL aligned with the daily batch cycle\n    streamingTtlSeconds: 5 * 60       // per-field TTL on each streaming feature\n);\n\n// Batch materialization: one HSET + EXPIRE per user, all pipelined.\nvar rows = new Dictionary<string, IReadOnlyDictionary<string, object>>\n{\n    [\"u0001\"] = new Dictionary<string, object>\n    {\n        [\"country_iso\"] = \"US\", [\"risk_segment\"] = \"low\",\n        [\"tx_count_7d\"] = 14, [\"avg_amount_30d\"] = 92.40,\n        [\"account_age_days\"] = 612, [\"chargeback_count_180d\"] = 0,\n    },\n};\nawait store.BulkLoadAsync(rows, 24 * 60 * 60);\n\n// Streaming write: HSET + HEXPIRE on just the fields that changed.\nawait store.UpdateStreamingAsync(\"u0001\", new Dictionary<string, object>\n{\n    [\"last_login_ts\"] = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),\n    [\"last_device_id\"] = \"ios-9f02\",\n    [\"tx_count_5m\"] = 3,\n    [\"failed_logins_15m\"] = 0,\n    [\"session_country\"] = \"US\",\n}, 5 * 60);\n\n// Inference read: HMGET of whatever the model needs.\nvar features = await store.GetFeaturesAsync(\"u0001\", new[]\n{\n    \"risk_segment\", \"tx_count_7d\", \"avg_amount_30d\",\n    \"tx_count_5m\", \"failed_logins_15m\",\n});\n\n// Batch scoring: pipelined HMGET across many users.\nvar batch = await store.BatchGetFeaturesAsync(\n    new[] { \"u0001\", \"u0002\", \"u0003\" },\n    new[] { \"risk_segment\", \"tx_count_5m\", \"failed_logins_15m\" });","section_id":"the-feature-store-helper"},{"id":"data-model-ex0","language":"text","code":"fs:user:u0001                                   TTL = 86400 s (key-level)\n  country_iso=US                                <no field TTL>\n  risk_segment=low                              <no field TTL>\n  account_age_days=612                          <no field TTL>\n  tx_count_7d=14                                <no field TTL>\n  avg_amount_30d=92.40                          <no field TTL>\n  chargeback_count_180d=0                       <no field TTL>\n  last_login_ts=1716998413541                   TTL = 300 s (per field, HEXPIRE)\n  last_device_id=ios-9f02                       TTL = 300 s (per field, HEXPIRE)\n  tx_count_5m=3                                 TTL = 300 s (per field, HEXPIRE)\n  failed_logins_15m=0                           TTL = 300 s (per field, HEXPIRE)\n  session_country=US                            TTL = 300 s (per field, HEXPIRE)","section_id":"data-model"},{"id":"bulk-loading-batch-features-ex0","language":"csharp","code":"public async Task<int> BulkLoadAsync(\n    IReadOnlyDictionary<string, IReadOnlyDictionary<string, object>> rows,\n    long ttlSeconds)\n{\n    if (rows.Count == 0) return 0;\n    var batch = _db.CreateBatch();\n    var tasks = new List<Task>(rows.Count * 2);\n    foreach (var (entityId, fields) in rows)\n    {\n        var key = (RedisKey)KeyFor(entityId);\n        var entries = new HashEntry[fields.Count];\n        int i = 0;\n        foreach (var (name, value) in fields)\n            entries[i++] = new HashEntry(name, EncodeValue(value));\n        tasks.Add(batch.HashSetAsync(key, entries));\n        tasks.Add(batch.KeyExpireAsync(key, TimeSpan.FromSeconds(ttlSeconds)));\n    }\n    batch.Execute();\n    await Task.WhenAll(tasks);\n    ...\n}","section_id":"bulk-loading-batch-features"},{"id":"streaming-writes-with-per-field-ttl-ex0","language":"csharp","code":"public async Task UpdateStreamingAsync(\n    string entityId,\n    IReadOnlyDictionary<string, object> fields,\n    long ttlSeconds)\n{\n    if (fields.Count == 0) return;\n    var key = (RedisKey)KeyFor(entityId);\n    var entries = new HashEntry[fields.Count];\n    var names = new RedisValue[fields.Count];\n    int i = 0;\n    foreach (var (name, value) in fields)\n    {\n        entries[i] = new HashEntry(name, EncodeValue(value));\n        names[i] = name;\n        i++;\n    }\n\n    var batch = _db.CreateBatch();\n    var hsetTask = batch.HashSetAsync(key, entries);\n    var hexpireTask = batch.HashFieldExpireAsync(\n        key, names, TimeSpan.FromSeconds(ttlSeconds));\n    batch.Execute();\n    await hsetTask;\n    var codes = await hexpireTask;\n    foreach (var code in codes)\n    {\n        if (code != ExpireResult.Success)\n        {\n            throw new InvalidOperationException(\n                $\"HEXPIRE did not set every field TTL for {key}: [{string.Join(\",\", codes)}]\");\n        }\n    }\n    ...\n}","section_id":"streaming-writes-with-per-field-ttl"},{"id":"inference-reads-with-hmget-ex0","language":"csharp","code":"public async Task<Dictionary<string, string>> GetFeaturesAsync(\n    string entityId, IReadOnlyList<string> fieldNames)\n{\n    var key = (RedisKey)KeyFor(entityId);\n    var out_ = new Dictionary<string, string>();\n    if (fieldNames.Count == 0) return out_;\n    var values = await _db.HashGetAsync(\n        key, fieldNames.Select(f => (RedisValue)f).ToArray());\n    for (int i = 0; i < fieldNames.Count; i++)\n    {\n        if (!values[i].IsNull)\n            out_[fieldNames[i]] = values[i].ToString();\n    }\n    ...\n}","section_id":"inference-reads-with-hmget"},{"id":"batch-scoring-with-pipelined-hmget-ex0","language":"csharp","code":"public async Task<Dictionary<string, Dictionary<string, string>>> BatchGetFeaturesAsync(\n    IReadOnlyList<string> entityIds, IReadOnlyList<string> fieldNames)\n{\n    if (entityIds.Count == 0 || fieldNames.Count == 0)\n        return new Dictionary<string, Dictionary<string, string>>();\n\n    var fieldValues = fieldNames.Select(f => (RedisValue)f).ToArray();\n    var batch = _db.CreateBatch();\n    var tasks = new Task<RedisValue[]>[entityIds.Count];\n    for (int i = 0; i < entityIds.Count; i++)\n        tasks[i] = batch.HashGetAsync(KeyFor(entityIds[i]), fieldValues);\n    batch.Execute();\n    var rows = await Task.WhenAll(tasks);\n    ...\n}","section_id":"batch-scoring-with-pipelined-hmget"},{"id":"the-streaming-worker-ex0","language":"csharp","code":"private async Task RunAsync(CancellationToken ct)\n{\n    try\n    {\n        while (!ct.IsCancellationRequested)\n        {\n            try { await Task.Delay(_tick, ct); }\n            catch (OperationCanceledException) { break; }\n            if (ct.IsCancellationRequested) break;\n\n            // Set tick_in_flight *before* the pause check so a\n            // concurrent pause+wait can never see tick_in_flight=0\n            // in the window between the pause check and the actual\n            // DoTick call. The finally block clears the flag whether\n            // we paused, succeeded, or threw.\n            Interlocked.Exchange(ref _tickInFlight, 1);\n            try\n            {\n                if (Volatile.Read(ref _paused) == 0)\n                    await DoTickAsync();\n            }\n            catch (Exception e)\n            {\n                Console.Error.WriteLine($\"[streaming-worker] tick failed: {e.Message}\");\n            }\n            finally\n            {\n                Interlocked.Exchange(ref _tickInFlight, 0);\n            }\n        }\n    }\n    finally\n    {\n        // Clear running and tick_in_flight no matter how the task\n        // exits so a later Start() can spin a fresh task.\n        Interlocked.Exchange(ref _running, 0);\n        Interlocked.Exchange(ref _tickInFlight, 0);\n    }\n}","section_id":"the-streaming-worker"},{"id":"the-batch-builder-ex0","language":"bash","code":"dotnet run --project . -- --mode build-features --count 500 --ttl-seconds 3600","section_id":"the-batch-builder"},{"id":"get-the-source-files-ex0","language":"bash","code":"git clone https://github.com/redis/docs.git\ncd docs/content/develop/use-cases/feature-store/dotnet\ndotnet build -c Release","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"dotnet run -c Release","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"Dropping any existing users under 'fs:user:*' for a clean demo run (pass --no-reset to keep them).\nRedis feature-store demo server listening on http://127.0.0.1:8091\nUsing Redis at localhost:6379 with key prefix 'fs:user:' (batch TTL 86400s, streaming TTL 300s)\nMaterialized 200 user(s); streaming worker running.","section_id":"start-the-demo-server"},{"id":"inspect-the-store-directly-with-redis-cli-ex0","language":"bash","code":"# How many users currently in the store\nredis-cli --scan --pattern 'fs:user:*' | wc -l\n\n# One user's full hash and key-level TTL\nredis-cli HGETALL fs:user:u0001\nredis-cli TTL    fs:user:u0001\n\n# Per-field TTL on the streaming fields\nredis-cli HTTL fs:user:u0001 FIELDS 5 \\\n  last_login_ts last_device_id tx_count_5m failed_logins_15m session_country\n\n# Sample HMGET as the model would issue it\nredis-cli HMGET fs:user:u0001 risk_segment tx_count_7d avg_amount_30d tx_count_5m","section_id":"inspect-the-store-directly-with-redis-cli"}]}
{"schema_version":2,"id":"develop/use-cases/feature-store/go","title":"Redis feature store with go-redis","url":"https://redis.io/docs/latest/develop/use-cases/feature-store/go/","summary":"Build a Redis-backed online feature store in Go with go-redis","aliases":["/develop/use-cases/feature-store/go-redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:50:30+01:00","children":[],"page_type":"content","content_hash":"d87a77d7d9d1c9d27c2b93e592e801d1a7297e271e25e7814669f6e4afcc5086","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a small Redis-backed online feature store in\nGo with [`go-redis`](https://redis.io/docs/latest/develop/clients/go). It includes a\nlocal web server built with Go's standard `net/http` package so you can\nbulk-load a batch of users with a key-level TTL, run a streaming worker that\noverwrites real-time features with per-field TTL, retrieve any subset of\nfeatures for one user under 1 ms, and pipeline `HMGET` across a hundred users\nfor batch scoring."},{"id":"overview","title":"Overview","role":"overview","text":"Each entity (here, a user) is one Redis\n[Hash](https://redis.io/docs/latest/develop/data-types/hashes) at a deterministic key —\n`fs:user:{id}`. The hash holds every feature for that entity as one field per\nfeature: batch-materialized aggregates (refreshed once a day) alongside\nstreaming-updated signals (refreshed every few seconds). One\n[`HMGET`](https://redis.io/docs/latest/commands/hmget) returns whichever subset the model\nneeds in one network round trip.\n\nTwo TTL layers solve the *mixed staleness* problem without an application-side\ncleaner:\n\n* A **key-level** [`EXPIRE`](https://redis.io/docs/latest/commands/expire) aligned with the\n  batch materialization cycle (24 hours in the demo). If the batch refresher\n  fails, the whole entity disappears at the next cycle and inference sees a\n  missing entity — which the model handler can detect and fall back on —\n  rather than silently outdated values.\n* A **per-field** [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) (Redis 7.4+) on\n  each streaming feature gives that field its own shorter expiry, independent\n  of the rest of the hash. If the streaming pipeline stops updating a feature,\n  the field self-cleans while the batch fields stay populated.\n\nIn this example, the batch features describe a user's longer-term shape\n(`country_iso`, `risk_segment`, `account_age_days`, `tx_count_7d`,\n`avg_amount_30d`, `chargeback_count_180d`) and are bulk-loaded by\n`build_features.go` — the demo's stand-in for a nightly Spark / Feast\nmaterialization job. The streaming features describe what the user is doing\nright now (`last_login_ts`, `last_device_id`, `tx_count_5m`,\n`failed_logins_15m`, `session_country`) and are written by\n`streaming_worker.go` — the demo's stand-in for a Flink / Kafka Streams job.\nThe inference handlers of the demo server read any subset of those features\nthrough `feature_store.go`'s helper type.\n\nThat gives you:\n\n* A single round trip for retrieval — any subset of features for one entity in\n  one [`HMGET`](https://redis.io/docs/latest/commands/hmget).\n* Sub-millisecond hot path. The Redis-side work is microseconds; in practice\n  the bottleneck is the network round trip plus the model's own feature-prep.\n* Pipelined batch scoring — one round trip for `N` users at once.\n* Independent freshness per feature, expressed as a server-side TTL rather\n  than as application logic.\n* Self-cleanup on pipeline failure: a stalled batch refresher lets entities\n  expire on schedule, and a stalled streaming worker lets each affected field\n  expire on its own timer."},{"id":"how-it-works","title":"How it works","role":"content","text":"There are three paths: a **batch path** that bulk-loads features once per\nmaterialization cycle, a **streaming path** that updates real-time features\nas events arrive, and an **inference path** that reads features on the\nrequest side."},{"id":"batch-path-per-materialization-cycle","title":"Batch path (per materialization cycle)","role":"content","text":"1. The batch job calls `SynthesizeUsers(N, seed)` (in production, the\n   equivalent computation lives in an offline pipeline against the warehouse).\n   The result is `map[string]FeatureMap` for every user in this cycle.\n2. `store.BulkLoad(ctx, rows, ttl)` batches one\n   [`HSET`](https://redis.io/docs/latest/commands/hset) plus one\n   [`EXPIRE`](https://redis.io/docs/latest/commands/expire) per user through go-redis's\n   [`Pipeline`](https://redis.io/docs/latest/develop/clients/go/transpipe), so the whole\n   batch ships in a single round trip. The `HSET` writes every batch field;\n   the `EXPIRE` is what makes the entity disappear if the next batch run\n   fails, so inference reads a missing entity rather than silently outdated\n   values."},{"id":"streaming-path-per-event","title":"Streaming path (per event)","role":"content","text":"When a user does something (login, transaction, page view) the streaming\nlayer computes whatever real-time signals fall out of that event and calls\n`store.UpdateStreaming(ctx, userID, fields, ttl)`. That batches:\n\n1. An [`HSET`](https://redis.io/docs/latest/commands/hset) writing the new field values.\n   Redis is single-threaded per shard, so this is atomic against any\n   concurrent batch write on the same hash — no version columns, no locks.\n2. An [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) over exactly the fields\n   that were written, with the streaming TTL. Each streaming field carries\n   its own per-field expiry independent of the rest of the hash. Stop the\n   worker and these fields drop out one by one as their TTLs elapse, while\n   the batch fields remain populated under the longer key-level TTL."},{"id":"inference-path-per-request","title":"Inference path (per request)","role":"content","text":"1. The model server picks the feature subset it needs (the schema is owned by\n   the model, not the store).\n2. It calls `store.GetFeatures(ctx, userID, names)`, which is one\n   [`HMGET`](https://redis.io/docs/latest/commands/hmget). Redis returns the values in\n   the same order as the requested fields, with `nil` for any field that\n   doesn't exist (or has expired).\n3. For batch inference, the model server calls\n   `store.BatchGetFeatures(ctx, userIDs, names)`, which pipelines one\n   [`HMGET`](https://redis.io/docs/latest/commands/hmget) per user across all `N` users\n   in a single network round trip."},{"id":"the-feature-store-helper","title":"The feature-store helper","role":"content","text":"The `FeatureStore` type wraps the read/write paths\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/feature-store/go/feature_store.go)):\n\n[code example]"},{"id":"package-layout","title":"Package layout","role":"content","text":"Go won't let `package main` live in the same directory as another package, so\nthe runnable entry points live in `cmd/`:\n\n[code example]\n\nBuild and run with `go run ./cmd/demo_server`. The shim is the only `main`\npackage; everything else is library code."},{"id":"data-model","title":"Data model","role":"content","text":"Each user is one Redis Hash. Every value is stored as a string — Redis hash\nfields are bytes on the wire, so the helper encodes booleans as `\"true\"` /\n`\"false\"` and renders numbers with `strconv`. The model server is responsible\nfor parsing back to the right type, the same way it would when reading any\nserialized feature store.\n\n[code example]\n\nThe batch fields sit under the key-level `EXPIRE`. The streaming fields each\ncarry their own [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire). If the\nstreaming pipeline stops, the streaming fields drop one by one as their\nper-field TTLs elapse; the batch fields stay until the daily key-level\n`EXPIRE` fires (or the next batch cycle re-pins them)."},{"id":"bulk-loading-batch-features","title":"Bulk-loading batch features","role":"content","text":"`BulkLoad` pipelines one `HSET` and one `EXPIRE` per user. With 500 users\nthat's 1000 commands in one network call — Redis processes them sequentially\non the server side but the client only pays one RTT.\n\n[code example]\n\ngo-redis's `Pipeline` is a *non-transactional* batch: commands queue up and\nship in one round trip, but they don't run inside a `MULTI/EXEC` block.\nThat's the right choice here because each user's `HSET` + `EXPIRE` pair is\nindependent of every other user's, and an all-or-nothing transaction would\nblock the server for the duration of the batch. For the rare case where the\npair has to be inseparable (a server crash between the two would leave the\nentity without a key-level TTL) you would wrap each user in `rdb.TxPipeline()`\nor a Lua script (see [`EVAL`](https://redis.io/docs/latest/commands/eval) /\n[Eval scripting](https://redis.io/docs/latest/develop/programmability/eval-intro)). For\na daily ingestion job that runs end-to-end every cycle, the next run re-pins\nthe TTL — no extra machinery needed.\n\nIn production, the equivalent of this script runs as an offline pipeline (a\nSpark or Feast `materialize` job) that reads from the warehouse and writes\ninto Redis. The\n[Feast `RedisOnlineStore`](https://docs.feast.dev/reference/online-stores/redis)\nprovider does exactly this under the hood; the in-house\n[Redis Feature Form](https://redis.io/docs/latest/develop/ai/featureform) integration\ncovers the materialize + serve path end-to-end."},{"id":"streaming-writes-with-per-field-ttl","title":"Streaming writes with per-field TTL","role":"content","text":"`UpdateStreaming` is the linchpin of the mixed-staleness story:\n\n[code example]\n\n[`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) sets the TTL on *individual*\nhash fields, not on the whole key. The two commands are sent in one round\ntrip and Redis executes them in pipeline order: the `HSET` runs first and\ncreates or overwrites the fields, then `HEXPIRE` attaches a TTL to each of\nthose same fields. `HEXPIRE` returns one status code per field — `1` if the\nTTL was set, `2` if the expiry was 0 or in the past (so Redis deleted the\nfield instead), `0` if an `NX | XX | GT | LT` conditional flag was set and\nnot met (we never use one here), `-2` if the field doesn't exist on the key.\nThe helper returns an error if any code is anything other than `1`, so the\n\"every streaming write renews its TTL\" invariant fails loudly rather than\nsilently leaving a streaming field with no expiry attached.\n\nIf a streaming pipeline stops, the streaming fields drop out one by one as\ntheir per-field TTLs elapse — there is no application-side cleaner involved.\n[`HTTL`](https://redis.io/docs/latest/commands/httl) lets the model side inspect the\nremaining TTL on any field, which is useful both for debugging (\"why is this\nfeature missing?\" → \"it expired three seconds ago\") and as a freshness signal\nin the model itself.\n\n> **HEXPIRE requires Redis 7.4 or later.** `HEXPIRE` and the field-level TTL\n> commands (`HTTL`, `HPERSIST`, `HEXPIREAT`, `HPEXPIRE`, `HPEXPIREAT`,\n> `HPTTL`, `HEXPIRETIME`, `HPEXPIRETIME`) were added in Redis 7.4. On older\n> Redis builds you would have to put streaming features on their own keys\n> (one key per feature, or one key per feature group) and set a key-level\n> `EXPIRE` instead — at the cost of giving up the single-`HMGET` retrieval."},{"id":"inference-reads-with-hmget","title":"Inference reads with HMGET","role":"content","text":"`GetFeatures` is one `HMGET`:\n\n[code example]\n\nThe model knows exactly which features it consumes, so the request path\nalways takes the `HMGET` branch with an explicit field list — that's the\nsub-millisecond path. `HGETALL` is the right call for debugging (which is\nwhat the demo's \"Inspect\" panel does) but not for serving: it forces Redis\nto serialize every field, including ones the model doesn't need.\n\nFields that don't exist (because they were never written, or because they\nexpired) come back as `nil` (a typed `nil`, not a `string` empty). The helper\ndrops them from the result map so the caller sees only the features that\nare actually available. A real model server would either treat missing\nvalues as a feature (\"this user has no streaming signal yet\") or fall back\nto a default from the model's training data."},{"id":"batch-scoring-with-pipelined-hmget","title":"Batch scoring with pipelined HMGET","role":"content","text":"For batch inference, the same `HMGET` shape pipelines across users:\n\n[code example]\n\nOne round trip for the whole batch — the demo regularly returns 100 users in\n1-2 ms against a local Redis. On a real network the round trip dominates;\npipelining is what keeps batch scoring practical.\n\nA Redis Cluster is different in two ways: a single `Pipeline.Exec` is bound\nto one shard, because non-cross-slot pipelines can only target one node; and\nthe keys for a typical user batch will land on multiple shards. For batch\nreads on a cluster, use the\n[`ClusterClient`](https://redis.io/docs/latest/develop/clients/go/connect) — its\n`Pipeline` knows how to dispatch per-shard, so you pay one round trip per\nshard rather than one for the whole batch. A hash tag like\n`fs:user:{vip}:u0001` forces a known set of keys onto the same shard so one\npipeline can cover all of them in a single round trip."},{"id":"the-streaming-worker","title":"The streaming worker","role":"content","text":"`streaming_worker.go` is the demo's stand-in for whatever Flink, Kafka\nStreams, or bespoke service computes the real-time features\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/feature-store/go/streaming_worker.go)).\nIt runs as a goroutine next to the demo server so the UI can start, pause,\nand resume it; in production this code would live in the streaming layer.\n\nEvery tick the worker picks a few random users, generates a new value for\neach streaming feature, and calls `store.UpdateStreaming(ctx, userID, fields, 0)`.\nThe demo defaults to 5 users per tick at 1-second intervals — so a 200-user\nstore sees roughly half its users refreshed in the first minute, and most\nafter a few minutes. Raise `--users-per-tick` or drop `--seed-users` if\nyou'd rather touch every user quickly.\n\n[code example]\n\nPausing the worker is what shows off the mixed-staleness behavior: leave it\npaused for longer than `streamingTTL` and the streaming fields disappear\nfrom every user's hash one by one, while the batch fields remain under the\nlonger key-level `EXPIRE`. The demo's `Pause / resume` button lets you see\nthis happen in real time.\n\n`Pause()` only blocks *future* ticks from running — the goroutine simply\nskips its turn on the next ticker fire. A reset that's about to `DEL` every\nkey needs to wait out an already-running tick too, which is what\n`WaitForIdle()` is for: the demo's `Reset` handler calls `worker.Pause()`\n*and* `worker.WaitForIdle()` before it issues the `DEL` sweep, so a\nmid-flight tick can't recreate a user under a streaming-only hash with no\nkey-level TTL."},{"id":"the-batch-builder","title":"The batch builder","role":"content","text":"`build_features.go` is the demo's nightly materializer\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/feature-store/go/build_features.go)).\nIt generates synthetic feature rows and calls `store.BulkLoad` once. The\nsynthesis itself is not the point — in a real deployment the equivalent\ncode reads from the offline store (Snowflake, BigQuery, Iceberg) and writes\nthe resulting hashes into Redis.\n\n[code example]\n\nYou can run the builder on its own (independently of the demo server) to\npopulate Redis from the command line:\n\n[code example]\n\nThat writes 500 users at `fs:user:*` with a one-hour key-level TTL, which is\nhow a typical operator would pre-seed a feature store from the command line\nwhen debugging."},{"id":"the-interactive-demo","title":"The interactive demo","role":"content","text":"`demo_server.go` runs a `net/http` server on port 8087. The HTML page lets\nyou:\n\n* **Bulk-load** any number of users (default 200) with a configurable\n  key-level TTL. Drop the TTL to 30 s and watch the entire store expire on\n  schedule — the same thing that happens if a daily refresher fails.\n* See the **store state** at a glance: user count, batch / streaming TTLs,\n  cumulative read/write counters.\n* See the **streaming worker** status (running / paused, ticks completed,\n  writes performed) and **pause or resume** it. Leave it paused for longer\n  than the streaming TTL to watch streaming fields drop out.\n* Run an **inference read** for any user with a chosen feature subset, and\n  see the value, the per-field TTL, and the read latency.\n* Run **batch scoring** with a pipelined `HMGET` across `N` users and see\n  the total elapsed time plus the per-user breakdown.\n* **Inspect** any user's full hash with field-level TTLs and the key-level\n  TTL — the right view for debugging \"why is this feature missing?\" at\n  read time.\n\nThe server holds one `FeatureStore` and one `StreamingWorker` for the\nlifetime of the process. Endpoints:\n\n| Endpoint                  | What it does                                                                        |\n|---------------------------|-------------------------------------------------------------------------------------|\n| `GET  /state`             | User count, TTL config, stats counters, worker status.                              |\n| `POST /bulk-load`         | Pipelined `HSET` + `EXPIRE` over N synthetic users with a chosen TTL.               |\n| `POST /worker/toggle`     | Pause / resume the streaming worker.                                                |\n| `POST /read`              | `HMGET` a chosen feature subset for one user; report latency and per-field TTLs.    |\n| `POST /batch-read`        | Pipeline `HMGET` across N users; report total latency and per-entity field counts.  |\n| `GET  /inspect`           | `HGETALL` + `HTTL` for one user; full hash view with per-field TTLs.                |\n| `POST /reset`             | Drop every user under the key prefix (used by the demo's reset button).             |"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* **Redis 7.4 or later.** [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) and\n  [`HTTL`](https://redis.io/docs/latest/commands/httl) were added in Redis 7.4; the\n  demo relies on per-field TTL for the mixed-staleness story.\n* **Go 1.21 or later.**\n* The `go-redis` v9 client. The demo's `go.mod` pins\n  `github.com/redis/go-redis/v9 v9.18.0` or later.\n\nIf your Redis server is running elsewhere, start the demo with `--redis-addr`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo lives in a small Go module under\n[`feature-store/go`](https://github.com/redis/docs/tree/main/content/develop/use-cases/feature-store/go).\nClone the repo or copy the directory:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From the module directory:\n\n[code example]\n\nYou should see:\n\n[code example]\n\nBy default the demo wipes the configured key prefix on startup so each run\nstarts from a clean state. Pass `--no-reset` to keep any existing data, or\n`--key-prefix <prefix>` to point the demo at a different prefix entirely.\n\nOpen [http://127.0.0.1:8087](http://127.0.0.1:8087) in a browser. Useful\nthings to try:\n\n* Pick a user and click **Read features** with a mixed batch/streaming\n  subset — you'll see batch fields with no per-field TTL (covered by the\n  key-level TTL) and streaming fields with a positive per-field TTL.\n* Click **Pipeline HMGET** with `count=100` to see the latency of a\n  100-user batch read.\n* Click **Pause / resume** on the streaming worker and leave it paused for\n  ~5 minutes (or restart the server with `--streaming-ttl-seconds 30` to\n  make it visible in seconds). Re-run **Read features** on any user and\n  watch the streaming fields disappear while the batch fields stay.\n* Click **Inspect** on a user to see the full hash with field-level TTLs.\n* Click **Bulk-load** with a short TTL (say 30 seconds) and watch the user\n  count fall to zero on the next minute — the same thing that happens if a\n  daily batch run fails to land.\n* Click **Reset** to drop every user and start over.\n\nThe server is read/write against your local Redis. The default key prefix\nis `fs:user:`. Pass `--no-reset` to keep existing data across restarts, or\n`--redis-addr` to point at a different Redis."},{"id":"production-usage","title":"Production usage","role":"content","text":"The guidance below focuses on the production concerns that are specific to\nrunning a feature store on Redis. For the generic go-redis production\nchecklist — connection-pool sizing, TLS, ACL, context cancellation, and\nretry policy — see the\n[go-redis production usage guide](https://redis.io/docs/latest/develop/clients/go/produsage)\nand the\n[connect-with-TLS recipe](https://redis.io/docs/latest/develop/clients/go/connect#connect-to-your-production-redis-with-tls).\nThe feature-store demo runs against `localhost` with the defaults; a real\ndeployment should harden the client first."},{"id":"plumb-the-right-context-to-each-call-site","title":"Plumb the right context to each call site","role":"content","text":"go-redis takes a `context.Context` on every command, and the right context\ndepends on the call site:\n\n* **Inference handlers**: pass `r.Context()` (the request context) into the\n  store calls. If the client hangs up, the in-flight `HMGET` is cancelled\n  promptly and the connection is returned to the pool — important under\n  sustained load.\n* **Background workers**: pass a server-lifetime context (a\n  `context.Background()`-derived one stored on the worker struct, as\n  `StreamingWorker` does). A worker driven off `r.Context()` would die on\n  the very next tick after its triggering request completes.\n* **Batch jobs**: a `context.WithTimeout` is the usual choice so a stuck\n  Redis can't hold the materialization pipeline open indefinitely."},{"id":"pick-the-batch-ttl-to-outlast-a-failed-refresher","title":"Pick the batch TTL to outlast a failed refresher","role":"content","text":"The whole-entity `EXPIRE` is your safety net against silent staleness from a\nbroken batch pipeline. Set it longer than your worst-case batch outage so a\nsingle missed run doesn't take the feature store offline, but short enough\nthat a sustained outage causes loud failures (missing entities) rather than\nquiet ones (yesterday's features being scored as today's). The standard\nchoice is one cycle of \"expected refresh interval × 2\" — for a daily batch,\n48 hours; for a 6-hour batch, 12 hours.\n\nThe same logic applies to the per-field streaming TTL: a few times the\nexpected update interval so a slow-but-alive streaming worker doesn't\nchurn features needlessly, but short enough that a stalled worker causes\nvisible freshness failures."},{"id":"co-locate-the-online-store-with-serving-not-with-training","title":"Co-locate the online store with serving, not with training","role":"content","text":"The online store's hash representation does *not* have to match the schema\nin your offline store. The batch materialization step is your chance to\nflatten joins, encode categoricals, and project to whatever shape the model\nserver wants — so the request path is exactly one `HMGET` and zero\ntransforms.\n\nThe training pipeline reads from the offline store with its own schema; the\nserving pipeline reads from Redis with the flattened serving schema.\nKeeping those two pipelines as the same code path is what prevents\ntraining-serving skew."},{"id":"pipeline-batch-reads-across-shards","title":"Pipeline batch reads across shards","role":"content","text":"On a single Redis instance, pipelining `HMGET` across `N` users through\n`Pipeline.Exec` is one round trip. A Redis Cluster is different: a single\n`Pipeline.Exec` is bound to one shard, because non-cross-slot pipelines can\nonly target one node, and the keys for a typical user batch will land on\nmultiple shards. For batch reads on a cluster, use the\n[`ClusterClient`](https://redis.io/docs/latest/develop/clients/go/connect) — its\n`Pipeline` knows how to bucket commands per-shard and ship one batch per\nshard in parallel. For a small number of frequently-queried users (a\ntop-N customer list, for example), a hash tag like `fs:user:{vip}:u0001`\nforces a known set of keys onto the same shard so one pipeline can cover\nall of them in a single round trip."},{"id":"make-hexpire-part-of-every-streaming-write","title":"Make HEXPIRE part of every streaming write","role":"content","text":"The single biggest correctness lever in this design is that the streaming\nwrite applies `HEXPIRE` *every time*. If a streaming worker writes a field\nwithout renewing its TTL, the field carries whatever expiry was there\nbefore — possibly none, possibly stale — and the mixed-staleness invariant\nbreaks. Keep the `HSET` and `HEXPIRE` in the same pipeline (or, even safer,\nin the same [Lua script](https://redis.io/docs/latest/develop/programmability/eval-intro)\nif you don't trust the call site)."},{"id":"avoid-hgetall-on-the-request-path","title":"Avoid HGETALL on the request path","role":"content","text":"`HGETALL` reads every field on the hash, including ones the model doesn't\nneed. With dozens of features per entity, that is wasted serialization work\non the server and wasted bandwidth on the wire. Always specify the field\nlist explicitly with `HMGet` in the model server.\n\nThe exception is debugging and feature-set discovery, where you genuinely\nwant the full hash. The demo's \"Inspect\" button uses `HGetAll` for exactly\nthis reason."},{"id":"inspect-the-store-directly-with-redis-cli","title":"Inspect the store directly with redis-cli","role":"content","text":"When testing or troubleshooting, the cli tells you everything:\n\n[code example]\n\nA streaming field that returns `-2` from `HTTL` doesn't exist on the hash\n(either it was never written, or it expired); `-1` means the field has no\nTTL set (and is therefore covered only by the key-level `EXPIRE`); any\npositive value is the remaining TTL in seconds."},{"id":"learn-more","title":"Learn more","role":"related","text":"This example uses the following Redis commands:\n\n* [`HSET`](https://redis.io/docs/latest/commands/hset) to write a feature or a whole\n  feature row in one call.\n* [`HMGET`](https://redis.io/docs/latest/commands/hmget) to retrieve any subset of\n  features for one entity in one round trip.\n* [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) for debugging and\n  feature-set discovery.\n* [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) and\n  [`HTTL`](https://redis.io/docs/latest/commands/httl) for per-field TTL on streaming\n  features (Redis 7.4+).\n* [`EXPIRE`](https://redis.io/docs/latest/commands/expire) and\n  [`TTL`](https://redis.io/docs/latest/commands/ttl) for the whole-entity TTL aligned\n  with the batch materialization cycle.\n* Pipelined `HMGET` across many entities for batch scoring with one network\n  round trip — see\n  [transactions and pipelining](https://redis.io/docs/latest/develop/clients/go/transpipe).\n\nSee the [go-redis documentation](https://redis.io/docs/latest/develop/clients/go) for\nthe full client reference, and the\n[Hashes overview](https://redis.io/docs/latest/develop/data-types/hashes) for the deeper\nconceptual model — including the listpack encoding that makes small hashes\nparticularly compact in memory, which matters at feature-store scale."}],"examples":[{"id":"the-feature-store-helper-ex0","language":"go","code":"package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/redis/go-redis/v9\"\n\tfs \"featurestore\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\trdb := redis.NewClient(&redis.Options{Addr: \"localhost:6379\"})\n\tdefer rdb.Close()\n\n\tstore := fs.NewFeatureStore(rdb,\n\t\t\"fs:user:\",\n\t\t24*time.Hour,   // whole-entity TTL aligned with the daily batch cycle\n\t\t5*time.Minute,  // per-field TTL on each streaming feature\n\t)\n\n\t// Batch materialization: one HSET + EXPIRE per user, all pipelined.\n\trows := map[string]fs.FeatureMap{\n\t\t\"u0001\": {\"country_iso\": \"US\", \"risk_segment\": \"low\",\n\t\t\t\"tx_count_7d\": 14, \"avg_amount_30d\": 92.40,\n\t\t\t\"account_age_days\": 612, \"chargeback_count_180d\": 0},\n\t\t\"u0002\": {\"country_iso\": \"GB\", \"risk_segment\": \"medium\",\n\t\t\t\"tx_count_7d\": 47, \"avg_amount_30d\": 220.10,\n\t\t\t\"account_age_days\": 1840, \"chargeback_count_180d\": 1},\n\t}\n\tstore.BulkLoad(ctx, rows, store.BatchTTL)\n\n\t// Streaming write: HSET + HEXPIRE on just the fields that changed.\n\tstore.UpdateStreaming(ctx, \"u0001\", fs.FeatureMap{\n\t\t\"last_login_ts\":     time.Now().UnixMilli(),\n\t\t\"last_device_id\":    \"ios-9f02\",\n\t\t\"tx_count_5m\":       3,\n\t\t\"failed_logins_15m\": 0,\n\t\t\"session_country\":   \"US\",\n\t}, store.StreamingTTL)\n\n\t// Inference read: HMGET of whatever the model needs.\n\tfeatures, _ := store.GetFeatures(ctx, \"u0001\", []string{\n\t\t\"risk_segment\", \"tx_count_7d\", \"avg_amount_30d\",\n\t\t\"tx_count_5m\", \"failed_logins_15m\",\n\t})\n\tfmt.Println(features)\n\n\t// Batch scoring: pipelined HMGET across many users.\n\tbatch, _ := store.BatchGetFeatures(ctx,\n\t\t[]string{\"u0001\", \"u0002\", \"u0003\"},\n\t\t[]string{\"risk_segment\", \"tx_count_5m\", \"failed_logins_15m\"},\n\t)\n\tfmt.Println(batch)\n}","section_id":"the-feature-store-helper"},{"id":"package-layout-ex0","language":"text","code":"feature-store/go/\n├── go.mod\n├── feature_store.go       (package featurestore)\n├── build_features.go      (package featurestore; SynthesizeUsers + CLI)\n├── streaming_worker.go    (package featurestore)\n├── demo_server.go         (package featurestore; RunDemoServer)\n└── cmd/\n    ├── build_features/main.go   (package main, shim → fs.BuildFeaturesCLI)\n    └── demo_server/main.go      (package main, shim → fs.RunDemoServer)","section_id":"package-layout"},{"id":"data-model-ex0","language":"text","code":"fs:user:u0001                                   TTL = 86400 s (key-level)\n  country_iso=US                                <no field TTL>\n  risk_segment=low                              <no field TTL>\n  account_age_days=612                          <no field TTL>\n  tx_count_7d=14                                <no field TTL>\n  avg_amount_30d=92.40                          <no field TTL>\n  chargeback_count_180d=0                       <no field TTL>\n  last_login_ts=1716998413541                   TTL = 300 s (per field, HEXPIRE)\n  last_device_id=ios-9f02                       TTL = 300 s (per field, HEXPIRE)\n  tx_count_5m=3                                 TTL = 300 s (per field, HEXPIRE)\n  failed_logins_15m=0                           TTL = 300 s (per field, HEXPIRE)\n  session_country=US                            TTL = 300 s (per field, HEXPIRE)","section_id":"data-model"},{"id":"bulk-loading-batch-features-ex0","language":"go","code":"func (fs *FeatureStore) BulkLoad(ctx context.Context, rows map[string]FeatureMap, ttl time.Duration) (int, error) {\n    if ttl == 0 {\n        ttl = fs.BatchTTL\n    }\n    if len(rows) == 0 {\n        return 0, nil\n    }\n    pipe := fs.rdb.Pipeline()\n    for entityID, fields := range rows {\n        key := fs.KeyFor(entityID)\n        encoded := make(map[string]any, len(fields))\n        for name, value := range fields {\n            encoded[name] = encode(value)\n        }\n        pipe.HSet(ctx, key, encoded)\n        pipe.Expire(ctx, key, ttl)\n    }\n    if _, err := pipe.Exec(ctx); err != nil {\n        return 0, fmt.Errorf(\"bulk load: %w\", err)\n    }\n    ...\n}","section_id":"bulk-loading-batch-features"},{"id":"streaming-writes-with-per-field-ttl-ex0","language":"go","code":"func (fs *FeatureStore) UpdateStreaming(ctx context.Context, entityID string, fields FeatureMap, ttl time.Duration) error {\n    if len(fields) == 0 {\n        return nil\n    }\n    if ttl == 0 {\n        ttl = fs.StreamingTTL\n    }\n    key := fs.KeyFor(entityID)\n    encoded := make(map[string]any, len(fields))\n    names := make([]string, 0, len(fields))\n    for name, value := range fields {\n        encoded[name] = encode(value)\n        names = append(names, name)\n    }\n    pipe := fs.rdb.Pipeline()\n    pipe.HSet(ctx, key, encoded)\n    hexpireCmd := pipe.HExpire(ctx, key, ttl, names...)\n    if _, err := pipe.Exec(ctx); err != nil {\n        return fmt.Errorf(\"update streaming: %w\", err)\n    }\n    codes, _ := hexpireCmd.Result()\n    for _, code := range codes {\n        if code != 1 {\n            return fmt.Errorf(\"HEXPIRE did not set every field TTL for %s: %v\", key, codes)\n        }\n    }\n    ...\n}","section_id":"streaming-writes-with-per-field-ttl"},{"id":"inference-reads-with-hmget-ex0","language":"go","code":"func (fs *FeatureStore) GetFeatures(ctx context.Context, entityID string, fieldNames []string) (map[string]string, error) {\n    key := fs.KeyFor(entityID)\n    if fieldNames == nil {\n        return fs.rdb.HGetAll(ctx, key).Result()\n    }\n    if len(fieldNames) == 0 {\n        return map[string]string{}, nil\n    }\n    values, err := fs.rdb.HMGet(ctx, key, fieldNames...).Result()\n    if err != nil {\n        return nil, err\n    }\n    out := make(map[string]string, len(fieldNames))\n    for i, name := range fieldNames {\n        if s, ok := values[i].(string); ok {\n            out[name] = s\n        }\n    }\n    return out, nil\n}","section_id":"inference-reads-with-hmget"},{"id":"batch-scoring-with-pipelined-hmget-ex0","language":"go","code":"func (fs *FeatureStore) BatchGetFeatures(ctx context.Context, entityIDs, fieldNames []string) (map[string]map[string]string, error) {\n    if len(entityIDs) == 0 || len(fieldNames) == 0 {\n        return map[string]map[string]string{}, nil\n    }\n    pipe := fs.rdb.Pipeline()\n    cmds := make([]*redis.SliceCmd, len(entityIDs))\n    for i, id := range entityIDs {\n        cmds[i] = pipe.HMGet(ctx, fs.KeyFor(id), fieldNames...)\n    }\n    if _, err := pipe.Exec(ctx); err != nil && !errors.Is(err, redis.Nil) {\n        return nil, err\n    }\n    out := make(map[string]map[string]string, len(entityIDs))\n    for i, id := range entityIDs {\n        values, _ := cmds[i].Result()\n        row := make(map[string]string, len(fieldNames))\n        for j, name := range fieldNames {\n            if s, ok := values[j].(string); ok {\n                row[name] = s\n            }\n        }\n        out[id] = row\n    }\n    return out, nil\n}","section_id":"batch-scoring-with-pipelined-hmget"},{"id":"the-streaming-worker-ex0","language":"go","code":"func (w *StreamingWorker) doTick(ctx context.Context) error {\n    ids, err := w.store.ListEntityIDs(ctx, 500)\n    if err != nil {\n        return err\n    }\n    if len(ids) == 0 {\n        return nil\n    }\n    n := w.usersPerTick\n    if n > len(ids) {\n        n = len(ids)\n    }\n    chosen := w.rng.Perm(len(ids))[:n]\n    nowMs := time.Now().UnixMilli()\n    for _, idx := range chosen {\n        fields := FeatureMap{\n            \"last_login_ts\":     nowMs,\n            \"last_device_id\":    w.choice(deviceIDs),\n            \"tx_count_5m\":       w.intn(13),\n            \"failed_logins_15m\": w.weightedInt(failedLoginBuckets, failedLoginWeights),\n            \"session_country\":   w.choice(sessionCountries),\n        }\n        if err := w.store.UpdateStreaming(ctx, ids[idx], fields, 0); err != nil {\n            return err\n        }\n    }\n    return nil\n}","section_id":"the-streaming-worker"},{"id":"the-batch-builder-ex0","language":"go","code":"func SynthesizeUsers(count int, seed int64) map[string]FeatureMap {\n    rng := rand.New(rand.NewSource(seed))\n    users := make(map[string]FeatureMap, count)\n    for i := 1; i <= count; i++ {\n        uid := fmt.Sprintf(\"u%04d\", i)\n        users[uid] = FeatureMap{\n            \"country_iso\":           countryChoices[rng.Intn(len(countryChoices))],\n            \"risk_segment\":          weightedChoiceString(rng, riskSegments, riskWeights),\n            \"account_age_days\":      rng.Intn(2400-7+1) + 7,\n            \"tx_count_7d\":           rng.Intn(81),\n            \"avg_amount_30d\":        roundTo2(rng.Float64()*345.0 + 5.0),\n            \"chargeback_count_180d\": weightedChoiceInt(rng, chargebackBuckets, chargebackWeights),\n        }\n    }\n    return users\n}","section_id":"the-batch-builder"},{"id":"the-batch-builder-ex1","language":"bash","code":"go run ./cmd/build_features --count 500 --ttl-seconds 3600","section_id":"the-batch-builder"},{"id":"get-the-source-files-ex0","language":"bash","code":"git clone https://github.com/redis/docs.git\ncd docs/content/develop/use-cases/feature-store/go\ngo mod tidy","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"go run ./cmd/demo_server","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"Dropping any existing users under 'fs:user:*' for a clean demo run (pass --no-reset to keep them).\nRedis feature-store demo server listening on http://127.0.0.1:8087\nUsing Redis at localhost:6379 with key prefix 'fs:user:' (batch TTL 86400s, streaming TTL 300s)\nMaterialized 200 user(s); streaming worker running.","section_id":"start-the-demo-server"},{"id":"inspect-the-store-directly-with-redis-cli-ex0","language":"bash","code":"# How many users currently in the store\nredis-cli --scan --pattern 'fs:user:*' | wc -l\n\n# One user's full hash and key-level TTL\nredis-cli HGETALL fs:user:u0001\nredis-cli TTL    fs:user:u0001\n\n# Per-field TTL on the streaming fields\nredis-cli HTTL fs:user:u0001 FIELDS 5 \\\n  last_login_ts last_device_id tx_count_5m failed_logins_15m session_country\n\n# Sample HMGET as the model would issue it\nredis-cli HMGET fs:user:u0001 risk_segment tx_count_7d avg_amount_30d tx_count_5m","section_id":"inspect-the-store-directly-with-redis-cli"}]}
{"schema_version":2,"id":"develop/use-cases/feature-store","title":"Redis feature store","url":"https://redis.io/docs/latest/develop/use-cases/feature-store/","summary":"Serve pre-computed ML features on the request path under tight latency budgets, with batch and streaming features kept fresh in the same store.","tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:50:30+01:00","children":[{"id":"develop/use-cases/feature-store/redis-py","summary":"Build a Redis-backed online feature store in Python with redis-py","title":"Redis feature store with redis-py","url":"https://redis.io/docs/latest/develop/use-cases/feature-store/redis-py/"},{"id":"develop/use-cases/feature-store/nodejs","summary":"Build a Redis-backed online feature store in Node.js with node-redis","title":"Redis feature store with node-redis","url":"https://redis.io/docs/latest/develop/use-cases/feature-store/nodejs/"},{"id":"develop/use-cases/feature-store/go","summary":"Build a Redis-backed online feature store in Go with go-redis","title":"Redis feature store with go-redis","url":"https://redis.io/docs/latest/develop/use-cases/feature-store/go/"},{"id":"develop/use-cases/feature-store/java-jedis","summary":"Build a Redis-backed online feature store in Java with Jedis","title":"Redis feature store with Jedis","url":"https://redis.io/docs/latest/develop/use-cases/feature-store/java-jedis/"},{"id":"develop/use-cases/feature-store/java-lettuce","summary":"Build a Redis-backed online feature store in Java with Lettuce","title":"Redis feature store with Lettuce","url":"https://redis.io/docs/latest/develop/use-cases/feature-store/java-lettuce/"},{"id":"develop/use-cases/feature-store/rust","summary":"Build a Redis-backed online feature store in Rust with redis-rs","title":"Redis feature store with redis-rs","url":"https://redis.io/docs/latest/develop/use-cases/feature-store/rust/"},{"id":"develop/use-cases/feature-store/dotnet","summary":"Build a Redis-backed online feature store in .NET with StackExchange.Redis","title":"Redis feature store with StackExchange.Redis","url":"https://redis.io/docs/latest/develop/use-cases/feature-store/dotnet/"},{"id":"develop/use-cases/feature-store/ruby","summary":"Build a Redis-backed online feature store in Ruby with redis-rb","title":"Redis feature store with redis-rb","url":"https://redis.io/docs/latest/develop/use-cases/feature-store/ruby/"},{"id":"develop/use-cases/feature-store/php","summary":"Build a Redis-backed online feature store in PHP with Predis","title":"Redis feature store with Predis","url":"https://redis.io/docs/latest/develop/use-cases/feature-store/php/"}],"page_type":"content","content_hash":"b571c96203a605dbfbc50876cc1f4d654858ebce712970d38e7cf3b6fcbc2d8f","sections":[{"id":"when-to-use-redis-as-a-feature-store","title":"When to use Redis as a feature store","role":"overview","text":"Use Redis as the online layer of a feature store when production models — fraud\nscoring, recommendations, dynamic pricing — need dozens of pre-computed features\nper prediction on every request, with sub-millisecond reads, mixed batch-and-streaming\nfreshness, and high write throughput from concurrent ingestion pipelines."},{"id":"why-the-problem-is-hard","title":"Why the problem is hard","role":"content","text":"An online feature store has to serve dozens of features per inference call inside\na request budget measured in milliseconds, while batch jobs and streaming\npipelines update those same features at very different cadences. Some of the\nobvious workarounds have real drawbacks:\n\n-   **Querying the offline warehouse directly** adds hundreds of milliseconds per\n    inference call, which makes real-time serving impossible.\n-   **A bespoke cache in front of the warehouse** solves latency but introduces\n    *training-serving skew*: the features served at inference drift from what the\n    model trained on, silently degrading accuracy whenever a transform changes\n    on one side and not the other.\n-   **Disk-backed online stores** hit a throughput wall when every user action\n    has to update a dozen features simultaneously across millions of entities —\n    the I/O mix of small concurrent writes is exactly what they are slowest at.\n-   **Single-TTL stores** can't handle mixed staleness: batch features refreshed\n    nightly coexist with streaming features updated every few seconds, and a\n    single per-key expiry can't express both. Worse, a failed ingestion\n    pipeline must *expire* its features rather than serve stale values silently.\n\nA workable online feature store needs sub-millisecond reads at request rate,\nhigh concurrent write throughput from mixed batch and streaming ingestion,\nindependent freshness controls per feature, and self-cleaning behavior when an\nupstream pipeline fails — without standing up a dedicated piece of\ninfrastructure beside the rest of the model-serving stack."},{"id":"what-you-can-expect-from-a-redis-solution","title":"What you can expect from a Redis solution","role":"content","text":"You can:\n\n-   Serve feature vectors to inference endpoints under 1 ms P99\n    (99% of requests have a latency of 1 ms or less) at millions of\n    reads per second from a single shard, and scale horizontally beyond that\n    with Redis Cluster.\n-   Run batch and streaming ingestion concurrently against the same entities\n    without locking or version columns — Redis is single-threaded per shard, so\n    individual field writes are atomic by construction.\n-   Apply *different* freshness guarantees to individual features within the same\n    entity hash: seconds for real-time signals, hours for batch aggregates, with\n    per-field TTL via [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire).\n-   Let stale streaming features self-expire when their ingestion pipeline\n    fails, so models receive missing features rather than silently outdated ones.\n-   Retrieve features for hundreds of entities in a single round trip for batch\n    scoring, using pipelined [`HMGET`](https://redis.io/docs/latest/commands/hmget).\n-   Plug into [Redis Feature Form](https://redis.io/docs/latest/develop/ai/featureform) —\n    Redis's own materialize / serve layer — or\n    [Feast](https://docs.feast.dev/) with a connection-string change, so no\n    bespoke serving code is required.\n-   Co-locate the online feature store on the same Redis instance already\n    handling cache, sessions, or rate limiting in the stack — no additional\n    infrastructure."},{"id":"how-redis-supports-the-solution","title":"How Redis supports the solution","role":"content","text":"In practice, each entity (a user, an account, an item) is a single\n[Hash](https://redis.io/docs/latest/develop/data-types/hashes) at a deterministic key like\n`fs:user:{id}`. The hash holds every feature for that entity as one field per\nfeature — batch-materialized aggregates alongside streaming-updated signals —\nso one [`HMGET`](https://redis.io/docs/latest/commands/hmget) call returns whatever subset\nthe model needs in one round trip. A key-level\n[`EXPIRE`](https://redis.io/docs/latest/commands/expire) aligns with the batch\nmaterialization cycle so a whole entity self-cleans when its pipeline stops\nrefreshing it, and per-field [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire)\nlets each streaming feature carry its own shorter expiry independent of the\nrest of the hash.\n\nRedis provides the following features that make it a good fit for an online\nfeature store:\n\n-   [Hashes](https://redis.io/docs/latest/develop/data-types/hashes) group every feature\n    for an entity under one key, so retrieval reads everything the model needs\n    in a single network round trip with [`HMGET`](https://redis.io/docs/latest/commands/hmget),\n    and small hashes use *listpack* encoding for compact in-memory representation.\n-   [`HSET`](https://redis.io/docs/latest/commands/hset) writes any subset of fields\n    atomically, so batch and streaming pipelines can update overlapping or\n    disjoint features on the same entity concurrently without locks or version\n    columns.\n-   [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) and\n    [`HTTL`](https://redis.io/docs/latest/commands/httl) (Redis 7.4+) give per-field TTLs,\n    so streaming features (5-minute freshness) and batch features (24-hour\n    freshness) can live in the same hash with independent expiry — the\n    *mixed-staleness* problem becomes a one-line server-side guarantee.\n-   [`EXPIRE`](https://redis.io/docs/latest/commands/expire) at the key level lets an\n    entity disappear entirely if its batch refresher fails, so inference sees\n    a missing entity (which the model handler can detect and fall back on)\n    rather than silently outdated values.\n-   [Pipelining](https://redis.io/docs/latest/develop/using-commands/pipelining) bundles\n    [`HMGET`](https://redis.io/docs/latest/commands/hmget) calls for many entities into\n    one round trip, which is the right primitive for batch scoring where the\n    model needs features for hundreds of entities at once.\n-   Sub-millisecond reads and writes from memory keep the feature store off the\n    critical path of inference, so the model-server's request budget is spent\n    on the model rather than on feature retrieval."},{"id":"ecosystem","title":"Ecosystem","role":"content","text":"The following libraries and platforms use Redis as their online feature store:\n\n-   **[Redis Feature Form](https://redis.io/docs/latest/develop/ai/featureform)** is\n    Redis's own feature-engineering platform. It defines features, labels, and\n    feature views in a Python definitions file, materializes them through a\n    [registered provider](https://redis.io/docs/latest/develop/ai/featureform/register-providers),\n    and [serves](https://redis.io/docs/latest/develop/ai/featureform/serve-features)\n    them from Redis as the low-latency online store. See the\n    [quickstart](https://redis.io/docs/latest/develop/ai/featureform/quickstart) for an\n    end-to-end walkthrough.\n-   **Python**: [Feast](https://docs.feast.dev/reference/online-stores/redis)\n    ships Redis as a first-class online store provider — point a Feast\n    `online_store` block at a Redis connection string and the\n    `RedisOnlineStore` backend handles materialization and serving.\n-   **Compute**: [Apache Spark](https://spark.apache.org/) batch jobs run the\n    nightly materialization, writing into Redis via the Redis Feature Form /\n    Feast materialize commands or directly with the\n    [`spark-redis`](https://github.com/RedisLabs/spark-redis) connector.\n-   **Streaming**: [Apache Flink](https://flink.apache.org/) or\n    [Kafka Streams](https://kafka.apache.org/documentation/streams/) compute the\n    real-time features and `HSET` them into Redis with per-field\n    [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) so each streaming signal\n    carries its own freshness window.\n-   **Infrastructure**: [Kubernetes](https://kubernetes.io/) co-locates Redis\n    pods alongside the model-serving containers, with horizontal-pod autoscaling\n    on the read replicas to track inference load;\n    [Active-Active geo-distribution](https://redis.io/docs/latest/operate/rs/databases/active-active)\n    on Redis Enterprise / Redis Cloud replicates the online store across\n    regions for low-latency reads close to each inference cluster."},{"id":"code-examples-to-build-your-own-redis-feature-store","title":"Code examples to build your own Redis feature store","role":"example","text":"The following guides show how to build a small Redis-backed online feature\nstore for a fraud-scoring model. Each guide includes a runnable interactive\ndemo that lets you bulk-load batch features, run a streaming worker that\nupdates real-time features with per-field TTL, retrieve any subset of features\nfor a single user under 1 ms, and pipeline batch reads across a hundred users.\n\n* [redis-py (Python)](https://redis.io/docs/latest/develop/use-cases/feature-store/redis-py)\n* [node-redis (Node.js)](https://redis.io/docs/latest/develop/use-cases/feature-store/nodejs)\n* [go-redis (Go)](https://redis.io/docs/latest/develop/use-cases/feature-store/go)\n* [Jedis (Java)](https://redis.io/docs/latest/develop/use-cases/feature-store/java-jedis)\n* [Lettuce (Java)](https://redis.io/docs/latest/develop/use-cases/feature-store/java-lettuce)\n* [redis-rs (Rust)](https://redis.io/docs/latest/develop/use-cases/feature-store/rust)\n* [StackExchange.Redis (C#)](https://redis.io/docs/latest/develop/use-cases/feature-store/dotnet)\n* [Predis (PHP)](https://redis.io/docs/latest/develop/use-cases/feature-store/php)\n* [redis-rb (Ruby)](https://redis.io/docs/latest/develop/use-cases/feature-store/ruby)"}],"examples":[]}
{"schema_version":2,"id":"develop/use-cases/feature-store/java-jedis","title":"Redis feature store with Jedis","url":"https://redis.io/docs/latest/develop/use-cases/feature-store/java-jedis/","summary":"Build a Redis-backed online feature store in Java with Jedis","aliases":["/develop/use-cases/feature-store/java","/develop/use-cases/feature-store/jedis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:50:30+01:00","children":[],"page_type":"content","content_hash":"547ca1bf4966c3b1e689c64721c787c7a2c957a6e49752471f947e28f086271a","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a small Redis-backed online feature store in\nJava with [Jedis](https://redis.io/docs/latest/develop/clients/jedis). It includes a\nlocal web server built with the JDK's `com.sun.net.httpserver.HttpServer` so\nyou can bulk-load a batch of users with a key-level TTL, run a streaming\nworker that overwrites real-time features with per-field TTL, retrieve any\nsubset of features for one user under 2 ms, and pipeline `HMGET` across a\nhundred users for batch scoring."},{"id":"overview","title":"Overview","role":"overview","text":"Each entity (here, a user) is one Redis\n[Hash](https://redis.io/docs/latest/develop/data-types/hashes) at a deterministic key —\n`fs:user:{id}`. The hash holds every feature for that entity as one field per\nfeature: batch-materialized aggregates (refreshed once a day) alongside\nstreaming-updated signals (refreshed every few seconds). One\n[`HMGET`](https://redis.io/docs/latest/commands/hmget) returns whichever subset the model\nneeds in one network round trip.\n\nTwo TTL layers solve the *mixed staleness* problem without an application-side\ncleaner:\n\n* A **key-level** [`EXPIRE`](https://redis.io/docs/latest/commands/expire) aligned with the\n  batch materialization cycle (24 hours in the demo). If the batch refresher\n  fails, the whole entity disappears at the next cycle and inference sees a\n  missing entity — which the model handler can detect and fall back on —\n  rather than silently outdated values.\n* A **per-field** [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) (Redis 7.4+) on\n  each streaming feature gives that field its own shorter expiry, independent\n  of the rest of the hash. If the streaming pipeline stops updating a feature,\n  the field self-cleans while the batch fields stay populated.\n\nIn this example, the batch features describe a user's longer-term shape\n(`country_iso`, `risk_segment`, `account_age_days`, `tx_count_7d`,\n`avg_amount_30d`, `chargeback_count_180d`) and are bulk-loaded by\n`BuildFeatures.java` — the demo's stand-in for a nightly Spark / Feast\nmaterialization job. The streaming features describe what the user is doing\nright now (`last_login_ts`, `last_device_id`, `tx_count_5m`,\n`failed_logins_15m`, `session_country`) and are written by\n`StreamingWorker.java` — the demo's stand-in for a Flink / Kafka Streams job.\nThe inference handlers of the demo server read any subset of those features\nthrough `FeatureStore.java`'s helper class.\n\nThat gives you:\n\n* A single round trip for retrieval — any subset of features for one entity\n  in one [`HMGET`](https://redis.io/docs/latest/commands/hmget).\n* Sub-millisecond hot path. The Redis-side work is microseconds; in practice\n  the bottleneck is the network round trip plus the model's own feature-prep.\n* Pipelined batch scoring — one round trip for `N` users at once.\n* Independent freshness per feature, expressed as a server-side TTL rather\n  than as application logic.\n* Self-cleanup on pipeline failure: a stalled batch refresher lets entities\n  expire on schedule, and a stalled streaming worker lets each affected field\n  expire on its own timer."},{"id":"how-it-works","title":"How it works","role":"content","text":"There are three paths: a **batch path** that bulk-loads features once per\nmaterialization cycle, a **streaming path** that updates real-time features\nas events arrive, and an **inference path** that reads features on the\nrequest side."},{"id":"batch-path-per-materialization-cycle","title":"Batch path (per materialization cycle)","role":"content","text":"1. The batch job calls `BuildFeatures.synthesizeUsers(N, seed)` (in\n   production, the equivalent computation lives in an offline pipeline against\n   the warehouse). The result is `Map<String, Map<String, Object>>` keyed by\n   user ID.\n2. `store.bulkLoad(rows, ttlSeconds)` queues one\n   [`HSET`](https://redis.io/docs/latest/commands/hset) plus one\n   [`EXPIRE`](https://redis.io/docs/latest/commands/expire) per user through Jedis's\n   [`Pipeline`](https://redis.io/docs/latest/develop/clients/jedis/transpipe), then\n   `pipe.sync()` ships the whole batch in a single round trip. The `HSET`\n   writes every batch field; the `EXPIRE` is what makes the entity disappear\n   if the next batch run fails, so inference reads a missing entity rather\n   than silently outdated values."},{"id":"streaming-path-per-event","title":"Streaming path (per event)","role":"content","text":"When a user does something (login, transaction, page view) the streaming\nlayer computes whatever real-time signals fall out of that event and calls\n`store.updateStreaming(userId, fields, ttlSeconds)`. That batches:\n\n1. An [`HSET`](https://redis.io/docs/latest/commands/hset) writing the new field values.\n   Redis is single-threaded per shard, so this is atomic against any\n   concurrent batch write on the same hash — no version columns, no locks.\n2. An [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) over exactly the fields\n   that were written, with the streaming TTL. Each streaming field carries\n   its own per-field expiry independent of the rest of the hash. Stop the\n   worker and these fields drop out one by one as their TTLs elapse, while\n   the batch fields remain populated under the longer key-level TTL."},{"id":"inference-path-per-request","title":"Inference path (per request)","role":"content","text":"1. The model server picks the feature subset it needs (the schema is owned by\n   the model, not the store).\n2. It calls `store.getFeatures(userId, names)`, which is one\n   [`HMGET`](https://redis.io/docs/latest/commands/hmget). Redis returns the values in\n   the same order as the requested fields, with `null` for any field that\n   doesn't exist (or has expired).\n3. For batch inference, the model server calls\n   `store.batchGetFeatures(userIds, names)`, which pipelines one\n   [`HMGET`](https://redis.io/docs/latest/commands/hmget) per user across all `N` users\n   in a single network round trip via Jedis's `Pipeline.sync()`."},{"id":"the-feature-store-helper","title":"The feature-store helper","role":"content","text":"The `FeatureStore` class wraps the read/write paths\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/feature-store/java-jedis/FeatureStore.java)):\n\n[code example]"},{"id":"project-layout","title":"Project layout","role":"content","text":"The four `.java` files and the `pom.xml` live in the same directory — the\n`build-helper-maven-plugin` adds the project root as the source directory so\nthe Java sources sit alongside the build descriptor. Run with:\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each user is one Redis Hash. Every value is stored as a string — Redis hash\nfields are bytes on the wire, so the helper encodes booleans as `\"true\"` /\n`\"false\"` (`encodeValue(Object)` in `FeatureStore.java`) and renders\neverything else with `Object.toString()`. The model server is responsible for\nparsing back to the right type, the same way it would when reading any\nserialized feature store.\n\n[code example]\n\nThe batch fields sit under the key-level `EXPIRE`. The streaming fields each\ncarry their own [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire). If the\nstreaming pipeline stops, the streaming fields drop one by one as their\nper-field TTLs elapse; the batch fields stay until the daily key-level\n`EXPIRE` fires (or the next batch cycle re-pins them)."},{"id":"bulk-loading-batch-features","title":"Bulk-loading batch features","role":"content","text":"`bulkLoad` queues one `HSET` and one `EXPIRE` per user into a single\n`Pipeline` and calls `sync()` to ship the lot. With 500 users that's 1000\ncommands in one network call — Redis processes them sequentially on the\nserver side but the client only pays one RTT.\n\n[code example]\n\nJedis's `pipelined()` is a non-transactional batch: commands queue up and\nship in one round trip, but they don't run inside a `MULTI/EXEC` block.\nThat's the right choice here because each user's `HSET` + `EXPIRE` pair is\nindependent of every other user's, and an all-or-nothing transaction would\nblock the server for the duration of the batch. For the rare case where the\npair has to be inseparable (a server crash between the two would leave the\nentity without a key-level TTL) you'd wrap each user in a `Transaction` or a\n[Lua script](https://redis.io/docs/latest/develop/programmability/eval-intro); for a\ndaily ingestion job that runs end-to-end every cycle, the next run re-pins\nthe TTL — no extra machinery needed.\n\nIn production, the equivalent of this script runs as an offline pipeline (a\nSpark or Feast `materialize` job) that reads from the warehouse and writes\ninto Redis. The\n[Feast `RedisOnlineStore`](https://docs.feast.dev/reference/online-stores/redis)\nprovider does exactly this under the hood; the in-house\n[Redis Feature Form](https://redis.io/docs/latest/develop/ai/featureform) integration\ncovers the materialize + serve path end-to-end."},{"id":"streaming-writes-with-per-field-ttl","title":"Streaming writes with per-field TTL","role":"content","text":"`updateStreaming` is the linchpin of the mixed-staleness story:\n\n[code example]\n\n[`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) sets the TTL on *individual*\nhash fields, not on the whole key. The two commands are queued in one\n`Pipeline` and Redis runs them in order: the `HSET` first creates or\noverwrites the fields, then `HEXPIRE` attaches a TTL to each of those same\nfields. `HEXPIRE` returns one status code per field — `1` if the TTL was\nset, `2` if the expiry was 0 or in the past (so Redis deleted the field\ninstead), `0` if an `NX | XX | GT | LT` conditional flag was set and not met\n(we never use one here), `-2` if the field doesn't exist on the key. The\nhelper throws if any code is anything other than `1`, so the \"every\nstreaming write renews its TTL\" invariant fails loudly rather than silently\nleaving a streaming field with no expiry attached.\n\n`Response<List<Long>>` is Jedis's deferred-result wrapper for pipelined\ncommands: queue the command, call `pipe.sync()` to ship the batch, then read\neach result via `.get()`. The `Response` for `hexpire` returns the per-field\ncodes; that list is what the helper validates above.\n\nIf a streaming pipeline stops, the streaming fields drop out one by one as\ntheir per-field TTLs elapse — there is no application-side cleaner involved.\n[`HTTL`](https://redis.io/docs/latest/commands/httl) lets the model side inspect the\nremaining TTL on any field, which is useful both for debugging (\"why is this\nfeature missing?\" → \"it expired three seconds ago\") and as a freshness signal\nin the model itself.\n\n> **HEXPIRE requires Redis 7.4 or later.** `HEXPIRE` and the field-level TTL\n> commands (`HTTL`, `HPERSIST`, `HEXPIREAT`, `HPEXPIRE`, `HPEXPIREAT`,\n> `HPTTL`, `HEXPIRETIME`, `HPEXPIRETIME`) were added in Redis 7.4. Jedis 5.2\n> was the first release with the bindings; the demo's `pom.xml` pins 6.2.\n> On older Redis builds you would have to put streaming features on their\n> own keys (one key per feature, or one key per feature group) and set a\n> key-level `EXPIRE` instead — at the cost of giving up the single-`HMGET`\n> retrieval."},{"id":"inference-reads-with-hmget","title":"Inference reads with HMGET","role":"content","text":"`getFeatures` is one `HMGET`:\n\n[code example]\n\nThe model knows exactly which features it consumes, so the request path\nalways takes the `HMGET` branch with an explicit field list — that's the\nsub-millisecond path. `HGETALL` is the right call for debugging (which is\nwhat the demo's \"Inspect\" panel does) but not for serving: it forces Redis\nto serialize every field, including ones the model doesn't need.\n\nFields that don't exist (because they were never written, or because they\nexpired) come back as `null` in the `List<String>` Jedis returns. The helper\ndrops them from the result `Map` so the caller sees only the features that\nare actually available. A real model server would either treat missing\nvalues as a feature (\"this user has no streaming signal yet\") or fall back\nto a default from the model's training data."},{"id":"batch-scoring-with-pipelined-hmget","title":"Batch scoring with pipelined HMGET","role":"content","text":"For batch inference, the same `HMGET` shape pipelines across users:\n\n[code example]\n\nOne round trip for the whole batch — the demo regularly returns 30 users in\n~1 ms against a local Redis. On a real network the round trip dominates;\npipelining is what keeps batch scoring practical.\n\nA Redis Cluster is different: a single `Pipeline.sync()` is bound to one\nshard, because cross-slot pipelines on a cluster connection don't make sense.\nFor batch reads on a cluster, use\n[`JedisCluster`](https://redis.io/docs/latest/develop/clients/jedis) and either fan out\nparallel `hmget` calls (the cluster client routes per-shard for you) or, for\ntighter control, group the IDs by hash slot ahead of time and issue one\n`Pipeline.sync()` against each shard's connection in parallel. A hash tag\nlike `fs:user:{vip}:u0001` forces a known set of keys onto the same shard so\none pipeline can cover all of them in a single round trip."},{"id":"the-streaming-worker","title":"The streaming worker","role":"content","text":"`StreamingWorker.java` is the demo's stand-in for whatever Flink, Kafka\nStreams, or bespoke service computes the real-time features\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/feature-store/java-jedis/StreamingWorker.java)).\nIt runs as a daemon `Thread` next to the demo server so the UI can start,\npause, and resume it; in production this code would live in the streaming\nlayer.\n\nEvery tick the worker picks a few random users, generates a new value for\neach streaming feature, and calls `store.updateStreaming(userId, fields)`.\nThe demo defaults to 5 users per tick at 1-second intervals — so a 200-user\nstore sees roughly half its users refreshed in the first minute, and most\nafter a few minutes. Raise `--users-per-tick` or drop `--seed-users` if\nyou'd rather touch every user quickly.\n\n[code example]\n\nPausing the worker is what shows off the mixed-staleness behavior: leave it\npaused for longer than `streamingTtlSeconds` and the streaming fields\ndisappear from every user's hash one by one, while the batch fields remain\nunder the longer key-level `EXPIRE`. The demo's `Pause / resume` button lets\nyou see this happen in real time.\n\n`pause()` only blocks *future* ticks from running — the thread checks the\nflag at the top of the loop and skips its turn. A reset that's about to\n`DEL` every key needs to wait out an already-running tick too, which is\nwhat `waitForIdle()` is for: the demo's `Reset` handler calls\n`worker.pause()` *and* `worker.waitForIdle()` before it issues the `DEL`\nsweep, so a mid-flight tick can't recreate a user under a streaming-only\nhash with no key-level TTL."},{"id":"the-batch-builder","title":"The batch builder","role":"content","text":"`BuildFeatures.java` is the demo's nightly materializer\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/feature-store/java-jedis/BuildFeatures.java)).\nIt generates synthetic feature rows and calls `store.bulkLoad` once. The\nsynthesis itself is not the point — in a real deployment the equivalent code\nreads from the offline store (Snowflake, BigQuery, Iceberg) and writes the\nresulting hashes into Redis.\n\n[code example]\n\nYou can run the builder on its own (independently of the demo server) to\npopulate Redis from the command line:\n\n[code example]\n\nThat writes 500 users at `fs:user:*` with a one-hour key-level TTL, which is\nhow a typical operator would pre-seed a feature store from the command line\nwhen debugging."},{"id":"the-interactive-demo","title":"The interactive demo","role":"content","text":"`DemoServer.java` runs the JDK `HttpServer` on port 8088 with a fixed thread\npool. The HTML page lets you:\n\n* **Bulk-load** any number of users (default 200) with a configurable\n  key-level TTL. Drop the TTL to 30 s and watch the entire store expire on\n  schedule — the same thing that happens if a daily refresher fails.\n* See the **store state** at a glance: user count, batch / streaming TTLs,\n  cumulative read/write counters.\n* See the **streaming worker** status (running / paused, ticks completed,\n  writes performed) and **pause or resume** it. Leave it paused for longer\n  than the streaming TTL to watch streaming fields drop out.\n* Run an **inference read** for any user with a chosen feature subset, and\n  see the value, the per-field TTL, and the read latency.\n* Run **batch scoring** with a pipelined `HMGET` across `N` users and see\n  the total elapsed time plus the per-user breakdown.\n* **Inspect** any user's full hash with field-level TTLs and the key-level\n  TTL — the right view for debugging \"why is this feature missing?\" at\n  read time.\n\nThe server holds one `FeatureStore` and one `StreamingWorker` for the\nlifetime of the process, plus a `JedisPool` that all handlers borrow\nconnections from. Endpoints:\n\n| Endpoint                  | What it does                                                                        |\n|---------------------------|-------------------------------------------------------------------------------------|\n| `GET  /state`             | User count, TTL config, stats counters, worker status.                              |\n| `POST /bulk-load`         | Pipelined `HSET` + `EXPIRE` over N synthetic users with a chosen TTL.               |\n| `POST /worker/toggle`     | Pause / resume the streaming worker.                                                |\n| `POST /read`              | `HMGET` a chosen feature subset for one user; report latency and per-field TTLs.    |\n| `POST /batch-read`        | Pipeline `HMGET` across N users; report total latency and per-entity field counts.  |\n| `GET  /inspect`           | `HGETALL` + `HTTL` for one user; full hash view with per-field TTLs.                |\n| `POST /reset`             | Drop every user under the key prefix (used by the demo's reset button).             |"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* **Redis 7.4 or later.** [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) and\n  [`HTTL`](https://redis.io/docs/latest/commands/httl) were added in Redis 7.4; the\n  demo relies on per-field TTL for the mixed-staleness story.\n* **Java 17 or later.** The demo uses switch expressions with arrow\n  labels (`case \"...\" -> ...`), records, and text blocks.\n* **Jedis 5.2 or later.** The demo's `pom.xml` pins\n  `redis.clients:jedis:6.2.0`. Field-level TTL bindings (`hexpire`, `httl`,\n  `hpersist`) ship from Jedis 5.2.\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host`\nand `--redis-port`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo lives in a small Maven project under\n[`feature-store/java-jedis`](https://github.com/redis/docs/tree/main/content/develop/use-cases/feature-store/java-jedis).\nClone the repo or copy the directory:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From the project directory:\n\n[code example]\n\nYou should see:\n\n[code example]\n\nBy default the demo wipes the configured key prefix on startup so each run\nstarts from a clean state. Pass `--no-reset` to keep any existing data, or\n`--key-prefix <prefix>` to point the demo at a different prefix entirely.\nMaven exec passes CLI args via `-Dexec.args`:\n\n[code example]\n\nOpen [http://127.0.0.1:8088](http://127.0.0.1:8088) in a browser. Useful\nthings to try:\n\n* Pick a user and click **Read features** with a mixed batch/streaming\n  subset — you'll see batch fields with no per-field TTL (covered by the\n  key-level TTL) and streaming fields with a positive per-field TTL.\n* Click **Pipeline HMGET** with `count=100` to see the latency of a\n  100-user batch read.\n* Click **Pause / resume** on the streaming worker and leave it paused for\n  ~5 minutes (or restart the server with `--streaming-ttl-seconds 30` to\n  make it visible in seconds). Re-run **Read features** on any user and\n  watch the streaming fields disappear while the batch fields stay.\n* Click **Inspect** on a user to see the full hash with field-level TTLs.\n* Click **Bulk-load** with a short TTL (say 30 seconds) and watch the user\n  count fall to zero on the next minute — the same thing that happens if a\n  daily batch run fails to land.\n* Click **Reset** to drop every user and start over.\n\nThe server is read/write against your local Redis. The default key prefix\nis `fs:user:`. Pass `--no-reset` to keep existing data across restarts, or\n`--redis-host` / `--redis-port` to point at a different Redis."},{"id":"production-usage","title":"Production usage","role":"content","text":"The guidance below focuses on the production concerns that are specific to\nrunning a feature store on Redis. For the generic Jedis production checklist\n— `JedisPool` sizing, AUTH/ACL, retry policy, sentinel/cluster failover —\nsee the\n[Jedis production usage guide](https://redis.io/docs/latest/develop/clients/jedis/produsage).\nFor TLS specifically, follow the\n[connect-with-TLS recipe](https://redis.io/docs/latest/develop/clients/jedis/connect#connect-to-your-production-redis-with-tls).\nThe feature-store demo runs against `localhost` with the defaults; a real\ndeployment should harden the client first."},{"id":"pick-the-batch-ttl-to-outlast-a-failed-refresher","title":"Pick the batch TTL to outlast a failed refresher","role":"content","text":"The whole-entity `EXPIRE` is your safety net against silent staleness from a\nbroken batch pipeline. Set it longer than your worst-case batch outage so a\nsingle missed run doesn't take the feature store offline, but short enough\nthat a sustained outage causes loud failures (missing entities) rather than\nquiet ones (yesterday's features being scored as today's). The standard\nchoice is one cycle of \"expected refresh interval × 2\" — for a daily batch,\n48 hours; for a 6-hour batch, 12 hours.\n\nThe same logic applies to the per-field streaming TTL: a few times the\nexpected update interval so a slow-but-alive streaming worker doesn't churn\nfeatures needlessly, but short enough that a stalled worker causes visible\nfreshness failures."},{"id":"co-locate-the-online-store-with-serving-not-with-training","title":"Co-locate the online store with serving, not with training","role":"content","text":"The online store's hash representation does *not* have to match the schema\nin your offline store. The batch materialization step is your chance to\nflatten joins, encode categoricals, and project to whatever shape the model\nserver wants — so the request path is exactly one `HMGET` and zero\ntransforms.\n\nThe training pipeline reads from the offline store with its own schema; the\nserving pipeline reads from Redis with the flattened serving schema.\nKeeping those two pipelines as the same code path is what prevents\ntraining-serving skew."},{"id":"pipeline-batch-reads-across-shards","title":"Pipeline batch reads across shards","role":"content","text":"On a single Redis instance, `Pipeline.sync()` across `N` `hmget` calls is\none round trip. A Redis Cluster is different: a single `Pipeline.sync()` is\nbound to one shard, because cross-slot pipelines on a cluster connection\ndon't make sense, and the keys for a typical user batch will land on\nmultiple shards. For batch reads on a cluster, use\n[`JedisCluster`](https://redis.io/docs/latest/develop/clients/jedis) — its\nimplementation routes per-shard for you. For tighter control, group the IDs\nby hash slot ahead of time and issue one `Pipeline.sync()` per shard's\nconnection in parallel. For a small number of frequently-queried users (a\ntop-N customer list, for example), a hash tag like `fs:user:{vip}:u0001`\nforces a known set of keys onto the same shard so one pipeline can cover\nall of them in a single round trip."},{"id":"make-hexpire-part-of-every-streaming-write","title":"Make HEXPIRE part of every streaming write","role":"content","text":"The single biggest correctness lever in this design is that the streaming\nwrite applies `HEXPIRE` *every time*. If a streaming worker writes a field\nwithout renewing its TTL, the field carries whatever expiry was there before\n— possibly none, possibly stale — and the mixed-staleness invariant breaks.\nKeep the `HSET` and `HEXPIRE` in the same pipeline (or, even safer, in the\nsame [Lua script](https://redis.io/docs/latest/develop/programmability/eval-intro) if\nyou don't trust the call site)."},{"id":"avoid-hgetall-on-the-request-path","title":"Avoid HGETALL on the request path","role":"content","text":"`HGETALL` reads every field on the hash, including ones the model doesn't\nneed. With dozens of features per entity, that is wasted serialization work\non the server and wasted bandwidth on the wire. Always specify the field\nlist explicitly with `hmget` in the model server.\n\nThe exception is debugging and feature-set discovery, where you genuinely\nwant the full hash. The demo's \"Inspect\" button uses `hgetAll` for exactly\nthis reason."},{"id":"size-the-jedispool-for-the-request-shape","title":"Size the JedisPool for the request shape","role":"content","text":"Every `FeatureStore` helper method borrows a connection from the\n`JedisPool` for the duration of one Redis call (or one `Pipeline.sync()`)\nand returns it via the try-with-resources block. One HTTP handler can\ntherefore borrow several connections sequentially — `/read`, for example,\nmakes one `hmget` call, one `httl` call, and one `ttl` call, each of\nwhich is its own borrow.\n\nThe demo uses `maxTotal=64`. In production, size `maxTotal` to comfortably\nexceed your peak concurrent borrow count: that's roughly\n`(concurrent HTTP handlers × Redis calls per handler in flight at once) +\n(background worker borrow rate)`. Setting it too low forces some borrows\nto block waiting for a returned connection — a slow read-side cliff that\ndoesn't show up under load tests with very few clients."},{"id":"inspect-the-store-directly-with-redis-cli","title":"Inspect the store directly with redis-cli","role":"content","text":"When testing or troubleshooting, the cli tells you everything:\n\n[code example]\n\nA streaming field that returns `-2` from `HTTL` doesn't exist on the hash\n(either it was never written, or it expired); `-1` means the field has no\nTTL set (and is therefore covered only by the key-level `EXPIRE`); any\npositive value is the remaining TTL in seconds."},{"id":"learn-more","title":"Learn more","role":"related","text":"This example uses the following Redis commands:\n\n* [`HSET`](https://redis.io/docs/latest/commands/hset) to write a feature or a whole\n  feature row in one call.\n* [`HMGET`](https://redis.io/docs/latest/commands/hmget) to retrieve any subset of\n  features for one entity in one round trip.\n* [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) for debugging and\n  feature-set discovery.\n* [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) and\n  [`HTTL`](https://redis.io/docs/latest/commands/httl) for per-field TTL on streaming\n  features (Redis 7.4+).\n* [`EXPIRE`](https://redis.io/docs/latest/commands/expire) and\n  [`TTL`](https://redis.io/docs/latest/commands/ttl) for the whole-entity TTL aligned\n  with the batch materialization cycle.\n* Pipelined `HMGET` across many entities for batch scoring with one network\n  round trip — see\n  [transactions and pipelining](https://redis.io/docs/latest/develop/clients/jedis/transpipe).\n\nSee the [Jedis documentation](https://redis.io/docs/latest/develop/clients/jedis) for\nthe full client reference, and the\n[Hashes overview](https://redis.io/docs/latest/develop/data-types/hashes) for the deeper\nconceptual model — including the listpack encoding that makes small hashes\nparticularly compact in memory, which matters at feature-store scale."}],"examples":[{"id":"the-feature-store-helper-ex0","language":"java","code":"import redis.clients.jedis.JedisPool;\nimport redis.clients.jedis.JedisPoolConfig;\n\nJedisPoolConfig cfg = new JedisPoolConfig();\ncfg.setMaxTotal(64);\nJedisPool pool = new JedisPool(cfg, \"localhost\", 6379);\nFeatureStore store = new FeatureStore(pool,\n    \"fs:user:\",\n    24L * 60L * 60L,    // whole-entity TTL aligned with the daily batch cycle\n    5L * 60L            // per-field TTL on each streaming feature\n);\n\n// Batch materialization: one HSET + EXPIRE per user, all pipelined.\nMap<String, Map<String, Object>> rows = Map.of(\n    \"u0001\", Map.of(\n        \"country_iso\", \"US\", \"risk_segment\", \"low\",\n        \"tx_count_7d\", 14, \"avg_amount_30d\", 92.40,\n        \"account_age_days\", 612, \"chargeback_count_180d\", 0),\n    \"u0002\", Map.of(\n        \"country_iso\", \"GB\", \"risk_segment\", \"medium\",\n        \"tx_count_7d\", 47, \"avg_amount_30d\", 220.10,\n        \"account_age_days\", 1840, \"chargeback_count_180d\", 1));\nstore.bulkLoad(rows);\n\n// Streaming write: HSET + HEXPIRE on just the fields that changed.\nstore.updateStreaming(\"u0001\", Map.of(\n    \"last_login_ts\", System.currentTimeMillis(),\n    \"last_device_id\", \"ios-9f02\",\n    \"tx_count_5m\", 3,\n    \"failed_logins_15m\", 0,\n    \"session_country\", \"US\"));\n\n// Inference read: HMGET of whatever the model needs.\nMap<String, String> features = store.getFeatures(\"u0001\", List.of(\n    \"risk_segment\", \"tx_count_7d\", \"avg_amount_30d\",\n    \"tx_count_5m\", \"failed_logins_15m\"));\n\n// Batch scoring: pipelined HMGET across many users.\nMap<String, Map<String, String>> batch = store.batchGetFeatures(\n    List.of(\"u0001\", \"u0002\", \"u0003\"),\n    List.of(\"risk_segment\", \"tx_count_5m\", \"failed_logins_15m\"));","section_id":"the-feature-store-helper"},{"id":"project-layout-ex0","language":"bash","code":"mvn package\nmvn exec:java -Dexec.mainClass=DemoServer","section_id":"project-layout"},{"id":"data-model-ex0","language":"text","code":"fs:user:u0001                                   TTL = 86400 s (key-level)\n  country_iso=US                                <no field TTL>\n  risk_segment=low                              <no field TTL>\n  account_age_days=612                          <no field TTL>\n  tx_count_7d=14                                <no field TTL>\n  avg_amount_30d=92.40                          <no field TTL>\n  chargeback_count_180d=0                       <no field TTL>\n  last_login_ts=1716998413541                   TTL = 300 s (per field, HEXPIRE)\n  last_device_id=ios-9f02                       TTL = 300 s (per field, HEXPIRE)\n  tx_count_5m=3                                 TTL = 300 s (per field, HEXPIRE)\n  failed_logins_15m=0                           TTL = 300 s (per field, HEXPIRE)\n  session_country=US                            TTL = 300 s (per field, HEXPIRE)","section_id":"data-model"},{"id":"bulk-loading-batch-features-ex0","language":"java","code":"public int bulkLoad(Map<String, Map<String, Object>> rows, long ttlSeconds) {\n    if (rows.isEmpty()) return 0;\n    try (Jedis jedis = pool.getResource()) {\n        Pipeline pipe = jedis.pipelined();\n        for (Map.Entry<String, Map<String, Object>> e : rows.entrySet()) {\n            String key = keyFor(e.getKey());\n            Map<String, String> encoded = encode(e.getValue());\n            pipe.hset(key, encoded);\n            pipe.expire(key, ttlSeconds);\n        }\n        pipe.sync();\n    }\n    ...\n}","section_id":"bulk-loading-batch-features"},{"id":"streaming-writes-with-per-field-ttl-ex0","language":"java","code":"public void updateStreaming(String entityId, Map<String, Object> fields, long ttlSeconds) {\n    if (fields.isEmpty()) return;\n    String key = keyFor(entityId);\n    Map<String, String> encoded = encode(fields);\n    String[] names = encoded.keySet().toArray(new String[0]);\n\n    List<Long> expireCodes;\n    try (Jedis jedis = pool.getResource()) {\n        Pipeline pipe = jedis.pipelined();\n        pipe.hset(key, encoded);\n        Response<List<Long>> expireResp = pipe.hexpire(key, ttlSeconds, names);\n        pipe.sync();\n        expireCodes = expireResp.get();\n    }\n    for (Long code : expireCodes) {\n        if (code == null || code != 1L) {\n            throw new IllegalStateException(\n                \"HEXPIRE did not set every field TTL for \" + key + \": \" + expireCodes);\n        }\n    }\n    ...\n}","section_id":"streaming-writes-with-per-field-ttl"},{"id":"inference-reads-with-hmget-ex0","language":"java","code":"public Map<String, String> getFeatures(String entityId, List<String> fieldNames) {\n    String key = keyFor(entityId);\n    Map<String, String> out = new LinkedHashMap<>();\n    if (fieldNames == null) {\n        try (Jedis jedis = pool.getResource()) {\n            Map<String, String> all = jedis.hgetAll(key);\n            if (all != null) out.putAll(all);\n        }\n        return out;\n    }\n    if (fieldNames.isEmpty()) return out;\n    List<String> values;\n    try (Jedis jedis = pool.getResource()) {\n        values = jedis.hmget(key, fieldNames.toArray(new String[0]));\n    }\n    for (int i = 0; i < fieldNames.size(); i++) {\n        String v = values.get(i);\n        if (v != null) out.put(fieldNames.get(i), v);\n    }\n    return out;\n}","section_id":"inference-reads-with-hmget"},{"id":"batch-scoring-with-pipelined-hmget-ex0","language":"java","code":"public Map<String, Map<String, String>> batchGetFeatures(\n        List<String> entityIds, List<String> fieldNames) {\n    if (entityIds.isEmpty() || fieldNames.isEmpty()) {\n        return Collections.emptyMap();\n    }\n    String[] names = fieldNames.toArray(new String[0]);\n    List<Response<List<String>>> responses = new ArrayList<>(entityIds.size());\n    try (Jedis jedis = pool.getResource()) {\n        Pipeline pipe = jedis.pipelined();\n        for (String id : entityIds) {\n            responses.add(pipe.hmget(keyFor(id), names));\n        }\n        pipe.sync();\n    }\n    Map<String, Map<String, String>> out = new LinkedHashMap<>();\n    for (int i = 0; i < entityIds.size(); i++) {\n        List<String> values = responses.get(i).get();\n        Map<String, String> row = new LinkedHashMap<>();\n        for (int j = 0; j < fieldNames.size(); j++) {\n            String v = values.get(j);\n            if (v != null) row.put(fieldNames.get(j), v);\n        }\n        out.put(entityIds.get(i), row);\n    }\n    return out;\n}","section_id":"batch-scoring-with-pipelined-hmget"},{"id":"the-streaming-worker-ex0","language":"java","code":"private void doTick() {\n    List<String> ids = store.listEntityIds(500);\n    if (ids.isEmpty()) return;\n    List<String> picks = sample(ids, usersPerTick);\n    long nowMs = System.currentTimeMillis();\n    for (String id : picks) {\n        Map<String, Object> fields = new LinkedHashMap<>();\n        fields.put(\"last_login_ts\", nowMs);\n        fields.put(\"last_device_id\", choice(DEVICE_IDS));\n        fields.put(\"tx_count_5m\", intn(13));\n        fields.put(\"failed_logins_15m\", weightedInt(FAILED_LOGIN_BUCKETS, FAILED_LOGIN_WEIGHTS));\n        fields.put(\"session_country\", choice(SESSION_COUNTRIES));\n        store.updateStreaming(id, fields);\n    }\n    ...\n}","section_id":"the-streaming-worker"},{"id":"the-batch-builder-ex0","language":"java","code":"public static Map<String, Map<String, Object>> synthesizeUsers(int count, long seed) {\n    Random rng = new Random(seed);\n    Map<String, Map<String, Object>> users = new LinkedHashMap<>(count);\n    for (int i = 1; i <= count; i++) {\n        String uid = String.format(\"u%04d\", i);\n        Map<String, Object> row = new LinkedHashMap<>();\n        row.put(\"country_iso\", COUNTRY_CHOICES.get(rng.nextInt(COUNTRY_CHOICES.size())));\n        row.put(\"risk_segment\", weightedChoice(rng, RISK_SEGMENTS, RISK_WEIGHTS));\n        row.put(\"account_age_days\", 7 + rng.nextInt(2394));\n        row.put(\"tx_count_7d\", rng.nextInt(81));\n        row.put(\"avg_amount_30d\", Math.round((5.0 + rng.nextDouble() * 345.0) * 100.0) / 100.0);\n        row.put(\"chargeback_count_180d\", weightedChoiceInt(rng, CHARGEBACK_BUCKETS, CHARGEBACK_WEIGHTS));\n        users.put(uid, row);\n    }\n    return users;\n}","section_id":"the-batch-builder"},{"id":"the-batch-builder-ex1","language":"bash","code":"mvn exec:java -Dexec.mainClass=BuildFeatures -Dexec.args=\"--count 500 --ttl-seconds 3600\"","section_id":"the-batch-builder"},{"id":"get-the-source-files-ex0","language":"bash","code":"git clone https://github.com/redis/docs.git\ncd docs/content/develop/use-cases/feature-store/java-jedis\nmvn package","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"mvn exec:java -Dexec.mainClass=DemoServer","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"Dropping any existing users under 'fs:user:*' for a clean demo run (pass --no-reset to keep them).\nRedis feature-store demo server listening on http://127.0.0.1:8088\nUsing Redis at localhost:6379 with key prefix 'fs:user:' (batch TTL 86400s, streaming TTL 300s)\nMaterialized 200 user(s); streaming worker running.","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex2","language":"bash","code":"mvn exec:java -Dexec.mainClass=DemoServer \\\n    -Dexec.args=\"--port 9000 --streaming-ttl-seconds 30\"","section_id":"start-the-demo-server"},{"id":"inspect-the-store-directly-with-redis-cli-ex0","language":"bash","code":"# How many users currently in the store\nredis-cli --scan --pattern 'fs:user:*' | wc -l\n\n# One user's full hash and key-level TTL\nredis-cli HGETALL fs:user:u0001\nredis-cli TTL    fs:user:u0001\n\n# Per-field TTL on the streaming fields\nredis-cli HTTL fs:user:u0001 FIELDS 5 \\\n  last_login_ts last_device_id tx_count_5m failed_logins_15m session_country\n\n# Sample HMGET as the model would issue it\nredis-cli HMGET fs:user:u0001 risk_segment tx_count_7d avg_amount_30d tx_count_5m","section_id":"inspect-the-store-directly-with-redis-cli"}]}
{"schema_version":2,"id":"develop/use-cases/feature-store/java-lettuce","title":"Redis feature store with Lettuce","url":"https://redis.io/docs/latest/develop/use-cases/feature-store/java-lettuce/","summary":"Build a Redis-backed online feature store in Java with Lettuce","aliases":["/develop/use-cases/feature-store/lettuce"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:50:30+01:00","children":[],"page_type":"content","content_hash":"a22eedaeee656bb82bd1eab98a9bc7fc27a8f5f3b874d06f10f9a1a2850b9e55","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a small Redis-backed online feature store in\nJava with [Lettuce](https://redis.io/docs/latest/develop/clients/lettuce), the\nasync-by-default Netty-based Redis client. The demo runs on top of the JDK's\n`com.sun.net.httpserver.HttpServer` so you can bulk-load a batch of users\nwith a key-level TTL, run a streaming worker that overwrites real-time\nfeatures with per-field TTL, retrieve any subset of features for one user\nunder 2 ms, and pipeline `HMGET` across a hundred users for batch scoring.\n\nThe [Jedis walkthrough](https://redis.io/docs/latest/develop/use-cases/feature-store/java-jedis)\ncovers the same flow with a synchronous, pool-borrowing client. This page\nfocuses on what's different in Lettuce — the multiplexed connection, the\n`RedisAsyncCommands` surface, and the auto-flush pipelining model — rather\nthan re-explaining the shared concepts."},{"id":"overview","title":"Overview","role":"overview","text":"Each entity (here, a user) is one Redis\n[Hash](https://redis.io/docs/latest/develop/data-types/hashes) at a deterministic key —\n`fs:user:{id}`. The hash holds every feature for that entity as one field per\nfeature: batch-materialized aggregates (refreshed once a day) alongside\nstreaming-updated signals (refreshed every few seconds). One\n[`HMGET`](https://redis.io/docs/latest/commands/hmget) returns whichever subset the model\nneeds in one network round trip.\n\nTwo TTL layers solve the *mixed staleness* problem without an application-side\ncleaner:\n\n* A **key-level** [`EXPIRE`](https://redis.io/docs/latest/commands/expire) aligned with the\n  batch materialization cycle (24 hours in the demo). If the batch refresher\n  fails, the whole entity disappears at the next cycle and inference sees a\n  missing entity — which the model handler can detect and fall back on —\n  rather than silently outdated values.\n* A **per-field** [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) (Redis 7.4+) on\n  each streaming feature gives that field its own shorter expiry, independent\n  of the rest of the hash. If the streaming pipeline stops updating a feature,\n  the field self-cleans while the batch fields stay populated.\n\nThat gives you:\n\n* A single round trip for retrieval — any subset of features for one entity\n  in one [`HMGET`](https://redis.io/docs/latest/commands/hmget).\n* Sub-millisecond hot path. The Redis-side work is microseconds; in practice\n  the bottleneck is the network round trip plus the model's own feature-prep.\n* Pipelined batch scoring — one round trip for `N` users at once.\n* Independent freshness per feature, expressed as a server-side TTL rather\n  than as application logic.\n* Self-cleanup on pipeline failure: a stalled batch refresher lets entities\n  expire on schedule, and a stalled streaming worker lets each affected field\n  expire on its own timer."},{"id":"how-lettuce-differs-from-jedis","title":"How Lettuce differs from Jedis","role":"content","text":"The big mental-model difference for someone arriving from Jedis:\n\n* **One shared, multiplexed connection.** A `StatefulRedisConnection<K, V>`\n  is thread-safe and serves the whole process. There's no `JedisPool`-style\n  per-call borrow — every handler in the HTTP thread pool *and* the\n  streaming worker share the same connection, and Netty handles the\n  serialization onto the underlying socket.\n* **Async-by-default API.** Every method on `RedisAsyncCommands<K, V>`\n  returns a `RedisFuture<T>` (which is a `CompletionStage<T>` and a\n  `Future<T>`). For synchronous code paths the helper blocks with `.get()`;\n  for reactive pipelines you'd compose with `.thenApply()` /\n  `.thenCompose()` or use the `.reactive()` API directly.\n* **Pipelining via connection-level auto-flush.** Lettuce doesn't have a\n  `pipelined()`-style builder. Instead, you toggle\n  `conn.setAutoFlushCommands(false)` on the connection, queue commands as\n  normal async calls (each returns its own `RedisFuture`), call\n  `conn.flushCommands()` to ship the batch, and toggle auto-flush back on.\n  `LettuceFutures.awaitAll(...)` waits for all the futures to resolve.\n\nIn short: reach for **Lettuce** when you need async/reactive composition\nor you're already in a reactive stack (Spring WebFlux, Project Reactor);\nreach for **Jedis** when blocking commands are common or you want a\nsimple sync API with explicit per-call connection lifetime. The\n[Lettuce](https://redis.io/docs/latest/develop/clients/lettuce) and\n[Jedis](https://redis.io/docs/latest/develop/clients/jedis) client guides cover the\ndeeper selection criteria.\n\nIn this example, the batch features describe a user's longer-term shape and\nare bulk-loaded by `BuildFeatures.java`. The streaming features describe\nwhat the user is doing right now and are written by `StreamingWorker.java`\non a daemon thread. The inference handlers of the demo server read any\nsubset of those features through `FeatureStore.java`'s helper class. All\nfour sources share one `StatefulRedisConnection` opened in `DemoServer.java`."},{"id":"the-feature-store-helper","title":"The feature-store helper","role":"content","text":"The `FeatureStore` class wraps the read/write paths\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/feature-store/java-lettuce/FeatureStore.java)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each user is one Redis Hash. Every value is stored as a string — Redis hash\nfields are bytes on the wire, so the helper encodes booleans as `\"true\"` /\n`\"false\"` (`encodeValue(Object)` in `FeatureStore.java`) and renders\neverything else with `Object.toString()`. The model server is responsible\nfor parsing back to the right type, the same way it would when reading any\nserialized feature store.\n\n[code example]"},{"id":"bulk-loading-batch-features","title":"Bulk-loading batch features","role":"content","text":"`bulkLoad` queues one `HSET` and one `EXPIRE` per user with auto-flush\ndisabled, flushes once, and waits for every `RedisFuture` to resolve.\n\n[code example]\n\nThe two important things to notice:\n\n1. **`setAutoFlushCommands(false)` is on the connection, not the async\n   commands.** It affects *every* call going through that\n   `StatefulRedisConnection` until it's flipped back. The `finally` block\n   restores auto-flush even if a queue step throws — failing to do so would\n   silently break every subsequent command in the JVM.\n2. **`LettuceFutures.awaitAll` blocks with a timeout.** With auto-flush off,\n   queued commands can sit in the local pipeline buffer indefinitely if\n   something below the flush goes wrong. The timeout gives `bulkLoad` a\n   clean failure mode rather than hanging forever.\n\nIn production, the equivalent of this script runs as an offline pipeline (a\nSpark or Feast `materialize` job) that reads from the warehouse and writes\ninto Redis. The\n[Feast `RedisOnlineStore`](https://docs.feast.dev/reference/online-stores/redis)\nprovider does exactly this under the hood; the in-house\n[Redis Feature Form](https://redis.io/docs/latest/develop/ai/featureform) integration\ncovers the materialize + serve path end-to-end."},{"id":"streaming-writes-with-per-field-ttl","title":"Streaming writes with per-field TTL","role":"content","text":"`updateStreaming` is the linchpin of the mixed-staleness story:\n\n[code example]\n\n[`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) sets the TTL on *individual*\nhash fields, not on the whole key. The two commands are queued under one\nflush so Redis runs them in pipeline order: the `HSET` first creates or\noverwrites the fields, then `HEXPIRE` attaches a TTL to each of those same\nfields. `HEXPIRE` returns one status code per field — `1` if the TTL was\nset, `2` if the expiry was 0 or in the past (so Redis deleted the field\ninstead), `0` if an `NX | XX | GT | LT` conditional flag was set and not\nmet (we never use one here), `-2` if the field doesn't exist on the key.\nThe helper throws if any code is anything other than `1`, so the \"every\nstreaming write renews its TTL\" invariant fails loudly rather than silently\nleaving a streaming field with no expiry attached.\n\nIf a streaming pipeline stops, the streaming fields drop out one by one as\ntheir per-field TTLs elapse. [`HTTL`](https://redis.io/docs/latest/commands/httl) lets\nthe model side inspect the remaining TTL on any field, which is useful both\nfor debugging and as a freshness signal in the model itself.\n\n> **HEXPIRE requires Redis 7.4 or later.** `HEXPIRE` and the field-level TTL\n> commands were added in Redis 7.4. Lettuce 6.4 was the first release with\n> the bindings; the demo's `pom.xml` pins 7.5.2.RELEASE."},{"id":"inference-reads-with-hmget","title":"Inference reads with HMGET","role":"content","text":"`getFeatures` is one `HMGET`:\n\n[code example]\n\nLettuce's `hmget` returns `List<KeyValue<K, V>>` rather than a parallel\n`List<V>` like Jedis. `KeyValue` is Lettuce's `Optional`-like wrapper:\n`kv.hasValue()` tells you whether Redis returned a value or a nil for that\nfield, and `kv.getValue()` unwraps it. The helper drops `hasValue()==false`\nentries so the caller's `Map<String, String>` only contains fields that\nactually exist on the hash."},{"id":"batch-scoring-with-pipelined-hmget","title":"Batch scoring with pipelined HMGET","role":"content","text":"The same connection-level flush pattern carries over to batch reads:\n\n[code example]\n\nOne round trip for the whole batch. The first call after server startup\nincludes a few milliseconds of Netty event-loop and connection warm-up;\nsteady-state, the demo returns a 100-user batch in 2-5 ms against a local\nRedis.\n\nA Redis Cluster is different: a single auto-flush batch is bound to one\nshard, because all the queued commands ship through one connection to one\nnode. For batch reads on a cluster, use\n[`RedisClusterClient`](https://redis.io/docs/latest/develop/clients/lettuce) — its\n`StatefulRedisClusterConnection` exposes `getConnection(slot)` for\nper-shard auto-flush batching, and the high-level `RedisAdvancedClusterAsyncCommands`\nfans out non-pipelined calls per shard automatically.\n\nA hash tag like `fs:user:{vip}:u0001` forces a known set of keys onto the\nsame shard so one auto-flush batch can cover all of them in a single round\ntrip."},{"id":"the-streaming-worker","title":"The streaming worker","role":"content","text":"`StreamingWorker.java` is the demo's stand-in for whatever Flink, Kafka\nStreams, or bespoke service computes the real-time features\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/feature-store/java-lettuce/StreamingWorker.java)).\nIt runs as a daemon `Thread` next to the demo server so the UI can start,\npause, and resume it; in production this code would live in the streaming\nlayer.\n\nThe lifecycle (start / stop / pause / resume / waitForIdle) is identical to\nthe Jedis demo — the worker thread itself doesn't care which client it's\ntalking to, only that `FeatureStore.updateStreaming` pipelines the\n`HSET` + `HEXPIRE` in order within one flush. The Lettuce helper achieves that through\nthe connection-level flush described above.\n\nPausing the worker is what shows off the mixed-staleness behavior: leave\nit paused for longer than `streamingTtlSeconds` and the streaming fields\ndisappear from every user's hash one by one, while the batch fields remain\nunder the longer key-level `EXPIRE`. The demo's `Pause / resume` button\nlets you see this happen in real time.\n\n`pause()` only blocks *future* ticks from running. A reset that's about to\n`DEL` every key also needs to wait out an already-running tick, which is\nwhat `waitForIdle()` is for. The demo's `Reset` handler calls\n`worker.pause()` *and* `worker.waitForIdle()` before it issues the `DEL`\nsweep, so a mid-flight tick can't recreate a user under a streaming-only\nhash with no key-level TTL."},{"id":"the-batch-builder","title":"The batch builder","role":"content","text":"`BuildFeatures.java` is the demo's nightly materializer\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/feature-store/java-lettuce/BuildFeatures.java)).\nIt generates synthetic feature rows and calls `store.bulkLoad` once. The\nsynthesis itself is not the point — in a real deployment the equivalent\ncode reads from the offline store (Snowflake, BigQuery, Iceberg) and writes\nthe resulting hashes into Redis.\n\nRun the builder on its own (independently of the demo server) to populate\nRedis from the command line:\n\n[code example]\n\nThat writes 500 users at `fs:user:*` with a one-hour key-level TTL, which\nis how a typical operator would pre-seed a feature store from the command\nline when debugging."},{"id":"the-interactive-demo","title":"The interactive demo","role":"content","text":"`DemoServer.java` runs the JDK `HttpServer` on port 8089 with a fixed\nthread pool. The HTML page lets you:\n\n* **Bulk-load** any number of users (default 200) with a configurable\n  key-level TTL.\n* See the **store state**: user count, batch / streaming TTLs, cumulative\n  read/write counters.\n* See the **streaming worker** status and **pause or resume** it.\n* Run an **inference read** for any user with a chosen feature subset, and\n  see the value, the per-field TTL, and the read latency.\n* Run **batch scoring** with a pipelined `HMGET` across `N` users.\n* **Inspect** any user's full hash with field-level TTLs and the key-level\n  TTL.\n\nThe server holds one `FeatureStore`, one `StreamingWorker`, one\n`RedisClient`, and one `StatefulRedisConnection` for the lifetime of the\nprocess. Every HTTP handler and the streaming worker share that single\nconnection — Lettuce multiplexes the commands across them automatically.\nEndpoints:\n\n| Endpoint                  | What it does                                                                        |\n|---------------------------|-------------------------------------------------------------------------------------|\n| `GET  /state`             | User count, TTL config, stats counters, worker status.                              |\n| `POST /bulk-load`         | Auto-flush batched `HSET` + `EXPIRE` over N synthetic users with a chosen TTL.      |\n| `POST /worker/toggle`     | Pause / resume the streaming worker.                                                |\n| `POST /read`              | `HMGET` a chosen feature subset for one user; report latency and per-field TTLs.    |\n| `POST /batch-read`        | Pipeline `HMGET` across N users; report total latency and per-entity field counts.  |\n| `GET  /inspect`           | `HGETALL` + `HTTL` for one user; full hash view with per-field TTLs.                |\n| `POST /reset`             | Drop every user under the key prefix (used by the demo's reset button).             |"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* **Redis 7.4 or later.** [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) and\n  [`HTTL`](https://redis.io/docs/latest/commands/httl) were added in Redis 7.4; the\n  demo relies on per-field TTL for the mixed-staleness story.\n* **Java 17 or later.** The demo uses switch expressions with arrow labels\n  (`case \"...\" -> ...`), records, and text blocks.\n* **Lettuce 6.4 or later.** The demo's `pom.xml` pins 7.5.2.RELEASE.\n  Field-level TTL bindings (`hexpire`, `httl`, `hpersist`) ship from\n  Lettuce 6.4.\n\nIf your Redis server is running elsewhere, start the demo with\n`--redis-uri redis://host:port`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo lives in a small Maven project under\n[`feature-store/java-lettuce`](https://github.com/redis/docs/tree/main/content/develop/use-cases/feature-store/java-lettuce).\nClone the repo or copy the directory:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From the project directory:\n\n[code example]\n\nYou should see:\n\n[code example]\n\nOpen [http://127.0.0.1:8089](http://127.0.0.1:8089). The first inference\nread after startup is a few milliseconds slower than the rest because\nLettuce / Netty are warming up the event loop and the underlying socket;\nsubsequent reads settle into 1-2 ms on a local Redis.\n\nUseful things to try:\n\n* Pick a user and click **Read features** with a mixed batch/streaming\n  subset — you'll see batch fields with no per-field TTL (covered by the\n  key-level TTL) and streaming fields with a positive per-field TTL.\n* Click **Pipeline HMGET** with `count=100` to see the latency of a\n  100-user batch read.\n* Click **Pause / resume** on the streaming worker and leave it paused for\n  ~5 minutes (or restart the server with `--streaming-ttl-seconds 30` to\n  make it visible in seconds). Re-run **Read features** on any user and\n  watch the streaming fields disappear while the batch fields stay.\n* Click **Inspect** on a user to see the full hash with field-level TTLs.\n* Click **Reset** to drop every user and start over.\n\nThe server is read/write against your local Redis. The default key prefix\nis `fs:user:`. Pass `--no-reset` to keep existing data across restarts, or\n`--redis-uri` to point at a different Redis."},{"id":"production-usage","title":"Production usage","role":"content","text":"The guidance below focuses on the production concerns that are specific to\nrunning a feature store on Redis. For the generic Lettuce production\nchecklist — `ClientResources` tuning, AUTH/ACL, retry policy,\nsentinel/cluster failover — see the\n[Lettuce client guide](https://redis.io/docs/latest/develop/clients/lettuce). For TLS\nspecifically, follow the\n[connect-with-TLS recipe](https://redis.io/docs/latest/develop/clients/lettuce/connect#tls-connection).\nThe feature-store demo runs against `localhost` with the defaults; a real\ndeployment should harden the client first."},{"id":"pick-the-batch-ttl-to-outlast-a-failed-refresher","title":"Pick the batch TTL to outlast a failed refresher","role":"content","text":"The whole-entity `EXPIRE` is your safety net against silent staleness from\na broken batch pipeline. Set it longer than your worst-case batch outage\nso a single missed run doesn't take the feature store offline, but short\nenough that a sustained outage causes loud failures (missing entities)\nrather than quiet ones (yesterday's features being scored as today's). The\nstandard choice is one cycle of \"expected refresh interval × 2\" — for a\ndaily batch, 48 hours; for a 6-hour batch, 12 hours.\n\nThe same logic applies to the per-field streaming TTL: a few times the\nexpected update interval so a slow-but-alive streaming worker doesn't\nchurn features needlessly, but short enough that a stalled worker causes\nvisible freshness failures."},{"id":"dont-share-auto-flush-state-across-unrelated-code-paths","title":"Don't share auto-flush state across unrelated code paths","role":"content","text":"`conn.setAutoFlushCommands(false)` flips a *connection-level* toggle that\naffects every call going through that connection until it's flipped\nback. If two threads run pipelined writes concurrently against the same\nconnection, they will fight over the flag — one thread's `flushCommands()`\nwill ship the other thread's still-being-queued commands, or its\nrestore-to-true will flush the other thread's queue prematurely. Worse,\na single non-pipelined read on that same connection will be silently\nqueued (and never flushed) while the flag is off.\n\nThe demo handles this by opening **two** connections from the same\n`RedisClient`:\n\n* **The shared read connection** stays in default auto-flush=true mode.\n  Every HTTP handler and the streaming worker use it for the\n  non-pipelined commands (`HMGET`, `HTTL`, `TTL`, `SCAN`, `DEL`,\n  `HGETALL`).\n* **The dedicated pipeline connection** is reserved for `bulkLoad`,\n  `updateStreaming`, and `batchGetFeatures`. These all acquire a single\n  `pipelineLock` inside the `FeatureStore` instance before they touch\n  the auto-flush flag, so concurrent batches block each other instead\n  of corrupting the state. With one lock and one connection, you get at\n  most one in-flight batch at a time on the pipeline side; the read\n  connection is unaffected.\n\nFor batch concurrency beyond what one connection sustains, scale this\npattern to a small\n[`BoundedAsyncPool<StatefulRedisConnection<K, V>>`](https://redis.io/docs/latest/develop/clients/lettuce)\nof pipeline connections and lease one per batch."},{"id":"pipeline-batch-reads-across-shards","title":"Pipeline batch reads across shards","role":"content","text":"On a single Redis instance, an auto-flush batched `HMGET` across `N` users\nis one round trip. A Redis Cluster is different: a single auto-flush batch\nis bound to one shard, because all queued commands ship to one node. For\nbatch reads on a cluster, use\n[`RedisClusterClient`](https://redis.io/docs/latest/develop/clients/lettuce) and one\nof:\n\n* Fan-out via `RedisAdvancedClusterAsyncCommands` — the cluster client\n  routes each `hmGet` to the right shard transparently. Easier to write,\n  slightly more overhead per call.\n* Bucket keys by slot with `SlotHash.getSlot(key)` and open one connection\n  per affected shard; auto-flush-batch each bucket separately. More code,\n  but one round trip per shard.\n\nFor a small number of frequently-queried users (a top-N customer list, for\nexample), a hash tag like `fs:user:{vip}:u0001` forces a known set of keys\nonto the same shard so one batch can cover them all."},{"id":"make-hexpire-part-of-every-streaming-write","title":"Make HEXPIRE part of every streaming write","role":"content","text":"The single biggest correctness lever in this design is that the streaming\nwrite applies `HEXPIRE` *every time*. If a streaming worker writes a field\nwithout renewing its TTL, the field carries whatever expiry was there\nbefore — possibly none, possibly stale — and the mixed-staleness invariant\nbreaks. Keep the `HSET` and `HEXPIRE` under the same flush boundary (or,\neven safer, in the same\n[Lua script](https://redis.io/docs/latest/develop/programmability/eval-intro) if you\ndon't trust the call site)."},{"id":"avoid-hgetall-on-the-request-path","title":"Avoid HGETALL on the request path","role":"content","text":"`HGETALL` reads every field on the hash, including ones the model doesn't\nneed. With dozens of features per entity, that is wasted serialization\nwork on the server and wasted bandwidth on the wire. Always specify the\nfield list explicitly with `hmget` in the model server.\n\nThe exception is debugging and feature-set discovery, where you genuinely\nwant the full hash. The demo's \"Inspect\" button uses `hgetall` for exactly\nthis reason."},{"id":"inspect-the-store-directly-with-redis-cli","title":"Inspect the store directly with redis-cli","role":"content","text":"When testing or troubleshooting, the cli tells you everything:\n\n[code example]\n\nA streaming field that returns `-2` from `HTTL` doesn't exist on the hash\n(either it was never written, or it expired); `-1` means the field has no\nTTL set (and is therefore covered only by the key-level `EXPIRE`); any\npositive value is the remaining TTL in seconds."},{"id":"learn-more","title":"Learn more","role":"related","text":"This example uses the following Redis commands:\n\n* [`HSET`](https://redis.io/docs/latest/commands/hset) to write a feature or a whole\n  feature row in one call.\n* [`HMGET`](https://redis.io/docs/latest/commands/hmget) to retrieve any subset of\n  features for one entity in one round trip.\n* [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) for debugging and\n  feature-set discovery.\n* [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) and\n  [`HTTL`](https://redis.io/docs/latest/commands/httl) for per-field TTL on streaming\n  features (Redis 7.4+).\n* [`EXPIRE`](https://redis.io/docs/latest/commands/expire) and\n  [`TTL`](https://redis.io/docs/latest/commands/ttl) for the whole-entity TTL aligned\n  with the batch materialization cycle.\n* Pipelined `HMGET` across many entities for batch scoring with one\n  network round trip via Lettuce's connection-level auto-flush.\n\nSee the [Lettuce documentation](https://redis.io/docs/latest/develop/clients/lettuce)\nfor the full client reference, and the\n[Hashes overview](https://redis.io/docs/latest/develop/data-types/hashes) for the\ndeeper conceptual model."}],"examples":[{"id":"the-feature-store-helper-ex0","language":"java","code":"import io.lettuce.core.RedisClient;\nimport io.lettuce.core.api.StatefulRedisConnection;\n\nRedisClient client = RedisClient.create(\"redis://localhost:6379\");\ntry (StatefulRedisConnection<String, String> conn = client.connect()) {\n    FeatureStore store = new FeatureStore(conn,\n        \"fs:user:\",\n        24L * 60L * 60L,    // whole-entity TTL aligned with the daily batch cycle\n        5L * 60L            // per-field TTL on each streaming feature\n    );\n\n    // Batch materialization: one HSET + EXPIRE per user, all pipelined\n    // through a single connection-level flush.\n    Map<String, Map<String, Object>> rows = Map.of(\n        \"u0001\", Map.of(\n            \"country_iso\", \"US\", \"risk_segment\", \"low\",\n            \"tx_count_7d\", 14, \"avg_amount_30d\", 92.40,\n            \"account_age_days\", 612, \"chargeback_count_180d\", 0));\n    store.bulkLoad(rows);\n\n    // Streaming write: HSET + HEXPIRE on just the fields that changed.\n    store.updateStreaming(\"u0001\", Map.of(\n        \"last_login_ts\", System.currentTimeMillis(),\n        \"last_device_id\", \"ios-9f02\",\n        \"tx_count_5m\", 3,\n        \"failed_logins_15m\", 0,\n        \"session_country\", \"US\"));\n\n    // Inference read: HMGET of whatever the model needs.\n    Map<String, String> features = store.getFeatures(\"u0001\", List.of(\n        \"risk_segment\", \"tx_count_7d\", \"avg_amount_30d\",\n        \"tx_count_5m\", \"failed_logins_15m\"));\n\n    // Batch scoring: pipelined HMGET across many users.\n    Map<String, Map<String, String>> batch = store.batchGetFeatures(\n        List.of(\"u0001\", \"u0002\", \"u0003\"),\n        List.of(\"risk_segment\", \"tx_count_5m\", \"failed_logins_15m\"));\n} finally {\n    client.shutdown();\n}","section_id":"the-feature-store-helper"},{"id":"data-model-ex0","language":"text","code":"fs:user:u0001                                   TTL = 86400 s (key-level)\n  country_iso=US                                <no field TTL>\n  risk_segment=low                              <no field TTL>\n  account_age_days=612                          <no field TTL>\n  tx_count_7d=14                                <no field TTL>\n  avg_amount_30d=92.40                          <no field TTL>\n  chargeback_count_180d=0                       <no field TTL>\n  last_login_ts=1716998413541                   TTL = 300 s (per field, HEXPIRE)\n  last_device_id=ios-9f02                       TTL = 300 s (per field, HEXPIRE)\n  tx_count_5m=3                                 TTL = 300 s (per field, HEXPIRE)\n  failed_logins_15m=0                           TTL = 300 s (per field, HEXPIRE)\n  session_country=US                            TTL = 300 s (per field, HEXPIRE)","section_id":"data-model"},{"id":"bulk-loading-batch-features-ex0","language":"java","code":"public int bulkLoad(Map<String, Map<String, Object>> rows, long ttlSeconds) {\n    if (rows.isEmpty()) return 0;\n\n    List<RedisFuture<?>> futures = new ArrayList<>(rows.size() * 2);\n    conn.setAutoFlushCommands(false);\n    try {\n        for (Map.Entry<String, Map<String, Object>> e : rows.entrySet()) {\n            String key = keyFor(e.getKey());\n            Map<String, String> encoded = encode(e.getValue());\n            futures.add(async.hset(key, encoded));\n            futures.add(async.expire(key, ttlSeconds));\n        }\n        conn.flushCommands();\n    } finally {\n        conn.setAutoFlushCommands(true);\n    }\n    if (!LettuceFutures.awaitAll(BATCH_TIMEOUT, futures.toArray(new RedisFuture[0]))) {\n        throw new IllegalStateException(\"bulkLoad: timed out after \" + BATCH_TIMEOUT);\n    }\n    ...\n}","section_id":"bulk-loading-batch-features"},{"id":"streaming-writes-with-per-field-ttl-ex0","language":"java","code":"public void updateStreaming(String entityId, Map<String, Object> fields, long ttlSeconds) {\n    if (fields.isEmpty()) return;\n    String key = keyFor(entityId);\n    Map<String, String> encoded = encode(fields);\n    String[] names = encoded.keySet().toArray(new String[0]);\n\n    RedisFuture<Long> hsetFut;\n    RedisFuture<List<Long>> hexpireFut;\n    conn.setAutoFlushCommands(false);\n    try {\n        hsetFut = async.hset(key, encoded);\n        hexpireFut = async.hexpire(key, ttlSeconds, names);\n        conn.flushCommands();\n    } finally {\n        conn.setAutoFlushCommands(true);\n    }\n    awaitOne(hsetFut);\n    List<Long> codes = awaitOne(hexpireFut);\n    for (Long code : codes) {\n        if (code == null || code != 1L) {\n            throw new IllegalStateException(\n                \"HEXPIRE did not set every field TTL for \" + key + \": \" + codes);\n        }\n    }\n    ...\n}","section_id":"streaming-writes-with-per-field-ttl"},{"id":"inference-reads-with-hmget-ex0","language":"java","code":"public Map<String, String> getFeatures(String entityId, List<String> fieldNames) {\n    String key = keyFor(entityId);\n    Map<String, String> out = new LinkedHashMap<>();\n    if (fieldNames == null) {\n        Map<String, String> all = awaitOne(async.hgetall(key));\n        if (all != null) out.putAll(all);\n        return out;\n    }\n    if (fieldNames.isEmpty()) return out;\n    List<KeyValue<String, String>> values = awaitOne(\n        async.hmget(key, fieldNames.toArray(new String[0])));\n    for (KeyValue<String, String> kv : values) {\n        if (kv != null && kv.hasValue()) {\n            out.put(kv.getKey(), kv.getValue());\n        }\n    }\n    return out;\n}","section_id":"inference-reads-with-hmget"},{"id":"batch-scoring-with-pipelined-hmget-ex0","language":"java","code":"public Map<String, Map<String, String>> batchGetFeatures(\n        List<String> entityIds, List<String> fieldNames) {\n    if (entityIds.isEmpty() || fieldNames.isEmpty()) {\n        return Collections.emptyMap();\n    }\n    String[] names = fieldNames.toArray(new String[0]);\n\n    List<RedisFuture<List<KeyValue<String, String>>>> futures =\n        new ArrayList<>(entityIds.size());\n    conn.setAutoFlushCommands(false);\n    try {\n        for (String id : entityIds) {\n            futures.add(async.hmget(keyFor(id), names));\n        }\n        conn.flushCommands();\n    } finally {\n        conn.setAutoFlushCommands(true);\n    }\n\n    Map<String, Map<String, String>> out = new LinkedHashMap<>();\n    for (int i = 0; i < entityIds.size(); i++) {\n        List<KeyValue<String, String>> values = awaitOne(futures.get(i));\n        Map<String, String> row = new LinkedHashMap<>();\n        for (KeyValue<String, String> kv : values) {\n            if (kv != null && kv.hasValue()) row.put(kv.getKey(), kv.getValue());\n        }\n        out.put(entityIds.get(i), row);\n    }\n    return out;\n}","section_id":"batch-scoring-with-pipelined-hmget"},{"id":"the-batch-builder-ex0","language":"bash","code":"mvn exec:java -Dexec.mainClass=BuildFeatures -Dexec.args=\"--count 500 --ttl-seconds 3600\"","section_id":"the-batch-builder"},{"id":"get-the-source-files-ex0","language":"bash","code":"git clone https://github.com/redis/docs.git\ncd docs/content/develop/use-cases/feature-store/java-lettuce\nmvn package","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"mvn exec:java -Dexec.mainClass=DemoServer","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"Dropping any existing users under 'fs:user:*' for a clean demo run (pass --no-reset to keep them).\nRedis feature-store demo server listening on http://127.0.0.1:8089\nUsing Redis at redis://localhost:6379 with key prefix 'fs:user:' (batch TTL 86400s, streaming TTL 300s)\nMaterialized 200 user(s); streaming worker running.","section_id":"start-the-demo-server"},{"id":"inspect-the-store-directly-with-redis-cli-ex0","language":"bash","code":"# How many users currently in the store\nredis-cli --scan --pattern 'fs:user:*' | wc -l\n\n# One user's full hash and key-level TTL\nredis-cli HGETALL fs:user:u0001\nredis-cli TTL    fs:user:u0001\n\n# Per-field TTL on the streaming fields\nredis-cli HTTL fs:user:u0001 FIELDS 5 \\\n  last_login_ts last_device_id tx_count_5m failed_logins_15m session_country\n\n# Sample HMGET as the model would issue it\nredis-cli HMGET fs:user:u0001 risk_segment tx_count_7d avg_amount_30d tx_count_5m","section_id":"inspect-the-store-directly-with-redis-cli"}]}
{"schema_version":2,"id":"develop/use-cases/feature-store/nodejs","title":"Redis feature store with node-redis","url":"https://redis.io/docs/latest/develop/use-cases/feature-store/nodejs/","summary":"Build a Redis-backed online feature store in Node.js with node-redis","aliases":["/develop/use-cases/feature-store/node-redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:50:30+01:00","children":[],"page_type":"content","content_hash":"d85c037e088bb6bb7d78679b378558c625a6bfbaaa4bfe0c787afa996b0b4e7f","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a small Redis-backed online feature store in\nNode.js with [`node-redis`](https://redis.io/docs/latest/develop/clients/nodejs). It\nincludes a local web server built with the Node.js standard `http` module so\nyou can bulk-load a batch of users with a key-level TTL, run a streaming\nworker that overwrites real-time features with per-field TTL, retrieve any\nsubset of features for one user under 1 ms, and pipeline `HMGET` across a\nhundred users for batch scoring."},{"id":"overview","title":"Overview","role":"overview","text":"Each entity (here, a user) is one Redis [Hash](https://redis.io/docs/latest/develop/data-types/hashes)\nat a deterministic key — `fs:user:{id}`. The hash holds every feature for that\nentity as one field per feature: batch-materialized aggregates (refreshed once\na day) alongside streaming-updated signals (refreshed every few seconds). One\n[`HMGET`](https://redis.io/docs/latest/commands/hmget) returns whichever subset the model\nneeds in one network round trip.\n\nTwo TTL layers solve the *mixed staleness* problem without an application-side\ncleaner:\n\n* A **key-level** [`EXPIRE`](https://redis.io/docs/latest/commands/expire) aligned with the\n  batch materialization cycle (24 hours in the demo). If the batch refresher\n  fails, the whole entity disappears at the next cycle and inference sees a\n  missing entity — which the model handler can detect and fall back on —\n  rather than silently outdated values.\n* A **per-field** [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) (Redis 7.4+) on\n  each streaming feature gives that field its own shorter expiry, independent\n  of the rest of the hash. If the streaming pipeline stops updating a feature,\n  the field self-cleans while the batch fields stay populated.\n\nIn this example, the batch features describe a user's longer-term shape\n(`country_iso`, `risk_segment`, `account_age_days`, `tx_count_7d`,\n`avg_amount_30d`, `chargeback_count_180d`) and are bulk-loaded by\n`buildFeatures.js` — the demo's stand-in for a nightly Spark / Feast\nmaterialization job. The streaming features describe what the user is doing\nright now (`last_login_ts`, `last_device_id`, `tx_count_5m`,\n`failed_logins_15m`, `session_country`) and are written by\n`streamingWorker.js` — the demo's stand-in for a Flink / Kafka Streams job.\nThe inference panel of the demo server reads any subset of those features\nthrough `featureStore.js`'s helper class.\n\nThat gives you:\n\n* A single round trip for retrieval — any subset of features for one entity\n  in one [`HMGET`](https://redis.io/docs/latest/commands/hmget).\n* Sub-millisecond hot path. The Redis-side work is microseconds; in practice\n  the bottleneck is the network round trip plus the model's own feature-prep.\n* Pipelined batch scoring — one round trip for `N` users at once.\n* Independent freshness per feature, expressed as a server-side TTL rather\n  than as application logic.\n* Self-cleanup on pipeline failure: a stalled batch refresher lets entities\n  expire on schedule, and a stalled streaming worker lets each affected field\n  expire on its own timer."},{"id":"how-it-works","title":"How it works","role":"content","text":"There are three paths: a **batch path** that bulk-loads features once per\nmaterialization cycle, a **streaming path** that updates real-time features as\nevents arrive, and an **inference path** that reads features on the request\nside."},{"id":"batch-path-per-materialization-cycle","title":"Batch path (per materialization cycle)","role":"content","text":"1. The batch job calls `synthesizeUsers(N)` (in production, the equivalent\n   computation lives in an offline pipeline against the warehouse). The result\n   is `{userId: {field: value, ...}}` for every user in this cycle.\n2. `store.bulkLoad(rows, ttlSeconds)` batches one\n   [`HSET`](https://redis.io/docs/latest/commands/hset) plus one\n   [`EXPIRE`](https://redis.io/docs/latest/commands/expire) per user through\n   [`multi().exec()`](https://redis.io/docs/latest/develop/clients/nodejs/transpipe), so\n   the whole batch ships in a single round trip. The `HSET` writes every batch\n   field; the `EXPIRE` is what makes the entity disappear if the next batch\n   run fails, so inference reads a missing entity rather than silently\n   outdated values."},{"id":"streaming-path-per-event","title":"Streaming path (per event)","role":"content","text":"When a user does something (login, transaction, page view) the streaming layer\ncomputes whatever real-time signals fall out of that event and calls\n`store.updateStreaming(userId, fields, ttlSeconds)`. That batches:\n\n1. An [`HSET`](https://redis.io/docs/latest/commands/hset) writing the new field values.\n   Redis is single-threaded per shard, so this is atomic against any\n   concurrent batch write on the same hash — no version columns, no locks.\n2. An [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) over exactly the fields\n   that were written, with the streaming TTL. Each streaming field carries\n   its own per-field expiry independent of the rest of the hash. Stop the\n   worker and these fields drop out one by one as their TTLs elapse, while\n   the batch fields remain populated under the longer key-level TTL."},{"id":"inference-path-per-request","title":"Inference path (per request)","role":"content","text":"1. The model server picks the feature subset it needs (the schema is owned by\n   the model, not the store).\n2. It calls `store.getFeatures(userId, names)`, which is one\n   [`HMGET`](https://redis.io/docs/latest/commands/hmget). Redis returns the values in\n   the same order as the requested fields, with `null` for any field that\n   doesn't exist (or has expired).\n3. For batch inference, the model server calls\n   `store.batchGetFeatures(userIds, names)`, which pipelines one\n   [`HMGET`](https://redis.io/docs/latest/commands/hmget) per user across all `N` users\n   in a single network round trip via\n   [`multi().exec()`](https://redis.io/docs/latest/develop/clients/nodejs/transpipe)."},{"id":"the-feature-store-helper","title":"The feature-store helper","role":"content","text":"The `FeatureStore` class wraps the read/write paths\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/feature-store/nodejs/featureStore.js)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each user is one Redis Hash. Every value is stored as a string — Redis hash\nfields are bytes-on-the-wire, so the helper encodes booleans as `\"true\"` /\n`\"false\"` and everything else with `String(value)`. The model server is\nresponsible for parsing back to the right type, the same way it would when\nreading any serialized feature store.\n\n[code example]\n\nThe batch fields sit under the key-level `EXPIRE`. The streaming fields each\ncarry their own [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire). If the\nstreaming pipeline stops, the streaming fields drop one by one as their\nper-field TTLs elapse; the batch fields stay until the daily key-level\n`EXPIRE` fires (or the next batch cycle re-pins them)."},{"id":"bulk-loading-batch-features","title":"Bulk-loading batch features","role":"content","text":"`bulkLoad` batches one `HSET` and one `EXPIRE` per user into a single round\ntrip through node-redis's `multi()`. With 500 users that's 1000 commands in\none network call — Redis processes them sequentially on the server side but\nthe client only pays one RTT.\n\n[code example]\n\n`multi().exec()` in node-redis 5 wraps the batched commands in `MULTI/EXEC`,\nso Redis runs the queued commands contiguously and returns the replies in\norder. Note that Redis transactions do *not* roll back commands that already\nsucceeded if a later command returns an error — node-redis surfaces those\nerrors by rejecting `exec()` with a `MultiErrorReply` whose `replies` array\nstill contains the successful results. For independent bulk-ingestion\ncommands that don't need the `MULTI/EXEC` wrapper at all,\n`multi().execAsPipeline()` ships the same batch in one round trip with\nslightly lower server-side overhead. (See\n[transactions and pipelining](https://redis.io/docs/latest/develop/clients/nodejs/transpipe)\nfor the full mental model.)\n\nIn production, the equivalent of this script runs as an offline pipeline (a\nSpark or Feast `materialize` job) that reads from the warehouse and writes\ninto Redis. The\n[Feast `RedisOnlineStore`](https://docs.feast.dev/reference/online-stores/redis)\nprovider does exactly this under the hood; the in-house\n[Redis Feature Form](https://redis.io/docs/latest/develop/ai/featureform) integration\ncovers the materialize + serve path end-to-end."},{"id":"streaming-writes-with-per-field-ttl","title":"Streaming writes with per-field TTL","role":"content","text":"`updateStreaming` is the linchpin of the mixed-staleness story:\n\n[code example]\n\n[`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) sets the TTL on *individual*\nhash fields, not on the whole key. Note node-redis's argument order:\n`hExpire(key, fields, seconds)` — the fields come *before* the TTL, the\nopposite of `EXPIRE(key, seconds)`. The two commands are sent in one round\ntrip and Redis executes them in pipeline order: the `HSET` runs first and\ncreates or overwrites the fields, then `HEXPIRE` attaches a TTL to each of\nthose same fields. `HEXPIRE` returns one status code per field — `1` if the\nTTL was set, `-2` if the field doesn't exist — so the helper throws if any\ncode is anything other than `1`. That makes the \"every streaming write\nrenews its TTL\" invariant fail loudly rather than silently leaving a\nstreaming field with no expiry attached.\n\nIf a streaming pipeline stops, the streaming fields drop out one by one as\ntheir per-field TTLs elapse — there is no application-side cleaner involved.\n[`HTTL`](https://redis.io/docs/latest/commands/httl) lets the model side inspect the\nremaining TTL on any field, which is useful both for debugging (\"why is this\nfeature missing?\" → \"it expired three seconds ago\") and as a freshness signal\nin the model itself.\n\n> **HEXPIRE requires Redis 7.4 or later.** `HEXPIRE` and the field-level TTL\n> commands (`HTTL`, `HPERSIST`, `HEXPIREAT`, `HPEXPIRE`, `HPEXPIREAT`,\n> `HPTTL`, `HEXPIRETIME`, `HPEXPIRETIME`) were added in Redis 7.4. On older\n> Redis builds you would have to put streaming features on their own keys\n> (one key per feature, or one key per feature group) and set a key-level\n> `EXPIRE` instead — at the cost of giving up the single-`HMGET` retrieval."},{"id":"inference-reads-with-hmget","title":"Inference reads with HMGET","role":"content","text":"`getFeatures` is one `HMGET`:\n\n[code example]\n\nThe model knows exactly which features it consumes, so the request path\nalways takes the `HMGET` branch with an explicit field list — that's the\nsub-millisecond path. `HGETALL` is the right call for debugging (which is\nwhat the demo's \"Inspect\" panel does) but not for serving: it forces Redis\nto serialize every field, including ones the model doesn't need.\n\nFields that don't exist (because they were never written, or because they\nexpired) come back as `null`. The helper drops them from the result object\nso the caller sees only the features that are actually available. A real\nmodel server would either treat missing values as a feature (\"this user has\nno streaming signal yet\") or fall back to a default from the model's\ntraining data."},{"id":"batch-scoring-with-pipelined-hmget","title":"Batch scoring with pipelined HMGET","role":"content","text":"For batch inference, the same `HMGET` shape pipelines across users:\n\n[code example]\n\nOne round trip for the whole batch — the demo regularly returns 100 users in\n2-3 ms against a local Redis. On a real network the round trip dominates;\npipelining is what keeps batch scoring practical.\n\nFor very large batches on a clustered deployment, the shape changes: a single\n`multi().exec()` is bound to one shard, because `MULTI/EXEC` cannot span hash\nslots, so the same `batchGetFeatures` call can only serve keys that hash to\nthe same shard. node-redis's\n[cluster client](https://github.com/redis/node-redis/blob/master/docs/clustering.md)\nroutes non-pipelined `hmGet` calls to the right shard transparently — so on a\ncluster, fan out `await Promise.all(ids.map((id) => client.hmGet(...)))` and\nthe client pipelines per-shard for you. For very latency-sensitive batch\ninference where the request-side cost of that fan-out matters, group the IDs\nby hash slot ahead of time and issue one `multi().exec()` per shard in\nparallel: each shard's batch then runs as one round trip. A hash tag like\n`fs:user:{vip}:u0001` forces a known set of keys onto the same shard so one\n`multi()` can cover all of them in a single round trip."},{"id":"the-streaming-worker","title":"The streaming worker","role":"content","text":"`streamingWorker.js` is the demo's stand-in for whatever Flink, Kafka Streams,\nor bespoke service computes the real-time features\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/feature-store/nodejs/streamingWorker.js)).\nIt runs as a `setTimeout` loop next to the demo server so the UI can start,\npause, and resume it; in production this code would live in the streaming\nlayer.\n\nEvery tick the worker picks a few random users, generates a new value for each\nstreaming feature, and calls `store.updateStreaming(userId, fields)`. The\ndemo defaults to 5 users per tick at 1-second intervals — so a 200-user store\nsees roughly half its users refreshed in the first minute, and most after a\nfew minutes. Drop `--seed-users` or raise `--users-per-tick` if you'd rather\nhave every user touched quickly.\n\n[code example]\n\nPausing the worker is what shows off the mixed-staleness behavior: leave it\npaused for longer than `streamingTtlSeconds` and the streaming fields\ndisappear from every user's hash one by one, while the batch fields remain\nunder the longer key-level `EXPIRE`. The demo's `Pause / resume` button lets\nyou see this happen in real time.\n\nThe worker uses a `setTimeout` chain rather than `setInterval`, so a slow\ntick (or a paused state) never queues up overlapping work — each tick fully\nfinishes before the next one schedules. That mirrors how a real Flink\noperator backpressures: don't accept the next input until the current one\nhas been committed."},{"id":"the-batch-builder","title":"The batch builder","role":"content","text":"`buildFeatures.js` is the demo's nightly materializer\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/feature-store/nodejs/buildFeatures.js)).\nIt generates synthetic feature rows and calls `store.bulkLoad` once. The\nsynthesis itself is not the point — in a real deployment the equivalent code\nreads from the offline store (Snowflake, BigQuery, Iceberg) and writes the\nresulting hashes into Redis.\n\n[code example]\n\nYou can run the builder on its own (independently of the demo server) to\npopulate Redis from the command line:\n\n[code example]\n\nThat writes 500 users at `fs:user:*` with a one-hour key-level TTL, which is\nhow a typical operator would pre-seed a feature store from the command line\nwhen debugging."},{"id":"the-interactive-demo","title":"The interactive demo","role":"content","text":"`demoServer.js` runs a Node.js `http` server on port 8086. The HTML page lets\nyou:\n\n* **Bulk-load** any number of users (default 200) with a configurable\n  key-level TTL. Drop the TTL to 30 s and watch the entire store expire on\n  schedule — the same thing that happens if a daily refresher fails.\n* See the **store state** at a glance: user count, batch / streaming TTLs,\n  cumulative read/write counters.\n* See the **streaming worker** status (running / paused, ticks completed,\n  writes performed) and **pause or resume** it. Leave it paused for longer\n  than the streaming TTL to watch streaming fields drop out.\n* Run an **inference read** for any user with a chosen feature subset, and\n  see the value, the per-field TTL, and the read latency.\n* Run **batch scoring** with a pipelined `HMGET` across `N` users and see\n  the total elapsed time plus the per-user breakdown.\n* **Inspect** any user's full hash with field-level TTLs and the key-level\n  TTL — the right view for debugging \"why is this feature missing?\" at\n  read time.\n\nThe server holds one `FeatureStore` instance and one `StreamingWorker` for\nthe lifetime of the process. Endpoints:\n\n| Endpoint                  | What it does                                                                        |\n|---------------------------|-------------------------------------------------------------------------------------|\n| `GET  /state`             | User count, TTL config, stats counters, worker status.                              |\n| `POST /bulk-load`         | Batched `HSET` + `EXPIRE` over N synthetic users with a chosen TTL.                 |\n| `POST /worker/toggle`     | Pause / resume the streaming worker.                                                |\n| `POST /read`              | `HMGET` a chosen feature subset for one user; report latency and per-field TTLs.    |\n| `POST /batch-read`        | Pipeline `HMGET` across N users; report total latency and per-entity field counts.  |\n| `GET  /inspect`           | `HGETALL` + `HTTL` for one user; full hash view with per-field TTLs.                |\n| `POST /reset`             | Drop every user under the key prefix (used by the demo's reset button).             |"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* **Redis 7.4 or later.** [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) and\n  [`HTTL`](https://redis.io/docs/latest/commands/httl) were added in Redis 7.4; the demo\n  relies on per-field TTL for the mixed-staleness story.\n* **Node.js 18 or later.**\n* The `node-redis` client. Install it with:\n\n  [code example]\n\n  Field-level TTL bindings (`hExpire`, `hTTL`) ship in node-redis 5.\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host`\nand `--redis-port`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of five files. Download them from the\n[`nodejs` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/feature-store/nodejs)\non GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From that directory:\n\n[code example]\n\nYou should see:\n\n[code example]\n\nBy default the demo wipes the configured key prefix on startup so each run\nstarts from a clean state. Pass `--no-reset` to keep any existing data, or\n`--key-prefix <prefix>` to point the demo at a different prefix entirely.\n\nOpen [http://127.0.0.1:8086](http://127.0.0.1:8086) in a browser. Useful\nthings to try:\n\n* Pick a user and click **Read features** with a mixed batch/streaming subset\n  — you'll see batch fields with no per-field TTL (covered by the key-level\n  TTL) and streaming fields with a positive per-field TTL.\n* Click **Pipeline HMGET** with `count=100` to see the latency of a 100-user\n  batch read.\n* Click **Pause / resume** on the streaming worker and leave it paused for\n  ~5 minutes (or restart the server with `--streaming-ttl-seconds 30` to\n  make it visible in seconds). Re-run **Read features** on any user and\n  watch the streaming fields disappear while the batch fields stay.\n* Click **Inspect** on a user to see the full hash with field-level TTLs.\n* Click **Bulk-load** with a short TTL (say 30 seconds) and watch the user\n  count fall to zero on the next minute — the same thing that happens if a\n  daily batch run fails to land.\n* Click **Reset** to drop every user and start over.\n\nThe server is read/write against your local Redis. The default key prefix is\n`fs:user:`. Pass `--no-reset` to keep existing data across restarts, or\n`--redis-host` / `--redis-port` to point at a different Redis."},{"id":"production-usage","title":"Production usage","role":"content","text":"The guidance below focuses on the production concerns that are specific to\nrunning a feature store on Redis. For the generic node-redis production\nchecklist — connection pooling, TLS and AUTH, error handling, retry policy —\nsee the [node-redis client guide](https://redis.io/docs/latest/develop/clients/nodejs)\nand the\n[connect-with-TLS recipe](https://redis.io/docs/latest/develop/clients/nodejs/connect#connect-to-your-production-redis-with-tls).\nThe feature-store demo runs against `localhost` with the defaults; a real\ndeployment should harden the client first."},{"id":"pick-the-batch-ttl-to-outlast-a-failed-refresher","title":"Pick the batch TTL to outlast a failed refresher","role":"content","text":"The whole-entity `EXPIRE` is your safety net against silent staleness from a\nbroken batch pipeline. Set it longer than your worst-case batch outage so a\nsingle missed run doesn't take the feature store offline, but short enough\nthat a sustained outage causes loud failures (missing entities) rather than\nquiet ones (yesterday's features being scored as today's). The standard\nchoice is one cycle of \"expected refresh interval × 2\" — for a daily batch,\n48 hours; for a 6-hour batch, 12 hours.\n\nThe same logic applies to the per-field streaming TTL: a few times the\nexpected update interval so a slow-but-alive streaming worker doesn't churn\nfeatures needlessly, but short enough that a stalled worker causes visible\nfreshness failures."},{"id":"co-locate-the-online-store-with-serving-not-with-training","title":"Co-locate the online store with serving, not with training","role":"content","text":"The online store's hash representation does *not* have to match the schema in\nyour offline store. The batch materialization step is your chance to flatten\njoins, encode categoricals, and project to whatever shape the model server\nwants — so the request path is exactly one `HMGET` and zero transforms.\n\nThe training pipeline reads from the offline store with its own schema; the\nserving pipeline reads from Redis with the flattened serving schema. Keeping\nthose two pipelines as the same code path is what prevents training-serving\nskew."},{"id":"pipeline-batch-reads-across-shards","title":"Pipeline batch reads across shards","role":"content","text":"On a single Redis instance, pipelining `HMGET` across `N` users through\n`multi().exec()` is one round trip. A Redis Cluster is different in two ways:\n`MULTI/EXEC` is bound to one shard, so a single `multi()` cannot span keys\nthat hash to different shards; and the keys for a typical user batch will\nland on multiple shards. For batch reads on a cluster, fan out parallel\n`hmGet` calls with `Promise.all` — node-redis's cluster client pipelines\nthe calls per-shard automatically — or, for tighter control, group the IDs\nby hash slot ahead of time and issue one `multi().exec()` per shard in\nparallel.\n\nFor a small number of frequently-queried users (a top-N customer list, for\nexample), a hash tag like `fs:user:{vip}:u0001` forces the keys onto the\nsame shard and lets one `multi().exec()` serve them all in one round trip."},{"id":"make-hexpire-part-of-every-streaming-write","title":"Make HEXPIRE part of every streaming write","role":"content","text":"The single biggest correctness lever in this design is that the streaming\nwrite applies `HEXPIRE` *every time*. If a streaming worker writes a field\nwithout renewing its TTL, the field carries whatever expiry was there before\n— possibly none, possibly stale — and the mixed-staleness invariant breaks.\nKeep the `HSET` and `HEXPIRE` in the same `multi()` (or, even safer, in the\nsame [Lua script](https://redis.io/docs/latest/develop/programmability/eval-intro) if\nyou don't trust the call site)."},{"id":"avoid-hgetall-on-the-request-path","title":"Avoid HGETALL on the request path","role":"content","text":"`HGETALL` reads every field on the hash, including ones the model doesn't\nneed. With dozens of features per entity, that is wasted serialization work\non the server and wasted bandwidth on the wire. Always specify the field list\nexplicitly with `hmGet` in the model server.\n\nThe exception is debugging and feature-set discovery, where you genuinely\nwant the full hash. The demo's \"Inspect\" button uses `hGetAll` for exactly\nthis reason."},{"id":"inspect-the-store-directly-with-redis-cli","title":"Inspect the store directly with redis-cli","role":"content","text":"When testing or troubleshooting, the cli tells you everything:\n\n[code example]\n\nA streaming field that returns `-2` from `HTTL` doesn't exist on the hash\n(either it was never written, or it expired); `-1` means the field has no\nTTL set (and is therefore covered only by the key-level `EXPIRE`); any\npositive value is the remaining TTL in seconds."},{"id":"learn-more","title":"Learn more","role":"related","text":"This example uses the following Redis commands:\n\n* [`HSET`](https://redis.io/docs/latest/commands/hset) to write a feature or a whole\n  feature row in one call.\n* [`HMGET`](https://redis.io/docs/latest/commands/hmget) to retrieve any subset of\n  features for one entity in one round trip.\n* [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) for debugging and\n  feature-set discovery.\n* [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) and\n  [`HTTL`](https://redis.io/docs/latest/commands/httl) for per-field TTL on streaming\n  features (Redis 7.4+).\n* [`EXPIRE`](https://redis.io/docs/latest/commands/expire) and\n  [`TTL`](https://redis.io/docs/latest/commands/ttl) for the whole-entity TTL aligned\n  with the batch materialization cycle.\n* Pipelined `HMGET` across many entities for batch scoring with one network\n  round trip — see\n  [transactions and pipelining](https://redis.io/docs/latest/develop/clients/nodejs/transpipe).\n\nSee the [node-redis documentation](https://redis.io/docs/latest/develop/clients/nodejs)\nfor the full client reference, and the\n[Hashes overview](https://redis.io/docs/latest/develop/data-types/hashes) for the deeper\nconceptual model — including the listpack encoding that makes small hashes\nparticularly compact in memory, which matters at feature-store scale."}],"examples":[{"id":"the-feature-store-helper-ex0","language":"javascript","code":"const { createClient } = require(\"redis\");\nconst { FeatureStore } = require(\"./featureStore\");\n\nconst client = createClient({ socket: { host: \"localhost\", port: 6379 } });\nclient.on(\"error\", (err) => console.error(\"Redis client error:\", err));\nawait client.connect();\n\nconst store = new FeatureStore({\n  redisClient: client,\n  keyPrefix: \"fs:user:\",\n  batchTtlSeconds: 24 * 60 * 60,    // whole-entity TTL aligned with the daily batch cycle\n  streamingTtlSeconds: 5 * 60,      // per-field TTL on each streaming feature\n});\n\n// Batch materialization: one HSET + EXPIRE per user, all batched.\nawait store.bulkLoad({\n  u0001: { country_iso: \"US\", risk_segment: \"low\",\n           tx_count_7d: 14, avg_amount_30d: 92.40,\n           account_age_days: 612, chargeback_count_180d: 0 },\n  u0002: { country_iso: \"GB\", risk_segment: \"medium\",\n           tx_count_7d: 47, avg_amount_30d: 220.10,\n           account_age_days: 1840, chargeback_count_180d: 1 },\n});\n\n// Streaming write: HSET + HEXPIRE on just the fields that changed.\nawait store.updateStreaming(\"u0001\", {\n  last_login_ts: 1716998413541,\n  last_device_id: \"ios-9f02\",\n  tx_count_5m: 3,\n  failed_logins_15m: 0,\n  session_country: \"US\",\n});\n\n// Inference read: HMGET of whatever the model needs.\nconst features = await store.getFeatures(\"u0001\", [\n  \"risk_segment\", \"tx_count_7d\", \"avg_amount_30d\",\n  \"tx_count_5m\", \"failed_logins_15m\",\n]);\n\n// Batch scoring: pipelined HMGET across many users.\nconst batch = await store.batchGetFeatures(\n  [\"u0001\", \"u0002\", \"u0003\"],\n  [\"risk_segment\", \"tx_count_5m\", \"failed_logins_15m\"],\n);","section_id":"the-feature-store-helper"},{"id":"data-model-ex0","language":"text","code":"fs:user:u0001                                   TTL = 86400 s (key-level)\n  country_iso=US                                <no field TTL>\n  risk_segment=low                              <no field TTL>\n  account_age_days=612                          <no field TTL>\n  tx_count_7d=14                                <no field TTL>\n  avg_amount_30d=92.40                          <no field TTL>\n  chargeback_count_180d=0                       <no field TTL>\n  last_login_ts=1716998413541                   TTL = 300 s (per field, HEXPIRE)\n  last_device_id=ios-9f02                       TTL = 300 s (per field, HEXPIRE)\n  tx_count_5m=3                                 TTL = 300 s (per field, HEXPIRE)\n  failed_logins_15m=0                           TTL = 300 s (per field, HEXPIRE)\n  session_country=US                            TTL = 300 s (per field, HEXPIRE)","section_id":"data-model"},{"id":"bulk-loading-batch-features-ex0","language":"javascript","code":"async bulkLoad(rows, ttlSeconds) {\n  const ttl = ttlSeconds ?? this.batchTtlSeconds;\n  const ids = Object.keys(rows);\n  if (ids.length === 0) return 0;\n\n  const pipe = this.redis.multi();\n  for (const entityId of ids) {\n    const key = this.keyFor(entityId);\n    const encoded = {};\n    for (const [name, value] of Object.entries(rows[entityId])) {\n      encoded[name] = encode(value);\n    }\n    pipe.hSet(key, encoded);\n    pipe.expire(key, ttl);\n  }\n  await pipe.exec();\n  ...\n}","section_id":"bulk-loading-batch-features"},{"id":"streaming-writes-with-per-field-ttl-ex0","language":"javascript","code":"async updateStreaming(entityId, fields, ttlSeconds) {\n  const names = Object.keys(fields);\n  if (names.length === 0) return;\n  const ttl = ttlSeconds ?? this.streamingTtlSeconds;\n  const key = this.keyFor(entityId);\n  const encoded = {};\n  for (const [name, value] of Object.entries(fields)) {\n    encoded[name] = encode(value);\n  }\n\n  const [, expireResult] = await this.redis\n    .multi()\n    .hSet(key, encoded)\n    .hExpire(key, names, ttl)\n    .exec();\n  if (!Array.isArray(expireResult) ||\n      expireResult.some((code) => Number(code) !== 1)) {\n    throw new Error(\n      `HEXPIRE did not set every field TTL for ${key}: ` +\n        JSON.stringify(expireResult),\n    );\n  }\n  ...\n}","section_id":"streaming-writes-with-per-field-ttl"},{"id":"inference-reads-with-hmget-ex0","language":"javascript","code":"async getFeatures(entityId, fieldNames = null) {\n  const key = this.keyFor(entityId);\n  if (fieldNames === null || fieldNames === undefined) {\n    return await this.redis.hGetAll(key);\n  }\n  const names = [...fieldNames];\n  if (names.length === 0) return {};\n  const values = await this.redis.hmGet(key, names);\n  const out = {};\n  for (let i = 0; i < names.length; i += 1) {\n    const v = values[i];\n    if (v !== null && v !== undefined) out[names[i]] = v;\n  }\n  return out;\n}","section_id":"inference-reads-with-hmget"},{"id":"batch-scoring-with-pipelined-hmget-ex0","language":"javascript","code":"async batchGetFeatures(entityIds, fieldNames) {\n  const ids = [...entityIds];\n  const names = [...fieldNames];\n  if (ids.length === 0 || names.length === 0) return {};\n\n  const pipe = this.redis.multi();\n  for (const entityId of ids) {\n    pipe.hmGet(this.keyFor(entityId), names);\n  }\n  const rows = await pipe.exec();\n\n  const out = {};\n  for (let i = 0; i < ids.length; i += 1) {\n    const values = rows[i] || [];\n    const row = {};\n    for (let j = 0; j < names.length; j += 1) {\n      const v = values[j];\n      if (v !== null && v !== undefined) row[names[j]] = v;\n    }\n    out[ids[i]] = row;\n  }\n  return out;\n}","section_id":"batch-scoring-with-pipelined-hmget"},{"id":"the-streaming-worker-ex0","language":"javascript","code":"async _tick() {\n  const ids = await this.store.listEntityIds(500);\n  if (ids.length === 0) return;\n  const chosen = this.rng.sample(ids, this.usersPerTick);\n  const nowMs = Date.now();\n  for (const entityId of chosen) {\n    const fields = {\n      last_login_ts: nowMs,\n      last_device_id: this.rng.choice(DEVICE_IDS),\n      tx_count_5m: this.rng.int(0, 12),\n      failed_logins_15m: this.rng.weightedChoice(\n        FAILED_LOGIN_BUCKETS, FAILED_LOGIN_WEIGHTS,\n      ),\n      session_country: this.rng.choice(SESSION_COUNTRIES),\n    };\n    await this.store.updateStreaming(entityId, fields);\n  }\n}","section_id":"the-streaming-worker"},{"id":"the-batch-builder-ex0","language":"javascript","code":"function synthesizeUsers(count, seed = 42) {\n  const rng = makeRng(seed);\n  const users = {};\n  for (let i = 1; i <= count; i += 1) {\n    const uid = `u${String(i).padStart(4, \"0\")}`;\n    users[uid] = {\n      country_iso: rng.choice(COUNTRY_CHOICES),\n      risk_segment: rng.weightedChoice(RISK_SEGMENTS, RISK_WEIGHTS),\n      account_age_days: rng.int(7, 2400),\n      tx_count_7d: rng.int(0, 80),\n      avg_amount_30d: Number(rng.float(5, 350).toFixed(2)),\n      chargeback_count_180d: rng.weightedChoice(\n        CHARGEBACK_BUCKETS, CHARGEBACK_WEIGHTS,\n      ),\n    };\n  }\n  return users;\n}","section_id":"the-batch-builder"},{"id":"the-batch-builder-ex1","language":"bash","code":"node buildFeatures.js --count 500 --ttl-seconds 3600","section_id":"the-batch-builder"},{"id":"prerequisites-ex0","language":"bash","code":"npm install \"redis@^5\"","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir feature-store-nodejs-demo && cd feature-store-nodejs-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/feature-store/nodejs\ncurl -O $BASE/package.json\ncurl -O $BASE/featureStore.js\ncurl -O $BASE/buildFeatures.js\ncurl -O $BASE/streamingWorker.js\ncurl -O $BASE/demoServer.js\nnpm install","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"node demoServer.js","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"Dropping any existing users under 'fs:user:*' for a clean demo run (pass --no-reset to keep them).\nRedis feature-store demo server listening on http://127.0.0.1:8086\nUsing Redis at localhost:6379 with key prefix 'fs:user:' (batch TTL 86400s, streaming TTL 300s)\nMaterialized 200 user(s); streaming worker running.","section_id":"start-the-demo-server"},{"id":"inspect-the-store-directly-with-redis-cli-ex0","language":"bash","code":"# How many users currently in the store\nredis-cli --scan --pattern 'fs:user:*' | wc -l\n\n# One user's full hash and key-level TTL\nredis-cli HGETALL fs:user:u0001\nredis-cli TTL    fs:user:u0001\n\n# Per-field TTL on the streaming fields\nredis-cli HTTL fs:user:u0001 FIELDS 5 \\\n  last_login_ts last_device_id tx_count_5m failed_logins_15m session_country\n\n# Sample HMGET as the model would issue it\nredis-cli HMGET fs:user:u0001 risk_segment tx_count_7d avg_amount_30d tx_count_5m","section_id":"inspect-the-store-directly-with-redis-cli"}]}
{"schema_version":2,"id":"develop/use-cases/feature-store/php/demo_template.html","title":"","url":"https://redis.io/docs/latest/develop/use-cases/feature-store/php/demo_template/","summary":"","tags":[],"last_updated":"2026-06-01T12:01:30+01:00","page_type":"content","content_hash":"0767e7249df67f2bb643bfa50a4b71f73e871c1e21be0fa2399b9d4009e46b5c","sections":[{"id":"content","title":"Content","role":"content","text":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n  <title>Redis Feature Store Demo (PHP)</title>\n  <style>\n    :root {\n      --bg: #eef3f1;\n      --panel: #ffffff;\n      --ink: #1d2730;\n      --accent: #267d6b;\n      --accent-dark: #1a594c;\n      --muted: #5c6770;\n      --line: #d4dfdb;\n      --ok: #d2ecdf;\n      --warn: #f8e0d0;\n      --pill: #d9ebe6;\n      --batch: #e6e0f0;\n      --stream: #d9ebe6;\n    }\n    * { box-sizing: border-box; }\n    body {\n      margin: 0;\n      font-family: Georgia, \"Times New Roman\", serif;\n      color: var(--ink);\n      background:\n        radial-gradient(circle at top left, #f3faf7, transparent 32rem),\n        linear-gradient(180deg, #ecf2f0 0%, var(--bg) 100%);\n      min-height: 100vh;\n    }\n    main { max-width: 1080px; margin: 0 auto; padding: 40px 20px 72px; }\n    h1 { font-size: clamp(2rem, 4.6vw, 3.4rem); line-height: 1.05; margin-bottom: 8px; }\n    p.lede { max-width: 58rem; font-size: 1.05rem; color: var(--muted); }\n    .grid { display: grid; gap: 18px; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); margin-top: 24px; }\n    .panel { background: rgba(255, 255, 255, 0.96); border: 1px solid var(--line); border-radius: 16px; padding: 20px; box-shadow: 0 10px 32px rgba(20, 60, 50, 0.07); }\n    .panel.wide { grid-column: 1 / -1; }\n    .panel h2 { margin-top: 0; margin-bottom: 8px; font-size: 1.25rem; }\n    .panel h3 { margin: 14px 0 6px; font-size: 1rem; }\n    .pill { display: inline-block; border-radius: 999px; background: var(--pill); color: var(--accent-dark); padding: 6px 10px; font-size: 0.85rem; margin-bottom: 10px; }\n    label { display: block; font-weight: bold; margin: 10px 0 4px; }\n    input, select { width: 100%; padding: 9px 11px; border-radius: 9px; border: 1px solid #c0d2cc; font: inherit; background: white; }\n    button { appearance: none; border: 0; border-radius: 999px; background: var(--accent); color: white; padding: 10px 16px; font: inherit; cursor: pointer; margin-right: 6px; margin-top: 10px; }\n    button.secondary { background: #3b4951; }\n    button.danger { background: #8a3a3a; }\n    button.small { padding: 5px 10px; font-size: 0.85rem; margin-top: 4px; }\n    button:hover { filter: brightness(0.92); }\n    dl { display: grid; grid-template-columns: max-content 1fr; gap: 6px 14px; margin: 0; }\n    dt { font-weight: bold; }\n    dd { margin: 0; word-break: break-word; }\n    .row { display: flex; gap: 8px; flex-wrap: wrap; }\n    .row > * { flex: 1 1 0; min-width: 110px; }\n    table { width: 100%; border-collapse: collapse; font-size: 0.92rem; }\n    th, td { text-align: left; padding: 6px 8px; border-bottom: 1px solid var(--line); }\n    th { color: var(--muted); font-weight: bold; }\n    code, .mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.85rem; }\n    .badge { display: inline-block; border-radius: 6px; padding: 2px 7px; font-size: 0.8rem; font-weight: bold; }\n    .badge.batch { background: var(--batch); color: #43326a; }\n    .badge.stream { background: var(--stream); color: #1d4a2c; }\n    .badge.expired { background: var(--warn); color: #6b3220; }\n    .badge.untracked { background: #eceff1; color: #3b4951; }\n    .badge.running { background: var(--ok); color: #1d4a2c; }\n    .badge.paused { background: var(--warn); color: #6b3220; }\n    .ttl-pos { color: #1a594c; font-weight: bold; }\n    .ttl-neg { color: #6b3220; }\n    .field-list { display: flex; gap: 6px 12px; flex-wrap: wrap; }\n    .field-list label { display: inline-flex; align-items: center; gap: 4px; margin: 0; font-weight: normal; font-size: 0.9rem; }\n    .field-list input { width: auto; }\n    #status { margin-top: 18px; padding: 12px 14px; border-radius: 12px; display: none; }\n    #status.ok { display: block; background: var(--ok); }\n    #status.error { display: block; background: var(--warn); }\n  </style>\n</head>\n<body>\n  <main>\n    <div class=\"pill\">Predis + PHP built-in server</div>\n    <h1>Redis Feature Store Demo</h1>\n    <p class=\"lede\">\n      A small fraud-scoring feature store. Each user is one Redis hash\n      at <code>__KEY_PREFIX__{id}</code> with a batch-materialized\n      <span class=\"badge batch\">batch</span> half (daily aggregates,\n      24-hour key-level <code>EXPIRE</code>) and a streaming\n      <span class=\"badge stream\">streaming</span> half (real-time\n      signals, <code>__STREAM_TTL__</code>s per-field <code>HEXPIRE</code>).\n      Inference reads any subset with one <code>HMGET</code>; batch\n      scoring pipelines <code>HMGET</code> across N users.\n    </p>\n\n    <div class=\"grid\">\n      <section class=\"panel wide\">\n        <h2>Store state</h2>\n        <div id=\"store-view\">Loading...</div>\n      </section>\n\n      <section class=\"panel\">\n        <h2>Materialize batch features</h2>\n        <p>Calls <code>HSET</code> + <code>EXPIRE</code> for each user\n          through one <code>Predis</code> pipeline — the whole batch\n          ships in one round trip.</p>\n        <label for=\"bulk-count\">How many users</label>\n        <input id=\"bulk-count\" type=\"number\" min=\"1\" max=\"2000\" value=\"200\">\n        <label for=\"bulk-ttl\">Key-level TTL (seconds)</label>\n        <input id=\"bulk-ttl\" type=\"number\" min=\"5\" max=\"172800\" value=\"86400\">\n        <p class=\"mono\" style=\"font-size: 0.85rem; color: var(--muted);\">\n          Drop the TTL to e.g. 30 s and watch entities disappear on\n          schedule — the same thing that happens if a daily refresher\n          fails.\n        </p>\n        <button id=\"bulk-button\">Bulk-load</button>\n        <button id=\"reset-button\" class=\"danger\">Reset (drop every user)</button>\n      </section>\n\n      <section class=\"panel\">\n        <h2>Streaming worker</h2>\n        <p>Picks <code>__USERS_PER_TICK__</code> users per tick, writes the\n          streaming features, applies <code>HEXPIRE</code>\n          <code>__STREAM_TTL__</code>s per field. Pause it and the\n          streaming fields drop out via per-field TTL while the batch\n          fields stay populated.</p>\n        <div id=\"worker-view\"></div>\n        <button id=\"worker-pause-button\" class=\"secondary\">Pause / resume</button>\n      </section>\n\n      <section class=\"panel wide\">\n        <h2>Inference read (HMGET)</h2>\n        <p>Pick a user and a feature subset. One <code>HMGET</code>\n          round trip returns whatever the model needs.</p>\n        <div class=\"row\">\n          <div>\n            <label for=\"read-user\">User</label>\n            <select id=\"read-user\"></select>\n          </div>\n          <div>\n            <label>&nbsp;</label>\n            <button id=\"read-button\" class=\"secondary\">Read features</button>\n          </div>\n        </div>\n        <h3>Feature subset</h3>\n        <p class=\"mono\" style=\"font-size: 0.85rem; color: var(--muted);\">\n          Tick to include in the <code>HMGET</code>. Per-field TTL is\n          shown next to each field in the result table.\n        </p>\n        <div id=\"read-fields\" class=\"field-list\"></div>\n        <div id=\"read-result\" style=\"margin-top: 16px;\">\n          <p>Pick a user and click <strong>Read features</strong>.</p>\n        </div>\n      </section>\n\n      <section class=\"panel\">\n        <h2>Batch scoring</h2>\n        <p>Pipelined <code>HMGET</code> across N random users via\n          <code>Predis</code> pipelines. One network round trip for the\n          whole batch.</p>\n        <label for=\"batch-count\">How many users</label>\n        <input id=\"batch-count\" type=\"number\" min=\"1\" max=\"500\" value=\"100\">\n        <button id=\"batch-button\" class=\"secondary\">Pipeline HMGET</button>\n        <div id=\"batch-result\" style=\"margin-top: 14px;\">\n          <p>(no batch read yet)</p>\n        </div>\n      </section>\n\n      <section class=\"panel\">\n        <h2>Inspect one user</h2>\n        <p><code>HGETALL</code> plus per-field <code>HTTL</code> and\n          key-level <code>TTL</code>. Useful for spotting which\n          streaming fields have already expired.</p>\n        <label for=\"inspect-user\">User</label>\n        <select id=\"inspect-user\"></select>\n        <button id=\"inspect-button\">Inspect</button>\n        <div id=\"inspect-result\" style=\"margin-top: 14px;\">\n          <p>(pick a user and click Inspect)</p>\n        </div>\n      </section>\n    </div>\n\n    <div id=\"status\"></div>\n  </main>\n\n  <script>\n    const BATCH_FIELDS = __BATCH_FIELDS_JSON__;\n    const STREAM_FIELDS = __STREAM_FIELDS_JSON__;\n\n    const storeView = document.getElementById(\"store-view\");\n    const workerView = document.getElementById(\"worker-view\");\n    const readUserSelect = document.getElementById(\"read-user\");\n    const inspectUserSelect = document.getElementById(\"inspect-user\");\n    const readFieldsBox = document.getElementById(\"read-fields\");\n    const readResult = document.getElementById(\"read-result\");\n    const batchResult = document.getElementById(\"batch-result\");\n    const inspectResult = document.getElementById(\"inspect-result\");\n    const statusBox = document.getElementById(\"status\");\n\n    function setStatus(message, kind) { statusBox.textContent = message; statusBox.className = kind; }\n    function escapeHtml(value) {\n      return String(value ?? \"\").replace(/[&<>\"']/g, (c) =>\n        ({ \"&\": \"&\", \"<\": \"<\", \">\": \">\", '\"': \"\"\", \"'\": \"'\" }[c]));\n    }\n    function classifyField(name) {\n      if (BATCH_FIELDS.includes(name)) return \"batch\";\n      if (STREAM_FIELDS.includes(name)) return \"stream\";\n      return \"other\";\n    }\n    function ttlLabel(seconds) {\n      if (seconds === -2) return '<span class=\"badge expired\">missing</span>';\n      if (seconds === -1) return '<span class=\"badge untracked\">no TTL</span>';\n      return `<span class=\"ttl-pos mono\">${seconds}s</span>`;\n    }\n\n    function renderStore(state) {\n      const stats = state.stats || {};\n      storeView.innerHTML = `\n        <dl>\n          <dt>Users in store</dt><dd>${state.entity_count}</dd>\n          <dt>Key prefix</dt><dd class=\"mono\">${escapeHtml(state.key_prefix)}*</dd>\n          <dt>Batch TTL</dt><dd>${state.batch_ttl_seconds}s</dd>\n          <dt>Streaming TTL</dt><dd>${state.streaming_ttl_seconds}s</dd>\n          <dt>Batch writes</dt><dd>${stats.batch_writes_total ?? 0}</dd>\n          <dt>Streaming writes</dt><dd>${stats.streaming_writes_total ?? 0}</dd>\n          <dt>Reads</dt><dd>${stats.reads_total ?? 0}</dd>\n          <dt>Fields returned</dt><dd>${stats.read_fields_total ?? 0}</dd>\n        </dl>`;\n    }\n    function renderWorker(state) {\n      const w = state.worker || {};\n      const badge = w.paused\n        ? '<span class=\"badge paused\">paused</span>'\n        : w.running ? '<span class=\"badge running\">running</span>' : '<span class=\"badge expired\">stopped</span>';\n      workerView.innerHTML = `<p>${badge} <span class=\"mono\">ticks=${w.tick_count ?? 0} writes=${w.writes_count ?? 0}</span></p>`;\n    }\n    function populateUserSelects(ids) {\n      for (const sel of [readUserSelect, inspectUserSelect]) {\n        const previous = sel.value;\n        sel.innerHTML = ids.map((id) => `<option value=\"${escapeHtml(id)}\">${escapeHtml(id)}</option>`).join(\"\");\n        if (ids.includes(previous)) sel.value = previous;\n      }\n    }\n    function renderFieldPicker() {\n      const allFields = [...BATCH_FIELDS, ...STREAM_FIELDS];\n      readFieldsBox.innerHTML = allFields.map((f) => {\n        const kind = classifyField(f);\n        const checked = [\"risk_segment\", \"tx_count_7d\", \"avg_amount_30d\", \"tx_count_5m\", \"failed_logins_15m\"].includes(f) ? \"checked\" : \"\";\n        return `<label><input type=\"checkbox\" name=\"field\" value=\"${escapeHtml(f)}\" ${checked}>\n          <span class=\"badge ${kind}\">${kind}</span>\n          <span class=\"mono\">${escapeHtml(f)}</span></label>`;\n      }).join(\"\");\n    }\n    function selectedReadFields() {\n      return Array.from(readFieldsBox.querySelectorAll('input[name=\"field\"]:checked')).map((el) => el.value);\n    }\n    async function refresh() {\n      const r = await fetch(\"/state\");\n      const state = await r.json();\n      renderStore(state); renderWorker(state); populateUserSelects(state.entity_ids || []);\n    }\n\n    document.getElementById(\"bulk-button\").addEventListener(\"click\", async () => {\n      const count = parseInt(document.getElementById(\"bulk-count\").value, 10) || 1;\n      const ttl = parseInt(document.getElementById(\"bulk-ttl\").value, 10) || 86400;\n      const body = new URLSearchParams({ count, ttl });\n      const r = await fetch(\"/bulk-load\", { method: \"POST\", body });\n      const d = await r.json();\n      if (!r.ok) { setStatus(d.error || \"Bulk-load failed.\", \"error\"); return; }\n      setStatus(`Materialized ${d.loaded} user(s) with a ${d.ttl_seconds}s key-level TTL in ${d.elapsed_ms.toFixed(1)} ms.`, \"ok\");\n      await refresh();\n    });\n    document.getElementById(\"reset-button\").addEventListener(\"click\", async () => {\n      if (!confirm(\"Drop every user from the store?\")) return;\n      const r = await fetch(\"/reset\", { method: \"POST\" });\n      const d = await r.json();\n      if (!r.ok) { setStatus(d.error || \"Reset failed.\", \"error\"); return; }\n      setStatus(`Reset. Dropped ${d.deleted} user(s).`, \"ok\");\n      await refresh();\n    });\n    document.getElementById(\"worker-pause-button\").addEventListener(\"click\", async () => {\n      const r = await fetch(\"/worker/toggle\", { method: \"POST\" });\n      const d = await r.json();\n      setStatus(d.paused ? \"Streaming worker paused.\" : \"Streaming worker resumed.\", \"ok\");\n      await refresh();\n    });\n    document.getElementById(\"read-button\").addEventListener(\"click\", async () => {\n      const user = readUserSelect.value;\n      if (!user) { setStatus(\"Pick a user first.\", \"error\"); return; }\n      const fields = selectedReadFields();\n      const body = new URLSearchParams();\n      body.set(\"user\", user);\n      for (const f of fields) body.append(\"field\", f);\n      const r = await fetch(\"/read\", { method: \"POST\", body });\n      const d = await r.json();\n      if (!r.ok) { setStatus(d.error || \"Read failed.\", \"error\"); return; }\n      const rows = (d.requested || []).map((name) => {\n        const value = d.values[name]; const ttl = d.ttls[name]; const kind = classifyField(name);\n        return `<tr>\n          <td><span class=\"badge ${kind}\">${kind}</span> <span class=\"mono\">${escapeHtml(name)}</span></td>\n          <td class=\"mono\">${value === undefined || value === null ? '<span class=\"badge expired\">missing</span>' : escapeHtml(value)}</td>\n          <td>${ttlLabel(ttl)}</td>\n        </tr>`;\n      }).join(\"\");\n      readResult.innerHTML = `\n        <p><strong>HMGET</strong> ${escapeHtml(user)} (${d.requested.length} field(s))\n          returned ${d.returned_count} value(s) in\n          <strong>${d.elapsed_ms.toFixed(2)} ms</strong>.\n          Key-level TTL: ${ttlLabel(d.key_ttl_seconds)}.</p>\n        ${d.requested.length === 0 ? \"\" : `<table><thead><tr><th>field</th><th>value</th><th>per-field TTL</th></tr></thead><tbody>${rows}</tbody></table>`}`;\n    });\n    document.getElementById(\"batch-button\").addEventListener(\"click\", async () => {\n      const count = parseInt(document.getElementById(\"batch-count\").value, 10) || 1;\n      const fields = selectedReadFields();\n      const body = new URLSearchParams();\n      body.set(\"count\", count);\n      for (const f of fields) body.append(\"field\", f);\n      const r = await fetch(\"/batch-read\", { method: \"POST\", body });\n      const d = await r.json();\n      if (!r.ok) { setStatus(d.error || \"Batch read failed.\", \"error\"); return; }\n      const perEntity = d.entity_count === 0 ? 0 : d.elapsed_ms / d.entity_count;\n      const rows = (d.sample || []).map((row) => `<tr><td class=\"mono\">${escapeHtml(row.id)}</td><td>${row.field_count}</td></tr>`).join(\"\");\n      batchResult.innerHTML = `\n        <p>Pipelined <code>HMGET</code> across <strong>${d.entity_count}</strong> users\n          (${d.field_count} field(s) each) in <strong>${d.elapsed_ms.toFixed(2)} ms</strong>\n          (~${perEntity.toFixed(3)} ms / user, one network round trip total).</p>\n        ${(d.sample || []).length === 0 ? \"\" : `<h3>Sample</h3><table><thead><tr><th>user</th><th>fields returned</th></tr></thead><tbody>${rows}</tbody></table>`}`;\n    });\n    document.getElementById(\"inspect-button\").addEventListener(\"click\", async () => {\n      const user = inspectUserSelect.value;\n      if (!user) { setStatus(\"Pick a user first.\", \"error\"); return; }\n      const params = new URLSearchParams({ user });\n      const r = await fetch(`/inspect?${params.toString()}`);\n      const d = await r.json();\n      if (!r.ok) { setStatus(d.error || \"Inspect failed.\", \"error\"); return; }\n      const rows = (d.fields || []).map((f) => `<tr>\n        <td><span class=\"badge ${classifyField(f.name)}\">${classifyField(f.name)}</span> <span class=\"mono\">${escapeHtml(f.name)}</span></td>\n        <td class=\"mono\">${escapeHtml(f.value)}</td>\n        <td>${ttlLabel(f.ttl_seconds)}</td></tr>`).join(\"\");\n      inspectResult.innerHTML = d.exists === false\n        ? `<p><span class=\"badge expired\">missing</span> <code>${escapeHtml(user)}</code> isn't in the store. Either it was never materialized or its key-level TTL expired.</p>`\n        : `<p>Key-level TTL: ${ttlLabel(d.key_ttl_seconds)} &nbsp; (${d.fields.length} field(s))</p>\n           <table><thead><tr><th>field</th><th>value</th><th>per-field TTL</th></tr></thead><tbody>${rows}</tbody></table>`;\n    });\n\n    renderFieldPicker(); refresh(); setInterval(refresh, 1500);\n  </script>\n</body>\n</html>"}],"examples":[]}
{"schema_version":2,"id":"develop/use-cases/feature-store/php","title":"Redis feature store with Predis","url":"https://redis.io/docs/latest/develop/use-cases/feature-store/php/","summary":"Build a Redis-backed online feature store in PHP with Predis","aliases":["/develop/use-cases/feature-store/predis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:50:30+01:00","children":[{"id":"develop/use-cases/feature-store/php/demo_template.html","summary":"","title":"","url":"https://redis.io/docs/latest/develop/use-cases/feature-store/php/demo_template/"}],"page_type":"content","content_hash":"bc876b09fc0e3e5d90a4e9ac3ba711d31392cd39e6f78c0ddad77e93abe925a4","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a small Redis-backed online feature store\nin PHP with [Predis](https://redis.io/docs/latest/develop/clients/php). The demo runs\non top of PHP's built-in development server (`php -S`) and uses a detached\nCLI process for the streaming worker, so you can bulk-load a batch of users\nwith a key-level TTL, watch real-time features expire per-field via\n`HEXPIRE`, retrieve any subset of features for one user under 2 ms, and\npipeline `HMGET` across a hundred users for batch scoring."},{"id":"overview","title":"Overview","role":"overview","text":"Each entity (here, a user) is one Redis\n[Hash](https://redis.io/docs/latest/develop/data-types/hashes) at a deterministic key —\n`fs:user:{id}`. The hash holds every feature for that entity as one field per\nfeature: batch-materialized aggregates (refreshed once a day) alongside\nstreaming-updated signals (refreshed every few seconds). One\n[`HMGET`](https://redis.io/docs/latest/commands/hmget) returns whichever subset the\nmodel needs in one network round trip.\n\nTwo TTL layers solve the *mixed staleness* problem without an\napplication-side cleaner:\n\n* A **key-level** [`EXPIRE`](https://redis.io/docs/latest/commands/expire) aligned with\n  the batch materialization cycle (24 hours in the demo). If the batch\n  refresher fails, the whole entity disappears at the next cycle and\n  inference sees a missing entity — which the model handler can detect and\n  fall back on — rather than silently outdated values.\n* A **per-field** [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) (Redis 7.4+)\n  on each streaming feature gives that field its own shorter expiry,\n  independent of the rest of the hash. If the streaming pipeline stops\n  updating a feature, the field self-cleans while the batch fields stay\n  populated.\n\nThat gives you:\n\n* A single round trip for retrieval — any subset of features for one entity\n  in one [`HMGET`](https://redis.io/docs/latest/commands/hmget).\n* Sub-millisecond hot path. The Redis-side work is microseconds; in\n  practice the bottleneck is the network round trip plus the model's own\n  feature-prep.\n* Pipelined batch scoring — one round trip for `N` users at once.\n* Independent freshness per feature, expressed as a server-side TTL rather\n  than as application logic.\n* Self-cleanup on pipeline failure: a stalled batch refresher lets entities\n  expire on schedule, and a stalled streaming worker lets each affected\n  field expire on its own timer."},{"id":"how-phps-request-model-shapes-the-demo","title":"How PHP's request model shapes the demo","role":"content","text":"PHP's hosting model is different from every other client in this use case.\n`php -S` gives each request a fresh PHP execution context, so a long-lived\nstreaming worker can't live inside the demo router the way it does in\nPython, Node.js, Go, or Java. The demo handles this by spawning the\nstreaming worker as a **separate, detached CLI process** the first time\nthe demo server is hit. The router and the worker then share state through\nRedis itself:\n\n* `fs:control:worker_pid` — PID of the running worker. The router checks\n  it on every request and respawns the worker if the PID is no longer\n  alive.\n* `fs:control:paused` — `1` while paused, `0` otherwise. The worker polls\n  this between ticks.\n* `fs:control:tick_in_flight` — set by the worker *before* each tick and\n  cleared after. The router's `/reset` handler waits for this to flip to\n  `0` before it issues the `DEL` sweep.\n* `fs:control:tick_count` / `fs:control:writes_count` — counters the\n  router reads to populate the UI.\n* `fs:control:stop` — graceful-shutdown flag the worker checks each tick.\n\nThis is the same race-free pause-and-wait-idle pattern as every other\nclient; it's just implemented through Redis primitives because there's no\nshared memory between the router and the worker.\n\nPredis-specific notes:\n\n* Predis 3 ships typed `hexpire()` and `httl()` methods. The helper uses\n  them directly. `HEXPIRE` returns one status code per field (`1` set,\n  `2` deleted because TTL was 0/past, `0` conditional flag not met, `-2`\n  no such field/key).\n* Predis 3's `hset()` accepts variadic `field, value, field, value, ...`\n  pairs but **not** a single field=>value map argument the way Predis 2\n  did. The helper flattens the encoded map and spreads it:\n  `$pipe->hset($key, ...$flat)`."},{"id":"how-it-works","title":"How it works","role":"content","text":"There are three paths: a **batch path** that bulk-loads features once per\nmaterialization cycle, a **streaming path** that updates real-time features\nas events arrive, and an **inference path** that reads features on the\nrequest side."},{"id":"batch-path-per-materialization-cycle","title":"Batch path (per materialization cycle)","role":"content","text":"1. The batch job calls `BuildFeatures::synthesizeUsers($count, $seed)`\n   (in production, the equivalent computation lives in an offline\n   pipeline against the warehouse). The result is\n   `array<string, array<string, mixed>>` keyed by user ID.\n2. `$store->bulkLoad($rows, $ttlSeconds)` queues one\n   [`HSET`](https://redis.io/docs/latest/commands/hset) plus one\n   [`EXPIRE`](https://redis.io/docs/latest/commands/expire) per user through\n   `$redis->pipeline(function ($pipe) { ... })`, so the whole batch ships\n   in a single round trip."},{"id":"streaming-path-per-tick","title":"Streaming path (per tick)","role":"content","text":"The detached `streaming_worker.php` process polls Redis once per tick and\ncalls `$store->updateStreaming($userId, $fields)` for a handful of random\nusers. That queues:\n\n1. An [`HSET`](https://redis.io/docs/latest/commands/hset) writing the new field\n   values. Redis is single-threaded per shard, so this is atomic against\n   any concurrent batch write on the same hash — no version columns, no\n   locks.\n2. An [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) over exactly the\n   fields that were written, with the streaming TTL. Each streaming field\n   carries its own per-field expiry independent of the rest of the hash.\n   Pause the worker (or stop it entirely) and these fields drop out one\n   by one as their TTLs elapse, while the batch fields remain populated\n   under the longer key-level TTL."},{"id":"inference-path-per-http-request","title":"Inference path (per HTTP request)","role":"content","text":"1. The model server picks the feature subset it needs (the schema is\n   owned by the model, not the store).\n2. It calls `$store->getFeatures($userId, $names)`, which is one\n   [`HMGET`](https://redis.io/docs/latest/commands/hmget). Redis returns the values\n   in the same order as the requested fields, with `null` for any field\n   that doesn't exist (or has expired).\n3. For batch inference, the model server calls\n   `$store->batchGetFeatures($userIds, $names)`, which pipelines one\n   [`HMGET`](https://redis.io/docs/latest/commands/hmget) per user across all `N`\n   users in a single network round trip."},{"id":"project-layout","title":"Project layout","role":"content","text":"[code example]\n\nRun the demo with `composer install && composer start`, or directly:\n`php -S 127.0.0.1:8094 demo_server.php`."},{"id":"the-feature-store-helper","title":"The feature-store helper","role":"content","text":"The `FeatureStore` class wraps the read/write paths\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/feature-store/php/FeatureStore.php)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each user is one Redis Hash. Every value is stored as a string — Redis\nhash fields are bytes on the wire, so the helper encodes booleans as\n`'true'` / `'false'` (`FeatureStore::encodeValue()`) and uses\n`(string)$value` for everything else. The model server is responsible\nfor parsing back to the right type, the same way it would when reading\nany serialized feature store.\n\n[code example]"},{"id":"bulk-loading-batch-features","title":"Bulk-loading batch features","role":"content","text":"`bulkLoad` queues one `HSET` and one `EXPIRE` per user through\n`$redis->pipeline(...)`, so the whole batch ships in a single round trip.\n\n[code example]\n\n`$redis->pipeline(callable)` is a non-transactional batch: commands queue\nup and ship in one round trip but they don't run inside a `MULTI/EXEC`\nblock. That's the right choice here because each user's `HSET` +\n`EXPIRE` pair is independent of every other user's. For the rare case\nwhere the pair has to be inseparable, use `$redis->transaction(...)` (or\na Lua script via [`EVAL`](https://redis.io/docs/latest/commands/eval) /\n[Eval scripting](https://redis.io/docs/latest/develop/programmability/eval-intro)).\n\nIn production, the equivalent of this script runs as an offline pipeline\n(a Spark or Feast `materialize` job) that reads from the warehouse and\nwrites into Redis. The\n[Feast `RedisOnlineStore`](https://docs.feast.dev/reference/online-stores/redis)\nprovider does exactly this under the hood; the in-house\n[Redis Feature Form](https://redis.io/docs/latest/develop/ai/featureform) integration\ncovers the materialize + serve path end-to-end."},{"id":"streaming-writes-with-per-field-ttl","title":"Streaming writes with per-field TTL","role":"content","text":"`updateStreaming` is the linchpin of the mixed-staleness story:\n\n[code example]\n\n[`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) sets the TTL on\n*individual* hash fields, not on the whole key. The two commands are\nqueued in the same `pipeline` callback so Redis runs them in order: the\n`HSET` first creates or overwrites the fields, then `HEXPIRE` attaches a\nTTL to each of those same fields. `hexpire()` returns one status code\nper field:\n\n* `1` — TTL set / updated.\n* `2` — the expiry was 0 or in the past, so Redis deleted the field\n  instead of applying a TTL.\n* `0` — an `NX | XX | GT | LT` conditional flag was specified and not\n  met (we never use one here).\n* `-2` — no such field, or no such key.\n\nThe helper throws if any code is anything other than `1`, so the \"every\nstreaming write renews its TTL\" invariant fails loudly rather than\nsilently leaving a streaming field with no expiry attached.\n\nIf a streaming pipeline stops, the streaming fields drop out one by one\nas their per-field TTLs elapse. `fieldTtlsSeconds` (which wraps `httl()`)\nlets the model side inspect the remaining TTL on any field — useful\nboth for debugging and as a freshness signal in the model itself.\n\n> **HEXPIRE requires Redis 7.4 or later.** `HEXPIRE` and the field-level\n> TTL commands were added in Redis 7.4. Predis 3.0 was the first major\n> release with the typed bindings; the demo's `composer.json` pins\n> `^3.0`."},{"id":"inference-reads-with-hmget","title":"Inference reads with HMGET","role":"content","text":"`getFeatures` is one `HMGET`:\n\n[code example]\n\nThe model knows exactly which features it consumes, so the request path\nalways takes the `hmget` branch with an explicit field list — that's\nthe sub-millisecond path. `hgetall` is the right call for debugging\n(which is what the demo's \"Inspect\" panel does) but not for serving:\nit forces Redis to serialize every field, including ones the model\ndoesn't need.\n\nFields that don't exist (because they were never written, or because\nthey expired) come back as `null`. The helper drops them from the\nresult array so the caller sees only the features that are actually\navailable."},{"id":"batch-scoring-with-pipelined-hmget","title":"Batch scoring with pipelined HMGET","role":"content","text":"For batch inference, the same `HMGET` shape pipelines across users:\n\n[code example]\n\nOne round trip for the whole batch. The demo regularly returns a\n30-user batch in ~1 ms against a local Redis.\n\nA Redis Cluster is different: a single `pipeline()` block ships through\none connection to one node. For batch reads on a cluster, configure\nPredis with a cluster connection profile and fan out parallel\nnon-pipelined `hmget` calls (the cluster client routes each one to the\nright shard), or group entity IDs by hash slot and run one pipeline\nagainst each shard's node-connection in parallel. A hash tag like\n`fs:user:{vip}:u0001` forces a known set of keys onto the same shard\nso one pipeline can cover them all in a single round trip."},{"id":"the-streaming-worker","title":"The streaming worker","role":"content","text":"`streaming_worker.php` is a small CLI shim that loads `StreamingWorker`\nand runs its tick loop until the demo server flips\n`fs:control:stop` to `1` (or SIGTERM lands). The class itself lives in\n`StreamingWorker.php`\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/feature-store/php/StreamingWorker.php)):\n\n[code example]\n\nThe pre-flight `tick_in_flight = 1` before the pause check, and the\nouter `finally` block that clears every control key on every exit\npath, are the same correctness levers as every other client in this\nuse case. The only difference is that the flags live in Redis rather\nthan in process memory.\n\nThe demo server's `/reset` handler reads the same Redis keys: it sets\n`fs:control:paused = 1`, polls `fs:control:tick_in_flight` until it\nsees `0`, then issues the `DEL` sweep. That's the cross-process\nequivalent of `worker.pause() + worker.wait_for_idle()` in the\nsingle-process clients.\n\n`demo_server.php` spawns the worker on the first request with\n`nohup ... &` (detached so it survives the per-request `php -S`\nprocess) and checks `pid_alive($pid)` on every subsequent request.\nIf the worker has died, it's respawned on the next request.\n\nTo shut the worker down cleanly from outside the demo (the detached\nprocess isn't tied to the foreground `php -S`), flip the stop flag\nwith `redis-cli`:\n\n[code example]\n\nThe worker's tick loop checks `fs:control:stop` at the top of every\niteration and exits, clearing every `fs:control:*` key on the way\nout so the next demo run starts from a clean slate."},{"id":"the-batch-builder","title":"The batch builder","role":"content","text":"`build_features.php` is the demo's nightly materializer\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/feature-store/php/build_features.php)).\nIt generates synthetic feature rows and calls `$store->bulkLoad()`\nonce. The synthesis itself is not the point — in a real deployment the\nequivalent code reads from the offline store (Snowflake, BigQuery,\nIceberg) and writes the resulting hashes into Redis.\n\nRun the builder on its own (independently of the demo server) to\npopulate Redis from the command line:\n\n[code example]\n\nThat writes 500 users at `fs:user:*` with a one-hour key-level TTL,\nwhich is how a typical operator would pre-seed a feature store from\nthe command line when debugging."},{"id":"the-interactive-demo","title":"The interactive demo","role":"content","text":"`demo_server.php` runs as a router script under `php -S` on port 8094.\nThe HTML page (loaded via `file_get_contents` from\n`demo_template.html`) lets you:\n\n* **Bulk-load** any number of users (default 200) with a configurable\n  key-level TTL.\n* See the **store state**: user count, batch / streaming TTLs,\n  cumulative read/write counters.\n* See the **streaming worker** status and **pause or resume** it. The\n  pause flag goes into Redis at `fs:control:paused`; the detached\n  worker process reads it between ticks.\n* Run an **inference read** for any user with a chosen feature subset,\n  and see the value, the per-field TTL, and the read latency.\n* Run **batch scoring** with a pipelined `HMGET` across `N` users.\n* **Inspect** any user's full hash with field-level TTLs and the\n  key-level TTL.\n\nEndpoints:\n\n| Endpoint                  | What it does                                                                        |\n|---------------------------|-------------------------------------------------------------------------------------|\n| `GET  /state`             | User count, TTL config, stats counters, worker status.                              |\n| `POST /bulk-load`         | Pipelined `HSET` + `EXPIRE` over N synthetic users with a chosen TTL.               |\n| `POST /worker/toggle`     | Pause / resume the streaming worker.                                                |\n| `POST /read`              | `HMGET` a chosen feature subset for one user; report latency and per-field TTLs.    |\n| `POST /batch-read`        | Pipeline `HMGET` across N users; report total latency and per-entity field counts.  |\n| `GET  /inspect`           | `HGETALL` + `HTTL` for one user; full hash view with per-field TTLs.                |\n| `POST /reset`             | Drop every user under the key prefix (used by the demo's reset button).             |\n\n> **PHP's `$_POST` doesn't preserve repeated keys.** The demo's `/read`\n> and `/batch-read` handlers parse the raw `php://input` body\n> manually via `parse_multi_form()` so the model can request several\n> features in one call (`field=a&field=b&field=c`). PHP's built-in\n> form-parsing would keep only the last `field=` value."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* **Redis 7.4 or later.** [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire)\n  and [`HTTL`](https://redis.io/docs/latest/commands/httl) were added in Redis 7.4;\n  the demo relies on per-field TTL for the mixed-staleness story.\n* **PHP 8.1 or later.** The demo uses readonly properties, named\n  arguments, and first-class callable syntax.\n* **Predis 3.0 or later.** The demo's `composer.json` pins `^3.0`.\n  Typed bindings for the field-TTL commands ship from 3.0.\n* **A POSIX shell environment** for the worker spawn (`nohup`,\n  `posix_kill`). The demo has been tested on macOS and Linux; Windows\n  would need a different process-detach approach.\n\nIf your Redis server is running elsewhere, start the demo with\n`REDIS_URI=tcp://host:port php -S 127.0.0.1:8094 demo_server.php`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo lives in a small Composer project under\n[`feature-store/php`](https://github.com/redis/docs/tree/main/content/develop/use-cases/feature-store/php).\nClone the repo or copy the directory:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From the project directory:\n\n[code example]\n\nThe first request to the server triggers the one-time bootstrap\n(reset + seed the store, spawn the streaming worker). You should see:\n\n[code example]\n\nOpen [http://127.0.0.1:8094](http://127.0.0.1:8094). Useful things to\ntry:\n\n* Pick a user and click **Read features** with a mixed batch/streaming\n  subset — you'll see batch fields with no per-field TTL (covered by\n  the key-level TTL) and streaming fields with a positive per-field\n  TTL.\n* Click **Pipeline HMGET** with `count=100` to see the latency of a\n  100-user batch read.\n* Click **Pause / resume** on the streaming worker and leave it\n  paused for ~5 minutes (or restart the server with\n  `STREAMING_TTL_SECONDS=30` to make it visible in seconds). Re-run\n  **Read features** on any user and watch the streaming fields\n  disappear while the batch fields stay.\n* Click **Inspect** on a user to see the full hash with field-level\n  TTLs.\n* Click **Reset** to drop every user and start over."},{"id":"production-usage","title":"Production usage","role":"content","text":"The guidance below focuses on the production concerns specific to\nrunning a feature store on Redis. For the generic Predis production\nchecklist — connection options,\n[transactions and pipelining](https://redis.io/docs/latest/develop/clients/php/transpipe),\nTLS, AUTH, error handling — see the\n[Predis client guide](https://redis.io/docs/latest/develop/clients/php) and the\n[connect recipe](https://redis.io/docs/latest/develop/clients/php/connect)."},{"id":"dont-run-php--s-in-production","title":"Don't run `php -S` in production","role":"content","text":"The built-in PHP development server is single-threaded and not\nproduction-grade. A real deployment runs PHP-FPM behind nginx or\nApache, with the streaming worker as a separate systemd / supervisord /\nKubernetes-cron-job process. The router script in `demo_server.php` is\nshaped for the demo; for production, extract the route handlers into a\nproper PHP framework (Symfony, Laravel, Slim) that pools `Predis\\Client`\nconnections per-worker."},{"id":"pick-the-batch-ttl-to-outlast-a-failed-refresher","title":"Pick the batch TTL to outlast a failed refresher","role":"content","text":"The whole-entity `EXPIRE` is your safety net against silent staleness\nfrom a broken batch pipeline. Set it longer than your worst-case batch\noutage so a single missed run doesn't take the feature store offline,\nbut short enough that a sustained outage causes loud failures (missing\nentities) rather than quiet ones (yesterday's features being scored as\ntoday's). The standard choice is one cycle of \"expected refresh\ninterval × 2\" — for a daily batch, 48 hours; for a 6-hour batch, 12\nhours.\n\nThe same logic applies to the per-field streaming TTL: a few times the\nexpected update interval so a slow-but-alive streaming worker doesn't\nchurn features needlessly, but short enough that a stalled worker\ncauses visible freshness failures."},{"id":"co-locate-the-online-store-with-serving-not-with-training","title":"Co-locate the online store with serving, not with training","role":"content","text":"The online store's hash representation does *not* have to match the\nschema in your offline store. The batch materialization step is your\nchance to flatten joins, encode categoricals, and project to whatever\nshape the model server wants — so the request path is exactly one\n`HMGET` and zero transforms.\n\nThe training pipeline reads from the offline store with its own\nschema; the serving pipeline reads from Redis with the flattened\nserving schema. Keeping those two pipelines as the same code path is\nwhat prevents training-serving skew."},{"id":"run-the-streaming-worker-as-a-real-process-supervisor","title":"Run the streaming worker as a real process supervisor","role":"content","text":"The demo spawns the worker with `nohup ... &` because it's the\nsimplest portable thing that works under `php -S`. In production,\nmanage the worker process with systemd / supervisord / Kubernetes —\nsomething that restarts it on crash, captures its logs properly, and\ngives you a clean shutdown path. The Redis-backed `fs:control:*`\nstate (pause flag, in-flight flag, counters) keeps working\nunchanged — that's the point of putting it in Redis."},{"id":"make-hexpire-part-of-every-streaming-write","title":"Make HEXPIRE part of every streaming write","role":"content","text":"The single biggest correctness lever in this design is that the\nstreaming write applies `HEXPIRE` *every time*. If a streaming worker\nwrites a field without renewing its TTL, the field carries whatever\nexpiry was there before — possibly none, possibly stale — and the\nmixed-staleness invariant breaks. Keep the `HSET` and `HEXPIRE` in\nthe same pipeline (or, even safer, in the same\n[Lua script](https://redis.io/docs/latest/develop/programmability/eval-intro) if\nyou don't trust the call site)."},{"id":"avoid-hgetall-on-the-request-path","title":"Avoid HGETALL on the request path","role":"content","text":"`HGETALL` reads every field on the hash, including ones the model\ndoesn't need. With dozens of features per entity, that is wasted\nserialization work on the server and wasted bandwidth on the wire.\nAlways specify the field list explicitly with `hmget` in the model\nserver.\n\nThe exception is debugging and feature-set discovery, where you\ngenuinely want the full hash. The demo's \"Inspect\" button uses\n`hgetall` for exactly this reason."},{"id":"inspect-the-store-directly-with-redis-cli","title":"Inspect the store directly with redis-cli","role":"content","text":"When testing or troubleshooting, the cli tells you everything:\n\n[code example]\n\nA streaming field that returns `-2` from `HTTL` doesn't exist on the\nhash (either it was never written, or it expired); `-1` means the\nfield has no TTL set (and is therefore covered only by the key-level\n`EXPIRE`); any positive value is the remaining TTL in seconds."},{"id":"learn-more","title":"Learn more","role":"related","text":"This example uses the following Redis commands:\n\n* [`HSET`](https://redis.io/docs/latest/commands/hset) to write a feature or a\n  whole feature row in one call.\n* [`HMGET`](https://redis.io/docs/latest/commands/hmget) to retrieve any subset\n  of features for one entity in one round trip.\n* [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) for debugging and\n  feature-set discovery.\n* [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) and\n  [`HTTL`](https://redis.io/docs/latest/commands/httl) for per-field TTL on\n  streaming features (Redis 7.4+).\n* [`EXPIRE`](https://redis.io/docs/latest/commands/expire) and\n  [`TTL`](https://redis.io/docs/latest/commands/ttl) for the whole-entity TTL\n  aligned with the batch materialization cycle.\n* Pipelined `HMGET` across many entities for batch scoring with one\n  network round trip — see\n  [transactions and pipelining](https://redis.io/docs/latest/develop/clients/php/transpipe).\n\nSee the [Predis documentation](https://redis.io/docs/latest/develop/clients/php)\nfor the full client reference, and the\n[Hashes overview](https://redis.io/docs/latest/develop/data-types/hashes) for the\ndeeper conceptual model."}],"examples":[{"id":"project-layout-ex0","language":"text","code":"feature-store/php/\n├── composer.json           — predis/predis ^3, PHP >= 8.1\n├── FeatureStore.php        — FeatureStore class\n├── StreamingWorker.php     — worker tick loop (used by the CLI process)\n├── BuildFeatures.php       — synthesize_users + helpers\n├── build_features.php      — CLI entry point for the materializer\n├── streaming_worker.php    — CLI entry point for the worker process\n├── demo_server.php         — php -S router (HTTP routes + worker spawn)\n└── demo_template.html      — HTML page, loaded by file_get_contents","section_id":"project-layout"},{"id":"the-feature-store-helper-ex0","language":"php","code":"<?php\nrequire __DIR__ . '/vendor/autoload.php';\nrequire __DIR__ . '/FeatureStore.php';\n\nuse Predis\\Client;\n\n$redis = new Client('tcp://127.0.0.1:6379');\n$store = new FeatureStore(\n    $redis,\n    keyPrefix: 'fs:user:',\n    batchTtlSeconds: 24 * 60 * 60,    // whole-entity TTL aligned with the daily batch cycle\n    streamingTtlSeconds: 5 * 60,      // per-field TTL on each streaming feature\n);\n\n// Batch materialization: one HSET + EXPIRE per user, all pipelined.\n$store->bulkLoad([\n    'u0001' => [\n        'country_iso' => 'US', 'risk_segment' => 'low',\n        'tx_count_7d' => 14, 'avg_amount_30d' => 92.40,\n        'account_age_days' => 612, 'chargeback_count_180d' => 0,\n    ],\n], 24 * 60 * 60);\n\n// Streaming write: HSET + HEXPIRE on just the fields that changed.\n$store->updateStreaming('u0001', [\n    'last_login_ts' => (int)(microtime(true) * 1000),\n    'last_device_id' => 'ios-9f02',\n    'tx_count_5m' => 3,\n    'failed_logins_15m' => 0,\n    'session_country' => 'US',\n], 5 * 60);\n\n// Inference read: HMGET of whatever the model needs.\n$features = $store->getFeatures('u0001', [\n    'risk_segment', 'tx_count_7d', 'avg_amount_30d',\n    'tx_count_5m', 'failed_logins_15m',\n]);\n\n// Batch scoring: pipelined HMGET across many users.\n$batch = $store->batchGetFeatures(\n    ['u0001', 'u0002', 'u0003'],\n    ['risk_segment', 'tx_count_5m', 'failed_logins_15m'],\n);","section_id":"the-feature-store-helper"},{"id":"data-model-ex0","language":"text","code":"fs:user:u0001                                   TTL = 86400 s (key-level)\n  country_iso=US                                <no field TTL>\n  risk_segment=low                              <no field TTL>\n  account_age_days=612                          <no field TTL>\n  tx_count_7d=14                                <no field TTL>\n  avg_amount_30d=92.40                          <no field TTL>\n  chargeback_count_180d=0                       <no field TTL>\n  last_login_ts=1716998413541                   TTL = 300 s (per field, HEXPIRE)\n  last_device_id=ios-9f02                       TTL = 300 s (per field, HEXPIRE)\n  tx_count_5m=3                                 TTL = 300 s (per field, HEXPIRE)\n  failed_logins_15m=0                           TTL = 300 s (per field, HEXPIRE)\n  session_country=US                            TTL = 300 s (per field, HEXPIRE)","section_id":"data-model"},{"id":"bulk-loading-batch-features-ex0","language":"php","code":"public function bulkLoad(array $rows, ?int $ttlSeconds = null): int\n{\n    if (count($rows) === 0) return 0;\n    $ttl = $ttlSeconds ?? $this->batchTtlSeconds;\n    $this->redis->pipeline(function ($pipe) use ($rows, $ttl) {\n        foreach ($rows as $entityId => $fields) {\n            $key = $this->keyFor((string)$entityId);\n            // Predis 3's `hset` accepts variadic field/value pairs\n            // (key, f1, v1, f2, v2, ...) but not a single field=>value\n            // map argument the way Predis 2 did — flatten the encoded\n            // map into that shape.\n            $flat = [];\n            foreach ($fields as $name => $value) {\n                $flat[] = $name;\n                $flat[] = self::encodeValue($value);\n            }\n            $pipe->hset($key, ...$flat);\n            $pipe->expire($key, $ttl);\n        }\n    });\n    ...\n}","section_id":"bulk-loading-batch-features"},{"id":"streaming-writes-with-per-field-ttl-ex0","language":"php","code":"public function updateStreaming(string $entityId, array $fields, ?int $ttlSeconds = null): void\n{\n    if (count($fields) === 0) return;\n    $ttl = $ttlSeconds ?? $this->streamingTtlSeconds;\n    $key = $this->keyFor($entityId);\n    $flat = [];\n    $names = [];\n    foreach ($fields as $name => $value) {\n        $names[] = $name;\n        $flat[] = $name;\n        $flat[] = self::encodeValue($value);\n    }\n\n    $results = $this->redis->pipeline(function ($pipe) use ($key, $flat, $names, $ttl) {\n        $pipe->hset($key, ...$flat);\n        $pipe->hexpire($key, $ttl, $names);\n    });\n    $codes = $results[1] ?? [];\n    foreach ($codes as $code) {\n        if ((int)$code !== 1) {\n            throw new RuntimeException(\n                \"HEXPIRE did not set every field TTL for {$key}: \" . json_encode($codes)\n            );\n        }\n    }\n    ...\n}","section_id":"streaming-writes-with-per-field-ttl"},{"id":"inference-reads-with-hmget-ex0","language":"php","code":"public function getFeatures(string $entityId, ?array $fieldNames): array\n{\n    $key = $this->keyFor($entityId);\n    if ($fieldNames === null) {\n        return $this->redis->hgetall($key);\n    }\n    if (count($fieldNames) === 0) return [];\n    $values = $this->redis->hmget($key, $fieldNames);\n    $out = [];\n    foreach ($fieldNames as $i => $n) {\n        if ($values[$i] !== null) $out[$n] = (string)$values[$i];\n    }\n    return $out;\n}","section_id":"inference-reads-with-hmget"},{"id":"batch-scoring-with-pipelined-hmget-ex0","language":"php","code":"public function batchGetFeatures(array $entityIds, array $fieldNames): array\n{\n    if (count($entityIds) === 0 || count($fieldNames) === 0) return [];\n    $rows = $this->redis->pipeline(function ($pipe) use ($entityIds, $fieldNames) {\n        foreach ($entityIds as $id) {\n            $pipe->hmget($this->keyFor($id), $fieldNames);\n        }\n    });\n    ...\n}","section_id":"batch-scoring-with-pipelined-hmget"},{"id":"the-streaming-worker-ex0","language":"php","code":"public function run(): void\n{\n    $this->redis->set('fs:control:worker_pid', (string)getmypid());\n    $this->redis->set('fs:control:running', '1');\n    // SIGTERM / SIGINT trap so the demo server's shutdown path\n    // can cleanly kill us via `posix_kill($pid, SIGTERM)`.\n    ...\n    try {\n        while (true) {\n            if ($this->redis->get('fs:control:stop') === '1') break;\n            $this->microsleep($this->tickSeconds);\n            if ($this->redis->get('fs:control:stop') === '1') break;\n            // Set tick_in_flight *before* the pause check so a\n            // concurrent pause + wait_for_idle (reset path) can\n            // never observe tick_in_flight=0 in the window between\n            // the pause check and the actual tick call.\n            $this->redis->set('fs:control:tick_in_flight', '1');\n            try {\n                if ($this->redis->get('fs:control:paused') !== '1') {\n                    $this->doTick();\n                }\n            } catch (\\Throwable $e) {\n                fwrite(STDERR, \"[streaming-worker] tick failed: \" . $e->getMessage() . \"\\n\");\n            } finally {\n                $this->redis->set('fs:control:tick_in_flight', '0');\n            }\n        }\n    } finally {\n        // Clear running, tick_in_flight, stop no matter how the loop\n        // exits so a later restart can spin a fresh worker with a\n        // clean slate.\n        $this->redis->del(...[\n            'fs:control:running', 'fs:control:tick_in_flight',\n            'fs:control:worker_pid', 'fs:control:stop',\n        ]);\n    }\n}","section_id":"the-streaming-worker"},{"id":"the-streaming-worker-ex1","language":"bash","code":"redis-cli SET fs:control:stop 1","section_id":"the-streaming-worker"},{"id":"the-batch-builder-ex0","language":"bash","code":"php build_features.php --count 500 --ttl-seconds 3600","section_id":"the-batch-builder"},{"id":"get-the-source-files-ex0","language":"bash","code":"git clone https://github.com/redis/docs.git\ncd docs/content/develop/use-cases/feature-store/php\ncomposer install","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"composer start\n# or, equivalently:\nphp -S 127.0.0.1:8094 demo_server.php","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"[Mon Jun  1 ...] PHP 8.4 Development Server (http://127.0.0.1:8094) started\n[Mon Jun  1 ...] 127.0.0.1:... Accepted","section_id":"start-the-demo-server"},{"id":"inspect-the-store-directly-with-redis-cli-ex0","language":"bash","code":"# How many users currently in the store\nredis-cli --scan --pattern 'fs:user:*' | wc -l\n\n# One user's full hash and key-level TTL\nredis-cli HGETALL fs:user:u0001\nredis-cli TTL    fs:user:u0001\n\n# Per-field TTL on the streaming fields\nredis-cli HTTL fs:user:u0001 FIELDS 5 \\\n  last_login_ts last_device_id tx_count_5m failed_logins_15m session_country\n\n# Sample HMGET as the model would issue it\nredis-cli HMGET fs:user:u0001 risk_segment tx_count_7d avg_amount_30d tx_count_5m\n\n# Inspect the worker's control state\nredis-cli MGET fs:control:worker_pid fs:control:paused \\\n  fs:control:tick_in_flight fs:control:tick_count","section_id":"inspect-the-store-directly-with-redis-cli"}]}
{"schema_version":2,"id":"develop/use-cases/feature-store/redis-py","title":"Redis feature store with redis-py","url":"https://redis.io/docs/latest/develop/use-cases/feature-store/redis-py/","summary":"Build a Redis-backed online feature store in Python with redis-py","aliases":["/develop/use-cases/feature-store/python"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:50:30+01:00","children":[],"page_type":"content","content_hash":"e52b87acb2383bf11752c435dc38a3cf3738eb9e1e19ac17312be493e3703eb3","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a small Redis-backed online feature store in\nPython with [`redis-py`](https://redis.io/docs/latest/develop/clients/redis-py). It\nincludes a local web server built with the Python standard library so you can\nbulk-load a batch of users with a key-level TTL, run a streaming worker that\noverwrites real-time features with per-field TTL, retrieve any subset of\nfeatures for one user under 1 ms, and pipeline `HMGET` across a hundred users\nfor batch scoring."},{"id":"overview","title":"Overview","role":"overview","text":"Each entity (here, a user) is one Redis [Hash](https://redis.io/docs/latest/develop/data-types/hashes)\nat a deterministic key — `fs:user:{id}`. The hash holds every feature for that\nentity as one field per feature: batch-materialized aggregates (refreshed once\na day) alongside streaming-updated signals (refreshed every few seconds). One\n[`HMGET`](https://redis.io/docs/latest/commands/hmget) returns whichever subset the model\nneeds in one network round trip.\n\nTwo TTL layers solve the *mixed staleness* problem without an application-side\ncleaner:\n\n* A **key-level** [`EXPIRE`](https://redis.io/docs/latest/commands/expire) aligned with the\n  batch materialization cycle (24 hours in the demo). If the batch refresher\n  fails, the whole entity disappears at the next cycle and inference sees a\n  missing entity — which the model handler can detect and fall back on —\n  rather than silently outdated values.\n* A **per-field** [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) (Redis 7.4+) on\n  each streaming feature gives that field its own shorter expiry, independent\n  of the rest of the hash. If the streaming pipeline stops updating a feature,\n  the field self-cleans while the batch fields stay populated.\n\nIn this example, the batch features describe a user's longer-term shape\n(`country_iso`, `risk_segment`, `account_age_days`, `tx_count_7d`,\n`avg_amount_30d`, `chargeback_count_180d`) and are bulk-loaded by\n`build_features.py` — the demo's stand-in for a nightly Spark / Feast\nmaterialization job. The streaming features describe what the user is doing\nright now (`last_login_ts`, `last_device_id`, `tx_count_5m`,\n`failed_logins_15m`, `session_country`) and are written by\n`streaming_worker.py` — the demo's stand-in for a Flink / Kafka Streams job.\nThe inference panel of the demo server reads any subset of those features\nthrough `feature_store.py`'s helper class.\n\nThat gives you:\n\n* A single round trip for retrieval — any subset of features for one entity\n  in one [`HMGET`](https://redis.io/docs/latest/commands/hmget).\n* Sub-millisecond hot path. The Redis-side work is microseconds; in practice\n  the bottleneck is the network round trip plus the model's own feature-prep.\n* Pipelined batch scoring — one round trip for `N` users at once.\n* Independent freshness per feature, expressed as a server-side TTL rather\n  than as application logic.\n* Self-cleanup on pipeline failure: a stalled batch refresher lets entities\n  expire on schedule, and a stalled streaming worker lets each affected field\n  expire on its own timer."},{"id":"how-it-works","title":"How it works","role":"content","text":"There are three paths: a **batch path** that bulk-loads features once per\nmaterialization cycle, a **streaming path** that updates real-time features as\nevents arrive, and an **inference path** that reads features on the request\nside."},{"id":"batch-path-per-materialization-cycle","title":"Batch path (per materialization cycle)","role":"content","text":"1. The batch job calls `synthesize_users(N)` (in production, the equivalent\n   computation lives in an offline pipeline against the warehouse). The result\n   is `{user_id: {field: value, ...}}` for every user in this cycle.\n2. `store.bulk_load(rows, ttl_seconds=86400)` pipelines one\n   [`HSET`](https://redis.io/docs/latest/commands/hset) plus one\n   [`EXPIRE`](https://redis.io/docs/latest/commands/expire) per user into a single\n   round trip. The `HSET` writes every batch field; the `EXPIRE` is what makes\n   the entity disappear if the next batch run fails, so inference reads a\n   missing entity rather than silently outdated values."},{"id":"streaming-path-per-event","title":"Streaming path (per event)","role":"content","text":"When a user does something (login, transaction, page view) the streaming layer\ncomputes whatever real-time signals fall out of that event and calls\n`store.update_streaming(user_id, fields, ttl_seconds=300)`. That pipelines:\n\n1. An [`HSET`](https://redis.io/docs/latest/commands/hset) writing the new field values.\n   Redis is single-threaded per shard, so this is atomic against any\n   concurrent batch write on the same hash — no version columns, no locks.\n2. An [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) over exactly the fields\n   that were written, with the streaming TTL. Each streaming field carries\n   its own per-field expiry independent of the rest of the hash. Stop the\n   worker and these fields drop out one by one as their TTLs elapse, while\n   the batch fields remain populated under the longer key-level TTL."},{"id":"inference-path-per-request","title":"Inference path (per request)","role":"content","text":"1. The model server picks the feature subset it needs (the schema is owned by\n   the model, not the store).\n2. It calls `store.get_features(user_id, names)`, which is one\n   [`HMGET`](https://redis.io/docs/latest/commands/hmget). Redis returns the values in\n   the same order as the requested fields, with `None` for any field that\n   doesn't exist (or has expired).\n3. For batch inference, the model server calls\n   `store.batch_get_features(user_ids, names)`, which pipelines one\n   [`HMGET`](https://redis.io/docs/latest/commands/hmget) per user across all `N` users\n   in a single network round trip."},{"id":"the-feature-store-helper","title":"The feature-store helper","role":"content","text":"The `RedisFeatureStore` class wraps the read/write paths\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/feature-store/redis-py/feature_store.py)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each user is one Redis Hash. Every value is stored as a string — Redis hash\nfields are bytes-on-the-wire, so the helper encodes booleans as `\"true\"` /\n`\"false\"` and numbers as their `str(...)` form. The model server is responsible\nfor parsing back to the right type, the same way it would when reading any\nserialized feature store.\n\n[code example]\n\nThe batch fields sit under the key-level `EXPIRE`. The streaming fields each\ncarry their own [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire). If the\nstreaming pipeline stops, the streaming fields drop one by one as their\nper-field TTLs elapse; the batch fields stay until the daily key-level\n`EXPIRE` fires (or the next batch cycle re-pins them)."},{"id":"bulk-loading-batch-features","title":"Bulk-loading batch features","role":"content","text":"`bulk_load` pipelines one `HSET` and one `EXPIRE` per user into a single round\ntrip. With 500 users that's 1000 commands in one network call — Redis processes\nthem sequentially on the server side but the client only pays one RTT.\n\n[code example]\n\n`transaction=False` skips the `MULTI/EXEC` wrapper that\n[`pipeline`](https://redis.io/docs/latest/develop/clients/redis-py/transpipe) defaults to\n— commands still queue and ship in one round trip, but they execute as\nindependent commands rather than as one atomic block. That is the right\nchoice here: each user's `HSET` + `EXPIRE` pair is independent of every\nother user's, and an all-or-nothing transaction would block the server for\nthe duration of the batch. It does *not* make the `HSET` + `EXPIRE` pair\natomic — in the extremely unlikely event the server crashes between the two,\nthe entity exists without a key-level TTL until the next batch run re-pins\nit. For an ingestion script that runs end-to-end every cycle this is fine;\nif you need the pair to be inseparable, wrap each user in its own tiny\n`MULTI/EXEC` or a Lua script (see [`EVAL`](https://redis.io/docs/latest/commands/eval) /\n[Eval scripting](https://redis.io/docs/latest/develop/programmability/eval-intro)).\n\nIn production, the equivalent of this script runs as an offline pipeline (a\nSpark or Feast `materialize` job) that reads from the warehouse and writes\ninto Redis. The\n[Feast `RedisOnlineStore`](https://docs.feast.dev/reference/online-stores/redis)\nprovider does exactly this under the hood."},{"id":"streaming-writes-with-per-field-ttl","title":"Streaming writes with per-field TTL","role":"content","text":"`update_streaming` is the linchpin of the mixed-staleness story:\n\n[code example]\n\n[`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) sets the TTL on *individual*\nhash fields, not on the whole key. The two commands are sent in one round trip\nand Redis executes them in pipeline order: the `HSET` runs first and creates\nor overwrites the fields, then `HEXPIRE` attaches a TTL to each of those same\nfields. `HEXPIRE` returns one status code per field — `1` if the TTL was set,\n`-2` if the field doesn't exist — so the helper raises if any code is anything\nother than `1`. That makes the \"every streaming write renews its TTL\"\ninvariant fail loudly rather than silently leaving a streaming field with no\nexpiry attached.\n\nIf a streaming pipeline stops, the streaming fields drop out one by one as\ntheir per-field TTLs elapse — there is no application-side cleaner involved.\n[`HTTL`](https://redis.io/docs/latest/commands/httl) lets the model side inspect the\nremaining TTL on any field, which is useful both for debugging (\"why is this\nfeature missing?\" → \"it expired three seconds ago\") and as a freshness signal\nin the model itself.\n\n> **HEXPIRE requires Redis 7.4 or later.** `HEXPIRE` and the field-level TTL\n> commands (`HTTL`, `HPERSIST`, `HEXPIREAT`, `HPEXPIRE`, `HPEXPIREAT`,\n> `HPTTL`, `HEXPIRETIME`, `HPEXPIRETIME`) were added in Redis 7.4. On older\n> Redis builds you would have to put streaming features on their own keys\n> (one key per feature, or one key per feature group) and set a key-level\n> `EXPIRE` instead — at the cost of giving up the single-`HMGET` retrieval."},{"id":"inference-reads-with-hmget","title":"Inference reads with HMGET","role":"content","text":"`get_features` is one `HMGET`:\n\n[code example]\n\nThe model knows exactly which features it consumes, so the request path always\ntakes the `HMGET` branch with an explicit field list — that's the\nsub-millisecond path. `HGETALL` is the right call for debugging (which is what\nthe demo's \"Inspect\" panel does) but not for serving: it forces Redis to\nserialize every field, including ones the model doesn't need.\n\nFields that don't exist (because they were never written, or because they\nexpired) come back as `None`. The helper drops them from the result dict so\nthe caller sees only the features that are actually available. A real model\nserver would either treat missing values as a feature (\"this user has no\nstreaming signal yet\") or fall back to a default from the model's training\ndata."},{"id":"batch-scoring-with-pipelined-hmget","title":"Batch scoring with pipelined HMGET","role":"content","text":"For batch inference, the same `HMGET` shape pipelines across users:\n\n[code example]\n\nOne round trip for the whole batch — the demo regularly returns 100 users in\n2-3 ms against a local Redis. On a real network the round trip dominates;\npipelining is what keeps batch scoring practical.\n\nFor very large batches on a clustered deployment, the same shape generalizes\nto one pipeline per shard: bucket the entity IDs by their hash slot\n(`cluster.keyslot(key)`), then issue one pipeline against each shard in\nparallel. `redis-py`'s\n[`RedisCluster` pipeline](https://redis-py.readthedocs.io/en/stable/clustering.html#redis-cluster-pipeline)\nhandles that automatically — the per-user `HMGET` calls are dispatched to the\nright shard transparently."},{"id":"the-streaming-worker","title":"The streaming worker","role":"content","text":"`streaming_worker.py` is the demo's stand-in for whatever Flink, Kafka Streams,\nor bespoke service computes the real-time features\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/feature-store/redis-py/streaming_worker.py)).\nIt runs as a daemon thread next to the demo server so the UI can start, pause,\nand resume it; in production this code would live in the streaming layer.\n\nEvery tick the worker picks a few random users, generates a new value for each\nstreaming feature, and calls `store.update_streaming(user_id, fields)`. The\ndemo defaults to 5 users per tick at 1-second intervals — so a 200-user store\nsees roughly half its users refreshed in the first minute, and most after a\nfew minutes. Drop `--seed-users` or raise `--users-per-tick` if you'd rather\nhave every user touched quickly.\n\n[code example]\n\nPausing the worker is what shows off the mixed-staleness behavior: leave it\npaused for longer than `streaming_ttl_seconds` and the streaming fields\ndisappear from every user's hash one by one, while the batch fields remain\nunder the longer key-level `EXPIRE`. The demo's `Pause / resume` button lets\nyou see this happen in real time."},{"id":"the-batch-builder","title":"The batch builder","role":"content","text":"`build_features.py` is the demo's nightly materializer\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/feature-store/redis-py/build_features.py)).\nIt generates synthetic feature rows and calls `store.bulk_load` once. The\nsynthesis itself is not the point — in a real deployment the equivalent code\nreads from the offline store (Snowflake, BigQuery, Iceberg) and writes the\nresulting hashes into Redis.\n\n[code example]\n\nYou can run the builder on its own (independently of the demo server) to\npopulate Redis from the command line:\n\n[code example]\n\nThat writes 500 users at `fs:user:*` with a one-hour key-level TTL, which is\nhow a typical operator would pre-seed a feature store from the command line\nwhen debugging."},{"id":"the-interactive-demo","title":"The interactive demo","role":"content","text":"`demo_server.py` runs a `ThreadingHTTPServer` on port 8085. The HTML page lets\nyou:\n\n* **Bulk-load** any number of users (default 200) with a configurable\n  key-level TTL. Drop the TTL to 30 s and watch the entire store expire on\n  schedule — the same thing that happens if a daily refresher fails.\n* See the **store state** at a glance: user count, batch / streaming TTLs,\n  cumulative read/write counters.\n* See the **streaming worker** status (running / paused, ticks completed,\n  writes performed) and **pause or resume** it. Leave it paused for longer\n  than the streaming TTL to watch streaming fields drop out.\n* Run an **inference read** for any user with a chosen feature subset, and\n  see the value, the per-field TTL, and the read latency.\n* Run **batch scoring** with a pipelined `HMGET` across `N` users and see\n  the total elapsed time plus the per-user breakdown.\n* **Inspect** any user's full hash with field-level TTLs and the key-level\n  TTL — the right view for debugging \"why is this feature missing?\" at\n  read time.\n\nThe server holds one `RedisFeatureStore` instance and one `StreamingWorker`\nfor the lifetime of the process. Endpoints:\n\n| Endpoint                  | What it does                                                                        |\n|---------------------------|-------------------------------------------------------------------------------------|\n| `GET  /state`             | User count, TTL config, stats counters, worker status.                              |\n| `POST /bulk-load`         | Pipelined `HSET` + `EXPIRE` over N synthetic users with a chosen TTL.               |\n| `POST /worker/toggle`     | Pause / resume the streaming worker.                                                |\n| `POST /read`              | `HMGET` a chosen feature subset for one user; report latency and per-field TTLs.    |\n| `POST /batch-read`        | Pipeline `HMGET` across N users; report total latency and per-entity field counts.  |\n| `GET  /inspect`           | `HGETALL` + `HTTL` for one user; full hash view with per-field TTLs.                |\n| `POST /reset`             | Drop every user under the key prefix (used by the demo's reset button).             |"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* **Redis 7.4 or later.** [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) and\n  [`HTTL`](https://redis.io/docs/latest/commands/httl) were added in Redis 7.4; the demo\n  relies on per-field TTL for the mixed-staleness story.\n* **Python 3.9 or later.**\n* The `redis-py` client. Install it with:\n\n  [code example]\n\n  Field-level TTL commands (`hexpire`, `httl`) were added to `redis-py` in 5.1.\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host`\nand `--redis-port`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of four Python files. Download them from the\n[`redis-py` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/feature-store/redis-py)\non GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From that directory:\n\n[code example]\n\nYou should see:\n\n[code example]\n\nBy default the demo wipes the configured key prefix on startup so each run\nstarts from a clean state. Pass `--no-reset` to keep any existing data, or\n`--key-prefix <prefix>` to point the demo at a different prefix entirely.\n\nOpen [http://127.0.0.1:8085](http://127.0.0.1:8085) in a browser. Useful things\nto try:\n\n* Pick a user and click **Read features** with a mixed batch/streaming subset\n  — you'll see batch fields with no per-field TTL (covered by the key-level\n  TTL) and streaming fields with a positive per-field TTL.\n* Click **Pipeline HMGET** with `count=100` to see the latency of a 100-user\n  batch read.\n* Click **Pause / resume** on the streaming worker and leave it paused for\n  ~5 minutes (or restart the server with `--streaming-ttl-seconds 30` to\n  make it visible in seconds). Re-run **Read features** on any user and\n  watch the streaming fields disappear while the batch fields stay.\n* Click **Inspect** on a user to see the full hash with field-level TTLs.\n* Click **Bulk-load** with a short TTL (say 30 seconds) and watch the user\n  count fall to zero on the next minute — the same thing that happens if a\n  daily batch run fails to land.\n* Click **Reset** to drop every user and start over.\n\nThe server is read/write against your local Redis. The default key prefix is\n`fs:user:`. Pass `--no-reset` to keep existing data across restarts, or\n`--redis-host` / `--redis-port` to point at a different Redis."},{"id":"production-usage","title":"Production usage","role":"content","text":"The guidance below focuses on the production concerns that are specific to\nrunning a feature store on Redis. For the generic redis-py production checklist\n— connection-pool sizing,\n[TLS and AUTH](https://redis.io/docs/latest/develop/clients/redis-py/connect#connect-to-your-production-redis-with-tls),\n[exception handling](https://redis.io/docs/latest/develop/clients/redis-py/produsage#exception-handling),\nand the rest — see the\n[redis-py production usage guide](https://redis.io/docs/latest/develop/clients/redis-py/produsage).\nThe feature-store demo runs against `localhost` with the defaults; a real\ndeployment should harden the client first."},{"id":"pick-the-batch-ttl-to-outlast-a-failed-refresher","title":"Pick the batch TTL to outlast a failed refresher","role":"content","text":"The whole-entity `EXPIRE` is your safety net against silent staleness from a\nbroken batch pipeline. Set it longer than your worst-case batch outage so a\nsingle missed run doesn't take the feature store offline, but short enough\nthat a sustained outage causes loud failures (missing entities) rather than\nquiet ones (yesterday's features being scored as today's). The standard\nchoice is one cycle of \"expected refresh interval × 2\" — for a daily batch,\n48 hours; for a 6-hour batch, 12 hours.\n\nThe same logic applies to the per-field streaming TTL: a few times the\nexpected update interval so a slow-but-alive streaming worker doesn't churn\nfeatures needlessly, but short enough that a stalled worker causes visible\nfreshness failures."},{"id":"co-locate-the-online-store-with-serving-not-with-training","title":"Co-locate the online store with serving, not with training","role":"content","text":"The online store's hash representation does *not* have to match the schema in\nyour offline store. The batch materialization step is your chance to flatten\njoins, encode categoricals, and project to whatever shape the model server\nwants — so the request path is exactly one `HMGET` and zero transforms.\n\nThe training pipeline reads from the offline store with its own schema; the\nserving pipeline reads from Redis with the flattened serving schema. Keeping\nthose two pipelines as the same code path is what prevents training-serving\nskew."},{"id":"pipeline-batch-reads-across-shards","title":"Pipeline batch reads across shards","role":"content","text":"On a single Redis instance, pipelining `HMGET` across `N` users is one round\ntrip. On a Redis Cluster, the keys land on different shards — `redis-py`'s\n[`RedisCluster` client](https://redis-py.readthedocs.io/en/stable/clustering.html)\ndispatches each `HMGET` to the right shard transparently, but you still pay\none round trip per shard rather than one for the whole batch. For very\nlatency-sensitive batch inference, group users by shard slot\n(`cluster.keyslot(key)`) and issue one pipeline per shard in parallel.\n\nFor a small number of frequently-queried users (a top-N customer list, for\nexample), a hash tag like `fs:user:{vip}:u0001` forces the keys onto the same\nshard and lets one pipeline serve them all in one round trip."},{"id":"make-hexpire-part-of-every-streaming-write","title":"Make HEXPIRE part of every streaming write","role":"content","text":"The single biggest correctness lever in this design is that the streaming\nwrite applies `HEXPIRE` *every time*. If a streaming worker writes a field\nwithout renewing its TTL, the field carries whatever expiry was there before\n— possibly none, possibly stale — and the mixed-staleness invariant breaks.\nKeep the `HSET` and `HEXPIRE` in the same pipeline (or, even safer, in the\nsame [Lua script](https://redis.io/docs/latest/develop/programmability/eval-intro) if\nyou don't trust the call site)."},{"id":"avoid-hgetall-on-the-request-path","title":"Avoid HGETALL on the request path","role":"content","text":"`HGETALL` reads every field on the hash, including ones the model doesn't\nneed. With dozens of features per entity, that is wasted serialization work\non the server and wasted bandwidth on the wire. Always specify the field list\nexplicitly with `HMGET` in the model server.\n\nThe exception is debugging and feature-set discovery, where you genuinely\nwant the full hash. The demo's \"Inspect\" button uses `HGETALL` for exactly\nthis reason."},{"id":"inspect-the-store-directly-with-redis-cli","title":"Inspect the store directly with redis-cli","role":"content","text":"When testing or troubleshooting, the cli tells you everything:\n\n[code example]\n\nA streaming field that returns `-2` from `HTTL` doesn't exist on the hash\n(either it was never written, or it expired); `-1` means the field has no\nTTL set (and is therefore covered only by the key-level `EXPIRE`); any\npositive value is the remaining TTL in seconds."},{"id":"learn-more","title":"Learn more","role":"related","text":"This example uses the following Redis commands:\n\n* [`HSET`](https://redis.io/docs/latest/commands/hset) to write a feature or a whole\n  feature row in one call.\n* [`HMGET`](https://redis.io/docs/latest/commands/hmget) to retrieve any subset of\n  features for one entity in one round trip.\n* [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) for debugging and\n  feature-set discovery.\n* [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) and\n  [`HTTL`](https://redis.io/docs/latest/commands/httl) for per-field TTL on streaming\n  features (Redis 7.4+).\n* [`EXPIRE`](https://redis.io/docs/latest/commands/expire) and\n  [`TTL`](https://redis.io/docs/latest/commands/ttl) for the whole-entity TTL aligned\n  with the batch materialization cycle.\n* Pipelined `HMGET` across many entities for batch scoring with one network\n  round trip — see [Pipelining](https://redis.io/docs/latest/develop/using-commands/pipelining).\n\nSee the [`redis-py` documentation](https://redis.io/docs/latest/develop/clients/redis-py)\nfor the full client reference, and the\n[Hashes overview](https://redis.io/docs/latest/develop/data-types/hashes) for the deeper\nconceptual model — including the listpack encoding that makes small hashes\nparticularly compact in memory, which matters at feature-store scale."}],"examples":[{"id":"the-feature-store-helper-ex0","language":"python","code":"import redis\nfrom feature_store import RedisFeatureStore\n\nr = redis.Redis(host=\"localhost\", port=6379, decode_responses=True)\nstore = RedisFeatureStore(\n    redis_client=r,\n    key_prefix=\"fs:user:\",\n    batch_ttl_seconds=24 * 60 * 60,    # whole-entity TTL aligned with the daily batch cycle\n    streaming_ttl_seconds=5 * 60,      # per-field TTL on each streaming feature\n)\n\n# Batch materialization: one HSET + EXPIRE per user, all pipelined.\nstore.bulk_load({\n    \"u0001\": {\"country_iso\": \"US\", \"risk_segment\": \"low\",\n              \"tx_count_7d\": 14, \"avg_amount_30d\": 92.40,\n              \"account_age_days\": 612, \"chargeback_count_180d\": 0},\n    \"u0002\": {\"country_iso\": \"GB\", \"risk_segment\": \"medium\",\n              \"tx_count_7d\": 47, \"avg_amount_30d\": 220.10,\n              \"account_age_days\": 1840, \"chargeback_count_180d\": 1},\n})\n\n# Streaming write: HSET + HEXPIRE on just the fields that changed.\nstore.update_streaming(\"u0001\", {\n    \"last_login_ts\": 1716998413541,\n    \"last_device_id\": \"ios-9f02\",\n    \"tx_count_5m\": 3,\n    \"failed_logins_15m\": 0,\n    \"session_country\": \"US\",\n})\n\n# Inference read: HMGET of whatever the model needs.\nfeatures = store.get_features(\"u0001\", [\n    \"risk_segment\", \"tx_count_7d\", \"avg_amount_30d\",\n    \"tx_count_5m\", \"failed_logins_15m\",\n])\n\n# Batch scoring: pipelined HMGET across many users.\nbatch = store.batch_get_features(\n    user_ids=[\"u0001\", \"u0002\", \"u0003\"],\n    field_names=[\"risk_segment\", \"tx_count_5m\", \"failed_logins_15m\"],\n)","section_id":"the-feature-store-helper"},{"id":"data-model-ex0","language":"text","code":"fs:user:u0001                                   TTL = 86400 s (key-level)\n  country_iso=US                                <no field TTL>\n  risk_segment=low                              <no field TTL>\n  account_age_days=612                          <no field TTL>\n  tx_count_7d=14                                <no field TTL>\n  avg_amount_30d=92.40                          <no field TTL>\n  chargeback_count_180d=0                       <no field TTL>\n  last_login_ts=1716998413541                   TTL = 300 s (per field, HEXPIRE)\n  last_device_id=ios-9f02                       TTL = 300 s (per field, HEXPIRE)\n  tx_count_5m=3                                 TTL = 300 s (per field, HEXPIRE)\n  failed_logins_15m=0                           TTL = 300 s (per field, HEXPIRE)\n  session_country=US                            TTL = 300 s (per field, HEXPIRE)","section_id":"data-model"},{"id":"bulk-loading-batch-features-ex0","language":"python","code":"def bulk_load(\n    self,\n    rows: Mapping[str, FeatureMap],\n    ttl_seconds: Optional[int] = None,\n) -> int:\n    ttl = self.batch_ttl_seconds if ttl_seconds is None else ttl_seconds\n    pipe = self.redis.pipeline(transaction=False)\n    for entity_id, fields in rows.items():\n        key = self.key_for(entity_id)\n        pipe.hset(key, mapping={k: _encode(v) for k, v in fields.items()})\n        pipe.expire(key, ttl)\n    pipe.execute()\n    ...","section_id":"bulk-loading-batch-features"},{"id":"streaming-writes-with-per-field-ttl-ex0","language":"python","code":"def update_streaming(\n    self,\n    entity_id: str,\n    fields: FeatureMap,\n    ttl_seconds: Optional[int] = None,\n) -> None:\n    if not fields:\n        return\n    ttl = self.streaming_ttl_seconds if ttl_seconds is None else ttl_seconds\n    key = self.key_for(entity_id)\n    encoded = {name: _encode(value) for name, value in fields.items()}\n\n    pipe = self.redis.pipeline(transaction=False)\n    pipe.hset(key, mapping=encoded)\n    pipe.hexpire(key, ttl, *encoded.keys())\n    pipe.execute()","section_id":"streaming-writes-with-per-field-ttl"},{"id":"inference-reads-with-hmget-ex0","language":"python","code":"def get_features(\n    self,\n    entity_id: str,\n    field_names: Optional[Iterable[str]] = None,\n) -> dict[str, str]:\n    key = self.key_for(entity_id)\n    if field_names is None:\n        return self.redis.hgetall(key)\n    names = list(field_names)\n    if not names:\n        return {}\n    values = self.redis.hmget(key, names)\n    return {n: v for n, v in zip(names, values) if v is not None}","section_id":"inference-reads-with-hmget"},{"id":"batch-scoring-with-pipelined-hmget-ex0","language":"python","code":"def batch_get_features(\n    self,\n    entity_ids: Iterable[str],\n    field_names: Iterable[str],\n) -> dict[str, dict[str, str]]:\n    ids = list(entity_ids)\n    names = list(field_names)\n    if not ids or not names:\n        return {}\n\n    pipe = self.redis.pipeline(transaction=False)\n    for entity_id in ids:\n        pipe.hmget(self.key_for(entity_id), names)\n    rows = pipe.execute()\n\n    out: dict[str, dict[str, str]] = {}\n    for entity_id, values in zip(ids, rows):\n        out[entity_id] = {n: v for n, v in zip(names, values) if v is not None}\n    return out","section_id":"batch-scoring-with-pipelined-hmget"},{"id":"the-streaming-worker-ex0","language":"python","code":"def _tick(self) -> None:\n    ids = self.store.list_entity_ids(limit=500)\n    if not ids:\n        return\n    chosen = self._rng.sample(ids, k=min(self.users_per_tick, len(ids)))\n    now_ms = int(time.time() * 1000)\n    for entity_id in chosen:\n        fields = {\n            \"last_login_ts\": now_ms,\n            \"last_device_id\": self._rng.choice(DEVICE_IDS),\n            \"tx_count_5m\": self._rng.randint(0, 12),\n            \"failed_logins_15m\": self._rng.choices(\n                (0, 1, 2, 5), weights=(70, 20, 8, 2), k=1,\n            )[0],\n            \"session_country\": self._rng.choice(SESSION_COUNTRIES),\n        }\n        self.store.update_streaming(entity_id, fields)","section_id":"the-streaming-worker"},{"id":"the-batch-builder-ex0","language":"python","code":"def synthesize_users(count: int, seed: int = 42) -> dict[str, dict]:\n    rng = random.Random(seed)\n    users: dict[str, dict] = {}\n    for i in range(1, count + 1):\n        uid = f\"u{i:04d}\"\n        users[uid] = {\n            \"country_iso\": rng.choice(COUNTRY_CHOICES),\n            \"risk_segment\": rng.choices(\n                RISK_SEGMENTS, weights=(70, 25, 5), k=1,\n            )[0],\n            \"account_age_days\": rng.randint(7, 2400),\n            \"tx_count_7d\": rng.randint(0, 80),\n            \"avg_amount_30d\": round(rng.uniform(5, 350), 2),\n            \"chargeback_count_180d\": rng.choices(\n                (0, 1, 2, 3), weights=(85, 10, 4, 1), k=1,\n            )[0],\n        }\n    return users","section_id":"the-batch-builder"},{"id":"the-batch-builder-ex1","language":"bash","code":"python3 build_features.py --count 500 --ttl-seconds 3600","section_id":"the-batch-builder"},{"id":"prerequisites-ex0","language":"bash","code":"pip install \"redis>=5.1\"","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir feature-store-demo && cd feature-store-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/feature-store/redis-py\ncurl -O $BASE/feature_store.py\ncurl -O $BASE/build_features.py\ncurl -O $BASE/streaming_worker.py\ncurl -O $BASE/demo_server.py","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"python3 demo_server.py","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"Dropping any existing users under 'fs:user:*' for a clean demo run (pass --no-reset to keep them).\nRedis feature-store demo server listening on http://127.0.0.1:8085\nUsing Redis at localhost:6379 with key prefix 'fs:user:' (batch TTL 86400s, streaming TTL 300s)\nMaterialized 200 user(s); streaming worker running.","section_id":"start-the-demo-server"},{"id":"inspect-the-store-directly-with-redis-cli-ex0","language":"bash","code":"# How many users currently in the store\nredis-cli --scan --pattern 'fs:user:*' | wc -l\n\n# One user's full hash and key-level TTL\nredis-cli HGETALL fs:user:u0001\nredis-cli TTL    fs:user:u0001\n\n# Per-field TTL on the streaming fields\nredis-cli HTTL fs:user:u0001 FIELDS 5 \\\n  last_login_ts last_device_id tx_count_5m failed_logins_15m session_country\n\n# Sample HMGET as the model would issue it\nredis-cli HMGET fs:user:u0001 risk_segment tx_count_7d avg_amount_30d tx_count_5m","section_id":"inspect-the-store-directly-with-redis-cli"}]}
{"schema_version":2,"id":"develop/use-cases/feature-store/ruby","title":"Redis feature store with redis-rb","url":"https://redis.io/docs/latest/develop/use-cases/feature-store/ruby/","summary":"Build a Redis-backed online feature store in Ruby with redis-rb","aliases":["/develop/use-cases/feature-store/redis-rb"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:50:30+01:00","children":[],"page_type":"content","content_hash":"6e81b73f36fafc0351129cfc5c11a4a1fae01bf61fa6b8c2f6151cac39f5f979","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a small Redis-backed online feature store\nin Ruby with the [`redis`](https://redis.io/docs/latest/develop/clients/ruby) gem. The\ndemo runs on top of WEBrick (the stdlib HTTP server) so you can bulk-load a\nbatch of users with a key-level TTL, run a streaming worker that overwrites\nreal-time features with per-field TTL, retrieve any subset of features for\none user under 2 ms, and pipeline `HMGET` across a hundred users for batch\nscoring."},{"id":"overview","title":"Overview","role":"overview","text":"Each entity (here, a user) is one Redis\n[Hash](https://redis.io/docs/latest/develop/data-types/hashes) at a deterministic key —\n`fs:user:{id}`. The hash holds every feature for that entity as one field per\nfeature: batch-materialized aggregates (refreshed once a day) alongside\nstreaming-updated signals (refreshed every few seconds). One\n[`HMGET`](https://redis.io/docs/latest/commands/hmget) returns whichever subset the\nmodel needs in one network round trip.\n\nTwo TTL layers solve the *mixed staleness* problem without an\napplication-side cleaner:\n\n* A **key-level** [`EXPIRE`](https://redis.io/docs/latest/commands/expire) aligned with\n  the batch materialization cycle (24 hours in the demo). If the batch\n  refresher fails, the whole entity disappears at the next cycle and\n  inference sees a missing entity — which the model handler can detect and\n  fall back on — rather than silently outdated values.\n* A **per-field** [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) (Redis 7.4+)\n  on each streaming feature gives that field its own shorter expiry,\n  independent of the rest of the hash. If the streaming pipeline stops\n  updating a feature, the field self-cleans while the batch fields stay\n  populated.\n\nThat gives you:\n\n* A single round trip for retrieval — any subset of features for one entity\n  in one [`HMGET`](https://redis.io/docs/latest/commands/hmget).\n* Sub-millisecond hot path. The Redis-side work is microseconds; in practice\n  the bottleneck is the network round trip plus the model's own\n  feature-prep.\n* Pipelined batch scoring — one round trip for `N` users at once.\n* Independent freshness per feature, expressed as a server-side TTL rather\n  than as application logic.\n* Self-cleanup on pipeline failure: a stalled batch refresher lets entities\n  expire on schedule, and a stalled streaming worker lets each affected\n  field expire on its own timer."},{"id":"how-redis-rb-fits-the-demo","title":"How redis-rb fits the demo","role":"content","text":"Two gem facts shape the helper:\n\n* **One shared `Redis` client serves the whole process.** The `redis` gem\n  uses a single TCP connection per `Redis` instance — and the instance is\n  thread-safe (synchronized with a mutex). Handing the same `Redis` to\n  every WEBrick worker thread and the streaming worker is fine and is the\n  canonical way to run this kind of demo.\n* **`Redis#call` is the escape hatch for commands not yet typed on the\n  gem.** redis-rb 5.4 ships no stable typed helpers for the per-field\n  TTL commands. The helper sends `HEXPIRE` and `HTTL` with\n  `r.call('HEXPIRE', key, ttl, 'FIELDS', count, *fields)` so the wire\n  bytes match the protocol exactly regardless of which patch release\n  is installed.\n\nIn this example, the batch features describe a user's longer-term shape\n(`country_iso`, `risk_segment`, `account_age_days`, `tx_count_7d`,\n`avg_amount_30d`, `chargeback_count_180d`) and are bulk-loaded by\n`build_features.rb` — the demo's stand-in for a nightly Spark / Feast\nmaterialization job. The streaming features describe what the user is doing\nright now (`last_login_ts`, `last_device_id`, `tx_count_5m`,\n`failed_logins_15m`, `session_country`) and are written by\n`streaming_worker.rb` — a daemon Ruby thread that stands in for a\nFlink / Kafka Streams job. The WEBrick servlet in `demo_server.rb` reads\nany subset of those features through `feature_store.rb`'s helper class."},{"id":"how-it-works","title":"How it works","role":"content","text":"There are three paths: a **batch path** that bulk-loads features once per\nmaterialization cycle, a **streaming path** that updates real-time features\nas events arrive, and an **inference path** that reads features on the\nrequest side."},{"id":"batch-path-per-materialization-cycle","title":"Batch path (per materialization cycle)","role":"content","text":"1. The batch job calls `synthesize_users(N, seed)` (in production, the\n   equivalent computation lives in an offline pipeline against the\n   warehouse). The result is `{user_id => {field => value, ...}}` for every\n   user in this cycle.\n2. `store.bulk_load(rows, ttl_seconds:)` queues one\n   [`HSET`](https://redis.io/docs/latest/commands/hset) plus one\n   [`EXPIRE`](https://redis.io/docs/latest/commands/expire) per user through\n   `redis.pipelined`, so the whole batch ships in a single round trip."},{"id":"streaming-path-per-event","title":"Streaming path (per event)","role":"content","text":"When a user does something (login, transaction, page view) the streaming\nlayer computes whatever real-time signals fall out of that event and calls\n`store.update_streaming(user_id, fields)`. That queues:\n\n1. An [`HSET`](https://redis.io/docs/latest/commands/hset) writing the new field\n   values. Redis is single-threaded per shard, so this is atomic against\n   any concurrent batch write on the same hash — no version columns, no\n   locks.\n2. An [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) over exactly the\n   fields that were written, with the streaming TTL. Each streaming field\n   carries its own per-field expiry independent of the rest of the hash.\n   Stop the worker and these fields drop out one by one as their TTLs\n   elapse, while the batch fields remain populated under the longer\n   key-level TTL."},{"id":"inference-path-per-request","title":"Inference path (per request)","role":"content","text":"1. The model server picks the feature subset it needs (the schema is\n   owned by the model, not the store).\n2. It calls `store.get_features(user_id, names)`, which is one\n   [`HMGET`](https://redis.io/docs/latest/commands/hmget). Redis returns the values\n   in the same order as the requested fields, with `nil` for any field\n   that doesn't exist (or has expired).\n3. For batch inference, the model server calls\n   `store.batch_get_features(user_ids, names)`, which pipelines one\n   [`HMGET`](https://redis.io/docs/latest/commands/hmget) per user across all `N`\n   users in a single network round trip."},{"id":"project-layout","title":"Project layout","role":"content","text":"[code example]\n\nRun with `bundle exec ruby demo_server.rb` or\n`bundle exec ruby build_features.rb --count 500`."},{"id":"the-feature-store-helper","title":"The feature-store helper","role":"content","text":"The `FeatureStore` class wraps the read/write paths\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/feature-store/ruby/feature_store.rb)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each user is one Redis Hash. Every value is stored as a string — Redis\nhash fields are bytes on the wire, so the helper renders booleans as\n`'true'` / `'false'` and uses `value.to_s` for everything else. The model\nserver is responsible for parsing back to the right type, the same way it\nwould when reading any serialized feature store.\n\n[code example]"},{"id":"bulk-loading-batch-features","title":"Bulk-loading batch features","role":"content","text":"`bulk_load` pipelines one `HSET` and one `EXPIRE` per user into a single\nround trip via `redis.pipelined`. With 500 users that's 1000 commands in\none network call — Redis processes them sequentially on the server side\nbut the client only pays one RTT.\n\n[code example]\n\n`Redis#pipelined` is a non-transactional batch: commands queue up and ship\nin one round trip but they don't run inside a `MULTI/EXEC` block. That's\nthe right choice here because each user's `HSET` + `EXPIRE` pair is\nindependent of every other user's, and an all-or-nothing transaction\nwould block the server for the duration of the batch. For the rare case\nwhere the pair has to be inseparable, use `redis.multi do |tx| ... end`\nor a Lua script via\n[`EVAL`](https://redis.io/docs/latest/commands/eval) /\n[Eval scripting](https://redis.io/docs/latest/develop/programmability/eval-intro).\n\nIn production, the equivalent of this script runs as an offline pipeline\n(a Spark or Feast `materialize` job) that reads from the warehouse and\nwrites into Redis. The\n[Feast `RedisOnlineStore`](https://docs.feast.dev/reference/online-stores/redis)\nprovider does exactly this under the hood; the in-house\n[Redis Feature Form](https://redis.io/docs/latest/develop/ai/featureform) integration\ncovers the materialize + serve path end-to-end."},{"id":"streaming-writes-with-per-field-ttl","title":"Streaming writes with per-field TTL","role":"content","text":"`update_streaming` is the linchpin of the mixed-staleness story:\n\n[code example]\n\n[`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) sets the TTL on\n*individual* hash fields, not on the whole key. The two commands are\nqueued in the same `pipelined` block so Redis runs them in order: the\n`HSET` first creates or overwrites the fields, then `HEXPIRE` attaches a\nTTL to each of those same fields. `HEXPIRE` returns one status code per\nfield:\n\n* `1` — TTL set / updated.\n* `2` — the expiry was 0 or in the past, so Redis deleted the field\n  instead of applying a TTL.\n* `0` — an `NX | XX | GT | LT` conditional flag was specified and not\n  met (we never use one here).\n* `-2` — no such field, or no such key.\n\nThe helper raises if any code is anything other than `1`, so the \"every\nstreaming write renews its TTL\" invariant fails loudly rather than\nsilently leaving a streaming field with no expiry attached.\n\nWhy `redis.call('HEXPIRE', ...)` instead of a typed `redis.hexpire`?\nredis-rb 5.4 ships no stable typed helpers for the per-field TTL\ncommands, so `Redis#call` is the canonical way to issue them. The wire\nbytes match the protocol exactly. The same `r.call('HTTL', ...)` shape\nappears in `field_ttls_seconds`.\n\nIf a streaming pipeline stops, the streaming fields drop out one by one\nas their per-field TTLs elapse. `field_ttls_seconds` lets the model side\ninspect the remaining TTL on any field — useful both for debugging\n(\"why is this feature missing?\" → \"it expired three seconds ago\") and as\na freshness signal in the model itself.\n\n> **HEXPIRE requires Redis 7.4 or later.** `HEXPIRE` and the field-level\n> TTL commands were added in Redis 7.4. The demo's `Gemfile` pins\n> `redis ~> 5.4`, which speaks the protocol natively."},{"id":"inference-reads-with-hmget","title":"Inference reads with HMGET","role":"content","text":"`get_features` is one `HMGET`:\n\n[code example]\n\nThe model knows exactly which features it consumes, so the request path\nalways takes the `hmget` branch with an explicit field list — that's the\nsub-millisecond path. `hgetall` is the right call for debugging (which is\nwhat the demo's \"Inspect\" panel does) but not for serving: it forces\nRedis to serialize every field, including ones the model doesn't need.\n\nFields that don't exist (because they were never written, or because they\nexpired) come back as `nil`. The helper drops them from the result hash\nso the caller sees only the features that are actually available."},{"id":"batch-scoring-with-pipelined-hmget","title":"Batch scoring with pipelined HMGET","role":"content","text":"For batch inference, the same `HMGET` shape pipelines across users:\n\n[code example]\n\nOne round trip for the whole batch. The demo returns a 30-user batch in\n~2 ms against a local Redis.\n\nA Redis Cluster is different: a single `redis.pipelined` block ships\nthrough one connection to one node. For batch reads on a cluster, use\nthe [`redis-clustering`](https://github.com/redis/redis-rb) gem\nand either fan out parallel `hmget` calls (the cluster client routes\neach one to the right shard) or, for tighter control, group entity IDs\nby hash slot and run one `pipelined` block per shard in parallel."},{"id":"the-streaming-worker","title":"The streaming worker","role":"content","text":"`streaming_worker.rb` is the demo's stand-in for whatever Flink, Kafka\nStreams, or bespoke service computes the real-time features\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/feature-store/ruby/streaming_worker.rb)).\nIt runs as a daemon `Thread` next to the WEBrick server so the UI can\nstart, pause, and resume it.\n\nThe lifecycle (start / stop / pause / resume / wait_for_idle) is the same\nas every other client in this use case. The two correctness levers:\n\n[code example]\n\nThe same pre-flight `@tick_in_flight = true` before the pause check and\nthe outer `ensure` block that clears both flags on every exit path\nappears in every other client demo, for the same reason: a reset that's\nabout to `DEL` every key needs to be able to call `worker.pause` to stop\n*future* ticks AND `worker.wait_for_idle` to flush a mid-flight tick\nbefore issuing the DEL sweep.\n\nPausing the worker is what shows off the mixed-staleness behavior: leave\nit paused for longer than `streaming_ttl_seconds` and the streaming\nfields disappear from every user's hash one by one, while the batch\nfields remain under the longer key-level `EXPIRE`. The demo's\n`Pause / resume` button lets you see this happen in real time."},{"id":"the-batch-builder","title":"The batch builder","role":"content","text":"`build_features.rb` is the demo's nightly materializer\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/feature-store/ruby/build_features.rb)).\nIt generates synthetic feature rows and calls `store.bulk_load` once.\n\nRun the builder on its own (independently of the demo server) to\npopulate Redis from the command line:\n\n[code example]\n\nThat writes 500 users at `fs:user:*` with a one-hour key-level TTL,\nwhich is how a typical operator would pre-seed a feature store from the\ncommand line when debugging."},{"id":"the-interactive-demo","title":"The interactive demo","role":"content","text":"`demo_server.rb` runs a WEBrick server on port 8093. The HTML page lets\nyou:\n\n* **Bulk-load** any number of users (default 200) with a configurable\n  key-level TTL.\n* See the **store state**: user count, batch / streaming TTLs,\n  cumulative read/write counters.\n* See the **streaming worker** status and **pause or resume** it.\n* Run an **inference read** for any user with a chosen feature subset,\n  and see the value, the per-field TTL, and the read latency.\n* Run **batch scoring** with a pipelined `HMGET` across `N` users.\n* **Inspect** any user's full hash with field-level TTLs and the\n  key-level TTL.\n\nThe server holds one `Redis` client, one `FeatureStore`, and one\n`StreamingWorker` for the lifetime of the process. Every WEBrick request\nthread shares the same `Redis` (the gem synchronizes its own access).\nEndpoints:\n\n| Endpoint                  | What it does                                                                        |\n|---------------------------|-------------------------------------------------------------------------------------|\n| `GET  /state`             | User count, TTL config, stats counters, worker status.                              |\n| `POST /bulk-load`         | Pipelined `HSET` + `EXPIRE` over N synthetic users with a chosen TTL.               |\n| `POST /worker/toggle`     | Pause / resume the streaming worker.                                                |\n| `POST /read`              | `HMGET` a chosen feature subset for one user; report latency and per-field TTLs.    |\n| `POST /batch-read`        | Pipeline `HMGET` across N users; report total latency and per-entity field counts.  |\n| `GET  /inspect`           | `HGETALL` + `HTTL` for one user; full hash view with per-field TTLs.                |\n| `POST /reset`             | Drop every user under the key prefix (used by the demo's reset button).             |"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* **Redis 7.4 or later.** [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire)\n  and [`HTTL`](https://redis.io/docs/latest/commands/httl) were added in Redis 7.4;\n  the demo relies on per-field TTL for the mixed-staleness story.\n* **Ruby 3.0 or later.**\n* The `redis` and `webrick` gems. The demo's `Gemfile` pins\n  `redis ~> 5.4` and `webrick ~> 1.9`. WEBrick was removed from Ruby's\n  default-gem set in 3.0, so the explicit pin keeps the demo runnable\n  on modern Rubies.\n\nIf your Redis server is running elsewhere, start the demo with\n`--redis-url redis://host:port`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo lives in a small directory under\n[`feature-store/ruby`](https://github.com/redis/docs/tree/main/content/develop/use-cases/feature-store/ruby).\nClone the repo or copy the directory:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From the project directory:\n\n[code example]\n\nYou should see:\n\n[code example]\n\nOpen [http://127.0.0.1:8093](http://127.0.0.1:8093). Useful things to try:\n\n* Pick a user and click **Read features** with a mixed batch/streaming\n  subset — you'll see batch fields with no per-field TTL (covered by the\n  key-level TTL) and streaming fields with a positive per-field TTL.\n* Click **Pipeline HMGET** with `count=100` to see the latency of a\n  100-user batch read.\n* Click **Pause / resume** on the streaming worker and leave it paused\n  for ~5 minutes (or restart the server with\n  `--streaming-ttl-seconds 30` to make it visible in seconds). Re-run\n  **Read features** on any user and watch the streaming fields\n  disappear while the batch fields stay.\n* Click **Inspect** on a user to see the full hash with field-level\n  TTLs.\n* Click **Reset** to drop every user and start over."},{"id":"production-usage","title":"Production usage","role":"content","text":"The guidance below focuses on the production concerns specific to\nrunning a feature store on Redis. For the generic redis-rb production\nchecklist — connection options, TLS, AUTH, retry policy — see the\n[`redis` gem documentation](https://redis.io/docs/latest/develop/clients/ruby).\nThe feature-store demo runs against `localhost` with the defaults; a\nreal deployment should harden the client first."},{"id":"pick-the-batch-ttl-to-outlast-a-failed-refresher","title":"Pick the batch TTL to outlast a failed refresher","role":"content","text":"The whole-entity `EXPIRE` is your safety net against silent staleness\nfrom a broken batch pipeline. Set it longer than your worst-case batch\noutage so a single missed run doesn't take the feature store offline,\nbut short enough that a sustained outage causes loud failures (missing\nentities) rather than quiet ones (yesterday's features being scored as\ntoday's). The standard choice is one cycle of \"expected refresh\ninterval × 2\" — for a daily batch, 48 hours; for a 6-hour batch, 12\nhours.\n\nThe same logic applies to the per-field streaming TTL: a few times the\nexpected update interval so a slow-but-alive streaming worker doesn't\nchurn features needlessly, but short enough that a stalled worker\ncauses visible freshness failures."},{"id":"co-locate-the-online-store-with-serving-not-with-training","title":"Co-locate the online store with serving, not with training","role":"content","text":"The online store's hash representation does *not* have to match the\nschema in your offline store. The batch materialization step is your\nchance to flatten joins, encode categoricals, and project to whatever\nshape the model server wants — so the request path is exactly one\n`HMGET` and zero transforms.\n\nThe training pipeline reads from the offline store with its own schema;\nthe serving pipeline reads from Redis with the flattened serving\nschema. Keeping those two pipelines as the same code path is what\nprevents training-serving skew."},{"id":"use-redis-clustering-for-cluster-deployments","title":"Use redis-clustering for cluster deployments","role":"content","text":"A single `redis.pipelined` block ships through one connection to one\nnode. On a Redis Cluster you need the\n[`redis-clustering`](https://github.com/redis/redis-rb) gem,\nwhich routes each command to the right shard transparently. For batch\nreads on a cluster, either fan out parallel `hmget` calls (each routed\nper-shard) or group entity IDs by hash slot ahead of time and run one\n`pipelined` block per shard in parallel.\n\nA hash tag like `fs:user:{vip}:u0001` forces a known set of keys onto\nthe same shard so one pipeline can cover them all in a single round\ntrip."},{"id":"make-hexpire-part-of-every-streaming-write","title":"Make HEXPIRE part of every streaming write","role":"content","text":"The single biggest correctness lever in this design is that the\nstreaming write applies `HEXPIRE` *every time*. If a streaming worker\nwrites a field without renewing its TTL, the field carries whatever\nexpiry was there before — possibly none, possibly stale — and the\nmixed-staleness invariant breaks. Keep the `HSET` and `HEXPIRE` in the\nsame pipeline (or, even safer, in the same\n[Lua script](https://redis.io/docs/latest/develop/programmability/eval-intro) if\nyou don't trust the call site)."},{"id":"avoid-hgetall-on-the-request-path","title":"Avoid HGETALL on the request path","role":"content","text":"`HGETALL` reads every field on the hash, including ones the model\ndoesn't need. With dozens of features per entity, that is wasted\nserialization work on the server and wasted bandwidth on the wire.\nAlways specify the field list explicitly with `hmget` in the model\nserver.\n\nThe exception is debugging and feature-set discovery, where you\ngenuinely want the full hash. The demo's \"Inspect\" button uses\n`hgetall` for exactly this reason."},{"id":"inspect-the-store-directly-with-redis-cli","title":"Inspect the store directly with redis-cli","role":"content","text":"When testing or troubleshooting, the cli tells you everything:\n\n[code example]\n\nA streaming field that returns `-2` from `HTTL` doesn't exist on the\nhash (either it was never written, or it expired); `-1` means the\nfield has no TTL set (and is therefore covered only by the key-level\n`EXPIRE`); any positive value is the remaining TTL in seconds."},{"id":"learn-more","title":"Learn more","role":"related","text":"This example uses the following Redis commands:\n\n* [`HSET`](https://redis.io/docs/latest/commands/hset) to write a feature or a\n  whole feature row in one call.\n* [`HMGET`](https://redis.io/docs/latest/commands/hmget) to retrieve any subset\n  of features for one entity in one round trip.\n* [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) for debugging and\n  feature-set discovery.\n* [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) and\n  [`HTTL`](https://redis.io/docs/latest/commands/httl) for per-field TTL on\n  streaming features (Redis 7.4+).\n* [`EXPIRE`](https://redis.io/docs/latest/commands/expire) and\n  [`TTL`](https://redis.io/docs/latest/commands/ttl) for the whole-entity TTL\n  aligned with the batch materialization cycle.\n\nSee the [`redis` gem documentation](https://redis.io/docs/latest/develop/clients/ruby)\nfor the full client reference, and the\n[Hashes overview](https://redis.io/docs/latest/develop/data-types/hashes) for the\ndeeper conceptual model."}],"examples":[{"id":"project-layout-ex0","language":"text","code":"feature-store/ruby/\n├── Gemfile                — redis ~> 5.4, webrick ~> 1.9\n├── feature_store.rb       — FeatureStore class\n├── streaming_worker.rb    — daemon-thread worker\n├── build_features.rb     — synthesize_users + CLI main\n└── demo_server.rb         — WEBrick servlet + HTML page (single file)","section_id":"project-layout"},{"id":"the-feature-store-helper-ex0","language":"ruby","code":"require 'redis'\nrequire_relative 'feature_store'\n\nredis = Redis.new(url: 'redis://localhost:6379')\nstore = FeatureStore.new(\n  redis: redis,\n  key_prefix: 'fs:user:',\n  batch_ttl_seconds: 24 * 60 * 60,    # whole-entity TTL aligned with the daily batch cycle\n  streaming_ttl_seconds: 5 * 60,      # per-field TTL on each streaming feature\n)\n\n# Batch materialization: one HSET + EXPIRE per user, all pipelined.\nstore.bulk_load({\n  'u0001' => {\n    'country_iso' => 'US', 'risk_segment' => 'low',\n    'tx_count_7d' => 14, 'avg_amount_30d' => 92.40,\n    'account_age_days' => 612, 'chargeback_count_180d' => 0,\n  },\n}, ttl_seconds: 24 * 60 * 60)\n\n# Streaming write: HSET + HEXPIRE on just the fields that changed.\nstore.update_streaming('u0001', {\n  'last_login_ts' => (Time.now.to_f * 1000).to_i,\n  'last_device_id' => 'ios-9f02',\n  'tx_count_5m' => 3,\n  'failed_logins_15m' => 0,\n  'session_country' => 'US',\n})\n\n# Inference read: HMGET of whatever the model needs.\nfeatures = store.get_features('u0001', [\n  'risk_segment', 'tx_count_7d', 'avg_amount_30d',\n  'tx_count_5m', 'failed_logins_15m',\n])\n\n# Batch scoring: pipelined HMGET across many users.\nbatch = store.batch_get_features(\n  %w[u0001 u0002 u0003],\n  %w[risk_segment tx_count_5m failed_logins_15m],\n)","section_id":"the-feature-store-helper"},{"id":"data-model-ex0","language":"text","code":"fs:user:u0001                                   TTL = 86400 s (key-level)\n  country_iso=US                                <no field TTL>\n  risk_segment=low                              <no field TTL>\n  account_age_days=612                          <no field TTL>\n  tx_count_7d=14                                <no field TTL>\n  avg_amount_30d=92.40                          <no field TTL>\n  chargeback_count_180d=0                       <no field TTL>\n  last_login_ts=1716998413541                   TTL = 300 s (per field, HEXPIRE)\n  last_device_id=ios-9f02                       TTL = 300 s (per field, HEXPIRE)\n  tx_count_5m=3                                 TTL = 300 s (per field, HEXPIRE)\n  failed_logins_15m=0                           TTL = 300 s (per field, HEXPIRE)\n  session_country=US                            TTL = 300 s (per field, HEXPIRE)","section_id":"data-model"},{"id":"bulk-loading-batch-features-ex0","language":"ruby","code":"def bulk_load(rows, ttl_seconds: nil)\n  return 0 if rows.empty?\n  ttl = ttl_seconds || @batch_ttl_seconds\n  @redis.pipelined do |pipe|\n    rows.each do |entity_id, fields|\n      key = key_for(entity_id)\n      encoded = fields.transform_values { |v| encode_value(v) }\n      pipe.hset(key, encoded)\n      pipe.expire(key, ttl)\n    end\n  end\n  ...\nend","section_id":"bulk-loading-batch-features"},{"id":"streaming-writes-with-per-field-ttl-ex0","language":"ruby","code":"def update_streaming(entity_id, fields, ttl_seconds: nil)\n  return if fields.empty?\n  ttl = ttl_seconds || @streaming_ttl_seconds\n  key = key_for(entity_id)\n  encoded = fields.transform_values { |v| encode_value(v) }\n  names = encoded.keys\n\n  results = @redis.pipelined do |pipe|\n    pipe.hset(key, encoded)\n    pipe.call('HEXPIRE', key, ttl, 'FIELDS', names.size, *names)\n  end\n  codes = results[1] || []\n  codes.each do |code|\n    unless code == 1\n      raise \"HEXPIRE did not set every field TTL for #{key}: #{codes.inspect}\"\n    end\n  end\n  ...\nend","section_id":"streaming-writes-with-per-field-ttl"},{"id":"inference-reads-with-hmget-ex0","language":"ruby","code":"def get_features(entity_id, field_names = nil)\n  key = key_for(entity_id)\n  if field_names.nil?\n    return @redis.hgetall(key)\n  end\n  return {} if field_names.empty?\n  values = @redis.hmget(key, *field_names)\n  out = {}\n  field_names.each_with_index do |n, i|\n    out[n] = values[i] unless values[i].nil?\n  end\n  out\nend","section_id":"inference-reads-with-hmget"},{"id":"batch-scoring-with-pipelined-hmget-ex0","language":"ruby","code":"def batch_get_features(entity_ids, field_names)\n  return {} if entity_ids.empty? || field_names.empty?\n  rows = @redis.pipelined do |pipe|\n    entity_ids.each { |id| pipe.hmget(key_for(id), *field_names) }\n  end\n  out = {}\n  entity_ids.each_with_index do |id, i|\n    values = rows[i] || []\n    row = {}\n    field_names.each_with_index do |n, j|\n      row[n] = values[j] unless values[j].nil?\n    end\n    out[id] = row\n  end\n  out\nend","section_id":"batch-scoring-with-pipelined-hmget"},{"id":"the-streaming-worker-ex0","language":"ruby","code":"def run\n  until @stop\n    sleep(@tick)\n    break if @stop\n    # Set tick_in_flight *before* the pause check so a concurrent\n    # pause + wait_for_idle can never observe tick_in_flight=false\n    # in the window between the pause check and the actual tick call.\n    @tick_in_flight = true\n    begin\n      do_tick unless @paused\n    rescue => e\n      warn \"[streaming-worker] tick failed: #{e.class}: #{e.message}\"\n    ensure\n      @tick_in_flight = false\n    end\n  end\nensure\n  # Clear running and tick_in_flight no matter how the thread exits\n  # so a later start can spin a fresh thread.\n  @running = false\n  @tick_in_flight = false\nend","section_id":"the-streaming-worker"},{"id":"the-batch-builder-ex0","language":"bash","code":"bundle exec ruby build_features.rb --count 500 --ttl-seconds 3600","section_id":"the-batch-builder"},{"id":"get-the-source-files-ex0","language":"bash","code":"git clone https://github.com/redis/docs.git\ncd docs/content/develop/use-cases/feature-store/ruby\nbundle install","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"bundle exec ruby demo_server.rb","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"Dropping any existing users under 'fs:user:*' for a clean demo run (pass --no-reset to keep them).\nRedis feature-store demo server listening on http://127.0.0.1:8093\nUsing Redis at redis://localhost:6379 with key prefix 'fs:user:' (batch TTL 86400s, streaming TTL 300s)\nMaterialized 200 user(s); streaming worker running.","section_id":"start-the-demo-server"},{"id":"inspect-the-store-directly-with-redis-cli-ex0","language":"bash","code":"# How many users currently in the store\nredis-cli --scan --pattern 'fs:user:*' | wc -l\n\n# One user's full hash and key-level TTL\nredis-cli HGETALL fs:user:u0001\nredis-cli TTL    fs:user:u0001\n\n# Per-field TTL on the streaming fields\nredis-cli HTTL fs:user:u0001 FIELDS 5 \\\n  last_login_ts last_device_id tx_count_5m failed_logins_15m session_country\n\n# Sample HMGET as the model would issue it\nredis-cli HMGET fs:user:u0001 risk_segment tx_count_7d avg_amount_30d tx_count_5m","section_id":"inspect-the-store-directly-with-redis-cli"}]}
{"schema_version":2,"id":"develop/use-cases/feature-store/rust/demo_template.html","title":"","url":"https://redis.io/docs/latest/develop/use-cases/feature-store/rust/demo_template/","summary":"","tags":[],"last_updated":"2026-06-01T10:27:42+01:00","page_type":"content","content_hash":"a0cd21b766896c022a4fab4d88b327efe5679dc7e2327183f3fc0ae9e7b2dea9","sections":[{"id":"content","title":"Content","role":"content","text":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n  <title>Redis Feature Store Demo (Rust)</title>\n  <style>\n    :root {\n      --bg: #eef3f1;\n      --panel: #ffffff;\n      --ink: #1d2730;\n      --accent: #267d6b;\n      --accent-dark: #1a594c;\n      --muted: #5c6770;\n      --line: #d4dfdb;\n      --ok: #d2ecdf;\n      --warn: #f8e0d0;\n      --pill: #d9ebe6;\n      --batch: #e6e0f0;\n      --stream: #d9ebe6;\n    }\n    * { box-sizing: border-box; }\n    body {\n      margin: 0;\n      font-family: Georgia, \"Times New Roman\", serif;\n      color: var(--ink);\n      background:\n        radial-gradient(circle at top left, #f3faf7, transparent 32rem),\n        linear-gradient(180deg, #ecf2f0 0%, var(--bg) 100%);\n      min-height: 100vh;\n    }\n    main { max-width: 1080px; margin: 0 auto; padding: 40px 20px 72px; }\n    h1 { font-size: clamp(2rem, 4.6vw, 3.4rem); line-height: 1.05; margin-bottom: 8px; }\n    p.lede { max-width: 58rem; font-size: 1.05rem; color: var(--muted); }\n    .grid { display: grid; gap: 18px; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); margin-top: 24px; }\n    .panel { background: rgba(255, 255, 255, 0.96); border: 1px solid var(--line); border-radius: 16px; padding: 20px; box-shadow: 0 10px 32px rgba(20, 60, 50, 0.07); }\n    .panel.wide { grid-column: 1 / -1; }\n    .panel h2 { margin-top: 0; margin-bottom: 8px; font-size: 1.25rem; }\n    .panel h3 { margin: 14px 0 6px; font-size: 1rem; }\n    .pill { display: inline-block; border-radius: 999px; background: var(--pill); color: var(--accent-dark); padding: 6px 10px; font-size: 0.85rem; margin-bottom: 10px; }\n    label { display: block; font-weight: bold; margin: 10px 0 4px; }\n    input, select { width: 100%; padding: 9px 11px; border-radius: 9px; border: 1px solid #c0d2cc; font: inherit; background: white; }\n    button { appearance: none; border: 0; border-radius: 999px; background: var(--accent); color: white; padding: 10px 16px; font: inherit; cursor: pointer; margin-right: 6px; margin-top: 10px; }\n    button.secondary { background: #3b4951; }\n    button.danger { background: #8a3a3a; }\n    button.small { padding: 5px 10px; font-size: 0.85rem; margin-top: 4px; }\n    button:hover { filter: brightness(0.92); }\n    dl { display: grid; grid-template-columns: max-content 1fr; gap: 6px 14px; margin: 0; }\n    dt { font-weight: bold; }\n    dd { margin: 0; word-break: break-word; }\n    .row { display: flex; gap: 8px; flex-wrap: wrap; }\n    .row > * { flex: 1 1 0; min-width: 110px; }\n    table { width: 100%; border-collapse: collapse; font-size: 0.92rem; }\n    th, td { text-align: left; padding: 6px 8px; border-bottom: 1px solid var(--line); }\n    th { color: var(--muted); font-weight: bold; }\n    code, .mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.85rem; }\n    .badge { display: inline-block; border-radius: 6px; padding: 2px 7px; font-size: 0.8rem; font-weight: bold; }\n    .badge.batch { background: var(--batch); color: #43326a; }\n    .badge.stream { background: var(--stream); color: #1d4a2c; }\n    .badge.expired { background: var(--warn); color: #6b3220; }\n    .badge.untracked { background: #eceff1; color: #3b4951; }\n    .badge.running { background: var(--ok); color: #1d4a2c; }\n    .badge.paused { background: var(--warn); color: #6b3220; }\n    .ttl-pos { color: #1a594c; font-weight: bold; }\n    .ttl-neg { color: #6b3220; }\n    .field-list { display: flex; gap: 6px 12px; flex-wrap: wrap; }\n    .field-list label { display: inline-flex; align-items: center; gap: 4px; margin: 0; font-weight: normal; font-size: 0.9rem; }\n    .field-list input { width: auto; }\n    #status { margin-top: 18px; padding: 12px 14px; border-radius: 12px; display: none; }\n    #status.ok { display: block; background: var(--ok); }\n    #status.error { display: block; background: var(--warn); }\n  </style>\n</head>\n<body>\n  <main>\n    <div class=\"pill\">redis-rs + axum + tokio</div>\n    <h1>Redis Feature Store Demo</h1>\n    <p class=\"lede\">\n      A small fraud-scoring feature store. Each user is one Redis hash\n      at <code>__KEY_PREFIX__{id}</code> with a batch-materialized\n      <span class=\"badge batch\">batch</span> half (daily aggregates,\n      24-hour key-level <code>EXPIRE</code>) and a streaming\n      <span class=\"badge stream\">streaming</span> half (real-time\n      signals, <code>__STREAM_TTL__</code>s per-field <code>HEXPIRE</code>).\n      Inference reads any subset with one <code>HMGET</code>; batch\n      scoring pipelines <code>HMGET</code> across N users.\n    </p>\n\n    <div class=\"grid\">\n      <section class=\"panel wide\">\n        <h2>Store state</h2>\n        <div id=\"store-view\">Loading...</div>\n      </section>\n\n      <section class=\"panel\">\n        <h2>Materialize batch features</h2>\n        <p>Calls <code>HSET</code> + <code>EXPIRE</code> for each user\n          through one <code>redis::pipe()</code> — the whole batch\n          ships in one round trip.</p>\n        <label for=\"bulk-count\">How many users</label>\n        <input id=\"bulk-count\" type=\"number\" min=\"1\" max=\"2000\" value=\"200\">\n        <label for=\"bulk-ttl\">Key-level TTL (seconds)</label>\n        <input id=\"bulk-ttl\" type=\"number\" min=\"5\" max=\"172800\" value=\"86400\">\n        <p class=\"mono\" style=\"font-size: 0.85rem; color: var(--muted);\">\n          Drop the TTL to e.g. 30 s and watch entities disappear on\n          schedule — the same thing that happens if a daily refresher\n          fails.\n        </p>\n        <button id=\"bulk-button\">Bulk-load</button>\n        <button id=\"reset-button\" class=\"danger\">Reset (drop every user)</button>\n      </section>\n\n      <section class=\"panel\">\n        <h2>Streaming worker</h2>\n        <p>Picks <code>__USERS_PER_TICK__</code> users per tick, writes the\n          streaming features, applies <code>HEXPIRE</code>\n          <code>__STREAM_TTL__</code>s per field. Pause it and the\n          streaming fields drop out via per-field TTL while the batch\n          fields stay populated.</p>\n        <div id=\"worker-view\"></div>\n        <button id=\"worker-pause-button\" class=\"secondary\">Pause / resume</button>\n      </section>\n\n      <section class=\"panel wide\">\n        <h2>Inference read (HMGET)</h2>\n        <p>Pick a user and a feature subset. One <code>HMGET</code>\n          round trip returns whatever the model needs.</p>\n        <div class=\"row\">\n          <div>\n            <label for=\"read-user\">User</label>\n            <select id=\"read-user\"></select>\n          </div>\n          <div>\n            <label>&nbsp;</label>\n            <button id=\"read-button\" class=\"secondary\">Read features</button>\n          </div>\n        </div>\n        <h3>Feature subset</h3>\n        <p class=\"mono\" style=\"font-size: 0.85rem; color: var(--muted);\">\n          Tick to include in the <code>HMGET</code>. Per-field TTL is\n          shown next to each field in the result table.\n        </p>\n        <div id=\"read-fields\" class=\"field-list\"></div>\n        <div id=\"read-result\" style=\"margin-top: 16px;\">\n          <p>Pick a user and click <strong>Read features</strong>.</p>\n        </div>\n      </section>\n\n      <section class=\"panel\">\n        <h2>Batch scoring</h2>\n        <p>Pipelined <code>HMGET</code> across N random users via\n          <code>redis::pipe()</code>. One network round trip for the\n          whole batch.</p>\n        <label for=\"batch-count\">How many users</label>\n        <input id=\"batch-count\" type=\"number\" min=\"1\" max=\"500\" value=\"100\">\n        <button id=\"batch-button\" class=\"secondary\">Pipeline HMGET</button>\n        <div id=\"batch-result\" style=\"margin-top: 14px;\">\n          <p>(no batch read yet)</p>\n        </div>\n      </section>\n\n      <section class=\"panel\">\n        <h2>Inspect one user</h2>\n        <p><code>HGETALL</code> plus per-field <code>HTTL</code> and\n          key-level <code>TTL</code>. Useful for spotting which\n          streaming fields have already expired.</p>\n        <label for=\"inspect-user\">User</label>\n        <select id=\"inspect-user\"></select>\n        <button id=\"inspect-button\">Inspect</button>\n        <div id=\"inspect-result\" style=\"margin-top: 14px;\">\n          <p>(pick a user and click Inspect)</p>\n        </div>\n      </section>\n    </div>\n\n    <div id=\"status\"></div>\n  </main>\n\n  <script>\n    const BATCH_FIELDS = __BATCH_FIELDS_JSON__;\n    const STREAM_FIELDS = __STREAM_FIELDS_JSON__;\n\n    const storeView = document.getElementById(\"store-view\");\n    const workerView = document.getElementById(\"worker-view\");\n    const readUserSelect = document.getElementById(\"read-user\");\n    const inspectUserSelect = document.getElementById(\"inspect-user\");\n    const readFieldsBox = document.getElementById(\"read-fields\");\n    const readResult = document.getElementById(\"read-result\");\n    const batchResult = document.getElementById(\"batch-result\");\n    const inspectResult = document.getElementById(\"inspect-result\");\n    const statusBox = document.getElementById(\"status\");\n\n    function setStatus(message, kind) { statusBox.textContent = message; statusBox.className = kind; }\n    function escapeHtml(value) {\n      return String(value ?? \"\").replace(/[&<>\"']/g, (c) =>\n        ({ \"&\": \"&\", \"<\": \"<\", \">\": \">\", '\"': \"\"\", \"'\": \"'\" }[c]));\n    }\n    function classifyField(name) {\n      if (BATCH_FIELDS.includes(name)) return \"batch\";\n      if (STREAM_FIELDS.includes(name)) return \"stream\";\n      return \"other\";\n    }\n    function ttlLabel(seconds) {\n      if (seconds === -2) return '<span class=\"badge expired\">missing</span>';\n      if (seconds === -1) return '<span class=\"badge untracked\">no TTL</span>';\n      return `<span class=\"ttl-pos mono\">${seconds}s</span>`;\n    }\n\n    function renderStore(state) {\n      const stats = state.stats || {};\n      storeView.innerHTML = `\n        <dl>\n          <dt>Users in store</dt><dd>${state.entity_count}</dd>\n          <dt>Key prefix</dt><dd class=\"mono\">${escapeHtml(state.key_prefix)}*</dd>\n          <dt>Batch TTL</dt><dd>${state.batch_ttl_seconds}s</dd>\n          <dt>Streaming TTL</dt><dd>${state.streaming_ttl_seconds}s</dd>\n          <dt>Batch writes</dt><dd>${stats.batch_writes_total ?? 0}</dd>\n          <dt>Streaming writes</dt><dd>${stats.streaming_writes_total ?? 0}</dd>\n          <dt>Reads</dt><dd>${stats.reads_total ?? 0}</dd>\n          <dt>Fields returned</dt><dd>${stats.read_fields_total ?? 0}</dd>\n        </dl>`;\n    }\n    function renderWorker(state) {\n      const w = state.worker || {};\n      const badge = w.paused\n        ? '<span class=\"badge paused\">paused</span>'\n        : w.running ? '<span class=\"badge running\">running</span>' : '<span class=\"badge expired\">stopped</span>';\n      workerView.innerHTML = `<p>${badge} <span class=\"mono\">ticks=${w.tick_count ?? 0} writes=${w.writes_count ?? 0}</span></p>`;\n    }\n    function populateUserSelects(ids) {\n      for (const sel of [readUserSelect, inspectUserSelect]) {\n        const previous = sel.value;\n        sel.innerHTML = ids.map((id) => `<option value=\"${escapeHtml(id)}\">${escapeHtml(id)}</option>`).join(\"\");\n        if (ids.includes(previous)) sel.value = previous;\n      }\n    }\n    function renderFieldPicker() {\n      const allFields = [...BATCH_FIELDS, ...STREAM_FIELDS];\n      readFieldsBox.innerHTML = allFields.map((f) => {\n        const kind = classifyField(f);\n        const checked = [\"risk_segment\", \"tx_count_7d\", \"avg_amount_30d\", \"tx_count_5m\", \"failed_logins_15m\"].includes(f) ? \"checked\" : \"\";\n        return `<label><input type=\"checkbox\" name=\"field\" value=\"${escapeHtml(f)}\" ${checked}>\n          <span class=\"badge ${kind}\">${kind}</span>\n          <span class=\"mono\">${escapeHtml(f)}</span></label>`;\n      }).join(\"\");\n    }\n    function selectedReadFields() {\n      return Array.from(readFieldsBox.querySelectorAll('input[name=\"field\"]:checked')).map((el) => el.value);\n    }\n    async function refresh() {\n      const r = await fetch(\"/state\");\n      const state = await r.json();\n      renderStore(state); renderWorker(state); populateUserSelects(state.entity_ids || []);\n    }\n\n    document.getElementById(\"bulk-button\").addEventListener(\"click\", async () => {\n      const count = parseInt(document.getElementById(\"bulk-count\").value, 10) || 1;\n      const ttl = parseInt(document.getElementById(\"bulk-ttl\").value, 10) || 86400;\n      const body = new URLSearchParams({ count, ttl });\n      const r = await fetch(\"/bulk-load\", { method: \"POST\", body });\n      const d = await r.json();\n      if (!r.ok) { setStatus(d.error || \"Bulk-load failed.\", \"error\"); return; }\n      setStatus(`Materialized ${d.loaded} user(s) with a ${d.ttl_seconds}s key-level TTL in ${d.elapsed_ms.toFixed(1)} ms.`, \"ok\");\n      await refresh();\n    });\n    document.getElementById(\"reset-button\").addEventListener(\"click\", async () => {\n      if (!confirm(\"Drop every user from the store?\")) return;\n      const r = await fetch(\"/reset\", { method: \"POST\" });\n      const d = await r.json();\n      if (!r.ok) { setStatus(d.error || \"Reset failed.\", \"error\"); return; }\n      setStatus(`Reset. Dropped ${d.deleted} user(s).`, \"ok\");\n      await refresh();\n    });\n    document.getElementById(\"worker-pause-button\").addEventListener(\"click\", async () => {\n      const r = await fetch(\"/worker/toggle\", { method: \"POST\" });\n      const d = await r.json();\n      setStatus(d.paused ? \"Streaming worker paused.\" : \"Streaming worker resumed.\", \"ok\");\n      await refresh();\n    });\n    document.getElementById(\"read-button\").addEventListener(\"click\", async () => {\n      const user = readUserSelect.value;\n      if (!user) { setStatus(\"Pick a user first.\", \"error\"); return; }\n      const fields = selectedReadFields();\n      const body = new URLSearchParams();\n      body.set(\"user\", user);\n      for (const f of fields) body.append(\"field\", f);\n      const r = await fetch(\"/read\", { method: \"POST\", body });\n      const d = await r.json();\n      if (!r.ok) { setStatus(d.error || \"Read failed.\", \"error\"); return; }\n      const rows = (d.requested || []).map((name) => {\n        const value = d.values[name]; const ttl = d.ttls[name]; const kind = classifyField(name);\n        return `<tr>\n          <td><span class=\"badge ${kind}\">${kind}</span> <span class=\"mono\">${escapeHtml(name)}</span></td>\n          <td class=\"mono\">${value === undefined || value === null ? '<span class=\"badge expired\">missing</span>' : escapeHtml(value)}</td>\n          <td>${ttlLabel(ttl)}</td>\n        </tr>`;\n      }).join(\"\");\n      readResult.innerHTML = `\n        <p><strong>HMGET</strong> ${escapeHtml(user)} (${d.requested.length} field(s))\n          returned ${d.returned_count} value(s) in\n          <strong>${d.elapsed_ms.toFixed(2)} ms</strong>.\n          Key-level TTL: ${ttlLabel(d.key_ttl_seconds)}.</p>\n        ${d.requested.length === 0 ? \"\" : `<table><thead><tr><th>field</th><th>value</th><th>per-field TTL</th></tr></thead><tbody>${rows}</tbody></table>`}`;\n    });\n    document.getElementById(\"batch-button\").addEventListener(\"click\", async () => {\n      const count = parseInt(document.getElementById(\"batch-count\").value, 10) || 1;\n      const fields = selectedReadFields();\n      const body = new URLSearchParams();\n      body.set(\"count\", count);\n      for (const f of fields) body.append(\"field\", f);\n      const r = await fetch(\"/batch-read\", { method: \"POST\", body });\n      const d = await r.json();\n      if (!r.ok) { setStatus(d.error || \"Batch read failed.\", \"error\"); return; }\n      const perEntity = d.entity_count === 0 ? 0 : d.elapsed_ms / d.entity_count;\n      const rows = (d.sample || []).map((row) => `<tr><td class=\"mono\">${escapeHtml(row.id)}</td><td>${row.field_count}</td></tr>`).join(\"\");\n      batchResult.innerHTML = `\n        <p>Pipelined <code>HMGET</code> across <strong>${d.entity_count}</strong> users\n          (${d.field_count} field(s) each) in <strong>${d.elapsed_ms.toFixed(2)} ms</strong>\n          (~${perEntity.toFixed(3)} ms / user, one network round trip total).</p>\n        ${(d.sample || []).length === 0 ? \"\" : `<h3>Sample</h3><table><thead><tr><th>user</th><th>fields returned</th></tr></thead><tbody>${rows}</tbody></table>`}`;\n    });\n    document.getElementById(\"inspect-button\").addEventListener(\"click\", async () => {\n      const user = inspectUserSelect.value;\n      if (!user) { setStatus(\"Pick a user first.\", \"error\"); return; }\n      const params = new URLSearchParams({ user });\n      const r = await fetch(`/inspect?${params.toString()}`);\n      const d = await r.json();\n      if (!r.ok) { setStatus(d.error || \"Inspect failed.\", \"error\"); return; }\n      const rows = (d.fields || []).map((f) => `<tr>\n        <td><span class=\"badge ${classifyField(f.name)}\">${classifyField(f.name)}</span> <span class=\"mono\">${escapeHtml(f.name)}</span></td>\n        <td class=\"mono\">${escapeHtml(f.value)}</td>\n        <td>${ttlLabel(f.ttl_seconds)}</td></tr>`).join(\"\");\n      inspectResult.innerHTML = d.exists === false\n        ? `<p><span class=\"badge expired\">missing</span> <code>${escapeHtml(user)}</code> isn't in the store. Either it was never materialized or its key-level TTL expired.</p>`\n        : `<p>Key-level TTL: ${ttlLabel(d.key_ttl_seconds)} &nbsp; (${d.fields.length} field(s))</p>\n           <table><thead><tr><th>field</th><th>value</th><th>per-field TTL</th></tr></thead><tbody>${rows}</tbody></table>`;\n    });\n\n    renderFieldPicker(); refresh(); setInterval(refresh, 1500);\n  </script>\n</body>\n</html>"}],"examples":[]}
{"schema_version":2,"id":"develop/use-cases/feature-store/rust","title":"Redis feature store with redis-rs","url":"https://redis.io/docs/latest/develop/use-cases/feature-store/rust/","summary":"Build a Redis-backed online feature store in Rust with redis-rs","aliases":["/develop/use-cases/feature-store/redis-rs"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:50:30+01:00","children":[{"id":"develop/use-cases/feature-store/rust/demo_template.html","summary":"","title":"","url":"https://redis.io/docs/latest/develop/use-cases/feature-store/rust/demo_template/"}],"page_type":"content","content_hash":"9fdee298fb180ffd52535578acb6ff29c5cf66eaa73dc2bb222980149fe9acd9","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a small Redis-backed online feature store in\nRust with the async [redis-rs](https://redis.io/docs/latest/develop/clients/rust) crate\nand `tokio`. The demo runs on top of the [axum](https://docs.rs/axum/)\nweb framework so you can bulk-load a batch of users with a key-level TTL, run\na streaming worker that overwrites real-time features with per-field TTL,\nretrieve any subset of features for one user under 2 ms, and pipeline `HMGET`\nacross a hundred users for batch scoring."},{"id":"overview","title":"Overview","role":"overview","text":"Each entity (here, a user) is one Redis\n[Hash](https://redis.io/docs/latest/develop/data-types/hashes) at a deterministic key —\n`fs:user:{id}`. The hash holds every feature for that entity as one field per\nfeature: batch-materialized aggregates (refreshed once a day) alongside\nstreaming-updated signals (refreshed every few seconds). One\n[`HMGET`](https://redis.io/docs/latest/commands/hmget) returns whichever subset the model\nneeds in one network round trip.\n\nTwo TTL layers solve the *mixed staleness* problem without an application-side\ncleaner:\n\n* A **key-level** [`EXPIRE`](https://redis.io/docs/latest/commands/expire) aligned with the\n  batch materialization cycle (24 hours in the demo). If the batch refresher\n  fails, the whole entity disappears at the next cycle and inference sees a\n  missing entity — which the model handler can detect and fall back on —\n  rather than silently outdated values.\n* A **per-field** [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) (Redis 7.4+) on\n  each streaming feature gives that field its own shorter expiry, independent\n  of the rest of the hash. If the streaming pipeline stops updating a feature,\n  the field self-cleans while the batch fields stay populated.\n\nThat gives you:\n\n* A single round trip for retrieval — any subset of features for one entity\n  in one [`HMGET`](https://redis.io/docs/latest/commands/hmget).\n* Sub-millisecond hot path. The Redis-side work is microseconds; in practice\n  the bottleneck is the network round trip plus the model's own feature-prep.\n* Pipelined batch scoring — one round trip for `N` users at once.\n* Independent freshness per feature, expressed as a server-side TTL rather\n  than as application logic.\n* Self-cleanup on pipeline failure: a stalled batch refresher lets entities\n  expire on schedule, and a stalled streaming worker lets each affected field\n  expire on its own timer."},{"id":"how-redis-rs-fits-the-demo","title":"How redis-rs fits the demo","role":"content","text":"Two crate facts shape the helper:\n\n* **`ConnectionManager` is the canonical async connection.** It owns a\n  multiplexed `MultiplexedConnection` underneath and transparently reconnects\n  on a dropped socket. The type is `Clone` — handing it to one HTTP handler,\n  the streaming worker, and the batch builder is just three `clone()` calls,\n  and they all share the same underlying connection. There's no pool to\n  manage.\n* **The `redis::cmd(\"HEXPIRE\")` builder is how you reach commands not yet\n  typed on the `AsyncCommands` trait.** Per-field TTL bindings (`hexpire`,\n  `httl`, `hpersist`) aren't part of the typed surface on redis-rs 0.27, so\n  the helper issues them with the generic command builder. The wire bytes\n  are identical to the typed form.\n\nIn this example, the batch features describe a user's longer-term shape\n(`country_iso`, `risk_segment`, `account_age_days`, `tx_count_7d`,\n`avg_amount_30d`, `chargeback_count_180d`) and are bulk-loaded by\n`build_features.rs` — the demo's stand-in for a nightly Spark / Feast\nmaterialization job. The streaming features describe what the user is doing\nright now (`last_login_ts`, `last_device_id`, `tx_count_5m`,\n`failed_logins_15m`, `session_country`) and are written by\n`streaming_worker.rs` — a tokio task that stands in for a Flink / Kafka\nStreams job. The HTTP handlers in `demo_server.rs` read any subset of those\nfeatures through `feature_store.rs`'s helper struct."},{"id":"how-it-works","title":"How it works","role":"content","text":"There are three paths: a **batch path** that bulk-loads features once per\nmaterialization cycle, a **streaming path** that updates real-time features\nas events arrive, and an **inference path** that reads features on the\nrequest side."},{"id":"batch-path-per-materialization-cycle","title":"Batch path (per materialization cycle)","role":"content","text":"1. The batch job calls `synthesize_users(N, seed)` (in production, the\n   equivalent computation lives in an offline pipeline against the\n   warehouse). The result is a `Vec<(String, FeatureMap)>` for every user\n   in this cycle.\n2. `store.bulk_load(&rows, ttl_seconds).await` queues one\n   [`HSET`](https://redis.io/docs/latest/commands/hset) plus one\n   [`EXPIRE`](https://redis.io/docs/latest/commands/expire) per user through a\n   non-transactional `redis::pipe()`, so the whole batch ships in a single\n   round trip."},{"id":"streaming-path-per-event","title":"Streaming path (per event)","role":"content","text":"When a user does something (login, transaction, page view) the streaming\nlayer computes whatever real-time signals fall out of that event and\ncalls `store.update_streaming(user_id, &fields, ttl_seconds).await`. That\nqueues:\n\n1. An [`HSET`](https://redis.io/docs/latest/commands/hset) writing the new field\n   values. Redis is single-threaded per shard, so this is atomic against\n   any concurrent batch write on the same hash — no version columns, no\n   locks.\n2. An [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) over exactly the\n   fields that were written, with the streaming TTL. Each streaming field\n   carries its own per-field expiry independent of the rest of the hash.\n   Stop the worker and these fields drop out one by one as their TTLs\n   elapse, while the batch fields remain populated under the longer\n   key-level TTL."},{"id":"inference-path-per-request","title":"Inference path (per request)","role":"content","text":"1. The model server picks the feature subset it needs (the schema is\n   owned by the model, not the store).\n2. It calls `store.get_features(user_id, &names).await`, which is one\n   [`HMGET`](https://redis.io/docs/latest/commands/hmget). Redis returns the values\n   in the same order as the requested fields, with `None` for any field\n   that doesn't exist (or has expired).\n3. For batch inference, the model server calls\n   `store.batch_get_features(&user_ids, &names).await`, which pipelines\n   one [`HMGET`](https://redis.io/docs/latest/commands/hmget) per user across all\n   `N` users in a single network round trip via `redis::pipe()`."},{"id":"the-feature-store-helper","title":"The feature-store helper","role":"content","text":"The `FeatureStore` struct wraps the read/write paths\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/feature-store/rust/feature_store.rs)):\n\n[code example]"},{"id":"project-layout","title":"Project layout","role":"content","text":"The crate is a small lib + two binaries:\n\n[code example]\n\nRun with `cargo run --release --bin demo_server` or\n`cargo run --release --bin build_features -- --count 500`."},{"id":"data-model","title":"Data model","role":"content","text":"Each user is one Redis Hash. Every value is stored as a string — Redis hash\nfields are bytes on the wire, so the helper encodes booleans as `\"true\"` /\n`\"false\"` and renders numbers via `i64::to_string` / `f64::to_string`. The\nmodel server is responsible for parsing back to the right type, the same way\nit would when reading any serialized feature store.\n\n[code example]"},{"id":"bulk-loading-batch-features","title":"Bulk-loading batch features","role":"content","text":"`bulk_load` pipelines one `HSET` and one `EXPIRE` per user into a single\nnon-transactional batch through `redis::pipe()`. With 500 users that's 1000\ncommands in one network call — Redis processes them sequentially on the\nserver side but the client only pays one RTT.\n\n[code example]\n\n`redis::pipe()` is a non-transactional builder: commands queue up and ship in\none round trip, but they don't run inside a `MULTI/EXEC` block. That's the\nright choice here because each user's `HSET` + `EXPIRE` pair is independent\nof every other user's, and an all-or-nothing transaction would block the\nserver for the duration of the batch. For the rare case where the pair has\nto be inseparable, swap to `redis::pipe().atomic()` (which wraps in\n`MULTI/EXEC`) or a Lua script via\n[`EVAL`](https://redis.io/docs/latest/commands/eval) /\n[Eval scripting](https://redis.io/docs/latest/develop/programmability/eval-intro).\n\nIn production, the equivalent of this script runs as an offline pipeline\n(a Spark or Feast `materialize` job) that reads from the warehouse and\nwrites into Redis. The\n[Feast `RedisOnlineStore`](https://docs.feast.dev/reference/online-stores/redis)\nprovider does exactly this under the hood; the in-house\n[Redis Feature Form](https://redis.io/docs/latest/develop/ai/featureform) integration\ncovers the materialize + serve path end-to-end."},{"id":"streaming-writes-with-per-field-ttl","title":"Streaming writes with per-field TTL","role":"content","text":"`update_streaming` is the linchpin of the mixed-staleness story:\n\n[code example]\n\n[`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) sets the TTL on *individual*\nhash fields, not on the whole key. The two commands are queued under one\nflush so Redis runs them in pipeline order: the `HSET` first creates or\noverwrites the fields, then `HEXPIRE` attaches a TTL to each of those same\nfields. `HEXPIRE` returns one status code per field — `1` if the TTL was\nset, `2` if the expiry was 0 or in the past (so Redis deleted the field\ninstead), `0` if an `NX | XX | GT | LT` conditional flag was specified and\nnot met (we never use one here), `-2` if the field doesn't exist on the\nkey. The helper returns a `RedisError` if any code is anything other than\n`1`, so the \"every streaming write renews its TTL\" invariant fails loudly\nrather than silently leaving a streaming field with no expiry attached.\n\nThe pipeline reply shape — `Vec<Vec<i64>>` — is the one tricky bit. redis-rs\nwraps each non-ignored command's reply in the outer `Vec`, even when there\nis only one such command. The HEXPIRE reply itself is an array, so we\nend up with one outer `Vec` containing one inner `Vec<i64>` of codes.\n\nIf a streaming pipeline stops, the streaming fields drop out one by one as\ntheir per-field TTLs elapse. `field_ttls_seconds` (which wraps `HTTL`) lets\nthe model side inspect the remaining TTL on any field — useful both for\ndebugging and as a freshness signal in the model itself.\n\n> **HEXPIRE requires Redis 7.4 or later.** `HEXPIRE` and the field-level\n> TTL commands (`HTTL`, `HPERSIST`, `HEXPIREAT`, `HPEXPIRE`, `HPEXPIREAT`,\n> `HPTTL`, `HEXPIRETIME`, `HPEXPIRETIME`) were added in Redis 7.4. The\n> demo's `Cargo.toml` pins `redis = \"0.27\"` and uses\n> `redis::cmd(\"HEXPIRE\")` because the typed binding doesn't ship on that\n> client line yet — the wire bytes are identical."},{"id":"inference-reads-with-hmget","title":"Inference reads with HMGET","role":"content","text":"`get_features` is one `HMGET`:\n\n[code example]\n\n`conn.hget` with a slice of field names is redis-rs's way of issuing\n`HMGET` (the typed `hmget` and `hget(slice)` produce the same wire bytes).\nThe reply is `Vec<Option<String>>` — fields that don't exist on the hash\ncome back as `None`, which the helper drops from the result map."},{"id":"batch-scoring-with-pipelined-hmget","title":"Batch scoring with pipelined HMGET","role":"content","text":"For batch inference, the same `HMGET` shape pipelines across users:\n\n[code example]\n\nOne round trip for the whole batch. The demo returns a 30-user batch in\nunder 1 ms against a local Redis.\n\nA Redis Cluster is different: a single `redis::pipe()` is bound to one\nconnection, and a `ConnectionManager` holds one connection to one node.\nFor batch reads on a cluster, use redis-rs's\n[`cluster_async`](https://docs.rs/redis/0.27/redis/cluster_async/index.html)\nclient and either fan out parallel `hget` calls (the cluster client routes\neach one to the right shard) or, for tighter control, group entity IDs by\nhash slot and run one pipeline per shard in parallel. A hash tag like\n`fs:user:{vip}:u0001` forces a known set of keys onto the same shard so\none pipeline can cover them all in a single round trip."},{"id":"the-streaming-worker","title":"The streaming worker","role":"content","text":"`streaming_worker.rs` is the demo's stand-in for whatever Flink, Kafka\nStreams, or bespoke service computes the real-time features\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/feature-store/rust/streaming_worker.rs)).\nIt runs as a tokio task next to the demo server so the UI can start,\npause, and resume it.\n\n[code example]\n\nThe same pre-flight-`tick_in_flight` + drop-`Guard` pattern as every other\nclient in this use case closes the pause/in-flight race: a reset that's\nabout to `DEL` every key calls `worker.pause()` to stop *future* ticks\n*and* `worker.wait_for_idle().await` to flush a mid-flight tick before\nissuing the DEL sweep.\n\nPausing the worker is what shows off the mixed-staleness behavior: leave\nit paused for longer than `streaming_ttl_seconds` and the streaming fields\ndisappear from every user's hash one by one, while the batch fields remain\nunder the longer key-level `EXPIRE`. The demo's `Pause / resume` button\nlets you see this happen in real time."},{"id":"the-batch-builder","title":"The batch builder","role":"content","text":"`build_features.rs` is the demo's nightly materializer\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/feature-store/rust/build_features.rs)).\nIt generates synthetic feature rows and calls `store.bulk_load` once. The\nsynthesis itself is not the point — in a real deployment the equivalent\ncode reads from the offline store (Snowflake, BigQuery, Iceberg) and writes\nthe resulting hashes into Redis.\n\nRun the builder on its own (independently of the demo server) to populate\nRedis from the command line:\n\n[code example]\n\nThat writes 500 users at `fs:user:*` with a one-hour key-level TTL, which\nis how a typical operator would pre-seed a feature store from the command\nline when debugging."},{"id":"the-interactive-demo","title":"The interactive demo","role":"content","text":"`demo_server.rs` runs the axum HTTP server on port 8090. The HTML page lets\nyou:\n\n* **Bulk-load** any number of users (default 200) with a configurable\n  key-level TTL.\n* See the **store state**: user count, batch / streaming TTLs, cumulative\n  read/write counters.\n* See the **streaming worker** status and **pause or resume** it.\n* Run an **inference read** for any user with a chosen feature subset, and\n  see the value, the per-field TTL, and the read latency.\n* Run **batch scoring** with a pipelined `HMGET` across `N` users.\n* **Inspect** any user's full hash with field-level TTLs and the key-level\n  TTL.\n\nThe server holds one `FeatureStore` and one `StreamingWorker` for the\nlifetime of the process. Both wrap clones of the same `ConnectionManager`,\nso every HTTP handler and the streaming worker share the underlying\nmultiplexed socket. Endpoints:\n\n| Endpoint                  | What it does                                                                        |\n|---------------------------|-------------------------------------------------------------------------------------|\n| `GET  /state`             | User count, TTL config, stats counters, worker status.                              |\n| `POST /bulk-load`         | Pipelined `HSET` + `EXPIRE` over N synthetic users with a chosen TTL.               |\n| `POST /worker/toggle`     | Pause / resume the streaming worker.                                                |\n| `POST /read`              | `HMGET` a chosen feature subset for one user; report latency and per-field TTLs.    |\n| `POST /batch-read`        | Pipeline `HMGET` across N users; report total latency and per-entity field counts.  |\n| `GET  /inspect`           | `HGETALL` + `HTTL` for one user; full hash view with per-field TTLs.                |\n| `POST /reset`             | Drop every user under the key prefix (used by the demo's reset button).             |"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* **Redis 7.4 or later.** [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) and\n  [`HTTL`](https://redis.io/docs/latest/commands/httl) were added in Redis 7.4; the\n  demo relies on per-field TTL for the mixed-staleness story.\n* **Rust 1.74 or later.** The demo uses `async fn` in traits, `let-else`,\n  and other recent ergonomics. Earlier stable Rust may compile after small\n  tweaks.\n* **redis-rs 0.27 or later.** The demo's `Cargo.toml` pins 0.27 with the\n  `tokio-comp`, `aio`, and `connection-manager` features. Per-field TTL\n  commands are issued via `redis::cmd(\"HEXPIRE\")`.\n\nIf your Redis server is running elsewhere, start the demo with\n`--redis-url redis://host:port/`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo lives in a small Cargo project under\n[`feature-store/rust`](https://github.com/redis/docs/tree/main/content/develop/use-cases/feature-store/rust).\nClone the repo or copy the directory:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From the project directory:\n\n[code example]\n\nYou should see:\n\n[code example]\n\nOpen [http://127.0.0.1:8090](http://127.0.0.1:8090). Useful things to try:\n\n* Pick a user and click **Read features** with a mixed batch/streaming\n  subset — you'll see batch fields with no per-field TTL (covered by the\n  key-level TTL) and streaming fields with a positive per-field TTL.\n* Click **Pipeline HMGET** with `count=100` to see the latency of a\n  100-user batch read.\n* Click **Pause / resume** on the streaming worker and leave it paused\n  for ~5 minutes (or restart the server with\n  `--streaming-ttl-seconds 30` to make it visible in seconds). Re-run\n  **Read features** on any user and watch the streaming fields disappear\n  while the batch fields stay.\n* Click **Inspect** on a user to see the full hash with field-level TTLs.\n* Click **Reset** to drop every user and start over."},{"id":"production-usage","title":"Production usage","role":"content","text":"The guidance below focuses on the production concerns specific to running\na feature store on Redis. For the generic redis-rs production checklist\n— TLS, AUTH, retry/backoff, error handling — see the\n[redis-rs client guide](https://redis.io/docs/latest/develop/clients/rust) and the\n[error-handling notes](https://redis.io/docs/latest/develop/clients/rust/error-handling).\nThe feature-store demo runs against `localhost` with the defaults; a real\ndeployment should harden the client first."},{"id":"pick-the-batch-ttl-to-outlast-a-failed-refresher","title":"Pick the batch TTL to outlast a failed refresher","role":"content","text":"The whole-entity `EXPIRE` is your safety net against silent staleness\nfrom a broken batch pipeline. Set it longer than your worst-case batch\noutage so a single missed run doesn't take the feature store offline,\nbut short enough that a sustained outage causes loud failures (missing\nentities) rather than quiet ones (yesterday's features being scored as\ntoday's). The standard choice is one cycle of \"expected refresh\ninterval × 2\" — for a daily batch, 48 hours; for a 6-hour batch, 12\nhours.\n\nThe same logic applies to the per-field streaming TTL: a few times the\nexpected update interval so a slow-but-alive streaming worker doesn't\nchurn features needlessly, but short enough that a stalled worker\ncauses visible freshness failures."},{"id":"co-locate-the-online-store-with-serving-not-with-training","title":"Co-locate the online store with serving, not with training","role":"content","text":"The online store's hash representation does *not* have to match the\nschema in your offline store. The batch materialization step is your\nchance to flatten joins, encode categoricals, and project to whatever\nshape the model server wants — so the request path is exactly one\n`HMGET` and zero transforms.\n\nThe training pipeline reads from the offline store with its own schema;\nthe serving pipeline reads from Redis with the flattened serving\nschema. Keeping those two pipelines as the same code path is what\nprevents training-serving skew."},{"id":"pipeline-batch-reads-across-shards","title":"Pipeline batch reads across shards","role":"content","text":"On a single Redis instance, a pipelined `HMGET` across `N` users is one\nround trip. A Redis Cluster is different: a single `redis::pipe()` ships\nthrough one connection to one node, so on a cluster you need redis-rs's\n[`cluster_async`](https://docs.rs/redis/0.27/redis/cluster_async/index.html)\nclient. Either fan out parallel `hget` calls (the cluster client routes\neach one to the right shard) or group entity IDs by hash slot and issue\none pipeline against each shard in parallel.\n\nA hash tag like `fs:user:{vip}:u0001` forces a known set of keys onto\nthe same shard so one pipeline can cover them all in a single round\ntrip."},{"id":"make-hexpire-part-of-every-streaming-write","title":"Make HEXPIRE part of every streaming write","role":"content","text":"The single biggest correctness lever in this design is that the\nstreaming write applies `HEXPIRE` *every time*. If a streaming worker\nwrites a field without renewing its TTL, the field carries whatever\nexpiry was there before — possibly none, possibly stale — and the\nmixed-staleness invariant breaks. Keep the `HSET` and `HEXPIRE` in the\nsame pipeline (or, even safer, in the same\n[Lua script](https://redis.io/docs/latest/develop/programmability/eval-intro) if\nyou don't trust the call site)."},{"id":"avoid-hgetall-on-the-request-path","title":"Avoid HGETALL on the request path","role":"content","text":"`HGETALL` reads every field on the hash, including ones the model\ndoesn't need. With dozens of features per entity, that is wasted\nserialization work on the server and wasted bandwidth on the wire.\nAlways specify the field list explicitly with `hget(&[...])` (or the\ntyped `hmget`) in the model server.\n\nThe exception is debugging and feature-set discovery, where you\ngenuinely want the full hash. The demo's \"Inspect\" button uses\n`hgetall` for exactly this reason."},{"id":"inspect-the-store-directly-with-redis-cli","title":"Inspect the store directly with redis-cli","role":"content","text":"When testing or troubleshooting, the cli tells you everything:\n\n[code example]\n\nA streaming field that returns `-2` from `HTTL` doesn't exist on the\nhash (either it was never written, or it expired); `-1` means the field\nhas no TTL set (and is therefore covered only by the key-level\n`EXPIRE`); any positive value is the remaining TTL in seconds."},{"id":"learn-more","title":"Learn more","role":"related","text":"This example uses the following Redis commands:\n\n* [`HSET`](https://redis.io/docs/latest/commands/hset) to write a feature or a whole\n  feature row in one call.\n* [`HMGET`](https://redis.io/docs/latest/commands/hmget) to retrieve any subset of\n  features for one entity in one round trip.\n* [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) for debugging and\n  feature-set discovery.\n* [`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire) and\n  [`HTTL`](https://redis.io/docs/latest/commands/httl) for per-field TTL on\n  streaming features (Redis 7.4+).\n* [`EXPIRE`](https://redis.io/docs/latest/commands/expire) and\n  [`TTL`](https://redis.io/docs/latest/commands/ttl) for the whole-entity TTL\n  aligned with the batch materialization cycle.\n* Pipelined `HMGET` across many entities for batch scoring with one\n  network round trip — see\n  [transactions and pipelining](https://redis.io/docs/latest/develop/clients/rust/transpipe).\n\nSee the [redis-rs documentation](https://redis.io/docs/latest/develop/clients/rust)\nfor the full client reference, and the\n[Hashes overview](https://redis.io/docs/latest/develop/data-types/hashes) for the\ndeeper conceptual model — including the listpack encoding that makes\nsmall hashes particularly compact in memory, which matters at\nfeature-store scale."}],"examples":[{"id":"the-feature-store-helper-ex0","language":"rust","code":"use redis::aio::ConnectionManager;\nuse feature_store_demo::feature_store::{FeatureStore, FeatureMap, FeatureValue};\nuse std::collections::BTreeMap;\n\nlet client = redis::Client::open(\"redis://127.0.0.1/\")?;\nlet conn = ConnectionManager::new(client).await?;\nlet store = FeatureStore::new(\n    conn,\n    \"fs:user:\",\n    24 * 60 * 60,    // whole-entity TTL aligned with the daily batch cycle\n    5 * 60,          // per-field TTL on each streaming feature\n);\n\n// Batch materialization: one HSET + EXPIRE per user, all pipelined\n// through one round trip.\nlet mut row: FeatureMap = BTreeMap::new();\nrow.insert(\"country_iso\".into(), FeatureValue::Str(\"US\".into()));\nrow.insert(\"risk_segment\".into(), FeatureValue::Str(\"low\".into()));\nrow.insert(\"tx_count_7d\".into(), FeatureValue::Int(14));\nrow.insert(\"avg_amount_30d\".into(), FeatureValue::Float(92.40));\nstore.bulk_load(&[(\"u0001\".into(), row)], 24 * 60 * 60).await?;\n\n// Streaming write: HSET + HEXPIRE on just the fields that changed.\nlet mut s: FeatureMap = BTreeMap::new();\ns.insert(\"last_login_ts\".into(), FeatureValue::Int(1716998413541));\ns.insert(\"tx_count_5m\".into(), FeatureValue::Int(3));\nstore.update_streaming(\"u0001\", &s, 5 * 60).await?;\n\n// Inference read: HMGET of whatever the model needs.\nlet features = store.get_features(\n    \"u0001\",\n    &[\"risk_segment\", \"tx_count_7d\", \"avg_amount_30d\",\n      \"tx_count_5m\", \"last_login_ts\"],\n).await?;\n\n// Batch scoring: pipelined HMGET across many users.\nlet batch = store.batch_get_features(\n    &[\"u0001\".into(), \"u0002\".into()],\n    &[\"risk_segment\", \"tx_count_5m\"],\n).await?;","section_id":"the-feature-store-helper"},{"id":"project-layout-ex0","language":"text","code":"feature-store/rust/\n├── Cargo.toml\n├── lib.rs                  (pub mod feature_store; pub mod streaming_worker; pub mod build_features;)\n├── feature_store.rs        — FeatureStore struct + methods\n├── streaming_worker.rs     — async tokio task worker\n├── build_features.rs       — SynthesizeUsers + cli_main()\n├── demo_server.rs          — main() for the demo server (axum)\n├── build_features_bin.rs   — main() for the CLI builder\n└── demo_template.html      — HTML page, baked in via include_str!","section_id":"project-layout"},{"id":"data-model-ex0","language":"text","code":"fs:user:u0001                                   TTL = 86400 s (key-level)\n  country_iso=US                                <no field TTL>\n  risk_segment=low                              <no field TTL>\n  account_age_days=612                          <no field TTL>\n  tx_count_7d=14                                <no field TTL>\n  avg_amount_30d=92.40                          <no field TTL>\n  chargeback_count_180d=0                       <no field TTL>\n  last_login_ts=1716998413541                   TTL = 300 s (per field, HEXPIRE)\n  last_device_id=ios-9f02                       TTL = 300 s (per field, HEXPIRE)\n  tx_count_5m=3                                 TTL = 300 s (per field, HEXPIRE)\n  failed_logins_15m=0                           TTL = 300 s (per field, HEXPIRE)\n  session_country=US                            TTL = 300 s (per field, HEXPIRE)","section_id":"data-model"},{"id":"bulk-loading-batch-features-ex0","language":"rust","code":"pub async fn bulk_load(\n    &self,\n    rows: &[(String, FeatureMap)],\n    ttl_seconds: u64,\n) -> RedisResult<usize> {\n    if rows.is_empty() { return Ok(0); }\n    let mut pipe = redis::pipe();\n    for (entity_id, fields) in rows {\n        let key = self.key_for(entity_id);\n        let encoded: Vec<(&str, String)> = fields\n            .iter().map(|(k, v)| (k.as_str(), v.encode())).collect();\n        pipe.hset_multiple(&key, &encoded).ignore();\n        pipe.expire(&key, ttl_seconds as i64).ignore();\n    }\n    let mut conn = self.conn.clone();\n    pipe.query_async::<()>(&mut conn).await?;\n    ...\n}","section_id":"bulk-loading-batch-features"},{"id":"streaming-writes-with-per-field-ttl-ex0","language":"rust","code":"pub async fn update_streaming(\n    &self,\n    entity_id: &str,\n    fields: &FeatureMap,\n    ttl_seconds: u64,\n) -> RedisResult<()> {\n    if fields.is_empty() { return Ok(()); }\n    let key = self.key_for(entity_id);\n    let encoded: Vec<(&str, String)> = fields.iter()\n        .map(|(k, v)| (k.as_str(), v.encode())).collect();\n    let names: Vec<&str> = fields.keys().map(|s| s.as_str()).collect();\n\n    let mut pipe = redis::pipe();\n    pipe.hset_multiple(&key, &encoded).ignore();\n    // HEXPIRE wire form: HEXPIRE key seconds FIELDS count field...\n    let mut hexpire = redis::cmd(\"HEXPIRE\");\n    hexpire.arg(&key).arg(ttl_seconds).arg(\"FIELDS\").arg(names.len());\n    for n in &names { hexpire.arg(n); }\n    pipe.add_command(hexpire);\n\n    let mut conn = self.conn.clone();\n    // Pipeline returns one entry per non-ignored command; HSET's\n    // reply was dropped with .ignore(), so the only remaining entry\n    // is HEXPIRE's per-field code list.\n    let pipe_result: Vec<Vec<i64>> = pipe.query_async(&mut conn).await?;\n    let codes = pipe_result.into_iter().next().unwrap_or_default();\n    for code in &codes {\n        if *code != 1 {\n            return Err(redis::RedisError::from((\n                redis::ErrorKind::ResponseError,\n                \"HEXPIRE invariant violated\",\n                format!(\"HEXPIRE did not set every field TTL for {key}: {codes:?}\"),\n            )));\n        }\n    }\n    ...\n}","section_id":"streaming-writes-with-per-field-ttl"},{"id":"inference-reads-with-hmget-ex0","language":"rust","code":"pub async fn get_features(\n    &self,\n    entity_id: &str,\n    field_names: &[&str],\n) -> RedisResult<BTreeMap<String, String>> {\n    if field_names.is_empty() { return Ok(BTreeMap::new()); }\n    let key = self.key_for(entity_id);\n    let mut conn = self.conn.clone();\n    let values: Vec<Option<String>> = conn.hget(&key, field_names).await?;\n    let mut out = BTreeMap::new();\n    for (n, v) in field_names.iter().zip(values.into_iter()) {\n        if let Some(s) = v { out.insert((*n).to_string(), s); }\n    }\n    ...\n}","section_id":"inference-reads-with-hmget"},{"id":"batch-scoring-with-pipelined-hmget-ex0","language":"rust","code":"pub async fn batch_get_features(\n    &self,\n    entity_ids: &[String],\n    field_names: &[&str],\n) -> RedisResult<BTreeMap<String, BTreeMap<String, String>>> {\n    if entity_ids.is_empty() || field_names.is_empty() {\n        return Ok(BTreeMap::new());\n    }\n    let mut pipe = redis::pipe();\n    for id in entity_ids {\n        pipe.hget(self.key_for(id), field_names);\n    }\n    let mut conn = self.conn.clone();\n    let rows: Vec<Vec<Option<String>>> = pipe.query_async(&mut conn).await?;\n    ...\n}","section_id":"batch-scoring-with-pipelined-hmget"},{"id":"the-streaming-worker-ex0","language":"rust","code":"async fn run(state: Arc<State>) {\n    struct Guard<'a>(&'a State);\n    impl Drop for Guard<'_> {\n        fn drop(&mut self) {\n            // Clear running and tick_in_flight no matter how the\n            // task exits — a panic, a manual stop, anything.\n            self.0.running.store(false, Ordering::Relaxed);\n            self.0.tick_in_flight.store(false, Ordering::Relaxed);\n        }\n    }\n    let _guard = Guard(&state);\n\n    let mut interval = time::interval(state.tick);\n    interval.set_missed_tick_behavior(time::MissedTickBehavior::Skip);\n    interval.tick().await;  // skip the first immediate tick\n\n    loop {\n        if state.stop.load(Ordering::Relaxed) { return; }\n        interval.tick().await;\n\n        // Set tick_in_flight *before* the pause check so a concurrent\n        // pause()+wait_for_idle() can never see tick_in_flight=false\n        // in the window between the pause check and the actual\n        // do_tick call.\n        state.tick_in_flight.store(true, Ordering::Relaxed);\n        let result = if !state.paused.load(Ordering::Relaxed) {\n            do_tick(&state).await\n        } else { Ok(()) };\n        state.tick_in_flight.store(false, Ordering::Relaxed);\n        if let Err(e) = result {\n            eprintln!(\"[streaming-worker] tick failed: {e}\");\n        }\n    }\n}","section_id":"the-streaming-worker"},{"id":"the-batch-builder-ex0","language":"bash","code":"cargo run --release --bin build_features -- --count 500 --ttl-seconds 3600","section_id":"the-batch-builder"},{"id":"get-the-source-files-ex0","language":"bash","code":"git clone https://github.com/redis/docs.git\ncd docs/content/develop/use-cases/feature-store/rust\ncargo build --release","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"cargo run --release --bin demo_server","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"Dropping any existing users under 'fs:user:*' for a clean demo run (pass --no-reset to keep them).\nRedis feature-store demo server listening on http://127.0.0.1:8090\nUsing Redis at redis://127.0.0.1/ with key prefix 'fs:user:' (batch TTL 86400s, streaming TTL 300s)\nMaterialized 200 user(s); streaming worker running.","section_id":"start-the-demo-server"},{"id":"inspect-the-store-directly-with-redis-cli-ex0","language":"bash","code":"# How many users currently in the store\nredis-cli --scan --pattern 'fs:user:*' | wc -l\n\n# One user's full hash and key-level TTL\nredis-cli HGETALL fs:user:u0001\nredis-cli TTL    fs:user:u0001\n\n# Per-field TTL on the streaming fields\nredis-cli HTTL fs:user:u0001 FIELDS 5 \\\n  last_login_ts last_device_id tx_count_5m failed_logins_15m session_country\n\n# Sample HMGET as the model would issue it\nredis-cli HMGET fs:user:u0001 risk_segment tx_count_7d avg_amount_30d tx_count_5m","section_id":"inspect-the-store-directly-with-redis-cli"}]}
{"schema_version":2,"id":"develop/use-cases","title":"Redis use cases","url":"https://redis.io/docs/latest/develop/use-cases/","summary":"Learn how to implement common use cases with Redis","tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:50:30+01:00","children":[{"id":"develop/use-cases/rate-limiter","summary":"Enforce request quotas across distributed services with Redis","title":"Redis rate limiter","url":"https://redis.io/docs/latest/develop/use-cases/rate-limiter/"},{"id":"develop/use-cases/session-store","summary":"Store web sessions in Redis.","title":"Redis session store","url":"https://redis.io/docs/latest/develop/use-cases/session-store/"},{"id":"develop/use-cases/leaderboard","summary":"Build ranked leaderboards with Redis sorted sets and user metadata","title":"Redis leaderboard","url":"https://redis.io/docs/latest/develop/use-cases/leaderboard/"},{"id":"develop/use-cases/time-series-dashboard","summary":"Build a rolling sensor graph demo with Redis time series data","title":"Rolling sensor graph demo with Redis","url":"https://redis.io/docs/latest/develop/use-cases/time-series-dashboard/"},{"id":"develop/use-cases/cache-aside","summary":"Cache database reads in Redis with TTL-bounded staleness.","title":"Redis cache-aside","url":"https://redis.io/docs/latest/develop/use-cases/cache-aside/"},{"id":"develop/use-cases/job-queue","summary":"Run a reliable background job queue with Redis.","title":"Redis job queue","url":"https://redis.io/docs/latest/develop/use-cases/job-queue/"},{"id":"develop/use-cases/prefetch-cache","summary":"Pre-load reference data into Redis so every read is a cache hit.","title":"Redis prefetch cache","url":"https://redis.io/docs/latest/develop/use-cases/prefetch-cache/"},{"id":"develop/use-cases/pub-sub","summary":"Broadcast real-time events to many consumers with Redis pub/sub.","title":"Redis pub/sub messaging","url":"https://redis.io/docs/latest/develop/use-cases/pub-sub/"},{"id":"develop/use-cases/streaming","summary":"Process ordered event streams with consumer groups, replay, and configurable retention.","title":"Redis streaming","url":"https://redis.io/docs/latest/develop/use-cases/streaming/"},{"id":"develop/use-cases/recommendation-engine","summary":"Serve personalized recommendations under tight latency budgets by combining vector similarity with structured filters in a single Redis call.","title":"Redis recommendation engine","url":"https://redis.io/docs/latest/develop/use-cases/recommendation-engine/"},{"id":"develop/use-cases/feature-store","summary":"Serve pre-computed ML features on the request path under tight latency budgets, with batch and streaming features kept fresh in the same store.","title":"Redis feature store","url":"https://redis.io/docs/latest/develop/use-cases/feature-store/"},{"id":"develop/use-cases/semantic-cache","summary":"Reuse LLM responses for queries that are semantically similar, not just byte-identical, to cut token costs, reduce latency, and share validated answers across users and sessions.","title":"Redis semantic cache","url":"https://redis.io/docs/latest/develop/use-cases/semantic-cache/"},{"id":"develop/use-cases/memory-layer","summary":"Persistent memory that spans sessions and tasks — working memory per thread, long-term semantic recall, and a time-ordered event log — on a single Redis instance, with sub-millisecond reads on the agent loop's hot path.","title":"Redis as a memory layer","url":"https://redis.io/docs/latest/develop/use-cases/memory-layer/"}],"page_type":"content","content_hash":"e9259d90520c1ee0b2d6ce6c9aa2a8530bf5e00485ae91b0054c2c297b31a612","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This section provides practical examples and reference implementations for common Redis use cases."},{"id":"available-use-cases","title":"Available use cases","role":"content","text":"* [Rate limiting](https://redis.io/docs/latest/develop/use-cases/rate-limiter) - Implement token bucket rate limiting with Redis\n* [Session storage](https://redis.io/docs/latest/develop/use-cases/session-store) - Store web sessions in Redis with TTL-based expiration\n* [Time series dashboard](https://redis.io/docs/latest/develop/use-cases/time-series-dashboard) - Build a rolling sensor graph demo with Redis time series data\n* [Leaderboards](https://redis.io/docs/latest/develop/use-cases/leaderboard) - Build a ranked leaderboard with sorted sets and user metadata\n* [Cache-aside](https://redis.io/docs/latest/develop/use-cases/cache-aside) - Cache database reads in Redis with TTL-bounded staleness\n* [Job queue](https://redis.io/docs/latest/develop/use-cases/job-queue) - Run a reliable background job queue with at-least-once delivery and visibility-timeout reclaim\n* [Prefetch cache](https://redis.io/docs/latest/develop/use-cases/prefetch-cache) - Pre-load reference data into Redis so every read is a cache hit, kept current by a CDC sync worker\n* [Pub/sub messaging](https://redis.io/docs/latest/develop/use-cases/pub-sub) - Broadcast real-time events to many consumers with channel and pattern subscriptions\n* [Streaming](https://redis.io/docs/latest/develop/use-cases/streaming) - Process ordered event streams with consumer groups, replay, and configurable retention\n* [Recommendation engine](https://redis.io/docs/latest/develop/use-cases/recommendation-engine) - Serve personalized recommendations under tight latency budgets by combining vector similarity with structured filters in a single Redis call\n* [Feature store](https://redis.io/docs/latest/develop/use-cases/feature-store) - Serve pre-computed ML features on the request path with mixed batch-and-streaming freshness using per-field TTL\n* [Semantic cache](https://redis.io/docs/latest/develop/use-cases/semantic-cache) - Reuse LLM responses for semantically similar queries to cut token costs and skip multi-second model calls on near-duplicate prompts\n* [Memory layer](https://redis.io/docs/latest/develop/use-cases/memory-layer) - Give AI agents persistent memory that spans sessions and tasks — working memory per thread, long-term semantic recall, and a time-ordered event log on one Redis instance"}],"examples":[]}
{"schema_version":2,"id":"develop/use-cases/job-queue/dotnet","title":"Redis job queue with StackExchange.Redis","url":"https://redis.io/docs/latest/develop/use-cases/job-queue/dotnet/","summary":"Implement a Redis job queue in C# with StackExchange.Redis","aliases":["/develop/use-cases/job-queue/stackexchange.redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:50:30+01:00","children":[],"page_type":"content","content_hash":"ab0dfacdd47d9759c611349e407f6972599ccd9c46962a53f3ea47b0dadc7bdb","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis-backed job queue in C# with [StackExchange.Redis](https://seredis.dev/). It includes a small ASP.NET Core minimal-API web server so you can enqueue jobs, watch a pool of workers drain them, and see the reclaimer recover jobs from a simulated worker crash."},{"id":"overview","title":"Overview","role":"overview","text":"A job queue lets your application offload background work — sending email, processing payments, image transcoding, ML inference, webhooks — from the request path. Producers enqueue jobs in milliseconds and return to the user; workers pull from the queue and process them on their own schedule.\n\nThat gives you:\n\n* Low-latency user-facing requests, even when downstream work is slow or bursty\n* Horizontal scale across many worker processes that share one Redis instance\n* At-least-once delivery so a worker crash doesn't lose work\n* Visibility-timeout reclaim that returns stuck jobs to the queue automatically\n* Job metadata, retry counts, and completion results in Redis hashes with TTL\n\nIn this example, each job is identified by a random hex ID and its payload, status, and result live in a Redis hash under `queue:jobs:job:{id}`. Pending IDs sit in a list, claimed IDs move atomically to a *processing* list, and completed or failed IDs land in short history lists."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. The application calls `queue.Enqueue(payload)`\n2. The helper writes the job metadata hash and `LPUSH`es the job ID onto the pending list\n3. A worker calls `queue.Claim(timeoutMs)`\n4. The helper runs a non-blocking `RPOPLPUSH` (via `ListRightPopLeftPush`) to atomically move the next pending ID into the processing list, then writes a per-claim `claim_token` plus `claimed_at_ms` on the hash. StackExchange.Redis intentionally does not expose blocking variants such as `BRPOPLPUSH`, so the demo polls on a 50 ms interval until either a job arrives or the timeout elapses.\n5. The worker runs the job and calls `queue.Complete(job, result)` or `queue.Fail(job, error)`\n6. `Complete` removes the job from the processing list, writes the result, and `LPUSH`es the ID onto the completed history (with `LTRIM` and an `EXPIRE` on the hash for cleanup)\n7. `Fail` either retries the job (back to pending) or moves it to the failed list once retries are exhausted\n\nIf a worker dies before completing a job, the job sits in the processing list with a `claimed_at_ms` older than the visibility timeout. A periodic call to `queue.ReclaimStuck()` finds those jobs and moves them back to pending so another worker can pick them up.\n\nEvery state change holds the token: a worker that has been reclaimed cannot later complete or fail a job another worker has already claimed."},{"id":"the-job-queue-helper","title":"The job queue helper","role":"content","text":"The `RedisJobQueue` class wraps the queue operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/job-queue/dotnet/RedisJobQueue.cs)):\n\n[code example]\n\nThe demo helper is synchronous to keep the code compact. A production helper would expose `EnqueueAsync`, `ClaimAsync`, and so on, using `HashSetAsync`, `ScriptEvaluateAsync`, and `await Task.Delay` — see the *Production usage* section below."},{"id":"data-model","title":"Data model","role":"content","text":"Each job's state lives in a Redis hash plus a position in one of four lists:\n\n[code example]\n\nA job's hash carries:\n\n[code example]\n\nThe implementation uses:\n\n* [`LPUSH`](https://redis.io/docs/latest/commands/lpush) to add new job IDs to the pending list\n* [`RPOPLPUSH`](https://redis.io/docs/latest/commands/rpoplpush) (via `IDatabase.ListRightPopLeftPush`) to atomically claim a job into the processing list. StackExchange.Redis does not expose the blocking [`BRPOPLPUSH`](https://redis.io/docs/latest/commands/brpoplpush) variant, so the helper polls.\n* [`LREM`](https://redis.io/docs/latest/commands/lrem) to remove a claimed job from the processing list on complete or fail\n* [`LTRIM`](https://redis.io/docs/latest/commands/ltrim) to cap the completed and failed history lists\n* [`HSET`](https://redis.io/docs/latest/commands/hset) / [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) for job metadata\n* [`EXPIRE`](https://redis.io/docs/latest/commands/expire) on completed and failed hashes for automatic cleanup\n* [`PUBLISH`](https://redis.io/docs/latest/commands/publish) on `queue:jobs:events` for completion signalling\n* [Lua scripting](https://redis.io/docs/latest/develop/programmability/eval-intro) ([`EVALSHA`](https://redis.io/docs/latest/commands/evalsha)) for the complete, fail, and reclaim flows so each runs atomically against the processing list and metadata hash"},{"id":"enqueueing-jobs","title":"Enqueueing jobs","role":"content","text":"`Enqueue()` writes the metadata hash and pushes the job ID onto the pending list in one batch:\n\n[code example]\n\nThe payload is serialised to JSON with `System.Text.Json` so the queue can carry arbitrary nested structures without forcing every field into a hash. A batch is the StackExchange.Redis equivalent of a Redis pipeline — it groups commands into one round trip but does not wrap them in `MULTI`/`EXEC` (no atomicity is needed here: a writer can't race itself)."},{"id":"claiming-jobs","title":"Claiming jobs","role":"content","text":"A worker tries to atomically pop the next pending job onto the processing list. The Python and Node ports use the blocking `BRPOPLPUSH`/`BLMOVE` variants; StackExchange.Redis ships only the non-blocking `ListRightPopLeftPush`, so the helper polls on a 50 ms interval until either a job arrives or the per-call timeout elapses:\n\n[code example]\n\n`Environment.TickCount64` is used for deadline arithmetic to avoid the 24.9-day overflow of the 32-bit `Environment.TickCount`.\n\nThe `claim_token` is the worker's proof of ownership for this attempt. Every subsequent state change (complete, fail) checks it before touching the processing list, so a worker that hung past the visibility timeout cannot interfere with the new claimant."},{"id":"completing-jobs","title":"Completing jobs","role":"content","text":"`Complete()` runs a Lua script so the processing-list removal, the metadata write, and the history push happen atomically:\n\n[code example]\n\n`IDatabase.ScriptEvaluate` ships the script body on first use and switches to `EVALSHA` automatically for subsequent calls — you don't have to manage the SHA yourself in StackExchange.Redis. The Lua script checks the token first and returns `0` if the worker no longer owns the job (because the reclaimer moved it back to pending). The metadata hash also gets an `EXPIRE` so completed jobs are cleaned up automatically."},{"id":"failing-and-retrying","title":"Failing and retrying","role":"content","text":"`Fail()` either retries the job (back to pending) or moves it to the failed list once retries are exhausted:\n\n[code example]\n\nThe attempt counter is incremented on every `Claim()`, so a job that fails three times is moved to the failed list with `attempts = 3` and the final `last_error` preserved."},{"id":"reclaiming-stuck-jobs","title":"Reclaiming stuck jobs","role":"content","text":"If a worker dies mid-job — the process is killed, the host loses power, the network partitions — the job sits in the processing list with a `claimed_at_ms` that never advances. A periodic call to `ReclaimStuck()` walks the processing list and moves any job past the visibility timeout back to pending:\n\n[code example]\n\nThe Lua script also handles a narrower race: a worker that crashed between the claim's `RPOPLPUSH` and writing `claimed_at_ms`. Those jobs are reclaimed after `2 × visibility_ms` using `enqueued_at_ms` as a fallback timer, so they aren't stranded forever."},{"id":"stats-and-history","title":"Stats and history","role":"content","text":"`Stats()` reports queue depth plus per-process counters:\n\n[code example]\n\nThe completed and failed lists are capped via `LTRIM` so they never grow unbounded; a real deployment would also write completion events to a longer-term audit log if needed."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* Redis 6.2 or later running locally on the default port (6379). Earlier versions still work, since the helper only uses commands that have existed since Redis 2.6.\n* .NET 8 SDK or later.\n* The `StackExchange.Redis` client at version 2.7 or newer. The demo project references `StackExchange.Redis` 2.7.33 in its `.csproj`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of five files. Download them from the [`dotnet` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/job-queue/dotnet) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From that directory:\n\n[code example]\n\nYou should see:\n\n[code example]\n\nOpen [http://127.0.0.1:8795](http://127.0.0.1:8795) in a browser. You can:\n\n* Enqueue jobs of different kinds (email, webhook, thumbnail, invoice) in batches.\n* Start a pool of workers with configurable size, work latency, and *failure* / *hang* rates. A non-zero hang rate simulates worker crashes.\n* Click **Run reclaim sweep** to move any timed-out processing jobs back to pending.\n* Watch pending / processing / completed / failed lists update every 800 ms.\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`. You can also tune the visibility timeout with `--visibility-ms` and the bind address with `--host` / `--port`. Set the `QUEUE_NAME` environment variable to use a different queue prefix."},{"id":"the-mock-worker-pool","title":"The mock worker pool","role":"content","text":"The demo includes a small `JobWorker` and `WorkerPool` ([`JobWorker.cs`](https://github.com/redis/docs/blob/main/content/develop/use-cases/job-queue/dotnet/JobWorker.cs), [`WorkerPool.cs`](https://github.com/redis/docs/blob/main/content/develop/use-cases/job-queue/dotnet/WorkerPool.cs)) that stands in for whatever real background work your application would run. Each worker is a dedicated background `Thread` that:\n\n* Polls `queue.Claim(500)` for new jobs.\n* `Thread.Sleep(workLatencyMs)` to simulate doing the work.\n* Either completes successfully, fails (calling `queue.Fail()`), or *hangs* — returning without completing or failing the job so the reclaimer has to recover it.\n\nThe `FailRate` and `HangRate` knobs let you watch the at-least-once delivery and reclaim behaviours from the UI without writing test code.\n\n`Program.cs` calls `ThreadPool.SetMinThreads(64, 64)` at startup so concurrent claims, batch awaits, and HTTP handlers don't starve each other while the pool grows on demand."},{"id":"production-usage","title":"Production usage","role":"content","text":""},{"id":"choose-a-visibility-timeout-that-matches-your-worst-case-job-latency","title":"Choose a visibility timeout that matches your worst-case job latency","role":"content","text":"The visibility timeout has to exceed the longest real job time, with margin. If it's too short, a healthy worker that's running a slow job will get its work duplicated when the reclaimer fires. If it's too long, a real crash takes longer to detect. Most production deployments use a per-queue value tuned to the 99th-percentile job latency — for example, 2 minutes for email and 30 minutes for video transcoding."},{"id":"run-the-reclaimer-on-a-schedule","title":"Run the reclaimer on a schedule","role":"content","text":"The demo only reclaims when you click the button. In production, run `ReclaimStuck()` from a periodic task (an `IHostedService` running on a `PeriodicTimer` for fast queues, or a separate scheduler process for slow ones), or from each worker before it polls on `Claim()`. Both patterns work as long as *someone* runs the sweep."},{"id":"make-the-helper-async-in-production","title":"Make the helper async in production","role":"content","text":"To keep the demo readable, the helper here is fully synchronous. In a real ASP.NET Core service you should use the async overloads — `HashSetAsync`, `ListLeftPushAsync`, `ScriptEvaluateAsync`, `HashGetAllAsync` — and `await Task.Delay` in the claim polling loop. That frees the calling thread while round trips are in flight, which matters a lot when many handlers share the multiplexer's single command pipeline."},{"id":"use-a-separate-redis-database-or-key-prefix-per-queue","title":"Use a separate Redis database or key prefix per queue","role":"content","text":"The helper takes a `queueName` constructor argument so you can run multiple independent queues against one Redis instance — for example, one queue per priority level, or one per job kind. Keep queue keys under a clearly-namespaced prefix (here, `queue:jobs:*`) so they're easy to inspect and easy to clear without touching application data."},{"id":"cap-the-completed-and-failed-history","title":"Cap the completed and failed history","role":"content","text":"The demo keeps the last 50 completed and 50 failed job IDs via `LTRIM`. If you need longer history for audit purposes, write completion events to a separate Redis Stream (or to an external store) and keep the in-queue history short. Stream consumer groups give you the same fan-out semantics with a much richer history."},{"id":"tune-maxattempts-per-job-kind","title":"Tune `maxAttempts` per job kind","role":"content","text":"A blanket `maxAttempts = 3` is a reasonable default for transient failures (network timeouts, rate limits). Jobs that talk to non-idempotent external systems — for example, posting a Stripe charge — need either application-level idempotency keys or a much lower retry count. The helper exposes `maxAttempts` so each queue can pick its own policy."},{"id":"poll-instead-of-blocking--and-watch-out-for-the-multiplexer","title":"Poll instead of blocking — and watch out for the multiplexer","role":"content","text":"StackExchange.Redis [intentionally does not expose blocking commands](https://seredis.dev/PipelinesMultiplexers.html#multiplexing) such as `BLPOP`, `BRPOPLPUSH`, or `BLMOVE`. The multiplexer interleaves all commands from all callers onto one TCP connection, so a long blocking call would stall every other request on the same connection. The helper therefore calls the non-blocking `ListRightPopLeftPush` and polls — at 50 ms it remains responsive without flooding Redis. If you really want a single connection dedicated to a blocking pop (a less common pattern in C#), spin up a second `ConnectionMultiplexer` configured with `AllowAdmin = true` and a separate database, and run the blocking call through its dedicated socket; this demo intentionally does not."},{"id":"bump-the-minimum-thread-pool-size-in-long-running-services","title":"Bump the minimum thread-pool size in long-running services","role":"content","text":".NET's thread pool grows by ~2 threads per second under load, which can starve concurrent workers when a burst of jobs arrives. The demo calls `ThreadPool.SetMinThreads(64, 64)` at startup. In a real service set the values to match the maximum number of simultaneous outstanding Redis calls plus the size of your HTTP thread pool."},{"id":"use-environmenttickcount64-for-deadline-arithmetic","title":"Use `Environment.TickCount64` for deadline arithmetic","role":"content","text":"`Environment.TickCount` is a 32-bit signed integer in milliseconds and overflows after roughly 24.9 days. For visibility-timeout and claim-poll deadlines, always use `Environment.TickCount64` (or `Stopwatch.GetTimestamp`) instead — see the `Claim` implementation above."},{"id":"inspect-queue-state-directly-in-redis","title":"Inspect queue state directly in Redis","role":"content","text":"Because the queue is just lists and hashes, you can inspect it with `redis-cli`:\n\n[code example]"},{"id":"learn-more","title":"Learn more","role":"related","text":"This example uses the following Redis commands:\n\n* [`LPUSH`](https://redis.io/docs/latest/commands/lpush) to enqueue a job ID.\n* [`RPOPLPUSH`](https://redis.io/docs/latest/commands/rpoplpush) to atomically claim a job into the processing list (the non-blocking variant of [`BRPOPLPUSH`](https://redis.io/docs/latest/commands/brpoplpush), used because StackExchange.Redis doesn't expose blocking pops).\n* [`LREM`](https://redis.io/docs/latest/commands/lrem) to remove a job from the processing list on complete or fail.\n* [`LRANGE`](https://redis.io/docs/latest/commands/lrange) and [`LLEN`](https://redis.io/docs/latest/commands/llen) to read queue depth and list contents.\n* [`LTRIM`](https://redis.io/docs/latest/commands/ltrim) to cap the completed and failed history.\n* [`HSET`](https://redis.io/docs/latest/commands/hset) and [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) for job metadata.\n* [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby) for the attempt counter.\n* [`EXPIRE`](https://redis.io/docs/latest/commands/expire) for automatic cleanup of completed and failed jobs.\n* [`PUBLISH`](https://redis.io/docs/latest/commands/publish) for job-completion notifications.\n* [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) for atomic complete, fail, and reclaim flows.\n\nSee the [StackExchange.Redis documentation](https://seredis.dev/) for the full client reference."}],"examples":[{"id":"the-job-queue-helper-ex0","language":"csharp","code":"using StackExchange.Redis;\nusing JobQueueDemo;\n\nvar redis = ConnectionMultiplexer.Connect(\"localhost:6379\");\nvar queue = new RedisJobQueue(redis, visibilityMs: 5000);\n\nvar jobId = queue.Enqueue(new Dictionary<string, object>\n{\n    [\"kind\"] = \"email\",\n    [\"recipient\"] = \"alice@example.com\",\n});\n\n// In a worker thread:\nvar job = queue.Claim(timeoutMs: 1000);\nif (job is not null)\n{\n    try\n    {\n        // ... run the job ...\n        queue.Complete(job, new Dictionary<string, object> { [\"sent_at\"] = \"2026-05-11T15:00:00Z\" });\n    }\n    catch (Exception ex)\n    {\n        queue.Fail(job, ex.Message);\n    }\n}\n\n// In a periodic sweeper:\nvar reclaimed = queue.ReclaimStuck();","section_id":"the-job-queue-helper"},{"id":"data-model-ex0","language":"text","code":"queue:jobs:pending          (list)   pending job IDs, oldest at the right\nqueue:jobs:processing       (list)   claimed but not yet completed\nqueue:jobs:completed        (list)   recent successes (LTRIM-capped history)\nqueue:jobs:failed           (list)   terminally failed jobs\nqueue:jobs:job:{id}         (hash)   per-job metadata\nqueue:jobs:events           (pubsub) completion notifications","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"queue:jobs:job:9a4f...\n  id              = 9a4f...\n  payload         = {\"kind\":\"email\",\"recipient\":\"alice@example.com\"}\n  status          = pending | processing | completed | failed\n  attempts        = 1\n  enqueued_at_ms  = 1715441000000\n  claimed_at_ms   = 1715441000123\n  claim_token     = b3c0d1e2...        (per-claim random token)\n  completed_at_ms = 1715441000456\n  result          = {\"sent_at\":\"...\"}\n  last_error      = \"smtp timeout\"","section_id":"data-model"},{"id":"enqueueing-jobs-ex0","language":"csharp","code":"public string Enqueue(IDictionary<string, object> payload)\n{\n    var jobId = RandomTokenHex(8);\n    var nowMs = NowMs();\n    var metaKey = MetaKey(jobId);\n\n    var hashEntries = new[]\n    {\n        new HashEntry(\"id\", jobId),\n        new HashEntry(\"payload\", JsonSerializer.Serialize(payload)),\n        new HashEntry(\"status\", \"pending\"),\n        new HashEntry(\"attempts\", 0),\n        new HashEntry(\"enqueued_at_ms\", nowMs),\n        new HashEntry(\"claim_token\", \"\"),\n    };\n\n    var batch = _db.CreateBatch();\n    var hashTask = batch.HashSetAsync(metaKey, hashEntries);\n    var pushTask = batch.ListLeftPushAsync(_pendingKey, jobId);\n    batch.Execute();\n    Task.WaitAll(hashTask, pushTask);\n\n    lock (_statsLock) { _enqueued++; }\n    return jobId;\n}","section_id":"enqueueing-jobs"},{"id":"claiming-jobs-ex0","language":"csharp","code":"public ClaimedJob? Claim(int timeoutMs = 1000)\n{\n    var deadline = Environment.TickCount64 + Math.Max(0, timeoutMs);\n    const int pollIntervalMs = 50;\n\n    RedisValue popped;\n    while (true)\n    {\n        popped = _db.ListRightPopLeftPush(_pendingKey, _processingKey);\n        if (!popped.IsNull) break;\n        var remaining = deadline - Environment.TickCount64;\n        if (remaining <= 0) return null;\n        Thread.Sleep((int)Math.Min(pollIntervalMs, remaining));\n    }\n\n    var jobId = (string)popped!;\n    var token = RandomTokenHex(8);\n    var nowMs = NowMs();\n    var metaKey = MetaKey(jobId);\n\n    var batch = _db.CreateBatch();\n    var hashSetTask = batch.HashSetAsync(metaKey, new[]\n    {\n        new HashEntry(\"status\", \"processing\"),\n        new HashEntry(\"claimed_at_ms\", nowMs),\n        new HashEntry(\"claim_token\", token),\n    });\n    var incrTask = batch.HashIncrementAsync(metaKey, \"attempts\", 1);\n    var hgetallTask = batch.HashGetAllAsync(metaKey);\n    batch.Execute();\n    Task.WaitAll(hashSetTask, incrTask, hgetallTask);\n\n    var meta = ToDictionary(hgetallTask.Result);\n    var payload = ParseJsonObject(meta.GetValueOrDefault(\"payload\", \"{}\"));\n    var attempts = int.TryParse(meta.GetValueOrDefault(\"attempts\", \"1\"), out var a) ? a : 1;\n    return new ClaimedJob(jobId, payload, attempts, token);\n}","section_id":"claiming-jobs"},{"id":"completing-jobs-ex0","language":"csharp","code":"public bool Complete(ClaimedJob job, IDictionary<string, object> result)\n{\n    var keys = new RedisKey[] { _metaPrefix, _processingKey, _completedKey };\n    var args = new RedisValue[]\n    {\n        job.Id, job.ClaimToken, \"completed\", NowMs(),\n        JsonSerializer.Serialize(result), _completedTtl, _completedHistory,\n    };\n\n    var raw = _db.ScriptEvaluate(CompleteScript, keys, args);\n    var ok = !raw.IsNull && (long)raw == 1;\n    if (!ok) return false;\n\n    _db.Publish(\n        RedisChannel.Literal(_eventsChannel),\n        JsonSerializer.Serialize(new Dictionary<string, string>\n        {\n            [\"id\"] = job.Id,\n            [\"status\"] = \"completed\",\n        }));\n\n    lock (_statsLock) { _completed++; }\n    return true;\n}","section_id":"completing-jobs"},{"id":"failing-and-retrying-ex0","language":"csharp","code":"public bool Fail(ClaimedJob job, string error)\n{\n    var retry = job.Attempts < _maxAttempts;\n    var keys = new RedisKey[]\n    {\n        _metaPrefix, _processingKey, _pendingKey, _failedKey,\n    };\n    var args = new RedisValue[]\n    {\n        job.Id, job.ClaimToken, error, NowMs(),\n        _completedTtl, _completedHistory, retry ? \"1\" : \"0\",\n    };\n\n    var raw = _db.ScriptEvaluate(FailScript, keys, args);\n    if (raw.IsNull || (long)raw == 0) return false;\n    // ... publish event, bump counter if final failure ...\n    return true;\n}","section_id":"failing-and-retrying"},{"id":"reclaiming-stuck-jobs-ex0","language":"csharp","code":"public IReadOnlyList<string> ReclaimStuck()\n{\n    var keys = new RedisKey[] { _pendingKey, _processingKey, _metaPrefix };\n    var args = new RedisValue[] { NowMs(), _visibilityMs };\n    var raw = _db.ScriptEvaluate(ReclaimScript, keys, args);\n    if (raw.IsNull) return Array.Empty<string>();\n    var reclaimedArray = (RedisResult[])raw!;\n    return reclaimedArray.Select(r => (string)r!).ToList();\n}","section_id":"reclaiming-stuck-jobs"},{"id":"stats-and-history-ex0","language":"csharp","code":"public IDictionary<string, object> Stats()\n{\n    var batch = _db.CreateBatch();\n    var pendingTask = batch.ListLengthAsync(_pendingKey);\n    var processingTask = batch.ListLengthAsync(_processingKey);\n    var completedTask = batch.ListLengthAsync(_completedKey);\n    var failedTask = batch.ListLengthAsync(_failedKey);\n    batch.Execute();\n    Task.WaitAll(pendingTask, processingTask, completedTask, failedTask);\n\n    lock (_statsLock)\n    {\n        return new Dictionary<string, object>\n        {\n            [\"enqueued_total\"] = _enqueued,\n            [\"completed_total\"] = _completed,\n            [\"failed_total\"] = _failed,\n            [\"reclaimed_total\"] = _reclaimed,\n            [\"pending_depth\"] = pendingTask.Result,\n            [\"processing_depth\"] = processingTask.Result,\n            [\"completed_depth\"] = completedTask.Result,\n            [\"failed_depth\"] = failedTask.Result,\n            [\"visibility_ms\"] = _visibilityMs,\n        };\n    }\n}","section_id":"stats-and-history"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir job-queue-demo && cd job-queue-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/job-queue/dotnet\ncurl -O $BASE/RedisJobQueue.cs\ncurl -O $BASE/JobWorker.cs\ncurl -O $BASE/WorkerPool.cs\ncurl -O $BASE/Program.cs\ncurl -O $BASE/JobQueueDemo.csproj","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"dotnet run","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"Connected to Redis at localhost:6379\nRedis job-queue demo server listening on http://127.0.0.1:8795\nUsing Redis at localhost:6379\nVisibility timeout: 5000 ms","section_id":"start-the-demo-server"},{"id":"inspect-queue-state-directly-in-redis-ex0","language":"bash","code":"# How many pending jobs?\nredis-cli LLEN queue:jobs:pending\n\n# Look at the next 5 jobs to be picked up.\nredis-cli LRANGE queue:jobs:pending -5 -1\n\n# Read a job's metadata.\nredis-cli HGETALL queue:jobs:job:9a4f0d1c\n\n# How many jobs are currently being processed?\nredis-cli LLEN queue:jobs:processing\n\n# Clear everything for this queue (be careful — this deletes work).\nredis-cli --scan --pattern 'queue:jobs:*' | xargs redis-cli DEL","section_id":"inspect-queue-state-directly-in-redis"}]}
{"schema_version":2,"id":"develop/use-cases/job-queue/go","title":"Redis job queue with go-redis","url":"https://redis.io/docs/latest/develop/use-cases/job-queue/go/","summary":"Implement a Redis job queue in Go with go-redis","aliases":["/develop/use-cases/job-queue/go-redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:50:30+01:00","children":[],"page_type":"content","content_hash":"a25e7a76ba2e6c3357eacee3cd1cdf70e1f37a0431a4b0d527feacc06ea21f24","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis-backed job queue in Go with [`go-redis`](https://redis.io/docs/latest/develop/clients/go). It includes a small local web server built with the Go standard library so you can enqueue jobs, watch a pool of workers drain them, and see the reclaimer recover jobs from a simulated worker crash."},{"id":"overview","title":"Overview","role":"overview","text":"A job queue lets your application offload background work — sending email, processing payments, image transcoding, ML inference, webhooks — from the request path. Producers enqueue jobs in milliseconds and return to the user; workers pull from the queue and process them on their own schedule.\n\nThat gives you:\n\n* Low-latency user-facing requests, even when downstream work is slow or bursty\n* Horizontal scale across many worker processes that share one Redis instance\n* At-least-once delivery so a worker crash doesn't lose work\n* Visibility-timeout reclaim that returns stuck jobs to the queue automatically\n* Job metadata, retry counts, and completion results in Redis hashes with TTL\n\nIn this example, each job is identified by a random hex ID and its payload, status, and result live in a Redis hash under `queue:jobs:job:{id}`. Pending IDs sit in a list, claimed IDs move atomically to a *processing* list, and completed or failed IDs land in short history lists."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. The application calls `queue.Enqueue(ctx, payload)`\n2. The helper writes the job metadata hash and `LPUSH`es the job ID onto the pending list\n3. A worker calls `queue.Claim(ctx, timeoutMs)`\n4. The helper runs `BLMOVE` to atomically move the next pending ID into the processing list and writes a per-claim `claim_token` plus `claimed_at_ms` on the hash\n5. The worker runs the job and calls `queue.Complete(ctx, job, result)` or `queue.Fail(ctx, job, errMsg)`\n6. `Complete` removes the job from the processing list, writes the result, and `LPUSH`es the ID onto the completed history (with `LTRIM` and an `EXPIRE` on the hash for cleanup)\n7. `Fail` either retries the job (back to pending) or moves it to the failed list once retries are exhausted\n\nIf a worker dies before completing a job, the job sits in the processing list with a `claimed_at_ms` older than the visibility timeout. A periodic call to `queue.ReclaimStuck(ctx)` finds those jobs and moves them back to pending so another worker can pick them up.\n\nEvery state change holds the token: a worker that has been reclaimed cannot later complete or fail a job another worker has already claimed."},{"id":"the-job-queue-helper","title":"The job queue helper","role":"content","text":"The `RedisJobQueue` type wraps the queue operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/job-queue/go/job_queue.go)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each job's state lives in a Redis hash plus a position in one of four lists:\n\n[code example]\n\nA job's hash carries:\n\n[code example]\n\nThe implementation uses:\n\n* [`LPUSH`](https://redis.io/docs/latest/commands/lpush) to add new job IDs to the pending list\n* [`BLMOVE`](https://redis.io/docs/latest/commands/blmove) to atomically claim a job into the processing list (the modern replacement for the deprecated [`BRPOPLPUSH`](https://redis.io/docs/latest/commands/brpoplpush))\n* [`LREM`](https://redis.io/docs/latest/commands/lrem) to remove a claimed job from the processing list on complete or fail\n* [`LTRIM`](https://redis.io/docs/latest/commands/ltrim) to cap the completed and failed history lists\n* [`HSET`](https://redis.io/docs/latest/commands/hset) / [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) for job metadata\n* [`EXPIRE`](https://redis.io/docs/latest/commands/expire) on completed and failed hashes for automatic cleanup\n* [`PUBLISH`](https://redis.io/docs/latest/commands/publish) on `queue:jobs:events` for completion signalling\n* [Lua scripting](https://redis.io/docs/latest/develop/programmability/eval-intro) ([`EVALSHA`](https://redis.io/docs/latest/commands/evalsha)) for the complete, fail, and reclaim flows so each runs atomically against the processing list and metadata hash"},{"id":"enqueueing-jobs","title":"Enqueueing jobs","role":"content","text":"`Enqueue()` writes the metadata hash and pushes the job ID onto the pending list in one pipeline:\n\n[code example]\n\nThe payload is stored as JSON so the queue can carry arbitrary nested structures without forcing every field into a hash."},{"id":"claiming-jobs-with-blmove","title":"Claiming jobs with BLMOVE","role":"content","text":"A worker blocks until a job is available, then atomically pops it from the pending list and pushes it onto the processing list. `BLMOVE` does both in a single Redis call:\n\n[code example]\n\nThe `claim_token` is the worker's proof of ownership for this attempt. Every subsequent state change (complete, fail) checks it before touching the processing list, so a worker that hung past the visibility timeout cannot interfere with the new claimant.\n\n`BLMOVE` is the modern replacement for `BRPOPLPUSH`, which was deprecated in Redis 6.2. `go-redis` exposes both for backwards compatibility but the helper uses `BLMOVE` with `RIGHT`/`LEFT` arguments to match `BRPOPLPUSH` semantics."},{"id":"completing-jobs","title":"Completing jobs","role":"content","text":"`Complete()` runs a Lua script so the processing-list removal, the metadata write, and the history push happen atomically:\n\n[code example]\n\nThe Lua script checks the token first and returns `0` if the worker no longer owns the job (because the reclaimer moved it back to pending). The metadata hash also gets an `EXPIRE` so completed jobs are cleaned up automatically."},{"id":"failing-and-retrying","title":"Failing and retrying","role":"content","text":"`Fail()` either retries the job (back to pending) or moves it to the failed list once retries are exhausted:\n\n[code example]\n\nThe attempt counter is incremented on every `Claim()`, so a job that fails three times is moved to the failed list with `attempts = 3` and the final `last_error` preserved."},{"id":"reclaiming-stuck-jobs","title":"Reclaiming stuck jobs","role":"content","text":"If a worker dies mid-job — the process is killed, the host loses power, the network partitions — the job sits in the processing list with a `claimed_at_ms` that never advances. A periodic call to `ReclaimStuck()` walks the processing list and moves any job past the visibility timeout back to pending:\n\n[code example]\n\nThe Lua script also handles a narrower race: a worker that crashed between `BLMOVE` and writing `claimed_at_ms`. Those jobs are reclaimed after `2 × visibility_ms` using `enqueued_at_ms` as a fallback timer, so they aren't stranded forever."},{"id":"stats-and-history","title":"Stats and history","role":"content","text":"`Stats()` reports queue depth plus per-process counters:\n\n[code example]\n\nThe completed and failed lists are capped via `LTRIM` so they never grow unbounded; a real deployment would also write completion events to a longer-term audit log if needed."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* Redis 6.2 or later running locally on the default port (6379). `BLMOVE` requires Redis 6.2+; earlier servers can fall back to the deprecated `BRPOPLPUSH` exposed on the same client.\n* Go 1.21 or later.\n* The `go-redis` client. The included `go.mod` pins:\n\n  [code example]"},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of five files. Download them from the [`go` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/job-queue/go) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From that directory, create a tiny `main.go` to start the server. Go's `package main` can't live in the same directory as `package jobqueue`, so put it in a subdirectory:\n\n[code example]\n\nThen build and run:\n\n[code example]\n\nYou should see:\n\n[code example]\n\nOpen [http://127.0.0.1:8792](http://127.0.0.1:8792) in a browser. You can:\n\n* Enqueue jobs of different kinds (email, webhook, thumbnail, invoice) in batches.\n* Start a pool of workers with configurable size, work latency, and *failure* / *hang* rates. A non-zero hang rate simulates worker crashes.\n* Click **Run reclaim sweep** to move any timed-out processing jobs back to pending.\n* Watch pending / processing / completed / failed lists update every 800 ms.\n\nIf your Redis server is running elsewhere, start the demo with `-redis-host` and `-redis-port`. You can also tune the visibility timeout with `-visibility-ms` and the queue keyspace with `-queue-name`."},{"id":"the-mock-worker-pool","title":"The mock worker pool","role":"content","text":"The demo includes a small `Worker` and `WorkerPool` ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/job-queue/go/worker.go)) that stands in for whatever real background work your application would run. Each worker:\n\n* Blocks on `queue.Claim()` for new jobs.\n* Sleeps `workLatencyMs` to simulate doing the work.\n* Either completes successfully, fails (calling `queue.Fail()`), or *hangs* — returning without completing or failing the job so the reclaimer has to recover it.\n\nThe `failRate` and `hangRate` knobs let you watch the at-least-once delivery and reclaim behaviours from the UI without writing test code."},{"id":"production-usage","title":"Production usage","role":"content","text":""},{"id":"choose-a-visibility-timeout-that-matches-your-worst-case-job-latency","title":"Choose a visibility timeout that matches your worst-case job latency","role":"content","text":"The visibility timeout has to exceed the longest real job time, with margin. If it's too short, a healthy worker that's running a slow job will get its work duplicated when the reclaimer fires. If it's too long, a real crash takes longer to detect. Most production deployments use a per-queue value tuned to the 99th-percentile job latency — for example, 2 minutes for email and 30 minutes for video transcoding."},{"id":"run-the-reclaimer-on-a-schedule","title":"Run the reclaimer on a schedule","role":"content","text":"The demo only reclaims when you click the button. In production, run `ReclaimStuck()` from a periodic goroutine (every few seconds for fast queues, every minute for slow ones), or from each worker before it blocks on `Claim()`. Both patterns work as long as *someone* runs the sweep."},{"id":"use-a-separate-redis-database-or-key-prefix-per-queue","title":"Use a separate Redis database or key prefix per queue","role":"content","text":"The helper takes a `QueueName` option so you can run multiple independent queues against one Redis instance — for example, one queue per priority level, or one per job kind. Keep queue keys under a clearly-namespaced prefix (here, `queue:jobs:*`) so they're easy to inspect and easy to clear without touching application data."},{"id":"cap-the-completed-and-failed-history","title":"Cap the completed and failed history","role":"content","text":"The demo keeps the last 50 completed and 50 failed job IDs via `LTRIM`. If you need longer history for audit purposes, write completion events to a separate Redis Stream (or to an external store) and keep the in-queue history short. Stream consumer groups give you the same fan-out semantics with a much richer history."},{"id":"tune-maxattempts-per-job-kind","title":"Tune `MaxAttempts` per job kind","role":"content","text":"A blanket `MaxAttempts = 3` is a reasonable default for transient failures (network timeouts, rate limits). Jobs that talk to non-idempotent external systems — for example, posting a Stripe charge — need either application-level idempotency keys or a much lower retry count. The helper exposes `MaxAttempts` so each queue can pick its own policy."},{"id":"use-contextcontext-for-clean-shutdown","title":"Use `context.Context` for clean shutdown","role":"content","text":"Workers in the demo run as goroutines that block in `Claim()` (`BLMOVE` is a blocking Redis call). The `WorkerPool` holds a parent `context.Context`; calling `pool.Stop()` cancels each worker's child context so the next `BLMOVE` returns and the goroutine exits. Wire your real worker pool to your service's shutdown context so `SIGTERM` produces a clean drain instead of a hard kill."},{"id":"inspect-queue-state-directly-in-redis","title":"Inspect queue state directly in Redis","role":"content","text":"Because the queue is just lists and hashes, you can inspect it with `redis-cli`:\n\n[code example]"},{"id":"learn-more","title":"Learn more","role":"related","text":"This example uses the following Redis commands:\n\n* [`LPUSH`](https://redis.io/docs/latest/commands/lpush) to enqueue a job ID.\n* [`BLMOVE`](https://redis.io/docs/latest/commands/blmove) to atomically claim a job into the processing list.\n* [`LREM`](https://redis.io/docs/latest/commands/lrem) to remove a job from the processing list on complete or fail.\n* [`LRANGE`](https://redis.io/docs/latest/commands/lrange) and [`LLEN`](https://redis.io/docs/latest/commands/llen) to read queue depth and list contents.\n* [`LTRIM`](https://redis.io/docs/latest/commands/ltrim) to cap the completed and failed history.\n* [`HSET`](https://redis.io/docs/latest/commands/hset) and [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) for job metadata.\n* [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby) for the attempt counter.\n* [`EXPIRE`](https://redis.io/docs/latest/commands/expire) for automatic cleanup of completed and failed jobs.\n* [`PUBLISH`](https://redis.io/docs/latest/commands/publish) for job-completion notifications.\n* [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) for atomic complete, fail, and reclaim flows.\n\nSee the [`go-redis` documentation](https://redis.io/docs/latest/develop/clients/go) for full client reference."}],"examples":[{"id":"the-job-queue-helper-ex0","language":"go","code":"import (\n    \"context\"\n\n    \"github.com/redis/go-redis/v9\"\n    \"jobqueue\"\n)\n\nclient := redis.NewClient(&redis.Options{Addr: \"localhost:6379\"})\nqueue := jobqueue.NewRedisJobQueue(client, jobqueue.Options{\n    VisibilityMs: 5000,\n})\n\nctx := context.Background()\njobID, _ := queue.Enqueue(ctx, map[string]any{\n    \"kind\":      \"email\",\n    \"recipient\": \"alice@example.com\",\n})\n\n// In a worker goroutine:\njob, _ := queue.Claim(ctx, 1000)\nif job != nil {\n    // ... run the job ...\n    if _, err := queue.Complete(ctx, job, map[string]any{\"sent_at\": \"2026-05-11T15:00:00Z\"}); err != nil {\n        queue.Fail(ctx, job, err.Error())\n    }\n}\n\n// In a periodic sweeper:\nreclaimed, _ := queue.ReclaimStuck(ctx)\n_ = reclaimed","section_id":"the-job-queue-helper"},{"id":"data-model-ex0","language":"text","code":"queue:jobs:pending          (list)   pending job IDs, oldest at the right\nqueue:jobs:processing       (list)   claimed but not yet completed\nqueue:jobs:completed        (list)   recent successes (LTRIM-capped history)\nqueue:jobs:failed           (list)   terminally failed jobs\nqueue:jobs:job:{id}         (hash)   per-job metadata\nqueue:jobs:events           (pubsub) completion notifications","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"queue:jobs:job:9a4f...\n  id              = 9a4f...\n  payload         = {\"kind\":\"email\",\"recipient\":\"alice@example.com\"}\n  status          = pending | processing | completed | failed\n  attempts        = 1\n  enqueued_at_ms  = 1715441000000\n  claimed_at_ms   = 1715441000123\n  claim_token     = b3c0d1e2...        (per-claim random token)\n  completed_at_ms = 1715441000456\n  result          = {\"sent_at\":\"...\"}\n  last_error      = \"smtp timeout\"","section_id":"data-model"},{"id":"enqueueing-jobs-ex0","language":"go","code":"func (q *RedisJobQueue) Enqueue(ctx context.Context, payload map[string]any) (string, error) {\n    jobID := tokenHex(8)\n    payloadJSON, _ := json.Marshal(payload)\n    now := nowMs()\n    meta := map[string]interface{}{\n        \"id\":             jobID,\n        \"payload\":        string(payloadJSON),\n        \"status\":         \"pending\",\n        \"attempts\":       0,\n        \"enqueued_at_ms\": now,\n        \"claim_token\":    \"\",\n    }\n    pipe := q.client.Pipeline()\n    pipe.HSet(ctx, q.metaKey(jobID), meta)\n    pipe.LPush(ctx, q.pendingKey, jobID)\n    if _, err := pipe.Exec(ctx); err != nil {\n        return \"\", err\n    }\n    return jobID, nil\n}","section_id":"enqueueing-jobs"},{"id":"claiming-jobs-with-blmove-ex0","language":"go","code":"func (q *RedisJobQueue) Claim(ctx context.Context, timeoutMs int) (*ClaimedJob, error) {\n    timeout := time.Duration(timeoutMs) * time.Millisecond\n    if timeout < 100*time.Millisecond {\n        timeout = 100 * time.Millisecond\n    }\n    jobID, err := q.client.BLMove(ctx, q.pendingKey, q.processingKey, \"RIGHT\", \"LEFT\", timeout).Result()\n    if err == redis.Nil {\n        return nil, nil\n    }\n    if err != nil {\n        return nil, err\n    }\n\n    token := tokenHex(8)\n    now := nowMs()\n    metaKey := q.metaKey(jobID)\n\n    pipe := q.client.Pipeline()\n    pipe.HSet(ctx, metaKey, map[string]interface{}{\n        \"status\":        \"processing\",\n        \"claimed_at_ms\": now,\n        \"claim_token\":   token,\n    })\n    pipe.HIncrBy(ctx, metaKey, \"attempts\", 1)\n    getAll := pipe.HGetAll(ctx, metaKey)\n    if _, err := pipe.Exec(ctx); err != nil {\n        return nil, err\n    }\n    // ... parse payload and attempts from getAll.Val() ...\n    return &ClaimedJob{ID: jobID, Payload: payload, Attempts: attempts, ClaimToken: token}, nil\n}","section_id":"claiming-jobs-with-blmove"},{"id":"completing-jobs-ex0","language":"go","code":"func (q *RedisJobQueue) Complete(ctx context.Context, job *ClaimedJob, result map[string]any) (bool, error) {\n    resultJSON, _ := json.Marshal(result)\n    keys := []string{q.metaPrefix, q.processingKey, q.completedKey}\n    args := []interface{}{\n        job.ID,\n        job.ClaimToken,\n        \"completed\",\n        nowMs(),\n        string(resultJSON),\n        q.completedTTL,\n        q.completedHistory,\n    }\n    res, err := q.completeScript.Run(ctx, q.client, keys, args...).Result()\n    if err != nil {\n        return false, err\n    }\n    n, _ := res.(int64)\n    if n != 1 {\n        return false, nil\n    }\n    eventPayload, _ := json.Marshal(map[string]any{\"id\": job.ID, \"status\": \"completed\"})\n    q.client.Publish(ctx, q.eventsChannel, string(eventPayload))\n    return true, nil\n}","section_id":"completing-jobs"},{"id":"failing-and-retrying-ex0","language":"go","code":"func (q *RedisJobQueue) Fail(ctx context.Context, job *ClaimedJob, errMsg string) (bool, error) {\n    retry := job.Attempts < q.maxAttempts\n    retryArg := \"0\"\n    if retry {\n        retryArg = \"1\"\n    }\n    keys := []string{q.metaPrefix, q.processingKey, q.pendingKey, q.failedKey}\n    args := []interface{}{\n        job.ID, job.ClaimToken, errMsg, nowMs(),\n        q.completedTTL, q.completedHistory, retryArg,\n    }\n    res, err := q.failScript.Run(ctx, q.client, keys, args...).Result()\n    if err != nil {\n        return false, err\n    }\n    n, _ := res.(int64)\n    return n != 0, nil\n}","section_id":"failing-and-retrying"},{"id":"reclaiming-stuck-jobs-ex0","language":"go","code":"func (q *RedisJobQueue) ReclaimStuck(ctx context.Context) ([]string, error) {\n    keys := []string{q.pendingKey, q.processingKey, q.metaPrefix}\n    args := []interface{}{nowMs(), q.visibilityMs}\n    res, err := q.reclaimScript.Run(ctx, q.client, keys, args...).Result()\n    if err != nil {\n        return nil, err\n    }\n    raw, _ := res.([]interface{})\n    out := make([]string, 0, len(raw))\n    for _, item := range raw {\n        if s, ok := item.(string); ok {\n            out = append(out, s)\n        }\n    }\n    return out, nil\n}","section_id":"reclaiming-stuck-jobs"},{"id":"stats-and-history-ex0","language":"go","code":"func (q *RedisJobQueue) Stats(ctx context.Context) (map[string]any, error) {\n    pipe := q.client.Pipeline()\n    pendingCmd := pipe.LLen(ctx, q.pendingKey)\n    processingCmd := pipe.LLen(ctx, q.processingKey)\n    completedCmd := pipe.LLen(ctx, q.completedKey)\n    failedCmd := pipe.LLen(ctx, q.failedKey)\n    if _, err := pipe.Exec(ctx); err != nil {\n        return nil, err\n    }\n    return map[string]any{\n        \"enqueued_total\":   q.enqueuedN,\n        \"completed_total\":  q.completedN,\n        \"failed_total\":     q.failedN,\n        \"reclaimed_total\":  q.reclaimedN,\n        \"pending_depth\":    pendingCmd.Val(),\n        \"processing_depth\": processingCmd.Val(),\n        \"completed_depth\":  completedCmd.Val(),\n        \"failed_depth\":     failedCmd.Val(),\n        \"visibility_ms\":    q.visibilityMs,\n    }, nil\n}","section_id":"stats-and-history"},{"id":"prerequisites-ex0","language":"text","code":"require github.com/redis/go-redis/v9 v9.18.0","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir job-queue-demo && cd job-queue-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/job-queue/go\ncurl -O $BASE/job_queue.go\ncurl -O $BASE/worker.go\ncurl -O $BASE/demo_server.go\ncurl -O $BASE/go.mod\ncurl -O $BASE/go.sum","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"mkdir -p cmd/demo\ncat > cmd/demo/main.go <<'EOF'\npackage main\n\nimport \"jobqueue\"\n\nfunc main() { jobqueue.RunDemoServer() }\nEOF","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"bash","code":"go mod tidy\ngo run ./cmd/demo","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex2","language":"text","code":"Redis job-queue demo server listening on http://127.0.0.1:8792\nUsing Redis at localhost:6379\nVisibility timeout: 5000 ms","section_id":"start-the-demo-server"},{"id":"inspect-queue-state-directly-in-redis-ex0","language":"bash","code":"# How many pending jobs?\nredis-cli LLEN queue:jobs:pending\n\n# Look at the next 5 jobs to be picked up.\nredis-cli LRANGE queue:jobs:pending -5 -1\n\n# Read a job's metadata.\nredis-cli HGETALL queue:jobs:job:9a4f0d1c\n\n# How many jobs are currently being processed?\nredis-cli LLEN queue:jobs:processing\n\n# Clear everything for this queue (be careful — this deletes work).\nredis-cli --scan --pattern 'queue:jobs:*' | xargs redis-cli DEL","section_id":"inspect-queue-state-directly-in-redis"}]}
{"schema_version":2,"id":"develop/use-cases/job-queue","title":"Redis job queue","url":"https://redis.io/docs/latest/develop/use-cases/job-queue/","summary":"Run a reliable background job queue with Redis.","tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:50:30+01:00","children":[{"id":"develop/use-cases/job-queue/redis-py","summary":"Implement a Redis job queue in Python with redis-py","title":"Redis job queue with redis-py","url":"https://redis.io/docs/latest/develop/use-cases/job-queue/redis-py/"},{"id":"develop/use-cases/job-queue/nodejs","summary":"Implement a Redis job queue in Node.js with node-redis","title":"Redis job queue with node-redis","url":"https://redis.io/docs/latest/develop/use-cases/job-queue/nodejs/"},{"id":"develop/use-cases/job-queue/go","summary":"Implement a Redis job queue in Go with go-redis","title":"Redis job queue with go-redis","url":"https://redis.io/docs/latest/develop/use-cases/job-queue/go/"},{"id":"develop/use-cases/job-queue/java-jedis","summary":"Implement a Redis job queue in Java with Jedis","title":"Redis job queue with Jedis","url":"https://redis.io/docs/latest/develop/use-cases/job-queue/java-jedis/"},{"id":"develop/use-cases/job-queue/java-lettuce","summary":"Implement a Redis job queue in Java with Lettuce","title":"Redis job queue with Lettuce","url":"https://redis.io/docs/latest/develop/use-cases/job-queue/java-lettuce/"},{"id":"develop/use-cases/job-queue/dotnet","summary":"Implement a Redis job queue in C# with StackExchange.Redis","title":"Redis job queue with StackExchange.Redis","url":"https://redis.io/docs/latest/develop/use-cases/job-queue/dotnet/"},{"id":"develop/use-cases/job-queue/php","summary":"Implement a Redis job queue in PHP with Predis","title":"Redis job queue with Predis","url":"https://redis.io/docs/latest/develop/use-cases/job-queue/php/"},{"id":"develop/use-cases/job-queue/ruby","summary":"Implement a Redis job queue in Ruby with redis-rb","title":"Redis job queue with redis-rb","url":"https://redis.io/docs/latest/develop/use-cases/job-queue/ruby/"},{"id":"develop/use-cases/job-queue/rust","summary":"Implement a Redis job queue in Rust with redis-rs","title":"Redis job queue with redis-rs","url":"https://redis.io/docs/latest/develop/use-cases/job-queue/rust/"}],"page_type":"content","content_hash":"962c107649f26a710d043b9adb4db6faf1bb2a9a48189462b32632c4c5e3222a","sections":[{"id":"when-to-use-a-job-queue","title":"When to use a job queue","role":"overview","text":"Use a Redis job queue when you need to offload background work — sending email, processing payments, image processing, ML inference, webhooks — from user-facing request paths and distribute it reliably across a pool of workers."},{"id":"why-the-problem-is-hard","title":"Why the problem is hard","role":"content","text":"Synchronous processing of background work blocks user requests and degrades latency under load.\nA single user action often triggers multiple downstream tasks (send email, update analytics,\nnotify billing) and without a shared queue each service needs point-to-point integration that\nbecomes brittle at scale. Some of the obvious workarounds have real drawbacks:\n\n-   **Simple in-process queues** lose jobs on crash and can't distribute work across multiple\n    workers or services.\n-   **Polling a database for pending rows** generates constant load on the primary database and\n    introduces handoff races between workers competing for the same row.\n-   **Adding a dedicated message broker** (RabbitMQ, Kafka, SQS) means another piece of\n    infrastructure to deploy, monitor, and pay for — often just to move jobs a few hops.\n\nA workable job queue needs at-least-once delivery, an atomic handoff from queue to worker, and\na way to reclaim jobs from workers that crashed mid-processing. You also need to track per-job\nstatus, retry counts, and completion results without those records leaking forever."},{"id":"what-you-can-expect-from-a-redis-solution","title":"What you can expect from a Redis solution","role":"content","text":"You can:\n\n-   Decouple APIs and services from workers so user-facing latency stays low under bursty load.\n-   Distribute jobs across many workers with at-least-once delivery and automatic retry of\n    failed or timed-out jobs.\n-   Run FIFO, LIFO, priority, and delayed-execution queues on core Redis data structures.\n-   Reclaim jobs from crashed workers using a visibility timeout, so no job is lost when a\n    worker dies mid-processing.\n-   Fan out a single user action to multiple downstream services through streams and consumer\n    groups.\n-   Use established libraries — Sidekiq, Celery, Bull/BullMQ, RQ — that implement reliable\n    queue patterns on Redis out of the box."},{"id":"how-redis-supports-the-solution","title":"How Redis supports the solution","role":"content","text":"In practice, a Redis job queue stores pending job IDs in a list and moves each claimed job\natomically to a *processing* list so a crashed worker's job can be reclaimed later. Job\nmetadata (payload, status, attempts, result) lives in a hash, and completed jobs are cleaned\nup automatically with a TTL.\n\nRedis provides the following features that make it a good fit for background jobs:\n\n-   [`LPUSH`](https://redis.io/docs/latest/commands/lpush) and [`BRPOPLPUSH`](https://redis.io/docs/latest/commands/brpoplpush)\n    (or [`BLMOVE`](https://redis.io/docs/latest/commands/blmove)) for atomic enqueue and blocking claim,\n    so a worker dequeues a job and registers it in the processing list in a single round trip.\n-   [Lists](https://redis.io/docs/latest/develop/data-types/lists) for the *processing list* visibility-timeout\n    pattern — jobs move atomically from pending to processing, and a reclaimer scans for\n    timed-out jobs and moves them back.\n-   [Sorted sets](https://redis.io/docs/latest/develop/data-types/sorted-sets)\n    ([`ZADD`](https://redis.io/docs/latest/commands/zadd),\n    [`ZRANGEBYSCORE`](https://redis.io/docs/latest/commands/zrangebyscore)) for delayed execution and\n    priority queues, scored by run-at timestamp or priority.\n-   [Streams](https://redis.io/docs/latest/develop/data-types/streams) with\n    [consumer groups](https://redis.io/docs/latest/develop/data-types/streams#consumer-groups) for fan-out\n    across multiple worker pools with independent progress tracking.\n-   [Hashes](https://redis.io/docs/latest/develop/data-types/hashes) for job metadata with\n    [`EXPIRE`](https://redis.io/docs/latest/commands/expire) so completed jobs are cleaned up automatically.\n-   [Pub/Sub](https://redis.io/docs/latest/develop/pubsub)\n    ([`PUBLISH`](https://redis.io/docs/latest/commands/publish),\n    [`SUBSCRIBE`](https://redis.io/docs/latest/commands/subscribe)) for job completion signalling so the\n    submitter is notified without polling.\n-   Sub-millisecond latency on enqueue and dequeue, which keeps the producer side cheap."},{"id":"ecosystem","title":"Ecosystem","role":"content","text":"The following libraries implement reliable job-queue patterns on Redis:\n\n-   **Python**: [Celery](https://docs.celeryq.dev/),\n    [RQ](https://python-rq.org/),\n    [Dramatiq](https://dramatiq.io/)\n-   **Ruby**: [Sidekiq](https://sidekiq.org/),\n    [Resque](https://github.com/resque/resque)\n-   **Node.js**: [Bull](https://github.com/OptimalBits/bull),\n    [BullMQ](https://docs.bullmq.io/)\n-   **Go**: [Asynq](https://github.com/hibiken/asynq)\n-   **Java**: [Redisson](https://redisson.org/),\n    [Spring Batch](https://spring.io/projects/spring-batch) (Redis job repository)\n-   **.NET**: [Hangfire](https://www.hangfire.io/) (Redis storage)"},{"id":"code-examples-to-build-your-own-redis-job-queue","title":"Code examples to build your own Redis job queue","role":"example","text":"The following guides show how to build a simple Redis-backed job queue.\nEach guide includes a runnable interactive demo for each of the following client libraries:\n\n* [redis-py (Python)](https://redis.io/docs/latest/develop/use-cases/job-queue/redis-py)\n* [node-redis (Node.js)](https://redis.io/docs/latest/develop/use-cases/job-queue/nodejs)\n* [go-redis (Go)](https://redis.io/docs/latest/develop/use-cases/job-queue/go)\n* [Jedis (Java)](https://redis.io/docs/latest/develop/use-cases/job-queue/java-jedis)\n* [Lettuce (Java)](https://redis.io/docs/latest/develop/use-cases/job-queue/java-lettuce)\n* [StackExchange.Redis (C#)](https://redis.io/docs/latest/develop/use-cases/job-queue/dotnet)\n* [Predis (PHP)](https://redis.io/docs/latest/develop/use-cases/job-queue/php)\n* [redis-rb (Ruby)](https://redis.io/docs/latest/develop/use-cases/job-queue/ruby)\n* [redis-rs (Rust)](https://redis.io/docs/latest/develop/use-cases/job-queue/rust)"}],"examples":[]}
{"schema_version":2,"id":"develop/use-cases/job-queue/java-jedis","title":"Redis job queue with Jedis","url":"https://redis.io/docs/latest/develop/use-cases/job-queue/java-jedis/","summary":"Implement a Redis job queue in Java with Jedis","aliases":["/develop/use-cases/job-queue/java","/develop/use-cases/job-queue/jedis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:50:30+01:00","children":[],"page_type":"content","content_hash":"dd904072e222cf1142d6d873c0fafef1540be6a2c8634141461dc423e48c2cf6","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis-backed job queue in Java with [`Jedis`](https://redis.io/docs/latest/develop/clients/jedis). It includes a small local web server built with Java's built-in `HttpServer` so you can enqueue jobs, watch a pool of workers drain them, and see the reclaimer recover jobs from a simulated worker crash."},{"id":"overview","title":"Overview","role":"overview","text":"A job queue lets your application offload background work — sending email, processing payments, image transcoding, ML inference, webhooks — from the request path. Producers enqueue jobs in milliseconds and return to the user; workers pull from the queue and process them on their own schedule.\n\nThat gives you:\n\n* Low-latency user-facing requests, even when downstream work is slow or bursty\n* Horizontal scale across many worker processes that share one Redis instance\n* At-least-once delivery so a worker crash doesn't lose work\n* Visibility-timeout reclaim that returns stuck jobs to the queue automatically\n* Job metadata, retry counts, and completion results in Redis hashes with TTL\n\nIn this example, each job is identified by a random hex ID and its payload, status, and result live in a Redis hash under `queue:jobs-jedis:job:{id}`. Pending IDs sit in a list, claimed IDs move atomically to a *processing* list, and completed or failed IDs land in short history lists."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. The application calls `queue.enqueue(payload)`\n2. The helper writes the job metadata hash and `LPUSH`es the job ID onto the pending list\n3. A worker calls `queue.claim(timeoutMs)`\n4. The helper runs `BRPOPLPUSH` to atomically move the next pending ID into the processing list and writes a per-claim `claim_token` plus `claimed_at_ms` on the hash\n5. The worker runs the job and calls `queue.complete(job, result)` or `queue.fail(job, error)`\n6. `complete` removes the job from the processing list, writes the result, and `LPUSH`es the ID onto the completed history (with `LTRIM` and an `EXPIRE` on the hash for cleanup)\n7. `fail` either retries the job (back to pending) or moves it to the failed list once retries are exhausted\n\nIf a worker dies before completing a job, the job sits in the processing list with a `claimed_at_ms` older than the visibility timeout. A periodic call to `queue.reclaimStuck()` finds those jobs and moves them back to pending so another worker can pick them up.\n\nEvery state change holds the token: a worker that has been reclaimed cannot later complete or fail a job another worker has already claimed."},{"id":"the-redisjobqueue-helper","title":"The `RedisJobQueue` helper","role":"content","text":"The `RedisJobQueue` class wraps the queue operations ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/job-queue/java-jedis/RedisJobQueue.java)):\n\n[code example]\n\nJedis operations are synchronous. The helper acquires a `Jedis` connection per call using `pool.getResource()` inside a try-with-resources block, so connections are returned to the pool even on errors. The blocking `claim()` method holds its own connection for the duration of the `BRPOPLPUSH` wait, which is fine because every other call uses a different connection."},{"id":"data-model","title":"Data model","role":"content","text":"Each job's state lives in a Redis hash plus a position in one of four lists:\n\n[code example]\n\nA job's hash carries:\n\n[code example]\n\nThe implementation uses:\n\n* [`LPUSH`](https://redis.io/docs/latest/commands/lpush) to add new job IDs to the pending list\n* [`BRPOPLPUSH`](https://redis.io/docs/latest/commands/brpoplpush) to atomically claim a job into the processing list\n* [`LREM`](https://redis.io/docs/latest/commands/lrem) to remove a claimed job from the processing list on complete or fail\n* [`LTRIM`](https://redis.io/docs/latest/commands/ltrim) to cap the completed and failed history lists\n* [`HSET`](https://redis.io/docs/latest/commands/hset) / [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) for job metadata\n* [`EXPIRE`](https://redis.io/docs/latest/commands/expire) on completed and failed hashes for automatic cleanup\n* [`PUBLISH`](https://redis.io/docs/latest/commands/publish) on `queue:jobs-jedis:events` for completion signalling\n* [Lua scripting](https://redis.io/docs/latest/develop/programmability/eval-intro) ([`EVAL`](https://redis.io/docs/latest/commands/eval)) for the complete, fail, and reclaim flows so each runs atomically against the processing list and metadata hash"},{"id":"enqueueing-jobs","title":"Enqueueing jobs","role":"content","text":"`enqueue()` writes the metadata hash and pushes the job ID onto the pending list in one pipeline:\n\n[code example]\n\nThe payload is stored as a JSON string so the queue can carry arbitrary nested structures without forcing every field into a hash."},{"id":"claiming-jobs-with-brpoplpush","title":"Claiming jobs with BRPOPLPUSH","role":"content","text":"A worker blocks until a job is available, then atomically pops it from the pending list and pushes it onto the processing list. `BRPOPLPUSH` does both in a single Redis call:\n\n[code example]\n\nThe `claim_token` is the worker's proof of ownership for this attempt. Every subsequent state change (complete, fail) checks it before touching the processing list, so a worker that hung past the visibility timeout cannot interfere with the new claimant."},{"id":"completing-jobs","title":"Completing jobs","role":"content","text":"`complete()` runs a Lua script via `EVAL` so the processing-list removal, the metadata write, and the history push happen atomically:\n\n[code example]\n\nThe Lua script checks the token first and returns `0` if the worker no longer owns the job (because the reclaimer moved it back to pending). The metadata hash also gets an `EXPIRE` so completed jobs are cleaned up automatically."},{"id":"failing-and-retrying","title":"Failing and retrying","role":"content","text":"`fail()` either retries the job (back to pending) or moves it to the failed list once retries are exhausted:\n\n[code example]\n\nThe attempt counter is incremented on every `claim()`, so a job that fails three times is moved to the failed list with `attempts = 3` and the final `last_error` preserved."},{"id":"reclaiming-stuck-jobs","title":"Reclaiming stuck jobs","role":"content","text":"If a worker dies mid-job — the process is killed, the host loses power, the network partitions — the job sits in the processing list with a `claimed_at_ms` that never advances. A periodic call to `reclaimStuck()` walks the processing list and moves any job past the visibility timeout back to pending:\n\n[code example]\n\nThe Lua script also handles a narrower race: a worker that crashed between `BRPOPLPUSH` and writing `claimed_at_ms`. Those jobs are reclaimed after `2 × visibility_ms` using `enqueued_at_ms` as a fallback timer, so they aren't stranded forever."},{"id":"stats-and-history","title":"Stats and history","role":"content","text":"`stats()` reports queue depth plus per-process counters:\n\n[code example]\n\nThe completed and failed lists are capped via `LTRIM` so they never grow unbounded; a real deployment would also write completion events to a separate audit log if needed."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before running the demo, make sure that:\n\n* Redis 6.2 or later is running locally on the default port (6379). Earlier versions still work, since the helper uses commands that have existed since Redis 2.6.\n* Java 17 or later (the demo uses text-block-free string concatenation but still relies on a modern JDK).\n* Jedis 5.x is on the classpath. The smallest workable classpath is the Jedis jar plus its two transitive dependencies, `commons-pool2` and `slf4j-api`.\n\nIf you use Maven:\n\n[code example]\n\nIf you use Gradle:\n\n[code example]"},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of five Java source files. Download them from the [`java-jedis` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/job-queue/java-jedis) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"A local demo server is included to show the queue in action ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/job-queue/java-jedis/DemoServer.java)). Compile and run with `javac` and `java`, listing each jar on the classpath:\n\n[code example]\n\nYou should see:\n\n[code example]\n\nOpen [http://127.0.0.1:8793](http://127.0.0.1:8793) in a browser. You can:\n\n* Enqueue jobs of different kinds (email, webhook, thumbnail, invoice) in batches.\n* Start a pool of workers with configurable size, work latency, and *failure* / *hang* rates. A non-zero hang rate simulates worker crashes.\n* Click **Run reclaim sweep** to move any timed-out processing jobs back to pending.\n* Watch pending / processing / completed / failed lists update every 800 ms.\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`. You can also tune the visibility timeout with `--visibility-ms`."},{"id":"the-mock-worker-pool","title":"The mock worker pool","role":"content","text":"The demo includes a small `JobWorker` ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/job-queue/java-jedis/JobWorker.java)) and `WorkerPool` ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/job-queue/java-jedis/WorkerPool.java)) that stand in for whatever real background work your application would run. Each worker:\n\n* Blocks on `queue.claim()` for new jobs.\n* Sleeps `workLatencyMs` to simulate doing the work.\n* Either completes successfully, fails (calling `queue.fail()`), or *hangs* — returning without completing or failing the job so the reclaimer has to recover it.\n\nWorkers run on daemon threads spawned by the pool; an `AtomicBoolean` stop flag lets the HTTP handlers shut workers down between requests. The `failRate` and `hangRate` knobs let you watch the at-least-once delivery and reclaim behaviours from the UI without writing test code."},{"id":"production-usage","title":"Production usage","role":"content","text":""},{"id":"choose-a-visibility-timeout-that-matches-your-worst-case-job-latency","title":"Choose a visibility timeout that matches your worst-case job latency","role":"content","text":"The visibility timeout has to exceed the longest real job time, with margin. If it's too short, a healthy worker that's running a slow job will get its work duplicated when the reclaimer fires. If it's too long, a real crash takes longer to detect. Most production deployments use a per-queue value tuned to the 99th-percentile job latency — for example, 2 minutes for email and 30 minutes for video transcoding."},{"id":"run-the-reclaimer-on-a-schedule","title":"Run the reclaimer on a schedule","role":"content","text":"The demo only reclaims when you click the button. In production, run `reclaimStuck()` from a `ScheduledExecutorService` (every few seconds for fast queues, every minute for slow ones), or from each worker before it blocks on `claim()`. Both patterns work as long as *someone* runs the sweep."},{"id":"size-jedispool-for-your-worker-count","title":"Size `JedisPool` for your worker count","role":"content","text":"`JedisPool` is thread-safe and connections are released back to the pool when the try-with-resources block exits. The demo bumps `maxTotal` to 32 to support the blocking `BRPOPLPUSH` call held by each worker plus the per-request connections used by the HTTP handlers. As a rule of thumb, `maxTotal` should be at least *workers + concurrent HTTP request threads + reclaimer threads + headroom*."},{"id":"use-a-separate-redis-database-or-key-prefix-per-queue","title":"Use a separate Redis database or key prefix per queue","role":"content","text":"The helper takes a `queueName` argument so you can run multiple independent queues against one Redis instance — for example, one queue per priority level, or one per job kind. Keep queue keys under a clearly-namespaced prefix (here, `queue:jobs-jedis:*`) so they're easy to inspect and easy to clear without touching application data."},{"id":"cap-the-completed-and-failed-history","title":"Cap the completed and failed history","role":"content","text":"The demo keeps the last 50 completed and 50 failed job IDs via `LTRIM`. If you need longer history for audit purposes, write completion events to a separate Redis Stream (or to an external store) and keep the in-queue history short. Stream consumer groups give you the same fan-out semantics with a much richer history."},{"id":"tune-maxattempts-per-job-kind","title":"Tune `maxAttempts` per job kind","role":"content","text":"A blanket `maxAttempts = 3` is a reasonable default for transient failures (network timeouts, rate limits). Jobs that talk to non-idempotent external systems — for example, posting a Stripe charge — need either application-level idempotency keys or a much lower retry count. The helper exposes `maxAttempts` so each queue can pick its own policy."},{"id":"inspect-queue-state-directly-in-redis","title":"Inspect queue state directly in Redis","role":"content","text":"Because the queue is just lists and hashes, you can inspect it with `redis-cli`:\n\n[code example]"},{"id":"learn-more","title":"Learn more","role":"related","text":"This example uses the following Redis commands:\n\n* [`LPUSH`](https://redis.io/docs/latest/commands/lpush) to enqueue a job ID.\n* [`BRPOPLPUSH`](https://redis.io/docs/latest/commands/brpoplpush) to atomically claim a job into the processing list.\n* [`LREM`](https://redis.io/docs/latest/commands/lrem) to remove a job from the processing list on complete or fail.\n* [`LRANGE`](https://redis.io/docs/latest/commands/lrange) and [`LLEN`](https://redis.io/docs/latest/commands/llen) to read queue depth and list contents.\n* [`LTRIM`](https://redis.io/docs/latest/commands/ltrim) to cap the completed and failed history.\n* [`HSET`](https://redis.io/docs/latest/commands/hset) and [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) for job metadata.\n* [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby) for the attempt counter.\n* [`EXPIRE`](https://redis.io/docs/latest/commands/expire) for automatic cleanup of completed and failed jobs.\n* [`PUBLISH`](https://redis.io/docs/latest/commands/publish) for job-completion notifications.\n* [`EVAL`](https://redis.io/docs/latest/commands/eval) for atomic complete, fail, and reclaim flows.\n\nSee the [Jedis documentation](https://redis.io/docs/latest/develop/clients/jedis) for full client reference."}],"examples":[{"id":"the-redisjobqueue-helper-ex0","language":"java","code":"import java.util.Map;\nimport redis.clients.jedis.JedisPool;\n\npublic class Main {\n    public static void main(String[] args) {\n        JedisPool pool = new JedisPool(\"localhost\", 6379);\n        RedisJobQueue queue = new RedisJobQueue(pool, \"jobs-jedis\", 5000, 300, 50, 3);\n\n        String jobId = queue.enqueue(Map.of(\n                \"kind\", \"email\",\n                \"recipient\", \"alice@example.com\"\n        ));\n\n        // In a worker thread:\n        RedisJobQueue.ClaimedJob job = queue.claim(1000);\n        if (job != null) {\n            try {\n                // ... run the job ...\n                queue.complete(job, Map.of(\"sent_at\", \"2026-05-11T15:00:00Z\"));\n            } catch (Exception exc) {\n                queue.fail(job, exc.getMessage());\n            }\n        }\n\n        // In a periodic sweeper:\n        java.util.List<String> reclaimed = queue.reclaimStuck();\n    }\n}","section_id":"the-redisjobqueue-helper"},{"id":"data-model-ex0","language":"text","code":"queue:jobs-jedis:pending          (list)   pending job IDs, oldest at the right\nqueue:jobs-jedis:processing       (list)   claimed but not yet completed\nqueue:jobs-jedis:completed        (list)   recent successes (LTRIM-capped history)\nqueue:jobs-jedis:failed           (list)   terminally failed jobs\nqueue:jobs-jedis:job:{id}         (hash)   per-job metadata\nqueue:jobs-jedis:events           (pubsub) completion notifications","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"queue:jobs-jedis:job:9a4f...\n  id              = 9a4f...\n  payload         = {\"kind\":\"email\",\"recipient\":\"alice@example.com\"}\n  status          = pending | processing | completed | failed\n  attempts        = 1\n  enqueued_at_ms  = 1715441000000\n  claimed_at_ms   = 1715441000123\n  claim_token     = b3c0d1e2...        (per-claim random token)\n  completed_at_ms = 1715441000456\n  result          = {\"sent_at\":\"...\"}\n  last_error      = \"smtp timeout\"","section_id":"data-model"},{"id":"enqueueing-jobs-ex0","language":"java","code":"public String enqueue(Map<String, Object> payload) {\n    String jobId = randomTokenHex(8);\n    long now = System.currentTimeMillis();\n    Map<String, String> meta = new LinkedHashMap<>();\n    meta.put(\"id\", jobId);\n    meta.put(\"payload\", JsonUtil.toJson(payload));\n    meta.put(\"status\", \"pending\");\n    meta.put(\"attempts\", \"0\");\n    meta.put(\"enqueued_at_ms\", Long.toString(now));\n    meta.put(\"claim_token\", \"\");\n\n    try (Jedis jedis = pool.getResource()) {\n        Pipeline pipe = jedis.pipelined();\n        pipe.hset(metaKey(jobId), meta);\n        pipe.lpush(pendingKey, jobId);\n        pipe.sync();\n    }\n    return jobId;\n}","section_id":"enqueueing-jobs"},{"id":"claiming-jobs-with-brpoplpush-ex0","language":"java","code":"public ClaimedJob claim(long timeoutMs) {\n    double timeoutSec = Math.max(timeoutMs / 1000.0, 0.1);\n    String jobId;\n    try (Jedis jedis = pool.getResource()) {\n        jobId = jedis.brpoplpush(pendingKey, processingKey, (int) Math.ceil(timeoutSec));\n    }\n    if (jobId == null) {\n        return null;\n    }\n\n    String token = randomTokenHex(8);\n    long now = System.currentTimeMillis();\n    String mk = metaKey(jobId);\n    Map<String, String> meta;\n    try (Jedis jedis = pool.getResource()) {\n        Pipeline pipe = jedis.pipelined();\n        Map<String, String> updates = new LinkedHashMap<>();\n        updates.put(\"status\", \"processing\");\n        updates.put(\"claimed_at_ms\", Long.toString(now));\n        updates.put(\"claim_token\", token);\n        pipe.hset(mk, updates);\n        pipe.hincrBy(mk, \"attempts\", 1);\n        Response<Map<String, String>> resp = pipe.hgetAll(mk);\n        pipe.sync();\n        meta = resp.get();\n    }\n    // ... parse payload, attempts, and return a ClaimedJob ...\n}","section_id":"claiming-jobs-with-brpoplpush"},{"id":"completing-jobs-ex0","language":"java","code":"public boolean complete(ClaimedJob job, Map<String, Object> result) {\n    List<String> keys = Arrays.asList(metaPrefix, processingKey, completedKey);\n    List<String> args = Arrays.asList(\n            job.id,\n            job.claimToken,\n            \"completed\",\n            Long.toString(System.currentTimeMillis()),\n            JsonUtil.toJson(result),\n            Integer.toString(completedTtl),\n            Integer.toString(completedHistory)\n    );\n    Object res;\n    try (Jedis jedis = pool.getResource()) {\n        res = jedis.eval(COMPLETE_SCRIPT, keys, args);\n    }\n    if (res == null || !\"1\".equals(res.toString())) {\n        return false;\n    }\n    // ... publish the completion event ...\n    return true;\n}","section_id":"completing-jobs"},{"id":"failing-and-retrying-ex0","language":"java","code":"public boolean fail(ClaimedJob job, String error) {\n    boolean retry = job.attempts < maxAttempts;\n    List<String> keys = Arrays.asList(metaPrefix, processingKey, pendingKey, failedKey);\n    List<String> args = Arrays.asList(\n            job.id,\n            job.claimToken,\n            error,\n            Long.toString(System.currentTimeMillis()),\n            Integer.toString(completedTtl),\n            Integer.toString(completedHistory),\n            retry ? \"1\" : \"0\"\n    );\n    Object res;\n    try (Jedis jedis = pool.getResource()) {\n        res = jedis.eval(FAIL_SCRIPT, keys, args);\n    }\n    return res != null && !\"0\".equals(res.toString());\n}","section_id":"failing-and-retrying"},{"id":"reclaiming-stuck-jobs-ex0","language":"java","code":"public List<String> reclaimStuck() {\n    List<String> keys = Arrays.asList(pendingKey, processingKey, metaPrefix);\n    List<String> args = Arrays.asList(\n            Long.toString(System.currentTimeMillis()),\n            Long.toString(visibilityMs)\n    );\n    Object res;\n    try (Jedis jedis = pool.getResource()) {\n        res = jedis.eval(RECLAIM_SCRIPT, keys, args);\n    }\n    // ... unwrap the list of reclaimed IDs ...\n}","section_id":"reclaiming-stuck-jobs"},{"id":"stats-and-history-ex0","language":"java","code":"public Map<String, Object> stats() {\n    long pending, processing, completed, failed;\n    try (Jedis jedis = pool.getResource()) {\n        Pipeline pipe = jedis.pipelined();\n        Response<Long> pendingResp = pipe.llen(pendingKey);\n        Response<Long> processingResp = pipe.llen(processingKey);\n        Response<Long> completedResp = pipe.llen(completedKey);\n        Response<Long> failedResp = pipe.llen(failedKey);\n        pipe.sync();\n        pending = pendingResp.get();\n        processing = processingResp.get();\n        completed = completedResp.get();\n        failed = failedResp.get();\n    }\n    Map<String, Object> out = new LinkedHashMap<>();\n    out.put(\"enqueued_total\", enqueuedTotal);\n    out.put(\"completed_total\", completedTotal);\n    out.put(\"failed_total\", failedTotal);\n    out.put(\"reclaimed_total\", reclaimedTotal);\n    out.put(\"pending_depth\", pending);\n    out.put(\"processing_depth\", processing);\n    out.put(\"completed_depth\", completed);\n    out.put(\"failed_depth\", failed);\n    out.put(\"visibility_ms\", visibilityMs);\n    return out;\n}","section_id":"stats-and-history"},{"id":"prerequisites-ex0","language":"xml","code":"<dependency>\n    <groupId>redis.clients</groupId>\n    <artifactId>jedis</artifactId>\n    <version>5.0.1</version>\n</dependency>","section_id":"prerequisites"},{"id":"prerequisites-ex1","language":"groovy","code":"implementation 'redis.clients:jedis:5.0.1'","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir job-queue-demo && cd job-queue-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/job-queue/java-jedis\ncurl -O $BASE/RedisJobQueue.java\ncurl -O $BASE/JobWorker.java\ncurl -O $BASE/WorkerPool.java\ncurl -O $BASE/DemoServer.java\ncurl -O $BASE/JsonUtil.java","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"javac -cp jedis-5.0.1.jar:commons-pool2-2.12.1.jar:slf4j-api-2.0.12.jar \\\n      JsonUtil.java RedisJobQueue.java JobWorker.java WorkerPool.java DemoServer.java\n\njava  -cp .:jedis-5.0.1.jar:commons-pool2-2.12.1.jar:slf4j-api-2.0.12.jar \\\n      DemoServer --port 8793 --visibility-ms 5000","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"Redis job-queue demo server listening on http://127.0.0.1:8793\nUsing Redis at localhost:6379\nVisibility timeout: 5000 ms","section_id":"start-the-demo-server"},{"id":"inspect-queue-state-directly-in-redis-ex0","language":"bash","code":"# How many pending jobs?\nredis-cli LLEN queue:jobs-jedis:pending\n\n# Look at the next 5 jobs to be picked up.\nredis-cli LRANGE queue:jobs-jedis:pending -5 -1\n\n# Read a job's metadata.\nredis-cli HGETALL queue:jobs-jedis:job:9a4f0d1c\n\n# How many jobs are currently being processed?\nredis-cli LLEN queue:jobs-jedis:processing\n\n# Clear everything for this queue (be careful — this deletes work).\nredis-cli --scan --pattern 'queue:jobs-jedis:*' | xargs redis-cli DEL","section_id":"inspect-queue-state-directly-in-redis"}]}
{"schema_version":2,"id":"develop/use-cases/job-queue/java-lettuce","title":"Redis job queue with Lettuce","url":"https://redis.io/docs/latest/develop/use-cases/job-queue/java-lettuce/","summary":"Implement a Redis job queue in Java with Lettuce","aliases":["/develop/use-cases/job-queue/lettuce"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:50:30+01:00","children":[],"page_type":"content","content_hash":"c28757d2f407795dec9143eb45ca5e7f3a5b35b83c03b198ccde94f461d9b794","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis-backed job queue in Java with [`Lettuce`](https://redis.io/docs/latest/develop/clients/lettuce). It includes a small local web server built on Java's standard `com.sun.net.httpserver.HttpServer` so you can enqueue jobs, watch a pool of workers drain them, and see the reclaimer recover jobs from a simulated worker crash."},{"id":"overview","title":"Overview","role":"overview","text":"A job queue lets your application offload background work — sending email, processing payments, image transcoding, ML inference, webhooks — from the request path. Producers enqueue jobs in milliseconds and return to the user; workers pull from the queue and process them on their own schedule.\n\nThat gives you:\n\n* Low-latency user-facing requests, even when downstream work is slow or bursty\n* Horizontal scale across many worker processes that share one Redis instance\n* At-least-once delivery so a worker crash doesn't lose work\n* Visibility-timeout reclaim that returns stuck jobs to the queue automatically\n* Job metadata, retry counts, and completion results in Redis hashes with TTL\n\nIn this example, each job is identified by a random hex ID and its payload, status, and result live in a Redis hash under `queue:jobs:job:{id}`. Pending IDs sit in a list, claimed IDs move atomically to a *processing* list, and completed or failed IDs land in short history lists."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. The application calls `queue.enqueue(payload)`\n2. The helper runs a single Lua script that writes the job metadata hash and `LPUSH`es the job ID onto the pending list\n3. A worker calls `queue.claim(timeoutMs)`\n4. The helper runs `BLMOVE` to atomically move the next pending ID into the processing list and writes a per-claim `claim_token` plus `claimed_at_ms` on the hash\n5. The worker runs the job and calls `queue.complete(job, result)` or `queue.fail(job, error)`\n6. `complete` removes the job from the processing list, writes the result, and `LPUSH`es the ID onto the completed history (with `LTRIM` and an `EXPIRE` on the hash for cleanup)\n7. `fail` either retries the job (back to pending) or moves it to the failed list once retries are exhausted\n\nIf a worker dies before completing a job, the job sits in the processing list with a `claimed_at_ms` older than the visibility timeout. A periodic call to `queue.reclaimStuck()` finds those jobs and moves them back to pending so another worker can pick them up.\n\nEvery state change holds the token: a worker that has been reclaimed cannot later complete or fail a job another worker has already claimed."},{"id":"the-job-queue-helper","title":"The job queue helper","role":"content","text":"The `RedisJobQueue` class wraps the queue operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/job-queue/java-lettuce/RedisJobQueue.java)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each job's state lives in a Redis hash plus a position in one of four lists:\n\n[code example]\n\nA job's hash carries:\n\n[code example]\n\nThe implementation uses:\n\n* [`LPUSH`](https://redis.io/docs/latest/commands/lpush) to add new job IDs to the pending list\n* [`BLMOVE`](https://redis.io/docs/latest/commands/blmove) to atomically claim a job into the processing list\n* [`LREM`](https://redis.io/docs/latest/commands/lrem) to remove a claimed job from the processing list on complete or fail\n* [`LTRIM`](https://redis.io/docs/latest/commands/ltrim) to cap the completed and failed history lists\n* [`HSET`](https://redis.io/docs/latest/commands/hset) / [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) for job metadata\n* [`EXPIRE`](https://redis.io/docs/latest/commands/expire) on completed and failed hashes for automatic cleanup\n* [`PUBLISH`](https://redis.io/docs/latest/commands/publish) on `queue:jobs:events` for completion signalling\n* [Lua scripting](https://redis.io/docs/latest/develop/programmability/eval-intro) ([`EVAL`](https://redis.io/docs/latest/commands/eval)) for the enqueue, complete, fail, and reclaim flows so each runs atomically against the processing list and metadata hash"},{"id":"why-we-use-lua-for-enqueue-too","title":"Why we use Lua for enqueue too","role":"content","text":"The redis-py reference implementation enqueues a job with a two-command pipeline (`HSET` + `LPUSH`). On a single Lettuce `StatefulRedisConnection` shared across HTTP handler threads, command pipelines from different threads can interleave their responses on the wire. That isn't a correctness problem for two unrelated commands, but it does make it harder to reason about the visible queue state under concurrency.\n\nThis port wraps the enqueue's two commands in a single Lua script so the metadata hash write and the pending-list push are atomic on the server. That keeps the demo lock-free without needing to serialise enqueue calls behind a `ReentrantLock`, and it matches the pattern already used for `complete`, `fail`, and `reclaim`.\n\nFor production code, prefer a connection pool over a shared connection: see [Production usage](#production-usage) below."},{"id":"enqueueing-jobs","title":"Enqueueing jobs","role":"content","text":"`enqueue()` runs a Lua script that writes the metadata hash and pushes the job ID onto the pending list in one round trip:\n\n[code example]\n\nThe payload is stored as JSON so the queue can carry arbitrary nested structures without forcing every field into a hash."},{"id":"claiming-jobs-with-blmove","title":"Claiming jobs with BLMOVE","role":"content","text":"A worker blocks until a job is available, then atomically pops it from the pending list and pushes it onto the processing list. `BLMOVE` does both in a single Redis call. (`BRPOPLPUSH` is deprecated in Redis 6.2+; `BLMOVE` with `rightLeft()` is the modern replacement.)\n\n[code example]\n\nThe `claim_token` is the worker's proof of ownership for this attempt. Every subsequent state change (complete, fail) checks it before touching the processing list, so a worker that hung past the visibility timeout cannot interfere with the new claimant."},{"id":"completing-jobs","title":"Completing jobs","role":"content","text":"`complete()` runs a Lua script so the processing-list removal, the metadata write, and the history push happen atomically:\n\n[code example]\n\nThe Lua script checks the token first and returns `0` if the worker no longer owns the job (because the reclaimer moved it back to pending). The metadata hash also gets an `EXPIRE` so completed jobs are cleaned up automatically."},{"id":"failing-and-retrying","title":"Failing and retrying","role":"content","text":"`fail()` either retries the job (back to pending) or moves it to the failed list once retries are exhausted:\n\n[code example]\n\nThe attempt counter is incremented on every `claim()`, so a job that fails three times is moved to the failed list with `attempts = 3` and the final `last_error` preserved."},{"id":"reclaiming-stuck-jobs","title":"Reclaiming stuck jobs","role":"content","text":"If a worker dies mid-job — the process is killed, the host loses power, the network partitions — the job sits in the processing list with a `claimed_at_ms` that never advances. A periodic call to `reclaimStuck()` walks the processing list and moves any job past the visibility timeout back to pending:\n\n[code example]\n\nThe Lua script also handles a narrower race: a worker that crashed between `BLMOVE` and writing `claimed_at_ms`. Those jobs are reclaimed after `2 × visibility_ms` using `enqueued_at_ms` as a fallback timer, so they aren't stranded forever."},{"id":"stats-and-history","title":"Stats and history","role":"content","text":"`stats()` reports queue depth plus per-process counters:\n\n[code example]\n\nThe completed and failed lists are capped via `LTRIM` so they never grow unbounded; a real deployment would also write completion events to a longer-term audit log if needed."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* Redis 6.2 or later running locally on the default port (6379). `BLMOVE` was added in 6.2; on earlier versions, swap it for `BRPOPLPUSH` in `claim()`.\n* JDK 17 or later.\n* Lettuce 6.1+ and its runtime dependencies (`netty-*`, `reactor-core`, `reactive-streams`).\n\nAdd Lettuce to your project:\n\n* If you use **Maven**:\n\n  [code example]\n\n* If you use **Gradle**:\n\n  [code example]"},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of four Java source files. Download them from the [`java-lettuce` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/job-queue/java-lettuce) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From that directory, compile the sources and run the server. With the Lettuce + netty + reactor jars staged in a local `lib/` directory:\n\n[code example]\n\nYou should see:\n\n[code example]\n\nOpen [http://127.0.0.1:8794](http://127.0.0.1:8794) in a browser. You can:\n\n* Enqueue jobs of different kinds (email, webhook, thumbnail, invoice) in batches.\n* Start a pool of workers with configurable size, work latency, and *failure* / *hang* rates. A non-zero hang rate simulates worker crashes.\n* Click **Run reclaim sweep** to move any timed-out processing jobs back to pending.\n* Watch pending / processing / completed / failed lists update every 800 ms.\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`. You can also tune the visibility timeout with `--visibility-ms` and pick an alternate Redis key prefix with `--queue-name`."},{"id":"the-mock-worker-pool","title":"The mock worker pool","role":"content","text":"The demo includes a small `JobWorker` and `WorkerPool` ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/job-queue/java-lettuce/JobWorker.java), [WorkerPool.java](https://github.com/redis/docs/blob/main/content/develop/use-cases/job-queue/java-lettuce/WorkerPool.java)) that stands in for whatever real background work your application would run. Each worker:\n\n* Blocks on `queue.claim()` for new jobs.\n* Sleeps `workLatencyMs` to simulate doing the work.\n* Either completes successfully, fails (calling `queue.fail()`), or *hangs* — returning without completing or failing the job so the reclaimer has to recover it.\n\nThe `failRate` and `hangRate` knobs let you watch the at-least-once delivery and reclaim behaviours from the UI without writing test code."},{"id":"production-usage","title":"Production usage","role":"content","text":""},{"id":"use-a-connection-pool-not-a-shared-connection","title":"Use a connection pool, not a shared connection","role":"content","text":"The demo shares a single `StatefulRedisConnection` across HTTP handlers and worker threads to keep the code compact. That has two consequences worth knowing about:\n\n* The `claim()` call uses `BLMOVE`, which blocks the shared connection for up to the claim timeout. With many workers sharing one connection, claim throughput is serialised. The demo uses a 500ms timeout so the connection stays responsive to other commands, but a real deployment will want each worker to own a connection.\n* Lettuce transactions (`MULTI`/`EXEC`) are connection-scoped, so any code that uses them would also need to serialise behind a `ReentrantLock`. This port avoids `MULTI`/`EXEC` entirely — the multi-command operations (`enqueue`, `complete`, `fail`, `reclaim`) all run as Lua scripts.\n\nIn production, use `ConnectionPoolSupport.createGenericObjectPool(redisClient::connect, poolConfig)` and acquire a connection per worker (and per request handler if you want fully concurrent pipelines):\n\n[code example]"},{"id":"choose-a-visibility-timeout-that-matches-your-worst-case-job-latency","title":"Choose a visibility timeout that matches your worst-case job latency","role":"content","text":"The visibility timeout has to exceed the longest real job time, with margin. If it's too short, a healthy worker that's running a slow job will get its work duplicated when the reclaimer fires. If it's too long, a real crash takes longer to detect. Most production deployments use a per-queue value tuned to the 99th-percentile job latency — for example, 2 minutes for email and 30 minutes for video transcoding."},{"id":"run-the-reclaimer-on-a-schedule","title":"Run the reclaimer on a schedule","role":"content","text":"The demo only reclaims when you click the button. In production, run `reclaimStuck()` from a `ScheduledExecutorService` (every few seconds for fast queues, every minute for slow ones), or from each worker before it blocks on `claim()`. Both patterns work as long as *someone* runs the sweep."},{"id":"use-a-separate-redis-database-or-key-prefix-per-queue","title":"Use a separate Redis database or key prefix per queue","role":"content","text":"The helper takes a `queueName` argument so you can run multiple independent queues against one Redis instance — for example, one queue per priority level, or one per job kind. Keep queue keys under a clearly-namespaced prefix (here, `queue:jobs:*`) so they're easy to inspect and easy to clear without touching application data."},{"id":"cap-the-completed-and-failed-history","title":"Cap the completed and failed history","role":"content","text":"The demo keeps the last 50 completed and 50 failed job IDs via `LTRIM`. If you need longer history for audit purposes, write completion events to a separate Redis Stream (or to an external store) and keep the in-queue history short. Stream consumer groups give you the same fan-out semantics with a much richer history."},{"id":"tune-maxattempts-per-job-kind","title":"Tune `maxAttempts` per job kind","role":"content","text":"A blanket `maxAttempts = 3` is a reasonable default for transient failures (network timeouts, rate limits). Jobs that talk to non-idempotent external systems — for example, posting a Stripe charge — need either application-level idempotency keys or a much lower retry count. The helper exposes `maxAttempts` so each queue can pick its own policy."},{"id":"inspect-queue-state-directly-in-redis","title":"Inspect queue state directly in Redis","role":"content","text":"Because the queue is just lists and hashes, you can inspect it with `redis-cli`:\n\n[code example]"},{"id":"learn-more","title":"Learn more","role":"related","text":"This example uses the following Redis commands:\n\n* [`LPUSH`](https://redis.io/docs/latest/commands/lpush) to enqueue a job ID.\n* [`BLMOVE`](https://redis.io/docs/latest/commands/blmove) to atomically claim a job into the processing list.\n* [`LREM`](https://redis.io/docs/latest/commands/lrem) to remove a job from the processing list on complete or fail.\n* [`LRANGE`](https://redis.io/docs/latest/commands/lrange) and [`LLEN`](https://redis.io/docs/latest/commands/llen) to read queue depth and list contents.\n* [`LTRIM`](https://redis.io/docs/latest/commands/ltrim) to cap the completed and failed history.\n* [`HSET`](https://redis.io/docs/latest/commands/hset) and [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) for job metadata.\n* [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby) for the attempt counter.\n* [`EXPIRE`](https://redis.io/docs/latest/commands/expire) for automatic cleanup of completed and failed jobs.\n* [`PUBLISH`](https://redis.io/docs/latest/commands/publish) for job-completion notifications.\n* [`EVAL`](https://redis.io/docs/latest/commands/eval) for atomic enqueue, complete, fail, and reclaim flows.\n\nSee the [Lettuce documentation](https://redis.io/docs/latest/develop/clients/lettuce) for full client reference."}],"examples":[{"id":"the-job-queue-helper-ex0","language":"java","code":"import io.lettuce.core.RedisClient;\nimport io.lettuce.core.api.StatefulRedisConnection;\nimport java.util.Map;\n\nRedisClient client = RedisClient.create(\"redis://localhost:6379\");\nStatefulRedisConnection<String, String> conn = client.connect();\n\nRedisJobQueue queue = new RedisJobQueue(conn, \"jobs\", 5000, 300, 50, 3);\n\nString jobId = queue.enqueue(Map.of(\n        \"kind\", \"email\",\n        \"recipient\", \"alice@example.com\"));\n\n// In a worker thread:\nRedisJobQueue.ClaimedJob job = queue.claim(1000);\nif (job != null) {\n    try {\n        // ... run the job ...\n        queue.complete(job, Map.of(\"sent_at\", \"2026-05-11T15:00:00Z\"));\n    } catch (Exception ex) {\n        queue.fail(job, ex.getMessage());\n    }\n}\n\n// In a periodic sweeper:\nList<String> reclaimed = queue.reclaimStuck();","section_id":"the-job-queue-helper"},{"id":"data-model-ex0","language":"text","code":"queue:jobs:pending          (list)   pending job IDs, oldest at the right\nqueue:jobs:processing       (list)   claimed but not yet completed\nqueue:jobs:completed        (list)   recent successes (LTRIM-capped history)\nqueue:jobs:failed           (list)   terminally failed jobs\nqueue:jobs:job:{id}         (hash)   per-job metadata\nqueue:jobs:events           (pubsub) completion notifications","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"queue:jobs:job:9a4f...\n  id              = 9a4f...\n  payload         = {\"kind\":\"email\",\"recipient\":\"alice@example.com\"}\n  status          = pending | processing | completed | failed\n  attempts        = 1\n  enqueued_at_ms  = 1715441000000\n  claimed_at_ms   = 1715441000123\n  claim_token     = b3c0d1e2...        (per-claim random token)\n  completed_at_ms = 1715441000456\n  result          = {\"sent_at\":\"...\"}\n  last_error      = \"smtp timeout\"","section_id":"data-model"},{"id":"enqueueing-jobs-ex0","language":"java","code":"public String enqueue(Map<String, Object> payload) {\n    String jobId = randomHex(8);\n    long now = System.currentTimeMillis();\n    String payloadJson = JsonUtil.toJson(payload);\n\n    String[] keys = { metaKey(jobId), pendingKey };\n    String[] argv = { jobId, payloadJson, Long.toString(now) };\n\n    conn.sync().eval(ENQUEUE_SCRIPT, ScriptOutputType.INTEGER, keys, argv);\n    return jobId;\n}","section_id":"enqueueing-jobs"},{"id":"claiming-jobs-with-blmove-ex0","language":"java","code":"public ClaimedJob claim(long timeoutMs) {\n    double timeoutSeconds = Math.max(timeoutMs / 1000.0, 0.1);\n    RedisCommands<String, String> sync = conn.sync();\n    String jobId = sync.blmove(pendingKey, processingKey,\n            LMoveArgs.Builder.rightLeft(), timeoutSeconds);\n    if (jobId == null) {\n        return null;\n    }\n\n    String token = randomHex(8);\n    long now = System.currentTimeMillis();\n    String meta = metaKey(jobId);\n\n    Map<String, String> updates = new LinkedHashMap<>();\n    updates.put(\"status\", \"processing\");\n    updates.put(\"claimed_at_ms\", Long.toString(now));\n    updates.put(\"claim_token\", token);\n    sync.hset(meta, updates);\n    sync.hincrby(meta, \"attempts\", 1);\n    Map<String, String> hash = sync.hgetall(meta);\n\n    Map<String, Object> payload = JsonUtil.parseObject(hash.getOrDefault(\"payload\", \"{}\"));\n    int attempts = Integer.parseInt(hash.getOrDefault(\"attempts\", \"1\"));\n    return new ClaimedJob(jobId, payload, attempts, token);\n}","section_id":"claiming-jobs-with-blmove"},{"id":"completing-jobs-ex0","language":"java","code":"public boolean complete(ClaimedJob job, Map<String, Object> result) {\n    String[] keys = { metaPrefix, processingKey, completedKey };\n    String[] argv = {\n            job.id,\n            job.claimToken,\n            \"completed\",\n            Long.toString(System.currentTimeMillis()),\n            JsonUtil.toJson(result),\n            Integer.toString(completedTtl),\n            Integer.toString(completedHistory),\n    };\n    Long ok = conn.sync().eval(COMPLETE_SCRIPT, ScriptOutputType.INTEGER, keys, argv);\n    if (ok == null || ok == 0L) {\n        return false;\n    }\n    publishEvent(job.id, \"completed\");\n    return true;\n}","section_id":"completing-jobs"},{"id":"failing-and-retrying-ex0","language":"java","code":"public boolean fail(ClaimedJob job, String error) {\n    boolean retry = job.attempts < maxAttempts;\n    String[] keys = { metaPrefix, processingKey, pendingKey, failedKey };\n    String[] argv = {\n            job.id, job.claimToken, error,\n            Long.toString(System.currentTimeMillis()),\n            Integer.toString(completedTtl),\n            Integer.toString(completedHistory),\n            retry ? \"1\" : \"0\",\n    };\n    Long result = conn.sync().eval(FAIL_SCRIPT, ScriptOutputType.INTEGER, keys, argv);\n    return result != null && result != 0L;\n}","section_id":"failing-and-retrying"},{"id":"reclaiming-stuck-jobs-ex0","language":"java","code":"public List<String> reclaimStuck() {\n    String[] keys = { pendingKey, processingKey, metaPrefix };\n    String[] argv = {\n            Long.toString(System.currentTimeMillis()),\n            Long.toString(visibilityMs),\n    };\n    List<Object> raw = conn.sync().eval(RECLAIM_SCRIPT, ScriptOutputType.MULTI, keys, argv);\n    List<String> reclaimed = new ArrayList<>();\n    if (raw != null) {\n        for (Object item : raw) {\n            if (item != null) reclaimed.add(item.toString());\n        }\n    }\n    return reclaimed;\n}","section_id":"reclaiming-stuck-jobs"},{"id":"stats-and-history-ex0","language":"java","code":"public Map<String, Object> stats() {\n    RedisCommands<String, String> sync = conn.sync();\n    long pending = sync.llen(pendingKey);\n    long processing = sync.llen(processingKey);\n    long completed = sync.llen(completedKey);\n    long failed = sync.llen(failedKey);\n    Map<String, Object> out = new LinkedHashMap<>();\n    out.put(\"enqueued_total\", enqueuedTotal);\n    out.put(\"completed_total\", completedTotal);\n    out.put(\"failed_total\", failedTotal);\n    out.put(\"reclaimed_total\", reclaimedTotal);\n    out.put(\"pending_depth\", pending);\n    out.put(\"processing_depth\", processing);\n    out.put(\"completed_depth\", completed);\n    out.put(\"failed_depth\", failed);\n    out.put(\"visibility_ms\", visibilityMs);\n    return out;\n}","section_id":"stats-and-history"},{"id":"prerequisites-ex0","language":"xml","code":"<dependency>\n      <groupId>io.lettuce</groupId>\n      <artifactId>lettuce-core</artifactId>\n      <version>6.7.1.RELEASE</version>\n  </dependency>","section_id":"prerequisites"},{"id":"prerequisites-ex1","language":"groovy","code":"implementation 'io.lettuce:lettuce-core:6.7.1.RELEASE'","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir job-queue-demo && cd job-queue-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/job-queue/java-lettuce\ncurl -O $BASE/RedisJobQueue.java\ncurl -O $BASE/JobWorker.java\ncurl -O $BASE/WorkerPool.java\ncurl -O $BASE/DemoServer.java","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"javac -cp \"lib/*\" -d build RedisJobQueue.java JobWorker.java WorkerPool.java DemoServer.java\njava -cp \"build:lib/*\" DemoServer --port 8794 --visibility-ms 5000","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"Redis job-queue demo server listening on http://127.0.0.1:8794\nUsing Redis at localhost:6379\nVisibility timeout: 5000 ms","section_id":"start-the-demo-server"},{"id":"use-a-connection-pool-not-a-shared-connection-ex0","language":"java","code":"GenericObjectPoolConfig<StatefulRedisConnection<String, String>> config = new GenericObjectPoolConfig<>();\nconfig.setMaxTotal(32);\nGenericObjectPool<StatefulRedisConnection<String, String>> pool =\n        ConnectionPoolSupport.createGenericObjectPool(client::connect, config);\n\ntry (StatefulRedisConnection<String, String> conn = pool.borrowObject()) {\n    new RedisJobQueue(conn, \"jobs\", 5000, 300, 50, 3).enqueue(payload);\n}","section_id":"use-a-connection-pool-not-a-shared-connection"},{"id":"inspect-queue-state-directly-in-redis-ex0","language":"bash","code":"# How many pending jobs?\nredis-cli LLEN queue:jobs:pending\n\n# Look at the next 5 jobs to be picked up.\nredis-cli LRANGE queue:jobs:pending -5 -1\n\n# Read a job's metadata.\nredis-cli HGETALL queue:jobs:job:9a4f0d1c\n\n# How many jobs are currently being processed?\nredis-cli LLEN queue:jobs:processing\n\n# Clear everything for this queue (be careful — this deletes work).\nredis-cli --scan --pattern 'queue:jobs:*' | xargs redis-cli DEL","section_id":"inspect-queue-state-directly-in-redis"}]}
{"schema_version":2,"id":"develop/use-cases/job-queue/nodejs","title":"Redis job queue with node-redis","url":"https://redis.io/docs/latest/develop/use-cases/job-queue/nodejs/","summary":"Implement a Redis job queue in Node.js with node-redis","aliases":["/develop/use-cases/job-queue/node-redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:50:30+01:00","children":[],"page_type":"content","content_hash":"8b744a981dee524c320f8c202e41918629e61b91343e6544d8e83157c2c86794","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis-backed job queue in Node.js with [`node-redis`](https://redis.io/docs/latest/develop/clients/nodejs). It includes a small local web server built with Node's standard `http` module so you can enqueue jobs, watch a pool of workers drain them, and see the reclaimer recover jobs from a simulated worker crash."},{"id":"overview","title":"Overview","role":"overview","text":"A job queue lets your application offload background work — sending email, processing payments, image transcoding, ML inference, webhooks — from the request path. Producers enqueue jobs in milliseconds and return to the user; workers pull from the queue and process them on their own schedule.\n\nThat gives you:\n\n* Low-latency user-facing requests, even when downstream work is slow or bursty\n* Horizontal scale across many worker processes that share one Redis instance\n* At-least-once delivery so a worker crash doesn't lose work\n* Visibility-timeout reclaim that returns stuck jobs to the queue automatically\n* Job metadata, retry counts, and completion results in Redis hashes with TTL\n\nIn this example, each job is identified by a random hex ID and its payload, status, and result live in a Redis hash under `queue:jobs:job:{id}`. Pending IDs sit in a list, claimed IDs move atomically to a *processing* list, and completed or failed IDs land in short history lists."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. The application calls `queue.enqueue(payload)`\n2. The helper writes the job metadata hash and `LPUSH`es the job ID onto the pending list\n3. A worker calls `queue.claim(timeoutMs)`\n4. The helper runs `BLMOVE` (the successor to `BRPOPLPUSH`) to atomically move the next pending ID into the processing list and writes a per-claim `claim_token` plus `claimed_at_ms` on the hash\n5. The worker runs the job and calls `queue.complete(job, result)` or `queue.fail(job, error)`\n6. `complete` removes the job from the processing list, writes the result, and `LPUSH`es the ID onto the completed history (with `LTRIM` and an `EXPIRE` on the hash for cleanup)\n7. `fail` either retries the job (back to pending) or moves it to the failed list once retries are exhausted\n\nIf a worker dies before completing a job, the job sits in the processing list with a `claimed_at_ms` older than the visibility timeout. A periodic call to `queue.reclaimStuck()` finds those jobs and moves them back to pending so another worker can pick them up.\n\nEvery state change holds the token: a worker that has been reclaimed cannot later complete or fail a job another worker has already claimed."},{"id":"the-job-queue-helper","title":"The job queue helper","role":"content","text":"The `RedisJobQueue` class wraps the queue operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/job-queue/nodejs/job_queue.js)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each job's state lives in a Redis hash plus a position in one of four lists:\n\n[code example]\n\nA job's hash carries:\n\n[code example]\n\nThe implementation uses:\n\n* [`LPUSH`](https://redis.io/docs/latest/commands/lpush) to add new job IDs to the pending list\n* [`BLMOVE`](https://redis.io/docs/latest/commands/blmove) to atomically claim a job into the processing list (the modern replacement for the deprecated [`BRPOPLPUSH`](https://redis.io/docs/latest/commands/brpoplpush))\n* [`LREM`](https://redis.io/docs/latest/commands/lrem) to remove a claimed job from the processing list on complete or fail\n* [`LTRIM`](https://redis.io/docs/latest/commands/ltrim) to cap the completed and failed history lists\n* [`HSET`](https://redis.io/docs/latest/commands/hset) / [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) for job metadata\n* [`EXPIRE`](https://redis.io/docs/latest/commands/expire) on completed and failed hashes for automatic cleanup\n* [`PUBLISH`](https://redis.io/docs/latest/commands/publish) on `queue:jobs:events` for completion signalling\n* [Lua scripting](https://redis.io/docs/latest/develop/programmability/eval-intro) ([`EVALSHA`](https://redis.io/docs/latest/commands/evalsha)) for the complete, fail, and reclaim flows so each runs atomically against the processing list and metadata hash"},{"id":"enqueueing-jobs","title":"Enqueueing jobs","role":"content","text":"`enqueue()` writes the metadata hash and pushes the job ID onto the pending list in one transaction (`MULTI` pipeline):\n\n[code example]\n\nThe payload is stored as JSON so the queue can carry arbitrary nested structures without forcing every field into a hash. Hash field values are all strings — `node-redis` won't convert numbers for you, so the helper coerces them explicitly."},{"id":"claiming-jobs-with-blmove","title":"Claiming jobs with BLMOVE","role":"content","text":"A worker blocks until a job is available, then atomically pops it from the pending list and pushes it onto the processing list. In Redis 6.2 and later this is `BLMOVE`; the older `BRPOPLPUSH` is deprecated and was removed from `node-redis` in v5:\n\n[code example]\n\n`BLMOVE pending processing RIGHT LEFT timeoutS` is the byte-for-byte equivalent of the old `BRPOPLPUSH pending processing timeoutS` — pop from the right end of pending, push onto the left end of processing, atomically, blocking up to `timeoutS` seconds.\n\nThe `claim_token` is the worker's proof of ownership for this attempt. Every subsequent state change (complete, fail) checks it before touching the processing list, so a worker that hung past the visibility timeout cannot interfere with the new claimant."},{"id":"completing-jobs","title":"Completing jobs","role":"content","text":"`complete()` runs a Lua script via `EVALSHA` so the processing-list removal, the metadata write, and the history push happen atomically:\n\n[code example]\n\nThe helper preloads each script with `SCRIPT LOAD` at first use and prefers `EVALSHA` to avoid resending the script body on every call. If Redis evicts the script cache and returns `NOSCRIPT`, the wrapper falls back to a full `EVAL` and re-caches the SHA.\n\nThe Lua script checks the token first and returns `0` if the worker no longer owns the job (because the reclaimer moved it back to pending). The metadata hash also gets an `EXPIRE` so completed jobs are cleaned up automatically."},{"id":"failing-and-retrying","title":"Failing and retrying","role":"content","text":"`fail()` either retries the job (back to pending) or moves it to the failed list once retries are exhausted:\n\n[code example]\n\nThe attempt counter is incremented on every `claim()`, so a job that fails three times is moved to the failed list with `attempts = 3` and the final `last_error` preserved."},{"id":"reclaiming-stuck-jobs","title":"Reclaiming stuck jobs","role":"content","text":"If a worker dies mid-job — the process is killed, the host loses power, the network partitions — the job sits in the processing list with a `claimed_at_ms` that never advances. A periodic call to `reclaimStuck()` walks the processing list and moves any job past the visibility timeout back to pending:\n\n[code example]\n\nThe Lua script also handles a narrower race: a worker that crashed between `BLMOVE` and writing `claimed_at_ms`. Those jobs are reclaimed after `2 × visibility_ms` using `enqueued_at_ms` as a fallback timer, so they aren't stranded forever."},{"id":"stats-and-history","title":"Stats and history","role":"content","text":"`stats()` reports queue depth plus per-process counters:\n\n[code example]\n\nThe completed and failed lists are capped via `LTRIM` so they never grow unbounded; a real deployment would also write completion events to a longer-term audit log if needed."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* Redis 6.2 or later running locally on the default port (6379). Earlier versions still work for the rest of the pattern, but `BLMOVE` requires Redis 6.2+; on older servers swap it for `BRPOPLPUSH`.\n* Node.js 18 or later (the helper uses native `async`/`await` and the `crypto` module).\n* The `node-redis` client at version 5.x. Install it with:\n\n  [code example]"},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of four files. Download them from the [`nodejs` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/job-queue/nodejs) on GitHub, or grab them with `curl`:\n\n[code example]\n\nThen install dependencies:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From that directory:\n\n[code example]\n\nYou should see:\n\n[code example]\n\nOpen [http://127.0.0.1:8791](http://127.0.0.1:8791) in a browser. You can:\n\n* Enqueue jobs of different kinds (email, webhook, thumbnail, invoice) in batches.\n* Start a pool of workers with configurable size, work latency, and *failure* / *hang* rates. A non-zero hang rate simulates worker crashes.\n* Click **Run reclaim sweep** to move any timed-out processing jobs back to pending.\n* Watch pending / processing / completed / failed lists update every 800 ms.\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`. You can also tune the visibility timeout with `--visibility-ms` and the bind address with `--host` / `--port`."},{"id":"the-mock-worker-pool","title":"The mock worker pool","role":"content","text":"The demo includes a small `Worker` and `WorkerPool` ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/job-queue/nodejs/worker.js)) that stands in for whatever real background work your application would run. Each worker:\n\n* Blocks on `queue.claim()` for new jobs.\n* Sleeps `workLatencyMs` to simulate doing the work.\n* Either completes successfully, fails (calling `queue.fail()`), or *hangs* — returning without completing or failing the job so the reclaimer has to recover it.\n\nThe `failRate` and `hangRate` knobs let you watch the at-least-once delivery and reclaim behaviours from the UI without writing test code.\n\nBecause Node.js is single-threaded, each \"worker\" is an async loop running on the event loop rather than an OS thread. The `claim(500)` call uses Redis's blocking `BLMOVE` so the loop spends almost all its time waiting on the server rather than spinning; multiple workers share one Redis client and Node's I/O scheduler interleaves their claims naturally."},{"id":"production-usage","title":"Production usage","role":"content","text":""},{"id":"choose-a-visibility-timeout-that-matches-your-worst-case-job-latency","title":"Choose a visibility timeout that matches your worst-case job latency","role":"content","text":"The visibility timeout has to exceed the longest real job time, with margin. If it's too short, a healthy worker that's running a slow job will get its work duplicated when the reclaimer fires. If it's too long, a real crash takes longer to detect. Most production deployments use a per-queue value tuned to the 99th-percentile job latency — for example, 2 minutes for email and 30 minutes for video transcoding."},{"id":"run-the-reclaimer-on-a-schedule","title":"Run the reclaimer on a schedule","role":"content","text":"The demo only reclaims when you click the button. In production, run `reclaimStuck()` from a periodic task (use `setInterval` for fast queues, or a separate scheduler process for slow ones), or from each worker before it blocks on `claim()`. Both patterns work as long as *someone* runs the sweep."},{"id":"use-a-separate-redis-database-or-key-prefix-per-queue","title":"Use a separate Redis database or key prefix per queue","role":"content","text":"The helper takes a `queueName` argument so you can run multiple independent queues against one Redis instance — for example, one queue per priority level, or one per job kind. Keep queue keys under a clearly-namespaced prefix (here, `queue:jobs:*`) so they're easy to inspect and easy to clear without touching application data."},{"id":"cap-the-completed-and-failed-history","title":"Cap the completed and failed history","role":"content","text":"The demo keeps the last 50 completed and 50 failed job IDs via `LTRIM`. If you need longer history for audit purposes, write completion events to a separate Redis Stream (or to an external store) and keep the in-queue history short. Stream consumer groups give you the same fan-out semantics with a much richer history."},{"id":"tune-maxattempts-per-job-kind","title":"Tune `maxAttempts` per job kind","role":"content","text":"A blanket `maxAttempts = 3` is a reasonable default for transient failures (network timeouts, rate limits). Jobs that talk to non-idempotent external systems — for example, posting a Stripe charge — need either application-level idempotency keys or a much lower retry count. The helper exposes `maxAttempts` so each queue can pick its own policy."},{"id":"prefer-blmove-over-brpoplpush","title":"Prefer `BLMOVE` over `BRPOPLPUSH`","role":"content","text":"`node-redis` v5 removed `client.brPopLPush()`; use `client.bLMove(src, dst, \"RIGHT\", \"LEFT\", timeoutSec)` instead. The two commands are functionally identical for this pattern: pop from the right of the source list and push onto the left of the destination list, atomically, blocking until a value appears. `BLMOVE` is more general (it accepts any combination of `LEFT`/`RIGHT` on either end) and is the recommended modern command."},{"id":"use-one-shared-client-not-a-pool","title":"Use one shared client, not a pool","role":"content","text":"`node-redis` pipelines commands automatically across a single TCP connection, so for most workloads you should create one `createClient()` instance per process and reuse it everywhere. The only reason to add a second connection is to dedicate one to a blocking call (such as `BLMOVE` with a long timeout, or `SUBSCRIBE`); this demo's `claim()` uses a short 500 ms timeout so a single client is fine."},{"id":"inspect-queue-state-directly-in-redis","title":"Inspect queue state directly in Redis","role":"content","text":"Because the queue is just lists and hashes, you can inspect it with `redis-cli`:\n\n[code example]"},{"id":"learn-more","title":"Learn more","role":"related","text":"This example uses the following Redis commands:\n\n* [`LPUSH`](https://redis.io/docs/latest/commands/lpush) to enqueue a job ID.\n* [`BLMOVE`](https://redis.io/docs/latest/commands/blmove) to atomically claim a job into the processing list (modern replacement for [`BRPOPLPUSH`](https://redis.io/docs/latest/commands/brpoplpush)).\n* [`LREM`](https://redis.io/docs/latest/commands/lrem) to remove a job from the processing list on complete or fail.\n* [`LRANGE`](https://redis.io/docs/latest/commands/lrange) and [`LLEN`](https://redis.io/docs/latest/commands/llen) to read queue depth and list contents.\n* [`LTRIM`](https://redis.io/docs/latest/commands/ltrim) to cap the completed and failed history.\n* [`HSET`](https://redis.io/docs/latest/commands/hset) and [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) for job metadata.\n* [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby) for the attempt counter.\n* [`EXPIRE`](https://redis.io/docs/latest/commands/expire) for automatic cleanup of completed and failed jobs.\n* [`PUBLISH`](https://redis.io/docs/latest/commands/publish) for job-completion notifications.\n* [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) for atomic complete, fail, and reclaim flows.\n\nSee the [`node-redis` documentation](https://redis.io/docs/latest/develop/clients/nodejs) for full client reference."}],"examples":[{"id":"the-job-queue-helper-ex0","language":"javascript","code":"const { createClient } = require(\"redis\");\nconst { RedisJobQueue } = require(\"./job_queue\");\n\nasync function main() {\n  const client = createClient({ url: \"redis://localhost:6379\" });\n  await client.connect();\n\n  const queue = new RedisJobQueue({ redisClient: client, visibilityMs: 5000 });\n\n  const jobId = await queue.enqueue({ kind: \"email\", recipient: \"alice@example.com\" });\n\n  // In a worker process:\n  const job = await queue.claim(1000);\n  if (job !== null) {\n    try {\n      // ... run the job ...\n      await queue.complete(job, { sent_at: \"2026-05-11T15:00:00Z\" });\n    } catch (err) {\n      await queue.fail(job, String(err));\n    }\n  }\n\n  // In a periodic sweeper:\n  const reclaimed = await queue.reclaimStuck();\n\n  await client.quit();\n}\n\nmain().catch(console.error);","section_id":"the-job-queue-helper"},{"id":"data-model-ex0","language":"text","code":"queue:jobs:pending          (list)   pending job IDs, oldest at the right\nqueue:jobs:processing       (list)   claimed but not yet completed\nqueue:jobs:completed        (list)   recent successes (LTRIM-capped history)\nqueue:jobs:failed           (list)   terminally failed jobs\nqueue:jobs:job:{id}         (hash)   per-job metadata\nqueue:jobs:events           (pubsub) completion notifications","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"queue:jobs:job:9a4f...\n  id              = 9a4f...\n  payload         = {\"kind\":\"email\",\"recipient\":\"alice@example.com\"}\n  status          = pending | processing | completed | failed\n  attempts        = 1\n  enqueued_at_ms  = 1715441000000\n  claimed_at_ms   = 1715441000123\n  claim_token     = b3c0d1e2...        (per-claim random token)\n  completed_at_ms = 1715441000456\n  result          = {\"sent_at\":\"...\"}\n  last_error      = \"smtp timeout\"","section_id":"data-model"},{"id":"enqueueing-jobs-ex0","language":"javascript","code":"async enqueue(payload) {\n  const jobId = crypto.randomBytes(8).toString(\"hex\");\n  const nowMs = Date.now();\n  const meta = {\n    id: jobId,\n    payload: JSON.stringify(payload),\n    status: \"pending\",\n    attempts: \"0\",\n    enqueued_at_ms: String(nowMs),\n    claim_token: \"\",\n  };\n  const multi = this.redis.multi();\n  multi.hSet(this._metaKey(jobId), meta);\n  multi.lPush(this.pendingKey, jobId);\n  await multi.exec();\n  this._enqueued += 1;\n  return jobId;\n}","section_id":"enqueueing-jobs"},{"id":"claiming-jobs-with-blmove-ex0","language":"javascript","code":"async claim(timeoutMs = 1000) {\n  const timeoutS = Math.max(timeoutMs / 1000, 0.1);\n  const jobId = await this.redis.blMove(\n    this.pendingKey,\n    this.processingKey,\n    \"RIGHT\",\n    \"LEFT\",\n    timeoutS,\n  );\n  if (jobId === null || jobId === undefined) {\n    return null;\n  }\n\n  const token = crypto.randomBytes(8).toString(\"hex\");\n  const nowMs = Date.now();\n  const metaKey = this._metaKey(jobId);\n  const multi = this.redis.multi();\n  multi.hSet(metaKey, {\n    status: \"processing\",\n    claimed_at_ms: String(nowMs),\n    claim_token: token,\n  });\n  multi.hIncrBy(metaKey, \"attempts\", 1);\n  multi.hGetAll(metaKey);\n  const results = await multi.exec();\n  const meta = results[2] || {};\n  return new ClaimedJob(jobId, JSON.parse(meta.payload || \"{}\"), Number(meta.attempts), token);\n}","section_id":"claiming-jobs-with-blmove"},{"id":"completing-jobs-ex0","language":"javascript","code":"async complete(job, result) {\n  const ok = await this._evalScript(COMPLETE_SCRIPT, this._completeSha, {\n    keys: [this.metaPrefix, this.processingKey, this.completedKey],\n    arguments: [\n      job.id,\n      job.claimToken,\n      \"completed\",\n      String(Date.now()),\n      JSON.stringify(result),\n      String(this.completedTtl),\n      String(this.completedHistory),\n    ],\n  });\n  if (!ok || Number(ok) === 0) {\n    return false;\n  }\n  await this.redis.publish(\n    this.eventsChannel,\n    JSON.stringify({ id: job.id, status: \"completed\" }),\n  );\n  this._completed += 1;\n  return true;\n}","section_id":"completing-jobs"},{"id":"failing-and-retrying-ex0","language":"javascript","code":"async fail(job, error) {\n  const retry = job.attempts < this.maxAttempts;\n  const result = await this._evalScript(FAIL_SCRIPT, this._failSha, {\n    keys: [this.metaPrefix, this.processingKey, this.pendingKey, this.failedKey],\n    arguments: [\n      job.id,\n      job.claimToken,\n      error,\n      String(Date.now()),\n      String(this.completedTtl),\n      String(this.completedHistory),\n      retry ? \"1\" : \"0\",\n    ],\n  });\n  return Boolean(result) && Number(result) !== 0;\n}","section_id":"failing-and-retrying"},{"id":"reclaiming-stuck-jobs-ex0","language":"javascript","code":"async reclaimStuck() {\n  const reclaimed = await this._evalScript(RECLAIM_SCRIPT, this._reclaimSha, {\n    keys: [this.pendingKey, this.processingKey, this.metaPrefix],\n    arguments: [String(Date.now()), String(this.visibilityMs)],\n  });\n  return Array.isArray(reclaimed) ? reclaimed : [];\n}","section_id":"reclaiming-stuck-jobs"},{"id":"stats-and-history-ex0","language":"javascript","code":"async stats() {\n  const multi = this.redis.multi();\n  multi.lLen(this.pendingKey);\n  multi.lLen(this.processingKey);\n  multi.lLen(this.completedKey);\n  multi.lLen(this.failedKey);\n  const [pending, processing, completed, failed] = await multi.exec();\n  return {\n    enqueued_total: this._enqueued,\n    completed_total: this._completed,\n    failed_total: this._failed,\n    reclaimed_total: this._reclaimed,\n    pending_depth: Number(pending) || 0,\n    processing_depth: Number(processing) || 0,\n    completed_depth: Number(completed) || 0,\n    failed_depth: Number(failed) || 0,\n    visibility_ms: this.visibilityMs,\n  };\n}","section_id":"stats-and-history"},{"id":"prerequisites-ex0","language":"bash","code":"npm install redis","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir job-queue-demo && cd job-queue-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/job-queue/nodejs\ncurl -O $BASE/job_queue.js\ncurl -O $BASE/worker.js\ncurl -O $BASE/demoServer.js\ncurl -O $BASE/package.json","section_id":"get-the-source-files"},{"id":"get-the-source-files-ex1","language":"bash","code":"npm install","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"node demoServer.js","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"Redis job-queue demo server listening on http://127.0.0.1:8791\nUsing Redis at localhost:6379\nVisibility timeout: 5000 ms","section_id":"start-the-demo-server"},{"id":"inspect-queue-state-directly-in-redis-ex0","language":"bash","code":"# How many pending jobs?\nredis-cli LLEN queue:jobs:pending\n\n# Look at the next 5 jobs to be picked up.\nredis-cli LRANGE queue:jobs:pending -5 -1\n\n# Read a job's metadata.\nredis-cli HGETALL queue:jobs:job:9a4f0d1c\n\n# How many jobs are currently being processed?\nredis-cli LLEN queue:jobs:processing\n\n# Clear everything for this queue (be careful — this deletes work).\nredis-cli --scan --pattern 'queue:jobs:*' | xargs redis-cli DEL","section_id":"inspect-queue-state-directly-in-redis"}]}
{"schema_version":2,"id":"develop/use-cases/job-queue/php","title":"Redis job queue with Predis","url":"https://redis.io/docs/latest/develop/use-cases/job-queue/php/","summary":"Implement a Redis job queue in PHP with Predis","aliases":["/develop/use-cases/job-queue/predis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:50:30+01:00","children":[],"page_type":"content","content_hash":"909e284f0a7af80660c6c97a9023fa8bab07f01bc1ed40259a344eecea39b2b1","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis-backed job queue in PHP with [Predis](https://github.com/predis/predis). It includes a small local web server built on PHP's built-in dev server so you can enqueue jobs, watch a pool of workers drain them, and see the reclaimer recover jobs from a simulated worker crash."},{"id":"overview","title":"Overview","role":"overview","text":"A job queue lets your application offload background work — sending email, processing payments, image transcoding, ML inference, webhooks — from the request path. Producers enqueue jobs in milliseconds and return to the user; workers pull from the queue and process them on their own schedule.\n\nThat gives you:\n\n* Low-latency user-facing requests, even when downstream work is slow or bursty\n* Horizontal scale across many worker processes that share one Redis instance\n* At-least-once delivery so a worker crash doesn't lose work\n* Visibility-timeout reclaim that returns stuck jobs to the queue automatically\n* Job metadata, retry counts, and completion results in Redis hashes with TTL\n\nIn this example, each job is identified by a random hex ID and its payload, status, and result live in a Redis hash under `queue:jobs:job:{id}`. Pending IDs sit in a list, claimed IDs move atomically to a *processing* list, and completed or failed IDs land in short history lists."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. The application calls `$queue->enqueue($payload)`\n2. The helper writes the job metadata hash and `LPUSH`es the job ID onto the pending list\n3. A worker calls `$queue->claim($timeoutMs)`\n4. The helper runs `BRPOPLPUSH` to atomically move the next pending ID into the processing list and writes a per-claim `claim_token` plus `claimed_at_ms` on the hash\n5. The worker runs the job and calls `$queue->complete($job, $result)` or `$queue->fail($job, $error)`\n6. `complete` removes the job from the processing list, writes the result, and `LPUSH`es the ID onto the completed history (with `LTRIM` and an `EXPIRE` on the hash for cleanup)\n7. `fail` either retries the job (back to pending) or moves it to the failed list once retries are exhausted\n\nIf a worker dies before completing a job, the job sits in the processing list with a `claimed_at_ms` older than the visibility timeout. A periodic call to `$queue->reclaimStuck()` finds those jobs and moves them back to pending so another worker can pick them up.\n\nEvery state change holds the token: a worker that has been reclaimed cannot later complete or fail a job another worker has already claimed."},{"id":"the-job-queue-helper","title":"The job queue helper","role":"content","text":"The `JobQueue` class wraps the queue operations ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/job-queue/php/JobQueue.php)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each job's state lives in a Redis hash plus a position in one of four lists:\n\n[code example]\n\nA job's hash carries:\n\n[code example]\n\nBecause PHP's built-in dev server runs each HTTP request in a fresh process, per-process counters (`enqueued_total`, `completed_total`, etc.) can't live in object properties — they wouldn't survive between requests. Instead the helper stores them in a Redis hash under `demo:queue_stats:{queueName}`, incremented with `HINCRBY` on each state change.\n\nThe implementation uses:\n\n* [`LPUSH`](https://redis.io/docs/latest/commands/lpush) to add new job IDs to the pending list\n* [`BRPOPLPUSH`](https://redis.io/docs/latest/commands/brpoplpush) to atomically claim a job into the processing list\n* [`LREM`](https://redis.io/docs/latest/commands/lrem) to remove a claimed job from the processing list on complete or fail\n* [`LTRIM`](https://redis.io/docs/latest/commands/ltrim) to cap the completed and failed history lists\n* [`HSET`](https://redis.io/docs/latest/commands/hset) / [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) for job metadata\n* [`EXPIRE`](https://redis.io/docs/latest/commands/expire) on completed and failed hashes for automatic cleanup\n* [`PUBLISH`](https://redis.io/docs/latest/commands/publish) on `queue:jobs:events` for completion signalling\n* [Lua scripting](https://redis.io/docs/latest/develop/programmability/eval-intro) for the complete, fail, and reclaim flows so each runs atomically against the processing list and metadata hash"},{"id":"enqueueing-jobs","title":"Enqueueing jobs","role":"content","text":"`enqueue()` writes the metadata hash and pushes the job ID onto the pending list in one pipeline:\n\n[code example]\n\nThe payload is stored as JSON so the queue can carry arbitrary nested structures without forcing every field into a hash. The `flattenFields()` helper turns the associative `$meta` array into the variadic `field, value, field, value` argument list that Predis's `hset()` expects in Predis 3.x."},{"id":"claiming-jobs-with-brpoplpush","title":"Claiming jobs with BRPOPLPUSH","role":"content","text":"A worker blocks until a job is available, then atomically pops it from the pending list and pushes it onto the processing list. `BRPOPLPUSH` does both in a single Redis call:\n\n[code example]\n\nThe `claim_token` is the worker's proof of ownership for this attempt. Every subsequent state change (complete, fail) checks it before touching the processing list, so a worker that hung past the visibility timeout cannot interfere with the new claimant.\n\nPredis exposes `BRPOPLPUSH` directly and accepts a whole-second timeout; sub-second blocking would need either a custom command or a non-blocking poll loop."},{"id":"completing-jobs","title":"Completing jobs","role":"content","text":"`complete()` runs a Lua script via `EVAL` so the processing-list removal, the metadata write, and the history push happen atomically:\n\n[code example]\n\nThe Lua script checks the token first and returns `0` if the worker no longer owns the job (because the reclaimer moved it back to pending). The metadata hash also gets an `EXPIRE` so completed jobs are cleaned up automatically."},{"id":"failing-and-retrying","title":"Failing and retrying","role":"content","text":"`fail()` either retries the job (back to pending) or moves it to the failed list once retries are exhausted:\n\n[code example]\n\nThe attempt counter is incremented on every `claim()`, so a job that fails three times is moved to the failed list with `attempts = 3` and the final `last_error` preserved."},{"id":"reclaiming-stuck-jobs","title":"Reclaiming stuck jobs","role":"content","text":"If a worker dies mid-job — the process is killed, the host loses power, the network partitions — the job sits in the processing list with a `claimed_at_ms` that never advances. A periodic call to `reclaimStuck()` walks the processing list and moves any job past the visibility timeout back to pending:\n\n[code example]\n\nThe Lua script also handles a narrower race: a worker that crashed between `BRPOPLPUSH` and writing `claimed_at_ms`. Those jobs are reclaimed after `2 × visibility_ms` using `enqueued_at_ms` as a fallback timer, so they aren't stranded forever."},{"id":"stats-and-history","title":"Stats and history","role":"content","text":"`stats()` reports queue depth plus the cross-process counters:\n\n[code example]\n\nThe completed and failed lists are capped via `LTRIM` so they never grow unbounded; a real deployment would also write completion events to a separate Redis Stream or audit store if it needs longer history."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* Redis 6.2 or later running locally on the default port (6379). Earlier versions still work, since the helper uses commands that have existed since Redis 2.6.\n* PHP 8.1 or later, with the `posix` and `pcntl` extensions enabled (both ship with the official PHP binary on macOS and most Linux distros).\n* The Predis client (3.x). Install it with [Composer](https://getcomposer.org/):\n\n  [code example]"},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of six files. Download them from the [`php` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/job-queue/php) on GitHub, or grab them with `curl`:\n\n[code example]\n\nThen install dependencies:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From that directory:\n\n[code example]\n\nYou should see:\n\n[code example]\n\nOpen [http://127.0.0.1:8796](http://127.0.0.1:8796) in a browser. You can:\n\n* Enqueue jobs of different kinds (email, webhook, thumbnail, invoice) in batches.\n* Start a pool of workers with configurable size, work latency, and *failure* / *hang* rates. A non-zero hang rate simulates worker crashes.\n* Click **Run reclaim sweep** to move any timed-out processing jobs back to pending.\n* Watch pending / processing / completed / failed lists update every 800 ms.\n\nTo point the demo at a different Redis instance, set `REDIS_HOST`, `REDIS_PORT`, and `VISIBILITY_MS` before launching the server:\n\n[code example]"},{"id":"the-worker-process-and-supervisor","title":"The worker process and supervisor","role":"content","text":"The demo uses two files that together stand in for whatever real background work your application would run:\n\n* [`JobWorker.php`](https://github.com/redis/docs/blob/main/content/develop/use-cases/job-queue/php/JobWorker.php) — the `JobWorker` class. A worker calls `$queue->claim(500)`, sleeps `workLatencyMs` to simulate doing the work, then either completes the job, fails it, or *hangs* — returning without completing or failing the job so the reclaimer has to recover it.\n* [`worker.php`](https://github.com/redis/docs/blob/main/content/develop/use-cases/job-queue/php/worker.php) — a CLI entry point that constructs a `JobQueue` and a `JobWorker` from command-line flags, then calls `$worker->run()` until SIGTERM. Run one manually like this:\n\n  [code example]\n\nWhen the UI's **Start / apply** button is clicked, the demo server spawns one `worker.php` process per worker through the `WorkerSupervisor` ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/job-queue/php/WorkerSupervisor.php)). The supervisor:\n\n* Builds the worker command line with the requested size, latency, and failure / hang rates.\n* Launches each worker via `proc_open()` so it survives the `php -S` request that started it.\n* Records each PID under `demo:workers:pids` in Redis. The next HTTP request — running in a fresh PHP process — reads that hash to learn which workers are alive.\n* Sends `SIGTERM` via `posix_kill()` when the **Stop workers** button is clicked.\n\nThe `fail_rate` and `hang_rate` knobs let you watch the at-least-once delivery and reclaim behaviours from the UI without writing test code."},{"id":"production-usage","title":"Production usage","role":"content","text":""},{"id":"dont-try-to-host-workers-inside-the-web-server","title":"Don't try to host workers inside the web server","role":"content","text":"PHP's traditional one-process-per-request model — the same one `php -S` uses for this demo — means worker threads or in-process pools die with the request that started them. In production, run workers as **separate long-lived processes**:\n\n* A systemd unit (`Type=simple`, `Restart=always`) per worker.\n* A container per worker scaled by Kubernetes, ECS, or Nomad.\n* A supervisor like Supervisord or Horizon driving N copies of `worker.php`.\n\nWhichever way you ship workers, they connect to Redis directly and never depend on the web tier being up."},{"id":"choose-a-visibility-timeout-that-matches-your-worst-case-job-latency","title":"Choose a visibility timeout that matches your worst-case job latency","role":"content","text":"The visibility timeout has to exceed the longest real job time, with margin. If it's too short, a healthy worker that's running a slow job will get its work duplicated when the reclaimer fires. If it's too long, a real crash takes longer to detect. Most production deployments use a per-queue value tuned to the 99th-percentile job latency — for example, 2 minutes for email and 30 minutes for video transcoding."},{"id":"run-the-reclaimer-on-a-schedule","title":"Run the reclaimer on a schedule","role":"content","text":"The demo only reclaims when you click the button. In production, run `$queue->reclaimStuck()` from a periodic task (every few seconds for fast queues, every minute for slow ones), or from each worker before it blocks on `claim()`. Both patterns work as long as *someone* runs the sweep. A small `php -r '... while (true) { $queue->reclaimStuck(); sleep(5); }'` loop run under systemd is enough for most deployments."},{"id":"use-a-separate-redis-database-or-key-prefix-per-queue","title":"Use a separate Redis database or key prefix per queue","role":"content","text":"The helper takes a `$queueName` argument so you can run multiple independent queues against one Redis instance — for example, one queue per priority level, or one per job kind. Keep queue keys under a clearly-namespaced prefix (here, `queue:jobs:*`) so they're easy to inspect and easy to clear without touching application data."},{"id":"cap-the-completed-and-failed-history","title":"Cap the completed and failed history","role":"content","text":"The demo keeps the last 50 completed and 50 failed job IDs via `LTRIM`. If you need longer history for audit purposes, write completion events to a separate Redis Stream (or to an external store) and keep the in-queue history short. Stream consumer groups give you the same fan-out semantics with a much richer history."},{"id":"tune-maxattempts-per-job-kind","title":"Tune `maxAttempts` per job kind","role":"content","text":"A blanket `maxAttempts = 3` is a reasonable default for transient failures (network timeouts, rate limits). Jobs that talk to non-idempotent external systems — for example, posting a Stripe charge — need either application-level idempotency keys or a much lower retry count. The helper exposes `maxAttempts` so each queue can pick its own policy."},{"id":"use-a-persistent-predis-connection-per-worker","title":"Use a persistent Predis connection per worker","role":"content","text":"Predis opens a new TCP connection on first use and reuses it for the life of the `Client` object. Workers are long-lived, so this is already what you want. Don't construct a fresh `Predis\\Client` inside the `run()` loop — let the worker own a single connection and reuse it across thousands of `claim()` calls."},{"id":"inspect-queue-state-directly-in-redis","title":"Inspect queue state directly in Redis","role":"content","text":"Because the queue is just lists and hashes, you can inspect it with `redis-cli`:\n\n[code example]"},{"id":"learn-more","title":"Learn more","role":"related","text":"This example uses the following Redis commands:\n\n* [`LPUSH`](https://redis.io/docs/latest/commands/lpush) to enqueue a job ID.\n* [`BRPOPLPUSH`](https://redis.io/docs/latest/commands/brpoplpush) to atomically claim a job into the processing list.\n* [`LREM`](https://redis.io/docs/latest/commands/lrem) to remove a job from the processing list on complete or fail.\n* [`LRANGE`](https://redis.io/docs/latest/commands/lrange) and [`LLEN`](https://redis.io/docs/latest/commands/llen) to read queue depth and list contents.\n* [`LTRIM`](https://redis.io/docs/latest/commands/ltrim) to cap the completed and failed history.\n* [`HSET`](https://redis.io/docs/latest/commands/hset) and [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) for job metadata.\n* [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby) for the attempt counter and the cross-request stats counters.\n* [`EXPIRE`](https://redis.io/docs/latest/commands/expire) for automatic cleanup of completed and failed jobs.\n* [`PUBLISH`](https://redis.io/docs/latest/commands/publish) for job-completion notifications.\n* [`EVAL`](https://redis.io/docs/latest/commands/eval) for atomic complete, fail, and reclaim flows.\n\nSee the [Predis README](https://github.com/predis/predis) for full client reference."}],"examples":[{"id":"the-job-queue-helper-ex0","language":"php","code":"require __DIR__ . '/vendor/autoload.php';\nrequire __DIR__ . '/JobQueue.php';\n\nuse Predis\\Client as PredisClient;\n\n$redis = new PredisClient(['host' => '127.0.0.1', 'port' => 6379]);\n$queue = new JobQueue($redis, 'jobs', 5000);\n\n$jobId = $queue->enqueue(['kind' => 'email', 'recipient' => 'alice@example.com']);\n\n// In a worker process:\n$job = $queue->claim(1000);\nif ($job !== null) {\n    try {\n        // ... run the job ...\n        $queue->complete($job, ['sent_at' => date('c')]);\n    } catch (\\Throwable $exc) {\n        $queue->fail($job, $exc->getMessage());\n    }\n}\n\n// In a periodic sweeper:\n$reclaimed = $queue->reclaimStuck();","section_id":"the-job-queue-helper"},{"id":"data-model-ex0","language":"text","code":"queue:jobs:pending          (list)   pending job IDs, oldest at the right\nqueue:jobs:processing       (list)   claimed but not yet completed\nqueue:jobs:completed        (list)   recent successes (LTRIM-capped history)\nqueue:jobs:failed           (list)   terminally failed jobs\nqueue:jobs:job:{id}         (hash)   per-job metadata\nqueue:jobs:events           (pubsub) completion notifications","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"queue:jobs:job:9a4f...\n  id              = 9a4f...\n  payload         = {\"kind\":\"email\",\"recipient\":\"alice@example.com\"}\n  status          = pending | processing | completed | failed\n  attempts        = 1\n  enqueued_at_ms  = 1715441000000\n  claimed_at_ms   = 1715441000123\n  claim_token     = b3c0d1e2...        (per-claim random token)\n  completed_at_ms = 1715441000456\n  result          = {\"sent_at\":\"...\"}\n  last_error      = \"smtp timeout\"","section_id":"data-model"},{"id":"enqueueing-jobs-ex0","language":"php","code":"public function enqueue(array $payload): string\n{\n    $jobId = bin2hex(random_bytes(8));\n    $nowMs = (int) round(microtime(true) * 1000);\n    $meta = [\n        'id' => $jobId,\n        'payload' => json_encode($payload),\n        'status' => 'pending',\n        'attempts' => '0',\n        'enqueued_at_ms' => (string) $nowMs,\n        'claim_token' => '',\n    ];\n\n    $pipe = $this->redis->pipeline();\n    $pipe->hset($this->metaKey($jobId), ...self::flattenFields($meta));\n    $pipe->lpush($this->pendingKey, [$jobId]);\n    $pipe->execute();\n\n    $this->redis->hincrby($this->statsKey, 'enqueued_total', 1);\n    return $jobId;\n}","section_id":"enqueueing-jobs"},{"id":"claiming-jobs-with-brpoplpush-ex0","language":"php","code":"public function claim(int $timeoutMs = 1000): ?ClaimedJob\n{\n    $timeoutSec = max(1, (int) ceil($timeoutMs / 1000));\n    $jobId = $this->redis->brpoplpush($this->pendingKey, $this->processingKey, $timeoutSec);\n    if ($jobId === null || $jobId === false || $jobId === '') {\n        return null;\n    }\n\n    $token = bin2hex(random_bytes(8));\n    $nowMs = (int) round(microtime(true) * 1000);\n    $metaKey = $this->metaKey($jobId);\n\n    $pipe = $this->redis->pipeline();\n    $pipe->hset($metaKey, ...self::flattenFields([\n        'status' => 'processing',\n        'claimed_at_ms' => (string) $nowMs,\n        'claim_token' => $token,\n    ]));\n    $pipe->hincrby($metaKey, 'attempts', 1);\n    $pipe->hgetall($metaKey);\n    [$_h, $_a, $meta] = $pipe->execute();\n\n    return new ClaimedJob(\n        (string) $jobId,\n        json_decode($meta['payload'] ?? '{}', true) ?: [],\n        (int) ($meta['attempts'] ?? 1),\n        $token\n    );\n}","section_id":"claiming-jobs-with-brpoplpush"},{"id":"completing-jobs-ex0","language":"php","code":"public function complete(ClaimedJob $job, array $result): bool\n{\n    $ok = $this->redis->eval(\n        self::COMPLETE_SCRIPT,\n        3,\n        $this->metaPrefix,\n        $this->processingKey,\n        $this->completedKey,\n        $job->id,\n        $job->claimToken,\n        'completed',\n        (string) self::nowMs(),\n        json_encode($result),\n        (string) $this->completedTtl,\n        (string) $this->completedHistory\n    );\n    if (!$ok) {\n        return false;\n    }\n    $this->redis->publish($this->eventsChannel,\n        json_encode(['id' => $job->id, 'status' => 'completed']));\n    $this->redis->hincrby($this->statsKey, 'completed_total', 1);\n    return true;\n}","section_id":"completing-jobs"},{"id":"failing-and-retrying-ex0","language":"php","code":"public function fail(ClaimedJob $job, string $error): bool\n{\n    $retry = $job->attempts < $this->maxAttempts;\n    $result = $this->redis->eval(\n        self::FAIL_SCRIPT,\n        4,\n        $this->metaPrefix,\n        $this->processingKey,\n        $this->pendingKey,\n        $this->failedKey,\n        $job->id,\n        $job->claimToken,\n        $error,\n        (string) self::nowMs(),\n        (string) $this->completedTtl,\n        (string) $this->completedHistory,\n        $retry ? '1' : '0'\n    );\n    return (bool) $result;\n}","section_id":"failing-and-retrying"},{"id":"reclaiming-stuck-jobs-ex0","language":"php","code":"public function reclaimStuck(): array\n{\n    $reclaimed = $this->redis->eval(\n        self::RECLAIM_SCRIPT,\n        3,\n        $this->pendingKey,\n        $this->processingKey,\n        $this->metaPrefix,\n        (string) self::nowMs(),\n        (string) $this->visibilityMs\n    );\n    return is_array($reclaimed) ? array_values(array_map('strval', $reclaimed)) : [];\n}","section_id":"reclaiming-stuck-jobs"},{"id":"stats-and-history-ex0","language":"php","code":"public function stats(): array\n{\n    $pipe = $this->redis->pipeline();\n    $pipe->llen($this->pendingKey);\n    $pipe->llen($this->processingKey);\n    $pipe->llen($this->completedKey);\n    $pipe->llen($this->failedKey);\n    $pipe->hgetall($this->statsKey);\n    [$pending, $processing, $completed, $failed, $statsHash] = $pipe->execute();\n\n    return [\n        'enqueued_total'   => (int) ($statsHash['enqueued_total']   ?? 0),\n        'completed_total'  => (int) ($statsHash['completed_total']  ?? 0),\n        'failed_total'     => (int) ($statsHash['failed_total']     ?? 0),\n        'reclaimed_total'  => (int) ($statsHash['reclaimed_total']  ?? 0),\n        'pending_depth'    => (int) $pending,\n        'processing_depth' => (int) $processing,\n        'completed_depth'  => (int) $completed,\n        'failed_depth'     => (int) $failed,\n        'visibility_ms'    => $this->visibilityMs,\n    ];\n}","section_id":"stats-and-history"},{"id":"prerequisites-ex0","language":"bash","code":"composer require \"predis/predis:^3.0\"","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir job-queue-demo && cd job-queue-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/job-queue/php\ncurl -O $BASE/JobQueue.php\ncurl -O $BASE/JobWorker.php\ncurl -O $BASE/WorkerSupervisor.php\ncurl -O $BASE/demo_server.php\ncurl -O $BASE/worker.php\ncurl -O $BASE/composer.json","section_id":"get-the-source-files"},{"id":"get-the-source-files-ex1","language":"bash","code":"composer install","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"php -S 127.0.0.1:8796 demo_server.php","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"[...] PHP 8.4.6 Development Server (http://127.0.0.1:8796) started","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex2","language":"bash","code":"REDIS_HOST=redis.local REDIS_PORT=6380 VISIBILITY_MS=10000 \\\n    php -S 127.0.0.1:8796 demo_server.php","section_id":"start-the-demo-server"},{"id":"the-worker-process-and-supervisor-ex0","language":"bash","code":"php worker.php --name worker-1 --work-latency-ms 200 --fail-rate 0 --hang-rate 0","section_id":"the-worker-process-and-supervisor"},{"id":"inspect-queue-state-directly-in-redis-ex0","language":"bash","code":"# How many pending jobs?\nredis-cli LLEN queue:jobs:pending\n\n# Look at the next 5 jobs to be picked up.\nredis-cli LRANGE queue:jobs:pending -5 -1\n\n# Read a job's metadata.\nredis-cli HGETALL queue:jobs:job:9a4f0d1c\n\n# How many jobs are currently being processed?\nredis-cli LLEN queue:jobs:processing\n\n# Read the demo counters (PHP-only — these live in Redis because each\n# HTTP request is its own process).\nredis-cli HGETALL demo:queue_stats:jobs\n\n# Clear everything for this queue (be careful — this deletes work).\nredis-cli --scan --pattern 'queue:jobs:*' | xargs redis-cli DEL","section_id":"inspect-queue-state-directly-in-redis"}]}
{"schema_version":2,"id":"develop/use-cases/job-queue/redis-py","title":"Redis job queue with redis-py","url":"https://redis.io/docs/latest/develop/use-cases/job-queue/redis-py/","summary":"Implement a Redis job queue in Python with redis-py","aliases":["/develop/use-cases/job-queue/python"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:50:30+01:00","children":[],"page_type":"content","content_hash":"9f31048bd8b559218a108b4b18ea414fddfa63894343019c960f798696f65a23","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis-backed job queue in Python with [`redis-py`](https://redis.io/docs/latest/develop/clients/redis-py). It includes a small local web server built with the Python standard library so you can enqueue jobs, watch a pool of workers drain them, and see the reclaimer recover jobs from a simulated worker crash."},{"id":"overview","title":"Overview","role":"overview","text":"A job queue lets your application offload background work — sending email, processing payments, image transcoding, ML inference, webhooks — from the request path. Producers enqueue jobs in milliseconds and return to the user; workers pull from the queue and process them on their own schedule.\n\nThat gives you:\n\n* Low-latency user-facing requests, even when downstream work is slow or bursty\n* Horizontal scale across many worker processes that share one Redis instance\n* At-least-once delivery so a worker crash doesn't lose work\n* Visibility-timeout reclaim that returns stuck jobs to the queue automatically\n* Job metadata, retry counts, and completion results in Redis hashes with TTL\n\nIn this example, each job is identified by a random hex ID and its payload, status, and result live in a Redis hash under `queue:jobs:job:{id}`. Pending IDs sit in a list, claimed IDs move atomically to a *processing* list, and completed or failed IDs land in short history lists."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. The application calls `queue.enqueue(payload)`\n2. The helper writes the job metadata hash and `LPUSH`es the job ID onto the pending list\n3. A worker calls `queue.claim(timeout_ms)`\n4. The helper runs `BRPOPLPUSH` to atomically move the next pending ID into the processing list and writes a per-claim `claim_token` plus `claimed_at_ms` on the hash\n5. The worker runs the job and calls `queue.complete(job, result)` or `queue.fail(job, error)`\n6. `complete` removes the job from the processing list, writes the result, and `LPUSH`es the ID onto the completed history (with `LTRIM` and an `EXPIRE` on the hash for cleanup)\n7. `fail` either retries the job (back to pending) or moves it to the failed list once retries are exhausted\n\nIf a worker dies before completing a job, the job sits in the processing list with a `claimed_at_ms` older than the visibility timeout. A periodic call to `queue.reclaim_stuck()` finds those jobs and moves them back to pending so another worker can pick them up.\n\nEvery state change holds the token: a worker that has been reclaimed cannot later complete or fail a job another worker has already claimed."},{"id":"the-job-queue-helper","title":"The job queue helper","role":"content","text":"The `RedisJobQueue` class wraps the queue operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/job-queue/redis-py/job_queue.py)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each job's state lives in a Redis hash plus a position in one of four lists:\n\n[code example]\n\nA job's hash carries:\n\n[code example]\n\nThe implementation uses:\n\n* [`LPUSH`](https://redis.io/docs/latest/commands/lpush) to add new job IDs to the pending list\n* [`BRPOPLPUSH`](https://redis.io/docs/latest/commands/brpoplpush) to atomically claim a job into the processing list\n* [`LREM`](https://redis.io/docs/latest/commands/lrem) to remove a claimed job from the processing list on complete or fail\n* [`LTRIM`](https://redis.io/docs/latest/commands/ltrim) to cap the completed and failed history lists\n* [`HSET`](https://redis.io/docs/latest/commands/hset) / [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) for job metadata\n* [`EXPIRE`](https://redis.io/docs/latest/commands/expire) on completed and failed hashes for automatic cleanup\n* [`PUBLISH`](https://redis.io/docs/latest/commands/publish) on `queue:jobs:events` for completion signalling\n* [Lua scripting](https://redis.io/docs/latest/develop/programmability/eval-intro) ([`EVALSHA`](https://redis.io/docs/latest/commands/evalsha)) for the complete, fail, and reclaim flows so each runs atomically against the processing list and metadata hash"},{"id":"enqueueing-jobs","title":"Enqueueing jobs","role":"content","text":"`enqueue()` writes the metadata hash and pushes the job ID onto the pending list in one pipeline:\n\n[code example]\n\nThe payload is stored as JSON so the queue can carry arbitrary nested structures without forcing every field into a hash."},{"id":"claiming-jobs-with-brpoplpush","title":"Claiming jobs with BRPOPLPUSH","role":"content","text":"A worker blocks until a job is available, then atomically pops it from the pending list and pushes it onto the processing list. `BRPOPLPUSH` does both in a single Redis call:\n\n[code example]\n\nThe `claim_token` is the worker's proof of ownership for this attempt. Every subsequent state change (complete, fail) checks it before touching the processing list, so a worker that hung past the visibility timeout cannot interfere with the new claimant."},{"id":"completing-jobs","title":"Completing jobs","role":"content","text":"`complete()` runs a Lua script so the processing-list removal, the metadata write, and the history push happen atomically:\n\n[code example]\n\nThe Lua script checks the token first and returns `0` if the worker no longer owns the job (because the reclaimer moved it back to pending). The metadata hash also gets an `EXPIRE` so completed jobs are cleaned up automatically."},{"id":"failing-and-retrying","title":"Failing and retrying","role":"content","text":"`fail()` either retries the job (back to pending) or moves it to the failed list once retries are exhausted:\n\n[code example]\n\nThe attempt counter is incremented on every `claim()`, so a job that fails three times is moved to the failed list with `attempts = 3` and the final `last_error` preserved."},{"id":"reclaiming-stuck-jobs","title":"Reclaiming stuck jobs","role":"content","text":"If a worker dies mid-job — the process is killed, the host loses power, the network partitions — the job sits in the processing list with a `claimed_at_ms` that never advances. A periodic call to `reclaim_stuck()` walks the processing list and moves any job past the visibility timeout back to pending:\n\n[code example]\n\nThe Lua script also handles a narrower race: a worker that crashed between `BRPOPLPUSH` and writing `claimed_at_ms`. Those jobs are reclaimed after `2 × visibility_ms` using `enqueued_at_ms` as a fallback timer, so they aren't stranded forever."},{"id":"stats-and-history","title":"Stats and history","role":"content","text":"`stats()` reports queue depth plus per-process counters:\n\n[code example]\n\nThe completed and failed lists are capped via `LTRIM` so they never grow unbounded; a real deployment would also write completion events to a longer-term audit log if needed."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* Redis 6.2 or later running locally on the default port (6379). Earlier versions still work, since the helper uses commands that have existed since Redis 2.6.\n* Python 3.9 or later.\n* The `redis-py` client. Install it with:\n\n  [code example]"},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of three Python files. Download them from the [`redis-py` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/job-queue/redis-py) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From that directory:\n\n[code example]\n\nYou should see:\n\n[code example]\n\nOpen [http://127.0.0.1:8090](http://127.0.0.1:8090) in a browser. You can:\n\n* Enqueue jobs of different kinds (email, webhook, thumbnail, invoice) in batches.\n* Start a pool of workers with configurable size, work latency, and *failure* / *hang* rates. A non-zero hang rate simulates worker crashes.\n* Click **Run reclaim sweep** to move any timed-out processing jobs back to pending.\n* Watch pending / processing / completed / failed lists update every 800 ms.\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`. You can also tune the visibility timeout with `--visibility-ms`."},{"id":"the-mock-worker-pool","title":"The mock worker pool","role":"content","text":"The demo includes a small `Worker` and `WorkerPool` ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/job-queue/redis-py/worker.py)) that stands in for whatever real background work your application would run. Each worker:\n\n* Blocks on `queue.claim()` for new jobs.\n* Sleeps `work_latency_ms` to simulate doing the work.\n* Either completes successfully, fails (calling `queue.fail()`), or *hangs* — returning without completing or failing the job so the reclaimer has to recover it.\n\nThe `fail_rate` and `hang_rate` knobs let you watch the at-least-once delivery and reclaim behaviours from the UI without writing test code."},{"id":"production-usage","title":"Production usage","role":"content","text":""},{"id":"choose-a-visibility-timeout-that-matches-your-worst-case-job-latency","title":"Choose a visibility timeout that matches your worst-case job latency","role":"content","text":"The visibility timeout has to exceed the longest real job time, with margin. If it's too short, a healthy worker that's running a slow job will get its work duplicated when the reclaimer fires. If it's too long, a real crash takes longer to detect. Most production deployments use a per-queue value tuned to the 99th-percentile job latency — for example, 2 minutes for email and 30 minutes for video transcoding."},{"id":"run-the-reclaimer-on-a-schedule","title":"Run the reclaimer on a schedule","role":"content","text":"The demo only reclaims when you click the button. In production, run `reclaim_stuck()` from a periodic task (every few seconds for fast queues, every minute for slow ones), or from each worker before it blocks on `claim()`. Both patterns work as long as *someone* runs the sweep."},{"id":"use-a-separate-redis-database-or-key-prefix-per-queue","title":"Use a separate Redis database or key prefix per queue","role":"content","text":"The helper takes a `queue_name` argument so you can run multiple independent queues against one Redis instance — for example, one queue per priority level, or one per job kind. Keep queue keys under a clearly-namespaced prefix (here, `queue:jobs:*`) so they're easy to inspect and easy to clear without touching application data."},{"id":"cap-the-completed-and-failed-history","title":"Cap the completed and failed history","role":"content","text":"The demo keeps the last 50 completed and 50 failed job IDs via `LTRIM`. If you need longer history for audit purposes, write completion events to a separate Redis Stream (or to an external store) and keep the in-queue history short. Stream consumer groups give you the same fan-out semantics with a much richer history."},{"id":"tune-max_attempts-per-job-kind","title":"Tune `max_attempts` per job kind","role":"content","text":"A blanket `max_attempts = 3` is a reasonable default for transient failures (network timeouts, rate limits). Jobs that talk to non-idempotent external systems — for example, posting a Stripe charge — need either application-level idempotency keys or a much lower retry count. The helper exposes `max_attempts` so each queue can pick its own policy."},{"id":"inspect-queue-state-directly-in-redis","title":"Inspect queue state directly in Redis","role":"content","text":"Because the queue is just lists and hashes, you can inspect it with `redis-cli`:\n\n[code example]"},{"id":"learn-more","title":"Learn more","role":"related","text":"This example uses the following Redis commands:\n\n* [`LPUSH`](https://redis.io/docs/latest/commands/lpush) to enqueue a job ID.\n* [`BRPOPLPUSH`](https://redis.io/docs/latest/commands/brpoplpush) to atomically claim a job into the processing list.\n* [`LREM`](https://redis.io/docs/latest/commands/lrem) to remove a job from the processing list on complete or fail.\n* [`LRANGE`](https://redis.io/docs/latest/commands/lrange) and [`LLEN`](https://redis.io/docs/latest/commands/llen) to read queue depth and list contents.\n* [`LTRIM`](https://redis.io/docs/latest/commands/ltrim) to cap the completed and failed history.\n* [`HSET`](https://redis.io/docs/latest/commands/hset) and [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) for job metadata.\n* [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby) for the attempt counter.\n* [`EXPIRE`](https://redis.io/docs/latest/commands/expire) for automatic cleanup of completed and failed jobs.\n* [`PUBLISH`](https://redis.io/docs/latest/commands/publish) for job-completion notifications.\n* [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) for atomic complete, fail, and reclaim flows.\n\nSee the [`redis-py` documentation](https://redis.io/docs/latest/develop/clients/redis-py) for full client reference."}],"examples":[{"id":"the-job-queue-helper-ex0","language":"python","code":"import redis\nfrom job_queue import RedisJobQueue\n\nr = redis.Redis(host=\"localhost\", port=6379, decode_responses=True)\nqueue = RedisJobQueue(redis_client=r, visibility_ms=5000)\n\njob_id = queue.enqueue({\"kind\": \"email\", \"recipient\": \"alice@example.com\"})\n\n# In a worker process:\njob = queue.claim(timeout_ms=1000)\nif job is not None:\n    try:\n        # ... run the job ...\n        queue.complete(job, result={\"sent_at\": \"2026-05-11T15:00:00Z\"})\n    except Exception as exc:\n        queue.fail(job, error=str(exc))\n\n# In a periodic sweeper:\nreclaimed = queue.reclaim_stuck()","section_id":"the-job-queue-helper"},{"id":"data-model-ex0","language":"text","code":"queue:jobs:pending          (list)   pending job IDs, oldest at the right\nqueue:jobs:processing       (list)   claimed but not yet completed\nqueue:jobs:completed        (list)   recent successes (LTRIM-capped history)\nqueue:jobs:failed           (list)   terminally failed jobs\nqueue:jobs:job:{id}         (hash)   per-job metadata\nqueue:jobs:events           (pubsub) completion notifications","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"queue:jobs:job:9a4f...\n  id              = 9a4f...\n  payload         = {\"kind\":\"email\",\"recipient\":\"alice@example.com\"}\n  status          = pending | processing | completed | failed\n  attempts        = 1\n  enqueued_at_ms  = 1715441000000\n  claimed_at_ms   = 1715441000123\n  claim_token     = b3c0d1e2...        (per-claim random token)\n  completed_at_ms = 1715441000456\n  result          = {\"sent_at\":\"...\"}\n  last_error      = \"smtp timeout\"","section_id":"data-model"},{"id":"enqueueing-jobs-ex0","language":"python","code":"def enqueue(self, payload: dict) -> str:\n    job_id = secrets.token_hex(8)\n    now_ms = self._now_ms()\n    meta = {\n        \"id\": job_id,\n        \"payload\": json.dumps(payload),\n        \"status\": \"pending\",\n        \"attempts\": 0,\n        \"enqueued_at_ms\": now_ms,\n        \"claim_token\": \"\",\n    }\n    pipe = self.redis.pipeline()\n    pipe.hset(self._meta_key(job_id), mapping=meta)\n    pipe.lpush(self.pending_key, job_id)\n    pipe.execute()\n    return job_id","section_id":"enqueueing-jobs"},{"id":"claiming-jobs-with-brpoplpush-ex0","language":"python","code":"def claim(self, timeout_ms: int = 1000) -> Optional[ClaimedJob]:\n    timeout_s = max(timeout_ms / 1000.0, 0.1)\n    job_id = self.redis.brpoplpush(self.pending_key, self.processing_key, timeout=timeout_s)\n    if job_id is None:\n        return None\n\n    token = secrets.token_hex(8)\n    now_ms = self._now_ms()\n    meta_key = self._meta_key(job_id)\n    pipe = self.redis.pipeline()\n    pipe.hset(meta_key, mapping={\n        \"status\": \"processing\",\n        \"claimed_at_ms\": now_ms,\n        \"claim_token\": token,\n    })\n    pipe.hincrby(meta_key, \"attempts\", 1)\n    pipe.hgetall(meta_key)\n    _, _, meta = pipe.execute()\n    return ClaimedJob(job_id, json.loads(meta[\"payload\"]), int(meta[\"attempts\"]), token)","section_id":"claiming-jobs-with-brpoplpush"},{"id":"completing-jobs-ex0","language":"python","code":"def complete(self, job: ClaimedJob, result: dict) -> bool:\n    ok = self._complete(\n        keys=[self.meta_prefix, self.processing_key, self.completed_key],\n        args=[\n            job.id,\n            job.claim_token,\n            \"completed\",\n            self._now_ms(),\n            json.dumps(result),\n            self.completed_ttl,\n            self.completed_history,\n        ],\n    )\n    if not ok:\n        return False\n    self.redis.publish(self.events_channel, json.dumps({\"id\": job.id, \"status\": \"completed\"}))\n    return True","section_id":"completing-jobs"},{"id":"failing-and-retrying-ex0","language":"python","code":"def fail(self, job: ClaimedJob, error: str) -> bool:\n    retry = job.attempts < self.max_attempts\n    result = self._fail(\n        keys=[self.meta_prefix, self.processing_key, self.pending_key, self.failed_key],\n        args=[\n            job.id,\n            job.claim_token,\n            error,\n            self._now_ms(),\n            self.completed_ttl,\n            self.completed_history,\n            \"1\" if retry else \"0\",\n        ],\n    )\n    return bool(result)","section_id":"failing-and-retrying"},{"id":"reclaiming-stuck-jobs-ex0","language":"python","code":"def reclaim_stuck(self) -> list[str]:\n    reclaimed = self._reclaim(\n        keys=[self.pending_key, self.processing_key, self.meta_prefix],\n        args=[self._now_ms(), self.visibility_ms],\n    )\n    return list(reclaimed)","section_id":"reclaiming-stuck-jobs"},{"id":"stats-and-history-ex0","language":"python","code":"def stats(self) -> dict:\n    pipe = self.redis.pipeline()\n    pipe.llen(self.pending_key)\n    pipe.llen(self.processing_key)\n    pipe.llen(self.completed_key)\n    pipe.llen(self.failed_key)\n    pending, processing, completed, failed = pipe.execute()\n    return {\n        \"enqueued_total\": self._enqueued,\n        \"completed_total\": self._completed,\n        \"failed_total\": self._failed,\n        \"reclaimed_total\": self._reclaimed,\n        \"pending_depth\": pending,\n        \"processing_depth\": processing,\n        \"completed_depth\": completed,\n        \"failed_depth\": failed,\n        \"visibility_ms\": self.visibility_ms,\n    }","section_id":"stats-and-history"},{"id":"prerequisites-ex0","language":"bash","code":"pip install \"redis>=5.0\"","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir job-queue-demo && cd job-queue-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/job-queue/redis-py\ncurl -O $BASE/job_queue.py\ncurl -O $BASE/worker.py\ncurl -O $BASE/demo_server.py","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"python3 demo_server.py","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"Redis job-queue demo server listening on http://127.0.0.1:8090\nUsing Redis at localhost:6379\nVisibility timeout: 5000 ms","section_id":"start-the-demo-server"},{"id":"inspect-queue-state-directly-in-redis-ex0","language":"bash","code":"# How many pending jobs?\nredis-cli LLEN queue:jobs:pending\n\n# Look at the next 5 jobs to be picked up.\nredis-cli LRANGE queue:jobs:pending -5 -1\n\n# Read a job's metadata.\nredis-cli HGETALL queue:jobs:job:9a4f0d1c\n\n# How many jobs are currently being processed?\nredis-cli LLEN queue:jobs:processing\n\n# Clear everything for this queue (be careful — this deletes work).\nredis-cli --scan --pattern 'queue:jobs:*' | xargs redis-cli DEL","section_id":"inspect-queue-state-directly-in-redis"}]}
{"schema_version":2,"id":"develop/use-cases/job-queue/ruby","title":"Redis job queue with redis-rb","url":"https://redis.io/docs/latest/develop/use-cases/job-queue/ruby/","summary":"Implement a Redis job queue in Ruby with redis-rb","aliases":["/develop/use-cases/job-queue/redis-rb"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:50:30+01:00","children":[],"page_type":"content","content_hash":"72fc884f7a7600ef1682c6564ee686802956fc23184d79e266110e035d9e5bad","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis-backed job queue in Ruby with [`redis-rb`](https://redis.io/docs/latest/develop/clients/ruby). It includes a small local web server built with `webrick` from the Ruby standard library so you can enqueue jobs, watch a pool of workers drain them, and see the reclaimer recover jobs from a simulated worker crash."},{"id":"overview","title":"Overview","role":"overview","text":"A job queue lets your application offload background work — sending email, processing payments, image transcoding, ML inference, webhooks — from the request path. Producers enqueue jobs in milliseconds and return to the user; workers pull from the queue and process them on their own schedule.\n\nThat gives you:\n\n* Low-latency user-facing requests, even when downstream work is slow or bursty\n* Horizontal scale across many worker processes that share one Redis instance\n* At-least-once delivery so a worker crash doesn't lose work\n* Visibility-timeout reclaim that returns stuck jobs to the queue automatically\n* Job metadata, retry counts, and completion results in Redis hashes with TTL\n\nIn this example, each job is identified by a random hex ID and its payload, status, and result live in a Redis hash under `queue:jobs:job:{id}`. Pending IDs sit in a list, claimed IDs move atomically to a *processing* list, and completed or failed IDs land in short history lists."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. The application calls `queue.enqueue(payload)`.\n2. The helper writes the job metadata hash and `LPUSH`es the job ID onto the pending list.\n3. A worker calls `queue.claim(timeout_ms: ...)`.\n4. The helper runs `BRPOPLPUSH` to atomically move the next pending ID into the processing list and writes a per-claim `claim_token` plus `claimed_at_ms` on the hash.\n5. The worker runs the job and calls `queue.complete(job, result)` or `queue.fail(job, error)`.\n6. `complete` removes the job from the processing list, writes the result, and `LPUSH`es the ID onto the completed history (with `LTRIM` and an `EXPIRE` on the hash for cleanup).\n7. `fail` either retries the job (back to pending) or moves it to the failed list once retries are exhausted.\n\nIf a worker dies before completing a job, the job sits in the processing list with a `claimed_at_ms` older than the visibility timeout. A periodic call to `queue.reclaim_stuck` finds those jobs and moves them back to pending so another worker can pick them up.\n\nEvery state change holds the token: a worker that has been reclaimed cannot later complete or fail a job another worker has already claimed."},{"id":"the-job-queue-helper","title":"The job queue helper","role":"content","text":"The `RedisJobQueue` class wraps the queue operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/job-queue/ruby/job_queue.rb)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each job's state lives in a Redis hash plus a position in one of four lists:\n\n[code example]\n\nA job's hash carries:\n\n[code example]\n\nThe implementation uses:\n\n* [`LPUSH`](https://redis.io/docs/latest/commands/lpush) to add new job IDs to the pending list.\n* [`BRPOPLPUSH`](https://redis.io/docs/latest/commands/brpoplpush) to atomically claim a job into the processing list.\n* [`LREM`](https://redis.io/docs/latest/commands/lrem) to remove a claimed job from the processing list on complete or fail.\n* [`LTRIM`](https://redis.io/docs/latest/commands/ltrim) to cap the completed and failed history lists.\n* [`HSET`](https://redis.io/docs/latest/commands/hset) / [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) for job metadata.\n* [`EXPIRE`](https://redis.io/docs/latest/commands/expire) on completed and failed hashes for automatic cleanup.\n* [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby) for the attempt counter and the shared totals hash so the demo's multiple worker threads (each with its own Redis connection) report a single consistent count.\n* [`PUBLISH`](https://redis.io/docs/latest/commands/publish) on `queue:jobs:events` for completion signalling.\n* [Lua scripting](https://redis.io/docs/latest/develop/programmability/eval-intro) ([`EVALSHA`](https://redis.io/docs/latest/commands/evalsha)) for the complete, fail, and reclaim flows so each runs atomically against the processing list and metadata hash."},{"id":"enqueueing-jobs","title":"Enqueueing jobs","role":"content","text":"`enqueue` writes the metadata hash and pushes the job ID onto the pending list in one pipeline:\n\n[code example]\n\nThe payload is stored as JSON so the queue can carry arbitrary nested structures without forcing every field into a hash."},{"id":"claiming-jobs-with-brpoplpush","title":"Claiming jobs with BRPOPLPUSH","role":"content","text":"A worker blocks until a job is available, then atomically pops it from the pending list and pushes it onto the processing list. `BRPOPLPUSH` does both in a single Redis call:\n\n[code example]\n\nThe `claim_token` is the worker's proof of ownership for this attempt. Every subsequent state change (complete, fail) checks it before touching the processing list, so a worker that hung past the visibility timeout cannot interfere with the new claimant."},{"id":"completing-jobs","title":"Completing jobs","role":"content","text":"`complete` runs a Lua script so the processing-list removal, the metadata write, and the history push happen atomically:\n\n[code example]\n\nThe Lua script checks the token first and returns `0` if the worker no longer owns the job (because the reclaimer moved it back to pending). The metadata hash also gets an `EXPIRE` so completed jobs are cleaned up automatically."},{"id":"failing-and-retrying","title":"Failing and retrying","role":"content","text":"`fail` either retries the job (back to pending) or moves it to the failed list once retries are exhausted:\n\n[code example]\n\nThe attempt counter is incremented on every `claim`, so a job that fails three times is moved to the failed list with `attempts = 3` and the final `last_error` preserved."},{"id":"reclaiming-stuck-jobs","title":"Reclaiming stuck jobs","role":"content","text":"If a worker dies mid-job — the process is killed, the host loses power, the network partitions — the job sits in the processing list with a `claimed_at_ms` that never advances. A periodic call to `reclaim_stuck` walks the processing list and moves any job past the visibility timeout back to pending:\n\n[code example]\n\nThe Lua script also handles a narrower race: a worker that crashed between `BRPOPLPUSH` and writing `claimed_at_ms`. Those jobs are reclaimed after `2 × visibility_ms` using `enqueued_at_ms` as a fallback timer, so they aren't stranded forever."},{"id":"stats-and-history","title":"Stats and history","role":"content","text":"`stats` reports queue depth plus shared counter totals:\n\n[code example]\n\nTotals live in a Redis hash so each worker thread — which holds its own Redis connection — increments them atomically with `HINCRBY` and the orchestrator sees a single consistent view. The completed and failed lists are capped via `LTRIM` so they never grow unbounded; a real deployment would also write completion events to a separate Stream if you need a longer audit history."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* Redis 6.2 or later running locally on the default port (6379). Earlier versions still work, since the helper uses commands that have existed since Redis 2.6.\n* Ruby 3.0 or later.\n* The `redis-rb` client (5.x) and the `webrick` gem (a stdlib gem in Ruby 3 — install it explicitly with `gem install webrick` if it isn't already on your load path):\n\n  [code example]"},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of three Ruby files. Download them from the [`ruby` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/job-queue/ruby) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From that directory:\n\n[code example]\n\nYou should see:\n\n[code example]\n\nOpen [http://127.0.0.1:8797](http://127.0.0.1:8797) in a browser. You can:\n\n* Enqueue jobs of different kinds (email, webhook, thumbnail, invoice) in batches.\n* Start a pool of workers with configurable size, work latency, and *failure* / *hang* rates. A non-zero hang rate simulates worker crashes.\n* Click **Run reclaim sweep** to move any timed-out processing jobs back to pending.\n* Watch pending / processing / completed / failed lists update every 800 ms.\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`. You can also tune the visibility timeout with `--visibility-ms` and pick a different queue name with `--queue-name` if you want to share a Redis instance with other demos."},{"id":"the-mock-worker-pool","title":"The mock worker pool","role":"content","text":"The demo includes a small `Worker` and `WorkerPool` ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/job-queue/ruby/worker.rb)) that stands in for whatever real background work your application would run. Each worker:\n\n* Blocks on `queue.claim` for new jobs.\n* Sleeps `work_latency_ms` to simulate doing the work.\n* Either completes successfully, fails (calling `queue.fail`), or *hangs* — returning without completing or failing the job so the reclaimer has to recover it.\n\nThe `fail_rate` and `hang_rate` knobs let you watch the at-least-once delivery and reclaim behaviours from the UI without writing test code.\n\nEach `Worker` runs in its own `Thread` and holds its **own** `RedisJobQueue` instance backed by a dedicated Redis connection. A blocking `BRPOPLPUSH` reserves the underlying connection until it returns, so giving each worker its own connection keeps the HTTP server's stats and jobs endpoints responsive even while every worker is parked on a claim call. The `WorkerPool` is constructed with a `queue_factory` lambda so it can mint one fresh helper per worker."},{"id":"production-usage","title":"Production usage","role":"content","text":""},{"id":"choose-a-visibility-timeout-that-matches-your-worst-case-job-latency","title":"Choose a visibility timeout that matches your worst-case job latency","role":"content","text":"The visibility timeout has to exceed the longest real job time, with margin. If it's too short, a healthy worker that's running a slow job will get its work duplicated when the reclaimer fires. If it's too long, a real crash takes longer to detect. Most production deployments use a per-queue value tuned to the 99th-percentile job latency — for example, 2 minutes for email and 30 minutes for video transcoding."},{"id":"run-the-reclaimer-on-a-schedule","title":"Run the reclaimer on a schedule","role":"content","text":"The demo only reclaims when you click the button. In production, run `reclaim_stuck` from a periodic task (every few seconds for fast queues, every minute for slow ones), or from each worker before it blocks on `claim`. Both patterns work as long as *someone* runs the sweep."},{"id":"use-a-separate-redis-database-or-key-prefix-per-queue","title":"Use a separate Redis database or key prefix per queue","role":"content","text":"The helper takes a `queue_name` argument so you can run multiple independent queues against one Redis instance — for example, one queue per priority level, or one per job kind. Keep queue keys under a clearly-namespaced prefix (here, `queue:jobs:*`) so they're easy to inspect and easy to clear without touching application data."},{"id":"cap-the-completed-and-failed-history","title":"Cap the completed and failed history","role":"content","text":"The demo keeps the last 50 completed and 50 failed job IDs via `LTRIM`. If you need longer history for audit purposes, write completion events to a separate Redis Stream (or to an external store) and keep the in-queue history short. Stream consumer groups give you the same fan-out semantics with a much richer history."},{"id":"tune-max_attempts-per-job-kind","title":"Tune `max_attempts` per job kind","role":"content","text":"A blanket `max_attempts = 3` is a reasonable default for transient failures (network timeouts, rate limits). Jobs that talk to non-idempotent external systems — for example, posting a Stripe charge — need either application-level idempotency keys or a much lower retry count. The helper exposes `max_attempts` so each queue can pick its own policy."},{"id":"give-each-blocking-worker-its-own-connection","title":"Give each blocking worker its own connection","role":"content","text":"`redis-rb` 5.x is thread-safe — every call through a `Redis` instance is serialised on an internal mutex — but a `BRPOPLPUSH` parks the connection until the call returns. If multiple Ruby threads share a single `Redis` instance and one is blocked on a claim, the others wait behind it. In the demo, the `WorkerPool` builds a fresh `Redis` (and a fresh `RedisJobQueue`) per worker. In production, use a connection pool (such as the `connection_pool` gem) and check out a dedicated connection for blocking commands."},{"id":"inspect-queue-state-directly-in-redis","title":"Inspect queue state directly in Redis","role":"content","text":"Because the queue is just lists and hashes, you can inspect it with `redis-cli`:\n\n[code example]"},{"id":"learn-more","title":"Learn more","role":"related","text":"This example uses the following Redis commands:\n\n* [`LPUSH`](https://redis.io/docs/latest/commands/lpush) to enqueue a job ID.\n* [`BRPOPLPUSH`](https://redis.io/docs/latest/commands/brpoplpush) to atomically claim a job into the processing list.\n* [`LREM`](https://redis.io/docs/latest/commands/lrem) to remove a job from the processing list on complete or fail.\n* [`LRANGE`](https://redis.io/docs/latest/commands/lrange) and [`LLEN`](https://redis.io/docs/latest/commands/llen) to read queue depth and list contents.\n* [`LTRIM`](https://redis.io/docs/latest/commands/ltrim) to cap the completed and failed history.\n* [`HSET`](https://redis.io/docs/latest/commands/hset) and [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) for job metadata.\n* [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby) for the attempt counter and the shared totals hash.\n* [`EXPIRE`](https://redis.io/docs/latest/commands/expire) for automatic cleanup of completed and failed jobs.\n* [`PUBLISH`](https://redis.io/docs/latest/commands/publish) for job-completion notifications.\n* [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) for atomic complete, fail, and reclaim flows.\n\nSee the [`redis-rb` documentation](https://redis.io/docs/latest/develop/clients/ruby) for full client reference."}],"examples":[{"id":"the-job-queue-helper-ex0","language":"ruby","code":"require 'redis'\nrequire_relative 'job_queue'\n\nredis = Redis.new(host: 'localhost', port: 6379)\nqueue = RedisJobQueue.new(redis: redis, visibility_ms: 5000)\n\njob_id = queue.enqueue(kind: 'email', recipient: 'alice@example.com')\n\n# In a worker process:\njob = queue.claim(timeout_ms: 1000)\nif job\n  begin\n    # ... run the job ...\n    queue.complete(job, sent_at: '2026-05-11T15:00:00Z')\n  rescue StandardError => e\n    queue.fail(job, e.message)\n  end\nend\n\n# In a periodic sweeper:\nreclaimed = queue.reclaim_stuck","section_id":"the-job-queue-helper"},{"id":"data-model-ex0","language":"text","code":"queue:jobs:pending          (list)   pending job IDs, oldest at the right\nqueue:jobs:processing       (list)   claimed but not yet completed\nqueue:jobs:completed        (list)   recent successes (LTRIM-capped history)\nqueue:jobs:failed           (list)   terminally failed jobs\nqueue:jobs:job:{id}         (hash)   per-job metadata\nqueue:jobs:events           (pubsub) completion notifications\nqueue:jobs:stats            (hash)   cross-process counter totals","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"queue:jobs:job:9a4f...\n  id              = 9a4f...\n  payload         = {\"kind\":\"email\",\"recipient\":\"alice@example.com\"}\n  status          = pending | processing | completed | failed\n  attempts        = 1\n  enqueued_at_ms  = 1715441000000\n  claimed_at_ms   = 1715441000123\n  claim_token     = b3c0d1e2...        (per-claim random token)\n  completed_at_ms = 1715441000456\n  result          = {\"sent_at\":\"...\"}\n  last_error      = \"smtp timeout\"","section_id":"data-model"},{"id":"enqueueing-jobs-ex0","language":"ruby","code":"def enqueue(payload)\n  job_id = SecureRandom.hex(8)\n  now_ms = self.class.now_ms\n  meta = {\n    'id' => job_id,\n    'payload' => JSON.generate(payload),\n    'status' => 'pending',\n    'attempts' => 0,\n    'enqueued_at_ms' => now_ms,\n    'claim_token' => '',\n  }\n  @redis.pipelined do |pipe|\n    pipe.hset(meta_key(job_id), meta)\n    pipe.lpush(@pending_key, job_id)\n    pipe.hincrby(@stats_key, 'enqueued_total', 1)\n  end\n  job_id\nend","section_id":"enqueueing-jobs"},{"id":"claiming-jobs-with-brpoplpush-ex0","language":"ruby","code":"def claim(timeout_ms: 1000)\n  timeout_s = [timeout_ms / 1000.0, 0.1].max\n  job_id = @redis.brpoplpush(@pending_key, @processing_key, timeout: timeout_s)\n  return nil if job_id.nil?\n\n  token = SecureRandom.hex(8)\n  now_ms = self.class.now_ms\n  mkey = meta_key(job_id)\n  results = @redis.pipelined do |pipe|\n    pipe.hset(mkey,\n              'status', 'processing',\n              'claimed_at_ms', now_ms,\n              'claim_token', token)\n    pipe.hincrby(mkey, 'attempts', 1)\n    pipe.hgetall(mkey)\n  end\n  meta = results.last || {}\n  payload = JSON.parse(meta['payload'] || '{}') rescue {}\n  ClaimedJob.new(job_id, payload, (meta['attempts'] || '1').to_i, token)\nend","section_id":"claiming-jobs-with-brpoplpush"},{"id":"completing-jobs-ex0","language":"ruby","code":"def complete(job, result)\n  ok = @redis.evalsha(\n    @complete_sha,\n    keys: [@meta_prefix, @processing_key, @completed_key],\n    argv: [\n      job.id,\n      job.claim_token,\n      'completed',\n      self.class.now_ms,\n      JSON.generate(result),\n      @completed_ttl,\n      @completed_history,\n    ],\n  )\n  return false if ok.nil? || ok.to_i.zero?\n\n  @redis.publish(@events_channel, JSON.generate(id: job.id, status: 'completed'))\n  @redis.hincrby(@stats_key, 'completed_total', 1)\n  true\nend","section_id":"completing-jobs"},{"id":"failing-and-retrying-ex0","language":"ruby","code":"def fail(job, error)\n  retry_flag = job.attempts < @max_attempts\n  result = @redis.evalsha(\n    @fail_sha,\n    keys: [@meta_prefix, @processing_key, @pending_key, @failed_key],\n    argv: [\n      job.id,\n      job.claim_token,\n      error,\n      self.class.now_ms,\n      @completed_ttl,\n      @completed_history,\n      retry_flag ? '1' : '0',\n    ],\n  )\n  return false if result.nil? || result.to_i.zero?\n  @redis.publish(@events_channel,\n                 JSON.generate(id: job.id, status: retry_flag ? 'retry' : 'failed'))\n  @redis.hincrby(@stats_key, 'failed_total', 1) unless retry_flag\n  true\nend","section_id":"failing-and-retrying"},{"id":"reclaiming-stuck-jobs-ex0","language":"ruby","code":"def reclaim_stuck\n  reclaimed = @redis.evalsha(\n    @reclaim_sha,\n    keys: [@pending_key, @processing_key, @meta_prefix],\n    argv: [self.class.now_ms, @visibility_ms],\n  ) || []\n  @redis.hincrby(@stats_key, 'reclaimed_total', reclaimed.length) if reclaimed.any?\n  reclaimed\nend","section_id":"reclaiming-stuck-jobs"},{"id":"stats-and-history-ex0","language":"ruby","code":"def stats\n  pending, processing, completed, failed, counters = @redis.pipelined do |pipe|\n    pipe.llen(@pending_key)\n    pipe.llen(@processing_key)\n    pipe.llen(@completed_key)\n    pipe.llen(@failed_key)\n    pipe.hgetall(@stats_key)\n  end\n  {\n    'enqueued_total'  => (counters['enqueued_total']  || 0).to_i,\n    'completed_total' => (counters['completed_total'] || 0).to_i,\n    'failed_total'    => (counters['failed_total']    || 0).to_i,\n    'reclaimed_total' => (counters['reclaimed_total'] || 0).to_i,\n    'pending_depth'    => pending,\n    'processing_depth' => processing,\n    'completed_depth'  => completed,\n    'failed_depth'     => failed,\n    'visibility_ms'    => @visibility_ms,\n  }\nend","section_id":"stats-and-history"},{"id":"prerequisites-ex0","language":"bash","code":"gem install redis webrick","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir job-queue-demo && cd job-queue-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/job-queue/ruby\ncurl -O $BASE/job_queue.rb\ncurl -O $BASE/worker.rb\ncurl -O $BASE/demo_server.rb","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"ruby demo_server.rb","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"Redis job-queue demo server listening on http://127.0.0.1:8797\nUsing Redis at localhost:6379\nVisibility timeout: 5000 ms","section_id":"start-the-demo-server"},{"id":"inspect-queue-state-directly-in-redis-ex0","language":"bash","code":"# How many pending jobs?\nredis-cli LLEN queue:jobs:pending\n\n# Look at the next 5 jobs to be picked up.\nredis-cli LRANGE queue:jobs:pending -5 -1\n\n# Read a job's metadata.\nredis-cli HGETALL queue:jobs:job:9a4f0d1c\n\n# How many jobs are currently being processed?\nredis-cli LLEN queue:jobs:processing\n\n# Read the shared counter totals.\nredis-cli HGETALL queue:jobs:stats\n\n# Clear everything for this queue (be careful — this deletes work).\nredis-cli --scan --pattern 'queue:jobs:*' | xargs redis-cli DEL","section_id":"inspect-queue-state-directly-in-redis"}]}
{"schema_version":2,"id":"develop/use-cases/job-queue/rust","title":"Redis job queue with redis-rs","url":"https://redis.io/docs/latest/develop/use-cases/job-queue/rust/","summary":"Implement a Redis job queue in Rust with redis-rs","aliases":["/develop/use-cases/job-queue/redis-rs"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:50:30+01:00","children":[],"page_type":"content","content_hash":"559161b1238c674d115174110a6c3af331ca9d4d41103b3b76969008693a1fae","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis-backed job queue in Rust with the [`redis`](https://crates.io/crates/redis) crate (redis-rs). It includes a small async web server built with [`axum`](https://docs.rs/axum/) so you can enqueue jobs, watch a pool of workers drain them, and see the reclaimer recover jobs from a simulated worker crash."},{"id":"overview","title":"Overview","role":"overview","text":"A job queue lets your application offload background work — sending email, processing payments, image transcoding, ML inference, webhooks — from the request path. Producers enqueue jobs in milliseconds and return to the user; workers pull from the queue and process them on their own schedule.\n\nThat gives you:\n\n* Low-latency user-facing requests, even when downstream work is slow or bursty\n* Horizontal scale across many worker processes that share one Redis instance\n* At-least-once delivery so a worker crash doesn't lose work\n* Visibility-timeout reclaim that returns stuck jobs to the queue automatically\n* Job metadata, retry counts, and completion results in Redis hashes with TTL\n\nIn this example, each job is identified by a random hex ID and its payload, status, and result live in a Redis hash under `queue:jobs:job:{id}`. Pending IDs sit in a list, claimed IDs move atomically to a *processing* list, and completed or failed IDs land in short history lists."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. The application calls `queue.enqueue(payload).await`\n2. The helper writes the job metadata hash and `LPUSH`es the job ID onto the pending list\n3. A worker task calls `queue.claim(timeout_ms).await`\n4. The helper runs [`BLMOVE`](https://redis.io/docs/latest/commands/blmove) to atomically move the next pending ID into the processing list and writes a per-claim `claim_token` plus `claimed_at_ms` on the hash\n5. The worker runs the job and calls `queue.complete(&job, result).await` or `queue.fail(&job, error).await`\n6. `complete` removes the job from the processing list, writes the result, and `LPUSH`es the ID onto the completed history (with `LTRIM` and an `EXPIRE` on the hash for cleanup)\n7. `fail` either retries the job (back to pending) or moves it to the failed list once retries are exhausted\n\nIf a worker dies before completing a job, the job sits in the processing list with a `claimed_at_ms` older than the visibility timeout. A periodic call to `queue.reclaim_stuck().await` finds those jobs and moves them back to pending so another worker can pick them up.\n\nEvery state change holds the token: a worker that has been reclaimed cannot later complete or fail a job another worker has already claimed."},{"id":"the-job-queue-helper","title":"The job queue helper","role":"content","text":"The `RedisJobQueue` struct wraps the queue operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/job-queue/rust/src/job_queue.rs)):\n\n[code example]\n\n`ConnectionManager` is a cheap-to-clone handle that reconnects automatically. Cloning the manager is the standard way to share Redis access across `tokio::spawn`ed tasks."},{"id":"data-model","title":"Data model","role":"content","text":"Each job's state lives in a Redis hash plus a position in one of four lists:\n\n[code example]\n\nA job's hash carries:\n\n[code example]\n\nThe implementation uses:\n\n* [`LPUSH`](https://redis.io/docs/latest/commands/lpush) to add new job IDs to the pending list\n* [`BLMOVE`](https://redis.io/docs/latest/commands/blmove) to atomically claim a job into the processing list (the modern replacement for the deprecated `BRPOPLPUSH`)\n* [`LREM`](https://redis.io/docs/latest/commands/lrem) to remove a claimed job from the processing list on complete or fail\n* [`LTRIM`](https://redis.io/docs/latest/commands/ltrim) to cap the completed and failed history lists\n* [`HSET`](https://redis.io/docs/latest/commands/hset) / [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) for job metadata\n* [`EXPIRE`](https://redis.io/docs/latest/commands/expire) on completed and failed hashes for automatic cleanup\n* [`PUBLISH`](https://redis.io/docs/latest/commands/publish) on `queue:jobs:events` for completion signalling\n* [Lua scripting](https://redis.io/docs/latest/develop/programmability/eval-intro) ([`EVALSHA`](https://redis.io/docs/latest/commands/evalsha)) for the complete, fail, and reclaim flows so each runs atomically against the processing list and metadata hash"},{"id":"enqueueing-jobs","title":"Enqueueing jobs","role":"content","text":"`enqueue()` writes the metadata hash and pushes the job ID onto the pending list in one pipeline:\n\n[code example]\n\nThe payload is stored as JSON so the queue can carry arbitrary nested structures without forcing every field into a hash."},{"id":"claiming-jobs-with-blmove","title":"Claiming jobs with BLMOVE","role":"content","text":"A worker blocks until a job is available, then atomically pops it from the pending list and pushes it onto the processing list. `BLMOVE` does both in a single Redis call (it's the modern replacement for `BRPOPLPUSH`, which is deprecated in Redis 6.2+):\n\n[code example]\n\nThe `claim_token` is the worker's proof of ownership for this attempt. Every subsequent state change (complete, fail) checks it before touching the processing list, so a worker that hung past the visibility timeout cannot interfere with the new claimant."},{"id":"completing-jobs","title":"Completing jobs","role":"content","text":"`complete()` runs a Lua script so the processing-list removal, the metadata write, and the history push happen atomically:\n\n[code example]\n\nThe Lua script checks the token first and returns `0` if the worker no longer owns the job (because the reclaimer moved it back to pending). The metadata hash also gets an `EXPIRE` so completed jobs are cleaned up automatically."},{"id":"failing-and-retrying","title":"Failing and retrying","role":"content","text":"`fail()` either retries the job (back to pending) or moves it to the failed list once retries are exhausted:\n\n[code example]\n\nThe attempt counter is incremented on every `claim()`, so a job that fails three times is moved to the failed list with `attempts = 3` and the final `last_error` preserved."},{"id":"reclaiming-stuck-jobs","title":"Reclaiming stuck jobs","role":"content","text":"If a worker dies mid-job — the process is killed, the host loses power, the network partitions — the job sits in the processing list with a `claimed_at_ms` that never advances. A periodic call to `reclaim_stuck()` walks the processing list and moves any job past the visibility timeout back to pending:\n\n[code example]\n\nThe Lua script also handles a narrower race: a worker that crashed between `BLMOVE` and writing `claimed_at_ms`. Those jobs are reclaimed after `2 × visibility_ms` using `enqueued_at_ms` as a fallback timer, so they aren't stranded forever."},{"id":"stats-and-history","title":"Stats and history","role":"content","text":"`stats()` reports queue depth plus per-process counters. The counters are held in `Arc<AtomicI64>` so they're cheap to read from any task that holds a clone of the queue:\n\n[code example]\n\nThe completed and failed lists are capped via `LTRIM` so they never grow unbounded; a real deployment would also write completion events to a longer-term audit log if needed."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* Redis 6.2 or later running locally on the default port (6379). `BLMOVE` requires Redis 6.2+; on older servers, replace the call with `BRPOPLPUSH`.\n* Rust 1.75 or later.\n* The [`redis`](https://crates.io/crates/redis) crate at 0.27+ (or 0.24+ if you only need `BRPOPLPUSH`-style claims).\n\nAdd the crate dependencies to your `Cargo.toml`:\n\n[code example]\n\nThe `connection-manager` feature gives you `ConnectionManager` — a cheap, cloneable, auto-reconnecting handle that's the right primitive for sharing one Redis client across many `tokio::spawn`ed tasks."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo uses a Cargo project with sources under `src/`. Download the files from the [`rust` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/job-queue/rust) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From that directory, build and run:\n\n[code example]\n\nYou should see:\n\n[code example]\n\nOpen [http://127.0.0.1:8798](http://127.0.0.1:8798) in a browser. You can:\n\n* Enqueue jobs of different kinds (email, webhook, thumbnail, invoice) in batches.\n* Start a pool of workers with configurable size, work latency, and *failure* / *hang* rates. A non-zero hang rate simulates worker crashes.\n* Click **Run reclaim sweep** to move any timed-out processing jobs back to pending.\n* Watch pending / processing / completed / failed lists update every 800 ms.\n\nThe demo accepts a `--visibility-ms` flag to tune the visibility timeout, and reads a `REDIS_URL` environment variable if your Redis lives somewhere other than `redis://127.0.0.1:6379/`."},{"id":"the-mock-worker-pool","title":"The mock worker pool","role":"content","text":"The demo includes a small `Worker` and `WorkerPool` ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/job-queue/rust/src/worker.rs)) that stands in for whatever real background work your application would run. Each worker is a `tokio::spawn`ed task that:\n\n* Blocks on `queue.claim(500).await` for new jobs.\n* `tokio::time::sleep`s `work_latency_ms` to simulate doing the work.\n* Either completes successfully, fails (calling `queue.fail()`), or *hangs* — returning without completing or failing the job so the reclaimer has to recover it.\n\nThe pool's shutdown channel is a `tokio::sync::watch::Receiver<bool>`. Calling `pool.stop()` flips the watch value to `true`; each worker checks it before the next `claim()`. The pool's `WorkerConfig` lives behind a `tokio::sync::Mutex` so the HTTP `/workers/configure` handler can update knobs without restarting the workers.\n\nThe `fail_rate` and `hang_rate` knobs let you watch the at-least-once delivery and reclaim behaviours from the UI without writing test code."},{"id":"production-usage","title":"Production usage","role":"content","text":""},{"id":"choose-a-visibility-timeout-that-matches-your-worst-case-job-latency","title":"Choose a visibility timeout that matches your worst-case job latency","role":"content","text":"The visibility timeout has to exceed the longest real job time, with margin. If it's too short, a healthy worker that's running a slow job will get its work duplicated when the reclaimer fires. If it's too long, a real crash takes longer to detect. Most production deployments use a per-queue value tuned to the 99th-percentile job latency — for example, 2 minutes for email and 30 minutes for video transcoding."},{"id":"run-the-reclaimer-on-a-schedule","title":"Run the reclaimer on a schedule","role":"content","text":"The demo only reclaims when you click the button. In production, run `reclaim_stuck()` from a `tokio::time::interval` loop (every few seconds for fast queues, every minute for slow ones), or from each worker before it blocks on `claim()`. Both patterns work as long as *someone* runs the sweep."},{"id":"share-one-connectionmanager-across-tasks","title":"Share one `ConnectionManager` across tasks","role":"content","text":"`ConnectionManager` is cheap to `clone()` — internally it's an `Arc` around the real connection — and it handles automatic reconnection on transient failures. The helper struct stores one and clones it inside every async method, so a `WorkerPool` of 32 workers still uses a single underlying multiplexed connection."},{"id":"use-a-separate-redis-database-or-key-prefix-per-queue","title":"Use a separate Redis database or key prefix per queue","role":"content","text":"The helper takes a `queue_name` argument so you can run multiple independent queues against one Redis instance — for example, one queue per priority level, or one per job kind. Keep queue keys under a clearly-namespaced prefix (here, `queue:jobs:*`) so they're easy to inspect and easy to clear without touching application data."},{"id":"cap-the-completed-and-failed-history","title":"Cap the completed and failed history","role":"content","text":"The demo keeps the last 50 completed and 50 failed job IDs via `LTRIM`. If you need longer history for audit purposes, write completion events to a separate Redis Stream (or to an external store) and keep the in-queue history short. Stream consumer groups give you the same fan-out semantics with a much richer history."},{"id":"tune-max_attempts-per-job-kind","title":"Tune `max_attempts` per job kind","role":"content","text":"A blanket `max_attempts = 3` is a reasonable default for transient failures (network timeouts, rate limits). Jobs that talk to non-idempotent external systems — for example, posting a Stripe charge — need either application-level idempotency keys or a much lower retry count. The helper exposes `max_attempts` on `JobQueueOptions` so each queue can pick its own policy."},{"id":"inspect-queue-state-directly-in-redis","title":"Inspect queue state directly in Redis","role":"content","text":"Because the queue is just lists and hashes, you can inspect it with `redis-cli`:\n\n[code example]"},{"id":"learn-more","title":"Learn more","role":"related","text":"This example uses the following Redis commands:\n\n* [`LPUSH`](https://redis.io/docs/latest/commands/lpush) to enqueue a job ID.\n* [`BLMOVE`](https://redis.io/docs/latest/commands/blmove) to atomically claim a job into the processing list.\n* [`LREM`](https://redis.io/docs/latest/commands/lrem) to remove a job from the processing list on complete or fail.\n* [`LRANGE`](https://redis.io/docs/latest/commands/lrange) and [`LLEN`](https://redis.io/docs/latest/commands/llen) to read queue depth and list contents.\n* [`LTRIM`](https://redis.io/docs/latest/commands/ltrim) to cap the completed and failed history.\n* [`HSET`](https://redis.io/docs/latest/commands/hset) and [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) for job metadata.\n* [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby) for the attempt counter.\n* [`EXPIRE`](https://redis.io/docs/latest/commands/expire) for automatic cleanup of completed and failed jobs.\n* [`PUBLISH`](https://redis.io/docs/latest/commands/publish) for job-completion notifications.\n* [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) for atomic complete, fail, and reclaim flows.\n\nSee the [`redis-rs` crate documentation](https://docs.rs/redis/) for the client reference."}],"examples":[{"id":"the-job-queue-helper-ex0","language":"rust","code":"use redis::aio::ConnectionManager;\nuse redis::Client;\nuse serde_json::json;\n\nuse jobqueue_demo::job_queue::{JobQueueOptions, RedisJobQueue};\n\n#[tokio::main]\nasync fn main() -> redis::RedisResult<()> {\n    let client = Client::open(\"redis://127.0.0.1:6379/\")?;\n    let conn = ConnectionManager::new(client).await?;\n    let queue = RedisJobQueue::new(\n        conn,\n        JobQueueOptions {\n            queue_name: \"jobs\".to_string(),\n            visibility_ms: 5000,\n            ..Default::default()\n        },\n    );\n\n    let job_id = queue\n        .enqueue(json!({\"kind\": \"email\", \"recipient\": \"alice@example.com\"}))\n        .await?;\n    println!(\"enqueued {}\", job_id);\n\n    // In a worker task:\n    if let Some(job) = queue.claim(1000).await? {\n        // ... run the job ...\n        queue\n            .complete(&job, json!({\"sent_at\": \"2026-05-11T15:00:00Z\"}))\n            .await?;\n    }\n\n    // In a periodic sweeper:\n    let reclaimed = queue.reclaim_stuck().await?;\n    println!(\"reclaimed {} job(s)\", reclaimed.len());\n\n    Ok(())\n}","section_id":"the-job-queue-helper"},{"id":"data-model-ex0","language":"text","code":"queue:jobs:pending          (list)   pending job IDs, oldest at the right\nqueue:jobs:processing       (list)   claimed but not yet completed\nqueue:jobs:completed        (list)   recent successes (LTRIM-capped history)\nqueue:jobs:failed           (list)   terminally failed jobs\nqueue:jobs:job:{id}         (hash)   per-job metadata\nqueue:jobs:events           (pubsub) completion notifications","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"queue:jobs:job:9a4f...\n  id              = 9a4f...\n  payload         = {\"kind\":\"email\",\"recipient\":\"alice@example.com\"}\n  status          = pending | processing | completed | failed\n  attempts        = 1\n  enqueued_at_ms  = 1715441000000\n  claimed_at_ms   = 1715441000123\n  claim_token     = b3c0d1e2...        (per-claim random token)\n  completed_at_ms = 1715441000456\n  result          = {\"sent_at\":\"...\"}\n  last_error      = \"smtp timeout\"","section_id":"data-model"},{"id":"enqueueing-jobs-ex0","language":"rust","code":"pub async fn enqueue(&self, payload: Value) -> RedisResult<String> {\n    let job_id = Self::token_hex(8);\n    let now_ms = Self::now_ms();\n    let meta_key = self.meta_key(&job_id);\n    let payload_str = serde_json::to_string(&payload).unwrap_or_else(|_| \"{}\".to_string());\n\n    let fields: Vec<(&str, String)> = vec![\n        (\"id\", job_id.clone()),\n        (\"payload\", payload_str),\n        (\"status\", \"pending\".to_string()),\n        (\"attempts\", \"0\".to_string()),\n        (\"enqueued_at_ms\", now_ms.to_string()),\n        (\"claim_token\", \"\".to_string()),\n    ];\n\n    let mut conn = self.conn.clone();\n    redis::pipe()\n        .atomic()\n        .hset_multiple(&meta_key, &fields)\n        .ignore()\n        .lpush(&self.pending_key, &job_id)\n        .ignore()\n        .query_async::<_, ()>(&mut conn)\n        .await?;\n\n    self.enqueued_total.fetch_add(1, Ordering::Relaxed);\n    Ok(job_id)\n}","section_id":"enqueueing-jobs"},{"id":"claiming-jobs-with-blmove-ex0","language":"rust","code":"pub async fn claim(&self, timeout_ms: u64) -> RedisResult<Option<ClaimedJob>> {\n    let timeout_secs = (timeout_ms as f64 / 1000.0).max(0.1);\n\n    let mut conn = self.conn.clone();\n    let job_id: Option<String> = redis::cmd(\"BLMOVE\")\n        .arg(&self.pending_key)\n        .arg(&self.processing_key)\n        .arg(\"RIGHT\")\n        .arg(\"LEFT\")\n        .arg(timeout_secs)\n        .query_async(&mut conn)\n        .await?;\n\n    let job_id = match job_id { Some(id) => id, None => return Ok(None) };\n\n    let token = Self::token_hex(8);\n    let now_ms = Self::now_ms();\n    let meta_key = self.meta_key(&job_id);\n\n    let claim_fields: Vec<(&str, String)> = vec![\n        (\"status\", \"processing\".to_string()),\n        (\"claimed_at_ms\", now_ms.to_string()),\n        (\"claim_token\", token.clone()),\n    ];\n\n    let (_, _, meta): ((), i64, HashMap<String, String>) = redis::pipe()\n        .atomic()\n        .hset_multiple(&meta_key, &claim_fields)\n        .hincr(&meta_key, \"attempts\", 1)\n        .hgetall(&meta_key)\n        .query_async(&mut conn)\n        .await?;\n\n    let payload = meta\n        .get(\"payload\")\n        .and_then(|raw| serde_json::from_str::<Value>(raw).ok())\n        .unwrap_or_else(|| json!({}));\n    let attempts = meta\n        .get(\"attempts\")\n        .and_then(|raw| raw.parse::<i64>().ok())\n        .unwrap_or(1);\n\n    Ok(Some(ClaimedJob { id: job_id, payload, attempts, claim_token: token }))\n}","section_id":"claiming-jobs-with-blmove"},{"id":"completing-jobs-ex0","language":"rust","code":"pub async fn complete(&self, job: &ClaimedJob, result: Value) -> RedisResult<bool> {\n    let result_str = serde_json::to_string(&result).unwrap_or_else(|_| \"{}\".to_string());\n    let now_ms = Self::now_ms();\n\n    let mut conn = self.conn.clone();\n    let ok: i64 = Script::new(COMPLETE_SCRIPT)\n        .key(&self.meta_prefix)\n        .key(&self.processing_key)\n        .key(&self.completed_key)\n        .arg(&job.id)\n        .arg(&job.claim_token)\n        .arg(\"completed\")\n        .arg(now_ms)\n        .arg(result_str)\n        .arg(self.completed_ttl as i64)\n        .arg(self.completed_history)\n        .invoke_async(&mut conn)\n        .await?;\n\n    if ok == 0 { return Ok(false); }\n\n    let event = json!({\"id\": job.id, \"status\": \"completed\"}).to_string();\n    let _: i64 = conn.publish(&self.events_channel, event).await?;\n    self.completed_total.fetch_add(1, Ordering::Relaxed);\n    Ok(true)\n}","section_id":"completing-jobs"},{"id":"failing-and-retrying-ex0","language":"rust","code":"pub async fn fail(&self, job: &ClaimedJob, error: &str) -> RedisResult<bool> {\n    let retry = job.attempts < self.max_attempts;\n    let now_ms = Self::now_ms();\n    let retry_arg = if retry { \"1\" } else { \"0\" };\n\n    let mut conn = self.conn.clone();\n    let result: i64 = Script::new(FAIL_SCRIPT)\n        .key(&self.meta_prefix)\n        .key(&self.processing_key)\n        .key(&self.pending_key)\n        .key(&self.failed_key)\n        .arg(&job.id)\n        .arg(&job.claim_token)\n        .arg(error)\n        .arg(now_ms)\n        .arg(self.completed_ttl as i64)\n        .arg(self.completed_history)\n        .arg(retry_arg)\n        .invoke_async(&mut conn)\n        .await?;\n\n    Ok(result != 0)\n}","section_id":"failing-and-retrying"},{"id":"reclaiming-stuck-jobs-ex0","language":"rust","code":"pub async fn reclaim_stuck(&self) -> RedisResult<Vec<String>> {\n    let now_ms = Self::now_ms();\n    let mut conn = self.conn.clone();\n    let reclaimed: Vec<String> = Script::new(RECLAIM_SCRIPT)\n        .key(&self.pending_key)\n        .key(&self.processing_key)\n        .key(&self.meta_prefix)\n        .arg(now_ms)\n        .arg(self.visibility_ms as i64)\n        .invoke_async(&mut conn)\n        .await?;\n\n    if !reclaimed.is_empty() {\n        self.reclaimed_total\n            .fetch_add(reclaimed.len() as i64, Ordering::Relaxed);\n    }\n    Ok(reclaimed)\n}","section_id":"reclaiming-stuck-jobs"},{"id":"stats-and-history-ex0","language":"rust","code":"pub async fn stats(&self) -> RedisResult<Value> {\n    let mut conn = self.conn.clone();\n    let (pending, processing, completed, failed): (i64, i64, i64, i64) = redis::pipe()\n        .atomic()\n        .llen(&self.pending_key)\n        .llen(&self.processing_key)\n        .llen(&self.completed_key)\n        .llen(&self.failed_key)\n        .query_async(&mut conn)\n        .await?;\n\n    Ok(json!({\n        \"enqueued_total\": self.enqueued_total.load(Ordering::Relaxed),\n        \"completed_total\": self.completed_total.load(Ordering::Relaxed),\n        \"failed_total\": self.failed_total.load(Ordering::Relaxed),\n        \"reclaimed_total\": self.reclaimed_total.load(Ordering::Relaxed),\n        \"pending_depth\": pending,\n        \"processing_depth\": processing,\n        \"completed_depth\": completed,\n        \"failed_depth\": failed,\n        \"visibility_ms\": self.visibility_ms,\n    }))\n}","section_id":"stats-and-history"},{"id":"prerequisites-ex0","language":"toml","code":"[dependencies]\nredis = { version = \"0.27\", features = [\"tokio-comp\", \"aio\", \"connection-manager\"] }\ntokio = { version = \"1\", features = [\"full\"] }\naxum = \"0.7\"\nserde = { version = \"1.0\", features = [\"derive\"] }\nserde_json = \"1.0\"\nrand = \"0.8\"","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir -p job-queue-demo/src && cd job-queue-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/job-queue/rust\ncurl -O $BASE/Cargo.toml\ncurl -O $BASE/Cargo.lock\ncurl -o src/job_queue.rs $BASE/src/job_queue.rs\ncurl -o src/worker.rs $BASE/src/worker.rs\ncurl -o src/main.rs $BASE/src/main.rs","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"cargo run --release","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"Redis job-queue demo server listening on http://127.0.0.1:8798\nUsing Redis at redis://127.0.0.1:6379/\nVisibility timeout: 5000 ms","section_id":"start-the-demo-server"},{"id":"inspect-queue-state-directly-in-redis-ex0","language":"bash","code":"# How many pending jobs?\nredis-cli LLEN queue:jobs:pending\n\n# Look at the next 5 jobs to be picked up.\nredis-cli LRANGE queue:jobs:pending -5 -1\n\n# Read a job's metadata.\nredis-cli HGETALL queue:jobs:job:9a4f0d1c\n\n# How many jobs are currently being processed?\nredis-cli LLEN queue:jobs:processing\n\n# Clear everything for this queue (be careful — this deletes work).\nredis-cli --scan --pattern 'queue:jobs:*' | xargs redis-cli DEL","section_id":"inspect-queue-state-directly-in-redis"}]}
{"schema_version":2,"id":"develop/use-cases/leaderboard/dotnet","title":"Redis leaderboard with .NET","url":"https://redis.io/docs/latest/develop/use-cases/leaderboard/dotnet/","summary":"Implement a Redis leaderboard in C# with StackExchange.Redis and sorted sets","aliases":["/develop/use-cases/leaderboard/stackexchange.redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:54:57+01:00","children":[],"page_type":"content","content_hash":"958a763409626e37160dc2043cec8b02330e360ba531309b5db57bf36623713c","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis-backed leaderboard in C# with the [`StackExchange.Redis`](https://redis.io/docs/latest/develop/clients/dotnet) client library. It uses a sorted set to store rank order, Redis hashes to store per-user metadata, and a small local web server so you can explore the leaderboard interactively in your browser."},{"id":"overview","title":"Overview","role":"overview","text":"Leaderboards are a natural fit for Redis. A sorted set stores each member together with a numeric score, and Redis maintains the ranking order automatically.\n\nThat gives you:\n\n* Fast score updates for existing users\n* Simple top `n` leaderboard queries\n* Efficient queries for entries around a specific rank position\n* Straightforward trimming to a fixed leaderboard size\n* A clean separation between rank data and richer user metadata\n\nIn this example, the leaderboard score data is stored in a sorted set called `leaderboard:demo`, and each user's metadata is stored in a hash such as `leaderboard:demo:user:player-17`."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. Store each user ID in a sorted set with their score\n2. Store per-user metadata in a separate Redis hash keyed by user ID\n3. Fetch the highest-ranked users with a reverse range query\n4. Fetch users around a given rank by calculating a rank window\n5. Trim the leaderboard after updates so only the top configured entries remain\n\nSeparating rank data from metadata keeps leaderboard operations efficient while still letting the application render richer profile details."},{"id":"installation","title":"Installation","role":"setup","text":"Add the StackExchange.Redis NuGet package to your project:\n\n[code example]\n\nOr add it directly to your `.csproj` file:\n\n[code example]"},{"id":"the-net-class","title":"The .NET class","role":"content","text":"The `RedisLeaderboard` class wraps common leaderboard operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/leaderboard/dotnet/RedisLeaderboard.cs)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"The implementation uses two Redis structures:\n\n[code example]\n\nThe score data lives in the sorted set, while the user details live in hashes keyed by the same user ID.\n\nThe implementation uses:\n\n* [`ZADD`](https://redis.io/docs/latest/commands/zadd) to add or update leaderboard scores\n* [`ZRANGE`](https://redis.io/docs/latest/commands/zrange) with the `REV` option to fetch the highest-ranked members\n* [`ZREVRANK`](https://redis.io/docs/latest/commands/zrevrank) to find a user's rank from the top\n* [`ZREMRANGEBYRANK`](https://redis.io/docs/latest/commands/zremrangebyrank) to trim the lowest-ranked overflow entries\n* [`HSET`](https://redis.io/docs/latest/commands/hset) and [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) to store and load user metadata\n* [`DEL`](https://redis.io/docs/latest/commands/del) to remove metadata for trimmed or deleted users"},{"id":"leaderboard-implementation","title":"Leaderboard implementation","role":"content","text":"The `UpsertUser()` method writes the score, updates metadata, and then trims the board if it exceeds the configured limit:\n\n[code example]\n\nTo fetch users around a rank, the implementation converts the requested rank and count into a reverse range window:\n\n[code example]"},{"id":"metadata-design","title":"Metadata design","role":"content","text":"The leaderboard stores only user IDs and scores in the sorted set. Richer details stay in a separate per-user hash. That means the same user ID can be ranked efficiently while still exposing extra fields such as:\n\n* Display name\n* Short description\n* Team or country\n* Avatar URL\n* Other lightweight profile fields\n\nThis is a useful pattern when the ranking view and the profile view need different data shapes."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of three files. Download them from the [`dotnet` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/leaderboard/dotnet) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"A local demo server ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/leaderboard/dotnet/Program.cs)) is included to show the leaderboard in action:\n\n[code example]\n\nThe demo provides an interactive web interface where you can:\n\n* Add or update a player score and metadata\n* Increase a player's score incrementally\n* View the top `n` players on the leaderboard\n* View the `n` players around a chosen rank\n* Change the maximum number of entries the leaderboard keeps\n* Reset the demo dataset to a known starting state\n\nThe demo assumes Redis is running on `localhost:6379` but you can specify a different host and port using the `--redis-host HOST` and `--redis-port PORT` command-line arguments. Visit `http://localhost:8080` in your browser to try it out."},{"id":"production-usage","title":"Production usage","role":"content","text":"This guide uses a deliberately small local demo so you can focus on the Redis leaderboard pattern. In production, you will usually want to add more validation, tighter concurrency control, and application-specific lifecycle rules."},{"id":"decide-how-ties-should-behave","title":"Decide how ties should behave","role":"content","text":"Redis sorted sets order primarily by score. When two members have the same score, Redis uses the member value as a secondary ordering rule. If your application needs a different tie-breaker, you may want to encode it in the score or store additional state."},{"id":"consider-how-you-expire-or-archive-old-data","title":"Consider how you expire or archive old data","role":"content","text":"Some leaderboards are permanent, while others reset daily, weekly, or seasonally. Depending on your use case, you may want to:\n\n* Namespace keys by season or event\n* Snapshot historical results elsewhere\n* Rebuild the current leaderboard from upstream data"},{"id":"keep-metadata-lightweight","title":"Keep metadata lightweight","role":"content","text":"Per-user hashes work best for small, frequently accessed profile details. Large profile documents or rarely used attributes are often better kept in another store, with Redis holding only the fields needed to render the leaderboard quickly."}],"examples":[{"id":"installation-ex0","language":"bash","code":"dotnet add package StackExchange.Redis","section_id":"installation"},{"id":"installation-ex1","language":"xml","code":"<PackageReference Include=\"StackExchange.Redis\" Version=\"2.7.33\" />","section_id":"installation"},{"id":"the-net-class-ex0","language":"csharp","code":"using StackExchange.Redis;\n\nvar redis = ConnectionMultiplexer.Connect(\"localhost:6379\");\nvar db = redis.GetDatabase();\n\nvar board = new RedisLeaderboard(\n    db: db,\n    key: \"leaderboard:demo\",\n    maxEntries: 100\n);\n\nvar entry = board.UpsertUser(\n    userId: \"player-1\",\n    score: 1200,\n    metadata: new Dictionary<string, string>\n    {\n        [\"name\"] = \"Ada\",\n        [\"description\"] = \"Solves production incidents before breakfast.\"\n    }\n);\n\nvar topPlayers = board.GetTop(5);","section_id":"the-net-class"},{"id":"data-model-ex0","language":"text","code":"leaderboard:demo\n  player-1 => 1225\n  player-2 => 1180\n  player-3 => 1105\n\nleaderboard:demo:user:player-1\n  name = Ada\n  description = Solves production incidents before breakfast.","section_id":"data-model"},{"id":"leaderboard-implementation-ex0","language":"csharp","code":"public LeaderboardEntry UpsertUser(\n    string userId,\n    double score,\n    IReadOnlyDictionary<string, string>? metadata = null)\n{\n    var payload = CoerceMetadata(metadata);\n    var batch = _db.CreateBatch();\n    var scoreTask = batch.SortedSetAddAsync(_key, userId, score);\n    Task metadataTask = Task.CompletedTask;\n    if (payload.Count > 0)\n    {\n        metadataTask = batch.HashSetAsync(\n            MetadataKey(userId),\n            payload.Select(pair => new HashEntry(pair.Key, pair.Value)).ToArray());\n    }\n    batch.Execute();\n    Task.WaitAll(scoreTask, metadataTask);\n\n    var trimmedUserIds = TrimToMaxEntries();\n    var entry = GetUserEntry(userId);\n    return entry with { TrimmedUserIds = trimmedUserIds };\n}","section_id":"leaderboard-implementation"},{"id":"leaderboard-implementation-ex1","language":"csharp","code":"public IReadOnlyList<LeaderboardEntry> GetAroundRank(int rank, int count)\n{\n    var normalizedRank = NormalizePositiveInt(rank, nameof(rank));\n    var normalizedCount = NormalizePositiveInt(count, nameof(count));\n    var totalEntries = (int)GetSize();\n\n    if (totalEntries <= normalizedCount)\n    {\n        return ListAll();\n    }\n\n    var halfWindow = normalizedCount / 2;\n    var start = Math.Max(0, normalizedRank - 1 - halfWindow);\n    var maxStart = totalEntries - normalizedCount;\n    if (start > maxStart)\n    {\n        start = maxStart;\n    }\n    var end = start + normalizedCount - 1;\n\n    var entries = _db.SortedSetRangeByRankWithScores(\n        _key,\n        start,\n        end,\n        Order.Descending);\n    return HydrateEntries(entries, start + 1);\n}","section_id":"leaderboard-implementation"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir leaderboard-demo && cd leaderboard-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/leaderboard/dotnet\ncurl -O $BASE/RedisLeaderboard.cs\ncurl -O $BASE/Program.cs\ncurl -O $BASE/LeaderboardDemo.csproj","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"dotnet run","section_id":"start-the-demo-server"}]}
{"schema_version":2,"id":"develop/use-cases/leaderboard/go","title":"Redis leaderboard with Go","url":"https://redis.io/docs/latest/develop/use-cases/leaderboard/go/","summary":"Implement a Redis leaderboard in Go with go-redis and sorted sets","aliases":["/develop/use-cases/leaderboard/go-redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:54:57+01:00","children":[],"page_type":"content","content_hash":"d1ee9be2a37ead57882fa97b68a934b49b0173b550c2e16492b9d6451736213f","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis-backed leaderboard in Go with [`go-redis`](https://redis.io/docs/latest/develop/clients/go). It uses a sorted set to store rank order, Redis hashes to store per-user metadata, and an exported local demo server so you can explore the leaderboard interactively in your browser."},{"id":"overview","title":"Overview","role":"overview","text":"Leaderboards are a natural fit for Redis. A sorted set stores each member together with a numeric score, and Redis maintains the ranking order automatically.\n\nThat gives you:\n\n* Fast score updates for existing users\n* Simple top `n` leaderboard queries\n* Efficient queries for entries around a specific rank position\n* Straightforward trimming to a fixed leaderboard size\n* A clean separation between rank data and richer user metadata\n\nIn this example, the leaderboard score data is stored in a sorted set called `leaderboard:demo`, and each user's metadata is stored in a hash such as `leaderboard:demo:user:player-17`."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. Store each user ID in a sorted set with their score\n2. Store per-user metadata in a separate Redis hash keyed by user ID\n3. Fetch the highest-ranked users with a reverse range query\n4. Fetch users around a given rank by calculating a rank window\n5. Trim the leaderboard after updates so only the top configured entries remain\n\nSeparating rank data from metadata keeps leaderboard operations efficient while still letting the application render richer profile details."},{"id":"installation","title":"Installation","role":"setup","text":"Install the `go-redis` package:\n\n[code example]"},{"id":"the-go-package","title":"The Go package","role":"content","text":"The `RedisLeaderboard` type wraps common leaderboard operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/leaderboard/go/leaderboard.go)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"The implementation uses two Redis structures:\n\n[code example]\n\nThe score data lives in the sorted set, while the user details live in hashes keyed by the same user ID.\n\nThe implementation uses:\n\n* [`ZADD`](https://redis.io/docs/latest/commands/zadd) to add or update leaderboard scores\n* [`ZRANGE`](https://redis.io/docs/latest/commands/zrange) with the `REV` option to fetch the highest-ranked members\n* [`ZREVRANK`](https://redis.io/docs/latest/commands/zrevrank) to find a user's rank from the top\n* [`ZREMRANGEBYRANK`](https://redis.io/docs/latest/commands/zremrangebyrank) to trim the lowest-ranked overflow entries\n* [`HSET`](https://redis.io/docs/latest/commands/hset) and [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) to store and load user metadata\n* [`DEL`](https://redis.io/docs/latest/commands/del) to remove metadata for trimmed or deleted users"},{"id":"leaderboard-implementation","title":"Leaderboard implementation","role":"content","text":"The `UpsertUser()` method writes the score, updates metadata, and then trims the board if it exceeds the configured limit:\n\n[code example]\n\nTo fetch users around a rank, the implementation converts the requested rank and count into a reverse range window:\n\n[code example]\n\nGo's `context.Context` is passed to every call, allowing you to set deadlines, propagate cancellation, and control request lifetimes explicitly."},{"id":"metadata-design","title":"Metadata design","role":"content","text":"The leaderboard stores only user IDs and scores in the sorted set. Richer details stay in a separate per-user hash. That means the same user ID can be ranked efficiently while still exposing extra fields such as:\n\n* Display name\n* Short description\n* Team or country\n* Avatar URL\n* Other lightweight profile fields\n\nThis is a useful pattern when the ranking view and the profile view need different data shapes."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of four files. Download them from the [`go` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/leaderboard/go) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"A local demo server ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/leaderboard/go/demo_server.go)) is included to show the leaderboard in action.\n\nTo run the demo, create a small `main.go` file in a separate directory that imports this package and calls `RunDemoServer()`:\n\n[code example]\n\nThen build and run:\n\n[code example]\n\nThe demo server uses the Go standard library for HTTP handling and exposes a small interactive page where you can:\n\n* Add or update a player score and metadata\n* Increase a player's score incrementally\n* View the top `n` players on the leaderboard\n* View the `n` players around a chosen rank\n* Change the maximum number of entries the leaderboard keeps\n* Reset the demo dataset to a known starting state\n\nAfter starting the server, visit `http://localhost:8080`."},{"id":"production-usage","title":"Production usage","role":"content","text":"This guide uses a deliberately small local demo so you can focus on the Redis leaderboard pattern. In production, you will usually want to add more validation, tighter concurrency control, and application-specific lifecycle rules."},{"id":"decide-how-ties-should-behave","title":"Decide how ties should behave","role":"content","text":"Redis sorted sets order primarily by score. When two members have the same score, Redis uses the member value as a secondary ordering rule. If your application needs a different tie-breaker, you may want to encode it in the score or store additional state."},{"id":"consider-how-you-expire-or-archive-old-data","title":"Consider how you expire or archive old data","role":"content","text":"Some leaderboards are permanent, while others reset daily, weekly, or seasonally. Depending on your use case, you may want to:\n\n* Namespace keys by season or event\n* Snapshot historical results elsewhere\n* Rebuild the current leaderboard from upstream data"},{"id":"keep-metadata-lightweight","title":"Keep metadata lightweight","role":"content","text":"Per-user hashes work best for small, frequently accessed profile details. Large profile documents or rarely used attributes are often better kept in another store, with Redis holding only the fields needed to render the leaderboard quickly."}],"examples":[{"id":"installation-ex0","language":"bash","code":"go get github.com/redis/go-redis/v9","section_id":"installation"},{"id":"the-go-package-ex0","language":"go","code":"package main\n\nimport (\n    \"context\"\n    \"fmt\"\n    \"log\"\n\n    leaderboard \"leaderboard\"\n\n    \"github.com/redis/go-redis/v9\"\n)\n\nfunc main() {\n    ctx := context.Background()\n\n    rdb := redis.NewClient(&redis.Options{\n        Addr: \"localhost:6379\",\n    })\n\n    board := leaderboard.NewRedisLeaderboard(leaderboard.Config{\n        Client:     rdb,\n        Key:        \"leaderboard:demo\",\n        MaxEntries: 100,\n    })\n\n    _, err := board.UpsertUser(ctx, \"player-1\", 1200, map[string]string{\n        \"name\":        \"Ada\",\n        \"description\": \"Solves production incidents before breakfast.\",\n    })\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    if _, err := board.IncrementScore(ctx, \"player-1\", 25, nil); err != nil {\n        log.Fatal(err)\n    }\n\n    topPlayers, err := board.GetTop(ctx, 5)\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    fmt.Println(topPlayers)\n}","section_id":"the-go-package"},{"id":"data-model-ex0","language":"text","code":"leaderboard:demo\n  player-1 => 1225\n  player-2 => 1180\n  player-3 => 1105\n\nleaderboard:demo:user:player-1\n  name = Ada\n  description = Solves production incidents before breakfast.","section_id":"data-model"},{"id":"leaderboard-implementation-ex0","language":"go","code":"func (lb *RedisLeaderboard) UpsertUser(\n    ctx context.Context,\n    userID string,\n    score float64,\n    metadata map[string]string,\n) (*Entry, error) {\n    metadataKey := lb.metadataKey(userID)\n\n    pipe := lb.client.TxPipeline()\n    pipe.ZAdd(ctx, lb.key, redis.Z{\n        Score:  score,\n        Member: userID,\n    })\n    if len(metadata) > 0 {\n        payload := make(map[string]any, len(metadata))\n        for field, value := range metadata {\n            payload[field] = value\n        }\n        pipe.HSet(ctx, metadataKey, payload)\n    }\n    if _, err := pipe.Exec(ctx); err != nil {\n        return nil, err\n    }\n\n    trimmedUserIDs, err := lb.trimToMaxEntries(ctx)\n    if err != nil {\n        return nil, err\n    }\n\n    entry, err := lb.GetUserEntry(ctx, userID)\n    if err != nil {\n        return nil, err\n    }\n    if entry != nil {\n        entry.TrimmedUserIDs = trimmedUserIDs\n    }\n    return entry, nil\n}","section_id":"leaderboard-implementation"},{"id":"leaderboard-implementation-ex1","language":"go","code":"func (lb *RedisLeaderboard) GetAroundRank(\n    ctx context.Context,\n    rank int,\n    count int,\n) ([]Entry, error) {\n    normalizedRank, err := normalizePositiveInt(rank, \"rank\")\n    if err != nil {\n        return nil, err\n    }\n    normalizedCount, err := normalizePositiveInt(count, \"count\")\n    if err != nil {\n        return nil, err\n    }\n\n    totalEntries, err := lb.GetSize(ctx)\n    if err != nil {\n        return nil, err\n    }\n\n    if totalEntries <= int64(normalizedCount) {\n        return lb.ListAll(ctx)\n    }\n\n    halfWindow := normalizedCount / 2\n    start := max(0, normalizedRank-1-halfWindow)\n    maxStart := int(totalEntries) - normalizedCount\n    if start > maxStart {\n        start = maxStart\n    }\n    end := start + normalizedCount - 1\n\n    entries, err := lb.client.ZRangeArgsWithScores(ctx, redis.ZRangeArgs{\n        Key:   lb.key,\n        Start: start,\n        Stop:  end,\n        Rev:   true,\n    }).Result()\n    if err != nil {\n        return nil, err\n    }\n\n    return lb.hydrateEntries(ctx, entries, start+1)\n}","section_id":"leaderboard-implementation"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir leaderboard-demo && cd leaderboard-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/leaderboard/go\ncurl -O $BASE/leaderboard.go\ncurl -O $BASE/demo_server.go\ncurl -O $BASE/go.mod\ncurl -O $BASE/go.sum","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"go","code":"package main\n\nimport leaderboard \"leaderboard\"\n\nfunc main() { leaderboard.RunDemoServer() }","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"bash","code":"# Install dependencies\ngo get github.com/redis/go-redis/v9\n\n# Build and run the demo server\ngo build -o demo ./...\n./demo","section_id":"start-the-demo-server"}]}
{"schema_version":2,"id":"develop/use-cases/leaderboard","title":"Redis leaderboard","url":"https://redis.io/docs/latest/develop/use-cases/leaderboard/","summary":"Build ranked leaderboards with Redis sorted sets and user metadata","tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:54:57+01:00","children":[{"id":"develop/use-cases/leaderboard/redis-py","summary":"Implement a Redis leaderboard in Python with redis-py and sorted sets","title":"Redis leaderboard with redis-py","url":"https://redis.io/docs/latest/develop/use-cases/leaderboard/redis-py/"},{"id":"develop/use-cases/leaderboard/nodejs","summary":"Implement a Redis leaderboard in JavaScript with node-redis and sorted sets","title":"Redis leaderboard with node-redis","url":"https://redis.io/docs/latest/develop/use-cases/leaderboard/nodejs/"},{"id":"develop/use-cases/leaderboard/go","summary":"Implement a Redis leaderboard in Go with go-redis and sorted sets","title":"Redis leaderboard with Go","url":"https://redis.io/docs/latest/develop/use-cases/leaderboard/go/"},{"id":"develop/use-cases/leaderboard/java-jedis","summary":"Implement a Redis leaderboard in Java with Jedis and sorted sets","title":"Redis leaderboard with Java and Jedis","url":"https://redis.io/docs/latest/develop/use-cases/leaderboard/java-jedis/"},{"id":"develop/use-cases/leaderboard/java-lettuce","summary":"Implement async and reactive Redis leaderboards in Java with Lettuce and sorted sets","title":"Redis leaderboard with Java (Lettuce)","url":"https://redis.io/docs/latest/develop/use-cases/leaderboard/java-lettuce/"},{"id":"develop/use-cases/leaderboard/dotnet","summary":"Implement a Redis leaderboard in C# with StackExchange.Redis and sorted sets","title":"Redis leaderboard with .NET","url":"https://redis.io/docs/latest/develop/use-cases/leaderboard/dotnet/"},{"id":"develop/use-cases/leaderboard/php","summary":"Implement a Redis leaderboard in PHP with Predis and sorted sets","title":"Redis leaderboard with PHP","url":"https://redis.io/docs/latest/develop/use-cases/leaderboard/php/"},{"id":"develop/use-cases/leaderboard/ruby","summary":"Implement a Redis leaderboard in Ruby with redis-rb and sorted sets","title":"Redis leaderboard with Ruby","url":"https://redis.io/docs/latest/develop/use-cases/leaderboard/ruby/"},{"id":"develop/use-cases/leaderboard/rust","summary":"Implement sync and async Redis leaderboards in Rust with redis-rs and sorted sets","title":"Redis leaderboard with Rust","url":"https://redis.io/docs/latest/develop/use-cases/leaderboard/rust/"}],"page_type":"content","content_hash":"f3109f1a330b5fae766dc0216480752dee899aa078d74579c1b508c40fa0a6f4","sections":[{"id":"when-to-use-a-redis-leaderboard","title":"When to use a Redis leaderboard","role":"overview","text":"Use a Redis leaderboard when you need to rank entities — players, products, sellers, stocks — by a score that changes continuously and serve those rankings to users in real time."},{"id":"why-the-problem-is-hard","title":"Why the problem is hard","role":"content","text":"Computing a single entity's rank in a relational database requires an `ORDER BY` across the full table — O(N) at best — and at millions of rows this pushes query time into seconds. Caching the result doesn't help because scores change on every user action, so any TTL-based cache is immediately stale or triggers thundering-herd refreshes on expiry.\n\nLocal in-process data structures break behind load balancers because no single instance holds the complete ranking. A dedicated OLAP or leaderboard service could solve the ranking problem but adds operational overhead for what is fundamentally a single sorted data structure.\n\nThis use case is also distinct from [Redis Streams](https://redis.io/docs/latest/develop/data-types/streams) (which record event history but don't maintain ranked state) and from the probabilistic [Top-K](https://redis.io/docs/latest/develop/data-types/probabilistic/top-k) data structure (which approximates heavy hitters but cannot return exact ranks or neighborhood queries)."},{"id":"what-you-can-expect-from-a-redis-solution","title":"What you can expect from a Redis solution","role":"content","text":"You can:\n\n-   Show any user their exact global rank and the neighbors immediately above and below them, updated on every score change.\n-   Serve top-N pages to thousands of concurrent readers without degrading write performance.\n-   Run daily, weekly, and monthly boards that create and expire themselves automatically.\n-   Aggregate time-windowed boards into composite rankings without application-side sorting.\n-   Support millions of ranked members in roughly 500 MB of RAM.\n-   Add leaderboard functionality to an existing deployment without provisioning new infrastructure."},{"id":"how-redis-supports-the-solution","title":"How Redis supports the solution","role":"content","text":"In practice, the leaderboard is stored as a Redis sorted set — one key per board — with members keyed by entity ID and scored by the value you want to rank on. A companion hash per entity holds metadata (name, avatar, tier) so the sorted set stays lightweight.\n\nRedis provides the following features that make it a good fit for leaderboards:\n\n-   [Sorted sets](https://redis.io/docs/latest/develop/data-types/sorted-sets) maintain rank order automatically, with\n    [`ZADD`](https://redis.io/docs/latest/commands/zadd), [`ZRANGE`](https://redis.io/docs/latest/commands/zrange), and\n    [`ZREVRANK`](https://redis.io/docs/latest/commands/zrevrank) all running in O(log N) regardless of set size.\n-   [`ZINCRBY`](https://redis.io/docs/latest/commands/zincrby) updates scores atomically in place — no\n    read-modify-write cycle and no cache invalidation.\n-   [`ZRANGE`](https://redis.io/docs/latest/commands/zrange) with `REV` and `LIMIT` serves both top-N and\n    \"around me\" neighborhood queries in a single command.\n-   [`EXPIRE`](https://redis.io/docs/latest/commands/expire) on a per-window key cleans up daily, weekly,\n    and monthly boards automatically, and\n    [`ZUNIONSTORE`](https://redis.io/docs/latest/commands/zunionstore) aggregates windows without\n    application-level coordination.\n-   [Hashes](https://redis.io/docs/latest/develop/data-types/hashes) store entity metadata so the sorted set\n    stays small and fast.\n-   Sub-millisecond latency on reads and writes means the leaderboard sits on the request path\n    without adding meaningful delay. If Redis is already in the stack, the leaderboard adds zero\n    marginal infrastructure."},{"id":"ecosystem","title":"Ecosystem","role":"content","text":"The following libraries and frameworks provide Redis sorted set integrations suitable for leaderboards:\n\n-   **Java**:\n    [Spring Data Redis (`ZSetOperations`)](https://docs.spring.io/spring-data/redis/reference/redis/template.html)\n-   **Python**: [redis-py](https://redis.readthedocs.io/),\n    [redis-om-python](https://github.com/redis/redis-om-python)\n-   **Node.js**: [ioredis](https://github.com/redis/ioredis),\n    [node-redis](https://github.com/redis/node-redis)\n-   **Infrastructure**: [Redis Cloud](https://redis.io/docs/latest/operate/rc)\n    (Active-Active for multi-region with conflict-free replicated sorted sets)"},{"id":"code-examples-to-build-your-own-redis-leaderboard","title":"Code examples to build your own Redis leaderboard","role":"example","text":"The following guides show how to build a simple Redis-backed leaderboard.\nEach guide includes a runnable interactive demo for each of the following client libraries:\n\n* [redis-py (Python)](https://redis.io/docs/latest/develop/use-cases/leaderboard/redis-py)\n* [node-redis (Node.js)](https://redis.io/docs/latest/develop/use-cases/leaderboard/nodejs)\n* [go-redis (Go)](https://redis.io/docs/latest/develop/use-cases/leaderboard/go)\n* [Jedis (Java)](https://redis.io/docs/latest/develop/use-cases/leaderboard/java-jedis)\n* [Lettuce (Java)](https://redis.io/docs/latest/develop/use-cases/leaderboard/java-lettuce)\n* [StackExchange.Redis (C#)](https://redis.io/docs/latest/develop/use-cases/leaderboard/dotnet)\n* [Predis (PHP)](https://redis.io/docs/latest/develop/use-cases/leaderboard/php)\n* [redis-rb (Ruby)](https://redis.io/docs/latest/develop/use-cases/leaderboard/ruby)\n* [redis-rs (Rust)](https://redis.io/docs/latest/develop/use-cases/leaderboard/rust)"}],"examples":[]}
{"schema_version":2,"id":"develop/use-cases/leaderboard/java-jedis","title":"Redis leaderboard with Java and Jedis","url":"https://redis.io/docs/latest/develop/use-cases/leaderboard/java-jedis/","summary":"Implement a Redis leaderboard in Java with Jedis and sorted sets","aliases":["/develop/use-cases/leaderboard/java","/develop/use-cases/leaderboard/jedis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:54:57+01:00","children":[],"page_type":"content","content_hash":"34278fed74b21e75968294a707a5cb010d818114e9da7ca065997fd15e48762a","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis-backed leaderboard in Java with the [`Jedis`](https://redis.io/docs/latest/develop/clients/jedis) client library. It uses a sorted set to store rank order, Redis hashes to store per-user metadata, and a small local web server so you can explore the leaderboard interactively in your browser."},{"id":"overview","title":"Overview","role":"overview","text":"Leaderboards are a classic Redis pattern. A sorted set stores each member together with a numeric score, and Redis maintains the ranking order automatically.\n\nThat gives you:\n\n* Fast score updates for existing users\n* Simple top `n` leaderboard queries\n* Efficient queries for entries around a specific rank position\n* Straightforward trimming to a fixed leaderboard size\n* A clean separation between rank data and richer user metadata\n\nIn this example, the leaderboard score data is stored in a sorted set called `leaderboard:demo`, and each user's metadata is stored in a hash such as `leaderboard:demo:user:player-17`."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. Store each user ID in a sorted set with their score\n2. Store per-user metadata in a separate Redis hash keyed by user ID\n3. Fetch the highest-ranked users with a reverse range query\n4. Fetch users around a given rank by calculating a rank window\n5. Trim the leaderboard after updates so only the top configured entries remain\n\nSeparating rank data from metadata keeps leaderboard operations efficient while still letting the application render richer profile details."},{"id":"installation","title":"Installation","role":"setup","text":"Add the Jedis dependency to your project:\n\n* If you use **Maven**:\n\n  [code example]\n\n* If you use **Gradle**:\n\n  [code example]"},{"id":"the-java-leaderboard","title":"The Java leaderboard","role":"content","text":"The `RedisLeaderboard` class wraps common leaderboard operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/leaderboard/java-jedis/RedisLeaderboard.java)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"The implementation uses two Redis structures:\n\n[code example]\n\nThe score data lives in the sorted set, while the user details live in hashes keyed by the same user ID.\n\nThe implementation uses:\n\n* [`ZADD`](https://redis.io/docs/latest/commands/zadd) to add or update leaderboard scores\n* [`ZRANGE`](https://redis.io/docs/latest/commands/zrange) with the `REV` option to fetch the highest-ranked members\n* [`ZREVRANK`](https://redis.io/docs/latest/commands/zrevrank) to find a user's rank from the top\n* [`ZREMRANGEBYRANK`](https://redis.io/docs/latest/commands/zremrangebyrank) to trim the lowest-ranked overflow entries\n* [`HSET`](https://redis.io/docs/latest/commands/hset) and [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) to store and load user metadata\n* [`DEL`](https://redis.io/docs/latest/commands/del) to remove metadata for trimmed or deleted users"},{"id":"leaderboard-implementation","title":"Leaderboard implementation","role":"content","text":"The `upsertUser()` method writes the score, updates metadata, and then trims the board if it exceeds the configured limit:\n\n[code example]\n\nTo fetch users around a rank, the implementation converts the requested rank and count into a reverse range window:\n\n[code example]\n\nJedis operations are synchronous and thread-safe when you use `JedisPool`, which manages a pool of connections internally."},{"id":"metadata-design","title":"Metadata design","role":"content","text":"The leaderboard stores only user IDs and scores in the sorted set. Richer details stay in a separate per-user hash. That means the same user ID can be ranked efficiently while still exposing extra fields such as:\n\n* Display name\n* Short description\n* Team or country\n* Avatar URL\n* Other lightweight profile fields\n\nThis is a useful pattern when the ranking view and the profile view need different data shapes."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of two Java files. Download them from the [`java-jedis` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/leaderboard/java-jedis) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"A local demo server ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/leaderboard/java-jedis/DemoServer.java)) is included to show the leaderboard in action:\n\n[code example]\n\nThe demo provides an interactive web interface where you can:\n\n* Add or update a player score and metadata\n* Increase a player's score incrementally\n* View the top `n` players on the leaderboard\n* View the `n` players around a chosen rank\n* Change the maximum number of entries the leaderboard keeps\n* Reset the demo dataset to a known starting state\n\nThe demo assumes Redis is running on `localhost:6379` but you can specify a different host and port using the `--redis-host HOST` and `--redis-port PORT` command-line arguments. Visit `http://localhost:8080` in your browser to try it out."},{"id":"production-usage","title":"Production usage","role":"content","text":"This guide uses a deliberately small local demo so you can focus on the Redis leaderboard pattern. In production, you will usually want to add more validation, tighter concurrency control, and application-specific lifecycle rules."},{"id":"decide-how-ties-should-behave","title":"Decide how ties should behave","role":"content","text":"Redis sorted sets order primarily by score. When two members have the same score, Redis uses the member value as a secondary ordering rule. If your application needs a different tie-breaker, you may want to encode it in the score or store additional state."},{"id":"consider-how-you-expire-or-archive-old-data","title":"Consider how you expire or archive old data","role":"content","text":"Some leaderboards are permanent, while others reset daily, weekly, or seasonally. Depending on your use case, you may want to:\n\n* Namespace keys by season or event\n* Snapshot historical results elsewhere\n* Rebuild the current leaderboard from upstream data"},{"id":"keep-metadata-lightweight","title":"Keep metadata lightweight","role":"content","text":"Per-user hashes work best for small, frequently accessed profile details. Large profile documents or rarely used attributes are often better kept in another store, with Redis holding only the fields needed to render the leaderboard quickly."}],"examples":[{"id":"installation-ex0","language":"xml","code":"<dependency>\n      <groupId>redis.clients</groupId>\n      <artifactId>jedis</artifactId>\n      <version>5.2.0</version>\n  </dependency>","section_id":"installation"},{"id":"installation-ex1","language":"groovy","code":"implementation 'redis.clients:jedis:5.2.0'","section_id":"installation"},{"id":"the-java-leaderboard-ex0","language":"java","code":"import java.util.Map;\n\nimport redis.clients.jedis.JedisPool;\n\npublic class Main {\n    public static void main(String[] args) {\n        JedisPool jedisPool = new JedisPool(\"localhost\", 6379);\n\n        RedisLeaderboard board = new RedisLeaderboard(\n                jedisPool,\n                \"leaderboard:demo\",\n                100\n        );\n\n        board.upsertUser(\n                \"player-1\",\n                1200,\n                Map.of(\n                        \"name\", \"Ada\",\n                        \"description\", \"Solves production incidents before breakfast.\"\n                )\n        );\n\n        board.incrementScore(\"player-1\", 25, Map.of());\n        System.out.println(board.getTop(5));\n    }\n}","section_id":"the-java-leaderboard"},{"id":"data-model-ex0","language":"text","code":"leaderboard:demo\n  player-1 => 1225\n  player-2 => 1180\n  player-3 => 1105\n\nleaderboard:demo:user:player-1\n  name = Ada\n  description = Solves production incidents before breakfast.","section_id":"data-model"},{"id":"leaderboard-implementation-ex0","language":"java","code":"public LeaderboardEntry upsertUser(\n        String userId,\n        double score,\n        Map<String, String> metadata\n) {\n    Map<String, String> payload = coerceMetadata(metadata);\n\n    try (Jedis jedis = jedisPool.getResource()) {\n        Transaction tx = jedis.multi();\n        tx.zadd(key, score, userId);\n        if (!payload.isEmpty()) {\n            tx.hset(metadataKey(userId), payload);\n        }\n        tx.exec();\n    }\n\n    List<String> trimmedUserIds = trimToMaxEntries();\n    LeaderboardEntry entry = getUserEntry(userId);\n    if (entry != null) {\n        entry = entry.withTrimmedUserIds(trimmedUserIds);\n    }\n    return entry;\n}","section_id":"leaderboard-implementation"},{"id":"leaderboard-implementation-ex1","language":"java","code":"public List<LeaderboardEntry> getAroundRank(int rank, int count) {\n    int normalizedRank = normalizePositiveInt(rank, \"rank\");\n    int normalizedCount = normalizePositiveInt(count, \"count\");\n    long totalEntries = getSize();\n\n    if (totalEntries <= normalizedCount) {\n        return listAll();\n    }\n\n    int halfWindow = normalizedCount / 2;\n    int start = Math.max(0, normalizedRank - 1 - halfWindow);\n    int maxStart = (int) totalEntries - normalizedCount;\n    if (start > maxStart) {\n        start = maxStart;\n    }\n    int end = start + normalizedCount - 1;\n\n    try (Jedis jedis = jedisPool.getResource()) {\n        Set<Tuple> entries = jedis.zrangeWithScores(\n                key,\n                ZRangeParams.zrangeParams(start, end).rev()\n        );\n        return hydrateEntries(entries, start + 1);\n    }\n}","section_id":"leaderboard-implementation"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir leaderboard-demo && cd leaderboard-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/leaderboard/java-jedis\ncurl -O $BASE/RedisLeaderboard.java\ncurl -O $BASE/DemoServer.java","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"# Compile\njavac -cp jedis-5.2.0.jar RedisLeaderboard.java DemoServer.java\n\n# Run the demo server\njava -cp .:jedis-5.2.0.jar DemoServer","section_id":"start-the-demo-server"}]}
{"schema_version":2,"id":"develop/use-cases/leaderboard/java-lettuce","title":"Redis leaderboard with Java (Lettuce)","url":"https://redis.io/docs/latest/develop/use-cases/leaderboard/java-lettuce/","summary":"Implement async and reactive Redis leaderboards in Java with Lettuce and sorted sets","aliases":["/develop/use-cases/leaderboard/lettuce"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:54:57+01:00","children":[],"page_type":"content","content_hash":"569be3e7771c313b34906eb3f64a030ccd05193c4bb7ca507defadf8c1b7e4e9","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement Redis-backed leaderboards in Java with the [`Lettuce`](https://redis.io/docs/latest/develop/clients/lettuce) client library. It focuses on asynchronous and reactive APIs, which are the recommended Lettuce usage patterns in these docs."},{"id":"overview","title":"Overview","role":"overview","text":"Leaderboards are a classic Redis pattern. A sorted set stores each member together with a numeric score, and Redis maintains the ranking order automatically.\n\nThat gives you:\n\n* Fast score updates for existing users\n* Simple top `n` leaderboard queries\n* Efficient queries for entries around a specific rank position\n* Straightforward trimming to a fixed leaderboard size\n* A clean separation between rank data and richer user metadata\n\nIn this example, the leaderboard score data is stored in a sorted set called `leaderboard:demo`, and each user's metadata is stored in a hash such as `leaderboard:demo:user:player-17`."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. Store each user ID in a sorted set with their score\n2. Store per-user metadata in a separate Redis hash keyed by user ID\n3. Fetch the highest-ranked users with a reverse range query\n4. Fetch users around a given rank by calculating a rank window\n5. Trim the leaderboard after updates so only the top configured entries remain\n\nSeparating rank data from metadata keeps leaderboard operations efficient while still letting the application render richer profile details."},{"id":"installation","title":"Installation","role":"setup","text":"Add the Lettuce dependency to your project:\n\n* If you use **Maven**:\n\n  [code example]\n\n* If you use **Gradle**:\n\n  [code example]"},{"id":"lettuce-vs-jedis","title":"Lettuce vs Jedis","role":"content","text":"In these docs, [`Jedis`](https://redis.io/docs/latest/develop/clients/jedis) is the recommended client when you want straightforward synchronous Java examples. The Lettuce examples here focus on async and reactive APIs, where Lettuce is especially strong."},{"id":"the-java-leaderboard","title":"The Java leaderboard","role":"content","text":"The async implementation is provided by `AsyncRedisLeaderboard`\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/leaderboard/java-lettuce/AsyncRedisLeaderboard.java)):\n\n[code example]\n\nThe reactive implementation is provided by `ReactiveRedisLeaderboard`\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/leaderboard/java-lettuce/ReactiveRedisLeaderboard.java)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"The implementation uses two Redis structures:\n\n[code example]\n\nThe score data lives in the sorted set, while the user details live in hashes keyed by the same user ID.\n\nThe implementation uses:\n\n* [`ZADD`](https://redis.io/docs/latest/commands/zadd) to add or update leaderboard scores\n* [`ZRANGE`](https://redis.io/docs/latest/commands/zrange) with the `REV` option to fetch the highest-ranked members\n* [`ZREVRANK`](https://redis.io/docs/latest/commands/zrevrank) to find a user's rank from the top\n* [`ZREMRANGEBYRANK`](https://redis.io/docs/latest/commands/zremrangebyrank) to trim the lowest-ranked overflow entries\n* [`HSET`](https://redis.io/docs/latest/commands/hset) and [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) to store and load user metadata\n* [`DEL`](https://redis.io/docs/latest/commands/del) to remove metadata for trimmed or deleted users"},{"id":"leaderboard-implementation","title":"Leaderboard implementation","role":"content","text":"The async `upsertUser()` method writes the score, updates metadata, and then trims the board if it exceeds the configured limit:\n\n[code example]\n\nTo fetch users around a rank, the implementation converts the requested rank and count into a reverse range window:\n\n[code example]"},{"id":"metadata-design","title":"Metadata design","role":"content","text":"The leaderboard stores only user IDs and scores in the sorted set. Richer details stay in a separate per-user hash. That means the same user ID can be ranked efficiently while still exposing extra fields such as:\n\n* Display name\n* Short description\n* Team or country\n* Avatar URL\n* Other lightweight profile fields\n\nThis is a useful pattern when the ranking view and the profile view need different data shapes."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of three Java files. Download them from the [`java-lettuce` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/leaderboard/java-lettuce) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"A local demo server ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/leaderboard/java-lettuce/DemoServer.java)) is included to show the leaderboard in action:\n\n[code example]\n\nThe demo uses the async implementation for the HTTP handlers and provides an interactive web interface where you can:\n\n* Add or update a player score and metadata\n* Increase a player's score incrementally\n* View the top `n` players on the leaderboard\n* View the `n` players around a chosen rank\n* Change the maximum number of entries the leaderboard keeps\n* Reset the demo dataset to a known starting state\n\nThe demo assumes Redis is running on `localhost:6379` but you can specify a different host and port using the `--redis-host HOST` and `--redis-port PORT` command-line arguments. Visit `http://localhost:8080` in your browser to try it out."},{"id":"production-usage","title":"Production usage","role":"content","text":"This guide uses a deliberately small local demo so you can focus on the Redis leaderboard pattern. In production, you will usually want to add more validation, tighter concurrency control, and application-specific lifecycle rules."},{"id":"decide-how-ties-should-behave","title":"Decide how ties should behave","role":"content","text":"Redis sorted sets order primarily by score. When two members have the same score, Redis uses the member value as a secondary ordering rule. If your application needs a different tie-breaker, you may want to encode it in the score or store additional state."},{"id":"consider-how-you-expire-or-archive-old-data","title":"Consider how you expire or archive old data","role":"content","text":"Some leaderboards are permanent, while others reset daily, weekly, or seasonally. Depending on your use case, you may want to:\n\n* Namespace keys by season or event\n* Snapshot historical results elsewhere\n* Rebuild the current leaderboard from upstream data"},{"id":"keep-metadata-lightweight","title":"Keep metadata lightweight","role":"content","text":"Per-user hashes work best for small, frequently accessed profile details. Large profile documents or rarely used attributes are often better kept in another store, with Redis holding only the fields needed to render the leaderboard quickly."}],"examples":[{"id":"installation-ex0","language":"xml","code":"<dependency>\n      <groupId>io.lettuce</groupId>\n      <artifactId>lettuce-core</artifactId>\n      <version>6.7.1.RELEASE</version>\n  </dependency>","section_id":"installation"},{"id":"installation-ex1","language":"groovy","code":"implementation 'io.lettuce:lettuce-core:6.7.1.RELEASE'","section_id":"installation"},{"id":"the-java-leaderboard-ex0","language":"java","code":"import java.util.Map;\n\nimport io.lettuce.core.RedisClient;\nimport io.lettuce.core.api.StatefulRedisConnection;\n\npublic class Main {\n    public static void main(String[] args) {\n        RedisClient redisClient = RedisClient.create(\"redis://localhost:6379\");\n        StatefulRedisConnection<String, String> connection = redisClient.connect();\n\n        AsyncRedisLeaderboard board = new AsyncRedisLeaderboard(\n                connection.async(),\n                \"leaderboard:demo\",\n                100\n        );\n\n        board.upsertUser(\n                \"player-1\",\n                1200,\n                Map.of(\n                        \"name\", \"Ada\",\n                        \"description\", \"Solves production incidents before breakfast.\"\n                )\n        ).thenCompose(entry -> board.incrementScore(\"player-1\", 25, Map.of()))\n         .thenCompose(entry -> board.getTop(5))\n         .thenAccept(System.out::println)\n         .join();\n\n        connection.close();\n        redisClient.shutdown();\n    }\n}","section_id":"the-java-leaderboard"},{"id":"the-java-leaderboard-ex1","language":"java","code":"import java.util.Map;\n\nimport io.lettuce.core.RedisClient;\nimport io.lettuce.core.api.StatefulRedisConnection;\n\npublic class Main {\n    public static void main(String[] args) {\n        RedisClient redisClient = RedisClient.create(\"redis://localhost:6379\");\n        StatefulRedisConnection<String, String> connection = redisClient.connect();\n\n        ReactiveRedisLeaderboard board = new ReactiveRedisLeaderboard(\n                connection.reactive(),\n                \"leaderboard:demo\",\n                100\n        );\n\n        board.upsertUser(\n                \"player-1\",\n                1200,\n                Map.of(\n                        \"name\", \"Ada\",\n                        \"description\", \"Solves production incidents before breakfast.\"\n                )\n        ).then(board.getTop(5))\n         .doOnNext(System.out::println)\n         .block();\n\n        connection.close();\n        redisClient.shutdown();\n    }\n}","section_id":"the-java-leaderboard"},{"id":"data-model-ex0","language":"text","code":"leaderboard:demo\n  player-1 => 1225\n  player-2 => 1180\n  player-3 => 1105\n\nleaderboard:demo:user:player-1\n  name = Ada\n  description = Solves production incidents before breakfast.","section_id":"data-model"},{"id":"leaderboard-implementation-ex0","language":"java","code":"public CompletableFuture<LeaderboardEntry> upsertUser(\n        String userId,\n        double score,\n        Map<String, String> metadata\n) {\n    Map<String, String> payload = coerceMetadata(metadata);\n\n    RedisFuture<Long> scoreFuture = commands.zadd(key, score, userId);\n    RedisFuture<Long> metadataFuture = payload.isEmpty()\n            ? null\n            : commands.hset(metadataKey(userId), payload);\n\n    CompletableFuture<Void> writes = metadataFuture == null\n            ? scoreFuture.toCompletableFuture().thenAccept(result -> {})\n            : CompletableFuture.allOf(\n                    scoreFuture.toCompletableFuture(),\n                    metadataFuture.toCompletableFuture()\n            );\n\n    return writes\n            .thenCompose(ignored -> trimToMaxEntries())\n            .thenCompose(trimmedUserIds -> getUserEntry(userId)\n                    .thenApply(entry -> entry.withTrimmedUserIds(trimmedUserIds)));\n}","section_id":"leaderboard-implementation"},{"id":"leaderboard-implementation-ex1","language":"java","code":"public CompletableFuture<List<LeaderboardEntry>> getAroundRank(int rank, int count) {\n    int normalizedRank = normalizePositiveInt(rank, \"rank\");\n    int normalizedCount = normalizePositiveInt(count, \"count\");\n\n    return getSize().thenCompose(totalEntries -> {\n        if (totalEntries <= normalizedCount) {\n            return listAll();\n        }\n\n        int halfWindow = normalizedCount / 2;\n        int start = Math.max(0, normalizedRank - 1 - halfWindow);\n        int maxStart = (int) totalEntries - normalizedCount;\n        if (start > maxStart) {\n            start = maxStart;\n        }\n        int end = start + normalizedCount - 1;\n\n        return zrangeWithScoresRev(start, end)\n                .toCompletableFuture()\n                .thenCompose(entries -> hydrateEntries(entries, start + 1));\n    });\n}","section_id":"leaderboard-implementation"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir leaderboard-demo && cd leaderboard-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/leaderboard/java-lettuce\ncurl -O $BASE/AsyncRedisLeaderboard.java\ncurl -O $BASE/ReactiveRedisLeaderboard.java\ncurl -O $BASE/DemoServer.java","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"# Compile\njavac -cp lettuce-core-6.7.1.RELEASE.jar AsyncRedisLeaderboard.java ReactiveRedisLeaderboard.java DemoServer.java\n\n# Run the demo server\njava -cp .:lettuce-core-6.7.1.RELEASE.jar DemoServer","section_id":"start-the-demo-server"}]}
{"schema_version":2,"id":"develop/use-cases/leaderboard/nodejs","title":"Redis leaderboard with node-redis","url":"https://redis.io/docs/latest/develop/use-cases/leaderboard/nodejs/","summary":"Implement a Redis leaderboard in JavaScript with node-redis and sorted sets","aliases":["/develop/use-cases/leaderboard/node-redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:54:57+01:00","children":[],"page_type":"content","content_hash":"0ac6a73d34e99f364f112a98892b574dbb8c4af3c16091b9ec02059c354f3189","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis-backed leaderboard in JavaScript with [`node-redis`](https://redis.io/docs/latest/develop/clients/nodejs). It uses a sorted set to store rank order, Redis hashes to store per-user metadata, and a small local web server so you can explore the leaderboard interactively in your browser."},{"id":"overview","title":"Overview","role":"overview","text":"Leaderboards are a classic Redis pattern. A sorted set stores each member together with a numeric score, and Redis maintains the ranking order automatically.\n\nThat gives you:\n\n* Fast score updates for existing users\n* Simple top `n` leaderboard queries\n* Efficient queries for entries around a specific rank position\n* Straightforward trimming to a fixed leaderboard size\n* A clean separation between rank data and richer user metadata\n\nIn this example, the leaderboard score data is stored in a sorted set called `leaderboard:demo`, and each user's metadata is stored in a hash such as `leaderboard:demo:user:player-17`."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. Store each user ID in a sorted set with their score\n2. Store per-user metadata in a separate Redis hash keyed by user ID\n3. Fetch the highest-ranked users with a reverse range query\n4. Fetch users around a given rank by calculating a rank window\n5. Trim the leaderboard after updates so only the top configured entries remain\n\nSeparating rank data from metadata keeps leaderboard operations efficient while still letting the application render richer profile details."},{"id":"installation","title":"Installation","role":"setup","text":"Install the `redis` package from npm:\n\n[code example]"},{"id":"the-javascript-leaderboard","title":"The JavaScript leaderboard","role":"content","text":"The `RedisLeaderboard` class wraps common leaderboard operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/leaderboard/nodejs/leaderboard.js)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"The implementation uses two Redis structures:\n\n[code example]\n\nThe score data lives in the sorted set, while the user details live in hashes keyed by the same user ID.\n\nThe implementation uses:\n\n* [`ZADD`](https://redis.io/docs/latest/commands/zadd) to add or update leaderboard scores\n* [`ZRANGE`](https://redis.io/docs/latest/commands/zrange) with the `REV` option to fetch the highest-ranked members\n* [`ZREVRANK`](https://redis.io/docs/latest/commands/zrevrank) to find a user's rank from the top\n* [`ZREMRANGEBYRANK`](https://redis.io/docs/latest/commands/zremrangebyrank) to trim the lowest-ranked overflow entries\n* [`HSET`](https://redis.io/docs/latest/commands/hset) and [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) to store and load user metadata\n* [`DEL`](https://redis.io/docs/latest/commands/del) to remove metadata for trimmed or deleted users"},{"id":"leaderboard-implementation","title":"Leaderboard implementation","role":"content","text":"The `upsertUser()` method writes the score, updates metadata, and then trims the board if it exceeds the configured limit:\n\n[code example]\n\nTo fetch users around a rank, the implementation converts the requested rank and count into a reverse range window:\n\n[code example]\n\nBecause `node-redis` is asynchronous, every leaderboard operation returns a Promise. Use `await` or `.then()` to work with the results."},{"id":"metadata-design","title":"Metadata design","role":"content","text":"The leaderboard stores only user IDs and scores in the sorted set. Richer details stay in a separate per-user hash. That means the same user ID can be ranked efficiently while still exposing extra fields such as:\n\n* Display name\n* Short description\n* Team or country\n* Avatar URL\n* Other lightweight profile fields\n\nThis is a useful pattern when the ranking view and the profile view need different data shapes."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of two JavaScript files. Download them from the [`nodejs` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/leaderboard/nodejs) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"A local demo server ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/leaderboard/nodejs/demoServer.js)) is included to show the leaderboard in action:\n\n[code example]\n\nThe demo server uses only built-in Node.js modules for HTTP handling:\n\n* `http` for the web server\n* `url` for query parsing and form decoding\n\nIt exposes a small interactive page where you can:\n\n* Add or update a player score and metadata\n* Increase a player's score incrementally\n* View the top `n` players on the leaderboard\n* View the `n` players around a chosen rank\n* Change the maximum number of entries the leaderboard keeps\n* Reset the demo dataset to a known starting state\n\nAfter starting the server, visit `http://localhost:8080`."},{"id":"production-usage","title":"Production usage","role":"content","text":"This guide uses a deliberately small local demo so you can focus on the Redis leaderboard pattern. In production, you will usually want to add more validation, tighter concurrency control, and application-specific lifecycle rules."},{"id":"decide-how-ties-should-behave","title":"Decide how ties should behave","role":"content","text":"Redis sorted sets order primarily by score. When two members have the same score, Redis uses the member value as a secondary ordering rule. If your application needs a different tie-breaker, you may want to encode it in the score or store additional state."},{"id":"consider-how-you-expire-or-archive-old-data","title":"Consider how you expire or archive old data","role":"content","text":"Some leaderboards are permanent, while others reset daily, weekly, or seasonally. Depending on your use case, you may want to:\n\n* Namespace keys by season or event\n* Snapshot historical results elsewhere\n* Rebuild the current leaderboard from upstream data"},{"id":"keep-metadata-lightweight","title":"Keep metadata lightweight","role":"content","text":"Per-user hashes work best for small, frequently accessed profile details. Large profile documents or rarely used attributes are often better kept in another store, with Redis holding only the fields needed to render the leaderboard quickly."}],"examples":[{"id":"installation-ex0","language":"bash","code":"npm install redis","section_id":"installation"},{"id":"the-javascript-leaderboard-ex0","language":"javascript","code":"const { createClient } = require(\"redis\");\nconst { RedisLeaderboard } = require(\"./leaderboard\");\n\nconst client = createClient({ url: \"redis://localhost:6379\" });\nawait client.connect();\n\nconst board = new RedisLeaderboard({\n  redisClient: client,\n  key: \"leaderboard:demo\",\n  maxEntries: 100,\n});\n\nawait board.upsertUser(\"player-1\", 1200, {\n  name: \"Ada\",\n  description: \"Solves production incidents before breakfast.\",\n});\n\nawait board.incrementScore(\"player-1\", 25);\nconst topPlayers = await board.getTop(5);\nconst playersNearRank = await board.getAroundRank(10, 5);","section_id":"the-javascript-leaderboard"},{"id":"data-model-ex0","language":"text","code":"leaderboard:demo\n  player-1 => 1225\n  player-2 => 1180\n  player-3 => 1105\n\nleaderboard:demo:user:player-1\n  name = Ada\n  description = Solves production incidents before breakfast.","section_id":"data-model"},{"id":"leaderboard-implementation-ex0","language":"javascript","code":"async upsertUser(userId, score, metadata = null) {\n  const metadataKey = this._metadataKey(userId);\n  const payload = this._coerceMetadata(metadata);\n\n  const multi = this.redis.multi();\n  multi.zAdd(this.key, [{ score: Number(score), value: userId }]);\n  if (Object.keys(payload).length > 0) {\n    multi.hSet(metadataKey, payload);\n  }\n  await multi.exec();\n\n  const trimmedUserIds = await this._trimToMaxEntries();\n  const entry = await this.getUserEntry(userId);\n  return entry\n    ? { ...entry, trimmedUserIds }\n    : { userId, score: Number(score), metadata: payload, trimmedUserIds };\n}","section_id":"leaderboard-implementation"},{"id":"leaderboard-implementation-ex1","language":"javascript","code":"async getAroundRank(rank, count) {\n  const normalizedRank = this._normalizePositiveInt(rank, \"rank\");\n  const normalizedCount = this._normalizePositiveInt(count, \"count\");\n  const totalEntries = await this.getSize();\n\n  if (totalEntries <= normalizedCount) {\n    return this.listAll();\n  }\n\n  const halfWindow = Math.floor(normalizedCount / 2);\n  let start = Math.max(0, normalizedRank - 1 - halfWindow);\n  start = Math.min(start, totalEntries - normalizedCount);\n  const end = start + normalizedCount - 1;\n\n  const entries = await this._zRangeWithScoresRev(start, end);\n  return this._hydrateEntries(entries, start + 1);\n}","section_id":"leaderboard-implementation"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir leaderboard-demo && cd leaderboard-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/leaderboard/nodejs\ncurl -O $BASE/leaderboard.js\ncurl -O $BASE/demoServer.js","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"# Install dependencies\nnpm install redis\n\n# Run the demo server\nnode demoServer.js","section_id":"start-the-demo-server"}]}
{"schema_version":2,"id":"develop/use-cases/leaderboard/php","title":"Redis leaderboard with PHP","url":"https://redis.io/docs/latest/develop/use-cases/leaderboard/php/","summary":"Implement a Redis leaderboard in PHP with Predis and sorted sets","aliases":["/develop/use-cases/leaderboard/predis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:54:57+01:00","children":[],"page_type":"content","content_hash":"7f31c9d4a26778024a6092fde80f31384b952e43ec80e8fbfdf2b5d51c3ec2e6","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis-backed leaderboard in PHP with [Predis](https://redis.io/docs/latest/develop/clients/php). It uses a sorted set to store rank order, Redis hashes to store per-user metadata, and a small local web server so you can explore the leaderboard interactively in your browser."},{"id":"overview","title":"Overview","role":"overview","text":"Leaderboards are a classic Redis pattern. A sorted set stores each member together with a numeric score, and Redis maintains the ranking order automatically.\n\nThat gives you:\n\n* Fast score updates for existing users\n* Simple top `n` leaderboard queries\n* Efficient queries for entries around a specific rank position\n* Straightforward trimming to a fixed leaderboard size\n* A clean separation between rank data and richer user metadata\n\nIn this example, the leaderboard score data is stored in a sorted set called `leaderboard:demo`, and each user's metadata is stored in a hash such as `leaderboard:demo:user:player-17`."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. Store each user ID in a sorted set with their score\n2. Store per-user metadata in a separate Redis hash keyed by user ID\n3. Fetch the highest-ranked users with a reverse range query\n4. Fetch users around a given rank by calculating a rank window\n5. Trim the leaderboard after updates so only the top configured entries remain\n\nSeparating rank data from metadata keeps leaderboard operations efficient while still letting the application render richer profile details."},{"id":"installation","title":"Installation","role":"setup","text":"Install Predis using Composer:\n\n[code example]"},{"id":"the-php-class","title":"The PHP class","role":"content","text":"The `RedisLeaderboard` class wraps common leaderboard operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/leaderboard/php/RedisLeaderboard.php)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"The implementation uses two Redis structures:\n\n[code example]\n\nThe score data lives in the sorted set, while the user details live in hashes keyed by the same user ID.\n\nThe implementation uses:\n\n* [`ZADD`](https://redis.io/docs/latest/commands/zadd) to add or update leaderboard scores\n* [`ZRANGE`](https://redis.io/docs/latest/commands/zrange) with the `REV` option to fetch the highest-ranked members\n* [`ZREVRANK`](https://redis.io/docs/latest/commands/zrevrank) to find a user's rank from the top\n* [`ZREMRANGEBYRANK`](https://redis.io/docs/latest/commands/zremrangebyrank) to trim the lowest-ranked overflow entries\n* [`HSET`](https://redis.io/docs/latest/commands/hset) and [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) to store and load user metadata\n* [`DEL`](https://redis.io/docs/latest/commands/del) to remove metadata for trimmed or deleted users"},{"id":"leaderboard-implementation","title":"Leaderboard implementation","role":"content","text":"The `upsertUser()` method writes the score, updates metadata, and then trims the board if it exceeds the configured limit:\n\n[code example]\n\nTo fetch users around a rank, the implementation converts the requested rank and count into a reverse range window:\n\n[code example]"},{"id":"metadata-design","title":"Metadata design","role":"content","text":"The leaderboard stores only user IDs and scores in the sorted set. Richer details stay in a separate per-user hash. That means the same user ID can be ranked efficiently while still exposing extra fields such as:\n\n* Display name\n* Short description\n* Team or country\n* Avatar URL\n* Other lightweight profile fields\n\nThis is a useful pattern when the ranking view and the profile view need different data shapes."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of two PHP files. Download them from the [`php` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/leaderboard/php) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"A local demo server ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/leaderboard/php/demo_server.php)) is included to show the leaderboard in action:\n\n[code example]\n\nThe demo provides an interactive web interface where you can:\n\n* Add or update a player score and metadata\n* Increase a player's score incrementally\n* View the top `n` players on the leaderboard\n* View the `n` players around a chosen rank\n* Change the maximum number of entries the leaderboard keeps\n* Reset the demo dataset to a known starting state\n\nThe demo assumes Redis is running on `localhost:6379` but you can specify a different host and port using the `REDIS_HOST` and `REDIS_PORT` environment variables:\n\n[code example]\n\nVisit `http://localhost:8080` in your browser to try it out."},{"id":"production-usage","title":"Production usage","role":"content","text":"This guide uses a deliberately small local demo so you can focus on the Redis leaderboard pattern. In production, you will usually want to add more validation, tighter concurrency control, and application-specific lifecycle rules."},{"id":"decide-how-ties-should-behave","title":"Decide how ties should behave","role":"content","text":"Redis sorted sets order primarily by score. When two members have the same score, Redis uses the member value as a secondary ordering rule. If your application needs a different tie-breaker, you may want to encode it in the score or store additional state."},{"id":"consider-how-you-expire-or-archive-old-data","title":"Consider how you expire or archive old data","role":"content","text":"Some leaderboards are permanent, while others reset daily, weekly, or seasonally. Depending on your use case, you may want to:\n\n* Namespace keys by season or event\n* Snapshot historical results elsewhere\n* Rebuild the current leaderboard from upstream data"},{"id":"keep-metadata-lightweight","title":"Keep metadata lightweight","role":"content","text":"Per-user hashes work best for small, frequently accessed profile details. Large profile documents or rarely used attributes are often better kept in another store, with Redis holding only the fields needed to render the leaderboard quickly."}],"examples":[{"id":"installation-ex0","language":"bash","code":"composer require predis/predis","section_id":"installation"},{"id":"the-php-class-ex0","language":"php","code":"<?php\n\nrequire __DIR__ . '/vendor/autoload.php';\n\nuse Predis\\Client;\n\n$redis = new Client([\n    'scheme' => 'tcp',\n    'host' => '127.0.0.1',\n    'port' => 6379,\n]);\n\n$board = new RedisLeaderboard(\n    redis: $redis,\n    key: 'leaderboard:demo',\n    maxEntries: 100\n);\n\n$entry = $board->upsertUser(\n    'player-1',\n    1200,\n    [\n        'name' => 'Ada',\n        'description' => 'Solves production incidents before breakfast.',\n    ]\n);\n\n$topPlayers = $board->getTop(5);","section_id":"the-php-class"},{"id":"data-model-ex0","language":"text","code":"leaderboard:demo\n  player-1 => 1225\n  player-2 => 1180\n  player-3 => 1105\n\nleaderboard:demo:user:player-1\n  name = Ada\n  description = Solves production incidents before breakfast.","section_id":"data-model"},{"id":"leaderboard-implementation-ex0","language":"php","code":"public function upsertUser(string $userId, float $score, array $metadata = []): array\n{\n    $payload = $this->coerceMetadata($metadata);\n\n    $pipeline = $this->redis->pipeline();\n    $pipeline->zadd($this->key, [$userId => $score]);\n    if ($payload !== []) {\n        $pipeline->hmset($this->metadataKey($userId), $payload);\n    }\n    $pipeline->execute();\n\n    $trimmedUserIds = $this->trimToMaxEntries();\n    $entry = $this->getUserEntry($userId);\n\n    return $entry === null\n        ? [\n            'rank' => 0,\n            'user_id' => $userId,\n            'score' => $score,\n            'metadata' => $payload,\n            'trimmed_user_ids' => $trimmedUserIds,\n        ]\n        : array_merge($entry, ['trimmed_user_ids' => $trimmedUserIds]);\n}","section_id":"leaderboard-implementation"},{"id":"leaderboard-implementation-ex1","language":"php","code":"public function getAroundRank(int $rank, int $count): array\n{\n    $normalizedRank = $this->normalizePositiveInt($rank, 'rank');\n    $normalizedCount = $this->normalizePositiveInt($count, 'count');\n    $totalEntries = $this->getSize();\n\n    if ($totalEntries <= $normalizedCount) {\n        return $this->listAll();\n    }\n\n    $halfWindow = intdiv($normalizedCount, 2);\n    $start = max(0, $normalizedRank - 1 - $halfWindow);\n    $maxStart = $totalEntries - $normalizedCount;\n    if ($start > $maxStart) {\n        $start = $maxStart;\n    }\n    $end = $start + $normalizedCount - 1;\n\n    $entries = $this->zrangeWithScoresRev($start, $end);\n    return $this->hydrateEntries($entries, $start + 1);\n}","section_id":"leaderboard-implementation"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir leaderboard-demo && cd leaderboard-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/leaderboard/php\ncurl -O $BASE/RedisLeaderboard.php\ncurl -O $BASE/demo_server.php","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"composer require predis/predis\nphp -S localhost:8080 demo_server.php","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"bash","code":"REDIS_HOST=myhost REDIS_PORT=6380 php -S localhost:8080 demo_server.php","section_id":"start-the-demo-server"}]}
{"schema_version":2,"id":"develop/use-cases/leaderboard/redis-py","title":"Redis leaderboard with redis-py","url":"https://redis.io/docs/latest/develop/use-cases/leaderboard/redis-py/","summary":"Implement a Redis leaderboard in Python with redis-py and sorted sets","aliases":["/develop/use-cases/leaderboard/python"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:54:57+01:00","children":[],"page_type":"content","content_hash":"73be31fe90702512a36e39d641be60ef2c6bb0b98ba279036f01b351bf776d7a","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis-backed leaderboard in Python with [`redis-py`](https://redis.io/docs/latest/develop/clients/redis-py). It uses a sorted set to store rank order, Redis hashes to store per-user metadata, and a small local web server so you can explore the leaderboard interactively in your browser."},{"id":"overview","title":"Overview","role":"overview","text":"Leaderboards are one of the classic Redis use cases. A sorted set stores each member together with a numeric score, and Redis keeps the members ordered by that score for you.\n\nThat gives you:\n\n* Fast score updates for existing users\n* Simple top `n` leaderboard queries\n* Efficient queries for entries around a specific rank position\n* Straightforward trimming to a fixed leaderboard size\n* A clean separation between rank data and richer user metadata\n\nIn this example, the leaderboard score data is stored in a sorted set called `leaderboard:demo`, and each user's metadata is stored in a hash such as `leaderboard:demo:user:player-17`."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. Store each user ID in a sorted set with their score\n2. Store per-user metadata in a separate Redis hash keyed by user ID\n3. Fetch the highest-ranked users with a reverse range query\n4. Fetch users around a given rank by calculating a rank window\n5. Trim the leaderboard after updates so only the top configured entries remain\n\nSeparating rank data from metadata keeps the leaderboard operations efficient while still letting you render richer profiles in the application."},{"id":"the-python-leaderboard","title":"The Python leaderboard","role":"content","text":"The `RedisLeaderboard` class wraps the common leaderboard operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/leaderboard/redis-py/leaderboard.py)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"The implementation uses two Redis structures:\n\n[code example]\n\nThe score data lives in the sorted set, while the user details live in hashes keyed by the same user ID.\n\nThe implementation uses:\n\n* [`ZADD`](https://redis.io/docs/latest/commands/zadd) to add or update leaderboard scores\n* [`ZRANGE`](https://redis.io/docs/latest/commands/zrange) with the `REV` option to fetch the highest-ranked members\n* [`ZREVRANK`](https://redis.io/docs/latest/commands/zrevrank) to find a user's rank from the top\n* [`ZREMRANGEBYRANK`](https://redis.io/docs/latest/commands/zremrangebyrank) to trim the lowest-ranked overflow entries\n* [`HSET`](https://redis.io/docs/latest/commands/hset) and [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) to store and load user metadata\n* [`DEL`](https://redis.io/docs/latest/commands/del) to remove metadata for trimmed or deleted users"},{"id":"leaderboard-implementation","title":"Leaderboard implementation","role":"content","text":"The `upsert_user()` method writes the score, updates metadata, and then trims the board if it exceeds the configured limit:\n\n[code example]\n\nTo fetch users around a rank, the implementation converts the requested rank and count into a reverse range window:\n\n[code example]\n\nIf you need stronger guarantees for highly concurrent production writes, you can move the update-and-trim behavior into a Lua script or a tighter transaction flow. For a small application or demo, this Python implementation is easy to follow and works well."},{"id":"metadata-design","title":"Metadata design","role":"content","text":"The leaderboard stores only user IDs and scores in the sorted set. Richer details stay in a separate per-user hash. That means the same user ID can be ranked efficiently while still exposing extra fields such as:\n\n* Display name\n* Short description\n* Team or country\n* Avatar URL\n* Other lightweight profile fields\n\nThis is a useful pattern when the ranking view and the profile view need different data shapes."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before running the demo, make sure that:\n\n* Redis is running and accessible. By default, the demo connects to `localhost:6379`.\n* The `redis` Python package is installed:\n\n[code example]\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of two Python files. Download them from the [`redis-py` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/leaderboard/redis-py) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"A local demo server ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/leaderboard/redis-py/demo_server.py)) is included to show the leaderboard in action:\n\n[code example]\n\nThe demo server uses only Python standard library features for HTTP handling:\n\n* [`http.server`](https://docs.python.org/3/library/http.server.html) for the web server\n* [`urllib.parse`](https://docs.python.org/3/library/urllib.parse.html) for form decoding and query parsing\n* [`json`](https://docs.python.org/3/library/json.html) for API responses\n\nIt exposes a small interactive page where you can:\n\n* Add or update a player score and metadata\n* Increase a player's score incrementally\n* View the top `n` players on the leaderboard\n* View the `n` players around a chosen rank\n* Change the maximum number of entries the leaderboard keeps\n* Reset the demo dataset to a known starting state\n\nAfter starting the server, visit `http://localhost:8080`."},{"id":"production-usage","title":"Production usage","role":"content","text":"This guide uses a deliberately small local demo so you can focus on the Redis leaderboard pattern. In production, you will usually want to add more validation, tighter concurrency control, and application-specific lifecycle rules."},{"id":"decide-how-ties-should-behave","title":"Decide how ties should behave","role":"content","text":"Redis sorted sets order primarily by score. When two members have the same score, Redis uses the member value as a secondary ordering rule. If your application needs a different tie-breaker, you may want to encode it in the score or store additional state."},{"id":"consider-how-you-expire-or-archive-old-data","title":"Consider how you expire or archive old data","role":"content","text":"Some leaderboards are permanent, while others reset daily, weekly, or seasonally. Depending on your use case, you may want to:\n\n* Namespace keys by season or event\n* Snapshot historical results elsewhere\n* Rebuild the current leaderboard from upstream data"},{"id":"keep-metadata-lightweight","title":"Keep metadata lightweight","role":"content","text":"Per-user hashes work best for small, frequently accessed profile details. Large profile documents or rarely used attributes are often better kept in another store, with Redis holding only the fields needed to render the leaderboard quickly."}],"examples":[{"id":"the-python-leaderboard-ex0","language":"python","code":"import redis\nfrom leaderboard import RedisLeaderboard\n\nr = redis.Redis(host=\"localhost\", port=6379, decode_responses=True)\nboard = RedisLeaderboard(redis_client=r, key=\"leaderboard:demo\", max_entries=100)\n\nboard.upsert_user(\n    \"player-1\",\n    1200,\n    {\n        \"name\": \"Ada\",\n        \"description\": \"Solves production incidents before breakfast.\",\n    },\n)\n\nboard.increment_score(\"player-1\", 25)\ntop_players = board.get_top(5)\nplayers_near_rank = board.get_around_rank(rank=10, count=5)","section_id":"the-python-leaderboard"},{"id":"data-model-ex0","language":"text","code":"leaderboard:demo\n  player-1 => 1225\n  player-2 => 1180\n  player-3 => 1105\n\nleaderboard:demo:user:player-1\n  name = Ada\n  description = Solves production incidents before breakfast.","section_id":"data-model"},{"id":"leaderboard-implementation-ex0","language":"python","code":"def upsert_user(\n    self,\n    user_id: str,\n    score: float,\n    metadata: Optional[dict[str, str]] = None,\n) -> dict[str, object]:\n    metadata_key = self._metadata_key(user_id)\n\n    with self.redis.pipeline() as pipeline:\n        pipeline.zadd(self.key, {user_id: float(score)})\n        if metadata:\n            pipeline.hset(\n                metadata_key,\n                mapping={field: str(value) for field, value in metadata.items()},\n            )\n        pipeline.execute()\n\n    trimmed_user_ids = self._trim_to_max_entries()\n    return self.get_user_entry(user_id) or {\n        \"user_id\": user_id,\n        \"score\": float(score),\n        \"metadata\": metadata or {},\n        \"trimmed_user_ids\": trimmed_user_ids,\n    }","section_id":"leaderboard-implementation"},{"id":"leaderboard-implementation-ex1","language":"python","code":"def get_around_rank(self, rank: int, count: int) -> list[dict[str, object]]:\n    normalized_rank = max(1, int(rank))\n    normalized_count = max(1, int(count))\n    half_window = normalized_count // 2\n    start = max(0, normalized_rank - 1 - half_window)\n    end = start + normalized_count - 1\n\n    entries = self.redis.zrange(\n        self.key,\n        start,\n        end,\n        desc=True,\n        withscores=True,\n    )\n    return self._hydrate_entries(entries, start_rank=start + 1)","section_id":"leaderboard-implementation"},{"id":"prerequisites-ex0","language":"bash","code":"pip install redis","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir leaderboard-demo && cd leaderboard-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/leaderboard/redis-py\ncurl -O $BASE/leaderboard.py\ncurl -O $BASE/demo_server.py","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"python demo_server.py","section_id":"start-the-demo-server"}]}
{"schema_version":2,"id":"develop/use-cases/leaderboard/ruby","title":"Redis leaderboard with Ruby","url":"https://redis.io/docs/latest/develop/use-cases/leaderboard/ruby/","summary":"Implement a Redis leaderboard in Ruby with redis-rb and sorted sets","aliases":["/develop/use-cases/leaderboard/redis-rb"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:54:57+01:00","children":[],"page_type":"content","content_hash":"6c19fda2ca75af3744e409e79b941ee4b13424c93fe70091b8998673a7a94ba4","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis-backed leaderboard in Ruby with the [`redis-rb`](https://redis.io/docs/latest/develop/clients/ruby) client library. It uses a sorted set to store rank order, Redis hashes to store per-user metadata, and a small local web server so you can explore the leaderboard interactively in your browser."},{"id":"overview","title":"Overview","role":"overview","text":"Leaderboards are a natural fit for Redis. A sorted set stores each member together with a numeric score, and Redis maintains the ranking order automatically.\n\nThat gives you:\n\n* Fast score updates for existing users\n* Simple top `n` leaderboard queries\n* Efficient queries for entries around a specific rank position\n* Straightforward trimming to a fixed leaderboard size\n* A clean separation between rank data and richer user metadata\n\nIn this example, the leaderboard score data is stored in a sorted set called `leaderboard:demo`, and each user's metadata is stored in a hash such as `leaderboard:demo:user:player-17`."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. Store each user ID in a sorted set with their score\n2. Store per-user metadata in a separate Redis hash keyed by user ID\n3. Fetch the highest-ranked users with a reverse range query\n4. Fetch users around a given rank by calculating a rank window\n5. Trim the leaderboard after updates so only the top configured entries remain\n\nSeparating rank data from metadata keeps leaderboard operations efficient while still letting the application render richer profile details."},{"id":"installation","title":"Installation","role":"setup","text":"Install the `redis` gem:\n\n[code example]\n\nOr add it to your `Gemfile`:\n\n[code example]\n\nThen run:\n\n[code example]"},{"id":"the-ruby-module","title":"The Ruby module","role":"content","text":"The `RedisLeaderboard` class wraps common leaderboard operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/leaderboard/ruby/leaderboard.rb)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"The implementation uses two Redis structures:\n\n[code example]\n\nThe score data lives in the sorted set, while the user details live in hashes keyed by the same user ID.\n\nThe implementation uses:\n\n* [`ZADD`](https://redis.io/docs/latest/commands/zadd) to add or update leaderboard scores\n* [`ZRANGE`](https://redis.io/docs/latest/commands/zrange) with the `REV` option to fetch the highest-ranked members\n* [`ZREVRANK`](https://redis.io/docs/latest/commands/zrevrank) to find a user's rank from the top\n* [`ZREMRANGEBYRANK`](https://redis.io/docs/latest/commands/zremrangebyrank) to trim the lowest-ranked overflow entries\n* [`HSET`](https://redis.io/docs/latest/commands/hset) and [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) to store and load user metadata\n* [`DEL`](https://redis.io/docs/latest/commands/del) to remove metadata for trimmed or deleted users"},{"id":"leaderboard-implementation","title":"Leaderboard implementation","role":"content","text":"The `upsert_user()` method writes the score, updates metadata, and then trims the board if it exceeds the configured limit:\n\n[code example]\n\nTo fetch users around a rank, the implementation converts the requested rank and count into a reverse range window:\n\n[code example]"},{"id":"metadata-design","title":"Metadata design","role":"content","text":"The leaderboard stores only user IDs and scores in the sorted set. Richer details stay in a separate per-user hash. That means the same user ID can be ranked efficiently while still exposing extra fields such as:\n\n* Display name\n* Short description\n* Team or country\n* Avatar URL\n* Other lightweight profile fields\n\nThis is a useful pattern when the ranking view and the profile view need different data shapes."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of two Ruby files. Download them from the [`ruby` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/leaderboard/ruby) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"A local demo server ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/leaderboard/ruby/demo_server.rb)) is included to show the leaderboard in action:\n\n[code example]\n\nThe demo provides an interactive web interface where you can:\n\n* Add or update a player score and metadata\n* Increase a player's score incrementally\n* View the top `n` players on the leaderboard\n* View the `n` players around a chosen rank\n* Change the maximum number of entries the leaderboard keeps\n* Reset the demo dataset to a known starting state\n\nThe demo assumes Redis is running on `localhost:6379` but you can specify a different host and port using the `--redis-host HOST` and `--redis-port PORT` command-line arguments. Visit `http://localhost:8080` in your browser to try it out."},{"id":"production-usage","title":"Production usage","role":"content","text":"This guide uses a deliberately small local demo so you can focus on the Redis leaderboard pattern. In production, you will usually want to add more validation, tighter concurrency control, and application-specific lifecycle rules."},{"id":"decide-how-ties-should-behave","title":"Decide how ties should behave","role":"content","text":"Redis sorted sets order primarily by score. When two members have the same score, Redis uses the member value as a secondary ordering rule. If your application needs a different tie-breaker, you may want to encode it in the score or store additional state."},{"id":"consider-how-you-expire-or-archive-old-data","title":"Consider how you expire or archive old data","role":"content","text":"Some leaderboards are permanent, while others reset daily, weekly, or seasonally. Depending on your use case, you may want to:\n\n* Namespace keys by season or event\n* Snapshot historical results elsewhere\n* Rebuild the current leaderboard from upstream data"},{"id":"keep-metadata-lightweight","title":"Keep metadata lightweight","role":"content","text":"Per-user hashes work best for small, frequently accessed profile details. Large profile documents or rarely used attributes are often better kept in another store, with Redis holding only the fields needed to render the leaderboard quickly."}],"examples":[{"id":"installation-ex0","language":"bash","code":"gem install redis","section_id":"installation"},{"id":"installation-ex1","language":"ruby","code":"gem 'redis', '~> 5.0'","section_id":"installation"},{"id":"installation-ex2","language":"bash","code":"bundle install","section_id":"installation"},{"id":"the-ruby-module-ex0","language":"ruby","code":"require 'redis'\nrequire_relative 'leaderboard'\n\nredis = Redis.new(host: 'localhost', port: 6379)\n\nboard = RedisLeaderboard.new(\n  redis: redis,\n  key: 'leaderboard:demo',\n  max_entries: 100\n)\n\nentry = board.upsert_user(\n  'player-1',\n  1200,\n  {\n    'name' => 'Ada',\n    'description' => 'Solves production incidents before breakfast.'\n  }\n)\n\ntop_players = board.get_top(5)","section_id":"the-ruby-module"},{"id":"data-model-ex0","language":"text","code":"leaderboard:demo\n  player-1 => 1225\n  player-2 => 1180\n  player-3 => 1105\n\nleaderboard:demo:user:player-1\n  name = Ada\n  description = Solves production incidents before breakfast.","section_id":"data-model"},{"id":"leaderboard-implementation-ex0","language":"ruby","code":"def upsert_user(user_id, score, metadata = {})\n  payload = coerce_metadata(metadata)\n\n  @redis.pipelined do |pipeline|\n    pipeline.zadd(@key, score, user_id)\n    pipeline.hset(metadata_key(user_id), payload) unless payload.empty?\n  end\n\n  trimmed_user_ids = trim_to_max_entries\n  entry = get_user_entry(user_id)\n\n  if entry.nil?\n    {\n      rank: 0,\n      user_id: user_id,\n      score: score.to_f,\n      metadata: payload,\n      trimmed_user_ids: trimmed_user_ids\n    }\n  else\n    entry.merge(trimmed_user_ids: trimmed_user_ids)\n  end\nend","section_id":"leaderboard-implementation"},{"id":"leaderboard-implementation-ex1","language":"ruby","code":"def get_around_rank(rank, count)\n  normalized_rank = normalize_positive_int(rank, \"rank\")\n  normalized_count = normalize_positive_int(count, \"count\")\n  total_entries = get_size\n\n  return list_all if total_entries <= normalized_count\n\n  half_window = normalized_count / 2\n  start = [0, normalized_rank - 1 - half_window].max\n  max_start = total_entries - normalized_count\n  start = max_start if start > max_start\n  ending = start + normalized_count - 1\n\n  entries = zrange_with_scores_rev(start, ending)\n  hydrate_entries(entries, start + 1)\nend","section_id":"leaderboard-implementation"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir leaderboard-demo && cd leaderboard-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/leaderboard/ruby\ncurl -O $BASE/leaderboard.rb\ncurl -O $BASE/demo_server.rb","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"gem install redis webrick\nruby demo_server.rb","section_id":"start-the-demo-server"}]}
{"schema_version":2,"id":"develop/use-cases/leaderboard/rust","title":"Redis leaderboard with Rust","url":"https://redis.io/docs/latest/develop/use-cases/leaderboard/rust/","summary":"Implement sync and async Redis leaderboards in Rust with redis-rs and sorted sets","aliases":["/develop/use-cases/leaderboard/redis-rs"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:54:57+01:00","children":[],"page_type":"content","content_hash":"1da2dc1a413dfdb370294a789d10bd8f123e6b89c099435b90ffc653f17501b8","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement Redis-backed leaderboards in Rust with the [`redis-rs`](https://redis.io/docs/latest/develop/clients/rust) client library. It includes both synchronous and asynchronous APIs, uses a sorted set to store rank order, stores per-user metadata in Redis hashes, and includes a small local web server so you can explore the leaderboard interactively in your browser."},{"id":"overview","title":"Overview","role":"overview","text":"Leaderboards are a natural fit for Redis. A sorted set stores each member together with a numeric score, and Redis maintains the ranking order automatically.\n\nThat gives you:\n\n* Fast score updates for existing users\n* Simple top `n` leaderboard queries\n* Efficient queries for entries around a specific rank position\n* Straightforward trimming to a fixed leaderboard size\n* A clean separation between rank data and richer user metadata\n\nIn this example, the leaderboard score data is stored in a sorted set called `leaderboard:demo`, and each user's metadata is stored in a hash such as `leaderboard:demo:user:player-17`."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. Store each user ID in a sorted set with their score\n2. Store per-user metadata in a separate Redis hash keyed by user ID\n3. Fetch the highest-ranked users with a reverse range query\n4. Fetch users around a given rank by calculating a rank window\n5. Trim the leaderboard after updates so only the top configured entries remain\n\nSeparating rank data from metadata keeps leaderboard operations efficient while still letting the application render richer profile details."},{"id":"installation","title":"Installation","role":"setup","text":"Add the `redis` crate to your `Cargo.toml`:\n\n[code example]"},{"id":"using-the-rust-module","title":"Using the Rust module","role":"content","text":"The module provides both a synchronous `RedisLeaderboard` and an asynchronous `AsyncRedisLeaderboard`\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/leaderboard/rust/leaderboard.rs))."},{"id":"synchronous-usage","title":"Synchronous usage","role":"content","text":"[code example]"},{"id":"asynchronous-usage","title":"Asynchronous usage","role":"content","text":"[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"The implementation uses two Redis structures:\n\n[code example]\n\nThe score data lives in the sorted set, while the user details live in hashes keyed by the same user ID.\n\nThe implementation uses:\n\n* [`ZADD`](https://redis.io/docs/latest/commands/zadd) to add or update leaderboard scores\n* [`ZRANGE`](https://redis.io/docs/latest/commands/zrange) with the `REV` option to fetch the highest-ranked members\n* [`ZREVRANK`](https://redis.io/docs/latest/commands/zrevrank) to find a user's rank from the top\n* [`ZREMRANGEBYRANK`](https://redis.io/docs/latest/commands/zremrangebyrank) to trim the lowest-ranked overflow entries\n* [`HSET`](https://redis.io/docs/latest/commands/hset) and [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) to store and load user metadata\n* [`DEL`](https://redis.io/docs/latest/commands/del) to remove metadata for trimmed or deleted users"},{"id":"leaderboard-implementation","title":"Leaderboard implementation","role":"content","text":"The synchronous `upsert_user()` method writes the score, updates metadata, and then trims the board if it exceeds the configured limit:\n\n[code example]\n\nTo fetch users around a rank, the implementation converts the requested rank and count into a reverse range window:\n\n[code example]"},{"id":"metadata-design","title":"Metadata design","role":"content","text":"The leaderboard stores only user IDs and scores in the sorted set. Richer details stay in a separate per-user hash. That means the same user ID can be ranked efficiently while still exposing extra fields such as:\n\n* Display name\n* Short description\n* Team or country\n* Avatar URL\n* Other lightweight profile fields\n\nThis is a useful pattern when the ranking view and the profile view need different data shapes."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of three files. Download them from the [`rust` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/leaderboard/rust) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"A local demo server ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/leaderboard/rust/demo_server.rs)) is included to show the leaderboard in action:\n\n[code example]\n\nThe demo uses the async leaderboard implementation for the HTTP handlers and provides an interactive web interface where you can:\n\n* Add or update a player score and metadata\n* Increase a player's score incrementally\n* View the top `n` players on the leaderboard\n* View the `n` players around a chosen rank\n* Change the maximum number of entries the leaderboard keeps\n* Reset the demo dataset to a known starting state\n\nThe demo assumes Redis is running on `localhost:6379` but you can specify a different host using the `REDIS_URL` environment variable. Visit `http://localhost:8080` in your browser to try it out."},{"id":"production-usage","title":"Production usage","role":"content","text":"This guide uses a deliberately small local demo so you can focus on the Redis leaderboard pattern. In production, you will usually want to add more validation, tighter concurrency control, and application-specific lifecycle rules."},{"id":"decide-how-ties-should-behave","title":"Decide how ties should behave","role":"content","text":"Redis sorted sets order primarily by score. When two members have the same score, Redis uses the member value as a secondary ordering rule. If your application needs a different tie-breaker, you may want to encode it in the score or store additional state."},{"id":"consider-how-you-expire-or-archive-old-data","title":"Consider how you expire or archive old data","role":"content","text":"Some leaderboards are permanent, while others reset daily, weekly, or seasonally. Depending on your use case, you may want to:\n\n* Namespace keys by season or event\n* Snapshot historical results elsewhere\n* Rebuild the current leaderboard from upstream data"},{"id":"keep-metadata-lightweight","title":"Keep metadata lightweight","role":"content","text":"Per-user hashes work best for small, frequently accessed profile details. Large profile documents or rarely used attributes are often better kept in another store, with Redis holding only the fields needed to render the leaderboard quickly."}],"examples":[{"id":"installation-ex0","language":"toml","code":"[dependencies]\nredis = { version = \"0.24\", features = [\"script\", \"tokio-comp\"] }\ntokio = { version = \"1\", features = [\"full\"] }","section_id":"installation"},{"id":"synchronous-usage-ex0","language":"rust","code":"use redis::Client;\n\nlet client = Client::open(\"redis://localhost:6379/\")?;\nlet mut con = client.get_connection()?;\n\nlet board = RedisLeaderboard::new(\"leaderboard:demo\", 100);\n\nboard.upsert_user(\n    &mut con,\n    \"player-1\",\n    1200.0,\n    Some(metadata_map(&[\n        (\"name\", \"Ada\"),\n        (\"description\", \"Solves production incidents before breakfast.\"),\n    ])),\n)?;\n\nlet top_players = board.get_top(&mut con, 5)?;","section_id":"synchronous-usage"},{"id":"asynchronous-usage-ex0","language":"rust","code":"use redis::Client;\n\nlet client = Client::open(\"redis://localhost:6379/\")?;\nlet mut con = client.get_multiplexed_async_connection().await?;\n\nlet board = AsyncRedisLeaderboard::new(\"leaderboard:demo\", 100);\n\nboard.upsert_user(\n    &mut con,\n    \"player-1\",\n    1200.0,\n    Some(metadata_map(&[\n        (\"name\", \"Ada\"),\n        (\"description\", \"Solves production incidents before breakfast.\"),\n    ])),\n).await?;\n\nlet top_players = board.get_top(&mut con, 5).await?;","section_id":"asynchronous-usage"},{"id":"data-model-ex0","language":"text","code":"leaderboard:demo\n  player-1 => 1225\n  player-2 => 1180\n  player-3 => 1105\n\nleaderboard:demo:user:player-1\n  name = Ada\n  description = Solves production incidents before breakfast.","section_id":"data-model"},{"id":"leaderboard-implementation-ex0","language":"rust","code":"pub fn upsert_user(\n    &self,\n    con: &mut dyn redis::ConnectionLike,\n    user_id: &str,\n    score: f64,\n    metadata: Option<Metadata>,\n) -> RedisResult<LeaderboardEntry> {\n    let payload = metadata.unwrap_or_default();\n\n    let mut pipe = redis::pipe();\n    pipe.atomic().cmd(\"ZADD\").arg(&self.key).arg(score).arg(user_id);\n    if !payload.is_empty() {\n        pipe.cmd(\"HSET\")\n            .arg(self.metadata_key(user_id))\n            .arg(flatten_metadata(&payload));\n    }\n    pipe.query::<()>(con)?;\n\n    let trimmed_user_ids = self.trim_to_max_entries(con)?;\n    let entry = self.get_user_entry(con, user_id)?;\n    Ok(entry.unwrap_or_else(|| LeaderboardEntry::new(0, user_id, score, payload, trimmed_user_ids)))\n}","section_id":"leaderboard-implementation"},{"id":"leaderboard-implementation-ex1","language":"rust","code":"pub fn get_around_rank(\n    &self,\n    con: &mut dyn redis::ConnectionLike,\n    rank: usize,\n    count: usize,\n) -> RedisResult<Vec<LeaderboardEntry>> {\n    let total_entries = self.get_size(con)? as usize;\n    if total_entries <= count {\n        return self.list_all(con);\n    }\n\n    let half_window = count / 2;\n    let mut start = rank.saturating_sub(1 + half_window);\n    let max_start = total_entries - count;\n    if start > max_start {\n        start = max_start;\n    }\n    let end = start + count - 1;\n\n    let entries: Vec<(String, f64)> = redis::cmd(\"ZRANGE\")\n        .arg(&self.key)\n        .arg(start)\n        .arg(end)\n        .arg(\"REV\")\n        .arg(\"WITHSCORES\")\n        .query(con)?;\n\n    self.hydrate_entries(con, entries, start + 1)\n}","section_id":"leaderboard-implementation"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir leaderboard-demo && cd leaderboard-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/leaderboard/rust\ncurl -O $BASE/Cargo.toml\ncurl -O $BASE/leaderboard.rs\ncurl -O $BASE/demo_server.rs","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"cargo run --bin demo_server","section_id":"start-the-demo-server"}]}
{"schema_version":2,"id":"develop/use-cases/memory-layer/dotnet","title":"Redis memory layer with NRedisStack","url":"https://redis.io/docs/latest/develop/use-cases/memory-layer/dotnet/","summary":"Build a Redis-backed memory layer in C# with NRedisStack, ONNX Runtime, and standard Redis commands — working memory in a Hash, long-term semantic recall as JSON with a vector index, and an event log in a Stream.","aliases":["/develop/use-cases/agent-memory/dotnet","/develop/use-cases/agent-memory/stackexchange.redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:54:57+01:00","children":[],"page_type":"content","content_hash":"e2d885fdc80b811b05a81f20145b78813b84bf393c91b1a8d9ad726c25419566","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a small Redis-backed memory layer in C# (.NET 8) with [NRedisStack](https://redis.io/docs/latest/develop/clients/dotnet) and the ONNX Runtime, using only standard Redis commands — no Redis Agent Memory SDK, no managed service. It includes a local web server built with the .NET [`HttpListener`](https://learn.microsoft.com/en-us/dotnet/api/system.net.httplistener) so you can send turns at the agent, watch working memory update in place, see semantically similar long-term memories recalled in real time, watch the write-time deduplication skip near-duplicates, and inspect the per-thread event log.\n\nThe embedder runs the ONNX-exported [`Xenova/all-MiniLM-L6-v2`](https://huggingface.co/Xenova/all-MiniLM-L6-v2) model — the same encoder the [Python](https://redis.io/docs/latest/develop/use-cases/memory-layer/redis-py) and [Node.js](https://redis.io/docs/latest/develop/use-cases/memory-layer/nodejs) examples use. .NET ONNX Runtime is the same C++ kernel that powers Python's `onnxruntime`, so the vectors produced here are numerically identical to the Python ones to within rounding noise. The distance bands the Python walkthrough quotes carry over to this demo without recalibration, and a memory written by one demo can be recalled by the other against the same Redis instance."},{"id":"overview","title":"Overview","role":"overview","text":"The memory layer splits across three Redis primitives, each handling one tier:\n\n* **Working memory** for the active session is a [Hash](https://redis.io/docs/latest/develop/data-types/hashes) at `agent:session:<threadId>` holding the goal, scratchpad, a rolling window of recent turns (as a JSON list inside one field), and a few audit timestamps. One [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) returns the whole session in a single round trip; every write refreshes the key's [`EXPIRE`](https://redis.io/docs/latest/commands/expire) so idle sessions decay on their own.\n* **Long-term memory** is a set of [JSON](https://redis.io/docs/latest/develop/data-types/json) documents at `agent:mem:<id>`, each carrying the memory text, a 384-dimensional embedding vector, and tag fields for user, namespace, kind (episodic / semantic), and source thread. A single [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) index covers the [HNSW vector field](https://redis.io/docs/latest/develop/ai/search-and-query/vectors) and every metadata field, so one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call performs the KNN with the metadata pre-filter in the same round trip. Write-time deduplication runs the same KNN at insert time and skips a new memory whose nearest existing entry is within a tighter threshold.\n* **Event log** for the agent's actions and observations is a [Stream](https://redis.io/docs/latest/develop/data-types/streams) at `agent:events:<threadId>`, appended with [`XADD MAXLEN ~`](https://redis.io/docs/latest/commands/xadd) so retention stays bounded automatically, replayed with [`XREVRANGE`](https://redis.io/docs/latest/commands/xrevrange).\n\nThat gives you:\n\n* A single round trip per tier: one [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) for the session, one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) for recall, one [`XADD`](https://redis.io/docs/latest/commands/xadd) for the event log.\n* Sub-millisecond reads on every step of the agent loop, so the memory layer doesn't dominate per-step latency.\n* Per-tier decay: short TTLs on working memory, longer on episodic memories, no TTL on semantic memories. Combined with a database-level [eviction policy](https://redis.io/docs/latest/develop/reference/eviction) (LFU is the common choice), memory stays bounded under pressure.\n* Scoping enforced inside the query: a recall query for `user=alice` will never see `user=bob`'s memories, because the TAG filter goes into the same [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call as the KNN."},{"id":"how-it-works","title":"How it works","role":"content","text":"Each turn through the agent loop touches all three tiers in one pass: append to working memory, recall similar long-term memories, write the turn back as a new memory (with deduplication), and append one event to the log."},{"id":"per-turn-flow","title":"Per-turn flow","role":"content","text":"1. The application calls `embedder.EncodeOne(text)` to turn the incoming turn into a 384-dimensional `float[]`.\n2. `session.AppendTurn(threadId, role, content, user, agentName)` reads the per-thread Hash with [`HGETALL`](https://redis.io/docs/latest/commands/hgetall), appends the new turn to the rolling window in application code, trims it back to the configured maximum, and writes the Hash back with [`HSET`](https://redis.io/docs/latest/commands/hset) + [`EXPIRE`](https://redis.io/docs/latest/commands/expire) inside a [`MULTI/EXEC`](https://redis.io/docs/latest/commands/multi). The session TTL refreshes on every write so an active thread stays alive.\n3. `memory.Recall(vec, user, @namespace, k: 5)` runs [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) with a TAG pre-filter and a `KNN 5` clause. Redis returns the closest matching memories together with their cosine distances; memories beyond the recall threshold are dropped before they reach the agent so an unrelated query doesn't surface confident-looking false positives.\n4. `memory.Remember(text, vec, user, @namespace, kind)` runs the same KNN with a tighter dedup threshold. If an existing memory is within the threshold, the new write is skipped and the existing memory's `hit_count` is incremented with [`JSON.NUMINCRBY`](https://redis.io/docs/latest/commands/json.numincrby); otherwise a fresh JSON document is written with [`JSON.SET`](https://redis.io/docs/latest/commands/json.set) and a per-kind [`EXPIRE`](https://redis.io/docs/latest/commands/expire) — `episodic` defaults to seven days, `semantic` has no TTL by default.\n5. `events.Record(threadId, action, detail)` appends one entry to the per-thread Stream with [`XADD MAXLEN ~`](https://redis.io/docs/latest/commands/xadd), bounding retention to roughly a thousand entries per thread without an explicit cleanup job.\n\nThe embedding is computed once and reused for steps 3 and 4 — there's no point encoding the same text twice. Recall runs before the write, so the agent doesn't see its own just-written turn echoed back as a recalled memory."},{"id":"the-session-store","title":"The session store","role":"content","text":"`AgentSession` wraps the working-memory Hash and the rolling turn window ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/memory-layer/dotnet/AgentSession.cs)):\n\n[code example]\n\nThe data model is one Hash per thread. The rolling turn window is stored as a JSON string in a single field so the whole session loads in one [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) — the hash never grows in size or field count as the conversation goes on.\n\n[code example]\n\nEvery write — `Start`, `AppendTurn`, `SetGoal`, `SetScratchpad` — runs the [`HSET`](https://redis.io/docs/latest/commands/hset) and [`EXPIRE`](https://redis.io/docs/latest/commands/expire) inside a [`MULTI`](https://redis.io/docs/latest/commands/multi) / [`EXEC`](https://redis.io/docs/latest/commands/exec) (via NRedisStack's `CreateTransaction`) so a connection drop between the two writes can't leave the session without a TTL."},{"id":"the-long-term-memory-store","title":"The long-term memory store","role":"content","text":"`LongTermMemory` owns the JSON documents, the vector index, the recall query, and the write-time deduplication ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/memory-layer/dotnet/LongTermMemory.cs)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each memory is a JSON document at `agent:mem:<id>`. The embedding is stored as a JSON array of floats so the document is human-readable from `redis-cli`; [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) still expects the *query* vector as raw `float32` bytes (packed by `LocalEmbedder.ToBytes` with explicit little-endian writes), regardless of how the indexed document stores it.\n\n[code example]\n\nThe Redis Search index is declared on the JSON document type with alias names so the query syntax stays compact (`FieldName(\"$.user\", \"user\")` writes `$.user AS user` into `FT.CREATE`):\n\n[code example]"},{"id":"the-query","title":"The query","role":"content","text":"Both recall and dedup share the same hybrid query: a TAG pre-filter in parentheses followed by `=>[KNN k @embedding $vec]`. With `DIALECT 2`, Redis applies the filter first and KNN-ranks only the matching documents.\n\n[code example]\n\n`distance` is the cosine *distance* (0 means identical, 2 means opposite). Recall and dedup share the same query shape; only the threshold differs — strict at write time so the index doesn't fill with paraphrases of the same fact, looser at read time so the agent gets a wider net of relevant memories."},{"id":"per-kind-ttls","title":"Per-kind TTLs","role":"content","text":"`Remember` resolves the entry's TTL from the memory's `kind`:\n\n| Kind      | Default TTL | When to use it                                              |\n|-----------|-------------|-------------------------------------------------------------|\n| `episodic` | 7 days     | Snapshots from a specific session that should decay.        |\n| `semantic` | none       | Distilled facts and preferences the agent carries forward.  |\n\nYou can override per write with `ttlSeconds: ...` on `Remember`, or pass a different `ttlByKind: ...` map to the `LongTermMemory` constructor — for example, to give semantic memories a six-month TTL while leaving episodic memories at seven days."},{"id":"the-event-log","title":"The event log","role":"content","text":"`AgentEventLog` is a thin wrapper over a per-thread Redis Stream ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/memory-layer/dotnet/AgentEventLog.cs)):\n\n[code example]\n\n`Record` calls [`XADD`](https://redis.io/docs/latest/commands/xadd) with `MAXLEN ~ 1000` (StackExchange.Redis's `useApproximateMaxLength: true`). The tilde lets Redis trim in whole-node units instead of exactly-N units, which is much cheaper at the cost of overshooting the bound by up to a node's worth — the right tradeoff for an audit log where exact length doesn't matter.\n\nThe Stream is independent of the session Hash and the long-term JSON documents: it answers \"what just happened\" without competing with either of those for indexing or memory budget. Consumer groups (not used in this demo) would let downstream workers — summarisers, consolidators, audit pipelines — replay the log without losing position."},{"id":"concurrency-caveats","title":"Concurrency caveats","role":"content","text":"The three helpers above trade correctness under heavy concurrency for clarity. Each is fine on a single-process demo, but lifting the code into a real multi-worker agent surfaces three races worth knowing about:\n\n* **Working memory is read-modify-write.** `AgentSession.AppendTurn` calls [`HGETALL`](https://redis.io/docs/latest/commands/hgetall), mutates the `recent_turns` list in application code, and writes the Hash back with [`HSET`](https://redis.io/docs/latest/commands/hset). Two concurrent turns on the same thread can both read the same `recent_turns`, append different entries, and write back — last writer wins, the other turn is silently lost. The robust fix is either a [`WATCH`](https://redis.io/docs/latest/commands/watch) / [`MULTI`](https://redis.io/docs/latest/commands/multi) / [`EXEC`](https://redis.io/docs/latest/commands/exec) loop around the read-modify-write or a small [Lua script](https://redis.io/docs/latest/commands/eval) that does the append atomically server-side.\n\n* **Long-term dedup is not atomic.** `LongTermMemory.Remember` runs a [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) KNN lookup, decides whether the candidate is a duplicate, and (if not) calls [`JSON.SET`](https://redis.io/docs/latest/commands/json.set). Two workers seeing the same fact in flight can each fail to see the other's not-yet-committed write and both insert a new memory. The pragmatic fix is to accept that the index will occasionally hold near-duplicates and run a background consolidator that periodically scans for memory pairs within a tight distance and merges them, rather than trying to make the write itself atomic.\n\n* **The active thread is server state.** The demo server keeps a single `CurrentThreadId` that `/new_thread` and `/reset` mutate. `HandleTurn` reads it without coordination, so a turn racing with a thread rotation can apply to the previous thread. This is cosmetic for a one-user browser demo. A multi-user agent would carry the thread id on the request itself rather than as shared server state.\n\nThose caveats are deliberate. A more conservative implementation would obscure the Redis-shaped parts of the pattern; the demo prioritizes a small, readable code path that maps directly onto the commands in the prose above."},{"id":"pre-seeding-long-term-memory","title":"Pre-seeding long-term memory","role":"content","text":"In a real deployment the memory store fills up organically as the agent reasons over user turns: each turn produces zero or more memories that flow into the store, with deduplication catching repeats. For the demo, `SeedMemory.cs` pre-loads a small set of mixed semantic and episodic memories so the very first recall query returns something useful ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/memory-layer/dotnet/SeedMemory.cs)):\n\n[code example]\n\nThe seed list mixes long-lived facts and preferences (`semantic`) with snapshots of past sessions (`episodic`), so the **Kind to write** control in the demo has something to switch between when a new turn is being remembered."},{"id":"the-interactive-demo","title":"The interactive demo","role":"content","text":"`Program.cs` runs a .NET [`HttpListener`](https://learn.microsoft.com/en-us/dotnet/api/system.net.httplistener) on port 8093, dispatching to handlers on the thread pool. The HTML page exposes three live panels — working memory, recalled memories, event log — plus a memories table for admin actions. Endpoints:\n\n| Endpoint            | What it does                                                                    |\n|---------------------|---------------------------------------------------------------------------------|\n| `GET  /state`       | Index info, current session, in-scope long-term memories, and recent events.    |\n| `POST /turn`        | Embed the text, append to working memory, recall similar memories, optionally write a new memory (with dedup), append an event. |\n| `POST /new_thread`  | Start a fresh thread; long-term memory and other threads are untouched.         |\n| `POST /reset`       | Drop every long-term memory and re-seed the sample set.                         |\n| `POST /drop_memory` | Delete a single long-term memory by id.                                         |\n\nThe server holds one `LocalEmbedder`, one `AgentSession`, one `LongTermMemory`, and one `AgentEventLog` for the lifetime of the process. The \"current thread\" is a single property on the demo object that the **New thread** button rotates — every browser tab inherits the same thread until you explicitly start a new one."},{"id":"run-the-demo-locally","title":"Run the demo locally","role":"content","text":"1.  Clone the [`redis/docs`](https://github.com/redis/docs) repository and change into the example\n    directory:\n\n    [code example]\n\n2.  Restore and build the project. You'll need the [.NET 8 SDK](https://dotnet.microsoft.com/download)\n    or later:\n\n    [code example]\n\n3.  Make sure a Redis instance with Redis Search and Redis JSON is running locally on\n    port 6379. [Redis Stack](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack)\n    ships both, or [Redis 8](https://redis.io/docs/latest/develop/ai/search-and-query) with the\n    Search and JSON modules enabled.\n\n4.  Start the demo. The first run downloads the\n    [`Xenova/all-MiniLM-L6-v2`](https://huggingface.co/Xenova/all-MiniLM-L6-v2) ONNX\n    weights (around 90 MB) and `vocab.txt` into a local `model_cache/` directory next\n    to the binary:\n\n    [code example]\n\n5.  Open <http://localhost:8093> and try some turns:\n\n    *  **\"Remind me which theme I prefer in editors.\"** — paraphrase of a seeded\n       semantic memory (\"The user dislikes dark mode and prefers a high-contrast\n       light theme...\"). You should see that memory recalled with a cosine\n       distance around 0.47, comfortably under the 0.55 default recall\n       threshold.\n    *  **\"What did we discuss about the order-routing outage?\"** — paraphrase of\n       a seeded episodic memory; the postmortem memory should recall around\n       0.44. Switch the **Kind to write** dropdown to `skip` so the question\n       itself doesn't enter long-term memory.\n    *  **\"I prefer concise answers without filler phrases.\"** — paraphrase of\n       a seeded *semantic* memory. Switch the **Kind to write** dropdown to\n       `semantic` so the dedup KNN runs in the same kind as the seed (dedup\n       is scoped per kind, on purpose, so an episodic write can't collapse\n       onto a semantic memory). You should then see the write **deduped**\n       onto the existing memory at a cosine distance around 0.15, with\n       `hit_count` ticking up in the memories table.\n    *  **\"My favorite color is teal.\"** — unrelated to any seed; nothing\n       recalls above the threshold (every seed lands above 0.8), and the new\n       memory is written as `episodic` (or `semantic`, depending on the\n       dropdown) under a fresh id.\n    *  Switch the **User** field to `bob` and re-ask any of the above — recall\n       returns nothing because the seed memories live under `default`. That's\n       the TAG pre-filter at work inside [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search).\n    *  Slide the **Recall threshold** down to 0.30 to see borderline paraphrases\n       drop out of the recall set, then back up to 0.70 to watch them return.\n\n    The .NET ONNX Runtime is the same C++ kernel that powers Python's\n    `onnxruntime`, so distances here match the Python demo to four decimal\n    places. `Xenova/all-MiniLM-L6-v2` puts a faithful paraphrase in the\n    0.15 – 0.50 cosine-distance range, a loose paraphrase or related topic in\n    the 0.50 – 0.80 range, and unrelated queries above 0.8 — which is what\n    motivates the 0.55 default recall threshold and the 0.20 default dedup\n    threshold. A stricter embedding model (or a domain-tuned one) would let\n    you tighten both; a noisier one would push them up. The right thresholds\n    are always a function of the model, the corpus, and how conservative the\n    agent needs to be about accepting a memory as a match.\n\nThe server is read/write against your local Redis. The default memory index is `agentmem:idx`, JSON keys live under `agent:mem:`, session Hashes under `agent:session:`, and event Streams under `agent:events:`. Useful flags (pass them after `--`, for example `dotnet run -- --no-reset`):\n\n* `--no-reset` — keep the existing long-term memories across restarts instead of dropping and re-seeding.\n* `--session-ttl-seconds` — change the working-memory TTL (default 3600).\n* `--dedup-threshold` — change the cosine-distance cutoff for write-time deduplication.\n* `--recall-threshold` — change the default cosine-distance cutoff for recall."}],"examples":[{"id":"the-session-store-ex0","language":"csharp","code":"using StackExchange.Redis;\nusing AgentMemoryDemo;\n\nvar mux = ConnectionMultiplexer.Connect(\"localhost:6379\");\nvar db = mux.GetDatabase();\n\nvar session = new AgentSession(\n    db,\n    keyPrefix: \"agent:session:\",\n    defaultTtlSeconds: 3600,  // one hour\n    maxTurns: 20);            // rolling window per thread\n\nstring threadId = session.NewThreadId();\nsession.Start(threadId, user: \"alice\", agentName: \"demo-agent\",\n    goal: \"Plan next week's meetings.\");\nsession.AppendTurn(threadId, role: \"user\",\n    content: \"Schedule a budget review with finance.\",\n    user: \"alice\", agentName: \"demo-agent\");\nSessionState? state = session.Load(threadId);\nConsole.WriteLine($\"{state?.TurnCount} {state?.RecentTurns.Count} {state?.TtlSeconds}\");","section_id":"the-session-store"},{"id":"the-session-store-ex1","language":"text","code":"agent:session:9f3d2a4b8c61\n  thread_id=9f3d2a4b8c61\n  user=alice\n  agent=demo-agent\n  goal=Plan next week's meetings.\n  scratchpad=Need to confirm finance's availability.\n  turn_count=4\n  created_ts=1715990400.12\n  last_active_ts=1715990650.83\n  recent_turns=[{\"role\":\"user\",\"content\":\"...\",\"ts\":...}, ...]","section_id":"the-session-store"},{"id":"the-long-term-memory-store-ex0","language":"csharp","code":"using AgentMemoryDemo;\n\nvar memory = new LongTermMemory(\n    db,\n    indexName: \"agentmem:idx\",\n    keyPrefix: \"agent:mem:\",\n    dedupThreshold: 0.20,    // cosine distance — tight at write time\n    recallThreshold: 0.55);  // looser at read time\nvar embedder = await LocalEmbedder.CreateAsync();\nmemory.CreateIndex();  // idempotent\n\n// Write a memory. The same KNN that powers recall also runs here at\n// a tighter threshold so paraphrases of the same fact collapse.\nfloat[] vec = embedder.EncodeOne(\"The user prefers light mode in editors.\");\nWriteResult result = memory.Remember(\n    text: \"The user prefers light mode in editors.\",\n    embedding: vec,\n    user: \"alice\",\n    @namespace: \"default\",\n    kind: \"semantic\",\n    sourceThread: \"9f3d2a4b8c61\");\nConsole.WriteLine($\"deduped={result.Deduped} id={result.Id} dist={result.ExistingDistance}\");\n\n// Recall against a later question.\nfloat[] q = embedder.EncodeOne(\"Which theme does this user like?\");\nvar hits = memory.Recall(\n    queryEmbedding: q,\n    user: \"alice\",\n    @namespace: \"default\",\n    k: 5);\nforeach (var h in hits)\n{\n    Console.WriteLine($\"{h.Distance:F3} [{h.Kind}] {h.Text}\");\n}","section_id":"the-long-term-memory-store"},{"id":"data-model-ex0","language":"json","code":"agent:mem:7c3f8a1b9e02\n{\n  \"id\": \"7c3f8a1b9e02\",\n  \"user\": \"alice\",\n  \"namespace\": \"default\",\n  \"kind\": \"semantic\",\n  \"source_thread\": \"9f3d2a4b8c61\",\n  \"text\": \"The user prefers light mode in editors.\",\n  \"embedding\": [0.013, -0.041, ...],\n  \"created_ts\": 1715990400.12,\n  \"hit_count\": 0\n}","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"FT.CREATE agentmem:idx\n  ON JSON PREFIX 1 agent:mem:\n  SCHEMA\n    $.text          AS text          TEXT\n    $.user          AS user          TAG\n    $.namespace     AS namespace     TAG\n    $.kind          AS kind          TAG\n    $.source_thread AS source_thread TAG\n    $.created_ts    AS created_ts    NUMERIC SORTABLE\n    $.hit_count     AS hit_count     NUMERIC SORTABLE\n    $.embedding     AS embedding     VECTOR HNSW 6\n                                       TYPE FLOAT32 DIM 384\n                                       DISTANCE_METRIC COSINE","section_id":"data-model"},{"id":"the-query-ex0","language":"text","code":"FT.SEARCH agentmem:idx\n  \"(@user:{alice} @namespace:{default} @kind:{semantic})\n     =>[KNN 5 @embedding $vec AS distance]\"\n  PARAMS 2 vec <384-float32-bytes>\n  SORTBY distance\n  RETURN 8 user namespace kind source_thread text created_ts hit_count distance\n  DIALECT 2","section_id":"the-query"},{"id":"the-event-log-ex0","language":"csharp","code":"var events = new AgentEventLog(db, maxLen: 1000);\nevents.Record(threadId, \"turn_appended:user\",\n    \"Schedule a budget review with finance.\");\nevents.Record(threadId, \"memory_written\",\n    \"wrote 7c3f8a1b9e02 as semantic\");\n\nforeach (var e in events.Recent(threadId, count: 20))\n{\n    Console.WriteLine($\"{e.Action} {e.Detail}\");\n}","section_id":"the-event-log"},{"id":"pre-seeding-long-term-memory-ex0","language":"csharp","code":"using AgentMemoryDemo;\n\nvar memory = new LongTermMemory(db);\nvar embedder = await LocalEmbedder.CreateAsync();\nmemory.CreateIndex();\nSeedMemory.Seed(memory, embedder, user: \"default\", @namespace: \"default\");","section_id":"pre-seeding-long-term-memory"},{"id":"run-the-demo-locally-ex0","language":"bash","code":"git clone https://github.com/redis/docs.git\n    cd docs/content/develop/use-cases/memory-layer/dotnet","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex1","language":"bash","code":"dotnet build","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex2","language":"bash","code":"dotnet run","section_id":"run-the-demo-locally"}]}
{"schema_version":2,"id":"develop/use-cases/memory-layer/go","title":"Redis memory layer with go-redis","url":"https://redis.io/docs/latest/develop/use-cases/memory-layer/go/","summary":"Build a Redis-backed memory layer in Go with go-redis, Hugot, and standard Redis commands — working memory in a Hash, long-term semantic recall as JSON with a vector index, and an event log in a Stream.","aliases":["/develop/use-cases/agent-memory/go","/develop/use-cases/agent-memory/go-redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:54:57+01:00","children":[],"page_type":"content","content_hash":"e594d51618b4f90f09a8b75ab84ef9882e07af821605fd15300dd6e2388fa6e1","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a small Redis-backed memory layer in Go with [`go-redis`](https://redis.io/docs/latest/develop/clients/go) and the [Hugot](https://pkg.go.dev/github.com/knights-analytics/hugot) library, using only standard Redis commands — no Redis Agent Memory SDK, no managed service. It includes a local web server built with Go's standard [`net/http`](https://pkg.go.dev/net/http) so you can send turns at the agent, watch working memory update in place, see semantically similar long-term memories recalled in real time, watch the write-time deduplication skip near-duplicates, and inspect the per-thread event log.\n\nThe embedder is [Hugot](https://pkg.go.dev/github.com/knights-analytics/hugot) running the ONNX-exported `sentence-transformers/all-MiniLM-L6-v2` model — the same encoder the [vector search guide](https://redis.io/docs/latest/develop/clients/go/vecsearch) uses for Go and the same one the [Python](https://redis.io/docs/latest/develop/use-cases/memory-layer/redis-py) example loads. Hugot drives the same ONNX Runtime kernel under the hood as Python's `onnxruntime`, so the vectors produced here are numerically identical to the Python ones to within rounding noise, and the distance bands the Python walkthrough quotes carry over to this demo without recalibration. A memory written by one demo can be recalled by the other against the same Redis instance."},{"id":"overview","title":"Overview","role":"overview","text":"The memory layer splits across three Redis primitives, each handling one tier:\n\n* **Working memory** for the active session is a [Hash](https://redis.io/docs/latest/develop/data-types/hashes) at `agent:session:<thread_id>` holding the goal, scratchpad, a rolling window of recent turns (as a JSON list inside one field), and a few audit timestamps. One [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) returns the whole session in a single round trip; every write refreshes the key's [`EXPIRE`](https://redis.io/docs/latest/commands/expire) so idle sessions decay on their own.\n* **Long-term memory** is a set of [JSON](https://redis.io/docs/latest/develop/data-types/json) documents at `agent:mem:<id>`, each carrying the memory text, a 384-dimensional embedding vector, and tag fields for user, namespace, kind (episodic / semantic), and source thread. A single [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) index covers the [HNSW vector field](https://redis.io/docs/latest/develop/ai/search-and-query/vectors) and every metadata field, so one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call performs the KNN with the metadata pre-filter in the same round trip. Write-time deduplication runs the same KNN at insert time and skips a new memory whose nearest existing entry is within a tighter threshold.\n* **Event log** for the agent's actions and observations is a [Stream](https://redis.io/docs/latest/develop/data-types/streams) at `agent:events:<thread_id>`, appended with [`XADD MAXLEN ~`](https://redis.io/docs/latest/commands/xadd) so retention stays bounded automatically, replayed with [`XREVRANGE`](https://redis.io/docs/latest/commands/xrevrange).\n\nThat gives you:\n\n* One Redis Search call per recall: [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) does the KNN + TAG pre-filter in a single round trip (a per-row [`TTL`](https://redis.io/docs/latest/commands/ttl) follow-up is the only other read the helper issues, just to populate the `ttl_seconds` field for the admin panel). Working memory is one [`HGETALL`](https://redis.io/docs/latest/commands/hgetall); the event log is one [`XADD`](https://redis.io/docs/latest/commands/xadd).\n* Sub-millisecond reads on every step of the agent loop, so the memory layer doesn't dominate per-step latency.\n* Per-tier decay: short TTLs on working memory, longer on episodic memories, no TTL on semantic memories. Combined with a database-level [eviction policy](https://redis.io/docs/latest/develop/reference/eviction) (LFU is the common choice), memory stays bounded under pressure.\n* Scoping enforced inside the query: a recall query for `user=alice` will never see `user=bob`'s memories, because the TAG filter goes into the same [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call as the KNN."},{"id":"how-it-works","title":"How it works","role":"content","text":"Each turn through the agent loop touches all three tiers in one pass: append to working memory, recall similar long-term memories, write the turn back as a new memory (with deduplication), and append one event to the log."},{"id":"per-turn-flow","title":"Per-turn flow","role":"content","text":"1. The application calls `embedder.EncodeOne(ctx, text)` to turn the incoming turn into a 384-element `[]float32`.\n2. `session.AppendTurn(ctx, threadID, AppendTurnParams{...})` reads the per-thread Hash with [`HGETALL`](https://redis.io/docs/latest/commands/hgetall), appends the new turn to the rolling window in application code, trims it back to the configured maximum, and writes the Hash back with [`HSET`](https://redis.io/docs/latest/commands/hset) + [`EXPIRE`](https://redis.io/docs/latest/commands/expire) inside a [`MULTI`](https://redis.io/docs/latest/commands/multi) / [`EXEC`](https://redis.io/docs/latest/commands/exec) (go-redis's `TxPipelined`). The session TTL refreshes on every write so an active thread stays alive.\n3. `memory.Recall(ctx, RecallParams{...})` runs [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) with a TAG pre-filter and a `KNN 5` clause. Redis returns the closest matching memories together with their cosine distances; memories beyond the recall threshold are dropped before they reach the agent so an unrelated query doesn't surface confident-looking false positives.\n4. `memory.Remember(ctx, RememberParams{...})` runs the same KNN with a tighter dedup threshold. If an existing memory is within the threshold, the new write is skipped and the existing memory's `hit_count` is incremented with [`JSON.NUMINCRBY`](https://redis.io/docs/latest/commands/json.numincrby); otherwise a fresh JSON document is written with [`JSON.SET`](https://redis.io/docs/latest/commands/json.set) and a per-kind [`EXPIRE`](https://redis.io/docs/latest/commands/expire) — `episodic` defaults to seven days, `semantic` has no TTL by default.\n5. `events.Record(ctx, threadID, action, detail)` appends one entry to the per-thread Stream with [`XADD MAXLEN ~`](https://redis.io/docs/latest/commands/xadd), bounding retention to roughly a thousand entries per thread without an explicit cleanup job.\n\nThe embedding is computed once and reused for steps 3 and 4 — there's no point encoding the same text twice. Recall runs before the write, so the agent doesn't see its own just-written turn echoed back as a recalled memory."},{"id":"the-session-store","title":"The session store","role":"content","text":"`AgentSession` wraps the working-memory Hash and the rolling turn window ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/memory-layer/go/session_store.go)):\n\n[code example]\n\nThe data model is one Hash per thread. The rolling turn window is stored as a JSON string in a single field so the whole session loads in one [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) — the hash never grows in size or field count as the conversation goes on.\n\n[code example]\n\nEvery write — `Start`, `AppendTurn`, `SetGoal` — runs the [`HSET`](https://redis.io/docs/latest/commands/hset) and [`EXPIRE`](https://redis.io/docs/latest/commands/expire) inside a `client.TxPipelined` block so a connection drop between the two writes can't leave the session without a TTL."},{"id":"the-long-term-memory-store","title":"The long-term memory store","role":"content","text":"`LongTermMemory` owns the JSON documents, the vector index, the recall query, and the write-time deduplication ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/memory-layer/go/long_term_memory.go)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each memory is a JSON document at `agent:mem:<id>`. The embedding is stored as a JSON array of floats so the document is human-readable from `redis-cli`; [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) still expects the *query* vector as raw `float32` bytes (`embeddings.go`'s `FloatsToBytes` packs them in little-endian order), regardless of how the indexed document stores it.\n\n[code example]\n\nThe Redis Search index is declared on the JSON document type with `As` aliases on each path so the query syntax stays compact:\n\n[code example]"},{"id":"the-query","title":"The query","role":"content","text":"Both recall and dedup share the same hybrid query: a TAG pre-filter in parentheses followed by `=>[KNN k @embedding $vec]`. With `DIALECT 2`, Redis applies the filter first and KNN-ranks only the matching documents.\n\n[code example]\n\n`distance` is the cosine *distance* (0 means identical, 2 means opposite). Recall and dedup share the same query shape; only the threshold differs — strict at write time so the index doesn't fill with paraphrases of the same fact, looser at read time so the agent gets a wider net of relevant memories."},{"id":"per-kind-ttls","title":"Per-kind TTLs","role":"content","text":"`Remember` resolves the entry's TTL from the memory's `Kind`:\n\n| Kind      | Default TTL | When to use it                                              |\n|-----------|-------------|-------------------------------------------------------------|\n| `episodic` | 7 days     | Snapshots from a specific session that should decay.        |\n| `semantic` | none       | Distilled facts and preferences the agent carries forward.  |\n\nYou can override per write by setting `TTLSeconds` on `RememberParams`, or pass a different `ttlByKind` map to `NewLongTermMemory` — for example, to give semantic memories a six-month TTL while leaving episodic memories at seven days."},{"id":"the-event-log","title":"The event log","role":"content","text":"`AgentEventLog` is a thin wrapper over a per-thread Redis Stream ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/memory-layer/go/event_log.go)):\n\n[code example]\n\n`Record` calls [`XADD`](https://redis.io/docs/latest/commands/xadd) with `MAXLEN ~ 1000` (go-redis's `XAddArgs.Approx: true`). The tilde lets Redis trim in whole-node units instead of exactly-N units, which is much cheaper at the cost of overshooting the bound by up to a node's worth — the right tradeoff for an audit log where exact length doesn't matter.\n\nThe Stream is independent of the session Hash and the long-term JSON documents: it answers \"what just happened\" without competing with either of those for indexing or memory budget. Consumer groups (not used in this demo) would let downstream workers — summarisers, consolidators, audit pipelines — replay the log without losing position."},{"id":"concurrency-caveats","title":"Concurrency caveats","role":"content","text":"The three helpers above trade correctness under heavy concurrency for clarity. Each is fine on a single-process demo, but lifting the code into a real multi-worker agent surfaces three races worth knowing about:\n\n* **Working memory is read-modify-write.** `AgentSession.AppendTurn` calls [`HGETALL`](https://redis.io/docs/latest/commands/hgetall), mutates the `RecentTurns` slice in application code, and writes the Hash back with [`HSET`](https://redis.io/docs/latest/commands/hset). Two concurrent turns on the same thread can both read the same `RecentTurns`, append different entries, and write back — last writer wins, the other turn is silently lost. The robust fix is either a [`WATCH`](https://redis.io/docs/latest/commands/watch) / [`MULTI`](https://redis.io/docs/latest/commands/multi) / [`EXEC`](https://redis.io/docs/latest/commands/exec) loop around the read-modify-write or a small [Lua script](https://redis.io/docs/latest/commands/eval) that does the append atomically server-side.\n\n* **Long-term dedup is not atomic.** `LongTermMemory.Remember` runs a [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) KNN lookup, decides whether the candidate is a duplicate, and (if not) calls [`JSON.SET`](https://redis.io/docs/latest/commands/json.set). Two workers seeing the same fact in flight can each fail to see the other's not-yet-committed write and both insert a new memory. The pragmatic fix is to accept that the index will occasionally hold near-duplicates and run a background consolidator that periodically scans for memory pairs within a tight distance and merges them, rather than trying to make the write itself atomic.\n\n* **The active thread is server state.** The demo server keeps a single `currentThreadID` (a `sync.Mutex`-protected string field on `AgentMemoryDemo`) that `/new_thread` and `/reset` mutate. `HandleTurn` reads it under the mutex but then drops the lock immediately, so a turn racing with a thread rotation can apply to the previous thread. This is cosmetic for a one-user browser demo. A multi-user agent would carry the thread id on the request itself rather than as shared server state.\n\nThose caveats are deliberate. A more conservative implementation would obscure the Redis-shaped parts of the pattern; the demo prioritizes a small, readable code path that maps directly onto the commands in the prose above."},{"id":"pre-seeding-long-term-memory","title":"Pre-seeding long-term memory","role":"content","text":"In a real deployment the memory store fills up organically as the agent reasons over user turns: each turn produces zero or more memories that flow into the store, with deduplication catching repeats. For the demo, `seed_memory.go` pre-loads a small set of mixed semantic and episodic memories so the very first recall query returns something useful ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/memory-layer/go/seed_memory.go)):\n\n[code example]\n\nThe seed list mixes long-lived facts and preferences (`semantic`) with snapshots of past sessions (`episodic`), so the **Kind to write** control in the demo has something to switch between when a new turn is being remembered."},{"id":"the-interactive-demo","title":"The interactive demo","role":"content","text":"`main.go` runs an [`http.Server`](https://pkg.go.dev/net/http#Server) on port 8090, dispatching to handlers on the standard library's request-per-goroutine pool. The HTML page exposes three live panels — working memory, recalled memories, event log — plus a memories table for admin actions. Endpoints:\n\n| Endpoint            | What it does                                                                    |\n|---------------------|---------------------------------------------------------------------------------|\n| `GET  /state`       | Index info, current session, in-scope long-term memories, and recent events.    |\n| `POST /turn`        | Embed the text, append to working memory, recall similar memories, optionally write a new memory (with dedup), append an event. |\n| `POST /new_thread`  | Start a fresh thread; long-term memory and other threads are untouched.         |\n| `POST /reset`       | Drop every long-term memory and re-seed the sample set.                         |\n| `POST /drop_memory` | Delete a single long-term memory by id.                                         |\n\nThe server holds one `LocalEmbedder`, one `AgentSession`, one `LongTermMemory`, and one `AgentEventLog` for the lifetime of the process. The \"current thread\" is a `sync.Mutex`-protected string field on `AgentMemoryDemo` that the **New thread** button rotates — every browser tab inherits the same thread until you explicitly start a new one."},{"id":"run-the-demo-locally","title":"Run the demo locally","role":"content","text":"1.  Clone the [`redis/docs`](https://github.com/redis/docs) repository and change into the example\n    directory:\n\n    [code example]\n\n2.  Resolve the dependencies. You'll need [Go 1.26](https://go.dev/dl/) or later (the version `go.mod` declares — Hugot tracks recent toolchain releases):\n\n    [code example]\n\n3.  Make sure a Redis instance with Redis Search and Redis JSON is running locally on\n    port 6379. [Redis Stack](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack)\n    ships both, or [Redis 8](https://redis.io/docs/latest/develop/ai/search-and-query) with the\n    Search and JSON modules enabled.\n\n4.  Start the demo. The first run downloads the ONNX-exported\n    `sentence-transformers/all-MiniLM-L6-v2` weights into the local `./models`\n    directory:\n\n    [code example]\n\n5.  Open <http://localhost:8090> and try some turns:\n\n    *  **\"Remind me which theme I prefer in editors.\"** — paraphrase of a seeded\n       semantic memory (\"The user dislikes dark mode and prefers a high-contrast\n       light theme...\"). You should see that memory recalled with a cosine\n       distance around 0.47, comfortably under the 0.55 default recall\n       threshold.\n    *  **\"What did we discuss about the order-routing outage?\"** — paraphrase of\n       a seeded episodic memory; the postmortem memory should recall around\n       0.44. Switch the **Kind to write** dropdown to `skip` so the question\n       itself doesn't enter long-term memory.\n    *  **\"I prefer concise answers without filler phrases.\"** — paraphrase of\n       a seeded *semantic* memory. Switch the **Kind to write** dropdown to\n       `semantic` so the dedup KNN runs in the same kind as the seed (dedup\n       is scoped per kind, on purpose, so an episodic write can't collapse\n       onto a semantic memory). You should then see the write **deduped**\n       onto the existing memory at a cosine distance around 0.15, with\n       `hit_count` ticking up in the memories table.\n    *  **\"My favorite color is teal.\"** — unrelated to any seed; nothing\n       recalls above the threshold (every seed lands above 0.8), and the new\n       memory is written as `episodic` (or `semantic`, depending on the\n       dropdown) under a fresh id.\n    *  Switch the **User** field to `bob` and re-ask any of the above — recall\n       returns nothing because the seed memories live under `default`. That's\n       the TAG pre-filter at work inside [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search).\n    *  Slide the **Recall threshold** down to 0.30 to see borderline paraphrases\n       drop out of the recall set, then back up to 0.70 to watch them return.\n\n    Hugot's ONNX Runtime is the same C++ kernel that powers Python's\n    `onnxruntime`, so distances here match the Python demo to four decimal\n    places. `sentence-transformers/all-MiniLM-L6-v2` puts a faithful paraphrase\n    in the 0.15 – 0.50 cosine-distance range, a loose paraphrase or related\n    topic in the 0.50 – 0.80 range, and unrelated queries above 0.8 — which\n    is what motivates the 0.55 default recall threshold and the 0.20 default\n    dedup threshold. A stricter embedding model (or a domain-tuned one) would\n    let you tighten both; a noisier one would push them up. The right\n    thresholds are always a function of the model, the corpus, and how\n    conservative the agent needs to be about accepting a memory as a match.\n\nThe server is read/write against your local Redis. The default memory index is `agentmem:idx`, JSON keys live under `agent:mem:`, session Hashes under `agent:session:`, and event Streams under `agent:events:`. Useful flags:\n\n* `--host` / `--port` — change the HTTP bind address (default `127.0.0.1:8090`).\n* `--redis-host` / `--redis-port` — point at a non-local Redis (default `localhost:6379`).\n* `--mem-index-name` / `--mem-key-prefix` / `--session-key-prefix` / `--event-key-prefix` — relocate the index name and the three key prefixes (to run several demos against one Redis without colliding, for example).\n* `--no-reset` — keep the existing long-term memories across restarts instead of dropping and re-seeding.\n* `--session-ttl-seconds` — change the working-memory TTL (default 3600).\n* `--dedup-threshold` — change the cosine-distance cutoff for write-time deduplication.\n* `--recall-threshold` — change the default cosine-distance cutoff for recall."}],"examples":[{"id":"the-session-store-ex0","language":"go","code":"import (\n    \"context\"\n    \"fmt\"\n    \"github.com/redis/go-redis/v9\"\n)\n\nclient := redis.NewClient(&redis.Options{Addr: \"localhost:6379\"})\nsession := NewAgentSession(client, \"agent:session:\", 3600, 20)\n\nctx := context.Background()\nthreadID := session.NewThreadID()\nstate, err := session.Start(ctx, threadID, StartParams{\n    User:  \"alice\",\n    Agent: \"demo-agent\",\n    Goal:  \"Plan next week's meetings.\",\n})\nif err != nil {\n    panic(err)\n}\nstate, err = session.AppendTurn(ctx, threadID, AppendTurnParams{\n    Role:    \"user\",\n    Content: \"Schedule a budget review with finance.\",\n    User:    \"alice\",\n    Agent:   \"demo-agent\",\n})\nif err != nil {\n    panic(err)\n}\nfmt.Println(state.TurnCount, len(state.RecentTurns), state.TTLSeconds)","section_id":"the-session-store"},{"id":"the-session-store-ex1","language":"text","code":"agent:session:9f3d2a4b8c61\n  thread_id=9f3d2a4b8c61\n  user=alice\n  agent=demo-agent\n  goal=Plan next week's meetings.\n  scratchpad=Need to confirm finance's availability.\n  turn_count=4\n  created_ts=1715990400.12\n  last_active_ts=1715990650.83\n  recent_turns=[{\"role\":\"user\",\"content\":\"...\",\"ts\":...}, ...]","section_id":"the-session-store"},{"id":"the-long-term-memory-store-ex0","language":"go","code":"memory := NewLongTermMemory(\n    client,\n    \"agentmem:idx\",\n    \"agent:mem:\",\n    384,\n    0.20,  // dedup threshold — tight at write time\n    0.55,  // recall threshold — looser at read time\n    nil,   // use the default per-kind TTL map\n)\nembedder, err := NewLocalEmbedder(ctx, \"\", \"\")\nif err != nil {\n    panic(err)\n}\ndefer embedder.Close()\nif err := memory.CreateIndex(ctx); err != nil {  // idempotent\n    panic(err)\n}\n\n// Write a memory. The same KNN that powers recall also runs here at\n// a tighter threshold so paraphrases of the same fact collapse.\nvec, err := embedder.EncodeOne(ctx, \"The user prefers light mode in editors.\")\nif err != nil {\n    panic(err)\n}\nresult, err := memory.Remember(ctx, RememberParams{\n    Text:         \"The user prefers light mode in editors.\",\n    Embedding:    vec,\n    User:         \"alice\",\n    Namespace:    \"default\",\n    Kind:         \"semantic\",\n    SourceThread: \"9f3d2a4b8c61\",\n})\nif err != nil {\n    panic(err)\n}\nfmt.Printf(\"deduped=%v id=%s dist=%v\\n\", result.Deduped, result.ID, result.ExistingDistance)\n\n// Recall against a later question.\nq, _ := embedder.EncodeOne(ctx, \"Which theme does this user like?\")\nhits, _ := memory.Recall(ctx, RecallParams{\n    QueryEmbedding: q,\n    User:           \"alice\",\n    Namespace:      \"default\",\n    K:              5,\n})\nfor _, h := range hits {\n    fmt.Printf(\"%.3f [%s] %s\\n\", *h.Distance, h.Kind, h.Text)\n}","section_id":"the-long-term-memory-store"},{"id":"data-model-ex0","language":"json","code":"agent:mem:7c3f8a1b9e02\n{\n  \"id\": \"7c3f8a1b9e02\",\n  \"user\": \"alice\",\n  \"namespace\": \"default\",\n  \"kind\": \"semantic\",\n  \"source_thread\": \"9f3d2a4b8c61\",\n  \"text\": \"The user prefers light mode in editors.\",\n  \"embedding\": [0.013, -0.041, ...],\n  \"created_ts\": 1715990400.12,\n  \"hit_count\": 0\n}","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"FT.CREATE agentmem:idx\n  ON JSON PREFIX 1 agent:mem:\n  SCHEMA\n    $.text          AS text          TEXT\n    $.user          AS user          TAG\n    $.namespace     AS namespace     TAG\n    $.kind          AS kind          TAG\n    $.source_thread AS source_thread TAG\n    $.created_ts    AS created_ts    NUMERIC SORTABLE\n    $.hit_count     AS hit_count     NUMERIC SORTABLE\n    $.embedding     AS embedding     VECTOR HNSW 6\n                                       TYPE FLOAT32 DIM 384\n                                       DISTANCE_METRIC COSINE","section_id":"data-model"},{"id":"the-query-ex0","language":"text","code":"FT.SEARCH agentmem:idx\n  \"(@user:{alice} @namespace:{default} @kind:{semantic})\n     =>[KNN 5 @embedding $vec AS distance]\"\n  PARAMS 2 vec <384-float32-bytes>\n  SORTBY distance\n  RETURN 8 user namespace kind source_thread text created_ts hit_count distance\n  DIALECT 2","section_id":"the-query"},{"id":"the-event-log-ex0","language":"go","code":"events := NewAgentEventLog(client, \"agent:events:\", 1000)\n_, _ = events.Record(ctx, threadID, \"turn_appended:user\",\n    \"Schedule a budget review with finance.\")\n_, _ = events.Record(ctx, threadID, \"memory_written\",\n    \"wrote 7c3f8a1b9e02 as semantic\")\n\nlist, _ := events.Recent(ctx, threadID, 20)\nfor _, e := range list {\n    fmt.Println(e.Action, e.Detail)\n}","section_id":"the-event-log"},{"id":"pre-seeding-long-term-memory-ex0","language":"go","code":"memory := NewLongTermMemory(client, \"agentmem:idx\", \"agent:mem:\",\n    384, 0.20, 0.55, nil)\nembedder, _ := NewLocalEmbedder(ctx, \"\", \"\")\ndefer embedder.Close()\n_ = memory.CreateIndex(ctx)\nwritten, _ := Seed(ctx, memory, embedder, \"default\", \"default\", \"seed\")\nfmt.Printf(\"seeded %d memories\\n\", written)","section_id":"pre-seeding-long-term-memory"},{"id":"run-the-demo-locally-ex0","language":"bash","code":"git clone https://github.com/redis/docs.git\n    cd docs/content/develop/use-cases/memory-layer/go","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex1","language":"bash","code":"go mod tidy","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex2","language":"bash","code":"go run .","section_id":"run-the-demo-locally"}]}
{"schema_version":2,"id":"develop/use-cases/memory-layer","title":"Redis as a memory layer","url":"https://redis.io/docs/latest/develop/use-cases/memory-layer/","summary":"Persistent memory that spans sessions and tasks — working memory per thread, long-term semantic recall, and a time-ordered event log — on a single Redis instance, with sub-millisecond reads on the agent loop's hot path.","aliases":["/develop/use-cases/agent-memory"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:54:57+01:00","children":[{"id":"develop/use-cases/memory-layer/redis-py","summary":"Build a Redis-backed memory layer in Python with redis-py, sentence-transformers, and standard Redis commands — working memory in a Hash, long-term semantic recall as JSON with a vector index, and an event log in a Stream.","title":"Redis memory layer with redis-py","url":"https://redis.io/docs/latest/develop/use-cases/memory-layer/redis-py/"},{"id":"develop/use-cases/memory-layer/nodejs","summary":"Build a Redis-backed memory layer in Node.js with node-redis, @xenova/transformers, and standard Redis commands — working memory in a Hash, long-term semantic recall as JSON with a vector index, and an event log in a Stream.","title":"Redis memory layer with node-redis","url":"https://redis.io/docs/latest/develop/use-cases/memory-layer/nodejs/"},{"id":"develop/use-cases/memory-layer/dotnet","summary":"Build a Redis-backed memory layer in C# with NRedisStack, ONNX Runtime, and standard Redis commands — working memory in a Hash, long-term semantic recall as JSON with a vector index, and an event log in a Stream.","title":"Redis memory layer with NRedisStack","url":"https://redis.io/docs/latest/develop/use-cases/memory-layer/dotnet/"},{"id":"develop/use-cases/memory-layer/rust","summary":"Build a Redis-backed memory layer in Rust with redis-rs, Candle, and standard Redis commands — working memory in a Hash, long-term semantic recall as JSON with a vector index, and an event log in a Stream.","title":"Redis memory layer with redis-rs","url":"https://redis.io/docs/latest/develop/use-cases/memory-layer/rust/"},{"id":"develop/use-cases/memory-layer/go","summary":"Build a Redis-backed memory layer in Go with go-redis, Hugot, and standard Redis commands — working memory in a Hash, long-term semantic recall as JSON with a vector index, and an event log in a Stream.","title":"Redis memory layer with go-redis","url":"https://redis.io/docs/latest/develop/use-cases/memory-layer/go/"},{"id":"develop/use-cases/memory-layer/java-jedis","summary":"Build a Redis-backed memory layer in Java with Jedis, DJL (PyTorch), and standard Redis commands — working memory in a Hash, long-term semantic recall as JSON with a vector index, and an event log in a Stream.","title":"Redis memory layer with Jedis","url":"https://redis.io/docs/latest/develop/use-cases/memory-layer/java-jedis/"},{"id":"develop/use-cases/memory-layer/java-lettuce","summary":"Build a Redis-backed memory layer in Java with Lettuce, DJL (PyTorch), and standard Redis commands — working memory in a Hash, long-term semantic recall as JSON with a vector index, and an event log in a Stream.","title":"Redis memory layer with Lettuce","url":"https://redis.io/docs/latest/develop/use-cases/memory-layer/java-lettuce/"},{"id":"develop/use-cases/memory-layer/php","summary":"Build a Redis-backed memory layer in PHP with Predis, TransformersPHP, and standard Redis commands — working memory in a Hash, long-term semantic recall as JSON with a vector index, and an event log in a Stream.","title":"Redis memory layer with Predis","url":"https://redis.io/docs/latest/develop/use-cases/memory-layer/php/"},{"id":"develop/use-cases/memory-layer/ruby","summary":"Build a Redis-backed memory layer in Ruby with redis-rb, informers, and standard Redis commands — working memory in a Hash, long-term semantic recall as JSON with a vector index, and an event log in a Stream.","title":"Redis memory layer with redis-rb","url":"https://redis.io/docs/latest/develop/use-cases/memory-layer/ruby/"}],"page_type":"content","content_hash":"ab8218fe8f22131a4c2fb32c955ae2791acf05d83b539572569364cbab3c63c6","sections":[{"id":"when-to-use-redis-as-a-memory-layer","title":"When to use Redis as a memory layer","role":"overview","text":"Use Redis as the memory layer when each reasoning step needs to recall both *what just happened in this session* and *what the agent has learned over time* under a strict per-step latency budget — without standing up a separate vector database, message broker, and session store for each tier.\n\nWhen you are ready for production, [Redis Agent Memory](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory) is the fastest way to get a production-ready memory layer running — as a managed service on Redis Cloud, or self-managed on-prem."},{"id":"why-the-problem-is-hard","title":"Why the problem is hard","role":"content","text":"LLMs are stateless. Every API call starts from zero unless the application supplies the relevant context. Without a memory layer, agents re-derive information through extra LLM calls, lose personalization between sessions, and cannot coordinate state in multi-agent deployments. Some of the obvious workarounds have real drawbacks:\n\n-   **A standalone vector database** can index long-term semantic memories, but doesn't cover working session state or an ordered action log, and putting a separate service on the agent's hot path adds latency that compounds across multi-step reasoning loops.\n-   **In-process or app-server session storage** keeps working memory close to the agent, but disappears on process restart and can't be shared across multi-agent or load-balanced deployments — exactly the topology most production agents end up in.\n-   **Stuffing everything into the LLM context window** shifts the cost of memory onto every API call, hits the model's context limit on long-running sessions, and reliably degrades reasoning quality as the context grows.\n\nThe core difficulty is that an agent needs *several kinds* of memory at once — short-lived working state per thread, durable semantic recall by meaning, and an audit trail of recent actions — each with its own retention rule and access pattern. Mapping all three onto a single primitive (only a vector index, only a key-value store, only an append log) forces compromises that show up as either lost context or extra LLM calls. Memory must also stay bounded; without deduplication, summarization, and background consolidation, stale context piles up and degrades downstream accuracy.\n\nThis pattern is distinct from generic [session storage](https://redis.io/docs/latest/develop/use-cases/session-store) (spans a single user session, no semantic recall), from [semantic caching](https://redis.io/docs/latest/develop/use-cases/semantic-cache) (deduplicates LLM calls, not accumulated agent knowledge), and from RAG retrieval against an external document corpus (static reference material, not the agent's own experience)."},{"id":"what-you-can-expect-from-a-redis-solution","title":"What you can expect from a Redis solution","role":"content","text":"You can:\n\n-   Persist and resume agent sessions by thread ID across restarts and across load-balanced workers.\n-   Recall long-term memories by semantic similarity instead of exact key, scoped per user, namespace, or memory kind.\n-   Prevent memory bloat by deduplicating near-identical memories at write time with the same vector index that powers recall.\n-   Run semantic caching, RAG retrieval, and the memory layer together on a single Redis deployment, sharing the same vector index infrastructure.\n-   Keep each step in the agent reasoning loop under budget — Redis reads and writes are sub-millisecond, so the memory layer doesn't dominate per-step latency."},{"id":"how-redis-supports-the-solution","title":"How Redis supports the solution","role":"content","text":"In practice, each tier of the memory layer maps onto a Redis primitive that's already in the cluster. **Working memory** for an active session is a [Hash](https://redis.io/docs/latest/develop/data-types/hashes) at a deterministic key such as `agent:session:{thread_id}`, holding the running scratchpad, current goal, and recent turns — written with [`HSET`](https://redis.io/docs/latest/commands/hset) and read in one round trip with [`HGETALL`](https://redis.io/docs/latest/commands/hgetall). **Long-term memory** — both episodic (\"what happened in past sessions\") and semantic (\"what the agent has learned about this user or domain\") — lives as [JSON](https://redis.io/docs/latest/develop/data-types/json) documents that carry an embedding vector, indexed by [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) on a [HNSW vector field](https://redis.io/docs/latest/develop/ai/search-and-query/vectors) together with tag fields (user, namespace, kind, source thread). The agent recalls memories with one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call that combines vector similarity with metadata filtering, and the same similarity check runs at write time to deduplicate near-identical memories before they enter the store. **A time-ordered event log** of the agent's recent actions and observations is a [Stream](https://redis.io/docs/latest/develop/data-types/streams) appended with [`XADD`](https://redis.io/docs/latest/commands/xadd), replayed with [`XREVRANGE`](https://redis.io/docs/latest/commands/xrevrange), and bounded with [`XTRIM`](https://redis.io/docs/latest/commands/xtrim).\n\nRedis provides the following features that make it a good fit for a memory layer:\n\n-   [Hashes](https://redis.io/docs/latest/develop/data-types/hashes) hold per-session working memory under one key, so loading or persisting a thread's state takes a single round trip.\n-   [JSON](https://redis.io/docs/latest/develop/data-types/json) documents store each long-term memory together with its embedding vector and metadata, so a similarity search returns everything the agent needs without a second lookup.\n-   [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) with [HNSW vector indexes](https://redis.io/docs/latest/develop/ai/search-and-query/vectors) recalls memories by meaning in sub-millisecond time, and the same [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call applies TAG and NUMERIC filters so user, namespace, and kind scoping happen inside the query rather than in application code.\n-   [Streams](https://redis.io/docs/latest/develop/data-types/streams) keep an ordered log of agent actions and observations, [`XTRIM`](https://redis.io/docs/latest/commands/xtrim) bounds retention without manual cleanup, and consumer groups let downstream workers — summarizers, consolidators — replay the log without losing position.\n-   [`EXPIRE`](https://redis.io/docs/latest/commands/expire) automates memory decay per tier — short TTLs on working memory, longer on episodic long-term memories, no TTL on semantic ones — so stale context falls off without a separate cleanup job. (The event log is bounded separately, by [`XADD MAXLEN`](https://redis.io/docs/latest/commands/xadd) on the Stream, not by `EXPIRE`.)\n-   Sub-millisecond reads and writes from memory keep each turn of the agent loop under budget, and a single Redis instance can carry working memory, long-term recall, the event log, semantic caching, and RAG retrieval at zero marginal infrastructure cost."},{"id":"ecosystem","title":"Ecosystem","role":"content","text":"The following libraries, frameworks, and managed services build on Redis for a memory layer:\n\n-   **Python**: [RedisVL](https://redis.io/docs/latest/develop/ai/redisvl) provides vector-index, session-manager, and semantic-memory helpers you can compose into a memory layer.\n-   **Frameworks**: [LangChain](https://redis.io/docs/latest/integrate/langchain-redis) supports Redis as a chat history and memory backend, and [LangGraph & Redis](https://redis.io/blog/langgraph-redis-build-smarter-ai-agents-with-memory-persistence/) ships a Redis checkpointer for persisting graph state across runs.\n-   **AWS**: [Amazon Bedrock](https://redis.io/docs/latest/integrate/amazon-bedrock) agent runtimes integrate with Redis for memory persistence and vector search.\n-   **Any language**: standard Redis client libraries cover the pattern below for custom agent loops.\n-   **Managed**: [Redis Agent Memory Server](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory) is a managed agent memory service with REST and MCP interfaces, working and long-term memory tiers, deduplication, summarization, and background consolidation — useful when you'd rather not build and operate the pattern below yourself."},{"id":"code-examples-to-build-your-own-redis-memory-layer","title":"Code examples to build your own Redis memory layer","role":"example","text":"The following guides show how to build a small Redis-backed memory layer using only standard Redis commands — working memory in a hash per thread, long-term memory as JSON documents with a vector index, an event log in a stream, and per-tier TTLs for decay. Each guide includes a runnable interactive demo where you can send turns, watch working memory update, see semantic recall against past memories, and inspect the event log.\n\n* [redis-py (Python)](https://redis.io/docs/latest/develop/use-cases/memory-layer/redis-py)\n* [node-redis (Node.js)](https://redis.io/docs/latest/develop/use-cases/memory-layer/nodejs)\n* [NRedisStack (C#)](https://redis.io/docs/latest/develop/use-cases/memory-layer/dotnet)\n* [redis-rs (Rust)](https://redis.io/docs/latest/develop/use-cases/memory-layer/rust)\n* [go-redis (Go)](https://redis.io/docs/latest/develop/use-cases/memory-layer/go)\n* [Jedis (Java)](https://redis.io/docs/latest/develop/use-cases/memory-layer/java-jedis)\n* [Lettuce (Java)](https://redis.io/docs/latest/develop/use-cases/memory-layer/java-lettuce)\n* [Predis (PHP)](https://redis.io/docs/latest/develop/use-cases/memory-layer/php)\n* [redis-rb (Ruby)](https://redis.io/docs/latest/develop/use-cases/memory-layer/ruby)"}],"examples":[]}
{"schema_version":2,"id":"develop/use-cases/memory-layer/java-jedis","title":"Redis memory layer with Jedis","url":"https://redis.io/docs/latest/develop/use-cases/memory-layer/java-jedis/","summary":"Build a Redis-backed memory layer in Java with Jedis, DJL (PyTorch), and standard Redis commands — working memory in a Hash, long-term semantic recall as JSON with a vector index, and an event log in a Stream.","aliases":["/develop/use-cases/agent-memory/java-jedis","/develop/use-cases/agent-memory/java","/develop/use-cases/agent-memory/jedis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:54:57+01:00","children":[],"page_type":"content","content_hash":"6f4069b61d37a0e2e64ead1708bb1c43dcae3d91c61929c5ce1fb0af16f32558","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a small Redis-backed memory layer in Java with [Jedis](https://redis.io/docs/latest/develop/clients/jedis) and [DJL](https://djl.ai/) (the Deep Java Library), using only standard Redis commands — no Redis Agent Memory SDK, no managed service. It includes a local web server built with the JDK's [`com.sun.net.httpserver`](https://docs.oracle.com/en/java/javase/17/docs/api/jdk.httpserver/com/sun/net/httpserver/package-summary.html) so you can send turns at the agent, watch working memory update in place, see semantically similar long-term memories recalled in real time, watch the write-time deduplication skip near-duplicates, and inspect the per-thread event log.\n\nThe embedder is [DJL](https://djl.ai/) (`ai.djl.huggingface.tokenizers` + `ai.djl.pytorch.pytorch-model-zoo`) running the canonical `sentence-transformers/all-MiniLM-L6-v2` PyTorch checkpoint — the same library and model the existing [Jedis vector-search example](https://redis.io/docs/latest/develop/clients/jedis/vecsearch) uses, and the same encoder the [Python](https://redis.io/docs/latest/develop/use-cases/memory-layer/redis-py) example loads. DJL drives libtorch through the same C++ runtime as Python's PyTorch bindings, so the vectors produced here are numerically identical to the Python ones to within rounding noise, and the distance bands the Python walkthrough quotes carry over to this demo without recalibration. A memory written by one demo can be recalled by the other against the same Redis instance."},{"id":"overview","title":"Overview","role":"overview","text":"The memory layer splits across three Redis primitives, each handling one tier:\n\n* **Working memory** for the active session is a [Hash](https://redis.io/docs/latest/develop/data-types/hashes) at `agent:session:<threadId>` holding the goal, scratchpad, a rolling window of recent turns (as a JSON list inside one field), and a few audit timestamps. One [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) returns the whole session in a single round trip; every write refreshes the key's [`EXPIRE`](https://redis.io/docs/latest/commands/expire) so idle sessions decay on their own.\n* **Long-term memory** is a set of [JSON](https://redis.io/docs/latest/develop/data-types/json) documents at `agent:mem:<id>`, each carrying the memory text, a 384-dimensional embedding vector, and tag fields for user, namespace, kind (episodic / semantic), and source thread. A single [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) index covers the [HNSW vector field](https://redis.io/docs/latest/develop/ai/search-and-query/vectors) and every metadata field, so one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call performs the KNN with the metadata pre-filter in the same round trip. Write-time deduplication runs the same KNN at insert time and skips a new memory whose nearest existing entry is within a tighter threshold.\n* **Event log** for the agent's actions and observations is a [Stream](https://redis.io/docs/latest/develop/data-types/streams) at `agent:events:<threadId>`, appended with [`XADD MAXLEN ~`](https://redis.io/docs/latest/commands/xadd) so retention stays bounded automatically, replayed with [`XREVRANGE`](https://redis.io/docs/latest/commands/xrevrange).\n\nThat gives you:\n\n* One Redis Search call per recall: [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) does the KNN + TAG pre-filter in a single round trip (a per-row [`TTL`](https://redis.io/docs/latest/commands/ttl) follow-up is the only other read the helper issues, just to populate the `ttl_seconds` field for the admin panel). Working memory is one [`HGETALL`](https://redis.io/docs/latest/commands/hgetall); the event log is one [`XADD`](https://redis.io/docs/latest/commands/xadd).\n* Sub-millisecond reads on every step of the agent loop, so the memory layer doesn't dominate per-step latency.\n* Per-tier decay: short TTLs on working memory, longer on episodic memories, no TTL on semantic memories. Combined with a database-level [eviction policy](https://redis.io/docs/latest/develop/reference/eviction) (LFU is the common choice), memory stays bounded under pressure.\n* Scoping enforced inside the query: a recall query for `user=alice` will never see `user=bob`'s memories, because the TAG filter goes into the same [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call as the KNN."},{"id":"how-it-works","title":"How it works","role":"content","text":"Each turn through the agent loop touches all three tiers in one pass: append to working memory, recall similar long-term memories, write the turn back as a new memory (with deduplication), and append one event to the log."},{"id":"per-turn-flow","title":"Per-turn flow","role":"content","text":"1. The application calls `embedder.encodeOne(text)` to turn the incoming turn into a 384-element `float[]`.\n2. `session.appendTurn(threadId, role, content, user, agent, null)` reads the per-thread Hash with [`HGETALL`](https://redis.io/docs/latest/commands/hgetall), appends the new turn to the rolling window in application code, trims it back to the configured maximum, and writes the Hash back with [`HSET`](https://redis.io/docs/latest/commands/hset) + [`EXPIRE`](https://redis.io/docs/latest/commands/expire) inside Jedis's `multi()` transaction. The session TTL refreshes on every write so an active thread stays alive.\n3. `memory.recall(vec, user, namespace, null, 5, threshold)` runs [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) with a TAG pre-filter and a `KNN 5` clause. Redis returns the closest matching memories together with their cosine distances; memories beyond the recall threshold are dropped before they reach the agent so an unrelated query doesn't surface confident-looking false positives.\n4. `memory.remember(text, vec, user, namespace, kind, threadId, null)` runs the same KNN with a tighter dedup threshold. If an existing memory is within the threshold, the new write is skipped and the existing memory's `hit_count` is incremented with [`JSON.NUMINCRBY`](https://redis.io/docs/latest/commands/json.numincrby) — best-effort: if the memory's TTL has elapsed between the recall and the bump, the increment quietly fails and the hit count for that recall is lost. Otherwise a fresh JSON document is written with [`JSON.SET`](https://redis.io/docs/latest/commands/json.set) and a per-kind [`EXPIRE`](https://redis.io/docs/latest/commands/expire) inside the same `multi()` transaction.\n5. `events.record(threadId, action, detail)` appends one entry to the per-thread Stream with [`XADD MAXLEN ~`](https://redis.io/docs/latest/commands/xadd) (`XAddParams.approximateTrimming()`), bounding retention to roughly a thousand entries per thread without an explicit cleanup job.\n\nThe embedding is computed once and reused for steps 3 and 4 — there's no point encoding the same text twice. Recall runs before the write, so the agent doesn't see its own just-written turn echoed back as a recalled memory."},{"id":"the-session-store","title":"The session store","role":"content","text":"`AgentSession` wraps the working-memory Hash and the rolling turn window ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/memory-layer/java-jedis/src/main/java/com/redis/agentmem/AgentSession.java)):\n\n[code example]\n\nThe data model is one Hash per thread. The rolling turn window is stored as a JSON string in a single field so the whole session loads in one [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) — the hash never grows in size or field count as the conversation goes on.\n\n[code example]\n\nEvery write — `start`, `appendTurn`, `setGoal` — runs the [`HSET`](https://redis.io/docs/latest/commands/hset) and [`EXPIRE`](https://redis.io/docs/latest/commands/expire) inside `jedis.multi()` so a connection drop between the two writes can't leave the session without a TTL."},{"id":"the-long-term-memory-store","title":"The long-term memory store","role":"content","text":"`LongTermMemory` owns the JSON documents, the vector index, the recall query, and the write-time deduplication ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/memory-layer/java-jedis/src/main/java/com/redis/agentmem/LongTermMemory.java)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each memory is a JSON document at `agent:mem:<id>`. The embedding is stored as a JSON array of floats so the document is human-readable from `redis-cli`; [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) still expects the *query* vector as raw `float32` bytes (`LocalEmbedder.toBytes()` packs them in little-endian order), regardless of how the indexed document stores it.\n\n[code example]\n\nThe Redis Search index is declared on the JSON document type with alias names on each path so the query syntax stays compact. Jedis spells the alias with `.as(\"...\")`:\n\n[code example]"},{"id":"the-query","title":"The query","role":"content","text":"Both recall and dedup share the same hybrid query: a TAG pre-filter in parentheses followed by `=>[KNN k @embedding $vec]`. With `DIALECT 2`, Redis applies the filter first and KNN-ranks only the matching documents.\n\n[code example]\n\n`distance` is the cosine *distance* (0 means identical, 2 means opposite). Recall and dedup share the same query shape; only the threshold differs — strict at write time so the index doesn't fill with paraphrases of the same fact, looser at read time so the agent gets a wider net of relevant memories."},{"id":"per-kind-ttls","title":"Per-kind TTLs","role":"content","text":"`remember` resolves the entry's TTL from the memory's `kind`:\n\n| Kind      | Default TTL | When to use it                                              |\n|-----------|-------------|-------------------------------------------------------------|\n| `episodic` | 7 days     | Snapshots from a specific session that should decay.        |\n| `semantic` | none       | Distilled facts and preferences the agent carries forward.  |\n\nYou can override per write by passing a non-null `ttlSeconds` to `remember`, or hand a different `Map<String, Long>` to the `LongTermMemory` constructor — for example, to give semantic memories a six-month TTL while leaving episodic memories at seven days."},{"id":"the-event-log","title":"The event log","role":"content","text":"`AgentEventLog` is a thin wrapper over a per-thread Redis Stream ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/memory-layer/java-jedis/src/main/java/com/redis/agentmem/AgentEventLog.java)):\n\n[code example]\n\n`record` calls [`XADD`](https://redis.io/docs/latest/commands/xadd) with `MAXLEN ~ 1000` via `XAddParams.xAddParams().maxLen(1000).approximateTrimming()`. The tilde lets Redis trim in whole-node units instead of exactly-N units, which is much cheaper at the cost of overshooting the bound by up to a node's worth — the right tradeoff for an audit log where exact length doesn't matter.\n\nThe Stream is independent of the session Hash and the long-term JSON documents: it answers \"what just happened\" without competing with either of those for indexing or memory budget. Consumer groups (not used in this demo) would let downstream workers — summarisers, consolidators, audit pipelines — replay the log without losing position."},{"id":"concurrency-caveats","title":"Concurrency caveats","role":"content","text":"The three helpers above trade correctness under heavy concurrency for clarity. Each is fine on a single-process demo, but lifting the code into a real multi-worker agent surfaces three races worth knowing about:\n\n* **Working memory is read-modify-write.** `AgentSession.appendTurn` calls [`HGETALL`](https://redis.io/docs/latest/commands/hgetall), mutates the `recentTurns` list in application code, and writes the Hash back with [`HSET`](https://redis.io/docs/latest/commands/hset). Two concurrent turns on the same thread can both read the same `recentTurns`, append different entries, and write back — last writer wins, the other turn is silently lost. The robust fix is either a [`WATCH`](https://redis.io/docs/latest/commands/watch) / [`MULTI`](https://redis.io/docs/latest/commands/multi) / [`EXEC`](https://redis.io/docs/latest/commands/exec) loop around the read-modify-write or a small [Lua script](https://redis.io/docs/latest/commands/eval) that does the append atomically server-side.\n\n* **Long-term dedup is not atomic.** `LongTermMemory.remember` runs a [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) KNN lookup, decides whether the candidate is a duplicate, and (if not) calls [`JSON.SET`](https://redis.io/docs/latest/commands/json.set). Two workers seeing the same fact in flight can each fail to see the other's not-yet-committed write and both insert a new memory. The pragmatic fix is to accept that the index will occasionally hold near-duplicates and run a background consolidator that periodically scans for memory pairs within a tight distance and merges them, rather than trying to make the write itself atomic.\n\n* **The active thread is server state.** The demo server keeps a single `currentThreadId` synchronized through an explicit mutex; `seedAll`, `newThread`, and `handleTurn` each release the lock between operations, so a turn racing with a thread rotation can capture the old id and apply to the previous thread. This is cosmetic for a one-user browser demo. A multi-user agent would carry the thread id on the request itself rather than as shared server state.\n\nA separate concern specific to Java + DJL: the `LocalEmbedder.encodeOne` / `encodeMany` methods are `synchronized` because the underlying `Predictor` is not thread-safe. The demo's `Executors.newCachedThreadPool` could otherwise call into one predictor from several handler threads at once and corrupt the inference state. A higher-throughput deployment would replace that lock with a small pool of `Predictor` instances or a dedicated single-threaded inference executor.\n\nThose caveats are deliberate. A more conservative implementation would obscure the Redis-shaped parts of the pattern; the demo prioritizes a small, readable code path that maps directly onto the commands in the prose above."},{"id":"pre-seeding-long-term-memory","title":"Pre-seeding long-term memory","role":"content","text":"In a real deployment the memory store fills up organically as the agent reasons over user turns: each turn produces zero or more memories that flow into the store, with deduplication catching repeats. For the demo, `SeedMemory` pre-loads a small set of mixed semantic and episodic memories so the very first recall query returns something useful ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/memory-layer/java-jedis/src/main/java/com/redis/agentmem/SeedMemory.java)):\n\n[code example]\n\nThe seed list mixes long-lived facts and preferences (`semantic`) with snapshots of past sessions (`episodic`), so the **Kind to write** control in the demo has something to switch between when a new turn is being remembered."},{"id":"the-interactive-demo","title":"The interactive demo","role":"content","text":"`DemoServer` runs the JDK's [`HttpServer`](https://docs.oracle.com/en/java/javase/17/docs/api/jdk.httpserver/com/sun/net/httpserver/HttpServer.html) on port 8092, with a cached thread pool dispatching requests to handlers. The HTML page exposes three live panels — working memory, recalled memories, event log — plus a memories table for admin actions. Endpoints:\n\n| Endpoint            | What it does                                                                    |\n|---------------------|---------------------------------------------------------------------------------|\n| `GET  /state`       | Index info, current session, in-scope long-term memories, and recent events.    |\n| `POST /turn`        | Embed the text, append to working memory, recall similar memories, optionally write a new memory (with dedup), append an event. |\n| `POST /new_thread`  | Start a fresh thread; long-term memory and other threads are untouched.         |\n| `POST /reset`       | Drop every long-term memory and re-seed the sample set.                         |\n| `POST /drop_memory` | Delete a single long-term memory by id.                                         |\n\nThe server holds one `LocalEmbedder`, one `AgentSession`, one `LongTermMemory`, and one `AgentEventLog` for the lifetime of the process. The \"current thread\" is a mutex-protected `String` field that the **New thread** button rotates — every browser tab inherits the same thread until you explicitly start a new one."},{"id":"run-the-demo-locally","title":"Run the demo locally","role":"content","text":"1.  Clone the [`redis/docs`](https://github.com/redis/docs) repository and change into the example\n    directory:\n\n    [code example]\n\n2.  Build the fat jar. You'll need a [JDK 17](https://adoptium.net/) or later and\n    [Maven](https://maven.apache.org/):\n\n    [code example]\n\n    The first build pulls Jedis, DJL, and the PyTorch native libraries — that takes\n    a couple of minutes the first time and is cached afterwards.\n\n3.  Make sure a Redis instance with Redis Search and Redis JSON is running locally on\n    port 6379. [Redis Stack](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack)\n    ships both, or [Redis 8](https://redis.io/docs/latest/develop/ai/search-and-query) with the\n    Search and JSON modules enabled.\n\n4.  Start the demo. The first run downloads the `sentence-transformers/all-MiniLM-L6-v2`\n    PyTorch weights into the local DJL cache (~90 MB):\n\n    [code example]\n\n    Or via Maven: `mvn -q exec:java`.\n\n5.  Open <http://localhost:8092> and try some turns:\n\n    *  **\"Remind me which theme I prefer in editors.\"** — paraphrase of a seeded\n       semantic memory (\"The user dislikes dark mode and prefers a high-contrast\n       light theme...\"). You should see that memory recalled with a cosine\n       distance around 0.47, comfortably under the 0.55 default recall\n       threshold.\n    *  **\"What did we discuss about the order-routing outage?\"** — paraphrase of\n       a seeded episodic memory; the postmortem memory should recall around\n       0.44. Switch the **Kind to write** dropdown to `skip` so the question\n       itself doesn't enter long-term memory.\n    *  **\"I prefer concise answers without filler phrases.\"** — paraphrase of\n       a seeded *semantic* memory. Switch the **Kind to write** dropdown to\n       `semantic` so the dedup KNN runs in the same kind as the seed (dedup\n       is scoped per kind, on purpose, so an episodic write can't collapse\n       onto a semantic memory). You should then see the write **deduped**\n       onto the existing memory at a cosine distance around 0.15, with\n       `hit_count` ticking up in the memories table.\n    *  **\"My favorite color is teal.\"** — unrelated to any seed; nothing\n       recalls above the threshold (every seed lands above 0.8), and the new\n       memory is written as `episodic` (or `semantic`, depending on the\n       dropdown) under a fresh id.\n    *  Switch the **User** field to `bob` and re-ask any of the above — recall\n       returns nothing because the seed memories live under `default`. That's\n       the TAG pre-filter at work inside [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search).\n    *  Slide the **Recall threshold** down to 0.30 to see borderline paraphrases\n       drop out of the recall set, then back up to 0.70 to watch them return.\n\n    DJL drives libtorch through the same C++ kernel as Python's PyTorch\n    bindings, so distances here match the Python demo to four decimal\n    places. `sentence-transformers/all-MiniLM-L6-v2` puts a faithful\n    paraphrase in the 0.15 – 0.50 cosine-distance range, a loose paraphrase\n    or related topic in the 0.50 – 0.80 range, and unrelated queries above\n    0.8 — which is what motivates the 0.55 default recall threshold and the\n    0.20 default dedup threshold. A stricter embedding model (or a\n    domain-tuned one) would let you tighten both; a noisier one would push\n    them up. The right thresholds are always a function of the model, the\n    corpus, and how conservative the agent needs to be about accepting a\n    memory as a match.\n\nThe server is read/write against your local Redis. The default memory index is `agentmem:idx`, JSON keys live under `agent:mem:`, session Hashes under `agent:session:`, and event Streams under `agent:events:`. Useful flags (pass them after the jar):\n\n* `--host` / `--port` — change the HTTP bind address (default `127.0.0.1:8092`).\n* `--redis-host` / `--redis-port` — point at a non-local Redis (default `localhost:6379`).\n* `--mem-index-name` / `--mem-key-prefix` / `--session-key-prefix` / `--event-key-prefix` — relocate the index name and the three key prefixes (to run several demos against one Redis without colliding, for example).\n* `--no-reset` — keep the existing long-term memories across restarts instead of dropping and re-seeding.\n* `--session-ttl-seconds` — change the working-memory TTL (default 3600).\n* `--dedup-threshold` — change the cosine-distance cutoff for write-time deduplication.\n* `--recall-threshold` — change the default cosine-distance cutoff for recall."}],"examples":[{"id":"the-session-store-ex0","language":"java","code":"import com.redis.agentmem.AgentSession;\nimport com.redis.agentmem.SessionState;\nimport redis.clients.jedis.HostAndPort;\nimport redis.clients.jedis.JedisPooled;\n\nJedisPooled jedis = new JedisPooled(new HostAndPort(\"localhost\", 6379));\nAgentSession session = new AgentSession(jedis, \"agent:session:\", 3600, 20);\n\nString threadId = session.newThreadId();\nSessionState state = session.start(threadId, \"alice\", \"demo-agent\",\n        \"Plan next week's meetings.\", null);\nstate = session.appendTurn(\n        threadId, \"user\", \"Schedule a budget review with finance.\",\n        \"alice\", \"demo-agent\", null);\nSystem.out.println(state.turnCount() + \" \" + state.recentTurns().size()\n        + \" \" + state.ttlSeconds());","section_id":"the-session-store"},{"id":"the-session-store-ex1","language":"text","code":"agent:session:9f3d2a4b8c61\n  thread_id=9f3d2a4b8c61\n  user=alice\n  agent=demo-agent\n  goal=Plan next week's meetings.\n  scratchpad=Need to confirm finance's availability.\n  turn_count=4\n  created_ts=1715990400.12\n  last_active_ts=1715990650.83\n  recent_turns=[{\"role\":\"user\",\"content\":\"...\",\"ts\":...}, ...]","section_id":"the-session-store"},{"id":"the-long-term-memory-store-ex0","language":"java","code":"import com.redis.agentmem.LocalEmbedder;\nimport com.redis.agentmem.LongTermMemory;\nimport com.redis.agentmem.MemoryRecord;\nimport com.redis.agentmem.WriteResult;\n\nLongTermMemory memory = new LongTermMemory(\n        jedis,\n        \"agentmem:idx\",\n        \"agent:mem:\",\n        384,\n        0.20,   // dedup threshold — tight at write time\n        0.55,   // recall threshold — looser at read time\n        null);  // default per-kind TTL map\nLocalEmbedder embedder = LocalEmbedder.create();\nmemory.createIndex();  // idempotent\n\n// Write a memory. The same KNN that powers recall also runs here at\n// a tighter threshold so paraphrases of the same fact collapse.\nfloat[] vec = embedder.encodeOne(\"The user prefers light mode in editors.\");\nWriteResult result = memory.remember(\n        \"The user prefers light mode in editors.\",\n        vec,\n        \"alice\",\n        \"default\",\n        \"semantic\",\n        \"9f3d2a4b8c61\",\n        null);\nSystem.out.printf(\"deduped=%s id=%s dist=%s%n\",\n        result.deduped(), result.id(), result.existingDistance());\n\n// Recall against a later question.\nfloat[] q = embedder.encodeOne(\"Which theme does this user like?\");\nfor (MemoryRecord h : memory.recall(q, \"alice\", \"default\", null, 5, null)) {\n    System.out.printf(\"%.3f [%s] %s%n\", h.distance(), h.kind(), h.text());\n}","section_id":"the-long-term-memory-store"},{"id":"data-model-ex0","language":"json","code":"agent:mem:7c3f8a1b9e02\n{\n  \"id\": \"7c3f8a1b9e02\",\n  \"user\": \"alice\",\n  \"namespace\": \"default\",\n  \"kind\": \"semantic\",\n  \"source_thread\": \"9f3d2a4b8c61\",\n  \"text\": \"The user prefers light mode in editors.\",\n  \"embedding\": [0.013, -0.041, ...],\n  \"created_ts\": 1715990400.12,\n  \"hit_count\": 0\n}","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"FT.CREATE agentmem:idx\n  ON JSON PREFIX 1 agent:mem:\n  SCHEMA\n    $.text          AS text          TEXT\n    $.user          AS user          TAG\n    $.namespace     AS namespace     TAG\n    $.kind          AS kind          TAG\n    $.source_thread AS source_thread TAG\n    $.created_ts    AS created_ts    NUMERIC SORTABLE\n    $.hit_count     AS hit_count     NUMERIC SORTABLE\n    $.embedding     AS embedding     VECTOR HNSW 6\n                                       TYPE FLOAT32 DIM 384\n                                       DISTANCE_METRIC COSINE","section_id":"data-model"},{"id":"the-query-ex0","language":"text","code":"FT.SEARCH agentmem:idx\n  \"(@user:{alice} @namespace:{default} @kind:{semantic})\n     =>[KNN 5 @embedding $vec AS distance]\"\n  PARAMS 2 vec <384-float32-bytes>\n  SORTBY distance\n  RETURN 8 user namespace kind source_thread text created_ts hit_count distance\n  DIALECT 2","section_id":"the-query"},{"id":"the-event-log-ex0","language":"java","code":"import com.redis.agentmem.AgentEvent;\nimport com.redis.agentmem.AgentEventLog;\n\nAgentEventLog events = new AgentEventLog(jedis, \"agent:events:\", 1000);\nevents.record(threadId, \"turn_appended:user\",\n        \"Schedule a budget review with finance.\");\nevents.record(threadId, \"memory_written\",\n        \"wrote 7c3f8a1b9e02 as semantic\");\n\nfor (AgentEvent e : events.recent(threadId, 20)) {\n    System.out.println(e.action() + \" \" + e.detail());\n}","section_id":"the-event-log"},{"id":"pre-seeding-long-term-memory-ex0","language":"java","code":"import com.redis.agentmem.SeedMemory;\n\nLongTermMemory memory = new LongTermMemory(jedis, \"agentmem:idx\",\n        \"agent:mem:\", 384, 0.20, 0.55, null);\nLocalEmbedder embedder = LocalEmbedder.create();\nmemory.createIndex();\nint written = SeedMemory.seed(memory, embedder, \"default\", \"default\", \"seed\");\nSystem.out.println(\"seeded \" + written + \" memories\");","section_id":"pre-seeding-long-term-memory"},{"id":"run-the-demo-locally-ex0","language":"bash","code":"git clone https://github.com/redis/docs.git\n    cd docs/content/develop/use-cases/memory-layer/java-jedis","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex1","language":"bash","code":"mvn -q package","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex2","language":"bash","code":"java -jar target/agent-memory-jedis.jar","section_id":"run-the-demo-locally"}]}
{"schema_version":2,"id":"develop/use-cases/memory-layer/java-lettuce","title":"Redis memory layer with Lettuce","url":"https://redis.io/docs/latest/develop/use-cases/memory-layer/java-lettuce/","summary":"Build a Redis-backed memory layer in Java with Lettuce, DJL (PyTorch), and standard Redis commands — working memory in a Hash, long-term semantic recall as JSON with a vector index, and an event log in a Stream.","aliases":["/develop/use-cases/agent-memory/java-lettuce","/develop/use-cases/agent-memory/lettuce"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:54:57+01:00","children":[],"page_type":"content","content_hash":"2896735f6c1efc4e5595aa2d89ba3dc8733c592d1e7b50ecf0dde16c280d56ba","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a small Redis-backed memory layer in Java with [Lettuce](https://redis.io/docs/latest/develop/clients/lettuce) and [DJL](https://djl.ai/) (the Deep Java Library), using only standard Redis commands — no Redis Agent Memory SDK, no managed service. It includes a local web server built with the JDK's [`com.sun.net.httpserver`](https://docs.oracle.com/en/java/javase/17/docs/api/jdk.httpserver/com/sun/net/httpserver/package-summary.html) so you can send turns at the agent, watch working memory update in place, see semantically similar long-term memories recalled in real time, watch the write-time deduplication skip near-duplicates, and inspect the per-thread event log.\n\nThe embedder is [DJL](https://djl.ai/) (`ai.djl.huggingface.tokenizers` + `ai.djl.pytorch.pytorch-model-zoo`) running the canonical `sentence-transformers/all-MiniLM-L6-v2` PyTorch checkpoint — the same library and model the existing [Lettuce vector-search example](https://redis.io/docs/latest/develop/clients/lettuce/vecsearch) uses, and the same encoder the [Python](https://redis.io/docs/latest/develop/use-cases/memory-layer/redis-py) example loads. DJL drives libtorch through the same C++ runtime as Python's PyTorch bindings, so the vectors produced here are numerically identical to the Python ones to within rounding noise, and the distance bands the Python walkthrough quotes carry over to this demo without recalibration. A memory written by one demo can be recalled by the other against the same Redis instance.\n\nThe big shape difference from the [sister Jedis port](https://redis.io/docs/latest/develop/use-cases/memory-layer/java-jedis) is that Lettuce 6.7 doesn't ship first-class `FT.*` or `JSON.*` bindings yet. The helper classes send those commands through Lettuce's generic `dispatch()` API with a custom `ProtocolKeyword`, and the FT.SEARCH/FT.INFO replies are parsed by walking the resulting `List<Object>` — see [`LongTermMemory.java`](https://github.com/redis/docs/blob/main/content/develop/use-cases/memory-layer/java-lettuce/src/main/java/com/redis/agentmem/LongTermMemory.java) for the parser. The same connection is pinned to RESP2 at startup so the reply shape stays a flat array (Lettuce 6.7 negotiates RESP3 by default, which wraps the data in a map structure the parser would have to special-case)."},{"id":"overview","title":"Overview","role":"overview","text":"The memory layer splits across three Redis primitives, each handling one tier:\n\n* **Working memory** for the active session is a [Hash](https://redis.io/docs/latest/develop/data-types/hashes) at `agent:session:<threadId>` holding the goal, scratchpad, a rolling window of recent turns (as a JSON list inside one field), and a few audit timestamps. One [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) returns the whole session in a single round trip; every write refreshes the key's [`EXPIRE`](https://redis.io/docs/latest/commands/expire) so idle sessions decay on their own.\n* **Long-term memory** is a set of [JSON](https://redis.io/docs/latest/develop/data-types/json) documents at `agent:mem:<id>`, each carrying the memory text, a 384-dimensional embedding vector, and tag fields for user, namespace, kind (episodic / semantic), and source thread. A single [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) index covers the [HNSW vector field](https://redis.io/docs/latest/develop/ai/search-and-query/vectors) and every metadata field, so one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call performs the KNN with the metadata pre-filter in the same round trip. Write-time deduplication runs the same KNN at insert time and skips a new memory whose nearest existing entry is within a tighter threshold.\n* **Event log** for the agent's actions and observations is a [Stream](https://redis.io/docs/latest/develop/data-types/streams) at `agent:events:<threadId>`, appended with [`XADD MAXLEN ~`](https://redis.io/docs/latest/commands/xadd) so retention stays bounded automatically, replayed with [`XREVRANGE`](https://redis.io/docs/latest/commands/xrevrange).\n\nThat gives you:\n\n* One Redis Search call per recall: [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) does the KNN + TAG pre-filter in a single round trip (a per-row [`TTL`](https://redis.io/docs/latest/commands/ttl) follow-up is the only other read the helper issues, just to populate the `ttl_seconds` field for the admin panel). Working memory is one [`HGETALL`](https://redis.io/docs/latest/commands/hgetall); the event log is one [`XADD`](https://redis.io/docs/latest/commands/xadd).\n* Sub-millisecond reads on every step of the agent loop, so the memory layer doesn't dominate per-step latency.\n* Per-tier decay: short TTLs on working memory, longer on episodic memories, no TTL on semantic memories. Combined with a database-level [eviction policy](https://redis.io/docs/latest/develop/reference/eviction) (LFU is the common choice), memory stays bounded under pressure.\n* Scoping enforced inside the query: a recall query for `user=alice` will never see `user=bob`'s memories, because the TAG filter goes into the same [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call as the KNN."},{"id":"how-it-works","title":"How it works","role":"content","text":"Each turn through the agent loop touches all three tiers in one pass: append to working memory, recall similar long-term memories, write the turn back as a new memory (with deduplication), and append one event to the log."},{"id":"per-turn-flow","title":"Per-turn flow","role":"content","text":"1. The application calls `embedder.encodeOne(text)` to turn the incoming turn into a 384-element `float[]`.\n2. `session.appendTurn(threadId, role, content, user, agent, null)` reads the per-thread Hash with [`HGETALL`](https://redis.io/docs/latest/commands/hgetall), appends the new turn to the rolling window in application code, trims it back to the configured maximum, and writes the Hash back with [`HSET`](https://redis.io/docs/latest/commands/hset) + [`EXPIRE`](https://redis.io/docs/latest/commands/expire) inside Lettuce's `multi()` / `exec()` transaction. The session TTL refreshes on every write so an active thread stays alive.\n3. `memory.recall(vec, user, namespace, null, 5, threshold)` issues `FT.SEARCH` via `dispatch()` with a TAG pre-filter and a `KNN 5` clause. Redis returns the closest matching memories together with their cosine distances; memories beyond the recall threshold are dropped before they reach the agent so an unrelated query doesn't surface confident-looking false positives.\n4. `memory.remember(text, vec, user, namespace, kind, threadId, null)` runs the same KNN with a tighter dedup threshold. If an existing memory is within the threshold, the new write is skipped and the existing memory's `hit_count` is incremented with `JSON.NUMINCRBY` (also via `dispatch()`) — best-effort: if the memory's TTL has elapsed between the recall and the bump, the increment quietly fails and the hit count for that recall is lost. Otherwise a fresh JSON document is written with `JSON.SET` and a per-kind [`EXPIRE`](https://redis.io/docs/latest/commands/expire) inside the same `multi()` transaction.\n5. `events.record(threadId, action, detail)` appends one entry to the per-thread Stream with [`XADD MAXLEN ~`](https://redis.io/docs/latest/commands/xadd) (`XAddArgs.Builder.maxlen(N).approximateTrimming()`), bounding retention to roughly a thousand entries per thread without an explicit cleanup job.\n\nThe embedding is computed once and reused for steps 3 and 4 — there's no point encoding the same text twice. Recall runs before the write, so the agent doesn't see its own just-written turn echoed back as a recalled memory."},{"id":"the-session-store","title":"The session store","role":"content","text":"`AgentSession` wraps the working-memory Hash and the rolling turn window ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/memory-layer/java-lettuce/src/main/java/com/redis/agentmem/AgentSession.java)):\n\n[code example]\n\nThe data model is one Hash per thread. The rolling turn window is stored as a JSON string in a single field so the whole session loads in one [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) — the hash never grows in size or field count as the conversation goes on.\n\n[code example]\n\nEvery write — `start`, `appendTurn`, `setGoal` — runs the [`HSET`](https://redis.io/docs/latest/commands/hset) and [`EXPIRE`](https://redis.io/docs/latest/commands/expire) inside `multi()` / `exec()` so a connection drop between the two writes can't leave the session without a TTL. The shared `txLock` serializes this whole MULTI…EXEC span against any other transaction on the connection — Lettuce's transaction state is connection-scoped, so two concurrent handler threads queueing into the same MULTI would interleave their writes."},{"id":"the-long-term-memory-store","title":"The long-term memory store","role":"content","text":"`LongTermMemory` owns the JSON documents, the vector index, the recall query, and the write-time deduplication ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/memory-layer/java-lettuce/src/main/java/com/redis/agentmem/LongTermMemory.java)):\n\n[code example]"},{"id":"sending-ft--json-commands-through-lettuce","title":"Sending FT.* / JSON.* commands through Lettuce","role":"content","text":"Lettuce 6.7 doesn't ship first-class `FT.*` or `JSON.*` bindings, so the helper sends them through `dispatch()` with a custom `ProtocolKeyword`. Each command is its own keyword whose `getBytes()` returns the literal command name; the rest is built up with `CommandArgs<byte[], byte[]>`:\n\n[code example]\n\nThe `NestedMultiOutput` collects the RESP reply into a `List<Object>` — a flat `[count, key1, fields1, key2, fields2, ...]` array under RESP2, where each `fields` is itself a nested list of alternating field/value pairs. The helper walks that with `parseAllHits()` and `fieldsToMap()` (both private to `LongTermMemory`).\n\nThe connection is pinned to RESP2 at startup with `ClientOptions.builder().protocolVersion(ProtocolVersion.RESP2)`. Lettuce 6.7 otherwise negotiates RESP3 against Redis 7+, which wraps `FT.SEARCH` and `FT.INFO` results in map / set shapes that the flat-array parser would have to special-case across every reply path."},{"id":"data-model","title":"Data model","role":"content","text":"Each memory is a JSON document at `agent:mem:<id>`. The embedding is stored as a JSON array of floats so the document is human-readable from `redis-cli`; [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) still expects the *query* vector as raw `float32` bytes (`LocalEmbedder.toBytes()` packs them in little-endian order), regardless of how the indexed document stores it.\n\n[code example]\n\nThe Redis Search index is declared on the JSON document type with alias names on each path so the query syntax stays compact:\n\n[code example]"},{"id":"the-query","title":"The query","role":"content","text":"Both recall and dedup share the same hybrid query: a TAG pre-filter in parentheses followed by `=>[KNN k @embedding $vec]`. With `DIALECT 2`, Redis applies the filter first and KNN-ranks only the matching documents.\n\n[code example]\n\n`distance` is the cosine *distance* (0 means identical, 2 means opposite). Recall and dedup share the same query shape; only the threshold differs — strict at write time so the index doesn't fill with paraphrases of the same fact, looser at read time so the agent gets a wider net of relevant memories."},{"id":"per-kind-ttls","title":"Per-kind TTLs","role":"content","text":"`remember` resolves the entry's TTL from the memory's `kind`:\n\n| Kind      | Default TTL | When to use it                                              |\n|-----------|-------------|-------------------------------------------------------------|\n| `episodic` | 7 days     | Snapshots from a specific session that should decay.        |\n| `semantic` | none       | Distilled facts and preferences the agent carries forward.  |\n\nYou can override per write by passing a non-null `ttlSeconds` to `remember`, or hand a different `Map<String, Long>` to the `LongTermMemory` constructor — for example, to give semantic memories a six-month TTL while leaving episodic memories at seven days."},{"id":"the-event-log","title":"The event log","role":"content","text":"`AgentEventLog` is a thin wrapper over a per-thread Redis Stream ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/memory-layer/java-lettuce/src/main/java/com/redis/agentmem/AgentEventLog.java)):\n\n[code example]\n\n`record` calls [`XADD`](https://redis.io/docs/latest/commands/xadd) with `MAXLEN ~ 1000` via `XAddArgs.Builder.maxlen(1000).approximateTrimming()`. The tilde lets Redis trim in whole-node units instead of exactly-N units, which is much cheaper at the cost of overshooting the bound by up to a node's worth — the right tradeoff for an audit log where exact length doesn't matter.\n\nThe Stream is independent of the session Hash and the long-term JSON documents: it answers \"what just happened\" without competing with either of those for indexing or memory budget. Consumer groups (not used in this demo) would let downstream workers — summarisers, consolidators, audit pipelines — replay the log without losing position."},{"id":"concurrency-caveats","title":"Concurrency caveats","role":"content","text":"The three helpers above trade correctness under heavy concurrency for clarity. Each is fine on a single-process demo, but lifting the code into a real multi-worker agent surfaces three races worth knowing about:\n\n* **Working memory is read-modify-write.** `AgentSession.appendTurn` calls [`HGETALL`](https://redis.io/docs/latest/commands/hgetall), mutates the `recentTurns` list in application code, and writes the Hash back with [`HSET`](https://redis.io/docs/latest/commands/hset). Two concurrent turns on the same thread can both read the same `recentTurns`, append different entries, and write back — last writer wins, the other turn is silently lost. The robust fix is either a [`WATCH`](https://redis.io/docs/latest/commands/watch) / [`MULTI`](https://redis.io/docs/latest/commands/multi) / [`EXEC`](https://redis.io/docs/latest/commands/exec) loop around the read-modify-write or a small [Lua script](https://redis.io/docs/latest/commands/eval) that does the append atomically server-side.\n\n* **Long-term dedup is not atomic.** `LongTermMemory.remember` runs a [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) KNN lookup, decides whether the candidate is a duplicate, and (if not) calls [`JSON.SET`](https://redis.io/docs/latest/commands/json.set). Two workers seeing the same fact in flight can each fail to see the other's not-yet-committed write and both insert a new memory. The pragmatic fix is to accept that the index will occasionally hold near-duplicates and run a background consolidator that periodically scans for memory pairs within a tight distance and merges them, rather than trying to make the write itself atomic.\n\n* **The active thread is server state.** The demo server keeps a single `currentThreadId` synchronized through an explicit mutex; `seedAll`, `newThread`, and `handleTurn` each release the lock between operations, so a turn racing with a thread rotation can capture the old id and apply to the previous thread. This is cosmetic for a one-user browser demo. A multi-user agent would carry the thread id on the request itself rather than as shared server state.\n\nTwo concerns specific to Java + Lettuce:\n\n* `LocalEmbedder.encodeOne` / `encodeMany` are `synchronized` because the underlying `Predictor` is not thread-safe. The demo's `Executors.newCachedThreadPool` could otherwise call into one predictor from several handler threads at once and corrupt the inference state. A higher-throughput deployment would replace that lock with a small pool of `Predictor` instances or a dedicated single-threaded inference executor.\n\n* All three helpers share a single Lettuce connection. Lettuce connections are thread-safe for individual command dispatch, but transaction state (`MULTI` / queued commands / `EXEC`) is connection-scoped — two concurrent threads queueing into the same `MULTI` would interleave their writes and each thread's `EXEC` would return a mix of replies. The shared `txLock` is passed into all three helpers (`AgentSession`, `LongTermMemory`, and `AgentEventLog`) and is held around every Redis call they issue, not only the `MULTI…EXEC` spans. That serializes every operation on the connection, which is fine for a single-user demo but trades concurrency for safety; a higher-throughput deployment would use a small pool of connections via Lettuce's `ConnectionPoolSupport` instead so handlers can run their commands in parallel.\n\nThose caveats are deliberate. A more conservative implementation would obscure the Redis-shaped parts of the pattern; the demo prioritizes a small, readable code path that maps directly onto the commands in the prose above."},{"id":"pre-seeding-long-term-memory","title":"Pre-seeding long-term memory","role":"content","text":"In a real deployment the memory store fills up organically as the agent reasons over user turns: each turn produces zero or more memories that flow into the store, with deduplication catching repeats. For the demo, `SeedMemory` pre-loads a small set of mixed semantic and episodic memories so the very first recall query returns something useful ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/memory-layer/java-lettuce/src/main/java/com/redis/agentmem/SeedMemory.java)):\n\n[code example]\n\nThe seed list mixes long-lived facts and preferences (`semantic`) with snapshots of past sessions (`episodic`), so the **Kind to write** control in the demo has something to switch between when a new turn is being remembered."},{"id":"the-interactive-demo","title":"The interactive demo","role":"content","text":"`DemoServer` runs the JDK's [`HttpServer`](https://docs.oracle.com/en/java/javase/17/docs/api/jdk.httpserver/com/sun/net/httpserver/HttpServer.html) on port 8093, with a cached thread pool dispatching requests to handlers. The HTML page exposes three live panels — working memory, recalled memories, event log — plus a memories table for admin actions. Endpoints:\n\n| Endpoint            | What it does                                                                    |\n|---------------------|---------------------------------------------------------------------------------|\n| `GET  /state`       | Index info, current session, in-scope long-term memories, and recent events.    |\n| `POST /turn`        | Embed the text, append to working memory, recall similar memories, optionally write a new memory (with dedup), append an event. |\n| `POST /new_thread`  | Start a fresh thread; long-term memory and other threads are untouched.         |\n| `POST /reset`       | Drop every long-term memory and re-seed the sample set.                         |\n| `POST /drop_memory` | Delete a single long-term memory by id.                                         |\n\nThe server holds one `LocalEmbedder`, one `AgentSession`, one `LongTermMemory`, and one `AgentEventLog` for the lifetime of the process. The \"current thread\" is a mutex-protected `String` field that the **New thread** button rotates — every browser tab inherits the same thread until you explicitly start a new one."},{"id":"run-the-demo-locally","title":"Run the demo locally","role":"content","text":"1.  Clone the [`redis/docs`](https://github.com/redis/docs) repository and change into the example\n    directory:\n\n    [code example]\n\n2.  Build the fat jar. You'll need a [JDK 17](https://adoptium.net/) or later and\n    [Maven](https://maven.apache.org/):\n\n    [code example]\n\n    The first build pulls Lettuce, DJL, and the PyTorch native libraries — that takes\n    a couple of minutes the first time and is cached afterwards.\n\n3.  Make sure a Redis instance with Redis Search and Redis JSON is running locally on\n    port 6379. [Redis Stack](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack)\n    ships both, or [Redis 8](https://redis.io/docs/latest/develop/ai/search-and-query) with the\n    Search and JSON modules enabled.\n\n4.  Start the demo. The first run downloads the `sentence-transformers/all-MiniLM-L6-v2`\n    PyTorch weights into the local DJL cache (~90 MB):\n\n    [code example]\n\n    Or via Maven: `mvn -q exec:java`.\n\n5.  Open <http://localhost:8093> and try some turns:\n\n    *  **\"Remind me which theme I prefer in editors.\"** — paraphrase of a seeded\n       semantic memory (\"The user dislikes dark mode and prefers a high-contrast\n       light theme...\"). You should see that memory recalled with a cosine\n       distance around 0.47, comfortably under the 0.55 default recall\n       threshold.\n    *  **\"What did we discuss about the order-routing outage?\"** — paraphrase of\n       a seeded episodic memory; the postmortem memory should recall around\n       0.44. Switch the **Kind to write** dropdown to `skip` so the question\n       itself doesn't enter long-term memory.\n    *  **\"I prefer concise answers without filler phrases.\"** — paraphrase of\n       a seeded *semantic* memory. Switch the **Kind to write** dropdown to\n       `semantic` so the dedup KNN runs in the same kind as the seed (dedup\n       is scoped per kind, on purpose, so an episodic write can't collapse\n       onto a semantic memory). You should then see the write **deduped**\n       onto the existing memory at a cosine distance around 0.15, with\n       `hit_count` ticking up in the memories table.\n    *  **\"My favorite color is teal.\"** — unrelated to any seed; nothing\n       recalls above the threshold (every seed lands above 0.8), and the new\n       memory is written as `episodic` (or `semantic`, depending on the\n       dropdown) under a fresh id.\n    *  Switch the **User** field to `bob` and re-ask any of the above — recall\n       returns nothing because the seed memories live under `default`. That's\n       the TAG pre-filter at work inside [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search).\n    *  Slide the **Recall threshold** down to 0.30 to see borderline paraphrases\n       drop out of the recall set, then back up to 0.70 to watch them return.\n\n    DJL drives libtorch through the same C++ kernel as Python's PyTorch\n    bindings, so distances here match the Python demo to four decimal\n    places. `sentence-transformers/all-MiniLM-L6-v2` puts a faithful\n    paraphrase in the 0.15 – 0.50 cosine-distance range, a loose paraphrase\n    or related topic in the 0.50 – 0.80 range, and unrelated queries above\n    0.8 — which is what motivates the 0.55 default recall threshold and the\n    0.20 default dedup threshold. A stricter embedding model (or a\n    domain-tuned one) would let you tighten both; a noisier one would push\n    them up. The right thresholds are always a function of the model, the\n    corpus, and how conservative the agent needs to be about accepting a\n    memory as a match.\n\nThe server is read/write against your local Redis. The default memory index is `agentmem:idx`, JSON keys live under `agent:mem:`, session Hashes under `agent:session:`, and event Streams under `agent:events:`. Useful flags (pass them after the jar):\n\n* `--host` / `--port` — change the HTTP bind address (default `127.0.0.1:8093`).\n* `--redis-host` / `--redis-port` — point at a non-local Redis (default `localhost:6379`).\n* `--mem-index-name` / `--mem-key-prefix` / `--session-key-prefix` / `--event-key-prefix` — relocate the index name and the three key prefixes (to run several demos against one Redis without colliding, for example).\n* `--no-reset` — keep the existing long-term memories across restarts instead of dropping and re-seeding.\n* `--session-ttl-seconds` — change the working-memory TTL (default 3600).\n* `--dedup-threshold` — change the cosine-distance cutoff for write-time deduplication.\n* `--recall-threshold` — change the default cosine-distance cutoff for recall."}],"examples":[{"id":"the-session-store-ex0","language":"java","code":"import com.redis.agentmem.AgentSession;\nimport com.redis.agentmem.SessionState;\nimport io.lettuce.core.ClientOptions;\nimport io.lettuce.core.RedisClient;\nimport io.lettuce.core.api.StatefulRedisConnection;\nimport io.lettuce.core.codec.ByteArrayCodec;\nimport io.lettuce.core.protocol.ProtocolVersion;\n\nRedisClient client = RedisClient.create(\"redis://localhost:6379\");\n// Pin to RESP2 so FT.SEARCH / FT.INFO reply with the flat arrays\n// the helper's parser expects (Lettuce 6.7 otherwise negotiates\n// RESP3 against Redis 7+ and wraps the same data in a map shape —\n// see the \"Sending FT.* / JSON.* commands through Lettuce\" section\n// below for the longer story).\nclient.setOptions(ClientOptions.builder()\n        .protocolVersion(ProtocolVersion.RESP2)\n        .build());\nStatefulRedisConnection<byte[], byte[]> connection = client.connect(ByteArrayCodec.INSTANCE);\n\n// Shared lock across helpers so concurrent MULTI/EXEC spans on the\n// single connection don't interleave queued commands.\nObject txLock = new Object();\nAgentSession session = new AgentSession(\n        connection, txLock, \"agent:session:\", 3600, 20);\n\nString threadId = session.newThreadId();\nSessionState state = session.start(threadId, \"alice\", \"demo-agent\",\n        \"Plan next week's meetings.\", null);\nstate = session.appendTurn(\n        threadId, \"user\", \"Schedule a budget review with finance.\",\n        \"alice\", \"demo-agent\", null);\nSystem.out.println(state.turnCount() + \" \" + state.recentTurns().size()\n        + \" \" + state.ttlSeconds());","section_id":"the-session-store"},{"id":"the-session-store-ex1","language":"text","code":"agent:session:9f3d2a4b8c61\n  thread_id=9f3d2a4b8c61\n  user=alice\n  agent=demo-agent\n  goal=Plan next week's meetings.\n  scratchpad=Need to confirm finance's availability.\n  turn_count=4\n  created_ts=1715990400.12\n  last_active_ts=1715990650.83\n  recent_turns=[{\"role\":\"user\",\"content\":\"...\",\"ts\":...}, ...]","section_id":"the-session-store"},{"id":"the-long-term-memory-store-ex0","language":"java","code":"import com.redis.agentmem.LocalEmbedder;\nimport com.redis.agentmem.LongTermMemory;\nimport com.redis.agentmem.MemoryRecord;\nimport com.redis.agentmem.WriteResult;\n\nLongTermMemory memory = new LongTermMemory(\n        connection,\n        txLock,\n        \"agentmem:idx\",\n        \"agent:mem:\",\n        384,\n        0.20,   // dedup threshold — tight at write time\n        0.55,   // recall threshold — looser at read time\n        null);  // default per-kind TTL map\nLocalEmbedder embedder = LocalEmbedder.create();\nmemory.createIndex();  // idempotent\n\n// Write a memory. The same KNN that powers recall also runs here at\n// a tighter threshold so paraphrases of the same fact collapse.\nfloat[] vec = embedder.encodeOne(\"The user prefers light mode in editors.\");\nWriteResult result = memory.remember(\n        \"The user prefers light mode in editors.\",\n        vec,\n        \"alice\",\n        \"default\",\n        \"semantic\",\n        \"9f3d2a4b8c61\",\n        null);\nSystem.out.printf(\"deduped=%s id=%s dist=%s%n\",\n        result.deduped(), result.id(), result.existingDistance());\n\n// Recall against a later question.\nfloat[] q = embedder.encodeOne(\"Which theme does this user like?\");\nfor (MemoryRecord h : memory.recall(q, \"alice\", \"default\", null, 5, null)) {\n    System.out.printf(\"%.3f [%s] %s%n\", h.distance(), h.kind(), h.text());\n}","section_id":"the-long-term-memory-store"},{"id":"sending-ft--json-commands-through-lettuce-ex0","language":"java","code":"private enum ModuleCommand implements ProtocolKeyword {\n    FT_CREATE(\"FT.CREATE\"),\n    FT_SEARCH(\"FT.SEARCH\"),\n    FT_INFO(\"FT.INFO\"),\n    FT_DROPINDEX(\"FT.DROPINDEX\"),\n    JSON_SET(\"JSON.SET\"),\n    JSON_NUMINCRBY(\"JSON.NUMINCRBY\");\n    // ...implementation elided\n}\n\nCommandArgs<byte[], byte[]> args = new CommandArgs<>(ByteArrayCodec.INSTANCE)\n        .add(indexNameBytes)\n        .add(filterClause + \"=>[KNN 5 @embedding $vec AS distance]\")\n        .add(\"PARAMS\").add(2).add(\"vec\".getBytes(UTF_8)).add(vecBytes)\n        .add(\"RETURN\").add(8).add(\"user\").add(\"namespace\").add(\"kind\")\n        .add(\"source_thread\").add(\"text\").add(\"created_ts\")\n        .add(\"hit_count\").add(\"distance\")\n        .add(\"SORTBY\").add(\"distance\").add(\"ASC\")\n        .add(\"LIMIT\").add(0).add(5)\n        .add(\"DIALECT\").add(2);\n\nList<Object> raw = sync.dispatch(\n        ModuleCommand.FT_SEARCH,\n        new NestedMultiOutput<>(ByteArrayCodec.INSTANCE),\n        args);","section_id":"sending-ft--json-commands-through-lettuce"},{"id":"data-model-ex0","language":"json","code":"agent:mem:7c3f8a1b9e02\n{\n  \"id\": \"7c3f8a1b9e02\",\n  \"user\": \"alice\",\n  \"namespace\": \"default\",\n  \"kind\": \"semantic\",\n  \"source_thread\": \"9f3d2a4b8c61\",\n  \"text\": \"The user prefers light mode in editors.\",\n  \"embedding\": [0.013, -0.041, ...],\n  \"created_ts\": 1715990400.12,\n  \"hit_count\": 0\n}","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"FT.CREATE agentmem:idx\n  ON JSON PREFIX 1 agent:mem:\n  SCHEMA\n    $.text          AS text          TEXT\n    $.user          AS user          TAG\n    $.namespace     AS namespace     TAG\n    $.kind          AS kind          TAG\n    $.source_thread AS source_thread TAG\n    $.created_ts    AS created_ts    NUMERIC SORTABLE\n    $.hit_count     AS hit_count     NUMERIC SORTABLE\n    $.embedding     AS embedding     VECTOR HNSW 6\n                                       TYPE FLOAT32 DIM 384\n                                       DISTANCE_METRIC COSINE","section_id":"data-model"},{"id":"the-query-ex0","language":"text","code":"FT.SEARCH agentmem:idx\n  \"(@user:{alice} @namespace:{default} @kind:{semantic})\n     =>[KNN 5 @embedding $vec AS distance]\"\n  PARAMS 2 vec <384-float32-bytes>\n  SORTBY distance\n  RETURN 8 user namespace kind source_thread text created_ts hit_count distance\n  DIALECT 2","section_id":"the-query"},{"id":"the-event-log-ex0","language":"java","code":"import com.redis.agentmem.AgentEvent;\nimport com.redis.agentmem.AgentEventLog;\n\nAgentEventLog events = new AgentEventLog(connection, \"agent:events:\", 1000);\nevents.record(threadId, \"turn_appended:user\",\n        \"Schedule a budget review with finance.\");\nevents.record(threadId, \"memory_written\",\n        \"wrote 7c3f8a1b9e02 as semantic\");\n\nfor (AgentEvent e : events.recent(threadId, 20)) {\n    System.out.println(e.action() + \" \" + e.detail());\n}","section_id":"the-event-log"},{"id":"pre-seeding-long-term-memory-ex0","language":"java","code":"import com.redis.agentmem.SeedMemory;\n\nLongTermMemory memory = new LongTermMemory(connection, txLock,\n        \"agentmem:idx\", \"agent:mem:\", 384, 0.20, 0.55, null);\nLocalEmbedder embedder = LocalEmbedder.create();\nmemory.createIndex();\nint written = SeedMemory.seed(memory, embedder, \"default\", \"default\", \"seed\");\nSystem.out.println(\"seeded \" + written + \" memories\");","section_id":"pre-seeding-long-term-memory"},{"id":"run-the-demo-locally-ex0","language":"bash","code":"git clone https://github.com/redis/docs.git\n    cd docs/content/develop/use-cases/memory-layer/java-lettuce","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex1","language":"bash","code":"mvn -q package","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex2","language":"bash","code":"java -jar target/agent-memory-lettuce.jar","section_id":"run-the-demo-locally"}]}
{"schema_version":2,"id":"develop/use-cases/memory-layer/nodejs","title":"Redis memory layer with node-redis","url":"https://redis.io/docs/latest/develop/use-cases/memory-layer/nodejs/","summary":"Build a Redis-backed memory layer in Node.js with node-redis, @xenova/transformers, and standard Redis commands — working memory in a Hash, long-term semantic recall as JSON with a vector index, and an event log in a Stream.","aliases":["/develop/use-cases/agent-memory/nodejs","/develop/use-cases/agent-memory/node-redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:54:57+01:00","children":[],"page_type":"content","content_hash":"1a4052ee240c4896acfdce8d3af2cd682e0be481bcd69f760c3b1bccc35a52e3","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a small Redis-backed memory layer in Node.js with [`node-redis`](https://redis.io/docs/latest/develop/clients/nodejs) and the [`@xenova/transformers`](https://www.npmjs.com/package/@xenova/transformers) library, using only standard Redis commands — no Redis Agent Memory SDK, no managed service. It includes a local web server built with Node's standard `http` module so you can send turns at the agent, watch working memory update in place, see semantically similar long-term memories recalled in real time, watch the write-time deduplication skip near-duplicates, and inspect the per-thread event log.\n\nWhen you are ready for production, [Redis Agent Memory](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory) is the fastest way to get a production-ready memory layer running — as a managed service on Redis Cloud, or self-managed on-prem.\n\nThe embedder is [`@xenova/transformers`](https://www.npmjs.com/package/@xenova/transformers) running the ONNX-exported [`Xenova/all-MiniLM-L6-v2`](https://huggingface.co/Xenova/all-MiniLM-L6-v2) model, which is the same encoder the [Python example](https://redis.io/docs/latest/develop/use-cases/memory-layer/redis-py) uses. Embeddings produced by the two implementations are numerically very close — paraphrase distances differ only at the fourth decimal place — so a memory written by one demo can be recalled by the other against the same Redis instance, and the distance bands the Python walkthrough quotes carry over to this one without recalibration."},{"id":"overview","title":"Overview","role":"overview","text":"The memory layer splits across three Redis primitives, each handling one tier:\n\n* **Working memory** for the active session is a [Hash](https://redis.io/docs/latest/develop/data-types/hashes) at `agent:session:<threadId>` holding the goal, scratchpad, a rolling window of recent turns (as a JSON list inside one field), and a few audit timestamps. One [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) returns the whole session in a single round trip; every write refreshes the key's [`EXPIRE`](https://redis.io/docs/latest/commands/expire) so idle sessions decay on their own.\n* **Long-term memory** is a set of [JSON](https://redis.io/docs/latest/develop/data-types/json) documents at `agent:mem:<id>`, each carrying the memory text, a 384-dimensional embedding vector, and tag fields for user, namespace, kind (episodic / semantic), and source thread. A single [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) index covers the [HNSW vector field](https://redis.io/docs/latest/develop/ai/search-and-query/vectors) and every metadata field, so one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call performs the KNN with the metadata pre-filter in the same round trip. Write-time deduplication runs the same KNN at insert time and skips a new memory whose nearest existing entry is within a tighter threshold.\n* **Event log** for the agent's actions and observations is a [Stream](https://redis.io/docs/latest/develop/data-types/streams) at `agent:events:<threadId>`, appended with [`XADD MAXLEN ~`](https://redis.io/docs/latest/commands/xadd) so retention stays bounded automatically, replayed with [`XREVRANGE`](https://redis.io/docs/latest/commands/xrevrange).\n\nThat gives you:\n\n* A single round trip per tier: one [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) for the session, one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) for recall, one [`XADD`](https://redis.io/docs/latest/commands/xadd) for the event log.\n* Sub-millisecond reads on every step of the agent loop, so the memory layer doesn't dominate per-step latency.\n* Per-tier decay: short TTLs on working memory, longer on episodic memories, no TTL on semantic memories. Combined with a database-level [eviction policy](https://redis.io/docs/latest/develop/reference/eviction) (LFU is the common choice), memory stays bounded under pressure.\n* Scoping enforced inside the query: a recall query for `user=alice` will never see `user=bob`'s memories, because the TAG filter goes into the same [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call as the KNN."},{"id":"how-it-works","title":"How it works","role":"content","text":"Each turn through the agent loop touches all three tiers in one pass: append to working memory, recall similar long-term memories, write the turn back as a new memory (with deduplication), and append one event to the log."},{"id":"per-turn-flow","title":"Per-turn flow","role":"content","text":"1. The application calls `embedder.encodeOne(text)` to turn the incoming turn into a 384-dimensional `Float32Array`.\n2. `session.appendTurn(threadId, { role, content })` reads the per-thread Hash with [`HGETALL`](https://redis.io/docs/latest/commands/hgetall), appends the new turn to the rolling window in application code, trims it back to the configured maximum, and writes the Hash back with an [`HSET`](https://redis.io/docs/latest/commands/hset) + [`EXPIRE`](https://redis.io/docs/latest/commands/expire) inside a [`MULTI/EXEC`](https://redis.io/docs/latest/commands/multi). The session TTL refreshes on every write so an active thread stays alive.\n3. `memory.recall({ queryEmbedding: vec, user, namespace, k: 5 })` runs [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) with a TAG pre-filter and a `KNN 5` clause. Redis returns the closest matching memories together with their cosine distances; memories beyond the recall threshold are dropped before they reach the agent so an unrelated query doesn't surface confident-looking false positives.\n4. `memory.remember({ text, embedding: vec, user, namespace, kind })` runs the same KNN with a tighter dedup threshold. If an existing memory is within the threshold, the new write is skipped and the existing memory's `hit_count` is incremented with [`JSON.NUMINCRBY`](https://redis.io/docs/latest/commands/json.numincrby); otherwise a fresh JSON document is written with [`JSON.SET`](https://redis.io/docs/latest/commands/json.set) and a per-kind [`EXPIRE`](https://redis.io/docs/latest/commands/expire) — `episodic` defaults to seven days, `semantic` has no TTL by default.\n5. `eventLog.record(threadId, action, detail)` appends one entry to the per-thread Stream with [`XADD MAXLEN ~`](https://redis.io/docs/latest/commands/xadd), bounding retention to roughly a thousand entries per thread without an explicit cleanup job.\n\nThe embedding is computed once and reused for steps 3 and 4 — there's no point encoding the same text twice. Recall runs before the write, so the agent doesn't see its own just-written turn echoed back as a recalled memory."},{"id":"the-session-store","title":"The session store","role":"content","text":"`AgentSession` wraps the working-memory Hash and the rolling turn window ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/memory-layer/nodejs/sessionStore.js)):\n\n[code example]\n\nThe data model is one Hash per thread. The rolling turn window is stored as a JSON string in a single field so the whole session loads in one [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) — the hash never grows in size or field count as the conversation goes on.\n\n[code example]\n\nEvery write — `start`, `appendTurn`, `setScratchpad` — runs the [`HSET`](https://redis.io/docs/latest/commands/hset) and [`EXPIRE`](https://redis.io/docs/latest/commands/expire) inside a [`MULTI`](https://redis.io/docs/latest/commands/multi) / [`EXEC`](https://redis.io/docs/latest/commands/exec) so a connection drop between the two writes can't leave the session without a TTL."},{"id":"the-long-term-memory-store","title":"The long-term memory store","role":"content","text":"`LongTermMemory` owns the JSON documents, the vector index, the recall query, and the write-time deduplication ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/memory-layer/nodejs/longTermMemory.js)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each memory is a JSON document at `agent:mem:<id>`. The embedding is a JSON array of floats so the document is human-readable from `redis-cli`; [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) still expects the *query* vector as raw `float32` bytes (a `Buffer` view over a `Float32Array` in Node), regardless of how the indexed document stores it.\n\n[code example]\n\nThe Redis Search index is declared on the JSON document type with `AS` aliases so the query syntax stays compact:\n\n[code example]"},{"id":"the-query","title":"The query","role":"content","text":"Both recall and dedup share the same hybrid query: a TAG pre-filter in parentheses followed by `=>[KNN k @embedding $vec]`. With `DIALECT 2`, Redis applies the filter first and KNN-ranks only the matching documents.\n\n[code example]\n\n`distance` is the cosine *distance* (0 means identical, 2 means opposite). Recall and dedup share the same query shape; only the threshold differs — strict at write time so the index doesn't fill with paraphrases of the same fact, looser at read time so the agent gets a wider net of relevant memories."},{"id":"per-kind-ttls","title":"Per-kind TTLs","role":"content","text":"`remember` resolves the entry's TTL from the memory's `kind`:\n\n| Kind      | Default TTL | When to use it                                              |\n|-----------|-------------|-------------------------------------------------------------|\n| `episodic` | 7 days     | Snapshots from a specific session that should decay.        |\n| `semantic` | none       | Distilled facts and preferences the agent carries forward.  |\n\nYou can override per write with `ttlSeconds: ...` on `remember`, or pass a different `ttlByKind: { ... }` to the `LongTermMemory` constructor — for example, to give semantic memories a six-month TTL while leaving episodic memories at seven days."},{"id":"the-event-log","title":"The event log","role":"content","text":"`AgentEventLog` is a thin wrapper over a per-thread Redis Stream ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/memory-layer/nodejs/eventLog.js)):\n\n[code example]\n\n`record` calls [`XADD`](https://redis.io/docs/latest/commands/xadd) with `MAXLEN ~ 1000`. The tilde lets Redis trim in whole-node units instead of exactly-N units, which is much cheaper at the cost of overshooting the bound by up to a node's worth — the right tradeoff for an audit log where exact length doesn't matter.\n\nThe Stream is independent of the session Hash and the long-term JSON documents: it answers \"what just happened\" without competing with either of those for indexing or memory budget. Consumer groups (not used in this demo) would let downstream workers — summarisers, consolidators, audit pipelines — replay the log without losing position."},{"id":"concurrency-caveats","title":"Concurrency caveats","role":"content","text":"The three helpers above trade correctness under heavy concurrency for clarity. Each is fine on a single-process demo, but lifting the code into a real multi-worker agent surfaces three races worth knowing about:\n\n* **Working memory is read-modify-write.** `AgentSession.appendTurn` calls [`HGETALL`](https://redis.io/docs/latest/commands/hgetall), mutates the `recent_turns` list in application code, and writes the Hash back with [`HSET`](https://redis.io/docs/latest/commands/hset). Two concurrent turns on the same thread can both read the same `recent_turns`, append different entries, and write back — last writer wins, the other turn is silently lost. The robust fix is either a [`WATCH`](https://redis.io/docs/latest/commands/watch) / [`MULTI`](https://redis.io/docs/latest/commands/multi) / [`EXEC`](https://redis.io/docs/latest/commands/exec) loop around the read-modify-write or a small [Lua script](https://redis.io/docs/latest/commands/eval) that does the append atomically server-side.\n\n* **Long-term dedup is not atomic.** `LongTermMemory.remember` runs a [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) KNN lookup, decides whether the candidate is a duplicate, and (if not) calls [`JSON.SET`](https://redis.io/docs/latest/commands/json.set). Two workers seeing the same fact in flight can each fail to see the other's not-yet-committed write and both insert a new memory. The pragmatic fix is to accept that the index will occasionally hold near-duplicates and run a background consolidator that periodically scans for memory pairs within a tight distance and merges them, rather than trying to make the write itself atomic.\n\n* **The active thread is server state.** The demo server keeps a single `currentThreadId` that `/new_thread` and `/reset` mutate. `handleTurn` reads it without coordination, so a turn racing with a thread rotation can apply to the previous thread. This is cosmetic for a one-user browser demo. A multi-user agent would carry the thread id on the request itself rather than as shared server state.\n\nThose caveats are deliberate. A more conservative implementation would obscure the Redis-shaped parts of the pattern; the demo prioritizes a small, readable code path that maps directly onto the commands in the prose above."},{"id":"pre-seeding-long-term-memory","title":"Pre-seeding long-term memory","role":"content","text":"In a real deployment the memory store fills up organically as the agent reasons over user turns: each turn produces zero or more memories that flow into the store, with deduplication catching repeats. For the demo, `seedMemory.js` pre-loads a small set of mixed semantic and episodic memories so the very first recall query returns something useful ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/memory-layer/nodejs/seedMemory.js)):\n\n[code example]\n\nThe seed list mixes long-lived facts and preferences (`semantic`) with snapshots of past sessions (`episodic`), so the **Kind to write** control in the demo has something to switch between when a new turn is being remembered."},{"id":"the-interactive-demo","title":"The interactive demo","role":"content","text":"`demoServer.js` runs a Node [`http`](https://nodejs.org/api/http.html) server on port 8089. The HTML page exposes three live panels — working memory, recalled memories, event log — plus a memories table for admin actions. Endpoints:\n\n| Endpoint            | What it does                                                                    |\n|---------------------|---------------------------------------------------------------------------------|\n| `GET  /state`       | Index info, current session, in-scope long-term memories, and recent events.    |\n| `POST /turn`        | Embed the text, append to working memory, recall similar memories, optionally write a new memory (with dedup), append an event. |\n| `POST /new_thread`  | Start a fresh thread; long-term memory and other threads are untouched.         |\n| `POST /reset`       | Drop every long-term memory and re-seed the sample set.                         |\n| `POST /drop_memory` | Delete a single long-term memory by id.                                         |\n\nThe server holds one `LocalEmbedder`, one `AgentSession`, one `LongTermMemory`, and one `AgentEventLog` for the lifetime of the process. The \"current thread\" is a single field on the demo object that the **New thread** button rotates — every browser tab inherits the same thread until you explicitly start a new one."},{"id":"run-the-demo-locally","title":"Run the demo locally","role":"content","text":"1.  Clone the [`redis/docs`](https://github.com/redis/docs) repository and change into the example\n    directory:\n\n    [code example]\n\n2.  Install the dependencies:\n\n    [code example]\n\n3.  Make sure a Redis instance with Redis Search and Redis JSON is running locally on\n    port 6379. [Redis Stack](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack)\n    ships both, or [Redis 8](https://redis.io/docs/latest/develop/ai/search-and-query) with the\n    Search and JSON modules enabled.\n\n4.  Start the demo server. The first run downloads the\n    [`Xenova/all-MiniLM-L6-v2`](https://huggingface.co/Xenova/all-MiniLM-L6-v2) ONNX\n    weights (around 90 MB) into the local Hugging Face cache:\n\n    [code example]\n\n5.  Open <http://localhost:8089> and try some turns:\n\n    *  **\"Remind me which theme I prefer in editors.\"** — paraphrase of a seeded\n       semantic memory (\"The user dislikes dark mode and prefers a high-contrast\n       light theme...\"). You should see that memory recalled with a cosine\n       distance around 0.47, comfortably under the 0.55 default recall\n       threshold.\n    *  **\"What did we discuss about the order-routing outage?\"** — paraphrase of\n       a seeded episodic memory; the postmortem memory should recall around\n       0.44. Switch the **Kind to write** dropdown to `skip` so the question\n       itself doesn't enter long-term memory.\n    *  **\"I prefer concise answers without filler phrases.\"** — paraphrase of\n       a seeded *semantic* memory. Switch the **Kind to write** dropdown to\n       `semantic` so the dedup KNN runs in the same kind as the seed (dedup\n       is scoped per kind, on purpose, so an episodic write can't collapse\n       onto a semantic memory). You should then see the write **deduped**\n       onto the existing memory at a cosine distance around 0.18 (the\n       ONNX-exported model runs slightly different arithmetic from the\n       PyTorch one, so paraphrase distances sit a hair higher than in the\n       Python demo), with `hit_count` ticking up in the memories table.\n    *  **\"My favorite color is teal.\"** — unrelated to any seed; nothing recalls\n       above the threshold (every seed lands above 0.8), and the new memory is\n       written as `episodic` (or `semantic`, depending on the dropdown) under a\n       fresh id.\n    *  Switch the **User** field to `bob` and re-ask any of the above — recall\n       returns nothing because the seed memories live under `default`. That's\n       the TAG pre-filter at work inside [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search).\n    *  Slide the **Recall threshold** down to 0.30 to see borderline paraphrases\n       drop out of the recall set, then back up to 0.70 to watch them return.\n\n    `Xenova/all-MiniLM-L6-v2` puts a faithful paraphrase in the 0.15 – 0.50\n    cosine-distance range, a loose paraphrase or related topic in the 0.50 – 0.80\n    range, and unrelated queries above 0.8 — which is what motivates the 0.55\n    default recall threshold and the 0.20 default dedup threshold. A stricter\n    embedding model (or a domain-tuned one) would let you tighten both; a\n    noisier one would push them up. The right thresholds are always a function\n    of the model, the corpus, and how conservative the agent needs to be about\n    accepting a memory as a match.\n\nThe server is read/write against your local Redis. The default memory index is `agentmem:idx`, JSON keys live under `agent:mem:`, session Hashes under `agent:session:`, and event Streams under `agent:events:`. Useful flags:\n\n* `--no-reset` — keep the existing long-term memories across restarts instead of dropping and re-seeding.\n* `--session-ttl-seconds` — change the working-memory TTL (default 3600).\n* `--dedup-threshold` — change the cosine-distance cutoff for write-time deduplication.\n* `--recall-threshold` — change the default cosine-distance cutoff for recall."}],"examples":[{"id":"the-session-store-ex0","language":"javascript","code":"import { createClient } from 'redis';\nimport { AgentSession } from './sessionStore.js';\n\nconst client = createClient();\nawait client.connect();\n\nconst session = new AgentSession({\n  client,\n  keyPrefix: 'agent:session:',\n  defaultTtlSeconds: 3600,  // one hour\n  maxTurns: 20,             // rolling window per thread\n});\n\nconst threadId = session.newThreadId();\nawait session.start(threadId, {\n  user: 'alice',\n  agent: 'demo-agent',\n  goal: \"Plan next week's meetings.\",\n});\nawait session.appendTurn(threadId, {\n  role: 'user',\n  content: 'Schedule a budget review with finance.',\n});\nconst state = await session.load(threadId);\nconsole.log(state.turn_count, state.recent_turns.length, state.ttl_seconds);","section_id":"the-session-store"},{"id":"the-session-store-ex1","language":"text","code":"agent:session:9f3d2a4b8c61\n  thread_id=9f3d2a4b8c61\n  user=alice\n  agent=demo-agent\n  goal=Plan next week's meetings.\n  scratchpad=Need to confirm finance's availability.\n  turn_count=4\n  created_ts=1715990400.12\n  last_active_ts=1715990650.83\n  recent_turns=[{\"role\":\"user\",\"content\":\"...\",\"ts\":...}, ...]","section_id":"the-session-store"},{"id":"the-long-term-memory-store-ex0","language":"javascript","code":"import { LongTermMemory } from './longTermMemory.js';\nimport { LocalEmbedder } from './embeddings.js';\n\nconst memory = new LongTermMemory({\n  client,\n  indexName: 'agentmem:idx',\n  keyPrefix: 'agent:mem:',\n  dedupThreshold: 0.20,    // cosine distance — tight at write time\n  recallThreshold: 0.55,   // looser at read time\n});\nconst embedder = await LocalEmbedder.create();\nawait memory.createIndex();  // idempotent\n\n// Write a memory. The same KNN that powers recall also runs here at\n// a tighter threshold so paraphrases of the same fact collapse.\nconst vec = await embedder.encodeOne('The user prefers light mode in editors.');\nconst result = await memory.remember({\n  text: 'The user prefers light mode in editors.',\n  embedding: vec,\n  user: 'alice',\n  namespace: 'default',\n  kind: 'semantic',\n  sourceThread: '9f3d2a4b8c61',\n});\nconsole.log(result.deduped, result.id, result.existingDistance);\n\n// Recall against a later question.\nconst q = await embedder.encodeOne('Which theme does this user like?');\nconst hits = await memory.recall({\n  queryEmbedding: q,\n  user: 'alice',\n  namespace: 'default',\n  k: 5,\n});\nfor (const h of hits) {\n  console.log(`${h.distance.toFixed(3)} [${h.kind}] ${h.text}`);\n}","section_id":"the-long-term-memory-store"},{"id":"data-model-ex0","language":"json","code":"agent:mem:7c3f8a1b9e02\n{\n  \"id\": \"7c3f8a1b9e02\",\n  \"user\": \"alice\",\n  \"namespace\": \"default\",\n  \"kind\": \"semantic\",\n  \"source_thread\": \"9f3d2a4b8c61\",\n  \"text\": \"The user prefers light mode in editors.\",\n  \"embedding\": [0.013, -0.041, ...],\n  \"created_ts\": 1715990400.12,\n  \"hit_count\": 0\n}","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"FT.CREATE agentmem:idx\n  ON JSON PREFIX 1 agent:mem:\n  SCHEMA\n    $.text          AS text          TEXT\n    $.user          AS user          TAG\n    $.namespace     AS namespace     TAG\n    $.kind          AS kind          TAG\n    $.source_thread AS source_thread TAG\n    $.created_ts    AS created_ts    NUMERIC SORTABLE\n    $.hit_count     AS hit_count     NUMERIC SORTABLE\n    $.embedding     AS embedding     VECTOR HNSW 6\n                                       TYPE FLOAT32 DIM 384\n                                       DISTANCE_METRIC COSINE","section_id":"data-model"},{"id":"the-query-ex0","language":"text","code":"FT.SEARCH agentmem:idx\n  \"(@user:{alice} @namespace:{default} @kind:{semantic})\n     =>[KNN 5 @embedding $vec AS distance]\"\n  PARAMS 2 vec <384-float32-bytes>\n  SORTBY distance\n  RETURN 8 user namespace kind source_thread text created_ts hit_count distance\n  DIALECT 2","section_id":"the-query"},{"id":"the-event-log-ex0","language":"javascript","code":"import { AgentEventLog } from './eventLog.js';\n\nconst events = new AgentEventLog({ client, maxLen: 1000 });\nawait events.record(threadId, 'turn_appended:user',\n  'Schedule a budget review with finance.');\nawait events.record(threadId, 'memory_written',\n  'wrote 7c3f8a1b9e02 as semantic');\n\nfor (const event of await events.recent(threadId, 20)) {\n  console.log(event.action, event.detail);\n}","section_id":"the-event-log"},{"id":"pre-seeding-long-term-memory-ex0","language":"javascript","code":"import { seed } from './seedMemory.js';\nimport { LongTermMemory } from './longTermMemory.js';\nimport { LocalEmbedder } from './embeddings.js';\n\nconst memory = new LongTermMemory({ client });\nconst embedder = await LocalEmbedder.create();\nawait memory.createIndex();\nawait seed(memory, embedder, { user: 'default', namespace: 'default' });","section_id":"pre-seeding-long-term-memory"},{"id":"run-the-demo-locally-ex0","language":"bash","code":"git clone https://github.com/redis/docs.git\n    cd docs/content/develop/use-cases/memory-layer/nodejs","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex1","language":"bash","code":"npm install","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex2","language":"bash","code":"npm start","section_id":"run-the-demo-locally"}]}
{"schema_version":2,"id":"develop/use-cases/memory-layer/php","title":"Redis memory layer with Predis","url":"https://redis.io/docs/latest/develop/use-cases/memory-layer/php/","summary":"Build a Redis-backed memory layer in PHP with Predis, TransformersPHP, and standard Redis commands — working memory in a Hash, long-term semantic recall as JSON with a vector index, and an event log in a Stream.","aliases":["/develop/use-cases/agent-memory/php","/develop/use-cases/agent-memory/predis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:54:57+01:00","children":[],"page_type":"content","content_hash":"f799390f941ce3e5b0d986155edc04bc03ec72e33f3e0f687bebda0c7e6fd5f6","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a small Redis-backed memory layer in PHP with [Predis](https://redis.io/docs/latest/develop/clients/php) and the [TransformersPHP](https://transformers.codewithkyrian.com/) library, using only standard Redis commands — no Redis Agent Memory SDK, no managed service. It includes a local web server built on PHP's `stream_socket_server` so you can send turns at the agent, watch working memory update in place, see semantically similar long-term memories recalled in real time, watch the write-time deduplication skip near-duplicates, and inspect the per-thread event log.\n\nThe embedder is [TransformersPHP](https://transformers.codewithkyrian.com/) running the ONNX-exported [`Xenova/all-MiniLM-L6-v2`](https://huggingface.co/Xenova/all-MiniLM-L6-v2) model through ONNX Runtime via FFI, which is the same encoder the [Python example](https://redis.io/docs/latest/develop/use-cases/memory-layer/redis-py) uses. Embeddings produced by the two implementations are numerically very close — paraphrase distances drift by less than 0.02 — so a memory written by one demo can be recalled by the other against the same Redis instance, and the distance bands the Python walkthrough quotes carry over to this one without recalibration. One quirk worth flagging up front: TransformersPHP 0.6 accepts a `normalize: true` keyword on the `feature-extraction` / `embeddings` pipeline but silently returns un-normalized vectors anyway, so the `Embedder` wrapper L2-normalizes in PHP code before handing the vector to recall or dedup — see [Embedder.php](https://github.com/redis/docs/blob/main/content/develop/use-cases/memory-layer/php/src/Embedder.php) for the workaround."},{"id":"overview","title":"Overview","role":"overview","text":"The memory layer splits across three Redis primitives, each handling one tier:\n\n* **Working memory** for the active session is a [Hash](https://redis.io/docs/latest/develop/data-types/hashes) at `agent:session:<thread_id>` holding the goal, scratchpad, a rolling window of recent turns (as a JSON list inside one field), and a few audit timestamps. One [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) returns the whole session in a single round trip; every write refreshes the key's [`EXPIRE`](https://redis.io/docs/latest/commands/expire) so idle sessions decay on their own.\n* **Long-term memory** is a set of [JSON](https://redis.io/docs/latest/develop/data-types/json) documents at `agent:mem:<id>`, each carrying the memory text, a 384-dimensional embedding vector, and tag fields for user, namespace, kind (episodic / semantic), and source thread. A single [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) index covers the [HNSW vector field](https://redis.io/docs/latest/develop/ai/search-and-query/vectors) and every metadata field, so one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call performs the KNN with the metadata pre-filter in the same round trip. Write-time deduplication runs the same KNN at insert time and skips a new memory whose nearest existing entry is within a tighter threshold.\n* **Event log** for the agent's actions and observations is a [Stream](https://redis.io/docs/latest/develop/data-types/streams) at `agent:events:<thread_id>`, appended with [`XADD MAXLEN ~`](https://redis.io/docs/latest/commands/xadd) so retention stays bounded automatically, replayed with [`XREVRANGE`](https://redis.io/docs/latest/commands/xrevrange).\n\nThat gives you:\n\n* A single round trip per tier: one [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) for the session, one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) for recall, one [`XADD`](https://redis.io/docs/latest/commands/xadd) for the event log. ([`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) itself is one round trip; the helper also issues one [`TTL`](https://redis.io/docs/latest/commands/ttl) call per returned row to populate `ttl_seconds` for the admin panel.)\n* Sub-millisecond reads on every step of the agent loop, so the memory layer doesn't dominate per-step latency.\n* Per-tier decay: short TTLs on working memory, longer on episodic memories, no TTL on semantic memories. Combined with a database-level [eviction policy](https://redis.io/docs/latest/develop/reference/eviction) (LFU is the common choice), memory stays bounded under pressure.\n* Scoping enforced inside the query: a recall query for `user=alice` will never see `user=bob`'s memories, because the TAG filter goes into the same [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call as the KNN."},{"id":"how-it-works","title":"How it works","role":"content","text":"Each turn through the agent loop touches all three tiers in one pass: append to working memory, recall similar long-term memories, write the turn back as a new memory (with deduplication), and append one event to the log."},{"id":"per-turn-flow","title":"Per-turn flow","role":"content","text":"1. The application calls `$embedder->encodeOne($text)` to turn the incoming turn into a 384-element `array` of floats, L2-normalized in PHP code (see the TransformersPHP normalization quirk noted above).\n2. `$session->appendTurn($threadId, role: ..., content: ...)` reads the per-thread Hash with [`HGETALL`](https://redis.io/docs/latest/commands/hgetall), appends the new turn to the rolling window in application code, trims it back to the configured maximum, and writes the Hash back with [`HSET`](https://redis.io/docs/latest/commands/hset) + [`EXPIRE`](https://redis.io/docs/latest/commands/expire) inside a [`MULTI/EXEC`](https://redis.io/docs/latest/commands/multi). The session TTL refreshes on every write so an active thread stays alive.\n3. `$memory->recall(queryEmbedding: $vec, user: ..., namespace: ..., k: 5)` runs [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) with a TAG pre-filter and a `KNN 5` clause. Redis returns the closest matching memories together with their cosine distances; memories beyond the recall threshold are dropped before they reach the agent so an unrelated query doesn't surface confident-looking false positives.\n4. `$memory->remember(text: ..., embedding: $vec, user: ..., namespace: ..., kind: ...)` runs the same KNN with a tighter dedup threshold. If an existing memory is within the threshold, the new write is skipped and the existing memory's `hit_count` is incremented with [`JSON.NUMINCRBY`](https://redis.io/docs/latest/commands/json.numincrby); otherwise a fresh JSON document is written with [`JSON.SET`](https://redis.io/docs/latest/commands/json.set) and a per-kind [`EXPIRE`](https://redis.io/docs/latest/commands/expire) — `episodic` defaults to seven days, `semantic` has no TTL by default.\n5. `$eventLog->record($threadId, $action, $detail)` appends one entry to the per-thread Stream with [`XADD MAXLEN ~`](https://redis.io/docs/latest/commands/xadd), bounding retention to roughly a thousand entries per thread without an explicit cleanup job.\n\nThe embedding is computed once and reused for steps 3 and 4 — there's no point encoding the same text twice. Recall runs before the write, so the agent doesn't see its own just-written turn echoed back as a recalled memory."},{"id":"the-session-store","title":"The session store","role":"content","text":"`AgentSession` wraps the working-memory Hash and the rolling turn window ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/memory-layer/php/src/AgentSession.php)):\n\n[code example]\n\nThe data model is one Hash per thread. The rolling turn window is stored as a JSON string in a single field so the whole session loads in one [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) — the hash never grows in size or field count as the conversation goes on.\n\n[code example]\n\nEvery write — `start`, `appendTurn`, `setScratchpad` — runs the [`HSET`](https://redis.io/docs/latest/commands/hset) and [`EXPIRE`](https://redis.io/docs/latest/commands/expire) inside a [`MULTI`](https://redis.io/docs/latest/commands/multi) / [`EXEC`](https://redis.io/docs/latest/commands/exec) so a connection drop between the two writes can't leave the session without a TTL."},{"id":"the-long-term-memory-store","title":"The long-term memory store","role":"content","text":"`LongTermMemory` owns the JSON documents, the vector index, the recall query, and the write-time deduplication ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/memory-layer/php/src/LongTermMemory.php)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each memory is a JSON document at `agent:mem:<id>`. The embedding is a JSON array of floats so the document is human-readable from `redis-cli`; [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) still expects the *query* vector as raw `float32` bytes (the demo packs them with PHP's [`pack('g*', ...)`](https://www.php.net/manual/en/function.pack.php), which emits little-endian float32), regardless of how the indexed document stores it.\n\n[code example]\n\nThe Redis Search index is declared on the JSON document type with `AS` aliases so the query syntax stays compact:\n\n[code example]"},{"id":"the-query","title":"The query","role":"content","text":"Both recall and dedup share the same hybrid query: a TAG pre-filter in parentheses followed by `=>[KNN k @embedding $vec]`. With `DIALECT 2`, Redis applies the filter first and KNN-ranks only the matching documents.\n\n[code example]\n\n`distance` is the cosine *distance* (0 means identical, 2 means opposite). Recall and dedup share the same query shape; only the threshold differs — strict at write time so the index doesn't fill with paraphrases of the same fact, looser at read time so the agent gets a wider net of relevant memories."},{"id":"per-kind-ttls","title":"Per-kind TTLs","role":"content","text":"`remember` resolves the entry's TTL from the memory's `kind`:\n\n| Kind      | Default TTL | When to use it                                              |\n|-----------|-------------|-------------------------------------------------------------|\n| `episodic` | 7 days     | Snapshots from a specific session that should decay.        |\n| `semantic` | none       | Distilled facts and preferences the agent carries forward.  |\n\nYou can override per write with `ttlSeconds: ...` on `remember`, or pass a different `ttlByKind: [...]` to the `LongTermMemory` constructor — for example, to give semantic memories a six-month TTL while leaving episodic memories at seven days."},{"id":"the-event-log","title":"The event log","role":"content","text":"`AgentEventLog` is a thin wrapper over a per-thread Redis Stream ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/memory-layer/php/src/AgentEventLog.php)):\n\n[code example]\n\n`record` calls [`XADD`](https://redis.io/docs/latest/commands/xadd) with `MAXLEN ~ 1000`. The tilde lets Redis trim in whole-node units instead of exactly-N units, which is much cheaper at the cost of overshooting the bound by up to a node's worth — the right tradeoff for an audit log where exact length doesn't matter.\n\nThe Stream is independent of the session Hash and the long-term JSON documents: it answers \"what just happened\" without competing with either of those for indexing or memory budget. Consumer groups (not used in this demo) would let downstream workers — summarizers, consolidators, audit pipelines — replay the log without losing position."},{"id":"concurrency-caveats","title":"Concurrency caveats","role":"content","text":"The three helpers above trade correctness under heavy concurrency for clarity. Each is fine on a single-process demo, but lifting the code into a real multi-worker agent surfaces three races worth knowing about:\n\n* **Working memory is read-modify-write.** `AgentSession::appendTurn` calls [`HGETALL`](https://redis.io/docs/latest/commands/hgetall), mutates the `recent_turns` list in application code, and writes the Hash back with [`HSET`](https://redis.io/docs/latest/commands/hset). Two concurrent turns on the same thread can both read the same `recent_turns`, append different entries, and write back — last writer wins, the other turn is silently lost. The robust fix is either a [`WATCH`](https://redis.io/docs/latest/commands/watch) / [`MULTI`](https://redis.io/docs/latest/commands/multi) / [`EXEC`](https://redis.io/docs/latest/commands/exec) loop around the read-modify-write or a small [Lua script](https://redis.io/docs/latest/commands/eval) that does the append atomically server-side.\n\n* **Long-term dedup is not atomic.** `LongTermMemory::remember` runs a [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) KNN lookup, decides whether the candidate is a duplicate, and (if not) calls [`JSON.SET`](https://redis.io/docs/latest/commands/json.set). Two workers seeing the same fact in flight can each fail to see the other's not-yet-committed write and both insert a new memory. The pragmatic fix is to accept that the index will occasionally hold near-duplicates and run a background consolidator that periodically scans for memory pairs within a tight distance and merges them, rather than trying to make the write itself atomic.\n\n* **The active thread is server state.** The demo server keeps a single `currentThreadId` field on a `DemoState` object that `/new_thread` and `/reset` mutate; `handleTurn` reads it without coordination, so a turn racing with a thread rotation can apply to the previous thread. This is cosmetic for a one-user browser demo. A multi-user agent would carry the thread id on the request itself rather than as shared server state.\n\n* **`Embedder` is shared.** TransformersPHP's `pipeline` object is constructed once and reused across every request; the underlying ONNX session is *not* documented as thread-safe. PHP's request model usually makes this a non-issue (one process, one in-flight request at a time on the blocking demo server), but if you front this code with a thread-pool runtime like Swoole or ReactPHP, fence the embedder behind a mutex or per-worker instance.\n\nThose caveats are deliberate. A more conservative implementation would obscure the Redis-shaped parts of the pattern; the demo prioritizes a small, readable code path that maps directly onto the commands in the prose above."},{"id":"pre-seeding-long-term-memory","title":"Pre-seeding long-term memory","role":"content","text":"In a real deployment the memory store fills up organically as the agent reasons over user turns: each turn produces zero or more memories that flow into the store, with deduplication catching repeats. For the demo, `SeedMemory.php` pre-loads a small set of mixed semantic and episodic memories so the very first recall query returns something useful ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/memory-layer/php/src/SeedMemory.php)):\n\n[code example]\n\nThe seed list mixes long-lived facts and preferences (`semantic`) with snapshots of past sessions (`episodic`), so the **Kind to write** control in the demo has something to switch between when a new turn is being remembered."},{"id":"the-interactive-demo","title":"The interactive demo","role":"content","text":"`demo_server.php` runs a [`stream_socket_server`](https://www.php.net/manual/en/function.stream-socket-server.php) HTTP/1.1 loop on port 8090. PHP's built-in `php -S` dev server re-runs the entry script for each request and discards process state in between, which would force the ~80 MB ONNX model to reload on every turn; the single-process socket loop keeps the embedder, the Predis connection, and the demo state alive between requests. The HTML page exposes three live panels — working memory, recalled memories, event log — plus a memories table for admin actions. Endpoints:\n\n| Endpoint            | What it does                                                                    |\n|---------------------|---------------------------------------------------------------------------------|\n| `GET  /state`       | Index info, current session, in-scope long-term memories, and recent events.    |\n| `POST /turn`        | Embed the text, append to working memory, recall similar memories, optionally write a new memory (with dedup), append an event. |\n| `POST /new_thread`  | Start a fresh thread; long-term memory and other threads are untouched.         |\n| `POST /reset`       | Drop every long-term memory and re-seed the sample set.                         |\n| `POST /drop_memory` | Delete a single long-term memory by id.                                         |\n\nThe server holds one `Embedder`, one `AgentSession`, one `LongTermMemory`, and one `AgentEventLog` for the lifetime of the process. The \"current thread\" is a single field on a `DemoState` object that the **New thread** button rotates — every browser tab inherits the same thread until you explicitly start a new one."},{"id":"run-the-demo-locally","title":"Run the demo locally","role":"content","text":"1.  Clone the [`redis/docs`](https://github.com/redis/docs) repository and change into the\n    example directory:\n\n    [code example]\n\n2.  Install the dependencies. TransformersPHP ships an installer plugin that downloads\n    the ONNX Runtime shared library for your platform; the `composer.json` in this\n    directory already allow-lists it.\n\n    [code example]\n\n    PHP 8.1 or later is required, with the [`ffi`](https://www.php.net/manual/en/book.ffi.php)\n    extension enabled (it ships with the official PHP builds and the Homebrew formula on macOS).\n\n3.  Make sure a Redis instance with Redis Search and Redis JSON is running locally on\n    port 6379. [Redis Stack](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack)\n    ships both, or [Redis 8](https://redis.io/docs/latest/develop/ai/search-and-query) with the\n    Search and JSON modules enabled.\n\n4.  Pre-fetch the embedding model (optional). The demo will lazy-download the\n    [`Xenova/all-MiniLM-L6-v2`](https://huggingface.co/Xenova/all-MiniLM-L6-v2) ONNX\n    weights (around 80 MB) on the first request, but the wait is more obvious when\n    it lands inside the first turn rather than at startup:\n\n    [code example]\n\n5.  Start the demo server:\n\n    [code example]\n\n6.  Open <http://localhost:8090> and try some turns:\n\n    *  **\"Remind me which theme I prefer in editors.\"** — paraphrase of a seeded\n       semantic memory (\"The user dislikes dark mode and prefers a high-contrast\n       light theme...\"). You should see that memory recalled with a cosine\n       distance around 0.47, comfortably under the 0.55 default recall\n       threshold.\n    *  **\"What did we discuss about the order-routing outage?\"** — paraphrase of\n       a seeded episodic memory; the postmortem memory should recall around\n       0.44. Switch the **Kind to write** dropdown to `skip` so the question\n       itself doesn't enter long-term memory.\n    *  **\"I prefer concise answers without filler phrases.\"** — paraphrase of\n       a seeded *semantic* memory. Switch the **Kind to write** dropdown to\n       `semantic` so the dedup KNN runs in the same kind as the seed (dedup\n       is scoped per kind, on purpose, so an episodic write can't collapse\n       onto a semantic memory). You should then see the write **deduped**\n       onto the existing memory at a cosine distance around 0.16 (the ONNX\n       Runtime FFI bindings TransformersPHP uses sit a hair behind the\n       PyTorch arithmetic the Python demo runs, so paraphrase distances are\n       a couple of hundredths higher than in the Python walkthrough), with\n       `hit_count` ticking up in the memories table.\n    *  **\"My favorite color is teal.\"** — unrelated to any seed; nothing recalls\n       above the threshold (every seed lands above 0.8), and the new memory is\n       written as `episodic` (or `semantic`, depending on the dropdown) under a\n       fresh id.\n    *  Switch the **User** field to `bob` and re-ask any of the above — recall\n       returns nothing because the seed memories live under `default`. That's\n       the TAG pre-filter at work inside [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search).\n    *  Slide the **Recall threshold** down to 0.30 to see borderline paraphrases\n       drop out of the recall set, then back up to 0.70 to watch them return.\n\n    `Xenova/all-MiniLM-L6-v2` puts a faithful paraphrase in the 0.15 – 0.50\n    cosine-distance range, a loose paraphrase or related topic in the 0.50 – 0.80\n    range, and unrelated queries above 0.8 — which is what motivates the 0.55\n    default recall threshold and the 0.20 default dedup threshold. A stricter\n    embedding model (or a domain-tuned one) would let you tighten both; a\n    noisier one would push them up. The right thresholds are always a function\n    of the model, the corpus, and how conservative the agent needs to be about\n    accepting a memory as a match.\n\nThe server is read/write against your local Redis. The default memory index is `agentmem:idx`, JSON keys live under `agent:mem:`, session Hashes under `agent:session:`, and event Streams under `agent:events:`. Useful flags:\n\n* `--host` — HTTP bind host (default `127.0.0.1`).\n* `--port` — HTTP bind port (default `8090`).\n* `--redis-host`, `--redis-port` — point the demo at a non-local Redis.\n* `--mem-index-name`, `--mem-key-prefix`, `--session-key-prefix`, `--event-key-prefix` — change the default key namespacing.\n* `--no-reset` — keep the existing long-term memories across restarts instead of dropping and re-seeding.\n* `--session-ttl-seconds` — change the working-memory TTL (default 3600).\n* `--dedup-threshold` — change the cosine-distance cutoff for write-time deduplication.\n* `--recall-threshold` — change the default cosine-distance cutoff for recall."}],"examples":[{"id":"the-session-store-ex0","language":"php","code":"<?php\n\nuse Predis\\Client;\nuse Redis\\AgentMemory\\AgentSession;\n\n$client = new Client(['scheme' => 'tcp', 'host' => 'localhost', 'port' => 6379]);\n$session = new AgentSession(\n    client: $client,\n    keyPrefix: 'agent:session:',\n    defaultTtlSeconds: 3600,  // one hour\n    maxTurns: 20,             // rolling window per thread\n);\n\n$threadId = $session->newThreadId();\n$session->start(\n    $threadId,\n    user: 'alice',\n    agent: 'demo-agent',\n    goal: \"Plan next week's meetings.\",\n);\n$session->appendTurn(\n    $threadId,\n    role: 'user',\n    content: 'Schedule a budget review with finance.',\n);\n$state = $session->load($threadId);\necho $state['turn_count'] . ' '\n    . count($state['recent_turns']) . ' '\n    . $state['ttl_seconds'] . \"\\n\";","section_id":"the-session-store"},{"id":"the-session-store-ex1","language":"text","code":"agent:session:9f3d2a4b8c61\n  thread_id=9f3d2a4b8c61\n  user=alice\n  agent=demo-agent\n  goal=Plan next week's meetings.\n  scratchpad=Need to confirm finance's availability.\n  turn_count=4\n  created_ts=1715990400.12\n  last_active_ts=1715990650.83\n  recent_turns=[{\"role\":\"user\",\"content\":\"...\",\"ts\":...}, ...]","section_id":"the-session-store"},{"id":"the-long-term-memory-store-ex0","language":"php","code":"<?php\n\nuse Redis\\AgentMemory\\Embedder;\nuse Redis\\AgentMemory\\LongTermMemory;\n\n$memory = new LongTermMemory(\n    client: $client,\n    indexName: 'agentmem:idx',\n    keyPrefix: 'agent:mem:',\n    dedupThreshold: 0.20,   // cosine distance — tight at write time\n    recallThreshold: 0.55,  // looser at read time\n);\n$embedder = new Embedder();\n$memory->createIndex();  // idempotent\n\n// Write a memory. The same KNN that powers recall also runs here at\n// a tighter threshold so paraphrases of the same fact collapse.\n$vec = $embedder->encodeOne('The user prefers light mode in editors.');\n$result = $memory->remember(\n    text: 'The user prefers light mode in editors.',\n    embedding: $vec,\n    user: 'alice',\n    namespace: 'default',\n    kind: 'semantic',\n    sourceThread: '9f3d2a4b8c61',\n);\nprintf(\"deduped=%s id=%s existing_distance=%s\\n\",\n    $result['deduped'] ? 'true' : 'false',\n    $result['id'],\n    var_export($result['existing_distance'], true));\n\n// Recall against a later question.\n$q = $embedder->encodeOne('Which theme does this user like?');\n$hits = $memory->recall(\n    queryEmbedding: $q,\n    user: 'alice',\n    namespace: 'default',\n    k: 5,\n);\nforeach ($hits as $h) {\n    printf(\"%.3f [%s] %s\\n\", $h['distance'], $h['kind'], $h['text']);\n}","section_id":"the-long-term-memory-store"},{"id":"data-model-ex0","language":"json","code":"agent:mem:7c3f8a1b9e02\n{\n  \"id\": \"7c3f8a1b9e02\",\n  \"user\": \"alice\",\n  \"namespace\": \"default\",\n  \"kind\": \"semantic\",\n  \"source_thread\": \"9f3d2a4b8c61\",\n  \"text\": \"The user prefers light mode in editors.\",\n  \"embedding\": [0.013, -0.041, ...],\n  \"created_ts\": 1715990400.12,\n  \"hit_count\": 0\n}","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"FT.CREATE agentmem:idx\n  ON JSON PREFIX 1 agent:mem:\n  SCHEMA\n    $.text          AS text          TEXT\n    $.user          AS user          TAG\n    $.namespace     AS namespace     TAG\n    $.kind          AS kind          TAG\n    $.source_thread AS source_thread TAG\n    $.created_ts    AS created_ts    NUMERIC SORTABLE\n    $.hit_count     AS hit_count     NUMERIC SORTABLE\n    $.embedding     AS embedding     VECTOR HNSW 6\n                                       TYPE FLOAT32 DIM 384\n                                       DISTANCE_METRIC COSINE","section_id":"data-model"},{"id":"the-query-ex0","language":"text","code":"FT.SEARCH agentmem:idx\n  \"(@user:{alice} @namespace:{default} @kind:{semantic})\n     =>[KNN 5 @embedding $vec AS distance]\"\n  PARAMS 2 vec <384-float32-bytes>\n  SORTBY distance\n  RETURN 8 user namespace kind source_thread text created_ts hit_count distance\n  DIALECT 2","section_id":"the-query"},{"id":"the-event-log-ex0","language":"php","code":"<?php\n\nuse Redis\\AgentMemory\\AgentEventLog;\n\n$events = new AgentEventLog(client: $client, maxLen: 1000);\n$events->record(\n    $threadId, 'turn_appended:user',\n    'Schedule a budget review with finance.',\n);\n$events->record(\n    $threadId, 'memory_written',\n    'wrote 7c3f8a1b9e02 as semantic',\n);\n\nforeach ($events->recent($threadId, 20) as $event) {\n    echo $event['action'] . ' ' . $event['detail'] . \"\\n\";\n}","section_id":"the-event-log"},{"id":"pre-seeding-long-term-memory-ex0","language":"php","code":"<?php\n\nuse Redis\\AgentMemory\\Embedder;\nuse Redis\\AgentMemory\\LongTermMemory;\nuse Redis\\AgentMemory\\SeedMemory;\n\n$memory = new LongTermMemory(client: $client);\n$embedder = new Embedder();\n$memory->createIndex();\nSeedMemory::seed($memory, $embedder, user: 'default', namespace: 'default');","section_id":"pre-seeding-long-term-memory"},{"id":"run-the-demo-locally-ex0","language":"bash","code":"git clone https://github.com/redis/docs.git\n    cd docs/content/develop/use-cases/memory-layer/php","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex1","language":"bash","code":"composer install","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex2","language":"bash","code":"composer run download-model","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex3","language":"bash","code":"php demo_server.php","section_id":"run-the-demo-locally"}]}
{"schema_version":2,"id":"develop/use-cases/memory-layer/redis-py","title":"Redis memory layer with redis-py","url":"https://redis.io/docs/latest/develop/use-cases/memory-layer/redis-py/","summary":"Build a Redis-backed memory layer in Python with redis-py, sentence-transformers, and standard Redis commands — working memory in a Hash, long-term semantic recall as JSON with a vector index, and an event log in a Stream.","aliases":["/develop/use-cases/agent-memory/redis-py","/develop/use-cases/agent-memory/python"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:54:57+01:00","children":[],"page_type":"content","content_hash":"81a63cd2ae32415b7f4b840f7784e012ed9f83f4d13c9f140f6cf2ce0bc1d7ac","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a small Redis-backed memory layer in Python with [`redis-py`](https://redis.io/docs/latest/develop/clients/redis-py) and the [`sentence-transformers`](https://www.sbert.net/) library, using only standard Redis commands — no Redis Agent Memory SDK, no managed service. It includes a local web server built with the Python standard library so you can send turns at the agent, watch working memory update in place, see semantically similar long-term memories recalled in real time, watch the write-time deduplication skip near-duplicates, and inspect the per-thread event log.\n\nWhen you are ready for production, [Redis Agent Memory](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory) is the fastest way to get a production-ready memory layer running — as a managed service on Redis Cloud, or self-managed on-prem."},{"id":"overview","title":"Overview","role":"overview","text":"The memory layer splits across three Redis primitives, each handling one tier:\n\n* **Working memory** for the active session is a [Hash](https://redis.io/docs/latest/develop/data-types/hashes) at `agent:session:<thread_id>` holding the goal, scratchpad, a rolling window of recent turns (as a JSON list inside one field), and a few audit timestamps. One [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) returns the whole session in a single round trip; every write refreshes the key's [`EXPIRE`](https://redis.io/docs/latest/commands/expire) so idle sessions decay on their own.\n* **Long-term memory** is a set of [JSON](https://redis.io/docs/latest/develop/data-types/json) documents at `agent:mem:<id>`, each carrying the memory text, a 384-dimensional embedding vector, and tag fields for user, namespace, kind (episodic / semantic), and source thread. A single [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) index covers the [HNSW vector field](https://redis.io/docs/latest/develop/ai/search-and-query/vectors) and every metadata field, so one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call performs the KNN with the metadata pre-filter in the same round trip. Write-time deduplication runs the same KNN at insert time and skips a new memory whose nearest existing entry is within a tighter threshold.\n* **Event log** for the agent's actions and observations is a [Stream](https://redis.io/docs/latest/develop/data-types/streams) at `agent:events:<thread_id>`, appended with [`XADD MAXLEN ~`](https://redis.io/docs/latest/commands/xadd) so retention stays bounded automatically, replayed with [`XREVRANGE`](https://redis.io/docs/latest/commands/xrevrange).\n\nThat gives you:\n\n* A single round trip per tier: one [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) for the session, one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) for recall, one [`XADD`](https://redis.io/docs/latest/commands/xadd) for the event log.\n* Sub-millisecond reads on every step of the agent loop, so the memory layer doesn't dominate the per-step latency.\n* Per-tier decay: short TTLs on working memory, longer on episodic memories, no TTL on semantic memories. Combined with a database-level [eviction policy](https://redis.io/docs/latest/develop/reference/eviction) (LFU is the common choice), memory stays bounded under pressure.\n* Scoping enforced inside the query: a recall query for `user=alice` will never see `user=bob`'s memories, because the TAG filter goes into the same [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call as the KNN."},{"id":"how-it-works","title":"How it works","role":"content","text":"Each turn through the agent loop touches all three tiers in one pass: append to working memory, recall similar long-term memories, write the turn back as a new memory (with deduplication), and append one event to the log."},{"id":"per-turn-flow","title":"Per-turn flow","role":"content","text":"1. The application calls `embedder.encode_one(text)` to turn the incoming turn into a 384-dimensional `float32` vector.\n2. `session.append_turn(thread_id, role, content)` reads the per-thread Hash with [`HGETALL`](https://redis.io/docs/latest/commands/hgetall), appends the new turn to the rolling window in application code, trims it back to the configured maximum, and writes the Hash back with an [`HSET`](https://redis.io/docs/latest/commands/hset) + [`EXPIRE`](https://redis.io/docs/latest/commands/expire) pipeline. The session TTL refreshes on every write so an active thread stays alive.\n3. `memory.recall(vec, user=..., namespace=..., k=5)` runs [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) with a TAG pre-filter and a `KNN 5` clause. Redis returns the closest matching memories together with their cosine distances; memories beyond the recall threshold are dropped before they reach the agent so an unrelated query doesn't surface confident-looking false positives.\n4. `memory.remember(text, vec, user=..., namespace=..., kind=...)` runs the same KNN with a tighter dedup threshold. If an existing memory is within the threshold, the new write is skipped and the existing memory's `hit_count` is incremented with [`JSON.NUMINCRBY`](https://redis.io/docs/latest/commands/json.numincrby); otherwise a fresh JSON document is written with [`JSON.SET`](https://redis.io/docs/latest/commands/json.set) and a per-kind [`EXPIRE`](https://redis.io/docs/latest/commands/expire) — `episodic` defaults to seven days, `semantic` has no TTL by default.\n5. `event_log.record(thread_id, action, detail)` appends one entry to the per-thread Stream with [`XADD MAXLEN ~`](https://redis.io/docs/latest/commands/xadd), bounding retention to roughly a thousand entries per thread without an explicit cleanup job.\n\nThe embedding is computed once and reused for steps 3 and 4 — there's no point encoding the same text twice. Recall runs before the write, so the agent doesn't see its own just-written turn echoed back as a recalled memory."},{"id":"the-session-store","title":"The session store","role":"content","text":"`AgentSession` wraps the working-memory Hash and the rolling turn window ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/memory-layer/redis-py/session_store.py)):\n\n[code example]\n\nThe data model is one Hash per thread. The rolling turn window is stored as a JSON string in a single field so the whole session loads in one [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) — the hash never grows in size or field count as the conversation goes on.\n\n[code example]\n\nEvery write — `start`, `append_turn`, `set_scratchpad` — runs the [`HSET`](https://redis.io/docs/latest/commands/hset) and [`EXPIRE`](https://redis.io/docs/latest/commands/expire) inside a `MULTI` / `EXEC` block, so a connection drop between the two writes can't leave the session without a TTL."},{"id":"the-long-term-memory-store","title":"The long-term memory store","role":"content","text":"`LongTermMemory` owns the JSON documents, the vector index, the recall query, and the write-time deduplication ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/memory-layer/redis-py/long_term_memory.py)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each memory is a JSON document at `agent:mem:<id>`. The embedding is a JSON array of floats so the document is human-readable from `redis-cli`; [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) still expects the *query* vector as raw `float32` bytes, regardless of how the indexed document stores it.\n\n[code example]\n\nThe Redis Search index is declared on the JSON document type with `as_name` aliases so the query syntax stays compact:\n\n[code example]"},{"id":"the-query","title":"The query","role":"content","text":"Both recall and dedup are the same hybrid query: a TAG pre-filter in parentheses followed by `=>[KNN k @embedding $vec]`. With `DIALECT 2`, Redis applies the filter first and KNN-ranks only the matching documents.\n\n[code example]\n\n`distance` is the cosine *distance* (0 means identical, 2 means opposite). Recall and dedup share the same query shape; only the threshold differs — strict at write time so the index doesn't fill with paraphrases of the same fact, looser at read time so the agent gets a wider net of relevant memories."},{"id":"per-kind-ttls","title":"Per-kind TTLs","role":"content","text":"`remember` resolves the entry's TTL from the memory's `kind`:\n\n| Kind      | Default TTL | When to use it                                              |\n|-----------|-------------|-------------------------------------------------------------|\n| `episodic` | 7 days     | Snapshots from a specific session that should decay.        |\n| `semantic` | none       | Distilled facts and preferences the agent carries forward.  |\n\nYou can override per write with `ttl_seconds=...` on `remember`, or pass a different `ttl_by_kind={...}` map to the `LongTermMemory` constructor — for example, to give semantic memories a six-month TTL while leaving episodic memories at seven days."},{"id":"the-event-log","title":"The event log","role":"content","text":"`AgentEventLog` is a thin wrapper over a per-thread Redis Stream ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/memory-layer/redis-py/event_log.py)):\n\n[code example]\n\n`record` calls [`XADD`](https://redis.io/docs/latest/commands/xadd) with `maxlen=~1000`. The tilde lets Redis trim in whole-node units instead of exactly-N units, which is much cheaper at the cost of overshooting the bound by up to a node's worth — the right tradeoff for an audit log where exact length doesn't matter.\n\nThe Stream is independent of the session Hash and the long-term JSON documents: it answers \"what just happened\" without competing with either of those for indexing or memory budget. Consumer groups (not used in this demo) would let downstream workers — summarizers, consolidators, audit pipelines — replay the log without losing position."},{"id":"concurrency-caveats","title":"Concurrency caveats","role":"content","text":"The three helpers above trade correctness under heavy concurrency for clarity. Each is fine on a single-process demo, but lifting the code into a real multi-worker agent surfaces three races worth knowing about:\n\n* **Working memory is read-modify-write.** `AgentSession.append_turn` calls [`HGETALL`](https://redis.io/docs/latest/commands/hgetall), mutates the `recent_turns` list in application code, and writes the Hash back with [`HSET`](https://redis.io/docs/latest/commands/hset). Two concurrent turns on the same thread can both read the same `recent_turns`, append different entries, and write back — last writer wins, the other turn is silently lost. The robust fix is either a [`WATCH`](https://redis.io/docs/latest/commands/watch) / [`MULTI`](https://redis.io/docs/latest/commands/multi) / [`EXEC`](https://redis.io/docs/latest/commands/exec) loop around the read-modify-write or a small [Lua script](https://redis.io/docs/latest/commands/eval) that does the append atomically server-side.\n\n* **Long-term dedup is not atomic.** `LongTermMemory.remember` runs a [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) KNN lookup, decides whether the candidate is a duplicate, and (if not) calls [`JSON.SET`](https://redis.io/docs/latest/commands/json.set). Two workers seeing the same fact in flight can each fail to see the other's not-yet-committed write and both insert a new memory. The pragmatic fix is to accept that the index will occasionally hold near-duplicates and run a background consolidator that periodically scans for memory pairs within a tight distance and merges them, rather than trying to make the write itself atomic.\n\n* **The active thread is server state.** The demo server keeps a single `current_thread_id` that `/new_thread` and `/reset` mutate under a lock; `handle_turn` reads it outside that lock, so a turn racing with a thread rotation can apply to the previous thread. This is cosmetic for a one-user browser demo. A multi-user agent would carry the thread id on the request itself rather than as shared server state.\n\nThose caveats are deliberate. A more conservative implementation would obscure the Redis-shaped parts of the pattern; the demo prioritizes a small, readable code path that maps directly onto the commands in the prose above."},{"id":"pre-seeding-long-term-memory","title":"Pre-seeding long-term memory","role":"content","text":"In a real deployment the memory store fills up organically as the agent reasons over user turns: each turn produces zero or more memories that flow into the store, with deduplication catching repeats. For the demo, `seed_memory.py` pre-loads a small set of mixed semantic and episodic memories so the very first recall query returns something useful ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/memory-layer/redis-py/seed_memory.py)):\n\n[code example]\n\nThe seed list mixes long-lived facts and preferences (`semantic`) with snapshots of past sessions (`episodic`), so the **Kind to write** control in the demo has something to switch between when a new turn is being remembered."},{"id":"the-interactive-demo","title":"The interactive demo","role":"content","text":"`demo_server.py` runs a [`ThreadingHTTPServer`](https://docs.python.org/3/library/http.server.html) on port 8086. The HTML page exposes three live panels — working memory, recalled memories, event log — plus a memories table for admin actions. Endpoints:\n\n| Endpoint            | What it does                                                                    |\n|---------------------|---------------------------------------------------------------------------------|\n| `GET  /state`       | Index info, current session, in-scope long-term memories, and recent events.    |\n| `POST /turn`        | Embed the text, append to working memory, recall similar memories, optionally write a new memory (with dedup), append an event. |\n| `POST /new_thread`  | Start a fresh thread; long-term memory and other threads are untouched.         |\n| `POST /reset`       | Drop every long-term memory and re-seed the sample set.                         |\n| `POST /drop_memory` | Delete a single long-term memory by id.                                         |\n\nThe server holds one `LocalEmbedder`, one `AgentSession`, one `LongTermMemory`, and one `AgentEventLog` for the lifetime of the process. The \"current thread\" is a class attribute that the **New thread** button rotates — every browser session inherits the same thread until you explicitly start a new one."},{"id":"run-the-demo-locally","title":"Run the demo locally","role":"content","text":"1.  Clone the [`redis/docs`](https://github.com/redis/docs) repository and change into the example\n    directory:\n\n    [code example]\n\n2.  Install the dependencies:\n\n    [code example]\n\n3.  Make sure a Redis instance with Redis Search and Redis JSON is running locally on\n    port 6379. [Redis Stack](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack) ships both,\n    or [Redis 8](https://redis.io/docs/latest/develop/ai/search-and-query) with the Search and JSON modules\n    enabled.\n\n4.  Start the demo server. The first run downloads the `all-MiniLM-L6-v2` model\n    (~80 MB) into the local Hugging Face cache:\n\n    [code example]\n\n5.  Open <http://localhost:8086> and try some turns:\n\n    *  **\"Remind me which theme I prefer in editors.\"** — paraphrase of a seeded\n       semantic memory (\"The user dislikes dark mode and prefers a high-contrast\n       light theme...\"). You should see that memory recalled with a cosine\n       distance around 0.47, comfortably under the 0.55 default recall\n       threshold.\n    *  **\"What did we discuss about the order-routing outage?\"** — paraphrase of\n       a seeded episodic memory; the postmortem memory should recall around\n       0.44. Switch the **Kind to write** dropdown to `skip` so the question\n       itself doesn't enter long-term memory.\n    *  **\"I prefer concise answers without filler phrases.\"** — paraphrase of\n       a seeded *semantic* memory. Switch the **Kind to write** dropdown to\n       `semantic` so the dedup KNN runs in the same kind as the seed (dedup\n       is scoped per kind, on purpose, so an episodic write can't collapse\n       onto a semantic memory). You should then see the write **deduped**\n       onto the existing memory at a cosine distance around 0.15, with\n       `hit_count` ticking up in the memories table.\n    *  **\"My favorite color is teal.\"** — unrelated to any seed; nothing\n       recalls above the threshold (every seed lands above 0.8), and the new\n       memory is written as `episodic` (or `semantic`, depending on the\n       dropdown) under a fresh id.\n    *  Switch the **User** field to `bob` and re-ask any of the above — recall\n       returns nothing because the seed memories live under `default`. That's\n       the TAG pre-filter at work inside [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search).\n    *  Slide the **Recall threshold** down to 0.30 to see borderline paraphrases\n       drop out of the recall set, then back up to 0.70 to watch them return.\n\n    `all-MiniLM-L6-v2` puts a faithful paraphrase in the 0.15 – 0.50\n    cosine-distance range, a loose paraphrase or related topic in the\n    0.50 – 0.80 range, and unrelated queries above 0.8 — which is what\n    motivates the 0.55 default recall threshold and the 0.20 default\n    dedup threshold. A stricter embedding model (or a domain-tuned one)\n    would let you tighten both; a noisier one would push them up. The\n    right thresholds are always a function of the model, the corpus,\n    and how conservative the agent needs to be about accepting a memory\n    as a match.\n\nThe server is read/write against your local Redis. The default memory index is `agentmem:idx`, JSON keys live under `agent:mem:`, session Hashes under `agent:session:`, and event Streams under `agent:events:`. Useful flags:\n\n* `--no-reset` — keep the existing long-term memories across restarts instead of dropping and re-seeding.\n* `--session-ttl-seconds` — change the working-memory TTL (default 3600).\n* `--dedup-threshold` — change the cosine-distance cutoff for write-time deduplication.\n* `--recall-threshold` — change the default cosine-distance cutoff for recall."}],"examples":[{"id":"the-session-store-ex0","language":"python","code":"import redis\nfrom session_store import AgentSession\n\nr = redis.Redis(host=\"localhost\", port=6379, decode_responses=False)\nsession = AgentSession(\n    redis_client=r,\n    key_prefix=\"agent:session:\",\n    default_ttl_seconds=3600,  # one hour\n    max_turns=20,              # rolling window per thread\n)\n\nthread_id = session.new_thread_id()\nsession.start(thread_id, user=\"alice\", agent=\"demo-agent\",\n              goal=\"Plan next week's meetings.\")\nsession.append_turn(thread_id, role=\"user\",\n                    content=\"Schedule a budget review with finance.\")\nstate = session.load(thread_id)\nprint(state.turn_count, len(state.recent_turns), state.ttl_seconds)","section_id":"the-session-store"},{"id":"the-session-store-ex1","language":"text","code":"agent:session:9f3d2a4b8c61\n  thread_id=9f3d2a4b8c61\n  user=alice\n  agent=demo-agent\n  goal=Plan next week's meetings.\n  scratchpad=Need to confirm finance's availability.\n  turn_count=4\n  created_ts=1715990400.12\n  last_active_ts=1715990650.83\n  recent_turns=[{\"role\":\"user\",\"content\":\"...\",\"ts\":...}, ...]","section_id":"the-session-store"},{"id":"the-long-term-memory-store-ex0","language":"python","code":"import numpy as np\nfrom long_term_memory import LongTermMemory\nfrom embeddings import LocalEmbedder\n\nmemory = LongTermMemory(\n    redis_client=r,\n    index_name=\"agentmem:idx\",\n    key_prefix=\"agent:mem:\",\n    dedup_threshold=0.20,   # cosine distance — tight at write time\n    recall_threshold=0.55,  # looser at read time\n)\nembedder = LocalEmbedder()\nmemory.create_index()  # idempotent\n\n# Write a memory. The same KNN that powers recall also runs here\n# at a tighter threshold so paraphrases of the same fact collapse.\nvec = embedder.encode_one(\"The user prefers light mode in editors.\")\nresult = memory.remember(\n    text=\"The user prefers light mode in editors.\",\n    embedding=np.asarray(vec, dtype=np.float32),\n    user=\"alice\",\n    namespace=\"default\",\n    kind=\"semantic\",\n    source_thread=\"9f3d2a4b8c61\",\n)\nprint(result.deduped, result.id, result.existing_distance)\n\n# Recall against a later question.\nq = embedder.encode_one(\"Which theme does this user like?\")\nhits = memory.recall(\n    query_embedding=np.asarray(q, dtype=np.float32),\n    user=\"alice\",\n    namespace=\"default\",\n    k=5,\n)\nfor h in hits:\n    print(f\"{h.distance:.3f} [{h.kind}] {h.text}\")","section_id":"the-long-term-memory-store"},{"id":"data-model-ex0","language":"json","code":"agent:mem:7c3f8a1b9e02\n{\n  \"id\": \"7c3f8a1b9e02\",\n  \"user\": \"alice\",\n  \"namespace\": \"default\",\n  \"kind\": \"semantic\",\n  \"source_thread\": \"9f3d2a4b8c61\",\n  \"text\": \"The user prefers light mode in editors.\",\n  \"embedding\": [0.013, -0.041, ...],\n  \"created_ts\": 1715990400.12,\n  \"hit_count\": 0\n}","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"FT.CREATE agentmem:idx\n  ON JSON PREFIX 1 agent:mem:\n  SCHEMA\n    $.text          AS text          TEXT\n    $.user          AS user          TAG\n    $.namespace     AS namespace     TAG\n    $.kind          AS kind          TAG\n    $.source_thread AS source_thread TAG\n    $.created_ts    AS created_ts    NUMERIC SORTABLE\n    $.hit_count     AS hit_count     NUMERIC SORTABLE\n    $.embedding     AS embedding     VECTOR HNSW 6\n                                       TYPE FLOAT32 DIM 384\n                                       DISTANCE_METRIC COSINE","section_id":"data-model"},{"id":"the-query-ex0","language":"text","code":"FT.SEARCH agentmem:idx\n  \"(@user:{alice} @namespace:{default} @kind:{semantic})\n     =>[KNN 5 @embedding $vec AS distance]\"\n  PARAMS 2 vec <384-float32-bytes>\n  SORTBY distance\n  RETURN 8 user namespace kind source_thread text created_ts hit_count distance\n  DIALECT 2","section_id":"the-query"},{"id":"the-event-log-ex0","language":"python","code":"from event_log import AgentEventLog\n\nevents = AgentEventLog(redis_client=r, max_len=1000)\nevents.record(thread_id, action=\"turn_appended:user\",\n              detail=\"Schedule a budget review with finance.\")\nevents.record(thread_id, action=\"memory_written\",\n              detail=\"wrote 7c3f8a1b9e02 as semantic\")\n\nfor event in events.recent(thread_id, count=20):\n    print(event.action, event.detail)","section_id":"the-event-log"},{"id":"pre-seeding-long-term-memory-ex0","language":"python","code":"from seed_memory import seed\nfrom long_term_memory import LongTermMemory\nfrom embeddings import LocalEmbedder\n\nmemory = LongTermMemory()\nembedder = LocalEmbedder()\nmemory.create_index()\nseed(memory, embedder, user=\"default\", namespace=\"default\")","section_id":"pre-seeding-long-term-memory"},{"id":"run-the-demo-locally-ex0","language":"bash","code":"git clone https://github.com/redis/docs.git\n    cd docs/content/develop/use-cases/memory-layer/redis-py","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex1","language":"bash","code":"pip install redis sentence-transformers numpy","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex2","language":"bash","code":"python demo_server.py","section_id":"run-the-demo-locally"}]}
{"schema_version":2,"id":"develop/use-cases/memory-layer/ruby","title":"Redis memory layer with redis-rb","url":"https://redis.io/docs/latest/develop/use-cases/memory-layer/ruby/","summary":"Build a Redis-backed memory layer in Ruby with redis-rb, informers, and standard Redis commands — working memory in a Hash, long-term semantic recall as JSON with a vector index, and an event log in a Stream.","aliases":["/develop/use-cases/agent-memory/ruby","/develop/use-cases/agent-memory/redis-rb"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:54:57+01:00","children":[],"page_type":"content","content_hash":"defeb388cddf62d8515c31a9b019a31aaf3d613ac95bd0bfafa35e95a0228a12","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a small Redis-backed memory layer in Ruby with [`redis-rb`](https://redis.io/docs/latest/develop/clients/ruby) and the [`informers`](https://github.com/ankane/informers) gem, using only standard Redis commands — no Redis Agent Memory SDK, no managed service. It includes a local web server built with the standard-library [`WEBrick`](https://github.com/ruby/webrick) HTTP server so you can send turns at the agent, watch working memory update in place, see semantically similar long-term memories recalled in real time, watch the write-time deduplication skip near-duplicates, and inspect the per-thread event log.\n\nThe embedder is [`informers`](https://github.com/ankane/informers), Ankane's Ruby port of Hugging Face transformers, running the ONNX-exported [`sentence-transformers/all-MiniLM-L6-v2`](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2) encoder through the `onnxruntime` gem — the same 384-d model the [Python example](https://redis.io/docs/latest/develop/use-cases/memory-layer/redis-py) and every other port use. The Ruby ONNX path produces vectors that match the Python PyTorch reference closely enough that paraphrase distances land on the same numbers down to the fourth decimal place; a memory written by one demo can be recalled by the other against the same Redis instance, and the distance bands the Python walkthrough quotes carry over to this one without recalibration."},{"id":"overview","title":"Overview","role":"overview","text":"The memory layer splits across three Redis primitives, each handling one tier:\n\n* **Working memory** for the active session is a [Hash](https://redis.io/docs/latest/develop/data-types/hashes) at `agent:session:<thread_id>` holding the goal, scratchpad, a rolling window of recent turns (as a JSON list inside one field), and a few audit timestamps. One [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) returns the whole session in a single round trip; every write refreshes the key's [`EXPIRE`](https://redis.io/docs/latest/commands/expire) so idle sessions decay on their own.\n* **Long-term memory** is a set of [JSON](https://redis.io/docs/latest/develop/data-types/json) documents at `agent:mem:<id>`, each carrying the memory text, a 384-dimensional embedding vector, and tag fields for user, namespace, kind (episodic / semantic), and source thread. A single [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) index covers the [HNSW vector field](https://redis.io/docs/latest/develop/ai/search-and-query/vectors) and every metadata field, so one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call performs the KNN with the metadata pre-filter in the same round trip. Write-time deduplication runs the same KNN at insert time and skips a new memory whose nearest existing entry is within a tighter threshold.\n* **Event log** for the agent's actions and observations is a [Stream](https://redis.io/docs/latest/develop/data-types/streams) at `agent:events:<thread_id>`, appended with [`XADD MAXLEN ~`](https://redis.io/docs/latest/commands/xadd) so retention stays bounded automatically, replayed with [`XREVRANGE`](https://redis.io/docs/latest/commands/xrevrange).\n\nThat gives you:\n\n* A single round trip per tier: one [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) for the session, one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) for recall, one [`XADD`](https://redis.io/docs/latest/commands/xadd) for the event log. ([`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) itself is one round trip; the helper also issues one [`TTL`](https://redis.io/docs/latest/commands/ttl) call per returned row to populate `ttl_seconds` for the admin panel.)\n* Sub-millisecond reads on every step of the agent loop, so the memory layer doesn't dominate per-step latency.\n* Per-tier decay: short TTLs on working memory, longer on episodic memories, no TTL on semantic memories. Combined with a database-level [eviction policy](https://redis.io/docs/latest/develop/reference/eviction) (LFU is the common choice), memory stays bounded under pressure.\n* Scoping enforced inside the query: a recall query for `user=alice` will never see `user=bob`'s memories, because the TAG filter goes into the same [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call as the KNN."},{"id":"how-it-works","title":"How it works","role":"content","text":"Each turn through the agent loop touches all three tiers in one pass: append to working memory, recall similar long-term memories, write the turn back as a new memory (with deduplication), and append one event to the log."},{"id":"per-turn-flow","title":"Per-turn flow","role":"content","text":"1. The application calls `embedder.encode_one(text)` to turn the incoming turn into a 384-element `Array<Float>`. `informers` runs L2 normalization inside the ONNX graph itself, so the returned vector is already a unit vector.\n2. `session.append_turn(thread_id, role:, content:)` reads the per-thread Hash with [`HGETALL`](https://redis.io/docs/latest/commands/hgetall), appends the new turn to the rolling window in application code, trims it back to the configured maximum, and writes the Hash back with [`HSET`](https://redis.io/docs/latest/commands/hset) + [`EXPIRE`](https://redis.io/docs/latest/commands/expire) inside a [`MULTI/EXEC`](https://redis.io/docs/latest/commands/multi). The session TTL refreshes on every write so an active thread stays alive.\n3. `memory.recall(query_vec, user:, namespace:, k: 5)` runs [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) with a TAG pre-filter and a `KNN 5` clause. Redis returns the closest matching memories together with their cosine distances; memories beyond the recall threshold are dropped before they reach the agent so an unrelated query doesn't surface confident-looking false positives.\n4. `memory.remember(text:, embedding:, user:, namespace:, kind:)` runs the same KNN with a tighter dedup threshold. If an existing memory is within the threshold, the new write is skipped and the existing memory's `hit_count` is incremented with [`JSON.NUMINCRBY`](https://redis.io/docs/latest/commands/json.numincrby); otherwise a fresh JSON document is written with [`JSON.SET`](https://redis.io/docs/latest/commands/json.set) and a per-kind [`EXPIRE`](https://redis.io/docs/latest/commands/expire) — `episodic` defaults to seven days, `semantic` has no TTL by default.\n5. `event_log.record(thread_id, action, detail)` appends one entry to the per-thread Stream with [`XADD MAXLEN ~`](https://redis.io/docs/latest/commands/xadd), bounding retention to roughly a thousand entries per thread without an explicit cleanup job.\n\nThe embedding is computed once and reused for steps 3 and 4 — there's no point encoding the same text twice. Recall runs before the write, so the agent doesn't see its own just-written turn echoed back as a recalled memory."},{"id":"the-session-store","title":"The session store","role":"content","text":"`AgentSession` wraps the working-memory Hash and the rolling turn window ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/memory-layer/ruby/lib/session_store.rb)):\n\n[code example]\n\nThe data model is one Hash per thread. The rolling turn window is stored as a JSON string in a single field so the whole session loads in one [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) — the hash never grows in size or field count as the conversation goes on.\n\n[code example]\n\nEvery write — `start`, `append_turn`, `set_scratchpad` — runs the [`HSET`](https://redis.io/docs/latest/commands/hset) and [`EXPIRE`](https://redis.io/docs/latest/commands/expire) inside a [`MULTI`](https://redis.io/docs/latest/commands/multi) / [`EXEC`](https://redis.io/docs/latest/commands/exec) so a connection drop between the two writes can't leave the session without a TTL."},{"id":"the-long-term-memory-store","title":"The long-term memory store","role":"content","text":"`LongTermMemory` owns the JSON documents, the vector index, the recall query, and the write-time deduplication ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/memory-layer/ruby/lib/long_term_memory.rb)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each memory is a JSON document at `agent:mem:<id>`. The embedding is a JSON array of floats so the document is human-readable from `redis-cli`; [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) still expects the *query* vector as raw `float32` bytes (the demo packs them with Ruby's [`Array#pack('e*')`](https://docs.ruby-lang.org/en/master/Array.html#method-i-pack), which emits little-endian float32), regardless of how the indexed document stores it.\n\n[code example]\n\nThe Redis Search index is declared on the JSON document type with `AS` aliases so the query syntax stays compact:\n\n[code example]"},{"id":"the-query","title":"The query","role":"content","text":"Both recall and dedup share the same hybrid query: a TAG pre-filter in parentheses followed by `=>[KNN k @embedding $vec]`. With `DIALECT 2`, Redis applies the filter first and KNN-ranks only the matching documents.\n\n[code example]\n\n`distance` is the cosine *distance* (0 means identical, 2 means opposite). Recall and dedup share the same query shape; only the threshold differs — strict at write time so the index doesn't fill with paraphrases of the same fact, looser at read time so the agent gets a wider net of relevant memories."},{"id":"per-kind-ttls","title":"Per-kind TTLs","role":"content","text":"`remember` resolves the entry's TTL from the memory's `kind`:\n\n| Kind      | Default TTL | When to use it                                              |\n|-----------|-------------|-------------------------------------------------------------|\n| `episodic` | 7 days     | Snapshots from a specific session that should decay.        |\n| `semantic` | none       | Distilled facts and preferences the agent carries forward.  |\n\nYou can override per write with `ttl_seconds: ...` on `remember`, or pass a different `ttl_by_kind: { ... }` to the `LongTermMemory` constructor — for example, to give semantic memories a six-month TTL while leaving episodic memories at seven days."},{"id":"the-event-log","title":"The event log","role":"content","text":"`AgentEventLog` is a thin wrapper over a per-thread Redis Stream ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/memory-layer/ruby/lib/event_log.rb)):\n\n[code example]\n\n`record` calls [`XADD`](https://redis.io/docs/latest/commands/xadd) with `MAXLEN ~ 1000`. The tilde lets Redis trim in whole-node units instead of exactly-N units, which is much cheaper at the cost of overshooting the bound by up to a node's worth — the right tradeoff for an audit log where exact length doesn't matter.\n\nThe Stream is independent of the session Hash and the long-term JSON documents: it answers \"what just happened\" without competing with either of those for indexing or memory budget. Consumer groups (not used in this demo) would let downstream workers — summarizers, consolidators, audit pipelines — replay the log without losing position."},{"id":"concurrency-caveats","title":"Concurrency caveats","role":"content","text":"The three helpers above trade correctness under heavy concurrency for clarity. Each is fine on a single-process demo, but lifting the code into a real multi-worker agent surfaces three races worth knowing about:\n\n* **Working memory is read-modify-write.** `AgentSession#append_turn` calls [`HGETALL`](https://redis.io/docs/latest/commands/hgetall), mutates the `recent_turns` array in application code, and writes the Hash back with [`HSET`](https://redis.io/docs/latest/commands/hset). Two concurrent turns on the same thread can both read the same `recent_turns`, append different entries, and write back — last writer wins, the other turn is silently lost. The robust fix is either a [`WATCH`](https://redis.io/docs/latest/commands/watch) / [`MULTI`](https://redis.io/docs/latest/commands/multi) / [`EXEC`](https://redis.io/docs/latest/commands/exec) loop around the read-modify-write or a small [Lua script](https://redis.io/docs/latest/commands/eval) that does the append atomically server-side.\n\n* **Long-term dedup is not atomic.** `LongTermMemory#remember` runs a [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) KNN lookup, decides whether the candidate is a duplicate, and (if not) calls [`JSON.SET`](https://redis.io/docs/latest/commands/json.set). Two workers seeing the same fact in flight can each fail to see the other's not-yet-committed write and both insert a new memory. The pragmatic fix is to accept that the index will occasionally hold near-duplicates and run a background consolidator that periodically scans for memory pairs within a tight distance and merges them, rather than trying to make the write itself atomic.\n\n* **The active thread is server state.** The demo server keeps a single `current_thread_id` accessor on the `AgentMemoryDemo` object that `/new_thread` and `/reset` mutate; `handle_turn` reads it without coordination, so a turn racing with a thread rotation can apply to the previous thread. This is cosmetic for a one-user browser demo. A multi-user agent would carry the thread id on the request itself rather than as shared server state.\n\n* **The embedder is shared and the demo server is multi-threaded.** `informers` constructs the ONNX session once and reuses it across every request, and `WEBrick::HTTPServer` runs each request in its own Ruby thread. The MRI GIL serializes Ruby bytecode but does not protect calls into `onnxruntime`'s C++ session, which is undocumented for thread-safety; the demo gets away with the unsynchronized sharing because a single browser sends one turn at a time. A multi-user agent — or anything that lets the browser fire requests in parallel — should wrap the embedder call in a `Mutex` or hand each worker its own `LocalEmbedder` instance.\n\nThose caveats are deliberate. A more conservative implementation would obscure the Redis-shaped parts of the pattern; the demo prioritizes a small, readable code path that maps directly onto the commands in the prose above."},{"id":"pre-seeding-long-term-memory","title":"Pre-seeding long-term memory","role":"content","text":"In a real deployment the memory store fills up organically as the agent reasons over user turns: each turn produces zero or more memories that flow into the store, with deduplication catching repeats. For the demo, `seed_memory.rb` pre-loads a small set of mixed semantic and episodic memories so the very first recall query returns something useful ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/memory-layer/ruby/lib/seed_memory.rb)):\n\n[code example]\n\nThe seed list mixes long-lived facts and preferences (`semantic`) with snapshots of past sessions (`episodic`), so the **Kind to write** control in the demo has something to switch between when a new turn is being remembered."},{"id":"the-interactive-demo","title":"The interactive demo","role":"content","text":"`demo_server.rb` runs a [`WEBrick::HTTPServer`](https://github.com/ruby/webrick) on port 8091. WEBrick was extracted from the stdlib in Ruby 3.0, so the `Gemfile` declares it as a runtime gem; the [semantic-cache Ruby example](https://redis.io/docs/latest/develop/use-cases/semantic-cache/ruby) uses the same setup. The HTML page exposes three live panels — working memory, recalled memories, event log — plus a memories table for admin actions. Endpoints:\n\n| Endpoint            | What it does                                                                    |\n|---------------------|---------------------------------------------------------------------------------|\n| `GET  /state`       | Index info, current session, in-scope long-term memories, and recent events.    |\n| `POST /turn`        | Embed the text, append to working memory, recall similar memories, optionally write a new memory (with dedup), append an event. |\n| `POST /new_thread`  | Start a fresh thread; long-term memory and other threads are untouched.         |\n| `POST /reset`       | Drop every long-term memory and re-seed the sample set.                         |\n| `POST /drop_memory` | Delete a single long-term memory by id.                                         |\n\nThe server holds one `LocalEmbedder`, one `AgentSession`, one `LongTermMemory`, and one `AgentEventLog` for the lifetime of the process. The \"current thread\" is a single accessor on the demo object that the **New thread** button rotates — every browser tab inherits the same thread until you explicitly start a new one."},{"id":"run-the-demo-locally","title":"Run the demo locally","role":"content","text":"1.  Clone the [`redis/docs`](https://github.com/redis/docs) repository and change into the\n    example directory:\n\n    [code example]\n\n2.  Install the dependencies. Ruby 3.2 or later is required.\n\n    [code example]\n\n    The committed `Gemfile.lock` was regenerated on a newer Ruby and Bundler\n    than the 3.2 floor (the `BUNDLED WITH` line records the Bundler that wrote\n    it); if `bundle install` complains about a Bundler version mismatch on an\n    older 3.2 install, either upgrade Bundler (`gem install bundler`) or delete\n    `Gemfile.lock` to regenerate it under your local Bundler.\n\n    The `onnxruntime` gem ships a pre-built shared library for macOS (arm64 / x86_64)\n    and Linux; on those platforms `bundle install` is the whole install. On other\n    platforms see the [`onnxruntime` README](https://github.com/ankane/onnxruntime-ruby#installation).\n\n3.  Make sure a Redis instance with Redis Search and Redis JSON is running locally on\n    port 6379. [Redis Stack](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack)\n    ships both, or [Redis 8](https://redis.io/docs/latest/develop/ai/search-and-query) with the\n    Search and JSON modules enabled.\n\n4.  Start the demo server. The first run downloads the\n    [`sentence-transformers/all-MiniLM-L6-v2`](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2)\n    ONNX weights (around 80 MB) into the local Hugging Face cache:\n\n    [code example]\n\n5.  Open <http://localhost:8091> and try some turns:\n\n    *  **\"Remind me which theme I prefer in editors.\"** — paraphrase of a seeded\n       semantic memory (\"The user dislikes dark mode and prefers a high-contrast\n       light theme...\"). You should see that memory recalled with a cosine\n       distance around 0.47, comfortably under the 0.55 default recall\n       threshold.\n    *  **\"What did we discuss about the order-routing outage?\"** — paraphrase of\n       a seeded episodic memory; the postmortem memory should recall around\n       0.44. Switch the **Kind to write** dropdown to `skip` so the question\n       itself doesn't enter long-term memory.\n    *  **\"I prefer concise answers without filler phrases.\"** — paraphrase of\n       a seeded *semantic* memory. Switch the **Kind to write** dropdown to\n       `semantic` so the dedup KNN runs in the same kind as the seed (dedup\n       is scoped per kind, on purpose, so an episodic write can't collapse\n       onto a semantic memory). You should then see the write **deduped**\n       onto the existing memory at a cosine distance around 0.15, with\n       `hit_count` ticking up in the memories table.\n    *  **\"My favorite color is teal.\"** — unrelated to any seed; nothing recalls\n       above the threshold (every seed lands above 0.8), and the new memory is\n       written as `episodic` (or `semantic`, depending on the dropdown) under a\n       fresh id.\n    *  Switch the **User** field to `bob` and re-ask any of the above — recall\n       returns nothing because the seed memories live under `default`. That's\n       the TAG pre-filter at work inside [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search).\n    *  Slide the **Recall threshold** down to 0.30 to see borderline paraphrases\n       drop out of the recall set, then back up to 0.70 to watch them return.\n\n    `sentence-transformers/all-MiniLM-L6-v2` puts a faithful paraphrase in the\n    0.15 – 0.50 cosine-distance range, a loose paraphrase or related topic in the\n    0.50 – 0.80 range, and unrelated queries above 0.8 — which is what motivates\n    the 0.55 default recall threshold and the 0.20 default dedup threshold. A\n    stricter embedding model (or a domain-tuned one) would let you tighten both;\n    a noisier one would push them up. The right thresholds are always a function\n    of the model, the corpus, and how conservative the agent needs to be about\n    accepting a memory as a match.\n\nThe server is read/write against your local Redis. The default memory index is `agentmem:idx`, JSON keys live under `agent:mem:`, session Hashes under `agent:session:`, and event Streams under `agent:events:`. Useful flags:\n\n* `--host` — HTTP bind host (default `127.0.0.1`).\n* `--port` — HTTP bind port (default `8091`).\n* `--redis-host`, `--redis-port` — point the demo at a non-local Redis.\n* `--mem-index-name`, `--mem-key-prefix`, `--session-key-prefix`, `--event-key-prefix` — change the default key namespacing.\n* `--no-reset` — keep the existing long-term memories across restarts instead of dropping and re-seeding.\n* `--session-ttl-seconds` — change the working-memory TTL (default 3600).\n* `--dedup-threshold` — change the cosine-distance cutoff for write-time deduplication.\n* `--recall-threshold` — change the default cosine-distance cutoff for recall."}],"examples":[{"id":"the-session-store-ex0","language":"ruby","code":"require 'redis'\nrequire_relative 'lib/session_store'\n\nclient = Redis.new(host: 'localhost', port: 6379)\nsession = AgentMemory::AgentSession.new(\n  redis_client: client,\n  key_prefix: 'agent:session:',\n  default_ttl_seconds: 3600,  # one hour\n  max_turns: 20               # rolling window per thread\n)\n\nthread_id = session.new_thread_id\nsession.start(\n  thread_id,\n  user: 'alice',\n  agent: 'demo-agent',\n  goal: \"Plan next week's meetings.\"\n)\nsession.append_turn(\n  thread_id,\n  role: 'user',\n  content: 'Schedule a budget review with finance.'\n)\nstate = session.load(thread_id)\nputs [state.turn_count, state.recent_turns.length, state.ttl_seconds].inspect","section_id":"the-session-store"},{"id":"the-session-store-ex1","language":"text","code":"agent:session:9f3d2a4b8c61\n  thread_id=9f3d2a4b8c61\n  user=alice\n  agent=demo-agent\n  goal=Plan next week's meetings.\n  scratchpad=Need to confirm finance's availability.\n  turn_count=4\n  created_ts=1715990400.12\n  last_active_ts=1715990650.83\n  recent_turns=[{\"role\":\"user\",\"content\":\"...\",\"ts\":...}, ...]","section_id":"the-session-store"},{"id":"the-long-term-memory-store-ex0","language":"ruby","code":"require_relative 'lib/embeddings'\nrequire_relative 'lib/long_term_memory'\n\nmemory = AgentMemory::LongTermMemory.new(\n  redis_client: client,\n  index_name: 'agentmem:idx',\n  key_prefix: 'agent:mem:',\n  dedup_threshold: 0.20,   # cosine distance — tight at write time\n  recall_threshold: 0.55   # looser at read time\n)\nembedder = AgentMemory::LocalEmbedder.new\nmemory.create_index  # idempotent\n\n# Write a memory. The same KNN that powers recall also runs here at\n# a tighter threshold so paraphrases of the same fact collapse.\nvec = embedder.encode_one('The user prefers light mode in editors.')\nresult = memory.remember(\n  text: 'The user prefers light mode in editors.',\n  embedding: vec,\n  user: 'alice',\n  namespace: 'default',\n  kind: 'semantic',\n  source_thread: '9f3d2a4b8c61'\n)\nputs result.deduped, result.id, result.existing_distance\n\n# Recall against a later question.\nq = embedder.encode_one('Which theme does this user like?')\nhits = memory.recall(q, user: 'alice', namespace: 'default', k: 5)\nhits.each { |h| puts format('%.3f [%s] %s', h.distance, h.kind, h.text) }","section_id":"the-long-term-memory-store"},{"id":"data-model-ex0","language":"json","code":"agent:mem:7c3f8a1b9e02\n{\n  \"id\": \"7c3f8a1b9e02\",\n  \"user\": \"alice\",\n  \"namespace\": \"default\",\n  \"kind\": \"semantic\",\n  \"source_thread\": \"9f3d2a4b8c61\",\n  \"text\": \"The user prefers light mode in editors.\",\n  \"embedding\": [0.013, -0.041, ...],\n  \"created_ts\": 1715990400.12,\n  \"hit_count\": 0\n}","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"FT.CREATE agentmem:idx\n  ON JSON PREFIX 1 agent:mem:\n  SCHEMA\n    $.text          AS text          TEXT\n    $.user          AS user          TAG\n    $.namespace     AS namespace     TAG\n    $.kind          AS kind          TAG\n    $.source_thread AS source_thread TAG\n    $.created_ts    AS created_ts    NUMERIC SORTABLE\n    $.hit_count     AS hit_count     NUMERIC SORTABLE\n    $.embedding     AS embedding     VECTOR HNSW 6\n                                       TYPE FLOAT32 DIM 384\n                                       DISTANCE_METRIC COSINE","section_id":"data-model"},{"id":"the-query-ex0","language":"text","code":"FT.SEARCH agentmem:idx\n  \"(@user:{alice} @namespace:{default} @kind:{semantic})\n     =>[KNN 5 @embedding $vec AS distance]\"\n  PARAMS 2 vec <384-float32-bytes>\n  SORTBY distance\n  RETURN 8 user namespace kind source_thread text created_ts hit_count distance\n  DIALECT 2","section_id":"the-query"},{"id":"the-event-log-ex0","language":"ruby","code":"require_relative 'lib/event_log'\n\nevents = AgentMemory::AgentEventLog.new(redis_client: client, max_len: 1000)\nevents.record(thread_id, 'turn_appended:user',\n              'Schedule a budget review with finance.')\nevents.record(thread_id, 'memory_written',\n              'wrote 7c3f8a1b9e02 as semantic')\n\nevents.recent(thread_id, count: 20).each do |e|\n  puts \"#{e.action} #{e.detail}\"\nend","section_id":"the-event-log"},{"id":"pre-seeding-long-term-memory-ex0","language":"ruby","code":"require_relative 'lib/seed_memory'\n\nmemory = AgentMemory::LongTermMemory.new(redis_client: client)\nembedder = AgentMemory::LocalEmbedder.new\nmemory.create_index\nAgentMemory::SeedMemory.seed(memory, embedder,\n                             user: 'default', namespace: 'default')","section_id":"pre-seeding-long-term-memory"},{"id":"run-the-demo-locally-ex0","language":"bash","code":"git clone https://github.com/redis/docs.git\n    cd docs/content/develop/use-cases/memory-layer/ruby","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex1","language":"bash","code":"bundle install","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex2","language":"bash","code":"bundle exec ruby demo_server.rb","section_id":"run-the-demo-locally"}]}
{"schema_version":2,"id":"develop/use-cases/memory-layer/rust","title":"Redis memory layer with redis-rs","url":"https://redis.io/docs/latest/develop/use-cases/memory-layer/rust/","summary":"Build a Redis-backed memory layer in Rust with redis-rs, Candle, and standard Redis commands — working memory in a Hash, long-term semantic recall as JSON with a vector index, and an event log in a Stream.","aliases":["/develop/use-cases/agent-memory/rust","/develop/use-cases/agent-memory/redis-rs"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:54:57+01:00","children":[],"page_type":"content","content_hash":"976708d86e986eed579e9fb74d6426b5556e20fdbd8d83509b4bad4993091b3f","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a small Redis-backed memory layer in Rust with [`redis-rs`](https://redis.io/docs/latest/develop/clients/rust) and the [Candle](https://github.com/huggingface/candle) inference framework, using only standard Redis commands — no Redis Agent Memory SDK, no managed service. It includes a local web server built with [`tiny_http`](https://docs.rs/tiny_http) so you can send turns at the agent, watch working memory update in place, see semantically similar long-term memories recalled in real time, watch the write-time deduplication skip near-duplicates, and inspect the per-thread event log.\n\nThe embedder is [Candle](https://github.com/huggingface/candle) running the canonical [`sentence-transformers/all-MiniLM-L6-v2`](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2) PyTorch checkpoint — the same weights the [Python](https://redis.io/docs/latest/develop/use-cases/memory-layer/redis-py) example loads. Candle reads the `.bin` weights directly, so the vectors produced here are bit-identical to the Python ones, and the distance bands the Python walkthrough quotes carry over to this demo without recalibration. A memory written by one demo can be recalled by the other against the same Redis instance."},{"id":"overview","title":"Overview","role":"overview","text":"The memory layer splits across three Redis primitives, each handling one tier:\n\n* **Working memory** for the active session is a [Hash](https://redis.io/docs/latest/develop/data-types/hashes) at `agent:session:<thread_id>` holding the goal, scratchpad, a rolling window of recent turns (as a JSON list inside one field), and a few audit timestamps. One [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) returns the whole session in a single round trip; every write refreshes the key's [`EXPIRE`](https://redis.io/docs/latest/commands/expire) so idle sessions decay on their own.\n* **Long-term memory** is a set of [JSON](https://redis.io/docs/latest/develop/data-types/json) documents at `agent:mem:<id>`, each carrying the memory text, a 384-dimensional embedding vector, and tag fields for user, namespace, kind (episodic / semantic), and source thread. A single [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) index covers the [HNSW vector field](https://redis.io/docs/latest/develop/ai/search-and-query/vectors) and every metadata field, so one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call performs the KNN with the metadata pre-filter in the same round trip. Write-time deduplication runs the same KNN at insert time and skips a new memory whose nearest existing entry is within a tighter threshold.\n* **Event log** for the agent's actions and observations is a [Stream](https://redis.io/docs/latest/develop/data-types/streams) at `agent:events:<thread_id>`, appended with [`XADD MAXLEN ~`](https://redis.io/docs/latest/commands/xadd) so retention stays bounded automatically, replayed with [`XREVRANGE`](https://redis.io/docs/latest/commands/xrevrange).\n\nThat gives you:\n\n* A single round trip per tier: one [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) for the session, one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) for recall, one [`XADD`](https://redis.io/docs/latest/commands/xadd) for the event log.\n* Sub-millisecond reads on every step of the agent loop, so the memory layer doesn't dominate per-step latency.\n* Per-tier decay: short TTLs on working memory, longer on episodic memories, no TTL on semantic memories. Combined with a database-level [eviction policy](https://redis.io/docs/latest/develop/reference/eviction) (LFU is the common choice), memory stays bounded under pressure.\n* Scoping enforced inside the query: a recall query for `user=alice` will never see `user=bob`'s memories, because the TAG filter goes into the same [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call as the KNN."},{"id":"how-it-works","title":"How it works","role":"content","text":"Each turn through the agent loop touches all three tiers in one pass: append to working memory, recall similar long-term memories, write the turn back as a new memory (with deduplication), and append one event to the log."},{"id":"per-turn-flow","title":"Per-turn flow","role":"content","text":"1. The application calls `embedder.encode_one(text)` to turn the incoming turn into a 384-dimensional `Vec<f32>`.\n2. `session.append_turn(thread_id, role, content, Some(user), ..., None)` reads the per-thread Hash with [`HGETALL`](https://redis.io/docs/latest/commands/hgetall), appends the new turn to the rolling window in application code, trims it back to the configured maximum, and writes the Hash back with an [`HSET`](https://redis.io/docs/latest/commands/hset) + [`EXPIRE`](https://redis.io/docs/latest/commands/expire) pipeline inside a [`MULTI`](https://redis.io/docs/latest/commands/multi) / [`EXEC`](https://redis.io/docs/latest/commands/exec). The session TTL refreshes on every write so an active thread stays alive.\n3. `memory.recall(&vec, user, Some(namespace), None, 5, None)` runs [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) with a TAG pre-filter and a `KNN 5` clause. Redis returns the closest matching memories together with their cosine distances; memories beyond the recall threshold are dropped before they reach the agent so an unrelated query doesn't surface confident-looking false positives.\n4. `memory.remember(text, &vec, user, namespace, kind, source_thread, None)` runs the same KNN with a tighter dedup threshold. If an existing memory is within the threshold, the new write is skipped and the existing memory's `hit_count` is incremented with [`JSON.NUMINCRBY`](https://redis.io/docs/latest/commands/json.numincrby); otherwise a fresh JSON document is written with [`JSON.SET`](https://redis.io/docs/latest/commands/json.set) and a per-kind [`EXPIRE`](https://redis.io/docs/latest/commands/expire) — `episodic` defaults to seven days, `semantic` has no TTL by default.\n5. `events.record(thread_id, action, detail)` appends one entry to the per-thread Stream with [`XADD MAXLEN ~`](https://redis.io/docs/latest/commands/xadd), bounding retention to roughly a thousand entries per thread without an explicit cleanup job.\n\nThe embedding is computed once and reused for steps 3 and 4 — there's no point encoding the same text twice. Recall runs before the write, so the agent doesn't see its own just-written turn echoed back as a recalled memory."},{"id":"the-session-store","title":"The session store","role":"content","text":"`AgentSession` wraps the working-memory Hash and the rolling turn window ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/memory-layer/rust/src/session_store.rs)):\n\n[code example]\n\nThe data model is one Hash per thread. The rolling turn window is stored as a JSON string in a single field so the whole session loads in one [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) — the hash never grows in size or field count as the conversation goes on.\n\n[code example]\n\nEvery write — `start`, `append_turn`, `set_goal`, `set_scratchpad` — runs the [`HSET`](https://redis.io/docs/latest/commands/hset) and [`EXPIRE`](https://redis.io/docs/latest/commands/expire) inside a `redis::pipe().atomic()` block so a connection drop between the two writes can't leave the session without a TTL."},{"id":"the-long-term-memory-store","title":"The long-term memory store","role":"content","text":"`LongTermMemory` owns the JSON documents, the vector index, the recall query, and the write-time deduplication ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/memory-layer/rust/src/long_term_memory.rs)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each memory is a JSON document at `agent:mem:<id>`. The embedding is stored as a JSON array of floats so the document is human-readable from `redis-cli`; [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) still expects the *query* vector as raw `float32` bytes (`byteorder::LittleEndian` via `embeddings::floats_to_bytes`), regardless of how the indexed document stores it.\n\n[code example]\n\nThe Redis Search index is declared on the JSON document type with `AS` aliases so the query syntax stays compact:\n\n[code example]"},{"id":"the-query","title":"The query","role":"content","text":"Both recall and dedup share the same hybrid query: a TAG pre-filter in parentheses followed by `=>[KNN k @embedding $vec]`. With `DIALECT 2`, Redis applies the filter first and KNN-ranks only the matching documents.\n\n[code example]\n\n`distance` is the cosine *distance* (0 means identical, 2 means opposite). Recall and dedup share the same query shape; only the threshold differs — strict at write time so the index doesn't fill with paraphrases of the same fact, looser at read time so the agent gets a wider net of relevant memories."},{"id":"per-kind-ttls","title":"Per-kind TTLs","role":"content","text":"`remember` resolves the entry's TTL from the memory's `kind`:\n\n| Kind      | Default TTL | When to use it                                              |\n|-----------|-------------|-------------------------------------------------------------|\n| `episodic` | 7 days     | Snapshots from a specific session that should decay.        |\n| `semantic` | none       | Distilled facts and preferences the agent carries forward.  |\n\nYou can override per write by passing `Some(ttl)` as the last argument to `remember`. The defaults live in `long_term_memory::default_ttl_for_kind`; swap it out if you want a different tier map."},{"id":"the-event-log","title":"The event log","role":"content","text":"`AgentEventLog` is a thin wrapper over a per-thread Redis Stream ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/memory-layer/rust/src/event_log.rs)):\n\n[code example]\n\n`record` calls [`XADD`](https://redis.io/docs/latest/commands/xadd) with `MAXLEN ~ 1000`. The tilde lets Redis trim in whole-node units instead of exactly-N units, which is much cheaper at the cost of overshooting the bound by up to a node's worth — the right tradeoff for an audit log where exact length doesn't matter.\n\nThe Stream is independent of the session Hash and the long-term JSON documents: it answers \"what just happened\" without competing with either of those for indexing or memory budget. Consumer groups (not used in this demo) would let downstream workers — summarisers, consolidators, audit pipelines — replay the log without losing position."},{"id":"concurrency-caveats","title":"Concurrency caveats","role":"content","text":"The three helpers above trade correctness under heavy concurrency for clarity. Each is fine on a single-process demo, but lifting the code into a real multi-worker agent surfaces three races worth knowing about:\n\n* **Working memory is read-modify-write.** `AgentSession::append_turn` calls [`HGETALL`](https://redis.io/docs/latest/commands/hgetall), mutates the `recent_turns` vector in application code, and writes the Hash back with [`HSET`](https://redis.io/docs/latest/commands/hset). Two concurrent turns on the same thread can both read the same `recent_turns`, append different entries, and write back — last writer wins, the other turn is silently lost. The robust fix is either a [`WATCH`](https://redis.io/docs/latest/commands/watch) / [`MULTI`](https://redis.io/docs/latest/commands/multi) / [`EXEC`](https://redis.io/docs/latest/commands/exec) loop around the read-modify-write or a small [Lua script](https://redis.io/docs/latest/commands/eval) that does the append atomically server-side. (Each helper holds a `Mutex<Connection>` that protects individual Redis commands against TCP-frame interleaving, but the lock is released between the `HGETALL` and the `HSET`, so it doesn't make the read-modify-write itself atomic.)\n\n* **Long-term dedup is not atomic.** `LongTermMemory::remember` runs a [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) KNN lookup, decides whether the candidate is a duplicate, and (if not) calls [`JSON.SET`](https://redis.io/docs/latest/commands/json.set). Two workers seeing the same fact in flight can each fail to see the other's not-yet-committed write and both insert a new memory. The pragmatic fix is to accept that the index will occasionally hold near-duplicates and run a background consolidator that periodically scans for memory pairs within a tight distance and merges them, rather than trying to make the write itself atomic.\n\n* **The active thread is server state.** The demo server keeps a single `current_thread_id` (a `Mutex<String>`) that `/new_thread` and `/reset` mutate. `handle_turn` reads it under the mutex but then drops the lock immediately, so a turn racing with a thread rotation can apply to the previous thread. This is cosmetic for a one-user browser demo. A multi-user agent would carry the thread id on the request itself rather than as shared server state.\n\nThose caveats are deliberate. A more conservative implementation would obscure the Redis-shaped parts of the pattern; the demo prioritizes a small, readable code path that maps directly onto the commands in the prose above."},{"id":"pre-seeding-long-term-memory","title":"Pre-seeding long-term memory","role":"content","text":"In a real deployment the memory store fills up organically as the agent reasons over user turns: each turn produces zero or more memories that flow into the store, with deduplication catching repeats. For the demo, `src/seed_memory.rs` pre-loads a small set of mixed semantic and episodic memories so the very first recall query returns something useful ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/memory-layer/rust/src/seed_memory.rs)):\n\n[code example]\n\nThe seed list mixes long-lived facts and preferences (`semantic`) with snapshots of past sessions (`episodic`), so the **Kind to write** control in the demo has something to switch between when a new turn is being remembered."},{"id":"the-interactive-demo","title":"The interactive demo","role":"content","text":"`src/main.rs` runs a [`tiny_http`](https://docs.rs/tiny_http) server on port 8094, spawning a worker thread per request. The HTML page exposes three live panels — working memory, recalled memories, event log — plus a memories table for admin actions. Endpoints:\n\n| Endpoint            | What it does                                                                    |\n|---------------------|---------------------------------------------------------------------------------|\n| `GET  /state`       | Index info, current session, in-scope long-term memories, and recent events.    |\n| `POST /turn`        | Embed the text, append to working memory, recall similar memories, optionally write a new memory (with dedup), append an event. |\n| `POST /new_thread`  | Start a fresh thread; long-term memory and other threads are untouched.         |\n| `POST /reset`       | Drop every long-term memory and re-seed the sample set.                         |\n| `POST /drop_memory` | Delete a single long-term memory by id.                                         |\n\nThe server holds one `LocalEmbedder`, one `AgentSession`, one `LongTermMemory`, and one `AgentEventLog` for the lifetime of the process, wrapped in an `Arc` so the worker threads can share them without copying. The \"current thread\" is a `Mutex<String>` that the **New thread** button rotates — every browser tab inherits the same thread until you explicitly start a new one."},{"id":"run-the-demo-locally","title":"Run the demo locally","role":"content","text":"1.  Clone the [`redis/docs`](https://github.com/redis/docs) repository and change into the example\n    directory:\n\n    [code example]\n\n2.  Build the project. You'll need a recent [Rust toolchain](https://www.rust-lang.org/tools/install)\n    (1.75 or later):\n\n    [code example]\n\n    Building in release mode matters here — Candle is CPU-bound on token throughput,\n    so the debug build is several times slower at the embedding step.\n\n3.  Make sure a Redis instance with Redis Search and Redis JSON is running locally on\n    port 6379. [Redis Stack](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack)\n    ships both, or [Redis 8](https://redis.io/docs/latest/develop/ai/search-and-query) with the\n    Search and JSON modules enabled.\n\n4.  Start the demo. The first run downloads the\n    [`sentence-transformers/all-MiniLM-L6-v2`](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2)\n    weights, tokenizer, and config from the Hugging Face Hub into the local cache\n    (`~/.cache/huggingface/hub/` by default):\n\n    [code example]\n\n5.  Open <http://localhost:8094> and try some turns:\n\n    *  **\"Remind me which theme I prefer in editors.\"** — paraphrase of a seeded\n       semantic memory (\"The user dislikes dark mode and prefers a high-contrast\n       light theme...\"). You should see that memory recalled with a cosine\n       distance around 0.47, comfortably under the 0.55 default recall\n       threshold.\n    *  **\"What did we discuss about the order-routing outage?\"** — paraphrase of\n       a seeded episodic memory; the postmortem memory should recall around\n       0.44. Switch the **Kind to write** dropdown to `skip` so the question\n       itself doesn't enter long-term memory.\n    *  **\"I prefer concise answers without filler phrases.\"** — paraphrase of\n       a seeded *semantic* memory. Switch the **Kind to write** dropdown to\n       `semantic` so the dedup KNN runs in the same kind as the seed (dedup\n       is scoped per kind, on purpose, so an episodic write can't collapse\n       onto a semantic memory). You should then see the write **deduped**\n       onto the existing memory at a cosine distance around 0.15, with\n       `hit_count` ticking up in the memories table.\n    *  **\"My favorite color is teal.\"** — unrelated to any seed; nothing\n       recalls above the threshold (every seed lands above 0.8), and the new\n       memory is written as `episodic` (or `semantic`, depending on the\n       dropdown) under a fresh id.\n    *  Switch the **User** field to `bob` and re-ask any of the above — recall\n       returns nothing because the seed memories live under `default`. That's\n       the TAG pre-filter at work inside [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search).\n    *  Slide the **Recall threshold** down to 0.30 to see borderline paraphrases\n       drop out of the recall set, then back up to 0.70 to watch them return.\n\n    Candle reads the same PyTorch weights as the Python `sentence-transformers`\n    library, so distances here match the Python demo bit-for-bit.\n    `sentence-transformers/all-MiniLM-L6-v2` puts a faithful paraphrase in the\n    0.15 – 0.50 cosine-distance range, a loose paraphrase or related topic in\n    the 0.50 – 0.80 range, and unrelated queries above 0.8 — which is what\n    motivates the 0.55 default recall threshold and the 0.20 default dedup\n    threshold. A stricter embedding model (or a domain-tuned one) would let\n    you tighten both; a noisier one would push them up. The right thresholds\n    are always a function of the model, the corpus, and how conservative the\n    agent needs to be about accepting a memory as a match.\n\nThe server is read/write against your local Redis. The default memory index is `agentmem:idx`, JSON keys live under `agent:mem:`, session Hashes under `agent:session:`, and event Streams under `agent:events:`. Useful flags (pass them after `--`, for example `cargo run --release -- --no-reset`):\n\n* `--no-reset` — keep the existing long-term memories across restarts instead of dropping and re-seeding.\n* `--session-ttl-seconds` — change the working-memory TTL (default 3600).\n* `--dedup-threshold` — change the cosine-distance cutoff for write-time deduplication.\n* `--recall-threshold` — change the default cosine-distance cutoff for recall."}],"examples":[{"id":"the-session-store-ex0","language":"rust","code":"use agent_memory_demo::session_store::AgentSession;\n\nlet client = redis::Client::open(\"redis://127.0.0.1/\")?;\nlet session = AgentSession::new(\n    &client,\n    \"agent:session:\",\n    3600,  // one hour TTL on the working-memory hash\n    20,    // rolling window per thread\n)?;\n\nlet thread_id = session.new_thread_id();\nsession.start(&thread_id, \"alice\", \"demo-agent\",\n    \"Plan next week's meetings.\", None)?;\nsession.append_turn(\n    &thread_id, \"user\",\n    \"Schedule a budget review with finance.\",\n    Some(\"alice\"), Some(\"demo-agent\"), None,\n)?;\nlet state = session.load(&thread_id)?;\nprintln!(\"{} {} {}\",\n    state.as_ref().map(|s| s.turn_count).unwrap_or(0),\n    state.as_ref().map(|s| s.recent_turns.len()).unwrap_or(0),\n    state.as_ref().map(|s| s.ttl_seconds).unwrap_or(0));","section_id":"the-session-store"},{"id":"the-session-store-ex1","language":"text","code":"agent:session:9f3d2a4b8c61\n  thread_id=9f3d2a4b8c61\n  user=alice\n  agent=demo-agent\n  goal=Plan next week's meetings.\n  scratchpad=Need to confirm finance's availability.\n  turn_count=4\n  created_ts=1715990400.12\n  last_active_ts=1715990650.83\n  recent_turns=[{\"role\":\"user\",\"content\":\"...\",\"ts\":...}, ...]","section_id":"the-session-store"},{"id":"the-long-term-memory-store-ex0","language":"rust","code":"use agent_memory_demo::embeddings::LocalEmbedder;\nuse agent_memory_demo::long_term_memory::LongTermMemory;\n\nlet memory = LongTermMemory::new(\n    &client,\n    \"agentmem:idx\",\n    \"agent:mem:\",\n    384,    // vector dimension\n    0.20,   // dedup threshold — tight at write time\n    0.55,   // recall threshold — looser at read time\n)?;\nlet embedder = LocalEmbedder::new(None)?;\nmemory.create_index()?;  // idempotent\n\n// Write a memory. The same KNN that powers recall also runs here\n// at a tighter threshold so paraphrases of the same fact collapse.\nlet vec = embedder.encode_one(\"The user prefers light mode in editors.\")?;\nlet result = memory.remember(\n    \"The user prefers light mode in editors.\",\n    &vec,\n    \"alice\",\n    \"default\",\n    \"semantic\",\n    \"9f3d2a4b8c61\",\n    None,\n)?;\nprintln!(\"deduped={} id={} dist={:?}\",\n    result.deduped, result.id, result.existing_distance);\n\n// Recall against a later question.\nlet q = embedder.encode_one(\"Which theme does this user like?\")?;\nlet hits = memory.recall(&q, \"alice\", Some(\"default\"), None, 5, None)?;\nfor h in &hits {\n    println!(\"{:.3} [{}] {}\",\n        h.distance.unwrap_or(0.0), h.kind, h.text);\n}","section_id":"the-long-term-memory-store"},{"id":"data-model-ex0","language":"json","code":"agent:mem:7c3f8a1b9e02\n{\n  \"id\": \"7c3f8a1b9e02\",\n  \"user\": \"alice\",\n  \"namespace\": \"default\",\n  \"kind\": \"semantic\",\n  \"source_thread\": \"9f3d2a4b8c61\",\n  \"text\": \"The user prefers light mode in editors.\",\n  \"embedding\": [0.013, -0.041, ...],\n  \"created_ts\": 1715990400.12,\n  \"hit_count\": 0\n}","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"FT.CREATE agentmem:idx\n  ON JSON PREFIX 1 agent:mem:\n  SCHEMA\n    $.text          AS text          TEXT\n    $.user          AS user          TAG\n    $.namespace     AS namespace     TAG\n    $.kind          AS kind          TAG\n    $.source_thread AS source_thread TAG\n    $.created_ts    AS created_ts    NUMERIC SORTABLE\n    $.hit_count     AS hit_count     NUMERIC SORTABLE\n    $.embedding     AS embedding     VECTOR HNSW 6\n                                       TYPE FLOAT32 DIM 384\n                                       DISTANCE_METRIC COSINE","section_id":"data-model"},{"id":"the-query-ex0","language":"text","code":"FT.SEARCH agentmem:idx\n  \"(@user:{alice} @namespace:{default} @kind:{semantic})\n     =>[KNN 5 @embedding $vec AS distance]\"\n  PARAMS 2 vec <384-float32-bytes>\n  SORTBY distance\n  RETURN 8 user namespace kind source_thread text created_ts hit_count distance\n  DIALECT 2","section_id":"the-query"},{"id":"the-event-log-ex0","language":"rust","code":"use agent_memory_demo::event_log::AgentEventLog;\n\nlet events = AgentEventLog::new(&client, \"agent:events:\", 1000)?;\nevents.record(&thread_id, \"turn_appended:user\",\n    \"Schedule a budget review with finance.\")?;\nevents.record(&thread_id, \"memory_written\",\n    \"wrote 7c3f8a1b9e02 as semantic\")?;\n\nfor e in events.recent(&thread_id, 20)? {\n    println!(\"{} {}\", e.action, e.detail);\n}","section_id":"the-event-log"},{"id":"pre-seeding-long-term-memory-ex0","language":"rust","code":"use agent_memory_demo::seed_memory::seed;\n\nlet memory = LongTermMemory::new(&client, \"agentmem:idx\", \"agent:mem:\",\n    384, 0.20, 0.55)?;\nlet embedder = LocalEmbedder::new(None)?;\nmemory.create_index()?;\nseed(&memory, &embedder, \"default\", \"default\", \"seed\")?;","section_id":"pre-seeding-long-term-memory"},{"id":"run-the-demo-locally-ex0","language":"bash","code":"git clone https://github.com/redis/docs.git\n    cd docs/content/develop/use-cases/memory-layer/rust","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex1","language":"bash","code":"cargo build --release","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex2","language":"bash","code":"cargo run --release","section_id":"run-the-demo-locally"}]}
{"schema_version":2,"id":"develop/use-cases/prefetch-cache/dotnet","title":"Redis prefetch cache with StackExchange.Redis","url":"https://redis.io/docs/latest/develop/use-cases/prefetch-cache/dotnet/","summary":"Implement a Redis prefetch cache in C# with StackExchange.Redis","aliases":["/develop/use-cases/prefetch-cache/stackexchange.redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:54:57+01:00","children":[],"page_type":"content","content_hash":"216f606fad033c9c538f0bc5e17c4cc3b38592d6fcb18d863f7698939c368257","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis prefetch cache in C# with [StackExchange.Redis](https://seredis.dev/). It includes a small local web server built with ASP.NET Core minimal APIs so you can watch the cache pre-load at startup, see a background sync worker apply primary mutations within milliseconds, and break the cache to confirm that reads never fall back to the primary."},{"id":"overview","title":"Overview","role":"overview","text":"Prefetch caching pre-loads a working set of reference data into Redis before the first request arrives, so every read on the request path is a cache hit. A separate sync worker keeps the cache current as the source of truth changes — there is no fall-back to the primary on the read path.\n\nThat gives you:\n\n* Near-100% cache hit ratios for reference and master data\n* Sub-millisecond reads for lookup-heavy paths at peak traffic\n* All reference-data reads offloaded from the primary database\n* Source-database changes propagated into Redis within a few milliseconds\n* A long safety-net TTL that bounds memory if the sync pipeline ever stops\n\nIn this example, each cached category is stored as a Redis hash under a key like `cache:category:{id}`. The hash holds the category fields (`id`, `name`, `display_order`, `featured`, `parent_id`) and the key has a long safety-net TTL that the sync worker refreshes on every add or update event. Delete events remove the cache key outright, so there is no TTL to refresh in that case."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow has three independent paths:\n\n1. **On startup**, the demo server calls `cache.BulkLoad(primary.ListRecords())`, which pipelines `DEL` + `HSET` + `EXPIRE` for every record in one round trip.\n2. **On every read**, the application calls `cache.Get(entityId)`, which runs `HGETALL` against Redis only. A miss is treated as an error, not a trigger to query the primary.\n3. **On every primary mutation**, the primary appends a change event to an in-process queue. The sync worker thread drains the queue and calls `cache.ApplyChange(event)`. For an `Upsert`, the helper rewrites the cache hash and refreshes the safety-net TTL; for a `Delete`, it removes the cache key.\n\nIn a real system the in-process change queue is replaced by a CDC pipeline — [Redis Data Integration](https://redis.io/docs/latest/integrate/redis-data-integration), Debezium plus a lightweight consumer, or an equivalent tool that tails the source's binlog/WAL and pushes events into Redis."},{"id":"the-prefetch-cache-helper","title":"The prefetch-cache helper","role":"content","text":"The `PrefetchCache` class wraps the cache operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/prefetch-cache/dotnet/PrefetchCache.cs)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each cached category is stored in a Redis hash:\n\n[code example]\n\nThe implementation uses:\n\n* [`HSET`](https://redis.io/docs/latest/commands/hset) + [`EXPIRE`](https://redis.io/docs/latest/commands/expire), batched, for the bulk load and every sync event\n* [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) on the read path\n* [`DEL`](https://redis.io/docs/latest/commands/del) for sync-delete events and explicit invalidation\n* [`SCAN`](https://redis.io/docs/latest/commands/scan) to enumerate the cached keyspace and to clear the prefix\n* [`TTL`](https://redis.io/docs/latest/commands/ttl) to surface remaining safety-net time in the demo UI"},{"id":"bulk-load-on-startup","title":"Bulk load on startup","role":"content","text":"The `BulkLoad` method pipelines a `DEL` + `HSET` + `EXPIRE` triple for every record through a StackExchange.Redis `IBatch`, so loading thousands of records takes one network RTT plus the time Redis spends executing the commands locally — typically tens of milliseconds even for a large reference table:\n\n[code example]\n\n`IBatch` is non-transactional on purpose for the **startup** path: nothing is reading the cache yet, the records do not need to be applied atomically as a set, and skipping `MULTI`/`EXEC` keeps the bulk load fast. The same method is used for the live `/reprefetch` reload, which is safe because the demo pauses the sync worker around the clear-and-reload sequence — see [Re-prefetch under load](#re-prefetch-under-load) below. If you call `BulkLoad` directly from your own code on a cache that is already serving reads, either pause your writers first or rewrite it with `IDatabase.CreateTransaction()` so callers cannot observe a half-loaded record."},{"id":"reads-from-redis-only","title":"Reads from Redis only","role":"content","text":"The `Get` method runs `HGETALL` and returns the cached hash. **It does not fall back to the primary on a miss.** In a healthy system, a miss never happens; if it does, the application surfaces it as an error and treats it as a sync-pipeline incident:\n\n[code example]\n\nThis is the key behavioural difference from [cache-aside](https://redis.io/docs/latest/develop/use-cases/cache-aside): the request path never touches the primary, so reference-data reads cannot contribute to primary database load."},{"id":"applying-sync-events","title":"Applying sync events","role":"content","text":"The sync worker calls `ApplyChange` for every primary mutation. For an `Upsert`, the helper rewrites the cache hash and refreshes the safety-net TTL inside a StackExchange.Redis transaction (`IDatabase.CreateTransaction()`) so the cache never holds a stale mix of old and new fields. For a `Delete`, it removes the cache key:\n\n[code example]\n\nThe `DEL` before the `HSET` ensures the cached hash contains exactly the fields the primary record has now — fields that have been dropped from the primary will not linger in Redis. StackExchange.Redis transactions are optimistic (WATCH-based under the hood), but the three commands here have no conditions so they queue and dispatch atomically in a single round trip.\n\nThe \"skip empty upserts\" early-return is important: `HSET` with an empty array of fields throws, and a CDC pipeline that ever emits an upsert without fields would crash the sync worker on first encounter. A production consumer would route the bad event to a dead-letter queue and alert; the demo simply drops it."},{"id":"the-sync-worker","title":"The sync worker","role":"content","text":"The `SyncWorker` runs a long-running background `Thread` (not a `Task`) so it can poll on the change queue without consuming a ThreadPool slot. Every change is applied to Redis as soon as it arrives\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/prefetch-cache/dotnet/SyncWorker.cs)):\n\n[code example]\n\n`ManualResetEventSlim` provides the pause and stop signals. `BlockingCollection<ChangeEvent>.TryTake(out _, timeout)` is the .NET equivalent of `queue.Queue.get(timeout=…)` from the reference; the 50 ms timeout keeps the worker responsive to pause and stop requests without busy-looping.\n\nIn production this loop is replaced by a CDC consumer reading from RDI's Redis output stream, Debezium's Kafka topic, or an equivalent change feed. The shape stays the same: drain events, apply them to Redis, advance the consumer offset."},{"id":"invalidation-and-re-prefetch","title":"Invalidation and re-prefetch","role":"content","text":"Two helpers exist for testing and recovery:\n\n* `Invalidate(entityId)` deletes a single cache key. The demo uses it to simulate a sync-pipeline failure on one record.\n* `Clear()` runs `SCAN MATCH cache:category:*` and deletes every key under the prefix. The demo uses it to simulate a full cache loss.\n\nIn both cases, the recovery path is to call `BulkLoad(primary.ListRecords())` again — re-prefetching from the primary. The demo exposes this as the \"Re-prefetch\" button so you can see the cache come back to a fully-warm state in one operation."},{"id":"re-prefetch-under-load","title":"Re-prefetch under load","role":"content","text":"`Clear()` and `BulkLoad()` are not atomic against the sync worker. If a change event arrives between the snapshot (`primary.ListRecords()`) and the bulk write, the bulk write can overwrite a newer value; if a change event arrives between `Clear()`'s `SCAN` and `DEL`, the cleared entry can immediately be recreated. The demo's `/clear` and `/reprefetch` handlers solve this by pausing the sync worker around the operation:\n\n[code example]\n\n`Pause()` waits for the worker to finish whatever event it is currently applying, parks the run loop, and returns. Change events that arrive during the pause sit in the primary's `BlockingCollection` queue and apply in order once `Resume()` is called, so no event is lost."},{"id":"hitmiss-accounting","title":"Hit/miss accounting","role":"content","text":"The helper keeps in-process counters for hits, misses, prefetched records, sync events applied, and the average lag between a primary change and its application to Redis. The demo UI surfaces these so you can confirm the cache is absorbing all reads and the sync worker is keeping up:\n\n[code example]\n\nIn production you would emit these as counters and gauges through `Meter`/`Counter<T>` and scrape with Prometheus or OpenTelemetry. The sync-lag metric is the most important: a sudden rise indicates the CDC pipeline is falling behind."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before running the demo, make sure that:\n\n* Redis is running and accessible. By default, the demo connects to `localhost:6379`.\n* The [.NET 8 SDK](https://dotnet.microsoft.com/download) (or newer) is installed:\n\n[code example]\n\nThe project file pins `StackExchange.Redis` at 2.7+, which `dotnet run` restores automatically on first invocation.\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of five files. Download them from the [`dotnet` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/prefetch-cache/dotnet) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From that directory:\n\n[code example]\n\nYou should see something like:\n\n[code example]\n\nAfter starting the server, visit `http://localhost:8787`.\n\nThe demo server uses ASP.NET Core minimal APIs and only standard .NET threading primitives:\n\n* `WebApplication.CreateBuilder()` for HTTP routing\n* `BlockingCollection<T>` for the change-event queue\n* `Thread` + `ManualResetEventSlim` for the sync worker\n\nIt exposes a small interactive page where you can:\n\n* See which IDs are in the cache and in the primary, side by side\n* Read a category through the cache and confirm every read is a hit\n* Update a field on the primary and watch the sync worker rewrite the cache hash\n* Add and delete categories and watch them appear and disappear from the cache\n* Invalidate one key or clear the entire cache to simulate a sync-pipeline failure\n* Re-prefetch from the primary to recover from a broken cache state\n* Watch the average sync lag, and confirm primary reads stay at one until you re-prefetch — each `/reprefetch` adds another primary read for the snapshot, but normal request traffic never reaches the primary at all"},{"id":"the-mock-primary-store","title":"The mock primary store","role":"content","text":"To make the demo self-contained, the example includes a `MockPrimaryStore` that stands in for a source-of-truth database\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/prefetch-cache/dotnet/MockPrimaryStore.cs)):\n\n[code example]\n\nEvery mutation appends a `ChangeEvent` to an in-process [`BlockingCollection<T>`](https://learn.microsoft.com/dotnet/api/system.collections.concurrent.blockingcollection-1). The sync worker drains the queue with a 50 ms timeout and applies each event to Redis. The emit happens while the mutation lock is held so two concurrent updates cannot interleave their event order on the queue. In a real system this queue is replaced by a CDC pipeline — RDI on Redis Enterprise or Debezium with a Redis consumer on open-source Redis."},{"id":"production-usage","title":"Production usage","role":"content","text":"This guide uses a deliberately small local demo so you can focus on the prefetch-cache pattern. In production, you will usually want to harden several aspects of it."},{"id":"replace-the-in-process-change-queue-with-a-real-cdc-pipeline","title":"Replace the in-process change queue with a real CDC pipeline","role":"content","text":"The demo's in-process queue is the simplest possible stand-in for a CDC change feed. In production, the change feed lives outside the application process: an RDI pipeline configured against your primary database, Debezium connectors writing to Kafka or a Redis stream, or your application explicitly publishing change events from the write path. Whatever you choose, the consumer side stays the same — read events, apply them to Redis, advance the offset."},{"id":"use-a-long-safety-net-ttl-not-a-freshness-ttl","title":"Use a long safety-net TTL, not a freshness TTL","role":"content","text":"The TTL on each cache key is a **safety net**: it bounds memory if the sync pipeline silently stops, so a stuck consumer cannot leave stale data in Redis indefinitely. The TTL is not the freshness mechanism — freshness comes from the sync worker, which refreshes the TTL on every add or update event (delete events remove the key). Pick a TTL that is comfortably longer than your worst-case sync lag plus your alerting window, so a transient sync hiccup never expires hot keys."},{"id":"decide-what-to-do-on-a-cache-miss","title":"Decide what to do on a cache miss","role":"content","text":"A prefetch cache treats a miss as an error or a missing record. The two reasonable strategies are:\n\n* **Return a 404 to the user.** Appropriate when the cache is authoritative for the lookup — for example, when the user is asking for a category by ID and the ID is not in the cache.\n* **Page on-call.** A sustained miss rate on IDs you know exist is an incident: either the prefetch did not run, or the sync pipeline is broken.\n\nWhichever you choose, do not fall back to the primary on the read path — that is what cache-aside is for, and conflating the two patterns breaks the load-isolation guarantee that prefetch provides."},{"id":"bound-the-working-set-to-what-fits-in-memory","title":"Bound the working set to what fits in memory","role":"content","text":"Prefetch only works if the entire dataset fits in Redis memory with headroom. Estimate the size of your reference data, multiply by a growth factor, and confirm the result fits within your Redis instance's `maxmemory` minus what other use cases need. If the working set grows beyond what Redis can hold, switch the dataset to a cache-aside pattern instead — the request path will pay miss latency, but you will not OOM."},{"id":"reconcile-periodically-against-the-primary","title":"Reconcile periodically against the primary","role":"content","text":"CDC pipelines are eventually consistent: an event can be lost (broker outage, consumer crash, configuration drift) and the cache can silently diverge from the source. Run a periodic reconciliation job that re-reads all primary records, compares them against the cache, and either re-prefetches or fixes individual entries. Even running it once a day catches drift that ad-hoc inspection would miss."},{"id":"namespace-cache-keys-in-shared-redis-deployments","title":"Namespace cache keys in shared Redis deployments","role":"content","text":"If multiple applications share a Redis deployment, prefix cache keys with the application name (`cache:billing:category:{id}`) so different services cannot clobber each other's entries. The helper takes a `prefix` argument exactly for this."},{"id":"prefer-async-on-hot-paths","title":"Prefer async on hot paths","role":"content","text":"The demo helper is synchronous (`HashGetAll`, `KeyDelete`, etc.) to keep the example compact. .NET's `ThreadPool` grows by only a couple of threads per second under load, so a synchronous helper combined with many concurrent HTTP handlers can starve workers and produce false cache-misses during traffic spikes. The demo works around this by calling `ThreadPool.SetMinThreads(64, 64)` at startup; a production helper would expose `async` methods (`HashGetAllAsync`, `KeyDeleteAsync`, `await Task.Delay`) and route requests through an async pipeline end-to-end. That removes the synchronous-blocking risk entirely and is the idiomatic shape for ASP.NET Core handlers."},{"id":"use-tickcount64-not-tickcount-for-any-deadline-arithmetic","title":"Use TickCount64 (not TickCount) for any deadline arithmetic","role":"content","text":"If you add timeout/deadline logic to your sync worker or maintenance handlers, use `Environment.TickCount64`, never `Environment.TickCount`. The 32-bit variant wraps every 24.9 days and adding a positive offset near the wraparound boundary produces a negative deadline that immediately exits the polling loop. The 64-bit variant has no practical wrap interval."},{"id":"inspect-cached-entries-directly-in-redis","title":"Inspect cached entries directly in Redis","role":"content","text":"When testing or troubleshooting, inspect the stored cache keys directly to confirm the bulk load and the sync worker are writing what you expect:\n\n[code example]\n\nIf a key is missing for an ID that still exists in the primary, the prefetch did not run, the key expired without a sync refresh, or someone invalidated it. If a key is still present for an ID that was deleted in the primary, the delete event has not yet been applied. If the TTL is much lower than the configured safety-net value on a hot key, the sync worker is not keeping up."},{"id":"learn-more","title":"Learn more","role":"related","text":"* [StackExchange.Redis documentation](https://seredis.dev/) - Install and use the StackExchange.Redis client\n* [HSET command](https://redis.io/docs/latest/commands/hset) - Write hash fields\n* [HGETALL command](https://redis.io/docs/latest/commands/hgetall) - Read every field of a hash\n* [EXPIRE command](https://redis.io/docs/latest/commands/expire) - Set key expiration in seconds\n* [DEL command](https://redis.io/docs/latest/commands/del) - Delete a key on invalidation or sync-delete\n* [SCAN command](https://redis.io/docs/latest/commands/scan) - Iterate the cached keyspace without blocking the server\n* [TTL command](https://redis.io/docs/latest/commands/ttl) - Inspect remaining safety-net time on a key\n* [Redis Data Integration](https://redis.io/docs/latest/integrate/redis-data-integration) - Configuration-driven CDC into Redis on Redis Enterprise and Redis Cloud"}],"examples":[{"id":"the-prefetch-cache-helper-ex0","language":"csharp","code":"using StackExchange.Redis;\nusing PrefetchCacheDemo;\n\nvar redis = ConnectionMultiplexer.Connect(\"localhost:6379\");\nvar primary = new MockPrimaryStore();\nvar cache = new PrefetchCache(redis.GetDatabase(), ttlSeconds: 3600);\n\n// Pre-load every primary record into Redis in one pipelined round trip.\ncache.BulkLoad(primary.ListRecords());\n\n// Start the sync worker so primary mutations propagate into Redis.\nvar sync = new SyncWorker(primary, cache);\nsync.Start();\n\n// Read paths now go to Redis only.\nvar result = cache.Get(\"cat-001\");","section_id":"the-prefetch-cache-helper"},{"id":"data-model-ex0","language":"text","code":"cache:category:cat-001\n  id            = cat-001\n  name          = Beverages\n  display_order = 1\n  featured      = true\n  parent_id     =","section_id":"data-model"},{"id":"bulk-load-on-startup-ex0","language":"csharp","code":"public int BulkLoad(IEnumerable<Dictionary<string, string>> records)\n{\n    var batch = _db.CreateBatch();\n    var tasks = new List<Task>();\n    var loaded = 0;\n    foreach (var record in records)\n    {\n        if (!record.TryGetValue(\"id\", out var entityId) || string.IsNullOrEmpty(entityId)) continue;\n        var cacheKey = CacheKey(entityId);\n        tasks.Add(batch.KeyDeleteAsync(cacheKey));\n        tasks.Add(batch.HashSetAsync(\n            cacheKey,\n            record.Select(p => new HashEntry(p.Key, p.Value)).ToArray()));\n        tasks.Add(batch.KeyExpireAsync(cacheKey, TimeSpan.FromSeconds(_ttlSeconds)));\n        loaded++;\n    }\n    if (loaded > 0)\n    {\n        batch.Execute();\n        Task.WaitAll(tasks.ToArray());\n    }\n    return loaded;\n}","section_id":"bulk-load-on-startup"},{"id":"reads-from-redis-only-ex0","language":"csharp","code":"public ReadResult Get(string entityId)\n{\n    var cacheKey = CacheKey(entityId);\n    var sw = System.Diagnostics.Stopwatch.StartNew();\n    var entries = _db.HashGetAll(cacheKey);\n    sw.Stop();\n    var redisLatencyMs = sw.Elapsed.TotalMilliseconds;\n\n    if (entries.Length > 0)\n    {\n        lock (_statsLock) { _hits++; }\n        return new ReadResult(ToDict(entries), Hit: true, redisLatencyMs);\n    }\n\n    lock (_statsLock) { _misses++; }\n    return new ReadResult(null, Hit: false, redisLatencyMs);\n}","section_id":"reads-from-redis-only"},{"id":"applying-sync-events-ex0","language":"csharp","code":"public void ApplyChange(ChangeEvent change)\n{\n    if (string.IsNullOrEmpty(change.Id)) return;\n    var cacheKey = CacheKey(change.Id);\n\n    if (change.Op == ChangeOp.Upsert)\n    {\n        if (change.Fields is null || change.Fields.Count == 0) return;\n        var tx = _db.CreateTransaction();\n        _ = tx.KeyDeleteAsync(cacheKey);\n        _ = tx.HashSetAsync(\n            cacheKey,\n            change.Fields.Select(p => new HashEntry(p.Key, p.Value)).ToArray());\n        _ = tx.KeyExpireAsync(cacheKey, TimeSpan.FromSeconds(_ttlSeconds));\n        tx.Execute();\n    }\n    else if (change.Op == ChangeOp.Delete)\n    {\n        _db.KeyDelete(cacheKey);\n    }\n}","section_id":"applying-sync-events"},{"id":"the-sync-worker-ex0","language":"csharp","code":"private void Run()\n{\n    while (!_stopEvent.IsSet)\n    {\n        if (_pauseEvent.IsSet)\n        {\n            _pausedIdleEvent.Set();\n            while (_pauseEvent.IsSet && !_stopEvent.IsSet)\n            {\n                _stopEvent.Wait(_pollTimeout);\n            }\n            _pausedIdleEvent.Reset();\n            continue;\n        }\n\n        var change = _primary.NextChange(_pollTimeout);\n        if (change is null) continue;\n        try { _cache.ApplyChange(change); }\n        catch (Exception ex)\n        {\n            Console.Error.WriteLine($\"[sync] failed to apply {change}: {ex.Message}\");\n        }\n    }\n}","section_id":"the-sync-worker"},{"id":"re-prefetch-under-load-ex0","language":"csharp","code":"sync.Pause();\ntry\n{\n    cache.Clear();\n    cache.BulkLoad(primary.ListRecords());\n}\nfinally\n{\n    sync.Resume();\n}","section_id":"re-prefetch-under-load"},{"id":"hitmiss-accounting-ex0","language":"csharp","code":"public Dictionary<string, object> Stats()\n{\n    lock (_statsLock)\n    {\n        var total = _hits + _misses;\n        var hitRate = total == 0 ? 0.0 : Math.Round(100.0 * _hits / total, 1);\n        var avgLag = _syncLagSamples == 0\n            ? 0.0\n            : Math.Round(_syncLagMsTotal / _syncLagSamples, 2);\n        return new Dictionary<string, object>\n        {\n            [\"hits\"] = _hits,\n            [\"misses\"] = _misses,\n            [\"hit_rate_pct\"] = hitRate,\n            [\"prefetched\"] = _prefetched,\n            [\"sync_events_applied\"] = _syncEventsApplied,\n            [\"sync_lag_ms_avg\"] = avgLag,\n        };\n    }\n}","section_id":"hitmiss-accounting"},{"id":"prerequisites-ex0","language":"bash","code":"dotnet --version","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir prefetch-cache-demo && cd prefetch-cache-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/prefetch-cache/dotnet\ncurl -O $BASE/PrefetchCacheDemo.csproj\ncurl -O $BASE/PrefetchCache.cs\ncurl -O $BASE/MockPrimaryStore.cs\ncurl -O $BASE/SyncWorker.cs\ncurl -O $BASE/Program.cs","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"dotnet run","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"Redis prefetch-cache demo server listening on http://127.0.0.1:8787\nUsing Redis at localhost:6379 with cache prefix 'cache:category:' and TTL 3600s\nPrefetched 5 records in 92.4 ms; sync worker running","section_id":"start-the-demo-server"},{"id":"the-mock-primary-store-ex0","language":"csharp","code":"public class MockPrimaryStore\n{\n    public MockPrimaryStore(int readLatencyMs = 80) { ... }\n\n    public List<Dictionary<string, string>> ListRecords()\n    {\n        Thread.Sleep(ReadLatencyMs);\n        ...\n    }\n\n    public bool UpdateField(string entityId, string field, string value)\n    {\n        lock (_lock)\n        {\n            ...\n            EmitChangeLocked(ChangeOp.Upsert, entityId, snapshot);\n        }\n        return true;\n    }\n}","section_id":"the-mock-primary-store"},{"id":"inspect-cached-entries-directly-in-redis-ex0","language":"bash","code":"redis-cli --scan --pattern 'cache:category:*'\nredis-cli HGETALL cache:category:cat-001\nredis-cli TTL cache:category:cat-001","section_id":"inspect-cached-entries-directly-in-redis"}]}
{"schema_version":2,"id":"develop/use-cases/prefetch-cache/go","title":"Redis prefetch cache with go-redis","url":"https://redis.io/docs/latest/develop/use-cases/prefetch-cache/go/","summary":"Implement a Redis prefetch cache in Go with go-redis","aliases":["/develop/use-cases/prefetch-cache/go-redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:54:57+01:00","children":[],"page_type":"content","content_hash":"d90e45db74f18b8a5fb470ad6eff680b8e13853fd387dff06f6d9a154f96eb24","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis prefetch cache in Go with [`go-redis`](https://redis.io/docs/latest/develop/clients/go). It includes a small local web server built with Go's standard `net/http` package so you can watch the cache pre-load at startup, see a background sync worker apply primary mutations within milliseconds, and break the cache to confirm that reads never fall back to the primary."},{"id":"overview","title":"Overview","role":"overview","text":"Prefetch caching pre-loads a working set of reference data into Redis before the first request arrives, so every read on the request path is a cache hit. A separate sync worker keeps the cache current as the source of truth changes — there is no fall-back to the primary on the read path.\n\nThat gives you:\n\n* Near-100% cache hit ratios for reference and master data\n* Sub-millisecond reads for lookup-heavy paths at peak traffic\n* All reference-data reads offloaded from the primary database\n* Source-database changes propagated into Redis within a few milliseconds\n* A long safety-net TTL that bounds memory if the sync pipeline ever stops\n\nIn this example, each cached category is stored as a Redis hash under a key like `cache:category:{id}`. The hash holds the category fields (`id`, `name`, `display_order`, `featured`, `parent_id`) and the key has a long safety-net TTL that the sync worker refreshes on every add or update event. Delete events remove the cache key outright, so there is no TTL to refresh in that case."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow has three independent paths:\n\n1. **On startup**, the demo server calls `cache.BulkLoad(ctx, primary.ListRecords())`, which pipelines `DEL` + `HSET` + `EXPIRE` for every record in one round trip.\n2. **On every read**, the application calls `cache.Get(ctx, id)`, which runs `HGETALL` against Redis only. A miss is treated as an error, not a trigger to query the primary.\n3. **On every primary mutation**, the primary appends a change event to an in-process channel. A sync-worker goroutine drains the channel and calls `cache.ApplyChange(ctx, event)`. For an `upsert`, the helper rewrites the cache hash and refreshes the safety-net TTL; for a `delete`, it removes the cache key.\n\nIn a real system the in-process channel is replaced by a CDC pipeline — [Redis Data Integration](https://redis.io/docs/latest/integrate/redis-data-integration), Debezium plus a lightweight consumer, or an equivalent tool that tails the source's binlog/WAL and pushes events into Redis."},{"id":"the-prefetch-cache-helper","title":"The prefetch-cache helper","role":"content","text":"The `PrefetchCache` type wraps the cache operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/prefetch-cache/go/cache.go)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each cached category is stored in a Redis hash:\n\n[code example]\n\nThe implementation uses:\n\n* [`HSET`](https://redis.io/docs/latest/commands/hset) + [`EXPIRE`](https://redis.io/docs/latest/commands/expire), pipelined, for the bulk load and every sync event\n* [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) on the read path\n* [`DEL`](https://redis.io/docs/latest/commands/del) for sync-delete events and explicit invalidation\n* [`SCAN`](https://redis.io/docs/latest/commands/scan) to enumerate the cached keyspace and to clear the prefix\n* [`TTL`](https://redis.io/docs/latest/commands/ttl) to surface remaining safety-net time in the demo UI"},{"id":"bulk-load-on-startup","title":"Bulk load on startup","role":"content","text":"`BulkLoad` pipelines a `DEL` + `HSET` + `EXPIRE` triple for every record. The pipeline is sent in a single round trip, so loading thousands of records takes one network RTT plus the time Redis spends executing the commands locally — typically tens of milliseconds even for a large reference table:\n\n[code example]\n\nThe pipeline uses `client.Pipeline()` (non-transactional) on the **startup** path: nothing is reading the cache yet, the records do not need to be applied atomically as a set, and skipping `MULTI`/`EXEC` keeps the bulk load fast. The same method is used for the live `/reprefetch` reload, which is safe because the demo pauses the sync worker around the clear-and-reload sequence — see [Re-prefetch under load](#re-prefetch-under-load) below. If you call `BulkLoad` directly from your own code on a cache that is already serving reads, either pause your writers first or rewrite it with `client.TxPipeline()` so callers cannot observe a half-loaded record."},{"id":"reads-from-redis-only","title":"Reads from Redis only","role":"content","text":"`Get` runs `HGETALL` and returns the cached hash. **It does not fall back to the primary on a miss.** In a healthy system, a miss never happens; if it does, the application surfaces it as an error and treats it as a sync-pipeline incident:\n\n[code example]\n\nThis is the key behavioural difference from [cache-aside](https://redis.io/docs/latest/develop/use-cases/cache-aside): the request path never touches the primary, so reference-data reads cannot contribute to primary database load."},{"id":"applying-sync-events","title":"Applying sync events","role":"content","text":"The sync worker calls `ApplyChange` for every primary mutation. For an `upsert`, the helper rewrites the cache hash and refreshes the safety-net TTL in one pipelined transaction so the cache never holds a stale mix of old and new fields. For a `delete`, it removes the cache key:\n\n[code example]\n\nThe `DEL` before the `HSET` ensures the cached hash contains exactly the fields the primary record has now — fields that have been dropped from the primary will not linger in Redis. `TxPipeline` wraps the three commands in `MULTI`/`EXEC` so concurrent readers can never observe the half-written intermediate state."},{"id":"the-sync-worker","title":"The sync worker","role":"content","text":"`SyncWorker` runs a single goroutine that blocks on the primary's change channel with a short timeout. Every change is applied to Redis as soon as it arrives\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/prefetch-cache/go/sync_worker.go)):\n\n[code example]\n\nPause and resume are coordinated through two channels stored on the worker:\n\n* `pausedIdle` is closed by the worker when the run loop has parked itself. `Pause()` waits on this channel so it can prove no `ApplyChange` is in flight before returning.\n* `resumeCh` is closed by `Resume()` to wake the parked select. Both channels are replaced with fresh values on each `Pause()` so a stale `Resume` from a previous cycle cannot prematurely unblock the next pause.\n\nIn production this loop is replaced by a CDC consumer reading from RDI's Redis output stream, Debezium's Kafka topic, or an equivalent change feed. The shape stays the same: drain events, apply them to Redis, advance the consumer offset."},{"id":"invalidation-and-re-prefetch","title":"Invalidation and re-prefetch","role":"content","text":"Two helpers exist for testing and recovery:\n\n* `Invalidate(ctx, id)` deletes a single cache key. The demo uses it to simulate a sync-pipeline failure on one record.\n* `Clear(ctx)` runs `SCAN MATCH cache:category:*` and deletes every key under the prefix. The demo uses it to simulate a full cache loss.\n\nIn both cases, the recovery path is to call `BulkLoad(ctx, primary.ListRecords())` again — re-prefetching from the primary. The demo exposes this as the \"Re-prefetch\" button so you can see the cache come back to a fully-warm state in one operation."},{"id":"re-prefetch-under-load","title":"Re-prefetch under load","role":"content","text":"`Clear()` and `BulkLoad()` are not atomic against the sync worker. If a change event arrives between the snapshot (`primary.ListRecords()`) and the bulk write, the bulk write can overwrite a newer value; if a change event arrives between `Clear()`'s `SCAN` and `DEL`, the cleared entry can immediately be recreated. The demo's `/clear` and `/reprefetch` handlers solve this by pausing the sync worker around the operation:\n\n[code example]\n\n`Pause()` waits for the worker goroutine to finish whatever event it is currently applying, parks the run loop, and returns. Change events that arrive during the pause sit on the primary's channel and apply in order once `Resume()` is called, so no event is lost. The demo also wraps the pause/resume pair in a `sync.Mutex` so two concurrent admin callers cannot interleave their pause/resume cycles."},{"id":"hitmiss-accounting","title":"Hit/miss accounting","role":"content","text":"The helper keeps in-process counters for hits, misses, prefetched records, sync events applied, and the average lag between a primary change and its application to Redis. The demo UI surfaces these so you can confirm the cache is absorbing all reads and the sync worker is keeping up:\n\n[code example]\n\nIn production you would emit these as Prometheus counters and gauges. The sync-lag metric is the most important: a sudden rise indicates the CDC pipeline is falling behind."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* Redis running and accessible. By default, the demo connects to `localhost:6379`.\n* Go 1.21 or later.\n* The `go-redis` client. The included `go.mod` pins:\n\n  [code example]\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of five files. Download them from the [`go` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/prefetch-cache/go) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"The helper, mock primary, sync worker, and demo handlers all live in `package prefetchcache`. Go's `package main` can't live in the same directory as another package, so create a tiny `main.go` shim in a subdirectory that calls into the package:\n\n[code example]\n\nThen build and run:\n\n[code example]\n\nYou should see something like:\n\n[code example]\n\nAfter starting the server, visit [http://localhost:8784](http://localhost:8784).\n\nThe demo server uses only Go's standard library plus `go-redis`:\n\n* [`net/http`](https://pkg.go.dev/net/http) for the web server\n* [`flag`](https://pkg.go.dev/flag) for CLI flags\n* Goroutines, channels, and `sync.Mutex` for the sync worker and stats counters\n\nIt exposes a small interactive page where you can:\n\n* See which IDs are in the cache and in the primary, side by side\n* Read a category through the cache and confirm every read is a hit\n* Update a field on the primary and watch the sync worker rewrite the cache hash\n* Add and delete categories and watch them appear and disappear from the cache\n* Invalidate one key or clear the entire cache to simulate a sync-pipeline failure\n* Re-prefetch from the primary to recover from a broken cache state\n* Watch the average sync lag, and confirm primary reads stay at one until you re-prefetch — each `/reprefetch` adds another primary read for the snapshot, but normal request traffic never reaches the primary at all\n\nIf you want to run the demo against a non-default cache prefix or port, pass `--port` and `--cache-prefix`:\n\n[code example]"},{"id":"the-mock-primary-store","title":"The mock primary store","role":"content","text":"To make the demo self-contained, the example includes a `MockPrimaryStore` that stands in for a source-of-truth database\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/prefetch-cache/go/primary.go)):\n\n[code example]\n\nEvery mutation appends a change event to an in-process buffered `chan Change`. The sync worker drains the channel with a 50 ms timeout via `NextChange` and applies each event to Redis. The change event is **emitted while the record lock is still held** (`emitChangeLocked` runs inside the `mu.Lock()` block) so two concurrent `UpdateField` calls cannot produce out-of-order events on the channel.\n\nIn a real system this channel is replaced by a CDC pipeline — RDI on Redis Enterprise or Debezium with a Redis consumer on open-source Redis."},{"id":"production-usage","title":"Production usage","role":"content","text":"This guide uses a deliberately small local demo so you can focus on the prefetch-cache pattern. In production, you will usually want to harden several aspects of it."},{"id":"replace-the-in-process-change-channel-with-a-real-cdc-pipeline","title":"Replace the in-process change channel with a real CDC pipeline","role":"content","text":"The demo's in-process channel is the simplest possible stand-in for a CDC change feed. In production, the change feed lives outside the application process: an RDI pipeline configured against your primary database, Debezium connectors writing to Kafka or a Redis stream, or your application explicitly publishing change events from the write path. Whatever you choose, the consumer side stays the same — read events, apply them to Redis, advance the offset."},{"id":"use-a-long-safety-net-ttl-not-a-freshness-ttl","title":"Use a long safety-net TTL, not a freshness TTL","role":"content","text":"The TTL on each cache key is a **safety net**: it bounds memory if the sync pipeline silently stops, so a stuck consumer cannot leave stale data in Redis indefinitely. The TTL is not the freshness mechanism — freshness comes from the sync worker, which refreshes the TTL on every add or update event (delete events remove the key). Pick a TTL that is comfortably longer than your worst-case sync lag plus your alerting window, so a transient sync hiccup never expires hot keys."},{"id":"decide-what-to-do-on-a-cache-miss","title":"Decide what to do on a cache miss","role":"content","text":"A prefetch cache treats a miss as an error or a missing record. The two reasonable strategies are:\n\n* **Return a 404 to the user.** Appropriate when the cache is authoritative for the lookup — for example, when the user is asking for a category by ID and the ID is not in the cache.\n* **Page on-call.** A sustained miss rate on IDs you know exist is an incident: either the prefetch did not run, or the sync pipeline is broken.\n\nWhichever you choose, do not fall back to the primary on the read path — that is what cache-aside is for, and conflating the two patterns breaks the load-isolation guarantee that prefetch provides."},{"id":"bound-the-working-set-to-what-fits-in-memory","title":"Bound the working set to what fits in memory","role":"content","text":"Prefetch only works if the entire dataset fits in Redis memory with headroom. Estimate the size of your reference data, multiply by a growth factor, and confirm the result fits within your Redis instance's `maxmemory` minus what other use cases need. If the working set grows beyond what Redis can hold, switch the dataset to a cache-aside pattern instead — the request path will pay miss latency, but you will not OOM."},{"id":"reconcile-periodically-against-the-primary","title":"Reconcile periodically against the primary","role":"content","text":"CDC pipelines are eventually consistent: an event can be lost (broker outage, consumer crash, configuration drift) and the cache can silently diverge from the source. Run a periodic reconciliation job that re-reads all primary records, compares them against the cache, and either re-prefetches or fixes individual entries. Even running it once a day catches drift that ad-hoc inspection would miss."},{"id":"namespace-cache-keys-in-shared-redis-deployments","title":"Namespace cache keys in shared Redis deployments","role":"content","text":"If multiple applications share a Redis deployment, prefix cache keys with the application name (`cache:billing:category:{id}`) so different services cannot clobber each other's entries. The helper takes a `prefix` constructor argument exactly for this."},{"id":"wire-shutdown-through-contextcontext","title":"Wire shutdown through `context.Context`","role":"content","text":"The sync worker runs on its own goroutine that blocks in `NextChange` (a channel select with a 50 ms timeout). The demo's `RunDemoServer` calls `syncWorker.Stop(2 * time.Second)` on SIGINT/SIGTERM, which cancels the worker's internal context and joins the goroutine. Wire your real sync worker to your service's shutdown context so `SIGTERM` produces a clean drain instead of a hard kill."},{"id":"inspect-cached-entries-directly-in-redis","title":"Inspect cached entries directly in Redis","role":"content","text":"When testing or troubleshooting, inspect the stored cache keys directly to confirm the bulk load and the sync worker are writing what you expect:\n\n[code example]\n\nIf a key is missing for an ID that still exists in the primary, the prefetch did not run, the key expired without a sync refresh, or someone invalidated it. If a key is still present for an ID that was deleted in the primary, the delete event has not yet been applied. If the TTL is much lower than the configured safety-net value on a hot key, the sync worker is not keeping up."},{"id":"learn-more","title":"Learn more","role":"related","text":"* [go-redis guide](https://redis.io/docs/latest/develop/clients/go) - Install and use the Go Redis client\n* [HSET command](https://redis.io/docs/latest/commands/hset) - Write hash fields\n* [HGETALL command](https://redis.io/docs/latest/commands/hgetall) - Read every field of a hash\n* [EXPIRE command](https://redis.io/docs/latest/commands/expire) - Set key expiration in seconds\n* [DEL command](https://redis.io/docs/latest/commands/del) - Delete a key on invalidation or sync-delete\n* [SCAN command](https://redis.io/docs/latest/commands/scan) - Iterate the cached keyspace without blocking the server\n* [TTL command](https://redis.io/docs/latest/commands/ttl) - Inspect remaining safety-net time on a key\n* [Redis Data Integration](https://redis.io/docs/latest/integrate/redis-data-integration) - Configuration-driven CDC into Redis on Redis Enterprise and Redis Cloud"}],"examples":[{"id":"the-prefetch-cache-helper-ex0","language":"go","code":"package main\n\nimport (\n    \"context\"\n\n    \"github.com/redis/go-redis/v9\"\n    \"prefetchcache\"\n)\n\nfunc main() {\n    client := redis.NewClient(&redis.Options{Addr: \"localhost:6379\"})\n    primary := prefetchcache.NewMockPrimaryStore(80)\n    cache := prefetchcache.NewPrefetchCache(client, \"cache:category:\", 3600)\n\n    ctx := context.Background()\n\n    // Pre-load every primary record into Redis in one pipelined round trip.\n    _, _ = cache.BulkLoad(ctx, primary.ListRecords())\n\n    // Start the sync worker so primary mutations propagate into Redis.\n    sync := prefetchcache.NewSyncWorker(primary, cache)\n    sync.Start()\n    defer sync.Stop(2 * time.Second)\n\n    // Read paths now go to Redis only.\n    result, _ := cache.Get(ctx, \"cat-001\")\n    _ = result\n}","section_id":"the-prefetch-cache-helper"},{"id":"data-model-ex0","language":"text","code":"cache:category:cat-001\n  id            = cat-001\n  name          = Beverages\n  display_order = 1\n  featured      = true\n  parent_id     =","section_id":"data-model"},{"id":"bulk-load-on-startup-ex0","language":"go","code":"func (c *PrefetchCache) BulkLoad(ctx context.Context, records []map[string]string) (int, error) {\n    loaded := 0\n    pipe := c.client.Pipeline()\n    for _, record := range records {\n        id := record[\"id\"]\n        if id == \"\" {\n            continue\n        }\n        cacheKey := c.cacheKey(id)\n        pipe.Del(ctx, cacheKey)\n        pipe.HSet(ctx, cacheKey, hashFields(record)...)\n        pipe.Expire(ctx, cacheKey, time.Duration(c.ttlSeconds)*time.Second)\n        loaded++\n    }\n    if loaded > 0 {\n        if _, err := pipe.Exec(ctx); err != nil {\n            return 0, err\n        }\n    }\n    return loaded, nil\n}","section_id":"bulk-load-on-startup"},{"id":"reads-from-redis-only-ex0","language":"go","code":"func (c *PrefetchCache) Get(ctx context.Context, id string) (GetResult, error) {\n    cacheKey := c.cacheKey(id)\n    started := time.Now()\n    cached, err := c.client.HGetAll(ctx, cacheKey).Result()\n    latencyMs := float64(time.Since(started).Microseconds()) / 1000.0\n    if err != nil {\n        return GetResult{RedisLatencyMs: latencyMs}, err\n    }\n    if len(cached) > 0 {\n        c.recordHit()\n        return GetResult{Record: cached, Hit: true, RedisLatencyMs: latencyMs}, nil\n    }\n    c.recordMiss()\n    return GetResult{Record: nil, Hit: false, RedisLatencyMs: latencyMs}, nil\n}","section_id":"reads-from-redis-only"},{"id":"applying-sync-events-ex0","language":"go","code":"func (c *PrefetchCache) ApplyChange(ctx context.Context, change Change) error {\n    if change.ID == \"\" {\n        return nil\n    }\n    cacheKey := c.cacheKey(change.ID)\n\n    switch change.Op {\n    case ChangeOpUpsert:\n        if len(change.Fields) == 0 {\n            // Malformed upsert with no fields. Skip rather than crash\n            // the sync worker: HSET with an empty mapping errors, and\n            // there's nothing to write anyway.\n            return nil\n        }\n        pipe := c.client.TxPipeline()\n        pipe.Del(ctx, cacheKey)\n        pipe.HSet(ctx, cacheKey, hashFields(change.Fields)...)\n        pipe.Expire(ctx, cacheKey, time.Duration(c.ttlSeconds)*time.Second)\n        if _, err := pipe.Exec(ctx); err != nil {\n            return err\n        }\n    case ChangeOpDelete:\n        if err := c.client.Del(ctx, cacheKey).Err(); err != nil {\n            return err\n        }\n    default:\n        return nil\n    }\n    return nil\n}","section_id":"applying-sync-events"},{"id":"the-sync-worker-ex0","language":"go","code":"func (w *SyncWorker) run(ctx context.Context, done chan struct{}) {\n    defer close(done)\n    for {\n        select {\n        case <-ctx.Done():\n            return\n        default:\n        }\n\n        // ... park here if paused ...\n\n        change, ok := w.primary.NextChange(w.pollTimeout)\n        if !ok {\n            continue\n        }\n        if err := w.cache.ApplyChange(ctx, change); err != nil {\n            log.Printf(\"[sync] failed to apply %s %s: %v\", change.Op, change.ID, err)\n        }\n    }\n}","section_id":"the-sync-worker"},{"id":"re-prefetch-under-load-ex0","language":"go","code":"s.sync.Pause(2 * time.Second)\n_, _ = s.cache.Clear(ctx)\nloaded, _ := s.cache.BulkLoad(ctx, s.primary.ListRecords())\ns.sync.Resume()","section_id":"re-prefetch-under-load"},{"id":"hitmiss-accounting-ex0","language":"go","code":"func (c *PrefetchCache) Stats() map[string]any {\n    c.mu.Lock()\n    defer c.mu.Unlock()\n    total := c.hits + c.misses\n    hitRate := 0.0\n    if total > 0 {\n        hitRate = roundTo(100.0*float64(c.hits)/float64(total), 1)\n    }\n    avgLag := 0.0\n    if c.syncLagSamples > 0 {\n        avgLag = roundTo(c.syncLagMsTotal/float64(c.syncLagSamples), 2)\n    }\n    return map[string]any{\n        \"hits\":                c.hits,\n        \"misses\":              c.misses,\n        \"hit_rate_pct\":        hitRate,\n        \"prefetched\":          c.prefetched,\n        \"sync_events_applied\": c.syncEventsApplied,\n        \"sync_lag_ms_avg\":     avgLag,\n    }\n}","section_id":"hitmiss-accounting"},{"id":"prerequisites-ex0","language":"text","code":"require github.com/redis/go-redis/v9 v9.18.0","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir prefetch-cache-demo && cd prefetch-cache-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/prefetch-cache/go\ncurl -O $BASE/cache.go\ncurl -O $BASE/primary.go\ncurl -O $BASE/sync_worker.go\ncurl -O $BASE/demo_server.go\ncurl -O $BASE/go.mod\ncurl -O $BASE/go.sum","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"mkdir -p cmd/demo\ncat > cmd/demo/main.go <<'EOF'\npackage main\n\nimport \"prefetchcache\"\n\nfunc main() { prefetchcache.RunDemoServer() }\nEOF","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"bash","code":"go mod tidy\ngo run ./cmd/demo","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex2","language":"text","code":"Redis prefetch-cache demo server listening on http://127.0.0.1:8784\nUsing Redis at localhost:6379 with cache prefix 'cache:category:' and TTL 3600s\nPrefetched 5 records in 83.0 ms; sync worker running","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex3","language":"bash","code":"go run ./cmd/demo --port 8784 --cache-prefix 'cache:category:'","section_id":"start-the-demo-server"},{"id":"the-mock-primary-store-ex0","language":"go","code":"type MockPrimaryStore struct {\n    readLatencyMs int\n\n    mu      sync.Mutex\n    reads   int\n    changes chan Change\n    records map[string]map[string]string\n}\n\nfunc (p *MockPrimaryStore) ListRecords() []map[string]string {\n    time.Sleep(time.Duration(p.readLatencyMs) * time.Millisecond)\n    // ... return a deep copy of every record under p.mu ...\n}\n\nfunc (p *MockPrimaryStore) UpdateField(id, field, value string) bool {\n    p.mu.Lock()\n    defer p.mu.Unlock()\n    rec, ok := p.records[id]\n    if !ok {\n        return false\n    }\n    rec[field] = value\n    p.emitChangeLocked(ChangeOpUpsert, id, copyRecord(rec))\n    return true\n}","section_id":"the-mock-primary-store"},{"id":"inspect-cached-entries-directly-in-redis-ex0","language":"bash","code":"redis-cli --scan --pattern 'cache:category:*'\nredis-cli HGETALL cache:category:cat-001\nredis-cli TTL cache:category:cat-001","section_id":"inspect-cached-entries-directly-in-redis"}]}
{"schema_version":2,"id":"develop/use-cases/prefetch-cache","title":"Redis prefetch cache","url":"https://redis.io/docs/latest/develop/use-cases/prefetch-cache/","summary":"Pre-load reference data into Redis so every read is a cache hit.","tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:54:57+01:00","children":[{"id":"develop/use-cases/prefetch-cache/redis-py","summary":"Implement a Redis prefetch cache in Python with redis-py","title":"Redis prefetch cache with redis-py","url":"https://redis.io/docs/latest/develop/use-cases/prefetch-cache/redis-py/"},{"id":"develop/use-cases/prefetch-cache/nodejs","summary":"Implement a Redis prefetch cache in Node.js with node-redis","title":"Redis prefetch cache with node-redis","url":"https://redis.io/docs/latest/develop/use-cases/prefetch-cache/nodejs/"},{"id":"develop/use-cases/prefetch-cache/go","summary":"Implement a Redis prefetch cache in Go with go-redis","title":"Redis prefetch cache with go-redis","url":"https://redis.io/docs/latest/develop/use-cases/prefetch-cache/go/"},{"id":"develop/use-cases/prefetch-cache/java-jedis","summary":"Implement a Redis prefetch cache in Java with Jedis","title":"Redis prefetch cache with Jedis","url":"https://redis.io/docs/latest/develop/use-cases/prefetch-cache/java-jedis/"},{"id":"develop/use-cases/prefetch-cache/java-lettuce","summary":"Implement a Redis prefetch cache in Java with Lettuce","title":"Redis prefetch cache with Lettuce","url":"https://redis.io/docs/latest/develop/use-cases/prefetch-cache/java-lettuce/"},{"id":"develop/use-cases/prefetch-cache/dotnet","summary":"Implement a Redis prefetch cache in C# with StackExchange.Redis","title":"Redis prefetch cache with StackExchange.Redis","url":"https://redis.io/docs/latest/develop/use-cases/prefetch-cache/dotnet/"},{"id":"develop/use-cases/prefetch-cache/php","summary":"Implement a Redis prefetch cache in PHP with Predis","title":"Redis prefetch cache with Predis","url":"https://redis.io/docs/latest/develop/use-cases/prefetch-cache/php/"},{"id":"develop/use-cases/prefetch-cache/ruby","summary":"Implement a Redis prefetch cache in Ruby with redis-rb","title":"Redis prefetch cache with redis-rb","url":"https://redis.io/docs/latest/develop/use-cases/prefetch-cache/ruby/"},{"id":"develop/use-cases/prefetch-cache/rust","summary":"Implement a Redis prefetch cache in Rust with redis-rs","title":"Redis prefetch cache with redis-rs","url":"https://redis.io/docs/latest/develop/use-cases/prefetch-cache/rust/"}],"page_type":"content","content_hash":"03e6da9d94f9b5b393d400371008a014186a5b524b0f410815aef4db17e8f2e2","sections":[{"id":"when-to-use-redis-prefetch-cache","title":"When to use Redis prefetch cache","role":"overview","text":"Use a Redis prefetch cache when you need to pre-load reference or master data into cache before the first request arrives, so every read is a hit and no request ever falls through to the primary database."},{"id":"why-the-problem-is-hard","title":"Why the problem is hard","role":"content","text":"Cache-aside guarantees cold-start misses: the first request for every key hits the primary, and between TTL expiry and the next read, every service re-fetches the same rows from a slow backend. At scale this creates latency spikes and sustained read pressure on the system of record — the load pattern is worst exactly when traffic is highest.\n\nPrefetch solves this by loading data proactively, but that brings its own constraints. The entire working set must fit in memory, and it must stay current as the source of truth changes. Building and maintaining the sync pipeline from the source database adds engineering cost and ongoing operational burden — once the cache is the only read path, any sync lag becomes a correctness problem rather than a freshness one.\n\nThis pattern is distinct from cache-aside, where the cache populates reactively on miss and the primary is always available as a fall-back. With prefetch, the application assumes the cache is authoritative on the read path; on a miss, it does not fall back to the primary (and a sustained miss rate is treated as an incident). It is also distinct from write-through caching, where every write to the application writes both the cache and the primary in lock-step — prefetch decouples the write path from the cache and lets a separate sync pipeline catch up."},{"id":"what-you-can-expect-from-a-redis-solution","title":"What you can expect from a Redis solution","role":"content","text":"You can:\n\n-   Achieve near-100% cache hit ratios for country codes, product categories, translations, configuration, and other reference tables.\n-   Keep P95 read latency under 1 ms for lookup-heavy request paths at peak traffic (that is to say 95% of requests have a latency of 1 ms or less).\n-   Sync source database changes into cache within seconds using a managed CDC pipeline (such as Redis Data Integration), or a small consumer in front of Debezium, Kafka, or a Redis stream.\n-   Offload all reference-data reads from the primary database, avoiding the cost of dedicated read replicas.\n-   Pre-warm the cache on deploy or restart so cold starts never reach the backend.\n-   Bound memory with a long safety-net TTL that expires entries if the sync pipeline ever stops, so a silent failure never serves stale data forever."},{"id":"how-redis-supports-the-solution","title":"How Redis supports the solution","role":"content","text":"In practice, the application loads the full working set into Redis once at startup using a pipelined bulk write, then a separate sync worker keeps Redis current as the source of truth changes. Every reference-data read goes to Redis only — there is no fall-back path to the primary on the request critical path.\n\nRedis provides the following features that make it a good fit for prefetch caching:\n\n-   [Hashes](https://redis.io/docs/latest/develop/data-types/hashes)\n    ([`HSET`](https://redis.io/docs/latest/commands/hset),\n    [`HGETALL`](https://redis.io/docs/latest/commands/hgetall)) and native\n    [JSON](https://redis.io/docs/latest/develop/data-types/json) documents\n    ([`JSON.SET`](https://redis.io/docs/latest/commands/json.set),\n    [`JSON.GET`](https://redis.io/docs/latest/commands/json.get)) map directly to common\n    reference-data lookup patterns — id-keyed records with a fixed set of fields,\n    or richer nested documents accessed by JSONPath.\n-   [Pipelined](https://redis.io/docs/latest/develop/clients/pools-and-muxing)\n    [`HSET`](https://redis.io/docs/latest/commands/hset) or\n    [`MSET`](https://redis.io/docs/latest/commands/mset) batches make the initial bulk load\n    fast: a few thousand records load in a single round trip, so the application\n    starts serving from a fully-warm cache within seconds of boot.\n-   [`EXPIRE`](https://redis.io/docs/latest/commands/expire) sets a long safety-net TTL on\n    each entry so memory stays bounded even if the sync pipeline silently stops —\n    not as the freshness mechanism, but as a guardrail.\n-   [`SCAN`](https://redis.io/docs/latest/commands/scan) iterates the prefetched keyspace\n    without blocking the server, so the application can audit cache coverage,\n    list available IDs, or run a periodic reconciliation pass against the source.\n-   [Streams](https://redis.io/docs/latest/develop/data-types/streams)\n    ([`XADD`](https://redis.io/docs/latest/commands/xadd),\n    [`XREAD`](https://redis.io/docs/latest/commands/xread)) provide a durable, replayable\n    change feed when the sync worker needs to resume from a known offset after\n    a restart — the canonical pattern for CDC consumers feeding Redis.\n-   Sub-millisecond reads from memory, so reference-data lookups never appear on\n    a flame graph. If Redis is already in the stack for sessions, rate limiting,\n    or cache-aside, prefetch runs on the same instance at zero marginal cost."},{"id":"ecosystem","title":"Ecosystem","role":"content","text":"The following libraries and frameworks support Redis-backed prefetch caching:\n\n-   **Java**:\n    [Spring Cache abstraction (`@Cacheable` with Redis cache store)](https://docs.spring.io/spring-data/redis/reference/redis/redis-cache.html),\n    populated by a startup `CommandLineRunner` for the bulk load.\n-   **Node.js**:\n    [Redis OM](https://github.com/redis/redis-om-node) for object-mapping\n    prefetched JSON documents.\n-   **Change-data-capture (CDC)** pipelines that stream source-database changes\n    into Redis without custom application code:\n    [Redis Data Integration (RDI)](https://redis.io/docs/latest/integrate/redis-data-integration)\n    for relational and NoSQL sources on Redis Enterprise / Redis Cloud;\n    [Debezium](https://debezium.io/) plus a lightweight Redis consumer for\n    open-source Redis.\n-   **API gateways**:\n    [Kong](https://docs.konghq.com/hub/) plugins to route reference-data reads to\n    Redis directly, bypassing the backend service entirely."},{"id":"code-examples-to-build-your-own-redis-prefetch-cache","title":"Code examples to build your own Redis prefetch cache","role":"example","text":"The following guides show how to build a simple Redis-backed prefetch cache in front of a primary store of reference data. Each guide includes a runnable interactive demo that pre-loads records on startup, runs a background sync worker that applies primary-store changes to Redis within milliseconds, and lets you watch the cache stay current as records are added, updated, and deleted on the source.\n\n* [redis-py (Python)](https://redis.io/docs/latest/develop/use-cases/prefetch-cache/redis-py)\n* [node-redis (Node.js)](https://redis.io/docs/latest/develop/use-cases/prefetch-cache/nodejs)\n* [go-redis (Go)](https://redis.io/docs/latest/develop/use-cases/prefetch-cache/go)\n* [Jedis (Java)](https://redis.io/docs/latest/develop/use-cases/prefetch-cache/java-jedis)\n* [Lettuce (Java)](https://redis.io/docs/latest/develop/use-cases/prefetch-cache/java-lettuce)\n* [StackExchange.Redis (C#)](https://redis.io/docs/latest/develop/use-cases/prefetch-cache/dotnet)\n* [Predis (PHP)](https://redis.io/docs/latest/develop/use-cases/prefetch-cache/php)\n* [redis-rb (Ruby)](https://redis.io/docs/latest/develop/use-cases/prefetch-cache/ruby)\n* [redis-rs (Rust)](https://redis.io/docs/latest/develop/use-cases/prefetch-cache/rust)"}],"examples":[]}
{"schema_version":2,"id":"develop/use-cases/prefetch-cache/java-jedis","title":"Redis prefetch cache with Jedis","url":"https://redis.io/docs/latest/develop/use-cases/prefetch-cache/java-jedis/","summary":"Implement a Redis prefetch cache in Java with Jedis","aliases":["/develop/use-cases/prefetch-cache/java","/develop/use-cases/prefetch-cache/jedis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:54:57+01:00","children":[],"page_type":"content","content_hash":"889def61fbfe44084999c3ca0deb9188dcdee39cce26ef7d9f68b428d85f5ee2","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis prefetch cache in Java with the [Jedis](https://redis.io/docs/latest/develop/clients/jedis) client library. It includes a small local web server built on the JDK's `com.sun.net.httpserver` so you can watch the cache pre-load at startup, see a background sync worker apply primary mutations within milliseconds, and break the cache to confirm that reads never fall back to the primary."},{"id":"overview","title":"Overview","role":"overview","text":"Prefetch caching pre-loads a working set of reference data into Redis before the first request arrives, so every read on the request path is a cache hit. A separate sync worker keeps the cache current as the source of truth changes — there is no fall-back to the primary on the read path.\n\nThat gives you:\n\n* Near-100% cache hit ratios for reference and master data\n* Sub-millisecond reads for lookup-heavy paths at peak traffic\n* All reference-data reads offloaded from the primary database\n* Source-database changes propagated into Redis within a few milliseconds\n* A long safety-net TTL that bounds memory if the sync pipeline ever stops\n\nIn this example, each cached category is stored as a Redis hash under a key like `cache:category:{id}`. The hash holds the category fields (`id`, `name`, `display_order`, `featured`, `parent_id`) and the key has a long safety-net TTL that the sync worker refreshes on every add or update event. Delete events remove the cache key outright, so there is no TTL to refresh in that case."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow has three independent paths:\n\n1. **On startup**, the demo server calls `cache.bulkLoad(primary.listRecords())`, which pipelines `DEL` + `HSET` + `EXPIRE` for every record in one round trip.\n2. **On every read**, the application calls `cache.get(entityId)`, which runs `HGETALL` against Redis only. A miss is treated as an error, not a trigger to query the primary.\n3. **On every primary mutation**, the primary appends a change event to an in-process queue. The sync worker thread drains the queue and calls `cache.applyChange(event)`. For an `upsert`, the helper rewrites the cache hash and refreshes the safety-net TTL; for a `delete`, it removes the cache key.\n\nIn a real system the in-process change queue is replaced by a CDC pipeline — [Redis Data Integration](https://redis.io/docs/latest/integrate/redis-data-integration), Debezium plus a lightweight consumer, or an equivalent tool that tails the source's binlog/WAL and pushes events into Redis."},{"id":"the-prefetch-cache-helper","title":"The prefetch-cache helper","role":"content","text":"The `PrefetchCache` class wraps the cache operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/prefetch-cache/java-jedis/PrefetchCache.java)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each cached category is stored in a Redis hash:\n\n[code example]\n\nThe implementation uses:\n\n* [`HSET`](https://redis.io/docs/latest/commands/hset) + [`EXPIRE`](https://redis.io/docs/latest/commands/expire), pipelined, for the bulk load and every sync event\n* [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) on the read path\n* [`DEL`](https://redis.io/docs/latest/commands/del) for sync-delete events and explicit invalidation\n* [`SCAN`](https://redis.io/docs/latest/commands/scan) to enumerate the cached keyspace and to clear the prefix\n* [`TTL`](https://redis.io/docs/latest/commands/ttl) to surface remaining safety-net time in the demo UI"},{"id":"bulk-load-on-startup","title":"Bulk load on startup","role":"content","text":"The `bulkLoad` method pipelines a `DEL` + `HSET` + `EXPIRE` triple for every record. The pipeline is sent in a single round trip, so loading thousands of records takes one network RTT plus the time Redis spends executing the commands locally — typically tens of milliseconds even for a large reference table:\n\n[code example]\n\nThe pipeline is intentionally non-transactional on the **startup** path: nothing is reading the cache yet, the records do not need to be applied atomically as a set, and skipping `MULTI`/`EXEC` keeps the bulk load fast. The same method is used for the live `/reprefetch` reload, which is safe because the demo pauses the sync worker around the clear-and-reload sequence — see [Re-prefetch under load](#re-prefetch-under-load) below. If you call `bulkLoad` directly from your own code on a cache that is already serving reads, either pause your writers first or wrap the writes in a `Transaction` so callers cannot observe a half-loaded record."},{"id":"reads-from-redis-only","title":"Reads from Redis only","role":"content","text":"The `get` method runs `HGETALL` and returns the cached hash. **It does not fall back to the primary on a miss.** In a healthy system, a miss never happens; if it does, the application surfaces it as an error and treats it as a sync-pipeline incident:\n\n[code example]\n\nThis is the key behavioural difference from [cache-aside](https://redis.io/docs/latest/develop/use-cases/cache-aside): the request path never touches the primary, so reference-data reads cannot contribute to primary database load."},{"id":"applying-sync-events","title":"Applying sync events","role":"content","text":"The sync worker calls `applyChange` for every primary mutation. For an `upsert`, the helper rewrites the cache hash and refreshes the safety-net TTL inside a `MULTI`/`EXEC` transaction so the cache never holds a stale mix of old and new fields. For a `delete`, it removes the cache key:\n\n[code example]\n\nThe `DEL` before the `HSET` ensures the cached hash contains exactly the fields the primary record has now — fields that have been dropped from the primary will not linger in Redis."},{"id":"the-sync-worker","title":"The sync worker","role":"content","text":"The `SyncWorker` runs a daemon thread that polls the primary's change queue with a short timeout. Every change is applied to Redis as soon as it arrives\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/prefetch-cache/java-jedis/SyncWorker.java)):\n\n[code example]\n\nIn production this loop is replaced by a CDC consumer reading from RDI's Redis output stream, Debezium's Kafka topic, or an equivalent change feed. The shape stays the same: drain events, apply them to Redis, advance the consumer offset."},{"id":"invalidation-and-re-prefetch","title":"Invalidation and re-prefetch","role":"content","text":"Two helpers exist for testing and recovery:\n\n* `invalidate(entityId)` deletes a single cache key. The demo uses it to simulate a sync-pipeline failure on one record.\n* `clear()` runs `SCAN MATCH cache:category:*` and deletes every key under the prefix. The demo uses it to simulate a full cache loss.\n\nIn both cases, the recovery path is to call `bulkLoad(primary.listRecords())` again — re-prefetching from the primary. The demo exposes this as the \"Re-prefetch\" button so you can see the cache come back to a fully-warm state in one operation."},{"id":"re-prefetch-under-load","title":"Re-prefetch under load","role":"content","text":"`clear()` and `bulkLoad()` are not atomic against the sync worker. If a change event arrives between the snapshot (`primary.listRecords()`) and the bulk write, the bulk write can overwrite a newer value; if a change event arrives between `clear()`'s `SCAN` and `DEL`, the cleared entry can immediately be recreated. The demo's `/clear` and `/reprefetch` handlers solve this by pausing the sync worker around the operation:\n\n[code example]\n\n`pause()` is built on a `ReentrantLock` plus two `Condition`s: it sets a pause flag, then blocks on the worker's \"idle\" condition until the worker reports it has finished whatever event it was applying. Change events that arrive during the pause sit in the primary's `LinkedBlockingQueue` and apply in order once `resume()` is called, so no event is lost."},{"id":"hitmiss-accounting","title":"Hit/miss accounting","role":"content","text":"The helper keeps in-process counters for hits, misses, prefetched records, sync events applied, and the average lag between a primary change and its application to Redis. All counter access is guarded by an intrinsic lock so the demo's parallel HTTP handlers and the sync thread can read and write them without tearing:\n\n[code example]\n\nIn production you would emit these as Micrometer counters and gauges rather than holding them in process memory. The sync-lag metric is the most important: a sudden rise indicates the CDC pipeline is falling behind."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before running the demo, make sure that:\n\n* Redis is running and accessible. By default, the demo connects to `localhost:6379`.\n* JDK 11 or later is installed.\n* The Jedis JAR (5.0+) and its dependencies are on your classpath. Get them from [Maven Central](https://repo1.maven.org/maven2/redis/clients/jedis/), or via Maven/Gradle in a project setup. You also need [`slf4j-api`](https://repo1.maven.org/maven2/org/slf4j/slf4j-api/) (Jedis declares it as a transitive dependency).\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of four files. Download them from the [`java-jedis` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/prefetch-cache/java-jedis) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From that directory, with the Jedis and `slf4j-api` jars in the working directory (the example assumes Jedis 5.1.2 and slf4j-api 2.0.13 — adjust the filenames to match your versions):\n\n[code example]\n\nYou should see something like:\n\n[code example]\n\nAfter starting the server, visit `http://localhost:8785`.\n\nThe demo server uses standard JDK libraries for HTTP handling and concurrency:\n\n* [`com.sun.net.httpserver.HttpServer`](https://docs.oracle.com/en/java/javase/21/docs/api/jdk.httpserver/com/sun/net/httpserver/HttpServer.html) for the web server, with a 16-thread fixed pool from `Executors.newFixedThreadPool(16)`\n* [`java.util.concurrent.LinkedBlockingQueue`](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/LinkedBlockingQueue.html) for the change feed between the mock primary and the sync worker\n* [`java.util.concurrent.locks.ReentrantLock`](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/locks/ReentrantLock.html) and [`Condition`](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/locks/Condition.html) for the sync worker's pause/resume coordination\n\nIt exposes a small interactive page where you can:\n\n* See which IDs are in the cache and in the primary, side by side\n* Read a category through the cache and confirm every read is a hit\n* Update a field on the primary and watch the sync worker rewrite the cache hash\n* Add and delete categories and watch them appear and disappear from the cache\n* Invalidate one key or clear the entire cache to simulate a sync-pipeline failure\n* Re-prefetch from the primary to recover from a broken cache state\n* Watch the average sync lag, and confirm primary reads stay at one until you re-prefetch — each `/reprefetch` adds another primary read for the snapshot, but normal request traffic never reaches the primary at all"},{"id":"the-mock-primary-store","title":"The mock primary store","role":"content","text":"To make the demo self-contained, the example includes a `MockPrimaryStore` that stands in for a source-of-truth database\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/prefetch-cache/java-jedis/MockPrimaryStore.java)):\n\n[code example]\n\nEvery mutation appends a change event to an in-process [`LinkedBlockingQueue`](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/LinkedBlockingQueue.html). The sync worker drains the queue with a 50 ms timeout and applies each event to Redis. In a real system this queue is replaced by a CDC pipeline — RDI on Redis Enterprise or Debezium with a Redis consumer on open-source Redis."},{"id":"production-usage","title":"Production usage","role":"content","text":"This guide uses a deliberately small local demo so you can focus on the prefetch-cache pattern. In production, you will usually want to harden several aspects of it."},{"id":"replace-the-in-process-change-queue-with-a-real-cdc-pipeline","title":"Replace the in-process change queue with a real CDC pipeline","role":"content","text":"The demo's in-process queue is the simplest possible stand-in for a CDC change feed. In production, the change feed lives outside the application process: an RDI pipeline configured against your primary database, Debezium connectors writing to Kafka or a Redis stream, or your application explicitly publishing change events from the write path. Whatever you choose, the consumer side stays the same — read events, apply them to Redis, advance the offset."},{"id":"use-a-long-safety-net-ttl-not-a-freshness-ttl","title":"Use a long safety-net TTL, not a freshness TTL","role":"content","text":"The TTL on each cache key is a **safety net**: it bounds memory if the sync pipeline silently stops, so a stuck consumer cannot leave stale data in Redis indefinitely. The TTL is not the freshness mechanism — freshness comes from the sync worker, which refreshes the TTL on every add or update event (delete events remove the key). Pick a TTL that is comfortably longer than your worst-case sync lag plus your alerting window, so a transient sync hiccup never expires hot keys."},{"id":"decide-what-to-do-on-a-cache-miss","title":"Decide what to do on a cache miss","role":"content","text":"A prefetch cache treats a miss as an error or a missing record. The two reasonable strategies are:\n\n* **Return a 404 to the user.** Appropriate when the cache is authoritative for the lookup — for example, when the user is asking for a category by ID and the ID is not in the cache.\n* **Page on-call.** A sustained miss rate on IDs you know exist is an incident: either the prefetch did not run, or the sync pipeline is broken.\n\nWhichever you choose, do not fall back to the primary on the read path — that is what cache-aside is for, and conflating the two patterns breaks the load-isolation guarantee that prefetch provides."},{"id":"bound-the-working-set-to-what-fits-in-memory","title":"Bound the working set to what fits in memory","role":"content","text":"Prefetch only works if the entire dataset fits in Redis memory with headroom. Estimate the size of your reference data, multiply by a growth factor, and confirm the result fits within your Redis instance's `maxmemory` minus what other use cases need. If the working set grows beyond what Redis can hold, switch the dataset to a cache-aside pattern instead — the request path will pay miss latency, but you will not OOM."},{"id":"reconcile-periodically-against-the-primary","title":"Reconcile periodically against the primary","role":"content","text":"CDC pipelines are eventually consistent: an event can be lost (broker outage, consumer crash, configuration drift) and the cache can silently diverge from the source. Run a periodic reconciliation job that re-reads all primary records, compares them against the cache, and either re-prefetches or fixes individual entries. Even running it once a day catches drift that ad-hoc inspection would miss."},{"id":"tune-the-jedispool","title":"Tune the JedisPool","role":"content","text":"The demo uses a default `JedisPoolConfig`. In production, set `setMaxTotal()` and `setMaxIdle()` to match your concurrency profile and server-side `maxclients`. Every cache call acquires a connection with try-with-resources so connections are returned to the pool even on exceptions; this is what lets the sync worker, the HTTP handlers, and any background job share Redis safely without an in-process lock."},{"id":"namespace-cache-keys-in-shared-redis-deployments","title":"Namespace cache keys in shared Redis deployments","role":"content","text":"If multiple applications share a Redis deployment, prefix cache keys with the application name (`cache:billing:category:{id}`) so different services cannot clobber each other's entries. The helper takes a `prefix` argument exactly for this."},{"id":"inspect-cached-entries-directly-in-redis","title":"Inspect cached entries directly in Redis","role":"content","text":"When testing or troubleshooting, inspect the stored cache keys directly to confirm the bulk load and the sync worker are writing what you expect:\n\n[code example]\n\nIf a key is missing for an ID that still exists in the primary, the prefetch did not run, the key expired without a sync refresh, or someone invalidated it. If a key is still present for an ID that was deleted in the primary, the delete event has not yet been applied. If the TTL is much lower than the configured safety-net value on a hot key, the sync worker is not keeping up."},{"id":"learn-more","title":"Learn more","role":"related","text":"* [Jedis guide](https://redis.io/docs/latest/develop/clients/jedis) - Install and use the Jedis Redis client\n* [HSET command](https://redis.io/docs/latest/commands/hset) - Write hash fields\n* [HGETALL command](https://redis.io/docs/latest/commands/hgetall) - Read every field of a hash\n* [EXPIRE command](https://redis.io/docs/latest/commands/expire) - Set key expiration in seconds\n* [DEL command](https://redis.io/docs/latest/commands/del) - Delete a key on invalidation or sync-delete\n* [SCAN command](https://redis.io/docs/latest/commands/scan) - Iterate the cached keyspace without blocking the server\n* [TTL command](https://redis.io/docs/latest/commands/ttl) - Inspect remaining safety-net time on a key\n* [Redis Data Integration](https://redis.io/docs/latest/integrate/redis-data-integration) - Configuration-driven CDC into Redis on Redis Enterprise and Redis Cloud"}],"examples":[{"id":"the-prefetch-cache-helper-ex0","language":"java","code":"import redis.clients.jedis.JedisPool;\nimport redis.clients.jedis.JedisPoolConfig;\n\nJedisPool pool = new JedisPool(new JedisPoolConfig(), \"localhost\", 6379);\nMockPrimaryStore primary = new MockPrimaryStore(80);\nPrefetchCache cache = new PrefetchCache(pool);\n\n// Pre-load every primary record into Redis in one pipelined round trip.\ncache.bulkLoad(primary.listRecords());\n\n// Start the sync worker so primary mutations propagate into Redis.\nSyncWorker sync = new SyncWorker(primary, cache);\nsync.start();\n\n// Read paths now go to Redis only.\nPrefetchCache.Result result = cache.get(\"cat-001\");\nSystem.out.printf(\"hit=%s latency=%.2fms record=%s%n\",\n    result.hit, result.redisLatencyMs, result.record);","section_id":"the-prefetch-cache-helper"},{"id":"data-model-ex0","language":"text","code":"cache:category:cat-001\n  id            = cat-001\n  name          = Beverages\n  display_order = 1\n  featured      = true\n  parent_id     =","section_id":"data-model"},{"id":"bulk-load-on-startup-ex0","language":"java","code":"public int bulkLoad(Iterable<Map<String, String>> records) {\n    int loaded = 0;\n    try (Jedis jedis = pool.getResource()) {\n        Pipeline pipe = jedis.pipelined();\n        for (Map<String, String> record : records) {\n            if (record == null) continue;\n            String entityId = record.get(\"id\");\n            if (entityId == null || entityId.isEmpty()) continue;\n            String cacheKey = cacheKey(entityId);\n            pipe.del(cacheKey);\n            pipe.hset(cacheKey, record);\n            pipe.expire(cacheKey, ttlSeconds);\n            loaded++;\n        }\n        if (loaded > 0) pipe.sync();\n    }\n    return loaded;\n}","section_id":"bulk-load-on-startup"},{"id":"reads-from-redis-only-ex0","language":"java","code":"public Result get(String entityId) {\n    String cacheKey = cacheKey(entityId);\n    long startedNs = System.nanoTime();\n    Map<String, String> cached;\n    try (Jedis jedis = pool.getResource()) {\n        cached = jedis.hgetAll(cacheKey);\n    }\n    double redisLatencyMs = (System.nanoTime() - startedNs) / 1_000_000.0;\n\n    if (cached != null && !cached.isEmpty()) {\n        synchronized (statsLock) { hits++; }\n        return new Result(cached, true, redisLatencyMs);\n    }\n    synchronized (statsLock) { misses++; }\n    return new Result(null, false, redisLatencyMs);\n}","section_id":"reads-from-redis-only"},{"id":"applying-sync-events-ex0","language":"java","code":"public void applyChange(Map<String, Object> change) {\n    Object op = change.get(\"op\");\n    String entityId = (String) change.get(\"id\");\n    if (entityId == null || entityId.isEmpty()) return;\n    String cacheKey = cacheKey(entityId);\n\n    if (\"upsert\".equals(op)) {\n        @SuppressWarnings(\"unchecked\")\n        Map<String, String> fields = (Map<String, String>) change.get(\"fields\");\n        if (fields == null || fields.isEmpty()) {\n            // Malformed upsert: skip rather than crash the sync worker.\n            return;\n        }\n        try (Jedis jedis = pool.getResource()) {\n            Transaction tx = jedis.multi();\n            tx.del(cacheKey);\n            tx.hset(cacheKey, fields);\n            tx.expire(cacheKey, ttlSeconds);\n            tx.exec();\n        }\n    } else if (\"delete\".equals(op)) {\n        try (Jedis jedis = pool.getResource()) {\n            jedis.del(cacheKey);\n        }\n    }\n}","section_id":"applying-sync-events"},{"id":"the-sync-worker-ex0","language":"java","code":"private void run() {\n    while (!stopRequested) {\n        if (pauseRequested) {\n            // Park until pause is lifted (see pause/resume below).\n            parkUntilResumed();\n            continue;\n        }\n        Map<String, Object> change = primary.nextChange(pollTimeoutMs);\n        if (change == null) continue;\n        try {\n            cache.applyChange(change);\n        } catch (Exception exc) {\n            System.err.println(\"[sync] failed to apply \" + change + \": \" + exc);\n        }\n    }\n}","section_id":"the-sync-worker"},{"id":"re-prefetch-under-load-ex0","language":"java","code":"sync.pause();\ntry {\n    cache.clear();\n    cache.bulkLoad(primary.listRecords());\n} finally {\n    sync.resume();\n}","section_id":"re-prefetch-under-load"},{"id":"hitmiss-accounting-ex0","language":"java","code":"public Map<String, Object> stats() {\n    synchronized (statsLock) {\n        long total = hits + misses;\n        double hitRate = total == 0 ? 0.0 : Math.round(1000.0 * hits / total) / 10.0;\n        double avgLag = syncLagSamples == 0 ? 0.0\n                : Math.round(100.0 * syncLagMsTotal / syncLagSamples) / 100.0;\n        Map<String, Object> stats = new LinkedHashMap<>();\n        stats.put(\"hits\", hits);\n        stats.put(\"misses\", misses);\n        stats.put(\"hit_rate_pct\", hitRate);\n        stats.put(\"prefetched\", prefetched);\n        stats.put(\"sync_events_applied\", syncEventsApplied);\n        stats.put(\"sync_lag_ms_avg\", avgLag);\n        return stats;\n    }\n}","section_id":"hitmiss-accounting"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir prefetch-cache-demo && cd prefetch-cache-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/prefetch-cache/java-jedis\ncurl -O $BASE/PrefetchCache.java\ncurl -O $BASE/MockPrimaryStore.java\ncurl -O $BASE/SyncWorker.java\ncurl -O $BASE/DemoServer.java","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"javac -cp jedis-5.1.2.jar:slf4j-api-2.0.13.jar \\\n    PrefetchCache.java MockPrimaryStore.java SyncWorker.java DemoServer.java\n\njava -cp .:jedis-5.1.2.jar:slf4j-api-2.0.13.jar \\\n    DemoServer --port 8785 --redis-host localhost --redis-port 6379","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"Redis prefetch-cache demo server listening on http://127.0.0.1:8785\nUsing Redis at localhost:6379 with cache prefix 'cache:category:' and TTL 3600s\nPrefetched 5 records in 103.3 ms; sync worker running","section_id":"start-the-demo-server"},{"id":"the-mock-primary-store-ex0","language":"java","code":"public List<Map<String, String>> listRecords() {\n    sleepLatency();\n    synchronized (lock) {\n        reads++;\n        // ... return a copy of every record\n    }\n}\n\npublic boolean updateField(String entityId, String field, String value) {\n    synchronized (lock) {\n        Map<String, String> record = records.get(entityId);\n        if (record == null) return false;\n        record.put(field, value);\n        // Emit the change event while still holding the records lock,\n        // so queue order matches mutation order.\n        emitChangeLocked(OP_UPSERT, entityId, new LinkedHashMap<>(record));\n    }\n    return true;\n}","section_id":"the-mock-primary-store"},{"id":"inspect-cached-entries-directly-in-redis-ex0","language":"bash","code":"redis-cli --scan --pattern 'cache:category:*'\nredis-cli HGETALL cache:category:cat-001\nredis-cli TTL cache:category:cat-001","section_id":"inspect-cached-entries-directly-in-redis"}]}
{"schema_version":2,"id":"develop/use-cases/prefetch-cache/java-lettuce","title":"Redis prefetch cache with Lettuce","url":"https://redis.io/docs/latest/develop/use-cases/prefetch-cache/java-lettuce/","summary":"Implement a Redis prefetch cache in Java with Lettuce","aliases":["/develop/use-cases/prefetch-cache/lettuce"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:54:57+01:00","children":[],"page_type":"content","content_hash":"e515a96dc62c9a17083e75fe015447351563f9bdde7d62418f2c1dcd85fd87fc","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis prefetch cache in Java with the [Lettuce](https://redis.io/docs/latest/develop/clients/lettuce) client library. It includes a small local web server built on the JDK's `com.sun.net.httpserver` so you can watch the cache pre-load at startup, see a background sync worker apply primary mutations within milliseconds, and break the cache to confirm that reads never fall back to the primary."},{"id":"overview","title":"Overview","role":"overview","text":"Prefetch caching pre-loads a working set of reference data into Redis before the first request arrives, so every read on the request path is a cache hit. A separate sync worker keeps the cache current as the source of truth changes — there is no fall-back to the primary on the read path.\n\nThat gives you:\n\n* Near-100% cache hit ratios for reference and master data\n* Sub-millisecond reads for lookup-heavy paths at peak traffic\n* All reference-data reads offloaded from the primary database\n* Source-database changes propagated into Redis within a few milliseconds\n* A long safety-net TTL that bounds memory if the sync pipeline ever stops\n\nIn this example, each cached category is stored as a Redis hash under a key like `cache:category:{id}`. The hash holds the category fields (`id`, `name`, `display_order`, `featured`, `parent_id`) and the key has a long safety-net TTL that the sync worker refreshes on every add or update event. Delete events remove the cache key outright, so there is no TTL to refresh in that case.\n\nThis guide uses Lettuce's synchronous command API (`StatefulRedisConnection.sync()`) for reads and event application, and the asynchronous API (`async()`) inside `bulkLoad` so that the startup pipeline of `DEL` + `HSET` + `EXPIRE` triples can batch into a single round trip without each command blocking on its own future. Lettuce's reactive API would work equally well for either path."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow has three independent paths:\n\n1. **On startup**, the demo server calls `cache.bulkLoad(primary.listRecords())`, which pipelines `DEL` + `HSET` + `EXPIRE` for every record in one round trip.\n2. **On every read**, the application calls `cache.get(entityId)`, which runs `HGETALL` against Redis only. A miss is treated as an error, not a trigger to query the primary.\n3. **On every primary mutation**, the primary appends a change event to an in-process queue. The sync worker thread drains the queue and calls `cache.applyChange(event)`. For an `upsert`, the helper rewrites the cache hash and refreshes the safety-net TTL; for a `delete`, it removes the cache key.\n\nIn a real system the in-process change queue is replaced by a CDC pipeline — [Redis Data Integration](https://redis.io/docs/latest/integrate/redis-data-integration), Debezium plus a lightweight consumer, or an equivalent tool that tails the source's binlog/WAL and pushes events into Redis."},{"id":"the-prefetch-cache-helper","title":"The prefetch-cache helper","role":"content","text":"The `PrefetchCache` class wraps the cache operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/prefetch-cache/java-lettuce/PrefetchCache.java)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each cached category is stored in a Redis hash:\n\n[code example]\n\nThe implementation uses:\n\n* [`HSET`](https://redis.io/docs/latest/commands/hset) + [`EXPIRE`](https://redis.io/docs/latest/commands/expire), pipelined, for the bulk load and every sync event\n* [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) on the read path\n* [`DEL`](https://redis.io/docs/latest/commands/del) for sync-delete events and explicit invalidation\n* [`SCAN`](https://redis.io/docs/latest/commands/scan) to enumerate the cached keyspace and to clear the prefix\n* [`TTL`](https://redis.io/docs/latest/commands/ttl) to surface remaining safety-net time in the demo UI\n* [`MULTI`](https://redis.io/docs/latest/commands/multi)/[`EXEC`](https://redis.io/docs/latest/commands/exec) for the transactional upsert path in `applyChange`"},{"id":"bulk-load-on-startup","title":"Bulk load on startup","role":"content","text":"The `bulkLoad` method pipelines a `DEL` + `HSET` + `EXPIRE` triple for every record using Lettuce's async API with `setAutoFlushCommands(false)`. The whole batch flushes in a single network round trip, so loading thousands of records takes one RTT plus the time Redis spends executing the commands locally — typically tens of milliseconds even for a large reference table:\n\n[code example]\n\nThe bulk load is intentionally non-transactional: nothing is reading the cache yet on the startup path, the records do not need to be applied atomically as a set, and skipping `MULTI`/`EXEC` keeps the pipeline fast. The same method is used for the live `/reprefetch` reload, which is safe because the demo pauses the sync worker around the clear-and-reload sequence — see [Re-prefetch under load](#re-prefetch-under-load) below. If you call `bulkLoad` directly from your own code on a cache that is already serving reads, either pause your writers first or rewrite it as a single `MULTI`/`EXEC` block so callers cannot observe a half-loaded record.\n\nUsing the async API here is important: the sync API blocks on every command's future, which would defeat the batching even with auto-flush disabled. The async API queues commands locally and only flushes them when `flushCommands()` is called, then waits on the resulting futures in bulk."},{"id":"reads-from-redis-only","title":"Reads from Redis only","role":"content","text":"The `get` method runs `HGETALL` and returns the cached hash. **It does not fall back to the primary on a miss.** In a healthy system, a miss never happens; if it does, the application surfaces it as an error and treats it as a sync-pipeline incident:\n\n[code example]\n\nThis is the key behavioural difference from [cache-aside](https://redis.io/docs/latest/develop/use-cases/cache-aside): the request path never touches the primary, so reference-data reads cannot contribute to primary database load."},{"id":"applying-sync-events","title":"Applying sync events","role":"content","text":"The sync worker calls `applyChange` for every primary mutation. For an `upsert`, the helper rewrites the cache hash and refreshes the safety-net TTL in one `MULTI`/`EXEC` block so the cache never holds a stale mix of old and new fields. For a `delete`, it removes the cache key:\n\n[code example]\n\nThe `DEL` before the `HSET` ensures the cached hash contains exactly the fields the primary record has now — fields that have been dropped from the primary will not linger in Redis.\n\nA Lettuce-specific point: a single `StatefulRedisConnection` is thread-safe for individual command calls, but `MULTI`/`EXEC` is connection-scoped state. If two threads issued transactions over the same connection at the same time, their queued commands would interleave. The demo shares one connection across HTTP handlers and the sync worker, so `txLock` (a `ReentrantLock`) serializes every transactional sequence. In production you would hand each transactional caller its own connection from a pool (see [Production usage](#production-usage)) or migrate the upsert path into a Lua script so the atomicity is server-side and no client-side lock is needed."},{"id":"the-sync-worker","title":"The sync worker","role":"content","text":"The `SyncWorker` runs a daemon thread that blocks on the primary's change queue with a short timeout. Every change is applied to Redis as soon as it arrives\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/prefetch-cache/java-lettuce/SyncWorker.java)):\n\n[code example]\n\nIn production this loop is replaced by a CDC consumer reading from RDI's Redis output stream, Debezium's Kafka topic, or an equivalent change feed. The shape stays the same: drain events, apply them to Redis, advance the consumer offset."},{"id":"invalidation-and-re-prefetch","title":"Invalidation and re-prefetch","role":"content","text":"Two helpers exist for testing and recovery:\n\n* `invalidate(entityId)` deletes a single cache key. The demo uses it to simulate a sync-pipeline failure on one record.\n* `clear()` runs `SCAN MATCH cache:category:*` and deletes every key under the prefix. The demo uses it to simulate a full cache loss.\n\nIn both cases, the recovery path is to call `bulkLoad(primary.listRecords())` again — re-prefetching from the primary. The demo exposes this as the \"Re-prefetch\" button so you can see the cache come back to a fully-warm state in one operation."},{"id":"re-prefetch-under-load","title":"Re-prefetch under load","role":"content","text":"`clear()` and `bulkLoad()` are not atomic against the sync worker. If a change event arrives between the snapshot (`primary.listRecords()`) and the bulk write, the bulk write can overwrite a newer value; if a change event arrives between `clear()`'s `SCAN` and `DEL`, the cleared entry can immediately be recreated. The demo's `/clear` and `/reprefetch` handlers solve this by pausing the sync worker around the operation:\n\n[code example]\n\n`pause()` waits for the worker to finish whatever event it is currently applying, parks the run loop, and returns. Change events that arrive during the pause sit in the primary's queue and apply in order once `resume()` is called, so no event is lost."},{"id":"hitmiss-accounting","title":"Hit/miss accounting","role":"content","text":"The helper keeps in-process counters for hits, misses, prefetched records, sync events applied, and the average lag between a primary change and its application to Redis. The demo UI surfaces these so you can confirm the cache is absorbing all reads and the sync worker is keeping up:\n\n[code example]\n\nIn production you would emit these as Micrometer counters and gauges or push them into your metrics pipeline. The sync-lag metric is the most important: a sudden rise indicates the CDC pipeline is falling behind."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before running the demo, make sure that:\n\n* Redis is running and accessible. By default, the demo connects to `localhost:6379`.\n* JDK 17 or later is installed (the demo uses Java text blocks for the inline HTML).\n* The Lettuce JAR (and its Netty + Reactor dependencies) is on your classpath.\n  Get them from\n  [Maven Central](https://repo1.maven.org/maven2/io/lettuce/lettuce-core/),\n  or via Maven/Gradle in a project setup.\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of four Java files. Download them from the [`java-lettuce` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/prefetch-cache/java-lettuce) on GitHub, or grab them with `curl`:\n\n[code example]\n\nYou also need Lettuce and its runtime dependencies on your classpath. The simplest way is to download them into a local `lib/` directory:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From the demo directory:\n\n[code example]\n\n(Where `lib/` contains `lettuce-core`, `reactor-core`, `reactive-streams`, and the relevant Netty jars.)\n\nYou should see something like:\n\n[code example]\n\nAfter starting the server, visit `http://localhost:8786`.\n\nThe demo server uses only standard JDK libraries for HTTP handling and concurrency:\n\n* [`com.sun.net.httpserver.HttpServer`](https://docs.oracle.com/en/java/javase/21/docs/api/jdk.httpserver/com/sun/net/httpserver/HttpServer.html) for the web server\n* [`java.util.concurrent.Executors`](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/Executors.html) for the request thread pool and sync-worker daemon\n* [`java.util.concurrent.LinkedBlockingQueue`](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/LinkedBlockingQueue.html) for the primary's in-process change feed\n\nIt exposes a small interactive page where you can:\n\n* See which IDs are in the cache and in the primary, side by side\n* Read a category through the cache and confirm every read is a hit\n* Update a field on the primary and watch the sync worker rewrite the cache hash\n* Add and delete categories and watch them appear and disappear from the cache\n* Invalidate one key or clear the entire cache to simulate a sync-pipeline failure\n* Re-prefetch from the primary to recover from a broken cache state\n* Watch the average sync lag, and confirm primary reads stay at one until you re-prefetch — each `/reprefetch` adds another primary read for the snapshot, but normal request traffic never reaches the primary at all"},{"id":"the-mock-primary-store","title":"The mock primary store","role":"content","text":"To make the demo self-contained, the example includes a `MockPrimaryStore` that stands in for a source-of-truth database\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/prefetch-cache/java-lettuce/MockPrimaryStore.java)):\n\n[code example]\n\nEvery mutation appends a change event to an in-process [`LinkedBlockingQueue`](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/LinkedBlockingQueue.html). The sync worker drains the queue with a 50 ms timeout and applies each event to Redis. The mutation lock is held across both the record update and the queue `offer`, so concurrent updates produce change events in the same order as their mutations — a correctness requirement the demo's pause/resume race test relies on. In a real system the queue is replaced by a CDC pipeline — RDI on Redis Enterprise or Debezium with a Redis consumer on open-source Redis."},{"id":"production-usage","title":"Production usage","role":"content","text":"This guide uses a deliberately small local demo so you can focus on the prefetch-cache pattern. In production, you will usually want to harden several aspects of it."},{"id":"use-a-connection-pool-for-transactions","title":"Use a connection pool for transactions","role":"content","text":"The demo shares a single `StatefulRedisConnection` across HTTP handlers and the sync worker, and serializes every `MULTI`/`EXEC` block with an in-process `ReentrantLock`. In production, use [`ConnectionPoolSupport`](https://github.com/redis/lettuce/wiki/Connection-Pooling) so each transactional caller (or each sync-worker partition, if you shard the change feed) gets its own connection. Once each transaction has a dedicated connection, you can drop `txLock` entirely. An alternative is to merge the `DEL`+`HSET`+`EXPIRE` upsert into a small Lua script invoked with `EVAL` — atomic server-side, lock-free on the client, and a single network round trip per event."},{"id":"replace-the-in-process-change-queue-with-a-real-cdc-pipeline","title":"Replace the in-process change queue with a real CDC pipeline","role":"content","text":"The demo's in-process queue is the simplest possible stand-in for a CDC change feed. In production, the change feed lives outside the application process: an RDI pipeline configured against your primary database, Debezium connectors writing to Kafka or a Redis stream, or your application explicitly publishing change events from the write path. Whatever you choose, the consumer side stays the same — read events, apply them to Redis, advance the offset."},{"id":"use-a-long-safety-net-ttl-not-a-freshness-ttl","title":"Use a long safety-net TTL, not a freshness TTL","role":"content","text":"The TTL on each cache key is a **safety net**: it bounds memory if the sync pipeline silently stops, so a stuck consumer cannot leave stale data in Redis indefinitely. The TTL is not the freshness mechanism — freshness comes from the sync worker, which refreshes the TTL on every add or update event (delete events remove the key). Pick a TTL that is comfortably longer than your worst-case sync lag plus your alerting window, so a transient sync hiccup never expires hot keys."},{"id":"decide-what-to-do-on-a-cache-miss","title":"Decide what to do on a cache miss","role":"content","text":"A prefetch cache treats a miss as an error or a missing record. The two reasonable strategies are:\n\n* **Return a 404 to the user.** Appropriate when the cache is authoritative for the lookup — for example, when the user is asking for a category by ID and the ID is not in the cache.\n* **Page on-call.** A sustained miss rate on IDs you know exist is an incident: either the prefetch did not run, or the sync pipeline is broken.\n\nWhichever you choose, do not fall back to the primary on the read path — that is what cache-aside is for, and conflating the two patterns breaks the load-isolation guarantee that prefetch provides."},{"id":"bound-the-working-set-to-what-fits-in-memory","title":"Bound the working set to what fits in memory","role":"content","text":"Prefetch only works if the entire dataset fits in Redis memory with headroom. Estimate the size of your reference data, multiply by a growth factor, and confirm the result fits within your Redis instance's `maxmemory` minus what other use cases need. If the working set grows beyond what Redis can hold, switch the dataset to a cache-aside pattern instead — the request path will pay miss latency, but you will not OOM."},{"id":"reconcile-periodically-against-the-primary","title":"Reconcile periodically against the primary","role":"content","text":"CDC pipelines are eventually consistent: an event can be lost (broker outage, consumer crash, configuration drift) and the cache can silently diverge from the source. Run a periodic reconciliation job that re-reads all primary records, compares them against the cache, and either re-prefetches or fixes individual entries. Even running it once a day catches drift that ad-hoc inspection would miss."},{"id":"consider-the-async-or-reactive-apis","title":"Consider the async or reactive APIs","role":"content","text":"For high-throughput or event-driven applications, Lettuce's `async()` (`CompletionStage`-based) or `reactive()` (Project Reactor) APIs let request-handling threads return immediately while Redis work continues. The prefetch-cache structure is identical — replace the synchronous `hgetall` / `multi`/`exec` calls with their async counterparts and chain them together. The bulk-load path in this helper already uses the async API to batch its pipeline."},{"id":"namespace-cache-keys-in-shared-redis-deployments","title":"Namespace cache keys in shared Redis deployments","role":"content","text":"If multiple applications share a Redis deployment, prefix cache keys with the application name (`cache:billing:category:{id}`) so different services cannot clobber each other's entries. The helper takes a `prefix` argument exactly for this."},{"id":"inspect-cached-entries-directly-in-redis","title":"Inspect cached entries directly in Redis","role":"content","text":"When testing or troubleshooting, inspect the stored cache keys directly to confirm the bulk load and the sync worker are writing what you expect:\n\n[code example]\n\nIf a key is missing for an ID that still exists in the primary, the prefetch did not run, the key expired without a sync refresh, or someone invalidated it. If a key is still present for an ID that was deleted in the primary, the delete event has not yet been applied. If the TTL is much lower than the configured safety-net value on a hot key, the sync worker is not keeping up."},{"id":"learn-more","title":"Learn more","role":"related","text":"* [Lettuce guide](https://redis.io/docs/latest/develop/clients/lettuce) - Install and use the Lettuce Redis client\n* [HSET command](https://redis.io/docs/latest/commands/hset) - Write hash fields\n* [HGETALL command](https://redis.io/docs/latest/commands/hgetall) - Read every field of a hash\n* [EXPIRE command](https://redis.io/docs/latest/commands/expire) - Set key expiration in seconds\n* [DEL command](https://redis.io/docs/latest/commands/del) - Delete a key on invalidation or sync-delete\n* [SCAN command](https://redis.io/docs/latest/commands/scan) - Iterate the cached keyspace without blocking the server\n* [TTL command](https://redis.io/docs/latest/commands/ttl) - Inspect remaining safety-net time on a key\n* [MULTI command](https://redis.io/docs/latest/commands/multi) / [EXEC command](https://redis.io/docs/latest/commands/exec) - Transactional upsert path in `applyChange`\n* [Redis Data Integration](https://redis.io/docs/latest/integrate/redis-data-integration) - Configuration-driven CDC into Redis on Redis Enterprise and Redis Cloud"}],"examples":[{"id":"the-prefetch-cache-helper-ex0","language":"java","code":"import io.lettuce.core.RedisClient;\nimport io.lettuce.core.RedisURI;\nimport io.lettuce.core.api.StatefulRedisConnection;\n\nRedisClient client = RedisClient.create(\n    RedisURI.builder().withHost(\"localhost\").withPort(6379).build());\nStatefulRedisConnection<String, String> connection = client.connect();\n\nMockPrimaryStore primary = new MockPrimaryStore(80);\nPrefetchCache cache = new PrefetchCache(connection, \"cache:category:\", 3600);\n\n// Pre-load every primary record into Redis in one pipelined round trip.\ncache.bulkLoad(primary.listRecords());\n\n// Start the sync worker so primary mutations propagate into Redis.\nSyncWorker sync = new SyncWorker(primary, cache);\nsync.start();\n\n// Read paths now go to Redis only.\nPrefetchCache.Result result = cache.get(\"cat-001\");","section_id":"the-prefetch-cache-helper"},{"id":"data-model-ex0","language":"text","code":"cache:category:cat-001\n  id            = cat-001\n  name          = Beverages\n  display_order = 1\n  featured      = true\n  parent_id     =","section_id":"data-model"},{"id":"bulk-load-on-startup-ex0","language":"java","code":"public int bulkLoad(Iterable<Map<String, String>> records) {\n    RedisAsyncCommands<String, String> async = connection.async();\n    connection.setAutoFlushCommands(false);\n    List<RedisFuture<?>> futures = new ArrayList<>();\n    int loaded = 0;\n    try {\n        for (Map<String, String> record : records) {\n            if (record == null) continue;\n            String entityId = record.get(\"id\");\n            if (entityId == null || entityId.isEmpty()) continue;\n            String cacheKey = cacheKey(entityId);\n            futures.add(async.del(cacheKey));\n            futures.add(async.hset(cacheKey, record));\n            futures.add(async.expire(cacheKey, ttlSeconds));\n            loaded += 1;\n        }\n        connection.flushCommands();\n        for (RedisFuture<?> future : futures) {\n            future.get();\n        }\n    } finally {\n        connection.setAutoFlushCommands(true);\n    }\n    if (loaded > 0) prefetched.addAndGet(loaded);\n    return loaded;\n}","section_id":"bulk-load-on-startup"},{"id":"reads-from-redis-only-ex0","language":"java","code":"public Result get(String entityId) {\n    RedisCommands<String, String> sync = connection.sync();\n    String cacheKey = cacheKey(entityId);\n\n    long startedNs = System.nanoTime();\n    Map<String, String> cached = sync.hgetall(cacheKey);\n    double redisLatencyMs = (System.nanoTime() - startedNs) / 1_000_000.0;\n\n    if (cached != null && !cached.isEmpty()) {\n        hits.incrementAndGet();\n        return new Result(cached, true, redisLatencyMs);\n    }\n    misses.incrementAndGet();\n    return new Result(null, false, redisLatencyMs);\n}","section_id":"reads-from-redis-only"},{"id":"applying-sync-events-ex0","language":"java","code":"public void applyChange(Map<String, Object> change) {\n    // ... validate op and id ...\n    if (\"upsert\".equals(op)) {\n        Map<String, String> fields = (Map<String, String>) change.get(\"fields\");\n        if (fields == null || fields.isEmpty()) return;\n        txLock.lock();\n        try {\n            sync.multi();\n            sync.del(cacheKey);\n            sync.hset(cacheKey, fields);\n            sync.expire(cacheKey, ttlSeconds);\n            sync.exec();\n        } finally {\n            txLock.unlock();\n        }\n    } else if (\"delete\".equals(op)) {\n        sync.del(cacheKey);\n    }\n    // ... record sync_events_applied counter and lag sample ...\n}","section_id":"applying-sync-events"},{"id":"the-sync-worker-ex0","language":"java","code":"private void run() {\n    while (!stopRequested) {\n        if (pauseRequested) {\n            // park until resume() ...\n            continue;\n        }\n        Map<String, Object> change = primary.nextChange(pollTimeoutMs);\n        if (change == null) continue;\n        try {\n            cache.applyChange(change);\n        } catch (Exception exc) {\n            System.err.printf(\"[sync] failed to apply %s: %s%n\",\n                    change, exc.getMessage());\n        }\n    }\n}","section_id":"the-sync-worker"},{"id":"re-prefetch-under-load-ex0","language":"java","code":"sync.pause(2000);\ntry {\n    cache.clear();\n    cache.bulkLoad(primary.listRecords());\n} finally {\n    sync.resume();\n}","section_id":"re-prefetch-under-load"},{"id":"hitmiss-accounting-ex0","language":"java","code":"public Map<String, Object> stats() {\n    long h = hits.get();\n    long m = misses.get();\n    long total = h + m;\n    double hitRate = total == 0 ? 0.0 : Math.round(1000.0 * h / total) / 10.0;\n    double avgLag;\n    synchronized (lagLock) {\n        avgLag = syncLagSamples == 0\n                ? 0.0\n                : Math.round(100.0 * syncLagMsTotal / syncLagSamples) / 100.0;\n    }\n    Map<String, Object> stats = new LinkedHashMap<>();\n    stats.put(\"hits\", h);\n    stats.put(\"misses\", m);\n    stats.put(\"hit_rate_pct\", hitRate);\n    stats.put(\"prefetched\", prefetched.get());\n    stats.put(\"sync_events_applied\", syncEventsApplied.get());\n    stats.put(\"sync_lag_ms_avg\", avgLag);\n    return stats;\n}","section_id":"hitmiss-accounting"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir prefetch-cache-demo && cd prefetch-cache-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/prefetch-cache/java-lettuce\ncurl -O $BASE/PrefetchCache.java\ncurl -O $BASE/MockPrimaryStore.java\ncurl -O $BASE/SyncWorker.java\ncurl -O $BASE/DemoServer.java","section_id":"get-the-source-files"},{"id":"get-the-source-files-ex1","language":"bash","code":"mkdir lib && cd lib\nLETTUCE=https://repo1.maven.org/maven2/io/lettuce/lettuce-core/6.5.0.RELEASE\ncurl -O $LETTUCE/lettuce-core-6.5.0.RELEASE.jar\nNETTY=https://repo1.maven.org/maven2/io/netty\nfor ARTIFACT in netty-buffer netty-codec netty-common netty-handler \\\n                netty-resolver netty-transport netty-transport-native-unix-common; do\n  curl -O \"$NETTY/$ARTIFACT/4.1.113.Final/$ARTIFACT-4.1.113.Final.jar\"\ndone\ncurl -O https://repo1.maven.org/maven2/io/projectreactor/reactor-core/3.6.6/reactor-core-3.6.6.jar\ncurl -O https://repo1.maven.org/maven2/org/reactivestreams/reactive-streams/1.0.4/reactive-streams-1.0.4.jar\ncd ..","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"javac -cp 'lib/*' PrefetchCache.java MockPrimaryStore.java SyncWorker.java DemoServer.java\njava -cp '.:lib/*' DemoServer --port 8786 --redis-host localhost --redis-port 6379","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"Redis prefetch-cache demo server listening on http://127.0.0.1:8786\nUsing Redis at localhost:6379 with cache prefix 'cache:category:' and TTL 3600s\nPrefetched 5 records in 90.9 ms; sync worker running","section_id":"start-the-demo-server"},{"id":"the-mock-primary-store-ex0","language":"java","code":"public class MockPrimaryStore {\n    public MockPrimaryStore(int readLatencyMs) { ... }\n\n    public List<Map<String, String>> listRecords() {\n        Thread.sleep(readLatencyMs);\n        // ...\n    }\n\n    public boolean updateField(String entityId, String field, String value) {\n        synchronized (lock) {\n            // ... mutate the record ...\n            emitChangeLocked(CHANGE_OP_UPSERT, entityId, copy);\n        }\n        return true;\n    }\n}","section_id":"the-mock-primary-store"},{"id":"inspect-cached-entries-directly-in-redis-ex0","language":"bash","code":"redis-cli --scan --pattern 'cache:category:*'\nredis-cli HGETALL cache:category:cat-001\nredis-cli TTL cache:category:cat-001","section_id":"inspect-cached-entries-directly-in-redis"}]}
{"schema_version":2,"id":"develop/use-cases/prefetch-cache/nodejs","title":"Redis prefetch cache with node-redis","url":"https://redis.io/docs/latest/develop/use-cases/prefetch-cache/nodejs/","summary":"Implement a Redis prefetch cache in Node.js with node-redis","aliases":["/develop/use-cases/prefetch-cache/node-redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:54:57+01:00","children":[],"page_type":"content","content_hash":"56f6645d4200370bd1d8c39ff9df8008c2467a92d50390b23cc219a57a03b6c3","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis prefetch cache in Node.js with [`node-redis`](https://redis.io/docs/latest/develop/clients/nodejs). It includes a small local web server built with the Node.js standard `http` module so you can watch the cache pre-load at startup, see a background sync worker apply primary mutations within milliseconds, and break the cache to confirm that reads never fall back to the primary."},{"id":"overview","title":"Overview","role":"overview","text":"Prefetch caching pre-loads a working set of reference data into Redis before the first request arrives, so every read on the request path is a cache hit. A separate sync worker keeps the cache current as the source of truth changes — there is no fall-back to the primary on the read path.\n\nThat gives you:\n\n* Near-100% cache hit ratios for reference and master data\n* Sub-millisecond reads for lookup-heavy paths at peak traffic\n* All reference-data reads offloaded from the primary database\n* Source-database changes propagated into Redis within a few milliseconds\n* A long safety-net TTL that bounds memory if the sync pipeline ever stops\n\nIn this example, each cached category is stored as a Redis hash under a key like `cache:category:{id}`. The hash holds the category fields (`id`, `name`, `display_order`, `featured`, `parent_id`) and the key has a long safety-net TTL that the sync worker refreshes on every add or update event. Delete events remove the cache key outright, so there is no TTL to refresh in that case."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow has three independent paths:\n\n1. **On startup**, the demo server calls `cache.bulkLoad(await primary.listRecords())`, which pipelines `DEL` + `HSET` + `EXPIRE` for every record in one round trip.\n2. **On every read**, the application calls `cache.get(entityId)`, which runs `HGETALL` against Redis only. A miss is treated as an error, not a trigger to query the primary.\n3. **On every primary mutation**, the primary appends a change event to an in-process queue. The sync worker async task drains the queue and calls `cache.applyChange(event)`. For an `upsert`, the helper rewrites the cache hash and refreshes the safety-net TTL; for a `delete`, it removes the cache key.\n\nIn a real system the in-process change queue is replaced by a CDC pipeline — [Redis Data Integration](https://redis.io/docs/latest/integrate/redis-data-integration), Debezium plus a lightweight consumer, or an equivalent tool that tails the source's binlog/WAL and pushes events into Redis."},{"id":"the-prefetch-cache-helper","title":"The prefetch-cache helper","role":"content","text":"The `PrefetchCache` class wraps the cache operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/prefetch-cache/nodejs/cache.js)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each cached category is stored in a Redis hash:\n\n[code example]\n\nThe implementation uses:\n\n* [`HSET`](https://redis.io/docs/latest/commands/hset) + [`EXPIRE`](https://redis.io/docs/latest/commands/expire), pipelined, for the bulk load and every sync event\n* [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) on the read path\n* [`DEL`](https://redis.io/docs/latest/commands/del) for sync-delete events and explicit invalidation\n* [`SCAN`](https://redis.io/docs/latest/commands/scan) to enumerate the cached keyspace and to clear the prefix\n* [`TTL`](https://redis.io/docs/latest/commands/ttl) to surface remaining safety-net time in the demo UI"},{"id":"bulk-load-on-startup","title":"Bulk load on startup","role":"content","text":"The `bulkLoad` method pipelines a `DEL` + `HSET` + `EXPIRE` triple for every record. The pipeline is sent in a single round trip, so loading thousands of records takes one network RTT plus the time Redis spends executing the commands locally — typically tens of milliseconds even for a large reference table:\n\n[code example]\n\n`execAsPipeline()` is intentional on the **startup** path: nothing is reading the cache yet, the records do not need to be applied atomically as a set, and skipping `MULTI`/`EXEC` keeps the bulk load fast. The same method is used for the live `/reprefetch` reload, which is safe because the demo pauses the sync worker around the clear-and-reload sequence — see [Re-prefetch under load](#re-prefetch-under-load) below. If you call `bulkLoad` directly from your own code on a cache that is already serving reads, either pause your writers first or rewrite it with `multi().exec()` so callers cannot observe a half-loaded record."},{"id":"reads-from-redis-only","title":"Reads from Redis only","role":"content","text":"The `get` method runs `HGETALL` and returns the cached hash. **It does not fall back to the primary on a miss.** In a healthy system, a miss never happens; if it does, the application surfaces it as an error and treats it as a sync-pipeline incident:\n\n[code example]\n\nThis is the key behavioural difference from [cache-aside](https://redis.io/docs/latest/develop/use-cases/cache-aside): the request path never touches the primary, so reference-data reads cannot contribute to primary database load."},{"id":"applying-sync-events","title":"Applying sync events","role":"content","text":"The sync worker calls `applyChange` for every primary mutation. For an `upsert`, the helper rewrites the cache hash and refreshes the safety-net TTL in one pipelined transaction so the cache never holds a stale mix of old and new fields. For a `delete`, it removes the cache key:\n\n[code example]\n\nThe `DEL` before the `HSET` ensures the cached hash contains exactly the fields the primary record has now — fields that have been dropped from the primary will not linger in Redis. The empty-fields guard avoids crashing the sync worker if a malformed event arrives, because node-redis rejects an `hSet` call with an empty object."},{"id":"the-sync-worker","title":"The sync worker","role":"content","text":"The `SyncWorker` runs an async task that awaits the primary's change queue with a short timeout. Every change is applied to Redis as soon as it arrives\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/prefetch-cache/nodejs/sync_worker.js)):\n\n[code example]\n\nIn production this loop is replaced by a CDC consumer reading from RDI's Redis output stream, Debezium's Kafka topic, or an equivalent change feed. The shape stays the same: drain events, apply them to Redis, advance the consumer offset."},{"id":"invalidation-and-re-prefetch","title":"Invalidation and re-prefetch","role":"content","text":"Two helpers exist for testing and recovery:\n\n* `invalidate(entityId)` deletes a single cache key. The demo uses it to simulate a sync-pipeline failure on one record.\n* `clear()` runs `SCAN MATCH cache:category:*` and deletes every key under the prefix. The demo uses it to simulate a full cache loss.\n\nIn both cases, the recovery path is to call `bulkLoad(await primary.listRecords())` again — re-prefetching from the primary. The demo exposes this as the \"Re-prefetch\" button so you can see the cache come back to a fully-warm state in one operation."},{"id":"re-prefetch-under-load","title":"Re-prefetch under load","role":"content","text":"`clear()` and `bulkLoad()` are not atomic against the sync worker. If a change event arrives between the snapshot (`primary.listRecords()`) and the bulk write, the bulk write can overwrite a newer value; if a change event arrives between `clear()`'s `SCAN` and `DEL`, the cleared entry can immediately be recreated. The demo's `/clear` and `/reprefetch` handlers solve this by pausing the sync worker around the operation:\n\n[code example]\n\n`pause()` waits for the worker to finish whatever event it is currently applying, parks the run loop, and returns. Change events that arrive during the pause sit in the primary's queue and apply in order once `resume()` is called, so no event is lost."},{"id":"hitmiss-accounting","title":"Hit/miss accounting","role":"content","text":"The helper keeps in-process counters for hits, misses, prefetched records, sync events applied, and the average lag between a primary change and its application to Redis. The demo UI surfaces these so you can confirm the cache is absorbing all reads and the sync worker is keeping up:\n\n[code example]\n\nIn production you would emit these as Prometheus counters and gauges. The sync-lag metric is the most important: a sudden rise indicates the CDC pipeline is falling behind."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before running the demo, make sure that:\n\n* Redis is running and accessible. By default, the demo connects to `localhost:6379`.\n* Node.js 18 or later is installed.\n* The `redis` (node-redis) package is installed via `npm install`. The demo pins to the latest 5.x.\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of five files. Download them from the [`nodejs` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/prefetch-cache/nodejs) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From that directory:\n\n[code example]\n\nYou should see something like:\n\n[code example]\n\nAfter starting the server, visit `http://localhost:8783`.\n\nThe demo server uses only Node.js standard library features for HTTP and concurrency:\n\n* The [`http`](https://nodejs.org/api/http.html) module for the web server, with manual route dispatch on `req.method` and `url.pathname`\n* The [`url`](https://nodejs.org/api/url.html) module's `URL` and `URLSearchParams` for query and form decoding\n* `async`/`await` for the sync worker's long-running task\n\nIt exposes a small interactive page where you can:\n\n* See which IDs are in the cache and in the primary, side by side\n* Read a category through the cache and confirm every read is a hit\n* Update a field on the primary and watch the sync worker rewrite the cache hash\n* Add and delete categories and watch them appear and disappear from the cache\n* Invalidate one key or clear the entire cache to simulate a sync-pipeline failure\n* Re-prefetch from the primary to recover from a broken cache state\n* Watch the average sync lag, and confirm primary reads stay at one until you re-prefetch — each `/reprefetch` adds another primary read for the snapshot, but normal request traffic never reaches the primary at all"},{"id":"the-mock-primary-store","title":"The mock primary store","role":"content","text":"To make the demo self-contained, the example includes a `MockPrimaryStore` that stands in for a source-of-truth database\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/prefetch-cache/nodejs/primary.js)):\n\n[code example]\n\nEvery mutation appends a change event to an in-process queue and the sync worker `await`s `primary.nextChange(timeoutMs)` to drain it. Node.js JS execution is single-threaded, so the mutate-then-emit pair runs without any `await` between the two steps — that's how the queue order stays aligned with the mutation order. In a real system this queue is replaced by a CDC pipeline — RDI on Redis Enterprise or Debezium with a Redis consumer on open-source Redis."},{"id":"production-usage","title":"Production usage","role":"content","text":"This guide uses a deliberately small local demo so you can focus on the prefetch-cache pattern. In production, you will usually want to harden several aspects of it."},{"id":"replace-the-in-process-change-queue-with-a-real-cdc-pipeline","title":"Replace the in-process change queue with a real CDC pipeline","role":"content","text":"The demo's in-process queue is the simplest possible stand-in for a CDC change feed. In production, the change feed lives outside the application process: an RDI pipeline configured against your primary database, Debezium connectors writing to Kafka or a Redis stream, or your application explicitly publishing change events from the write path. Whatever you choose, the consumer side stays the same — read events, apply them to Redis, advance the offset."},{"id":"use-a-long-safety-net-ttl-not-a-freshness-ttl","title":"Use a long safety-net TTL, not a freshness TTL","role":"content","text":"The TTL on each cache key is a **safety net**: it bounds memory if the sync pipeline silently stops, so a stuck consumer cannot leave stale data in Redis indefinitely. The TTL is not the freshness mechanism — freshness comes from the sync worker, which refreshes the TTL on every add or update event (delete events remove the key). Pick a TTL that is comfortably longer than your worst-case sync lag plus your alerting window, so a transient sync hiccup never expires hot keys."},{"id":"decide-what-to-do-on-a-cache-miss","title":"Decide what to do on a cache miss","role":"content","text":"A prefetch cache treats a miss as an error or a missing record. The two reasonable strategies are:\n\n* **Return a 404 to the user.** Appropriate when the cache is authoritative for the lookup — for example, when the user is asking for a category by ID and the ID is not in the cache.\n* **Page on-call.** A sustained miss rate on IDs you know exist is an incident: either the prefetch did not run, or the sync pipeline is broken.\n\nWhichever you choose, do not fall back to the primary on the read path — that is what cache-aside is for, and conflating the two patterns breaks the load-isolation guarantee that prefetch provides."},{"id":"bound-the-working-set-to-what-fits-in-memory","title":"Bound the working set to what fits in memory","role":"content","text":"Prefetch only works if the entire dataset fits in Redis memory with headroom. Estimate the size of your reference data, multiply by a growth factor, and confirm the result fits within your Redis instance's `maxmemory` minus what other use cases need. If the working set grows beyond what Redis can hold, switch the dataset to a cache-aside pattern instead — the request path will pay miss latency, but you will not OOM."},{"id":"reconcile-periodically-against-the-primary","title":"Reconcile periodically against the primary","role":"content","text":"CDC pipelines are eventually consistent: an event can be lost (broker outage, consumer crash, configuration drift) and the cache can silently diverge from the source. Run a periodic reconciliation job that re-reads all primary records, compares them against the cache, and either re-prefetches or fixes individual entries. Even running it once a day catches drift that ad-hoc inspection would miss."},{"id":"namespace-cache-keys-in-shared-redis-deployments","title":"Namespace cache keys in shared Redis deployments","role":"content","text":"If multiple applications share a Redis deployment, prefix cache keys with the application name (`cache:billing:category:{id}`) so different services cannot clobber each other's entries. The helper takes a `prefix` constructor option exactly for this."},{"id":"use-a-connection-pool-or-a-single-multiplexed-client","title":"Use a connection pool or a single multiplexed client","role":"content","text":"The demo shares a single `createClient()` connection across the HTTP handlers and the sync worker. node-redis 5.x multiplexes commands over that one socket, so a single client handles concurrent reads, transactions, and pipelined writes without any in-process locking. In production, monitor the client's `error` event and reconnect-and-retry on transient failures, and consider a small pool of clients if you need to isolate slow blocking commands from the rest of your traffic."},{"id":"inspect-cached-entries-directly-in-redis","title":"Inspect cached entries directly in Redis","role":"content","text":"When testing or troubleshooting, inspect the stored cache keys directly to confirm the bulk load and the sync worker are writing what you expect:\n\n[code example]\n\nIf a key is missing for an ID that still exists in the primary, the prefetch did not run, the key expired without a sync refresh, or someone invalidated it. If a key is still present for an ID that was deleted in the primary, the delete event has not yet been applied. If the TTL is much lower than the configured safety-net value on a hot key, the sync worker is not keeping up."},{"id":"learn-more","title":"Learn more","role":"related","text":"* [node-redis guide](https://redis.io/docs/latest/develop/clients/nodejs) - Install and use the Node.js Redis client\n* [HSET command](https://redis.io/docs/latest/commands/hset) - Write hash fields\n* [HGETALL command](https://redis.io/docs/latest/commands/hgetall) - Read every field of a hash\n* [EXPIRE command](https://redis.io/docs/latest/commands/expire) - Set key expiration in seconds\n* [DEL command](https://redis.io/docs/latest/commands/del) - Delete a key on invalidation or sync-delete\n* [SCAN command](https://redis.io/docs/latest/commands/scan) - Iterate the cached keyspace without blocking the server\n* [TTL command](https://redis.io/docs/latest/commands/ttl) - Inspect remaining safety-net time on a key\n* [Redis Data Integration](https://redis.io/docs/latest/integrate/redis-data-integration) - Configuration-driven CDC into Redis on Redis Enterprise and Redis Cloud"}],"examples":[{"id":"the-prefetch-cache-helper-ex0","language":"javascript","code":"const { createClient } = require(\"redis\");\nconst { PrefetchCache } = require(\"./cache\");\nconst { MockPrimaryStore } = require(\"./primary\");\nconst { SyncWorker } = require(\"./sync_worker\");\n\nconst client = createClient({ socket: { host: \"localhost\", port: 6379 } });\nawait client.connect();\n\nconst primary = new MockPrimaryStore();\nconst cache = new PrefetchCache({ redisClient: client, ttlSeconds: 3600 });\n\n// Pre-load every primary record into Redis in one pipelined round trip.\nawait cache.bulkLoad(await primary.listRecords());\n\n// Start the sync worker so primary mutations propagate into Redis.\nconst sync = new SyncWorker({ primary, cache });\nsync.start();\n\n// Read paths now go to Redis only.\nconst { record, hit, redisLatencyMs } = await cache.get(\"cat-001\");","section_id":"the-prefetch-cache-helper"},{"id":"data-model-ex0","language":"text","code":"cache:category:cat-001\n  id            = cat-001\n  name          = Beverages\n  display_order = 1\n  featured      = true\n  parent_id     =","section_id":"data-model"},{"id":"bulk-load-on-startup-ex0","language":"javascript","code":"async bulkLoad(records) {\n  let loaded = 0;\n  const pipe = this.redis.multi();\n  for (const record of records) {\n    const entityId = record && record.id;\n    if (!entityId) continue;\n    const cacheKey = this._cacheKey(entityId);\n    pipe.del(cacheKey);\n    pipe.hSet(cacheKey, record);\n    pipe.expire(cacheKey, this.ttlSeconds);\n    loaded += 1;\n  }\n  if (loaded > 0) {\n    await pipe.execAsPipeline();\n  }\n  this._prefetched += loaded;\n  return loaded;\n}","section_id":"bulk-load-on-startup"},{"id":"reads-from-redis-only-ex0","language":"javascript","code":"async get(entityId) {\n  const cacheKey = this._cacheKey(entityId);\n\n  const started = process.hrtime.bigint();\n  const cached = await this.redis.hGetAll(cacheKey);\n  const redisLatencyMs = Number(process.hrtime.bigint() - started) / 1e6;\n\n  if (cached && Object.keys(cached).length > 0) {\n    this._hits += 1;\n    return { record: cached, hit: true, redisLatencyMs };\n  }\n\n  this._misses += 1;\n  return { record: null, hit: false, redisLatencyMs };\n}","section_id":"reads-from-redis-only"},{"id":"applying-sync-events-ex0","language":"javascript","code":"async applyChange(change) {\n  const { op, id: entityId, fields } = change;\n  if (!entityId) return;\n\n  const cacheKey = this._cacheKey(entityId);\n\n  if (op === \"upsert\") {\n    if (!fields || Object.keys(fields).length === 0) return;\n    await this.redis\n      .multi()\n      .del(cacheKey)\n      .hSet(cacheKey, fields)\n      .expire(cacheKey, this.ttlSeconds)\n      .exec();\n  } else if (op === \"delete\") {\n    await this.redis.del(cacheKey);\n  }\n}","section_id":"applying-sync-events"},{"id":"the-sync-worker-ex0","language":"javascript","code":"async _run() {\n  while (!this._stopped) {\n    if (this._paused) {\n      if (this._pausedIdleResolve) this._pausedIdleResolve();\n      while (this._paused && !this._stopped) await this._resumePromise;\n      continue;\n    }\n\n    const change = await this.primary.nextChange(this.pollTimeoutMs);\n    if (change == null) continue;\n    try {\n      await this.cache.applyChange(change);\n    } catch (err) {\n      console.error(`[sync] failed to apply ${JSON.stringify(change)}: ${err && err.message}`);\n    }\n  }\n}","section_id":"the-sync-worker"},{"id":"re-prefetch-under-load-ex0","language":"javascript","code":"await sync.pause();\ntry {\n  await cache.clear();\n  await cache.bulkLoad(await primary.listRecords());\n} finally {\n  sync.resume();\n}","section_id":"re-prefetch-under-load"},{"id":"hitmiss-accounting-ex0","language":"javascript","code":"stats() {\n  const total = this._hits + this._misses;\n  const hitRate = total > 0 ? Math.round((1000 * this._hits) / total) / 10 : 0.0;\n  const avgLag = this._syncLagSamples > 0\n    ? Math.round((this._syncLagMsTotal / this._syncLagSamples) * 100) / 100\n    : 0.0;\n  return {\n    hits: this._hits,\n    misses: this._misses,\n    hit_rate_pct: hitRate,\n    prefetched: this._prefetched,\n    sync_events_applied: this._syncEventsApplied,\n    sync_lag_ms_avg: avgLag,\n  };\n}","section_id":"hitmiss-accounting"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir prefetch-cache-demo && cd prefetch-cache-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/prefetch-cache/nodejs\ncurl -O $BASE/cache.js\ncurl -O $BASE/primary.js\ncurl -O $BASE/sync_worker.js\ncurl -O $BASE/demoServer.js\ncurl -O $BASE/package.json\nnpm install","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"node demoServer.js","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"Redis prefetch-cache demo server listening on http://127.0.0.1:8783\nUsing Redis at localhost:6379 with cache prefix 'cache:category:' and TTL 3600s\nPrefetched 5 records in 85.5 ms; sync worker running","section_id":"start-the-demo-server"},{"id":"the-mock-primary-store-ex0","language":"javascript","code":"class MockPrimaryStore {\n  constructor({ readLatencyMs = 80 } = {}) { /* ... */ }\n\n  async listRecords() {\n    await new Promise((r) => setTimeout(r, this.readLatencyMs));\n    this._reads += 1;\n    return [...this._records.values()].map((r) => ({ ...r }));\n  }\n\n  updateField(entityId, field, value) {\n    const record = this._records.get(entityId);\n    if (record === undefined) return false;\n    record[field] = String(value);\n    this._emitChange(\"upsert\", entityId, { ...record });\n    return true;\n  }\n}","section_id":"the-mock-primary-store"},{"id":"inspect-cached-entries-directly-in-redis-ex0","language":"bash","code":"redis-cli --scan --pattern 'cache:category:*'\nredis-cli HGETALL cache:category:cat-001\nredis-cli TTL cache:category:cat-001","section_id":"inspect-cached-entries-directly-in-redis"}]}
{"schema_version":2,"id":"develop/use-cases/prefetch-cache/php","title":"Redis prefetch cache with Predis","url":"https://redis.io/docs/latest/develop/use-cases/prefetch-cache/php/","summary":"Implement a Redis prefetch cache in PHP with Predis","aliases":["/develop/use-cases/prefetch-cache/predis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:54:57+01:00","children":[],"page_type":"content","content_hash":"7b20550b21c9435053ad36c575e8b1639002bb1ff57e645e384e2a98e52aeb5a","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis prefetch cache in PHP with [Predis](https://github.com/predis/predis). It includes a small local web server built on PHP's built-in dev server so you can watch the cache pre-load at startup, see a background sync worker apply primary mutations within milliseconds, and break the cache to confirm that reads never fall back to the primary."},{"id":"overview","title":"Overview","role":"overview","text":"Prefetch caching pre-loads a working set of reference data into Redis before the first request arrives, so every read on the request path is a cache hit. A separate sync worker keeps the cache current as the source of truth changes — there is no fall-back to the primary on the read path.\n\nThat gives you:\n\n* Near-100% cache hit ratios for reference and master data\n* Sub-millisecond reads for lookup-heavy paths at peak traffic\n* All reference-data reads offloaded from the primary database\n* Source-database changes propagated into Redis within a few milliseconds\n* A long safety-net TTL that bounds memory if the sync pipeline ever stops\n\nIn this example, each cached category is stored as a Redis hash under a key like `cache:category:{id}`. The hash holds the category fields (`id`, `name`, `display_order`, `featured`, `parent_id`) and the key has a long safety-net TTL that the sync worker refreshes on every add or update event. Delete events remove the cache key outright, so there is no TTL to refresh in that case."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow has three independent paths:\n\n1. **On startup**, the demo server calls `$cache->bulkLoad($primary->listRecords())`, which pipelines `DEL` + `HSET` + `EXPIRE` for every record in one round trip.\n2. **On every read**, the application calls `$cache->get($entityId)`, which runs `HGETALL` against Redis only. A miss is treated as an error, not a trigger to query the primary.\n3. **On every primary mutation**, the primary appends a change event to a Redis list (`demo:primary:changes`). A separate `sync_worker.php` process drains the list with `BRPOP` and calls `$cache->applyChange($event)`. For an `upsert`, the helper rewrites the cache hash and refreshes the safety-net TTL; for a `delete`, it removes the cache key.\n\nIn a real system, the Redis-list change feed is replaced by a CDC pipeline — [Redis Data Integration](https://redis.io/docs/latest/integrate/redis-data-integration), Debezium plus a lightweight consumer, or an equivalent tool that tails the source's binlog/WAL and pushes events into Redis."},{"id":"the-prefetch-cache-helper","title":"The prefetch-cache helper","role":"content","text":"The `PrefetchCache` class wraps the cache operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/prefetch-cache/php/Cache.php)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each cached category is stored in a Redis hash:\n\n[code example]\n\nThe implementation uses:\n\n* [`HSET`](https://redis.io/docs/latest/commands/hset) + [`EXPIRE`](https://redis.io/docs/latest/commands/expire), pipelined, for the bulk load and every sync event\n* [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) on the read path\n* [`DEL`](https://redis.io/docs/latest/commands/del) for sync-delete events and explicit invalidation\n* [`SCAN`](https://redis.io/docs/latest/commands/scan) to enumerate the cached keyspace and to clear the prefix\n* [`TTL`](https://redis.io/docs/latest/commands/ttl) to surface remaining safety-net time in the demo UI"},{"id":"bulk-load-on-startup","title":"Bulk load on startup","role":"content","text":"The `bulkLoad` method pipelines a `DEL` + `HSET` + `EXPIRE` triple for every record. The pipeline is sent in a single round trip, so loading thousands of records takes one network RTT plus the time Redis spends executing the commands locally — typically tens of milliseconds even for a large reference table:\n\n[code example]\n\nThe Predis pipeline is non-transactional by default, which is intentional on the **startup** path: nothing is reading the cache yet, the records do not need to be applied atomically as a set, and skipping `MULTI`/`EXEC` keeps the bulk load fast. The same method is used for the live `/reprefetch` reload, which is safe because the demo pauses the sync worker around the clear-and-reload sequence — see [Re-prefetch under load](#re-prefetch-under-load) below. If you call `bulkLoad` directly from your own code on a cache that is already serving reads, either pause your writers first or wrap the pipeline in a `MULTI`/`EXEC` transaction so callers cannot observe a half-loaded record.\n\n`flattenFields()` converts the associative `['field' => 'value']` record into the variadic field/value/field/value form `HSET` expects in Predis 3.x. Predis 1.x accepted an associative array directly; the 1.x form raises `wrong number of arguments for 'hset'` against 3.x."},{"id":"reads-from-redis-only","title":"Reads from Redis only","role":"content","text":"The `get` method runs `HGETALL` and returns the cached hash. **It does not fall back to the primary on a miss.** In a healthy system, a miss never happens; if it does, the application surfaces it as an error and treats it as a sync-pipeline incident:\n\n[code example]\n\nThis is the key behavioural difference from [cache-aside](https://redis.io/docs/latest/develop/use-cases/cache-aside): the request path never touches the primary, so reference-data reads cannot contribute to primary database load."},{"id":"applying-sync-events","title":"Applying sync events","role":"content","text":"The sync worker calls `applyChange` for every primary mutation. For an `upsert`, the helper rewrites the cache hash and refreshes the safety-net TTL in one pipelined `MULTI`/`EXEC` transaction so the cache never holds a stale mix of old and new fields. For a `delete`, it removes the cache key:\n\n[code example]\n\nThe `DEL` before the `HSET` ensures the cached hash contains exactly the fields the primary record has now — fields that have been dropped from the primary will not linger in Redis. The malformed-upsert guard (`empty($fields)` returns early) prevents `HSET` from being called with no arguments, which raises an error in Predis 3.x."},{"id":"the-sync-worker","title":"The sync worker","role":"content","text":"The sync worker is a separate long-running PHP process: `sync_worker.php` ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/prefetch-cache/php/sync_worker.php)). The demo server spawns one of these on first request through a `SyncWorkerSupervisor` and tracks its PID in Redis ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/prefetch-cache/php/SyncWorker.php)):\n\n[code example]\n\n`MockPrimaryStore::nextChange()` blocks on `BRPOP demo:primary:changes <timeout>`, so the worker uses a fraction of a CPU when idle and reacts within milliseconds when a change arrives.\n\nIn production this loop is replaced by a CDC consumer reading from RDI's Redis output stream, Debezium's Kafka topic, or an equivalent change feed. The shape stays the same: drain events, apply them to Redis, advance the consumer offset."},{"id":"invalidation-and-re-prefetch","title":"Invalidation and re-prefetch","role":"content","text":"Two helpers exist for testing and recovery:\n\n* `invalidate($entityId)` deletes a single cache key. The demo uses it to simulate a sync-pipeline failure on one record.\n* `clear()` runs `SCAN MATCH cache:category:*` and deletes every key under the prefix. The demo uses it to simulate a full cache loss.\n\nIn both cases, the recovery path is to call `bulkLoad($primary->listRecords())` again — re-prefetching from the primary. The demo exposes this as the \"Re-prefetch\" button so you can see the cache come back to a fully-warm state in one operation."},{"id":"re-prefetch-under-load","title":"Re-prefetch under load","role":"content","text":"`clear()` and `bulkLoad()` are not atomic against the sync worker. If a change event arrives between the snapshot (`primary->listRecords()`) and the bulk write, the bulk write can overwrite a newer value; if a change event arrives between `clear()`'s `SCAN` and `DEL`, the cleared entry can immediately be recreated. The demo's `/clear` and `/reprefetch` handlers solve this by pausing the sync worker around the operation:\n\n[code example]\n\n`pause()` writes `demo:sync:paused = \"1\"`, then waits up to 2 seconds for the worker to write `demo:sync:idle = \"1\"` (the worker writes this key once it has parked itself on the pause flag). Once `pause()` returns true, no `applyChange` is in flight and no new event will be drained from the change list. Change events that arrive during the pause sit on `demo:primary:changes` and apply in order once `resume()` is called.\n\nThis pattern is forced by PHP's process model: the demo server runs under `php -S`, every HTTP request is a fresh process, and the sync worker lives in a separate long-running process. The pause / idle signals cannot live in shared memory; Redis is the only place every component already talks to."},{"id":"hitmiss-accounting","title":"Hit/miss accounting","role":"content","text":"The helper keeps counters for hits, misses, prefetched records, sync events applied, and the average lag between a primary change and its application to Redis. The demo UI surfaces these so you can confirm the cache is absorbing all reads and the sync worker is keeping up.\n\nIn PHP under `php -S`, every HTTP request runs in its own process, so the counters cannot live in object properties. The helper stores them in a Redis hash under `demo:stats:{prefix}` and uses `HINCRBY` / `HINCRBYFLOAT` to update them. Every HTTP request, plus the sync worker process, sees the same totals:\n\n[code example]\n\nIn production you would emit these as Prometheus counters and gauges. The sync-lag metric is the most important: a sudden rise indicates the CDC pipeline is falling behind."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before running the demo, make sure that:\n\n* PHP 8.1 or later is installed (`php --version`)\n* [Composer](https://getcomposer.org/) is installed\n* Redis is running and accessible. By default, the demo connects to `127.0.0.1:6379`\n\nIf your Redis server is running elsewhere, start the demo with the `PREFETCH_REDIS_HOST` and `PREFETCH_REDIS_PORT` environment variables."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of six files. Download them from the [`php` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/prefetch-cache/php) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From that directory, install Predis and start the server:\n\n[code example]\n\nAfter starting the server, visit `http://127.0.0.1:8788`.\n\nOn the first request the server will:\n\n* Seed the mock primary store into Redis (records under `demo:primary:hash:{id}`, ID set under `demo:primary:ids`).\n* Bulk-load every primary record into the cache.\n* Spawn a long-running `sync_worker.php` process via `proc_open` and record its PID in `demo:sync:pid`.\n\nThe sync worker keeps running until you stop it manually. To stop everything cleanly:\n\n[code example]\n\nThe demo server uses only the PHP standard library and Predis:\n\n* PHP's [built-in dev server](https://www.php.net/manual/en/features.commandline.webserver.php) for HTTP\n* [`proc_open`](https://www.php.net/manual/en/function.proc-open.php) + `posix_kill` for spawning and signalling the sync worker\n* [Predis](https://github.com/predis/predis) for every Redis command\n\nIt exposes a small interactive page where you can:\n\n* See which IDs are in the cache and in the primary, side by side\n* Read a category through the cache and confirm every read is a hit\n* Update a field on the primary and watch the sync worker rewrite the cache hash\n* Add and delete categories and watch them appear and disappear from the cache\n* Invalidate one key or clear the entire cache to simulate a sync-pipeline failure\n* Re-prefetch from the primary to recover from a broken cache state\n* Watch the average sync lag, and confirm primary reads stay at one until you re-prefetch — each `/reprefetch` adds another primary read for the snapshot, but normal request traffic never reaches the primary at all"},{"id":"the-mock-primary-store","title":"The mock primary store","role":"content","text":"To make the demo self-contained, the example includes a `MockPrimaryStore` that stands in for a source-of-truth database\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/prefetch-cache/php/Primary.php)).\n\nEach record lives in its own Redis hash under `demo:primary:hash:{id}`, and the set `demo:primary:ids` tracks the current ID universe. The change feed is a Redis list (`demo:primary:changes`); every mutation `LPUSH`es a JSON-encoded change event and the sync worker drains the list with `BRPOP`.\n\nThe reference Python implementation guards mutation + emit with an in-process lock so two concurrent updates produce change events in queue order matching mutation order. PHP doesn't have shared memory across requests, so the PHP port runs each mutation as a Lua script on the Redis server. Lua scripts run atomically on the Redis main thread, so the mutation and the `LPUSH` happen as one step and the queue order can't get scrambled:\n\n[code example]\n\nIn a real system this list-based change feed is replaced by a CDC pipeline — RDI on Redis Enterprise or Debezium with a Redis consumer on open-source Redis."},{"id":"production-usage","title":"Production usage","role":"content","text":"This guide uses a deliberately small local demo so you can focus on the prefetch-cache pattern. In production, you will usually want to harden several aspects of it."},{"id":"replace-the-in-process-change-queue-with-a-real-cdc-pipeline","title":"Replace the in-process change queue with a real CDC pipeline","role":"content","text":"The demo's Redis-list change feed is the simplest possible stand-in for a CDC change feed. In production, the change feed lives outside the application process: an RDI pipeline configured against your primary database, Debezium connectors writing to Kafka or a Redis stream, or your application explicitly publishing change events from the write path. Whatever you choose, the consumer side stays the same — read events, apply them to Redis, advance the offset."},{"id":"run-the-sync-worker-as-a-managed-service","title":"Run the sync worker as a managed service","role":"content","text":"Under `php -S`, every HTTP request runs in its own short-lived process, so this demo spawns a separate `sync_worker.php` process via `proc_open` and records its PID in Redis. That keeps the demo self-contained, but it is not how you would run this in production: the supervisor's lifecycle is bound to whichever HTTP request happened to spawn the worker, and a stale PID can survive a server restart.\n\nIn production, run the sync worker as a managed service — systemd, supervisord, Kubernetes, or whatever your platform uses to run long-lived workers — and remove the in-request spawning entirely. The pause/resume coordination still belongs in Redis (`demo:sync:paused` / `demo:sync:idle`), because the application processes that handle `/clear` and `/reprefetch` still need a way to ask the out-of-band worker to park itself before they rewrite the cache. The supervisor in this demo is only there to give you a one-command \"press play and watch the cache work\" experience."},{"id":"use-a-long-safety-net-ttl-not-a-freshness-ttl","title":"Use a long safety-net TTL, not a freshness TTL","role":"content","text":"The TTL on each cache key is a **safety net**: it bounds memory if the sync pipeline silently stops, so a stuck consumer cannot leave stale data in Redis indefinitely. The TTL is not the freshness mechanism — freshness comes from the sync worker, which refreshes the TTL on every add or update event (delete events remove the key). Pick a TTL that is comfortably longer than your worst-case sync lag plus your alerting window, so a transient sync hiccup never expires hot keys."},{"id":"decide-what-to-do-on-a-cache-miss","title":"Decide what to do on a cache miss","role":"content","text":"A prefetch cache treats a miss as an error or a missing record. The two reasonable strategies are:\n\n* **Return a 404 to the user.** Appropriate when the cache is authoritative for the lookup — for example, when the user is asking for a category by ID and the ID is not in the cache.\n* **Page on-call.** A sustained miss rate on IDs you know exist is an incident: either the prefetch did not run, or the sync pipeline is broken.\n\nWhichever you choose, do not fall back to the primary on the read path — that is what cache-aside is for, and conflating the two patterns breaks the load-isolation guarantee that prefetch provides."},{"id":"bound-the-working-set-to-what-fits-in-memory","title":"Bound the working set to what fits in memory","role":"content","text":"Prefetch only works if the entire dataset fits in Redis memory with headroom. Estimate the size of your reference data, multiply by a growth factor, and confirm the result fits within your Redis instance's `maxmemory` minus what other use cases need. If the working set grows beyond what Redis can hold, switch the dataset to a cache-aside pattern instead — the request path will pay miss latency, but you will not OOM."},{"id":"reconcile-periodically-against-the-primary","title":"Reconcile periodically against the primary","role":"content","text":"CDC pipelines are eventually consistent: an event can be lost (broker outage, consumer crash, configuration drift) and the cache can silently diverge from the source. Run a periodic reconciliation job that re-reads all primary records, compares them against the cache, and either re-prefetches or fixes individual entries. Even running it once a day catches drift that ad-hoc inspection would miss."},{"id":"namespace-cache-keys-in-shared-redis-deployments","title":"Namespace cache keys in shared Redis deployments","role":"content","text":"If multiple applications share a Redis deployment, prefix cache keys with the application name (`cache:billing:category:{id}`) so different services cannot clobber each other's entries. The helper takes a `prefix` argument exactly for this."},{"id":"inspect-cached-entries-directly-in-redis","title":"Inspect cached entries directly in Redis","role":"content","text":"When testing or troubleshooting, inspect the stored cache keys directly to confirm the bulk load and the sync worker are writing what you expect:\n\n[code example]\n\nIf a key is missing for an ID that still exists in the primary, the prefetch did not run, the key expired without a sync refresh, or someone invalidated it. If a key is still present for an ID that was deleted in the primary, the delete event has not yet been applied. If the TTL is much lower than the configured safety-net value on a hot key, the sync worker is not keeping up."},{"id":"learn-more","title":"Learn more","role":"related","text":"* [Predis on GitHub](https://github.com/predis/predis) - The Predis client for PHP\n* [HSET command](https://redis.io/docs/latest/commands/hset) - Write hash fields\n* [HGETALL command](https://redis.io/docs/latest/commands/hgetall) - Read every field of a hash\n* [EXPIRE command](https://redis.io/docs/latest/commands/expire) - Set key expiration in seconds\n* [DEL command](https://redis.io/docs/latest/commands/del) - Delete a key on invalidation or sync-delete\n* [SCAN command](https://redis.io/docs/latest/commands/scan) - Iterate the cached keyspace without blocking the server\n* [BRPOP command](https://redis.io/docs/latest/commands/brpop) - Block on a list for the next change event\n* [TTL command](https://redis.io/docs/latest/commands/ttl) - Inspect remaining safety-net time on a key\n* [Redis Data Integration](https://redis.io/docs/latest/integrate/redis-data-integration) - Configuration-driven CDC into Redis on Redis Enterprise and Redis Cloud"}],"examples":[{"id":"the-prefetch-cache-helper-ex0","language":"php","code":"require __DIR__ . '/vendor/autoload.php';\nrequire __DIR__ . '/Cache.php';\nrequire __DIR__ . '/Primary.php';\nrequire __DIR__ . '/SyncWorker.php';\n\nuse Predis\\Client as PredisClient;\n\n$redis = new PredisClient(['host' => '127.0.0.1', 'port' => 6379]);\n$primary = new MockPrimaryStore($redis);\n$cache = new PrefetchCache($redis, 'cache:category:', 3600);\n\n// Pre-load every primary record into Redis in one pipelined round trip.\n$cache->bulkLoad($primary->listRecords());\n\n// Spawn the long-running sync_worker.php process.\n$supervisor = new SyncWorkerSupervisor($redis, __DIR__ . '/sync_worker.php');\n$supervisor->start();\n\n// Read paths now go to Redis only.\n[$record, $hit, $redisMs] = $cache->get('cat-001');","section_id":"the-prefetch-cache-helper"},{"id":"data-model-ex0","language":"text","code":"cache:category:cat-001\n  id            = cat-001\n  name          = Beverages\n  display_order = 1\n  featured      = true\n  parent_id     =","section_id":"data-model"},{"id":"bulk-load-on-startup-ex0","language":"php","code":"public function bulkLoad(iterable $records): int\n{\n    $loaded = 0;\n    $pipe = $this->redis->pipeline();\n    foreach ($records as $record) {\n        $entityId = $record['id'] ?? '';\n        if ($entityId === '') {\n            continue;\n        }\n        $cacheKey = $this->cacheKey($entityId);\n        $pipe->del([$cacheKey]);\n        $pipe->hset($cacheKey, ...self::flattenFields($record));\n        $pipe->expire($cacheKey, $this->ttlSeconds);\n        $loaded++;\n    }\n    if ($loaded > 0) {\n        $pipe->execute();\n        $this->redis->hincrby($this->statsKey, 'prefetched', $loaded);\n    }\n    return $loaded;\n}","section_id":"bulk-load-on-startup"},{"id":"reads-from-redis-only-ex0","language":"php","code":"public function get(string $entityId): array\n{\n    $cacheKey = $this->cacheKey($entityId);\n    $started = microtime(true);\n    $cached = $this->redis->hgetall($cacheKey);\n    $redisLatencyMs = (microtime(true) - $started) * 1000.0;\n\n    if (is_array($cached) && !empty($cached)) {\n        $this->redis->hincrby($this->statsKey, 'hits', 1);\n        return [$cached, true, $redisLatencyMs];\n    }\n\n    $this->redis->hincrby($this->statsKey, 'misses', 1);\n    return [null, false, $redisLatencyMs];\n}","section_id":"reads-from-redis-only"},{"id":"applying-sync-events-ex0","language":"php","code":"public function applyChange(array $change): void\n{\n    $op = $change['op'] ?? '';\n    $entityId = $change['id'] ?? '';\n    if ($entityId === '') {\n        return;\n    }\n    $cacheKey = $this->cacheKey($entityId);\n\n    if ($op === 'upsert') {\n        $fields = $change['fields'] ?? null;\n        if (!is_array($fields) || empty($fields)) {\n            return;\n        }\n        $tx = $this->redis->transaction();\n        $tx->del([$cacheKey]);\n        $tx->hset($cacheKey, ...self::flattenFields($fields));\n        $tx->expire($cacheKey, $this->ttlSeconds);\n        $tx->execute();\n    } elseif ($op === 'delete') {\n        $this->redis->del([$cacheKey]);\n    }\n    // ... record sync_events_applied + sync lag\n}","section_id":"applying-sync-events"},{"id":"the-sync-worker-ex0","language":"php","code":"public function run(): void\n{\n    if (function_exists('pcntl_async_signals')) {\n        pcntl_async_signals(true);\n        pcntl_signal(SIGTERM, function () { $this->stop = true; });\n    }\n\n    while (!$this->stop) {\n        if ($this->isPaused()) {\n            $this->redis->set($this->idleKey, '1');\n            while ($this->isPaused() && !$this->stop) {\n                usleep(20 * 1000);\n            }\n            $this->redis->set($this->idleKey, '0');\n            continue;\n        }\n\n        $change = $this->primary->nextChange($this->pollTimeoutS);\n        if ($change === null) {\n            continue;\n        }\n        try {\n            $this->cache->applyChange($change);\n        } catch (\\Throwable $exc) {\n            fwrite(STDERR, \"[sync] failed to apply event: \" . $exc->getMessage() . \"\\n\");\n        }\n    }\n}","section_id":"the-sync-worker"},{"id":"re-prefetch-under-load-ex0","language":"php","code":"$supervisor->pause();\ntry {\n    $cache->clear();\n    $cache->bulkLoad($primary->listRecords());\n} finally {\n    $supervisor->resume();\n}","section_id":"re-prefetch-under-load"},{"id":"hitmiss-accounting-ex0","language":"php","code":"public function stats(): array\n{\n    $raw = $this->redis->hgetall($this->statsKey) ?: [];\n    $hits = (int) ($raw['hits'] ?? 0);\n    $misses = (int) ($raw['misses'] ?? 0);\n    $prefetched = (int) ($raw['prefetched'] ?? 0);\n    $applied = (int) ($raw['sync_events_applied'] ?? 0);\n    $lagTotal = (float) ($raw['sync_lag_ms_total'] ?? 0.0);\n    $lagSamples = (int) ($raw['sync_lag_samples'] ?? 0);\n\n    $total = $hits + $misses;\n    $hitRate = $total > 0 ? round(100.0 * $hits / $total, 1) : 0.0;\n    $avgLag = $lagSamples > 0 ? round($lagTotal / $lagSamples, 2) : 0.0;\n\n    return [\n        'hits' => $hits,\n        'misses' => $misses,\n        'hit_rate_pct' => $hitRate,\n        'prefetched' => $prefetched,\n        'sync_events_applied' => $applied,\n        'sync_lag_ms_avg' => $avgLag,\n    ];\n}","section_id":"hitmiss-accounting"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir prefetch-cache-demo && cd prefetch-cache-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/prefetch-cache/php\ncurl -O $BASE/Cache.php\ncurl -O $BASE/Primary.php\ncurl -O $BASE/SyncWorker.php\ncurl -O $BASE/sync_worker.php\ncurl -O $BASE/demo_server.php\ncurl -O $BASE/composer.json","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"composer install\nphp -S 127.0.0.1:8788 demo_server.php","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"bash","code":"# Stop the demo server with Ctrl+C, then kill the sync worker:\nkill $(redis-cli get demo:sync:pid)","section_id":"start-the-demo-server"},{"id":"the-mock-primary-store-ex0","language":"php","code":"private const UPDATE_SCRIPT = <<<'LUA'\nlocal id = ARGV[1]\nlocal field = ARGV[2]\nlocal value = ARGV[3]\nlocal now_ms = tonumber(ARGV[4])\nlocal ids_key = KEYS[1]\nlocal hash_key = KEYS[2]\nlocal changes_key = KEYS[3]\nif redis.call('SISMEMBER', ids_key, id) == 0 then\n  return 0\nend\nredis.call('HSET', hash_key, field, value)\nlocal raw = redis.call('HGETALL', hash_key)\nlocal fields = {}\nfor i = 1, #raw, 2 do\n  fields[raw[i]] = raw[i + 1]\nend\nlocal change = cjson.encode({\n  op = 'upsert',\n  id = id,\n  fields = fields,\n  timestamp_ms = now_ms,\n})\nredis.call('LPUSH', changes_key, change)\nreturn 1\nLUA;","section_id":"the-mock-primary-store"},{"id":"inspect-cached-entries-directly-in-redis-ex0","language":"bash","code":"redis-cli --scan --pattern 'cache:category:*'\nredis-cli HGETALL cache:category:cat-001\nredis-cli TTL cache:category:cat-001","section_id":"inspect-cached-entries-directly-in-redis"}]}
{"schema_version":2,"id":"develop/use-cases/prefetch-cache/redis-py","title":"Redis prefetch cache with redis-py","url":"https://redis.io/docs/latest/develop/use-cases/prefetch-cache/redis-py/","summary":"Implement a Redis prefetch cache in Python with redis-py","aliases":["/develop/use-cases/prefetch-cache/python"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:54:57+01:00","children":[],"page_type":"content","content_hash":"906c7dca2fc18701464500937dafc33f128c020b63d178218bdab6278c335c90","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis prefetch cache in Python with [`redis-py`](https://redis.io/docs/latest/develop/clients/redis-py). It includes a small local web server built with the Python standard library so you can watch the cache pre-load at startup, see a background sync worker apply primary mutations within milliseconds, and break the cache to confirm that reads never fall back to the primary."},{"id":"overview","title":"Overview","role":"overview","text":"Prefetch caching pre-loads a working set of reference data into Redis before the first request arrives, so every read on the request path is a cache hit. A separate sync worker keeps the cache current as the source of truth changes — there is no fall-back to the primary on the read path.\n\nThat gives you:\n\n* Near-100% cache hit ratios for reference and master data\n* Sub-millisecond reads for lookup-heavy paths at peak traffic\n* All reference-data reads offloaded from the primary database\n* Source-database changes propagated into Redis within a few milliseconds\n* A long safety-net TTL that bounds memory if the sync pipeline ever stops\n\nIn this example, each cached category is stored as a Redis hash under a key like `cache:category:{id}`. The hash holds the category fields (`id`, `name`, `display_order`, `featured`, `parent_id`) and the key has a long safety-net TTL that the sync worker refreshes on every add or update event. Delete events remove the cache key outright, so there is no TTL to refresh in that case."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow has three independent paths:\n\n1. **On startup**, the demo server calls `cache.bulk_load(primary.list_records())`, which pipelines `DEL` + `HSET` + `EXPIRE` for every record in one round trip.\n2. **On every read**, the application calls `cache.get(entity_id)`, which runs `HGETALL` against Redis only. A miss is treated as an error, not a trigger to query the primary.\n3. **On every primary mutation**, the primary appends a change event to an in-process queue. The sync worker thread drains the queue and calls `cache.apply_change(event)`. For an `upsert`, the helper rewrites the cache hash and refreshes the safety-net TTL; for a `delete`, it removes the cache key.\n\nIn a real system the in-process change queue is replaced by a CDC pipeline — [Redis Data Integration](https://redis.io/docs/latest/integrate/redis-data-integration), Debezium plus a lightweight consumer, or an equivalent tool that tails the source's binlog/WAL and pushes events into Redis."},{"id":"the-prefetch-cache-helper","title":"The prefetch-cache helper","role":"content","text":"The `PrefetchCache` class wraps the cache operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/prefetch-cache/redis-py/cache.py)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each cached category is stored in a Redis hash:\n\n[code example]\n\nThe implementation uses:\n\n* [`HSET`](https://redis.io/docs/latest/commands/hset) + [`EXPIRE`](https://redis.io/docs/latest/commands/expire), pipelined, for the bulk load and every sync event\n* [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) on the read path\n* [`DEL`](https://redis.io/docs/latest/commands/del) for sync-delete events and explicit invalidation\n* [`SCAN`](https://redis.io/docs/latest/commands/scan) to enumerate the cached keyspace and to clear the prefix\n* [`TTL`](https://redis.io/docs/latest/commands/ttl) to surface remaining safety-net time in the demo UI"},{"id":"bulk-load-on-startup","title":"Bulk load on startup","role":"content","text":"The `bulk_load` method pipelines a `DEL` + `HSET` + `EXPIRE` triple for every record. The pipeline is sent in a single round trip, so loading thousands of records takes one network RTT plus the time Redis spends executing the commands locally — typically tens of milliseconds even for a large reference table:\n\n[code example]\n\n`transaction=False` is intentional on the **startup** path: nothing is reading the cache yet, the records do not need to be applied atomically as a set, and skipping `MULTI`/`EXEC` keeps the bulk load fast. The same method is used for the live `/reprefetch` reload, which is safe because the demo pauses the sync worker around the clear-and-reload sequence — see [Re-prefetch under load](#re-prefetch-under-load) below. If you call `bulk_load` directly from your own code on a cache that is already serving reads, either pause your writers first or rewrite it with `pipeline(transaction=True)` so callers cannot observe a half-loaded record."},{"id":"reads-from-redis-only","title":"Reads from Redis only","role":"content","text":"The `get` method runs `HGETALL` and returns the cached hash. **It does not fall back to the primary on a miss.** In a healthy system, a miss never happens; if it does, the application surfaces it as an error and treats it as a sync-pipeline incident:\n\n[code example]\n\nThis is the key behavioural difference from [cache-aside](https://redis.io/docs/latest/develop/use-cases/cache-aside): the request path never touches the primary, so reference-data reads cannot contribute to primary database load."},{"id":"applying-sync-events","title":"Applying sync events","role":"content","text":"The sync worker calls `apply_change` for every primary mutation. For an `upsert`, the helper rewrites the cache hash and refreshes the safety-net TTL in one pipelined transaction so the cache never holds a stale mix of old and new fields. For a `delete`, it removes the cache key:\n\n[code example]\n\nThe `DEL` before the `HSET` ensures the cached hash contains exactly the fields the primary record has now — fields that have been dropped from the primary will not linger in Redis."},{"id":"the-sync-worker","title":"The sync worker","role":"content","text":"The `SyncWorker` runs a daemon thread that blocks on the primary's change queue with a short timeout. Every change is applied to Redis as soon as it arrives\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/prefetch-cache/redis-py/sync_worker.py)):\n\n[code example]\n\nIn production this loop is replaced by a CDC consumer reading from RDI's Redis output stream, Debezium's Kafka topic, or an equivalent change feed. The shape stays the same: drain events, apply them to Redis, advance the consumer offset."},{"id":"invalidation-and-re-prefetch","title":"Invalidation and re-prefetch","role":"content","text":"Two helpers exist for testing and recovery:\n\n* `invalidate(entity_id)` deletes a single cache key. The demo uses it to simulate a sync-pipeline failure on one record.\n* `clear()` runs `SCAN MATCH cache:category:*` and deletes every key under the prefix. The demo uses it to simulate a full cache loss.\n\nIn both cases, the recovery path is to call `bulk_load(primary.list_records())` again — re-prefetching from the primary. The demo exposes this as the \"Re-prefetch\" button so you can see the cache come back to a fully-warm state in one operation."},{"id":"re-prefetch-under-load","title":"Re-prefetch under load","role":"content","text":"`clear()` and `bulk_load()` are not atomic against the sync worker. If a change event arrives between the snapshot (`primary.list_records()`) and the bulk write, the bulk write can overwrite a newer value; if a change event arrives between `clear()`'s `SCAN` and `DEL`, the cleared entry can immediately be recreated. The demo's `/clear` and `/reprefetch` handlers solve this by pausing the sync worker around the operation:\n\n[code example]\n\n`pause()` waits for the worker to finish whatever event it is currently applying, parks the run loop, and returns. Change events that arrive during the pause sit in the primary's queue and apply in order once `resume()` is called, so no event is lost."},{"id":"hitmiss-accounting","title":"Hit/miss accounting","role":"content","text":"The helper keeps in-process counters for hits, misses, prefetched records, sync events applied, and the average lag between a primary change and its application to Redis. The demo UI surfaces these so you can confirm the cache is absorbing all reads and the sync worker is keeping up:\n\n[code example]\n\nIn production you would emit these as Prometheus counters and gauges. The sync-lag metric is the most important: a sudden rise indicates the CDC pipeline is falling behind."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before running the demo, make sure that:\n\n* Redis is running and accessible. By default, the demo connects to `localhost:6379`.\n* The `redis` Python package is installed:\n\n[code example]\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of four files. Download them from the [`redis-py` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/prefetch-cache/redis-py) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From that directory:\n\n[code example]\n\nYou should see something like:\n\n[code example]\n\nAfter starting the server, visit `http://localhost:8082`.\n\nThe demo server uses only Python standard library features for HTTP handling and concurrency:\n\n* [`http.server`](https://docs.python.org/3/library/http.server.html) for the web server\n* [`urllib.parse`](https://docs.python.org/3/library/urllib.parse.html) for query and form decoding\n* [`threading`](https://docs.python.org/3/library/threading.html) for the sync worker daemon\n\nIt exposes a small interactive page where you can:\n\n* See which IDs are in the cache and in the primary, side by side\n* Read a category through the cache and confirm every read is a hit\n* Update a field on the primary and watch the sync worker rewrite the cache hash\n* Add and delete categories and watch them appear and disappear from the cache\n* Invalidate one key or clear the entire cache to simulate a sync-pipeline failure\n* Re-prefetch from the primary to recover from a broken cache state\n* Watch the average sync lag, and confirm primary reads stay at one until you re-prefetch — each `/reprefetch` adds another primary read for the snapshot, but normal request traffic never reaches the primary at all"},{"id":"the-mock-primary-store","title":"The mock primary store","role":"content","text":"To make the demo self-contained, the example includes a `MockPrimaryStore` that stands in for a source-of-truth database\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/prefetch-cache/redis-py/primary.py)):\n\n[code example]\n\nEvery mutation appends a change event to an in-process [`queue.Queue`](https://docs.python.org/3/library/queue.html). The sync worker drains the queue with a 50 ms timeout and applies each event to Redis. In a real system this queue is replaced by a CDC pipeline — RDI on Redis Enterprise or Debezium with a Redis consumer on open-source Redis."},{"id":"production-usage","title":"Production usage","role":"content","text":"This guide uses a deliberately small local demo so you can focus on the prefetch-cache pattern. In production, you will usually want to harden several aspects of it."},{"id":"replace-the-in-process-change-queue-with-a-real-cdc-pipeline","title":"Replace the in-process change queue with a real CDC pipeline","role":"content","text":"The demo's in-process queue is the simplest possible stand-in for a CDC change feed. In production, the change feed lives outside the application process: an RDI pipeline configured against your primary database, Debezium connectors writing to Kafka or a Redis stream, or your application explicitly publishing change events from the write path. Whatever you choose, the consumer side stays the same — read events, apply them to Redis, advance the offset."},{"id":"use-a-long-safety-net-ttl-not-a-freshness-ttl","title":"Use a long safety-net TTL, not a freshness TTL","role":"content","text":"The TTL on each cache key is a **safety net**: it bounds memory if the sync pipeline silently stops, so a stuck consumer cannot leave stale data in Redis indefinitely. The TTL is not the freshness mechanism — freshness comes from the sync worker, which refreshes the TTL on every add or update event (delete events remove the key). Pick a TTL that is comfortably longer than your worst-case sync lag plus your alerting window, so a transient sync hiccup never expires hot keys."},{"id":"decide-what-to-do-on-a-cache-miss","title":"Decide what to do on a cache miss","role":"content","text":"A prefetch cache treats a miss as an error or a missing record. The two reasonable strategies are:\n\n* **Return a 404 to the user.** Appropriate when the cache is authoritative for the lookup — for example, when the user is asking for a category by ID and the ID is not in the cache.\n* **Page on-call.** A sustained miss rate on IDs you know exist is an incident: either the prefetch did not run, or the sync pipeline is broken.\n\nWhichever you choose, do not fall back to the primary on the read path — that is what cache-aside is for, and conflating the two patterns breaks the load-isolation guarantee that prefetch provides."},{"id":"bound-the-working-set-to-what-fits-in-memory","title":"Bound the working set to what fits in memory","role":"content","text":"Prefetch only works if the entire dataset fits in Redis memory with headroom. Estimate the size of your reference data, multiply by a growth factor, and confirm the result fits within your Redis instance's `maxmemory` minus what other use cases need. If the working set grows beyond what Redis can hold, switch the dataset to a cache-aside pattern instead — the request path will pay miss latency, but you will not OOM."},{"id":"reconcile-periodically-against-the-primary","title":"Reconcile periodically against the primary","role":"content","text":"CDC pipelines are eventually consistent: an event can be lost (broker outage, consumer crash, configuration drift) and the cache can silently diverge from the source. Run a periodic reconciliation job that re-reads all primary records, compares them against the cache, and either re-prefetches or fixes individual entries. Even running it once a day catches drift that ad-hoc inspection would miss."},{"id":"namespace-cache-keys-in-shared-redis-deployments","title":"Namespace cache keys in shared Redis deployments","role":"content","text":"If multiple applications share a Redis deployment, prefix cache keys with the application name (`cache:billing:category:{id}`) so different services cannot clobber each other's entries. The helper takes a `prefix` argument exactly for this."},{"id":"inspect-cached-entries-directly-in-redis","title":"Inspect cached entries directly in Redis","role":"content","text":"When testing or troubleshooting, inspect the stored cache keys directly to confirm the bulk load and the sync worker are writing what you expect:\n\n[code example]\n\nIf a key is missing for an ID that still exists in the primary, the prefetch did not run, the key expired without a sync refresh, or someone invalidated it. If a key is still present for an ID that was deleted in the primary, the delete event has not yet been applied. If the TTL is much lower than the configured safety-net value on a hot key, the sync worker is not keeping up."},{"id":"learn-more","title":"Learn more","role":"related","text":"* [redis-py guide](https://redis.io/docs/latest/develop/clients/redis-py) - Install and use the Python Redis client\n* [HSET command](https://redis.io/docs/latest/commands/hset) - Write hash fields\n* [HGETALL command](https://redis.io/docs/latest/commands/hgetall) - Read every field of a hash\n* [EXPIRE command](https://redis.io/docs/latest/commands/expire) - Set key expiration in seconds\n* [DEL command](https://redis.io/docs/latest/commands/del) - Delete a key on invalidation or sync-delete\n* [SCAN command](https://redis.io/docs/latest/commands/scan) - Iterate the cached keyspace without blocking the server\n* [TTL command](https://redis.io/docs/latest/commands/ttl) - Inspect remaining safety-net time on a key\n* [Redis Data Integration](https://redis.io/docs/latest/integrate/redis-data-integration) - Configuration-driven CDC into Redis on Redis Enterprise and Redis Cloud"}],"examples":[{"id":"the-prefetch-cache-helper-ex0","language":"python","code":"import redis\nfrom cache import PrefetchCache\nfrom primary import MockPrimaryStore\nfrom sync_worker import SyncWorker\n\nr = redis.Redis(host=\"localhost\", port=6379, decode_responses=True)\nprimary = MockPrimaryStore()\ncache = PrefetchCache(redis_client=r, ttl_seconds=3600)\n\n# Pre-load every primary record into Redis in one pipelined round trip.\ncache.bulk_load(primary.list_records())\n\n# Start the sync worker so primary mutations propagate into Redis.\nsync = SyncWorker(primary=primary, cache=cache)\nsync.start()\n\n# Read paths now go to Redis only.\nrecord, hit, redis_ms = cache.get(\"cat-001\")","section_id":"the-prefetch-cache-helper"},{"id":"data-model-ex0","language":"text","code":"cache:category:cat-001\n  id            = cat-001\n  name          = Beverages\n  display_order = 1\n  featured      = true\n  parent_id     =","section_id":"data-model"},{"id":"bulk-load-on-startup-ex0","language":"python","code":"def bulk_load(self, records: Iterable[dict[str, str]]) -> int:\n    loaded = 0\n    pipe = self.redis.pipeline(transaction=False)\n    for record in records:\n        entity_id = record.get(\"id\")\n        if not entity_id:\n            continue\n        cache_key = self._cache_key(entity_id)\n        pipe.delete(cache_key)\n        pipe.hset(cache_key, mapping=record)\n        pipe.expire(cache_key, self.ttl_seconds)\n        loaded += 1\n    if loaded:\n        pipe.execute()\n    return loaded","section_id":"bulk-load-on-startup"},{"id":"reads-from-redis-only-ex0","language":"python","code":"def get(\n    self,\n    entity_id: str,\n) -> tuple[Optional[dict[str, str]], bool, float]:\n    cache_key = self._cache_key(entity_id)\n\n    started = time.perf_counter()\n    cached = self.redis.hgetall(cache_key)\n    redis_latency_ms = (time.perf_counter() - started) * 1000.0\n\n    if cached:\n        with self._stats_lock:\n            self._hits += 1\n        return cached, True, redis_latency_ms\n\n    with self._stats_lock:\n        self._misses += 1\n    return None, False, redis_latency_ms","section_id":"reads-from-redis-only"},{"id":"applying-sync-events-ex0","language":"python","code":"def apply_change(self, change: dict) -> None:\n    op = change.get(\"op\")\n    entity_id = change.get(\"id\")\n    if not entity_id:\n        return\n\n    cache_key = self._cache_key(entity_id)\n\n    if op == \"upsert\":\n        fields = change.get(\"fields\") or {}\n        pipe = self.redis.pipeline(transaction=True)\n        pipe.delete(cache_key)\n        pipe.hset(cache_key, mapping=fields)\n        pipe.expire(cache_key, self.ttl_seconds)\n        pipe.execute()\n    elif op == \"delete\":\n        self.redis.delete(cache_key)","section_id":"applying-sync-events"},{"id":"the-sync-worker-ex0","language":"python","code":"def _run(self) -> None:\n    while not self._stop_event.is_set():\n        change = self.primary.next_change(timeout=self.poll_timeout_s)\n        if change is None:\n            continue\n        try:\n            self.cache.apply_change(change)\n        except Exception as exc:\n            print(f\"[sync] failed to apply {change!r}: {exc}\")","section_id":"the-sync-worker"},{"id":"re-prefetch-under-load-ex0","language":"python","code":"self.sync.pause()\ntry:\n    self.cache.clear()\n    self.cache.bulk_load(self.primary.list_records())\nfinally:\n    self.sync.resume()","section_id":"re-prefetch-under-load"},{"id":"hitmiss-accounting-ex0","language":"python","code":"def stats(self) -> dict[str, float]:\n    with self._stats_lock:\n        total = self._hits + self._misses\n        hit_rate = round(100.0 * self._hits / total, 1) if total else 0.0\n        avg_lag = (\n            round(self._sync_lag_ms_total / self._sync_lag_samples, 2)\n            if self._sync_lag_samples\n            else 0.0\n        )\n        return {\n            \"hits\": self._hits,\n            \"misses\": self._misses,\n            \"hit_rate_pct\": hit_rate,\n            \"prefetched\": self._prefetched,\n            \"sync_events_applied\": self._sync_events_applied,\n            \"sync_lag_ms_avg\": avg_lag,\n        }","section_id":"hitmiss-accounting"},{"id":"prerequisites-ex0","language":"bash","code":"pip install redis","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir prefetch-cache-demo && cd prefetch-cache-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/prefetch-cache/redis-py\ncurl -O $BASE/cache.py\ncurl -O $BASE/primary.py\ncurl -O $BASE/sync_worker.py\ncurl -O $BASE/demo_server.py","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"python3 demo_server.py","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"Redis prefetch-cache demo server listening on http://127.0.0.1:8082\nUsing Redis at localhost:6379 with cache prefix 'cache:category:' and TTL 3600s\nPrefetched 5 records in 82.1 ms; sync worker running","section_id":"start-the-demo-server"},{"id":"the-mock-primary-store-ex0","language":"python","code":"class MockPrimaryStore:\n    def __init__(self, read_latency_ms: int = 80) -> None:\n        ...\n\n    def list_records(self) -> list[dict[str, str]]:\n        time.sleep(self.read_latency_ms / 1000.0)\n        ...\n\n    def update_field(self, entity_id: str, field: str, value: str) -> bool:\n        ...\n        self._emit_change(CHANGE_OP_UPSERT, entity_id, snapshot)\n        return True","section_id":"the-mock-primary-store"},{"id":"inspect-cached-entries-directly-in-redis-ex0","language":"bash","code":"redis-cli --scan --pattern 'cache:category:*'\nredis-cli HGETALL cache:category:cat-001\nredis-cli TTL cache:category:cat-001","section_id":"inspect-cached-entries-directly-in-redis"}]}
{"schema_version":2,"id":"develop/use-cases/prefetch-cache/ruby","title":"Redis prefetch cache with redis-rb","url":"https://redis.io/docs/latest/develop/use-cases/prefetch-cache/ruby/","summary":"Implement a Redis prefetch cache in Ruby with redis-rb","aliases":["/develop/use-cases/prefetch-cache/redis-rb"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:54:57+01:00","children":[],"page_type":"content","content_hash":"1b74421f7b168470f12c84bea9076fdabb6af5083307a273f918407e7091a951","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis prefetch cache in Ruby with [`redis-rb`](https://redis.io/docs/latest/develop/clients/ruby). It includes a small local web server built with the Ruby standard library `webrick` so you can watch the cache pre-load at startup, see a background sync worker apply primary mutations within milliseconds, and break the cache to confirm that reads never fall back to the primary."},{"id":"overview","title":"Overview","role":"overview","text":"Prefetch caching pre-loads a working set of reference data into Redis before the first request arrives, so every read on the request path is a cache hit. A separate sync worker keeps the cache current as the source of truth changes — there is no fall-back to the primary on the read path.\n\nThat gives you:\n\n* Near-100% cache hit ratios for reference and master data\n* Sub-millisecond reads for lookup-heavy paths at peak traffic\n* All reference-data reads offloaded from the primary database\n* Source-database changes propagated into Redis within a few milliseconds\n* A long safety-net TTL that bounds memory if the sync pipeline ever stops\n\nIn this example, each cached category is stored as a Redis hash under a key like `cache:category:{id}`. The hash holds the category fields (`id`, `name`, `display_order`, `featured`, `parent_id`) and the key has a long safety-net TTL that the sync worker refreshes on every add or update event. Delete events remove the cache key outright, so there is no TTL to refresh in that case."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow has three independent paths:\n\n1. **On startup**, the demo server calls `cache.bulk_load(primary.list_records)`, which pipelines `DEL` + `HSET` + `EXPIRE` for every record in one round trip.\n2. **On every read**, the application calls `cache.get(entity_id)`, which runs `HGETALL` against Redis only. A miss is treated as an error, not a trigger to query the primary.\n3. **On every primary mutation**, the primary appends a change event to an in-process queue. The sync worker thread drains the queue and calls `cache.apply_change(event)`. For an `upsert`, the helper rewrites the cache hash and refreshes the safety-net TTL; for a `delete`, it removes the cache key.\n\nIn a real system the in-process change queue is replaced by a CDC pipeline — [Redis Data Integration](https://redis.io/docs/latest/integrate/redis-data-integration), Debezium plus a lightweight consumer, or an equivalent tool that tails the source's binlog/WAL and pushes events into Redis."},{"id":"the-prefetch-cache-helper","title":"The prefetch-cache helper","role":"content","text":"The `PrefetchCache` class wraps the cache operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/prefetch-cache/ruby/cache.rb)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each cached category is stored in a Redis hash:\n\n[code example]\n\nThe implementation uses:\n\n* [`HSET`](https://redis.io/docs/latest/commands/hset) + [`EXPIRE`](https://redis.io/docs/latest/commands/expire), pipelined, for the bulk load and every sync event\n* [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) on the read path\n* [`DEL`](https://redis.io/docs/latest/commands/del) for sync-delete events and explicit invalidation\n* [`SCAN`](https://redis.io/docs/latest/commands/scan) to enumerate the cached keyspace and to clear the prefix\n* [`TTL`](https://redis.io/docs/latest/commands/ttl) to surface remaining safety-net time in the demo UI"},{"id":"bulk-load-on-startup","title":"Bulk load on startup","role":"content","text":"The `bulk_load` method pipelines a `DEL` + `HSET` + `EXPIRE` triple for every record. The pipeline is sent in a single round trip, so loading thousands of records takes one network RTT plus the time Redis spends executing the commands locally — typically tens of milliseconds even for a large reference table:\n\n[code example]\n\nThe `pipelined` block sends every command in one network round trip without `MULTI`/`EXEC`. This is intentional on the **startup** path: nothing is reading the cache yet, the records do not need to be applied atomically as a set, and skipping `MULTI`/`EXEC` keeps the bulk load fast. The same method is used for the live `/reprefetch` reload, which is safe because the demo pauses the sync worker around the clear-and-reload sequence — see [Re-prefetch under load](#re-prefetch-under-load) below. If you call `bulk_load` directly from your own code on a cache that is already serving reads, either pause your writers first or rewrite it with `multi` so callers cannot observe a half-loaded record."},{"id":"reads-from-redis-only","title":"Reads from Redis only","role":"content","text":"The `get` method runs `HGETALL` and returns the cached hash. **It does not fall back to the primary on a miss.** In a healthy system, a miss never happens; if it does, the application surfaces it as an error and treats it as a sync-pipeline incident:\n\n[code example]\n\nThis is the key behavioural difference from [cache-aside](https://redis.io/docs/latest/develop/use-cases/cache-aside): the request path never touches the primary, so reference-data reads cannot contribute to primary database load."},{"id":"applying-sync-events","title":"Applying sync events","role":"content","text":"The sync worker calls `apply_change` for every primary mutation. For an `upsert`, the helper rewrites the cache hash and refreshes the safety-net TTL in one `MULTI`/`EXEC` transaction so the cache never holds a stale mix of old and new fields. For a `delete`, it removes the cache key:\n\n[code example]\n\nThe `DEL` before the `HSET` ensures the cached hash contains exactly the fields the primary record has now — fields that have been dropped from the primary will not linger in Redis. The `Mutex` around `multi` is a redis-rb-specific guard: a single `Redis.new` connection is thread-safe for individual commands, but a `multi` block uses the underlying connection for the duration of the transaction, so concurrent transactions on the same client must be serialised by the caller (or you must use a connection pool). The demo uses one shared client plus a mutex; production code should use [`connection_pool`](https://github.com/mperham/connection_pool) and check out a connection per transaction instead."},{"id":"the-sync-worker","title":"The sync worker","role":"content","text":"The `SyncWorker` runs a daemon thread that blocks on the primary's change queue with a short timeout. Every change is applied to Redis as soon as it arrives\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/prefetch-cache/ruby/sync_worker.rb)):\n\n[code example]\n\nIn production this loop is replaced by a CDC consumer reading from RDI's Redis output stream, Debezium's Kafka topic, or an equivalent change feed. The shape stays the same: drain events, apply them to Redis, advance the consumer offset."},{"id":"invalidation-and-re-prefetch","title":"Invalidation and re-prefetch","role":"content","text":"Two helpers exist for testing and recovery:\n\n* `invalidate(entity_id)` deletes a single cache key. The demo uses it to simulate a sync-pipeline failure on one record.\n* `clear` runs `SCAN MATCH cache:category:*` and deletes every key under the prefix. The demo uses it to simulate a full cache loss.\n\nIn both cases, the recovery path is to call `bulk_load(primary.list_records)` again — re-prefetching from the primary. The demo exposes this as the \"Re-prefetch\" button so you can see the cache come back to a fully-warm state in one operation."},{"id":"re-prefetch-under-load","title":"Re-prefetch under load","role":"content","text":"`clear` and `bulk_load` are not atomic against the sync worker. If a change event arrives between the snapshot (`primary.list_records`) and the bulk write, the bulk write can overwrite a newer value; if a change event arrives between `clear`'s `SCAN` and `DEL`, the cleared entry can immediately be recreated. The demo's `/clear` and `/reprefetch` handlers solve this by pausing the sync worker around the operation:\n\n[code example]\n\n`pause` waits for the worker to finish whatever event it is currently applying, parks the run loop on a `ConditionVariable`, and returns. Change events that arrive during the pause sit in the primary's `Queue` and apply in order once `resume` is called, so no event is lost."},{"id":"hitmiss-accounting","title":"Hit/miss accounting","role":"content","text":"The helper keeps in-process counters for hits, misses, prefetched records, sync events applied, and the average lag between a primary change and its application to Redis. The demo UI surfaces these so you can confirm the cache is absorbing all reads and the sync worker is keeping up:\n\n[code example]\n\nIn production you would emit these as Prometheus counters and gauges. The sync-lag metric is the most important: a sudden rise indicates the CDC pipeline is falling behind."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before running the demo, make sure that:\n\n* Redis is running and accessible. By default, the demo connects to `localhost:6379`.\n* Ruby 2.6 or later is installed (the demo uses `webrick`, which is stdlib in 2.x and is shipped as a bundled gem in 3.x).\n* The `redis` gem (`redis-rb` 5.x) is installed:\n\n[code example]\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of four files. Download them from the [`ruby` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/prefetch-cache/ruby) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From that directory:\n\n[code example]\n\nYou should see something like:\n\n[code example]\n\nAfter starting the server, visit `http://localhost:8789`.\n\nThe demo server uses only Ruby standard library features for HTTP handling and concurrency:\n\n* [`webrick`](https://github.com/ruby/webrick) for the web server\n* [`uri`](https://docs.ruby-lang.org/en/master/URI.html) and `req.query` for query and form decoding\n* [`Thread`](https://docs.ruby-lang.org/en/master/Thread.html), [`Mutex`](https://docs.ruby-lang.org/en/master/Thread/Mutex.html), [`ConditionVariable`](https://docs.ruby-lang.org/en/master/Thread/ConditionVariable.html), and [`Queue`](https://docs.ruby-lang.org/en/master/Thread/Queue.html) for the sync worker daemon\n\nIt exposes a small interactive page where you can:\n\n* See which IDs are in the cache and in the primary, side by side\n* Read a category through the cache and confirm every read is a hit\n* Update a field on the primary and watch the sync worker rewrite the cache hash\n* Add and delete categories and watch them appear and disappear from the cache\n* Invalidate one key or clear the entire cache to simulate a sync-pipeline failure\n* Re-prefetch from the primary to recover from a broken cache state\n* Watch the average sync lag, and confirm primary reads stay at one until you re-prefetch — each `/reprefetch` adds another primary read for the snapshot, but normal request traffic never reaches the primary at all"},{"id":"the-mock-primary-store","title":"The mock primary store","role":"content","text":"To make the demo self-contained, the example includes a `MockPrimaryStore` that stands in for a source-of-truth database\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/prefetch-cache/ruby/primary.rb)):\n\n[code example]\n\nEvery mutation appends a change event to an in-process [`Queue`](https://docs.ruby-lang.org/en/master/Thread/Queue.html). The sync worker drains the queue with a 50 ms timeout and applies each event to Redis. Ruby's `Queue#pop` does not accept a timeout directly, so `next_change` wraps a non-blocking `pop(true)` in a short polling loop — that keeps the worker responsive to its stop flag.\n\nIn a real system this queue is replaced by a CDC pipeline — RDI on Redis Enterprise or Debezium with a Redis consumer on open-source Redis."},{"id":"production-usage","title":"Production usage","role":"content","text":"This guide uses a deliberately small local demo so you can focus on the prefetch-cache pattern. In production, you will usually want to harden several aspects of it."},{"id":"replace-the-in-process-change-queue-with-a-real-cdc-pipeline","title":"Replace the in-process change queue with a real CDC pipeline","role":"content","text":"The demo's in-process queue is the simplest possible stand-in for a CDC change feed. In production, the change feed lives outside the application process: an RDI pipeline configured against your primary database, Debezium connectors writing to Kafka or a Redis stream, or your application explicitly publishing change events from the write path. Whatever you choose, the consumer side stays the same — read events, apply them to Redis, advance the offset."},{"id":"use-a-long-safety-net-ttl-not-a-freshness-ttl","title":"Use a long safety-net TTL, not a freshness TTL","role":"content","text":"The TTL on each cache key is a **safety net**: it bounds memory if the sync pipeline silently stops, so a stuck consumer cannot leave stale data in Redis indefinitely. The TTL is not the freshness mechanism — freshness comes from the sync worker, which refreshes the TTL on every add or update event (delete events remove the key). Pick a TTL that is comfortably longer than your worst-case sync lag plus your alerting window, so a transient sync hiccup never expires hot keys."},{"id":"decide-what-to-do-on-a-cache-miss","title":"Decide what to do on a cache miss","role":"content","text":"A prefetch cache treats a miss as an error or a missing record. The two reasonable strategies are:\n\n* **Return a 404 to the user.** Appropriate when the cache is authoritative for the lookup — for example, when the user is asking for a category by ID and the ID is not in the cache.\n* **Page on-call.** A sustained miss rate on IDs you know exist is an incident: either the prefetch did not run, or the sync pipeline is broken.\n\nWhichever you choose, do not fall back to the primary on the read path — that is what cache-aside is for, and conflating the two patterns breaks the load-isolation guarantee that prefetch provides."},{"id":"bound-the-working-set-to-what-fits-in-memory","title":"Bound the working set to what fits in memory","role":"content","text":"Prefetch only works if the entire dataset fits in Redis memory with headroom. Estimate the size of your reference data, multiply by a growth factor, and confirm the result fits within your Redis instance's `maxmemory` minus what other use cases need. If the working set grows beyond what Redis can hold, switch the dataset to a cache-aside pattern instead — the request path will pay miss latency, but you will not OOM."},{"id":"reconcile-periodically-against-the-primary","title":"Reconcile periodically against the primary","role":"content","text":"CDC pipelines are eventually consistent: an event can be lost (broker outage, consumer crash, configuration drift) and the cache can silently diverge from the source. Run a periodic reconciliation job that re-reads all primary records, compares them against the cache, and either re-prefetches or fixes individual entries. Even running it once a day catches drift that ad-hoc inspection would miss."},{"id":"use-a-connection-pool-for-transactions-not-a-shared-client--mutex","title":"Use a connection pool for transactions, not a shared client + mutex","role":"content","text":"A single `Redis.new` client is thread-safe for individual commands because Ruby's GIL serialises access to the C-level command pipeline. But `multi` blocks use the underlying connection for the duration of the transaction, so two threads cannot run `multi` blocks concurrently on the same client without interleaving. The demo gets away with one shared client guarded by a `Mutex`, which is fine for a small local server. In production, use [`connection_pool`](https://github.com/mperham/connection_pool) to keep a pool of `Redis` clients and check one out per transaction:\n\n[code example]\n\nThat gives you per-thread transactions without a global lock and reuses connections across requests."},{"id":"namespace-cache-keys-in-shared-redis-deployments","title":"Namespace cache keys in shared Redis deployments","role":"content","text":"If multiple applications share a Redis deployment, prefix cache keys with the application name (`cache:billing:category:{id}`) so different services cannot clobber each other's entries. The helper takes a `prefix` argument exactly for this."},{"id":"inspect-cached-entries-directly-in-redis","title":"Inspect cached entries directly in Redis","role":"content","text":"When testing or troubleshooting, inspect the stored cache keys directly to confirm the bulk load and the sync worker are writing what you expect:\n\n[code example]\n\nIf a key is missing for an ID that still exists in the primary, the prefetch did not run, the key expired without a sync refresh, or someone invalidated it. If a key is still present for an ID that was deleted in the primary, the delete event has not yet been applied. If the TTL is much lower than the configured safety-net value on a hot key, the sync worker is not keeping up."},{"id":"learn-more","title":"Learn more","role":"related","text":"* [redis-rb guide](https://redis.io/docs/latest/develop/clients/ruby) - Install and use the Ruby Redis client\n* [HSET command](https://redis.io/docs/latest/commands/hset) - Write hash fields\n* [HGETALL command](https://redis.io/docs/latest/commands/hgetall) - Read every field of a hash\n* [EXPIRE command](https://redis.io/docs/latest/commands/expire) - Set key expiration in seconds\n* [DEL command](https://redis.io/docs/latest/commands/del) - Delete a key on invalidation or sync-delete\n* [SCAN command](https://redis.io/docs/latest/commands/scan) - Iterate the cached keyspace without blocking the server\n* [TTL command](https://redis.io/docs/latest/commands/ttl) - Inspect remaining safety-net time on a key\n* [Redis Data Integration](https://redis.io/docs/latest/integrate/redis-data-integration) - Configuration-driven CDC into Redis on Redis Enterprise and Redis Cloud"}],"examples":[{"id":"the-prefetch-cache-helper-ex0","language":"ruby","code":"require \"redis\"\nrequire_relative \"cache\"\nrequire_relative \"primary\"\nrequire_relative \"sync_worker\"\n\nredis = Redis.new(host: \"localhost\", port: 6379)\nprimary = MockPrimaryStore.new\ncache = PrefetchCache.new(redis_client: redis, ttl_seconds: 3600)\n\n# Pre-load every primary record into Redis in one pipelined round trip.\ncache.bulk_load(primary.list_records)\n\n# Start the sync worker so primary mutations propagate into Redis.\nsync = SyncWorker.new(primary: primary, cache: cache)\nsync.start\n\n# Read paths now go to Redis only.\nrecord, hit, redis_ms = cache.get(\"cat-001\")","section_id":"the-prefetch-cache-helper"},{"id":"data-model-ex0","language":"text","code":"cache:category:cat-001\n  id            = cat-001\n  name          = Beverages\n  display_order = 1\n  featured      = true\n  parent_id     =","section_id":"data-model"},{"id":"bulk-load-on-startup-ex0","language":"ruby","code":"def bulk_load(records)\n  loaded = 0\n  @apply_lock.synchronize do\n    @redis.pipelined do |pipe|\n      records.each do |record|\n        entity_id = record[\"id\"] || record[:id]\n        next if entity_id.nil? || entity_id.to_s.empty?\n        cache_key = cache_key_for(entity_id)\n        pipe.del(cache_key)\n        pipe.hset(cache_key, stringify_record(record))\n        pipe.expire(cache_key, @ttl_seconds)\n        loaded += 1\n      end\n    end\n  end\n  @stats_lock.synchronize { @prefetched += loaded }\n  loaded\nend","section_id":"bulk-load-on-startup"},{"id":"reads-from-redis-only-ex0","language":"ruby","code":"def get(entity_id)\n  cache_key = cache_key_for(entity_id)\n\n  started = monotonic_ms\n  cached = @redis.hgetall(cache_key)\n  redis_latency_ms = monotonic_ms - started\n\n  if cached && !cached.empty?\n    @stats_lock.synchronize { @hits += 1 }\n    [cached, true, redis_latency_ms]\n  else\n    @stats_lock.synchronize { @misses += 1 }\n    [nil, false, redis_latency_ms]\n  end\nend","section_id":"reads-from-redis-only"},{"id":"applying-sync-events-ex0","language":"ruby","code":"def apply_change(change)\n  op = change[:op] || change[\"op\"]\n  entity_id = change[:id] || change[\"id\"]\n  return if entity_id.nil? || entity_id.to_s.empty?\n\n  cache_key = cache_key_for(entity_id)\n\n  case op\n  when \"upsert\"\n    fields = change[:fields] || change[\"fields\"]\n    return if fields.nil? || fields.empty?\n\n    @apply_lock.synchronize do\n      @redis.multi do |tx|\n        tx.del(cache_key)\n        tx.hset(cache_key, stringify_record(fields))\n        tx.expire(cache_key, @ttl_seconds)\n      end\n    end\n  when \"delete\"\n    @redis.del(cache_key)\n  end\n  # stats update omitted for brevity\nend","section_id":"applying-sync-events"},{"id":"the-sync-worker-ex0","language":"ruby","code":"def run_loop\n  loop do\n    should_stop, should_pause = @state_mutex.synchronize { [@stop, @pause] }\n    break if should_stop\n\n    if should_pause\n      @state_mutex.synchronize do\n        @paused_idle = true\n        @state_cv.broadcast\n        while @pause && !@stop\n          @state_cv.wait(@state_mutex, @poll_timeout_s)\n        end\n        @paused_idle = false\n      end\n      next\n    end\n\n    change = @primary.next_change(@poll_timeout_s)\n    next if change.nil?\n    begin\n      @cache.apply_change(change)\n    rescue => exc\n      warn \"[sync] failed to apply #{change.inspect}: #{exc}\"\n    end\n  end\nend","section_id":"the-sync-worker"},{"id":"re-prefetch-under-load-ex0","language":"ruby","code":"@sync.pause\nbegin\n  @cache.clear\n  @cache.bulk_load(@primary.list_records)\nensure\n  @sync.resume\nend","section_id":"re-prefetch-under-load"},{"id":"hitmiss-accounting-ex0","language":"ruby","code":"def stats\n  @stats_lock.synchronize do\n    total = @hits + @misses\n    hit_rate = total.zero? ? 0.0 : (100.0 * @hits / total).round(1)\n    avg_lag = @sync_lag_samples.zero? ? 0.0 : (@sync_lag_ms_total / @sync_lag_samples).round(2)\n    {\n      \"hits\" => @hits,\n      \"misses\" => @misses,\n      \"hit_rate_pct\" => hit_rate,\n      \"prefetched\" => @prefetched,\n      \"sync_events_applied\" => @sync_events_applied,\n      \"sync_lag_ms_avg\" => avg_lag,\n    }\n  end\nend","section_id":"hitmiss-accounting"},{"id":"prerequisites-ex0","language":"bash","code":"gem install redis webrick","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir prefetch-cache-demo && cd prefetch-cache-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/prefetch-cache/ruby\ncurl -O $BASE/cache.rb\ncurl -O $BASE/primary.rb\ncurl -O $BASE/sync_worker.rb\ncurl -O $BASE/demo_server.rb","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"ruby demo_server.rb","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"Redis prefetch-cache demo server listening on http://127.0.0.1:8789\nUsing Redis at localhost:6379 with cache prefix 'cache:category:' and TTL 3600s\nPrefetched 5 records in 86.5 ms; sync worker running","section_id":"start-the-demo-server"},{"id":"the-mock-primary-store-ex0","language":"ruby","code":"class MockPrimaryStore\n  def initialize(read_latency_ms: 80)\n    # ...\n  end\n\n  def list_records\n    sleep(@read_latency_ms / 1000.0)\n    # ...\n  end\n\n  def update_field(entity_id, field, value)\n    @lock.synchronize do\n      record = @records[entity_id]\n      return false if record.nil?\n      record[field] = value\n      emit_change_locked(CHANGE_OP_UPSERT, entity_id, record.dup)\n    end\n    true\n  end\nend","section_id":"the-mock-primary-store"},{"id":"use-a-connection-pool-for-transactions-not-a-shared-client--mutex-ex0","language":"ruby","code":"require \"connection_pool\"\n\nREDIS = ConnectionPool.new(size: 16, timeout: 5) { Redis.new(host: \"localhost\") }\n\nREDIS.with do |redis|\n  redis.multi do |tx|\n    tx.del(cache_key)\n    tx.hset(cache_key, fields)\n    tx.expire(cache_key, ttl_seconds)\n  end\nend","section_id":"use-a-connection-pool-for-transactions-not-a-shared-client--mutex"},{"id":"inspect-cached-entries-directly-in-redis-ex0","language":"bash","code":"redis-cli --scan --pattern 'cache:category:*'\nredis-cli HGETALL cache:category:cat-001\nredis-cli TTL cache:category:cat-001","section_id":"inspect-cached-entries-directly-in-redis"}]}
{"schema_version":2,"id":"develop/use-cases/prefetch-cache/rust","title":"Redis prefetch cache with redis-rs","url":"https://redis.io/docs/latest/develop/use-cases/prefetch-cache/rust/","summary":"Implement a Redis prefetch cache in Rust with redis-rs","aliases":["/develop/use-cases/prefetch-cache/redis-rs"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:54:57+01:00","children":[],"page_type":"content","content_hash":"ad2ce8173a8ee78e0f00c95aa1cd1fa26bf9d7f208552f0e1306d4f9615011ae","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis prefetch cache in Rust with the [`redis`](https://crates.io/crates/redis) crate (redis-rs). It includes a small local web server built with [`axum`](https://github.com/tokio-rs/axum) and [`tokio`](https://tokio.rs/) so you can watch the cache pre-load at startup, see a background sync worker apply primary mutations within milliseconds, and break the cache to confirm that reads never fall back to the primary."},{"id":"overview","title":"Overview","role":"overview","text":"Prefetch caching pre-loads a working set of reference data into Redis before the first request arrives, so every read on the request path is a cache hit. A separate sync worker keeps the cache current as the source of truth changes — there is no fall-back to the primary on the read path.\n\nThat gives you:\n\n* Near-100% cache hit ratios for reference and master data\n* Sub-millisecond reads for lookup-heavy paths at peak traffic\n* All reference-data reads offloaded from the primary database\n* Source-database changes propagated into Redis within a few milliseconds\n* A long safety-net TTL that bounds memory if the sync pipeline ever stops\n\nIn this example, each cached category is stored as a Redis hash under a key like `cache:category:{id}`. The hash holds the category fields (`id`, `name`, `display_order`, `featured`, `parent_id`) and the key has a long safety-net TTL that the sync worker refreshes on every add or update event. Delete events remove the cache key outright, so there is no TTL to refresh in that case."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow has three independent paths:\n\n1. **On startup**, the demo server calls `cache.bulk_load(primary.list_records().await).await`, which pipelines `DEL` + `HSET` + `EXPIRE` for every record in one round trip.\n2. **On every read**, the application calls `cache.get(entity_id).await`, which runs `HGETALL` against Redis only. A miss is treated as an error, not a trigger to query the primary.\n3. **On every primary mutation**, the primary appends a change event to an in-process `tokio::sync::mpsc` channel. The sync worker task drains the channel and calls `cache.apply_change(event).await`. For an `upsert`, the helper rewrites the cache hash and refreshes the safety-net TTL; for a `delete`, it removes the cache key.\n\nIn a real system the in-process change channel is replaced by a CDC pipeline — [Redis Data Integration](https://redis.io/docs/latest/integrate/redis-data-integration), Debezium plus a lightweight consumer, or an equivalent tool that tails the source's binlog/WAL and pushes events into Redis."},{"id":"the-prefetch-cache-helper","title":"The prefetch-cache helper","role":"content","text":"The `PrefetchCache` struct wraps the cache operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/prefetch-cache/rust/cache.rs)):\n\n[code example]\n\nThe helper uses [`ConnectionManager`](https://docs.rs/redis/latest/redis/aio/struct.ConnectionManager.html), which is cheap to clone, auto-reconnects on failure, and is the recommended way to share a redis-rs connection across tokio tasks."},{"id":"data-model","title":"Data model","role":"content","text":"Each cached category is stored in a Redis hash:\n\n[code example]\n\nThe implementation uses:\n\n* [`HSET`](https://redis.io/docs/latest/commands/hset) + [`EXPIRE`](https://redis.io/docs/latest/commands/expire), pipelined, for the bulk load and every sync event\n* [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) on the read path\n* [`DEL`](https://redis.io/docs/latest/commands/del) for sync-delete events and explicit invalidation\n* [`SCAN`](https://redis.io/docs/latest/commands/scan) to enumerate the cached keyspace and to clear the prefix\n* [`TTL`](https://redis.io/docs/latest/commands/ttl) to surface remaining safety-net time in the demo UI"},{"id":"bulk-load-on-startup","title":"Bulk load on startup","role":"content","text":"The `bulk_load` method pipelines a `DEL` + `HSET` + `EXPIRE` triple for every record. The pipeline is sent in a single round trip, so loading thousands of records takes one network RTT plus the time Redis spends executing the commands locally — typically tens of milliseconds even for a large reference table:\n\n[code example]\n\nA plain `redis::pipe()` (without `.atomic()`) is intentional on the **startup** path: nothing is reading the cache yet, the records do not need to be applied atomically as a set, and skipping `MULTI`/`EXEC` keeps the bulk load fast. The same method is used for the live `/reprefetch` reload, which is safe because the demo pauses the sync worker around the clear-and-reload sequence — see [Re-prefetch under load](#re-prefetch-under-load) below. If you call `bulk_load` directly from your own code on a cache that is already serving reads, either pause your writers first or switch to `redis::pipe().atomic()` so callers cannot observe a half-loaded record."},{"id":"reads-from-redis-only","title":"Reads from Redis only","role":"content","text":"The `get` method runs `HGETALL` and returns the cached hash. **It does not fall back to the primary on a miss.** In a healthy system, a miss never happens; if it does, the application surfaces it as an error and treats it as a sync-pipeline incident:\n\n[code example]\n\nThis is the key behavioural difference from [cache-aside](https://redis.io/docs/latest/develop/use-cases/cache-aside): the request path never touches the primary, so reference-data reads cannot contribute to primary database load."},{"id":"applying-sync-events","title":"Applying sync events","role":"content","text":"The sync worker calls `apply_change` for every primary mutation. For an `upsert`, the helper rewrites the cache hash and refreshes the safety-net TTL in one `MULTI`/`EXEC` pipeline so the cache never holds a stale mix of old and new fields. For a `delete`, it removes the cache key:\n\n[code example]\n\nThe `DEL` before the `HSET` ensures the cached hash contains exactly the fields the primary record has now — fields that have been dropped from the primary will not linger in Redis. The early return on empty `fields` matters: redis-rs panics if you call `hset_multiple` with an empty slice, so a malformed upsert with no fields is dropped (in a real CDC consumer you would route it to a dead-letter queue)."},{"id":"the-sync-worker","title":"The sync worker","role":"content","text":"The `SyncWorker` runs a long-lived `tokio::task` that polls the primary's change channel with a short timeout. Every change is applied to Redis as soon as it arrives\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/prefetch-cache/rust/sync_worker.rs)):\n\n[code example]\n\nIn production this loop is replaced by a CDC consumer reading from RDI's Redis output stream, Debezium's Kafka topic, or an equivalent change feed. The shape stays the same: drain events, apply them to Redis, advance the consumer offset."},{"id":"invalidation-and-re-prefetch","title":"Invalidation and re-prefetch","role":"content","text":"Two helpers exist for testing and recovery:\n\n* `invalidate(entity_id)` deletes a single cache key. The demo uses it to simulate a sync-pipeline failure on one record.\n* `clear()` runs `SCAN MATCH cache:category:*` and deletes every key under the prefix. The demo uses it to simulate a full cache loss.\n\nIn both cases, the recovery path is to call `bulk_load(primary.list_records().await).await` again — re-prefetching from the primary. The demo exposes this as the \"Re-prefetch\" button so you can see the cache come back to a fully-warm state in one operation."},{"id":"re-prefetch-under-load","title":"Re-prefetch under load","role":"content","text":"`clear()` and `bulk_load()` are not atomic against the sync worker. If a change event arrives between the snapshot (`primary.list_records()`) and the bulk write, the bulk write can overwrite a newer value; if a change event arrives between `clear()`'s `SCAN` and `DEL`, the cleared entry can immediately be recreated. The demo's `/clear` and `/reprefetch` handlers solve this by pausing the sync worker around the operation:\n\n[code example]\n\n`pause()` flips a `tokio::sync::watch` flag and blocks on a `tokio::sync::Notify` until the worker confirms it has parked. Change events that arrive during the pause sit in the primary's channel and apply in order once `resume()` is called, so no event is lost."},{"id":"hitmiss-accounting","title":"Hit/miss accounting","role":"content","text":"The helper keeps thread-safe atomic counters for hits, misses, prefetched records, sync events applied, and the average lag between a primary change and its application to Redis. The demo UI surfaces these so you can confirm the cache is absorbing all reads and the sync worker is keeping up:\n\n[code example]\n\nIn production you would emit these as Prometheus counters and gauges. The sync-lag metric is the most important: a sudden rise indicates the CDC pipeline is falling behind."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before running the demo, make sure that:\n\n* Redis is running and accessible. By default, the demo connects to `localhost:6379`.\n* You have a working [Rust toolchain](https://www.rust-lang.org/tools/install) (`cargo` 1.70+).\n* The demo's `Cargo.toml` pins the `redis` crate at 0.24+ with the `tokio-comp`, `aio`, and `connection-manager` features; `cargo build` will pull the rest of the dependency graph.\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of five files. Download them from the [`rust` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/prefetch-cache/rust) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From that directory:\n\n[code example]\n\nYou should see something like:\n\n[code example]\n\nAfter starting the server, visit `http://localhost:8790`.\n\nThe demo accepts these flags:\n\n* `--host` / `--port` — HTTP bind host and port (defaults to `127.0.0.1:8790`)\n* `--redis-host` / `--redis-port` — Redis location\n* `--cache-prefix` — cache key prefix (default `cache:category:`)\n* `--ttl-seconds` — safety-net TTL in seconds (default `3600`)\n* `--primary-latency-ms` — simulated primary read latency (default `80`)\n\nIt exposes a small interactive page where you can:\n\n* See which IDs are in the cache and in the primary, side by side\n* Read a category through the cache and confirm every read is a hit\n* Update a field on the primary and watch the sync worker rewrite the cache hash\n* Add and delete categories and watch them appear and disappear from the cache\n* Invalidate one key or clear the entire cache to simulate a sync-pipeline failure\n* Re-prefetch from the primary to recover from a broken cache state\n* Watch the average sync lag, and confirm primary reads stay at one until you re-prefetch — each `/reprefetch` adds another primary read for the snapshot, but normal request traffic never reaches the primary at all"},{"id":"the-mock-primary-store","title":"The mock primary store","role":"content","text":"To make the demo self-contained, the example includes a `MockPrimaryStore` that stands in for a source-of-truth database\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/prefetch-cache/rust/primary.rs)):\n\n[code example]\n\nEvery mutation appends a change event to an in-process [`tokio::sync::mpsc`](https://docs.rs/tokio/latest/tokio/sync/mpsc/index.html) channel. The emit happens **while the mutation lock is held**, so two concurrent updates cannot interleave their channel sends — the queue order always matches the order in which the records map was mutated. The sync worker drains the channel with a 50 ms timeout via `tokio::time::timeout` and applies each event to Redis. In a real system this channel is replaced by a CDC pipeline — RDI on Redis Enterprise or Debezium with a Redis consumer on open-source Redis."},{"id":"production-usage","title":"Production usage","role":"content","text":"This guide uses a deliberately small local demo so you can focus on the prefetch-cache pattern. In production, you will usually want to harden several aspects of it."},{"id":"replace-the-in-process-change-channel-with-a-real-cdc-pipeline","title":"Replace the in-process change channel with a real CDC pipeline","role":"content","text":"The demo's in-process channel is the simplest possible stand-in for a CDC change feed. In production, the change feed lives outside the application process: an RDI pipeline configured against your primary database, Debezium connectors writing to Kafka or a Redis stream, or your application explicitly publishing change events from the write path. Whatever you choose, the consumer side stays the same — read events, apply them to Redis, advance the offset."},{"id":"use-a-long-safety-net-ttl-not-a-freshness-ttl","title":"Use a long safety-net TTL, not a freshness TTL","role":"content","text":"The TTL on each cache key is a **safety net**: it bounds memory if the sync pipeline silently stops, so a stuck consumer cannot leave stale data in Redis indefinitely. The TTL is not the freshness mechanism — freshness comes from the sync worker, which refreshes the TTL on every add or update event (delete events remove the key). Pick a TTL that is comfortably longer than your worst-case sync lag plus your alerting window, so a transient sync hiccup never expires hot keys."},{"id":"decide-what-to-do-on-a-cache-miss","title":"Decide what to do on a cache miss","role":"content","text":"A prefetch cache treats a miss as an error or a missing record. The two reasonable strategies are:\n\n* **Return a 404 to the user.** Appropriate when the cache is authoritative for the lookup — for example, when the user is asking for a category by ID and the ID is not in the cache.\n* **Page on-call.** A sustained miss rate on IDs you know exist is an incident: either the prefetch did not run, or the sync pipeline is broken.\n\nWhichever you choose, do not fall back to the primary on the read path — that is what cache-aside is for, and conflating the two patterns breaks the load-isolation guarantee that prefetch provides."},{"id":"bound-the-working-set-to-what-fits-in-memory","title":"Bound the working set to what fits in memory","role":"content","text":"Prefetch only works if the entire dataset fits in Redis memory with headroom. Estimate the size of your reference data, multiply by a growth factor, and confirm the result fits within your Redis instance's `maxmemory` minus what other use cases need. If the working set grows beyond what Redis can hold, switch the dataset to a cache-aside pattern instead — the request path will pay miss latency, but you will not OOM."},{"id":"reconcile-periodically-against-the-primary","title":"Reconcile periodically against the primary","role":"content","text":"CDC pipelines are eventually consistent: an event can be lost (broker outage, consumer crash, configuration drift) and the cache can silently diverge from the source. Run a periodic reconciliation job that re-reads all primary records, compares them against the cache, and either re-prefetches or fixes individual entries. Even running it once a day catches drift that ad-hoc inspection would miss."},{"id":"namespace-cache-keys-in-shared-redis-deployments","title":"Namespace cache keys in shared Redis deployments","role":"content","text":"If multiple applications share a Redis deployment, prefix cache keys with the application name (`cache:billing:category:{id}`) so different services cannot clobber each other's entries. `CacheConfig::prefix` takes the prefix exactly for this; the demo also accepts `--cache-prefix` on the command line."},{"id":"use-connectionmanager-not-a-bare-connection","title":"Use `ConnectionManager`, not a bare connection","role":"content","text":"`redis::aio::ConnectionManager` is the recommended async connection type for redis-rs. It is cheap to clone (the demo passes a clone to every async task), reconnects automatically on transient failures, and pipelines commands behind the scenes. A bare `redis::aio::Connection` is not `Clone` and cannot be safely shared across tokio tasks. If you need a true connection pool — for example, to fan out heavy `MULTI`/`EXEC` traffic across multiple sockets — use [`deadpool-redis`](https://crates.io/crates/deadpool-redis) or [`bb8-redis`](https://crates.io/crates/bb8-redis)."},{"id":"inspect-cached-entries-directly-in-redis","title":"Inspect cached entries directly in Redis","role":"content","text":"When testing or troubleshooting, inspect the stored cache keys directly to confirm the bulk load and the sync worker are writing what you expect:\n\n[code example]\n\nIf a key is missing for an ID that still exists in the primary, the prefetch did not run, the key expired without a sync refresh, or someone invalidated it. If a key is still present for an ID that was deleted in the primary, the delete event has not yet been applied. If the TTL is much lower than the configured safety-net value on a hot key, the sync worker is not keeping up."},{"id":"learn-more","title":"Learn more","role":"related","text":"* [redis-rs crate](https://crates.io/crates/redis) - Install and use the Rust Redis client\n* [`HSET`](https://redis.io/docs/latest/commands/hset) - Write hash fields\n* [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) - Read every field of a hash\n* [`EXPIRE`](https://redis.io/docs/latest/commands/expire) - Set key expiration in seconds\n* [`DEL`](https://redis.io/docs/latest/commands/del) - Delete a key on invalidation or sync-delete\n* [`SCAN`](https://redis.io/docs/latest/commands/scan) - Iterate the cached keyspace without blocking the server\n* [`TTL`](https://redis.io/docs/latest/commands/ttl) - Inspect remaining safety-net time on a key\n* [Redis Data Integration](https://redis.io/docs/latest/integrate/redis-data-integration) - Configuration-driven CDC into Redis on Redis Enterprise and Redis Cloud"}],"examples":[{"id":"the-prefetch-cache-helper-ex0","language":"rust","code":"use redis::aio::ConnectionManager;\nuse redis::Client;\n\nlet client = Client::open(\"redis://localhost:6379/\")?;\nlet conn = ConnectionManager::new(client).await?;\n\nlet cache = PrefetchCache::new(conn, CacheConfig::default());\nlet primary = MockPrimaryStore::new(80);\n\n// Pre-load every primary record into Redis in one pipelined round trip.\nlet records = primary.list_records().await;\ncache.bulk_load(records).await?;\n\n// Start the sync worker so primary mutations propagate into Redis.\nlet sync = Arc::new(SyncWorker::new(primary.clone(), cache.clone()));\nsync.start().await;\n\n// Read paths now go to Redis only.\nlet result = cache.get(\"cat-001\").await?;","section_id":"the-prefetch-cache-helper"},{"id":"data-model-ex0","language":"text","code":"cache:category:cat-001\n  id            = cat-001\n  name          = Beverages\n  display_order = 1\n  featured      = true\n  parent_id     =","section_id":"data-model"},{"id":"bulk-load-on-startup-ex0","language":"rust","code":"pub async fn bulk_load(\n    &self,\n    records: Vec<HashMap<String, String>>,\n) -> RedisResult<u64> {\n    let mut pipe = redis::pipe();\n    let mut loaded: u64 = 0;\n    for record in &records {\n        let entity_id = match record.get(\"id\") {\n            Some(v) if !v.is_empty() => v.clone(),\n            _ => continue,\n        };\n        let cache_key = self.cache_key(&entity_id);\n        let pairs: Vec<(String, String)> =\n            record.iter().map(|(k, v)| (k.clone(), v.clone())).collect();\n        pipe.del(&cache_key).ignore();\n        pipe.hset_multiple(&cache_key, &pairs).ignore();\n        pipe.expire(&cache_key, self.cfg.ttl_seconds).ignore();\n        loaded += 1;\n    }\n    if loaded > 0 {\n        let mut conn = self.conn.clone();\n        pipe.query_async::<_, ()>(&mut conn).await?;\n    }\n    self.stats.prefetched.fetch_add(loaded, Ordering::Relaxed);\n    Ok(loaded)\n}","section_id":"bulk-load-on-startup"},{"id":"reads-from-redis-only-ex0","language":"rust","code":"pub async fn get(&self, entity_id: &str) -> RedisResult<GetResult> {\n    let cache_key = self.cache_key(entity_id);\n    let mut conn = self.conn.clone();\n\n    let started = Instant::now();\n    let cached: HashMap<String, String> = conn.hgetall(&cache_key).await?;\n    let redis_latency_ms = started.elapsed().as_secs_f64() * 1000.0;\n\n    if !cached.is_empty() {\n        self.stats.hits.fetch_add(1, Ordering::Relaxed);\n        Ok(GetResult { record: Some(cached), hit: true, redis_latency_ms })\n    } else {\n        self.stats.misses.fetch_add(1, Ordering::Relaxed);\n        Ok(GetResult { record: None, hit: false, redis_latency_ms })\n    }\n}","section_id":"reads-from-redis-only"},{"id":"applying-sync-events-ex0","language":"rust","code":"pub async fn apply_change(&self, change: &ChangeEvent) -> RedisResult<()> {\n    let cache_key = self.cache_key(&change.id);\n    let mut conn = self.conn.clone();\n\n    match change.op.as_str() {\n        \"upsert\" => {\n            let fields = match &change.fields {\n                Some(f) if !f.is_empty() => f,\n                _ => return Ok(()),\n            };\n            let pairs: Vec<(String, String)> =\n                fields.iter().map(|(k, v)| (k.clone(), v.clone())).collect();\n            redis::pipe()\n                .atomic()\n                .del(&cache_key).ignore()\n                .hset_multiple(&cache_key, &pairs).ignore()\n                .expire(&cache_key, self.cfg.ttl_seconds).ignore()\n                .query_async::<_, ()>(&mut conn).await?;\n        }\n        \"delete\" => {\n            let _: i64 = conn.del(&cache_key).await?;\n        }\n        _ => {}\n    }\n    Ok(())\n}","section_id":"applying-sync-events"},{"id":"the-sync-worker-ex0","language":"rust","code":"async fn run_loop(\n    primary: Arc<MockPrimaryStore>,\n    cache: PrefetchCache,\n    poll_timeout: Duration,\n    state: WorkerState,\n) {\n    loop {\n        if *state.stop_rx.borrow() { return; }\n        if *state.pause_rx.borrow() {\n            state.idle_notify.notify_waiters();\n            // park until the pause is lifted or the worker is stopped\n            // ...\n            continue;\n        }\n        match primary.next_change(poll_timeout).await {\n            Some(change) => {\n                if let Err(err) = cache.apply_change(&change).await {\n                    eprintln!(\"[sync] failed to apply {:?} id={}: {}\", change.op, change.id, err);\n                }\n            }\n            None => continue,\n        }\n    }\n}","section_id":"the-sync-worker"},{"id":"re-prefetch-under-load-ex0","language":"rust","code":"let _ = state.sync.pause(Duration::from_secs(2)).await;\nlet _ = state.cache.clear().await.unwrap_or(0);\nlet records = state.primary.list_records().await;\nlet loaded = state.cache.bulk_load(records).await.unwrap_or(0);\nstate.sync.resume().await;","section_id":"re-prefetch-under-load"},{"id":"hitmiss-accounting-ex0","language":"rust","code":"pub fn stats(&self) -> serde_json::Value {\n    let hits = self.stats.hits.load(Ordering::Relaxed);\n    let misses = self.stats.misses.load(Ordering::Relaxed);\n    // ...\n    serde_json::json!({\n        \"hits\": hits,\n        \"misses\": misses,\n        \"hit_rate_pct\": hit_rate_pct,\n        \"prefetched\": prefetched,\n        \"sync_events_applied\": sync_events_applied,\n        \"sync_lag_ms_avg\": sync_lag_ms_avg,\n    })\n}","section_id":"hitmiss-accounting"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir prefetch-cache-demo && cd prefetch-cache-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/prefetch-cache/rust\ncurl -O $BASE/Cargo.toml\ncurl -O $BASE/cache.rs\ncurl -O $BASE/primary.rs\ncurl -O $BASE/sync_worker.rs\ncurl -O $BASE/demo_server.rs","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"cargo run --bin demo_server","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"Redis prefetch-cache demo server listening on http://127.0.0.1:8790\nUsing Redis at localhost:6379 with cache prefix 'cache:category:' and TTL 3600s\nPrefetched 5 records in 90.1 ms; sync worker running","section_id":"start-the-demo-server"},{"id":"the-mock-primary-store-ex0","language":"rust","code":"pub struct MockPrimaryStore {\n    pub read_latency_ms: u64,\n    reads: AtomicU64,\n    inner: Mutex<Inner>,                          // records + tx\n    rx: Mutex<mpsc::UnboundedReceiver<ChangeEvent>>,\n}\n\npub async fn update_field(&self, entity_id: &str, field: &str, value: &str) -> bool {\n    let mut inner = self.inner.lock().await;\n    // ... mutate the record ...\n    emit_locked(&inner.tx, CHANGE_OP_UPSERT, entity_id, Some(snapshot));\n    true\n}","section_id":"the-mock-primary-store"},{"id":"inspect-cached-entries-directly-in-redis-ex0","language":"bash","code":"redis-cli --scan --pattern 'cache:category:*'\nredis-cli HGETALL cache:category:cat-001\nredis-cli TTL cache:category:cat-001","section_id":"inspect-cached-entries-directly-in-redis"}]}
{"schema_version":2,"id":"develop/use-cases/pub-sub/dotnet","title":"Redis pub/sub with StackExchange.Redis","url":"https://redis.io/docs/latest/develop/use-cases/pub-sub/dotnet/","summary":"Implement Redis pub/sub messaging in C# with StackExchange.Redis","aliases":["/develop/use-cases/pub-sub/stackexchange.redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-17T10:28:27+01:00","children":[],"page_type":"content","content_hash":"3889fd597ef6f5d691872a1255695dcd6f4df81931f65456d67307492b2fffc3","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis-backed pub/sub broadcaster in C# with [StackExchange.Redis](https://seredis.dev/). It includes a small ASP.NET Core minimal-API web server so you can publish messages to named channels, add and remove subscribers live, and watch Redis fan out each message to every interested listener."},{"id":"overview","title":"Overview","role":"overview","text":"Pub/sub lets your application broadcast events — chat messages, cache invalidation signals, presence updates, notifications — to many consumers without per-pair wiring. The publisher names a *channel*; every client currently subscribed to that channel receives the message, in publish order, with sub-millisecond fan-out.\n\nThat gives you:\n\n* Many-to-many event delivery with no message storage cost in Redis\n* Exact-match subscriptions (`SUBSCRIBE orders:new`) for known topics\n* Pattern subscriptions (`PSUBSCRIBE notifications:*`) for whole topic hierarchies\n* Live server-side introspection through `PUBSUB CHANNELS`, `PUBSUB NUMSUB`, and `PUBSUB NUMPAT`\n* At-most-once delivery: subscribers that are offline when a message is published miss it, so durable state should live in keys or a Stream, not in the pub/sub channel itself\n\nIn this example, the publisher side calls `PUBLISH` with a JSON-encoded body and counts how many subscribers Redis reported delivering to. Every subscriber's handler is wired to a single shared subscribe connection managed transparently by `ConnectionMultiplexer`."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. The application calls `hub.Subscribe(name, channels)` or `hub.PSubscribe(name, patterns)`\n2. The helper wraps each target in a `RedisChannel.Literal()` or `RedisChannel.Pattern()` value and attaches a handler via `ISubscriber.Subscribe(...)`\n3. The application (or another process) calls `hub.Publish(channel, message)`\n4. Redis fans the message out over the multiplexer's shared subscribe socket\n5. The multiplexer routes each incoming message to every matching handler, where the helper wraps it as a `ReceivedMessage`, appends it to a per-subscription ring buffer, and bumps the received counter\n6. The publisher receives the integer subscriber count back from `PUBLISH`, which is the number of clients Redis delivered to right then\n\nPattern subscriptions match channels by glob (`*`, `?`, `[abc]`). A single message that matches both an exact subscription and a pattern subscription is delivered twice — once to the channel handler and once to the pattern handler."},{"id":"the-pubsub-hub-helper","title":"The pub/sub hub helper","role":"content","text":"The `RedisPubSubHub` class wraps the publish, subscribe, and introspection operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/pub-sub/dotnet/RedisPubSubHub.cs)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Pub/sub has no Redis keyspace footprint of its own — channels are server-side routing entries, not stored values. The hub keeps its own bookkeeping in process memory:\n\n[code example]\n\nThe implementation uses:\n\n* [`PUBLISH`](https://redis.io/docs/latest/commands/publish) to fan a JSON-encoded message out to every subscriber of a channel\n* [`SUBSCRIBE`](https://redis.io/docs/latest/commands/subscribe) for exact-match subscribers (via `RedisChannel.Literal`)\n* [`PSUBSCRIBE`](https://redis.io/docs/latest/commands/psubscribe) for glob-style pattern subscribers (via `RedisChannel.Pattern`)\n* [`PUBSUB CHANNELS`](https://redis.io/docs/latest/commands/pubsub-channels) to list the channels with at least one active exact-match subscriber\n* [`PUBSUB NUMSUB`](https://redis.io/docs/latest/commands/pubsub-numsub) to count subscribers per channel\n* [`PUBSUB NUMPAT`](https://redis.io/docs/latest/commands/pubsub-numpat) to count active pattern subscriptions server-wide\n* `ConnectionMultiplexer`'s shared subscribe connection: every subscription added via `ISubscriber.Subscribe(...)` rides on the same socket, so adding a fifth subscriber doesn't open a fifth connection"},{"id":"publishing-messages","title":"Publishing messages","role":"content","text":"`Publish()` JSON-encodes the message body, calls `PUBLISH`, and updates the per-channel publish counter:\n\n[code example]\n\nThe integer returned by `PUBLISH` is what Redis itself reports — the number of subscribers (direct and pattern) that received the message in that call. It's a useful sanity check that the channel name is actually being listened to: a steady stream of `0`s means you have a typo somewhere or your subscriber crashed.\n\nA small note about StackExchange.Redis 2.7+: `RedisChannel` no longer has an implicit conversion from `string`, so you have to spell out whether the channel is a literal name or a glob pattern. That's by design — it's the same type used for both `SUBSCRIBE` and `PSUBSCRIBE`, and the wrong choice would silently route a literal channel through pattern matching (or vice versa). Use `RedisChannel.Literal(\"channel:name\")` for `PUBLISH` and exact-match `SUBSCRIBE`, and `RedisChannel.Pattern(\"channel:*\")` for `PSUBSCRIBE`."},{"id":"subscribing-to-channels","title":"Subscribing to channels","role":"content","text":"`Subscribe()` creates a named in-process `Subscription`. Internally, the `Subscription` constructor builds a `RedisChannel.Literal` for each target and attaches a single handler delegate via `ISubscriber.Subscribe`:\n\n[code example]\n\nA few details matter here:\n\n* StackExchange.Redis multiplexes every subscription in the process onto a **single shared subscribe connection** managed by the `ConnectionMultiplexer`. Unlike redis-py or node-redis, you don't open a new TCP connection per subscriber — adding a thousand `Subscribe()` calls still costs one socket. This also means a single subscriber's `Unsubscribe(binding, handler)` only detaches its own handler; the shared subscribe socket stays up for the others.\n* The handler is stored as a field so the same delegate instance is used to subscribe and unsubscribe — StackExchange.Redis matches by delegate identity when detaching.\n* The handler signature is `Action<RedisChannel, RedisValue>`. Do not block in this handler: it runs on a ThreadPool worker that's shared across every subscription in the process. Buffer the message and return quickly, as the helper does."},{"id":"pattern-subscriptions-with-psubscribe","title":"Pattern subscriptions with PSUBSCRIBE","role":"content","text":"`PSubscribe()` works the same way but wraps each target in `RedisChannel.Pattern` so the underlying command is `PSUBSCRIBE`:\n\n[code example]\n\nWhen a published channel matches a pattern, the handler receives the *actual* matched channel name — not the pattern. The pattern itself is implicit in the registration. The helper records both, by running the matched channel back through a small glob matcher to find which of the subscription's registered patterns it satisfies:\n\n[code example]\n\nThat distinction is useful for routing: a pattern subscriber can do one thing for the whole hierarchy (e.g., increment a counter) and dispatch on the specific channel within its handler (e.g., \"invalidate this region's cache\")."},{"id":"inspecting-active-subscribers","title":"Inspecting active subscribers","role":"content","text":"Redis exposes a small set of pub/sub introspection commands that report on subscriber state without traversing any keyspace:\n\n[code example]\n\n`PUBSUB CHANNELS` is a server-wide command, not connection-scoped, so `IServer.SubscriptionChannels(pattern)` in StackExchange.Redis requires a specific endpoint — the helper resolves the multiplexer's first endpoint and calls it there. The helper also filters out the library-internal `__Booksleeve_MasterChanged` channel that the multiplexer maintains for replica-change notifications.\n\n`PUBSUB CHANNELS` only reports channels with at least one exact-match subscriber — pattern subscribers do not appear here. That's a deliberate Redis design choice: a glob like `*` would otherwise show up as a subscriber to every conceivable channel. `PUBSUB NUMPAT` covers the pattern side as a single global count.\n\n`PUBSUB NUMSUB` doesn't have a first-class wrapper in StackExchange.Redis, so the helper drops down to `IDatabase.Execute(\"PUBSUB\", \"NUMSUB\", ch1, ch2, ...)` and parses the flat array Redis returns."},{"id":"stats-and-history","title":"Stats and history","role":"content","text":"`Stats()` reports publish and receive counters plus the size of the subscription registry:\n\n[code example]\n\n`delivered_total` is what Redis itself counted; `received_total` is what this process's in-memory subscribers saw. In a single-process demo they should track each other closely — a sustained divergence usually means a handler threw, or a subscriber was closed while a publisher kept publishing. (Pub/sub is at-most-once: if your subscriber wasn't attached at publish time, the message is gone.)"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* Redis 6.2 or later running locally on the default port (6379). Earlier versions still work for plain `PUBLISH`/`SUBSCRIBE`; `PUBSUB NUMPAT` is older than that.\n* [.NET SDK 8.0](https://dotnet.microsoft.com/) or later.\n* The [StackExchange.Redis](https://www.nuget.org/packages/StackExchange.Redis) client (version 2.7 or newer). The included `PubSubDemo.csproj` already lists it as a `PackageReference`; `dotnet run` resolves it on first invocation."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of three C# files plus a `.csproj`. Download them from the [`dotnet` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/pub-sub/dotnet) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From that directory:\n\n[code example]\n\nThe first run pulls the NuGet packages, so it takes a few seconds; subsequent runs start immediately. You should see:\n\n[code example]\n\nOpen [http://localhost:8100](http://localhost:8100) in a browser. You can:\n\n* Publish messages of any text to any channel name in any batch size.\n* Add named subscribers that listen on either a specific channel (`orders:new`) or a glob pattern (`notifications:*`). A single subscriber can listen on multiple targets — enter them comma-separated.\n* Watch each subscriber's incoming-message panel update every 800 ms.\n* See the server-side view: `PUBSUB CHANNELS` lists exact-match channels with subscribers, `PUBSUB NUMSUB` gives per-channel counts, and `PUBSUB NUMPAT` counts active pattern subscriptions.\n* Click **Reset** to drop every subscription, zero the counters, and re-seed the three default subscribers.\n\nIf your Redis server is running elsewhere, pass `--redis-host` and `--redis-port`. To listen on a different HTTP port, use `dotnet run --urls http://localhost:9000` or the `--port` flag."},{"id":"production-usage","title":"Production usage","role":"content","text":""},{"id":"pubsub-is-at-most-once--pair-it-with-durable-state-if-you-need-replay","title":"Pub/sub is at-most-once — pair it with durable state if you need replay","role":"content","text":"A subscriber that's offline when a message is published misses it permanently. For events you can't afford to lose, write the durable record (the order row, the cache key version, the audit log entry) to its primary store, then `PUBLISH` a notification so live consumers can pick it up immediately. On reconnect, consumers reconcile by reading the durable store, not by waiting for missed pub/sub messages. If you actually need replay or at-least-once delivery, switch to [Redis Streams](https://redis.io/docs/latest/develop/data-types/streams) with consumer groups."},{"id":"one-connectionmultiplexer-for-the-whole-process","title":"One ConnectionMultiplexer for the whole process","role":"content","text":"`ConnectionMultiplexer` is expensive to create and thread-safe to share — open it once at startup and hand out `IDatabase` and `ISubscriber` references everywhere. Behind the scenes, the multiplexer keeps a small fixed pool of sockets (one for regular commands, one for subscribe-mode, plus a few for clusters) and pipelines every command over them. Opening a fresh multiplexer per request would waste connections and lose the pipelining win."},{"id":"subscriptions-share-one-subscribe-connection--handler-hygiene-matters","title":"Subscriptions share one subscribe connection — handler hygiene matters","role":"content","text":"Where redis-py and node-redis open a fresh TCP connection for every subscriber, StackExchange.Redis multiplexes every `ISubscriber.Subscribe(...)` registration onto a single shared subscribe socket. That's cheaper and simpler, but it means a slow handler blocks the dispatch path for every other subscription in the process until it returns. The demo's `OnMessage` buffers and returns immediately. In production, do the same: if you need heavier work, dispatch the buffered message to a `Channel<T>`, a `Task.Run`, or a Redis Streams consumer group, and let the handler hand off and return.\n\nThe .NET ThreadPool grows by ~2 threads/second under load, which can briefly delay handlers if a single multiplexer is fanning out to hundreds of subscriptions during a burst. The demo bumps the floor with `ThreadPool.SetMinThreads(64, 64)` at startup — keep that line, or the equivalent, in any pub/sub-heavy service."},{"id":"choose-a-topic-naming-convention-up-front","title":"Choose a topic naming convention up front","role":"content","text":"A flat namespace gets ugly fast — `email`, `email_high_priority`, `email_high_priority_billing`. Pick a colon-separated hierarchy (`notifications:billing:invoice`, `cache:invalidate:products:p-001`) so consumers can subscribe at the right level: a billing service uses `notifications:billing:*`, the audit logger uses `notifications:*`. Glob patterns are evaluated for every published message, so don't go wild with multiple wildcards on hot paths — `*:*:*` matches everything and costs more than a flat `notifications:*` would."},{"id":"use-the-matched-channel-not-the-pattern-for-routing-inside-a-handler","title":"Use the matched channel, not the pattern, for routing inside a handler","role":"content","text":"For a pattern subscription, the handler's `RedisChannel` argument is the *actual* matched channel — `notifications:billing:invoice`, not `notifications:*`. Dispatch on the matched channel inside the handler if you need per-key routing. The original pattern is implicit in the registration; the helper recovers it for the UI by glob-matching, but production code rarely needs to."},{"id":"tune-the-subscriber-buffer-for-your-traffic-shape","title":"Tune the subscriber buffer for your traffic shape","role":"content","text":"The demo caps each subscription's in-memory message buffer at 50. That's right for showing the recent activity in a UI, but a real subscriber typically processes each message and discards it — the buffer is only there for human inspection. If you keep a buffer, make sure it's bounded; an unbounded ring on a chatty pattern subscriber will eventually OOM the process."},{"id":"sharded-pubsub-on-a-redis-cluster","title":"Sharded pub/sub on a Redis Cluster","role":"content","text":"On a Redis Cluster, plain `PUBLISH` fans every message out to every node via the cluster bus, which becomes a hotspot at high throughput. Redis 7.0 added [sharded pub/sub](https://redis.io/docs/latest/develop/pubsub#sharded-pubsub): channels are hashed to slots, and `SPUBLISH` / `SSUBSCRIBE` only touch the shard that owns the slot. StackExchange.Redis exposes the sharded variants via `ISubscriber.Publish(..., flags: CommandFlags.None)` on a cluster-aware multiplexer once your topology is sharded; pick channel names whose hash distribution matches your traffic."},{"id":"inspect-pubsub-state-directly-in-redis","title":"Inspect pub/sub state directly in Redis","role":"content","text":"Because pub/sub has no keyspace, `KEYS`/`SCAN` won't show you anything. Use the introspection commands instead:\n\n[code example]\n\n`redis-cli` in subscribe mode only exits with `Ctrl-C` — it can't issue any other commands while subscribed."},{"id":"learn-more","title":"Learn more","role":"related","text":"This example uses the following Redis commands:\n\n* [`PUBLISH`](https://redis.io/docs/latest/commands/publish) to fan a message out to every subscriber of a channel.\n* [`SUBSCRIBE`](https://redis.io/docs/latest/commands/subscribe) and [`UNSUBSCRIBE`](https://redis.io/docs/latest/commands/unsubscribe) for exact-match topic subscriptions.\n* [`PSUBSCRIBE`](https://redis.io/docs/latest/commands/psubscribe) and [`PUNSUBSCRIBE`](https://redis.io/docs/latest/commands/punsubscribe) for glob-style pattern subscriptions.\n* [`PUBSUB CHANNELS`](https://redis.io/docs/latest/commands/pubsub-channels) to list channels with at least one active exact-match subscriber.\n* [`PUBSUB NUMSUB`](https://redis.io/docs/latest/commands/pubsub-numsub) to count subscribers per named channel.\n* [`PUBSUB NUMPAT`](https://redis.io/docs/latest/commands/pubsub-numpat) to count active pattern subscriptions server-wide.\n\nSee the [StackExchange.Redis documentation](https://seredis.dev/) for full client reference, including the [`ISubscriber` interface](https://seredis.dev/PubSubOrder.html) and the multiplexer's shared subscribe-connection model."}],"examples":[{"id":"the-pubsub-hub-helper-ex0","language":"csharp","code":"using PubSubDemo;\nusing StackExchange.Redis;\n\nvar multiplexer = ConnectionMultiplexer.Connect(\"localhost:6379\");\nvar hub = new RedisPubSubHub(multiplexer);\n\n// Exact-match subscriber\nhub.Subscribe(\"orders-listener\", new[] { \"orders:new\" });\n\n// Pattern subscriber covering an entire topic hierarchy\nhub.PSubscribe(\"all-notifications\", new[] { \"notifications:*\" });\n\n// Publish — returns Redis' delivered count for this PUBLISH\nvar delivered = hub.Publish(\"orders:new\",\n    new { order_id = 42, total = 199.0 });\nConsole.WriteLine($\"Redis delivered to {delivered} subscriber(s)\");\n\n// Look at what each subscriber received\nforeach (var sub in hub.Subscriptions())\n{\n    Console.WriteLine($\"{sub.Name} {sub.ReceivedTotal} messages\");\n    foreach (var msg in sub.Messages(limit: 5))\n    {\n        Console.WriteLine($\"  {msg.Channel} {msg.Payload}\");\n    }\n}\n\nhub.Unsubscribe(\"orders-listener\");\nhub.Shutdown(); // closes every remaining subscription","section_id":"the-pubsub-hub-helper"},{"id":"data-model-ex0","language":"text","code":"RedisPubSubHub                          (in-process)\n  _subscriptions            ConcurrentDictionary<string, Subscription>\n  _publishedTotal           long\n  _deliveredTotal           long\n  _channelPublished         ConcurrentDictionary<string, long>\n\nSubscription                            (in-process, one per subscriber)\n  Name                      string\n  Targets                   IReadOnlyList<string>\n  IsPattern                 bool\n  _buffer                   LinkedList<ReceivedMessage>      (capped, default 50)\n  _received                 long\n  _bindings                 RedisChannel[]                    (one per target)\n  _handler                  Action<RedisChannel, RedisValue>  (shared multiplexer)","section_id":"data-model"},{"id":"publishing-messages-ex0","language":"csharp","code":"public long Publish(string channel, object? message)\n{\n    var payload = JsonSerializer.Serialize(message);\n    var delivered = _subscriber.Publish(RedisChannel.Literal(channel), payload);\n    Interlocked.Increment(ref _publishedTotal);\n    Interlocked.Add(ref _deliveredTotal, delivered);\n    _channelPublished.AddOrUpdate(channel, 1, (_, current) => current + 1);\n    return delivered;\n}","section_id":"publishing-messages"},{"id":"subscribing-to-channels-ex0","language":"csharp","code":"public Subscription Subscribe(string name, IEnumerable<string> channels) =>\n    Register(name, channels, isPattern: false);\n\n// Inside Subscription's constructor:\n_handler = OnMessage;\nforeach (var binding in _bindings)\n{\n    _subscriber.Subscribe(binding, _handler);\n}","section_id":"subscribing-to-channels"},{"id":"pattern-subscriptions-with-psubscribe-ex0","language":"csharp","code":"hub.PSubscribe(\"all-notifications\", new[] { \"notifications:*\" });\nhub.PSubscribe(\"cache-invalidator\", new[] { \"cache:invalidate:*\" });","section_id":"pattern-subscriptions-with-psubscribe"},{"id":"pattern-subscriptions-with-psubscribe-ex1","language":"csharp","code":"private void OnMessage(RedisChannel actualChannel, RedisValue value)\n{\n    string? pattern = null;\n    if (IsPattern)\n    {\n        var name = (string)actualChannel!;\n        pattern = MatchPattern(name) ?? Targets[0];\n    }\n    // ...wrap as ReceivedMessage with both channel and pattern...\n}","section_id":"pattern-subscriptions-with-psubscribe"},{"id":"inspecting-active-subscribers-ex0","language":"csharp","code":"hub.ActiveChannels();                  // PUBSUB CHANNELS *\nhub.ChannelSubscriberCounts(channels); // PUBSUB NUMSUB ch1 ch2 ...\nhub.PatternSubscriberCount();          // PUBSUB NUMPAT","section_id":"inspecting-active-subscribers"},{"id":"stats-and-history-ex0","language":"csharp","code":"public Dictionary<string, object> Stats()\n{\n    var subs = _subscriptions.Values.ToArray();\n    var channelPublished = _channelPublished\n        .ToDictionary(kv => kv.Key, kv => kv.Value);\n    var receivedTotal = subs.Sum(s => s.ReceivedTotal);\n\n    return new Dictionary<string, object>\n    {\n        [\"published_total\"] = Interlocked.Read(ref _publishedTotal),\n        [\"delivered_total\"] = Interlocked.Read(ref _deliveredTotal),\n        [\"received_total\"] = receivedTotal,\n        [\"active_subscriptions\"] = (long)subs.Length,\n        [\"channel_published\"] = channelPublished,\n        [\"pattern_subscriptions\"] = PatternSubscriberCount(),\n    };\n}","section_id":"stats-and-history"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir pub-sub-demo && cd pub-sub-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/pub-sub/dotnet\ncurl -O $BASE/PubSubDemo.csproj\ncurl -O $BASE/RedisPubSubHub.cs\ncurl -O $BASE/Program.cs","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"dotnet run","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"Redis pub/sub demo server listening on http://0.0.0.0:8100\nUsing Redis at localhost:6379\nSeeded 3 default subscription(s)","section_id":"start-the-demo-server"},{"id":"inspect-pubsub-state-directly-in-redis-ex0","language":"bash","code":"# Which channels currently have at least one exact-match subscriber?\nredis-cli pubsub channels '*'\n\n# How many subscribers does each channel have?\nredis-cli pubsub numsub orders:new notifications:billing chat:lobby\n\n# How many active pattern subscriptions across the whole server?\nredis-cli pubsub numpat\n\n# Subscribe interactively from the CLI to watch traffic on a pattern\nredis-cli psubscribe 'orders:*'","section_id":"inspect-pubsub-state-directly-in-redis"}]}
{"schema_version":2,"id":"develop/use-cases/pub-sub/go","title":"Redis pub/sub with go-redis","url":"https://redis.io/docs/latest/develop/use-cases/pub-sub/go/","summary":"Implement Redis pub/sub messaging in Go with go-redis","aliases":["/develop/use-cases/pub-sub/go-redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-17T10:28:27+01:00","children":[],"page_type":"content","content_hash":"95b9da135226829e18eb32c8551619ce6a9bc4113799737869887cd80328ff67","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis-backed pub/sub broadcaster in Go with [`go-redis`](https://redis.io/docs/latest/develop/clients/go). It includes a small local web server built with the Go standard library so you can publish messages to named channels, add and remove subscribers live, and watch Redis fan out each message to every interested listener."},{"id":"overview","title":"Overview","role":"overview","text":"Pub/sub lets your application broadcast events — chat messages, cache invalidation signals, presence updates, notifications — to many consumers without per-pair wiring. The publisher names a *channel*; every client currently subscribed to that channel receives the message, in publish order, with sub-millisecond fan-out.\n\nThat gives you:\n\n* Many-to-many event delivery with no message storage cost in Redis\n* Exact-match subscriptions (`SUBSCRIBE orders:new`) for known topics\n* Pattern subscriptions (`PSUBSCRIBE notifications:*`) for whole topic hierarchies\n* Live server-side introspection through `PUBSUB CHANNELS`, `PUBSUB NUMSUB`, and `PUBSUB NUMPAT`\n* At-most-once delivery: subscribers that are offline when a message is published miss it, so durable state should live in keys or a Stream, not in the pub/sub channel itself\n\nIn this example, the publisher side calls `PUBLISH` with a JSON-encoded body and counts how many subscribers Redis reported delivering to. Each in-process subscriber owns its own Redis connection plus a background goroutine that reads from the go-redis message channel and pushes each delivery into a per-subscriber ring buffer."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. The application calls `hub.Subscribe(ctx, name, channels)` or `hub.PSubscribe(ctx, name, patterns)`\n2. The helper opens a `*redis.PubSub` (one Redis connection per subscriber), reads its `Channel()` in a goroutine, and registers the new `Subscription` under its name\n3. The application (or another process) calls `hub.Publish(ctx, channel, message)`\n4. Redis fans the message out over every subscribing client's open socket\n5. Each subscriber's goroutine wraps the raw `*redis.Message` as a `ReceivedMessage`, appends it to a bounded per-subscriber buffer, and bumps the atomic counter\n6. The publisher receives the integer subscriber count back from `PUBLISH`, which is the number of clients Redis delivered to right then\n\nPattern subscriptions match channels by glob (`*`, `?`, `[abc]`). A single message that matches both an exact subscription and a pattern subscription is delivered twice — once as a `message` and once as a `pmessage`. The helper exposes which pattern matched (if any) through `ReceivedMessage.Pattern`."},{"id":"the-pubsub-hub-helper","title":"The pub/sub hub helper","role":"content","text":"The `RedisPubSubHub` type wraps the publish, subscribe, and introspection operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/pub-sub/go/pubsub_hub.go)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Pub/sub has no Redis keyspace footprint of its own — channels are server-side routing entries, not stored values. The hub keeps its own bookkeeping in process memory:\n\n[code example]\n\nThe implementation uses:\n\n* [`PUBLISH`](https://redis.io/docs/latest/commands/publish) to fan a JSON-encoded message out to every subscriber of a channel\n* [`SUBSCRIBE`](https://redis.io/docs/latest/commands/subscribe) for exact-match subscribers\n* [`PSUBSCRIBE`](https://redis.io/docs/latest/commands/psubscribe) for glob-style pattern subscribers\n* [`PUBSUB CHANNELS`](https://redis.io/docs/latest/commands/pubsub-channels) to list the channels with at least one active exact-match subscriber\n* [`PUBSUB NUMSUB`](https://redis.io/docs/latest/commands/pubsub-numsub) to count subscribers per channel\n* [`PUBSUB NUMPAT`](https://redis.io/docs/latest/commands/pubsub-numpat) to count active pattern subscriptions server-wide\n* The go-redis `*redis.PubSub.Channel()` helper, which spins up an internal goroutine that pumps messages off the socket into a Go channel — so the helper just ranges over that channel and never touches `Receive()` or `ReceiveMessage()` directly"},{"id":"publishing-messages","title":"Publishing messages","role":"content","text":"`Publish()` JSON-encodes the message body, calls `PUBLISH`, and updates the per-channel publish counter:\n\n[code example]\n\nThe integer returned by `PUBLISH` is what Redis itself reports — the number of subscribers (direct and pattern) that received the message in that call. It's a useful sanity check that the channel name is actually being listened to: a steady stream of `0`s means you have a typo somewhere or your subscriber crashed."},{"id":"subscribing-to-channels","title":"Subscribing to channels","role":"content","text":"`Subscribe()` creates a named in-process `Subscription` that owns its own `*redis.PubSub` and dispatch goroutine:\n\n[code example]\n\nInside `register`, the helper opens a `PubSub`, reads its `Channel()` to get a buffered Go channel of incoming messages, and starts a goroutine that pumps each delivery into the subscription's ring buffer:\n\n[code example]\n\nA few details matter here:\n\n* Each `Subscription` gets its own `*redis.PubSub` (and therefore its own connection from the client pool). Sharing one across business subscribers would couple their lifetimes — closing one would close the channel for the others.\n* `ps.Channel()` is the right API to use here: go-redis runs an internal goroutine that pumps off the socket into a buffered Go channel and reconnects on transient failures. The helper just ranges over that channel.\n* Closing the `PubSub` (via `sub.Close()`) closes its underlying message channel, which causes the `range` loop in `sub.run()` to terminate cleanly. The helper waits on a `done` channel to be sure the goroutine has finished before returning from `Close()`.\n\nThe dispatch goroutine wraps each `*redis.Message` as a `ReceivedMessage` and tries to decode the payload as JSON, falling back to the raw string if it doesn't parse:\n\n[code example]"},{"id":"pattern-subscriptions-with-psubscribe","title":"Pattern subscriptions with PSUBSCRIBE","role":"content","text":"`PSubscribe()` works the same way but routes messages through `PSUBSCRIBE` so each binding is a glob, not a literal channel name:\n\n[code example]\n\nWhen a published channel matches a pattern, go-redis populates both `Message.Channel` (the actual channel) and `Message.Pattern` (the pattern that matched). Exact-match deliveries leave `Pattern` as the empty string, so the helper carries the pattern as a `*string` and serialises it as `null` in JSON for exact matches:\n\n[code example]\n\nThat distinction is useful for routing: a pattern subscriber can do one thing for the whole hierarchy (e.g., increment a counter) and dispatch on the specific channel within its handler (e.g., \"invalidate this region's cache\")."},{"id":"inspecting-active-subscribers","title":"Inspecting active subscribers","role":"content","text":"Redis exposes a small set of pub/sub introspection commands that report on subscriber state without traversing any keyspace:\n\n[code example]\n\n`PUBSUB CHANNELS` only reports channels with at least one exact-match subscriber — pattern subscribers do not appear here. That's a deliberate Redis design choice: a glob like `*` would otherwise show up as a subscriber to every conceivable channel. `PUBSUB NUMPAT` covers the pattern side as a single global count."},{"id":"stats-and-history","title":"Stats and history","role":"content","text":"`Stats()` reports publish and receive counters plus the size of the subscription registry:\n\n[code example]\n\n`DeliveredTotal` is what Redis itself counted; `ReceivedTotal` is what this process's in-memory subscribers saw. In a single-process demo they should track each other closely — a sustained divergence usually means a subscriber goroutine exited, or a subscriber crashed while a publisher kept publishing. (Pub/sub is at-most-once: if your subscriber wasn't connected at publish time, the message is gone.)\n\nThe `Stats` struct uses `json:\"...\"` tags with snake_case names so the demo UI's shared JavaScript can read the same wire shape across every client port."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* Redis 6.2 or later running locally on the default port (6379). Earlier versions still work for plain `PUBLISH`/`SUBSCRIBE`; `PUBSUB NUMPAT` is older than that.\n* Go 1.23 or later (matching the version declared in this port's `go.mod`).\n* The `go-redis` client. The included `go.mod` pins:\n\n  [code example]"},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of three Go files plus a `go.mod` and `go.sum`. Download them from the [`go` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/pub-sub/go) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"Go's `package main` can't live in the same directory as `package pubsub`, so create a tiny `main.go` in a subdirectory that calls the demo entry point:\n\n[code example]\n\nThen build and run:\n\n[code example]\n\nYou should see:\n\n[code example]\n\nOpen [http://127.0.0.1:8097](http://127.0.0.1:8097) in a browser. You can:\n\n* Publish messages of any text to any channel name in any batch size.\n* Add named subscribers that listen on either a specific channel (`orders:new`) or a glob pattern (`notifications:*`). A single subscriber can listen on multiple targets — enter them comma-separated.\n* Watch each subscriber's incoming-message panel update every 800 ms.\n* See the server-side view: `PUBSUB CHANNELS` lists exact-match channels with subscribers, `PUBSUB NUMSUB` gives per-channel counts, and `PUBSUB NUMPAT` counts active pattern subscriptions.\n* Click **Reset** to drop every subscription, zero the counters, and re-seed the three default subscribers.\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`."},{"id":"production-usage","title":"Production usage","role":"content","text":""},{"id":"pubsub-is-at-most-once--pair-it-with-durable-state-if-you-need-replay","title":"Pub/sub is at-most-once — pair it with durable state if you need replay","role":"content","text":"A subscriber that's offline when a message is published misses it permanently. For events you can't afford to lose, write the durable record (the order row, the cache key version, the audit log entry) to its primary store, then `PUBLISH` a notification so live consumers can pick it up immediately. On reconnect, consumers reconcile by reading the durable store, not by waiting for missed pub/sub messages. If you actually need replay or at-least-once delivery, switch to [Redis Streams](https://redis.io/docs/latest/develop/data-types/streams) with consumer groups."},{"id":"use-a-separate-connection-or-pubsub-object-per-subscriber","title":"Use a separate connection (or `PubSub` object) per subscriber","role":"content","text":"A go-redis `*redis.PubSub` puts its connection into subscribe-only mode: ordinary commands (`Get`, `HSet`, etc.) using that connection will hang. Every call to `client.Subscribe(...)` / `client.PSubscribe(...)` checks out a fresh connection from the pool, so size the pool generously when you have many subscribers, and don't share one `PubSub` object across business subscribers (closing it would close the channel for all of them)."},{"id":"prefer-redispubsubchannel-over-a-hand-written-receivemessage-loop","title":"Prefer `*redis.PubSub.Channel()` over a hand-written `ReceiveMessage` loop","role":"content","text":"`Channel()` runs an internal goroutine that pumps the socket into a buffered Go channel and transparently reconnects on transient failures. A hand-written `for { ReceiveMessage(ctx) }` loop has to handle reconnect, timeouts, and back-pressure itself. Use `Channel()` unless you genuinely need fine-grained control over the read loop, in which case `ReceiveTimeout()` plus your own reconnect logic is the next step down."},{"id":"choose-a-topic-naming-convention-up-front","title":"Choose a topic naming convention up front","role":"content","text":"A flat namespace gets ugly fast — `email`, `email_high_priority`, `email_high_priority_billing`. Pick a colon-separated hierarchy (`notifications:billing:invoice`, `cache:invalidate:products:p-001`) so consumers can subscribe at the right level: a billing service uses `notifications:billing:*`, the audit logger uses `notifications:*`. Glob patterns are evaluated for every published message, so don't go wild with multiple wildcards on hot paths — `*:*:*` matches everything and costs more than a flat `notifications:*` would."},{"id":"dont-do-heavy-work-in-the-dispatch-goroutine","title":"Don't do heavy work in the dispatch goroutine","role":"content","text":"The dispatch goroutine reads messages from a single Go channel. If your handler blocks (synchronous HTTP call, big computation, slow DB write), the next message waits behind it and the subscriber's effective throughput drops to whatever the handler's latency is. For heavier work, the handler should hand the message off to a worker pool, a buffered job channel, or — for true durable handoff — a [Redis Streams](https://redis.io/docs/latest/develop/data-types/streams) consumer group."},{"id":"tune-the-subscriber-buffer-for-your-traffic-shape","title":"Tune the subscriber buffer for your traffic shape","role":"content","text":"The demo caps each subscriber's in-memory message buffer at 50. That's right for showing the recent activity in a UI, but a real subscriber typically processes each message and discards it — the buffer is only there for human inspection. If you keep a buffer, make sure it's bounded; an unbounded ring on a chatty pattern subscriber will eventually OOM the process. Likewise, `Channel()` accepts a `redis.WithChannelSize(size)` option if you need a larger or smaller internal buffer between the socket reader and your consumer goroutine."},{"id":"sharded-pubsub-on-a-redis-cluster","title":"Sharded pub/sub on a Redis Cluster","role":"content","text":"On a Redis Cluster, plain `PUBLISH` fans every message out to every node via the cluster bus, which becomes a hotspot at high throughput. Redis 7.0 added [sharded pub/sub](https://redis.io/docs/latest/develop/pubsub#sharded-pubsub): channels are hashed to slots, and `SPUBLISH` / `SSUBSCRIBE` only touch the shard that owns the slot. go-redis exposes the matching `SPublish`, `SSubscribe`, `PubSubShardChannels`, and `PubSubShardNumSub` methods on the cluster client. If you're scaling pub/sub on a cluster, prefer the sharded commands and pick channel names whose hash distribution matches your traffic."},{"id":"inspect-pubsub-state-directly-in-redis","title":"Inspect pub/sub state directly in Redis","role":"content","text":"Because pub/sub has no keyspace, `KEYS`/`SCAN` won't show you anything. Use the introspection commands instead:\n\n[code example]\n\n`redis-cli` in subscribe mode only exits with `Ctrl-C` — it can't issue any other commands while subscribed."},{"id":"learn-more","title":"Learn more","role":"related","text":"This example uses the following Redis commands:\n\n* [`PUBLISH`](https://redis.io/docs/latest/commands/publish) to fan a message out to every subscriber of a channel.\n* [`SUBSCRIBE`](https://redis.io/docs/latest/commands/subscribe) and [`UNSUBSCRIBE`](https://redis.io/docs/latest/commands/unsubscribe) for exact-match topic subscriptions.\n* [`PSUBSCRIBE`](https://redis.io/docs/latest/commands/psubscribe) and [`PUNSUBSCRIBE`](https://redis.io/docs/latest/commands/punsubscribe) for glob-style pattern subscriptions.\n* [`PUBSUB CHANNELS`](https://redis.io/docs/latest/commands/pubsub-channels) to list channels with at least one active exact-match subscriber.\n* [`PUBSUB NUMSUB`](https://redis.io/docs/latest/commands/pubsub-numsub) to count subscribers per named channel.\n* [`PUBSUB NUMPAT`](https://redis.io/docs/latest/commands/pubsub-numpat) to count active pattern subscriptions server-wide.\n\nSee the [`go-redis` documentation](https://redis.io/docs/latest/develop/clients/go) for full client reference."}],"examples":[{"id":"the-pubsub-hub-helper-ex0","language":"go","code":"import (\n    \"context\"\n\n    \"github.com/redis/go-redis/v9\"\n    \"pubsub\"\n)\n\nclient := redis.NewClient(&redis.Options{Addr: \"localhost:6379\"})\nhub := pubsub.NewRedisPubSubHub(client, 50)\nctx := context.Background()\n\n// Exact-match subscriber\nhub.Subscribe(ctx, \"orders-listener\", []string{\"orders:new\"})\n\n// Pattern subscriber covering an entire topic hierarchy\nhub.PSubscribe(ctx, \"all-notifications\", []string{\"notifications:*\"})\n\n// Publish — returns Redis' delivered count for this PUBLISH\ndelivered, _ := hub.Publish(ctx, \"orders:new\", map[string]any{\n    \"order_id\": 42,\n    \"total\":    199.0,\n})\nfmt.Printf(\"Redis delivered to %d subscriber(s)\\n\", delivered)\n\n// Look at what each subscriber received\nfor _, sub := range hub.Subscriptions() {\n    fmt.Println(sub.Name(), sub.ReceivedTotal(), \"messages\")\n    for _, msg := range sub.Messages(5) {\n        fmt.Println(\"  \", msg.Channel, msg.Payload)\n    }\n}\n\nhub.Unsubscribe(\"orders-listener\")\nhub.Shutdown() // closes every remaining subscription","section_id":"the-pubsub-hub-helper"},{"id":"data-model-ex0","language":"text","code":"RedisPubSubHub                          (in-process)\n  subscriptions             map[string]*Subscription\n  publishedTotal            int64\n  deliveredTotal            int64\n  channelPublished          map[channel]int\n\nSubscription                            (in-process, one per subscriber)\n  name                      string\n  targets                   []string  (channels or patterns)\n  isPattern                 bool\n  buffer                    []*ReceivedMessage (capped, default 50)\n  received                  int64                (atomic)\n  pubsub                    *redis.PubSub        (owns one connection)\n  goroutine                 reads ps.Channel()","section_id":"data-model"},{"id":"publishing-messages-ex0","language":"go","code":"func (h *RedisPubSubHub) Publish(ctx context.Context, channel string, message interface{}) (int64, error) {\n    payload, err := json.Marshal(message)\n    if err != nil {\n        return 0, err\n    }\n    delivered, err := h.client.Publish(ctx, channel, payload).Result()\n    if err != nil {\n        return 0, err\n    }\n    h.statsMu.Lock()\n    h.publishedTotal++\n    h.deliveredTotal += delivered\n    h.channelPublished[channel]++\n    h.statsMu.Unlock()\n    return delivered, nil\n}","section_id":"publishing-messages"},{"id":"subscribing-to-channels-ex0","language":"go","code":"func (h *RedisPubSubHub) Subscribe(ctx context.Context, name string, channels []string) (*Subscription, error) {\n    return h.register(ctx, name, channels, false)\n}","section_id":"subscribing-to-channels"},{"id":"subscribing-to-channels-ex1","language":"go","code":"var ps *redis.PubSub\nif isPattern {\n    ps = h.client.PSubscribe(ctx, targets...)\n} else {\n    ps = h.client.Subscribe(ctx, targets...)\n}\nsub := &Subscription{\n    name:      name,\n    targets:   targets,\n    isPattern: isPattern,\n    pubsub:    ps,\n    ch:        ps.Channel(),\n    // ...\n}\ngo sub.run()","section_id":"subscribing-to-channels"},{"id":"subscribing-to-channels-ex2","language":"go","code":"func (s *Subscription) dispatch(msg *redis.Message) {\n    var pattern *string\n    if msg.Pattern != \"\" {\n        p := msg.Pattern\n        pattern = &p\n    }\n    var payload interface{}\n    if err := json.Unmarshal([]byte(msg.Payload), &payload); err != nil {\n        payload = msg.Payload\n    }\n    wrapped := &ReceivedMessage{\n        Channel:      msg.Channel,\n        Pattern:      pattern,\n        Payload:      payload,\n        ReceivedAtMs: time.Now().UnixMilli(),\n    }\n    // ... prepend to bounded buffer, increment atomic counter ...\n}","section_id":"subscribing-to-channels"},{"id":"pattern-subscriptions-with-psubscribe-ex0","language":"go","code":"hub.PSubscribe(ctx, \"all-notifications\", []string{\"notifications:*\"})\nhub.PSubscribe(ctx, \"cache-invalidator\", []string{\"cache:invalidate:*\"})","section_id":"pattern-subscriptions-with-psubscribe"},{"id":"pattern-subscriptions-with-psubscribe-ex1","language":"go","code":"type ReceivedMessage struct {\n    Channel      string      `json:\"channel\"`\n    Pattern      *string     `json:\"pattern\"`\n    Payload      interface{} `json:\"payload\"`\n    ReceivedAtMs int64       `json:\"received_at_ms\"`\n}","section_id":"pattern-subscriptions-with-psubscribe"},{"id":"inspecting-active-subscribers-ex0","language":"go","code":"hub.ActiveChannels(ctx, \"*\")                                  // PUBSUB CHANNELS *\nhub.ChannelSubscriberCounts(ctx, []string{\"orders:new\", ...}) // PUBSUB NUMSUB ch1 ch2 ...\nhub.PatternSubscriberCount(ctx)                               // PUBSUB NUMPAT","section_id":"inspecting-active-subscribers"},{"id":"stats-and-history-ex0","language":"go","code":"func (h *RedisPubSubHub) Stats(ctx context.Context) Stats {\n    // ... snapshot counters under statsMu ...\n    subs := h.Subscriptions()\n    var received int64\n    for _, sub := range subs {\n        received += sub.ReceivedTotal()\n    }\n    patternSubs, _ := h.PatternSubscriberCount(ctx)\n    return Stats{\n        PublishedTotal:       published,\n        DeliveredTotal:       delivered,\n        ReceivedTotal:        received,\n        ActiveSubscriptions:  len(subs),\n        ChannelPublished:     perChannel,\n        PatternSubscriptions: patternSubs,\n    }\n}","section_id":"stats-and-history"},{"id":"prerequisites-ex0","language":"text","code":"require github.com/redis/go-redis/v9 v9.18.0","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir pub-sub-demo && cd pub-sub-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/pub-sub/go\ncurl -O $BASE/pubsub_hub.go\ncurl -O $BASE/demo_server.go\ncurl -O $BASE/go.mod\ncurl -O $BASE/go.sum","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"mkdir -p cmd/demo\ncat > cmd/demo/main.go <<'EOF'\npackage main\n\nimport \"pubsub\"\n\nfunc main() { pubsub.RunDemoServer() }\nEOF","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"bash","code":"go mod tidy\ngo run ./cmd/demo","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex2","language":"text","code":"Redis pub/sub demo server listening on http://127.0.0.1:8097\nUsing Redis at localhost:6379\nSeeded 3 default subscription(s)","section_id":"start-the-demo-server"},{"id":"inspect-pubsub-state-directly-in-redis-ex0","language":"bash","code":"# Which channels currently have at least one exact-match subscriber?\nredis-cli pubsub channels '*'\n\n# How many subscribers does each channel have?\nredis-cli pubsub numsub orders:new notifications:billing chat:lobby\n\n# How many active pattern subscriptions across the whole server?\nredis-cli pubsub numpat\n\n# Subscribe interactively from the CLI to watch traffic on a pattern\nredis-cli psubscribe 'orders:*'","section_id":"inspect-pubsub-state-directly-in-redis"}]}
{"schema_version":2,"id":"develop/use-cases/pub-sub","title":"Redis pub/sub messaging","url":"https://redis.io/docs/latest/develop/use-cases/pub-sub/","summary":"Broadcast real-time events to many consumers with Redis pub/sub.","tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-17T10:28:27+01:00","children":[{"id":"develop/use-cases/pub-sub/redis-py","summary":"Implement Redis pub/sub messaging in Python with redis-py","title":"Redis pub/sub with redis-py","url":"https://redis.io/docs/latest/develop/use-cases/pub-sub/redis-py/"},{"id":"develop/use-cases/pub-sub/nodejs","summary":"Implement Redis pub/sub messaging in Node.js with node-redis","title":"Redis pub/sub with node-redis","url":"https://redis.io/docs/latest/develop/use-cases/pub-sub/nodejs/"},{"id":"develop/use-cases/pub-sub/go","summary":"Implement Redis pub/sub messaging in Go with go-redis","title":"Redis pub/sub with go-redis","url":"https://redis.io/docs/latest/develop/use-cases/pub-sub/go/"},{"id":"develop/use-cases/pub-sub/java-jedis","summary":"Implement Redis pub/sub messaging in Java with Jedis","title":"Redis pub/sub with Jedis","url":"https://redis.io/docs/latest/develop/use-cases/pub-sub/java-jedis/"},{"id":"develop/use-cases/pub-sub/java-lettuce","summary":"Implement Redis pub/sub messaging in Java with Lettuce","title":"Redis pub/sub with Lettuce","url":"https://redis.io/docs/latest/develop/use-cases/pub-sub/java-lettuce/"},{"id":"develop/use-cases/pub-sub/dotnet","summary":"Implement Redis pub/sub messaging in C# with StackExchange.Redis","title":"Redis pub/sub with StackExchange.Redis","url":"https://redis.io/docs/latest/develop/use-cases/pub-sub/dotnet/"},{"id":"develop/use-cases/pub-sub/php","summary":"Implement Redis pub/sub messaging in PHP with Predis","title":"Redis pub/sub with Predis","url":"https://redis.io/docs/latest/develop/use-cases/pub-sub/php/"},{"id":"develop/use-cases/pub-sub/ruby","summary":"Implement Redis pub/sub messaging in Ruby with redis-rb","title":"Redis pub/sub with redis-rb","url":"https://redis.io/docs/latest/develop/use-cases/pub-sub/ruby/"},{"id":"develop/use-cases/pub-sub/rust","summary":"Implement Redis pub/sub messaging in Rust with redis-rs","title":"Redis pub/sub with redis-rs","url":"https://redis.io/docs/latest/develop/use-cases/pub-sub/rust/"}],"page_type":"content","content_hash":"e6064fa6802e9cf737daf7850ed9834f50cae7d8bc0617577c7452ce6c818091","sections":[{"id":"when-to-use-redis-pubsub","title":"When to use Redis pub/sub","role":"overview","text":"Use Redis pub/sub when you need to broadcast real-time events — notifications, chat messages, cache invalidation signals, UI updates — from one or more producers to many consumers without tight coupling."},{"id":"why-the-problem-is-hard","title":"Why the problem is hard","role":"content","text":"Point-to-point communication between services creates tight coupling that becomes brittle as\nthe number of producers and consumers grows. Each new subscriber means another integration.\nSome of the obvious workarounds have real drawbacks:\n\n-   **In-process event buses** don't work across instances or services — every new pod or worker\n    starts with an empty bus.\n-   **Full message brokers** (Kafka, RabbitMQ) add operational overhead and latency that's\n    overkill when you don't need message persistence, replay, or delivery guarantees.\n-   **Per-pair direct connections** (one publisher, one subscriber, hard-coded URLs) don't fan\n    out and don't survive instance churn — adding a fourth consumer means touching the producer.\n\nA workable broadcast layer needs sub-millisecond fan-out, no per-subscriber configuration on\nthe publisher side, and a way to route events through named topics so subscribers can pick what\nthey care about without coordinating with publishers.\n\nIf you also need persistence, replay, or at-least-once delivery, the answer is\n[Redis Streams](https://redis.io/docs/latest/develop/data-types/streams), not pub/sub — the two solve\ndifferent problems on the same infrastructure."},{"id":"what-you-can-expect-from-a-redis-solution","title":"What you can expect from a Redis solution","role":"content","text":"You can:\n\n-   Fan out events across microservices, pods, or edge nodes without direct service-to-service\n    calls.\n-   Broadcast cache invalidation to all application instances simultaneously.\n-   Push real-time updates to WebSocket clients across multiple server nodes (the\n    [Socket.IO](https://socket.io/) Redis adapter pattern).\n-   Signal user presence, typing indicators, or live dashboard updates with sub-millisecond\n    latency.\n-   Subscribe to flexible topic hierarchies with glob patterns — a single `orders:*`\n    subscription captures all order events without per-channel wiring.\n-   Keep durable state in regular Redis keys or external systems and use pub/sub purely as the\n    transport."},{"id":"how-redis-supports-the-solution","title":"How Redis supports the solution","role":"content","text":"In practice, a publisher calls `PUBLISH channel payload` and Redis fans the message out to every\nclient currently subscribed to that channel, in the order the messages were published.\nSubscribers register interest with `SUBSCRIBE channel` (exact-match) or `PSUBSCRIBE pattern`\n(glob-match), and the connection then switches into a subscribe-only mode that pushes incoming\nmessages over the same socket. Delivery is at-most-once: a subscriber that's offline when the\nmessage is published misses it for good.\n\nRedis provides the following features that make it a good fit for broadcast messaging:\n\n-   [`PUBLISH`](https://redis.io/docs/latest/commands/publish) for fan-out from any client to every active\n    subscriber of a channel, with a sub-millisecond hop through Redis.\n-   [`SUBSCRIBE`](https://redis.io/docs/latest/commands/subscribe) and\n    [`UNSUBSCRIBE`](https://redis.io/docs/latest/commands/unsubscribe) for exact-match channel\n    subscriptions, the simplest topic-based routing model.\n-   [`PSUBSCRIBE`](https://redis.io/docs/latest/commands/psubscribe) and\n    [`PUNSUBSCRIBE`](https://redis.io/docs/latest/commands/punsubscribe) for glob-style pattern\n    subscriptions (`cache:invalidate:*`, `news.*.headline`), so a subscriber can listen to whole\n    topic hierarchies without pre-registering every channel.\n-   [`PUBSUB CHANNELS`](https://redis.io/docs/latest/commands/pubsub-channels),\n    [`PUBSUB NUMSUB`](https://redis.io/docs/latest/commands/pubsub-numsub), and\n    [`PUBSUB NUMPAT`](https://redis.io/docs/latest/commands/pubsub-numpat) for introspection — list active\n    channels, count subscribers per channel, count active pattern subscriptions.\n-   [Sharded pub/sub](https://redis.io/docs/latest/develop/pubsub#sharded-pubsub)\n    ([`SSUBSCRIBE`](https://redis.io/docs/latest/commands/ssubscribe),\n    [`SPUBLISH`](https://redis.io/docs/latest/commands/spublish)) in Redis 7.0+ so the same pattern scales\n    horizontally on a Redis Cluster without every message touching every node.\n-   No message storage overhead — messages are delivered to active subscribers and discarded\n    immediately, keeping the messaging path stateless and fast.\n-   [Keyspace notifications](https://redis.io/docs/latest/develop/pubsub/keyspace-notifications) for\n    receiving events about key changes (`SET`, expiration, eviction) through the same pub/sub\n    transport."},{"id":"ecosystem","title":"Ecosystem","role":"content","text":"The following frameworks and libraries use Redis pub/sub for broadcast messaging:\n\n-   **Node.js**: [Socket.IO](https://socket.io/) Redis adapter for cross-node WebSocket fan-out\n-   **Python**: [`redis-py`](https://redis.readthedocs.io/) subscribers with\n    [FastAPI](https://redis.io/docs/latest/integrate/fastapi) or [Django Channels](https://channels.readthedocs.io/)\n    for WebSocket push and event listeners\n-   **Java**: [Spring Data Redis](https://spring.io/projects/spring-data-redis) message listener\n    containers for inter-service messaging\n-   **Ruby**: [Action Cable](https://guides.rubyonrails.org/action_cable_overview.html) Redis\n    adapter for Rails WebSocket broadcasting\n-   **Go**: [`go-redis`](https://github.com/redis/go-redis) `PubSub` for event listeners and\n    cluster-wide notifications\n-   **Infrastructure**: [Kong](https://konghq.com/) and [NGINX](https://www.nginx.com/) event\n    hooks; Kubernetes cluster-wide event bus via a shared Redis instance"},{"id":"code-examples-to-build-your-own-redis-pubsub-broadcaster","title":"Code examples to build your own Redis pub/sub broadcaster","role":"example","text":"The following guides show how to build a simple Redis-backed pub/sub broadcaster.\nEach guide includes a runnable interactive demo for each of the following client libraries:\n\n* [redis-py (Python)](https://redis.io/docs/latest/develop/use-cases/pub-sub/redis-py)\n* [node-redis (Node.js)](https://redis.io/docs/latest/develop/use-cases/pub-sub/nodejs)\n* [go-redis (Go)](https://redis.io/docs/latest/develop/use-cases/pub-sub/go)\n* [Jedis (Java)](https://redis.io/docs/latest/develop/use-cases/pub-sub/java-jedis)\n* [Lettuce (Java)](https://redis.io/docs/latest/develop/use-cases/pub-sub/java-lettuce)\n* [StackExchange.Redis (C#)](https://redis.io/docs/latest/develop/use-cases/pub-sub/dotnet)\n* [Predis (PHP)](https://redis.io/docs/latest/develop/use-cases/pub-sub/php)\n* [redis-rb (Ruby)](https://redis.io/docs/latest/develop/use-cases/pub-sub/ruby)\n* [redis-rs (Rust)](https://redis.io/docs/latest/develop/use-cases/pub-sub/rust)"}],"examples":[]}
{"schema_version":2,"id":"develop/use-cases/pub-sub/java-jedis","title":"Redis pub/sub with Jedis","url":"https://redis.io/docs/latest/develop/use-cases/pub-sub/java-jedis/","summary":"Implement Redis pub/sub messaging in Java with Jedis","aliases":["/develop/use-cases/pub-sub/java","/develop/use-cases/pub-sub/jedis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-17T10:28:27+01:00","children":[],"page_type":"content","content_hash":"315ec14445959955b36a045d5b9effd545c01c60ef55bc2acb2ca281c7118957","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis-backed pub/sub broadcaster in Java with [`Jedis`](https://redis.io/docs/latest/develop/clients/jedis). It includes a small local web server built with Java's built-in `HttpServer` so you can publish messages to named channels, add and remove subscribers live, and watch Redis fan out each message to every interested listener."},{"id":"overview","title":"Overview","role":"overview","text":"Pub/sub lets your application broadcast events — chat messages, cache invalidation signals, presence updates, notifications — to many consumers without per-pair wiring. The publisher names a *channel*; every client currently subscribed to that channel receives the message, in publish order, with sub-millisecond fan-out.\n\nThat gives you:\n\n* Many-to-many event delivery with no message storage cost in Redis\n* Exact-match subscriptions (`SUBSCRIBE orders:new`) for known topics\n* Pattern subscriptions (`PSUBSCRIBE notifications:*`) for whole topic hierarchies\n* Live server-side introspection through `PUBSUB CHANNELS`, `PUBSUB NUMSUB`, and `PUBSUB NUMPAT`\n* At-most-once delivery: subscribers that are offline when a message is published miss it, so durable state should live in keys or a Stream, not in the pub/sub channel itself\n\nIn this example, the publisher side calls `PUBLISH` with a JSON-encoded body and counts how many subscribers Redis reported delivering to. Each in-process subscriber owns its own `Jedis` connection from the pool and a dedicated worker thread that pumps incoming messages into a buffer."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. The application calls `hub.subscribe(name, channels)` or `hub.psubscribe(name, patterns)`\n2. The helper acquires a dedicated `Jedis` connection from the pool, builds a `JedisPubSub` listener, and starts a daemon thread that calls `jedis.subscribe(listener, channels...)` (or `psubscribe(...)`) — both calls block for the lifetime of the subscription\n3. The application (or another process) calls `hub.publish(channel, message)`\n4. Redis fans the message out over every subscribing client's open socket\n5. The listener's `onMessage` (or `onPMessage`) callback fires on the subscriber thread, wraps the raw message as a `ReceivedMessage`, and appends it to a per-subscriber ring buffer\n6. The publisher receives the integer subscriber count back from `PUBLISH`, which is the number of clients Redis delivered to right then\n\nPattern subscriptions match channels by glob (`*`, `?`, `[abc]`). A single message that matches both an exact subscription and a pattern subscription is delivered twice — once as an `onMessage` and once as an `onPMessage`."},{"id":"the-pubsub-hub-helper","title":"The pub/sub hub helper","role":"content","text":"The `RedisPubSubHub` class wraps the publish, subscribe, and introspection operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/pub-sub/java-jedis/RedisPubSubHub.java)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Pub/sub has no Redis keyspace footprint of its own — channels are server-side routing entries, not stored values. The hub keeps its own bookkeeping in process memory:\n\n[code example]\n\nThe implementation uses:\n\n* [`PUBLISH`](https://redis.io/docs/latest/commands/publish) to fan a JSON-encoded message out to every subscriber of a channel\n* [`SUBSCRIBE`](https://redis.io/docs/latest/commands/subscribe) for exact-match subscribers\n* [`PSUBSCRIBE`](https://redis.io/docs/latest/commands/psubscribe) for glob-style pattern subscribers\n* [`PUBSUB CHANNELS`](https://redis.io/docs/latest/commands/pubsub-channels) to list the channels with at least one active exact-match subscriber\n* [`PUBSUB NUMSUB`](https://redis.io/docs/latest/commands/pubsub-numsub) to count subscribers per channel\n* [`PUBSUB NUMPAT`](https://redis.io/docs/latest/commands/pubsub-numpat) to count active pattern subscriptions server-wide\n* Jedis' `JedisPubSub` listener subclass plus a dedicated daemon thread per subscriber so the blocking `subscribe()`/`psubscribe()` call does not freeze the rest of the application"},{"id":"publishing-messages","title":"Publishing messages","role":"content","text":"`publish()` JSON-encodes the message body, calls `PUBLISH`, and updates the per-channel publish counter:\n\n[code example]\n\nThe integer returned by `PUBLISH` is what Redis itself reports — the number of subscribers (direct and pattern) that received the message in that call. It's a useful sanity check that the channel name is actually being listened to: a steady stream of `0`s means you have a typo somewhere or your subscriber crashed."},{"id":"subscribing-to-channels","title":"Subscribing to channels","role":"content","text":"`subscribe()` creates a named in-process `Subscription` that owns its own `Jedis` connection and a dedicated worker thread:\n\n[code example]\n\nInside the `Subscription` constructor, the helper builds a `JedisPubSub` listener, starts the dispatch thread, and blocks until Redis has acknowledged every `SUBSCRIBE` so the very first publish can't race ahead:\n\n[code example]\n\nA few details matter here:\n\n* `jedis.subscribe(...)` and `jedis.psubscribe(...)` **block the calling thread** until the listener unsubscribes from every target. Running each subscription on its own thread is the only way to keep the rest of the application responsive — there is no \"run in background\" helper in Jedis.\n* Each `Subscription` gets its own `Jedis` connection from the pool. A connection that has issued `SUBSCRIBE` or `PSUBSCRIBE` is switched into subscribe-only mode, so other commands on the same connection would fail. Sharing one connection across subscribers would also couple their lifetimes — unsubscribing one would tear down the channel for the others.\n* The constructor waits on a `CountDownLatch` that's decremented once per target inside `onSubscribe` / `onPSubscribe`. Without it, the parent thread could return from `subscribe()` while the spawned worker thread is still about to send the SUBSCRIBE command — and a `PUBLISH` issued immediately afterwards would beat the subscription into Redis.\n* The `JedisPubSub` listener is the same object on both threads: it's safe to call `listener.unsubscribe()` from a different thread because Jedis ships the unsubscribe command back over the listener's own socket. After the unsubscribe round-trip completes, the blocking `subscribe()` call returns and the worker thread exits."},{"id":"pattern-subscriptions-with-psubscribe","title":"Pattern subscriptions with PSUBSCRIBE","role":"content","text":"`psubscribe()` works the same way but routes messages through `PSUBSCRIBE` so each binding is a glob, not a literal channel name:\n\n[code example]\n\nWhen a published channel matches a pattern, the `onPMessage` callback receives both the matched channel and the original pattern:\n\n[code example]\n\nThat distinction is useful for routing: a pattern subscriber can do one thing for the whole hierarchy (e.g., increment a counter) and dispatch on the specific channel within its callback (e.g., \"invalidate this region's cache\")."},{"id":"inspecting-active-subscribers","title":"Inspecting active subscribers","role":"content","text":"Redis exposes a small set of pub/sub introspection commands that report on subscriber state without traversing any keyspace:\n\n[code example]\n\n`PUBSUB CHANNELS` only reports channels with at least one exact-match subscriber — pattern subscribers do not appear here. That's a deliberate Redis design choice: a glob like `*` would otherwise show up as a subscriber to every conceivable channel. `PUBSUB NUMPAT` covers the pattern side as a single global count.\n\nEach of these commands acquires a regular (non-subscribe) `Jedis` connection from the pool with try-with-resources, so the bookkeeping calls never compete with the dedicated subscriber connections."},{"id":"stats-and-history","title":"Stats and history","role":"content","text":"`stats()` reports publish and receive counters plus the size of the subscription registry:\n\n[code example]\n\n`delivered_total` is what Redis itself counted; `received_total` is what this process's in-memory subscribers saw. In a single-process demo they should track each other closely — a sustained divergence usually means a callback raised, or a subscriber crashed while a publisher kept publishing. (Pub/sub is at-most-once: if your subscriber wasn't connected at publish time, the message is gone.)"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* Redis 6.2 or later running locally on the default port (6379). Earlier versions still work for plain `PUBLISH`/`SUBSCRIBE`; `PUBSUB NUMPAT` is older than that.\n* Java 17 or later.\n* Jedis 5.x on the classpath. The smallest workable classpath is the Jedis jar plus its two transitive dependencies, `commons-pool2` and `slf4j-api`.\n\nIf you use Maven:\n\n[code example]\n\nIf you use Gradle:\n\n[code example]"},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of three Java source files. Download them from the [`java-jedis` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/pub-sub/java-jedis) on GitHub, or grab them with `curl`:\n\n[code example]\n\nYou also need the three jars on the classpath. Grab them from [Maven Central](https://search.maven.org/) — search for `jedis`, `commons-pool2`, and `slf4j-api`, then download each jar from the artefact page — or copy them out of an existing local `~/.m2/repository` checkout if you've used these libraries before:\n\n* `jedis-5.1.2.jar`\n* `commons-pool2-2.12.0.jar`\n* `slf4j-api-2.0.13.jar`\n\n(Any 5.x Jedis release works; the transitive versions of `commons-pool2` and `slf4j-api` are pinned by the Jedis POM, so use the versions listed in the Jedis 5.1.2 [Maven page](https://search.maven.org/artifact/redis.clients/jedis/5.1.2/jar) if you want an exact match. Other recent patch versions of either library work fine.)"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"A local demo server is included to show the hub in action ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/pub-sub/java-jedis/DemoServer.java)). Compile and run with `javac` and `java`, listing each jar on the classpath:\n\n[code example]\n\nYou should see:\n\n[code example]\n\nOpen [http://127.0.0.1:8098](http://127.0.0.1:8098) in a browser. You can:\n\n* Publish messages of any text to any channel name in any batch size.\n* Add named subscribers that listen on either a specific channel (`orders:new`) or a glob pattern (`notifications:*`). A single subscriber can listen on multiple targets — enter them comma-separated.\n* Watch each subscriber's incoming-message panel update every 800 ms.\n* See the server-side view: `PUBSUB CHANNELS` lists exact-match channels with subscribers, `PUBSUB NUMSUB` gives per-channel counts, and `PUBSUB NUMPAT` counts active pattern subscriptions.\n* Click **Reset** to drop every subscription, zero the counters, and re-seed the three default subscribers.\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`."},{"id":"production-usage","title":"Production usage","role":"content","text":""},{"id":"pubsub-is-at-most-once--pair-it-with-durable-state-if-you-need-replay","title":"Pub/sub is at-most-once — pair it with durable state if you need replay","role":"content","text":"A subscriber that's offline when a message is published misses it permanently. For events you can't afford to lose, write the durable record (the order row, the cache key version, the audit log entry) to its primary store, then `PUBLISH` a notification so live consumers can pick it up immediately. On reconnect, consumers reconcile by reading the durable store, not by waiting for missed pub/sub messages. If you actually need replay or at-least-once delivery, switch to [Redis Streams](https://redis.io/docs/latest/develop/data-types/streams) with consumer groups."},{"id":"give-every-subscriber-its-own-jedis-connection","title":"Give every subscriber its own Jedis connection","role":"content","text":"A `Jedis` instance that has called `subscribe(...)` or `psubscribe(...)` is in subscribe-only mode: any other command on the same connection will fail. Always pull the subscriber's connection from the pool with `pool.getResource()` and dedicate it to that subscriber until it's torn down. The helper does this for you — every `Subscription` opens a fresh connection and owns it for the subscription's lifetime — but if you write your own subscriber code, the same rule applies.\n\nA related corollary: never let the pool size cap your subscriber count. If you have 100 long-lived subscribers in one process, `JedisPoolConfig.setMaxTotal(...)` needs to allow at least 100 connections for the subscribers plus whatever your publishers and bookkeeping calls need. Set it generously; idle subscriber connections cost almost nothing on the Redis side."},{"id":"unsubscribe-through-the-listener-not-the-connection","title":"Unsubscribe through the listener, not the connection","role":"content","text":"`jedis.subscribe(listener, channels...)` blocks until the listener calls `listener.unsubscribe()` (or `listener.punsubscribe()` for a pattern subscription). It's safe to call those methods from any thread: Jedis ships the unsubscribe command back over the listener's socket, Redis acknowledges, and then the blocking `subscribe()` call returns and the worker thread exits.\n\nClosing the underlying `Jedis` instance directly works as a fallback — the socket read errors out and the blocking call returns — but it's a less clean shutdown and produces a stack trace in the logs. The helper does both: it calls `listener.unsubscribe()` first, then closes the connection from the worker thread's `finally` block as a belt-and-braces guard."},{"id":"choose-a-topic-naming-convention-up-front","title":"Choose a topic naming convention up front","role":"content","text":"A flat namespace gets ugly fast — `email`, `email_high_priority`, `email_high_priority_billing`. Pick a colon-separated hierarchy (`notifications:billing:invoice`, `cache:invalidate:products:p-001`) so consumers can subscribe at the right level: a billing service uses `notifications:billing:*`, the audit logger uses `notifications:*`. Glob patterns are evaluated for every published message, so don't go wild with multiple wildcards on hot paths — `*:*:*` matches everything and costs more than a flat `notifications:*` would."},{"id":"dont-do-heavy-work-in-onmessage--onpmessage","title":"Don't do heavy work in `onMessage` / `onPMessage`","role":"content","text":"The `JedisPubSub` callbacks run on the subscriber thread that owns the connection. If a callback blocks (synchronous HTTP call, big computation, slow DB write), the next message waits behind it and the subscriber's effective throughput drops to whatever the callback's latency is. For heavier work, the callback should hand the message off to a worker pool (`Executors.newFixedThreadPool(...)`) or a queue, or — for true durable handoff — a [Redis Streams](https://redis.io/docs/latest/develop/data-types/streams) consumer group."},{"id":"tune-the-subscriber-buffer-for-your-traffic-shape","title":"Tune the subscriber buffer for your traffic shape","role":"content","text":"The demo caps each subscriber's in-memory message buffer at 50. That's right for showing the recent activity in a UI, but a real subscriber typically processes each message and discards it — the buffer is only there for human inspection. If you keep a buffer, make sure it's bounded; an unbounded ring on a chatty pattern subscriber will eventually OOM the process."},{"id":"sharded-pubsub-on-a-redis-cluster","title":"Sharded pub/sub on a Redis Cluster","role":"content","text":"On a Redis Cluster, plain `PUBLISH` fans every message out to every node via the cluster bus, which becomes a hotspot at high throughput. Redis 7.0 added [sharded pub/sub](https://redis.io/docs/latest/develop/pubsub#sharded-pubsub): channels are hashed to slots, and `SPUBLISH` / `SSUBSCRIBE` only touch the shard that owns the slot. Jedis 5.x exposes `jedis.spublish(...)` and a `JedisShardedPubSub` listener subclass. If you're scaling pub/sub on a cluster, prefer the sharded commands and pick channel names whose hash distribution matches your traffic."},{"id":"inspect-pubsub-state-directly-in-redis","title":"Inspect pub/sub state directly in Redis","role":"content","text":"Because pub/sub has no keyspace, `KEYS`/`SCAN` won't show you anything. Use the introspection commands instead:\n\n[code example]\n\n`redis-cli` in subscribe mode only exits with `Ctrl-C` — it can't issue any other commands while subscribed."},{"id":"learn-more","title":"Learn more","role":"related","text":"This example uses the following Redis commands:\n\n* [`PUBLISH`](https://redis.io/docs/latest/commands/publish) to fan a message out to every subscriber of a channel.\n* [`SUBSCRIBE`](https://redis.io/docs/latest/commands/subscribe) and [`UNSUBSCRIBE`](https://redis.io/docs/latest/commands/unsubscribe) for exact-match topic subscriptions.\n* [`PSUBSCRIBE`](https://redis.io/docs/latest/commands/psubscribe) and [`PUNSUBSCRIBE`](https://redis.io/docs/latest/commands/punsubscribe) for glob-style pattern subscriptions.\n* [`PUBSUB CHANNELS`](https://redis.io/docs/latest/commands/pubsub-channels) to list channels with at least one active exact-match subscriber.\n* [`PUBSUB NUMSUB`](https://redis.io/docs/latest/commands/pubsub-numsub) to count subscribers per named channel.\n* [`PUBSUB NUMPAT`](https://redis.io/docs/latest/commands/pubsub-numpat) to count active pattern subscriptions server-wide.\n\nSee the [Jedis documentation](https://redis.io/docs/latest/develop/clients/jedis) for full client reference, including the [`JedisPubSub` listener](https://github.com/redis/jedis/blob/master/src/main/java/redis/clients/jedis/JedisPubSub.java) abstract class."}],"examples":[{"id":"the-pubsub-hub-helper-ex0","language":"java","code":"import java.util.List;\nimport java.util.Map;\nimport redis.clients.jedis.JedisPool;\n\npublic class Main {\n    public static void main(String[] args) {\n        JedisPool pool = new JedisPool(\"localhost\", 6379);\n        RedisPubSubHub hub = new RedisPubSubHub(pool);\n\n        // Exact-match subscriber\n        hub.subscribe(\"orders-listener\", List.of(\"orders:new\"));\n\n        // Pattern subscriber covering an entire topic hierarchy\n        hub.psubscribe(\"all-notifications\", List.of(\"notifications:*\"));\n\n        // Publish — returns Redis' delivered count for this PUBLISH\n        int delivered = hub.publish(\"orders:new\", Map.of(\"order_id\", 42, \"total\", 199.0));\n        System.out.printf(\"Redis delivered to %d subscriber(s)%n\", delivered);\n\n        // Look at what each subscriber received\n        for (RedisPubSubHub.Subscription sub : hub.subscriptions()) {\n            System.out.printf(\"%s %d messages%n\", sub.name(), sub.receivedTotal());\n            for (RedisPubSubHub.ReceivedMessage m : sub.messages(5)) {\n                System.out.printf(\"   %s %s%n\", m.channel(), m.payload());\n            }\n        }\n\n        hub.unsubscribe(\"orders-listener\");\n        hub.shutdown();  // closes every remaining subscription\n    }\n}","section_id":"the-pubsub-hub-helper"},{"id":"data-model-ex0","language":"text","code":"RedisPubSubHub                          (in-process)\n  subscriptions             Map<String, Subscription>\n  publishedTotal            AtomicLong\n  deliveredTotal            AtomicLong\n  channelPublished          Map<String, Long>      (per-channel publish counts)\n\nSubscription                            (in-process, one per subscriber)\n  name                      String\n  targets                   List<String>            (channels or patterns)\n  isPattern                 boolean\n  buffer                    Deque<ReceivedMessage>  (capped, default 50)\n  received                  long\n  listener                  JedisPubSub             (subscribed on its own connection)\n  thread                    Thread                  (blocks inside jedis.subscribe())","section_id":"data-model"},{"id":"publishing-messages-ex0","language":"java","code":"public int publish(String channel, Object message) {\n    String payload = JsonUtil.toJson(message);\n    long delivered;\n    try (Jedis jedis = pool.getResource()) {\n        delivered = jedis.publish(channel, payload);\n    }\n    publishedTotal.incrementAndGet();\n    deliveredTotal.addAndGet(delivered);\n    channelPublished.merge(channel, 1L, Long::sum);\n    return (int) delivered;\n}","section_id":"publishing-messages"},{"id":"subscribing-to-channels-ex0","language":"java","code":"public Subscription subscribe(String name, List<String> channels) {\n    return register(name, channels, false);\n}","section_id":"subscribing-to-channels"},{"id":"subscribing-to-channels-ex1","language":"java","code":"this.jedis = pool.getResource();\n\nfinal CountDownLatch ackLatch = new CountDownLatch(targets.size());\n\nthis.listener = new JedisPubSub() {\n    @Override public void onMessage(String channel, String message) {\n        dispatch(channel, null, message);\n    }\n    @Override public void onPMessage(String pattern, String channel, String message) {\n        dispatch(channel, pattern, message);\n    }\n    @Override public void onSubscribe(String channel, int n) { ackLatch.countDown(); }\n    @Override public void onPSubscribe(String pattern, int n) { ackLatch.countDown(); }\n};\n\nfinal String[] targetArr = this.targets.toArray(new String[0]);\nthis.thread = new Thread(() -> {\n    if (isPattern) {\n        jedis.psubscribe(listener, targetArr);\n    } else {\n        jedis.subscribe(listener, targetArr);\n    }\n}, \"pubsub-\" + name);\nthis.thread.setDaemon(true);\nthis.thread.start();\n\nif (!ackLatch.await(2, TimeUnit.SECONDS)) {\n    throw new RuntimeException(\"subscribe acknowledgement did not arrive for '\" + name + \"'\");\n}","section_id":"subscribing-to-channels"},{"id":"pattern-subscriptions-with-psubscribe-ex0","language":"java","code":"hub.psubscribe(\"all-notifications\", List.of(\"notifications:*\"));\nhub.psubscribe(\"cache-invalidator\", List.of(\"cache:invalidate:*\"));","section_id":"pattern-subscriptions-with-psubscribe"},{"id":"pattern-subscriptions-with-psubscribe-ex1","language":"java","code":"@Override\npublic void onPMessage(String pattern, String channel, String message) {\n    dispatch(channel, pattern, message);\n}","section_id":"pattern-subscriptions-with-psubscribe"},{"id":"inspecting-active-subscribers-ex0","language":"java","code":"hub.activeChannels(\"*\");                  // PUBSUB CHANNELS *\nhub.channelSubscriberCounts(channels);    // PUBSUB NUMSUB ch1 ch2 ...\nhub.patternSubscriberCount();             // PUBSUB NUMPAT","section_id":"inspecting-active-subscribers"},{"id":"stats-and-history-ex0","language":"java","code":"public Map<String, Object> stats() {\n    List<Subscription> subs = subscriptions();\n    long receivedTotal = 0;\n    for (Subscription sub : subs) {\n        receivedTotal += sub.receivedTotal();\n    }\n    Map<String, Object> out = new LinkedHashMap<>();\n    out.put(\"published_total\", publishedTotal.get());\n    out.put(\"delivered_total\", deliveredTotal.get());          // sum of PUBLISH return values\n    out.put(\"received_total\", receivedTotal);\n    out.put(\"active_subscriptions\", (long) subs.size());\n    out.put(\"channel_published\", new LinkedHashMap<>(channelPublished));\n    out.put(\"pattern_subscriptions\", patternSubscriberCount());\n    return out;\n}","section_id":"stats-and-history"},{"id":"prerequisites-ex0","language":"xml","code":"<dependency>\n    <groupId>redis.clients</groupId>\n    <artifactId>jedis</artifactId>\n    <version>5.1.2</version>\n</dependency>","section_id":"prerequisites"},{"id":"prerequisites-ex1","language":"groovy","code":"implementation 'redis.clients:jedis:5.1.2'","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir pub-sub-demo && cd pub-sub-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/pub-sub/java-jedis\ncurl -O $BASE/RedisPubSubHub.java\ncurl -O $BASE/DemoServer.java\ncurl -O $BASE/JsonUtil.java","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"javac -cp jedis-5.1.2.jar:commons-pool2-2.12.0.jar:slf4j-api-2.0.13.jar \\\n      JsonUtil.java RedisPubSubHub.java DemoServer.java\n\njava  -cp .:jedis-5.1.2.jar:commons-pool2-2.12.0.jar:slf4j-api-2.0.13.jar \\\n      DemoServer --port 8098","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"Redis pub/sub demo server listening on http://127.0.0.1:8098\nUsing Redis at localhost:6379\nSeeded 3 default subscription(s)","section_id":"start-the-demo-server"},{"id":"inspect-pubsub-state-directly-in-redis-ex0","language":"bash","code":"# Which channels currently have at least one exact-match subscriber?\nredis-cli pubsub channels '*'\n\n# How many subscribers does each channel have?\nredis-cli pubsub numsub orders:new notifications:billing chat:lobby\n\n# How many active pattern subscriptions across the whole server?\nredis-cli pubsub numpat\n\n# Subscribe interactively from the CLI to watch traffic on a pattern\nredis-cli psubscribe 'orders:*'","section_id":"inspect-pubsub-state-directly-in-redis"}]}
{"schema_version":2,"id":"develop/use-cases/pub-sub/java-lettuce","title":"Redis pub/sub with Lettuce","url":"https://redis.io/docs/latest/develop/use-cases/pub-sub/java-lettuce/","summary":"Implement Redis pub/sub messaging in Java with Lettuce","aliases":["/develop/use-cases/pub-sub/lettuce"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-17T10:28:27+01:00","children":[],"page_type":"content","content_hash":"a5462855c43625f46a3b2de2643a27dfe3133135e584882921cde38d3f8f0839","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis-backed pub/sub broadcaster in Java with the [Lettuce](https://redis.io/docs/latest/develop/clients/lettuce) client library. It includes a small local web server built on the JDK's `com.sun.net.httpserver` so you can publish messages to named channels, add and remove subscribers live, and watch Redis fan out each message to every interested listener."},{"id":"overview","title":"Overview","role":"overview","text":"Pub/sub lets your application broadcast events — chat messages, cache invalidation signals, presence updates, notifications — to many consumers without per-pair wiring. The publisher names a *channel*; every client currently subscribed to that channel receives the message, in publish order, with sub-millisecond fan-out.\n\nThat gives you:\n\n* Many-to-many event delivery with no message storage cost in Redis\n* Exact-match subscriptions (`SUBSCRIBE orders:new`) for known topics\n* Pattern subscriptions (`PSUBSCRIBE notifications:*`) for whole topic hierarchies\n* Live server-side introspection through `PUBSUB CHANNELS`, `PUBSUB NUMSUB`, and `PUBSUB NUMPAT`\n* At-most-once delivery: subscribers that are offline when a message is published miss it, so durable state should live in keys or a Stream, not in the pub/sub channel itself\n\nIn this example, the publisher side calls `PUBLISH` with a JSON-encoded body and counts how many subscribers Redis reported delivering to. Each in-process subscriber owns its own pub/sub connection and a Lettuce listener that pumps incoming messages into a callback."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. The application calls `hub.subscribe(name, channels)` or `hub.psubscribe(name, patterns)`\n2. The helper opens a dedicated `StatefulRedisPubSubConnection`, attaches a `RedisPubSubListener`, and issues `SUBSCRIBE` or `PSUBSCRIBE` on it\n3. The application (or another process) calls `hub.publish(channel, message)`\n4. Redis fans the message out over every subscribing client's open socket\n5. Each subscriber's listener wraps the raw message as a `ReceivedMessage`, appends it to a per-subscriber ring buffer, and increments the received counter\n6. The publisher receives the integer subscriber count back from `PUBLISH`, which is the number of clients Redis delivered to right then\n\nPattern subscriptions match channels by glob (`*`, `?`, `[abc]`). A single message that matches both an exact subscription and a pattern subscription is delivered twice — once through `RedisPubSubListener.message(channel, msg)` and once through `RedisPubSubListener.message(pattern, channel, msg)`."},{"id":"the-pubsub-hub-helper","title":"The pub/sub hub helper","role":"content","text":"The `RedisPubSubHub` class wraps the publish, subscribe, and introspection operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/pub-sub/java-lettuce/RedisPubSubHub.java)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Pub/sub has no Redis keyspace footprint of its own — channels are server-side routing entries, not stored values. The hub keeps its own bookkeeping in process memory:\n\n[code example]\n\nThe implementation uses:\n\n* [`PUBLISH`](https://redis.io/docs/latest/commands/publish) to fan a JSON-encoded message out to every subscriber of a channel\n* [`SUBSCRIBE`](https://redis.io/docs/latest/commands/subscribe) for exact-match subscribers\n* [`PSUBSCRIBE`](https://redis.io/docs/latest/commands/psubscribe) for glob-style pattern subscribers\n* [`PUBSUB CHANNELS`](https://redis.io/docs/latest/commands/pubsub-channels) to list the channels with at least one active exact-match subscriber\n* [`PUBSUB NUMSUB`](https://redis.io/docs/latest/commands/pubsub-numsub) to count subscribers per channel\n* [`PUBSUB NUMPAT`](https://redis.io/docs/latest/commands/pubsub-numpat) to count active pattern subscriptions server-wide\n* Lettuce's `RedisPubSubListener` interface to dispatch messages without writing a thread loop by hand — messages are delivered on a Netty event-loop thread"},{"id":"publishing-messages","title":"Publishing messages","role":"content","text":"`publish()` JSON-encodes the message body, calls `PUBLISH`, and updates the per-channel publish counter:\n\n[code example]\n\nThe integer returned by `PUBLISH` is what Redis itself reports — the number of subscribers (direct and pattern) that received the message in that call. It's a useful sanity check that the channel name is actually being listened to: a steady stream of `0`s means you have a typo somewhere or your subscriber crashed.\n\nPublishing reuses the shared `StatefulRedisConnection`, which is thread-safe for individual commands, so concurrent HTTP handlers can publish without coordination."},{"id":"subscribing-to-channels","title":"Subscribing to channels","role":"content","text":"`subscribe()` creates a named in-process `Subscription` that owns its own pub/sub connection:\n\n[code example]\n\nInside the `Subscription` constructor, the helper opens a dedicated pub/sub connection, registers a listener, then issues `SUBSCRIBE`:\n\n[code example]\n\nA few details matter here:\n\n* Each `Subscription` gets its own `StatefulRedisPubSubConnection` (and therefore its own Redis socket). Lettuce switches a pub/sub connection into subscribe-only mode after the first `SUBSCRIBE` or `PSUBSCRIBE`, so sharing one across business-logic subscribers would couple their lifetimes — closing one would close the channel for the others.\n* `RedisPubSubListener` overloads `message()` twice: the two-argument form for plain `SUBSCRIBE` deliveries and the three-argument form for `PSUBSCRIBE` deliveries that carry the originating pattern. The helper routes both into a single `record()` method that writes into a bounded ring buffer.\n* Messages arrive on a Netty event-loop thread, not on the calling HTTP handler thread. The buffer is guarded with a `synchronized` block so a polling `/state` request can read it safely while a publish is in flight.\n* The constructor uses Lettuce's `sync()` API (not `async()`) so it blocks until Redis acknowledges the SUBSCRIBE for every channel before the `Subscription` is handed back to the caller. With the unawaited `async()` variant, a `PUBLISH` issued immediately after subscribing could race ahead of the acknowledgement and the first message would be lost."},{"id":"pattern-subscriptions-with-psubscribe","title":"Pattern subscriptions with PSUBSCRIBE","role":"content","text":"`psubscribe()` works the same way but routes messages through `PSUBSCRIBE` so each binding is a glob, not a literal channel name:\n\n[code example]\n\nWhen a published channel matches a pattern, Lettuce dispatches through the three-argument `message(String pattern, String channel, String body)` overload and the helper records both the matched channel and the original pattern:\n\n[code example]\n\nThat distinction is useful for routing: a pattern subscriber can do one thing for the whole hierarchy (e.g., increment a counter) and dispatch on the specific channel within its callback (e.g., \"invalidate this region's cache\")."},{"id":"inspecting-active-subscribers","title":"Inspecting active subscribers","role":"content","text":"Redis exposes a small set of pub/sub introspection commands that report on subscriber state without traversing any keyspace:\n\n[code example]\n\nLettuce exposes these as ordinary commands on `RedisCommands`: `pubsubChannels(pattern)`, `pubsubNumsub(channels...)`, and `pubsubNumpat()`. The helper runs each one against the shared `StatefulRedisConnection`.\n\n`PUBSUB CHANNELS` only reports channels with at least one exact-match subscriber — pattern subscribers do not appear here. That's a deliberate Redis design choice: a glob like `*` would otherwise show up as a subscriber to every conceivable channel. `PUBSUB NUMPAT` covers the pattern side as a single global count."},{"id":"stats-and-history","title":"Stats and history","role":"content","text":"`stats()` reports publish and receive counters plus the size of the subscription registry:\n\n[code example]\n\n`delivered_total` is what Redis itself counted; `received_total` is what this process's in-memory subscribers saw. In a single-process demo they should track each other closely — a sustained divergence usually means a listener threw, or a subscriber's pub/sub connection dropped while a publisher kept publishing. (Pub/sub is at-most-once: if your subscriber wasn't connected at publish time, the message is gone.)"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* Redis 6.2 or later running locally on the default port (6379). Earlier versions still work for plain `PUBLISH`/`SUBSCRIBE`; `PUBSUB NUMPAT` is older than that.\n* JDK 11 or later.\n* The Lettuce JAR (6.1 or later) and its Netty + Reactor dependencies on your classpath. Get them from [Maven Central](https://repo1.maven.org/maven2/io/lettuce/lettuce-core/), or via Maven/Gradle in a project setup."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of two Java files. Download them from the [`java-lettuce` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/pub-sub/java-lettuce) on GitHub, or grab them with `curl`:\n\n[code example]\n\nYou also need a `lib/` directory containing the Lettuce client and its runtime dependencies — at minimum:\n\n* `lettuce-core-6.x.jar`\n* `netty-buffer-4.1.x.jar`, `netty-codec-4.1.x.jar`, `netty-common-4.1.x.jar`, `netty-handler-4.1.x.jar`, `netty-resolver-4.1.x.jar`, `netty-transport-4.1.x.jar`, `netty-transport-native-unix-common-4.1.x.jar`\n* `reactor-core-3.x.jar`, `reactive-streams-1.0.x.jar`\n\nThe simplest way to collect them is with Maven. Create a minimal `pom.xml` declaring `io.lettuce:lettuce-core` as the only dependency, then run `mvn dependency:copy-dependencies -DoutputDirectory=lib`."},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From the directory containing `RedisPubSubHub.java`, `DemoServer.java`, and `lib/`:\n\n[code example]\n\nYou should see:\n\n[code example]\n\nOpen [http://127.0.0.1:8099](http://127.0.0.1:8099) in a browser. You can:\n\n* Publish messages of any text to any channel name in any batch size.\n* Add named subscribers that listen on either a specific channel (`orders:new`) or a glob pattern (`notifications:*`). A single subscriber can listen on multiple targets — enter them comma-separated.\n* Watch each subscriber's incoming-message panel update every 800 ms.\n* See the server-side view: `PUBSUB CHANNELS` lists exact-match channels with subscribers, `PUBSUB NUMSUB` gives per-channel counts, and `PUBSUB NUMPAT` counts active pattern subscriptions.\n* Click **Reset** to drop every subscription, zero the counters, and re-seed the three default subscribers.\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`."},{"id":"production-usage","title":"Production usage","role":"content","text":""},{"id":"pubsub-is-at-most-once--pair-it-with-durable-state-if-you-need-replay","title":"Pub/sub is at-most-once — pair it with durable state if you need replay","role":"content","text":"A subscriber that's offline when a message is published misses it permanently. For events you can't afford to lose, write the durable record (the order row, the cache key version, the audit log entry) to its primary store, then `PUBLISH` a notification so live consumers can pick it up immediately. On reconnect, consumers reconcile by reading the durable store, not by waiting for missed pub/sub messages. If you actually need replay or at-least-once delivery, switch to [Redis Streams](https://redis.io/docs/latest/develop/data-types/streams) with consumer groups."},{"id":"use-a-separate-statefulredispubsubconnection-per-subscriber","title":"Use a separate `StatefulRedisPubSubConnection` per subscriber","role":"content","text":"Lettuce switches a pub/sub connection into subscribe-only mode after `SUBSCRIBE` or `PSUBSCRIBE`: ordinary commands (`GET`, `HSET`, etc.) on that connection will block or fail. Always create the pub/sub connection from `RedisClient.connectPubSub()` — separate from the connection you use for `PUBLISH` and other commands — and give every business-logic subscriber its own. Sharing one pub/sub connection across subscribers couples their lifetimes (closing one closes the channel for the others) and serialises listener invocations on a single Netty event-loop thread."},{"id":"dont-do-heavy-work-inside-the-listener-callback","title":"Don't do heavy work inside the listener callback","role":"content","text":"`RedisPubSubListener.message()` runs on a Netty event-loop thread. If your listener blocks (synchronous HTTP call, big computation, slow JDBC write), it parks an I/O thread that should be handling other connections, and the next message on the same socket waits behind it. For heavier work, the listener should hand the message off to an `ExecutorService` worker pool, a `BlockingQueue`, or — for true durable handoff — a [Redis Streams](https://redis.io/docs/latest/develop/data-types/streams) consumer group."},{"id":"choose-a-topic-naming-convention-up-front","title":"Choose a topic naming convention up front","role":"content","text":"A flat namespace gets ugly fast — `email`, `email_high_priority`, `email_high_priority_billing`. Pick a colon-separated hierarchy (`notifications:billing:invoice`, `cache:invalidate:products:p-001`) so consumers can subscribe at the right level: a billing service uses `notifications:billing:*`, the audit logger uses `notifications:*`. Glob patterns are evaluated for every published message, so don't go wild with multiple wildcards on hot paths — `*:*:*` matches everything and costs more than a flat `notifications:*` would."},{"id":"tune-the-subscriber-buffer-for-your-traffic-shape","title":"Tune the subscriber buffer for your traffic shape","role":"content","text":"The demo caps each subscriber's in-memory message buffer at 50 entries. That's right for showing the recent activity in a UI, but a real subscriber typically processes each message and discards it — the buffer is only there for human inspection. If you keep a buffer, make sure it's bounded; an unbounded ring on a chatty pattern subscriber will eventually OOM the process."},{"id":"sharded-pubsub-on-a-redis-cluster","title":"Sharded pub/sub on a Redis Cluster","role":"content","text":"On a Redis Cluster, plain `PUBLISH` fans every message out to every node via the cluster bus, which becomes a hotspot at high throughput. Redis 7.0 added [sharded pub/sub](https://redis.io/docs/latest/develop/pubsub#sharded-pubsub): channels are hashed to slots, and `SPUBLISH` / `SSUBSCRIBE` only touch the shard that owns the slot. Lettuce exposes these as `spublish()` and `ssubscribe()` on the cluster connection. If you're scaling pub/sub on a cluster, prefer the sharded commands and pick channel names whose hash distribution matches your traffic."},{"id":"inspect-pubsub-state-directly-in-redis","title":"Inspect pub/sub state directly in Redis","role":"content","text":"Because pub/sub has no keyspace, `KEYS`/`SCAN` won't show you anything. Use the introspection commands instead:\n\n[code example]\n\n`redis-cli` in subscribe mode only exits with `Ctrl-C` — it can't issue any other commands while subscribed."},{"id":"learn-more","title":"Learn more","role":"related","text":"This example uses the following Redis commands:\n\n* [`PUBLISH`](https://redis.io/docs/latest/commands/publish) to fan a message out to every subscriber of a channel.\n* [`SUBSCRIBE`](https://redis.io/docs/latest/commands/subscribe) and [`UNSUBSCRIBE`](https://redis.io/docs/latest/commands/unsubscribe) for exact-match topic subscriptions.\n* [`PSUBSCRIBE`](https://redis.io/docs/latest/commands/psubscribe) and [`PUNSUBSCRIBE`](https://redis.io/docs/latest/commands/punsubscribe) for glob-style pattern subscriptions.\n* [`PUBSUB CHANNELS`](https://redis.io/docs/latest/commands/pubsub-channels) to list channels with at least one active exact-match subscriber.\n* [`PUBSUB NUMSUB`](https://redis.io/docs/latest/commands/pubsub-numsub) to count subscribers per named channel.\n* [`PUBSUB NUMPAT`](https://redis.io/docs/latest/commands/pubsub-numpat) to count active pattern subscriptions server-wide.\n\nSee the [Lettuce guide](https://redis.io/docs/latest/develop/clients/lettuce) for full client reference, including the `StatefulRedisPubSubConnection` and `RedisPubSubListener` types used in this example."}],"examples":[{"id":"the-pubsub-hub-helper-ex0","language":"java","code":"import io.lettuce.core.RedisClient;\nimport io.lettuce.core.RedisURI;\nimport io.lettuce.core.api.StatefulRedisConnection;\n\nimport java.util.Arrays;\n\nRedisClient client = RedisClient.create(\n    RedisURI.builder().withHost(\"localhost\").withPort(6379).build());\nStatefulRedisConnection<String, String> connection = client.connect();\nRedisPubSubHub hub = new RedisPubSubHub(client, connection);\n\n// Exact-match subscriber.\nhub.subscribe(\"orders-listener\", Arrays.asList(\"orders:new\"));\n\n// Pattern subscriber covering an entire topic hierarchy.\nhub.psubscribe(\"all-notifications\", Arrays.asList(\"notifications:*\"));\n\n// Publish — returns Redis' delivered count for this PUBLISH.\njava.util.Map<String, Object> payload = new java.util.LinkedHashMap<>();\npayload.put(\"order_id\", 42);\npayload.put(\"total\", 199.0);\nint delivered = hub.publish(\"orders:new\", payload);\nSystem.out.printf(\"Redis delivered to %d subscriber(s)%n\", delivered);\n\n// Look at what each subscriber received.\nfor (RedisPubSubHub.Subscription sub : hub.subscriptions()) {\n    System.out.println(sub.name() + \" \" + sub.receivedTotal() + \" messages\");\n    for (RedisPubSubHub.ReceivedMessage m : sub.messages(5)) {\n        System.out.println(\"  \" + m.channel + \" \" + m.payload);\n    }\n}\n\nhub.unsubscribe(\"orders-listener\");\nhub.shutdown();  // closes every remaining subscription\nconnection.close();\nclient.shutdown();","section_id":"the-pubsub-hub-helper"},{"id":"data-model-ex0","language":"text","code":"RedisPubSubHub                                    (in-process)\n  subscriptions             Map<String, Subscription>\n  publishedTotal            AtomicLong\n  deliveredTotal            AtomicLong\n  channelPublished          Map<channel, Long>\n\nSubscription                                      (in-process, one per subscriber)\n  name                      String\n  targets                   List<channel | pattern>\n  isPattern                 boolean\n  buffer                    Deque<ReceivedMessage>      (capped, default 50)\n  received                  long\n  psConnection              StatefulRedisPubSubConnection<String, String>\n  listener                  RedisPubSubListener<String, String>","section_id":"data-model"},{"id":"publishing-messages-ex0","language":"java","code":"public int publish(String channel, Object message) {\n    String payload = JsonCodec.encode(message);\n    long delivered = connection.sync().publish(channel, payload);\n    publishedTotal.incrementAndGet();\n    deliveredTotal.addAndGet(delivered);\n    channelPublished.merge(channel, 1L, Long::sum);\n    return (int) delivered;\n}","section_id":"publishing-messages"},{"id":"subscribing-to-channels-ex0","language":"java","code":"public Subscription subscribe(String name, List<String> channels) {\n    return register(name, channels, false);\n}","section_id":"subscribing-to-channels"},{"id":"subscribing-to-channels-ex1","language":"java","code":"this.psConnection = client.connectPubSub();\n\nthis.psConnection.addListener(new RedisPubSubListener<String, String>() {\n    @Override public void message(String channel, String msg) {\n        record(channel, null, msg);\n    }\n    @Override public void message(String pattern, String channel, String msg) {\n        record(channel, pattern, msg);\n    }\n    @Override public void subscribed(String channel, long count) {}\n    @Override public void psubscribed(String pattern, long count) {}\n    @Override public void unsubscribed(String channel, long count) {}\n    @Override public void punsubscribed(String pattern, long count) {}\n});\n\nif (isPattern) {\n    psConnection.sync().psubscribe(targets.toArray(new String[0]));\n} else {\n    psConnection.sync().subscribe(targets.toArray(new String[0]));\n}","section_id":"subscribing-to-channels"},{"id":"pattern-subscriptions-with-psubscribe-ex0","language":"java","code":"hub.psubscribe(\"all-notifications\", Arrays.asList(\"notifications:*\"));\nhub.psubscribe(\"cache-invalidator\", Arrays.asList(\"cache:invalidate:*\"));","section_id":"pattern-subscriptions-with-psubscribe"},{"id":"pattern-subscriptions-with-psubscribe-ex1","language":"java","code":"@Override\npublic void message(String pattern, String channel, String msg) {\n    record(channel, pattern, msg);  // exact subscriptions pass null for pattern\n}","section_id":"pattern-subscriptions-with-psubscribe"},{"id":"inspecting-active-subscribers-ex0","language":"java","code":"hub.activeChannels(\"*\");                                            // PUBSUB CHANNELS *\nhub.channelSubscriberCounts(Arrays.asList(\"orders:new\"));            // PUBSUB NUMSUB ...\nhub.patternSubscriberCount();                                        // PUBSUB NUMPAT","section_id":"inspecting-active-subscribers"},{"id":"stats-and-history-ex0","language":"java","code":"public Map<String, Object> stats() {\n    List<Subscription> subs = subscriptions();\n    long received = 0;\n    for (Subscription sub : subs) received += sub.receivedTotal();\n    Map<String, Object> out = new LinkedHashMap<>();\n    out.put(\"published_total\", publishedTotal.get());\n    out.put(\"delivered_total\", deliveredTotal.get());           // sum of PUBLISH return values\n    out.put(\"received_total\", received);\n    out.put(\"active_subscriptions\", (long) subs.size());\n    out.put(\"channel_published\", new LinkedHashMap<>(channelPublished));\n    out.put(\"pattern_subscriptions\", patternSubscriberCount());\n    return out;\n}","section_id":"stats-and-history"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir pub-sub-demo && cd pub-sub-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/pub-sub/java-lettuce\ncurl -O $BASE/RedisPubSubHub.java\ncurl -O $BASE/DemoServer.java","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"javac -cp 'lib/*' RedisPubSubHub.java DemoServer.java\njava -cp '.:lib/*' DemoServer --port 8099 --redis-host localhost --redis-port 6379","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"Redis pub/sub demo server listening on http://127.0.0.1:8099\nUsing Redis at localhost:6379\nSeeded 3 default subscription(s)","section_id":"start-the-demo-server"},{"id":"inspect-pubsub-state-directly-in-redis-ex0","language":"bash","code":"# Which channels currently have at least one exact-match subscriber?\nredis-cli pubsub channels '*'\n\n# How many subscribers does each channel have?\nredis-cli pubsub numsub orders:new notifications:billing chat:lobby\n\n# How many active pattern subscriptions across the whole server?\nredis-cli pubsub numpat\n\n# Subscribe interactively from the CLI to watch traffic on a pattern\nredis-cli psubscribe 'orders:*'","section_id":"inspect-pubsub-state-directly-in-redis"}]}
{"schema_version":2,"id":"develop/use-cases/pub-sub/nodejs","title":"Redis pub/sub with node-redis","url":"https://redis.io/docs/latest/develop/use-cases/pub-sub/nodejs/","summary":"Implement Redis pub/sub messaging in Node.js with node-redis","aliases":["/develop/use-cases/pub-sub/node-redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-17T10:28:27+01:00","children":[],"page_type":"content","content_hash":"e1f1f34e7472f14484ec9dbc40767f4983f707330d9443e125737dee059d6451","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis-backed pub/sub broadcaster in Node.js with [`node-redis`](https://redis.io/docs/latest/develop/clients/nodejs). It includes a small local web server built with the Node.js standard `http` module so you can publish messages to named channels, add and remove subscribers live, and watch Redis fan out each message to every interested listener."},{"id":"overview","title":"Overview","role":"overview","text":"Pub/sub lets your application broadcast events — chat messages, cache invalidation signals, presence updates, notifications — to many consumers without per-pair wiring. The publisher names a *channel*; every client currently subscribed to that channel receives the message, in publish order, with sub-millisecond fan-out.\n\nThat gives you:\n\n* Many-to-many event delivery with no message storage cost in Redis\n* Exact-match subscriptions (`SUBSCRIBE orders:new`) for known topics\n* Pattern subscriptions (`PSUBSCRIBE notifications:*`) for whole topic hierarchies\n* Live server-side introspection through `PUBSUB CHANNELS`, `PUBSUB NUMSUB`, and `PUBSUB NUMPAT`\n* At-most-once delivery: subscribers that are offline when a message is published miss it, so durable state should live in keys or a Stream, not in the pub/sub channel itself\n\nIn this example, the publisher side calls `PUBLISH` with a JSON-encoded body and counts how many subscribers Redis reported delivering to. Each in-process subscriber owns its own Redis connection (obtained via `client.duplicate()`) and a listener callback that handles every delivered message."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. The application calls `hub.subscribe(name, channels)` or `hub.psubscribe(name, patterns)`\n2. The helper duplicates the hub's Redis client to get a dedicated subscribe-mode connection, then binds each target to a dispatch callback via `client.subscribe(channel, listener)` or `client.pSubscribe(pattern, listener)`\n3. The application (or another process) calls `hub.publish(channel, message)`\n4. Redis fans the message out over every subscribing client's open socket\n5. Each subscriber's listener wraps the raw message as a `ReceivedMessage`, appends it to a per-subscriber ring buffer, and invokes the optional callback\n6. The publisher receives the integer subscriber count back from `PUBLISH`, which is the number of clients Redis delivered to right then\n\nPattern subscriptions match channels by glob (`*`, `?`, `[abc]`). A single message that matches both an exact subscription and a pattern subscription is delivered twice — once to the exact subscriber and once to the pattern subscriber."},{"id":"the-pubsub-hub-helper","title":"The pub/sub hub helper","role":"content","text":"The `RedisPubSubHub` class wraps the publish, subscribe, and introspection operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/pub-sub/nodejs/pubsub_hub.js)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Pub/sub has no Redis keyspace footprint of its own — channels are server-side routing entries, not stored values. The hub keeps its own bookkeeping in process memory:\n\n[code example]\n\nThe implementation uses:\n\n* [`PUBLISH`](https://redis.io/docs/latest/commands/publish) to fan a JSON-encoded message out to every subscriber of a channel\n* [`SUBSCRIBE`](https://redis.io/docs/latest/commands/subscribe) for exact-match subscribers\n* [`PSUBSCRIBE`](https://redis.io/docs/latest/commands/psubscribe) for glob-style pattern subscribers\n* [`PUBSUB CHANNELS`](https://redis.io/docs/latest/commands/pubsub-channels) to list the channels with at least one active exact-match subscriber\n* [`PUBSUB NUMSUB`](https://redis.io/docs/latest/commands/pubsub-numsub) to count subscribers per channel\n* [`PUBSUB NUMPAT`](https://redis.io/docs/latest/commands/pubsub-numpat) to count active pattern subscriptions server-wide\n* node-redis 5.x's listener-based `subscribe` / `pSubscribe` API to dispatch messages without writing a polling loop by hand"},{"id":"publishing-messages","title":"Publishing messages","role":"content","text":"`publish()` JSON-encodes the message body, calls `PUBLISH`, and updates the per-channel publish counter:\n\n[code example]\n\nThe integer returned by `PUBLISH` is what Redis itself reports — the number of subscribers (direct and pattern) that received the message in that call. It's a useful sanity check that the channel name is actually being listened to: a steady stream of `0`s means you have a typo somewhere or your subscriber crashed."},{"id":"subscribing-to-channels","title":"Subscribing to channels","role":"content","text":"`subscribe()` creates a named in-process `Subscription` that owns its own connection and listener:\n\n[code example]\n\nInside `Subscription._connect`, the helper duplicates the hub's client to get a dedicated connection and registers a listener for each target:\n\n[code example]\n\nA few details matter here:\n\n* **Each `Subscription` owns its own connection.** In node-redis 5.x, a client used for `subscribe` / `pSubscribe` is in subscribe-only mode for the lifetime of those bindings — regular commands (`GET`, `SET`, …) on the same client will throw. The hub keeps one regular client for `PUBLISH` and the `PUBSUB *` introspection commands; every subscription duplicates it to get a fresh socket.\n* **The listener receives `(message, channel)` already decoded as strings.** Pass `bufferMode = true` as the third argument if you need raw `Buffer`s instead — the demo doesn't.\n* **Closing one subscription doesn't affect another.** Each `Subscription.close()` unsubscribes its own connection and then `quit()`s it; the hub's regular client and every other subscription keep going."},{"id":"pattern-subscriptions-with-psubscribe","title":"Pattern subscriptions with PSUBSCRIBE","role":"content","text":"`psubscribe()` works the same way but routes messages through `PSUBSCRIBE` so each binding is a glob, not a literal channel name:\n\n[code example]\n\nFor pattern subscriptions the dispatch callback needs to know both the matched channel and the originating pattern. node-redis only delivers `(message, channel)` to the listener, so the helper binds the pattern explicitly via a closure:\n\n[code example]\n\nThat distinction is useful for routing: a pattern subscriber can do one thing for the whole hierarchy (e.g., increment a counter) and dispatch on the specific channel within its callback (e.g., \"invalidate this region's cache\")."},{"id":"inspecting-active-subscribers","title":"Inspecting active subscribers","role":"content","text":"Redis exposes a small set of pub/sub introspection commands that report on subscriber state without traversing any keyspace:\n\n[code example]\n\n`PUBSUB CHANNELS` only reports channels with at least one exact-match subscriber — pattern subscribers do not appear here. That's a deliberate Redis design choice: a glob like `*` would otherwise show up as a subscriber to every conceivable channel. `PUBSUB NUMPAT` covers the pattern side as a single global count."},{"id":"stats-and-history","title":"Stats and history","role":"content","text":"`stats()` reports publish and receive counters plus the size of the subscription registry:\n\n[code example]\n\n`delivered_total` is what Redis itself counted; `received_total` is what this process's in-memory subscribers saw. In a single-process demo they should track each other closely — a sustained divergence usually means a listener threw, or a subscriber crashed while a publisher kept publishing. (Pub/sub is at-most-once: if your subscriber wasn't connected at publish time, the message is gone.)"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* Redis 6.2 or later running locally on the default port (6379). Earlier versions still work for plain `PUBLISH`/`SUBSCRIBE`; `PUBSUB NUMPAT` is older than that.\n* Node.js 18 or later.\n* The [`node-redis`](https://github.com/redis/node-redis) client at version 5.x. The demo's `package.json` declares `\"redis\": \"^5.0.0\"`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of three files. Download them from the [`nodejs` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/pub-sub/nodejs) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From that directory:\n\n[code example]\n\nYou should see:\n\n[code example]\n\nOpen [http://127.0.0.1:8096](http://127.0.0.1:8096) in a browser. You can:\n\n* Publish messages of any text to any channel name in any batch size.\n* Add named subscribers that listen on either a specific channel (`orders:new`) or a glob pattern (`notifications:*`). A single subscriber can listen on multiple targets — enter them comma-separated.\n* Watch each subscriber's incoming-message panel update every 800 ms.\n* See the server-side view: `PUBSUB CHANNELS` lists exact-match channels with subscribers, `PUBSUB NUMSUB` gives per-channel counts, and `PUBSUB NUMPAT` counts active pattern subscriptions.\n* Click **Reset** to drop every subscription, zero the counters, and re-seed the three default subscribers.\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`. Use `--port` to change the HTTP listen port."},{"id":"production-usage","title":"Production usage","role":"content","text":""},{"id":"pubsub-is-at-most-once--pair-it-with-durable-state-if-you-need-replay","title":"Pub/sub is at-most-once — pair it with durable state if you need replay","role":"content","text":"A subscriber that's offline when a message is published misses it permanently. For events you can't afford to lose, write the durable record (the order row, the cache key version, the audit log entry) to its primary store, then `PUBLISH` a notification so live consumers can pick it up immediately. On reconnect, consumers reconcile by reading the durable store, not by waiting for missed pub/sub messages. If you actually need replay or at-least-once delivery, switch to [Redis Streams](https://redis.io/docs/latest/develop/data-types/streams) with consumer groups."},{"id":"use-a-dedicated-connection-per-subscriber","title":"Use a dedicated connection per subscriber","role":"content","text":"A node-redis client used for `subscribe` / `pSubscribe` is in subscribe-only mode for the lifetime of those bindings: ordinary commands (`GET`, `HSET`, …) on the same client throw. Always duplicate the client — as the helper does with `client.duplicate()` — so each subscriber has its own socket. Sharing one subscriber socket across unrelated callers couples their lifetimes (unsubscribing one drops the channel for the others) and serialises every listener on a single connection's event loop."},{"id":"choose-a-topic-naming-convention-up-front","title":"Choose a topic naming convention up front","role":"content","text":"A flat namespace gets ugly fast — `email`, `email_high_priority`, `email_high_priority_billing`. Pick a colon-separated hierarchy (`notifications:billing:invoice`, `cache:invalidate:products:p-001`) so consumers can subscribe at the right level: a billing service uses `notifications:billing:*`, the audit logger uses `notifications:*`. Glob patterns are evaluated for every published message, so don't go wild with multiple wildcards on hot paths — `*:*:*` matches everything and costs more than a flat `notifications:*` would."},{"id":"dont-do-heavy-work-in-the-listener-callback","title":"Don't do heavy work in the listener callback","role":"content","text":"node-redis dispatches incoming messages on the event loop. If your listener does synchronous heavy work (big JSON parse, sync crypto, blocking computation), the next message waits behind it and the subscriber's effective throughput drops to whatever the listener's latency is. For heavier work, the listener should push the message onto a worker queue or hand it off to an async pipeline — for true durable handoff, write it onto a [Redis Streams](https://redis.io/docs/latest/develop/data-types/streams) consumer group."},{"id":"tune-the-subscriber-buffer-for-your-traffic-shape","title":"Tune the subscriber buffer for your traffic shape","role":"content","text":"The demo caps each subscriber's in-memory message buffer at 50. That's right for showing the recent activity in a UI, but a real subscriber typically processes each message and discards it — the buffer is only there for human inspection. If you keep a buffer, make sure it's bounded; an unbounded array on a chatty pattern subscriber will eventually OOM the Node process."},{"id":"reconnection-and-listener-re-registration","title":"Reconnection and listener re-registration","role":"content","text":"node-redis 5.x reconnects automatically and re-registers active `subscribe` / `pSubscribe` listeners after the new connection is up, so you don't have to write the recovery path by hand. What it doesn't replay is messages that arrived during the disconnect — those are gone. If your consumers can't tolerate those gaps, treat pub/sub as the live-notification layer and reconcile from a durable source on every reconnect."},{"id":"sharded-pubsub-on-a-redis-cluster","title":"Sharded pub/sub on a Redis Cluster","role":"content","text":"On a Redis Cluster, plain `PUBLISH` fans every message out to every node via the cluster bus, which becomes a hotspot at high throughput. Redis 7.0 added [sharded pub/sub](https://redis.io/docs/latest/develop/pubsub#sharded-pubsub): channels are hashed to slots, and `SPUBLISH` / `SSUBSCRIBE` only touch the shard that owns the slot. If you're scaling pub/sub on a cluster, prefer the sharded commands and pick channel names whose hash distribution matches your traffic."},{"id":"inspect-pubsub-state-directly-in-redis","title":"Inspect pub/sub state directly in Redis","role":"content","text":"Because pub/sub has no keyspace, `KEYS`/`SCAN` won't show you anything. Use the introspection commands instead:\n\n[code example]\n\n`redis-cli` in subscribe mode only exits with `Ctrl-C` — it can't issue any other commands while subscribed."},{"id":"learn-more","title":"Learn more","role":"related","text":"This example uses the following Redis commands:\n\n* [`PUBLISH`](https://redis.io/docs/latest/commands/publish) to fan a message out to every subscriber of a channel.\n* [`SUBSCRIBE`](https://redis.io/docs/latest/commands/subscribe) and [`UNSUBSCRIBE`](https://redis.io/docs/latest/commands/unsubscribe) for exact-match topic subscriptions.\n* [`PSUBSCRIBE`](https://redis.io/docs/latest/commands/psubscribe) and [`PUNSUBSCRIBE`](https://redis.io/docs/latest/commands/punsubscribe) for glob-style pattern subscriptions.\n* [`PUBSUB CHANNELS`](https://redis.io/docs/latest/commands/pubsub-channels) to list channels with at least one active exact-match subscriber.\n* [`PUBSUB NUMSUB`](https://redis.io/docs/latest/commands/pubsub-numsub) to count subscribers per named channel.\n* [`PUBSUB NUMPAT`](https://redis.io/docs/latest/commands/pubsub-numpat) to count active pattern subscriptions server-wide.\n\nSee the [`node-redis` documentation](https://redis.io/docs/latest/develop/clients/nodejs) for full client reference, including the [pub/sub guide](https://github.com/redis/node-redis/blob/master/docs/pub-sub.md) covering the listener-based `subscribe` / `pSubscribe` API and reconnection behaviour."}],"examples":[{"id":"the-pubsub-hub-helper-ex0","language":"javascript","code":"const { createClient } = require(\"redis\");\nconst { RedisPubSubHub } = require(\"./pubsub_hub\");\n\nconst client = createClient({ url: \"redis://localhost:6379\" });\nawait client.connect();\nconst hub = new RedisPubSubHub(client);\n\n// Exact-match subscriber\nawait hub.subscribe(\"orders-listener\", [\"orders:new\"]);\n\n// Pattern subscriber covering an entire topic hierarchy\nawait hub.psubscribe(\"all-notifications\", [\"notifications:*\"]);\n\n// Publish — returns Redis' delivered count for this PUBLISH\nconst delivered = await hub.publish(\"orders:new\", { order_id: 42, total: 199.0 });\nconsole.log(`Redis delivered to ${delivered} subscriber(s)`);\n\n// Look at what each subscriber received\nfor (const sub of hub.subscriptions()) {\n  console.log(sub.name, sub.receivedTotal(), \"messages\");\n  for (const message of sub.messages(5)) {\n    console.log(\"  \", message.channel, message.payload);\n  }\n}\n\nawait hub.unsubscribe(\"orders-listener\");\nawait hub.shutdown(); // closes every remaining subscription","section_id":"the-pubsub-hub-helper"},{"id":"data-model-ex0","language":"text","code":"RedisPubSubHub                          (in-process)\n  subscriptions             Map<string, Subscription>\n  published_total           number\n  delivered_total           number\n  channel_published         Map<channel, count>\n\nSubscription                            (in-process, one per subscriber)\n  name                      string\n  targets                   string[]      (channels or patterns)\n  isPattern                 boolean\n  buffer                    ReceivedMessage[]  (capped, default 50)\n  received                  number\n  client                    RedisClient        (dedicated, subscribe-mode)","section_id":"data-model"},{"id":"publishing-messages-ex0","language":"javascript","code":"async publish(channel, message) {\n  const payload = JSON.stringify(message);\n  const delivered = Number(await this.redis.publish(channel, payload));\n  this._published_total += 1;\n  this._delivered_total += delivered;\n  this._channel_published.set(\n    channel,\n    (this._channel_published.get(channel) || 0) + 1,\n  );\n  return delivered;\n}","section_id":"publishing-messages"},{"id":"subscribing-to-channels-ex0","language":"javascript","code":"async subscribe(name, channels, onMessage = null) {\n  return this._register(name, channels, false, onMessage);\n}","section_id":"subscribing-to-channels"},{"id":"subscribing-to-channels-ex1","language":"javascript","code":"this._client = hubClient.duplicate();\nawait this._client.connect();\nfor (const channel of this.targets) {\n  await this._client.subscribe(channel, (message, channel) =>\n    this._dispatch(message, channel),\n  );\n}","section_id":"subscribing-to-channels"},{"id":"pattern-subscriptions-with-psubscribe-ex0","language":"javascript","code":"await hub.psubscribe(\"all-notifications\", [\"notifications:*\"]);\nawait hub.psubscribe(\"cache-invalidator\", [\"cache:invalidate:*\"]);","section_id":"pattern-subscriptions-with-psubscribe"},{"id":"pattern-subscriptions-with-psubscribe-ex1","language":"javascript","code":"for (const pattern of this.targets) {\n  await this._client.pSubscribe(pattern, (message, channel) => {\n    this._dispatch(message, channel, pattern);\n  });\n}","section_id":"pattern-subscriptions-with-psubscribe"},{"id":"inspecting-active-subscribers-ex0","language":"javascript","code":"await hub.activeChannels();                 // PUBSUB CHANNELS *\nawait hub.channelSubscriberCounts(channels); // PUBSUB NUMSUB ch1 ch2 ...\nawait hub.patternSubscriberCount();          // PUBSUB NUMPAT","section_id":"inspecting-active-subscribers"},{"id":"stats-and-history-ex0","language":"javascript","code":"async stats() {\n  const subs = this.subscriptions();\n  const received_total = subs.reduce((sum, s) => sum + s.receivedTotal(), 0);\n  const channel_published = {};\n  for (const [channel, count] of this._channel_published.entries()) {\n    channel_published[channel] = count;\n  }\n  return {\n    published_total: this._published_total,\n    delivered_total: this._delivered_total,\n    received_total,\n    active_subscriptions: subs.length,\n    channel_published,\n    pattern_subscriptions: await this.patternSubscriberCount(),\n  };\n}","section_id":"stats-and-history"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir pub-sub-demo && cd pub-sub-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/pub-sub/nodejs\ncurl -O $BASE/package.json\ncurl -O $BASE/pubsub_hub.js\ncurl -O $BASE/demo_server.js\nnpm install","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"node demo_server.js","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"Redis pub/sub demo server listening on http://127.0.0.1:8096\nUsing Redis at localhost:6379\nSeeded 3 default subscription(s)","section_id":"start-the-demo-server"},{"id":"inspect-pubsub-state-directly-in-redis-ex0","language":"bash","code":"# Which channels currently have at least one exact-match subscriber?\nredis-cli pubsub channels '*'\n\n# How many subscribers does each channel have?\nredis-cli pubsub numsub orders:new notifications:billing chat:lobby\n\n# How many active pattern subscriptions across the whole server?\nredis-cli pubsub numpat\n\n# Subscribe interactively from the CLI to watch traffic on a pattern\nredis-cli psubscribe 'orders:*'","section_id":"inspect-pubsub-state-directly-in-redis"}]}
{"schema_version":2,"id":"develop/use-cases/pub-sub/php","title":"Redis pub/sub with Predis","url":"https://redis.io/docs/latest/develop/use-cases/pub-sub/php/","summary":"Implement Redis pub/sub messaging in PHP with Predis","aliases":["/develop/use-cases/pub-sub/predis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-17T10:28:27+01:00","children":[],"page_type":"content","content_hash":"c70a4621f5a90c85cab2ccffeed869f99dc5d2e44ba3b74d536b53c2a6036bcc","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis-backed pub/sub broadcaster in PHP with [Predis](https://github.com/predis/predis). It includes a small local web server built on PHP's built-in dev server so you can publish messages to named channels, add and remove subscribers live, and watch Redis fan out each message to every interested listener."},{"id":"overview","title":"Overview","role":"overview","text":"Pub/sub lets your application broadcast events — chat messages, cache invalidation signals, presence updates, notifications — to many consumers without per-pair wiring. The publisher names a *channel*; every client currently subscribed to that channel receives the message, in publish order, with sub-millisecond fan-out.\n\nThat gives you:\n\n* Many-to-many event delivery with no message storage cost in Redis\n* Exact-match subscriptions (`SUBSCRIBE orders:new`) for known topics\n* Pattern subscriptions (`PSUBSCRIBE notifications:*`) for whole topic hierarchies\n* Live server-side introspection through `PUBSUB CHANNELS`, `PUBSUB NUMSUB`, and `PUBSUB NUMPAT`\n* At-most-once delivery: subscribers that are offline when a message is published miss it, so durable state should live in keys or a Stream, not in the pub/sub channel itself\n\nIn this example, the publisher side calls `PUBLISH` with a JSON-encoded body and counts how many subscribers Redis reported delivering to. Each subscriber runs in its own detached PHP CLI process and writes the messages it receives to a Redis list so the demo server can render them."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. The application calls `$hub->subscribe($name, $channels)` or `$hub->psubscribe($name, $patterns)`\n2. The helper spawns a detached `php subscriber_worker.php` process, records its PID in Redis under `demo:pubsub:sub:{name}:pid`, and stores the subscription metadata under `demo:pubsub:sub:{name}:meta`\n3. The worker connects to Redis, calls `pubSubLoop()` against the channels or patterns, and writes each received message to `demo:pubsub:sub:{name}:messages`\n4. The application (or another process) calls `$hub->publish($channel, $message)`\n5. Redis fans the message out over every subscribing client's open socket\n6. The subscribing worker reads the message, JSON-decodes the payload, and `LPUSH`es a structured record onto its messages list — capped via `LTRIM` and with a `HINCRBY` on `received_total`\n7. The publisher receives the integer subscriber count back from `PUBLISH`, which is the number of clients Redis delivered to right then\n\nPattern subscriptions match channels by glob (`*`, `?`, `[abc]`). A single message that matches both an exact subscription and a pattern subscription is delivered twice — once as a `message` and once as a `pmessage`.\n\nThis port is **structurally different from the other clients in this use case**: every other client keeps subscribers as in-process objects with a background thread (or async task), but PHP's built-in `php -S` development server runs each HTTP request in a brand-new short-lived process, so an in-process subscriber would die as soon as the request that created it returned. The helper sidesteps that by spawning detached OS processes and keeping every piece of cross-request state in Redis. See [Production usage](#production-usage) for the longer story."},{"id":"the-pubsub-hub-helper","title":"The pub/sub hub helper","role":"content","text":"The `RedisPubSubHub` class wraps the publish, subscribe, and introspection operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/pub-sub/php/PubSubHub.php)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Pub/sub has no Redis keyspace footprint of its own — channels are server-side routing entries, not stored values. The hub keeps its own bookkeeping under the `demo:pubsub:*` namespace so every PHP process (the demo server, every worker, the CLI) sees the same state:\n\n[code example]\n\nA subscriber's `messages` list holds JSON records of the form:\n\n[code example]\n\n`pattern` is the matching pattern for pattern subscribers and `null` for exact-match subscribers — the demo UI uses that to render \"via `notifications:*`\" annotations next to messages received by pattern subscribers.\n\nThe implementation uses:\n\n* [`PUBLISH`](https://redis.io/docs/latest/commands/publish) to fan a JSON-encoded message out to every subscriber of a channel\n* [`SUBSCRIBE`](https://redis.io/docs/latest/commands/subscribe) for exact-match subscribers (called from the worker)\n* [`PSUBSCRIBE`](https://redis.io/docs/latest/commands/psubscribe) for glob-style pattern subscribers (called from the worker)\n* [`PUBSUB CHANNELS`](https://redis.io/docs/latest/commands/pubsub-channels) to list the channels with at least one active exact-match subscriber\n* [`PUBSUB NUMSUB`](https://redis.io/docs/latest/commands/pubsub-numsub) to count subscribers per channel\n* [`PUBSUB NUMPAT`](https://redis.io/docs/latest/commands/pubsub-numpat) to count active pattern subscriptions server-wide\n* [`LPUSH`](https://redis.io/docs/latest/commands/lpush) and [`LTRIM`](https://redis.io/docs/latest/commands/ltrim) on each subscriber's messages list to keep a recent-history buffer\n* [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby) on each subscriber's meta hash to maintain a `received_total` counter that the demo UI can read\n* The Predis `pubSubLoop()` method to iterate incoming pub/sub events without writing the protocol-decode loop by hand"},{"id":"publishing-messages","title":"Publishing messages","role":"content","text":"`publish()` JSON-encodes the message body, calls `PUBLISH`, and updates the cross-request publish counters:\n\n[code example]\n\nThe integer returned by `PUBLISH` is what Redis itself reports — the number of subscribers (direct and pattern) that received the message in that call. It's a useful sanity check that the channel name is actually being listened to: a steady stream of `0`s means you have a typo somewhere or your subscriber crashed.\n\nCounters live in Redis (under `demo:pubsub:stats`) rather than as object properties because every HTTP request runs in its own PHP process under `php -S`. An object property would be reset on every request."},{"id":"subscribing-to-channels","title":"Subscribing to channels","role":"content","text":"`subscribe()` spawns a `subscriber_worker.php` process that owns its own Redis connection and runs the pub/sub loop:\n\n[code example]\n\n`HSETNX` on the metadata hash gives the helper an atomic \"claim this name\" without a separate lock key. After the name is staked, the rest of the metadata is filled in and the worker is spawned.\n\n`waitForSubscription()` polls `PUBSUB NUMSUB` (or `PUBSUB NUMPAT`) for up to two seconds so a `publish()` that follows immediately after `subscribe()` doesn't race the worker's `SUBSCRIBE` call — without it, a fast-fire publish could land before the worker socket reached subscribe-only mode."},{"id":"spawning-a-detached-worker","title":"Spawning a detached worker","role":"content","text":"Spawning a worker from a PHP request handler is more delicate than it looks. The `php -S` development server keeps its listening socket open in every child it forks or execs from a request handler — if the worker inherits that FD, the dev server's port gets a phantom listener that can hijack new connections, *and* the worker can't be killed cleanly. There are two defences:\n\n[code example]\n\n* On Linux, `setsid -f` puts the worker into a new session and process group, detached from the dev-server's group.\n* On macOS (no `setsid`), a `/bin/sh -c '… & echo $!'` wrapper does the same thing — backgrounds the worker and echoes its PID on stdout for the parent to capture.\n* Every standard FD is redirected to a file (or `/dev/null`) so the dev server's accept socket can't leak into the worker.\n\nThe same pattern is used in the [job-queue PHP port](../../job-queue/php/) and the [cache-aside PHP port](../../cache-aside/php/) for their respective worker types."},{"id":"inside-the-subscriber-worker","title":"Inside the subscriber worker","role":"content","text":"The worker ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/pub-sub/php/subscriber_worker.php)) is a small CLI that connects to Redis, enters `pubSubLoop()`, and writes each received message to the per-subscription messages list:\n\n[code example]\n\nA few details matter here:\n\n* **Two Redis clients per worker.** Once Predis enters `pubSubLoop()`, the underlying connection is in subscribe-only mode — sending an `LPUSH` on the same connection fails with `ERR Can't execute 'lpush': only (P|S)SUBSCRIBE / (P|S)UNSUBSCRIBE / PING / QUIT / RESET are allowed in this context`. The worker therefore keeps a separate `$writeRedis` client for the buffering pipeline. (The reference Python implementation hides this behind `redis-py`'s `PubSub` object, which manages its own dedicated connection internally — but the constraint is the same on the wire.)\n* **`read_write_timeout => 0` on the subscribe socket.** A pub/sub loop is supposed to block indefinitely waiting for messages. The default Predis timeout would otherwise drop the connection during quiet periods.\n* **SIGTERM is handled with `pcntl_async_signals`.** The hub's `unsubscribe()` sends SIGTERM to the worker. The worker catches it and sets a `$stop` flag, which is checked at the start of every loop iteration — so the worker exits cleanly *after* the next message it receives. For long-quiet subscriptions, the hub falls back to SIGKILL after 80 ms."},{"id":"pattern-subscriptions-with-psubscribe","title":"Pattern subscriptions with PSUBSCRIBE","role":"content","text":"`psubscribe()` works the same way but the worker calls `PSUBSCRIBE` instead of `SUBSCRIBE`:\n\n[code example]\n\nWhen a published channel matches a pattern, the worker's `pubSubLoop()` delivers an event with `kind === 'pmessage'` whose `pattern` field carries the matching glob. That distinction is useful for routing: a pattern subscriber can do one thing for the whole hierarchy (e.g., increment a counter) and dispatch on the specific channel within its logic (e.g., \"invalidate this region's cache\")."},{"id":"inspecting-active-subscribers","title":"Inspecting active subscribers","role":"content","text":"Redis exposes a small set of pub/sub introspection commands that report on subscriber state without traversing any keyspace:\n\n[code example]\n\n`PUBSUB CHANNELS` only reports channels with at least one *exact-match* subscriber — pattern subscribers do not appear here. That's a deliberate Redis design choice: a glob like `*` would otherwise show up as a subscriber to every conceivable channel. `PUBSUB NUMPAT` covers the pattern side as a single global count.\n\nPredis 3.x doesn't expose dedicated PHP helpers for these commands, so the hub sends them via `executeRaw(['PUBSUB', 'CHANNELS', $pattern])` — a clean fallback for any command the client doesn't model directly."},{"id":"stats-and-history","title":"Stats and history","role":"content","text":"`stats()` reads the publish counters from Redis and sums received counts across every active subscription:\n\n[code example]\n\n`delivered_total` is what Redis itself counted; `received_total` is what the workers have logged into their per-subscription Redis lists. In a single-process demo they should track each other closely — a sustained divergence usually means a worker crashed while a publisher kept publishing. (Pub/sub is at-most-once: if your subscriber wasn't connected at publish time, the message is gone.)"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* Redis 6.2 or later running locally on the default port (6379). Earlier versions still work for plain `PUBLISH`/`SUBSCRIBE`; `PUBSUB NUMPAT` is older than that.\n* PHP 8.1 or later, with the `posix` and `pcntl` extensions enabled (both ship with the official PHP binary on macOS and most Linux distros).\n* The Predis client (3.x). Install it with [Composer](https://getcomposer.org/):\n\n  [code example]"},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of four files plus the Composer manifest. Download them from the [`php` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/pub-sub/php) on GitHub, or grab them with `curl`:\n\n[code example]\n\nThen install dependencies:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From that directory:\n\n[code example]\n\nYou should see:\n\n[code example]\n\nOpen [http://127.0.0.1:8101](http://127.0.0.1:8101) in a browser. You can:\n\n* Publish messages of any text to any channel name in any batch size.\n* Add named subscribers that listen on either a specific channel (`orders:new`) or a glob pattern (`notifications:*`). A single subscriber can listen on multiple targets — enter them comma-separated.\n* Watch each subscriber's incoming-message panel update every 800 ms (the data is read out of Redis on every poll, since the workers live in separate PHP processes).\n* See the server-side view: `PUBSUB CHANNELS` lists exact-match channels with subscribers, `PUBSUB NUMSUB` gives per-channel counts, and `PUBSUB NUMPAT` counts active pattern subscriptions.\n* Click **Reset** to drop every subscription, zero the counters, and re-seed the three default subscribers.\n\nThe first time the server handles a request, it seeds three default subscriptions (`orders-listener`, `billing-listener`, `all-notifications`). A flag in Redis (`demo:pubsub:seeded`) prevents the seed from re-running on every request, so `unsubscribe` doesn't get immediately undone; defaults come back when you click **Reset**.\n\nIf your Redis server is running elsewhere, set `REDIS_HOST` and `REDIS_PORT` before launching the server:\n\n[code example]"},{"id":"stopping-the-demo-cleanly","title":"Stopping the demo cleanly","role":"content","text":"`php -S` doesn't run a shutdown handler when you Ctrl-C out of it, so the subscriber worker processes — which are *intentionally* detached so they survive request boundaries — will outlive the demo server unless you stop them first. Before stopping the server, click **Reset** in the UI (which calls `$hub->shutdown()` and kills every worker), or run:\n\n[code example]\n\nIf you forgot, clean up by name:\n\n[code example]"},{"id":"production-usage","title":"Production usage","role":"content","text":""},{"id":"why-this-php-port-differs-from-the-others","title":"Why this PHP port differs from the others","role":"content","text":"Every other client in this use case keeps subscribers as in-process objects with a background thread (Python's `run_in_thread`, .NET's `IConnectionMultiplexer.Subscribe`, Node's `subscriber.on('message', ...)`, Go's `<-ch`, etc.). That works because those runtimes have a long-lived server process that owns the subscriber's connection, callback, and dispatch loop.\n\nPHP's traditional one-process-per-request model — used by `php -S`, mod_php, PHP-FPM with the default `pm` setting — fundamentally doesn't fit that shape. A subscriber created inside an HTTP handler dies the moment the handler returns. Even if you used a long-running PHP daemon (Roadrunner, Swoole, ReactPHP), you'd still need separate worker processes if you wanted multiple independent subscribers, because Predis's `pubSubLoop()` blocks the calling process.\n\nThis port keeps each subscriber as a **separate OS process**, with its full state (PID, received counter, message buffer) persisted in Redis. Every HTTP request reconstructs its view of the subscriber registry from those keys. The pattern is closer to how a real production PHP application would run pub/sub: a `supervisord`, `systemd`, or container orchestrator drives N copies of `subscriber_worker.php`, each owning one logical subscription, and the web tier never tries to host a subscriber itself. The demo just inlines the supervision (via `proc_open` + `posix_kill`) so a single `php -S` command is enough to play with the pattern end-to-end."},{"id":"pubsub-is-at-most-once--pair-it-with-durable-state-if-you-need-replay","title":"Pub/sub is at-most-once — pair it with durable state if you need replay","role":"content","text":"A subscriber that's offline when a message is published misses it permanently. For events you can't afford to lose, write the durable record (the order row, the cache key version, the audit log entry) to its primary store, then `PUBLISH` a notification so live consumers can pick it up immediately. On reconnect, consumers reconcile by reading the durable store, not by waiting for missed pub/sub messages. If you actually need replay or at-least-once delivery, switch to [Redis Streams](https://redis.io/docs/latest/develop/data-types/streams) with consumer groups."},{"id":"use-a-separate-connection-per-subscriber-and-a-second-one-for-any-non-pubsub-work","title":"Use a separate connection per subscriber (and a *second* one for any non-pubsub work)","role":"content","text":"Predis's `pubSubLoop()` puts its connection into subscribe-only mode: ordinary commands (`GET`, `HSET`, `LPUSH`, etc.) on the same connection will fail with `ERR Can't execute 'lpush': only (P|S)SUBSCRIBE / ...`. The worker in this demo therefore keeps two `Predis\\Client` instances — `$subRedis` for the loop and `$writeRedis` for the receive-buffering pipeline. The same rule applies in production: any work the subscriber needs to do as a result of a received message (writing to a key, publishing a follow-up event, hitting a downstream API) must go through a separate connection."},{"id":"choose-a-topic-naming-convention-up-front","title":"Choose a topic naming convention up front","role":"content","text":"A flat namespace gets ugly fast — `email`, `email_high_priority`, `email_high_priority_billing`. Pick a colon-separated hierarchy (`notifications:billing:invoice`, `cache:invalidate:products:p-001`) so consumers can subscribe at the right level: a billing service uses `notifications:billing:*`, the audit logger uses `notifications:*`. Glob patterns are evaluated for every published message, so don't go wild with multiple wildcards on hot paths — `*:*:*` matches everything and costs more than a flat `notifications:*` would."},{"id":"dont-do-heavy-work-in-the-message-handling-loop","title":"Don't do heavy work in the message-handling loop","role":"content","text":"The worker reads messages from a single socket. If the per-message work blocks (synchronous HTTP call, big computation, slow DB write), the next message waits behind it and the subscriber's effective throughput drops to whatever the work's latency is. For heavier work, push the message onto a Redis [list](https://redis.io/docs/latest/develop/data-types/lists) or [stream](https://redis.io/docs/latest/develop/data-types/streams) and let a separate pool of workers consume it — exactly the pattern in the [job-queue use case](https://redis.io/docs/latest/develop/use-cases/job-queue)."},{"id":"tune-the-subscriber-message-buffer-for-your-traffic-shape","title":"Tune the subscriber message buffer for your traffic shape","role":"content","text":"The demo caps each subscriber's Redis-side message list at 50 (via `LTRIM 0 49`). That's right for showing the recent activity in a UI, but a real subscriber typically processes each message and discards it — the buffer is only there for human inspection. If you keep a buffer, make sure it's bounded; an unbounded `LPUSH` on a chatty pattern subscriber will eventually OOM Redis."},{"id":"sharded-pubsub-on-a-redis-cluster","title":"Sharded pub/sub on a Redis Cluster","role":"content","text":"On a Redis Cluster, plain `PUBLISH` fans every message out to every node via the cluster bus, which becomes a hotspot at high throughput. Redis 7.0 added [sharded pub/sub](https://redis.io/docs/latest/develop/pubsub#sharded-pubsub): channels are hashed to slots, and `SPUBLISH` / `SSUBSCRIBE` only touch the shard that owns the slot. If you're scaling pub/sub on a cluster, prefer the sharded commands and pick channel names whose hash distribution matches your traffic. Predis 3.x exposes both via `ssubscribe()` on the pub/sub loop and `executeRaw(['SPUBLISH', ...])`."},{"id":"inspect-pubsub-state-directly-in-redis","title":"Inspect pub/sub state directly in Redis","role":"content","text":"Because pub/sub has no keyspace, `KEYS`/`SCAN` won't show you the routing table. Use the introspection commands instead:\n\n[code example]\n\n`redis-cli` in subscribe mode only exits with `Ctrl-C` — it can't issue any other commands while subscribed.\n\nThe demo's own bookkeeping (PIDs, message buffers, counters) lives under regular keys, so you *can* inspect that with `SCAN`:\n\n[code example]"},{"id":"learn-more","title":"Learn more","role":"related","text":"This example uses the following Redis commands:\n\n* [`PUBLISH`](https://redis.io/docs/latest/commands/publish) to fan a message out to every subscriber of a channel.\n* [`SUBSCRIBE`](https://redis.io/docs/latest/commands/subscribe) and [`UNSUBSCRIBE`](https://redis.io/docs/latest/commands/unsubscribe) for exact-match topic subscriptions.\n* [`PSUBSCRIBE`](https://redis.io/docs/latest/commands/psubscribe) and [`PUNSUBSCRIBE`](https://redis.io/docs/latest/commands/punsubscribe) for glob-style pattern subscriptions.\n* [`PUBSUB CHANNELS`](https://redis.io/docs/latest/commands/pubsub-channels) to list channels with at least one active exact-match subscriber.\n* [`PUBSUB NUMSUB`](https://redis.io/docs/latest/commands/pubsub-numsub) to count subscribers per named channel.\n* [`PUBSUB NUMPAT`](https://redis.io/docs/latest/commands/pubsub-numpat) to count active pattern subscriptions server-wide.\n* [`LPUSH`](https://redis.io/docs/latest/commands/lpush), [`LTRIM`](https://redis.io/docs/latest/commands/ltrim), and [`LRANGE`](https://redis.io/docs/latest/commands/lrange) for the per-subscriber recent-message buffers.\n* [`HSET`](https://redis.io/docs/latest/commands/hset), [`HGETALL`](https://redis.io/docs/latest/commands/hgetall), and [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby) for the cross-request stats and per-subscription meta.\n\nSee the [Predis README](https://github.com/predis/predis) for full client reference, including the [pub/sub consumer](https://github.com/predis/predis/blob/main/src/Consumer/PubSub/Consumer.php) returned by `pubSubLoop()`."}],"examples":[{"id":"the-pubsub-hub-helper-ex0","language":"php","code":"require __DIR__ . '/vendor/autoload.php';\nrequire __DIR__ . '/PubSubHub.php';\n\nuse Predis\\Client as PredisClient;\n\n$redis = new PredisClient(['host' => '127.0.0.1', 'port' => 6379]);\n$hub = new RedisPubSubHub($redis, __DIR__ . '/subscriber_worker.php');\n\n// Exact-match subscriber — spawns a worker process.\n$hub->subscribe('orders-listener', ['orders:new']);\n\n// Pattern subscriber covering an entire topic hierarchy.\n$hub->psubscribe('all-notifications', ['notifications:*']);\n\n// Publish — returns Redis' delivered count for this PUBLISH.\n$delivered = $hub->publish('orders:new', ['order_id' => 42, 'total' => 199.0]);\necho \"Redis delivered to {$delivered} subscriber(s)\\n\";\n\n// Look at what each subscriber has received so far.\nforeach ($hub->subscriptions() as $sub) {\n    echo \"{$sub['name']}: {$sub['received_total']} message(s)\\n\";\n    foreach ($sub['messages'] as $m) {\n        echo \"  {$m['channel']}: \" . json_encode($m['payload']) . \"\\n\";\n    }\n}\n\n$hub->unsubscribe('orders-listener');\n$hub->shutdown();   // kills every remaining worker","section_id":"the-pubsub-hub-helper"},{"id":"data-model-ex0","language":"text","code":"demo:pubsub:stats                          (hash) published_total, delivered_total\ndemo:pubsub:channel_published              (hash) channel -> publish count\ndemo:pubsub:seeded                         (string flag) \"1\" once defaults are seeded\n\ndemo:pubsub:sub:{name}:meta                (hash) name, kind, targets, is_pattern,\n                                                  created_at_ms, received_total\ndemo:pubsub:sub:{name}:pid                 (string) worker process PID\ndemo:pubsub:sub:{name}:messages            (list)  recent messages, capped via LTRIM","section_id":"data-model"},{"id":"data-model-ex1","language":"json","code":"{\n  \"channel\": \"orders:new\",\n  \"pattern\": null,\n  \"payload\": {\"body\": \"hello\", \"seq\": 1, \"of\": 3},\n  \"received_at_ms\": 1715441000123\n}","section_id":"data-model"},{"id":"publishing-messages-ex0","language":"php","code":"public function publish(string $channel, $message): int\n{\n    $payload = is_string($message)\n        ? $message\n        : json_encode($message, JSON_UNESCAPED_SLASHES);\n    $delivered = (int) $this->redis->publish($channel, $payload);\n\n    $pipe = $this->redis->pipeline();\n    $pipe->hincrby('demo:pubsub:stats', 'published_total', 1);\n    $pipe->hincrby('demo:pubsub:stats', 'delivered_total', $delivered);\n    $pipe->hincrby('demo:pubsub:channel_published', $channel, 1);\n    $pipe->execute();\n\n    return $delivered;\n}","section_id":"publishing-messages"},{"id":"subscribing-to-channels-ex0","language":"php","code":"public function subscribe(string $name, array $channels): array\n{\n    return $this->register($name, $channels, false);\n}\n\nprivate function register(string $name, array $targets, bool $isPattern): array\n{\n    // Stake the name atomically so two concurrent /subscribe calls\n    // can't both spawn a worker for the same subscription.\n    if (!$this->redis->hsetnx($this->metaKey($name), 'name', $name)) {\n        throw new InvalidArgumentException(\"subscription named '{$name}' already exists\");\n    }\n    $this->redis->hset($this->metaKey($name),\n        'kind',           $isPattern ? 'pattern' : 'channel',\n        'targets',        implode(',', $targets),\n        'is_pattern',     $isPattern ? '1' : '0',\n        'created_at_ms',  (string) self::nowMs(),\n        'received_total', '0'\n    );\n    $pid = $this->spawnWorker($name, $isPattern ? 'pattern' : 'channel', $targets);\n    $this->redis->set($this->pidKey($name), (string) $pid);\n    $this->waitForSubscription($name, $isPattern, $targets);\n    return $this->getSubscription($name) ?? [];\n}","section_id":"subscribing-to-channels"},{"id":"spawning-a-detached-worker-ex0","language":"php","code":"$workerArgs = [\n    $this->phpBinary, $this->workerScript,\n    '--name', $name,\n    '--kind', $kind,\n    '--target', implode(',', $targets),\n    '--redis-host', $this->redisHost,\n    '--redis-port', (string) $this->redisPort,\n];\n\nif (PHP_OS_FAMILY === 'Darwin') {\n    // macOS' setsid has no -f flag. Use a shell wrapper that\n    // backgrounds the worker and echoes its PID so we can capture it.\n    $escaped = array_map('escapeshellarg', $workerArgs);\n    $shellCmd = sprintf('exec %s >>%s 2>&1 </dev/null & echo $!',\n        implode(' ', $escaped), escapeshellarg($logFile));\n    $args = ['/bin/sh', '-c', $shellCmd];\n} else {\n    $args = array_merge(['setsid', '-f'], $workerArgs);\n}\n\n$descriptorSpec = [\n    0 => ['file', '/dev/null', 'r'],\n    1 => ['pipe', 'w'],\n    2 => ['file', $logFile, 'a'],\n];\n$proc = proc_open($args, $descriptorSpec, $pipes);","section_id":"spawning-a-detached-worker"},{"id":"inside-the-subscriber-worker-ex0","language":"php","code":"$subRedis = new PredisClient([\n    'host' => $opts['redis-host'],\n    'port' => $opts['redis-port'],\n    'read_write_timeout' => 0,  // never time out the subscribe socket\n]);\n$writeRedis = new PredisClient([\n    'host' => $opts['redis-host'],\n    'port' => $opts['redis-port'],\n]);\n\n$loop = $subRedis->pubSubLoop();\nif ($kind === 'pattern') {\n    $loop->psubscribe(...$targets);\n} else {\n    $loop->subscribe(...$targets);\n}\n\nforeach ($loop as $event) {\n    if ($event->kind === 'message' || $event->kind === 'pmessage') {\n        $record = [\n            'channel'        => $event->channel,\n            'pattern'        => $event->kind === 'pmessage' ? $event->pattern : null,\n            'payload'        => json_decode($event->payload, true) ?? $event->payload,\n            'received_at_ms' => (int) round(microtime(true) * 1000),\n        ];\n        $pipe = $writeRedis->pipeline();\n        $pipe->lpush($messagesKey, [json_encode($record, JSON_UNESCAPED_SLASHES)]);\n        $pipe->ltrim($messagesKey, 0, $bufferSize - 1);\n        $pipe->hincrby($metaKey, 'received_total', 1);\n        $pipe->execute();\n    }\n}","section_id":"inside-the-subscriber-worker"},{"id":"pattern-subscriptions-with-psubscribe-ex0","language":"php","code":"$hub->psubscribe('all-notifications', ['notifications:*']);\n$hub->psubscribe('cache-invalidator', ['cache:invalidate:*']);","section_id":"pattern-subscriptions-with-psubscribe"},{"id":"inspecting-active-subscribers-ex0","language":"php","code":"$hub->activeChannels();                  // PUBSUB CHANNELS *\n$hub->channelSubscriberCounts($chans);   // PUBSUB NUMSUB ch1 ch2 ...\n$hub->patternSubscriberCount();          // PUBSUB NUMPAT","section_id":"inspecting-active-subscribers"},{"id":"stats-and-history-ex0","language":"php","code":"public function stats(): array\n{\n    $pipe = $this->redis->pipeline();\n    $pipe->hgetall('demo:pubsub:stats');\n    $pipe->hgetall('demo:pubsub:channel_published');\n    [$stats, $perChannel] = $pipe->execute();\n\n    $receivedTotal = 0;\n    foreach ($this->subscriptionNames() as $name) {\n        $receivedTotal += (int) $this->redis->hget($this->metaKey($name), 'received_total');\n    }\n\n    return [\n        'published_total'       => (int) ($stats['published_total'] ?? 0),\n        'delivered_total'       => (int) ($stats['delivered_total'] ?? 0),\n        'received_total'        => $receivedTotal,\n        'active_subscriptions'  => count($this->subscriptionNames()),\n        'channel_published'     => $perChannel,\n        'pattern_subscriptions' => $this->patternSubscriberCount(),\n    ];\n}","section_id":"stats-and-history"},{"id":"prerequisites-ex0","language":"bash","code":"composer require \"predis/predis:^3.0\"","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir pub-sub-demo && cd pub-sub-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/pub-sub/php\ncurl -O $BASE/PubSubHub.php\ncurl -O $BASE/subscriber_worker.php\ncurl -O $BASE/demo_server.php\ncurl -O $BASE/composer.json","section_id":"get-the-source-files"},{"id":"get-the-source-files-ex1","language":"bash","code":"composer install","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"php -S 127.0.0.1:8101 demo_server.php","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"[...] PHP 8.4.6 Development Server (http://127.0.0.1:8101) started","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex2","language":"bash","code":"REDIS_HOST=redis.local REDIS_PORT=6380 php -S 127.0.0.1:8101 demo_server.php","section_id":"start-the-demo-server"},{"id":"stopping-the-demo-cleanly-ex0","language":"bash","code":"curl -X POST http://127.0.0.1:8101/reset","section_id":"stopping-the-demo-cleanly"},{"id":"stopping-the-demo-cleanly-ex1","language":"bash","code":"pgrep -f subscriber_worker.php | xargs kill\nredis-cli --scan --pattern 'demo:pubsub:*' | xargs redis-cli del","section_id":"stopping-the-demo-cleanly"},{"id":"inspect-pubsub-state-directly-in-redis-ex0","language":"bash","code":"# Which channels currently have at least one exact-match subscriber?\nredis-cli pubsub channels '*'\n\n# How many subscribers does each channel have?\nredis-cli pubsub numsub orders:new notifications:billing chat:lobby\n\n# How many active pattern subscriptions across the whole server?\nredis-cli pubsub numpat\n\n# Subscribe interactively from the CLI to watch traffic on a pattern.\nredis-cli psubscribe 'orders:*'","section_id":"inspect-pubsub-state-directly-in-redis"},{"id":"inspect-pubsub-state-directly-in-redis-ex1","language":"bash","code":"# Every key the demo owns.\nredis-cli --scan --pattern 'demo:pubsub:*'\n\n# Recent messages received by a specific subscriber.\nredis-cli lrange demo:pubsub:sub:orders-listener:messages 0 9\n\n# Worker PIDs.\nredis-cli --scan --pattern 'demo:pubsub:sub:*:pid' \\\n  | while read k; do echo \"$k = $(redis-cli get \"$k\")\"; done","section_id":"inspect-pubsub-state-directly-in-redis"}]}
{"schema_version":2,"id":"develop/use-cases/pub-sub/redis-py","title":"Redis pub/sub with redis-py","url":"https://redis.io/docs/latest/develop/use-cases/pub-sub/redis-py/","summary":"Implement Redis pub/sub messaging in Python with redis-py","aliases":["/develop/use-cases/pub-sub/python"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-17T10:28:27+01:00","children":[],"page_type":"content","content_hash":"491db2923b9b9be5bb43ba84c3fbe6cd5037e1758a3a8e7a967c50b5a6323d9e","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis-backed pub/sub broadcaster in Python with [`redis-py`](https://redis.io/docs/latest/develop/clients/redis-py). It includes a small local web server built with the Python standard library so you can publish messages to named channels, add and remove subscribers live, and watch Redis fan out each message to every interested listener."},{"id":"overview","title":"Overview","role":"overview","text":"Pub/sub lets your application broadcast events — chat messages, cache invalidation signals, presence updates, notifications — to many consumers without per-pair wiring. The publisher names a *channel*; every client currently subscribed to that channel receives the message, in publish order, with sub-millisecond fan-out.\n\nThat gives you:\n\n* Many-to-many event delivery with no message storage cost in Redis\n* Exact-match subscriptions (`SUBSCRIBE orders:new`) for known topics\n* Pattern subscriptions (`PSUBSCRIBE notifications:*`) for whole topic hierarchies\n* Live server-side introspection through `PUBSUB CHANNELS`, `PUBSUB NUMSUB`, and `PUBSUB NUMPAT`\n* At-most-once delivery: subscribers that are offline when a message is published miss it, so durable state should live in keys or a Stream, not in the pub/sub channel itself\n\nIn this example, the publisher side calls `PUBLISH` with a JSON-encoded body and counts how many subscribers Redis reported delivering to. Each in-process subscriber owns its own Redis connection and a background thread that pumps incoming messages into a callback."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. The application calls `hub.subscribe(name, channels)` or `hub.psubscribe(name, patterns)`\n2. The helper creates a redis-py `PubSub` object, binds each target to a dispatch callback, and starts a background thread on `run_in_thread()`\n3. The application (or another process) calls `hub.publish(channel, message)`\n4. Redis fans the message out over every subscribing client's open socket\n5. Each subscriber's dispatch thread wraps the raw message as a `ReceivedMessage`, appends it to a per-subscriber ring buffer, and invokes the optional callback\n6. The publisher receives the integer subscriber count back from `PUBLISH`, which is the number of clients Redis delivered to right then\n\nPattern subscriptions match channels by glob (`*`, `?`, `[abc]`). A single message that matches both an exact subscription and a pattern subscription is delivered twice — once as a `message` and once as a `pmessage`."},{"id":"the-pubsub-hub-helper","title":"The pub/sub hub helper","role":"content","text":"The `RedisPubSubHub` class wraps the publish, subscribe, and introspection operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/pub-sub/redis-py/pubsub_hub.py)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Pub/sub has no Redis keyspace footprint of its own — channels are server-side routing entries, not stored values. The hub keeps its own bookkeeping in process memory:\n\n[code example]\n\nThe implementation uses:\n\n* [`PUBLISH`](https://redis.io/docs/latest/commands/publish) to fan a JSON-encoded message out to every subscriber of a channel\n* [`SUBSCRIBE`](https://redis.io/docs/latest/commands/subscribe) for exact-match subscribers\n* [`PSUBSCRIBE`](https://redis.io/docs/latest/commands/psubscribe) for glob-style pattern subscribers\n* [`PUBSUB CHANNELS`](https://redis.io/docs/latest/commands/pubsub-channels) to list the channels with at least one active exact-match subscriber\n* [`PUBSUB NUMSUB`](https://redis.io/docs/latest/commands/pubsub-numsub) to count subscribers per channel\n* [`PUBSUB NUMPAT`](https://redis.io/docs/latest/commands/pubsub-numpat) to count active pattern subscriptions server-wide\n* The redis-py `pubsub.run_in_thread()` helper to dispatch messages from each subscriber's own connection without writing the thread loop by hand"},{"id":"publishing-messages","title":"Publishing messages","role":"content","text":"`publish()` JSON-encodes the message body, calls `PUBLISH`, and updates the per-channel publish counter:\n\n[code example]\n\nThe integer returned by `PUBLISH` is what Redis itself reports — the number of subscribers (direct and pattern) that received the message in that call. It's a useful sanity check that the channel name is actually being listened to: a steady stream of `0`s means you have a typo somewhere or your subscriber crashed."},{"id":"subscribing-to-channels","title":"Subscribing to channels","role":"content","text":"`subscribe()` creates a named in-process `Subscription` that owns its own `PubSub` object and dispatch thread:\n\n[code example]\n\nInside `Subscription.__init__`, the helper binds each target to a callback and starts a background thread:\n\n[code example]\n\nA few details matter here:\n\n* `pubsub(ignore_subscribe_messages=True)` skips the `subscribe`/`unsubscribe` acknowledgements Redis sends back on the same socket, so the dispatch callback only ever sees real published payloads.\n* Each `Subscription` gets its own `PubSub` (and therefore its own Redis connection). Sharing one connection across subscribers would couple their lifetimes — closing one would close the channel for the others.\n* `run_in_thread` runs a short poll loop (`sleep_time=0.01`) rather than blocking on `listen()`. That's the cleanest way to support `subscription.close()` without leaving a thread stuck inside a blocking socket read."},{"id":"pattern-subscriptions-with-psubscribe","title":"Pattern subscriptions with PSUBSCRIBE","role":"content","text":"`psubscribe()` works the same way but routes messages through `PSUBSCRIBE` so each binding is a glob, not a literal channel name:\n\n[code example]\n\nWhen a published channel matches a pattern, the dispatch callback receives both the matched channel and the original pattern:\n\n[code example]\n\nThat distinction is useful for routing: a pattern subscriber can do one thing for the whole hierarchy (such as increment a counter) and dispatch on the specific channel within its callback (such as \"invalidate this region's cache\")."},{"id":"inspecting-active-subscribers","title":"Inspecting active subscribers","role":"content","text":"Redis exposes a small set of pub/sub introspection commands that report on subscriber state without traversing any keyspace:\n\n[code example]\n\n`PUBSUB CHANNELS` only reports channels with at least one exact-match subscriber — pattern subscribers do not appear here. That's a deliberate Redis design choice: a glob like `*` would otherwise show up as a subscriber to every conceivable channel. `PUBSUB NUMPAT` covers the pattern side as a single global count."},{"id":"stats-and-history","title":"Stats and history","role":"content","text":"`stats()` reports publish and receive counters plus the size of the subscription registry:\n\n[code example]\n\n`delivered_total` is what Redis itself counted; `received_total` is what this process's in-memory subscribers saw. In a single-process demo they should track each other closely — a sustained divergence usually means a callback raised, or a subscriber crashed while a publisher kept publishing. (Pub/sub is at-most-once: if your subscriber wasn't connected at publish time, the message is gone.)"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* Redis 6.2 or later running locally on the default port (6379). Earlier versions still work for plain `PUBLISH`/`SUBSCRIBE`; `PUBSUB NUMPAT` is older than that.\n* Python 3.9 or later.\n* The `redis-py` client. Install it with:\n\n  [code example]"},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of two Python files. Download them from the [`redis-py` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/pub-sub/redis-py) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From that directory:\n\n[code example]\n\nYou should see:\n\n[code example]\n\nOpen [http://127.0.0.1:8095](http://127.0.0.1:8095) in a browser. You can:\n\n* Publish messages of any text to any channel name in any batch size.\n* Add named subscribers that listen on either a specific channel (`orders:new`) or a glob pattern (`notifications:*`). A single subscriber can listen on multiple targets — enter them comma-separated.\n* Watch each subscriber's incoming-message panel update every 800 ms.\n* See the server-side view: `PUBSUB CHANNELS` lists exact-match channels with subscribers, `PUBSUB NUMSUB` gives per-channel counts, and `PUBSUB NUMPAT` counts active pattern subscriptions.\n* Click **Reset** to drop every subscription, zero the counters, and re-seed the three default subscribers.\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`."},{"id":"production-usage","title":"Production usage","role":"content","text":""},{"id":"pubsub-is-at-most-once--pair-it-with-durable-state-if-you-need-replay","title":"Pub/sub is at-most-once — pair it with durable state if you need replay","role":"content","text":"A subscriber that's offline when a message is published misses it permanently. For events you can't afford to lose, write the durable record (the order row, the cache key version, the audit log entry) to its primary store, then `PUBLISH` a notification so live consumers can pick it up immediately. On reconnect, consumers reconcile by reading the durable store, not by waiting for missed pub/sub messages. If you actually need replay or at-least-once delivery, switch to [Redis Streams](https://redis.io/docs/latest/develop/data-types/streams) with consumer groups."},{"id":"use-a-separate-connection-or-pubsub-object-per-subscriber","title":"Use a separate connection (or `PubSub` object) per subscriber","role":"content","text":"A redis-py `PubSub` puts its connection into subscribe-only mode: ordinary commands (`GET`, `HSET`, etc.) on the same connection will hang. Always create the `PubSub` from a client whose pool can spare a connection, or — as the helper does — give every subscriber its own `PubSub`. Sharing one `PubSub` across business-logic subscribers couples their lifetimes (closing one closes the channel for the others) and serialises their callbacks on a single dispatch thread."},{"id":"choose-a-topic-naming-convention-up-front","title":"Choose a topic naming convention up front","role":"content","text":"A flat namespace gets ugly fast — `email`, `email_high_priority`, `email_high_priority_billing`. Pick a colon-separated hierarchy (`notifications:billing:invoice`, `cache:invalidate:products:p-001`) so consumers can subscribe at the right level: a billing service uses `notifications:billing:*`, the audit logger uses `notifications:*`. Glob patterns are evaluated for every published message, so don't go wild with multiple wildcards on hot paths — `*:*:*` matches everything and costs more than a flat `notifications:*` would."},{"id":"dont-do-heavy-work-in-the-dispatch-callback","title":"Don't do heavy work in the dispatch callback","role":"content","text":"The dispatch thread reads messages from a single socket. If the callback blocks (synchronous HTTP call, big computation, slow DB write), the next message waits behind it and the subscriber's effective throughput drops to whatever the callback's latency is. For heavier work, the callback should hand the message off to a worker pool, a queue, or — for true durable handoff — a [Redis Streams](https://redis.io/docs/latest/develop/data-types/streams) consumer group."},{"id":"tune-the-subscriber-buffer-for-your-traffic-shape","title":"Tune the subscriber buffer for your traffic shape","role":"content","text":"The demo caps each subscriber's in-memory message buffer at 50. That's right for showing the recent activity in a UI, but a real subscriber typically processes each message and discards it — the buffer is only there for human inspection. If you keep a buffer, make sure it's bounded; an unbounded ring on a chatty pattern subscriber will eventually OOM the process."},{"id":"sharded-pubsub-on-a-redis-cluster","title":"Sharded pub/sub on a Redis Cluster","role":"content","text":"On a Redis Cluster, plain `PUBLISH` fans every message out to every node via the cluster bus, which becomes a hotspot at high throughput. Redis 7.0 added [sharded pub/sub](https://redis.io/docs/latest/develop/pubsub#sharded-pubsub): channels are hashed to slots, and `SPUBLISH` / `SSUBSCRIBE` only touch the shard that owns the slot. If you're scaling pub/sub on a cluster, prefer the sharded commands and pick channel names whose hash distribution matches your traffic."},{"id":"inspect-pubsub-state-directly-in-redis","title":"Inspect pub/sub state directly in Redis","role":"content","text":"Because pub/sub has no keyspace, `KEYS`/`SCAN` won't show you anything. Use the introspection commands instead:\n\n[code example]\n\n`redis-cli` in subscribe mode only exits with `Ctrl-C` — it can't issue any other commands while subscribed."},{"id":"learn-more","title":"Learn more","role":"related","text":"This example uses the following Redis commands:\n\n* [`PUBLISH`](https://redis.io/docs/latest/commands/publish) to fan a message out to every subscriber of a channel.\n* [`SUBSCRIBE`](https://redis.io/docs/latest/commands/subscribe) and [`UNSUBSCRIBE`](https://redis.io/docs/latest/commands/unsubscribe) for exact-match topic subscriptions.\n* [`PSUBSCRIBE`](https://redis.io/docs/latest/commands/psubscribe) and [`PUNSUBSCRIBE`](https://redis.io/docs/latest/commands/punsubscribe) for glob-style pattern subscriptions.\n* [`PUBSUB CHANNELS`](https://redis.io/docs/latest/commands/pubsub-channels) to list channels with at least one active exact-match subscriber.\n* [`PUBSUB NUMSUB`](https://redis.io/docs/latest/commands/pubsub-numsub) to count subscribers per named channel.\n* [`PUBSUB NUMPAT`](https://redis.io/docs/latest/commands/pubsub-numpat) to count active pattern subscriptions server-wide.\n\nSee the [`redis-py` documentation](https://redis.io/docs/latest/develop/clients/redis-py) for full client reference, including the [`PubSub` class](https://redis.readthedocs.io/en/stable/advanced_features.html#publish-subscribe) and the `run_in_thread()` dispatch helper."}],"examples":[{"id":"the-pubsub-hub-helper-ex0","language":"python","code":"import redis\nfrom pubsub_hub import RedisPubSubHub\n\nr = redis.Redis(host=\"localhost\", port=6379, decode_responses=True)\nhub = RedisPubSubHub(redis_client=r)\n\n# Exact-match subscriber\nhub.subscribe(name=\"orders-listener\", channels=[\"orders:new\"])\n\n# Pattern subscriber covering an entire topic hierarchy\nhub.psubscribe(name=\"all-notifications\", patterns=[\"notifications:*\"])\n\n# Publish — returns Redis' delivered count for this PUBLISH\ndelivered = hub.publish(\"orders:new\", {\"order_id\": 42, \"total\": 199.0})\nprint(f\"Redis delivered to {delivered} subscriber(s)\")\n\n# Look at what each subscriber received\nfor sub in hub.subscriptions():\n    print(sub.name, sub.received_total(), \"messages\")\n    for message in sub.messages(limit=5):\n        print(\"  \", message.channel, message.payload)\n\nhub.unsubscribe(\"orders-listener\")\nhub.shutdown()  # closes every remaining subscription","section_id":"the-pubsub-hub-helper"},{"id":"data-model-ex0","language":"text","code":"RedisPubSubHub                          (in-process)\n  subscriptions             dict[str, Subscription]\n  published_total           int\n  delivered_total           int\n  channel_published         dict[channel -> count]\n\nSubscription                            (in-process, one per subscriber)\n  name                      str\n  targets                   list[channel | pattern]\n  is_pattern                bool\n  buffer                    deque[ReceivedMessage]      (capped, default 50)\n  received_total            int\n  pubsub                    redis.client.PubSub          (owns one connection)\n  thread                    PubSubWorkerThread           (run_in_thread)","section_id":"data-model"},{"id":"publishing-messages-ex0","language":"python","code":"def publish(self, channel: str, message: object) -> int:\n    payload = json.dumps(message, default=str)\n    delivered = int(self.redis.publish(channel, payload))\n    with self._stats_lock:\n        self._published_total += 1\n        self._delivered_total += delivered\n        self._channel_published[channel] += 1\n    return delivered","section_id":"publishing-messages"},{"id":"subscribing-to-channels-ex0","language":"python","code":"def subscribe(self, name: str, channels: list[str], on_message=None) -> Subscription:\n    return self._register(name, channels, is_pattern=False, on_message=on_message)","section_id":"subscribing-to-channels"},{"id":"subscribing-to-channels-ex1","language":"python","code":"self._pubsub = hub.redis.pubsub(ignore_subscribe_messages=True)\nbindings = {target: self._dispatch for target in self.targets}\nif is_pattern:\n    self._pubsub.psubscribe(**bindings)\nelse:\n    self._pubsub.subscribe(**bindings)\nself._thread = self._pubsub.run_in_thread(sleep_time=0.01, daemon=True)","section_id":"subscribing-to-channels"},{"id":"pattern-subscriptions-with-psubscribe-ex0","language":"python","code":"hub.psubscribe(name=\"all-notifications\", patterns=[\"notifications:*\"])\nhub.psubscribe(name=\"cache-invalidator\", patterns=[\"cache:invalidate:*\"])","section_id":"pattern-subscriptions-with-psubscribe"},{"id":"pattern-subscriptions-with-psubscribe-ex1","language":"python","code":"def _dispatch(self, raw: dict) -> None:\n    channel = raw.get(\"channel\")   # the actual channel the message was sent to\n    pattern = raw.get(\"pattern\")   # the pattern that matched (None for exact subs)\n    data    = raw.get(\"data\")\n    ...","section_id":"pattern-subscriptions-with-psubscribe"},{"id":"inspecting-active-subscribers-ex0","language":"python","code":"hub.active_channels()                  # PUBSUB CHANNELS *\nhub.channel_subscriber_counts(chs)     # PUBSUB NUMSUB ch1 ch2 ...\nhub.pattern_subscriber_count()         # PUBSUB NUMPAT","section_id":"inspecting-active-subscribers"},{"id":"stats-and-history-ex0","language":"python","code":"def stats(self) -> dict:\n    return {\n        \"published_total\": self._published_total,\n        \"delivered_total\": self._delivered_total,         # sum of PUBLISH return values\n        \"received_total\": sum(s.received_total() for s in self.subscriptions()),\n        \"active_subscriptions\": len(self.subscriptions()),\n        \"channel_published\": dict(self._channel_published),\n        \"pattern_subscriptions\": self.pattern_subscriber_count(),\n    }","section_id":"stats-and-history"},{"id":"prerequisites-ex0","language":"bash","code":"pip install \"redis>=5.0\"","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir pub-sub-demo && cd pub-sub-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/pub-sub/redis-py\ncurl -O $BASE/pubsub_hub.py\ncurl -O $BASE/demo_server.py","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"python3 demo_server.py","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"Redis pub/sub demo server listening on http://127.0.0.1:8095\nUsing Redis at localhost:6379\nSeeded 3 default subscription(s)","section_id":"start-the-demo-server"},{"id":"inspect-pubsub-state-directly-in-redis-ex0","language":"bash","code":"# Which channels currently have at least one exact-match subscriber?\nredis-cli pubsub channels '*'\n\n# How many subscribers does each channel have?\nredis-cli pubsub numsub orders:new notifications:billing chat:lobby\n\n# How many active pattern subscriptions across the whole server?\nredis-cli pubsub numpat\n\n# Subscribe interactively from the CLI to watch traffic on a pattern\nredis-cli psubscribe 'orders:*'","section_id":"inspect-pubsub-state-directly-in-redis"}]}
{"schema_version":2,"id":"develop/use-cases/pub-sub/ruby","title":"Redis pub/sub with redis-rb","url":"https://redis.io/docs/latest/develop/use-cases/pub-sub/ruby/","summary":"Implement Redis pub/sub messaging in Ruby with redis-rb","aliases":["/develop/use-cases/pub-sub/redis-rb"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-17T10:28:27+01:00","children":[],"page_type":"content","content_hash":"e14b752f7af08f69efe67ea76e4a6f83d9c6b4fe483d03d974dcd1bd4ddfbe7d","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis-backed pub/sub broadcaster in Ruby with [`redis-rb`](https://redis.io/docs/latest/develop/clients/ruby). It includes a small local web server built with the Ruby standard library [`webrick`](https://github.com/ruby/webrick) so you can publish messages to named channels, add and remove subscribers live, and watch Redis fan out each message to every interested listener."},{"id":"overview","title":"Overview","role":"overview","text":"Pub/sub lets your application broadcast events — chat messages, cache invalidation signals, presence updates, notifications — to many consumers without per-pair wiring. The publisher names a *channel*; every client currently subscribed to that channel receives the message, in publish order, with sub-millisecond fan-out.\n\nThat gives you:\n\n* Many-to-many event delivery with no message storage cost in Redis\n* Exact-match subscriptions (`SUBSCRIBE orders:new`) for known topics\n* Pattern subscriptions (`PSUBSCRIBE notifications:*`) for whole topic hierarchies\n* Live server-side introspection through `PUBSUB CHANNELS`, `PUBSUB NUMSUB`, and `PUBSUB NUMPAT`\n* At-most-once delivery: subscribers that are offline when a message is published miss it, so durable state should live in keys or a Stream, not in the pub/sub channel itself\n\nIn this example, the publisher side calls `PUBLISH` with a JSON-encoded body and counts how many subscribers Redis reported delivering to. Each in-process subscriber owns its own Redis connection and a background Ruby `Thread` that pumps incoming messages into a callback."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. The application calls `hub.subscribe(name:, channels:)` or `hub.psubscribe(name:, patterns:)`\n2. The helper creates a fresh `Redis` client for the subscriber, spawns a Ruby `Thread`, and inside that thread calls `redis.subscribe(*channels)` (or `redis.psubscribe(*patterns)`), which blocks the connection in subscribe-only mode\n3. The application (or another process) calls `hub.publish(channel, message)` on the hub's plain (non-subscriber) Redis client\n4. Redis fans the message out over every subscribing client's open socket\n5. Each subscriber's worker thread receives the message inside the `on.message` / `on.pmessage` callback, wraps it as a `ReceivedMessage`, and pushes it into a per-subscriber ring buffer\n6. The publisher receives the integer subscriber count back from `PUBLISH`, which is the number of clients Redis delivered to right then\n\nPattern subscriptions match channels by glob (`*`, `?`, `[abc]`). A single message that matches both an exact subscription and a pattern subscription is delivered twice — once as a `message` and once as a `pmessage`."},{"id":"the-pubsub-hub-helper","title":"The pub/sub hub helper","role":"content","text":"The `RedisPubSubHub` class wraps the publish, subscribe, and introspection operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/pub-sub/ruby/pubsub_hub.rb)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Pub/sub has no Redis keyspace footprint of its own — channels are server-side routing entries, not stored values. The hub keeps its own bookkeeping in process memory:\n\n[code example]\n\nThe implementation uses:\n\n* [`PUBLISH`](https://redis.io/docs/latest/commands/publish) to fan a JSON-encoded message out to every subscriber of a channel\n* [`SUBSCRIBE`](https://redis.io/docs/latest/commands/subscribe) for exact-match subscribers\n* [`PSUBSCRIBE`](https://redis.io/docs/latest/commands/psubscribe) for glob-style pattern subscribers\n* [`PUBSUB CHANNELS`](https://redis.io/docs/latest/commands/pubsub-channels) to list the channels with at least one active exact-match subscriber\n* [`PUBSUB NUMSUB`](https://redis.io/docs/latest/commands/pubsub-numsub) to count subscribers per channel\n* [`PUBSUB NUMPAT`](https://redis.io/docs/latest/commands/pubsub-numpat) to count active pattern subscriptions server-wide\n* A Ruby `Thread` per subscriber, each with its own `Redis` instance, so `redis.subscribe` can monopolise its connection without blocking the rest of the process"},{"id":"publishing-messages","title":"Publishing messages","role":"content","text":"`publish` JSON-encodes the message body, calls `PUBLISH`, and updates the per-channel publish counter:\n\n[code example]\n\nThe integer returned by `PUBLISH` is what Redis itself reports — the number of subscribers (direct and pattern) that received the message in that call. It's a useful sanity check that the channel name is actually being listened to: a steady stream of `0`s means you have a typo somewhere or your subscriber crashed."},{"id":"subscribing-to-channels","title":"Subscribing to channels","role":"content","text":"`subscribe` creates a named in-process `Subscription` that owns its own `Redis` client and a worker thread. Inside `Subscription#initialize` (the relevant excerpt):\n\n[code example]\n\nA few details matter here:\n\n* **Each Subscription gets its own Redis client.** In redis-rb 5.x, `Redis#subscribe` puts its connection into subscribe-only mode: ordinary commands (`GET`, `HSET`) on the same connection will raise. Sharing one client across subscribers would also couple their lifetimes — unsubscribing one would close the channel for the others.\n* **The thread blocks inside `subscribe`.** Unlike redis-py's `pubsub.run_in_thread()`, redis-rb's subscribe loop *is* the worker — it doesn't return until the server has unsubscribed the connection. That's why the helper spawns one `Thread` per subscriber and stores it.\n* **A `Queue` latch lets the constructor wait for the first SUBSCRIBE ack.** Without it, a `publish` issued immediately after `subscribe` could race the subscribe handshake and arrive before Redis knew about the client. Blocking on the `on.subscribe` callback before returning from `initialize` removes that race."},{"id":"pattern-subscriptions-with-psubscribe","title":"Pattern subscriptions with PSUBSCRIBE","role":"content","text":"`psubscribe` works the same way but routes messages through `PSUBSCRIBE` so each binding is a glob, not a literal channel name:\n\n[code example]\n\nWhen a published channel matches a pattern, redis-rb invokes the `on.pmessage` callback with both the matched channel and the original pattern:\n\n[code example]\n\nThat distinction is useful for routing: a pattern subscriber can do one thing for the whole hierarchy (e.g., increment a counter) and dispatch on the specific channel within its callback (e.g., \"invalidate this region's cache\")."},{"id":"unsubscribing-without-sentinel-messages","title":"Unsubscribing without sentinel messages","role":"content","text":"`hub.unsubscribe(name)` (and `hub.shutdown` for everything) needs to break the worker thread out of its blocking `subscribe` call. The cleanest way in redis-rb 5.x is to call `unsubscribe` (or `punsubscribe`) on the *same* subscriber connection from another thread — redis-rb sends the command down the open subscribe socket, Redis responds with the unsubscribe acknowledgement, the subscribe block returns, and the worker thread exits:\n\n[code example]\n\nThat avoids the older \"publish a STOP sentinel and unsubscribe from inside the handler\" pattern, which pollutes the user-facing channel namespace with control messages."},{"id":"inspecting-active-subscribers","title":"Inspecting active subscribers","role":"content","text":"Redis exposes a small set of pub/sub introspection commands that report on subscriber state without traversing any keyspace:\n\n[code example]\n\n`PUBSUB CHANNELS` only reports channels with at least one exact-match subscriber — pattern subscribers do not appear here. That's a deliberate Redis design choice: a glob like `*` would otherwise show up as a subscriber to every conceivable channel. `PUBSUB NUMPAT` covers the pattern side as a single global count.\n\nNote that redis-rb returns `PUBSUB NUMSUB` as an alternating `[channel, count, channel, count, …]` array; the helper pairs the elements up into a hash before returning to keep the wire shape consistent with the other clients."},{"id":"stats-and-history","title":"Stats and history","role":"content","text":"`stats` reports publish and receive counters plus the size of the subscription registry:\n\n[code example]\n\n`delivered_total` is what Redis itself counted; `received_total` is what this process's in-memory subscribers saw. In a single-process demo they should track each other closely — a sustained divergence usually means a callback raised, or a subscriber crashed while a publisher kept publishing. (Pub/sub is at-most-once: if your subscriber wasn't connected at publish time, the message is gone.)"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* Redis 6.2 or later running locally on the default port (6379). Earlier versions still work for plain `PUBLISH`/`SUBSCRIBE`; `PUBSUB NUMPAT` is older than that.\n* Ruby 3.0 or later.\n* The [`redis-rb`](https://github.com/redis/redis-rb) gem (5.x). Install it with:\n\n  [code example]\n\n* The [`webrick`](https://github.com/ruby/webrick) gem (it was extracted from the Ruby standard library in Ruby 3.0):\n\n  [code example]"},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of two Ruby files. Download them from the [`ruby` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/pub-sub/ruby) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From that directory:\n\n[code example]\n\nYou should see:\n\n[code example]\n\nOpen [http://127.0.0.1:8102](http://127.0.0.1:8102) in a browser. You can:\n\n* Publish messages of any text to any channel name in any batch size.\n* Add named subscribers that listen on either a specific channel (`orders:new`) or a glob pattern (`notifications:*`). A single subscriber can listen on multiple targets — enter them comma-separated.\n* Watch each subscriber's incoming-message panel update every 800 ms.\n* See the server-side view: `PUBSUB CHANNELS` lists exact-match channels with subscribers, `PUBSUB NUMSUB` gives per-channel counts, and `PUBSUB NUMPAT` counts active pattern subscriptions.\n* Click **Reset** to drop every subscription, zero the counters, and re-seed the three default subscribers.\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`. The HTTP bind host and port can be overridden with `--host` and `--port`."},{"id":"production-usage","title":"Production usage","role":"content","text":""},{"id":"pubsub-is-at-most-once--pair-it-with-durable-state-if-you-need-replay","title":"Pub/sub is at-most-once — pair it with durable state if you need replay","role":"content","text":"A subscriber that's offline when a message is published misses it permanently. For events you can't afford to lose, write the durable record (the order row, the cache key version, the audit log entry) to its primary store, then `PUBLISH` a notification so live consumers can pick it up immediately. On reconnect, consumers reconcile by reading the durable store, not by waiting for missed pub/sub messages. If you actually need replay or at-least-once delivery, switch to [Redis Streams](https://redis.io/docs/latest/develop/data-types/streams) with consumer groups."},{"id":"give-every-subscriber-its-own-redis-connection","title":"Give every subscriber its own Redis connection","role":"content","text":"In redis-rb 5.x, `Redis#subscribe` blocks the calling thread *and* monopolises the underlying connection — any non-pub/sub command issued on the same client will raise. The helper sidesteps that by creating a fresh `Redis` client for every `Subscription`, and using a separate plain client on the hub for `PUBLISH` and the `PUBSUB CHANNELS` / `NUMSUB` / `NUMPAT` introspection commands. In a long-running service, treat each subscriber as owning its connection for the duration of its subscription, and pool connections separately for command-mode work."},{"id":"choose-a-topic-naming-convention-up-front","title":"Choose a topic naming convention up front","role":"content","text":"A flat namespace gets ugly fast — `email`, `email_high_priority`, `email_high_priority_billing`. Pick a colon-separated hierarchy (`notifications:billing:invoice`, `cache:invalidate:products:p-001`) so consumers can subscribe at the right level: a billing service uses `notifications:billing:*`, the audit logger uses `notifications:*`. Glob patterns are evaluated for every published message, so don't go wild with multiple wildcards on hot paths — `*:*:*` matches everything and costs more than a flat `notifications:*` would."},{"id":"dont-do-heavy-work-in-the-subscribe-block","title":"Don't do heavy work in the subscribe block","role":"content","text":"The worker thread reads messages from a single socket inside the `on.message` / `on.pmessage` callback. If the callback blocks (synchronous HTTP call, big computation, slow DB write), the next message waits behind it and the subscriber's effective throughput drops to whatever the callback's latency is. For heavier work, the callback should hand the message off to a worker pool (a [`Concurrent::ThreadPoolExecutor`](https://github.com/ruby-concurrency/concurrent-ruby), a Sidekiq job, …) or — for true durable handoff — a [Redis Streams](https://redis.io/docs/latest/develop/data-types/streams) consumer group."},{"id":"tune-the-subscriber-buffer-for-your-traffic-shape","title":"Tune the subscriber buffer for your traffic shape","role":"content","text":"The demo caps each subscriber's in-memory message buffer at 50. That's right for showing the recent activity in a UI, but a real subscriber typically processes each message and discards it — the buffer is only there for human inspection. If you keep a buffer, make sure it's bounded; an unbounded ring on a chatty pattern subscriber will eventually OOM the process."},{"id":"use-the-gil-friendly-cooperative-concurrency-model","title":"Use the GIL-friendly cooperative concurrency model","role":"content","text":"Ruby (MRI) threads release the GIL on blocking I/O, so a per-subscriber thread sleeping inside a socket read does not stall the rest of the process. That's why one Ruby thread per active subscriber is a workable model even at hundreds of subscribers — you're not consuming a CPU thread, just a `Redis` connection. If you instead need thousands of subscribers per process, consider switching to one of the fibre-based async drivers (e.g. [`async-redis`](https://github.com/socketry/async-redis)) so subscriber sockets can multiplex inside a single reactor."},{"id":"sharded-pubsub-on-a-redis-cluster","title":"Sharded pub/sub on a Redis Cluster","role":"content","text":"On a Redis Cluster, plain `PUBLISH` fans every message out to every node via the cluster bus, which becomes a hotspot at high throughput. Redis 7.0 added [sharded pub/sub](https://redis.io/docs/latest/develop/pubsub#sharded-pubsub): channels are hashed to slots, and `SPUBLISH` / `SSUBSCRIBE` only touch the shard that owns the slot. If you're scaling pub/sub on a cluster, prefer the sharded commands and pick channel names whose hash distribution matches your traffic."},{"id":"inspect-pubsub-state-directly-in-redis","title":"Inspect pub/sub state directly in Redis","role":"content","text":"Because pub/sub has no keyspace, `KEYS`/`SCAN` won't show you anything. Use the introspection commands instead:\n\n[code example]\n\n`redis-cli` in subscribe mode only exits with `Ctrl-C` — it can't issue any other commands while subscribed."},{"id":"learn-more","title":"Learn more","role":"related","text":"This example uses the following Redis commands:\n\n* [`PUBLISH`](https://redis.io/docs/latest/commands/publish) to fan a message out to every subscriber of a channel.\n* [`SUBSCRIBE`](https://redis.io/docs/latest/commands/subscribe) and [`UNSUBSCRIBE`](https://redis.io/docs/latest/commands/unsubscribe) for exact-match topic subscriptions.\n* [`PSUBSCRIBE`](https://redis.io/docs/latest/commands/psubscribe) and [`PUNSUBSCRIBE`](https://redis.io/docs/latest/commands/punsubscribe) for glob-style pattern subscriptions.\n* [`PUBSUB CHANNELS`](https://redis.io/docs/latest/commands/pubsub-channels) to list channels with at least one active exact-match subscriber.\n* [`PUBSUB NUMSUB`](https://redis.io/docs/latest/commands/pubsub-numsub) to count subscribers per named channel.\n* [`PUBSUB NUMPAT`](https://redis.io/docs/latest/commands/pubsub-numpat) to count active pattern subscriptions server-wide.\n\nSee the [`redis-rb` documentation](https://redis.io/docs/latest/develop/clients/ruby) for full client reference, including the [`Redis#subscribe`](https://www.rubydoc.info/gems/redis/Redis#subscribe-instance_method) and [`Redis#psubscribe`](https://www.rubydoc.info/gems/redis/Redis#psubscribe-instance_method) helpers."}],"examples":[{"id":"the-pubsub-hub-helper-ex0","language":"ruby","code":"require 'redis'\nrequire_relative 'pubsub_hub'\n\nhub = PubSubHub::RedisPubSubHub.new(\n  redis_options: { host: 'localhost', port: 6379 }\n)\n\n# Exact-match subscriber\nhub.subscribe(name: 'orders-listener', channels: ['orders:new'])\n\n# Pattern subscriber covering an entire topic hierarchy\nhub.psubscribe(name: 'all-notifications', patterns: ['notifications:*'])\n\n# Publish — returns Redis' delivered count for this PUBLISH\ndelivered = hub.publish('orders:new', { 'order_id' => 42, 'total' => 199.0 })\nputs \"Redis delivered to #{delivered} subscriber(s)\"\n\n# Look at what each subscriber received\nhub.subscriptions.each do |sub|\n  puts \"#{sub.name} #{sub.received_total} messages\"\n  sub.messages(5).each do |message|\n    puts \"  #{message.channel} #{message.payload.inspect}\"\n  end\nend\n\nhub.unsubscribe('orders-listener')\nhub.shutdown  # closes every remaining subscription","section_id":"the-pubsub-hub-helper"},{"id":"data-model-ex0","language":"text","code":"RedisPubSubHub                          (in-process)\n  subscriptions             Hash{String => Subscription}\n  published_total           Integer\n  delivered_total           Integer\n  channel_published         Hash{channel => count}\n\nSubscription                            (in-process, one per subscriber)\n  name                      String\n  targets                   Array<channel | pattern>\n  is_pattern?               Boolean\n  buffer                    Array<ReceivedMessage>     (capped, default 50)\n  received_total            Integer\n  redis                     Redis                       (its own connection)\n  thread                    Thread                       (blocks on subscribe)","section_id":"data-model"},{"id":"publishing-messages-ex0","language":"ruby","code":"def publish(channel, message)\n  payload = JSON.generate(message)\n  delivered = @redis.publish(channel, payload).to_i\n  @stats_lock.synchronize do\n    @published_total += 1\n    @delivered_total += delivered\n    @channel_published[channel] += 1\n  end\n  delivered\nend","section_id":"publishing-messages"},{"id":"subscribing-to-channels-ex0","language":"ruby","code":"@redis = Redis.new(redis_options)\n@thread = Thread.new do\n  @redis.subscribe(*@targets) do |on|\n    on.subscribe   { |_channel, _count| @ready_latch.push(true) }\n    on.message     { |channel, raw|    dispatch(channel: channel, pattern: nil, raw: raw) }\n    on.unsubscribe { |_channel, _count| }\n  end\nend\n@ready_latch.pop  # block until the server acks SUBSCRIBE","section_id":"subscribing-to-channels"},{"id":"pattern-subscriptions-with-psubscribe-ex0","language":"ruby","code":"hub.psubscribe(name: 'all-notifications', patterns: ['notifications:*'])\nhub.psubscribe(name: 'cache-invalidator', patterns: ['cache:invalidate:*'])","section_id":"pattern-subscriptions-with-psubscribe"},{"id":"pattern-subscriptions-with-psubscribe-ex1","language":"ruby","code":"@redis.psubscribe(*@targets) do |on|\n  on.pmessage do |pattern, channel, raw|\n    dispatch(channel: channel, pattern: pattern, raw: raw)\n  end\nend","section_id":"pattern-subscriptions-with-psubscribe"},{"id":"unsubscribing-without-sentinel-messages-ex0","language":"ruby","code":"def close\n  return if @closed\n  @closed = true\n  if @is_pattern\n    @redis.punsubscribe\n  else\n    @redis.unsubscribe\n  end\n  @thread.join(2)\n  @redis.close\nend","section_id":"unsubscribing-without-sentinel-messages"},{"id":"inspecting-active-subscribers-ex0","language":"ruby","code":"hub.active_channels                       # PUBSUB CHANNELS *\nhub.channel_subscriber_counts(channels)   # PUBSUB NUMSUB ch1 ch2 ...\nhub.pattern_subscriber_count              # PUBSUB NUMPAT","section_id":"inspecting-active-subscribers"},{"id":"stats-and-history-ex0","language":"ruby","code":"def stats\n  published_total, delivered_total, channel_published =\n    @stats_lock.synchronize do\n      [@published_total, @delivered_total, @channel_published.dup]\n    end\n  subs = subscriptions\n  {\n    'published_total'       => published_total,\n    'delivered_total'       => delivered_total,           # sum of PUBLISH return values\n    'received_total'        => subs.sum(&:received_total),\n    'active_subscriptions'  => subs.length,\n    'channel_published'     => channel_published,\n    'pattern_subscriptions' => pattern_subscriber_count\n  }\nend","section_id":"stats-and-history"},{"id":"prerequisites-ex0","language":"bash","code":"gem install redis","section_id":"prerequisites"},{"id":"prerequisites-ex1","language":"bash","code":"gem install webrick","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir pub-sub-demo && cd pub-sub-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/pub-sub/ruby\ncurl -O $BASE/pubsub_hub.rb\ncurl -O $BASE/demo_server.rb","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"gem install redis webrick\nruby demo_server.rb","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"Redis pub/sub demo server listening on http://127.0.0.1:8102\nUsing Redis at localhost:6379\nSeeded 3 default subscription(s)","section_id":"start-the-demo-server"},{"id":"inspect-pubsub-state-directly-in-redis-ex0","language":"bash","code":"# Which channels currently have at least one exact-match subscriber?\nredis-cli pubsub channels '*'\n\n# How many subscribers does each channel have?\nredis-cli pubsub numsub orders:new notifications:billing chat:lobby\n\n# How many active pattern subscriptions across the whole server?\nredis-cli pubsub numpat\n\n# Subscribe interactively from the CLI to watch traffic on a pattern\nredis-cli psubscribe 'orders:*'","section_id":"inspect-pubsub-state-directly-in-redis"}]}
{"schema_version":2,"id":"develop/use-cases/pub-sub/rust","title":"Redis pub/sub with redis-rs","url":"https://redis.io/docs/latest/develop/use-cases/pub-sub/rust/","summary":"Implement Redis pub/sub messaging in Rust with redis-rs","aliases":["/develop/use-cases/pub-sub/redis-rs"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-17T10:28:27+01:00","children":[],"page_type":"content","content_hash":"533b716be2fab617c6e50f6421407a83727b5124ad1c63a9ae8b983171696bc0","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis-backed pub/sub broadcaster in Rust with the [redis](https://crates.io/crates/redis) crate (redis-rs). It includes a small local web server built on [axum](https://github.com/tokio-rs/axum) so you can publish messages to named channels, add and remove subscribers live, and watch Redis fan out each message to every interested listener."},{"id":"overview","title":"Overview","role":"overview","text":"Pub/sub lets your application broadcast events — chat messages, cache invalidation signals, presence updates, notifications — to many consumers without per-pair wiring. The publisher names a *channel*; every client currently subscribed to that channel receives the message, in publish order, with sub-millisecond fan-out.\n\nThat gives you:\n\n* Many-to-many event delivery with no message storage cost in Redis\n* Exact-match subscriptions (`SUBSCRIBE orders:new`) for known topics\n* Pattern subscriptions (`PSUBSCRIBE notifications:*`) for whole topic hierarchies\n* Live server-side introspection through `PUBSUB CHANNELS`, `PUBSUB NUMSUB`, and `PUBSUB NUMPAT`\n* At-most-once delivery: subscribers that are offline when a message is published miss it, so durable state should live in keys or a Stream, not in the pub/sub channel itself\n\nIn this example, the publisher side calls `PUBLISH` with a JSON-encoded body and counts how many subscribers Redis reported delivering to. Each in-process subscriber owns its own Redis pub/sub connection and a Tokio task that pumps incoming messages into a ring buffer."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. The application calls `hub.subscribe(name, channels)` or `hub.psubscribe(name, patterns)`\n2. The helper opens a dedicated `redis::aio::PubSub` connection from the shared `Client`, calls `subscribe` or `psubscribe` for each target, and spawns a Tokio task that loops on `pubsub.on_message()`\n3. The application (or another process) calls `hub.publish(channel, &message)`\n4. Redis fans the message out over every subscribing client's open socket\n5. Each subscriber's task wraps the raw message as a `ReceivedMessage`, prepends it to a per-subscriber ring buffer, and bumps the received counter\n6. The publisher receives the integer subscriber count back from `PUBLISH`, which is the number of clients Redis delivered to right then\n\nPattern subscriptions match channels by glob (`*`, `?`, `[abc]`). A single message that matches both an exact subscription and a pattern subscription is delivered twice — once as a `message` and once as a `pmessage`."},{"id":"the-pubsub-hub-helper","title":"The pub/sub hub helper","role":"content","text":"The `PubSubHub` struct wraps the publish, subscribe, and introspection operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/pub-sub/rust/pubsub_hub.rs)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Pub/sub has no Redis keyspace footprint of its own — channels are server-side routing entries, not stored values. The hub keeps its own bookkeeping in process memory:\n\n[code example]\n\nThe implementation uses:\n\n* [`PUBLISH`](https://redis.io/docs/latest/commands/publish) to fan a JSON-encoded message out to every subscriber of a channel\n* [`SUBSCRIBE`](https://redis.io/docs/latest/commands/subscribe) for exact-match subscribers\n* [`PSUBSCRIBE`](https://redis.io/docs/latest/commands/psubscribe) for glob-style pattern subscribers\n* [`PUBSUB CHANNELS`](https://redis.io/docs/latest/commands/pubsub-channels) to list the channels with at least one active exact-match subscriber\n* [`PUBSUB NUMSUB`](https://redis.io/docs/latest/commands/pubsub-numsub) to count subscribers per channel\n* [`PUBSUB NUMPAT`](https://redis.io/docs/latest/commands/pubsub-numpat) to count active pattern subscriptions server-wide\n* `redis::aio::PubSub::on_message()` to obtain a `Stream<Item = Msg>` and forward each delivered message into the subscriber's buffer"},{"id":"publishing-messages","title":"Publishing messages","role":"content","text":"`publish()` JSON-encodes the message body, calls `PUBLISH`, and updates the per-channel publish counter:\n\n[code example]\n\nThe integer returned by `PUBLISH` is what Redis itself reports — the number of subscribers (direct and pattern) that received the message in that call. It's a useful sanity check that the channel name is actually being listened to: a steady stream of `0`s means you have a typo somewhere or your subscriber crashed.\n\nA single `ConnectionManager` is reused for every publish. `ConnectionManager` is `Clone`, transparently reconnects on transient failures, and multiplexes commands over a single TCP connection, so it's the right shared resource for non-blocking commands. Pub/sub connections, in contrast, must be dedicated per subscriber — see below."},{"id":"subscribing-to-channels","title":"Subscribing to channels","role":"content","text":"`subscribe()` creates a named in-process `Subscription` that owns its own pub/sub connection and dispatch task:\n\n[code example]\n\nInside `register`, the helper opens a fresh pub/sub connection, binds each target, and spawns a Tokio task to pump messages:\n\n[code example]\n\nA few details matter here:\n\n* `client.get_async_pubsub()` returns a `redis::aio::PubSub` that owns its own connection. The connection switches into subscribe-only mode the moment you call `subscribe`/`psubscribe`, so it cannot be reused for ordinary commands — that's why the hub keeps a separate `ConnectionManager` for `PUBLISH` and the introspection commands.\n* `pubsub.on_message()` returns a `Stream<Item = Msg>` that **borrows** from `pubsub`. The spawned task therefore has to own the `PubSub` and use the stream locally; if you tried to move the `PubSub` out of the task mid-loop, the borrow checker would refuse. Confining the stream to an inner block lets the task call `unsubscribe` on the `PubSub` after the loop ends.\n* `tokio::select!` with a `oneshot::Receiver<()>` gives clean shutdown. When `close()` fires the sender, the loop exits at the next poll, the task unsubscribes for tidiness, and the `PubSub` is dropped — Redis releases the channel slot as soon as the socket closes."},{"id":"pattern-subscriptions-with-psubscribe","title":"Pattern subscriptions with PSUBSCRIBE","role":"content","text":"`psubscribe()` works the same way but routes each binding through `PSUBSCRIBE` so it's a glob, not a literal channel name:\n\n[code example]\n\nWhen a published channel matches a pattern, the `redis::Msg` carries both the matched channel and the original pattern. `dispatch()` lifts them onto the `ReceivedMessage`:\n\n[code example]\n\nThat distinction is useful for routing: a pattern subscriber can do one thing for the whole hierarchy (e.g., increment a counter) and dispatch on the specific channel within its callback (e.g., \"invalidate this region's cache\")."},{"id":"inspecting-active-subscribers","title":"Inspecting active subscribers","role":"content","text":"Redis exposes a small set of pub/sub introspection commands that report on subscriber state without traversing any keyspace. The hub wraps them as plain async methods:\n\n[code example]\n\n`PUBSUB CHANNELS` only reports channels with at least one exact-match subscriber — pattern subscribers do not appear here. That's a deliberate Redis design choice: a glob like `*` would otherwise show up as a subscriber to every conceivable channel. `PUBSUB NUMPAT` covers the pattern side as a single global count."},{"id":"stats-and-history","title":"Stats and history","role":"content","text":"`stats()` reports publish and receive counters plus the size of the subscription registry:\n\n[code example]\n\n`delivered_total` is what Redis itself counted; `received_total` is what this process's in-memory subscribers saw. In a single-process demo they should track each other closely — a sustained divergence usually means a dispatch task panicked, or a subscriber's task died while a publisher kept publishing. (Pub/sub is at-most-once: if your subscriber wasn't connected at publish time, the message is gone.)"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* Redis 6.2 or later running locally on the default port (6379). Earlier versions still work for plain `PUBLISH`/`SUBSCRIBE`; `PUBSUB NUMPAT` is older than that.\n* A Rust toolchain (rustup + cargo) capable of building the 2021 edition.\n* The `redis` crate at version 0.27+ with the `tokio-comp`, `aio`, and `connection-manager` features, plus `tokio`, `axum`, `serde`, `serde_json`, and `futures-util` (declared in `Cargo.toml`)."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of three files. Download them from the [`rust` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/pub-sub/rust) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From that directory:\n\n[code example]\n\nYou should see:\n\n[code example]\n\nOpen [http://127.0.0.1:8103](http://127.0.0.1:8103) in a browser. You can:\n\n* Publish messages of any text to any channel name in any batch size.\n* Add named subscribers that listen on either a specific channel (`orders:new`) or a glob pattern (`notifications:*`). A single subscriber can listen on multiple targets — enter them comma-separated.\n* Watch each subscriber's incoming-message panel update every 800 ms.\n* See the server-side view: `PUBSUB CHANNELS` lists exact-match channels with subscribers, `PUBSUB NUMSUB` gives per-channel counts, and `PUBSUB NUMPAT` counts active pattern subscriptions.\n* Click **Reset** to drop every subscription, zero the counters, and re-seed the three default subscribers.\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`, or use the `REDIS_HOST` / `REDIS_PORT` environment variables. `--port` and `--host` change the HTTP bind."},{"id":"production-usage","title":"Production usage","role":"content","text":""},{"id":"pubsub-is-at-most-once--pair-it-with-durable-state-if-you-need-replay","title":"Pub/sub is at-most-once — pair it with durable state if you need replay","role":"content","text":"A subscriber that's offline when a message is published misses it permanently. For events you can't afford to lose, write the durable record (the order row, the cache key version, the audit log entry) to its primary store, then `PUBLISH` a notification so live consumers can pick it up immediately. On reconnect, consumers reconcile by reading the durable store, not by waiting for missed pub/sub messages. If you actually need replay or at-least-once delivery, switch to [Redis Streams](https://redis.io/docs/latest/develop/data-types/streams) with consumer groups."},{"id":"use-a-separate-connection-per-subscriber","title":"Use a separate connection per subscriber","role":"content","text":"A `redis::aio::PubSub` switches its connection into subscribe-only mode: ordinary commands (`GET`, `HSET`, etc.) on the same connection will fail. Always create the `PubSub` from a `Client` that can spare another connection, and — as the helper does — give every subscriber its own `PubSub`. Sharing one `PubSub` across business-logic subscribers couples their lifetimes (closing one closes the channel for the others) and serialises their dispatch on a single tokio task.\n\nFor everything that *isn't* a subscribe — `PUBLISH`, `PUBSUB CHANNELS`, `PUBSUB NUMSUB`, `PUBSUB NUMPAT` — use the shared `ConnectionManager`. It transparently reconnects on transient failures and is cheap to clone for use across many concurrent tokio tasks. For very high-throughput workloads, look at [`bb8-redis`](https://crates.io/crates/bb8-redis) or [`deadpool-redis`](https://crates.io/crates/deadpool-redis) for explicit pooling."},{"id":"stream-borrowing--the-on_message-pattern","title":"Stream borrowing — the on_message() pattern","role":"content","text":"`pubsub.on_message()` returns a `Stream<Item = redis::Msg>` whose lifetime is tied to the `PubSub`. That means you cannot move the `PubSub` out of the spawned task while the stream is live — the borrow checker will refuse. The two workable patterns are:\n\n* Own the `PubSub` inside the task and confine `on_message()` to an inner scope, the way the helper does. That lets you call `unsubscribe` on the `PubSub` after the loop ends and before the connection drops.\n* Convert each incoming `Msg` into an owned `ReceivedMessage` immediately, so the task never has to hand a borrowed value across `await` points.\n\nDon't try to pass the stream itself across thread boundaries; copy the data out, push it into a `tokio::sync::mpsc` channel, or — as here — into a `Mutex<VecDeque>`, and process from there."},{"id":"choose-a-topic-naming-convention-up-front","title":"Choose a topic naming convention up front","role":"content","text":"A flat namespace gets ugly fast — `email`, `email_high_priority`, `email_high_priority_billing`. Pick a colon-separated hierarchy (`notifications:billing:invoice`, `cache:invalidate:products:p-001`) so consumers can subscribe at the right level: a billing service uses `notifications:billing:*`, the audit logger uses `notifications:*`. Glob patterns are evaluated for every published message, so don't go wild with multiple wildcards on hot paths — `*:*:*` matches everything and costs more than a flat `notifications:*` would."},{"id":"dont-do-heavy-work-in-the-dispatch-task","title":"Don't do heavy work in the dispatch task","role":"content","text":"Each subscriber's task reads messages from a single socket. If the work it does per message blocks (synchronous HTTP call inside the loop, big computation, slow DB write through a sync API), the next message waits behind it and the subscriber's effective throughput drops to whatever that latency is. For heavier work, the dispatch should hand the message off to a worker pool — a `tokio::sync::mpsc` to a separate set of consumer tasks, an [`axum`](https://github.com/tokio-rs/axum) background task, or — for true durable handoff — a [Redis Streams](https://redis.io/docs/latest/develop/data-types/streams) consumer group."},{"id":"tune-the-subscriber-buffer-for-your-traffic-shape","title":"Tune the subscriber buffer for your traffic shape","role":"content","text":"The demo caps each subscriber's in-memory message buffer at 50. That's right for showing the recent activity in a UI, but a real subscriber typically processes each message and discards it — the buffer is only there for human inspection. If you keep a buffer, make sure it's bounded; an unbounded `VecDeque` on a chatty pattern subscriber will eventually OOM the process."},{"id":"sharded-pubsub-on-a-redis-cluster","title":"Sharded pub/sub on a Redis Cluster","role":"content","text":"On a Redis Cluster, plain `PUBLISH` fans every message out to every node via the cluster bus, which becomes a hotspot at high throughput. Redis 7.0 added [sharded pub/sub](https://redis.io/docs/latest/develop/pubsub#sharded-pubsub): channels are hashed to slots, and `SPUBLISH` / `SSUBSCRIBE` only touch the shard that owns the slot. If you're scaling pub/sub on a cluster, prefer the sharded commands and pick channel names whose hash distribution matches your traffic."},{"id":"inspect-pubsub-state-directly-in-redis","title":"Inspect pub/sub state directly in Redis","role":"content","text":"Because pub/sub has no keyspace, `KEYS`/`SCAN` won't show you anything. Use the introspection commands instead:\n\n[code example]\n\n`redis-cli` in subscribe mode only exits with `Ctrl-C` — it can't issue any other commands while subscribed."},{"id":"learn-more","title":"Learn more","role":"related","text":"This example uses the following Redis commands:\n\n* [`PUBLISH`](https://redis.io/docs/latest/commands/publish) to fan a message out to every subscriber of a channel.\n* [`SUBSCRIBE`](https://redis.io/docs/latest/commands/subscribe) and [`UNSUBSCRIBE`](https://redis.io/docs/latest/commands/unsubscribe) for exact-match topic subscriptions.\n* [`PSUBSCRIBE`](https://redis.io/docs/latest/commands/psubscribe) and [`PUNSUBSCRIBE`](https://redis.io/docs/latest/commands/punsubscribe) for glob-style pattern subscriptions.\n* [`PUBSUB CHANNELS`](https://redis.io/docs/latest/commands/pubsub-channels) to list channels with at least one active exact-match subscriber.\n* [`PUBSUB NUMSUB`](https://redis.io/docs/latest/commands/pubsub-numsub) to count subscribers per named channel.\n* [`PUBSUB NUMPAT`](https://redis.io/docs/latest/commands/pubsub-numpat) to count active pattern subscriptions server-wide.\n\nSee the [redis crate on crates.io](https://crates.io/crates/redis) for the full client reference, including the [`redis::aio::PubSub`](https://docs.rs/redis/latest/redis/aio/struct.PubSub.html) type and the [`Stream`-based `on_message`](https://docs.rs/redis/latest/redis/aio/struct.PubSub.html#method.on_message) helper."}],"examples":[{"id":"the-pubsub-hub-helper-ex0","language":"rust","code":"use redis::Client;\nuse serde_json::json;\n\nmod pubsub_hub;\nuse pubsub_hub::{PubSubHub, DEFAULT_BUFFER_SIZE};\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn std::error::Error>> {\n    let client = Client::open(\"redis://localhost:6379/\")?;\n    let hub = PubSubHub::new(client, DEFAULT_BUFFER_SIZE).await?;\n\n    // Exact-match subscriber\n    hub.subscribe(\"orders-listener\", vec![\"orders:new\".to_string()]).await?;\n\n    // Pattern subscriber covering an entire topic hierarchy\n    hub.psubscribe(\"all-notifications\", vec![\"notifications:*\".to_string()]).await?;\n\n    // Publish — returns Redis' delivered count for this PUBLISH\n    let delivered = hub.publish(\"orders:new\", &json!({ \"order_id\": 42, \"total\": 199.0 })).await?;\n    println!(\"Redis delivered to {} subscriber(s)\", delivered);\n\n    // Look at what each subscriber received\n    for sub in hub.subscriptions() {\n        println!(\"{} {} messages\", sub.name(), sub.received_total());\n        for m in sub.messages(Some(5)) {\n            println!(\"  {} {}\", m.channel, m.payload);\n        }\n    }\n\n    hub.unsubscribe(\"orders-listener\").await;\n    hub.shutdown().await;  // closes every remaining subscription\n    Ok(())\n}","section_id":"the-pubsub-hub-helper"},{"id":"data-model-ex0","language":"text","code":"PubSubHub                                (in-process, Arc<HubInner>)\n  subscriptions             HashMap<String, Arc<Subscription>>\n  published_total           AtomicU64\n  delivered_total           AtomicU64\n  channel_published         HashMap<channel, count>\n\nSubscription                             (in-process, one per subscriber)\n  name                      String\n  targets                   Vec<String>\n  is_pattern                bool\n  buffer                    VecDeque<ReceivedMessage>     (capped, default 50)\n  received                  AtomicU64\n  pubsub                    redis::aio::PubSub             (owned by a tokio task)\n  stop_tx                   oneshot::Sender<()>            (for clean shutdown)","section_id":"data-model"},{"id":"publishing-messages-ex0","language":"rust","code":"pub async fn publish(&self, channel: &str, message: &Value) -> RedisResult<i64> {\n    let payload = serde_json::to_string(message)\n        .unwrap_or_else(|_| serde_json::to_string(&Value::Null).unwrap());\n    let mut conn = self.inner.publisher.clone();\n    let delivered: i64 = conn.publish(channel, payload).await?;\n    self.inner.published_total.fetch_add(1, Ordering::Relaxed);\n    self.inner.delivered_total.fetch_add(delivered as u64, Ordering::Relaxed);\n    let mut chan = self.inner.channel_published.lock().unwrap();\n    *chan.entry(channel.to_string()).or_insert(0) += 1;\n    Ok(delivered)\n}","section_id":"publishing-messages"},{"id":"subscribing-to-channels-ex0","language":"rust","code":"pub async fn subscribe(\n    &self,\n    name: &str,\n    channels: Vec<String>,\n) -> Result<Arc<Subscription>, HubError> {\n    self.register(name, channels, false).await\n}","section_id":"subscribing-to-channels"},{"id":"subscribing-to-channels-ex1","language":"rust","code":"let mut pubsub = self.inner.client.get_async_pubsub().await?;\nif is_pattern {\n    for pattern in &targets { pubsub.psubscribe(pattern).await?; }\n} else {\n    for channel in &targets { pubsub.subscribe(channel).await?; }\n}\n\nlet (stop_tx, stop_rx) = oneshot::channel();\nlet sub_for_task = sub.clone();\nlet handle = tokio::spawn(async move {\n    {\n        let mut stop_rx = stop_rx;\n        let mut stream = pubsub.on_message();\n        loop {\n            tokio::select! {\n                biased;\n                _ = &mut stop_rx => break,\n                next = stream.next() => match next {\n                    Some(msg) => dispatch(&sub_for_task, &msg),\n                    None => break,\n                }\n            }\n        }\n    }\n    // After the stream borrow ends, unsubscribe cleanly before drop.\n    for target in &targets_for_task {\n        if is_pattern {\n            let _ = pubsub.punsubscribe(target).await;\n        } else {\n            let _ = pubsub.unsubscribe(target).await;\n        }\n    }\n});","section_id":"subscribing-to-channels"},{"id":"pattern-subscriptions-with-psubscribe-ex0","language":"rust","code":"hub.psubscribe(\"all-notifications\", vec![\"notifications:*\".to_string()]).await?;\nhub.psubscribe(\"cache-invalidator\", vec![\"cache:invalidate:*\".to_string()]).await?;","section_id":"pattern-subscriptions-with-psubscribe"},{"id":"pattern-subscriptions-with-psubscribe-ex1","language":"rust","code":"fn dispatch(sub: &Arc<Subscription>, msg: &redis::Msg) {\n    let channel = msg.get_channel_name().to_string();\n    let pattern = msg.get_pattern::<String>().ok();\n    let raw_payload: String = msg.get_payload().unwrap_or_default();\n    let payload: Value = serde_json::from_str(&raw_payload).unwrap_or(Value::String(raw_payload));\n    // ... push onto the bounded ring buffer, bump the counter ...\n}","section_id":"pattern-subscriptions-with-psubscribe"},{"id":"inspecting-active-subscribers-ex0","language":"rust","code":"hub.active_channels(\"*\").await?;             // PUBSUB CHANNELS *\nhub.channel_subscriber_counts(&channels).await?;  // PUBSUB NUMSUB ch1 ch2 ...\nhub.pattern_subscriber_count().await?;       // PUBSUB NUMPAT","section_id":"inspecting-active-subscribers"},{"id":"stats-and-history-ex0","language":"rust","code":"pub async fn stats(&self) -> Stats {\n    let subs = self.subscriptions();\n    let received_total: u64 = subs.iter().map(|s| s.received_total()).sum();\n    let channel_published = self.inner.channel_published.lock().unwrap().clone();\n    let pattern_subscriptions = self.pattern_subscriber_count().await.unwrap_or(0);\n    Stats {\n        published_total: self.inner.published_total.load(Ordering::Relaxed),\n        delivered_total: self.inner.delivered_total.load(Ordering::Relaxed),\n        received_total,\n        active_subscriptions: subs.len() as u64,\n        channel_published,\n        pattern_subscriptions,\n    }\n}","section_id":"stats-and-history"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir pub-sub-demo && cd pub-sub-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/pub-sub/rust\ncurl -O $BASE/Cargo.toml\ncurl -O $BASE/pubsub_hub.rs\ncurl -O $BASE/demo_server.rs","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"cargo run --release","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"Redis pub/sub demo server listening on http://127.0.0.1:8103\nUsing Redis at localhost:6379\nSeeded 3 default subscription(s)","section_id":"start-the-demo-server"},{"id":"inspect-pubsub-state-directly-in-redis-ex0","language":"bash","code":"# Which channels currently have at least one exact-match subscriber?\nredis-cli pubsub channels '*'\n\n# How many subscribers does each channel have?\nredis-cli pubsub numsub orders:new notifications:billing chat:lobby\n\n# How many active pattern subscriptions across the whole server?\nredis-cli pubsub numpat\n\n# Subscribe interactively from the CLI to watch traffic on a pattern\nredis-cli psubscribe 'orders:*'","section_id":"inspect-pubsub-state-directly-in-redis"}]}
{"schema_version":2,"id":"develop/use-cases/rate-limiter/dotnet","title":"Token bucket rate limiter with Redis and .NET","url":"https://redis.io/docs/latest/develop/use-cases/rate-limiter/dotnet/","summary":"Implement a token bucket rate limiter using Redis and Lua scripts in .NET","aliases":["/develop/use-cases/rate-limiter/stackexchange.redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-29T14:34:47+01:00","children":[],"page_type":"content","content_hash":"695c6f68b4e61e6c705cac09b31fa72c8606bfeb70e67ed9d174bcd962194d89","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a distributed token bucket rate limiter using Redis and Lua scripts in .NET with the [`StackExchange.Redis`](https://redis.io/docs/latest/develop/clients/dotnet) client library."},{"id":"overview","title":"Overview","role":"overview","text":"Rate limiting is a critical technique for controlling the rate at which operations are performed. Common use cases include:\n\n* Limiting API requests per user or IP address\n* Preventing abuse and protecting against denial-of-service attacks\n* Ensuring fair resource allocation across multiple clients\n* Throttling background jobs or batch operations\n\nThe **token bucket algorithm** is a popular rate limiting approach that allows bursts of traffic while maintaining an average rate limit over time. This guide covers the .NET implementation using the [`StackExchange.Redis`](https://redis.io/docs/latest/develop/clients/dotnet) client library, taking advantage of `ConnectionMultiplexer` for connection management and `record` types for clean result types."},{"id":"how-it-works","title":"How it works","role":"content","text":"The token bucket algorithm works like a bucket that holds tokens:\n\n1. **Initialization**: The bucket starts with a maximum capacity of tokens\n2. **Refill**: Tokens are added to the bucket at a constant rate (for example, 1 token per second)\n3. **Consumption**: Each request consumes one token from the bucket\n4. **Decision**: If tokens are available, the request is allowed; otherwise, it's denied\n5. **Capacity limit**: The bucket never exceeds its maximum capacity\n\nThis approach allows for burst traffic (using accumulated tokens) while enforcing an average rate limit over time."},{"id":"why-use-redis","title":"Why use Redis?","role":"content","text":"Redis is ideal for distributed rate limiting because:\n\n* **Atomic operations**: Lua scripts execute atomically, preventing race conditions\n* **Shared state**: Multiple application servers can share the same rate limit counters\n* **High performance**: In-memory operations provide microsecond latency\n* **Automatic expiration**: Keys can be set to expire automatically (though not used in this implementation)"},{"id":"the-lua-script","title":"The Lua script","role":"content","text":"The core of this implementation is a Lua script that runs atomically on the Redis server. This ensures that checking and updating the token bucket happens in a single operation, preventing race conditions in distributed environments.\n\nHere's how the script works:\n\n[code example]"},{"id":"script-breakdown","title":"Script breakdown","role":"content","text":"1. **State retrieval**: Uses [`HMGET`](https://redis.io/docs/latest/commands/hmget) to fetch the current token count and last refill time from a hash\n2. **Initialization**: On first use, sets tokens to full capacity\n3. **Token refill calculation**: Computes how many tokens should be added based on elapsed time\n4. **Capacity enforcement**: Uses `math.min()` to ensure tokens never exceed capacity\n5. **Token consumption**: Decrements the token count if available\n6. **State update**: Uses [`HMSET`](https://redis.io/docs/latest/commands/hmset) to save the new state\n7. **Return value**: Returns both the decision (allowed/denied) and remaining tokens"},{"id":"why-atomicity-matters","title":"Why atomicity matters","role":"content","text":"Without atomic execution, race conditions could occur:\n\n* **Double spending**: Two requests could read the same token count and both succeed when only one should\n* **Lost updates**: Concurrent updates could overwrite each other's changes\n* **Inconsistent state**: Token count and refill time could become desynchronized\n\nUsing [`EVAL`](https://redis.io/docs/latest/commands/eval) or [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) ensures the entire operation executes atomically, making it safe for distributed systems."},{"id":"installation","title":"Installation","role":"setup","text":"Add the StackExchange.Redis NuGet package to your project:\n\n[code example]\n\nOr add it directly to your `.csproj` file:\n\n[code example]"},{"id":"using-the-net-class","title":"Using the .NET class","role":"content","text":"The `TokenBucket` class provides an interface for rate limiting\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/rate-limiter/dotnet/TokenBucket.cs)):\n\n[code example]\n\nThe `Allow` method executes the Lua script synchronously via StackExchange.Redis. The `ConnectionMultiplexer` manages connections automatically, including reconnection and multiplexing commands across a single TCP connection. The result is returned as a `RateLimitResult` record containing both the decision and the remaining token count."},{"id":"configuration-parameters","title":"Configuration parameters","role":"configuration","text":"* **capacity**: Maximum number of tokens in the bucket (controls burst size)\n* **refillRate**: Number of tokens added per refill interval\n* **refillInterval**: Time in seconds between refills\n\nFor example:\n* `capacity=10, refillRate=1, refillInterval=1.0` allows 1 request per second with bursts up to 10\n* `capacity=100, refillRate=10, refillInterval=1.0` allows 10 requests per second with bursts up to 100\n* `capacity=60, refillRate=1, refillInterval=60.0` allows 1 request per minute with bursts up to 60"},{"id":"rate-limit-keys","title":"Rate limit keys","role":"content","text":"The `key` parameter identifies what you're rate limiting. Common patterns:\n\n* **Per user**: `user:{userId}` - Limit each user independently\n* **Per IP address**: `ip:{ipAddress}` - Limit by client IP\n* **Per API endpoint**: `api:{endpoint}:{userId}` - Different limits per endpoint\n* **Global**: `global:api` - Single limit shared across all requests"},{"id":"script-caching-with-evalsha","title":"Script caching with EVALSHA","role":"content","text":"The .NET implementation uses [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) for optimal performance. On first use, the Lua script is loaded into Redis with `SCRIPT LOAD`, and subsequent calls use the cached SHA1 hash. If the script is evicted from the cache, the class automatically falls back to [`EVAL`](https://redis.io/docs/latest/commands/eval) and reloads the script. Script loading is thread-safe, using `volatile` fields and locking to handle concurrent access.\n\n[code example]"},{"id":"thread-safety","title":"Thread safety","role":"content","text":"The `TokenBucket` class is thread-safe. StackExchange.Redis's `IDatabase` is designed for concurrent use, and you can share a single `TokenBucket` instance across your entire application:\n\n[code example]"},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of three files. Download them from the [`dotnet` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/rate-limiter/dotnet) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"A demonstration HTTP server is included to show the rate limiter in action\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/rate-limiter/dotnet/Program.cs)):\n\n[code example]\n\nThe demo provides an interactive web interface where you can:\n\n* Submit requests and see them allowed or denied in real-time\n* View the current token count\n* Adjust rate limit parameters dynamically\n* Test different rate limiting scenarios\n\nThe demo assumes Redis is running on `localhost:6379` but you can specify a different host and port using the `--redis-host HOST` and `--redis-port PORT` command-line arguments. Visit `http://localhost:8080` in your browser to try it out."},{"id":"response-headers","title":"Response headers","role":"returns","text":"It's common to include rate limit information in HTTP response headers:\n\n[code example]"},{"id":"customization","title":"Customization","role":"content","text":""},{"id":"using-as-aspnet-core-middleware","title":"Using as ASP.NET Core middleware","role":"content","text":"You can wrap the rate limiter as middleware for use with ASP.NET Core:\n\n[code example]"},{"id":"error-handling","title":"Error handling","role":"errors","text":"The `Allow` method may throw a `RedisException` if the Redis connection is lost. Wrap calls in try/catch blocks for production use:\n\n[code example]"},{"id":"alternative-rate-limiting-algorithms","title":"Alternative rate limiting algorithms","role":"content","text":"The token bucket algorithm above handles most use cases but Redis supports\nother rate limiter patterns that might fit your requirements better. The table\nbelow lists four other algorithms alongside token bucket and summarizes\ntheir features:\n\n| Algorithm | Memory | Accuracy | Burst behavior | Best for |\n|---|---|---|---|---|\n| [Token bucket](#how-it-works) | 1 key (hash) | Exact | Controlled bursts | APIs with bursty traffic |\n| [Fixed window counter](#fixed-window-counter) | 1 key (string) | Approximate | 2x burst at boundaries | Simple API limits |\n| [Sliding window log](#sliding-window-log) | O(n) entries | Exact | No bursts | High-value APIs, audit trails |\n| [Sliding window counter](#sliding-window-counter) | 2 keys (string) | Near-exact | Smoothed boundaries | General-purpose APIs |\n| [Leaky bucket (policing)](#leaky-bucket-policing) | 1 key (hash) | Exact | No bursts | Strict no-burst enforcement |\n\nThe sections below give example implementations of these other algorithms.\nThe three time-based algorithms call `redis.call('TIME')` inside the Lua\nscript to derive the current timestamp from the Redis server clock. This\neliminates clock drift when the limiter runs across multiple application\nservers. The fixed window counter reads no clock: the key's TTL defines\nthe window."},{"id":"fixed-window-counter","title":"Fixed window counter","role":"content","text":"Counts requests within discrete, non-overlapping time intervals.\nSimplest algorithm — one key per window, one `EVAL` round trip.\n\n[code example]\n\n**Trade-off**: A client can make 2x requests by sending `limit` requests\nat the end of one window and `limit` requests at the start of the next."},{"id":"sliding-window-log","title":"Sliding window log","role":"content","text":"Records the exact timestamp of every request in a sorted set.\nProvides a true rolling window with no boundary bursts.\n\n[code example]\n\n**Trade-off**: Memory grows O(n) with request volume. Not ideal for\nhigh-volume, high-cardinality rate limiting."},{"id":"sliding-window-counter","title":"Sliding window counter","role":"content","text":"Blends two fixed-window counters using a weighted average to approximate\na true sliding window. Near-exact accuracy with the same low memory\nfootprint as a fixed window. The two keys use hash tags so they map\nto the same slot in Redis Cluster.\n\n[code example]\n\n**Trade-off**: The weighted estimate may let slightly more or fewer\nrequests through than the exact limit. Negligible for most apps."},{"id":"leaky-bucket-policing","title":"Leaky bucket (policing)","role":"content","text":"A virtual bucket fills with incoming requests and drains at a fixed rate.\nIf the bucket is full, requests are rejected immediately. This is the\npolicing variant — requests are allowed or denied instantly with no delay.\n\n[code example]\n\n**Trade-off**: Overflow traffic is rejected immediately. Clients must\nhandle `429 Too Many Requests` and retry with backoff."},{"id":"learn-more","title":"Learn more","role":"related","text":"* [EVAL command](https://redis.io/docs/latest/commands/eval) - Execute Lua scripts\n* [EVALSHA command](https://redis.io/docs/latest/commands/evalsha) - Execute cached Lua scripts\n* [Lua scripting](https://redis.io/docs/latest/develop/programmability/eval-intro) - Introduction to Redis Lua scripting\n* [HMGET command](https://redis.io/docs/latest/commands/hmget) - Get multiple hash fields\n* [HMSET command](https://redis.io/docs/latest/commands/hmset) - Set multiple hash fields\n* [.NET client](https://redis.io/docs/latest/develop/clients/dotnet) - Redis .NET client documentation"}],"examples":[{"id":"the-lua-script-ex0","language":"lua","code":"local key = KEYS[1]\nlocal capacity = tonumber(ARGV[1])\nlocal refill_rate = tonumber(ARGV[2])\nlocal refill_interval = tonumber(ARGV[3])\nlocal now = tonumber(ARGV[4])\n\n-- Get current state or initialize\nlocal bucket = redis.call('HMGET', key, 'tokens', 'last_refill')\nlocal tokens = tonumber(bucket[1])\nlocal last_refill = tonumber(bucket[2])\n\n-- Initialize if this is the first request\nif tokens == nil then\n    tokens = capacity\n    last_refill = now\nend\n\n-- Calculate token refill\nlocal time_passed = now - last_refill\nlocal refills = math.floor(time_passed / refill_interval)\n\nif refills > 0 then\n    tokens = math.min(capacity, tokens + (refills * refill_rate))\n    last_refill = last_refill + (refills * refill_interval)\nend\n\n-- Try to consume a token\nlocal allowed = 0\nif tokens >= 1 then\n    tokens = tokens - 1\n    allowed = 1\nend\n\n-- Update state\nredis.call('HMSET', key, 'tokens', tokens, 'last_refill', last_refill)\n\n-- Return result: allowed (1 or 0) and remaining tokens\nreturn {allowed, tokens}","section_id":"the-lua-script"},{"id":"installation-ex0","language":"bash","code":"dotnet add package StackExchange.Redis","section_id":"installation"},{"id":"installation-ex1","language":"xml","code":"<PackageReference Include=\"StackExchange.Redis\" Version=\"2.8.0\" />","section_id":"installation"},{"id":"using-the-net-class-ex0","language":"csharp","code":"using StackExchange.Redis;\n\n// Create a Redis connection\nvar redis = ConnectionMultiplexer.Connect(\"localhost:6379\");\nvar db = redis.GetDatabase();\n\n// Create a rate limiter: 10 requests per second\nvar limiter = new TokenBucket(\n    db: db,\n    capacity: 10,          // Maximum burst size\n    refillRate: 1,         // Add 1 token per interval\n    refillInterval: 1.0    // Every 1 second\n);\n\n// Check if a request should be allowed\nvar result = limiter.Allow(\"user:123\");\n\nif (result.Allowed)\n{\n    Console.WriteLine($\"Request allowed. {result.Remaining} tokens remaining.\");\n    // Process the request\n}\nelse\n{\n    Console.WriteLine(\"Request denied. Rate limit exceeded.\");\n    // Return 429 Too Many Requests\n}","section_id":"using-the-net-class"},{"id":"script-caching-with-evalsha-ex0","language":"csharp","code":"// The class handles script caching automatically.\n// First call loads the script, subsequent calls use EVALSHA.\nvar result1 = limiter.Allow(\"user:123\"); // Uses EVAL + caches\nvar result2 = limiter.Allow(\"user:123\"); // Uses EVALSHA (faster)","section_id":"script-caching-with-evalsha"},{"id":"thread-safety-ex0","language":"csharp","code":"// Create a shared limiter instance\nvar limiter = new TokenBucket(db, capacity: 10, refillRate: 1, refillInterval: 1.0);\n\n// Safe to call from multiple threads\nvar tasks = Enumerable.Range(0, 20).Select(i =>\n{\n    var result = limiter.Allow(\"shared:resource\");\n    Console.WriteLine($\"Request {i}: allowed={result.Allowed} remaining={result.Remaining}\");\n    return result;\n}).ToList();","section_id":"thread-safety"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir rate-limiter-demo && cd rate-limiter-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/rate-limiter/dotnet\ncurl -O $BASE/TokenBucket.cs\ncurl -O $BASE/Program.cs\ncurl -O $BASE/RateLimiterDemo.csproj","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"# Run the demo server\ndotnet run","section_id":"start-the-demo-server"},{"id":"response-headers-ex0","language":"csharp","code":"int capacity = 10;\ndouble refillInterval = 1.0;\nvar limiter = new TokenBucket(db, capacity: capacity, refillRate: 1, refillInterval: refillInterval);\n\nvar result = limiter.Allow($\"user:{userId}\");\n\n// Add standard rate limit headers\nResponse.Headers[\"X-RateLimit-Limit\"] = capacity.ToString();\nResponse.Headers[\"X-RateLimit-Remaining\"] = ((int)result.Remaining).ToString();\nResponse.Headers[\"X-RateLimit-Reset\"] =\n    DateTimeOffset.UtcNow.AddSeconds(refillInterval).ToUnixTimeSeconds().ToString();\n\nif (!result.Allowed)\n{\n    Response.Headers[\"Retry-After\"] = ((int)refillInterval).ToString();\n    Response.StatusCode = 429; // Too Many Requests\n}","section_id":"response-headers"},{"id":"using-as-aspnet-core-middleware-ex0","language":"csharp","code":"public class RateLimitMiddleware\n{\n    private readonly RequestDelegate _next;\n    private readonly TokenBucket _limiter;\n\n    public RateLimitMiddleware(RequestDelegate next, IDatabase db)\n    {\n        _next = next;\n        _limiter = new TokenBucket(db, capacity: 10, refillRate: 1, refillInterval: 1.0);\n    }\n\n    public async Task InvokeAsync(HttpContext context)\n    {\n        var key = $\"ip:{context.Connection.RemoteIpAddress}\";\n        var result = _limiter.Allow(key);\n\n        context.Response.Headers[\"X-RateLimit-Remaining\"] =\n            ((int)result.Remaining).ToString();\n\n        if (!result.Allowed)\n        {\n            context.Response.StatusCode = 429;\n            await context.Response.WriteAsJsonAsync(\n                new { error = \"Rate limit exceeded\" });\n            return;\n        }\n\n        await _next(context);\n    }\n}","section_id":"using-as-aspnet-core-middleware"},{"id":"error-handling-ex0","language":"csharp","code":"try\n{\n    var result = limiter.Allow(\"user:123\");\n    // Handle result\n}\ncatch (RedisException ex)\n{\n    Console.Error.WriteLine($\"Rate limiter error: {ex.Message}\");\n    // Fail open: allow the request when Redis is unavailable\n    // Or fail closed: deny the request\n}","section_id":"error-handling"},{"id":"fixed-window-counter-ex0","language":"csharp","code":"using StackExchange.Redis;\n\nconst string FixedWindowScript = @\"\nlocal key    = KEYS[1]\nlocal limit  = tonumber(ARGV[1])\nlocal window = tonumber(ARGV[2])\n\nlocal count = redis.call('INCR', key)\nif count == 1 then\n    redis.call('EXPIRE', key, window)\nend\n\nlocal ttl = redis.call('PTTL', key)\n\nif count > limit then\n    return {0, ttl}\nend\nreturn {1, ttl}\n\";\n\n// Returns (allowed, retryAfterMs). retryAfterMs is 0 when the request is allowed.\nstatic (bool Allowed, long RetryAfterMs) FixedWindowAllow(\n    IDatabase db, string key, int limit, int windowSeconds)\n{\n    var result = (RedisResult[])db.ScriptEvaluate(\n        FixedWindowScript,\n        new RedisKey[] { key },\n        new RedisValue[] { limit, windowSeconds });\n\n    var allowed = (long)result[0] == 1;\n    return (allowed, allowed ? 0 : (long)result[1]);\n}","section_id":"fixed-window-counter"},{"id":"sliding-window-log-ex0","language":"csharp","code":"using StackExchange.Redis;\n\nconst string SlidingWindowLogScript = @\"\nlocal key    = KEYS[1]\nlocal limit  = tonumber(ARGV[1])\nlocal window = tonumber(ARGV[2])\nlocal member = ARGV[3]\n\nlocal t      = redis.call('TIME')\nlocal now    = tonumber(t[1]) + tonumber(t[2]) / 1e6\nlocal cutoff = now - window\n\nredis.call('ZREMRANGEBYSCORE', key, '-inf', cutoff)\n\nlocal count = redis.call('ZCARD', key)\n\nif count < limit then\n    redis.call('ZADD', key, now, member)\n    redis.call('EXPIRE', key, window * 2)\n    return {1, 0}\nend\n\nlocal oldest = redis.call('ZRANGE', key, 0, 0, 'WITHSCORES')\nlocal retry_after_ms = 0\nif oldest[2] then\n    retry_after_ms = math.floor((tonumber(oldest[2]) + window - now) * 1000)\nend\n\nreturn {0, retry_after_ms}\n\";\n\nstatic (bool Allowed, long RetryAfterMs) SlidingWindowLogAllow(\n    IDatabase db, string key, int limit, int windowSeconds)\n{\n    var member = Guid.NewGuid().ToString();\n\n    var result = (RedisResult[])db.ScriptEvaluate(\n        SlidingWindowLogScript,\n        new RedisKey[] { key },\n        new RedisValue[] { limit, windowSeconds, member });\n\n    return ((long)result[0] == 1, (long)result[1]);\n}","section_id":"sliding-window-log"},{"id":"sliding-window-counter-ex0","language":"csharp","code":"using StackExchange.Redis;\n\nconst string SlidingWindowCounterScript = @\"\nlocal base   = KEYS[1]\nlocal limit  = tonumber(ARGV[1])\nlocal window = tonumber(ARGV[2])\n\nlocal t   = redis.call('TIME')\nlocal now = tonumber(t[1]) + tonumber(t[2]) / 1e6\n\nlocal window_num = math.floor(now / window)\nlocal elapsed     = (now % window) / window\n\nlocal curr_key = base .. ':' .. window_num\nlocal prev_key = base .. ':' .. (window_num - 1)\n\nlocal prev = tonumber(redis.call('GET', prev_key) or 0)\nlocal curr = tonumber(redis.call('GET', curr_key) or 0)\n\nlocal estimate = prev * (1 - elapsed) + curr\n\nif estimate >= limit then\n    return {0, 0}\nend\n\nlocal new_count = redis.call('INCR', curr_key)\nif new_count == 1 then\n    redis.call('EXPIRE', curr_key, window * 2)\nend\n\nreturn {1, 0}\n\";\n\nstatic bool SlidingWindowCounterAllow(\n    IDatabase db, string key, int limit, int windowSeconds)\n{\n    var result = (RedisResult[])db.ScriptEvaluate(\n        SlidingWindowCounterScript,\n        new RedisKey[] { $\"{{{key}}}\" },\n        new RedisValue[] { limit, windowSeconds });\n\n    return (long)result[0] == 1;\n}","section_id":"sliding-window-counter"},{"id":"leaky-bucket-policing-ex0","language":"csharp","code":"using StackExchange.Redis;\n\nconst string LeakyBucketScript = @\"\nlocal key       = KEYS[1]\nlocal capacity  = tonumber(ARGV[1])\nlocal leak_rate = tonumber(ARGV[2])\n\nlocal t   = redis.call('TIME')\nlocal now = tonumber(t[1]) + tonumber(t[2]) / 1e6\n\nlocal data      = redis.call('HGETALL', key)\nlocal level     = 0\nlocal last_leak = now\n\nif #data > 0 then\n    for i = 1, #data, 2 do\n        if data[i] == 'level' then\n            level = tonumber(data[i+1])\n        elseif data[i] == 'last_leak' then\n            last_leak = tonumber(data[i+1])\n        end\n    end\nend\n\nlocal elapsed = now - last_leak\nlevel = math.max(0, level - elapsed * leak_rate)\n\nif level + 1 > capacity then\n    return {0, math.floor((level + 1 - capacity) / leak_rate * 1000)}\nend\n\nlevel = level + 1\nlocal ttl = math.ceil(capacity / leak_rate) + 1\n\nredis.call('HSET', key, 'level', level, 'last_leak', now)\nredis.call('EXPIRE', key, ttl)\n\nreturn {1, 0}\n\";\n\nstatic (bool Allowed, long RetryAfterMs) LeakyBucketAllow(\n    IDatabase db, string key, int capacity, double leakRate)\n{\n    var result = (RedisResult[])db.ScriptEvaluate(\n        LeakyBucketScript,\n        new RedisKey[] { key },\n        new RedisValue[] { capacity, leakRate });\n\n    return ((long)result[0] == 1, (long)result[1]);\n}","section_id":"leaky-bucket-policing"}]}
{"schema_version":2,"id":"develop/use-cases/rate-limiter/go","title":"Token bucket rate limiter with Redis and Go","url":"https://redis.io/docs/latest/develop/use-cases/rate-limiter/go/","summary":"Implement a token bucket rate limiter using Redis and Lua scripts in Go","aliases":["/develop/use-cases/rate-limiter/go-redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-29T14:34:47+01:00","children":[],"page_type":"content","content_hash":"fb15ac31a18b2f5376c028f48a8da54be875985e3b08edebdc538bad4734ab05","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a distributed token bucket rate limiter using Redis and Lua scripts in Go with the [`go-redis`](https://redis.io/docs/latest/develop/clients/go) client library."},{"id":"overview","title":"Overview","role":"overview","text":"Rate limiting is a critical technique for controlling the rate at which operations are performed. Common use cases include:\n\n* Limiting API requests per user or IP address\n* Preventing abuse and protecting against denial-of-service attacks\n* Ensuring fair resource allocation across multiple clients\n* Throttling background jobs or batch operations\n\nThe **token bucket algorithm** is a popular rate limiting approach that allows bursts of traffic while maintaining an average rate limit over time. This guide covers the Go implementation using the [`go-redis`](https://redis.io/docs/latest/develop/clients/go) client library, taking advantage of Go's `context.Context` for cancellation and timeouts, explicit error handling, and goroutine safety."},{"id":"how-it-works","title":"How it works","role":"content","text":"The token bucket algorithm works like a bucket that holds tokens:\n\n1. **Initialization**: The bucket starts with a maximum capacity of tokens\n2. **Refill**: Tokens are added to the bucket at a constant rate (for example, 1 token per second)\n3. **Consumption**: Each request consumes one token from the bucket\n4. **Decision**: If tokens are available, the request is allowed; otherwise, it's denied\n5. **Capacity limit**: The bucket never exceeds its maximum capacity\n\nThis approach allows for burst traffic (using accumulated tokens) while enforcing an average rate limit over time."},{"id":"why-use-redis","title":"Why use Redis?","role":"content","text":"Redis is ideal for distributed rate limiting because:\n\n* **Atomic operations**: Lua scripts execute atomically, preventing race conditions\n* **Shared state**: Multiple application servers can share the same rate limit counters\n* **High performance**: In-memory operations provide microsecond latency\n* **Automatic expiration**: Keys can be set to expire automatically (though not used in this implementation)"},{"id":"the-lua-script","title":"The Lua script","role":"content","text":"The core of this implementation is a Lua script that runs atomically on the Redis server. This ensures that checking and updating the token bucket happens in a single operation, preventing race conditions in distributed environments.\n\nHere's how the script works:\n\n[code example]"},{"id":"script-breakdown","title":"Script breakdown","role":"content","text":"1. **State retrieval**: Uses [`HMGET`](https://redis.io/docs/latest/commands/hmget) to fetch the current token count and last refill time from a hash\n2. **Initialization**: On first use, sets tokens to full capacity\n3. **Token refill calculation**: Computes how many tokens should be added based on elapsed time\n4. **Capacity enforcement**: Uses `math.min()` to ensure tokens never exceed capacity\n5. **Token consumption**: Decrements the token count if available\n6. **State update**: Uses [`HMSET`](https://redis.io/docs/latest/commands/hmset) to save the new state\n7. **Return value**: Returns both the decision (allowed/denied) and remaining tokens"},{"id":"why-atomicity-matters","title":"Why atomicity matters","role":"content","text":"Without atomic execution, race conditions could occur:\n\n* **Double spending**: Two requests could read the same token count and both succeed when only one should\n* **Lost updates**: Concurrent updates could overwrite each other's changes\n* **Inconsistent state**: Token count and refill time could become desynchronized\n\nUsing [`EVAL`](https://redis.io/docs/latest/commands/eval) or [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) ensures the entire operation executes atomically, making it safe for distributed systems."},{"id":"installation","title":"Installation","role":"setup","text":"Install the `go-redis` package:\n\n[code example]"},{"id":"using-the-go-package","title":"Using the Go package","role":"content","text":"The `TokenBucket` struct provides a concurrency-safe interface for rate limiting\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/rate-limiter/go/token_bucket.go)):\n\n[code example]\n\nGo's `context.Context` is passed to every call, allowing you to set deadlines and handle cancellation. The `Allow` method returns an explicit `error` value following Go conventions."},{"id":"configuration-parameters","title":"Configuration parameters","role":"configuration","text":"* **Capacity**: Maximum number of tokens in the bucket (controls burst size)\n* **RefillRate**: Number of tokens added per refill interval\n* **RefillInterval**: Time in seconds between refills\n\nFor example:\n* `Capacity: 10, RefillRate: 1, RefillInterval: 1.0` allows 1 request per second with bursts up to 10\n* `Capacity: 100, RefillRate: 10, RefillInterval: 1.0` allows 10 requests per second with bursts up to 100\n* `Capacity: 60, RefillRate: 1, RefillInterval: 60.0` allows 1 request per minute with bursts up to 60"},{"id":"rate-limit-keys","title":"Rate limit keys","role":"content","text":"The `key` parameter identifies what you're rate limiting. Common patterns:\n\n* **Per user**: `user:{userID}` - Limit each user independently\n* **Per IP address**: `ip:{ipAddress}` - Limit by client IP\n* **Per API endpoint**: `api:{endpoint}:{userID}` - Different limits per endpoint\n* **Global**: `global:api` - Single limit shared across all requests"},{"id":"script-caching-with-evalsha","title":"Script caching with EVALSHA","role":"content","text":"The Go implementation uses [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) for optimal performance. On first use, the Lua script is loaded into Redis with `SCRIPT LOAD`, and subsequent calls use the cached SHA1 hash. If the script is evicted from the cache, the module automatically falls back to [`EVAL`](https://redis.io/docs/latest/commands/eval) and reloads the script. The script loading is protected with a `sync.Once` to ensure thread safety across goroutines.\n\n[code example]"},{"id":"goroutine-safety","title":"Goroutine safety","role":"content","text":"The `TokenBucket` struct is safe for concurrent use from multiple goroutines. You can share a single limiter instance across your application:\n\n[code example]"},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of four files. Download them from the [`go` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/rate-limiter/go) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"A demonstration HTTP server is included to show the rate limiter in action\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/rate-limiter/go/demo_server.go)):\n\nTo run the demo, create a `main.go` file that calls the exported `RunDemoServer()` function:\n\n[code example]\n\nThen build and run:\n\n[code example]\n\nThe demo provides an interactive web interface where you can:\n\n* Submit requests and see them allowed or denied in real-time\n* View the current token count\n* Adjust rate limit parameters dynamically\n* Test different rate limiting scenarios\n\nThe demo assumes Redis is running on `localhost:6379` but you can specify a different host and port using the `--redis-host` and `--redis-port` command-line arguments. Visit `http://localhost:8080` in your browser to try it out."},{"id":"response-headers","title":"Response headers","role":"returns","text":"It's common to include rate limit information in HTTP response headers:\n\n[code example]"},{"id":"customization","title":"Customization","role":"content","text":""},{"id":"using-as-http-middleware","title":"Using as HTTP middleware","role":"content","text":"You can wrap the rate limiter as HTTP middleware for use with the standard library or any compatible router:\n\n[code example]"},{"id":"error-handling","title":"Error handling","role":"errors","text":"The `Allow` method returns an error if the Redis connection is lost. Decide whether to fail open (allow requests) or fail closed (deny requests) based on your requirements:\n\n[code example]"},{"id":"using-with-context-timeouts","title":"Using with context timeouts","role":"content","text":"Go's `context.Context` lets you set deadlines for rate limit checks, which is useful when you don't want a slow Redis connection to block request processing:\n\n[code example]"},{"id":"alternative-rate-limiting-algorithms","title":"Alternative rate limiting algorithms","role":"content","text":"The token bucket algorithm above handles most use cases but Redis supports\nother rate limiter patterns that might fit your requirements better. The table\nbelow lists four other algorithms alongside token bucket and summarizes\ntheir features:\n\n| Algorithm | Memory | Accuracy | Burst behavior | Best for |\n|---|---|---|---|---|\n| [Token bucket](#how-it-works) | 1 key (hash) | Exact | Controlled bursts | APIs with bursty traffic |\n| [Fixed window counter](#fixed-window-counter) | 1 key (string) | Approximate | 2x burst at boundaries | Simple API limits |\n| [Sliding window log](#sliding-window-log) | O(n) entries | Exact | No bursts | High-value APIs, audit trails |\n| [Sliding window counter](#sliding-window-counter) | 2 keys (string) | Near-exact | Smoothed boundaries | General-purpose APIs |\n| [Leaky bucket (policing)](#leaky-bucket-policing) | 1 key (hash) | Exact | No bursts | Strict no-burst enforcement |\n\nThe sections below give example implementations of these other algorithms.\nThe three time-based algorithms call `redis.call('TIME')` inside the Lua\nscript to derive the current timestamp from the Redis server clock. This\neliminates clock drift when the limiter runs across multiple application\nservers. The fixed window counter reads no clock: the key's TTL defines\nthe window."},{"id":"fixed-window-counter","title":"Fixed window counter","role":"content","text":"Counts requests within discrete, non-overlapping time intervals.\nSimplest algorithm — one key per window, one `EVAL` round trip.\n\n[code example]\n\n**Trade-off**: A client can make 2x requests by sending `limit` requests\nat the end of one window and `limit` requests at the start of the next."},{"id":"sliding-window-log","title":"Sliding window log","role":"content","text":"Records the exact timestamp of every request in a sorted set.\nProvides a true rolling window with no boundary bursts.\n\n[code example]\n\n**Trade-off**: Memory grows O(n) with request volume. Not ideal for\nhigh-volume, high-cardinality rate limiting."},{"id":"sliding-window-counter","title":"Sliding window counter","role":"content","text":"Blends two fixed-window counters using a weighted average to approximate\na true sliding window. Near-exact accuracy with the same low memory\nfootprint as a fixed window. The two keys use hash tags so they map\nto the same slot in Redis Cluster.\n\n[code example]\n\n**Trade-off**: The weighted estimate may let slightly more or fewer\nrequests through than the exact limit. Negligible for most apps."},{"id":"leaky-bucket-policing","title":"Leaky bucket (policing)","role":"content","text":"A virtual bucket fills with incoming requests and drains at a fixed rate.\nIf the bucket is full, requests are rejected immediately. This is the\npolicing variant — requests are allowed or denied instantly with no delay.\n\n[code example]\n\n**Trade-off**: Overflow traffic is rejected immediately. Clients must\nhandle `429 Too Many Requests` and retry with backoff."},{"id":"learn-more","title":"Learn more","role":"related","text":"* [EVAL command](https://redis.io/docs/latest/commands/eval) - Execute Lua scripts\n* [EVALSHA command](https://redis.io/docs/latest/commands/evalsha) - Execute cached Lua scripts\n* [Lua scripting](https://redis.io/docs/latest/develop/programmability/eval-intro) - Introduction to Redis Lua scripting\n* [HMGET command](https://redis.io/docs/latest/commands/hmget) - Get multiple hash fields\n* [HMSET command](https://redis.io/docs/latest/commands/hmset) - Set multiple hash fields\n* [Go client](https://redis.io/docs/latest/develop/clients/go) - Redis Go client documentation"}],"examples":[{"id":"the-lua-script-ex0","language":"lua","code":"local key = KEYS[1]\nlocal capacity = tonumber(ARGV[1])\nlocal refill_rate = tonumber(ARGV[2])\nlocal refill_interval = tonumber(ARGV[3])\nlocal now = tonumber(ARGV[4])\n\n-- Get current state or initialize\nlocal bucket = redis.call('HMGET', key, 'tokens', 'last_refill')\nlocal tokens = tonumber(bucket[1])\nlocal last_refill = tonumber(bucket[2])\n\n-- Initialize if this is the first request\nif tokens == nil then\n    tokens = capacity\n    last_refill = now\nend\n\n-- Calculate token refill\nlocal time_passed = now - last_refill\nlocal refills = math.floor(time_passed / refill_interval)\n\nif refills > 0 then\n    tokens = math.min(capacity, tokens + (refills * refill_rate))\n    last_refill = last_refill + (refills * refill_interval)\nend\n\n-- Try to consume a token\nlocal allowed = 0\nif tokens >= 1 then\n    tokens = tokens - 1\n    allowed = 1\nend\n\n-- Update state\nredis.call('HMSET', key, 'tokens', tokens, 'last_refill', last_refill)\n\n-- Return result: allowed (1 or 0) and remaining tokens\nreturn {allowed, tokens}","section_id":"the-lua-script"},{"id":"installation-ex0","language":"bash","code":"go get github.com/redis/go-redis/v9","section_id":"installation"},{"id":"using-the-go-package-ex0","language":"go","code":"package main\n\nimport (\n    \"context\"\n    \"fmt\"\n    \"log\"\n    \"time\"\n\n    \"github.com/redis/go-redis/v9\"\n)\n\nfunc main() {\n    ctx := context.Background()\n\n    // Create a Redis connection\n    rdb := redis.NewClient(&redis.Options{\n        Addr: \"localhost:6379\",\n    })\n\n    // Create a rate limiter: 10 requests per second\n    limiter := NewTokenBucket(TokenBucketConfig{\n        Client:         rdb,\n        Capacity:       10,           // Maximum burst size\n        RefillRate:     1,            // Add 1 token per interval\n        RefillInterval: time.Second,  // Every 1 second\n    })\n\n    // Check if a request should be allowed\n    allowed, remaining, err := limiter.Allow(ctx, \"user:123\")\n    if err != nil {\n        log.Fatalf(\"rate limiter error: %v\", err)\n    }\n\n    if allowed {\n        fmt.Printf(\"Request allowed. %.0f tokens remaining.\\n\", remaining)\n        // Process the request\n    } else {\n        fmt.Println(\"Request denied. Rate limit exceeded.\")\n        // Return 429 Too Many Requests\n    }\n}","section_id":"using-the-go-package"},{"id":"script-caching-with-evalsha-ex0","language":"go","code":"// The package handles script caching automatically.\n// First call loads the script, subsequent calls use EVALSHA.\nallowed1, remaining1, _ := limiter.Allow(ctx, \"user:123\") // Uses EVAL + caches\nallowed2, remaining2, _ := limiter.Allow(ctx, \"user:123\") // Uses EVALSHA (faster)","section_id":"script-caching-with-evalsha"},{"id":"goroutine-safety-ex0","language":"go","code":"limiter := NewTokenBucket(TokenBucketConfig{\n    Client:         rdb,\n    Capacity:       10,\n    RefillRate:     1,\n    RefillInterval: time.Second,\n})\n\n// Safe to call from multiple goroutines\nvar wg sync.WaitGroup\nfor i := 0; i < 20; i++ {\n    wg.Add(1)\n    go func(id int) {\n        defer wg.Done()\n        allowed, remaining, err := limiter.Allow(ctx, \"shared:resource\")\n        if err != nil {\n            log.Printf(\"goroutine %d: error: %v\", id, err)\n            return\n        }\n        log.Printf(\"goroutine %d: allowed=%v remaining=%.0f\", id, allowed, remaining)\n    }(i)\n}\nwg.Wait()","section_id":"goroutine-safety"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir rate-limiter-demo && cd rate-limiter-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/rate-limiter/go\ncurl -O $BASE/token_bucket.go\ncurl -O $BASE/demo_server.go\ncurl -O $BASE/go.mod\ncurl -O $BASE/go.sum","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"go","code":"package main\n\nimport \"ratelimiter\"\n\nfunc main() { ratelimiter.RunDemoServer() }","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"bash","code":"# Install dependencies\ngo get github.com/redis/go-redis/v9\n\n# Build and run the demo server\ngo build -o demo ./...\n./demo","section_id":"start-the-demo-server"},{"id":"response-headers-ex0","language":"go","code":"// Store config values for use in response headers\ncapacity := 10\nrefillInterval := time.Second\n\nlimiter := NewTokenBucket(TokenBucketConfig{\n    Client:         rdb,\n    Capacity:       capacity,\n    RefillRate:     1,\n    RefillInterval: refillInterval,\n})\n\nallowed, remaining, err := limiter.Allow(ctx, fmt.Sprintf(\"user:%s\", userID))\nif err != nil {\n    http.Error(w, \"Internal Server Error\", http.StatusInternalServerError)\n    return\n}\n\n// Add standard rate limit headers\nw.Header().Set(\"X-RateLimit-Limit\", strconv.Itoa(capacity))\nw.Header().Set(\"X-RateLimit-Remaining\", strconv.FormatFloat(remaining, 'f', 0, 64))\nw.Header().Set(\"X-RateLimit-Reset\", strconv.FormatInt(\n    time.Now().Add(refillInterval).Unix(), 10))\n\nif !allowed {\n    w.Header().Set(\"Retry-After\", strconv.FormatFloat(refillInterval.Seconds(), 'f', 0, 64))\n    http.Error(w, \"Too Many Requests\", http.StatusTooManyRequests)\n    return\n}","section_id":"response-headers"},{"id":"using-as-http-middleware-ex0","language":"go","code":"func RateLimitMiddleware(limiter *TokenBucket, keyFn func(*http.Request) string) func(http.Handler) http.Handler {\n    return func(next http.Handler) http.Handler {\n        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n            key := keyFn(r)\n            allowed, remaining, err := limiter.Allow(r.Context(), key)\n            if err != nil {\n                http.Error(w, \"Internal Server Error\", http.StatusInternalServerError)\n                return\n            }\n\n            w.Header().Set(\"X-RateLimit-Remaining\", strconv.FormatFloat(remaining, 'f', 0, 64))\n\n            if !allowed {\n                http.Error(w, \"Rate limit exceeded\", http.StatusTooManyRequests)\n                return\n            }\n            next.ServeHTTP(w, r)\n        })\n    }\n}\n\n// Apply per-IP rate limiting\nmux := http.NewServeMux()\nhandler := RateLimitMiddleware(limiter, func(r *http.Request) string {\n    return fmt.Sprintf(\"ip:%s\", r.RemoteAddr)\n})(mux)","section_id":"using-as-http-middleware"},{"id":"error-handling-ex0","language":"go","code":"allowed, remaining, err := limiter.Allow(ctx, \"user:123\")\nif err != nil {\n    log.Printf(\"rate limiter error: %v\", err)\n    // Fail open: allow the request when Redis is unavailable\n    allowed = true\n    // Or fail closed: deny the request\n    // allowed = false\n}","section_id":"error-handling"},{"id":"using-with-context-timeouts-ex0","language":"go","code":"// Set a 100ms timeout for the rate limit check\nctx, cancel := context.WithTimeout(r.Context(), 100*time.Millisecond)\ndefer cancel()\n\nallowed, remaining, err := limiter.Allow(ctx, \"user:123\")\nif err != nil {\n    // Context deadline exceeded or Redis error\n    log.Printf(\"rate limit check timed out: %v\", err)\n    allowed = true // fail open\n}","section_id":"using-with-context-timeouts"},{"id":"fixed-window-counter-ex0","language":"go","code":"import (\n\t\"context\"\n\n\t\"github.com/redis/go-redis/v9\"\n)\n\nconst fixedWindowScript = `\nlocal key    = KEYS[1]\nlocal limit  = tonumber(ARGV[1])\nlocal window = tonumber(ARGV[2])\n\nlocal count = redis.call('INCR', key)\nif count == 1 then\n    redis.call('EXPIRE', key, window)\nend\n\nlocal ttl = redis.call('PTTL', key)\n\nif count > limit then\n    return {0, ttl}\nend\nreturn {1, ttl}\n`\n\n// FixedWindowAllow reports whether key is within limit for the current window.\n// When a request is denied, it also returns the milliseconds until the window resets.\nfunc FixedWindowAllow(ctx context.Context, rdb *redis.Client, key string,\n\tlimit, windowSeconds int) (bool, int64, error) {\n\n\tscript := redis.NewScript(fixedWindowScript)\n\tres, err := script.Run(ctx, rdb, []string{key}, limit, windowSeconds).Int64Slice()\n\tif err != nil {\n\t\treturn false, 0, err\n\t}\n\n\tallowed := res[0] == 1\n\tif allowed {\n\t\treturn true, 0, nil\n\t}\n\treturn false, res[1], nil\n}","section_id":"fixed-window-counter"},{"id":"sliding-window-log-ex0","language":"go","code":"import (\n\t\"context\"\n\t\"crypto/rand\"\n\t\"encoding/hex\"\n\n\t\"github.com/redis/go-redis/v9\"\n)\n\nconst slidingWindowLogScript = `\nlocal key    = KEYS[1]\nlocal limit  = tonumber(ARGV[1])\nlocal window = tonumber(ARGV[2])\nlocal member = ARGV[3]\n\nlocal t      = redis.call('TIME')\nlocal now    = tonumber(t[1]) + tonumber(t[2]) / 1e6\nlocal cutoff = now - window\n\nredis.call('ZREMRANGEBYSCORE', key, '-inf', cutoff)\n\nlocal count = redis.call('ZCARD', key)\n\nif count < limit then\n    redis.call('ZADD', key, now, member)\n    redis.call('EXPIRE', key, window * 2)\n    return {1, 0}\nend\n\nlocal oldest = redis.call('ZRANGE', key, 0, 0, 'WITHSCORES')\nlocal retry_after_ms = 0\nif oldest[2] then\n    retry_after_ms = math.floor((tonumber(oldest[2]) + window - now) * 1000)\nend\n\nreturn {0, retry_after_ms}\n`\n\n// SlidingWindowLogAllow reports whether key is within limit over the trailing\n// window, and the milliseconds until the oldest entry leaves it.\nfunc SlidingWindowLogAllow(ctx context.Context, rdb *redis.Client, key string,\n\tlimit, windowSeconds int) (bool, int64, error) {\n\n\tbuf := make([]byte, 16)\n\tif _, err := rand.Read(buf); err != nil {\n\t\treturn false, 0, err\n\t}\n\tmember := hex.EncodeToString(buf)\n\n\tscript := redis.NewScript(slidingWindowLogScript)\n\tres, err := script.Run(ctx, rdb, []string{key}, limit, windowSeconds, member).Int64Slice()\n\tif err != nil {\n\t\treturn false, 0, err\n\t}\n\treturn res[0] == 1, res[1], nil\n}","section_id":"sliding-window-log"},{"id":"sliding-window-counter-ex0","language":"go","code":"import (\n\t\"context\"\n\n\t\"github.com/redis/go-redis/v9\"\n)\n\nconst slidingWindowCounterScript = `\nlocal base   = KEYS[1]\nlocal limit  = tonumber(ARGV[1])\nlocal window = tonumber(ARGV[2])\n\nlocal t   = redis.call('TIME')\nlocal now = tonumber(t[1]) + tonumber(t[2]) / 1e6\n\nlocal window_num = math.floor(now / window)\nlocal elapsed     = (now % window) / window\n\nlocal curr_key = base .. ':' .. window_num\nlocal prev_key = base .. ':' .. (window_num - 1)\n\nlocal prev = tonumber(redis.call('GET', prev_key) or 0)\nlocal curr = tonumber(redis.call('GET', curr_key) or 0)\n\nlocal estimate = prev * (1 - elapsed) + curr\n\nif estimate >= limit then\n    return {0, 0}\nend\n\nlocal new_count = redis.call('INCR', curr_key)\nif new_count == 1 then\n    redis.call('EXPIRE', curr_key, window * 2)\nend\n\nreturn {1, 0}\n`\n\n// SlidingWindowCounterAllow reports whether key is within limit, weighting the\n// previous window's count by how far the current window has progressed.\nfunc SlidingWindowCounterAllow(ctx context.Context, rdb *redis.Client, key string,\n\tlimit, windowSeconds int) (bool, error) {\n\n\tscript := redis.NewScript(slidingWindowCounterScript)\n\tres, err := script.Run(ctx, rdb, []string{\"{\" + key + \"}\"}, limit, windowSeconds).Int64Slice()\n\tif err != nil {\n\t\treturn false, err\n\t}\n\treturn res[0] == 1, nil\n}","section_id":"sliding-window-counter"},{"id":"leaky-bucket-policing-ex0","language":"go","code":"import (\n\t\"context\"\n\n\t\"github.com/redis/go-redis/v9\"\n)\n\nconst leakyBucketScript = `\nlocal key       = KEYS[1]\nlocal capacity  = tonumber(ARGV[1])\nlocal leak_rate = tonumber(ARGV[2])\n\nlocal t   = redis.call('TIME')\nlocal now = tonumber(t[1]) + tonumber(t[2]) / 1e6\n\nlocal data      = redis.call('HGETALL', key)\nlocal level     = 0\nlocal last_leak = now\n\nif #data > 0 then\n    for i = 1, #data, 2 do\n        if data[i] == 'level' then\n            level = tonumber(data[i+1])\n        elseif data[i] == 'last_leak' then\n            last_leak = tonumber(data[i+1])\n        end\n    end\nend\n\nlocal elapsed = now - last_leak\nlevel = math.max(0, level - elapsed * leak_rate)\n\nif level + 1 > capacity then\n    return {0, math.floor((level + 1 - capacity) / leak_rate * 1000)}\nend\n\nlevel = level + 1\nlocal ttl = math.ceil(capacity / leak_rate) + 1\n\nredis.call('HSET', key, 'level', level, 'last_leak', now)\nredis.call('EXPIRE', key, ttl)\n\nreturn {1, 0}\n`\n\n// LeakyBucketAllow adds one request to the bucket for key and reports whether it\n// fits. When the bucket is full it returns the milliseconds until it drains enough.\nfunc LeakyBucketAllow(ctx context.Context, rdb *redis.Client, key string,\n\tcapacity int, leakRate float64) (bool, int64, error) {\n\n\tscript := redis.NewScript(leakyBucketScript)\n\tres, err := script.Run(ctx, rdb, []string{key}, capacity, leakRate).Int64Slice()\n\tif err != nil {\n\t\treturn false, 0, err\n\t}\n\treturn res[0] == 1, res[1], nil\n}","section_id":"leaky-bucket-policing"}]}
{"schema_version":2,"id":"develop/use-cases/rate-limiter","title":"Redis rate limiter","url":"https://redis.io/docs/latest/develop/use-cases/rate-limiter/","summary":"Enforce request quotas across distributed services with Redis","tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-17T10:28:27+01:00","children":[{"id":"develop/use-cases/rate-limiter/redis-py","summary":"Implement a token bucket rate limiter with Redis and redis-py","title":"Token bucket rate limiter with Redis and redis-py","url":"https://redis.io/docs/latest/develop/use-cases/rate-limiter/redis-py/"},{"id":"develop/use-cases/rate-limiter/nodejs","summary":"Implement a token bucket rate limiter using Redis and Lua scripts in Node.js","title":"Token bucket rate limiter with Redis and Node.js","url":"https://redis.io/docs/latest/develop/use-cases/rate-limiter/nodejs/"},{"id":"develop/use-cases/rate-limiter/go","summary":"Implement a token bucket rate limiter using Redis and Lua scripts in Go","title":"Token bucket rate limiter with Redis and Go","url":"https://redis.io/docs/latest/develop/use-cases/rate-limiter/go/"},{"id":"develop/use-cases/rate-limiter/java-jedis","summary":"Implement a token bucket rate limiter using Redis and Lua scripts in Java","title":"Token bucket rate limiter with Redis and Java","url":"https://redis.io/docs/latest/develop/use-cases/rate-limiter/java-jedis/"},{"id":"develop/use-cases/rate-limiter/dotnet","summary":"Implement a token bucket rate limiter using Redis and Lua scripts in .NET","title":"Token bucket rate limiter with Redis and .NET","url":"https://redis.io/docs/latest/develop/use-cases/rate-limiter/dotnet/"},{"id":"develop/use-cases/rate-limiter/java-lettuce","summary":"Implement a token bucket rate limiter using Redis and Lua scripts in Java with Lettuce","title":"Token bucket rate limiter with Redis and Java (Lettuce)","url":"https://redis.io/docs/latest/develop/use-cases/rate-limiter/java-lettuce/"},{"id":"develop/use-cases/rate-limiter/php","summary":"Implement a token bucket rate limiter using Redis and Lua scripts in PHP","title":"Token bucket rate limiter with Redis and PHP","url":"https://redis.io/docs/latest/develop/use-cases/rate-limiter/php/"},{"id":"develop/use-cases/rate-limiter/ruby","summary":"Implement a token bucket rate limiter using Redis and Lua scripts in Ruby","title":"Token bucket rate limiter with Redis and Ruby","url":"https://redis.io/docs/latest/develop/use-cases/rate-limiter/ruby/"},{"id":"develop/use-cases/rate-limiter/rust","summary":"Implement a token bucket rate limiter using Redis and Lua scripts in Rust","title":"Token bucket rate limiter with Redis and Rust","url":"https://redis.io/docs/latest/develop/use-cases/rate-limiter/rust/"}],"page_type":"content","content_hash":"b52f3177d50a32949bfa872745f1a42a371acc5ecc7a6376f695f364a7119808","sections":[{"id":"when-to-use-a-redis-rate-limiter","title":"When to use a Redis rate limiter","role":"overview","text":"Use a Redis rate limiter when you need to enforce per-user, per-API, or per-tenant request quotas consistently across distributed service instances."},{"id":"why-the-problem-is-hard","title":"Why the problem is hard","role":"content","text":"APIs and services get overloaded by traffic spikes, abusive clients, or misconfigured integrations. Without rate limiting, downstream systems — databases, third-party APIs, LLM providers, payment gateways — become unavailable or generate unbounded costs.\n\nLocal per-process counters break behind load balancers: the same client bypasses limits by hitting different instances. The rate limiter needs a centralized store fast enough to sit on the request path without adding meaningful latency."},{"id":"what-you-can-expect-from-a-redis-solution","title":"What you can expect from a Redis solution","role":"content","text":"You can:\n\n-   Enforce quotas per user, IP, API key, tenant, or model across all instances.\n-   Run fixed window, sliding window, and token bucket algorithms on a single Redis deployment.\n-   Add a synchronous rate check to any gateway or service on the request path.\n-   Protect downstream systems from overload without adding meaningful latency."},{"id":"how-redis-supports-the-solution","title":"How Redis supports the solution","role":"content","text":"In practice, a Redis rate limiter is a single shared store that every service instance queries on every request, returning an allow/deny decision in sub-millisecond time. Counters live behind keys scoped to the dimension you are limiting on (user, IP, API key, tenant, model), and time windows are cleaned up by Redis itself.\n\nRedis provides the following features that make it a good fit for rate limiting:\n\n-   [`INCR`](https://redis.io/docs/latest/commands/incr) and [`EXPIRE`](https://redis.io/docs/latest/commands/expire)\n    give you atomic fixed-window counters with automatic time-window cleanup.\n-   [Hashes](https://redis.io/docs/latest/develop/data-types/hashes),\n    [sorted sets](https://redis.io/docs/latest/develop/data-types/sorted-sets), and\n    [strings](https://redis.io/docs/latest/develop/data-types/strings) cover the data shapes needed for\n    sliding window and token bucket algorithms.\n-   Hash field expiration in Redis 8\n    ([`HEXPIRE`](https://redis.io/docs/latest/commands/hexpire)) further simplifies time-bound\n    rate limiting at the field level.\n-   [Lua scripting](https://redis.io/docs/latest/develop/programmability/eval-intro) with\n    [`EVAL`](https://redis.io/docs/latest/commands/eval) keeps the read-decide-update cycle atomic, so\n    concurrent requests cannot double-spend tokens or lose counter updates.\n-   Sub-millisecond latency means the rate check sits on the synchronous request path\n    without adding meaningful delay, even at millions of requests per second.\n-   [Active-Active replication](https://redis.io/docs/latest/operate/rs/databases/active-active)\n    provides CRDT-based global consistency for rate limits enforced across regions."},{"id":"ecosystem","title":"Ecosystem","role":"content","text":"The following libraries and frameworks provide Redis-backed rate limiting:\n\n-   **Node.js**:\n    [rate-limiter-flexible](https://github.com/animir/node-rate-limiter-flexible),\n    [express-rate-limit](https://github.com/express-rate-limit/express-rate-limit)\n    (with a Redis store)\n-   **Python**: [limits](https://limits.readthedocs.io/),\n    plus custom implementations on top of [redis-py](https://redis.readthedocs.io/)\n-   **Go**: [go-redis/redis_rate](https://github.com/go-redis/redis_rate)\n-   **API gateways**:\n    [Apache APISIX](https://apisix.apache.org/docs/apisix/plugins/limit-count/),\n    [Kong](https://docs.konghq.com/hub/kong-inc/rate-limiting/), and\n    [Envoy](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/rate_limit_filter)\n    use Redis for distributed rate limiting"},{"id":"code-examples-to-build-your-own-redis-rate-limiter","title":"Code examples to build your own Redis rate limiter","role":"example","text":"The following guides show how to build a token bucket rate limiter with Redis and Lua scripts.\nEach guide includes a runnable interactive demo for each of the following client libraries:\n\n* [redis-py (Python)](https://redis.io/docs/latest/develop/use-cases/rate-limiter/redis-py)\n* [node-redis (Node.js)](https://redis.io/docs/latest/develop/use-cases/rate-limiter/nodejs)\n* [go-redis (Go)](https://redis.io/docs/latest/develop/use-cases/rate-limiter/go)\n* [Jedis (Java)](https://redis.io/docs/latest/develop/use-cases/rate-limiter/java-jedis)\n* [Lettuce (Java)](https://redis.io/docs/latest/develop/use-cases/rate-limiter/java-lettuce)\n* [StackExchange.Redis (C#)](https://redis.io/docs/latest/develop/use-cases/rate-limiter/dotnet)\n* [Predis (PHP)](https://redis.io/docs/latest/develop/use-cases/rate-limiter/php)\n* [redis-rb (Ruby)](https://redis.io/docs/latest/develop/use-cases/rate-limiter/ruby)\n* [redis-rs (Rust)](https://redis.io/docs/latest/develop/use-cases/rate-limiter/rust)"}],"examples":[]}
{"schema_version":2,"id":"develop/use-cases/rate-limiter/java-jedis","title":"Token bucket rate limiter with Redis and Java","url":"https://redis.io/docs/latest/develop/use-cases/rate-limiter/java-jedis/","summary":"Implement a token bucket rate limiter using Redis and Lua scripts in Java","aliases":["/develop/use-cases/rate-limiter/java","/develop/use-cases/rate-limiter/jedis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-29T14:34:47+01:00","children":[],"page_type":"content","content_hash":"1b719b840ef317447074b60a88972fea7d4913405ca9bcc7398711f7ddf877e5","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a distributed token bucket rate limiter using Redis and Lua scripts in Java with the [`Jedis`](https://redis.io/docs/latest/develop/clients/jedis) client library."},{"id":"overview","title":"Overview","role":"overview","text":"Rate limiting is a critical technique for controlling the rate at which operations are performed. Common use cases include:\n\n* Limiting API requests per user or IP address\n* Preventing abuse and protecting against denial-of-service attacks\n* Ensuring fair resource allocation across multiple clients\n* Throttling background jobs or batch operations\n\nThe **token bucket algorithm** is a popular rate limiting approach that allows bursts of traffic while maintaining an average rate limit over time. This guide covers the Java implementation using the [`Jedis`](https://redis.io/docs/latest/develop/clients/jedis) client library, taking advantage of Java's `try-with-resources` for connection management, `JedisPool` for connection pooling, and checked exceptions for error handling."},{"id":"how-it-works","title":"How it works","role":"content","text":"The token bucket algorithm works like a bucket that holds tokens:\n\n1. **Initialization**: The bucket starts with a maximum capacity of tokens\n2. **Refill**: Tokens are added to the bucket at a constant rate (for example, 1 token per second)\n3. **Consumption**: Each request consumes one token from the bucket\n4. **Decision**: If tokens are available, the request is allowed; otherwise, it's denied\n5. **Capacity limit**: The bucket never exceeds its maximum capacity\n\nThis approach allows for burst traffic (using accumulated tokens) while enforcing an average rate limit over time."},{"id":"why-use-redis","title":"Why use Redis?","role":"content","text":"Redis is ideal for distributed rate limiting because:\n\n* **Atomic operations**: Lua scripts execute atomically, preventing race conditions\n* **Shared state**: Multiple application servers can share the same rate limit counters\n* **High performance**: In-memory operations provide microsecond latency\n* **Automatic expiration**: Keys can be set to expire automatically (though not used in this implementation)"},{"id":"the-lua-script","title":"The Lua script","role":"content","text":"The core of this implementation is a Lua script that runs atomically on the Redis server. This ensures that checking and updating the token bucket happens in a single operation, preventing race conditions in distributed environments.\n\nHere's how the script works:\n\n[code example]"},{"id":"script-breakdown","title":"Script breakdown","role":"content","text":"1. **State retrieval**: Uses [`HMGET`](https://redis.io/docs/latest/commands/hmget) to fetch the current token count and last refill time from a hash\n2. **Initialization**: On first use, sets tokens to full capacity\n3. **Token refill calculation**: Computes how many tokens should be added based on elapsed time\n4. **Capacity enforcement**: Uses `math.min()` to ensure tokens never exceed capacity\n5. **Token consumption**: Decrements the token count if available\n6. **State update**: Uses [`HMSET`](https://redis.io/docs/latest/commands/hmset) to save the new state\n7. **Return value**: Returns both the decision (allowed/denied) and remaining tokens"},{"id":"why-atomicity-matters","title":"Why atomicity matters","role":"content","text":"Without atomic execution, race conditions could occur:\n\n* **Double spending**: Two requests could read the same token count and both succeed when only one should\n* **Lost updates**: Concurrent updates could overwrite each other's changes\n* **Inconsistent state**: Token count and refill time could become desynchronized\n\nUsing [`EVAL`](https://redis.io/docs/latest/commands/eval) or [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) ensures the entire operation executes atomically, making it safe for distributed systems."},{"id":"installation","title":"Installation","role":"setup","text":"Add the Jedis dependency to your project:\n\n* If you use **Maven**:\n\n  [code example]\n\n* If you use **Gradle**:\n\n  [code example]"},{"id":"using-the-java-class","title":"Using the Java class","role":"content","text":"The `TokenBucket` class provides a thread-safe interface for rate limiting\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/rate-limiter/java-jedis/TokenBucket.java)):\n\n[code example]\n\nJedis operations are synchronous and thread-safe when using `JedisPool`, which manages a pool of connections internally. The `allow()` method returns a `RateLimitResult` record containing both the decision and the remaining token count."},{"id":"configuration-parameters","title":"Configuration parameters","role":"configuration","text":"* **capacity**: Maximum number of tokens in the bucket (controls burst size)\n* **refillRate**: Number of tokens added per refill interval\n* **refillInterval**: Time in seconds between refills\n\nFor example:\n* `capacity=10, refillRate=1, refillInterval=1.0` allows 1 request per second with bursts up to 10\n* `capacity=100, refillRate=10, refillInterval=1.0` allows 10 requests per second with bursts up to 100\n* `capacity=60, refillRate=1, refillInterval=60.0` allows 1 request per minute with bursts up to 60"},{"id":"rate-limit-keys","title":"Rate limit keys","role":"content","text":"The `key` parameter identifies what you're rate limiting. Common patterns:\n\n* **Per user**: `user:{userId}` - Limit each user independently\n* **Per IP address**: `ip:{ipAddress}` - Limit by client IP\n* **Per API endpoint**: `api:{endpoint}:{userId}` - Different limits per endpoint\n* **Global**: `global:api` - Single limit shared across all requests"},{"id":"script-caching-with-evalsha","title":"Script caching with EVALSHA","role":"content","text":"The Java implementation uses [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) for optimal performance. On first use, the Lua script is loaded into Redis with `SCRIPT LOAD`, and subsequent calls use the cached SHA1 hash. If the script is evicted from the cache, the class automatically falls back to [`EVAL`](https://redis.io/docs/latest/commands/eval) and reloads the script. The script loading uses `volatile` and synchronization to ensure thread safety across multiple threads.\n\n[code example]"},{"id":"thread-safety","title":"Thread safety","role":"content","text":"The `TokenBucket` class is thread-safe. You can share a single instance across your application, including from multiple threads in a servlet container or web framework:\n\n[code example]"},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of two Java files. Download them from the [`java-jedis` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/rate-limiter/java-jedis) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"A demonstration HTTP server is included to show the rate limiter in action\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/rate-limiter/java-jedis/DemoServer.java)):\n\n[code example]\n\nThe demo provides an interactive web interface where you can:\n\n* Submit requests and see them allowed or denied in real-time\n* View the current token count\n* Adjust rate limit parameters dynamically\n* Test different rate limiting scenarios\n\nThe demo assumes Redis is running on `localhost:6379` but you can specify a different host and port using the `--redis-host HOST` and `--redis-port PORT` command-line arguments. Visit `http://localhost:8080` in your browser to try it out."},{"id":"response-headers","title":"Response headers","role":"returns","text":"It's common to include rate limit information in HTTP response headers:\n\n[code example]"},{"id":"customization","title":"Customization","role":"content","text":""},{"id":"using-as-a-servlet-filter","title":"Using as a servlet filter","role":"content","text":"You can wrap the rate limiter as a servlet filter for use with any Java web framework:\n\n[code example]"},{"id":"error-handling","title":"Error handling","role":"errors","text":"The `allow()` method may throw a `JedisException` if the Redis connection is lost. Wrap calls in try/catch blocks for production use:\n\n[code example]"},{"id":"alternative-rate-limiting-algorithms","title":"Alternative rate limiting algorithms","role":"content","text":"The token bucket algorithm above handles most use cases but Redis supports\nother rate limiter patterns that might fit your requirements better. The table\nbelow lists four other algorithms alongside token bucket and summarizes\ntheir features:\n\n| Algorithm | Memory | Accuracy | Burst behavior | Best for |\n|---|---|---|---|---|\n| [Token bucket](#how-it-works) | 1 key (hash) | Exact | Controlled bursts | APIs with bursty traffic |\n| [Fixed window counter](#fixed-window-counter) | 1 key (string) | Approximate | 2x burst at boundaries | Simple API limits |\n| [Sliding window log](#sliding-window-log) | O(n) entries | Exact | No bursts | High-value APIs, audit trails |\n| [Sliding window counter](#sliding-window-counter) | 2 keys (string) | Near-exact | Smoothed boundaries | General-purpose APIs |\n| [Leaky bucket (policing)](#leaky-bucket-policing) | 1 key (hash) | Exact | No bursts | Strict no-burst enforcement |\n\nThe sections below give example implementations of these other algorithms.\nThe three time-based algorithms call `redis.call('TIME')` inside the Lua\nscript to derive the current timestamp from the Redis server clock. This\neliminates clock drift when the limiter runs across multiple application\nservers. The fixed window counter reads no clock: the key's TTL defines\nthe window."},{"id":"fixed-window-counter","title":"Fixed window counter","role":"content","text":"Counts requests within discrete, non-overlapping time intervals.\nSimplest algorithm — one key per window, one `EVAL` round trip.\n\n[code example]\n\n**Trade-off**: A client can make 2x requests by sending `limit` requests\nat the end of one window and `limit` requests at the start of the next."},{"id":"sliding-window-log","title":"Sliding window log","role":"content","text":"Records the exact timestamp of every request in a sorted set.\nProvides a true rolling window with no boundary bursts.\n\n[code example]\n\n**Trade-off**: Memory grows O(n) with request volume. Not ideal for\nhigh-volume, high-cardinality rate limiting."},{"id":"sliding-window-counter","title":"Sliding window counter","role":"content","text":"Blends two fixed-window counters using a weighted average to approximate\na true sliding window. Near-exact accuracy with the same low memory\nfootprint as a fixed window. The two keys use hash tags so they map\nto the same slot in Redis Cluster.\n\n[code example]\n\n**Trade-off**: The weighted estimate may let slightly more or fewer\nrequests through than the exact limit. Negligible for most apps."},{"id":"leaky-bucket-policing","title":"Leaky bucket (policing)","role":"content","text":"A virtual bucket fills with incoming requests and drains at a fixed rate.\nIf the bucket is full, requests are rejected immediately. This is the\npolicing variant — requests are allowed or denied instantly with no delay.\n\n[code example]\n\n**Trade-off**: Overflow traffic is rejected immediately. Clients must\nhandle `429 Too Many Requests` and retry with backoff."},{"id":"learn-more","title":"Learn more","role":"related","text":"* [EVAL command](https://redis.io/docs/latest/commands/eval) - Execute Lua scripts\n* [EVALSHA command](https://redis.io/docs/latest/commands/evalsha) - Execute cached Lua scripts\n* [Lua scripting](https://redis.io/docs/latest/develop/programmability/eval-intro) - Introduction to Redis Lua scripting\n* [HMGET command](https://redis.io/docs/latest/commands/hmget) - Get multiple hash fields\n* [HMSET command](https://redis.io/docs/latest/commands/hmset) - Set multiple hash fields\n* [Jedis client](https://redis.io/docs/latest/develop/clients/jedis) - Redis Java client documentation"}],"examples":[{"id":"the-lua-script-ex0","language":"lua","code":"local key = KEYS[1]\nlocal capacity = tonumber(ARGV[1])\nlocal refill_rate = tonumber(ARGV[2])\nlocal refill_interval = tonumber(ARGV[3])\nlocal now = tonumber(ARGV[4])\n\n-- Get current state or initialize\nlocal bucket = redis.call('HMGET', key, 'tokens', 'last_refill')\nlocal tokens = tonumber(bucket[1])\nlocal last_refill = tonumber(bucket[2])\n\n-- Initialize if this is the first request\nif tokens == nil then\n    tokens = capacity\n    last_refill = now\nend\n\n-- Calculate token refill\nlocal time_passed = now - last_refill\nlocal refills = math.floor(time_passed / refill_interval)\n\nif refills > 0 then\n    tokens = math.min(capacity, tokens + (refills * refill_rate))\n    last_refill = last_refill + (refills * refill_interval)\nend\n\n-- Try to consume a token\nlocal allowed = 0\nif tokens >= 1 then\n    tokens = tokens - 1\n    allowed = 1\nend\n\n-- Update state\nredis.call('HMSET', key, 'tokens', tokens, 'last_refill', last_refill)\n\n-- Return result: allowed (1 or 0) and remaining tokens\nreturn {allowed, tokens}","section_id":"the-lua-script"},{"id":"installation-ex0","language":"xml","code":"<dependency>\n      <groupId>redis.clients</groupId>\n      <artifactId>jedis</artifactId>\n      <version>5.2.0</version>\n  </dependency>","section_id":"installation"},{"id":"installation-ex1","language":"groovy","code":"implementation 'redis.clients:jedis:5.2.0'","section_id":"installation"},{"id":"using-the-java-class-ex0","language":"java","code":"import redis.clients.jedis.JedisPool;\n\npublic class Main {\n    public static void main(String[] args) {\n        // Create a Redis connection pool\n        JedisPool jedisPool = new JedisPool(\"localhost\", 6379);\n\n        // Create a rate limiter: 10 requests per second\n        TokenBucket limiter = new TokenBucket(10, 1, 1.0, jedisPool);\n\n        // Check if a request should be allowed\n        TokenBucket.RateLimitResult result = limiter.allow(\"user:123\");\n\n        if (result.allowed()) {\n            System.out.printf(\"Request allowed. %.0f tokens remaining.%n\", result.remaining());\n            // Process the request\n        } else {\n            System.out.println(\"Request denied. Rate limit exceeded.\");\n            // Return 429 Too Many Requests\n        }\n    }\n}","section_id":"using-the-java-class"},{"id":"script-caching-with-evalsha-ex0","language":"java","code":"// The class handles script caching automatically.\n// First call loads the script, subsequent calls use EVALSHA.\nTokenBucket.RateLimitResult result1 = limiter.allow(\"user:123\"); // Uses EVAL + caches\nTokenBucket.RateLimitResult result2 = limiter.allow(\"user:123\"); // Uses EVALSHA (faster)","section_id":"script-caching-with-evalsha"},{"id":"thread-safety-ex0","language":"java","code":"// Create a shared limiter instance\nTokenBucket limiter = new TokenBucket(10, 1, 1.0, jedisPool);\n\n// Safe to call from multiple threads\nExecutorService executor = Executors.newFixedThreadPool(10);\nfor (int i = 0; i < 20; i++) {\n    final int id = i;\n    executor.submit(() -> {\n        TokenBucket.RateLimitResult result = limiter.allow(\"shared:resource\");\n        System.out.printf(\"thread %d: allowed=%b remaining=%.0f%n\",\n                id, result.allowed(), result.remaining());\n    });\n}\nexecutor.shutdown();","section_id":"thread-safety"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir rate-limiter-demo && cd rate-limiter-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/rate-limiter/java-jedis\ncurl -O $BASE/TokenBucket.java\ncurl -O $BASE/DemoServer.java","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"# Compile\njavac -cp jedis-5.2.0.jar TokenBucket.java DemoServer.java\n\n# Run the demo server\njava -cp .:jedis-5.2.0.jar DemoServer","section_id":"start-the-demo-server"},{"id":"response-headers-ex0","language":"java","code":"int capacity = 10;\ndouble refillInterval = 1.0;\nTokenBucket limiter = new TokenBucket(capacity, 1, refillInterval, jedisPool);\n\nTokenBucket.RateLimitResult result = limiter.allow(\"user:\" + userId);\n\n// Add standard rate limit headers\nresponse.setHeader(\"X-RateLimit-Limit\", String.valueOf(capacity));\nresponse.setHeader(\"X-RateLimit-Remaining\", String.valueOf((int) result.remaining()));\nresponse.setHeader(\"X-RateLimit-Reset\",\n        String.valueOf(System.currentTimeMillis() / 1000 + (long) refillInterval));\n\nif (!result.allowed()) {\n    response.setHeader(\"Retry-After\", String.valueOf((int) refillInterval));\n    response.setStatus(429); // Too Many Requests\n}","section_id":"response-headers"},{"id":"using-as-a-servlet-filter-ex0","language":"java","code":"public class RateLimitFilter implements Filter {\n    private TokenBucket limiter;\n\n    @Override\n    public void init(FilterConfig config) {\n        JedisPool jedisPool = new JedisPool(\"localhost\", 6379);\n        limiter = new TokenBucket(10, 1, 1.0, jedisPool);\n    }\n\n    @Override\n    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)\n            throws IOException, ServletException {\n        HttpServletRequest httpReq = (HttpServletRequest) req;\n        HttpServletResponse httpRes = (HttpServletResponse) res;\n\n        String key = \"ip:\" + httpReq.getRemoteAddr();\n        TokenBucket.RateLimitResult result = limiter.allow(key);\n\n        httpRes.setHeader(\"X-RateLimit-Remaining\",\n                String.valueOf((int) result.remaining()));\n\n        if (!result.allowed()) {\n            httpRes.setStatus(429);\n            httpRes.getWriter().write(\"{\\\"error\\\": \\\"Rate limit exceeded\\\"}\");\n            return;\n        }\n        chain.doFilter(req, res);\n    }\n}","section_id":"using-as-a-servlet-filter"},{"id":"error-handling-ex0","language":"java","code":"try {\n    TokenBucket.RateLimitResult result = limiter.allow(\"user:123\");\n    // Handle result\n} catch (JedisException e) {\n    System.err.println(\"Rate limiter error: \" + e.getMessage());\n    // Fail open: allow the request when Redis is unavailable\n    // Or fail closed: deny the request\n}","section_id":"error-handling"},{"id":"fixed-window-counter-ex0","language":"java","code":"import java.util.List;\n\nimport redis.clients.jedis.Jedis;\nimport redis.clients.jedis.JedisPool;\n\npublic class FixedWindowLimiter {\n\n    private static final String SCRIPT = \"\"\"\n            local key    = KEYS[1]\n            local limit  = tonumber(ARGV[1])\n            local window = tonumber(ARGV[2])\n\n            local count = redis.call('INCR', key)\n            if count == 1 then\n                redis.call('EXPIRE', key, window)\n            end\n\n            local ttl = redis.call('PTTL', key)\n\n            if count > limit then\n                return {0, ttl}\n            end\n            return {1, ttl}\n            \"\"\";\n\n    public record Decision(boolean allowed, long retryAfterMs) {}\n\n    @SuppressWarnings(\"unchecked\")\n    public static Decision allow(JedisPool pool, String key, int limit, int windowSeconds) {\n        try (Jedis jedis = pool.getResource()) {\n            List<Long> result = (List<Long>) jedis.eval(\n                    SCRIPT,\n                    List.of(key),\n                    List.of(String.valueOf(limit), String.valueOf(windowSeconds)));\n\n            boolean allowed = result.get(0) == 1L;\n            return new Decision(allowed, allowed ? 0L : result.get(1));\n        }\n    }\n}","section_id":"fixed-window-counter"},{"id":"sliding-window-log-ex0","language":"java","code":"import java.util.List;\nimport java.util.UUID;\n\nimport redis.clients.jedis.Jedis;\nimport redis.clients.jedis.JedisPool;\n\npublic class SlidingWindowLogLimiter {\n\n    private static final String SCRIPT = \"\"\"\n            local key    = KEYS[1]\n            local limit  = tonumber(ARGV[1])\n            local window = tonumber(ARGV[2])\n            local member = ARGV[3]\n\n            local t      = redis.call('TIME')\n            local now    = tonumber(t[1]) + tonumber(t[2]) / 1e6\n            local cutoff = now - window\n\n            redis.call('ZREMRANGEBYSCORE', key, '-inf', cutoff)\n\n            local count = redis.call('ZCARD', key)\n\n            if count < limit then\n                redis.call('ZADD', key, now, member)\n                redis.call('EXPIRE', key, window * 2)\n                return {1, 0}\n            end\n\n            local oldest = redis.call('ZRANGE', key, 0, 0, 'WITHSCORES')\n            local retry_after_ms = 0\n            if oldest[2] then\n                retry_after_ms = math.floor((tonumber(oldest[2]) + window - now) * 1000)\n            end\n\n            return {0, retry_after_ms}\n            \"\"\";\n\n    public record Decision(boolean allowed, long retryAfterMs) {}\n\n    @SuppressWarnings(\"unchecked\")\n    public static Decision allow(JedisPool pool, String key, int limit, int windowSeconds) {\n        try (Jedis jedis = pool.getResource()) {\n            List<Long> result = (List<Long>) jedis.eval(\n                    SCRIPT,\n                    List.of(key),\n                    List.of(String.valueOf(limit),\n                            String.valueOf(windowSeconds),\n                            UUID.randomUUID().toString()));\n\n            return new Decision(result.get(0) == 1L, result.get(1));\n        }\n    }\n}","section_id":"sliding-window-log"},{"id":"sliding-window-counter-ex0","language":"java","code":"import java.util.List;\n\nimport redis.clients.jedis.Jedis;\nimport redis.clients.jedis.JedisPool;\n\npublic class SlidingWindowCounterLimiter {\n\n    private static final String SCRIPT = \"\"\"\n            local base   = KEYS[1]\n            local limit  = tonumber(ARGV[1])\n            local window = tonumber(ARGV[2])\n\n            local t   = redis.call('TIME')\n            local now = tonumber(t[1]) + tonumber(t[2]) / 1e6\n\n            local window_num = math.floor(now / window)\n            local elapsed     = (now % window) / window\n\n            local curr_key = base .. ':' .. window_num\n            local prev_key = base .. ':' .. (window_num - 1)\n\n            local prev = tonumber(redis.call('GET', prev_key) or 0)\n            local curr = tonumber(redis.call('GET', curr_key) or 0)\n\n            local estimate = prev * (1 - elapsed) + curr\n\n            if estimate >= limit then\n                return {0, 0}\n            end\n\n            local new_count = redis.call('INCR', curr_key)\n            if new_count == 1 then\n                redis.call('EXPIRE', curr_key, window * 2)\n            end\n\n            return {1, 0}\n            \"\"\";\n\n    @SuppressWarnings(\"unchecked\")\n    public static boolean allow(JedisPool pool, String key, int limit, int windowSeconds) {\n        try (Jedis jedis = pool.getResource()) {\n            List<Long> result = (List<Long>) jedis.eval(\n                    SCRIPT,\n                    List.of(\"{\" + key + \"}\"),\n                    List.of(String.valueOf(limit), String.valueOf(windowSeconds)));\n\n            return result.get(0) == 1L;\n        }\n    }\n}","section_id":"sliding-window-counter"},{"id":"leaky-bucket-policing-ex0","language":"java","code":"import java.util.List;\n\nimport redis.clients.jedis.Jedis;\nimport redis.clients.jedis.JedisPool;\n\npublic class LeakyBucketLimiter {\n\n    private static final String SCRIPT = \"\"\"\n            local key       = KEYS[1]\n            local capacity  = tonumber(ARGV[1])\n            local leak_rate = tonumber(ARGV[2])\n\n            local t   = redis.call('TIME')\n            local now = tonumber(t[1]) + tonumber(t[2]) / 1e6\n\n            local data      = redis.call('HGETALL', key)\n            local level     = 0\n            local last_leak = now\n\n            if #data > 0 then\n                for i = 1, #data, 2 do\n                    if data[i] == 'level' then\n                        level = tonumber(data[i+1])\n                    elseif data[i] == 'last_leak' then\n                        last_leak = tonumber(data[i+1])\n                    end\n                end\n            end\n\n            local elapsed = now - last_leak\n            level = math.max(0, level - elapsed * leak_rate)\n\n            if level + 1 > capacity then\n                return {0, math.floor((level + 1 - capacity) / leak_rate * 1000)}\n            end\n\n            level = level + 1\n            local ttl = math.ceil(capacity / leak_rate) + 1\n\n            redis.call('HSET', key, 'level', level, 'last_leak', now)\n            redis.call('EXPIRE', key, ttl)\n\n            return {1, 0}\n            \"\"\";\n\n    public record Decision(boolean allowed, long retryAfterMs) {}\n\n    @SuppressWarnings(\"unchecked\")\n    public static Decision allow(JedisPool pool, String key, int capacity, double leakRate) {\n        try (Jedis jedis = pool.getResource()) {\n            List<Long> result = (List<Long>) jedis.eval(\n                    SCRIPT,\n                    List.of(key),\n                    List.of(String.valueOf(capacity), String.valueOf(leakRate)));\n\n            return new Decision(result.get(0) == 1L, result.get(1));\n        }\n    }\n}","section_id":"leaky-bucket-policing"}]}
{"schema_version":2,"id":"develop/use-cases/rate-limiter/java-lettuce","title":"Token bucket rate limiter with Redis and Java (Lettuce)","url":"https://redis.io/docs/latest/develop/use-cases/rate-limiter/java-lettuce/","summary":"Implement a token bucket rate limiter using Redis and Lua scripts in Java with Lettuce","aliases":["/develop/use-cases/rate-limiter/lettuce"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-29T14:34:47+01:00","children":[],"page_type":"content","content_hash":"df9f4cbdf5658dc8f9460514a35d380e82b462f19e0ef008d50fd28b19bcf134","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a distributed token bucket rate limiter using Redis and Lua scripts in Java with the [`Lettuce`](https://redis.io/docs/latest/develop/clients/lettuce) client library."},{"id":"overview","title":"Overview","role":"overview","text":"Rate limiting is a critical technique for controlling the rate at which operations are performed. Common use cases include:\n\n* Limiting API requests per user or IP address\n* Preventing abuse and protecting against denial-of-service attacks\n* Ensuring fair resource allocation across multiple clients\n* Throttling background jobs or batch operations\n\nThe **token bucket algorithm** is a popular rate limiting approach that allows bursts of traffic while maintaining an average rate limit over time. This guide covers the Java implementation using the [`Lettuce`](https://redis.io/docs/latest/develop/clients/lettuce) client library, taking advantage of Lettuce's `RedisClient` for connection management, `StatefulRedisConnection` for multiplexed connections, and support for synchronous, asynchronous, and reactive command execution."},{"id":"how-it-works","title":"How it works","role":"content","text":"The token bucket algorithm works like a bucket that holds tokens:\n\n1. **Initialization**: The bucket starts with a maximum capacity of tokens\n2. **Refill**: Tokens are added to the bucket at a constant rate (for example, 1 token per second)\n3. **Consumption**: Each request consumes one token from the bucket\n4. **Decision**: If tokens are available, the request is allowed; otherwise, it's denied\n5. **Capacity limit**: The bucket never exceeds its maximum capacity\n\nThis approach allows for burst traffic (using accumulated tokens) while enforcing an average rate limit over time."},{"id":"why-use-redis","title":"Why use Redis?","role":"content","text":"Redis is ideal for distributed rate limiting because:\n\n* **Atomic operations**: Lua scripts execute atomically, preventing race conditions\n* **Shared state**: Multiple application servers can share the same rate limit counters\n* **High performance**: In-memory operations provide microsecond latency\n* **Automatic expiration**: Keys can be set to expire automatically (though not used in this implementation)"},{"id":"the-lua-script","title":"The Lua script","role":"content","text":"The core of this implementation is a Lua script that runs atomically on the Redis server. This ensures that checking and updating the token bucket happens in a single operation, preventing race conditions in distributed environments.\n\nHere's how the script works:\n\n[code example]"},{"id":"script-breakdown","title":"Script breakdown","role":"content","text":"1. **State retrieval**: Uses [`HMGET`](https://redis.io/docs/latest/commands/hmget) to fetch the current token count and last refill time from a hash\n2. **Initialization**: On first use, sets tokens to full capacity\n3. **Token refill calculation**: Computes how many tokens should be added based on elapsed time\n4. **Capacity enforcement**: Uses `math.min()` to ensure tokens never exceed capacity\n5. **Token consumption**: Decrements the token count if available\n6. **State update**: Uses [`HMSET`](https://redis.io/docs/latest/commands/hmset) to save the new state\n7. **Return value**: Returns both the decision (allowed/denied) and remaining tokens"},{"id":"why-atomicity-matters","title":"Why atomicity matters","role":"content","text":"Without atomic execution, race conditions could occur:\n\n* **Double spending**: Two requests could read the same token count and both succeed when only one should\n* **Lost updates**: Concurrent updates could overwrite each other's changes\n* **Inconsistent state**: Token count and refill time could become desynchronized\n\nUsing [`EVAL`](https://redis.io/docs/latest/commands/eval) or [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) ensures the entire operation executes atomically, making it safe for distributed systems."},{"id":"installation","title":"Installation","role":"setup","text":"Add the Lettuce dependency to your project:\n\n* If you use **Maven**:\n\n  [code example]\n\n* If you use **Gradle**:\n\n  [code example]"},{"id":"lettuce-vs-jedis","title":"Lettuce vs Jedis","role":"content","text":"Unlike [`Jedis`](https://redis.io/docs/latest/develop/clients/jedis), which uses a connection pool (`JedisPool`) with one thread per connection, Lettuce uses a single `RedisClient` with multiplexed connections through `StatefulRedisConnection`. This means:\n\n* **No connection pool needed**: A single connection handles multiple concurrent requests\n* **Thread-safe by default**: The `StatefulRedisConnection` and its command interfaces are thread-safe\n* **Sync, async, and reactive**: You can choose the execution model that fits your application"},{"id":"using-the-java-class","title":"Using the Java class","role":"content","text":"The `TokenBucket` class provides a thread-safe interface for rate limiting\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/rate-limiter/java-lettuce/TokenBucket.java)):\n\n[code example]\n\nNote that Lettuce takes a `StatefulRedisConnection` directly rather than a connection pool. The connection is multiplexed, so a single connection can safely handle concurrent requests from multiple threads. The `allow()` method returns a `RateLimitResult` record containing both the decision and the remaining token count."},{"id":"configuration-parameters","title":"Configuration parameters","role":"configuration","text":"* **capacity**: Maximum number of tokens in the bucket (controls burst size)\n* **refillRate**: Number of tokens added per refill interval\n* **refillInterval**: Time in seconds between refills\n\nFor example:\n* `capacity=10, refillRate=1, refillInterval=1.0` allows 1 request per second with bursts up to 10\n* `capacity=100, refillRate=10, refillInterval=1.0` allows 10 requests per second with bursts up to 100\n* `capacity=60, refillRate=1, refillInterval=60.0` allows 1 request per minute with bursts up to 60"},{"id":"rate-limit-keys","title":"Rate limit keys","role":"content","text":"The `key` parameter identifies what you're rate limiting. Common patterns:\n\n* **Per user**: `user:{userId}` - Limit each user independently\n* **Per IP address**: `ip:{ipAddress}` - Limit by client IP\n* **Per API endpoint**: `api:{endpoint}:{userId}` - Different limits per endpoint\n* **Global**: `global:api` - Single limit shared across all requests"},{"id":"script-caching-with-evalsha","title":"Script caching with EVALSHA","role":"content","text":"The Java implementation uses [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) for optimal performance. On first use, the Lua script is loaded into Redis using Lettuce's `scriptLoad()` command, and subsequent calls use the cached SHA1 hash. If the script is evicted from the cache, the class automatically falls back to [`EVAL`](https://redis.io/docs/latest/commands/eval) and reloads the script. The script SHA is computed once on first use and cached for subsequent calls.\n\n[code example]"},{"id":"thread-safety","title":"Thread safety","role":"content","text":"The `TokenBucket` class is thread-safe because Lettuce's `StatefulRedisConnection` is inherently thread-safe and multiplexes commands over a single connection. You can share a single instance across your application:\n\n[code example]"},{"id":"asynchronous-usage","title":"Asynchronous usage","role":"content","text":"Lettuce also supports asynchronous command execution. The async version of the rate limiter\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/rate-limiter/java-lettuce/AsyncTokenBucket.java)) returns a `CompletableFuture<RateLimitResult>`:\n\n[code example]"},{"id":"reactive-usage","title":"Reactive usage","role":"content","text":"Lettuce's reactive API is useful when you are already using Reactor in a non-blocking application.\nThe reactive version of the rate limiter ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/rate-limiter/java-lettuce/ReactiveTokenBucket.java)) returns a `Mono<RateLimitResult>`:\n\n[code example]"},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of four Java files. Download them from the [`java-lettuce` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/rate-limiter/java-lettuce) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"A demonstration HTTP server is included to show the rate limiter in action\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/rate-limiter/java-lettuce/DemoServer.java)):\n\n[code example]\n\nThe demo provides an interactive web interface where you can:\n\n* Submit requests and see them allowed or denied in real-time\n* View the current token count\n* Adjust rate limit parameters dynamically\n* Test different rate limiting scenarios\n\nThe demo assumes Redis is running on `localhost:6379` but you can specify a different host and port using the `--redis-host HOST` and `--redis-port PORT` command-line arguments. Visit `http://localhost:8080` in your browser to try it out."},{"id":"response-headers","title":"Response headers","role":"returns","text":"It's common to include rate limit information in HTTP response headers:\n\n[code example]"},{"id":"customization","title":"Customization","role":"content","text":""},{"id":"using-as-a-servlet-filter","title":"Using as a servlet filter","role":"content","text":"You can wrap the rate limiter as a servlet filter for use with any Java web framework:\n\n[code example]\n\nNote that unlike the Jedis version, the Lettuce servlet filter includes a `destroy()` method to properly shut down the `RedisClient` and close the connection. This is important because Lettuce manages its own event loop threads that need to be cleaned up."},{"id":"error-handling","title":"Error handling","role":"errors","text":"The `allow()` method may throw a `RedisException` if the Redis connection is lost. Wrap calls in try/catch blocks for production use:\n\n[code example]\n\nLettuce also supports automatic reconnection by default. If the connection to Redis is temporarily lost, Lettuce will attempt to reconnect and replay queued commands, providing better resilience than Jedis out of the box."},{"id":"alternative-rate-limiting-algorithms","title":"Alternative rate limiting algorithms","role":"content","text":"The token bucket algorithm above handles most use cases but Redis supports\nother rate limiter patterns that might fit your requirements better. The table\nbelow lists four other algorithms alongside token bucket and summarizes\ntheir features:\n\n| Algorithm | Memory | Accuracy | Burst behavior | Best for |\n|---|---|---|---|---|\n| [Token bucket](#how-it-works) | 1 key (hash) | Exact | Controlled bursts | APIs with bursty traffic |\n| [Fixed window counter](#fixed-window-counter) | 1 key (string) | Approximate | 2x burst at boundaries | Simple API limits |\n| [Sliding window log](#sliding-window-log) | O(n) entries | Exact | No bursts | High-value APIs, audit trails |\n| [Sliding window counter](#sliding-window-counter) | 2 keys (string) | Near-exact | Smoothed boundaries | General-purpose APIs |\n| [Leaky bucket (policing)](#leaky-bucket-policing) | 1 key (hash) | Exact | No bursts | Strict no-burst enforcement |\n\nThe sections below give example implementations of these other algorithms.\nThe three time-based algorithms call `redis.call('TIME')` inside the Lua\nscript to derive the current timestamp from the Redis server clock. This\neliminates clock drift when the limiter runs across multiple application\nservers. The fixed window counter reads no clock: the key's TTL defines\nthe window."},{"id":"fixed-window-counter","title":"Fixed window counter","role":"content","text":"Counts requests within discrete, non-overlapping time intervals.\nSimplest algorithm — one key per window, one `EVAL` round trip.\n\n[code example]\n\n**Trade-off**: A client can make 2x requests by sending `limit` requests\nat the end of one window and `limit` requests at the start of the next."},{"id":"sliding-window-log","title":"Sliding window log","role":"content","text":"Records the exact timestamp of every request in a sorted set.\nProvides a true rolling window with no boundary bursts.\n\n[code example]\n\n**Trade-off**: Memory grows O(n) with request volume. Not ideal for\nhigh-volume, high-cardinality rate limiting."},{"id":"sliding-window-counter","title":"Sliding window counter","role":"content","text":"Blends two fixed-window counters using a weighted average to approximate\na true sliding window. Near-exact accuracy with the same low memory\nfootprint as a fixed window. The two keys use hash tags so they map\nto the same slot in Redis Cluster.\n\n[code example]\n\n**Trade-off**: The weighted estimate may let slightly more or fewer\nrequests through than the exact limit. Negligible for most apps."},{"id":"leaky-bucket-policing","title":"Leaky bucket (policing)","role":"content","text":"A virtual bucket fills with incoming requests and drains at a fixed rate.\nIf the bucket is full, requests are rejected immediately. This is the\npolicing variant — requests are allowed or denied instantly with no delay.\n\n[code example]\n\n**Trade-off**: Overflow traffic is rejected immediately. Clients must\nhandle `429 Too Many Requests` and retry with backoff."},{"id":"learn-more","title":"Learn more","role":"related","text":"* [EVAL command](https://redis.io/docs/latest/commands/eval) - Execute Lua scripts\n* [EVALSHA command](https://redis.io/docs/latest/commands/evalsha) - Execute cached Lua scripts\n* [Lua scripting](https://redis.io/docs/latest/develop/programmability/eval-intro) - Introduction to Redis Lua scripting\n* [HMGET command](https://redis.io/docs/latest/commands/hmget) - Get multiple hash fields\n* [HMSET command](https://redis.io/docs/latest/commands/hmset) - Set multiple hash fields\n* [Lettuce client](https://redis.io/docs/latest/develop/clients/lettuce) - Redis Lettuce client documentation"}],"examples":[{"id":"the-lua-script-ex0","language":"lua","code":"local key = KEYS[1]\nlocal capacity = tonumber(ARGV[1])\nlocal refill_rate = tonumber(ARGV[2])\nlocal refill_interval = tonumber(ARGV[3])\nlocal now = tonumber(ARGV[4])\n\n-- Get current state or initialize\nlocal bucket = redis.call('HMGET', key, 'tokens', 'last_refill')\nlocal tokens = tonumber(bucket[1])\nlocal last_refill = tonumber(bucket[2])\n\n-- Initialize if this is the first request\nif tokens == nil then\n    tokens = capacity\n    last_refill = now\nend\n\n-- Calculate token refill\nlocal time_passed = now - last_refill\nlocal refills = math.floor(time_passed / refill_interval)\n\nif refills > 0 then\n    tokens = math.min(capacity, tokens + (refills * refill_rate))\n    last_refill = last_refill + (refills * refill_interval)\nend\n\n-- Try to consume a token\nlocal allowed = 0\nif tokens >= 1 then\n    tokens = tokens - 1\n    allowed = 1\nend\n\n-- Update state\nredis.call('HMSET', key, 'tokens', tokens, 'last_refill', last_refill)\n\n-- Return result: allowed (1 or 0) and remaining tokens\nreturn {allowed, tokens}","section_id":"the-lua-script"},{"id":"installation-ex0","language":"xml","code":"<dependency>\n      <groupId>io.lettuce</groupId>\n      <artifactId>lettuce-core</artifactId>\n      <version>6.7.1.RELEASE</version>\n  </dependency>","section_id":"installation"},{"id":"installation-ex1","language":"groovy","code":"implementation 'io.lettuce:lettuce-core:6.7.1.RELEASE'","section_id":"installation"},{"id":"using-the-java-class-ex0","language":"java","code":"import io.lettuce.core.RedisClient;\nimport io.lettuce.core.api.StatefulRedisConnection;\n\npublic class Main {\n    public static void main(String[] args) {\n        // Create a Redis client and connection\n        RedisClient redisClient = RedisClient.create(\"redis://localhost:6379\");\n        StatefulRedisConnection<String, String> connection = redisClient.connect();\n\n        // Create a rate limiter: 10 requests per second\n        TokenBucket limiter = new TokenBucket(10, 1, 1.0, connection.sync());\n\n        // Check if a request should be allowed\n        TokenBucket.RateLimitResult result = limiter.allow(\"user:123\");\n\n        if (result.allowed()) {\n            System.out.printf(\"Request allowed. %.0f tokens remaining.%n\", result.remaining());\n            // Process the request\n        } else {\n            System.out.println(\"Request denied. Rate limit exceeded.\");\n            // Return 429 Too Many Requests\n        }\n\n        // Clean up\n        connection.close();\n        redisClient.shutdown();\n    }\n}","section_id":"using-the-java-class"},{"id":"script-caching-with-evalsha-ex0","language":"java","code":"// The class handles script caching automatically.\n// First call loads the script, subsequent calls use EVALSHA.\nTokenBucket.RateLimitResult result1 = limiter.allow(\"user:123\"); // Uses EVAL + caches\nTokenBucket.RateLimitResult result2 = limiter.allow(\"user:123\"); // Uses EVALSHA (faster)","section_id":"script-caching-with-evalsha"},{"id":"thread-safety-ex0","language":"java","code":"// Create shared resources\nRedisClient redisClient = RedisClient.create(\"redis://localhost:6379\");\nStatefulRedisConnection<String, String> connection = redisClient.connect();\nTokenBucket limiter = new TokenBucket(10, 1, 1.0, connection.sync());\n\n// Safe to call from multiple threads\nExecutorService executor = Executors.newFixedThreadPool(10);\nfor (int i = 0; i < 20; i++) {\n    final int id = i;\n    executor.submit(() -> {\n        TokenBucket.RateLimitResult result = limiter.allow(\"shared:resource\");\n        System.out.printf(\"thread %d: allowed=%b remaining=%.0f%n\",\n                id, result.allowed(), result.remaining());\n    });\n}\nexecutor.shutdown();\n\n// Clean up when done\nconnection.close();\nredisClient.shutdown();","section_id":"thread-safety"},{"id":"asynchronous-usage-ex0","language":"java","code":"import io.lettuce.core.RedisClient;\nimport io.lettuce.core.api.StatefulRedisConnection;\n\nRedisClient redisClient = RedisClient.create(\"redis://localhost:6379\");\nStatefulRedisConnection<String, String> connection = redisClient.connect();\n\nAsyncTokenBucket limiter = new AsyncTokenBucket(10, 1, 1.0, connection.async());\n\nlimiter.allow(\"user:123\").thenAccept(result -> {\n    if (result.allowed()) {\n        System.out.printf(\"Request allowed. %.0f tokens remaining.%n\", result.remaining());\n    } else {\n        System.out.println(\"Request denied. Rate limit exceeded.\");\n    }\n}).join();\n\nconnection.close();\nredisClient.shutdown();","section_id":"asynchronous-usage"},{"id":"reactive-usage-ex0","language":"java","code":"import io.lettuce.core.RedisClient;\nimport io.lettuce.core.api.StatefulRedisConnection;\n\nRedisClient redisClient = RedisClient.create(\"redis://localhost:6379\");\nStatefulRedisConnection<String, String> connection = redisClient.connect();\n\nReactiveTokenBucket limiter = new ReactiveTokenBucket(10, 1, 1.0, connection.reactive());\n\nlimiter.allow(\"user:123\")\n        .doOnNext(result -> {\n            if (result.allowed()) {\n                System.out.printf(\"Request allowed. %.0f tokens remaining.%n\", result.remaining());\n            } else {\n                System.out.println(\"Request denied. Rate limit exceeded.\");\n            }\n        })\n        .block();\n\nconnection.close();\nredisClient.shutdown();","section_id":"reactive-usage"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir rate-limiter-demo && cd rate-limiter-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/rate-limiter/java-lettuce\ncurl -O $BASE/TokenBucket.java\ncurl -O $BASE/AsyncTokenBucket.java\ncurl -O $BASE/ReactiveTokenBucket.java\ncurl -O $BASE/DemoServer.java","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"# Compile\njavac -cp lettuce-core-6.7.1.RELEASE.jar TokenBucket.java DemoServer.java\n\n# Run the demo server\njava -cp .:lettuce-core-6.7.1.RELEASE.jar DemoServer","section_id":"start-the-demo-server"},{"id":"response-headers-ex0","language":"java","code":"int capacity = 10;\ndouble refillInterval = 1.0;\nRedisClient redisClient = RedisClient.create(\"redis://localhost:6379\");\nStatefulRedisConnection<String, String> connection = redisClient.connect();\nTokenBucket limiter = new TokenBucket(capacity, 1, refillInterval, connection.sync());\n\nTokenBucket.RateLimitResult result = limiter.allow(\"user:\" + userId);\n\n// Add standard rate limit headers\nresponse.setHeader(\"X-RateLimit-Limit\", String.valueOf(capacity));\nresponse.setHeader(\"X-RateLimit-Remaining\", String.valueOf((int) result.remaining()));\nresponse.setHeader(\"X-RateLimit-Reset\",\n        String.valueOf(System.currentTimeMillis() / 1000 + (long) refillInterval));\n\nif (!result.allowed()) {\n    response.setHeader(\"Retry-After\", String.valueOf((int) refillInterval));\n    response.setStatus(429); // Too Many Requests\n}","section_id":"response-headers"},{"id":"using-as-a-servlet-filter-ex0","language":"java","code":"public class RateLimitFilter implements Filter {\n    private RedisClient redisClient;\n    private StatefulRedisConnection<String, String> connection;\n    private TokenBucket limiter;\n\n    @Override\n    public void init(FilterConfig config) {\n        redisClient = RedisClient.create(\"redis://localhost:6379\");\n        connection = redisClient.connect();\n        limiter = new TokenBucket(10, 1, 1.0, connection.sync());\n    }\n\n    @Override\n    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)\n            throws IOException, ServletException {\n        HttpServletRequest httpReq = (HttpServletRequest) req;\n        HttpServletResponse httpRes = (HttpServletResponse) res;\n\n        String key = \"ip:\" + httpReq.getRemoteAddr();\n        TokenBucket.RateLimitResult result = limiter.allow(key);\n\n        httpRes.setHeader(\"X-RateLimit-Remaining\",\n                String.valueOf((int) result.remaining()));\n\n        if (!result.allowed()) {\n            httpRes.setStatus(429);\n            httpRes.getWriter().write(\"{\\\"error\\\": \\\"Rate limit exceeded\\\"}\");\n            return;\n        }\n        chain.doFilter(req, res);\n    }\n\n    @Override\n    public void destroy() {\n        if (connection != null) connection.close();\n        if (redisClient != null) redisClient.shutdown();\n    }\n}","section_id":"using-as-a-servlet-filter"},{"id":"error-handling-ex0","language":"java","code":"import io.lettuce.core.RedisException;\n\ntry {\n    TokenBucket.RateLimitResult result = limiter.allow(\"user:123\");\n    // Handle result\n} catch (RedisException e) {\n    System.err.println(\"Rate limiter error: \" + e.getMessage());\n    // Fail open: allow the request when Redis is unavailable\n    // Or fail closed: deny the request\n}","section_id":"error-handling"},{"id":"fixed-window-counter-ex0","language":"java","code":"import java.util.List;\n\nimport io.lettuce.core.ScriptOutputType;\nimport io.lettuce.core.api.sync.RedisCommands;\n\npublic class FixedWindowLimiter {\n\n    private static final String SCRIPT = \"\"\"\n            local key    = KEYS[1]\n            local limit  = tonumber(ARGV[1])\n            local window = tonumber(ARGV[2])\n\n            local count = redis.call('INCR', key)\n            if count == 1 then\n                redis.call('EXPIRE', key, window)\n            end\n\n            local ttl = redis.call('PTTL', key)\n\n            if count > limit then\n                return {0, ttl}\n            end\n            return {1, ttl}\n            \"\"\";\n\n    public record Decision(boolean allowed, long retryAfterMs) {}\n\n    public static Decision allow(RedisCommands<String, String> commands, String key,\n                                 int limit, int windowSeconds) {\n        List<Long> result = commands.eval(\n                SCRIPT,\n                ScriptOutputType.MULTI,\n                new String[]{key},\n                String.valueOf(limit), String.valueOf(windowSeconds));\n\n        boolean allowed = result.get(0) == 1L;\n        return new Decision(allowed, allowed ? 0L : result.get(1));\n    }\n}","section_id":"fixed-window-counter"},{"id":"sliding-window-log-ex0","language":"java","code":"import java.util.List;\nimport java.util.UUID;\n\nimport io.lettuce.core.ScriptOutputType;\nimport io.lettuce.core.api.sync.RedisCommands;\n\npublic class SlidingWindowLogLimiter {\n\n    private static final String SCRIPT = \"\"\"\n            local key    = KEYS[1]\n            local limit  = tonumber(ARGV[1])\n            local window = tonumber(ARGV[2])\n            local member = ARGV[3]\n\n            local t      = redis.call('TIME')\n            local now    = tonumber(t[1]) + tonumber(t[2]) / 1e6\n            local cutoff = now - window\n\n            redis.call('ZREMRANGEBYSCORE', key, '-inf', cutoff)\n\n            local count = redis.call('ZCARD', key)\n\n            if count < limit then\n                redis.call('ZADD', key, now, member)\n                redis.call('EXPIRE', key, window * 2)\n                return {1, 0}\n            end\n\n            local oldest = redis.call('ZRANGE', key, 0, 0, 'WITHSCORES')\n            local retry_after_ms = 0\n            if oldest[2] then\n                retry_after_ms = math.floor((tonumber(oldest[2]) + window - now) * 1000)\n            end\n\n            return {0, retry_after_ms}\n            \"\"\";\n\n    public record Decision(boolean allowed, long retryAfterMs) {}\n\n    public static Decision allow(RedisCommands<String, String> commands, String key,\n                                 int limit, int windowSeconds) {\n        List<Long> result = commands.eval(\n                SCRIPT,\n                ScriptOutputType.MULTI,\n                new String[]{key},\n                String.valueOf(limit),\n                String.valueOf(windowSeconds),\n                UUID.randomUUID().toString());\n\n        return new Decision(result.get(0) == 1L, result.get(1));\n    }\n}","section_id":"sliding-window-log"},{"id":"sliding-window-counter-ex0","language":"java","code":"import java.util.List;\n\nimport io.lettuce.core.ScriptOutputType;\nimport io.lettuce.core.api.sync.RedisCommands;\n\npublic class SlidingWindowCounterLimiter {\n\n    private static final String SCRIPT = \"\"\"\n            local base   = KEYS[1]\n            local limit  = tonumber(ARGV[1])\n            local window = tonumber(ARGV[2])\n\n            local t   = redis.call('TIME')\n            local now = tonumber(t[1]) + tonumber(t[2]) / 1e6\n\n            local window_num = math.floor(now / window)\n            local elapsed     = (now % window) / window\n\n            local curr_key = base .. ':' .. window_num\n            local prev_key = base .. ':' .. (window_num - 1)\n\n            local prev = tonumber(redis.call('GET', prev_key) or 0)\n            local curr = tonumber(redis.call('GET', curr_key) or 0)\n\n            local estimate = prev * (1 - elapsed) + curr\n\n            if estimate >= limit then\n                return {0, 0}\n            end\n\n            local new_count = redis.call('INCR', curr_key)\n            if new_count == 1 then\n                redis.call('EXPIRE', curr_key, window * 2)\n            end\n\n            return {1, 0}\n            \"\"\";\n\n    public static boolean allow(RedisCommands<String, String> commands, String key,\n                                int limit, int windowSeconds) {\n        List<Long> result = commands.eval(\n                SCRIPT,\n                ScriptOutputType.MULTI,\n                new String[]{\"{\" + key + \"}\"},\n                String.valueOf(limit), String.valueOf(windowSeconds));\n\n        return result.get(0) == 1L;\n    }\n}","section_id":"sliding-window-counter"},{"id":"leaky-bucket-policing-ex0","language":"java","code":"import java.util.List;\n\nimport io.lettuce.core.ScriptOutputType;\nimport io.lettuce.core.api.sync.RedisCommands;\n\npublic class LeakyBucketLimiter {\n\n    private static final String SCRIPT = \"\"\"\n            local key       = KEYS[1]\n            local capacity  = tonumber(ARGV[1])\n            local leak_rate = tonumber(ARGV[2])\n\n            local t   = redis.call('TIME')\n            local now = tonumber(t[1]) + tonumber(t[2]) / 1e6\n\n            local data      = redis.call('HGETALL', key)\n            local level     = 0\n            local last_leak = now\n\n            if #data > 0 then\n                for i = 1, #data, 2 do\n                    if data[i] == 'level' then\n                        level = tonumber(data[i+1])\n                    elseif data[i] == 'last_leak' then\n                        last_leak = tonumber(data[i+1])\n                    end\n                end\n            end\n\n            local elapsed = now - last_leak\n            level = math.max(0, level - elapsed * leak_rate)\n\n            if level + 1 > capacity then\n                return {0, math.floor((level + 1 - capacity) / leak_rate * 1000)}\n            end\n\n            level = level + 1\n            local ttl = math.ceil(capacity / leak_rate) + 1\n\n            redis.call('HSET', key, 'level', level, 'last_leak', now)\n            redis.call('EXPIRE', key, ttl)\n\n            return {1, 0}\n            \"\"\";\n\n    public record Decision(boolean allowed, long retryAfterMs) {}\n\n    public static Decision allow(RedisCommands<String, String> commands, String key,\n                                 int capacity, double leakRate) {\n        List<Long> result = commands.eval(\n                SCRIPT,\n                ScriptOutputType.MULTI,\n                new String[]{key},\n                String.valueOf(capacity), String.valueOf(leakRate));\n\n        return new Decision(result.get(0) == 1L, result.get(1));\n    }\n}","section_id":"leaky-bucket-policing"}]}
{"schema_version":2,"id":"develop/use-cases/rate-limiter/nodejs","title":"Token bucket rate limiter with Redis and Node.js","url":"https://redis.io/docs/latest/develop/use-cases/rate-limiter/nodejs/","summary":"Implement a token bucket rate limiter using Redis and Lua scripts in Node.js","aliases":["/develop/use-cases/rate-limiter/node-redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-29T14:34:47+01:00","children":[],"page_type":"content","content_hash":"77d5e009a67f477d4cccb31802b74677fe30a8cce380e2f596220c048022fd95","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a distributed token bucket rate limiter using Redis and Lua scripts in Node.js with async/await."},{"id":"overview","title":"Overview","role":"overview","text":"Rate limiting is a critical technique for controlling the rate at which operations are performed. Common use cases include:\n\n* Limiting API requests per user or IP address\n* Preventing abuse and protecting against denial-of-service attacks\n* Ensuring fair resource allocation across multiple clients\n* Throttling background jobs or batch operations\n\nThe **token bucket algorithm** is a popular rate limiting approach that allows bursts of traffic while maintaining an average rate limit over time. This guide covers the Node.js implementation using the [`node-redis`](https://redis.io/docs/latest/develop/clients/nodejs) client library."},{"id":"how-it-works","title":"How it works","role":"content","text":"The token bucket algorithm works like a bucket that holds tokens:\n\n1. **Initialization**: The bucket starts with a maximum capacity of tokens\n2. **Refill**: Tokens are added to the bucket at a constant rate (for example, 1 token per second)\n3. **Consumption**: Each request consumes one token from the bucket\n4. **Decision**: If tokens are available, the request is allowed; otherwise, it's denied\n5. **Capacity limit**: The bucket never exceeds its maximum capacity\n\nThis approach allows for burst traffic (using accumulated tokens) while enforcing an average rate limit over time."},{"id":"why-use-redis","title":"Why use Redis?","role":"content","text":"Redis is ideal for distributed rate limiting because:\n\n* **Atomic operations**: Lua scripts execute atomically, preventing race conditions\n* **Shared state**: Multiple application servers can share the same rate limit counters\n* **High performance**: In-memory operations provide microsecond latency\n* **Automatic expiration**: Keys can be set to expire automatically (though not used in this implementation)"},{"id":"the-lua-script","title":"The Lua script","role":"content","text":"The core of this implementation is a Lua script that runs atomically on the Redis server. This ensures that checking and updating the token bucket happens in a single operation, preventing race conditions in distributed environments.\n\nHere's how the script works:\n\n[code example]"},{"id":"script-breakdown","title":"Script breakdown","role":"content","text":"1. **State retrieval**: Uses [`HMGET`](https://redis.io/docs/latest/commands/hmget) to fetch the current token count and last refill time from a hash\n2. **Initialization**: On first use, sets tokens to full capacity\n3. **Token refill calculation**: Computes how many tokens should be added based on elapsed time\n4. **Capacity enforcement**: Uses `math.min()` to ensure tokens never exceed capacity\n5. **Token consumption**: Decrements the token count if available\n6. **State update**: Uses [`HMSET`](https://redis.io/docs/latest/commands/hmset) to save the new state\n7. **Return value**: Returns both the decision (allowed/denied) and remaining tokens"},{"id":"why-atomicity-matters","title":"Why atomicity matters","role":"content","text":"Without atomic execution, race conditions could occur:\n\n* **Double spending**: Two requests could read the same token count and both succeed when only one should\n* **Lost updates**: Concurrent updates could overwrite each other's changes\n* **Inconsistent state**: Token count and refill time could become desynchronized\n\nUsing [`EVAL`](https://redis.io/docs/latest/commands/eval) or [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) ensures the entire operation executes atomically, making it safe for distributed systems."},{"id":"installation","title":"Installation","role":"setup","text":"Install the `redis` package from npm:\n\n[code example]"},{"id":"using-the-nodejs-module","title":"Using the Node.js module","role":"content","text":"The `TokenBucket` class provides an async interface for rate limiting\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/rate-limiter/nodejs/tokenBucket.js)):\n\n[code example]\n\nBecause `node-redis` operations are asynchronous, the `allow()` method returns a Promise. Use `await` or `.then()` to handle the result."},{"id":"configuration-parameters","title":"Configuration parameters","role":"configuration","text":"* **capacity**: Maximum number of tokens in the bucket (controls burst size)\n* **refillRate**: Number of tokens added per refill interval\n* **refillInterval**: Time in seconds between refills\n\nFor example:\n* `capacity: 10, refillRate: 1, refillInterval: 1.0` allows 1 request per second with bursts up to 10\n* `capacity: 100, refillRate: 10, refillInterval: 1.0` allows 10 requests per second with bursts up to 100\n* `capacity: 60, refillRate: 1, refillInterval: 60.0` allows 1 request per minute with bursts up to 60"},{"id":"rate-limit-keys","title":"Rate limit keys","role":"content","text":"The `key` parameter identifies what you're rate limiting. Common patterns:\n\n* **Per user**: `user:{userId}` - Limit each user independently\n* **Per IP address**: `ip:{ipAddress}` - Limit by client IP\n* **Per API endpoint**: `api:{endpoint}:{userId}` - Different limits per endpoint\n* **Global**: `global:api` - Single limit shared across all requests"},{"id":"script-caching-with-evalsha","title":"Script caching with EVALSHA","role":"content","text":"The Node.js implementation uses [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) for optimal performance. On first use, the Lua script is loaded into Redis with `SCRIPT LOAD`, and subsequent calls use the cached SHA1 hash. If the script is evicted from the cache, the module automatically falls back to [`EVAL`](https://redis.io/docs/latest/commands/eval) and reloads the script.\n\n[code example]"},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of two JavaScript files. Download them from the [`nodejs` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/rate-limiter/nodejs) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"A demonstration HTTP server is included to show the rate limiter in action\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/rate-limiter/nodejs/demoServer.js)):\n\n[code example]\n\nThe demo provides an interactive web interface where you can:\n\n* Submit requests and see them allowed or denied in real-time\n* View the current token count\n* Adjust rate limit parameters dynamically\n* Test different rate limiting scenarios\n\nThe demo assumes Redis is running on `localhost:6379` but you can specify a different host and port using the `--redis-host HOST` and `--redis-port PORT` command-line arguments. Visit `http://localhost:8080` in your browser to try it out."},{"id":"response-headers","title":"Response headers","role":"returns","text":"It's common to include rate limit information in HTTP response headers:\n\n[code example]"},{"id":"customization","title":"Customization","role":"content","text":""},{"id":"using-with-express-middleware","title":"Using with Express middleware","role":"content","text":"You can wrap the rate limiter as Express middleware for easy integration:\n\n[code example]"},{"id":"error-handling","title":"Error handling","role":"errors","text":"The `allow()` method may throw if the Redis connection is lost. Wrap calls in try/catch blocks for production use:\n\n[code example]"},{"id":"alternative-rate-limiting-algorithms","title":"Alternative rate limiting algorithms","role":"content","text":"The token bucket algorithm above handles most use cases but Redis supports\nother rate limiter patterns that might fit your requirements better. The table\nbelow lists four other algorithms alongside token bucket and summarizes\ntheir features:\n\n| Algorithm | Memory | Accuracy | Burst behavior | Best for |\n|---|---|---|---|---|\n| [Token bucket](#how-it-works) | 1 key (hash) | Exact | Controlled bursts | APIs with bursty traffic |\n| [Fixed window counter](#fixed-window-counter) | 1 key (string) | Approximate | 2x burst at boundaries | Simple API limits |\n| [Sliding window log](#sliding-window-log) | O(n) entries | Exact | No bursts | High-value APIs, audit trails |\n| [Sliding window counter](#sliding-window-counter) | 2 keys (string) | Near-exact | Smoothed boundaries | General-purpose APIs |\n| [Leaky bucket (policing)](#leaky-bucket-policing) | 1 key (hash) | Exact | No bursts | Strict no-burst enforcement |\n\nThe sections below give example implementations of these other algorithms.\nThe three time-based algorithms call `redis.call('TIME')` inside the Lua\nscript to derive the current timestamp from the Redis server clock. This\neliminates clock drift when the limiter runs across multiple application\nservers. The fixed window counter reads no clock: the key's TTL defines\nthe window."},{"id":"fixed-window-counter","title":"Fixed window counter","role":"content","text":"Counts requests within discrete, non-overlapping time intervals.\nSimplest algorithm — one key per window, one `EVAL` round trip.\n\n[code example]\n\n**Trade-off**: A client can make 2x requests by sending `limit` requests\nat the end of one window and `limit` requests at the start of the next."},{"id":"sliding-window-log","title":"Sliding window log","role":"content","text":"Records the exact timestamp of every request in a sorted set.\nProvides a true rolling window with no boundary bursts.\n\n[code example]\n\n**Trade-off**: Memory grows O(n) with request volume. Not ideal for\nhigh-volume, high-cardinality rate limiting."},{"id":"sliding-window-counter","title":"Sliding window counter","role":"content","text":"Blends two fixed-window counters using a weighted average to approximate\na true sliding window. Near-exact accuracy with the same low memory\nfootprint as a fixed window. The two keys use hash tags so they map\nto the same slot in Redis Cluster.\n\n[code example]\n\n**Trade-off**: The weighted estimate may let slightly more or fewer\nrequests through than the exact limit. Negligible for most apps."},{"id":"leaky-bucket-policing","title":"Leaky bucket (policing)","role":"content","text":"A virtual bucket fills with incoming requests and drains at a fixed rate.\nIf the bucket is full, requests are rejected immediately. This is the\npolicing variant — requests are allowed or denied instantly with no delay.\n\n[code example]\n\n**Trade-off**: Overflow traffic is rejected immediately. Clients must\nhandle `429 Too Many Requests` and retry with backoff."},{"id":"learn-more","title":"Learn more","role":"related","text":"* [EVAL command](https://redis.io/docs/latest/commands/eval) - Execute Lua scripts\n* [EVALSHA command](https://redis.io/docs/latest/commands/evalsha) - Execute cached Lua scripts\n* [Lua scripting](https://redis.io/docs/latest/develop/programmability/eval-intro) - Introduction to Redis Lua scripting\n* [HMGET command](https://redis.io/docs/latest/commands/hmget) - Get multiple hash fields\n* [HMSET command](https://redis.io/docs/latest/commands/hmset) - Set multiple hash fields\n* [Node.js client](https://redis.io/docs/latest/develop/clients/nodejs) - Redis Node.js client documentation"}],"examples":[{"id":"the-lua-script-ex0","language":"lua","code":"local key = KEYS[1]\nlocal capacity = tonumber(ARGV[1])\nlocal refill_rate = tonumber(ARGV[2])\nlocal refill_interval = tonumber(ARGV[3])\nlocal now = tonumber(ARGV[4])\n\n-- Get current state or initialize\nlocal bucket = redis.call('HMGET', key, 'tokens', 'last_refill')\nlocal tokens = tonumber(bucket[1])\nlocal last_refill = tonumber(bucket[2])\n\n-- Initialize if this is the first request\nif tokens == nil then\n    tokens = capacity\n    last_refill = now\nend\n\n-- Calculate token refill\nlocal time_passed = now - last_refill\nlocal refills = math.floor(time_passed / refill_interval)\n\nif refills > 0 then\n    tokens = math.min(capacity, tokens + (refills * refill_rate))\n    last_refill = last_refill + (refills * refill_interval)\nend\n\n-- Try to consume a token\nlocal allowed = 0\nif tokens >= 1 then\n    tokens = tokens - 1\n    allowed = 1\nend\n\n-- Update state\nredis.call('HMSET', key, 'tokens', tokens, 'last_refill', last_refill)\n\n-- Return result: allowed (1 or 0) and remaining tokens\nreturn {allowed, tokens}","section_id":"the-lua-script"},{"id":"installation-ex0","language":"bash","code":"npm install redis","section_id":"installation"},{"id":"using-the-nodejs-module-ex0","language":"javascript","code":"const { createClient } = require('redis');\nconst { TokenBucket } = require('./tokenBucket');\n\n// Create a Redis connection\nconst client = createClient({ url: 'redis://localhost:6379' });\nawait client.connect();\n\n// Create a rate limiter: 10 requests per second\nconst limiter = new TokenBucket({\n    redisClient: client,\n    capacity: 10,        // Maximum burst size\n    refillRate: 1,       // Add 1 token per interval\n    refillInterval: 1.0  // Every 1 second\n});\n\n// Check if a request should be allowed\nconst { allowed, remaining } = await limiter.allow('user:123');\n\nif (allowed) {\n    console.log(`Request allowed. ${remaining} tokens remaining.`);\n    // Process the request\n} else {\n    console.log('Request denied. Rate limit exceeded.');\n    // Return 429 Too Many Requests\n}\n\n// Disconnect when done\nawait client.disconnect();","section_id":"using-the-nodejs-module"},{"id":"script-caching-with-evalsha-ex0","language":"javascript","code":"// The module handles script caching automatically.\n// First call loads the script, subsequent calls use EVALSHA.\nconst result1 = await limiter.allow('user:123'); // Uses EVAL + caches\nconst result2 = await limiter.allow('user:123'); // Uses EVALSHA (faster)","section_id":"script-caching-with-evalsha"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir rate-limiter-demo && cd rate-limiter-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/rate-limiter/nodejs\ncurl -O $BASE/tokenBucket.js\ncurl -O $BASE/demoServer.js","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"# Install dependencies\nnpm install redis\n\n# Run the demo server\nnode demoServer.js","section_id":"start-the-demo-server"},{"id":"response-headers-ex0","language":"javascript","code":"const { allowed, remaining } = await limiter.allow(`user:${userId}`);\n\n// Add standard rate limit headers\nres.set('X-RateLimit-Limit', String(limiter.capacity));\nres.set('X-RateLimit-Remaining', String(Math.floor(remaining)));\nres.set('X-RateLimit-Reset', String(Math.floor(Date.now() / 1000 + limiter.refillInterval)));\n\nif (!allowed) {\n    res.set('Retry-After', String(Math.ceil(limiter.refillInterval)));\n    res.status(429).json({ error: 'Too Many Requests' });\n    return;\n}","section_id":"response-headers"},{"id":"using-with-express-middleware-ex0","language":"javascript","code":"function rateLimitMiddleware(limiter, keyFn) {\n    return async (req, res, next) => {\n        const key = keyFn(req);\n        const { allowed, remaining } = await limiter.allow(key);\n\n        res.set('X-RateLimit-Remaining', String(Math.floor(remaining)));\n\n        if (!allowed) {\n            res.status(429).json({ error: 'Rate limit exceeded' });\n            return;\n        }\n        next();\n    };\n}\n\n// Apply per-IP rate limiting\napp.use(rateLimitMiddleware(limiter, (req) => `ip:${req.ip}`));","section_id":"using-with-express-middleware"},{"id":"error-handling-ex0","language":"javascript","code":"try {\n    const { allowed, remaining } = await limiter.allow('user:123');\n    // Handle result\n} catch (err) {\n    console.error('Rate limiter error:', err);\n    // Fail open or closed depending on your policy\n}","section_id":"error-handling"},{"id":"fixed-window-counter-ex0","language":"javascript","code":"const FIXED_WINDOW_SCRIPT = `\nlocal key    = KEYS[1]\nlocal limit  = tonumber(ARGV[1])\nlocal window = tonumber(ARGV[2])\n\nlocal count = redis.call('INCR', key)\nif count == 1 then\n    redis.call('EXPIRE', key, window)\nend\n\nlocal ttl = redis.call('PTTL', key)\n\nif count > limit then\n    return {0, ttl}\nend\nreturn {1, ttl}\n`;\n\n// Returns { allowed, retryAfterMs }. retryAfterMs is 0 when the request is allowed.\nasync function fixedWindowAllow(client, key, limit, windowSeconds) {\n  const [allowed, ttl] = await client.eval(FIXED_WINDOW_SCRIPT, {\n    keys: [key],\n    arguments: [String(limit), String(windowSeconds)],\n  });\n\n  return {\n    allowed: allowed === 1,\n    retryAfterMs: allowed === 1 ? 0 : Number(ttl),\n  };\n}","section_id":"fixed-window-counter"},{"id":"sliding-window-log-ex0","language":"javascript","code":"const { randomUUID } = require(\"crypto\");\n\nconst SLIDING_WINDOW_LOG_SCRIPT = `\nlocal key    = KEYS[1]\nlocal limit  = tonumber(ARGV[1])\nlocal window = tonumber(ARGV[2])\nlocal member = ARGV[3]\n\nlocal t      = redis.call('TIME')\nlocal now    = tonumber(t[1]) + tonumber(t[2]) / 1e6\nlocal cutoff = now - window\n\nredis.call('ZREMRANGEBYSCORE', key, '-inf', cutoff)\n\nlocal count = redis.call('ZCARD', key)\n\nif count < limit then\n    redis.call('ZADD', key, now, member)\n    redis.call('EXPIRE', key, window * 2)\n    return {1, 0}\nend\n\nlocal oldest = redis.call('ZRANGE', key, 0, 0, 'WITHSCORES')\nlocal retry_after_ms = 0\nif oldest[2] then\n    retry_after_ms = math.floor((tonumber(oldest[2]) + window - now) * 1000)\nend\n\nreturn {0, retry_after_ms}\n`;\n\nasync function slidingWindowLogAllow(client, key, limit, windowSeconds) {\n  const [allowed, retryAfterMs] = await client.eval(SLIDING_WINDOW_LOG_SCRIPT, {\n    keys: [key],\n    arguments: [String(limit), String(windowSeconds), randomUUID()],\n  });\n\n  return { allowed: allowed === 1, retryAfterMs: Number(retryAfterMs) };\n}","section_id":"sliding-window-log"},{"id":"sliding-window-counter-ex0","language":"javascript","code":"const SLIDING_WINDOW_COUNTER_SCRIPT = `\nlocal base   = KEYS[1]\nlocal limit  = tonumber(ARGV[1])\nlocal window = tonumber(ARGV[2])\n\nlocal t   = redis.call('TIME')\nlocal now = tonumber(t[1]) + tonumber(t[2]) / 1e6\n\nlocal window_num = math.floor(now / window)\nlocal elapsed     = (now % window) / window\n\nlocal curr_key = base .. ':' .. window_num\nlocal prev_key = base .. ':' .. (window_num - 1)\n\nlocal prev = tonumber(redis.call('GET', prev_key) or 0)\nlocal curr = tonumber(redis.call('GET', curr_key) or 0)\n\nlocal estimate = prev * (1 - elapsed) + curr\n\nif estimate >= limit then\n    return {0, 0}\nend\n\nlocal new_count = redis.call('INCR', curr_key)\nif new_count == 1 then\n    redis.call('EXPIRE', curr_key, window * 2)\nend\n\nreturn {1, 0}\n`;\n\nasync function slidingWindowCounterAllow(client, key, limit, windowSeconds) {\n  const [allowed] = await client.eval(SLIDING_WINDOW_COUNTER_SCRIPT, {\n    keys: [`{${key}}`],\n    arguments: [String(limit), String(windowSeconds)],\n  });\n\n  return { allowed: allowed === 1 };\n}","section_id":"sliding-window-counter"},{"id":"leaky-bucket-policing-ex0","language":"javascript","code":"const LEAKY_BUCKET_SCRIPT = `\nlocal key       = KEYS[1]\nlocal capacity  = tonumber(ARGV[1])\nlocal leak_rate = tonumber(ARGV[2])\n\nlocal t   = redis.call('TIME')\nlocal now = tonumber(t[1]) + tonumber(t[2]) / 1e6\n\nlocal data      = redis.call('HGETALL', key)\nlocal level     = 0\nlocal last_leak = now\n\nif #data > 0 then\n    for i = 1, #data, 2 do\n        if data[i] == 'level' then\n            level = tonumber(data[i+1])\n        elseif data[i] == 'last_leak' then\n            last_leak = tonumber(data[i+1])\n        end\n    end\nend\n\nlocal elapsed = now - last_leak\nlevel = math.max(0, level - elapsed * leak_rate)\n\nif level + 1 > capacity then\n    return {0, math.floor((level + 1 - capacity) / leak_rate * 1000)}\nend\n\nlevel = level + 1\nlocal ttl = math.ceil(capacity / leak_rate) + 1\n\nredis.call('HSET', key, 'level', level, 'last_leak', now)\nredis.call('EXPIRE', key, ttl)\n\nreturn {1, 0}\n`;\n\nasync function leakyBucketAllow(client, key, capacity, leakRate) {\n  const [allowed, retryAfterMs] = await client.eval(LEAKY_BUCKET_SCRIPT, {\n    keys: [key],\n    arguments: [String(capacity), String(leakRate)],\n  });\n\n  return { allowed: allowed === 1, retryAfterMs: Number(retryAfterMs) };\n}","section_id":"leaky-bucket-policing"}]}
{"schema_version":2,"id":"develop/use-cases/rate-limiter/php","title":"Token bucket rate limiter with Redis and PHP","url":"https://redis.io/docs/latest/develop/use-cases/rate-limiter/php/","summary":"Implement a token bucket rate limiter using Redis and Lua scripts in PHP","aliases":["/develop/use-cases/rate-limiter/predis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-29T14:34:47+01:00","children":[],"page_type":"content","content_hash":"b09ceb37301e7dfd53429a800b9ae9c9d567da74fb952ec0e4beb5a2214e00dd","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a distributed token bucket rate limiter using Redis and Lua scripts in PHP with the [Predis](https://redis.io/docs/latest/develop/clients/php) client library."},{"id":"overview","title":"Overview","role":"overview","text":"Rate limiting is a critical technique for controlling the rate at which operations are performed. Common use cases include:\n\n* Limiting API requests per user or IP address\n* Preventing abuse and protecting against denial-of-service attacks\n* Ensuring fair resource allocation across multiple clients\n* Throttling background jobs or batch operations\n\nThe **token bucket algorithm** is a popular rate limiting approach that allows bursts of traffic while maintaining an average rate limit over time. This guide covers the PHP implementation using the [Predis](https://redis.io/docs/latest/develop/clients/php) client library, taking advantage of PHP's associative arrays and Composer-based dependency management."},{"id":"how-it-works","title":"How it works","role":"content","text":"The token bucket algorithm works like a bucket that holds tokens:\n\n1. **Initialization**: The bucket starts with a maximum capacity of tokens\n2. **Refill**: Tokens are added to the bucket at a constant rate (for example, 1 token per second)\n3. **Consumption**: Each request consumes one token from the bucket\n4. **Decision**: If tokens are available, the request is allowed; otherwise, it's denied\n5. **Capacity limit**: The bucket never exceeds its maximum capacity\n\nThis approach allows for burst traffic (using accumulated tokens) while enforcing an average rate limit over time."},{"id":"why-use-redis","title":"Why use Redis?","role":"content","text":"Redis is ideal for distributed rate limiting because:\n\n* **Atomic operations**: Lua scripts execute atomically, preventing race conditions\n* **Shared state**: Multiple application servers can share the same rate limit counters\n* **High performance**: In-memory operations provide microsecond latency\n* **Automatic expiration**: Keys can be set to expire automatically (though not used in this implementation)"},{"id":"the-lua-script","title":"The Lua script","role":"content","text":"The core of this implementation is a Lua script that runs atomically on the Redis server. This ensures that checking and updating the token bucket happens in a single operation, preventing race conditions in distributed environments.\n\nHere's how the script works:\n\n[code example]"},{"id":"script-breakdown","title":"Script breakdown","role":"content","text":"1. **State retrieval**: Uses [`HMGET`](https://redis.io/docs/latest/commands/hmget) to fetch the current token count and last refill time from a hash\n2. **Initialization**: On first use, sets tokens to full capacity\n3. **Token refill calculation**: Computes how many tokens should be added based on elapsed time\n4. **Capacity enforcement**: Uses `math.min()` to ensure tokens never exceed capacity\n5. **Token consumption**: Decrements the token count if available\n6. **State update**: Uses [`HMSET`](https://redis.io/docs/latest/commands/hmset) to save the new state\n7. **Return value**: Returns both the decision (allowed/denied) and remaining tokens"},{"id":"why-atomicity-matters","title":"Why atomicity matters","role":"content","text":"Without atomic execution, race conditions could occur:\n\n* **Double spending**: Two requests could read the same token count and both succeed when only one should\n* **Lost updates**: Concurrent updates could overwrite each other's changes\n* **Inconsistent state**: Token count and refill time could become desynchronized\n\nUsing [`EVAL`](https://redis.io/docs/latest/commands/eval) or [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) ensures the entire operation executes atomically, making it safe for distributed systems."},{"id":"installation","title":"Installation","role":"setup","text":"Install the Predis client library using Composer:\n\n[code example]"},{"id":"using-the-php-class","title":"Using the PHP class","role":"content","text":"The `TokenBucket` class provides a simple interface for rate limiting\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/rate-limiter/php/TokenBucket.php)):\n\n[code example]\n\nThe `allow()` method returns an associative array with two keys: `allowed` (boolean) and `remaining` (number of tokens left in the bucket)."},{"id":"configuration-parameters","title":"Configuration parameters","role":"configuration","text":"* **capacity**: Maximum number of tokens in the bucket (controls burst size)\n* **refillRate**: Number of tokens added per refill interval\n* **refillInterval**: Time in seconds between refills\n\nFor example:\n* `capacity: 10, refillRate: 1, refillInterval: 1.0` allows 1 request per second with bursts up to 10\n* `capacity: 100, refillRate: 10, refillInterval: 1.0` allows 10 requests per second with bursts up to 100\n* `capacity: 60, refillRate: 1, refillInterval: 60.0` allows 1 request per minute with bursts up to 60"},{"id":"rate-limit-keys","title":"Rate limit keys","role":"content","text":"The `key` parameter identifies what you're rate limiting. Common patterns:\n\n* **Per user**: `user:{userId}` - Limit each user independently\n* **Per IP address**: `ip:{ipAddress}` - Limit by client IP\n* **Per API endpoint**: `api:{endpoint}:{userId}` - Different limits per endpoint\n* **Global**: `global:api` - Single limit shared across all requests"},{"id":"script-caching-with-evalsha","title":"Script caching with EVALSHA","role":"content","text":"The PHP implementation uses [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) for optimal performance. On first use, the Lua script is loaded into Redis with `SCRIPT LOAD`, and subsequent calls use the cached SHA1 hash. If the script is evicted from the cache, the class automatically falls back to [`EVAL`](https://redis.io/docs/latest/commands/eval) and reloads the script.\n\n[code example]"},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of two PHP files. Download them from the [`php` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/rate-limiter/php) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"A demonstration web server is included to show the rate limiter in action\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/rate-limiter/php/demo_server.php)):\n\n[code example]\n\nThe demo provides an interactive web interface where you can:\n\n* Submit requests and see them allowed or denied in real-time\n* View the current token count\n* Adjust rate limit parameters dynamically\n* Test different rate limiting scenarios\n\nThe demo assumes Redis is running on `localhost:6379` but you can specify a different host and port using the `REDIS_HOST` and `REDIS_PORT` environment variables:\n\n[code example]\n\nVisit `http://localhost:8080` in your browser to try it out."},{"id":"response-headers","title":"Response headers","role":"returns","text":"It's common to include rate limit information in HTTP response headers:\n\n[code example]"},{"id":"customization","title":"Customization","role":"content","text":""},{"id":"using-as-psr-15-middleware","title":"Using as PSR-15 middleware","role":"content","text":"You can wrap the rate limiter as PSR-15 middleware for use with frameworks like Slim or Laravel:\n\n[code example]"},{"id":"error-handling","title":"Error handling","role":"errors","text":"The `allow()` method may throw a `Predis\\Connection\\ConnectionException` if the Redis connection is lost. Wrap calls in try/catch blocks for production use:\n\n[code example]"},{"id":"alternative-rate-limiting-algorithms","title":"Alternative rate limiting algorithms","role":"content","text":"The token bucket algorithm above handles most use cases but Redis supports\nother rate limiter patterns that might fit your requirements better. The table\nbelow lists four other algorithms alongside token bucket and summarizes\ntheir features:\n\n| Algorithm | Memory | Accuracy | Burst behavior | Best for |\n|---|---|---|---|---|\n| [Token bucket](#how-it-works) | 1 key (hash) | Exact | Controlled bursts | APIs with bursty traffic |\n| [Fixed window counter](#fixed-window-counter) | 1 key (string) | Approximate | 2x burst at boundaries | Simple API limits |\n| [Sliding window log](#sliding-window-log) | O(n) entries | Exact | No bursts | High-value APIs, audit trails |\n| [Sliding window counter](#sliding-window-counter) | 2 keys (string) | Near-exact | Smoothed boundaries | General-purpose APIs |\n| [Leaky bucket (policing)](#leaky-bucket-policing) | 1 key (hash) | Exact | No bursts | Strict no-burst enforcement |\n\nThe sections below give example implementations of these other algorithms.\nThe three time-based algorithms call `redis.call('TIME')` inside the Lua\nscript to derive the current timestamp from the Redis server clock. This\neliminates clock drift when the limiter runs across multiple application\nservers. The fixed window counter reads no clock: the key's TTL defines\nthe window."},{"id":"fixed-window-counter","title":"Fixed window counter","role":"content","text":"Counts requests within discrete, non-overlapping time intervals.\nSimplest algorithm — one key per window, one `EVAL` round trip.\n\n[code example]\n\n**Trade-off**: A client can make 2x requests by sending `limit` requests\nat the end of one window and `limit` requests at the start of the next."},{"id":"sliding-window-log","title":"Sliding window log","role":"content","text":"Records the exact timestamp of every request in a sorted set.\nProvides a true rolling window with no boundary bursts.\n\n[code example]\n\n**Trade-off**: Memory grows O(n) with request volume. Not ideal for\nhigh-volume, high-cardinality rate limiting."},{"id":"sliding-window-counter","title":"Sliding window counter","role":"content","text":"Blends two fixed-window counters using a weighted average to approximate\na true sliding window. Near-exact accuracy with the same low memory\nfootprint as a fixed window. The two keys use hash tags so they map\nto the same slot in Redis Cluster.\n\n[code example]\n\n**Trade-off**: The weighted estimate may let slightly more or fewer\nrequests through than the exact limit. Negligible for most apps."},{"id":"leaky-bucket-policing","title":"Leaky bucket (policing)","role":"content","text":"A virtual bucket fills with incoming requests and drains at a fixed rate.\nIf the bucket is full, requests are rejected immediately. This is the\npolicing variant — requests are allowed or denied instantly with no delay.\n\n[code example]\n\n**Trade-off**: Overflow traffic is rejected immediately. Clients must\nhandle `429 Too Many Requests` and retry with backoff."},{"id":"learn-more","title":"Learn more","role":"related","text":"* [EVAL command](https://redis.io/docs/latest/commands/eval) - Execute Lua scripts\n* [EVALSHA command](https://redis.io/docs/latest/commands/evalsha) - Execute cached Lua scripts\n* [Lua scripting](https://redis.io/docs/latest/develop/programmability/eval-intro) - Introduction to Redis Lua scripting\n* [HMGET command](https://redis.io/docs/latest/commands/hmget) - Get multiple hash fields\n* [HMSET command](https://redis.io/docs/latest/commands/hmset) - Set multiple hash fields\n* [PHP client](https://redis.io/docs/latest/develop/clients/php) - Redis PHP client documentation"}],"examples":[{"id":"the-lua-script-ex0","language":"lua","code":"local key = KEYS[1]\nlocal capacity = tonumber(ARGV[1])\nlocal refill_rate = tonumber(ARGV[2])\nlocal refill_interval = tonumber(ARGV[3])\nlocal now = tonumber(ARGV[4])\n\n-- Get current state or initialize\nlocal bucket = redis.call('HMGET', key, 'tokens', 'last_refill')\nlocal tokens = tonumber(bucket[1])\nlocal last_refill = tonumber(bucket[2])\n\n-- Initialize if this is the first request\nif tokens == nil then\n    tokens = capacity\n    last_refill = now\nend\n\n-- Calculate token refill\nlocal time_passed = now - last_refill\nlocal refills = math.floor(time_passed / refill_interval)\n\nif refills > 0 then\n    tokens = math.min(capacity, tokens + (refills * refill_rate))\n    last_refill = last_refill + (refills * refill_interval)\nend\n\n-- Try to consume a token\nlocal allowed = 0\nif tokens >= 1 then\n    tokens = tokens - 1\n    allowed = 1\nend\n\n-- Update state\nredis.call('HMSET', key, 'tokens', tokens, 'last_refill', last_refill)\n\n-- Return result: allowed (1 or 0) and remaining tokens\nreturn {allowed, tokens}","section_id":"the-lua-script"},{"id":"installation-ex0","language":"bash","code":"composer require predis/predis","section_id":"installation"},{"id":"using-the-php-class-ex0","language":"php","code":"<?php\n\nrequire __DIR__ . '/vendor/autoload.php';\n\nuse Predis\\Client;\n\n// Create a Redis connection\n$redis = new Client([\n    'scheme' => 'tcp',\n    'host'   => '127.0.0.1',\n    'port'   => 6379,\n]);\n\n// Create a rate limiter: 10 requests per second\n$limiter = new TokenBucket(\n    redis: $redis,\n    capacity: 10,        // Maximum burst size\n    refillRate: 1,       // Add 1 token per interval\n    refillInterval: 1.0  // Every 1 second\n);\n\n// Check if a request should be allowed\n$result = $limiter->allow('user:123');\n\nif ($result['allowed']) {\n    echo \"Request allowed. {$result['remaining']} tokens remaining.\\n\";\n    // Process the request\n} else {\n    echo \"Request denied. Rate limit exceeded.\\n\";\n    // Return 429 Too Many Requests\n}","section_id":"using-the-php-class"},{"id":"script-caching-with-evalsha-ex0","language":"php","code":"// The class handles script caching automatically.\n// First call loads the script, subsequent calls use EVALSHA.\n$result1 = $limiter->allow('user:123'); // Uses EVAL + caches\n$result2 = $limiter->allow('user:123'); // Uses EVALSHA (faster)","section_id":"script-caching-with-evalsha"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir rate-limiter-demo && cd rate-limiter-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/rate-limiter/php\ncurl -O $BASE/TokenBucket.php\ncurl -O $BASE/demo_server.php","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"# Install dependencies\ncomposer require predis/predis\n\n# Run the demo server\nphp -S localhost:8080 demo_server.php","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"bash","code":"REDIS_HOST=myhost REDIS_PORT=6380 php -S localhost:8080 demo_server.php","section_id":"start-the-demo-server"},{"id":"response-headers-ex0","language":"php","code":"$capacity = 10;\n$refillInterval = 1.0;\n\n$limiter = new TokenBucket(\n    redis: $redis,\n    capacity: $capacity,\n    refillRate: 1,\n    refillInterval: $refillInterval,\n);\n\n$result = $limiter->allow(\"user:{$userId}\");\n\n// Add standard rate limit headers\nheader('X-RateLimit-Limit: ' . $capacity);\nheader('X-RateLimit-Remaining: ' . (int) $result['remaining']);\nheader('X-RateLimit-Reset: ' . (int) (time() + $refillInterval));\n\nif (!$result['allowed']) {\n    header('Retry-After: ' . (int) $refillInterval);\n    http_response_code(429);\n    echo json_encode(['error' => 'Too Many Requests']);\n    exit;\n}","section_id":"response-headers"},{"id":"using-as-psr-15-middleware-ex0","language":"php","code":"use Psr\\Http\\Message\\ResponseInterface;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Psr\\Http\\Server\\MiddlewareInterface;\nuse Psr\\Http\\Server\\RequestHandlerInterface;\n\nclass RateLimitMiddleware implements MiddlewareInterface\n{\n    public function __construct(\n        private TokenBucket $limiter,\n        private \\Closure $keyFn,\n    ) {}\n\n    public function process(\n        ServerRequestInterface $request,\n        RequestHandlerInterface $handler,\n    ): ResponseInterface {\n        $key = ($this->keyFn)($request);\n        $result = $this->limiter->allow($key);\n\n        if (!$result['allowed']) {\n            $response = new \\Slim\\Psr7\\Response(429);\n            $response->getBody()->write(\n                json_encode(['error' => 'Rate limit exceeded'])\n            );\n            return $response->withHeader('Content-Type', 'application/json');\n        }\n\n        $response = $handler->handle($request);\n        return $response->withHeader(\n            'X-RateLimit-Remaining',\n            (string) (int) $result['remaining']\n        );\n    }\n}\n\n// Apply per-IP rate limiting\n$middleware = new RateLimitMiddleware(\n    $limiter,\n    fn(ServerRequestInterface $req) => 'ip:' . $req->getServerParams()['REMOTE_ADDR']\n);","section_id":"using-as-psr-15-middleware"},{"id":"error-handling-ex0","language":"php","code":"try {\n    $result = $limiter->allow('user:123');\n    // Handle result\n} catch (\\Predis\\Connection\\ConnectionException $e) {\n    error_log('Rate limiter error: ' . $e->getMessage());\n    // Fail open or closed depending on your policy\n}","section_id":"error-handling"},{"id":"fixed-window-counter-ex0","language":"php","code":"<?php\n\nuse Predis\\Client;\n\nconst FIXED_WINDOW_SCRIPT = <<<'LUA'\nlocal key    = KEYS[1]\nlocal limit  = tonumber(ARGV[1])\nlocal window = tonumber(ARGV[2])\n\nlocal count = redis.call('INCR', key)\nif count == 1 then\n    redis.call('EXPIRE', key, window)\nend\n\nlocal ttl = redis.call('PTTL', key)\n\nif count > limit then\n    return {0, ttl}\nend\nreturn {1, ttl}\nLUA;\n\n/**\n * @return array{allowed: bool, retry_after_ms: int}\n */\nfunction fixedWindowAllow(Client $redis, string $key, int $limit, int $windowSeconds): array\n{\n    [$allowed, $ttl] = $redis->eval(FIXED_WINDOW_SCRIPT, 1, $key, $limit, $windowSeconds);\n\n    return [\n        'allowed' => $allowed === 1,\n        'retry_after_ms' => $allowed === 1 ? 0 : (int) $ttl,\n    ];\n}","section_id":"fixed-window-counter"},{"id":"sliding-window-log-ex0","language":"php","code":"<?php\n\nuse Predis\\Client;\n\nconst SLIDING_WINDOW_LOG_SCRIPT = <<<'LUA'\nlocal key    = KEYS[1]\nlocal limit  = tonumber(ARGV[1])\nlocal window = tonumber(ARGV[2])\nlocal member = ARGV[3]\n\nlocal t      = redis.call('TIME')\nlocal now    = tonumber(t[1]) + tonumber(t[2]) / 1e6\nlocal cutoff = now - window\n\nredis.call('ZREMRANGEBYSCORE', key, '-inf', cutoff)\n\nlocal count = redis.call('ZCARD', key)\n\nif count < limit then\n    redis.call('ZADD', key, now, member)\n    redis.call('EXPIRE', key, window * 2)\n    return {1, 0}\nend\n\nlocal oldest = redis.call('ZRANGE', key, 0, 0, 'WITHSCORES')\nlocal retry_after_ms = 0\nif oldest[2] then\n    retry_after_ms = math.floor((tonumber(oldest[2]) + window - now) * 1000)\nend\n\nreturn {0, retry_after_ms}\nLUA;\n\nfunction slidingWindowLogAllow(Client $redis, string $key, int $limit, int $windowSeconds): array\n{\n    $member = bin2hex(random_bytes(16));\n\n    [$allowed, $retryAfterMs] = $redis->eval(\n        SLIDING_WINDOW_LOG_SCRIPT, 1, $key, $limit, $windowSeconds, $member\n    );\n\n    return ['allowed' => $allowed === 1, 'retry_after_ms' => (int) $retryAfterMs];\n}","section_id":"sliding-window-log"},{"id":"sliding-window-counter-ex0","language":"php","code":"<?php\n\nuse Predis\\Client;\n\nconst SLIDING_WINDOW_COUNTER_SCRIPT = <<<'LUA'\nlocal base   = KEYS[1]\nlocal limit  = tonumber(ARGV[1])\nlocal window = tonumber(ARGV[2])\n\nlocal t   = redis.call('TIME')\nlocal now = tonumber(t[1]) + tonumber(t[2]) / 1e6\n\nlocal window_num = math.floor(now / window)\nlocal elapsed     = (now % window) / window\n\nlocal curr_key = base .. ':' .. window_num\nlocal prev_key = base .. ':' .. (window_num - 1)\n\nlocal prev = tonumber(redis.call('GET', prev_key) or 0)\nlocal curr = tonumber(redis.call('GET', curr_key) or 0)\n\nlocal estimate = prev * (1 - elapsed) + curr\n\nif estimate >= limit then\n    return {0, 0}\nend\n\nlocal new_count = redis.call('INCR', curr_key)\nif new_count == 1 then\n    redis.call('EXPIRE', curr_key, window * 2)\nend\n\nreturn {1, 0}\nLUA;\n\nfunction slidingWindowCounterAllow(Client $redis, string $key, int $limit, int $windowSeconds): array\n{\n    [$allowed] = $redis->eval(\n        SLIDING_WINDOW_COUNTER_SCRIPT, 1, '{' . $key . '}', $limit, $windowSeconds\n    );\n\n    return ['allowed' => $allowed === 1];\n}","section_id":"sliding-window-counter"},{"id":"leaky-bucket-policing-ex0","language":"php","code":"<?php\n\nuse Predis\\Client;\n\nconst LEAKY_BUCKET_SCRIPT = <<<'LUA'\nlocal key       = KEYS[1]\nlocal capacity  = tonumber(ARGV[1])\nlocal leak_rate = tonumber(ARGV[2])\n\nlocal t   = redis.call('TIME')\nlocal now = tonumber(t[1]) + tonumber(t[2]) / 1e6\n\nlocal data      = redis.call('HGETALL', key)\nlocal level     = 0\nlocal last_leak = now\n\nif #data > 0 then\n    for i = 1, #data, 2 do\n        if data[i] == 'level' then\n            level = tonumber(data[i+1])\n        elseif data[i] == 'last_leak' then\n            last_leak = tonumber(data[i+1])\n        end\n    end\nend\n\nlocal elapsed = now - last_leak\nlevel = math.max(0, level - elapsed * leak_rate)\n\nif level + 1 > capacity then\n    return {0, math.floor((level + 1 - capacity) / leak_rate * 1000)}\nend\n\nlevel = level + 1\nlocal ttl = math.ceil(capacity / leak_rate) + 1\n\nredis.call('HSET', key, 'level', level, 'last_leak', now)\nredis.call('EXPIRE', key, ttl)\n\nreturn {1, 0}\nLUA;\n\nfunction leakyBucketAllow(Client $redis, string $key, int $capacity, float $leakRate): array\n{\n    [$allowed, $retryAfterMs] = $redis->eval(\n        LEAKY_BUCKET_SCRIPT, 1, $key, $capacity, $leakRate\n    );\n\n    return ['allowed' => $allowed === 1, 'retry_after_ms' => (int) $retryAfterMs];\n}","section_id":"leaky-bucket-policing"}]}
{"schema_version":2,"id":"develop/use-cases/rate-limiter/redis-py","title":"Token bucket rate limiter with Redis and redis-py","url":"https://redis.io/docs/latest/develop/use-cases/rate-limiter/redis-py/","summary":"Implement a token bucket rate limiter with Redis and redis-py","aliases":["/develop/use-cases/rate-limiter/demo_server.py","/develop/use-cases/rate-limiter/token_bucket.py","/develop/use-cases/rate-limiter/python"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-29T14:34:47+01:00","children":[],"page_type":"content","content_hash":"e153e9e3d17bc085038508406d2bcb01595072c9e76b80e2aa3c36a4ce853ecd","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a distributed token bucket rate limiter using Redis and Lua scripts for atomic operations."},{"id":"overview","title":"Overview","role":"overview","text":"Rate limiting is a critical technique for controlling the rate at which operations are performed. Common use cases include:\n\n* Limiting API requests per user or IP address\n* Preventing abuse and protecting against denial-of-service attacks\n* Ensuring fair resource allocation across multiple clients\n* Throttling background jobs or batch operations\n\nThe **token bucket algorithm** is a popular rate limiting approach that allows bursts of traffic while maintaining an average rate limit over time."},{"id":"how-it-works","title":"How it works","role":"content","text":"The token bucket algorithm works like a bucket that holds tokens:\n\n1. **Initialization**: The bucket starts with a maximum capacity of tokens\n2. **Refill**: Tokens are added to the bucket at a constant rate (for example, 1 token per second)\n3. **Consumption**: Each request consumes one token from the bucket\n4. **Decision**: If tokens are available, the request is allowed; otherwise, it's denied\n5. **Capacity limit**: The bucket never exceeds its maximum capacity\n\nThis approach allows for burst traffic (using accumulated tokens) while enforcing an average rate limit over time."},{"id":"why-use-redis","title":"Why use Redis?","role":"content","text":"Redis is ideal for distributed rate limiting because:\n\n* **Atomic operations**: Lua scripts execute atomically, preventing race conditions\n* **Shared state**: Multiple application servers can share the same rate limit counters\n* **High performance**: In-memory operations provide microsecond latency\n* **Automatic expiration**: Keys can be set to expire automatically (though not used in this implementation)"},{"id":"the-lua-script","title":"The Lua script","role":"content","text":"The core of this implementation is a Lua script that runs atomically on the Redis server. This ensures that checking and updating the token bucket happens in a single operation, preventing race conditions in distributed environments.\n\nHere's how the script works:\n\n[code example]"},{"id":"script-breakdown","title":"Script breakdown","role":"content","text":"1. **State retrieval**: Uses [`HMGET`](https://redis.io/docs/latest/commands/hmget) to fetch the current token count and last refill time from a hash\n2. **Initialization**: On first use, sets tokens to full capacity\n3. **Token refill calculation**: Computes how many tokens should be added based on elapsed time\n4. **Capacity enforcement**: Uses `math.min()` to ensure tokens never exceed capacity\n5. **Token consumption**: Decrements the token count if available\n6. **State update**: Uses [`HMSET`](https://redis.io/docs/latest/commands/hmset) to save the new state\n7. **Return value**: Returns both the decision (allowed/denied) and remaining tokens"},{"id":"why-atomicity-matters","title":"Why atomicity matters","role":"content","text":"Without atomic execution, race conditions could occur:\n\n* **Double spending**: Two requests could read the same token count and both succeed when only one should\n* **Lost updates**: Concurrent updates could overwrite each other's changes\n* **Inconsistent state**: Token count and refill time could become desynchronized\n\nUsing [`EVAL`](https://redis.io/docs/latest/commands/eval) or [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) ensures the entire operation executes atomically, making it safe for distributed systems."},{"id":"installation","title":"Installation","role":"setup","text":"Install the `redis` package:\n\n[code example]"},{"id":"using-the-python-module","title":"Using the Python module","role":"content","text":"The `TokenBucket` class provides a simple interface for rate limiting\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/rate-limiter/redis-py/token_bucket.py)):\n\n[code example]"},{"id":"configuration-parameters","title":"Configuration parameters","role":"configuration","text":"* **capacity**: Maximum number of tokens in the bucket (controls burst size)\n* **refill_rate**: Number of tokens added per refill interval\n* **refill_interval**: Time in seconds between refills\n\nFor example:\n* `capacity=10, refill_rate=1, refill_interval=1.0` allows 1 request per second with bursts up to 10\n* `capacity=100, refill_rate=10, refill_interval=1.0` allows 10 requests per second with bursts up to 100\n* `capacity=60, refill_rate=1, refill_interval=60.0` allows 1 request per minute with bursts up to 60"},{"id":"rate-limit-keys","title":"Rate limit keys","role":"content","text":"The `key` parameter identifies what you're rate limiting. Common patterns:\n\n* **Per user**: `user:{user_id}` - Limit each user independently\n* **Per IP address**: `ip:{ip_address}` - Limit by client IP\n* **Per API endpoint**: `api:{endpoint}:{user_id}` - Different limits per endpoint\n* **Global**: `global:api` - Single limit shared across all requests"},{"id":"script-caching-with-evalsha","title":"Script caching with EVALSHA","role":"content","text":"The Python module uses [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) for optimal\nperformance. `TokenBucket` computes the script's SHA1 digest when you create it,\nloads the script into Redis with `SCRIPT LOAD` on first use, and sends every\nlater request as `EVALSHA`. If the script has been evicted from the server's\ncache, it catches the resulting error, falls back to\n[`EVAL`](https://redis.io/docs/latest/commands/eval), and reloads the script.\n\n[code example]\n\nThe [alternative algorithm examples](#alternative-rate-limiting-algorithms) use\n`register_script()` instead, which wraps the same behavior in a `Script` object.\nThat object derives the digest from the script text rather than from per-call\nstate, so re-registering on each request adds no round trip: the digest is\nunchanged and `EVALSHA` still hits the cached script."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of two Python files. Download them from the [`redis-py` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/rate-limiter/redis-py) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"A demonstration web server is included to show the rate limiter in action\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/rate-limiter/redis-py/demo_server.py)):\n\n[code example]\n\nThe demo provides an interactive web interface where you can:\n\n* Submit requests and see them allowed or denied in real-time\n* View the current token count\n* Adjust rate limit parameters dynamically\n* Test different rate limiting scenarios\n\nThe demo assumes Redis is running on `localhost:6379` but you can easily change the host\nand port in the `demo_server.py` script. Visit `http://localhost:8080` in your browser to try it out."},{"id":"response-headers","title":"Response headers","role":"returns","text":"It's common to include rate limit information in HTTP response headers:\n\n[code example]"},{"id":"alternative-rate-limiting-algorithms","title":"Alternative rate limiting algorithms","role":"content","text":"The token bucket algorithm above handles most use cases but Redis supports\nother rate limiter patterns that might fit your requirements better. The table\nbelow lists four other algorithms alongside token bucket and summarizes\ntheir features:\n\n| Algorithm | Memory | Accuracy | Burst behavior | Best for |\n|---|---|---|---|---|\n| [Token bucket](#how-it-works) | 1 key (hash) | Exact | Controlled bursts | APIs with bursty traffic |\n| [Fixed window counter](#fixed-window-counter) | 1 key (string) | Approximate | 2x burst at boundaries | Simple API limits |\n| [Sliding window log](#sliding-window-log) | O(n) entries | Exact | No bursts | High-value APIs, audit trails |\n| [Sliding window counter](#sliding-window-counter) | 2 keys (string) | Near-exact | Smoothed boundaries | General-purpose APIs |\n| [Leaky bucket (policing)](#leaky-bucket-policing) | 1 key (hash) | Exact | No bursts | Strict no-burst enforcement |\n\nThe sections below give example implementations of these other algorithms.\nThe three time-based algorithms call `redis.call('TIME')` inside the Lua\nscript to derive the current timestamp from the Redis server clock. This\neliminates clock drift when the limiter runs across multiple application\nservers. The fixed window counter reads no clock: the key's TTL defines\nthe window."},{"id":"fixed-window-counter","title":"Fixed window counter","role":"content","text":"Counts requests within discrete, non-overlapping time intervals.\nSimplest algorithm — one key per window, one `EVAL` round trip.\n\n[code example]\n\n**Trade-off**: A client can make 2x requests by sending `limit` requests\nat the end of one window and `limit` requests at the start of the next."},{"id":"sliding-window-log","title":"Sliding window log","role":"content","text":"Records the exact timestamp of every request in a sorted set.\nProvides a true rolling window with no boundary bursts.\n\n[code example]\n\n**Trade-off**: Memory grows O(n) with request volume. Not ideal for\nhigh-volume, high-cardinality rate limiting."},{"id":"sliding-window-counter","title":"Sliding window counter","role":"content","text":"Blends two fixed-window counters using a weighted average to approximate\na true sliding window. Near-exact accuracy with the same low memory\nfootprint as a fixed window. The two keys use hash tags so they map\nto the same slot in Redis Cluster.\n\n[code example]\n\n**Trade-off**: The weighted estimate may let slightly more or fewer\nrequests through than the exact limit. Negligible for most apps."},{"id":"leaky-bucket-policing","title":"Leaky bucket (policing)","role":"content","text":"A virtual bucket fills with incoming requests and drains at a fixed rate.\nIf the bucket is full, requests are rejected immediately. This is the\npolicing variant — requests are allowed or denied instantly with no delay.\n\n[code example]\n\n**Trade-off**: Overflow traffic is rejected immediately. Clients must\nhandle `429 Too Many Requests` and retry with backoff."},{"id":"learn-more","title":"Learn more","role":"related","text":"* [EVAL command](https://redis.io/docs/latest/commands/eval) - Execute Lua scripts\n* [EVALSHA command](https://redis.io/docs/latest/commands/evalsha) - Execute cached Lua scripts\n* [Lua scripting](https://redis.io/docs/latest/develop/programmability/eval-intro) - Introduction to Redis Lua scripting\n* [HMGET command](https://redis.io/docs/latest/commands/hmget) - Get multiple hash fields\n* [HMSET command](https://redis.io/docs/latest/commands/hmset) - Set multiple hash fields\n* [Transactions](https://redis.io/docs/latest/develop/using-commands/transactions) - Alternative to Lua scripts for atomicity\n* [redis-rate-limiting-python](https://github.com/YashwinReddy29/redis-rate-limiting-python) - community-maintained GitHub repo (contributed by Yashwin Reddy) with examples of the alternative rate limiting algorithms."}],"examples":[{"id":"the-lua-script-ex0","language":"lua","code":"local key = KEYS[1]\nlocal capacity = tonumber(ARGV[1])\nlocal refill_rate = tonumber(ARGV[2])\nlocal refill_interval = tonumber(ARGV[3])\nlocal now = tonumber(ARGV[4])\n\n-- Get current state or initialize\nlocal bucket = redis.call('HMGET', key, 'tokens', 'last_refill')\nlocal tokens = tonumber(bucket[1])\nlocal last_refill = tonumber(bucket[2])\n\n-- Initialize if this is the first request\nif tokens == nil then\n    tokens = capacity\n    last_refill = now\nend\n\n-- Calculate token refill\nlocal time_passed = now - last_refill\nlocal refills = math.floor(time_passed / refill_interval)\n\nif refills > 0 then\n    tokens = math.min(capacity, tokens + (refills * refill_rate))\n    last_refill = last_refill + (refills * refill_interval)\nend\n\n-- Try to consume a token\nlocal allowed = 0\nif tokens >= 1 then\n    tokens = tokens - 1\n    allowed = 1\nend\n\n-- Update state\nredis.call('HMSET', key, 'tokens', tokens, 'last_refill', last_refill)\n\n-- Return result: allowed (1 or 0) and remaining tokens\nreturn {allowed, tokens}","section_id":"the-lua-script"},{"id":"installation-ex0","language":"bash","code":"pip install redis","section_id":"installation"},{"id":"using-the-python-module-ex0","language":"python","code":"import redis\nfrom token_bucket import TokenBucket\n\n# Create a Redis connection\nr = redis.Redis(host='localhost', port=6379, decode_responses=True)\n\n# Create a rate limiter: 1 request per second\nlimiter = TokenBucket(\n    redis_client=r,\n    capacity=10,        # Maximum burst size\n    refill_rate=1,      # Add 1 token per interval\n    refill_interval=1.0 # Every 1 second\n)\n\n# Check if a request should be allowed\nallowed, remaining = limiter.allow('user:123')\n\nif allowed:\n    print(f\"Request allowed. {remaining} tokens remaining.\")\n    # Process the request\nelse:\n    print(\"Request denied. Rate limit exceeded.\")\n    # Return 429 Too Many Requests","section_id":"using-the-python-module"},{"id":"script-caching-with-evalsha-ex0","language":"python","code":"limiter = TokenBucket(capacity=10, refill_rate=1, refill_interval=1.0)\n\nlimiter.allow('user:123')  # SCRIPT LOAD, then EVALSHA\nlimiter.allow('user:123')  # EVALSHA against the cached script","section_id":"script-caching-with-evalsha"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir rate-limiter-demo && cd rate-limiter-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/rate-limiter/redis-py\ncurl -O $BASE/token_bucket.py\ncurl -O $BASE/demo_server.py","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"# Install dependencies\npip install redis\n\n# Run the demo server\npython demo_server.py","section_id":"start-the-demo-server"},{"id":"response-headers-ex0","language":"python","code":"allowed, remaining = limiter.allow(f'user:{user_id}')\n\n# Add standard rate limit headers\nresponse.headers['X-RateLimit-Limit'] = str(limiter.capacity)\nresponse.headers['X-RateLimit-Remaining'] = str(int(remaining))\nresponse.headers['X-RateLimit-Reset'] = str(int(time.time() + limiter.refill_interval))\n\nif not allowed:\n    response.status_code = 429  # Too Many Requests\n    response.headers['Retry-After'] = str(int(limiter.refill_interval))","section_id":"response-headers"},{"id":"fixed-window-counter-ex0","language":"python","code":"import redis\n\nSCRIPT = \"\"\"\nlocal key    = KEYS[1]\nlocal limit  = tonumber(ARGV[1])\nlocal window = tonumber(ARGV[2])\n\nlocal count = redis.call('INCR', key)\nif count == 1 then\n    redis.call('EXPIRE', key, window)\nend\n\nlocal ttl = redis.call('PTTL', key)\n\nif count > limit then\n    return {0, ttl}\nend\nreturn {1, ttl}\n\"\"\"\n\n\ndef is_allowed(client: redis.Redis, key: str, limit: int, window_seconds: int) -> dict:\n    script = client.register_script(SCRIPT)\n    allowed, ttl = script(keys=[key], args=[limit, window_seconds], client=client)\n    return {\"allowed\": bool(allowed), \"retry_after_ms\": ttl if not allowed else 0}","section_id":"fixed-window-counter"},{"id":"sliding-window-log-ex0","language":"python","code":"import uuid\nimport redis\n\nSCRIPT = \"\"\"\nlocal key    = KEYS[1]\nlocal limit  = tonumber(ARGV[1])\nlocal window = tonumber(ARGV[2])\nlocal member = ARGV[3]\n\nlocal t      = redis.call('TIME')\nlocal now    = tonumber(t[1]) + tonumber(t[2]) / 1e6\nlocal cutoff = now - window\n\nredis.call('ZREMRANGEBYSCORE', key, '-inf', cutoff)\n\nlocal count = redis.call('ZCARD', key)\n\nif count < limit then\n    redis.call('ZADD', key, now, member)\n    redis.call('EXPIRE', key, window * 2)\n    return {1, 0}\nend\n\nlocal oldest = redis.call('ZRANGE', key, 0, 0, 'WITHSCORES')\nlocal retry_after_ms = 0\nif oldest[2] then\n    retry_after_ms = math.floor((tonumber(oldest[2]) + window - now) * 1000)\nend\n\nreturn {0, retry_after_ms}\n\"\"\"\n\n\ndef is_allowed(client: redis.Redis, key: str, limit: int, window_seconds: int) -> dict:\n    script = client.register_script(SCRIPT)\n    member = str(uuid.uuid4())\n    allowed, retry_after_ms = script(\n        keys=[key], args=[limit, window_seconds, member], client=client\n    )\n    return {\"allowed\": bool(allowed), \"retry_after_ms\": int(retry_after_ms)}","section_id":"sliding-window-log"},{"id":"sliding-window-counter-ex0","language":"python","code":"import redis\n\nSCRIPT = \"\"\"\nlocal base   = KEYS[1]\nlocal limit  = tonumber(ARGV[1])\nlocal window = tonumber(ARGV[2])\n\nlocal t   = redis.call('TIME')\nlocal now = tonumber(t[1]) + tonumber(t[2]) / 1e6\n\nlocal window_num = math.floor(now / window)\nlocal elapsed     = (now % window) / window\n\nlocal curr_key = base .. ':' .. window_num\nlocal prev_key = base .. ':' .. (window_num - 1)\n\nlocal prev = tonumber(redis.call('GET', prev_key) or 0)\nlocal curr = tonumber(redis.call('GET', curr_key) or 0)\n\nlocal estimate = prev * (1 - elapsed) + curr\n\nif estimate >= limit then\n    return {0, 0}\nend\n\nlocal new_count = redis.call('INCR', curr_key)\nif new_count == 1 then\n    redis.call('EXPIRE', curr_key, window * 2)\nend\n\nreturn {1, 0}\n\"\"\"\n\n\ndef is_allowed(client: redis.Redis, key: str, limit: int, window_seconds: int) -> dict:\n    script = client.register_script(SCRIPT)\n    allowed, _ = script(\n        keys=[\"{\" + key + \"}\"],\n        args=[limit, window_seconds],\n        client=client,\n    )\n    return {\"allowed\": bool(allowed)}","section_id":"sliding-window-counter"},{"id":"leaky-bucket-policing-ex0","language":"python","code":"import redis\n\nSCRIPT = \"\"\"\nlocal key       = KEYS[1]\nlocal capacity  = tonumber(ARGV[1])\nlocal leak_rate = tonumber(ARGV[2])\n\nlocal t   = redis.call('TIME')\nlocal now = tonumber(t[1]) + tonumber(t[2]) / 1e6\n\nlocal data      = redis.call('HGETALL', key)\nlocal level     = 0\nlocal last_leak = now\n\nif #data > 0 then\n    for i = 1, #data, 2 do\n        if data[i] == 'level' then\n            level = tonumber(data[i+1])\n        elseif data[i] == 'last_leak' then\n            last_leak = tonumber(data[i+1])\n        end\n    end\nend\n\nlocal elapsed = now - last_leak\nlevel = math.max(0, level - elapsed * leak_rate)\n\nif level + 1 > capacity then\n    return {0, math.floor((level + 1 - capacity) / leak_rate * 1000)}\nend\n\nlevel = level + 1\nlocal ttl = math.ceil(capacity / leak_rate) + 1\n\nredis.call('HSET', key, 'level', level, 'last_leak', now)\nredis.call('EXPIRE', key, ttl)\n\nreturn {1, 0}\n\"\"\"\n\n\ndef is_allowed(client: redis.Redis, key: str, capacity: int, leak_rate: float) -> dict:\n    script = client.register_script(SCRIPT)\n    allowed, retry_after_ms = script(\n        keys=[key], args=[capacity, leak_rate], client=client\n    )\n    return {\"allowed\": bool(allowed), \"retry_after_ms\": int(retry_after_ms)}","section_id":"leaky-bucket-policing"}]}
{"schema_version":2,"id":"develop/use-cases/rate-limiter/ruby","title":"Token bucket rate limiter with Redis and Ruby","url":"https://redis.io/docs/latest/develop/use-cases/rate-limiter/ruby/","summary":"Implement a token bucket rate limiter using Redis and Lua scripts in Ruby","aliases":["/develop/use-cases/rate-limiter/redis-rb"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-29T14:34:47+01:00","children":[],"page_type":"content","content_hash":"de6687b402fd51002d284911af9564063efe37ec3ea323f162210bea96010a69","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a distributed token bucket rate limiter using Redis and Lua scripts in Ruby with the [`redis-rb`](https://redis.io/docs/latest/develop/clients/ruby) client library."},{"id":"overview","title":"Overview","role":"overview","text":"Rate limiting is a critical technique for controlling the rate at which operations are performed. Common use cases include:\n\n* Limiting API requests per user or IP address\n* Preventing abuse and protecting against denial-of-service attacks\n* Ensuring fair resource allocation across multiple clients\n* Throttling background jobs or batch operations\n\nThe **token bucket algorithm** is a popular rate limiting approach that allows bursts of traffic while maintaining an average rate limit over time. This guide covers the Ruby implementation using the [`redis-rb`](https://redis.io/docs/latest/develop/clients/ruby) gem."},{"id":"how-it-works","title":"How it works","role":"content","text":"The token bucket algorithm works like a bucket that holds tokens:\n\n1. **Initialization**: The bucket starts with a maximum capacity of tokens\n2. **Refill**: Tokens are added to the bucket at a constant rate (for example, 1 token per second)\n3. **Consumption**: Each request consumes one token from the bucket\n4. **Decision**: If tokens are available, the request is allowed; otherwise, it's denied\n5. **Capacity limit**: The bucket never exceeds its maximum capacity\n\nThis approach allows for burst traffic (using accumulated tokens) while enforcing an average rate limit over time."},{"id":"why-use-redis","title":"Why use Redis?","role":"content","text":"Redis is ideal for distributed rate limiting because:\n\n* **Atomic operations**: Lua scripts execute atomically, preventing race conditions\n* **Shared state**: Multiple application servers can share the same rate limit counters\n* **High performance**: In-memory operations provide microsecond latency\n* **Automatic expiration**: Keys can be set to expire automatically (though not used in this implementation)"},{"id":"the-lua-script","title":"The Lua script","role":"content","text":"The core of this implementation is a Lua script that runs atomically on the Redis server. This ensures that checking and updating the token bucket happens in a single operation, preventing race conditions in distributed environments.\n\nHere's how the script works:\n\n[code example]"},{"id":"script-breakdown","title":"Script breakdown","role":"content","text":"1. **State retrieval**: Uses [`HMGET`](https://redis.io/docs/latest/commands/hmget) to fetch the current token count and last refill time from a hash\n2. **Initialization**: On first use, sets tokens to full capacity\n3. **Token refill calculation**: Computes how many tokens should be added based on elapsed time\n4. **Capacity enforcement**: Uses `math.min()` to ensure tokens never exceed capacity\n5. **Token consumption**: Decrements the token count if available\n6. **State update**: Uses [`HMSET`](https://redis.io/docs/latest/commands/hmset) to save the new state\n7. **Return value**: Returns both the decision (allowed/denied) and remaining tokens"},{"id":"why-atomicity-matters","title":"Why atomicity matters","role":"content","text":"Without atomic execution, race conditions could occur:\n\n* **Double spending**: Two requests could read the same token count and both succeed when only one should\n* **Lost updates**: Concurrent updates could overwrite each other's changes\n* **Inconsistent state**: Token count and refill time could become desynchronized\n\nUsing [`EVAL`](https://redis.io/docs/latest/commands/eval) or [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) ensures the entire operation executes atomically, making it safe for distributed systems."},{"id":"installation","title":"Installation","role":"setup","text":"Install the `redis` gem:\n\n[code example]\n\nOr add it to your `Gemfile`:\n\n[code example]\n\nThen run:\n\n[code example]"},{"id":"using-the-ruby-module","title":"Using the Ruby module","role":"content","text":"The `TokenBucket` class provides a simple interface for rate limiting\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/rate-limiter/ruby/token_bucket.rb)):\n\n[code example]\n\nRuby's keyword arguments make the constructor parameters self-documenting, and the `allow` method returns a Hash with `:allowed` and `:remaining` keys."},{"id":"configuration-parameters","title":"Configuration parameters","role":"configuration","text":"* **capacity**: Maximum number of tokens in the bucket (controls burst size)\n* **refill_rate**: Number of tokens added per refill interval\n* **refill_interval**: Time in seconds between refills\n\nFor example:\n* `capacity: 10, refill_rate: 1, refill_interval: 1.0` allows 1 request per second with bursts up to 10\n* `capacity: 100, refill_rate: 10, refill_interval: 1.0` allows 10 requests per second with bursts up to 100\n* `capacity: 60, refill_rate: 1, refill_interval: 60.0` allows 1 request per minute with bursts up to 60"},{"id":"rate-limit-keys","title":"Rate limit keys","role":"content","text":"The `key` parameter identifies what you're rate limiting. Common patterns:\n\n* **Per user**: `user:{user_id}` - Limit each user independently\n* **Per IP address**: `ip:{ip_address}` - Limit by client IP\n* **Per API endpoint**: `api:{endpoint}:{user_id}` - Different limits per endpoint\n* **Global**: `global:api` - Single limit shared across all requests"},{"id":"script-caching-with-evalsha","title":"Script caching with EVALSHA","role":"content","text":"The Ruby implementation uses [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) for optimal performance. On first use, the Lua script is loaded into Redis with `SCRIPT LOAD`, and subsequent calls use the cached SHA1 hash. If the script is evicted from the cache, the module automatically falls back to [`EVAL`](https://redis.io/docs/latest/commands/eval) and reloads the script.\n\n[code example]"},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of two Ruby files. Download them from the [`ruby` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/rate-limiter/ruby) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"A demonstration web server is included to show the rate limiter in action\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/rate-limiter/ruby/demo_server.rb)):\n\n[code example]\n\nThe demo provides an interactive web interface where you can:\n\n* Submit requests and see them allowed or denied in real-time\n* View the current token count\n* Adjust rate limit parameters dynamically\n* Test different rate limiting scenarios\n\nThe demo assumes Redis is running on `localhost:6379` but you can specify a different host and port using the `--redis-host HOST` and `--redis-port PORT` command-line arguments. Visit `http://localhost:8080` in your browser to try it out."},{"id":"response-headers","title":"Response headers","role":"returns","text":"It's common to include rate limit information in HTTP response headers:\n\n[code example]"},{"id":"customization","title":"Customization","role":"content","text":""},{"id":"using-with-rack-middleware","title":"Using with Rack middleware","role":"content","text":"You can wrap the rate limiter as Rack middleware for easy integration with any Rack-based framework (Rails, Sinatra, Hanami):\n\n[code example]"},{"id":"error-handling","title":"Error handling","role":"errors","text":"The `allow` method may raise an error if the Redis connection is lost. Wrap calls in a `begin/rescue` block for production use:\n\n[code example]"},{"id":"alternative-rate-limiting-algorithms","title":"Alternative rate limiting algorithms","role":"content","text":"The token bucket algorithm above handles most use cases but Redis supports\nother rate limiter patterns that might fit your requirements better. The table\nbelow lists four other algorithms alongside token bucket and summarizes\ntheir features:\n\n| Algorithm | Memory | Accuracy | Burst behavior | Best for |\n|---|---|---|---|---|\n| [Token bucket](#how-it-works) | 1 key (hash) | Exact | Controlled bursts | APIs with bursty traffic |\n| [Fixed window counter](#fixed-window-counter) | 1 key (string) | Approximate | 2x burst at boundaries | Simple API limits |\n| [Sliding window log](#sliding-window-log) | O(n) entries | Exact | No bursts | High-value APIs, audit trails |\n| [Sliding window counter](#sliding-window-counter) | 2 keys (string) | Near-exact | Smoothed boundaries | General-purpose APIs |\n| [Leaky bucket (policing)](#leaky-bucket-policing) | 1 key (hash) | Exact | No bursts | Strict no-burst enforcement |\n\nThe sections below give example implementations of these other algorithms.\nThe three time-based algorithms call `redis.call('TIME')` inside the Lua\nscript to derive the current timestamp from the Redis server clock. This\neliminates clock drift when the limiter runs across multiple application\nservers. The fixed window counter reads no clock: the key's TTL defines\nthe window."},{"id":"fixed-window-counter","title":"Fixed window counter","role":"content","text":"Counts requests within discrete, non-overlapping time intervals.\nSimplest algorithm — one key per window, one `EVAL` round trip.\n\n[code example]\n\n**Trade-off**: A client can make 2x requests by sending `limit` requests\nat the end of one window and `limit` requests at the start of the next."},{"id":"sliding-window-log","title":"Sliding window log","role":"content","text":"Records the exact timestamp of every request in a sorted set.\nProvides a true rolling window with no boundary bursts.\n\n[code example]\n\n**Trade-off**: Memory grows O(n) with request volume. Not ideal for\nhigh-volume, high-cardinality rate limiting."},{"id":"sliding-window-counter","title":"Sliding window counter","role":"content","text":"Blends two fixed-window counters using a weighted average to approximate\na true sliding window. Near-exact accuracy with the same low memory\nfootprint as a fixed window. The two keys use hash tags so they map\nto the same slot in Redis Cluster.\n\n[code example]\n\n**Trade-off**: The weighted estimate may let slightly more or fewer\nrequests through than the exact limit. Negligible for most apps."},{"id":"leaky-bucket-policing","title":"Leaky bucket (policing)","role":"content","text":"A virtual bucket fills with incoming requests and drains at a fixed rate.\nIf the bucket is full, requests are rejected immediately. This is the\npolicing variant — requests are allowed or denied instantly with no delay.\n\n[code example]\n\n**Trade-off**: Overflow traffic is rejected immediately. Clients must\nhandle `429 Too Many Requests` and retry with backoff."},{"id":"learn-more","title":"Learn more","role":"related","text":"* [EVAL command](https://redis.io/docs/latest/commands/eval) - Execute Lua scripts\n* [EVALSHA command](https://redis.io/docs/latest/commands/evalsha) - Execute cached Lua scripts\n* [Lua scripting](https://redis.io/docs/latest/develop/programmability/eval-intro) - Introduction to Redis Lua scripting\n* [HMGET command](https://redis.io/docs/latest/commands/hmget) - Get multiple hash fields\n* [HMSET command](https://redis.io/docs/latest/commands/hmset) - Set multiple hash fields\n* [Ruby client](https://redis.io/docs/latest/develop/clients/ruby) - Redis Ruby client documentation"}],"examples":[{"id":"the-lua-script-ex0","language":"lua","code":"local key = KEYS[1]\nlocal capacity = tonumber(ARGV[1])\nlocal refill_rate = tonumber(ARGV[2])\nlocal refill_interval = tonumber(ARGV[3])\nlocal now = tonumber(ARGV[4])\n\n-- Get current state or initialize\nlocal bucket = redis.call('HMGET', key, 'tokens', 'last_refill')\nlocal tokens = tonumber(bucket[1])\nlocal last_refill = tonumber(bucket[2])\n\n-- Initialize if this is the first request\nif tokens == nil then\n    tokens = capacity\n    last_refill = now\nend\n\n-- Calculate token refill\nlocal time_passed = now - last_refill\nlocal refills = math.floor(time_passed / refill_interval)\n\nif refills > 0 then\n    tokens = math.min(capacity, tokens + (refills * refill_rate))\n    last_refill = last_refill + (refills * refill_interval)\nend\n\n-- Try to consume a token\nlocal allowed = 0\nif tokens >= 1 then\n    tokens = tokens - 1\n    allowed = 1\nend\n\n-- Update state\nredis.call('HMSET', key, 'tokens', tokens, 'last_refill', last_refill)\n\n-- Return result: allowed (1 or 0) and remaining tokens\nreturn {allowed, tokens}","section_id":"the-lua-script"},{"id":"installation-ex0","language":"bash","code":"gem install redis","section_id":"installation"},{"id":"installation-ex1","language":"ruby","code":"gem 'redis', '~> 5.0'","section_id":"installation"},{"id":"installation-ex2","language":"bash","code":"bundle install","section_id":"installation"},{"id":"using-the-ruby-module-ex0","language":"ruby","code":"require 'redis'\nrequire_relative 'token_bucket'\n\n# Create a Redis connection\nredis = Redis.new(host: 'localhost', port: 6379)\n\n# Create a rate limiter: 10 requests per second\nlimiter = TokenBucket.new(\n  redis: redis,\n  capacity: 10,          # Maximum burst size\n  refill_rate: 1,        # Add 1 token per interval\n  refill_interval: 1.0   # Every 1 second\n)\n\n# Check if a request should be allowed\nresult = limiter.allow('user:123')\n\nif result[:allowed]\n  puts \"Request allowed. #{result[:remaining]} tokens remaining.\"\n  # Process the request\nelse\n  puts 'Request denied. Rate limit exceeded.'\n  # Return 429 Too Many Requests\nend","section_id":"using-the-ruby-module"},{"id":"script-caching-with-evalsha-ex0","language":"ruby","code":"# The module handles script caching automatically.\n# First call loads the script, subsequent calls use EVALSHA.\nresult1 = limiter.allow('user:123') # Uses EVAL + caches\nresult2 = limiter.allow('user:123') # Uses EVALSHA (faster)","section_id":"script-caching-with-evalsha"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir rate-limiter-demo && cd rate-limiter-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/rate-limiter/ruby\ncurl -O $BASE/token_bucket.rb\ncurl -O $BASE/demo_server.rb","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"# Install dependencies\ngem install redis webrick\n\n# Run the demo server\nruby demo_server.rb","section_id":"start-the-demo-server"},{"id":"response-headers-ex0","language":"ruby","code":"result = limiter.allow(\"user:#{user_id}\")\n\n# Add standard rate limit headers\nresponse['X-RateLimit-Limit'] = limiter.capacity.to_s\nresponse['X-RateLimit-Remaining'] = result[:remaining].to_i.to_s\nresponse['X-RateLimit-Reset'] = (Time.now.to_i + limiter.refill_interval).to_s\n\nunless result[:allowed]\n  response.status = 429 # Too Many Requests\n  response['Retry-After'] = limiter.refill_interval.ceil.to_s\nend","section_id":"response-headers"},{"id":"using-with-rack-middleware-ex0","language":"ruby","code":"class RateLimitMiddleware\n  def initialize(app, limiter:, key_proc:)\n    @app = app\n    @limiter = limiter\n    @key_proc = key_proc\n  end\n\n  def call(env)\n    key = @key_proc.call(env)\n    result = @limiter.allow(key)\n\n    if result[:allowed]\n      status, headers, body = @app.call(env)\n      headers['X-RateLimit-Remaining'] = result[:remaining].to_i.to_s\n      [status, headers, body]\n    else\n      [429, { 'Content-Type' => 'application/json', 'Retry-After' => @limiter.refill_interval.ceil.to_s },\n       ['{\"error\":\"Rate limit exceeded\"}']]\n    end\n  end\nend\n\n# Apply per-IP rate limiting\nuse RateLimitMiddleware, limiter: limiter, key_proc: ->(env) { \"ip:#{env['REMOTE_ADDR']}\" }","section_id":"using-with-rack-middleware"},{"id":"error-handling-ex0","language":"ruby","code":"begin\n  result = limiter.allow('user:123')\n  # Handle result\nrescue Redis::BaseError => e\n  puts \"Rate limiter error: #{e.message}\"\n  # Fail open or closed depending on your policy\nend","section_id":"error-handling"},{"id":"fixed-window-counter-ex0","language":"ruby","code":"require \"redis\"\n\nFIXED_WINDOW_SCRIPT = <<~LUA\n  local key    = KEYS[1]\n  local limit  = tonumber(ARGV[1])\n  local window = tonumber(ARGV[2])\n\n  local count = redis.call('INCR', key)\n  if count == 1 then\n      redis.call('EXPIRE', key, window)\n  end\n\n  local ttl = redis.call('PTTL', key)\n\n  if count > limit then\n      return {0, ttl}\n  end\n  return {1, ttl}\nLUA\n\n# Returns a hash with :allowed and :retry_after_ms keys.\ndef fixed_window_allow(redis, key, limit, window_seconds)\n  allowed, ttl = redis.eval(FIXED_WINDOW_SCRIPT,\n                            keys: [key], argv: [limit, window_seconds])\n\n  { allowed: allowed == 1, retry_after_ms: allowed == 1 ? 0 : ttl }\nend","section_id":"fixed-window-counter"},{"id":"sliding-window-log-ex0","language":"ruby","code":"require \"redis\"\nrequire \"securerandom\"\n\nSLIDING_WINDOW_LOG_SCRIPT = <<~LUA\n  local key    = KEYS[1]\n  local limit  = tonumber(ARGV[1])\n  local window = tonumber(ARGV[2])\n  local member = ARGV[3]\n\n  local t      = redis.call('TIME')\n  local now    = tonumber(t[1]) + tonumber(t[2]) / 1e6\n  local cutoff = now - window\n\n  redis.call('ZREMRANGEBYSCORE', key, '-inf', cutoff)\n\n  local count = redis.call('ZCARD', key)\n\n  if count < limit then\n      redis.call('ZADD', key, now, member)\n      redis.call('EXPIRE', key, window * 2)\n      return {1, 0}\n  end\n\n  local oldest = redis.call('ZRANGE', key, 0, 0, 'WITHSCORES')\n  local retry_after_ms = 0\n  if oldest[2] then\n      retry_after_ms = math.floor((tonumber(oldest[2]) + window - now) * 1000)\n  end\n\n  return {0, retry_after_ms}\nLUA\n\ndef sliding_window_log_allow(redis, key, limit, window_seconds)\n  allowed, retry_after_ms = redis.eval(SLIDING_WINDOW_LOG_SCRIPT,\n                                       keys: [key],\n                                       argv: [limit, window_seconds, SecureRandom.uuid])\n\n  { allowed: allowed == 1, retry_after_ms: retry_after_ms }\nend","section_id":"sliding-window-log"},{"id":"sliding-window-counter-ex0","language":"ruby","code":"require \"redis\"\n\nSLIDING_WINDOW_COUNTER_SCRIPT = <<~LUA\n  local base   = KEYS[1]\n  local limit  = tonumber(ARGV[1])\n  local window = tonumber(ARGV[2])\n\n  local t   = redis.call('TIME')\n  local now = tonumber(t[1]) + tonumber(t[2]) / 1e6\n\n  local window_num = math.floor(now / window)\n  local elapsed     = (now % window) / window\n\n  local curr_key = base .. ':' .. window_num\n  local prev_key = base .. ':' .. (window_num - 1)\n\n  local prev = tonumber(redis.call('GET', prev_key) or 0)\n  local curr = tonumber(redis.call('GET', curr_key) or 0)\n\n  local estimate = prev * (1 - elapsed) + curr\n\n  if estimate >= limit then\n      return {0, 0}\n  end\n\n  local new_count = redis.call('INCR', curr_key)\n  if new_count == 1 then\n      redis.call('EXPIRE', curr_key, window * 2)\n  end\n\n  return {1, 0}\nLUA\n\ndef sliding_window_counter_allow(redis, key, limit, window_seconds)\n  allowed, = redis.eval(SLIDING_WINDOW_COUNTER_SCRIPT,\n                        keys: [\"{#{key}}\"], argv: [limit, window_seconds])\n\n  { allowed: allowed == 1 }\nend","section_id":"sliding-window-counter"},{"id":"leaky-bucket-policing-ex0","language":"ruby","code":"require \"redis\"\n\nLEAKY_BUCKET_SCRIPT = <<~LUA\n  local key       = KEYS[1]\n  local capacity  = tonumber(ARGV[1])\n  local leak_rate = tonumber(ARGV[2])\n\n  local t   = redis.call('TIME')\n  local now = tonumber(t[1]) + tonumber(t[2]) / 1e6\n\n  local data      = redis.call('HGETALL', key)\n  local level     = 0\n  local last_leak = now\n\n  if #data > 0 then\n      for i = 1, #data, 2 do\n          if data[i] == 'level' then\n              level = tonumber(data[i+1])\n          elseif data[i] == 'last_leak' then\n              last_leak = tonumber(data[i+1])\n          end\n      end\n  end\n\n  local elapsed = now - last_leak\n  level = math.max(0, level - elapsed * leak_rate)\n\n  if level + 1 > capacity then\n      return {0, math.floor((level + 1 - capacity) / leak_rate * 1000)}\n  end\n\n  level = level + 1\n  local ttl = math.ceil(capacity / leak_rate) + 1\n\n  redis.call('HSET', key, 'level', level, 'last_leak', now)\n  redis.call('EXPIRE', key, ttl)\n\n  return {1, 0}\nLUA\n\ndef leaky_bucket_allow(redis, key, capacity, leak_rate)\n  allowed, retry_after_ms = redis.eval(LEAKY_BUCKET_SCRIPT,\n                                       keys: [key], argv: [capacity, leak_rate])\n\n  { allowed: allowed == 1, retry_after_ms: retry_after_ms }\nend","section_id":"leaky-bucket-policing"}]}
{"schema_version":2,"id":"develop/use-cases/rate-limiter/rust","title":"Token bucket rate limiter with Redis and Rust","url":"https://redis.io/docs/latest/develop/use-cases/rate-limiter/rust/","summary":"Implement a token bucket rate limiter using Redis and Lua scripts in Rust","aliases":["/develop/use-cases/rate-limiter/redis-rs"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-29T14:34:47+01:00","children":[],"page_type":"content","content_hash":"529e1e325d96d1545d81f3b9bbc3d92c0be14febd88b848c7e96268cc675cf8d","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a distributed token bucket rate limiter using Redis and Lua scripts in Rust with the [`redis-rs`](https://redis.io/docs/latest/develop/clients/rust) client library."},{"id":"overview","title":"Overview","role":"overview","text":"Rate limiting is a critical technique for controlling the rate at which operations are performed. Common use cases include:\n\n* Limiting API requests per user or IP address\n* Preventing abuse and protecting against denial-of-service attacks\n* Ensuring fair resource allocation across multiple clients\n* Throttling background jobs or batch operations\n\nThe **token bucket algorithm** is a popular rate limiting approach that allows bursts of traffic while maintaining an average rate limit over time. This guide covers the Rust implementation using the [`redis-rs`](https://redis.io/docs/latest/develop/clients/rust) client library, taking advantage of Rust's type safety, ownership model, and zero-cost abstractions."},{"id":"how-it-works","title":"How it works","role":"content","text":"The token bucket algorithm works like a bucket that holds tokens:\n\n1. **Initialization**: The bucket starts with a maximum capacity of tokens\n2. **Refill**: Tokens are added to the bucket at a constant rate (for example, 1 token per second)\n3. **Consumption**: Each request consumes one token from the bucket\n4. **Decision**: If tokens are available, the request is allowed; otherwise, it's denied\n5. **Capacity limit**: The bucket never exceeds its maximum capacity\n\nThis approach allows for burst traffic (using accumulated tokens) while enforcing an average rate limit over time."},{"id":"why-use-redis","title":"Why use Redis?","role":"content","text":"Redis is ideal for distributed rate limiting because:\n\n* **Atomic operations**: Lua scripts execute atomically, preventing race conditions\n* **Shared state**: Multiple application servers can share the same rate limit counters\n* **High performance**: In-memory operations provide microsecond latency\n* **Automatic expiration**: Keys can be set to expire automatically (though not used in this implementation)"},{"id":"the-lua-script","title":"The Lua script","role":"content","text":"The core of this implementation is a Lua script that runs atomically on the Redis server. This ensures that checking and updating the token bucket happens in a single operation, preventing race conditions in distributed environments.\n\nHere's how the script works:\n\n[code example]"},{"id":"script-breakdown","title":"Script breakdown","role":"content","text":"1. **State retrieval**: Uses [`HMGET`](https://redis.io/docs/latest/commands/hmget) to fetch the current token count and last refill time from a hash\n2. **Initialization**: On first use, sets tokens to full capacity\n3. **Token refill calculation**: Computes how many tokens should be added based on elapsed time\n4. **Capacity enforcement**: Uses `math.min()` to ensure tokens never exceed capacity\n5. **Token consumption**: Decrements the token count if available\n6. **State update**: Uses [`HMSET`](https://redis.io/docs/latest/commands/hmset) to save the new state\n7. **Return value**: Returns both the decision (allowed/denied) and remaining tokens"},{"id":"why-atomicity-matters","title":"Why atomicity matters","role":"content","text":"Without atomic execution, race conditions could occur:\n\n* **Double spending**: Two requests could read the same token count and both succeed when only one should\n* **Lost updates**: Concurrent updates could overwrite each other's changes\n* **Inconsistent state**: Token count and refill time could become desynchronized\n\nUsing [`EVAL`](https://redis.io/docs/latest/commands/eval) or [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) ensures the entire operation executes atomically, making it safe for distributed systems."},{"id":"installation","title":"Installation","role":"setup","text":"Add the `redis` crate to your `Cargo.toml`:\n\n[code example]\n\nFor async support, enable the `tokio-comp` or `async-std-comp` feature:\n\n[code example]"},{"id":"using-the-rust-module","title":"Using the Rust module","role":"content","text":"The `TokenBucket` struct provides a type-safe interface for rate limiting\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/rate-limiter/rust/token_bucket.rs)):\n\n[code example]\n\nRust's `Result` type provides compile-time error handling, ensuring all Redis errors are handled explicitly. The `TokenBucket` struct owns its configuration, leveraging Rust's ownership model to prevent data races."},{"id":"configuration-parameters","title":"Configuration parameters","role":"configuration","text":"* **capacity**: Maximum number of tokens in the bucket (controls burst size)\n* **refill_rate**: Number of tokens added per refill interval (as `f64`)\n* **refill_interval**: Time in seconds between refills (as `f64`)\n\nFor example:\n* `capacity: 10, refill_rate: 1.0, refill_interval: 1.0` allows 1 request per second with bursts up to 10\n* `capacity: 100, refill_rate: 10.0, refill_interval: 1.0` allows 10 requests per second with bursts up to 100\n* `capacity: 60, refill_rate: 1.0, refill_interval: 60.0` allows 1 request per minute with bursts up to 60"},{"id":"rate-limit-keys","title":"Rate limit keys","role":"content","text":"The `key` parameter identifies what you're rate limiting. Common patterns:\n\n* **Per user**: `user:{user_id}` - Limit each user independently\n* **Per IP address**: `ip:{ip_address}` - Limit by client IP\n* **Per API endpoint**: `api:{endpoint}:{user_id}` - Different limits per endpoint\n* **Global**: `global:api` - Single limit shared across all requests"},{"id":"script-caching-with-evalsha","title":"Script caching with EVALSHA","role":"content","text":"The Rust implementation uses [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) for optimal performance. The script is loaded once with `SCRIPT LOAD`, and subsequent calls use the cached SHA1 hash. If the script is evicted, the module automatically falls back to [`EVAL`](https://redis.io/docs/latest/commands/eval) and reloads it:\n\n[code example]"},{"id":"thread-safety-with-arc-and-mutex","title":"Thread safety with Arc and Mutex","role":"content","text":"For concurrent access across threads, wrap the connection in `Arc<Mutex<>>`:\n\n[code example]"},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo uses a Cargo project. Download the files from the [`rust` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/rate-limiter/rust) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"A demonstration HTTP server is included to show the rate limiter in action\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/rate-limiter/rust/demo_server.rs)):\n\n[code example]\n\nThe demo provides an interactive web interface where you can:\n\n* Submit requests and see them allowed or denied in real-time\n* View the current token count\n* Adjust rate limit parameters dynamically\n* Test different rate limiting scenarios\n\nThe demo assumes Redis is running on `localhost:6379` but you can specify a different host using the `REDIS_URL` environment variable. Visit `http://localhost:8080` in your browser to try it out."},{"id":"response-headers","title":"Response headers","role":"returns","text":"It's common to include rate limit information in HTTP response headers. Here's an example using a hypothetical web framework:\n\n[code example]"},{"id":"customization","title":"Customization","role":"content","text":""},{"id":"using-with-asyncawait","title":"Using with async/await","role":"content","text":"For async web frameworks like `actix-web` or `axum`, use the async version of the Redis client:\n\n[code example]"},{"id":"error-handling-strategies","title":"Error handling strategies","role":"errors","text":"The `allow` method returns a `RedisResult`, which you should handle based on your requirements. Decide whether to fail open (allow requests) or fail closed (deny requests) when Redis is unavailable:\n\n[code example]"},{"id":"using-with-connection-pools","title":"Using with connection pools","role":"content","text":"For production use, maintain a connection pool rather than creating connections per request:\n\n[code example]"},{"id":"custom-token-consumption","title":"Custom token consumption","role":"content","text":"To consume multiple tokens per request, modify the Lua script or call `allow` multiple times:\n\n[code example]"},{"id":"alternative-rate-limiting-algorithms","title":"Alternative rate limiting algorithms","role":"content","text":"The token bucket algorithm above handles most use cases but Redis supports\nother rate limiter patterns that might fit your requirements better. The table\nbelow lists four other algorithms alongside token bucket and summarizes\ntheir features:\n\n| Algorithm | Memory | Accuracy | Burst behavior | Best for |\n|---|---|---|---|---|\n| [Token bucket](#how-it-works) | 1 key (hash) | Exact | Controlled bursts | APIs with bursty traffic |\n| [Fixed window counter](#fixed-window-counter) | 1 key (string) | Approximate | 2x burst at boundaries | Simple API limits |\n| [Sliding window log](#sliding-window-log) | O(n) entries | Exact | No bursts | High-value APIs, audit trails |\n| [Sliding window counter](#sliding-window-counter) | 2 keys (string) | Near-exact | Smoothed boundaries | General-purpose APIs |\n| [Leaky bucket (policing)](#leaky-bucket-policing) | 1 key (hash) | Exact | No bursts | Strict no-burst enforcement |\n\nThe sections below give example implementations of these other algorithms.\nThe three time-based algorithms call `redis.call('TIME')` inside the Lua\nscript to derive the current timestamp from the Redis server clock. This\neliminates clock drift when the limiter runs across multiple application\nservers. The fixed window counter reads no clock: the key's TTL defines\nthe window."},{"id":"fixed-window-counter","title":"Fixed window counter","role":"content","text":"Counts requests within discrete, non-overlapping time intervals.\nSimplest algorithm — one key per window, one `EVAL` round trip.\n\n[code example]\n\n**Trade-off**: A client can make 2x requests by sending `limit` requests\nat the end of one window and `limit` requests at the start of the next."},{"id":"sliding-window-log","title":"Sliding window log","role":"content","text":"Records the exact timestamp of every request in a sorted set.\nProvides a true rolling window with no boundary bursts.\n\n[code example]\n\n**Trade-off**: Memory grows O(n) with request volume. Not ideal for\nhigh-volume, high-cardinality rate limiting."},{"id":"sliding-window-counter","title":"Sliding window counter","role":"content","text":"Blends two fixed-window counters using a weighted average to approximate\na true sliding window. Near-exact accuracy with the same low memory\nfootprint as a fixed window. The two keys use hash tags so they map\nto the same slot in Redis Cluster.\n\n[code example]\n\n**Trade-off**: The weighted estimate may let slightly more or fewer\nrequests through than the exact limit. Negligible for most apps."},{"id":"leaky-bucket-policing","title":"Leaky bucket (policing)","role":"content","text":"A virtual bucket fills with incoming requests and drains at a fixed rate.\nIf the bucket is full, requests are rejected immediately. This is the\npolicing variant — requests are allowed or denied instantly with no delay.\n\n[code example]\n\n**Trade-off**: Overflow traffic is rejected immediately. Clients must\nhandle `429 Too Many Requests` and retry with backoff."},{"id":"learn-more","title":"Learn more","role":"related","text":"* [EVAL command](https://redis.io/docs/latest/commands/eval) - Execute Lua scripts\n* [EVALSHA command](https://redis.io/docs/latest/commands/evalsha) - Execute cached Lua scripts\n* [Lua scripting](https://redis.io/docs/latest/develop/programmability/eval-intro) - Introduction to Redis Lua scripting\n* [HMGET command](https://redis.io/docs/latest/commands/hmget) - Get multiple hash fields\n* [HMSET command](https://redis.io/docs/latest/commands/hmset) - Set multiple hash fields\n* [Rust client](https://redis.io/docs/latest/develop/clients/rust) - Redis Rust client documentation"}],"examples":[{"id":"the-lua-script-ex0","language":"lua","code":"local key = KEYS[1]\nlocal capacity = tonumber(ARGV[1])\nlocal refill_rate = tonumber(ARGV[2])\nlocal refill_interval = tonumber(ARGV[3])\nlocal now = tonumber(ARGV[4])\n\n-- Get current state or initialize\nlocal bucket = redis.call('HMGET', key, 'tokens', 'last_refill')\nlocal tokens = tonumber(bucket[1])\nlocal last_refill = tonumber(bucket[2])\n\n-- Initialize if this is the first request\nif tokens == nil then\n    tokens = capacity\n    last_refill = now\nend\n\n-- Calculate token refill\nlocal time_passed = now - last_refill\nlocal refills = math.floor(time_passed / refill_interval)\n\nif refills > 0 then\n    tokens = math.min(capacity, tokens + (refills * refill_rate))\n    last_refill = last_refill + (refills * refill_interval)\nend\n\n-- Try to consume a token\nlocal allowed = 0\nif tokens >= 1 then\n    tokens = tokens - 1\n    allowed = 1\nend\n\n-- Update state\nredis.call('HMSET', key, 'tokens', tokens, 'last_refill', last_refill)\n\n-- Return result: allowed (1 or 0) and remaining tokens\nreturn {allowed, tokens}","section_id":"the-lua-script"},{"id":"installation-ex0","language":"toml","code":"[dependencies]\nredis = \"0.24\"","section_id":"installation"},{"id":"installation-ex1","language":"toml","code":"[dependencies]\nredis = { version = \"0.24\", features = [\"tokio-comp\"] }\ntokio = { version = \"1\", features = [\"full\"] }","section_id":"installation"},{"id":"using-the-rust-module-ex0","language":"rust","code":"use redis::{Client, Commands, RedisResult};\n\nfn main() -> RedisResult<()> {\n    // Create a Redis connection\n    let client = Client::open(\"redis://localhost:6379/\")?;\n    let mut con = client.get_connection()?;\n\n    // Create a rate limiter: 10 requests per second\n    let limiter = TokenBucket::new(\n        10,   // capacity: Maximum burst size\n        1.0,  // refill_rate: Add 1 token per interval\n        1.0,  // refill_interval: Every 1 second\n    );\n\n    // Check if a request should be allowed\n    let result = limiter.allow(&mut con, \"user:123\")?;\n\n    if result.allowed {\n        println!(\"Request allowed. {} tokens remaining.\", result.remaining);\n        // Process the request\n    } else {\n        println!(\"Request denied. Rate limit exceeded.\");\n        // Return 429 Too Many Requests\n    }\n\n    Ok(())\n}","section_id":"using-the-rust-module"},{"id":"script-caching-with-evalsha-ex0","language":"rust","code":"// The module handles script caching automatically.\n// First call loads the script, subsequent calls use EVALSHA.\nlet result1 = limiter.allow(&mut con, \"user:123\")?; // Uses EVAL + caches\nlet result2 = limiter.allow(&mut con, \"user:123\")?; // Uses EVALSHA (faster)","section_id":"script-caching-with-evalsha"},{"id":"thread-safety-with-arc-and-mutex-ex0","language":"rust","code":"use std::sync::{Arc, Mutex};\nuse std::thread;\n\nlet client = Client::open(\"redis://localhost:6379/\")?;\nlet connection = Arc::new(Mutex::new(client.get_connection()?));\n\nlet limiter = TokenBucket::new(10, 1.0, 1.0);\n\nlet handles: Vec<_> = (0..10)\n    .map(|i| {\n        let con = Arc::clone(&connection);\n        let limiter = limiter.clone();\n        thread::spawn(move || {\n            let mut con = con.lock().unwrap();\n            match limiter.allow(&mut *con, \"shared:resource\") {\n                Ok(result) => {\n                    println!(\"Thread {}: allowed={}, remaining={}\",\n                             i, result.allowed, result.remaining);\n                }\n                Err(e) => eprintln!(\"Thread {}: error: {}\", i, e),\n            }\n        })\n    })\n    .collect();\n\nfor handle in handles {\n    handle.join().unwrap();\n}","section_id":"thread-safety-with-arc-and-mutex"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir rate-limiter-demo && cd rate-limiter-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/rate-limiter/rust\ncurl -O $BASE/Cargo.toml\ncurl -O $BASE/Cargo.lock\ncurl -O $BASE/token_bucket.rs\ncurl -O $BASE/demo_server.rs","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"# Install dependencies\ncargo build\n\n# Run the demo server\ncargo run --bin demo","section_id":"start-the-demo-server"},{"id":"response-headers-ex0","language":"rust","code":"use std::time::{SystemTime, UNIX_EPOCH};\n\nfn handle_request(\n    limiter: &TokenBucket,\n    con: &mut Connection,\n    user_id: &str,\n) -> Result<Response, Error> {\n    let result = limiter.allow(con, &format!(\"user:{}\", user_id))?;\n\n    let mut response = Response::new();\n\n    // Add standard rate limit headers\n    response.headers.insert(\n        \"X-RateLimit-Limit\",\n        limiter.capacity.to_string(),\n    );\n    response.headers.insert(\n        \"X-RateLimit-Remaining\",\n        result.remaining.floor().to_string(),\n    );\n\n    let reset_time = SystemTime::now()\n        .duration_since(UNIX_EPOCH)\n        .unwrap()\n        .as_secs() + limiter.refill_interval as u64;\n    response.headers.insert(\n        \"X-RateLimit-Reset\",\n        reset_time.to_string(),\n    );\n\n    if !result.allowed {\n        response.status = 429; // Too Many Requests\n        response.headers.insert(\n            \"Retry-After\",\n            limiter.refill_interval.floor().to_string(),\n        );\n        return Ok(response);\n    }\n\n    // Process the request\n    Ok(response)\n}","section_id":"response-headers"},{"id":"using-with-asyncawait-ex0","language":"rust","code":"use redis::aio::Connection;\n\nasync fn rate_limit_check(\n    limiter: &TokenBucket,\n    con: &mut Connection,\n    key: &str,\n) -> redis::RedisResult<RateLimitResult> {\n    limiter.allow_async(con, key).await\n}\n\n// Example with axum\nuse axum::{\n    extract::State,\n    http::StatusCode,\n    response::IntoResponse,\n};\n\nasync fn handle_request(\n    State(app_state): State<AppState>,\n) -> impl IntoResponse {\n    let mut con = app_state.redis_pool.get().await.unwrap();\n\n    match app_state.limiter.allow_async(&mut con, \"user:123\").await {\n        Ok(result) if result.allowed => {\n            (StatusCode::OK, \"Request processed\")\n        }\n        Ok(_) => {\n            (StatusCode::TOO_MANY_REQUESTS, \"Rate limit exceeded\")\n        }\n        Err(e) => {\n            eprintln!(\"Redis error: {}\", e);\n            (StatusCode::INTERNAL_SERVER_ERROR, \"Service unavailable\")\n        }\n    }\n}","section_id":"using-with-asyncawait"},{"id":"error-handling-strategies-ex0","language":"rust","code":"// Fail open: allow requests when Redis is unavailable\nlet allowed = match limiter.allow(&mut con, \"user:123\") {\n    Ok(result) => result.allowed,\n    Err(e) => {\n        eprintln!(\"Rate limiter error: {}. Failing open.\", e);\n        true // Allow the request\n    }\n};\n\n// Or fail closed: deny requests when Redis is unavailable\nlet allowed = match limiter.allow(&mut con, \"user:123\") {\n    Ok(result) => result.allowed,\n    Err(e) => {\n        eprintln!(\"Rate limiter error: {}. Failing closed.\", e);\n        false // Deny the request\n    }\n};","section_id":"error-handling-strategies"},{"id":"using-with-connection-pools-ex0","language":"rust","code":"use r2d2_redis::{r2d2, RedisConnectionManager};\n\n// Create a connection pool\nlet manager = RedisConnectionManager::new(\"redis://localhost:6379\")?;\nlet pool = r2d2::Pool::builder()\n    .max_size(15)\n    .build(manager)?;\n\nlet limiter = TokenBucket::new(10, 1.0, 1.0);\n\n// Use the pool\nlet mut con = pool.get()?;\nlet result = limiter.allow(&mut *con, \"user:123\")?;","section_id":"using-with-connection-pools"},{"id":"custom-token-consumption-ex0","language":"rust","code":"// Reserve 5 tokens for a batch operation\nlet mut tokens_acquired = 0;\nfor _ in 0..5 {\n    match limiter.allow(&mut con, \"batch:operation\")? {\n        result if result.allowed => tokens_acquired += 1,\n        _ => break,\n    }\n}\n\nif tokens_acquired == 5 {\n    println!(\"Batch operation allowed\");\n} else {\n    println!(\"Not enough tokens. Acquired: {}\", tokens_acquired);\n}","section_id":"custom-token-consumption"},{"id":"fixed-window-counter-ex0","language":"rust","code":"use redis::Script;\n\nconst FIXED_WINDOW_SCRIPT: &str = r#\"\nlocal key    = KEYS[1]\nlocal limit  = tonumber(ARGV[1])\nlocal window = tonumber(ARGV[2])\n\nlocal count = redis.call('INCR', key)\nif count == 1 then\n    redis.call('EXPIRE', key, window)\nend\n\nlocal ttl = redis.call('PTTL', key)\n\nif count > limit then\n    return {0, ttl}\nend\nreturn {1, ttl}\n\"#;\n\n/// Returns `(allowed, retry_after_ms)`. `retry_after_ms` is 0 when allowed.\nfn fixed_window_allow(\n    con: &mut dyn redis::ConnectionLike,\n    key: &str,\n    limit: i64,\n    window_seconds: i64,\n) -> redis::RedisResult<(bool, i64)> {\n    let (allowed, ttl): (i64, i64) = Script::new(FIXED_WINDOW_SCRIPT)\n        .key(key)\n        .arg(limit)\n        .arg(window_seconds)\n        .invoke(con)?;\n\n    Ok((allowed == 1, if allowed == 1 { 0 } else { ttl }))\n}","section_id":"fixed-window-counter"},{"id":"sliding-window-log-ex0","language":"rust","code":"// Add to Cargo.toml: uuid = { version = \"1\", features = [\"v4\"] }\nuse redis::Script;\nuse uuid::Uuid;\n\nconst SLIDING_WINDOW_LOG_SCRIPT: &str = r#\"\nlocal key    = KEYS[1]\nlocal limit  = tonumber(ARGV[1])\nlocal window = tonumber(ARGV[2])\nlocal member = ARGV[3]\n\nlocal t      = redis.call('TIME')\nlocal now    = tonumber(t[1]) + tonumber(t[2]) / 1e6\nlocal cutoff = now - window\n\nredis.call('ZREMRANGEBYSCORE', key, '-inf', cutoff)\n\nlocal count = redis.call('ZCARD', key)\n\nif count < limit then\n    redis.call('ZADD', key, now, member)\n    redis.call('EXPIRE', key, window * 2)\n    return {1, 0}\nend\n\nlocal oldest = redis.call('ZRANGE', key, 0, 0, 'WITHSCORES')\nlocal retry_after_ms = 0\nif oldest[2] then\n    retry_after_ms = math.floor((tonumber(oldest[2]) + window - now) * 1000)\nend\n\nreturn {0, retry_after_ms}\n\"#;\n\nfn sliding_window_log_allow(\n    con: &mut dyn redis::ConnectionLike,\n    key: &str,\n    limit: i64,\n    window_seconds: i64,\n) -> redis::RedisResult<(bool, i64)> {\n    let member = Uuid::new_v4().to_string();\n\n    let (allowed, retry_after_ms): (i64, i64) = Script::new(SLIDING_WINDOW_LOG_SCRIPT)\n        .key(key)\n        .arg(limit)\n        .arg(window_seconds)\n        .arg(member)\n        .invoke(con)?;\n\n    Ok((allowed == 1, retry_after_ms))\n}","section_id":"sliding-window-log"},{"id":"sliding-window-counter-ex0","language":"rust","code":"use redis::Script;\n\nconst SLIDING_WINDOW_COUNTER_SCRIPT: &str = r#\"\nlocal base   = KEYS[1]\nlocal limit  = tonumber(ARGV[1])\nlocal window = tonumber(ARGV[2])\n\nlocal t   = redis.call('TIME')\nlocal now = tonumber(t[1]) + tonumber(t[2]) / 1e6\n\nlocal window_num = math.floor(now / window)\nlocal elapsed     = (now % window) / window\n\nlocal curr_key = base .. ':' .. window_num\nlocal prev_key = base .. ':' .. (window_num - 1)\n\nlocal prev = tonumber(redis.call('GET', prev_key) or 0)\nlocal curr = tonumber(redis.call('GET', curr_key) or 0)\n\nlocal estimate = prev * (1 - elapsed) + curr\n\nif estimate >= limit then\n    return {0, 0}\nend\n\nlocal new_count = redis.call('INCR', curr_key)\nif new_count == 1 then\n    redis.call('EXPIRE', curr_key, window * 2)\nend\n\nreturn {1, 0}\n\"#;\n\nfn sliding_window_counter_allow(\n    con: &mut dyn redis::ConnectionLike,\n    key: &str,\n    limit: i64,\n    window_seconds: i64,\n) -> redis::RedisResult<bool> {\n    let (allowed, _): (i64, i64) = Script::new(SLIDING_WINDOW_COUNTER_SCRIPT)\n        .key(format!(\"{{{}}}\", key))\n        .arg(limit)\n        .arg(window_seconds)\n        .invoke(con)?;\n\n    Ok(allowed == 1)\n}","section_id":"sliding-window-counter"},{"id":"leaky-bucket-policing-ex0","language":"rust","code":"use redis::Script;\n\nconst LEAKY_BUCKET_SCRIPT: &str = r#\"\nlocal key       = KEYS[1]\nlocal capacity  = tonumber(ARGV[1])\nlocal leak_rate = tonumber(ARGV[2])\n\nlocal t   = redis.call('TIME')\nlocal now = tonumber(t[1]) + tonumber(t[2]) / 1e6\n\nlocal data      = redis.call('HGETALL', key)\nlocal level     = 0\nlocal last_leak = now\n\nif #data > 0 then\n    for i = 1, #data, 2 do\n        if data[i] == 'level' then\n            level = tonumber(data[i+1])\n        elseif data[i] == 'last_leak' then\n            last_leak = tonumber(data[i+1])\n        end\n    end\nend\n\nlocal elapsed = now - last_leak\nlevel = math.max(0, level - elapsed * leak_rate)\n\nif level + 1 > capacity then\n    return {0, math.floor((level + 1 - capacity) / leak_rate * 1000)}\nend\n\nlevel = level + 1\nlocal ttl = math.ceil(capacity / leak_rate) + 1\n\nredis.call('HSET', key, 'level', level, 'last_leak', now)\nredis.call('EXPIRE', key, ttl)\n\nreturn {1, 0}\n\"#;\n\nfn leaky_bucket_allow(\n    con: &mut dyn redis::ConnectionLike,\n    key: &str,\n    capacity: i64,\n    leak_rate: f64,\n) -> redis::RedisResult<(bool, i64)> {\n    let (allowed, retry_after_ms): (i64, i64) = Script::new(LEAKY_BUCKET_SCRIPT)\n        .key(key)\n        .arg(capacity)\n        .arg(leak_rate)\n        .invoke(con)?;\n\n    Ok((allowed == 1, retry_after_ms))\n}","section_id":"leaky-bucket-policing"}]}
{"schema_version":2,"id":"develop/use-cases/recommendation-engine/dotnet","title":"Redis recommendation engine with NRedisStack","url":"https://redis.io/docs/latest/develop/use-cases/recommendation-engine/dotnet/","summary":"Build a Redis-backed recommendation engine in C# with NRedisStack and SmartComponents.LocalEmbeddings","aliases":["/develop/use-cases/recommendation-engine/stackexchange.redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-17T10:28:27+01:00","children":[],"page_type":"content","content_hash":"c10502ec7f937df5bdd5e734d5b0bc2f55dec4ffca6f36b9b80cbd0c803938ed","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a small Redis-backed product recommendation service in C# with [`NRedisStack`](https://redis.io/docs/latest/develop/clients/dotnet) (the Redis Stack wrapper on top of [StackExchange.Redis](https://seredis.dev/)) and the [`SmartComponents.LocalEmbeddings`](https://www.nuget.org/packages/SmartComponents.LocalEmbeddings) library, which ships a bundled 384-dimensional [BAAI `bge-micro-v2`](https://huggingface.co/TaylorAI/bge-micro-v2) ONNX model (no API key, no shared library to install). The other ports in this guide series use [`sentence-transformers/all-MiniLM-L6-v2`](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2), also a 384-dim encoder; the index schema is identical, so the same `FT.SEARCH` query shape works for both. It includes a local web server built on `System.Net.HttpListener` so you can embed a natural-language query, run a KNN retrieval with structured pre-filters in one round trip, feed clicks back as a session signal, and watch the next recommendation incorporate them immediately."},{"id":"overview","title":"Overview","role":"overview","text":"Each product is stored as a single Redis [Hash](https://redis.io/docs/latest/develop/data-types/hashes) at `product:<id>`. The hash holds the structured metadata (name, description, category, brand, price, rating, in-stock flag) alongside the raw `float32` bytes of a 384-dimensional embedding. A single [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) index covers every field, so one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call with a `KNN` clause does the vector similarity *and* the TAG / NUMERIC / TEXT pre-filtering in the same pass — no cross-store joins.\n\nPer-user state lives in `user:<id>:features`: a session vector written as an exponentially weighted average of recently-clicked item embeddings, plus per-category affinity counters incremented atomically with [`HINCRBYFLOAT`](https://redis.io/docs/latest/commands/hincrbyfloat). [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) does *not* read that hash directly; instead, the application reads it on the next request and passes the session vector to `FT.SEARCH` as the query parameter. The two-step is what lets a click feed the very next recommendation without a batch cycle or cache invalidation.\n\nThat gives you:\n\n* A single round trip for retrieval — vector KNN + structured filters in one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search).\n* Sub-millisecond hot path once the query is embedded; embedding the query is the bottleneck, and that's a model-side cost, not a Redis one.\n* Real-time session signals — a click writes a new session vector and bumps an affinity counter; the next query reads them and folds them in.\n* No-downtime embedding refresh — [`HSET`](https://redis.io/docs/latest/commands/hset) on the vector field, and the HNSW index reflects the change on the next query."},{"id":"how-it-works","title":"How it works","role":"content","text":"There are two distinct paths: a **query path** runs every time the application wants a recommendation, and a **click path** runs every time the user interacts with a product."},{"id":"query-path-per-recommendation-request","title":"Query path (per recommendation request)","role":"content","text":"1. The application calls `embedder.EncodeOne(queryText)` to turn a natural-language query into a 384-dimensional `float[]`.\n2. The application reads the user's session vector and affinities from the user features hash. If a session vector exists, it gets blended into the query vector with a tunable weight, so the user's recent clicks pull retrieval toward what they've been engaging with.\n3. `recommender.CandidateRetrieve(queryVec, opts)` runs [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) with a pre-filter clause built from the request's TAG / NUMERIC / TEXT inputs, followed by a `KNN k @embedding $vec` clause. Redis returns up to `k` candidates with the cosine distance to the query (lower is closer).\n4. `recommender.Rerank(candidates, userFeatures, weight)` subtracts a log-scaled per-category affinity bonus from each candidate's distance and re-sorts the list closest-first. The log scaling keeps repeated clicks from running away with the ranking."},{"id":"click-path-per-user-interaction","title":"Click path (per user interaction)","role":"content","text":"When the user clicks a product, `recommender.RecordClick(userId, productId)` does the following:\n\n1. Reads the clicked item's embedding from its hash.\n2. Reads the user's previous session vector from the user features hash, blends the new click in via an exponentially weighted moving average, and writes the new session vector back with [`HSET`](https://redis.io/docs/latest/commands/hset). This is a read-modify-write — atomic against any single write but not against a concurrent click for the same user; in practice, per-user click streams don't generate the contention to make this matter, and if a deployment does, the read and write can be wrapped in [`WATCH/MULTI/EXEC`](https://redis.io/docs/latest/commands/multi) or a small Lua script.\n3. Bumps the per-category affinity counter with [`HINCRBYFLOAT`](https://redis.io/docs/latest/commands/hincrbyfloat) — atomic, no read needed — and the click count with [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby).\n\nThe next query path picks both changes up the next time it reads the user features hash.\n\nRefreshing an item's embedding follows a similar shape: encode the new text, write the vector bytes back with [`HSET`](https://redis.io/docs/latest/commands/hset), and the HNSW index reflects the change on the next query without a rebuild."},{"id":"the-recommender-helper","title":"The recommender helper","role":"content","text":"The `Recommender` class wraps the Redis Search index and the retrieval flow\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/recommendation-engine/dotnet/Recommender.cs)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each product is one Redis Hash. The vector field is raw little-endian `float32` bytes — no JSON wrapping — because the Redis Search vector encoding expects exactly that.\n\n[code example]\n\nThe Redis Search index schema treats every field as queryable in its natural type:\n\n[code example]\n\nPer-user state is a separate hash. The session vector is stored as raw `float32` bytes the same way; affinity counters are stored as plain numeric strings, one field per category, prefixed with `aff:` so they don't collide with anything else.\n\n[code example]"},{"id":"the-query","title":"The query","role":"content","text":"The KNN clause is a hybrid query: a pre-filter expression in parentheses, then `=>[KNN k @embedding $vec]`. With dialect 2 (the default in NRedisStack 1.0+), Redis applies the filter first and then KNN-ranks only the matching documents.\n\n[code example]\n\nWhen there's no filter, the pre-filter clause is just `(*)`. `vector_score` returned by Redis is the cosine *distance* (0 means identical, 2 means opposite), so the result is sorted ascending — a score of 0.0 is a perfect match."},{"id":"binary-fields-with-nredisstack-and-stackexchangeredis","title":"Binary fields with NRedisStack and StackExchange.Redis","role":"content","text":"The embedding field is binary, while everything else in the same hash is text. StackExchange.Redis handles mixed types in a single `HSET` automatically — pass `byte[]` for binary fields and `string` for text fields in the same `HashEntry` array:\n\n[code example]\n\n`FT.SEARCH` parameter values follow the same convention — pass the raw `byte[]` to `Query.AddParam` and NRedisStack includes it in the command body without any encoding.\n\nThe `Recommender` class uses `IDatabase.CreateBatch()` to pipeline the `HSET`s for the catalogue, so the initial seed of 37 products goes out in a single round trip."},{"id":"the-catalog-builder","title":"The catalog builder","role":"content","text":"Item vectors are pre-computed once and stored in `catalog.json` so the demo server can boot quickly. `BuildCatalog.cs` and the `build-catalog` sub-command are the reference for how to do that — and are the code you'd adapt for a real catalog ingestion pipeline ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/recommendation-engine/dotnet/BuildCatalog.cs)):\n\n[code example]\n\nIn production the equivalent of this script lives in an offline pipeline: embed once on catalog updates and ship the vectors into Redis with [`HSET`](https://redis.io/docs/latest/commands/hset). The serving tier still embeds the *query* on each request, but that's usually fronted by a dedicated model server or batched at the API gateway rather than co-located with the data tier as it is in this demo."},{"id":"the-interactive-demo","title":"The interactive demo","role":"content","text":"`DemoServer.cs` runs an `HttpListener` server with one demo user (`demo`). The HTML page lets you:\n\n* Type a natural-language query and toggle filters: TAG (category, brand, in-stock), NUMERIC (price range, rating), and TEXT (the **Description contains** field, a phrase pre-filter against the `description` text index).\n* Toggle session blending and category-affinity re-ranking independently to see what each layer contributes.\n* Click any product card to record a click into the session. The page re-renders the user features panel immediately — the click wrote to the user features hash, and the next search reads that hash to fold the update in.\n* Refresh a single product's embedding with new text and watch the ranking change on the next query.\n\nThe server holds one `Embedder` instance and one `Recommender` for the lifetime of the process. Endpoints:\n\n| Endpoint                  | What it does                                                                |\n|---------------------------|-----------------------------------------------------------------------------|\n| `GET  /state`             | Index info, user features, full catalog listing.                            |\n| `POST /search`            | Embed the query, run `FT.SEARCH` with filters + KNN, optionally re-rank.    |\n| `POST /click`             | Record a click for the demo user: update session vector and affinity.       |\n| `POST /reset-user`        | Drop the user features hash.                                                |\n| `POST /reset-index`       | Drop the index and documents and re-seed from `catalog.json`.               |\n| `POST /refresh-embedding` | Embed new text and overwrite one product's vector with `HSET`.              |"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* Redis 7.4 or later with the Redis Search module (Redis Stack or Redis Community Edition).\n* [.NET 8 SDK](https://dotnet.microsoft.com/download) or later.\n* NuGet packages, all declared in `RecommendationDemo.csproj`: `StackExchange.Redis`, `NRedisStack` (the Redis Stack wrapper that exposes `FT.*` and `JSON.*` over the StackExchange.Redis multiplexer), and `SmartComponents.LocalEmbeddings` (in-process ONNX runtime that ships a 384-dimensional sentence encoder with no external download or API key)."},{"id":"run-the-demo-locally","title":"Run the demo locally","role":"content","text":"1.  Clone the [`redis/docs`](https://github.com/redis/docs) repository and change into the example directory:\n\n    [code example]\n\n2.  Make sure a Redis instance with the Redis Search module is running locally on port 6379. [Redis Stack](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack) or [Redis 8 with Search](https://redis.io/docs/latest/develop/ai/search-and-query) both work.\n\n3.  Generate the catalog with pre-computed embeddings. The first run loads the ONNX model bundled with `SmartComponents.LocalEmbeddings` (no external download):\n\n    [code example]\n\n4.  Start the demo server:\n\n    [code example]\n\n5.  Open <http://localhost:8084> and try some queries:\n\n    * **\"insulated down jacket for cold weather\"** — filtered to `outerwear`, in-stock only.\n    * **\"comfortable shoes for trail running\"** — filtered to `footwear`.\n    * Add **Description contains: waterproof** to either query above to see a TEXT pre-filter combine with the KNN.\n    * Click a couple of products to seed a session, then re-run the same query with **Blend session vector into query** on and watch the ranking shift.\n    * Use **Refresh embedding** to change a product's vector — for example, change the Alpine down parka's text to \"heavy duty arctic expedition parka with hood\" and re-run the jacket query to see the result move.\n\nThe server is read/write against your local Redis. The default index name is `recommend:idx` and product keys live under `product:`. Pass `--no-reset` to keep an existing index across restarts, `--redis-host` / `--redis-port` to point at a different Redis, or `--index-name` and `--key-prefix` to share a Redis instance with other ports without collisions."}],"examples":[{"id":"the-recommender-helper-ex0","language":"csharp","code":"using StackExchange.Redis;\nusing RecommendationDemo;\n\nvar mux = ConnectionMultiplexer.Connect(\"localhost:6379\");\nvar recommender = new Recommender(\n    mux.GetDatabase(),\n    indexName: \"recommend:idx\",\n    keyPrefix: \"product:\");\nrecommender.CreateIndex();                 // idempotent\n\n// SmartComponents.LocalEmbeddings ships its own 384-dim ONNX model\n// (BAAI's bge-micro-v2) which the Embedder loads on construction. The\n// other ports in this guide series use sentence-transformers/all-MiniLM-L6-v2,\n// also 384-dim. Both encode to the same vector field; vectors aren't\n// interchangeable across the two models, but each port produces its own\n// catalog.json so cross-port mixing isn't an issue.\nusing var embedder = new Embedder();       // bge-micro-v2, 384-dim\nvar queryVec = embedder.EncodeOne(\"warm waterproof jacket for hiking\");\n\n// Retrieval: KNN with structured pre-filter in one round trip.\n// Filters combine TAG (Category, Brand, InStockOnly), NUMERIC\n// (MinPrice/MaxPrice, MinRating), and TEXT (TextMatch against\n// TextField) — Redis applies them all in front of the KNN.\nvar candidates = recommender.CandidateRetrieve(queryVec, new RetrieveOptions\n{\n    Filter = new FilterOptions\n    {\n        Category    = \"outerwear\",\n        InStockOnly = true,\n        MinPrice    = 50,\n        MaxPrice    = 200,\n        TextMatch   = \"waterproof\",   // TEXT pre-filter on @description\n    },\n    K = 10,\n});\n\n// Record a click — updates the user's session vector and category\n// affinity atomically; the next call to CandidateRetrieve sees it.\nrecommender.RecordClick(\"alice\", \"p001\");\n\n// Pull user features so the next retrieval can blend the session\n// vector into the query and apply the category-affinity re-rank.\nvar features = recommender.GetUserFeatures(\"alice\");\ncandidates = recommender.CandidateRetrieve(queryVec, new RetrieveOptions\n{\n    Filter = new FilterOptions { Category = \"outerwear\", InStockOnly = true },\n    K = 10,\n    SessionVec    = features.SessionVec,\n    SessionWeight = 0.3,\n});\ncandidates = recommender.Rerank(candidates, features, affinityWeight: 0.15);\n\n// Hot embedding refresh — overwrite the vector for one product;\n// the HNSW index reflects the change on the next FT.SEARCH.\nvar newVec = embedder.EncodeOne(\"heavy-duty arctic expedition parka\");\nrecommender.RefreshEmbedding(\"p001\", newVec);","section_id":"the-recommender-helper"},{"id":"data-model-ex0","language":"text","code":"product:p001\n  name=Alpine down parka\n  description=Heavyweight 800-fill goose down parka...\n  category=outerwear\n  brand=northpeak\n  price=289.0\n  rating=4.7\n  in_stock=true\n  embedding=<384 × float32 little-endian bytes>","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"FT.CREATE recommend:idx\n  ON HASH PREFIX 1 product:\n  SCHEMA\n    name        TEXT\n    description TEXT\n    category    TAG\n    brand       TAG\n    in_stock    TAG\n    price       NUMERIC SORTABLE\n    rating      NUMERIC SORTABLE\n    embedding   VECTOR HNSW 6 TYPE FLOAT32 DIM 384 DISTANCE_METRIC COSINE","section_id":"data-model"},{"id":"data-model-ex2","language":"text","code":"user:alice:features\n  session_vec=<384 × float32 little-endian bytes>\n  aff:outerwear=2.0\n  aff:footwear=1.0\n  last_clicked_id=p015\n  last_clicked_category=footwear\n  clicks=3","section_id":"data-model"},{"id":"the-query-ex0","language":"text","code":"FT.SEARCH recommend:idx\n  \"(@category:{outerwear} @in_stock:{true} @price:[50 200])\n     =>[KNN 10 @embedding $vec AS vector_score]\"\n  PARAMS 2 vec <384-float32-bytes>\n  SORTBY vector_score\n  RETURN 8 name description category brand price rating in_stock vector_score\n  DIALECT 2","section_id":"the-query"},{"id":"binary-fields-with-nredisstack-and-stackexchangeredis-ex0","language":"csharp","code":"using StackExchange.Redis;\n\ndb.HashSet(\"product:p001\", new HashEntry[]\n{\n    new(\"name\",      \"Alpine down parka\"),\n    new(\"price\",     \"289.0\"),\n    new(\"embedding\", Embedder.FloatsToBytes(vector)),   // byte[]\n});","section_id":"binary-fields-with-nredisstack-and-stackexchangeredis"},{"id":"the-catalog-builder-ex0","language":"csharp","code":"using var embedder = new Embedder();\nvar texts = CatalogSeed.All.Select(p => p.EmbedText()).ToList();\nvar vectors = embedder.EncodeMany(texts);\n\nvar file = new CatalogFile\n{\n    Model = embedder.ModelName,\n    Dim   = embedder.Dim,\n    Products = CatalogSeed.All.Select((p, i) => new CatalogProduct\n    {\n        Id = p.Id, Name = p.Name, Description = p.Description,\n        Category = p.Category, Brand = p.Brand,\n        Price = p.Price, InStock = p.InStock, Rating = p.Rating,\n        EmbeddingB64 = Convert.ToBase64String(\n            Embedder.FloatsToBytes(vectors[i])),\n    }).ToList(),\n};\nFile.WriteAllText(\"catalog.json\", JsonSerializer.Serialize(file, opts));","section_id":"the-catalog-builder"},{"id":"run-the-demo-locally-ex0","language":"bash","code":"git clone https://github.com/redis/docs.git\n    cd docs/content/develop/use-cases/recommendation-engine/dotnet","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex1","language":"bash","code":"dotnet run -- build-catalog","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex2","language":"bash","code":"dotnet run -- --port 8084","section_id":"run-the-demo-locally"}]}
{"schema_version":2,"id":"develop/use-cases/recommendation-engine/go","title":"Redis recommendation engine with go-redis","url":"https://redis.io/docs/latest/develop/use-cases/recommendation-engine/go/","summary":"Build a Redis-backed recommendation engine in Go with go-redis and Hugot","aliases":["/develop/use-cases/recommendation-engine/go-redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-17T10:28:27+01:00","children":[],"page_type":"content","content_hash":"b9eeda7fa054fef81917bd0e46c1b6cb4fb3a1d96405ed996d4f428c8bf2fe95","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a small Redis-backed product recommendation service in Go with [`go-redis`](https://redis.io/docs/latest/develop/clients/go) and the [Hugot](https://pkg.go.dev/github.com/knights-analytics/hugot) library (pure-Go ONNX runtime, no shared library to install). It includes a local web server built on Go's standard `net/http` package so you can embed a natural-language query, run a KNN retrieval with structured pre-filters in one round trip, feed clicks back as a session signal, and watch the next recommendation incorporate them immediately."},{"id":"overview","title":"Overview","role":"overview","text":"Each product is stored as a single Redis [Hash](https://redis.io/docs/latest/develop/data-types/hashes) at `product:<id>`. The hash holds the structured metadata (name, description, category, brand, price, rating, in-stock flag) alongside the raw `float32` bytes of a 384-dimensional embedding. A single [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) index covers every field, so one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call with a `KNN` clause does the vector similarity *and* the TAG / NUMERIC / TEXT pre-filtering in the same pass — no cross-store joins.\n\nPer-user state lives in `user:<id>:features`: a session vector written as an exponentially weighted average of recently-clicked item embeddings, plus per-category affinity counters incremented atomically with [`HINCRBYFLOAT`](https://redis.io/docs/latest/commands/hincrbyfloat). [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) does *not* read that hash directly; instead, the application reads it on the next request and passes the session vector to `FT.SEARCH` as the query parameter. The two-step is what lets a click feed the very next recommendation without a batch cycle or cache invalidation.\n\nThat gives you:\n\n* A single round trip for retrieval — vector KNN + structured filters in one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search).\n* Sub-millisecond hot path once the query is embedded; embedding the query is the bottleneck, and that's a model-side cost, not a Redis one.\n* Real-time session signals — a click writes a new session vector and bumps an affinity counter; the next query reads them and folds them in.\n* No-downtime embedding refresh — [`HSET`](https://redis.io/docs/latest/commands/hset) on the vector field, and the HNSW index reflects the change on the next query."},{"id":"how-it-works","title":"How it works","role":"content","text":"There are two distinct paths: a **query path** runs every time the application wants a recommendation, and a **click path** runs every time the user interacts with a product."},{"id":"query-path-per-recommendation-request","title":"Query path (per recommendation request)","role":"content","text":"1. The application calls `embedder.EncodeOne(ctx, queryText)` to turn a natural-language query into a 384-dimensional `[]float32`.\n2. The application reads the user's session vector and affinities from the user features hash. If a session vector exists, it gets blended into the query vector with a tunable weight, so the user's recent clicks pull retrieval toward what they've been engaging with.\n3. `recommender.CandidateRetrieve(ctx, queryVec, opts)` runs [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) with a pre-filter clause built from the request's TAG / NUMERIC / TEXT inputs, followed by a `KNN k @embedding $vec` clause. Redis returns up to `k` candidates with the cosine distance to the query (lower is closer).\n4. `recommender.Rerank(candidates, userFeatures, weight)` subtracts a log-scaled per-category affinity bonus from each candidate's distance and re-sorts the list closest-first. The log scaling keeps repeated clicks from running away with the ranking."},{"id":"click-path-per-user-interaction","title":"Click path (per user interaction)","role":"content","text":"When the user clicks a product, `recommender.RecordClick(ctx, userID, productID, nil)` does the following:\n\n1. Reads the clicked item's embedding from its hash.\n2. Reads the user's previous session vector from the user features hash, blends the new click in via an exponentially weighted moving average, and writes the new session vector back with [`HSET`](https://redis.io/docs/latest/commands/hset). This is a read-modify-write — atomic against any single write but not against a concurrent click for the same user; in practice, per-user click streams don't generate the contention to make this matter, and if a deployment does, the read and write can be wrapped in [`WATCH/MULTI/EXEC`](https://redis.io/docs/latest/commands/multi) or a small Lua script.\n3. Bumps the per-category affinity counter with [`HINCRBYFLOAT`](https://redis.io/docs/latest/commands/hincrbyfloat) — atomic, no read needed — and the click count with [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby).\n\nThe next query path picks both changes up the next time it reads the user features hash.\n\nRefreshing an item's embedding follows a similar shape: encode the new text, write the vector bytes back with [`HSET`](https://redis.io/docs/latest/commands/hset), and the HNSW index reflects the change on the next query without a rebuild."},{"id":"the-recommender-helper","title":"The recommender helper","role":"content","text":"The `RedisRecommender` struct wraps the Redis Search index and the retrieval flow\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/recommendation-engine/go/recommender.go)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each product is one Redis Hash. The vector field is raw little-endian `float32` bytes — no JSON wrapping — because the Redis Search vector encoding expects exactly that.\n\n[code example]\n\nThe Redis Search index schema treats every field as queryable in its natural type:\n\n[code example]\n\nPer-user state is a separate hash. The session vector is stored as raw `float32` bytes the same way; affinity counters are stored as plain numeric strings, one field per category, prefixed with `aff:` so they don't collide with anything else.\n\n[code example]"},{"id":"the-query","title":"The query","role":"content","text":"The KNN clause is a hybrid query: a pre-filter expression in parentheses, then `=>[KNN k @embedding $vec]`. With dialect 2 (the default in `go-redis` v9.8+), Redis applies the filter first and then KNN-ranks only the matching documents.\n\n[code example]\n\nWhen there's no filter, the pre-filter clause is just `(*)`. `vector_score` returned by Redis is the cosine *distance* (0 means identical, 2 means opposite), so the result is sorted ascending — a score of 0.0 is a perfect match."},{"id":"binary-fields-with-go-redis-v9","title":"Binary fields with `go-redis` v9","role":"content","text":"The embedding field is binary, while everything else in the same hash is text. `go-redis` v9 handles mixed types in a single `HSET` automatically — pass `[]byte` for binary fields and `string` for text fields in the same `map[string]any`:\n\n[code example]\n\n`FT.SEARCH` parameter values are passed the same way — pass `[]byte` for the `vec` parameter and `go-redis` includes it in the command body without any encoding.\n\nThe `Protocol: 2` option is important: `go-redis` v9 currently marks `FT.SEARCH`'s RESP3 responses unstable because their shape may still change. RESP2 keeps everything stable for production use."},{"id":"the-catalog-builder","title":"The catalog builder","role":"content","text":"Item vectors are pre-computed once and stored in `catalog.json` so the demo server can boot quickly. `build_catalog.go` and the `cmd/build_catalog` shim are the reference for how to do that — and are the code you'd adapt for a real catalog ingestion pipeline\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/recommendation-engine/go/build_catalog.go)):\n\n[code example]\n\nIn production the equivalent of this script lives in an offline pipeline: embed once on catalog updates and ship the vectors into Redis with [`HSET`](https://redis.io/docs/latest/commands/hset). The serving tier still embeds the *query* on each request, but that's usually fronted by a dedicated model server or batched at the API gateway rather than co-located with the data tier as it is in this demo."},{"id":"the-interactive-demo","title":"The interactive demo","role":"content","text":"`demo_server.go` runs Go's standard `net/http` server with one demo user (`demo`). The HTML page lets you:\n\n* Type a natural-language query and toggle filters: TAG (category, brand, in-stock), NUMERIC (price range, rating), and TEXT (the **Description contains** field, a phrase pre-filter against the `description` text index).\n* Toggle session blending and category-affinity re-ranking independently to see what each layer contributes.\n* Click any product card to record a click into the session. The page re-renders the user features panel immediately — the click wrote to the user features hash, and the next search reads that hash to fold the update in.\n* Refresh a single product's embedding with new text and watch the ranking change on the next query.\n\nThe server holds one `LocalEmbedder` instance and one `RedisRecommender` for the lifetime of the process. Endpoints:\n\n| Endpoint                  | What it does                                                                |\n|---------------------------|-----------------------------------------------------------------------------|\n| `GET  /state`             | Index info, user features, full catalog listing.                            |\n| `POST /search`            | Embed the query, run `FT.SEARCH` with filters + KNN, optionally re-rank.    |\n| `POST /click`             | Record a click for the demo user: update session vector and affinity.       |\n| `POST /reset-user`        | Drop the user features hash.                                                |\n| `POST /reset-index`       | Drop the index and documents and re-seed from `catalog.json`.               |\n| `POST /refresh-embedding` | Embed new text and overwrite one product's vector with `HSET`.              |"},{"id":"run-the-demo-locally","title":"Run the demo locally","role":"content","text":"1.  Clone the [`redis/docs`](https://github.com/redis/docs) repository and change into the example\n    directory:\n\n    [code example]\n\n2.  Fetch the dependencies (`go-redis` v9 and Hugot):\n\n    [code example]\n\n3.  Make sure a Redis instance with the Redis Search module is running locally on\n    port 6379. [Redis Stack](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack) or\n    [Redis 8 with Search](https://redis.io/docs/latest/develop/ai/search-and-query) both work.\n\n4.  Generate the catalog with pre-computed embeddings. The first run downloads the\n    `all-MiniLM-L6-v2` model (~80 MB) into a local `models/` directory:\n\n    [code example]\n\n5.  Start the demo server:\n\n    [code example]\n\n6.  Open <http://localhost:8084> and try some queries:\n\n    * **\"insulated down jacket for cold weather\"** — filtered to `outerwear`, in-stock only.\n    * **\"comfortable shoes for trail running\"** — filtered to `footwear`.\n    * Add **Description contains: waterproof** to either query above to see a TEXT pre-filter\n      combine with the KNN.\n    * Click a couple of products to seed a session, then re-run the same query\n      with **Blend session vector into query** on and watch the ranking shift.\n    * Use **Refresh embedding** to change a product's vector — for example,\n      change the Alpine down parka's text to \"heavy duty arctic expedition parka\n      with hood\" and re-run the jacket query to see the result move.\n\nThe server is read/write against your local Redis. The default index name is `recommend:idx` and product keys live under `product:`. Pass `--reset=false` to keep an existing index across restarts, or `--redis-addr` to point at a different Redis."}],"examples":[{"id":"the-recommender-helper-ex0","language":"go","code":"package main\n\nimport (\n    \"context\"\n    \"log\"\n\n    \"github.com/redis/go-redis/v9\"\n    rec \"recommendationengine\"\n)\n\nfunc main() {\n    ctx := context.Background()\n    rdb := redis.NewClient(&redis.Options{\n        Addr:     \"localhost:6379\",\n        Protocol: 2, // go-redis marks FT.SEARCH RESP3 unstable.\n    })\n    recommender := rec.NewRecommender(rdb,\n        rec.WithIndexName(\"recommend:idx\"),\n    )\n    if err := recommender.CreateIndex(ctx); err != nil {\n        log.Fatal(err)\n    }\n\n    embedder, err := rec.NewLocalEmbedder(ctx, \"\", \"\")  // all-MiniLM-L6-v2\n    if err != nil {\n        log.Fatal(err)\n    }\n    defer embedder.Close()\n\n    queryVec, _ := embedder.EncodeOne(ctx, \"warm waterproof jacket for hiking\")\n\n    // Retrieval: KNN with structured pre-filter in one round trip.\n    // Filters combine TAG (Category, Brand, InStockOnly), NUMERIC\n    // (MinPrice/MaxPrice, MinRating), and TEXT (TextMatch against\n    // TextField) — Redis applies them all in front of the KNN.\n    minPrice, maxPrice := 50.0, 200.0\n    candidates, _ := recommender.CandidateRetrieve(ctx, queryVec, rec.RetrieveOptions{\n        FilterOptions: rec.FilterOptions{\n            Category:    \"outerwear\",\n            InStockOnly: true,\n            MinPrice:    &minPrice,\n            MaxPrice:    &maxPrice,\n            TextMatch:   \"waterproof\", // TEXT pre-filter on @description\n        },\n        K: 10,\n    })\n\n    // Record a click — updates the user's session vector and category\n    // affinity atomically; the next call to CandidateRetrieve sees it.\n    _, _ = recommender.RecordClick(ctx, \"alice\", \"p001\", nil)\n\n    // Pull user features so the next retrieval can blend the session\n    // vector into the query and apply the category-affinity re-rank.\n    features, _ := recommender.GetUserFeatures(ctx, \"alice\")\n    candidates, _ = recommender.CandidateRetrieve(ctx, queryVec, rec.RetrieveOptions{\n        FilterOptions: rec.FilterOptions{Category: \"outerwear\", InStockOnly: true},\n        K:             10,\n        SessionVec:    features.SessionVec,\n        SessionWeight: 0.3,\n    })\n    candidates = recommender.Rerank(candidates, features, 0.15)\n\n    // Hot embedding refresh — overwrite the vector for one product;\n    // the HNSW index reflects the change on the next FT.SEARCH.\n    newVec, _ := embedder.EncodeOne(ctx, \"heavy-duty arctic expedition parka\")\n    _ = recommender.RefreshEmbedding(ctx, \"p001\", newVec)\n}","section_id":"the-recommender-helper"},{"id":"data-model-ex0","language":"text","code":"product:p001\n  name=Alpine down parka\n  description=Heavyweight 800-fill goose down parka...\n  category=outerwear\n  brand=northpeak\n  price=289.0\n  rating=4.7\n  in_stock=true\n  embedding=<384 × float32 little-endian bytes>","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"FT.CREATE recommend:idx\n  ON HASH PREFIX 1 product:\n  SCHEMA\n    name        TEXT\n    description TEXT\n    category    TAG\n    brand       TAG\n    in_stock    TAG\n    price       NUMERIC SORTABLE\n    rating      NUMERIC SORTABLE\n    embedding   VECTOR HNSW 6 TYPE FLOAT32 DIM 384 DISTANCE_METRIC COSINE","section_id":"data-model"},{"id":"data-model-ex2","language":"text","code":"user:alice:features\n  session_vec=<384 × float32 little-endian bytes>\n  aff:outerwear=2.0\n  aff:footwear=1.0\n  last_clicked_id=p015\n  last_clicked_category=footwear\n  clicks=3","section_id":"data-model"},{"id":"the-query-ex0","language":"text","code":"FT.SEARCH recommend:idx\n  \"(@category:{outerwear} @in_stock:{true} @price:[50 200])\n     =>[KNN 10 @embedding $vec AS vector_score]\"\n  PARAMS 2 vec <384-float32-bytes>\n  SORTBY vector_score\n  RETURN 8 name description category brand price rating in_stock vector_score\n  DIALECT 2","section_id":"the-query"},{"id":"binary-fields-with-go-redis-v9-ex0","language":"go","code":"import \"github.com/redis/go-redis/v9\"\n\nrdb := redis.NewClient(&redis.Options{\n    Addr:     \"localhost:6379\",\n    Protocol: 2, // RESP2: go-redis marks FT.SEARCH RESP3 unstable.\n})\n\n// Mixed types in one HSET. Strings stay strings; the byte slice gets\n// stored verbatim and round-trips through HGET as a Go string (the\n// recommender re-interprets it as float32 bytes).\nrdb.HSet(ctx, \"product:p001\", map[string]any{\n    \"name\":      \"Alpine down parka\",\n    \"price\":     \"289.0\",\n    \"embedding\": rec.FloatsToBytes(vector), // []byte\n})","section_id":"binary-fields-with-go-redis-v9"},{"id":"the-catalog-builder-ex0","language":"go","code":"// In package recommendationengine:\nvar CatalogSeed = []Product{\n    {ID: \"p001\", Name: \"Alpine down parka\",\n        Description: \"Heavyweight 800-fill goose down parka...\",\n        Category:    \"outerwear\", Brand: \"northpeak\",\n        Price: 289.00, InStock: true, Rating: 4.7},\n    // ... rest of the catalog ...\n}\n\n// BuildCatalog runs the embedding model over each product and writes\n// catalog.json. The demo server reads that file at startup.\nembedder, _ := NewLocalEmbedder(ctx, \"\", \"\")\n_ = BuildCatalog(ctx, embedder, \"catalog.json\")","section_id":"the-catalog-builder"},{"id":"run-the-demo-locally-ex0","language":"bash","code":"git clone https://github.com/redis/docs.git\n    cd docs/content/develop/use-cases/recommendation-engine/go","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex1","language":"bash","code":"go mod download","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex2","language":"bash","code":"go run ./cmd/build_catalog","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex3","language":"bash","code":"go run ./cmd/demo_server --port 8084","section_id":"run-the-demo-locally"}]}
{"schema_version":2,"id":"develop/use-cases/recommendation-engine","title":"Redis recommendation engine","url":"https://redis.io/docs/latest/develop/use-cases/recommendation-engine/","summary":"Serve personalized recommendations under tight latency budgets by combining vector similarity with structured filters in a single Redis call.","tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-17T10:28:27+01:00","children":[{"id":"develop/use-cases/recommendation-engine/redis-py","summary":"Build a Redis-backed recommendation engine in Python with redis-py and sentence-transformers","title":"Redis recommendation engine with redis-py","url":"https://redis.io/docs/latest/develop/use-cases/recommendation-engine/redis-py/"},{"id":"develop/use-cases/recommendation-engine/nodejs","summary":"Build a Redis-backed recommendation engine in Node.js with node-redis and @xenova/transformers","title":"Redis recommendation engine with node-redis","url":"https://redis.io/docs/latest/develop/use-cases/recommendation-engine/nodejs/"},{"id":"develop/use-cases/recommendation-engine/go","summary":"Build a Redis-backed recommendation engine in Go with go-redis and Hugot","title":"Redis recommendation engine with go-redis","url":"https://redis.io/docs/latest/develop/use-cases/recommendation-engine/go/"},{"id":"develop/use-cases/recommendation-engine/rust","summary":"Build a Redis-backed recommendation engine in Rust with redis-rs and fastembed","title":"Redis recommendation engine with redis-rs","url":"https://redis.io/docs/latest/develop/use-cases/recommendation-engine/rust/"},{"id":"develop/use-cases/recommendation-engine/dotnet","summary":"Build a Redis-backed recommendation engine in C# with NRedisStack and SmartComponents.LocalEmbeddings","title":"Redis recommendation engine with NRedisStack","url":"https://redis.io/docs/latest/develop/use-cases/recommendation-engine/dotnet/"},{"id":"develop/use-cases/recommendation-engine/java-jedis","summary":"Build a Redis-backed recommendation engine in Java with Jedis and DJL (HuggingFace tokenizers + ONNX Runtime)","title":"Redis recommendation engine with Jedis","url":"https://redis.io/docs/latest/develop/use-cases/recommendation-engine/java-jedis/"},{"id":"develop/use-cases/recommendation-engine/java-lettuce","summary":"Build a Redis-backed recommendation engine in Java with Lettuce and DJL (HuggingFace tokenizers + ONNX Runtime)","title":"Redis recommendation engine with Lettuce","url":"https://redis.io/docs/latest/develop/use-cases/recommendation-engine/java-lettuce/"},{"id":"develop/use-cases/recommendation-engine/php","summary":"Build a Redis-backed recommendation engine in PHP with Predis and TransformersPHP","title":"Redis recommendation engine with Predis","url":"https://redis.io/docs/latest/develop/use-cases/recommendation-engine/php/"},{"id":"develop/use-cases/recommendation-engine/ruby","summary":"Build a Redis-backed recommendation engine in Ruby with redis-rb and informers","title":"Redis recommendation engine with redis-rb","url":"https://redis.io/docs/latest/develop/use-cases/recommendation-engine/ruby/"}],"page_type":"content","content_hash":"2a3f00d4bd648a4bfd394cba49c792bedfeb681d18c219a805b10beab5a8362a","sections":[{"id":"when-to-use-redis-as-a-recommendation-engine","title":"When to use Redis as a recommendation engine","role":"overview","text":"Use Redis as the serving layer for a recommendation engine when you need to combine embedding similarity with structured attribute filters under a tight latency budget — typically a multi-stage pipeline of candidate retrieval, scoring, and re-ranking that has to return in tens of milliseconds, while incorporating real-time session signals that can't wait for a batch cycle."},{"id":"why-the-problem-is-hard","title":"Why the problem is hard","role":"content","text":"Recommendation pipelines have to produce a ranked list in roughly 200 ms end to end, and the serving phase needs simultaneous access to item embeddings, item metadata, user features, and recent interaction history. Some of the obvious workarounds have real drawbacks:\n\n-   **A relational database** can store the embeddings, metadata, and user features, but it cannot\n    perform sub-millisecond approximate nearest-neighbour search at serving-time concurrency. KNN\n    over millions of vectors is not what a row store is built for.\n-   **A dedicated vector database** handles KNN with metadata filtering, but adds a separate\n    scaling and monitoring surface, another network hop, and a synchronization layer between item\n    metadata in the primary store and embeddings in the vector store. Most are also optimized for\n    batch-loaded read-heavy workloads, not sub-millisecond writes interleaved with reads when\n    session signals need to update user features mid-request.\n-   **Pre-computing recommendations offline** removes the serving-time cost but cannot react to\n    in-session behavior — the user clicks something, and the next request still ranks against the\n    yesterday's offline batch.\n\nA workable serving layer needs vector KNN, structured pre-filters, and per-user feature updates that take effect within the request path — without standing up a separate vector store and synchronizing it with the primary."},{"id":"what-you-can-expect-from-a-redis-solution","title":"What you can expect from a Redis solution","role":"content","text":"You can:\n\n-   Return personalized recommendations with P99 latency under 10 ms for the retrieval stage at\n    peak concurrency.\n-   Combine embedding similarity with TAG, NUMERIC, and TEXT pre-filters in a single\n    [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call, with no cross-store joins.\n-   Incorporate real-time session signals (clicks, dwell time, cart adds) into the *next*\n    recommendation without waiting for a batch pipeline — a session vector written with\n    [`HSET`](https://redis.io/docs/latest/commands/hset) is visible to the very next read of the user\n    features hash, which the application passes through to\n    [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) as the query vector.\n-   Refresh item embeddings from the offline training pipeline without serving downtime by\n    overwriting the vector field with [`HSET`](https://redis.io/docs/latest/commands/hset); the HNSW index\n    reflects the new vector on the next query. For schema changes (different dimensionality or\n    model), the same playbook scales out to a dual-field write followed by\n    [`FT.ALTER`](https://redis.io/docs/latest/commands/ft.alter) or a swap of the indexed key prefix.\n-   Co-locate item embeddings, item metadata, user features, and short-term interaction state in\n    one serving layer, eliminating cross-store hops on the request path.\n-   Run the recommendation index on the same Redis instance already in the stack handling cache,\n    sessions, or rate limiting — no additional infrastructure."},{"id":"how-redis-supports-the-solution","title":"How Redis supports the solution","role":"content","text":"In practice, each item is a single [Hash](https://redis.io/docs/latest/develop/data-types/hashes) or\n[JSON](https://redis.io/docs/latest/develop/data-types/json) document holding both its embedding vector and\nits structured metadata — category, brand, price, in-stock flag, popularity score. A single\n[Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) index covers the vector field and\nevery filter field, so one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call can do KNN\nretrieval over millions of items with a TAG, NUMERIC, or TEXT pre-filter applied in the same\npass. Per-user features live in a separate hash that the application updates atomically with\n[`HSET`](https://redis.io/docs/latest/commands/hset) and\n[`HINCRBYFLOAT`](https://redis.io/docs/latest/commands/hincrbyfloat); the next time the application reads\nthat hash to build a query, it sees the click, so session signals feed scoring without any batch\ncycle.\n\nRedis provides the following features that make it a good fit for a recommendation serving layer:\n\n-   [Hashes](https://redis.io/docs/latest/develop/data-types/hashes) and\n    [JSON](https://redis.io/docs/latest/develop/data-types/json) store each item's embedding and structured\n    metadata in a single record, so retrieval reads everything the scorer needs in one round trip.\n-   [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) with\n    [HNSW vector indexes](https://redis.io/docs/latest/develop/ai/search-and-query/vectors) performs\n    approximate KNN over the embedding field at HNSW speeds, and the same\n    [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call applies TAG / NUMERIC / TEXT filters\n    to constrain the candidate set in one pass.\n-   [`FT.HYBRID`](https://redis.io/docs/latest/commands/ft.hybrid) (Redis 8.4+) combines text and vector\n    similarity into a single ranked result via Reciprocal Rank Fusion or linear combination, for\n    queries where lexical match should contribute to the score, not just gate the candidate set.\n-   [`HSET`](https://redis.io/docs/latest/commands/hset) and\n    [`HINCRBYFLOAT`](https://redis.io/docs/latest/commands/hincrbyfloat) updates to user feature hashes are\n    atomic, so the next time the application reads that hash to build a query it sees the click\n    — session signals feed scoring without any batch cycle or cache invalidation.\n-   Overwriting the vector field with [`HSET`](https://redis.io/docs/latest/commands/hset) re-trains the\n    HNSW entry in place; for embedding-model changes,\n    [`FT.ALTER`](https://redis.io/docs/latest/commands/ft.alter) and dual-field write patterns let you swap\n    in a new model without taking the serving index offline.\n-   Sub-millisecond reads and writes from memory let the recommendation index ride on the same\n    Redis instance already handling cache, sessions, or rate limiting at zero marginal cost."},{"id":"ecosystem","title":"Ecosystem","role":"content","text":"The following libraries and frameworks build on Redis Search for recommendation workloads:\n\n-   **Python**: [RedisVL](https://github.com/redis/redis-vl-python) for a high-level vector-search\n    client, and the\n    [LangChain Redis integration](https://python.langchain.com/docs/integrations/vectorstores/redis/)\n    for embedding-driven retrieval chains.\n-   **Node.js**: [`redis-om-node`](https://github.com/redis/redis-om-node) for object-mapped\n    Hash/JSON documents and Redis Search queries.\n-   **Go**: [`go-redis`](https://github.com/redis/go-redis) with first-class Redis Search support\n    for FT.SEARCH KNN and structured filters.\n-   **ML platforms**: [NVIDIA Merlin](https://github.com/NVIDIA-Merlin) for an online feature store\n    and retrieval layer that uses Redis as the serving substrate."},{"id":"code-examples-to-build-your-own-redis-recommendation-engine","title":"Code examples to build your own Redis recommendation engine","role":"example","text":"The following guides show how to build a small Redis-backed product recommendation service. Each guide includes a runnable interactive demo that lets you embed a query, retrieve candidates with KNN, filter by category and price, feed clicks back as a session signal, and watch the next recommendation incorporate them immediately.\n\n* [redis-py (Python)](https://redis.io/docs/latest/develop/use-cases/recommendation-engine/redis-py)\n* [node-redis (Node.js)](https://redis.io/docs/latest/develop/use-cases/recommendation-engine/nodejs)\n* [go-redis (Go)](https://redis.io/docs/latest/develop/use-cases/recommendation-engine/go)\n* [redis-rs (Rust)](https://redis.io/docs/latest/develop/use-cases/recommendation-engine/rust)\n* [NRedisStack (C#)](https://redis.io/docs/latest/develop/use-cases/recommendation-engine/dotnet)\n* [Jedis (Java)](https://redis.io/docs/latest/develop/use-cases/recommendation-engine/java-jedis)\n* [Lettuce (Java)](https://redis.io/docs/latest/develop/use-cases/recommendation-engine/java-lettuce)\n* [Predis (PHP)](https://redis.io/docs/latest/develop/use-cases/recommendation-engine/php)\n* [redis-rb (Ruby)](https://redis.io/docs/latest/develop/use-cases/recommendation-engine/ruby)"}],"examples":[]}
{"schema_version":2,"id":"develop/use-cases/recommendation-engine/java-jedis","title":"Redis recommendation engine with Jedis","url":"https://redis.io/docs/latest/develop/use-cases/recommendation-engine/java-jedis/","summary":"Build a Redis-backed recommendation engine in Java with Jedis and DJL (HuggingFace tokenizers + ONNX Runtime)","aliases":["/develop/use-cases/recommendation-engine/java","/develop/use-cases/recommendation-engine/jedis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-17T10:28:27+01:00","children":[],"page_type":"content","content_hash":"970406b026e60110853bed4f866c12e58f62b5b2fe2dc7dd26c0770da80c0292","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a small Redis-backed product recommendation service in Java with the [Jedis](https://redis.io/docs/latest/develop/clients/jedis) client library and the [Deep Java Library](https://djl.ai/) (DJL) with its HuggingFace tokenizer integration and the ONNX Runtime inference engine. It includes a local web server built on the JDK's `com.sun.net.httpserver` so you can embed a natural-language query, run a KNN retrieval with structured pre-filters in one round trip, feed clicks back as a session signal, and watch the next recommendation incorporate them immediately."},{"id":"overview","title":"Overview","role":"overview","text":"Each product is stored as a single Redis [Hash](https://redis.io/docs/latest/develop/data-types/hashes) at `product:<id>`. The hash holds the structured metadata (name, description, category, brand, price, rating, in-stock flag) alongside the raw `float32` bytes of a 384-dimensional embedding. A single [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) index covers every field, so one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call with a `KNN` clause does the vector similarity *and* the TAG / NUMERIC / TEXT pre-filtering in the same pass &mdash; no cross-store joins.\n\nPer-user state lives in `user:<id>:features`: a session vector written as an exponentially weighted average of recently-clicked item embeddings, plus per-category affinity counters incremented atomically with [`HINCRBYFLOAT`](https://redis.io/docs/latest/commands/hincrbyfloat). [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) does *not* read that hash directly; instead, the application reads it on the next request and passes the session vector to `FT.SEARCH` as the query parameter. The two-step is what lets a click feed the very next recommendation without a batch cycle or cache invalidation.\n\nThat gives you:\n\n* A single round trip for retrieval &mdash; vector KNN + structured filters in one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search).\n* Sub-millisecond hot path once the query is embedded; embedding the query is the bottleneck, and that's a model-side cost, not a Redis one.\n* Real-time session signals &mdash; a click writes a new session vector and bumps an affinity counter; the next query reads them and folds them in.\n* No-downtime embedding refresh &mdash; [`HSET`](https://redis.io/docs/latest/commands/hset) on the vector field, and the HNSW index reflects the change on the next query."},{"id":"how-it-works","title":"How it works","role":"content","text":"There are two distinct paths: a **query path** runs every time the application wants a recommendation, and a **click path** runs every time the user interacts with a product."},{"id":"query-path-per-recommendation-request","title":"Query path (per recommendation request)","role":"content","text":"1. The application calls `embedder.encodeOne(queryText)` to turn a natural-language query into a 384-dimensional `float32` vector. DJL's `Predictor` runs the [HuggingFace tokenizer](https://github.com/deepjavalibrary/djl/tree/master/extensions/tokenizers) and ONNX Runtime inference end-to-end.\n2. The application reads the user's session vector and affinities from the user features hash. If a session vector exists, it gets blended into the query vector with a tunable weight, so the user's recent clicks pull retrieval toward what they've been engaging with.\n3. `recommender.candidateRetrieve(queryVec, opts)` runs [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) with a pre-filter clause built from the request's TAG / NUMERIC / TEXT inputs, followed by a `KNN k @embedding $vec` clause. Redis returns up to `k` candidates with the cosine distance to the query (lower is closer).\n4. `recommender.rerank(candidates, userFeatures, affinityWeight)` subtracts a log-scaled per-category affinity bonus from each candidate's distance and re-sorts the list closest-first. The log scaling keeps repeated clicks from running away with the ranking."},{"id":"click-path-per-user-interaction","title":"Click path (per user interaction)","role":"content","text":"When the user clicks a product, `recommender.recordClick(userId, productId, ewmaAlpha, affinityStep)` does the following:\n\n1. Reads the clicked item's embedding from its hash.\n2. Reads the user's previous session vector from the user features hash, blends the new click in via an exponentially weighted moving average, and writes the new session vector back with [`HSET`](https://redis.io/docs/latest/commands/hset). This is a read-modify-write &mdash; atomic against any single write but not against a concurrent click for the same user. In practice, per-user click streams don't generate the contention to make this matter, and if a deployment does, the read and write can be wrapped in [`WATCH/MULTI/EXEC`](https://redis.io/docs/latest/commands/multi) or a small Lua script.\n3. Bumps the per-category affinity counter with [`HINCRBYFLOAT`](https://redis.io/docs/latest/commands/hincrbyfloat) &mdash; atomic, no read needed &mdash; and the click count with [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby).\n\nThe next query path picks both changes up the next time it reads the user features hash.\n\nRefreshing an item's embedding follows a similar shape: encode the new text, write the vector bytes back with [`HSET`](https://redis.io/docs/latest/commands/hset), and the HNSW index reflects the change on the next query without a rebuild."},{"id":"the-recommender-helper","title":"The recommender helper","role":"content","text":"The `Recommender` class wraps the Redis Search index and the retrieval flow\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/recommendation-engine/java-jedis/Recommender.java)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each product is one Redis Hash. The vector field is raw little-endian `float32` bytes &mdash; no JSON wrapping &mdash; because the Redis Search vector encoding expects exactly that. Jedis 6's typed `hset(String, Map<String, String>)` overload would UTF-8-encode the bytes and corrupt the vector, so the helper uses the `byte[]`-typed `hset(byte[], byte[], byte[])` overload for the embedding field and the String-typed overload for everything else, in the same pipelined batch.\n\n[code example]\n\nThe Redis Search index schema treats every field as queryable in its natural type:\n\n[code example]\n\nPer-user state is a separate hash. The session vector is stored as raw `float32` bytes the same way; affinity counters are stored as plain numeric strings, one field per category, prefixed with `aff:` so they don't collide with anything else.\n\n[code example]"},{"id":"the-query","title":"The query","role":"content","text":"The KNN clause is a hybrid query: a pre-filter expression in parentheses, then `=>[KNN k @embedding $vec]`. With `DIALECT 2`, Redis applies the filter first and then KNN-ranks only the matching documents.\n\n[code example]\n\nWhen there's no filter, the pre-filter clause is just `(*)`. `vector_score` is the cosine *distance* (0 means identical, 2 means opposite), so the result is sorted ascending and the top row is the closest candidate to the query.\n\nThe Jedis equivalent uses `JedisPooled.ftSearch(String, Query)` with the binary parameter wired through `Query.addParam(\"vec\", floatsToBytes(vec))`:\n\n[code example]"},{"id":"the-local-embedder","title":"The local embedder","role":"content","text":"The `LocalEmbedder` class wraps DJL's HuggingFace text-embedding pipeline so the rest of the helper can hand it a string and get back a unit-normalised `float[]`\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/recommendation-engine/java-jedis/LocalEmbedder.java)):\n\n[code example]\n\nThe model URL routes through DJL's model zoo. The `ai.djl.huggingface.onnxruntime` group ID pulls a pre-converted ONNX bundle (model weights plus `tokenizer.json`) so we don't need a separate conversion step. `optEngine(\"OnnxRuntime\")` pins the inference engine to ONNX Runtime, which means the demo only drags in the small ONNX Runtime native library rather than the much heavier PyTorch native library that `ai.djl.huggingface.pytorch` would require.\n\n`Predictor` is *not* thread-safe; the wrapper guards calls with `synchronized` because the JDK HttpServer dispatches each request on a worker thread. For higher concurrency in production you'd hold a pool of `Predictor` instances backed by one `ZooModel`."},{"id":"the-catalogue-builder","title":"The catalogue builder","role":"content","text":"Item vectors are pre-computed once and stored in `catalog.json` so the demo server can boot quickly. `BuildCatalog` is the reference for how to do that &mdash; and is the program you'd adapt for a real catalogue ingestion pipeline\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/recommendation-engine/java-jedis/BuildCatalog.java)):\n\n[code example]\n\nIn production the equivalent lives in an offline pipeline: embed once on catalogue updates and ship the vectors into Redis with [`HSET`](https://redis.io/docs/latest/commands/hset). The serving tier still embeds the *query* on each request, but that's usually fronted by a dedicated model server or batched at the API gateway rather than co-located with the data tier as it is in this demo.\n\nThe shared `catalog.json` wire format (model name, dim, list of products with base64-encoded `float32` LE bytes for the vector) is identical to what the Python, Node, and Go ports produce, so you can re-use any port's catalog with the Java demo as long as the embedding model matches."},{"id":"the-interactive-demo","title":"The interactive demo","role":"content","text":"`DemoServer` runs `com.sun.net.httpserver.HttpServer` with a 16-thread executor and one demo user (`demo`). The HTML page lets you:\n\n* Type a natural-language query and toggle filters: TAG (category, brand, in-stock), NUMERIC (price range, rating), and TEXT (the **Description contains** field, a phrase pre-filter against the `description` text index).\n* Toggle session blending and category-affinity re-ranking independently to see what each layer contributes.\n* Click any product card to record a click into the session. The page re-renders the user features panel immediately &mdash; the click wrote to the user features hash, and the next search reads that hash to fold the update in.\n* Refresh a single product's embedding with new text and watch the ranking change on the next query.\n\nThe server holds one `LocalEmbedder` and one `Recommender` for the lifetime of the process. Endpoints:\n\n| Endpoint                  | What it does                                                                |\n|---------------------------|-----------------------------------------------------------------------------|\n| `GET  /state`             | Index info, user features, full catalogue listing.                          |\n| `POST /search`            | Embed the query, run `FT.SEARCH` with filters + KNN, optionally re-rank.    |\n| `POST /click`             | Record a click for the demo user: update session vector and affinity.       |\n| `POST /reset-user`        | Drop the user features hash.                                                |\n| `POST /reset-index`       | Drop the index and documents and re-seed from `catalog.json`.               |\n| `POST /refresh-embedding` | Embed new text and overwrite one product's vector with `HSET`.              |"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before running the demo, make sure that:\n\n* Redis 7.0 or later with the Redis Search module is running and accessible. By default the demo connects to `localhost:6379`. [Redis Stack](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack) or [Redis 8 with Search](https://redis.io/docs/latest/develop/ai/search-and-query) both work.\n* JDK 17 or later is installed (the demo's inline HTML uses text blocks, which require JDK 15+; 17+ keeps the demo on a current LTS).\n* [Maven](https://maven.apache.org/) 3.9 or later for dependency resolution. DJL pulls in a couple of dozen transitive jars, so a manual `javac -cp ...` build is impractical; the `pom.xml` shipped next to the source files lets Maven handle that.\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`."},{"id":"run-the-demo-locally","title":"Run the demo locally","role":"content","text":"1.  Clone the [`redis/docs`](https://github.com/redis/docs) repository and change into the example\n    directory:\n\n    [code example]\n\n2.  Build the project. The first build downloads the dependency graph (Jedis, DJL API, the\n    HuggingFace tokenizer extension, the ONNX Runtime engine, and Gson):\n\n    [code example]\n\n3.  Generate the catalogue with pre-computed embeddings. The first run downloads the\n    `all-MiniLM-L6-v2` ONNX bundle (~80&nbsp;MB) into the local DJL cache\n    (`~/.djl.ai/cache/`):\n\n    [code example]\n\n4.  Start the demo server:\n\n    [code example]\n\n    Override the defaults with `-Dexec.args=\"--port 8090 --redis-host 127.0.0.1\"`.\n\n5.  Open <http://localhost:8084> and try some queries:\n\n    * **\"insulated down jacket for cold weather\"** &mdash; filtered to `outerwear`, in-stock only.\n    * **\"comfortable shoes for trail running\"** &mdash; filtered to `footwear`.\n    * Add **Description contains: waterproof** to either query above to see a TEXT pre-filter\n      combine with the KNN.\n    * Click a couple of products to seed a session, then re-run the same query\n      with **Blend session vector into query** on and watch the ranking shift.\n    * Use **Refresh embedding** to change a product's vector &mdash; for example,\n      change the Alpine down parka's text to \"heavy duty arctic expedition parka\n      with hood\" and re-run the jacket query to see the result move.\n\nThe server is read/write against your local Redis. The default index name is `recommend:idx` and product keys live under `product:`. Pass `--no-reset` to keep an existing index across restarts, or `--index-name` / `--key-prefix` to point the demo at a different prefix entirely."}],"examples":[{"id":"the-recommender-helper-ex0","language":"java","code":"import redis.clients.jedis.JedisPooled;\n\n// JedisPooled is a pool-backed UnifiedJedis: it exposes both the\n// FT.* search commands and a pipelined() API in one client.\nJedisPooled jedis = new JedisPooled(\"localhost\", 6379);\nRecommender recommender = new Recommender(jedis, \"recommend:idx\",\n        \"product:\", \"user:\", 384);\n\nLocalEmbedder embedder = new LocalEmbedder();   // all-MiniLM-L6-v2 via DJL + ONNX Runtime\n\n// One-time index setup (idempotent).\nrecommender.createIndex();\n\n// Embed the natural-language query.\nfloat[] queryVec = embedder.encodeOne(\"warm waterproof jacket for hiking\");\n\n// Retrieval: KNN with structured pre-filter in one round trip.\n// Filters combine TAG (category, brand, inStockOnly), NUMERIC\n// (price range, rating), and TEXT (textMatch against textField) —\n// Redis applies them all in front of the KNN.\nRecommender.RetrieveOptions opts = new Recommender.RetrieveOptions();\nopts.category = \"outerwear\";\nopts.inStockOnly = true;\nopts.minPrice = 50.0;\nopts.maxPrice = 200.0;\nopts.textMatch = \"waterproof\";   // TEXT pre-filter on @description\nopts.k = 10;\nList<Recommender.Candidate> candidates = recommender.candidateRetrieve(queryVec, opts);\n\n// Record a click — updates the user's session vector and category\n// affinity atomically; the next call to candidateRetrieve sees it.\nrecommender.recordClick(\"alice\", \"p001\", 0.4, 1.0);\n\n// Pull user features so the next retrieval can blend the session\n// vector into the query and apply the category-affinity re-rank.\nRecommender.UserFeatures features = recommender.getUserFeatures(\"alice\");\nopts.sessionVec = features.sessionVec;\nopts.sessionWeight = 0.3;\ncandidates = recommender.candidateRetrieve(queryVec, opts);\ncandidates = recommender.rerank(candidates, features, 0.15);\n\n// Hot embedding refresh — overwrite the vector for one product; the\n// HNSW index reflects the change on the next FT.SEARCH.\nfloat[] newVector = embedder.encodeOne(\"heavy-duty arctic expedition parka\");\nrecommender.refreshEmbedding(\"p001\", newVector);","section_id":"the-recommender-helper"},{"id":"data-model-ex0","language":"text","code":"product:p001\n  name=Alpine down parka\n  description=Heavyweight 800-fill goose down parka...\n  category=outerwear\n  brand=northpeak\n  price=289.0\n  rating=4.7\n  in_stock=true\n  embedding=<384 x float32 little-endian bytes>","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"FT.CREATE recommend:idx\n  ON HASH PREFIX 1 product:\n  SCHEMA\n    name        TEXT\n    description TEXT\n    category    TAG\n    brand       TAG\n    in_stock    TAG\n    price       NUMERIC SORTABLE\n    rating      NUMERIC SORTABLE\n    embedding   VECTOR HNSW 6 TYPE FLOAT32 DIM 384 DISTANCE_METRIC COSINE","section_id":"data-model"},{"id":"data-model-ex2","language":"text","code":"user:alice:features\n  session_vec=<384 x float32 little-endian bytes>\n  aff:outerwear=2.0\n  aff:footwear=1.0\n  last_clicked_id=p015\n  last_clicked_category=footwear\n  clicks=3","section_id":"data-model"},{"id":"the-query-ex0","language":"text","code":"FT.SEARCH recommend:idx\n  \"(@category:{outerwear} @in_stock:{true} @price:[50.0 200.0])\n     =>[KNN 10 @embedding $vec AS vector_score]\"\n  PARAMS 2 vec <384-float32-bytes>\n  SORTBY vector_score\n  RETURN 8 name description category brand price rating in_stock vector_score\n  DIALECT 2","section_id":"the-query"},{"id":"the-query-ex1","language":"java","code":"String knn = filterClause + \"=>[KNN \" + k + \" @embedding $vec AS vector_score]\";\nQuery q = new Query(knn)\n        .addParam(\"vec\", Recommender.floatsToBytes(queryVec))\n        .returnFields(\n                \"name\", \"description\", \"category\", \"brand\",\n                \"price\", \"rating\", \"in_stock\", \"vector_score\")\n        .setSortBy(\"vector_score\", true)\n        .limit(0, k)\n        .dialect(2);\nSearchResult result = jedis.ftSearch(\"recommend:idx\", q);","section_id":"the-query"},{"id":"the-local-embedder-ex0","language":"java","code":"Criteria<String, float[]> criteria = Criteria.builder()\n        .setTypes(String.class, float[].class)\n        .optModelUrls(\"djl://ai.djl.huggingface.onnxruntime/sentence-transformers/all-MiniLM-L6-v2\")\n        .optEngine(\"OnnxRuntime\")\n        .optTranslatorFactory(new TextEmbeddingTranslatorFactory())\n        .optProgress(new ProgressBar())\n        .build();\nZooModel<String, float[]> model = criteria.loadModel();\nPredictor<String, float[]> predictor = model.newPredictor();\nfloat[] vector = predictor.predict(\"warm waterproof jacket for hiking\");","section_id":"the-local-embedder"},{"id":"the-catalogue-builder-ex0","language":"java","code":"List<CatalogSeed.Seed> seeds = CatalogSeed.all();\nList<String> texts = new ArrayList<>();\nfor (CatalogSeed.Seed s : seeds) {\n    texts.add(CatalogSeed.embedTextFor(s));    // \"<name>. <description>\"\n}\ntry (LocalEmbedder embedder = new LocalEmbedder()) {\n    float[][] vectors = embedder.encodeMany(texts);\n    Catalog.write(Path.of(\"catalog.json\"),\n            embedder.getModelName(), vectors[0].length, seeds, vectors);\n}","section_id":"the-catalogue-builder"},{"id":"run-the-demo-locally-ex0","language":"bash","code":"git clone https://github.com/redis/docs.git\n    cd docs/content/develop/use-cases/recommendation-engine/java-jedis","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex1","language":"bash","code":"mvn -DskipTests package","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex2","language":"bash","code":"mvn -DskipTests exec:java -Dexec.mainClass=BuildCatalog","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex3","language":"bash","code":"mvn -DskipTests exec:java -Dexec.mainClass=DemoServer","section_id":"run-the-demo-locally"}]}
{"schema_version":2,"id":"develop/use-cases/recommendation-engine/java-lettuce","title":"Redis recommendation engine with Lettuce","url":"https://redis.io/docs/latest/develop/use-cases/recommendation-engine/java-lettuce/","summary":"Build a Redis-backed recommendation engine in Java with Lettuce and DJL (HuggingFace tokenizers + ONNX Runtime)","aliases":["/develop/use-cases/recommendation-engine/lettuce"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-17T10:28:27+01:00","children":[],"page_type":"content","content_hash":"56feb3512bb39ca65e87c1033b38e7292f6d697aadaef6d2bd0778299e677459","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a small Redis-backed product recommendation service in Java with the [Lettuce](https://redis.io/docs/latest/develop/clients/lettuce) client library and the [Deep Java Library](https://djl.ai/) (DJL) with its HuggingFace tokenizer integration and the ONNX Runtime inference engine. It includes a local web server built on the JDK's `com.sun.net.httpserver` so you can embed a natural-language query, run a KNN retrieval with structured pre-filters in one round trip, feed clicks back as a session signal, and watch the next recommendation incorporate them immediately."},{"id":"overview","title":"Overview","role":"overview","text":"Each product is stored as a single Redis [Hash](https://redis.io/docs/latest/develop/data-types/hashes) at `product:<id>`. The hash holds the structured metadata (name, description, category, brand, price, rating, in-stock flag) alongside the raw `float32` bytes of a 384-dimensional embedding. A single [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) index covers every field, so one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call with a `KNN` clause does the vector similarity *and* the TAG / NUMERIC / TEXT pre-filtering in the same pass &mdash; no cross-store joins.\n\nPer-user state lives in `user:<id>:features`: a session vector written as an exponentially weighted average of recently-clicked item embeddings, plus per-category affinity counters incremented atomically with [`HINCRBYFLOAT`](https://redis.io/docs/latest/commands/hincrbyfloat). [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) does *not* read that hash directly; instead, the application reads it on the next request and passes the session vector to `FT.SEARCH` as the query parameter. The two-step is what lets a click feed the very next recommendation without a batch cycle or cache invalidation.\n\nThat gives you:\n\n* A single round trip for retrieval &mdash; vector KNN + structured filters in one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search).\n* Sub-millisecond hot path once the query is embedded; embedding the query is the bottleneck, and that's a model-side cost, not a Redis one.\n* Real-time session signals &mdash; a click writes a new session vector and bumps an affinity counter; the next query reads them and folds them in.\n* No-downtime embedding refresh &mdash; [`HSET`](https://redis.io/docs/latest/commands/hset) on the vector field, and the HNSW index reflects the change on the next query."},{"id":"how-it-works","title":"How it works","role":"content","text":"There are two distinct paths: a **query path** runs every time the application wants a recommendation, and a **click path** runs every time the user interacts with a product."},{"id":"query-path-per-recommendation-request","title":"Query path (per recommendation request)","role":"content","text":"1. The application calls `embedder.encodeOne(queryText)` to turn a natural-language query into a 384-dimensional `float32` vector. DJL's `Predictor` runs the [HuggingFace tokenizer](https://github.com/deepjavalibrary/djl/tree/master/extensions/tokenizers) and ONNX Runtime inference end-to-end.\n2. The application reads the user's session vector and affinities from the user features hash. If a session vector exists, it gets blended into the query vector with a tunable weight, so the user's recent clicks pull retrieval toward what they've been engaging with.\n3. `recommender.candidateRetrieve(queryVec, opts)` runs [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) with a pre-filter clause built from the request's TAG / NUMERIC / TEXT inputs, followed by a `KNN k @embedding $vec` clause. Redis returns up to `k` candidates with the cosine distance to the query (lower is closer).\n4. `recommender.rerank(candidates, userFeatures, affinityWeight)` subtracts a log-scaled per-category affinity bonus from each candidate's distance and re-sorts the list closest-first. The log scaling keeps repeated clicks from running away with the ranking."},{"id":"click-path-per-user-interaction","title":"Click path (per user interaction)","role":"content","text":"When the user clicks a product, `recommender.recordClick(userId, productId, ewmaAlpha, affinityStep)` does the following:\n\n1. Reads the clicked item's embedding from its hash.\n2. Reads the user's previous session vector from the user features hash, blends the new click in via an exponentially weighted moving average, and writes the new session vector back with [`HSET`](https://redis.io/docs/latest/commands/hset). This is a read-modify-write &mdash; atomic against any single write but not against a concurrent click for the same user. In practice, per-user click streams don't generate the contention to make this matter, and if a deployment does, the read and write can be wrapped in [`WATCH/MULTI/EXEC`](https://redis.io/docs/latest/commands/multi) or a small Lua script.\n3. Bumps the per-category affinity counter with [`HINCRBYFLOAT`](https://redis.io/docs/latest/commands/hincrbyfloat) &mdash; atomic, no read needed &mdash; and the click count with [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby).\n\nThe next query path picks both changes up the next time it reads the user features hash.\n\nRefreshing an item's embedding follows a similar shape: encode the new text, write the vector bytes back with [`HSET`](https://redis.io/docs/latest/commands/hset), and the HNSW index reflects the change on the next query without a rebuild."},{"id":"the-recommender-helper","title":"The recommender helper","role":"content","text":"The `Recommender` class wraps the Redis Search index and the retrieval flow\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/recommendation-engine/java-lettuce/Recommender.java)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each product is one Redis Hash. The vector field is raw little-endian `float32` bytes &mdash; no JSON wrapping &mdash; because the Redis Search vector encoding expects exactly that.\n\n[code example]\n\nThe Redis Search index schema treats every field as queryable in its natural type:\n\n[code example]\n\n> [!NOTE]\n> The other ports in this guide series set `description` to `WEIGHT 0.5` so phrase matches in the longer `description` field don't outweigh matches in the short `name` field. Lettuce 7's typed `TextFieldArgs.weight(long)` accepts only integer weights, so this port leaves `description` at its default weight of 1. The difference is only visible if you compare BM25-style TEXT relevance scores side by side; KNN retrieval and TEXT pre-filtering are unaffected.\n\nPer-user state is a separate hash. The session vector is stored as raw `float32` bytes the same way; affinity counters are stored as plain numeric strings, one field per category, prefixed with `aff:` so they don't collide with anything else.\n\n[code example]"},{"id":"the-query","title":"The query","role":"content","text":"The KNN clause is a hybrid query: a pre-filter expression in parentheses, then `=>[KNN k @embedding $vec]`. With `DIALECT 2`, Redis applies the filter first and then KNN-ranks only the matching documents.\n\n[code example]\n\nWhen there's no filter, the pre-filter clause is just `(*)`. `vector_score` is the cosine *distance* (0 means identical, 2 means opposite), so the result is sorted ascending and the top row is the closest candidate to the query.\n\nThe Lettuce equivalent uses the typed `ftSearch` method on the binary-codec connection's `RediSearchCommands`. The query expression is passed as the value type (`byte[]` on the binary connection, so UTF-8-encoded) and the binary `$vec` parameter goes through `SearchArgs.Builder.param(...)`:\n\n[code example]"},{"id":"lettuce-specifics-binary-fields-and-pipelining","title":"Lettuce specifics: binary fields and pipelining","role":"content","text":"Two things in the helper change shape relative to the [Jedis port](https://redis.io/docs/latest/develop/use-cases/recommendation-engine/java-jedis):"},{"id":"1-codec-choice-for-the-binary-embedding-field","title":"1. Codec choice for the binary embedding field","role":"content","text":"Lettuce's default `StringCodec` UTF-8-decodes every hash value, which would corrupt the raw `float32` bytes that the Redis Search vector field expects. Following the [Lettuce vector-search reference](https://redis.io/docs/latest/develop/clients/lettuce/vecsearch), the helper opens a *second* connection bound to a `<String, byte[]>` codec (built with `RedisCodec.of(StringCodec.UTF8, ByteArrayCodec.INSTANCE)`) and routes every command that reads or writes the `embedding` field &mdash; including `FT.SEARCH`, whose `$vec` parameter is raw bytes too &mdash; through that connection. The structured fields share the same hash and are written through the same binary connection as their UTF-8 bytes so Redis sees an identical wire format to what the Jedis port writes.\n\n[code example]"},{"id":"2-pipelining-with-setautoflushcommandsfalse","title":"2. Pipelining with `setAutoFlushCommands(false)`","role":"content","text":"Jedis pipelines via a dedicated `Pipeline` object you obtain with `client.pipelined()`. Lettuce instead drives pipelining at the *connection* level: turn auto-flush off, queue async commands, flush, then await the futures.\n\n[code example]\n\nThe toggle is connection-wide, so the helper owns its binary connection rather than borrowing a shared one &mdash; any other thread issuing commands on the same connection while auto-flush is off would be stalled until `flushCommands()` is called."},{"id":"a-note-on-ftinfo","title":"A note on `FT.INFO`","role":"content","text":"Lettuce 7's `RediSearchCommands` exposes typed wrappers for most `FT.*` commands, but `FT.INFO` isn't one of them. The helper dispatches the raw command via `connection.sync().dispatch(...)` with a `NestedMultiOutput` so it can parse the alternating-pair reply, the same approach the [streaming Lettuce port](https://redis.io/docs/latest/develop/use-cases/streaming/java-lettuce) uses for `XAUTOCLAIM`'s extended reply."},{"id":"the-local-embedder","title":"The local embedder","role":"content","text":"The `LocalEmbedder` class wraps DJL's HuggingFace text-embedding pipeline so the rest of the helper can hand it a string and get back a unit-normalised `float[]`\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/recommendation-engine/java-lettuce/LocalEmbedder.java)):\n\n[code example]\n\nThe model URL routes through DJL's model zoo. The `ai.djl.huggingface.onnxruntime` group ID pulls a pre-converted ONNX bundle (model weights plus `tokenizer.json`) so we don't need a separate conversion step. `optEngine(\"OnnxRuntime\")` pins the inference engine to ONNX Runtime, which means the demo only drags in the small ONNX Runtime native library rather than the much heavier PyTorch native library that `ai.djl.huggingface.pytorch` would require.\n\n`Predictor` is *not* thread-safe; the wrapper guards calls with `synchronized` because the JDK HttpServer dispatches each request on a worker thread. For higher concurrency in production you'd hold a pool of `Predictor` instances backed by one `ZooModel`."},{"id":"the-catalogue-builder","title":"The catalogue builder","role":"content","text":"Item vectors are pre-computed once and stored in `catalog.json` so the demo server can boot quickly. `BuildCatalog` is the reference for how to do that &mdash; and is the program you'd adapt for a real catalogue ingestion pipeline\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/recommendation-engine/java-lettuce/BuildCatalog.java)):\n\n[code example]\n\nIn production the equivalent lives in an offline pipeline: embed once on catalogue updates and ship the vectors into Redis with [`HSET`](https://redis.io/docs/latest/commands/hset). The serving tier still embeds the *query* on each request, but that's usually fronted by a dedicated model server or batched at the API gateway rather than co-located with the data tier as it is in this demo.\n\nThe shared `catalog.json` wire format (model name, dim, list of products with base64-encoded `float32` LE bytes for the vector) is identical to what the Python, Node, and Go ports produce, so you can re-use any port's catalog with the Lettuce demo as long as the embedding model matches."},{"id":"the-interactive-demo","title":"The interactive demo","role":"content","text":"`DemoServer` runs `com.sun.net.httpserver.HttpServer` with a 16-thread executor and one demo user (`demo`). The HTML page lets you:\n\n* Type a natural-language query and toggle filters: TAG (category, brand, in-stock), NUMERIC (price range, rating), and TEXT (the **Description contains** field, a phrase pre-filter against the `description` text index).\n* Toggle session blending and category-affinity re-ranking independently to see what each layer contributes.\n* Click any product card to record a click into the session. The page re-renders the user features panel immediately &mdash; the click wrote to the user features hash, and the next search reads that hash to fold the update in.\n* Refresh a single product's embedding with new text and watch the ranking change on the next query.\n\nThe server holds one `RedisClient`, two `StatefulRedisConnection`s (regular + binary-codec), one `LocalEmbedder`, and one `Recommender` for the lifetime of the process. Endpoints:\n\n| Endpoint                  | What it does                                                                |\n|---------------------------|-----------------------------------------------------------------------------|\n| `GET  /state`             | Index info, user features, full catalogue listing.                          |\n| `POST /search`            | Embed the query, run `FT.SEARCH` with filters + KNN, optionally re-rank.    |\n| `POST /click`             | Record a click for the demo user: update session vector and affinity.       |\n| `POST /reset-user`        | Drop the user features hash.                                                |\n| `POST /reset-index`       | Drop the index and documents and re-seed from `catalog.json`.               |\n| `POST /refresh-embedding` | Embed new text and overwrite one product's vector with `HSET`.              |"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before running the demo, make sure that:\n\n* Redis 7.0 or later with the Redis Search module is running and accessible. By default the demo connects to `localhost:6379`. [Redis Stack](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack) or [Redis 8 with Search](https://redis.io/docs/latest/develop/ai/search-and-query) both work.\n* JDK 17 or later is installed (the demo's inline HTML uses text blocks, which require JDK 15+; 17+ keeps the demo on a current LTS).\n* [Maven](https://maven.apache.org/) 3.9 or later for dependency resolution. DJL pulls in a couple of dozen transitive jars, so a manual `javac -cp ...` build is impractical; the `pom.xml` shipped next to the source files lets Maven handle that. Lettuce 7.x is required &mdash; the typed `FT.*` API used by this demo (`ftCreate`, `ftSearch`, `ftDropindex`, `ftTagvals`, the `SearchArgs` / `FieldArgs` / `CreateArgs` builders) lives in `io.lettuce.core.search` and was introduced in the 7.x release line.\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`."},{"id":"run-the-demo-locally","title":"Run the demo locally","role":"content","text":"1.  Clone the [`redis/docs`](https://github.com/redis/docs) repository and change into the example\n    directory:\n\n    [code example]\n\n2.  Build the project. The first build downloads the dependency graph (Lettuce 7, DJL API, the\n    HuggingFace tokenizer extension, the ONNX Runtime engine, and Gson):\n\n    [code example]\n\n3.  Generate the catalogue with pre-computed embeddings. The first run downloads the\n    `all-MiniLM-L6-v2` ONNX bundle (~80&nbsp;MB) into the local DJL cache\n    (`~/.djl.ai/cache/`):\n\n    [code example]\n\n4.  Start the demo server:\n\n    [code example]\n\n    Override the defaults with `-Dexec.args=\"--port 8090 --redis-host 127.0.0.1\"`.\n\n5.  Open <http://localhost:8084> and try some queries:\n\n    * **\"insulated down jacket for cold weather\"** &mdash; filtered to `outerwear`, in-stock only.\n    * **\"comfortable shoes for trail running\"** &mdash; filtered to `footwear`.\n    * Add **Description contains: waterproof** to either query above to see a TEXT pre-filter\n      combine with the KNN.\n    * Click a couple of products to seed a session, then re-run the same query\n      with **Blend session vector into query** on and watch the ranking shift.\n    * Use **Refresh embedding** to change a product's vector &mdash; for example,\n      change the Alpine down parka's text to \"heavy duty arctic expedition parka\n      with hood\" and re-run the jacket query to see the result move.\n\nThe server is read/write against your local Redis. The default index name is `recommend:idx` and product keys live under `product:`. Pass `--no-reset` to keep an existing index across restarts, or `--index-name` / `--key-prefix` to point the demo at a different prefix entirely."}],"examples":[{"id":"the-recommender-helper-ex0","language":"java","code":"import io.lettuce.core.RedisClient;\nimport io.lettuce.core.api.StatefulRedisConnection;\nimport io.lettuce.core.codec.ByteArrayCodec;\nimport io.lettuce.core.codec.RedisCodec;\nimport io.lettuce.core.codec.StringCodec;\n\n// One client, two connections: a String/String one for structured\n// commands and FT.* index management, plus a String/byte[] one for\n// every command that touches the binary embedding field (including\n// FT.SEARCH, whose $vec parameter is raw bytes too).\nRedisClient client = RedisClient.create(\"redis://localhost:6379\");\nStatefulRedisConnection<String, String> conn = client.connect();\nStatefulRedisConnection<String, byte[]> binConn = client.connect(\n        RedisCodec.of(StringCodec.UTF8, ByteArrayCodec.INSTANCE));\n\nRecommender recommender = new Recommender(conn, binConn,\n        \"recommend:idx\", \"product:\", \"user:\", 384);\n\nLocalEmbedder embedder = new LocalEmbedder();   // all-MiniLM-L6-v2 via DJL + ONNX Runtime\n\n// One-time index setup (idempotent).\nrecommender.createIndex();\n\n// Embed the natural-language query.\nfloat[] queryVec = embedder.encodeOne(\"warm waterproof jacket for hiking\");\n\n// Retrieval: KNN with structured pre-filter in one round trip.\n// Filters combine TAG (category, brand, inStockOnly), NUMERIC\n// (price range, rating), and TEXT (textMatch against textField) —\n// Redis applies them all in front of the KNN.\nRecommender.RetrieveOptions opts = new Recommender.RetrieveOptions();\nopts.category = \"outerwear\";\nopts.inStockOnly = true;\nopts.minPrice = 50.0;\nopts.maxPrice = 200.0;\nopts.textMatch = \"waterproof\";   // TEXT pre-filter on @description\nopts.k = 10;\nList<Recommender.Candidate> candidates = recommender.candidateRetrieve(queryVec, opts);\n\n// Record a click — updates the user's session vector and category\n// affinity atomically; the next call to candidateRetrieve sees it.\nrecommender.recordClick(\"alice\", \"p001\", 0.4, 1.0);\n\n// Pull user features so the next retrieval can blend the session\n// vector into the query and apply the category-affinity re-rank.\nRecommender.UserFeatures features = recommender.getUserFeatures(\"alice\");\nopts.sessionVec = features.sessionVec;\nopts.sessionWeight = 0.3;\ncandidates = recommender.candidateRetrieve(queryVec, opts);\ncandidates = recommender.rerank(candidates, features, 0.15);\n\n// Hot embedding refresh — overwrite the vector for one product; the\n// HNSW index reflects the change on the next FT.SEARCH.\nfloat[] newVector = embedder.encodeOne(\"heavy-duty arctic expedition parka\");\nrecommender.refreshEmbedding(\"p001\", newVector);","section_id":"the-recommender-helper"},{"id":"data-model-ex0","language":"text","code":"product:p001\n  name=Alpine down parka\n  description=Heavyweight 800-fill goose down parka...\n  category=outerwear\n  brand=northpeak\n  price=289.0\n  rating=4.7\n  in_stock=true\n  embedding=<384 x float32 little-endian bytes>","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"FT.CREATE recommend:idx\n  ON HASH PREFIX 1 product:\n  SCHEMA\n    name        TEXT\n    description TEXT\n    category    TAG\n    brand       TAG\n    in_stock    TAG\n    price       NUMERIC SORTABLE\n    rating      NUMERIC SORTABLE\n    embedding   VECTOR HNSW 6 TYPE FLOAT32 DIM 384 DISTANCE_METRIC COSINE","section_id":"data-model"},{"id":"data-model-ex2","language":"text","code":"user:alice:features\n  session_vec=<384 x float32 little-endian bytes>\n  aff:outerwear=2.0\n  aff:footwear=1.0\n  last_clicked_id=p015\n  last_clicked_category=footwear\n  clicks=3","section_id":"data-model"},{"id":"the-query-ex0","language":"text","code":"FT.SEARCH recommend:idx\n  \"(@category:{outerwear} @in_stock:{true} @price:[50.0 200.0])\n     =>[KNN 10 @embedding $vec AS vector_score]\"\n  PARAMS 2 vec <384-float32-bytes>\n  SORTBY vector_score\n  RETURN 8 name description category brand price rating in_stock vector_score\n  DIALECT 2","section_id":"the-query"},{"id":"the-query-ex1","language":"java","code":"String knn = filterClause + \"=>[KNN \" + k + \" @embedding $vec AS vector_score]\";\nSearchArgs<String, byte[]> args = SearchArgs.<String, byte[]>builder()\n        .param(\"vec\", Recommender.floatsToBytes(queryVec))\n        .returnField(\"name\").returnField(\"description\")\n        .returnField(\"category\").returnField(\"brand\")\n        .returnField(\"price\").returnField(\"rating\")\n        .returnField(\"in_stock\").returnField(\"vector_score\")\n        .sortBy(SortByArgs.<String>builder().attribute(\"vector_score\").build())\n        .limit(0, k)\n        .dialect(QueryDialects.DIALECT2)\n        .build();\nSearchReply<String, byte[]> reply = binConn.sync()\n        .ftSearch(\"recommend:idx\",\n                knn.getBytes(StandardCharsets.UTF_8), args);","section_id":"the-query"},{"id":"1-codec-choice-for-the-binary-embedding-field-ex0","language":"java","code":"RedisClient client = RedisClient.create(\"redis://localhost:6379\");\nStatefulRedisConnection<String, String> conn = client.connect();\nStatefulRedisConnection<String, byte[]> binConn = client.connect(\n        RedisCodec.of(StringCodec.UTF8, ByteArrayCodec.INSTANCE));","section_id":"1-codec-choice-for-the-binary-embedding-field"},{"id":"2-pipelining-with-setautoflushcommandsfalse-ex0","language":"java","code":"RedisAsyncCommands<String, byte[]> async = binConn.async();\nbinConn.setAutoFlushCommands(false);\ntry {\n    List<RedisFuture<Long>> futures = new ArrayList<>();\n    for (Product p : products) {\n        Map<String, byte[]> fields = ...;          // name, description, ..., embedding\n        futures.add(async.hset(productKey(p.id), fields));\n    }\n    binConn.flushCommands();\n    for (RedisFuture<Long> f : futures) f.get();\n} finally {\n    binConn.setAutoFlushCommands(true);\n}","section_id":"2-pipelining-with-setautoflushcommandsfalse"},{"id":"the-local-embedder-ex0","language":"java","code":"Criteria<String, float[]> criteria = Criteria.builder()\n        .setTypes(String.class, float[].class)\n        .optModelUrls(\"djl://ai.djl.huggingface.onnxruntime/sentence-transformers/all-MiniLM-L6-v2\")\n        .optEngine(\"OnnxRuntime\")\n        .optTranslatorFactory(new TextEmbeddingTranslatorFactory())\n        .optProgress(new ProgressBar())\n        .build();\nZooModel<String, float[]> model = criteria.loadModel();\nPredictor<String, float[]> predictor = model.newPredictor();\nfloat[] vector = predictor.predict(\"warm waterproof jacket for hiking\");","section_id":"the-local-embedder"},{"id":"the-catalogue-builder-ex0","language":"java","code":"List<CatalogSeed.Seed> seeds = CatalogSeed.all();\nList<String> texts = new ArrayList<>();\nfor (CatalogSeed.Seed s : seeds) {\n    texts.add(CatalogSeed.embedTextFor(s));    // \"<name>. <description>\"\n}\ntry (LocalEmbedder embedder = new LocalEmbedder()) {\n    float[][] vectors = embedder.encodeMany(texts);\n    Catalog.write(Path.of(\"catalog.json\"),\n            embedder.getModelName(), vectors[0].length, seeds, vectors);\n}","section_id":"the-catalogue-builder"},{"id":"run-the-demo-locally-ex0","language":"bash","code":"git clone https://github.com/redis/docs.git\n    cd docs/content/develop/use-cases/recommendation-engine/java-lettuce","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex1","language":"bash","code":"mvn -DskipTests package","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex2","language":"bash","code":"mvn -DskipTests exec:java -Dexec.mainClass=BuildCatalog","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex3","language":"bash","code":"mvn -DskipTests exec:java -Dexec.mainClass=DemoServer","section_id":"run-the-demo-locally"}]}
{"schema_version":2,"id":"develop/use-cases/recommendation-engine/nodejs","title":"Redis recommendation engine with node-redis","url":"https://redis.io/docs/latest/develop/use-cases/recommendation-engine/nodejs/","summary":"Build a Redis-backed recommendation engine in Node.js with node-redis and @xenova/transformers","aliases":["/develop/use-cases/recommendation-engine/node-redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-17T10:28:27+01:00","children":[],"page_type":"content","content_hash":"7999b20550fbd2e8b92945dbe915d2c35ea71d7ba4e82e283f1d156cac56a67c","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a small Redis-backed product recommendation service in Node.js with [`node-redis`](https://redis.io/docs/latest/develop/clients/nodejs) and the [`@xenova/transformers`](https://www.npmjs.com/package/@xenova/transformers) library. It includes a local web server built on the Node.js `http` module so you can embed a natural-language query, run a KNN retrieval with structured pre-filters in one round trip, feed clicks back as a session signal, and watch the next recommendation incorporate them immediately."},{"id":"overview","title":"Overview","role":"overview","text":"Each product is stored as a single Redis [Hash](https://redis.io/docs/latest/develop/data-types/hashes) at `product:<id>`. The hash holds the structured metadata (name, description, category, brand, price, rating, in-stock flag) alongside the raw `float32` bytes of a 384-dimensional embedding. A single [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) index covers every field, so one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call with a `KNN` clause does the vector similarity *and* the TAG / NUMERIC / TEXT pre-filtering in the same pass — no cross-store joins.\n\nPer-user state lives in `user:<id>:features`: a session vector written as an exponentially weighted average of recently-clicked item embeddings, plus per-category affinity counters incremented atomically with [`HINCRBYFLOAT`](https://redis.io/docs/latest/commands/hincrbyfloat). [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) does *not* read that hash directly; instead, the application reads it on the next request and passes the session vector to `FT.SEARCH` as the query parameter. The two-step is what lets a click feed the very next recommendation without a batch cycle or cache invalidation.\n\nThat gives you:\n\n* A single round trip for retrieval — vector KNN + structured filters in one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search).\n* Sub-millisecond hot path once the query is embedded; embedding the query is the bottleneck, and that's a model-side cost, not a Redis one.\n* Real-time session signals — a click writes a new session vector and bumps an affinity counter; the next query reads them and folds them in.\n* No-downtime embedding refresh — [`HSET`](https://redis.io/docs/latest/commands/hset) on the vector field, and the HNSW index reflects the change on the next query."},{"id":"how-it-works","title":"How it works","role":"content","text":"There are two distinct paths: a **query path** runs every time the application wants a recommendation, and a **click path** runs every time the user interacts with a product."},{"id":"query-path-per-recommendation-request","title":"Query path (per recommendation request)","role":"content","text":"1. The application calls `embedder.encodeOne(queryText)` to turn a natural-language query into a 384-dimensional `Float32Array`.\n2. The application reads the user's session vector and affinities from the user features hash. If a session vector exists, it gets blended into the query vector with a tunable weight, so the user's recent clicks pull retrieval toward what they've been engaging with.\n3. `recommender.candidateRetrieve(queryVec, ...)` runs [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) with a pre-filter clause built from the request's TAG / NUMERIC / TEXT inputs, followed by a `KNN k @embedding $vec` clause. Redis returns up to `k` candidates with the cosine distance to the query (lower is closer).\n4. `recommender.rerank(candidates, userFeatures)` subtracts a log-scaled per-category affinity bonus from each candidate's distance and re-sorts the list closest-first. The log scaling keeps repeated clicks from running away with the ranking."},{"id":"click-path-per-user-interaction","title":"Click path (per user interaction)","role":"content","text":"When the user clicks a product, `recommender.recordClick(userId, productId)` does the following:\n\n1. Reads the clicked item's embedding from its hash.\n2. Reads the user's previous session vector from the user features hash, blends the new click in via an exponentially weighted moving average, and writes the new session vector back with [`HSET`](https://redis.io/docs/latest/commands/hset). This is a read-modify-write — atomic against any single write but not against a concurrent click for the same user; in practice, per-user click streams don't generate the contention to make this matter, and if a deployment does, the read and write can be wrapped in [`WATCH/MULTI/EXEC`](https://redis.io/docs/latest/commands/multi) or a small Lua script.\n3. Bumps the per-category affinity counter with [`HINCRBYFLOAT`](https://redis.io/docs/latest/commands/hincrbyfloat) — atomic, no read needed — and the click count with [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby).\n\nThe next query path picks both changes up the next time it reads the user features hash.\n\nRefreshing an item's embedding follows a similar shape: encode the new text, write the vector bytes back with [`HSET`](https://redis.io/docs/latest/commands/hset), and the HNSW index reflects the change on the next query without a rebuild."},{"id":"the-recommender-helper","title":"The recommender helper","role":"content","text":"The `RedisRecommender` class wraps the Redis Search index and the retrieval flow\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/recommendation-engine/nodejs/recommender.js)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each product is one Redis Hash. The vector field is raw little-endian `float32` bytes — no JSON wrapping — because the Redis Search vector encoding expects exactly that. In Node.js, that's a `Buffer` view over a `Float32Array`.\n\n[code example]\n\nThe Redis Search index schema treats every field as queryable in its natural type:\n\n[code example]\n\nIn `node-redis` 5.x that schema is expressed with the `SCHEMA_FIELD_TYPE` and `SCHEMA_VECTOR_FIELD_ALGORITHM` constants — see [`recommender.js`](https://github.com/redis/docs/blob/main/content/develop/use-cases/recommendation-engine/nodejs/recommender.js).\n\nPer-user state is a separate hash. The session vector is stored as raw `float32` bytes the same way; affinity counters are stored as plain numeric strings, one field per category, prefixed with `aff:` so they don't collide with anything else.\n\n[code example]"},{"id":"the-query","title":"The query","role":"content","text":"The KNN clause is a hybrid query: a pre-filter expression in parentheses, then `=>[KNN k @embedding $vec]`. With `DIALECT 2`, Redis applies the filter first and then KNN-ranks only the matching documents.\n\n[code example]\n\nWhen there's no filter, the pre-filter clause is just `(*)`. `vector_score` returned by Redis is the cosine distance (0 = identical, 2 = opposite), where 0 is a perfect match — so the result is sorted ascending."},{"id":"binary-fields-with-node-redis-5x","title":"Binary fields with `node-redis` 5.x","role":"content","text":"`node-redis` returns text by default. The embedding field is binary, so the helper derives a *type-mapped view* of the same connection that returns blob strings as `Buffer` instances:\n\n[code example]\n\nThe `RedisRecommender` keeps both views and routes reads accordingly: the binary view for the `embedding` and `session_vec` fields, the default view for `category`, `clicks`, and the rest."},{"id":"the-catalog-builder","title":"The catalog builder","role":"content","text":"Item vectors are pre-computed once and stored in `catalog.json` so the demo server can boot quickly. `buildCatalog.js` is the reference for how to do that — and is the script you'd adapt for a real catalog ingestion pipeline\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/recommendation-engine/nodejs/buildCatalog.js)):\n\n[code example]\n\nIn production the equivalent of this script lives in an offline pipeline: embed once on catalog updates and ship the vectors into Redis with [`HSET`](https://redis.io/docs/latest/commands/hset). The serving tier still embeds the *query* on each request, but that's usually fronted by a dedicated model server or batched at the API gateway rather than co-located with the data tier as it is in this demo."},{"id":"the-interactive-demo","title":"The interactive demo","role":"content","text":"`demoServer.js` runs an `http` server with one demo user (`demo`). The HTML page lets you:\n\n* Type a natural-language query and toggle filters: TAG (category, brand, in-stock), NUMERIC (price range, rating), and TEXT (the **Description contains** field, a phrase pre-filter against the `description` text index).\n* Toggle session blending and category-affinity re-ranking independently to see what each layer contributes.\n* Click any product card to record a click into the session. The page re-renders the user features panel immediately — the click wrote to the user features hash, and the next search reads that hash to fold the update in.\n* Refresh a single product's embedding with new text and watch the ranking change on the next query.\n\nThe server holds one `LocalEmbedder` and one `RedisRecommender` for the lifetime of the process. Endpoints:\n\n| Endpoint                | What it does                                                                |\n|-------------------------|-----------------------------------------------------------------------------|\n| `GET  /state`           | Index info, user features, full catalog listing.                            |\n| `POST /search`          | Embed the query, run `FT.SEARCH` with filters + KNN, optionally re-rank.    |\n| `POST /click`           | Record a click for the demo user: update session vector and affinity.       |\n| `POST /reset-user`      | Drop the user features hash.                                                |\n| `POST /reset-index`     | Drop the index and documents and re-seed from `catalog.json`.               |\n| `POST /refresh-embedding` | Embed new text and overwrite one product's vector with `HSET`.            |"},{"id":"run-the-demo-locally","title":"Run the demo locally","role":"content","text":"1.  Clone the [`redis/docs`](https://github.com/redis/docs) repository and change into the example\n    directory:\n\n    [code example]\n\n2.  Install the dependencies:\n\n    [code example]\n\n3.  Make sure a Redis instance with the Redis Search module is running locally on\n    port 6379. [Redis Stack](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack) or\n    [Redis 8 with Search](https://redis.io/docs/latest/develop/ai/search-and-query) both work.\n\n4.  Generate the catalog with pre-computed embeddings. The first run downloads the\n    `all-MiniLM-L6-v2` model (~80 MB) into the local Transformers.js cache:\n\n    [code example]\n\n5.  Start the demo server:\n\n    [code example]\n\n6.  Open <http://localhost:8085> and try some queries:\n\n    * **\"insulated down jacket for cold weather\"** — filtered to `outerwear`, in-stock only.\n    * **\"comfortable shoes for trail running\"** — filtered to `footwear`.\n    * Add **Description contains: waterproof** to either query above to see a TEXT pre-filter\n      combine with the KNN.\n    * Click a couple of products to seed a session, then re-run the same query\n      with **Blend session vector into query** on and watch the ranking shift.\n    * Use **Refresh embedding** to change a product's vector — for example,\n      change the Alpine down parka's text to \"heavy duty arctic expedition parka\n      with hood\" and re-run the jacket query to see the result move.\n\nThe server is read/write against your local Redis. The default index name is `recommend:idx` and product keys live under `product:`. Pass `--no-reset` to keep an existing index across restarts, or `--redis-url redis://host:port` to point at a different Redis."}],"examples":[{"id":"the-recommender-helper-ex0","language":"javascript","code":"import { createClient } from \"redis\";\nimport { LocalEmbedder } from \"./embeddings.js\";\nimport { RedisRecommender } from \"./recommender.js\";\n\nconst client = createClient();\nawait client.connect();\nconst recommender = new RedisRecommender({\n  redisClient: client,\n  indexName: \"recommend:idx\",\n});\nconst embedder = new LocalEmbedder();  // Xenova/all-MiniLM-L6-v2\n\n// One-time index setup (idempotent).\nawait recommender.createIndex();\n\n// Embed the natural-language query.\nconst queryVec = await embedder.encodeOne(\"warm waterproof jacket for hiking\");\n\n// Retrieval: KNN with structured pre-filter in one round trip.\n// Filters combine TAG (category, brand, inStockOnly), NUMERIC\n// (price range, rating), and TEXT (textMatch against textField) —\n// Redis applies them all in front of the KNN.\nconst candidates = await recommender.candidateRetrieve(queryVec, {\n  category: \"outerwear\",\n  inStockOnly: true,\n  minPrice: 50,\n  maxPrice: 200,\n  textMatch: \"waterproof\",        // TEXT pre-filter on @description\n  k: 10,\n});\n\n// Record a click — updates the user's session vector and category\n// affinity atomically; the next call to candidateRetrieve sees it.\nawait recommender.recordClick(\"alice\", \"p001\");\n\n// Pull user features so the next retrieval can blend the session\n// vector into the query and apply the category-affinity re-rank.\nconst features = await recommender.getUserFeatures(\"alice\");\nconst reranked = recommender.rerank(\n  await recommender.candidateRetrieve(queryVec, {\n    category: \"outerwear\",\n    inStockOnly: true,\n    k: 10,\n    sessionVec: features.session_vec,\n    sessionWeight: 0.3,\n  }),\n  features,\n  0.15,\n);\n\n// Hot embedding refresh — overwrite the vector for one product; the\n// HNSW index reflects the change on the next FT.SEARCH.\nconst newVector = await embedder.encodeOne(\"heavy-duty arctic expedition parka\");\nawait recommender.refreshEmbedding(\"p001\", newVector);","section_id":"the-recommender-helper"},{"id":"data-model-ex0","language":"text","code":"product:p001\n  name=Alpine down parka\n  description=Heavyweight 800-fill goose down parka...\n  category=outerwear\n  brand=northpeak\n  price=289\n  rating=4.7\n  in_stock=true\n  embedding=<384 × float32 little-endian bytes>","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"FT.CREATE recommend:idx\n  ON HASH PREFIX 1 product:\n  SCHEMA\n    name        TEXT\n    description TEXT\n    category    TAG\n    brand       TAG\n    in_stock    TAG\n    price       NUMERIC SORTABLE\n    rating      NUMERIC SORTABLE\n    embedding   VECTOR HNSW 6 TYPE FLOAT32 DIM 384 DISTANCE_METRIC COSINE","section_id":"data-model"},{"id":"data-model-ex2","language":"text","code":"user:alice:features\n  session_vec=<384 × float32 little-endian bytes>\n  aff:outerwear=2\n  aff:footwear=1\n  last_clicked_id=p015\n  last_clicked_category=footwear\n  clicks=3","section_id":"data-model"},{"id":"the-query-ex0","language":"text","code":"FT.SEARCH recommend:idx\n  \"(@category:{outerwear} @in_stock:{true} @price:[50 200])\n     =>[KNN 10 @embedding $vec AS vector_score]\"\n  PARAMS 2 vec <384-float32-bytes>\n  SORTBY vector_score\n  RETURN 8 name description category brand price rating in_stock vector_score\n  DIALECT 2","section_id":"the-query"},{"id":"binary-fields-with-node-redis-5x-ex0","language":"javascript","code":"import { RESP_TYPES } from \"redis\";\n\n// One connection, two views: ``client`` returns strings; ``redisBuf``\n// returns Buffer for every bulk-string reply (HGET, HMGET, HGETALL).\n// Both share the underlying socket.\nconst redisBuf = client.withTypeMapping({\n  [RESP_TYPES.BLOB_STRING]: Buffer,\n});\n\nconst embedding = await redisBuf.hGet(\"product:p001\", \"embedding\");\n// embedding is a Buffer of 384*4 = 1536 bytes","section_id":"binary-fields-with-node-redis-5x"},{"id":"the-catalog-builder-ex0","language":"javascript","code":"import { LocalEmbedder } from \"./embeddings.js\";\n\nconst CATALOG = [\n  { id: \"p001\", name: \"Alpine down parka\",\n    description: \"Heavyweight 800-fill goose down parka...\",\n    category: \"outerwear\", brand: \"northpeak\",\n    price: 289.00, in_stock: true, rating: 4.7 },\n  // ... rest of the catalog ...\n];\n\nconst embedder = new LocalEmbedder();\nconst vectors = await embedder.encodeMany(\n  CATALOG.map(p => `${p.name}. ${p.description}`),\n);\n// Each vector is 384 float32s, packed and written into catalog.json\n// alongside the structured fields. The demo server reads that file at\n// startup and HSETs every product into Redis.","section_id":"the-catalog-builder"},{"id":"run-the-demo-locally-ex0","language":"bash","code":"git clone https://github.com/redis/docs.git\n    cd docs/content/develop/use-cases/recommendation-engine/nodejs","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex1","language":"bash","code":"npm install","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex2","language":"bash","code":"node buildCatalog.js","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex3","language":"bash","code":"node demoServer.js","section_id":"run-the-demo-locally"}]}
{"schema_version":2,"id":"develop/use-cases/recommendation-engine/php","title":"Redis recommendation engine with Predis","url":"https://redis.io/docs/latest/develop/use-cases/recommendation-engine/php/","summary":"Build a Redis-backed recommendation engine in PHP with Predis and TransformersPHP","aliases":["/develop/use-cases/recommendation-engine/predis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-17T10:28:27+01:00","children":[],"page_type":"content","content_hash":"58f05b87ce389c4dd9b557ebeee64a170ad065281275df88e37c8e519ccdd4a9","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a small Redis-backed product recommendation service in PHP with [`predis/predis`](https://redis.io/docs/latest/develop/clients/php) and the [`codewithkyrian/transformers`](https://transformers.codewithkyrian.com/) (TransformersPHP) library. It includes a local web server built on PHP's built-in development server so you can embed a natural-language query, run a KNN retrieval with structured pre-filters in one round trip, feed clicks back as a session signal, and watch the next recommendation incorporate them immediately."},{"id":"overview","title":"Overview","role":"overview","text":"Each product is stored as a single Redis [Hash](https://redis.io/docs/latest/develop/data-types/hashes) at `product:<id>`. The hash holds the structured metadata (name, description, category, brand, price, rating, in-stock flag) alongside the raw `float32` bytes of a 384-dimensional embedding. A single [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) index covers every field, so one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call with a `KNN` clause does the vector similarity *and* the TAG / NUMERIC / TEXT pre-filtering in the same pass — no cross-store joins.\n\nPer-user state lives in `user:<id>:features`: a session vector written as an exponentially weighted average of recently-clicked item embeddings, plus per-category affinity counters incremented atomically with [`HINCRBYFLOAT`](https://redis.io/docs/latest/commands/hincrbyfloat). [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) does *not* read that hash directly; instead, the application reads it on the next request and passes the session vector to `FT.SEARCH` as the query parameter. The two-step is what lets a click feed the very next recommendation without a batch cycle or cache invalidation.\n\nThat gives you:\n\n* A single round trip for retrieval — vector KNN + structured filters in one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search).\n* Sub-millisecond hot path once the query is embedded; embedding the query is the bottleneck, and that's a model-side cost, not a Redis one.\n* Real-time session signals — a click writes a new session vector and bumps an affinity counter; the next query reads them and folds them in.\n* No-downtime embedding refresh — [`HSET`](https://redis.io/docs/latest/commands/hset) on the vector field, and the HNSW index reflects the change on the next query."},{"id":"how-it-works","title":"How it works","role":"content","text":"There are two distinct paths: a **query path** runs every time the application wants a recommendation, and a **click path** runs every time the user interacts with a product."},{"id":"query-path-per-recommendation-request","title":"Query path (per recommendation request)","role":"content","text":"1. The application calls `$embedder->encodeOne($queryText)` to turn a natural-language query into a 384-element `float[]`.\n2. The application reads the user's session vector and affinities from the user features hash. If a session vector exists, it gets blended into the query vector with a tunable weight, so the user's recent clicks pull retrieval toward what they've been engaging with.\n3. `$recommender->candidateRetrieve($queryVec, ...)` runs [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) with a pre-filter clause built from the request's TAG / NUMERIC / TEXT inputs, followed by a `KNN k @embedding $vec` clause. Redis returns up to `k` candidates with the cosine distance to the query (lower is closer).\n4. `$recommender->rerank($candidates, $userFeatures)` subtracts a log-scaled per-category affinity bonus from each candidate's distance and re-sorts the list closest-first. The log scaling keeps repeated clicks from running away with the ranking."},{"id":"click-path-per-user-interaction","title":"Click path (per user interaction)","role":"content","text":"When the user clicks a product, `$recommender->recordClick($userId, $productId)` does the following:\n\n1. Reads the clicked item's embedding from its hash.\n2. Reads the user's previous session vector from the user features hash, blends the new click in via an exponentially weighted moving average, and writes the new session vector back with [`HSET`](https://redis.io/docs/latest/commands/hset). This is a read-modify-write — atomic against any single write but not against a concurrent click for the same user; in practice, per-user click streams don't generate the contention to make this matter, and if a deployment does, the read and write can be wrapped in [`WATCH/MULTI/EXEC`](https://redis.io/docs/latest/commands/multi) or a small Lua script.\n3. Bumps the per-category affinity counter with [`HINCRBYFLOAT`](https://redis.io/docs/latest/commands/hincrbyfloat) — atomic, no read needed — and the click count with [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby).\n\nThe next query path picks both changes up the next time it reads the user features hash.\n\nRefreshing an item's embedding follows a similar shape: encode the new text, write the vector bytes back with [`HSET`](https://redis.io/docs/latest/commands/hset), and the HNSW index reflects the change on the next query without a rebuild."},{"id":"the-recommender-helper","title":"The recommender helper","role":"content","text":"The `Recommender` class wraps the Redis Search index and the retrieval flow\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/recommendation-engine/php/Recommender.php)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each product is one Redis Hash. The vector field is raw little-endian `float32` bytes — no JSON wrapping — because the Redis Search vector encoding expects exactly that. In PHP, `pack('g*', ...$vector)` produces those bytes from a `float[]`, and PHP strings are 8-bit binary-safe so the bytes round-trip cleanly through Predis.\n\n[code example]\n\nThe Redis Search index schema treats every field as queryable in its natural type:\n\n[code example]\n\nPredis exposes Redis Search through dedicated client methods (`ftcreate`, `ftsearch`, `ftinfo`, `ftdropindex`, `fttagvals`) and matching schema-field classes (`TextField`, `TagField`, `NumericField`, `VectorField`) — see [`Recommender.php`](https://github.com/redis/docs/blob/main/content/develop/use-cases/recommendation-engine/php/Recommender.php). For a step-by-step walk-through of the typed Predis API for vector indexing, see the [PHP vector-search reference](https://redis.io/docs/latest/develop/clients/php/vecsearch).\n\nPer-user state is a separate hash. The session vector is stored as raw `float32` bytes the same way; affinity counters are stored as plain numeric strings, one field per category, prefixed with `aff:` so they don't collide with anything else.\n\n[code example]"},{"id":"the-query","title":"The query","role":"content","text":"The KNN clause is a hybrid query: a pre-filter expression in parentheses, then `=>[KNN k @embedding $vec]`. With `DIALECT 2`, Redis applies the filter first and then KNN-ranks only the matching documents.\n\n[code example]\n\nWhen there's no filter, the pre-filter clause is just `(*)`. `vector_score` is the cosine *distance* returned by Redis (0 = identical, 2 = opposite), so the result is sorted ascending and the top row is the closest candidate to the query. From [v3.0.0](https://github.com/predis/predis/releases/tag/v3.0.0) onwards Predis sets `DIALECT 2` by default on `ftsearch`, so the `->dialect('2')` call on `SearchArguments` is explicit but not strictly required."},{"id":"binary-fields-with-predis","title":"Binary fields with Predis","role":"content","text":"`predis/predis` returns hash field values as ordinary PHP strings. PHP strings are 8-bit binary-safe, so the raw `float32` bytes of the embedding round-trip through `hget` / `hmget` / `hgetall` and `hset` / `hmset` without any extra encoding. The helper uses `pack('g*', ...)` and `unpack('g*', ...)` to convert between PHP `float[]` arrays and the binary form Redis Search expects.\n\n[code example]"},{"id":"the-catalogue-builder","title":"The catalogue builder","role":"content","text":"Item vectors are pre-computed once and stored in `catalog.json` so the demo server can boot quickly. `build_catalog.php` is the reference for how to do that — and is the script you'd adapt for a real catalogue ingestion pipeline ([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/recommendation-engine/php/build_catalog.php)):\n\n[code example]\n\nIn production the equivalent of this script lives in an offline pipeline: embed once on catalogue updates and ship the vectors into Redis with [`HSET`](https://redis.io/docs/latest/commands/hset). The serving tier still embeds the *query* on each request, but that's usually fronted by a dedicated model server or batched at the API gateway rather than co-located with the data tier as it is in this demo."},{"id":"the-interactive-demo","title":"The interactive demo","role":"content","text":"`demo_server.php` runs against PHP's built-in development server with one demo user (`demo`). The HTML page lets you:\n\n* Type a natural-language query and toggle filters: TAG (category, brand, in-stock), NUMERIC (price range, rating), and TEXT (the **Description contains** field, a phrase pre-filter against the `description` text index).\n* Toggle session blending and category-affinity re-ranking independently to see what each layer contributes.\n* Click any product card to record a click into the session. The page re-renders the user features panel immediately — the click wrote to the user features hash, and the next search reads that hash to fold the update in.\n* Refresh a single product's embedding with new text and watch the ranking change on the next query.\n\n`php -S` runs each HTTP request in a fresh process, so the embedding model and the Predis connection are constructed once per request. The recent-clicks ring lives in Redis under `demo:reco:*` so successive requests can see each other; everything else is read from Redis on demand. Endpoints:\n\n| Endpoint                  | What it does                                                                |\n|---------------------------|-----------------------------------------------------------------------------|\n| `GET  /state`             | Index info, user features, full catalogue listing.                          |\n| `POST /search`            | Embed the query, run `FT.SEARCH` with filters + KNN, optionally re-rank.    |\n| `POST /click`             | Record a click for the demo user: update session vector and affinity.       |\n| `POST /reset-user`        | Drop the user features hash.                                                |\n| `POST /reset-index`       | Drop the index and documents and re-seed from `catalog.json`.               |\n| `POST /refresh-embedding` | Embed new text and overwrite one product's vector with `HSET`.              |"},{"id":"run-the-demo-locally","title":"Run the demo locally","role":"content","text":"1.  Clone the [`redis/docs`](https://github.com/redis/docs) repository and change into the example\n    directory:\n\n    [code example]\n\n2.  Make sure the PHP [FFI](https://www.php.net/manual/en/book.ffi.php)\n    extension is enabled. TransformersPHP loads the ONNX Runtime native\n    library through FFI; without it the embedder cannot construct.\n\n    [code example]\n\n    If FFI is missing, install the `php-ffi` package for your distribution\n    (or rebuild PHP with `--with-ffi`) and set `ffi.enable=true` in your\n    `php.ini`. The CLI is permissive by default; the built-in dev server\n    is more restrictive, so the launch command below passes\n    `-d ffi.enable=true` explicitly.\n\n3.  Install the dependencies:\n\n    [code example]\n\n    The `codewithkyrian/transformers-libsloader` Composer plugin\n    downloads the ONNX Runtime and supporting native libraries for\n    your platform into `vendor/codewithkyrian/transformers/libs/` —\n    this needs `allow-plugins` in `composer.json` (already set in the\n    example). The download adds ~70 MB on the first install.\n\n4.  Make sure a Redis instance with the Redis Search module is running locally on\n    port 6379. [Redis Stack](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack) or\n    [Redis 8 with Search](https://redis.io/docs/latest/develop/ai/search-and-query) both work.\n\n5.  Generate the catalogue with pre-computed embeddings. The first run downloads the\n    `Xenova/all-MiniLM-L6-v2` model (~80 MB) into the local TransformersPHP cache:\n\n    [code example]\n\n6.  Start the demo server. The `-d ffi.enable=true` flag lets the dev\n    server load the ONNX Runtime via FFI; the `-d error_reporting`\n    flag silences harmless deprecation notices from a third-party\n    dependency on PHP 8.4:\n\n    [code example]\n\n7.  Open <http://localhost:8091> and try some queries:\n\n    * **\"insulated down jacket for cold weather\"** — filtered to `outerwear`, in-stock only.\n    * **\"comfortable shoes for trail running\"** — filtered to `footwear`.\n    * Add **Description contains: waterproof** to either query above to see a TEXT pre-filter\n      combine with the KNN.\n    * Click a couple of products to seed a session, then re-run the same query\n      with **Blend session vector into query** on and watch the ranking shift.\n    * Use **Refresh embedding** to change a product's vector — for example,\n      change the Alpine down parka's text to \"heavy duty arctic expedition parka\n      with hood\" and re-run the jacket query to see the result move.\n\nThe server is read/write against your local Redis. The default index name is `recommend:idx` and product keys live under `product:`. Override either with `INDEX_NAME=...` / `KEY_PREFIX=...` env vars before starting the server, or set `NO_RESET=1` to keep an existing index across restarts. `REDIS_HOST` / `REDIS_PORT` point at a different Redis."}],"examples":[{"id":"the-recommender-helper-ex0","language":"php","code":"<?php\nrequire __DIR__ . '/vendor/autoload.php';\nrequire __DIR__ . '/Embedder.php';\nrequire __DIR__ . '/Recommender.php';\n\nuse Predis\\Client;\nuse Redis\\RecommendationEngine\\Embedder;\nuse Redis\\RecommendationEngine\\Recommender;\n\n$client = new Client(['host' => '127.0.0.1', 'port' => 6379]);\n$recommender = new Recommender($client, 'recommend:idx');\n$embedder = new Embedder();  // Xenova/all-MiniLM-L6-v2 (ONNX)\n\n// One-time index setup (idempotent).\n$recommender->createIndex();\n\n// Embed the natural-language query.\n$queryVec = $embedder->encodeOne('warm waterproof jacket for hiking');\n\n// Retrieval: KNN with structured pre-filter in one round trip.\n// Filters combine TAG (category, brand, inStockOnly), NUMERIC\n// (price range, rating), and TEXT (textMatch against textField) —\n// Redis applies them all in front of the KNN.\n$candidates = $recommender->candidateRetrieve($queryVec, [\n    'category' => 'outerwear',\n    'inStockOnly' => true,\n    'minPrice' => 50,\n    'maxPrice' => 200,\n    'textMatch' => 'waterproof',     // TEXT pre-filter on @description\n    'k' => 10,\n]);\n\n// Record a click — updates the user's session vector and category\n// affinity atomically; the next call to candidateRetrieve sees it.\n$recommender->recordClick('alice', 'p001');\n\n// Pull user features so the next retrieval can blend the session\n// vector into the query and apply the category-affinity re-rank.\n$features = $recommender->getUserFeatures('alice');\n$candidates = $recommender->candidateRetrieve($queryVec, [\n    'category' => 'outerwear',\n    'inStockOnly' => true,\n    'k' => 10,\n    'sessionVec' => $features['session_vec'],\n    'sessionWeight' => 0.3,\n]);\n$candidates = $recommender->rerank($candidates, $features, 0.15);\n\n// Hot embedding refresh — overwrite the vector for one product; the\n// HNSW index reflects the change on the next FT.SEARCH.\n$newVector = $embedder->encodeOne('heavy-duty arctic expedition parka');\n$recommender->refreshEmbedding('p001', $newVector);","section_id":"the-recommender-helper"},{"id":"data-model-ex0","language":"text","code":"product:p001\n  name=Alpine down parka\n  description=Heavyweight 800-fill goose down parka...\n  category=outerwear\n  brand=northpeak\n  price=289\n  rating=4.7\n  in_stock=true\n  embedding=<384 × float32 little-endian bytes>","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"FT.CREATE recommend:idx\n  ON HASH PREFIX 1 product:\n  SCHEMA\n    name        TEXT\n    description TEXT\n    category    TAG\n    brand       TAG\n    in_stock    TAG\n    price       NUMERIC SORTABLE\n    rating      NUMERIC SORTABLE\n    embedding   VECTOR HNSW 6 TYPE FLOAT32 DIM 384 DISTANCE_METRIC COSINE","section_id":"data-model"},{"id":"data-model-ex2","language":"text","code":"user:alice:features\n  session_vec=<384 × float32 little-endian bytes>\n  aff:outerwear=2\n  aff:footwear=1\n  last_clicked_id=p015\n  last_clicked_category=footwear\n  clicks=3","section_id":"data-model"},{"id":"the-query-ex0","language":"text","code":"FT.SEARCH recommend:idx\n  \"(@category:{outerwear} @in_stock:{true} @price:[50 200])\n     =>[KNN 10 @embedding $vec AS vector_score]\"\n  PARAMS 2 vec <384-float32-bytes>\n  SORTBY vector_score\n  RETURN 8 name description category brand price rating in_stock vector_score\n  DIALECT 2","section_id":"the-query"},{"id":"binary-fields-with-predis-ex0","language":"php","code":"// Write a vector\n$client->hset('product:p001', 'embedding', pack('g*', ...$vec));\n\n// Read it back; ``unpack`` returns a 1-indexed array, so reset to 0-indexed.\n$bytes = $client->hget('product:p001', 'embedding');\n$vec = array_values(unpack('g*', $bytes));","section_id":"binary-fields-with-predis"},{"id":"the-catalogue-builder-ex0","language":"php","code":"use Redis\\RecommendationEngine\\Embedder;\n\n$CATALOG = [\n    ['id' => 'p001', 'name' => 'Alpine down parka',\n     'description' => 'Heavyweight 800-fill goose down parka...',\n     'category' => 'outerwear', 'brand' => 'northpeak',\n     'price' => 289.00, 'in_stock' => true, 'rating' => 4.7],\n    // ... rest of the catalogue ...\n];\n\n$embedder = new Embedder();\nforeach ($CATALOG as $product) {\n    $vec = $embedder->encodeOne($product['name'] . '. ' . $product['description']);\n    // Each vector is 384 float32s, packed and written into catalog.json\n    // alongside the structured fields. The demo server reads that file\n    // at startup and HSETs every product into Redis.\n}","section_id":"the-catalogue-builder"},{"id":"run-the-demo-locally-ex0","language":"bash","code":"git clone https://github.com/redis/docs.git\n    cd docs/content/develop/use-cases/recommendation-engine/php","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex1","language":"bash","code":"php -m | grep -i ffi    # should print \"FFI\"","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex2","language":"bash","code":"composer install","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex3","language":"bash","code":"php build_catalog.php","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex4","language":"bash","code":"php -d ffi.enable=true \\\n        -d \"error_reporting=E_ALL & ~E_DEPRECATED\" \\\n        -S 127.0.0.1:8091 demo_server.php","section_id":"run-the-demo-locally"}]}
{"schema_version":2,"id":"develop/use-cases/recommendation-engine/redis-py","title":"Redis recommendation engine with redis-py","url":"https://redis.io/docs/latest/develop/use-cases/recommendation-engine/redis-py/","summary":"Build a Redis-backed recommendation engine in Python with redis-py and sentence-transformers","aliases":["/develop/use-cases/recommendation-engine/python"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-17T10:28:27+01:00","children":[],"page_type":"content","content_hash":"1c5abe7329bfd25c4a2be073fb7ba7f6681bc199f8a10e4316be807b755c10de","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a small Redis-backed product recommendation service in Python with [`redis-py`](https://redis.io/docs/latest/develop/clients/redis-py) and the [`sentence-transformers`](https://www.sbert.net/) library. It includes a local web server built with the Python standard library so you can embed a natural-language query, run a KNN retrieval with structured pre-filters in one round trip, feed clicks back as a session signal, and watch the next recommendation incorporate them immediately."},{"id":"overview","title":"Overview","role":"overview","text":"Each product is stored as a single Redis [Hash](https://redis.io/docs/latest/develop/data-types/hashes) at `product:<id>`. The hash holds the structured metadata (name, description, category, brand, price, rating, in-stock flag) alongside the raw `float32` bytes of a 384-dimensional embedding. A single [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) index covers every field, so one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call with a `KNN` clause does the vector similarity *and* the TAG / NUMERIC / TEXT pre-filtering in the same pass — no cross-store joins.\n\nPer-user state lives in `user:<id>:features`: a session vector written as an exponentially weighted average of recently-clicked item embeddings, plus per-category affinity counters incremented atomically with [`HINCRBYFLOAT`](https://redis.io/docs/latest/commands/hincrbyfloat). [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) does *not* read that hash directly; instead, the application reads it on the next request and passes the session vector to `FT.SEARCH` as the query parameter. The two-step is what lets a click feed the very next recommendation without a batch cycle or cache invalidation.\n\nThat gives you:\n\n* A single round trip for retrieval — vector KNN + structured filters in one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search).\n* Sub-millisecond hot path once the query is embedded; embedding the query is the bottleneck, and that's a model-side cost, not a Redis one.\n* Real-time session signals — a click writes a new session vector and bumps an affinity counter; the next query reads them and folds them in.\n* No-downtime embedding refresh — [`HSET`](https://redis.io/docs/latest/commands/hset) on the vector field, and the HNSW index reflects the change on the next query."},{"id":"how-it-works","title":"How it works","role":"content","text":"There are two distinct paths: a **query path** runs every time the application wants a recommendation, and a **click path** runs every time the user interacts with a product."},{"id":"query-path-per-recommendation-request","title":"Query path (per recommendation request)","role":"content","text":"1. The application calls `embedder.encode_one(query_text)` to turn a natural-language query into a 384-dimensional `float32` vector.\n2. The application reads the user's session vector and affinities from the user features hash. If a session vector exists, it gets blended into the query vector with a tunable weight, so the user's recent clicks pull retrieval toward what they've been engaging with.\n3. `recommender.candidate_retrieve(query_vec, ...)` runs [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) with a pre-filter clause built from the request's TAG / NUMERIC / TEXT inputs, followed by a `KNN k @embedding $vec` clause. Redis returns up to `k` candidates with the cosine distance to the query (lower is closer).\n4. `recommender.rerank(candidates, user_features)` subtracts a log-scaled per-category affinity bonus from each candidate's distance and re-sorts the list closest-first. The log scaling keeps repeated clicks from running away with the ranking."},{"id":"click-path-per-user-interaction","title":"Click path (per user interaction)","role":"content","text":"When the user clicks a product, `recommender.record_click(user_id, product_id)` does the following:\n\n1. Reads the clicked item's embedding from its hash.\n2. Reads the user's previous session vector from the user features hash, blends the new click in via an exponentially weighted moving average, and writes the new session vector back with [`HSET`](https://redis.io/docs/latest/commands/hset). This is a read-modify-write — atomic against any single write but not against a concurrent click for the same user; in practice, per-user click streams don't generate the contention to make this matter, and if a deployment does, the read and write can be wrapped in [`WATCH/MULTI/EXEC`](https://redis.io/docs/latest/commands/multi) or a small Lua script.\n3. Bumps the per-category affinity counter with [`HINCRBYFLOAT`](https://redis.io/docs/latest/commands/hincrbyfloat) — atomic, no read needed — and the click count with [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby).\n\nThe next query path picks both changes up the next time it reads the user features hash.\n\nRefreshing an item's embedding follows a similar shape: encode the new text, write the vector bytes back with [`HSET`](https://redis.io/docs/latest/commands/hset), and the HNSW index reflects the change on the next query without a rebuild."},{"id":"the-recommender-helper","title":"The recommender helper","role":"content","text":"The `RedisRecommender` class wraps the Redis Search index and the retrieval flow\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/recommendation-engine/redis-py/recommender.py)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each product is one Redis Hash. The vector field is raw little-endian `float32` bytes — no JSON wrapping — because the Redis Search vector encoding expects exactly that.\n\n[code example]\n\nThe Redis Search index schema treats every field as queryable in its natural type:\n\n[code example]\n\nPer-user state is a separate hash. The session vector is stored as raw `float32` bytes the same way; affinity counters are stored as plain numeric strings, one field per category, prefixed with `aff:` so they don't collide with anything else.\n\n[code example]"},{"id":"the-query","title":"The query","role":"content","text":"The KNN clause is a hybrid query: a pre-filter expression in parentheses, then `=>[KNN k @embedding $vec]`. With `DIALECT 2`, Redis applies the filter first and then KNN-ranks only the matching documents.\n\n[code example]\n\nWhen there's no filter, the pre-filter clause is just `(*)`. `vector_score` is the cosine *distance* (0 means identical, 2 means opposite), so the result is sorted ascending and the top row is the closest candidate to the query."},{"id":"the-catalogue-builder","title":"The catalogue builder","role":"content","text":"Item vectors are pre-computed once and stored in `catalog.json` so the demo server can boot quickly. `build_catalog.py` is the reference for how to do that — and is the script you'd adapt for a real catalogue ingestion pipeline\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/recommendation-engine/redis-py/build_catalog.py)):\n\n[code example]\n\nIn production the equivalent of this script lives in an offline pipeline: embed once on catalogue updates and ship the vectors into Redis with [`HSET`](https://redis.io/docs/latest/commands/hset). The serving tier still embeds the *query* on each request, but that's usually fronted by a dedicated model server or batched at the API gateway rather than co-located with the data tier as it is in this demo."},{"id":"the-interactive-demo","title":"The interactive demo","role":"content","text":"`demo_server.py` runs a ThreadingHTTPServer with one demo user (`demo`). The HTML page lets you:\n\n* Type a natural-language query and toggle filters: TAG (category, brand, in-stock), NUMERIC (price range, rating), and TEXT (the **Description contains** field, a phrase pre-filter against the `description` text index).\n* Toggle session blending and category-affinity re-ranking independently to see what each layer contributes.\n* Click any product card to record a click into the session. The page re-renders the user features panel immediately — the click wrote to the user features hash, and the next search reads that hash to fold the update in.\n* Refresh a single product's embedding with new text and watch the ranking change on the next query.\n\nThe server holds one `LocalEmbedder` instance and one `RedisRecommender` for the lifetime of the process. Endpoints:\n\n| Endpoint                | What it does                                                                |\n|-------------------------|-----------------------------------------------------------------------------|\n| `GET  /state`           | Index info, user features, full catalogue listing.                          |\n| `POST /search`          | Embed the query, run `FT.SEARCH` with filters + KNN, optionally re-rank.    |\n| `POST /click`           | Record a click for the demo user: update session vector and affinity.       |\n| `POST /reset-user`      | Drop the user features hash.                                                |\n| `POST /reset-index`     | Drop the index and documents and re-seed from `catalog.json`.               |\n| `POST /refresh-embedding` | Embed new text and overwrite one product's vector with `HSET`.            |"},{"id":"run-the-demo-locally","title":"Run the demo locally","role":"content","text":"1.  Clone the [`redis/docs`](https://github.com/redis/docs) repository and change into the example\n    directory:\n\n    [code example]\n\n2.  Install the dependencies:\n\n    [code example]\n\n3.  Make sure a Redis instance with the Redis Search module is running locally on\n    port 6379. [Redis Stack](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack) or\n    [Redis 8 with Search](https://redis.io/docs/latest/develop/ai/search-and-query) both work.\n\n4.  Generate the catalogue with pre-computed embeddings. The first run downloads the\n    `all-MiniLM-L6-v2` model (~80 MB) into the local Hugging Face cache:\n\n    [code example]\n\n5.  Start the demo server:\n\n    [code example]\n\n6.  Open <http://localhost:8084> and try some queries:\n\n    * **\"insulated down jacket for cold weather\"** — filtered to `outerwear`, in-stock only.\n    * **\"comfortable shoes for trail running\"** — filtered to `footwear`.\n    * Add **Description contains: waterproof** to either query above to see a TEXT pre-filter\n      combine with the KNN.\n    * Click a couple of products to seed a session, then re-run the same query\n      with **Blend session vector into query** on and watch the ranking shift.\n    * Use **Refresh embedding** to change a product's vector — for example,\n      change the Alpine down parka's text to \"heavy duty arctic expedition parka\n      with hood\" and re-run the jacket query to see the result move.\n\nThe server is read/write against your local Redis. The default index name is `recommend:idx` and product keys live under `product:`. Pass `--no-reset` to keep an existing index across restarts, or `--redis-host` / `--redis-port` to point at a different Redis."}],"examples":[{"id":"the-recommender-helper-ex0","language":"python","code":"import redis\nfrom embeddings import LocalEmbedder\nfrom recommender import RedisRecommender\n\n# Use decode_responses=False because the embedding field is raw bytes;\n# the helper decodes text fields explicitly where it needs them.\nr = redis.Redis(host=\"localhost\", port=6379, decode_responses=False)\nrecommender = RedisRecommender(redis_client=r, index_name=\"recommend:idx\")\nembedder = LocalEmbedder()  # sentence-transformers/all-MiniLM-L6-v2\n\n# One-time index setup (idempotent).\nrecommender.create_index()\n\n# Embed the natural-language query.\nquery_vec = embedder.encode_one(\"warm waterproof jacket for hiking\")\n\n# Retrieval: KNN with structured pre-filter in one round trip.\n# Filters combine TAG (category, brand, in_stock_only), NUMERIC\n# (price range, rating), and TEXT (text_match against text_field) —\n# Redis applies them all in front of the KNN.\ncandidates = recommender.candidate_retrieve(\n    query_vec,\n    category=\"outerwear\",\n    in_stock_only=True,\n    min_price=50,\n    max_price=200,\n    text_match=\"waterproof\",        # TEXT pre-filter on @description\n    k=10,\n)\n\n# Record a click — updates the user's session vector and category\n# affinity atomically; the next call to candidate_retrieve sees it.\nrecommender.record_click(\"alice\", \"p001\")\n\n# Pull user features so the next retrieval can blend the session\n# vector into the query and apply the category-affinity re-rank.\nfeatures = recommender.get_user_features(\"alice\")\ncandidates = recommender.candidate_retrieve(\n    query_vec,\n    category=\"outerwear\",\n    in_stock_only=True,\n    k=10,\n    session_vec=features[\"session_vec\"],\n    session_weight=0.3,\n)\ncandidates = recommender.rerank(candidates, features, affinity_weight=0.15)\n\n# Hot embedding refresh — overwrite the vector for one product; the\n# HNSW index reflects the change on the next FT.SEARCH.\nimport numpy as np\nnew_vector = embedder.encode_one(\"heavy-duty arctic expedition parka\")\nrecommender.refresh_embedding(\"p001\", new_vector)","section_id":"the-recommender-helper"},{"id":"data-model-ex0","language":"text","code":"product:p001\n  name=Alpine down parka\n  description=Heavyweight 800-fill goose down parka...\n  category=outerwear\n  brand=northpeak\n  price=289.0\n  rating=4.7\n  in_stock=true\n  embedding=<384 × float32 little-endian bytes>","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"FT.CREATE recommend:idx\n  ON HASH PREFIX 1 product:\n  SCHEMA\n    name        TEXT\n    description TEXT\n    category    TAG\n    brand       TAG\n    in_stock    TAG\n    price       NUMERIC SORTABLE\n    rating      NUMERIC SORTABLE\n    embedding   VECTOR HNSW 6 TYPE FLOAT32 DIM 384 DISTANCE_METRIC COSINE","section_id":"data-model"},{"id":"data-model-ex2","language":"text","code":"user:alice:features\n  session_vec=<384 × float32 little-endian bytes>\n  aff:outerwear=2.0\n  aff:footwear=1.0\n  last_clicked_id=p015\n  last_clicked_category=footwear\n  clicks=3","section_id":"data-model"},{"id":"the-query-ex0","language":"text","code":"FT.SEARCH recommend:idx\n  \"(@category:{outerwear} @in_stock:{true} @price:[50.0 200.0])\n     =>[KNN 10 @embedding $vec AS vector_score]\"\n  PARAMS 2 vec <384-float32-bytes>\n  SORTBY vector_score\n  RETURN 8 name description category brand price rating in_stock vector_score\n  DIALECT 2","section_id":"the-query"},{"id":"the-catalogue-builder-ex0","language":"python","code":"from embeddings import LocalEmbedder\n\nCATALOG = [\n    {\"id\": \"p001\", \"name\": \"Alpine down parka\",\n     \"description\": \"Heavyweight 800-fill goose down parka...\",\n     \"category\": \"outerwear\", \"brand\": \"northpeak\",\n     \"price\": 289.00, \"in_stock\": True, \"rating\": 4.7},\n    # ... rest of the catalogue ...\n]\n\nembedder = LocalEmbedder()\nvectors = embedder.encode_many(\n    f\"{p['name']}. {p['description']}\" for p in CATALOG\n)\n# Each vector is 384 float32s, packed and written into catalog.json\n# alongside the structured fields. The demo server reads that file at\n# startup and HSETs every product into Redis.","section_id":"the-catalogue-builder"},{"id":"run-the-demo-locally-ex0","language":"bash","code":"git clone https://github.com/redis/docs.git\n    cd docs/content/develop/use-cases/recommendation-engine/redis-py","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex1","language":"bash","code":"pip install redis sentence-transformers numpy","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex2","language":"bash","code":"python build_catalog.py","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex3","language":"bash","code":"python demo_server.py","section_id":"run-the-demo-locally"}]}
{"schema_version":2,"id":"develop/use-cases/recommendation-engine/ruby","title":"Redis recommendation engine with redis-rb","url":"https://redis.io/docs/latest/develop/use-cases/recommendation-engine/ruby/","summary":"Build a Redis-backed recommendation engine in Ruby with redis-rb and informers","aliases":["/develop/use-cases/recommendation-engine/redis-rb"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-17T10:28:27+01:00","children":[],"page_type":"content","content_hash":"262825f50d4e3e44bebe36657572977b380826391356a5d522fc65dfbf023fe9","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a small Redis-backed product recommendation service in Ruby with the [`redis-rb`](https://redis.io/docs/latest/develop/clients/ruby) gem and the [`informers`](https://github.com/ankane/informers) gem for local text embeddings. It includes a small local web server built on Ruby's `webrick` library so you can embed a natural-language query, run a KNN retrieval with structured pre-filters in one round trip, feed clicks back as a session signal, and watch the next recommendation incorporate them immediately."},{"id":"overview","title":"Overview","role":"overview","text":"Each product is stored as a single Redis [Hash](https://redis.io/docs/latest/develop/data-types/hashes) at `product:<id>`. The hash holds the structured metadata (name, description, category, brand, price, rating, in-stock flag) alongside the raw `float32` bytes of a 384-dimensional embedding. A single [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) index covers every field, so one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call with a `KNN` clause does the vector similarity *and* the TAG / NUMERIC / TEXT pre-filtering in the same pass — no cross-store joins.\n\nPer-user state lives in `user:<id>:features`: a session vector written as an exponentially weighted average of recently-clicked item embeddings, plus per-category affinity counters incremented atomically with [`HINCRBYFLOAT`](https://redis.io/docs/latest/commands/hincrbyfloat). [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) does *not* read that hash directly; instead, the application reads it on the next request and passes the session vector to `FT.SEARCH` as the query parameter. The two-step is what lets a click feed the very next recommendation without a batch cycle or cache invalidation.\n\nThat gives you:\n\n* A single round trip for retrieval — vector KNN + structured filters in one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search).\n* Sub-millisecond hot path once the query is embedded; embedding the query is the bottleneck, and that's a model-side cost, not a Redis one.\n* Real-time session signals — a click writes a new session vector and bumps an affinity counter; the next query reads them and folds them in.\n* No-downtime embedding refresh — [`HSET`](https://redis.io/docs/latest/commands/hset) on the vector field, and the HNSW index reflects the change on the next query."},{"id":"how-it-works","title":"How it works","role":"content","text":"There are two distinct paths: a **query path** runs every time the application wants a recommendation, and a **click path** runs every time the user interacts with a product."},{"id":"query-path-per-recommendation-request","title":"Query path (per recommendation request)","role":"content","text":"1. The application calls `embedder.encode_one(query_text)` to turn a natural-language query into a 384-dimensional `float32` vector.\n2. The application reads the user's session vector and affinities from the user features hash. If a session vector exists, it gets blended into the query vector with a tunable weight, so the user's recent clicks pull retrieval toward what they've been engaging with.\n3. `recommender.candidate_retrieve(query_vec, ...)` runs [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) with a pre-filter clause built from the request's TAG / NUMERIC / TEXT inputs, followed by a `KNN k @embedding $vec` clause. Redis returns up to `k` candidates with the cosine distance to the query (lower is closer).\n4. `recommender.rerank(candidates, user_features)` subtracts a log-scaled per-category affinity bonus from each candidate's distance and re-sorts the list closest-first. The log scaling keeps repeated clicks from running away with the ranking."},{"id":"click-path-per-user-interaction","title":"Click path (per user interaction)","role":"content","text":"When the user clicks a product, `recommender.record_click(user_id, product_id)` does the following:\n\n1. Reads the clicked item's embedding from its hash.\n2. Reads the user's previous session vector from the user features hash, blends the new click in via an exponentially weighted moving average, and writes the new session vector back with [`HSET`](https://redis.io/docs/latest/commands/hset). This is a read-modify-write — atomic against any single write but not against a concurrent click for the same user; in practice, per-user click streams don't generate the contention to make this matter, and if a deployment does, the read and write can be wrapped in [`WATCH/MULTI/EXEC`](https://redis.io/docs/latest/commands/multi) or a small Lua script.\n3. Bumps the per-category affinity counter with [`HINCRBYFLOAT`](https://redis.io/docs/latest/commands/hincrbyfloat) — atomic, no read needed — and the click count with [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby).\n\nThe next query path picks both changes up the next time it reads the user features hash.\n\nRefreshing an item's embedding follows a similar shape: encode the new text, write the vector bytes back with [`HSET`](https://redis.io/docs/latest/commands/hset), and the HNSW index reflects the change on the next query without a rebuild."},{"id":"the-recommender-helper","title":"The recommender helper","role":"content","text":"The `RedisRecommender` class wraps the Redis Search index and the retrieval flow\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/recommendation-engine/ruby/recommender.rb)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each product is one Redis Hash. The vector field is raw little-endian `float32` bytes — no JSON wrapping — because the Redis Search vector encoding expects exactly that. In Ruby, `Array#pack('e*')` packs a `Float` array into the on-wire form, and `String#unpack('e*')` decodes it back; redis-rb hands binary replies back as `ASCII-8BIT` strings, so no decoder configuration is needed.\n\n[code example]\n\nThe Redis Search index schema treats every field as queryable in its natural type:\n\n[code example]\n\n`redis-rb` 5.x does not ship typed bindings for the Redis Search module, so the helper drives `FT.CREATE`, `FT.SEARCH`, `FT.INFO`, `FT.DROPINDEX`, and `FT.TAGVALS` through the raw `redis.call(...)` escape hatch. The replies come back as nested Ruby Arrays, which the helper walks pair-by-pair into hashes; everything else in the file uses the typed `hset` / `hget` / `hmget` / `hincrbyfloat` / `pipelined` wrappers.\n\nPer-user state is a separate hash. The session vector is stored as raw `float32` bytes the same way; affinity counters are stored as plain numeric strings, one field per category, prefixed with `aff:` so they don't collide with anything else.\n\n[code example]"},{"id":"the-query","title":"The query","role":"content","text":"The KNN clause is a hybrid query: a pre-filter expression in parentheses, then `=>[KNN k @embedding $vec]`. With `DIALECT 2`, Redis applies the filter first and then KNN-ranks only the matching documents.\n\n[code example]\n\nWhen there's no filter, the pre-filter clause is just `(*)`. `vector_score` is the cosine *distance* (0 means identical, 2 means opposite), so the result is sorted ascending and the top row is the closest candidate to the query. TAG values are backslash-escaped before being interpolated into a `@tag:{...}` clause so a value that happens to contain a space, hyphen, or other Redis Search syntax character can't accidentally close the brace or inject an additional clause."},{"id":"the-catalogue-builder","title":"The catalogue builder","role":"content","text":"Item vectors are pre-computed once and stored in `catalog.json` so the demo server can boot quickly. `build_catalog.rb` is the reference for how to do that — and is the script you'd adapt for a real catalogue ingestion pipeline\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/recommendation-engine/ruby/build_catalog.rb)):\n\n[code example]\n\nIn production the equivalent of this script lives in an offline pipeline: embed once on catalogue updates and ship the vectors into Redis with [`HSET`](https://redis.io/docs/latest/commands/hset). The serving tier still embeds the *query* on each request, but that's usually fronted by a dedicated model server or batched at the API gateway rather than co-located with the data tier as it is in this demo."},{"id":"the-interactive-demo","title":"The interactive demo","role":"content","text":"`demo_server.rb` runs a WEBrick HTTP server with one demo user (`demo`). The HTML page lets you:\n\n* Type a natural-language query and toggle filters: TAG (category, brand, in-stock), NUMERIC (price range, rating), and TEXT (the **Description contains** field, a phrase pre-filter against the `description` text index).\n* Toggle session blending and category-affinity re-ranking independently to see what each layer contributes.\n* Click any product card to record a click into the session. The page re-renders the user features panel immediately — the click wrote to the user features hash, and the next search reads that hash to fold the update in.\n* Refresh a single product's embedding with new text and watch the ranking change on the next query.\n\nThe server holds one `LocalEmbedder` instance and one `RedisRecommender` for the lifetime of the process. Endpoints:\n\n| Endpoint                | What it does                                                                |\n|-------------------------|-----------------------------------------------------------------------------|\n| `GET  /state`           | Index info, user features, full catalogue listing.                          |\n| `POST /search`          | Embed the query, run `FT.SEARCH` with filters + KNN, optionally re-rank.    |\n| `POST /click`           | Record a click for the demo user: update session vector and affinity.       |\n| `POST /reset-user`      | Drop the user features hash.                                                |\n| `POST /reset-index`     | Drop the index and documents and re-seed from `catalog.json`.               |\n| `POST /refresh-embedding` | Embed new text and overwrite one product's vector with `HSET`.            |"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* Redis 7.x or later with the Redis Search module loaded (Redis Stack, or Redis 8 with Search).\n* Ruby 3.2 or later. `informers` pulls in the `onnxruntime` gem, which requires Ruby >= 3.2. The demo also uses `webrick`, which was removed from Ruby's standard library in Ruby 3.0 and is declared in the Gemfile. On older macOS releases the system Ruby at `/usr/bin/ruby` is Ruby 2.6 and won't work; install a newer Ruby with [Homebrew](https://docs.brew.sh/Homebrew-and-Python#virtualenv) (`brew install ruby`, then run `bundle` from `/opt/homebrew/opt/ruby/bin`), [`rbenv`](https://github.com/rbenv/rbenv), or [`asdf`](https://asdf-vm.com/).\n* `redis-rb` 5.x for the typed core commands and the `redis.call(...)` raw escape hatch for the Redis Search calls.\n* `informers` (the Ankane ONNX-runtime port of sentence-transformers) for the local text embedder. The first call downloads `sentence-transformers/all-MiniLM-L6-v2` (~80 MB) into the local Hugging Face cache."},{"id":"run-the-demo-locally","title":"Run the demo locally","role":"content","text":"1. Clone the [`redis/docs`](https://github.com/redis/docs) repository and change into the example\n   directory:\n\n   [code example]\n\n2. Install the gems:\n\n   [code example]\n\n3. Make sure a Redis instance with the Redis Search module is running locally on\n   port 6379. [Redis Stack](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack) or\n   [Redis 8 with Search](https://redis.io/docs/latest/develop/ai/search-and-query) both work.\n\n4. Generate the catalogue with pre-computed embeddings. The first run downloads the\n   `all-MiniLM-L6-v2` model (~80 MB) into the local Hugging Face cache:\n\n   [code example]\n\n5. Start the demo server:\n\n   [code example]\n\n6. Open <http://localhost:8085> and try some queries:\n\n   * **\"insulated down jacket for cold weather\"** — filtered to `outerwear`, in-stock only.\n   * **\"comfortable shoes for trail running\"** — filtered to `footwear`.\n   * Add **Description contains: waterproof** to either query above to see a TEXT pre-filter\n     combine with the KNN.\n   * Click a couple of products to seed a session, then re-run the same query\n     with **Blend session vector into query** on and watch the ranking shift.\n   * Use **Refresh embedding** to change a product's vector — for example,\n     change the Alpine down parka's text to \"heavy duty arctic expedition parka\n     with hood\" and re-run the jacket query to see the result move.\n\nThe server is read/write against your local Redis. The default index name is `recommend:idx` and product keys live under `product:`. Pass `--no-reset` to keep an existing index across restarts, `--redis-host` / `--redis-port` to point at a different Redis, or `--index-name` / `--key-prefix` to isolate the demo from any other Search index on the same instance."}],"examples":[{"id":"the-recommender-helper-ex0","language":"ruby","code":"require 'redis'\nrequire_relative 'embeddings'\nrequire_relative 'recommender'\n\n# redis-rb returns binary replies as ASCII-8BIT strings automatically,\n# so the embedding bytes round-trip cleanly with no extra flags.\nredis = Redis.new(host: 'localhost', port: 6379)\nrecommender = RedisRecommender.new(redis: redis, index_name: 'recommend:idx')\nembedder = LocalEmbedder.new  # sentence-transformers/all-MiniLM-L6-v2\n\n# One-time index setup (idempotent).\nrecommender.create_index\n\n# Embed the natural-language query.\nquery_vec = embedder.encode_one('warm waterproof jacket for hiking')\n\n# Retrieval: KNN with structured pre-filter in one round trip.\n# Filters combine TAG (category, brand, in_stock_only), NUMERIC\n# (price range, rating), and TEXT (text_match against text_field) --\n# Redis applies them all in front of the KNN.\ncandidates = recommender.candidate_retrieve(\n  query_vec,\n  category: 'outerwear',\n  in_stock_only: true,\n  min_price: 50,\n  max_price: 200,\n  text_match: 'waterproof',   # TEXT pre-filter on @description\n  k: 10,\n)\n\n# Record a click -- updates the user's session vector and category\n# affinity atomically; the next call to candidate_retrieve sees it.\nrecommender.record_click('alice', 'p001')\n\n# Pull user features so the next retrieval can blend the session\n# vector into the query and apply the category-affinity re-rank.\nfeatures = recommender.get_user_features('alice')\ncandidates = recommender.candidate_retrieve(\n  query_vec,\n  category: 'outerwear',\n  in_stock_only: true,\n  k: 10,\n  session_vec: features[:session_vec],\n  session_weight: 0.3,\n)\ncandidates = recommender.rerank(candidates, features, affinity_weight: 0.15)\n\n# Hot embedding refresh -- overwrite the vector for one product; the\n# HNSW index reflects the change on the next FT.SEARCH.\nnew_vector = embedder.encode_one('heavy-duty arctic expedition parka')\nrecommender.refresh_embedding('p001', new_vector)","section_id":"the-recommender-helper"},{"id":"data-model-ex0","language":"text","code":"product:p001\n  name=Alpine down parka\n  description=Heavyweight 800-fill goose down parka...\n  category=outerwear\n  brand=northpeak\n  price=289.0\n  rating=4.7\n  in_stock=true\n  embedding=<384 x float32 little-endian bytes>","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"FT.CREATE recommend:idx\n  ON HASH PREFIX 1 product:\n  SCHEMA\n    name        TEXT\n    description TEXT\n    category    TAG\n    brand       TAG\n    in_stock    TAG\n    price       NUMERIC SORTABLE\n    rating      NUMERIC SORTABLE\n    embedding   VECTOR HNSW 6 TYPE FLOAT32 DIM 384 DISTANCE_METRIC COSINE","section_id":"data-model"},{"id":"data-model-ex2","language":"text","code":"user:alice:features\n  session_vec=<384 x float32 little-endian bytes>\n  aff:outerwear=2.0\n  aff:footwear=1.0\n  last_clicked_id=p015\n  last_clicked_category=footwear\n  clicks=3","section_id":"data-model"},{"id":"the-query-ex0","language":"text","code":"FT.SEARCH recommend:idx\n  \"(@category:{outerwear} @in_stock:{true} @price:[50.0 200.0])\n     =>[KNN 10 @embedding $vec AS vector_score]\"\n  PARAMS 2 vec <384-float32-bytes>\n  SORTBY vector_score\n  RETURN 8 name description category brand price rating in_stock vector_score\n  DIALECT 2","section_id":"the-query"},{"id":"the-catalogue-builder-ex0","language":"ruby","code":"require_relative 'embeddings'\n\nCATALOG = [\n  { 'id' => 'p001', 'name' => 'Alpine down parka',\n    'description' => 'Heavyweight 800-fill goose down parka...',\n    'category' => 'outerwear', 'brand' => 'northpeak',\n    'price' => 289.00, 'in_stock' => true, 'rating' => 4.7 },\n  # ... rest of the catalogue ...\n]\n\nembedder = LocalEmbedder.new\nvectors = embedder.encode_many(\n  CATALOG.map { |p| \"#{p['name']}. #{p['description']}\" }\n)\n# Each vector is 384 float32s, packed and written into catalog.json\n# alongside the structured fields. The demo server reads that file at\n# startup and HSETs every product into Redis.","section_id":"the-catalogue-builder"},{"id":"run-the-demo-locally-ex0","language":"bash","code":"git clone https://github.com/redis/docs.git\n   cd docs/content/develop/use-cases/recommendation-engine/ruby","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex1","language":"bash","code":"bundle install","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex2","language":"bash","code":"bundle exec ruby build_catalog.rb","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex3","language":"bash","code":"bundle exec ruby demo_server.rb","section_id":"run-the-demo-locally"}]}
{"schema_version":2,"id":"develop/use-cases/recommendation-engine/rust","title":"Redis recommendation engine with redis-rs","url":"https://redis.io/docs/latest/develop/use-cases/recommendation-engine/rust/","summary":"Build a Redis-backed recommendation engine in Rust with redis-rs and fastembed","aliases":["/develop/use-cases/recommendation-engine/redis-rs"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-17T10:28:27+01:00","children":[],"page_type":"content","content_hash":"28d8c80c52a29c81f4c1e2387d284f741b4ff4d7a04a35e5d12dd6541b22855c","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a small Redis-backed product recommendation service in Rust with [`redis-rs`](https://redis.io/docs/latest/develop/clients) and the [`fastembed`](https://crates.io/crates/fastembed) crate (an ONNX-runtime-backed sentence-transformers wrapper). It includes a local web server built on `axum` and `tokio` so you can embed a natural-language query, run a KNN retrieval with structured pre-filters in one round trip, feed clicks back as a session signal, and watch the next recommendation incorporate them immediately."},{"id":"overview","title":"Overview","role":"overview","text":"Each product is stored as a single Redis [Hash](https://redis.io/docs/latest/develop/data-types/hashes) at `product:<id>`. The hash holds the structured metadata (name, description, category, brand, price, rating, in-stock flag) alongside the raw `float32` bytes of a 384-dimensional embedding. A single [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) index covers every field, so one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call with a `KNN` clause does the vector similarity *and* the TAG / NUMERIC / TEXT pre-filtering in the same pass — no cross-store joins.\n\nPer-user state lives in `user:<id>:features`: a session vector written as an exponentially weighted average of recently-clicked item embeddings, plus per-category affinity counters incremented atomically with [`HINCRBYFLOAT`](https://redis.io/docs/latest/commands/hincrbyfloat). [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) does *not* read that hash directly; instead, the application reads it on the next request and passes the session vector to `FT.SEARCH` as the query parameter. The two-step is what lets a click feed the very next recommendation without a batch cycle or cache invalidation.\n\nThat gives you:\n\n* A single round trip for retrieval — vector KNN + structured filters in one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search).\n* Sub-millisecond hot path once the query is embedded; embedding the query is the bottleneck, and that's a model-side cost, not a Redis one.\n* Real-time session signals — a click writes a new session vector and bumps an affinity counter; the next query reads them and folds them in.\n* No-downtime embedding refresh — [`HSET`](https://redis.io/docs/latest/commands/hset) on the vector field, and the HNSW index reflects the change on the next query."},{"id":"how-it-works","title":"How it works","role":"content","text":"There are two distinct paths: a **query path** runs every time the application wants a recommendation, and a **click path** runs every time the user interacts with a product."},{"id":"query-path-per-recommendation-request","title":"Query path (per recommendation request)","role":"content","text":"1. The application calls `embedder.encode_one(query_text)` to turn a natural-language query into a 384-dimensional `Vec<f32>`.\n2. The application reads the user's session vector and affinities from the user features hash. If a session vector exists, it gets blended into the query vector with a tunable weight, so the user's recent clicks pull retrieval toward what they've been engaging with.\n3. `recommender.candidate_retrieve(&query_vec, opts)` runs [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) with a pre-filter clause built from the request's TAG / NUMERIC / TEXT inputs, followed by a `KNN k @embedding $vec` clause. Redis returns up to `k` candidates with the cosine distance to the query (lower is closer).\n4. `recommender.rerank(&mut candidates, &features, 0.15)` subtracts a log-scaled per-category affinity bonus from each candidate's distance and re-sorts the list closest-first. The log scaling keeps repeated clicks from running away with the ranking."},{"id":"click-path-per-user-interaction","title":"Click path (per user interaction)","role":"content","text":"When the user clicks a product, `recommender.record_click(user_id, product_id)` does the following:\n\n1. Reads the clicked item's embedding from its hash.\n2. Reads the user's previous session vector from the user features hash, blends the new click in via an exponentially weighted moving average, and writes the new session vector back with [`HSET`](https://redis.io/docs/latest/commands/hset). This is a read-modify-write — atomic against any single write but not against a concurrent click for the same user; in practice, per-user click streams don't generate the contention to make this matter, and if a deployment does, the read and write can be wrapped in [`WATCH/MULTI/EXEC`](https://redis.io/docs/latest/commands/multi) or a small Lua script.\n3. Bumps the per-category affinity counter with [`HINCRBYFLOAT`](https://redis.io/docs/latest/commands/hincrbyfloat) — atomic, no read needed — and the click count with [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby).\n\nThe three writes ship in a single pipelined round trip after the read; the next query path picks both changes up the next time it reads the user features hash.\n\nRefreshing an item's embedding follows a similar shape: encode the new text, write the vector bytes back with [`HSET`](https://redis.io/docs/latest/commands/hset), and the HNSW index reflects the change on the next query without a rebuild."},{"id":"the-recommender-helper","title":"The recommender helper","role":"content","text":"The `RedisRecommender` struct wraps the Redis Search index and the retrieval flow\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/recommendation-engine/rust/recommender.rs)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each product is one Redis Hash. The vector field is raw little-endian `float32` bytes — no JSON wrapping — because the Redis Search vector encoding expects exactly that.\n\n[code example]\n\nThe Redis Search index schema treats every field as queryable in its natural type:\n\n[code example]\n\nPer-user state is a separate hash. The session vector is stored as raw `float32` bytes the same way; affinity counters are stored as plain numeric strings, one field per category, prefixed with `aff:` so they don't collide with anything else.\n\n[code example]"},{"id":"the-query","title":"The query","role":"content","text":"The KNN clause is a hybrid query: a pre-filter expression in parentheses, then `=>[KNN k @embedding $vec]`. With `DIALECT 2`, Redis applies the filter first and then KNN-ranks only the matching documents.\n\n[code example]\n\nWhen there's no filter, the pre-filter clause is just `(*)`. `vector_score` is the cosine *distance* (0 means identical, 2 means opposite), so the result is sorted ascending and the top row is the closest candidate to the query."},{"id":"binary-fields-with-redis-rs","title":"Binary fields with `redis-rs`","role":"content","text":"The embedding field is binary, while everything else in the same hash is text. `redis-rs` lets you mix the two in a single `HSET` by building the command by hand and passing each value with its native type — strings stay strings, byte slices go through verbatim:\n\n[code example]\n\n`FT.SEARCH` parameter values are passed the same way — pass a `&[u8]` for the `vec` parameter and `redis-rs` includes it in the command body without any encoding. The recommender pipelines all 37 catalog `HSET`s into a single round trip on the seed path.\n\nWhen reading binary fields back, ask for `Vec<u8>` (or `Option<Vec<u8>>`) explicitly. The recommender uses `HGETALL` and decodes each field by name so it can route binary `session_vec` separately from the text affinity counters living in the same hash."},{"id":"the-catalog-builder","title":"The catalog builder","role":"content","text":"Item vectors are pre-computed once and stored in `catalog.json` so the demo server can boot quickly. `build_catalog.rs` is the reference for how to do that — and is the code you'd adapt for a real catalog ingestion pipeline\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/recommendation-engine/rust/build_catalog.rs)):\n\n[code example]\n\nIn production the equivalent of this script lives in an offline pipeline: embed once on catalog updates and ship the vectors into Redis with [`HSET`](https://redis.io/docs/latest/commands/hset). The serving tier still embeds the *query* on each request, but that's usually fronted by a dedicated model server or batched at the API gateway rather than co-located with the data tier as it is in this demo."},{"id":"the-interactive-demo","title":"The interactive demo","role":"content","text":"`demo_server.rs` runs an `axum` HTTP server on top of `tokio` with one demo user (`demo`). The HTML page lets you:\n\n* Type a natural-language query and toggle filters: TAG (category, brand, in-stock), NUMERIC (price range, rating), and TEXT (the **Description contains** field, a phrase pre-filter against the `description` text index).\n* Toggle session blending and category-affinity re-ranking independently to see what each layer contributes.\n* Click any product card to record a click into the session. The page re-renders the user features panel immediately — the click wrote to the user features hash, and the next search reads that hash to fold the update in.\n* Refresh a single product's embedding with new text and watch the ranking change on the next query.\n\nThe server holds one `LocalEmbedder` instance and one `RedisRecommender` for the lifetime of the process. Embedding inference runs on a dedicated blocking task (`tokio::task::spawn_blocking`) so the ONNX call doesn't stall the async runtime. Endpoints:\n\n| Endpoint                  | What it does                                                                |\n|---------------------------|-----------------------------------------------------------------------------|\n| `GET  /state`             | Index info, user features, full catalog listing.                            |\n| `POST /search`            | Embed the query, run `FT.SEARCH` with filters + KNN, optionally re-rank.    |\n| `POST /click`             | Record a click for the demo user: update session vector and affinity.       |\n| `POST /reset-user`        | Drop the user features hash.                                                |\n| `POST /reset-index`       | Drop the index and documents and re-seed from `catalog.json`.               |\n| `POST /refresh-embedding` | Embed new text and overwrite one product's vector with `HSET`.              |"},{"id":"run-the-demo-locally","title":"Run the demo locally","role":"content","text":"1.  Clone the [`redis/docs`](https://github.com/redis/docs) repository and change into the example\n    directory:\n\n    [code example]\n\n2.  Fetch the dependencies and build:\n\n    [code example]\n\n3.  Make sure a Redis instance with the Redis Search module is running locally on\n    port 6379. [Redis Stack](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack) or\n    [Redis 8 with Search](https://redis.io/docs/latest/develop/ai/search-and-query) both work.\n\n4.  Generate the catalog with pre-computed embeddings. The first run downloads the\n    `all-MiniLM-L6-v2` model (~80 MB) into the local Hugging Face cache:\n\n    [code example]\n\n5.  Start the demo server:\n\n    [code example]\n\n6.  Open <http://localhost:8088> and try some queries:\n\n    * **\"insulated down jacket for cold weather\"** — filtered to `outerwear`, in-stock only.\n    * **\"comfortable shoes for trail running\"** — filtered to `footwear`.\n    * Add **Description contains: waterproof** to either query above to see a TEXT pre-filter\n      combine with the KNN.\n    * Click a couple of products to seed a session, then re-run the same query\n      with **Blend session vector into query** on and watch the ranking shift.\n    * Use **Refresh embedding** to change a product's vector — for example,\n      change the Alpine down parka's text to \"heavy duty arctic expedition parka\n      with hood\" and re-run the jacket query to see the result move.\n\nThe server is read/write against your local Redis. The default index name is `recommend:idx` and product keys live under `product:`. Pass `--no-reset` to keep an existing index across restarts, or `--redis-host` / `--redis-port` to point at a different Redis. Pass `--index-name` and `--key-prefix` to run alongside another port's demo without colliding."}],"examples":[{"id":"the-recommender-helper-ex0","language":"rust","code":"use redis::aio::ConnectionManager;\nuse redis::Client;\n\nmod catalog_seed;\nmod embeddings;\nmod recommender;\n\nuse embeddings::LocalEmbedder;\nuse recommender::{FilterOptions, RedisRecommender, RetrieveOptions};\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn std::error::Error>> {\n    let client = Client::open(\"redis://localhost:6379/\")?;\n    let conn = ConnectionManager::new(client).await?;\n    let recommender = RedisRecommender::new(conn)\n        .with_index_name(\"recommend:idx\".to_string());\n\n    // One-time index setup (idempotent).\n    recommender.create_index().await?;\n\n    let embedder = LocalEmbedder::new()?;  // sentence-transformers/all-MiniLM-L6-v2\n\n    // Embed the natural-language query.\n    let query_vec = embedder.encode_one(\"warm waterproof jacket for hiking\")?;\n\n    // Retrieval: KNN with structured pre-filter in one round trip.\n    // Filters combine TAG (category, brand, in_stock_only), NUMERIC\n    // (min_price/max_price, min_rating), and TEXT (text_match against\n    // text_field) — Redis applies them all in front of the KNN.\n    let candidates = recommender.candidate_retrieve(&query_vec, RetrieveOptions {\n        filter: FilterOptions {\n            category: Some(\"outerwear\".to_string()),\n            in_stock_only: true,\n            min_price: Some(50.0),\n            max_price: Some(200.0),\n            text_match: Some(\"waterproof\".to_string()),  // TEXT pre-filter on @description\n            ..Default::default()\n        },\n        k: 10,\n        ..Default::default()\n    }).await?;\n\n    // Record a click — updates the user's session vector and category\n    // affinity atomically; the next call to candidate_retrieve sees it.\n    let _ = recommender.record_click(\"alice\", \"p001\").await?;\n\n    // Pull user features so the next retrieval can blend the session\n    // vector into the query and apply the category-affinity re-rank.\n    let features = recommender.get_user_features(\"alice\").await?;\n    let mut candidates = recommender.candidate_retrieve(&query_vec, RetrieveOptions {\n        filter: FilterOptions {\n            category: Some(\"outerwear\".to_string()),\n            in_stock_only: true,\n            ..Default::default()\n        },\n        k: 10,\n        session_vec: features.session_vec.clone(),\n        session_weight: 0.3,\n    }).await?;\n    recommender.rerank(&mut candidates, &features, 0.15);\n\n    // Hot embedding refresh — overwrite the vector for one product;\n    // the HNSW index reflects the change on the next FT.SEARCH.\n    let new_vec = embedder.encode_one(\"heavy-duty arctic expedition parka\")?;\n    recommender.refresh_embedding(\"p001\", &new_vec).await?;\n\n    Ok(())\n}","section_id":"the-recommender-helper"},{"id":"data-model-ex0","language":"text","code":"product:p001\n  name=Alpine down parka\n  description=Heavyweight 800-fill goose down parka...\n  category=outerwear\n  brand=northpeak\n  price=289.0\n  rating=4.7\n  in_stock=true\n  embedding=<384 × float32 little-endian bytes>","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"FT.CREATE recommend:idx\n  ON HASH PREFIX 1 product:\n  SCHEMA\n    name        TEXT\n    description TEXT\n    category    TAG\n    brand       TAG\n    in_stock    TAG\n    price       NUMERIC SORTABLE\n    rating      NUMERIC SORTABLE\n    embedding   VECTOR HNSW 6 TYPE FLOAT32 DIM 384 DISTANCE_METRIC COSINE","section_id":"data-model"},{"id":"data-model-ex2","language":"text","code":"user:alice:features\n  session_vec=<384 × float32 little-endian bytes>\n  aff:outerwear=2.0\n  aff:footwear=1.0\n  last_clicked_id=p015\n  last_clicked_category=footwear\n  clicks=3","section_id":"data-model"},{"id":"the-query-ex0","language":"text","code":"FT.SEARCH recommend:idx\n  \"(@category:{outerwear} @in_stock:{true} @price:[50 200])\n     =>[KNN 10 @embedding $vec AS vector_score]\"\n  PARAMS 2 vec <384-float32-bytes>\n  SORTBY vector_score\n  RETURN 8 name description category brand price rating in_stock vector_score\n  DIALECT 2","section_id":"the-query"},{"id":"binary-fields-with-redis-rs-ex0","language":"rust","code":"let bytes = embeddings::floats_to_bytes(&vector); // Vec<u8>, little-endian float32s\n\nredis::cmd(\"HSET\")\n    .arg(format!(\"product:{}\", id))\n    .arg(\"name\").arg(\"Alpine down parka\")\n    .arg(\"price\").arg(\"289.0\")\n    .arg(\"embedding\").arg(bytes.as_slice())  // &[u8]\n    .query_async::<i64>(&mut conn).await?;","section_id":"binary-fields-with-redis-rs"},{"id":"the-catalog-builder-ex0","language":"rust","code":"let seed = catalog_seed::catalog_seed();\nlet texts: Vec<String> = seed.iter().map(catalog_seed::embed_text_for).collect();\n\nlet embedder = embeddings::LocalEmbedder::new()?;\nlet vectors = embedder.encode_many(texts)?;\n// Each vector is 384 float32s, packed and written into catalog.json\n// alongside the structured fields. The demo server reads that file at\n// startup and HSETs every product into Redis.","section_id":"the-catalog-builder"},{"id":"run-the-demo-locally-ex0","language":"bash","code":"git clone https://github.com/redis/docs.git\n    cd docs/content/develop/use-cases/recommendation-engine/rust","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex1","language":"bash","code":"cargo build --release","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex2","language":"bash","code":"cargo run --release --bin build_catalog","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex3","language":"bash","code":"cargo run --release --bin demo_server -- --port 8088","section_id":"run-the-demo-locally"}]}
{"schema_version":2,"id":"develop/use-cases/semantic-cache/dotnet","title":"Redis semantic cache with NRedisStack","url":"https://redis.io/docs/latest/develop/use-cases/semantic-cache/dotnet/","summary":"Build a Redis-backed semantic cache for LLM responses in C# with NRedisStack and ONNX Runtime","aliases":["/develop/use-cases/semantic-cache/stackexchange.redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T17:06:11+01:00","children":[],"page_type":"content","content_hash":"2fca6053692cf37c97f8df6fee42c547e61005f4717601b997facf1466bccccf","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a small Redis-backed semantic cache for LLM responses in C# / .NET with [`NRedisStack`](https://redis.io/docs/latest/develop/clients/dotnet/nredisstack) and [ONNX Runtime](https://onnxruntime.ai/) running the [`sentence-transformers/all-MiniLM-L6-v2`](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2) encoder locally. It includes a local web server built with the BCL's standard `System.Net.HttpListener` so you can send paraphrased prompts at a mock LLM, watch the cache decide hit or miss, sweep the cosine-distance threshold, and see the cumulative latency and token savings build up."},{"id":"overview","title":"Overview","role":"overview","text":"Each cache entry is stored as a single Redis [Hash](https://redis.io/docs/latest/develop/data-types/hashes) at `cache:<id>`. The hash holds the original prompt, the LLM's response, the raw `float32` bytes of a 384-dimensional embedding of the prompt, and metadata fields — tenant, locale, model version, safety flag — plus a `created_ts` and a `hit_count`. A single [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) index covers the embedding field and every metadata field, so one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call with a `KNN` clause does the vector lookup *and* the TAG pre-filter in the same round trip — no cross-store joins.\n\nThe lookup is thresholded: [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) always returns the nearest entry that satisfies the filters, but the application only serves it as a hit when the reported cosine distance is at or below `DistanceThreshold`. Anything further away is treated as a miss; the caller runs the LLM and writes the new prompt, response, and embedding back to the same key pattern with a TTL.\n\nThe embedder is [ONNX Runtime](https://onnxruntime.ai/) loading the ONNX export of [`sentence-transformers/all-MiniLM-L6-v2`](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2) (mirrored at [`Xenova/all-MiniLM-L6-v2`](https://huggingface.co/Xenova/all-MiniLM-L6-v2) on Hugging Face) paired with the `BertTokenizer` from [`Microsoft.ML.Tokenizers`](https://www.nuget.org/packages/Microsoft.ML.Tokenizers). This is the same 384-dimensional encoder the [Python example](https://redis.io/docs/latest/develop/use-cases/semantic-cache/redis-py), the [Node.js example](https://redis.io/docs/latest/develop/use-cases/semantic-cache/nodejs), and the [Java/Jedis example](https://redis.io/docs/latest/develop/use-cases/semantic-cache/java-jedis) use. Embeddings produced by the implementations are semantically equivalent — paraphrase distances differ only at the second or third decimal place — so a cache populated by one demo can be queried by another against the same Redis instance.\n\nThat gives you:\n\n* A single round trip for lookup — vector KNN + metadata pre-filter in one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search).\n* Tens of milliseconds on a hit vs. a multi-second LLM call on a miss; the embedding step is the bottleneck either way, and that's a model-side cost, not a Redis one.\n* Tenant, locale, and model-version isolation enforced inside the query, not in application code — a write under one tenant cannot be served to another.\n* Bounded memory: every entry has an [`EXPIRE`](https://redis.io/docs/latest/commands/expire) TTL, and a database-level [eviction policy](https://redis.io/docs/latest/develop/reference/eviction) (LRU / LFU) caps the cache size under pressure."},{"id":"how-it-works","title":"How it works","role":"content","text":"A query goes through three stages: **embed**, **lookup**, and (on a miss) **call the LLM and write back**."},{"id":"hit-path-the-goal","title":"Hit path (the goal)","role":"content","text":"1. The application calls `embedder.EncodeOne(prompt)` to turn the incoming text into a 384-element `float[]`.\n2. `cache.Lookup(queryVec, tenant, locale, modelVersion, \"ok\", threshold)` runs [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) with a TAG pre-filter and a `KNN 1` clause. Redis returns the closest cached prompt that satisfies the filters along with its cosine distance.\n3. If the distance is at or below the threshold, the cache returns a `CacheHit` record containing the cached response. The helper also runs an [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby) on `hit_count` and an [`EXPIRE`](https://redis.io/docs/latest/commands/expire) refresh inside a [`MULTI/EXEC`](https://redis.io/docs/latest/commands/multi) (built with `IDatabase.CreateTransaction()`), so a frequently used answer keeps its TTL and the demo UI can see which entries are load-bearing.\n4. The LLM is not called at all. The application returns the cached response to the user."},{"id":"miss-path","title":"Miss path","role":"content","text":"When the distance is above the threshold — or there is no candidate in scope at all — the helper returns a `CacheMiss` record instead, carrying the distance of the nearest candidate (if any) for logging. The application then:\n\n1. Calls the LLM with the prompt.\n2. Calls `cache.Put(prompt, response, embedding, tenant, locale, modelVersion, ...)`. The same embedding the lookup used is reused — no re-encode. The helper writes the Hash with [`HSET`](https://redis.io/docs/latest/commands/hset) and an [`EXPIRE`](https://redis.io/docs/latest/commands/expire) TTL inside a single [`MULTI/EXEC`](https://redis.io/docs/latest/commands/multi) so the entry never lands without a TTL on a partial failure.\n3. Returns the LLM's response to the user. The next semantically similar prompt under the same metadata scope will be a hit."},{"id":"the-cache-helper","title":"The cache helper","role":"content","text":"The `RedisSemanticCache` class wraps the Redis Search index and the lookup / write flow\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/semantic-cache/dotnet/RedisSemanticCache.cs)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each cache entry is one Redis Hash. The vector field is raw little-endian `float32` bytes — no JSON wrapping — because the Redis Search vector encoding expects exactly that. The helper packs the `float[]` with `BinaryPrimitives.WriteSingleLittleEndian` so the byte order is pinned to little-endian regardless of host architecture; every supported .NET runtime today is little-endian, and the explicit encoding matches the bytes the Python, Node, Go, and Java ports write.\n\n[code example]\n\nThe Redis Search index schema treats every field as queryable in its natural type:\n\n[code example]"},{"id":"the-query","title":"The query","role":"content","text":"The lookup is a hybrid query: a TAG pre-filter expression in parentheses, then `=>[KNN 1 @embedding $vec]`. With `DIALECT 2`, Redis applies the filter first and KNN-ranks only the matching documents. In NRedisStack:\n\n[code example]\n\n`distance` is the cosine *distance* (0 means identical, 2 means opposite). The result is sorted ascending, so the top row is the closest candidate. The application inspects `distance` against the threshold and decides hit or miss in user code — Redis returns the row either way, and treating it as a hit or a miss is a policy decision the cache helper owns, not a server-side filter."},{"id":"the-mock-llm","title":"The mock LLM","role":"content","text":"To make the latency and token savings visible without requiring an API key, `MockLLM.cs` provides a deterministic stand-in\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/semantic-cache/dotnet/MockLLM.cs)):\n\n[code example]\n\nThe mock sleeps for the configured latency, then keyword-matches against a small FAQ table to produce an answer. The deliberate slowness is what makes a hit visibly cheaper than a miss in the demo. In production code, you would replace `MockLLM` with your real client of choice — an HTTP call to OpenAI, Anthropic, an Azure OpenAI deployment, a self-hosted vLLM endpoint, anything — without changing the cache helper."},{"id":"pre-seeding-the-cache","title":"Pre-seeding the cache","role":"content","text":"In a real deployment the cache fills up organically: a first-time question is a miss, the LLM answers, and the response is written back. For the demo, `SeedCache.cs` pre-loads a small set of canonical FAQ prompts so the very first query lands on a hit\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/semantic-cache/dotnet/SeedCache.cs)):\n\n[code example]\n\nThe seed list stores the canonical phrasing of each question (\"What is your return policy?\"). Paraphrases of any of these prompts (\"How do I return an item?\", \"Can I get a refund?\") embed close to the canonical entry, so the cache lookup serves the stored response without ever calling the model."},{"id":"the-interactive-demo","title":"The interactive demo","role":"content","text":"`Program.cs` runs an HTTP server built on the BCL's `System.Net.HttpListener` — no ASP.NET Core, no Kestrel, no Minimal API. The HTML page lets you:\n\n* Type a prompt and toggle metadata: tenant, locale, model version. Each combination is a separate cache namespace inside the same index.\n* Slide the cosine-distance threshold and see hits flip to misses (and back) on the same prompt, with the actual distance reported on each query.\n* Submit with **Ask** to run the full hit-or-miss path (calls the LLM on a miss, writes the answer back). Submit with **Lookup only (no LLM)** to sweep the threshold against a fixed prompt without polluting the cache.\n* Watch the cumulative panel build up: total queries, cache hits, cache misses, hit ratio, tokens not spent, LLM milliseconds not waited.\n* Inspect every cached entry, including remaining TTL and total hit count, and drop individual entries to simulate eviction.\n\nThe server holds one `LocalEmbedder`, one `RedisSemanticCache`, and one `MockLLM` for the lifetime of the process. The HTML page is shared with the Python, Node.js, Go, and Java demos; `index.html` ships next to the binary via a `<None CopyToOutputDirectory>` entry in the `.csproj`, so `dotnet bin/Release/net8.0/SemanticCacheDemo.dll` works from any working directory. Endpoints:\n\n| Endpoint        | What it does                                                                  |\n|-----------------|-------------------------------------------------------------------------------|\n| `GET  /state`   | Index info and the full list of cached entries.                               |\n| `POST /query`   | Embed the prompt, run `FT.SEARCH`, on miss call the LLM and write back.       |\n| `POST /reset`   | Drop every cached entry and re-seed from the FAQ list.                        |\n| `POST /drop`    | Delete a single cached entry by id.                                           |"},{"id":"run-the-demo-locally","title":"Run the demo locally","role":"content","text":"1.  Clone the [`redis/docs`](https://github.com/redis/docs) repository and change into the example\n    directory:\n\n    [code example]\n\n2.  Make sure a Redis instance with the Redis Search module is running locally on\n    port 6379. [Redis Stack](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack) or\n    [Redis 8 with Search](https://redis.io/docs/latest/develop/ai/search-and-query) both work.\n\n3.  Build the project. This pulls `NRedisStack`, `Microsoft.ML.OnnxRuntime`, and\n    `Microsoft.ML.Tokenizers` from NuGet. The first build takes a couple of minutes:\n\n    [code example]\n\n4.  Run the demo. The first run downloads the ONNX export of\n    `sentence-transformers/all-MiniLM-L6-v2` (~90 MB) and the matching BERT\n    `vocab.txt` into a local `model_cache/` directory next to the binary; every\n    subsequent run is offline:\n\n    [code example]\n\n    Or run the built binary directly:\n\n    [code example]\n\n5.  Open <http://localhost:8092> and try some queries:\n\n    * **\"What is your return policy?\"** — exact match against the seed, distance ≈ 0,\n      hit at any threshold.\n    * **\"How fast is delivery?\"** — paraphrase of the shipping seed; distance\n      around 0.30, hit at the default threshold of 0.5.\n    * **\"How do I return an item?\"** — slightly looser paraphrase of the returns\n      seed; distance around 0.49, still a hit at the default threshold. Slide\n      the threshold down to 0.4 to see this one flip to a miss.\n    * **\"What payment methods do you accept?\"** — unrelated to anything in the\n      seed; distance > 0.6, so you'll see a miss, the mock LLM kicks in for\n      ~1.5 s, the new answer is cached, and a follow-up of the same question\n      is now an immediate hit.\n    * Switch the **Tenant** dropdown to `globex` or `initech` and re-ask any\n      seeded question — the result flips to a miss because the cache entries\n      live under `acme`. That's the metadata pre-filter at work inside `FT.SEARCH`.\n\nThe server is read/write against your local Redis. The default index name is `semcache:idx` and entry keys live under `cache:`. Flags mirror the Python, Node, Go, and Java demos: `--no-reset` to keep an existing cache across restarts, `--threshold` to change the default cosine-distance cutoff, `--llm-latency-ms` to make the mock LLM faster or slower for the demo, or `--port` to listen on a different port."}],"examples":[{"id":"the-cache-helper-ex0","language":"csharp","code":"using NRedisStack.RedisStackCommands;\nusing StackExchange.Redis;\nusing SemanticCacheDemo;\n\nvar mux = ConnectionMultiplexer.Connect(\"localhost:6379\");\nvar db = mux.GetDatabase();\n\nvar embedder = await LocalEmbedder.CreateAsync();   // sentence-transformers/all-MiniLM-L6-v2\n\nvar cache = new RedisSemanticCache(\n    db,\n    indexName: \"semcache:idx\",\n    keyPrefix: \"cache:\",\n    vectorDim: 384,\n    distanceThreshold: 0.5,    // cosine distance, lower = stricter\n    defaultTtlSeconds: 3600);\n\n// One-time index setup (idempotent).\ncache.CreateIndex();\n\n// 1) Embed the prompt.\nstring prompt = \"How do I return an item?\";\nfloat[] queryVec = embedder.EncodeOne(prompt);\n\n// 2) Look up under a metadata scope. The TAG filter and the KNN\n//    travel together in one FT.SEARCH.\nvar result = cache.Lookup(\n    queryVec, tenant: \"acme\", locale: \"en\",\n    modelVersion: \"gpt-4.5-2026\");\n\nstring response;\nif (result is CacheHit hit)\n{\n    response = hit.Response;\n    Console.WriteLine($\"hit ({hit.Distance:F3}): {response}\");\n}\nelse\n{\n    // 3a) Miss — call the LLM. (Use your real client here.)\n    response = CallLlm(prompt);\n\n    // 3b) Cache the new entry. Reuses the same embedding bytes the\n    //     lookup used, so we don't pay the encoder twice.\n    cache.Put(\n        prompt: prompt,\n        response: response,\n        embedding: queryVec,\n        tenant: \"acme\",\n        locale: \"en\",\n        modelVersion: \"gpt-4.5-2026\");\n}","section_id":"the-cache-helper"},{"id":"data-model-ex0","language":"text","code":"cache:7c3f8a1b9e02\n  prompt=How do I return an item?\n  response=You can return any unworn item within 30 days...\n  tenant=acme\n  locale=en\n  model_version=gpt-4.5-2026\n  safety=ok\n  created_ts=1715990400.123\n  hit_count=4\n  embedding=<384 × float32 little-endian bytes>","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"FT.CREATE semcache:idx\n  ON HASH PREFIX 1 cache:\n  SCHEMA\n    prompt         TEXT\n    response       TEXT\n    tenant         TAG\n    locale         TAG\n    model_version  TAG\n    safety         TAG\n    created_ts     NUMERIC SORTABLE\n    hit_count      NUMERIC SORTABLE\n    embedding      VECTOR HNSW 6 TYPE FLOAT32 DIM 384 DISTANCE_METRIC COSINE","section_id":"data-model"},{"id":"the-query-ex0","language":"csharp","code":"// `.` and `-` (and other punctuation) are TAG-value syntax in Redis\n// Search and must be backslash-escaped — `gpt-4.5-2026` raw would\n// be parsed as three tokens. The helper's EscapeTagValue does this\n// for every TAG value; the literal below shows what the parser\n// actually sees on the wire.\nvar query = new Query(\n        \"(@tenant:{acme} @locale:{en} @model_version:{gpt\\\\-4\\\\.5\\\\-2026} @safety:{ok})\"\n        + \"=>[KNN 1 @embedding $vec AS distance]\")\n    .ReturnFields(\n        \"prompt\", \"response\", \"tenant\", \"locale\",\n        \"model_version\", \"hit_count\", \"distance\")\n    .SetSortBy(\"distance\", ascending: true)\n    .Limit(0, 1)\n    .AddParam(\"vec\", LocalEmbedder.ToBytes(queryVec))\n    .Dialect(2);\n\nvar result = db.FT().Search(\"semcache:idx\", query);","section_id":"the-query"},{"id":"the-mock-llm-ex0","language":"csharp","code":"using SemanticCacheDemo;\n\nvar llm = new MockLLM(modelVersion: \"gpt-4.5-2026\", latencyMs: 1500.0);\nvar response = llm.Complete(\"What is your return policy?\");\n// response.Text         — the templated answer text\n// response.LatencyMs    — wall-clock time the call took\n// response.TotalTokens  — estimated prompt + completion tokens","section_id":"the-mock-llm"},{"id":"pre-seeding-the-cache-ex0","language":"csharp","code":"cache.CreateIndex();\nSeedCache.Seed(cache, embedder, tenant: \"acme\", locale: \"en\");","section_id":"pre-seeding-the-cache"},{"id":"run-the-demo-locally-ex0","language":"bash","code":"git clone https://github.com/redis/docs.git\n    cd docs/content/develop/use-cases/semantic-cache/dotnet","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex1","language":"bash","code":"dotnet build -c Release","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex2","language":"bash","code":"dotnet run -c Release","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex3","language":"bash","code":"dotnet bin/Release/net8.0/SemanticCacheDemo.dll","section_id":"run-the-demo-locally"}]}
{"schema_version":2,"id":"develop/use-cases/semantic-cache/go","title":"Redis semantic cache with go-redis","url":"https://redis.io/docs/latest/develop/use-cases/semantic-cache/go/","summary":"Build a Redis-backed semantic cache for LLM responses in Go with go-redis and Hugot","aliases":["/develop/use-cases/semantic-cache/go-redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T17:06:11+01:00","children":[],"page_type":"content","content_hash":"b79e2a44d52ddeb81ab4d752ac5cfb98676de5cf425327d799071f38be77845c","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a small Redis-backed semantic cache for LLM responses in Go with [`go-redis`](https://redis.io/docs/latest/develop/clients/go) and the [Hugot](https://pkg.go.dev/github.com/knights-analytics/hugot) library. It includes a local web server built with Go's standard `net/http` package so you can send paraphrased prompts at a mock LLM, watch the cache decide hit or miss, sweep the cosine-distance threshold, and see the cumulative latency and token savings build up."},{"id":"overview","title":"Overview","role":"overview","text":"Each cache entry is stored as a single Redis [Hash](https://redis.io/docs/latest/develop/data-types/hashes) at `cache:<id>`. The hash holds the original prompt, the LLM's response, the raw `float32` bytes of a 384-dimensional embedding of the prompt, and metadata fields — tenant, locale, model version, safety flag — plus a `created_ts` and a `hit_count`. A single [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) index covers the embedding field and every metadata field, so one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call with a `KNN` clause does the vector lookup *and* the TAG pre-filter in the same round trip — no cross-store joins.\n\nThe lookup is thresholded: [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) always returns the nearest entry that satisfies the filters, but the application only serves it as a hit when the reported cosine distance is at or below `DistanceThreshold`. Anything further away is treated as a miss; the caller runs the LLM and writes the new prompt, response, and embedding back to the same key pattern with a TTL.\n\nThe embedder is [Hugot](https://pkg.go.dev/github.com/knights-analytics/hugot) running the ONNX-exported [`sentence-transformers/all-MiniLM-L6-v2`](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2) model, which is the same encoder the [Python example](https://redis.io/docs/latest/develop/use-cases/semantic-cache/redis-py) and the [Node.js example](https://redis.io/docs/latest/develop/use-cases/semantic-cache/nodejs) use. Embeddings produced by the three implementations are semantically equivalent — paraphrase distances differ only at the fourth decimal place — so a cache populated by one demo can be queried by the others against the same Redis instance.\n\nThat gives you:\n\n* A single round trip for lookup — vector KNN + metadata pre-filter in one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search).\n* Tens of milliseconds on a hit vs. a multi-second LLM call on a miss; the embedding step is the bottleneck either way, and that's a model-side cost, not a Redis one.\n* Tenant, locale, and model-version isolation enforced inside the query, not in application code — a write under one tenant cannot be served to another.\n* Bounded memory: every entry has an [`EXPIRE`](https://redis.io/docs/latest/commands/expire) TTL, and a database-level [eviction policy](https://redis.io/docs/latest/develop/reference/eviction) (LRU / LFU) caps the cache size under pressure."},{"id":"how-it-works","title":"How it works","role":"content","text":"A query goes through three stages: **embed**, **lookup**, and (on a miss) **call the LLM and write back**."},{"id":"hit-path-the-goal","title":"Hit path (the goal)","role":"content","text":"1. The application calls `embedder.EncodeOne(ctx, prompt)` to turn the incoming text into a 384-element `[]float32`.\n2. `cache.Lookup(ctx, queryVec, LookupParams{Tenant: ..., Locale: ..., ModelVersion: ...})` runs [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) with a TAG pre-filter and a `KNN 1` clause. Redis returns the closest cached prompt that satisfies the filters along with its cosine distance.\n3. If the distance is at or below the threshold, the cache returns a `LookupResult` with the `Hit` field populated. The helper also runs an [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby) on `hit_count` and an [`EXPIRE`](https://redis.io/docs/latest/commands/expire) refresh inside a [`MULTI/EXEC`](https://redis.io/docs/latest/commands/multi), so a frequently used answer keeps its TTL and the demo UI can see which entries are load-bearing.\n4. The LLM is not called at all. The application returns the cached response to the user."},{"id":"miss-path","title":"Miss path","role":"content","text":"When the distance is above the threshold — or there is no candidate in scope at all — the helper returns a `LookupResult` with the `Miss` field populated instead, carrying the distance of the nearest candidate (if any) for logging. The application then:\n\n1. Calls the LLM with the prompt.\n2. Calls `cache.Put(ctx, PutParams{Prompt: ..., Response: ..., Embedding: queryVec, ...})`. The same embedding the lookup used is reused — no re-encode. The helper writes the Hash with [`HSET`](https://redis.io/docs/latest/commands/hset) and an [`EXPIRE`](https://redis.io/docs/latest/commands/expire) TTL inside a single [`MULTI/EXEC`](https://redis.io/docs/latest/commands/multi) so the entry never lands without a TTL on a partial failure.\n3. Returns the LLM's response to the user. The next semantically similar prompt under the same metadata scope will be a hit."},{"id":"the-cache-helper","title":"The cache helper","role":"content","text":"The `RedisSemanticCache` struct wraps the Redis Search index and the lookup / write flow\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/semantic-cache/go/cache.go)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each cache entry is one Redis Hash. The vector field is raw little-endian `float32` bytes — no JSON wrapping — because the Redis Search vector encoding expects exactly that. The helper uses `binary.LittleEndian` rather than `binary.NativeEndian` so the byte layout is independent of the host architecture (every supported Go target is little-endian today, but pinning the encoding makes the docs example portable in principle).\n\n[code example]\n\nThe Redis Search index schema treats every field as queryable in its natural type:\n\n[code example]"},{"id":"the-query","title":"The query","role":"content","text":"The lookup is a hybrid query: a TAG pre-filter expression in parentheses, then `=>[KNN 1 @embedding $vec]`. With `DialectVersion: 2`, Redis applies the filter first and KNN-ranks only the matching documents. In `go-redis`:\n\n[code example]\n\n`distance` is the cosine *distance* (0 means identical, 2 means opposite). The result is sorted ascending, so the top row is the closest candidate. The application inspects `distance` against the threshold and decides hit or miss in user code — Redis returns the row either way, and treating it as a hit or a miss is a policy decision the cache helper owns, not a server-side filter."},{"id":"the-mock-llm","title":"The mock LLM","role":"content","text":"To make the latency and token savings visible without requiring an API key, `mockllm.go` provides a deterministic stand-in\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/semantic-cache/go/mockllm.go)):\n\n[code example]\n\nThe mock sleeps for the configured latency, then keyword-matches against a small FAQ table to produce an answer. The deliberate slowness is what makes a hit visibly cheaper than a miss in the demo. In production code, you would replace `MockLLM` with your real client of choice — an OpenAI Go SDK, an Anthropic SDK, an internal vLLM endpoint, anything — without changing the cache helper."},{"id":"pre-seeding-the-cache","title":"Pre-seeding the cache","role":"content","text":"In a real deployment the cache fills up organically: a first-time question is a miss, the LLM answers, and the response is written back. For the demo, `seedcache.go` pre-loads a small set of canonical FAQ prompts so the very first query lands on a hit\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/semantic-cache/go/seedcache.go)):\n\n[code example]\n\nThe seed list stores the canonical phrasing of each question (\"What is your return policy?\"). Paraphrases of any of these prompts (\"How do I return an item?\", \"Can I get a refund?\") embed close to the canonical entry, so the cache lookup serves the stored response without ever calling the model."},{"id":"the-interactive-demo","title":"The interactive demo","role":"content","text":"`main.go` runs a Go `net/http` server. The HTML page lets you:\n\n* Type a prompt and toggle metadata: tenant, locale, model version. Each combination is a separate cache namespace inside the same index.\n* Slide the cosine-distance threshold and see hits flip to misses (and back) on the same prompt, with the actual distance reported on each query.\n* Submit with **Ask** to run the full hit-or-miss path (calls the LLM on a miss, writes the answer back). Submit with **Lookup only (no LLM)** to sweep the threshold against a fixed prompt without polluting the cache.\n* Watch the cumulative panel build up: total queries, cache hits, cache misses, hit ratio, tokens not spent, LLM milliseconds not waited.\n* Inspect every cached entry, including remaining TTL and total hit count, and drop individual entries to simulate eviction.\n\nThe server holds one `LocalEmbedder`, one `RedisSemanticCache`, and one `MockLLM` for the lifetime of the process. The HTML page is shared with the Python and Node.js demos and is loaded from `index.html` next to `main.go`. Endpoints:\n\n| Endpoint        | What it does                                                                  |\n|-----------------|-------------------------------------------------------------------------------|\n| `GET  /state`   | Index info and the full list of cached entries.                               |\n| `POST /query`   | Embed the prompt, run `FT.SEARCH`, on miss call the LLM and write back.       |\n| `POST /reset`   | Drop every cached entry and re-seed from the FAQ list.                        |\n| `POST /drop`    | Delete a single cached entry by id.                                           |"},{"id":"run-the-demo-locally","title":"Run the demo locally","role":"content","text":"1.  Clone the [`redis/docs`](https://github.com/redis/docs) repository and change into the example\n    directory:\n\n    [code example]\n\n2.  Pull the Go modules:\n\n    [code example]\n\n    This demo uses Hugot's pure-Go inference backend (`hugot.NewGoSession`),\n    so no native ONNX Runtime C library is required. If you'd prefer the\n    higher-throughput ONNX Runtime backend you can swap `NewGoSession` for\n    `NewORTSession` and follow the [ONNX Runtime install\n    instructions](https://onnxruntime.ai/docs/install/) — the rest of the\n    demo is unchanged.\n\n3.  Make sure a Redis instance with the Redis Search module is running locally on\n    port 6379. [Redis Stack](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack) or\n    [Redis 8 with Search](https://redis.io/docs/latest/develop/ai/search-and-query) both work.\n\n4.  Start the demo server. The first run downloads the ONNX-exported\n    `sentence-transformers/all-MiniLM-L6-v2` model into the local `./models`\n    directory:\n\n    [code example]\n\n5.  Open <http://localhost:8088> and try some queries:\n\n    * **\"What is your return policy?\"** — exact match against the seed, distance ≈ 0,\n      hit at any threshold.\n    * **\"How fast is delivery?\"** — paraphrase of the shipping seed; distance\n      around 0.30, hit at the default threshold of 0.5.\n    * **\"How do I return an item?\"** — slightly looser paraphrase of the returns\n      seed; distance around 0.49, still a hit at the default threshold. Slide\n      the threshold down to 0.4 to see this one flip to a miss.\n    * **\"What payment methods do you accept?\"** — unrelated to anything in the\n      seed; distance > 0.6, so you'll see a miss, the mock LLM kicks in for\n      ~1.5 s, the new answer is cached, and a follow-up of the same question\n      is now an immediate hit.\n    * Switch the **Tenant** dropdown to `globex` or `initech` and re-ask any\n      seeded question — the result flips to a miss because the cache entries\n      live under `acme`. That's the metadata pre-filter at work inside `FT.SEARCH`.\n\nThe server is read/write against your local Redis. The default index name is `semcache:idx` and entry keys live under `cache:`. Flags mirror the Python and Node.js demos: `--no-reset` to keep an existing cache across restarts, `--threshold` to change the default cosine-distance cutoff, or `--llm-latency-ms` to make the mock LLM faster or slower for the demo."}],"examples":[{"id":"the-cache-helper-ex0","language":"go","code":"import (\n    \"context\"\n    \"fmt\"\n\n    \"github.com/redis/go-redis/v9\"\n)\n\nctx := context.Background()\nclient := redis.NewClient(&redis.Options{Addr: \"localhost:6379\"})\n\ncache := NewRedisSemanticCache(\n    client,\n    \"semcache:idx\",\n    \"cache:\",\n    384,    // vector dimension\n    0.5,    // cosine distance threshold; lower = stricter\n    3600,   // default TTL in seconds (one hour)\n)\nembedder, _ := NewLocalEmbedder(ctx, \"\", \"\")  // sentence-transformers/all-MiniLM-L6-v2\n\n// One-time index setup (idempotent).\n_ = cache.CreateIndex(ctx)\n\n// 1) Embed the prompt.\nprompt := \"How do I return an item?\"\nqueryVec, _ := embedder.EncodeOne(ctx, prompt)\n\n// 2) Look up under a metadata scope. The TAG filter and the KNN\n//    travel together in one FT.SEARCH.\nresult, _ := cache.Lookup(ctx, queryVec, LookupParams{\n    Tenant:       \"acme\",\n    Locale:       \"en\",\n    ModelVersion: \"gpt-4.5-2026\",\n})\n\nvar response string\nif result.Hit != nil {\n    response = result.Hit.Response\n    fmt.Printf(\"hit (%.3f): %s\\n\", result.Hit.Distance, response)\n} else {\n    // 3a) Miss — call the LLM. (Use your real client here.)\n    response = callLLM(prompt)\n\n    // 3b) Cache the new entry. Reuses the same embedding bytes the\n    //     lookup used, so we don't pay the encoder twice.\n    _, _ = cache.Put(ctx, PutParams{\n        Prompt:       prompt,\n        Response:     response,\n        Embedding:    queryVec,\n        Tenant:       \"acme\",\n        Locale:       \"en\",\n        ModelVersion: \"gpt-4.5-2026\",\n    })\n}","section_id":"the-cache-helper"},{"id":"data-model-ex0","language":"text","code":"cache:7c3f8a1b9e02\n  prompt=How do I return an item?\n  response=You can return any unworn item within 30 days...\n  tenant=acme\n  locale=en\n  model_version=gpt-4.5-2026\n  safety=ok\n  created_ts=1715990400.123\n  hit_count=4\n  embedding=<384 × float32 little-endian bytes>","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"FT.CREATE semcache:idx\n  ON HASH PREFIX 1 cache:\n  SCHEMA\n    prompt         TEXT\n    response       TEXT\n    tenant         TAG\n    locale         TAG\n    model_version  TAG\n    safety         TAG\n    created_ts     NUMERIC SORTABLE\n    hit_count      NUMERIC SORTABLE\n    embedding      VECTOR HNSW 6 TYPE FLOAT32 DIM 384 DISTANCE_METRIC COSINE","section_id":"data-model"},{"id":"the-query-ex0","language":"go","code":"result, _ := client.FTSearchWithArgs(ctx,\n    \"semcache:idx\",\n    \"(@tenant:{acme} @locale:{en} @model_version:{gpt\\\\-4\\\\.5\\\\-2026} @safety:{ok})\"+\n        \"=>[KNN 1 @embedding $vec AS distance]\",\n    &redis.FTSearchOptions{\n        DialectVersion: 2,\n        Params:         map[string]any{\"vec\": FloatsToBytes(queryVec)},\n        SortBy: []redis.FTSearchSortBy{\n            {FieldName: \"distance\", Asc: true},\n        },\n        Return: []redis.FTSearchReturn{\n            {FieldName: \"prompt\"}, {FieldName: \"response\"},\n            {FieldName: \"tenant\"}, {FieldName: \"locale\"},\n            {FieldName: \"model_version\"},\n            {FieldName: \"hit_count\"}, {FieldName: \"distance\"},\n        },\n        LimitOffset: 0,\n        Limit:       1,\n    },\n).Result()","section_id":"the-query"},{"id":"the-mock-llm-ex0","language":"go","code":"llm := NewMockLLM(\"\", 1500)  // 1.5 seconds per call\nresp := llm.Complete(\"What is your return policy?\")\n// resp.Response       — the templated answer text\n// resp.LatencyMs      — wall-clock time the call took\n// resp.TotalTokens()  — estimated prompt + completion tokens","section_id":"the-mock-llm"},{"id":"pre-seeding-the-cache-ex0","language":"go","code":"_ = cache.CreateIndex(ctx)\n_, _ = Seed(ctx, cache, embedder, SeedOptions{Tenant: \"acme\", Locale: \"en\"})","section_id":"pre-seeding-the-cache"},{"id":"run-the-demo-locally-ex0","language":"bash","code":"git clone https://github.com/redis/docs.git\n    cd docs/content/develop/use-cases/semantic-cache/go","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex1","language":"bash","code":"go mod tidy","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex2","language":"bash","code":"go run .","section_id":"run-the-demo-locally"}]}
{"schema_version":2,"id":"develop/use-cases/semantic-cache","title":"Redis semantic cache","url":"https://redis.io/docs/latest/develop/use-cases/semantic-cache/","summary":"Reuse LLM responses for queries that are semantically similar, not just byte-identical, to cut token costs, reduce latency, and share validated answers across users and sessions.","tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T17:06:11+01:00","children":[{"id":"develop/use-cases/semantic-cache/redis-py","summary":"Build a Redis-backed semantic cache for LLM responses in Python with redis-py and sentence-transformers","title":"Redis semantic cache with redis-py","url":"https://redis.io/docs/latest/develop/use-cases/semantic-cache/redis-py/"},{"id":"develop/use-cases/semantic-cache/nodejs","summary":"Build a Redis-backed semantic cache for LLM responses in Node.js with node-redis and @xenova/transformers","title":"Redis semantic cache with node-redis","url":"https://redis.io/docs/latest/develop/use-cases/semantic-cache/nodejs/"},{"id":"develop/use-cases/semantic-cache/go","summary":"Build a Redis-backed semantic cache for LLM responses in Go with go-redis and Hugot","title":"Redis semantic cache with go-redis","url":"https://redis.io/docs/latest/develop/use-cases/semantic-cache/go/"},{"id":"develop/use-cases/semantic-cache/java-jedis","summary":"Build a Redis-backed semantic cache for LLM responses in Java with Jedis and DJL (PyTorch)","title":"Redis semantic cache with Jedis","url":"https://redis.io/docs/latest/develop/use-cases/semantic-cache/java-jedis/"},{"id":"develop/use-cases/semantic-cache/java-lettuce","summary":"Build a Redis-backed semantic cache for LLM responses in Java with Lettuce and DJL (PyTorch)","title":"Redis semantic cache with Lettuce","url":"https://redis.io/docs/latest/develop/use-cases/semantic-cache/java-lettuce/"},{"id":"develop/use-cases/semantic-cache/php","summary":"Build a Redis-backed semantic cache for LLM responses in PHP with Predis and transformers-php","title":"Redis semantic cache with Predis","url":"https://redis.io/docs/latest/develop/use-cases/semantic-cache/php/"},{"id":"develop/use-cases/semantic-cache/ruby","summary":"Build a Redis-backed semantic cache for LLM responses in Ruby with redis-rb and informers","title":"Redis semantic cache with redis-rb","url":"https://redis.io/docs/latest/develop/use-cases/semantic-cache/ruby/"},{"id":"develop/use-cases/semantic-cache/dotnet","summary":"Build a Redis-backed semantic cache for LLM responses in C# with NRedisStack and ONNX Runtime","title":"Redis semantic cache with NRedisStack","url":"https://redis.io/docs/latest/develop/use-cases/semantic-cache/dotnet/"},{"id":"develop/use-cases/semantic-cache/rust","summary":"Build a Redis-backed semantic cache for LLM responses in Rust with redis-rs and Candle","title":"Redis semantic cache with redis-rs","url":"https://redis.io/docs/latest/develop/use-cases/semantic-cache/rust/"}],"page_type":"content","content_hash":"ea563066362350155a60ca39cf221f982487f164e8d4864e8c07f6d2f0266fa9","sections":[{"id":"when-to-use-redis-as-a-semantic-cache","title":"When to use Redis as a semantic cache","role":"overview","text":"Use Redis as a semantic cache when you need to reuse LLM responses for queries that are semantically similar — not just byte-identical — so paraphrased and near-duplicate questions skip the full embed-retrieve-generate pipeline and return a previously validated answer in tens of milliseconds."},{"id":"why-the-problem-is-hard","title":"Why the problem is hard","role":"content","text":"Every repeated or paraphrased question that reaches an LLM triggers the full pipeline — embedding, retrieval, generation — driving per-query cost up by 10–100x compared to a cache lookup, and pushing P95 latency (the 95th percentile latency) into multi-second territory. Some of the obvious workarounds have real drawbacks:\n\n-   **A traditional exact-match cache** (string key to response) only hits when the query is byte-identical, so it misses near-duplicates like *\"What's your return policy?\"* versus *\"How do I return an item?\"* — exactly the queries that dominate FAQ-style workloads.\n-   **A standalone vector database** can find similar past queries, but adds operational overhead for what is fundamentally a caching problem, and most lack first-class TTL management, eviction, and metadata filtering — features the cache needs in order to stay correct under churn.\n-   **Skipping the cache and relying on prompt caching at the model provider** discounts repeated *prefixes* but still runs the model end-to-end on every call, so it does not address latency or full-response reuse across users.\n\nThe core difficulty is threshold tuning: too loose and you serve wrong answers, too tight and the hit rate collapses. Effective semantic caching combines soft similarity matching with hard metadata boundaries (tenant, locale, model version, safety flags) so reuse stays inside well-defined limits.\n\nThis pattern is distinct from using Redis for RAG vector search. Semantic caching stores **complete LLM responses**, not document chunks, and the goal is to **skip the LLM entirely** on a hit rather than feed retrieved context into one."},{"id":"what-you-can-expect-from-a-redis-solution","title":"What you can expect from a Redis solution","role":"content","text":"You can:\n\n-   Return cached answers for paraphrased and near-duplicate queries in tens of milliseconds instead of multi-second LLM round trips.\n-   Reduce LLM token spend on workloads with repeated query patterns — FAQ bots, helpdesks, internal knowledge assistants — by 30% or more without a measurable quality regression.\n-   Scope cached answers by tenant, locale, or model version so reuse stays within defined boundaries, applied inside the similarity query rather than in application code.\n-   Let stale answers expire automatically and shed cold entries under memory pressure without manual cleanup.\n-   Share validated, high-quality answers across users, sessions, and channels while keeping the cache non-authoritative and rebuildable at any time.\n-   Add semantic caching to an existing Redis deployment without provisioning a separate vector database or cache service — it is an additional index and key pattern on the same instance."},{"id":"how-redis-supports-the-solution","title":"How Redis supports the solution","role":"content","text":"In practice, each cache entry is a single [Hash](https://redis.io/docs/latest/develop/data-types/hashes) or [JSON](https://redis.io/docs/latest/develop/data-types/json) document holding the prompt, its embedding vector, the LLM response, and metadata fields — tenant, locale, model version, safety flags. A [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) index covers the embedding field together with the metadata fields, so a single [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call performs KNN against the cached prompts with a TAG or NUMERIC pre-filter applied in the same pass. On a hit above the configured distance threshold the application serves the cached response directly; on a miss it runs the LLM and writes the new prompt, response, and metadata back to the same key pattern with a TTL.\n\nRedis provides the following features that make it a good fit for a semantic cache:\n\n-   [Hashes](https://redis.io/docs/latest/develop/data-types/hashes) and [JSON](https://redis.io/docs/latest/develop/data-types/json) store the prompt, embedding, response, and metadata together under a single key, so a cache hit returns everything the application needs in one round trip.\n-   [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) with [HNSW vector indexes](https://redis.io/docs/latest/develop/ai/search-and-query/vectors) finds the nearest cached prompt above a configurable similarity threshold in sub-millisecond time, and the same [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call applies TAG and NUMERIC filters so tenant isolation and namespace scoping happen inside the query, not in application logic.\n-   [`EXPIRE`](https://redis.io/docs/latest/commands/expire) sets a TTL on each cache entry so stale answers age out without manual cleanup, keeping the cache aligned with the underlying knowledge base.\n-   Database-level [eviction policies](https://redis.io/docs/latest/develop/reference/eviction) (LRU / LFU) bound memory under pressure and shed cold entries automatically, so the cache stays within budget as the prompt distribution shifts.\n-   Sub-millisecond reads and writes from memory let the semantic cache ride on the same Redis instance already handling sessions, rate limiting, or RAG retrieval at zero marginal cost."},{"id":"ecosystem","title":"Ecosystem","role":"content","text":"The following libraries, frameworks, and managed services build on Redis for semantic caching:\n\n-   **Python**: [RedisVL](https://github.com/redis/redis-vl-python) provides the `SemanticCache` API with built-in embedding, distance thresholds, TTL, and metadata filters. See the [RedisVL LLM cache user guide](https://redis.io/docs/latest/develop/ai/redisvl/user_guide/how_to_guides/llmcache) and the [LangCache integration guide](https://redis.io/docs/latest/develop/ai/redisvl/user_guide/how_to_guides/langcache_semantic_cache).\n-   **Frameworks**: [LangChain](https://python.langchain.com/docs/integrations/llm_caching/#redis-cache) (Redis as an LLM cache and vector store), [LlamaIndex](https://developers.llamaindex.ai/python/examples/vector_stores/redisindexdemo/), and [LangGraph](https://langchain-ai.github.io/langgraph/) for agent memory and response caching.\n-   **Managed**: [Redis LangCache](https://redis.io/docs/latest/develop/ai/context-engine/langcache) is a fully managed semantic cache with a REST API, configurable distance thresholds, automatic eviction, and built-in metrics — no index management or embedding wiring required."},{"id":"code-examples-to-build-your-own-redis-semantic-cache","title":"Code examples to build your own Redis semantic cache","role":"example","text":"The following guides show how to build a small Redis-backed semantic cache that sits in front of an LLM call. Each guide includes a runnable interactive demo that embeds an incoming prompt, runs a thresholded KNN lookup against the cache with tenant and locale filters, serves the cached response on a hit, and on a miss calls the LLM and writes the new prompt, response, and metadata back with a TTL.\n\n* [redis-py (Python)](https://redis.io/docs/latest/develop/use-cases/semantic-cache/redis-py)\n* [node-redis (Node.js)](https://redis.io/docs/latest/develop/use-cases/semantic-cache/nodejs)\n* [go-redis (Go)](https://redis.io/docs/latest/develop/use-cases/semantic-cache/go)\n* [redis-rs (Rust)](https://redis.io/docs/latest/develop/use-cases/semantic-cache/rust)\n* [NRedisStack (C#)](https://redis.io/docs/latest/develop/use-cases/semantic-cache/dotnet)\n* [Jedis (Java)](https://redis.io/docs/latest/develop/use-cases/semantic-cache/java-jedis)\n* [Lettuce (Java)](https://redis.io/docs/latest/develop/use-cases/semantic-cache/java-lettuce)\n* [Predis (PHP)](https://redis.io/docs/latest/develop/use-cases/semantic-cache/php)\n* [redis-rb (Ruby)](https://redis.io/docs/latest/develop/use-cases/semantic-cache/ruby)"}],"examples":[]}
{"schema_version":2,"id":"develop/use-cases/semantic-cache/java-jedis","title":"Redis semantic cache with Jedis","url":"https://redis.io/docs/latest/develop/use-cases/semantic-cache/java-jedis/","summary":"Build a Redis-backed semantic cache for LLM responses in Java with Jedis and DJL (PyTorch)","aliases":["/develop/use-cases/semantic-cache/java","/develop/use-cases/semantic-cache/jedis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T17:06:11+01:00","children":[{"id":"develop/use-cases/semantic-cache/java-jedis/README","summary":"","title":"","url":"https://redis.io/docs/latest/develop/use-cases/semantic-cache/java-jedis/readme/"}],"page_type":"content","content_hash":"c956c9dfb61cdb260fde2f06241fd1646ce7700db43569184292b6b50fc63dd9","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a small Redis-backed semantic cache for LLM responses in Java with [Jedis](https://redis.io/docs/latest/develop/clients/jedis) and [DJL (Deep Java Library)](https://djl.ai/) running the [`sentence-transformers/all-MiniLM-L6-v2`](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2) encoder locally on PyTorch. It includes a local web server built with the JDK's standard `com.sun.net.httpserver.HttpServer` so you can send paraphrased prompts at a mock LLM, watch the cache decide hit or miss, sweep the cosine-distance threshold, and see the cumulative latency and token savings build up."},{"id":"overview","title":"Overview","role":"overview","text":"Each cache entry is stored as a single Redis [Hash](https://redis.io/docs/latest/develop/data-types/hashes) at `cache:<id>`. The hash holds the original prompt, the LLM's response, the raw `float32` bytes of a 384-dimensional embedding of the prompt, and metadata fields — tenant, locale, model version, safety flag — plus a `created_ts` and a `hit_count`. A single [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) index covers the embedding field and every metadata field, so one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call with a `KNN` clause does the vector lookup *and* the TAG pre-filter in the same round trip — no cross-store joins.\n\nThe lookup is thresholded: [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) always returns the nearest entry that satisfies the filters, but the application only serves it as a hit when the reported cosine distance is at or below `distanceThreshold`. Anything further away is treated as a miss; the caller runs the LLM and writes the new prompt, response, and embedding back to the same key pattern with a TTL.\n\nThe embedder is [DJL](https://djl.ai/) loading the [`sentence-transformers/all-MiniLM-L6-v2`](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2) PyTorch model from the DJL model zoo. This is the same 384-dimensional encoder the [Python example](https://redis.io/docs/latest/develop/use-cases/semantic-cache/redis-py) and the [Node.js example](https://redis.io/docs/latest/develop/use-cases/semantic-cache/nodejs) use. Embeddings produced by the three implementations are semantically equivalent — paraphrase distances differ only at the fourth decimal place — so a cache populated by one demo can be queried by another against the same Redis instance.\n\nThat gives you:\n\n* A single round trip for lookup — vector KNN + metadata pre-filter in one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search).\n* Tens of milliseconds on a hit vs. a multi-second LLM call on a miss; the embedding step is the bottleneck either way, and that's a model-side cost, not a Redis one.\n* Tenant, locale, and model-version isolation enforced inside the query, not in application code — a write under one tenant cannot be served to another.\n* Bounded memory: every entry has an [`EXPIRE`](https://redis.io/docs/latest/commands/expire) TTL, and a database-level [eviction policy](https://redis.io/docs/latest/develop/reference/eviction) (LRU / LFU) caps the cache size under pressure."},{"id":"how-it-works","title":"How it works","role":"content","text":"A query goes through three stages: **embed**, **lookup**, and (on a miss) **call the LLM and write back**."},{"id":"hit-path-the-goal","title":"Hit path (the goal)","role":"content","text":"1. The application calls `embedder.encodeOne(prompt)` to turn the incoming text into a 384-dimensional `float[]`.\n2. `cache.lookup(queryVec, tenant, locale, modelVersion, \"ok\", threshold)` runs [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) with a TAG pre-filter and a `KNN 1` clause. Redis returns the closest cached prompt that satisfies the filters along with its cosine distance.\n3. If the distance is at or below the threshold, the cache returns a `CacheHit` containing the cached response. The helper also runs an [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby) on `hit_count` and an [`EXPIRE`](https://redis.io/docs/latest/commands/expire) refresh inside a [`MULTI/EXEC`](https://redis.io/docs/latest/commands/multi), so a frequently used answer keeps its TTL and the demo UI can see which entries are load-bearing.\n4. The LLM is not called at all. The application returns the cached response to the user."},{"id":"miss-path","title":"Miss path","role":"content","text":"When the distance is above the threshold — or there is no candidate in scope at all — the helper returns a `CacheMiss` instead, carrying the distance of the nearest candidate (if any) for logging. The application then:\n\n1. Calls the LLM with the prompt.\n2. Calls `cache.put(prompt, response, embedding, tenant, locale, modelVersion, ...)`. The same embedding the lookup used is reused — no re-encode. The helper writes the Hash with [`HSET`](https://redis.io/docs/latest/commands/hset) and an [`EXPIRE`](https://redis.io/docs/latest/commands/expire) TTL inside a single [`MULTI/EXEC`](https://redis.io/docs/latest/commands/multi) so the entry never lands without a TTL on a partial failure.\n3. Returns the LLM's response to the user. The next semantically similar prompt under the same metadata scope will be a hit."},{"id":"the-cache-helper","title":"The cache helper","role":"content","text":"The `RedisSemanticCache` class wraps the Redis Search index and the lookup / write flow\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/semantic-cache/java-jedis/src/main/java/com/redis/semcache/RedisSemanticCache.java)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each cache entry is one Redis Hash. The vector field is raw little-endian `float32` bytes — no JSON wrapping — because the Redis Search vector encoding expects exactly that. The helper packs the `float[]` with a `ByteBuffer` in `ByteOrder.LITTLE_ENDIAN`, which matches the bytes Redis Search reads and is identical to the encoding the Python and Node ports write.\n\n[code example]\n\nThe Redis Search index schema treats every field as queryable in its natural type:\n\n[code example]"},{"id":"the-query","title":"The query","role":"content","text":"The lookup is a hybrid query: a TAG pre-filter expression in parentheses, then `=>[KNN 1 @embedding $vec]`. With `DIALECT 2`, Redis applies the filter first and KNN-ranks only the matching documents. In Jedis:\n\n[code example]\n\n`distance` is the cosine *distance* (0 means identical, 2 means opposite). The result is sorted ascending, so the top row is the closest candidate. The application inspects `distance` against the threshold and decides hit or miss in user code — Redis returns the row either way, and treating it as a hit or a miss is a policy decision the cache helper owns, not a server-side filter."},{"id":"the-mock-llm","title":"The mock LLM","role":"content","text":"To make the latency and token savings visible without requiring an API key, `MockLLM.java` provides a deterministic stand-in\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/semantic-cache/java-jedis/src/main/java/com/redis/semcache/MockLLM.java)):\n\n[code example]\n\nThe mock sleeps for the configured latency, then keyword-matches against a small FAQ table to produce an answer. The deliberate slowness is what makes a hit visibly cheaper than a miss in the demo. In production code, you would replace `MockLLM` with your real client of choice — an HTTP call to OpenAI, Anthropic, a self-hosted vLLM endpoint, anything — without changing the cache helper."},{"id":"pre-seeding-the-cache","title":"Pre-seeding the cache","role":"content","text":"In a real deployment the cache fills up organically: a first-time question is a miss, the LLM answers, and the response is written back. For the demo, `SeedCache.java` pre-loads a small set of canonical FAQ prompts so the very first query lands on a hit\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/semantic-cache/java-jedis/src/main/java/com/redis/semcache/SeedCache.java)):\n\n[code example]\n\nThe seed list stores the canonical phrasing of each question (\"What is your return policy?\"). Paraphrases of any of these prompts (\"How do I return an item?\", \"Can I get a refund?\") embed close to the canonical entry, so the cache lookup serves the stored response without ever calling the model."},{"id":"the-interactive-demo","title":"The interactive demo","role":"content","text":"`DemoServer.java` runs an HTTP server built on the JDK's `com.sun.net.httpserver.HttpServer` — no Spring, no Jetty, no embedded framework. The HTML page lets you:\n\n* Type a prompt and toggle metadata: tenant, locale, model version. Each combination is a separate cache namespace inside the same index.\n* Slide the cosine-distance threshold and see hits flip to misses (and back) on the same prompt, with the actual distance reported on each query.\n* Submit with **Ask** to run the full hit-or-miss path (calls the LLM on a miss, writes the answer back). Submit with **Lookup only (no LLM)** to sweep the threshold against a fixed prompt without polluting the cache.\n* Watch the cumulative panel build up: total queries, cache hits, cache misses, hit ratio, tokens not spent, LLM milliseconds not waited.\n* Inspect every cached entry, including remaining TTL and total hit count, and drop individual entries to simulate eviction.\n\nThe server holds one `LocalEmbedder`, one `RedisSemanticCache`, and one `MockLLM` for the lifetime of the process. The HTML page is shared with the Python, Node.js, and Go demos; the build embeds `index.html` from the project root as a classpath resource so the jar runs from any working directory. Endpoints:\n\n| Endpoint        | What it does                                                                  |\n|-----------------|-------------------------------------------------------------------------------|\n| `GET  /state`   | Index info and the full list of cached entries.                               |\n| `POST /query`   | Embed the prompt, run `FT.SEARCH`, on miss call the LLM and write back.       |\n| `POST /reset`   | Drop every cached entry and re-seed from the FAQ list.                        |\n| `POST /drop`    | Delete a single cached entry by id.                                           |"},{"id":"run-the-demo-locally","title":"Run the demo locally","role":"content","text":"1.  Clone the [`redis/docs`](https://github.com/redis/docs) repository and change into the example\n    directory:\n\n    [code example]\n\n2.  Make sure a Redis instance with the Redis Search module is running locally on\n    port 6379. [Redis Stack](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack) or\n    [Redis 8 with Search](https://redis.io/docs/latest/develop/ai/search-and-query) both work.\n\n3.  Build the project with Maven. This pulls Jedis, DJL, and the PyTorch native\n    libraries. The first build takes a couple of minutes:\n\n    [code example]\n\n4.  Run the demo. The first run also downloads the `sentence-transformers/all-MiniLM-L6-v2`\n    PyTorch weights into the local DJL cache (~90 MB); every subsequent run is offline:\n\n    [code example]\n\n    Or with `mvn`:\n\n    [code example]\n\n5.  Open <http://localhost:8089> and try some queries:\n\n    * **\"What is your return policy?\"** — exact match against the seed, distance ≈ 0,\n      hit at any threshold.\n    * **\"How fast is delivery?\"** — paraphrase of the shipping seed; distance\n      around 0.30, hit at the default threshold of 0.5.\n    * **\"How do I return an item?\"** — slightly looser paraphrase of the returns\n      seed; distance around 0.49, still a hit at the default threshold. Slide\n      the threshold down to 0.4 to see this one flip to a miss.\n    * **\"What payment methods do you accept?\"** — unrelated to anything in the\n      seed; distance > 0.8, so you'll see a miss, the mock LLM kicks in for\n      ~1.5 s, the new answer is cached, and a follow-up of the same question\n      is now an immediate hit.\n    * Switch the **Tenant** dropdown to `globex` or `initech` and re-ask any\n      seeded question — the result flips to a miss because the cache entries\n      live under `acme`. That's the metadata pre-filter at work inside `FT.SEARCH`.\n\nThe server is read/write against your local Redis. The default index name is `semcache:idx` and entry keys live under `cache:`. Flags mirror the Python and Node demos: `--no-reset` to keep an existing cache across restarts, `--threshold` to change the default cosine-distance cutoff, `--llm-latency-ms` to make the mock LLM faster or slower for the demo, or `--port` to listen on a different port."}],"examples":[{"id":"the-cache-helper-ex0","language":"java","code":"import redis.clients.jedis.JedisPooled;\nimport com.redis.semcache.RedisSemanticCache;\nimport com.redis.semcache.LocalEmbedder;\nimport com.redis.semcache.LookupResult;\nimport com.redis.semcache.CacheHit;\n\nJedisPooled jedis = new JedisPooled(\"localhost\", 6379);\nLocalEmbedder embedder = LocalEmbedder.create();   // sentence-transformers/all-MiniLM-L6-v2\n\nRedisSemanticCache cache = new RedisSemanticCache(\n        jedis,\n        \"semcache:idx\",\n        \"cache:\",\n        384,\n        0.5,    // cosine distance, lower = stricter\n        3600    // TTL in seconds (one hour)\n);\n\n// One-time index setup (idempotent).\ncache.createIndex();\n\n// 1) Embed the prompt.\nString prompt = \"How do I return an item?\";\nfloat[] queryVec = embedder.encodeOne(prompt);\n\n// 2) Look up under a metadata scope. The TAG filter and the KNN\n//    travel together in one FT.SEARCH.\nLookupResult result = cache.lookup(\n        queryVec, \"acme\", \"en\", \"gpt-4.5-2026\", \"ok\", null);\n\nString response;\nif (result instanceof CacheHit hit) {\n    response = hit.response();\n    System.out.printf(\"hit (%.3f): %s%n\", hit.distance(), response);\n} else {\n    // 3a) Miss — call the LLM. (Use your real client here.)\n    response = callLlm(prompt);\n\n    // 3b) Cache the new entry. Reuses the same embedding bytes the\n    //     lookup used, so we don't pay the encoder twice.\n    cache.put(\n            prompt,\n            response,\n            queryVec,\n            \"acme\",\n            \"en\",\n            \"gpt-4.5-2026\",\n            \"ok\",\n            null,   // ttl override (null = default)\n            null    // entry id (null = generated)\n    );\n}","section_id":"the-cache-helper"},{"id":"data-model-ex0","language":"text","code":"cache:7c3f8a1b9e02\n  prompt=How do I return an item?\n  response=You can return any unworn item within 30 days...\n  tenant=acme\n  locale=en\n  model_version=gpt-4.5-2026\n  safety=ok\n  created_ts=1715990400.123\n  hit_count=4\n  embedding=<384 × float32 little-endian bytes>","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"FT.CREATE semcache:idx\n  ON HASH PREFIX 1 cache:\n  SCHEMA\n    prompt         TEXT\n    response       TEXT\n    tenant         TAG\n    locale         TAG\n    model_version  TAG\n    safety         TAG\n    created_ts     NUMERIC SORTABLE\n    hit_count      NUMERIC SORTABLE\n    embedding      VECTOR HNSW 6 TYPE FLOAT32 DIM 384 DISTANCE_METRIC COSINE","section_id":"data-model"},{"id":"the-query-ex0","language":"java","code":"Query q = new Query(\n        \"(@tenant:{acme} @locale:{en} @model_version:{gpt\\\\-4\\\\.5\\\\-2026} @safety:{ok})\"\n                + \"=>[KNN 1 @embedding $vec AS distance]\")\n        .returnFields(\"prompt\", \"response\", \"tenant\", \"locale\",\n                \"model_version\", \"hit_count\", \"distance\")\n        .setSortBy(\"distance\", true)\n        .limit(0, 1)\n        .addParam(\"vec\", LocalEmbedder.toBytes(queryVec))\n        .dialect(2);\n\nSearchResult result = jedis.ftSearch(\"semcache:idx\", q);","section_id":"the-query"},{"id":"the-mock-llm-ex0","language":"java","code":"import com.redis.semcache.MockLLM;\n\nMockLLM llm = new MockLLM(\"gpt-4.5-2026\", 1500.0);\nMockLLM.Response response = llm.complete(\"What is your return policy?\");\n// response.response()      — the templated answer text\n// response.latencyMs()     — wall-clock time the call took\n// response.totalTokens()   — estimated prompt + completion tokens","section_id":"the-mock-llm"},{"id":"pre-seeding-the-cache-ex0","language":"java","code":"import com.redis.semcache.SeedCache;\n\ncache.createIndex();\nSeedCache.seed(cache, embedder, \"acme\", \"en\", \"gpt-4.5-2026\");","section_id":"pre-seeding-the-cache"},{"id":"run-the-demo-locally-ex0","language":"bash","code":"git clone https://github.com/redis/docs.git\n    cd docs/content/develop/use-cases/semantic-cache/java-jedis","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex1","language":"bash","code":"mvn -q package","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex2","language":"bash","code":"java -jar target/semantic-cache-jedis.jar","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex3","language":"bash","code":"mvn -q exec:java","section_id":"run-the-demo-locally"}]}
{"schema_version":2,"id":"develop/use-cases/semantic-cache/java-jedis/README","title":"","url":"https://redis.io/docs/latest/develop/use-cases/semantic-cache/java-jedis/readme/","summary":"","tags":[],"last_updated":"2026-05-21T15:23:25+01:00","page_type":"content","content_hash":"684cc75ca9d068c229ff3dda304b0cf6f97f02257429a0c9ca0c6e2f96786d18","sections":[{"id":"content","title":"Content","role":"content","text":"# Redis semantic-cache demo (Java + Jedis)\n\nSee `_index.md` for the full walkthrough. Quick start:\n\n[code example]\n\nThen open <http://localhost:8089>.\n\nNotable flags (full list with `--help`):\n\n| Flag                      | Default            |\n|---------------------------|--------------------|\n| `--port`                  | `8089`             |\n| `--redis-host`            | `localhost`        |\n| `--redis-port`            | `6379`             |\n| `--index-name`            | `semcache:idx`     |\n| `--key-prefix`            | `cache:`           |\n| `--ttl-seconds`           | `3600`             |\n| `--threshold`             | `0.5`              |\n| `--llm-latency-ms`        | `1500.0`           |\n| `--no-reset`              | (re-seeds by default) |"}],"examples":[{"id":"content-ex0","language":"bash","code":"# 1. Make sure Redis with the Search module is running on localhost:6379.\n# 2. Build the fat jar (first build pulls Jedis, DJL, and the PyTorch\n#    native libraries; takes a minute or two):\nmvn -q package\n\n# 3. Run. The first run downloads the sentence-transformers/all-MiniLM-L6-v2\n#    PyTorch weights into the local DJL cache (~90 MB).\njava -jar target/semantic-cache-jedis.jar\n\n# Or with Maven directly:\nmvn -q exec:java","section_id":"content"}]}
{"schema_version":2,"id":"develop/use-cases/semantic-cache/java-lettuce","title":"Redis semantic cache with Lettuce","url":"https://redis.io/docs/latest/develop/use-cases/semantic-cache/java-lettuce/","summary":"Build a Redis-backed semantic cache for LLM responses in Java with Lettuce and DJL (PyTorch)","aliases":["/develop/use-cases/semantic-cache/lettuce"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T17:06:11+01:00","children":[{"id":"develop/use-cases/semantic-cache/java-lettuce/README","summary":"","title":"","url":"https://redis.io/docs/latest/develop/use-cases/semantic-cache/java-lettuce/readme/"}],"page_type":"content","content_hash":"7a26b95ffda5066043b01f8002d86dabc8bd96439c750d707fc1479f6e24cc3e","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a small Redis-backed semantic cache for LLM responses in Java with [Lettuce](https://redis.io/docs/latest/develop/clients/lettuce) and [DJL (Deep Java Library)](https://djl.ai/) running the [`sentence-transformers/all-MiniLM-L6-v2`](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2) encoder locally on PyTorch. It includes a local web server built with the JDK's standard `com.sun.net.httpserver.HttpServer` so you can send paraphrased prompts at a mock LLM, watch the cache decide hit or miss, sweep the cosine-distance threshold, and see the cumulative latency and token savings build up."},{"id":"overview","title":"Overview","role":"overview","text":"Each cache entry is stored as a single Redis [Hash](https://redis.io/docs/latest/develop/data-types/hashes) at `cache:<id>`. The hash holds the original prompt, the LLM's response, the raw `float32` bytes of a 384-dimensional embedding of the prompt, and metadata fields — tenant, locale, model version, safety flag — plus a `created_ts` and a `hit_count`. A single [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) index covers the embedding field and every metadata field, so one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call with a `KNN` clause does the vector lookup *and* the TAG pre-filter in the same round trip — no cross-store joins.\n\nThe lookup is thresholded: [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) always returns the nearest entry that satisfies the filters, but the application only serves it as a hit when the reported cosine distance is at or below `distanceThreshold`. Anything further away is treated as a miss; the caller runs the LLM and writes the new prompt, response, and embedding back to the same key pattern with a TTL.\n\nThe embedder is [DJL](https://djl.ai/) loading the [`sentence-transformers/all-MiniLM-L6-v2`](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2) PyTorch model from the DJL model zoo. This is the same 384-dimensional encoder the [Python example](https://redis.io/docs/latest/develop/use-cases/semantic-cache/redis-py), the [Node.js example](https://redis.io/docs/latest/develop/use-cases/semantic-cache/nodejs), and the [Jedis example](https://redis.io/docs/latest/develop/use-cases/semantic-cache/java-jedis) use. Embeddings produced by the four implementations are semantically equivalent — paraphrase distances differ only at the fourth decimal place — so a cache populated by one demo can be queried by another against the same Redis instance.\n\nThat gives you:\n\n* A single round trip for lookup — vector KNN + metadata pre-filter in one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search).\n* Tens of milliseconds on a hit vs. a multi-second LLM call on a miss; the embedding step is the bottleneck either way, and that's a model-side cost, not a Redis one.\n* Tenant, locale, and model-version isolation enforced inside the query, not in application code — a write under one tenant cannot be served to another.\n* Bounded memory: every entry has an [`EXPIRE`](https://redis.io/docs/latest/commands/expire) TTL, and a database-level [eviction policy](https://redis.io/docs/latest/develop/reference/eviction) (LRU / LFU) caps the cache size under pressure."},{"id":"how-it-works","title":"How it works","role":"content","text":"A query goes through three stages: **embed**, **lookup**, and (on a miss) **call the LLM and write back**."},{"id":"hit-path-the-goal","title":"Hit path (the goal)","role":"content","text":"1. The application calls `embedder.encodeOne(prompt)` to turn the incoming text into a 384-dimensional `float[]`.\n2. `cache.lookup(queryVec, tenant, locale, modelVersion, \"ok\", threshold)` runs [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) with a TAG pre-filter and a `KNN 1` clause. Redis returns the closest cached prompt that satisfies the filters along with its cosine distance.\n3. If the distance is at or below the threshold, the cache returns a `CacheHit` containing the cached response. The helper also runs an [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby) on `hit_count` and an [`EXPIRE`](https://redis.io/docs/latest/commands/expire) refresh inside a [`MULTI/EXEC`](https://redis.io/docs/latest/commands/multi), so a frequently used answer keeps its TTL and the demo UI can see which entries are load-bearing.\n4. The LLM is not called at all. The application returns the cached response to the user."},{"id":"miss-path","title":"Miss path","role":"content","text":"When the distance is above the threshold — or there is no candidate in scope at all — the helper returns a `CacheMiss` instead, carrying the distance of the nearest candidate (if any) for logging. The application then:\n\n1. Calls the LLM with the prompt.\n2. Calls `cache.put(prompt, response, embedding, tenant, locale, modelVersion, ...)`. The same embedding the lookup used is reused — no re-encode. The helper writes the Hash with [`HSET`](https://redis.io/docs/latest/commands/hset) and an [`EXPIRE`](https://redis.io/docs/latest/commands/expire) TTL inside a single [`MULTI/EXEC`](https://redis.io/docs/latest/commands/multi) so the entry never lands without a TTL on a partial failure.\n3. Returns the LLM's response to the user. The next semantically similar prompt under the same metadata scope will be a hit."},{"id":"the-cache-helper","title":"The cache helper","role":"content","text":"The `RedisSemanticCache` class wraps the Redis Search index and the lookup / write flow\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/semantic-cache/java-lettuce/src/main/java/com/redis/semcache/RedisSemanticCache.java)):\n\n[code example]\n\nThe connection uses Lettuce's `ByteArrayCodec` so the binary float-32 bytes of the embedding can share an `HSET` mapping with the UTF-8 text fields without a second connection. RESP2 is pinned explicitly: Lettuce 6.7 negotiates RESP3 by default, which returns the [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) reply as a map keyed by `results` / `total_results` instead of the flat alternating list the demo's parser expects, and pinning RESP2 keeps the wire format identical to what the Python, Node, and Jedis ports speak."},{"id":"data-model","title":"Data model","role":"content","text":"Each cache entry is one Redis Hash. The vector field is raw little-endian `float32` bytes — no JSON wrapping — because the Redis Search vector encoding expects exactly that. The helper packs the `float[]` with a `ByteBuffer` in `ByteOrder.LITTLE_ENDIAN`, which matches the bytes Redis Search reads and is identical to the encoding the Python and Node ports write.\n\n[code example]\n\nThe Redis Search index schema treats every field as queryable in its natural type:\n\n[code example]"},{"id":"the-query","title":"The query","role":"content","text":"The lookup is a hybrid query: a TAG pre-filter expression in parentheses, then `=>[KNN 1 @embedding $vec]`. With `DIALECT 2`, Redis applies the filter first and KNN-ranks only the matching documents. Lettuce 6.7 doesn't yet ship first-class [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) bindings, so the helper sends the command through `RedisCommands.dispatch()` with a custom `ProtocolKeyword` — the same machinery Lettuce uses internally for any command it does support natively, just spelled out by the caller. The wire bytes are identical to typing the command in `redis-cli`:\n\n[code example]\n\n`distance` is the cosine *distance* (0 means identical, 2 means opposite). The result is sorted ascending, so the top row is the closest candidate. The application inspects `distance` against the threshold and decides hit or miss in user code — Redis returns the row either way, and treating it as a hit or a miss is a policy decision the cache helper owns, not a server-side filter."},{"id":"the-mock-llm","title":"The mock LLM","role":"content","text":"To make the latency and token savings visible without requiring an API key, `MockLLM.java` provides a deterministic stand-in\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/semantic-cache/java-lettuce/src/main/java/com/redis/semcache/MockLLM.java)):\n\n[code example]\n\nThe mock sleeps for the configured latency, then keyword-matches against a small FAQ table to produce an answer. The deliberate slowness is what makes a hit visibly cheaper than a miss in the demo. In production code, you would replace `MockLLM` with your real client of choice — an HTTP call to OpenAI, Anthropic, a self-hosted vLLM endpoint, anything — without changing the cache helper."},{"id":"pre-seeding-the-cache","title":"Pre-seeding the cache","role":"content","text":"In a real deployment the cache fills up organically: a first-time question is a miss, the LLM answers, and the response is written back. For the demo, `SeedCache.java` pre-loads a small set of canonical FAQ prompts so the very first query lands on a hit\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/semantic-cache/java-lettuce/src/main/java/com/redis/semcache/SeedCache.java)):\n\n[code example]\n\nThe seed list stores the canonical phrasing of each question (\"What is your return policy?\"). Paraphrases of any of these prompts (\"How do I return an item?\", \"Can I get a refund?\") embed close to the canonical entry, so the cache lookup serves the stored response without ever calling the model."},{"id":"the-interactive-demo","title":"The interactive demo","role":"content","text":"`DemoServer.java` runs an HTTP server built on the JDK's `com.sun.net.httpserver.HttpServer` — no Spring, no Jetty, no embedded framework. The HTML page lets you:\n\n* Type a prompt and toggle metadata: tenant, locale, model version. Each combination is a separate cache namespace inside the same index.\n* Slide the cosine-distance threshold and see hits flip to misses (and back) on the same prompt, with the actual distance reported on each query.\n* Submit with **Ask** to run the full hit-or-miss path (calls the LLM on a miss, writes the answer back). Submit with **Lookup only (no LLM)** to sweep the threshold against a fixed prompt without polluting the cache.\n* Watch the cumulative panel build up: total queries, cache hits, cache misses, hit ratio, tokens not spent, LLM milliseconds not waited.\n* Inspect every cached entry, including remaining TTL and total hit count, and drop individual entries to simulate eviction.\n\nThe server holds one `LocalEmbedder`, one `RedisSemanticCache`, and one `MockLLM` for the lifetime of the process. The HTML page is shared with the Python, Node.js, Go, and Jedis demos; the build embeds `index.html` from the project root as a classpath resource so the jar runs from any working directory. Endpoints:\n\n| Endpoint        | What it does                                                                  |\n|-----------------|-------------------------------------------------------------------------------|\n| `GET  /state`   | Index info and the full list of cached entries.                               |\n| `POST /query`   | Embed the prompt, run `FT.SEARCH`, on miss call the LLM and write back.       |\n| `POST /reset`   | Drop every cached entry and re-seed from the FAQ list.                        |\n| `POST /drop`    | Delete a single cached entry by id.                                           |"},{"id":"run-the-demo-locally","title":"Run the demo locally","role":"content","text":"1.  Clone the [`redis/docs`](https://github.com/redis/docs) repository and change into the example\n    directory:\n\n    [code example]\n\n2.  Make sure a Redis instance with the Redis Search module is running locally on\n    port 6379. [Redis Stack](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack) or\n    [Redis 8 with Search](https://redis.io/docs/latest/develop/ai/search-and-query) both work.\n\n3.  Build the project with Maven. This pulls Lettuce, DJL, and the PyTorch native\n    libraries. The first build takes a couple of minutes:\n\n    [code example]\n\n4.  Run the demo. The first run also downloads the `sentence-transformers/all-MiniLM-L6-v2`\n    PyTorch weights into the local DJL cache (~90 MB); every subsequent run is offline:\n\n    [code example]\n\n    Or with `mvn`:\n\n    [code example]\n\n5.  Open <http://localhost:8090> and try some queries:\n\n    * **\"What is your return policy?\"** — exact match against the seed, distance ≈ 0,\n      hit at any threshold.\n    * **\"How fast is delivery?\"** — paraphrase of the shipping seed; distance\n      around 0.30, hit at the default threshold of 0.5.\n    * **\"How do I return an item?\"** — slightly looser paraphrase of the returns\n      seed; distance around 0.49, still a hit at the default threshold. Slide\n      the threshold down to 0.4 to see this one flip to a miss.\n    * **\"What payment methods do you accept?\"** — unrelated to anything in the\n      seed; distance > 0.65, so you'll see a miss, the mock LLM kicks in for\n      ~1.5 s, the new answer is cached, and a follow-up of the same question\n      is now an immediate hit.\n    * Switch the **Tenant** dropdown to `globex` or `initech` and re-ask any\n      seeded question — the result flips to a miss because the cache entries\n      live under `acme`. That's the metadata pre-filter at work inside `FT.SEARCH`.\n\nThe server is read/write against your local Redis. The default index name is `semcache:idx` and entry keys live under `cache:`. Flags mirror the Python, Node, and Jedis demos: `--no-reset` to keep an existing cache across restarts, `--threshold` to change the default cosine-distance cutoff, `--llm-latency-ms` to make the mock LLM faster or slower for the demo, or `--port` to listen on a different port."}],"examples":[{"id":"the-cache-helper-ex0","language":"java","code":"import io.lettuce.core.ClientOptions;\nimport io.lettuce.core.RedisClient;\nimport io.lettuce.core.api.StatefulRedisConnection;\nimport io.lettuce.core.codec.ByteArrayCodec;\nimport io.lettuce.core.protocol.ProtocolVersion;\nimport com.redis.semcache.RedisSemanticCache;\nimport com.redis.semcache.LocalEmbedder;\nimport com.redis.semcache.LookupResult;\nimport com.redis.semcache.CacheHit;\n\nRedisClient client = RedisClient.create(\"redis://localhost:6379\");\nclient.setOptions(ClientOptions.builder()\n        .protocolVersion(ProtocolVersion.RESP2)\n        .build());\nStatefulRedisConnection<byte[], byte[]> connection =\n        client.connect(ByteArrayCodec.INSTANCE);\n\nLocalEmbedder embedder = LocalEmbedder.create();   // sentence-transformers/all-MiniLM-L6-v2\n\nRedisSemanticCache cache = new RedisSemanticCache(\n        connection,\n        \"semcache:idx\",\n        \"cache:\",\n        384,\n        0.5,    // cosine distance, lower = stricter\n        3600    // TTL in seconds (one hour)\n);\n\n// One-time index setup (idempotent).\ncache.createIndex();\n\n// 1) Embed the prompt.\nString prompt = \"How do I return an item?\";\nfloat[] queryVec = embedder.encodeOne(prompt);\n\n// 2) Look up under a metadata scope. The TAG filter and the KNN\n//    travel together in one FT.SEARCH.\nLookupResult result = cache.lookup(\n        queryVec, \"acme\", \"en\", \"gpt-4.5-2026\", \"ok\", null);\n\nString response;\nif (result instanceof CacheHit hit) {\n    response = hit.response();\n    System.out.printf(\"hit (%.3f): %s%n\", hit.distance(), response);\n} else {\n    // 3a) Miss — call the LLM. (Use your real client here.)\n    response = callLlm(prompt);\n\n    // 3b) Cache the new entry. Reuses the same embedding bytes the\n    //     lookup used, so we don't pay the encoder twice.\n    cache.put(\n            prompt,\n            response,\n            queryVec,\n            \"acme\",\n            \"en\",\n            \"gpt-4.5-2026\",\n            \"ok\",\n            null,   // ttl override (null = default)\n            null    // entry id (null = generated)\n    );\n}","section_id":"the-cache-helper"},{"id":"data-model-ex0","language":"text","code":"cache:7c3f8a1b9e02\n  prompt=How do I return an item?\n  response=You can return any unworn item within 30 days...\n  tenant=acme\n  locale=en\n  model_version=gpt-4.5-2026\n  safety=ok\n  created_ts=1715990400.123\n  hit_count=4\n  embedding=<384 × float32 little-endian bytes>","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"FT.CREATE semcache:idx\n  ON HASH PREFIX 1 cache:\n  SCHEMA\n    prompt         TEXT\n    response       TEXT\n    tenant         TAG\n    locale         TAG\n    model_version  TAG\n    safety         TAG\n    created_ts     NUMERIC SORTABLE\n    hit_count      NUMERIC SORTABLE\n    embedding      VECTOR HNSW 6 TYPE FLOAT32 DIM 384 DISTANCE_METRIC COSINE","section_id":"data-model"},{"id":"the-query-ex0","language":"java","code":"CommandArgs<byte[], byte[]> args = new CommandArgs<>(ByteArrayCodec.INSTANCE)\n        .add(indexNameBytes)\n        .add(\"(@tenant:{acme} @locale:{en} @model_version:{gpt\\\\-4\\\\.5\\\\-2026} @safety:{ok})\"\n                + \"=>[KNN 1 @embedding $vec AS distance]\")\n        .add(\"RETURN\").add(7)\n        .add(\"prompt\").add(\"response\").add(\"tenant\").add(\"locale\")\n        .add(\"model_version\").add(\"hit_count\").add(\"distance\")\n        .add(\"SORTBY\").add(\"distance\").add(\"ASC\")\n        .add(\"LIMIT\").add(0).add(1)\n        .add(\"PARAMS\").add(2).add(\"vec\".getBytes()).add(LocalEmbedder.toBytes(queryVec))\n        .add(\"DIALECT\").add(2);\n\nList<Object> raw = sync.dispatch(\n        FtCommand.FT_SEARCH,\n        new NestedMultiOutput<>(ByteArrayCodec.INSTANCE),\n        args\n);","section_id":"the-query"},{"id":"the-mock-llm-ex0","language":"java","code":"import com.redis.semcache.MockLLM;\n\nMockLLM llm = new MockLLM(\"gpt-4.5-2026\", 1500.0);\nMockLLM.Response response = llm.complete(\"What is your return policy?\");\n// response.response()      — the templated answer text\n// response.latencyMs()     — wall-clock time the call took\n// response.totalTokens()   — estimated prompt + completion tokens","section_id":"the-mock-llm"},{"id":"pre-seeding-the-cache-ex0","language":"java","code":"import com.redis.semcache.SeedCache;\n\ncache.createIndex();\nSeedCache.seed(cache, embedder, \"acme\", \"en\", \"gpt-4.5-2026\");","section_id":"pre-seeding-the-cache"},{"id":"run-the-demo-locally-ex0","language":"bash","code":"git clone https://github.com/redis/docs.git\n    cd docs/content/develop/use-cases/semantic-cache/java-lettuce","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex1","language":"bash","code":"mvn -q package","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex2","language":"bash","code":"java -jar target/semantic-cache-lettuce.jar","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex3","language":"bash","code":"mvn -q exec:java","section_id":"run-the-demo-locally"}]}
{"schema_version":2,"id":"develop/use-cases/semantic-cache/java-lettuce/README","title":"","url":"https://redis.io/docs/latest/develop/use-cases/semantic-cache/java-lettuce/readme/","summary":"","tags":[],"last_updated":"2026-05-22T09:34:16+01:00","page_type":"content","content_hash":"167c493132753926c44a8b9c4db07d30471eb656c46c52a66baf491c8f99ded8","sections":[{"id":"content","title":"Content","role":"content","text":"# Redis semantic-cache demo (Java + Lettuce)\n\nSee `_index.md` for the full walkthrough. Quick start:\n\n[code example]\n\nThen open <http://localhost:8090>.\n\nNotable flags (full list with `--help`):\n\n| Flag                      | Default            |\n|---------------------------|--------------------|\n| `--port`                  | `8090`             |\n| `--redis-host`            | `localhost`        |\n| `--redis-port`            | `6379`             |\n| `--index-name`            | `semcache:idx`     |\n| `--key-prefix`            | `cache:`           |\n| `--ttl-seconds`           | `3600`             |\n| `--threshold`             | `0.5`              |\n| `--llm-latency-ms`        | `1500.0`           |\n| `--no-reset`              | (re-seeds by default) |"}],"examples":[{"id":"content-ex0","language":"bash","code":"# 1. Make sure Redis with the Search module is running on localhost:6379.\n# 2. Build the fat jar (first build pulls Lettuce, DJL, and the PyTorch\n#    native libraries; takes a minute or two):\nmvn -q package\n\n# 3. Run. The first run downloads the sentence-transformers/all-MiniLM-L6-v2\n#    PyTorch weights into the local DJL cache (~90 MB).\njava -jar target/semantic-cache-lettuce.jar\n\n# Or with Maven directly:\nmvn -q exec:java","section_id":"content"}]}
{"schema_version":2,"id":"develop/use-cases/semantic-cache/nodejs","title":"Redis semantic cache with node-redis","url":"https://redis.io/docs/latest/develop/use-cases/semantic-cache/nodejs/","summary":"Build a Redis-backed semantic cache for LLM responses in Node.js with node-redis and @xenova/transformers","aliases":["/develop/use-cases/semantic-cache/node-redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T17:06:11+01:00","children":[],"page_type":"content","content_hash":"4d33255e21395b027cd5d5c38129853386ae5469ec27526cdc109ba2f68a7824","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a small Redis-backed semantic cache for LLM responses in Node.js with [`node-redis`](https://redis.io/docs/latest/develop/clients/nodejs) and the [`@xenova/transformers`](https://www.npmjs.com/package/@xenova/transformers) library. It includes a local web server built with Node's standard `http` module so you can send paraphrased prompts at a mock LLM, watch the cache decide hit or miss, sweep the cosine-distance threshold, and see the cumulative latency and token savings build up."},{"id":"overview","title":"Overview","role":"overview","text":"Each cache entry is stored as a single Redis [Hash](https://redis.io/docs/latest/develop/data-types/hashes) at `cache:<id>`. The hash holds the original prompt, the LLM's response, the raw `float32` bytes of a 384-dimensional embedding of the prompt, and metadata fields — tenant, locale, model version, safety flag — plus a `created_ts` and a `hit_count`. A single [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) index covers the embedding field and every metadata field, so one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call with a `KNN` clause does the vector lookup *and* the TAG pre-filter in the same round trip — no cross-store joins.\n\nThe lookup is thresholded: [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) always returns the nearest entry that satisfies the filters, but the application only serves it as a hit when the reported cosine distance is at or below `distanceThreshold`. Anything further away is treated as a miss; the caller runs the LLM and writes the new prompt, response, and embedding back to the same key pattern with a TTL.\n\nThe embedder is [`@xenova/transformers`](https://www.npmjs.com/package/@xenova/transformers) running the ONNX-exported [`Xenova/all-MiniLM-L6-v2`](https://huggingface.co/Xenova/all-MiniLM-L6-v2) model, which is the same encoder the [Python example](https://redis.io/docs/latest/develop/use-cases/semantic-cache/redis-py) uses. Embeddings produced by the two implementations are semantically equivalent — paraphrase distances differ only at the fourth decimal place — so a cache populated by one demo can be queried by the other against the same Redis instance.\n\nThat gives you:\n\n* A single round trip for lookup — vector KNN + metadata pre-filter in one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search).\n* Tens of milliseconds on a hit vs. a multi-second LLM call on a miss; the embedding step is the bottleneck either way, and that's a model-side cost, not a Redis one.\n* Tenant, locale, and model-version isolation enforced inside the query, not in application code — a write under one tenant cannot be served to another.\n* Bounded memory: every entry has an [`EXPIRE`](https://redis.io/docs/latest/commands/expire) TTL, and a database-level [eviction policy](https://redis.io/docs/latest/develop/reference/eviction) (LRU / LFU) caps the cache size under pressure."},{"id":"how-it-works","title":"How it works","role":"content","text":"A query goes through three stages: **embed**, **lookup**, and (on a miss) **call the LLM and write back**."},{"id":"hit-path-the-goal","title":"Hit path (the goal)","role":"content","text":"1. The application calls `embedder.encodeOne(prompt)` to turn the incoming text into a 384-dimensional `Float32Array`.\n2. `cache.lookup({ queryVec, tenant, locale, modelVersion })` runs [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) with a TAG pre-filter and a `KNN 1` clause. Redis returns the closest cached prompt that satisfies the filters along with its cosine distance.\n3. If the distance is at or below the threshold, the cache returns a hit (`{ kind: 'hit', ... }`) containing the cached response. The helper also runs an [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby) on `hit_count` and an [`EXPIRE`](https://redis.io/docs/latest/commands/expire) refresh inside a [`MULTI/EXEC`](https://redis.io/docs/latest/commands/multi), so a frequently used answer keeps its TTL and the demo UI can see which entries are load-bearing.\n4. The LLM is not called at all. The application returns the cached response to the user."},{"id":"miss-path","title":"Miss path","role":"content","text":"When the distance is above the threshold — or there is no candidate in scope at all — the helper returns a miss instead, carrying the distance of the nearest candidate (if any) for logging. The application then:\n\n1. Calls the LLM with the prompt.\n2. Calls `cache.put({ prompt, response, embedding, tenant, locale, modelVersion })`. The same embedding the lookup used is reused — no re-encode. The helper writes the Hash with [`HSET`](https://redis.io/docs/latest/commands/hset) and an [`EXPIRE`](https://redis.io/docs/latest/commands/expire) TTL inside a single [`MULTI/EXEC`](https://redis.io/docs/latest/commands/multi) so the entry never lands without a TTL on a partial failure.\n3. Returns the LLM's response to the user. The next semantically similar prompt under the same metadata scope will be a hit."},{"id":"the-cache-helper","title":"The cache helper","role":"content","text":"The `RedisSemanticCache` class wraps the Redis Search index and the lookup / write flow\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/semantic-cache/nodejs/cache.js)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each cache entry is one Redis Hash. The vector field is raw little-endian `float32` bytes — no JSON wrapping — because the Redis Search vector encoding expects exactly that. Node's `Float32Array` is little-endian on every supported platform (x86_64, arm64), so a `Buffer.from(vec.buffer)` produces the exact bytes Redis Search reads.\n\n[code example]\n\nThe Redis Search index schema treats every field as queryable in its natural type:\n\n[code example]"},{"id":"the-query","title":"The query","role":"content","text":"The lookup is a hybrid query: a TAG pre-filter expression in parentheses, then `=>[KNN 1 @embedding $vec]`. With `DIALECT: 2`, Redis applies the filter first and KNN-ranks only the matching documents. In `node-redis`:\n\n[code example]\n\n`distance` is the cosine *distance* (0 means identical, 2 means opposite). The result is sorted ascending, so the top row is the closest candidate. The application inspects `distance` against the threshold and decides hit or miss in user code — Redis returns the row either way, and treating it as a hit or a miss is a policy decision the cache helper owns, not a server-side filter."},{"id":"the-mock-llm","title":"The mock LLM","role":"content","text":"To make the latency and token savings visible without requiring an API key, `mockLlm.js` provides a deterministic stand-in\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/semantic-cache/nodejs/mockLlm.js)):\n\n[code example]\n\nThe mock sleeps for the configured latency, then keyword-matches against a small FAQ table to produce an answer. The deliberate slowness is what makes a hit visibly cheaper than a miss in the demo. In production code, you would replace `MockLLM` with your real client of choice — OpenAI's Node SDK, Anthropic's SDK, an internal vLLM endpoint, anything — without changing the cache helper."},{"id":"pre-seeding-the-cache","title":"Pre-seeding the cache","role":"content","text":"In a real deployment the cache fills up organically: a first-time question is a miss, the LLM answers, and the response is written back. For the demo, `seedCache.js` pre-loads a small set of canonical FAQ prompts so the very first query lands on a hit\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/semantic-cache/nodejs/seedCache.js)):\n\n[code example]\n\nThe seed list stores the canonical phrasing of each question (\"What is your return policy?\"). Paraphrases of any of these prompts (\"How do I return an item?\", \"Can I get a refund?\") embed close to the canonical entry, so the cache lookup serves the stored response without ever calling the model."},{"id":"the-interactive-demo","title":"The interactive demo","role":"content","text":"`demoServer.js` runs a Node `http` server. The HTML page lets you:\n\n* Type a prompt and toggle metadata: tenant, locale, model version. Each combination is a separate cache namespace inside the same index.\n* Slide the cosine-distance threshold and see hits flip to misses (and back) on the same prompt, with the actual distance reported on each query.\n* Submit with **Ask** to run the full hit-or-miss path (calls the LLM on a miss, writes the answer back). Submit with **Lookup only (no LLM)** to sweep the threshold against a fixed prompt without polluting the cache.\n* Watch the cumulative panel build up: total queries, cache hits, cache misses, hit ratio, tokens not spent, LLM milliseconds not waited.\n* Inspect every cached entry, including remaining TTL and total hit count, and drop individual entries to simulate eviction.\n\nThe server holds one `LocalEmbedder`, one `RedisSemanticCache`, and one `MockLLM` for the lifetime of the process. The HTML page is shared with the Python demo and is loaded from `index.html` next to `demoServer.js`. Endpoints:\n\n| Endpoint        | What it does                                                                  |\n|-----------------|-------------------------------------------------------------------------------|\n| `GET  /state`   | Index info and the full list of cached entries.                               |\n| `POST /query`   | Embed the prompt, run `FT.SEARCH`, on miss call the LLM and write back.       |\n| `POST /reset`   | Drop every cached entry and re-seed from the FAQ list.                        |\n| `POST /drop`    | Delete a single cached entry by id.                                           |"},{"id":"run-the-demo-locally","title":"Run the demo locally","role":"content","text":"1.  Clone the [`redis/docs`](https://github.com/redis/docs) repository and change into the example\n    directory:\n\n    [code example]\n\n2.  Install the dependencies:\n\n    [code example]\n\n3.  Make sure a Redis instance with the Redis Search module is running locally on\n    port 6379. [Redis Stack](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack) or\n    [Redis 8 with Search](https://redis.io/docs/latest/develop/ai/search-and-query) both work.\n\n4.  Start the demo server. The first run downloads the ONNX-exported\n    `Xenova/all-MiniLM-L6-v2` model into the local Hugging Face cache:\n\n    [code example]\n\n5.  Open <http://localhost:8087> and try some queries:\n\n    * **\"What is your return policy?\"** — exact match against the seed, distance ≈ 0,\n      hit at any threshold.\n    * **\"How fast is delivery?\"** — paraphrase of the shipping seed; distance\n      around 0.30, hit at the default threshold of 0.5.\n    * **\"How do I return an item?\"** — slightly looser paraphrase of the returns\n      seed; distance around 0.49, still a hit at the default threshold. Slide\n      the threshold down to 0.4 to see this one flip to a miss.\n    * **\"What payment methods do you accept?\"** — unrelated to anything in the\n      seed; distance > 0.8, so you'll see a miss, the mock LLM kicks in for\n      ~1.5 s, the new answer is cached, and a follow-up of the same question\n      is now an immediate hit.\n    * Switch the **Tenant** dropdown to `globex` or `initech` and re-ask any\n      seeded question — the result flips to a miss because the cache entries\n      live under `acme`. That's the metadata pre-filter at work inside `FT.SEARCH`.\n\nThe server is read/write against your local Redis. The default index name is `semcache:idx` and entry keys live under `cache:`. Flags mirror the Python demo: `--no-reset` to keep an existing cache across restarts, `--threshold` to change the default cosine-distance cutoff, or `--llm-latency-ms` to make the mock LLM faster or slower for the demo."}],"examples":[{"id":"the-cache-helper-ex0","language":"javascript","code":"import { createClient } from 'redis';\nimport { RedisSemanticCache } from './cache.js';\nimport { LocalEmbedder } from './embeddings.js';\n\nconst client = createClient();\nawait client.connect();\n\nconst cache = new RedisSemanticCache({\n  client,\n  indexName: 'semcache:idx',\n  distanceThreshold: 0.5,    // cosine distance, lower = stricter\n  defaultTtlSeconds: 3600,   // one hour\n});\nconst embedder = await LocalEmbedder.create();  // Xenova/all-MiniLM-L6-v2\n\n// One-time index setup (idempotent).\nawait cache.createIndex();\n\n// 1) Embed the prompt.\nconst prompt = 'How do I return an item?';\nconst queryVec = await embedder.encodeOne(prompt);\n\n// 2) Look up under a metadata scope. The TAG filter and the KNN\n//    travel together in one FT.SEARCH.\nconst result = await cache.lookup({\n  queryVec,\n  tenant: 'acme',\n  locale: 'en',\n  modelVersion: 'gpt-4.5-2026',\n});\n\nlet response;\nif (result.kind === 'hit') {\n  response = result.response;\n  console.log(`hit (${result.distance.toFixed(3)}): ${response}`);\n} else {\n  // 3a) Miss — call the LLM. (Use your real client here.)\n  response = await callLlm(prompt);\n\n  // 3b) Cache the new entry. Reuses the same embedding bytes the\n  //     lookup used, so we don't pay the encoder twice.\n  await cache.put({\n    prompt,\n    response,\n    embedding: queryVec,\n    tenant: 'acme',\n    locale: 'en',\n    modelVersion: 'gpt-4.5-2026',\n  });\n}","section_id":"the-cache-helper"},{"id":"data-model-ex0","language":"text","code":"cache:7c3f8a1b9e02\n  prompt=How do I return an item?\n  response=You can return any unworn item within 30 days...\n  tenant=acme\n  locale=en\n  model_version=gpt-4.5-2026\n  safety=ok\n  created_ts=1715990400.123\n  hit_count=4\n  embedding=<384 × float32 little-endian bytes>","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"FT.CREATE semcache:idx\n  ON HASH PREFIX 1 cache:\n  SCHEMA\n    prompt         TEXT\n    response       TEXT\n    tenant         TAG\n    locale         TAG\n    model_version  TAG\n    safety         TAG\n    created_ts     NUMERIC SORTABLE\n    hit_count      NUMERIC SORTABLE\n    embedding      VECTOR HNSW 6 TYPE FLOAT32 DIM 384 DISTANCE_METRIC COSINE","section_id":"data-model"},{"id":"the-query-ex0","language":"javascript","code":"const result = await client.ft.search(\n  'semcache:idx',\n  '(@tenant:{acme} @locale:{en} @model_version:{gpt\\-4\\.5\\-2026} @safety:{ok})'\n    + '=>[KNN 1 @embedding $vec AS distance]',\n  {\n    PARAMS: { vec: Buffer.from(queryVec.buffer, queryVec.byteOffset, queryVec.byteLength) },\n    DIALECT: 2,\n    SORTBY: 'distance',\n    RETURN: ['prompt', 'response', 'tenant', 'locale',\n             'model_version', 'hit_count', 'distance'],\n    LIMIT: { from: 0, size: 1 },\n  },\n);","section_id":"the-query"},{"id":"the-mock-llm-ex0","language":"javascript","code":"import { MockLLM } from './mockLlm.js';\n\nconst llm = new MockLLM({ latencyMs: 1500.0 });\nconst response = await llm.complete('What is your return policy?');\n// response.response       — the templated answer text\n// response.latencyMs      — wall-clock time the call took\n// response.totalTokens    — estimated prompt + completion tokens","section_id":"the-mock-llm"},{"id":"pre-seeding-the-cache-ex0","language":"javascript","code":"import { seed } from './seedCache.js';\n\nawait cache.createIndex();\nawait seed(cache, embedder, { tenant: 'acme', locale: 'en' });","section_id":"pre-seeding-the-cache"},{"id":"run-the-demo-locally-ex0","language":"bash","code":"git clone https://github.com/redis/docs.git\n    cd docs/content/develop/use-cases/semantic-cache/nodejs","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex1","language":"bash","code":"npm install","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex2","language":"bash","code":"npm start","section_id":"run-the-demo-locally"}]}
{"schema_version":2,"id":"develop/use-cases/semantic-cache/php","title":"Redis semantic cache with Predis","url":"https://redis.io/docs/latest/develop/use-cases/semantic-cache/php/","summary":"Build a Redis-backed semantic cache for LLM responses in PHP with Predis and transformers-php","aliases":["/develop/use-cases/semantic-cache/predis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T17:06:11+01:00","children":[],"page_type":"content","content_hash":"67b3fa5d00b8b99ecacbec9df6526de2be887c891d9a0d43506a69ca1be08dfa","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a small Redis-backed semantic cache for LLM responses in PHP with [Predis](https://redis.io/docs/latest/develop/clients/php) and [TransformersPHP](https://transformers.codewithkyrian.com/) running the [`sentence-transformers/all-MiniLM-L6-v2`](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2) encoder locally on ONNX Runtime. It includes a local web server built with PHP's built-in development HTTP server so you can send paraphrased prompts at a mock LLM, watch the cache decide hit or miss, sweep the cosine-distance threshold, and see the cumulative latency and token savings build up."},{"id":"overview","title":"Overview","role":"overview","text":"Each cache entry is stored as a single Redis [Hash](https://redis.io/docs/latest/develop/data-types/hashes) at `cache:<id>`. The hash holds the original prompt, the LLM's response, the raw `float32` bytes of a 384-dimensional embedding of the prompt, and metadata fields — tenant, locale, model version, safety flag — plus a `created_ts` and a `hit_count`. A single [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) index covers the embedding field and every metadata field, so one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call with a `KNN` clause does the vector lookup *and* the TAG pre-filter in the same round trip — no cross-store joins.\n\nThe lookup is thresholded: [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) always returns the nearest entry that satisfies the filters, but the application only serves it as a hit when the reported cosine distance is at or below `distanceThreshold`. Anything further away is treated as a miss; the caller runs the LLM and writes the new prompt, response, and embedding back to the same key pattern with a TTL.\n\nThe embedder is [TransformersPHP](https://transformers.codewithkyrian.com/) running the [`Xenova/all-MiniLM-L6-v2`](https://huggingface.co/Xenova/all-MiniLM-L6-v2) ONNX export — the same 384-dimensional encoder the [Node.js example](https://redis.io/docs/latest/develop/use-cases/semantic-cache/nodejs) uses. The library is the established choice for vector embeddings in PHP (see [Index and query vectors](https://redis.io/docs/latest/develop/clients/php/vecsearch) for the precedent). Cosine distances differ from the Python and Jedis ports by only a few thousandths because of small numerical differences between ONNX Runtime and PyTorch, so a cache populated by one demo can be queried by another against the same Redis instance with very nearly the same hit/miss behaviour.\n\nThat gives you:\n\n* A single round trip for lookup — vector KNN + metadata pre-filter in one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search).\n* Tens of milliseconds on a hit vs. a multi-second LLM call on a miss; the embedding step is the bottleneck either way, and that's a model-side cost, not a Redis one.\n* Tenant, locale, and model-version isolation enforced inside the query, not in application code — a write under one tenant cannot be served to another.\n* Bounded memory: every entry has an [`EXPIRE`](https://redis.io/docs/latest/commands/expire) TTL, and a database-level [eviction policy](https://redis.io/docs/latest/develop/reference/eviction) (LRU / LFU) caps the cache size under pressure."},{"id":"how-it-works","title":"How it works","role":"content","text":"A query goes through three stages: **embed**, **lookup**, and (on a miss) **call the LLM and write back**."},{"id":"hit-path-the-goal","title":"Hit path (the goal)","role":"content","text":"1. The application calls `$embedder->encodeOne($prompt)` to turn the incoming text into a 384-element `float` array.\n2. `$cache->lookup($queryVec, tenant: ..., locale: ..., modelVersion: ...)` runs [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) with a TAG pre-filter and a `KNN 1` clause. Redis returns the closest cached prompt that satisfies the filters along with its cosine distance.\n3. If the distance is at or below the threshold, the cache returns a `CacheHit` containing the cached response. The helper also issues an [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby) on `hit_count` and an [`EXPIRE`](https://redis.io/docs/latest/commands/expire) refresh inside a [`MULTI/EXEC`](https://redis.io/docs/latest/commands/multi), so a frequently used answer keeps its TTL and the demo UI can see which entries are load-bearing.\n4. The LLM is not called at all. The application returns the cached response to the user."},{"id":"miss-path","title":"Miss path","role":"content","text":"When the distance is above the threshold — or there is no candidate in scope at all — the helper returns a `CacheMiss` instead, carrying the distance of the nearest candidate (if any) for logging. The application then:\n\n1. Calls the LLM with the prompt.\n2. Calls `$cache->put($prompt, $response, $embedding, tenant: ..., locale: ..., modelVersion: ...)`. The same embedding the lookup used is reused — no re-encode. The helper writes the Hash with [`HSET`](https://redis.io/docs/latest/commands/hset) and an [`EXPIRE`](https://redis.io/docs/latest/commands/expire) TTL inside a single [`MULTI/EXEC`](https://redis.io/docs/latest/commands/multi) so the entry never lands without a TTL on a partial failure.\n3. Returns the LLM's response to the user. The next semantically similar prompt under the same metadata scope will be a hit."},{"id":"the-cache-helper","title":"The cache helper","role":"content","text":"The `RedisSemanticCache` class wraps the Redis Search index and the lookup / write flow\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/semantic-cache/php/src/RedisSemanticCache.php)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each cache entry is one Redis Hash. The vector field is raw little-endian `float32` bytes — no JSON wrapping — because the Redis Search vector encoding expects exactly that. The helper packs the embedding with PHP's [`pack('g*', ...)`](https://www.php.net/manual/en/function.pack.php) (the `g` format is a little-endian single-precision IEEE-754 float), matching the encoding the Python, Node.js, Go, and Jedis ports write.\n\n[code example]\n\nThe Redis Search index schema treats every field as queryable in its natural type:\n\n[code example]"},{"id":"the-query","title":"The query","role":"content","text":"The lookup is a hybrid query: a TAG pre-filter expression in parentheses, then `=>[KNN 1 @embedding $vec]`. With `DIALECT 2`, Redis applies the filter first and KNN-ranks only the matching documents. In Predis:\n\n[code example]\n\n`distance` is the cosine *distance* (0 means identical, 2 means opposite). The result is sorted ascending, so the top row is the closest candidate. The application inspects `distance` against the threshold and decides hit or miss in user code — Redis returns the row either way, and treating it as a hit or a miss is a policy decision the cache helper owns, not a server-side filter.\n\nPredis 3.x defaults to query dialect 2; the cache helper sets it explicitly so the code reads correctly against earlier versions too. See [Index and query vectors](https://redis.io/docs/latest/develop/clients/php/vecsearch) for more on Predis's vector-search helpers."},{"id":"the-mock-llm","title":"The mock LLM","role":"content","text":"To make the latency and token savings visible without requiring an API key, `MockLLM.php` provides a deterministic stand-in\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/semantic-cache/php/src/MockLLM.php)):\n\n[code example]\n\nThe mock sleeps for the configured latency, then keyword-matches against a small FAQ table to produce an answer. The deliberate slowness is what makes a hit visibly cheaper than a miss in the demo. In production code, you would replace `MockLLM` with your real client of choice — an HTTP call to OpenAI, Anthropic, a self-hosted vLLM endpoint, anything — without changing the cache helper."},{"id":"pre-seeding-the-cache","title":"Pre-seeding the cache","role":"content","text":"In a real deployment the cache fills up organically: a first-time question is a miss, the LLM answers, and the response is written back. For the demo, `SeedCache.php` pre-loads a small set of canonical FAQ prompts so the very first query lands on a hit\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/semantic-cache/php/src/SeedCache.php)):\n\n[code example]\n\nThe seed list stores the canonical phrasing of each question (\"What is your return policy?\"). Paraphrases of any of these prompts (\"How do I return an item?\", \"Can I get a refund?\") embed close to the canonical entry, so the cache lookup serves the stored response without ever calling the model.\n\nThe seed helper embeds the prompts one at a time rather than as a single batched `encodeMany` call. TransformersPHP's attention-mask handling produces slightly different mean-pooled vectors for variable-length inputs inside a batch versus single-input calls, and that 0.01-cosine-distance drift would otherwise make a self-lookup of a seeded prompt look like a near-match instead of a clean zero-distance hit."},{"id":"the-interactive-demo","title":"The interactive demo","role":"content","text":"`public/index.php` is a front controller for PHP's built-in HTTP server — no Slim, no Symfony, no embedded framework. The HTML page lets you:\n\n* Type a prompt and toggle metadata: tenant, locale, model version. Each combination is a separate cache namespace inside the same index.\n* Slide the cosine-distance threshold and see hits flip to misses (and back) on the same prompt, with the actual distance reported on each query.\n* Submit with **Ask** to run the full hit-or-miss path (calls the LLM on a miss, writes the answer back). Submit with **Lookup only (no LLM)** to sweep the threshold against a fixed prompt without polluting the cache.\n* Watch the cumulative panel build up: total queries, cache hits, cache misses, hit ratio, tokens not spent, LLM milliseconds not waited.\n* Inspect every cached entry, including remaining TTL and total hit count, and drop individual entries to simulate eviction.\n\nThe front controller rebuilds a `LocalEmbedder`, a `RedisSemanticCache`, and a `MockLLM` on every request because PHP's built-in server is single-process and does not share user-land objects between requests. The first request is therefore slow (the embedder reloads the tokenizer and ONNX session); subsequent requests reuse the cached model files on disk and are fast. The HTML page is shared with the Python, Node.js, Go, and Jedis demos; the same `index.html` works against any of the language ports without modification. Endpoints:\n\n| Endpoint        | What it does                                                                  |\n|-----------------|-------------------------------------------------------------------------------|\n| `GET  /state`   | Index info and the full list of cached entries.                               |\n| `POST /query`   | Embed the prompt, run `FT.SEARCH`, on miss call the LLM and write back.       |\n| `POST /reset`   | Drop every cached entry and re-seed from the FAQ list.                        |\n| `POST /drop`    | Delete a single cached entry by id.                                           |"},{"id":"configuration","title":"Configuration","role":"configuration","text":"PHP's CLI flag parsing is awkward, so the demo reads configuration from environment variables rather than `--`-style flags. All variables have defaults; override only what you need.\n\n| Variable                    | Default        | Purpose                                            |\n|-----------------------------|----------------|----------------------------------------------------|\n| `SEMCACHE_PORT`             | `8093`         | TCP port for the dev server                        |\n| `SEMCACHE_REDIS_HOST`       | `localhost`    | Redis host                                         |\n| `SEMCACHE_REDIS_PORT`       | `6379`         | Redis port                                         |\n| `SEMCACHE_INDEX_NAME`       | `semcache:idx` | Redis Search index name                            |\n| `SEMCACHE_KEY_PREFIX`       | `cache:`       | Prefix for cache entry hashes                      |\n| `SEMCACHE_TTL_SECONDS`      | `3600`         | TTL on each cache entry                            |\n| `SEMCACHE_THRESHOLD`        | `0.5`          | Default cosine-distance threshold                  |\n| `SEMCACHE_LLM_LATENCY_MS`   | `1500`         | Mock LLM sleep, milliseconds                       |\n| `SEMCACHE_RESEED`           | `true`         | Re-seed FAQ entries on the first request           |"},{"id":"run-the-demo-locally","title":"Run the demo locally","role":"content","text":"1.  Clone the [`redis/docs`](https://github.com/redis/docs) repository and change into the example\n    directory:\n\n    [code example]\n\n2.  Make sure a Redis instance with the Redis Search module is running locally on\n    port 6379. [Redis Stack](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack) or\n    [Redis 8 with Search](https://redis.io/docs/latest/develop/ai/search-and-query) both work.\n\n3.  Install the PHP dependencies with [Composer](https://getcomposer.org/). This step also\n    downloads the prebuilt TransformersPHP native libraries (ONNX Runtime, OpenBLAS, Rindow's matlib FFI shim) for your platform — about 90 MB on macOS arm64:\n\n    [code example]\n\n    The example requires PHP 8.2 or later and uses [Predis](https://github.com/predis/predis) for Redis access, with no PHP extensions required beyond the standard `ffi` shipped with most builds.\n\n4.  Start the demo. The included `run.sh` sets the PHP `ffi.enable=true` directive that\n    TransformersPHP needs at runtime, caps `post_max_size` at 1 MiB to match the demo's\n    body-size budget, and silences PHP 8.4 deprecation notices that `codewithkyrian/transformers`\n    0.5.x emits on the latest PHP — the underlying inference is unaffected. The first run\n    downloads the `Xenova/all-MiniLM-L6-v2` ONNX weights (~30 MB) into the local Hugging\n    Face cache; every subsequent run is offline:\n\n    [code example]\n\n    To pick a different port or threshold, set the corresponding environment variable\n    before invoking the script:\n\n    [code example]\n\n5.  Open <http://localhost:8093> and try some queries:\n\n    * **\"What is your return policy?\"** — exact match against the seed, distance ≈ 0,\n      hit at any threshold.\n    * **\"How fast is delivery?\"** — paraphrase of the shipping seed; distance\n      around 0.30, hit at the default threshold of 0.5.\n    * **\"How do I return an item?\"** — slightly looser paraphrase of the returns\n      seed; distance around 0.49, still a hit at the default threshold. Slide\n      the threshold down to 0.4 to see this one flip to a miss.\n    * **\"What payment methods do you accept?\"** — unrelated to anything in the\n      seed, but the embedding model still finds shallow surface-form similarity\n      with the canonical \"What ___ do you ___?\" phrasing of the seeds, so the\n      distance lands around 0.66. At the default threshold of 0.5 you will see\n      a miss, the mock LLM kicks in for ~1.5 s, the new answer is cached, and\n      a follow-up of the same question is now an immediate hit. At threshold\n      0.7 the same query is a borderline hit — that's the cosine-distance\n      cutoff working exactly as advertised.\n    * Switch the **Tenant** dropdown to `globex` or `initech` and re-ask any\n      seeded question — the result flips to a miss because the cache entries\n      live under `acme`. That's the metadata pre-filter at work inside `FT.SEARCH`.\n\nThe server is read/write against your local Redis. The default index name is `semcache:idx` and entry keys live under `cache:`. Set `SEMCACHE_RESEED=false` to keep an existing cache across restarts, `SEMCACHE_THRESHOLD` to change the default cosine-distance cutoff, `SEMCACHE_LLM_LATENCY_MS` to make the mock LLM faster or slower for the demo, or `SEMCACHE_PORT` to listen on a different port."}],"examples":[{"id":"the-cache-helper-ex0","language":"php","code":"use Predis\\Client;\nuse Redis\\SemanticCache\\{RedisSemanticCache, LocalEmbedder, CacheHit};\n\n$client = new Client(['host' => 'localhost', 'port' => 6379]);\n$embedder = LocalEmbedder::create();   // sentence-transformers/all-MiniLM-L6-v2\n\n$cache = new RedisSemanticCache(\n    client: $client,\n    indexName: 'semcache:idx',\n    keyPrefix: 'cache:',\n    distanceThreshold: 0.5,    // cosine distance, lower = stricter\n    defaultTtlSeconds: 3600,   // one hour\n);\n\n// One-time index setup (idempotent).\n$cache->createIndex();\n\n// 1) Embed the prompt.\n$prompt = 'How do I return an item?';\n$queryVec = $embedder->encodeOne($prompt);\n\n// 2) Look up under a metadata scope. The TAG filter and the KNN\n//    travel together in one FT.SEARCH.\n$result = $cache->lookup(\n    queryVec: $queryVec,\n    tenant: 'acme',\n    locale: 'en',\n    modelVersion: 'gpt-4.5-2026',\n);\n\nif ($result instanceof CacheHit) {\n    $response = $result->response;\n    printf(\"hit (%.3f): %s\\n\", $result->distance, $response);\n} else {\n    // 3a) Miss — call the LLM. (Use your real client here.)\n    $response = call_llm($prompt);\n\n    // 3b) Cache the new entry. Reuses the same embedding bytes the\n    //     lookup used, so we don't pay the encoder twice.\n    $cache->put(\n        prompt: $prompt,\n        response: $response,\n        embedding: $queryVec,\n        tenant: 'acme',\n        locale: 'en',\n        modelVersion: 'gpt-4.5-2026',\n    );\n}","section_id":"the-cache-helper"},{"id":"data-model-ex0","language":"text","code":"cache:7c3f8a1b9e02\n  prompt=How do I return an item?\n  response=You can return any unworn item within 30 days...\n  tenant=acme\n  locale=en\n  model_version=gpt-4.5-2026\n  safety=ok\n  created_ts=1715990400.123\n  hit_count=4\n  embedding=<384 × float32 little-endian bytes>","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"FT.CREATE semcache:idx\n  ON HASH PREFIX 1 cache:\n  SCHEMA\n    prompt         TEXT\n    response       TEXT\n    tenant         TAG\n    locale         TAG\n    model_version  TAG\n    safety         TAG\n    created_ts     NUMERIC SORTABLE\n    hit_count      NUMERIC SORTABLE\n    embedding      VECTOR HNSW 6 TYPE FLOAT32 DIM 384 DISTANCE_METRIC COSINE","section_id":"data-model"},{"id":"the-query-ex0","language":"php","code":"use Predis\\Command\\Argument\\Search\\SearchArguments;\n\n$arguments = (new SearchArguments())\n    ->addReturn(7, 'prompt', 'response', 'tenant', 'locale',\n                'model_version', 'hit_count', 'distance')\n    ->sortBy('distance', 'asc')\n    ->limit(0, 1)\n    ->dialect('2')\n    ->params(['vec', pack('g*', ...$queryVec)]);\n\n$raw = $client->ftsearch(\n    'semcache:idx',\n    '(@tenant:{acme} @locale:{en} @model_version:{gpt\\-4\\.5\\-2026} @safety:{ok})'\n        . '=>[KNN 1 @embedding $vec AS distance]',\n    $arguments,\n);","section_id":"the-query"},{"id":"the-mock-llm-ex0","language":"php","code":"use Redis\\SemanticCache\\MockLLM;\n\n$llm = new MockLLM(latencyMs: 1500.0);\n$response = $llm->complete('What is your return policy?');\n// $response['response']         — the templated answer text\n// $response['latency_ms']       — wall-clock time the call took\n// $response['total_tokens']     — estimated prompt + completion tokens","section_id":"the-mock-llm"},{"id":"pre-seeding-the-cache-ex0","language":"php","code":"use Redis\\SemanticCache\\SeedCache;\n\n$cache->createIndex();\nSeedCache::seed($cache, $embedder, tenant: 'acme', locale: 'en');","section_id":"pre-seeding-the-cache"},{"id":"run-the-demo-locally-ex0","language":"bash","code":"git clone https://github.com/redis/docs.git\n    cd docs/content/develop/use-cases/semantic-cache/php","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex1","language":"bash","code":"composer install","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex2","language":"bash","code":"./run.sh","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex3","language":"bash","code":"SEMCACHE_PORT=8093 SEMCACHE_THRESHOLD=0.4 ./run.sh","section_id":"run-the-demo-locally"}]}
{"schema_version":2,"id":"develop/use-cases/semantic-cache/redis-py","title":"Redis semantic cache with redis-py","url":"https://redis.io/docs/latest/develop/use-cases/semantic-cache/redis-py/","summary":"Build a Redis-backed semantic cache for LLM responses in Python with redis-py and sentence-transformers","aliases":["/develop/use-cases/semantic-cache/python"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T17:06:11+01:00","children":[],"page_type":"content","content_hash":"a94b9f2a0b708f8f5247359517af3cc424b30181b6f7a8f93e65fdae72df2472","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a small Redis-backed semantic cache for LLM responses in Python with [`redis-py`](https://redis.io/docs/latest/develop/clients/redis-py) and the [`sentence-transformers`](https://www.sbert.net/) library. It includes a local web server built with the Python standard library so you can send paraphrased prompts at a mock LLM, watch the cache decide hit or miss, sweep the cosine-distance threshold, and see the cumulative latency and token savings build up."},{"id":"overview","title":"Overview","role":"overview","text":"Each cache entry is stored as a single Redis [Hash](https://redis.io/docs/latest/develop/data-types/hashes) at `cache:<id>`. The hash holds the original prompt, the LLM's response, the raw `float32` bytes of a 384-dimensional embedding of the prompt, and metadata fields — tenant, locale, model version, safety flag — plus a `created_ts` and a `hit_count`. A single [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) index covers the embedding field and every metadata field, so one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call with a `KNN` clause does the vector lookup *and* the TAG pre-filter in the same round trip — no cross-store joins.\n\nThe lookup is thresholded: [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) always returns the nearest entry that satisfies the filters, but the application only serves it as a hit when the reported cosine distance is at or below `distance_threshold`. Anything further away is treated as a miss; the caller runs the LLM and writes the new prompt, response, and embedding back to the same key pattern with a TTL.\n\nThat gives you:\n\n* A single round trip for lookup — vector KNN + metadata pre-filter in one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search).\n* Tens of milliseconds on a hit vs. a multi-second LLM call on a miss; the embedding step is the bottleneck either way, and that's a model-side cost, not a Redis one.\n* Tenant, locale, and model-version isolation enforced inside the query, not in application code — a write under one tenant cannot be served to another.\n* Bounded memory: every entry has an [`EXPIRE`](https://redis.io/docs/latest/commands/expire) TTL, and a database-level [eviction policy](https://redis.io/docs/latest/develop/reference/eviction) (LRU / LFU) caps the cache size under pressure."},{"id":"how-it-works","title":"How it works","role":"content","text":"A query goes through three stages: **embed**, **lookup**, and (on a miss) **call the LLM and write back**."},{"id":"hit-path-the-goal","title":"Hit path (the goal)","role":"content","text":"1. The application calls `embedder.encode_one(prompt)` to turn the incoming text into a 384-dimensional `float32` vector.\n2. `cache.lookup(query_vec, tenant=..., locale=..., model_version=...)` runs [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) with a TAG pre-filter and a `KNN 1` clause. Redis returns the closest cached prompt that satisfies the filters along with its cosine distance.\n3. If the distance is at or below the threshold, the cache returns a `CacheHit` containing the cached response. The helper also pipelines an [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby) on `hit_count` and an [`EXPIRE`](https://redis.io/docs/latest/commands/expire) refresh, so a frequently used answer keeps its TTL and the demo UI can see which entries are load-bearing.\n4. The LLM is not called at all. The application returns the cached response to the user."},{"id":"miss-path","title":"Miss path","role":"content","text":"When the distance is above the threshold — or there is no candidate in scope at all — the helper returns a `CacheMiss` instead, carrying the distance of the nearest candidate (if any) for logging. The application then:\n\n1. Calls the LLM with the prompt.\n2. Calls `cache.put(prompt, response, embedding, tenant=..., locale=..., model_version=...)`. The same embedding the lookup used is reused — no re-encode. The helper writes the Hash with [`HSET`](https://redis.io/docs/latest/commands/hset) and an [`EXPIRE`](https://redis.io/docs/latest/commands/expire) TTL in a pipeline.\n3. Returns the LLM's response to the user. The next semantically similar prompt under the same metadata scope will be a hit."},{"id":"the-cache-helper","title":"The cache helper","role":"content","text":"The `RedisSemanticCache` class wraps the Redis Search index and the lookup / write flow\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/semantic-cache/redis-py/cache.py)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each cache entry is one Redis Hash. The vector field is raw little-endian `float32` bytes — no JSON wrapping — because the Redis Search vector encoding expects exactly that.\n\n[code example]\n\nThe Redis Search index schema treats every field as queryable in its natural type:\n\n[code example]\n\nThe `prompt` and `response` TEXT fields aren't used by the cache lookup itself — that's vector-only — but they make it possible to grep the cache by content from `redis-cli` for debugging or admin tooling."},{"id":"the-query","title":"The query","role":"content","text":"The lookup is a hybrid query: a TAG pre-filter expression in parentheses, then `=>[KNN 1 @embedding $vec]`. With `DIALECT 2`, Redis applies the filter first and KNN-ranks only the matching documents.\n\n[code example]\n\n`distance` is the cosine *distance* (0 means identical, 2 means opposite). The result is sorted ascending, so the top row is the closest candidate. The application inspects `distance` against the threshold and decides hit or miss in user code — Redis returns the row either way, and treating it as a hit or a miss is a policy decision the cache helper owns, not a server-side filter."},{"id":"the-mock-llm","title":"The mock LLM","role":"content","text":"To make the latency and token savings visible without requiring an API key, `mock_llm.py` provides a deterministic stand-in\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/semantic-cache/redis-py/mock_llm.py)):\n\n[code example]\n\nThe mock sleeps for the configured latency, then keyword-matches against a small FAQ table to produce an answer. The deliberate slowness is what makes a hit visibly cheaper than a miss in the demo. In production code, you would replace `MockLLM` with your real client of choice — OpenAI, Anthropic, Bedrock, vLLM, Ollama, anything — without changing the cache helper."},{"id":"pre-seeding-the-cache","title":"Pre-seeding the cache","role":"content","text":"In a real deployment the cache fills up organically: a first-time question is a miss, the LLM answers, and the response is written back. For the demo, `seed_cache.py` pre-loads a small set of canonical FAQ prompts so the very first query lands on a hit\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/semantic-cache/redis-py/seed_cache.py)):\n\n[code example]\n\nThe seed list stores the canonical phrasing of each question (\"What is your return policy?\"). Paraphrases of any of these prompts (\"How do I return an item?\", \"Can I get a refund?\") embed close to the canonical entry, so the cache lookup serves the stored response without ever calling the model."},{"id":"the-interactive-demo","title":"The interactive demo","role":"content","text":"`demo_server.py` runs a ThreadingHTTPServer. The HTML page lets you:\n\n* Type a prompt and toggle metadata: tenant, locale, model version. Each combination is a separate cache namespace inside the same index.\n* Slide the cosine-distance threshold and see hits flip to misses (and back) on the same prompt, with the actual distance reported on each query.\n* Submit with **Ask** to run the full hit-or-miss path (calls the LLM on a miss, writes the answer back). Submit with **Lookup only (no LLM)** to sweep the threshold against a fixed prompt without polluting the cache.\n* Watch the cumulative panel build up: total queries, cache hits, cache misses, hit ratio, tokens not spent, LLM seconds not waited.\n* Inspect every cached entry, including remaining TTL and total hit count, and drop individual entries to simulate eviction.\n\nThe server holds one `LocalEmbedder`, one `RedisSemanticCache`, and one `MockLLM` for the lifetime of the process. Endpoints:\n\n| Endpoint        | What it does                                                                  |\n|-----------------|-------------------------------------------------------------------------------|\n| `GET  /state`   | Index info and the full list of cached entries.                               |\n| `POST /query`   | Embed the prompt, run `FT.SEARCH`, on miss call the LLM and write back.       |\n| `POST /reset`   | Drop every cached entry and re-seed from the FAQ list.                        |\n| `POST /drop`    | Delete a single cached entry by id.                                           |"},{"id":"run-the-demo-locally","title":"Run the demo locally","role":"content","text":"1.  Clone the [`redis/docs`](https://github.com/redis/docs) repository and change into the example\n    directory:\n\n    [code example]\n\n2.  Install the dependencies:\n\n    [code example]\n\n3.  Make sure a Redis instance with the Redis Search module is running locally on\n    port 6379. [Redis Stack](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack) or\n    [Redis 8 with Search](https://redis.io/docs/latest/develop/ai/search-and-query) both work.\n\n4.  Start the demo server. The first run downloads the `all-MiniLM-L6-v2` model\n    (~80 MB) into the local Hugging Face cache:\n\n    [code example]\n\n5.  Open <http://localhost:8085> and try some queries:\n\n    * **\"What is your return policy?\"** — exact match against the seed, distance ≈ 0,\n      hit at any threshold.\n    * **\"How fast is delivery?\"** — paraphrase of the shipping seed; distance\n      around 0.30, hit at the default threshold of 0.5.\n    * **\"How do I return an item?\"** — slightly looser paraphrase of the returns\n      seed; distance around 0.49, still a hit at the default threshold. Slide\n      the threshold down to 0.4 to see this one flip to a miss.\n    * **\"What payment methods do you accept?\"** — unrelated to anything in the\n      seed; distance > 0.8, so you'll see a miss, the mock LLM kicks in for\n      ~1.5 s, the new answer is cached, and a follow-up of the same question\n      is now an immediate hit.\n    * Switch the **Tenant** dropdown to `globex` or `initech` and re-ask any\n      seeded question — the result flips to a miss because the cache entries\n      live under `acme`. That's the metadata pre-filter at work inside `FT.SEARCH`.\n\n    `all-MiniLM-L6-v2` puts FAQ-style paraphrases in the 0.3–0.5 cosine-distance\n    range and unrelated queries above 0.8, which is what motivates the 0.5\n    default. A stricter embedding model (or a domain-tuned one) would let you\n    drop the threshold further; a noisier one would push it up. The right\n    threshold is always a function of the model, the corpus, and how\n    conservative the application needs to be about reuse.\n\nThe server is read/write against your local Redis. The default index name is `semcache:idx` and entry keys live under `cache:`. Pass `--no-reset` to keep an existing cache across restarts, `--threshold` to change the default cosine-distance cutoff, or `--llm-latency-ms` to make the mock LLM faster or slower for the demo."}],"examples":[{"id":"the-cache-helper-ex0","language":"python","code":"import redis\nfrom cache import RedisSemanticCache, CacheHit, CacheMiss\nfrom embeddings import LocalEmbedder\n\n# Use decode_responses=False because the embedding field is raw bytes;\n# the helper decodes text fields explicitly where it needs them.\nr = redis.Redis(host=\"localhost\", port=6379, decode_responses=False)\ncache = RedisSemanticCache(\n    redis_client=r,\n    index_name=\"semcache:idx\",\n    distance_threshold=0.5,    # cosine distance, lower = stricter\n    default_ttl_seconds=3600,  # one hour\n)\nembedder = LocalEmbedder()  # sentence-transformers/all-MiniLM-L6-v2\n\n# One-time index setup (idempotent).\ncache.create_index()\n\n# 1) Embed the prompt.\nprompt = \"How do I return an item?\"\nquery_vec = embedder.encode_one(prompt)\n\n# 2) Look up under a metadata scope. The TAG filter and the KNN\n#    travel together in one FT.SEARCH.\nresult = cache.lookup(\n    query_vec,\n    tenant=\"acme\",\n    locale=\"en\",\n    model_version=\"gpt-4.5-2026\",\n)\n\nif isinstance(result, CacheHit):\n    response = result.response\n    print(f\"hit ({result.distance:.3f}): {response}\")\nelse:\n    # 3a) Miss — call the LLM. (Use your real client here.)\n    response = call_llm(prompt)\n\n    # 3b) Cache the new entry. Reuses the same embedding bytes the\n    #     lookup used, so we don't pay the encoder twice.\n    cache.put(\n        prompt=prompt,\n        response=response,\n        embedding=query_vec,\n        tenant=\"acme\",\n        locale=\"en\",\n        model_version=\"gpt-4.5-2026\",\n    )","section_id":"the-cache-helper"},{"id":"data-model-ex0","language":"text","code":"cache:7c3f8a1b9e02\n  prompt=How do I return an item?\n  response=You can return any unworn item within 30 days...\n  tenant=acme\n  locale=en\n  model_version=gpt-4.5-2026\n  safety=ok\n  created_ts=1715990400.123\n  hit_count=4\n  embedding=<384 × float32 little-endian bytes>","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"FT.CREATE semcache:idx\n  ON HASH PREFIX 1 cache:\n  SCHEMA\n    prompt         TEXT\n    response       TEXT\n    tenant         TAG\n    locale         TAG\n    model_version  TAG\n    safety         TAG\n    created_ts     NUMERIC SORTABLE\n    hit_count      NUMERIC SORTABLE\n    embedding      VECTOR HNSW 6 TYPE FLOAT32 DIM 384 DISTANCE_METRIC COSINE","section_id":"data-model"},{"id":"the-query-ex0","language":"text","code":"FT.SEARCH semcache:idx\n  \"(@tenant:{acme} @locale:{en} @model_version:{gpt\\-4\\.5\\-2026} @safety:{ok})\n     =>[KNN 1 @embedding $vec AS distance]\"\n  PARAMS 2 vec <384-float32-bytes>\n  SORTBY distance\n  RETURN 7 prompt response tenant locale model_version hit_count distance\n  DIALECT 2","section_id":"the-query"},{"id":"the-mock-llm-ex0","language":"python","code":"from mock_llm import MockLLM\n\nllm = MockLLM(latency_ms=1500.0)  # one and a half seconds per call\nresponse = llm.complete(\"What is your return policy?\")\n# response.response       — the templated answer text\n# response.latency_ms     — wall-clock time the call took\n# response.total_tokens   — estimated prompt + completion tokens","section_id":"the-mock-llm"},{"id":"pre-seeding-the-cache-ex0","language":"python","code":"from seed_cache import seed\nfrom cache import RedisSemanticCache\nfrom embeddings import LocalEmbedder\n\ncache = RedisSemanticCache()\nembedder = LocalEmbedder()\ncache.create_index()\nseed(cache, embedder, tenant=\"acme\", locale=\"en\")","section_id":"pre-seeding-the-cache"},{"id":"run-the-demo-locally-ex0","language":"bash","code":"git clone https://github.com/redis/docs.git\n    cd docs/content/develop/use-cases/semantic-cache/redis-py","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex1","language":"bash","code":"pip install redis sentence-transformers numpy","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex2","language":"bash","code":"python demo_server.py","section_id":"run-the-demo-locally"}]}
{"schema_version":2,"id":"develop/use-cases/semantic-cache/ruby","title":"Redis semantic cache with redis-rb","url":"https://redis.io/docs/latest/develop/use-cases/semantic-cache/ruby/","summary":"Build a Redis-backed semantic cache for LLM responses in Ruby with redis-rb and informers","aliases":["/develop/use-cases/semantic-cache/redis-rb"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T17:06:11+01:00","children":[],"page_type":"content","content_hash":"3b32d18a79e67158889ddbac5b674263cbbd1c6396816dc7e619e77f817d0eba","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a small Redis-backed semantic cache for LLM responses in Ruby with [`redis-rb`](https://redis.io/docs/latest/develop/clients/ruby) and the [`informers`](https://github.com/ankane/informers) gem, a Ruby port of Hugging Face transformers that runs the ONNX-exported [`sentence-transformers/all-MiniLM-L6-v2`](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2) encoder locally on CPU. It includes a local web server built with the standard-library [`WEBrick`](https://github.com/ruby/webrick) HTTP server so you can send paraphrased prompts at a mock LLM, watch the cache decide hit or miss, sweep the cosine-distance threshold, and see the cumulative latency and token savings build up."},{"id":"overview","title":"Overview","role":"overview","text":"Each cache entry is stored as a single Redis [Hash](https://redis.io/docs/latest/develop/data-types/hashes) at `cache:<id>`. The hash holds the original prompt, the LLM's response, the raw `float32` bytes of a 384-dimensional embedding of the prompt, and metadata fields — tenant, locale, model version, safety flag — plus a `created_ts` and a `hit_count`. A single [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) index covers the embedding field and every metadata field, so one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call with a `KNN` clause does the vector lookup *and* the TAG pre-filter in the same round trip — no cross-store joins.\n\nThe lookup is thresholded: [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) always returns the nearest entry that satisfies the filters, but the application only serves it as a hit when the reported cosine distance is at or below `distance_threshold`. Anything further away is treated as a miss; the caller runs the LLM and writes the new prompt, response, and embedding back to the same key pattern with a TTL.\n\nThe embedder is [`informers`](https://github.com/ankane/informers) running the ONNX-exported [`sentence-transformers/all-MiniLM-L6-v2`](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2) model — the same 384-dimensional encoder the [Python example](https://redis.io/docs/latest/develop/use-cases/semantic-cache/redis-py), the [Node.js example](https://redis.io/docs/latest/develop/use-cases/semantic-cache/nodejs), the [Go example](https://redis.io/docs/latest/develop/use-cases/semantic-cache/go), and the [Jedis example](https://redis.io/docs/latest/develop/use-cases/semantic-cache/java-jedis) use. Embeddings produced by the Ruby ONNX path are semantically equivalent to the PyTorch ones — paraphrase distances differ by ~0.01, the same drift the Node.js Xenova ONNX path sees — so a cache populated by one demo can be queried by another against the same Redis instance.\n\nThat gives you:\n\n* A single round trip for lookup — vector KNN + metadata pre-filter in one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search).\n* Tens of milliseconds on a hit vs. a multi-second LLM call on a miss; the embedding step is the bottleneck either way, and that's a model-side cost, not a Redis one.\n* Tenant, locale, and model-version isolation enforced inside the query, not in application code — a write under one tenant cannot be served to another.\n* Bounded memory: every entry has an [`EXPIRE`](https://redis.io/docs/latest/commands/expire) TTL, and a database-level [eviction policy](https://redis.io/docs/latest/develop/reference/eviction) (LRU / LFU) caps the cache size under pressure."},{"id":"how-it-works","title":"How it works","role":"content","text":"A query goes through three stages: **embed**, **lookup**, and (on a miss) **call the LLM and write back**."},{"id":"hit-path-the-goal","title":"Hit path (the goal)","role":"content","text":"1. The application calls `embedder.encode_one(prompt)` to turn the incoming text into a 384-element `Array<Float>`.\n2. `cache.lookup(query_vec, tenant:, locale:, model_version:)` runs [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) with a TAG pre-filter and a `KNN 1` clause. Redis returns the closest cached prompt that satisfies the filters along with its cosine distance.\n3. If the distance is at or below the threshold, the cache returns a `CacheHit` containing the cached response. The helper also runs an [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby) on `hit_count` and an [`EXPIRE`](https://redis.io/docs/latest/commands/expire) refresh inside a [`MULTI/EXEC`](https://redis.io/docs/latest/commands/multi), so a frequently used answer keeps its TTL and the demo UI can see which entries are load-bearing.\n4. The LLM is not called at all. The application returns the cached response to the user."},{"id":"miss-path","title":"Miss path","role":"content","text":"When the distance is above the threshold — or there is no candidate in scope at all — the helper returns a `CacheMiss` instead, carrying the distance of the nearest candidate (if any) for logging. The application then:\n\n1. Calls the LLM with the prompt.\n2. Calls `cache.put(prompt:, response:, embedding:, tenant:, locale:, model_version:)`. The same embedding the lookup used is reused — no re-encode. The helper writes the Hash with [`HSET`](https://redis.io/docs/latest/commands/hset) and an [`EXPIRE`](https://redis.io/docs/latest/commands/expire) TTL inside a single [`MULTI/EXEC`](https://redis.io/docs/latest/commands/multi) so the entry never lands without a TTL on a partial failure.\n3. Returns the LLM's response to the user. The next semantically similar prompt under the same metadata scope will be a hit."},{"id":"the-cache-helper","title":"The cache helper","role":"content","text":"The `RedisSemanticCache` class wraps the Redis Search index and the lookup / write flow\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/semantic-cache/ruby/lib/cache.rb)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each cache entry is one Redis Hash. The vector field is raw little-endian `float32` bytes — no JSON wrapping — because the Redis Search vector encoding expects exactly that. The helper packs the `Array<Float>` with Ruby's [`Array#pack`](https://docs.ruby-lang.org/en/master/Array.html#method-i-pack) directive `'e*'`, which is little-endian single-precision float; the resulting `String` is ASCII-8BIT (binary) so `redis-rb` ships the exact bytes without any UTF-8 transcoding.\n\n[code example]\n\nThe Redis Search index schema treats every field as queryable in its natural type:\n\n[code example]"},{"id":"the-query","title":"The query","role":"content","text":"The lookup is a hybrid query: a TAG pre-filter expression in parentheses, then `=>[KNN 1 @embedding $vec]`. With `DIALECT 2`, Redis applies the filter first and KNN-ranks only the matching documents. `redis-rb` doesn't ship dedicated `FT.*` bindings — it exposes a generic `#call` method that lets you send any command, so the helper builds the argument list directly:\n\n[code example]\n\n`distance` is the cosine *distance* (0 means identical, 2 means opposite). The result is sorted ascending, so the top row is the closest candidate. The application inspects `distance` against the threshold and decides hit or miss in user code — Redis returns the row either way, and treating it as a hit or a miss is a policy decision the cache helper owns, not a server-side filter.\n\n`FT.SEARCH` returns a flat array — `[total, key1, [field1, value1, ...], key2, [...], ...]` — that the helper parses into per-document hashes; binary fields like `embedding` and the encoded vector come back as ASCII-8BIT Strings so a future tooling change that wants the raw bytes doesn't have to re-encode."},{"id":"the-mock-llm","title":"The mock LLM","role":"content","text":"To make the latency and token savings visible without requiring an API key, `mock_llm.rb` provides a deterministic stand-in\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/semantic-cache/ruby/lib/mock_llm.rb)):\n\n[code example]\n\nThe mock sleeps for the configured latency, then keyword-matches against a small FAQ table to produce an answer. The deliberate slowness is what makes a hit visibly cheaper than a miss in the demo. In production code, you would replace `MockLLM` with your real client of choice — `openai`, `anthropic`, an internal vLLM endpoint, anything — without changing the cache helper."},{"id":"pre-seeding-the-cache","title":"Pre-seeding the cache","role":"content","text":"In a real deployment the cache fills up organically: a first-time question is a miss, the LLM answers, and the response is written back. For the demo, `seed_cache.rb` pre-loads a small set of canonical FAQ prompts so the very first query lands on a hit\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/semantic-cache/ruby/lib/seed_cache.rb)):\n\n[code example]\n\nThe seed list stores the canonical phrasing of each question (\"What is your return policy?\"). Paraphrases of any of these prompts (\"How do I return an item?\", \"Can I get a refund?\") embed close to the canonical entry, so the cache lookup serves the stored response without ever calling the model. The seed phase batches the encoder calls into a single `encode_many` so the model dispatch is paid once across the whole seed list."},{"id":"the-interactive-demo","title":"The interactive demo","role":"content","text":"`demo_server.rb` runs a [`WEBrick`](https://github.com/ruby/webrick) HTTP server. The HTML page lets you:\n\n* Type a prompt and toggle metadata: tenant, locale, model version. Each combination is a separate cache namespace inside the same index.\n* Slide the cosine-distance threshold and see hits flip to misses (and back) on the same prompt, with the actual distance reported on each query.\n* Submit with **Ask** to run the full hit-or-miss path (calls the LLM on a miss, writes the answer back). Submit with **Lookup only (no LLM)** to sweep the threshold against a fixed prompt without polluting the cache.\n* Watch the cumulative panel build up: total queries, cache hits, cache misses, hit ratio, tokens not spent, LLM milliseconds not waited.\n* Inspect every cached entry, including remaining TTL and total hit count, and drop individual entries to simulate eviction.\n\nThe server holds one `LocalEmbedder`, one `RedisSemanticCache`, and one `MockLLM` for the lifetime of the process. The HTML page is shared with the Python, Node.js, Go, and Jedis demos and is loaded from `index.html` next to `demo_server.rb`. Endpoints:\n\n| Endpoint        | What it does                                                                  |\n|-----------------|-------------------------------------------------------------------------------|\n| `GET  /state`   | Index info and the full list of cached entries.                               |\n| `POST /query`   | Embed the prompt, run `FT.SEARCH`, on miss call the LLM and write back.       |\n| `POST /reset`   | Drop every cached entry and re-seed from the FAQ list.                        |\n| `POST /drop`    | Delete a single cached entry by id.                                           |"},{"id":"run-the-demo-locally","title":"Run the demo locally","role":"content","text":"1.  Clone the [`redis/docs`](https://github.com/redis/docs) repository and change into the example\n    directory:\n\n    [code example]\n\n2.  Make sure you have Ruby 3.2 or newer and [Bundler](https://bundler.io/), then install the gems:\n\n    [code example]\n\n    `informers` pulls in [`onnxruntime`](https://rubygems.org/gems/onnxruntime) (which ships the ONNX Runtime shared library as a native extension) and [`tokenizers`](https://rubygems.org/gems/tokenizers) (a Hugging Face fast-tokenizer Rust binding). Both come as pre-built binary gems for `arm64-darwin`, `x86_64-darwin`, `aarch64-linux`, and `x86_64-linux`, so there is no system ONNX install step on those platforms.\n\n3.  Make sure a Redis instance with the Redis Search module is running locally on\n    port 6379. [Redis Stack](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack) or\n    [Redis 8 with Search](https://redis.io/docs/latest/develop/ai/search-and-query) both work.\n\n4.  Start the demo server. The first run downloads the ONNX-exported\n    `sentence-transformers/all-MiniLM-L6-v2` model into the local Hugging Face\n    cache (~22 MB):\n\n    [code example]\n\n5.  Open <http://localhost:8094> and try some queries:\n\n    * **\"What is your return policy?\"** — exact match against the seed, distance ≈ 0,\n      hit at any threshold.\n    * **\"How fast is delivery?\"** — paraphrase of the shipping seed; distance\n      around 0.30, hit at the default threshold of 0.5.\n    * **\"How do I return an item?\"** — slightly looser paraphrase of the returns\n      seed; distance around 0.49, still a hit at the default threshold. Slide\n      the threshold down to 0.4 to see this one flip to a miss.\n    * **\"What payment methods do you accept?\"** — unrelated to anything in the\n      seed; distance > 0.6, so at the default threshold you'll see a miss, the\n      mock LLM kicks in for ~1.5 s, the new answer is cached, and a follow-up\n      of the same question is now an immediate hit.\n    * Switch the **Tenant** dropdown to `globex` or `initech` and re-ask any\n      seeded question — the result flips to a miss because the cache entries\n      live under `acme`. That's the metadata pre-filter at work inside `FT.SEARCH`.\n\nThe server is read/write against your local Redis. The default index name is `semcache:idx` and entry keys live under `cache:`. Flags mirror the Python, Node.js, Go, and Jedis demos: `--no-reset` to keep an existing cache across restarts, `--threshold` to change the default cosine-distance cutoff, `--llm-latency-ms` to make the mock LLM faster or slower for the demo, or `--port` to listen on a different port."}],"examples":[{"id":"the-cache-helper-ex0","language":"ruby","code":"require 'redis'\nrequire_relative 'lib/cache'\nrequire_relative 'lib/embeddings'\n\nclient = Redis.new(host: 'localhost', port: 6379)\n\ncache = SemCache::RedisSemanticCache.new(\n  redis_client: client,\n  index_name: 'semcache:idx',\n  distance_threshold: 0.5,    # cosine distance, lower = stricter\n  default_ttl_seconds: 3600   # one hour\n)\nembedder = SemCache::LocalEmbedder.new   # sentence-transformers/all-MiniLM-L6-v2\n\n# One-time index setup (idempotent).\ncache.create_index\n\n# 1) Embed the prompt.\nprompt = 'How do I return an item?'\nquery_vec = embedder.encode_one(prompt)\n\n# 2) Look up under a metadata scope. The TAG filter and the KNN\n#    travel together in one FT.SEARCH.\nresult = cache.lookup(\n  query_vec,\n  tenant: 'acme',\n  locale: 'en',\n  model_version: 'gpt-4.5-2026'\n)\n\nif result.is_a?(SemCache::CacheHit)\n  puts \"hit (#{format('%.3f', result.distance)}): #{result.response}\"\nelse\n  # 3a) Miss — call the LLM. (Use your real client here.)\n  response = call_llm(prompt)\n\n  # 3b) Cache the new entry. Reuses the same embedding bytes the\n  #     lookup used, so we don't pay the encoder twice.\n  cache.put(\n    prompt: prompt,\n    response: response,\n    embedding: query_vec,\n    tenant: 'acme',\n    locale: 'en',\n    model_version: 'gpt-4.5-2026'\n  )\nend","section_id":"the-cache-helper"},{"id":"data-model-ex0","language":"text","code":"cache:7c3f8a1b9e02\n  prompt=How do I return an item?\n  response=You can return any unworn item within 30 days...\n  tenant=acme\n  locale=en\n  model_version=gpt-4.5-2026\n  safety=ok\n  created_ts=1715990400.123\n  hit_count=4\n  embedding=<384 × float32 little-endian bytes>","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"FT.CREATE semcache:idx\n  ON HASH PREFIX 1 cache:\n  SCHEMA\n    prompt         TEXT\n    response       TEXT\n    tenant         TAG\n    locale         TAG\n    model_version  TAG\n    safety         TAG\n    created_ts     NUMERIC SORTABLE\n    hit_count      NUMERIC SORTABLE\n    embedding      VECTOR HNSW 6 TYPE FLOAT32 DIM 384 DISTANCE_METRIC COSINE","section_id":"data-model"},{"id":"the-query-ex0","language":"ruby","code":"args = [\n  'FT.SEARCH', 'semcache:idx',\n  '(@tenant:{acme} @locale:{en} @model_version:{gpt\\-4\\.5\\-2026} @safety:{ok})' \\\n    '=>[KNN 1 @embedding $vec AS distance]',\n  'PARAMS', '2', 'vec', query_vec.pack('e*'),\n  'SORTBY', 'distance', 'ASC',\n  'RETURN', '7',\n  'prompt', 'response', 'tenant', 'locale',\n  'model_version', 'hit_count', 'distance',\n  'LIMIT', '0', '1',\n  'DIALECT', '2'\n]\nreply = client.call(*args)","section_id":"the-query"},{"id":"the-mock-llm-ex0","language":"ruby","code":"require_relative 'lib/mock_llm'\n\nllm = SemCache::MockLLM.new(latency_ms: 1500.0)\nresponse = llm.complete('What is your return policy?')\n# response.response       — the templated answer text\n# response.latency_ms     — wall-clock time the call took\n# response.total_tokens   — estimated prompt + completion tokens","section_id":"the-mock-llm"},{"id":"pre-seeding-the-cache-ex0","language":"ruby","code":"cache.create_index\nSemCache::SeedCache.seed(cache, embedder, tenant: 'acme', locale: 'en')","section_id":"pre-seeding-the-cache"},{"id":"run-the-demo-locally-ex0","language":"bash","code":"git clone https://github.com/redis/docs.git\n    cd docs/content/develop/use-cases/semantic-cache/ruby","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex1","language":"bash","code":"bundle install","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex2","language":"bash","code":"bundle exec ruby demo_server.rb","section_id":"run-the-demo-locally"}]}
{"schema_version":2,"id":"develop/use-cases/semantic-cache/rust","title":"Redis semantic cache with redis-rs","url":"https://redis.io/docs/latest/develop/use-cases/semantic-cache/rust/","summary":"Build a Redis-backed semantic cache for LLM responses in Rust with redis-rs and Candle","aliases":["/develop/use-cases/semantic-cache/redis-rs"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T17:06:11+01:00","children":[],"page_type":"content","content_hash":"fe16f2d0dd00aef207b4723bd1b7bc929f88761031910c5e641e5018e0f6c78d","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a small Redis-backed semantic cache for LLM responses in Rust with [`redis-rs`](https://redis.io/docs/latest/develop/clients) and the [Candle](https://github.com/huggingface/candle) machine-learning framework. It includes a local web server built with the [`tiny_http`](https://crates.io/crates/tiny_http) crate so you can send paraphrased prompts at a mock LLM, watch the cache decide hit or miss, sweep the cosine-distance threshold, and see the cumulative latency and token savings build up."},{"id":"overview","title":"Overview","role":"overview","text":"Each cache entry is stored as a single Redis [Hash](https://redis.io/docs/latest/develop/data-types/hashes) at `cache:<id>`. The hash holds the original prompt, the LLM's response, the raw `float32` bytes of a 384-dimensional embedding of the prompt, and metadata fields — tenant, locale, model version, safety flag — plus a `created_ts` and a `hit_count`. A single [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) index covers the embedding field and every metadata field, so one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) call with a `KNN` clause does the vector lookup *and* the TAG pre-filter in the same round trip — no cross-store joins.\n\nThe lookup is thresholded: [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) always returns the nearest entry that satisfies the filters, but the application only serves it as a hit when the reported cosine distance is at or below `distance_threshold`. Anything further away is treated as a miss; the caller runs the LLM and writes the new prompt, response, and embedding back to the same key pattern with a TTL.\n\nThe embedder is [Candle](https://github.com/huggingface/candle) running the [`sentence-transformers/all-MiniLM-L6-v2`](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2) model, which is the same encoder the [Python example](https://redis.io/docs/latest/develop/use-cases/semantic-cache/redis-py), the [Node.js example](https://redis.io/docs/latest/develop/use-cases/semantic-cache/nodejs), the [Go example](https://redis.io/docs/latest/develop/use-cases/semantic-cache/go), and the [Java example](https://redis.io/docs/latest/develop/use-cases/semantic-cache/java-jedis) use. Embeddings produced by the five implementations are semantically equivalent — paraphrase distances differ only at the fourth decimal place — so a cache populated by one demo can be queried by another against the same Redis instance.\n\nThat gives you:\n\n* A single round trip for lookup — vector KNN + metadata pre-filter in one [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search).\n* Tens of milliseconds on a hit vs. a multi-second LLM call on a miss; the embedding step is the bottleneck either way, and that's a model-side cost, not a Redis one.\n* Tenant, locale, and model-version isolation enforced inside the query, not in application code — a write under one tenant cannot be served to another.\n* Bounded memory: every entry has an [`EXPIRE`](https://redis.io/docs/latest/commands/expire) TTL, and a database-level [eviction policy](https://redis.io/docs/latest/develop/reference/eviction) (LRU / LFU) caps the cache size under pressure."},{"id":"how-it-works","title":"How it works","role":"content","text":"A query goes through three stages: **embed**, **lookup**, and (on a miss) **call the LLM and write back**."},{"id":"hit-path-the-goal","title":"Hit path (the goal)","role":"content","text":"1. The application calls `embedder.encode_one(prompt)` to turn the incoming text into a 384-element `Vec<f32>`.\n2. `cache.lookup(&query_vec, LookupParams { tenant, locale, model_version, .. })` runs [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search) with a TAG pre-filter and a `KNN 1` clause. Redis returns the closest cached prompt that satisfies the filters along with its cosine distance.\n3. If the distance is at or below the threshold, the cache returns a `LookupResult::Hit` carrying the cached response. The helper also runs an [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby) on `hit_count` and an [`EXPIRE`](https://redis.io/docs/latest/commands/expire) refresh inside a [`MULTI/EXEC`](https://redis.io/docs/latest/commands/multi), so a frequently used answer keeps its TTL and the demo UI can see which entries are load-bearing.\n4. The LLM is not called at all. The application returns the cached response to the user."},{"id":"miss-path","title":"Miss path","role":"content","text":"When the distance is above the threshold — or there is no candidate in scope at all — the helper returns a `LookupResult::Miss` instead, carrying the distance of the nearest candidate (if any) for logging. The application then:\n\n1. Calls the LLM with the prompt.\n2. Calls `cache.put(PutParams { prompt, response, embedding: &query_vec, .. })`. The same embedding the lookup used is reused — no re-encode. The helper writes the Hash with [`HSET`](https://redis.io/docs/latest/commands/hset) and an [`EXPIRE`](https://redis.io/docs/latest/commands/expire) TTL inside a single [`MULTI/EXEC`](https://redis.io/docs/latest/commands/multi) so the entry never lands without a TTL on a partial failure.\n3. Returns the LLM's response to the user. The next semantically similar prompt under the same metadata scope will be a hit."},{"id":"the-cache-helper","title":"The cache helper","role":"content","text":"The `RedisSemanticCache` struct wraps the Redis Search index and the lookup / write flow\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/semantic-cache/rust/src/cache.rs)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each cache entry is one Redis Hash. The vector field is raw little-endian `float32` bytes — no JSON wrapping — because the Redis Search vector encoding expects exactly that. The helper uses `byteorder::LittleEndian::write_f32` rather than the host-default `f32::to_le_bytes` casts so the encoding contract is visible in the source and consistent with the [Go example](https://redis.io/docs/latest/develop/use-cases/semantic-cache/go)'s `binary.LittleEndian.PutUint32`.\n\n[code example]\n\nThe Redis Search index schema treats every field as queryable in its natural type:\n\n[code example]"},{"id":"the-query","title":"The query","role":"content","text":"The lookup is a hybrid query: a TAG pre-filter expression in parentheses, then `=>[KNN 1 @embedding $vec]`. With `DIALECT 2`, Redis applies the filter first and KNN-ranks only the matching documents. `redis-rs` doesn't ship a typed `FT.SEARCH` builder, so the helper composes the command directly:\n\n[code example]\n\n`distance` is the cosine *distance* (0 means identical, 2 means opposite). The result is sorted ascending, so the top row is the closest candidate. The application inspects `distance` against the threshold and decides hit or miss in user code — Redis returns the row either way, and treating it as a hit or a miss is a policy decision the cache helper owns, not a server-side filter."},{"id":"the-mock-llm","title":"The mock LLM","role":"content","text":"To make the latency and token savings visible without requiring an API key, `mock_llm.rs` provides a deterministic stand-in\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/semantic-cache/rust/src/mock_llm.rs)):\n\n[code example]\n\nThe mock sleeps for the configured latency, then keyword-matches against a small FAQ table to produce an answer. The deliberate slowness is what makes a hit visibly cheaper than a miss in the demo. In production code, you would replace `MockLlm` with your real client of choice — an OpenAI Rust SDK, an Anthropic SDK, an internal vLLM endpoint, anything — without changing the cache helper."},{"id":"pre-seeding-the-cache","title":"Pre-seeding the cache","role":"content","text":"In a real deployment the cache fills up organically: a first-time question is a miss, the LLM answers, and the response is written back. For the demo, `seed_cache.rs` pre-loads a small set of canonical FAQ prompts so the very first query lands on a hit\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/semantic-cache/rust/src/seed_cache.rs)):\n\n[code example]\n\nThe seed list stores the canonical phrasing of each question (\"What is your return policy?\"). Paraphrases of any of these prompts (\"How do I return an item?\", \"Can I get a refund?\") embed close to the canonical entry, so the cache lookup serves the stored response without ever calling the model."},{"id":"the-interactive-demo","title":"The interactive demo","role":"content","text":"`main.rs` runs a [`tiny_http`](https://crates.io/crates/tiny_http) server. The HTML page lets you:\n\n* Type a prompt and toggle metadata: tenant, locale, model version. Each combination is a separate cache namespace inside the same index.\n* Slide the cosine-distance threshold and see hits flip to misses (and back) on the same prompt, with the actual distance reported on each query.\n* Submit with **Ask** to run the full hit-or-miss path (calls the LLM on a miss, writes the answer back). Submit with **Lookup only (no LLM)** to sweep the threshold against a fixed prompt without polluting the cache.\n* Watch the cumulative panel build up: total queries, cache hits, cache misses, hit ratio, tokens not spent, LLM milliseconds not waited.\n* Inspect every cached entry, including remaining TTL and total hit count, and drop individual entries to simulate eviction.\n\nThe server holds one `LocalEmbedder`, one `RedisSemanticCache`, and one `MockLlm` for the lifetime of the process. The HTML page is shared with the Python, Node.js, Go, and Java demos and is loaded from `index.html` next to `main.rs`. Endpoints:\n\n| Endpoint        | What it does                                                                  |\n|-----------------|-------------------------------------------------------------------------------|\n| `GET  /state`   | Index info and the full list of cached entries.                               |\n| `POST /query`   | Embed the prompt, run `FT.SEARCH`, on miss call the LLM and write back.       |\n| `POST /reset`   | Drop every cached entry and re-seed from the FAQ list.                        |\n| `POST /drop`    | Delete a single cached entry by id.                                           |"},{"id":"run-the-demo-locally","title":"Run the demo locally","role":"content","text":"1.  Clone the [`redis/docs`](https://github.com/redis/docs) repository and change into the example\n    directory:\n\n    [code example]\n\n2.  Make sure a recent Rust toolchain is installed (Rust 1.75 or newer).\n    [`rustup`](https://rustup.rs/) is the usual route. The build pulls in\n    Candle and the Hugging Face tokenizer crates, so the first `cargo\n    build` takes a few minutes.\n\n3.  Make sure a Redis instance with the Redis Search module is running locally on\n    port 6379. [Redis Stack](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack) or\n    [Redis 8 with Search](https://redis.io/docs/latest/develop/ai/search-and-query) both work.\n\n4.  Build and run the demo server. The first run downloads the\n    `sentence-transformers/all-MiniLM-L6-v2` weights (~87 MB) into the local\n    Hugging Face cache:\n\n    [code example]\n\n    Candle uses a pure-Rust tensor backend, so there's no native ONNX\n    Runtime library to install. If you'd prefer the higher-throughput ONNX\n    Runtime backend you can swap the `BertModel` forward call for an `ort`\n    `Session::run` — the cache helper, mock LLM, and HTTP server are\n    unchanged.\n\n5.  Open <http://localhost:8091> and try some queries:\n\n    * **\"What is your return policy?\"** — exact match against the seed, distance ≈ 0,\n      hit at any threshold.\n    * **\"How fast is delivery?\"** — paraphrase of the shipping seed; distance\n      around 0.30, hit at the default threshold of 0.5.\n    * **\"How do I return an item?\"** — slightly looser paraphrase of the returns\n      seed; distance around 0.49, still a hit at the default threshold. Slide\n      the threshold down to 0.4 to see this one flip to a miss.\n    * **\"What payment methods do you accept?\"** — unrelated to anything in the\n      seed; distance > 0.6, so you'll see a miss, the mock LLM kicks in for\n      ~1.5 s, the new answer is cached, and a follow-up of the same question\n      is now an immediate hit.\n    * Switch the **Tenant** dropdown to `globex` or `initech` and re-ask any\n      seeded question — the result flips to a miss because the cache entries\n      live under `acme`. That's the metadata pre-filter at work inside `FT.SEARCH`.\n\nThe server is read/write against your local Redis. The default index name is `semcache:idx` and entry keys live under `cache:`. Flags mirror the Python, Node.js, Go, and Java demos: `--no-reset` to keep an existing cache across restarts, `--threshold` to change the default cosine-distance cutoff, or `--llm-latency-ms` to make the mock LLM faster or slower for the demo."}],"examples":[{"id":"the-cache-helper-ex0","language":"rust","code":"use redis::Client;\n// `cache`, `embeddings`, and `seed_cache` are the demo's local modules.\nuse crate::cache::{LookupParams, LookupResult, PutParams, RedisSemanticCache};\nuse crate::embeddings::LocalEmbedder;\n\nlet client = Client::open(\"redis://localhost:6379/\")?;\n\nlet cache = RedisSemanticCache::new(\n    client,\n    \"semcache:idx\",\n    \"cache:\",\n    384,    // vector dimension\n    0.5,    // cosine distance threshold; lower = stricter\n    3600,   // default TTL in seconds (one hour)\n)?;\nlet embedder = LocalEmbedder::new(None)?;  // sentence-transformers/all-MiniLM-L6-v2\n\n// One-time index setup (idempotent).\ncache.create_index()?;\n\n// 1) Embed the prompt.\nlet prompt = \"How do I return an item?\";\nlet query_vec = embedder.encode_one(prompt)?;\n\n// 2) Look up under a metadata scope. The TAG filter and the KNN\n//    travel together in one FT.SEARCH.\nlet result = cache.lookup(\n    &query_vec,\n    LookupParams {\n        tenant: Some(\"acme\"),\n        locale: Some(\"en\"),\n        model_version: Some(\"gpt-4.5-2026\"),\n        safety: None,\n        distance_threshold: None,\n    },\n)?;\n\nlet response = match result {\n    LookupResult::Hit(hit) => {\n        println!(\"hit ({:.3}): {}\", hit.distance, hit.response);\n        hit.response\n    }\n    LookupResult::Miss(_) => {\n        // 3a) Miss — call the LLM. (Use your real client here.)\n        let llm_response = call_llm(prompt);\n\n        // 3b) Cache the new entry. Reuses the same embedding bytes\n        //     the lookup used, so we don't pay the encoder twice.\n        cache.put(PutParams {\n            prompt,\n            response: &llm_response,\n            embedding: &query_vec,\n            tenant: \"acme\",\n            locale: \"en\",\n            model_version: \"gpt-4.5-2026\",\n            safety: \"ok\",\n            ttl_seconds: None,\n            entry_id: None,\n        })?;\n        llm_response\n    }\n};","section_id":"the-cache-helper"},{"id":"data-model-ex0","language":"text","code":"cache:7c3f8a1b9e02\n  prompt=How do I return an item?\n  response=You can return any unworn item within 30 days...\n  tenant=acme\n  locale=en\n  model_version=gpt-4.5-2026\n  safety=ok\n  created_ts=1715990400.123\n  hit_count=4\n  embedding=<384 × float32 little-endian bytes>","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"FT.CREATE semcache:idx\n  ON HASH PREFIX 1 cache:\n  SCHEMA\n    prompt         TEXT\n    response       TEXT\n    tenant         TAG\n    locale         TAG\n    model_version  TAG\n    safety         TAG\n    created_ts     NUMERIC SORTABLE\n    hit_count      NUMERIC SORTABLE\n    embedding      VECTOR HNSW 6 TYPE FLOAT32 DIM 384 DISTANCE_METRIC COSINE","section_id":"data-model"},{"id":"the-query-ex0","language":"rust","code":"let value: redis::Value = redis::cmd(\"FT.SEARCH\")\n    .arg(\"semcache:idx\")\n    .arg(\"(@tenant:{acme} @locale:{en} @model_version:{gpt\\\\-4\\\\.5\\\\-2026} @safety:{ok})\\\n          =>[KNN 1 @embedding $vec AS distance]\")\n    .arg(\"PARAMS\").arg(2).arg(\"vec\").arg(&vec_bytes[..])\n    .arg(\"SORTBY\").arg(\"distance\").arg(\"ASC\")\n    .arg(\"RETURN\").arg(7)\n    .arg(\"prompt\").arg(\"response\").arg(\"tenant\").arg(\"locale\")\n    .arg(\"model_version\").arg(\"hit_count\").arg(\"distance\")\n    .arg(\"LIMIT\").arg(0).arg(1)\n    .arg(\"DIALECT\").arg(2)\n    .query(&mut con)?;","section_id":"the-query"},{"id":"the-mock-llm-ex0","language":"rust","code":"use crate::mock_llm::MockLlm;\n\nlet llm = MockLlm::new(None, 1500.0);  // 1.5 seconds per call\nlet resp = llm.complete(\"What is your return policy?\");\n// resp.response          — the templated answer text\n// resp.latency_ms        — wall-clock time the call took\n// resp.total_tokens()    — estimated prompt + completion tokens","section_id":"the-mock-llm"},{"id":"pre-seeding-the-cache-ex0","language":"rust","code":"cache.create_index()?;\nseed(&cache, &embedder, SeedOptions {\n    tenant: \"acme\",\n    locale: \"en\",\n    model_version: \"gpt-4.5-2026\",\n})?;","section_id":"pre-seeding-the-cache"},{"id":"run-the-demo-locally-ex0","language":"bash","code":"git clone https://github.com/redis/docs.git\n    cd docs/content/develop/use-cases/semantic-cache/rust","section_id":"run-the-demo-locally"},{"id":"run-the-demo-locally-ex1","language":"bash","code":"cargo run --release","section_id":"run-the-demo-locally"}]}
{"schema_version":2,"id":"develop/use-cases/session-store/dotnet","title":"Redis session store with C#/.NET","url":"https://redis.io/docs/latest/develop/use-cases/session-store/dotnet/","summary":"Implement a Redis-backed session store in C#/.NET with StackExchange.Redis","aliases":["/develop/use-cases/session-store/stackexchange.redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T17:06:11+01:00","children":[],"page_type":"content","content_hash":"7117656aade3a63cb3b71e85e85ac9d3a974673446053522d8db19c15a9b60ba","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis-backed session store in .NET with [`StackExchange.Redis`](https://redis.io/docs/latest/develop/clients/dotnet). It includes a small local web server built with ASP.NET Core minimal APIs so you can see the session lifecycle end to end."},{"id":"overview","title":"Overview","role":"overview","text":"Session storage is a common Redis use case for web applications. Instead of keeping session state in local process memory, you store it in Redis and send the browser only an opaque session ID in a cookie.\n\nThat gives you:\n\n* Shared sessions across multiple app servers\n* Automatic expiration using Redis TTLs\n* Fast reads and updates for small pieces of per-user state\n* A clean separation between browser cookies and server-side session data\n\nIn this example, each session is stored as a Redis hash with a key like `session:{session_id}`. The hash holds lightweight fields such as the username, page view count, timestamps, and the configured session TTL. The key also has an expiration so inactive sessions are removed automatically."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. A user submits a login form\n2. The server generates a random session ID with `RandomNumberGenerator`\n3. The server stores session data in Redis under `session:{id}`\n4. The server sends a `sid` cookie containing only the session ID\n5. Later requests read the cookie, load the hash from Redis, and refresh the TTL\n6. Logging out deletes the Redis key and clears the cookie\n\nBecause the cookie only contains an opaque identifier, the browser never receives the actual session data. That stays in Redis."},{"id":"the-net-session-store","title":"The .NET session store","role":"content","text":"The `RedisSessionStore` class wraps the basic session operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/session-store/dotnet/RedisSessionStore.cs)):\n\n[code example]\n\nThe class uses `IDatabase` from StackExchange.Redis, so you can share a single connection multiplexer across your application and inject the database where you need it."},{"id":"data-model","title":"Data model","role":"content","text":"Each session is stored in a Redis hash:\n\n[code example]\n\nThe implementation uses:\n\n* [`HSET`](https://redis.io/docs/latest/commands/hset) to create and update session fields\n* [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) to load the session\n* [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby) to update counters\n* [`EXPIRE`](https://redis.io/docs/latest/commands/expire) to implement sliding expiration\n* [`DEL`](https://redis.io/docs/latest/commands/del) to remove a session on logout\n\nThe store treats `created_at`, `last_accessed_at`, and `session_ttl` as reserved internal fields, so caller-provided session data cannot overwrite them."},{"id":"session-store-implementation","title":"Session store implementation","role":"content","text":"The `CreateSession()` method generates a random session ID, writes the initial hash fields, and sets the TTL:\n\n[code example]\n\nWhen the application reads a session, it refreshes the configured TTL so active users stay logged in:\n\n[code example]\n\nThis is a simple and effective pattern for many apps. For more complex requirements, you might add separate metadata keys, rotate session IDs after login, or store less frequently accessed data elsewhere."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before running the demo, make sure that:\n\n* Redis is running and accessible. By default, the demo connects to `localhost:6379`.\n* The StackExchange.Redis package is installed:\n\n[code example]\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of three files. Download them from the [`dotnet` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/session-store/dotnet) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From that directory:\n\n[code example]\n\nThe demo server uses ASP.NET Core minimal APIs and exposes a small interactive page where you can:\n\n* Start a session with a username\n* Choose a short TTL and watch the session expire\n* See the Redis-backed session data rendered in the browser\n* Increment a page-view counter stored in Redis\n* Change the active session TTL from the page\n* Log out and delete the session\n\nAfter starting the server, visit `http://localhost:8080`."},{"id":"cookie-handling","title":"Cookie handling","role":"content","text":"The browser cookie should contain only the session ID:\n\n[code example]\n\nAvoid storing user profiles, roles, or other sensitive session data directly in cookies. Keep that information in Redis and let the cookie act only as a lookup token."},{"id":"production-usage","title":"Production usage","role":"content","text":"This guide uses a deliberately small local demo so you can focus on the Redis session pattern. In production, you will usually want to harden the cookie, session lifecycle, and deployment details around it."},{"id":"secure-the-session-cookie","title":"Secure the session cookie","role":"content","text":"Set cookie attributes that match your deployment and threat model:\n\n* Keep `HttpOnly` enabled so JavaScript cannot read the session cookie\n* Use the `Secure` attribute when serving your app over HTTPS\n* Choose an appropriate `SameSite` policy for your login flow and cross-site behavior"},{"id":"keep-session-data-lightweight","title":"Keep session data lightweight","role":"content","text":"Redis-backed sessions work best when each session stores small, frequently accessed values:\n\n* Usernames, IDs, and feature flags are a good fit\n* Large profiles, document blobs, or activity feeds should usually live elsewhere\n* Consider storing only references if the session needs to point to larger data"},{"id":"handle-expiration-deliberately","title":"Handle expiration deliberately","role":"content","text":"Sliding expiration is convenient, but it also defines how long a hijacked cookie remains useful. For production apps, consider:\n\n* Shorter inactivity TTLs for sensitive applications\n* Separate absolute expiration limits for long-lived sessions\n* Session ID rotation after login or privilege changes"},{"id":"use-a-framework-integration-where-appropriate","title":"Use a framework integration where appropriate","role":"content","text":"This example keeps everything explicit so you can see the Redis session pattern clearly. In a real app, you will often wrap the same Redis operations behind middleware for ASP.NET Core, MVC, or another .NET web framework."},{"id":"next-steps","title":"Next steps","role":"content","text":"You now have a complete Redis-backed session example in .NET using StackExchange.Redis. From here you can:\n\n* Adapt the store to your web framework\n* Add session ID rotation or absolute expiration\n* Store additional lightweight session metadata in the same Redis hash\n* Reuse the same Redis deployment across multiple application instances\n\nFor more Redis data modeling patterns, see:\n\n* [Session store overview](https://redis.io/docs/latest/develop/use-cases/session-store)\n* [.NET client guide](https://redis.io/docs/latest/develop/clients/dotnet)\n* [Redis data types](https://redis.io/docs/latest/develop/data-types)"}],"examples":[{"id":"the-net-session-store-ex0","language":"csharp","code":"using StackExchange.Redis;\n\nvar redis = ConnectionMultiplexer.Connect(\"localhost:6379\");\nvar db = redis.GetDatabase();\n\nvar store = new RedisSessionStore(db: db, ttl: 1800);\n\nvar sessionId = store.CreateSession(new Dictionary<string, string>\n{\n    [\"username\"] = \"andrew\",\n    [\"page_views\"] = \"0\"\n});\n\nvar session = store.GetSession(sessionId);\nConsole.WriteLine(session?[\"username\"]);\n\nstore.IncrementField(sessionId, \"page_views\");\nstore.DeleteSession(sessionId);","section_id":"the-net-session-store"},{"id":"data-model-ex0","language":"text","code":"session:abc123...\n  username = andrew\n  page_views = 3\n  session_ttl = 15\n  created_at = 2026-04-02T12:34:56+00:00\n  last_accessed_at = 2026-04-02T12:40:10+00:00","section_id":"data-model"},{"id":"session-store-implementation-ex0","language":"csharp","code":"public string CreateSession(\n    IDictionary<string, string>? data = null,\n    int? ttl = null)\n{\n    var sessionId = CreateSessionId();\n    var key = SessionKey(sessionId);\n    var now = Timestamp();\n    var sessionTtl = NormalizeTtl(ttl);\n\n    var payload = new Dictionary<string, string>();\n    if (data is not null)\n    {\n        foreach (var (field, value) in data)\n        {\n            if (!ReservedSessionFields.Contains(field))\n            {\n                payload[field] = value;\n            }\n        }\n    }\n\n    payload[\"created_at\"] = now;\n    payload[\"last_accessed_at\"] = now;\n    payload[\"session_ttl\"] = sessionTtl.ToString();\n\n    var batch = _db.CreateBatch();\n    _ = batch.HashSetAsync(key, payload.Select(entry => new HashEntry(entry.Key, entry.Value)).ToArray());\n    _ = batch.KeyExpireAsync(key, TimeSpan.FromSeconds(sessionTtl));\n    batch.Execute();\n\n    return sessionId;\n}","section_id":"session-store-implementation"},{"id":"session-store-implementation-ex1","language":"csharp","code":"public Dictionary<string, string>? GetSession(string sessionId, bool refreshTtl = true)\n{\n    var key = SessionKey(sessionId);\n    var session = ReadSession(key);\n    if (!IsValidSession(session))\n    {\n        return null;\n    }\n\n    if (!refreshTtl)\n    {\n        return session;\n    }\n\n    var sessionTtl = NormalizeTtl(int.Parse(session[\"session_ttl\"]));\n    var now = Timestamp();\n\n    var batch = _db.CreateBatch();\n    _ = batch.HashSetAsync(key, new[] { new HashEntry(\"last_accessed_at\", now) });\n    _ = batch.KeyExpireAsync(key, TimeSpan.FromSeconds(sessionTtl));\n    var refreshedTask = batch.HashGetAllAsync(key);\n    batch.Execute();\n\n    var refreshed = ToDictionary(refreshedTask.GetAwaiter().GetResult());\n    return IsValidSession(refreshed) ? refreshed : null;\n}","section_id":"session-store-implementation"},{"id":"prerequisites-ex0","language":"bash","code":"dotnet add package StackExchange.Redis","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir session-store-demo && cd session-store-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/session-store/dotnet\ncurl -O $BASE/RedisSessionStore.cs\ncurl -O $BASE/Program.cs\ncurl -O $BASE/SessionStoreDemo.csproj","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"dotnet run","section_id":"start-the-demo-server"},{"id":"cookie-handling-ex0","language":"csharp","code":"context.Response.Cookies.Append(\"sid\", sessionId, new CookieOptions\n{\n    HttpOnly = true,\n    Path = \"/\",\n    SameSite = SameSiteMode.Lax\n});","section_id":"cookie-handling"}]}
{"schema_version":2,"id":"develop/use-cases/session-store/go","title":"Redis session store with Go","url":"https://redis.io/docs/latest/develop/use-cases/session-store/go/","summary":"Implement a Redis-backed session store in Go with go-redis","aliases":["/develop/use-cases/session-store/go-redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T17:06:11+01:00","children":[],"page_type":"content","content_hash":"ef7fb17f01454af417ef33b5f0851de092baad766e1ca5d374a2ff46498dda46","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis-backed session store in Go with the [`go-redis`](https://redis.io/docs/latest/develop/clients/go) client library. It includes a small local web server built with Go's standard `net/http` package so you can see the session lifecycle end to end."},{"id":"overview","title":"Overview","role":"overview","text":"Session storage is a common Redis use case for web applications. Instead of keeping session state in local process memory, you store it in Redis and send the browser only an opaque session ID in a cookie.\n\nThat gives you:\n\n* Shared sessions across multiple app servers\n* Automatic expiration using Redis TTLs\n* Fast reads and updates for small pieces of per-user state\n* A clean separation between browser cookies and server-side session data\n\nIn this example, each session is stored as a Redis hash with a key like `session:{session_id}`. The hash holds lightweight fields such as the username, page view count, timestamps, and the configured session TTL. The key also has an expiration so inactive sessions are removed automatically."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. A user submits a login form\n2. The server generates a random session ID with Go's `crypto/rand` package\n3. The server stores session data in Redis under `session:{id}`\n4. The server sends a `sid` cookie containing only the session ID\n5. Later requests read the cookie, load the hash from Redis, and refresh the TTL\n6. Logging out deletes the Redis key and clears the cookie\n\nBecause the cookie only contains an opaque identifier, the browser never receives the actual session data. That stays in Redis."},{"id":"the-go-session-store","title":"The Go session store","role":"content","text":"The `RedisSessionStore` type wraps the basic session operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/session-store/go/session_store.go)):\n\n[code example]\n\nGo's `context.Context` is passed to every store call, making it easy to set deadlines and cancellations in a real application."},{"id":"data-model","title":"Data model","role":"content","text":"Each session is stored in a Redis hash:\n\n[code example]\n\nThe implementation uses:\n\n* [`HSET`](https://redis.io/docs/latest/commands/hset) to create and update session fields\n* [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) to load the session\n* [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby) to update counters\n* [`EXPIRE`](https://redis.io/docs/latest/commands/expire) to implement sliding expiration\n* [`DEL`](https://redis.io/docs/latest/commands/del) to remove a session on logout\n\nThe store treats `created_at`, `last_accessed_at`, and `session_ttl` as reserved internal fields, so caller-provided session data cannot overwrite them."},{"id":"session-store-implementation","title":"Session store implementation","role":"content","text":"The `CreateSession()` method generates a random session ID, writes the initial hash fields, and sets the TTL:\n\n[code example]\n\nWhen the application reads a session, it refreshes the configured TTL so active users stay logged in:\n\n[code example]\n\nThis is a simple and effective pattern for many apps. For more complex requirements, you might add separate metadata keys, rotate session IDs after login, or store less frequently accessed data elsewhere."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before running the demo, make sure that:\n\n* Redis is running and accessible. By default, the demo connects to `localhost:6379`.\n* The `go-redis` package is available:\n\n[code example]\n\nIf your Redis server is running elsewhere, start the demo with `-redis-host` and `-redis-port`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of four files. Download them from the [`go` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/session-store/go) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"Create a `main.go` file in the same directory:\n\n[code example]\n\nThen build and run:\n\n[code example]\n\nThe demo server uses Go's standard library for HTTP handling:\n\n* [`net/http`](https://pkg.go.dev/net/http) for the web server and cookies\n* [`net/url`](https://pkg.go.dev/net/url) for form parsing\n* [`html`](https://pkg.go.dev/html) for rendering session values safely\n\nIt exposes a small interactive page where you can:\n\n* Start a session with a username\n* Choose a short TTL and watch the session expire\n* See the Redis-backed session data rendered in the browser\n* Increment a page-view counter stored in Redis\n* Change the active session TTL from the page\n* Log out and delete the session\n\nAfter starting the server, visit `http://localhost:8080`."},{"id":"cookie-handling","title":"Cookie handling","role":"content","text":"The browser cookie should contain only the session ID:\n\n[code example]\n\nAvoid storing user profiles, roles, or other sensitive session data directly in cookies. Keep that information in Redis and let the cookie act only as a lookup token."},{"id":"production-usage","title":"Production usage","role":"content","text":"This guide uses a deliberately small local demo so you can focus on the Redis session pattern. In production, you will usually want to harden the cookie, session lifecycle, and deployment details around it."},{"id":"secure-the-session-cookie","title":"Secure the session cookie","role":"content","text":"Set cookie attributes that match your deployment and threat model:\n\n* Keep `HttpOnly` enabled so JavaScript cannot read the session cookie\n* Use the `Secure` attribute when serving your app over HTTPS\n* Choose an appropriate `SameSite` policy for your login flow and cross-site behavior"},{"id":"keep-session-data-lightweight","title":"Keep session data lightweight","role":"content","text":"Redis-backed sessions work best when each session stores small, frequently accessed values:\n\n* Usernames, IDs, and feature flags are a good fit\n* Large profiles, document blobs, or activity feeds should usually live elsewhere\n* Consider storing only references if the session needs to point to larger data"},{"id":"handle-expiration-deliberately","title":"Handle expiration deliberately","role":"content","text":"Sliding expiration is convenient, but it also defines how long a hijacked cookie remains useful. For production apps, consider:\n\n* Shorter inactivity TTLs for sensitive applications\n* Separate absolute expiration limits for long-lived sessions\n* Session ID rotation after login or privilege changes"},{"id":"use-a-framework-integration-where-appropriate","title":"Use a framework integration where appropriate","role":"content","text":"This example keeps everything explicit so you can see the Redis session pattern clearly. In a real app, you will often wrap the same Redis operations behind middleware for Gin, Echo, Fiber, or another Go web framework."},{"id":"next-steps","title":"Next steps","role":"content","text":"You now have a complete Redis-backed session example in Go using `go-redis`. From here you can:\n\n* Adapt the store to your web framework\n* Add session ID rotation or absolute expiration\n* Store additional lightweight session metadata in the same Redis hash\n* Reuse the same Redis deployment across multiple application instances\n\nFor more Redis data modeling patterns, see:\n\n* [Session store overview](https://redis.io/docs/latest/develop/use-cases/session-store)\n* [Go client guide](https://redis.io/docs/latest/develop/clients/go)\n* [Redis data types](https://redis.io/docs/latest/develop/data-types)"}],"examples":[{"id":"the-go-session-store-ex0","language":"go","code":"package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\n\t\"github.com/redis/go-redis/v9\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\trdb := redis.NewClient(&redis.Options{\n\t\tAddr: \"localhost:6379\",\n\t})\n\n\tstore := NewRedisSessionStore(SessionStoreConfig{\n\t\tClient: rdb,\n\t\tTTL:    1800,\n\t})\n\n\tsessionID, err := store.CreateSession(ctx, map[string]string{\n\t\t\"username\":   \"andrew\",\n\t\t\"page_views\": \"0\",\n\t}, 0)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tsession, ok, err := store.GetSession(ctx, sessionID, true)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tif ok {\n\t\tfmt.Println(session[\"username\"])\n\t}\n\n\t_, _, _ = store.IncrementField(ctx, sessionID, \"page_views\", 1)\n\t_, _ = store.DeleteSession(ctx, sessionID)\n}","section_id":"the-go-session-store"},{"id":"data-model-ex0","language":"text","code":"session:abc123...\n  username = andrew\n  page_views = 3\n  session_ttl = 15\n  created_at = 2026-04-02T12:34:56+00:00\n  last_accessed_at = 2026-04-02T12:40:10+00:00","section_id":"data-model"},{"id":"session-store-implementation-ex0","language":"go","code":"func (s *RedisSessionStore) CreateSession(\n\tctx context.Context,\n\tdata map[string]string,\n\tttl int,\n) (string, error) {\n\tsessionID, err := randomSessionID()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tkey := s.sessionKey(sessionID)\n\tnow := s.timestamp()\n\tsessionTTL, err := s.normalizeTTL(ttl)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tpayload := map[string]string{\n\t\t\"created_at\":       now,\n\t\t\"last_accessed_at\": now,\n\t\t\"session_ttl\":      strconv.Itoa(sessionTTL),\n\t}\n\n\tfor field, value := range data {\n\t\tif !reservedSessionFields[field] {\n\t\t\tpayload[field] = value\n\t\t}\n\t}\n\n\tpipe := s.client.TxPipeline()\n\tpipe.HSet(ctx, key, payload)\n\tpipe.Expire(ctx, key, time.Duration(sessionTTL)*time.Second)\n\t_, err = pipe.Exec(ctx)\n\treturn sessionID, err\n}","section_id":"session-store-implementation"},{"id":"session-store-implementation-ex1","language":"go","code":"func (s *RedisSessionStore) GetSession(\n\tctx context.Context,\n\tsessionID string,\n\trefreshTTL bool,\n) (map[string]string, bool, error) {\n\tkey := s.sessionKey(sessionID)\n\tsession, err := s.client.HGetAll(ctx, key).Result()\n\tif err != nil {\n\t\treturn nil, false, err\n\t}\n\tif !isValidSession(session) {\n\t\treturn nil, false, nil\n\t}\n\n\tif !refreshTTL {\n\t\treturn session, true, nil\n\t}\n\n\tsessionTTL, err := s.normalizeTTLString(session[\"session_ttl\"])\n\tif err != nil {\n\t\treturn nil, false, err\n\t}\n\n\tpipe := s.client.TxPipeline()\n\tpipe.HSet(ctx, key, map[string]string{\"last_accessed_at\": s.timestamp()})\n\tpipe.Expire(ctx, key, time.Duration(sessionTTL)*time.Second)\n\tgetCmd := pipe.HGetAll(ctx, key)\n\tif _, err := pipe.Exec(ctx); err != nil {\n\t\treturn nil, false, err\n\t}\n\n\trefreshed := getCmd.Val()\n\treturn refreshed, isValidSession(refreshed), nil\n}","section_id":"session-store-implementation"},{"id":"prerequisites-ex0","language":"bash","code":"go get github.com/redis/go-redis/v9","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir session-store-demo && cd session-store-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/session-store/go\ncurl -O $BASE/session_store.go\ncurl -O $BASE/demo_server.go\ncurl -O $BASE/go.mod\ncurl -O $BASE/go.sum","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"go","code":"package main\n\nimport \"sessionstore\"\n\nfunc main() { sessionstore.RunDemoServer() }","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"bash","code":"go build -o demo ./...\n./demo","section_id":"start-the-demo-server"},{"id":"cookie-handling-ex0","language":"go","code":"http.SetCookie(w, &http.Cookie{\n\tName:     \"sid\",\n\tValue:    sessionID,\n\tPath:     \"/\",\n\tHttpOnly: true,\n\tSameSite: http.SameSiteLaxMode,\n})","section_id":"cookie-handling"}]}
{"schema_version":2,"id":"develop/use-cases/session-store","title":"Redis session store","url":"https://redis.io/docs/latest/develop/use-cases/session-store/","summary":"Store web sessions in Redis.","tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T17:06:11+01:00","children":[{"id":"develop/use-cases/session-store/redis-py","summary":"Implement a Redis-backed session store in Python with redis-py","title":"Redis session store with redis-py","url":"https://redis.io/docs/latest/develop/use-cases/session-store/redis-py/"},{"id":"develop/use-cases/session-store/nodejs","summary":"Implement a Redis-backed session store in Node.js with node-redis","title":"Redis session store with node-redis","url":"https://redis.io/docs/latest/develop/use-cases/session-store/nodejs/"},{"id":"develop/use-cases/session-store/go","summary":"Implement a Redis-backed session store in Go with go-redis","title":"Redis session store with Go","url":"https://redis.io/docs/latest/develop/use-cases/session-store/go/"},{"id":"develop/use-cases/session-store/java-jedis","summary":"Implement a Redis-backed session store in Java with Jedis","title":"Redis session store with Java and Jedis","url":"https://redis.io/docs/latest/develop/use-cases/session-store/java-jedis/"},{"id":"develop/use-cases/session-store/java-lettuce","summary":"Implement a Redis-backed session store in Java with Lettuce","title":"Redis session store with Java (Lettuce)","url":"https://redis.io/docs/latest/develop/use-cases/session-store/java-lettuce/"},{"id":"develop/use-cases/session-store/dotnet","summary":"Implement a Redis-backed session store in C#/.NET with StackExchange.Redis","title":"Redis session store with C#/.NET","url":"https://redis.io/docs/latest/develop/use-cases/session-store/dotnet/"},{"id":"develop/use-cases/session-store/php","summary":"Implement a Redis-backed session store in PHP with Predis","title":"Redis session store with PHP","url":"https://redis.io/docs/latest/develop/use-cases/session-store/php/"},{"id":"develop/use-cases/session-store/ruby","summary":"Implement a Redis-backed session store in Ruby with redis-rb","title":"Redis session store with Ruby","url":"https://redis.io/docs/latest/develop/use-cases/session-store/ruby/"},{"id":"develop/use-cases/session-store/rust","summary":"Implement a Redis-backed session store in Rust with redis-rs","title":"Redis session store with Rust","url":"https://redis.io/docs/latest/develop/use-cases/session-store/rust/"}],"page_type":"content","content_hash":"38a86244cfd4726d1cb489f0e6f05a3d44c2684f64c260baaa07e2bf0bf69bbd","sections":[{"id":"when-to-use-a-session-store","title":"When to use a session store","role":"overview","text":"Use a Redis session store when you need to share per-user session state (login context, shopping carts, preferences) across stateless application servers without sticky sessions or database round-trips."},{"id":"why-the-problem-is-hard","title":"Why the problem is hard","role":"content","text":"HTTP is stateless, so you must rely on external state to recognize a returning user between requests.\nSome of the obvious approaches have significant drawbacks:\n\n-   **Storing sessions on individual app servers**: this requires\n    [sticky routing](https://en.wikipedia.org/wiki/Load_balancing_(computing)#Persistence), which creates\n    hot spots and precludes [failover](https://en.wikipedia.org/wiki/Failover).\n-   **Moving session reads to a relational database**: this adds 5–20 ms per request. If you have\n    thousands of concurrent users then session reads dominate connection pools. This turns every page load\n    into a contention point that degrades the primary database for all other workloads.\n\nSessions need automatic cleanup without external sweep jobs. You will incur\ndirect business costs (abandoned carts, forced re-authentication, interrupted checkouts) if\nyou lose sessions, so durability matters more than it does with pure caching.\n\nAt operational scale, you also need to run queries against multiple sessions (for example, find all\ncarts containing a recalled product, count sessions per tenant), which is more complicated than just reading individual sessions."},{"id":"what-you-can-expect-from-a-redis-solution","title":"What you can expect from a Redis solution","role":"content","text":"You can:\n\n-   Eliminate sticky sessions so any instance can serve any user behind a load balancer.\n-   Expire inactive sessions automatically without cleanup jobs or database sweeps.\n-   Update individual session fields without re-serializing the full session.\n-   Track sessions across multiple devices per user, including logout-all.\n-   Query across active sessions for operational and security tasks without key scanning.\n-   Retain session data across deployments and node restarts with configurable durability."},{"id":"how-redis-supports-the-solution","title":"How Redis supports the solution","role":"content","text":"In practice, each session is stored as a Redis hash — one hash per session ID — and the key is set to expire automatically so inactive sessions are cleaned up without any external job.\n\nRedis provides the following features that make it a good fit for session storage:\n\n-   [Hashes](https://redis.io/docs/latest/develop/data-types/hashes) for field-level session access without \n    deserializing an entire session blob.\n-   [`EXPIRE`](https://redis.io/docs/latest/commands/expire) with sliding TTL resets on each request so active \n    sessions stay alive while inactive ones are cleaned up automatically.\n-   [Sets](https://redis.io/docs/latest/develop/data-types/sets) ([`SADD`](https://redis.io/docs/latest/commands/sadd), \n    [`SMEMBERS`](https://redis.io/docs/latest/commands/smembers)) to track all sessions per user for multi-device \n    management ([`DEL`](https://redis.io/docs/latest/commands/del) plus [`SREM`](https://redis.io/docs/latest/commands/srem) \n    handle explicit logout or logout-all).\n-   AOF and RDB [persistence](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence) to let \n    sessions survive process and node restarts within their expiration window. \n-   Secondary indexing (querying by field values rather than by key) via\n    [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) to support\n    cross-session queries at runtime (finding affected carts, counting sessions by tenant)\n    without key scanning.\n-   Sub-millisecond latency on the request path, which is already on the same Redis instance\n    in most stacks."},{"id":"ecosystem","title":"Ecosystem","role":"content","text":"The following libraries and frameworks provide Redis session store integrations:\n\n-   **Java**:\n    [Spring Session Data Redis](https://docs.spring.io/spring-session/reference/configuration/redis.html)\n-   **Node.js**: [`connect-redis`](https://www.npmjs.com/package/connect-redis)\n    ([express-session](https://www.npmjs.com/package/express-session) store)\n-   **Python**: [Flask-Session](https://flask-session.readthedocs.io/en/latest/),\n    [django-redis](https://pypi.org/project/django-redis/)\n-   **API gateways**: [Kong](https://docs.konghq.com/hub/kong-inc/session/),\n    [Envoy](https://www.envoyproxy.io/docs/envoy/latest/start/quick-start/configuration-dynamic-control-plane)\n    (Redis-backed session and token storage)"},{"id":"code-examples-to-build-your-own-redis-session-store","title":"Code examples to build your own Redis session store","role":"example","text":"The following guides show how to build a simple Redis-backed session store.\nEach guide includes a runnable example to illustrate using the session store with a basic local\nweb server for each of the following client libraries:\n\n* [redis-py (Python)](https://redis.io/docs/latest/develop/use-cases/session-store/redis-py)\n* [node-redis (Node.js)](https://redis.io/docs/latest/develop/use-cases/session-store/nodejs)\n* [go-redis (Go)](https://redis.io/docs/latest/develop/use-cases/session-store/go)\n* [Jedis (Java)](https://redis.io/docs/latest/develop/use-cases/session-store/java-jedis)\n* [Lettuce (Java)](https://redis.io/docs/latest/develop/use-cases/session-store/java-lettuce)\n* [StackExchange.Redis (C#)](https://redis.io/docs/latest/develop/use-cases/session-store/dotnet)\n* [Predis (PHP)](https://redis.io/docs/latest/develop/use-cases/session-store/php)\n* [redis-rb (Ruby)](https://redis.io/docs/latest/develop/use-cases/session-store/ruby)\n* [redis-rs (Rust)](https://redis.io/docs/latest/develop/use-cases/session-store/rust)"}],"examples":[]}
{"schema_version":2,"id":"develop/use-cases/session-store/java-jedis","title":"Redis session store with Java and Jedis","url":"https://redis.io/docs/latest/develop/use-cases/session-store/java-jedis/","summary":"Implement a Redis-backed session store in Java with Jedis","aliases":["/develop/use-cases/session-store/java","/develop/use-cases/session-store/jedis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T17:06:11+01:00","children":[],"page_type":"content","content_hash":"be61584de8cee97b3676761f99dd680b31d210468ee685b6ba9943b2fffdc0a4","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis-backed session store in Java with [`Jedis`](https://redis.io/docs/latest/develop/clients/jedis). It includes a small local web server built with Java's built-in `HttpServer` so you can see the session lifecycle end to end."},{"id":"overview","title":"Overview","role":"overview","text":"Session storage is a common Redis use case for web applications. Instead of keeping session state in local process memory, you store it in Redis and send the browser only an opaque session ID in a cookie.\n\nThat gives you:\n\n* Shared sessions across multiple app servers\n* Automatic expiration using Redis TTLs\n* Fast reads and updates for small pieces of per-user state\n* A clean separation between browser cookies and server-side session data\n\nIn this example, each session is stored as a Redis hash with a key like `session:{session_id}`. The hash holds lightweight fields such as the username, page view count, timestamps, and the configured session TTL. The key also has an expiration so inactive sessions are removed automatically."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. A user submits a login form\n2. The server generates a random session ID with Java's `SecureRandom`\n3. The server stores session data in Redis under `session:{id}`\n4. The server sends a `sid` cookie containing only the session ID\n5. Later requests read the cookie, load the hash from Redis, and refresh the TTL\n6. Logging out deletes the Redis key and clears the cookie\n\nBecause the cookie only contains an opaque identifier, the browser never receives the actual session data. That stays in Redis."},{"id":"the-java-session-store","title":"The Java session store","role":"content","text":"The `RedisSessionStore` class wraps the basic session operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/session-store/java-jedis/RedisSessionStore.java)):\n\n[code example]\n\nJedis operations are synchronous, and `JedisPool` gives you safe connection reuse across requests."},{"id":"data-model","title":"Data model","role":"content","text":"Each session is stored in a Redis hash:\n\n[code example]\n\nThe implementation uses:\n\n* [`HSET`](https://redis.io/docs/latest/commands/hset) to create and update session fields\n* [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) to load the session\n* [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby) to update counters\n* [`EXPIRE`](https://redis.io/docs/latest/commands/expire) to implement sliding expiration\n* [`DEL`](https://redis.io/docs/latest/commands/del) to remove a session on logout\n\nThe store treats `created_at`, `last_accessed_at`, and `session_ttl` as reserved internal fields, so caller-provided session data cannot overwrite them."},{"id":"session-store-implementation","title":"Session store implementation","role":"content","text":"The `createSession()` method generates a random session ID, writes the initial hash fields, and sets the TTL:\n\n[code example]\n\nWhen the application reads a session, it refreshes the configured TTL so active users stay logged in:\n\n[code example]\n\nThis is a simple and effective pattern for many apps. For more complex requirements, you might add separate metadata keys, rotate session IDs after login, or store less frequently accessed data elsewhere."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before running the demo, make sure that:\n\n* Redis is running and accessible. By default, the demo connects to `localhost:6379`.\n* Jedis is available in your project:\n\n* If you use **Maven**:\n\n  [code example]\n\n* If you use **Gradle**:\n\n  [code example]\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of two Java files. Download them from the [`java-jedis` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/session-store/java-jedis) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From that directory:\n\n[code example]\n\nThe demo server uses standard Java libraries for HTTP handling:\n\n* [`com.sun.net.httpserver.HttpServer`](https://docs.oracle.com/en/java/javase/21/docs/api/jdk.httpserver/com/sun/net/httpserver/HttpServer.html) for the web server\n* [`java.net.HttpCookie`](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/net/HttpCookie.html) style cookie parsing and headers\n* [`java.net.URLDecoder`](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/net/URLDecoder.html) for form decoding\n\nIt exposes a small interactive page where you can:\n\n* Start a session with a username\n* Choose a short TTL and watch the session expire\n* See the Redis-backed session data rendered in the browser\n* Increment a page-view counter stored in Redis\n* Change the active session TTL from the page\n* Log out and delete the session\n\nAfter starting the server, visit `http://localhost:8080`."},{"id":"cookie-handling","title":"Cookie handling","role":"content","text":"The browser cookie should contain only the session ID:\n\n[code example]\n\nAvoid storing user profiles, roles, or other sensitive session data directly in cookies. Keep that information in Redis and let the cookie act only as a lookup token."},{"id":"production-usage","title":"Production usage","role":"content","text":"This guide uses a deliberately small local demo so you can focus on the Redis session pattern. In production, you will usually want to harden the cookie, session lifecycle, and deployment details around it."},{"id":"secure-the-session-cookie","title":"Secure the session cookie","role":"content","text":"Set cookie attributes that match your deployment and threat model:\n\n* Keep `HttpOnly` enabled so JavaScript cannot read the session cookie\n* Use the `Secure` attribute when serving your app over HTTPS\n* Choose an appropriate `SameSite` policy for your login flow and cross-site behavior"},{"id":"keep-session-data-lightweight","title":"Keep session data lightweight","role":"content","text":"Redis-backed sessions work best when each session stores small, frequently accessed values:\n\n* Usernames, IDs, and feature flags are a good fit\n* Large profiles, document blobs, or activity feeds should usually live elsewhere\n* Consider storing only references if the session needs to point to larger data"},{"id":"handle-expiration-deliberately","title":"Handle expiration deliberately","role":"content","text":"Sliding expiration is convenient, but it also defines how long a hijacked cookie remains useful. For production apps, consider:\n\n* Shorter inactivity TTLs for sensitive applications\n* Separate absolute expiration limits for long-lived sessions\n* Session ID rotation after login or privilege changes"},{"id":"use-a-framework-integration-where-appropriate","title":"Use a framework integration where appropriate","role":"content","text":"This example keeps everything explicit so you can see the Redis session pattern clearly. In a real app, you will often wrap the same Redis operations behind middleware for Spring MVC, Spring Boot, Micronaut, or another Java web framework."},{"id":"next-steps","title":"Next steps","role":"content","text":"You now have a complete Redis-backed session example in Java using Jedis. From here you can:\n\n* Adapt the store to your web framework\n* Add session ID rotation or absolute expiration\n* Store additional lightweight session metadata in the same Redis hash\n* Reuse the same Redis deployment across multiple application instances\n\nFor more Redis data modeling patterns, see:\n\n* [Session store overview](https://redis.io/docs/latest/develop/use-cases/session-store)\n* [Jedis guide](https://redis.io/docs/latest/develop/clients/jedis)\n* [Redis data types](https://redis.io/docs/latest/develop/data-types)"}],"examples":[{"id":"the-java-session-store-ex0","language":"java","code":"import java.util.Map;\nimport redis.clients.jedis.JedisPool;\n\npublic class Main {\n    public static void main(String[] args) {\n        JedisPool jedisPool = new JedisPool(\"localhost\", 6379);\n        RedisSessionStore store = new RedisSessionStore(jedisPool, \"session:\", 1800);\n\n        String sessionId = store.createSession(Map.of(\n                \"username\", \"andrew\",\n                \"page_views\", \"0\"\n        ), null);\n\n        Map<String, String> session = store.getSession(sessionId, true);\n        System.out.println(session.get(\"username\"));\n\n        store.incrementField(sessionId, \"page_views\", 1);\n        store.deleteSession(sessionId);\n        jedisPool.close();\n    }\n}","section_id":"the-java-session-store"},{"id":"data-model-ex0","language":"text","code":"session:abc123...\n  username = andrew\n  page_views = 3\n  session_ttl = 15\n  created_at = 2026-04-02T12:34:56+00:00\n  last_accessed_at = 2026-04-02T12:40:10+00:00","section_id":"data-model"},{"id":"session-store-implementation-ex0","language":"java","code":"public String createSession(Map<String, String> data, Integer ttl) {\n    String sessionId = randomSessionId();\n    String key = sessionKey(sessionId);\n    String now = timestamp();\n    int sessionTtl = normalizeTtl(ttl);\n\n    Map<String, String> payload = new HashMap<>();\n    if (data != null) {\n        for (Map.Entry<String, String> entry : data.entrySet()) {\n            if (!RESERVED_SESSION_FIELDS.contains(entry.getKey())) {\n                payload.put(entry.getKey(), String.valueOf(entry.getValue()));\n            }\n        }\n    }\n\n    payload.put(\"created_at\", now);\n    payload.put(\"last_accessed_at\", now);\n    payload.put(\"session_ttl\", String.valueOf(sessionTtl));\n\n    try (Jedis jedis = jedisPool.getResource()) {\n        Pipeline pipeline = jedis.pipelined();\n        pipeline.hset(key, payload);\n        pipeline.expire(key, sessionTtl);\n        pipeline.sync();\n    }\n\n    return sessionId;\n}","section_id":"session-store-implementation"},{"id":"session-store-implementation-ex1","language":"java","code":"public Map<String, String> getSession(String sessionId, boolean refreshTtl) {\n    String key = sessionKey(sessionId);\n\n    try (Jedis jedis = jedisPool.getResource()) {\n        Map<String, String> session = jedis.hgetAll(key);\n        if (!isValidSession(session)) {\n            return null;\n        }\n\n        if (!refreshTtl) {\n            return session;\n        }\n\n        int sessionTtl = normalizeTtl(Integer.valueOf(session.get(\"session_ttl\")));\n        Pipeline pipeline = jedis.pipelined();\n        pipeline.hset(key, \"last_accessed_at\", timestamp());\n        pipeline.expire(key, sessionTtl);\n        Response<Map<String, String>> refreshed = pipeline.hgetAll(key);\n        pipeline.sync();\n\n        return isValidSession(refreshed.get()) ? refreshed.get() : null;\n    }\n}","section_id":"session-store-implementation"},{"id":"prerequisites-ex0","language":"xml","code":"<dependency>\n      <groupId>redis.clients</groupId>\n      <artifactId>jedis</artifactId>\n      <version>5.2.0</version>\n  </dependency>","section_id":"prerequisites"},{"id":"prerequisites-ex1","language":"groovy","code":"implementation 'redis.clients:jedis:5.2.0'","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir session-store-demo && cd session-store-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/session-store/java-jedis\ncurl -O $BASE/RedisSessionStore.java\ncurl -O $BASE/DemoServer.java","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"javac -cp jedis-5.2.0.jar RedisSessionStore.java DemoServer.java\njava -cp .:jedis-5.2.0.jar DemoServer","section_id":"start-the-demo-server"},{"id":"cookie-handling-ex0","language":"java","code":"exchange.getResponseHeaders().add(\n        \"Set-Cookie\",\n        \"sid=\" + sessionId + \"; Path=/; HttpOnly; SameSite=Lax\"\n);","section_id":"cookie-handling"}]}
{"schema_version":2,"id":"develop/use-cases/session-store/java-lettuce","title":"Redis session store with Java (Lettuce)","url":"https://redis.io/docs/latest/develop/use-cases/session-store/java-lettuce/","summary":"Implement a Redis-backed session store in Java with Lettuce","aliases":["/develop/use-cases/session-store/lettuce"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T17:06:11+01:00","children":[],"page_type":"content","content_hash":"6cc523d21bb5af25f196db6cddba1cc545a473e75210e588090dd75acba7019f","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis-backed session store in Java with [`Lettuce`](https://redis.io/docs/latest/develop/clients/lettuce). It includes both asynchronous and reactive store APIs, plus a small local demo server built on Java's built-in `HttpServer`."},{"id":"overview","title":"Overview","role":"overview","text":"Session storage is a common Redis use case for web applications. Instead of keeping session state in local process memory, you store it in Redis and send the browser only an opaque session ID in a cookie.\n\nThat gives you:\n\n* Shared sessions across multiple app servers\n* Automatic expiration using Redis TTLs\n* Fast reads and updates for small pieces of per-user state\n* A clean separation between browser cookies and server-side session data\n\nIn this example, each session is stored as a Redis hash with a key like `session:{session_id}`. The hash holds lightweight fields such as the username, page view count, timestamps, and the configured session TTL. The key also has an expiration so inactive sessions are removed automatically."},{"id":"why-async-and-reactive","title":"Why async and reactive","role":"content","text":"For Lettuce, we generally show asynchronous and reactive APIs rather than a synchronous API:\n\n* Async with `RedisAsyncCommands` works well for standard Java applications using `CompletableFuture`\n* Reactive with `RedisReactiveCommands` is a good fit when you are already using Reactor\n* For synchronous Java session-store examples, we recommend [Jedis](https://redis.io/docs/latest/develop/use-cases/session-store/java-jedis)"},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. A user submits a login form\n2. The server generates a random session ID with `SecureRandom`\n3. The server stores session data in Redis under `session:{id}`\n4. The server sends a `sid` cookie containing only the session ID\n5. Later requests read the cookie, load the hash from Redis, and refresh the TTL\n6. Logging out deletes the Redis key and clears the cookie\n\nBecause the cookie only contains an opaque identifier, the browser never receives the actual session data. That stays in Redis."},{"id":"the-lettuce-session-stores","title":"The Lettuce session stores","role":"content","text":"The async and reactive session store classes wrap the basic session operations:\n\n* Async API: [AsyncSessionStore.java](https://github.com/redis/docs/blob/main/content/develop/use-cases/session-store/java-lettuce/AsyncSessionStore.java)\n* Reactive API: [ReactiveSessionStore.java](https://github.com/redis/docs/blob/main/content/develop/use-cases/session-store/java-lettuce/ReactiveSessionStore.java)"},{"id":"async-usage","title":"Async usage","role":"content","text":"[code example]"},{"id":"reactive-usage","title":"Reactive usage","role":"content","text":"[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each session is stored in a Redis hash:\n\n[code example]\n\nThe implementation uses:\n\n* [`HSET`](https://redis.io/docs/latest/commands/hset) to create and update session fields\n* [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) to load the session\n* [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby) to update counters\n* [`EXPIRE`](https://redis.io/docs/latest/commands/expire) to implement sliding expiration\n* [`DEL`](https://redis.io/docs/latest/commands/del) to remove a session on logout\n\nThe store treats `created_at`, `last_accessed_at`, and `session_ttl` as reserved internal fields, so caller-provided session data cannot overwrite them."},{"id":"session-store-implementation","title":"Session store implementation","role":"content","text":"The `createSession()` method generates a random session ID, writes the initial hash fields, and sets the TTL:\n\n[code example]\n\nWhen the application reads a session, it refreshes the configured TTL so active users stay logged in:\n\n[code example]\n\nThis is a simple and effective pattern for many apps. For more complex requirements, you might add separate metadata keys, rotate session IDs after login, or store less frequently accessed data elsewhere."},{"id":"installation","title":"Installation","role":"setup","text":"Add Lettuce to your project:\n\n* If you use **Maven**:\n\n  [code example]\n\n* If you use **Gradle**:\n\n  [code example]"},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of three Java files. Download them from the [`java-lettuce` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/session-store/java-lettuce) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"A local demo server is included to show the session store in action\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/session-store/java-lettuce/DemoServer.java)):\n\n[code example]\n\nThe demo uses the async Lettuce API and exposes a small interactive page where you can:\n\n* Start a session with a username\n* Choose a short TTL and watch the session expire\n* See the Redis-backed session data rendered in the browser\n* Increment a page-view counter stored in Redis\n* Change the active session TTL from the page\n* Log out and delete the session\n\nThe demo assumes Redis is running on `localhost:6379`, but you can override that with `--redis-host` and `--redis-port`. After starting the server, visit `http://localhost:8080`."},{"id":"cookie-handling","title":"Cookie handling","role":"content","text":"The browser cookie should contain only the session ID:\n\n[code example]\n\nAvoid storing user profiles, roles, or other sensitive session data directly in cookies. Keep that information in Redis and let the cookie act only as a lookup token."},{"id":"production-usage","title":"Production usage","role":"content","text":"This guide uses a deliberately small local demo so you can focus on the Redis session pattern. In production, you will usually want to harden the cookie, session lifecycle, and deployment details around it."},{"id":"secure-the-session-cookie","title":"Secure the session cookie","role":"content","text":"Set cookie attributes that match your deployment and threat model:\n\n* Keep `HttpOnly` enabled so JavaScript cannot read the session cookie\n* Use the `Secure` attribute when serving your app over HTTPS\n* Choose an appropriate `SameSite` policy for your login flow and cross-site behavior"},{"id":"keep-session-data-lightweight","title":"Keep session data lightweight","role":"content","text":"Redis-backed sessions work best when each session stores small, frequently accessed values:\n\n* Usernames, IDs, and feature flags are a good fit\n* Large profiles, document blobs, or activity feeds should usually live elsewhere\n* Consider storing only references if the session needs to point to larger data"},{"id":"handle-expiration-deliberately","title":"Handle expiration deliberately","role":"content","text":"Sliding expiration is convenient, but it also defines how long a hijacked cookie remains useful. For production apps, consider:\n\n* Shorter inactivity TTLs for sensitive applications\n* Separate absolute expiration limits for long-lived sessions\n* Session ID rotation after login or privilege changes"},{"id":"use-a-framework-integration-where-appropriate","title":"Use a framework integration where appropriate","role":"content","text":"This example keeps everything explicit so you can see the Redis session pattern clearly. In a real app, you will often wrap the same Redis operations behind middleware for Spring WebFlux, Vert.x, Micronaut, or another Java framework using non-blocking I/O."},{"id":"next-steps","title":"Next steps","role":"content","text":"You now have Redis-backed session examples in Java using both Jedis and Lettuce. From here you can:\n\n* Choose Jedis for synchronous apps or Lettuce for async/reactive apps\n* Add session ID rotation or absolute expiration\n* Store additional lightweight session metadata in the same Redis hash\n* Reuse the same Redis deployment across multiple application instances\n\nFor more Redis data modeling patterns, see:\n\n* [Session store overview](https://redis.io/docs/latest/develop/use-cases/session-store)\n* [Lettuce guide](https://redis.io/docs/latest/develop/clients/lettuce)\n* [Jedis session store](https://redis.io/docs/latest/develop/use-cases/session-store/java-jedis)"}],"examples":[{"id":"async-usage-ex0","language":"java","code":"import io.lettuce.core.RedisClient;\nimport io.lettuce.core.api.StatefulRedisConnection;\nimport java.util.Map;\n\nRedisClient redisClient = RedisClient.create(\"redis://localhost:6379\");\nStatefulRedisConnection<String, String> connection = redisClient.connect();\n\nAsyncSessionStore store = new AsyncSessionStore(connection.async(), \"session:\", 1800);\n\nstore.createSession(Map.of(\n        \"username\", \"andrew\",\n        \"page_views\", \"0\"\n), null)\n    .thenCompose(sessionId -> store.getSession(sessionId, true))\n    .thenAccept(session -> {\n        if (session != null) {\n            System.out.println(session.get(\"username\"));\n        }\n    })\n    .join();\n\nconnection.close();\nredisClient.shutdown();","section_id":"async-usage"},{"id":"reactive-usage-ex0","language":"java","code":"import io.lettuce.core.RedisClient;\nimport io.lettuce.core.api.StatefulRedisConnection;\nimport java.util.Map;\n\nRedisClient redisClient = RedisClient.create(\"redis://localhost:6379\");\nStatefulRedisConnection<String, String> connection = redisClient.connect();\n\nReactiveSessionStore store = new ReactiveSessionStore(connection.reactive(), \"session:\", 1800);\n\nstore.createSession(Map.of(\n        \"username\", \"andrew\",\n        \"page_views\", \"0\"\n), null)\n    .flatMap(sessionId -> store.getSession(sessionId, true))\n    .doOnNext(session -> System.out.println(session.get(\"username\")))\n    .block();\n\nconnection.close();\nredisClient.shutdown();","section_id":"reactive-usage"},{"id":"data-model-ex0","language":"text","code":"session:abc123...\n  username = andrew\n  page_views = 3\n  session_ttl = 15\n  created_at = 2026-04-08T12:34:56Z\n  last_accessed_at = 2026-04-08T12:40:10Z","section_id":"data-model"},{"id":"session-store-implementation-ex0","language":"java","code":"public CompletableFuture<String> createSession(Map<String, String> data, Integer ttl) {\n    String sessionId = createSessionId();\n    String key = sessionKey(sessionId);\n    String now = timestamp();\n    int sessionTtl = normalizeTtl(ttl);\n\n    Map<String, String> payload = sessionPayload(data, now, sessionTtl);\n\n    return commands.hset(key, payload)\n            .toCompletableFuture()\n            .thenCompose(ignore -> commands.expire(key, sessionTtl).toCompletableFuture())\n            .thenApply(ignore -> sessionId);\n}","section_id":"session-store-implementation"},{"id":"session-store-implementation-ex1","language":"java","code":"public CompletableFuture<Map<String, String>> getSession(String sessionId, boolean refreshTtl) {\n    String key = sessionKey(sessionId);\n\n    return commands.hgetall(key).toCompletableFuture().thenCompose(session -> {\n        if (!isValidSession(session)) {\n            return CompletableFuture.completedFuture(null);\n        }\n        if (!refreshTtl) {\n            return CompletableFuture.completedFuture(session);\n        }\n\n        int sessionTtl = normalizeTtl(Integer.parseInt(session.get(\"session_ttl\")));\n        return commands.hset(key, \"last_accessed_at\", timestamp()).toCompletableFuture()\n                .thenCompose(ignore -> commands.expire(key, sessionTtl).toCompletableFuture())\n                .thenCompose(ignore -> commands.hgetall(key).toCompletableFuture())\n                .thenApply(refreshed -> isValidSession(refreshed) ? refreshed : null);\n    });\n}","section_id":"session-store-implementation"},{"id":"installation-ex0","language":"xml","code":"<dependency>\n      <groupId>io.lettuce</groupId>\n      <artifactId>lettuce-core</artifactId>\n      <version>6.7.1.RELEASE</version>\n  </dependency>","section_id":"installation"},{"id":"installation-ex1","language":"groovy","code":"implementation 'io.lettuce:lettuce-core:6.7.1.RELEASE'","section_id":"installation"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir session-store-demo && cd session-store-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/session-store/java-lettuce\ncurl -O $BASE/AsyncSessionStore.java\ncurl -O $BASE/ReactiveSessionStore.java\ncurl -O $BASE/DemoServer.java","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"javac -cp lettuce-core-6.7.1.RELEASE.jar AsyncSessionStore.java DemoServer.java\njava -cp .:lettuce-core-6.7.1.RELEASE.jar DemoServer","section_id":"start-the-demo-server"},{"id":"cookie-handling-ex0","language":"java","code":"exchange.getResponseHeaders().add(\n        \"Set-Cookie\",\n        \"sid=\" + sessionId + \"; Path=/; HttpOnly; SameSite=Lax\"\n);","section_id":"cookie-handling"}]}
{"schema_version":2,"id":"develop/use-cases/session-store/nodejs","title":"Redis session store with node-redis","url":"https://redis.io/docs/latest/develop/use-cases/session-store/nodejs/","summary":"Implement a Redis-backed session store in Node.js with node-redis","aliases":["/develop/use-cases/session-store/node-redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T17:06:11+01:00","children":[],"page_type":"content","content_hash":"65bdf3a08d3be3e17db67843a8857f83ed8af9f274a89c428d98f251074d8bea","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis-backed session store in Node.js with [`node-redis`](https://redis.io/docs/latest/develop/clients/nodejs). It includes a small local web server built with Node's standard `http` module so you can see the session lifecycle end to end."},{"id":"overview","title":"Overview","role":"overview","text":"Session storage is a common Redis use case for web applications. Instead of keeping session state in local process memory, you store it in Redis and send the browser only an opaque session ID in a cookie.\n\nThat gives you:\n\n* Shared sessions across multiple app servers\n* Automatic expiration using Redis TTLs\n* Fast reads and updates for small pieces of per-user state\n* A clean separation between browser cookies and server-side session data\n\nIn this example, each session is stored as a Redis hash with a key like `session:{session_id}`. The hash holds lightweight fields such as the username, page view count, timestamps, and the configured session TTL. The key also has an expiration so inactive sessions are removed automatically."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. A user submits a login form\n2. The server generates a random session ID with Node's `crypto` module\n3. The server stores session data in Redis under `session:{id}`\n4. The server sends a `sid` cookie containing only the session ID\n5. Later requests read the cookie, load the hash from Redis, and refresh the TTL\n6. Logging out deletes the Redis key and clears the cookie\n\nBecause the cookie only contains an opaque identifier, the browser never receives the actual session data. That stays in Redis."},{"id":"the-nodejs-session-store","title":"The Node.js session store","role":"content","text":"The `RedisSessionStore` class wraps the basic session operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/session-store/nodejs/sessionStore.js)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each session is stored in a Redis hash:\n\n[code example]\n\nThe implementation uses:\n\n* [`HSET`](https://redis.io/docs/latest/commands/hset) to create and update session fields\n* [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) to load the session\n* [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby) to update counters\n* [`EXPIRE`](https://redis.io/docs/latest/commands/expire) to implement sliding expiration\n* [`DEL`](https://redis.io/docs/latest/commands/del) to remove a session on logout\n\nThe store treats `created_at`, `last_accessed_at`, and `session_ttl` as reserved internal fields, so caller-provided session data cannot overwrite them."},{"id":"session-store-implementation","title":"Session store implementation","role":"content","text":"The `createSession()` method generates a random session ID, writes the initial hash fields, and sets the TTL:\n\n[code example]\n\nWhen the application reads a session, it refreshes the configured TTL so active users stay logged in:\n\n[code example]\n\nThis is a simple and effective pattern for many apps. For more complex requirements, you might add separate metadata keys, rotate session IDs after login, or store less frequently accessed data elsewhere."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before running the demo, make sure that:\n\n* Redis is running and accessible. By default, the demo connects to `localhost:6379`.\n* The `redis` package is installed:\n\n[code example]\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of two JavaScript files. Download them from the [`nodejs` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/session-store/nodejs) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From that directory:\n\n[code example]\n\nThe demo server uses Node's standard library for HTTP handling:\n\n* [`http`](https://nodejs.org/api/http.html) for the web server\n* [`url`](https://nodejs.org/api/url.html) for request parsing\n* [`crypto`](https://nodejs.org/api/crypto.html) for generating opaque session IDs\n\nIt exposes a small interactive page where you can:\n\n* Start a session with a username\n* Choose a short TTL and watch the session expire\n* See the Redis-backed session data rendered in the browser\n* Increment a page-view counter stored in Redis\n* Change the active session TTL from the page\n* Log out and delete the session\n\nAfter starting the server, visit `http://localhost:8080`."},{"id":"cookie-handling","title":"Cookie handling","role":"content","text":"The browser cookie should contain only the session ID:\n\n[code example]\n\nAvoid storing user profiles, roles, or other sensitive session data directly in cookies. Keep that information in Redis and let the cookie act only as a lookup token."},{"id":"production-usage","title":"Production usage","role":"content","text":"This guide uses a deliberately small local demo so you can focus on the Redis session pattern. In production, you will usually want to harden the cookie, session lifecycle, and deployment details around it."},{"id":"secure-the-session-cookie","title":"Secure the session cookie","role":"content","text":"Set cookie attributes that match your deployment and threat model:\n\n* Keep `HttpOnly` enabled so JavaScript cannot read the session cookie\n* Use the `Secure` attribute when serving your app over HTTPS\n* Choose an appropriate `SameSite` policy for your login flow and cross-site behavior"},{"id":"keep-session-data-lightweight","title":"Keep session data lightweight","role":"content","text":"Redis-backed sessions work best when each session stores small, frequently accessed values:\n\n* Usernames, IDs, and feature flags are a good fit\n* Large profiles, document blobs, or activity feeds should usually live elsewhere\n* Consider storing only references if the session needs to point to larger data"},{"id":"handle-expiration-deliberately","title":"Handle expiration deliberately","role":"content","text":"Sliding expiration is convenient, but it also defines how long a hijacked cookie remains useful. For production apps, consider:\n\n* Shorter inactivity TTLs for sensitive applications\n* Separate absolute expiration limits for long-lived sessions\n* Session ID rotation after login or privilege changes"},{"id":"use-a-framework-integration-where-appropriate","title":"Use a framework integration where appropriate","role":"content","text":"This example keeps everything explicit so you can see the Redis session pattern clearly. In a real app, you will often wrap the same Redis operations behind middleware for Express, Fastify, Next.js, or another framework."},{"id":"next-steps","title":"Next steps","role":"content","text":"You now have a complete Redis-backed session example in Node.js using `node-redis`. From here you can:\n\n* Adapt the store to your web framework\n* Add session ID rotation or absolute expiration\n* Store additional lightweight session metadata in the same Redis hash\n* Reuse the same Redis deployment across multiple application instances\n\nFor more Redis data modeling patterns, see:\n\n* [Session store overview](https://redis.io/docs/latest/develop/use-cases/session-store)\n* [node-redis guide](https://redis.io/docs/latest/develop/clients/nodejs)\n* [Redis data types](https://redis.io/docs/latest/develop/data-types)"}],"examples":[{"id":"the-nodejs-session-store-ex0","language":"javascript","code":"const { createClient } = require(\"redis\");\nconst { RedisSessionStore } = require(\"./sessionStore\");\n\nasync function main() {\n  const client = createClient({ url: \"redis://localhost:6379\" });\n  await client.connect();\n\n  const store = new RedisSessionStore({ redisClient: client, ttl: 1800 });\n\n  const sessionId = await store.createSession({\n    username: \"andrew\",\n    page_views: \"0\",\n  });\n\n  const session = await store.getSession(sessionId);\n  console.log(session.username);\n\n  await store.incrementField(sessionId, \"page_views\");\n  await store.deleteSession(sessionId);\n  await client.disconnect();\n}\n\nmain().catch(console.error);","section_id":"the-nodejs-session-store"},{"id":"data-model-ex0","language":"text","code":"session:abc123...\n  username = andrew\n  page_views = 3\n  session_ttl = 15\n  created_at = 2026-04-02T12:34:56+00:00\n  last_accessed_at = 2026-04-02T12:40:10+00:00","section_id":"data-model"},{"id":"session-store-implementation-ex0","language":"javascript","code":"async createSession(data = {}, ttl = undefined) {\n  const sessionId = randomBytes(32).toString(\"base64url\");\n  const key = this._sessionKey(sessionId);\n  const now = this._timestamp();\n  const sessionTtl = this._normalizeTtl(ttl);\n\n  const payload = {};\n  for (const [field, value] of Object.entries(data)) {\n    if (!RESERVED_SESSION_FIELDS.has(field)) {\n      payload[field] = String(value);\n    }\n  }\n\n  payload.created_at = now;\n  payload.last_accessed_at = now;\n  payload.session_ttl = String(sessionTtl);\n\n  await this.redis.multi().hSet(key, payload).expire(key, sessionTtl).exec();\n  return sessionId;\n}","section_id":"session-store-implementation"},{"id":"session-store-implementation-ex1","language":"javascript","code":"async getSession(sessionId, refreshTtl = true) {\n  const key = this._sessionKey(sessionId);\n  const session = await this.redis.hGetAll(key);\n  if (!this._isValidSession(session)) {\n    return null;\n  }\n\n  if (!refreshTtl) {\n    return session;\n  }\n\n  const sessionTtl = this._normalizeTtl(session.session_ttl);\n  const now = this._timestamp();\n  const [, , refreshedSession] = await this.redis\n    .multi()\n    .hSet(key, { last_accessed_at: now })\n    .expire(key, sessionTtl)\n    .hGetAll(key)\n    .exec();\n\n  return this._isValidSession(refreshedSession) ? refreshedSession : null;\n}","section_id":"session-store-implementation"},{"id":"prerequisites-ex0","language":"bash","code":"npm install redis","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir session-store-demo && cd session-store-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/session-store/nodejs\ncurl -O $BASE/sessionStore.js\ncurl -O $BASE/demoServer.js","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"node demoServer.js","section_id":"start-the-demo-server"},{"id":"cookie-handling-ex0","language":"javascript","code":"res.setHeader(\n  \"Set-Cookie\",\n  \"sid=\" + encodeURIComponent(sessionId) + \"; Path=/; HttpOnly; SameSite=Lax\"\n);","section_id":"cookie-handling"}]}
{"schema_version":2,"id":"develop/use-cases/session-store/php","title":"Redis session store with PHP","url":"https://redis.io/docs/latest/develop/use-cases/session-store/php/","summary":"Implement a Redis-backed session store in PHP with Predis","aliases":["/develop/use-cases/session-store/predis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T17:06:11+01:00","children":[],"page_type":"content","content_hash":"17abf334df2b4e672441752d55c90b88c5a12c0f2fea8f6dfdde9df343253ec3","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis-backed session store in PHP with [Predis](https://redis.io/docs/latest/develop/clients/php). It includes a small local web server using PHP's built-in development server so you can see the session lifecycle end to end."},{"id":"overview","title":"Overview","role":"overview","text":"Session storage is a common Redis use case for web applications. Instead of keeping session state in local process memory, you store it in Redis and send the browser only an opaque session ID in a cookie.\n\nThat gives you:\n\n* Shared sessions across multiple app servers\n* Automatic expiration using Redis TTLs\n* Fast reads and updates for small pieces of per-user state\n* A clean separation between browser cookies and server-side session data\n\nIn this example, each session is stored as a Redis hash with a key like `session:{session_id}`. The hash holds lightweight fields such as the username, page view count, timestamps, and the configured session TTL. The key also has an expiration so inactive sessions are removed automatically."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. A user submits a login form\n2. The server generates a random session ID with PHP's `random_bytes()`\n3. The server stores session data in Redis under `session:{id}`\n4. The server sends a `sid` cookie containing only the session ID\n5. Later requests read the cookie, load the hash from Redis, and refresh the TTL\n6. Logging out deletes the Redis key and clears the cookie\n\nBecause the cookie only contains an opaque identifier, the browser never receives the actual session data. That stays in Redis."},{"id":"the-php-session-store","title":"The PHP session store","role":"content","text":"The `RedisSessionStore` class wraps the basic session operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/session-store/php/SessionStore.php)):\n\n[code example]\n\nThe methods return associative arrays and scalar values, which fits naturally with idiomatic PHP application code."},{"id":"data-model","title":"Data model","role":"content","text":"Each session is stored in a Redis hash:\n\n[code example]\n\nThe implementation uses:\n\n* [`HSET`](https://redis.io/docs/latest/commands/hset) to create and update session fields\n* [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) to load the session\n* [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby) to update counters\n* [`EXPIRE`](https://redis.io/docs/latest/commands/expire) to implement sliding expiration\n* [`DEL`](https://redis.io/docs/latest/commands/del) to remove a session on logout\n\nThe store treats `created_at`, `last_accessed_at`, and `session_ttl` as reserved internal fields, so caller-provided session data cannot overwrite them."},{"id":"session-store-implementation","title":"Session store implementation","role":"content","text":"The `createSession()` method generates a random session ID, writes the initial hash fields, and sets the TTL:\n\n[code example]\n\nWhen the application reads a session, it refreshes the configured TTL so active users stay logged in:\n\n[code example]\n\nThis is a simple and effective pattern for many apps. For more complex requirements, you might add separate metadata keys, rotate session IDs after login, or store less frequently accessed data elsewhere."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before running the demo, make sure that:\n\n* Redis is running and accessible. By default, the demo connects to `localhost:6379`.\n* Predis is installed:\n\n[code example]\n\nIf your Redis server is running elsewhere, start the demo with the `REDIS_HOST` and `REDIS_PORT` environment variables."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of two PHP files. Download them from the [`php` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/session-store/php) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From that directory:\n\n[code example]\n\nThe demo exposes a small interactive page where you can:\n\n* Start a session with a username\n* Choose a short TTL and watch the session expire\n* See the Redis-backed session data rendered in the browser\n* Increment a page-view counter stored in Redis\n* Change the active session TTL from the page\n* Log out and delete the session\n\nIf Redis is running somewhere else, pass the connection settings as environment variables:\n\n[code example]\n\nAfter starting the server, visit `http://localhost:8080`."},{"id":"cookie-handling","title":"Cookie handling","role":"content","text":"The browser cookie should contain only the session ID:\n\n[code example]\n\nAvoid storing user profiles, roles, or other sensitive session data directly in cookies. Keep that information in Redis and let the cookie act only as a lookup token."},{"id":"production-usage","title":"Production usage","role":"content","text":"This guide uses a deliberately small local demo so you can focus on the Redis session pattern. In production, you will usually want to harden the cookie, session lifecycle, and deployment details around it."},{"id":"secure-the-session-cookie","title":"Secure the session cookie","role":"content","text":"Set cookie attributes that match your deployment and threat model:\n\n* Keep `HttpOnly` enabled so JavaScript cannot read the session cookie\n* Use the `Secure` attribute when serving your app over HTTPS\n* Choose an appropriate `SameSite` policy for your login flow and cross-site behavior"},{"id":"keep-session-data-lightweight","title":"Keep session data lightweight","role":"content","text":"Redis-backed sessions work best when each session stores small, frequently accessed values:\n\n* Usernames, IDs, and feature flags are a good fit\n* Large profiles, document blobs, or activity feeds should usually live elsewhere\n* Consider storing only references if the session needs to point to larger data"},{"id":"handle-expiration-deliberately","title":"Handle expiration deliberately","role":"content","text":"Sliding expiration is convenient, but it also defines how long a hijacked cookie remains useful. For production apps, consider:\n\n* Shorter inactivity TTLs for sensitive applications\n* Separate absolute expiration limits for long-lived sessions\n* Session ID rotation after login or privilege changes"},{"id":"use-a-framework-integration-where-appropriate","title":"Use a framework integration where appropriate","role":"content","text":"This example keeps everything explicit so you can see the Redis session pattern clearly. In a real app, you will often wrap the same Redis operations behind middleware for Laravel, Symfony, Slim, or another PHP framework."},{"id":"next-steps","title":"Next steps","role":"content","text":"You now have a complete Redis-backed session example in PHP using Predis. From here you can:\n\n* Adapt the store to your web framework\n* Add session ID rotation or absolute expiration\n* Store additional lightweight session metadata in the same Redis hash\n* Reuse the same Redis deployment across multiple application instances\n\nFor more Redis data modeling patterns, see:\n\n* [Session store overview](https://redis.io/docs/latest/develop/use-cases/session-store)\n* [PHP client guide](https://redis.io/docs/latest/develop/clients/php)\n* [Redis data types](https://redis.io/docs/latest/develop/data-types)"}],"examples":[{"id":"the-php-session-store-ex0","language":"php","code":"<?php\n\nrequire __DIR__ . '/vendor/autoload.php';\nrequire __DIR__ . '/SessionStore.php';\n\nuse Predis\\Client;\n\n$redis = new Client([\n    'scheme' => 'tcp',\n    'host' => '127.0.0.1',\n    'port' => 6379,\n]);\n\n$store = new RedisSessionStore(redis: $redis, ttl: 1800);\n\n$sessionId = $store->createSession([\n    'username' => 'andrew',\n    'page_views' => '0',\n]);\n\n$session = $store->getSession($sessionId);\necho $session['username'] . PHP_EOL;\n\n$store->incrementField($sessionId, 'page_views');\n$store->deleteSession($sessionId);","section_id":"the-php-session-store"},{"id":"data-model-ex0","language":"text","code":"session:abc123...\n  username = andrew\n  page_views = 3\n  session_ttl = 15\n  created_at = 2026-04-02T12:34:56+00:00\n  last_accessed_at = 2026-04-02T12:40:10+00:00","section_id":"data-model"},{"id":"session-store-implementation-ex0","language":"php","code":"public function createSession(array $data = [], ?int $ttl = null): string\n{\n    $sessionId = $this->createSessionId();\n    $key = $this->sessionKey($sessionId);\n    $now = $this->timestamp();\n    $sessionTtl = $this->normalizeTtl($ttl);\n\n    $payload = [];\n    foreach ($data as $field => $value) {\n        if (!in_array($field, self::RESERVED_SESSION_FIELDS, true)) {\n            $payload[$field] = (string) $value;\n        }\n    }\n\n    $payload['created_at'] = $now;\n    $payload['last_accessed_at'] = $now;\n    $payload['session_ttl'] = (string) $sessionTtl;\n\n    $this->redis->pipeline(function ($pipe) use ($key, $payload, $sessionTtl): void {\n        $pipe->hset($key, $payload);\n        $pipe->expire($key, $sessionTtl);\n    });\n\n    return $sessionId;\n}","section_id":"session-store-implementation"},{"id":"session-store-implementation-ex1","language":"php","code":"public function getSession(string $sessionId, bool $refreshTtl = true): ?array\n{\n    $key = $this->sessionKey($sessionId);\n    $session = $this->redis->hgetall($key);\n    if (!$this->isValidSession($session)) {\n        return null;\n    }\n\n    if (!$refreshTtl) {\n        return $session;\n    }\n\n    $sessionTtl = $this->normalizeTtl((int) $session['session_ttl']);\n    $result = $this->redis->pipeline(function ($pipe) use ($key, $sessionTtl): void {\n        $pipe->hset($key, 'last_accessed_at', $this->timestamp());\n        $pipe->expire($key, $sessionTtl);\n        $pipe->hgetall($key);\n    });\n\n    $refreshed = $result[2] ?? [];\n    return $this->isValidSession($refreshed) ? $refreshed : null;\n}","section_id":"session-store-implementation"},{"id":"prerequisites-ex0","language":"bash","code":"composer require predis/predis","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir session-store-demo && cd session-store-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/session-store/php\ncurl -O $BASE/SessionStore.php\ncurl -O $BASE/demo_server.php","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"composer require predis/predis\nphp -S localhost:8080 demo_server.php","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"bash","code":"REDIS_HOST=myhost REDIS_PORT=6380 php -S localhost:8080 demo_server.php","section_id":"start-the-demo-server"},{"id":"cookie-handling-ex0","language":"php","code":"setcookie('sid', $sessionId, [\n    'expires' => 0,\n    'path' => '/',\n    'httponly' => true,\n    'samesite' => 'Lax',\n]);","section_id":"cookie-handling"}]}
{"schema_version":2,"id":"develop/use-cases/session-store/redis-py","title":"Redis session store with redis-py","url":"https://redis.io/docs/latest/develop/use-cases/session-store/redis-py/","summary":"Implement a Redis-backed session store in Python with redis-py","aliases":["/develop/use-cases/session-store/python"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T17:06:11+01:00","children":[],"page_type":"content","content_hash":"1d3c74f4542bf23921c75fa02dd36486810551b04eff8b98ccf9b657988705f5","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis-backed session store in Python with [`redis-py`](https://redis.io/docs/latest/develop/clients/redis-py). It includes a small local web server built with the Python standard library so you can see the session lifecycle end to end."},{"id":"overview","title":"Overview","role":"overview","text":"Session storage is a common Redis use case for web applications. Instead of keeping session state in local process memory, you store it in Redis and send the browser only an opaque session ID in a cookie.\n\nThat gives you:\n\n* Shared sessions across multiple app servers\n* Automatic expiration using Redis TTLs\n* Fast reads and updates for small pieces of per-user state\n* A clean separation between browser cookies and server-side session data\n\nIn this example, each session is stored as a Redis hash with a key like `session:{session_id}`. The hash holds lightweight fields such as the username, page view count, timestamps, and the configured session TTL. The key also has an expiration so inactive sessions are removed automatically."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. A user submits a login form\n2. The server generates a random session ID with Python's `secrets` module\n3. The server stores session data in Redis under `session:{id}`\n4. The server sends a `sid` cookie containing only the session ID\n5. Later requests read the cookie, load the hash from Redis, and refresh the TTL\n6. Logging out deletes the Redis key and clears the cookie\n\nBecause the cookie only contains an opaque identifier, the browser never receives the actual session data. That stays in Redis."},{"id":"the-python-session-store","title":"The Python session store","role":"content","text":"The `RedisSessionStore` class wraps the basic session operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/session-store/redis-py/session_store.py)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each session is stored in a Redis hash:\n\n[code example]\n\nThe implementation uses:\n\n* [`HSET`](https://redis.io/docs/latest/commands/hset) to create and update session fields\n* [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) to load the session\n* [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby) to update counters\n* [`EXPIRE`](https://redis.io/docs/latest/commands/expire) to implement sliding expiration\n* [`DEL`](https://redis.io/docs/latest/commands/del) to remove a session on logout\n* [`TTL`](https://redis.io/docs/latest/commands/ttl) to read the remaining session lifetime\n\nThe store defines `created_at`, `last_accessed_at`, and `session_ttl` as `RESERVED_SESSION_FIELDS` so caller-provided session data cannot overwrite them."},{"id":"session-store-implementation","title":"Session store implementation","role":"content","text":"The `create_session()` method generates a random session ID, writes the initial hash fields, and sets the TTL:\n\n[code example]\n\nWhen the application reads a session, it uses a `WATCH`/`MULTI`/`EXEC` block to refresh the TTL atomically. If another client modifies the key between the read and the update, `WatchError` is raised and the operation retries from the start:\n\n[code example]\n\nThe `increment_field()` method uses [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby) to atomically increment a numeric field and also refreshes `last_accessed_at` and the TTL in the same `WATCH`/`MULTI`/`EXEC` block:\n\n[code example]\n\nThe `delete_session()` method removes the session hash from Redis when the user logs out and returns `True` if a key was deleted, `False` if it did not exist:\n\n[code example]\n\nThe `update_session()` method writes new values to arbitrary session fields and refreshes the TTL. Reserved fields are silently excluded. It returns `False` if the session does not exist:\n\n[code example]\n\nThe `set_session_ttl()` method replaces the stored `session_ttl` field and calls `EXPIRE` with the new value immediately, so the change takes effect on the running session without waiting for the next request:\n\n[code example]\n\nThe `get_ttl()` method returns the remaining lifetime of a session in seconds, read directly from Redis using [`TTL`](https://redis.io/docs/latest/commands/ttl):\n\n[code example]\n\nThis is a simple and effective pattern for many apps. For more complex requirements, you might add separate metadata keys, rotate session IDs after login, or store less frequently accessed data elsewhere. Multi-device session tracking — tracking all sessions per user in a Redis Set so you can implement logout-all — is outside the scope of this guide."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before running the demo, make sure that:\n\n* Redis is running and accessible. By default, the demo connects to `localhost:6379`.\n* The `redis` Python package is installed:\n\n[code example]\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of two Python files. Download them from the [`redis-py` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/session-store/redis-py) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From that directory:\n\n[code example]\n\nThe demo server uses only Python standard library features for HTTP handling:\n\n* [`http.server`](https://docs.python.org/3/library/http.server.html) for the web server\n* [`http.cookies`](https://docs.python.org/3/library/http.cookies.html) for cookie parsing and response cookies\n* [`urllib.parse`](https://docs.python.org/3/library/urllib.parse.html) for form decoding\n\nIt exposes a small interactive page where you can:\n\n* Start a session with a username\n* Choose a short TTL and watch the session expire\n* See the Redis-backed session data rendered in the browser\n* Increment a page-view counter stored in Redis\n* Change the active session TTL from the page\n* Log out and delete the session\n\nAfter starting the server, visit `http://localhost:8080`."},{"id":"cookie-handling","title":"Cookie handling","role":"content","text":"The browser cookie should contain only the session ID:\n\n[code example]\n\nAvoid storing user profiles, roles, or other sensitive session data directly in cookies. Keep that information in Redis and let the cookie act only as a lookup token."},{"id":"production-usage","title":"Production usage","role":"content","text":"This guide uses a deliberately small local demo so you can focus on the Redis session pattern. In production, you will usually want to harden the cookie, session lifecycle, and deployment details around it."},{"id":"secure-the-session-cookie","title":"Secure the session cookie","role":"content","text":"Set cookie attributes that match your deployment and threat model:\n\n* Keep `HttpOnly` enabled so JavaScript cannot read the session cookie\n* Use the `Secure` attribute when serving your app over HTTPS\n* Choose an appropriate `SameSite` policy for your login flow and cross-site behavior\n* Consider a browser cookie lifetime that matches how you want the session to behave on the client side"},{"id":"rotate-session-ids-after-authentication-changes","title":"Rotate session IDs after authentication changes","role":"content","text":"When a user logs in, logs out, or their privilege level changes, consider rotating to a new session ID instead of continuing to use the existing one. This reduces the risk of session fixation and gives you a clean point to re-issue the browser cookie."},{"id":"store-only-small-frequently-accessed-session-data","title":"Store only small, frequently accessed session data","role":"content","text":"Redis-backed sessions work well for small, frequently accessed state such as:\n\n* User identifiers\n* Lightweight preferences\n* CSRF-related state\n* Simple counters or timestamps\n\nAvoid treating the session as a general-purpose profile store. Large or rarely used data is often better kept in your main database or another dedicated store."},{"id":"add-csrf-protection-when-needed","title":"Add CSRF protection when needed","role":"content","text":"If your application uses cookie-based authentication, make sure your form and API design includes appropriate CSRF protections where needed. The right approach depends on your framework, request patterns, and whether the application accepts cross-site requests."},{"id":"namespace-session-keys-in-shared-redis-deployments","title":"Namespace session keys in shared Redis deployments","role":"content","text":"If multiple applications or environments share the same Redis deployment, use a clear key prefix strategy such as `session:app-a:` or `session:staging:`. Namespacing helps avoid collisions, simplifies cleanup, and makes it easier to inspect keys during operations or debugging."},{"id":"inspect-sessions-directly-in-redis","title":"Inspect sessions directly in Redis","role":"content","text":"When testing or troubleshooting, inspect the stored session key directly to confirm that the application is writing the fields and TTL you expect. For example, after creating a session, you can verify the hash contents and expiration with `redis-cli`:\n\n[code example]"},{"id":"learn-more","title":"Learn more","role":"related","text":"* [redis-py guide](https://redis.io/docs/latest/develop/clients/redis-py) - Install and use the Python Redis client\n* [EXPIRE command](https://redis.io/docs/latest/commands/expire) - Set key expiration\n* [HSET command](https://redis.io/docs/latest/commands/hset) - Set hash fields\n* [HGETALL command](https://redis.io/docs/latest/commands/hgetall) - Read a full session hash\n* [HINCRBY command](https://redis.io/docs/latest/commands/hincrby) - Increment counters in a session"}],"examples":[{"id":"the-python-session-store-ex0","language":"python","code":"import redis\nfrom session_store import RedisSessionStore\n\nr = redis.Redis(host=\"localhost\", port=6379, decode_responses=True)\nstore = RedisSessionStore(redis_client=r, ttl=1800)\n\nsession_id = store.create_session(\n    {\n        \"username\": \"andrew\",\n        \"page_views\": \"0\",\n    }\n)\n\nsession = store.get_session(session_id)\nprint(session[\"username\"])\n\nstore.increment_field(session_id, \"page_views\")\nstore.delete_session(session_id)","section_id":"the-python-session-store"},{"id":"data-model-ex0","language":"text","code":"session:abc123...\n  username = andrew\n  page_views = 3\n  session_ttl = 15\n  created_at = 2026-04-02T12:34:56+00:00\n  last_accessed_at = 2026-04-02T12:40:10+00:00","section_id":"data-model"},{"id":"session-store-implementation-ex0","language":"python","code":"def create_session(\n    self,\n    data: Optional[dict[str, str]] = None,\n    ttl: Optional[int] = None,\n) -> str:\n    session_id = secrets.token_urlsafe(32)\n    key = self._session_key(session_id)\n    now = self._timestamp()\n    session_ttl = self._normalize_ttl(ttl)\n\n    payload = {}\n    if data:\n        payload.update(\n            {\n                field: str(value)\n                for field, value in data.items()\n                if field not in RESERVED_SESSION_FIELDS\n            }\n        )\n    payload.update(\n        {\n            \"created_at\": now,\n            \"last_accessed_at\": now,\n            \"session_ttl\": str(session_ttl),\n        }\n    )\n\n    # Pipeline sends HSET and EXPIRE together so the key never exists without a TTL.\n    pipeline = self.redis.pipeline()\n    pipeline.hset(key, mapping=payload)\n    pipeline.expire(key, session_ttl)\n    pipeline.execute()\n    return session_id","section_id":"session-store-implementation"},{"id":"session-store-implementation-ex1","language":"python","code":"def get_session(\n    self,\n    session_id: str,\n    refresh_ttl: bool = True,\n) -> Optional[dict[str, str]]:\n    key = self._session_key(session_id)\n\n    with self.redis.pipeline() as pipeline:\n        while True:\n            try:\n                # WATCH causes the transaction to abort if another client\n                # modifies the key before the MULTI/EXEC block completes.\n                pipeline.watch(key)\n                session = self._load_session_data(pipeline, key)\n                if session is None:\n                    pipeline.unwatch()\n                    return None\n\n                session_ttl = self._normalize_ttl(int(session[\"session_ttl\"]))\n\n                if not refresh_ttl:\n                    pipeline.unwatch()\n                    return session\n\n                now = self._timestamp()\n                pipeline.multi()\n                pipeline.hset(key, mapping={\"last_accessed_at\": now})\n                pipeline.expire(key, session_ttl)\n                pipeline.hgetall(key)\n                _, _, refreshed_session = pipeline.execute()\n\n                if not refreshed_session or not RESERVED_SESSION_FIELDS.issubset(refreshed_session):\n                    return None\n\n                return refreshed_session\n            except redis.WatchError:\n                # Another client modified the key; retry from the start.\n                continue","section_id":"session-store-implementation"},{"id":"session-store-implementation-ex2","language":"python","code":"def increment_field(\n    self,\n    session_id: str,\n    field: str,\n    amount: int = 1,\n) -> Optional[int]:\n    key = self._session_key(session_id)\n\n    with self.redis.pipeline() as pipeline:\n        while True:\n            try:\n                pipeline.watch(key)\n                session = self._load_session_data(pipeline, key)\n                if session is None:\n                    pipeline.unwatch()\n                    return None\n\n                session_ttl = self._normalize_ttl(int(session[\"session_ttl\"]))\n\n                pipeline.multi()\n                pipeline.hincrby(key, field, amount)\n                pipeline.hset(key, mapping={\"last_accessed_at\": self._timestamp()})\n                pipeline.expire(key, session_ttl)\n                new_value, _, _ = pipeline.execute()\n                return int(new_value)\n            except redis.WatchError:\n                continue","section_id":"session-store-implementation"},{"id":"session-store-implementation-ex3","language":"python","code":"def delete_session(self, session_id: str) -> bool:\n    return self.redis.delete(self._session_key(session_id)) == 1","section_id":"session-store-implementation"},{"id":"session-store-implementation-ex4","language":"python","code":"def update_session(self, session_id: str, data: dict[str, str]) -> bool:\n    key = self._session_key(session_id)\n\n    payload = {\n        field: str(value)\n        for field, value in data.items()\n        if field not in RESERVED_SESSION_FIELDS\n    }\n\n    with self.redis.pipeline() as pipeline:\n        while True:\n            try:\n                pipeline.watch(key)\n                session = self._load_session_data(pipeline, key)\n                if session is None:\n                    pipeline.unwatch()\n                    return False\n\n                if not payload:\n                    pipeline.unwatch()\n                    return True\n\n                session_ttl = self._normalize_ttl(int(session[\"session_ttl\"]))\n                payload[\"last_accessed_at\"] = self._timestamp()\n\n                pipeline.multi()\n                pipeline.hset(key, mapping=payload)\n                pipeline.expire(key, session_ttl)\n                pipeline.execute()\n                return True\n            except redis.WatchError:\n                continue","section_id":"session-store-implementation"},{"id":"session-store-implementation-ex5","language":"python","code":"def set_session_ttl(self, session_id: str, ttl: int) -> bool:\n    key = self._session_key(session_id)\n    session_ttl = self._normalize_ttl(ttl)\n\n    with self.redis.pipeline() as pipeline:\n        while True:\n            try:\n                pipeline.watch(key)\n                session = self._load_session_data(pipeline, key)\n                if session is None:\n                    pipeline.unwatch()\n                    return False\n\n                pipeline.multi()\n                pipeline.hset(\n                    key,\n                    mapping={\n                        \"session_ttl\": str(session_ttl),\n                        \"last_accessed_at\": self._timestamp(),\n                    },\n                )\n                pipeline.expire(key, session_ttl)\n                pipeline.execute()\n                return True\n            except redis.WatchError:\n                continue","section_id":"session-store-implementation"},{"id":"session-store-implementation-ex6","language":"python","code":"def get_ttl(self, session_id: str) -> int:\n    return int(self.redis.ttl(self._session_key(session_id)))","section_id":"session-store-implementation"},{"id":"prerequisites-ex0","language":"bash","code":"pip install redis","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir session-store-demo && cd session-store-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/session-store/redis-py\ncurl -O $BASE/session_store.py\ncurl -O $BASE/demo_server.py","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"python demo_server.py","section_id":"start-the-demo-server"},{"id":"cookie-handling-ex0","language":"python","code":"cookie = SimpleCookie()\ncookie[\"sid\"] = session_id\ncookie[\"sid\"][\"path\"] = \"/\"\ncookie[\"sid\"][\"httponly\"] = True\ncookie[\"sid\"][\"samesite\"] = \"Lax\"","section_id":"cookie-handling"},{"id":"inspect-sessions-directly-in-redis-ex0","language":"bash","code":"redis-cli HGETALL session:<session_id>\nredis-cli TTL session:<session_id>","section_id":"inspect-sessions-directly-in-redis"}]}
{"schema_version":2,"id":"develop/use-cases/session-store/ruby","title":"Redis session store with Ruby","url":"https://redis.io/docs/latest/develop/use-cases/session-store/ruby/","summary":"Implement a Redis-backed session store in Ruby with redis-rb","aliases":["/develop/use-cases/session-store/redis-rb"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T17:06:11+01:00","children":[],"page_type":"content","content_hash":"c294bf5e3b082de380ffb9da6d75c7975db7a2d45a407996a189bbb795f4a29b","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis-backed session store in Ruby with [`redis-rb`](https://redis.io/docs/latest/develop/clients/ruby). It includes a small local web server built with WEBrick so you can see the session lifecycle end to end."},{"id":"overview","title":"Overview","role":"overview","text":"Session storage is a common Redis use case for web applications. Instead of keeping session state in local process memory, you store it in Redis and send the browser only an opaque session ID in a cookie.\n\nThat gives you:\n\n* Shared sessions across multiple app servers\n* Automatic expiration using Redis TTLs\n* Fast reads and updates for small pieces of per-user state\n* A clean separation between browser cookies and server-side session data\n\nIn this example, each session is stored as a Redis hash with a key like `session:{session_id}`. The hash holds lightweight fields such as the username, page view count, timestamps, and the configured session TTL. The key also has an expiration so inactive sessions are removed automatically."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. A user submits a login form\n2. The server generates a random session ID with Ruby's `SecureRandom`\n3. The server stores session data in Redis under `session:{id}`\n4. The server sends a `sid` cookie containing only the session ID\n5. Later requests read the cookie, load the hash from Redis, and refresh the TTL\n6. Logging out deletes the Redis key and clears the cookie\n\nBecause the cookie only contains an opaque identifier, the browser never receives the actual session data. That stays in Redis."},{"id":"the-ruby-session-store","title":"The Ruby session store","role":"content","text":"The `RedisSessionStore` class wraps the basic session operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/session-store/ruby/session_store.rb)):\n\n[code example]\n\nRuby's keyword arguments make the constructor options readable, and the store returns hashes and scalars that fit naturally with typical Ruby application code."},{"id":"data-model","title":"Data model","role":"content","text":"Each session is stored in a Redis hash:\n\n[code example]\n\nThe implementation uses:\n\n* [`HSET`](https://redis.io/docs/latest/commands/hset) to create and update session fields\n* [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) to load the session\n* [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby) to update counters\n* [`EXPIRE`](https://redis.io/docs/latest/commands/expire) to implement sliding expiration\n* [`DEL`](https://redis.io/docs/latest/commands/del) to remove a session on logout\n\nThe store treats `created_at`, `last_accessed_at`, and `session_ttl` as reserved internal fields, so caller-provided session data cannot overwrite them."},{"id":"session-store-implementation","title":"Session store implementation","role":"content","text":"The `create_session()` method generates a random session ID, writes the initial hash fields, and sets the TTL:\n\n[code example]\n\nWhen the application reads a session, it refreshes the configured TTL so active users stay logged in:\n\n[code example]\n\nThis is a simple and effective pattern for many apps. For more complex requirements, you might add separate metadata keys, rotate session IDs after login, or store less frequently accessed data elsewhere."},{"id":"installation","title":"Installation","role":"setup","text":"Install the `redis` gem:\n\n[code example]\n\nOr add it to your `Gemfile`:\n\n[code example]\n\nThen run:\n\n[code example]"},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of two Ruby files. Download them from the [`ruby` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/session-store/ruby) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From that directory:\n\n[code example]\n\nThe demo exposes a small interactive page where you can:\n\n* Start a session with a username\n* Choose a short TTL and watch the session expire\n* See the Redis-backed session data rendered in the browser\n* Increment a page-view counter stored in Redis\n* Change the active session TTL from the page\n* Log out and delete the session\n\nThe demo assumes Redis is running on `localhost:6379`, but you can override that with `--redis-host` and `--redis-port`. After starting the server, visit `http://localhost:8080`."},{"id":"cookie-handling","title":"Cookie handling","role":"content","text":"The browser cookie should contain only the session ID:\n\n[code example]\n\nAvoid storing user profiles, roles, or other sensitive session data directly in cookies. Keep that information in Redis and let the cookie act only as a lookup token."},{"id":"production-usage","title":"Production usage","role":"content","text":"This guide uses a deliberately small local demo so you can focus on the Redis session pattern. In production, you will usually want to harden the cookie, session lifecycle, and deployment details around it."},{"id":"secure-the-session-cookie","title":"Secure the session cookie","role":"content","text":"Set cookie attributes that match your deployment and threat model:\n\n* Keep `HttpOnly` enabled so JavaScript cannot read the session cookie\n* Use the `Secure` attribute when serving your app over HTTPS\n* Choose an appropriate `SameSite` policy for your login flow and cross-site behavior"},{"id":"keep-session-data-lightweight","title":"Keep session data lightweight","role":"content","text":"Redis-backed sessions work best when each session stores small, frequently accessed values:\n\n* Usernames, IDs, and feature flags are a good fit\n* Large profiles, document blobs, or activity feeds should usually live elsewhere\n* Consider storing only references if the session needs to point to larger data"},{"id":"handle-expiration-deliberately","title":"Handle expiration deliberately","role":"content","text":"Sliding expiration is convenient, but it also defines how long a hijacked cookie remains useful. For production apps, consider:\n\n* Shorter inactivity TTLs for sensitive applications\n* Separate absolute expiration limits for long-lived sessions\n* Session ID rotation after login or privilege changes"},{"id":"use-a-framework-integration-where-appropriate","title":"Use a framework integration where appropriate","role":"content","text":"This example keeps everything explicit so you can see the Redis session pattern clearly. In a real app, you will often wrap the same Redis operations behind middleware for Rails, Sinatra, Hanami, or another Rack-based framework."},{"id":"next-steps","title":"Next steps","role":"content","text":"You now have a complete Redis-backed session example in Ruby using `redis-rb`. From here you can:\n\n* Adapt the store to your web framework\n* Add session ID rotation or absolute expiration\n* Store additional lightweight session metadata in the same Redis hash\n* Reuse the same Redis deployment across multiple application instances\n\nFor more Redis data modeling patterns, see:\n\n* [Session store overview](https://redis.io/docs/latest/develop/use-cases/session-store)\n* [Ruby client guide](https://redis.io/docs/latest/develop/clients/ruby)\n* [Redis data types](https://redis.io/docs/latest/develop/data-types)"}],"examples":[{"id":"the-ruby-session-store-ex0","language":"ruby","code":"require \"redis\"\nrequire_relative \"session_store\"\n\nredis = Redis.new(host: \"localhost\", port: 6379)\nstore = RedisSessionStore.new(redis: redis, ttl: 1800)\n\nsession_id = store.create_session(\n  {\n    username: \"andrew\",\n    page_views: \"0\"\n  }\n)\n\nsession = store.get_session(session_id)\nputs session[\"username\"]\n\nstore.increment_field(session_id, \"page_views\")\nstore.delete_session(session_id)","section_id":"the-ruby-session-store"},{"id":"data-model-ex0","language":"text","code":"session:abc123...\n  username = andrew\n  page_views = 3\n  session_ttl = 15\n  created_at = 2026-04-08T12:34:56Z\n  last_accessed_at = 2026-04-08T12:40:10Z","section_id":"data-model"},{"id":"session-store-implementation-ex0","language":"ruby","code":"def create_session(data = {}, ttl: nil)\n  session_id = SecureRandom.urlsafe_base64(32)\n  key = session_key(session_id)\n  now = timestamp\n  session_ttl = normalize_ttl(ttl)\n\n  payload = user_payload(data).merge(\n    \"created_at\" => now,\n    \"last_accessed_at\" => now,\n    \"session_ttl\" => session_ttl.to_s\n  )\n\n  @redis.pipelined do |pipeline|\n    pipeline.hset(key, payload)\n    pipeline.expire(key, session_ttl)\n  end\n\n  session_id\nend","section_id":"session-store-implementation"},{"id":"session-store-implementation-ex1","language":"ruby","code":"def get_session(session_id, refresh_ttl: true)\n  key = session_key(session_id)\n  session = @redis.hgetall(key)\n  return nil unless valid_session?(session)\n\n  return session unless refresh_ttl\n\n  session_ttl = normalize_ttl(Integer(session[\"session_ttl\"]))\n  result = @redis.pipelined do |pipeline|\n    pipeline.hset(key, \"last_accessed_at\", timestamp)\n    pipeline.expire(key, session_ttl)\n    pipeline.hgetall(key)\n  end\n\n  refreshed = result[2] || {}\n  valid_session?(refreshed) ? refreshed : nil\nend","section_id":"session-store-implementation"},{"id":"installation-ex0","language":"bash","code":"gem install redis","section_id":"installation"},{"id":"installation-ex1","language":"ruby","code":"gem \"redis\", \"~> 5.0\"","section_id":"installation"},{"id":"installation-ex2","language":"bash","code":"bundle install","section_id":"installation"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir session-store-demo && cd session-store-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/session-store/ruby\ncurl -O $BASE/session_store.rb\ncurl -O $BASE/demo_server.rb","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"gem install redis webrick\nruby demo_server.rb","section_id":"start-the-demo-server"},{"id":"cookie-handling-ex0","language":"ruby","code":"cookie = WEBrick::Cookie.new(\"sid\", session_id)\ncookie.path = \"/\"\ncookie.httponly = true\nres.cookies << cookie","section_id":"cookie-handling"}]}
{"schema_version":2,"id":"develop/use-cases/session-store/rust","title":"Redis session store with Rust","url":"https://redis.io/docs/latest/develop/use-cases/session-store/rust/","summary":"Implement a Redis-backed session store in Rust with redis-rs","aliases":["/develop/use-cases/session-store/redis-rs"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T17:06:11+01:00","children":[],"page_type":"content","content_hash":"4417e5b20962a668e1305da1458622b4eed182386812e6e0e955211dbfebaad1","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to implement a Redis-backed session store in Rust with [`redis-rs`](https://redis.io/docs/latest/develop/clients/rust). The module includes both synchronous and asynchronous APIs, and the demo server uses the async path so you can see the session lifecycle end to end."},{"id":"overview","title":"Overview","role":"overview","text":"Session storage is a common Redis use case for web applications. Instead of keeping session state in local process memory, you store it in Redis and send the browser only an opaque session ID in a cookie.\n\nThat gives you:\n\n* Shared sessions across multiple app servers\n* Automatic expiration using Redis TTLs\n* Fast reads and updates for small pieces of per-user state\n* A clean separation between browser cookies and server-side session data\n\nIn this example, each session is stored as a Redis hash with a key like `session:{session_id}`. The hash holds lightweight fields such as the username, page view count, timestamps, and the configured session TTL. The key also has an expiration so inactive sessions are removed automatically."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. A user submits a login form\n2. The server generates a random session ID with Rust's `rand` support in `getrandom`\n3. The server stores session data in Redis under `session:{id}`\n4. The server sends a `sid` cookie containing only the session ID\n5. Later requests read the cookie, load the hash from Redis, and refresh the TTL\n6. Logging out deletes the Redis key and clears the cookie\n\nBecause the cookie only contains an opaque identifier, the browser never receives the actual session data. That stays in Redis."},{"id":"the-rust-session-store","title":"The Rust session store","role":"content","text":"The `RedisSessionStore` struct wraps the basic session operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/session-store/rust/session_store.rs))."},{"id":"synchronous-usage","title":"Synchronous usage","role":"content","text":"[code example]"},{"id":"asynchronous-usage","title":"Asynchronous usage","role":"content","text":"[code example]\n\nThis mirrors the Rust client docs, where it is common to offer both sync and async examples."},{"id":"data-model","title":"Data model","role":"content","text":"Each session is stored in a Redis hash:\n\n[code example]\n\nThe implementation uses:\n\n* [`HSET`](https://redis.io/docs/latest/commands/hset) to create and update session fields\n* [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) to load the session\n* [`HINCRBY`](https://redis.io/docs/latest/commands/hincrby) to update counters\n* [`EXPIRE`](https://redis.io/docs/latest/commands/expire) to implement sliding expiration\n* [`DEL`](https://redis.io/docs/latest/commands/del) to remove a session on logout\n\nThe store treats `created_at`, `last_accessed_at`, and `session_ttl` as reserved internal fields, so caller-provided session data cannot overwrite them."},{"id":"session-store-implementation","title":"Session store implementation","role":"content","text":"The `create_session()` and `create_session_async()` methods generate a random session ID, write the initial hash fields, and set the TTL:\n\n[code example]\n\nWhen the application reads a session, it refreshes the configured TTL so active users stay logged in:\n\n[code example]\n\nThis is a simple and effective pattern for many apps. For more complex requirements, you might add separate metadata keys, rotate session IDs after login, or store less frequently accessed data elsewhere."},{"id":"installation","title":"Installation","role":"setup","text":"Add the crates you need to `Cargo.toml`:\n\n[code example]\n\nIf you only need the synchronous API, you can omit the async server dependencies and the Tokio feature."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of three files. Download them from the [`rust` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/session-store/rust) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From that directory, build and run:\n\n[code example]\n\nThe demo uses `axum` with an async Redis connection and exposes a small interactive page where you can:\n\n* Start a session with a username\n* Choose a short TTL and watch the session expire\n* See the Redis-backed session data rendered in the browser\n* Increment a page-view counter stored in Redis\n* Change the active session TTL from the page\n* Log out and delete the session\n\nThe demo assumes Redis is running on `localhost:6379`, but you can override that with `REDIS_URL`. After starting the server, visit `http://localhost:8080`."},{"id":"cookie-handling","title":"Cookie handling","role":"content","text":"The browser cookie should contain only the session ID:\n\n[code example]\n\nAvoid storing user profiles, roles, or other sensitive session data directly in cookies. Keep that information in Redis and let the cookie act only as a lookup token."},{"id":"production-usage","title":"Production usage","role":"content","text":"This guide uses a deliberately small local demo so you can focus on the Redis session pattern. In production, you will usually want to harden the cookie, session lifecycle, and deployment details around it."},{"id":"secure-the-session-cookie","title":"Secure the session cookie","role":"content","text":"Set cookie attributes that match your deployment and threat model:\n\n* Keep `HttpOnly` enabled so browser JavaScript cannot read the session cookie\n* Use the `Secure` attribute when serving your app over HTTPS\n* Choose an appropriate `SameSite` policy for your login flow and cross-site behavior"},{"id":"keep-session-data-lightweight","title":"Keep session data lightweight","role":"content","text":"Redis-backed sessions work best when each session stores small, frequently accessed values:\n\n* Usernames, IDs, and feature flags are a good fit\n* Large profiles, document blobs, or activity feeds should usually live elsewhere\n* Consider storing only references if the session needs to point to larger data"},{"id":"handle-expiration-deliberately","title":"Handle expiration deliberately","role":"content","text":"Sliding expiration is convenient, but it also defines how long a hijacked cookie remains useful. For production apps, consider:\n\n* Shorter inactivity TTLs for sensitive applications\n* Separate absolute expiration limits for long-lived sessions\n* Session ID rotation after login or privilege changes"},{"id":"use-a-framework-integration-where-appropriate","title":"Use a framework integration where appropriate","role":"content","text":"This example keeps everything explicit so you can see the Redis session pattern clearly. In a real app, you will often wrap the same Redis operations behind middleware for axum, actix-web, Rocket, or another Rust web framework."},{"id":"next-steps","title":"Next steps","role":"content","text":"You now have a complete Redis-backed session example in Rust using `redis-rs`. From here you can:\n\n* Adapt the store to your web framework\n* Add session ID rotation or absolute expiration\n* Store additional lightweight session metadata in the same Redis hash\n* Reuse the same Redis deployment across multiple application instances\n\nFor more Redis data modeling patterns, see:\n\n* [Session store overview](https://redis.io/docs/latest/develop/use-cases/session-store)\n* [Rust client guide](https://redis.io/docs/latest/develop/clients/rust)\n* [Redis data types](https://redis.io/docs/latest/develop/data-types)"}],"examples":[{"id":"synchronous-usage-ex0","language":"rust","code":"use redis::Client;\nuse std::collections::HashMap;\n\nfn main() -> redis::RedisResult<()> {\n    let client = Client::open(\"redis://localhost:6379/\")?;\n    let mut con = client.get_connection()?;\n\n    let store = RedisSessionStore::new(\"session:\", 1800)?;\n\n    let session_id = store.create_session(\n        &mut con,\n        &HashMap::from([\n            (\"username\".to_string(), \"andrew\".to_string()),\n            (\"page_views\".to_string(), \"0\".to_string()),\n        ]),\n        None,\n    )?;\n\n    let session = store.get_session(&mut con, &session_id, true)?;\n    if let Some(session) = session {\n        println!(\"{}\", session[\"username\"]);\n    }\n\n    let _ = store.increment_field(&mut con, &session_id, \"page_views\", 1)?;\n    let _ = store.delete_session(&mut con, &session_id)?;\n    Ok(())\n}","section_id":"synchronous-usage"},{"id":"asynchronous-usage-ex0","language":"rust","code":"use redis::{AsyncCommands, Client};\nuse std::collections::HashMap;\n\n#[tokio::main]\nasync fn main() -> redis::RedisResult<()> {\n    let client = Client::open(\"redis://localhost:6379/\")?;\n    let mut con = client.get_async_connection().await?;\n\n    let store = RedisSessionStore::new(\"session:\", 1800)?;\n\n    let session_id = store\n        .create_session_async(\n            &mut con,\n            &HashMap::from([\n                (\"username\".to_string(), \"andrew\".to_string()),\n                (\"page_views\".to_string(), \"0\".to_string()),\n            ]),\n            None,\n        )\n        .await?;\n\n    let session = store.get_session_async(&mut con, &session_id, true).await?;\n    if let Some(session) = session {\n        println!(\"{}\", session[\"username\"]);\n    }\n\n    let _ = store\n        .increment_field_async(&mut con, &session_id, \"page_views\", 1)\n        .await?;\n    let _ = store.delete_session_async(&mut con, &session_id).await?;\n    Ok(())\n}","section_id":"asynchronous-usage"},{"id":"data-model-ex0","language":"text","code":"session:abc123...\n  username = andrew\n  page_views = 3\n  session_ttl = 15\n  created_at = 2026-04-02T12:34:56+00:00\n  last_accessed_at = 2026-04-02T12:40:10+00:00","section_id":"data-model"},{"id":"session-store-implementation-ex0","language":"rust","code":"pub fn create_session(\n    &self,\n    con: &mut impl redis::ConnectionLike,\n    data: &HashMap<String, String>,\n    ttl: Option<usize>,\n) -> RedisResult<String> {\n    let session_id = self.create_session_id();\n    let key = self.session_key(&session_id);\n    let now = self.timestamp();\n    let session_ttl = self.normalize_ttl(ttl)?;\n\n    let payload = self.session_payload(data, &now, session_ttl);\n    let payload_pairs = Self::hash_pairs(&payload);\n\n    let _: () = con.hset_multiple(&key, &payload_pairs)?;\n    let _: bool = con.expire(&key, session_ttl as i64)?;\n    Ok(session_id)\n}","section_id":"session-store-implementation"},{"id":"session-store-implementation-ex1","language":"rust","code":"pub async fn get_session_async<C>(\n    &self,\n    con: &mut C,\n    session_id: &str,\n    refresh_ttl: bool,\n) -> RedisResult<Option<HashMap<String, String>>>\nwhere\n    C: redis::aio::ConnectionLike + Send,\n{\n    let key = self.session_key(session_id);\n    let session: HashMap<String, String> = con.hgetall(&key).await?;\n    if !self.is_valid_session(&session) {\n        return Ok(None);\n    }\n\n    if !refresh_ttl {\n        return Ok(Some(session));\n    }\n\n    let session_ttl = self.normalize_ttl(Some(session[\"session_ttl\"].parse()?))?;\n    let _: usize = con.hset(&key, \"last_accessed_at\", self.timestamp()).await?;\n    let _: bool = con.expire(&key, session_ttl as i64).await?;\n\n    let refreshed: HashMap<String, String> = con.hgetall(&key).await?;\n    Ok(self.is_valid_session(&refreshed).then_some(refreshed))\n}","section_id":"session-store-implementation"},{"id":"installation-ex0","language":"toml","code":"[dependencies]\nredis = { version = \"0.24\", features = [\"tokio-comp\"] }\ntokio = { version = \"1\", features = [\"full\"] }\naxum = \"0.7\"\nserde = { version = \"1.0\", features = [\"derive\"] }\ntime = { version = \"0.3\", features = [\"formatting\"] }","section_id":"installation"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir session-store-demo && cd session-store-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/session-store/rust\ncurl -O $BASE/Cargo.toml\ncurl -O $BASE/session_store.rs\ncurl -O $BASE/demo_server.rs","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"cargo build\ncargo run --bin demo_server","section_id":"start-the-demo-server"},{"id":"cookie-handling-ex0","language":"rust","code":"headers.insert(\n    header::SET_COOKIE,\n    HeaderValue::from_str(&format!(\n        \"sid={}; Path=/; HttpOnly; SameSite=Lax\",\n        session_id\n    ))?,\n);","section_id":"cookie-handling"}]}
{"schema_version":2,"id":"develop/use-cases/streaming/dotnet","title":"Redis streaming with StackExchange.Redis","url":"https://redis.io/docs/latest/develop/use-cases/streaming/dotnet/","summary":"Implement a Redis event-streaming pipeline in C# with StackExchange.Redis","aliases":["/develop/use-cases/streaming/stackexchange.redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:55:04+01:00","children":[],"page_type":"content","content_hash":"0218bdfae761cb9f37bb19618bf9610eadc57f72899ae464bafd6c5e71e33e31","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a Redis-backed event-streaming pipeline in C# with [StackExchange.Redis](https://seredis.dev/). It includes a small local web server built with ASP.NET Core's minimal API so you can produce events into a single Redis Stream, watch two independent consumer groups read it at their own pace, and recover stuck deliveries with `XAUTOCLAIM` after simulating a consumer crash."},{"id":"overview","title":"Overview","role":"overview","text":"A Redis Stream is an append-only log of field/value entries with auto-generated, time-ordered IDs. Producers append with [`XADD`](https://redis.io/docs/latest/commands/xadd); consumers belong to *consumer groups* and read with [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup). The group as a whole tracks a single `last-delivered-id` cursor, and each consumer gets its own pending-entries list (PEL) of messages it has been handed but not yet acknowledged. Once a consumer has processed an entry it calls [`XACK`](https://redis.io/docs/latest/commands/xack) to clear the entry from its PEL; entries left unacknowledged past an idle threshold can be reassigned to a healthy consumer with [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim).\n\nThat gives you:\n\n* Ordered, durable history that many independent consumer groups can read at their own pace\n* At-least-once delivery, with per-consumer pending lists and automatic recovery of crashed consumers\n* Horizontal scaling within a group — add a consumer and Redis automatically splits the work\n* Replay of any range with [`XRANGE`](https://redis.io/docs/latest/commands/xrange), independent of consumer-group state\n* Bounded retention through [`XADD MAXLEN ~`](https://redis.io/docs/latest/commands/xadd) or\n  [`XTRIM MINID ~`](https://redis.io/docs/latest/commands/xtrim), without a separate cleanup job\n\nIn this example, producers append order events (`order.placed`, `order.paid`, `order.shipped`, `order.cancelled`) to a single stream at `demo:events:orders`. Two consumer groups read the same stream:\n\n* **`notifications`** — two consumers (`worker-a`, `worker-b`) sharing the work, modelling a fan-out worker pool.\n* **`analytics`** — one consumer (`worker-c`) processing the full event flow on its own."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. The application calls `stream.Produce(eventType, payload)` which runs [`XADD`](https://redis.io/docs/latest/commands/xadd) with an approximate [`MAXLEN ~`](https://redis.io/docs/latest/commands/xadd) cap. Redis assigns an auto-generated time-ordered ID.\n2. Each consumer thread polls [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) with the special ID `>` (meaning \"deliver entries this group has not yet delivered to anyone\") on a short interval.\n3. After processing each entry, the consumer calls [`XACK`](https://redis.io/docs/latest/commands/xack) so Redis can drop it from the group's pending list.\n4. If a consumer is killed (or crashes) before acking, its entries sit in the group's PEL. A periodic [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) sweep reassigns idle entries to a healthy consumer.\n5. Anyone — including code outside the consumer groups — can read history with [`XRANGE`](https://redis.io/docs/latest/commands/xrange) without affecting any group's cursor.\n\nEach consumer group has its own cursor (`last-delivered-id`) and its own pending list, so the two groups in this demo process the same events without coordinating with each other."},{"id":"the-event-stream-helper","title":"The event-stream helper","role":"content","text":"The `EventStream` class wraps the stream operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/streaming/dotnet/EventStream.cs)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each event is a single stream entry — a flat dictionary of field/value strings — with an auto-generated time-ordered ID:\n\n[code example]\n\nThe ID is `{milliseconds}-{sequence}`, monotonically increasing within the stream, so you can range-query by approximate wall-clock time without an extra index. (IDs are ordered within a stream, not across streams — two events appended to different streams at the same millisecond can produce the same ID.) The implementation uses:\n\n* [`XADD ... MAXLEN ~ n`](https://redis.io/docs/latest/commands/xadd), pipelined through `IBatch`, for batch production with a retention cap\n* [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) with the special ID `>` for fresh deliveries to a consumer\n* [`XACK`](https://redis.io/docs/latest/commands/xack) on every processed entry\n* [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) for sweeping idle pending entries to a healthy consumer\n* [`XRANGE`](https://redis.io/docs/latest/commands/xrange) for replay and audit\n* [`XPENDING`](https://redis.io/docs/latest/commands/xpending) for inspecting the per-group pending list\n* [`XINFO STREAM`](https://redis.io/docs/latest/commands/xinfo-stream),\n  [`XINFO GROUPS`](https://redis.io/docs/latest/commands/xinfo-groups), and\n  [`XINFO CONSUMERS`](https://redis.io/docs/latest/commands/xinfo-consumers) for surface-level observability\n* [`XTRIM`](https://redis.io/docs/latest/commands/xtrim) for explicit retention enforcement"},{"id":"producing-events","title":"Producing events","role":"content","text":"`ProduceBatch` pipelines `XADD` calls in a single round trip through `IDatabase.CreateBatch()`. Each call carries an approximate `MAXLEN ~` cap so the stream stays bounded as it rolls forward:\n\n[code example]\n\nThe `~` flavour of `MAXLEN` lets Redis trim at a macro-node boundary, which is much cheaper than exact trimming and is what you want when the cap is a retention *guardrail*, not a hard size constraint. With 300 events produced and `MAXLEN ~ 50`, you might end up with 100 entries left — Redis released the oldest whole macro-node and stopped. The next `XADD` will keep length stable.\n\nIf you genuinely need an exact cap (rare), pass `useApproximateMaxLength: false`. The performance difference is significant on busy streams."},{"id":"reading-with-a-consumer-group","title":"Reading with a consumer group","role":"content","text":"Each consumer in a group runs the same `XREADGROUP` poll. The special ID `>` means \"deliver entries this group has not yet delivered to *anyone*\":\n\n[code example]\n\nStackExchange.Redis intentionally does not expose a blocking `XREADGROUP` (the long-blocking variant would monopolise the multiplexer's single command pipeline). The consumer thread polls on a short fixed interval (100 ms in the demo) so the call returns promptly when there is nothing waiting and Redis hands out the next batch as soon as producers append more entries. A production helper would naturally be `async` (`StreamReadGroupAsync` with `await Task.Delay`), which gives the same effect without parking a thread.\n\nReading with an explicit ID like `0-0` instead of `>` does something different — it replays entries already delivered to *this* consumer name (its private PEL). That is the canonical recovery path when the same consumer restarts: catch up on its own pending entries first, then resume reading new ones."},{"id":"acknowledging-entries","title":"Acknowledging entries","role":"content","text":"Once the consumer has processed an entry, `XACK` tells Redis it can drop the entry from the group's pending list:\n\n[code example]\n\nThis is the linchpin of at-least-once delivery: an entry that is never acked stays in the PEL until a claim moves it elsewhere. If your consumer thread crashes between processing and ack, the next claim sweep picks the entry back up. The one caveat is retention: `XADD MAXLEN ~` and `XTRIM` can release the entry's *payload* even while its ID is still in the PEL. The next `XAUTOCLAIM` returns those IDs in its `DeletedIds` list and removes them from the PEL inside the same command — the entry cannot be retried, so the caller should log it and route to a dead-letter store for audit. The example handles this explicitly in `/autoclaim` further down.\n\nThe trade-off is the opposite of pub/sub: a slow or crashed consumer doesn't lose messages, but it does mean your downstream system must be idempotent. If you process an order twice because the first attempt died after the side effect but before the ack, the second attempt must be safe."},{"id":"multiple-consumer-groups-one-stream","title":"Multiple consumer groups, one stream","role":"content","text":"The big difference between Redis Streams and a job queue is that any number of independent consumer groups can read the same stream. The demo sets up two groups on `demo:events:orders`:\n\n[code example]\n\nEach group has its own cursor. Producing 5 events results in `notifications` and `analytics` each receiving all 5, with no coordination between them. Within `notifications`, the work is split across `worker-a` and `worker-b`: Redis hands each `XREADGROUP` call whatever entries are not yet delivered to anyone in the group, so adding a second worker doubles throughput without any rebalance logic.\n\nThe `startId: \"0-0\"` argument means \"deliver everything in the stream from the beginning\" — useful in a demo and for fresh groups bootstrapping from history. In production, a brand-new group reading a long-existing stream usually starts at `$` (\"only events after this point\") and uses [`XRANGE`](https://redis.io/docs/latest/commands/xrange) explicitly if it needs history."},{"id":"recovering-crashed-consumers-with-xautoclaim","title":"Recovering crashed consumers with XAUTOCLAIM","role":"content","text":"The demo's \"Crash next 3\" button tells a chosen consumer to drop its next three deliveries on the floor without acking them — the same effect as a worker process dying mid-message. Those entries stay in the group's PEL with their delivery counter incremented. Once they have been idle for at least `claimMinIdleMs`, any healthy consumer in the group can rescue them by calling `XAUTOCLAIM` *with itself as the target*. `ConsumerWorker.ReapIdlePel` wraps that pattern:\n\n[code example]\n\nThe underlying `stream.Autoclaim` helper pages through the group's PEL with `XAUTOCLAIM`'s continuation cursor, using StackExchange.Redis 2.7+'s typed `StreamAutoClaim` wrapper:\n\n[code example]\n\nA single `XAUTOCLAIM` call scans up to `pageCount` PEL entries starting at `startId`, reassigns the ones idle for at least `minIdleTimeInMs` to the named consumer, and returns a continuation cursor on `StreamAutoClaimResult.NextStartId`. For a full sweep, loop until the cursor returns to `0-0` (with a `maxPages` safety net so one call cannot monopolise a very large PEL). The delivery counter is incremented on every claim — after a few cycles you can use it to spot a *poison-pill* message that crashes every consumer that touches it, and route it to a dead-letter stream so the bad entry stops cycling. (New entries keep flowing past the poison pill — `XREADGROUP >` still delivers fresh work — but the bad entry's repeated reclaim wastes consumer time and keeps the PEL larger than it needs to be.)\n\nThe `StreamAutoClaimResult.DeletedIds` list contains PEL entry IDs whose stream payload was already trimmed by the time the claim ran (typically because `MAXLEN ~` retention outran a slow consumer). `XAUTOCLAIM` removes those dangling slots from the PEL itself, so the caller does *not* need to `XACK` them — but the entries cannot be retried either, so log and route them to a dead-letter store for offline inspection. Redis 7.0 introduced this third return element; the example requires Redis 7.0+ for that reason.\n\n`ReapIdlePel` is the right primitive for the recovery path because it claims and processes in one step: every entry the call returned is now in *this* consumer's PEL, so the same consumer is responsible for processing and acking it. In production each consumer thread runs `ReapIdlePel` periodically (every few seconds, on a timer) so a crashed peer's entries never sit invisibly. The demo exposes it as a manual button so you can trigger the reap after waiting for the idle threshold.\n\n`XCLAIM` (singular, no auto) does the same thing for a specific list of entry IDs you already have in hand — useful when you want to take ownership of one known stuck entry, or when you need to move a specific consumer's PEL to a peer (the case the demo's \"Remove consumer\" button handles via `HandoverPending`). `XAUTOCLAIM` cannot filter by source consumer, so it cannot be used for a per-consumer handover."},{"id":"replay-with-xrange","title":"Replay with XRANGE","role":"content","text":"`XRANGE` reads a slice of history. It is completely independent of any consumer group — no cursors move, no acks happen — so it is safe to call any number of times, from any process:\n\n[code example]\n\nThe special IDs `-` and `+` mean \"from the very beginning\" and \"to the very end\". You can also pass real IDs (`1716998413541-0`) or just the millisecond part (`1716998413541`, which Redis interprets as \"any entry with this timestamp\").\n\nTypical uses:\n\n* **Bootstrapping a new projection** — read the entire stream from `-` and build a derived view in another store (a search index, a SQL table, a different cache). Doing this against a consumer group would consume the entries; `XRANGE` lets you do it without disrupting live consumers.\n* **Auditing recent activity** — read the last few minutes by ID range without touching any group cursor.\n* **Debugging** — fetch one specific entry by its ID, or a tight range around an incident timestamp, to see exactly what producers wrote."},{"id":"the-consumer-worker-thread","title":"The consumer worker thread","role":"content","text":"`ConsumerWorker` wraps the `XREADGROUP` → process → `XACK` loop in a background thread\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/streaming/dotnet/ConsumerWorker.cs)):\n\n[code example]\n\n`HandleEntry` either acks (the normal path) or, when the demo has asked the worker to \"crash\", drops the entry on the floor and increments a counter so the UI can show what is currently in the PEL waiting to be claimed.\n\nRecovery of stuck PEL entries — this consumer's, after a restart, or another consumer's, after a crash — runs through a separate `ReapIdlePel` method rather than the read loop. That method calls `XAUTOCLAIM` with this consumer as the target, then processes whatever was claimed in the same flow as new entries. This is the textbook Streams pattern: each consumer is its own reaper, running `XAUTOCLAIM(self)` periodically (or on demand) so a crashed peer's entries never sit invisibly in the PEL. The demo's \"XAUTOCLAIM to selected\" button calls `ReapIdlePel` on the chosen consumer; in production you would run it from a timer every few seconds.\n\nNote that the worker's main read loop deliberately does *not* call `XREADGROUP 0` to drain its own PEL on every iteration. That would re-deliver every pending entry continuously and *reset its idle counter to zero* each time, which would keep crashed entries below the `XAUTOCLAIM` threshold forever. Using `XAUTOCLAIM(self)` as the recovery primitive — which only fires for entries idle longer than `minIdleTime` — avoids that whole class of bug.\n\nThe pause and crash levers exist only for the demo. A real consumer is just the read-process-ack loop — everything else in this class is instrumentation."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* Redis 7.0 or later. `XAUTOCLAIM` was added in Redis 6.2, but its reply gained a third\n  element (the list of deleted IDs) in 7.0; the example relies on that shape.\n* .NET 8 SDK or later.\n* The `StackExchange.Redis` NuGet package at version 2.7 or later (already declared in `StreamingDemo.csproj`).\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of four files. Download them from the [`dotnet` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/streaming/dotnet) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From that directory:\n\n[code example]\n\nYou should see:\n\n[code example]\n\nBy default the demo wipes the configured stream key on startup so each run starts from a clean state. Pass `--no-reset` to keep any existing data at the key (useful when re-running against the same stream to inspect prior state), or `--stream-key <name>` to point the demo at a different key entirely.\n\nOpen [http://localhost:8785](http://localhost:8785) in a browser. You can:\n\n* **Produce** any number of events of a chosen type (or random types). Watch the stream length grow and the tail update.\n* See each **consumer group**: its `last-delivered-id`, the size of its pending list, and the consumers in it. Each consumer shows its processed count, pending count, and idle time.\n* **Add or remove** consumers within a group at runtime to see Redis split the work across the new shape.\n* Click **Crash next 3** on a consumer to drop its next three deliveries — the same effect as a worker process dying after `XREADGROUP` but before `XACK`. Watch the **Pending entries (XPENDING)** panel fill up.\n* Wait until the idle time exceeds the threshold (default 5000 ms), pick a healthy target consumer, and click **XAUTOCLAIM to selected** — the stuck entries are reassigned and the delivery counter increments.\n* **Replay (XRANGE)** any range to confirm the full history is independent of consumer-group state.\n* **XTRIM** with an approximate `MAXLEN` to bound retention. Note that an approximate trim only releases whole macro-nodes — `MAXLEN ~ 50` on a small stream may not delete anything; on a 300-entry stream it typically lands at around 100.\n* Click **Reset demo** to drop the stream and re-seed the default groups."},{"id":"production-usage","title":"Production usage","role":"content","text":""},{"id":"use-the-async-api-on-the-request-path","title":"Use the async API on the request path","role":"content","text":"The demo helper is synchronous (`StreamAdd`, `StreamReadGroup`, `StreamAutoClaim`, `Thread.Sleep`) to keep the code compact. In production, prefer the `Async` overloads — `StreamAddAsync`, `StreamReadGroupAsync`, `StreamAutoClaimAsync`, `StreamAcknowledgeAsync` — together with `await Task.Delay` so request-handling threads return to the ThreadPool while the loader is in flight. The streaming structure is identical; just propagate `await`s through the call chain."},{"id":"threadpool-sizing-for-synchronous-consumers","title":"ThreadPool sizing for synchronous consumers","role":"content","text":"`Program.cs` calls `ThreadPool.SetMinThreads(64, 64)` at startup. With multiple consumer groups each running a polling thread per consumer, the default ThreadPool can grow too slowly under load and starve the polling threads. Raising the floor up front is a property of *this synchronous demo*; an async helper (see above) avoids the issue entirely because the poll naturally yields the thread between iterations."},{"id":"pick-retention-by-length-or-by-minimum-id","title":"Pick retention by length or by minimum ID","role":"content","text":"The demo uses `MAXLEN ~` on every `XADD`. Two alternatives are worth considering:\n\n* `MINID ~ <id>` — keep only entries newer than an ID. If you want \"the last 24 hours\", compute the wall-clock cutoff and call `stream.TrimMinid(\"{ms}-0\")`. This is the right pattern when retention is time-bounded.\n* No cap on `XADD` plus a periodic `XTRIM` job — useful if your producer is hot and the per-`XADD` work has to stay minimal, or if retention rules are complex (a separate process can also factor in consumer-group lag).\n\nIn all three cases the trimming is approximate by default. Use exact trimming (`MAXLEN n` or `MINID id` without `~`) only when you genuinely need an exact count."},{"id":"dont-let-consumer-group-lag-silently-grow","title":"Don't let consumer-group lag silently grow","role":"content","text":"`XINFO GROUPS` reports each group's `lag` (entries the group has not yet read) and `pending` (entries delivered but not acked). In production, alert on either of these crossing a threshold — a steadily growing pending count usually means consumers are crashing without `XAUTOCLAIM` running, and a growing lag means consumers can't keep up with producers.\n\nThe same applies inside a group: `XINFO CONSUMERS` reports per-consumer pending counts and idle times, so you can spot one slow consumer holding entries that the rest of the group is waiting on."},{"id":"make-consumer-logic-idempotent","title":"Make consumer logic idempotent","role":"content","text":"`XAUTOCLAIM` can re-deliver an entry to a different consumer after a crash. If your processing has side effects (sending email, charging a card, updating a downstream store), make sure the same entry processed twice gives the same result — use an idempotency key, an upsert with conditional check, or a once-per-id guard table. Redis Streams cannot give you exactly-once semantics on its own."},{"id":"bound-the-delivery-counter-as-a-poison-pill-signal","title":"Bound the delivery counter as a poison-pill signal","role":"content","text":"`XPENDING` returns each entry's delivery count, incremented on every claim. If an entry has been delivered (and dropped) several times, the next consumer is unlikely to fare better. After some threshold — `deliveries >= 5`, say — route the entry to a *dead-letter stream*, ack it on the original group, and alert. New entries keep flowing past a poison pill (`XREADGROUP >` still delivers fresh work), but the bad entry's repeated reclaim wastes consumer time and keeps the PEL bigger than it needs to be — without a DLQ threshold it can also slowly trip retention/lag alerts."},{"id":"partition-by-tenant-or-entity-for-scale","title":"Partition by tenant or entity for scale","role":"content","text":"A single Redis Stream is a single key, and on a Redis Cluster a single key lives on a single shard. If your throughput exceeds what one shard can handle, partition the stream — for example by tenant ID (`events:orders:{tenant_a}`, `events:orders:{tenant_b}`) — so different tenants land on different shards. Hash-tags (`{tenant_a}`) keep all related streams on the same shard if you need to multi-stream atomically.\n\nPer-entity partitioning (`events:order:{order_id}`) is the canonical pattern when you treat each entity's stream as the event-sourcing log for that entity: every state change for one order goes on its own stream, which is also bounded in size by the entity's lifetime."},{"id":"use-a-separate-consumer-pool-per-group","title":"Use a separate consumer pool per group","role":"content","text":"The demo runs every consumer in one process. In production each consumer group is usually its own deployment — its own pool of pods or VMs — so a slow projection in `analytics` cannot pull `notifications` workers off their stream. Each pod runs one consumer thread per CPU core, with `XAUTOCLAIM` either embedded in the consumer loop (every N reads, claim idle entries to self) or run by a separate reaper."},{"id":"stackexchangeredis-does-not-expose-a-blocking-xreadgroup","title":"StackExchange.Redis does not expose a blocking XREADGROUP","role":"content","text":"StackExchange.Redis intentionally omits the long-blocking variant of `XREADGROUP` (and `XREAD`) because a blocking command would monopolise the multiplexer's single command pipeline — every other call on the same connection would queue behind it. The idiomatic workaround for this demo is a short polling interval on the synchronous wrapper; the idiomatic production pattern is `StreamReadGroupAsync` with `await Task.Delay`, which yields the thread between iterations without ever holding the multiplexer."},{"id":"dont-read-with-xread-no-group-and-then-try-to-ack","title":"Don't read with XREAD (no group) and then try to ack","role":"content","text":"`XREAD` and `XREADGROUP` are different mechanisms. `XREAD` is a tail-the-log read with no consumer-group state — entries are not added to any PEL, and you cannot `XACK` them. If you want at-least-once delivery and crash recovery, you must read through a consumer group.\n\n`XREAD` is still useful for read-only tail clients (a UI streaming events, a debugger, a `tail -f`-style command-line tool). It's just not part of the at-least-once path."},{"id":"inspect-the-stream-directly-with-redis-cli","title":"Inspect the stream directly with redis-cli","role":"content","text":"When testing or troubleshooting, inspect the stream directly to confirm the consumer state is what you expect:\n\n[code example]\n\nIf a group's `lag` is growing while consumers' `idle` times are short, consumers are healthy but producers are outpacing them — add more consumers. If `pending` is growing while `lag` is small, consumers are *receiving* entries but not *acking* them — either they are crashing mid-message or your acking logic has a bug."},{"id":"learn-more","title":"Learn more","role":"related","text":"This example uses the following Redis commands:\n\n* [`XADD`](https://redis.io/docs/latest/commands/xadd) to append an event with an approximate `MAXLEN` cap.\n* [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) to read new entries for a consumer in a group.\n* [`XACK`](https://redis.io/docs/latest/commands/xack) to acknowledge a processed entry.\n* [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) to reassign idle pending entries to a healthy consumer.\n* [`XCLAIM`](https://redis.io/docs/latest/commands/xclaim) to take ownership of a specific list of pending entry IDs by hand (used by `HandoverPending` to move a leaving consumer's PEL to a peer, since `XAUTOCLAIM` has no source-consumer filter).\n* [`XRANGE`](https://redis.io/docs/latest/commands/xrange) for replay and audit, independent of consumer-group state.\n* [`XPENDING`](https://redis.io/docs/latest/commands/xpending) to inspect the per-group pending list with idle times and delivery counts.\n* [`XTRIM`](https://redis.io/docs/latest/commands/xtrim) for explicit retention enforcement.\n* [`XGROUP CREATE`](https://redis.io/docs/latest/commands/xgroup-create) and\n  [`XGROUP DELCONSUMER`](https://redis.io/docs/latest/commands/xgroup-delconsumer) to manage groups and consumers.\n* [`XINFO STREAM`](https://redis.io/docs/latest/commands/xinfo-stream),\n  [`XINFO GROUPS`](https://redis.io/docs/latest/commands/xinfo-groups), and\n  [`XINFO CONSUMERS`](https://redis.io/docs/latest/commands/xinfo-consumers) for observability.\n\nSee the [StackExchange.Redis docs](https://seredis.dev/) for the full client reference, and the [Streams overview](https://redis.io/docs/latest/develop/data-types/streams) for the deeper conceptual model — consumer groups, the PEL, claim semantics, capped streams, and the differences with Kafka partitions."}],"examples":[{"id":"the-event-stream-helper-ex0","language":"csharp","code":"using StackExchange.Redis;\nusing StreamingDemo;\n\nvar redis = ConnectionMultiplexer.Connect(\"localhost:6379\");\nvar stream = new EventStream(\n    redis.GetDatabase(),\n    streamKey: \"demo:events:orders\",\n    maxlenApprox: 2000,        // retention guardrail\n    claimMinIdleMs: 5000);     // XAUTOCLAIM threshold\n\n// Producer\nvar streamId = stream.Produce(\n    \"order.placed\",\n    new Dictionary<string, string?>\n    {\n        [\"order_id\"] = \"o-1234\",\n        [\"customer\"] = \"alice\",\n        [\"amount\"] = \"49.50\",\n    });\n\n// Consumer group + one consumer\nstream.EnsureGroup(\"notifications\", startId: \"0-0\");\nvar entries = stream.Consume(\"notifications\", \"worker-a\", count: 10);\nforeach (var entry in entries)\n{\n    Handle(entry.Fields);                              // your processing\n    stream.Ack(\"notifications\", new[] { entry.Id });   // XACK\n}\n\n// Recover stuck PEL entries by reaping them into a healthy consumer.\n// The textbook pattern: each consumer periodically calls XAUTOCLAIM\n// with itself as the target and processes whatever it claimed.\n// ConsumerWorker.ReapIdlePel wraps that flow; the low-level helper\n// stream.Autoclaim(group, targetName) is also available if you want\n// to drive XAUTOCLAIM directly.\nvar result = workerB.ReapIdlePel();\n// result == new ReapResult(Claimed: N, DeletedIds: [...], Processed: M)\n// DeletedIds are PEL entries whose payload was already trimmed.\n// Redis 7+ has already removed those slots from the PEL, so no XACK\n// is needed — log them and route to a dead-letter store for audit.\n\n// Replay history (independent of any group's cursor)\nforeach (var entry in stream.Replay(\"-\", \"+\", count: 50))\n{\n    Console.WriteLine($\"{entry.Id} {string.Join(\",\", entry.Fields)}\");\n}","section_id":"the-event-stream-helper"},{"id":"data-model-ex0","language":"text","code":"demo:events:orders\n  1716998413541-0   type=order.placed     order_id=o-1234   customer=alice  amount=49.50  ts_ms=...\n  1716998413542-0   type=order.paid       order_id=o-1234   customer=alice  amount=49.50  ts_ms=...\n  1716998413542-1   type=order.shipped    order_id=o-1235   customer=bob    amount=12.00  ts_ms=...\n  ...","section_id":"data-model"},{"id":"producing-events-ex0","language":"csharp","code":"public string[] ProduceBatch(IEnumerable<(string EventType, IDictionary<string, string?> Payload)> events)\n{\n    var eventList = events.ToList();\n    var batch = _db.CreateBatch();\n    var addTasks = new Task<RedisValue>[eventList.Count];\n    for (var i = 0; i < eventList.Count; i++)\n    {\n        var (eventType, payload) = eventList[i];\n        var pairs = EncodeFields(eventType, payload);\n        addTasks[i] = batch.StreamAddAsync(\n            _streamKey,\n            pairs,\n            messageId: null,\n            maxLength: _maxlenApprox,\n            useApproximateMaxLength: true);\n    }\n    batch.Execute();\n    Task.WaitAll(addTasks);\n\n    var ids = addTasks.Select(t => (string)t.Result!).ToArray();\n    Interlocked.Add(ref _producedTotal, ids.Length);\n    return ids;\n}","section_id":"producing-events"},{"id":"reading-with-a-consumer-group-ex0","language":"csharp","code":"public List<StreamRecord> Consume(string group, string consumer, int count = 10)\n{\n    var entries = _db.StreamReadGroup(\n        _streamKey,\n        group,\n        consumer,\n        position: \">\",\n        count: count);\n    return ToRecords(entries);\n}","section_id":"reading-with-a-consumer-group"},{"id":"acknowledging-entries-ex0","language":"csharp","code":"public long Ack(string group, IEnumerable<string> ids)\n{\n    var idArray = ids.Select(id => (RedisValue)id).ToArray();\n    if (idArray.Length == 0) return 0;\n    var n = _db.StreamAcknowledge(_streamKey, group, idArray);\n    Interlocked.Add(ref _ackedTotal, n);\n    return n;\n}","section_id":"acknowledging-entries"},{"id":"multiple-consumer-groups-one-stream-ex0","language":"csharp","code":"stream.EnsureGroup(\"notifications\", startId: \"0-0\");\nstream.EnsureGroup(\"analytics\",     startId: \"0-0\");","section_id":"multiple-consumer-groups-one-stream"},{"id":"recovering-crashed-consumers-with-xautoclaim-ex0","language":"csharp","code":"public ReapResult ReapIdlePel()\n{\n    var swept = _stream.Autoclaim(Group, Name, pageCount: 100, maxPages: 10);\n    var processed = 0;\n    foreach (var record in swept.Claimed)\n    {\n        try\n        {\n            HandleEntry(record.Id, record.Fields);\n            processed++;\n        }\n        catch (Exception ex)\n        {\n            Console.Error.WriteLine($\"[{Group}/{Name}] reap failed on {record.Id}: {ex.Message}\");\n        }\n    }\n    lock (_lock) { _reaped += processed; }\n    return new ReapResult(swept.Claimed.Count, swept.DeletedIds, processed);\n}","section_id":"recovering-crashed-consumers-with-xautoclaim"},{"id":"recovering-crashed-consumers-with-xautoclaim-ex1","language":"csharp","code":"public AutoClaimResult Autoclaim(\n    string group,\n    string consumer,\n    int pageCount = 100,\n    string startId = \"0-0\",\n    int maxPages = 10)\n{\n    var claimedAll = new List<StreamRecord>();\n    var deletedAll = new List<string>();\n    var cursor = startId;\n    for (var i = 0; i < maxPages; i++)\n    {\n        var result = _db.StreamAutoClaim(\n            _streamKey,\n            group,\n            consumer,\n            minIdleTimeInMs: _claimMinIdleMs,\n            startAtId: cursor,\n            count: pageCount);\n        if (result.IsNull) break;\n        foreach (var entry in result.ClaimedEntries)\n            claimedAll.Add(EntryToRecord(entry));\n        foreach (var id in result.DeletedIds ?? Array.Empty<RedisValue>())\n            deletedAll.Add((string)id!);\n        var nextId = (string)result.NextStartId!;\n        if (nextId == \"0-0\") break;\n        cursor = nextId;\n    }\n    Interlocked.Add(ref _claimedTotal, claimedAll.Count);\n    return new AutoClaimResult(claimedAll, deletedAll);\n}","section_id":"recovering-crashed-consumers-with-xautoclaim"},{"id":"replay-with-xrange-ex0","language":"csharp","code":"public List<StreamRecord> Replay(string startId = \"-\", string endId = \"+\", int count = 100)\n{\n    var entries = _db.StreamRange(\n        _streamKey,\n        minId: startId,\n        maxId: endId,\n        count: count);\n    return entries.Select(EntryToRecord).ToList();\n}","section_id":"replay-with-xrange"},{"id":"the-consumer-worker-thread-ex0","language":"csharp","code":"private void Run()\n{\n    while (!_stop)\n    {\n        bool paused;\n        lock (_lock) { paused = _paused; }\n        if (paused) { Thread.Sleep(50); continue; }\n\n        List<StreamRecord> entries;\n        try\n        {\n            entries = _stream.Consume(Group, Name, count: 10);\n        }\n        catch (Exception ex)\n        {\n            Console.Error.WriteLine($\"[{Group}/{Name}] read failed: {ex.Message}\");\n            Thread.Sleep(500);\n            continue;\n        }\n\n        if (entries.Count == 0)\n        {\n            Thread.Sleep(_pollIntervalMs);\n            continue;\n        }\n\n        foreach (var entry in entries)\n        {\n            Dispatch(entry.Id, entry.Fields);\n        }\n    }\n}","section_id":"the-consumer-worker-thread"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir streaming-demo && cd streaming-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/streaming/dotnet\ncurl -O $BASE/EventStream.cs\ncurl -O $BASE/ConsumerWorker.cs\ncurl -O $BASE/Program.cs\ncurl -O $BASE/StreamingDemo.csproj","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"dotnet run","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"Deleting any existing data at key 'demo:events:orders' for a clean demo run (pass --no-reset to keep it).\nRedis streaming demo server listening on http://localhost:8785\nUsing Redis at localhost:6379 with stream key 'demo:events:orders' (MAXLEN ~ 2000)\nSeeded 3 consumer(s) across 2 group(s)","section_id":"start-the-demo-server"},{"id":"inspect-the-stream-directly-with-redis-cli-ex0","language":"bash","code":"# Stream summary\nredis-cli XLEN demo:events:orders\nredis-cli XINFO STREAM demo:events:orders\n\n# Group cursors and pending counts\nredis-cli XINFO GROUPS demo:events:orders\n\n# Consumers within a group\nredis-cli XINFO CONSUMERS demo:events:orders notifications\n\n# Pending entries with idle time and delivery count\nredis-cli XPENDING demo:events:orders notifications - + 20\n\n# Tail the stream live (no consumer-group state — like tail -f)\nredis-cli XREAD BLOCK 0 STREAMS demo:events:orders '$'\n\n# Replay a range\nredis-cli XRANGE demo:events:orders - + COUNT 50","section_id":"inspect-the-stream-directly-with-redis-cli"}]}
{"schema_version":2,"id":"develop/use-cases/streaming/go","title":"Redis streaming with go-redis","url":"https://redis.io/docs/latest/develop/use-cases/streaming/go/","summary":"Implement a Redis event-streaming pipeline in Go with go-redis","aliases":["/develop/use-cases/streaming/go-redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:55:04+01:00","children":[],"page_type":"content","content_hash":"dc37a4fb40eb92cc57215f845e5907dc5f07df07161ead2b00c03d5ca8d97957","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a Redis-backed event-streaming pipeline in Go with [`go-redis`](https://redis.io/docs/latest/develop/clients/go). It includes a small local web server built with Go's standard `net/http` package so you can produce events into a single Redis Stream, watch two independent consumer groups read it at their own pace, and recover stuck deliveries with `XAUTOCLAIM` after simulating a consumer crash."},{"id":"overview","title":"Overview","role":"overview","text":"A Redis Stream is an append-only log of field/value entries with auto-generated, time-ordered IDs. Producers append with [`XADD`](https://redis.io/docs/latest/commands/xadd); consumers belong to *consumer groups* and read with [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup). The group as a whole tracks a single `last-delivered-id` cursor, and each consumer gets its own pending-entries list (PEL) of messages it has been handed but not yet acknowledged. Once a consumer has processed an entry it calls [`XACK`](https://redis.io/docs/latest/commands/xack) to clear the entry from its PEL; entries left unacknowledged past an idle threshold can be reassigned to a healthy consumer with [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim).\n\nThat gives you:\n\n* Ordered, durable history that many independent consumer groups can read at their own pace\n* At-least-once delivery, with per-consumer pending lists and automatic recovery of crashed consumers\n* Horizontal scaling within a group — add a consumer and Redis automatically splits the work\n* Replay of any range with [`XRANGE`](https://redis.io/docs/latest/commands/xrange), independent of consumer-group state\n* Bounded retention through [`XADD MAXLEN ~`](https://redis.io/docs/latest/commands/xadd) or\n  [`XTRIM MINID ~`](https://redis.io/docs/latest/commands/xtrim), without a separate cleanup job\n\nIn this example, producers append order events (`order.placed`, `order.paid`, `order.shipped`, `order.cancelled`) to a single stream at `demo:events:orders`. Two consumer groups read the same stream:\n\n* **`notifications`** — two consumers (`worker-a`, `worker-b`) sharing the work, modelling a fan-out worker pool.\n* **`analytics`** — one consumer (`worker-c`) processing the full event flow on its own."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. The application calls `stream.Produce(ctx, eventType, payload)` which runs [`XADD`](https://redis.io/docs/latest/commands/xadd) with an approximate [`MAXLEN ~`](https://redis.io/docs/latest/commands/xadd) cap. Redis assigns an auto-generated time-ordered ID.\n2. Each consumer goroutine loops on [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) with the special ID `>` (meaning \"deliver entries this group has not yet delivered to anyone\") and a short block timeout.\n3. After processing each entry, the consumer calls [`XACK`](https://redis.io/docs/latest/commands/xack) so Redis can drop it from the group's pending list.\n4. If a consumer is killed (or crashes) before acking, its entries sit in the group's PEL. A periodic [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) sweep reassigns idle entries to a healthy consumer.\n5. Anyone — including code outside the consumer groups — can read history with [`XRANGE`](https://redis.io/docs/latest/commands/xrange) without affecting any group's cursor.\n\nEach consumer group has its own cursor (`last-delivered-id`) and its own pending list, so the two groups in this demo process the same events without coordinating with each other."},{"id":"the-event-stream-helper","title":"The event-stream helper","role":"content","text":"The `EventStream` type wraps the stream operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/streaming/go/event_stream.go)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each event is a single stream entry — a flat map of field/value strings — with an auto-generated time-ordered ID:\n\n[code example]\n\nThe ID is `{milliseconds}-{sequence}`, monotonically increasing within the stream, so you can range-query by approximate wall-clock time without an extra index. (IDs are ordered within a stream, not across streams — two events appended to different streams at the same millisecond can produce the same ID.) The implementation uses:\n\n* [`XADD ... MAXLEN ~ n`](https://redis.io/docs/latest/commands/xadd), pipelined, for batch production with a retention cap\n* [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) with the special ID `>` for fresh deliveries to a consumer\n* [`XACK`](https://redis.io/docs/latest/commands/xack) on every processed entry\n* [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) for sweeping idle pending entries to a healthy consumer\n* [`XRANGE`](https://redis.io/docs/latest/commands/xrange) for replay and audit\n* [`XPENDING`](https://redis.io/docs/latest/commands/xpending) for inspecting the per-group pending list\n* [`XINFO STREAM`](https://redis.io/docs/latest/commands/xinfo-stream),\n  [`XINFO GROUPS`](https://redis.io/docs/latest/commands/xinfo-groups), and\n  [`XINFO CONSUMERS`](https://redis.io/docs/latest/commands/xinfo-consumers) for surface-level observability\n* [`XTRIM`](https://redis.io/docs/latest/commands/xtrim) for explicit retention enforcement"},{"id":"producing-events","title":"Producing events","role":"content","text":"`ProduceBatch` pipelines `XADD` calls in a single round trip. Each call carries an approximate `MAXLEN ~` cap so the stream stays bounded as it rolls forward:\n\n[code example]\n\nThe `~` flavour of `MAXLEN` lets Redis trim at a macro-node boundary, which is much cheaper than exact trimming and is what you want when the cap is a retention *guardrail*, not a hard size constraint. With 300 events produced and `MAXLEN ~ 50`, you might end up with 100 entries left — Redis released the oldest whole macro-node and stopped. The next `XADD` will keep length stable.\n\nIf you genuinely need an exact cap (rare), drop the `Approx: true` field on `XAddArgs`. The performance difference is significant on busy streams."},{"id":"reading-with-a-consumer-group","title":"Reading with a consumer group","role":"content","text":"Each consumer in a group runs the same `XREADGROUP` loop. The special ID `>` means \"deliver entries this group has not yet delivered to *anyone*\":\n\n[code example]\n\n`blockMs` makes the call efficient even when the stream is idle: the client parks on the server until either an entry arrives or the timeout expires, so consumers don't busy-loop.\n\nReading with an explicit ID like `0` instead of `>` does something different — it replays entries already delivered to *this* consumer name (its private PEL). That is the canonical recovery path when the same consumer restarts: catch up on its own pending entries first, then resume reading new ones. The helper exposes that path separately as `ConsumeOwnPel`."},{"id":"acknowledging-entries","title":"Acknowledging entries","role":"content","text":"Once the consumer has processed an entry, `XACK` tells Redis it can drop the entry from the group's pending list:\n\n[code example]\n\nThis is the linchpin of at-least-once delivery: an entry that is never acked stays in the PEL until a claim moves it elsewhere. If your consumer goroutine crashes between processing and ack, the next claim sweep picks the entry back up. The one caveat is retention: `XADD MAXLEN ~` and `XTRIM` can release the entry's *payload* even while its ID is still in the PEL. The next `XAUTOCLAIM` returns those IDs in its `deleted` list and removes them from the PEL inside the same command — the entry cannot be retried, so the caller should log it and route to a dead-letter store for audit. The example handles this explicitly in `Autoclaim` further down.\n\nThe trade-off is the opposite of pub/sub: a slow or crashed consumer doesn't lose messages, but it does mean your downstream system must be idempotent. If you process an order twice because the first attempt died after the side effect but before the ack, the second attempt must be safe."},{"id":"multiple-consumer-groups-one-stream","title":"Multiple consumer groups, one stream","role":"content","text":"The big difference between Redis Streams and a job queue is that any number of independent consumer groups can read the same stream. The demo sets up two groups on `demo:events:orders`:\n\n[code example]\n\nEach group has its own cursor. Producing 5 events results in `notifications` and `analytics` each receiving all 5, with no coordination between them. Within `notifications`, the work is split across `worker-a` and `worker-b`: Redis hands each `XREADGROUP` call whatever entries are not yet delivered to anyone in the group, so adding a second worker doubles throughput without any rebalance logic.\n\nThe `\"0-0\"` argument means \"deliver everything in the stream from the beginning\" — useful in a demo and for fresh groups bootstrapping from history. In production, a brand-new group reading a long-existing stream usually starts at `$` (\"only events after this point\") and uses [`XRANGE`](https://redis.io/docs/latest/commands/xrange) explicitly if it needs history."},{"id":"recovering-crashed-consumers-with-xautoclaim","title":"Recovering crashed consumers with XAUTOCLAIM","role":"content","text":"The demo's \"Crash next 3\" button tells a chosen consumer to drop its next three deliveries on the floor without acking them — the same effect as a worker process dying mid-message. Those entries stay in the group's PEL with their delivery counter incremented. Once they have been idle for at least `claimMinIdleMs`, any healthy consumer in the group can rescue them by calling `XAUTOCLAIM` *with itself as the target*. `ConsumerWorker.ReapIdlePel` wraps that pattern:\n\n[code example]\n\nThe underlying `stream.Autoclaim` helper pages through the group's PEL with `XAUTOCLAIM`'s continuation cursor:\n\n[code example]\n\nA single `XAUTOCLAIM` call scans up to `pageCount` PEL entries starting at `startID`, reassigns the ones idle for at least `claimMinIdleMs` to the named consumer, and returns a continuation cursor in the first slot of the reply. For a full sweep, loop until the cursor returns to `0-0` (with a `maxPages` safety net so one call cannot monopolise a very large PEL). The delivery counter is incremented on every claim — after a few cycles you can use it to spot a *poison-pill* message that crashes every consumer that touches it, and route it to a dead-letter stream so the bad entry stops cycling. (New entries keep flowing past the poison pill — `XREADGROUP >` still delivers fresh work — but the bad entry's repeated reclaim wastes consumer time and keeps the PEL larger than it needs to be.)\n\nThe `deleted` list contains PEL entry IDs whose stream payload was already trimmed by the time the claim ran (typically because `MAXLEN ~` retention outran a slow consumer). `XAUTOCLAIM` removes those dangling slots from the PEL itself, so the caller does *not* need to `XACK` them — but the entries cannot be retried either, so log and route them to a dead-letter store for offline inspection. Redis 7.0 introduced this third return element; the example requires Redis 7.0+ for that reason.\n\n`go-redis` v9's typed `client.XAutoClaim` wrapper discards the third (deleted-IDs) element of the reply, so the helper issues `XAUTOCLAIM` through `client.Do(...)` and parses the raw reply itself. If you don't need the deleted-IDs list, the typed wrapper is more ergonomic.\n\n`ReapIdlePel` is the right primitive for the recovery path because it claims and processes in one step: every entry the call returned is now in *this* consumer's PEL, so the same consumer is responsible for processing and acking it. In production each consumer goroutine runs `ReapIdlePel` periodically (every few seconds, on a timer) so a crashed peer's entries never sit invisibly. The demo exposes it as a manual button so you can trigger the reap after waiting for the idle threshold.\n\n`XCLAIM` (singular, no auto) does the same thing for a specific list of entry IDs you already have in hand — useful when you want to take ownership of one known stuck entry, or when you need to move a specific consumer's PEL to a peer (the case the demo's \"Remove consumer\" button handles via `HandoverPending`). `XAUTOCLAIM` cannot filter by source consumer, so it cannot be used for a per-consumer handover."},{"id":"replay-with-xrange","title":"Replay with XRANGE","role":"content","text":"`XRANGE` reads a slice of history. It is completely independent of any consumer group — no cursors move, no acks happen — so it is safe to call any number of times, from any process:\n\n[code example]\n\nThe special IDs `-` and `+` mean \"from the very beginning\" and \"to the very end\". You can also pass real IDs (`1716998413541-0`) or just the millisecond part (`1716998413541`, which Redis interprets as \"any entry with this timestamp\").\n\nTypical uses:\n\n* **Bootstrapping a new projection** — read the entire stream from `-` and build a derived view in another store (a search index, a SQL table, a different cache). Doing this against a consumer group would consume the entries; `XRANGE` lets you do it without disrupting live consumers.\n* **Auditing recent activity** — read the last few minutes by ID range without touching any group cursor.\n* **Debugging** — fetch one specific entry by its ID, or a tight range around an incident timestamp, to see exactly what producers wrote."},{"id":"the-consumer-worker-goroutine","title":"The consumer worker goroutine","role":"content","text":"`ConsumerWorker` wraps the `XREADGROUP` → process → `XACK` loop in a daemon goroutine\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/streaming/go/consumer_worker.go)):\n\n[code example]\n\n`handleEntry` either acks (the normal path) or, when the demo has asked the worker to \"crash\", drops the entry on the floor and increments a counter so the UI can show what is currently in the PEL waiting to be claimed.\n\nRecovery of stuck PEL entries — this consumer's, after a restart, or another consumer's, after a crash — runs through a separate `ReapIdlePel` method rather than the read loop. That method calls `XAUTOCLAIM` with this consumer as the target, then processes whatever was claimed in the same flow as new entries. This is the textbook Streams pattern: each consumer is its own reaper, running `XAUTOCLAIM(self)` periodically (or on demand) so a crashed peer's entries never sit invisibly in the PEL. The demo's \"XAUTOCLAIM to selected\" button calls `ReapIdlePel` on the chosen consumer; in production you would run it from a timer every few seconds.\n\nNote that the worker's main read loop deliberately does *not* call `XREADGROUP 0` to drain its own PEL on every iteration. That would re-deliver every pending entry continuously and *reset its idle counter to zero* each time, which would keep crashed entries below the `XAUTOCLAIM` threshold forever. Using `XAUTOCLAIM(self)` as the recovery primitive — which only fires for entries idle longer than `claimMinIdleMs` — avoids that whole class of bug.\n\nThe pause and crash levers exist only for the demo. A real consumer is just the read-process-ack loop — everything else in this type is instrumentation.\n\nA per-entry failure (typically `XACK` against Redis) must not kill the goroutine — that would silently halt this consumer while every other entry sat in its PEL waiting for `XAUTOCLAIM`. The dispatch wrapper logs the failure and continues; the entry stays unacked and the next `ReapIdlePel` call recovers it once it exceeds the idle threshold."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* Redis 7.0 or later. `XAUTOCLAIM` was added in Redis 6.2, but its reply gained a third\n  element (the list of deleted IDs) in 7.0; the example relies on that shape.\n* Go 1.21 or later.\n* The `go-redis` client. The included `go.mod` pins:\n\n  [code example]\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of five files. Download them from the [`go` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/streaming/go) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"The helper, consumer worker, and demo HTTP handlers all live in `package streaming`. Go's `package main` can't live in the same directory as another package, so create a tiny `main.go` shim in a subdirectory that calls into the package:\n\n[code example]\n\nThen build and run:\n\n[code example]\n\nYou should see:\n\n[code example]\n\nBy default the demo wipes the configured stream key on startup so each run starts from a clean state. Pass `--no-reset` to keep any existing data at the key (useful when re-running against the same stream to inspect prior state), or `--stream-key <name>` to point the demo at a different key entirely.\n\nOpen [http://127.0.0.1:8083](http://127.0.0.1:8083) in a browser. You can:\n\n* **Produce** any number of events of a chosen type (or random types). Watch the stream length grow and the tail update.\n* See each **consumer group**: its `last-delivered-id`, the size of its pending list, and the consumers in it. Each consumer shows its processed count, pending count, and idle time.\n* **Add or remove** consumers within a group at runtime to see Redis split the work across the new shape.\n* Click **Crash next 3** on a consumer to drop its next three deliveries — the same effect as a worker process dying after `XREADGROUP` but before `XACK`. Watch the **Pending entries (XPENDING)** panel fill up.\n* Wait until the idle time exceeds the threshold (default 5000 ms), pick a healthy target consumer, and click **XAUTOCLAIM to selected** — the stuck entries are reassigned and the delivery counter increments.\n* **Replay (XRANGE)** any range to confirm the full history is independent of consumer-group state.\n* **XTRIM** with an approximate `MAXLEN` to bound retention. Note that an approximate trim only releases whole macro-nodes — `MAXLEN ~ 50` on a small stream may not delete anything; on a 300-entry stream it typically lands at around 100.\n* Click **Reset demo** to drop the stream and re-seed the default groups."},{"id":"production-usage","title":"Production usage","role":"content","text":""},{"id":"pick-retention-by-length-or-by-minimum-id","title":"Pick retention by length or by minimum ID","role":"content","text":"The demo uses `MAXLEN ~` on every `XADD`. Two alternatives are worth considering:\n\n* `MINID ~ <id>` — keep only entries newer than an ID. If you want \"the last 24 hours\", compute the wall-clock cutoff and pass `XTRIM MINID ~ <ms>-0` (the helper exposes this as `TrimMinid`). This is the right pattern when retention is time-bounded.\n* No cap on `XADD` plus a periodic `XTRIM` job — useful if your producer is hot and the per-`XADD` work has to stay minimal, or if retention rules are complex (a separate process can also factor in consumer-group lag).\n\nIn all three cases the trimming is approximate by default. Use exact trimming (`XTrimMaxLen` / `XTrimMinID` instead of the `Approx` variants) only when you genuinely need an exact count."},{"id":"dont-let-consumer-group-lag-silently-grow","title":"Don't let consumer-group lag silently grow","role":"content","text":"`XINFO GROUPS` reports each group's `lag` (entries the group has not yet read) and `pending` (entries delivered but not acked). In production, alert on either of these crossing a threshold — a steadily growing pending count usually means consumers are crashing without `XAUTOCLAIM` running, and a growing lag means consumers can't keep up with producers.\n\nThe same applies inside a group: `XINFO CONSUMERS` reports per-consumer pending counts and idle times, so you can spot one slow consumer holding entries that the rest of the group is waiting on."},{"id":"make-consumer-logic-idempotent","title":"Make consumer logic idempotent","role":"content","text":"`XAUTOCLAIM` can re-deliver an entry to a different consumer after a crash. If your processing has side effects (sending email, charging a card, updating a downstream store), make sure the same entry processed twice gives the same result — use an idempotency key, an upsert with conditional check, or a once-per-id guard table. Redis Streams cannot give you exactly-once semantics on its own."},{"id":"bound-the-delivery-counter-as-a-poison-pill-signal","title":"Bound the delivery counter as a poison-pill signal","role":"content","text":"`XPENDING` returns each entry's delivery count, incremented on every claim. If an entry has been delivered (and dropped) several times, the next consumer is unlikely to fare better. After some threshold — `deliveries >= 5`, say — route the entry to a *dead-letter stream*, ack it on the original group, and alert. New entries keep flowing past a poison pill (`XREADGROUP >` still delivers fresh work), but the bad entry's repeated reclaim wastes consumer time and keeps the PEL bigger than it needs to be — without a DLQ threshold it can also slowly trip retention/lag alerts."},{"id":"partition-by-tenant-or-entity-for-scale","title":"Partition by tenant or entity for scale","role":"content","text":"A single Redis Stream is a single key, and on a Redis Cluster a single key lives on a single shard. If your throughput exceeds what one shard can handle, partition the stream — for example by tenant ID (`events:orders:{tenant_a}`, `events:orders:{tenant_b}`) — so different tenants land on different shards. Hash-tags (`{tenant_a}`) keep all related streams on the same shard if you need to multi-stream atomically.\n\nPer-entity partitioning (`events:order:{order_id}`) is the canonical pattern when you treat each entity's stream as the event-sourcing log for that entity: every state change for one order goes on its own stream, which is also bounded in size by the entity's lifetime."},{"id":"use-a-separate-consumer-pool-per-group","title":"Use a separate consumer pool per group","role":"content","text":"The demo runs every consumer in one process. In production each consumer group is usually its own deployment — its own pool of pods or VMs — so a slow projection in `analytics` cannot pull `notifications` workers off their stream. Each pod runs one consumer goroutine per CPU core, with `XAUTOCLAIM` either embedded in the consumer loop (every N reads, claim idle entries to self) or run by a separate reaper."},{"id":"dont-read-with-xread-no-group-and-then-try-to-ack","title":"Don't read with XREAD (no group) and then try to ack","role":"content","text":"`XREAD` and `XREADGROUP` are different mechanisms. `XREAD` is a tail-the-log read with no consumer-group state — entries are not added to any PEL, and you cannot `XACK` them. If you want at-least-once delivery and crash recovery, you must read through a consumer group.\n\n`XREAD` is still useful for read-only tail clients (a UI streaming events, a debugger, a `tail -f`-style command-line tool). It's just not part of the at-least-once path."},{"id":"wire-shutdown-through-contextcontext","title":"Wire shutdown through `context.Context`","role":"content","text":"Each consumer goroutine receives a `context.Context` that the worker's `Stop()` cancels. The demo's `RunDemoServer` traps `SIGINT`/`SIGTERM`, shuts the HTTP server down with a 5-second deadline, then stops every consumer goroutine. Wire your real service's `SIGTERM` handler to the same `context.CancelFunc` so an in-flight `XREADGROUP` block unblocks promptly and any half-processed entry stays in the PEL for the next reaper to recover."},{"id":"go-redis-xautoclaim-does-not-surface-deleted-ids","title":"go-redis XAutoClaim does not surface deleted IDs","role":"content","text":"In `go-redis` v9, the typed `client.XAutoClaim(ctx, ...)` wrapper reads the deleted-IDs slot of the reply and discards it before returning to the caller. The same is true of `XAutoClaimJustID`. The helper in this guide uses `client.Do(ctx, \"XAUTOCLAIM\", ...)` and parses the raw reply itself so the deleted-IDs list is preserved. If you don't need the deleted-IDs list (for example, because you don't trim aggressively and your `MAXLEN ~` cap is comfortably larger than any consumer's worst-case PEL size), the typed wrapper is more ergonomic."},{"id":"inspect-the-stream-directly-with-redis-cli","title":"Inspect the stream directly with redis-cli","role":"content","text":"When testing or troubleshooting, inspect the stream directly to confirm the consumer state is what you expect:\n\n[code example]\n\nIf a group's `lag` is growing while consumers' `idle` times are short, consumers are healthy but producers are outpacing them — add more consumers. If `pending` is growing while `lag` is small, consumers are *receiving* entries but not *acking* them — either they are crashing mid-message or your acking logic has a bug."},{"id":"learn-more","title":"Learn more","role":"related","text":"This example uses the following Redis commands:\n\n* [`XADD`](https://redis.io/docs/latest/commands/xadd) to append an event with an approximate `MAXLEN` cap.\n* [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) to read new entries for a consumer in a group.\n* [`XACK`](https://redis.io/docs/latest/commands/xack) to acknowledge a processed entry.\n* [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) to reassign idle pending entries to a healthy consumer.\n* [`XCLAIM`](https://redis.io/docs/latest/commands/xclaim) to take ownership of a specific list of pending entry IDs by hand (used by `HandoverPending` to move a leaving consumer's PEL to a peer, since `XAUTOCLAIM` has no source-consumer filter).\n* [`XRANGE`](https://redis.io/docs/latest/commands/xrange) for replay and audit, independent of consumer-group state.\n* [`XPENDING`](https://redis.io/docs/latest/commands/xpending) to inspect the per-group pending list with idle times and delivery counts.\n* [`XTRIM`](https://redis.io/docs/latest/commands/xtrim) for explicit retention enforcement.\n* [`XGROUP CREATE`](https://redis.io/docs/latest/commands/xgroup-create) and\n  [`XGROUP DELCONSUMER`](https://redis.io/docs/latest/commands/xgroup-delconsumer) to manage groups and consumers.\n* [`XINFO STREAM`](https://redis.io/docs/latest/commands/xinfo-stream),\n  [`XINFO GROUPS`](https://redis.io/docs/latest/commands/xinfo-groups), and\n  [`XINFO CONSUMERS`](https://redis.io/docs/latest/commands/xinfo-consumers) for observability.\n\nSee the [`go-redis` documentation](https://redis.io/docs/latest/develop/clients/go) for the full client reference, and the [Streams overview](https://redis.io/docs/latest/develop/data-types/streams) for the deeper conceptual model — consumer groups, the PEL, claim semantics, capped streams, and the differences with Kafka partitions."}],"examples":[{"id":"the-event-stream-helper-ex0","language":"go","code":"package main\n\nimport (\n    \"context\"\n\n    \"github.com/redis/go-redis/v9\"\n    \"streaming\"\n)\n\nfunc main() {\n    client := redis.NewClient(&redis.Options{Addr: \"localhost:6379\"})\n    stream := streaming.NewEventStream(\n        client,\n        \"demo:events:orders\",\n        2000, // approximate MAXLEN retention guardrail\n        5000, // XAUTOCLAIM idle threshold, in ms\n    )\n\n    ctx := context.Background()\n\n    // Producer\n    streamID, _ := stream.Produce(ctx, \"order.placed\", map[string]string{\n        \"order_id\": \"o-1234\", \"customer\": \"alice\", \"amount\": \"49.50\",\n    })\n    _ = streamID\n\n    // Consumer group + one consumer\n    _ = stream.EnsureGroup(ctx, \"notifications\", \"0-0\")\n    entries, _ := stream.Consume(ctx, \"notifications\", \"worker-a\", 10, 500)\n    for _, e := range entries {\n        handle(e.Fields)                                       // your processing\n        _, _ = stream.Ack(ctx, \"notifications\", []string{e.ID}) // XACK\n    }\n\n    // Recover stuck PEL entries by reaping them into a healthy consumer.\n    // The textbook pattern: each consumer periodically calls XAUTOCLAIM\n    // with itself as the target and processes whatever it claimed.\n    // `ConsumerWorker.ReapIdlePel` wraps that flow; the low-level helper\n    // `stream.Autoclaim(group, targetName, ...)` is also available if\n    // you want to drive XAUTOCLAIM directly.\n    result, _ := workerB.ReapIdlePel(ctx)\n    // result == streaming.ReapResult{Claimed: N, Processed: M, DeletedIDs: [...]}\n    // DeletedIDs are PEL entries whose payload was already trimmed.\n    // Redis 7+ has already removed those slots from the PEL, so no XACK\n    // is needed — log them and route to a dead-letter store for audit.\n\n    // Replay history (independent of any group's cursor)\n    history, _ := stream.Replay(ctx, \"-\", \"+\", 50)\n    for _, e := range history {\n        _ = e\n    }\n}","section_id":"the-event-stream-helper"},{"id":"data-model-ex0","language":"text","code":"demo:events:orders\n  1716998413541-0   type=order.placed     order_id=o-1234   customer=alice  amount=49.50  ts_ms=...\n  1716998413542-0   type=order.paid       order_id=o-1234   customer=alice  amount=49.50  ts_ms=...\n  1716998413542-1   type=order.shipped    order_id=o-1235   customer=bob    amount=12.00  ts_ms=...\n  ...","section_id":"data-model"},{"id":"producing-events-ex0","language":"go","code":"func (s *EventStream) ProduceBatch(ctx context.Context, events []ProducerEvent) ([]string, error) {\n    pipe := s.client.Pipeline()\n    cmds := make([]*redis.StringCmd, 0, len(events))\n    for _, ev := range events {\n        fields := encodeFields(ev.Type, ev.Payload)\n        cmd := pipe.XAdd(ctx, &redis.XAddArgs{\n            Stream: s.streamKey,\n            MaxLen: s.maxlenApprox,\n            Approx: true,\n            Values: fields,\n        })\n        cmds = append(cmds, cmd)\n    }\n    if _, err := pipe.Exec(ctx); err != nil {\n        return nil, err\n    }\n    // ...collect ids from cmds and update stats...\n}","section_id":"producing-events"},{"id":"reading-with-a-consumer-group-ex0","language":"go","code":"func (s *EventStream) Consume(ctx context.Context, group, consumer string, count int64, blockMs int64) ([]Entry, error) {\n    res, err := s.client.XReadGroup(ctx, &redis.XReadGroupArgs{\n        Group:    group,\n        Consumer: consumer,\n        Streams:  []string{s.streamKey, \">\"},\n        Count:    count,\n        Block:    time.Duration(blockMs) * time.Millisecond,\n    }).Result()\n    if err != nil {\n        if errors.Is(err, redis.Nil) {\n            return nil, nil\n        }\n        return nil, err\n    }\n    return flattenStreams(res), nil\n}","section_id":"reading-with-a-consumer-group"},{"id":"acknowledging-entries-ex0","language":"go","code":"func (s *EventStream) Ack(ctx context.Context, group string, ids []string) (int64, error) {\n    if len(ids) == 0 {\n        return 0, nil\n    }\n    return s.client.XAck(ctx, s.streamKey, group, ids...).Result()\n}","section_id":"acknowledging-entries"},{"id":"multiple-consumer-groups-one-stream-ex0","language":"go","code":"_ = stream.EnsureGroup(ctx, \"notifications\", \"0-0\")\n_ = stream.EnsureGroup(ctx, \"analytics\",     \"0-0\")","section_id":"multiple-consumer-groups-one-stream"},{"id":"recovering-crashed-consumers-with-xautoclaim-ex0","language":"go","code":"func (w *ConsumerWorker) ReapIdlePel(ctx context.Context) (ReapResult, error) {\n    claimed, deleted, err := w.stream.Autoclaim(ctx, w.group, w.name, 100, \"0-0\", 10)\n    if err != nil {\n        return ReapResult{}, err\n    }\n    processed := 0\n    for _, entry := range claimed {\n        if perErr := w.handleEntry(ctx, entry.ID, entry.Fields); perErr != nil {\n            log.Printf(\"[%s/%s] reap failed on %s: %v\", w.group, w.name, entry.ID, perErr)\n            continue\n        }\n        processed++\n    }\n    // ...update reaped counter and return...\n    return ReapResult{\n        Claimed:    len(claimed),\n        Processed:  processed,\n        DeletedIDs: deleted,\n    }, nil\n}","section_id":"recovering-crashed-consumers-with-xautoclaim"},{"id":"recovering-crashed-consumers-with-xautoclaim-ex1","language":"go","code":"func (s *EventStream) Autoclaim(\n    ctx context.Context, group, consumer string,\n    pageCount int64, startID string, maxPages int,\n) ([]Entry, []string, error) {\n    cursor := startID\n    claimedAll, deletedAll := []Entry{}, []string{}\n    for i := 0; i < maxPages; i++ {\n        nextCursor, claimed, deleted, err := s.doAutoclaim(ctx, group, consumer, cursor, pageCount)\n        if err != nil {\n            return nil, nil, err\n        }\n        claimedAll = append(claimedAll, claimed...)\n        deletedAll = append(deletedAll, deleted...)\n        if nextCursor == \"0-0\" {\n            break\n        }\n        cursor = nextCursor\n    }\n    return claimedAll, deletedAll, nil\n}","section_id":"recovering-crashed-consumers-with-xautoclaim"},{"id":"replay-with-xrange-ex0","language":"go","code":"func (s *EventStream) Replay(ctx context.Context, startID, endID string, count int64) ([]Entry, error) {\n    msgs, err := s.client.XRangeN(ctx, s.streamKey, startID, endID, count).Result()\n    if err != nil {\n        if errors.Is(err, redis.Nil) {\n            return nil, nil\n        }\n        return nil, err\n    }\n    return xMessagesToEntries(msgs), nil\n}","section_id":"replay-with-xrange"},{"id":"the-consumer-worker-goroutine-ex0","language":"go","code":"func (w *ConsumerWorker) run(ctx context.Context, done chan struct{}) {\n    defer close(done)\n    for {\n        if ctx.Err() != nil {\n            return\n        }\n        // ...park here if paused...\n        entries, err := w.stream.Consume(ctx, w.group, w.name, 10, 500)\n        if err != nil {\n            if ctx.Err() != nil {\n                return\n            }\n            log.Printf(\"[%s/%s] read failed: %v\", w.group, w.name, err)\n            // ...short back-off...\n            continue\n        }\n        for _, entry := range entries {\n            if ctx.Err() != nil {\n                return\n            }\n            w.dispatch(ctx, entry.ID, entry.Fields)\n        }\n    }\n}","section_id":"the-consumer-worker-goroutine"},{"id":"prerequisites-ex0","language":"text","code":"require github.com/redis/go-redis/v9 v9.18.0","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir streaming-demo && cd streaming-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/streaming/go\ncurl -O $BASE/event_stream.go\ncurl -O $BASE/consumer_worker.go\ncurl -O $BASE/demo_server.go\ncurl -O $BASE/go.mod\ncurl -O $BASE/go.sum","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"mkdir -p cmd/demo\ncat > cmd/demo/main.go <<'EOF'\npackage main\n\nimport \"streaming\"\n\nfunc main() { streaming.RunDemoServer() }\nEOF","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"bash","code":"go mod tidy\ngo run ./cmd/demo","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex2","language":"text","code":"Deleting any existing data at key 'demo:events:orders' for a clean demo run (pass --no-reset to keep it).\nRedis streaming demo server listening on http://127.0.0.1:8083\nUsing Redis at localhost:6379 with stream key 'demo:events:orders' (MAXLEN ~ 2000)\nSeeded 3 consumer(s) across 2 group(s)","section_id":"start-the-demo-server"},{"id":"inspect-the-stream-directly-with-redis-cli-ex0","language":"bash","code":"# Stream summary\nredis-cli XLEN demo:events:orders\nredis-cli XINFO STREAM demo:events:orders\n\n# Group cursors and pending counts\nredis-cli XINFO GROUPS demo:events:orders\n\n# Consumers within a group\nredis-cli XINFO CONSUMERS demo:events:orders notifications\n\n# Pending entries with idle time and delivery count\nredis-cli XPENDING demo:events:orders notifications - + 20\n\n# Tail the stream live (no consumer-group state — like tail -f)\nredis-cli XREAD BLOCK 0 STREAMS demo:events:orders '$'\n\n# Replay a range\nredis-cli XRANGE demo:events:orders - + COUNT 50","section_id":"inspect-the-stream-directly-with-redis-cli"}]}
{"schema_version":2,"id":"develop/use-cases/streaming","title":"Redis streaming","url":"https://redis.io/docs/latest/develop/use-cases/streaming/","summary":"Process ordered event streams with consumer groups, replay, and configurable retention.","tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:55:04+01:00","children":[{"id":"develop/use-cases/streaming/redis-py","summary":"Implement a Redis event-streaming pipeline in Python with redis-py","title":"Redis streaming with redis-py","url":"https://redis.io/docs/latest/develop/use-cases/streaming/redis-py/"},{"id":"develop/use-cases/streaming/nodejs","summary":"Implement a Redis event-streaming pipeline in Node.js with node-redis","title":"Redis streaming with node-redis","url":"https://redis.io/docs/latest/develop/use-cases/streaming/nodejs/"},{"id":"develop/use-cases/streaming/go","summary":"Implement a Redis event-streaming pipeline in Go with go-redis","title":"Redis streaming with go-redis","url":"https://redis.io/docs/latest/develop/use-cases/streaming/go/"},{"id":"develop/use-cases/streaming/java-jedis","summary":"Implement a Redis event-streaming pipeline in Java with Jedis","title":"Redis streaming with Jedis","url":"https://redis.io/docs/latest/develop/use-cases/streaming/java-jedis/"},{"id":"develop/use-cases/streaming/java-lettuce","summary":"Implement a Redis event-streaming pipeline in Java with Lettuce","title":"Redis streaming with Lettuce","url":"https://redis.io/docs/latest/develop/use-cases/streaming/java-lettuce/"},{"id":"develop/use-cases/streaming/dotnet","summary":"Implement a Redis event-streaming pipeline in C# with StackExchange.Redis","title":"Redis streaming with StackExchange.Redis","url":"https://redis.io/docs/latest/develop/use-cases/streaming/dotnet/"},{"id":"develop/use-cases/streaming/php","summary":"Implement a Redis event-streaming pipeline in PHP with Predis","title":"Redis streaming with Predis","url":"https://redis.io/docs/latest/develop/use-cases/streaming/php/"},{"id":"develop/use-cases/streaming/ruby","summary":"Implement a Redis event-streaming pipeline in Ruby with redis-rb","title":"Redis streaming with redis-rb","url":"https://redis.io/docs/latest/develop/use-cases/streaming/ruby/"},{"id":"develop/use-cases/streaming/rust","summary":"Implement a Redis event-streaming pipeline in Rust with redis-rs","title":"Redis streaming with redis-rs","url":"https://redis.io/docs/latest/develop/use-cases/streaming/rust/"}],"page_type":"content","content_hash":"c1d0c871b20e72a4c5ad25bcc154b3c2fb91720aac6fa0905ebaa29665428b6b","sections":[{"id":"when-to-use-redis-streaming","title":"When to use Redis streaming","role":"overview","text":"Use Redis streaming when you need to process and deliver ordered event streams — user actions, telemetry, transactions, inter-service messages — with consumer groups, replay, and configurable retention, without standing up a dedicated streaming platform."},{"id":"why-the-problem-is-hard","title":"Why the problem is hard","role":"content","text":"Continuous event flows pushed through primary databases or ad-hoc queues add latency on the request path, make backpressure hard to control, and tightly couple producers to consumers. Some of the obvious workarounds have real drawbacks:\n\n-   **A dedicated streaming platform** (Kafka, Pulsar) solves all of this but adds significant\n    operational overhead — separate clusters, partition management, consumer rebalancing — that's\n    disproportionate when retention windows are hours or days, not months.\n-   **Pub/sub** ([Redis Pub/Sub](https://redis.io/docs/latest/develop/pubsub), MQTT) is fire-and-forget\n    transport: messages are delivered to whoever is connected and discarded, with no persistence,\n    replay, or consumer tracking.\n-   **Polling a primary database for new rows** generates constant load on the system of record,\n    struggles to order events from concurrent writers, and offers no replay or per-consumer cursor.\n\nA workable streaming layer needs an ordered, durable log, independent consumer tracking with\nacknowledgment, at-least-once delivery, and retention controls — all without introducing a\nseparate broker for moderate-scale workloads.\n\nThis pattern is distinct from [pub/sub](https://redis.io/docs/latest/develop/use-cases/pub-sub), which is\nat-most-once transport with no history: a subscriber that's offline when a message is published\nmisses it for good. It is also distinct from a\n[job queue](https://redis.io/docs/latest/develop/use-cases/job-queue), where each task is claimed by exactly\none worker and discarded after it completes. Streaming retains the ordered history, so many\nindependent consumer groups can read the same events at their own pace and replay from any point."},{"id":"what-you-can-expect-from-a-redis-solution","title":"What you can expect from a Redis solution","role":"content","text":"You can:\n\n-   Deliver ordered events to multiple independent consumer groups, each processing the full\n    stream at its own pace.\n-   Scale consumers horizontally within a group to share work across workers, with at-least-once\n    delivery and per-consumer tracking.\n-   Replay historical events for debugging, bootstrapping a new projection, or rebuilding a\n    downstream system from scratch.\n-   Bound memory by retaining events by length or by minimum ID, without a separate cleanup job.\n-   Recover unacknowledged entries from crashed consumers, so a worker dying mid-message does not\n    silently lose work (entries trimmed by `MAXLEN ~` before they are acked are surfaced in\n    `XAUTOCLAIM`'s deleted-IDs list, so the caller can route them to a dead-letter store rather\n    than retry against a missing payload).\n-   Partition streams by tenant, region, or entity for load distribution and per-entity event\n    sourcing.\n-   Replace a dedicated Kafka deployment for moderate-scale, short-retention streaming workloads\n    using infrastructure you already run."},{"id":"how-redis-supports-the-solution","title":"How Redis supports the solution","role":"content","text":"In practice, producers append events to a stream with\n[`XADD`](https://redis.io/docs/latest/commands/xadd) and Redis assigns each entry an auto-generated,\ntime-ordered ID. Consumers either read the stream directly with\n[`XREAD`](https://redis.io/docs/latest/commands/xread), or join a *consumer group* and read with\n[`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup). Each consumer gets its own\npending-entries list of in-flight messages, while the group as a whole tracks a single\n`last-delivered-id` cursor that advances as entries are handed out to any consumer. Once a consumer finishes processing an\nentry, it acknowledges it with [`XACK`](https://redis.io/docs/latest/commands/xack); entries left\nunacknowledged past a timeout can be reassigned to a healthy consumer with\n[`XCLAIM`](https://redis.io/docs/latest/commands/xclaim) or\n[`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim).\n\nRedis provides the following features that make it a good fit for streaming:\n\n-   [Streams](https://redis.io/docs/latest/develop/data-types/streams)\n    ([`XADD`](https://redis.io/docs/latest/commands/xadd),\n    [`XLEN`](https://redis.io/docs/latest/commands/xlen)) provide an append-only log with auto-generated\n    time-ordered IDs, so ordering is intrinsic to the data structure rather than something the\n    application has to maintain.\n-   [Consumer groups](https://redis.io/docs/latest/develop/data-types/streams#consumer-groups)\n    ([`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup),\n    [`XACK`](https://redis.io/docs/latest/commands/xack)) give at-least-once delivery with per-consumer\n    cursors and acknowledgment, so workers in a group share the stream's work and multiple groups\n    read the same stream independently.\n-   [`XRANGE`](https://redis.io/docs/latest/commands/xrange) and\n    [`XREVRANGE`](https://redis.io/docs/latest/commands/xrevrange) support replay and range queries —\n    bootstrap a new projection from the start of the stream, audit recent events, or run\n    point-in-time reads by ID range.\n-   [`XPENDING`](https://redis.io/docs/latest/commands/xpending),\n    [`XCLAIM`](https://redis.io/docs/latest/commands/xclaim), and\n    [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) recover messages a crashed consumer\n    left in flight, so no event sits invisibly past its processing window.\n-   Retention controls — [`XADD ... MAXLEN ~ n`](https://redis.io/docs/latest/commands/xadd) and\n    [`XTRIM MINID ~ id`](https://redis.io/docs/latest/commands/xtrim) — bound stream size by length or by\n    oldest event, so memory stays bounded as the stream rolls forward.\n-   Sub-millisecond reads and writes from memory, so streaming runs on the same Redis instance\n    already handling cache, sessions, or rate limiting at zero marginal cost."},{"id":"ecosystem","title":"Ecosystem","role":"content","text":"The following libraries and frameworks use Redis Streams for event-driven workloads:\n\n-   **Java**:\n    [Spring Data Redis Streams](https://docs.spring.io/spring-data/redis/reference/redis/redis-streams.html)\n    for consumer-group processing with producer/consumer abstractions and pending-entries handling.\n-   **Node.js**: [`node-redis`](https://github.com/redis/node-redis) and\n    [`ioredis`](https://github.com/redis/ioredis) for stream producers and consumers in\n    event-driven APIs.\n-   **Python**: [`redis-py`](https://redis.readthedocs.io/) with\n    [FastAPI](https://redis.io/docs/latest/integrate/fastapi) or [Django](https://www.djangoproject.com/) for\n    microservice event pipelines.\n-   **Infrastructure**:\n    [Active-Active geo-distribution](https://redis.io/docs/latest/operate/rs/databases/active-active) on\n    Redis Enterprise / Redis Cloud for cross-region stream replication;\n    [Azure Managed Redis](https://azure.microsoft.com/en-us/products/managed-redis) with\n    [Azure Functions](https://azure.microsoft.com/en-us/products/functions) for serverless event\n    backbones."},{"id":"code-examples-to-build-your-own-redis-streaming-pipeline","title":"Code examples to build your own Redis streaming pipeline","role":"example","text":"The following guides show how to build a simple Redis-backed event stream with producers and\nconsumer groups. Each guide includes a runnable interactive demo that lets you produce events,\nscale consumers within a group, replay history from any point, and watch independent groups\nread the same stream at their own pace.\n\n* [redis-py (Python)](https://redis.io/docs/latest/develop/use-cases/streaming/redis-py)\n* [node-redis (Node.js)](https://redis.io/docs/latest/develop/use-cases/streaming/nodejs)\n* [go-redis (Go)](https://redis.io/docs/latest/develop/use-cases/streaming/go)\n* [Jedis (Java)](https://redis.io/docs/latest/develop/use-cases/streaming/java-jedis)\n* [Lettuce (Java)](https://redis.io/docs/latest/develop/use-cases/streaming/java-lettuce)\n* [StackExchange.Redis (C#)](https://redis.io/docs/latest/develop/use-cases/streaming/dotnet)\n* [Predis (PHP)](https://redis.io/docs/latest/develop/use-cases/streaming/php)\n* [redis-rb (Ruby)](https://redis.io/docs/latest/develop/use-cases/streaming/ruby)\n* [redis-rs (Rust)](https://redis.io/docs/latest/develop/use-cases/streaming/rust)"}],"examples":[]}
{"schema_version":2,"id":"develop/use-cases/streaming/java-jedis","title":"Redis streaming with Jedis","url":"https://redis.io/docs/latest/develop/use-cases/streaming/java-jedis/","summary":"Implement a Redis event-streaming pipeline in Java with Jedis","aliases":["/develop/use-cases/streaming/java","/develop/use-cases/streaming/jedis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:55:04+01:00","children":[],"page_type":"content","content_hash":"cdafb7454cc5c86e3276bebf0d47685a0344759aa9c77dd905e2ee83b26b3e3a","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a Redis-backed event-streaming pipeline in Java with the [Jedis](https://redis.io/docs/latest/develop/clients/jedis) client library. It includes a small local web server built on the JDK's `com.sun.net.httpserver` so you can produce events into a single Redis Stream, watch two independent consumer groups read it at their own pace, and recover stuck deliveries with `XAUTOCLAIM` after simulating a consumer crash."},{"id":"overview","title":"Overview","role":"overview","text":"A Redis Stream is an append-only log of field/value entries with auto-generated, time-ordered IDs. Producers append with [`XADD`](https://redis.io/docs/latest/commands/xadd); consumers belong to *consumer groups* and read with [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup). The group as a whole tracks a single `last-delivered-id` cursor, and each consumer gets its own pending-entries list (PEL) of messages it has been handed but not yet acknowledged. Once a consumer has processed an entry it calls [`XACK`](https://redis.io/docs/latest/commands/xack) to clear the entry from its PEL; entries left unacknowledged past an idle threshold can be reassigned to a healthy consumer with [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim).\n\nThat gives you:\n\n* Ordered, durable history that many independent consumer groups can read at their own pace\n* At-least-once delivery, with per-consumer pending lists and automatic recovery of crashed consumers\n* Horizontal scaling within a group &mdash; add a consumer and Redis automatically splits the work\n* Replay of any range with [`XRANGE`](https://redis.io/docs/latest/commands/xrange), independent of consumer-group state\n* Bounded retention through [`XADD MAXLEN ~`](https://redis.io/docs/latest/commands/xadd) or\n  [`XTRIM MINID ~`](https://redis.io/docs/latest/commands/xtrim), without a separate cleanup job\n\nIn this example, producers append order events (`order.placed`, `order.paid`, `order.shipped`, `order.cancelled`) to a single stream at `demo:events:orders`. Two consumer groups read the same stream:\n\n* **`notifications`** &mdash; two consumers (`worker-a`, `worker-b`) sharing the work, modelling a fan-out worker pool.\n* **`analytics`** &mdash; one consumer (`worker-c`) processing the full event flow on its own."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. The application calls `stream.produce(eventType, payload)` which runs [`XADD`](https://redis.io/docs/latest/commands/xadd) with an approximate [`MAXLEN ~`](https://redis.io/docs/latest/commands/xadd) cap. Redis assigns an auto-generated time-ordered ID.\n2. Each consumer thread loops on [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) with the special ID `>` (meaning \"deliver entries this group has not yet delivered to anyone\") and a short block timeout.\n3. After processing each entry, the consumer calls [`XACK`](https://redis.io/docs/latest/commands/xack) so Redis can drop it from the group's pending list.\n4. If a consumer is killed (or crashes) before acking, its entries sit in the group's PEL. A periodic [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) sweep reassigns idle entries to a healthy consumer.\n5. Anyone &mdash; including code outside the consumer groups &mdash; can read history with [`XRANGE`](https://redis.io/docs/latest/commands/xrange) without affecting any group's cursor.\n\nEach consumer group has its own cursor (`last-delivered-id`) and its own pending list, so the two groups in this demo process the same events without coordinating with each other."},{"id":"the-event-stream-helper","title":"The event-stream helper","role":"content","text":"The `EventStream` class wraps the stream operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/streaming/java-jedis/EventStream.java)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each event is a single stream entry &mdash; a flat map of field/value strings &mdash; with an auto-generated time-ordered ID:\n\n[code example]\n\nThe ID is `{milliseconds}-{sequence}`, monotonically increasing within the stream, so you can range-query by approximate wall-clock time without an extra index. (IDs are ordered within a stream, not across streams &mdash; two events appended to different streams at the same millisecond can produce the same ID.) The implementation uses:\n\n* [`XADD ... MAXLEN ~ n`](https://redis.io/docs/latest/commands/xadd), pipelined, for batch production with a retention cap\n* [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) with the special ID `>` for fresh deliveries to a consumer\n* [`XACK`](https://redis.io/docs/latest/commands/xack) on every processed entry\n* [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) for sweeping idle pending entries to a healthy consumer\n* [`XRANGE`](https://redis.io/docs/latest/commands/xrange) for replay and audit\n* [`XPENDING`](https://redis.io/docs/latest/commands/xpending) for inspecting the per-group pending list\n* [`XINFO STREAM`](https://redis.io/docs/latest/commands/xinfo-stream),\n  [`XINFO GROUPS`](https://redis.io/docs/latest/commands/xinfo-groups), and\n  [`XINFO CONSUMERS`](https://redis.io/docs/latest/commands/xinfo-consumers) for surface-level observability\n* [`XTRIM`](https://redis.io/docs/latest/commands/xtrim) for explicit retention enforcement"},{"id":"producing-events","title":"Producing events","role":"content","text":"`produceBatch` pipelines `XADD` calls in a single round trip. Each call carries an approximate `MAXLEN ~` cap so the stream stays bounded as it rolls forward:\n\n[code example]\n\nThe `~` flavour of `MAXLEN` lets Redis trim at a macro-node boundary, which is much cheaper than exact trimming and is what you want when the cap is a retention *guardrail*, not a hard size constraint. With 300 events produced and `MAXLEN ~ 50`, you might end up with 100 entries left &mdash; Redis released the oldest whole macro-node and stopped. The next `XADD` will keep length stable.\n\nIf you genuinely need an exact cap (rare), drop the `.approximateTrimming()` call. The performance difference is significant on busy streams."},{"id":"reading-with-a-consumer-group","title":"Reading with a consumer group","role":"content","text":"Each consumer in a group runs the same `XREADGROUP` loop. The special ID `>` means \"deliver entries this group has not yet delivered to *anyone*\":\n\n[code example]\n\n`blockMs` makes the call efficient even when the stream is idle: the client parks on the server until either an entry arrives or the timeout expires, so consumers don't busy-loop. The Jedis instance is acquired from a `JedisPool` with try-with-resources, so the blocking call holds *its* connection for the duration of the block but other handlers and workers can grab their own connections in parallel.\n\nReading with an explicit ID like `0-0` instead of `>` does something different &mdash; it replays entries already delivered to *this* consumer name (its private PEL). That is the canonical recovery path when the same consumer restarts: catch up on its own pending entries first, then resume reading new ones (`consumeOwnPel` exposes that read)."},{"id":"acknowledging-entries","title":"Acknowledging entries","role":"content","text":"Once the consumer has processed an entry, `XACK` tells Redis it can drop the entry from the group's pending list:\n\n[code example]\n\nThis is the linchpin of at-least-once delivery: an entry that is never acked stays in the PEL until a claim moves it elsewhere. If your consumer thread crashes between processing and ack, the next claim sweep picks the entry back up. The one caveat is retention: `XADD MAXLEN ~` and `XTRIM` can release the entry's *payload* even while its ID is still in the PEL. The next `XAUTOCLAIM` returns those IDs in its `deletedIds` list and removes them from the PEL inside the same command &mdash; the entry cannot be retried, so the caller should log it and route to a dead-letter store for audit. The example handles this explicitly in `reapIdlePel` further down.\n\nThe trade-off is the opposite of pub/sub: a slow or crashed consumer doesn't lose messages, but it does mean your downstream system must be idempotent. If you process an order twice because the first attempt died after the side effect but before the ack, the second attempt must be safe."},{"id":"multiple-consumer-groups-one-stream","title":"Multiple consumer groups, one stream","role":"content","text":"The big difference between Redis Streams and a job queue is that any number of independent consumer groups can read the same stream. The demo sets up two groups on `demo:events:orders`:\n\n[code example]\n\nEach group has its own cursor. Producing 5 events results in `notifications` and `analytics` each receiving all 5, with no coordination between them. Within `notifications`, the work is split across `worker-a` and `worker-b`: Redis hands each `XREADGROUP` call whatever entries are not yet delivered to anyone in the group, so adding a second worker doubles throughput without any rebalance logic.\n\nThe `\"0-0\"` argument means \"deliver everything in the stream from the beginning\" &mdash; useful in a demo and for fresh groups bootstrapping from history. In production, a brand-new group reading a long-existing stream usually starts at `$` (\"only events after this point\") and uses [`XRANGE`](https://redis.io/docs/latest/commands/xrange) explicitly if it needs history."},{"id":"recovering-crashed-consumers-with-xautoclaim","title":"Recovering crashed consumers with XAUTOCLAIM","role":"content","text":"The demo's \"Crash next 3\" button tells a chosen consumer to drop its next three deliveries on the floor without acking them &mdash; the same effect as a worker process dying mid-message. Those entries stay in the group's PEL with their delivery counter incremented. Once they have been idle for at least `claimMinIdleMs`, any healthy consumer in the group can rescue them by calling `XAUTOCLAIM` *with itself as the target*. `ConsumerWorker.reapIdlePel` wraps that pattern:\n\n[code example]\n\nThe underlying `stream.autoclaim` helper pages through the group's PEL with `XAUTOCLAIM`'s continuation cursor:\n\n[code example]\n\nA single `XAUTOCLAIM` call scans up to `pageCount` PEL entries starting at `startId`, reassigns the ones idle for at least `minIdleTime` to the named consumer, and returns a continuation cursor in the first slot of the reply. For a full sweep, loop until the cursor returns to `0-0` (with a `maxPages` safety net so one call cannot monopolise a very large PEL). The delivery counter is incremented on every claim &mdash; after a few cycles you can use it to spot a *poison-pill* message that crashes every consumer that touches it, and route it to a dead-letter stream so the bad entry stops cycling. (New entries keep flowing past the poison pill &mdash; `XREADGROUP >` still delivers fresh work &mdash; but the bad entry's repeated reclaim wastes consumer time and keeps the PEL larger than it needs to be.)\n\nThe `deletedIds` list contains PEL entry IDs whose stream payload was already trimmed by the time the claim ran (typically because `MAXLEN ~` retention outran a slow consumer). `XAUTOCLAIM` removes those dangling slots from the PEL itself, so the caller does *not* need to `XACK` them &mdash; but the entries cannot be retried either, so log and route them to a dead-letter store for offline inspection. Redis 7.0 introduced this third return element; the example requires Redis 7.0+ for that reason.\n\nJedis 6.x's typed `jedis.xautoclaim(...)` overload returns only the cursor and the claimed entries (as `Map.Entry<StreamEntryID, List<StreamEntry>>`) &mdash; the deleted-IDs slot is dropped. To surface it, the helper sends `XAUTOCLAIM` through `Jedis.sendCommand` and parses all three reply elements by hand.\n\n`reapIdlePel` is the right primitive for the recovery path because it claims and processes in one step: every entry the call returned is now in *this* consumer's PEL, so the same consumer is responsible for processing and acking it. In production each consumer thread runs `reapIdlePel` periodically (every few seconds, on a timer) so a crashed peer's entries never sit invisibly. The demo exposes it as a manual button so you can trigger the reap after waiting for the idle threshold.\n\n`XCLAIM` (singular, no auto) does the same thing for a specific list of entry IDs you already have in hand &mdash; useful when you want to take ownership of one known stuck entry, or when you need to move a specific consumer's PEL to a peer (the case the demo's \"Remove consumer\" button handles via `handoverPending`). `XAUTOCLAIM` cannot filter by source consumer, so it cannot be used for a per-consumer handover."},{"id":"replay-with-xrange","title":"Replay with XRANGE","role":"content","text":"`XRANGE` reads a slice of history. It is completely independent of any consumer group &mdash; no cursors move, no acks happen &mdash; so it is safe to call any number of times, from any process:\n\n[code example]\n\nThe special IDs `-` and `+` mean \"from the very beginning\" and \"to the very end\". You can also pass real IDs (`1716998413541-0`) or just the millisecond part (`1716998413541`, which Redis interprets as \"any entry with this timestamp\").\n\nTypical uses:\n\n* **Bootstrapping a new projection** &mdash; read the entire stream from `-` and build a derived view in another store (a search index, a SQL table, a different cache). Doing this against a consumer group would consume the entries; `XRANGE` lets you do it without disrupting live consumers.\n* **Auditing recent activity** &mdash; read the last few minutes by ID range without touching any group cursor.\n* **Debugging** &mdash; fetch one specific entry by its ID, or a tight range around an incident timestamp, to see exactly what producers wrote."},{"id":"the-consumer-worker-thread","title":"The consumer worker thread","role":"content","text":"`ConsumerWorker` wraps the `XREADGROUP` &rarr; process &rarr; `XACK` loop in a daemon thread\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/streaming/java-jedis/ConsumerWorker.java)):\n\n[code example]\n\n`dispatch` calls `handleEntry`, which either acks (the normal path) or, when the demo has asked the worker to \"crash\", drops the entry on the floor and increments a counter so the UI can show what is currently in the PEL waiting to be claimed. A failure inside the per-entry handler (typically an `XACK` against Redis) is caught and logged so a single bad entry can never kill the daemon thread &mdash; that would silently halt this consumer while every other entry sat in its PEL waiting for `XAUTOCLAIM`.\n\nRecovery of stuck PEL entries &mdash; this consumer's, after a restart, or another consumer's, after a crash &mdash; runs through a separate `reapIdlePel` method rather than the read loop. That method calls `XAUTOCLAIM` with this consumer as the target, then processes whatever was claimed in the same flow as new entries. This is the textbook Streams pattern: each consumer is its own reaper, running `XAUTOCLAIM(self)` periodically (or on demand) so a crashed peer's entries never sit invisibly in the PEL. The demo's \"XAUTOCLAIM to selected\" button calls `reapIdlePel` on the chosen consumer; in production you would run it from a timer every few seconds.\n\nNote that the worker's main read loop deliberately does *not* call `XREADGROUP 0` to drain its own PEL on every iteration. That would re-deliver every pending entry continuously and *reset its idle counter to zero* each time, which would keep crashed entries below the `XAUTOCLAIM` threshold forever. Using `XAUTOCLAIM(self)` as the recovery primitive &mdash; which only fires for entries idle longer than `minIdleTime` &mdash; avoids that whole class of bug.\n\nThe pause and crash levers exist only for the demo. A real consumer is just the read-process-ack loop &mdash; everything else in this class is instrumentation."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before running the demo, make sure that:\n\n* Redis 7.0 or later is running and accessible. By default, the demo connects to `localhost:6379`. `XAUTOCLAIM` was added in Redis 6.2, but its reply gained a third element (the list of deleted IDs) in 7.0; the example relies on that shape.\n* JDK 17 or later is installed (the demo's inline HTML uses text blocks, introduced as a preview in JDK 13 and finalised in JDK 15; 17+ keeps the demo on a current LTS).\n* The Jedis JAR (5.0+; the example was tested against 6.2.0) and its dependencies are on your classpath. Get them from [Maven Central](https://repo1.maven.org/maven2/redis/clients/jedis/), or via Maven/Gradle in a project setup. You also need [`slf4j-api`](https://repo1.maven.org/maven2/org/slf4j/slf4j-api/) and [`commons-pool2`](https://repo1.maven.org/maven2/org/apache/commons/commons-pool2/), which Jedis declares as transitive dependencies.\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of three Java files. Download them from the [`java-jedis` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/streaming/java-jedis) on GitHub, or grab them with `curl`:\n\n[code example]\n\nDownload the JARs into the same directory (the example assumes Jedis 6.2.0, `slf4j-api` 2.0.12, and `commons-pool2` 2.12.1 &mdash; adjust the filenames if you use different versions):\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From that directory:\n\n[code example]\n\nYou should see something like:\n\n[code example]\n\nBy default the demo wipes the configured stream key on startup so each run starts from a clean state. Pass `--no-reset` to keep any existing data at the key (useful when re-running against the same stream to inspect prior state), or `--stream-key <name>` to point the demo at a different key entirely.\n\nOpen [http://127.0.0.1:8083](http://127.0.0.1:8083) in a browser. You can:\n\n* **Produce** any number of events of a chosen type (or random types). Watch the stream length grow and the tail update.\n* See each **consumer group**: its `last-delivered-id`, the size of its pending list, and the consumers in it. Each consumer shows its processed count, pending count, and idle time.\n* **Add or remove** consumers within a group at runtime to see Redis split the work across the new shape.\n* Click **Crash next 3** on a consumer to drop its next three deliveries &mdash; the same effect as a worker process dying after `XREADGROUP` but before `XACK`. Watch the **Pending entries (XPENDING)** panel fill up.\n* Wait until the idle time exceeds the threshold (default 5000 ms), pick a healthy target consumer, and click **XAUTOCLAIM to selected** &mdash; the stuck entries are reassigned and the delivery counter increments.\n* **Replay (XRANGE)** any range to confirm the full history is independent of consumer-group state.\n* **XTRIM** with an approximate `MAXLEN` to bound retention. Note that an approximate trim only releases whole macro-nodes &mdash; `MAXLEN ~ 50` on a small stream may not delete anything; on a 300-entry stream it typically lands at around 100.\n* Click **Reset demo** to drop the stream and re-seed the default groups.\n\nThe demo server uses standard JDK libraries for HTTP handling and concurrency:\n\n* [`com.sun.net.httpserver.HttpServer`](https://docs.oracle.com/en/java/javase/21/docs/api/jdk.httpserver/com/sun/net/httpserver/HttpServer.html) for the web server, with a 16-thread fixed pool from `Executors.newFixedThreadPool(16)`\n* [`java.util.concurrent.locks.ReentrantLock`](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/locks/ReentrantLock.html) for the in-process consumer-worker registry so add/remove handlers cannot race\n* Daemon threads (one per `ConsumerWorker`) for the `XREADGROUP` &rarr; process &rarr; `XACK` loop"},{"id":"production-usage","title":"Production usage","role":"content","text":"This guide uses a deliberately small local demo so you can focus on the streaming pattern. In production, you will usually want to harden several aspects of it."},{"id":"pick-retention-by-length-or-by-minimum-id","title":"Pick retention by length or by minimum ID","role":"content","text":"The demo uses `MAXLEN ~` on every `XADD`. Two alternatives are worth considering:\n\n* `MINID ~ <id>` &mdash; keep only entries newer than an ID. If you want \"the last 24 hours\", compute the wall-clock cutoff and pass `XTRIM MINID ~ <ms>-0`. This is the right pattern when retention is time-bounded. `EventStream.trimMinid` calls `XTRIM ... MINID ~`.\n* No cap on `XADD` plus a periodic `XTRIM` job &mdash; useful if your producer is hot and the per-`XADD` work has to stay minimal, or if retention rules are complex (a separate process can also factor in consumer-group lag).\n\nIn all three cases the trimming is approximate by default. Use exact trimming (`MAXLEN n` or `MINID id` without `~`) only when you genuinely need an exact count."},{"id":"dont-let-consumer-group-lag-silently-grow","title":"Don't let consumer-group lag silently grow","role":"content","text":"`XINFO GROUPS` reports each group's `lag` (entries the group has not yet read) and `pending` (entries delivered but not acked). In production, alert on either of these crossing a threshold &mdash; a steadily growing pending count usually means consumers are crashing without `XAUTOCLAIM` running, and a growing lag means consumers can't keep up with producers.\n\nThe same applies inside a group: `XINFO CONSUMERS` reports per-consumer pending counts and idle times, so you can spot one slow consumer holding entries that the rest of the group is waiting on."},{"id":"make-consumer-logic-idempotent","title":"Make consumer logic idempotent","role":"content","text":"`XAUTOCLAIM` can re-deliver an entry to a different consumer after a crash. If your processing has side effects (sending email, charging a card, updating a downstream store), make sure the same entry processed twice gives the same result &mdash; use an idempotency key, an upsert with conditional check, or a once-per-id guard table. Redis Streams cannot give you exactly-once semantics on its own."},{"id":"bound-the-delivery-counter-as-a-poison-pill-signal","title":"Bound the delivery counter as a poison-pill signal","role":"content","text":"`XPENDING` returns each entry's delivery count, incremented on every claim. If an entry has been delivered (and dropped) several times, the next consumer is unlikely to fare better. After some threshold &mdash; `deliveries >= 5`, say &mdash; route the entry to a *dead-letter stream*, ack it on the original group, and alert. New entries keep flowing past a poison pill (`XREADGROUP >` still delivers fresh work), but the bad entry's repeated reclaim wastes consumer time and keeps the PEL bigger than it needs to be &mdash; without a DLQ threshold it can also slowly trip retention/lag alerts."},{"id":"partition-by-tenant-or-entity-for-scale","title":"Partition by tenant or entity for scale","role":"content","text":"A single Redis Stream is a single key, and on a Redis Cluster a single key lives on a single shard. If your throughput exceeds what one shard can handle, partition the stream &mdash; for example by tenant ID (`events:orders:{tenant_a}`, `events:orders:{tenant_b}`) &mdash; so different tenants land on different shards. Hash-tags (`{tenant_a}`) keep all related streams on the same shard if you need to multi-stream atomically.\n\nPer-entity partitioning (`events:order:{order_id}`) is the canonical pattern when you treat each entity's stream as the event-sourcing log for that entity: every state change for one order goes on its own stream, which is also bounded in size by the entity's lifetime."},{"id":"tune-the-jedispool","title":"Tune the JedisPool","role":"content","text":"The demo bumps `JedisPoolConfig.setMaxTotal(64)` and `setMaxIdle(32)` because every blocking `XREADGROUP` call holds *its* Jedis instance for the full block duration (here 500 ms by default). If the pool were sized at the default 8, three consumers blocking at once would leave only five connections for the 16 HTTP handlers, which can starve `/state` polling under load. In production size the pool to at least *(blocking consumers) + (concurrent request threads) + headroom* and keep that ceiling below the server-side `maxclients`.\n\nEvery helper acquires its Jedis from the pool with try-with-resources, so connections always go back to the pool on success or exception. No in-process lock is needed across helpers because each call uses its own connection."},{"id":"dont-read-with-xread-no-group-and-then-try-to-ack","title":"Don't read with XREAD (no group) and then try to ack","role":"content","text":"`XREAD` and `XREADGROUP` are different mechanisms. `XREAD` is a tail-the-log read with no consumer-group state &mdash; entries are not added to any PEL, and you cannot `XACK` them. If you want at-least-once delivery and crash recovery, you must read through a consumer group.\n\n`XREAD` is still useful for read-only tail clients (a UI streaming events, a debugger, a `tail -f`-style command-line tool). It's just not part of the at-least-once path."},{"id":"use-a-separate-consumer-pool-per-group","title":"Use a separate consumer pool per group","role":"content","text":"The demo runs every consumer in one process. In production each consumer group is usually its own deployment &mdash; its own pool of pods or VMs &mdash; so a slow projection in `analytics` cannot pull `notifications` workers off their stream. Each pod runs one consumer thread per CPU core, with `XAUTOCLAIM` either embedded in the consumer loop (every N reads, claim idle entries to self) or run by a separate reaper."},{"id":"inspect-the-stream-directly-with-redis-cli","title":"Inspect the stream directly with redis-cli","role":"content","text":"When testing or troubleshooting, inspect the stream directly to confirm the consumer state is what you expect:\n\n[code example]\n\nIf a group's `lag` is growing while consumers' `idle` times are short, consumers are healthy but producers are outpacing them &mdash; add more consumers. If `pending` is growing while `lag` is small, consumers are *receiving* entries but not *acking* them &mdash; either they are crashing mid-message or your acking logic has a bug."},{"id":"learn-more","title":"Learn more","role":"related","text":"This example uses the following Redis commands:\n\n* [`XADD`](https://redis.io/docs/latest/commands/xadd) to append an event with an approximate `MAXLEN` cap.\n* [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) to read new entries for a consumer in a group.\n* [`XACK`](https://redis.io/docs/latest/commands/xack) to acknowledge a processed entry.\n* [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) to reassign idle pending entries to a healthy consumer.\n* [`XRANGE`](https://redis.io/docs/latest/commands/xrange) for replay and audit, independent of consumer-group state.\n* [`XPENDING`](https://redis.io/docs/latest/commands/xpending) to inspect the per-group pending list with idle times and delivery counts.\n* [`XTRIM`](https://redis.io/docs/latest/commands/xtrim) for explicit retention enforcement.\n* [`XGROUP CREATE`](https://redis.io/docs/latest/commands/xgroup-create) and\n  [`XGROUP DELCONSUMER`](https://redis.io/docs/latest/commands/xgroup-delconsumer) to manage groups and consumers.\n* [`XINFO STREAM`](https://redis.io/docs/latest/commands/xinfo-stream),\n  [`XINFO GROUPS`](https://redis.io/docs/latest/commands/xinfo-groups), and\n  [`XINFO CONSUMERS`](https://redis.io/docs/latest/commands/xinfo-consumers) for observability.\n\nSee the [Jedis guide](https://redis.io/docs/latest/develop/clients/jedis) for the full client reference, and the [Streams overview](https://redis.io/docs/latest/develop/data-types/streams) for the deeper conceptual model &mdash; consumer groups, the PEL, claim semantics, capped streams, and the differences with Kafka partitions."}],"examples":[{"id":"the-event-stream-helper-ex0","language":"java","code":"import redis.clients.jedis.JedisPool;\nimport redis.clients.jedis.JedisPoolConfig;\n\nJedisPool pool = new JedisPool(new JedisPoolConfig(), \"localhost\", 6379);\nEventStream stream = new EventStream(\n        pool,\n        \"demo:events:orders\",\n        2000,   // approximate MAXLEN retention guardrail\n        5000);  // XAUTOCLAIM idle threshold (ms)\n\n// Producer\nMap<String, String> payload = new LinkedHashMap<>();\npayload.put(\"order_id\", \"o-1234\");\npayload.put(\"customer\", \"alice\");\npayload.put(\"amount\", \"49.50\");\nString streamId = stream.produce(\"order.placed\", payload);\n\n// Consumer group + one consumer\nstream.ensureGroup(\"notifications\", \"0-0\");\nList<EventStream.Entry> entries =\n        stream.consume(\"notifications\", \"worker-a\", 10, 500L);\nfor (EventStream.Entry entry : entries) {\n    handle(entry.fields);                                      // your processing\n    stream.ack(\"notifications\", List.of(entry.id));            // XACK\n}\n\n// Recover stuck PEL entries by reaping them into a healthy consumer.\n// The textbook pattern: each consumer periodically calls XAUTOCLAIM\n// with itself as the target and processes whatever it claimed.\n// ConsumerWorker.reapIdlePel wraps that flow; the low-level helper\n// stream.autoclaim(group, target, ...) is also available if you want\n// to drive XAUTOCLAIM directly.\nConsumerWorker.ReapResult result = workerB.reapIdlePel();\n// result.claimed   == number of entries pulled into this consumer's PEL\n// result.processed == number that were handled + acked\n// result.deletedIds == PEL entries whose payload was already trimmed.\n// Redis 7+ has already removed those slots from the PEL, so no XACK is\n// needed — log them and route to a dead-letter store for audit.\n\n// Replay history (independent of any group's cursor)\nfor (EventStream.Entry entry : stream.replay(\"-\", \"+\", 50)) {\n    System.out.println(entry.id + \" \" + entry.fields);\n}","section_id":"the-event-stream-helper"},{"id":"data-model-ex0","language":"text","code":"demo:events:orders\n  1716998413541-0   type=order.placed     order_id=o-1234   customer=alice  amount=49.50  ts_ms=...\n  1716998413542-0   type=order.paid       order_id=o-1234   customer=alice  amount=49.50  ts_ms=...\n  1716998413542-1   type=order.shipped    order_id=o-1235   customer=bob    amount=12.00  ts_ms=...\n  ...","section_id":"data-model"},{"id":"producing-events-ex0","language":"java","code":"public List<String> produceBatch(List<Map.Entry<String, Map<String, String>>> events) {\n    List<String> ids = new ArrayList<>(events.size());\n    try (Jedis jedis = pool.getResource()) {\n        Pipeline pipe = jedis.pipelined();\n        List<Response<StreamEntryID>> responses = new ArrayList<>(events.size());\n        for (Map.Entry<String, Map<String, String>> event : events) {\n            Map<String, String> fields = encodeFields(event.getKey(), event.getValue());\n            XAddParams params = new XAddParams()\n                    .maxLen(maxlenApprox)\n                    .approximateTrimming();\n            responses.add(pipe.xadd(streamKey, params, fields));\n        }\n        pipe.sync();\n        for (Response<StreamEntryID> resp : responses) {\n            StreamEntryID id = resp.get();\n            ids.add(id == null ? null : id.toString());\n        }\n    }\n    return ids;\n}","section_id":"producing-events"},{"id":"reading-with-a-consumer-group-ex0","language":"java","code":"public List<Entry> consume(String group, String consumer, int count, long blockMs) {\n    try (Jedis jedis = pool.getResource()) {\n        XReadGroupParams params = new XReadGroupParams()\n                .count(count)\n                .block((int) blockMs);\n        Map<String, StreamEntryID> streams = new LinkedHashMap<>();\n        streams.put(streamKey, StreamEntryID.UNRECEIVED_ENTRY);\n        List<Map.Entry<String, List<StreamEntry>>> result =\n                jedis.xreadGroup(group, consumer, params, streams);\n        return flattenEntries(result);\n    }\n}","section_id":"reading-with-a-consumer-group"},{"id":"acknowledging-entries-ex0","language":"java","code":"public long ack(String group, List<String> ids) {\n    if (ids == null || ids.isEmpty()) {\n        return 0L;\n    }\n    StreamEntryID[] entryIds = new StreamEntryID[ids.size()];\n    for (int i = 0; i < ids.size(); i++) {\n        entryIds[i] = new StreamEntryID(ids.get(i));\n    }\n    try (Jedis jedis = pool.getResource()) {\n        return jedis.xack(streamKey, group, entryIds);\n    }\n}","section_id":"acknowledging-entries"},{"id":"multiple-consumer-groups-one-stream-ex0","language":"java","code":"stream.ensureGroup(\"notifications\", \"0-0\");\nstream.ensureGroup(\"analytics\",     \"0-0\");","section_id":"multiple-consumer-groups-one-stream"},{"id":"recovering-crashed-consumers-with-xautoclaim-ex0","language":"java","code":"public ReapResult reapIdlePel() {\n    EventStream.AutoClaimResult result = stream.autoclaim(group, name, 100, \"0-0\", 10);\n    int processedCount = 0;\n    for (EventStream.Entry entry : result.claimed) {\n        try {\n            handleEntry(entry.id, entry.fields);\n            processedCount++;\n        } catch (Exception exc) {\n            System.err.printf(\n                    \"[%s/%s] reap failed on %s: %s%n\",\n                    group, name, entry.id, exc);\n        }\n    }\n    return new ReapResult(\n            result.claimed.size(), processedCount, result.deletedIds);\n}","section_id":"recovering-crashed-consumers-with-xautoclaim"},{"id":"recovering-crashed-consumers-with-xautoclaim-ex1","language":"java","code":"public AutoClaimResult autoclaim(\n        String group, String consumer, int pageCount, String startId, int maxPages) {\n    List<Entry> claimedAll = new ArrayList<>();\n    List<String> deletedAll = new ArrayList<>();\n    String cursor = (startId == null || startId.isEmpty()) ? \"0-0\" : startId;\n    try (Jedis jedis = pool.getResource()) {\n        for (int page = 0; page < maxPages; page++) {\n            // Use sendCommand for the raw 3-element XAUTOCLAIM reply\n            // (next-id, claimed-entries, deleted-ids). Jedis 6's typed\n            // xautoclaim wrapper returns only the first two slots.\n            Object raw = jedis.sendCommand(\n                    XAutoClaimRaw.XAUTOCLAIM,\n                    streamKey, group, consumer,\n                    Integer.toString(claimMinIdleMs),\n                    cursor,\n                    \"COUNT\", Integer.toString(pageCount));\n            ParsedAutoClaim parsed = parseAutoClaim(raw);\n            claimedAll.addAll(parsed.claimed);\n            deletedAll.addAll(parsed.deletedIds);\n            if (\"0-0\".equals(parsed.nextCursor)) {\n                break;\n            }\n            cursor = parsed.nextCursor;\n        }\n    }\n    return new AutoClaimResult(claimedAll, deletedAll);\n}","section_id":"recovering-crashed-consumers-with-xautoclaim"},{"id":"replay-with-xrange-ex0","language":"java","code":"public List<Entry> replay(String startId, String endId, int count) {\n    try (Jedis jedis = pool.getResource()) {\n        List<StreamEntry> rows = jedis.xrange(streamKey, startId, endId, count);\n        return toEntries(rows);\n    }\n}","section_id":"replay-with-xrange"},{"id":"the-consumer-worker-thread-ex0","language":"java","code":"private void run() {\n    while (!stopRequested) {\n        if (paused) {\n            sleep(50L);\n            continue;\n        }\n        List<EventStream.Entry> entries;\n        try {\n            entries = stream.consume(group, name, 10, 500L);\n        } catch (Exception exc) {\n            System.err.printf(\"[%s/%s] read failed: %s%n\", group, name, exc);\n            sleep(500L);\n            continue;\n        }\n        for (EventStream.Entry entry : entries) {\n            dispatch(entry.id, entry.fields);\n        }\n    }\n}","section_id":"the-consumer-worker-thread"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir streaming-demo && cd streaming-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/streaming/java-jedis\ncurl -O $BASE/EventStream.java\ncurl -O $BASE/ConsumerWorker.java\ncurl -O $BASE/DemoServer.java","section_id":"get-the-source-files"},{"id":"get-the-source-files-ex1","language":"bash","code":"JEDIS_VER=6.2.0\nSLF4J_VER=2.0.12\nPOOL_VER=2.12.1\ncurl -O https://repo1.maven.org/maven2/redis/clients/jedis/$JEDIS_VER/jedis-$JEDIS_VER.jar\ncurl -O https://repo1.maven.org/maven2/org/slf4j/slf4j-api/$SLF4J_VER/slf4j-api-$SLF4J_VER.jar\ncurl -O https://repo1.maven.org/maven2/org/apache/commons/commons-pool2/$POOL_VER/commons-pool2-$POOL_VER.jar","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"javac -cp jedis-6.2.0.jar:slf4j-api-2.0.12.jar:commons-pool2-2.12.1.jar \\\n    EventStream.java ConsumerWorker.java DemoServer.java\n\njava -cp .:jedis-6.2.0.jar:slf4j-api-2.0.12.jar:commons-pool2-2.12.1.jar \\\n    DemoServer --port 8083 --redis-host localhost --redis-port 6379","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"Deleting any existing data at key 'demo:events:orders' for a clean demo run (pass --no-reset to keep it).\nRedis streaming demo server listening on http://127.0.0.1:8083\nUsing Redis at localhost:6379 with stream key 'demo:events:orders' (MAXLEN ~ 2000)\nSeeded 3 consumer(s) across 2 group(s)","section_id":"start-the-demo-server"},{"id":"inspect-the-stream-directly-with-redis-cli-ex0","language":"bash","code":"# Stream summary\nredis-cli XLEN demo:events:orders\nredis-cli XINFO STREAM demo:events:orders\n\n# Group cursors and pending counts\nredis-cli XINFO GROUPS demo:events:orders\n\n# Consumers within a group\nredis-cli XINFO CONSUMERS demo:events:orders notifications\n\n# Pending entries with idle time and delivery count\nredis-cli XPENDING demo:events:orders notifications - + 20\n\n# Tail the stream live (no consumer-group state — like tail -f)\nredis-cli XREAD BLOCK 0 STREAMS demo:events:orders '$'\n\n# Replay a range\nredis-cli XRANGE demo:events:orders - + COUNT 50","section_id":"inspect-the-stream-directly-with-redis-cli"}]}
{"schema_version":2,"id":"develop/use-cases/streaming/java-lettuce","title":"Redis streaming with Lettuce","url":"https://redis.io/docs/latest/develop/use-cases/streaming/java-lettuce/","summary":"Implement a Redis event-streaming pipeline in Java with Lettuce","aliases":["/develop/use-cases/streaming/lettuce"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:55:04+01:00","children":[],"page_type":"content","content_hash":"0d6d908b4342cd5ae12e4811871f9151f98fa09a7020d71832d4fbb3e8890d43","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a Redis-backed event-streaming pipeline in Java with the [Lettuce](https://redis.io/docs/latest/develop/clients/lettuce) client library. It includes a small local web server built on the JDK's `com.sun.net.httpserver` so you can produce events into a single Redis Stream, watch two independent consumer groups read it at their own pace, and recover stuck deliveries with `XAUTOCLAIM` after simulating a consumer crash."},{"id":"overview","title":"Overview","role":"overview","text":"A Redis Stream is an append-only log of field/value entries with auto-generated, time-ordered IDs. Producers append with [`XADD`](https://redis.io/docs/latest/commands/xadd); consumers belong to *consumer groups* and read with [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup). The group as a whole tracks a single `last-delivered-id` cursor, and each consumer gets its own pending-entries list (PEL) of messages it has been handed but not yet acknowledged. Once a consumer has processed an entry it calls [`XACK`](https://redis.io/docs/latest/commands/xack) to clear the entry from its PEL; entries left unacknowledged past an idle threshold can be reassigned to a healthy consumer with [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim).\n\nThat gives you:\n\n* Ordered, durable history that many independent consumer groups can read at their own pace\n* At-least-once delivery, with per-consumer pending lists and automatic recovery of crashed consumers\n* Horizontal scaling within a group — add a consumer and Redis automatically splits the work\n* Replay of any range with [`XRANGE`](https://redis.io/docs/latest/commands/xrange), independent of consumer-group state\n* Bounded retention through [`XADD MAXLEN ~`](https://redis.io/docs/latest/commands/xadd) or\n  [`XTRIM MINID ~`](https://redis.io/docs/latest/commands/xtrim), without a separate cleanup job\n\nIn this example, producers append order events (`order.placed`, `order.paid`, `order.shipped`, `order.cancelled`) to a single stream at `demo:events:orders`. Two consumer groups read the same stream:\n\n* **`notifications`** — two consumers (`worker-a`, `worker-b`) sharing the work, modelling a fan-out worker pool.\n* **`analytics`** — one consumer (`worker-c`) processing the full event flow on its own."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. The application calls `stream.produce(eventType, payload)` which runs [`XADD`](https://redis.io/docs/latest/commands/xadd) with an approximate [`MAXLEN ~`](https://redis.io/docs/latest/commands/xadd) cap. Redis assigns an auto-generated time-ordered ID.\n2. Each `ConsumerWorker` runs a daemon thread that loops on [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) with the special ID `>` (meaning \"deliver entries this group has not yet delivered to anyone\") and a short block timeout.\n3. After processing each entry, the consumer calls [`XACK`](https://redis.io/docs/latest/commands/xack) so Redis can drop it from the group's pending list.\n4. If a consumer is killed (or crashes) before acking, its entries sit in the group's PEL. A periodic [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) sweep reassigns idle entries to a healthy consumer.\n5. Anyone — including code outside the consumer groups — can read history with [`XRANGE`](https://redis.io/docs/latest/commands/xrange) without affecting any group's cursor.\n\nEach consumer group has its own cursor (`last-delivered-id`) and its own pending list, so the two groups in this demo process the same events without coordinating with each other."},{"id":"the-event-stream-helper","title":"The event-stream helper","role":"content","text":"The `EventStream` class wraps the stream operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/streaming/java-lettuce/EventStream.java)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each event is a single stream entry — a flat map of field/value strings — with an auto-generated time-ordered ID:\n\n[code example]\n\nThe ID is `{milliseconds}-{sequence}`, monotonically increasing within the stream, so you can range-query by approximate wall-clock time without an extra index. (IDs are ordered within a stream, not across streams — two events appended to different streams at the same millisecond can produce the same ID.) The implementation uses:\n\n* [`XADD`](https://redis.io/docs/latest/commands/xadd) with [`XAddArgs.maxlen(n).approximateTrimming()`](https://github.com/redis/lettuce/) on every append, so the stream stays bounded as it rolls forward\n* [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) with `XReadArgs.StreamOffset.lastConsumed(...)` (the `>` offset in CLI) for fresh deliveries\n* [`XACK`](https://redis.io/docs/latest/commands/xack) on every processed entry\n* [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) for sweeping idle pending entries to a healthy consumer\n* [`XRANGE`](https://redis.io/docs/latest/commands/xrange) for replay and audit\n* [`XPENDING`](https://redis.io/docs/latest/commands/xpending) for inspecting the per-group pending list\n* [`XINFO STREAM`](https://redis.io/docs/latest/commands/xinfo-stream),\n  [`XINFO GROUPS`](https://redis.io/docs/latest/commands/xinfo-groups), and\n  [`XINFO CONSUMERS`](https://redis.io/docs/latest/commands/xinfo-consumers) for surface-level observability\n* [`XTRIM`](https://redis.io/docs/latest/commands/xtrim) for explicit retention enforcement"},{"id":"producing-events","title":"Producing events","role":"content","text":"`produceBatch` queues several `XADD` calls through Lettuce's async API. Each call carries an approximate `MAXLEN ~` cap so the stream stays bounded as it rolls forward:\n\n[code example]\n\nThe `~` flavour of `MAXLEN` (set with `approximateTrimming()`) lets Redis trim at a macro-node boundary, which is much cheaper than exact trimming and is what you want when the cap is a retention *guardrail*, not a hard size constraint. With 300 events produced and `MAXLEN ~ 50`, you might end up with 100 entries left — Redis released the oldest whole macro-node and stopped. The next `XADD` keeps length stable.\n\nIf you genuinely need an exact cap (rare), call `.exactTrimming()` instead of `.approximateTrimming()`. The performance difference is significant on busy streams.\n\nA Lettuce-specific point on batching: the obvious \"true pipeline\" pattern of `setAutoFlushCommands(false)` + queue + `flushCommands()` + bulk-await is **connection-scoped** — auto-flush off on the shared connection stalls every *other* thread that is issuing sync commands on the same connection. In this demo the consumer workers' `XREADGROUP` loops use the sync API on the same connection, so flipping the connection's auto-flush flag to batch produces would freeze every consumer thread until the batch finished. The helper sidesteps that by leaving auto-flush on; Lettuce still pipelines the queued async `XADD` calls when they arrive faster than the round-trip latency. For truly large produce batches you would use a dedicated `RedisClient.connect()` for the producer and toggle `setAutoFlushCommands(false)` on *that* connection only."},{"id":"reading-with-a-consumer-group","title":"Reading with a consumer group","role":"content","text":"Each consumer in a group runs the same `XREADGROUP` loop. The `XReadArgs.StreamOffset.lastConsumed(streamKey)` offset is the CLI's `>` — \"deliver entries this group has not yet delivered to *anyone*\":\n\n[code example]\n\n`blockMs` makes the call efficient even when the stream is idle: the client parks on the server until either an entry arrives or the timeout expires, so consumers don't busy-loop.\n\nReading with an explicit ID like `0` (via `XReadArgs.StreamOffset.from(streamKey, \"0\")`) does something different — it replays entries already delivered to *this* consumer name (its private PEL). That is the canonical recovery path when the same consumer restarts: catch up on its own pending entries first, then resume reading new ones. `EventStream` exposes that as `consumeOwnPel`."},{"id":"acknowledging-entries","title":"Acknowledging entries","role":"content","text":"Once the consumer has processed an entry, `XACK` tells Redis it can drop the entry from the group's pending list:\n\n[code example]\n\nThis is the linchpin of at-least-once delivery: an entry that is never acked stays in the PEL until a claim moves it elsewhere. If your consumer thread crashes between processing and ack, the next claim sweep picks the entry back up. The one caveat is retention: `XADD MAXLEN ~` and `XTRIM` can release the entry's *payload* even while its ID is still in the PEL. The next `XAUTOCLAIM` returns those IDs in its deleted-IDs list and removes them from the PEL inside the same command — the entry cannot be retried, so the caller should log it and route to a dead-letter store for audit.\n\nThe trade-off is the opposite of pub/sub: a slow or crashed consumer doesn't lose messages, but it does mean your downstream system must be idempotent. If you process an order twice because the first attempt died after the side effect but before the ack, the second attempt must be safe."},{"id":"multiple-consumer-groups-one-stream","title":"Multiple consumer groups, one stream","role":"content","text":"The big difference between Redis Streams and a job queue is that any number of independent consumer groups can read the same stream. The demo sets up two groups on `demo:events:orders`:\n\n[code example]\n\nEach group has its own cursor. Producing 5 events results in `notifications` and `analytics` each receiving all 5, with no coordination between them. Within `notifications`, the work is split across `worker-a` and `worker-b`: Redis hands each `XREADGROUP` call whatever entries are not yet delivered to anyone in the group, so adding a second worker doubles throughput without any rebalance logic.\n\nThe `\"0-0\"` start ID means \"deliver everything in the stream from the beginning\" — useful in a demo and for fresh groups bootstrapping from history. In production, a brand-new group reading a long-existing stream usually starts at `$` (\"only events after this point\") and uses [`XRANGE`](https://redis.io/docs/latest/commands/xrange) explicitly if it needs history."},{"id":"recovering-crashed-consumers-with-xautoclaim","title":"Recovering crashed consumers with XAUTOCLAIM","role":"content","text":"The demo's \"Crash next 3\" button tells a chosen consumer to drop its next three deliveries on the floor without acking them — the same effect as a worker process dying mid-message. Those entries stay in the group's PEL with their delivery counter incremented. Once they have been idle for at least `claim_min_idle_ms`, any healthy consumer in the group can rescue them by calling `XAUTOCLAIM` *with itself as the target*. `ConsumerWorker.reapIdlePel` wraps that pattern\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/streaming/java-lettuce/ConsumerWorker.java)):\n\n[code example]\n\nThe underlying `stream.autoclaim` helper pages through the group's PEL with `XAUTOCLAIM`'s continuation cursor:\n\n[code example]\n\nA single `XAUTOCLAIM` call scans up to `pageCount` PEL entries starting at `startId`, reassigns the ones idle for at least `min_idle_time` to the named consumer, and returns a continuation cursor in the first slot of the reply. For a full sweep, loop until the cursor returns to `\"0-0\"` (with a `maxPages` safety net so one call cannot monopolise a very large PEL). The delivery counter is incremented on every claim — after a few cycles you can use it to spot a *poison-pill* message that crashes every consumer that touches it, and route it to a dead-letter stream so the bad entry stops cycling. (New entries keep flowing past the poison pill — `XREADGROUP >` still delivers fresh work — but the bad entry's repeated reclaim wastes consumer time and keeps the PEL larger than it needs to be.)\n\nThe `deletedIds` list contains PEL entry IDs whose stream payload was already trimmed by the time the claim ran (typically because `MAXLEN ~` retention outran a slow consumer). `XAUTOCLAIM` on Redis 7+ removes those dangling slots from the PEL itself, so the caller does *not* need to `XACK` them — but the entries cannot be retried either, so log and route them to a dead-letter store for offline inspection. Redis 7.0 introduced this third return element; the example requires Redis 7.0+ for that reason.\n\nA Lettuce-specific quirk: Lettuce's typed `xautoclaim` method returns a `ClaimedMessages` object that only exposes the continuation cursor and the claimed messages — it does **not** surface the third (deleted-IDs) slot. To preserve the textbook `(cursor, claimed, deletedIds)` shape that the other clients return, `EventStream.autoclaim` dispatches the command itself with a `NestedMultiOutput` and parses all three slots manually. It's a small amount of boilerplate, but until Lettuce extends `ClaimedMessages` there is no clean way to read the deleted IDs through the typed helper.\n\n`reapIdlePel` is the right primitive for the recovery path because it claims and processes in one step: every entry the call returned is now in *this* consumer's PEL, so the same consumer is responsible for processing and acking it. In production each consumer thread runs `reapIdlePel` periodically (every few seconds, on a timer) so a crashed peer's entries never sit invisibly. The demo exposes it as a manual button so you can trigger the reap after waiting for the idle threshold.\n\n`XCLAIM` (singular, no auto) does the same thing for a specific list of entry IDs you already have in hand — useful when you want to take ownership of one known stuck entry, or when you need to move a specific consumer's PEL to a peer (the case the demo's \"Remove consumer\" button handles via `handoverPending`). `XAUTOCLAIM` cannot filter by source consumer, so it cannot be used for a per-consumer handover."},{"id":"replay-with-xrange","title":"Replay with XRANGE","role":"content","text":"`XRANGE` reads a slice of history. It is completely independent of any consumer group — no cursors move, no acks happen — so it is safe to call any number of times, from any process:\n\n[code example]\n\nThe special IDs `-` and `+` mean \"from the very beginning\" and \"to the very end\". You can also pass real IDs (`1716998413541-0`) or just the millisecond part (`1716998413541`, which Redis interprets as \"any entry with this timestamp\").\n\nTypical uses:\n\n* **Bootstrapping a new projection** — read the entire stream from `-` and build a derived view in another store (a search index, a SQL table, a different cache). Doing this against a consumer group would consume the entries; `XRANGE` lets you do it without disrupting live consumers.\n* **Auditing recent activity** — read the last few minutes by ID range without touching any group cursor.\n* **Debugging** — fetch one specific entry by its ID, or a tight range around an incident timestamp, to see exactly what producers wrote."},{"id":"the-consumer-worker-thread","title":"The consumer worker thread","role":"content","text":"`ConsumerWorker` wraps the `XREADGROUP` → process → `XACK` loop in a daemon thread:\n\n[code example]\n\n`dispatch` either acks (the normal path) or, when the demo has asked the worker to \"crash\", drops the entry on the floor and increments a counter so the UI can show what is currently in the PEL waiting to be claimed.\n\nRecovery of stuck PEL entries — this consumer's, after a restart, or another consumer's, after a crash — runs through a separate `reapIdlePel` method rather than the read loop. That method calls `XAUTOCLAIM` with this consumer as the target, then processes whatever was claimed in the same flow as new entries. This is the textbook Streams pattern: each consumer is its own reaper, running `XAUTOCLAIM(self)` periodically (or on demand) so a crashed peer's entries never sit invisibly in the PEL. The demo's \"XAUTOCLAIM to selected\" button calls `reapIdlePel` on the chosen consumer; in production you would run it from a timer every few seconds.\n\nNote that the worker's main read loop deliberately does *not* read its own PEL on every iteration (the `consumeOwnPel` helper exists for the explicit recovery case). Re-delivering every pending entry on every loop iteration would *reset its idle counter to zero* each time, which would keep crashed entries below the `XAUTOCLAIM` threshold forever. Using `XAUTOCLAIM(self)` as the recovery primitive — which only fires for entries idle longer than `min_idle_time` — avoids that whole class of bug.\n\nThe pause and crash levers exist only for the demo. A real consumer is just the read-process-ack loop — everything else in this class is instrumentation."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* Redis 7.0 or later. `XAUTOCLAIM` was added in Redis 6.2, but its reply gained a third element (the list of deleted IDs) in 7.0; the example relies on that shape.\n* JDK 17 or later (the demo uses Java text blocks for the inlined HTML).\n* The Lettuce JAR (and its Netty + Reactor dependencies) on your classpath. Get them from\n  [Maven Central](https://repo1.maven.org/maven2/io/lettuce/lettuce-core/),\n  or via Maven/Gradle in a project setup.\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of three Java files. Download them from the [`java-lettuce` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/streaming/java-lettuce) on GitHub, or grab them with `curl`:\n\n[code example]\n\nYou also need Lettuce and its runtime dependencies on your classpath. The simplest way is to download them into a local `lib/` directory:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From the demo directory:\n\n[code example]\n\nYou should see:\n\n[code example]\n\nBy default the demo wipes the configured stream key on startup so each run starts from a clean state. Pass `--no-reset` to keep any existing data at the key (useful when re-running against the same stream to inspect prior state), or `--stream-key <name>` to point the demo at a different key entirely. Other supported flags: `--port`, `--redis-host`, `--redis-port`, `--maxlen`, `--claim-idle-ms`.\n\nOpen [http://127.0.0.1:8784](http://127.0.0.1:8784) in a browser. You can:\n\n* **Produce** any number of events of a chosen type (or random types). Watch the stream length grow and the tail update.\n* See each **consumer group**: its `last-delivered-id`, the size of its pending list, and the consumers in it. Each consumer shows its processed count, pending count, and idle time.\n* **Add or remove** consumers within a group at runtime to see Redis split the work across the new shape.\n* Click **Crash next 3** on a consumer to drop its next three deliveries — the same effect as a worker process dying after `XREADGROUP` but before `XACK`. Watch the **Pending entries (XPENDING)** panel fill up.\n* Wait until the idle time exceeds the threshold (default 5000 ms), pick a healthy target consumer, and click **XAUTOCLAIM to selected** — the stuck entries are reassigned and the delivery counter increments.\n* **Replay (XRANGE)** any range to confirm the full history is independent of consumer-group state.\n* **XTRIM** with an approximate `MAXLEN` to bound retention. Note that an approximate trim only releases whole macro-nodes — `MAXLEN ~ 50` on a small stream may not delete anything; on a 300-entry stream it typically lands at around 100.\n* Click **Reset demo** to drop the stream and re-seed the default groups."},{"id":"production-usage","title":"Production usage","role":"content","text":""},{"id":"pick-retention-by-length-or-by-minimum-id","title":"Pick retention by length or by minimum ID","role":"content","text":"The demo uses `MAXLEN ~` on every `XADD`. Two alternatives are worth considering:\n\n* `MINID ~ <id>` — keep only entries newer than an ID. If you want \"the last 24 hours\", compute the wall-clock cutoff and call `stream.trimMinid(...)` (or `XTRIM MINID ~ <ms>-0`). This is the right pattern when retention is time-bounded.\n* No cap on `XADD` plus a periodic `XTRIM` job — useful if your producer is hot and the per-`XADD` work has to stay minimal, or if retention rules are complex (a separate process can also factor in consumer-group lag).\n\nIn all three cases the trimming is approximate by default. Use exact trimming (`.exactTrimming()`) only when you genuinely need an exact count."},{"id":"dont-let-consumer-group-lag-silently-grow","title":"Don't let consumer-group lag silently grow","role":"content","text":"`XINFO GROUPS` reports each group's `lag` (entries the group has not yet read) and `pending` (entries delivered but not acked). In production, alert on either of these crossing a threshold — a steadily growing pending count usually means consumers are crashing without `XAUTOCLAIM` running, and a growing lag means consumers can't keep up with producers.\n\nThe same applies inside a group: `XINFO CONSUMERS` reports per-consumer pending counts and idle times, so you can spot one slow consumer holding entries that the rest of the group is waiting on."},{"id":"make-consumer-logic-idempotent","title":"Make consumer logic idempotent","role":"content","text":"`XAUTOCLAIM` can re-deliver an entry to a different consumer after a crash. If your processing has side effects (sending email, charging a card, updating a downstream store), make sure the same entry processed twice gives the same result — use an idempotency key, an upsert with conditional check, or a once-per-id guard table. Redis Streams cannot give you exactly-once semantics on its own."},{"id":"bound-the-delivery-counter-as-a-poison-pill-signal","title":"Bound the delivery counter as a poison-pill signal","role":"content","text":"`XPENDING` returns each entry's delivery count, incremented on every claim. If an entry has been delivered (and dropped) several times, the next consumer is unlikely to fare better. After some threshold — `deliveries >= 5`, say — route the entry to a *dead-letter stream*, ack it on the original group, and alert. New entries keep flowing past a poison pill (`XREADGROUP >` still delivers fresh work), but the bad entry's repeated reclaim wastes consumer time and keeps the PEL bigger than it needs to be — without a DLQ threshold it can also slowly trip retention/lag alerts."},{"id":"partition-by-tenant-or-entity-for-scale","title":"Partition by tenant or entity for scale","role":"content","text":"A single Redis Stream is a single key, and on a Redis Cluster a single key lives on a single shard. If your throughput exceeds what one shard can handle, partition the stream — for example by tenant ID (`events:orders:{tenant_a}`, `events:orders:{tenant_b}`) — so different tenants land on different shards. Hash-tags (`{tenant_a}`) keep all related streams on the same shard if you need to multi-stream atomically.\n\nPer-entity partitioning (`events:order:{order_id}`) is the canonical pattern when you treat each entity's stream as the event-sourcing log for that entity: every state change for one order goes on its own stream, which is also bounded in size by the entity's lifetime."},{"id":"use-a-separate-consumer-pool-per-group","title":"Use a separate consumer pool per group","role":"content","text":"The demo runs every consumer in one process. In production each consumer group is usually its own deployment — its own pool of pods or VMs — so a slow projection in `analytics` cannot pull `notifications` workers off their stream. Each pod runs one consumer thread per CPU core, with `XAUTOCLAIM` either embedded in the consumer loop (every N reads, claim idle entries to self) or run by a separate reaper."},{"id":"dont-read-with-xread-no-group-and-then-try-to-ack","title":"Don't read with XREAD (no group) and then try to ack","role":"content","text":"`XREAD` and `XREADGROUP` are different mechanisms. `XREAD` is a tail-the-log read with no consumer-group state — entries are not added to any PEL, and you cannot `XACK` them. If you want at-least-once delivery and crash recovery, you must read through a consumer group.\n\n`XREAD` is still useful for read-only tail clients (a UI streaming events, a debugger, a `tail -f`-style command-line tool). It's just not part of the at-least-once path."},{"id":"use-a-dedicated-connection-or-a-pool-for-batched-produces","title":"Use a dedicated connection (or a pool) for batched produces","role":"content","text":"The demo shares one `StatefulRedisConnection` across HTTP handlers and consumer threads. Lettuce is thread-safe for individual commands, but `setAutoFlushCommands(false)` is **connection-wide**: turning auto-flush off so an async batch can flush in one round trip would stall every other thread that is mid-flight on the same connection — and sync calls on those threads would silently hang until something else triggered a flush. The helper's `produceBatch` therefore leaves auto-flush on and relies on Lettuce's natural pipelining of arrival-rate-faster-than-RTT writes. For large produce batches use [`ConnectionPoolSupport`](https://github.com/redis/lettuce/wiki/Connection-Pooling) (or a second `RedisClient.connect()` dedicated to the producer) and toggle `setAutoFlushCommands(false)` on the dedicated connection only.\n\nIf you mix `setAutoFlushCommands(false)` with the **sync** API on the same connection, the call deadlocks silently — each sync call awaits its own future, and with auto-flush off those futures never complete. Always use the async API (`connection.async()`) inside the auto-flush-off window and await the futures in bulk; restore auto-flush to `true` in a `finally` block."},{"id":"inspect-the-stream-directly-with-redis-cli","title":"Inspect the stream directly with redis-cli","role":"content","text":"When testing or troubleshooting, inspect the stream directly to confirm the consumer state is what you expect:\n\n[code example]\n\nIf a group's `lag` is growing while consumers' `idle` times are short, consumers are healthy but producers are outpacing them — add more consumers. If `pending` is growing while `lag` is small, consumers are *receiving* entries but not *acking* them — either they are crashing mid-message or your acking logic has a bug."},{"id":"learn-more","title":"Learn more","role":"related","text":"This example uses the following Redis commands:\n\n* [`XADD`](https://redis.io/docs/latest/commands/xadd) to append an event with an approximate `MAXLEN` cap.\n* [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) to read new entries for a consumer in a group.\n* [`XACK`](https://redis.io/docs/latest/commands/xack) to acknowledge a processed entry.\n* [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) to reassign idle pending entries to a healthy consumer.\n* [`XRANGE`](https://redis.io/docs/latest/commands/xrange) for replay and audit, independent of consumer-group state.\n* [`XPENDING`](https://redis.io/docs/latest/commands/xpending) to inspect the per-group pending list with idle times and delivery counts.\n* [`XTRIM`](https://redis.io/docs/latest/commands/xtrim) for explicit retention enforcement.\n* [`XGROUP CREATE`](https://redis.io/docs/latest/commands/xgroup-create) and\n  [`XGROUP DELCONSUMER`](https://redis.io/docs/latest/commands/xgroup-delconsumer) to manage groups and consumers.\n* [`XINFO STREAM`](https://redis.io/docs/latest/commands/xinfo-stream),\n  [`XINFO GROUPS`](https://redis.io/docs/latest/commands/xinfo-groups), and\n  [`XINFO CONSUMERS`](https://redis.io/docs/latest/commands/xinfo-consumers) for observability.\n\nSee the [Lettuce guide](https://redis.io/docs/latest/develop/clients/lettuce) for the full client reference, and the [Streams overview](https://redis.io/docs/latest/develop/data-types/streams) for the deeper conceptual model — consumer groups, the PEL, claim semantics, capped streams, and the differences with Kafka partitions."}],"examples":[{"id":"the-event-stream-helper-ex0","language":"java","code":"import io.lettuce.core.RedisClient;\nimport io.lettuce.core.RedisURI;\nimport io.lettuce.core.api.StatefulRedisConnection;\n\nRedisClient client = RedisClient.create(\n        RedisURI.builder().withHost(\"localhost\").withPort(6379).build());\nStatefulRedisConnection<String, String> connection = client.connect();\n\nEventStream stream = new EventStream(\n        connection,\n        \"demo:events:orders\",\n        2000L,    // approximate MAXLEN retention guardrail\n        5000L);   // XAUTOCLAIM idle threshold (ms)\n\n// Producer\nMap<String, String> payload = new LinkedHashMap<>();\npayload.put(\"order_id\", \"o-1234\");\npayload.put(\"customer\", \"alice\");\npayload.put(\"amount\", \"49.50\");\nString streamId = stream.produce(\"order.placed\", payload);\n\n// Consumer group + one consumer\nstream.ensureGroup(\"notifications\", \"0-0\");\nList<EventStream.Entry> entries = stream.consume(\n        \"notifications\", \"worker-a\", 10L, 500L);\nfor (EventStream.Entry entry : entries) {\n    handle(entry.fields);                                     // your processing\n    stream.ack(\"notifications\", List.of(entry.id));           // XACK\n}\n\n// Recover stuck PEL entries by reaping them into a healthy consumer.\n// The textbook pattern: each consumer periodically calls XAUTOCLAIM\n// with itself as the target and processes whatever it claimed.\n// ConsumerWorker.reapIdlePel() wraps that flow; the low-level helper\n// stream.autoclaim(group, target, ...) is also available if you want\n// to drive XAUTOCLAIM directly.\nConsumerWorker.ReapResult result = workerB.reapIdlePel();\n// result.claimed, result.processed, result.deletedIds\n// deletedIds are PEL entries whose payload was already trimmed.\n// Redis 7+ has already removed those slots from the PEL, so no XACK\n// is needed — log them and route to a dead-letter store for audit.\n\n// Replay history (independent of any group's cursor)\nfor (EventStream.Entry entry : stream.replay(\"-\", \"+\", 50L)) {\n    System.out.println(entry.id + \" \" + entry.fields);\n}","section_id":"the-event-stream-helper"},{"id":"data-model-ex0","language":"text","code":"demo:events:orders\n  1716998413541-0   type=order.placed     order_id=o-1234   customer=alice  amount=49.50  ts_ms=...\n  1716998413542-0   type=order.paid       order_id=o-1234   customer=alice  amount=49.50  ts_ms=...\n  1716998413542-1   type=order.shipped    order_id=o-1235   customer=bob    amount=12.00  ts_ms=...\n  ...","section_id":"data-model"},{"id":"producing-events-ex0","language":"java","code":"public List<String> produceBatch(Iterable<Map.Entry<String, Map<String, String>>> events) {\n    RedisAsyncCommands<String, String> async = connection.async();\n    List<RedisFuture<String>> futures = new ArrayList<>();\n    for (Map.Entry<String, Map<String, String>> event : events) {\n        Map<String, String> fields = encodeFields(event.getKey(), event.getValue());\n        XAddArgs args = XAddArgs.Builder.maxlen(maxlenApprox).approximateTrimming();\n        futures.add(async.xadd(streamKey, args, fields));\n    }\n    List<String> ids = new ArrayList<>(futures.size());\n    for (RedisFuture<String> future : futures) {\n        ids.add(future.get());\n    }\n    return ids;\n}","section_id":"producing-events"},{"id":"reading-with-a-consumer-group-ex0","language":"java","code":"public List<Entry> consume(String group, String consumer, long count, long blockMs) {\n    XReadArgs.StreamOffset<String> offset =\n            XReadArgs.StreamOffset.lastConsumed(streamKey);\n    XReadArgs args = XReadArgs.Builder.count(count).block(blockMs);\n    List<StreamMessage<String, String>> raw = connection.sync()\n            .xreadgroup(Consumer.from(group, consumer), args, offset);\n    return toEntries(raw);\n}","section_id":"reading-with-a-consumer-group"},{"id":"acknowledging-entries-ex0","language":"java","code":"public long ack(String group, Collection<String> ids) {\n    if (ids == null || ids.isEmpty()) return 0L;\n    Long n = connection.sync().xack(streamKey, group, ids.toArray(new String[0]));\n    return n == null ? 0L : n;\n}","section_id":"acknowledging-entries"},{"id":"multiple-consumer-groups-one-stream-ex0","language":"java","code":"stream.ensureGroup(\"notifications\", \"0-0\");\nstream.ensureGroup(\"analytics\",     \"0-0\");","section_id":"multiple-consumer-groups-one-stream"},{"id":"recovering-crashed-consumers-with-xautoclaim-ex0","language":"java","code":"public ReapResult reapIdlePel() {\n    EventStream.AutoClaimResult result = stream.autoclaim(\n            group, name, 100L, \"0-0\", 10);\n    int processedThisCall = 0;\n    for (EventStream.Entry entry : result.claimed) {\n        try {\n            handleEntry(entry.id, entry.fields);\n            processedThisCall += 1;\n        } catch (Exception exc) {\n            System.err.printf(\"[%s/%s] reap failed on %s: %s%n\",\n                    group, name, entry.id, exc.getMessage());\n        }\n    }\n    return new ReapResult(\n            result.claimed.size(), processedThisCall, result.deletedIds);\n}","section_id":"recovering-crashed-consumers-with-xautoclaim"},{"id":"recovering-crashed-consumers-with-xautoclaim-ex1","language":"java","code":"public AutoClaimResult autoclaim(\n        String group, String consumer, long pageCount, String startId, int maxPages) {\n    List<Entry> claimedAll = new ArrayList<>();\n    List<String> deletedAll = new ArrayList<>();\n    String cursor = startId == null ? \"0-0\" : startId;\n    String lastCursor = cursor;\n    for (int i = 0; i < maxPages; i++) {\n        AutoClaimPage page = autoclaimPage(group, consumer, pageCount, cursor);\n        claimedAll.addAll(page.claimed);\n        deletedAll.addAll(page.deletedIds);\n        lastCursor = page.nextCursor;\n        if (\"0-0\".equals(page.nextCursor)) break;\n        cursor = page.nextCursor;\n    }\n    return new AutoClaimResult(claimedAll, deletedAll, lastCursor);\n}","section_id":"recovering-crashed-consumers-with-xautoclaim"},{"id":"replay-with-xrange-ex0","language":"java","code":"public List<Entry> replay(String startId, String endId, long count) {\n    Range<String> range = Range.create(\n            startId == null ? \"-\" : startId,\n            endId == null ? \"+\" : endId);\n    List<StreamMessage<String, String>> raw = connection.sync()\n            .xrange(streamKey, range, Limit.from(count));\n    return toEntries(raw);\n}","section_id":"replay-with-xrange"},{"id":"the-consumer-worker-thread-ex0","language":"java","code":"private void run() {\n    while (!stopRequested) {\n        if (paused) {\n            sleepQuietly(50L);\n            continue;\n        }\n        List<EventStream.Entry> entries;\n        try {\n            entries = stream.consume(group, name, 10L, 500L);\n        } catch (Exception exc) {\n            System.err.printf(\"[%s/%s] read failed: %s%n\",\n                    group, name, exc.getMessage());\n            sleepQuietly(500L);\n            continue;\n        }\n        if (entries == null || entries.isEmpty()) continue;\n        for (EventStream.Entry entry : entries) {\n            dispatch(entry.id, entry.fields);\n        }\n    }\n}","section_id":"the-consumer-worker-thread"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir streaming-demo && cd streaming-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/streaming/java-lettuce\ncurl -O $BASE/EventStream.java\ncurl -O $BASE/ConsumerWorker.java\ncurl -O $BASE/DemoServer.java","section_id":"get-the-source-files"},{"id":"get-the-source-files-ex1","language":"bash","code":"mkdir lib && cd lib\nLETTUCE=https://repo1.maven.org/maven2/io/lettuce/lettuce-core/6.5.0.RELEASE\ncurl -O $LETTUCE/lettuce-core-6.5.0.RELEASE.jar\nNETTY=https://repo1.maven.org/maven2/io/netty\nfor ARTIFACT in netty-buffer netty-codec netty-common netty-handler \\\n                netty-resolver netty-transport netty-transport-native-unix-common; do\n  curl -O \"$NETTY/$ARTIFACT/4.1.113.Final/$ARTIFACT-4.1.113.Final.jar\"\ndone\ncurl -O https://repo1.maven.org/maven2/io/projectreactor/reactor-core/3.6.6/reactor-core-3.6.6.jar\ncurl -O https://repo1.maven.org/maven2/org/reactivestreams/reactive-streams/1.0.4/reactive-streams-1.0.4.jar\ncd ..","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"javac -cp 'lib/*' EventStream.java ConsumerWorker.java DemoServer.java\njava -cp '.:lib/*' DemoServer","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"Deleting any existing data at key 'demo:events:orders' for a clean demo run (pass --no-reset to keep it).\nRedis streaming demo server listening on http://127.0.0.1:8784\nUsing Redis at localhost:6379 with stream key 'demo:events:orders' (MAXLEN ~ 2000)\nSeeded 3 consumer(s) across 2 group(s)","section_id":"start-the-demo-server"},{"id":"inspect-the-stream-directly-with-redis-cli-ex0","language":"bash","code":"# Stream summary\nredis-cli XLEN demo:events:orders\nredis-cli XINFO STREAM demo:events:orders\n\n# Group cursors and pending counts\nredis-cli XINFO GROUPS demo:events:orders\n\n# Consumers within a group\nredis-cli XINFO CONSUMERS demo:events:orders notifications\n\n# Pending entries with idle time and delivery count\nredis-cli XPENDING demo:events:orders notifications - + 20\n\n# Tail the stream live (no consumer-group state — like tail -f)\nredis-cli XREAD BLOCK 0 STREAMS demo:events:orders '$'\n\n# Replay a range\nredis-cli XRANGE demo:events:orders - + COUNT 50","section_id":"inspect-the-stream-directly-with-redis-cli"}]}
{"schema_version":2,"id":"develop/use-cases/streaming/nodejs","title":"Redis streaming with node-redis","url":"https://redis.io/docs/latest/develop/use-cases/streaming/nodejs/","summary":"Implement a Redis event-streaming pipeline in Node.js with node-redis","aliases":["/develop/use-cases/streaming/node-redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:55:04+01:00","children":[],"page_type":"content","content_hash":"d81ccfd43db799aec61d3e584c3a05c9ef3fe29625a389a076d2b2af0ecad953","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a Redis-backed event-streaming pipeline in Node.js with [`node-redis`](https://redis.io/docs/latest/develop/clients/nodejs). It includes a small local web server built with the Node.js standard `http` module so you can produce events into a single Redis Stream, watch two independent consumer groups read it at their own pace, and recover stuck deliveries with `XAUTOCLAIM` after simulating a consumer crash."},{"id":"overview","title":"Overview","role":"overview","text":"A Redis Stream is an append-only log of field/value entries with auto-generated, time-ordered IDs. Producers append with [`XADD`](https://redis.io/docs/latest/commands/xadd); consumers belong to *consumer groups* and read with [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup). The group as a whole tracks a single `last-delivered-id` cursor, and each consumer gets its own pending-entries list (PEL) of messages it has been handed but not yet acknowledged. Once a consumer has processed an entry it calls [`XACK`](https://redis.io/docs/latest/commands/xack) to clear the entry from its PEL; entries left unacknowledged past an idle threshold can be reassigned to a healthy consumer with [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim).\n\nThat gives you:\n\n* Ordered, durable history that many independent consumer groups can read at their own pace\n* At-least-once delivery, with per-consumer pending lists and automatic recovery of crashed consumers\n* Horizontal scaling within a group — add a consumer and Redis automatically splits the work\n* Replay of any range with [`XRANGE`](https://redis.io/docs/latest/commands/xrange), independent of consumer-group state\n* Bounded retention through [`XADD MAXLEN ~`](https://redis.io/docs/latest/commands/xadd) or\n  [`XTRIM MINID ~`](https://redis.io/docs/latest/commands/xtrim), without a separate cleanup job\n\nIn this example, producers append order events (`order.placed`, `order.paid`, `order.shipped`, `order.cancelled`) to a single stream at `demo:events:orders`. Two consumer groups read the same stream:\n\n* **`notifications`** — two consumers (`worker-a`, `worker-b`) sharing the work, modelling a fan-out worker pool.\n* **`analytics`** — one consumer (`worker-c`) processing the full event flow on its own."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. The application calls `stream.produce(eventType, payload)` which runs [`XADD`](https://redis.io/docs/latest/commands/xadd) with an approximate [`MAXLEN ~`](https://redis.io/docs/latest/commands/xadd) cap. Redis assigns an auto-generated time-ordered ID.\n2. Each consumer's async loop calls [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) with the special ID `>` (meaning \"deliver entries this group has not yet delivered to anyone\") and a short block timeout.\n3. After processing each entry, the consumer calls [`XACK`](https://redis.io/docs/latest/commands/xack) so Redis can drop it from the group's pending list.\n4. If a consumer is killed (or crashes) before acking, its entries sit in the group's PEL. A periodic [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) sweep reassigns idle entries to a healthy consumer.\n5. Anyone — including code outside the consumer groups — can read history with [`XRANGE`](https://redis.io/docs/latest/commands/xrange) without affecting any group's cursor.\n\nEach consumer group has its own cursor (`last-delivered-id`) and its own pending list, so the two groups in this demo process the same events without coordinating with each other."},{"id":"the-event-stream-helper","title":"The event-stream helper","role":"content","text":"The `EventStream` class wraps the stream operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/streaming/nodejs/eventStream.js)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each event is a single stream entry — a flat object of field/value strings — with an auto-generated time-ordered ID:\n\n[code example]\n\nThe ID is `{milliseconds}-{sequence}`, monotonically increasing within the stream, so you can range-query by approximate wall-clock time without an extra index. (IDs are ordered within a stream, not across streams — two events appended to different streams at the same millisecond can produce the same ID.) The implementation uses:\n\n* [`XADD ... MAXLEN ~ n`](https://redis.io/docs/latest/commands/xadd), pipelined, for batch production with a retention cap\n* [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) with the special ID `>` for fresh deliveries to a consumer\n* [`XACK`](https://redis.io/docs/latest/commands/xack) on every processed entry\n* [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) for sweeping idle pending entries to a healthy consumer\n* [`XRANGE`](https://redis.io/docs/latest/commands/xrange) for replay and audit\n* [`XPENDING`](https://redis.io/docs/latest/commands/xpending) for inspecting the per-group pending list\n* [`XINFO STREAM`](https://redis.io/docs/latest/commands/xinfo-stream),\n  [`XINFO GROUPS`](https://redis.io/docs/latest/commands/xinfo-groups), and\n  [`XINFO CONSUMERS`](https://redis.io/docs/latest/commands/xinfo-consumers) for surface-level observability\n* [`XTRIM`](https://redis.io/docs/latest/commands/xtrim) for explicit retention enforcement"},{"id":"producing-events","title":"Producing events","role":"content","text":"`produceBatch` pipelines `XADD` calls in a single round trip. Each call carries an approximate `MAXLEN ~` cap so the stream stays bounded as it rolls forward:\n\n[code example]\n\nThe `~` flavour of `MAXLEN` lets Redis trim at a macro-node boundary, which is much cheaper than exact trimming and is what you want when the cap is a retention *guardrail*, not a hard size constraint. With 300 events produced and `MAXLEN ~ 50`, you might end up with 100 entries left — Redis released the oldest whole macro-node and stopped. The next `XADD` will keep length stable.\n\nIf you genuinely need an exact cap (rare), use `strategyModifier: \"=\"` instead of `\"~\"`. The performance difference is significant on busy streams."},{"id":"reading-with-a-consumer-group","title":"Reading with a consumer group","role":"content","text":"Each consumer in a group runs the same `XREADGROUP` loop. The special ID `>` means \"deliver entries this group has not yet delivered to *anyone*\":\n\n[code example]\n\n`blockMs` makes the call efficient even when the stream is idle: the client parks on the server until either an entry arrives or the timeout expires, so consumers don't busy-loop. Because `XREADGROUP BLOCK` ties up the underlying socket until the server replies, each consumer in this demo uses its own Redis client (a duplicated connection) so its blocking read never serialises behind another worker's read or behind an HTTP-handler command.\n\nReading with an explicit ID like `0-0` instead of `>` does something different — it replays entries already delivered to *this* consumer name (its private PEL). That is the canonical recovery path when the same consumer restarts: catch up on its own pending entries first, then resume reading new ones."},{"id":"acknowledging-entries","title":"Acknowledging entries","role":"content","text":"Once the consumer has processed an entry, `XACK` tells Redis it can drop the entry from the group's pending list:\n\n[code example]\n\nThis is the linchpin of at-least-once delivery: an entry that is never acked stays in the PEL until a claim moves it elsewhere. If your consumer crashes between processing and ack, the next claim sweep picks the entry back up. The one caveat is retention: `XADD MAXLEN ~` and `XTRIM` can release the entry's *payload* even while its ID is still in the PEL. The next `XAUTOCLAIM` returns those IDs in its `deletedMessages` list and removes them from the PEL inside the same command — the entry cannot be retried, so the caller should log it and route to a dead-letter store for audit. The example handles this explicitly in `autoclaim` further down.\n\nThe trade-off is the opposite of pub/sub: a slow or crashed consumer doesn't lose messages, but it does mean your downstream system must be idempotent. If you process an order twice because the first attempt died after the side effect but before the ack, the second attempt must be safe."},{"id":"multiple-consumer-groups-one-stream","title":"Multiple consumer groups, one stream","role":"content","text":"The big difference between Redis Streams and a job queue is that any number of independent consumer groups can read the same stream. The demo sets up two groups on `demo:events:orders`:\n\n[code example]\n\nEach group has its own cursor. Producing 5 events results in `notifications` and `analytics` each receiving all 5, with no coordination between them. Within `notifications`, the work is split across `worker-a` and `worker-b`: Redis hands each `XREADGROUP` call whatever entries are not yet delivered to anyone in the group, so adding a second worker doubles throughput without any rebalance logic.\n\nThe `\"0-0\"` second argument means \"deliver everything in the stream from the beginning\" — useful in a demo and for fresh groups bootstrapping from history. In production, a brand-new group reading a long-existing stream usually starts at `\"$\"` (\"only events after this point\") and uses [`XRANGE`](https://redis.io/docs/latest/commands/xrange) explicitly if it needs history."},{"id":"recovering-crashed-consumers-with-xautoclaim","title":"Recovering crashed consumers with XAUTOCLAIM","role":"content","text":"The demo's \"Crash next 3\" button tells a chosen consumer to drop its next three deliveries on the floor without acking them — the same effect as a worker process dying mid-message. Those entries stay in the group's PEL with their delivery counter incremented. Once they have been idle for at least `claimMinIdleMs`, any healthy consumer in the group can rescue them by calling `XAUTOCLAIM` *with itself as the target*. `ConsumerWorker.reapIdlePel` wraps that pattern:\n\n[code example]\n\nThe underlying `stream.autoclaim` helper pages through the group's PEL with `XAUTOCLAIM`'s continuation cursor:\n\n[code example]\n\nA single `XAUTOCLAIM` call scans up to `pageCount` PEL entries starting at `startId`, reassigns the ones idle for at least `claimMinIdleMs` to the named consumer, and returns a continuation cursor in `reply.nextId`. For a full sweep, loop until the cursor returns to `\"0-0\"` (with a `maxPages` safety net so one call cannot monopolise a very large PEL). The delivery counter is incremented on every claim — after a few cycles you can use it to spot a *poison-pill* message that crashes every consumer that touches it, and route it to a dead-letter stream so the bad entry stops cycling. (New entries keep flowing past the poison pill — `XREADGROUP >` still delivers fresh work — but the bad entry's repeated reclaim wastes consumer time and keeps the PEL larger than it needs to be.)\n\nThe `deletedMessages` list contains PEL entry IDs whose stream payload was already trimmed by the time the claim ran (typically because `MAXLEN ~` retention outran a slow consumer). `XAUTOCLAIM` removes those dangling slots from the PEL itself, so the caller does *not* need to `XACK` them — but the entries cannot be retried either, so log and route them to a dead-letter store for offline inspection. Redis 7.0 introduced this third return element; the example requires Redis 7.0+ for that reason.\n\n`reapIdlePel` is the right primitive for the recovery path because it claims and processes in one step: every entry the call returned is now in *this* consumer's PEL, so the same consumer is responsible for processing and acking it. In production each consumer runs `reapIdlePel` periodically (every few seconds, on a timer) so a crashed peer's entries never sit invisibly. The demo exposes it as a manual button so you can trigger the reap after waiting for the idle threshold.\n\n`XCLAIM` (singular, no auto) does the same thing for a specific list of entry IDs you already have in hand — useful when you want to take ownership of one known stuck entry, or when you need to move a specific consumer's PEL to a peer (the case the demo's \"Remove consumer\" button handles via `handoverPending`). `XAUTOCLAIM` cannot filter by source consumer, so it cannot be used for a per-consumer handover."},{"id":"replay-with-xrange","title":"Replay with XRANGE","role":"content","text":"`XRANGE` reads a slice of history. It is completely independent of any consumer group — no cursors move, no acks happen — so it is safe to call any number of times, from any process:\n\n[code example]\n\nThe special IDs `-` and `+` mean \"from the very beginning\" and \"to the very end\". You can also pass real IDs (`1716998413541-0`) or just the millisecond part (`1716998413541`, which Redis interprets as \"any entry with this timestamp\").\n\nTypical uses:\n\n* **Bootstrapping a new projection** — read the entire stream from `-` and build a derived view in another store (a search index, a SQL table, a different cache). Doing this against a consumer group would consume the entries; `XRANGE` lets you do it without disrupting live consumers.\n* **Auditing recent activity** — read the last few minutes by ID range without touching any group cursor.\n* **Debugging** — fetch one specific entry by its ID, or a tight range around an incident timestamp, to see exactly what producers wrote."},{"id":"the-consumer-worker-thread","title":"The consumer worker thread","role":"content","text":"`ConsumerWorker` wraps the `XREADGROUP` → process → `XACK` loop in an async task\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/streaming/nodejs/consumerWorker.js)):\n\n[code example]\n\n`_dispatch` wraps the per-entry handling in a try/catch so a failure (typically `XACK` against Redis) doesn't kill the loop. The actual handler either acks (the normal path) or, when the demo has asked the worker to \"crash\", drops the entry on the floor and increments a counter so the UI can show what is currently in the PEL waiting to be claimed.\n\nNode.js is single-threaded for JavaScript execution, so the worker \"thread\" is really an async task on the event loop. That's fine for the demo — concurrent `XREADGROUP` calls from multiple workers run interleaved, each parked on its own Redis client until either a message arrives or the BLOCK timeout expires. Two architectural choices follow from the single connection-per-blocking-call model:\n\n* Each worker owns its **own duplicated Redis client** dedicated to `XREADGROUP BLOCK`. node-redis serialises commands on a single connection, so sharing one client across workers (or with the HTTP handlers) would serialise their reads through that socket.\n* All other commands (`XACK`, `XAUTOCLAIM`, `XCLAIM`, `XPENDING`, `XADD`) flow through a shared `EventStream` so the produced/acked/claimed counters aggregate across workers.\n\nRecovery of stuck PEL entries — this consumer's, after a restart, or another consumer's, after a crash — runs through a separate `reapIdlePel` method rather than the read loop. That method calls `XAUTOCLAIM` with this consumer as the target, then processes whatever was claimed in the same flow as new entries. This is the textbook Streams pattern: each consumer is its own reaper, running `XAUTOCLAIM(self)` periodically (or on demand) so a crashed peer's entries never sit invisibly in the PEL. The demo's \"XAUTOCLAIM to selected\" button calls `reapIdlePel` on the chosen consumer; in production you would run it from a timer every few seconds.\n\nNote that the worker's main read loop deliberately does *not* call `XREADGROUP 0` to drain its own PEL on every iteration. That would re-deliver every pending entry continuously and *reset its idle counter to zero* each time, which would keep crashed entries below the `XAUTOCLAIM` threshold forever. Using `XAUTOCLAIM(self)` as the recovery primitive — which only fires for entries idle longer than `claimMinIdleMs` — avoids that whole class of bug.\n\nThe pause and crash levers exist only for the demo. A real consumer is just the read-process-ack loop — everything else in this class is instrumentation."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* Redis 7.0 or later. `XAUTOCLAIM` was added in Redis 6.2, but its reply gained a third\n  element (the list of deleted IDs) in 7.0; the example relies on that shape.\n* Node.js 18 or later.\n* The `node-redis` 5.x client. The demo's `package.json` declares it as a dependency:\n\n  [code example]\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of three JavaScript files plus `package.json`. Download them from the [`nodejs` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/streaming/nodejs) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From that directory:\n\n[code example]\n\nYou should see:\n\n[code example]\n\nBy default the demo wipes the configured stream key on startup so each run starts from a clean state. Pass `--no-reset` to keep any existing data at the key (useful when re-running against the same stream to inspect prior state), or `--stream-key <name>` to point the demo at a different key entirely.\n\nOpen [http://127.0.0.1:8083](http://127.0.0.1:8083) in a browser. You can:\n\n* **Produce** any number of events of a chosen type (or random types). Watch the stream length grow and the tail update.\n* See each **consumer group**: its `last-delivered-id`, the size of its pending list, and the consumers in it. Each consumer shows its processed count, pending count, and idle time.\n* **Add or remove** consumers within a group at runtime to see Redis split the work across the new shape.\n* Click **Crash next 3** on a consumer to drop its next three deliveries — the same effect as a worker process dying after `XREADGROUP` but before `XACK`. Watch the **Pending entries (XPENDING)** panel fill up.\n* Wait until the idle time exceeds the threshold (default 5000 ms), pick a healthy target consumer, and click **XAUTOCLAIM to selected** — the stuck entries are reassigned and the delivery counter increments.\n* **Replay (XRANGE)** any range to confirm the full history is independent of consumer-group state.\n* **XTRIM** with an approximate `MAXLEN` to bound retention. Note that an approximate trim only releases whole macro-nodes — `MAXLEN ~ 50` on a small stream may not delete anything; on a 300-entry stream it typically lands at around 100.\n* Click **Reset demo** to drop the stream and re-seed the default groups."},{"id":"production-usage","title":"Production usage","role":"content","text":""},{"id":"pick-retention-by-length-or-by-minimum-id","title":"Pick retention by length or by minimum ID","role":"content","text":"The demo uses `MAXLEN ~` on every `XADD`. Two alternatives are worth considering:\n\n* `MINID ~ <id>` — keep only entries newer than an ID. If you want \"the last 24 hours\", compute the wall-clock cutoff and pass `XTRIM MINID ~ <ms>-0`. This is the right pattern when retention is time-bounded.\n* No cap on `XADD` plus a periodic `XTRIM` job — useful if your producer is hot and the per-`XADD` work has to stay minimal, or if retention rules are complex (a separate process can also factor in consumer-group lag).\n\nIn all three cases the trimming is approximate by default. Use exact trimming (`MAXLEN n` or `MINID id` without `~`) only when you genuinely need an exact count."},{"id":"dont-let-consumer-group-lag-silently-grow","title":"Don't let consumer-group lag silently grow","role":"content","text":"`XINFO GROUPS` reports each group's `lag` (entries the group has not yet read) and `pending` (entries delivered but not acked). In production, alert on either of these crossing a threshold — a steadily growing pending count usually means consumers are crashing without `XAUTOCLAIM` running, and a growing lag means consumers can't keep up with producers.\n\nThe same applies inside a group: `XINFO CONSUMERS` reports per-consumer pending counts and idle times, so you can spot one slow consumer holding entries that the rest of the group is waiting on."},{"id":"make-consumer-logic-idempotent","title":"Make consumer logic idempotent","role":"content","text":"`XAUTOCLAIM` can re-deliver an entry to a different consumer after a crash. If your processing has side effects (sending email, charging a card, updating a downstream store), make sure the same entry processed twice gives the same result — use an idempotency key, an upsert with conditional check, or a once-per-id guard table. Redis Streams cannot give you exactly-once semantics on its own."},{"id":"bound-the-delivery-counter-as-a-poison-pill-signal","title":"Bound the delivery counter as a poison-pill signal","role":"content","text":"`XPENDING` returns each entry's delivery count, incremented on every claim. If an entry has been delivered (and dropped) several times, the next consumer is unlikely to fare better. After some threshold — `deliveries >= 5`, say — route the entry to a *dead-letter stream*, ack it on the original group, and alert. New entries keep flowing past a poison pill (`XREADGROUP >` still delivers fresh work), but the bad entry's repeated reclaim wastes consumer time and keeps the PEL bigger than it needs to be — without a DLQ threshold it can also slowly trip retention/lag alerts."},{"id":"partition-by-tenant-or-entity-for-scale","title":"Partition by tenant or entity for scale","role":"content","text":"A single Redis Stream is a single key, and on a Redis Cluster a single key lives on a single shard. If your throughput exceeds what one shard can handle, partition the stream — for example by tenant ID (`events:orders:{tenant_a}`, `events:orders:{tenant_b}`) — so different tenants land on different shards. Hash-tags (`{tenant_a}`) keep all related streams on the same shard if you need to multi-stream atomically.\n\nPer-entity partitioning (`events:order:{order_id}`) is the canonical pattern when you treat each entity's stream as the event-sourcing log for that entity: every state change for one order goes on its own stream, which is also bounded in size by the entity's lifetime."},{"id":"use-a-separate-consumer-pool-per-group","title":"Use a separate consumer pool per group","role":"content","text":"The demo runs every consumer in one Node.js process. In production each consumer group is usually its own deployment — its own pool of pods or VMs — so a slow projection in `analytics` cannot pull `notifications` workers off their stream. Each pod runs one consumer per CPU core (or as many async loops as you can fit on the event loop without throughput-limiting on per-entry processing), with `XAUTOCLAIM` either embedded in the consumer loop (every N reads, claim idle entries to self) or run by a separate reaper."},{"id":"one-redis-client-per-blocking-xreadgroup-call","title":"One Redis client per blocking `XREADGROUP` call","role":"content","text":"`XREADGROUP BLOCK` parks the underlying connection until either an entry arrives or the BLOCK timeout expires. node-redis sends commands on a single TCP connection in arrival order, so sharing one client across multiple consumers (or with HTTP-handler commands) serialises every command behind any in-flight blocking read. The demo gives each worker its own duplicated client for the blocking read, and routes `XACK` / `XAUTOCLAIM` / `XCLAIM` / `XADD` / inspection commands through a shared client. The same pattern (or a connection pool that hands out one connection per blocking call) applies to any production deployment."},{"id":"dont-read-with-xread-no-group-and-then-try-to-ack","title":"Don't read with XREAD (no group) and then try to ack","role":"content","text":"`XREAD` and `XREADGROUP` are different mechanisms. `XREAD` is a tail-the-log read with no consumer-group state — entries are not added to any PEL, and you cannot `XACK` them. If you want at-least-once delivery and crash recovery, you must read through a consumer group.\n\n`XREAD` is still useful for read-only tail clients (a UI streaming events, a debugger, a `tail -f`-style command-line tool). It's just not part of the at-least-once path."},{"id":"inspect-the-stream-directly-with-redis-cli","title":"Inspect the stream directly with redis-cli","role":"content","text":"When testing or troubleshooting, inspect the stream directly to confirm the consumer state is what you expect:\n\n[code example]\n\nIf a group's `lag` is growing while consumers' `idle` times are short, consumers are healthy but producers are outpacing them — add more consumers. If `pending` is growing while `lag` is small, consumers are *receiving* entries but not *acking* them — either they are crashing mid-message or your acking logic has a bug."},{"id":"learn-more","title":"Learn more","role":"related","text":"This example uses the following Redis commands:\n\n* [`XADD`](https://redis.io/docs/latest/commands/xadd) to append an event with an approximate `MAXLEN` cap.\n* [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) to read new entries for a consumer in a group.\n* [`XACK`](https://redis.io/docs/latest/commands/xack) to acknowledge a processed entry.\n* [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) to reassign idle pending entries to a healthy consumer.\n* [`XCLAIM`](https://redis.io/docs/latest/commands/xclaim) to take ownership of a specific list of pending entry IDs by hand (used by `handoverPending` to move a leaving consumer's PEL to a peer, since `XAUTOCLAIM` has no source-consumer filter).\n* [`XRANGE`](https://redis.io/docs/latest/commands/xrange) for replay and audit, independent of consumer-group state.\n* [`XPENDING`](https://redis.io/docs/latest/commands/xpending) to inspect the per-group pending list with idle times and delivery counts.\n* [`XTRIM`](https://redis.io/docs/latest/commands/xtrim) for explicit retention enforcement.\n* [`XGROUP CREATE`](https://redis.io/docs/latest/commands/xgroup-create) and\n  [`XGROUP DELCONSUMER`](https://redis.io/docs/latest/commands/xgroup-delconsumer) to manage groups and consumers.\n* [`XINFO STREAM`](https://redis.io/docs/latest/commands/xinfo-stream),\n  [`XINFO GROUPS`](https://redis.io/docs/latest/commands/xinfo-groups), and\n  [`XINFO CONSUMERS`](https://redis.io/docs/latest/commands/xinfo-consumers) for observability.\n\nSee the [`node-redis` documentation](https://redis.io/docs/latest/develop/clients/nodejs) for the full client reference, and the [Streams overview](https://redis.io/docs/latest/develop/data-types/streams) for the deeper conceptual model — consumer groups, the PEL, claim semantics, capped streams, and the differences with Kafka partitions."}],"examples":[{"id":"the-event-stream-helper-ex0","language":"javascript","code":"const { createClient } = require(\"redis\");\nconst { EventStream } = require(\"./eventStream\");\n\nconst client = createClient({ socket: { host: \"localhost\", port: 6379 } });\nawait client.connect();\n\nconst stream = new EventStream({\n  redisClient: client,\n  streamKey: \"demo:events:orders\",\n  maxlenApprox: 2000,        // retention guardrail\n  claimMinIdleMs: 5000,      // XAUTOCLAIM threshold\n});\n\n// Producer\nconst streamId = await stream.produce(\"order.placed\", {\n  order_id: \"o-1234\",\n  customer: \"alice\",\n  amount: \"49.50\",\n});\n\n// Consumer group + one consumer\nawait stream.ensureGroup(\"notifications\", \"0-0\");\nconst entries = await stream.consume(\"notifications\", \"worker-a\", 10, 500);\nfor (const [entryId, fields] of entries) {\n  handle(fields);                                    // your processing\n  await stream.ack(\"notifications\", [entryId]);      // XACK\n}\n\n// Recover stuck PEL entries by reaping them into a healthy consumer.\n// The textbook pattern: each consumer periodically calls XAUTOCLAIM\n// with itself as the target and processes whatever it claimed.\n// `ConsumerWorker.reapIdlePel` wraps that flow; the low-level helper\n// `stream.autoclaim(group, targetName)` is also available if you\n// want to drive XAUTOCLAIM directly.\nconst result = await workerB.reapIdlePel();\n// result === { claimed: N, processed: M, deletedIds: [...] }\n// deletedIds are PEL entries whose payload was already trimmed.\n// Redis 7+ has already removed those slots from the PEL, so no XACK\n// is needed — log them and route to a dead-letter store for audit.\n\n// Replay history (independent of any group's cursor)\nfor (const [entryId, fields] of await stream.replay(\"-\", \"+\", 50)) {\n  console.log(entryId, fields);\n}","section_id":"the-event-stream-helper"},{"id":"data-model-ex0","language":"text","code":"demo:events:orders\n  1716998413541-0   type=order.placed     order_id=o-1234   customer=alice  amount=49.50  ts_ms=...\n  1716998413542-0   type=order.paid       order_id=o-1234   customer=alice  amount=49.50  ts_ms=...\n  1716998413542-1   type=order.shipped    order_id=o-1235   customer=bob    amount=12.00  ts_ms=...\n  ...","section_id":"data-model"},{"id":"producing-events-ex0","language":"javascript","code":"async produceBatch(events) {\n  const list = Array.from(events);\n  if (list.length === 0) return [];\n  const pipe = this.redis.multi();\n  for (const [eventType, payload] of list) {\n    const fields = EventStream._encodeFields(eventType, payload);\n    pipe.xAdd(this.streamKey, \"*\", fields, {\n      TRIM: {\n        strategy: \"MAXLEN\",\n        strategyModifier: \"~\",\n        threshold: this.maxlenApprox,\n      },\n    });\n  }\n  // execAsPipeline sends the commands in one round trip without\n  // wrapping them in MULTI/EXEC.\n  const ids = await pipe.execAsPipeline();\n  this._producedTotal += ids.length;\n  return ids.map((id) => String(id));\n}","section_id":"producing-events"},{"id":"reading-with-a-consumer-group-ex0","language":"javascript","code":"async consume(group, consumer, count = 10, blockMs = 500) {\n  const raw = await this.redis.xReadGroup(\n    group,\n    consumer,\n    [{ key: this.streamKey, id: \">\" }],\n    { COUNT: count, BLOCK: blockMs },\n  );\n  return flattenEntries(raw);\n}","section_id":"reading-with-a-consumer-group"},{"id":"acknowledging-entries-ex0","language":"javascript","code":"async ack(group, ids) {\n  const idList = Array.from(ids);\n  if (idList.length === 0) return 0;\n  const n = Number(await this.redis.xAck(this.streamKey, group, idList));\n  this._ackedTotal += n;\n  return n;\n}","section_id":"acknowledging-entries"},{"id":"multiple-consumer-groups-one-stream-ex0","language":"javascript","code":"await stream.ensureGroup(\"notifications\", \"0-0\");\nawait stream.ensureGroup(\"analytics\",     \"0-0\");","section_id":"multiple-consumer-groups-one-stream"},{"id":"recovering-crashed-consumers-with-xautoclaim-ex0","language":"javascript","code":"async reapIdlePel() {\n  const { claimed, deletedIds } = await this.stream.autoclaim(\n    this.group,\n    this.name,\n    { pageCount: 100, maxPages: 10 },\n  );\n  let processed = 0;\n  for (const [entryId, fields] of claimed) {\n    try {\n      if (this.processLatencyMs) {\n        await sleep(this.processLatencyMs);\n      }\n      await this._handleEntry(entryId, fields);\n      processed += 1;\n    } catch (err) {\n      console.error(`reap failed on ${entryId}: ${err.message}`);\n    }\n  }\n  this._reaped += processed;\n  return { claimed: claimed.length, deletedIds, processed };\n}","section_id":"recovering-crashed-consumers-with-xautoclaim"},{"id":"recovering-crashed-consumers-with-xautoclaim-ex1","language":"javascript","code":"async autoclaim(group, consumer, options = {}) {\n  const { pageCount = 100, startId = \"0-0\", maxPages = 10 } = options;\n  const claimedAll = [];\n  const deletedAll = [];\n  let cursor = startId;\n  for (let i = 0; i < maxPages; i += 1) {\n    const reply = await this.redis.xAutoClaim(\n      this.streamKey, group, consumer,\n      this.claimMinIdleMs, cursor, { COUNT: pageCount },\n    );\n    for (const entry of reply.messages || []) {\n      const tuple = toTuple(entry);\n      if (tuple) claimedAll.push(tuple);\n    }\n    for (const id of reply.deletedMessages || []) {\n      deletedAll.push(String(id));\n    }\n    const nextId = String(reply.nextId || \"0-0\");\n    if (nextId === \"0-0\") break;\n    cursor = nextId;\n  }\n  this._claimedTotal += claimedAll.length;\n  return { claimed: claimedAll, deletedIds: deletedAll };\n}","section_id":"recovering-crashed-consumers-with-xautoclaim"},{"id":"replay-with-xrange-ex0","language":"javascript","code":"async replay(startId = \"-\", endId = \"+\", count = 100) {\n  const raw = await this.redis.xRange(this.streamKey, startId, endId, {\n    COUNT: count,\n  });\n  const out = [];\n  for (const entry of raw || []) {\n    const tuple = toTuple(entry);\n    if (tuple) out.push(tuple);\n  }\n  return out;\n}","section_id":"replay-with-xrange"},{"id":"the-consumer-worker-thread-ex0","language":"javascript","code":"async _run() {\n  while (!this._stopped) {\n    if (this._paused) {\n      await sleep(50);\n      continue;\n    }\n\n    let entries;\n    try {\n      // Use the dedicated blocking client so the shared client stays\n      // free for HTTP-handler commands.\n      entries = await this.blockingStream.consume(\n        this.group, this.name, 10, 500,\n      );\n    } catch (err) {\n      console.error(`[${this.group}/${this.name}] read failed: ${err.message}`);\n      await sleep(500);\n      continue;\n    }\n\n    for (const [entryId, fields] of entries) {\n      if (this._stopped) break;\n      await this._dispatch(entryId, fields);\n    }\n  }\n  this._runPromise = null;\n}","section_id":"the-consumer-worker-thread"},{"id":"prerequisites-ex0","language":"bash","code":"npm install","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir streaming-demo && cd streaming-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/streaming/nodejs\ncurl -O $BASE/package.json\ncurl -O $BASE/eventStream.js\ncurl -O $BASE/consumerWorker.js\ncurl -O $BASE/demoServer.js","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"npm install\nnode demoServer.js","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"Deleting any existing data at key 'demo:events:orders' for a clean demo run (pass --no-reset to keep it).\nRedis streaming demo server listening on http://127.0.0.1:8083\nUsing Redis at localhost:6379 with stream key 'demo:events:orders' (MAXLEN ~ 2000)\nSeeded 3 consumer(s) across 2 group(s)","section_id":"start-the-demo-server"},{"id":"inspect-the-stream-directly-with-redis-cli-ex0","language":"bash","code":"# Stream summary\nredis-cli XLEN demo:events:orders\nredis-cli XINFO STREAM demo:events:orders\n\n# Group cursors and pending counts\nredis-cli XINFO GROUPS demo:events:orders\n\n# Consumers within a group\nredis-cli XINFO CONSUMERS demo:events:orders notifications\n\n# Pending entries with idle time and delivery count\nredis-cli XPENDING demo:events:orders notifications - + 20\n\n# Tail the stream live (no consumer-group state — like tail -f)\nredis-cli XREAD BLOCK 0 STREAMS demo:events:orders '$'\n\n# Replay a range\nredis-cli XRANGE demo:events:orders - + COUNT 50","section_id":"inspect-the-stream-directly-with-redis-cli"}]}
{"schema_version":2,"id":"develop/use-cases/streaming/php","title":"Redis streaming with Predis","url":"https://redis.io/docs/latest/develop/use-cases/streaming/php/","summary":"Implement a Redis event-streaming pipeline in PHP with Predis","aliases":["/develop/use-cases/streaming/predis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:55:04+01:00","children":[],"page_type":"content","content_hash":"df3d2fc1f594e72d51716180c80762701aa25309b30dee4dff380721e302cac7","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a Redis-backed event-streaming pipeline in PHP with [Predis](https://github.com/predis/predis). It includes a small local web server built on PHP's built-in dev server so you can produce events into a single Redis Stream, watch two independent consumer groups read it at their own pace, and recover stuck deliveries with `XAUTOCLAIM` after simulating a consumer crash."},{"id":"overview","title":"Overview","role":"overview","text":"A Redis Stream is an append-only log of field/value entries with auto-generated, time-ordered IDs. Producers append with [`XADD`](https://redis.io/docs/latest/commands/xadd); consumers belong to *consumer groups* and read with [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup). The group as a whole tracks a single `last-delivered-id` cursor, and each consumer gets its own pending-entries list (PEL) of messages it has been handed but not yet acknowledged. Once a consumer has processed an entry it calls [`XACK`](https://redis.io/docs/latest/commands/xack) to clear the entry from its PEL; entries left unacknowledged past an idle threshold can be reassigned to a healthy consumer with [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim).\n\nThat gives you:\n\n* Ordered, durable history that many independent consumer groups can read at their own pace\n* At-least-once delivery, with per-consumer pending lists and automatic recovery of crashed consumers\n* Horizontal scaling within a group — add a consumer and Redis automatically splits the work\n* Replay of any range with [`XRANGE`](https://redis.io/docs/latest/commands/xrange), independent of consumer-group state\n* Bounded retention through [`XADD MAXLEN ~`](https://redis.io/docs/latest/commands/xadd) or\n  [`XTRIM MINID ~`](https://redis.io/docs/latest/commands/xtrim), without a separate cleanup job\n\nIn this example, producers append order events (`order.placed`, `order.paid`, `order.shipped`, `order.cancelled`) to a single stream at `demo:events:orders`. Two consumer groups read the same stream:\n\n* **`notifications`** — two consumers (`worker-a`, `worker-b`) sharing the work, modelling a fan-out worker pool.\n* **`analytics`** — one consumer (`worker-c`) processing the full event flow on its own.\n\nThis port is **structurally different from the other clients in this use case**: every other client runs its consumers as in-process threads or async tasks, but PHP's built-in `php -S` development server runs each HTTP request in a brand-new short-lived process, so an in-process consumer would die as soon as the request that started it returned. The helper sidesteps that by spawning each consumer as a detached OS process and keeping every piece of cross-request state in Redis. See [Production usage](#production-usage) for the longer story."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. The application calls `$stream->produce($eventType, $payload)` which runs [`XADD`](https://redis.io/docs/latest/commands/xadd) with an approximate [`MAXLEN ~`](https://redis.io/docs/latest/commands/xadd) cap. Redis assigns an auto-generated time-ordered ID.\n2. Each consumer process loops on [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) with the special ID `>` (meaning \"deliver entries this group has not yet delivered to anyone\") and a short block timeout.\n3. After processing each entry, the consumer calls [`XACK`](https://redis.io/docs/latest/commands/xack) so Redis can drop it from the group's pending list.\n4. If a consumer is killed (or crashes) before acking, its entries sit in the group's PEL. A periodic [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) sweep reassigns idle entries to a healthy consumer.\n5. Anyone — including code outside the consumer groups — can read history with [`XRANGE`](https://redis.io/docs/latest/commands/xrange) without affecting any group's cursor.\n\nEach consumer group has its own cursor (`last-delivered-id`) and its own pending list, so the two groups in this demo process the same events without coordinating with each other."},{"id":"the-event-stream-helper","title":"The event-stream helper","role":"content","text":"The `EventStream` class wraps the stream operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/streaming/php/EventStream.php)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each event is a single stream entry — a flat dict of field/value strings — with an auto-generated time-ordered ID:\n\n[code example]\n\nThe ID is `{milliseconds}-{sequence}`, monotonically increasing within the stream, so you can range-query by approximate wall-clock time without an extra index. (IDs are ordered within a stream, not across streams — two events appended to different streams at the same millisecond can produce the same ID.)\n\nThe PHP port also keeps a small per-process bookkeeping keyspace under `demo:streaming:*` so every fresh `php -S` request can find the running consumers:\n\n[code example]\n\nThe implementation uses:\n\n* [`XADD ... MAXLEN ~ n`](https://redis.io/docs/latest/commands/xadd), pipelined, for batch production with a retention cap\n* [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) with the special ID `>` for fresh deliveries to a consumer\n* [`XACK`](https://redis.io/docs/latest/commands/xack) on every processed entry\n* [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) for sweeping idle pending entries to a healthy consumer\n* [`XCLAIM`](https://redis.io/docs/latest/commands/xclaim) for handing one consumer's PEL over to a peer before removing it\n* [`XRANGE`](https://redis.io/docs/latest/commands/xrange) for replay and audit\n* [`XPENDING`](https://redis.io/docs/latest/commands/xpending) for inspecting the per-group pending list\n* [`XINFO STREAM`](https://redis.io/docs/latest/commands/xinfo-stream),\n  [`XINFO GROUPS`](https://redis.io/docs/latest/commands/xinfo-groups), and\n  [`XINFO CONSUMERS`](https://redis.io/docs/latest/commands/xinfo-consumers) for surface-level observability\n* [`XTRIM`](https://redis.io/docs/latest/commands/xtrim) for explicit retention enforcement"},{"id":"producing-events","title":"Producing events","role":"content","text":"`produceBatch()` pipelines `XADD` calls in a single round trip. Each call carries an approximate `MAXLEN ~` cap so the stream stays bounded as it rolls forward:\n\n[code example]\n\nThe `~` flavour of `MAXLEN` lets Redis trim at a macro-node boundary, which is much cheaper than exact trimming and is what you want when the cap is a retention *guardrail*, not a hard size constraint. With 300 events produced and `MAXLEN ~ 50`, you might end up with 100 entries left — Redis released the oldest whole macro-node and stopped. The next `XADD` will keep length stable.\n\nIf you genuinely need an exact cap (rare), drop the `~` from the `trim` array. The performance difference is significant on busy streams.\n\nPredis 3.x's `xadd()` takes the fields as an **associative array** in the second argument and the trim options as an `['trim' => ['MAXLEN', '~', n]]` entry in the fourth — a different shape from `hset()`, which takes its fields variadically. Skim the [Predis stream tests](https://github.com/predis/predis/tree/main/tests/Predis/Command/Redis) if you need to confirm the signature for a command this guide doesn't show."},{"id":"reading-with-a-consumer-group","title":"Reading with a consumer group","role":"content","text":"Each consumer runs the same `XREADGROUP` loop. The special ID `>` means \"deliver entries this group has not yet delivered to *anyone*\":\n\n[code example]\n\n`blockMs` makes the call efficient even when the stream is idle: the client parks on the server until either an entry arrives or the timeout expires, so consumers don't busy-loop.\n\nReading with an explicit ID like `0-0` instead of `>` does something different — it replays entries already delivered to *this* consumer name (its private PEL). That is the canonical recovery path when the same consumer restarts: catch up on its own pending entries first, then resume reading new ones. The helper exposes that as `consumeOwnPel()`."},{"id":"acknowledging-entries","title":"Acknowledging entries","role":"content","text":"Once the consumer has processed an entry, `XACK` tells Redis it can drop the entry from the group's pending list. Predis 3.x's `xack()` takes the IDs **variadically** rather than as a single array, so the helper unpacks the list with `...`:\n\n[code example]\n\nThis is the linchpin of at-least-once delivery: an entry that is never acked stays in the PEL until a claim moves it elsewhere. If your consumer process crashes between processing and ack, the next claim sweep picks the entry back up. The one caveat is retention: `XADD MAXLEN ~` and `XTRIM` can release the entry's *payload* even while its ID is still in the PEL. The next `XAUTOCLAIM` returns those IDs in its `deletedIds` list and removes them from the PEL inside the same command — the entry cannot be retried, so the caller should log it and route to a dead-letter store for audit.\n\nThe trade-off is the opposite of pub/sub: a slow or crashed consumer doesn't lose messages, but it does mean your downstream system must be idempotent. If you process an order twice because the first attempt died after the side effect but before the ack, the second attempt must be safe."},{"id":"multiple-consumer-groups-one-stream","title":"Multiple consumer groups, one stream","role":"content","text":"The big difference between Redis Streams and a job queue is that any number of independent consumer groups can read the same stream. The demo sets up two groups on `demo:events:orders`:\n\n[code example]\n\nEach group has its own cursor. Producing 5 events results in `notifications` and `analytics` each receiving all 5, with no coordination between them. Within `notifications`, the work is split across `worker-a` and `worker-b`: Redis hands each `XREADGROUP` call whatever entries are not yet delivered to anyone in the group, so adding a second worker doubles throughput without any rebalance logic.\n\nThe `'0-0'` argument means \"deliver everything in the stream from the beginning\" — useful in a demo and for fresh groups bootstrapping from history. In production, a brand-new group reading a long-existing stream usually starts at `$` (\"only events after this point\") and uses [`XRANGE`](https://redis.io/docs/latest/commands/xrange) explicitly if it needs history."},{"id":"recovering-crashed-consumers-with-xautoclaim","title":"Recovering crashed consumers with XAUTOCLAIM","role":"content","text":"The demo's \"Crash next 3\" button tells a chosen consumer to drop its next three deliveries on the floor without acking them — the same effect as a worker process dying mid-message. Those entries stay in the group's PEL with their delivery counter incremented. Once they have been idle for at least `claimMinIdleMs`, any healthy consumer in the group can rescue them by calling `XAUTOCLAIM` *with itself as the target*. `ConsumerWorker::reapIdlePel()` wraps that pattern:\n\n[code example]\n\nThe underlying `$stream->autoclaim()` helper pages through the group's PEL with `XAUTOCLAIM`'s continuation cursor:\n\n[code example]\n\nA single `XAUTOCLAIM` call scans up to `pageCount` PEL entries starting at `startId`, reassigns the ones idle for at least `claimMinIdleMs` to the named consumer, and returns a continuation cursor in the first slot of the reply. For a full sweep, loop until the cursor returns to `0-0` (with a `maxPages` safety net so one call cannot monopolise a very large PEL). The delivery counter is incremented on every claim — after a few cycles you can use it to spot a *poison-pill* message that crashes every consumer that touches it, and route it to a dead-letter stream so the bad entry stops cycling. (New entries keep flowing past the poison pill — `XREADGROUP >` still delivers fresh work — but the bad entry's repeated reclaim wastes consumer time and keeps the PEL larger than it needs to be.)\n\nThe `deletedIds` list contains PEL entry IDs whose stream payload was already trimmed by the time the claim ran (typically because `MAXLEN ~` retention outran a slow consumer). `XAUTOCLAIM` removes those dangling slots from the PEL itself, so the caller does *not* need to `XACK` them — but the entries cannot be retried either, so log and route them to a dead-letter store for offline inspection. Redis 7.0 introduced this third return element; the example requires Redis 7.0+ for that reason.\n\n`reapIdlePel` is the right primitive for the recovery path because it claims and processes in one step: every entry the call returned is now in *this* consumer's PEL, so the same consumer is responsible for processing and acking it. In production each consumer process runs `reapIdlePel` periodically (every few seconds, on a timer) so a crashed peer's entries never sit invisibly. The demo exposes it as a manual button so you can trigger the reap after waiting for the idle threshold.\n\n`XCLAIM` (singular, no auto) does the same thing for a specific list of entry IDs you already have in hand — useful when you want to take ownership of one known stuck entry, or when you need to move a specific consumer's PEL to a peer (the case the demo's \"Remove consumer\" button handles via `handoverPending()`). `XAUTOCLAIM` cannot filter by source consumer, so it cannot be used for a per-consumer handover."},{"id":"replay-with-xrange","title":"Replay with XRANGE","role":"content","text":"`XRANGE` reads a slice of history. It is completely independent of any consumer group — no cursors move, no acks happen — so it is safe to call any number of times, from any process:\n\n[code example]\n\nThe special IDs `-` and `+` mean \"from the very beginning\" and \"to the very end\". You can also pass real IDs (`1716998413541-0`) or just the millisecond part (`1716998413541`, which Redis interprets as \"any entry with this timestamp\").\n\nTypical uses:\n\n* **Bootstrapping a new projection** — read the entire stream from `-` and build a derived view in another store (a search index, a SQL table, a different cache). Doing this against a consumer group would consume the entries; `XRANGE` lets you do it without disrupting live consumers.\n* **Auditing recent activity** — read the last few minutes by ID range without touching any group cursor.\n* **Debugging** — fetch one specific entry by its ID, or a tight range around an incident timestamp, to see exactly what producers wrote."},{"id":"the-consumer-worker-process","title":"The consumer worker process","role":"content","text":"`ConsumerWorker` wraps the `XREADGROUP` → process → `XACK` loop and is intended to run as a **separate CLI process**\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/streaming/php/ConsumerWorker.php)):\n\n[code example]\n\n`handleEntry()` either acks (the normal path) or, when the demo's `crash_next` counter is `> 0`, drops the entry on the floor and increments the per-consumer `crashed_drops` count. The crash check uses a tiny Lua script (`GET` + conditional `DECR`) so two simultaneous deliveries can't both undercount the counter past zero.\n\nRecovery of stuck PEL entries — this consumer's, after a restart, or another consumer's, after a crash — runs through `reapIdlePel()` rather than the read loop. That method calls `XAUTOCLAIM` with this consumer as the target, then processes whatever was claimed in the same flow as new entries. This is the textbook Streams pattern: each consumer is its own reaper, running `XAUTOCLAIM(self)` periodically (or on demand) so a crashed peer's entries never sit invisibly in the PEL. The demo's \"XAUTOCLAIM to selected\" button calls `reapIdlePel()` on the chosen consumer; in production you would run it from a timer every few seconds.\n\nNote that the worker's main read loop deliberately does *not* call `XREADGROUP 0` to drain its own PEL on every iteration. That would re-deliver every pending entry continuously and *reset its idle counter to zero* each time, which would keep crashed entries below the `XAUTOCLAIM` threshold forever. Using `XAUTOCLAIM(self)` as the recovery primitive — which only fires for entries idle longer than `min_idle_time` — avoids that whole class of bug.\n\nThe pause and crash levers exist only for the demo. A real consumer is just the read-process-ack loop — everything else in this class is instrumentation."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* Redis 7.0 or later. `XAUTOCLAIM` was added in Redis 6.2, but its reply gained a third\n  element (the list of deleted IDs) in 7.0; the example relies on that shape.\n* PHP 8.1 or later, with the `pcntl` and `posix` extensions enabled (both ship with the\n  official PHP binary on macOS and most Linux distros).\n* The Predis client (3.x). Install it with [Composer](https://getcomposer.org/):\n\n  [code example]\n\nIf your Redis server is running elsewhere, start the demo with `REDIS_HOST=...` and `REDIS_PORT=...` (see [Start the demo server](#start-the-demo-server) below)."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of four files plus the Composer manifest. Download them from the [`php` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/streaming/php) on GitHub, or grab them with `curl`:\n\n[code example]\n\nThen install dependencies:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From that directory:\n\n[code example]\n\nYou should see:\n\n[code example]\n\nBy default the demo wipes the configured stream key on startup so each run starts from a clean state. The Composer-built-in `php -S` doesn't accept user CLI flags through to the script, so the demo uses environment variables for the equivalent overrides:\n\n| Env var              | CLI equivalent       | Default                | Meaning                                                                                       |\n|----------------------|----------------------|------------------------|-----------------------------------------------------------------------------------------------|\n| `REDIS_HOST`         | `--redis-host`       | `127.0.0.1`            | Redis host the demo server and every worker connect to.                                       |\n| `REDIS_PORT`         | `--redis-port`       | `6379`                 | Redis port.                                                                                   |\n| `STREAM_KEY`         | `--stream-key`       | `demo:events:orders`   | The Redis Stream key the demo writes to and reads from.                                       |\n| `MAXLEN`             | `--maxlen`           | `2000`                 | Approximate `MAXLEN ~` cap on every `XADD`.                                                   |\n| `CLAIM_IDLE_MS`      | `--claim-idle-ms`    | `5000`                 | Minimum idle time before `XAUTOCLAIM` may reassign a pending entry.                           |\n| `NO_RESET`           | `--no-reset`         | (reset on first request) | Set to `1` to keep any existing data at `STREAM_KEY` instead of dropping it on first request. |\n| `PROCESS_LATENCY_MS` | —                    | `25`                   | Per-entry processing latency the workers simulate (purely for visualisation).                 |\n\nFor example, to point the demo at a different stream and tighten the autoclaim window:\n\n[code example]\n\nOpen [http://127.0.0.1:8083](http://127.0.0.1:8083) in a browser. You can:\n\n* **Produce** any number of events of a chosen type (or random types). Watch the stream length grow and the tail update.\n* See each **consumer group**: its `last-delivered-id`, the size of its pending list, and the consumers in it. Each consumer shows its processed count, pending count, and idle time.\n* **Add or remove** consumers within a group at runtime to see Redis split the work across the new shape.\n* Click **Crash next 3** on a consumer to drop its next three deliveries — the same effect as a worker process dying after `XREADGROUP` but before `XACK`. Watch the **Pending entries (XPENDING)** panel fill up.\n* Wait until the idle time exceeds the threshold (default 5000 ms), pick a healthy target consumer, and click **XAUTOCLAIM to selected** — the stuck entries are reassigned and the delivery counter increments.\n* **Replay (XRANGE)** any range to confirm the full history is independent of consumer-group state.\n* **XTRIM** with an approximate `MAXLEN` to bound retention. Note that an approximate trim only releases whole macro-nodes — `MAXLEN ~ 50` on a small stream may not delete anything; on a 300-entry stream it typically lands at around 100.\n* Click **Reset demo** to drop the stream, kill every worker, and re-seed the default groups."},{"id":"stopping-the-demo-cleanly","title":"Stopping the demo cleanly","role":"content","text":"`php -S` doesn't run a shutdown handler when you Ctrl-C out of it, and the consumer worker processes — which are *intentionally* detached so they survive request boundaries — will outlive the demo server unless you stop them first. Before stopping the server, click **Reset demo** in the UI (which kills every worker), or run:\n\n[code example]\n\nIf you forgot, clean up by name:\n\n[code example]"},{"id":"production-usage","title":"Production usage","role":"content","text":""},{"id":"why-this-php-port-differs-from-the-others","title":"Why this PHP port differs from the others","role":"content","text":"Every other client in this use case keeps consumers as in-process objects with a background thread (Python's `threading.Thread`, .NET's task pool, Node's event loop, Go's goroutines, etc.). That works because those runtimes have a long-lived server process that owns the consumer's connection, callback, and dispatch loop.\n\nPHP's traditional one-process-per-request model — used by `php -S`, mod_php, PHP-FPM with the default `pm` setting — fundamentally doesn't fit that shape. A consumer created inside an HTTP handler dies the moment the handler returns. Even if you used a long-running PHP daemon (Roadrunner, Swoole, ReactPHP), you'd still need separate worker processes if you wanted multiple independent consumers, because Predis's blocking `XREADGROUP` call blocks the calling process.\n\nThis port therefore keeps each consumer as a **separate OS process**, with its full state (PID, processed/reaped/dropped counts, recent buffer) persisted in Redis. Every HTTP request reconstructs its view of the consumer registry from those keys. The pattern is closer to how a real production PHP application would run stream consumers: a `supervisord`, `systemd`, or container orchestrator drives N copies of `ConsumerWorker.php`, each owning one logical consumer in one logical group, and the web tier never tries to host a consumer itself. The demo just inlines the supervision (via `proc_open` + `posix_kill`) so a single `php -S` command is enough to play with the pattern end-to-end.\n\nTwo cross-process subtleties are worth calling out, because both will bite anyone who tries to copy this pattern naively:\n\n* **Capture the worker's real PID, not a wrapper's.** A `proc_open(['setsid', '-f', $args...])` call returns the *wrapper's* PID — `setsid -f` forks, exec's the worker as the new session leader, and the wrapper exits. A subsequent `posix_kill($recordedPid, SIGTERM)` then signals a dead PID and the worker survives. The fix is a shell-wrapped `& echo $!` pattern that backgrounds the worker and echoes its real PID back through the wrapper's stdout pipe, which is what `spawnWorker()` in `demo_server.php` does on both Linux and macOS.\n* **Pause/resume across processes uses Redis flags, not in-process events.** The reference Python port uses a `threading.Event` to park a consumer while the demo server hands its PEL off to a peer; this port uses two Redis keys per worker (`paused` and `idle`). The demo server `SET`s `paused=1`, waits for the worker to write `idle=1` (the worker checks the flag at the top of every loop iteration with a 20 ms spin-wait), runs the surgical operation, then `DEL`s both keys. That's what makes the \"Remove consumer\" handover safe even though the demo server can't touch the worker's in-memory state directly."},{"id":"pick-retention-by-length-or-by-minimum-id","title":"Pick retention by length or by minimum ID","role":"content","text":"The demo uses `MAXLEN ~` on every `XADD`. Two alternatives are worth considering:\n\n* `MINID ~ <id>` — keep only entries newer than an ID. If you want \"the last 24 hours\", compute the wall-clock cutoff and pass `XTRIM MINID ~ <ms>-0`. This is the right pattern when retention is time-bounded.\n* No cap on `XADD` plus a periodic `XTRIM` job — useful if your producer is hot and the per-`XADD` work has to stay minimal, or if retention rules are complex (a separate process can also factor in consumer-group lag).\n\nIn all three cases the trimming is approximate by default. Use exact trimming (`MAXLEN n` or `MINID id` without `~`) only when you genuinely need an exact count."},{"id":"dont-let-consumer-group-lag-silently-grow","title":"Don't let consumer-group lag silently grow","role":"content","text":"`XINFO GROUPS` reports each group's `lag` (entries the group has not yet read) and `pending` (entries delivered but not acked). In production, alert on either of these crossing a threshold — a steadily growing pending count usually means consumers are crashing without `XAUTOCLAIM` running, and a growing lag means consumers can't keep up with producers.\n\nThe same applies inside a group: `XINFO CONSUMERS` reports per-consumer pending counts and idle times, so you can spot one slow consumer holding entries that the rest of the group is waiting on."},{"id":"make-consumer-logic-idempotent","title":"Make consumer logic idempotent","role":"content","text":"`XAUTOCLAIM` can re-deliver an entry to a different consumer after a crash. If your processing has side effects (sending email, charging a card, updating a downstream store), make sure the same entry processed twice gives the same result — use an idempotency key, an upsert with conditional check, or a once-per-id guard table. Redis Streams cannot give you exactly-once semantics on its own."},{"id":"bound-the-delivery-counter-as-a-poison-pill-signal","title":"Bound the delivery counter as a poison-pill signal","role":"content","text":"`XPENDING` returns each entry's delivery count, incremented on every claim. If an entry has been delivered (and dropped) several times, the next consumer is unlikely to fare better. After some threshold — `deliveries >= 5`, say — route the entry to a *dead-letter stream*, ack it on the original group, and alert. New entries keep flowing past a poison pill (`XREADGROUP >` still delivers fresh work), but the bad entry's repeated reclaim wastes consumer time and keeps the PEL bigger than it needs to be — without a DLQ threshold it can also slowly trip retention/lag alerts."},{"id":"partition-by-tenant-or-entity-for-scale","title":"Partition by tenant or entity for scale","role":"content","text":"A single Redis Stream is a single key, and on a Redis Cluster a single key lives on a single shard. If your throughput exceeds what one shard can handle, partition the stream — for example by tenant ID (`events:orders:{tenant_a}`, `events:orders:{tenant_b}`) — so different tenants land on different shards. Hash-tags (`{tenant_a}`) keep all related streams on the same shard if you need to multi-stream atomically.\n\nPer-entity partitioning (`events:order:{order_id}`) is the canonical pattern when you treat each entity's stream as the event-sourcing log for that entity: every state change for one order goes on its own stream, which is also bounded in size by the entity's lifetime."},{"id":"use-a-separate-consumer-pool-per-group","title":"Use a separate consumer pool per group","role":"content","text":"The demo runs every consumer alongside one demo server. In production each consumer group is usually its own deployment — its own pool of pods or VMs — so a slow projection in `analytics` cannot pull `notifications` workers off their stream. Each pod runs one consumer process per CPU core, with `XAUTOCLAIM` either embedded in the consumer loop (every N reads, claim idle entries to self) or run by a separate reaper. `supervisord`, `systemd`, or a container orchestrator owns the process lifecycle, not your web tier."},{"id":"dont-read-with-xread-no-group-and-then-try-to-ack","title":"Don't read with XREAD (no group) and then try to ack","role":"content","text":"`XREAD` and `XREADGROUP` are different mechanisms. `XREAD` is a tail-the-log read with no consumer-group state — entries are not added to any PEL, and you cannot `XACK` them. If you want at-least-once delivery and crash recovery, you must read through a consumer group.\n\n`XREAD` is still useful for read-only tail clients (a UI streaming events, a debugger, a `tail -f`-style command-line tool). It's just not part of the at-least-once path."},{"id":"inspect-the-stream-directly-with-redis-cli","title":"Inspect the stream directly with redis-cli","role":"content","text":"When testing or troubleshooting, inspect the stream directly to confirm the consumer state is what you expect:\n\n[code example]\n\nIf a group's `lag` is growing while consumers' `idle` times are short, consumers are healthy but producers are outpacing them — add more consumers. If `pending` is growing while `lag` is small, consumers are *receiving* entries but not *acking* them — either they are crashing mid-message or your acking logic has a bug."},{"id":"learn-more","title":"Learn more","role":"related","text":"This example uses the following Redis commands:\n\n* [`XADD`](https://redis.io/docs/latest/commands/xadd) to append an event with an approximate `MAXLEN` cap.\n* [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) to read new entries for a consumer in a group.\n* [`XACK`](https://redis.io/docs/latest/commands/xack) to acknowledge a processed entry.\n* [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) to reassign idle pending entries to a healthy consumer.\n* [`XCLAIM`](https://redis.io/docs/latest/commands/xclaim) to reassign specific entry IDs (used here to hand a consumer's PEL over to a peer before deletion).\n* [`XRANGE`](https://redis.io/docs/latest/commands/xrange) for replay and audit, independent of consumer-group state.\n* [`XPENDING`](https://redis.io/docs/latest/commands/xpending) to inspect the per-group pending list with idle times and delivery counts.\n* [`XTRIM`](https://redis.io/docs/latest/commands/xtrim) for explicit retention enforcement.\n* [`XGROUP CREATE`](https://redis.io/docs/latest/commands/xgroup-create) and\n  [`XGROUP DELCONSUMER`](https://redis.io/docs/latest/commands/xgroup-delconsumer) to manage groups and consumers.\n* [`XINFO STREAM`](https://redis.io/docs/latest/commands/xinfo-stream),\n  [`XINFO GROUPS`](https://redis.io/docs/latest/commands/xinfo-groups), and\n  [`XINFO CONSUMERS`](https://redis.io/docs/latest/commands/xinfo-consumers) for observability.\n\nSee the [Predis README](https://github.com/predis/predis) for the full client reference, and the [Streams overview](https://redis.io/docs/latest/develop/data-types/streams) for the deeper conceptual model — consumer groups, the PEL, claim semantics, capped streams, and the differences with Kafka partitions."}],"examples":[{"id":"the-event-stream-helper-ex0","language":"php","code":"require __DIR__ . '/vendor/autoload.php';\nrequire __DIR__ . '/EventStream.php';\n\nuse Predis\\Client as PredisClient;\n\n$redis = new PredisClient(['host' => '127.0.0.1', 'port' => 6379]);\n$stream = new EventStream(\n    $redis,\n    'demo:events:orders',\n    2000,        // retention guardrail (approximate MAXLEN)\n    5000         // XAUTOCLAIM idle threshold (ms)\n);\n\n// Producer\n$streamId = $stream->produce('order.placed', [\n    'order_id' => 'o-1234',\n    'customer' => 'alice',\n    'amount'   => '49.50',\n]);\n\n// Consumer group + one consumer\n$stream->ensureGroup('notifications', '0-0');\n$entries = $stream->consume('notifications', 'worker-a', count: 10, blockMs: 500);\nforeach ($entries as [$entryId, $fields]) {\n    handle($fields);                                // your processing\n    $stream->ack('notifications', [$entryId]);      // XACK\n}\n\n// Recover stuck PEL entries by reaping them into a healthy consumer.\n// The textbook pattern: each consumer periodically calls XAUTOCLAIM\n// with itself as the target and processes whatever it claimed.\n// `ConsumerWorker::reapIdlePel()` wraps that flow; the low-level\n// helper `$stream->autoclaim($group, $target)` is also available if\n// you want to drive XAUTOCLAIM directly.\n$result = $worker->reapIdlePel();\n// $result == ['claimed' => N, 'processed' => M, 'deleted_ids' => [...]]\n// deleted_ids are PEL entries whose payload was already trimmed.\n// Redis 7+ has already removed those slots from the PEL, so no XACK\n// is needed — log them and route to a dead-letter store for audit.\n\n// Replay history (independent of any group's cursor)\nforeach ($stream->replay('-', '+', 50) as [$entryId, $fields]) {\n    print \"$entryId \" . json_encode($fields) . \"\\n\";\n}","section_id":"the-event-stream-helper"},{"id":"data-model-ex0","language":"text","code":"demo:events:orders\n  1716998413541-0   type=order.placed     order_id=o-1234   customer=alice  amount=49.50  ts_ms=...\n  1716998413542-0   type=order.paid       order_id=o-1234   customer=alice  amount=49.50  ts_ms=...\n  1716998413542-1   type=order.shipped    order_id=o-1235   customer=bob    amount=12.00  ts_ms=...\n  ...","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"demo:streaming:stats                            (hash)   produced_total, acked_total, claimed_total\ndemo:streaming:workers                          (set)    \"{group}/{name}\" entries for every spawned worker\ndemo:streaming:worker:{group}:{name}:pid        (string) worker process PID\ndemo:streaming:worker:{group}:{name}:processed  (string) per-consumer ack count\ndemo:streaming:worker:{group}:{name}:reaped     (string) per-consumer XAUTOCLAIM-claimed count\ndemo:streaming:worker:{group}:{name}:crashed_drops (string) per-consumer drop count\ndemo:streaming:worker:{group}:{name}:crash_next (string) integer counter for the crash lever\ndemo:streaming:worker:{group}:{name}:paused     (string) \"1\" while paused, deleted otherwise\ndemo:streaming:worker:{group}:{name}:idle       (string) \"1\" while the worker has acknowledged the pause\ndemo:streaming:worker:{group}:{name}:recent     (list)   last N processed entries for the UI","section_id":"data-model"},{"id":"producing-events-ex0","language":"php","code":"public function produceBatch(array $events): array\n{\n    $stream = $this->streamKey;\n    $maxlen = $this->maxlenApprox;\n    $results = $this->redis->pipeline(function ($pipe) use ($events, $stream, $maxlen) {\n        foreach ($events as [$eventType, $payload]) {\n            $fields = self::encodeFields($eventType, $payload);\n            // XADD <key> <fields-assoc> <id=*> {trim => [MAXLEN, ~, n]}\n            $pipe->xadd($stream, $fields, '*', ['trim' => ['MAXLEN', '~', $maxlen]]);\n        }\n    });\n    // ...\n}","section_id":"producing-events"},{"id":"reading-with-a-consumer-group-ex0","language":"php","code":"public function consume(string $group, string $consumer, int $count = 10, int $blockMs = 500): array\n{\n    // Predis xreadgroup signature:\n    // xreadgroup(group, consumer, count, block, noack, key, id)\n    $raw = $this->redis->xreadgroup(\n        $group, $consumer, $count, $blockMs, false,\n        $this->streamKey, '>'\n    );\n    return self::flattenReadGroup($raw);\n}","section_id":"reading-with-a-consumer-group"},{"id":"acknowledging-entries-ex0","language":"php","code":"public function ack(string $group, array $ids): int\n{\n    if (empty($ids)) {\n        return 0;\n    }\n    $n = (int) $this->redis->xack($this->streamKey, $group, ...$ids);\n    if ($n > 0) {\n        $this->redis->hincrby(self::STATS_KEY, 'acked_total', $n);\n    }\n    return $n;\n}","section_id":"acknowledging-entries"},{"id":"multiple-consumer-groups-one-stream-ex0","language":"php","code":"$stream->ensureGroup('notifications', '0-0');\n$stream->ensureGroup('analytics',     '0-0');","section_id":"multiple-consumer-groups-one-stream"},{"id":"recovering-crashed-consumers-with-xautoclaim-ex0","language":"php","code":"public function reapIdlePel(): array\n{\n    $result = $this->stream->autoclaim($this->group, $this->name, 100, '0-0', 10);\n    $claimed = $result['claimed'];\n    $deletedIds = $result['deletedIds'];\n\n    $processed = 0;\n    foreach ($claimed as [$entryId, $fields]) {\n        try {\n            $this->handleEntry($entryId, $fields, /*viaReap*/ true);\n            $processed++;\n        } catch (\\Throwable $exc) {\n            fwrite(STDERR, \"[{$this->group}/{$this->name}] reap failed on {$entryId}: \" . $exc->getMessage() . \"\\n\");\n        }\n    }\n    return [\n        'claimed' => count($claimed),\n        'processed' => $processed,\n        'deleted_ids' => $deletedIds,\n    ];\n}","section_id":"recovering-crashed-consumers-with-xautoclaim"},{"id":"recovering-crashed-consumers-with-xautoclaim-ex1","language":"php","code":"public function autoclaim(\n    string $group, string $consumer,\n    int $pageCount = 100, string $startId = '0-0', int $maxPages = 10\n): array {\n    $claimedAll = []; $deletedAll = []; $cursor = $startId;\n    for ($i = 0; $i < $maxPages; $i++) {\n        $reply = $this->redis->xautoclaim(\n            $this->streamKey, $group, $consumer,\n            $this->claimMinIdleMs, $cursor, $pageCount\n        );\n        // Reply shape: [nextCursor, [[id, [k,v,k,v,...]], ...], [deletedIds...]]\n        $nextCursor = (string) $reply[0];\n        foreach (($reply[1] ?? []) as $entry) {\n            $claimedAll[] = [(string) $entry[0], self::pairsToDict($entry[1] ?? [])];\n        }\n        foreach (($reply[2] ?? []) as $id) { $deletedAll[] = (string) $id; }\n        if ($nextCursor === '0-0') break;\n        $cursor = $nextCursor;\n    }\n    return ['claimed' => $claimedAll, 'deletedIds' => $deletedAll];\n}","section_id":"recovering-crashed-consumers-with-xautoclaim"},{"id":"replay-with-xrange-ex0","language":"php","code":"public function replay(string $startId = '-', string $endId = '+', int $count = 100): array\n{\n    $raw = $this->redis->xrange($this->streamKey, $startId, $endId, $count);\n    $out = [];\n    foreach ($raw as $id => $fields) {\n        $out[] = [(string) $id, is_array($fields) ? $fields : []];\n    }\n    return $out;\n}","section_id":"replay-with-xrange"},{"id":"the-consumer-worker-process-ex0","language":"php","code":"public function run(): void\n{\n    // SIGTERM handler so the demo server's posix_kill gives the\n    // worker a chance to leave the loop cleanly.\n    $stop = false;\n    pcntl_async_signals(true);\n    pcntl_signal(SIGTERM, function () use (&$stop) { $stop = true; });\n\n    while (!$stop) {\n        // Cross-process pause flag (see Production usage).\n        if ((string) $this->redis->get($pausedKey) === '1') {\n            $this->redis->set($idleKey, '1');\n            usleep(20 * 1000);\n            continue;\n        }\n        $entries = $this->stream->consume($this->group, $this->name, 10, 500);\n        foreach ($entries as [$entryId, $fields]) {\n            usleep($this->processLatencyMs * 1000);\n            $this->handleEntry($entryId, $fields, /*viaReap*/ false);\n        }\n    }\n}","section_id":"the-consumer-worker-process"},{"id":"prerequisites-ex0","language":"bash","code":"composer require \"predis/predis:^3.0\"","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir streaming-demo && cd streaming-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/streaming/php\ncurl -O $BASE/EventStream.php\ncurl -O $BASE/ConsumerWorker.php\ncurl -O $BASE/demo_server.php\ncurl -O $BASE/composer.json","section_id":"get-the-source-files"},{"id":"get-the-source-files-ex1","language":"bash","code":"composer install","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"php -S 127.0.0.1:8083 demo_server.php","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"[...] PHP 8.4.6 Development Server (http://127.0.0.1:8083) started","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex2","language":"bash","code":"STREAM_KEY=demo:events:orders-php CLAIM_IDLE_MS=500 php -S 127.0.0.1:8083 demo_server.php","section_id":"start-the-demo-server"},{"id":"stopping-the-demo-cleanly-ex0","language":"bash","code":"curl -X POST http://127.0.0.1:8083/reset","section_id":"stopping-the-demo-cleanly"},{"id":"stopping-the-demo-cleanly-ex1","language":"bash","code":"pgrep -f ConsumerWorker.php | xargs kill\nredis-cli --scan --pattern 'demo:streaming:*' | xargs redis-cli del","section_id":"stopping-the-demo-cleanly"},{"id":"inspect-the-stream-directly-with-redis-cli-ex0","language":"bash","code":"# Stream summary\nredis-cli XLEN demo:events:orders\nredis-cli XINFO STREAM demo:events:orders\n\n# Group cursors and pending counts\nredis-cli XINFO GROUPS demo:events:orders\n\n# Consumers within a group\nredis-cli XINFO CONSUMERS demo:events:orders notifications\n\n# Pending entries with idle time and delivery count\nredis-cli XPENDING demo:events:orders notifications - + 20\n\n# Tail the stream live (no consumer-group state — like tail -f)\nredis-cli XREAD BLOCK 0 STREAMS demo:events:orders '$'\n\n# Replay a range\nredis-cli XRANGE demo:events:orders - + COUNT 50\n\n# The PHP port's own bookkeeping keys\nredis-cli --scan --pattern 'demo:streaming:*'","section_id":"inspect-the-stream-directly-with-redis-cli"}]}
{"schema_version":2,"id":"develop/use-cases/streaming/redis-py","title":"Redis streaming with redis-py","url":"https://redis.io/docs/latest/develop/use-cases/streaming/redis-py/","summary":"Implement a Redis event-streaming pipeline in Python with redis-py","aliases":["/develop/use-cases/streaming/python"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:55:04+01:00","children":[],"page_type":"content","content_hash":"0ce04244789477172ed65cff768c803dc5c810ce7d7ab1064de91f30b270cc67","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a Redis-backed event-streaming pipeline in Python with [`redis-py`](https://redis.io/docs/latest/develop/clients/redis-py). It includes a small local web server built with the Python standard library so you can produce events into a single Redis Stream, watch two independent consumer groups read it at their own pace, and recover stuck deliveries with `XAUTOCLAIM` after simulating a consumer crash."},{"id":"overview","title":"Overview","role":"overview","text":"A Redis Stream is an append-only log of field/value entries with auto-generated, time-ordered IDs. Producers append with [`XADD`](https://redis.io/docs/latest/commands/xadd); consumers belong to *consumer groups* and read with [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup). The group as a whole tracks a single `last-delivered-id` cursor, and each consumer gets its own pending-entries list (PEL) of messages it has been handed but not yet acknowledged. Once a consumer has processed an entry it calls [`XACK`](https://redis.io/docs/latest/commands/xack) to clear the entry from its PEL; entries left unacknowledged past an idle threshold can be reassigned to a healthy consumer with [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim).\n\nThat gives you:\n\n* Ordered, durable history that many independent consumer groups can read at their own pace\n* At-least-once delivery, with per-consumer pending lists and automatic recovery of crashed consumers\n* Horizontal scaling within a group — add a consumer and Redis automatically splits the work\n* Replay of any range with [`XRANGE`](https://redis.io/docs/latest/commands/xrange), independent of consumer-group state\n* Bounded retention through [`XADD MAXLEN ~`](https://redis.io/docs/latest/commands/xadd) or\n  [`XTRIM MINID ~`](https://redis.io/docs/latest/commands/xtrim), without a separate cleanup job\n\nIn this example, producers append order events (`order.placed`, `order.paid`, `order.shipped`, `order.cancelled`) to a single stream at `demo:events:orders`. Two consumer groups read the same stream:\n\n* **`notifications`** — two consumers (`worker-a`, `worker-b`) sharing the work, modelling a fan-out worker pool.\n* **`analytics`** — one consumer (`worker-c`) processing the full event flow on its own."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. The application calls `stream.produce(event_type, payload)` which runs [`XADD`](https://redis.io/docs/latest/commands/xadd) with an approximate [`MAXLEN ~`](https://redis.io/docs/latest/commands/xadd) cap. Redis assigns an auto-generated time-ordered ID.\n2. Each consumer thread loops on [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) with the special ID `>` (meaning \"deliver entries this group has not yet delivered to anyone\") and a short block timeout.\n3. After processing each entry, the consumer calls [`XACK`](https://redis.io/docs/latest/commands/xack) so Redis can drop it from the group's pending list.\n4. If a consumer is killed (or crashes) before acking, its entries sit in the group's PEL. A periodic [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) sweep reassigns idle entries to a healthy consumer.\n5. Anyone — including code outside the consumer groups — can read history with [`XRANGE`](https://redis.io/docs/latest/commands/xrange) without affecting any group's cursor.\n\nEach consumer group has its own cursor (`last-delivered-id`) and its own pending list, so the two groups in this demo process the same events without coordinating with each other."},{"id":"the-event-stream-helper","title":"The event-stream helper","role":"content","text":"The `RedisEventStream` class wraps the stream operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/streaming/redis-py/event_stream.py)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each event is a single stream entry — a flat dict of field/value strings — with an auto-generated time-ordered ID:\n\n[code example]\n\nThe ID is `{milliseconds}-{sequence}`, monotonically increasing within the stream, so you can range-query by approximate wall-clock time without an extra index. (IDs are ordered within a stream, not across streams — two events appended to different streams at the same millisecond can produce the same ID.) The implementation uses:\n\n* [`XADD ... MAXLEN ~ n`](https://redis.io/docs/latest/commands/xadd), pipelined, for batch production with a retention cap\n* [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) with the special ID `>` for fresh deliveries to a consumer\n* [`XACK`](https://redis.io/docs/latest/commands/xack) on every processed entry\n* [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) for sweeping idle pending entries to a healthy consumer\n* [`XRANGE`](https://redis.io/docs/latest/commands/xrange) for replay and audit\n* [`XPENDING`](https://redis.io/docs/latest/commands/xpending) for inspecting the per-group pending list\n* [`XINFO STREAM`](https://redis.io/docs/latest/commands/xinfo-stream),\n  [`XINFO GROUPS`](https://redis.io/docs/latest/commands/xinfo-groups), and\n  [`XINFO CONSUMERS`](https://redis.io/docs/latest/commands/xinfo-consumers) for surface-level observability\n* [`XTRIM`](https://redis.io/docs/latest/commands/xtrim) for explicit retention enforcement"},{"id":"producing-events","title":"Producing events","role":"content","text":"`produce_batch` pipelines `XADD` calls in a single round trip. Each call carries an approximate `MAXLEN ~` cap so the stream stays bounded as it rolls forward:\n\n[code example]\n\nThe `~` flavour of `MAXLEN` lets Redis trim at a macro-node boundary, which is much cheaper than exact trimming and is what you want when the cap is a retention *guardrail*, not a hard size constraint. With 300 events produced and `MAXLEN ~ 50`, you might end up with 100 entries left — Redis released the oldest whole macro-node and stopped. The next `XADD` will keep length stable.\n\nIf you genuinely need an exact cap (rare), drop `approximate=True`. The performance difference is significant on busy streams."},{"id":"reading-with-a-consumer-group","title":"Reading with a consumer group","role":"content","text":"Each consumer in a group runs the same `XREADGROUP` loop. The special ID `>` means \"deliver entries this group has not yet delivered to *anyone*\":\n\n[code example]\n\n`block_ms` makes the call efficient even when the stream is idle: the client parks on the server until either an entry arrives or the timeout expires, so consumers don't busy-loop.\n\nReading with an explicit ID like `0-0` instead of `>` does something different — it replays entries already delivered to *this* consumer name (its private PEL). That is the canonical recovery path when the same consumer restarts: catch up on its own pending entries first, then resume reading new ones."},{"id":"acknowledging-entries","title":"Acknowledging entries","role":"content","text":"Once the consumer has processed an entry, `XACK` tells Redis it can drop the entry from the group's pending list:\n\n[code example]\n\nThis is the linchpin of at-least-once delivery: an entry that is never acked stays in the PEL until a claim moves it elsewhere. If your consumer thread crashes between processing and ack, the next claim sweep picks the entry back up. The one caveat is retention: `XADD MAXLEN ~` and `XTRIM` can release the entry's *payload* even while its ID is still in the PEL. The next `XAUTOCLAIM` returns those IDs in its `deleted` list and removes them from the PEL inside the same command — the entry cannot be retried, so the caller should log it and route to a dead-letter store for audit. The example handles this explicitly in `_handle_autoclaim` further down.\n\nThe trade-off is the opposite of pub/sub: a slow or crashed consumer doesn't lose messages, but it does mean your downstream system must be idempotent. If you process an order twice because the first attempt died after the side effect but before the ack, the second attempt must be safe."},{"id":"multiple-consumer-groups-one-stream","title":"Multiple consumer groups, one stream","role":"content","text":"The big difference between Redis Streams and a job queue is that any number of independent consumer groups can read the same stream. The demo sets up two groups on `demo:events:orders`:\n\n[code example]\n\nEach group has its own cursor. Producing 5 events results in `notifications` and `analytics` each receiving all 5, with no coordination between them. Within `notifications`, the work is split across `worker-a` and `worker-b`: Redis hands each `XREADGROUP` call whatever entries are not yet delivered to anyone in the group, so adding a second worker doubles throughput without any rebalance logic.\n\nThe `start_id=\"0-0\"` argument means \"deliver everything in the stream from the beginning\" — useful in a demo and for fresh groups bootstrapping from history. In production, a brand-new group reading a long-existing stream usually starts at `$` (\"only events after this point\") and uses [`XRANGE`](https://redis.io/docs/latest/commands/xrange) explicitly if it needs history."},{"id":"recovering-crashed-consumers-with-xautoclaim","title":"Recovering crashed consumers with XAUTOCLAIM","role":"content","text":"The demo's \"Crash next 3\" button tells a chosen consumer to drop its next three deliveries on the floor without acking them — the same effect as a worker process dying mid-message. Those entries stay in the group's PEL with their delivery counter incremented. Once they have been idle for at least `claim_min_idle_ms`, any healthy consumer in the group can rescue them by calling `XAUTOCLAIM` *with itself as the target*. `ConsumerWorker.reap_idle_pel` wraps that pattern:\n\n[code example]\n\nThe underlying `stream.autoclaim` helper pages through the group's PEL with `XAUTOCLAIM`'s continuation cursor:\n\n[code example]\n\nA single `XAUTOCLAIM` call scans up to `page_count` PEL entries starting at `start_id`, reassigns the ones idle for at least `min_idle_time` to the named consumer, and returns a continuation cursor in the first slot of the reply. For a full sweep, loop until the cursor returns to `0-0` (with a `max_pages` safety net so one call cannot monopolise a very large PEL). The delivery counter is incremented on every claim — after a few cycles you can use it to spot a *poison-pill* message that crashes every consumer that touches it, and route it to a dead-letter stream so the bad entry stops cycling. (New entries keep flowing past the poison pill — `XREADGROUP >` still delivers fresh work — but the bad entry's repeated reclaim wastes consumer time and keeps the PEL larger than it needs to be.)\n\nThe `deleted` list contains PEL entry IDs whose stream payload was already trimmed by the time the claim ran (typically because `MAXLEN ~` retention outran a slow consumer). `XAUTOCLAIM` removes those dangling slots from the PEL itself, so the caller does *not* need to `XACK` them — but the entries cannot be retried either, so log and route them to a dead-letter store for offline inspection. Redis 7.0 introduced this third return element; the example requires Redis 7.0+ for that reason.\n\n`reap_idle_pel` is the right primitive for the recovery path because it claims and processes in one step: every entry the call returned is now in *this* consumer's PEL, so the same consumer is responsible for processing and acking it. In production each consumer thread runs `reap_idle_pel` periodically (every few seconds, on a timer) so a crashed peer's entries never sit invisibly. The demo exposes it as a manual button so you can trigger the reap after waiting for the idle threshold.\n\n`XCLAIM` (singular, no auto) does the same thing for a specific list of entry IDs you already have in hand — useful when you want to take ownership of one known stuck entry, or when you need to move a specific consumer's PEL to a peer (the case the demo's \"Remove consumer\" button handles via `handover_pending`). `XAUTOCLAIM` cannot filter by source consumer, so it cannot be used for a per-consumer handover."},{"id":"replay-with-xrange","title":"Replay with XRANGE","role":"content","text":"`XRANGE` reads a slice of history. It is completely independent of any consumer group — no cursors move, no acks happen — so it is safe to call any number of times, from any process:\n\n[code example]\n\nThe special IDs `-` and `+` mean \"from the very beginning\" and \"to the very end\". You can also pass real IDs (`1716998413541-0`) or just the millisecond part (`1716998413541`, which Redis interprets as \"any entry with this timestamp\").\n\nTypical uses:\n\n* **Bootstrapping a new projection** — read the entire stream from `-` and build a derived view in another store (a search index, a SQL table, a different cache). Doing this against a consumer group would consume the entries; `XRANGE` lets you do it without disrupting live consumers.\n* **Auditing recent activity** — read the last few minutes by ID range without touching any group cursor.\n* **Debugging** — fetch one specific entry by its ID, or a tight range around an incident timestamp, to see exactly what producers wrote."},{"id":"the-consumer-worker-thread","title":"The consumer worker thread","role":"content","text":"`ConsumerWorker` wraps the `XREADGROUP` → process → `XACK` loop in a daemon thread\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/streaming/redis-py/consumer_worker.py)):\n\n[code example]\n\n`_handle_entry` either acks (the normal path) or, when the demo has asked the worker to \"crash\", drops the entry on the floor and increments a counter so the UI can show what is currently in the PEL waiting to be claimed.\n\nRecovery of stuck PEL entries — this consumer's, after a restart, or another consumer's, after a crash — runs through a separate `reap_idle_pel` method rather than the read loop. That method calls `XAUTOCLAIM` with this consumer as the target, then processes whatever was claimed in the same flow as new entries. This is the textbook Streams pattern: each consumer is its own reaper, running `XAUTOCLAIM(self)` periodically (or on demand) so a crashed peer's entries never sit invisibly in the PEL. The demo's \"XAUTOCLAIM to selected\" button calls `reap_idle_pel` on the chosen consumer; in production you would run it from a timer every few seconds.\n\nNote that the worker's main read loop deliberately does *not* call `XREADGROUP 0` to drain its own PEL on every iteration. That would re-deliver every pending entry continuously and *reset its idle counter to zero* each time, which would keep crashed entries below the `XAUTOCLAIM` threshold forever. Using `XAUTOCLAIM(self)` as the recovery primitive — which only fires for entries idle longer than `min_idle_time` — avoids that whole class of bug.\n\nThe pause and crash levers exist only for the demo. A real consumer is just the read-process-ack loop — everything else in this class is instrumentation."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* Redis 7.0 or later. `XAUTOCLAIM` was added in Redis 6.2, but its reply gained a third\n  element (the list of deleted IDs) in 7.0; the example relies on that shape.\n* Python 3.9 or later.\n* The `redis-py` client. Install it with:\n\n  [code example]\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of three Python files. Download them from the [`redis-py` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/streaming/redis-py) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From that directory:\n\n[code example]\n\nYou should see:\n\n[code example]\n\nBy default the demo wipes the configured stream key on startup so each run starts from a clean state. Pass `--no-reset` to keep any existing data at the key (useful when re-running against the same stream to inspect prior state), or `--stream-key <name>` to point the demo at a different key entirely.\n\nOpen [http://127.0.0.1:8083](http://127.0.0.1:8083) in a browser. You can:\n\n* **Produce** any number of events of a chosen type (or random types). Watch the stream length grow and the tail update.\n* See each **consumer group**: its `last-delivered-id`, the size of its pending list, and the consumers in it. Each consumer shows its processed count, pending count, and idle time.\n* **Add or remove** consumers within a group at runtime to see Redis split the work across the new shape.\n* Click **Crash next 3** on a consumer to drop its next three deliveries — the same effect as a worker process dying after `XREADGROUP` but before `XACK`. Watch the **Pending entries (XPENDING)** panel fill up.\n* Wait until the idle time exceeds the threshold (default 5000 ms), pick a healthy target consumer, and click **XAUTOCLAIM to selected** — the stuck entries are reassigned and the delivery counter increments.\n* **Replay (XRANGE)** any range to confirm the full history is independent of consumer-group state.\n* **XTRIM** with an approximate `MAXLEN` to bound retention. Note that an approximate trim only releases whole macro-nodes — `MAXLEN ~ 50` on a small stream may not delete anything; on a 300-entry stream it typically lands at around 100.\n* Click **Reset demo** to drop the stream and re-seed the default groups."},{"id":"production-usage","title":"Production usage","role":"content","text":""},{"id":"pick-retention-by-length-or-by-minimum-id","title":"Pick retention by length or by minimum ID","role":"content","text":"The demo uses `MAXLEN ~` on every `XADD`. Two alternatives are worth considering:\n\n* `MINID ~ <id>` — keep only entries newer than an ID. If you want \"the last 24 hours\", compute the wall-clock cutoff and pass `XTRIM MINID ~ <ms>-0`. This is the right pattern when retention is time-bounded.\n* No cap on `XADD` plus a periodic `XTRIM` job — useful if your producer is hot and the per-`XADD` work has to stay minimal, or if retention rules are complex (a separate process can also factor in consumer-group lag).\n\nIn all three cases the trimming is approximate by default. Use exact trimming (`MAXLEN n` or `MINID id` without `~`) only when you genuinely need an exact count."},{"id":"dont-let-consumer-group-lag-silently-grow","title":"Don't let consumer-group lag silently grow","role":"content","text":"`XINFO GROUPS` reports each group's `lag` (entries the group has not yet read) and `pending` (entries delivered but not acked). In production, alert on either of these crossing a threshold — a steadily growing pending count usually means consumers are crashing without `XAUTOCLAIM` running, and a growing lag means consumers can't keep up with producers.\n\nThe same applies inside a group: `XINFO CONSUMERS` reports per-consumer pending counts and idle times, so you can spot one slow consumer holding entries that the rest of the group is waiting on."},{"id":"make-consumer-logic-idempotent","title":"Make consumer logic idempotent","role":"content","text":"`XAUTOCLAIM` can re-deliver an entry to a different consumer after a crash. If your processing has side effects (sending email, charging a card, updating a downstream store), make sure the same entry processed twice gives the same result — use an idempotency key, an upsert with conditional check, or a once-per-id guard table. Redis Streams cannot give you exactly-once semantics on its own."},{"id":"bound-the-delivery-counter-as-a-poison-pill-signal","title":"Bound the delivery counter as a poison-pill signal","role":"content","text":"`XPENDING` returns each entry's delivery count, incremented on every claim. If an entry has been delivered (and dropped) several times, the next consumer is unlikely to fare better. After some threshold — `deliveries >= 5`, say — route the entry to a *dead-letter stream*, ack it on the original group, and alert. New entries keep flowing past a poison pill (`XREADGROUP >` still delivers fresh work), but the bad entry's repeated reclaim wastes consumer time and keeps the PEL bigger than it needs to be — without a DLQ threshold it can also slowly trip retention/lag alerts."},{"id":"partition-by-tenant-or-entity-for-scale","title":"Partition by tenant or entity for scale","role":"content","text":"A single Redis Stream is a single key, and on a Redis Cluster a single key lives on a single shard. If your throughput exceeds what one shard can handle, partition the stream — for example by tenant ID (`events:orders:{tenant_a}`, `events:orders:{tenant_b}`) — so different tenants land on different shards. Hash-tags (`{tenant_a}`) keep all related streams on the same shard if you need to multi-stream atomically.\n\nPer-entity partitioning (`events:order:{order_id}`) is the canonical pattern when you treat each entity's stream as the event-sourcing log for that entity: every state change for one order goes on its own stream, which is also bounded in size by the entity's lifetime."},{"id":"use-a-separate-consumer-pool-per-group","title":"Use a separate consumer pool per group","role":"content","text":"The demo runs every consumer in one process. In production each consumer group is usually its own deployment — its own pool of pods or VMs — so a slow projection in `analytics` cannot pull `notifications` workers off their stream. Each pod runs one consumer thread per CPU core, with `XAUTOCLAIM` either embedded in the consumer loop (every N reads, claim idle entries to self) or run by a separate reaper."},{"id":"dont-read-with-xread-no-group-and-then-try-to-ack","title":"Don't read with XREAD (no group) and then try to ack","role":"content","text":"`XREAD` and `XREADGROUP` are different mechanisms. `XREAD` is a tail-the-log read with no consumer-group state — entries are not added to any PEL, and you cannot `XACK` them. If you want at-least-once delivery and crash recovery, you must read through a consumer group.\n\n`XREAD` is still useful for read-only tail clients (a UI streaming events, a debugger, a `tail -f`-style command-line tool). It's just not part of the at-least-once path."},{"id":"inspect-the-stream-directly-with-redis-cli","title":"Inspect the stream directly with redis-cli","role":"content","text":"When testing or troubleshooting, inspect the stream directly to confirm the consumer state is what you expect:\n\n[code example]\n\nIf a group's `lag` is growing while consumers' `idle` times are short, consumers are healthy but producers are outpacing them — add more consumers. If `pending` is growing while `lag` is small, consumers are *receiving* entries but not *acking* them — either they are crashing mid-message or your acking logic has a bug."},{"id":"learn-more","title":"Learn more","role":"related","text":"This example uses the following Redis commands:\n\n* [`XADD`](https://redis.io/docs/latest/commands/xadd) to append an event with an approximate `MAXLEN` cap.\n* [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) to read new entries for a consumer in a group.\n* [`XACK`](https://redis.io/docs/latest/commands/xack) to acknowledge a processed entry.\n* [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) to reassign idle pending entries to a healthy consumer.\n* [`XRANGE`](https://redis.io/docs/latest/commands/xrange) for replay and audit, independent of consumer-group state.\n* [`XPENDING`](https://redis.io/docs/latest/commands/xpending) to inspect the per-group pending list with idle times and delivery counts.\n* [`XTRIM`](https://redis.io/docs/latest/commands/xtrim) for explicit retention enforcement.\n* [`XGROUP CREATE`](https://redis.io/docs/latest/commands/xgroup-create) and\n  [`XGROUP DELCONSUMER`](https://redis.io/docs/latest/commands/xgroup-delconsumer) to manage groups and consumers.\n* [`XINFO STREAM`](https://redis.io/docs/latest/commands/xinfo-stream),\n  [`XINFO GROUPS`](https://redis.io/docs/latest/commands/xinfo-groups), and\n  [`XINFO CONSUMERS`](https://redis.io/docs/latest/commands/xinfo-consumers) for observability.\n\nSee the [`redis-py` documentation](https://redis.io/docs/latest/develop/clients/redis-py) for the full client reference, and the [Streams overview](https://redis.io/docs/latest/develop/data-types/streams) for the deeper conceptual model — consumer groups, the PEL, claim semantics, capped streams, and the differences with Kafka partitions."}],"examples":[{"id":"the-event-stream-helper-ex0","language":"python","code":"import redis\nfrom event_stream import RedisEventStream\n\nr = redis.Redis(host=\"localhost\", port=6379, decode_responses=True)\nstream = RedisEventStream(\n    redis_client=r,\n    stream_key=\"demo:events:orders\",\n    maxlen_approx=2000,        # retention guardrail\n    claim_min_idle_ms=5000,    # XAUTOCLAIM threshold\n)\n\n# Producer\nstream_id = stream.produce(\n    \"order.placed\",\n    {\"order_id\": \"o-1234\", \"customer\": \"alice\", \"amount\": \"49.50\"},\n)\n\n# Consumer group + one consumer\nstream.ensure_group(\"notifications\", start_id=\"0-0\")\nentries = stream.consume(\"notifications\", \"worker-a\", count=10, block_ms=500)\nfor entry_id, fields in entries:\n    handle(fields)                              # your processing\n    stream.ack(\"notifications\", [entry_id])     # XACK\n\n# Recover stuck PEL entries by reaping them into a healthy consumer.\n# The textbook pattern: each consumer periodically calls XAUTOCLAIM\n# with itself as the target and processes whatever it claimed.\n# `ConsumerWorker.reap_idle_pel` wraps that flow; the low-level helper\n# `stream.autoclaim(group, target_name)` is also available if you\n# want to drive XAUTOCLAIM directly.\nresult = worker_b.reap_idle_pel()\n# result == {\"claimed\": N, \"processed\": M, \"deleted_ids\": [...]}\n# deleted_ids are PEL entries whose payload was already trimmed.\n# Redis 7+ has already removed those slots from the PEL, so no XACK\n# is needed — log them and route to a dead-letter store for audit.\n\n# Replay history (independent of any group's cursor)\nfor entry_id, fields in stream.replay(\"-\", \"+\", count=50):\n    print(entry_id, fields)","section_id":"the-event-stream-helper"},{"id":"data-model-ex0","language":"text","code":"demo:events:orders\n  1716998413541-0   type=order.placed     order_id=o-1234   customer=alice  amount=49.50  ts_ms=...\n  1716998413542-0   type=order.paid       order_id=o-1234   customer=alice  amount=49.50  ts_ms=...\n  1716998413542-1   type=order.shipped    order_id=o-1235   customer=bob    amount=12.00  ts_ms=...\n  ...","section_id":"data-model"},{"id":"producing-events-ex0","language":"python","code":"def produce_batch(self, events: Iterable[tuple[str, dict]]) -> list[str]:\n    pipe = self.redis.pipeline(transaction=False)\n    for event_type, payload in events:\n        fields = self._encode_fields(event_type, payload)\n        pipe.xadd(\n            self.stream_key,\n            fields,\n            maxlen=self.maxlen_approx,\n            approximate=True,\n        )\n    ids = pipe.execute()\n    ...\n    return list(ids)","section_id":"producing-events"},{"id":"reading-with-a-consumer-group-ex0","language":"python","code":"def consume(\n    self,\n    group: str,\n    consumer: str,\n    count: int = 10,\n    block_ms: int = 500,\n) -> list[Entry]:\n    result = self.redis.xreadgroup(\n        group,\n        consumer,\n        {self.stream_key: \">\"},\n        count=count,\n        block=block_ms,\n    )\n    return _flatten_entries(result)","section_id":"reading-with-a-consumer-group"},{"id":"acknowledging-entries-ex0","language":"python","code":"def ack(self, group: str, ids: Iterable[str]) -> int:\n    ids = list(ids)\n    if not ids:\n        return 0\n    return int(self.redis.xack(self.stream_key, group, *ids))","section_id":"acknowledging-entries"},{"id":"multiple-consumer-groups-one-stream-ex0","language":"python","code":"stream.ensure_group(\"notifications\", start_id=\"0-0\")\nstream.ensure_group(\"analytics\",     start_id=\"0-0\")","section_id":"multiple-consumer-groups-one-stream"},{"id":"recovering-crashed-consumers-with-xautoclaim-ex0","language":"python","code":"def reap_idle_pel(self) -> dict:\n    claimed, deleted = self.stream.autoclaim(\n        self.group, self.name, page_count=100, max_pages=10,\n    )\n    processed = 0\n    for entry_id, fields in claimed:\n        try:\n            self._handle_entry(entry_id, fields)\n            processed += 1\n        except Exception as exc:\n            print(f\"reap failed on {entry_id}: {exc}\")\n    return {\n        \"claimed\": len(claimed),\n        \"deleted_ids\": deleted,\n        \"processed\": processed,\n    }","section_id":"recovering-crashed-consumers-with-xautoclaim"},{"id":"recovering-crashed-consumers-with-xautoclaim-ex1","language":"python","code":"def autoclaim(\n    self,\n    group: str,\n    consumer: str,\n    page_count: int = 100,\n    start_id: str = \"0-0\",\n    max_pages: int = 10,\n) -> tuple[list[Entry], list[str]]:\n    claimed_all, deleted_all = [], []\n    cursor = start_id\n    for _ in range(max_pages):\n        next_id, claimed, deleted = self.redis.xautoclaim(\n            self.stream_key,\n            group,\n            consumer,\n            min_idle_time=self.claim_min_idle_ms,\n            start_id=cursor,\n            count=page_count,\n        )\n        claimed_all.extend(claimed)\n        deleted_all.extend(deleted or [])\n        if next_id == \"0-0\":\n            break\n        cursor = next_id\n    return claimed_all, deleted_all","section_id":"recovering-crashed-consumers-with-xautoclaim"},{"id":"replay-with-xrange-ex0","language":"python","code":"def replay(\n    self,\n    start_id: str = \"-\",\n    end_id: str = \"+\",\n    count: int = 100,\n) -> list[Entry]:\n    return list(self.redis.xrange(\n        self.stream_key, min=start_id, max=end_id, count=count,\n    ))","section_id":"replay-with-xrange"},{"id":"the-consumer-worker-thread-ex0","language":"python","code":"def _run(self) -> None:\n    while not self._stop_event.is_set():\n        if self._paused.is_set():\n            time.sleep(0.05)\n            continue\n        try:\n            entries = self.stream.consume(\n                self.group, self.name, count=10, block_ms=500,\n            )\n        except Exception as exc:\n            print(f\"[{self.group}/{self.name}] read failed: {exc}\")\n            time.sleep(0.5)\n            continue\n\n        for entry_id, fields in entries:\n            if self.process_latency_ms:\n                time.sleep(self.process_latency_ms / 1000.0)\n            self._handle_entry(entry_id, fields)","section_id":"the-consumer-worker-thread"},{"id":"prerequisites-ex0","language":"bash","code":"pip install \"redis>=5.0\"","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir streaming-demo && cd streaming-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/streaming/redis-py\ncurl -O $BASE/event_stream.py\ncurl -O $BASE/consumer_worker.py\ncurl -O $BASE/demo_server.py","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"python3 demo_server.py","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"Deleting any existing data at key 'demo:events:orders' for a clean demo run (pass --no-reset to keep it).\nRedis streaming demo server listening on http://127.0.0.1:8083\nUsing Redis at localhost:6379 with stream key 'demo:events:orders' (MAXLEN ~ 2000)\nSeeded 3 consumer(s) across 2 group(s)","section_id":"start-the-demo-server"},{"id":"inspect-the-stream-directly-with-redis-cli-ex0","language":"bash","code":"# Stream summary\nredis-cli XLEN demo:events:orders\nredis-cli XINFO STREAM demo:events:orders\n\n# Group cursors and pending counts\nredis-cli XINFO GROUPS demo:events:orders\n\n# Consumers within a group\nredis-cli XINFO CONSUMERS demo:events:orders notifications\n\n# Pending entries with idle time and delivery count\nredis-cli XPENDING demo:events:orders notifications - + 20\n\n# Tail the stream live (no consumer-group state — like tail -f)\nredis-cli XREAD BLOCK 0 STREAMS demo:events:orders '$'\n\n# Replay a range\nredis-cli XRANGE demo:events:orders - + COUNT 50","section_id":"inspect-the-stream-directly-with-redis-cli"}]}
{"schema_version":2,"id":"develop/use-cases/streaming/ruby","title":"Redis streaming with redis-rb","url":"https://redis.io/docs/latest/develop/use-cases/streaming/ruby/","summary":"Implement a Redis event-streaming pipeline in Ruby with redis-rb","aliases":["/develop/use-cases/streaming/redis-rb"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:55:04+01:00","children":[],"page_type":"content","content_hash":"8853a85b6c62ab127b5efb25aeb73d31be7b9784dace928cf946c41953eaf4cb","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a Redis-backed event-streaming pipeline in Ruby with the [`redis-rb`](https://redis.io/docs/latest/develop/clients/ruby) gem. It includes a small local web server built on Ruby's standard `webrick` library so you can produce events into a single Redis Stream, watch two independent consumer groups read it at their own pace, and recover stuck deliveries with `XAUTOCLAIM` after simulating a consumer crash."},{"id":"overview","title":"Overview","role":"overview","text":"A Redis Stream is an append-only log of field/value entries with auto-generated, time-ordered IDs. Producers append with [`XADD`](https://redis.io/docs/latest/commands/xadd); consumers belong to *consumer groups* and read with [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup). The group as a whole tracks a single `last-delivered-id` cursor, and each consumer gets its own pending-entries list (PEL) of messages it has been handed but not yet acknowledged. Once a consumer has processed an entry it calls [`XACK`](https://redis.io/docs/latest/commands/xack) to clear the entry from its PEL; entries left unacknowledged past an idle threshold can be reassigned to a healthy consumer with [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim).\n\nThat gives you:\n\n* Ordered, durable history that many independent consumer groups can read at their own pace\n* At-least-once delivery, with per-consumer pending lists and automatic recovery of crashed consumers\n* Horizontal scaling within a group — add a consumer and Redis automatically splits the work\n* Replay of any range with [`XRANGE`](https://redis.io/docs/latest/commands/xrange), independent of consumer-group state\n* Bounded retention through [`XADD MAXLEN ~`](https://redis.io/docs/latest/commands/xadd) or\n  [`XTRIM MINID ~`](https://redis.io/docs/latest/commands/xtrim), without a separate cleanup job\n\nIn this example, producers append order events (`order.placed`, `order.paid`, `order.shipped`, `order.cancelled`) to a single stream at `demo:events:orders`. Two consumer groups read the same stream:\n\n* **`notifications`** — two consumers (`worker-a`, `worker-b`) sharing the work, modelling a fan-out worker pool.\n* **`analytics`** — one consumer (`worker-c`) processing the full event flow on its own."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. The application calls `stream.produce(event_type, payload)` which runs [`XADD`](https://redis.io/docs/latest/commands/xadd) with an approximate [`MAXLEN ~`](https://redis.io/docs/latest/commands/xadd) cap. Redis assigns an auto-generated time-ordered ID.\n2. Each consumer thread loops on [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) with the special ID `>` (meaning \"deliver entries this group has not yet delivered to anyone\") and a short block timeout.\n3. After processing each entry, the consumer calls [`XACK`](https://redis.io/docs/latest/commands/xack) so Redis can drop it from the group's pending list.\n4. If a consumer is killed (or crashes) before acking, its entries sit in the group's PEL. A periodic [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) sweep reassigns idle entries to a healthy consumer.\n5. Anyone — including code outside the consumer groups — can read history with [`XRANGE`](https://redis.io/docs/latest/commands/xrange) without affecting any group's cursor.\n\nEach consumer group has its own cursor (`last-delivered-id`) and its own pending list, so the two groups in this demo process the same events without coordinating with each other."},{"id":"the-event-stream-helper","title":"The event-stream helper","role":"content","text":"The `RedisEventStream` class wraps the stream operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/streaming/ruby/event_stream.rb)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each event is a single stream entry — a flat hash of field/value strings — with an auto-generated time-ordered ID:\n\n[code example]\n\nThe ID is `{milliseconds}-{sequence}`, monotonically increasing within the stream, so you can range-query by approximate wall-clock time without an extra index. (IDs are ordered within a stream, not across streams — two events appended to different streams at the same millisecond can produce the same ID.) The implementation uses:\n\n* [`XADD ... MAXLEN ~ n`](https://redis.io/docs/latest/commands/xadd), pipelined, for batch production with a retention cap\n* [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) with the special ID `>` for fresh deliveries to a consumer\n* [`XACK`](https://redis.io/docs/latest/commands/xack) on every processed entry\n* [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) for sweeping idle pending entries to a healthy consumer\n* [`XRANGE`](https://redis.io/docs/latest/commands/xrange) for replay and audit\n* [`XPENDING`](https://redis.io/docs/latest/commands/xpending) for inspecting the per-group pending list\n* [`XINFO STREAM`](https://redis.io/docs/latest/commands/xinfo-stream),\n  [`XINFO GROUPS`](https://redis.io/docs/latest/commands/xinfo-groups), and\n  [`XINFO CONSUMERS`](https://redis.io/docs/latest/commands/xinfo-consumers) for surface-level observability\n* [`XTRIM`](https://redis.io/docs/latest/commands/xtrim) for explicit retention enforcement"},{"id":"producing-events","title":"Producing events","role":"content","text":"`produce_batch` pipelines `XADD` calls in a single round trip. Each call carries an approximate `MAXLEN ~` cap so the stream stays bounded as it rolls forward:\n\n[code example]\n\nThe `~` flavour of `MAXLEN` lets Redis trim at a macro-node boundary, which is much cheaper than exact trimming and is what you want when the cap is a retention *guardrail*, not a hard size constraint. With 300 events produced and `MAXLEN ~ 50`, you might end up with 100 entries left — Redis released the oldest whole macro-node and stopped. The next `XADD` will keep length stable.\n\nIf you genuinely need an exact cap (rare), drop `approximate: true`. The performance difference is significant on busy streams."},{"id":"reading-with-a-consumer-group","title":"Reading with a consumer group","role":"content","text":"Each consumer in a group runs the same `XREADGROUP` loop. The special ID `>` means \"deliver entries this group has not yet delivered to *anyone*\":\n\n[code example]\n\n`block_ms` makes the call efficient even when the stream is idle: the client parks on the server until either an entry arrives or the timeout expires, so consumers don't busy-loop.\n\nReading with an explicit ID like `0-0` instead of `>` does something different — it replays entries already delivered to *this* consumer name (its private PEL). That is the canonical recovery path when the same consumer restarts: catch up on its own pending entries first, then resume reading new ones.\n\nA `redis-rb` `Redis` instance holds a single TCP connection and serialises every command through a monitor; a blocking `XREADGROUP BLOCK 500` parks that monitor for up to 500 ms, so every consumer must use its *own* `Redis` connection or its blocking read will park every other thread sharing the same client. The demo gives each `ConsumerWorker` a dedicated read connection and shares one connection across the HTTP handlers (which only issue non-blocking commands)."},{"id":"acknowledging-entries","title":"Acknowledging entries","role":"content","text":"Once the consumer has processed an entry, `XACK` tells Redis it can drop the entry from the group's pending list:\n\n[code example]\n\nThis is the linchpin of at-least-once delivery: an entry that is never acked stays in the PEL until a claim moves it elsewhere. If your consumer thread crashes between processing and ack, the next claim sweep picks the entry back up. The one caveat is retention: `XADD MAXLEN ~` and `XTRIM` can release the entry's *payload* even while its ID is still in the PEL. The next `XAUTOCLAIM` returns those IDs in its `deleted` list and removes them from the PEL inside the same command — the entry cannot be retried, so the caller should log it and route to a dead-letter store for audit. The example handles this explicitly in `reap_idle_pel` further down.\n\nThe trade-off is the opposite of pub/sub: a slow or crashed consumer doesn't lose messages, but it does mean your downstream system must be idempotent. If you process an order twice because the first attempt died after the side effect but before the ack, the second attempt must be safe."},{"id":"multiple-consumer-groups-one-stream","title":"Multiple consumer groups, one stream","role":"content","text":"The big difference between Redis Streams and a job queue is that any number of independent consumer groups can read the same stream. The demo sets up two groups on `demo:events:orders`:\n\n[code example]\n\nEach group has its own cursor. Producing 5 events results in `notifications` and `analytics` each receiving all 5, with no coordination between them. Within `notifications`, the work is split across `worker-a` and `worker-b`: Redis hands each `XREADGROUP` call whatever entries are not yet delivered to anyone in the group, so adding a second worker doubles throughput without any rebalance logic.\n\nThe `'0-0'` start ID means \"deliver everything in the stream from the beginning\" — useful in a demo and for fresh groups bootstrapping from history. In production, a brand-new group reading a long-existing stream usually starts at `'$'` (\"only events after this point\") and uses [`XRANGE`](https://redis.io/docs/latest/commands/xrange) explicitly if it needs history."},{"id":"recovering-crashed-consumers-with-xautoclaim","title":"Recovering crashed consumers with XAUTOCLAIM","role":"content","text":"The demo's \"Crash next 3\" button tells a chosen consumer to drop its next three deliveries on the floor without acking them — the same effect as a worker process dying mid-message. Those entries stay in the group's PEL with their delivery counter incremented. Once they have been idle for at least `claim_min_idle_ms`, any healthy consumer in the group can rescue them by calling `XAUTOCLAIM` *with itself as the target*. `ConsumerWorker#reap_idle_pel` wraps that pattern:\n\n[code example]\n\nThe underlying `stream.autoclaim` helper pages through the group's PEL with `XAUTOCLAIM`'s continuation cursor. `redis-rb` 5.x exposes a typed `xautoclaim(...)` wrapper, but it discards the third element of the reply (the list of IDs whose payload was already trimmed). The helper drops down to the raw command via `redis.call(...)` so the deleted-IDs list survives:\n\n[code example]\n\nA single `XAUTOCLAIM` call scans up to `page_count` PEL entries starting at `start_id`, reassigns the ones idle for at least `min_idle_time` to the named consumer, and returns a continuation cursor in the first slot of the reply. For a full sweep, loop until the cursor returns to `'0-0'` (with a `max_pages` safety net so one call cannot monopolise a very large PEL). The delivery counter is incremented on every claim — after a few cycles you can use it to spot a *poison-pill* message that crashes every consumer that touches it, and route it to a dead-letter stream so the bad entry stops cycling. (New entries keep flowing past the poison pill — `XREADGROUP >` still delivers fresh work — but the bad entry's repeated reclaim wastes consumer time and keeps the PEL larger than it needs to be.)\n\nThe `deleted_ids` list contains PEL entry IDs whose stream payload was already trimmed by the time the claim ran (typically because `MAXLEN ~` retention outran a slow consumer). `XAUTOCLAIM` removes those dangling slots from the PEL itself, so the caller does *not* need to `XACK` them — but the entries cannot be retried either, so log and route them to a dead-letter store for offline inspection. Redis 7.0 introduced this third return element; the example requires Redis 7.0+ for that reason.\n\n`reap_idle_pel` is the right primitive for the recovery path because it claims and processes in one step: every entry the call returned is now in *this* consumer's PEL, so the same consumer is responsible for processing and acking it. In production each consumer thread runs `reap_idle_pel` periodically (every few seconds, on a timer) so a crashed peer's entries never sit invisibly. The demo exposes it as a manual button so you can trigger the reap after waiting for the idle threshold.\n\n`XCLAIM` (singular, no auto) does the same thing for a specific list of entry IDs you already have in hand — useful when you want to take ownership of one known stuck entry, or when you need to move a specific consumer's PEL to a peer (the case the demo's \"Remove consumer\" button handles via `handover_pending`). `XAUTOCLAIM` cannot filter by source consumer, so it cannot be used for a per-consumer handover."},{"id":"replay-with-xrange","title":"Replay with XRANGE","role":"content","text":"`XRANGE` reads a slice of history. It is completely independent of any consumer group — no cursors move, no acks happen — so it is safe to call any number of times, from any process:\n\n[code example]\n\nThe special IDs `-` and `+` mean \"from the very beginning\" and \"to the very end\". You can also pass real IDs (`1716998413541-0`) or just the millisecond part (`1716998413541`, which Redis interprets as \"any entry with this timestamp\").\n\nTypical uses:\n\n* **Bootstrapping a new projection** — read the entire stream from `-` and build a derived view in another store (a search index, a SQL table, a different cache). Doing this against a consumer group would consume the entries; `XRANGE` lets you do it without disrupting live consumers.\n* **Auditing recent activity** — read the last few minutes by ID range without touching any group cursor.\n* **Debugging** — fetch one specific entry by its ID, or a tight range around an incident timestamp, to see exactly what producers wrote."},{"id":"the-consumer-worker-thread","title":"The consumer worker thread","role":"content","text":"`ConsumerWorker` wraps the `XREADGROUP` → process → `XACK` loop in a Ruby `Thread`\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/streaming/ruby/consumer_worker.rb)):\n\n[code example]\n\n`dispatch` either acks (the normal path) or, when the demo has asked the worker to \"crash\", drops the entry on the floor and increments a counter so the UI can show what is currently in the PEL waiting to be claimed. The `begin/rescue` block around the per-entry handler is important: a transient failure (typically `XACK` against a flaky network) must not kill the worker thread — otherwise every other entry in this consumer's PEL would sit invisibly waiting for `XAUTOCLAIM`. The entry stays unacked instead; the next `reap_idle_pel` call (here or on any consumer in the group) recovers it.\n\nEach `ConsumerWorker` is constructed with two `RedisEventStream` handles:\n\n* A `read_stream` with its own dedicated `Redis` connection. The blocking `XREADGROUP BLOCK 500` would otherwise park the demo server's primary connection (and any HTTP handler sharing it) for up to 500 ms.\n* A `shared_stream` pointing at the demo server's primary connection. Non-blocking commands (`XACK`, `XAUTOCLAIM`) run through this handle so stats land in a single aggregate counter the UI can render.\n\nRecovery of stuck PEL entries — this consumer's, after a restart, or another consumer's, after a crash — runs through a separate `reap_idle_pel` method rather than the read loop. That method calls `XAUTOCLAIM` with this consumer as the target, then processes whatever was claimed in the same flow as new entries. This is the textbook Streams pattern: each consumer is its own reaper, running `XAUTOCLAIM(self)` periodically (or on demand) so a crashed peer's entries never sit invisibly in the PEL. The demo's \"XAUTOCLAIM to selected\" button calls `reap_idle_pel` on the chosen consumer; in production you would run it from a timer every few seconds.\n\nNote that the worker's main read loop deliberately does *not* call `XREADGROUP 0` to drain its own PEL on every iteration. That would re-deliver every pending entry continuously and *reset its idle counter to zero* each time, which would keep crashed entries below the `XAUTOCLAIM` threshold forever. Using `XAUTOCLAIM(self)` as the recovery primitive — which only fires for entries idle longer than `min_idle_time` — avoids that whole class of bug.\n\nThe pause and crash levers exist only for the demo. A real consumer is just the read-process-ack loop — everything else in this class is instrumentation."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* Redis 7.0 or later. `XAUTOCLAIM` was added in Redis 6.2, but its reply gained a third\n  element (the list of deleted IDs) in 7.0; the example relies on that shape.\n* Ruby 3.0 or later.\n* The `redis` gem at version 5.x. `webrick` was removed from Ruby's standard library in\n  Ruby 3.0, so install both:\n\n  [code example]\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of three Ruby files. Download them from the [`ruby` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/streaming/ruby) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From that directory, install the gems and run the server:\n\n[code example]\n\nYou should see:\n\n[code example]\n\nBy default the demo wipes the configured stream key on startup so each run starts from a clean state. Pass `--no-reset` to keep any existing data at the key (useful when re-running against the same stream to inspect prior state), or `--stream-key <name>` to point the demo at a different key entirely.\n\nOpen [http://127.0.0.1:8787](http://127.0.0.1:8787) in a browser. You can:\n\n* **Produce** any number of events of a chosen type (or random types). Watch the stream length grow and the tail update.\n* See each **consumer group**: its `last-delivered-id`, the size of its pending list, and the consumers in it. Each consumer shows its processed count, pending count, and idle time.\n* **Add or remove** consumers within a group at runtime to see Redis split the work across the new shape.\n* Click **Crash next 3** on a consumer to drop its next three deliveries — the same effect as a worker process dying after `XREADGROUP` but before `XACK`. Watch the **Pending entries (XPENDING)** panel fill up.\n* Wait until the idle time exceeds the threshold (default 5000 ms), pick a healthy target consumer, and click **XAUTOCLAIM to selected** — the stuck entries are reassigned and the delivery counter increments.\n* **Replay (XRANGE)** any range to confirm the full history is independent of consumer-group state.\n* **XTRIM** with an approximate `MAXLEN` to bound retention. Note that an approximate trim only releases whole macro-nodes — `MAXLEN ~ 50` on a small stream may not delete anything; on a 300-entry stream it typically lands at around 100.\n* Click **Reset demo** to drop the stream and re-seed the default groups."},{"id":"production-usage","title":"Production usage","role":"content","text":""},{"id":"pick-retention-by-length-or-by-minimum-id","title":"Pick retention by length or by minimum ID","role":"content","text":"The demo uses `MAXLEN ~` on every `XADD`. Two alternatives are worth considering:\n\n* `MINID ~ <id>` — keep only entries newer than an ID. If you want \"the last 24 hours\", compute the wall-clock cutoff and pass `XTRIM MINID ~ <ms>-0`. This is the right pattern when retention is time-bounded.\n* No cap on `XADD` plus a periodic `XTRIM` job — useful if your producer is hot and the per-`XADD` work has to stay minimal, or if retention rules are complex (a separate process can also factor in consumer-group lag).\n\nIn all three cases the trimming is approximate by default. Use exact trimming (`MAXLEN n` or `MINID id` without `~`) only when you genuinely need an exact count."},{"id":"dont-let-consumer-group-lag-silently-grow","title":"Don't let consumer-group lag silently grow","role":"content","text":"`XINFO GROUPS` reports each group's `lag` (entries the group has not yet read) and `pending` (entries delivered but not acked). In production, alert on either of these crossing a threshold — a steadily growing pending count usually means consumers are crashing without `XAUTOCLAIM` running, and a growing lag means consumers can't keep up with producers.\n\nThe same applies inside a group: `XINFO CONSUMERS` reports per-consumer pending counts and idle times, so you can spot one slow consumer holding entries that the rest of the group is waiting on."},{"id":"make-consumer-logic-idempotent","title":"Make consumer logic idempotent","role":"content","text":"`XAUTOCLAIM` can re-deliver an entry to a different consumer after a crash. If your processing has side effects (sending email, charging a card, updating a downstream store), make sure the same entry processed twice gives the same result — use an idempotency key, an upsert with conditional check, or a once-per-id guard table. Redis Streams cannot give you exactly-once semantics on its own."},{"id":"bound-the-delivery-counter-as-a-poison-pill-signal","title":"Bound the delivery counter as a poison-pill signal","role":"content","text":"`XPENDING` returns each entry's delivery count, incremented on every claim. If an entry has been delivered (and dropped) several times, the next consumer is unlikely to fare better. After some threshold — `deliveries >= 5`, say — route the entry to a *dead-letter stream*, ack it on the original group, and alert. New entries keep flowing past a poison pill (`XREADGROUP >` still delivers fresh work), but the bad entry's repeated reclaim wastes consumer time and keeps the PEL bigger than it needs to be — without a DLQ threshold it can also slowly trip retention/lag alerts."},{"id":"partition-by-tenant-or-entity-for-scale","title":"Partition by tenant or entity for scale","role":"content","text":"A single Redis Stream is a single key, and on a Redis Cluster a single key lives on a single shard. If your throughput exceeds what one shard can handle, partition the stream — for example by tenant ID (`events:orders:{tenant_a}`, `events:orders:{tenant_b}`) — so different tenants land on different shards. Hash-tags (`{tenant_a}`) keep all related streams on the same shard if you need to multi-stream atomically.\n\nPer-entity partitioning (`events:order:{order_id}`) is the canonical pattern when you treat each entity's stream as the event-sourcing log for that entity: every state change for one order goes on its own stream, which is also bounded in size by the entity's lifetime."},{"id":"use-a-separate-consumer-pool-per-group","title":"Use a separate consumer pool per group","role":"content","text":"The demo runs every consumer in one Ruby process. In production each consumer group is usually its own deployment — its own pool of pods or VMs — so a slow projection in `analytics` cannot pull `notifications` workers off their stream. Each pod runs one consumer thread per CPU core (mind the GVL: pure-Ruby consumers won't go in parallel on MRI), with `XAUTOCLAIM` either embedded in the consumer loop (every N reads, claim idle entries to self) or run by a separate reaper."},{"id":"use-one-redis-connection-per-consumer-thread","title":"Use one `Redis` connection per consumer thread","role":"content","text":"A `redis-rb` `Redis` instance holds a single TCP socket and serialises every command through an internal monitor. A blocking call like `XREADGROUP BLOCK 500` parks that monitor for the full block window, so a second thread issuing *any* command — even a simple `XACK` — would queue behind it. The demo gives each `ConsumerWorker` a dedicated read connection for the blocking `XREADGROUP` loop and routes its `XACK` and `XAUTOCLAIM` calls through the demo server's shared connection so the aggregate stats counter stays in sync. In production with `redis-rb`, use a `ConnectionPool` (from the `connection_pool` gem) for the non-blocking side and a dedicated per-consumer `Redis` for the blocking reads."},{"id":"dont-read-with-xread-no-group-and-then-try-to-ack","title":"Don't read with XREAD (no group) and then try to ack","role":"content","text":"`XREAD` and `XREADGROUP` are different mechanisms. `XREAD` is a tail-the-log read with no consumer-group state — entries are not added to any PEL, and you cannot `XACK` them. If you want at-least-once delivery and crash recovery, you must read through a consumer group.\n\n`XREAD` is still useful for read-only tail clients (a UI streaming events, a debugger, a `tail -f`-style command-line tool). It's just not part of the at-least-once path."},{"id":"inspect-the-stream-directly-with-redis-cli","title":"Inspect the stream directly with redis-cli","role":"content","text":"When testing or troubleshooting, inspect the stream directly to confirm the consumer state is what you expect:\n\n[code example]\n\nIf a group's `lag` is growing while consumers' `idle` times are short, consumers are healthy but producers are outpacing them — add more consumers. If `pending` is growing while `lag` is small, consumers are *receiving* entries but not *acking* them — either they are crashing mid-message or your acking logic has a bug."},{"id":"learn-more","title":"Learn more","role":"related","text":"This example uses the following Redis commands:\n\n* [`XADD`](https://redis.io/docs/latest/commands/xadd) to append an event with an approximate `MAXLEN` cap.\n* [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) to read new entries for a consumer in a group.\n* [`XACK`](https://redis.io/docs/latest/commands/xack) to acknowledge a processed entry.\n* [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) to reassign idle pending entries to a healthy consumer.\n* [`XCLAIM`](https://redis.io/docs/latest/commands/xclaim) to take ownership of a specific list of pending entry IDs by hand (used by `handover_pending` to move a leaving consumer's PEL to a peer, since `XAUTOCLAIM` has no source-consumer filter).\n* [`XRANGE`](https://redis.io/docs/latest/commands/xrange) for replay and audit, independent of consumer-group state.\n* [`XPENDING`](https://redis.io/docs/latest/commands/xpending) to inspect the per-group pending list with idle times and delivery counts.\n* [`XTRIM`](https://redis.io/docs/latest/commands/xtrim) for explicit retention enforcement.\n* [`XGROUP CREATE`](https://redis.io/docs/latest/commands/xgroup-create) and\n  [`XGROUP DELCONSUMER`](https://redis.io/docs/latest/commands/xgroup-delconsumer) to manage groups and consumers.\n* [`XINFO STREAM`](https://redis.io/docs/latest/commands/xinfo-stream),\n  [`XINFO GROUPS`](https://redis.io/docs/latest/commands/xinfo-groups), and\n  [`XINFO CONSUMERS`](https://redis.io/docs/latest/commands/xinfo-consumers) for observability.\n\nSee the [`redis-rb` documentation](https://redis.io/docs/latest/develop/clients/ruby) for the full client reference, and the [Streams overview](https://redis.io/docs/latest/develop/data-types/streams) for the deeper conceptual model — consumer groups, the PEL, claim semantics, capped streams, and the differences with Kafka partitions."}],"examples":[{"id":"the-event-stream-helper-ex0","language":"ruby","code":"require 'redis'\nrequire_relative 'event_stream'\n\nredis = Redis.new(host: 'localhost', port: 6379)\nstream = RedisEventStream.new(\n  redis: redis,\n  stream_key: 'demo:events:orders',\n  maxlen_approx: 2000,        # retention guardrail\n  claim_min_idle_ms: 5000,    # XAUTOCLAIM threshold\n)\n\n# Producer\nstream_id = stream.produce(\n  'order.placed',\n  { 'order_id' => 'o-1234', 'customer' => 'alice', 'amount' => '49.50' },\n)\n\n# Consumer group + one consumer\nstream.ensure_group('notifications', '0-0')\nentries = stream.consume('notifications', 'worker-a', count: 10, block_ms: 500)\nentries.each do |entry_id, fields|\n  handle(fields)                                # your processing\n  stream.ack('notifications', [entry_id])       # XACK\nend\n\n# Recover stuck PEL entries by reaping them into a healthy consumer.\n# The textbook pattern: each consumer periodically calls XAUTOCLAIM\n# with itself as the target and processes whatever it claimed.\n# `ConsumerWorker#reap_idle_pel` wraps that flow; the low-level helper\n# `stream.autoclaim(group, target_name)` is also available if you\n# want to drive XAUTOCLAIM directly.\nresult = worker_b.reap_idle_pel\n# result == { claimed: N, processed: M, deleted_ids: [...] }\n# deleted_ids are PEL entries whose payload was already trimmed.\n# Redis 7+ has already removed those slots from the PEL, so no XACK\n# is needed -- log them and route to a dead-letter store for audit.\n\n# Replay history (independent of any group's cursor)\nstream.replay(start_id: '-', end_id: '+', count: 50).each do |entry_id, fields|\n  puts \"#{entry_id} #{fields.inspect}\"\nend","section_id":"the-event-stream-helper"},{"id":"data-model-ex0","language":"text","code":"demo:events:orders\n  1716998413541-0   type=order.placed     order_id=o-1234   customer=alice  amount=49.50  ts_ms=...\n  1716998413542-0   type=order.paid       order_id=o-1234   customer=alice  amount=49.50  ts_ms=...\n  1716998413542-1   type=order.shipped    order_id=o-1235   customer=bob    amount=12.00  ts_ms=...\n  ...","section_id":"data-model"},{"id":"producing-events-ex0","language":"ruby","code":"def produce_batch(events)\n  events = events.to_a\n  return [] if events.empty?\n  ids = @redis.pipelined do |pipe|\n    events.each do |event_type, payload|\n      fields = encode_fields(event_type, payload)\n      pipe.xadd(@stream_key, fields,\n                maxlen: @maxlen_approx, approximate: true)\n    end\n  end\n  @stats_lock.synchronize { @produced_total += ids.length }\n  ids\nend","section_id":"producing-events"},{"id":"reading-with-a-consumer-group-ex0","language":"ruby","code":"def consume(group, consumer, count: 10, block_ms: 500)\n  result = @redis.xreadgroup(group, consumer, @stream_key, '>',\n                             count: count, block: block_ms)\n  flatten_entries(result)\nend","section_id":"reading-with-a-consumer-group"},{"id":"acknowledging-entries-ex0","language":"ruby","code":"def ack(group, ids)\n  ids = Array(ids)\n  return 0 if ids.empty?\n  n = @redis.xack(@stream_key, group, ids).to_i\n  @stats_lock.synchronize { @acked_total += n }\n  n\nend","section_id":"acknowledging-entries"},{"id":"multiple-consumer-groups-one-stream-ex0","language":"ruby","code":"stream.ensure_group('notifications', '0-0')\nstream.ensure_group('analytics',     '0-0')","section_id":"multiple-consumer-groups-one-stream"},{"id":"recovering-crashed-consumers-with-xautoclaim-ex0","language":"ruby","code":"def reap_idle_pel\n  result = @shared_stream.autoclaim(@group, @name,\n                                    page_count: 100, max_pages: 10)\n  claimed = result[:claimed]\n  deleted = result[:deleted_ids]\n  processed = 0\n  claimed.each do |entry_id, fields|\n    begin\n      handle_entry(entry_id, fields)\n      processed += 1\n    rescue StandardError => exc\n      warn \"[#{@group}/#{@name}] reap failed on #{entry_id}: #{exc}\"\n    end\n  end\n  @lock.synchronize { @reaped += processed }\n  { claimed: claimed.length, processed: processed, deleted_ids: deleted }\nend","section_id":"recovering-crashed-consumers-with-xautoclaim"},{"id":"recovering-crashed-consumers-with-xautoclaim-ex1","language":"ruby","code":"def autoclaim(group, consumer, page_count: 100, start_id: '0-0',\n              max_pages: 10)\n  claimed_all = []\n  deleted_all = []\n  cursor = start_id\n  max_pages.times do\n    reply = @redis.call('XAUTOCLAIM', @stream_key, group, consumer,\n                        @claim_min_idle_ms.to_s, cursor,\n                        'COUNT', page_count.to_s)\n    next_cursor = reply[0]\n    claimed = parse_entries(reply[1])\n    deleted = Array(reply[2])\n    claimed_all.concat(claimed)\n    deleted_all.concat(deleted)\n    break if next_cursor == '0-0'\n    cursor = next_cursor\n  end\n  @stats_lock.synchronize { @claimed_total += claimed_all.length }\n  { claimed: claimed_all, deleted_ids: deleted_all }\nend","section_id":"recovering-crashed-consumers-with-xautoclaim"},{"id":"replay-with-xrange-ex0","language":"ruby","code":"def replay(start_id: '-', end_id: '+', count: 100)\n  rows = @redis.xrange(@stream_key, start_id, end_id, count: count)\n  rows.map { |entry_id, fields| [entry_id, fields] }\nend","section_id":"replay-with-xrange"},{"id":"the-consumer-worker-thread-ex0","language":"ruby","code":"def run\n  until @stop\n    if @lock.synchronize { @paused }\n      sleep 0.05\n      next\n    end\n    begin\n      entries = @read_stream.consume(@group, @name, count: 10, block_ms: 500)\n    rescue StandardError => exc\n      # Don't kill the thread on a transient Redis error; a real\n      # consumer would log this and back off.\n      warn \"[#{@group}/#{@name}] read failed: #{exc}\"\n      sleep 0.5\n      next\n    end\n\n    entries.each do |entry_id, fields|\n      dispatch(entry_id, fields)\n    end\n  end\nend","section_id":"the-consumer-worker-thread"},{"id":"prerequisites-ex0","language":"bash","code":"gem install redis webrick","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir streaming-demo && cd streaming-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/streaming/ruby\ncurl -O $BASE/event_stream.rb\ncurl -O $BASE/consumer_worker.rb\ncurl -O $BASE/demo_server.rb","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"gem install redis webrick\nruby demo_server.rb","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"Deleting any existing data at key 'demo:events:orders' for a clean demo run (pass --no-reset to keep it).\nRedis streaming demo server listening on http://127.0.0.1:8787\nUsing Redis at localhost:6379 with stream key 'demo:events:orders' (MAXLEN ~ 2000)\nSeeded 3 consumer(s) across 2 group(s)","section_id":"start-the-demo-server"},{"id":"inspect-the-stream-directly-with-redis-cli-ex0","language":"bash","code":"# Stream summary\nredis-cli XLEN demo:events:orders\nredis-cli XINFO STREAM demo:events:orders\n\n# Group cursors and pending counts\nredis-cli XINFO GROUPS demo:events:orders\n\n# Consumers within a group\nredis-cli XINFO CONSUMERS demo:events:orders notifications\n\n# Pending entries with idle time and delivery count\nredis-cli XPENDING demo:events:orders notifications - + 20\n\n# Tail the stream live (no consumer-group state — like tail -f)\nredis-cli XREAD BLOCK 0 STREAMS demo:events:orders '$'\n\n# Replay a range\nredis-cli XRANGE demo:events:orders - + COUNT 50","section_id":"inspect-the-stream-directly-with-redis-cli"}]}
{"schema_version":2,"id":"develop/use-cases/streaming/rust","title":"Redis streaming with redis-rs","url":"https://redis.io/docs/latest/develop/use-cases/streaming/rust/","summary":"Implement a Redis event-streaming pipeline in Rust with redis-rs","aliases":["/develop/use-cases/streaming/redis-rs"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:55:04+01:00","children":[],"page_type":"content","content_hash":"639d516c6749d3b4be32fc4a4ef87f1f2ff9df772c178c680fbce42ba3037b63","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a Redis-backed event-streaming pipeline in Rust with the [`redis`](https://crates.io/crates/redis) crate (redis-rs). It includes a small local web server built with `axum` and `tokio` so you can produce events into a single Redis Stream, watch two independent consumer groups read it at their own pace, and recover stuck deliveries with `XAUTOCLAIM` after simulating a consumer crash."},{"id":"overview","title":"Overview","role":"overview","text":"A Redis Stream is an append-only log of field/value entries with auto-generated, time-ordered IDs. Producers append with [`XADD`](https://redis.io/docs/latest/commands/xadd); consumers belong to *consumer groups* and read with [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup). The group as a whole tracks a single `last-delivered-id` cursor, and each consumer gets its own pending-entries list (PEL) of messages it has been handed but not yet acknowledged. Once a consumer has processed an entry it calls [`XACK`](https://redis.io/docs/latest/commands/xack) to clear the entry from its PEL; entries left unacknowledged past an idle threshold can be reassigned to a healthy consumer with [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim).\n\nThat gives you:\n\n* Ordered, durable history that many independent consumer groups can read at their own pace\n* At-least-once delivery, with per-consumer pending lists and automatic recovery of crashed consumers\n* Horizontal scaling within a group — add a consumer and Redis automatically splits the work\n* Replay of any range with [`XRANGE`](https://redis.io/docs/latest/commands/xrange), independent of consumer-group state\n* Bounded retention through [`XADD MAXLEN ~`](https://redis.io/docs/latest/commands/xadd) or\n  [`XTRIM MINID ~`](https://redis.io/docs/latest/commands/xtrim), without a separate cleanup job\n\nIn this example, producers append order events (`order.placed`, `order.paid`, `order.shipped`, `order.cancelled`) to a single stream at `demo:events:orders`. Two consumer groups read the same stream:\n\n* **`notifications`** — two consumers (`worker-a`, `worker-b`) sharing the work, modelling a fan-out worker pool.\n* **`analytics`** — one consumer (`worker-c`) processing the full event flow on its own."},{"id":"how-it-works","title":"How it works","role":"content","text":"The flow looks like this:\n\n1. The application calls `stream.produce(event_type, payload).await` which runs [`XADD`](https://redis.io/docs/latest/commands/xadd) with an approximate [`MAXLEN ~`](https://redis.io/docs/latest/commands/xadd) cap. Redis assigns an auto-generated time-ordered ID.\n2. Each consumer task loops on [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) with the special ID `>` (meaning \"deliver entries this group has not yet delivered to anyone\") and a short block timeout.\n3. After processing each entry, the consumer calls [`XACK`](https://redis.io/docs/latest/commands/xack) so Redis can drop it from the group's pending list.\n4. If a consumer is killed (or crashes) before acking, its entries sit in the group's PEL. A periodic [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) sweep reassigns idle entries to a healthy consumer.\n5. Anyone — including code outside the consumer groups — can read history with [`XRANGE`](https://redis.io/docs/latest/commands/xrange) without affecting any group's cursor.\n\nEach consumer group has its own cursor (`last-delivered-id`) and its own pending list, so the two groups in this demo process the same events without coordinating with each other."},{"id":"the-event-stream-helper","title":"The event-stream helper","role":"content","text":"The `EventStream` struct wraps the stream operations\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/streaming/rust/event_stream.rs)):\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Each event is a single stream entry — a flat map of field/value strings — with an auto-generated time-ordered ID:\n\n[code example]\n\nThe ID is `{milliseconds}-{sequence}`, monotonically increasing within the stream, so you can range-query by approximate wall-clock time without an extra index. (IDs are ordered within a stream, not across streams — two events appended to different streams at the same millisecond can produce the same ID.) The implementation uses:\n\n* [`XADD ... MAXLEN ~ n`](https://redis.io/docs/latest/commands/xadd), pipelined, for batch production with a retention cap\n* [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) with the special ID `>` for fresh deliveries to a consumer\n* [`XACK`](https://redis.io/docs/latest/commands/xack) on every processed entry\n* [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) for sweeping idle pending entries to a healthy consumer\n* [`XRANGE`](https://redis.io/docs/latest/commands/xrange) for replay and audit\n* [`XPENDING`](https://redis.io/docs/latest/commands/xpending) for inspecting the per-group pending list\n* [`XINFO STREAM`](https://redis.io/docs/latest/commands/xinfo-stream),\n  [`XINFO GROUPS`](https://redis.io/docs/latest/commands/xinfo-groups), and\n  [`XINFO CONSUMERS`](https://redis.io/docs/latest/commands/xinfo-consumers) for surface-level observability\n* [`XTRIM`](https://redis.io/docs/latest/commands/xtrim) for explicit retention enforcement"},{"id":"producing-events","title":"Producing events","role":"content","text":"`produce_batch` pipelines `XADD` calls in a single round trip. Each call carries an approximate `MAXLEN ~` cap so the stream stays bounded as it rolls forward:\n\n[code example]\n\nThe `~` flavour of `MAXLEN` lets Redis trim at a macro-node boundary, which is much cheaper than exact trimming and is what you want when the cap is a retention *guardrail*, not a hard size constraint. With 300 events produced and `MAXLEN ~ 50`, you might end up with 100 entries left — Redis released the oldest whole macro-node and stopped. The next `XADD` will keep length stable.\n\nIf you genuinely need an exact cap (rare), use `MAXLEN` without the `~`. The performance difference is significant on busy streams."},{"id":"reading-with-a-consumer-group","title":"Reading with a consumer group","role":"content","text":"Each consumer in a group runs the same `XREADGROUP` loop. The special ID `>` means \"deliver entries this group has not yet delivered to *anyone*\":\n\n[code example]\n\n`block_ms` makes the call efficient even when the stream is idle: the client parks on the server until either an entry arrives or the timeout expires, so consumers don't busy-loop.\n\nReading with an explicit ID like `0-0` instead of `>` does something different — it replays entries already delivered to *this* consumer name (its private PEL). That is the canonical recovery path when the same consumer restarts: catch up on its own pending entries first, then resume reading new ones."},{"id":"acknowledging-entries","title":"Acknowledging entries","role":"content","text":"Once the consumer has processed an entry, `XACK` tells Redis it can drop the entry from the group's pending list:\n\n[code example]\n\nThis is the linchpin of at-least-once delivery: an entry that is never acked stays in the PEL until a claim moves it elsewhere. If your consumer task crashes between processing and ack, the next claim sweep picks the entry back up. The one caveat is retention: `XADD MAXLEN ~` and `XTRIM` can release the entry's *payload* even while its ID is still in the PEL. The next `XAUTOCLAIM` returns those IDs in its `deleted` list and removes them from the PEL inside the same command — the entry cannot be retried, so the caller should log it and route to a dead-letter store for audit. The example handles this explicitly in the autoclaim flow further down.\n\nThe trade-off is the opposite of pub/sub: a slow or crashed consumer doesn't lose messages, but it does mean your downstream system must be idempotent. If you process an order twice because the first attempt died after the side effect but before the ack, the second attempt must be safe."},{"id":"multiple-consumer-groups-one-stream","title":"Multiple consumer groups, one stream","role":"content","text":"The big difference between Redis Streams and a job queue is that any number of independent consumer groups can read the same stream. The demo sets up two groups on `demo:events:orders`:\n\n[code example]\n\nEach group has its own cursor. Producing 5 events results in `notifications` and `analytics` each receiving all 5, with no coordination between them. Within `notifications`, the work is split across `worker-a` and `worker-b`: Redis hands each `XREADGROUP` call whatever entries are not yet delivered to anyone in the group, so adding a second worker doubles throughput without any rebalance logic.\n\nThe `\"0-0\"` argument means \"deliver everything in the stream from the beginning\" — useful in a demo and for fresh groups bootstrapping from history. In production, a brand-new group reading a long-existing stream usually starts at `$` (\"only events after this point\") and uses [`XRANGE`](https://redis.io/docs/latest/commands/xrange) explicitly if it needs history."},{"id":"recovering-crashed-consumers-with-xautoclaim","title":"Recovering crashed consumers with XAUTOCLAIM","role":"content","text":"The demo's \"Crash next 3\" button tells a chosen consumer to drop its next three deliveries on the floor without acking them — the same effect as a worker process dying mid-message. Those entries stay in the group's PEL with their delivery counter incremented. Once they have been idle for at least `claim_min_idle_ms`, any healthy consumer in the group can rescue them by calling `XAUTOCLAIM` *with itself as the target*. `ConsumerWorker::reap_idle_pel` wraps that pattern\n([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/streaming/rust/consumer_worker.rs)):\n\n[code example]\n\nThe underlying `stream.autoclaim` helper pages through the group's PEL with `XAUTOCLAIM`'s continuation cursor. redis-rs 0.24 has no typed wrapper for `XAUTOCLAIM`, so the helper builds it from raw `redis::cmd(\"XAUTOCLAIM\")` and decodes the three-element Redis 7+ reply by hand:\n\n[code example]\n\nA single `XAUTOCLAIM` call scans up to `page_count` PEL entries starting at `start_id`, reassigns the ones idle for at least `min_idle_time` to the named consumer, and returns a continuation cursor in the first slot of the reply. For a full sweep, loop until the cursor returns to `0-0` (with a `max_pages` safety net so one call cannot monopolise a very large PEL). The delivery counter is incremented on every claim — after a few cycles you can use it to spot a *poison-pill* message that crashes every consumer that touches it, and route it to a dead-letter stream so the bad entry stops cycling. (New entries keep flowing past the poison pill — `XREADGROUP >` still delivers fresh work — but the bad entry's repeated reclaim wastes consumer time and keeps the PEL larger than it needs to be.)\n\nThe `deleted` list contains PEL entry IDs whose stream payload was already trimmed by the time the claim ran (typically because `MAXLEN ~` retention outran a slow consumer). `XAUTOCLAIM` removes those dangling slots from the PEL itself, so the caller does *not* need to `XACK` them — but the entries cannot be retried either, so log and route them to a dead-letter store for offline inspection. Redis 7.0 introduced this third return element; the example requires Redis 7.0+ for that reason.\n\n`reap_idle_pel` is the right primitive for the recovery path because it claims and processes in one step: every entry the call returned is now in *this* consumer's PEL, so the same consumer is responsible for processing and acking it. In production each consumer task runs `reap_idle_pel` periodically (every few seconds, on a timer) so a crashed peer's entries never sit invisibly. The demo exposes it as a manual button so you can trigger the reap after waiting for the idle threshold.\n\n`XCLAIM` (singular, no auto) does the same thing for a specific list of entry IDs you already have in hand — useful when you want to take ownership of one known stuck entry, or when you need to move a specific consumer's PEL to a peer (the case the demo's \"Remove consumer\" button handles via `handover_pending`). `XAUTOCLAIM` cannot filter by source consumer, so it cannot be used for a per-consumer handover."},{"id":"replay-with-xrange","title":"Replay with XRANGE","role":"content","text":"`XRANGE` reads a slice of history. It is completely independent of any consumer group — no cursors move, no acks happen — so it is safe to call any number of times, from any task:\n\n[code example]\n\nThe special IDs `-` and `+` mean \"from the very beginning\" and \"to the very end\". You can also pass real IDs (`1716998413541-0`) or just the millisecond part (`1716998413541`, which Redis interprets as \"any entry with this timestamp\").\n\nTypical uses:\n\n* **Bootstrapping a new projection** — read the entire stream from `-` and build a derived view in another store (a search index, a SQL table, a different cache). Doing this against a consumer group would consume the entries; `XRANGE` lets you do it without disrupting live consumers.\n* **Auditing recent activity** — read the last few minutes by ID range without touching any group cursor.\n* **Debugging** — fetch one specific entry by its ID, or a tight range around an incident timestamp, to see exactly what producers wrote."},{"id":"the-consumer-worker-task","title":"The consumer worker task","role":"content","text":"`ConsumerWorker` wraps the `XREADGROUP` → process → `XACK` loop in a spawned tokio task. The main loop reads new entries with `XREADGROUP >`, processes each entry with a small sleep to simulate work, then acks it. If the demo has asked the worker to drop the next *n* deliveries, those entries are recorded as dropped and left in the PEL so `XAUTOCLAIM` can recover them later:\n\n[code example]\n\n`dispatch` wraps `handle_entry` so an `XACK` failure (a Redis hiccup) logs a \"handler error\" entry and continues, rather than tearing down the spawned task. Letting a Redis error bubble through `spawn`'s panic boundary would silently halt the consumer while every other entry sat in its PEL waiting for `XAUTOCLAIM`.\n\nRecovery of stuck PEL entries — this consumer's, after a restart, or another consumer's, after a crash — runs through a separate `reap_idle_pel` method rather than the read loop. That method calls `XAUTOCLAIM` with this consumer as the target, then processes whatever was claimed in the same flow as new entries. This is the textbook Streams pattern: each consumer is its own reaper, running `XAUTOCLAIM(self)` periodically (or on demand) so a crashed peer's entries never sit invisibly in the PEL. The demo's \"XAUTOCLAIM to selected\" button calls `reap_idle_pel` on the chosen consumer; in production you would run it from a timer every few seconds.\n\nNote that the worker's main read loop deliberately does *not* call `XREADGROUP 0` to drain its own PEL on every iteration. That would re-deliver every pending entry continuously and *reset its idle counter to zero* each time, which would keep crashed entries below the `XAUTOCLAIM` threshold forever. Using `XAUTOCLAIM(self)` as the recovery primitive — which only fires for entries idle longer than `min_idle_time` — avoids that whole class of bug.\n\nThe pause and crash levers exist only for the demo. A real consumer is just the read-process-ack loop — everything else in this class is instrumentation."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* Redis 7.0 or later. `XAUTOCLAIM` was added in Redis 6.2, but its reply gained a third\n  element (the list of deleted IDs) in 7.0; the example relies on that shape.\n* Rust 1.70 or later (stable).\n* The `redis` crate at version 0.24+, with features `[\"tokio-comp\", \"aio\", \"connection-manager\", \"streams\"]`. The `connection-manager` feature gives `ConnectionManager` for cheap, cloneable, auto-reconnecting connections; `streams` adds the typed reply structs for `XINFO`/`XPENDING`.\n\nThe `Cargo.toml` for this demo pins all the runtime crates:\n\n[code example]\n\nIf your Redis server is running elsewhere, start the demo with `--redis-host` and `--redis-port`."},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of four files. Download them from the [`rust` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/streaming/rust) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"From that directory:\n\n[code example]\n\nYou should see:\n\n[code example]\n\nBy default the demo wipes the configured stream key on startup so each run starts from a clean state. Pass `--no-reset` to keep any existing data at the key (useful when re-running against the same stream to inspect prior state), or `--stream-key <name>` to point the demo at a different key entirely.\n\nOpen [http://127.0.0.1:8788](http://127.0.0.1:8788) in a browser. You can:\n\n* **Produce** any number of events of a chosen type (or random types). Watch the stream length grow and the tail update.\n* See each **consumer group**: its `last-delivered-id`, the size of its pending list, and the consumers in it. Each consumer shows its processed count, pending count, and idle time.\n* **Add or remove** consumers within a group at runtime to see Redis split the work across the new shape.\n* Click **Crash next 3** on a consumer to drop its next three deliveries — the same effect as a worker process dying after `XREADGROUP` but before `XACK`. Watch the **Pending entries (XPENDING)** panel fill up.\n* Wait until the idle time exceeds the threshold (default 5000 ms), pick a healthy target consumer, and click **XAUTOCLAIM to selected** — the stuck entries are reassigned and the delivery counter increments.\n* **Replay (XRANGE)** any range to confirm the full history is independent of consumer-group state.\n* **XTRIM** with an approximate `MAXLEN` to bound retention. Note that an approximate trim only releases whole macro-nodes — `MAXLEN ~ 50` on a small stream may not delete anything; on a 300-entry stream it typically lands at around 100.\n* Click **Reset demo** to drop the stream and re-seed the default groups."},{"id":"production-usage","title":"Production usage","role":"content","text":""},{"id":"use-one-redis-connection-per-blocking-consumer-in-real-workloads","title":"Use one Redis connection per blocking consumer in real workloads","role":"content","text":"`redis-rs`'s `ConnectionManager` wraps a single multiplexed connection. That's the right default for short non-blocking commands — `XADD`, `XACK`, `XPENDING`, `XINFO` — but blocking calls like `XREADGROUP ... BLOCK 500` tie up the server side of the connection for the block duration. If many consumers share one `ConnectionManager`, their `XREADGROUP` calls effectively serialise: while Redis is parked waiting for the first consumer's block to expire (or deliver), the second consumer's request can't even leave the local pipeline. For the demo this is fine — three consumers reading an idle stream produce a steady 1.5s round-trip — but a production deployment with N consumers should hold one `ConnectionManager` per consumer (or per small fan-out) so the block windows don't fight each other."},{"id":"atomically-reserve-consumer-names-in-the-in-process-registry","title":"Atomically reserve consumer names in the in-process registry","role":"content","text":"The demo keeps an in-process registry of `(group, name) → ConsumerWorker` so the HTTP routes can find a worker by name, route a \"crash next 3\" request to it, and move its PEL on remove. Two concurrent `add-worker` requests with the same name must produce **one** success and **one** \"already exists\" error — not two registry entries with one orphaned. Because the registry is mutated from inside `async` HTTP handlers, the duplicate-check has to be atomic against any concurrent caller.\n\nThe pitfall here is that `std::sync::Mutex` is `!Send` and so cannot be held across `.await`; the natural \"check the map → release the lock → spawn the worker → re-acquire and insert\" structure leaves a window where two callers can both pass the check and both insert. The demo uses a `tokio::sync::Mutex` instead and holds it across the whole reservation + spawn sequence:\n\n[code example]\n\n`tokio::sync::Mutex` is `Send`, so the compiler is happy to keep the guard alive across `.await`. The result is that the name is reserved, the worker is spawned, and the insert lands while no other caller can race in. A second concurrent call with the same name waits on the lock, sees the entry, and returns `false`."},{"id":"pick-retention-by-length-or-by-minimum-id","title":"Pick retention by length or by minimum ID","role":"content","text":"The demo uses `MAXLEN ~` on every `XADD`. Two alternatives are worth considering:\n\n* `MINID ~ <id>` — keep only entries newer than an ID. If you want \"the last 24 hours\", compute the wall-clock cutoff and call `stream.trim_minid(\"<ms>-0\").await?`. This is the right pattern when retention is time-bounded.\n* No cap on `XADD` plus a periodic `XTRIM` task — useful if your producer is hot and the per-`XADD` work has to stay minimal, or if retention rules are complex (a separate task can also factor in consumer-group lag).\n\nIn all three cases the trimming is approximate by default. Use exact trimming (`MAXLEN n` or `MINID id` without `~`) only when you genuinely need an exact count."},{"id":"dont-let-consumer-group-lag-silently-grow","title":"Don't let consumer-group lag silently grow","role":"content","text":"`XINFO GROUPS` reports each group's `lag` (entries the group has not yet read) and `pending` (entries delivered but not acked). In production, alert on either of these crossing a threshold — a steadily growing pending count usually means consumers are crashing without `XAUTOCLAIM` running, and a growing lag means consumers can't keep up with producers.\n\nThe same applies inside a group: `XINFO CONSUMERS` reports per-consumer pending counts and idle times, so you can spot one slow consumer holding entries that the rest of the group is waiting on."},{"id":"make-consumer-logic-idempotent","title":"Make consumer logic idempotent","role":"content","text":"`XAUTOCLAIM` can re-deliver an entry to a different consumer after a crash. If your processing has side effects (sending email, charging a card, updating a downstream store), make sure the same entry processed twice gives the same result — use an idempotency key, an upsert with conditional check, or a once-per-id guard table. Redis Streams cannot give you exactly-once semantics on its own."},{"id":"bound-the-delivery-counter-as-a-poison-pill-signal","title":"Bound the delivery counter as a poison-pill signal","role":"content","text":"`XPENDING` returns each entry's delivery count, incremented on every claim. If an entry has been delivered (and dropped) several times, the next consumer is unlikely to fare better. After some threshold — `deliveries >= 5`, say — route the entry to a *dead-letter stream*, ack it on the original group, and alert. New entries keep flowing past a poison pill (`XREADGROUP >` still delivers fresh work), but the bad entry's repeated reclaim wastes consumer time and keeps the PEL bigger than it needs to be — without a DLQ threshold it can also slowly trip retention/lag alerts."},{"id":"partition-by-tenant-or-entity-for-scale","title":"Partition by tenant or entity for scale","role":"content","text":"A single Redis Stream is a single key, and on a Redis Cluster a single key lives on a single shard. If your throughput exceeds what one shard can handle, partition the stream — for example by tenant ID (`events:orders:{tenant_a}`, `events:orders:{tenant_b}`) — so different tenants land on different shards. Hash-tags (`{tenant_a}`) keep all related streams on the same shard if you need to multi-stream atomically.\n\nPer-entity partitioning (`events:order:{order_id}`) is the canonical pattern when you treat each entity's stream as the event-sourcing log for that entity: every state change for one order goes on its own stream, which is also bounded in size by the entity's lifetime."},{"id":"use-a-separate-consumer-pool-per-group","title":"Use a separate consumer pool per group","role":"content","text":"The demo runs every consumer in one process. In production each consumer group is usually its own deployment — its own pool of pods or VMs — so a slow projection in `analytics` cannot pull `notifications` workers off their stream. Each pod runs one consumer task per CPU core, with `XAUTOCLAIM` either embedded in the consumer loop (every N reads, claim idle entries to self) or run by a separate reaper task."},{"id":"dont-read-with-xread-no-group-and-then-try-to-ack","title":"Don't read with XREAD (no group) and then try to ack","role":"content","text":"`XREAD` and `XREADGROUP` are different mechanisms. `XREAD` is a tail-the-log read with no consumer-group state — entries are not added to any PEL, and you cannot `XACK` them. If you want at-least-once delivery and crash recovery, you must read through a consumer group.\n\n`XREAD` is still useful for read-only tail clients (a UI streaming events, a debugger, a `tail -f`-style command-line tool). It's just not part of the at-least-once path."},{"id":"inspect-the-stream-directly-with-redis-cli","title":"Inspect the stream directly with redis-cli","role":"content","text":"When testing or troubleshooting, inspect the stream directly to confirm the consumer state is what you expect:\n\n[code example]\n\nIf a group's `lag` is growing while consumers' `idle` times are short, consumers are healthy but producers are outpacing them — add more consumers. If `pending` is growing while `lag` is small, consumers are *receiving* entries but not *acking* them — either they are crashing mid-message or your acking logic has a bug."},{"id":"learn-more","title":"Learn more","role":"related","text":"This example uses the following Redis commands:\n\n* [`XADD`](https://redis.io/docs/latest/commands/xadd) to append an event with an approximate `MAXLEN` cap.\n* [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) to read new entries for a consumer in a group.\n* [`XACK`](https://redis.io/docs/latest/commands/xack) to acknowledge a processed entry.\n* [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) to reassign idle pending entries to a healthy consumer.\n* [`XCLAIM`](https://redis.io/docs/latest/commands/xclaim) to take ownership of a specific list of pending entry IDs by hand (used by `handover_pending` to move a leaving consumer's PEL to a peer, since `XAUTOCLAIM` has no source-consumer filter).\n* [`XRANGE`](https://redis.io/docs/latest/commands/xrange) for replay and audit, independent of consumer-group state.\n* [`XPENDING`](https://redis.io/docs/latest/commands/xpending) to inspect the per-group pending list with idle times and delivery counts.\n* [`XTRIM`](https://redis.io/docs/latest/commands/xtrim) for explicit retention enforcement.\n* [`XGROUP CREATE`](https://redis.io/docs/latest/commands/xgroup-create) and\n  [`XGROUP DELCONSUMER`](https://redis.io/docs/latest/commands/xgroup-delconsumer) to manage groups and consumers.\n* [`XINFO STREAM`](https://redis.io/docs/latest/commands/xinfo-stream),\n  [`XINFO GROUPS`](https://redis.io/docs/latest/commands/xinfo-groups), and\n  [`XINFO CONSUMERS`](https://redis.io/docs/latest/commands/xinfo-consumers) for observability.\n\nSee the [`redis-rs` documentation](https://docs.rs/redis/) for the full client reference, and the [Streams overview](https://redis.io/docs/latest/develop/data-types/streams) for the deeper conceptual model — consumer groups, the PEL, claim semantics, capped streams, and the differences with Kafka partitions."}],"examples":[{"id":"the-event-stream-helper-ex0","language":"rust","code":"use redis::aio::ConnectionManager;\nuse redis::Client;\nuse std::collections::HashMap;\n\nlet client = Client::open(\"redis://127.0.0.1:6379/\")?;\nlet conn = ConnectionManager::new(client).await?;\nlet stream = EventStream::new(\n    conn,\n    \"demo:events:orders\",   // stream key\n    2000,                   // approximate MAXLEN guardrail\n    5000,                   // XAUTOCLAIM idle threshold (ms)\n);\n\n// Producer\nlet mut payload = HashMap::new();\npayload.insert(\"order_id\".into(), \"o-1234\".into());\npayload.insert(\"customer\".into(), \"alice\".into());\npayload.insert(\"amount\".into(), \"49.50\".into());\nlet stream_id = stream.produce(\"order.placed\", payload).await?;\n\n// Consumer group + one consumer\nstream.ensure_group(\"notifications\", \"0-0\").await?;\nlet entries = stream.consume(\"notifications\", \"worker-a\", 10, 500).await?;\nfor (entry_id, fields) in entries {\n    handle(&fields);\n    stream.ack(\"notifications\", vec![entry_id]).await?;   // XACK\n}\n\n// Recover stuck PEL entries by reaping them into a healthy consumer.\n// The textbook pattern: each consumer periodically calls XAUTOCLAIM\n// with itself as the target and processes whatever it claimed.\n// `ConsumerWorker::reap_idle_pel` wraps that flow; the low-level helper\n// `stream.autoclaim(group, target, ...)` is also available if you\n// want to drive XAUTOCLAIM directly.\nlet result = worker_b.reap_idle_pel().await;\n// result == ReapResult { claimed: N, processed: M, deleted_ids: [...] }\n// deleted_ids are PEL entries whose payload was already trimmed.\n// Redis 7+ has already removed those slots from the PEL, so no XACK\n// is needed — log them and route to a dead-letter store for audit.\n\n// Replay history (independent of any group's cursor)\nfor (entry_id, fields) in stream.replay(\"-\", \"+\", 50).await? {\n    println!(\"{} {:?}\", entry_id, fields);\n}","section_id":"the-event-stream-helper"},{"id":"data-model-ex0","language":"text","code":"demo:events:orders\n  1716998413541-0   type=order.placed     order_id=o-1234   customer=alice  amount=49.50  ts_ms=...\n  1716998413542-0   type=order.paid       order_id=o-1234   customer=alice  amount=49.50  ts_ms=...\n  1716998413542-1   type=order.shipped    order_id=o-1235   customer=bob    amount=12.00  ts_ms=...\n  ...","section_id":"data-model"},{"id":"producing-events-ex0","language":"rust","code":"pub async fn produce_batch(\n    &self,\n    events: Vec<(String, HashMap<String, String>)>,\n) -> RedisResult<Vec<String>> {\n    if events.is_empty() {\n        return Ok(Vec::new());\n    }\n    let mut pipe = redis::pipe();\n    for (event_type, payload) in &events {\n        let fields = encode_fields(event_type, payload);\n        pipe.cmd(\"XADD\")\n            .arg(&self.stream_key)\n            .arg(\"MAXLEN\")\n            .arg(\"~\")\n            .arg(self.maxlen_approx)\n            .arg(\"*\");\n        for (k, v) in &fields {\n            pipe.arg(k).arg(v);\n        }\n    }\n    let mut conn = self.conn.clone();\n    let ids: Vec<String> = pipe.query_async(&mut conn).await?;\n    self.stats\n        .produced_total\n        .fetch_add(ids.len() as u64, Ordering::Relaxed);\n    Ok(ids)\n}","section_id":"producing-events"},{"id":"reading-with-a-consumer-group-ex0","language":"rust","code":"pub async fn consume(\n    &self,\n    group: &str,\n    consumer: &str,\n    count: usize,\n    block_ms: usize,\n) -> RedisResult<Vec<Entry>> {\n    let opts = StreamReadOptions::default()\n        .group(group, consumer)\n        .count(count)\n        .block(block_ms);\n    let mut conn = self.conn.clone();\n    let reply: Option<StreamReadReply> = conn\n        .xread_options(&[self.stream_key.as_str()], &[\">\"], &opts)\n        .await?;\n    Ok(flatten_read_reply(reply))\n}","section_id":"reading-with-a-consumer-group"},{"id":"acknowledging-entries-ex0","language":"rust","code":"pub async fn ack(&self, group: &str, ids: Vec<String>) -> RedisResult<i64> {\n    if ids.is_empty() {\n        return Ok(0);\n    }\n    let mut conn = self.conn.clone();\n    let n: i64 = conn.xack(&self.stream_key, group, &ids).await?;\n    self.stats.acked_total.fetch_add(n as u64, Ordering::Relaxed);\n    Ok(n)\n}","section_id":"acknowledging-entries"},{"id":"multiple-consumer-groups-one-stream-ex0","language":"rust","code":"stream.ensure_group(\"notifications\", \"0-0\").await?;\nstream.ensure_group(\"analytics\",     \"0-0\").await?;","section_id":"multiple-consumer-groups-one-stream"},{"id":"recovering-crashed-consumers-with-xautoclaim-ex0","language":"rust","code":"pub async fn reap_idle_pel(&self) -> ReapResult {\n    let (claimed, deleted) = match self\n        .stream\n        .autoclaim(&self.group, &self.name, 100, \"0-0\", 10)\n        .await\n    {\n        Ok(v) => v,\n        Err(_) => return ReapResult::default(),\n    };\n    let mut processed: u64 = 0;\n    for (entry_id, fields) in claimed.iter() {\n        tokio::time::sleep(self.process_latency).await;\n        if self.handle_entry(entry_id.clone(), fields.clone()).await.is_ok() {\n            processed += 1;\n        }\n    }\n    ReapResult {\n        claimed: claimed.len() as u64,\n        processed,\n        deleted_ids: deleted,\n    }\n}","section_id":"recovering-crashed-consumers-with-xautoclaim"},{"id":"recovering-crashed-consumers-with-xautoclaim-ex1","language":"rust","code":"pub async fn autoclaim(\n    &self,\n    group: &str,\n    consumer: &str,\n    page_count: usize,\n    start_id: &str,\n    max_pages: usize,\n) -> RedisResult<(Vec<Entry>, Vec<String>)> {\n    let mut claimed_all: Vec<Entry> = Vec::new();\n    let mut deleted_all: Vec<String> = Vec::new();\n    let mut cursor = start_id.to_string();\n    let mut conn = self.conn.clone();\n    for _ in 0..max_pages {\n        let raw: Value = redis::cmd(\"XAUTOCLAIM\")\n            .arg(&self.stream_key)\n            .arg(group)\n            .arg(consumer)\n            .arg(self.claim_min_idle_ms)\n            .arg(&cursor)\n            .arg(\"COUNT\")\n            .arg(page_count)\n            .query_async(&mut conn)\n            .await?;\n        let (next_cursor, claimed, deleted) = parse_autoclaim_reply(raw)?;\n        claimed_all.extend(claimed);\n        deleted_all.extend(deleted);\n        if next_cursor == \"0-0\" {\n            break;\n        }\n        cursor = next_cursor;\n    }\n    Ok((claimed_all, deleted_all))\n}","section_id":"recovering-crashed-consumers-with-xautoclaim"},{"id":"replay-with-xrange-ex0","language":"rust","code":"pub async fn replay(\n    &self,\n    start_id: &str,\n    end_id: &str,\n    count: usize,\n) -> RedisResult<Vec<Entry>> {\n    let mut conn = self.conn.clone();\n    let reply: StreamRangeReply = conn\n        .xrange_count(&self.stream_key, start_id, end_id, count)\n        .await?;\n    Ok(stream_ids_to_entries(reply.ids))\n}","section_id":"replay-with-xrange"},{"id":"the-consumer-worker-task-ex0","language":"rust","code":"async fn run_loop(self: Arc<Self>) {\n    loop {\n        let (stop, paused) = {\n            let inner = self.inner.lock().await;\n            (inner.stop, inner.paused)\n        };\n        if stop {\n            return;\n        }\n        if paused {\n            tokio::time::sleep(Duration::from_millis(50)).await;\n            continue;\n        }\n\n        let entries: Vec<Entry> =\n            match self.stream.consume(&self.group, &self.name, 10, 500).await {\n                Ok(v) => v,\n                Err(err) => {\n                    eprintln!(\"[{}/{}] read failed: {}\", self.group, self.name, err);\n                    tokio::time::sleep(Duration::from_millis(500)).await;\n                    continue;\n                }\n            };\n        for (entry_id, fields) in entries {\n            self.dispatch(entry_id, fields).await;\n        }\n    }\n}","section_id":"the-consumer-worker-task"},{"id":"prerequisites-ex0","language":"toml","code":"[dependencies]\nredis = { version = \"0.24\", features = [\"tokio-comp\", \"aio\", \"connection-manager\", \"streams\"] }\ntokio = { version = \"1\", features = [\"full\"] }\naxum = \"0.7\"\nserde = { version = \"1.0\", features = [\"derive\"] }\nserde_json = \"1.0\"\nrand = \"0.8\"","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir streaming-demo && cd streaming-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/streaming/rust\ncurl -O $BASE/Cargo.toml\ncurl -O $BASE/event_stream.rs\ncurl -O $BASE/consumer_worker.rs\ncurl -O $BASE/demo_server.rs","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"cargo run --release","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"text","code":"Deleting any existing data at key 'demo:events:orders' for a clean demo run (pass --no-reset to keep it).\nRedis streaming demo server listening on http://127.0.0.1:8788\nUsing Redis at localhost:6379 with stream key 'demo:events:orders' (MAXLEN ~ 2000)\nSeeded 3 consumer(s) across 2 group(s)","section_id":"start-the-demo-server"},{"id":"atomically-reserve-consumer-names-in-the-in-process-registry-ex0","language":"rust","code":"async fn add_worker(self: &Arc<Self>, group: &str, name: &str) -> bool {\n    let key: WorkerKey = (group.to_string(), name.to_string());\n    let mut guard = self.workers.lock().await;\n    if guard.contains_key(&key) {\n        return false;\n    }\n    let _ = self.stream.ensure_group(group, \"0-0\").await;\n    let worker = ConsumerWorker::new(self.stream.clone(), group, name);\n    worker.start().await;\n    guard.insert(key, worker);\n    true\n}","section_id":"atomically-reserve-consumer-names-in-the-in-process-registry"},{"id":"inspect-the-stream-directly-with-redis-cli-ex0","language":"bash","code":"# Stream summary\nredis-cli XLEN demo:events:orders\nredis-cli XINFO STREAM demo:events:orders\n\n# Group cursors and pending counts\nredis-cli XINFO GROUPS demo:events:orders\n\n# Consumers within a group\nredis-cli XINFO CONSUMERS demo:events:orders notifications\n\n# Pending entries with idle time and delivery count\nredis-cli XPENDING demo:events:orders notifications - + 20\n\n# Tail the stream live (no consumer-group state — like tail -f)\nredis-cli XREAD BLOCK 0 STREAMS demo:events:orders '$'\n\n# Replay a range\nredis-cli XRANGE demo:events:orders - + COUNT 50","section_id":"inspect-the-stream-directly-with-redis-cli"}]}
{"schema_version":2,"id":"develop/use-cases/time-series-dashboard/dotnet","title":"Rolling sensor graph demo with Redis and .NET","url":"https://redis.io/docs/latest/develop/use-cases/time-series-dashboard/dotnet/","summary":"Build a Redis-backed rolling sensor graph demo in .NET with NRedisStack","aliases":["/develop/use-cases/time-series-dashboard/stackexchange.redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:55:04+01:00","children":[],"page_type":"content","content_hash":"4436765c6c25fada6aa1e0028d0ba4d2cdc9fea95321cbaf1c349b1fd2d3b724","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a compact rolling sensor graph demo in .NET with [`NRedisStack`](https://redis.io/docs/latest/develop/clients/dotnet/nredisstack), the Redis Stack extension for [`StackExchange.Redis`](https://redis.io/docs/latest/develop/clients/dotnet). The example simulates three power sensors, ingests readings into Redis, and serves a local browser dashboard that updates in real time."},{"id":"overview","title":"Overview","role":"overview","text":"Time series are a natural fit for telemetry, monitoring, and IoT-style workloads. In this example, Redis stores a stream of timestamped readings from three simulated power sensors.\n\nThe demo is designed to make a few ideas easy to see:\n\n* Raw readings arrive every 500ms\n* Each graph shows only the most recent 12 seconds\n* Older samples disappear because the time series retention is short\n* 3-second buckets summarize the same readings with minimum, maximum, and average values\n* The bucket summaries are drawn under the same moving time scale as the graph"},{"id":"how-it-works","title":"How it works","role":"content","text":"The example has three main parts:\n\n1. A `SensorSimulator` generates realistic-looking power readings with drift and occasional spikes\n2. A `RedisTimeSeriesStore` creates the time series keys and issues Redis TimeSeries queries\n3. A small local HTTP server built with ASP.NET Core minimal APIs renders three stacked combined graph-and-bucket views and polls a JSON snapshot endpoint\n\nEach sensor is stored in its own time series with labels such as `sensor_type`, `sensor_id`, `zone`, and `unit`. The dashboard then uses [`TS.MADD`](https://redis.io/docs/latest/commands/ts.madd) to ingest new readings and [`TS.RANGE`](https://redis.io/docs/latest/commands/ts.range) to query both raw samples and aggregated bucket summaries. The aggregate queries use aligned buckets so the bucket boundaries stay stable as the visible window moves."},{"id":"the-net-files","title":"The .NET files","role":"content","text":"The implementation is split across three small files:\n\n* [`SensorSimulator.cs`](https://github.com/redis/docs/blob/main/content/develop/use-cases/time-series-dashboard/dotnet/SensorSimulator.cs) - Sensor definitions and sample generation\n* [`RedisTimeSeriesStore.cs`](https://github.com/redis/docs/blob/main/content/develop/use-cases/time-series-dashboard/dotnet/RedisTimeSeriesStore.cs) - Redis TimeSeries command helpers\n* [`Program.cs`](https://github.com/redis/docs/blob/main/content/develop/use-cases/time-series-dashboard/dotnet/Program.cs) - Local HTTP server and inline dashboard UI\n\nThe Redis helper calls `db.TS()` to get an `ITimeSeriesCommandsAsync` from NRedisStack, then issues time series commands as strongly typed methods (`CreateAsync`, `MAddAsync`, `RangeAsync`, `GetAsync`) that return `TimeSeriesTuple` values rather than raw `RedisResult` arrays."},{"id":"data-model","title":"Data model","role":"content","text":"Series keys use this pattern:\n\n[code example]\n\nFor example:\n\n[code example]\n\nEach time series has labels similar to:\n\n[code example]\n\nThe demo uses a 12-second retention period so the graphs visibly slide forward as old samples expire. The setup is also idempotent, so you can stop and restart the demo without first cleaning up the time series keys."},{"id":"redis-commands-used","title":"Redis commands used","role":"content","text":"The implementation issues these time series commands through NRedisStack's typed `db.TS()` API:\n\n* [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create) (`CreateAsync`) - Create one time series per sensor with retention and labels\n* [`TS.MADD`](https://redis.io/docs/latest/commands/ts.madd) (`MAddAsync`) - Batch-ingest readings from all three sensors every 500ms\n* [`TS.GET`](https://redis.io/docs/latest/commands/ts.get) (`GetAsync`) - Fetch the latest reading for a sensor\n* [`TS.RANGE`](https://redis.io/docs/latest/commands/ts.range) (`RangeAsync`) - Read raw recent samples and aggregated 3-second buckets\n* `ALIGN 0` with `TS.RANGE ... AGGREGATION` - Keep bucket boundaries stable as the visible window moves"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before running the demo, make sure that:\n\n* Redis is running and accessible. By default, the demo connects to `localhost:6379`.\n* Your Redis deployment includes time series support.\n* The NRedisStack and StackExchange.Redis packages are available:\n\n[code example]"},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of three C# source files plus the project file. Download them from the [`dotnet` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/time-series-dashboard/dotnet) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"Start the dashboard server:\n\n[code example]\n\nThe server accepts optional flags if your Redis instance is not on the default host and port:\n\n[code example]\n\nAfter starting the server, visit `http://localhost:8080`.\n\nThe dashboard polls a JSON snapshot endpoint several times per second to show:\n\n* Three stacked rolling graphs of raw sensor readings\n* A bucket summary strip aligned to the same time axis as each graph\n* Minimum, maximum, and average values for each visible bucket\n* A moving 12-second window where old samples disappear as retention expires them\n\nBecause the graph and the bucket summary share the same moving time scale, you can see how raw samples relate to their aggregate bucket without switching views or interpreting a separate table."},{"id":"what-to-look-for","title":"What to look for","role":"content","text":"As you watch the dashboard, pay attention to how the Redis query patterns map to the UI:\n\n* New points arrive every 500ms through `TS.MADD`\n* The graphs show raw values returned by `TS.RANGE`\n* The bucket summaries use `TS.RANGE ... ALIGN 0 AGGREGATION min|max|avg 3000`\n* The left edge of the graph keeps advancing because the time series retention is short\n* The bucket boundaries stay fixed even while the visible window moves\n* The dashboard remains safe to rerun because series creation is idempotent"},{"id":"why-this-shape-works-well","title":"Why this shape works well","role":"content","text":"This demo intentionally uses only three sensors and a very short time horizon. That keeps the visualization small enough to understand at a glance while still demonstrating:\n\n* Repeated high-frequency ingest\n* Querying recent raw history\n* Aggregating into fixed time buckets\n* Short retention and visible expiration\n\nFor a first time series example, this is often easier to understand than a larger dashboard with many metrics, filters, or tables."},{"id":"production-considerations","title":"Production considerations","role":"content","text":"This example intentionally keeps the server and UI small so the Redis behavior is easy to follow. In production, you would usually want to add:\n\n* Authentication and authorization\n* Separate static assets instead of inline HTML\n* Better error reporting and health checks\n* Deployment-specific retention, window sizes, and aggregation intervals\n* Stronger key namespacing if multiple applications share the same Redis deployment"},{"id":"learn-more","title":"Learn more","role":"related","text":"* [NRedisStack guide](https://redis.io/docs/latest/develop/clients/dotnet/nredisstack) - Use NRedisStack for Redis Stack data types and commands\n* [.NET client guide](https://redis.io/docs/latest/develop/clients/dotnet) - Install and use the .NET client\n* [Time series overview](https://redis.io/docs/latest/develop/data-types/timeseries) - Time series concepts and commands\n* [TS.RANGE command](https://redis.io/docs/latest/commands/ts.range) - Query raw and aggregated ranges from a time series\n* [TS.MADD command](https://redis.io/docs/latest/commands/ts.madd) - Add multiple samples in one call\n* [TS.CREATE command](https://redis.io/docs/latest/commands/ts.create) - Create a time series with labels and retention"}],"examples":[{"id":"data-model-ex0","language":"text","code":"ts:sensor:power_consumption:{sensor_id}","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"ts:sensor:power_consumption:power-1\nts:sensor:power_consumption:power-2\nts:sensor:power_consumption:power-3","section_id":"data-model"},{"id":"data-model-ex2","language":"text","code":"site = demo\nsensor_type = power_consumption\nsensor_id = power-1\nzone = north\nunit = watts","section_id":"data-model"},{"id":"prerequisites-ex0","language":"bash","code":"dotnet add package NRedisStack\ndotnet add package StackExchange.Redis","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir time-series-dashboard-demo && cd time-series-dashboard-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/time-series-dashboard/dotnet\ncurl -O $BASE/SensorSimulator.cs\ncurl -O $BASE/RedisTimeSeriesStore.cs\ncurl -O $BASE/Program.cs\ncurl -O $BASE/TimeSeriesDashboardDemo.csproj","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"dotnet run","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"bash","code":"dotnet run -- --redis-host 127.0.0.1 --redis-port 6379 --port 8080","section_id":"start-the-demo-server"}]}
{"schema_version":2,"id":"develop/use-cases/time-series-dashboard/go","title":"Rolling sensor graph demo with Go","url":"https://redis.io/docs/latest/develop/use-cases/time-series-dashboard/go/","summary":"Build a Redis-backed rolling sensor graph demo in Go with go-redis","aliases":["/develop/use-cases/time-series-dashboard/go-redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:55:04+01:00","children":[],"page_type":"content","content_hash":"b9c5a47640138532cb873bf2ce94eaf08cd707d54da8ee5764562cec539b9a73","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a compact rolling sensor graph demo in Go with the [`go-redis`](https://redis.io/docs/latest/develop/clients/go) client library and Redis time series support. The example simulates three power sensors, ingests readings into Redis, and serves a local browser dashboard that updates in real time."},{"id":"overview","title":"Overview","role":"overview","text":"Time series are a natural fit for telemetry, monitoring, and IoT-style workloads. In this example, Redis stores a stream of timestamped readings from three simulated power sensors.\n\nThe demo is designed to make a few ideas easy to see:\n\n* Raw readings arrive every 500ms\n* Each graph shows only the most recent 12 seconds\n* Older samples disappear because the time series retention is short\n* 3-second buckets summarize the same readings with minimum, maximum, and average values\n* The bucket summaries are drawn under the same moving time scale as the graph"},{"id":"how-it-works","title":"How it works","role":"content","text":"The example has three main parts:\n\n1. A `SensorSimulator` generates realistic-looking power readings with drift and occasional spikes\n2. A `RedisTimeSeriesStore` creates the time series keys and issues Redis TimeSeries queries\n3. A small local HTTP server built with Go's standard `net/http` package renders three stacked combined graph-and-bucket views and polls a JSON snapshot endpoint\n\nEach sensor is stored in its own time series with labels such as `sensor_type`, `sensor_id`, `zone`, and `unit`. The dashboard then uses [`TS.MADD`](https://redis.io/docs/latest/commands/ts.madd) to ingest new readings and [`TS.RANGE`](https://redis.io/docs/latest/commands/ts.range) to query both raw samples and aggregated bucket summaries. The aggregate queries use aligned buckets so the bucket boundaries stay stable as the visible window moves."},{"id":"the-go-files","title":"The Go files","role":"content","text":"The implementation is split across three files:\n\n* [`sensor_simulator.go`](https://github.com/redis/docs/blob/main/content/develop/use-cases/time-series-dashboard/go/sensor_simulator.go) - Sensor definitions and sample generation\n* [`timeseries_store.go`](https://github.com/redis/docs/blob/main/content/develop/use-cases/time-series-dashboard/go/timeseries_store.go) - Redis TimeSeries command helpers\n* [`demo_server.go`](https://github.com/redis/docs/blob/main/content/develop/use-cases/time-series-dashboard/go/demo_server.go) - Local HTTP server and inline dashboard UI\n\nThe Redis helper issues time series commands with `client.Do(...)`, which keeps the example small while still making the Redis command flow explicit."},{"id":"data-model","title":"Data model","role":"content","text":"Series keys use this pattern:\n\n[code example]\n\nFor example:\n\n[code example]\n\nEach time series has labels similar to:\n\n[code example]\n\nThe demo uses a 12-second retention period so the graphs visibly slide forward as old samples expire. The setup is also idempotent, so you can stop and restart the demo without first cleaning up the time series keys."},{"id":"redis-commands-used","title":"Redis commands used","role":"content","text":"The implementation uses these time series commands directly through `go-redis`:\n\n* [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create) - Create one time series per sensor with retention and labels\n* [`TS.MADD`](https://redis.io/docs/latest/commands/ts.madd) - Batch-ingest readings from all three sensors every 500ms\n* [`TS.GET`](https://redis.io/docs/latest/commands/ts.get) - Fetch the latest reading for a sensor\n* [`TS.RANGE`](https://redis.io/docs/latest/commands/ts.range) - Read raw recent samples and aggregated 3-second buckets\n* `ALIGN 0` with `TS.RANGE ... AGGREGATION` - Keep bucket boundaries stable as the visible window moves"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before running the demo, make sure that:\n\n* Redis is running and accessible. By default, the demo connects to `localhost:6379`.\n* Your Redis deployment includes time series support.\n* The `go-redis` package is available:\n\n[code example]"},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of three Go source files plus a `go.mod` module file. Download them from the [`go` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/time-series-dashboard/go) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"Build and run the demo:\n\n[code example]\n\nThe server accepts optional flags if your Redis instance is not on the default host and port:\n\n[code example]\n\nAfter starting the server, visit `http://localhost:8080`.\n\nThe dashboard polls a JSON snapshot endpoint several times per second to show:\n\n* Three stacked rolling graphs of raw sensor readings\n* A bucket summary strip aligned to the same time axis as each graph\n* Minimum, maximum, and average values for each visible bucket\n* A moving 12-second window where old samples disappear as retention expires them\n\nBecause the graph and the bucket summary share the same moving time scale, you can see how raw samples relate to their aggregate bucket without switching views or interpreting a separate table."},{"id":"what-to-look-for","title":"What to look for","role":"content","text":"As you watch the dashboard, pay attention to how the Redis query patterns map to the UI:\n\n* New points arrive every 500ms through `TS.MADD`\n* The graphs show raw values returned by `TS.RANGE`\n* The bucket summaries use `TS.RANGE ... ALIGN 0 AGGREGATION min|max|avg 3000`\n* The left edge of the graph keeps advancing because the time series retention is short\n* The bucket boundaries stay fixed even while the visible window moves\n* The dashboard remains safe to rerun because series creation is idempotent"},{"id":"why-this-shape-works-well","title":"Why this shape works well","role":"content","text":"This demo intentionally uses only three sensors and a very short time horizon. That keeps the visualization small enough to understand at a glance while still demonstrating:\n\n* Repeated high-frequency ingest\n* Querying recent raw history\n* Aggregating into fixed time buckets\n* Short retention and visible expiration\n\nFor a first time series example, this is often easier to understand than a larger dashboard with many metrics, filters, or tables."},{"id":"production-considerations","title":"Production considerations","role":"content","text":"This example intentionally keeps the server and UI small so the Redis behavior is easy to follow. In production, you would usually want to add:\n\n* Authentication and authorization\n* Separate static assets instead of inline HTML\n* Better error reporting and health checks\n* Deployment-specific retention, window sizes, and aggregation intervals\n* Stronger key namespacing if multiple applications share the same Redis deployment"},{"id":"learn-more","title":"Learn more","role":"related","text":"* [Go client guide](https://redis.io/docs/latest/develop/clients/go) - Install and use the Go client\n* [Time series overview](https://redis.io/docs/latest/develop/data-types/timeseries) - Time series concepts and commands\n* [TS.RANGE command](https://redis.io/docs/latest/commands/ts.range) - Query raw and aggregated ranges from a time series\n* [TS.MADD command](https://redis.io/docs/latest/commands/ts.madd) - Add multiple samples in one call\n* [TS.CREATE command](https://redis.io/docs/latest/commands/ts.create) - Create a time series with labels and retention"}],"examples":[{"id":"data-model-ex0","language":"text","code":"ts:sensor:power_consumption:{sensor_id}","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"ts:sensor:power_consumption:power-1\nts:sensor:power_consumption:power-2\nts:sensor:power_consumption:power-3","section_id":"data-model"},{"id":"data-model-ex2","language":"text","code":"site = demo\nsensor_type = power_consumption\nsensor_id = power-1\nzone = north\nunit = watts","section_id":"data-model"},{"id":"prerequisites-ex0","language":"bash","code":"go get github.com/redis/go-redis/v9","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir time-series-dashboard-demo && cd time-series-dashboard-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/time-series-dashboard/go\ncurl -O $BASE/sensor_simulator.go\ncurl -O $BASE/timeseries_store.go\ncurl -O $BASE/demo_server.go\ncurl -O $BASE/go.mod\ncurl -O $BASE/go.sum","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"go build -o dashboard ./...\n./dashboard","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"bash","code":"./dashboard -redis-host 127.0.0.1 -redis-port 6379 -port 8080","section_id":"start-the-demo-server"}]}
{"schema_version":2,"id":"develop/use-cases/time-series-dashboard","title":"Rolling sensor graph demo with Redis","url":"https://redis.io/docs/latest/develop/use-cases/time-series-dashboard/","summary":"Build a rolling sensor graph demo with Redis time series data","tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:55:04+01:00","children":[{"id":"develop/use-cases/time-series-dashboard/redis-py","summary":"Build a Redis-backed rolling sensor graph demo in Python with redis-py","title":"Rolling sensor graph demo with Redis and redis-py","url":"https://redis.io/docs/latest/develop/use-cases/time-series-dashboard/redis-py/"},{"id":"develop/use-cases/time-series-dashboard/nodejs","summary":"Build a Redis-backed rolling sensor graph demo in Node.js with node-redis","title":"Rolling sensor graph demo with Redis and Node.js","url":"https://redis.io/docs/latest/develop/use-cases/time-series-dashboard/nodejs/"},{"id":"develop/use-cases/time-series-dashboard/java-jedis","summary":"Build a Redis-backed rolling sensor graph demo in Java with Jedis","title":"Rolling sensor graph demo with Java and Jedis","url":"https://redis.io/docs/latest/develop/use-cases/time-series-dashboard/java-jedis/"},{"id":"develop/use-cases/time-series-dashboard/go","summary":"Build a Redis-backed rolling sensor graph demo in Go with go-redis","title":"Rolling sensor graph demo with Go","url":"https://redis.io/docs/latest/develop/use-cases/time-series-dashboard/go/"},{"id":"develop/use-cases/time-series-dashboard/java-lettuce","summary":"Build a Redis-backed rolling sensor graph demo in Java with Lettuce","title":"Rolling sensor graph demo with Java and Lettuce","url":"https://redis.io/docs/latest/develop/use-cases/time-series-dashboard/java-lettuce/"},{"id":"develop/use-cases/time-series-dashboard/dotnet","summary":"Build a Redis-backed rolling sensor graph demo in .NET with NRedisStack","title":"Rolling sensor graph demo with Redis and .NET","url":"https://redis.io/docs/latest/develop/use-cases/time-series-dashboard/dotnet/"},{"id":"develop/use-cases/time-series-dashboard/rust","summary":"Build a Redis-backed rolling sensor graph demo in Rust with redis-rs","title":"Rolling sensor graph demo with Rust","url":"https://redis.io/docs/latest/develop/use-cases/time-series-dashboard/rust/"},{"id":"develop/use-cases/time-series-dashboard/php","summary":"Build a Redis-backed rolling sensor graph demo in PHP with Predis","title":"Rolling sensor graph demo with Redis and PHP","url":"https://redis.io/docs/latest/develop/use-cases/time-series-dashboard/php/"},{"id":"develop/use-cases/time-series-dashboard/ruby","summary":"Build a Redis-backed rolling sensor graph demo in Ruby with redis-rb","title":"Rolling sensor graph demo with Redis and Ruby","url":"https://redis.io/docs/latest/develop/use-cases/time-series-dashboard/ruby/"}],"page_type":"content","content_hash":"8f25fa07b08af591a42b7c7fb272f66ad69c0abe06ff799b8d64edda261e4aec","sections":[{"id":"when-to-use-redis-time-series","title":"When to use Redis time series","role":"overview","text":"Use Redis time series when you need to ingest, query, and retain high-velocity time-ordered numeric data — sensor readings, application metrics, telemetry, or prices — with sub-millisecond latency and automatic downsampling."},{"id":"why-the-problem-is-hard","title":"Why the problem is hard","role":"content","text":"Time series workloads invert the typical read-heavy ratio: ingestion can reach hundreds of thousands of samples per second while reads are comparatively infrequent. Relational and document databases were not designed for this write volume and degrade under sustained load.\n\nWithout a purpose-built structure, teams approximate time series with sorted sets or custom Lua scripts, which works at small scale but lacks native aggregation, automatic downsampling, and per-sample retention — leading to fragile cleanup jobs and growing memory costs.\n\nDedicated time series databases like InfluxDB or TimescaleDB handle petabyte-scale cold archival and batch analytics, but add a separate system to provision, scale, and monitor when the requirement is real-time latency on an instance already in the stack. ([Redis Streams](https://redis.io/docs/latest/develop/data-types/streams) solve a different problem — ordered event logs with consumer-group delivery, not numeric aggregation over time windows.)"},{"id":"what-you-can-expect-from-a-redis-solution","title":"What you can expect from a Redis solution","role":"content","text":"You can:\n\n-   Ingest device telemetry or application metrics at sustained high volume without a separate ingestion pipeline.\n-   Run aggregated range queries over arbitrary time windows for dashboards and alerting with single-digit millisecond response times.\n-   Define multi-tier downsampling (raw → 1-minute averages → 1-hour averages) that runs automatically as data arrives.\n-   Enforce retention policies that keep memory bounded without manual cleanup jobs.\n-   Query across thousands of series by label for root-cause analysis and operational monitoring.\n-   Power real-time operational dashboards without a separate metrics backend."},{"id":"how-redis-supports-the-solution","title":"How Redis supports the solution","role":"content","text":"In practice, each metric is stored as a Redis [time series](https://redis.io/docs/latest/develop/data-types/timeseries) key with optional labels for the dimensions you want to query by (region, host, sensor type), and compaction rules to write downsampled summaries into companion keys as new samples arrive.\n\nRedis provides the following features that make it a good fit for time series workloads:\n\n-   The [time series](https://redis.io/docs/latest/develop/data-types/timeseries) data structure\n    (`TS.*` commands) is designed for sustained high-velocity ingestion with sub-millisecond\n    write latency.\n-   [`TS.RANGE`](https://redis.io/docs/latest/commands/ts.range) and\n    [`TS.MRANGE`](https://redis.io/docs/latest/commands/ts.mrange) return aggregated results\n    (`avg`, `sum`, `min`, `max`, `count`, `std.p`, `std.s`, `twa`, and others) over\n    arbitrary time windows in single-digit milliseconds, eliminating application-side\n    aggregation logic.\n-   [`TS.CREATERULE`](https://redis.io/docs/latest/commands/ts.createrule) defines source-to-destination\n    compaction with an aggregation function and bucket duration, so downsampling runs inside\n    Redis without an external pipeline.\n-   The `RETENTION` parameter on [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create) enforces\n    per-sample trimming relative to the newest sample, keeping memory bounded without\n    manual cleanup or key-level [`EXPIRE`](https://redis.io/docs/latest/commands/expire).\n-   Label-based secondary indexing enables cross-series queries\n    ([`TS.MRANGE`](https://redis.io/docs/latest/commands/ts.mrange) … `FILTER`) across thousands of keys\n    by any dimension — region, host, sensor type — in a single call."},{"id":"ecosystem","title":"Ecosystem","role":"content","text":"The following tools and libraries integrate with Redis time series:\n\n-   **Grafana**:\n    [Redis Data Source plugin](https://grafana.com/grafana/plugins/redis-datasource/)\n    for real-time time series dashboards\n-   **Prometheus**: RedisTimeSeries remote read/write adapter\n-   **Telegraf**: output plugin for streaming collected metrics into Redis\n-   **Client libraries**: `TS.*` commands are available through standard Redis client\n    libraries including [redis-py](https://redis.readthedocs.io/),\n    [Jedis](https://github.com/redis/jedis),\n    [ioredis](https://github.com/redis/ioredis), and\n    [go-redis](https://github.com/redis/go-redis)\n-   **Infrastructure**: [Redis Cloud](https://redis.io/docs/latest/operate/rc) for managed deployments\n    with built-in time series support"},{"id":"code-examples-to-build-your-own-redis-time-series-dashboard","title":"Code examples to build your own Redis time series dashboard","role":"example","text":"The following guides show how to build a rolling sensor graph demo backed by Redis time series.\nEach guide includes a runnable example with three simulated sensors, a rolling graph of raw\nreadings, bucketed min/max/average summaries on the same time axis, and a short retention\nwindow where old samples visibly expire:\n\n* [redis-py (Python)](https://redis.io/docs/latest/develop/use-cases/time-series-dashboard/redis-py)\n* [node-redis (Node.js)](https://redis.io/docs/latest/develop/use-cases/time-series-dashboard/nodejs)\n* [go-redis (Go)](https://redis.io/docs/latest/develop/use-cases/time-series-dashboard/go)\n* [Jedis (Java)](https://redis.io/docs/latest/develop/use-cases/time-series-dashboard/java-jedis)\n* [Lettuce (Java)](https://redis.io/docs/latest/develop/use-cases/time-series-dashboard/java-lettuce)\n* [NRedisStack (C#)](https://redis.io/docs/latest/develop/use-cases/time-series-dashboard/dotnet)\n* [Predis (PHP)](https://redis.io/docs/latest/develop/use-cases/time-series-dashboard/php)\n* [redis-rb (Ruby)](https://redis.io/docs/latest/develop/use-cases/time-series-dashboard/ruby)\n* [redis-rs (Rust)](https://redis.io/docs/latest/develop/use-cases/time-series-dashboard/rust)"}],"examples":[]}
{"schema_version":2,"id":"develop/use-cases/time-series-dashboard/java-jedis","title":"Rolling sensor graph demo with Java and Jedis","url":"https://redis.io/docs/latest/develop/use-cases/time-series-dashboard/java-jedis/","summary":"Build a Redis-backed rolling sensor graph demo in Java with Jedis","aliases":["/develop/use-cases/time-series-dashboard/java","/develop/use-cases/time-series-dashboard/jedis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:55:04+01:00","children":[],"page_type":"content","content_hash":"2c15262b2c6c8f0415703aedf1ac02478f13a815a790de81dd2daa9a63d38101","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a compact rolling sensor graph demo in Java with [`Jedis`](https://redis.io/docs/latest/develop/clients/jedis) and Redis time series support. The example simulates three power sensors, ingests readings into Redis, and serves a local browser dashboard that updates in real time."},{"id":"overview","title":"Overview","role":"overview","text":"Time series are a natural fit for telemetry, monitoring, and IoT-style workloads. In this example, Redis stores a stream of timestamped readings from three simulated power sensors.\n\nThe demo is designed to make a few ideas easy to see:\n\n* Raw readings arrive every 500ms\n* Each graph shows only the most recent 12 seconds\n* Older samples disappear because the time series retention is short\n* 3-second buckets summarize the same readings with minimum, maximum, and average values\n* The bucket summaries are drawn under the same moving time scale as the graph"},{"id":"how-it-works","title":"How it works","role":"content","text":"The example has three main parts:\n\n1. A `SensorSimulator` generates realistic-looking power readings with drift and occasional spikes\n2. A `RedisTimeSeriesStore` creates the time series keys and issues Redis TimeSeries queries\n3. A small local HTTP server built with Java's built-in `HttpServer` renders three stacked combined graph-and-bucket views and polls a JSON snapshot endpoint\n\nEach sensor is stored in its own time series with labels such as `sensor_type`, `sensor_id`, `zone`, and `unit`. The dashboard then uses [`TS.MADD`](https://redis.io/docs/latest/commands/ts.madd) to ingest new readings and [`TS.RANGE`](https://redis.io/docs/latest/commands/ts.range) to query both raw samples and aggregated bucket summaries. The aggregate queries use aligned buckets so the bucket boundaries stay stable as the visible window moves."},{"id":"the-java-classes","title":"The Java classes","role":"content","text":"The implementation is split across three small files:\n\n* [`SensorSimulator.java`](https://github.com/redis/docs/blob/main/content/develop/use-cases/time-series-dashboard/java-jedis/SensorSimulator.java) - Sensor definitions and sample generation\n* [`RedisTimeSeriesStore.java`](https://github.com/redis/docs/blob/main/content/develop/use-cases/time-series-dashboard/java-jedis/RedisTimeSeriesStore.java) - Redis TimeSeries command helpers\n* [`DemoServer.java`](https://github.com/redis/docs/blob/main/content/develop/use-cases/time-series-dashboard/java-jedis/DemoServer.java) - Local HTTP server and inline dashboard UI\n\nThe Redis helper uses Jedis' low-level command path with a small custom `ProtocolCommand` enum so the example can issue `TS.CREATE`, `TS.MADD`, `TS.GET`, and `TS.RANGE` directly."},{"id":"data-model","title":"Data model","role":"content","text":"Series keys use this pattern:\n\n[code example]\n\nFor example:\n\n[code example]\n\nEach time series has labels similar to:\n\n[code example]\n\nThe demo uses a 12-second retention period so the graphs visibly slide forward as old samples expire. The setup is also idempotent, so you can stop and restart the demo without first cleaning up the time series keys."},{"id":"redis-commands-used","title":"Redis commands used","role":"content","text":"The implementation uses these time series commands directly through Jedis:\n\n* [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create) - Create one time series per sensor with retention and labels\n* [`TS.MADD`](https://redis.io/docs/latest/commands/ts.madd) - Batch-ingest readings from all three sensors every 500ms\n* [`TS.GET`](https://redis.io/docs/latest/commands/ts.get) - Fetch the latest reading for a sensor\n* [`TS.RANGE`](https://redis.io/docs/latest/commands/ts.range) - Read raw recent samples and aggregated 3-second buckets\n* `ALIGN 0` with `TS.RANGE ... AGGREGATION` - Keep bucket boundaries stable as the visible window moves"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before running the demo, make sure that:\n\n* Redis is running and accessible. By default, the demo connects to `localhost:6379`.\n* Your Redis deployment includes time series support.\n* Jedis is available in your project.\n\nIf you use Maven:\n\n[code example]"},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of three Java files. Download them from the [`java-jedis` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/time-series-dashboard/java-jedis) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"Compile and run the demo:\n\n[code example]\n\nThe server accepts optional flags if your Redis instance is not on the default host and port:\n\n[code example]\n\nAfter starting the server, visit `http://localhost:8080`.\n\nThe dashboard polls a JSON snapshot endpoint several times per second to show:\n\n* Three stacked rolling graphs of raw sensor readings\n* A bucket summary strip aligned to the same time axis as each graph\n* Minimum, maximum, and average values for each visible bucket\n* A moving 12-second window where old samples disappear as retention expires them\n\nBecause the graph and the bucket summary share the same moving time scale, you can see how raw samples relate to their aggregate bucket without switching views or interpreting a separate table."},{"id":"what-to-look-for","title":"What to look for","role":"content","text":"As you watch the dashboard, pay attention to how the Redis query patterns map to the UI:\n\n* New points arrive every 500ms through `TS.MADD`\n* The graphs show raw values returned by `TS.RANGE`\n* The bucket summaries use `TS.RANGE ... ALIGN 0 AGGREGATION min|max|avg 3000`\n* The left edge of the graph keeps advancing because the time series retention is short\n* The bucket boundaries stay fixed even while the visible window moves\n* The dashboard remains safe to rerun because series creation is idempotent"},{"id":"why-this-shape-works-well","title":"Why this shape works well","role":"content","text":"This demo intentionally uses only three sensors and a very short time horizon. That keeps the visualization small enough to understand at a glance while still demonstrating:\n\n* Repeated high-frequency ingest\n* Querying recent raw history\n* Aggregating into fixed time buckets\n* Short retention and visible expiration\n\nFor a first time series example, this is often easier to understand than a larger dashboard with many metrics, filters, or tables."},{"id":"production-considerations","title":"Production considerations","role":"content","text":"This example intentionally keeps the server and UI small so the Redis behavior is easy to follow. In production, you would usually want to add:\n\n* Authentication and authorization\n* Separate static assets instead of inline HTML\n* Better error reporting and health checks\n* Deployment-specific retention, window sizes, and aggregation intervals\n* Stronger key namespacing if multiple applications share the same Redis deployment"},{"id":"learn-more","title":"Learn more","role":"related","text":"* [Jedis guide](https://redis.io/docs/latest/develop/clients/jedis) - Install and use the Java client\n* [Time series overview](https://redis.io/docs/latest/develop/data-types/timeseries) - Time series concepts and commands\n* [TS.RANGE command](https://redis.io/docs/latest/commands/ts.range) - Query raw and aggregated ranges from a time series\n* [TS.MADD command](https://redis.io/docs/latest/commands/ts.madd) - Add multiple samples in one call\n* [TS.CREATE command](https://redis.io/docs/latest/commands/ts.create) - Create a time series with labels and retention"}],"examples":[{"id":"data-model-ex0","language":"text","code":"ts:sensor:power_consumption:{sensor_id}","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"ts:sensor:power_consumption:power-1\nts:sensor:power_consumption:power-2\nts:sensor:power_consumption:power-3","section_id":"data-model"},{"id":"data-model-ex2","language":"text","code":"site = demo\nsensor_type = power_consumption\nsensor_id = power-1\nzone = north\nunit = watts","section_id":"data-model"},{"id":"prerequisites-ex0","language":"xml","code":"<dependency>\n    <groupId>redis.clients</groupId>\n    <artifactId>jedis</artifactId>\n    <version>5.0.1</version>\n</dependency>","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir time-series-dashboard-demo && cd time-series-dashboard-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/time-series-dashboard/java-jedis\ncurl -O $BASE/SensorSimulator.java\ncurl -O $BASE/RedisTimeSeriesStore.java\ncurl -O $BASE/DemoServer.java","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"javac -cp jedis-5.0.1.jar:commons-pool2-2.12.1.jar SensorSimulator.java RedisTimeSeriesStore.java DemoServer.java\njava -cp .:jedis-5.0.1.jar:commons-pool2-2.12.1.jar DemoServer","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"bash","code":"java -cp .:jedis-5.0.1.jar:commons-pool2-2.12.1.jar DemoServer --redis-host 127.0.0.1 --redis-port 6379 --port 8080","section_id":"start-the-demo-server"}]}
{"schema_version":2,"id":"develop/use-cases/time-series-dashboard/java-lettuce","title":"Rolling sensor graph demo with Java and Lettuce","url":"https://redis.io/docs/latest/develop/use-cases/time-series-dashboard/java-lettuce/","summary":"Build a Redis-backed rolling sensor graph demo in Java with Lettuce","aliases":["/develop/use-cases/time-series-dashboard/lettuce"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:55:04+01:00","children":[],"page_type":"content","content_hash":"5e62c0e92b14d9efe372a84a1be1668a8a26cd2ee03f276ee58264e52d9ab4b7","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a compact rolling sensor graph demo in Java with [`Lettuce`](https://redis.io/docs/latest/develop/clients/lettuce) and Redis time series support. The example simulates three power sensors, ingests readings into Redis, and serves a local browser dashboard that updates in real time."},{"id":"overview","title":"Overview","role":"overview","text":"Time series are a natural fit for telemetry, monitoring, and IoT-style workloads. In this example, Redis stores a stream of timestamped readings from three simulated power sensors.\n\nThe demo is designed to make a few ideas easy to see:\n\n* Raw readings arrive every 500ms\n* Each graph shows only the most recent 12 seconds\n* Older samples disappear because the time series retention is short\n* 3-second buckets summarize the same readings with minimum, maximum, and average values\n* The bucket summaries are drawn under the same moving time scale as the graph"},{"id":"why-async-and-reactive","title":"Why async and reactive","role":"content","text":"For Lettuce, we show asynchronous and reactive APIs rather than a synchronous API:\n\n* Async with `RedisAsyncCommands` works well for a small local demo server using `CompletableFuture`\n* Reactive with `RedisReactiveCommands` is a good fit when you are already using Reactor\n* For a synchronous Java version of this dashboard, see [Jedis](https://redis.io/docs/latest/develop/use-cases/time-series-dashboard/java-jedis)"},{"id":"how-it-works","title":"How it works","role":"content","text":"The example has four main parts:\n\n1. A `SensorSimulator` generates realistic-looking power readings with drift and occasional spikes\n2. An `AsyncRedisTimeSeriesStore` wraps the Redis TimeSeries commands with `CompletableFuture`\n3. A `ReactiveRedisTimeSeriesStore` exposes the same Redis TimeSeries operations as Reactor `Mono` pipelines\n4. A small local HTTP server built with Java's built-in `HttpServer` renders three stacked combined graph-and-bucket views and polls a JSON snapshot endpoint\n\nEach sensor is stored in its own time series with labels such as `sensor_type`, `sensor_id`, `zone`, and `unit`. The dashboard uses [`TS.MADD`](https://redis.io/docs/latest/commands/ts.madd) to ingest new readings and [`TS.RANGE`](https://redis.io/docs/latest/commands/ts.range) to query both raw samples and aggregated bucket summaries. The aggregate queries use aligned buckets so the bucket boundaries stay stable as the visible window moves."},{"id":"the-java-classes","title":"The Java classes","role":"content","text":"The implementation is split across four small files:\n\n* [`SensorSimulator.java`](https://github.com/redis/docs/blob/main/content/develop/use-cases/time-series-dashboard/java-lettuce/SensorSimulator.java) - Sensor definitions and sample generation\n* [`AsyncRedisTimeSeriesStore.java`](https://github.com/redis/docs/blob/main/content/develop/use-cases/time-series-dashboard/java-lettuce/AsyncRedisTimeSeriesStore.java) - Async Lettuce Redis TimeSeries helpers\n* [`ReactiveRedisTimeSeriesStore.java`](https://github.com/redis/docs/blob/main/content/develop/use-cases/time-series-dashboard/java-lettuce/ReactiveRedisTimeSeriesStore.java) - Reactive Lettuce Redis TimeSeries helpers\n* [`DemoServer.java`](https://github.com/redis/docs/blob/main/content/develop/use-cases/time-series-dashboard/java-lettuce/DemoServer.java) - Local HTTP server and inline dashboard UI\n\nLettuce does not provide dedicated TimeSeries helpers in the core command interfaces, so both store classes use Lettuce's low-level `dispatch()` API with `TS.CREATE`, `TS.MADD`, `TS.GET`, and `TS.RANGE`."},{"id":"data-model","title":"Data model","role":"content","text":"Series keys use this pattern:\n\n[code example]\n\nFor example:\n\n[code example]\n\nEach time series has labels similar to:\n\n[code example]\n\nThe demo uses a 12-second retention period so the graphs visibly slide forward as old samples expire. The setup is also idempotent, so you can stop and restart the demo without first cleaning up the time series keys."},{"id":"redis-commands-used","title":"Redis commands used","role":"content","text":"The implementation uses these time series commands directly through Lettuce:\n\n* [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create) - Create one time series per sensor with retention and labels\n* [`TS.MADD`](https://redis.io/docs/latest/commands/ts.madd) - Batch-ingest readings from all three sensors every 500ms\n* [`TS.GET`](https://redis.io/docs/latest/commands/ts.get) - Fetch the latest reading for a sensor\n* [`TS.RANGE`](https://redis.io/docs/latest/commands/ts.range) - Read raw recent samples and aggregated 3-second buckets\n* `ALIGN 0` with `TS.RANGE ... AGGREGATION` - Keep bucket boundaries stable as the visible window moves"},{"id":"async-usage","title":"Async usage","role":"content","text":"[code example]"},{"id":"reactive-usage","title":"Reactive usage","role":"content","text":"[code example]"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before running the demo, make sure that:\n\n* Redis is running and accessible. By default, the demo connects to `localhost:6379`.\n* Your Redis deployment includes time series support.\n* Lettuce is available in your project.\n\nIf you use Maven:\n\n[code example]"},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of four Java files. Download them from the [`java-lettuce` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/time-series-dashboard/java-lettuce) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"Compile and run the demo:\n\n[code example]\n\nThe server accepts optional flags if your Redis instance is not on the default host and port:\n\n[code example]\n\nAfter starting the server, visit `http://localhost:8080`.\n\nThe demo server itself uses the async Lettuce store so it can keep the implementation compact, but the reactive store exposes the same operations for Reactor-based applications.\n\nThe dashboard polls a JSON snapshot endpoint several times per second to show:\n\n* Three stacked rolling graphs of raw sensor readings\n* A bucket summary strip aligned to the same time axis as each graph\n* Minimum, maximum, and average values for each visible bucket\n* A moving 12-second window where old samples disappear as retention expires them\n\nBecause the graph and the bucket summary share the same moving time scale, you can see how raw samples relate to their aggregate bucket without switching views or interpreting a separate table."},{"id":"what-to-look-for","title":"What to look for","role":"content","text":"As you watch the dashboard, pay attention to how the Redis query patterns map to the UI:\n\n* New points arrive every 500ms through `TS.MADD`\n* The graphs show raw values returned by `TS.RANGE`\n* The bucket summaries use `TS.RANGE ... ALIGN 0 AGGREGATION min|max|avg 3000`\n* The left edge of the graph keeps advancing because the time series retention is short\n* The bucket boundaries stay fixed even while the visible window moves\n* The dashboard remains safe to rerun because series creation is idempotent"},{"id":"why-this-shape-works-well","title":"Why this shape works well","role":"content","text":"This demo intentionally uses only three sensors and a very short time horizon. That keeps the visualization small enough to understand at a glance while still demonstrating:\n\n* Repeated high-frequency ingest\n* Querying recent raw history\n* Aggregating into fixed time buckets\n* Short retention and visible expiration\n\nFor a first time series example, this is often easier to understand than a larger dashboard with many metrics, filters, or tables."},{"id":"production-considerations","title":"Production considerations","role":"content","text":"This example intentionally keeps the server and UI small so the Redis behavior is easy to follow. In production, you would usually want to add:\n\n* Authentication and authorization\n* Separate static assets instead of inline HTML\n* Better error reporting and health checks\n* Deployment-specific retention, window sizes, and aggregation intervals\n* Stronger key namespacing if multiple applications share the same Redis deployment"},{"id":"learn-more","title":"Learn more","role":"related","text":"* [Lettuce guide](https://redis.io/docs/latest/develop/clients/lettuce) - Install and use the Java client\n* [Time series overview](https://redis.io/docs/latest/develop/data-types/timeseries) - Time series concepts and commands\n* [TS.RANGE command](https://redis.io/docs/latest/commands/ts.range) - Query raw and aggregated ranges from a time series\n* [TS.MADD command](https://redis.io/docs/latest/commands/ts.madd) - Add multiple samples in one call\n* [TS.CREATE command](https://redis.io/docs/latest/commands/ts.create) - Create a time series with labels and retention"}],"examples":[{"id":"data-model-ex0","language":"text","code":"ts:sensor:power_consumption:{sensor_id}","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"ts:sensor:power_consumption:power-1\nts:sensor:power_consumption:power-2\nts:sensor:power_consumption:power-3","section_id":"data-model"},{"id":"data-model-ex2","language":"text","code":"site = demo\nsensor_type = power_consumption\nsensor_id = power-1\nzone = north\nunit = watts","section_id":"data-model"},{"id":"async-usage-ex0","language":"java","code":"import io.lettuce.core.RedisClient;\nimport io.lettuce.core.RedisURI;\nimport io.lettuce.core.api.StatefulRedisConnection;\n\nRedisClient redisClient = RedisClient.create(RedisURI.Builder.redis(\"localhost\", 6379).build());\nStatefulRedisConnection<String, String> connection = redisClient.connect();\n\nAsyncRedisTimeSeriesStore store = new AsyncRedisTimeSeriesStore(connection.async(), SensorSimulator.SENSORS);\n\nstore.ensureSchema()\n    .thenCompose(ignore -> store.addSamples(new SensorSimulator(SensorSimulator.SENSORS).nextSamples()))\n    .thenCompose(ignore -> store.dashboardSnapshot())\n    .thenAccept(snapshot -> System.out.println(snapshot.get(\"sensors\")))\n    .join();\n\nconnection.close();\nredisClient.shutdown();","section_id":"async-usage"},{"id":"reactive-usage-ex0","language":"java","code":"import io.lettuce.core.RedisClient;\nimport io.lettuce.core.RedisURI;\nimport io.lettuce.core.api.StatefulRedisConnection;\n\nRedisClient redisClient = RedisClient.create(RedisURI.Builder.redis(\"localhost\", 6379).build());\nStatefulRedisConnection<String, String> connection = redisClient.connect();\n\nReactiveRedisTimeSeriesStore store = new ReactiveRedisTimeSeriesStore(connection.reactive(), SensorSimulator.SENSORS);\n\nstore.ensureSchema()\n    .then(store.addSamples(new SensorSimulator(SensorSimulator.SENSORS).nextSamples()))\n    .then(store.dashboardSnapshot())\n    .doOnNext(snapshot -> System.out.println(snapshot.get(\"sensors\")))\n    .block();\n\nconnection.close();\nredisClient.shutdown();","section_id":"reactive-usage"},{"id":"prerequisites-ex0","language":"xml","code":"<dependency>\n    <groupId>io.lettuce</groupId>\n    <artifactId>lettuce-core</artifactId>\n    <version>6.7.1.RELEASE</version>\n</dependency>","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir time-series-dashboard-demo && cd time-series-dashboard-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/time-series-dashboard/java-lettuce\ncurl -O $BASE/SensorSimulator.java\ncurl -O $BASE/AsyncRedisTimeSeriesStore.java\ncurl -O $BASE/ReactiveRedisTimeSeriesStore.java\ncurl -O $BASE/DemoServer.java","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"javac -cp lettuce-core-6.7.1.RELEASE.jar:reactor-core-3.6.6.jar:reactive-streams-1.0.4.jar SensorSimulator.java AsyncRedisTimeSeriesStore.java ReactiveRedisTimeSeriesStore.java DemoServer.java\njava -cp .:lettuce-core-6.7.1.RELEASE.jar:reactor-core-3.6.6.jar:reactive-streams-1.0.4.jar DemoServer","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"bash","code":"java -cp .:lettuce-core-6.7.1.RELEASE.jar:reactor-core-3.6.6.jar:reactive-streams-1.0.4.jar DemoServer --redis-host 127.0.0.1 --redis-port 6379 --port 8080","section_id":"start-the-demo-server"}]}
{"schema_version":2,"id":"develop/use-cases/time-series-dashboard/nodejs","title":"Rolling sensor graph demo with Redis and Node.js","url":"https://redis.io/docs/latest/develop/use-cases/time-series-dashboard/nodejs/","summary":"Build a Redis-backed rolling sensor graph demo in Node.js with node-redis","aliases":["/develop/use-cases/time-series-dashboard/node-redis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:55:04+01:00","children":[],"page_type":"content","content_hash":"be377af9bc7e98cc1813f6000ad8b7732258fd7f3abddbfd2cb84360575ed474","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a compact rolling sensor graph demo in Node.js with [`node-redis`](https://redis.io/docs/latest/develop/clients/nodejs) and Redis time series support. The example simulates three power sensors, ingests readings into Redis, and serves a local browser dashboard that updates in real time."},{"id":"overview","title":"Overview","role":"overview","text":"Time series are a natural fit for telemetry, monitoring, and IoT-style workloads. In this example, Redis stores a stream of timestamped readings from three simulated power sensors.\n\nThe demo is designed to make a few ideas easy to see:\n\n* Raw readings arrive every 500ms\n* Each graph shows only the most recent 12 seconds\n* Older samples disappear because the time series retention is short\n* 3-second buckets summarize the same readings with minimum, maximum, and average values\n* The bucket summaries are drawn under the same moving time scale as the graph"},{"id":"how-it-works","title":"How it works","role":"content","text":"The example has three main parts:\n\n1. A `SensorSimulator` generates realistic-looking power readings with drift and occasional spikes\n2. A `RedisTimeSeriesStore` creates the time series keys and issues Redis TimeSeries queries\n3. A small local HTTP server built with Node's `http` module renders three stacked combined graph-and-bucket views and polls a JSON snapshot endpoint\n\nEach sensor is stored in its own time series with labels such as `sensor_type`, `sensor_id`, `zone`, and `unit`. The dashboard then uses [`TS.MADD`](https://redis.io/docs/latest/commands/ts.madd) to ingest new readings and [`TS.RANGE`](https://redis.io/docs/latest/commands/ts.range) to query both raw samples and aggregated bucket summaries. The aggregate queries use aligned buckets so the bucket boundaries stay stable as the visible window moves."},{"id":"the-nodejs-modules","title":"The Node.js modules","role":"content","text":"The implementation is split across three small files:\n\n* [`sensorSimulator.js`](https://github.com/redis/docs/blob/main/content/develop/use-cases/time-series-dashboard/nodejs/sensorSimulator.js) - Sensor definitions and sample generation\n* [`timeseriesStore.js`](https://github.com/redis/docs/blob/main/content/develop/use-cases/time-series-dashboard/nodejs/timeseriesStore.js) - Redis TimeSeries command helpers\n* [`dashboard.js`](https://github.com/redis/docs/blob/main/content/develop/use-cases/time-series-dashboard/nodejs/dashboard.js) - Local HTTP server and inline dashboard UI\n\nThe `RedisTimeSeriesStore` class wraps the Redis operations directly through `node-redis`'s generic `sendCommand()` interface:\n\n[code example]"},{"id":"data-model","title":"Data model","role":"content","text":"Series keys use this pattern:\n\n[code example]\n\nFor example:\n\n[code example]\n\nEach time series has labels similar to:\n\n[code example]\n\nThe demo uses a 12-second retention period so the graphs visibly slide forward as old samples expire. The setup is also idempotent, so you can stop and restart the demo without first cleaning up the time series keys."},{"id":"redis-commands-used","title":"Redis commands used","role":"content","text":"The implementation uses these time series commands directly through `node-redis`:\n\n* [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create) - Create one time series per sensor with retention and labels\n* [`TS.MADD`](https://redis.io/docs/latest/commands/ts.madd) - Batch-ingest readings from all three sensors every 500ms\n* [`TS.GET`](https://redis.io/docs/latest/commands/ts.get) - Fetch the latest reading for a sensor\n* [`TS.RANGE`](https://redis.io/docs/latest/commands/ts.range) - Read raw recent samples and aggregated 3-second buckets\n* `ALIGN 0` with `TS.RANGE ... AGGREGATION` - Keep bucket boundaries stable as the visible window moves"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before running the demo, make sure that:\n\n* Redis is running and accessible. By default, the demo connects to `localhost:6379`.\n* Your Redis deployment includes time series support.\n* The `redis` package is installed:\n\n[code example]"},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of three Node.js files. Download them from the [`nodejs` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/time-series-dashboard/nodejs) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"Start the dashboard server:\n\n[code example]\n\nThe server accepts optional flags if your Redis instance is not on the default host and port:\n\n[code example]\n\nAfter starting the server, visit `http://localhost:8080`.\n\nThe dashboard polls a JSON snapshot endpoint several times per second to show:\n\n* Three stacked rolling graphs of raw sensor readings\n* A bucket summary strip aligned to the same time axis as each graph\n* Minimum, maximum, and average values for each visible bucket\n* A moving 12-second window where old samples disappear as retention expires them\n\nBecause the graph and the bucket summary share the same moving time scale, you can see how raw samples relate to their aggregate bucket without switching views or interpreting a separate table."},{"id":"what-to-look-for","title":"What to look for","role":"content","text":"As you watch the dashboard, pay attention to how the Redis query patterns map to the UI:\n\n* New points arrive every 500ms through `TS.MADD`\n* The graphs show raw values returned by `TS.RANGE`\n* The bucket summaries use `TS.RANGE ... ALIGN 0 AGGREGATION min|max|avg 3000`\n* The left edge of the graph keeps advancing because the time series retention is short\n* The bucket boundaries stay fixed even while the visible window moves\n* The dashboard remains safe to rerun because series creation is idempotent"},{"id":"why-this-shape-works-well","title":"Why this shape works well","role":"content","text":"This demo intentionally uses only three sensors and a very short time horizon. That keeps the visualization small enough to understand at a glance while still demonstrating:\n\n* Repeated high-frequency ingest\n* Querying recent raw history\n* Aggregating into fixed time buckets\n* Short retention and visible expiration\n\nFor a first time series example, this is often easier to understand than a larger dashboard with many metrics, filters, or tables."},{"id":"production-considerations","title":"Production considerations","role":"content","text":"This example intentionally keeps the server and UI small so the Redis behavior is easy to follow. In production, you would usually want to add:\n\n* Authentication and authorization\n* Separate static assets instead of inline HTML\n* Better error reporting and health checks\n* Deployment-specific retention, window sizes, and aggregation intervals\n* Stronger key namespacing if multiple applications share the same Redis deployment"},{"id":"learn-more","title":"Learn more","role":"related","text":"* [node-redis guide](https://redis.io/docs/latest/develop/clients/nodejs) - Install and use the Node.js client\n* [Time series overview](https://redis.io/docs/latest/develop/data-types/timeseries) - Time series concepts and commands\n* [TS.RANGE command](https://redis.io/docs/latest/commands/ts.range) - Query raw and aggregated ranges from a time series\n* [TS.MADD command](https://redis.io/docs/latest/commands/ts.madd) - Add multiple samples in one call\n* [TS.CREATE command](https://redis.io/docs/latest/commands/ts.create) - Create a time series with labels and retention"}],"examples":[{"id":"the-nodejs-modules-ex0","language":"javascript","code":"const { createClient } = require(\"redis\");\nconst { SENSORS } = require(\"./sensorSimulator\");\nconst { RedisTimeSeriesStore } = require(\"./timeseriesStore\");\n\nasync function main() {\n  const client = createClient({ url: \"redis://localhost:6379\" });\n  await client.connect();\n\n  const store = new RedisTimeSeriesStore({\n    redisClient: client,\n    sensors: SENSORS,\n  });\n\n  await store.ensureSchema();\n  const snapshot = await store.dashboardSnapshot();\n  console.log(snapshot.sensors[0].raw_points.length);\n\n  await client.disconnect();\n}\n\nmain().catch(console.error);","section_id":"the-nodejs-modules"},{"id":"data-model-ex0","language":"text","code":"ts:sensor:power_consumption:{sensor_id}","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"ts:sensor:power_consumption:power-1\nts:sensor:power_consumption:power-2\nts:sensor:power_consumption:power-3","section_id":"data-model"},{"id":"data-model-ex2","language":"text","code":"site = demo\nsensor_type = power_consumption\nsensor_id = power-1\nzone = north\nunit = watts","section_id":"data-model"},{"id":"prerequisites-ex0","language":"bash","code":"npm install redis","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir time-series-dashboard-demo && cd time-series-dashboard-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/time-series-dashboard/nodejs\ncurl -O $BASE/sensorSimulator.js\ncurl -O $BASE/timeseriesStore.js\ncurl -O $BASE/dashboard.js","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"node dashboard.js","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"bash","code":"node dashboard.js --redis-host 127.0.0.1 --redis-port 6379 --port 8080","section_id":"start-the-demo-server"}]}
{"schema_version":2,"id":"develop/use-cases/time-series-dashboard/php","title":"Rolling sensor graph demo with Redis and PHP","url":"https://redis.io/docs/latest/develop/use-cases/time-series-dashboard/php/","summary":"Build a Redis-backed rolling sensor graph demo in PHP with Predis","aliases":["/develop/use-cases/time-series-dashboard/predis"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:55:04+01:00","children":[],"page_type":"content","content_hash":"429bcb1309e9897b3a6630dda71bf1c109d3a3a32b0ad4407f6a9ce2f7b9550d","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a compact rolling sensor graph demo in PHP with [Predis](https://redis.io/docs/latest/develop/clients/php) and Redis time series support. The example simulates three power sensors, ingests readings into Redis, and serves a local browser dashboard that updates in real time."},{"id":"overview","title":"Overview","role":"overview","text":"Time series are a natural fit for telemetry, monitoring, and IoT-style workloads. In this example, Redis stores a stream of timestamped readings from three simulated power sensors.\n\nThe demo is designed to make a few ideas easy to see:\n\n* Raw readings arrive every 500ms\n* Each graph shows only the most recent 12 seconds\n* Older samples disappear because the time series retention is short\n* 3-second buckets summarize the same readings with minimum, maximum, and average values\n* The bucket summaries are drawn under the same moving time scale as the graph"},{"id":"how-it-works","title":"How it works","role":"content","text":"The example has three main parts:\n\n1. A `SensorSimulator` generates realistic-looking power readings with drift and occasional spikes\n2. A `RedisTimeSeriesStore` creates the time series keys and issues Redis TimeSeries queries\n3. A small local router script for PHP's built-in server renders three stacked combined graph-and-bucket views and serves a JSON snapshot endpoint\n\nEach sensor is stored in its own time series with labels such as `sensor_type`, `sensor_id`, `zone`, and `unit`. The dashboard then uses [`TS.MADD`](https://redis.io/docs/latest/commands/ts.madd) to ingest new readings and [`TS.RANGE`](https://redis.io/docs/latest/commands/ts.range) to query both raw samples and aggregated bucket summaries. The aggregate queries use aligned buckets so the bucket boundaries stay stable as the visible window moves.\n\nBecause PHP's built-in development server is request-driven, this version advances the simulator on each `/api/snapshot` request and persists the simulator state in Redis. That keeps the demo simple to run while still producing the same rolling dashboard behavior as the long-lived server examples."},{"id":"the-php-files","title":"The PHP files","role":"content","text":"The implementation is split across three small files:\n\n* [`SensorSimulator.php`](https://github.com/redis/docs/blob/main/content/develop/use-cases/time-series-dashboard/php/SensorSimulator.php) - Sensor definitions and sample generation\n* [`RedisTimeSeriesStore.php`](https://github.com/redis/docs/blob/main/content/develop/use-cases/time-series-dashboard/php/RedisTimeSeriesStore.php) - Redis TimeSeries command helpers\n* [`demo_server.php`](https://github.com/redis/docs/blob/main/content/develop/use-cases/time-series-dashboard/php/demo_server.php) - PHP router, simulator advancement, and inline dashboard UI\n\nThe Redis helper issues time series commands through Predis' `executeRaw()` method, which keeps the example small while still making the Redis command flow explicit."},{"id":"data-model","title":"Data model","role":"content","text":"Series keys use this pattern:\n\n[code example]\n\nFor example:\n\n[code example]\n\nEach time series has labels similar to:\n\n[code example]\n\nThe demo uses a 12-second retention period so the graphs visibly slide forward as old samples expire. The setup is also idempotent, so you can stop and restart the demo without first cleaning up the time series keys."},{"id":"redis-commands-used","title":"Redis commands used","role":"content","text":"The implementation uses these time series commands directly through Predis:\n\n* [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create) - Create one time series per sensor with retention and labels\n* [`TS.MADD`](https://redis.io/docs/latest/commands/ts.madd) - Batch-ingest readings from all three sensors every 500ms\n* [`TS.GET`](https://redis.io/docs/latest/commands/ts.get) - Fetch the latest reading for a sensor\n* [`TS.RANGE`](https://redis.io/docs/latest/commands/ts.range) - Read raw recent samples and aggregated 3-second buckets\n* `ALIGN 0` with `TS.RANGE ... AGGREGATION` - Keep bucket boundaries stable as the visible window moves"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before running the demo, make sure that:\n\n* Redis is running and accessible. By default, the demo connects to `localhost:6379`.\n* Your Redis deployment includes time series support.\n* Predis is installed:\n\n[code example]"},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of three PHP files. Download them from the [`php` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/time-series-dashboard/php) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"Start the dashboard server:\n\n[code example]\n\nIf your Redis instance is not on the default host and port, pass them as environment variables:\n\n[code example]\n\nAfter starting the server, visit `http://localhost:8080`.\n\nThe dashboard polls a JSON snapshot endpoint several times per second to show:\n\n* Three stacked rolling graphs of raw sensor readings\n* A bucket summary strip aligned to the same time axis as each graph\n* Minimum, maximum, and average values for each visible bucket\n* A moving 12-second window where old samples disappear as retention expires them\n\nBecause the graph and the bucket summary share the same moving time scale, you can see how raw samples relate to their aggregate bucket without switching views or interpreting a separate table."},{"id":"what-to-look-for","title":"What to look for","role":"content","text":"As you watch the dashboard, pay attention to how the Redis query patterns map to the UI:\n\n* New points arrive every 500ms through `TS.MADD`\n* The graphs show raw values returned by `TS.RANGE`\n* The bucket summaries use `TS.RANGE ... ALIGN 0 AGGREGATION min|max|avg 3000`\n* The left edge of the graph keeps advancing because the time series retention is short\n* The bucket boundaries stay fixed even while the visible window moves\n* The dashboard remains safe to rerun because series creation is idempotent"},{"id":"why-this-shape-works-well","title":"Why this shape works well","role":"content","text":"This demo intentionally uses only three sensors and a very short time horizon. That keeps the visualization small enough to understand at a glance while still demonstrating:\n\n* Repeated high-frequency ingest\n* Querying recent raw history\n* Aggregating into fixed time buckets\n* Short retention and visible expiration\n\nFor a first time series example, this is often easier to understand than a larger dashboard with many metrics, filters, or tables."},{"id":"production-considerations","title":"Production considerations","role":"content","text":"This example intentionally keeps the server and UI small so the Redis behavior is easy to follow. In production, you would usually want to add:\n\n* Authentication and authorization\n* Separate static assets instead of inline HTML\n* Better error reporting and health checks\n* A long-lived worker if you want ingest to continue independently of dashboard requests\n* Deployment-specific retention, window sizes, and aggregation intervals"},{"id":"learn-more","title":"Learn more","role":"related","text":"* [PHP client guide](https://redis.io/docs/latest/develop/clients/php) - Install and use the PHP client\n* [Time series overview](https://redis.io/docs/latest/develop/data-types/timeseries) - Time series concepts and commands\n* [TS.RANGE command](https://redis.io/docs/latest/commands/ts.range) - Query raw and aggregated ranges from a time series\n* [TS.MADD command](https://redis.io/docs/latest/commands/ts.madd) - Add multiple samples in one call\n* [TS.CREATE command](https://redis.io/docs/latest/commands/ts.create) - Create a time series with labels and retention"}],"examples":[{"id":"data-model-ex0","language":"text","code":"ts:sensor:power_consumption:{sensor_id}","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"ts:sensor:power_consumption:power-1\nts:sensor:power_consumption:power-2\nts:sensor:power_consumption:power-3","section_id":"data-model"},{"id":"data-model-ex2","language":"text","code":"site = demo\nsensor_type = power_consumption\nsensor_id = power-1\nzone = north\nunit = watts","section_id":"data-model"},{"id":"prerequisites-ex0","language":"bash","code":"composer require predis/predis","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir time-series-dashboard-demo && cd time-series-dashboard-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/time-series-dashboard/php\ncurl -O $BASE/SensorSimulator.php\ncurl -O $BASE/RedisTimeSeriesStore.php\ncurl -O $BASE/demo_server.php","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"composer require predis/predis\nphp -S localhost:8080 demo_server.php","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"bash","code":"REDIS_HOST=127.0.0.1 REDIS_PORT=6379 php -S localhost:8080 demo_server.php","section_id":"start-the-demo-server"}]}
{"schema_version":2,"id":"develop/use-cases/time-series-dashboard/redis-py","title":"Rolling sensor graph demo with Redis and redis-py","url":"https://redis.io/docs/latest/develop/use-cases/time-series-dashboard/redis-py/","summary":"Build a Redis-backed rolling sensor graph demo in Python with redis-py","aliases":["/develop/use-cases/time-series-dashboard/python"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:55:04+01:00","children":[],"page_type":"content","content_hash":"df47987ba228982ad63975267ec229742b96760aaf2eec139087a7dd66a6a5df","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a compact rolling sensor graph demo in Python with [`redis-py`](https://redis.io/docs/latest/develop/clients/redis-py) and Redis time series support. The example simulates three power sensors, ingests readings into Redis, and serves a local browser dashboard that updates in real time."},{"id":"overview","title":"Overview","role":"overview","text":"Time series are a natural fit for telemetry, monitoring, and IoT-style workloads. In this example, Redis stores a stream of timestamped readings from three simulated power sensors.\n\nThe demo is designed to make a few ideas easy to see:\n\n* Raw readings arrive every 500ms\n* Each graph shows only the most recent 12 seconds\n* Older samples disappear because the time series retention is short\n* 3-second buckets summarize the same readings with minimum, maximum, and average values\n* The bucket summaries are drawn under the same moving time scale as the graph"},{"id":"how-it-works","title":"How it works","role":"content","text":"The example has three main parts:\n\n1. A `SensorSimulator` generates realistic-looking power readings with drift and occasional spikes\n2. A `RedisTimeSeriesStore` creates the time series keys and issues Redis time series queries\n3. A small local HTTP server renders three stacked graphs and polls a JSON snapshot endpoint\n\nEach sensor is stored in its own time series with labels such as `sensor_type`, `sensor_id`, `zone`, and `unit`. The dashboard then uses [`TS.MADD`](https://redis.io/docs/latest/commands/ts.madd) to ingest new readings and [`TS.RANGE`](https://redis.io/docs/latest/commands/ts.range) to query both raw samples and aggregated bucket summaries. The aggregate queries use aligned buckets so the bucket boundaries stay stable as the visible window moves."},{"id":"data-model","title":"Data model","role":"content","text":"Time series keys use this pattern:\n\n[code example]\n\nFor example:\n\n[code example]\n\nEach time series has labels similar to:\n\n[code example]\n\nThe demo uses a 12-second retention period so the graphs visibly slide forward as old samples expire. The setup is also idempotent, so you can stop and restart the demo without first cleaning up the time series keys."},{"id":"redis-commands-used","title":"Redis commands used","role":"content","text":"The implementation uses these time series commands directly through `redis-py`'s generic command interface:\n\n* [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create) - Create one time series per sensor with retention and labels\n* [`TS.MADD`](https://redis.io/docs/latest/commands/ts.madd) - Batch-ingest readings from all three sensors every 500ms\n* [`TS.GET`](https://redis.io/docs/latest/commands/ts.get) - Fetch the latest reading for a sensor\n* [`TS.RANGE`](https://redis.io/docs/latest/commands/ts.range) - Read raw recent samples and aggregated 3-second buckets\n* `ALIGN 0` with `TS.RANGE ... AGGREGATION` - Keep bucket boundaries stable as the visible window moves"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before running the demo, make sure that:\n\n* Redis is running and accessible. By default, the demo connects to `localhost:6379`.\n* Your Redis deployment includes time series support.\n* The `redis` Python package is installed:\n\n[code example]"},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of three Python files. Download them from the [`redis-py` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/time-series-dashboard/redis-py) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"Start the dashboard server:\n\n[code example]\n\nThe server accepts optional flags if your Redis instance is not on the default host and port:\n\n[code example]\n\nAfter starting the server, visit `http://localhost:8080`.\n\nThe dashboard polls a JSON snapshot endpoint several times per second to show:\n\n* Three stacked rolling graphs of raw sensor readings\n* A bucket summary strip aligned to the same time axis as each graph\n* Minimum, maximum, and average values for each visible bucket\n* A moving 12-second window where old samples disappear as retention expires them\n\nBecause the graph and the bucket summary share the same moving time scale, you can see how raw samples relate to their aggregate bucket without switching views or interpreting a separate table."},{"id":"what-to-look-for","title":"What to look for","role":"content","text":"As you watch the dashboard, pay attention to how the Redis query patterns map to the UI:\n\n* New points arrive every 500ms through `TS.MADD`\n* The graphs show raw values returned by `TS.RANGE`\n* The bucket summaries use `TS.RANGE ... ALIGN 0 AGGREGATION min|max|avg 3000`\n* The left edge of the graph keeps advancing because the time series retention is short\n* The bucket boundaries stay fixed even while the visible window moves\n* The dashboard remains safe to rerun because series creation is idempotent"},{"id":"why-this-shape-works-well","title":"Why this shape works well","role":"content","text":"This demo intentionally uses only three sensors and a very short time horizon. That keeps the visualization small enough to understand at a glance while still demonstrating:\n\n* Repeated high-frequency ingest\n* Querying recent raw history\n* Aggregating into fixed time buckets\n* Short retention and visible expiration\n\nFor a first time series example, this is often easier to understand than a larger dashboard with many metrics, filters, or tables."},{"id":"production-considerations","title":"Production considerations","role":"content","text":"This example intentionally keeps the server and UI small so the Redis behavior is easy to follow. In production, you would usually want to add:\n\n* Authentication and authorization\n* Persistent frontend assets instead of inline HTML\n* Better error reporting and health checks\n* Deployment-specific retention, window sizes, and aggregation intervals\n* Stronger key namespacing if multiple applications share the same Redis deployment"},{"id":"learn-more","title":"Learn more","role":"related","text":"* [redis-py guide](https://redis.io/docs/latest/develop/clients/redis-py) - Install and use the Python client\n* [Time series overview](https://redis.io/docs/latest/develop/data-types/timeseries) - Time series concepts and commands\n* [TS.RANGE command](https://redis.io/docs/latest/commands/ts.range) - Query raw and aggregated ranges from a time series\n* [TS.MADD command](https://redis.io/docs/latest/commands/ts.madd) - Add multiple samples in one call\n* [TS.CREATE command](https://redis.io/docs/latest/commands/ts.create) - Create a time series with labels and retention"}],"examples":[{"id":"data-model-ex0","language":"text","code":"ts:sensor:power_consumption:{sensor_id}","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"ts:sensor:power_consumption:power-1\nts:sensor:power_consumption:power-2\nts:sensor:power_consumption:power-3","section_id":"data-model"},{"id":"data-model-ex2","language":"text","code":"site = demo\nsensor_type = power_consumption\nsensor_id = power-1\nzone = north\nunit = watts","section_id":"data-model"},{"id":"prerequisites-ex0","language":"bash","code":"pip install redis","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir time-series-dashboard-demo && cd time-series-dashboard-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/time-series-dashboard/redis-py\ncurl -O $BASE/sensor_simulator.py\ncurl -O $BASE/timeseries_store.py\ncurl -O $BASE/dashboard.py","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"python dashboard.py","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"bash","code":"python dashboard.py --redis-host 127.0.0.1 --redis-port 6379 --port 8080","section_id":"start-the-demo-server"}]}
{"schema_version":2,"id":"develop/use-cases/time-series-dashboard/ruby","title":"Rolling sensor graph demo with Redis and Ruby","url":"https://redis.io/docs/latest/develop/use-cases/time-series-dashboard/ruby/","summary":"Build a Redis-backed rolling sensor graph demo in Ruby with redis-rb","aliases":["/develop/use-cases/time-series-dashboard/redis-rb"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:55:04+01:00","children":[],"page_type":"content","content_hash":"2b60cff804e908715f56545bddfa8d702ff10b6ef7c1067ef3caf3b39bfca6a0","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a compact rolling sensor graph demo in Ruby with [`redis-rb`](https://redis.io/docs/latest/develop/clients/ruby) and Redis time series support. The example simulates three power sensors, ingests readings into Redis, and serves a local browser dashboard that updates in real time."},{"id":"overview","title":"Overview","role":"overview","text":"Time series are a natural fit for telemetry, monitoring, and IoT-style workloads. In this example, Redis stores a stream of timestamped readings from three simulated power sensors.\n\nThe demo is designed to make a few ideas easy to see:\n\n* Raw readings arrive every 500ms\n* Each graph shows only the most recent 12 seconds\n* Older samples disappear because the time series retention is short\n* 3-second buckets summarize the same readings with minimum, maximum, and average values\n* The bucket summaries are drawn under the same moving time scale as the graph"},{"id":"how-it-works","title":"How it works","role":"content","text":"The example has three main parts:\n\n1. A `SensorSimulator` generates realistic-looking power readings with drift and occasional spikes\n2. A `RedisTimeSeriesStore` creates the time series keys and issues Redis TimeSeries queries\n3. A small local web server built with WEBrick renders three stacked combined graph-and-bucket views and serves a JSON snapshot endpoint\n\nEach sensor is stored in its own time series with labels such as `sensor_type`, `sensor_id`, `zone`, and `unit`. The dashboard uses [`TS.MADD`](https://redis.io/docs/latest/commands/ts.madd) to ingest new readings and [`TS.RANGE`](https://redis.io/docs/latest/commands/ts.range) to query both raw samples and aggregated bucket summaries. The aggregate queries use aligned buckets so the bucket boundaries stay stable as the visible window moves."},{"id":"the-ruby-files","title":"The Ruby files","role":"content","text":"The implementation is split across three small files:\n\n* [`sensor_simulator.rb`](https://github.com/redis/docs/blob/main/content/develop/use-cases/time-series-dashboard/ruby/sensor_simulator.rb) - Sensor definitions and sample generation\n* [`timeseries_store.rb`](https://github.com/redis/docs/blob/main/content/develop/use-cases/time-series-dashboard/ruby/timeseries_store.rb) - Redis TimeSeries command helpers\n* [`demo_server.rb`](https://github.com/redis/docs/blob/main/content/develop/use-cases/time-series-dashboard/ruby/demo_server.rb) - Local WEBrick server, background ingest loop, and inline dashboard UI\n\nThe Redis helper issues time series commands directly through `redis-rb`'s low-level `call` method, which keeps the example small while still making the Redis command flow explicit."},{"id":"data-model","title":"Data model","role":"content","text":"Series keys use this pattern:\n\n[code example]\n\nFor example:\n\n[code example]\n\nEach time series has labels similar to:\n\n[code example]\n\nThe demo uses a 12-second retention period so the graphs visibly slide forward as old samples expire. The setup is also idempotent, so you can stop and restart the demo without first cleaning up the time series keys."},{"id":"redis-commands-used","title":"Redis commands used","role":"content","text":"The implementation uses these time series commands directly through `redis-rb`:\n\n* [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create) - Create one time series per sensor with retention and labels\n* [`TS.MADD`](https://redis.io/docs/latest/commands/ts.madd) - Batch-ingest readings from all three sensors every 500ms\n* [`TS.GET`](https://redis.io/docs/latest/commands/ts.get) - Fetch the latest reading for a sensor\n* [`TS.RANGE`](https://redis.io/docs/latest/commands/ts.range) - Read raw recent samples and aggregated 3-second buckets\n* `ALIGN 0` with `TS.RANGE ... AGGREGATION` - Keep bucket boundaries stable as the visible window moves"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before running the demo, make sure that:\n\n* Redis is running and accessible. By default, the demo connects to `localhost:6379`.\n* Your Redis deployment includes time series support.\n* The required gems are installed:\n\n[code example]"},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo consists of three Ruby files. Download them from the [`ruby` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/time-series-dashboard/ruby) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"Start the dashboard server:\n\n[code example]\n\nThe server accepts optional flags if your Redis instance is not on the default host and port:\n\n[code example]\n\nYou can also override the same values with `REDIS_HOST`, `REDIS_PORT`, and `PORT`.\n\nAfter starting the server, visit `http://localhost:8080`.\n\nThe dashboard polls a JSON snapshot endpoint several times per second to show:\n\n* Three stacked rolling graphs of raw sensor readings\n* A bucket summary strip aligned to the same time axis as each graph\n* Minimum, maximum, and average values for each visible bucket\n* A moving 12-second window where old samples disappear as retention expires them\n\nBecause the graph and the bucket summary share the same moving time scale, you can see how raw samples relate to their aggregate bucket without switching views or interpreting a separate table."},{"id":"what-to-look-for","title":"What to look for","role":"content","text":"As you watch the dashboard, pay attention to how the Redis query patterns map to the UI:\n\n* New points arrive every 500ms through `TS.MADD`\n* The graphs show raw values returned by `TS.RANGE`\n* The bucket summaries use `TS.RANGE ... ALIGN 0 AGGREGATION min|max|avg 3000`\n* The left edge of the graph keeps advancing because the time series retention is short\n* The bucket boundaries stay fixed even while the visible window moves\n* The dashboard remains safe to rerun because series creation is idempotent"},{"id":"why-this-shape-works-well","title":"Why this shape works well","role":"content","text":"This demo intentionally uses only three sensors and a very short time horizon. That keeps the visualization small enough to understand at a glance while still demonstrating:\n\n* Repeated high-frequency ingest\n* Querying recent raw history\n* Aggregating into fixed time buckets\n* Short retention and visible expiration\n\nFor a first time series example, this is often easier to understand than a larger dashboard with many metrics, filters, or tables."},{"id":"production-considerations","title":"Production considerations","role":"content","text":"This example intentionally keeps the server and UI small so the Redis behavior is easy to follow. In production, you would usually want to add:\n\n* Authentication and authorization\n* Separate static assets instead of inline HTML\n* Better error reporting and health checks\n* Deployment-specific retention, window sizes, and aggregation intervals\n* Stronger key namespacing if multiple applications share the same Redis deployment"},{"id":"learn-more","title":"Learn more","role":"related","text":"* [Ruby client guide](https://redis.io/docs/latest/develop/clients/ruby) - Install and use the Ruby client\n* [Time series overview](https://redis.io/docs/latest/develop/data-types/timeseries) - Time series concepts and commands\n* [TS.RANGE command](https://redis.io/docs/latest/commands/ts.range) - Query raw and aggregated ranges from a time series\n* [TS.MADD command](https://redis.io/docs/latest/commands/ts.madd) - Add multiple samples in one call\n* [TS.CREATE command](https://redis.io/docs/latest/commands/ts.create) - Create a time series with labels and retention"}],"examples":[{"id":"data-model-ex0","language":"text","code":"ts:sensor:power_consumption:{sensor_id}","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"ts:sensor:power_consumption:power-1\nts:sensor:power_consumption:power-2\nts:sensor:power_consumption:power-3","section_id":"data-model"},{"id":"data-model-ex2","language":"text","code":"site = demo\nsensor_type = power_consumption\nsensor_id = power-1\nzone = north\nunit = watts","section_id":"data-model"},{"id":"prerequisites-ex0","language":"bash","code":"gem install redis webrick","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir time-series-dashboard-demo && cd time-series-dashboard-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/time-series-dashboard/ruby\ncurl -O $BASE/sensor_simulator.rb\ncurl -O $BASE/timeseries_store.rb\ncurl -O $BASE/demo_server.rb","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"ruby demo_server.rb","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"bash","code":"ruby demo_server.rb --redis-host 127.0.0.1 --redis-port 6379 --port 8080","section_id":"start-the-demo-server"}]}
{"schema_version":2,"id":"develop/use-cases/time-series-dashboard/rust","title":"Rolling sensor graph demo with Rust","url":"https://redis.io/docs/latest/develop/use-cases/time-series-dashboard/rust/","summary":"Build a Redis-backed rolling sensor graph demo in Rust with redis-rs","aliases":["/develop/use-cases/time-series-dashboard/redis-rs"],"tags":["docs","develop","stack","oss","rs","rc"],"last_updated":"2026-09-16T16:55:04+01:00","children":[],"page_type":"content","content_hash":"8d6a9f00eb8a23bec031f834ca549f8caace5b031cf8f7d19559673aa0ef6a04","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to build a compact rolling sensor graph demo in Rust with [`redis-rs`](https://redis.io/docs/latest/develop/clients/rust) and Redis time series support. The example simulates three power sensors, ingests readings into Redis, and serves a local browser dashboard that updates in real time."},{"id":"overview","title":"Overview","role":"overview","text":"Time series are a natural fit for telemetry, monitoring, and IoT-style workloads. In this example, Redis stores a stream of timestamped readings from three simulated power sensors.\n\nThe demo is designed to make a few ideas easy to see:\n\n* Raw readings arrive every 500ms\n* Each graph shows only the most recent 12 seconds\n* Older samples disappear because the time series retention is short\n* 3-second buckets summarize the same readings with minimum, maximum, and average values\n* The bucket summaries are drawn under the same moving time scale as the graph"},{"id":"how-it-works","title":"How it works","role":"content","text":"The example has three main parts:\n\n1. A `SensorSimulator` generates realistic-looking power readings with drift and occasional spikes\n2. A `RedisTimeSeriesStore` creates the time series keys and issues Redis TimeSeries queries through `redis::cmd(...)`\n3. A small local HTTP server built with `axum` renders three stacked combined graph-and-bucket views and polls a JSON snapshot endpoint\n\nEach sensor is stored in its own time series with labels such as `sensor_type`, `sensor_id`, `zone`, and `unit`. The dashboard uses [`TS.MADD`](https://redis.io/docs/latest/commands/ts.madd) to ingest new readings and [`TS.RANGE`](https://redis.io/docs/latest/commands/ts.range) to query both raw samples and aggregated bucket summaries. The aggregate queries use aligned buckets so the bucket boundaries stay stable as the visible window moves."},{"id":"the-rust-files","title":"The Rust files","role":"content","text":"The implementation is split across three files:\n\n* [`sensor_simulator.rs`](https://github.com/redis/docs/blob/main/content/develop/use-cases/time-series-dashboard/rust/sensor_simulator.rs) - Sensor definitions and sample generation\n* [`timeseries_store.rs`](https://github.com/redis/docs/blob/main/content/develop/use-cases/time-series-dashboard/rust/timeseries_store.rs) - Redis TimeSeries command helpers\n* [`demo_server.rs`](https://github.com/redis/docs/blob/main/content/develop/use-cases/time-series-dashboard/rust/demo_server.rs) - Local HTTP server and inline dashboard UI\n\nThe Redis helper issues time series commands with `redis::cmd(...)`, which keeps the example small while still making the Redis command flow explicit."},{"id":"data-model","title":"Data model","role":"content","text":"Series keys use this pattern:\n\n[code example]\n\nFor example:\n\n[code example]\n\nEach time series has labels similar to:\n\n[code example]\n\nThe demo uses a 12-second retention period so the graphs visibly slide forward as old samples expire. The setup is also idempotent, so you can stop and restart the demo without first cleaning up the time series keys."},{"id":"redis-commands-used","title":"Redis commands used","role":"content","text":"The implementation uses these time series commands directly through `redis-rs`:\n\n* [`TS.CREATE`](https://redis.io/docs/latest/commands/ts.create) - Create one time series per sensor with retention and labels\n* [`TS.MADD`](https://redis.io/docs/latest/commands/ts.madd) - Batch-ingest readings from all three sensors every 500ms\n* [`TS.GET`](https://redis.io/docs/latest/commands/ts.get) - Fetch the latest reading for a sensor\n* [`TS.RANGE`](https://redis.io/docs/latest/commands/ts.range) - Read raw recent samples and aggregated 3-second buckets\n* `ALIGN 0` with `TS.RANGE ... AGGREGATION` - Keep bucket boundaries stable as the visible window moves"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before running the demo, make sure that:\n\n* Redis is running and accessible. By default, the demo connects to `localhost:6379`.\n* Your Redis deployment includes time series support.\n* The required Rust crates are available:\n\n[code example]"},{"id":"running-the-demo","title":"Running the demo","role":"content","text":""},{"id":"get-the-source-files","title":"Get the source files","role":"content","text":"The demo uses a Cargo project with sources at the project root. Download the files from the [`rust` source folder](https://github.com/redis/docs/tree/main/content/develop/use-cases/time-series-dashboard/rust) on GitHub, or grab them with `curl`:\n\n[code example]"},{"id":"start-the-demo-server","title":"Start the demo server","role":"content","text":"Build and run the demo:\n\n[code example]\n\nThe server accepts optional flags if your Redis instance is not on the default host and port:\n\n[code example]\n\nYou can also override the full Redis URL with `REDIS_URL`.\n\nAfter starting the server, visit `http://localhost:8080`.\n\nThe dashboard polls a JSON snapshot endpoint several times per second to show:\n\n* Three stacked rolling graphs of raw sensor readings\n* A bucket summary strip aligned to the same time axis as each graph\n* Minimum, maximum, and average values for each visible bucket\n* A moving 12-second window where old samples disappear as retention expires them\n\nBecause the graph and the bucket summary share the same moving time scale, you can see how raw samples relate to their aggregate bucket without switching views or interpreting a separate table."},{"id":"what-to-look-for","title":"What to look for","role":"content","text":"As you watch the dashboard, pay attention to how the Redis query patterns map to the UI:\n\n* New points arrive every 500ms through `TS.MADD`\n* The graphs show raw values returned by `TS.RANGE`\n* The bucket summaries use `TS.RANGE ... ALIGN 0 AGGREGATION min|max|avg 3000`\n* The left edge of the graph keeps advancing because the time series retention is short\n* The bucket boundaries stay fixed even while the visible window moves\n* The dashboard remains safe to rerun because series creation is idempotent"},{"id":"why-this-shape-works-well","title":"Why this shape works well","role":"content","text":"This demo intentionally uses only three sensors and a very short time horizon. That keeps the visualization small enough to understand at a glance while still demonstrating:\n\n* Repeated high-frequency ingest\n* Querying recent raw history\n* Aggregating into fixed time buckets\n* Short retention and visible expiration\n\nFor a first time series example, this is often easier to understand than a larger dashboard with many metrics, filters, or tables."},{"id":"production-considerations","title":"Production considerations","role":"content","text":"This example intentionally keeps the server and UI small so the Redis behavior is easy to follow. In production, you would usually want to add:\n\n* Authentication and authorization\n* Separate static assets instead of inline HTML\n* Better error reporting and health checks\n* Deployment-specific retention, window sizes, and aggregation intervals\n* Stronger key namespacing if multiple applications share the same Redis deployment"},{"id":"learn-more","title":"Learn more","role":"related","text":"* [Rust client guide](https://redis.io/docs/latest/develop/clients/rust) - Install and use the Rust client\n* [Time series overview](https://redis.io/docs/latest/develop/data-types/timeseries) - Time series concepts and commands\n* [TS.RANGE command](https://redis.io/docs/latest/commands/ts.range) - Query raw and aggregated ranges from a time series\n* [TS.MADD command](https://redis.io/docs/latest/commands/ts.madd) - Add multiple samples in one call\n* [TS.CREATE command](https://redis.io/docs/latest/commands/ts.create) - Create a time series with labels and retention"}],"examples":[{"id":"data-model-ex0","language":"text","code":"ts:sensor:power_consumption:{sensor_id}","section_id":"data-model"},{"id":"data-model-ex1","language":"text","code":"ts:sensor:power_consumption:power-1\nts:sensor:power_consumption:power-2\nts:sensor:power_consumption:power-3","section_id":"data-model"},{"id":"data-model-ex2","language":"text","code":"site = demo\nsensor_type = power_consumption\nsensor_id = power-1\nzone = north\nunit = watts","section_id":"data-model"},{"id":"prerequisites-ex0","language":"bash","code":"cargo build","section_id":"prerequisites"},{"id":"get-the-source-files-ex0","language":"bash","code":"mkdir time-series-dashboard-demo && cd time-series-dashboard-demo\nBASE=https://raw.githubusercontent.com/redis/docs/main/content/develop/use-cases/time-series-dashboard/rust\ncurl -O $BASE/Cargo.toml\ncurl -O $BASE/sensor_simulator.rs\ncurl -O $BASE/timeseries_store.rs\ncurl -O $BASE/demo_server.rs","section_id":"get-the-source-files"},{"id":"start-the-demo-server-ex0","language":"bash","code":"cargo run --bin demo_server","section_id":"start-the-demo-server"},{"id":"start-the-demo-server-ex1","language":"bash","code":"cargo run --bin demo_server -- --redis-host 127.0.0.1 --redis-port 6379 --port 8080","section_id":"start-the-demo-server"}]}
{"schema_version":2,"id":"develop/using-commands","title":"Using Redis commands","url":"https://redis.io/docs/latest/develop/using-commands/","summary":"Learn how to interact with Redis using commands.","aliases":["/develop/interact/","/develop/interact","/develop/use/","/develop/use","/develop/use/pipelining/","/develop/use/pipelining"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T14:32:43+01:00","children":[{"id":"develop/using-commands/keyspace","summary":"Managing keys in Redis: Key expiration, scanning, altering and querying the key space","title":"Keys and values","url":"https://redis.io/docs/latest/develop/using-commands/keyspace/"},{"id":"develop/using-commands/pipelining","summary":"How to optimize round-trip times by batching Redis commands","title":"Redis pipelining","url":"https://redis.io/docs/latest/develop/using-commands/pipelining/"},{"id":"develop/using-commands/transactions","summary":"How transactions work in Redis","title":"Transactions","url":"https://redis.io/docs/latest/develop/using-commands/transactions/"},{"id":"develop/using-commands/multi-key-operations","summary":"Multi-key command behavior across Redis configurations and clustering setups","title":"Multi-key operations","url":"https://redis.io/docs/latest/develop/using-commands/multi-key-operations/"}],"page_type":"content","content_hash":"78f54cbaf117a7fcfe2aa3840deea9f1edff28233a21c32f2ddca760cf002f41","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Client applications and tools interact with Redis using commands. Most of the\ncommands implement [data types](https://redis.io/docs/latest/develop/data-types) to store and retrieve data,\nbut there are also commands that deal with server configuration, security, and more. \n\nThe sections below give an overview of how Redis commands operate. See the\n[Redis commands reference](/commands) for a complete list of commands."},{"id":"command-structure","title":"Command structure","role":"syntax","text":"Each command is identified by a unique name. Related groups of commands\ntend to follow a consistent naming convention. For example, all commands that\ndeal with [hashes](https://redis.io/docs/latest/develop/data-types/hashes) start with the `H` prefix.\nMost commands receive one or more arguments that specify the data to operate on.\nFor data type commands, the first argument is usually the [key](https://redis.io/docs/latest/develop/using-commands/keyspace) that identifies the target data object.\n\nAfter you issue a command, the server attempts to process it and then returns\na response. Commands that update data typically return a status message (such as `OK`)\nor a number that indicates the number of items changed or updated. Commands that\nretrieve data return the requested data. An unsuccessful command returns an\nerror message that describes the problem.\n\nInteracting with a Redis server involves a sequence of commands and responses.\nThe effect of a given command is the same regardless of whether you send it\nfrom a [client library](https://redis.io/docs/latest/develop/clients), or from a client tool\nsuch as [redis-cli](https://redis.io/docs/latest/develop/tools/cli) or\n[Redis Insight](https://redis.io/docs/latest/develop/tools/insight). This is very useful\nduring development. You can use a high-level tool to experiment with a\ncommand, set up test data, or prototype a data model, and then access the\nprepared data from your application code. Most Redis code examples are\npresented with an excerpt of a CLI session and the equivalent application code\nfor each client library."},{"id":"batching-commands","title":"Batching commands","role":"content","text":"Although you can issue Redis commands one at a time, it's often more efficient\nto batch a sequence of related commands together into a *pipeline*. A pipeline\nsends several commands to the server as a single communication and receives\nthe responses in the same way. See\n[Pipelining](https://redis.io/docs/latest/develop/using-commands/pipelining) for a full\ndescription of the technique and see also the pipelining examples for the\n[client libraries](https://redis.io/docs/latest/develop/clients).\n\nAnother reason to batch commands is to treat them as an uninterrupted unit.\nYou should do this if you need to be sure that the commands are all\ncompleted without the same data being modified by another client (which\ncould leave the data in an inconsistent state). Redis uses *transactions*\nto implement this behavior. See\n[Transactions](https://redis.io/docs/latest/develop/using-commands/transactions) for\nmore information and see also the transaction examples for the\n[client libraries](https://redis.io/docs/latest/develop/clients)."},{"id":"more-information","title":"More information","role":"content","text":"The other pages in this section describe Redis command concepts in more detail:"}],"examples":[]}
{"schema_version":2,"id":"develop/using-commands/keyspace","title":"Keys and values","url":"https://redis.io/docs/latest/develop/using-commands/keyspace/","summary":"Managing keys in Redis: Key expiration, scanning, altering and querying the key space","aliases":["/develop/use/keyspace","/manual/keyspace/","/develop/keyspace/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T14:32:43+01:00","page_type":"content","content_hash":"660ce2e35879198605543582deb776d978e572eabb94056d90932e91b42be306","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Every data object that you store in a Redis database has its own unique\n*key*. The key is a string that you pass to Redis commands to\nretrieve the corresponding object or modify its data. The data object associated with a\nparticular key is known as the *value* and the two together are known as\nas *key-value pair*."},{"id":"content-of-keys","title":"Content of keys","role":"content","text":"A key is typically a textual name that has some meaning within your\ndata model. Unlike variable names in a programming language, Redis keys have few\nrestrictions on their format, so keys with whitespace or punctuation characters\nare mostly fine (for example, \"1st Attempt\", or \"% of price in $\"). Redis doesn't\nsupport namespaces or other categories for keys, so you must take care to avoid\nname collisions. However, there is a convention for using the colon \":\"\ncharacter to split keys into sections (for example, \"person:1\", \"person:2\",\n\"office:London\", \"office:NewYork:1\"). You can use this as a simple way to collect\nkeys together into categories.\n\nAlthough keys are usually textual, Redis actually implements *binary-safe* keys,\nso you can use any sequence of bytes as a valid key, such as a JPEG file or\na struct value from your app. The empty string is also a valid key in Redis.\n\nThere are also a few other things to bear in mind about keys: \n\n* Very long keys are not a good idea. For instance, a key of 1024 bytes is a bad\n  idea not only memory-wise, but also because the lookup of the key in the\n  dataset may require several costly key-comparisons. Even when the task at hand\n  is to match the existence of a large value, hashing it (for example\n  with SHA1) is a better idea, especially from the perspective of memory\n  and bandwidth.\n* Very short keys are often not a good idea. There is little point in writing\n  \"u1000flw\" as a key if you can instead write \"user:1000:followers\".  The latter\n  is more readable and the added space is minor compared to the space used by\n  the key object itself and the value object. While short keys will obviously\n  consume a bit less memory, your job is to find the right balance.\n* Try to stick with a schema. For instance \"object-type:id\" is a good\n  idea, as in \"user:1000\". Dots or dashes are often used for multi-word\n  fields, as in \"comment:4321:reply.to\" or \"comment:4321:reply-to\".\n* The maximum allowed key size is 512 MB."},{"id":"hashtags","title":"Hashtags","role":"content","text":"Redis uses [hashing](https://en.wikipedia.org/wiki/Hash_table) to retrieve the\nvalue associated with a key in a highly efficient way. Hashing involves combining the\nraw byte values from the key to produce an integer *index* number. The index is then\nused to locate the *hash slot* where the value for the key is stored.\n\nNormally, the whole key is used to calculate the hash index, but there are some\nsituations where you need to hash only a part of the key. You can select the\nsection of the key you want to hash using a pair of curly braces `{...}` to create\na *hashtag*. For example, the keys `person:1` and `person:2` produce different\nhash indices but `{person}:1` and `{person}:2` produce the same index because\nonly the `person` hashtag section in the braces is used for the hash calculation.\n\nA common use of hashtags is to allow\n[multi-key operations](https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering)\nwith a *clustered* database (see\n[Database clustering](https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering#multikey-operations)\nfor more information). Redis doesn't allow most multi-key operations in a clustered database\nunless all the keys produce the same hash index. For example, the\n[SINTER](https://redis.io/docs/latest/commands/sinter)\ncommand finds the [intersection](https://en.wikipedia.org/wiki/Intersection_(set_theory))\nof two different [set](https://redis.io/docs/latest/develop/data-types/sets) values.\nThis means that the command\n\n[code example]\n\nwon't work with a clustered database but\n\n[code example]\n\nwill work because the hashtag ensures the two keys produce the same hash index.\n\nNote that although hashtags are useful in certain cases, you shouldn't make\na habit of using them generally. If you have too many keys mapped to the same\nhash slot then this will eventually harm the performance of your database.\nSee [Database clustering](https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering)\nfor more information about how to use hashtags."},{"id":"altering-and-querying-the-key-space","title":"Altering and querying the key space","role":"content","text":"There are commands that are not defined on particular types, but are useful\nin order to interact with the space of keys, and thus, can be used with\nkeys of any type.\n\nFor example the [`EXISTS`](https://redis.io/docs/latest/commands/exists) command returns 1 or 0 to signal if a given key\nexists or not in the database, while the [`DEL`](https://redis.io/docs/latest/commands/del) command deletes a key\nand associated value, whatever the value is.\n\n    > set mykey hello\n    OK\n    > exists mykey\n    (integer) 1\n    > del mykey\n    (integer) 1\n    > exists mykey\n    (integer) 0\n\nFrom the examples you can also see how [`DEL`](https://redis.io/docs/latest/commands/del) itself returns 1 or 0 depending on whether\nthe key was removed (it existed) or not (there was no such key with that\nname).\n\nThere are many key space related commands, but the above two are the\nessential ones together with the [`TYPE`](https://redis.io/docs/latest/commands/type) command, which returns the kind\nof value stored at the specified key:\n\n    > set mykey x\n    OK\n    > type mykey\n    string\n    > del mykey\n    (integer) 1\n    > type mykey\n    none"},{"id":"key-expiration","title":"Key expiration","role":"content","text":"Before moving on, we should look at an important Redis feature that works regardless of the type of value you're storing: key expiration. Key expiration lets you set a timeout for a key, also known as a \"time to live\", or \"TTL\". When the time to live elapses, the key is automatically destroyed. \n\nA few important notes about key expiration:\n\n* They can be set both using seconds or milliseconds precision.\n* However the expire time resolution is always 1 millisecond.\n* Information about expires are replicated and persisted on disk, the time virtually passes when your Redis server remains stopped (this means that Redis saves the date at which a key will expire).\n\nUse the [`EXPIRE`](https://redis.io/docs/latest/commands/expire) command to set a key's expiration:\n\n    > set key some-value\n    OK\n    > expire key 5\n    (integer) 1\n    > get key (immediately)\n    \"some-value\"\n    > get key (after some time)\n    (nil)\n\nThe key vanished between the two [`GET`](https://redis.io/docs/latest/commands/get) calls, since the second call was\ndelayed more than 5 seconds. In the example above we used [`EXPIRE`](https://redis.io/docs/latest/commands/expire) in\norder to set the expire (it can also be used in order to set a different\nexpire to a key already having one, like [`PERSIST`](https://redis.io/docs/latest/commands/persist) can be used in order\nto remove the expire and make the key persistent forever). However we\ncan also create keys with expires using other Redis commands. For example\nusing [`SET`](https://redis.io/docs/latest/commands/set) options:\n\n    > set key 100 ex 10\n    OK\n    > ttl key\n    (integer) 9\n\nThe example above sets a key with the string value `100`, having an expire\nof ten seconds. Later the [`TTL`](https://redis.io/docs/latest/commands/ttl) command is called in order to check the\nremaining time to live for the key.\n\nIn order to set and check expires in milliseconds, check the [`PEXPIRE`](https://redis.io/docs/latest/commands/pexpire) and\nthe [`PTTL`](https://redis.io/docs/latest/commands/pttl) commands, and the full list of [`SET`](https://redis.io/docs/latest/commands/set) options."},{"id":"navigating-the-keyspace","title":"Navigating the keyspace","role":"content","text":""},{"id":"scan","title":"Scan","role":"content","text":"To incrementally  iterate over the keys in a Redis database in an efficient manner, you can use the [`SCAN`](https://redis.io/docs/latest/commands/scan) command.\n\nSince [`SCAN`](https://redis.io/docs/latest/commands/scan) allows for incremental iteration, returning only a small number of elements per call, it can be used in production without the downside of commands like [`KEYS`](https://redis.io/docs/latest/commands/keys) or [`SMEMBERS`](https://redis.io/docs/latest/commands/smembers) that may block the server for a long time (even several seconds) when called against big collections of keys or elements.\n\nHowever while blocking commands like [`SMEMBERS`](https://redis.io/docs/latest/commands/smembers) are able to provide all the elements that are part of a Set in a given moment.\nThe [`SCAN`](https://redis.io/docs/latest/commands/scan) family of commands only offer limited guarantees about the returned elements since the collection that we incrementally iterate can change during the iteration process."},{"id":"keys","title":"Keys","role":"content","text":"Another way to iterate over the keyspace is to use the [`KEYS`](https://redis.io/docs/latest/commands/keys) command, but this approach should be used with care, since [`KEYS`](https://redis.io/docs/latest/commands/keys) will block the Redis server until all keys are returned.\n\n**Warning**: consider [`KEYS`](https://redis.io/docs/latest/commands/keys) as a command that should only be used in production\nenvironments with extreme care.\n\n[`KEYS`](https://redis.io/docs/latest/commands/keys) may ruin performance when it is executed against large databases.\nThis command is intended for debugging and special operations, such as changing\nyour keyspace layout.\nDon't use [`KEYS`](https://redis.io/docs/latest/commands/keys) in your regular application code.\nIf you're looking for a way to find keys in a subset of your keyspace, consider\nusing [`SCAN`](https://redis.io/docs/latest/commands/scan) or [sets](https://redis.io/docs/latest/develop/data-types/sets).\n\n[tdts]: /develop/data-types#sets\n\nSupported glob-style patterns:\n\n* `h?llo` matches `hello`, `hallo` and `hxllo`\n* `h*llo` matches `hllo` and `heeeello`\n* `h[ae]llo` matches `hello` and `hallo,` but not `hillo`\n* `h[^e]llo` matches `hallo`, `hbllo`, ... but not `hello`\n* `h[a-b]llo` matches `hallo` and `hbllo`\n\nUse `\\` to escape special characters if you want to match them verbatim."}],"examples":[{"id":"hashtags-ex0","language":"bash","code":"SINTER group:1 group:2","section_id":"hashtags"},{"id":"hashtags-ex1","language":"bash","code":"SINTER {group}:1 {group}:2","section_id":"hashtags"}]}
{"schema_version":2,"id":"develop/using-commands/multi-key-operations","title":"Multi-key operations","url":"https://redis.io/docs/latest/develop/using-commands/multi-key-operations/","summary":"Multi-key command behavior across Redis configurations and clustering setups","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-01-16T06:26:38-08:00","page_type":"content","content_hash":"cc91ff397cbab28aea88db3b64860bf4b02bd8cda1d3680ec16f26937ed1365b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Multi-key operations in Redis allow you to work with multiple keys in a single command, but their behavior varies significantly depending on your Redis configuration and clustering setup. This page provides a quick reference for developers working with multi-key operations across different Redis configurations."},{"id":"configurations","title":"Configurations","role":"configuration","text":"Redis supports five distinct configurations, each with different multi-key command behaviors:\n\n1. **ROS/RS clustering disabled** - Single Redis instance\n2. **ROS, clustering enabled** - Redis Open Source cluster\n3. **RS, clustering enabled, OSS cluster API enabled** - Redis Software with ROS cluster compatibility\n4. **RS, clustering enabled, OSS cluster API disabled** - Redis Software proprietary clustering\n5. **RS, Active-Active** - Redis Software Active-Active (considered clustered even with a single shard)\n\nROS stands for Redis Open Source and RS stands for Redis Software."},{"id":"command-behaviors","title":"Command behaviors","role":"syntax","text":"For each configuration, commands exhibit one of three behaviors:\n\n- **single-slot**: Commands must operate on keys within the same hash slot\n- **cross-slot (all shards)**: Commands can operate across all shards in the cluster\n- **cross-slot (within a single shard)**: Commands can operate across slots but only within a single shard"},{"id":"read-only-commands","title":"Read-only commands","role":"content","text":"| Behavior | Commands |\n|----------|----------|\n| **ROS/RS clustering disabled:**<br>&ndash; the whole DB (single shard)<br><br>**ROS clustering enabled, RS clustering enabled (OSS cluster API enabled):**<br>&ndash; the current shard<br><br>**RS clustering enabled (OSS cluster API disabled):**<br>&ndash; all shards | DBSIZE, KEYS, SCAN |\n| **ROS/RS clustering disabled:**<br>&ndash; cross-slot<br><br>**ROS clustering enabled, RS clustering enabled (OSS cluster API enabled):**<br>&ndash; single-slot<br><br>**RS clustering enabled (OSS cluster API disabled):**<br>&ndash; cross-slot (all shards) | EXISTS, MGET |\n| **ROS/RS clustering disabled:**<br>&ndash; cross-slot<br><br>**ROS clustering enabled, RS clustering enabled (OSS cluster API enabled), RS clustering enabled (OSS cluster API disabled):**<br>&ndash; single-slot | PFCOUNT, SDIFF, SINTER, SINTERCARD, SUNION, WATCH, XREAD, XREADGROUP, ZDIFF, ZINTER, ZINTERCARD, ZUNION |\n| **ROS/RS clustering disabled:**<br>&ndash; cross-slot<br><br>**ROS clustering enabled, RS clustering enabled (OSS cluster API enabled), RS clustering enabled (OSS cluster API disabled):**<br>&ndash; single-shard | JSON.MGET<br><br>Users won't get a CROSSSLOT error. However, when clustering is enabled, and not all specified keys are in the same slot, users will get partial results for all the slots on the current shard. |\n| **ROS/RS clustering disabled:**<br>&ndash; cross-slot (all shards)<br><br>**ROS clustering enabled, RS clustering enabled (OSS cluster API enabled), RS clustering enabled (OSS cluster API disabled):**<br>&ndash; cross-slot (all shards), cannot be part of a transaction | TS.MGET, TS.MRANGE, TS.MREVRANGE, TS.QUERYINDEX |"},{"id":"read-write-commands","title":"Read-write commands","role":"content","text":"| Behavior | Commands |\n|----------|----------|\n| **ROS/RS clustering disabled:**<br>&ndash; the whole DB (single shard)<br><br>**ROS clustering enabled, RS clustering enabled (OSS cluster API enabled):**<br>&ndash; the current shard<br><br>**RS clustering enabled (OSS cluster API disabled):**<br>&ndash; all shards | FLUSHALL, FLUSHDB |\n| **ROS/RS clustering disabled:**<br>&ndash; cross-slot<br><br>**ROS clustering enabled, RS clustering enabled (OSS cluster API enabled):**<br>&ndash; single-slot<br><br>**RS clustering enabled (OSS cluster API disabled):**<br>&ndash; cross-slot (all shards) | DEL, MSET, TOUCH, UNLINK<br><br>Note: on Active-Active, DEL, MSET, and UNLINK are single-slot |\n| **ROS/RS clustering disabled:**<br>&ndash; cross-slot<br><br>**ROS clustering enabled, RS clustering enabled (OSS cluster API enabled), RS clustering enabled (OSS cluster API disabled):**<br>&ndash; single-slot | BITOP, BLMOVE, BLMPOP, BLPOP, BRPOP, BRPOPLPUSH, BZMPOP, BZPOPMAX, BZPOPMIN, CMS.MERGE, COPY, GEORADIUS or GEORADIUSBYMEMBER (with STORE or STOREDIST), GEOSEARCHSTORE, JSON.MSET, LMOVE, LMPOP, MSETNX, PFMERGE, RENAME, RENAMENX, RPOPLPUSH, SDIFFSTORE, SINTERSTORE, SMOVE, SUNIONSTORE, TDIGEST.MERGE, TS.MADD, ZDIFFSTORE, ZINTERSTORE, ZMPOP, ZRANGESTORE, ZUNIONSTORE |\n| **ROS/RS clustering disabled:**<br>&ndash; cross-slot<br><br>**ROS clustering enabled, RS clustering enabled (OSS cluster API enabled), RS clustering enabled (OSS cluster API disabled):**<br>&ndash; single-shard | TS.CREATERULE, TS.DELETERULE<br><br>Users won't get a CROSSSLOT error. However, when clustering is enabled and the two specified keys are not in the same slot, users will get `(error) ERR TSDB: the key does not exist`. |"},{"id":"pipelines-transactions-and-scripts","title":"Pipelines, transactions, and scripts","role":"content","text":"| Behavior | Operations |\n|----------|------------|\n| **ROS/RS clustering disabled:**<br>&ndash; cross-slot<br>**ROS clustering enabled, RS clustering enabled (OSS cluster API enabled):**<br>&ndash; single-slot<br>**RS clustering enabled (OSS cluster API disabled):**<br>&ndash; cross-slot (all shards) | Pipelines |\n| **ROS/RS clustering disabled:**<br>&ndash; cross-slot<br>**ROS clustering enabled, RS clustering enabled (OSS cluster API enabled), RS clustering enabled (OSS cluster API disabled):**<br>&ndash; single-slot | Keys in a `MULTI/EXEC` transaction<br>Keys in a Lua script executed using EVAL or EVALSHA |"},{"id":"examples-by-configuration","title":"Examples by Configuration","role":"example","text":""},{"id":"single-instance-no-clustering","title":"Single Instance (No Clustering)","role":"content","text":"In a single Redis instance, all multi-key operations work without restrictions:\n\n[code example]"},{"id":"clustered-environments","title":"Clustered Environments","role":"content","text":"In clustered setups, you need to consider slot distribution:\n\n[code example]"},{"id":"active-active-databases","title":"Active-Active Databases","role":"content","text":"Active-Active databases have additional restrictions for write operations:\n\n[code example]"},{"id":"troubleshooting-multi-key-operations","title":"Troubleshooting Multi-Key Operations","role":"errors","text":""},{"id":"common-error-messages","title":"Common Error Messages","role":"content","text":"- **CROSSSLOT**: Keys in request don't hash to the same slot\n- **MOVED**: Key has moved to a different node (during resharding)\n- **TRYAGAIN**: Operation temporarily unavailable (during migration)"},{"id":"solutions","title":"Solutions","role":"content","text":"1. **Use hash tags** to group related keys\n2. **Redesign data model** to minimize cross-slot operations  \n3. **Check cluster state** during errors\n4. **Implement retry logic** for temporary failures"},{"id":"performance-considerations","title":"Performance Considerations","role":"performance","text":"- **Single-slot operations** are fastest as they don't require coordination\n- **Cross-slot operations** may have higher latency due to internal routing\n- **Pattern commands** (KEYS, FLUSHALL) scan all shards and can be expensive\n- **Module operations** may have optimized cross-slot implementations\n\nChoose your Redis configuration and design your data model based on your multi-key operation requirements."}],"examples":[{"id":"single-instance-no-clustering-ex0","language":"redis","code":"# Pipeline operations work across any keys\nPIPELINE\nSET user:1 \"Alice\"\nSET product:100 \"Widget\"\nGET user:1\nGET product:100\nEXEC\n\n# Transactions work with any keys\nMULTI\nSET counter:a 1\nSET counter:b 2\nINCR counter:a\nINCR counter:b\nEXEC","section_id":"single-instance-no-clustering"},{"id":"clustered-environments-ex0","language":"redis","code":"# This may fail if keys are in different slots\nMSET user:1 \"Alice\" user:2 \"Bob\"\n\n# Use hash tags to ensure same slot\nMSET {users}:1 \"Alice\" {users}:2 \"Bob\"\n\n# Check which slot a key belongs to\nCLUSTER KEYSLOT user:1\nCLUSTER KEYSLOT {users}:1","section_id":"clustered-environments"},{"id":"active-active-databases-ex0","language":"redis","code":"# Read operations can work across slots\nMGET user:1 user:2 product:100\n\n# Write operations must be in same slot\nMSET {data}:user:1 \"Alice\" {data}:user:2 \"Bob\"","section_id":"active-active-databases"}]}
{"schema_version":2,"id":"develop/using-commands/pipelining","title":"Redis pipelining","url":"https://redis.io/docs/latest/develop/using-commands/pipelining/","summary":"How to optimize round-trip times by batching Redis commands","aliases":["/develop/use/pipelining","/manual/pipelining/","/develop/reference/pipelining"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-29T16:29:37+01:00","page_type":"content","content_hash":"0bb19ff46ba510b735480405681c426ae1bc31c6f41476d78d77c0222334350b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis pipelining is a technique for improving performance by issuing multiple commands at once without waiting for the response to each individual command. Pipelining is supported by most Redis clients. This document describes the problem that pipelining is designed to solve and how pipelining works in Redis."},{"id":"requestresponse-protocols-and-round-trip-time-rtt","title":"Request/Response protocols and round-trip time (RTT)","role":"content","text":"Redis is a TCP server using the client-server model and what is called a *Request/Response* protocol.\n\nThis means that usually a request is accomplished with the following steps:\n\n* The client sends a query to the server, and reads from the socket, usually in a blocking way, for the server response.\n* The server processes the command and sends the response back to the client.\n\nSo for instance a four commands sequence is something like this:\n\n * *Client:* INCR X\n * *Server:* 1\n * *Client:* INCR X\n * *Server:* 2\n * *Client:* INCR X\n * *Server:* 3\n * *Client:* INCR X\n * *Server:* 4\n\nClients and Servers are connected via a network link.\nSuch a link can be very fast (a loopback interface) or very slow (a connection established over the Internet with many hops between the two hosts).\nWhatever the network latency is, it takes time for the packets to travel from the client to the server, and back from the server to the client to carry the reply.\n\nThis time is called RTT (Round Trip Time).\nIt's easy to see how this can affect performance when a client needs to perform many requests in a row (for instance adding many elements to the same list, or populating a database with many keys).\nFor instance if the RTT time is 250 milliseconds (in the case of a very slow link over the Internet), even if the server is able to process 100k requests per second, we'll be able to process at max four requests per second.\n\nIf the interface used is a loopback interface, the RTT is much shorter, typically sub-millisecond, but even this will add up to a lot if you need to perform many writes in a row.\n\nFortunately there is a way to improve this use case."},{"id":"redis-pipelining","title":"Redis Pipelining","role":"content","text":"A Request/Response server can be implemented so that it is able to process new requests even if the client hasn't already read the old responses.\nThis way it is possible to send *multiple commands* to the server without waiting for the replies at all, and finally read the replies in a single step.\n\nThis is called pipelining, and is a technique widely in use for many decades.\nFor instance many POP3 protocol implementations already support this feature, dramatically speeding up the process of downloading new emails from the server.\n\nRedis has supported pipelining since its early days, so whatever version you are running, you can use pipelining with Redis.\nThis is an example using the raw netcat utility:\n\n[code example]\n\nThis time we don't pay the cost of RTT for every call, but just once for the three commands.\n\nTo be explicit, with pipelining the order of operations of our very first example will be the following:\n\n * *Client:* INCR X\n * *Client:* INCR X\n * *Client:* INCR X\n * *Client:* INCR X\n * *Server:* 1\n * *Server:* 2\n * *Server:* 3\n * *Server:* 4\n\n> **IMPORTANT NOTE**: While the client sends commands using pipelining, the server will be forced to queue the replies, using memory. So if you need to send a lot of commands with pipelining, it is better to send them as batches each containing a reasonable number, for instance 10k commands, read the replies, and then send another 10k commands again, and so forth. The speed will be nearly the same, but the additional memory used will be at most the amount needed to queue the replies for these 10k commands."},{"id":"its-not-just-a-matter-of-rtt","title":"It's not just a matter of RTT","role":"content","text":"Pipelining is not just a way to reduce the latency cost associated with the\nround trip time, it actually greatly improves the number of operations\nyou can perform per second in a given Redis server.\nThis is because without using pipelining, serving each command is very cheap from\nthe point of view of accessing the data structures and producing the reply,\nbut it is very costly from the point of view of doing the socket I/O. This\ninvolves calling the `read()` and `write()` syscall, that means going from user\nland to kernel land.\nThe context switch is a huge speed penalty.\n\nWhen pipelining is used, many commands are usually read with a single `read()`\nsystem call, and multiple replies are delivered with a single `write()` system\ncall. Consequently, the number of total queries performed per second\ninitially increases almost linearly with longer pipelines, and eventually\nreaches 10 times the baseline obtained without pipelining, as shown in this figure.\n\n![Pipeline size and IOPs](https://redis.io/docs/latest/images/dev/reference/pipeline_iops.webp)"},{"id":"a-real-world-code-example","title":"A real world code example","role":"content","text":"In the following benchmark we'll use the Redis Ruby client, supporting pipelining, to test the speed improvement due to pipelining:\n\n[code example]\n\nRunning the above simple script yields the following figures on my Mac OS X system, running over the loopback interface, where pipelining will provide the smallest improvement as the RTT is already pretty low:\n\n[code example]\nAs you can see, using pipelining, we improved the transfer by a factor of five."},{"id":"pipelining-vs-scripting","title":"Pipelining vs Scripting","role":"content","text":"Using [Redis scripting](https://redis.io/docs/latest/commands/eval), available since Redis 2.6, a number of use cases for pipelining can be addressed more efficiently using scripts that perform a lot of the work needed at the server side.\nA big advantage of scripting is that it is able to both read and write data with minimal latency, making operations like *read, compute, write* very fast (pipelining can't help in this scenario since the client needs the reply of the read command before it can call the write command).\n\nSometimes the application may also want to send [`EVAL`](https://redis.io/docs/latest/commands/eval) or [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) commands in a pipeline. \nThis is entirely possible and Redis explicitly supports it with the [SCRIPT LOAD](https://redis.io/docs/latest/commands/script-load) command (it guarantees that [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) can be called without the risk of failing)."},{"id":"appendix-why-are-busy-loops-slow-even-on-the-loopback-interface","title":"Appendix: Why are busy loops slow even on the loopback interface?","role":"content","text":"Even with all the background covered in this page, you may still wonder why\na Redis benchmark like the following (in pseudo code), is slow even when\nexecuted in the loopback interface, when the server and the client are running\nin the same physical machine:\n\n[code example]\n\nAfter all, if both the Redis process and the benchmark are running in the same\nbox, isn't it just copying messages in memory from one place to another without\nany actual latency or networking involved?\n\nThe reason is that processes in a system are not always running, actually it is\nthe kernel scheduler that lets the process run. \nSo, for instance, when the benchmark is allowed to run, it reads the reply from the Redis server (related to the last command executed), and writes a new command.\nThe command is now in the loopback interface buffer, but in order to be read by the server, the kernel should schedule the server process (currently blocked in a system call)\nto run, and so forth.\nSo in practical terms the loopback interface still involves network-like latency, because of how the kernel scheduler works.\n\nBasically a busy loop benchmark is the silliest thing that can be done when\nmetering performances on a networked server. The wise thing is just avoiding\nbenchmarking in this way."}],"examples":[{"id":"redis-pipelining-ex0","language":"bash ","code":"$ (printf \"PING\\r\\nPING\\r\\nPING\\r\\n\"; sleep 1) | nc localhost 6379\n+PONG\n+PONG\n+PONG","section_id":"redis-pipelining"},{"id":"a-real-world-code-example-ex0","language":"ruby","code":"require 'rubygems'\nrequire 'redis'\n\ndef bench(descr)\n  start = Time.now\n  yield\n  puts \"#{descr} #{Time.now - start} seconds\"\nend\n\ndef without_pipelining\n  r = Redis.new\n  10_000.times do\n    r.ping\n  end\nend\n\ndef with_pipelining\n  r = Redis.new\n  r.pipelined do |rp|\n    10_000.times do\n      rp.ping\n    end\n  end\nend\n\nbench('without pipelining') do\n  without_pipelining\nend\nbench('with pipelining') do\n  with_pipelining\nend","section_id":"a-real-world-code-example"},{"id":"a-real-world-code-example-ex1","language":"plaintext","code":"without pipelining 1.185238 seconds\nwith pipelining 0.250783 seconds","section_id":"a-real-world-code-example"},{"id":"appendix-why-are-busy-loops-slow-even-on-the-loopback-interface-ex0","language":"sh","code":"FOR-ONE-SECOND:\n    Redis.SET(\"foo\",\"bar\")\nEND","section_id":"appendix-why-are-busy-loops-slow-even-on-the-loopback-interface"}]}
{"schema_version":2,"id":"develop/using-commands/transactions","title":"Transactions","url":"https://redis.io/docs/latest/develop/using-commands/transactions/","summary":"How transactions work in Redis","aliases":["/develop/interact/transactions","/manual/transactions/","/interact/transactions/","/develop/reference/transactions/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-11T14:32:43+01:00","page_type":"content","content_hash":"5572bc3253a4a85cdaa175cc4de7fa30878e08ee339225cdefaf8fe7726a8ec9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Transactions allow the execution of a group of commands\nin a single step, they are centered around the commands \n[`MULTI`](https://redis.io/docs/latest/commands/multi), [`EXEC`](https://redis.io/docs/latest/commands/exec), [`DISCARD`](https://redis.io/docs/latest/commands/discard) and [`WATCH`](https://redis.io/docs/latest/commands/watch).\nRedis Transactions make two important guarantees:\n\n* All the commands in a transaction are serialized and executed\nsequentially. A request sent by another client will never be\nserved **in the middle** of the execution of a Redis Transaction.\nThis guarantees that the commands are executed as a single\nisolated operation.\n\n* The [`EXEC`](https://redis.io/docs/latest/commands/exec) command\ntriggers the execution of all the commands in the transaction, so\nif a client loses the connection to the server in the context of a\ntransaction before calling the [`EXEC`](https://redis.io/docs/latest/commands/exec) command none of the operations\nare performed, instead if the [`EXEC`](https://redis.io/docs/latest/commands/exec) command is called, all the\noperations are performed. When using the\n[append-only file](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence#append-only-file) Redis makes sure\nto use a single write(2) syscall to write the transaction on disk.\nHowever if the Redis server crashes or is killed by the system administrator\nin some hard way it is possible that only a partial number of operations\nare registered. Redis will detect this condition at restart, and will exit with an error.\nUsing the `redis-check-aof` tool it is possible to fix the\nappend only file that will remove the partial transaction so that the\nserver can start again.\n\nStarting with version 2.2, Redis allows for an extra guarantee to the\nabove two, in the form of optimistic locking in a way very similar to a\ncheck-and-set (CAS) operation.\nThis is documented [later](#cas) on this page."},{"id":"usage","title":"Usage","role":"syntax","text":"A Redis Transaction is entered using the [`MULTI`](https://redis.io/docs/latest/commands/multi) command. The command\nalways replies with `OK`. At this point the user can issue multiple\ncommands. Instead of executing these commands, Redis will queue\nthem. All the commands are executed once [`EXEC`](https://redis.io/docs/latest/commands/exec) is called.\n\nCalling [`DISCARD`](https://redis.io/docs/latest/commands/discard) instead will flush the transaction queue and will exit\nthe transaction.\n\nThe following example increments keys `foo` and `bar` atomically.\n\n[code example]\n\nAs is clear from the session above, [`EXEC`](https://redis.io/docs/latest/commands/exec) returns an\narray of replies, where every element is the reply of a single command\nin the transaction, in the same order the commands were issued.\n\nWhen a Redis connection is in the context of a [`MULTI`](https://redis.io/docs/latest/commands/multi) request,\nall commands will reply with the string `QUEUED` (sent as a Status Reply\nfrom the point of view of the Redis protocol). A queued command is\nsimply scheduled for execution when [`EXEC`](https://redis.io/docs/latest/commands/exec) is called."},{"id":"errors-inside-a-transaction","title":"Errors inside a transaction","role":"errors","text":"During a transaction it is possible to encounter two kind of command errors:\n\n* A command may fail to be queued, so there may be an error before [`EXEC`](https://redis.io/docs/latest/commands/exec) is called.\nFor instance the command may be syntactically wrong (wrong number of arguments,\nwrong command name, ...), or there may be some critical condition like an out of\nmemory condition (if the server is configured to have a memory limit using the `maxmemory` directive).\n* A command may fail *after* [`EXEC`](https://redis.io/docs/latest/commands/exec) is called, for instance since we performed\nan operation against a key with the wrong value (like calling a list operation against a string value).\n\nStarting with Redis 2.6.5, the server will detect an error during the accumulation of commands.\nIt will then refuse to execute the transaction returning an error during [`EXEC`](https://redis.io/docs/latest/commands/exec), discarding the transaction.\n\n> **Note for Redis < 2.6.5:** Prior to Redis 2.6.5 clients needed to detect errors occurring prior to [`EXEC`](https://redis.io/docs/latest/commands/exec) by checking\nthe return value of the queued command: if the command replies with QUEUED it was\nqueued correctly, otherwise Redis returns an error.\nIf there is an error while queueing a command, most clients\nwill abort and discard the transaction. Otherwise, if the client elected to proceed with the transaction\nthe [`EXEC`](https://redis.io/docs/latest/commands/exec) command would execute all commands queued successfully regardless of previous errors.\n\nErrors happening *after* [`EXEC`](https://redis.io/docs/latest/commands/exec) instead are not handled in a special way:\nall the other commands will be executed even if some command fails during the transaction.\n\nThis is more clear on the protocol level. In the following example one\ncommand will fail when executed even if the syntax is right:\n\n[code example]\n\n[`EXEC`](https://redis.io/docs/latest/commands/exec) returned two-element [bulk string reply](https://redis.io/docs/latest/develop/reference/protocol-spec#bulk-string-reply) where one is an `OK` code and\nthe other an error reply. It's up to the client library to find a\nsensible way to provide the error to the user.\n\nIt's important to note that\n**even when a command fails, all the other commands in the queue are processed** – Redis will _not_ stop the\nprocessing of commands.\n\nAnother example, again using the wire protocol with `telnet`, shows how\nsyntax errors are reported ASAP instead:\n\n[code example]\n\nThis time due to the syntax error the bad [`INCR`](https://redis.io/docs/latest/commands/incr) command is not queued\nat all."},{"id":"what-about-rollbacks","title":"What about rollbacks?","role":"content","text":"Redis does not support rollbacks of transactions since supporting rollbacks\nwould have a significant impact on the simplicity and performance of Redis."},{"id":"discarding-the-command-queue","title":"Discarding the command queue","role":"content","text":"[`DISCARD`](https://redis.io/docs/latest/commands/discard) can be used in order to abort a transaction. In this case, no\ncommands are executed and the state of the connection is restored to\nnormal.\n\n[code example]\n<a name=\"cas\"></a>"},{"id":"optimistic-locking-using-check-and-set","title":"Optimistic locking using check-and-set","role":"content","text":"[`WATCH`](https://redis.io/docs/latest/commands/watch) is used to provide a check-and-set (CAS) behavior to Redis\ntransactions.\n\n[`WATCH`](https://redis.io/docs/latest/commands/watch)ed keys are monitored in order to detect changes against them. If\nat least one watched key is modified before the [`EXEC`](https://redis.io/docs/latest/commands/exec) command, the\nwhole transaction aborts, and [`EXEC`](https://redis.io/docs/latest/commands/exec) returns a [Null reply](https://redis.io/docs/latest/develop/reference/protocol-spec#nil-reply) to notify that\nthe transaction failed.\n\nFor example, imagine we have the need to atomically increment the value\nof a key by 1 (let's suppose Redis doesn't have [`INCR`](https://redis.io/docs/latest/commands/incr)).\n\nThe first try may be the following:\n\n[code example]\n\nThis will work reliably only if we have a single client performing the\noperation in a given time. If multiple clients try to increment the key\nat about the same time there will be a race condition. For instance,\nclient A and B will read the old value, for instance, 10. The value will\nbe incremented to 11 by both the clients, and finally [`SET`](https://redis.io/docs/latest/commands/set) as the value\nof the key. So the final value will be 11 instead of 12.\n\nThanks to [`WATCH`](https://redis.io/docs/latest/commands/watch) we are able to model the problem very well:\n\n[code example]\n\nUsing the above code, if there are race conditions and another client\nmodifies the result of `val` in the time between our call to [`WATCH`](https://redis.io/docs/latest/commands/watch) and\nour call to [`EXEC`](https://redis.io/docs/latest/commands/exec), the transaction will fail.\n\nWe just have to repeat the operation hoping this time we'll not get a\nnew race. This form of locking is called _optimistic locking_.\nIn many use cases, multiple clients will be accessing different keys,\nso collisions are unlikely – usually there's no need to repeat the operation.\n\nStarting with version 8.4, Redis offers new atomic compare-and-set and compare-and-delete commands for string keys.\nA client can use a single ([`SET`](https://redis.io/docs/latest/commands/set) command with the `IFEQ`/`IFNE`/`IFDEQ`/`IFDNE` options to atomically update a string key only if its value has not changed since it was fetched.\nThis is much simpler and faster.\nSimilarly, a client can use a single [`DELEX`](https://redis.io/docs/latest/commands/delex) command for compare-and-delete: atomically deleting a string key if its value hasn't changed since it was fetched."},{"id":"watch-explained","title":"WATCH explained","role":"content","text":"So what is [`WATCH`](https://redis.io/docs/latest/commands/watch) really about? It is a command that will\nmake the [`EXEC`](https://redis.io/docs/latest/commands/exec) conditional: we are asking Redis to perform\nthe transaction only if none of the [`WATCH`](https://redis.io/docs/latest/commands/watch)ed keys were modified. This includes\nmodifications made by the client, like write commands, and by Redis itself,\nlike expiration or eviction. If keys were modified between when they were\n[`WATCH`](https://redis.io/docs/latest/commands/watch)ed and when the [`EXEC`](https://redis.io/docs/latest/commands/exec) was received, the entire transaction will be aborted\ninstead.\n\n**NOTE**\n* In Redis versions before 6.0.9, an expired key would not cause a transaction\nto be aborted. [More on this](https://github.com/redis/redis/pull/7920)\n* Commands within a transaction won't trigger the [`WATCH`](https://redis.io/docs/latest/commands/watch) condition since they\nare only queued until the [`EXEC`](https://redis.io/docs/latest/commands/exec) is sent.\n\n[`WATCH`](https://redis.io/docs/latest/commands/watch) can be called multiple times. Simply all the [`WATCH`](https://redis.io/docs/latest/commands/watch) calls will\nhave the effects to watch for changes starting from the call, up to\nthe moment [`EXEC`](https://redis.io/docs/latest/commands/exec) is called. You can also send any number of keys to a\nsingle [`WATCH`](https://redis.io/docs/latest/commands/watch) call.\n\nWhen [`EXEC`](https://redis.io/docs/latest/commands/exec) is called, all keys are [`UNWATCH`](https://redis.io/docs/latest/commands/unwatch)ed, regardless of whether\nthe transaction was aborted or not.  Also when a client connection is\nclosed, everything gets [`UNWATCH`](https://redis.io/docs/latest/commands/unwatch)ed.\n\nIt is also possible to use the [`UNWATCH`](https://redis.io/docs/latest/commands/unwatch) command (without arguments)\nin order to flush all the watched keys. Sometimes this is useful as we\noptimistically lock a few keys, since possibly we need to perform a\ntransaction to alter those keys, but after reading the current content\nof the keys we don't want to proceed.  When this happens we just call\n[`UNWATCH`](https://redis.io/docs/latest/commands/unwatch) so that the connection can already be used freely for new\ntransactions."},{"id":"using-watch-to-implement-zpop","title":"Using WATCH to implement ZPOP","role":"content","text":"A good example to illustrate how [`WATCH`](https://redis.io/docs/latest/commands/watch) can be used to create new\natomic operations otherwise not supported by Redis is to implement ZPOP\n([`ZPOPMIN`](https://redis.io/docs/latest/commands/zpopmin), [`ZPOPMAX`](https://redis.io/docs/latest/commands/zpopmax) and their blocking variants have only been added\nin version 5.0), that is a command that pops the element with the lower\nscore from a sorted set in an atomic way. This is the simplest\nimplementation:\n\n[code example]\n\nIf [`EXEC`](https://redis.io/docs/latest/commands/exec) fails (i.e. returns a [Null reply](https://redis.io/docs/latest/develop/reference/protocol-spec#nil-reply)) we just repeat the operation."},{"id":"redis-scripting-and-transactions","title":"Redis scripting and transactions","role":"content","text":"Something else to consider for transaction like operations in redis are\n[redis scripts](https://redis.io/docs/latest/commands/eval) which are transactional. Everything\nyou can do with a Redis Transaction, you can also do with a script, and\nusually the script will be both simpler and faster."}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"> MULTI\nOK\n> INCR foo\nQUEUED\n> INCR bar\nQUEUED\n> EXEC\n1) (integer) 1\n2) (integer) 1","section_id":"usage"},{"id":"errors-inside-a-transaction-ex0","language":"plaintext","code":"Trying 127.0.0.1...\nConnected to localhost.\nEscape character is '^]'.\nMULTI\n+OK\nSET a abc\n+QUEUED\nLPOP a\n+QUEUED\nEXEC\n*2\n+OK\n-WRONGTYPE Operation against a key holding the wrong kind of value","section_id":"errors-inside-a-transaction"},{"id":"errors-inside-a-transaction-ex1","language":"plaintext","code":"MULTI\n+OK\nINCR a b c\n-ERR wrong number of arguments for 'incr' command","section_id":"errors-inside-a-transaction"},{"id":"discarding-the-command-queue-ex0","language":"plaintext","code":"> SET foo 1\nOK\n> MULTI\nOK\n> INCR foo\nQUEUED\n> DISCARD\nOK\n> GET foo\n\"1\"","section_id":"discarding-the-command-queue"},{"id":"optimistic-locking-using-check-and-set-ex0","language":"plaintext","code":"val = GET mykey\nval = val + 1\nSET mykey $val","section_id":"optimistic-locking-using-check-and-set"},{"id":"optimistic-locking-using-check-and-set-ex1","language":"plaintext","code":"WATCH mykey\nval = GET mykey\nval = val + 1\nMULTI\nSET mykey $val\nEXEC","section_id":"optimistic-locking-using-check-and-set"},{"id":"using-watch-to-implement-zpop-ex0","language":"plaintext","code":"WATCH zset\nelement = ZRANGE zset 0 0\nMULTI\nZREM zset element\nEXEC","section_id":"using-watch-to-implement-zpop"}]}
{"schema_version":2,"id":"develop/whats-new/6-2","title":"Redis 6.2","url":"https://redis.io/docs/latest/develop/whats-new/6-2/","summary":"What's new in Redis 6.2","tags":["docs","operate","rs","rc"],"last_updated":"2026-03-09T06:38:54-07:00","page_type":"content","content_hash":"861787f4a724af09cced7643133f27bde7c3ce65b2af9615164bb56a19bf38ab","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis version 6.2 introduces new capabilities designed to improve data indexing, querying, and analytics. This update brings multi-value indexing, expanded wildcard query support, and a new probabilistic data structure for quantile estimation. Additionally, significant enhancements to Redis streams and time series data processing offer greater flexibility for developers working with real-time and historical datasets. Over 25 new commands have been added to Redis that address key feature requests and further extending its capabilities.\nBelow is a detailed breakdown of these improvements."},{"id":"new-features","title":"New features","role":"content","text":""},{"id":"multi-value-indexing-and-querying","title":"Multi-value indexing and querying","role":"content","text":"Redis now supports indexing and querying multi-value attributes across all field types, including `TEXT`, `TAG`, `NUMERIC`, `GEO`, and `VECTOR`. Developers can define JSONPath expressions leading to arrays or multiple scalar values, overcoming the previous limitation of indexing only single scalar attributes."},{"id":"wildcard-query-support","title":"Wildcard query support","role":"content","text":"Redis Search now enables suffix and infix wildcard searches for `TEXT` and `TAG` fields. This enhancement provides greater flexibility in data retrieval and filtering."},{"id":"t-digest-a-new-probabilistic-data-structure-for-quantile-estimation","title":"t-digest: a new probabilistic data structure for quantile estimation","role":"content","text":"Redis introduces t-digest, an advanced probabilistic data structure that efficiently estimates quantiles in large datasets or continuous data streams. This is particularly beneficial for analytics and monitoring applications where quantile calculations are required."},{"id":"retrieve-aggregation-results-for-ongoing-time-series-buckets","title":"Retrieve aggregation results for ongoing time series buckets","role":"content","text":"A new feature allows users to retrieve the latest, still-open time series buckets during compaction."},{"id":"time-weighted-average-aggregator-for-time-series","title":"Time-weighted average aggregator for time series","role":"content","text":"Redis now includes a time-weighted average aggregator, improving accuracy in average-over-time calculations. This feature is especially valuable for time series data with irregular sampling intervals."},{"id":"gap-filling-for-time-series-data","title":"Gap-filling for time series data","role":"content","text":"To improve time series analytics, Redis introduces gap-filling capabilities. This feature allows interpolation of missing values or repetition of the last known value for empty time buckets, ensuring continuity in time series analysis."},{"id":"improvements","title":"Improvements","role":"content","text":""},{"id":"existing-data-structures","title":"Existing data structures","role":"content","text":"Redis 6.2 introduces over 25 new commands, fulfilling long-standing community requests. Notably:\n\n- The long-awaited `ZUNION` and `ZINTER` commands now allow direct retrieval of results, unlike `ZUNIONSTORE` and `ZINTERSTORE`, which store results in a key.\n- Redis streams enhancements include:\n  - Support for exclusive range queries, providing finer control over data retrieval.\n  - The ability to filter pending messages based on idle time, improving message management.\n  - A new mechanism to automatically claim pending messages from a stream consumer group, transferring ownership of messages that have exceeded their idle timeout to a new consumer without requiring manual acknowledgment."},{"id":"component-versions","title":"Component versions","role":"content","text":"The Redis version 6.2 is built from the following component versions:\n\n- [Redis 6.2](https://github.com/redis/redis/blob/6.2/00-RELEASENOTES)\n- [Search 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes/)\n- [JSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes/)\n- [Time series 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes/)\n- [Bloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes/)\n- [Graph 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes/)"}],"examples":[]}
{"schema_version":2,"id":"develop/whats-new/7-2","title":"Redis 7.2","url":"https://redis.io/docs/latest/develop/whats-new/7-2/","summary":"What's new in Redis 7.2","tags":["docs","operate","rs","rcw"],"last_updated":"2026-08-26T15:09:52+01:00","page_type":"content","content_hash":"2861de1d50be695a6817d9351eb26d800f7a20691f588ff5f04ff5f60d9f581e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis version 7.2 introduces new capabilities, including improved geospatial queries, and streamlined JSON data manipulation. Performance optimizations, client-side enhancements, and behavioral refinements further improve the efficiency, security, and usability of Redis.\nBelow is a detailed breakdown of these updates."},{"id":"new-features","title":"New features","role":"content","text":""},{"id":"geospatial-queries-with-polygon-search","title":"Geospatial queries with polygon search","role":"content","text":"Redis Search now supports querying geospatial data using polygon search, enabling developers to efficiently filter and retrieve data within complex geographic boundaries."},{"id":"streamlined-data-manipulation-in-json","title":"Streamlined data manipulation in JSON","role":"content","text":"JSON now includes two new commands for improved data handling:\n\n- `JSON.MERGE`: Merges a given JSON value into matching paths, allowing more flexible updates.\n- `JSON.MSET`: Sets or updates multiple JSON values simultaneously based on specified key-path-value triplets, improving efficiency when handling structured data."},{"id":"improvements","title":"Improvements","role":"content","text":""},{"id":"existing-data-structures","title":"Existing data structures","role":"content","text":"Significant performance improvements have been made across Redis data types. Sorted sets, commonly used for gaming leaderboards, now see performance improvements ranging from [30% to 100%](https://redis.io/blog/introducing-redis-7-2/).\n\nAdditionally, Redis stream consumer tracking has been enhanced to provide better visibility into consumer activity, and blocked stream commands now return a distinct error when the target key no longer exists."},{"id":"redis-search-improvements","title":"Redis Search improvements","role":"content","text":"Redis Search has received several updates, including optimized `SORT BY` operations and the addition of a new `FORMAT` response in RESP3, improving both efficiency and readability."},{"id":"script-execution-enhancemets","title":"Script execution enhancemets","role":"content","text":"Client-side tracking now monitors actual keys read during script execution, improving key usage tracking accuracy. Additionally, blocked commands will re-evaluate security checks before execution, ensuring compliance with updated permissions. Standardized ACL failure messages and error codes now provide clearer error handling."},{"id":"client-and-replication-enhancements","title":"Client and replication enhancements","role":"content","text":"TLS-based replication now supports Server Name Indication (SNI) to improve compatibility with secure deployments. The `HELLO` command behavior has also been refined to modify client state only upon successful execution, ensuring more predictable client behavior."},{"id":"changes","title":"Changes","role":"content","text":""},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"Redis 7.2 introduces several backward-incompatible changes. Lua scripts no longer support the `print()` function, blocking of `PFCOUNT` and `PUBLISH` in read-only scripts, and time sampling freezing during command execution. Error handling updates include case changes in error responses, new behavior for `ZPOPMIN/ZPOPMAX` with `count 0`, and adjustments to `XCLAIM/XAUTOCLAIM`. ACL changes affect command categorization and key access permissions, while command introspection now includes per-subcommand statistics. Redis now allows certain `CONFIG` commands during loading and tracks statistics only when commands are executed."},{"id":"expired-keys-are-now-deleted-from-replica-indexes","title":"Expired keys are now deleted from replica indexes","role":"content","text":"Expired keys are now deleted from Redis Search replica indexes, ensuring that queries return an empty array rather than `nil` when the data no longer exists."},{"id":"other-changes","title":"Other changes","role":"content","text":"Redis Stack 7.2 no longer includes Graph capabilities. For more details, refer to the [RedisGraph End-of-Life Announcement](https://redis.io/blog/redisgraph-eol/)."},{"id":"component-versions","title":"Component versions","role":"content","text":"The Redis version 7.2 includes the following components:\n\n- [Redis 7.2](https://github.com/redis/redis/blob/7.2/00-RELEASENOTES)\n- [Search 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes/)\n- [JSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes/)\n- [Time series 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes/)\n- [Bloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes/)"}],"examples":[]}
{"schema_version":2,"id":"develop/whats-new/7-4","title":"Redis 7.4","url":"https://redis.io/docs/latest/develop/whats-new/7-4/","summary":"What's new in Redis 7.4","tags":["docs","operate","rs","rc"],"last_updated":"2026-03-09T06:38:54-07:00","page_type":"content","content_hash":"ad205b8c87ad4c6392a6d1c894bed56c1c89cb67025ad5c0d959951f6455e79a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis 7.4 introduces several new features and improvements aimed at enhancing memory efficiency, performance, and ease of use for various applications. These updates include support for hash field expiration, new memory-efficient data types for AI workloads, simplified secondary indexing, and time series optimizations. Additionally, Redis 7.4 brings several behavior and component changes.\nBelow is a detailed breakdown of these updates."},{"id":"new-features","title":"New features","role":"content","text":""},{"id":"hash-field-expiration-support","title":"Hash field expiration support","role":"content","text":"Redis 7.4 adds the ability to set expiration times for individual hash fields or adjust their remaining TTL. This feature, long-requested by users, improves memory efficiency and performance, especially in caching and session storage scenarios."},{"id":"new-memory-efficient-data-types-for-ai-workloads","title":"New memory-efficient data types for AI workloads","role":"content","text":"With the growing demand for AI applications, Redis 7.4 introduces `BFLOAT16` and `FLOAT16` data types. These new types reduce memory usage by up to 47% and lower latency by as much as 59% under load, making them ideal for storing and processing vector embeddings in AI-powered applications, including vector databases and Retrieval Augmented Generation (RAG) systems."},{"id":"time-series-optimization-with-insertion-filters","title":"Time series optimization with insertion filters","role":"content","text":"Redis 7.4 introduces insertion filters for time series data, allowing sensors to ignore new measurements when the differences in time or value are minimal. This feature helps reduce the size of time series data and boosts efficiency."},{"id":"improvements","title":"Improvements","role":"content","text":""},{"id":"simplified-secondary-indexing","title":"Simplified secondary indexing","role":"content","text":"Redis Search now offers a more straightforward approach to secondary indexing with the addition of the `TAG` index type. Querying tags with special characters (like `@` and `.`) is easier, as it no longer requires escaping; simply wrap query terms in double quotes. The update also includes improved handling of empty and missing fields, making the data model more flexible. Geospatial search has been enhanced with new operators, such as `INTERSECT` and `DISJOIN`, and memory usage reporting for indexes has been improved."},{"id":"changes","title":"Changes","role":"content","text":""},{"id":"behavior-changes","title":"Behavior changes","role":"content","text":"Redis 7.4 includes behavior changes such as using jemalloc instead of libc for allocating Lua VM code. This adjustment reduces memory fragmentation and improves performance. Additionally, the `ACL LOAD` command has been modified to ensure that only clients with affected user configurations are disconnected, reducing unnecessary disruptions."},{"id":"component-versions","title":"Component versions","role":"content","text":"The Redis version 7.4 includes the following components:\n\n- [Redis 7.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisce-7.4-release-notes/)\n- [Search 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes/)\n- [JSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes/)\n- [Time series 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes/)\n- [Bloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes/)"}],"examples":[]}
{"schema_version":2,"id":"develop/whats-new/8-0","title":"Redis 8.0","url":"https://redis.io/docs/latest/develop/whats-new/8-0/","summary":"What's new in Redis 8.0","aliases":["/develop/whats-new/8-0-rc-1/"],"tags":["docs","operate","rs","rc"],"last_updated":"2026-07-30T08:54:30-07:00","page_type":"content","content_hash":"9119b2bb05eb8671f23f3accd74bf856152483a96a275781b8d0ebbd4c228256","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis 8.0 introduces powerful new capabilities, including the beta release of the Vector Set data structure, designed for AI use cases such as semantic search and recommendation systems.\n\nThis release also delivers over 30 performance improvements, including:\n- Up to 87% faster command execution\n- Up to 18% faster replication\n\nAdditionally, the Redis Community Edition has been renamed to Redis Open Source, marking a shift toward a unified, modern distribution. This version offers the full power of Redis — no modules, no fragmentation — just Redis.\n\nBelow is a detailed breakdown of these updates."},{"id":"new-features","title":"New features","role":"content","text":""},{"id":"vector-set-beta","title":"Vector set [beta]","role":"content","text":"The new `Vector set` data structure supports high-dimensional vector similarity search, which is ideal for AI use cases such as semantic search and recommendation systems. Vector set complements Redis’s existing vector search capabilities via Redis Search and is currently available in beta. APIs and behaviors may change in future releases."},{"id":"new-hash-commands","title":"New hash commands","role":"content","text":"Redis 8 builds on Redis 7.4’s introduction of field-level expiration in hashes and adds three new commands for working with hashes:\n- [HGETEX](https://redis.io/docs/latest/commands/hgetex/): Fetch a hash field and optionally set an expiration\n- [HSETEX](https://redis.io/docs/latest/commands/hsetex/): Set a hash field and optionally set an expiration\n- [HGETDEL](https://redis.io/docs/latest/commands/hgetdel/): Fetch and delete a hash field\n\nThese commands simplify common caching and session management usage patterns."},{"id":"improvements","title":"Improvements","role":"content","text":""},{"id":"redis-search-improvements","title":"Redis Search Improvements","role":"content","text":"Redis Search allows users to use Redis as a document database, a vector database, a secondary index, and a search engine. With Redis Search, users can define indexes for hash and JSON documents, and use a rich query language for vector search, full-text search, geospatial queries, and aggregations.\n\nUse cases include:\n\n- Vector Search: Search based on semantic similarity using vector embeddings stored in `JSON` or `hashes`\n- Exact Matching: Lookups using filters, tags, and ranges\n- Full-Text Search: Supports stemming, synonyms, and fuzzy matching\n\nRedis Search powers a wide range of applications, from AI retrieval-augmented generation (RAG) systems to full-featured search engines."},{"id":"access-control-improvements","title":"Access Control Improvements","role":"content","text":"Access Control Lists (ACLs) have been updated to support the new data structures introduced in Redis 8. Existing ACL categories such as `@read` and `@write` now include commands for `JSON`, `time series`, `VECTOR`, and `probabilistic` data structures.\n\nThese updates allow for more precise control over which operations users can perform on each data structure."},{"id":"performance-improvements","title":"Performance improvements","role":"performance","text":"Redis 8 delivers the largest performance leap in Redis history with over 30 optimizations, including:\n- Up to 87% lower command latency\n- 35% memory savings for replica nodes\n- 16x more query processing capacity with horizontal and vertical scaling\n\nThese improvements benefit both single-node and clustered deployments. More details are available in the [Redis 8 GA](https://redis.io/blog/redis-8-ga/) blog post."},{"id":"changes","title":"Changes","role":"content","text":""},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"#### ACL behavior\nCommands from included modules are now covered under standard categories (e.g., `+@read`, `+@write`). For example, a user with `+@all` `-@write` will no longer be able to execute `JSON.SET` as they could before.\nExplicit inclusion of new command categories is required to maintain access.\n\n#### Redis Search\n\nThe following changes affect behavior and validation in Redis Search:\n- Enforces validation for `LIMIT` arguments (offset must be 0 if limit is 0).\n- Enforces parsing rules for `FT.CURSOR READ` and `FT.ALIASADD`.\n- Parentheses are now required for exponentiation precedence in `APPLY` expressions.\n- Invalid input now returns errors instead of empty results.\n- Default values revisited for reducers like `AVG`, `COUNT`, `SUM`, `STDDEV`, `QUANTILE`, and others.\n- Updates to scoring (`BM25` is now the default instead of `TF-IDF`).\n- Improved handling of expired records, memory constraints, and malformed fields.\n\nFor a full list of Redis Search-related changes, see the [release notes](https://github.com/redis/redis/releases)."},{"id":"other-changes","title":"Other changes","role":"content","text":"#### One Redis\nOver time, Redis introduced several modules to address new use cases such as search, vector similarity, time series analysis, and probabilistic modeling. While these modules extended Redis’s functionality, managing version compatibility and module installation introduced friction.\n\nRedis Stack partially solved this by bundling modules together, but it also created fragmentation across the ecosystem.\n\nRedis 8 merges Redis Stack and Redis Community Edition into a single unified distribution: Redis Open Source. All previously modular functionality is now built into the Redis Open Source package, eliminating the need to manage separate modules. You now get a single, consistent feature set across all deployments."},{"id":"component-versions","title":"Component versions","role":"content","text":"Redis 8.0 delivers just Redis — no modules, no fragmentation."}],"examples":[]}
{"schema_version":2,"id":"develop/whats-new/8-10","title":"Redis 8.10","url":"https://redis.io/docs/latest/develop/whats-new/8-10/","summary":"What's new in Redis 8.10","tags":["docs","operate","rs","rc"],"last_updated":"2026-09-11T15:36:02+01:00","page_type":"content","content_hash":"fb4a0d4931a164efaf35b43cf1f0c9155de38de37896e3ab60bb27d25f5a5b76","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis 8.10 introduces new features and performance improvements, building on the foundation of Redis 8.8.\n\nThis release delivers improvements across multiple areas:\n- Compact hashes: a new hash encoding that reduces memory usage, plus the `HIMPORT` bulk-insertion command\n- `BACKUP`: node-side backup and restore based on multi-part AOF (MP-AOF)\n- `LMOVEM`, `BLMOVEM`: move multiple elements between lists\n- `SUNIONCARD`, `SDIFFCARD`: get the cardinality of the union or difference of sets\n- `XREAD`, `XREADGROUP`: new `MAXCOUNT` and `MAXSIZE` arguments to cap the cumulative reply\n- New `SCRIPT_RUNNER` command flag for commands that execute scripts or functions\n- `FT.ALIASLIST`: get all aliases for a Search index\n- `FT.AGGREGATE`: new `COLLECT` reducer, plus stricter query `TIMEOUT` enforcement\n- Extensive JSONPath extensions for JSON\n- New Time Series commands: `TS.NRANGE`, `TS.NREVRANGE`, `TS.READ`, `TS.QUERYLABELS`\n- Performance and resource utilization improvements across core, Search, and JSON\n- Numerous bug fixes across core, Search, JSON, Time Series, and Probabilistic\n- New Modules API events and functions\n- New standalone redis-cli installation support\n\nBelow is a detailed breakdown of these updates."},{"id":"supported-operating-systems","title":"Supported operating systems","role":"compatibility","text":"Redis 8.10 is tested on:\n\n- Ubuntu 22.04 (Jammy Jellyfish), 24.04 (Noble Numbat), 26.04 (Resolute Raccoon)\n- Rocky Linux 8.10, 9.7, 10.1\n- AlmaLinux 8.10, 9.7, 10.1\n- Debian 12.13 (Bookworm), Debian 13.4 (Trixie)\n- Alpine 3.23\n- macOS 14.8.4 (Sonoma), 15.7.4 (Sequoia), 26.3 (Tahoe) - for both Intel and ARM"},{"id":"security-fixes","title":"Security fixes","role":"security","text":"- Fixed an issue where a user could manipulate data read by a connection by injecting `\\r\\n` sequences into a Redis error reply."},{"id":"new-features","title":"New features","role":"content","text":""},{"id":"compact-hashes","title":"Compact hashes","role":"content","text":"Redis 8.10 introduces compact hashes (#15364), a new hash encoding that reduces memory usage by storing hash field names just once for keys that share a schema. The new [`HIMPORT`](https://redis.io/docs/latest/commands/himport) command (#15364) provides high-throughput bulk insertion of compact hashes."},{"id":"backup-command","title":"BACKUP command","role":"content","text":"The new [`BACKUP`](https://redis.io/docs/latest/commands/backup) command (#15441) adds a node-side implementation for backup and restore based on multi-part AOF (MP-AOF)."},{"id":"list-multi-element-moves","title":"List multi-element moves","role":"content","text":"The new [`LMOVEM`](https://redis.io/docs/latest/commands/lmovem) and [`BLMOVEM`](https://redis.io/docs/latest/commands/blmovem) commands (#15405) move multiple elements between lists in a single operation."},{"id":"set-cardinality-commands","title":"Set cardinality commands","role":"content","text":"The new [`SUNIONCARD`](https://redis.io/docs/latest/commands/sunioncard) command (#14893) returns the cardinality of the union of multiple sets, and the new [`SDIFFCARD`](https://redis.io/docs/latest/commands/sdiffcard) command (#15278) returns the cardinality of the difference between sets, without materializing the result."},{"id":"streams-reply-caps","title":"Streams reply caps","role":"content","text":"[`XREAD`](https://redis.io/docs/latest/commands/xread) and [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) accept new `MAXCOUNT` and `MAXSIZE` arguments (#15282) to cap the cumulative number of reply entries and the cumulative reply size."},{"id":"script_runner-command-flag","title":"SCRIPT_RUNNER command flag","role":"content","text":"A new `SCRIPT_RUNNER` command flag (#15337) flags commands that execute scripts or functions."},{"id":"slowlog-get-total-argument-count","title":"SLOWLOG GET total argument count","role":"content","text":"[`SLOWLOG GET`](https://redis.io/docs/latest/commands/slowlog-get) adds a new reply argument (#15347): the total argument count for the logged command."},{"id":"ftaliaslist","title":"FT.ALIASLIST","role":"content","text":"The new [`FT.ALIASLIST`](https://redis.io/docs/latest/commands/ft.aliaslist) command (#Q9626) returns all aliases for an index (RED-197340)."},{"id":"additional-stemmer-languages","title":"Additional stemmer languages","role":"content","text":"Redis Search adds stemmer support for Malay and Tagalog (#Q9052, RED-132425)."},{"id":"ftaggregate-collect-reducer","title":"FT.AGGREGATE COLLECT reducer","role":"content","text":"[`FT.AGGREGATE`](https://redis.io/docs/latest/commands/ft.aggregate) gains a new `COLLECT` reducer (#Q9291) that separates remote and local reducer invocations (RED-177887)."},{"id":"stricter-query-timeout-enforcement","title":"Stricter query timeout enforcement","role":"content","text":"[`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search), [`FT.AGGREGATE`](https://redis.io/docs/latest/commands/ft.aggregate), and [`FT.HYBRID`](https://redis.io/docs/latest/commands/ft.hybrid) now enforce the query `TIMEOUT` more strictly (#Q8169, #Q8236, #Q9234, #Q9443, #Q9861).\n\nThe `search-on-timeout` policy now offers three options:\n\n- `FAIL` — reject timed-out queries. With `search-workers > 0` (the default), the timeout is enforced preemptively.\n- `RETURN` (default) — return best-effort partial results, without enforcing strictness during post-processing.\n- `RETURN_STRICT` (new) — return best-effort partial results while enforcing the timeout through the post-processing (result) pipeline. Available when `search-workers > 0`.\n\nQueries executed on the main thread (that is, when `search-workers` is 0) are capped by [`search-_max-foreground-timeout-limit`](https://redis.io/docs/latest/develop/ai/search-and-query/administration/configuration#search-_max-foreground-timeout-limit) (default `60000` ms)."},{"id":"jsonpath-extensions","title":"JSONPath extensions","role":"content","text":"JSON adds extensive JSONPath extensions (#J1602, #J1603, #J1604, #J1607, #J1618; MOD-16274, MOD-16275):\n\n- Projection expressions at the top level of a JSONPath query\n- `==` and `!=` can now compare any literal, including array and object literals\n- Filter negation operator: `!`\n- `size`/`sizeof` and `empty` operators on string, array, object, and nodelist\n- `in` and `nin` operators: membership test on an array and nodelist\n- Operators on numbers: binary `-`, `+`, `*`, `/`, `%`, and unary `-` and `+`\n- Operator on object: `~`\n- `length()` function on array, object, and string\n- Functions on number: `abs()`, `ceiling()`, `floor()`\n- Functions on string: `match()`, `search()`\n- Strings concatenation with `concat()`\n- Functions on array: `first()`, `last()`, `index()`, `append()`\n- Aggregation functions on array: `min()`, `max()`, `avg()`, `sum()`, `stddev()`\n- Function on object: `keys()`\n- Function on nodelist: `count()`\n- Function on nodelist with exactly one node: `value()`\n- Relations functions on array and nodelist: `subsetof()`, `anyof()`, `noneof()`"},{"id":"time-series-range-and-query-commands","title":"Time series range and query commands","role":"content","text":"- The new [`TS.NRANGE`](https://redis.io/docs/latest/commands/ts.nrange) and [`TS.NREVRANGE`](https://redis.io/docs/latest/commands/ts.nrevrange) commands (#T2052) query a range across multiple time series and group results by timestamp (RED-149232).\n- The new [`TS.READ`](https://redis.io/docs/latest/commands/ts.read) command (#T2054) performs an optionally blocking read (RED-132421).\n- The new [`TS.QUERYLABELS`](https://redis.io/docs/latest/commands/ts.querylabels) command (#T2090) returns a list of labels and label-values (RED-132355).\n- [`TS.MRANGE`](https://redis.io/docs/latest/commands/ts.mrange) and [`TS.MREVRANGE`](https://redis.io/docs/latest/commands/ts.mrevrange) accept a new `EXCLUDEEMPTY` argument (#T2072) to exclude series with no reported samples (RED-132536)."},{"id":"bug-fixes","title":"Bug fixes","role":"content","text":"Redis 8.10 includes a broad set of bug fixes (compared to 8.8.0)."},{"id":"core-fixes","title":"Core fixes","role":"content","text":"- `SORT`, `GEORADIUS`, `GEORADIUSBYMEMBER`, `XREAD`, `XREADGROUP`: ACL permission bypass (#15478).\n- I/O thread busy looping for replica clients (#15329, #15467).\n- Duplicate KeyMeta restoration in `RESTORE`-based AOF rewrites (#15466).\n- `MEMORY USAGE` over-reports memory consumption (#15462).\n- Full sync under heavy write load (#15447).\n- Unit mismatch disables the FAST expire cycle stale trigger (#15412).\n- Crash on `VRANDMEMBER` with `LLONG_MIN` count (#15392).\n- `CONFIG SET`: crash on of TLS options in non-TLS builds (#15409).\n- ACL key-name leak in BCAST client-side caching invalidations (#15371).\n- Signed overflow in `BITFIELD` offset parsing (#15433).\n- The select-based event loop backend enqueues registered file descriptors that `select()` did not mark ready (#15446).\n- `mem_clients_normal` drift when a replica drops its cached master after a failed partial resync (#15309).\n- Crash on missing module numeric config (#15357).\n- In-progress atomic slot migration tasks keep running on `RM_ResetDataset` and `RM_RdbLoad` (#15377).\n- NULL dereference (#15391, #15356).\n- Overflow on memory unit conversions (#15390).\n- `SET` does not enforce mutually exclusive `NX`/`XX` and `IF*` options (#15291).\n- `CONFIG SET` does not reject duplicate arguments when using both primary name and alias (#15322).\n- `LSET` out-of-range 64-bit index truncation (#15407).\n- Improve RDB load robustness for streams (#15308).\n- Tighter cluster bus parsing for `PING`, `PONG`, and `MEET` packets (#15263).\n- Partial crash log on LoongArch architecture (#15247).\n- `VADD ... CAS SETATTR`: wrong attributes count (#15270).\n- Typo in `release.h` that could cause build failures."},{"id":"redis-search-fixes","title":"Redis Search fixes","role":"content","text":"- `FT.CURSOR READ`: crash after the underlying index was dropped (MOD-16703).\n- `FT.SEARCH` with `LIMIT` returns too many results in cluster mode over RESP3 (MOD-16767).\n- `FT.INFO` could report incorrect values for multi-value TAG fields after document updates (MOD-16745).\n- `FT.INFO` reports a negative `num_records` after garbage collection on indexes with an `INDEXMISSING` field (MOD-16940).\n- Vector search: crash (SIGSEGV) in FP32/L2 distance computation on x86 without AVX support (MOD-16730).\n- Range query returns incomplete results when the lower bound is an excluded empty string (MOD-15897).\n- `FT.AGGREGATE .. WITHCOUNT` coordinator leaks resources on timeout (MOD-16210).\n- Local `FT.HYBRID` ignores `ON_TIMEOUT RETURN_STRICT` and continues execution past the deadline (MOD-16492).\n- Blocked search clients trigger unnecessary query dumps, adding CPU and log overhead (MOD-16518).\n- `FT.HYBRID` supports `EXPLAINSCORE`, exposing the fusion method (`RRF` or `LINEAR`) (MOD-10044)."},{"id":"json-fixes","title":"JSON fixes","role":"content","text":"- Wrong results when evaluating paths with recursive descent (MOD-6722, MOD-14664).\n- Any literal that starts with true/false/null is interpreted as true/false/null (MOD-7266).\n- Improve RDB load robustness."},{"id":"time-series-fixes","title":"Time series fixes","role":"content","text":"- `TS.INFO`: inaccurate memory usage calculation (MOD-6409).\n- Aggregation fixes (MOD-8187, MOD-16224, MOD-15565).\n- Improve RDB load robustness.\n- Issues after cluster topology changes.\n- Error on the first cross-shard command (TLS).\n- `TS.INCRBY` with `TIMESTAMP *` replication timestamp drift."},{"id":"probabilistic-fixes","title":"Probabilistic fixes","role":"content","text":"- `CF.LOADCHUNK` partial replication (MOD-16050).\n- Improve RDB load robustness."},{"id":"performance-and-resource-utilization-improvements","title":"Performance and resource utilization improvements","role":"performance","text":"Redis 8.10 delivers a broad set of performance improvements (compared to 8.8.0)."},{"id":"core-improvements","title":"Core improvements","role":"content","text":"- Optimize `lpSeek()` by validating entries only when necessary (#15376).\n- Optimize wide `HSET`/`HMSET` on a fresh hash with a single batched listpack append (#15345).\n- Avoid recomputing allocator fragmentation twice per cron tick (#15330, RED-200844).\n- Trim excess SDS allocation in inline command parsing (#15259).\n- rax memory reduction: leaf-inlining for fixed-length-key trees, improving `XREADGROUP` performance (#15256).\n- Improve `RESTORE REPLACE` performance for new keys (#15397).\n- Optimizes an internal memory accounting (#14704)."},{"id":"redis-search-improvements","title":"Redis Search improvements","role":"content","text":"- Write operations no longer block on a read lock held throughout vector range query result collection (#Q10246, MOD-16437).\n- Vector search: reduced HNSW index memory usage with one-byte per-node locks (#Q10392, MOD-16696)."},{"id":"json-improvements","title":"JSON improvements","role":"content","text":"- JSON memory object footprint improvements (#J1617, MOD-16608)."},{"id":"modules-api","title":"Modules API","role":"content","text":"- `RedisModuleEvent_ClusterTopologyChange`: cluster topology change (#15350).\n- `REDISMODULE_SUBEVENT_FORK_CHILD_*`: allow multi-threaded modules to quiesce background work before `fork()` (#15327).\n- `REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_MIGRATE_MODULE_PROPAGATE_END`: inject commands after the ASM replication stream (#15373).\n- `RedisModule_AddPostNotificationJobForKey`: binds deferred work to a specific key from a keyspace-notification handler (#15242)."},{"id":"configuration-parameters","title":"Configuration parameters","role":"configuration","text":"Redis 8.10 introduces new configuration parameters for compact hashes (#15364):\n\n- `hash-rdb-load-min-template-entries`: minimum field count to convert a plain hash to a template during load.\n- `hash-rdb-load-max-template-entries`: maximum field count for load-time conversion.\n- `hash-rdb-load-template-disassembly-threshold`: minimum number of keys a converted template must end up with to be kept."},{"id":"metrics","title":"Metrics","role":"content","text":"Redis 8.10 adds new metrics for compact hashes (#15364):\n\n- `INFO STATS` `hash_templates`: number of distinct compact hash templates.\n- `INFO STATS` `hash_template_keys`: total number of keys backed by a compact hash template.\n- `INFO MEMORY` `used_memory_hash_templates`: total memory used by all compact hash templates.\n- `MEMORY STATS` `hash.templates`: total memory used by all compact hash templates.\n- `MEMORY USAGE <key>` reports the key's own memory plus its share of a compact hash template's cost."},{"id":"cli-tools","title":"CLI tools","role":"content","text":"- Adds `--latency-percentiles <p1,p2,...>` to `--latency` / `--latency-history` for reporting user-chosen percentiles (#15352).\n- `redis-cli --cluster rebalance`: CROSSSLOT error when using `-user` without `-a` (#15262).\n- `redis-cli --cluster reshard` and `rebalance` now move slots with server-side atomic slot migration (#15338)."}],"examples":[]}
{"schema_version":2,"id":"develop/whats-new/8-2","title":"Redis 8.2","url":"https://redis.io/docs/latest/develop/whats-new/8-2/","summary":"What's new in Redis 8.2","tags":["docs","operate","rs","rc"],"last_updated":"2026-09-11T15:36:02+01:00","page_type":"content","content_hash":"503af3f60ea90c5a131790b62180ae9351e58a41efa7a81027ac9f4748afd667","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis 8.2 builds on the foundation of Redis 8.0 with significant performance and memory optimizations, enhanced streaming capabilities, and improved cluster management tools.\n\nThis release delivers major improvements across multiple areas:\n- Enhanced Redis Streams with new commands for better consumer group management\n- New bitmap operations for advanced bit manipulation\n- Advanced cluster monitoring with per-slot usage metrics\n- Optimized vector search with new compression algorithms\n- Enhanced keyspace notifications with new event types\n- More than 15 performance and resource utilization improvements\n\nBelow is a detailed breakdown of these updates."},{"id":"security-improvements","title":"Security improvements","role":"security","text":"Redis 8.2 includes important security fixes:\n- **CVE-2025-27151**: Fixed potential stack overflow and RCE vulnerability in redis-check-aof.\n- **CVE-2025-32023**: Fixed out-of-bounds write vulnerability in HyperLogLog commands.\n- **CVE-2025-48367**: Improved connection handling to retry accepting connections even when errors occur."},{"id":"new-features","title":"New features","role":"content","text":""},{"id":"enhanced-redis-streams","title":"Enhanced Redis Streams","role":"content","text":"Redis 8.2 introduces powerful new commands that simplify consumer group management and stream lifecycle operations:\n\n- [`XDELEX`](https://redis.io/docs/latest/commands/xdelex): Delete entries from streams with advanced consumer group handling options.\n- [`XACKDEL`](https://redis.io/docs/latest/commands/xackdel): Acknowledge and delete stream entries in a single atomic operation.\n\nThese commands include `KEEPREF`, `DELREF`, and `ACKED` options that give you precise control over how consumer group references are handled during entry deletion. This simplifies common patterns where you need to clean up acknowledged entries while working with multiple consumer groups.\n\nThe [`XADD`](https://redis.io/docs/latest/commands/xadd) and [`XTRIM`](https://redis.io/docs/latest/commands/xtrim) commands have also been extended with these same options, providing consistent behavior across all commands that remove stream entries."},{"id":"cluster-monitoring-improvements","title":"Cluster monitoring improvements","role":"content","text":"The new [`CLUSTER SLOT-STATS`](https://redis.io/docs/latest/commands/cluster-slot-stats) command provides detailed per-slot usage metrics including:\n- Key count per slot\n- CPU time consumption\n- Network ingress and egress\n\nThis enhanced visibility helps you optimize cluster performance and identify hotspots in your data distribution."},{"id":"vector-search-enhancements","title":"Vector search enhancements","role":"content","text":"The `VSIM` command now supports the `IN` operator for filtering expressions, giving you more flexibility when performing vector similarity searches with complex filtering criteria."},{"id":"enhanced-bitmap-operations","title":"Enhanced bitmap operations","role":"content","text":"Redis 8.2 extends the [`BITOP`](https://redis.io/docs/latest/commands/bitop) command with new operators that provide more sophisticated bit manipulation capabilities:\n\n- **DIFF**: Perform bitwise difference operations between bitmaps.\n- **DIFF1**: Calculate the difference with optimized single-bit operations.\n- **ANDOR**: Combine AND and OR operations in a single command.\n- **ONE**: Specialized operation for single-bit scenarios.\n\nThese new operators enable more complex bitmap workflows and can simplify operations that previously required multiple commands."},{"id":"improved-keyspace-notifications","title":"Improved keyspace notifications","role":"content","text":"Redis 8.2 introduces new keyspace notification event types that provide better visibility into data changes:\n\n- **OVERWRITTEN**: Triggered when a key's value is completely overwritten.\n- **TYPE_CHANGED**: Fired when a key's data type changes.\n\nThese events help you build more responsive applications by providing granular notifications about specific types of data modifications."},{"id":"improvements","title":"Improvements","role":"content","text":"Redis 8.2 delivers substantial performance improvements across core operations."},{"id":"performance-optimizations","title":"Performance optimizations","role":"performance","text":"- **BITCOUNT**: Enhanced with prefetching optimizations for faster bit counting operations.\n- **SCAN**: Optimized to perform expiration checks only on databases with volatile keys, reducing unnecessary overhead.\n- **List operations**: `LREM`, `LPOS`, and `LINSERT` benefit from cached `string2ll` results in `quicklistCompare`.\n- **Sorted set operations**: `ZRANK` and related commands use the same caching optimizations.\n- **Key operations**: `COPY`, `RENAME`, and `RESTORE` are optimized when TTL is used.\n- **Client management**: Reduced overhead in client cron operations to avoid blocking the main thread.\n- **Vector operations**: Improved RDB loading and RESTORE speed for vector sets by storing worst link information."},{"id":"memory-efficiency-improvements","title":"Memory efficiency improvements","role":"content","text":"- **JSON data types**: Memory footprint improvements through number inlining.\n- **Memory tracking**: Reduced overhead associated with tracking malloc's usable memory.\n- **Expiry handling**: Optimized expiry checks in scan callbacks.\n- **Vector compression**: Additional compression variants for the SVS-VAMANA vector index reduce memory usage."},{"id":"vector-search-performance","title":"Vector search performance","role":"content","text":"Redis 8.2 introduces the `SHARD_K_RATIO` parameter for KNN vector queries in Redis clusters. This unstable feature allows you to favor network latency over accuracy, providing faster responses when exact precision isn't critical."},{"id":"enhanced-metrics","title":"Enhanced metrics","role":"content","text":"Redis 8.2 provides new metrics to help you monitor and troubleshoot your Redis instances.\n\n#### Memory metrics\n\n- `used_memory_peak_time`: Timestamp when `used_memory_peak` was reached\n\n#### Replication metrics\n\n- `master_current_sync_attempts`: Number of sync attempts since last disconnection\n- `master_total_sync_attempts`: Total number of sync attempts to master\n- `master_link_up_since_seconds`: Duration since the replication link was established\n- `total_disconnect_time_sec`: Cumulative time the replica has been disconnected\n\nThese metrics provide better visibility into replication health and help you identify patterns in connection stability."},{"id":"component-versions","title":"Component versions","role":"content","text":"Redis 8.2 continues the unified distribution approach introduced in Redis 8.0, delivering all functionality in a single Redis Open Source package without separate modules."}],"examples":[]}
{"schema_version":2,"id":"develop/whats-new/8-4","title":"Redis 8.4","url":"https://redis.io/docs/latest/develop/whats-new/8-4/","summary":"What's new in Redis 8.4","tags":["docs","operate","rs","rc"],"last_updated":"2026-09-11T15:36:02+01:00","page_type":"content","content_hash":"993eaac1f556a5a8e2f3527eb9d17deaa7fc16fbe6c7301a3866212245e085a1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis 8.4 builds on the foundation of Redis 8.2 with significant enhancements to cluster operations, string manipulation, and stream processing capabilities.\n\nThis release delivers major improvements across multiple areas:\n- Atomic cluster slot migration for zero-downtime operations\n- Enhanced string operations with atomic compare-and-set functionality\n- Advanced stream processing with idle entry claiming\n- Hybrid search capabilities combining multiple ranking algorithms\n- High-performance SIMD optimizations for bit operations and vector processing\n- Improved JSON handling with better memory efficiency\n\nBelow is a detailed breakdown of these updates."},{"id":"new-features","title":"New features","role":"content","text":""},{"id":"atomic-cluster-operations","title":"Atomic cluster operations","role":"content","text":"Redis 8.4 introduces the [`CLUSTER MIGRATION`](https://redis.io/docs/latest/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."},{"id":"enhanced-string-operations","title":"Enhanced string operations","role":"content","text":"Redis 8.4 extends string manipulation capabilities with powerful new commands that enable atomic compare-and-set operations:\n\n- [`DELEX`](https://redis.io/docs/latest/commands/delex): Delete a key only if its value matches a specified condition.\n- [`DIGEST`](https://redis.io/docs/latest/commands/digest): Generate cryptographic digests of key values for integrity verification.\n\nThe [`SET`](https://redis.io/docs/latest/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."},{"id":"multi-key-expiration-management","title":"Multi-key expiration management","role":"content","text":"The new [`MSETEX`](https://redis.io/docs/latest/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."},{"id":"advanced-stream-processing","title":"Advanced stream processing","role":"content","text":"Redis 8.4 enhances stream processing with the `CLAIM min-idle-time` option for [`XREADGROUP`](https://redis.io/docs/latest/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."},{"id":"hybrid-search-capabilities","title":"Hybrid search capabilities","role":"content","text":"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."},{"id":"enhanced-aof-reliability","title":"Enhanced AOF reliability","role":"content","text":"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."},{"id":"improvements","title":"Improvements","role":"content","text":"Redis 8.4 delivers substantial performance improvements through advanced SIMD optimizations and enhanced algorithms."},{"id":"performance-optimizations","title":"Performance optimizations","role":"performance","text":"- **BITCOUNT**: Enhanced with AVX2 and AVX512 popcount implementations for x86 architectures, plus Arm Neon SIMD vectorization for ARM processors.\n- **HyperLogLog**: Optimized with branchless comparisons and Arm Neon SIMD vectorization for improved cardinality estimation performance.\n- **Vector operations**: `VADD` and `VSIM` commands benefit from AVX2 and AVX512 dot product implementations for faster vector processing.\n- **Command processing**: New lookahead prefetching parses multiple commands in advance through a lookahead pipeline, reducing processing latency."},{"id":"memory-efficiency-improvements","title":"Memory efficiency improvements","role":"content","text":"- **JSON data types**: Improved memory footprint through homogeneous array optimization and short string inlining.\n- **Lua integration**: Enhanced JSON array handling with the new `decode_array_with_array_mt` configuration parameter for better memory utilization."},{"id":"search-and-indexing-enhancements","title":"Search and indexing enhancements","role":"content","text":"Redis 8.4 introduces several improvements to search functionality:\n\n- **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.\n- **OOM handling**: The `search-on-oom` parameter controls query behavior during out-of-memory conditions, with options to ignore, fail, or return partial results.\n- **Multi-threading**: The `search-io-threads` parameter allows you to configure communication threads for cluster manager coordination, improving search performance in clustered environments.\n- **Index updates**: Search indexes now support updates during atomic slot migrations, maintaining search functionality during cluster operations."},{"id":"enhanced-configuration","title":"Enhanced configuration","role":"content","text":"Redis 8.4 introduces new configuration parameters that give you greater control over system behavior:\n\n- `lookahead`: Runtime-configurable lookahead depth for command prefetching (default: 16)\n- `aof-load-corrupt-tail-max-size`: Maximum corrupted tail size for automatic AOF repair\n- `decode_array_with_array_mt`: Controls how Lua handles empty JSON arrays"},{"id":"component-versions","title":"Component versions","role":"content","text":"Redis 8.4 continues the unified distribution approach, delivering all functionality in a single Redis Open Source package without separate modules. This includes:\n\n- **RedisTimeSeries**: Enhanced with `HELP` and `COMMAND DOCS` support for time series commands\n- **RedisBloom**: Improved with `HELP` and `COMMAND DOCS` support for probabilistic commands  \n- **RediSearch**: Advanced hybrid search capabilities and improved cluster coordination"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"When using Redis 8.4, be aware of these current limitations:\n\n- 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.\n- `FT.PROFILE`, `FT.EXPLAIN`, and `FT.EXPLAINCLI` don't include `FT.HYBRID` options.\n- `FT.HYBRID` metrics aren't displayed in `FT.INFO` and `INFO` commands.\n- Several `FT.HYBRID` options (`EXPLAINSCORE`, `SHARD_K_RATIO`, `YIELD_DISTANCE_AS`, `WITHCURSOR`) are not yet available.\n- Post-filtering after the `COMBINE` step using `FILTER` is not currently supported."}],"examples":[]}
{"schema_version":2,"id":"develop/whats-new/8-6","title":"Redis 8.6","url":"https://redis.io/docs/latest/develop/whats-new/8-6/","summary":"What's new in Redis 8.6","tags":["docs","operate","rs","rc"],"last_updated":"2026-09-11T15:36:02+01:00","page_type":"content","content_hash":"e141b71c252495e627511a17fcbbeee9905a91204a9ecdd4269f6f7b1fafdd78","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis 8.6 builds on the foundation of Redis 8.4 with significant enhancements to stream reliability, memory management, and security features.\n\nThis release delivers major improvements across multiple areas:\n- Stream idempotency with at-most-once delivery guarantees\n- New eviction policies for least recently modified data\n- Hot key detection and reporting capabilities\n- TLS certificate-based automatic client authentication\n- Enhanced time series support with NaN values and new aggregators\n- Substantial memory reduction for hashes and sorted sets\n- Keys memory size histograms for better monitoring\n- More than 20 performance and resource utilization improvements\n\nBelow is a detailed breakdown of these updates."},{"id":"security-improvements","title":"Security improvements","role":"security","text":"Redis 8.6 includes important security and privacy enhancements:\n- **Privacy protection**: Personally identifiable information is now hidden from ACL logs and server logs across Redis core and modules.\n- **ACL security fix**: Fixed key-pattern bypass vulnerability in `MSETEX` command.\n- **Module security**: Addressed out-of-bounds read vulnerability when loading invalid RDB files in RedisBloom."},{"id":"new-features","title":"New features","role":"content","text":""},{"id":"stream-idempotency","title":"Stream idempotency","role":"content","text":"Redis 8.6 introduces powerful idempotency features for [Redis Streams](https://redis.io/docs/latest/develop/data-types/streams) that provide at-most-once delivery guarantees:\n\n- **IDMPAUTO argument**: Automatically generates idempotent identifiers for `XADD` operations.\n- **IDMP argument**: Allows manual specification of idempotent identifiers.\n\nThese features prevent duplicate entries in streams, making Redis Streams more reliable for critical data processing pipelines where duplicate processing could cause issues."},{"id":"enhanced-memory-management","title":"Enhanced memory management","role":"content","text":"Redis 8.6 introduces new [eviction policies](https://redis.io/docs/latest/develop/reference/eviction) that provide more granular control over memory management:\n\n- **volatile-lrm**: Evicts keys with expiration times based on least recently modified criteria.\n- **allkeys-lrm**: Evicts any keys based on least recently modified criteria.\n\nThese policies complement existing LRU (Least Recently Used) policies by focusing on modification patterns rather than access patterns, providing better control for write-heavy workloads."},{"id":"hot-key-detection","title":"Hot key detection","role":"content","text":"The new [`HOTKEYS`](https://redis.io/docs/latest/commands/hotkeys) commands enable real-time detection and reporting of frequently accessed keys. This feature helps identify performance bottlenecks and optimize data distribution in clustered environments."},{"id":"tls-certificate-based-authentication","title":"TLS certificate-based authentication","role":"content","text":"Redis 8.6 introduces automatic client authentication using TLS certificates. This feature streamlines secure connections by automatically mapping TLS certificates to Redis users, reducing the need for password-based authentication in certificate-enabled environments."},{"id":"enhanced-time-series-capabilities","title":"Enhanced time series capabilities","role":"content","text":"Redis 8.6 extends [time series](https://redis.io/docs/latest/develop/data-types/timeseries) functionality with:\n\n- **NaN value support**: Time series can now store and process Not-a-Number values for incomplete or invalid data points.\n- **New aggregators**: `COUNTNAN` and `COUNTALL` aggregators provide better analytics for datasets containing NaN values."},{"id":"memory-monitoring-and-analytics","title":"Memory monitoring and analytics","role":"content","text":"Redis 8.6 introduces advanced memory monitoring capabilities:\n\n- **Keys memory size histograms**: Collect detailed memory consumption histograms per data type to better understand memory usage patterns.\n- **Per-slot statistics**: New cluster slot statistics provide insights into resource consumption across cluster nodes."},{"id":"improvements","title":"Improvements","role":"content","text":"Redis 8.6 delivers substantial performance improvements across core operations and data structures."},{"id":"performance-optimizations","title":"Performance optimizations","role":"performance","text":"- **Reply optimization**: Copy-avoidance path reduces memory copies for bulk string replies and optimized list node allocation.\n- **Data structure improvements**: Hash and sorted set operations benefit from unified field/value and score/value structures, delivering substantial memory reduction.\n- **Iterator optimization**: Improved listpack iterator performance for hash field operations.\n- **Command optimization**: Enhanced `ZRANK`, `XREADGROUP CLAIM` performance and optimized set commands with expiration.\n- **Prefetching enhancements**: Improved prefetching logic and sizing algorithms.\n- **Hardware utilization**: ARM AArch64 systems now utilize hardware clocks by default for better performance.\n- **ACL optimization**: Optimized user ACL permission verification for faster access control.\n- **Vector operations**: Enhanced vector set operations with optimized distance calculations for Intel, AMD, and ARM architectures.\n- **System efficiency**: Reduced per-command syscalls by reusing cached time when hardware monotonic clock is available."},{"id":"replication-improvements","title":"Replication improvements","role":"content","text":"- **Diskless replication**: RDB compression is now disabled during diskless replication to improve transfer speeds.\n- **Configuration flexibility**: New `flushdb` option for `repl-diskless-load` provides better control over dataset handling during replication."},{"id":"configuration-enhancements","title":"Configuration enhancements","role":"configuration","text":"Redis 8.6 introduces new configuration parameters that provide greater control over system behavior:\n\n- **Stream idempotency**: `stream-idmp-duration` and `stream-idmp-maxsize` control default settings for idempotent stream production.\n- **TLS authentication**: `tls-auth-clients-user` enables certificate-based automatic client authentication.\n- **Memory policies**: Support for new `volatile-lrm` and `allkeys-lrm` eviction policies.\n- **Cluster monitoring**: `cluster-slot-stats-enabled` enables per-slot resource consumption statistics collection.\n- **Memory analytics**: `key-memory-histograms` enables collection of memory consumption histograms per data type."},{"id":"modules-api-enhancements","title":"Modules API enhancements","role":"content","text":"Redis 8.6 extends the modules API with new key metadata capabilities:\n\n- **RM_CreateKeyMetaClass**: Define new key-metadata classes for custom data organization.\n- **RM_ReleaseKeyMetaClass**: Release key-metadata classes when no longer needed.\n- **RM_SetKeyMeta**: Attach or update metadata values for keys under specific metadata classes.\n- **RM_GetKeyMeta**: Retrieve metadata values for keys under specific metadata classes.\n\nThese APIs enable module developers to associate custom metadata with Redis keys, opening new possibilities for advanced data management and analytics."},{"id":"enhanced-monitoring-and-metrics","title":"Enhanced monitoring and metrics","role":"content","text":"Redis 8.6 introduces new metrics that provide deeper insights into system behavior:\n\n- **Memory distribution metrics**: New metrics (`db0_distrib_lists_sizes`, `db0_distrib_sets_sizes`, `db0_distrib_hashes_sizes`, `db0_distrib_zsets_sizes`) provide detailed memory usage distribution across different data types.\n- **TLS authentication metrics**: `acl_access_denied_tls_cert` tracks failed TLS certificate-based authentication attempts for security monitoring."},{"id":"component-versions","title":"Component versions","role":"content","text":"Redis 8.6 continues the unified distribution approach, delivering all functionality in a single Redis Open Source package without separate modules. This includes:\n\n- **RedisTimeSeries**: Enhanced with NaN value support and new aggregators.\n- **RedisBloom**: Improved security with RDB loading fixes and atomic slot migration support.\n- **RedisJSON**: Better privacy protection and enhanced atomic slot migration capabilities."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"When using Redis 8.6, be aware of these current limitations:\n\n- **Redis Search**: During load rebalancing operations (such as Atomic Slot Migration), cursors may miss some results due to data movement between nodes.\n- **Stream idempotency**: Avoid using `XADD` with the new `IDMP` or `IDMPAUTO` options when using `appendonly yes` with `aof-use-rdb-preamble no` (non-default configuration). This limitation will be removed in the next patch release.\n\n- [Redis 8.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.6-release-notes)"}],"examples":[]}
{"schema_version":2,"id":"develop/whats-new/8-8","title":"Redis 8.8","url":"https://redis.io/docs/latest/develop/whats-new/8-8/","summary":"What's new in Redis 8.8","tags":["docs","operate","rs","rc"],"last_updated":"2026-09-11T15:36:02+01:00","page_type":"content","content_hash":"7c46e1eb6895051e5f7a447073b41d323d831f8a8ecc9d82465d5d6527c25027","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis 8.8 introduces new features and performance improvements, building on the foundation of Redis 8.6.\n\nThis release delivers improvements across multiple areas:\n- A new Array data structure\n- Subkey notification for hash fields (field-level notifications)\n- `INCREX`: window counter rate limiter combining `INCR`, `INCRBY`, `INCRBYFLOAT`, bounds, and expiration\n- `XNACK`: new streams command that lets consumers explicitly release pending messages\n- `ZUNION`, `ZINTER`, `ZUNIONSTORE`, `ZINTERSTORE`: new `COUNT` aggregator\n- `JSON.SET`: new `FPHA` argument to specify the FP type for homogeneous FP arrays\n- `TS.RANGE`, `TS.REVRANGE`, `TS.MRANGE`, `TS.MREVRANGE`: multiple aggregators in a single command\n- `FT.HYBRID` `KNN` clause: new argument to request fewer candidates per shard\n- `FT.PROFILE` `HYBRID`: profiling support for `FT.HYBRID`\n- New in-group sorting reducer - `COLLECT` for `FT.AGGREGATE`\n- Substantial Search and Query performance improvements (Rust iterators, devirtualized vector hot paths, AArch64 atomics)\n- HyperLogLog, `MGET`/`MSET`, and `HGETALL` performance improvements\n- Numerous security fixes and bug fixes across core, Search, JSON, Time Series, and Probabilistic\n\nBelow is a detailed breakdown of these updates."},{"id":"supported-operating-systems","title":"Supported operating systems","role":"compatibility","text":"Redis 8.8 is tested on:\n\n- Ubuntu 22.04 (Jammy Jellyfish), 24.04 (Noble Numbat), 26.04 (Resolute Raccoon)\n- Rocky Linux 8.10, 9.7, 10.1\n- AlmaLinux 8.10, 9.7, 10.1\n- Debian 12.13 (Bookworm), Debian 13.4 (Trixie)\n- Alpine 3.23\n- macOS 14.8.4 (Sonoma), 15.7.4 (Sequoia), 26.3 (Tahoe) - for both Intel and ARM"},{"id":"security-fixes","title":"Security fixes","role":"security","text":"Redis 8.8 includes important security fixes (compared to 8.8-M03):\n\n- (CVE-2026-23479) Use-After-Free in unblock client flow may lead to remote code execution.\n- (CVE-2026-25243) Invalid memory access in `RESTORE` may lead to remote code execution.\n- (CVE-2026-23631) Lua Use-After-Free may lead to remote code execution.\n- (CVE-2026-25588) Invalid memory access in `RESTORE` may lead to remote code execution (Time Series).\n- (CVE-2026-25589) Invalid memory access in `RESTORE` may lead to remote code execution (Probabilistic)."},{"id":"new-features","title":"New features","role":"content","text":""},{"id":"array-data-structure","title":"Array data structure","role":"content","text":"Redis 8.8 introduces a new Array data structure (#15162), providing a dedicated data type for ordered, indexable collections."},{"id":"hash-field-level-notifications","title":"Hash field-level notifications","role":"content","text":"Redis 8.8 adds subkey notifications for hash fields, enabling field-level keyspace notifications for hash data."},{"id":"increx-rate-limiter","title":"INCREX rate limiter","role":"content","text":"The new [`INCREX`](https://redis.io/docs/latest/commands/increx) command (#15045) is a window counter rate limiter that combines `INCR`, `INCRBY`, `INCRBYFLOAT`, bounds, and expiration into a single atomic operation. It simplifies common rate-limiting patterns by eliminating the need for multi-command transactions."},{"id":"xnack-streams-command","title":"XNACK streams command","role":"content","text":"The new [`XNACK`](https://redis.io/docs/latest/commands/xnack) command allows stream consumers to explicitly release pending messages, providing finer-grained control over consumer group pending entry lists."},{"id":"sorted-set-count-aggregator","title":"Sorted set COUNT aggregator","role":"content","text":"[`ZUNION`](https://redis.io/docs/latest/commands/zunion), [`ZINTER`](https://redis.io/docs/latest/commands/zinter), [`ZUNIONSTORE`](https://redis.io/docs/latest/commands/zunionstore), and [`ZINTERSTORE`](https://redis.io/docs/latest/commands/zinterstore) support a new `COUNT` aggregator for combining sorted sets."},{"id":"jsonset-fpha-argument","title":"JSON.SET FPHA argument","role":"content","text":"[`JSON.SET`](https://redis.io/docs/latest/commands/json.set) accepts a new `FPHA` argument that specifies the floating-point type for homogeneous FP arrays."},{"id":"time-series-multiple-aggregators","title":"Time series multiple aggregators","role":"content","text":"[`TS.RANGE`](https://redis.io/docs/latest/commands/ts.range), [`TS.REVRANGE`](https://redis.io/docs/latest/commands/ts.revrange), [`TS.MRANGE`](https://redis.io/docs/latest/commands/ts.mrange), and [`TS.MREVRANGE`](https://redis.io/docs/latest/commands/ts.mrange) now support multiple aggregators in a single command."},{"id":"fthybrid-candidates-per-shard","title":"FT.HYBRID candidates per shard","role":"content","text":"The [`FT.HYBRID`](https://redis.io/docs/latest/commands/ft.hybrid) `KNN` clause accepts a new argument to request fewer candidates per shard, reducing overhead in cluster mode."},{"id":"ftprofile-hybrid","title":"FT.PROFILE HYBRID","role":"content","text":"[`FT.PROFILE`](https://redis.io/docs/latest/commands/ft.profile) adds profiling support for [`FT.HYBRID`](https://redis.io/docs/latest/commands/ft.hybrid) queries."},{"id":"in-group-sorting-reducer","title":"In-group sorting reducer","role":"content","text":"A new reducer for [`FT.AGGREGATE`](https://redis.io/docs/latest/commands/ft.aggregate) allows you to unwind grouped documents after `GROUPBY` and sort them within each group, enabling more sophisticated aggregation pipelines."},{"id":"bug-fixes","title":"Bug fixes","role":"content","text":"Redis 8.8 includes a broad set of bug fixes (compared to 8.8-M03)."},{"id":"core-fixes","title":"Core fixes","role":"content","text":"- `SUBSCRIBE`, `PSUBSCRIBE`, `SSUBSCRIBE`: crash on OOM (RED-167788).\n- `CONFIG SET`: some settings allow invalid characters (RED-167787).\n- `SCRIPT DEBUG`: potential crash on scripts (RED-175507).\n- `VADD`: crash or buffer overflow on large `REDUCE` value (RED-170921).\n- `VSET`: crash on huge allocations (MOD-12678).\n- `cluster-announce-ip` rejecting hostnames (regression) (#15188).\n- Double free when loading streams with duplicate consumer PEL entries (#15095).\n- Issues processing corrupt Streams RDB data (#15124).\n- `fast_float_strtod` rounding mismatch (#15111).\n- `vecClear` reset the logical size without releasing element ownership (#15190).\n- `MULTI` queue incorrect memory accounting (#15163).\n- Cluster crash when `CLIENT KILL` unsubscribes `SSUBSCRIBE` client inside `EXEC` (#15094).\n- Listpack backlength encoding thresholds off-by-one (#15151).\n- Under-copy in the Lua debugger (#15115).\n- Sentinel config injection via `SENTINEL SET` (#14970).\n- Client output buffer memory tracking not accounting for copy-avoided bulk string references (#14934).\n- `INCREX` syntax update (#15237).\n- Memory tracking can be enabled at runtime in non-clustered mode (#15005)."},{"id":"search-and-query-fixes","title":"Search and Query fixes","role":"content","text":"- `FT.PROFILE HYBRID` returns an empty reply (MOD-14778).\n- `FT.SPELLCHECK` treats `PARAMS` placeholders as literal terms instead of resolving them (MOD-10596).\n- `FT.PROFILE` output is inconsistent when a profiled value is missing (MOD-10560).\n- `FT.CREATE` now rejects schema definitions with invalid option combinations at creation time (MOD-14655).\n- `PERSIST` and `HPERSIST` notifications are not reflected in index expiration tracking (MOD-14800).\n- Race condition in `FT.HYBRID` causes intermittent failures under concurrent hybrid query load (MOD-14732).\n- Crash on `FT.SEARCH` when topology validation fails (for example, some nodes unreachable) (MOD-14475).\n- Coordinator deadlock under mixed `FT.SEARCH` and `FT.AGGREGATE` load (MOD-14268).\n- Memory leak when `FT.DROPINDEX` runs concurrently with in-flight hybrid queries (MOD-14135).\n- `FT.CURSOR READ` timeout and `ON_TIMEOUT FAIL` not enforced on coordinator and shard (MOD-14284, MOD-14998).\n- Cursors not cleaned up after `MAXIDLE`, causing resource exhaustion (MOD-6430).\n- Coordinator `RETURN_STRICT` returns wrong data on partial results, including `SORTBY` pipeline (MOD-13617).\n- `MAXPREFIXEXPANSION` warnings not propagated to clients in cluster mode (MOD-13804).\n- Search commands fail when no worker thread is available instead of falling back to main thread (MOD-14921).\n- RDB load missing validation of `FT.CREATE` arguments, allowing corrupt index state on load (MOD-13118).\n- Use-after-move in `Indexer_Process` causes crash during indexing (MOD-14980).\n- Deadlock between background query and main-thread writer (MOD-15364).\n- `FT.PROFILE` prints output using wrong iterator type (MOD-14678).\n- Confusing error returned when `DEBUG_PARAMS_COUNT` is zero (MOD-15118).\n- Stack-smashing error in coordinator code path (MOD-14649)."},{"id":"json-fixes","title":"JSON fixes","role":"content","text":"- Trailing chars are ignored (MOD-7266).\n- Wrong mutation ordering for array commands with recursive paths (MOD-6722).\n- JSONPath evaluation issues (MOD-14664, MOD-7264, MOD-7272, MOD-7270, MOD-7268)."},{"id":"time-series-fixes","title":"Time series fixes","role":"content","text":"- Potential crash on disconnections and TLS failures (MOD-14850).\n- `count`, `countNaN`, `countAll` reducers return NaN when all values are NaN (MOD-14420).\n- Cluster topology changes during a multi-shard command are not handled (MOD-14439)."},{"id":"probabilistic-fixes","title":"Probabilistic fixes","role":"content","text":"- Memory leak on RDB load (MOD-15418)."},{"id":"performance-and-resource-utilization-improvements","title":"Performance and resource utilization improvements","role":"performance","text":"Redis 8.8 delivers a broad set of performance improvements (compared to 8.6)."},{"id":"core-improvements","title":"Core improvements","role":"content","text":"- HyperLogLog: 4 independent accumulators that are merged at the end (#15049).\n- Batched prefetch for `MGET` and `MSET` (#15133).\n- Batched prefetch for `HGETALL` on hashtable-encoded hashes (#14988).\n- Pass size hint to jemalloc for faster deallocation (#15071).\n- Reduces allocator and accounting overhead by adding compile-time jemalloc tuning (#15096)."},{"id":"search-and-query-improvements","title":"Search and Query improvements","role":"content","text":"- Vector index hot path (HNSW and brute-force) devirtualized, reducing per-query latency (MOD-14916).\n- Inline LSE atomics enabled on AArch64, improving atomic operation throughput on ARM64 (MOD-14916, MOD-15419).\n- Expiration handling overhead reduced when many keys expire simultaneously (MOD-14916).\n- LTO (link-time optimization) enabled for x86_64 release builds (MOD-14700).\n- Shard-level timeout adjusted to coordinator dispatch time for more accurate accounting (MOD-13189).\n- Iterators ported to Rust, reducing FFI overhead.\n- `numRecords` no longer updated for vector fields, removing unnecessary write overhead on ingest (MOD-15487).\n- VecSim SVS thread pool integrated with the worker pool for better thread utilization (MOD-9881)."},{"id":"configuration-parameters","title":"Configuration parameters","role":"configuration","text":"Redis 8.8 introduces new configuration parameters (compared to 8.8-M03):\n\n- Slowlog entry truncation limits (#15182):\n  - `slowlog-entry-max-argc`: maximum number of command arguments kept in a slowlog entry.\n  - `slowlog-entry-max-string-len`: maximum length of a command argument in a slowlog entry.\n- Default maximum worker threads value updated; `MAX_WORKER_THREADS` is now a string config (MOD-14486, MOD-14763)."},{"id":"metrics","title":"Metrics","role":"content","text":"- `FT.PROFILE`: added queue time tracking (MOD-13602)."},{"id":"cli-tools","title":"CLI tools","role":"content","text":"- Fixed memory leak on malformed legacy help entry in `redis-cli` (#15150)."}],"examples":[]}
{"schema_version":2,"id":"develop/whats-new","title":"What's new?","url":"https://redis.io/docs/latest/develop/whats-new/","summary":"High-level description of important updates to the Develop section","tags":["docs","operate","rs","rc"],"last_updated":"2026-09-24T18:15:36-07:00","children":[{"id":"develop/whats-new/8-10","summary":"What's new in Redis 8.10","title":"Redis 8.10","url":"https://redis.io/docs/latest/develop/whats-new/8-10/"},{"id":"develop/whats-new/8-8","summary":"What's new in Redis 8.8","title":"Redis 8.8","url":"https://redis.io/docs/latest/develop/whats-new/8-8/"},{"id":"develop/whats-new/8-6","summary":"What's new in Redis 8.6","title":"Redis 8.6","url":"https://redis.io/docs/latest/develop/whats-new/8-6/"},{"id":"develop/whats-new/8-4","summary":"What's new in Redis 8.4","title":"Redis 8.4","url":"https://redis.io/docs/latest/develop/whats-new/8-4/"},{"id":"develop/whats-new/8-2","summary":"What's new in Redis 8.2","title":"Redis 8.2","url":"https://redis.io/docs/latest/develop/whats-new/8-2/"},{"id":"develop/whats-new/8-0","summary":"What's new in Redis 8.0","title":"Redis 8.0","url":"https://redis.io/docs/latest/develop/whats-new/8-0/"},{"id":"develop/whats-new/7-4","summary":"What's new in Redis 7.4","title":"Redis 7.4","url":"https://redis.io/docs/latest/develop/whats-new/7-4/"},{"id":"develop/whats-new/7-2","summary":"What's new in Redis 7.2","title":"Redis 7.2","url":"https://redis.io/docs/latest/develop/whats-new/7-2/"},{"id":"develop/whats-new/6-2","summary":"What's new in Redis 6.2","title":"Redis 6.2","url":"https://redis.io/docs/latest/develop/whats-new/6-2/"},{"id":"develop/whats-new/redis-feature-sets","summary":"Highlights of what's new for Redis feature sets","title":"Redis feature sets","url":"https://redis.io/docs/latest/develop/whats-new/redis-feature-sets/"}],"page_type":"content","content_hash":"4d82568276d5fe8330c2a9f57a84d3d635162a66aa6c0f740640b2c182d4e4f0","sections":[{"id":"q3-2026-july---september-updates","title":"Q3 2026 (July - September) Updates","role":"overview","text":""},{"id":"redis-version-updates","title":"Redis Version Updates","role":"content","text":"- [Redis 8.10](https://redis.io/docs/latest/develop/whats-new/8-10) - Compact Hashes for lower memory usage and higher throughput; incremental backup and restore; extensive JSONPath extensions; new commands for Lists, Sets, Search, and Time Series, including `LMOVEM/BLMOVEM`, `SUNIONCARD/SDIFFCARD`, `FT.ALIASLIST`, `TS.NRANGE/TS.NREVRANGE`, and `TS.READ`; a new `FT.AGGREGATE COLLECT` reducer; stricter query timeout enforcement; and performance improvements across Redis core and Streams.\n\n---"},{"id":"redis-iris-context-engine","title":"Redis Iris (Context Engine)","role":"content","text":"- Added self-managed deployment documentation for all four Redis Iris services: [LangCache](https://redis.io/docs/latest/operate/iris/langcache/self-managed), [Agent Memory](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed), [Context Retriever](https://redis.io/docs/latest/develop/ai/context-engine/context-retriever/install), and [Data Integration](https://redis.io/docs/latest/integrate/redis-data-integration)\n- Updated [LangCache](https://redis.io/docs/latest/develop/ai/context-engine/langcache), [Agent Memory](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory), [Context Retriever](https://redis.io/docs/latest/develop/ai/context-engine/context-retriever), and [Data Integration](https://redis.io/docs/latest/develop/ai/context-engine/data-integration) quickstarts with fully managed and self-managed deployment paths\n- Added [Redis Iris concepts](https://redis.io/docs/latest/develop/ai/context-engine/concepts) explaining how agents get context from Redis Iris\n- Added new [AI agent builder](https://redis.io/docs/latest/develop/ai/agent-builder) templates for knowledge assistants (RAG) and Redis Iris conversational assistants"},{"id":"q2-2026-april---june-updates","title":"Q2 2026 (April - June) Updates","role":"content","text":""},{"id":"redis-version-updates-1","title":"Redis Version Updates","role":"content","text":"- [Redis 8.8](https://redis.io/docs/latest/develop/whats-new/8-8) - New Array data structure, `INCREX` rate limiter, in-group sorting reducer for `FT.AGGREGATE`, Rust iterators and devirtualized vector hot paths in Search, HyperLogLog and `MGET`/`MSET`/`HGETALL` performance improvements."},{"id":"q1-2026-january---march-updates","title":"Q1 2026 (January - March) Updates","role":"content","text":""},{"id":"tools","title":"Tools","role":"content","text":"- Redis Insight [v3.2.0 release notes](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.3.2.0)\n- Redis Insight [v3.0.3 release notes](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.3.0.3)\n- Redis Insight [v2.68.0 release notes](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.68.0)\n- Redis Insight [v2.66.0 release notes](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.66.0)\n\n---"},{"id":"redis-ai--vectors","title":"Redis AI & Vectors","role":"content","text":"- Added [n8n vector store integration](https://redis.io/docs/latest/integrate/n8n-vector-store)\n- Updated [RedisVL 0.7.0 documentation](https://redis.io/docs/latest/develop/ai/redisvl/0.7.0)\n- Updated [RedisVL 0.9.1 documentation](https://redis.io/docs/latest/develop/ai/redisvl/0.9.1)\n- Updated [RedisVL 0.12.1 documentation](https://redis.io/docs/latest/develop/ai/redisvl/0.12.1) (28 pages)\n\n---"},{"id":"data-types","title":"Data Types","role":"content","text":"- Enhanced [example progression](https://redis.io/docs/latest/develop/data-types) with metadata for:\n  - [Strings](https://redis.io/docs/latest/develop/data-types/strings)\n  - [Hashes](https://redis.io/docs/latest/develop/data-types/hashes)\n  - [Lists](https://redis.io/docs/latest/develop/data-types/lists)\n  - [Sets](https://redis.io/docs/latest/develop/data-types/sets)\n  - [Sorted sets](https://redis.io/docs/latest/develop/data-types/sorted-sets)\n  - [Bitmaps](https://redis.io/docs/latest/develop/data-types/strings/bitmaps)\n  - [Geospatial](https://redis.io/docs/latest/develop/data-types/geospatial)\n  - [JSON](https://redis.io/docs/latest/develop/data-types/json)\n  - [Time series](https://redis.io/docs/latest/develop/data-types/timeseries)\n  - [Vector sets](https://redis.io/docs/latest/develop/data-types/vector-sets)\n\n---"},{"id":"client-libraries","title":"Client Libraries","role":"content","text":"#### Ruby (NEW)\n- Added [Ruby client documentation](https://redis.io/docs/latest/develop/clients/ruby)\n- Landing page code examples for redis-rb\n\n#### ioredis (NEW)\n- Added [ioredis connect page](https://redis.io/docs/latest/develop/clients/ioredis/connect)\n\n#### node-redis (JavaScript)\n- Added Smart client handoffs section in the Connect page\n\n#### Rust\n- Added [Rust AMR/EntraID connection page](https://redis.io/docs/latest/develop/clients/rust/amr)\n- Added Rust error handling documentation\n- Added Rust transactions and pipelines documentation\n\n#### Python (redis-py)\n- Updated failover documentation with initial health checks\n- Added failback configuration details\n- Added observability page\n- Added Smart client handoffs section in the Connect page\n\n#### Java (Jedis)\n- Added failback configuration section\n- Updated failover behavior documentation\n\n#### Java (Lettuce)\n- Added failover documentation\n- Added `LagAwareStrategy` dependencies\n- Added Smart client handoffs section in the Connect page\n\n#### .NET (NRedisStack)\n- Added [FusionCache integration page](https://redis.io/docs/latest/integrate/fusioncache)\n\n#### Go\n- Added observability page\n- Added Smart client handoffs section in the Connect page\n\n---"},{"id":"documentation-updates","title":"Documentation Updates","role":"content","text":"- Added BinderHub support for \"Run in browser\" functionality with Python examples\n- Added Jupyter notebook support with pre-configured environments\n- Enhanced example progression metadata across all data types\n- Added testable code example shortcodes with language-specific markers\n- Fixed fuzzy search documentation with specific attribute examples\n- Updated client library description differences across all major clients\n- Added observability overview with OpenTelemetry metrics\n- Added Smart client handoffs overview\n- Docs for n8n Redis vector store integration\n- Added railroad diagrams and API methods to all command pages\n\n---"},{"id":"redis-version-updates-2","title":"Redis Version Updates","role":"content","text":"- [Redis 8.6](https://redis.io/docs/latest/develop/whats-new/8-6) - Stream idempotency, least recently modified eviction policies, hot key detection, TLS certificate-based authentication, enhanced time series with NaN support, memory optimizations\n- [Redis 8.4](https://redis.io/docs/latest/develop/whats-new/8-4) - Updated documentation (removed RC1 designation)"},{"id":"q4-2025-october---december-updates","title":"Q4 2025 (October - December) Updates","role":"content","text":""},{"id":"tools-1","title":"Tools","role":"content","text":"- Enhanced Redis Insight documentation with autocomplete features\n\n---"},{"id":"redis-ai--vectors-1","title":"Redis AI & Vectors","role":"content","text":"- Launched [AI Agent Builder](https://redis.io/docs/latest/develop/ai/agent-builder) - interactive code generator for production-ready AI agents\n- Documented [RedisVL 0.9.0](https://redis.io/docs/latest/develop/ai/redisvl) (28 pages):\n  - Vector search and indexing\n  - Semantic caching\n  - Message history management\n  - Rerankers and routers\n  - Vectorizers\n- Added [AI video tutorials](https://redis.io/docs/latest/develop/ai/ai-videos) with YouTube content\n- Added [AI notebook collection](https://redis.io/docs/latest/develop/ai/notebook-collection) with notebook links\n- Added [LangCache documentation](https://redis.io/docs/latest/develop/ai/context-engine/langcache) with API examples\n\n---"},{"id":"client-libraries-1","title":"Client Libraries","role":"content","text":"#### Rust (NEW)\n- Added [Rust client documentation](https://redis.io/docs/latest/develop/clients/rust)\n- Enabled Rust-Sync and Rust-Async client variants\n- Landing page code examples for Rust\n\n#### Java (Jedis)\n- Added [vector set embedding examples](https://redis.io/docs/latest/develop/clients/jedis/vecsets)\n\n#### .NET (NRedisStack)\n- Added [vector set embedding examples](https://redis.io/docs/latest/develop/clients/dotnet/vecsets)\n- Enabled C#-Sync and C#-Async client variants with async examples\n\n---"},{"id":"documentation-updates-1","title":"Documentation Updates","role":"content","text":"- Integrated BinderHub for \"Run in browser\" functionality with Python examples\n- Launched Testable Code Examples (TCE) framework for hands-on learning\n- Added new integration guides (cloud platforms, monitoring tools, AI/ML frameworks)\n\n---"},{"id":"q3-2025-july---september-updates","title":"Q3 2025 (July - September) Updates","role":"content","text":""},{"id":"tools-2","title":"Tools","role":"content","text":"- Added [redis-cli installation instructions](https://redis.io/docs/latest/develop/tools/cli)\n\n---"},{"id":"redis-ai--vectors-2","title":"Redis AI & Vectors","role":"content","text":"- Updated [vector types documentation](https://redis.io/docs/latest/develop/ai/search-and-query/vectors) for INT8 and UINT8\n- Added [GEO search precision information](https://redis.io/docs/latest/develop/ai/search-and-query/indexing/geoindex)\n- Enhanced [LangCache SDK](https://redis.io/docs/latest/develop/ai/context-engine/langcache) with savings calculator and improved API documentation\n\n---"},{"id":"data-types-1","title":"Data Types","role":"content","text":"- [Bitmaps](https://redis.io/docs/latest/develop/data-types/strings/bitmaps):\n  - Added BITOP documentation with Python testable code examples\n  - Added bit operation diagrams\n\n---"},{"id":"client-libraries-2","title":"Client Libraries","role":"content","text":"#### Java (Jedis)\n- Added [vector set embedding examples](https://redis.io/docs/latest/develop/clients/jedis/vecsets)\n\n#### .NET (NRedisStack)\n- Added [vector set embedding examples](https://redis.io/docs/latest/develop/clients/dotnet/vecsets)\n- Enabled C#-Sync and C#-Async client variants with async examples on the landing page\n\n#### Rust (NEW)\n- Added initial [Rust client documentation](https://redis.io/docs/latest/develop/clients/rust)\n- Enabled Rust-Sync and Rust-Async client variants\n- Landing page code examples for Rust\n\n---"},{"id":"documentation-updates-2","title":"Documentation Updates","role":"content","text":"- Added [close/quit details](https://redis.io/docs/latest/develop/clients) to client landing pages\n- Added [redlock-universal](https://redis.io/docs/latest/develop/clients/patterns/distributed-locks) to Redlock implementations list\n- Fixed [keyspace notifications](https://redis.io/docs/latest/develop/pubsub/keyspace-notifications) typo\n- Updated Unicode quotes to ASCII throughout documentation\n- Fixed broken links and improved cross-references"},{"id":"q2-2025-april---june-updates","title":"Q2 2025 (April - June) Updates","role":"content","text":""},{"id":"tools-3","title":"Tools","role":"content","text":"- Redis Insight [v2.70.1 release notes](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.70.1)\n- Redis Insight [v1.4.0 release notes](https://redis.io/docs/latest/develop/tools/insight/release-notes/v1.4.0)\n- Updated [Redis Insight pages](https://redis.io/docs/latest/develop/tools/insight) with consistent image-card layout\n- Added Redis Insight SVG icons and download links across [tools documentation](https://redis.io/docs/latest/develop/tools)\n\n---"},{"id":"redis-ai--vectors-3","title":"Redis AI & Vectors","role":"content","text":"- Reorganized [search and query documentation](https://redis.io/docs/latest/develop/ai/search-and-query) under AI section\n- Added [AI video tutorials](https://redis.io/docs/latest/develop/ai/ai-videos) with YouTube content\n- Added [AI notebook collection](https://redis.io/docs/latest/develop/ai/notebook-collection) with 8 new notebook links\n- Expanded vector examples across multiple clients:\n  - [Python vector sets](https://redis.io/docs/latest/develop/clients/redis-py/vecsets)\n  - [Go vector sets](https://redis.io/docs/latest/develop/clients/go/vecsets)\n  - [JavaScript vector sets](https://redis.io/docs/latest/develop/clients/nodejs/vecsets)\n  - [Lettuce vector queries](https://redis.io/docs/latest/develop/clients/lettuce/vecsearch)\n  - [Lettuce vector sets](https://redis.io/docs/latest/develop/clients/lettuce/vecsets)\n- Updated [redisvl documentation](https://redis.io/docs/latest/develop/ai/redisvl) for versions 0.6.0-0.8.2\n- Added [LangCache SDK](https://redis.io/docs/latest/develop/ai/context-engine/langcache) documentation with [API reference](https://redis.io/docs/latest/develop/ai/context-engine/langcache/api-examples)\n\n---"},{"id":"redis-80--82-features","title":"Redis 8.0 & 8.2 Features","role":"content","text":"- [Redis Open Source 8.2 documentation](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.2-release-notes)\n- Updated [Redis 8.0 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.0-release-notes)\n- [Redis Search expiration capabilities](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/expiration) in Redis 8\n- Enhanced [TAG documentation](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/tags) per GitHub issues\n- [Vector quantization and compression](https://redis.io/docs/latest/develop/ai/search-and-query/vectors/svs-compression) moved to dedicated page\n\n---"},{"id":"data-types-2","title":"Data Types","role":"content","text":"- TimeSeries:\n  - [Enhanced time series examples](https://redis.io/docs/latest/develop/data-types/timeseries) with query and aggregation info\n  - Added testable code examples (TCE) support\n- Probabilistic:\n  - Added testable examples for [Python](https://redis.io/docs/latest/develop/clients/redis-py/prob), [C#](https://redis.io/docs/latest/develop/clients/dotnet/nredisstack/prob), [Java](https://redis.io/docs/latest/develop/clients/jedis/prob), and [Go](https://redis.io/docs/latest/develop/clients/go/prob)\n  - Updated [Cuckoo filter documentation](https://redis.io/docs/latest/develop/data-types/probabilistic/cuckoo-filter)\n\n---"},{"id":"client-libraries-3","title":"Client Libraries","role":"content","text":"#### Python (redis-py)\n- Added [reconnection examples](https://redis.io/docs/latest/develop/clients/redis-py/connect#retrying-connections)\n- Enhanced [timeout and retry details](https://redis.io/docs/latest/develop/clients/redis-py/produsage#timeouts)\n- Fixed [pip install command](https://redis.io/docs/latest/develop/clients/redis-py/amr) for redis-py entraid extension\n- Added [Binder environment links](https://redis.io/docs/latest/develop/clients/redis-py#connect-and-test) for Python examples\n\n#### Java (Jedis)\n- Updated to [latest Jedis version](https://redis.io/docs/latest/develop/clients/jedis#install)\n- Added [reconnection examples](https://redis.io/docs/latest/develop/clients/jedis/connect#retrying-a-command-after-a-connection-failure)\n- Enhanced [probabilistic data type examples](https://redis.io/docs/latest/develop/clients/jedis/prob)\n\n#### Node.js\n- Added [command reliability information](https://redis.io/docs/latest/develop/clients/nodejs/produsage#command-execution-reliability)\n- Fixed [reconnection details](https://redis.io/docs/latest/develop/clients/nodejs/connect#reconnect-after-disconnection)\n\n#### .NET (StackExchange.Redis/NRedisStack)\n- Added [retries and timeouts](https://redis.io/docs/latest/develop/clients/dotnet/produsage) to production usage advice\n- Enhanced [dialect 2 notes](https://redis.io/docs/latest/develop/clients/dotnet/nredisstack/queryjson)\n\n#### Go (go-redis)\n- Added [retries and timeouts](https://redis.io/docs/latest/develop/clients/go/produsage) to production usage\n- Enhanced [dialect 2 notes](https://redis.io/docs/latest/develop/clients/go/queryjson)\n- Added [Connect with AMR](https://redis.io/docs/latest/develop/clients/go/amr) page.\n\n#### Lettuce\n- Updated to [latest Lettuce version](https://redis.io/docs/latest/develop/clients/lettuce#install)\n- Added [command reliability information](https://redis.io/docs/latest/develop/clients/lettuce/produsage#connection-and-execution-reliability)\n- Added [JSON query examples](https://redis.io/docs/latest/develop/clients/lettuce/queryjson)\n\n#### PHP (Predis)\n- Enhanced [dialect 2 notes](https://redis.io/docs/latest/develop/clients/php/queryjson)\n\n---"},{"id":"documentation-structure--navigation","title":"Documentation Structure & Navigation","role":"content","text":"- Reorganized [develop section navigation](https://redis.io/docs/latest/develop) with improved sidebar structure\n- Moved [programmability section](https://redis.io/docs/latest/develop/programmability) into develop area\n- Relocated [patterns folder](https://redis.io/docs/latest/develop/clients/patterns) to clients section\n- Added [Using commands section](https://redis.io/docs/latest/develop/using-commands) to develop area\n- Enhanced [keyspace notifications](https://redis.io/docs/latest/develop/pubsub/keyspace-notifications) and [pub/sub](https://redis.io/docs/latest/develop/pubsub) documentation\n- Updated [transactions](https://redis.io/docs/latest/develop/using-commands/transactions) and [pipeline](https://redis.io/docs/latest/develop/using-commands/pipelining) pages\n- Added comprehensive aliases for backward compatibility\n\n---"},{"id":"protocol--technical-updates","title":"Protocol & Technical Updates","role":"content","text":"- Fixed [RESP protocol specification](https://redis.io/docs/latest/develop/reference/protocol-spec) attribute byte documentation\n- Enhanced [FT.AGGREGATE expression precedence](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/aggregations-syntax) documentation\n- Updated [distributed locks](https://redis.io/docs/latest/develop/clients/patterns/distributed-locks) documentation\n- Fixed [FP32 vectorsets endianness](https://redis.io/docs/latest/develop/data-types/vector-sets#endianness-considerations-for-fp32-format) documentation"},{"id":"q1-2025-january---march-updates","title":"Q1 2025 (January - March) Updates","role":"content","text":""},{"id":"tools-4","title":"Tools","role":"content","text":"- Redis Insight [v2.66 release notes](https://redis.io/docs/latest/develop/tools/insight/release-notes/v.2.66.0)\n- Updated CLI output samples for [`bigkeys`, `memkeys`, `keystats`](https://redis.io/docs/latest/develop/tools/cli)\n\n---"},{"id":"redis-ai--vectors-4","title":"Redis AI & Vectors","role":"content","text":"- Expanded vector examples:\n  - [Python](https://redis.io/docs/latest/develop/clients/redis-py/vecsearch)\n  - [Node.js](https://redis.io/docs/latest/develop/clients/nodejs/vecsearch)\n  - [Java (Jedis)](https://redis.io/docs/latest/develop/clients/jedis/vecsearch)\n  - [Go](https://redis.io/docs/latest/develop/clients/go/vecsearch)\n  - [.NET](https://redis.io/docs/latest/develop/clients/dotnet/nredisstack/vecsearch)\n- Updated AI integrations:\n  - [AI overview](https://redis.io/docs/latest/develop/ai)\n  - [RAG intro](https://redis.io/docs/latest/develop/get-started/rag)\n  - [Redis in AI](https://redis.io/docs/latest/develop/get-started/redis-in-ai)\n\n---"},{"id":"data-types-3","title":"Data Types","role":"content","text":"- TimeSeries:\n  - [`COMPACTION_POLICY`](https://redis.io/docs/latest/develop/data-types/timeseries/configuration)\n  - [Client-side caching update](https://redis.io/docs/latest/develop/clients/client-side-caching)\n- JSON:\n  - [Active memory defragmentation](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json/commands)\n- Probabilistic:\n  - [Bloom filter](https://redis.io/docs/latest/develop/data-types/probabilistic/bloom-filter)\n  - [Count-min sketch](https://redis.io/docs/latest/develop/data-types/probabilistic/count-min-sketch)\n  - [Top-K](https://redis.io/docs/latest/develop/data-types/probabilistic/top-k)\n  - [Cuckoo filter](https://redis.io/docs/latest/develop/data-types/probabilistic/cuckoo-filter)\n\n---"},{"id":"commands--api-docs","title":"Commands & API Docs","role":"syntax","text":"- Pages updated for format and accuracy:\n  - [ACL SETUSER](https://redis.io/docs/latest/commands/acl-setuser)\n  - [JSON.GET](https://redis.io/docs/latest/commands/json.get)\n  - [TS.ADD](https://redis.io/docs/latest/commands/ts.add)\n  - [SCAN](https://redis.io/docs/latest/commands/scan)\n  - [SORT](https://redis.io/docs/latest/commands/sort)\n- RESP3 reply types documented in [Hiredis command page](https://redis.io/docs/latest/develop/clients/hiredis/issue-commands)\n- [CSC behavior clarified](https://redis.io/docs/latest/develop/clients/client-side-caching)\n\n---"},{"id":"search--query","title":"Search & Query","role":"content","text":"- Best practices:\n  - [Dev-to-prod guide](https://redis.io/docs/latest/develop/ai/search-and-query/best-practices/dev-to-prod-best-practices)\n  - [Scalable queries](https://redis.io/docs/latest/develop/ai/search-and-query/best-practices/scalable-query-best-practices)\n  - [Index lifecycle](https://redis.io/docs/latest/develop/ai/search-and-query/best-practices/index-mgmt-best-practices)\n- New/updated topics:\n  - [Autocomplete](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/autocomplete)\n  - [Escaping & tokenization](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/escaping)\n  - [Geo indexing](https://redis.io/docs/latest/develop/ai/search-and-query/indexing/geoindex)\n  - [Sorting, scoring, stemming](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/sorting)\n\n---"},{"id":"client-libraries-4","title":"Client Libraries","role":"content","text":"#### Go\n- [Trans/pipe examples](https://redis.io/docs/latest/develop/clients/go/transpipe)\n- [JSON queries](https://redis.io/docs/latest/develop/clients/go/queryjson)\n\n#### .NET\n- [Vector search](https://redis.io/docs/latest/develop/clients/dotnet/nredisstack/vecsearch)\n- [Trans/pipe usage](https://redis.io/docs/latest/develop/clients/dotnet/transpipe)\n- [JSON queries](https://redis.io/docs/latest/develop/clients/dotnet/nredisstack/queryjson)\n\n#### Java (Jedis)\n- [Vector search](https://redis.io/docs/latest/develop/clients/jedis/vecsearch)\n- [Trans/pipe usage](https://redis.io/docs/latest/develop/clients/jedis/transpipe)\n\n#### Node.js\n- [Vector queries](https://redis.io/docs/latest/develop/clients/nodejs/vecsearch)\n- [Trans/pipe examples](https://redis.io/docs/latest/develop/clients/nodejs/transpipe)\n- [JSON queries](https://redis.io/docs/latest/develop/clients/nodejs/queryjson)\n\n#### Redis-py\n- [ScanIter usage](https://redis.io/docs/latest/develop/clients/redis-py/scaniter)\n- [Vector search](https://redis.io/docs/latest/develop/clients/redis-py/vecsearch)\n- [Trans/pipe usage](https://redis.io/docs/latest/develop/clients/redis-py/transpipe)\n- [JSON queries](https://redis.io/docs/latest/develop/clients/redis-py/queryjson)\n\n#### Lettuce\n- [Cluster connection](https://redis.io/docs/latest/develop/clients/lettuce/connect)\n- [Production usage](https://redis.io/docs/latest/develop/clients/lettuce/produsage)\n\n#### Hiredis\n- Full client guide:\n  - [Overview](https://redis.io/docs/latest/develop/clients/hiredis)\n  - [Connect](https://redis.io/docs/latest/develop/clients/hiredis/connect)\n  - [Issue commands](https://redis.io/docs/latest/develop/clients/hiredis/issue-commands)\n  - [Handle replies](https://redis.io/docs/latest/develop/clients/hiredis/handle-replies)\n  - [Transactions and pipelines](https://redis.io/docs/latest/develop/clients/hiredis/transpipe)"},{"id":"q4-2024-october---december-updates","title":"Q4 2024 (October - December) Updates","role":"content","text":"* Updated the RESP3 specification document to include the [attribute type](https://redis.io/docs/latest/develop/reference/protocol-spec#attributes).\n* Updates to the [key eviction](https://redis.io/docs/latest/develop/reference/eviction) page.\n* Updates to the Redis Insight page related to its new Redis Search auto-completion [feature](https://redis.io/docs/latest/develop/tools/insight#workbench).\n* Restructured and added testable connection examples to the [client pages](https://redis.io/docs/latest/develop/clients).\n* Added [Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce) and [Redis Stack](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisstack) release notes.\n* Added new [Redis for AI](https://redis.io/docs/latest/develop/ai) page.\n* Added new [Predis (PHP client library)](https://redis.io/docs/latest/develop/clients/php) page."},{"id":"q3-2024-july---september-updates","title":"Q3 2024 (July - September) Updates","role":"content","text":"* Updated the [RAG with Redis quick start guide](https://redis.io/docs/latest/develop/get-started/rag).\n* Updates for [Redis Open Source version 7.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce).\n* Added new [Redis Insight debugging](https://redis.io/docs/latest/develop/tools/insight/debugging) page.\n* Completed a major re-write/restructuring of the [vector indexing page](https://redis.io/docs/latest/develop/ai/search-and-query/vectors).\n* Added new [client-side caching page](https://redis.io/docs/latest/develop/clients/client-side-caching).\n* Added new documentation for the [RDI in Redis Insight feature](https://redis.io/docs/latest/develop/tools/insight/rdi-connector).\n* Added new documentation for the [Redis for VS Code feature](https://redis.io/docs/latest/develop/tools/redis-for-vscode).\n* Added multi-language code examples to Redis Search [query](https://redis.io/docs/latest/develop/ai/search-and-query/query) pages.\n* Added client-side caching information to the [supported clients](https://redis.io/docs/latest/develop/clients/client-side-caching#which-client-libraries-support-client-side-caching) pages.\n* Numerous changes to the [Redis client content](https://redis.io/docs/latest/develop/clients)."}],"examples":[]}
{"schema_version":2,"id":"develop/whats-new/redis-feature-sets","title":"Redis feature sets","url":"https://redis.io/docs/latest/develop/whats-new/redis-feature-sets/","summary":"Highlights of what's new for Redis feature sets","tags":["docs","operate","rs","rc"],"last_updated":"2026-09-11T15:36:02+01:00","page_type":"content","content_hash":"970756d1cc666bab1d308f04b152976f7a653de7a03adc21b0a0f823bb888083","sections":[{"id":"content","title":"Content","role":"content","text":"A Redis feature set includes a specific Redis database version along with the advanced capabilities and data structures provided by compatible module versions.\n\nTo use a new feature introduced in a later feature set, you must upgrade the corresponding components according to the following table.\n\n| Redis feature set | What's new |\n|-------------------|------------|\n| **Feature set version:** 8.10| See [here](https://redis.io/docs/latest/develop/whats-new/8-10)|\n| **Feature set version:** 8.8| See [here](https://redis.io/docs/latest/develop/whats-new/8-8)|\n| **Feature set version:** 8.6| See [here](https://redis.io/docs/latest/develop/whats-new/8-6)|\n| **Feature set version:** 8.4| See [here](https://redis.io/docs/latest/develop/whats-new/8-4)|\n| **Feature set version:** 8.2| See [here](https://redis.io/docs/latest/develop/whats-new/8-2)|\n| **Feature set version:** 8.0| See [here](https://redis.io/docs/latest/develop/whats-new/8-0)|\n| **Feature set version:** 7.4<br /><br />**Component versions:**<br />[Redis 7.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisce-7.4-release-notes)<br />[Search 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[JSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[Time series 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[Bloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) | **Hash**: <br>- [Expiration of individual hash fields](https://redis.io/docs/latest/develop/data-types/hashes#field-expiration). <br> **Streams**: <br>- To start reading from the last stream message, use [`XREAD`](https://redis.io/docs/latest/commands/xread) with the new ID value `+`.<br> **Time series**: <br>Insertion-filter for close samples. <br> **JSON**: <br>- A fix to not duplicate `AOF` commands multiple times in [`JSON.MSET`](https://redis.io/docs/latest/commands/json.mset). <br> **Probabilistic**: <br>- Returns an error if [`CMS.MERGE`](https://redis.io/docs/latest/commands/cms.merge) results in an overflow or underflow. <br> **Redis Search**: <br>- New `BFLOAT16` and `FLOAT16` vector data types, reducing memory consumed by vectors while preserving accuracy. <br>- Support for indexing empty and missing values and enhanced developer experience for queries with exact matching capabilities. <br>- You can match `TAG` fields without needing to escape special characters. <br>- 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. |\n| **Feature set version:** 7.2<br /><br />**Component versions:**<br />[Redis 7.2](https://raw.githubusercontent.com/redis/redis/7.2/00-RELEASENOTES)<br />[Search 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[JSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[Time series 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[Bloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes)<br />[Gears 2.0](https://github.com/RedisGears/RedisGears/releases) | - Performance and resource utilization improvements, including significant memory and speed optimizations for lists, sets, and sorted sets.<br />**JSON**:<br />- New JSON commands: [`JSON.MERGE`](https://redis.io/docs/latest/commands/json.merge) and [`JSON.MSET`](https://redis.io/docs/latest/commands/json.mset).<br />**Redis Search:**<br />- [Geo polygon search](https://redis.io/docs/latest/commands/ft.search#examples).<br>**Compatibility changes**:<br>- Redis 7.2 uses a new format (version 11) for RDB files, which is incompatible with older versions.<br />- Redis feature set 7.2 does not include [graph capabilities](https://redis.io/blog/redisgraph-eol/). |\n| **Feature set version:** 6.2<br /><br />**Component versions:**<br />[Redis 6.2](https://raw.githubusercontent.com/redis/redis/6.2/00-RELEASENOTES)<br />[Search 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[JSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[Time series 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[Bloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)<br />[Graph 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes) | **Time series**:<br />- Time series gap filling.<br />**JSON**:<br />- Improved JSON path parser.<br />**Probabilistic:**<br />- New probabilistic data structure t-digest.<br />**Redis Search:**<br />- Wildcard queries for `TEXT` and `TAG`.<br />- Suffix search.<br />- Multi-value indexing and queries.<br />**Graph**:<br />- New pathfinding algorithms for graphs. |"}],"examples":[]}
{"schema_version":2,"id":"get-started","title":"Open Source","url":"https://redis.io/docs/latest/get-started/","summary":"Get started with Redis Open Source","tags":[],"last_updated":"2026-08-03T12:11:45-07:00","children":[],"page_type":"content","content_hash":"d90ee2561fe03a10699a6f80b60c8309a7e63706b2a95abc6a53ce253263d49e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis is an [in-memory data store](https://redis.io/docs/latest/develop/get-started/data-store) used by millions of developers as a cache, [vector database](https://redis.io/docs/latest/develop/get-started/search-tutorial/vector-search), [document database](https://redis.io/docs/latest/develop/get-started/search-tutorial), [streaming engine](https://redis.io/docs/latest/develop/data-types/streams), and message broker. Redis has built-in replication and different levels of [on-disk persistence](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence). It supports complex [data types](https://redis.io/docs/latest/develop/data-types/) (for example, strings, hashes, lists, sets, sorted sets, and JSON), with atomic operations defined on those data types.\n\nSince 2009, the Redis open source project has inspired an enthusiastic and active community of users and contributors. The Redis core source repository is hosted under [https://github.com/redis/redis](https://github.com/redis/redis) along with many of the client libraries. See the [Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack/) page for more details and links.\n\nYou can install Redis from source or from an executable/distribution for your OS.\n\n* Install Redis on Linux using [APT](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/apt), [RPM](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/rpm), or [Snap](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/snap)\n* [Install Redis on macOS](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/homebrew)\n* [Run Redis on Windows using Docker](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/windows)\n* [Run Redis on Docker](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/docker)\n* [Install Redis from Source](https://redis.io/docs/latest/operate/oss_and_stack/install/build-stack)\n* [Install Redis with Redis Stack and Redis Insight](https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-stack)"},{"id":"use-cases","title":"Use cases","role":"content","text":"The following quick start guides will show you how to use Redis for the following specific purposes:\n\n- [Data structure store](https://redis.io/docs/latest/develop/get-started/data-store)\n- [Document database](https://redis.io/docs/latest/develop/get-started/search-tutorial)\n- [Vector database](https://redis.io/docs/latest/develop/get-started/search-tutorial/vector-search)\n- [AI agents and chatbots](https://redis.io/docs/latest/develop/get-started/redis-in-ai)\n- [Retrieval Augmented Generation (RAG) with Redis](https://redis.io/docs/latest/develop/get-started/rag)"},{"id":"data-integration-tools-libraries-and-frameworks","title":"Data integration tools, libraries, and frameworks","role":"content","text":"- [Client API libraries](https://redis.io/docs/latest/develop/clients)\n- [Redis Data Integration](https://redis.io/docs/latest/integrate/redis-data-integration/)\n- [Redis vector library for Python](https://redis.io/docs/latest/develop/ai/redisvl/)\n- [Redis Cloud with Amazon Bedrock](https://redis.io/docs/latest/integrate/amazon-bedrock/)\n- [Object-mapping for .NET](https://redis.io/docs/latest/integrate/redisom-for-net/)\n- [Spring Data Redis for Java](https://redis.io/docs/latest/integrate/spring-framework-cache/)\n\nYou can find a complete list of integrations on the [integrations and frameworks hub](https://redis.io/docs/latest/integrate/).\n\nTo learn more, refer to the [develop with Redis](https://redis.io/docs/latest/develop/) documentation."},{"id":"deployment-options","title":"Deployment options","role":"content","text":"You can deploy Redis with the following methods:\n\n- As a service by using [Redis Cloud](https://redis.io/docs/latest/operate/rc/), the fastest way to deploy Redis on your preferred cloud platform.\n- By installing [Redis Enterprise Software](https://redis.io/docs/latest/operate/rs/) in an on-premises data center or on Cloud infrastructure.\n- On a variety Kubernetes distributions by using the [Redis Enterprise operator for Kubernetes](https://redis.io/docs/latest/operate/kubernetes/).\n\nThe following guides will help you to get started with your preferred deployment method.\n\nGet started with **[Redis Cloud](https://redis.io/docs/latest/operate/rc/)** by creating a database:\n\n- The [Redis Cloud quick start](https://redis.io/docs/latest/operate/rc/rc-quickstart) helps you create a free database.  (Start here if you're new.)\n- [Create an Essentials database](https://redis.io/docs/latest/operate/rc/databases/create-database/create-essentials-database) with a memory limit up to 12 GB.\n- [Create a Pro database](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new) that suits your workload and offers seamless scaling.\n\nInstall a **[Redis Enterprise Software](https://redis.io/docs/latest/operate/rs/)** cluster:\n\n- [Redis Enterprise on Linux quick start](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/redis-enterprise-software-quickstart)\n- [Redis Enterprise on Docker quick start](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart)\n- [Get started with Redis Enterprise's Active-Active feature](https://redis.io/docs/latest/operate/rs/databases/active-active/get-started)\n- [Install and upgrade Redis Enterprise](https://redis.io/docs/latest/operate/rs/installing-upgrading)\n\nLeverage **[Redis Enterprise for Kubernetes](https://redis.io/docs/latest/operate/kubernetes/)** to simply deploy a Redis Enterprise cluster on Kubernetes:\n\n- [Deploy Redis Enterprise for Kubernetes](https://redis.io/docs/latest/operate/kubernetes/deployment/quick-start)\n- [Deploy Redis Enterprise for Kubernetes with OpenShift](https://redis.io/docs/latest/operate/kubernetes/deployment/openshift/)\n\nTo learn more, refer to the [Redis products](https://redis.io/docs/latest/operate/) documentation."},{"id":"provisioning-and-observability-tools","title":"Provisioning and observability tools","role":"content","text":"- [Pulumi provider for Redis Cloud](https://redis.io/docs/latest/integrate/pulumi-provider-for-redis-cloud/)\n- [Terraform provider for Redis Cloud](https://redis.io/docs/latest/integrate/terraform-provider-for-redis-cloud/)\n- [Prometheus and Grafana with Redis Cloud](https://redis.io/docs/latest/integrate/prometheus-with-redis-cloud)\n- [Prometheus and Grafana with Redis Enterprise](https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise/)\n\nYou can find a complete list of integrations on the [libraries and tools hub](https://redis.io/docs/latest/integrate/)."}],"examples":[]}
{"schema_version":2,"id":"glossary","title":"Glossary","url":"https://redis.io/docs/latest/glossary/","summary":"","aliases":["/glossary/"],"tags":["Glossary"],"last_updated":"2026-10-06T08:53:14-07:00","children":[],"page_type":"content","content_hash":"0db7191f41547cf98eea59b2b8bfcf6b5dca6f4784a108781e255604d27a2ee7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"<dl class=\"glossary\">"},{"id":"letter-a","title":"A, B","role":"content","text":"Allows you to manage permissions based on key patterns.\n\nMore info: [redis.io/operate/oss_and_stack/management/security/acl](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl); [ACL wikipedia](https://en.wikipedia.org/wiki/Access-control_list); [Database access control](https://redis.io/docs/latest/operate/rs/security/access-control); [Update database ACLs](https://redis.io/docs/latest/operate/rs/security/access-control/ldap/update-database-acls); [Role-based access control](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/role-based-access-control)\n\n\n<a name=\"active-active\"></a>\n\nGeo-distributed databases that span multiple [Redis Enterprise Software](https://redis.io/docs/latest/#redis-enterprise-software) [clusters](https://redis.io/docs/latest/#cluster). Active-Active databases, also known as conflict-free replicated databases (CRDB), depend on [multi-primary replication](https://redis.io/docs/latest/#multi-primary-replication) and [conflict-free replicated data types (CRDTs)](https://redis.io/docs/latest/#conflict-free-replicated-data-types-crdt) to power a simple development experience for geo-distributed applications.\n\nMore info: [Active-Active geo-distributed Redis](https://redis.io/docs/latest/operate/rs/databases/active-active), [Geo-distributed Active-Active Redis applications](https://redis.io/docs/latest/operate/rs/databases/active-active/), [Developing applications for Active-Active databases](https://redis.io/docs/latest/operate/rs/databases/active-active/develop/)\n\n\n\nA \"member database\" of a global [Active-Active database](https://redis.io/docs/latest/#active-active) which is made up of its own master and replica [shards](https://redis.io/docs/latest/#shard) spanning a single [cluster](https://redis.io/docs/latest/#cluster).\n\nMore info: [Active-Active database instances](https://redis.io/docs/latest/operate/rs/databases/active-active/create)\n\n\n\nProvides applications read-only access to replicas of the data set from different geographical locations. The Redis Enterprise implementation of active-passive [replication](https://redis.io/docs/latest/#replication) is called [Replica Of](https://redis.io/docs/latest/#replica-of).\n\nMore info: [Database replication](https://redis.io/docs/latest/operate/rs/databases/durability-ha/replication), [Replica Of](https://redis.io/docs/latest/operate/rs/databases/import-export/replica-of)\n\n\n\nEach node runs a web server that is used to provide the user with access to the Redis Enterprise Software admin console (previously known as management UI). The admin console allows viewing and managing the entire [cluster](https://redis.io/docs/latest/#cluster), so it does not matter which [node](https://redis.io/docs/latest/#redis-enterprise-nodes) is used to access it.\n\nMore info: [Manage clusters](https://redis.io/docs/latest/operate/rs/clusters)\n\n\n\nA piece of code that intercepts requests to the Kubernetes API server prior to persistence of the object.\n\nMore info: [Using Admission Controllers](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/)\n\n\n\nLog files that keep a record of data changes by writing to the end of a file. This happens with every write, or every second to allow data recovering the entire\ndataset by replaying the append-only log from the beginning to the end.\n\nMore info: [Data Persistence](https://redis.io/docs/latest/operate/rc/databases/configuration/data-persistence), [Data Persistence with Redis Enterprise Software](https://redis.io/docs/latest/operate/rs/databases/configure/database-persistence)\n\n\n\nA probabilistic data structure that tests whether an element is a member of a set. False positive matches are possible, but false negatives are not. Bloom filters are space-efficient and provide fast membership testing.\n\nMore info: [Bloom filters](https://redis.io/docs/latest/develop/data-types/probabilistic/bloom-filter), [RedisBloom](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/bloom)"},{"id":"letter-c","title":"C","role":"content","text":"A distributed database is causally consistent if it maintains the same order of operations on a piece of data across all database copies.\n\nMore info: [Causal consistency wikipedia](https://en.wikipedia.org/wiki/Causal_consistency), [Causal consistency in an Active-Active database](https://redis.io/docs/latest/operate/rs/databases/active-active/causal-consistency)\n\n\n\nClassless Inter-Domain Routing (CIDR) is a method to allocate and route IP addresses. A CIDR allowlist defines a range of IP addresses and permits connections to them.\n\nMore info: [CIDR wikipedia](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing), [Configure CIDR allowlist](https://redis.io/docs/latest/operate/rc/security/cidr-whitelist)\n\n\n\nConcurrency or updates and writes refer to more than events that happen at the same wall clock time across member [Active-Active databases](https://redis.io/docs/latest/#active-active). Concurrent updates refer to the fact that updates happen in between sync events that catch up member Active-Active databases with updates that happened on other member Active-Active databases.\n\nMore info: [Developing applications for Active-Active databases](https://redis.io/docs/latest/operate/rs/databases/active-active/develop), [Conflict-free replicated data types (CRDT)](https://redis.io/docs/latest/#conflict-free-replicated-data-types-crdt)\n\n\n\n\nConsistency models describe the way a distributed system keeps replicated data consistent between copies.\n\nMore info: [Consistency models](https://en.wikipedia.org/wiki/Consistency_model)\n\n\n\n\nA Redis Enterprise cluster is composed of identical nodes that are deployed within a data center or stretched across local availability zones.\n\nMore info: [Database clustering](https://redis.io/docs/latest/operate/rc/databases/configuration/clustering)\n\n\n\nAn internally managed [Redis database](https://redis.io/docs/latest/#redis-enterprise-database) that acts as a single repository for all [cluster](https://redis.io/docs/latest/#cluster) metadata.\n\nMore info: [Redis Enterprise cluster architecture](https://redis.io/docs/latest/operate/rs/clusters)\n\n\n\nA collection of Redis Enterprise services responsible for [provisioning](https://redis.io/docs/latest/#provisioning), [migration](https://redis.io/docs/latest/#migration), monitoring, [re-sharding](https://redis.io/docs/latest/#re-sharding), [rebalancing](https://redis.io/docs/latest/#rebalancing), de-provisioning, auto-scaling.\n\nMore info: [Redis Enterprise cluster architecture](https://redis.io/docs/latest/operate/rs/clusters), [Database operations](https://redis.io/docs/latest/operate/rs/databases)\n\n\n\n\nConflict-free replicated databases (CRDB) are an alternate name for [Active-Active databases](#active-active).\n\n\n\nTechniques used by Redis data types in Active-Active databases that handle conflicting concurrent writes across member Active-Active databases. The Redis Enterprise implementation of CRDT is called an Active-Active database (formerly known as CRDB).\n\nMore info: [CRDT info](https://redis.io/docs/latest/operate/rs/databases/active-active/develop/#info), [Active-Active geo-distributed Redis](https://redis.io/docs/latest/operate/rs/databases/active-active), [CRDT wikipedia](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type)\n\n\n\nControl loops that watch the state of your Kubernetes cluster and make or request changes where needed to move the current cluster state closer to the desired state.\n\nMore info: [Controllers](https://kubernetes.io/docs/concepts/architecture/controller/)\n\n\n\nExtensions of the Kubernetes API that allow you to store and retrieve structured data. Custom resources let you extend Kubernetes capabilities without modifying the core Kubernetes code.\n\nMore info: [Custom Resources](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/)\n\n\n\nA cluster-wide resource that specifies which settings can be configured via custom resource files. CRDs define the structure and validation rules for custom resources.\n\nMore info: [CustomResourceDefinition](https://kubernetes.io/docs/reference/glossary/?fundamental=true#term-CustomResourceDefinition), [Custom Resources](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/)"},{"id":"letter-d","title":"D - F","role":"content","text":"Defines how excess data is handled when the [database](https://redis.io/docs/latest/#redis-enterprise-database) exceeds the memory limit.\n\nMore info: [Data Eviction Policy](https://redis.io/docs/latest/operate/rc/databases/configuration/data-eviction-policies), [Redis Enterprise data eviction](https://redis.io/docs/latest/operate/rs/databases/memory-performance/eviction-policy)\n\n\n\nA configuration approach where you specify the desired state of your system, and the system automatically makes the necessary changes to achieve that state.\n\nMore info: [Managing Kubernetes Objects Using Declarative Configuration](https://kubernetes.io/docs/tasks/manage-kubernetes-objects/declarative-config/)\n\n\n\nFeatures are marked as _deprecated_ when they're scheduled to be removed from our products, generally because they've been replaced by new features.\n\nFor details, see [obsolete](#obsolete).\n\n\n\nNaming system for computers, services, or other resources connected to the Internet or a private network. It associates various information with domain names assigned to each of the participating entities.\n\nMore info: [DNS wikipedia](https://en.wikipedia.org/wiki/Domain_Name_System)\n\n\n\n\nAfter updating data on one instance of a distributed database, the other database copies may have stale data for a short time while they sync. Eventual consistency means that the updated data will eventually be the same across all database copies.\n\nMore info: [Eventual consistency wikipedia](https://en.wikipedia.org/wiki/Eventual_consistency)\n\n\n\nThe automatic process of switching to a backup system when the primary system fails. In Redis Enterprise, failover promotes replica shards to primary when the original primary becomes unavailable.\n\nMore info: [High availability](https://redis.io/docs/latest/operate/rs/databases/durability-ha), [Replica HA](https://redis.io/docs/latest/operate/rs/databases/configure/replica-ha)\n\n\n\nA domain name that includes a list of domain labels to specify the exact location in the DNS.\n\nMore info: [FQDN wikipedia](https://en.wikipedia.org/wiki/Fully_qualified_domain_name)\n\n\n\n\nLinux command to synchronize a file's in-core state with a storage device. Used in [data persistence](https://redis.io/docs/latest/operate/rs/databases/configure/database-persistence) operations.\n\nMore info: [`fsync` man page](https://man7.org/linux/man-pages/man2/fsync.2.html), [Data persistence](https://redis.io/docs/latest/operate/rs/databases/configure/database-persistence)\n\n\n\nA search technique that examines all words in every stored document to find matches to search criteria. Redis provides full-text search capabilities through its Search and Query features.\n\nMore info: [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query), [Full-text search](https://redis.io/docs/latest/develop/ai/search-and-query/query/full-text)"},{"id":"letter-g","title":"G - J","role":"content","text":"The process of creating data structures that improve the speed of data retrieval operations. Redis supports various types of indexing including secondary indexing, vector indexing, and full-text indexing.\n\nMore info: [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query), [Indexing concepts](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts)\n\n\n\nJavaScript Object Notation (JSON) is a lightweight data-interchange format. Redis provides native JSON support through RedisJSON, allowing you to store, update, and retrieve JSON values.\n\nMore info: [JSON data type](https://redis.io/docs/latest/develop/data-types/json), [RedisJSON](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json)\n\n\n\nThe result of a hash calculation used in [database clustering](https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering) to determine which [shard](https://redis.io/docs/latest/#shard) stores a particular key.\n\nMore info: [Database clustering](https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering), [Sharding](https://redis.io/docs/latest/#sharding)\n\n\n\nA part of the key that is used in the hash calculation to determine the [hash slot](https://redis.io/docs/latest/#hash-slot) for [database clustering](https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering).\n\nMore info: [Database clustering](https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering), [Hash slots](https://redis.io/docs/latest/#hash-slot)\n\n\n\n\nHigh availability (HA) is a characteristic of distributed systems that keeps systems available for users for longer than normal periods of time. This is done by reducing single points of failure, increasing redundancy, and making recovering from failures easier.\n\nMore info: [Redis Enterprise durability and high availability](https://redis.io/docs/latest/operate/rs/databases/durability-ha/), [High availability wikipedia](https://en.wikipedia.org/wiki/High_availability)\n\n\n\nAn API object that manages external access to the services in a Kubernetes cluster, typically HTTP.\n\nMore info: [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/), [Ingress routing for Redis Enterprise for Kubernetes](https://redis.io/docs/latest/operate/kubernetes/networking/ingress)"},{"id":"letter-k","title":"K, L","role":"content","text":"A command-line tool for communicating with a Kubernetes API server.\n\nMore info: [Overview of kubectl](https://kubernetes.io/docs/reference/kubectl/overview/)\n\n\n\nA protocol for accessing and maintaining distributed directory services over an IP network, often used to authenticate users.\n\nMore info: [LDAP wikipedia](https://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol), [LDAP authentication](https://redis.io/docs/latest/operate/rs/security/access-control/ldap)\n\n\n\nThe time delay between when a command is issued and when a response is received. Redis is designed for low latency, typically operating in the sub-millisecond range.\n\nMore info: [Latency monitoring](https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/latency-monitor), [Latency diagnosis](https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/latency)"},{"id":"letter-m","title":"M - O","role":"content","text":"[Node](https://redis.io/docs/latest/#redis-enterprise-nodes) that operates as the leader of a [cluster](https://redis.io/docs/latest/#cluster). Also known as the primary node.\n\nMore info: [Redis Enterprise cluster architecture](https://redis.io/docs/latest/operate/rs/clusters), [High availability](https://redis.io/docs/latest/#high-availability)\n\n\n\nDeciding when and where [shards](https://redis.io/docs/latest/#shard) will be moved if more network throughput, memory, or CPU resources are needed.\n\nMore info: [Database operations](https://redis.io/docs/latest/operate/rs/databases), [Cluster management](https://redis.io/docs/latest/operate/rs/clusters)\n\n\n\nProtocol that resolves hostnames to the IP addresses that do not include a local name server.\n\nMore info: [multicast DNS wikipedia](https://en.wikipedia.org/wiki/Multicast_DNS)\n\n\n\nA Redis extension that adds new commands, data types, and capabilities to Redis. Redis Enterprise includes several modules like RedisJSON, RedisSearch, RedisTimeSeries, and RedisBloom.\n\nMore info: [Redis modules](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise), [Install modules](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules)\n\n\n\nThe process of observing and tracking the performance, health, and behavior of Redis databases, clusters, and nodes using metrics and alerts.\n\nMore info: [Monitoring with metrics and alerts](https://redis.io/docs/latest/operate/rs/monitoring), [Metrics reference](https://redis.io/docs/latest/operate/rs/references/metrics)\n\n\n\nMethod of authenticating users with pieces of evidence of the user's identity. When MFA is enabled on Redis Cloud, users must enter their username, password, and an authentication code when logging in.\n\nMore info: [Multi-factor authentication](https://redis.io/docs/latest/operate/rc/security/access-control/multi-factor-authentication)\n\n\n\nAlso known as multi-master replication, Active-Active databases have multiple primary nodes (one on each participating cluster) to enable concurrent writes operations.\n\nMore info: [Multi-primary replication](https://redis.io/docs/latest/operate/rs/databases/active-active/#multi-primary-replication)\n\n\n\n\nAn abstraction used by Kubernetes to support multiple virtual clusters on the same physical cluster.\n\nMore info: [Namespaces](https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/)\n\n\n\nWhen features are removed from our products, they're generally replaced by new features that provide a better experience, more functionality, improved security, and other benefits.\n\nTo provide a transition period, we mark older features as _deprecated_ when introducing replacement features.  This gives you time to adjust your deployments, apps, and processes to support the new features.  During this transition, the older features continue to work as a courtesy.\n\nEventually, older features are removed from the product.  When this happens, they're considered _obsolete_, partly because they can no longer be used.\n\nFor best results, we advise against relying on deprecated features for any length of time.\n\n\n\nOperators are software extensions to Kubernetes that make use of custom resources to manage applications and their components.\n\nMore info: [operator pattern](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/)\n\n\n\nIf a member [Active-Active database](https://redis.io/docs/latest/#active-active) is in an out of memory situation, that member is marked \"inconsistent\" by Redis Enterprise Software, the member stops responding to user traffic, and the [syncer](https://redis.io/docs/latest/#syncer) initiates full reconciliation with other peers in the Active-Active database.\n\nMore info: [Active-Active database troubleshooting](https://redis.io/docs/latest/operate/rs/databases/active-active/syncer), [Memory management](https://redis.io/docs/latest/operate/rs/databases/memory-performance)"},{"id":"letter-p","title":"P - Q","role":"content","text":"The ability to store data permanently to disk so that it survives server restarts. Redis supports RDB snapshots and AOF (Append-Only File) persistence methods.\n\nMore info: [Data persistence](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence), [Redis Enterprise persistence](https://redis.io/docs/latest/operate/rs/databases/configure/database-persistence)\n\n\n\n[Clusters](https://redis.io/docs/latest/#cluster) participating in the [multi-primary replication](https://redis.io/docs/latest/#multi-primary-replication) of an [Active-Active database](https://redis.io/docs/latest/#active-active).\n\nMore info: [Active-Active geo-distributed Redis](https://redis.io/docs/latest/operate/rs/databases/active-active), [Create Active-Active database](https://redis.io/docs/latest/operate/rs/databases/active-active/create)\n\n\n\nA piece of storage in the cluster that has been provisioned by an administrator or dynamically provisioned using storage classes.\n\nMore info: [Persistent Volumes](https://kubernetes.io/docs/concepts/storage/persistent-volumes/)\n\n\n\nA request for storage by a user that serves as an abstract representation of PersistentVolume (PV) resources. PVCs consume PV resources and can request specific size and access modes.\n\nMore info: [Persistent Volumes](https://kubernetes.io/docs/concepts/storage/persistent-volumes/)\n\n\n\nThe smallest deployable units of computing that you can create and manage in Kubernetes. A pod represents a single instance of a running process in your cluster.\n\nMore info: [Pods](https://kubernetes.io/docs/concepts/workloads/pods/)\n\n\n\nDeciding where [shards](https://redis.io/docs/latest/#shard) will be created and placed within a [cluster](https://redis.io/docs/latest/#cluster).\n\nMore info: [Database operations](https://redis.io/docs/latest/operate/rs/databases), [Create databases](https://redis.io/docs/latest/operate/rs/databases/create)\n\n\n\nDetermines the number and location of active proxies tied to a single endpoint that receive incoming traffic for a database.\n\nFor more info, see [Proxy policy](https://redis.io/docs/latest/operate/rs/databases/configure/proxy-policy).\n\n\n\n[Node](https://redis.io/docs/latest/#redis-enterprise-nodes) provisioned only for [cluster](https://redis.io/docs/latest/#cluster) operations that can be elected as a [master node](https://redis.io/docs/latest/#master-node). The quorum node participates in the cluster quorum and must be explicitly assigned this role via the `rladmin` command.\n\nMore info: [Redis Enterprise cluster architecture](https://redis.io/docs/latest/operate/rs/clusters), [rladmin command reference](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin)"},{"id":"letter-r","title":"R","role":"content","text":"A collection of Redis modules that extends Redis with modern data models and processing engines. Includes capabilities for document databases, graph databases, time series, and more.\n\nMore info: [Redis Stack](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise), [Redis Stack features](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/enterprise-capabilities)\n\n\n\nRedis Enterprise feature that helps to ensure high availability in the event of a rack or zone failure. In the event of a rack or zone failure, the replicas and endpoints in the remaining racks/zones will be promoted.\n\nMore info: [Rack-zone awareness in Redis Enterprise Software](https://redis.io/docs/latest/operate/rs/clusters/configure/rack-zone-awareness)\n\n\n\n[Databases](https://redis.io/docs/latest/#redis-enterprise-database) using [replication](https://redis.io/docs/latest/#replication) or [Active-Active](https://redis.io/docs/latest/#active-active) maintain a backlog to synchronize the primary and replica [shards](https://redis.io/docs/latest/#shard).\n\nMore info: [Database replication](https://redis.io/docs/latest/operate/rs/databases/durability-ha/replication), [Active-Active databases](https://redis.io/docs/latest/operate/rs/databases/active-active)\n\n\n\nDistributing keys and their values among new [shards](https://redis.io/docs/latest/#shard) to optimize performance and resource utilization.\n\nMore info: [Database clustering](https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering), [Database operations](https://redis.io/docs/latest/operate/rs/databases)\n\n\n\nMoving [shards](https://redis.io/docs/latest/#shard) to [nodes](https://redis.io/docs/latest/#redis-enterprise-nodes) where more resources are available.\n\nMore info: [Database operations](https://redis.io/docs/latest/operate/rs/databases), [Cluster management](https://redis.io/docs/latest/operate/rs/clusters)\n\n\n\nThe cloud version of Redis Enterprise.\n\nMore info: [Redis Cloud](https://redis.io/docs/latest/operate/rc), [Redis Cloud quick start](https://redis.io/docs/latest/operate/rc/rc-quickstart)\n\n\n\nCollection of [Redis Enterprise nodes](https://redis.io/docs/latest/#redis-enterprise-nodes). A [cluster](https://redis.io/docs/latest/#cluster) pools system resources across nodes in the cluster and supports multi-tenant [database](https://redis.io/docs/latest/#redis-enterprise-database) instances.\n\nMore info: [Redis Enterprise cluster architecture](https://redis.io/docs/latest/operate/rs/clusters), [Cluster management](https://redis.io/docs/latest/operate/rs/clusters)\n\n\n\nLogical entity that manages your entire dataset across multiple [Redis instances](https://redis.io/docs/latest/#redis-instance). It segments the data into [shards](https://redis.io/docs/latest/#shard) and distributes them among [nodes](https://redis.io/docs/latest/#redis-enterprise-nodes).\n\nMore info: [Database operations](https://redis.io/docs/latest/operate/rs/databases), [Create databases](https://redis.io/docs/latest/operate/rs/databases/create)\n\n\n\nPhysical or virtual machines or containers that runs a collection of Redis Enterprise services within a [cluster](https://redis.io/docs/latest/#cluster).\n\nMore info: [Redis Enterprise cluster architecture](https://redis.io/docs/latest/operate/rs/clusters), [Install and setup](https://redis.io/docs/latest/operate/rs/installing-upgrading)\n\n\n\nThe on-premises version of Redis Enterprise.\n\nMore info: [Redis Enterprise Software](https://redis.io/docs/latest/operate/rs), [Install Redis Enterprise Software](https://redis.io/docs/latest/operate/rs/installing-upgrading)\n\n\n\nSingle-threaded Redis Open Source database. Redis OSS was renamed Redis Open Source with the Redis 8 in Redis Open Source release.\n\nMore info: [Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack), [Database clustering](https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering)\n\n\n\nPreviously known as Redis on Flash. Enables your Redis databases to span both RAM and dedicated flash memory (SSD). Auto Tiering manages the location of key values (RAM vs Flash) in the database via a LRU-based (least-recently-used) mechanism.\n\nMore info: [Auto Tiering](https://redis.io/docs/latest/operate/rs/databases/flash/), [Auto Tiering  quick start](https://redis.io/docs/latest/operate/rs/databases/flash/quickstart)\n\n\n\nHigh availability feature of Redis Enterprise Software. After a node failure, the cluster automatically migrates remaining replica shards to available nodes. Previously known as \"Slave HA\" or `slave_ha`.\n\nMore info: [High availability for replica shards](https://redis.io/docs/latest/operate/rs/databases/configure/replica-ha)\n\n\n\nThe Redis Enterprise implementation of active-passive database replication.\n\nMore info: [Replica Of\n](https://redis.io/docs/latest/operate/rs/databases/import-export/replica-of/)\n\n\n\nA ReplicaSet is a type of Kubernetes resource that (aims to) maintain a set of replica pods running at any given time.\n\nMore info: [ReplicaSet](https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/)\n\n\n\n\nDatabase replication provides a mechanism to ensure high availability. When replication is enabled, your dataset is replicated to a replica shard,\nwhich is constantly synchronized with the primary shard. If the primary\nshard fails, an automatic failover happens and the replica shard is promoted.\n\nMore info: [Database replication](https://redis.io/docs/latest/operate/rs/databases/durability-ha/replication)\n\n\n\nKubernetes objects that grant the permissions defined in a role to a user or set of users, including service accounts.\n\nMore info: [RoleBinding and ClusterRoleBinding](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#rolebinding-and-clusterrolebinding)\n\n\n\nA security approach that restricts system access to authorized users.\n\nMore info: [RBAC wikipedia](https://en.wikipedia.org/wiki/Role-based_access_control); [Database access control](https://redis.io/docs/latest/operate/rs/security/access-control); [Role-based access control](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/role-based-access-control)\n\n\n\nKubernetes objects that contain rules representing a set of permissions. Roles define what actions can be performed on which resources.\n\nMore info: [Role and ClusterRole](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#role-and-clusterrole)"},{"id":"letter-s","title":"S","role":"content","text":"The ability to query and retrieve data using various search methods including full-text search, vector search, and secondary indexing. Redis provides search capabilities through its Search and Query features.\n\nMore info: [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query), [RedisSearch](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search)\n\n\n\nCreating additional data structures to enable fast lookups on fields other than the primary key. Redis supports secondary indexing for JSON documents, hashes, and other data types.\n\nMore info: [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query), [Indexing concepts](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts)\n\n\n\nKubernetes term for object that stores sensitive information, such as passwords, OAuth tokens, and ssh keys.\n\nMore info: [Secrets](https://kubernetes.io/docs/concepts/configuration/secret/)\n\n\n\nProvides an identity for processes that run in a pod, allowing them to access the Kubernetes API and other resources.\n\nMore info: [Service Accounts](https://kubernetes.io/docs/concepts/security/service-accounts/)\n\n\n\nAn abstract way to expose an application running on a set of pods as a network service in Kubernetes.\n\nMore info: [Services](https://kubernetes.io/docs/concepts/services-networking/service/)\n\n\n\nRedis process that is part of the Redis clustered database.\n\nMore info: [Database clustering](https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering), [terminology](https://redis.io/docs/latest/operate/rs/references/terminology)\n\n\n\nTechnique that has been used to scale larger data storage and processing loads. Sharding take your data, partitions it into smaller pieces and then send the data to different locations depending on which partition the data has been assigned to. Related to [hash slots](https://redis.io/docs/latest/#hash-slot) and [shards](https://redis.io/docs/latest/#shard).\n\nMore info: [Database clustering](https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering), [Shards](https://redis.io/docs/latest/#shard)\n\n\n\nFramework for adding authentication support and data security to connection-based protocols via replaceable mechanisms.\n\nMore info: [SASL wikipedia](https://en.wikipedia.org/wiki/Simple_Authentication_and_Security_Layer)\n\n\n\nData persistence file that performs a data dump every one, six, or twelve hours. Used alongside [append-only file (AoF)](https://redis.io/docs/latest/#append-only-file-aof) for data persistence.\n\nMore info: [Data persistence](https://redis.io/docs/latest/operate/rs/databases/configure/database-persistence), [Append-only file (AoF)](https://redis.io/docs/latest/#append-only-file-aof)\n\n\n\nA way for administrators to describe the classes of storage they offer in Kubernetes, including different quality-of-service levels, backup policies, or arbitrary policies.\n\nMore info: [Storage Classes](https://kubernetes.io/docs/concepts/storage/storage-classes/)\n\n\n\nProcess on each node hosting an Active-Active database instance that synchronizes a backlog of operations between participating clusters.\n\nMore info: [Syncer process](https://redis.io/docs/latest/operate/rs/databases/active-active/syncer)"},{"id":"letter-t","title":"T - Z","role":"content","text":"The number of operations or requests that can be processed per unit of time. Redis is designed for high throughput, capable of handling hundreds of thousands to millions of operations per second.\n\nMore info: [Redis benchmarks](https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/benchmarks), [Performance optimization](https://redis.io/docs/latest/operate/oss_and_stack/management/optimization)\n\n\n\nA sequence of data points indexed in time order. Redis provides native time series support through RedisTimeSeries for storing and querying time-stamped data.\n\nMore info: [Time series data type](https://redis.io/docs/latest/develop/data-types/timeseries), [RedisTimeSeries](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/timeseries)\n\n\n\nA key that is logically deleted but stays in memory until it is collected by the garbage collector. Common in [Active-Active databases](https://redis.io/docs/latest/#active-active) during conflict resolution.\n\nMore info: [Active-Active database development](https://redis.io/docs/latest/operate/rs/databases/active-active/develop), [Conflict-free replicated data types (CRDT)](https://redis.io/docs/latest/#conflict-free-replicated-data-types-crdt)\n\n\n\nProtocols that provide communications security over a computer network.\n\nMore info: [TLS wikipedia](https://en.wikipedia.org/wiki/Transport_Layer_Security), [Cloud database TLS](https://redis.io/docs/latest/operate/rc/security/database-security/tls-ssl), [Redis Enterprise TLS](https://redis.io/docs/latest/operate/rs/security/encryption/tls)\n\n\n\nA search method that finds similar items by comparing high-dimensional vectors representing data features. Redis supports vector search for AI applications, machine learning, and similarity matching.\n\nMore info: [Vector search](https://redis.io/docs/latest/develop/ai/search-and-query/vectors), [Vector indexing](https://redis.io/docs/latest/develop/ai/search-and-query/query/vector-search)\n\n\n\nNetworking connection between two VPCs that enables you to route traffic between them using private IP addresses. Instances in either VPC can communicate with each other as if they are within the same network.\n\nMore info: [VPC wikipedia](https://en.wikipedia.org/wiki/Virtual_private_cloud), [Enable VPC peering](https://redis.io/docs/latest/operate/rc/security/vpc-peering)\n\n\n</dl>"}],"examples":[]}
{"schema_version":2,"id":"integrate/amazon-bedrock/create-agent","title":"Create a Bedrock agent","url":"https://redis.io/docs/latest/integrate/amazon-bedrock/create-agent/","summary":"Shows how to set up your Agent in Amazon Bedrock.","aliases":["/integrate/aws-bedrock/create-agent/"],"tags":["docs","integrate","oss","rs","rc"],"last_updated":"2026-10-02T14:54:03+01:00","page_type":"content","content_hash":"92cb856369067b40ee62a9525a26ee1b877a90f881cc0cbdf56bfac0eced0ff8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"After you have [created a knowledge base](https://redis.io/docs/latest/integrate/amazon-bedrock/create-knowledge-base), you can use it to create an agent on Amazon Bedrock.\n\nBefore you begin this guide, you will need:\n\n- An [AWS IAM Role](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-service.html) with [permissions for the Bedrock agent](https://docs.aws.amazon.com/bedrock/latest/userguide/security_iam_id-based-policy-examples.html).\n\n- A [Bedrock knowledge base](https://redis.io/docs/latest/integrate/amazon-bedrock/create-knowledge-base) connected to a [Redis Cloud vector database](https://redis.io/docs/latest/integrate/amazon-bedrock/set-up-redis)."},{"id":"create-an-agent","title":"Create an agent","role":"content","text":"1. Sign in to the [AWS console](https://console.aws.amazon.com/). \n\n1. Use the **Services** menu to locate and select **Machine Learning** > **Amazon Bedrock**.  This takes you to the Amazon Bedrock admin panel.\n\n1. Select **Agents** > **Create Agent** to create your knowledge base.\n\n    ![The Create Agent button.](https://redis.io/docs/latest/images/rc/bedrock-aws-button-create-agent.png)\n\n1. In the **Agent name** section, enter a name and description for your agent.\n\n1. Select whether or not you want the agent to be able to ask for additional information in the **User input** section.\n\n1. Select the IAM role for the Bedrock agent in the **IAM Permissions** section. \n\n1. Choose how long you want your idle session timeout to be in the **Idle session timeout** section. Select **Next** to continue.\n\n1. In the **Model details** section, choose which model you want to use and enter the instructions for your agent. Select **Next** to continue.\n\n1. In the **Action groups** section, you may specify any tasks you would like the agent to perform. Select **Next** to continue.\n\n1. Select the [knowledge base](#create-a-knowledge-base) you created and summarize the information in the knowledge base in the **Knowledge base instructions for Agent** form. Select **Add another knowledge base** if you would like to add multiple knowledge bases. \n\n     ![The Add another knowledge base button.](https://redis.io/docs/latest/images/rc/bedrock-aws-button-add-knowledge-base.png)\n\n    Select **Next** to continue.\n\n1. Review your agent before you create it. Select **Create Agent** to finish creation.\n\n    ![The Create Agent button.](https://redis.io/docs/latest/images/rc/bedrock-aws-button-create-agent.png)\n\nAmazon Bedrock will create your agent and link it to your knowledge base. This will take some time. \n\nYour agent will have a status of **Ready** when it is ready to be tested. \n\n![A Bedrock agent with a Ready status.](https://redis.io/docs/latest/images/rc/bedrock-aws-status-agent-ready.png)\n\nSelect the name of your agent to view the versions and draft aliases of your agent. You can also test your agent by entering prompts in the **Enter your message here** field."}],"examples":[]}
{"schema_version":2,"id":"integrate/amazon-bedrock/create-knowledge-base","title":"Create a Bedrock knowledge base","url":"https://redis.io/docs/latest/integrate/amazon-bedrock/create-knowledge-base/","summary":"Shows how to set up your Knowledge base in Amazon Bedrock.","aliases":["/integrate/aws-bedrock/create-knowledge-base/"],"tags":["docs","integrate","oss","rs","rc"],"last_updated":"2026-10-02T14:54:03+01:00","page_type":"content","content_hash":"0aff1b57b05a365d6897aebe7588dd92f4b82a6905f71ef554d1bbc8c460e51b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"After you have set up a vector database with Redis Cloud, you can use it to create a knowledge base for your models.\n\nBefore you begin this guide, you will need:\n\n- An [AWS S3 Bucket](https://docs.aws.amazon.com/AmazonS3/latest/userguide/creating-buckets-s3.html) with text data that you want to use to train your models.\n\n- An [AWS IAM Role](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-service.html) with permissions for the Bedrock knowledge base.\n\n- A Redis database that is [set up for Amazon Bedrock](https://redis.io/docs/latest/integrate/amazon-bedrock/set-up-redis)"},{"id":"create-knowledge-base","title":"Create knowledge base","role":"content","text":"To use your Redis database to create a knowledge base on Amazon Bedrock:\n\n1. Sign in to the [AWS console](https://console.aws.amazon.com/). \n\n1. Use the **Services** menu to locate and select **Machine Learning** > **Amazon Bedrock**.  This takes you to the Amazon Bedrock admin panel.\n\n1. Select **Knowledge base** > **Create knowledge base** to create your knowledge base.\n\n    ![The Create knowledge base button.](https://redis.io/docs/latest/images/rc/bedrock-aws-button-create-knowledge-base.png)\n\n1. In the **Knowledge base details** section, enter a name and description for your knowledge base. \n\n1. Select the IAM role for the Bedrock knowledge base in the **IAM Permissions** section. Select **Next** to add the data source.\n\n1. Enter a name for the data source and connect your S3 bucket in the **Data source** section.\n\n1. In the **Vector database** section, select **Redis Cloud** and select the checkbox to agree with the legal disclaimer.\n\n    ![The Redis Cloud selection for your vector database.](https://redis.io/docs/latest/images/rc/bedrock-aws-select-redis-vector-db.png)\n\n    Fill in the fields with the following information:\n\n    - **Endpoint URL**: Public endpoint of your database. This can be found in the [Redis Cloud console](https://cloud.redis.io/) from the database list or from the **General** section of the **Configuration** tab for the source database.\n    - **Credentials Secret ARN**: [Amazon Resource Name (ARN)](https://docs.aws.amazon.com/secretsmanager/latest/userguide/reference_iam-permissions.html#iam-resources) of your [database credentials secret](https://redis.io/docs/latest/integrate/amazon-bedrock/set-up-redis#store-secret).\n    - **Vector Index name**: Name of the [vector index](https://redis.io/docs/latest/integrate/amazon-bedrock/set-up-redis#create-vector-index) \n    - **Vector field**: Name of the [vector field](https://redis.io/docs/latest/integrate/amazon-bedrock/set-up-redis#create-vector-index) of the vector index\n    - **Text field**: Name of the [text field](https://redis.io/docs/latest/integrate/amazon-bedrock/set-up-redis#create-vector-index) of the vector index\n    - **Metadata field**: Name of the [metadata field](https://redis.io/docs/latest/integrate/amazon-bedrock/set-up-redis#create-vector-index) of the vector index\n\n    Select **Next** to review your settings.\n\n1. Review your knowledge base before you create it. Select **Create knowledge base** to finish creation.\n\n    ![The Create knowledge base button.](https://redis.io/docs/latest/images/rc/bedrock-aws-button-create-knowledge-base.png)\n\nAmazon Bedrock will sync the data from the S3 bucket and load it into your Redis database. This will take some time.\n\nYour knowledge base will have a status of **Ready** when it is ready to be connected to an Agent.\n\n![A Bedrock knowledge base with a Ready status.](https://redis.io/docs/latest/images/rc/bedrock-aws-status-knowledge-base-ready.png)\n\nSelect the name of your knowledge base to view the syncing status of your data sources. The data source will have a status of **Ready** when it is synced to the vector database.\n\n![A Bedrock data source with a Ready status.](https://redis.io/docs/latest/images/rc/bedrock-aws-status-data-source-ready.png)\n\nAfter the knowledge base is ready, you can use it to [Create an agent](https://redis.io/docs/latest/integrate/amazon-bedrock/create-agent)."}],"examples":[]}
{"schema_version":2,"id":"integrate/amazon-bedrock","title":"Amazon Bedrock","url":"https://redis.io/docs/latest/integrate/amazon-bedrock/","summary":"Shows how to use your Redis database with Amazon Bedrock to customize foundational models.","aliases":["/integrate/aws-bedrock/"],"tags":["docs","integrate","oss","rs","rc"],"last_updated":"2026-09-17T14:55:03+01:00","children":[{"id":"integrate/amazon-bedrock/set-up-redis","summary":"Shows how to set up your Redis database for Amazon Bedrock.","title":"Set up Redis for Bedrock","url":"https://redis.io/docs/latest/integrate/amazon-bedrock/set-up-redis/"},{"id":"integrate/amazon-bedrock/create-knowledge-base","summary":"Shows how to set up your Knowledge base in Amazon Bedrock.","title":"Create a Bedrock knowledge base","url":"https://redis.io/docs/latest/integrate/amazon-bedrock/create-knowledge-base/"},{"id":"integrate/amazon-bedrock/create-agent","summary":"Shows how to set up your Agent in Amazon Bedrock.","title":"Create a Bedrock agent","url":"https://redis.io/docs/latest/integrate/amazon-bedrock/create-agent/"}],"page_type":"content","content_hash":"fd63f129ee17f1d313a0158212a6fb5a1c103b7a790466891b85fd5a3db0bab8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Amazon Bedrock](https://aws.amazon.com/bedrock/) streamlines GenAI deployment by offering foundational models (FMs) as a unified API, eliminating complex infrastructure management. It lets you create AI-powered [Agents](https://aws.amazon.com/bedrock/agents/) that execute complex tasks. Through [Knowledge Bases](https://aws.amazon.com/bedrock/knowledge-bases/) within Amazon Bedrock, you can seamlessly tether FMs to your proprietary data sources using retrieval-augmented generation (RAG). This direct integration amplifies the FM's intelligence based on your organization's resources.\n\nAmazon Bedrock lets you choose Redis Cloud as the [vector database](https://redis.io/solutions/vector-search/) for your agent's Knowledge Base. Once Redis Cloud is integrated with Amazon Bedrock, it automatically reads text documents from your Amazon Simple Storage Service (S3) buckets. This process lets the large language model (LLM) pinpoint and extract pertinent context in response to user queries, ensuring your AI agents are well-informed and grounded in their responses.\n\nFor more information about the Redis integration with Amazon Bedrock, see the [Amazon Bedrock integration blog post](https://redis.io/blog/amazon-bedrock-integration-with-redis-enterprise/).\n\nTo fully set up Bedrock with Redis Cloud, you will need to do the following:\n\n1. [Set up a Redis Cloud subscription and vector database](https://redis.io/docs/latest/integrate/amazon-bedrock/set-up-redis) for Bedrock.\n\n1. [Create a knowledge base](https://redis.io/docs/latest/integrate/amazon-bedrock/create-knowledge-base) connected to your vector database.\n\n1. [Create an agent](https://redis.io/docs/latest/integrate/amazon-bedrock/create-agent) connected to your knowledge base."},{"id":"more-info","title":"More info","role":"content","text":"- [Amazon Bedrock integration blog post](https://redis.io/blog/amazon-bedrock-integration-with-redis-enterprise/)\n- [Detailed steps](https://github.com/redis-applied-ai/aws-redis-bedrock-stack/blob/main/README.md)"}],"examples":[]}
{"schema_version":2,"id":"integrate/amazon-bedrock/set-up-redis","title":"Set up Redis for Bedrock","url":"https://redis.io/docs/latest/integrate/amazon-bedrock/set-up-redis/","summary":"Shows how to set up your Redis database for Amazon Bedrock.","aliases":["/integrate/aws-bedrock/set-up-redis/"],"tags":["docs","integrate","oss","rs","rc"],"last_updated":"2026-10-06T08:53:14-07:00","page_type":"content","content_hash":"67587293246006152ceace8c81a4588a4ae4cb8a6ab75f857c9533e3c1d331c8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You need to set up your Redis Cloud database before you can set it as the vector database in Amazon Bedrock. To do this, you need to:\n\n1. [Sign up for Redis Cloud and create a database](#sign-up-create-subscription)\n1. [Enable Transport Layer Security (TLS) for the database and save the certificates](#get-certs)\n1. [Store database credentials in AWS secrets manager](#store-secret)\n1. [Create a vector index in your database](#create-vector-index) for Bedrock to use\n\nAfter you set up the database, you can use the database information to set it as your knowledge base database when you [create a knowledge base](https://redis.io/docs/latest/integrate/amazon-bedrock/create-knowledge-base)."},{"id":"sign-up-create-subscription","title":"Sign up and create a database","role":"content","text":"To set up a Redis Cloud instance for Bedrock, you need to:\n\n1. [Sign up for Redis Cloud](#sign-up) if you do not already have an account.\n1. [Create a database](#create-sub) to use for your Bedrock knowledge base."},{"id":"sign-up","title":"Sign up for Redis Cloud using AWS Marketplace","role":"content","text":"1.  Select the [Redis Cloud](https://aws.amazon.com/marketplace/pp/prodview-mwscixe4ujhkq?sr=0-1&ref_=beagle&applicationId=AWSMPContessa) AWS marketplace link from Bedrock to be taken to the Redis Cloud plan listing.\n\n    ![The Redis Cloud listing on AWS Marketplace](https://redis.io/docs/latest/images/rc/aws-marketplace-rc-listing.png)\n\n1.  Subscribe to Redis Cloud listing, locate the **Set Up Your Account** button, and then select it to begin mapping your Redis Cloud account with your AWS Marketplace account.\n\n    ![Use the Set Up Your Account button after subscribing to Redis Cloud with your AWS Marketplace account.](https://redis.io/docs/latest/images/rc/aws-marketplace-account-setup-button.png)\n\n1.  Sign in to the [Redis Cloud console](https://cloud.redis.io).\n\n1.  Select the Redis account to be mapped to your AWS Marketplace account and confirm that your payment method will change and that the connection cannot be undone.\n\n    ![Use the AWS Marketplace dialog to map your Redis Cloud account to your AWS Marketplace account.](https://redis.io/docs/latest/images/rc/aws-marketplace-map-account-dialog.png)\n\n1.  Use the **Map account** button to confirm your choice.\n\n1.  Once your Redis account is mapped to your AWS Marketplace account, a message appears in the upper, left corner of the account panel.\n\n    ![The AWS Marketplace badge appears when your Redis Cloud account is mapped to an AWS Marketplace account.](https://redis.io/docs/latest/images/rc/aws-marketplace-billing-badge.png)\n\n    In addition, AWS Marketplace is reported as the selected payment method."},{"id":"create-sub","title":"Create a database","role":"content","text":"1. In the [Redis Cloud console](https://cloud.redis.io/), select **New database**. \n\n    ![The New Database button creates a new database.](https://redis.io/docs/latest/images/rc/button-database-new.png)\n\n1. When the **New database** page appears, select **Pro** to create a Pro plan.\n\n    ![The Subscription selection panel with Pro selected.](https://redis.io/docs/latest/images/rc/create-database-subscription-pro-new.png)\n\n1. After you select **Pro**, the **Database settings** section will appear. For this guide, continue with **Easy create** to get started faster.\n\n    ![The database settings section.](https://redis.io/docs/latest/images/rc/create-pro-db-settings.png)\n\n    If you'd like to select all of the configuration options yourself, select **Custom settings**. See [Create a Redis Cloud Pro database](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new#custom-settings) for more details.\n\n1. Redis will generate a database name for you. If you want to change it, you can do so in the **Database name** field.\n\n    ![The database name, cloud vendor and region settings.](https://redis.io/docs/latest/images/rc/pro-easy-create-vendor.png)\n\n1. Select **Amazon Web Services** as the cloud vendor and select a region.\n\n1. In the **Database Sizing** section:\n\n    ![The Dataset size, throughput, and High availability settings.](https://redis.io/docs/latest/images/rc/pro-easy-create-size-throughput.png)\n \n    - Select [**High availability**](https://redis.io/docs/latest/operate/rc/databases/configuration/high-availability) if it is not already selected.\n    - Set the Dataset size of your database based on the amount of data that Bedrock will pull from your Simple Storage Service (S3) [bucket](https://docs.aws.amazon.com/AmazonS3/latest/userguide/creating-buckets-s3.html). See [Find out the size of your S3 buckets](https://aws.amazon.com/blogs/storage/find-out-the-size-of-your-amazon-s3-buckets/) to find out how much knowledge base data is stored in your S3 bucket and pick the closest size, rounded up, from the table below. \n\n        | Total Size of Documents in S3 | Database size without replication | Database size with replication |\n|-------------------------------|-----------------------------------|--------------------------------|\n| 10,000 kb                     | 135 Mb                             | 270 Mb                         |\n| 100,000 kb                    | 1.35 Gb                            | 2.7 Gb                         |\n| 1,000,000 kb                  | 13.5 Gb                              | 27 Gb                          |\n| 10,000,000 kb                 | 135 Gb                             | 270 Gb                         |\n\n        For more information on sizing, see the [Bedrock integration blog post](https://redis.io/blog/amazon-bedrock-integration-with-redis-enterprise/#right-size-your-database-for-amazon-bedrock).\n\n1. Select **View all settings** to review the database settings that we selected for you.\n\n    ![The optimal database settings.](https://redis.io/docs/latest/images/rc/pro-easy-create-optimal-settings.png)\n\n    If you want to change these settings, select [**Switch to custom settings**](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new#custom-settings).\n\n1. You will not need to enter a payment method, as it's automatically assigned to your AWS Marketplace account. Select **Confirm & pay** to create your new database.\n\n    ![Select Confirm & pay to create your new database.](https://redis.io/docs/latest/images/rc/button-create-db-confirm-pay.png)\n\n    Note that databases are created in the background.  While they are provisioning, you aren't allowed to make changes.  (The process generally takes 10-15 minutes.)\n\n    Use the **Databases list** to check the status of your subscription.  You will also receive an email when your database is ready to use."},{"id":"get-certs","title":"Enable TLS and get certificates","role":"content","text":"For your database to be fully secure, you must enable [Transport Layer Security (TLS)](https://redis.io/docs/latest/operate/rc/security/database-security/tls-ssl#enable-tls) for your database with client authentication.\n\n1. Select **Databases** from the [Redis Cloud console](https://cloud.redis.io/) menu and then select your database from the list.\n\n1. From the database's **Configuration** screen, select the **Edit** button:\n\n    ![The Edit database button lets you change selected database properties.](https://redis.io/docs/latest/images/rc/button-database-edit.png)\n\n1. In the **Security** section, use the **Transport layer security (TLS)** toggle to enable TLS:\n\n    ![Use the Transport Layer Security toggle to enable TLS.](https://redis.io/docs/latest/images/rc/database-details-configuration-tab-security-tls-toggle.png)\n\n1. Select **Download server certificate** to download the Redis Cloud certificate bundle `redis_ca.pem`:\n\n    ![Use the Download server certificate button to download the Redis Cloud CA certificates.](https://redis.io/docs/latest/images/rc/button-database-config-security-server-ca-download.png)\n\n1. Select the **Mutual TLS (require client authentication)** checkbox to require client authentication.\n\n1. Select **Add client certificate** to add a certificate.\n\n    ![The Add client certificate button.](https://redis.io/docs/latest/images/rc/mtls-add-client-certificate.png)\n\n1. Either provide an [X.509 client certificate](https://en.wikipedia.org/wiki/X.509) or chain in PEM format for your client or select **Generate** to create one:\n\n    ![Provide or generate a certificate for Mutual TLS.](https://redis.io/docs/latest/images/rc/database-details-configuration-tab-security-tls-client-auth-certificate.png)\n\n    - If you generate your certificate from the Redis Cloud console, a **Download certificate** button will appear after it is generated. Select it to download the certificate. \n\n        ![The Download certificate button.](https://redis.io/docs/latest/images/rc/mtls-download-certificate.png)\n        \n        The download contains:\n\n        - `redis-db-<database_id>.crt` – the certificate's public key.\n\n        - `redis-db-<database_id>.key` – the certificate's private key.\n\n        > [!NOTE]\n        > You must download the certificate using the button at this point.  After your changes have been applied, the full bundle of public and private keys will no longer be available for download.\n    \n    - If you provide a client certificate, you will see the certificate details before you save your changes.\n\n        ![The Download certificate button.](https://redis.io/docs/latest/images/rc/mtls-certificate-details.png)\n\n1. To apply your changes and enable TLS, select the **Save database** button:\n\n    ![Use the Save database button to save database changes.](https://redis.io/docs/latest/images/rc/button-database-save.png)"},{"id":"store-secret","title":"Store database credentials in AWS secrets manager","role":"content","text":"In the [AWS Management Console](https://console.aws.amazon.com/), use the **Services** menu to locate and select **Security, Identity, and Compliance** > **Secrets Manager**. [Create a secret](https://docs.aws.amazon.com/secretsmanager/latest/userguide/create_secret.html) of type **Other type of secret** with the following key/value fields:\n\n- `username`: Database username\n- `password`: Database password\n- `serverCertificate`: Contents of the [server certificate](https://redis.io/docs/latest/operate/rc/security/database-security/tls-ssl#download-certificates) (`redis_ca.pem`)\n- `clientCertificate`: Contents of the client certificate (`redis_user.crt`)\n- `clientPrivateKey`: Contents of the client private key (`redis_user_private.key`)\n\nAfter you store this secret, you can view and copy the [Amazon Resource Name (ARN)](https://docs.aws.amazon.com/secretsmanager/latest/userguide/reference_iam-permissions.html#iam-resources) of your secret on the secret details page."},{"id":"create-vector-index","title":"Create a vector index in your database","role":"content","text":"After your Redis Cloud database is set up, create a search index with a vector field using [FT.CREATE](https://redis.io/docs/latest/commands/ft.create) as your knowledge base for Amazon Bedrock. You can accomplish this using **Redis Insight** or `redis-cli`."},{"id":"redis-insight","title":"Redis Insight","role":"content","text":"[Redis Insight](https://redis.io/docs/latest/develop/tools/insight) is a free Redis GUI that allows you to visualize and optimize your data in Redis. \n\nTo create your vector index in Redis Insight:\n\n1. [Download and install Redis Insight](https://redis.io/insight/) if you don't have it already.\n\n1. In the [Redis Cloud console](https://cloud.redis.io/), in your database's **Configuration** tab, select the **Connect** button next to your database to open the connection wizard.\n\n    ![Connect button.](https://redis.io/docs/latest/images/rc/button-connect.png#no-click)\n\n1. In the connection wizard, under **Redis Insight Desktop**, select **Public Endpoint**. Select **Open with Redis Insight** to connect to the database with Redis Insight.\n\n1. Select **Use TLS**. In the **CA Certificate** section, select **Add new CA certificate**. Give the certificate a name in the **Name** field, and enter the contents of `redis_ca.pem` into the **Certificate** field.\n\n    ![The Redis Insight Add CA Certificate section.](https://redis.io/docs/latest/images/rc/ri-bedrock-add-ca-cert.png)\n\n1. Select **Requires TLS Client Authentication**. In the **Client Certificate** section, select **Add new certificate**. Give the certificate a name in the **Name** field. Enter the contents of `redis_user.crt` into the **Certificate** field, and the contents of `redis_user_private.key` into the **Private Key** field.\n\n    ![The Redis Insight Add Client Certificate section.](https://redis.io/docs/latest/images/rc/ri-bedrock-add-client-cert.png)\n\n1. Select **Add Redis Database** to connect to the database.\n\n1. Select your database alias to connect to your database. Select the **Workbench** icon to go to the workbench.\n\n    ![The Redis Insight workbench icon.](https://redis.io/docs/latest/images/rc/ri-bedrock-workbench.png)\n\n1. Enter the [FT.CREATE](https://redis.io/docs/latest/commands/ft.create) command to create an index. \n\n    [code example]\n\n    Replace the following fields:\n\n    - `<index_name>` with the vector index name\n    - `<text_field>` with the text field name\n    - `<metadata_field>` with the metadata field name\n    - `<vector_field>` with the vector field name\n\n1. Select **Run** to create the index.\n\n    ![The Redis Insight run button.](https://redis.io/docs/latest/images/rc/ri-bedrock-run-button.png)"},{"id":"redis-cli","title":"`redis-cli`","role":"content","text":"The [`redis-cli`](https://redis.io/docs/latest/develop/tools/cli) command-line utility lets you connect and run Redis commands directly from the command line. To use `redis-cli`, you can [install Redis](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules).\n\nPublic endpoint and port details are available from the **Databases** list or the database's **Configuration** screen. Select **Connect** to view how to connect to your database with `redis-cli`.\n\n[code example]\n\nAfter you are connected with `redis-cli`, create an index using [FT.CREATE](https://redis.io/docs/latest/commands/ft.create). \n\n[code example]\n\nReplace the following fields:\n- `<index_name>` with the vector index name\n- `<text_field>` with the text field name\n- `<metadata_field>` with the metadata field name\n- `<vector_field>` with the vector field name"},{"id":"next-steps","title":"Next steps","role":"content","text":"After your Redis database is set up, you can use it to [create a knowledge base](https://redis.io/docs/latest/integrate/amazon-bedrock/create-knowledge-base) in Amazon Bedrock."}],"examples":[{"id":"redis-insight-ex0","language":"text","code":"FT.CREATE <index_name>                    \n        ON HASH                \n        SCHEMA\n            \"<text_field>\" TEXT\n            \"<metadata_field>\" TEXT                   \n            \"<vector_field>\"  VECTOR FLAT     \n                6                          \n                \"TYPE\" \"FLOAT32\"            \n                \"DIM\" 1536                   \n                \"DISTANCE_METRIC\" \"COSINE\"","section_id":"redis-insight"},{"id":"redis-cli-ex0","language":"sh","code":"redis-cli -h <endpoint> -p <port> --tls --cacert redis_ca.pem \\\n    --cert redis_user.crt --key redis_user_private.key","section_id":"redis-cli"},{"id":"redis-cli-ex1","language":"text","code":"FT.CREATE <index_name>                    \n    ON HASH                \n    SCHEMA\n        \"<text_field>\" TEXT\n        \"<metadata_field>\" TEXT                   \n        \"<vector_field>\"  VECTOR FLAT     \n            6                          \n            \"TYPE\" \"FLOAT32\"            \n            \"DIM\" 1536                   \n            \"DISTANCE_METRIC\" \"COSINE\"","section_id":"redis-cli"}]}
{"schema_version":2,"id":"integrate/aws-redis-cloud","title":"Redis Cloud on AWS","url":"https://redis.io/docs/latest/integrate/aws-redis-cloud/","summary":"Deploy Redis Cloud on Amazon Web Services","tags":["docs","integrate","rc"],"last_updated":"2026-09-17T14:54:42+01:00","children":[],"page_type":"content","content_hash":"c2944a77b027557ad5fff074148dfde0e1781a36d41e264103c33818e2083d71","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"Deploy Redis Cloud on Amazon Web Services (AWS) to leverage Redis's performance with AWS's global infrastructure and services."},{"id":"overview","title":"Overview","role":"overview","text":"Redis Cloud on AWS provides a fully managed Redis service that combines the power of Redis with AWS's reliable, scalable cloud infrastructure. Deploy Redis databases across multiple AWS regions with enterprise-grade security, automatic scaling, and 24/7 monitoring."},{"id":"key-features","title":"Key Features","role":"content","text":"- **VPC Peering**: Secure, private connectivity between Redis Cloud and your AWS VPC\n- **AWS Transit Gateway**: Private connectivity to multiple resources\n- **Multi-AZ Deployment**: High availability across AWS Availability Zones\n- **AWS Marketplace**: Simple billing through your existing AWS account\n- **Global Distribution**: Deploy in 12+ AWS regions worldwide\n- **Auto-scaling**: Automatic scaling based on memory usage and throughput\n- **Enterprise Security**: Data encryption, compliance certifications, and access control\n- **24/7 Monitoring**: Comprehensive monitoring and alerting capabilities"},{"id":"getting-started","title":"Getting Started","role":"setup","text":"Get started with [Redis Cloud](https://redis.io/docs/latest/operate/rc/rc-quickstart) on AWS to create and manage your Redis databases with step-by-step setup instructions and best practices."}],"examples":[]}
{"schema_version":2,"id":"integrate/azure-redis-cloud","title":"Redis Cloud on Azure","url":"https://redis.io/docs/latest/integrate/azure-redis-cloud/","summary":"Deploy Redis Cloud on Microsoft Azure","tags":["docs","integrate","rc"],"last_updated":"2026-09-17T14:54:42+01:00","children":[],"page_type":"content","content_hash":"55ed116906891641f79643c5e7158069e16c94c3b7103bf26d29bd0ed3b8d7ee","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"Deploy Redis Cloud on Microsoft Azure to combine Redis's high performance with Azure's enterprise-grade cloud platform and services."},{"id":"overview","title":"Overview","role":"overview","text":"Redis Cloud on Azure provides a fully managed Redis service that integrates seamlessly with Azure's enterprise cloud platform. Deploy Redis databases across multiple Azure regions with enterprise-grade security, compliance, and Azure-native integrations."},{"id":"regions","title":"Regions","role":"content","text":"Redis Cloud Essentials is available on multiple Microsoft Azure regions, see [Supported regions](https://redis.io/docs/latest/operate/rc/supported-regions#microsoft-azure)."},{"id":"getting-started","title":"Getting Started","role":"setup","text":"Get started with [Redis Cloud](https://redis.io/docs/latest/operate/rc/rc-quickstart) to create and manage your Redis databases."}],"examples":[]}
{"schema_version":2,"id":"integrate/confluent-with-redis-cloud","title":"Confluent with Redis Cloud","url":"https://redis.io/docs/latest/integrate/confluent-with-redis-cloud/","summary":"Describes how to integrate Redis Cloud into Confluent Cloud.","tags":["docs","integrate","rc"],"last_updated":"2026-10-02T14:54:03+01:00","children":[],"page_type":"content","content_hash":"521dd11b893b294080f239893422f230fec79b7d2041f669edda9f58a123c8d7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can send data from [Confluent Cloud](https://confluent.cloud/) to your Redis Cloud database using the [Redis Sink connector for Confluent Cloud](https://docs.confluent.io/cloud/current/connectors/cc-redis-sink.html)."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before you add the Redis Sink Confluent connector to your Confluent Cloud cluster:\n\n1. [Create a database](https://redis.io/docs/latest/operate/rc/databases/create-database) in the same region as your Confluent Cloud cluster.\n\n1. If you decide to [enable Transport Layer Security (TLS)](https://redis.io/docs/latest/operate/rc/security/database-security/tls-ssl) for your Redis database, [download the server certificate](https://redis.io/docs/latest/operate/rc/security/database-security/tls-ssl#download-certificates) from the Redis Cloud console and [encode it](#encode-server-certificate) to be used with Confluent Cloud.\n\n1. Ensure you meet the prerequisites in the [Redis Sink connector documentation](https://docs.confluent.io/cloud/current/connectors/cc-redis-sink.html#quick-start) to set up your Redis Sink with Confluent Cloud."},{"id":"encode-server-certificate","title":"Encode server certificate","role":"content","text":"If you decide to enable Transport Layer Security (TLS) for your database, you will need to encode the [server certificate](https://redis.io/docs/latest/operate/rc/security/database-security/tls-ssl#download-certificates) (`redis_ca.pem`) for use as the Confluent Cloud Truststore file. To do this:\n\n1. Use a base64 utility to encode `redis_ca.pem` into base64 in a new file. For example, using the [`base64` command-line utility](https://linux.die.net/man/1/base64):\n\n    [code example]\n\n1. Using a text editor, add the following text to the beginning of the truststore file:\n\n    [code example]\n\n1. Save and close the truststore file."},{"id":"connect-the-redis-sink-connector-to-redis-cloud","title":"Connect the Redis Sink connector to Redis Cloud","role":"content","text":"To add the Redis Sink connector to your Confluent Cloud environment from the Redis Cloud console: \n\n1. From the [Redis Cloud console](https://cloud.redis.io/), select **Account Settings** and then select the **Integrations** tab.\n\n1. Select the **Configure** button in the **Confluent** tile. \n\n    ![The Confluent integration tile.](https://redis.io/docs/latest/images/rc/account-settings-integrations-confluent.png)\n\n1. This will take you to [New Sink Connector](https://confluent.cloud/go/new-sink-connector/RedisSink) on Confluent Cloud. If you have more than one Confluent Cloud environment or Cluster, select your environment and cluster from the lists and select **Continue**.\n\n    ![Select your environment and cluster from the Create a Connector selector.](https://redis.io/docs/latest/images/rc/confluent-create-connector.png)\n\n1. From there, follow the steps to [Enter the connector details](https://docs.confluent.io/cloud/current/connectors/cc-redis-sink.html#step-4-enter-the-connector-details) on the Confluent documentation.\n\n    When you get to the **Authentication** step, fill in the fields with the following information:\n\n    - **Redis hostname**: The Public endpoint of your database, without the port number. This can be found in the [Redis Cloud console](https://cloud.redis.io/) from the database list or from the **General** section of the **Configuration** tab for the source database.\n    - **Redis port number**: The database's port. This is the number at the end of your database's Public endpoint.\n    - **Redis database index**: Set this to 0 for a Redis Cloud database.\n    - **Redis server password**: Enter the database password. If you have not set your own database user and password, use the [default user password](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/default-user), which appears in the **Security** section of the **Configuration** tab of the database details screen.\n    - **SSL mode**: Set depending on what type of [TLS authentication](https://redis.io/docs/latest/operate/rc/security/database-security/tls-ssl) is set for your database.\n        - If TLS authentication is turned off, select **disabled**.\n        - If TLS authentication is turned on, select **server**. \n    - **Trustore file**: If the **SSL mode** is set to **server**, upload the truststore file created when you [encoded the server certificate](#encode-server-certificate).\n    - **Redis Server mode**: If [OSS Cluster API](https://redis.io/docs/latest/operate/rc/databases/configuration/clustering#oss-cluster-api) is enabled, select **Cluster**. Otherwise, select **Standalone**.\n        \n    Select **Continue** once you have entered the database information. Enter the rest of the [connector details](https://docs.confluent.io/cloud/current/connectors/cc-redis-sink.html#step-4-enter-the-connector-details) from the **Configuration** step.\n\n1. [Connect to your database](https://redis.io/docs/latest/operate/rc/rc-quickstart#connect-to-a-database) to verify that data is being stored."}],"examples":[{"id":"encode-server-certificate-ex0","language":"sh","code":"$ base64 -i redis_ca.pem -o <truststore_file_name>","section_id":"encode-server-certificate"},{"id":"encode-server-certificate-ex1","language":"text","code":"data:text/plain;base64","section_id":"encode-server-certificate"}]}
{"schema_version":2,"id":"integrate/datadog-with-redis-cloud","title":"Datadog with Redis Cloud","url":"https://redis.io/docs/latest/integrate/datadog-with-redis-cloud/","summary":"To collect, view, and monitor metrics data from your databases and other cluster components, you can connect Datadog to your Redis Cloud cluster using the Redis Datadog Integration.","tags":["docs","integrate","rs"],"last_updated":"2026-10-02T14:54:03+01:00","children":[],"page_type":"content","content_hash":"75b5ed688d1102e8e8fac84f101478a552d633243980235d0364c9fa3064c54b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Datadog](https://www.datadoghq.com/) is used by organizations of all sizes and across a wide range of industries to \nenable digital transformation and cloud migration, drive collaboration among development, operations, security and \nbusiness teams, accelerate time to market for applications, reduce time to problem resolution, secure applications and \ninfrastructure, understand user behavior, and track key business metrics.\n\nThe Datadog Integration for Redis Cloud uses the Datadog Integration API to connect to Redis metrics exporters. \nThe integration is based on Datadog's \n[OpenMetrics integration](https://datadoghq.dev/integrations-core/base/openmetrics/) in their core API. This integration \nenables Redis Cloud users to export metrics directly to Datadog for analysis, and includes Redis-designed \ndashboards for use in monitoring Redis Cloud clusters.\n\nThis integration makes it possible to:\n- Collect and display metrics not available in the admin console\n- Set up automatic alerts for node or cluster events\n- Display these metrics alongside data from other systems\n\n![screenshot of datadog dashboard](https://redis.io/docs/latest/images/rc/redis-cloud-datadog.png)"},{"id":"install-redis-datadog-integration-for-redis-cloud","title":"Install Redis' Datadog Integration for Redis Cloud","role":"setup","text":"Installing the Datadog integration is a two-step process. Firstly, the installation must be part of your configuration. \nSelect 'Integrations' from the menu in the Datadog portal and then enter 'Redis' in the search bar, then select \n'Redis Cloud by Redis, Inc.'. Next click 'Install Integration' in the top-right corner of the overview page.\n\nIf you have not already created a VPC between the Redis Cloud cluster and the network in which the machine hosting the \nDatadog agent lives you should do so now. Please visit [VPC Peering](https://redis.io/docs/latest/operate/rc/security/vpc-peering/) \nand follow the instructions for the cloud platform of your choice.\n\nReturning to the Datadog console, open the 'Configure' tab of the integration and follow the instructions for installing \nthe integration on the local machine. After it has been installed follow the instruction for adding an instance to the \nconf.yaml in /etc/datadog-agent/conf.d/redis_cloud.d.\n\nAfter you have edited the conf.yaml file please restart the service and check its status:\n\n[code example]\n\nfollowed by:\n\n[code example]\n\nto be certain that the service itself is running and did not encounter any problems. Next, check the output of the \nservice; in the terminal on the Datadog agent host run the following command:\n\n[code example]\n\nIt will take several minutes for data to reach Datadog. Finally, check the Datadog console by selecting \nInfrastructure -> Host Map from the menu and then finding the host that is monitoring the Redis Cloud instance. The host \nshould be present, and in its list of components there should be a section called 'rdsc', which is the namespace used by \nthe Redis Cloud integration, although this can take several minutes to appear. It is also possible to verify the metrics \nby choosing Metrics -> Explorer from the menu and entering 'rdsc.bdb_up'."},{"id":"view-metrics","title":"View metrics","role":"content","text":"The Redis Cloud Integration for Datadog contains pre-defined dashboards to aid in monitoring your Redis Cloud deployment.\n\nThe following dashboards are currently available:\n\n- Overview\n- Database\n- Network\n\nA number of additional dashboards will be included in the next release (v1.1.0)."},{"id":"monitor-metrics","title":"Monitor metrics","role":"content","text":"See [Observability and monitoring guidance](https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise/observability) for monitoring details."}],"examples":[{"id":"install-redis-datadog-integration-for-redis-cloud-ex0","language":"shell","code":"sudo service datadog-agent restart","section_id":"install-redis-datadog-integration-for-redis-cloud"},{"id":"install-redis-datadog-integration-for-redis-cloud-ex1","language":"shell","code":"sudo service datadog-agent status","section_id":"install-redis-datadog-integration-for-redis-cloud"},{"id":"install-redis-datadog-integration-for-redis-cloud-ex2","language":"shell","code":"tail -f /var/log/datadog/agent.log","section_id":"install-redis-datadog-integration-for-redis-cloud"}]}
{"schema_version":2,"id":"integrate/datadog-with-redis-enterprise","title":"Datadog with Redis Software","url":"https://redis.io/docs/latest/integrate/datadog-with-redis-enterprise/","summary":"To collect, view, and monitor metrics data from your databases and other cluster components, you can connect Datadog to your Redis Software cluster using the Redis Datadog Integration.","tags":["docs","integrate","rs"],"last_updated":"2026-10-02T14:54:03+01:00","children":[],"page_type":"content","content_hash":"807d3242a9b5562e96ec29a94dbf5141a545aa34ff3af3898815d94659288f95","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Datadog](https://www.datadoghq.com/) is used by organizations of all sizes and across a wide range of industries to \nenable digital transformation and cloud migration, drive collaboration among development, operations, security and \nbusiness teams, accelerate time to market for applications, reduce time to problem resolution, secure applications and \ninfrastructure, understand user behavior, and track key business metrics.\n\nThe Datadog Integration for Redis Software uses Datadog's Integration API to connect to Redis metrics exporters. \nThe integration is based on the Datadog \n[OpenMetrics integration](https://datadoghq.dev/integrations-core/base/openmetrics/) in their core API. This integration \nenables Redis Software users to export metrics directly to Datadog for analysis, and includes Redis-designed \ndashboards for use in monitoring Redis Software clusters.\n\nThis integration makes it possible to:\n- Collect and display metrics not available in the admin console\n- Set up automatic alerts for node or cluster events\n- Display these metrics alongside data from other systems\n\n![](https://redis.io/docs/latest/images/rc/redis-cloud-datadog.png)"},{"id":"install-redis-datadog-integration-for-redis-software","title":"Install Redis' Datadog Integration for Redis Software","role":"setup","text":"Installing the Datadog integration is a two-step process. Firstly, the installation must be part of your configuration. \nSelect 'Integrations' from the menu  in the Datadog portal and then enter 'Redis' in the search bar, then select \n'Redis Software by Redis, Inc.'. Next click 'Install Integration' in the top-right corner of the overview page. \nOnce it has been installed follow the instructions for adding an instance to the conf.yaml in \n/etc/datadog-agent/conf.d/redis_cloud.d.\n\nAfter you have edited the conf.yaml file please restart the service and check its status:\n\n[code example]\n\nfollowed by:\n\n[code example]\n\nto be certain that the service itself is running and did not encounter any problems. Next, check the output of the \nservice; in the terminal on the Datadog agent host run the following command:\n\n[code example]\n\nIt will take several minutes for data to reach Datadog. Finally, check the Datadog console by selecting \nInfrastructure -> Host Map from the menu and then finding the host that is monitoring the Redis Software instance. The host \nshould be present, and in its list of components there should be a section called 'rdse', which is the namespace used by \nthe Redis Software integration, although this can take several minutes to appear. It is also possible to verify the metrics \nby choosing Metrics -> Explorer from the menu and entering 'rdse.bdb_up'."},{"id":"view-metrics","title":"View metrics","role":"content","text":"The Redis Software Integration for Datadog contains pre-defined dashboards to aid in monitoring your Redis Software deployment.\n\nThe following dashboards are currently available:\n\n- Overview\n- Database\n- Node\n- Shard\n- Active-Active\n- Proxy\n- Proxy Threads"},{"id":"monitor-metrics","title":"Monitor metrics","role":"content","text":"See [Observability and monitoring guidance](https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise/observability) for monitoring details."}],"examples":[{"id":"install-redis-datadog-integration-for-redis-software-ex0","language":"shell","code":"sudo service datadog-agent restart","section_id":"install-redis-datadog-integration-for-redis-software"},{"id":"install-redis-datadog-integration-for-redis-software-ex1","language":"shell","code":"sudo service datadog-agent status","section_id":"install-redis-datadog-integration-for-redis-software"},{"id":"install-redis-datadog-integration-for-redis-software-ex2","language":"shell","code":"tail -f /var/log/datadog/agent.log","section_id":"install-redis-datadog-integration-for-redis-software"}]}
{"schema_version":2,"id":"integrate/docker-redis-enterprise","title":"Redis Software with Docker","url":"https://redis.io/docs/latest/integrate/docker-redis-enterprise/","summary":"Run Redis Software in Docker containers","tags":["docs","integrate","rs"],"last_updated":"2026-09-17T14:54:42+01:00","children":[],"page_type":"content","content_hash":"b443097c6e51d88803c5ac9c25dc75412bcc48bd0e3d4e827f22761a0fa036fb","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"Deploy Redis Software using Docker containers for quick development setups and testing environments."},{"id":"overview","title":"Overview","role":"overview","text":"Redis Software with Docker provides a containerized version of Redis Software, perfect for development, testing, and proof-of-concept deployments. Get enterprise features like clustering, high availability, and advanced security in a simple Docker container."},{"id":"key-features","title":"Key Features","role":"content","text":"- **Enterprise Features**: Full Redis Software capabilities in a container\n- **Multi-tenancy**: Multiple databases with resource isolation\n- **High Availability**: Built-in clustering and automatic failover\n- **Advanced Security**: Role-based access control and encryption\n- **Monitoring Dashboard**: Web-based management interface\n- **Advanced capability support**: JSON, time series, and probabilistic data structures, and Redis Search.\n- **Easy Setup**: Quick deployment with Docker Compose or single container\n- **Development Ready**: Perfect for local development and testing environments"},{"id":"getting-started","title":"Getting Started","role":"setup","text":"Get started with the [Redis Software Docker quickstart](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) guide."}],"examples":[]}
{"schema_version":2,"id":"integrate/dynatrace-with-redis-cloud","title":"Dynatrace with Redis Cloud","url":"https://redis.io/docs/latest/integrate/dynatrace-with-redis-cloud/","summary":"To collect, view, and monitor metrics data from your databases and other cluster components, you can connect Dynatrace to your Redis Cloud cluster using the Redis Dynatrace Integration.","tags":["docs","integrate","rs"],"last_updated":"2026-10-02T14:54:03+01:00","children":[],"page_type":"content","content_hash":"b1fe396f66f8ac183b90711fd4c26f3649d2b72a630395ef579b4274f7d3b7c3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Dynatrace](https://www.dynatrace.com/) is used by organizations of all sizes and across a wide range of industries to \nenable digital transformation and cloud migration, drive collaboration among development, operations, security and \nbusiness teams, accelerate time to market for applications, reduce time to problem resolution, secure applications and \ninfrastructure, understand user behavior, and track key business metrics.\n\nThe Redis Dynatrace Integration for Redis Cloud uses Prometheus remote write functionality to connect Prometheus data \nsources to Dynatrace. This integration enables Redis Cloud users to export metrics to Dynatrace for analysis, \nand includes Redis-designed dashboards for use in monitoring Redis Cloud clusters.\n\nThis integration makes it possible to:\n- Collect and display metrics not available in the admin console\n- Set up automatic alerts for node or cluster events\n- Display these metrics alongside data from other systems\n\n![](https://redis.io/docs/latest/images/rc/redis-cloud-dynatrace.png)"},{"id":"install-redis-dynatrace-integration-for-redis-cloud","title":"Install Redis' Dynatrace Integration for Redis Cloud","role":"setup","text":"The Dynatrace Integration is based on a feature of the Prometheus data source. Prometheus can forward metrics on to \nanother destination using remote writes. This will require a Prometheus installation inside the same datacenter as the \nRedis Cloud deployment.\n\nIf you have not already created a VPC between the Redis Cloud cluster and the network in which the machine hosting \nPrometheus lives you should do so now. Please visit [VPC Peering](https://redis.io/docs/latest/operate/rc/security/vpc-peering/) \nand follow the instructions for the cloud platform of your choice.\n\nYou'll also need the [Prometheus endpoint for your database](https://redis.io/docs/latest/operate/rc/databases/monitor-performance#connect-to-prometheus) from the [Redis Cloud console](https://cloud.redis.io/).\n\nIn the **Metrics** tab of your database, select **Connect to Prometheus > Copy Prometheus endpoint** to save your Prometheus endpoint to the clipboard.\n\n![Use the Connect to Prometheus button to get the Prometheus endpoint.](https://redis.io/docs/latest/images/rc/database-metrics-connect-prometheus.png)\n\nYou can also get the Prometheus endpoint by calling [`GET /subscriptions/{subscriptionId}`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Subscriptions-Pro/operation/getSubscriptionById) and getting the `prometheusEndpoint` from the response."},{"id":"view-metrics","title":"View metrics","role":"content","text":"The Redis Cloud Integration for Dynatrace contains pre-defined dashboards to aid in monitoring your Redis Software deployment.\n\nThe following dashboards are currently available:\n\n- Cluster: top-level statistics indicating the general health of the cluster\n- Database: performance metrics at the database level\n- Shard: low-level details of an individual shard\n- Active-Active: replication and performance for geo-replicated clusters\n- Proxy: network and command information regarding the proxy\n- Proxy Threads: processor usage information regarding the proxy's component threads"},{"id":"monitor-metrics","title":"Monitor metrics","role":"content","text":"Dynatrace dashboards can be filtered using the text area. For example, when viewing a cluster dashboard it is possible \nfilter the display to show data for only one cluster by typing 'cluster' in the text area and waiting for the system to\nretrieve the relevant data before choosing one of the options in the 'cluster' section.\n\nCertain types of data do not know the name of the database from which they were drawn. The dashboard should have a list \nof database names and ids; use the id value when filtering input to the dashboard."}],"examples":[]}
{"schema_version":2,"id":"integrate/dynatrace-with-redis-enterprise","title":"Dynatrace with Redis Software","url":"https://redis.io/docs/latest/integrate/dynatrace-with-redis-enterprise/","summary":"To collect, view, and monitor metrics data from your databases and other cluster components, you can connect Dynatrace to your Redis Software cluster using the Redis Dynatrace Integration.","tags":["docs","integrate","rs"],"last_updated":"2026-10-02T14:54:03+01:00","children":[],"page_type":"content","content_hash":"729abe22d28c74fa4c7bb569dafebec88d1aec1b6bc69822fcbef90a42b1bef3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Dynatrace](https://www.dynatrace.com/) is used by organizations of all sizes and across a wide range of industries to \nenable digital transformation and cloud migration, drive collaboration among development, operations, security and \nbusiness teams, accelerate time to market for applications, reduce time to problem resolution, secure applications and \ninfrastructure, understand user behavior, and track key business metrics.\n\nThe Dynatrace Integration for Redis Software uses Prometheus remote write functionality to connect Prometheus data \nsources to Dynatrace. This integration enables Redis Software users to export metrics to Dynatrace for analysis, \nand includes Redis-designed dashboards for use in monitoring Redis Software clusters.\n\nThis integration makes it possible to:\n- Collect and display metrics not available in the admin console\n- Set up automatic alerts for node or cluster events\n- Display these metrics alongside data from other systems\n\n![](https://redis.io/docs/latest/images/rs/redis-enterprise-dynatrace.png)"},{"id":"install-redis-dynatrace-integration-for-redis-software","title":"Install Redis' Dynatrace Integration for Redis Software","role":"setup","text":"At the present time the Dynatrace integration is not signed by Dynatrace, meaning that it will be necessary to download \nthe source configuration and dashboards and assemble them and sign them cryptologically with a certificate that you have \ncreated. The instructions for this procedure can be found on the Dynatrace \n[site](https://docs.dynatrace.com/docs/extend-dynatrace/extensions20/sign-extension). Please note that the instructions \nwould have you place the dashboards next to the src folder; this is incorrect, the dashboards should be located inside \nthe src folder."},{"id":"view-metrics","title":"View metrics","role":"content","text":"The Redis Software Integration for Dynatrace contains pre-defined dashboards to aid in monitoring your Redis Software deployment.\n\nThe following dashboards are currently available:\n\n- Cluster: top-level statistics indicating the general health of the cluster\n- Database: performance metrics at the database level\n- Node: machine performance statistics\n- Shard: low-level details of an individual shard\n- Active-Active: replication and performance for geo-replicated clusters\n- Proxy: network and command information regarding the proxy\n- Proxy Threads: processor usage information regarding the proxy's component threads"},{"id":"monitor-metrics","title":"Monitor metrics","role":"content","text":"Dynatrace dashboards can be filtered using the text area. For example, when viewing a cluster dashboard it is possible to\nfilter the display to show data for only one cluster by typing 'cluster' in the text area and waiting for the system to\nretrieve the relevant data before choosing one of the options in the 'cluster' section.\n\nCertain types of data do not know the name of the database from which they were drawn. The dashboard should have a list \nof database names and ids; use the id value when filtering input to the dashboard."}],"examples":[]}
{"schema_version":2,"id":"integrate/fastapi","title":"Redis with FastAPI","url":"https://redis.io/docs/latest/integrate/fastapi/","summary":"Add idiomatic Redis connection management and dependency-injection caching to FastAPI apps.","tags":["docs","integrate","oss","rs","rc"],"last_updated":"2026-09-17T14:55:03+01:00","children":[],"page_type":"content","content_hash":"ed74c29b061453d5b597d288a9709f522aa116eb8d9d8545facee685f3ce3996","sections":[{"id":"requirements","title":"Requirements","role":"content","text":"See [Requirements](https://github.com/redis/fastapi-redis-sdk#requirements) on the\nGitHub repo for the full set of dependencies used by `fastapi-redis-sdk`.\n\nYou also need a running Redis server. You can run one locally with\n[Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-redis),\nuse [Docker](https://hub.docker.com/_/redis), or connect to\n[Redis Cloud](https://redis.io/cloud/)."},{"id":"install-and-import","title":"Install and import","role":"setup","text":"Use the following command to install `fastapi-redis-sdk`:\n\n[code example]\n\nNote that although you install the package as `fastapi-redis-sdk`, you import it as\n`redis_fastapi`:\n\n[code example]"},{"id":"quick-start","title":"Quick start","role":"content","text":"Attach Redis to your app with the [fluent builder](https://en.wikipedia.org/wiki/Fluent_interface). The `lifespan()` call hooks into\nthe [FastAPI lifespan events](https://fastapi.tiangolo.com/advanced/events/) to open a\nconnection pool at startup and close it cleanly on shutdown. Inject `AsyncRedisDep`\ninto your `async` endpoints to get a ready-to-use client:\n\n[code example]\n\nThe builder wraps any existing lifespan, so multiple libraries can register their own\nstartup and shutdown logic without conflicting."},{"id":"configuration","title":"Configuration","role":"configuration","text":"All settings are read from environment variables prefixed with `REDIS_`, or from a\n`.env` file in your project root. The simplest setup is a single connection URL:\n\n[code example]\n\nAlternatively, you can configure individual fields:\n\n[code example]\n\nWhen `REDIS_URL` is set, it takes precedence over the individual connection fields.\nThe most commonly used variables are described in the table below:\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `REDIS_URL` | - | Full Redis URL (takes precedence over the fields below) |\n| `REDIS_HOST` | `localhost` | Redis host |\n| `REDIS_PORT` | `6379` | Redis port |\n| `REDIS_PASSWORD` | - | Redis password (stored securely as a Pydantic `SecretStr`) |\n| `REDIS_SSL` | `false` | Enable TLS (or use a `rediss://` URL) |\n| `REDIS_CLUSTER` | `false` | Enable [OSS Cluster mode](#cluster-mode) |\n| `REDIS_PREFIX` | `redis:fastapi` | Global prefix applied to all keys |\n| `REDIS_DEFAULT_TTL` | `0` | Default cache TTL in seconds (`0` = no expiry) |\n| `REDIS_MAX_CONNECTIONS` | - | Maximum pooled connections |\n\nConfiguration is validated with [Pydantic Settings](https://docs.pydantic.dev/latest/concepts/pydantic_settings/),\nso invalid values (for example, a port outside 1–65535) fail fast at startup. For the\nfull environment-variable reference, TLS options, and programmatic configuration, see\nthe [SDK configuration guide](https://redis.github.io/fastapi-redis-sdk/guide/configuration/)."},{"id":"caching","title":"Caching","role":"content","text":"Enable caching by adding `.caching()` to the builder chain. The SDK then offers two\ncomplementary approaches that share the same connection pool:\n\n| Approach | Best for |\n|----------|----------|\n| `cache()`, `cache_evict()`, `cache_put()` | Most endpoints — read, invalidate, and write-through |\n| `CacheBackend` | Complex invalidation, conditional or dynamic caching |"},{"id":"dependency-injection-factories","title":"Dependency-injection factories","role":"content","text":"`cache()`, `cache_evict()`, and `cache_put()` are\n[dependency](https://fastapi.tiangolo.com/tutorial/dependencies/) factories you attach\nto a route. Because all three share the same `key_builder`, a `GET`, `DELETE`, and\n`PUT` on the same path target the exact same cache key:\n\n[code example]\n\nCached responses include an `X-Redis-Cache` header (`HIT` or `MISS`) along with\n`Cache-Control` and `ETag` headers.\n\nThe example below drives a cached endpoint with FastAPI's\n[`TestClient`](https://fastapi.tiangolo.com/reference/testclient/) so you can see the\n`MISS` → `HIT` → eviction cycle, plus the HTTP caching headers, in action:\n\nFoundational: cache a GET response so the first request is a MISS and the next is served from Redis as a HIT\n\n**Difficulty:** Beginner\n\n**Available in:** Python\n\n##### Python\n\n[code example]\n\n\n\nEvicting a resource clears its cached entry, so the following read is a `MISS` again:\n\nInvalidate a cached entry so the next read recomputes the response\n\n**Difficulty:** Beginner\n\n**Available in:** Python\n\n##### Python\n\n[code example]"},{"id":"cachebackend-for-full-control","title":"CacheBackend for full control","role":"content","text":"For conditional caching, cascade invalidation, dynamic TTLs, or caching intermediate\nresults, inject `CacheBackendDep` and call its `get`/`set`/`delete`/`has`/`delete_group`\nmethods directly. Values are serialized to and from JSON automatically:\n\n[code example]\n\nSee the [SDK caching guide](https://redis.github.io/fastapi-redis-sdk/guide/caching/)\nfor detailed patterns and best practices."},{"id":"http-caching","title":"HTTP caching","role":"content","text":"Responses cached with the DI factories carry standard HTTP caching headers, so clients\nand proxies can revalidate cheaply. The SDK sets `Cache-Control` from the entry's TTL\nand emits a weak `ETag`; when a client returns that tag in an `If-None-Match` header,\nthe server responds with `304 Not Modified` and no body:\n\nUse ETag and Cache-Control headers so a revalidation request returns 304 Not Modified\n\n**Difficulty:** Intermediate\n\n**Available in:** Python\n\n##### Python\n\n[code example]"},{"id":"cluster-mode","title":"Cluster mode","role":"content","text":"To work with an [OSS Cluster](https://redis.io/docs/latest/operate/oss_and_stack/management/scaling),\nset `REDIS_CLUSTER=true` and point `REDIS_URL` at the cluster nodes:\n\n[code example]\n\nIn cluster mode, `AsyncRedisDep` yields an `AsyncRedisCluster` client."},{"id":"observability","title":"Observability","role":"content","text":"The SDK can emit [OpenTelemetry](https://opentelemetry.io/) spans and metrics for every\ncache operation. Telemetry is opt-in and a zero-cost no-op when disabled. Install the\noptional dependency and enable it on the builder:\n\n[code example]\n\n[code example]\n\nCalling `.otel()` on the builder is what activates the cache telemetry. To also emit\nredis-py's low-level command spans and connection-pool metrics, set\n`REDIS_OTEL_REDIS_ENABLED=true`. See the\n[SDK configuration guide](https://redis.github.io/fastapi-redis-sdk/guide/configuration/#opentelemetry)\nfor the full list of spans and metrics that are emitted."},{"id":"more-information","title":"More information","role":"content","text":"- [fastapi-redis-sdk on GitHub](https://github.com/redis/fastapi-redis-sdk)\n- [fastapi-redis-sdk documentation](https://redis.github.io/fastapi-redis-sdk/)\n- [fastapi-redis-sdk on PyPI](https://pypi.org/project/fastapi-redis-sdk/)\n- [redis-py client documentation](https://redis.io/docs/latest/develop/clients/redis-py)\n- [FastAPI documentation](https://fastapi.tiangolo.com/)"}],"examples":[{"id":"install-and-import-ex0","language":"bash","code":"pip install fastapi-redis-sdk","section_id":"install-and-import"},{"id":"install-and-import-ex1","language":"python","code":"from redis_fastapi import FastAPIRedis","section_id":"install-and-import"},{"id":"quick-start-ex0","language":"python","code":"from fastapi import FastAPI\nfrom redis_fastapi import FastAPIRedis, AsyncRedisDep\n\napp = FastAPI()\nFastAPIRedis(app).lifespan()\n\n@app.get(\"/items\")\nasync def get_items(redis: AsyncRedisDep):\n    return {\"items\": await redis.get(\"items\")}","section_id":"quick-start"},{"id":"configuration-ex0","language":"bash","code":"export REDIS_URL=redis://user:pass@host:6379/0","section_id":"configuration"},{"id":"configuration-ex1","language":"bash","code":"export REDIS_HOST=redis.example.com\nexport REDIS_PORT=6380\nexport REDIS_PASSWORD=secret","section_id":"configuration"},{"id":"dependency-injection-factories-ex0","language":"python","code":"from fastapi import Depends, FastAPI\nfrom redis_fastapi import FastAPIRedis, cache, cache_evict, cache_put, default_key_builder\n\napp = FastAPI()\nFastAPIRedis(app).lifespan().caching()\n\n# READ - cache the GET response for 5 minutes\n@app.get(\"/products/{product_id}\", dependencies=[Depends(cache(ttl=300, eviction_group=\"products\"))])\nasync def get_product(product_id: int):\n    return await db.get_product(product_id)\n\n# INVALIDATE - evict the cached entry when the product is deleted\n@app.delete(\n    \"/products/{product_id}\",\n    dependencies=[Depends(cache_evict(eviction_group=\"products\", key_builder=default_key_builder))],\n)\nasync def delete_product(product_id: int):\n    await db.delete(product_id)\n\n# WRITE-THROUGH - refresh the cache so the next GET is a HIT\n@app.put(\n    \"/products/{product_id}\",\n    dependencies=[Depends(cache_put(eviction_group=\"products\", key_builder=default_key_builder, ttl=300))],\n)\nasync def replace_product(product_id: int, body: Product):\n    return await db.update(product_id, body)","section_id":"dependency-injection-factories"},{"id":"dependency-injection-factories-ex1","language":"python","code":"# The first request is a MISS: the handler runs and the response is cached.\nfirst = client.get(\"/cache-demo\")\nprint(first.headers[\"X-Redis-Cache\"])\n# >>> MISS\n\n# A second request within the TTL is a HIT, served from Redis without\n# re-running the handler, so the cached body is returned unchanged.\nsecond = client.get(\"/cache-demo\")\nprint(second.headers[\"X-Redis-Cache\"])\n# >>> HIT\n\nprint(first.json() == second.json())\n# >>> True","section_id":"dependency-injection-factories"},{"id":"dependency-injection-factories-ex2","language":"python","code":"# Deleting the resource evicts its cache entry, so the next read is a MISS\n# again and the handler recomputes a fresh response.\nclient.delete(\"/cache-demo\")\n\nthird = client.get(\"/cache-demo\")\nprint(third.headers[\"X-Redis-Cache\"])\n# >>> MISS\n\nprint(third.json() == first.json())\n# >>> False","section_id":"dependency-injection-factories"},{"id":"cachebackend-for-full-control-ex0","language":"python","code":"from redis_fastapi import CacheBackendDep\n\n@app.get(\"/dashboard/{user_id}\")\nasync def dashboard(user_id: int, cache: CacheBackendDep):\n    cached = await cache.get(f\"stats:{user_id}\", eviction_group=\"dashboard\")\n    if cached is not None:\n        return cached\n    result = await compute_dashboard(user_id)\n    await cache.set(f\"stats:{user_id}\", result, ttl=300, eviction_group=\"dashboard\")\n    return result","section_id":"cachebackend-for-full-control"},{"id":"http-caching-ex0","language":"python","code":"# Cached responses carry standard HTTP caching headers. Evict first so the next\n# request is a fresh MISS whose Cache-Control reflects the full 30-second TTL.\nclient.delete(\"/cache-demo\")\nmiss = client.get(\"/cache-demo\")\nprint(miss.headers[\"Cache-Control\"])\n# >>> max-age=30\n\n# The cached response also carries a weak ETag. Replaying it with If-None-Match\n# lets the server answer 304 Not Modified with no body.\netag = miss.headers[\"ETag\"]\nnot_modified = client.get(\"/cache-demo\", headers={\"If-None-Match\": etag})\nprint(not_modified.status_code)\n# >>> 304","section_id":"http-caching"},{"id":"cluster-mode-ex0","language":"bash","code":"export REDIS_CLUSTER=true\nexport REDIS_URL=redis://node1:6379,node2:6379,node3:6379","section_id":"cluster-mode"},{"id":"observability-ex0","language":"bash","code":"pip install fastapi-redis-sdk[otel]","section_id":"observability"},{"id":"observability-ex1","language":"python","code":"FastAPIRedis(app).lifespan().caching().otel()","section_id":"observability"}]}
{"schema_version":2,"id":"integrate/fusioncache","title":"FusionCache for C#/.NET","url":"https://redis.io/docs/latest/integrate/fusioncache/","summary":"Easy to use, fast and robust hybrid cache with advanced resiliency features for C#/.NET","tags":["docs","integrate","oss","rs","rc"],"last_updated":"2026-03-10T11:44:45-07:00","children":[],"page_type":"content","content_hash":"7e1afa62f59a42b5175d639250d693fb7b74cafccebaabc731399ba1ccf50fde","sections":[{"id":"overview","title":"Overview","role":"overview","text":"FusionCache can transparently handle multiple cache levels by mixing a memory cache and a distributed cache together, getting the best of both worlds. It allows for a sophisticated yet incredibly easy to use developer experience by handling all the complexities of a modern, fast and robust hybrid cache that can deal with the most complex and demanding scenarios. When coupled with Redis, performance, robustness and scaling will not be a problem anymore."},{"id":"key-features","title":"Key Features","role":"content","text":"- **Hybrid**: Can be used as both L1 (memory only) or L1+L2 (memory + distributed)\n- **Tagging**: Powerful tagging capabilities to easily work with multiple entries at once\n- **Backplane**: For instant multi-node distributed notifications\n- **Stampede Protection**: Both on single node (with a memory locker) and multi-node (with a distributed locker)\n- **Redis Perfect Match**: Native support for all distributed parts (cache, backplane and locker)\n- **Resiliency**: Fully resilient thanks to features like Fail-Safe, Eager Refresh, Factory Timeouts, Auto-Recovery and more\n- **Sync+Async**: Full support for both programming models\n- **.NET Ecosystem**: Seamless integration with ASP.NET Core, Entity Framework, and other .NET technologies"},{"id":"install","title":"Install","role":"setup","text":"Using the dotnet CLI, run:\n\n[code example]\n\nOther packages are also available to work with different distributed caches, serializers, backplanes and more.\nSee the [FusionCache docs](https://github.com/ZiggyCreatures/FusionCache/blob/main/docs/README.md) for more information."},{"id":"getting-started","title":"Getting Started","role":"setup","text":"First, install the packages for Redis (with JSON serialization):\n\n[code example]\n\nThen at startup:\n\n[code example]\n\nUse code like the following to share the Redis connection multiplexer:\n\n[code example]\n\nFusionCache is now ready to use. The simple example below demonstrates the `GetOrSetAsync` method but see [Core Methods](https://github.com/ZiggyCreatures/FusionCache/blob/main/docs/CoreMethods.md) in the FusionCache docs for full details of the API.\n\n[code example]"},{"id":"further-reading","title":"Further Reading","role":"content","text":"To learn more about FusionCache and its advanced features:\n\n- [FusionCache Documentation](https://github.com/ZiggyCreatures/FusionCache)\n- [FusionCache NuGet Package](https://www.nuget.org/packages/ZiggyCreatures.FusionCache)\n- [Redis Backplane Package](https://www.nuget.org/packages/ZiggyCreatures.FusionCache.Backplane.StackExchangeRedis)"}],"examples":[{"id":"install-ex0","language":"powershell","code":"dotnet add package ZiggyCreatures.FusionCache","section_id":"install"},{"id":"getting-started-ex0","language":"powershell","code":"dotnet add package ZiggyCreatures.FusionCache\ndotnet add package Microsoft.Extensions.Caching.StackExchangeRedis\ndotnet add package ZiggyCreatures.FusionCache.Serialization.SystemTextJson\ndotnet add package ZiggyCreatures.FusionCache.Backplane.StackExchangeRedis","section_id":"getting-started"},{"id":"getting-started-ex1","language":"csharp","code":"services.AddFusionCache()\n    .WithSerializer(\n        new FusionCacheSystemTextJsonSerializer()\n    )\n    .WithDistributedCache(\n        new RedisCache(new RedisCacheOptions { Configuration = \"localhost:6379\" })\n    )\n    .WithBackplane(\n        new RedisBackplane(new RedisBackplaneOptions { Configuration = \"localhost:6379\" })\n    );","section_id":"getting-started"},{"id":"getting-started-ex2","language":"csharp","code":"var muxer = ConnectionMultiplexer.Connect(\"localhost:6379\")\n\nservices.AddFusionCache()\n    .WithSerializer(\n        new FusionCacheSystemTextJsonSerializer()\n    )\n    .WithDistributedCache(\n        new RedisCache(new RedisCacheOptions { ConnectionMultiplexerFactory = () => muxer })\n    )\n    .WithBackplane(\n        new RedisBackplane(new RedisBackplaneOptions { ConnectionMultiplexerFactory = () => muxer })\n    );","section_id":"getting-started"},{"id":"getting-started-ex3","language":"csharp","code":"var product1 = await cache.GetOrSetAsync<Product>(\n    \"product:1\",\n    async _ => GetProductFromDb(1),\n    tags: [\"sales\", \"product\"]\n);\n\nvar product2 = await cache.GetOrSetAsync<Product>(\n    \"product:2\",\n    async _ => GetProductFromDb(2),\n    tags: [\"sales\", \"product\"]\n);\n\nvar order = await cache.GetOrSetAsync<Order>(\n    \"order:42\",\n    async _ => GetOrderFromDb(42),\n    tags: [\"sales\", \"order\"]\n);\n\n// LATER...\n\nawait cache.RemoveByTagAsync(\"product\");\n\n// NOW BOTH product:1 AND product:2 ARE GONE, ONLY order:42 IS STILL IN THE CACHE","section_id":"getting-started"}]}
{"schema_version":2,"id":"integrate/gcp-redis-cloud","title":"Redis Cloud on Google Cloud","url":"https://redis.io/docs/latest/integrate/gcp-redis-cloud/","summary":"Deploy Redis Cloud on Google Cloud Platform","tags":["docs","integrate","rc"],"last_updated":"2026-09-17T14:54:42+01:00","children":[],"page_type":"content","content_hash":"f52c1251877711fb125ae89546b6fe6a1c3b57a2cbf72cfa95932f746a2f718c","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"Deploy Redis Cloud on Google Cloud Platform (GCP) to leverage Redis's performance with Google's scalable cloud infrastructure and AI/ML services."},{"id":"overview","title":"Overview","role":"overview","text":"Redis Cloud on Google Cloud provides a fully managed Redis service that integrates with Google's advanced cloud platform and AI/ML services. Deploy Redis databases across Google's global network with enterprise security, automatic scaling, and seamless GCP integrations."},{"id":"key-features","title":"Key Features","role":"content","text":"- **Private Service Connect**: Secure, private connectivity within Google Cloud\n- **Global Network**: Deploy across Google's worldwide network of regions\n- **Auto-scaling**: Intelligent scaling based on traffic patterns and usage\n- **Enterprise Security**: Google Cloud IAM integration and advanced security features"},{"id":"getting-started","title":"Getting Started","role":"setup","text":"Get started with [Redis Cloud](https://redis.io/docs/latest/operate/rc/rc-quickstart) on Google Cloud to create and manage your Redis databases."}],"examples":[]}
{"schema_version":2,"id":"integrate/go-redis","title":"Go client for Redis","url":"https://redis.io/docs/latest/integrate/go-redis/","summary":"Learn how to build with Redis and Go","tags":["docs","integrate","oss","rs","rc"],"last_updated":"2026-09-17T14:54:53+01:00","children":[],"page_type":"content","content_hash":"8dae7b1b7996c8c127c1bd9746ac7f20b79d1517a0deca838ec040c0823c4ba2","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"Connect your Go application to a Redis database using the go-redis client library."},{"id":"overview","title":"Overview","role":"overview","text":"go-redis is a type-safe, Redis client library for Go that supports Redis 6.0+ features including Redis Cluster, Redis Sentinel, and Redis streams. It provides a clean, idiomatic Go API for interacting with Redis."},{"id":"key-features","title":"Key Features","role":"content","text":"- **Type Safety**: Strongly typed commands and responses\n- **Redis Cluster Support**: Built-in support for Redis Cluster deployments\n- **Redis Sentinel**: Automatic failover with Redis Sentinel\n- **Pipelining**: Efficient command batching for improved performance\n- **Pub/Sub**: Real-time messaging with Redis Pub/Sub\n- **Streams**: Support for Redis Streams data structure\n- **Connection Pooling**: Automatic connection management\n- **Context Support**: Full context.Context integration for cancellation and timeouts"},{"id":"getting-started","title":"Getting Started","role":"setup","text":"Refer to the complete [Go guide](https://redis.io/docs/latest/develop/clients/go) to install, connect, and use go-redis with detailed examples and best practices."}],"examples":[]}
{"schema_version":2,"id":"integrate/google-adk/agent-memory-server","title":"Agent Memory Server (deprecated)","url":"https://redis.io/docs/latest/integrate/google-adk/agent-memory-server/","summary":"Deprecated. The opensource-agent-memory backend is no longer maintained. Migrate existing deployments to Redis Agent Memory.","tags":["docs","integrate","oss","rs","rc"],"last_updated":"2026-09-17T14:55:03+01:00","page_type":"content","content_hash":"a0da4c71f688c24cb46d518b69b7597a330fa696708aa781a9ad31ef4b437b8c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!WARNING]\n> **Deprecated. Do not use for new work.**\n>\n> The `opensource-agent-memory` backend and the\n> [Agent Memory Server](https://github.com/redis/agent-memory-server) it targets\n> are deprecated and are not maintained going forward. Its capabilities are not\n> being carried forward, and support for it will be removed from adk-redis.\n>\n> New agents should use\n> [Redis Agent Memory](https://redis.io/docs/latest/integrate/google-adk/redis-agent-memory)\n> with `backend=\"redis-agent-memory\"`, on\n> [Redis Cloud](https://redis.io/docs/latest/operate/rc/context-engine/agent-memory) or\n> [self-managed](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed)\n> on your own Kubernetes cluster.\n>\n> This page exists only to help existing deployments migrate."},{"id":"what-it-was","title":"What it was","role":"content","text":"Agent Memory Server is a separate open source memory server, documented in\n[its own repository](https://github.com/redis/agent-memory-server). It is not\nRedis Agent Memory and does not speak the\n[Agent Memory Data Plane API](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/api-reference).\nadk-redis reached it with `backend=\"opensource-agent-memory\"`, an\n`api_base_url` pointing at the server, and the `agent-memory-client>=0.14.0`\ndependency.\n\nA handful of capabilities existed only on this backend: auto-summarization,\nextraction strategies, recency-boosted search, and an MCP endpoint. They are\nconfigured through `model_name`, `context_window_max`, `extraction_strategy`,\n`extraction_strategy_config`, and `recency_boost`, none of which have any effect\nwith `redis-agent-memory`. See the\n[Agent Memory Server repository](https://github.com/redis/agent-memory-server)\nfor the server's own setup and reference material.\n\n> [!NOTE]\n> Running Agent Memory yourself never required this backend. Self-managed Agent\n> Memory is supported and maintained, and uses `backend=\"redis-agent-memory\"` just\n> like Redis Cloud. You choose a deployment with `api_base_url`, not with\n> `backend`."},{"id":"migrate-to-redis-agent-memory","title":"Migrate to Redis Agent Memory","role":"content","text":"1. Provision a Redis Agent Memory store, on\n   [Redis Cloud](https://redis.io/docs/latest/operate/iris/agent-memory/create-service)\n   or [self-managed](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed).\n   Either gives you a Data Plane endpoint, an API key, and a store ID.\n2. Change `backend` from `opensource-agent-memory` to `redis-agent-memory` on\n   every service and tool config, or drop the field, since it is the default.\n3. Point `api_base_url` at the Data Plane endpoint, and add `api_key` and\n   `store_id`.\n4. Remove the backend-only settings listed above. They are silently inert on\n   `redis-agent-memory`.\n5. Replace any MCP memory wiring with the\n   [REST memory tools](https://redis.io/docs/latest/integrate/google-adk/redis-agent-memory#rest-tools),\n   which work the same way on both backends.\n6. Drop `agent-memory-client` from your dependencies. The `memory` extra\n   requires `redis-agent-memory>=0.2.0`.\n\n[code example]\n\nChanging configuration does not copy existing memories. Plan for a\nre-extraction window, or run both backends while long-term memory repopulates."},{"id":"examples-still-on-this-backend","title":"Examples still on this backend","role":"example","text":"Three examples in the adk-redis repository have not yet moved:\n[simple_redis_memory](https://github.com/redis-developer/adk-redis/tree/main/examples/simple_redis_memory),\n[travel_agent_memory_hybrid](https://github.com/redis-developer/adk-redis/tree/main/examples/travel_agent_memory_hybrid),\nand\n[fitness_coach_mcp](https://github.com/redis-developer/adk-redis/tree/main/examples/fitness_coach_mcp)\n(which uses the MCP endpoint this backend alone provides).\n\nFor a supported starting point, use\n[managed_memory_quickstart](https://redis.io/docs/latest/integrate/google-adk/examples#managed_memory_quickstart).\n[travel_agent_memory_tools](https://github.com/redis-developer/adk-redis/tree/main/examples/travel_agent_memory_tools)\nswitches backends with `REDIS_MEMORY_BACKEND`, so it already runs on\n`redis-agent-memory`."},{"id":"more-info","title":"More info","role":"content","text":"- [Redis Agent Memory](https://redis.io/docs/latest/integrate/google-adk/redis-agent-memory): the supported backend\n- [Self-managed Agent Memory](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed): run Agent Memory on your own Kubernetes cluster\n- [Agent Memory Server on GitHub](https://github.com/redis/agent-memory-server): the deprecated server's own documentation"}],"examples":[{"id":"migrate-to-redis-agent-memory-ex0","language":"python","code":"# Before: deprecated backend\nconfig = RedisSessionMemoryServiceConfig(\n    backend=\"opensource-agent-memory\",\n    api_base_url=\"http://localhost:8088\",\n    default_namespace=\"my_app\",\n    model_name=\"gemini-2.5-flash\",\n    context_window_max=8000,\n)\n\n# After: Redis Agent Memory, on Redis Cloud or self-managed\nconfig = RedisSessionMemoryServiceConfig(\n    backend=\"redis-agent-memory\",\n    api_base_url=\"https://your-endpoint.redis.io\",\n    api_key=\"your-api-key\",\n    store_id=\"your-store-id\",\n    default_namespace=\"my_app\",\n)","section_id":"migrate-to-redis-agent-memory"}]}
{"schema_version":2,"id":"integrate/google-adk/examples","title":"adk-redis examples","url":"https://redis.io/docs/latest/integrate/google-adk/examples/","summary":"Complete examples for every adk-redis capability.","tags":["docs","integrate","oss","rs","rc"],"last_updated":"2026-09-17T14:55:03+01:00","page_type":"content","content_hash":"a783373ed6bc0b997ba403be339dc57d93942106b9bc2d5bbdeef46d107224f6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The [adk-redis repository](https://github.com/redis-developer/adk-redis/tree/main/examples) includes ten complete examples, each focused on a specific capability. The seven below run on supported backends; the remaining three are listed under [Deprecated backend examples](#deprecated-backend-examples)."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"All examples require:\n\n- **Python 3.10+**\n- **Redis 8.4+**: `docker run -d --name redis -p 6379:6379 redis:8.4-alpine`\n- **A memory backend** (for memory examples): a [Redis Agent Memory](https://redis.io/docs/latest/integrate/google-adk/redis-agent-memory) store on Redis Cloud or self-managed.\n- **API keys**: Most examples need a `GOOGLE_API_KEY` for Gemini"},{"id":"managed_memory_quickstart","title":"`managed_memory_quickstart`","role":"content","text":"**Backend:** `redis-agent-memory` &middot; **Run:** `python main.py`\n\nThe smallest memory example, and the counterpart to `simple_redis_memory`. Uses `redis-agent-memory`, so there is no Agent Memory Server and no Docker to set up. Wires `RedisSessionMemoryService` and `RedisLongTermMemoryService` to an agent with ADK's built-in `preload_memory` and `load_memory` tools.\n\n[View on GitHub](https://github.com/redis-developer/adk-redis/tree/main/examples/managed_memory_quickstart)"},{"id":"travel_agent_memory_tools","title":"`travel_agent_memory_tools`","role":"content","text":"**Backend:** `redis-agent-memory`, switchable &middot; **Run:** `adk web .`\n\nUses REST-based memory tools exclusively, without framework-managed services. The LLM has full control over when to search, create, update, and delete memories. Set `REDIS_MEMORY_BACKEND` to switch backends.\n\n[View on GitHub](https://github.com/redis-developer/adk-redis/tree/main/examples/travel_agent_memory_tools)"},{"id":"redis_search_tools","title":"`redis_search_tools`","role":"content","text":"**Capability:** Vector, text, and range search &middot; **Run:** `adk web .`\n\nThree in-process RedisVL [search tools](https://redis.io/docs/latest/integrate/google-adk/search-tools) plugged into a single agent with a product catalog dataset.\n\n[View on GitHub](https://github.com/redis-developer/adk-redis/tree/main/examples/redis_search_tools)"},{"id":"redis_sql_search","title":"`redis_sql_search`","role":"content","text":"**Capability:** SQL `SELECT` search &middot; **Run:** `adk web .`\n\nA 10-product catalog with the `RedisSQLSearchTool`. The agent emits parameterized SQL (`WHERE category = 'electronics' AND price < :max_price`) to answer structured catalog questions. Requires `pip install 'adk-redis[sql]'`.\n\n[View on GitHub](https://github.com/redis-developer/adk-redis/tree/main/examples/redis_sql_search)"},{"id":"redisvl_mcp_search","title":"`redisvl_mcp_search`","role":"content","text":"**Capability:** RedisVL MCP server via ADK's `McpToolset` &middot; **Run:** `adk web .`\n\nThe MCP counterpart of `redis_search_tools`. A `rvl mcp` server hosts a knowledge-base index in hybrid (vector + BM25) mode and the agent connects via ADK's native `McpToolset`. No adk-redis wrapper involved; the standard `McpToolset` + `StdioConnectionParams` pattern is used.\n\n[View on GitHub](https://github.com/redis-developer/adk-redis/tree/main/examples/redisvl_mcp_search)"},{"id":"semantic_cache","title":"`semantic_cache`","role":"content","text":"**Capability:** Local semantic caching (RedisVL) &middot; **Run:** `python main.py`\n\nDemonstrates LLM response caching and tool result caching using the `RedisVLCacheProvider` with local embeddings and ADK callbacks.\n\n[View on GitHub](https://github.com/redis-developer/adk-redis/tree/main/examples/semantic_cache)"},{"id":"langcache_cache","title":"`langcache_cache`","role":"content","text":"**Capability:** Managed semantic caching (LangCache) &middot; **Run:** `python main.py`\n\nUses the managed [LangCache](https://redis.io/docs/latest/integrate/google-adk/semantic-caching) service for semantic caching with server-side embeddings. No local vectorizer required.\n\n[View on GitHub](https://github.com/redis-developer/adk-redis/tree/main/examples/langcache_cache)"},{"id":"running-an-example","title":"Running an example","role":"content","text":"Examples marked `python main.py` run as scripts. Examples marked `adk web .`\nrun in the ADK developer UI from inside the example directory.\n\n[code example]"},{"id":"deprecated-backend-examples","title":"Deprecated backend examples","role":"content","text":"Three examples are written against the deprecated `opensource-agent-memory`\nbackend: `simple_redis_memory`, `travel_agent_memory_hybrid`, and\n`fitness_coach_mcp`. They are listed under\n[Agent Memory Server (deprecated)](https://redis.io/docs/latest/integrate/google-adk/agent-memory-server#examples-still-on-this-backend).\nFor a `redis-agent-memory` starting point, use `managed_memory_quickstart` above."},{"id":"more-info","title":"More info","role":"content","text":"- [Car dealership tutorial](https://redis.io/tutorials/build-a-car-dealership-agent-with-google-adk-and-redis-agent-memory/): Full walkthrough building an agent from scratch\n- [adk-redis README](https://github.com/redis-developer/adk-redis): Installation and overview"}],"examples":[{"id":"running-an-example-ex0","language":"bash","code":"pip install adk-redis[all]\ncd examples/managed_memory_quickstart\nexport GOOGLE_API_KEY=your-key\npython main.py","section_id":"running-an-example"}]}
{"schema_version":2,"id":"integrate/google-adk","title":"Redis with Google Agent Development Kit (ADK)","url":"https://redis.io/docs/latest/integrate/google-adk/","summary":"Build AI agents with persistent memory, semantic search, and caching using Redis and Google ADK.","tags":["docs","integrate","oss","rs","rc"],"last_updated":"2026-09-17T14:55:03+01:00","children":[{"id":"integrate/google-adk/redis-agent-memory","summary":"Session and long-term memory for Google ADK agents using Redis Agent Memory.","title":"Redis Agent Memory","url":"https://redis.io/docs/latest/integrate/google-adk/redis-agent-memory/"},{"id":"integrate/google-adk/integration-patterns","summary":"Approaches for connecting Google ADK agents to Redis memory.","title":"Memory integration patterns","url":"https://redis.io/docs/latest/integrate/google-adk/integration-patterns/"},{"id":"integrate/google-adk/search-tools","summary":"Vector, hybrid, text, range, and SQL search tools for Google ADK agents, plus the RedisVL MCP server.","title":"Redis search tools","url":"https://redis.io/docs/latest/integrate/google-adk/search-tools/"},{"id":"integrate/google-adk/semantic-caching","summary":"Cache LLM responses and tool results using semantic similarity with Redis.","title":"Semantic caching","url":"https://redis.io/docs/latest/integrate/google-adk/semantic-caching/"},{"id":"integrate/google-adk/examples","summary":"Complete examples for every adk-redis capability.","title":"adk-redis examples","url":"https://redis.io/docs/latest/integrate/google-adk/examples/"},{"id":"integrate/google-adk/agent-memory-server","summary":"Deprecated. The opensource-agent-memory backend is no longer maintained. Migrate existing deployments to Redis Agent Memory.","title":"Agent Memory Server (deprecated)","url":"https://redis.io/docs/latest/integrate/google-adk/agent-memory-server/"}],"page_type":"content","content_hash":"798680335167141737f173c8940edc9340c6d566361713276badb3a8316e27bf","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Google Agent Development Kit (ADK)](https://google.github.io/adk-docs/) provides clean abstractions for building AI agents: interfaces for memory, sessions, tools, and callbacks. [adk-redis](https://github.com/redis-developer/adk-redis) implements these interfaces using Redis, giving agents persistent two-tier memory, semantic search for RAG, and response caching without requiring changes to agent logic."},{"id":"architecture","title":"Architecture","role":"content","text":"adk-redis connects several backend systems to the ADK framework:\n\n- **[Redis Agent Memory](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory)** handles working memory (sessions), long-term memory (extracted facts), auto-summarization, and memory search. Use the default `redis-agent-memory` for new work. It runs either on [Redis Cloud](https://redis.io/docs/latest/operate/rc/context-engine/agent-memory) or [self-managed](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed) on your own Kubernetes cluster; both share one Data Plane API, so you pick a deployment by pointing `api_base_url` at the right endpoint.\n- **[RedisVL](https://redis.io/docs/latest/develop/ai/redisvl)** (Redis Vector Library) powers the search tools and local semantic cache provider.\n- **[LangCache](https://redis.io/langcache/)** provides managed semantic caching with server-side embeddings.\n\n> [!NOTE]\n> [Agent Memory Server](https://github.com/redis/agent-memory-server)\n> (`opensource-agent-memory`) is now deprecated. If you have an existing\n> deployment, see\n> [Agent Memory Server (deprecated)](https://redis.io/docs/latest/integrate/google-adk/agent-memory-server),\n> which also covers migrating to Redis Agent Memory."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"- **Redis 8.4+** with vector search support, for the search tools and the local semantic cache\n- **A [Redis Agent Memory](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory) store**, for the session and memory services, which gives you a Data Plane endpoint, an API key, and a store ID\n\nProvision a store, then pass its Data Plane endpoint, API key, and store ID to the services.\n\n- On **Redis Cloud**, there is nothing to run. See [Create an Agent Memory service](https://redis.io/docs/latest/operate/iris/agent-memory/create-service).\n- To run it **yourself**, see [Self-managed Agent Memory](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed) for deployment, configuration, and operations on your own Kubernetes cluster.\n\nBoth use `backend=\"redis-agent-memory\"`. Only `api_base_url` differs."},{"id":"installation","title":"Installation","role":"setup","text":"[code example]\n\nThe `memory` extra requires `redis-agent-memory>=0.2.0`."},{"id":"quick-start","title":"Quick start","role":"content","text":"Wire up Redis Agent Memory in a few lines:\n\n[code example]"},{"id":"capabilities","title":"Capabilities","role":"content","text":"| Capability | Description | Page |\n|------------|-------------|------|\n| **Redis Agent Memory** | Session and long-term memory on Redis Cloud or self-managed, via framework services or REST tools | [Redis Agent Memory](https://redis.io/docs/latest/integrate/google-adk/redis-agent-memory) |\n| **Integration patterns** | Framework-managed, LLM-controlled REST, and MCP tools | [Integration patterns](https://redis.io/docs/latest/integrate/google-adk/integration-patterns) |\n| **Search tools** | Vector, hybrid, text, range, and SQL search via RedisVL, plus the `rvl mcp` server over `McpToolset` | [Search tools](https://redis.io/docs/latest/integrate/google-adk/search-tools) |\n| **Semantic caching** | LLM response and tool result caching, with stable entry IDs and targeted invalidation | [Semantic caching](https://redis.io/docs/latest/integrate/google-adk/semantic-caching) |\n| **Examples** | Complete examples covering all capabilities | [Examples](https://redis.io/docs/latest/integrate/google-adk/examples) |\n| **Agent Memory Server** (deprecated) | Reference for the deprecated `opensource-agent-memory` backend, and how to migrate off it | [Agent Memory Server (deprecated)](https://redis.io/docs/latest/integrate/google-adk/agent-memory-server) |"},{"id":"more-info","title":"More info","role":"content","text":"- [adk-redis on GitHub](https://github.com/redis-developer/adk-redis)\n- [adk-redis on PyPI](https://pypi.org/project/adk-redis/)\n- [Car dealership tutorial](https://redis.io/tutorials/build-a-car-dealership-agent-with-google-adk-and-redis-agent-memory/)\n- [RedisVL documentation](https://redis.io/docs/latest/develop/ai/redisvl)\n- [Google ADK documentation](https://google.github.io/adk-docs/)"}],"examples":[{"id":"installation-ex0","language":"bash","code":"# Memory and session services (both backends)\npip install adk-redis[memory]\n\n# Search tools via RedisVL\npip install adk-redis[search]\n\n# SQL-style search tool (sql-redis)\npip install adk-redis[sql]\n\n# Managed semantic caching via LangCache\npip install adk-redis[langcache]\n\n# Everything\npip install adk-redis[all]\n\n# For the RedisVL MCP server (used with ADK's native McpToolset)\npip install 'redisvl[mcp]>=0.18.2'","section_id":"installation"},{"id":"quick-start-ex0","language":"python","code":"from google.adk import Agent\nfrom google.adk.agents.callback_context import CallbackContext\nfrom google.adk.runners import Runner\nfrom adk_redis.sessions import (\n    RedisSessionMemoryService,\n    RedisSessionMemoryServiceConfig,\n)\nfrom adk_redis.memory import (\n    RedisLongTermMemoryService,\n    RedisLongTermMemoryServiceConfig,\n)\n\nsession_service = RedisSessionMemoryService(\n    config=RedisSessionMemoryServiceConfig(\n        backend=\"redis-agent-memory\",\n        api_base_url=\"https://your-endpoint.redis.io\",\n        api_key=\"your-api-key\",\n        store_id=\"your-store-id\",\n        default_namespace=\"my_app\",\n    )\n)\nmemory_service = RedisLongTermMemoryService(\n    config=RedisLongTermMemoryServiceConfig(\n        backend=\"redis-agent-memory\",\n        api_base_url=\"https://your-endpoint.redis.io\",\n        api_key=\"your-api-key\",\n        store_id=\"your-store-id\",\n        default_namespace=\"my_app\",\n    )\n)\n\nasync def after_agent(callback_context: CallbackContext):\n    await callback_context.add_session_to_memory()\n\nagent = Agent(\n    name=\"my_agent\",\n    model=\"gemini-2.5-flash\",\n    instruction=\"You are a helpful assistant with long-term memory.\",\n    after_agent_callback=after_agent,\n)\n\nrunner = Runner(\n    agent=agent,\n    app_name=\"my_app\",\n    session_service=session_service,\n    memory_service=memory_service,\n)","section_id":"quick-start"}]}
{"schema_version":2,"id":"integrate/google-adk/integration-patterns","title":"Memory integration patterns","url":"https://redis.io/docs/latest/integrate/google-adk/integration-patterns/","summary":"Approaches for connecting Google ADK agents to Redis memory.","tags":["docs","integrate","oss","rs","rc"],"last_updated":"2026-09-17T14:55:03+01:00","page_type":"content","content_hash":"41f7269f1d4d7def191508d580d36c8e0704af46ae1712ca95e9193bc71e46bf","sections":[{"id":"overview","title":"Overview","role":"overview","text":"adk-redis offers two approaches for connecting agents to memory. Each has different tradeoffs around control and complexity."},{"id":"comparison","title":"Comparison","role":"content","text":"| Approach | Control | Complexity | Protocol | Best for |\n|----------|---------|-----------|----------|----------|\n| **ADK services** | Framework | Low | HTTP | Invisible infrastructure |\n| **REST tools** | LLM | Medium | HTTP | Explicit memory management |\n\nBoth use `backend=\"redis-agent-memory\"`, the default, on Redis Cloud or\nself-managed. See\n[Redis Agent Memory](https://redis.io/docs/latest/integrate/google-adk/redis-agent-memory#choose-a-deployment)\nfor the deployment options.\n\n> [!NOTE]\n> A third approach, MCP memory tools, is only available on the deprecated\n> [Agent Memory Server](https://redis.io/docs/latest/integrate/google-adk/agent-memory-server)\n> backend."},{"id":"1-adk-services-framework-managed","title":"1. ADK services (framework-managed)","role":"content","text":"Configure `RedisSessionMemoryService` and `RedisLongTermMemoryService`, pass them to the `Runner`, and the framework handles everything automatically. Memory extraction happens in the background. Search happens before each agent turn. The agent code never directly interacts with memory.\n\n[code example]\n\n**Tradeoffs:** Simplest to implement, hardest to customize. The agent has no explicit control over what gets stored or when it searches."},{"id":"2-rest-tools-llm-controlled","title":"2. REST tools (LLM-controlled)","role":"content","text":"Give the agent explicit memory tools that the LLM calls like any other function. The LLM decides when to search memory, what to store, and what to update.\n\n[code example]\n\n**Tradeoffs:** Requires prompt engineering to teach the LLM memory management strategy, but gives the agent genuine autonomy over its own memory."},{"id":"hybrid-approach","title":"Hybrid approach","role":"content","text":"The most powerful configuration combines framework services with REST tools. Framework services handle session persistence and automatic background extraction. REST tools give the LLM explicit CRUD control on top.\n\n[code example]\n\nThe pattern works on `redis-agent-memory`. The\nexample that demonstrates it,\n[travel_agent_memory_hybrid](https://github.com/redis-developer/adk-redis/tree/main/examples/travel_agent_memory_hybrid),\nis currently written against the deprecated backend; see\n[Agent Memory Server (deprecated)](https://redis.io/docs/latest/integrate/google-adk/agent-memory-server#examples-still-on-this-backend)."},{"id":"more-info","title":"More info","role":"content","text":"- [managed_memory_quickstart](https://github.com/redis-developer/adk-redis/tree/main/examples/managed_memory_quickstart): Framework services on Redis Agent Memory\n- [travel_agent_memory_tools](https://github.com/redis-developer/adk-redis/tree/main/examples/travel_agent_memory_tools): REST tools only\n- [Agent Memory Server (deprecated)](https://redis.io/docs/latest/integrate/google-adk/agent-memory-server): MCP tools and other deprecated-backend patterns\n- [Car dealership tutorial](https://redis.io/tutorials/build-a-car-dealership-agent-with-google-adk-and-redis-agent-memory/)"}],"examples":[{"id":"1-adk-services-framework-managed-ex0","language":"python","code":"from google.adk.runners import Runner\nfrom adk_redis.sessions import (\n    RedisSessionMemoryService,\n    RedisSessionMemoryServiceConfig,\n)\nfrom adk_redis.memory import (\n    RedisLongTermMemoryService,\n    RedisLongTermMemoryServiceConfig,\n)\n\nrunner = Runner(\n    agent=agent,\n    app_name=\"my_app\",\n    session_service=RedisSessionMemoryService(\n        config=RedisSessionMemoryServiceConfig(\n            backend=\"redis-agent-memory\",\n            api_base_url=\"https://your-endpoint.redis.io\",\n            api_key=\"your-api-key\",\n            store_id=\"your-store-id\",\n            default_namespace=\"my_app\",\n        )\n    ),\n    memory_service=RedisLongTermMemoryService(\n        config=RedisLongTermMemoryServiceConfig(\n            backend=\"redis-agent-memory\",\n            api_base_url=\"https://your-endpoint.redis.io\",\n            api_key=\"your-api-key\",\n            store_id=\"your-store-id\",\n            default_namespace=\"my_app\",\n        )\n    ),\n)","section_id":"1-adk-services-framework-managed"},{"id":"2-rest-tools-llm-controlled-ex0","language":"python","code":"from adk_redis.tools.memory import (\n    SearchMemoryTool,\n    CreateMemoryTool,\n    UpdateMemoryTool,\n    DeleteMemoryTool,\n    MemoryToolConfig,\n)\n\nconfig = MemoryToolConfig(\n    backend=\"redis-agent-memory\",\n    api_base_url=\"https://your-endpoint.redis.io\",\n    api_key=\"your-api-key\",\n    store_id=\"your-store-id\",\n    default_namespace=\"my_app\",\n)\n\nagent = Agent(\n    model=\"gemini-2.5-flash\",\n    name=\"memory_agent\",\n    tools=[\n        SearchMemoryTool(config=config),\n        CreateMemoryTool(config=config),\n        UpdateMemoryTool(config=config),\n        DeleteMemoryTool(config=config),\n    ],\n)","section_id":"2-rest-tools-llm-controlled"},{"id":"hybrid-approach-ex0","language":"python","code":"# LLM-controlled tools on the Agent\nagent = Agent(\n    model=\"gemini-2.5-flash\",\n    name=\"hybrid_agent\",\n    tools=[\n        SearchMemoryTool(config=config),\n        CreateMemoryTool(config=config),\n        UpdateMemoryTool(config=config),\n        DeleteMemoryTool(config=config),\n    ],\n)\n\n# Framework-managed services on the Runner\nrunner = Runner(\n    agent=agent,\n    app_name=\"my_app\",\n    session_service=session_service,   # Auto session management\n    memory_service=memory_service,     # Auto memory search\n)","section_id":"hybrid-approach"}]}
{"schema_version":2,"id":"integrate/google-adk/redis-agent-memory","title":"Redis Agent Memory","url":"https://redis.io/docs/latest/integrate/google-adk/redis-agent-memory/","summary":"Session and long-term memory for Google ADK agents using Redis Agent Memory.","tags":["docs","integrate","oss","rs","rc"],"last_updated":"2026-09-17T14:55:03+01:00","page_type":"content","content_hash":"0dea09fdc665fd1eae5384bdbd31ec11ba2e08906ee6c198b61621cf2133734c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Agent Memory gives ADK agents two tiers of persistent memory:\n\n- **Session memory**: session-scoped storage for the current conversation.\n- **Long-term memory**: facts extracted from past conversations, stored as vectors in Redis and searchable by semantic similarity with recency boosting."},{"id":"choose-a-deployment","title":"Choose a deployment","role":"content","text":"Set `backend=\"redis-agent-memory\"`, the default, on every service and tool\nconfig. Redis Cloud and self-managed Agent Memory share one\n[Data Plane API](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/api-reference),\nso you select a deployment by pointing `api_base_url` at the right Data Plane,\nnot by changing `backend`:\n\n| Deployment | `api_base_url` | Setup |\n|------------|----------------|-------|\n| Redis Cloud | Your Redis Cloud Agent Memory endpoint | [Create an Agent Memory service](https://redis.io/docs/latest/operate/iris/agent-memory/create-service) |\n| Self-managed | Your own Data Plane URL | [Self-managed Agent Memory](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed) |\n\n> [!NOTE]\n> Running Agent Memory yourself does not mean using the deprecated\n> `opensource-agent-memory` backend. Self-managed Agent Memory is supported and\n> maintained, and uses `backend=\"redis-agent-memory\"` like Redis Cloud. The\n> deprecated backend targets a different system, the open source Agent Memory\n> Server, which does not speak the Data Plane API. See\n> [Agent Memory Server (deprecated)](https://redis.io/docs/latest/integrate/google-adk/agent-memory-server)\n> if you have an existing deployment to migrate.\n\nWire memory into an ADK agent one of two ways:\n\n| Approach | Control | Best for |\n|----------|---------|----------|\n| **Framework services** | ADK Runner (automatic) | Invisible infrastructure |\n| **REST tools** | LLM (explicit) | Agent autonomy over memory |\n\nSee [Integration patterns](https://redis.io/docs/latest/integrate/google-adk/integration-patterns) for detailed tradeoff comparison."},{"id":"session-memory","title":"Session memory","role":"content","text":"`RedisSessionMemoryService` implements ADK's `BaseSessionService`. It stores the current conversation in the configured memory backend.\n\n[code example]\n\n> [!NOTE]\n> `RedisWorkingMemorySessionService` and `RedisWorkingMemorySessionServiceConfig`\n> were renamed to `RedisSessionMemoryService` and\n> `RedisSessionMemoryServiceConfig` in adk-redis 0.0.8. The old names remain as\n> deprecated aliases that emit a `DeprecationWarning` and will be removed in\n> 0.1.0. The module `adk_redis.sessions.working_memory` also moved to\n> `adk_redis.sessions.session_memory`."},{"id":"configuration","title":"Configuration","role":"configuration","text":"| Parameter | Description | Default |\n|-----------|-------------|---------|\n| `backend` | Memory backend | `redis-agent-memory` |\n| `api_base_url` | Data Plane endpoint | `http://localhost:8000` |\n| `api_key` | API key | `None` |\n| `store_id` | Store ID | `None` |\n| `default_namespace` | Isolates data between applications | `None` |\n| `timeout` | Request timeout in seconds | `30.0` |\n| `timeout_ms` | Request timeout in milliseconds. Overrides `timeout`. | `None` |\n| `session_ttl_seconds` | Expiry for stored sessions | `None` |"},{"id":"incremental-appends","title":"Incremental appends","role":"content","text":"The session service uses an incremental append API: it sends only new messages rather than re-sending the entire conversation on every turn. Network overhead stays proportional to message size, not conversation length."},{"id":"supported-operations","title":"Supported operations","role":"compatibility","text":"The service implements all of ADK's session methods:\n- `create_session`: Create a new session\n- `get_session`: Retrieve an existing session\n- `list_sessions`: List sessions for an app/user\n- `delete_session`: Remove a session\n- `append_event`: Add a new message (incremental)"},{"id":"long-term-memory","title":"Long-term memory","role":"content","text":"`RedisLongTermMemoryService` implements ADK's `BaseMemoryService`. After each conversation, the memory backend extracts structured information (facts, preferences, episodic events), embeds them as vectors, and stores them in Redis for semantic search across all past sessions.\n\n[code example]"},{"id":"configuration-1","title":"Configuration","role":"configuration","text":"| Parameter | Description | Default |\n|-----------|-------------|---------|\n| `backend` | Memory backend | `redis-agent-memory` |\n| `api_base_url` | Data Plane endpoint | `http://localhost:8000` |\n| `api_key` | API key | `None` |\n| `store_id` | Store ID | `None` |\n| `default_namespace` | Namespace for data isolation | `None` |\n| `timeout` | Request timeout in seconds | `30.0` |\n| `search_top_k` | Maximum memories returned per search | `10` |\n| `similarity_threshold` | Minimum similarity for a match (0-1) | `None` |\n| `distance_threshold` | Maximum vector distance for a match (0-1) | `None` |\n| `store_events_as_messages` | Store session events as chat messages | `True` |\n| `default_memory_type` | Memory type applied to new memories | `semantic` |\n| `default_topics` | Topics applied to new memories | `[]` |"},{"id":"framework-services","title":"Framework services","role":"content","text":"Pass both services to an ADK `Runner`. The framework handles memory automatically: sessions are persisted via session memory, long-term memory is searched before each agent turn, and an `after_agent_callback` triggers extraction in the background.\n\n[code example]"},{"id":"runtime-flow","title":"Runtime flow","role":"content","text":"1. ADK creates or retrieves a session via `RedisSessionMemoryService`.\n2. Long-term memory is searched for context relevant to the current conversation.\n3. User messages are appended to session memory incrementally.\n4. The LLM generates a response using session context plus retrieved memories.\n5. `after_agent_callback` triggers `add_session_to_memory()` for background extraction."},{"id":"rest-tools","title":"REST tools","role":"content","text":"Give the agent explicit memory tools that the LLM calls like any other function. The LLM decides when to search memory, what to store, and what to update. No framework services required. The tools share a single `MemoryToolConfig`.\n\nadk-redis ships six memory tools:\n\n| Tool | Description |\n|------|-------------|\n| `SearchMemoryTool` | Search long-term memories by query |\n| `CreateMemoryTool` | Store new long-term memories |\n| `GetMemoryTool` | Fetch a single memory by ID |\n| `UpdateMemoryTool` | Update an existing memory by ID |\n| `DeleteMemoryTool` | Delete memories by ID |\n| `MemoryPromptTool` | Enrich the agent prompt with relevant memories |\n\n[code example]\n\nRequires prompt engineering to teach the LLM memory management strategy, but gives the agent genuine autonomy over its own memory."},{"id":"invocation-scoped-users","title":"Invocation-scoped users","role":"content","text":"The memory tools resolve the acting user from the ADK `tool_context` before falling back to the user configured on `MemoryToolConfig`. A single shared `Runner` therefore stays scoped to the user of each invocation, with no per-user tool instances.\n\n`CreateMemoryTool.run_async()` also accepts an application-supplied `id` for idempotent writes against Redis Agent Memory. IDs are derived with namespace and user scope to prevent cross-tenant collisions, and are never exposed to the LLM."},{"id":"mcp-tools","title":"MCP tools","role":"content","text":"MCP memory tools are only available on the deprecated\n[Agent Memory Server](https://redis.io/docs/latest/integrate/google-adk/agent-memory-server)\nbackend. The `redis-agent-memory` backend does not expose an MCP endpoint, on\nRedis Cloud or self-managed; use the REST tools above."},{"id":"more-info","title":"More info","role":"content","text":"- [Integration patterns](https://redis.io/docs/latest/integrate/google-adk/integration-patterns): Detailed tradeoff comparison of the approaches\n- [managed_memory_quickstart](https://github.com/redis-developer/adk-redis/tree/main/examples/managed_memory_quickstart): Framework services, no Docker\n- [travel_agent_memory_tools](https://github.com/redis-developer/adk-redis/tree/main/examples/travel_agent_memory_tools): REST tools only\n- [Self-managed Agent Memory](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed): run Agent Memory on your own Kubernetes cluster\n- [Agent Memory Server (deprecated)](https://redis.io/docs/latest/integrate/google-adk/agent-memory-server): existing deployments and migration"}],"examples":[{"id":"session-memory-ex0","language":"python","code":"from adk_redis.sessions import (\n    RedisSessionMemoryService,\n    RedisSessionMemoryServiceConfig,\n)\n\nsession_service = RedisSessionMemoryService(\n    config=RedisSessionMemoryServiceConfig(\n        backend=\"redis-agent-memory\",\n        api_base_url=\"https://your-endpoint.redis.io\",\n        api_key=\"your-api-key\",\n        store_id=\"your-store-id\",\n        default_namespace=\"my_app\",\n    )\n)","section_id":"session-memory"},{"id":"long-term-memory-ex0","language":"python","code":"from adk_redis.memory import (\n    RedisLongTermMemoryService,\n    RedisLongTermMemoryServiceConfig,\n)\n\nmemory_service = RedisLongTermMemoryService(\n    config=RedisLongTermMemoryServiceConfig(\n        backend=\"redis-agent-memory\",\n        api_base_url=\"https://your-endpoint.redis.io\",\n        api_key=\"your-api-key\",\n        store_id=\"your-store-id\",\n        default_namespace=\"my_app\",\n    )\n)","section_id":"long-term-memory"},{"id":"framework-services-ex0","language":"python","code":"from google.adk import Agent\nfrom google.adk.agents.callback_context import CallbackContext\nfrom google.adk.runners import Runner\n\nasync def after_agent(callback_context: CallbackContext):\n    await callback_context.add_session_to_memory()\n\nagent = Agent(\n    name=\"memory_agent\",\n    model=\"gemini-2.5-flash\",\n    instruction=\"You are a helpful assistant with long-term memory.\",\n    after_agent_callback=after_agent,\n)\n\nrunner = Runner(\n    agent=agent,\n    app_name=\"my_app\",\n    session_service=session_service,\n    memory_service=memory_service,\n)","section_id":"framework-services"},{"id":"rest-tools-ex0","language":"python","code":"from adk_redis.tools.memory import (\n    SearchMemoryTool,\n    CreateMemoryTool,\n    GetMemoryTool,\n    UpdateMemoryTool,\n    DeleteMemoryTool,\n    MemoryPromptTool,\n    MemoryToolConfig,\n)\n\nconfig = MemoryToolConfig(\n    backend=\"redis-agent-memory\",\n    api_base_url=\"https://your-endpoint.redis.io\",\n    api_key=\"your-api-key\",\n    store_id=\"your-store-id\",\n    default_namespace=\"my_app\",\n)\n\nagent = Agent(\n    model=\"gemini-2.5-flash\",\n    name=\"memory_agent\",\n    tools=[\n        SearchMemoryTool(config=config),\n        CreateMemoryTool(config=config),\n        GetMemoryTool(config=config),\n        UpdateMemoryTool(config=config),\n        DeleteMemoryTool(config=config),\n        MemoryPromptTool(config=config),\n    ],\n)","section_id":"rest-tools"}]}
{"schema_version":2,"id":"integrate/google-adk/search-tools","title":"Redis search tools","url":"https://redis.io/docs/latest/integrate/google-adk/search-tools/","summary":"Vector, hybrid, text, range, and SQL search tools for Google ADK agents, plus the RedisVL MCP server.","tags":["docs","integrate","oss","rs","rc"],"last_updated":"2026-09-17T14:55:03+01:00","page_type":"content","content_hash":"3f6020cfca816033c20ab02bbcae14b7e8275fc8e2ea1656bdbfbc3aaf07cacb","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"adk-redis provides five in-process search tools that wrap [RedisVL](https://redis.io/docs/latest/develop/ai/redisvl) query types into ADK-compatible tools. The LLM sees each tool as a callable function and gets back structured results. For multi-agent or polyglot deployments, the same RedisVL index can also be served over MCP via the `rvl mcp` server and consumed with ADK's native `McpToolset` (see the [RedisVL MCP server](#redisvl-mcp-server) section below)."},{"id":"overview","title":"Overview","role":"overview","text":"| Tool | Query type | Use case |\n|------|-----------|----------|\n| `RedisVectorSearchTool` | KNN vector similarity | Conceptual/semantic queries |\n| `RedisHybridSearchTool` | Vector + BM25 | Queries with specific terms + concepts |\n| `RedisTextSearchTool` | BM25 keyword | Exact terms, error messages, IDs |\n| `RedisRangeSearchTool` | Distance threshold | Exhaustive retrieval within a radius |\n| `RedisSQLSearchTool` | SQL `SELECT` | Structured filters (`WHERE`, `BETWEEN`, parameterized) |"},{"id":"vector-search","title":"Vector search","role":"content","text":"Embeds the query using a vectorizer and performs K-nearest-neighbor search.\n\n[code example]"},{"id":"hybrid-search","title":"Hybrid search","role":"content","text":"Combines vector similarity with BM25 keyword matching. Auto-detects whether your Redis server supports native hybrid search (Redis 8.4+ with RedisVL 0.13+). Falls back to client-side aggregation if not.\n\n[code example]"},{"id":"text-search","title":"Text search","role":"content","text":"Pure BM25 keyword search. No embeddings or vectorizer needed.\n\n[code example]"},{"id":"range-search","title":"Range search","role":"content","text":"Returns all documents within a distance threshold instead of top-K. Useful for exhaustive retrieval.\n\n[code example]"},{"id":"sql-search","title":"SQL search","role":"content","text":"`RedisSQLSearchTool` wraps `redisvl.query.SQLQuery`. The LLM emits a SQL `SELECT` statement (with optional `:name` parameter placeholders) and the tool translates it into the right `FT.SEARCH` or `FT.AGGREGATE` call. Best for structured filters: tag equality, numeric ranges, multi-predicate `WHERE` clauses. Requires `pip install 'adk-redis[sql]'`.\n\n[code example]\n\nThe LLM might emit a call like:\n\n[code example]\n\nwith `params={\"max_price\": 100}`. See the [redis_sql_search example](https://github.com/redis-developer/adk-redis/tree/main/examples/redis_sql_search) for a runnable demo."},{"id":"redisvl-mcp-server","title":"RedisVL MCP server","role":"content","text":"The five tools above run in-process against a Python `SearchIndex`. To serve one Redis index to multiple agents (Python, JS, Claude Desktop), or to put server-side guardrails like `--read-only` and bearer auth between the agent and the index, run RedisVL's MCP server (`rvl mcp`) and connect ADK's native `McpToolset` to it.\n\n[code example]\n\nThe server is configured per index via a YAML file and exposes two tools: `search-records` (vector / fulltext / hybrid, chosen at server start, with schema-aware filter and return-field hints) and `upsert-records` (suppress with `--read-only`). Supports `stdio`, `sse`, and `streamable-http` transports; bearer auth on HTTP.\n\nInstall the CLI with `pip install 'redisvl[mcp]>=0.18.2'`. For a runnable demo, see the [redisvl_mcp_search example](https://github.com/redis-developer/adk-redis/tree/main/examples/redisvl_mcp_search)."},{"id":"when-to-choose-in-process-vs-mcp","title":"When to choose in-process vs MCP","role":"content","text":"| Path | Use when |\n|------|----------|\n| In-process tools (above) | Single Python agent, fast onboarding, complex Python-side `FilterExpression` composition. |\n| MCP server | Multi-agent or polyglot deployments, server-side ops gates, schema-aware tool descriptions. |\n\nRange and SQL search have no MCP equivalent today; use the in-process tools for either."},{"id":"multi-tool-agent","title":"Multi-tool agent","role":"content","text":"Wire multiple search tools into a single agent and let the LLM choose the right one:\n\n[code example]\n\nThe `name` and `description` on each tool matter: the LLM reads them to decide which tool to call and when. Specific descriptions like \"Find products by semantic similarity\" work better than generic ones like \"search documents\"."},{"id":"more-info","title":"More info","role":"content","text":"- [redis_search_tools example](https://github.com/redis-developer/adk-redis/tree/main/examples/redis_search_tools): vector, text, and range tools with a product catalog\n- [redis_sql_search example](https://github.com/redis-developer/adk-redis/tree/main/examples/redis_sql_search): SQL `SELECT` against a bound index\n- [redisvl_mcp_search example](https://github.com/redis-developer/adk-redis/tree/main/examples/redisvl_mcp_search): same corpus served via `rvl mcp` + ADK `McpToolset`\n- [RedisVL documentation](https://redis.io/docs/latest/develop/ai/redisvl)"}],"examples":[{"id":"vector-search-ex0","language":"python","code":"from redisvl.index import SearchIndex\nfrom redisvl.utils.vectorize import HFTextVectorizer\nfrom adk_redis import RedisVectorSearchTool, RedisVectorQueryConfig\n\nvectorizer = HFTextVectorizer(model=\"redis/langcache-embed-v2\")\nindex = SearchIndex.from_existing(\"products\", redis_url=\"redis://localhost:6379\")\n\nvector_tool = RedisVectorSearchTool(\n    index=index,\n    vectorizer=vectorizer,\n    config=RedisVectorQueryConfig(num_results=5),\n    return_fields=[\"name\", \"description\", \"price\"],\n    name=\"search_products\",\n    description=\"Find products by semantic similarity.\",\n)","section_id":"vector-search"},{"id":"hybrid-search-ex0","language":"python","code":"from adk_redis import RedisHybridSearchTool, RedisHybridQueryConfig\n\nhybrid_tool = RedisHybridSearchTool(\n    index=index,\n    vectorizer=vectorizer,\n    config=RedisHybridQueryConfig(\n        text_field_name=\"content\",\n        combination_method=\"LINEAR\",\n        linear_alpha=0.7,\n    ),\n    name=\"search_docs\",\n    description=\"Search documents using semantic and keyword matching.\",\n)","section_id":"hybrid-search"},{"id":"text-search-ex0","language":"python","code":"from adk_redis import RedisTextSearchTool, RedisTextQueryConfig\n\ntext_tool = RedisTextSearchTool(\n    index=index,\n    config=RedisTextQueryConfig(\n        text_field_name=\"content\",\n        text_scorer=\"BM25STD\",\n    ),\n    return_fields=[\"title\", \"content\"],\n    name=\"keyword_search\",\n    description=\"Search for exact terms and phrases.\",\n)","section_id":"text-search"},{"id":"range-search-ex0","language":"python","code":"from adk_redis import RedisRangeSearchTool, RedisRangeQueryConfig\n\nrange_tool = RedisRangeSearchTool(\n    index=index,\n    vectorizer=vectorizer,\n    config=RedisRangeQueryConfig(distance_threshold=0.5),\n    return_fields=[\"title\", \"content\"],\n    name=\"range_search\",\n    description=\"Find all documents within a semantic distance threshold.\",\n)","section_id":"range-search"},{"id":"sql-search-ex0","language":"python","code":"from adk_redis import RedisSQLSearchTool\n\nsql_tool = RedisSQLSearchTool(\n    index=index,\n    name=\"catalog_sql_search\",\n    description=(\n        \"Run a SQL SELECT against the product catalog. \"\n        \"Use :name placeholders for values.\"\n    ),\n)","section_id":"sql-search"},{"id":"sql-search-ex1","language":"sql","code":"SELECT title, brand, price\nFROM products\nWHERE category = 'electronics' AND price < :max_price","section_id":"sql-search"},{"id":"redisvl-mcp-server-ex0","language":"python","code":"from google.adk import Agent\nfrom google.adk.tools.mcp_tool import McpToolset\nfrom google.adk.tools.mcp_tool.mcp_session_manager import StdioConnectionParams\nfrom mcp import StdioServerParameters\n\nagent = Agent(\n    model=\"gemini-2.5-flash\",\n    name=\"redis_mcp_agent\",\n    instruction=\"Use the search-records tool to answer questions.\",\n    tools=[\n        McpToolset(\n            connection_params=StdioConnectionParams(\n                server_params=StdioServerParameters(\n                    command=\"rvl\",\n                    args=[\n                        \"mcp\",\n                        \"--config\",\n                        \"/path/to/mcp_config.yaml\",\n                        \"--read-only\",\n                    ],\n                ),\n                timeout=30,\n            ),\n            tool_filter=[\"search-records\"],\n        ),\n    ],\n)","section_id":"redisvl-mcp-server"},{"id":"multi-tool-agent-ex0","language":"python","code":"from google.adk import Agent\n\nagent = Agent(\n    model=\"gemini-2.5-flash\",\n    name=\"search_agent\",\n    instruction=(\n        \"You have three search tools. Use search_products for conceptual \"\n        \"queries, keyword_search for exact terms, range_search for \"\n        \"exhaustive retrieval.\"\n    ),\n    tools=[vector_tool, text_tool, range_tool],\n)","section_id":"multi-tool-agent"}]}
{"schema_version":2,"id":"integrate/google-adk/semantic-caching","title":"Semantic caching","url":"https://redis.io/docs/latest/integrate/google-adk/semantic-caching/","summary":"Cache LLM responses and tool results using semantic similarity with Redis.","tags":["docs","integrate","oss","rs","rc"],"last_updated":"2026-08-03T10:57:43-04:00","page_type":"content","content_hash":"8fe0910af402178c6f47fa66ba10d8adde499243b004cad917503582e080b04f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"adk-redis provides semantic caching at two levels: LLM response caching and tool result caching, both backed by Redis. Caching uses ADK's callback system, so enabling it requires no changes to your agent's core logic."},{"id":"how-it-works","title":"How it works","role":"content","text":"Before each LLM call (or tool execution), the cache checks whether a semantically similar prompt already exists in Redis. If so, the cached response is returned immediately. If not, the call proceeds and the response is stored for future lookups."},{"id":"cache-providers","title":"Cache providers","role":"content","text":"Two backends are available:\n\n| Provider | Embeddings | Setup | Best for |\n|----------|-----------|-------|----------|\n| `RedisVLCacheProvider` | Local (you provide vectorizer) | Self-managed Redis | Full control |\n| `LangCacheProvider` | Server-side (managed) | API key from Redis Cloud | Zero embedding overhead |"},{"id":"redisvl-provider-local-embeddings","title":"RedisVL provider (local embeddings)","role":"content","text":"[code example]"},{"id":"langcache-provider-managed","title":"LangCache provider (managed)","role":"content","text":"No local vectorizer needed. Embeddings are generated server-side.\n\n[code example]\n\nSet `server_url` to the endpoint for your LangCache region."},{"id":"cache-entry-ids-and-targeted-invalidation","title":"Cache entry IDs and targeted invalidation","role":"content","text":"Both providers return a `CacheEntry` from `check()` and an entry ID from\n`store()`. When the backend exposes a stable identifier, `CacheEntry.entry_id`\ncarries it, and you can retire exactly that entry with `delete_by_id()` instead\nof clearing the whole cache.\n\n[code example]\n\n`CacheEntry` also carries the matched `prompt`, the cached `response`, the match\n`distance`, and any `metadata` stored alongside the entry. `entry_id` is `None`\nwhen the backend does not expose an identifier."},{"id":"llm-response-cache","title":"LLM response cache","role":"content","text":"Intercepts model calls through ADK's `before_model_callback` and `after_model_callback`.\n\n[code example]"},{"id":"configuration-notes","title":"Configuration notes","role":"configuration","text":"- **`first_message_only=True`** caches only the first message in a session. Later messages depend on conversation context, making cache hits unreliable.\n- Function call responses and errors are automatically excluded from caching.\n- **`distance_threshold`** (set on the provider) controls how similar two prompts need to be for a cache hit. `0.0` = exact match only. `0.1` = small phrasing variations. Higher values risk returning wrong cached responses."},{"id":"tool-result-cache","title":"Tool result cache","role":"content","text":"Caches tool executions using `before_tool_callback` and `after_tool_callback`.\n\n[code example]\n\nThe `tool_names` set specifies which tools to cache. Not all tools are idempotent: cache `get_weather` but not `send_email`."},{"id":"more-info","title":"More info","role":"content","text":"- [semantic_cache example](https://github.com/redis-developer/adk-redis/tree/main/examples/semantic_cache): Local caching with RedisVL\n- [langcache_cache example](https://github.com/redis-developer/adk-redis/tree/main/examples/langcache_cache): Managed caching with LangCache"}],"examples":[{"id":"redisvl-provider-local-embeddings-ex0","language":"python","code":"from redisvl.utils.vectorize import HFTextVectorizer\nfrom adk_redis.cache import RedisVLCacheProvider, RedisVLCacheProviderConfig\n\nprovider = RedisVLCacheProvider(\n    config=RedisVLCacheProviderConfig(\n        redis_url=\"redis://localhost:6379\",\n        name=\"my_cache\",\n        ttl=3600,\n        distance_threshold=0.1,\n    ),\n    vectorizer=HFTextVectorizer(model=\"redis/langcache-embed-v2\"),\n)","section_id":"redisvl-provider-local-embeddings"},{"id":"langcache-provider-managed-ex0","language":"python","code":"import os\n\nfrom adk_redis.cache import LangCacheProvider, LangCacheProviderConfig\n\nprovider = LangCacheProvider(\n    config=LangCacheProviderConfig(\n        cache_id=os.environ[\"LANGCACHE_CACHE_ID\"],\n        api_key=os.environ[\"LANGCACHE_API_KEY\"],\n        server_url=\"https://aws-us-east-1.langcache.redis.io\",\n        ttl=3600,\n    )\n)","section_id":"langcache-provider-managed"},{"id":"cache-entry-ids-and-targeted-invalidation-ex0","language":"python","code":"entry = await provider.check(prompt=\"What is the return policy?\")\n\nif entry is not None and entry.entry_id is not None:\n    # Retire one stale answer without touching unrelated entries\n    await provider.delete_by_id(entry.entry_id)","section_id":"cache-entry-ids-and-targeted-invalidation"},{"id":"llm-response-cache-ex0","language":"python","code":"from adk_redis.cache import (\n    LLMResponseCache,\n    LLMResponseCacheConfig,\n    create_llm_cache_callbacks,\n)\n\nllm_cache = LLMResponseCache(\n    provider=provider,\n    config=LLMResponseCacheConfig(\n        first_message_only=True,\n        include_app_name=True,\n        include_user_id=True,\n    ),\n)\n\nbefore_cb, after_cb = create_llm_cache_callbacks(llm_cache)\n\nagent = Agent(\n    name=\"cached_agent\",\n    model=\"gemini-2.5-flash\",\n    instruction=\"You are a helpful assistant.\",\n    before_model_callback=before_cb,\n    after_model_callback=after_cb,\n)","section_id":"llm-response-cache"},{"id":"tool-result-cache-ex0","language":"python","code":"from adk_redis.cache import (\n    ToolCache,\n    ToolCacheConfig,\n    create_tool_cache_callbacks,\n)\n\ntool_cache = ToolCache(\n    provider=provider,\n    config=ToolCacheConfig(\n        tool_names={\"web_search\", \"get_weather\"},\n    ),\n)\n\nbefore_tool_cb, after_tool_cb = create_tool_cache_callbacks(tool_cache)","section_id":"tool-result-cache"}]}
{"schema_version":2,"id":"integrate/heroku-redis","title":"Redis Cloud on Heroku","url":"https://redis.io/docs/latest/integrate/heroku-redis/","summary":"Deploy Redis Cloud as a Heroku add-on","tags":["docs","integrate","rc"],"last_updated":"2025-09-26T14:14:00-04:00","children":[],"page_type":"content","content_hash":"a40d108f2866698c1d8a8f7390860ee78bee14545c93f0d5e5c8ecdc877fa6e0","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"Deploy Redis Cloud as a Heroku add-on to enhance your applications with high-performance data storage and caching capabilities."},{"id":"overview","title":"Overview","role":"overview","text":"The [Redis Cloud Heroku add-on](https://elements.heroku.com/addons/rediscloud) provides a fully managed Redis service as a Heroku add-on, making it easy to quickly add high-performance data storage to your Heroku applications. Redis Cloud on Heroku is perfect for web applications requiring fast caching, session storage, and real-time features."},{"id":"key-features","title":"Key Features","role":"content","text":"- **One-Click Installation**: Easy installation through Heroku add-ons marketplace\n- **Automatic Provisioning**: Instant Redis database setup with connection credentials\n- **Heroku Integration**: Seamless integration with Heroku's platform and tooling\n- **Scaling**: Easy scaling through Heroku dashboard or CLI\n- **Monitoring**: Built-in monitoring and alerting for your Redis databases\n- **Backup & Recovery**: Automated backups and point-in-time recovery\n- **Multi-language Support**: Works with all programming languages supported by Heroku"},{"id":"get-started","title":"Get started","role":"content","text":"Learn how to [add Redis Cloud to Heroku applications](https://devcenter.heroku.com/articles/rediscloud)."}],"examples":[]}
{"schema_version":2,"id":"integrate/hiredis","title":"C client for Redis","url":"https://redis.io/docs/latest/integrate/hiredis/","summary":"Learn how to build with Redis and C","tags":["docs","integrate","oss","rs","rc"],"last_updated":"2026-09-17T14:54:53+01:00","children":[],"page_type":"content","content_hash":"4363784998bb666c7dbf25b18ffc320676a40c20fe95fc63288e380ea8bd37c2","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"Connect your C application to a Redis database using the hiredis client library."},{"id":"overview","title":"Overview","role":"overview","text":"hiredis is a minimalistic C client library for Redis that provides a simple, efficient interface for Redis operations. It's designed to be lightweight, fast, and easy to integrate into C applications with minimal dependencies."},{"id":"key-features","title":"Key Features","role":"content","text":"- **Lightweight**: Minimal memory footprint and dependencies\n- **High Performance**: Optimized for speed and low latency\n- **Synchronous & Asynchronous**: Support for both blocking and non-blocking operations\n- **Pipeline Support**: Efficient command batching for improved throughput\n- **Error Handling**: Comprehensive error reporting and handling\n- **Cross-platform**: Works on Linux, macOS, Windows, and embedded systems\n- **Thread Safe**: Safe for use in multi-threaded applications\n- **Simple API**: Clean, straightforward C interface"},{"id":"getting-started","title":"Getting Started","role":"setup","text":"Refer to the complete [C guide](https://redis.io/docs/latest/develop/clients/hiredis) to install, connect, and use hiredis."}],"examples":[]}
{"schema_version":2,"id":"integrate","title":"Libraries and tools","url":"https://redis.io/docs/latest/integrate/","summary":"Find libraries, tools, frameworks, and platforms that integrate with Redis.","tags":[],"last_updated":"2026-09-28T17:31:37-07:00","children":[{"id":"integrate/redis-data-integration","summary":"","title":"Redis Data Integration","url":"https://redis.io/docs/latest/integrate/redis-data-integration/"},{"id":"integrate/redisinsight","summary":"Visual client tool for creating, managing, and analyzing Redis databases","title":"Redis Insight","url":"https://redis.io/docs/latest/integrate/redisinsight/"},{"id":"integrate/redis-mcp","summary":"Access a Redis server using any MCP client.","title":"Redis MCP","url":"https://redis.io/docs/latest/integrate/redis-mcp/"},{"id":"integrate/redis-py","summary":"Learn how to build with Redis and Python","title":"Python client for Redis","url":"https://redis.io/docs/latest/integrate/redis-py/"},{"id":"integrate/redisvl","summary":"This is the Redis vector library (RedisVL).","title":"RedisVL","url":"https://redis.io/docs/latest/integrate/redisvl/"},{"id":"integrate/riot","summary":"Redis Input/Output Tools","title":"RIOT-X","url":"https://redis.io/docs/latest/integrate/riot/"},{"id":"integrate/jedis","summary":"Learn how to build with Redis and Java","title":"Java client for Redis","url":"https://redis.io/docs/latest/integrate/jedis/"},{"id":"integrate/lettuce","summary":"Learn how to build with Redis and Java","title":"Java client for Redis","url":"https://redis.io/docs/latest/integrate/lettuce/"},{"id":"integrate/node-redis","summary":"Learn how to build with Redis and Node.js","title":"Node.js client for Redis","url":"https://redis.io/docs/latest/integrate/node-redis/"},{"id":"integrate/NRedisStack","summary":"Learn how to build with Redis and C#/.NET","title":"C#/.NET client for Redis","url":"https://redis.io/docs/latest/integrate/nredisstack/"},{"id":"integrate/amazon-bedrock","summary":"Shows how to use your Redis database with Amazon Bedrock to customize foundational models.","title":"Amazon Bedrock","url":"https://redis.io/docs/latest/integrate/amazon-bedrock/"},{"id":"integrate/go-redis","summary":"Learn how to build with Redis and Go","title":"Go client for Redis","url":"https://redis.io/docs/latest/integrate/go-redis/"},{"id":"integrate/n8n-vector-store","summary":"Learn how to use Redis as a vector store with n8n workflows","title":"n8n Redis vector store","url":"https://redis.io/docs/latest/integrate/n8n-vector-store/"},{"id":"integrate/php-redis","summary":"Learn how to build with Redis and PHP","title":"PHP client for Redis","url":"https://redis.io/docs/latest/integrate/php-redis/"},{"id":"integrate/pulumi-provider-for-redis-cloud","summary":"Explains how to use Pulumi to provision Redis Cloud infrastructure","title":"Pulumi provider for Redis Cloud","url":"https://redis.io/docs/latest/integrate/pulumi-provider-for-redis-cloud/"},{"id":"integrate/terraform-provider-for-redis-cloud","summary":"","title":"Terraform provider for Redis Cloud","url":"https://redis.io/docs/latest/integrate/terraform-provider-for-redis-cloud/"},{"id":"integrate/hiredis","summary":"Learn how to build with Redis and C","title":"C client for Redis","url":"https://redis.io/docs/latest/integrate/hiredis/"},{"id":"integrate/prometheus-with-redis-enterprise","summary":"Use Prometheus and Grafana to collect and visualize Redis Software metrics.","title":"Prometheus and Grafana with Redis Software","url":"https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise/"},{"id":"integrate/redis-ai-libraries","summary":"Build AI applications with Redis vector database and semantic caching","title":"Redis for AI","url":"https://redis.io/docs/latest/integrate/redis-ai-libraries/"},{"id":"integrate/prometheus-with-redis-cloud","summary":"Use Prometheus and Grafana to collect and visualize Redis Cloud metrics.","title":"Prometheus and Grafana with Redis Cloud","url":"https://redis.io/docs/latest/integrate/prometheus-with-redis-cloud/"},{"id":"integrate/redis-rb","summary":"Learn how to build with Redis and Ruby","title":"Ruby client for Redis","url":"https://redis.io/docs/latest/integrate/redis-rb/"},{"id":"integrate/datadog-with-redis-cloud","summary":"To collect, view, and monitor metrics data from your databases and other cluster components, you can connect Datadog to your Redis Cloud cluster using the Redis Datadog Integration.","title":"Datadog with Redis Cloud","url":"https://redis.io/docs/latest/integrate/datadog-with-redis-cloud/"},{"id":"integrate/datadog-with-redis-enterprise","summary":"To collect, view, and monitor metrics data from your databases and other cluster components, you can connect Datadog to your Redis Software cluster using the Redis Datadog Integration.","title":"Datadog with Redis Software","url":"https://redis.io/docs/latest/integrate/datadog-with-redis-enterprise/"},{"id":"integrate/dynatrace-with-redis-cloud","summary":"To collect, view, and monitor metrics data from your databases and other cluster components, you can connect Dynatrace to your Redis Cloud cluster using the Redis Dynatrace Integration.","title":"Dynatrace with Redis Cloud","url":"https://redis.io/docs/latest/integrate/dynatrace-with-redis-cloud/"},{"id":"integrate/dynatrace-with-redis-enterprise","summary":"To collect, view, and monitor metrics data from your databases and other cluster components, you can connect Dynatrace to your Redis Software cluster using the Redis Dynatrace Integration.","title":"Dynatrace with Redis Software","url":"https://redis.io/docs/latest/integrate/dynatrace-with-redis-enterprise/"},{"id":"integrate/nagios-with-redis-enterprise","summary":"The Redis Software (RS) Nagios plugin enables you to monitor the status of RS related objects and alerts. The RS alerts can be related to the cluster, nodes, or databases.","title":"Nagios with Redis Software","url":"https://redis.io/docs/latest/integrate/nagios-with-redis-enterprise/"},{"id":"integrate/new-relic-with-redis-cloud","summary":"To collect, view, and monitor metrics data from your databases and other cluster components, you can connect New Relic to your Redis Cloud cluster using the Redis New Relic Integration.","title":"New Relic with Redis Cloud","url":"https://redis.io/docs/latest/integrate/new-relic-with-redis-cloud/"},{"id":"integrate/new-relic-with-redis-enterprise","summary":"To collect, view, and monitor metrics data from your databases and other cluster components, you can connect New Relic to your Redis Software cluster using the Redis New Relic Integration.","title":"New Relic with Redis Software","url":"https://redis.io/docs/latest/integrate/new-relic-with-redis-enterprise/"},{"id":"integrate/railway-redis","summary":"Deploy and host Redis on Railway. The Railway Redis database template allows you to provision and connect to a Redis database with zero configuration.","title":"Redis on Railway","url":"https://redis.io/docs/latest/integrate/railway-redis/"},{"id":"integrate/uptrace-with-redis-enterprise","summary":"To collect, view, and monitor metrics data from your databases and other cluster components, you can connect Uptrace to your Redis Software cluster using OpenTelemetry Collector.","title":"Uptrace with Redis Software","url":"https://redis.io/docs/latest/integrate/uptrace-with-redis-enterprise/"},{"id":"integrate/confluent-with-redis-cloud","summary":"Describes how to integrate Redis Cloud into Confluent Cloud.","title":"Confluent with Redis Cloud","url":"https://redis.io/docs/latest/integrate/confluent-with-redis-cloud/"},{"id":"integrate/spring-framework-cache","summary":"Plug Redis into your Spring application with minimal effort","title":"Spring Data Redis","url":"https://redis.io/docs/latest/integrate/spring-framework-cache/"},{"id":"integrate/fastapi","summary":"Add idiomatic Redis connection management and dependency-injection caching to FastAPI apps.","title":"Redis with FastAPI","url":"https://redis.io/docs/latest/integrate/fastapi/"},{"id":"integrate/fusioncache","summary":"Easy to use, fast and robust hybrid cache with advanced resiliency features for C#/.NET","title":"FusionCache for C#/.NET","url":"https://redis.io/docs/latest/integrate/fusioncache/"},{"id":"integrate/redisom-for-net","summary":"Learn how to build with Redis Stack and .NET","title":"RedisOM for .NET","url":"https://redis.io/docs/latest/integrate/redisom-for-net/"},{"id":"integrate/redisom-for-java","summary":"Learn how to build with Redis Stack and Spring","title":"RedisOM for Java","url":"https://redis.io/docs/latest/integrate/redisom-for-java/"},{"id":"integrate/redisom-for-node-js","summary":"Learn how to build with Redis Stack and Node.js","title":"RedisOM for Node.js","url":"https://redis.io/docs/latest/integrate/redisom-for-node-js/"},{"id":"integrate/redisom-for-python","summary":"Learn how to build with Redis Stack and Python","title":"RedisOM for Python","url":"https://redis.io/docs/latest/integrate/redisom-for-python/"},{"id":"integrate/rust-redis","summary":"Learn how to build with Redis and Rust","title":"Rust client for Redis","url":"https://redis.io/docs/latest/integrate/rust-redis/"},{"id":"integrate/aws-redis-cloud","summary":"Deploy Redis Cloud on Amazon Web Services","title":"Redis Cloud on AWS","url":"https://redis.io/docs/latest/integrate/aws-redis-cloud/"},{"id":"integrate/azure-redis-cloud","summary":"Deploy Redis Cloud on Microsoft Azure","title":"Redis Cloud on Azure","url":"https://redis.io/docs/latest/integrate/azure-redis-cloud/"},{"id":"integrate/gcp-redis-cloud","summary":"Deploy Redis Cloud on Google Cloud Platform","title":"Redis Cloud on Google Cloud","url":"https://redis.io/docs/latest/integrate/gcp-redis-cloud/"},{"id":"integrate/vercel-redis","summary":"Integrate Redis with Vercel for fast, scalable web applications","title":"Redis with Vercel","url":"https://redis.io/docs/latest/integrate/vercel-redis/"},{"id":"integrate/heroku-redis","summary":"Deploy Redis Cloud as a Heroku add-on","title":"Redis Cloud on Heroku","url":"https://redis.io/docs/latest/integrate/heroku-redis/"},{"id":"integrate/kubernetes-redis","summary":"Deploy and manage Redis on Kubernetes","title":"Redis on Kubernetes","url":"https://redis.io/docs/latest/integrate/kubernetes-redis/"},{"id":"integrate/docker-redis-enterprise","summary":"Run Redis Software in Docker containers","title":"Redis Software with Docker","url":"https://redis.io/docs/latest/integrate/docker-redis-enterprise/"},{"id":"integrate/google-adk","summary":"Build AI agents with persistent memory, semantic search, and caching using Redis and Google ADK.","title":"Redis with Google Agent Development Kit (ADK)","url":"https://redis.io/docs/latest/integrate/google-adk/"},{"id":"integrate/langchain-redis","summary":"Build AI applications with Redis and LangChain","title":"Redis with LangChain","url":"https://redis.io/docs/latest/integrate/langchain-redis/"}],"page_type":"index","sections":[],"examples":[]}
{"schema_version":2,"id":"integrate/jedis","title":"Java client for Redis","url":"https://redis.io/docs/latest/integrate/jedis/","summary":"Learn how to build with Redis and Java","tags":["docs","integrate","oss","rs","rc"],"last_updated":"2026-09-17T14:54:53+01:00","children":[],"page_type":"content","content_hash":"c0feb903af418c3ea994e8ff4e34c7ec68ca227ae2caad0bcf9a2d5502dae1e3","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"Connect your Java application to a Redis database using the Jedis client library."},{"id":"overview","title":"Overview","role":"overview","text":"Jedis is a popular Java client for Redis that provides a simple, synchronous interface for Redis operations. It offers comprehensive Redis command support and is widely used in Java applications for its straightforward API and reliable performance."},{"id":"key-features","title":"Key Features","role":"content","text":"- **Synchronous Operations**: Simple, blocking API that's easy to understand and use\n- **Complete Redis Support**: Full coverage of all Redis commands and data types\n- **Connection Pooling**: Built-in connection pool management for multi-threaded applications\n- **Redis Cluster**: Native support for Redis Cluster deployments\n- **Sentinel Support**: High availability with Redis Sentinel integration\n- **Pipeline Support**: Command batching for improved performance\n- **Pub/Sub**: Real-time messaging with Redis publish/subscribe\n- **Thread Safety**: Safe connection pooling for concurrent Java applications"},{"id":"getting-started","title":"Getting Started","role":"setup","text":"Refer to the complete [Jedis guide](https://redis.io/docs/latest/develop/clients/jedis) to install, connect, and use Jedis."}],"examples":[]}
{"schema_version":2,"id":"integrate/kubernetes-redis","title":"Redis on Kubernetes","url":"https://redis.io/docs/latest/integrate/kubernetes-redis/","summary":"Deploy and manage Redis on Kubernetes","tags":["docs","integrate","oss","rs","rc"],"last_updated":"2026-09-17T14:54:42+01:00","children":[],"page_type":"content","content_hash":"da8e327cbb75548328aeb263611598098cdb3122666324be971e431e895f48e1","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"Deploy and manage Redis on Kubernetes using Redis Enterprise Operator and Helm charts for scalable containerized deployments."},{"id":"overview","title":"Overview","role":"overview","text":"Redis on Kubernetes provides cloud-native Redis deployments using the Redis Enterprise Operator and Helm charts. Perfect for containerized applications requiring high availability, automatic scaling, and seamless integration with Kubernetes ecosystems."},{"id":"key-features","title":"Key Features","role":"content","text":"- **Redis Enterprise Operator**: Kubernetes-native operator for automated Redis management\n- **Helm Charts**: Easy deployment and configuration management\n- **Auto-scaling**: Automatic scaling based on resource usage and demand\n- **High Availability**: Multi-zone deployments with automatic failover\n- **Persistent Storage**: Integration with Kubernetes persistent volumes\n- **Service Discovery**: Native Kubernetes service discovery and load balancing\n- **Resource Management**: CPU and memory limits with Kubernetes resource controls\n- **Monitoring Integration**: Prometheus and Grafana integration for observability"},{"id":"getting-started","title":"Getting Started","role":"setup","text":"Learn how to [deploy Redis on Kubernetes](https://redis.io/docs/latest/operate/kubernetes) for cloud-native applications."}],"examples":[]}
{"schema_version":2,"id":"integrate/langchain-redis","title":"Redis with LangChain","url":"https://redis.io/docs/latest/integrate/langchain-redis/","summary":"Build AI applications with Redis and LangChain","tags":["docs","integrate","oss","rs","rc"],"last_updated":"2025-09-23T14:03:20-07:00","children":[],"page_type":"content","content_hash":"87ca678c10cad033f3fe71059327a031afebf8670f7c024f690a2e3dcd359539","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"Integrate Redis with LangChain to build powerful AI applications with vector search, semantic caching, and conversation memory."},{"id":"overview","title":"Overview","role":"overview","text":"Redis integration with LangChain enables you to build sophisticated AI applications with persistent memory, vector search capabilities, and semantic caching. Perfect for chatbots, recommendation systems, and AI-powered applications requiring fast data retrieval."},{"id":"key-features","title":"Key Features","role":"content","text":"- **Vector Search**: High-performance similarity search for embeddings and AI models\n- **Conversation Memory**: Persistent chat history and context for AI conversations\n- **Semantic Caching**: Cache AI model responses for faster inference and cost reduction\n- **Document Storage**: Store and retrieve documents for retrieval-augmented generation (RAG)\n- **Real-time Updates**: Live data updates for dynamic AI applications\n- **Scalable Architecture**: Handle large-scale AI workloads with Redis performance\n- **Multi-modal Support**: Support for text, image, and other data types\n- **LangChain Integration**: Native integration with LangChain's ecosystem and tools"},{"id":"getting-started","title":"Getting Started","role":"setup","text":"Learn how to [build AI chatbots with Redis and LangChain](https://redis.io/learn/howtos/solutions/vector/gen-ai-chatbot) for enhanced AI experiences."}],"examples":[]}
{"schema_version":2,"id":"integrate/lettuce","title":"Java client for Redis","url":"https://redis.io/docs/latest/integrate/lettuce/","summary":"Learn how to build with Redis and Java","tags":["docs","integrate","oss","rs","rc"],"last_updated":"2026-09-17T14:54:53+01:00","children":[],"page_type":"content","content_hash":"8ff691e25531b282652189346c929c6fcbc87bf88f60cad2261dae6fe081aa6b","sections":[{"id":"content","title":"Content","role":"content","text":"Connect your Java application to a Redis database using the Lettuce client library. \n\nRefer to the complete [Lettuce guide](https://redis.io/docs/latest/develop/clients/lettuce) to install, connect, and use Lettuce."}],"examples":[]}
{"schema_version":2,"id":"integrate/n8n-vector-store","title":"n8n Redis vector store","url":"https://redis.io/docs/latest/integrate/n8n-vector-store/","summary":"Learn how to use Redis as a vector store with n8n workflows","tags":["docs","integrate","oss","rs","rc"],"last_updated":"2026-09-17T14:55:03+01:00","children":[],"page_type":"content","content_hash":"14bfeda4c8ad1aa1c69921313d21a1ea0724f5248aa8231edb58c9d3644c06d4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[n8n](https://n8n.io/) is a platform that lets you automate data manipulation tasks.\nYou specify the tasks using a visual graph notation known as a *workflow*. Each node in the\ngraph represents a task, and the edges represent the data flow between tasks.\n\nThe\n[Redis vector store](https://docs.n8n.io/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.vectorstoreredis/)\nnode lets you access [vector search](https://redis.io/docs/latest/develop/ai/search-and-query/vectors) capabilities from\nyour n8n workflows. Some typical usage patterns include:\n\n-   **Use as a regular node to insert and retrieve documents**: Use the Redis Vector Store as a\n    regular node to insert or get documents in the regular connection flow without using an agent.\n-   **Connect directly to an AI agent as a tool**: Connect the Redis Vector Store node directly to\n    the tool connector of an AI agent to use a vector store as a resource when answering queries.\n-   **Use a retriever to fetch documents**: Use the\n    [Vector Store Retriever](https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.retrievervectorstore/)\n    node with the Redis Vector Store node to fetch documents that match the given chat input, often used with \n    the [Question and Answer Chain](https://docs.n8n.io/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.chainretrievalqa/)\n    node.\n-   **Use the Vector Store Question Answer Tool to answer questions**: Use the Vector Store\n    [Question Answer Tool](https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.toolvectorstore/)\n    to summarize results and answer questions from the Redis Vector Store node, rather than connecting it \n    directly as a tool.\n\nThe vector store node is a [cluster node](https://docs.n8n.io/integrations/builtin/cluster-nodes/)\nthat is designed to work with one or more *sub-nodes*. These let you choose additional\nfunctionality, such as an embedding model to calculate the embedding vectors used in the search.\n\nThe sections below summarize how you can use the Redis vector store node in n8n,\nbut see the [Redis vector store node documentation](https://docs.n8n.io/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.vectorstoreredis/) (available from the n8n workflow editor)\nfor more information."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"To use the Redis vector store node, you need a Redis server with Redis Search enabled.\nSee the [Redis Cloud quickstart guide](https://redis.io/docs/latest/operate/rc/rc-quickstart)\nor the [Redis Open Source installation guide](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack)\nto learn how to set up a suitable Redis server within minutes."},{"id":"available-operations","title":"Available operations","role":"content","text":"The Redis Vector Store node has four operation modes that determine what operations you can perform:\n\n-   **Get Many**: Retrieve multiple documents from your vector database by providing a prompt. The prompt is \n    embedded and used for similarity search, returning the most similar documents with their similarity scores. \n    Useful for retrieving a list of similar documents to pass to an agent as additional context.\n-   **Insert Documents**: Insert new documents into your vector database.\n-   **Retrieve Documents (as Vector Store for Chain/Tool)**: Use with a\n    [Vector Store Retriever](https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.retrievervectorstore/)\n    to retrieve documents from a vector database and provide them to the retriever connected to a chain.\n    You must connect the node to a retriever node or root node in this mode.\n-   **Retrieve Documents (as Tool for AI Agent)**: Use the vector store as a tool resource when answering \n    queries. The agent uses the vector store when the vector store name and description match the question \n    details."},{"id":"example-workflows","title":"Example workflows","role":"example","text":"Redis provides two well-annotated example workflows to demonstrate how you can use the Redis vector\nstore node in n8n.\n\n-   [**Chat with GitHub issues using OpenAI and Redis Vector Search**](https://n8n.io/workflows/10837-chat-with-github-issues-using-openai-and-redis-vector-search/):\n    This workflow uses the Redis vector store node to implement a basic RAG pattern. The chat answers\n    questions about GitHub issues from the list of issues in a repository.\n-   [**Semantic caching**](https://n8n.io/workflows/10887-reduce-llm-costs-with-semantic-caching-using-redis-vector-store-and-huggingface/):\n    This workflow implements a [semantic cache](https://redis.io/blog/what-is-semantic-caching/)\n    that checks for previously answered questions before calling an LLM. This pattern can help\n    reduce LLM costs by answering questions from cached results."}],"examples":[]}
{"schema_version":2,"id":"integrate/nagios-with-redis-enterprise","title":"Nagios with Redis Software","url":"https://redis.io/docs/latest/integrate/nagios-with-redis-enterprise/","summary":"The Redis Software (RS) Nagios plugin enables you to monitor the status of RS related objects and alerts. The RS alerts can be related to the cluster, nodes, or databases.","tags":["docs","integrate","rs"],"last_updated":"2026-09-17T14:54:30+01:00","children":[],"page_type":"content","content_hash":"d7591a4a3b947548a56272fe2d7381be10b44fd9a9ebadd17ae12f56fcead5f9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The Nagios plugin has been retired as of Redis Software version 7.2.4.\n\n\nThe Redis Software (RS) Nagios plugin enables you to monitor the status of RS related\nobjects and alerts. The RS alerts can be related to the cluster, nodes,\nor databases.\n\nThe alerts that can be monitored via Nagios are the same alerts that can\nbe configured in the RS UI in the Settings ­\\> Alerts page, or the\nspecific Database ­\\> Configuration page.\n\nAll alert configurations (active / not active, setting thresholds, etc')\ncan only be done through the RS UI, they cannot be configured in Nagios.\nThrough Nagios you can only view the status and information of the\nalerts.\n\nThe full list of alerts can be found in the plugin package itself (in\n\"/rlec_obj/rlec_services.cfg\" file, more details below).\n\nRS Nagios plugin support API password retrieval from Gnome keyring,\nKWallet, Windows credential vault, Mac OS X Keychain, if present, or\notherwise Linux Secret Service compatible password store. With no\nkeyring service available, the password is saved with base64 encoding,\nunder the user home directory."},{"id":"configuring-the-nagios-plugin","title":"Configuring the Nagios plugin","role":"configuration","text":"In order to configure the Nagios plugin you need to copy the files that\ncome with the package into your Nagios environment and place them in a\nNagios configuration directory. Or, alternatively you can copy parts of\nthe package configuration into your existing Nagios configuration.\n\nIf Keyring capabilities are needed to store the password, python keyring\npackage should be installed and used by following the below steps from\nthe operating system CLI on Nagios machine:\n\n1. pip install keyring ­to install the package (See\n    https://pip.pypa.io/en/stable/installing/ on how to install python\n    pip if needed).\n1. keyring set RS-Nagios ­\\<RS user email\\> to set the password.\n    User email should be identical to the email used in Nagios\n    configuration and the password should be set using the same user\n    that run the Nagios server.\n\nThen, you need to update the local parameters, such as hostnames,\naddresses, and object IDs, to the values relevant for your\nRS deployment.\n\nFinally, you need to set the configuration for each node and database\nyou would like to monitor. More details below.\n\nThe RS Nagios package includes two components:\n\n- The plugin itself ­- with suffix \"rlec_nagios_plugin\"\n- Configuration files - with suffix \"rlec_nagios_conf\"\n\nBelow is the list of files included in these packages and instructions\nregarding what updates need to be made to these flies.\n\nNote : The instructions below assume you are running on Ubuntu, have a\nclean Nagios installation, and the base Nagios directory is\n\"/usr/local/nagios/\""},{"id":"step-1","title":"Step 1","role":"content","text":"Copy the folder named \"libexec\" from the plugin folder and its contents\nto \"/usr/local/nagios/\"\n\nThese files included in it are:\n\n- check_rlec_alert\n- check_rlec_node\n- check_rlec_bdb\n- email_stub\n- rlecdigest.py\n\nNote : The check_rlec_alert, check_rlec_node, check_rlec_bdb files\nare the actual plugin implementation. You can run each of them with a\n\"­h\" switch in order to retrieve their documentation and their expected\nparameters."},{"id":"step-2","title":"Step 2","role":"content","text":"Add the following lines to your \"nagios.cfg\":\n\n- cfg_dir=/usr/local/nagios/etc/rlec_obj\n- cfg_dir=/usr/local/nagios/etc/rlec_local\n- resource_file=/usr/local/nagios/etc/rlec_resource.cfg"},{"id":"step-3","title":"Step 3","role":"content","text":"Copy the configuration files along with their folders to\n\"/usr/local/nagios/etc\" and make the required updates, as detailed\nbelow.\n\n1. Under the \"/etc\" folder:\n    1. \"rlec_resource.cfg \" ­ holds global variables definitions for\n        the user and password to use to connect to RS. You should update\n        the variables to the relevant user and password for your\n        deployment.\n    1. \"rlec_local \" folder\n    1. \"rlec_obj\" folder\n1. Under the \"/rlec_local\" folder:\n    1. \"cluster.cfg \" ­ holds configuration details at the cluster\n        level. If you would like to monitor more than one cluster then\n        you need to duplicate the two existing entries in the file for\n        each cluster.\n        1. The first \"define host\" section defines a variable for the\n            IP address of the cluster that is used in other\n            configuration files.\n            1. Update the \"address\" to the Cluster Name (FQDN) as\n                defined in DNS, or the IP address of one of the nodes in\n                the cluster.\n            1. If you are configuring more than one RS then when\n                duplicating this section you should make sure:\n                1. The \"name\" is unique.\n        1. In the second \"define host\" section:\n            1. The \"host_name \" in each entry must be unique.\n            1. The \"display_name\" in each entry can be updated to a\n                user-friendly name that are shown in Nagios UI.\n    1. \"contacts.cfg \" ­ holds configuration details who to send emails\n        to. It should be updated to values relevant for your deployment.\n        If this file already exists in your existing Nagios environment\n        then you should update it accordingly.\n    1. \"databases.cfg\" ­ holds configuration details of the databases\n        to monitor. The \"define host\" section should be duplicated for\n        every database to monitor.\n        1. \"host_name\" should be a unique value.\n        1. \"display_name \" should be updated to a user-friendly name\n            to show in the UI.\n        1. \"_RLECID \" should be the database's internal ID that can\n            be retrieved from\n            [`rladmin status`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status) command output.\n    1. \"nodes.cfg \" ­ holds configuration details of the nodes in the\n        cluster. The \"define host\" section should be duplicated for\n        every node in the cluster.\n        1. \"host_name\" should be a unique value.\n        1. \"display_name \" should be updated to a user-friendly name\n            to show in the UI.\n        1. \"address\" should be updated to the DNS name mapped to the\n            IP address of the node, or to the IP address itself.\n        1. \"_RLECID \" should be the node's internal ID that can be\n            retrieved\n            from [`rladmin status`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status) command output.\n    1. Under the \"/rlec_obj\" folder:\n        1. \"rlec_cmd.cfg\" ­ holds configuration details of how to\n            activate the plugin. No need to make any updates to it.\n        1. \"rlec_groups.cfg\" holds definitions of host groups. No need\n            to make any updates to it.\n        1. \"rlec_services.cfg\" holds definitions of all alerts that\n            are monitored. No need to make any updates to it.\n        1. \"rlec_templates.cfg\" holds general RS Nagios definitions.\n            No need to make any updates to it."}],"examples":[]}
{"schema_version":2,"id":"integrate/new-relic-with-redis-cloud","title":"New Relic with Redis Cloud","url":"https://redis.io/docs/latest/integrate/new-relic-with-redis-cloud/","summary":"To collect, view, and monitor metrics data from your databases and other cluster components, you can connect New Relic to your Redis Cloud cluster using the Redis New Relic Integration.","tags":["docs","integrate","rs"],"last_updated":"2026-10-02T14:54:03+01:00","children":[],"page_type":"content","content_hash":"c909574654daa494222f8bbcaa2858e0728b17ce405612e303572e289720cb4b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[New Relic](https://newrelic.com/?customer-bypass=true) is used by organizations of all sizes and across a wide range of industries to \nenable digital transformation and cloud migration, drive collaboration among development, operations, security and \nbusiness teams, accelerate time to market for applications, reduce time to problem resolution, secure applications and \ninfrastructure, understand user behavior, and track key business metrics.\n\nThe New Relic Integration for Redis Cloud uses Prometheus remote write functionality to connect Prometheus data \nsources to New Relic. This integration enables Redis Cloud users to export metrics to New Relic for analysis, \nand includes Redis-designed dashboards for use in monitoring Redis Cloud clusters.\n\nThis integration makes it possible to:\n- Collect and display metrics not available in the admin console\n- Set up automatic alerts for node or cluster events\n- Display these metrics alongside data from other systems\n\n![](https://redis.io/docs/latest/images/rc/redis-cloud-newrelic.png)"},{"id":"install-redis-new-relic-integration-for-redis-cloud","title":"Install Redis' New Relic Integration for Redis Cloud","role":"setup","text":"The New Relic Integration for Redis is based on a feature of the Prometheus data source. Prometheus can forward metrics on to \nanother destination using remote writes. This will require a Prometheus installation inside the same datacenter as the \nRedis Cloud deployment.\n\nIf you have not already created a VPC between the Redis Cloud cluster and the network in which the machine hosting \nPrometheus lives you should do so now. Please visit [VPC Peering](https://redis.io/docs/latest/operate/rc/security/vpc-peering/) \nand follow the instructions for the cloud platform of your choice.\n\nFinally, the Prometheus installation must be configured to pull metrics from Redis Cloud and write them to New Relic. There \nare two sections, first the pull from Redis and second the write to New Relic.\n\nGet metrics from Redis Cloud:\n\n[code example]\n\nReplace `REDIS_CLOUD_HOST` with the [Prometheus endpoint for your database](https://redis.io/docs/latest/operate/rc/databases/monitor-performance#connect-to-prometheus), which you can get from the [Redis Cloud console](https://cloud.redis.io/). \n\nIn the **Metrics** tab of your database, select **Connect to Prometheus > Copy Prometheus endpoint** to save your Prometheus endpoint to the clipboard.\n\n![Use the Connect to Prometheus button to get the Prometheus endpoint.](https://redis.io/docs/latest/images/rc/database-metrics-connect-prometheus.png)\n\nYou can also get the Prometheus endpoint by calling [`GET /subscriptions/{subscriptionId}`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Subscriptions-Pro/operation/getSubscriptionById) and getting the `prometheusEndpoint` from the response.\n\n\nWrite them to New Relic:\n\n[code example]"},{"id":"view-metrics","title":"View metrics","role":"content","text":"The Redis Cloud Integration for New Relic contains pre-defined dashboards to aid in monitoring your Redis Software deployment.\n\nThe following dashboards are currently available:\n\n- Cluster: top-level statistics indicating the general health of the cluster\n- Database: performance metrics at the database level\n- Node\n- Shard: low-level details of an individual shard\n- Active-Active: replication and performance for geo-replicated clusters\n- Proxy: network and command information regarding the proxy\n- Proxy Threads: processor usage information regarding the proxy's component threads"},{"id":"monitor-metrics","title":"Monitor metrics","role":"content","text":"New Relic dashboards can be filtered using the text area. For example, when viewing a cluster dashboard it is possible to \nfilter the display to show data for only one cluster by typing 'cluster' in the text area and waiting for the system to\nretrieve the relevant data before choosing one of the options in the 'cluster' section.\n\nCertain types of data do not know the name of the database from which they were drawn. The dashboard should have a list \nof database names and ids; use the id value when filtering input to the dashboard."}],"examples":[{"id":"install-redis-new-relic-integration-for-redis-cloud-ex0","language":"yaml","code":"- job_name: \"redis-cloud\"\n    scrape_interval: 30s\n    scrape_timeout: 30s\n    metrics_path: /\n    scheme: https\n    tls_config:\n      insecure_skip_verify: true\n    static_configs:\n      # The default Redis Cloud Prometheus port is 8070. \n      # Replace REDIS_CLOUD_HOST with your cluster's hostname.\n      - targets: [\"REDIS_CLOUD_HOST:8070\"]","section_id":"install-redis-new-relic-integration-for-redis-cloud"},{"id":"install-redis-new-relic-integration-for-redis-cloud-ex1","language":"yaml","code":"# Remote write configuration for New Relic.\n# - Replace REDIS_CLOUD_SERVICE NAME with any name you'd like to use to refer to this data source.\n# - Replace NEW_RELIC_BEARER_TOKEN with the token you generated on the New Relic Administration -> API Keys page.\nremote_write:\n- url: https://metric-api.newrelic.com/prometheus/v1/write?prometheus_server=REDIS_CLOUD_SERVICE_NAME\n  authorization:\n    credentials: NEW_RELIC_BEARER_TOKEN","section_id":"install-redis-new-relic-integration-for-redis-cloud"}]}
{"schema_version":2,"id":"integrate/new-relic-with-redis-enterprise","title":"New Relic with Redis Software","url":"https://redis.io/docs/latest/integrate/new-relic-with-redis-enterprise/","summary":"To collect, view, and monitor metrics data from your databases and other cluster components, you can connect New Relic to your Redis Software cluster using the Redis New Relic Integration.","tags":["docs","integrate","rs"],"last_updated":"2026-10-02T14:54:03+01:00","children":[],"page_type":"content","content_hash":"cc7c42e0ee08b9ad4613960505cd0faee4faad41d15f0006346edc766ffe9c30","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[New Relic](https://newrelic.com/?customer-bypass=true) is used by organizations of all sizes and across a wide range of industries to \nenable digital transformation and cloud migration, drive collaboration among development, operations, security and \nbusiness teams, accelerate time to market for applications, reduce time to problem resolution, secure applications and \ninfrastructure, understand user behavior, and track key business metrics.\n\nThe New Relic Integration for Redis Software uses Prometheus remote write functionality to connect Prometheus data \nsources to New Relic. This integration enables Redis Software users to export metrics to New Relic for analysis, \nand includes Redis-designed dashboards for use in monitoring Redis Software clusters.\n\nThis integration makes it possible to:\n- Collect and display metrics not available in the admin console\n- Set up automatic alerts for node or cluster events\n- Display these metrics alongside data from other systems\n\n![](https://redis.io/docs/latest/images/rs/redis-enterprise-newrelic.png)"},{"id":"install-redis-new-relic-integration-for-redis-software","title":"Install Redis' New Relic Integration for Redis Software","role":"setup","text":"The New Relic Integration for Redis is based on a feature of the Prometheus data source. Prometheus can forward metrics on to \nanother destination using remote writes. The Prometheus installation must be configured to pull metrics from Redis \nEnterprise and write them to New Relic. There are two sections, first the pull from Redis and second the write to New Relic.\n\nGet metrics from Redis Software:\n\n[code example]\n\nWrite them to New Relic:\n\n[code example]"},{"id":"view-metrics","title":"View metrics","role":"content","text":"The Redis Software Integration for New Relic contains pre-defined dashboards to aid in monitoring your Redis Software deployment.\n\nThe following dashboards are currently available:\n\n- Cluster: top-level statistics indicating the general health of the cluster\n- Database: performance metrics at the database level\n- Node\n- Shard: low-level details of an individual shard\n- Active-Active: replication and performance for geo-replicated clusters\n- Proxy: network and command information regarding the proxy\n- Proxy Threads: processor usage information regarding the proxy's component threads"},{"id":"monitor-metrics","title":"Monitor metrics","role":"content","text":"New Relic dashboards can be filtered using the text area. For example, when viewing a cluster dashboard it is possible to\nfilter the display to show data for only one cluster by typing 'cluster' in the text area and waiting for the system to\nretrieve the relevant data before choosing one of the options in the 'cluster' section.\n\nCertain types of data do not know the name of the database from which they were drawn. The dashboard should have a list \nof database names and ids; use the id value when filtering input to the dashboard."}],"examples":[{"id":"install-redis-new-relic-integration-for-redis-software-ex0","language":"yaml","code":"- job_name: \"redis-enterprise\"\n    scrape_interval: 30s\n    scrape_timeout: 30s\n    metrics_path: /\n    scheme: https\n    tls_config:\n      insecure_skip_verify: true\n    static_configs:\n      # The default Redis Software Prometheus port is 8070. \n      # Replace REDIS_ENTERPRISE_HOST with your cluster's hostname.\n      - targets: [\"REDIS_ENTERPRISE_HOST:8070\"]","section_id":"install-redis-new-relic-integration-for-redis-software"},{"id":"install-redis-new-relic-integration-for-redis-software-ex1","language":"yaml","code":"# Remote write configuration for New Relic.\n# - Replace REDIS_ENTERPRISE_SERVICE NAME with any name you'd like to use to refer to this data source.\n# - Replace NEW_RELIC_BEARER_TOKEN with the token you generated on the New Relic Administration -> API Keys page.\nremote_write:\n- url: https://metric-api.newrelic.com/prometheus/v1/write?prometheus_server=REDIS_ENTERPRISE_SERVICE_NAME\n  authorization:\n    credentials: NEW_RELIC_BEARER_TOKEN","section_id":"install-redis-new-relic-integration-for-redis-software"}]}
{"schema_version":2,"id":"integrate/node-redis","title":"Node.js client for Redis","url":"https://redis.io/docs/latest/integrate/node-redis/","summary":"Learn how to build with Redis and Node.js","tags":["docs","integrate","oss","rs","rc"],"last_updated":"2026-09-17T14:54:53+01:00","children":[],"page_type":"content","content_hash":"047fc5b72baf318fa608c24866151363dfb9edc1f39a1c009362c3c94c6ad81e","sections":[{"id":"content","title":"Content","role":"content","text":"Connect your Node.js application to a Redis database using the node-redis client library. \n\nRefer to the complete [Node.js guide](https://redis.io/docs/latest/develop/clients/nodejs) to install, connect, and use node-redis."}],"examples":[]}
{"schema_version":2,"id":"integrate/NRedisStack","title":"C#/.NET client for Redis","url":"https://redis.io/docs/latest/integrate/nredisstack/","summary":"Learn how to build with Redis and C#/.NET","tags":["docs","integrate","oss","rs","rc"],"last_updated":"2026-09-17T14:54:53+01:00","children":[],"page_type":"content","content_hash":"ea7f827afe19513d52bca894193e4089bbb115a16fb029981b2ede2af77382ec","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"Connect your C#/.NET application to a Redis database using the StackExchange.Redis client library."},{"id":"overview","title":"Overview","role":"overview","text":"StackExchange.Redis is a .NET client library for Redis. It provides strongly-typed APIs for Redis \ndata structures and advanced features, making it ideal for modern .NET applications."},{"id":"key-features","title":"Key Features","role":"content","text":"- **Strongly Typed**: Type-safe APIs with full IntelliSense support in Visual Studio\n- **Async/Await**: Native asynchronous programming support for high-performance applications\n- **Connection Multiplexing**: Efficient connection sharing across multiple operations\n- **Cluster Support**: Full Redis Cluster support for distributed deployments\n- **Pipeline Support**: Command batching for improved throughput\n- **.NET Ecosystem**: Seamless integration with ASP.NET Core, Entity Framework, and other .NET technologies"},{"id":"getting-started","title":"Getting Started","role":"setup","text":"Refer to the complete [C#/.NET guide](https://redis.io/docs/latest/develop/clients/dotnet) to install, connect, and use StackExchange.Redis."}],"examples":[]}
{"schema_version":2,"id":"integrate/php-redis","title":"PHP client for Redis","url":"https://redis.io/docs/latest/integrate/php-redis/","summary":"Learn how to build with Redis and PHP","tags":["docs","integrate","oss","rs","rc"],"last_updated":"2026-09-17T14:54:53+01:00","children":[],"page_type":"content","content_hash":"69d26f161dc2c1791c82969681bb1052bb2df9419653d3972359b50c64564900","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"Predis is the recommended PHP client for Redis."},{"id":"overview","title":"Overview","role":"overview","text":"Predis is a flexible and feature-complete Redis client for PHP that supports Redis 2.0+ and provides a clean, object-oriented interface."},{"id":"key-features","title":"Key Features","role":"content","text":"- **Pure PHP implementation**: easy Composer installation, framework integration\n- **Full Redis Support**: Complete command coverage for all Redis features\n- **Framework Integration**: Seamless integration with Laravel, Symfony, and other PHP frameworks\n- **Connection Management**: Automatic connection pooling and reconnection handling\n- **Flexible Configuration**: Customizable connection parameters and command options\n- **Error Handling**: Comprehensive exception handling for robust applications\n- **Async Support**: Non-blocking operations for high-throughput applications"},{"id":"getting-started","title":"Getting Started","role":"setup","text":"Refer to the complete [Predis guide](https://redis.io/docs/latest/develop/clients/php) to install, connect, and use PHP Redis clients with detailed examples and framework integration guides."}],"examples":[]}
{"schema_version":2,"id":"integrate/prometheus-with-redis-cloud","title":"Prometheus and Grafana with Redis Cloud","url":"https://redis.io/docs/latest/integrate/prometheus-with-redis-cloud/","summary":"Use Prometheus and Grafana to collect and visualize Redis Cloud metrics.","aliases":["/operate/rc/cloud-integrations/prometheus-integration"],"tags":["docs","integrate","rc"],"last_updated":"2026-10-02T14:54:03+01:00","children":[],"page_type":"content","content_hash":"2293b6a0fd287c8ac3aaf8ec7d5d26b74bbe20f17ff70fdfafaa60b46cf81e07","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can use Prometheus and Grafana to collect and visualize your Redis Cloud metrics.\n\n- [Prometheus](https://prometheus.io/) is an open source systems monitoring and alerting toolkit that can scrape metrics from different sources.\n- [Grafana](https://grafana.com/) is an open source metrics visualization tool that can process Prometheus data.\n\nRedis Cloud exposes its metrics through a Prometheus endpoint. You can configure your Prometheus server to scrape metrics from your Redis Cloud subscription on port 8070.\n\nThe Redis Cloud Prometheus endpoint is exposed on Redis Cloud's internal network. To access this network, enable [VPC peering](https://redis.io/docs/latest/operate/rc/security/vpc-peering), [Private Service Connect](https://redis.io/docs/latest/operate/rc/security/private-service-connect), [AWS Transit Gateway](https://redis.io/docs/latest/operate/rc/security/aws-transit-gateway), or [AWS PrivateLink](https://redis.io/docs/latest/operate/rc/security/aws-privatelink). Private connectivity options are only available with Redis Cloud Pro. You cannot use Prometheus and Grafana with Redis Cloud Essentials.\n\n  > [!NOTE]\n  >   \n  > The Prometheus endpoint's metrics cover all databases within a subscription. Any database added to or removed from the subscription is reflected automatically. \n  >\n  >   \n\nFor more information on how Prometheus communicates with Redis Software clusters, see [Prometheus integration with Redis Software](https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise)."},{"id":"quick-start","title":"Quick start","role":"content","text":"You can quickly set up Prometheus and Grafana for testing using the Prometheus and Grafana Docker images."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"1. Create a [Redis Cloud Pro database](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new). \n\n1. Set up [VPC peering](https://redis.io/docs/latest/operate/rc/security/vpc-peering).\n\n1. Get the Prometheus endpoint for your database from the [Redis Cloud console](https://redis.io/docs/latest/operate/rc/databases/monitor-performance#connect-to-prometheus).\n\n    In the **Metrics** tab of your database, select **Connect to Prometheus > Copy Prometheus endpoint** to save your Prometheus endpoint to the clipboard.\n\n![Use the Connect to Prometheus button to get the Prometheus endpoint.](https://redis.io/docs/latest/images/rc/database-metrics-connect-prometheus.png)\n\nYou can also get the Prometheus endpoint by calling [`GET /subscriptions/{subscriptionId}`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Subscriptions-Pro/operation/getSubscriptionById) and getting the `prometheusEndpoint` from the response.\n\n\n1. Create an instance to run Prometheus and Grafana on the same cloud provider as your Redis Cloud subscription (for example, Amazon Web Services or Google Cloud). This instance must:\n    - Exist in the same region as your Redis Cloud subscription.\n    - Connect to the VPC subnet that is peered with your Redis Cloud subscription.\n    - Allow outbound connections to port 8070, so that Prometheus can scrape the Redis Cloud server for data.\n    - Allow inbound connections to port 9090 for Prometheus and port 3000 for Grafana.\n    - Be located in one of the CIDR ranges of the RFC-1918 internal IP standard, which is comprised of three CIDR ranges:\n\n        - 10.0.0.0/8\n        - 172.16.0.0/12\n        - 192.168.0.0/16\n        \n        The Prometheus endpoint is subject to a whitelist according to this standard."},{"id":"set-up-prometheus","title":"Set up Prometheus","role":"content","text":"To get started with custom monitoring with Prometheus on Docker:\n\n1. Create a directory on the Prometheus instance called `prometheus` and create a `prometheus.yml` file in that directory.\n\n1. Add the following contents to `prometheus.yml`. Replace `<prometheus_endpoint>` with the Prometheus endpoint.\n\n    [code example]\n\n1. Create a `docker-compose.yml` file with instructions to set up the Prometheus and Grafana Docker images.\n\n    [code example]\n\n1. To start the containers, run:\n\n    [code example]\n\n1. To check that all the containers are up, run: `docker ps`\n1. In your browser, sign in to Prometheus at `http://localhost:9090` to make sure the server is running.\n1. Select **Status** and then **Targets** to check that Prometheus is collecting data from the Redis Cloud cluster.\n\n    ![The Redis Software target showing that Prometheus is connected to the Redis Software Cluster.](https://redis.io/docs/latest/images/rs/prometheus-target.png)\n\n    If Prometheus is connected to the cluster, you can type **node_up** in the Expression field on the Prometheus home page to see the cluster metrics.\n\nSee [Prometheus Metrics](https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise/prometheus-metrics-definitions) for a list of metrics that Prometheus collects from Redis Software clusters."},{"id":"set-up-grafana","title":"Set up Grafana","role":"content","text":"Once the Prometheus and Grafana Docker containers are running, and Prometheus is connected to your Redis Cloud subscription, you can set up your Grafana dashboards.\n\n1. Sign in to Grafana. If you installed Grafana with Docker, go to `http://localhost:3000` and sign in with:\n\n    - Username: `admin`\n    - Password: `secret`\n\n1. In the Grafana configuration menu, select **Data Sources**.\n\n1. Select **Add data source**.\n\n1. Select **Prometheus** from the list of data source types.\n\n    ![The Prometheus data source in the list of data sources on Grafana.](https://redis.io/docs/latest/images/rs/prometheus-datasource.png)\n\n1. Enter the Prometheus configuration information:\n\n    - Name: `redis-cloud`\n    - URL: `http://prometheus-server:9090`\n    - Access: `Server`\n\n    ![The Prometheus connection form in Grafana.](https://redis.io/docs/latest/images/rc/prometheus-connection.png)\n\n    > [!NOTE]\n    > - If the network port is not accessible to the Grafana server, select the **Browser** option from the Access menu.\n    > - In a testing environment, you can select **Skip TLS verification**.\n    >\n    >     \n\n1. Add dashboards for your subscription and database metrics.\n    To add preconfigured dashboards:\n    1. In the Grafana dashboards menu, select **Manage**.\n    1. Select **Import**.\n    1. Add the [subscription status](https://grafana.com/grafana/dashboards/18406-subscription-status-dashboard/) and [database status](https://grafana.com/grafana/dashboards/18407-database-status-dashboard/) dashboards."},{"id":"grafana-dashboards-for-redis-cloud","title":"Grafana dashboards for Redis Cloud","role":"content","text":"Redis publishes preconfigured dashboards for Redis Cloud and Grafana:\n\n* The [subscription status dashboard](https://grafana.com/grafana/dashboards/18406-subscription-status-dashboard/) provides an overview of your Redis Cloud subscriptions.\n* The [database status dashboard](https://grafana.com/grafana/dashboards/18407-database-status-dashboard/) displays specific database metrics, including latency, memory usage, ops/second, and key count.\n* The [Active-Active dashboard](https://github.com/redis-field-engineering/redis-enterprise-observability/blob/main/grafana/dashboards/grafana_v9-11/cloud/basic/redis-cloud-active-active-dashboard_v9-11.json) displays metrics specific to [Active-Active databases](https://redis.io/docs/latest/operate/rc/databases/active-active).\n\n> [!NOTE]\n> The database status dashboard requires the Infinity data source plugin. Install it from **Connections > Add new connection**, search for **Infinity**, and add it as a data source before importing the dashboard.\n\nThese dashboards are open source. For additional dashboard options, or to file an issue, see the [Redis Enterprise observability Github repository](https://github.com/redis-field-engineering/redis-enterprise-observability/tree/main/grafana).\n\nFor more information about configuring Grafana dashboards, see the [Grafana documentation](https://grafana.com/docs/)."}],"examples":[{"id":"set-up-prometheus-ex0","language":"yml ","code":"global:\n        scrape_interval: 15s\n        evaluation_interval: 15s\n\n        # Attach these labels to any time series or alerts when communicating with\n        # external systems (federation, remote storage, Alertmanager).\n        external_labels:\n            monitor: \"prometheus-stack-monitor\"\n\n    # Load and evaluate rules in this file every 'evaluation_interval' seconds.\n    #rule_files:\n    # - \"first.rules\"\n    # - \"second.rules\"\n\n    scrape_configs:\n        # scrape Prometheus itself\n      - job_name: prometheus\n        scrape_interval: 10s\n        scrape_timeout: 5s\n        static_configs:\n          - targets: [\"localhost:9090\"]\n\n        # scrape Redis Cloud\n      - job_name: redis-cloud\n        scrape_interval: 30s\n        scrape_timeout: 30s\n        metrics_path: /  # For v2, use /v2\n        scheme: https\n        static_configs:\n          - targets: [\"<prometheus_endpoint>:8070\"]","section_id":"set-up-prometheus"},{"id":"set-up-prometheus-ex1","language":"yml","code":"version: '3'\n    services:\n        prometheus-server:\n            image: prom/prometheus\n            ports:\n                - 9090:9090\n            volumes:\n                - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml\n\n        grafana-ui:\n            image: grafana/grafana\n            ports:\n                - 3000:3000\n            environment:\n                - GF_SECURITY_ADMIN_PASSWORD=secret\n            links:\n                - prometheus-server:prometheus","section_id":"set-up-prometheus"},{"id":"set-up-prometheus-ex2","language":"sh","code":"$ docker compose up -d","section_id":"set-up-prometheus"}]}
{"schema_version":2,"id":"integrate/prometheus-with-redis-enterprise","title":"Prometheus and Grafana with Redis Software","url":"https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise/","summary":"Use Prometheus and Grafana to collect and visualize Redis Software metrics.","tags":["docs","integrate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","children":[{"id":"integrate/prometheus-with-redis-enterprise/observability","summary":"Using monitoring and observability with Redis Software","title":"Redis Software observability and monitoring guidance","url":"https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise/observability/"},{"id":"integrate/prometheus-with-redis-enterprise/prometheus-metrics-v1","summary":"V1 metrics available to Prometheus.","title":"Prometheus metrics v1","url":"https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise/prometheus-metrics-v1/"},{"id":"integrate/prometheus-with-redis-enterprise/prometheus-metrics-v1-to-v2","summary":"Transition from v1 metrics to v2 PromQL equivalents.","title":"Transition from Prometheus v1 to Prometheus v2","url":"https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise/prometheus-metrics-v1-to-v2/"},{"id":"integrate/prometheus-with-redis-enterprise/prometheus-metrics-definitions","summary":"V2 metrics available to Prometheus as of Redis Software version 7.8.2.","title":"Prometheus metrics v2","url":"https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise/prometheus-metrics-definitions/"}],"page_type":"content","content_hash":"05317c5c74e088f31dc3862127969fbc245e7a2555f626bf3d5320def596a590","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can use Prometheus and Grafana to collect and visualize your Redis Software metrics.\n\nMetrics are exposed at the cluster, node, database, shard, and proxy levels.\n\n\n- [Prometheus](https://prometheus.io/) is an open source systems monitoring and alerting toolkit that aggregates metrics from different sources.\n- [Grafana](https://grafana.com/) is an open source metrics visualization tool that processes Prometheus data.\n\nYou can use Prometheus and Grafana to:\n- Collect and display metrics not available in the Cluster Manager UI\n\n- Set up automatic alerts for all resources\n\n- Display Redis Software metrics alongside data from other systems\n\n![Graphic showing how Prometheus and Grafana collect and display data from a Redis Software Cluster. Prometheus collects metrics from the Redis Software cluster, and Grafana queries those metrics for visualization.](https://redis.io/docs/latest/images/rs/grafana-prometheus.png)\n\nTo get started with Prometheus and Grafana, see the following [quick start](#quick-start) or see [Redis Software Observability with Prometheus and Grafana](https://redis.io/learn/operate/observability/redis-software-prometheus-and-grafana) for a more detailed tutorial."},{"id":"quick-start","title":"Quick start","role":"content","text":""},{"id":"hardware-requirements","title":"Hardware requirements","role":"content","text":"The minimum hardware requirements to set up Redis Enterprise Software monitoring in production using Prometheus, Grafana, and the metrics stream engine are:\n\n- **CPU**: 4 vCPUs\n- **Memory**: 8-12 GB RAM\n- **Storage**: 100 GB SSD\n\nYou should also consider the following to determine your deployment's hardware requirements:\n\n- The number of Redis instances being scraped.\n\n- Lower scrape intervals, such as 15 seconds versus 1 minute, increase resource usage. The Redis scraping interval is 30 seconds.\n\n- Longer retention increases storage requirements. Basic retention is 90 days.\n\n- For high availability, run Prometheus in a replicated or federated mode for redundancy, and use Grafana’s load balancing for multiple users.\n\n- Factor in overhead if Prometheus is writing metrics to remote storage such as Thanos or Cortex.\n\n- Ensure high disk IOPS for Prometheus TSDB for better write and read performance.\n\nFor more details about Grafana hardware requirements, see the [offical Grafana documentation](https://grafana.com/docs/enterprise-traces/latest/setup/hardware-requirements/)."},{"id":"setup-steps","title":"Setup steps","role":"setup","text":"To get started with Prometheus and Grafana:\n\n1. Install Docker on your system. For installation instructions, see the [official Docker documentation](https://docs.docker.com/get-started/get-docker/).\n\n1. Create a directory called 'prometheus' on your local machine.\n\n1. Within that directory, create a configuration file called `prometheus.yml`.\n1. Add the following contents to the configuration file and replace `<cluster_name>` with your Redis Software cluster's FQDN:\n\n    **v2 (metrics stream engine):**\n\n[code example]\n\n**v1 (deprecated):**\n\n[code example]\n\n\n\n    > [!NOTE]\n    > **Use a single scrape target.** The v2 endpoint is cluster-wide. Every node aggregates metrics from all nodes and returns the same complete result, so one target is enough. If you list one target per node, Prometheus stores every series once per target and multiplies each `sum()`-based dashboard panel by the number of targets. This produces no error. Prometheus reports every target as up and Grafana renders normally. Use your cluster FQDN as the single target so metrics remain available if a node goes down.\n\n1. Set up your Prometheus and Grafana servers. See the official [Prometheus installation](https://prometheus.io/docs/prometheus/latest/installation/) and [Grafana installation](https://grafana.com/docs/grafana/latest/setup-grafana/installation/) documentation for help.\n\n    > [!NOTE]\n    > We recommend running Prometheus in Docker only for development and testing.\n\n    To set up Prometheus and Grafana on Docker, follow these steps. For additional help, see the official [Prometheus](https://prometheus.io/docs/prometheus/latest/installation/#using-docker) and [Grafana](https://grafana.com/docs/grafana/latest/setup-grafana/installation/docker/) Docker image documentation.\n\n    1. Create a _docker-compose.yml_ file:\n\n        [code example]\n\n    1. To start the containers, run:\n\n        [code example]\n\n    1. To check that all of the containers are up, run: \n    \n        [code example]\n\n    1. In your browser, sign in to Prometheus at `http://localhost:9090` to make sure the server is running.\n\n    1. Select **Status** and then **Targets** to check that Prometheus is collecting data from your Redis Software cluster.\n\n        ![The Redis Software target showing that Prometheus is connected to the Redis Software Cluster.](https://redis.io/docs/latest/images/rs/prometheus-target.png)\n\n        If Prometheus is connected to the cluster, you can type **node_up** in the Expression field on the Prometheus home page to see the cluster metrics.\n\n        To confirm you are not scraping duplicate targets, query `count(up{job=\"redis-enterprise\"})`. A correct configuration returns `1`. To confirm the cluster can reach every node, query `node_metrics_up`, which reports `0` for any node the aggregator failed to scrape.\n\n1. Configure the Grafana datasource:\n\n    1. Sign in to Grafana. If you installed Grafana locally, go to `http://localhost:3000` and sign in with:\n\n        - Username: admin\n        - Password: secret\n\n    1. In the Grafana configuration menu, select **Data Sources**.\n\n    1. Select **Add data source**.\n\n    1. Select **Prometheus** from the list of data source types.\n\n        ![The Prometheus data source in the list of data sources on Grafana.](https://redis.io/docs/latest/images/rs/prometheus-datasource.png)\n\n    1. Enter the Prometheus configuration information:\n\n        - Name: `redis-enterprise`\n        - URL: `http://<your prometheus server name>:9090`\n\n        ![The Prometheus connection form in Grafana.](https://redis.io/docs/latest/images/rs/prometheus-connection.png)\n\n    > [!NOTE]\n    > - If the network port is not accessible to the Grafana server, select the **Browser** option from the Access menu.\n    > - In a testing environment, you can select **Skip TLS verification**.\n\n1. Add dashboards for cluster, database, node, and shard metrics.\n    To add preconfigured dashboards:\n    1. In the Grafana dashboards menu, select **Manage**.\n    1. Click **Import**.\n    1. Upload one or more [Grafana dashboards](#grafana-dashboards-for-redis-software).\n\n1. To set up alerts in Prometheus or Grafana, see the following resources:\n\n    **v2 (metrics stream engine):**\n\nFor v2 metrics alerts:\n\n- [Official Prometheus alerting documentation](https://prometheus.io/docs/alerting/latest/overview/)\n\n- [Configuring Prometheus for v2 metrics alerts](https://github.com/redis-field-engineering/redis-enterprise-observability/tree/main/prometheus_v2#readme)\n    \n- [Example Prometheus alert rules for v2 metrics](https://github.com/redis-field-engineering/redis-enterprise-observability/blob/main/prometheus_v2/rules/alerts.yml)\n\n- [Tutorial on how to set up alerting for Redis](https://redis.io/tutorials/operate/observability/redis-software-prometheus-and-grafana/#how-do-you-set-up-alerting-for-redis)\n\n- [Official Grafana Alerting documentation](https://grafana.com/docs/grafana/latest/alerting/)\n\n**v1 (deprecated):**\n\nFor v1 metrics alerts:\n\n- [Official Prometheus alerting documentation](https://prometheus.io/docs/alerting/latest/overview/)\n\n- [Configuring Prometheus for v1 metrics alerts](https://github.com/redis-field-engineering/redis-enterprise-observability/tree/main/prometheus#readme)\n    \n- [Example Prometheus alert rules for v1 metrics](https://github.com/redis-field-engineering/redis-enterprise-observability/blob/main/prometheus/rules/alerts.yml)\n\n- [Official Grafana Alerting documentation](https://grafana.com/docs/grafana/latest/alerting/)"},{"id":"grafana-dashboards-for-redis-software","title":"Grafana dashboards for Redis Software","role":"content","text":"Redis publishes preconfigured dashboards for Redis Software and Grafana.\n\n> [!NOTE]\n> V1 dashboards are not compatible with the v2 metrics exporter endpoint. Make sure to use the correct dashboard version for your metrics endpoint.\n\nThese dashboards are open source. For additional dashboard options, or to file an issue, see the [Redis Software observability Github repository](https://github.com/redis-field-engineering/redis-enterprise-observability/).\n\nFor more information about configuring Grafana dashboards, see the [Grafana documentation](https://grafana.com/docs/).\n\n**v2 (metrics stream engine):**\n\nUse the following dashboards when connecting to the v2 metrics endpoint (`https://<cluster_name>:8070/v2`):\n\n* The [cluster status dashboard](https://github.com/redis-field-engineering/redis-enterprise-observability/blob/main/grafana_v2/dashboards/grafana_v9-11/software/basic/redis-software-cluster-dashboard_v9-11.json) provides an overview of your Redis Software clusters.\n* The [database status dashboard](https://github.com/redis-field-engineering/redis-enterprise-observability/blob/main/grafana_v2/dashboards/grafana_v9-11/software/basic/redis-software-database-dashboard_v9-11.json) displays specific database metrics, including latency, memory usage, ops/second, and key count.\n* The [node metrics dashboard](https://github.com/redis-field-engineering/redis-enterprise-observability/blob/main/grafana_v2/dashboards/grafana_v9-11/software/basic/redis-software-node-dashboard_v9-11.json) provides metrics for each of the nodes hosting your cluster.\n* The [shard metrics dashboard](https://github.com/redis-field-engineering/redis-enterprise-observability/blob/main/grafana_v2/dashboards/grafana_v9-11/software/basic/redis-software-shard-dashboard_v9-11.json) displays metrics for the individual Redis processes running on your cluster nodes.\n* The [Active-Active dashboard](https://github.com/redis-field-engineering/redis-enterprise-observability/blob/main/grafana_v2/dashboards/grafana_v9-11/software/basic/redis-software-active-active-dashboard_v9-11.json) displays metrics specific to [Active-Active databases](https://redis.io/docs/latest/operate/rs/databases/active-active).\n* The [QPS dashboard - Redis Search metrics](https://github.com/redis-field-engineering/redis-enterprise-observability/blob/main/grafana_v2/dashboards/grafana_v9-11/search/RediSearchQPS.json) displays metrics specific to Redis Search, showcasing QPS, Query Latency, Indexing performance, and more.\n* The [OPS dashboards](https://github.com/redis-field-engineering/redis-enterprise-observability/tree/main/grafana_v2/dashboards/grafana_v9-11/software/ops) are advanced operational dashboards for on-premises deployments.\n\n**v1 (deprecated):**\n\nUse the following dashboards when connecting to the v1 metrics endpoint (`https://<cluster_name>:8070/`):\n\n* The [cluster status dashboard](https://github.com/redis-field-engineering/redis-enterprise-observability/blob/main/grafana/dashboards/grafana_v9-11/software/basic/redis-software-cluster-dashboard_v9-11.json) provides an overview of your Redis Software clusters.\n* The [database status dashboard](https://github.com/redis-field-engineering/redis-enterprise-observability/blob/main/grafana/dashboards/grafana_v9-11/software/basic/redis-software-database-dashboard_v9-11.json) displays specific database metrics, including latency, memory usage, ops/second, and key count.\n* The [node metrics dashboard](https://github.com/redis-field-engineering/redis-enterprise-observability/blob/main/grafana/dashboards/grafana_v9-11/software/basic/redis-software-node-dashboard_v9-11.json) provides metrics for each of the nodes hosting your cluster.\n* The [shard metrics dashboard](https://github.com/redis-field-engineering/redis-enterprise-observability/blob/main/grafana/dashboards/grafana_v9-11/software/basic/redis-software-shard-dashboard_v9-11.json) displays metrics for the individual Redis processes running on your cluster nodes.\n* The [Active-Active dashboard](https://github.com/redis-field-engineering/redis-enterprise-observability/blob/main/grafana/dashboards/grafana_v9-11/software/basic/redis-software-active-active-dashboard_v9-11.json) displays metrics specific to [Active-Active databases](https://redis.io/docs/latest/operate/rs/databases/active-active)."}],"examples":[{"id":"setup-steps-ex0","language":"yml","code":"global:\n  scrape_interval: 15s\n  evaluation_interval: 15s\n\n# Attach these labels to any time series or alerts when communicating with\n# external systems (federation, remote storage, Alertmanager).\n  external_labels:\n    monitor: \"prometheus-stack-monitor\"\n\n# Load and evaluate rules in this file every 'evaluation_interval' seconds.\n#rule_files:\n# - \"first.rules\"\n# - \"second.rules\"\n\nscrape_configs:\n# scrape Prometheus itself\n  - job_name: prometheus\n    scrape_interval: 10s\n    scrape_timeout: 5s\n    static_configs:\n      - targets: [\"localhost:9090\"]\n\n# scrape Redis Software\n  - job_name: redis-enterprise\n    scrape_interval: 30s\n    scrape_timeout: 30s\n    metrics_path: /v2\n    scheme: https\n    tls_config:\n      insecure_skip_verify: true\n    static_configs:\n      - targets: [\"<cluster_name>:8070\"]","section_id":"setup-steps"},{"id":"setup-steps-ex1","language":"yml","code":"global:\n  scrape_interval: 15s\n  evaluation_interval: 15s\n\n# Attach these labels to any time series or alerts when communicating with\n# external systems (federation, remote storage, Alertmanager).\n  external_labels:\n    monitor: \"prometheus-stack-monitor\"\n\n# Load and evaluate rules in this file every 'evaluation_interval' seconds.\n#rule_files:\n# - \"first.rules\"\n# - \"second.rules\"\n\nscrape_configs:\n# scrape Prometheus itself\n  - job_name: prometheus\n    scrape_interval: 10s\n    scrape_timeout: 5s\n    static_configs:\n      - targets: [\"localhost:9090\"]\n\n# scrape Redis Software\n  - job_name: redis-enterprise\n    scrape_interval: 30s\n    scrape_timeout: 30s\n    metrics_path: /\n    scheme: https\n    tls_config:\n      insecure_skip_verify: true\n    static_configs:\n      - targets: [\"<cluster_name>:8070\"]","section_id":"setup-steps"},{"id":"setup-steps-ex2","language":"yml","code":"version: '3'\n        services:\n            prometheus-server:\n                image: prom/prometheus\n                ports:\n                    - 9090:9090\n                volumes:\n                    - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml\n                    # Persistent storage for Prometheus\n                    - prometheus-data:/prometheus\n\n            grafana-ui:\n                image: grafana/grafana\n                ports:\n                    - 3000:3000\n                environment:\n                    - GF_SECURITY_ADMIN_PASSWORD=secret\n                volumes:\n                    # Persistent storage for Grafana\n                    - grafana-storage:/var/lib/grafana\n                links:\n                    - prometheus-server:prometheus\n\n        # Define Docker-managed volumes for persistent storage\n        # These volumes are created automatically and persist data between container restarts\n        volumes:\n            prometheus-data:\n            grafana-storage:","section_id":"setup-steps"},{"id":"setup-steps-ex3","language":"sh","code":"$ docker compose up -d","section_id":"setup-steps"},{"id":"setup-steps-ex4","language":"sh","code":"docker ps","section_id":"setup-steps"}]}
{"schema_version":2,"id":"integrate/prometheus-with-redis-enterprise/observability","title":"Redis Software observability and monitoring guidance","url":"https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise/observability/","summary":"Using monitoring and observability with Redis Software","tags":["docs","integrate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"ada72460836b4bd64dda53434dd94de0bee0aee145c6451edcfe2a3355e52a59","sections":[{"id":"introduction","title":"Introduction","role":"overview","text":"This document provides observability and monitoring guidance for developers running applications\nthat connect to Redis Software. In particular, this guide focuses on the systems\nand resources that are most likely to impact the performance of your application.\n\nThe screenshot below shows a dashboard with relevant statistics for a node:\n\n![Dashboard showing relevant statistics for a Node](https://redis.io/docs/latest/images/node_summary.png)\n\nTo effectively monitor a Redis Software cluster you need to observe\ncore cluster resources and key database performance indicators as described in the following sections for this guide.\n\nCore cluster resources include:\n\n* Memory utilization\n* CPU utilization\n* Database connections\n* Network traffic\n* Synchronization\n\nKey database performance indicators include:\n\n* Latency\n* Cache hit rate\n* Key eviction rate\n* Proxy Performance\n\nDashboard showing an overview of cluster metrics:\n\n![Dashboard showing an overview of cluster metrics](https://redis.io/docs/latest/images/cluster_overview.png)\n\nIn addition to manually monitoring these resources and indicators, it is best practice to set up alerts."},{"id":"core-cluster-resource-monitoring","title":"Core cluster resource monitoring","role":"content","text":"Redis Software version 7.8.2 introduces a preview of the new metrics stream engine that exposes the v2 Prometheus scraping endpoint at `https://<cluster_name>:8070/v2`. This new engine exports all time-series metrics to external monitoring tools such as Grafana, DataDog, NewRelic, and Dynatrace using Prometheus.\n\nThe new engine enables real-time monitoring, including full monitoring during maintenance operations, providing full visibility into performance during events such as shards' failovers and scaling operations. See [Monitoring with metrics and alerts](https://redis.io/docs/latest/operate/rs/monitoring/) for more details.\n\nIf you are already using the existing scraping endpoint for integration, follow [this guide](https://redis.io/docs/latest/operate/rs/references/metrics/prometheus-metrics-v1-to-v2) to transition and try the new engine. You can scrape both existing and new endpoints simultaneously, which lets you create advanced dashboards and transition smoothly."},{"id":"memory","title":"Memory","role":"content","text":"Every Redis Software database has a maximum configured memory limit to ensure isolation\nin a multi-database cluster.\n\n| Metric name | Definition | Unit |\n| ------ | ------ | :------ |\n| Memory usage percentage metric | Percentage of used memory relative to the configured memory limit for a given database | Percentage |\n\nDashboard displaying high-level cluster metrics - [Cluster Dashboard](https://github.com/redis-field-engineering/redis-enterprise-observability/blob/main/grafana/dashboards/grafana_v9-11/software/classic/cluster_dashboard_v9-11.json)\n\n![Dashboard displaying high-level cluster metrics](https://redis.io/docs/latest/images/playbook_used-memory.png)"},{"id":"thresholds","title":"Thresholds","role":"content","text":"The appropriate memory threshold depends on how the application is using Redis.\n\n* Caching workloads, which permit Redis to evict keys, can safely use 100% of available memory.\n* Non-caching workloads do not permit key eviction and should be closely monitored as soon as memory usage reaches 80%."},{"id":"caching-workloads","title":"Caching workloads","role":"content","text":"For applications using Redis solely as a cache, you can safely let the memory usage\nreach 100% as long as you have an [eviction policy](https://redis.io/blog/cache-eviction-strategies/) in place. This will ensure\nthat Redis can evict keys while continuing to accept new writes.\n\n**Note:** Eviction will increase write command latency as Redis has to cleanup the memory/objects before accepting a new write to prevent OOM when memory usage is at 100%.\n\nWhile your Redis database is using 100% of available memory in a caching context,\nit's still important to monitor performance. The key performance indicators include:\n\n* Latency\n* Cache hit ratio\n* Evicted keys"},{"id":"read-latency","title":"Read latency","role":"content","text":"**Latency** has two important definitions, depending on context:\n\n* In the context of Redis itself, latency is **the time it takes for Redis\nto respond to a request**. The [Latency](#latency) section below provides a broader discussion of this metric.\n\n\n\n* In the context of your application, Latency is **the time it takes for the application\nto process a request**. This will include the time it takes to execute both reads and writes\nto Redis, as well as calls to other databases and services. Note that its possible for\nRedis to report low latency while the application is experiencing high latency.\nThis may indicate a low cache hit ratio, ultimately caused by insufficient memory.\n\nYou need to monitor both application-level and Redis-level latency to diagnose\ncaching performance issues in production."},{"id":"cache-hit-ratio-and-eviction","title":"Cache hit ratio and eviction","role":"content","text":"**Cache hit ratio** is the percentage of read requests that Redis serves successfully.\n**Eviction rate** is the rate at which Redis evicts keys from the cache. These metrics\nare sometimes inversely correlated: a high eviction rate may cause a low cache hit ratio if too many frequently-used keys are being evicted.\n\nIf the Redis server is empty, the hit ratio will be 0%. As the application runs and the fills the cache,\nthe hit ratio will increase.\n\n**When the entire cached working set fits in memory**, the cache hit ratio will reach close to 100%\nwhile the percent of used memory will remain below 100%.\n\n**When the working set cannot fit in memory**, the eviction policy will start to evict keys.\nIt is important to choose a policy that generally evicts rarely-used keys to keep the cache hit ratio as high as possible.\n\nIn both cases, keys will may be manually invalidated by the application or evicted through\nthe uses of TTLs (time-to-live) and an eviction policy.\n\nThe ideal cache hit ratio depends on the application, but generally, the ratio should be greater than 50%.\nLow hit ratios coupled with high numbers of object evictions may indicate that your cache is too small.\nThis can cause thrashing on the application side, a scenario where the cache is constantly being invalidated.\n\nThis means that when your Redis database is using 100% of available memory, you need\nto measure the rate of\n[key evictions](https://redis.io/docs/latest/operate/rs/references/metrics/database-operations#evicted-objectssec).\n\nAn acceptable rate of key evictions depends on the total number of keys in the database\nand the measure of application-level latency. If application latency is high,\ncheck to see that key evictions have not increased."},{"id":"eviction-policies","title":"Eviction policies","role":"content","text":"| Name | Description |\n| ------ | :------ |\n|noeviction | New values aren’t saved when memory limit is reached. When a database uses replication, this applies to the primary database |\n|allkeys-lru | Keeps most recently used keys; removes least recently used (LRU) keys |\n|allkeys-lfu | Keeps frequently used keys; removes least frequently used (LFU) keys |\n|volatile-lru | Removes least recently used keys with the expire field set to true. |\n|volatile-lfu | Removes least frequently used keys with the expire field set to true. |\n|allkeys-random | Randomly removes keys to make space for the new data added. |\n|volatile-random | Randomly removes keys with expire field set to true. |\n|volatile-ttl | Removes keys with expire field set to true and the shortest remaining time-to-live (TTL) value. |"},{"id":"eviction-policy-guidelines","title":"Eviction policy guidelines","role":"content","text":"* Use the allkeys-lru policy when you expect a power-law distribution in the popularity of your requests. That is, you expect a subset of elements will be accessed far more often than the rest. This is a good policy to choose if you are unsure.\n\n* Use the allkeys-random if you have a cyclic access where all the keys are scanned continuously, or when you expect the distribution to be uniform.\n\n* Use the volatile-ttl if you want to be able to provide hints to Redis about what are good candidates for expiration by using different TTL values when you create your cache objects.\n\nThe volatile-lru and volatile-random policies are mainly useful when you want to use a single instance for both caching and to have a set of persistent keys. However it is usually a better idea to run two Redis instances to solve such a problem.\n\n**Note:** Setting an expire value to a key costs memory, so using a policy like allkeys-lru is more memory efficient because there is no need for an expire configuration for the key to be evicted under memory pressure."},{"id":"non-caching-workloads","title":"Non-caching workloads","role":"content","text":"If no eviction policy is enabled, then Redis will stop accepting writes when memory usage reaches 100%.\nTherefore, for non-caching workloads, it is best practice to configure an alert at 80% memory usage.\nAfter your database reaches this 80% threshold, you should closely review the rate of memory usage growth."},{"id":"troubleshooting","title":"Troubleshooting","role":"errors","text":"|Issue | Possible causes | Remediation |\n| ------ | ------ | :------ |\n|Redis memory usage has reached 100% |This may indicate an insufficient Redis memory limit for your application's workload | For non-caching workloads (where eviction is unacceptable), immediately increase the memory limit for the database. You can accomplish this through the Redis Software console or its API. Alternatively, you can contact Redis support to assist. For caching workloads, you need to monitor performance closely. Confirm that you have an [eviction policy](https://redis.io/docs/latest/operate/rs/databases/memory-performance/eviction-policy) in place. If your application's performance starts to degrade, you may need to increase the memory limit, as described above. |\n|Redis has stopped accepting writes | Memory is at 100% and no eviction policy is in place | Increase the database's total amount of memory. If this is for a caching workload, consider enabling an [eviction policy](https://redis.io/docs/latest/operate/rs/databases/memory-performance/eviction-policy). In addition, you may want to determine whether the application can set a reasonable TTL (time-to-live) on some or all of the data being written to Redis. |\n|Cache hit ratio is steadily decreasing | The application's working set size may be steadily increasing. Alternatively, the application may be misconfigured (for example, generating more than one unique cache key per cached item.) | If the working set size is increasing, consider increasing the memory limit for the database. If the application is misconfigured, review the application's cache key generation logic. |"},{"id":"cpu","title":"CPU","role":"content","text":"Redis Software provides several CPU metrics:\n\n| Metric name | Definition | Unit |\n| ------ | ------ | :------ |\n| Shard CPU | CPU time portion spent by database shards as a percentage | up to 100% per shard |\n| Proxy CPU | CPU time portion spent by the cluster's proxy(s) as a percentage | 100% per proxy thread |\n| Node CPU (User and System) | CPU time portion spent by all user-space and kernel-level processesas a Percentage | 100% per node CPU |\n\n\nTo understand CPU metrics, it's worth recalling how a Redis Software cluster is organized.\nA cluster consists of one or more nodes. Each node is a VM (or cloud compute instance) or\na bare-metal server.\n\nA database is a set of processes, known as shards, deployed across the nodes of a cluster.\n\nIn the dashboard, shard CPU is the CPU utilization of the processes that make up the database.\nWhen diagnosing performance issues, start by looking at shard CPU.\n\nDashboard displaying CPU usage - [Database Dashboard](https://github.com/redis-field-engineering/redis-enterprise-observability/blob/main/grafana/dashboards/grafana_v9-11/software/classic/database_dashboard_v9-11.json)\n\n![Dashboard displaying CPU usage](https://redis.io/docs/latest/images/playbook_database-cpu-shard.png)"},{"id":"thresholds-1","title":"Thresholds","role":"content","text":"In general, we define high CPU as any CPU utilization above 80% of total capacity.\n\nShard CPU should remain below 80%. Shards are single-threaded, so a shard CPU of 100% means that the shard is fully utilized.\n\nDisplay showing Proxy CPU usage - [Proxy Dashboard](https://github.com/redis-field-engineering/redis-enterprise-observability/blob/main/grafana/dashboards/grafana_v9-11/software/classic/proxy_dashboard_v9-11.json)\n\n![Display showing Proxy CPU usage](https://redis.io/docs/latest/images/playbook_proxy-cpu-usage.png)\n\nProxy CPU should remain below 80% of total capacity.\nThe proxy is a multi-threaded process that handles client connections and forwards requests to the appropriate shard.\nBecause the total number of proxy threads is configurable, the proxy CPU may exceed 100%.\nA proxy configured with 6 threads can reach 600% CPU utilization, so in this case,\nkeeping utilization below 80% means keeping the total proxy CPU usage below 480%.\n\nDashboard displaying an ensemble of Node CPU usage data - [Node Dashboard](https://github.com/redis-field-engineering/redis-enterprise-observability/blob/main/grafana/dashboards/grafana_v9-11/software/classic/node_dashboard_v9-11.json)\n\n![Dashboard displaying an ensemble of Node CPU usage data](https://redis.io/docs/latest/images/node_cpu.png)\n\nNode CPU should also remain below 80% of total capacity. As with the proxy, the node CPU is variable depending\non the CPU capacity of the node. You will need to calibrate your alerting based on the number of cores in your nodes."},{"id":"troubleshooting-1","title":"Troubleshooting","role":"errors","text":"High CPU utilization has multiple possible causes. Common causes include an under-provisioned cluster,\nexcess inefficient Redis operations, and hot master shards.\n\n\n| Issue | Possible causes | Remediation \n| ------ | ------ | :------ |\n|High CPU utilization across all shards of a database | This usually indicates that the database is under-provisioned in terms of number of shards. A secondary cause may be that the application is running too many inefficient Redis operations. | You can detect slow Redis operations by enabling the slow log in the Redis Software UI. First, rule out inefficient Redis operations as the cause of the high CPU utilization. The Latency section below includes a broader discussion of this metric in the context of your application. If inefficient Redis operations are not the cause, then increase the number of shards in the database. |\n|High CPU utilization on a single shard, with the remaining shards having low CPU utilization | This usually indicates a master shard with at least one hot key. Hot keys are keys that are accessed extremely frequently (for example, more than 1000 times per second). | Hot key issues generally cannot be resolved by increasing the number of shards. To resolve this issue, see the section on Hot keys below. |\n| High Proxy CPU | There are several possible causes of high proxy CPU. First, review the behavior of connections to the database. Frequent cycling of connections, especially with TLS is enabled, can cause high proxy CPU utilization. This is especially true when you see more than 100 connections per second per thread. Such behavior is almost always a sign of a misbehaving application. Review the total number of operations per second against the cluster. If you see more than 50k operations per second per thread, you may need to increase the number of proxy threads. | In the case of high connection cycling, review the application's connection behavior. In the case of high operations per second, [increase the number of proxy threads](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-proxy). |\n|High Node CPU | You will typically detect high shard or proxy CPU utilization before you detect high node CPU utilization. Use the remediation steps above to address high shard and proxy CPU utilization. In spite of this, if you see high node CPU utilization, you may need to increase the number of nodes in the cluster. | Consider increasing the number of nodes in the cluster and the rebalancing the shards across the new nodes. This is a complex operation and you should do it with the help of Redis support. |\n|High System CPU | Most of the issues above will reflect user-space CPU utilization. However, if you see high system CPU utilization, this may indicate a problem at the network or storage level. | Review network bytes in and network bytes out to rule out any unexpected spikes in network traffic. You may need perform some deeper network diagnostics to identify the cause of the high system CPU utilization. For example, with high rates of packet loss, you may need to review network configurations or even the network hardware. |"},{"id":"connections","title":"Connections","role":"content","text":"The Redis Software database dashboard indicates the total number of connections to the database.\n\nYou should monitor this connection count metric with both a minimum and maximum number of connections in mind.\nBased on the number of application instances connecting to Redis (and whether your application uses [connection pooling](https://redis.io/docs/latest/develop/clients/pools-and-muxing)),\nyou should have a rough idea of the minimum and maximum number of connections you expect to see for any given database.\nThis number should remain relatively constant over time."},{"id":"troubleshooting-2","title":"Troubleshooting","role":"errors","text":"| Issue | Possible causes | Remediation |\n| ------ | ------ | :------ |\n|Fewer connections to Redis than expected |The application may not be connecting to the correct Redis database. There may be a network partition between the application and the Redis database. | Confirm that the application can successfully connect to Redis. This may require consulting the application logs or the application's connection configuration. | \n|Connection count continues to grow over time | Your application may not be releasing connections. The most common of such a connection leak is a manually implemented connection pool or a connection pool that is not properly configured. | Review the application's connection configuration |\n|Erratic connection counts (for example, spikes and drops) | Application misbehavior ([thundering herds](https://en.wikipedia.org/wiki/Thundering_herd_problem), connection cycling, or networking issues) | Review the application logs and network traffic to determine the cause of the erratic connection counts. |\n\n\nDashboard displaying connections - [Database Dashboard](https://github.com/redis-field-engineering/redis-enterprise-observability/blob/main/grafana/dashboards/grafana_v9-11/software/classic/database_dashboard_v9-11.json)\n\n![Dashboard displaying connections](https://redis.io/docs/latest/images/playbook_database-used-connections.png)"},{"id":"network-ingressegress","title":"Network ingress/egress","role":"content","text":"The network ingress/egress panel shows the amount of data being sent to and received from the database.\nLarge spikes in network traffic can indicate that the cluster is under-provisioned or that\nthe application is reading and/or writing unusually [large keys](#large-keys). A correlation between high network traffic\nand high CPU utilization may indicate a large key scenario.\n\n#### Unbalanced database endpoint\n\nOne possible cause of network traffic spikes is that the database endpoint is not located on the same node as the master shards. In addition to added network latency, if data plane internode encryption is enabled, CPU consumption can increase as well.\n\nOne solution is to use the optimal shard placement and proxy policy to ensure endpoints are collocated on nodes hosting master shards. If you need to restore balance (for example, after node failure) you can manually failover shard(s) with the `rladmin` cli tool.\n\nExtreme network traffic utilization may approach the limits of the underlying network infrastructure.\nIn this case, the only remediation is to add more nodes to the cluster and scale the database's shards across them."},{"id":"synchronization","title":"Synchronization","role":"content","text":"In Redis Software, geographically-distributed synchronization is based on Conflict-free replicated data types (CRDT) technology.\nThe Redis Software implementation of CRDT is called an Active-Active database (formerly known as CRDB).\nWith Active-Active databases, applications can read and write to the same data set from different geographical locations seamlessly and with low latency, without changing the way the application connects to the database.\n\nAn Active-Active architecture is a data resiliency architecture that distributes the database information over multiple data centers using independent and geographically distributed clusters and nodes.\nIt is a network of separate processing nodes, each having access to a common replicated database such that all nodes can participate in a common application ensuring local low latency with each region being able to run in isolation.\n\nTo achieve consistency between participating clusters, Redis Active-Active synchronization uses a process called the syncer.\n\nThe syncer keeps a replication backlog, which stores changes to the dataset that the syncer sends to other participating clusters.\nThe syncer uses partial syncs to keep replicas up to date with changes, or a full sync in the event a replica or primary is lost.\n\nDashboard displaying connection metrics between zones - [Synchronization Dashboard](https://github.com/redis-field-engineering/redis-enterprise-observability/blob/main/grafana/dashboards/grafana_v9-11/software/classic/synchronization_dashboard_v9-11.json)\n\n![Dashboard displaying connection metrics between zones](https://redis.io/docs/latest/images/playbook_network-connectivity.png)\n\nCRDT provides three fundamental benefits over other geo-distributed solutions:\n\n* It offers local latency on read and write operations, regardless of the number of geo-replicated regions and their distance from each other.\n* It enables seamless conflict resolution (\"conflict-free\") for simple and complex data types like those of Redis core.\n* Even if most of the geo-replicated regions in a CRDT database (for example, 3 out of 5) are down, the remaining geo-replicated regions are uninterrupted and can continue to handle read and write operations, ensuring business continuity."},{"id":"database-performance-indicators","title":"Database performance indicators","role":"content","text":"There are several key performance indicators that report your database's performance against your application's workload:\n\n* Latency\n* Cache hit rate\n* Key eviction rate"},{"id":"latency","title":"Latency","role":"content","text":"Latency is **the time it takes for Redis to respond to a request**.\nRedis Software measures latency from the first byte received by the proxy to the last byte sent in the command's response.\n\nAn adequately provisioned Redis database running efficient Redis operations will report an average latency below 1 millisecond. In fact, it's common to measure\nlatency in terms of microseconds. Businesses regularly achieve, and sometimes require, average latencies of 400-600\nmicroseconds.\n\nDashboard display of latency metrics - [Database Dashboard](https://github.com/redis-field-engineering/redis-enterprise-observability/blob/main/grafana/dashboards/grafana_v9-11/software/classic/database_dashboard_v9-11.json)\n\n![Dashboard display of latency metrics](https://redis.io/docs/latest/images/playbook_database-cluster-latency.png)\n\nThe metrics distinguish between read and write latency. Understanding whether high latency is due\nto read or writes can help you to isolate the underlying issue.\n\nNote that these latency metrics do not include network round trip time or application-level serialization,\nwhich is why it's essential to measure request latency at the application, as well.\n\nDisplay showing a noticeable spike in latency\n\n![Display showing a noticeable spike in latency](https://redis.io/docs/latest/images/latency_spike.png)"},{"id":"troubleshooting-3","title":"Troubleshooting","role":"errors","text":"Here are some possible causes of high database latency. Note that high database latency is just one of the reasons\nwhy application latency might be high. Application latency can be caused by a variety of factors, including\na low cache hit rate.\n\n| Issue | Possible causes | Remediation |\n| ------ | ------ | :------ |\n|Slow database operations | Confirm that there are no excessive slow operations in the Redis slow log. | If possible, reduce the number of slow operations being sent to the database. <br/>If this not possible, consider increasing the number of shards in the database. |\n|Increased traffic to the database | Review the network traffic and the database operations per second chart to determine if increased traffic is causing the latency. | If the database is underprovisioned due to increased traffic, consider increasing the number of shards in the database. |\n|Insufficient CPU | Check to see if the CPU utilization is increasing. | Confirm that slow operations are not causing the high CPU utilization. If the high CPU utilization is due to increased load, consider adding shards to the database. |"},{"id":"cache-hit-rate","title":"Cache hit rate","role":"content","text":"**Cache hit rate** is the percentage of all read operations that return a response. **Note:** Cache hit rate is a composite statistic that is computed by dividing the number of read hits by the total number of read operations.\nWhen an application tries to read a key that exists, this is known as a **cache hit**.\nAlternatively, when an application tries to read a key that does not exist, this is knows as a **cache miss**.\n\nFor caching workloads, the cache hit rate should generally be above 50%, although\nthe exact ideal cache hit rate can vary greatly depending on the application and depending on whether the cache\nis already populated.\n\nDashboard showing the cache hit ratio along with read/write misses - [Database Dashboard](https://github.com/redis-field-engineering/redis-enterprise-observability/blob/main/grafana/dashboards/grafana_v9-11/software/classic/database_dashboard_v9-11.json)\n\n![Dashboard showing the cache hit ratio along with read/write misses](https://redis.io/docs/latest/images/playbook_cache-hit.png)\n\n**Note:** Redis Software actually reports four different cache hit / miss metrics.\nThese are defined as follows:\n\n| Metric name | Definition | \n| ------ | :------ |\n| bdb_read_hits | The number of successful read operations |\n| bdb_read_misses | The number of read operations returning null |\n| bdb_write_hits | The number of write operations against existing keys |\n| bdb_write_misses | The number of write operations that create new keys |"},{"id":"troubleshooting-4","title":"Troubleshooting","role":"errors","text":"Cache hit rate is usually only relevant for caching workloads. Eviction will begin after the database approaches its maximum memory capacity.\n\nA high or increasing rate of evictions will negatively affect database latency, especially\nif the rate of necessary key evictions exceeds the rate of new key insertions.\n\nSee the [Cache hit ratio and eviction](#cache-hit-ratio-and-eviction) section for tips on troubleshooting cache hit rate."},{"id":"key-eviction-rate","title":"Key eviction rate","role":"content","text":"They **key eviction rate** is rate at which objects are being evicted from the database.\nSee [eviction policy](https://redis.io/docs/latest/operate/rs/databases/memory-performance/eviction-policy) for a discussion of key eviction and its relationship with memory usage.\n\nDashboard displaying object evictions - [Database Dashboard](https://github.com/redis-field-engineering/redis-enterprise-observability/blob/main/grafana/dashboards/grafana_v9-11/software/classic/database_dashboard_v9-11.json)\n\n![Dashboard displaying object evictions](https://redis.io/docs/latest/images/playbook_eviction-expiration.png)"},{"id":"proxy-performance","title":"Proxy performance","role":"content","text":"Redis Software provides high-performance data access through a proxy process that manages and optimizes access to shards within the cluster. Each node contains a single proxy process. Each proxy can be active and take incoming traffic or it can be passive and wait for failovers."},{"id":"proxy-policies","title":"Proxy policies","role":"content","text":"| Policy | Description |\n| ------ | :------ |\n|Single | There is only a single proxy that is bound to the database. This is the default database configuration and preferable in most use cases. |\n|All Master Shards | There are multiple proxies that are bound to the database, one on each node that hosts a database master shard. This mode fits most use cases that require multiple proxies. |\n|All Nodes | There are multiple proxies that are bound to the database, one on each node in the cluster, regardless of whether or not there is a shard from this database on the node. This mode should be used only in special cases, such as using a load balancer. |\n\nDashboard displaying proxy thread activity - [Proxy Thread Dashboard](https://github.com/redis-field-engineering/redis-enterprise-observability/blob/main/grafana/dashboards/grafana_v9-11/cloud/basic/redis-cloud-proxy-dashboard_v9-11.json)\n\n![Dashboard displaying proxy thread activity](https://redis.io/docs/latest/images/proxy-thread-dashboard.png)\n\nIf you need to, you can tune the number of proxy threads using the [`rladmin tune proxy`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-proxy) command to make the proxy use more CPU cores.\nCores used by the proxy won't be available for Redis, therefore we need to take into account the number of Redis nodes on the host and the total number of available cores.\n\nThe command has a few parameters you can use to set a new number of proxy cores:\n\n* `id|all` - you can either tune a specific proxy by its id, or all proxies.\n\n* `mode` - determines whether or not the proxy can automatically adjust the number of threads depending on load.\n\n* `threads` and `max_threads` - determine the initial number of threads created on startup, and the maximum number of threads allowed.\n\n* `scale_threshold` - determines the CPU utilization threshold that triggers spawning new threads. This CPU utilization level needs to be maintained for at least scale_duration seconds before automatic scaling is performed.\n\nThe following table indicates ideal proxy thread counts for the specified environments.\n\n\n| Total Cores | Redis (ROR) | Redis on Flash (ROF) |\n| ------ | ------ | :------ |\n|1|1|1 |\n|4|3|3 |\n|8|5|3 |\n|12|8|4 |\n|16|10|5 |\n|32|24|10 |\n|64/96|32|20 |\n|128|32|32 |"},{"id":"data-access-anti-patterns","title":"Data access anti-patterns","role":"content","text":"There are three data access patterns that can limit the performance of your Redis database:\n\n* Slow operations\n* Hot keys\n* Large keys\n\nThis section defines each of these patterns and describes how to diagnose and mitigate them."},{"id":"slow-operations","title":"Slow operations","role":"content","text":"**Slow operations** are operations that take longer than a few milliseconds to complete.\n\nNot all Redis operations are equally efficient.\nThe most efficient Redis operations are O(1) operations; that is, they have a constant time complexity.\nExample of such operations include [GET](https://redis.io/docs/latest/commands/get),\n[SET](https://redis.io/docs/latest/commands/set), [SADD](https://redis.io/docs/latest/commands/sadd),\nand [HSET](https://redis.io/docs/latest/commands/hset).\n\nThese constant time operations are unlikely to cause high CPU utilization. **Note:** Even so,\nit's still possible for a high rate of constant time operations to overwhelm an underprovisioned database.\n\nOther Redis operations exhibit greater levels of time complexity.\nO(n) (linear time) operations are more likely to cause high CPU utilization.\nExamples include [HGETALL](https://redis.io/docs/latest/commands/hgetall), [SMEMBERS](https://redis.io/docs/latest/commands/smembers),\nand [LREM](https://redis.io/docs/latest/commands/lrem).\nThese operations are not necessarily problematic, but they can be if executed against data structures holding\na large number of elements (for example, a list with 1 million elements).\n\nHowever, the [KEYS](https://redis.io/docs/latest/commands/keys) command should almost never be run against a\nproduction system, since returning a list of all keys in a large Redis database can cause significant slowdowns\nand block other operations. If you need to scan the keyspace, especially in a production cluster, always use the\n[SCAN](https://redis.io/docs/latest/commands/scan) command instead."},{"id":"troubleshooting-5","title":"Troubleshooting","role":"errors","text":"The best way to discover slow operations is to view the slow log.\nThe slow log is available in the Redis Software and Redis Cloud consoles:\n* [Redis Software slow log docs](https://redis.io/docs/latest/operate/rs/clusters/logging/redis-slow-log)\n* [Redis Cloud slow log docs](https://redis.io/docs/latest/operate/rc/databases/view-edit-database#other-actions-and-info)\n\nRedis Cloud dashboard showing slow database operations\n\n![Redis Cloud dashboard showing slow database operations](https://redis.io/docs/latest/images/slow_log.png)\n\n| Issue | Remediation |\n| ------ | :------ |\n|The KEYS command shows up in the slow log |Find the application that issues the KEYS command and replace it with a SCAN command. In an emergency situation, you can [alter the ACLs for the database user](https://redis.io/docs/latest/operate/rs/security/access-control/redis-acl-overview) so that Redis will reject the KEYS command altogether. |\n|The slow log shows a significant number of slow, O(n) operations | If these operations are being issued against large data structures, then the application may need to be refactored to use more efficient Redis commands. |\n|The slow logs contains only O(1) commands, and these commands are taking several milliseconds or more to complete |This likely indicates that the database is underprovisioned. Consider increasing the number of shards and/or nodes. |"},{"id":"hot-keys","title":"Hot keys","role":"content","text":"A **hot key** is a key that is accessed extremely frequently (for example, thousands of times a second or more).\n\nEach key in Redis belongs to one, and only one, shard.\nFor this reason, a hot key can cause high CPU utilization on that one shard,\nwhich can increase latency for all other operations."},{"id":"troubleshooting-6","title":"Troubleshooting","role":"errors","text":"You may suspect that you have a hot key if you see high CPU utilization on a single shard.\nThere are two main way to identify hot keys: using the Redis CLI and sampling the operations against Redis.\n\nTo use the Redis CLI to identify hot keys:\n\n1. First confirm that you have enough available memory to enable an eviction policy.\n2. Next, enable the LFU (least-frequently used) eviction policy on the database.\n3. Finally, run `redis-cli --hotkeys`\n\nYou may also identify hot keys by sampling the operations against Redis.\nYou can use do this by running the [MONITOR](https://redis.io/docs/latest/commands/monitor) command\nagainst the high CPU shard. Because this is a potentially high-impact operation, you should only\nuse this technique as a secondary option. For mission-critical databases, consider\ncontacting Redis support for assistance."},{"id":"remediation","title":"Remediation","role":"content","text":"After you discover a hot key, you need to find a way to reduce the number of operations against it.\nThis means getting an understanding of the application's access pattern and the reasons for such frequent access.\n\nIf the hot key operations are read-only, consider implementing an application-local cache so\nthat fewer read requests are sent to Redis. For example, even a local cache that expires every 5 seconds\ncan entirely eliminate a hot key issue."},{"id":"large-keys","title":"Large keys","role":"content","text":"**Large keys** are keys that are hundreds of kilobytes or larger.\nHigh network traffic and high CPU utilization can be caused by large keys."},{"id":"troubleshooting-7","title":"Troubleshooting","role":"errors","text":"To identify large keys, you can sample the keyspace using the Redis CLI.\n\nRun `redis-cli --memkeys` against your database to sample the keyspace in real time\nand potentially identify the largest keys in your database."},{"id":"remediation-1","title":"Remediation","role":"content","text":"Addressing a large key issue requires understanding why the application is creating large keys in the first place.\nAs such, it's difficult to provide general advice to solving this issue. Resolution often requires a change\nto the application's data model or the way it interacts with Redis."},{"id":"alerting","title":"Alerting","role":"content","text":"The Redis Software observability package includes a suite of alerts and their associated tests for use with Prometheus.\n\n- [Alerts for the v1 metrics endpoint](https://github.com/redis-field-engineering/redis-enterprise-observability/tree/main/prometheus)\n\n- [Alerts for the v2 metrics endpoint](https://github.com/redis-field-engineering/redis-enterprise-observability/tree/main/prometheus_v2)\n\n> [!NOTE]\n> Not all the alerts are appropriate for all environments; for example, installations that do not use persistence have no need for storage alerts.\n\nThe alerts are packaged with a series of tests\nthat validate the individual triggers. You can use these tests to validate your modifications to these alerts for specific environments and use cases.\n\n- [Tests for the v1 metrics endpoint](https://github.com/redis-field-engineering/redis-enterprise-observability/tree/main/prometheus/tests)\n\n- [Tests for the v2 metrics endpoint](https://github.com/redis-field-engineering/redis-enterprise-observability/tree/main/prometheus_v2/tests)\n\nTo use these alerts, install [Prometheus Alertmanager](https://prometheus.io/docs/alerting/latest/configuration/).\nFor a comprehensive guide to alerting with Prometheus and Grafana,\nsee the [Grafana blog post on the subject](https://grafana.com/blog/2020/02/25/step-by-step-guide-to-setting-up-prometheus-alertmanager-with-slack-pagerduty-and-gmail/)."},{"id":"configure-prometheus","title":"Configure Prometheus","role":"configuration","text":"To configure Prometheus for alerting, open the `prometheus.yml` configuration file.\n\nUncomment the `Alertmanager` section of the file.\nThe following configuration starts Alertmanager and instructs it to listen on its default port of 9093.\n\n[code example]\n\nThe Rule file section of the config file instructs Alertmanager to read specific rules files.\nIf you pasted the `alerts.yml` file into `/etc/prometheus` then the following configuration would be required.\n\n[code example]\n\nAfter you've done this, restart Prometheus.\n\nThe built-in configuration, `error_rules.yml`, has a single alert: Critical Connection Exception.\nIf you open the Prometheus console, by default located at port 9090, and select the Alert tab,\nyou will see this alert, as well as the alerts in any other file you have included as a rules file.\n\n![prometheus alerts image](https://redis.io/docs/latest/images/playbook_prometheus-alerts.png)\n\nThe following is a list of alerts contained in the `alerts.yml` file. There are several points to consider:\n\n- Not all Redis Software deployments export all metrics\n- Most metrics only alert if the specified trigger persists for a given duration"},{"id":"list-of-alerts","title":"List of alerts","role":"content","text":"| Description | Trigger |\n| ------ | :------ |\n|Average latency has reached a warning level | round(bdb_avg_latency * 1000) > 1 |\n|Average latency has reached a critical level indicating system degradation | round(bdb_avg_latency * 1000) > 4 |\n|Absence of any connection indicates improper configuration or firewall issue | bdb_conns < 1 |\n|A flood of connections has occurred that will impact normal operations | bdb_conns > 64000 |\n|Absence of any requests indicates improperly configured clients | bdb_total_req < 1 |\n|Excessive number of client requests indicates configuration and/or programmatic issues | bdb_total_req > 1000000 |\n|The database in question will soon be unable to accept new data | round((bdb_used_memory/bdb_memory_limit) * 100) > 98 |\n|The database in question will be unable to accept new data in two hours | round((bdb_used_memory/bdb_memory_limit) ** 100) < 98 and (predict_linear(bdb_used_memory[15m], 2 ** 3600) / bdb_memory_limit) > 0.3 and round(predict_linear(bdb_used_memory[15m], 2 * 3600)/bdb_memory_limit) > 0.98 |\n|Database read operations are failing to find entries more than 50% of the time | (100 * bdb_read_hits)/(bdb_read_hits + bdb_read_misses) < 50 |\n|In situations where TTL values are not set this indicates a problem | bdb_evicted_objects > 1 |\n|Replication between nodes is not in a satisfactory state | bdb_replicaof_syncer_status > 0 |\n|Record synchronization between nodes is not in a satisfactory state | bdb_crdt_syncer_status > 0 |\n|The amount by which replication lags behind events is worrisome | bdb_replicaof_syncer_local_ingress_lag_time > 500 |\n|The amount by which object replication lags behind events is worrisome | bdb_crdt_syncer_local_ingress_lag_time > 500 |\n|The number of active nodes is less than expected | count(node_up) != 3 |\n|Persistent storage will soon be exhausted | round((node_persistent_storage_free/node_persistent_storage_avail) * 100) <= 5 |\n|Ephemeral storage will soon be exhausted | round((node_ephemeral_storage_free/node_ephemeral_storage_avail) * 100) <= 5 |\n|The node in question is close to running out of memory | round((node_available_memory/node_free_memory) * 100) <= 15 |\n|The node in question has exceeded expected levels of CPU usage | round((1 - node_cpu_idle) * 100) >= 80 |\n|The shard in question is not reachable | redis_up == 0 |\n|The master shard is not reachable | floor(redis_master_link_status{role=\"slave\"}) < 1 |\n|The shard in question has exceeded expected levels of CPU usage | redis_process_cpu_usage_percent >= 80 |\n|The master shard has exceeded expected levels of CPU usage | redis_process_cpu_usage_percent{role=\"master\"} > 0.75 and redis_process_cpu_usage_percent{role=\"master\"} > on (bdb) group_left() (avg by (bdb)(redis_process_cpu_usage_percent{role=\"master\"}) + on(bdb) 1.2 * stddev by (bdb) (redis_process_cpu_usage_percent{role=\"master\"})) |\n|The shard in question has an unhealthily high level of connections | redis_connected_clients > 500 |"},{"id":"appendix-a-grafana-dashboards","title":"Appendix A: Grafana Dashboards","role":"content","text":"Grafana dashboards are available for Redis Software and Redis Cloud deployments.\n\nThese dashboards come in three styles, which may be used together to provide\na full picture of your deployment.\n\n1. Classic dashboards provide detailed information about the cluster, nodes, and individual databases.\n2. Basic dashboards provide a high-level overviews of the various cluster components.\n3. Extended dashboards. These require a third-party library to perform ReST calls.\n\nThere are also two workflow dashboards for Redis Software that provide drill-down functionality."},{"id":"software","title":"Software","role":"content","text":"- [Basic](https://github.com/redis-field-engineering/redis-enterprise-observability/tree/main/grafana/dashboards/grafana_v9-11/software/basic)\n- [Extended](https://github.com/redis-field-engineering/redis-enterprise-observability/tree/main/grafana/dashboards/grafana_v9-11/software/extended)\n- [Classic](https://github.com/redis-field-engineering/redis-enterprise-observability/tree/main/grafana/dashboards/grafana_v9-11/software/classic)"},{"id":"workflow","title":"Workflow","role":"content","text":"- [Database](https://github.com/redis-field-engineering/redis-enterprise-observability/tree/main/grafana/dashboards/grafana_v9-11/workflow/databases)\n- [Node](https://github.com/redis-field-engineering/redis-enterprise-observability/tree/main/grafana/dashboards/grafana_v9-11/workflow/nodes)"},{"id":"cloud","title":"Cloud","role":"content","text":"- [Basic](https://github.com/redis-field-engineering/redis-enterprise-observability/tree/main/grafana/dashboards/grafana_v9-11/cloud/basic)\n- [Extended](https://github.com/redis-field-engineering/redis-enterprise-observability/tree/main/grafana/dashboards/grafana_v9-11/cloud/extended)\n\n**Note:** - The 'workflow' dashboards are intended to be used as a package. Therefore they should all be installed, as they contain links to the other dashboards in the group permitting rapid navigation between the overview and the drill-down views."}],"examples":[{"id":"configure-prometheus-ex0","language":"plaintext","code":"# Alertmanager configuration\nalerting:\n  alertmanagers:\n    - static_configs:\n        - targets:\n          - alertmanager:9093","section_id":"configure-prometheus"},{"id":"configure-prometheus-ex1","language":"plaintext","code":"# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.\nrule_files:\n  - \"error_rules.yml\"\n  - \"alerts.yml\"","section_id":"configure-prometheus"}]}
{"schema_version":2,"id":"integrate/prometheus-with-redis-enterprise/prometheus-metrics-definitions","title":"Prometheus metrics v2","url":"https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise/prometheus-metrics-definitions/","summary":"V2 metrics available to Prometheus as of Redis Software version 7.8.2.","tags":["docs","integrate","rs"],"last_updated":"2026-09-17T14:54:30+01:00","page_type":"content","content_hash":"7a9330326ee787a4cbab0a570af64671522c1ad95d31cb2ecdafb96d048a3524","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can [integrate Redis Software with Prometheus and Grafana](https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise) to create dashboards for important metrics.\n\nThe v2 metrics in the following tables are available as of Redis Software version 7.8.0. For help transitioning from v1 metrics to v2 PromQL, see [Prometheus v1 metrics and equivalent v2 PromQL](https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise/prometheus-metrics-v1-to-v2).\n\nThe v2 scraping endpoint also exposes metrics for `node_exporter`. For more information, see the [Prometheus node_exporter GitHub repository](https://github.com/prometheus/node_exporter)."},{"id":"database-metrics","title":"Database metrics","role":"content","text":"| Metric | Type | Unit | Description |\n| :-------- | :--- | :--- | :---------- |\n| <span class=\"break-all\">endpoint_accepted_connections</span> | counter | count | Number of incoming accepted client connections |\n| endpoint_client_connections | counter | count | Number of client connection establishment events |\n| endpoint_client_disconnections | counter | count | Number of client disconnections initiated by the client |\n| <span class=\"break-all\">endpoint_client_connection_expired</span> | counter | count | Total number of client connections with expired TTL (Time To Live) |\n| <span class=\"break-all\">endpoint_client_establishment_failures</span> | counter | count | Number of client connections that failed to establish properly |\n| <span class=\"break-all\">endpoint_client_expiration_refresh</span> | counter | count | Number of expiration time changes of clients |\n| <span class=\"break-all\">endpoint_client_tracking_off_requests</span> | counter | count | Total number of `CLIENT TRACKING OFF` requests |\n| <span class=\"break-all\">endpoint_client_tracking_on_requests</span> | counter | count | Total number of `CLIENT TRACKING ON` requests |\n| <span class=\"break-all\">endpoint_connections_rate</span> | gauge | connections/second | The rate of incoming connections. Computed as `n_accepted / N` for the last interval where `n_accepted` is the number of accepted connections in this interval, and `N` is the interval in seconds. |\n| <span class=\"break-all\">endpoint_disconnected_cba_client</span> | counter | count | Number of certificate-based clients disconnected |\n| <span class=\"break-all\">endpoint_disconnected_ldap_client</span> | counter | count | Number of LDAP clients disconnected |\n| <span class=\"break-all\">endpoint_disconnected_user_password_client</span> | counter | count | Number of user&password clients disconnected |\n| <span class=\"break-all\">endpoint_dispatch_failures</span> | counter | count | Number of clients closed due to failure to be dispatched to workers |\n| <span class=\"break-all\">endpoint_disposed_commands_after_client_caching</span> | counter | count | Total number of client caching commands that were disposed due to misuse |\n| endpoint_egress | counter | bytes | Number of egress bytes |\n| endpoint_egress_pending | counter | bytes | Number of send-pending bytes |\n| <span class=\"break-all\">endpoint_egress_pending_discarded</span> | counter | bytes | Number of send-pending bytes that were discarded due to disconnection |\n| <span class=\"break-all\">endpoint_failed_cba_authentication</span> | counter | count | Number of clients that failed certificate-based authentication |\n| <span class=\"break-all\">endpoint_failed_ldap_authentication</span> | counter | count | Number of clients that failed LDAP authentication |\n| <span class=\"break-all\">endpoint_failed_user_password_authentication</span> | counter | count | Number of clients that failed user password authentication |\n| endpoint_ingress | counter | bytes | Number of ingress bytes |\n| <span class=\"break-all\">endpoint_longest_pipeline_histogram</span> | histogram | count | Tracks the distribution of longest observed pipeline lengths, where a pipeline is a sequence of client commands sent without waiting for responses. |\n| endpoint_other_requests | counter | count | Number of other requests |\n| <span class=\"break-all\">endpoint_other_requests_latency_histogram</span> | histogram | microseconds | Latency (in µs) histogram of other commands |\n| <span class=\"break-all\">endpoint_other_requests_latency_histogram_bucket</span> | histogram | microseconds | Latency histograms for commands other than read or write commands. Can be used to represent different latency percentiles.<br />p99.9 example:<br /><span class=\"break-all\">`histogram_quantile(0.999, sum(rate(endpoint_other_requests_latency_histogram_bucket{cluster=\"$cluster\", db=\"$db\"}[$__rate_interval]) ) by (le, db))`</span> |\n| endpoint_other_responses | counter | count | Number of other responses |\n| <span class=\"break-all\">endpoint_ping_failures</span> | gauge | count | Number of consecutive endpoint ping failures. Labels: endpoint_uid |\n| <span class=\"break-all\">endpoint_ping_failure_duration_seconds</span> | gauge | seconds | Duration of ongoing endpoint failures (0 when healthy). Labels: endpoint_uid |\n| endpoint_proxy_disconnections | counter | count | Number of client disconnections initiated by the proxy |\n| <span class=\"break-all\">endpoint_rate_limit_ok</span> | gauge | — | Rate limit status based on the last 2 intervals.<br />0 = rate limit was recently exceeded<br />1 = rate limit was not recently exceeded |\n| <span class=\"break-all\">endpoint_rate_limit_overflows</span> | counter | count | Total number of rate limit overflows |\n| endpoint_read_requests | counter | count | Number of read requests |\n| <span class=\"break-all\">endpoint_read_requests_latency_histogram</span> | histogram | microseconds | Latency (in µs) histogram of read commands |\n| <span class=\"break-all\">endpoint_read_requests_latency_histogram_bucket</span> | histogram | microseconds | Latency histograms for read commands. Can be used to represent different latency percentiles.<br />p99.9 example:<br /><span class=\"break-all\">`histogram_quantile(0.999, sum(rate(endpoint_read_requests_latency_histogram_bucket{cluster=\"$cluster\", db=\"$db\"}[$__rate_interval]) ) by (le, db))`</span> |\n| endpoint_read_responses | counter | count | Number of read responses |\n| <span class=\"break-all\">endpoint_successful_cba_authentication</span> | counter | count | Number of clients that successfully authenticated with certificate-based authentication |\n| <span class=\"break-all\">endpoint_successful_ldap_authentication</span> | counter | count | Number of clients that successfully authenticated with LDAP |\n| <span class=\"break-all\">endpoint_successful_user_password_authentication</span> | counter | count | Number of clients that successfully authenticated with user&password |\n| endpoint_write_requests | counter | count | Number of write requests |\n| <span class=\"break-all\">endpoint_write_requests_latency_histogram</span> | histogram | microseconds | Latency (in µs) histogram of write commands |\n| <span class=\"break-all\">endpoint_write_requests_latency_histogram_bucket</span> | histogram | microseconds | Latency histograms for write commands. Can be used to represent different latency percentiles.<br />p99.9 example:<br /><span class=\"break-all\">`histogram_quantile(0.999, sum(rate(endpoint_write_requests_latency_histogram_bucket{cluster=\"$cluster\", db=\"$db\"}[$__rate_interval]) ) by (le, db))`</span> |\n| endpoint_write_responses | counter | count | Number of write responses |\n| db_config | gauge | — | This is an information metric that holds database configuration within labels such as: db_name, db_version, db_port, tls_mode |\n| db_status | gauge | — | This is a status metric that reports on various database statuses: 0 = active, 1 = active-change-pending, 2 = pending, 3 = import-pending, 4 = delete-pending, 5 = recovery, 99 = unknown |\n| db_tags | gauge | — | Information metric that exposes database tags as labels; the value is always `1`. See [Database tags in metrics](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/db-tags-in-metrics). |"},{"id":"node-metrics","title":"Node metrics","role":"content","text":"| Metric | Type | Unit | Description |\n| :-------- | :--- | :--- | :---------- |\n| node_available_flash_bytes | gauge | bytes | Available flash in the node (bytes) |\n| <span class=\"break-all\">node_available_flash_no_overbooking_bytes</span> | gauge | bytes | Available flash in the node (bytes), without taking into account overbooking |\n| <span class=\"break-all\">node_available_memory_bytes</span> | gauge | bytes | Amount of free memory in the node (bytes) |\n| <span class=\"break-all\">node_available_memory_no_overbooking_bytes</span> | gauge | bytes | Available RAM in the node (bytes) without taking into account overbooking |\n| node_bigstore_free_bytes | gauge | bytes | Sum of free space of back-end flash (used by flash database's [BigRedis]) on all cluster nodes (bytes); returned only when BigRedis is enabled |\n| <span class=\"break-all\">node_cert_expires_in_seconds</span> | gauge | seconds | Certificate expiration (in seconds) per given node; read more about [certificates in Redis Software](https://redis.io/docs/latest/operate/rs/security/certificates) and [monitoring certificates](https://redis.io/docs/latest/operate/rs/security/certificates/monitor-certificates) |\n| <span class=\"break-all\">customer_managed_ine_certificates</span> | gauge | — | Indicates whether customer-provided internode encryption certificates are in use<br />0=No<br />1=Yes |\n| <span class=\"break-all\">node_ephemeral_storage_avail_bytes</span> | gauge | bytes | Disk space available to RLEC processes on configured ephemeral disk (bytes) |\n| <span class=\"break-all\">node_ephemeral_storage_free_bytes</span> | gauge | bytes | Free disk space on configured ephemeral disk (bytes) |\n| node_memory_MemFree_bytes | gauge | bytes | Free memory in the node (bytes) |\n| <span class=\"break-all\">node_persistent_storage_avail_bytes</span> | gauge | bytes | Disk space available to RLEC processes on configured persistent disk (bytes) |\n| <span class=\"break-all\">node_persistent_storage_free_bytes</span> | gauge | bytes | Free disk space on configured persistent disk (bytes) |\n| <span class=\"break-all\">node_provisional_flash_bytes</span> | gauge | bytes | Amount of flash available for new shards on this node, taking into account overbooking, max Redis servers, reserved flash, and provision and migration thresholds (bytes) |\n| <span class=\"break-all\">node_provisional_flash_no_overbooking_bytes</span> | gauge | bytes | Amount of flash available for new shards on this node, without taking into account overbooking, max Redis servers, reserved flash, and provision and migration thresholds (bytes) |\n| <span class=\"break-all\">node_provisional_memory_bytes</span> | gauge | bytes | Amount of RAM that is available for provisioning to databases out of the total RAM allocated for databases |\n| <span class=\"break-all\">node_provisional_memory_no_overbooking_bytes</span> | gauge | bytes | Amount of RAM that is available for provisioning to databases out of the total RAM allocated for databases, without taking into account overbooking |\n| node_metrics_up | gauge | — | Node is part of the cluster and is connected (1 = connected, 0 = not connected) |\n| <span class=\"break-all\">dmc_ping_failures</span> | gauge | count | Number of consecutive DMC ping failures |\n| <span class=\"break-all\">dmc_ping_failure_duration_seconds</span> | gauge | seconds | Duration of ongoing DMC failures (0 when healthy) |"},{"id":"cluster-metrics","title":"Cluster metrics","role":"content","text":"| Metric | Type | Unit | Description |\n| :-------- | :--- | :--- | :---------- |\n| <span class=\"break-all\">generation{node=<node_uid>}</span> | gauge | — | Current generation (state-version) number of the cluster watchdog on the specific node |\n| <span class=\"break-all\">has_quorum{node=<node_uid>, has_witness_disk=BOOL}</span> | gauge | — | Has_quorum = 1<br />No quorum = 0 |\n| <span class=\"break-all\">is_primary{node=<node_uid>}</span> | gauge | — | primary = 1<br />secondary = 0 |\n| <span class=\"break-all\">license_expiration_days</span> | gauge | days | Number of days until the license expires |\n| <span class=\"break-all\">license_shards_limit</span> | gauge | count | Total shard limit by the license by shard type (ram / flash) |\n| <span class=\"break-all\">total_live_nodes_count{node=<node_uid>}</span> | gauge | count | Number of live nodes |\n| <span class=\"break-all\">total_nodes_count{node=<node_uid>}</span> | gauge | count | Number of nodes |\n| <span class=\"break-all\">primary_selections_total{node=<node_uid>}</span> | counter | count | Monotonic counter for each selection process that started |\n| users_count | gauge | count | Current number of users on the cluster |"},{"id":"replication-metrics","title":"Replication metrics","role":"content","text":"| Metric | Type | Unit | Description |\n| :-------- | :--- | :--- | :---------- |\n| <span class=\"break-all\">database_syncer_config</span> | gauge | — | Used as a placeholder for configuration labels |\n| <span class=\"break-all\">database_syncer_current_status</span> | gauge | — | Syncer status for traffic; 0 = in-sync, 1 = out of sync |\n| <span class=\"break-all\">database_syncer_dst_connectivity_state</span> | gauge | — | Destination connectivity state (1 = connected, 0 = disconnected) |\n| <span class=\"break-all\">database_syncer_dst_connectivity_state_ms</span> | gauge | milliseconds | Destination connectivity state duration |\n| <span class=\"break-all\">database_syncer_dst_lag</span> | gauge | milliseconds | Lag in milliseconds between the syncer and the destination |\n| <span class=\"break-all\">database_syncer_dst_repl_offset</span> | gauge | bytes | Offset of the last command acknowledged |\n| <span class=\"break-all\">database_syncer_flush_counter</span> | gauge | count | Number of destination flushes |\n| <span class=\"break-all\">database_syncer_ingress_bytes</span> | gauge | bytes | Number of bytes read from source shard |\n| <span class=\"break-all\">database_syncer_ingress_bytes_decompressed</span> | gauge | bytes | Number of bytes read from source shard |\n| <span class=\"break-all\">database_syncer_internal_state</span> | gauge | — | Internal state of the syncer |\n| <span class=\"break-all\">database_syncer_lag_ms</span> | gauge | milliseconds | Lag time between the source and the destination for traffic in milliseconds |\n| <span class=\"break-all\">database_syncer_rdb_size</span> | gauge | bytes | The source's RDB size in bytes to be transferred during the syncing phase |\n| <span class=\"break-all\">database_syncer_rdb_transferred</span> | gauge | bytes | Number of bytes transferred from the source's RDB during the syncing phase |\n| <span class=\"break-all\">database_syncer_src_connectivity_state</span> | gauge | — | Source connectivity state (1 = connected, 0 = disconnected) |\n| <span class=\"break-all\">database_syncer_src_connectivity_state_ms</span> | gauge | milliseconds | Source connectivity state duration |\n| <span class=\"break-all\">database_syncer_src_repl_offset</span> | gauge | bytes | Last known source offset |\n| <span class=\"break-all\">database_syncer_state</span> | gauge | — | Internal state of the shard syncer |\n| <span class=\"break-all\">database_syncer_syncer_repl_offset</span> | gauge | bytes | Offset of the last command handled by the syncer |\n| <span class=\"break-all\">database_syncer_total_requests</span> | gauge | count | Number of destination writes |\n| <span class=\"break-all\">database_syncer_total_responses</span> | gauge | count | Number of destination writes acknowledged |"},{"id":"shard-metrics","title":"Shard metrics","role":"content","text":"| Metric | Type | Unit | Description |\n| :-------- | :--- | :--- | :---------- |\n| redis_server_active_defrag_running | gauge | percent | Automatic memory defragmentation current aggressiveness (% cpu) |\n| redis_server_allocator_active | gauge | bytes | Total used memory, including external fragmentation |\n| redis_server_allocator_allocated | gauge | bytes | Total allocated memory |\n| redis_server_allocator_resident | gauge | bytes | Total resident memory (RSS) |\n| redis_server_aof_last_cow_size | gauge | bytes | Last AOFR, CopyOnWrite memory |\n| <span class=\"break-all\">redis_server_aof_rewrite_in_progress</span> | gauge | — | Indicates whether an AOF rewrite is in progress (1 = in progress, 0 = otherwise) |\n| redis_server_aof_rewrites | gauge | count | Number of AOF rewrites this process executed |\n| redis_server_aof_delayed_fsync | gauge | count | Number of times an AOF fsync caused delays in the main Redis thread (inducing latency); this can indicate that the disk is slow or overloaded |\n| redis_server_blocked_clients | gauge | count | Count the clients waiting on a blocking call |\n| redis_server_connected_clients | gauge | count | Number of client connections to the specific shard |\n| redis_server_connected_slaves | gauge | count | Number of connected replicas |\n| redis_server_db_avg_ttl | gauge | milliseconds | Average TTL of all volatile keys |\n| redis_server_db0_avg_ttl | gauge | milliseconds | Average TTL of all volatile keys. Deprecated. |\n| redis_server_db_keys | gauge | count | Total key count. |\n| redis_server_db0_keys | gauge | count | Total key count. Deprecated. |\n| redis_server_evicted_keys | gauge | count | Keys evicted so far (since restart) |\n| <span class=\"break-all\">redis_server_expire_cycle_cpu_milliseconds</span> | gauge | milliseconds | The cumulative amount of time spent on active expiry cycles |\n| redis_server_expired_keys | gauge | count | Keys expired so far since restart |\n| redis_server_forwarding_state | gauge | — | Shard forwarding state (1 = on, 0 = off) |\n| redis_server_hashes_items_under_1M | gauge | count | Number of hash keys with under 1 million elements |\n| redis_server_hashes_items_1M_to_8M | gauge | count | Number of hash keys with an element count between 1 million and 8 million |\n| redis_server_hashes_items_over_8M | gauge | count | Number of hash keys with over 8 million elements |\n| redis_server_keys_trimmed | gauge | count | The number of keys that were trimmed in the current or last resharding process |\n| redis_server_keyspace_read_hits | gauge | count | Number of read operations accessing an existing keyspace |\n| redis_server_keyspace_read_misses | gauge | count | Number of read operations accessing a non-existing keyspace |\n| redis_server_keyspace_write_hits | gauge | count | Number of write operations accessing an existing keyspace |\n| <span class=\"break-all\">redis_server_keyspace_write_misses</span> | gauge | count | Number of write operations accessing a non-existing keyspace |\n| redis_server_lists_items_under_1M | gauge | count | Number of list keys with under 1 million elements |\n| redis_server_lists_items_1M_to_8M | gauge | count | Number of list keys with an element count between 1 million and 8 million |\n| redis_server_lists_items_over_8M | gauge | count | Number of list keys with over 8 million elements |\n| redis_server_master_link_status | gauge | — | Indicates whether the replica is connected to its master (1 = up, 2 = down, 3 = none, 99 = unknown) |\n| redis_server_master_repl_offset | gauge | bytes | Number of bytes sent to replicas by the shard; calculate the throughput for a time period by comparing the value at different times |\n| <span class=\"break-all\">redis_server_master_sync_in_progress</span> | gauge | — | The primary shard is synchronizing (1 true; 0 false) |\n| redis_server_max_process_mem | gauge | bytes | Current memory limit configured by redis_mgr according to node free memory |\n| redis_server_maxmemory | gauge | bytes | Current memory limit configured by redis_mgr according to database memory limits. <br /><br />To calculate the percent memory usage:<br /><span class=\"break-all\">`sum by (cluster,db)(redis_server_used_memory{role=\"master\"}) / (avg by(cluster,db)(db_memory_limit_bytes) / max by(cluster,db)(db_replication_factor))`</span> |\n| redis_server_mem_aof_buffer | gauge | bytes | Current size of AOF buffer |\n| redis_server_mem_clients_normal | gauge | bytes | Current memory used for input and output buffers of non-replica clients |\n| redis_server_mem_clients_slaves | gauge | bytes | Current memory used for input and output buffers of replica clients |\n| <span class=\"break-all\">redis_server_mem_fragmentation_ratio</span> | gauge | ratio | Memory fragmentation ratio (1.3 means 30% overhead) |\n| <span class=\"break-all\">redis_server_mem_not_counted_for_evict</span> | gauge | bytes | Portion of used_memory (in bytes) that's not counted for eviction and OOM error |\n| <span class=\"break-all\">redis_server_mem_replication_backlog</span> | gauge | bytes | Size of replication backlog |\n| <span class=\"break-all\">redis_server_slave_buffer</span> | gauge | bytes | Reports the effective replica output buffer hard limit for a shard, derived from the database slave_buffer configuration. For slave_buffer=auto, the hard limit is calculated as used_memory * auto_slavebuf_ratio / 100, then bounded by auto_slavebuf_min and auto_slavebuf_max. |\n| <span class=\"break-all\">redis_server_module_fork_in_progress</span> | gauge | — | A binary value that indicates if there is an active fork spawned by a module (1) or not (0) |\n| <span class=\"break-all\">namedprocess_namegroup_cpu_seconds_total</span> | counter | seconds | Shard process CPU usage in seconds |\n| <span class=\"break-all\">namedprocess_namegroup_thread_cpu_seconds_total</span> | counter | seconds | Shard main thread CPU time spent in seconds |\n| <span class=\"break-all\">namedprocess_namegroup_open_filedesc</span> | gauge | count | Shard number of open file descriptors |\n| <span class=\"break-all\">namedprocess_namegroup_memory_bytes</span> | gauge | bytes | Shard memory size in bytes |\n| <span class=\"break-all\">namedprocess_namegroup_oldest_start_time_seconds</span> | gauge | timestamp seconds | Shard start time of the process since unix epoch in seconds |\n| <span class=\"break-all\">redis_server_rdb_bgsave_in_progress</span> | gauge | — | Indicates whether bgsave is currently in progress (1 = in progress, 0 = otherwise) |\n| redis_server_rdb_last_cow_size | gauge | bytes | Last bgsave (or SYNC fork) used CopyOnWrite memory |\n| redis_server_rdb_saves | gauge | count | Total count of bgsaves since the process was restarted (including replica fullsync and persistence) |\n| redis_server_sets_items_under_1M | gauge | count | Number of set keys with under 1 million elements |\n| redis_server_sets_items_1M_to_8M | gauge | count | Number of set keys with an element count between 1 million and 8 million |\n| redis_server_sets_items_over_8M | gauge | count | Number of set keys with over 8 million elements |\n| redis_server_repl_touch_bytes | gauge | bytes | Number of bytes sent to replicas as TOUCH commands by the shard as a result of a READ command that was processed; calculate the throughput for a time period by comparing the value at different times |\n| <span class=\"break-all\">redis_server_total_commands_processed</span> | gauge | count | Number of commands processed by the shard; calculate the number of commands for a time period by comparing the value at different times |\n| <span class=\"break-all\">redis_server_total_connections_received</span> | gauge | count | Number of connections received by the shard; calculate the number of connections for a time period by comparing the value at different times |\n| redis_server_total_net_input_bytes | gauge | bytes | Number of bytes received by the shard; calculate the throughput for a time period by comparing the value at different times |\n| <span class=\"break-all\">redis_server_total_net_output_bytes</span> | gauge | bytes | Number of bytes sent by the shard; calculate the throughput for a time period by comparing the value at different times |\n| redis_server_up | gauge | — | Shard is up and running (1 = up, 0 = down) |\n| <span class=\"break-all\">redis_server_strings_sizes_under_128M</span> | gauge | count | Number of string keys with a memory size under 128 megabytes |\n| <span class=\"break-all\">redis_server_strings_sizes_128M_to_512M</span> | gauge | count | Number of string keys with a memory size between 128 and 512 megabytes |\n| <span class=\"break-all\">redis_server_strings_sizes_over_512M</span> | gauge | count | Number of string keys with a memory size over 512 megabytes |\n| redis_server_used_memory | gauge | bytes | Memory used by shard (in BigRedis this includes flash) (bytes) |\n| redis_server_zsets_items_under_1M | gauge | count | Number of sorted set keys with under 1 million elements |\n| redis_server_zsets_items_1M_to_8M | gauge | count | Number of sorted set keys with an element count between 1 million and 8 million |\n| redis_server_zsets_items_over_8M | gauge | count | Number of sorted set keys with over 8 million elements |\n| <span class=\"break-all\">redis_server_search_gc_bytes_collected</span> | gauge | bytes | The total amount of memory freed by the garbage collectors from indexes in the shard's memory in bytes. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_bytes_collected</span> | gauge | bytes | The total amount of memory freed by the garbage collectors from indexes in the shard's memory in bytes. Deprecated in 8.0 (renamed <span class=\"break-all\">redis_server_search_gc_bytes_collected</span>), but still available in older versions. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_gc_marked_deleted_vectors</span> | gauge | count | The number of vectors marked as deleted in the vector indexes that have not yet been cleaned. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_marked_deleted_vectors</span> | gauge | count | The number of vectors marked as deleted in the vector indexes that have not yet been cleaned. Deprecated in 8.0 (renamed <span class=\"break-all\">redis_server_search_gc_marked_deleted_vectors</span>), but still available in older versions. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_gc_total_cycles</span> | gauge | count | The total number of garbage collection cycles executed. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_total_cycles</span> | gauge | count | The total number of garbage collection cycles executed. Deprecated in 8.0 (renamed <span class=\"break-all\">redis_server_search_gc_total_cycles</span>), but still available in older versions. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_gc_total_docs_not_collected_by_gc</span> | gauge | count | The number of documents marked as deleted, whose memory has not yet been freed by the garbage collector. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_total_docs_not_collected_by_gc</span> | gauge | count | The number of documents marked as deleted, whose memory has not yet been freed by the garbage collector. Deprecated in 8.0 (renamed <span class=\"break-all\">redis_server_search_gc_total_docs_not_collected_by_gc</span>), but still available in older versions. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_gc_total_ms_run</span> | gauge | milliseconds | The total duration of all garbage collection cycles in the shard, measured in milliseconds. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_total_ms_run</span> | gauge | milliseconds | The total duration of all garbage collection cycles in the shard, measured in milliseconds. Deprecated in 8.0 (renamed <span class=\"break-all\">redis_server_search_gc_total_ms_run</span>), but still available in older versions. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_cursors_internal_idle</span> | gauge | count | The total number of coordinator cursors that are currently holding pending results in the shard. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_cursors_user_idle</span> | gauge | count | The total number of cursors that were explicitly requested by users, that are currently holding pending results in the shard. <sup>[4](#tnote-4)</sup> |\n| redis_server_search_global_idle | gauge | count | The total number of user and internal cursors currently holding pending results in the shard. Deprecated in 8.0 (split into <span class=\"break-all\">redis_server_search_cursors_internal_idle</span> and <span class=\"break-all\">redis_server_search_cursors_user_idle</span>), but still available in older versions. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_cursors_internal_active</span> | gauge | count | The total number of coordinator cursors in the shard, either holding pending results or actively executing `FT.CURSOR READ`. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_cursors_user_active</span> | gauge | count | The total number of user cursors in the shard, either holding pending results or actively executing `FT.CURSOR READ`. <sup>[4](#tnote-4)</sup> |\n| redis_server_search_global_total | gauge | count | The total number of user and internal cursors in the shard, either holding pending results or actively executing `FT.CURSOR READ`. Deprecated in 8.0 (split into <span class=\"break-all\">redis_server_search_cursors_internal_active</span> and <span class=\"break-all\">redis_server_search_cursors_user_active</span>), but still available in older versions. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_number_of_indexes</span> | gauge | count | Total number of indexes in the shard <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_number_of_active_indexes</span> | gauge | count | The total number of indexes running a background indexing and/or background query processing operation. Background indexing refers to vector ingestion process, or in-progress background indexer. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_total_num_docs_in_indexes</span> | gauge | count | The total number of documents currently indexed across all indexes in the shard. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_number_of_active_indexes_running_queries</span> | gauge | count | Total count of indexes currently running a background query process. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_number_of_active_indexes_indexing</span> | gauge | count | Total count of indexes currently undergoing a background indexing process. Background indexing refers to vector ingestion process, or in-progress background indexer. This metric is limited by the number of WORKER threads allocated for writing operations + the number of indexes. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_total_active_write_threads</span> | gauge | count | Total count of background write (indexing) processes currently running in the shard. Background indexing refers to vector ingestion process, or in-progress background indexer. This metric is limited by the number of threads allocated for writing operations. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_text_Text</span> | gauge | count | The total number of `TEXT` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_text_Sortable</span> | gauge | count | The total number of `SORTABLE TEXT` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_text_NoIndex</span> | gauge | count | The total number of `NOINDEX TEXT` fields across all indexes in the shard; i.e., used for sorting only but not indexed. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_text_IndexErrors</span> | gauge | count | The total number of indexing failures caused by attempts to index a document containing a `TEXT` field. This field appears only if `TEXT` fields exist. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_numeric_Numeric</span> | gauge | count | The total number of `NUMERIC` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_numeric_Sortable</span> | gauge | count | The total number of `SORTABLE NUMERIC` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_numeric_NoIndex</span> | gauge | count | The total number of `NOINDEX NUMERIC` fields across all indexes in the shard, which are used for sorting only but not indexed. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_numeric_IndexErrors</span> | gauge | count | The total number of indexing failures caused by attempts to index a document containing a `NUMERIC` field. This field appears only if `NUMERIC` fields exist. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_tag_Tag</span> | gauge | count | The total number of `TAG` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_tag_Sortable</span> | gauge | count | The total number of `SORTABLE TAG` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_tag_NoIndex</span> | gauge | count | The total number of `NOINDEX TAG` fields across all indexes in the shard; i.e., used for sorting only but not indexed. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_tag_CaseSensitive</span> | gauge | count | The total number of `CASESENSITIVE TAG` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_tag_IndexErrors</span> | gauge | count | The total number of indexing failures caused by attempts to index a document containing a `TAG` field. This field appears only if `TAG` fields exist. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_geo_Geo</span> | gauge | count | The total number of `GEO` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_geo_Sortable</span> | gauge | count | The total number of `SORTABLE GEO` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_geo_NoIndex</span> | gauge | count | The total number of `NOINDEX GEO` fields across all indexes in the shard; i.e., used for sorting only but not indexed. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_geo_IndexErrors</span> | gauge | count | The total number of indexing failures caused by attempts to index a document containing a `GEO` field. This field appears only if `GEO` fields exist. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_vector_Vector</span> | gauge | count | The total number of `VECTOR` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_vector_Flat</span> | gauge | count | The total number of `FLAT VECTOR` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_vector_HNSW</span> | gauge | count | The total number of `HNSW VECTOR` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_vector_SVS_VAMANA</span> | gauge | count | The total number of `SVS-VAMANA VECTOR` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_vector_SVS_VAMANA_Compressed</span> | gauge | count | The total number of `SVS-VAMANA VECTOR` fields with `COMPRESSION` enabled (e.g., `LVQ8`) across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_vector_IndexErrors</span> | gauge | count | The total number of indexing failures caused by attempts to index a document containing a `VECTOR` field. This field appears only if `VECTOR` fields exist. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_geoshape_Geoshape</span> | gauge | count | The total number of `GEOSHAPE` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_geoshape_Sortable</span> | gauge | count | The total number of `SORTABLE GEOSHAPE` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_geoshape_NoIndex</span> | gauge | count | The total number of `NOINDEX GEOSHAPE` fields across all indexes in the shard; i.e., used for sorting only but not indexed. This field appears only if its value is larger than 0. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_geoshape_IndexErrors</span> | gauge | count | The total number of indexing failures caused by attempts to index a document containing a `GEOSHAPE` field. This field appears only if `GEOSHAPE` fields exist. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_total_indexing_ops_text_fields</span> | gauge | count | The total number of indexing operations performed on `TEXT` fields across all indexes in the shard. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_total_indexing_ops_tag_fields</span> | gauge | count | The total number of indexing operations performed on `TAG` fields across all indexes in the shard. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_total_indexing_ops_numeric_fields</span> | gauge | count | The total number of indexing operations performed on `NUMERIC` fields across all indexes in the shard. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_total_indexing_ops_geo_fields</span> | gauge | count | The total number of indexing operations performed on `GEO` fields across all indexes in the shard. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_total_indexing_ops_geoshape_fields</span> | gauge | count | The total number of indexing operations performed on `GEOSHAPE` fields across all indexes in the shard. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_total_indexing_ops_vector_fields</span> | gauge | count | The total number of indexing operations performed on `VECTOR` fields across all indexes in the shard. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_used_memory_indexes</span> | gauge | bytes | The total memory allocated by all indexes in the shard in bytes. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_smallest_memory_index</span> | gauge | bytes | The memory usage of the index with the smallest memory usage in the shard in bytes. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_largest_memory_index</span> | gauge | bytes | The memory usage of the index with the largest memory usage in the shard in bytes. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_total_indexing_time</span> | gauge | milliseconds | The total time spent on indexing operations, excluding the background indexing of vectors in the `HNSW` graph. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_used_memory_vector_index</span> | gauge | bytes | The total memory usage of all vector indexes in the shard. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_total_queries_processed</span> | gauge | count | The total number of successful query executions (When using cursors, not counting reading from existing cursors) in the shard. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_total_query_commands</span> | gauge | count | The total number of successful query command executions (including `FT.SEARCH`, `FT.AGGREGATE`, and `FT.CURSOR READ`). <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_total_query_execution_time_ms</span> | gauge | milliseconds | The cumulative execution time of all query commands, including `FT.SEARCH`, `FT.AGGREGATE`, and `FT.CURSOR READ`, measured in ms. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_total_active_queries</span> | gauge | count | The total number of background queries currently being executed in the shard, excluding `FT.CURSOR READ`. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_total_coord_dispatch_time_ms</span> | gauge | milliseconds | The cumulative time the coordinator spent building and dispatching query commands (`FT.SEARCH`, `FT.AGGREGATE`, and `FT.HYBRID`) to the shards, measured in ms. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_errors_indexing_failures</span> | gauge | count | The total number of indexing failures recorded across all indexes in the shard. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_errors_for_index_with_max_failures</span> | gauge | count | The number of indexing failures in the index with the highest count of failures. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_OOM_indexing_failures_indexes_count</span> | gauge | count | The count of indexes that experienced out-of-memory (OOM) failures during background indexing. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_shard_total_query_errors_syntax</span> | gauge | count | The total number of query syntax errors occurred in the shard. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_shard_total_query_errors_arguments</span> | gauge | count | The total number of queries in the shard that failed due to missing or invalid arguments. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_shard_total_query_errors_timeout</span> | gauge | count | The total number of query timeout errors occurred in the shard (when timeout policy is 'fail'). <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_shard_total_query_warnings_timeout</span> | gauge | count | The total number of query timeout warnings occurred in the shard (when timeout policy is 'return partial results'). <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_shard_total_query_errors_oom</span> | gauge | count | The total number of query out-of-memory errors occurred in the shard. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_shard_total_query_warnings_oom</span> | gauge | count | The total number of query out-of-memory warnings occurred in the shard. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_shard_total_query_warnings_max_prefix_expansions</span> | gauge | count | The total number of max prefix expansion warnings occurred in the shard. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_coord_total_query_errors_syntax</span> | gauge | count | The total number of query syntax errors occurred at the coordinator. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_coord_total_query_errors_arguments</span> | gauge | count | The total number of query argument errors encountered by the shard's coordinator. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_coord_total_query_errors_timeout</span> | gauge | count | The total number of query timeout errors encountered by the shard's coordinator (when timeout policy is 'fail'). <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_coord_total_query_warnings_timeout</span> | gauge | count | The total number of query timeout warnings encountered by the shard's coordinator (when timeout policy is 'return partial results'). <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_coord_total_query_errors_oom</span> | gauge | count | The total number of query out-of-memory errors encountered by the shard's coordinator. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_coord_total_query_warnings_oom</span> | gauge | count | The total number of query out-of-memory warnings encountered by the shard's coordinator. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_coord_total_query_warnings_max_prefix_expansions</span> | gauge | count | The total number of max prefix expansion warnings encountered by the shard's coordinator. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_uv_threads_running_queries</span> | gauge | count | The number of I/O threads currently handling query distribution to shards in cluster environments. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_uv_threads_running_topology_update</span> | gauge | count | The number of UV threads currently running topology updates. <sup>[3](#tnote-3)</sup> |\n| <span class=\"break-all\">redis_server_search_active_worker_threads</span> | gauge | count | The number of active worker threads. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_active_coord_threads</span> | gauge | count | The number of active coordinator threads. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_workers_low_priority_pending_jobs</span> | gauge | count | The number of pending low-priority jobs in worker threads, such as vector background indexing, graph updates, and vector garbage collection in graph-based indexes. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_workers_high_priority_pending_jobs</span> | gauge | count | The number of pending high-priority jobs in worker threads, such as query execution. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_workers_admin_priority_pending_jobs</span> | gauge | count | The number of pending admin-priority jobs in worker threads, such as threadpool configuration changes. <sup>[3](#tnote-3)</sup> |\n| <span class=\"break-all\">redis_server_search_coord_high_priority_pending_jobs</span> | gauge | count | The number of pending jobs in the coordinator thread queue. Coordinator threads only have a high-priority queue and are primarily used for query distribution. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">shard_ping_failures</span> | gauge | count | Number of consecutive ping failures for a shard. Labels: shard_uid, role |\n| <span class=\"break-all\">shard_ping_failure_duration_seconds</span> | gauge | seconds | Duration of ongoing failures (0 when healthy). Labels: shard_uid, role |\n\n1. <a name=\"tnote-1\"></a> Available since RediSearch 2.6.\n2. <a name=\"tnote-2\"></a> Available since RediSearch 2.8.\n3. <a name=\"tnote-3\"></a> Available since RediSearch 2.10.\n4. <a name=\"tnote-4\"></a> Available since RediSearch 8.0."}],"examples":[]}
{"schema_version":2,"id":"integrate/prometheus-with-redis-enterprise/prometheus-metrics-v1","title":"Prometheus metrics v1","url":"https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise/prometheus-metrics-v1/","summary":"V1 metrics available to Prometheus.","tags":["docs","integrate","rs"],"last_updated":"2026-09-17T14:54:30+01:00","page_type":"content","content_hash":"5b57852d4801476927da904495f77b35a4051527bcda00c1cc1e57d0780bcb5c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can [integrate Redis Software with Prometheus and Grafana](https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise) to create dashboards for important metrics.\n\nAs of Redis Software version 7.8.2, v1 metrics are deprecated but still available. For help transitioning from v1 metrics to v2 PromQL, see [Prometheus v1 metrics and equivalent v2 PromQL](https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise/prometheus-metrics-v1-to-v2).\n\n\nThe following tables include the v1 metrics available to Prometheus."},{"id":"database-metrics","title":"Database metrics","role":"content","text":"| Metric | Type | Unit | Description |\n| :-------- | :--- | :--- | :---------- |\n| bdb_avg_latency | gauge | seconds | Average latency of operations on the database (seconds); returned only when there is traffic |\n| bdb_avg_latency_max | gauge | seconds | Highest value of average latency of operations on the database (seconds); returned only when there is traffic |\n| bdb_avg_read_latency | gauge | seconds | Average latency of read operations (seconds); returned only when there is traffic |\n| bdb_avg_read_latency_max | gauge | seconds | Highest value of average latency of read operations (seconds); returned only when there is traffic |\n| bdb_avg_write_latency | gauge | seconds | Average latency of write operations (seconds); returned only when there is traffic |\n| bdb_avg_write_latency_max | gauge | seconds | Highest value of average latency of write operations (seconds); returned only when there is traffic |\n| bdb_bigstore_shard_count | gauge | count | Shard count by database and by storage engine (driver - rocksdb / speedb); Only for databases with Auto Tiering enabled |\n| bdb_conns | gauge | count | Number of client connections to the database |\n| bdb_egress_bytes | gauge | bytes/second | Rate of outgoing network traffic from the database (bytes/sec) |\n| bdb_egress_bytes_max | gauge | bytes/second | Highest value of the rate of outgoing network traffic from the database (bytes/sec) |\n| bdb_evicted_objects | gauge | evictions/second | Rate of key evictions from database (evictions/sec) |\n| bdb_evicted_objects_max | gauge | evictions/second | Highest value of the rate of key evictions from database (evictions/sec) |\n| bdb_expired_objects | gauge | expirations/second | Rate of keys expired in database (expirations/sec) |\n| bdb_expired_objects_max | gauge | expirations/second | Highest value of the rate of keys expired in database (expirations/sec) |\n| bdb_fork_cpu_system | gauge | ratio | Cores utilization in system mode for all Redis shard fork child processes of this database |\n| bdb_fork_cpu_system_max | gauge | ratio | Highest value of cores utilization in system mode for all Redis shard fork child processes of this database |\n| bdb_fork_cpu_user | gauge | ratio | Cores utilization in user mode for all Redis shard fork child processes of this database |\n| bdb_fork_cpu_user_max | gauge | ratio | Highest value of cores utilization in user mode for all Redis shard fork child processes of this database |\n| bdb_ingress_bytes | gauge | bytes/second | Rate of incoming network traffic to the database (bytes/sec) |\n| bdb_ingress_bytes_max | gauge | bytes/second | Highest value of the rate of incoming network traffic to the database (bytes/sec) |\n| bdb_instantaneous_ops_per_sec | gauge | operations/second | Request rate handled by all shards of database (ops/sec) |\n| bdb_main_thread_cpu_system | gauge | ratio | Cores utilization in system mode for all Redis shard main threads of this database |\n| bdb_main_thread_cpu_system_max | gauge | ratio | Highest value of cores utilization in system mode for all Redis shard main threads of this database |\n| bdb_main_thread_cpu_user | gauge | ratio | Cores utilization in user mode for all Redis shard main threads of this database |\n| bdb_main_thread_cpu_user_max | gauge | ratio | Highest value of cores utilization in user mode for all Redis shard main threads of this database |\n| bdb_mem_frag_ratio | gauge | ratio | RAM fragmentation ratio (RSS / allocated RAM) |\n| bdb_mem_size_lua | gauge | bytes | Redis lua scripting heap size (bytes) |\n| bdb_memory_limit | gauge | bytes | Configured RAM limit for the database |\n| bdb_monitor_sessions_count | gauge | count | Number of clients connected in monitor mode to the database |\n| bdb_no_of_keys | gauge | count | Number of keys in database |\n| bdb_other_req | gauge | operations/second | Rate of other (non read/write) requests on the database (ops/sec) |\n| bdb_other_req_max | gauge | operations/second | Highest value of the rate of other (non read/write) requests on the database (ops/sec) |\n| bdb_other_res | gauge | operations/second | Rate of other (non read/write) responses on the database (ops/sec) |\n| bdb_other_res_max | gauge | operations/second | Highest value of the rate of other (non read/write) responses on the database (ops/sec) |\n| bdb_pubsub_channels | gauge | count | Count the pub/sub channels with subscribed clients |\n| bdb_pubsub_channels_max | gauge | count | Highest value of count the pub/sub channels with subscribed clients |\n| bdb_pubsub_patterns | gauge | count | Count the pub/sub patterns with subscribed clients |\n| bdb_pubsub_patterns_max | gauge | count | Highest value of count the pub/sub patterns with subscribed clients |\n| bdb_read_hits | gauge | operations/second | Rate of read operations accessing an existing key (ops/sec) |\n| bdb_read_hits_max | gauge | operations/second | Highest value of the rate of read operations accessing an existing key (ops/sec) |\n| bdb_read_misses | gauge | operations/second | Rate of read operations accessing a non-existing key (ops/sec) |\n| bdb_read_misses_max | gauge | operations/second | Highest value of the rate of read operations accessing a non-existing key (ops/sec) |\n| bdb_read_req | gauge | operations/second | Rate of read requests on the database (ops/sec) |\n| bdb_read_req_max | gauge | operations/second | Highest value of the rate of read requests on the database (ops/sec) |\n| bdb_read_res | gauge | operations/second | Rate of read responses on the database (ops/sec) |\n| bdb_read_res_max | gauge | operations/second | Highest value of the rate of read responses on the database (ops/sec) |\n| bdb_shard_cpu_system | gauge | ratio | Cores utilization in system mode for all Redis shard processes of this database |\n| bdb_shard_cpu_system_max | gauge | ratio | Highest value of cores utilization in system mode for all Redis shard processes of this database |\n| bdb_shard_cpu_user | gauge | ratio | Cores utilization in user mode for the Redis shard process |\n| bdb_shard_cpu_user_max | gauge | ratio | Highest value of cores utilization in user mode for the Redis shard process |\n| bdb_shards_used | gauge | count | Used shard count by database and by shard type (ram / flash) |\n| bdb_total_connections_received | gauge | connections/second | Rate of new client connections to the database (connections/sec) |\n| bdb_total_connections_received_max | gauge | connections/second | Highest value of the rate of new client connections to the database (connections/sec) |\n| bdb_total_req | gauge | operations/second | Rate of all requests on the database (ops/sec) |\n| bdb_total_req_max | gauge | operations/second | Highest value of the rate of all requests on the database (ops/sec) |\n| bdb_total_res | gauge | operations/second | Rate of all responses on the database (ops/sec) |\n| bdb_total_res_max | gauge | operations/second | Highest value of the rate of all responses on the database (ops/sec) |\n| bdb_up | gauge | — | Database is up and running |\n| bdb_used_memory | gauge | bytes | Memory used by the database (in BigRedis this includes flash) (bytes) |\n| bdb_write_hits | gauge | operations/second | Rate of write operations accessing an existing key (ops/sec) |\n| bdb_write_hits_max | gauge | operations/second | Highest value of the rate of write operations accessing an existing key (ops/sec) |\n| bdb_write_misses | gauge | operations/second | Rate of write operations accessing a non-existing key (ops/sec) |\n| bdb_write_misses_max | gauge | operations/second | Highest value of the rate of write operations accessing a non-existing key (ops/sec) |\n| bdb_write_req | gauge | operations/second | Rate of write requests on the database (ops/sec) |\n| bdb_write_req_max | gauge | operations/second | Highest value of the rate of write requests on the database (ops/sec) |\n| bdb_write_res | gauge | operations/second | Rate of write responses on the database (ops/sec) |\n| bdb_write_res_max | gauge | operations/second | Highest value of the rate of write responses on the database (ops/sec) |\n| bdb_no_of_expires | gauge | count | Current number of volatile keys in the database |"},{"id":"node-metrics","title":"Node metrics","role":"content","text":"| Metric | Type | Unit | Description |\n| :-------- | :--- | :--- | :---------- |\n| node_available_flash | gauge | bytes | Available flash in the node (bytes) |\n| node_available_flash_no_overbooking | gauge | bytes | Available flash in the node (bytes), without taking into account overbooking |\n| node_available_memory | gauge | bytes | Amount of free memory in the node (bytes) that is available for database provisioning |\n| node_available_memory_no_overbooking | gauge | bytes | Available ram in the node (bytes) without taking into account overbooking |\n| node_avg_latency | gauge | microseconds | Average latency of requests handled by endpoints on the node in microseconds; returned only when there is traffic |\n| node_bigstore_free | gauge | bytes | Sum of free space of back-end flash (used by flash database's BigRedis) on all cluster nodes (bytes); returned only when BigRedis is enabled |\n| node_bigstore_iops | gauge | operations/second | Rate of i/o operations against back-end flash for all shards which are part of a flash-based database (BigRedis) in the cluster (ops/sec); returned only when BigRedis is enabled |\n| node_bigstore_kv_ops | gauge | operations/second | Rate of value read/write operations against back-end flash for all shards which are part of a flash-based database (BigRedis) in the cluster (ops/sec); returned only when BigRedis is enabled |\n| node_bigstore_throughput | gauge | bytes/second | Throughput i/o operations against back-end flash for all shards which are part of a flash-based database (BigRedis) in the cluster (bytes/sec); returned only when BigRedis is enabled |\n| node_cert_expiration_seconds | gauge | seconds | Certificate expiration (in seconds) per given node; read more about [certificates in Redis Software](https://redis.io/docs/latest/operate/rs/security/certificates) and [monitoring certificates](https://redis.io/docs/latest/operate/rs/security/certificates/monitor-certificates) |\n| node_conns | gauge | count | Number of clients connected to endpoints on node |\n| node_cpu_idle | gauge | ratio | CPU idle time portion (0-1, multiply by 100 to get percent) |\n| node_cpu_idle_max | gauge | ratio | Highest value of CPU idle time portion (0-1, multiply by 100 to get percent) |\n| node_cpu_idle_median | gauge | ratio | Median value of CPU idle time portion (0-1, multiply by 100 to get percent) |\n| node_cpu_idle_min | gauge | ratio | Lowest value of CPU idle time portion (0-1, multiply by 100 to get percent) |\n| node_cpu_system | gauge | ratio | CPU time portion spent in the kernel (0-1, multiply by 100 to get percent) |\n| node_cpu_system_max | gauge | ratio | Highest value of CPU time portion spent in the kernel (0-1, multiply by 100 to get percent) |\n| node_cpu_system_median | gauge | ratio | Median value of CPU time portion spent in the kernel (0-1, multiply by 100 to get percent) |\n| node_cpu_system_min | gauge | ratio | Lowest value of CPU time portion spent in the kernel (0-1, multiply by 100 to get percent) |\n| node_cpu_user | gauge | ratio | CPU time portion spent by users-pace processes (0-1, multiply by 100 to get percent) |\n| node_cpu_user_max | gauge | ratio | Highest value of CPU time portion spent by users-pace processes (0-1, multiply by 100 to get percent) |\n| node_cpu_user_median | gauge | ratio | Median value of CPU time portion spent by users-pace processes (0-1, multiply by 100 to get percent) |\n| node_cpu_user_min | gauge | ratio | Lowest value of CPU time portion spent by users-pace processes (0-1, multiply by 100 to get percent) |\n| node_cur_aof_rewrites | gauge | count | Number of aof rewrites that are currently performed by shards on this node |\n| node_egress_bytes | gauge | bytes/second | Rate of outgoing network traffic to node (bytes/sec) |\n| node_egress_bytes_max | gauge | bytes/second | Highest value of the rate of outgoing network traffic to node (bytes/sec) |\n| node_egress_bytes_median | gauge | bytes/second | Median value of the rate of outgoing network traffic to node (bytes/sec) |\n| node_egress_bytes_min | gauge | bytes/second | Lowest value of the rate of outgoing network traffic to node (bytes/sec) |\n| node_ephemeral_storage_avail | gauge | bytes | Disk space available to RLEC processes on configured ephemeral disk (bytes) |\n| node_ephemeral_storage_free | gauge | bytes | Free disk space on configured ephemeral disk (bytes) |\n| node_free_memory | gauge | bytes | Free memory in the node (bytes) |\n| node_ingress_bytes | gauge | bytes/second | Rate of incoming network traffic to node (bytes/sec) |\n| node_ingress_bytes_max | gauge | bytes/second | Highest value of the rate of incoming network traffic to node (bytes/sec) |\n| node_ingress_bytes_median | gauge | bytes/second | Median value of the rate of incoming network traffic to node (bytes/sec) |\n| node_ingress_bytes_min | gauge | bytes/second | Lowest value of the rate of incoming network traffic to node (bytes/sec) |\n| node_persistent_storage_avail | gauge | bytes | Disk space available to RLEC processes on configured persistent disk (bytes) |\n| node_persistent_storage_free | gauge | bytes | Free disk space on configured persistent disk (bytes) |\n| node_provisional_flash | gauge | bytes | Amount of flash available for new shards on this node, taking into account overbooking, max Redis servers, reserved flash and provision and migration thresholds (bytes) |\n| node_provisional_flash_no_overbooking | gauge | bytes | Amount of flash available for new shards on this node, without taking into account overbooking, max Redis servers, reserved flash and provision and migration thresholds (bytes) |\n| node_provisional_memory | gauge | bytes | Amount of RAM that is available for provisioning to databases out of the total RAM allocated for databases |\n| node_provisional_memory_no_overbooking | gauge | bytes | Amount of RAM that is available for provisioning to databases out of the total RAM allocated for databases, without taking into account overbooking |\n| node_total_req | gauge | operations/second | Request rate handled by endpoints on node (ops/sec) |\n| node_up | gauge | — | Node is part of the cluster and is connected |"},{"id":"cluster-metrics","title":"Cluster metrics","role":"content","text":"| Metric | Type | Unit | Description |\n| :-------- | :--- | :--- | :---------- |\n| cluster_shards_limit | gauge | count | Total shard limit by the license by shard type (ram / flash) |"},{"id":"proxy-metrics","title":"Proxy metrics","role":"content","text":"| Metric | Type | Unit | Description |\n| :-------- | :--- | :--- | :---------- |\n| listener_acc_latency | counter | seconds | Accumulative latency (sum of the latencies) of all types of commands on the database. For the average latency, divide this value by listener_total_res |\n| listener_acc_latency_max | counter | seconds | Highest value of accumulative latency of all types of commands on the database |\n| listener_acc_other_latency | counter | seconds | Accumulative latency (sum of the latencies) of commands that are the type \"other\" on the database. For the average latency, divide this value by listener_other_res |\n| listener_acc_other_latency_max | counter | seconds | Highest value of accumulative latency of commands that are the type \"other\" on the database |\n| listener_acc_read_latency | counter | seconds | Accumulative latency (sum of the latencies) of commands that are the type \"read\" on the database. For the average latency, divide this value by listener_read_res |\n| listener_acc_read_latency_max | counter | seconds | Highest value of accumulative latency of commands that are the type \"read\" on the database |\n| listener_acc_write_latency | counter | seconds | Accumulative latency (sum of the latencies) of commands that are the type \"write\" on the database. For the average latency, divide this value by listener_write_res |\n| listener_acc_write_latency_max | counter | seconds | Highest value of accumulative latency of commands that are the type \"write\" on the database |\n| listener_auth_cmds | counter | count | Number of memcached AUTH commands sent to the database |\n| listener_auth_cmds_max | counter | count | Highest value of the number of memcached AUTH commands sent to the database |\n| listener_auth_errors | counter | count | Number of error responses to memcached AUTH commands |\n| listener_auth_errors_max | counter | count | Highest value of the number of error responses to memcached AUTH commands |\n| listener_cmd_flush | counter | count | Number of memcached FLUSH_ALL commands sent to the database |\n| listener_cmd_flush_max | counter | count | Highest value of the number of memcached FLUSH_ALL commands sent to the database |\n| listener_cmd_get | counter | count | Number of memcached GET commands sent to the database |\n| listener_cmd_get_max | counter | count | Highest value of the number of memcached GET commands sent to the database |\n| listener_cmd_set | counter | count | Number of memcached SET commands sent to the database |\n| listener_cmd_set_max | counter | count | Highest value of the number of memcached SET commands sent to the database |\n| listener_cmd_touch | counter | count | Number of memcached TOUCH commands sent to the database |\n| listener_cmd_touch_max | counter | count | Highest value of the number of memcached TOUCH commands sent to the database |\n| listener_conns | counter | count | Number of clients connected to the endpoint |\n| listener_egress_bytes | counter | bytes/second | Rate of outgoing network traffic to the endpoint (bytes/sec) |\n| listener_egress_bytes_max | counter | bytes/second | Highest value of the rate of outgoing network traffic to the endpoint (bytes/sec) |\n| listener_ingress_bytes | counter | bytes/second | Rate of incoming network traffic to the endpoint (bytes/sec) |\n| listener_ingress_bytes_max | counter | bytes/second | Highest value of the rate of incoming network traffic to the endpoint (bytes/sec) |\n| listener_last_req_time | counter | timestamp seconds | Time of last command sent to the database |\n| listener_last_res_time | counter | timestamp seconds | Time of last response sent from the database |\n| listener_max_connections_exceeded | counter | count | Number of times the Number of clients connected to the database at the same time has exceeded the max limit |\n| listener_max_connections_exceeded_max | counter | count | Highest value of the number of times the Number of clients connected to the database at the same time has exceeded the max limit |\n| listener_monitor_sessions_count | counter | count | Number of client connected in monitor mode to the endpoint |\n| listener_other_req | counter | operations/second | Rate of other (non read/write) requests on the endpoint (ops/sec) |\n| listener_other_req_max | counter | operations/second | Highest value of the rate of other (non read/write) requests on the endpoint (ops/sec) |\n| listener_other_res | counter | operations/second | Rate of other (non read/write) responses on the endpoint (ops/sec) |\n| listener_other_res_max | counter | operations/second | Highest value of the rate of other (non read/write) responses on the endpoint (ops/sec) |\n| listener_other_started_res | counter | count | Number of responses sent from the database of type \"other\" |\n| listener_other_started_res_max | counter | count | Highest value of the number of responses sent from the database of type \"other\" |\n| listener_read_req | counter | operations/second | Rate of read requests on the endpoint (ops/sec) |\n| listener_read_req_max | counter | operations/second | Highest value of the rate of read requests on the endpoint (ops/sec) |\n| listener_read_res | counter | operations/second | Rate of read responses on the endpoint (ops/sec) |\n| listener_read_res_max | counter | operations/second | Highest value of the rate of read responses on the endpoint (ops/sec) |\n| listener_read_started_res | counter | count | Number of responses sent from the database of type \"read\" |\n| listener_read_started_res_max | counter | count | Highest value of the number of responses sent from the database of type \"read\" |\n| listener_total_connections_received | counter | connections/second | Rate of new client connections to the endpoint (connections/sec) |\n| listener_total_connections_received_max | counter | connections/second | Highest value of the rate of new client connections to the endpoint (connections/sec) |\n| listener_total_req | counter | operations/second | Request rate handled by the endpoint (ops/sec) |\n| listener_total_req_max | counter | operations/second | Highest value of the rate of all requests on the endpoint (ops/sec) |\n| listener_total_res | counter | operations/second | Rate of all responses on the endpoint (ops/sec) |\n| listener_total_res_max | counter | operations/second | Highest value of the rate of all responses on the endpoint (ops/sec) |\n| listener_total_started_res | counter | count | Number of responses sent from the database of all types |\n| listener_total_started_res_max | counter | count | Highest value of the number of responses sent from the database of all types |\n| listener_write_req | counter | operations/second | Rate of write requests on the endpoint (ops/sec) |\n| listener_write_req_max | counter | operations/second | Highest value of the rate of write requests on the endpoint (ops/sec) |\n| listener_write_res | counter | operations/second | Rate of write responses on the endpoint (ops/sec) |\n| listener_write_res_max | counter | operations/second | Highest value of the rate of write responses on the endpoint (ops/sec) |\n| listener_write_started_res | counter | count | Number of responses sent from the database of type \"write\" |\n| listener_write_started_res_max | counter | count | Highest value of the number of responses sent from the database of type \"write\" |"},{"id":"replication-metrics","title":"Replication metrics","role":"content","text":"| Metric | Type | Unit | Description |\n| :-------- | :--- | :--- | :---------- |\n| bdb_replicaof_syncer_ingress_bytes | gauge | bytes/second | Rate of compressed incoming network traffic to a Replica Of database (bytes/sec) |\n| bdb_replicaof_syncer_ingress_bytes_decompressed | gauge | bytes/second | Rate of decompressed incoming network traffic to a Replica Of database (bytes/sec) |\n| bdb_replicaof_syncer_local_ingress_lag_time | gauge | milliseconds | Lag time between the source and the destination for Replica Of traffic (ms) |\n| bdb_replicaof_syncer_status | gauge | — | Syncer status for Replica Of traffic; 0 = in-sync, 1 = syncing, 2 = out of sync |\n| bdb_crdt_syncer_ingress_bytes | gauge | bytes/second | Rate of compressed incoming network traffic to CRDB (bytes/sec) |\n| bdb_crdt_syncer_ingress_bytes_decompressed | gauge | bytes/second | Rate of decompressed incoming network traffic to CRDB (bytes/sec) |\n| bdb_crdt_syncer_local_ingress_lag_time | gauge | milliseconds | Lag time between the source and the destination (ms) for CRDB traffic |\n| bdb_crdt_syncer_status | gauge | — | Syncer status for CRDB traffic; 0 = in-sync, 1 = syncing, 2 = out of sync |"},{"id":"shard-metrics","title":"Shard metrics","role":"content","text":"| Metric | Type | Unit | Description |\n| :-------- | :--- | :--- | :---------- |\n| redis_active_defrag_running | gauge | percent | Automatic memory defragmentation current aggressiveness (% cpu) |\n| redis_allocator_active | gauge | bytes | Total used memory, including external fragmentation |\n| redis_allocator_allocated | gauge | bytes | Total allocated memory |\n| redis_allocator_resident | gauge | bytes | Total resident memory (RSS) |\n| redis_aof_last_cow_size | gauge | bytes | Last AOFR, CopyOnWrite memory |\n| redis_aof_rewrite_in_progress | gauge | — | Indicates whether an AOF rewrite is in progress (1 = in progress, 0 = otherwise) |\n| redis_aof_rewrites | gauge | count | Number of AOF rewrites this process executed |\n| redis_aof_delayed_fsync | gauge | count | Number of times an AOF fsync caused delays in the Redis main thread (inducing latency); this can indicate that the disk is slow or overloaded |\n| redis_blocked_clients | gauge | count | Count the clients waiting on a blocking call |\n| redis_connected_clients | gauge | count | Number of client connections to the specific shard |\n| redis_connected_slaves | gauge | count | Number of connected replicas |\n| redis_db0_avg_ttl | gauge | milliseconds | Average TTL of all volatile keys. Deprecated. |\n| redis_db0_expires | gauge | count | Total count of volatile keys. Deprecated. |\n| redis_db0_keys | gauge | count | Total key count. Deprecated. |\n| redis_evicted_keys | gauge | count | Keys evicted so far (since restart) |\n| redis_expire_cycle_cpu_milliseconds | gauge | milliseconds | The cumulative amount of time spent on active expiry cycles |\n| redis_expired_keys | gauge | count | Keys expired so far (since restart) |\n| redis_forwarding_state | gauge | — | Shard forwarding state (1 = on, 0 = off) |\n| redis_keys_trimmed | gauge | count | The number of keys that were trimmed in the current or last resharding process |\n| redis_keyspace_read_hits | gauge | count | Number of read operations accessing an existing keyspace |\n| redis_keyspace_read_misses | gauge | count | Number of read operations accessing a non-existing keyspace |\n| redis_keyspace_write_hits | gauge | count | Number of write operations accessing an existing keyspace |\n| redis_keyspace_write_misses | gauge | count | Number of write operations accessing a non-existing keyspace |\n| redis_master_link_status | gauge | — | Indicates if the replica is connected to its master (1 = up, 2 = down, 3 = none, 99 = unknown) |\n| redis_master_repl_offset | gauge | bytes | Number of bytes sent to replicas by the shard; calculate the throughput for a time period by comparing the value at different times |\n| redis_master_sync_in_progress | gauge | — | The primary shard is synchronizing (1 true; 0 false) |\n| redis_max_process_mem | gauge | bytes | Current memory limit configured by redis_mgr according to node free memory |\n| redis_maxmemory | gauge | bytes | Current memory limit configured by redis_mgr according to database memory limits |\n| redis_mem_aof_buffer | gauge | bytes | Current size of AOF buffer |\n| redis_mem_clients_normal | gauge | bytes | Current memory used for input and output buffers of non-replica clients |\n| redis_mem_clients_slaves | gauge | bytes | Current memory used for input and output buffers of replica clients |\n| redis_mem_fragmentation_ratio | gauge | ratio | Memory fragmentation ratio (1.3 means 30% overhead) |\n| redis_mem_not_counted_for_evict | gauge | bytes | Portion of used_memory (in bytes) that's not counted for eviction and OOM error |\n| redis_mem_replication_backlog | gauge | bytes | Size of replication backlog |\n| redis_module_fork_in_progress | gauge | — | A binary value that indicates if there is an active fork spawned by a module (1) or not (0) |\n| redis_process_cpu_system_seconds_total | counter | seconds | Shard process system CPU time spent in seconds |\n| redis_process_cpu_usage_percent | counter | percent | Shard process cpu usage precentage |\n| redis_process_cpu_user_seconds_total | counter | seconds | Shard user CPU time spent in seconds |\n| redis_process_main_thread_cpu_system_seconds_total | counter | seconds | Shard main thread system CPU time spent in seconds |\n| redis_process_main_thread_cpu_user_seconds_total | counter | seconds | Shard main thread user CPU time spent in seconds |\n| redis_process_max_fds | gauge | count | Shard maximum number of open file descriptors |\n| redis_process_open_fds | gauge | count | Shard number of open file descriptors |\n| redis_process_resident_memory_bytes | gauge | bytes | Shard resident memory size in bytes |\n| redis_process_start_time_seconds | gauge | timestamp seconds | Shard start time of the process since unix epoch in seconds |\n| redis_process_virtual_memory_bytes | gauge | bytes | Shard virtual memory in bytes |\n| redis_rdb_bgsave_in_progress | gauge | — | Indicates whether bgsave is currently in progress (1 = in progress, 0 = otherwise) |\n| redis_rdb_last_cow_size | gauge | bytes | Last bgsave (or SYNC fork) used CopyOnWrite memory |\n| redis_rdb_saves | gauge | count | Total count of bgsaves since process was restarted (including replica fullsync and persistence) |\n| redis_repl_touch_bytes | gauge | bytes | Number of bytes sent to replicas as TOUCH commands by the shard as a result of a READ command that was processed; calculate the throughput for a time period by comparing the value at different times |\n| redis_total_commands_processed | gauge | count | Number of commands processed by the shard; calculate the number of commands for a time period by comparing the value at different times |\n| redis_total_connections_received | gauge | count | Number of connections received by the shard; calculate the number of connections for a time period by comparing the value at different times |\n| redis_total_net_input_bytes | gauge | bytes | Number of bytes received by the shard; calculate the throughput for a time period by comparing the value at different times |\n| redis_total_net_output_bytes | gauge | bytes | Number of bytes sent by the shard; calculate the throughput for a time period by comparing the value at different times |\n| redis_up | gauge | — | Shard is up and running. The value is always 1; the metric is absent while the shard is down. |\n| redis_used_memory | gauge | bytes | Memory used by shard (in BigRedis this includes flash) (bytes) |"}],"examples":[]}
{"schema_version":2,"id":"integrate/prometheus-with-redis-enterprise/prometheus-metrics-v1-to-v2","title":"Transition from Prometheus v1 to Prometheus v2","url":"https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise/prometheus-metrics-v1-to-v2/","summary":"Transition from v1 metrics to v2 PromQL equivalents.","tags":["docs","integrate","rs"],"last_updated":"2026-09-17T14:54:30+01:00","page_type":"content","content_hash":"576efc9798d50f71fc12bd193802df74406d5e959a5b6fe40608d0fdacbdcb81","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can [integrate Redis Software with Prometheus and Grafana](https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise) to create dashboards for important metrics.\n\nAs of Redis Software version 7.8.2, [PromQL (Prometheus Query Language)](https://prometheus.io/docs/prometheus/latest/querying/basics/) metrics are available. V1 metrics are deprecated but still available.\n\nTo transition from v1 metrics to v2 metrics, you need to change the `metrics_path` in your Prometheus configuration file from `/` to `/v2` to use the new scraping endpoint.\n\nHere's an example of the updated scraping configuration in `prometheus.yml`:\n\n[code example]\n\n> [!NOTE]\n> **Use a single scrape target.** The v2 endpoint is cluster-wide. Every node aggregates metrics from all nodes and returns the same complete result, so one target is enough. If you list one target per node, Prometheus stores every series once per target and multiplies each `sum()`-based dashboard panel by the number of targets. This produces no error. Prometheus reports every target as up and Grafana renders normally. Use your cluster FQDN as the single target so metrics remain available if a node goes down.\n\nThe reason for a single target changed in v2. On v1, only the cluster master served the metrics endpoint and other nodes returned a redirect, so the protocol effectively forced one target. On v2, every node returns the full cluster view and no redirects are involved. If your v1 configuration listed multiple node targets, reduce it to one.\n\nIf you prefer a per-node scrape topology, scrape `/v2/node`, which returns only that node's own metrics. Aggregation adds the `cluster` and `node` labels, so `/v2/node` responses omit them. Add `relabel_configs` to supply those labels before using `/v2/node` with the Redis Software Grafana dashboards.\n\nIt is possible to scrape both v1 and v2 endpoints simultaneously during the transition period to prepare dashboards and ensure a smooth transition.\n\nYou can use the following tables to transition from v1 metrics to equivalent v2 PromQL. For a list of all available v2 PromQL metrics, see [Prometheus metrics v2](https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise/prometheus-metrics-definitions)."},{"id":"database-metrics","title":"Database metrics","role":"content","text":"| V1&nbsp;metric | Equivalent V2 PromQL | Description |\n| --------- | :------------------- | :---------- |\n| <span class=\"break-all\">bdb_avg_latency</span> | <span class=\"break-all\">`(sum by (db)(irate(endpoint_read_requests_latency_histogram_sum{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_write_requests_latency_histogram_sum{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_other_requests_latency_histogram_sum{db=\"\"$db\"\"}[$__rate_interval])))/(sum by (db)(irate(endpoint_read_requests{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_write_requests{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_other_requests{db=\"\"$db\"\"}[$__rate_interval])))/1000000`</span> | Average latency of operations on the database (seconds); returned only when there is traffic |\n| <span class=\"break-all\">bdb_avg_latency_max</span> | <span class=\"break-all\">`avg(histogram_quantile(1, sum by (le, db) (irate(endpoint_read_requests_latency_histogram_bucket{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_write_requests_latency_histogram_bucket{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_other_requests_latency_histogram_bucket{db=\"\"$db\"\"}[$__rate_interval])))) / 1000000`</span> | Highest value of average latency of operations on the database (seconds); returned only when there is traffic |\n| <span class=\"break-all\">bdb_avg_read_latency</span> | <span class=\"break-all\">`(sum(irate(endpoint_read_requests_latency_histogram_sum{db=\"\"$db\"\"}[$__rate_interval]))/sum(irate(endpoint_read_requests{db=\"\"$db\"\"}[$__rate_interval])))/1000000`</span> | Average latency of read operations (seconds); returned only when there is traffic |\n| <span class=\"break-all\">bdb_avg_read_latency_max</span></span> | <span class=\"break-all\">`histogram_quantile(1, sum by (le) (irate(endpoint_read_requests_latency_histogram_bucket{db=\"\"$db\"\"}[$__rate_interval]))) / 1000000`</span> | Highest value of average latency of read operations (seconds); returned only when there is traffic |\n| <span class=\"break-all\">bdb_avg_write_latency</span> | <span class=\"break-all\">`(sum(irate(endpoint_write_requests_latency_histogram_sum{db=\"\"$db\"\"}[$__rate_interval]))/sum(irate(endpoint_write_requests{db=\"\"$db\"\"}[$__rate_interval])))/1000000`</span> | Average latency of write operations (seconds); returned only when there is traffic |\n| <span class=\"break-all\">bdb_avg_write_latency_max</span> | <span class=\"break-all\">`histogram_quantile(1, sum by (le) (irate(endpoint_write_requests_latency_histogram_bucket{db=\"\"$db\"\"}[$__rate_interval]))) / 1000000`</span> | Highest value of average latency of write operations (seconds); returned only when there is traffic |\n| <span class=\"break-all\">bdb_bigstore_shard_count</span> | <span class=\"break-all\">`sum((sum(label_replace(label_replace(namedprocess_namegroup_thread_count{groupname=~\"redis-\\d+\", threadname=~\"(speedb\\|rocksdb).*\"}, \"redis\", \"$1\", \"groupname\", \"redis-(\\d+)\"), \"driver\", \"$1\", \"threadname\", \"(speedb\\|rocksdb).*\")) by (redis, driver) > bool 0) * on (redis) group_left(db) redis_server_up) by (db, driver)`</span> | Shard count by database and by storage engine (driver - rocksdb / speedb); Only for databases with Auto Tiering enabled |\n| <span class=\"break-all\">bdb_conns</span> | <span class=\"break-all\">`sum(endpoint_client_connections{cluster=\"$cluster\", db=\"$db\"} - endpoint_client_disconnections{cluster=\"$cluster\", db=\"$db\"} - endpoint_proxy_disconnections{cluster=\"$cluster\", db=\"$db\"})`</span> | Number of client connections to database |\n| <span class=\"break-all\">bdb_egress_bytes</span> | <span class=\"break-all\">`sum by(db) (irate(endpoint_egress{db=\"$db\"}[$__rate_interval]))`</span> | Rate of outgoing network traffic from the database (bytes/sec) |\n| <span class=\"break-all\">bdb_egress_bytes_max</span> | <span class=\"break-all\">`max_over_time (sum by(db) (irate(endpoint_egress{db=\"$db\"}[$__rate_interval]))[$__range:])`</span> | Highest value of the rate of outgoing network traffic from the database (bytes/sec) |\n| <span class=\"break-all\">bdb_evicted_objects</span> | <span class=\"break-all\">`sum by (db) (irate(redis_server_evicted_keys{role=\"master\"}[$__rate_interval]))`</span> | Rate of key evictions from database (evictions/sec) |\n| <span class=\"break-all\">bdb_evicted_objects_max</span> | <span class=\"break-all\">`sum by (db) (irate(redis_server_evicted_keys{role=\"master\"}[$__rate_interval]))`</span> | Highest value of the rate of key evictions from database (evictions/sec) |\n| <span class=\"break-all\">bdb_expired_objects</span> | <span class=\"break-all\">`sum by (db) (irate(redis_server_expired_keys{role=\"master\"}[$__rate_interval]))`</span> | Rate of keys expired in database (expirations/sec) |\n| <span class=\"break-all\">bdb_expired_objects_max</span> | <span class=\"break-all\">`sum by (db) (irate(redis_server_expired_keys{role=\"master\"}[$__rate_interval]))`</span> | Highest value of the rate of keys expired in database (expirations/sec) |\n| <span class=\"break-all\">bdb_fork_cpu_system</span> | <span class=\"break-all\">`sum by (db) (irate(namedprocess_namegroup_thread_cpu_seconds_total{mode=\"system\"}[$__rate_interval]))`</span> | Cores utilization in system mode for all Redis shard fork child processes of this database |\n| <span class=\"break-all\">bdb_fork_cpu_system_max</span> | <span class=\"break-all\">`sum by (db) (irate(namedprocess_namegroup_thread_cpu_seconds_total{mode=\"system\"}[$__rate_interval]))`</span> | Highest value of cores utilization in system mode for all Redis shard fork child processes of this database |\n| <span class=\"break-all\">bdb_fork_cpu_user</span> | <span class=\"break-all\">`sum by (db) (irate(namedprocess_namegroup_thread_cpu_seconds_total{mode=\"user\"}[$__rate_interval]))`</span> | Cores utilization in user mode for all Redis shard fork child processes of this database |\n| <span class=\"break-all\">bdb_fork_cpu_user_max</span> | <span class=\"break-all\">`sum by (db) (irate(namedprocess_namegroup_thread_cpu_seconds_total{mode=\"user\"}[$__rate_interval]))`</span> | Highest value of cores utilization in user mode for all Redis shard fork child processes of this database |\n| <span class=\"break-all\">bdb_ingress_bytes</span> | <span class=\"break-all\">`sum by(db) (irate(endpoint_ingress{db=\"$db\"}[$__rate_interval]))`</span> | Rate of incoming network traffic to database (bytes/sec) |\n| <span class=\"break-all\">bdb_ingress_bytes_max</span> | <span class=\"break-all\">`max_over_time (sum by(db) (irate(endpoint_ingress{db=\"$db\"}[$__rate_interval]))[$__range:])`</span> | Highest value of the rate of incoming network traffic to database (bytes/sec) |\n| <span class=\"break-all\">bdb_instantaneous_ops_per_sec</span></span> | <span class=\"break-all\">`sum by(db) (redis_server_instantaneous_ops_per_sec)`</span> | Request rate handled by all shards of database (ops/sec) |\n| <span class=\"break-all\">bdb_main_thread_cpu_system</span> | <span class=\"break-all\">`sum by(db) (irate(namedprocess_namegroup_thread_cpu_seconds_total{mode=\"system\", threadname=~\"redis-server.*\"}[$__rate_interval]))`</span> | Cores utilization in system mode for all Redis shard main threads of this database |\n| <span class=\"break-all\">bdb_main_thread_cpu_system_max</span></span> | <span class=\"break-all\">`sum by(db) (irate(namedprocess_namegroup_thread_cpu_seconds_total{mode=\"system\", threadname=~\"redis-server.*\"}[$__rate_interval]))`</span> | Highest value of cores utilization in system mode for all Redis shard main threads of this database |\n| <span class=\"break-all\">bdb_main_thread_cpu_user</span> | <span class=\"break-all\">`sum by(irate(namedprocess_namegroup_thread_cpu_seconds_total{mode=\"user\", threadname=~\"redis-server.*\"}[$__rate_interval]))`</span> | Cores utilization in user mode for all Redis shard main threads of this database |\n| <span class=\"break-all\">bdb_main_thread_cpu_user_max</span></span> | <span class=\"break-all\">`sum by(irate(namedprocess_namegroup_thread_cpu_seconds_total{mode=\"user\", threadname=~\"redis-server.*\"}[$__rate_interval]))`</span> | Highest value of cores utilization in user mode for all Redis shard main threads of this database |\n| <span class=\"break-all\">bdb_mem_frag_ratio</span> | <span class=\"break-all\">`avg(redis_server_mem_fragmentation_ratio)`</span> | RAM fragmentation ratio (RSS / allocated RAM) |\n| <span class=\"break-all\">bdb_mem_size_lua</span> | <span class=\"break-all\">`sum by(db) (redis_server_used_memory_lua)`</span> | Redis lua scripting heap size (bytes) |\n| <span class=\"break-all\">bdb_memory_limit</span> | <span class=\"break-all\">`db_memory_limit_bytes`</span> | Configured RAM limit for the database |\n| <span class=\"break-all\">bdb_monitor_sessions_count</span> | <span class=\"break-all\">`sum by(db) (endpoint_monitor_sessions_count)`</span> | Number of clients connected in monitor mode to the database |\n| <span class=\"break-all\">bdb_no_of_keys</span> | <span class=\"break-all\">`sum by (db) (redis_server_db_keys{role=\"master\"})`</span> | Number of keys in database |\n| <span class=\"break-all\">bdb_other_req</span> | <span class=\"break-all\">`sum by(db) (irate(endpoint_other_requests{db=\"$db\"}[$__rate_interval]))`</span> | Rate of other (non read/write) requests on the database (ops/sec) |\n| <span class=\"break-all\">bdb_other_req_max</span> | <span class=\"break-all\">`max_over_time (sum by(db) (irate(endpoint_other_requests{db=\"$db\"}[$__rate_interval]))[$__range:])`</span> | Highest value of the rate of other (non read/write) requests on the database (ops/sec) |\n| <span class=\"break-all\">bdb_other_res</span> | <span class=\"break-all\">`sum by(db) (irate(endpoint_other_responses{db=\"$db\"}[$__rate_interval]))`</span> | Rate of other (non read/write) responses on the database (ops/sec) |\n| <span class=\"break-all\">bdb_other_res_max</span> | <span class=\"break-all\">`max_over_time (sum by(db) (irate(endpoint_other_responses{db=\"$db\"}[$__rate_interval]))[$__range:])`</span> | Highest value of the rate of other (non read/write) responses on the database (ops/sec) |\n| <span class=\"break-all\">bdb_pubsub_channels</span> | <span class=\"break-all\">`sum by(db) (redis_server_pubsub_channels)`</span> | Count the pub/sub channels with subscribed clients |\n| <span class=\"break-all\">bdb_pubsub_channels_max</span> | <span class=\"break-all\">`sum by(db) (redis_server_pubsub_channels)`</span> | Highest value of count the pub/sub channels with subscribed clients |\n| <span class=\"break-all\">bdb_pubsub_patterns</span> | <span class=\"break-all\">`sum by(db) (redis_server_pubsub_patterns)`</span> | Count the pub/sub patterns with subscribed clients |\n| <span class=\"break-all\">bdb_pubsub_patterns_max</span></span> | <span class=\"break-all\">`sum by(db) (redis_server_pubsub_patterns)`</span> | Highest value of count the pub/sub patterns with subscribed clients |\n| <span class=\"break-all\">bdb_read_hits</span> | <span class=\"break-all\">`sum by (db) (irate(redis_server_keyspace_read_hits{role=\"master\"}[$__rate_interval]))`</span> | Rate of read operations accessing an existing key (ops/sec) |\n| <span class=\"break-all\">bdb_read_hits_max</span> | <span class=\"break-all\">`sum by (db) (irate(redis_server_keyspace_read_hits{role=\"master\"}[$__rate_interval]))`</span> | Highest value of the rate of read operations accessing an existing key (ops/sec) |\n| <span class=\"break-all\">bdb_read_misses</span> | <span class=\"break-all\">`sum by (db) (irate(redis_server_keyspace_read_misses{role=\"master\"}[$__rate_interval]))`</span> | Rate of read operations accessing a non-existing key (ops/sec) |\n| <span class=\"break-all\">bdb_read_misses_max</span> | <span class=\"break-all\">`sum by (db) (irate(redis_server_keyspace_read_misses{role=\"master\"}[$__rate_interval]))`</span> | Highest value of the rate of read operations accessing a non-existing key (ops/sec) |\n| <span class=\"break-all\">bdb_read_req</span> | <span class=\"break-all\">`sum by (db) (irate(endpoint_read_requests{db=\"$db\"}[$__rate_interval]))`</span> | Rate of read requests on the database (ops/sec) |\n| <span class=\"break-all\">bdb_read_req_max</span> | <span class=\"break-all\">`max_over_time (sum by(db) (irate(endpoint_read_requests{db=\"$db\"}[$__rate_interval]))[$__range:])`</span> | Highest value of the rate of read requests on the database (ops/sec) |\n| <span class=\"break-all\">bdb_read_res</span> | <span class=\"break-all\">`sum by (db) (irate(endpoint_read_responses{\"$db\"}[$__rate_interval]))`</span> | Rate of read responses on the database (ops/sec) |\n| <span class=\"break-all\">bdb_read_res_max</span> | <span class=\"break-all\">`max_over_time (sum by(db) (irate(endpoint_read_responses{db=\"$db\"}[$__rate_interval]))[$__range:])`</span> | Highest value of the rate of read responses on the database (ops/sec) |\n| <span class=\"break-all\">bdb_shard_cpu_system</span> | <span class=\"break-all\">`sum by(db) (irate(namedprocess_namegroup_thread_cpu_seconds_total{mode=\"system\", role=\"master\"}[$__rate_interval]))`</span> | Cores utilization in system mode for all Redis shard processes of this database |\n| <span class=\"break-all\">bdb_shard_cpu_system_max<span></span> | <span class=\"break-all\">`sum by(db) (irate(namedprocess_namegroup_thread_cpu_seconds_total{mode=\"system\", role=\"master\"}[$__rate_interval]))`</span> | Highest value of cores utilization in system mode for all Redis shard processes of this database |\n| <span class=\"break-all\">bdb_shard_cpu_user</span> | <span class=\"break-all\">`sum by(db) (irate(namedprocess_namegroup_thread_cpu_seconds_total{mode=\"user\", role=\"master\"}[$__rate_interval]))`</span> | Cores utilization in user mode for the Redis shard process |\n| <span class=\"break-all\">bdb_shard_cpu_user_max</span> | <span class=\"break-all\">`sum by(db) (irate(namedprocess_namegroup_thread_cpu_seconds_total{mode=\"user\", role=\"master\"}[$__rate_interval]))`</span> | Highest value of cores utilization in user mode for the Redis shard process |\n| <span class=\"break-all\">bdb_shards_used</span> | <span class=\"break-all\">`sum((sum(label_replace(label_replace(label_replace(namedprocess_namegroup_thread_count{groupname=~\"redis-\\d+\"}, \"redis\", \"$1\", \"groupname\", \"redis-(\\d+)\"), \"shard_type\", \"flash\", \"threadname\", \"(bigstore).*\"), \"shard_type\", \"ram\", \"shard_type\", \"\")) by (redis, shard_type) > bool 0) * on (redis) group_left(db) redis_server_up) by (db, shard_type)`</span> | Used shard count by database and by shard type (ram / flash) |\n| <span class=\"break-all\">bdb_total_connections_received</span> | <span class=\"break-all\">`sum by(db) (irate(endpoint_client_connections{db=\"$db\"}[$__rate_interval]))`</span> | Rate of new client connections to database (connections/sec) |\n| <span class=\"break-all\">bdb_total_connections_received_max<span></span> | <span class=\"break-all\">`max_over_time (sum by(db) (irate(endpoint_client_connections{db=\"$db\"}[$__rate_interval]))[$__range:])`</span> | Highest value of the rate of new client connections to database (connections/sec) |\n| <span class=\"break-all\">bdb_total_req</span> | <span class=\"break-all\">`sum by (db) (irate(endpoint_read_requests{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_write_requests{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_other_requests{db=\"\"$db\"\"}[$__rate_interval]))`</span> | Rate of all requests on the database (ops/sec) |\n| <span class=\"break-all\">bdb_total_req_max</span> | <span class=\"break-all\">`max_over_time(sum by (db) (irate(endpoint_read_requests{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_write_requests{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_other_requests{db=\"\"$db\"\"}[$__rate_interval])) [$__range:])`</span> | Highest value of the rate of all requests on the database (ops/sec) |\n| <span class=\"break-all\">bdb_total_res</span> | <span class=\"break-all\">`sum by (db) (irate(endpoint_read_responses{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_write_responses{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_other_responses{db=\"\"$db\"\"}[$__rate_interval]))`</span> | Rate of all responses on the database (ops/sec) |\n| <span class=\"break-all\">bdb_total_res_max</span> | <span class=\"break-all\">`max_over_time(sum by (db) (irate(endpoint_read_responses{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_write_responses{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_other_responses{db=\"\"$db\"\"}[$__rate_interval])) [$__range:])`</span> | Highest value of the rate of all responses on the database (ops/sec) |\n| <span class=\"break-all\">bdb_up</span> | <span class=\"break-all\">`min by(db) (redis_server_up)`</span> | Database is up and running |\n| <span class=\"break-all\">bdb_used_memory</span> | <span class=\"break-all\">`sum by (db) (redis_server_used_memory)`</span> | Memory used by database (in BigRedis this includes flash) (bytes) |\n| <span class=\"break-all\">bdb_write_hits</span> | <span class=\"break-all\">`sum by (db) (irate(redis_server_keyspace_write_hits{role=\"master\"}[$__rate_interval]))`</span> | Rate of write operations accessing an existing key (ops/sec) |\n| <span class=\"break-all\">bdb_write_hits_max</span> | <span class=\"break-all\">`sum by (db) (irate(redis_server_keyspace_write_hits{role=\"master\"}[$__rate_interval]))`</span> | Highest value of the rate of write operations accessing an existing key (ops/sec) |\n| <span class=\"break-all\">bdb_write_misses</span> | <span class=\"break-all\">`sum by (db) (irate(redis_server_keyspace_write_misses{role=\"master\"}[$__rate_interval]))`</span> | Rate of write operations accessing a non-existing key (ops/sec) |\n| <span class=\"break-all\">bdb_write_misses_max</span> | <span class=\"break-all\">`sum by (db) (irate(redis_server_keyspace_write_misses{role=\"master\"}[$__rate_interval]))`</span> | Highest value of the rate of write operations accessing a non-existing key (ops/sec) |\n| <span class=\"break-all\">bdb_write_req</span> | <span class=\"break-all\">`sum by (db) (irate(endpoint_write_requests{db=\"$db\"}[$__rate_interval]))`</span> | Rate of write requests on the database (ops/sec) |\n| <span class=\"break-all\">bdb_write_req_max</span> | <span class=\"break-all\">`max_over_time(sum by (db) (irate(endpoint_write_requests{db=\"$db\"}[$__rate_interval]))[$__range:])`</span> | Highest value of the rate of write requests on the database (ops/sec) |\n| <span class=\"break-all\">bdb_write_res</span> | <span class=\"break-all\">`sum by(db) (irate(endpoint_write_responses{db=\"$db\"}[$__rate_interval]))`</span> | Rate of write responses on the database (ops/sec) |\n| <span class=\"break-all\">bdb_write_res_max</span> | <span class=\"break-all\">`max_over_time(sum by (db) (irate(endpoint_write_responses{db=\"$db\"}[$__rate_interval]))[$__range:])`</span> | Highest value of the rate of write responses on the database (ops/sec) |\n| <span class=\"break-all\">bdb_no_of_expires</span> | <span class=\"break-all\">`sum by(db) (redis_server_db_expires{role=\"master\"})`</span> | Current number of volatile keys in the database |"},{"id":"node-metrics","title":"Node metrics","role":"content","text":"| V1&nbsp;metric | Equivalent V2 PromQL | Description |\n| --------- | :------------------- | :---------- |\n| <span class=\"break-all\">node_available_flash</span> | <span class=\"break-all\">`node_available_flash_bytes`</span> | Available flash in the node (bytes) |\n| <span class=\"break-all\">node_available_flash_no_overbooking</span> | <span class=\"break-all\">`node_available_flash_no_overbooking_bytes`</span> | Available flash in the node (bytes), without taking into account overbooking |\n| <span class=\"break-all\">node_available_memory</span> | <span class=\"break-all\">`node_available_memory_bytes`</span> | Amount of free memory in the node (bytes) that is available for database provisioning |\n| <span class=\"break-all\">node_available_memory_no_overbooking</span> | <span class=\"break-all\">`node_available_memory_no_overbooking_bytes`</span> | Available RAM in the node (bytes) without taking into account overbooking |\n| <span class=\"break-all\">node_avg_latency</span> | <span class=\"break-all\">`sum by (proxy) (irate(endpoint_acc_latency[$__rate_interval])) / sum by (proxy) (irate(endpoint_total_started_res[$__rate_interval]))`</span> | Average latency of requests handled by endpoints on the node in microseconds; returned only when there is traffic |\n| <span class=\"break-all\">node_bigstore_free</span> | <span class=\"break-all\">`node_bigstore_free_bytes`</span> | Sum of free space of back-end flash (used by flash database's [BigRedis]) on all cluster nodes (bytes); returned only when BigRedis is enabled |\n| <span class=\"break-all\">node_bigstore_iops</span> | <span class=\"break-all\">`node_flash_reads_total + node_flash_writes_total`</span> | Rate of I/O operations against back-end flash for all shards which are part of a flash-based database (BigRedis) in the cluster (ops/sec); returned only when BigRedis is enabled |\n| <span class=\"break-all\">node_bigstore_kv_ops</span> | <span class=\"break-all\">`sum by (node) (irate(redis_server_big_io_dels[$__rate_interval]) + irate(redis_server_big_io_reads[$__rate_interval]) + irate(redis_server_big_io_writes[$__rate_interval]))`</span> | Rate of value read/write operations against back-end flash for all shards which are part of a flash-based database (BigRedis) in the cluster (ops/sec); returned only when BigRedis is enabled |\n| <span class=\"break-all\">node_bigstore_throughput</span> | <span class=\"break-all\">`sum by (node) (irate(redis_server_big_io_read_bytes[$__rate_interval]) + irate(redis_server_big_io_write_bytes[$__rate_interval]))`</span> | Throughput I/O operations against back-end flash for all shards which are part of a flash-based database (BigRedis) in the cluster (bytes/sec); returned only when BigRedis is enabled |\n| <span class=\"break-all\">node_cert_expiration_seconds</span> | <span class=\"break-all\">`node_cert_expires_in_seconds`</span> | Certificate expiration (in seconds) per given node; read more about [certificates in Redis Software](https://redis.io/docs/latest/operate/rs/security/certificates) and [monitoring certificates](https://redis.io/docs/latest/operate/rs/security/certificates/monitor-certificates) |\n| <span class=\"break-all\">node_conns</span> | <span class=\"break-all\">`sum by (node) (endpoint_client_connections)`</span> | Number of clients connected to endpoints on node |\n| <span class=\"break-all\">node_cpu_idle</span> | <span class=\"break-all\">`avg by (node) (irate(node_cpu_seconds_total{mode=\"idle\"}[$__rate_interval]))`</span> | CPU idle time portion (0-1, multiply by 100 to get percent) |\n| <span class=\"break-all\">node_cpu_idle_max</span> | <span class=\"break-all\">N/A</span> | Highest value of CPU idle time portion (0-1, multiply by 100 to get percent) |\n| <span class=\"break-all\">node_cpu_idle_median</span> | <span class=\"break-all\">N/A</span> | Median value of CPU idle time portion (0-1, multiply by 100 to get percent) |\n| <span class=\"break-all\">node_cpu_idle_min</span> | <span class=\"break-all\">N/A</span> | Lowest value of CPU idle time portion (0-1, multiply by 100 to get percent) |\n| <span class=\"break-all\">node_cpu_system</span> | <span class=\"break-all\">`avg by (node) (irate(node_cpu_seconds_total{mode=\"system\"}[$__rate_interval]))`</span> | CPU time portion spent in the kernel (0-1, multiply by 100 to get percent) |\n| <span class=\"break-all\">node_cpu_system_max</span> | <span class=\"break-all\">N/A</span> | Highest value of CPU time portion spent in the kernel (0-1, multiply by 100 to get percent) |\n| <span class=\"break-all\">node_cpu_system_median</span> | <span class=\"break-all\">N/A</span> | Median value of CPU time portion spent in the kernel (0-1, multiply by 100 to get percent) |\n| <span class=\"break-all\">node_cpu_system_min</span> | <span class=\"break-all\">N/A</span> | Lowest value of CPU time portion spent in the kernel (0-1, multiply by 100 to get percent) |\n| <span class=\"break-all\">node_cpu_user</span> | <span class=\"break-all\">`avg by (node) (irate(node_cpu_seconds_total{mode=\"user\"}[$__rate_interval]))`</span> | CPU time portion spent by user-space processes (0-1, multiply by 100 to get percent) |\n| <span class=\"break-all\">node_cpu_user_max</span> | <span class=\"break-all\">N/A</span> | Highest value of CPU time portion spent by user-space processes (0-1, multiply by 100 to get percent) |\n| <span class=\"break-all\">node_cpu_user_median</span> | <span class=\"break-all\">N/A</span> | Median value of CPU time portion spent by user-space processes (0-1, multiply by 100 to get percent) |\n| <span class=\"break-all\">node_cpu_user_min</span> | <span class=\"break-all\">N/A</span> | Lowest value of CPU time portion spent by user-space processes (0-1, multiply by 100 to get percent) |\n| <span class=\"break-all\">node_cur_aof_rewrites</span> | <span class=\"break-all\">`sum by (cluster, node) (redis_server_aof_rewrite_in_progress)`</span> | Number of AOF rewrites that are currently performed by shards on this node |\n| <span class=\"break-all\">node_egress_bytes</span> | <span class=\"break-all\">`irate(node_network_transmit_bytes_total{device=\"<interface>\"}[$__rate_interval])`</span> | Rate of outgoing network traffic to node (bytes/sec) |\n| <span class=\"break-all\">node_egress_bytes_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of outgoing network traffic to node (bytes/sec) |\n| <span class=\"break-all\">node_egress_bytes_median</span> | <span class=\"break-all\">N/A</span> | Median value of the rate of outgoing network traffic to node (bytes/sec) |\n| <span class=\"break-all\">node_egress_bytes_min</span> | <span class=\"break-all\">N/A</span> | Lowest value of the rate of outgoing network traffic to node (bytes/sec) |\n| <span class=\"break-all\">node_ephemeral_storage_avail</span> | <span class=\"break-all\">`node_ephemeral_storage_avail_bytes`</span> | Disk space available to RLEC processes on configured ephemeral disk (bytes) |\n| <span class=\"break-all\">node_ephemeral_storage_free</span> | <span class=\"break-all\">`node_ephemeral_storage_free_bytes`</span> | Free disk space on configured ephemeral disk (bytes) |\n| <span class=\"break-all\">node_free_memory</span> | <span class=\"break-all\">`node_memory_MemFree_bytes`</span> | Free memory in the node (bytes) |\n| <span class=\"break-all\">node_ingress_bytes</span> | <span class=\"break-all\">`irate(node_network_receive_bytes_total{device=\"<interface>\"}[$__rate_interval])`</span> | Rate of incoming network traffic to node (bytes/sec) |\n| <span class=\"break-all\">node_ingress_bytes_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of incoming network traffic to node (bytes/sec) |\n| <span class=\"break-all\">node_ingress_bytes_median</span> | <span class=\"break-all\">N/A</span> | Median value of the rate of incoming network traffic to node (bytes/sec) |\n| <span class=\"break-all\">node_ingress_bytes_min</span> | <span class=\"break-all\">N/A</span> | Lowest value of the rate of incoming network traffic to node (bytes/sec) |\n| <span class=\"break-all\">node_persistent_storage_avail</span> | <span class=\"break-all\">`node_persistent_storage_avail_bytes`</span> | Disk space available to RLEC processes on configured persistent disk (bytes) |\n| <span class=\"break-all\">node_persistent_storage_free</span> | <span class=\"break-all\">`node_persistent_storage_free_bytes`</span> | Free disk space on configured persistent disk (bytes) |\n| <span class=\"break-all\">node_provisional_flash</span> | <span class=\"break-all\">`node_provisional_flash_bytes`</span> | Amount of flash available for new shards on this node, taking into account overbooking, max Redis servers, reserved flash, and provision and migration thresholds (bytes) |\n| <span class=\"break-all\">node_provisional_flash_no_overbooking</span> | <span class=\"break-all\">`node_provisional_flash_no_overbooking_bytes`</span> | Amount of flash available for new shards on this node, without taking into account overbooking, max Redis servers, reserved flash, and provision and migration thresholds (bytes) |\n| <span class=\"break-all\">node_provisional_memory</span> | <span class=\"break-all\">`node_provisional_memory_bytes`</span> | Amount of RAM that is available for provisioning to databases out of the total RAM allocated for databases |\n| <span class=\"break-all\">node_provisional_memory_no_overbooking</span> | <span class=\"break-all\">`node_provisional_memory_no_overbooking_bytes`</span> | Amount of RAM that is available for provisioning to databases out of the total RAM allocated for databases, without taking into account overbooking |\n| <span class=\"break-all\">node_total_req</span> | <span class=\"break-all\">`sum by (cluster, node) (irate(endpoint_read_requests[$__rate_interval]) +  irate(endpoint_write_requests[$__rate_interval]) +  irate(endpoint_other_requests[$__rate_interval]) )`</span> | Request rate handled by endpoints on node (ops/sec) |\n| <span class=\"break-all\">node_up</span> | <span class=\"break-all\">`node_metrics_up`</span> | Node is part of the cluster and is connected |"},{"id":"cluster-metrics","title":"Cluster metrics","role":"content","text":"| V1&nbsp;metric | Equivalent V2 PromQL | Description |\n| --------- | :------------------- | :---------- |\n| cluster_shards_limit | `license_shards_limit` | Total shard limit by the license by shard type (ram / flash) |"},{"id":"proxy-metrics","title":"Proxy metrics","role":"content","text":"| V1&nbsp;metric | Equivalent V2 PromQL | Description |\n| --------- | :------------------- | :---------- |\n| <span class=\"break-all\">listener_acc_latency</span> | <span class=\"break-all\">N/A</span> | Accumulative latency (sum of the latencies) of all types of commands on the database. For the average latency, divide this value by listener_total_res |\n| <span class=\"break-all\">listener_acc_latency_max</span> | <span class=\"break-all\">N/A</span> | Highest value of accumulative latency of all types of commands on the database |\n| <span class=\"break-all\">listener_acc_other_latency</span> | <span class=\"break-all\">N/A</span> | Accumulative latency (sum of the latencies) of commands that are a type \"other\" on the database. For the average latency, divide this value by listener_other_res |\n| <span class=\"break-all\">listener_acc_other_latency_max</span> | <span class=\"break-all\">N/A</span> | Highest value of accumulative latency of commands that are a type \"other\" on the database |\n| <span class=\"break-all\">listener_acc_read_latency</span> | <span class=\"break-all\">N/A</span> | Accumulative latency (sum of the latencies) of commands that are a type \"read\" on the database. For the average latency, divide this value by listener_read_res |\n| <span class=\"break-all\">listener_acc_read_latency_max</span> | <span class=\"break-all\">N/A</span> | Highest value of accumulative latency of commands that are a type \"read\" on the database |\n| <span class=\"break-all\">listener_acc_write_latency</span> | <span class=\"break-all\">N/A</span> | Accumulative latency (sum of the latencies) of commands that are a type \"write\" on the database. For the average latency, divide this value by listener_write_res |\n| <span class=\"break-all\">listener_acc_write_latency_max</span> | <span class=\"break-all\">N/A</span> | Highest value of accumulative latency of commands that are a type \"write\" on the database |\n| <span class=\"break-all\">listener_auth_cmds</span> | <span class=\"break-all\">N/A</span> | Number of memcached AUTH commands sent to the database |\n| <span class=\"break-all\">listener_auth_cmds_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the number of memcached AUTH commands sent to the database |\n| <span class=\"break-all\">listener_auth_errors</span> | <span class=\"break-all\">N/A</span> | Number of error responses to memcached AUTH commands |\n| <span class=\"break-all\">listener_auth_errors_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the number of error responses to memcached AUTH commands |\n| <span class=\"break-all\">listener_cmd_flush</span> | <span class=\"break-all\">N/A</span> | Number of memcached FLUSH_ALL commands sent to the database |\n| <span class=\"break-all\">listener_cmd_flush_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the number of memcached FLUSH_ALL commands sent to the database |\n| <span class=\"break-all\">listener_cmd_get</span> | <span class=\"break-all\">N/A</span> | Number of memcached GET commands sent to the database |\n| <span class=\"break-all\">listener_cmd_get_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the number of memcached GET commands sent to the database |\n| <span class=\"break-all\">listener_cmd_set</span> | <span class=\"break-all\">N/A</span> | Number of memcached SET commands sent to the database |\n| <span class=\"break-all\">listener_cmd_set_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the number of memcached SET commands sent to the database |\n| <span class=\"break-all\">listener_cmd_touch</span> | <span class=\"break-all\">N/A</span> | Number of memcached TOUCH commands sent to the database |\n| <span class=\"break-all\">listener_cmd_touch_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the number of memcached TOUCH commands sent to the database |\n| <span class=\"break-all\">listener_conns</span> | <span class=\"break-all\">N/A</span> | Number of clients connected to the endpoint |\n| <span class=\"break-all\">listener_egress_bytes</span> | <span class=\"break-all\">N/A</span> | Rate of outgoing network traffic to the endpoint (bytes/sec) |\n| <span class=\"break-all\">listener_egress_bytes_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of outgoing network traffic to the endpoint (bytes/sec) |\n| <span class=\"break-all\">listener_ingress_bytes</span> | <span class=\"break-all\">N/A</span> | Rate of incoming network traffic to the endpoint (bytes/sec) |\n| <span class=\"break-all\">listener_ingress_bytes_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of incoming network traffic to the endpoint (bytes/sec) |\n| <span class=\"break-all\">listener_last_req_time</span> | <span class=\"break-all\">N/A</span> | Time of last command sent to the database |\n| <span class=\"break-all\">listener_last_res_time</span> | <span class=\"break-all\">N/A</span> | Time of last response sent from the database |\n| <span class=\"break-all\">listener_max_connections_exceeded</span> | <span class=\"break-all\">`irate(endpoint_maximal_connections_exceeded[$__rate_interval])`</span> | Number of times the number of clients connected to the database at the same time has exceeded the max limit |\n| <span class=\"break-all\">listener_max_connections_exceeded_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the number of times the number of clients connected to the database at the same time has exceeded the max limit |\n| <span class=\"break-all\">listener_monitor_sessions_count</span> | <span class=\"break-all\">N/A</span> | Number of clients connected in monitor mode to the endpoint |\n| <span class=\"break-all\">listener_other_req</span> | <span class=\"break-all\">N/A</span> | Rate of other (non-read/write) requests on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_other_req_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of other (non-read/write) requests on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_other_res</span> | <span class=\"break-all\">N/A</span> | Rate of other (non-read/write) responses on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_other_res_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of other (non-read/write) responses on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_other_started_res</span> | <span class=\"break-all\">N/A</span> | Number of responses sent from the database of type \"other\" |\n| <span class=\"break-all\">listener_other_started_res_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the number of responses sent from the database of type \"other\" |\n| <span class=\"break-all\">listener_read_req</span> | <span class=\"break-all\">`irate(endpoint_read_requests[$__rate_interval])`</span> | Rate of read requests on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_read_req_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of read requests on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_read_res</span> | <span class=\"break-all\">`irate(endpoint_read_responses[$__rate_interval])`</span> | Rate of read responses on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_read_res_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of read responses on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_read_started_res</span> | <span class=\"break-all\">N/A</span> | Number of responses sent from the database of type \"read\" |\n| <span class=\"break-all\">listener_read_started_res_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the number of responses sent from the database of type \"read\" |\n| <span class=\"break-all\">listener_total_connections_received</span> | <span class=\"break-all\">`irate(endpoint_total_connections_received[$__rate_interval])`</span> | Rate of new client connections to the endpoint (connections/sec) |\n| <span class=\"break-all\">listener_total_connections_received_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of new client connections to the endpoint (connections/sec) |\n| <span class=\"break-all\">listener_total_req</span> | <span class=\"break-all\">N/A</span> | Request rate handled by the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_total_req_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of all requests on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_total_res</span> | <span class=\"break-all\">N/A</span> | Rate of all responses on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_total_res_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of all responses on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_total_started_res</span> | <span class=\"break-all\">N/A</span> | Number of responses sent from the database of all types |\n| <span class=\"break-all\">listener_total_started_res_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the number of responses sent from the database of all types |\n| <span class=\"break-all\">listener_write_req</span> | <span class=\"break-all\">`irate(endpoint_write_requests[$__rate_interval])`</span> | Rate of write requests on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_write_req_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of write requests on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_write_res</span> | <span class=\"break-all\">`irate(endpoint_write_responses[$__rate_interval])`</span> | Rate of write responses on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_write_res_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of write responses on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_write_started_res</span> | <span class=\"break-all\">N/A</span> | Number of responses sent from the database of type \"write\" |\n| <span class=\"break-all\">listener_write_started_res_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the number of responses sent from the database of type \"write\" |"},{"id":"replication-metrics","title":"Replication metrics","role":"content","text":"| V1&nbsp;metric | Equivalent V2 PromQL | Description |\n| --------- | :------------------- | :---------- |\n| <span class=\"break-all\">bdb_replicaof_syncer_ingress_bytes</span> | <span class=\"break-all\">`rate(replica_src_ingress_bytes[$__rate_interval])`</span> | Rate of compressed incoming network traffic to a Replica Of database (bytes/sec) |\n| <span class=\"break-all\">bdb_replicaof_syncer_ingress_bytes_decompressed</span> | <span class=\"break-all\">`rate(replica_src_ingress_bytes_decompressed[$__rate_interval])`</span> | Rate of decompressed incoming network traffic to a Replica Of database (bytes/sec) |\n| <span class=\"break-all\">bdb_replicaof_syncer_local_ingress_lag_time</span> | <span class=\"break-all\">`database_syncer_lag_ms{syncer_type=\"replicaof\"}`</span> | Lag time between the source and the destination for Replica Of traffic (ms) |\n| <span class=\"break-all\">bdb_replicaof_syncer_status</span> | <span class=\"break-all\">`database_syncer_current_status{syncer_type=\"replicaof\"}`</span> | Syncer status for Replica Of traffic; 0 = in-sync, 1 = syncing, 2 = out of sync |\n| <span class=\"break-all\">bdb_crdt_syncer_ingress_bytes</span> | <span class=\"break-all\">`rate(crdt_src_ingress_bytes[$__rate_interval])`</span> | Rate of compressed incoming network traffic to CRDB (bytes/sec) |\n| <span class=\"break-all\">bdb_crdt_syncer_ingress_bytes_decompressed</span> | <span class=\"break-all\">`rate(crdt_src_ingress_bytes_decompressed[$__rate_interval])`</span> | Rate of decompressed incoming network traffic to CRDB (bytes/sec) |\n| <span class=\"break-all\">bdb_crdt_syncer_local_ingress_lag_time</span> | <span class=\"break-all\">`database_syncer_lag_ms{syncer_type=\"crdt\"}`</span> | Lag time between the source and the destination (ms) for CRDB traffic |\n| <span class=\"break-all\">bdb_crdt_syncer_status</span> | <span class=\"break-all\">`database_syncer_current_status{syncer_type=\"crdt\"}`</span> | Syncer status for CRDB traffic; 0 = in-sync, 1 = syncing, 2 = out of sync |"},{"id":"shard-metrics","title":"Shard metrics","role":"content","text":"| V1&nbsp;metric | Equivalent V2 PromQL | Description |\n| --------- | :------------------- | :---------- |\n| <span class=\"break-all\">redis_active_defrag_running</span> | <span class=\"break-all\">`redis_server_active_defrag_running`</span> | Automatic memory defragmentation current aggressiveness (% cpu) |\n| <span class=\"break-all\">redis_allocator_active</span> | <span class=\"break-all\">`redis_server_allocator_active`</span> | Total used memory, including external fragmentation |\n| <span class=\"break-all\">redis_allocator_allocated</span> | <span class=\"break-all\">`redis_server_allocator_allocated`</span> | Total allocated memory |\n| <span class=\"break-all\">redis_allocator_resident</span> | <span class=\"break-all\">`redis_server_allocator_resident`</span> | Total resident memory (RSS) |\n| <span class=\"break-all\">redis_aof_last_cow_size</span> | <span class=\"break-all\">`redis_server_aof_last_cow_size`</span> | Last AOFR, CopyOnWrite memory |\n| <span class=\"break-all\">redis_aof_rewrite_in_progress</span> | <span class=\"break-all\">`redis_server_aof_rewrite_in_progress`</span> | Indicates whether an AOF rewrite is in progress (1 = in progress, 0 = otherwise) |\n| <span class=\"break-all\">redis_aof_rewrites</span> | <span class=\"break-all\">`redis_server_aof_rewrites`</span> | Number of AOF rewrites this process executed |\n| <span class=\"break-all\">redis_aof_delayed_fsync</span> | <span class=\"break-all\">`redis_server_aof_delayed_fsync`</span> | Number of times an AOF fsync caused delays in the main Redis thread (inducing latency); this can indicate that the disk is slow or overloaded |\n| <span class=\"break-all\">redis_blocked_clients</span> | <span class=\"break-all\">`redis_server_blocked_clients`</span> | Count the clients waiting on a blocking call |\n| <span class=\"break-all\">redis_connected_clients</span> | <span class=\"break-all\">`redis_server_connected_clients`</span> | Number of client connections to the specific shard |\n| <span class=\"break-all\">redis_connected_slaves</span> | <span class=\"break-all\">`redis_server_connected_slaves`</span> | Number of connected replicas |\n| <span class=\"break-all\">redis_db0_avg_ttl</span> | <span class=\"break-all\">`redis_server_db0_avg_ttl`</span> | Average TTL of all volatile keys |\n| <span class=\"break-all\">redis_db0_expires</span> | <span class=\"break-all\">`redis_server_expired_keys`</span> | Total count of volatile keys |\n| <span class=\"break-all\">redis_db0_keys</span> | <span class=\"break-all\">`redis_server_db0_keys`</span> | Total key count |\n| <span class=\"break-all\">redis_evicted_keys</span> | <span class=\"break-all\">`redis_server_evicted_keys`</span> | Keys evicted so far (since restart) |\n| <span class=\"break-all\">redis_expire_cycle_cpu_milliseconds</span> | <span class=\"break-all\">`redis_server_expire_cycle_cpu_milliseconds`</span> | The cumulative amount of time spent on active expiry cycles |\n| <span class=\"break-all\">redis_expired_keys</span> | <span class=\"break-all\">`redis_server_expired_keys`</span> | Keys expired so far (since restart) |\n| <span class=\"break-all\">redis_forwarding_state</span> | <span class=\"break-all\">`redis_server_forwarding_state`</span> | Shard forwarding state (1 = on, 0 = off) |\n| <span class=\"break-all\">redis_keys_trimmed</span> | <span class=\"break-all\">`redis_server_keys_trimmed`</span> | The number of keys that were trimmed in the current or last resharding process |\n| <span class=\"break-all\">redis_keyspace_read_hits</span> | <span class=\"break-all\">`redis_server_keyspace_read_hits`</span> | Number of read operations accessing an existing keyspace |\n| <span class=\"break-all\">redis_keyspace_read_misses</span> | <span class=\"break-all\">`redis_server_keyspace_read_misses`</span> | Number of read operations accessing a non-existing keyspace |\n| <span class=\"break-all\">redis_keyspace_write_hits</span> | <span class=\"break-all\">`redis_server_keyspace_write_hits`</span> | Number of write operations accessing an existing keyspace |\n| <span class=\"break-all\">redis_keyspace_write_misses</span> | <span class=\"break-all\">`redis_server_keyspace_write_misses`</span> | Number of write operations accessing a non-existing keyspace |\n| <span class=\"break-all\">redis_master_link_status</span> | <span class=\"break-all\">`redis_server_master_link_status`</span> | Indicates if the replica is connected to its master (1 = up, 2 = down, 3 = none, 99 = unknown) |\n| <span class=\"break-all\">redis_master_repl_offset</span> | <span class=\"break-all\">`redis_server_master_repl_offset`</span> | Number of bytes sent to replicas by the shard; calculate the throughput for a time period by comparing the value at different times |\n| <span class=\"break-all\">redis_master_sync_in_progress</span> | <span class=\"break-all\">`redis_server_master_sync_in_progress`</span> | The master shard is synchronizing (1 true; 0 false) |\n| <span class=\"break-all\">redis_max_process_mem</span> | <span class=\"break-all\">`redis_server_max_process_mem`</span> | Current memory limit configured by redis_mgr according to node free memory |\n| <span class=\"break-all\">redis_maxmemory</span> | <span class=\"break-all\">`redis_server_maxmemory`</span> | Current memory limit configured by redis_mgr according to database memory limits. <br /><br />To calculate the percent memory usage:<br /><span class=\"break-all\">`sum by (cluster,db)(redis_server_used_memory{role=\"master\"}) / (avg by(cluster,db)(db_memory_limit_bytes) / max by(cluster,db)(db_replication_factor))`</span> |\n| <span class=\"break-all\">redis_mem_aof_buffer</span> | <span class=\"break-all\">`redis_server_mem_aof_buffer`</span> | Current size of AOF buffer |\n| <span class=\"break-all\">redis_mem_clients_normal</span> | <span class=\"break-all\">`redis_server_mem_clients_normal`</span> | Current memory used for input and output buffers of non-replica clients |\n| <span class=\"break-all\">redis_mem_clients_slaves</span> | <span class=\"break-all\">`redis_server_mem_clients_slaves`</span> | Current memory used for input and output buffers of replica clients |\n| <span class=\"break-all\">redis_mem_fragmentation_ratio</span> | <span class=\"break-all\">`redis_server_mem_fragmentation_ratio`</span> | Memory fragmentation ratio (1.3 means 30% overhead) |\n| <span class=\"break-all\">redis_mem_not_counted_for_evict</span> | <span class=\"break-all\">`redis_server_mem_not_counted_for_evict`</span> | Portion of used_memory (in bytes) that's not counted for eviction and OOM error |\n| <span class=\"break-all\">redis_mem_replication_backlog</span> | <span class=\"break-all\">`redis_server_mem_replication_backlog`</span> | Size of replication backlog |\n| <span class=\"break-all\">redis_module_fork_in_progress</span> | <span class=\"break-all\">`redis_server_module_fork_in_progress`</span> | A binary value that indicates if there is an active fork spawned by a module (1) or not (0) |\n| <span class=\"break-all\">redis_process_cpu_system_seconds_total</span> | <span class=\"break-all\">`namedprocess_namegroup_cpu_seconds_total{mode=\"system\"}`</span> | Shard process system CPU time spent in seconds |\n| <span class=\"break-all\">redis_process_cpu_usage_percent</span> | <span class=\"break-all\">`namedprocess_namegroup_cpu_seconds_total{mode=~\"system\\|user\"}`</span> | Shard process CPU usage percentage |\n| <span class=\"break-all\">redis_process_cpu_user_seconds_total</span> | <span class=\"break-all\">`namedprocess_namegroup_cpu_seconds_total{mode=\"user\"}`</span> | Shard user CPU time spent in seconds |\n| <span class=\"break-all\">redis_process_main_thread_cpu_system_seconds_total</span> | <span class=\"break-all\">`namedprocess_namegroup_thread_cpu_seconds_total{mode=\"system\",threadname=\"redis-server\"}`</span> | Shard main thread system CPU time spent in seconds |\n| <span class=\"break-all\">redis_process_main_thread_cpu_user_seconds_total</span> | <span class=\"break-all\">`namedprocess_namegroup_thread_cpu_seconds_total{mode=\"user\",threadname=\"redis-server\"}`</span> | Shard main thread user CPU time spent in seconds |\n| <span class=\"break-all\">redis_process_max_fds</span> | <span class=\"break-all\">`max(namedprocess_namegroup_open_filedesc)`</span> | Shard maximum number of open file descriptors |\n| <span class=\"break-all\">redis_process_open_fds</span> | <span class=\"break-all\">`namedprocess_namegroup_open_filedesc`</span> | Shard number of open file descriptors |\n| <span class=\"break-all\">redis_process_resident_memory_bytes</span> | <span class=\"break-all\">`namedprocess_namegroup_memory_bytes{memtype=\"resident\"}`</span> | Shard resident memory size in bytes |\n| <span class=\"break-all\">redis_process_start_time_seconds</span> | <span class=\"break-all\">`namedprocess_namegroup_oldest_start_time_seconds`</span> | Shard start time of the process since unix epoch in seconds |\n| <span class=\"break-all\">redis_process_virtual_memory_bytes</span> | <span class=\"break-all\">`namedprocess_namegroup_memory_bytes{memtype=\"virtual\"}`</span> | Shard virtual memory in bytes |\n| <span class=\"break-all\">redis_rdb_bgsave_in_progress</span> | <span class=\"break-all\">`redis_server_rdb_bgsave_in_progress`</span> | Indicates whether bgsave is currently in progress (1 = in progress, 0 = otherwise) |\n| <span class=\"break-all\">redis_rdb_last_cow_size</span> | <span class=\"break-all\">`redis_server_rdb_last_cow_size`</span> | Last bgsave (or SYNC fork) used CopyOnWrite memory |\n| <span class=\"break-all\">redis_rdb_saves</span> | <span class=\"break-all\">`redis_server_rdb_saves`</span> | Total count of bgsaves since the process was restarted (including replica fullsync and persistence) |\n| <span class=\"break-all\">redis_repl_touch_bytes</span> | <span class=\"break-all\">`redis_server_repl_touch_bytes`</span> | Number of bytes sent to replicas as TOUCH commands by the shard as a result of a READ command that was processed; calculate the throughput for a time period by comparing the value at different times |\n| <span class=\"break-all\">redis_total_commands_processed</span> | <span class=\"break-all\">`redis_server_total_commands_processed`</span> | Number of commands processed by the shard; calculate the number of commands for a time period by comparing the value at different times |\n| <span class=\"break-all\">redis_total_connections_received</span> | <span class=\"break-all\">`redis_server_total_connections_received`</span> | Number of connections received by the shard; calculate the number of connections for a time period by comparing the value at different times |\n| <span class=\"break-all\">redis_total_net_input_bytes</span> | <span class=\"break-all\">`redis_server_total_net_input_bytes`</span> | Number of bytes received by the shard; calculate the throughput for a time period by comparing the value at different times |\n| <span class=\"break-all\">redis_total_net_output_bytes</span> | <span class=\"break-all\">`redis_server_total_net_output_bytes`</span> | Number of bytes sent by the shard; calculate the throughput for a time period by comparing the value at different times |\n| <span class=\"break-all\">redis_up</span> | <span class=\"break-all\">`redis_server_up`</span> | Shard is up and running |\n| <span class=\"break-all\">redis_used_memory</span> | <span class=\"break-all\">`redis_server_used_memory`</span> | Memory used by shard (in BigRedis this includes flash) (bytes) |"}],"examples":[{"id":"overview-ex0","language":"yaml","code":"scrape_configs:\n  # Scrape Redis Software\n  - job_name: redis-enterprise\n    scrape_interval: 30s\n    scrape_timeout: 30s\n    metrics_path: /v2\n    scheme: https\n    tls_config:\n      insecure_skip_verify: true\n    static_configs:\n      - targets: [\"<cluster_name>:8070\"]","section_id":"overview"}]}
{"schema_version":2,"id":"integrate/pulumi-provider-for-redis-cloud/get-started","title":"Get started with Pulumi","url":"https://redis.io/docs/latest/integrate/pulumi-provider-for-redis-cloud/get-started/","summary":"Shows how to install the Redis Cloud Pulumi provider and create a subscription.","tags":["docs","integrate","rc"],"last_updated":"2026-09-17T14:54:42+01:00","page_type":"content","content_hash":"89cdd43bfde488b196839659ddd390152ca03da1deb1f401376916c791d705d7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Here, you'll learn how to use the [Redis Cloud Pulumi provider](https://redis.io/docs/latest/integrate/pulumi-provider-for-redis-cloud) to create a Redis Cloud Pro subscription and a database using Python."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"1. [Install Pulumi](https://www.pulumi.com/docs/install/) and [create a Pulumi account](https://app.pulumi.com/signin) if you do not have one already.\n\n1. [Create a Redis Cloud account](https://redis.io/docs/latest/operate/rc/rc-quickstart#create-an-account) if you do not have one already.\n\n1. [Enable the Redis Cloud API](https://redis.io/docs/latest/operate/rc/api/get-started/enable-the-api).\n\n1. Get your Redis Cloud [API keys](https://redis.io/docs/latest/operate/rc/api/get-started/manage-api-keys)."},{"id":"install-the-pulumi-provider-files","title":"Install the Pulumi provider files","role":"setup","text":"1.  In your Python project, create an empty folder. From this folder, run `pulumi new rediscloud-python`.\n\n1. Log into Pulumi using your [Pulumi access token](https://app.pulumi.com/account/tokens) if prompted.\n\n1.  Enter a project name, description, and stack name.\n\n1.  Enter your Redis Cloud access and secret keys.\n\n1.  Enter the credit card type (Visa, Mastercard) on file with your Redis Cloud account.\n\n1.  Enter the last four numbers of the card on file with your Redis Cloud account.\n\nOnce these steps are completed, the dependencies needed for the project will be installed and a Python virtual environment will be created."},{"id":"deploy-resources-with-pulumi","title":"Deploy resources with Pulumi","role":"content","text":"The Pulumi Python project includes three main files:\n\n- `pulumi.yaml` : A metadata file which is used to help configure the Python runtime environment.\n\n- `pulumi.YOUR_PROJECT_NAME.yaml`: Contains the information related to the Cloud API access and secret key, credit card type and last 4 digits.\n\n- `__main__.py`: A Pulumi template file that creates a Redis Cloud Pro subscription. Use this template file as a starting point to create the subscription with a cloud provider and define specifications for the database (this includes memory, throughput, Redis advanced capabilities, and other information).\n\nTo deploy the resources described in `__main__.py`, run `pulumi up`. This will take some time. You will be able to see your subscription being created through the [Redis Cloud console](https://cloud.redis.io/).\n\nIf you want to remove these resources, run `pulumi down`."},{"id":"more-info","title":"More info","role":"content","text":"- [Redis Cloud Pulumi registry](https://www.pulumi.com/registry/packages/rediscloud/)\n- [Pulumi documentation](https://www.pulumi.com/docs/)"}],"examples":[]}
{"schema_version":2,"id":"integrate/pulumi-provider-for-redis-cloud","title":"Pulumi provider for Redis Cloud","url":"https://redis.io/docs/latest/integrate/pulumi-provider-for-redis-cloud/","summary":"Explains how to use Pulumi to provision Redis Cloud infrastructure","tags":["docs","integrate","rc"],"last_updated":"2026-09-17T14:54:42+01:00","children":[{"id":"integrate/pulumi-provider-for-redis-cloud/get-started","summary":"Shows how to install the Redis Cloud Pulumi provider and create a subscription.","title":"Get started with Pulumi","url":"https://redis.io/docs/latest/integrate/pulumi-provider-for-redis-cloud/get-started/"}],"page_type":"content","content_hash":"77ec296fc0b47ea46eafd2b9054353de98872114892acf7ca43653e2601e024e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Pulumi](https://www.pulumi.com/) is an automation tool that allows you to easily provision infrastructure as code. Pulumi allows developers to write infrastructure code using programming languages rather than using domain-specific languages.\n\nWith the [Redis Cloud Resource Provider](https://www.pulumi.com/registry/packages/rediscloud/), you can create Redis Cloud resources in a programming language. The Pulumi Redis Cloud Provider supports the following programming languages:\n\n* TypeScript\n* Python\n* C#\n* Java\n* Go\n* YAML\n\nThe Redis Cloud Pulumi provider is based on the [Redis Cloud Terraform provider](https://redis.io/docs/latest/integrate/terraform-provider-for-redis-cloud).\n\n> [!NOTE]\n> The Redis Cloud Pulumi Redis Cloud provider supports Redis Cloud Pro. It does not support Redis Cloud Essentials.\n\nSee [Get started with Pulumi](https://redis.io/docs/latest/integrate/pulumi-provider-for-redis-cloud/get-started) for an example of how to use the Pulumi provider with Python."},{"id":"resources-and-functions","title":"Resources and functions","role":"content","text":"Pulumi resources represent the fundamental units that make up cloud infrastructure. A provider can make functions available in its SDK and resource types. These functions are often used to acquire information that is not part of a resource.\n\nThe Redis Cloud Pulumi provider allows for the following resources:\n\n* [`Subscription`](https://www.pulumi.com/registry/packages/rediscloud/api-docs/subscription/): The basic building block of a Redis Cloud subscription.\n* [`SubscriptionDatabase`](https://www.pulumi.com/registry/packages/rediscloud/api-docs/subscriptiondatabase/): Represents a Redis database which belongs to a specific Redis Cloud subscription.\n* [`SubscriptionPeering`](https://www.pulumi.com/registry/packages/rediscloud/api-docs/subscriptionpeering/): A VPC peering connection (AWS or GCP) to a specific Redis Cloud subscription.\n* [`CloudAccount`](https://www.pulumi.com/registry/packages/rediscloud/api-docs/cloudaccount/): Represents an AWS account in which you want to deploy Redis Cloud infrastructure components.\n\n    > [!NOTE]\n    > The \"bring your own AWS account\" option for Redis Cloud has been deprecated. The `CloudAccount` resource is only available for legacy Redis Cloud integrations.\n\n* [`ActiveActiveSubscription`](https://www.pulumi.com/registry/packages/rediscloud/api-docs/activeactivesubscription/): The basic building block of an active-active Redis Cloud subscription.\n* [`ActiveActiveSubscriptionDatabase`](https://www.pulumi.com/registry/packages/rediscloud/api-docs/activeactivesubscriptiondatabase/): Represents a Redis database which belongs to a specific Redis Cloud active-active subscription.\n* [`ActiveActiveSubscriptionRegions`](https://www.pulumi.com/registry/packages/rediscloud/api-docs/activeactivesubscriptionregions/): The different regions where the active-active subscription will be deployed.\n* [`ActiveActiveSubscriptionPeering`](https://www.pulumi.com/registry/packages/rediscloud/api-docs/activeactivesubscriptionpeering/): A VPC peering connection (AWS or GCP) to a specific Redis Cloud active-active subscription.\n* [`AclRule`](https://www.pulumi.com/registry/packages/rediscloud/api-docs/aclrule/), [`AclRole`](https://www.pulumi.com/registry/packages/rediscloud/api-docs/aclrole/), and [`AclUser`](https://www.pulumi.com/registry/packages/rediscloud/api-docs/acluser/): Rules, Roles, and Users for [Role-based access control](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/role-based-access-control).\n\nIt also allows for the following functions:\n\n* [`GetCloudAccount`](https://www.pulumi.com/registry/packages/rediscloud/api-docs/getcloudaccount/): Get the information related to the AWS account.\n\n    > [!NOTE]\n    > The \"bring your own AWS account\" option for Redis Cloud has been deprecated. The `CloudAccount` resource is only available for legacy Redis Cloud integrations.\n\n* [`GetDataPersistence`](https://www.pulumi.com/registry/packages/rediscloud/api-docs/getdatapersistence/): Get the type of database persistence.\n* [`GetDatabase`](https://www.pulumi.com/registry/packages/rediscloud/api-docs/getdatabase/): Get the information related to a specific database.\n* [`GetDatabaseModules`](https://www.pulumi.com/registry/packages/rediscloud/api-docs/getdatabasemodules/): Get the capabilities for a specific database.\n* [`GetPaymentMethod`](https://www.pulumi.com/registry/packages/rediscloud/api-docs/getpaymentmethod/): Get the payment method related to the Redis Cloud account.\n* [`GetRegions`](https://www.pulumi.com/registry/packages/rediscloud/api-docs/getregions/): Get the regions related to an active-active subscription\n* [`GetSubscription`](https://www.pulumi.com/registry/packages/rediscloud/api-docs/getsubscription/): Get the information related to a specific subscription.\n* [`GetSubscriptionPeerings`](https://www.pulumi.com/registry/packages/rediscloud/api-docs/getsubscriptionpeerings/): Get the VPC peerings (AWS or GCP) related to a specific subscription.\n* [`GetAclRule`](https://www.pulumi.com/registry/packages/rediscloud/api-docs/getaclrule/), [`GetAclRole`](https://www.pulumi.com/registry/packages/rediscloud/api-docs/getaclrole/), and [`GetAclUser`](https://www.pulumi.com/registry/packages/rediscloud/api-docs/getacluser/): Get the Rules, Roles, and Users for [Role-based access control](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/role-based-access-control)."},{"id":"more-info","title":"More info","role":"content","text":"- [Get started with Pulumi](https://redis.io/docs/latest/integrate/pulumi-provider-for-redis-cloud/get-started)\n- [Redis Cloud Pulumi registry](https://www.pulumi.com/registry/packages/rediscloud/)\n- [Pulumi documentation](https://www.pulumi.com/docs/)"}],"examples":[]}
{"schema_version":2,"id":"integrate/railway-redis","title":"Redis on Railway","url":"https://redis.io/docs/latest/integrate/railway-redis/","summary":"Deploy and host Redis on Railway. The Railway Redis database template allows you to provision and connect to a Redis database with zero configuration.","tags":["docs","integrate","oss","rs"],"last_updated":"2026-10-02T14:54:03+01:00","children":[],"page_type":"content","content_hash":"10787996d8dbe12ce030a50d49879482d082d8fe936065feaad449c27304dca1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Railway](https://railway.com?utm_medium=integration&utm_source=docs&utm_campaign=redis) simplifies your infrastructure stack from databases to servers to observability with a single, scalable, easy-to-use platform. Companies of all sizes deploy full-stack applications on Railway and benefit from highly performant networking, intuitive vertical and horizontal scaling, and isolated environments with pull request deploys. Railway's template marketplace includes thousands of one-click deploy templates, including Redis.\n\n![](https://redis.io/docs/latest/images/rc/railway-redis-canvas.png)\n\nThis is a guide to deploying Redis on Railway, using the official template."},{"id":"deploy-redis-to-railway","title":"Deploy Redis to Railway","role":"content","text":"Navigate to a new Railway project at [railway.com/new](https://railway.com/new?utm_medium=integration&utm_source=docs&utm_campaign=redis). Add a Redis database to your project via the `ctrl / cmd + k` menu, and type `Redis`.\n\nYou can also deploy it via the [template](https://railway.com/deploy/redis) from the Railway template marketplace.\n\n![](https://redis.io/docs/latest/images/rc/railway-redis-add.gif)\n\nUpon deployment, you will have a Redis service running in your project, deployed from the [redis](https://hub.docker.com/_/redis) Docker image.\n\nSince the deployed container is pulled from the [redis](https://hub.docker.com/_/redis) image in Docker Hub, you can modify the deployment based on the [instructions in Docker Hub](https://hub.docker.com/_/redis)."},{"id":"connecting-to-your-railway-redis-service","title":"Connecting to your Railway Redis Service","role":"content","text":"It is possible to connect to Redis externally by using the [TCP Proxy](https://docs.railway.com/deploy/exposing-your-app?utm_medium=integration&utm_source=docs&utm_campaign=redis#tcp-proxying) which is enabled by default."},{"id":"backup-and-monitoring","title":"Backup and Monitoring","role":"content","text":"Especially for production environments, performing backups and monitoring the health of your data is essential. Consider adding:\n\n- **Backups**: Automate regular backups to ensure data recovery in case of failure. We suggest checking out Railway's native [Backups](https://docs.railway.com/reference/backups?utm_medium=integration&utm_source=docs&utm_campaign=redis) feature.\n\n- **Observability**: Implement monitoring for insights into performance and health of your Redis cluster. You can integrate a Redis exporter for Prometheus, although we do not provide a specific template at this time."},{"id":"additional-resources","title":"Additional Resources","role":"content","text":"- [Railway Redis guide](https://docs.railway.com/guides/redis?utm_medium=integration&utm_source=docs&utm_campaign=redis)\n- [Railway platform features](https://railway.com/features?utm_medium=integration&utm_source=docs&utm_campaign=redis)"}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-ai-libraries","title":"Redis for AI","url":"https://redis.io/docs/latest/integrate/redis-ai-libraries/","summary":"Build AI applications with Redis vector database and semantic caching","tags":["docs","integrate","oss","rs","rc"],"last_updated":"2026-09-17T14:55:03+01:00","children":[],"page_type":"content","content_hash":"8af0c9b7302a6d3e8d5cbfda7b3208dc333ff3151a17cc6141c43a4423da2841","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"Build powerful AI applications using Redis as your vector database with specialized libraries for Python, JavaScript, and Java."},{"id":"overview","title":"Overview","role":"overview","text":"Redis provides comprehensive AI libraries and tools to help you build intelligent applications with vector search, retrieval-augmented generation (RAG), semantic caching, and more. Whether you're working with LangChain, LlamaIndex, or building custom AI solutions, Redis has the tools you need.\n\n[Explore the complete Redis for AI documentation](https://redis.io/docs/latest/develop/ai)"},{"id":"key-features","title":"Key Features","role":"content","text":"- **Vector Search**: Store and query vector embeddings with HNSW and FLAT index types\n- **Semantic Caching**: Cache LLM responses to reduce costs and improve performance\n- **RAG Support**: Build retrieval-augmented generation applications with popular frameworks\n- **Multi-language Support**: Libraries available for Python, JavaScript, and Java\n- **Real-time Performance**: Sub-millisecond query latency for production AI applications"},{"id":"ai-libraries","title":"AI Libraries","role":"content","text":""},{"id":"redisvl-python","title":"RedisVL (Python)","role":"content","text":"The Redis Vector Library (RedisVL) is a Python client library for building AI applications with Redis.\n\n- **Vector Search**: Create and query vector indexes with ease\n- **Semantic Caching**: Built-in LLM cache for faster responses\n- **RAG Utilities**: Tools for building retrieval-augmented generation apps\n- **Framework Integration**: Works with LangChain, LlamaIndex, and more\n\n[Learn more about RedisVL](https://redis.io/docs/latest/develop/ai/redisvl)"},{"id":"langchain-integration","title":"LangChain Integration","role":"content","text":"Use Redis with LangChain for vector stores, semantic caching, and chat message history.\n\n- **Vector Store**: Store and retrieve embeddings for RAG applications\n- **Semantic Cache**: Cache LLM responses based on semantic similarity\n- **Chat History**: Persist conversation history for AI agents\n\n[Learn more about LangChain integration](https://redis.io/docs/latest/integrate/langchain-redis)"},{"id":"client-libraries-with-vector-search","title":"Client Libraries with Vector Search","role":"content","text":"All major Redis client libraries support vector search operations:\n\n- **redis-py (Python)**: [Vector search guide](https://redis.io/docs/latest/develop/clients/redis-py/vecsearch)\n- **node-redis (JavaScript)**: [Vector search guide](https://redis.io/docs/latest/develop/clients/nodejs/vecsearch)\n- **Jedis (Java)**: [Vector search guide](https://redis.io/docs/latest/develop/clients/jedis/vecsearch)\n- **NRedisStack (C#/.NET)**: [Vector search guide](https://redis.io/docs/latest/develop/clients/dotnet/nredisstack/vecsearch)\n- **go-redis (Go)**: [Vector search guide](https://redis.io/docs/latest/develop/clients/go/vecsearch)"},{"id":"getting-started","title":"Getting Started","role":"setup","text":""},{"id":"quick-start-guides","title":"Quick Start Guides","role":"content","text":"- [Redis vector database quick start](https://redis.io/docs/latest/develop/get-started/search-tutorial/vector-search)\n- [RAG quick start guide](https://redis.io/docs/latest/develop/get-started/rag)"},{"id":"tutorials-and-examples","title":"Tutorials and Examples","role":"content","text":"Explore our [AI notebooks collection](https://redis.io/docs/latest/develop/ai/notebook-collection) with examples for:\n\n- RAG implementations with RedisVL, LangChain, and LlamaIndex\n- Advanced RAG techniques and optimizations\n- Integration with cloud platforms like Azure and Vertex AI"},{"id":"video-tutorials","title":"Video Tutorials","role":"content","text":"Watch our [AI video collection](https://redis.io/docs/latest/develop/ai/ai-videos) for practical demonstrations."},{"id":"use-cases","title":"Use Cases","role":"content","text":"- **Retrieval-Augmented Generation (RAG)**: Enhance LLM responses with relevant context\n- **Semantic Search**: Find similar documents, images, or products\n- **Recommendation Systems**: Build real-time personalized recommendations\n- **AI Agents**: Create autonomous agents with memory and tool use\n- **Chatbots**: Build conversational AI with context and history"},{"id":"additional-resources","title":"Additional Resources","role":"content","text":"- [Complete AI documentation](https://redis.io/docs/latest/develop/ai)\n- [Ecosystem integrations](https://redis.io/docs/latest/develop/ai/ecosystem-integrations)\n- [Vector search benchmarks](https://redis.io/blog/benchmarking-results-for-vector-databases/)\n- [RAG best practices](https://redis.io/blog/get-better-rag-responses-with-ragas/)"}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/architecture/classic-vs-flink","title":"Differences between the classic and Flink processors","url":"https://redis.io/docs/latest/integrate/redis-data-integration/architecture/classic-vs-flink/","summary":"Compare the classic and Flink stream processor implementations.","tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-18T16:39:42+01:00","page_type":"content","content_hash":"79390b95e7dbf03e4d710302564a0d20e5837ba7f98c3f564842eae64ab96bde","sections":[{"id":"overview","title":"Overview","role":"overview","text":"RDI ships with two stream processor implementations. Both consume the same\nsource streams, share the same job-level configuration model, and write to\nthe same Redis target, but they differ in architecture, supported features,\nconfiguration, observability, error handling, and performance.\n\nThis page summarizes those differences. See\n[Which processor should I use?](https://redis.io/docs/latest/integrate/redis-data-integration/faq#which-processor-should-i-use)\nin the FAQ for the recommendation, and\n[Migrate from the classic processor to the Flink processor](https://redis.io/docs/latest/integrate/redis-data-integration/installation/migration-classic-to-flink)\nfor a step-by-step migration guide."},{"id":"at-a-glance","title":"At a glance","role":"content","text":"| Aspect | Classic processor | Flink processor |\n|---|---|---|\n| Implementation | Python | Java on top of [Apache Flink](https://flink.apache.org/) |\n| Scaling | Single replica | Horizontal: TaskManager replicas × task slots per TaskManager |\n| Fault tolerance | Source-stream consumer-group replay | Source-stream consumer-group replay plus Flink checkpointing |\n| Metrics endpoint | `rdi-metrics-exporter` service | Flink JobManager `/metrics` (no metrics exporter) |\n| Metric naming | `rdi_*` (e.g., `rdi_incoming_entries`) | `flink_*` (e.g., `flink_jobmanager_job_operator_coordinator_stream_type_rdiRecords`) |\n| End-to-end latency | Bounded by the per-batch read-process-write cycle | Records flow through pipelined operator chains without a per-batch barrier |\n| Snapshot throughput | Limited by single shared reader and writer | Parallelized across all task slots |\n| Expression and `redis.lookup` result caching | Not supported | Optional, opt-in per transformation |"},{"id":"architecture-and-deployment","title":"Architecture and deployment","role":"content","text":"The classic processor runs as a single pod managed by the operator\nand can be deployed on either VMs or Kubernetes through the RDI Helm\nchart.\n\nThe Flink processor runs as an Apache Flink application cluster managed by\nRDI: one JobManager pod plus one or more TaskManager pods. Source,\ntransformation, and sink operators run as parallel subtasks across\nall task slots in the cluster. The Flink processor scales\nhorizontally by changing the number of TaskManager replicas\n(`advanced.resources.taskManager.replicas`); with adaptive\nparallelism, the default parallelism is the product of TaskManager\nreplicas and task slots per TaskManager.\n\nBoth processors retain at-least-once delivery semantics; the Flink\nprocessor adds Flink checkpointing on top of the shared\nconsumer-group replay mechanism.\n\nSee\n[Configure the Flink processor](https://redis.io/docs/latest/integrate/redis-data-integration/installation/install-k8s#configure-the-flink-processor)\nfor the Kubernetes Helm settings, and\n[Configure the Flink processor](https://redis.io/docs/latest/integrate/redis-data-integration/installation/install-vm#configure-the-flink-processor)\nfor VM installations."},{"id":"configuration","title":"Configuration","role":"configuration","text":"The two processors share the same `config.yaml` envelope and the same\n`connections`, `sources`, `targets`, and `jobs` sections. The only\ndifferences are inside the `processors:` block, which is selected via\n`processors.type` (`classic` or `flink`, default `flink`). Properties\nthat apply to only one implementation are annotated with\n**Classic processor only.** or **Flink processor only.** in the\n[pipeline configuration reference](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config#processors),\nand are silently ignored by the other implementation. The Flink\nprocessor exposes additional fine-grained tuning under\n`processors.advanced.*`."},{"id":"transformation-extensions","title":"Transformation extensions","role":"content","text":"The two processors support the same set of transformation blocks\n(`filter`, `map`, `add_field`, `remove_field`, `rename_field`,\n`redis.lookup`), the same expression languages (JMESPath and SQL),\nand the same data types in output blocks: `hash`, `json`, `set`,\n`sorted_set`, `stream`, and `string`. Pipelines written for one processor\ngenerally execute on the other without changes.\n\nThe Flink processor adds three optional, performance-oriented\nextensions that are not available with the classic processor:\n\n-   **Expression result caching** through a per-expression `cache:`\n    block on `filter`, `map`, `add_field`, and `redis.lookup` arguments.\n-   **`redis.lookup` result caching** through a `lookup_cache:` block.\n-   **`redis.lookup` batching**, which groups lookups into a single\n    Redis pipeline. Batching is enabled by default with sensible\n    defaults; the optional `batch:` block lets you override them.\n\nSee\n[Caching expression results](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/caching-expression-results)\nfor examples and\n[`redis.lookup`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation/lookup)\nfor the full property list.\n\nThe Flink processor also accepts an advanced matcher syntax in a job's `source` section, where\n`server_name`, `db`, `schema`, and `table` each accept a list of names, and an entry prefixed\nwith `regex:` selects all names that match the regular expression. One job can then process\nmultiple tables, potentially from different sources, databases, or schemas. See\n[Job files](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples) for more information."},{"id":"metrics","title":"Metrics","role":"content","text":"The two processors expose different Prometheus metric sets and use\ndifferent naming schemes, so dashboards and alerts cannot be reused\nas-is between them. The classic processor exposes its metrics through\nthe `rdi-metrics-exporter` service. The Flink processor emits metrics\ndirectly from the JobManager and TaskManager pods through Flink's\nnative Prometheus reporter; no metrics exporter is deployed.\n\nSee\n[Observability — Flink processor metrics](https://redis.io/docs/latest/integrate/redis-data-integration/observability#flink-processor-metrics)\nfor the customer-facing list of metrics."},{"id":"error-handling-and-dlq","title":"Error handling and DLQ","role":"errors","text":"Both processors implement a dead-letter queue (DLQ) at\n`dlq:{stream_name}` and honor the same top-level `error_handling`\n(`dlq` or `ignore`) and `dlq_max_messages` properties. The Flink\nprocessor surfaces a few corner cases as DLQ entries that the classic\nprocessor logs and skips (for example, missing parent\nkeys in nested writes and exceptions thrown by `when` expressions on\n`redis.lookup`). The DLQ entry field set and value encoding also\ndiffer: the classic processor uses Python-stringified values,\nwhile the Flink processor uses JSON."},{"id":"performance","title":"Performance","role":"performance","text":"The Flink processor delivers significantly higher throughput during\nthe initial snapshot and lower end-to-end latency in steady state.\nThe classic processor uses a sequential read-process-write batching\ncycle, so each record waits for its batch to complete before being\nwritten to the target. The Flink processor pipelines records through\noperator chains without a per-batch barrier, and parallelizes work\nacross all task slots, which both lowers per-record latency and\nraises throughput.\n\nThe Flink processor has a larger baseline memory footprint (JVM plus\nFlink runtime overhead per TaskManager) but, for most pipelines, the\nperformance gains and the additional features (horizontal scaling, caching)\noutweigh that cost."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/architecture","title":"Architecture","url":"https://redis.io/docs/latest/integrate/redis-data-integration/architecture/","summary":"Discover the main components of RDI","aliases":["/integrate/redis-data-integration/ingest/architecture/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-10-18T02:02:34+03:00","children":[{"id":"integrate/redis-data-integration/architecture/classic-vs-flink","summary":"Compare the classic and Flink stream processor implementations.","title":"Differences between the classic and Flink processors","url":"https://redis.io/docs/latest/integrate/redis-data-integration/architecture/classic-vs-flink/"}],"page_type":"content","content_hash":"fa0b30ca4871054d3813633267a4f573d65e793c169ead92e2c4d9330b169e58","sections":[{"id":"overview","title":"Overview","role":"overview","text":"RDI implements a [change data capture](https://en.wikipedia.org/wiki/Change_data_capture) (CDC) pattern that tracks changes to the data in one or\nmore non-Redis *source* databases and makes corresponding changes to a Redis\n*target* database. You can use the target as a cache to improve performance\nbecause it will typically handle read queries much faster than the source.\n\nTo use RDI, you define a *dataset* that specifies which data items\nyou want to capture from each source and how you want to\nrepresent them in the target. For example, if the source is a\nrelational database then you specify which table columns you want\nto capture but you don't need to store them in an equivalent table\nstructure in the target. This means you can choose whatever target\nrepresentation is most suitable for your app. To convert from the\nsource to the target representation, RDI applies *transformations*\nto the data after capture.\n\nRDI synchronizes the dataset between the sources and the target using\na *data pipeline* that implements several processing steps\nin sequence:\n\n1.  A *CDC collector* per source captures changes to the source\n    databases. RDI currently uses an open source collector called\n    [Debezium](https://debezium.io/) for this step.\n\n1.  Each collector records the captured changes using\n[Redis streams](https://redis.io/docs/latest/develop/data-types/streams)\n    in the RDI database.\n\n1.  A *stream processor* reads data from the streams and applies\n    any transformations that you have defined (if you don't need\n    any custom transformations then it uses defaults).\n    It then writes the data to the target database for your app to use.\n\nNote that the RDI control processes run on dedicated virtual machines (VMs)\noutside the Redis\nEnterprise cluster where the target database is kept. However, RDI keeps\nits state and configuration data and also the change data streams in a Redis database on the same cluster as the target. The following diagram shows the pipeline steps and the path the data takes on its way from the source to the target:\n\n![](https://redis.io/docs/latest/images/rdi/ingest/ingest-dataflow.webp)\n\nWhen you first start RDI, the target database is empty and so all\nof the data in the source databases is essentially \"change\" data.\nRDI collects this data in a phase called *initial cache loading*,\nwhich can take minutes or hours to finish, depending on the size\nof the source data. Once the initial cache loading is complete,\nthere is a *snapshot* dataset in the target that will gradually\nchange when new data gets captured from the sources. At this point,\nRDI automatically enters a second phase called *change streaming*, where\nchanges in the data are captured as they happen. Changes are usually\nadded to the target within a few seconds after capture."},{"id":"at-least-once-delivery-guarantee","title":"At-least-once delivery guarantee","role":"content","text":"RDI guarantees *at-least-once delivery* to the target. This means that\na given change will never be lost, but it might be added to the target\nmore than once. Apart from a slight performance overhead, adding a\nchange multiple times is harmless because the multiple writes\nare [*idempotent*](https://en.wikipedia.org/wiki/Idempotence) (that is\nto say that all writes after the first one make no change to the\noverall state).\n\n\nThe delivery guarantee applies to change events captured by RDI. If a Debezium\ncollector's saved source position becomes unavailable, a new snapshot cannot replay\nchanges whose source log history has expired. Missed deletes can leave stale records\nin the target. See the\n[missed-delete timeline](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config#missed-deletes)\nand [recovery guidance](https://redis.io/docs/latest/integrate/redis-data-integration/troubleshooting#unavailable-source-history)."},{"id":"checkpointing","title":"Checkpointing","role":"content","text":"The processor checkpoint is separate from the collector's position in source change\nhistory. See [Understand CDC offsets and source retention](https://redis.io/docs/latest/integrate/redis-data-integration/cdc-offsets-and-retention)\nfor how source retention affects recovery after an interruption.\n\nRDI uses Redis streams to store the sequence of change events\ncaptured from the sources. The events are then retrieved in order\nfrom the streams, processed, and written to the target. The stream\nprocessor uses a *checkpoint* mechanism to keep track of the last\nevent in the sequence that it has successfully processed and stored. If the processor fails\nfor any reason, it can restart from the last checkpoint and\nre-process any events that might not have been written to the target.\nThis ensures that all changes are eventually recorded, even in the\nface of failures."},{"id":"backpressure-mechanism","title":"Backpressure mechanism","role":"content","text":"Sometimes, data records can get added to the streams faster than RDI can\nprocess them. This can happen if the target is slowed or disconnected\nor simply if a source quickly generates a lot of change data.\nIf this continues, then the streams will eventually occupy all the\navailable memory. When RDI detects this situation, it applies a\n*backpressure* mechanism to slow or stop the flow of incoming data.\nChange data is held at the sources until RDI clears the backlog and has\nenough free memory to resume streaming.\n\n> [!NOTE]\n> The Debezium log sometimes reports that RDI has run out\n> of memory (usually while creating the initial snapshot). This is not\n> an error, just an informative message to note that RDI has applied\n> the backpressure mechanism."},{"id":"supported-sources","title":"Supported sources","role":"compatibility","text":"RDI supports the following database sources using [Debezium Server](https://debezium.io/documentation/reference/stable/operations/debezium-server.html) connectors:\n\n| Database | Versions | AWS RDS  Versions | GCP SQL Versions |\n| :-- | :-- | :-- | :-- |\n| Oracle | 19c, 21c, 23ai (LogMiner only) | 19c, 21c | - |\n| MariaDB | 10.5, 11.4.x, 11.7.x | 10.4 to 10.11 | - |\n| MongoDB | 6.0, 7.0, 8.0 | - | - |\n| MySQL | 5.7, 8.0.x, 8.4.x, 9.0, 9.1 | 8.0.x | 8.0 |\n| PostgreSQL | 10, 11, 12, 13, 14, 15, 16, 17, 18 | 11, 12, 13, 14, 15, 16, 17, 18 | 15 |\n| Supabase (uses PostgreSQL) | 10, 11, 12, 13, 14, 15, 16, 17  | - | - |\n| SQL Server | 2017, 2019, 2022 | 2016, 2017, 2019, 2022 | 2019 |\n| Spanner | - | - | All versions |\n| AlloyDB for PostgreSQL | 14.2, 15.7 | - | 14.2, 15.7 |\n| AWS Aurora/PostgreSQL | 15 | 15 | - |\n| Neon | 14, 15, 16, 17 | - | - |\n| Snowflake (preview) | - | - | - |"},{"id":"how-rdi-is-deployed","title":"How RDI is deployed","role":"content","text":"RDI is designed with three *planes* that provide its services.\n\nThe *control plane* contains the processes that keep RDI active.\nIt includes:\n\n-   An *API server* process that exposes a REST API to observe and control RDI.\n-   An *operator* process that manages the *data plane* processes.\n-   A *metrics exporter* process that reads metrics from the RDI database\n    and exports them as [Prometheus](https://prometheus.io/) metrics.\n\nThe *data plane* contains the processes that actually move the data.\nIt includes a *CDC collector* for each source and the *stream processor*, which implement\nthe two phases of the pipeline lifecycle (initial cache loading and change streaming).\n\nThe *management plane* provides tools that let you interact\nwith the control plane.\n\n-   Use the CLI tool to install and administer RDI and to deploy\n    and manage a pipeline.\n-   Use the pipeline editor included in Redis Insight to design\n    or edit a pipeline.\n\nThe diagram below shows all RDI components and the interactions between them:\n\n![](https://redis.io/docs/latest/images/rdi/ingest/ingest-control-plane.webp)"},{"id":"stream-processor-implementations","title":"Stream processor implementations","role":"content","text":"RDI provides two implementations of the stream processor, *classic* and\n*Flink*. You select the implementation per pipeline through the\n[`processors.type`](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config#processors)\nproperty in `config.yaml`. The default is `flink`, so set `classic` explicitly to run a\npipeline on the classic processor.\n\nSee\n[Differences between the classic and Flink processors](https://redis.io/docs/latest/integrate/redis-data-integration/architecture/classic-vs-flink)\nfor a side-by-side comparison and\n[Migrate from the classic processor to the Flink processor](https://redis.io/docs/latest/integrate/redis-data-integration/installation/migration-classic-to-flink)\nfor guidance on migrating an existing pipeline to the Flink processor."},{"id":"vm-and-kubernetes-deployments","title":"VM and Kubernetes deployments","role":"content","text":"The following sections describe the VM configurations you can use to\ndeploy RDI."},{"id":"rdi-on-your-own-vms","title":"RDI on your own VMs","role":"content","text":"For this deployment, you must provide two VMs. The collectors and stream processor\nare active on one VM, while on the other they are in standby to provide high availability.\nThe two operators running on both VMs use a leader election algorithm to decide which\nVM is the active one (the \"leader\").\nThe diagram below shows this configuration:\n\n![](https://redis.io/docs/latest/images/rdi/ingest/ingest-active-passive-vms.webp)\n\nSee [Install on VMs](https://redis.io/docs/latest/integrate/redis-data-integration/installation/install-vm)\nfor more information."},{"id":"rdi-on-kubernetes","title":"RDI on Kubernetes","role":"content","text":"You can use the RDI [Helm chart](https://helm.sh/docs/topics/charts/) to install\non [Kubernetes (K8s)](https://kubernetes.io/), including Red Hat\n[OpenShift](https://docs.openshift.com/). This creates:\n\n-   A K8s [namespace](https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/) named `rdi`.\n    You can also use a different namespace name if you prefer.\n-   [Deployments](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) and\n    [services](https://kubernetes.io/docs/concepts/services-networking/service/) for the\n    [RDI operator](https://redis.io/docs/latest/integrate/redis-data-integration/architecture#how-rdi-is-deployed),\n    [metrics exporter](https://redis.io/docs/latest/integrate/redis-data-integration/observability), and API server.\n-   A [service account](https://kubernetes.io/docs/concepts/security/service-accounts/)\n    and [RBAC resources](https://kubernetes.io/docs/reference/access-authn-authz/rbac) for the RDI operator.\n-   A [ConfigMap](https://kubernetes.io/docs/concepts/configuration/configmap/) with RDI database details.\n-   [Secrets](https://kubernetes.io/docs/concepts/configuration/secret/)\n    with the RDI database credentials and TLS certificates.\n-   Other optional K8s resources such as [ingresses](https://kubernetes.io/docs/concepts/services-networking/ingress/)\n    that can be enabled depending on your K8s environment and needs.\n\nSee [Install on Kubernetes](https://redis.io/docs/latest/integrate/redis-data-integration/installation/install-k8s)\nfor more information."},{"id":"secrets-and-security-considerations","title":"Secrets and security considerations","role":"content","text":"The credentials for the database connections, as well as the certificates\nfor [TLS](https://en.wikipedia.org/wiki/Transport_Layer_Security) and\n[mTLS](https://en.wikipedia.org/wiki/Mutual_authentication#mTLS) are saved in K8s secrets.\nRDI stores all state and configuration data inside the Redis Enterprise cluster\nand does not store any other data on your RDI VMs or anywhere else outside the cluster."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/cdc-offsets-and-retention","title":"Understand CDC offsets and source retention","url":"https://redis.io/docs/latest/integrate/redis-data-integration/cdc-offsets-and-retention/","summary":"Learn why RDI can lose access to source change history during an interruption and how to plan for recovery.","tags":["docs","integrate","rs","rdi"],"last_updated":"2026-10-18T04:42:56+03:00","page_type":"content","content_hash":"839bc007f903023cc7536531e24fd678a19c47f7f99d52ec00c815660faea9c0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Data Integration (RDI) can resume after an interruption only if the source still provides the change history and capture state it needs. Saving a position does not preserve that history.\n\nImagine that RDI stops on Friday and starts again on Sunday. In this example, Oracle is the source database. Your application keeps writing to it during those two days. RDI remembers where it stopped, but Oracle has removed some of the redo logs needed to continue. The collector reports that its saved system change number (SCN) is no longer available.\n\nThe source tables still exist. RDI has not forgotten its position. What is missing is the history between that position and the present.\n\nTwo days is only an example. There is no universal expiration time for an RDI offset. The outcome depends on the database, its settings, the amount of change, and how far capture had already fallen behind."},{"id":"think-of-an-offset-as-a-bookmark","title":"Think of an offset as a bookmark","role":"content","text":"Change data capture (CDC) lets RDI follow changes instead of repeatedly reading the entire source dataset. Four concepts explain how it works:\n\n- A **snapshot** reads the current dataset to establish a starting point.\n- **Change history** records subsequent inserts, updates, and deletes. Depending on the database, this history lives in logs, change tables, or a change stream.\n- An **offset** records the collector's progress through that history. Think of it as a bookmark, not a copy of the pages.\n- **Retention** determines how long, or how much of, that history remains available.\n\nThe source continues maintaining its history while RDI is disconnected. If the required history remains available, RDI can read the missed changes. If the source removes it, the bookmark alone cannot reconstruct those changes.\n\n![Two timelines show RDI resuming when its bookmark is inside retained history and failing when required history has been removed.](https://redis.io/docs/latest/images/rdi/cdc-retention/bookmark.svg)\n\n*These timelines are illustrations, not database defaults. The important boundary is the oldest history still available when capture resumes.*\n\nRDI handles changes in two steps: the collector reads them from the source, then the processor transforms them and writes them to the target. This page focuses on the collector's bookmark. The processor separately tracks its progress so it can recover after a restart. See [Checkpointing](https://redis.io/docs/latest/integrate/redis-data-integration/architecture#checkpointing) for that part of the pipeline."},{"id":"see-how-each-source-keeps-history","title":"See how each source keeps history","role":"content","text":"Each source has its own retention rules. These are source-database settings, not an RDI memory limit. A size limit usually means space on disk. The examples assume capture was working before the interruption."},{"id":"oracle-keep-a-complete-readable-redo-chain","title":"Oracle: keep a complete, readable redo chain","role":"content","text":"**Retention controls: cleanup rules and disk space.** There is no single \"keep CDC for this many hours\" setting. Backup and cleanup jobs can remove archived logs based on their age or backup status. Oracle can also free disk space in its fast recovery area by removing logs that its deletion rules allow. Check both the [archive deletion policy](https://docs.oracle.com/en/database/oracle/oracle-database/19/bradv/configuring-rman-client-basic.html#GUID-FA142760-8ACB-43DA-939C-8407618D2D0F) and any scheduled cleanup jobs.\n\nOracle records changes in redo logs. With LogMiner, RDI reads online and archived redo and tracks system change numbers (SCNs). An SCN does not expire as a number. The problem is whether Oracle can provide the history needed from that position.\n\nCleanup can remove an old archive, but a gap in the middle also matters. The file containing the saved SCN and the newest files can all exist while a required file between them is missing. With Oracle Real Application Clusters (RAC), mining needs the required redo from all relevant threads.\n\n![Oracle archive files form a complete readable chain, or a missing middle file prevents continuation even though the saved SCN's file still exists.](https://redis.io/docs/latest/images/rdi/cdc-retention/oracle.svg)\n\nUnavailable does not always mean deleted. Archiving might be delayed, or the archive destination might be inaccessible. Check availability before choosing a fresh snapshot. See [Oracle LogMiner requirements](https://docs.oracle.com/en/database/oracle/oracle-database/19/sutil/oracle-logminer-utility.html).\n\nThe logs also need matching table definitions. For example, an old change might refer to a column that has since been renamed or removed. LogMiner can fail to read that change if it uses today's table definition. Keeping more logs does not fix this. See the [LogMiner strategy guidance](https://debezium.io/documentation/reference/3.5/connectors/oracle.html#oracle-logminer-mining-strategies).\n\nAn outage is not the only risk. A long transaction can hold the restart position in older redo while other transactions continue. Quiet captured activity can also leave the saved offset behind. Monitor the saved position, not just recent events or process uptime. Use the [connector's offset and heartbeat guidance](https://debezium.io/documentation/reference/3.5/connectors/oracle.html#low-change-frequency-offset-management) for your Oracle configuration.\n\n![A long-running Oracle transaction holds an older restart position while other transactions commit. Removing that older redo makes a later restart unsafe.](https://redis.io/docs/latest/images/rdi/cdc-retention/oracle-transactions.svg)\n\nTwo other cases need a different diagnosis. During a snapshot, a “snapshot too old” error concerns [undo history for consistent reads](https://docs.oracle.com/en/database/oracle/oracle-database/19/refrn/UNDO_RETENTION.html), not streaming redo retention. With XStream, recovery also depends on Oracle's capture checkpoints and outbound-server state; a saved client position alone is insufficient. See [Prepare Oracle](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/oracle) and [XStream restartability](https://docs.oracle.com/en/database/oracle/oracle-database/19/xstrm/xstream-out-concepts.html)."},{"id":"mysql-and-mariadb-preserve-files-and-source-continuity","title":"MySQL and MariaDB: preserve files and source continuity","role":"content","text":"**Retention controls: an age limit, automatic cleanup, and manual or scheduled purges.** [MySQL](https://dev.mysql.com/doc/refman/8.0/en/replication-options-binary-log.html#sysvar_binlog_expire_logs_seconds) and MariaDB can expire old log files. Cleanup can happen at server startup or when the binary log is flushed, including during rotation. It does not necessarily happen at the exact expiry time. In MySQL 8.0.29 and later, `binlog_expire_logs_auto_purge` can disable automatic cleanup even when an age limit is set. [MariaDB 11.4 and later](https://mariadb.com/docs/server/server-management/server-monitoring-logs/binary-log/using-and-maintaining-the-binary-log#limiting-the-binlog-size) can also limit the total log size on disk. A limit on each individual file's size only controls rotation; it does not set the total retention window.\n\nMySQL and MariaDB record changes in binary logs, often called *binlogs*. A bookmark can name a file and a position inside it. Rotating to a new file is normal. Older files must remain available until capture has read them.\n\nExpiration or manual purging can remove required events. Resetting the binary logs can remove history immediately, regardless of the planned retention window. The source tables can remain intact throughout.\n\n![Binary-log file rotation preserves unread events, purging a required file removes them, and switching servers requires checking whether the saved position maps to the new source history.](https://redis.io/docs/latest/images/rdi/cdc-retention/mysql-mariadb.svg)\n\nA source switch is a separate risk. File positions belong to a particular server's history. Global transaction identifiers (GTIDs) help identify transactions across servers; they do not recreate deleted events. Before a migration or failover, check that the replacement source has the required history and that your RDI configuration can resume from it.\n\nReading older changes also needs the right **schema history**: the record of how tables and columns changed. Rewinding across a column addition, rename, or removal can fail or produce wrong fields if that history does not match. Having the log files is not enough. See [how the connector uses schema history](https://debezium.io/documentation/reference/3.5/connectors/mysql.html#mysql-schema-history-topic).\n\nSize retention and storage for peak writes. Coordinate purges and log resets with capture. See [Prepare MySQL and MariaDB](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/my-sql-mariadb), [MySQL purging](https://dev.mysql.com/doc/refman/8.0/en/purge-binary-logs.html), [MySQL log resets](https://dev.mysql.com/doc/refman/8.4/en/reset-binary-logs-and-gtids.html), and [MariaDB transaction positioning](https://mariadb.com/docs/server/ha-and-performance/standard-replication/gtid)."},{"id":"postgresql-preserve-wal-and-slot-state","title":"PostgreSQL: preserve WAL and slot state","role":"content","text":"**Retention controls: slot progress, an optional disk-size limit, and an optional idle-time limit.** A slot normally keeps the history its reader still needs, rather than keeping a fixed number of days. Size limits and idle timeouts depend on the PostgreSQL version. They take effect at database checkpoints, when PostgreSQL saves recovery state. See [version-specific retention controls](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/postgresql#manage-wal-retention).\n\nPostgreSQL records changes in its write-ahead log (WAL). RDI uses a logical replication slot and tracks a log sequence number (LSN). A valid slot can retain needed WAL while RDI is stopped. Disk usage can therefore grow throughout an outage. A full disk is a storage failure, not an expired LSN.\n\nTwo configured limits can break continuity differently. A finite slot size limit can allow required WAL to be removed at a checkpoint. PostgreSQL 18 can also invalidate an inactive slot at a checkpoint after an enabled idle timeout, even if WAL files remain. That timeout is disabled by default.\n\n![PostgreSQL shows a slot retaining growing WAL, a size limit followed by checkpoint-time WAL removal, and a separate idle timer that invalidates the slot even when WAL remains.](https://redis.io/docs/latest/images/rdi/cdc-retention/postgresql.svg)\n\nKeeping WAL is only part of recovery. Dropping and recreating a slot does not restore its old decoding state. After failover, the new primary needs usable slot state and the required history. A routine restart of the same database does not itself remove a persistent slot.\n\nResuming is also different from rewinding. The slot remembers which changes the collector has confirmed as received. Moving RDI's bookmark backward does not make that same slot send changes from before its confirmed position, even if the old WAL files still exist. See [the slot's confirmed position](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/postgresql#manage-wal-retention).\n\n![An original PostgreSQL primary has saved slot state, while a replacement primary has WAL but lacks that slot state. Reusing the slot name does not restore it.](https://redis.io/docs/latest/images/rdi/cdc-retention/postgresql-slot-state.svg)\n\nMonitor slot health, retained WAL, and free disk together. See [Manage WAL retention](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/postgresql#manage-wal-retention) for version-specific controls and [PostgreSQL failover guidance](https://www.postgresql.org/docs/18/logical-replication-failover.html) for slot continuity. Supabase, Neon, AlloyDB, Amazon Aurora PostgreSQL, and other managed variants can have additional cleanup, storage, and failover policies. Do not assume they preserve every idle slot."},{"id":"sql-server-watch-both-capture-stages","title":"SQL Server: watch both capture stages","role":"content","text":"**Retention controls: a time window and a scheduled cleanup job.** The window is configured in minutes. The cleanup job removes old rows from the CDC change tables when it runs. This is not a memory-size limit; the tables also need enough disk space. See [CDC cleanup jobs](https://learn.microsoft.com/en-us/sql/relational-databases/track-changes/administer-and-monitor-change-data-capture-sql-server#cleanup-job).\n\nSQL Server first captures transaction-log changes into CDC change tables. RDI then reads those tables using an LSN-based position. If source capture stops, newer changes cannot reach RDI yet and the transaction log can grow. If RDI falls behind while capture continues, CDC cleanup can remove rows it still needs.\n\n![SQL Server copies the transaction log into CDC tables before RDI reads them. Cleanup can overtake RDI's saved LSN, or maintenance can delete the old capture instance and its table.](https://redis.io/docs/latest/images/rdi/cdc-retention/sql-server.svg)\n\nHistory can also disappear during maintenance. Disabling a capture instance deletes its change table, even inside the retention window. A new instance does not recreate those old rows. Follow the [schema-change guidance](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/sql-server#drop-the-old-capture-table) before removing an old instance.\n\nMonitor source capture and RDI reading separately. Keeping transaction-log backups alone does not make cleaned-up CDC rows available to this reader. See Microsoft's [CDC administration](https://learn.microsoft.com/en-us/sql/relational-databases/track-changes/administer-and-monitor-change-data-capture-sql-server) and [capture-instance removal](https://learn.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sys-sp-cdc-disable-table-transact-sql) references."},{"id":"mongodb-account-for-write-volume-and-old-values","title":"MongoDB: account for write volume and old values","role":"content","text":"**Retention controls: log size, with an optional minimum time in hours.** MongoDB normally removes the oldest entries automatically as its operation log fills. A minimum retention time can make the log grow beyond its configured size. It is a minimum age to preserve, not a deadline to delete. Check the [oplog settings](https://www.mongodb.com/docs/manual/core/replica-set-oplog/) and your hosting service's limits.\n\nMongoDB change streams provide a **resume token**. Resuming requires enough history in the replica set's operation log, or *oplog*. For a size-limited oplog, a busy day covers fewer hours than a quiet day. The same weekend interruption can therefore succeed once and fail after a large write burst.\n\n![Two equal-size MongoDB oplogs cover different periods. Quiet writes preserve Friday's token through Sunday; busy writes leave only Sunday's history.](https://redis.io/docs/latest/images/rdi/cdc-retention/mongodb.svg)\n\nSome pipelines also need a document's old values, called a **pre-image**. Custom Redis keys can depend on fields that changed or were deleted. An event can remain in the oplog after its pre-image is gone. A usable resume token does not guarantee that all required old values remain.\n\nPre-images have separate cleanup rules: they can expire by age, or disappear when the matching event leaves the oplog. Background cleanup removes them.\n\n![MongoDB retains a change event in the oplog while its document pre-image is unavailable. This can affect pipelines using custom Redis keys without making the resume token invalid.](https://redis.io/docs/latest/images/rdi/cdc-retention/mongodb-pre-images.svg)\n\nReading an old event does not always return the document as it looked then. When the collector uses a [full-document lookup](https://www.mongodb.com/docs/manual/changestreams/#lookup-full-document-for-update-operations), it gets the document's values at lookup time. For example, an older update can return Sunday's values instead of Friday's.\n\nMonitor the actual oplog time window and any required pre-images. Dropping or renaming watched objects can also [invalidate a stream, depending on its watch scope](https://www.mongodb.com/docs/manual/reference/change-events/invalidate/); this is separate from history expiry. See [Prepare MongoDB](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/mongodb) and [pre-image retention](https://www.mongodb.com/docs/manual/changestreams/#change-streams-with-document-pre--and-post-images)."},{"id":"spanner-distinguish-two-retention-windows","title":"Spanner: distinguish two retention windows","role":"content","text":"**Retention controls: two time windows, with automatic cleanup.** You set how long to keep change-stream records and, separately, old database versions. These are durations, not memory or log-size limits. Spanner manages cleanup; you do not schedule a cleanup job. See [change-stream retention](https://docs.cloud.google.com/spanner/docs/change-streams/manage) and [database version retention](https://docs.cloud.google.com/spanner/docs/timestamp-bounds#maximum_timestamp_staleness).\n\nSpanner has two independent histories. **Database version retention** keeps older row versions for RDI's fixed-timestamp snapshot. A slow or interrupted snapshot can lose access to that timestamp. **Change-stream retention** keeps change records for streaming. Increasing one window does not extend the other.\n\nKeep both retention durations in [RDI's source configuration](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/spanner#5-configure-rdi-for-spanner) in sync with the database settings. RDI uses its configured durations for retention checks; changing only the database settings does not update RDI.\n\n![Spanner compares two separate histories: both can cover the work, snapshot row versions can expire while change records remain, or streaming history can expire after a completed snapshot.](https://redis.io/docs/latest/images/rdi/cdc-retention/spanner.svg)\n\nChanges to the stream can remove history before an outage ends. Reducing retention makes records outside the new window unavailable immediately. Dropping a stream deletes its records; recreating the same name does not restore them.\n\nAlso preserve what the stream watches. Suspending capture or excluding a required table or operation means those changes were never recorded. Longer retention cannot recover events that were never captured.\n\nPlan version history for snapshot duration and change-stream history for lag, interruptions, and catch-up. See [Prepare Spanner](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/spanner), [timestamp limits](https://docs.cloud.google.com/spanner/docs/timestamp-bounds#maximum_timestamp_staleness), and [change-stream management](https://docs.cloud.google.com/spanner/docs/change-streams/manage)."},{"id":"snowflake-preserve-table-history-and-identity","title":"Snowflake: preserve table history and identity","role":"content","text":"**Retention controls: time in days, with automatic management.** On the source table, `DATA_RETENTION_TIME_IN_DAYS` sets how long Snowflake keeps old table versions through its **Time Travel** feature. `MAX_DATA_EXTENSION_TIME_IN_DAYS` limits how far Snowflake can extend retention to protect streams; its default is 14 days. Check both settings and any extension held by RDI's existing stream. Do not assume that every saved timestamp has 14 days of protection. These controls use storage, not a stream memory limit. See [Snowflake retention rules](https://docs.snowflake.com/en/user-guide/streams-intro#data-retention-period-and-staleness).\n\nRDI's bookmark is a saved table timestamp. When RDI resumes, it creates a stream at that timestamp to read the changes since then. It also recreates the stream when fetching later batches. If the required history is gone, creating the stream at the saved timestamp fails. Creating a new stream does not bring back old history.\n\n![Snowflake lets RDI recreate a stream when its saved timestamp is still available. Creation fails when that history has expired or when the timestamp predates a replacement table's history.](https://redis.io/docs/latest/images/rdi/cdc-retention/snowflake.svg)\n\nReplacing a table is another risk. RDI creates streams using the configured table name, which can now refer to the replacement. The name alone does not prove that the original history is still available. If the saved timestamp predates the replacement table's history, RDI cannot create the stream at that time.\n\nMonitor how old RDI's saved timestamp is against the history still available for that table. The existing stream's `STALE_AFTER` value is a useful warning, but it does not prove that RDI can recreate a stream from its saved timestamp. RDI manages its streams; do not manually consume or replace them to clear an error.\n\nRDI also checks whether streams have data. Snowflake can protect an empty stream from staleness when that check returns false. This does not protect pending changes during a collector outage.\n\nSnowflake support is in private preview. See [Prepare Snowflake](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/snowflake), [creating a stream at a past timestamp](https://docs.snowflake.com/en/sql-reference/sql/create-stream#usage-notes), and [empty-stream checks](https://docs.snowflake.com/en/sql-reference/functions/system_stream_has_data#usage-notes)."},{"id":"plan-for-the-whole-interruption","title":"Plan for the whole interruption","role":"content","text":"RDI does not have to be completely down for history to become unavailable. A network failure, lost source access, or sustained capture lag can leave its position behind. A slow target can build a backlog until [backpressure](https://redis.io/docs/latest/integrate/redis-data-integration/architecture#backpressure-mechanism) stops capture. Source cleanup continues independently.\n\nUse a conservative planning budget:\n\n**Existing capture lag + longest expected interruption + catch-up allowance + safety margin.**\n\nThis is a planning aid, not a universal database setting. For example, a 48-hour outage budget needs more than 48 hours of retained history if capture was already behind. Catch-up also needs spare processing capacity while the application continues writing.\n\nBefore maintenance, check capture progress, available history, and storage. After restoring or replacing a source, also verify that the saved offset still belongs to its history and capture state. For size-based limits, estimate how much history peak writes produce, not just average writes. Where history is retained by a slot, check how much additional disk an outage could consume.\n\nAlert while there is still time to act. Combine [RDI monitoring](https://redis.io/docs/latest/integrate/redis-data-integration/observability) with source-database checks. A running process or a saved offset does not prove that capture is advancing or that its history remains available. Confirm managed-service limits and cleanup policies with the database owner."},{"id":"recover-when-the-history-is-gone","title":"Recover when the history is gone","role":"content","text":"First identify whether required history is missing or temporarily inaccessible. A permissions or archive-access problem needs a different correction from deleted history.\n\nIf the database can make the original history available again through a supported procedure, resuming may still be possible. Otherwise, rebuilding from a fresh snapshot establishes a new starting point. Increasing retention protects future history; it cannot recreate removed records.\n\nChanging an offset does not bring back missing history either. An offset can record both a log position and progress within a group of changes. Editing only part of it can skip or repeat changes. Rewinding can send events again; moving forward skips events. Restoring an old offset does not undo changes already written to Redis.\n\nA schema-history rebuild is different from a data snapshot. It reads current table definitions, not the rows. It cannot recreate older definitions needed to read old changes, or repair data already written with wrong fields. See the [connector's schema-recovery restrictions](https://debezium.io/documentation/reference/3.5/connectors/mysql.html#mysql-property-snapshot-mode).\n\nA snapshot reads what exists now. It does not recover every intermediate event. Consider a row that existed in Redis on Friday but was deleted from the source on Saturday: Sunday's snapshot contains no row to overwrite that old Redis key. Recovery must account for such stale target data as well as current source rows.\n\nUse the [pipeline reset guidance](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/deploy#reset-a-pipeline) with your source's snapshot configuration and a plan to remove stale target data. Do not move an offset forward just to silence an error. Verify actual field values, inserts, updates, and deletes before relying on the target again; matching row counts alone is not enough."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/data-denormalization","title":"Data denormalization","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/data-denormalization/","summary":"Learn about denormalization strategies","aliases":["/integrate/redis-data-integration/ingest/data-pipelines/data-denormalization/","/integrate/redis-data-integration/data-transformation/data-denormalization/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-10-18T03:56:52+03:00","page_type":"content","content_hash":"c283b01303b72d56921886ded9145213bacf55864aecd6695a366a69a010a0de","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The data in the source database is often\n[*normalized*](https://en.wikipedia.org/wiki/Database_normalization).\nThis means that columns can't have composite values (such as arrays) and relationships between entities\nare expressed as mappings of primary keys to foreign keys between different tables.\nNormalized data models reduce redundancy and improve data integrity for write queries but this comes\nat the expense of speed.\nA Redis cache, on the other hand, is focused on making *read* queries fast, so RDI provides data\n*denormalization* to help with this.\n\nThe supported denormalization techniques are joining\n[one-to-one relationships](#joining-one-to-one-relationships) (using `merge`) and\njoining [one-to-many relationships](#joining-one-to-many-relationships) (using nesting),\nboth described below.\n\n> [!NOTE]\n> The [`redis.lookup`](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-lookup-example)\n> transformation is *not* a supported way to denormalize data that RDI ingests. RDI\n> can't guarantee that a key written by one job is present or up to date when another\n> job looks it up, so lookups against pipeline-populated data can miss or return stale\n> values. Use the techniques on this page instead. See\n> [Reading Redis data with redis.lookup](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-lookup-example)\n> for details."},{"id":"joining-one-to-one-relationships","title":"Joining one-to-one relationships","role":"content","text":"You can join one-to-one relationships by making more than one job to write to the same Redis key.\n\nFirst, you must configure the parent entity to use `merge` as the `on_update` strategy.\n\n[code example]\n\nThen, you can configure the child entity to write to the same Redis key as the parent entity. You can do this by using the `key` attribute in the `with` block of the job, as shown in this example:\n\n[code example]\n\nThe joined data will look like this in Redis:\n\n[code example]\n\n> [!NOTE]\n> If you don't set `merge` as the `on_update` strategy for all jobs targeting the same key, the entire parent record in Redis will be overwritten whenever any related record in the source database is updated. This will result in the loss of values written by other jobs.\n\nWhen using this approach, you must ensure that the `key` expression in the child job matches the key expression in the parent job. If you use a different key expression, the child data will not be written to the same Redis key as the parent data.\n\nIn the example above, the `addresses` job uses the default key pattern to write to the same Redis key as the `customers` job. You can find more information about the default key pattern [here](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-set-key-name).\n\nYou can also use custom keys for the parent entity, as long as you use the same key for all jobs that write to the same Redis key.\n\n> [!NOTE]\n> If you are using the same key for different jobs, deleting any of the entities will result in the key being removed from the target.\n> For an example workaround, see [Write to the same key from multiple jobs](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-write-same-key)."},{"id":"joining-one-to-many-relationships","title":"Joining one-to-many relationships","role":"content","text":"To join one-to-many relationships, you can use the *Nesting* strategy.\nWith this, the parent object (the \"one\") is represented as a JSON document with the children (the \"many\") nested inside it as a JSON map attribute. The diagram below shows a nesting with the child objects in a map called `InvoiceLineItems`:\n\n![](https://redis.io/docs/latest/images/rdi/ingest/nest-flow.webp)\n\n\nTo configure normalization, you must first configure the parent entity to use JSON as the target data type. Add `data_type: json` to the parent job as shown in the example below:\n\n[code example]\n\nAfter you have configured the parent entity, you can then configure the child entities to be nested under it, based on their relation type. To do this, use the `nest` block, as shown in this example:\n\n[code example]\n\nThe job has a `with` section under `output` that includes the `nest` block.\nThe job must include the following attributes in the `nest` block:\n\n- `parent`: This specifies the config of the parent entities. You only\n  need to supply the parent `table` name. Note that this attribute refers to a Redis *key* that will be added to the target\n  database, not to a table you can access from the pipeline. See [Using nesting](#using-nesting) below\n  for the format of the key that is generated.\n- `nesting_key`: The unique key of each child entry in the JSON map that will be created under the path.\n- `parent_key`: The field in the parent entity that stores the unique ID (foreign key) of the parent entity. This can't be a composite key.\n- `child_key`: The field in the child entity that stores the unique ID (foreign key) to the parent entity. You only need to add this attribute if the name of the child's foreign key field is different from the parent's. This can't be a composite key.\n- `path`: The [JSONPath](https://goessner.net/articles/JsonPath/)\n  for the map where you want to store the child entities. The path must start with the `$` character, which denotes\n  the document root.\n- `structure`: (Optional) The type of JSON nesting structure for the child entities. Currently, only a JSON map\n  is supported so if you supply this attribute then the value must be `map`."},{"id":"using-nesting","title":"Using nesting","role":"content","text":"There are several important things to note when you use nesting:\n\n- When you specify `nest` in the job, you must also set the `data_type` attribute to `json` and\n  the `on_update` attribute to `merge` in the surrounding `output` block.\n- Key expressions are *not* supported for the `nest` output blocks. The parent key is always calculated\n  using the following template:\n\n  [code example]\n  \n  For example:\n  \n  [code example]\n\n- If you specify `expire` in the `nest` output block then this will set the expiration on the *parent* object.\n- You can only use one level of nesting.\n- If you are using PostgreSQL then you must make the following change for all child tables that you want to nest:\n  \n  [code example]\n  \n  Nesting needs the child's `parent_key` to identify the parent Redis document, including\n  when the child is deleted. The default replica identity does not provide the full old\n  row, so a non-key `parent_key` can be unavailable. `FULL` records all old column values\n  in the write-ahead log (WAL). It can increase WAL volume and reduce performance.\n  See [Which PostgreSQL row values are available](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/postgresql#understand-which-row-values-are-available)\n  for the difference between old row values and unchanged large values.\n- Prior to RDI v1.12.1, there is a known limitation if you change the foreign key of a child object. In that scenario, the child object will be added to the new parent, but the old parent will not be updated."}],"examples":[{"id":"joining-one-to-one-relationships-ex0","language":"yaml","code":"# jobs/customers.yaml\nsource:\n  table: customers\n\noutput:\n  - uses: redis.write\n    with:\n      data_type: json\n      on_update: merge","section_id":"joining-one-to-one-relationships"},{"id":"joining-one-to-one-relationships-ex1","language":"yaml","code":"# jobs/addresses.yaml\nsource:\n  table: addresses\n\ntransform:\n  - uses: add_field\n    with:\n      field: customer_address\n      language: jmespath\n      # You can use the following JMESPath expression to create a JSON object and combine the address fields into a single object.\n      expression: |\n        {\n          \"street\": street,\n          \"city\": city,\n          \"state\": state,\n          \"zip\": zip\n        }\n\noutput:\n  - uses: redis.write\n    with:\n      data_type: json\n      # We specify the key to write to the same key as the parent entity.\n      key:\n        expression: concat(['customers:id:', customer_id])\n        language: jmespath\n      on_update: merge\n      mapping:\n        # You can specify one or more fields to write to the parent entity.\n        - customer_address: customer_address","section_id":"joining-one-to-one-relationships"},{"id":"joining-one-to-one-relationships-ex2","language":"json","code":"{\n  \"id\": \"1\",\n  \"first_name\": \"John\",\n  \"last_name\": \"Doe\",\n  \"customer_address\": {\n    \"street\": \"123 Main St\",\n    \"city\": \"Anytown\",\n    \"state\": \"CA\",\n    \"zip\": \"12345\"\n  }\n}","section_id":"joining-one-to-one-relationships"},{"id":"joining-one-to-many-relationships-ex0","language":"yaml","code":"# jobs/invoice.yaml\nsource:\n  schema: public\n  table: Invoice\n\noutput:\n  - uses: redis.write\n    with:\n      # Setting the data type to json ensures that the parent object will be created in a way that supports nesting.\n      data_type: json\n      # Important: do not set a custom key for the parent entity.\n      # When nesting the child object under the parent, the parent key is automatically calculated based on\n      # the parent table name and the parent key field and if a custom key is set, it will cause a mismatch\n      # between the key used to write the parent and the key used to write the child.","section_id":"joining-one-to-many-relationships"},{"id":"joining-one-to-many-relationships-ex1","language":"yaml","code":"# jobs/invoice_line.yaml\nsource:\n  schema: public\n  table: InvoiceLine\noutput:\n  - uses: redis.write\n    with:\n      nest: # cannot co-exist with other parameters such as 'key'\n        parent:\n          # schema: public\n          table: Invoice\n        nesting_key: InvoiceLineId # the unique key in the composite structure under which the child data will be stored\n        parent_key: InvoiceId\n        child_key: InvoiceId # optional, if different from parent_key\n        path: $.InvoiceLineItems # path must start from document root ($)\n        structure: map # only map supported for now\n      on_update: merge # only merge supported for now\n      data_type: json # only json supported for now","section_id":"joining-one-to-many-relationships"},{"id":"using-nesting-ex0","language":"bash","code":"<nest.parent.table>:<nest.parent_key>:<nest.parent_key.value | nest.child_key.value>","section_id":"using-nesting"},{"id":"using-nesting-ex1","language":"bash","code":"Invoice:InvoiceId:1","section_id":"using-nesting"},{"id":"using-nesting-ex2","language":"sql","code":"ALTER TABLE <TABLE_NAME> REPLICA IDENTITY FULL;","section_id":"using-nesting"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/deploy","title":"Deploy a pipeline","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/deploy/","summary":"Learn how to deploy an RDI pipeline","aliases":["/integrate/redis-data-integration/ingest/data-pipelines/data-type-handling/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-10-13T08:44:20+03:00","page_type":"content","content_hash":"b3d34307f75430cdb808bc44707846884b0436de696041f6fdf8e99909e329e3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The sections below explain how to deploy a pipeline after you have created the required\n[configuration](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines)."},{"id":"set-secrets","title":"Set secrets","role":"content","text":"Before you deploy your pipeline, you must set the authentication secrets for the source\nand target databases. Every secret belongs to one database: a source, identified by its\nname in `config.yaml`, or the target. You name that database with the `--db` option of the\n[`redis-di set-secret`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-set-secret)\ncommand.\n\nThe table below lists the available secret keys. The username and password are required,\nwhile the other keys are only relevant for TLS/mTLS connections.\n\n| Secret key | Description |\n| :-- | :-- |\n| `USERNAME` | Username for the database |\n| `PASSWORD` | Password for the database |\n| `CACERT` | (For TLS only) CA certificate |\n| `CERT` | (For mTLS only) Client certificate |\n| `KEY` | (For mTLS only) Private key |\n| `KEY_PASSWORD` | (For mTLS only) Private key password |\n\nYou can reference a secret in `config.yaml` using an environment variable that is derived from\nthe secret key and the database name. The variable name consists of the database name in\nuppercase (with each dash replaced by an underscore), followed by `_DB_`, followed by the key.\nFor example, if you set `PASSWORD` with `--db mysql` the corresponding environment variable\nis `MYSQL_DB_PASSWORD`, which the source references as\n`${MYSQL_DB_PASSWORD}`. If you set `PASSWORD` with `--db target`, the environment variable is `TARGET_DB_PASSWORD`. The sample\n[config.yaml file](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config#example)\nshows these references in use, and\n[Multiple sources in one pipeline](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/multiple-sources)\ncovers the source naming rules.\n\n> [!NOTE]\n> The scope-prefixed keys `SOURCE_DB_*` and `TARGET_DB_*` are also accepted, and can be\n> used without specifying `--db`. RDI accepts a `SOURCE_DB_*` key only for a pipeline with exactly\n> one source. Prefer the per-database keys with `--db`, which work for any pipeline. See\n> [Existing names are kept after an upgrade](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/multiple-sources#existing-names-are-kept-after-an-upgrade) for more information.\n\n> [!NOTE]\n> When creating secrets for TLS or mTLS, ensure that all certificates and keys are in `PEM` format. The only exception to this is that for PostgreSQL, the source's private key secret must be in `DER` format. If you have a key in `PEM` format, you must convert it to `DER` before creating the secret using the command:\n\n[code example]\n\nThis command assumes that the private key is not encrypted. See the [`openssl` documentation](https://docs.openssl.org/master/) to learn how to convert an encrypted private key."},{"id":"set-secrets-with-the-cli","title":"Set secrets with the CLI","role":"content","text":"Use [`redis-di set-secret`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-set-secret)\nto set secrets for any installation type (VM, Kubernetes, or Redis Cloud).\n\nThe command lines for a source named `mysql` are shown below. If your pipeline has multiple sources, you should run the command once for each source, using the appropriate database name.\n\n[code example]\n\nThe corresponding command lines for target secrets are:\n\n[code example]\n\nBy default, `set-secret` waits for the pipeline to apply the change before returning. When you set\nseveral secrets at once, set all but the last one with `--wait=false` to avoid a timeout while the\npipeline is only partially updated. See [Wait for changes to complete](#wait) below for details."},{"id":"manage-secrets-with-the-cli","title":"Manage secrets with the CLI","role":"content","text":"Along with `set-secret`, the CLI has commands to list, inspect, and delete secrets. Because the API\nnever returns secret values, these commands show only the secret keys and whether they are set, not\nthe stored values.\n\n[code example]\n\nSee the reference pages for\n[`list-secrets`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list-secrets),\n[`get-secret`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-get-secret),\n[`describe-secret`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-describe-secret),\nand [`delete-secret`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-delete-secret)\nfor the full list of options."},{"id":"set-secrets-for-k8shelm-deployment-using-kubectl-command","title":"Set secrets for K8s/Helm deployment using Kubectl command","role":"content","text":"> [!NOTE]\n> It is strongly recommended to manage secrets with the `redis-di` CLI rather than with\n> `kubectl` directly. The CLI applies the correct labels automatically, validates the secret keys, and\n> works the same way across all installation types.\n\nFor a Kubernetes/Helm deployment, you can also use [`kubectl create secret generic`](https://kubernetes.io/docs/reference/kubectl/generated/kubectl_create/kubectl_create_secret_generic/)\nto set secrets instead of the CLI. The general pattern of the commands is:\n\n[code example]\n\nWhere `<DB>` is `<source>-db` for the secrets of a source, or `target-db` for target secrets.\nThe examples below use a source named `mysql`, so its secret is `mysql-db`.\n\nIf you use TLS or mTLS for either the source or target databases, you also need to create the\n`<source>-db-ssl` and/or `target-db-ssl` K8s secrets that contain the certificates used\nto establish secure connections. The general pattern of the commands is:\n\n[code example]\n\nThe specific command lines for source secrets are as follows:\n\n[code example]\n\nThe corresponding command lines for target secrets are:\n\n[code example]\n\nNote that the certificate paths contained in the `CACERT`, `CERT`, and `KEY` secrets are internal to RDI, so you *must* use the values shown in the example above. Each source has its own certificate directory, named after the source (for example, `/etc/certificates/mysql_db/` for a source named `mysql`). You should only change the certificate paths when you create the `<source>-db-ssl` and `target-db-ssl` secrets.\n\nYou must also label any secrets that you create directly with `kubectl` so that the RDI operator\ndiscovers them as pipeline secrets. Give each secret the following labels, where the\n`app.kubernetes.io/instance` label corresponds to the pipeline name (the name is just\n`default` for the default pipeline):\n\n| Label | Value |\n| :-- | :-- |\n| `app.kubernetes.io/name` | `pipeline` |\n| `app.kubernetes.io/instance` | `default` |\n| `product` | `rdi` |\n\nApply the labels to each secret with [`kubectl label`](https://kubernetes.io/docs/reference/kubectl/generated/kubectl_label/):\n\n[code example]"},{"id":"deploy-a-pipeline","title":"Deploy a pipeline","role":"content","text":"When you have created your configuration, including the [jobs](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples), you are\nready to deploy. Use the\n[`redis-di deploy`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-deploy)\ncommand to deploy a pipeline:\n\n[code example]\n\nRDI first validates the configuration and then deploys it if it is correct. You can control the\nvalidation and what happens after deployment with the following options:\n\n- `--dry-run`: Validate the configuration without deploying it. Off by default.\n- `--validate-tables`: Validate the configuration against the source and target databases, for\n  example that the tables it references exist. On by default; pass `--validate-tables=false` to skip\n  this check, which is useful when the databases are not reachable at deploy time.\n- `--validate-cdc`: Additionally validate that the source database is correctly configured for\n  [change data capture (CDC)](https://redis.io/docs/latest/integrate/redis-data-integration/architecture#overview).\n  Off by default; enable it with `--validate-cdc`.\n- `--start`: Start the pipeline as soon as it is deployed. On by default; pass `--start=false` to\n  deploy the pipeline without starting it, then start it later with\n  [`redis-di start`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-start).\n\nSee the [`redis-di deploy`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-deploy)\nreference page for the full list of options.\n\nYou can also use [Redis Insight](https://redis.io/docs/latest/develop/tools/insight/rdi-connector)\nto configure and deploy pipelines for both VM and K8s installations."},{"id":"display-the-pipeline-status","title":"Display the pipeline status","role":"content","text":"Once a pipeline is deployed, use the\n[`redis-di describe`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-describe)\ncommand (also available as `redis-di status`) to display its status. This combines the pipeline\nconfiguration with its runtime status, showing its overall state, its sources and targets, its jobs\nand components, and its per-stream statistics and performance metrics.\n\n[code example]\n\nTo watch the status update live, pair the command with `watch`:\n\n[code example]\n\nFor a shorter overview, [`redis-di list`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list)\nprints a one-line summary of the pipeline, and\n[`redis-di get`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-get)\ndoes the same for a single pipeline. See the\n[`redis-di describe`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-describe)\nreference page for details."},{"id":"start-and-stop-a-pipeline","title":"Start and stop a pipeline","role":"content","text":"Use [`redis-di stop`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-stop)\nto pause a running pipeline and\n[`redis-di start`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-start)\nto resume it. Stopping a pipeline halts data processing without deleting the pipeline or its\nconfiguration. A Debezium source restarts according to its\n[snapshot mode](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config#choose-a-snapshot-mode)\nand the availability of its saved source position.\n\n[code example]\n\nTo act on a single source instead of the whole pipeline, add `--source`:\n\n[code example]\n\nNote that a source can only run if its parent pipeline is running. See\n[Multiple sources in one pipeline](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/multiple-sources) for more information."},{"id":"reset-a-pipeline","title":"Reset a pipeline","role":"content","text":"Use [`redis-di reset`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-reset)\nto clear the pipeline's saved source positions. With the default `initial` snapshot\nmode, a Debezium collector reloads selected source data and then resumes change data capture\n(CDC). Reset does not change the configured\n[snapshot mode](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config#choose-a-snapshot-mode).\nModes that skip existing rows do not reload them after a reset, and `initial_only` mode\ndoes not capture subsequent changes.\n\nReset does not flush the target Redis database. A new snapshot can overwrite target\nrecords, but [missed deletes can leave stale records](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config#missed-deletes).\n\n[code example]\n\nAdd `--source` to reset a single source. The other sources keep their data, but the whole\npipeline stops while the reset runs and starts again afterwards:\n\n[code example]"},{"id":"clear-a-pipeline","title":"Clear a pipeline","role":"content","text":"To stop a pipeline and discard its configuration, deploy an empty configuration with the\n`--empty` option of\n[`redis-di deploy`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-deploy):\n\n[code example]\n\nRDI removes the pipeline's data plane components and deletes its data from the RDI database,\nincluding the change data streams, offsets, schema history, dead-letter queue entries,\nstatistics, deduplication state, and record counters of every source. The pipeline itself\nremains, with an empty configuration, so you can deploy a new configuration to it at any time.\n\nThe pipeline secrets are not affected, so remove them yourself with\n[`redis-di delete-secret`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-delete-secret)\nif you no longer need them. The records the pipeline wrote to the target database are not\ndeleted either.\n\n> [!NOTE]\n> Clearing a pipeline discards the source aliases of any source that predates RDI's\n> support for multiple sources, so if you deploy such a source again under the same name you\n> must set its secrets again, and also change its secret references and job `server_name` values. See\n> [Redeploying a configuration after clearing a pipeline](https://redis.io/docs/latest/integrate/redis-data-integration/installation/upgrade#redeploying-a-configuration-after-clearing-a-pipeline)\n> for a before and after example."},{"id":"wait","title":"Wait for changes to complete","role":"content","text":"The commands that change a pipeline's state, namely `deploy`, `start`, `stop`, `reset`,\n`set-secret`, and `delete-secret`, do not return as soon as the API accepts the request. By default,\nthey wait for the pipeline to finish transitioning to the expected state, polling its status until it\nsucceeds, reaches an error, or the `--timeout` (2 minutes by default) elapses. This is usually what\nyou want: the command reflects the real outcome, so a script can rely on the change having taken\neffect and can fail fast if it did not.\n\nIn some cases, though, a pipeline needs *several* changes before it can transition to a healthy state,\nand waiting after each individual change would time out. The clearest example is rotating both the\nusername and the password of a database: if you set only the username with the default `--wait=true`,\nthe pipeline tries to reconnect with the new username and the old password, fails, and the command\ntimes out after two minutes with the pipeline in a broken state.\n\nTo avoid this, set all the related secrets, or at least all of them except the last, with\n`--wait=false`, so the pipeline applies them together and only the final command waits for it to\nbecome healthy:\n\n[code example]\n\nThe same applies to any set of changes that are only valid together."}],"examples":[{"id":"set-secrets-ex0","language":"bash","code":"openssl pkcs8 -topk8 -inform PEM -outform DER \\\n    -in /path/to/myclient.pem \\\n    -out /path/to/myclient.pk8 -nocrypt","section_id":"set-secrets"},{"id":"set-secrets-with-the-cli-ex0","language":"bash","code":"# For username and password\nredis-di set-secret USERNAME --db mysql yourUsername\nredis-di set-secret PASSWORD --db mysql yourPassword\n\n# With source TLS, in addition to the above\nredis-di set-secret CACERT --db mysql /path/to/myca.crt\n\n# With source mTLS, in addition to the above\nredis-di set-secret CERT --db mysql /path/to/myclient.crt\nredis-di set-secret KEY --db mysql /path/to/myclient.key\n# Use this only if the private key is password-protected\nredis-di set-secret KEY_PASSWORD --db mysql yourKeyPassword","section_id":"set-secrets-with-the-cli"},{"id":"set-secrets-with-the-cli-ex1","language":"bash","code":"# For username and password\nredis-di set-secret USERNAME --db target yourUsername\nredis-di set-secret PASSWORD --db target yourPassword\n\n# With target TLS, in addition to the above\nredis-di set-secret CACERT --db target /path/to/myca.crt\n\n# With target mTLS, in addition to the above\nredis-di set-secret CERT --db target /path/to/myclient.crt\nredis-di set-secret KEY --db target /path/to/myclient.key\n# Use this only if the private key is password-protected\nredis-di set-secret KEY_PASSWORD --db target yourKeyPassword","section_id":"set-secrets-with-the-cli"},{"id":"manage-secrets-with-the-cli-ex0","language":"bash","code":"# List all the secrets of a pipeline, with the database each one belongs to\nredis-di list-secrets\n\n# List only the secrets of one database\nredis-di list-secrets --db mysql\n\n# Show a single secret and whether it is set\nredis-di describe-secret PASSWORD --db mysql\n\n# Delete a secret (prompts for confirmation unless you add --force)\nredis-di delete-secret CACERT --db mysql","section_id":"manage-secrets-with-the-cli"},{"id":"set-secrets-for-k8shelm-deployment-using-kubectl-command-ex0","language":"bash","code":"kubectl create secret generic <DB> \\\n--namespace=rdi \\\n--from-literal=<SECRET-NAME>=<SECRET-VALUE>","section_id":"set-secrets-for-k8shelm-deployment-using-kubectl-command"},{"id":"set-secrets-for-k8shelm-deployment-using-kubectl-command-ex1","language":"bash","code":"kubectl create secret generic <DB>-ssl \\\n--namespace=rdi \\\n--from-file=<FILE-NAME>=<FILE-PATH>","section_id":"set-secrets-for-k8shelm-deployment-using-kubectl-command"},{"id":"set-secrets-for-k8shelm-deployment-using-kubectl-command-ex2","language":"bash","code":"# Without source TLS\n# Create or update mysql-db secret\nkubectl create secret generic mysql-db --namespace=rdi \\\n--from-literal=MYSQL_DB_USERNAME=yourUsername \\\n--from-literal=MYSQL_DB_PASSWORD=yourPassword \\\n--save-config --dry-run=client -o yaml | kubectl apply -f -\n\n# With source TLS\n# Create or update mysql-db secret\nkubectl create secret generic mysql-db --namespace=rdi \\\n--from-literal=MYSQL_DB_USERNAME=yourUsername \\\n--from-literal=MYSQL_DB_PASSWORD=yourPassword \\\n--from-literal=MYSQL_DB_CACERT=/etc/certificates/mysql_db/ca.crt \\\n--save-config --dry-run=client -o yaml | kubectl apply -f -\n# Create or update mysql-db-ssl secret\nkubectl create secret generic mysql-db-ssl --namespace=rdi \\\n--from-file=ca.crt=/path/to/myca.crt \\\n--save-config --dry-run=client -o yaml | kubectl apply -f -\n\n# With source mTLS\n# Create or update mysql-db secret. Include the MYSQL_DB_KEY_PASSWORD line\n# only if the private key is password-protected.\nkubectl create secret generic mysql-db --namespace=rdi \\\n--from-literal=MYSQL_DB_USERNAME=yourUsername \\\n--from-literal=MYSQL_DB_PASSWORD=yourPassword \\\n--from-literal=MYSQL_DB_CACERT=/etc/certificates/mysql_db/ca.crt \\\n--from-literal=MYSQL_DB_CERT=/etc/certificates/mysql_db/client.crt \\\n--from-literal=MYSQL_DB_KEY=/etc/certificates/mysql_db/client.key \\\n--from-literal=MYSQL_DB_KEY_PASSWORD=yourKeyPassword \\\n--save-config --dry-run=client -o yaml | kubectl apply -f -\n# Create or update mysql-db-ssl secret\nkubectl create secret generic mysql-db-ssl --namespace=rdi \\\n--from-file=ca.crt=/path/to/myca.crt \\\n--from-file=client.crt=/path/to/myclient.crt \\\n--from-file=client.key=/path/to/myclient.key \\\n--save-config --dry-run=client -o yaml | kubectl apply -f -","section_id":"set-secrets-for-k8shelm-deployment-using-kubectl-command"},{"id":"set-secrets-for-k8shelm-deployment-using-kubectl-command-ex3","language":"bash","code":"# Without target TLS\n# Create or update target-db secret\nkubectl create secret generic target-db --namespace=rdi \\\n--from-literal=TARGET_DB_USERNAME=yourUsername \\\n--from-literal=TARGET_DB_PASSWORD=yourPassword \\\n--save-config --dry-run=client -o yaml | kubectl apply -f -\n\n# With target TLS\n# Create or update target-db secret\nkubectl create secret generic target-db --namespace=rdi \\\n--from-literal=TARGET_DB_USERNAME=yourUsername \\\n--from-literal=TARGET_DB_PASSWORD=yourPassword \\\n--from-literal=TARGET_DB_CACERT=/etc/certificates/target_db/ca.crt \\\n--save-config --dry-run=client -o yaml | kubectl apply -f -\n# Create or update target-db-ssl secret\nkubectl create secret generic target-db-ssl --namespace=rdi \\\n--from-file=ca.crt=/path/to/myca.crt \\\n--save-config --dry-run=client -o yaml | kubectl apply -f -\n\n# With target mTLS\n# Create or update target-db secret. Include the TARGET_DB_KEY_PASSWORD line\n# only if the private key is password-protected.\nkubectl create secret generic target-db --namespace=rdi \\\n--from-literal=TARGET_DB_USERNAME=yourUsername \\\n--from-literal=TARGET_DB_PASSWORD=yourPassword \\\n--from-literal=TARGET_DB_CACERT=/etc/certificates/target_db/ca.crt \\\n--from-literal=TARGET_DB_CERT=/etc/certificates/target_db/client.crt \\\n--from-literal=TARGET_DB_KEY=/etc/certificates/target_db/client.key \\\n--from-literal=TARGET_DB_KEY_PASSWORD=yourKeyPassword \\\n--save-config --dry-run=client -o yaml | kubectl apply -f -\n# Create or update target-db-ssl secret\nkubectl create secret generic target-db-ssl --namespace=rdi \\\n--from-file=ca.crt=/path/to/myca.crt \\\n--from-file=client.crt=/path/to/myclient.crt \\\n--from-file=client.key=/path/to/myclient.key \\\n--save-config --dry-run=client -o yaml | kubectl apply -f -","section_id":"set-secrets-for-k8shelm-deployment-using-kubectl-command"},{"id":"set-secrets-for-k8shelm-deployment-using-kubectl-command-ex4","language":"bash","code":"kubectl label secret mysql-db --namespace=rdi --overwrite \\\n  app.kubernetes.io/name=pipeline \\\n  app.kubernetes.io/instance=default \\\n  product=rdi\nkubectl label secret target-db --namespace=rdi --overwrite \\\n  app.kubernetes.io/name=pipeline \\\n  app.kubernetes.io/instance=default \\\n  product=rdi\n\n# With source TLS or mTLS\nkubectl label secret mysql-db-ssl --namespace=rdi --overwrite \\\n  app.kubernetes.io/name=pipeline \\\n  app.kubernetes.io/instance=default \\\n  product=rdi\n\n# With target TLS or mTLS\nkubectl label secret target-db-ssl --namespace=rdi --overwrite \\\n  app.kubernetes.io/name=pipeline \\\n  app.kubernetes.io/instance=default \\\n  product=rdi","section_id":"set-secrets-for-k8shelm-deployment-using-kubectl-command"},{"id":"deploy-a-pipeline-ex0","language":"bash","code":"redis-di deploy --dir <path to pipeline folder>","section_id":"deploy-a-pipeline"},{"id":"display-the-pipeline-status-ex0","language":"bash","code":"redis-di describe","section_id":"display-the-pipeline-status"},{"id":"display-the-pipeline-status-ex1","language":"bash","code":"watch -n 1 redis-di describe","section_id":"display-the-pipeline-status"},{"id":"start-and-stop-a-pipeline-ex0","language":"bash","code":"redis-di stop\nredis-di start","section_id":"start-and-stop-a-pipeline"},{"id":"start-and-stop-a-pipeline-ex1","language":"bash","code":"redis-di stop --source mysql\nredis-di start --source mysql","section_id":"start-and-stop-a-pipeline"},{"id":"reset-a-pipeline-ex0","language":"bash","code":"redis-di reset","section_id":"reset-a-pipeline"},{"id":"reset-a-pipeline-ex1","language":"bash","code":"redis-di reset --source mysql","section_id":"reset-a-pipeline"},{"id":"clear-a-pipeline-ex0","language":"bash","code":"redis-di deploy --empty","section_id":"clear-a-pipeline"},{"id":"wait-ex0","language":"bash","code":"redis-di set-secret USERNAME --db mysql newUsername --wait=false\nredis-di set-secret PASSWORD --db mysql newPassword","section_id":"wait"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines","title":"Data pipelines","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/","summary":"Learn how to configure RDI for data capture and transformation.","aliases":["/integrate/redis-data-integration/ingest/data-pipelines/","/integrate/redis-data-integration/data-pipelines/data-pipelines/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-10-02T14:54:03+01:00","children":[{"id":"integrate/redis-data-integration/data-pipelines/prepare-dbs","summary":"Enable CDC features in your source databases","title":"Prepare source databases","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/"},{"id":"integrate/redis-data-integration/data-pipelines/pipeline-config","summary":"Learn how to specify the main configuration details for an RDI pipeline.","title":"Pipeline configuration file","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config/"},{"id":"integrate/redis-data-integration/data-pipelines/multiple-sources","summary":"Learn how to ingest from several source databases into one Redis target.","title":"Multiple sources in one pipeline","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/multiple-sources/"},{"id":"integrate/redis-data-integration/data-pipelines/transform-examples","summary":"Learn how to configure job files for data transformation.","title":"Job files","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/"},{"id":"integrate/redis-data-integration/data-pipelines/data-denormalization","summary":"Learn about denormalization strategies","title":"Data denormalization","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/data-denormalization/"},{"id":"integrate/redis-data-integration/data-pipelines/rejected-records","summary":"Learn how RDI stores records that cannot be processed.","title":"Rejected records","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/rejected-records/"},{"id":"integrate/redis-data-integration/data-pipelines/deploy","summary":"Learn how to deploy an RDI pipeline","title":"Deploy a pipeline","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/deploy/"},{"id":"integrate/redis-data-integration/data-pipelines/supported-types","summary":"Learn about supported data types for each source database.","title":"Supported data types by source","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/supported-types/"}],"page_type":"content","content_hash":"de14e012038224426dae59298d18a200f9736f8613c34ce95fd1d3b066347fe9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"RDI uses *pipelines* to implement\n[change data capture](https://en.wikipedia.org/wiki/Change_data_capture) (CDC). (See the\n[architecture overview](https://redis.io/docs/latest/integrate/redis-data-integration/architecture#overview)\nfor an introduction to pipelines.)\nThe sections below explain how pipelines work and give an overview of how to configure and\ndeploy them."},{"id":"how-a-pipeline-works","title":"How a pipeline works","role":"content","text":"An RDI pipeline captures change data records from one or more source databases, and\ntransforms them into Redis data structures. It writes each of these new structures to a\nRedis target database under its own key.\n\nBy default, RDI transforms the source data into\n[hashes](https://redis.io/docs/latest/develop/data-types/hashes) or\n[JSON objects](https://redis.io/docs/latest/develop/data-types/json) for the target with a\nstandard data mapping and a standard format for the key.\nHowever, you can also provide your own custom transformation [jobs](#job-files)\nfor each source table, using your own data mapping and key pattern. You specify these\njobs declaratively with YAML configuration files that require no coding.\n\nData transformation involves two stages:\n\n1.  The data ingested during CDC is automatically transformed to an intermediate JSON\n    change event format.\n1.  RDI passes this JSON change event data to your custom transformation for further\n    processing.\n\nThe diagram below shows the flow of data through the pipeline:\n\n![](https://redis.io/docs/latest/images/rdi/ingest/RDIPipeDataflow.webp)\n\nYou can provide a job file for each source table that needs a custom\ntransformation. You can also add a *default job file* for any tables that don't have their own.\nYou must specify the full name of the source table in the job file (or the special\nname \"*\" in the default job) and you\ncan also include filtering logic to skip data that matches a particular condition.\nAs part of the transformation, you can specify any of the following data types\nto store the data in Redis:\n\n- [JSON](https://redis.io/docs/latest/develop/data-types/json)\n- [Hashes](https://redis.io/docs/latest/develop/data-types/hashes)\n- [Sets](https://redis.io/docs/latest/develop/data-types/sets)\n- [Streams](https://redis.io/docs/latest/develop/data-types/streams)\n- [Sorted sets](https://redis.io/docs/latest/develop/data-types/sorted-sets)\n- [Strings](https://redis.io/docs/latest/develop/data-types/strings)"},{"id":"pipeline-lifecycle","title":"Pipeline lifecycle","role":"content","text":"After you deploy a pipeline, it goes through the following phases:\n\n1. *Deploy* - when you deploy the pipeline, RDI first validates it before use.\nThen, the [operator](https://redis.io/docs/latest/integrate/redis-data-integration/architecture#how-rdi-is-deployed) creates and configures the collector and stream processor that will run the pipeline.\n1. *Snapshot* - The collector starts the pipeline by creating a snapshot of the full\ndataset. This involves reading all the relevant source data, transforming it and then\nwriting it into the Redis target. This phase typically takes minutes to\nhours if you have a lot of data.\n1. *CDC* - Once the snapshot is complete, the collector starts listening for updates to\nthe source data. Whenever a change is committed to the source, the collector captures\nit and adds it to the target through the pipeline. This phase continues indefinitely\nunless you change the pipeline configuration. \n1. *Update* - If you update the pipeline configuration, the operator applies it\nto the collector and the stream processor. Note that the changes only affect newly-captured\ndata unless you reset the pipeline completely. Once RDI has accepted the updates, the\npipeline returns to the CDC phase with the new configuration.\n1. *Reset* - There are circumstances where you might want to rebuild the dataset\ncompletely. For example, you might want to apply a new transformation to all the source\ndata or refresh the dataset if RDI is disconnected from the\nsource for a long time. In situations like these, you can *reset* the pipeline back\nto the snapshot phase. When this is complete, the pipeline continues with CDC as usual."},{"id":"using-a-pipeline","title":"Using a pipeline","role":"content","text":"Follow the steps described in the sections below to prepare and run an RDI pipeline.\n\nThe following example uses a single-source pipeline. See\n[Multiple sources in one pipeline](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/multiple-sources)\nfor details on how to capture multiple sources in one pipeline."},{"id":"1-prepare-the-source-database","title":"1. Prepare the source database","role":"content","text":"Before using the pipeline you must first prepare your source database to use\nthe Debezium connector for *change data capture (CDC)*. See the\n[architecture overview](https://redis.io/docs/latest/integrate/redis-data-integration/architecture#overview)\nfor more information about CDC.\nEach database type has a different set of preparation steps. You can\nfind the preparation guides for the databases that RDI supports in the\n[Prepare source databases](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs)\nsection."},{"id":"2-configure-the-pipeline","title":"2. Configure the pipeline","role":"content","text":"RDI uses a set of [YAML](https://en.wikipedia.org/wiki/YAML)\nfiles to configure each pipeline. The folder structure of the\nconfiguration is shown below:\n\n[code example]\n\nThe main configuration for the pipeline is in the `config.yaml` file.\nThis specifies the connection details for the source database (such\nas host, username, and password) and also the queries that RDI will use\nto extract the required data. You should place job files in the `Jobs`\nfolder if you want to specify your own data transformations.\n\nSee\n[Pipeline configuration file](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config)\nfor a full description of the `config.yaml` file and some example configurations."},{"id":"3-create-job-files-optional","title":"3. Create job files (optional)","role":"content","text":"You can use one or more job files to configure which fields from the source tables\nyou want to use, and which data structure you want to write to the target. You\ncan also optionally specify a transformation to apply to the data before writing it\nto the target. See the\n[Job files](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples)\nsection for full details of the file format and examples of common tasks for job files."},{"id":"4-deploy-the-pipeline","title":"4. Deploy the pipeline","role":"content","text":"When your configuration is ready, you must deploy it to start using the pipeline. See\n[Deploy a pipeline](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/deploy)\nto learn how to do this."},{"id":"more-information","title":"More information","role":"content","text":"See the other pages in this section for more information and examples:"}],"examples":[{"id":"2-configure-the-pipeline-ex0","language":"hierarchy {type=\"filesystem\"}","code":"\"(root)\":\n    \"config.yaml\":\n        _meta:\n            description: \"\\\"config.yaml\\\" is the main pipeline configuration file.\"\n    \"jobs\":\n        _meta:\n            description: \"The 'jobs' folder containing optional job files.\"\n        \"default-job.yaml\":\n            _meta:\n                description: \"A default job.\"\n        \"job1.yaml\":\n          _meta:\n                description: \"Each job file must have a unique name.\"\n        \"...\":\n            _meta:\n                ellipsis: true\n                description: \"Other job files, if required.\"","section_id":"2-configure-the-pipeline"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/multiple-sources","title":"Multiple sources in one pipeline","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/multiple-sources/","summary":"Learn how to ingest from several source databases into one Redis target.","tags":["docs","integrate","rs","rdi"],"last_updated":"2026-10-13T08:44:20+03:00","page_type":"content","content_hash":"e070db07423f1a4e3eba21058ec27490e272b91f08a8746c714900c1f481bcbf","sections":[{"id":"overview","title":"Overview","role":"overview","text":"One RDI pipeline can capture changes from several source databases and write them all to the\nsame Redis target. The sources can be of different database types and each has its\nown collector, its own credentials, and its own set of Redis streams to ensure it\nis independent of the other sources.\n\n> [!NOTE]\n> You must use RDI API v2 to manage a pipeline with several sources. RDI API v1 supports only\n> single-source pipelines. See the\n> [RDI API migration guide](https://redis.io/docs/latest/integrate/redis-data-integration/reference/api-migration) for more information."},{"id":"name-your-sources","title":"Name your sources","role":"content","text":"Each source is an entry in the `sources` section of\n[`config.yaml`](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config),\nwith the source name as key:\n\n[code example]\n\nA source name must:\n\n- Start with a lowercase letter.\n- Contain only lowercase letters, digits, and dashes.\n- End with a letter or a digit.\n- Be at most 22 characters long.\n\nThe names `rdi` and `target` are reserved and cannot be used for sources.\n\n> [!WARNING]\n> If your pipeline has a source created before RDI supported multiple sources, do not name a new\n> source after any schema or database of that older source. The change data streams of the older\n> source do not contain a source name segment, so a new source named after one of its schemas would claim keys\n> that belong to the older source, and resetting or removing the new source would delete the older\n> source's data. See\n> [Existing names are kept after an upgrade](#existing-names-are-kept-after-an-upgrade).\n\nRDI derives the environment variables that contain the source's credentials from the source\nname. For example, the `connection` section of a source named `mysql` references `${MYSQL_DB_USERNAME}`\nand `${MYSQL_DB_PASSWORD}`. See\n[Set secrets](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/deploy#set-secrets)\nfor details of how RDI derives those names and for the full list of secret keys.\n\nThe source name also appears in the resources RDI creates for the source. The table below\nlists the names derived from a source named `mysql`.\n\n| Resource | Name |\n| :-- | :-- |\n| Credentials secret | `mysql-db` |\n| TLS secret | `mysql-db-ssl` |\n| Environment variable prefix | `MYSQL_DB_` |\n| Certificate directory | `/etc/certificates/mysql_db/` |\n| Collector deployment | `collector-mysql` |\n| Change data streams | `data:{rdi}:mysql.<qualified_table_name>` |\n| Dead-letter queue streams | `dlq:data:{rdi}:mysql.<qualified_table_name>` |\n| Metric collection | `collector-mysql_metrics` |\n| Metrics endpoint path on a VM installation | `/collector-mysql/metrics` |\n\nIn the stream names, `<qualified_table_name>` is the qualified table name:\n`<database>.<table>` for MySQL and MariaDB, `<database>.<collection>` for MongoDB,\n`<schema>.<table>` for Oracle, PostgreSQL, Snowflake, and Spanner, and\n`<database>.<schema>.<table>` for SQL Server.\n\nEach source also accepts an optional `name` property, which is a display name\nof up to 100 characters. Unlike the source name, it is not used as an identifier,\nso there is no restriction on the characters you can use."},{"id":"configure-several-sources","title":"Configure several sources","role":"configuration","text":"Add one entry per source in the `config.yaml` file (see\n[Pipeline configuration file](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config)\nfor a full description of this file).\n[`redis-di scaffold`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-scaffold)\ngenerates a configuration with one source, named by its `--source-name` option, so add any\nfurther sources by editing `config.yaml`.\n\nSources of different types can be mixed freely, but a source's collector `type` and its\n`connection` type have to match. Use `cdc`, the default, for the relational databases and\nMongoDB, `flink` for a Spanner connection, and `riotx` for a Snowflake connection. RDI\nrejects any other combination when you deploy the pipeline. See\n[Prepare source databases](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs)\nto learn how to prepare each source database.\n\nThe following example captures from a MySQL database and a PostgreSQL database, each with its\nown credentials:\n\n[code example]"},{"id":"set-secrets-for-each-source","title":"Set secrets for each source","role":"content","text":"Set a source's credentials with the source name in the `--db` option:\n\n[code example]\n\nThe secret keys used as CLI arguments are the same for every source: `USERNAME`, `PASSWORD`, and, for\n[Transport Layer Security (TLS)](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/deploy#set-secrets)\nconnections, `CACERT`, `CERT`, `KEY`, and `KEY_PASSWORD`. Use `--db target` for the\ntarget database: `redis-di set-secret PASSWORD --db target <password>`.\n\nEach source's `connection` section then references its own secrets: `${MYSQL_DB_USERNAME}` and\n`${MYSQL_DB_PASSWORD}` for `mysql`, `${POSTGRESQL_DB_USERNAME}` and `${POSTGRESQL_DB_PASSWORD}`\nfor `postgresql`, and `${TARGET_DB_PASSWORD}` for the target.\n\nSee [Set secrets](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/deploy#set-secrets)\nfor the full secret reference."},{"id":"select-sources-in-jobs","title":"Select sources in jobs","role":"content","text":"A [job](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples)\nselects the source it processes by setting `server_name` to the source name:\n\n[code example]\n\nWhen a pipeline has more than one source, every job must set `server_name`, and the value\nmust match one of the sources in `config.yaml`. RDI rejects the pipeline when a job has no\n`server_name`, or when its `server_name` matches no source. The one exception is the default\njob for `table: \"*\"`: it is a source-agnostic catch-all, so it needs no `server_name`.\n\nFor a source that existed before RDI supported multiple sources, set `server_name` to `rdi`\nrather than to the name the source has in `config.yaml`. See\n[Existing names are kept after an upgrade](#existing-names-are-kept-after-an-upgrade).\n\nIn a pipeline with a single source, `server_name` is optional. If you omit it, the\njob does not filter by source.\n\nNo two jobs may select the same records, so make sure the source selectors of your jobs do\nnot overlap. RDI rejects the pipeline when it finds two jobs that intersect.\n\nWith the [Flink processor](https://redis.io/docs/latest/integrate/redis-data-integration/architecture/classic-vs-flink),\n`server_name` also accepts a list of source names, and an entry prefixed with `regex:` selects\nall sources that match the regular expression, so one job can process multiple tables,\npotentially from different sources, databases, or schemas. See\n[Job files](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples)\nfor details."},{"id":"add-or-remove-a-source","title":"Add or remove a source","role":"content","text":"To add a source, set its secrets first, then add it to `config.yaml` and deploy. Adding a\nsource does not interrupt other sources that are already running.\n\nTo remove a source, delete its entry from `config.yaml` and deploy. RDI removes the source's\ncollector and deletes that source's data from the RDI database, including its change data streams,\nDebezium offsets, schema history, dead-letter queue entries, statistics, deduplication state,\nand record counters. The other sources keep their data, and RDI stops the whole pipeline\nwhile the deletion runs and starts it again afterwards. No further action is\nneeded for this cleanup, but a source you add later under the same name has no saved\nposition. With the default `initial` mode, it takes a new snapshot. Other modes follow\n[their configured snapshot behavior](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config#choose-a-snapshot-mode).\n\nThe source's secrets are not deleted, so remove them yourself with\n[`redis-di delete-secret`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-delete-secret)\nif you no longer need them. The records the pipeline wrote to the target database are not deleted\neither.\n\nAdding the same source again is straightforward, unless the source you removed predates RDI's support\nfor multiple sources. For such a source the names from before the upgrade are kept only while it\nexists, so any source you add under the same name is treated as a new source, for which RDI derives the\nnames instead. Adapt the configuration accordingly:\n\n- Set the source's secrets again, for example\n  `redis-di set-secret PASSWORD --db mysql <password>`.\n- Change the secret references in its `connection` section from `${SOURCE_DB_*}` to\n  `${MYSQL_DB_*}`, for a source named `mysql`.\n- Change `server_name` from `rdi` to the source name in every job associated with it.\n\nSee\n[Redeploying a configuration after clearing a pipeline](#redeploying-a-configuration-after-clearing-a-pipeline)\nfor a before and after example, and\n[Existing names are kept after an upgrade](#existing-names-are-kept-after-an-upgrade)\nfor the full list of names involved.\n\nNote that renaming a source is not supported. Renaming a source in `config.yaml` is equivalent\nto removing the source and adding a new source with the new name. This implies in particular:\n\n- You must create the source's secrets under the new name and update `${...}` references in\n  its `connection` section.\n- You must update `server_name` for every job that reads from the source.\n- The data present in the RDI database under the old name is deleted, as it is for any removed source.\n- The new source has no saved position. With the default `initial` mode, it takes a\n  new snapshot. Other modes follow\n  [their configured snapshot behavior](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config#choose-a-snapshot-mode)."},{"id":"start-stop-and-reset-a-single-source","title":"Start, stop, and reset a single source","role":"content","text":"Pass `--source` to act on a single source instead of the whole pipeline:\n\n[code example]\n\nA source runs only while its pipeline runs, so starting one source does not start a stopped\npipeline. Generally, stopping one source leaves the others running, and when one source fails, the other sources keep capturing changes. The only exception to this is a source of type\n`external`. RDI creates no collector for this, so you cannot start or stop it.\n\nStopping a source scales its collector down to zero replicas and leaves the rest of the\nsource's resources in place. When you restart a collector, its\n[snapshot mode](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config#choose-a-snapshot-mode)\nand the availability of its saved position determine whether it resumes streaming or\ntakes a new snapshot.\n\nResetting a single source deletes that source's data from the RDI database, including its change data streams, Debezium\noffsets, schema history, dead-letter queue entries, statistics, deduplication state, and record counters.\nThe reset source then follows its configured\n[snapshot mode](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config#choose-a-snapshot-mode)\nwith no saved position. With the default `initial` mode, it takes a new snapshot and\nresumes streaming. Every other source keeps its saved position, but all collectors\nrestart and follow their configured modes. For example, a source using `always` mode takes\nanother snapshot on restart even though its position was preserved. RDI stops the whole\npipeline while the reset runs and starts it again afterwards.\n\nReset does not delete records from the target Redis database. A new snapshot can\n[leave stale target records after missed deletes](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config#missed-deletes)."},{"id":"monitor-each-source","title":"Monitor each source","role":"content","text":"Use [`redis-di describe`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-describe)\nto see the state of every source at once.\n\nIn its output, the `Sources` section lists each source with its sync mode and\nwhether it is connected.\nThe `Components` section lists one collector per source. Errors are reported against the\ncomponent they came from. See the\n[`redis-di describe`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-describe)\nreference page for more details.\n\nNote that while the sources are independent of each other in the data they capture, the\npipeline status is not broken down per source. RDI reports the whole pipeline in an error state when a\nsingle source fails, so you should use the `Components` section to find out which one has failed.\n\nEach Debezium collector has its own metric collection, named after the collector, such as\n`collector-mysql_metrics`. The Flink and RIOT-X collectors don't have metric collections.\n\nIn Prometheus, you can break the per-stream record counters down per source, since the stream\nname contains the source name. With the\n[Flink processor](https://redis.io/docs/latest/integrate/redis-data-integration/architecture/classic-vs-flink)\nthe counters are reported by\n`flink_jobmanager_job_operator_coordinator_stream_type_rdiRecords`, which has a `stream`\nlabel; with the classic processor they are reported by `rdi_incoming_entries`, which has an\nequivalent `data_source` label. See\n[Flink processor metrics](https://redis.io/docs/latest/integrate/redis-data-integration/observability#flink-processor-metrics),\n[Stream processor metrics](https://redis.io/docs/latest/integrate/redis-data-integration/observability#stream-processor-metrics),\nand, for the per-source collector endpoints,\n[Accessing the metrics](https://redis.io/docs/latest/integrate/redis-data-integration/observability#accessing-the-metrics).\n\nDead-letter queue streams have Redis keys containing a\n`<source>.<qualified_table_name>` section.\nThis makes it easy to attribute rejected records to their source. See\n[Rejected records](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/rejected-records) for more information."},{"id":"existing-names-are-kept-after-an-upgrade","title":"Existing names are kept after an upgrade","role":"content","text":"Before RDI supported multiple sources per pipeline, every source-scoped resource had\na name including the word `source` instead of the actual source name in `config.yaml`.\nFor a source that existed before you upgraded to a version that supports multiple sources,\nthose names are kept unchanged, regardless of what the source is called in `config.yaml`.\nIn particular, for such a source:\n\n- Its secret environment variables are still named `SOURCE_DB_*`, so its `connection` section\n  can keep referencing these secrets.\n- Its Kubernetes secrets are still named `source-db` and `source-db-ssl`.\n- Its Kubernetes deployment and other resources are still named `collector-source`.\n- Its data streams are still named `data:{rdi}:<qualified_table_name>`, and its offset\n  and schema history keys are still `metadata:debezium:offsets` and\n  `metadata:debezium:schema_history`.\n- Its `server_name` is still `rdi`, or, for a Spanner source, its instance ID.\n\nRDI keeps these names in a mapping from the source name in `config.yaml` to\nthe internal name the source had before the upgrade. This mapping lasts only as long as the source\ndoes: RDI discards it as soon as the source is removed from the configuration, whether you remove that one\nsource or\n[clear the whole pipeline](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/deploy#clear-a-pipeline).\nA source you add afterwards under the same name is treated as a new source, so\nRDI derives its names from the source name. See\n[Add or remove a source](#add-or-remove-a-source) for what you have to change in that case.\n\nFor a source you add after the upgrade under any other name, RDI derives all of these names\nfrom the source name, as described on this page.\n\nSee [Upgrading RDI](https://redis.io/docs/latest/integrate/redis-data-integration/installation/upgrade)\nfor more information."},{"id":"redeploying-a-configuration-after-clearing-a-pipeline","title":"Redeploying a configuration after clearing a pipeline","role":"content","text":"A configuration exported from an upgraded pipeline still references the names from before the\nupgrade, so deploying it again after\n[clearing the pipeline](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/deploy#clear-a-pipeline)\nfails, because the mapping that made those names resolve is gone.\n\nA source and a job of such an upgraded pipeline:\n\n[code example]\n\n[code example]\n\nThe same source and job, adapted to deploy as a new source named `mysql`:\n\n[code example]\n\n[code example]\n\nSet the source's secrets under its actual name before you deploy:\n\n[code example]\n\nThe source has no saved position because its internal data was deleted. With the\ndefault `initial` mode, it takes a fresh snapshot. Other modes follow\n[their configured snapshot behavior](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config#choose-a-snapshot-mode).\nRecords already written to the target remain. A new snapshot can overwrite them, but\nit does not remove records for rows that are absent from the source."}],"examples":[{"id":"name-your-sources-ex0","language":"yaml","code":"sources:\n  mysql: # this source is named 'mysql'\n    type: cdc","section_id":"name-your-sources"},{"id":"configure-several-sources-ex0","language":"yaml","code":"sources:\n  mysql:\n    type: cdc\n    connection:\n      type: mysql\n      host: <MYSQL_DB_HOST>\n      port: 3306\n      user: ${MYSQL_DB_USERNAME}\n      password: ${MYSQL_DB_PASSWORD}\n    databases:\n      - inventory\n    tables:\n      inventory.customers: {}\n      inventory.orders: {}\n  postgresql:\n    type: cdc\n    connection:\n      type: postgresql\n      host: <POSTGRESQL_DB_HOST>\n      port: 5432\n      database: billing\n      user: ${POSTGRESQL_DB_USERNAME}\n      password: ${POSTGRESQL_DB_PASSWORD}\n    schemas:\n      - public\n    tables:\n      public.clients: {}\ntargets:\n  target:\n    connection:\n      type: redis\n      host: <TARGET_DB_HOST>\n      port: 6379\n      password: ${TARGET_DB_PASSWORD}","section_id":"configure-several-sources"},{"id":"set-secrets-for-each-source-ex0","language":"bash","code":"redis-di set-secret USERNAME --db mysql <username>\nredis-di set-secret PASSWORD --db mysql <password>\nredis-di set-secret USERNAME --db postgresql <username>\nredis-di set-secret PASSWORD --db postgresql <password>","section_id":"set-secrets-for-each-source"},{"id":"select-sources-in-jobs-ex0","language":"yaml","code":"source:\n  server_name: mysql\n  db: inventory\n  table: customers","section_id":"select-sources-in-jobs"},{"id":"start-stop-and-reset-a-single-source-ex0","language":"bash","code":"redis-di stop --source mysql\nredis-di start --source mysql\nredis-di reset --source mysql","section_id":"start-stop-and-reset-a-single-source"},{"id":"redeploying-a-configuration-after-clearing-a-pipeline-ex0","language":"yaml","code":"sources:\n  mysql:\n    connection:\n      user: ${SOURCE_DB_USERNAME}\n      password: ${SOURCE_DB_PASSWORD}","section_id":"redeploying-a-configuration-after-clearing-a-pipeline"},{"id":"redeploying-a-configuration-after-clearing-a-pipeline-ex1","language":"yaml","code":"source:\n  server_name: rdi\n  db: inventory\n  table: customers","section_id":"redeploying-a-configuration-after-clearing-a-pipeline"},{"id":"redeploying-a-configuration-after-clearing-a-pipeline-ex2","language":"yaml","code":"sources:\n  mysql:\n    connection:\n      user: ${MYSQL_DB_USERNAME}\n      password: ${MYSQL_DB_PASSWORD}","section_id":"redeploying-a-configuration-after-clearing-a-pipeline"},{"id":"redeploying-a-configuration-after-clearing-a-pipeline-ex3","language":"yaml","code":"source:\n  server_name: mysql\n  db: inventory\n  table: customers","section_id":"redeploying-a-configuration-after-clearing-a-pipeline"},{"id":"redeploying-a-configuration-after-clearing-a-pipeline-ex4","language":"bash","code":"redis-di set-secret USERNAME --db mysql <username>\nredis-di set-secret PASSWORD --db mysql <password>","section_id":"redeploying-a-configuration-after-clearing-a-pipeline"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/pipeline-config","title":"Pipeline configuration file","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config/","summary":"Learn how to specify the main configuration details for an RDI pipeline.","aliases":["/integrate/redis-data-integration/ingest/data-pipelines/data-pipelines/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-10-13T08:44:20+03:00","page_type":"content","content_hash":"f08f10dc1ef3a05197e89048539a029db7d26b9188014521ac0688d4cc3592c9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The main configuration details for an RDI pipeline are in the `config.yaml` file.\nThis file specifies the connection details for the source and target databases,\nand also the set of tables you want to capture. You can also add one or more\n[job files](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples)\nif you want to apply custom transformations to the captured data.\n\nEach section explains one part of the file. Start with the minimal example, then\nadd only the optional properties that you need. See the\n[configuration file reference](https://redis.io/docs/latest/integrate/redis-data-integration/reference/config-yaml-reference)\nfor all supported properties."},{"id":"before-you-start","title":"Before you start","role":"content","text":"Before you create `config.yaml`:\n\n1. [Prepare the source database](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs) for change data capture.\n1. [Install RDI](https://redis.io/docs/latest/integrate/redis-data-integration/installation).\n1. [Set the secrets](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/deploy#set-secrets) that the file references."},{"id":"start-with-a-minimal-file","title":"Start with a minimal file","role":"content","text":"The following example shows the required structure of a `config.yaml` file. Values of the\nform \"`${name}`\" refer to secrets that you should set as described in\n[Set secrets](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/deploy#set-secrets).\nIn particular, you should normally use secrets as shown to set the source\nand target username and password rather than storing them in plain text in this file.\n\n[code example]\n\nThe processor properties all have defaults, so add them only when you need to change the\ndefault behavior. In particular, the pipeline runs on the Flink processor unless you set\n`type` to `classic`."},{"id":"build-the-file-with-an-ai-assistant","title":"Build the file with an AI assistant","role":"content","text":"Copy the following prompt into your AI assistant. The prompt tells the assistant\nto use the RDI documentation as its source of truth and to flag unsupported requests.\n\n[code example]"},{"id":"sections","title":"Sections","role":"content","text":"The main sections of the file configure [`sources`](#sources), [`targets`](#targets),\nand [`processors`](#processors)."},{"id":"sources","title":"Sources","role":"content","text":"The `sources` section has one subsection per source database. Each subsection starts\nwith the source name, which identifies the source and must be unique (in the example,\nthe source is called `mysql`).\n\nRDI also derives the environment variables that contain the source's credentials from the\nsource name, for example `${MYSQL_DB_USERNAME}` and `${MYSQL_DB_PASSWORD}` for a source\nnamed `mysql`. See\n[Multiple sources in one pipeline](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/multiple-sources)\nfor the source naming rules and for capturing from more than one source database. For a source\nthat existed before RDI supported multiple sources, these variables are still named\n`SOURCE_DB_*`; see\n[Existing names are kept after an upgrade](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/multiple-sources#existing-names-are-kept-after-an-upgrade).\n\nThe example configuration contains the following data:\n\n- `type`: The collector to use for the pipeline. Use `cdc` for MariaDB, MySQL,\n  MongoDB, Oracle, PostgreSQL, or SQL Server. Use `flink` for Google Cloud\n  Spanner. Use `riotx` for Snowflake. Use `external` when you provide and manage\n  the collector. RDI doesn't create collector resources for an `external` source,\n  so omit the other properties in the source section.\n- `connection`: The connection details for the source database: `type`, `host`, `port`,\n  and credentials (`user` and `password`).\n  See the [configuration file reference](https://redis.io/docs/latest/integrate/redis-data-integration/reference/config-yaml-reference#sourcesconnection)\n  for the required fields for each source database type.\n  - If you use [TLS](https://en.wikipedia.org/wiki/Transport_Layer_Security)/\n    or [mTLS](https://en.wikipedia.org/wiki/Mutual_authentication#mTLS) to connect\n    to the source database, you may need to specify additional properties in the\n    `advanced` section with references to the corresponding certificates depending\n    on the source database type. Note that these properties **must** be references to\n    secrets that you should set as described in [Set secrets](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/deploy#set-secrets).\n- `databases`: List of all databases to collect data from for source database types\n  that support multiple databases, such as `mysql` and `mariadb`.\n- `schemas`: List of all schemas to collect data from for source database types\n  that support multiple schemas, such as `oracle`, `postgresql`, and `sqlserver`.\n- `tables`: List of all tables to collect data from. Each table is identified by its\n  full name, including a database or schema prefix. If there is a single\n  database or schema, this prefix can be omitted.\n  For each table, you can specify:\n  - `columns`: A list of the columns you are interested in (the default is to\n    include all columns)\n  - `keys`: A list of columns to create a composite key if your table\n    doesn't already have a [`PRIMARY KEY`](https://www.w3schools.com/sql/sql_primarykey.asp) or\n    [`UNIQUE`](https://www.w3schools.com/sql/sql_unique.asp) constraint.\n  - `snapshot_sql`: A query to be used when performing the initial snapshot.\n    By default, a query that contains all listed columns of all listed tables will be used.\n- `advanced`: These optional properties configure other Debezium-specific features.\n  The available sub-sections are:\n  - `source`: Properties for reading from the source database.\n    See the Debezium [Source connectors](https://debezium.io/documentation/reference/stable/connectors/)\n    pages for more information about the properties available for each database type.\n  - `sink`: Properties for writing to Redis streams in the RDI database.\n    See the Debezium [Redis stream properties](https://debezium.io/documentation/reference/stable/operations/debezium-server.html#_redis_stream)\n    page for the full set of available properties.\n  - `quarkus`: Properties for the Debezium server, such as the log level. See the\n    Quarkus [Configuration options](https://quarkus.io/guides/all-config)\n    docs for the full set of available properties.\n  - `java_options`: controls the JAVA_OPTS environment variable (for RDI 1.15.1 and above). Use it to modify the default values for Java heap size and other Java options for the Debezium server.\n    For example, set it to `\"-Xmx2g -Xms512m\"` to set the maximum heap size to 2 GB and the initial heap size to 512 MB."},{"id":"choose-a-snapshot-mode","title":"Choose a snapshot mode","role":"content","text":"Use `sources.<source-name>.advanced.source.snapshot.mode` to control when a\nDebezium collector loads existing source data before streaming changes. This property\napplies to sources with `type: cdc`, including MariaDB, MongoDB, MySQL, Oracle,\nPostgreSQL, and SQL Server. It does not configure Spanner, Snowflake, or external\ncollectors.\n\nAdd the property under the source you want to configure. For example, this file\nuses `initial` mode for a MySQL source:\n\n[code example]\n\nRDI 1.19.1 and RDI 2.0.0 bundle Debezium 3.5. The comparison and connector\nnotes in this section apply to those releases. RDI 2.2.0 is planned to use\nDebezium 3.7.\n\nThe collector saves an *offset*, which is its position in the source's change history.\nA missing offset means there is no saved position, as on first deployment or after a\nreset. An unavailable position means an offset exists, but the source no longer has\nthe log history needed to resume from it.\n\nWith the default `initial` mode, the collector snapshots when there is no saved\nposition, then streams changes. A restart with a completed snapshot and a valid saved\nposition resumes streaming. Deployment or restart alone does not require a new snapshot.\nIf the saved position is no longer available, `initial` does not automatically\nre-snapshot to recover, and the collector stops and reports an error.\n\nWith `when_needed` mode, the collector also takes a new snapshot when it detects that the\nsaved position is no longer available. This can reload all selected data after an\noutage without a manual reset. The snapshot captures current rows, rather than\nreconstructing every missed change.\n\nIf the collector restarts during a snapshot, the snapshot starts again. Rows deleted\nwhile the snapshot is interrupted can remain as stale records in the target because\nthe new snapshot does not replay their delete events.\n\nUse `when_needed` with monitoring and alerting. Recovery can start a long-running\nsnapshot, which can increase latency. Missed deletes can leave stale target records\neven after the snapshot finishes.\n\n#### Missed deletes can leave stale target records {#missed-deletes}\n\nA new snapshot copies existing source rows. It does not replay missed deletes or remove\ntarget records for rows absent from the snapshot.\n\nThe timeline shows one row whose delete event is lost before collection resumes.\nTime progresses from top to bottom.\n\n[code example]\n\nAn interruption alone does not cause this gap. If the saved position and the required\nlog history are still available, the collector can resume and capture the delete.\nIn this example, the history expires before the collector resumes, and the new snapshot\ncontains no row 42 to overwrite or delete its target record. Streaming resumes after\nthe snapshot, but the stale record can remain in Redis.\n\n\n#### Compare snapshot modes\n\nThe available modes and their exact behavior depend on the source connector and its\nversion. The following table describes the six values listed in the RDI configuration\nreference. A listed value is not necessarily supported by every connector.\n\n| Mode | Existing source data | Restart with a saved position | After a reset clears the position |\n| :-- | :-- | :-- | :-- |\n| `initial` (default) | Snapshot when no offset exists, then stream changes. | Resume after a completed snapshot if the position is valid. No automatic snapshot recovery for unavailable log history. | Snapshot, then stream changes. |\n| `when_needed` | Snapshot when no offset exists, then stream changes. | Resume if the position is valid. Snapshot again if the connector detects an unavailable position. | Snapshot, then stream changes. |\n| `always` | Snapshot on every collector start, then stream changes. | Take a new snapshot even if the position is valid. | Snapshot, then stream changes. |\n| `initial_only` | Take an initial snapshot without streaming subsequent changes. | Restart an interrupted initial snapshot; do not capture subsequent changes after it completes. | Snapshot without ongoing change capture. |\n| `no_data` | Skip existing rows. Initialize any required schema or source position, then stream changes. | Resume streaming if the position is valid. No data snapshot to recover unavailable history. | Skip existing rows and start streaming according to the connector's rules. |\n| `never` | Skip the data snapshot where this mode is supported. | Resume according to the connector's rules; no data snapshot recovery. | Connector-specific startup behavior; existing rows are not reloaded. |\n\n\nOn Kubernetes deployments, the collector exits after completing an `initial_only`\nsnapshot. Kubernetes restarts it, but the collector sees that the snapshot is complete\nand exits again. This restart loop can make the source appear disconnected in\nmonitoring. Check snapshot completion separately from collector connectivity;\n`initial_only` mode does not capture subsequent changes.\n\n\nAn interrupted initial snapshot can restart when the collector starts again, including\nwith `initial` and `initial_only`.\n\nFor Debezium 3.5, note these connector differences:\n\n- PostgreSQL `no_data` performs no snapshot. It resumes from a saved log sequence\n  number (LSN), or from the replication slot's creation position when no LSN is saved.\n  This requires the relevant write-ahead log (WAL) history to remain available.\n- Other connectors can capture schema or initialize a position without copying\n  existing rows in `no_data` mode.\n- `never` is a deprecated alias for `no_data` in PostgreSQL and MongoDB. MySQL and\n  MariaDB retain `never` with connector-specific prerequisites. Oracle and SQL Server\n  do not offer `never`; use `no_data` to skip existing rows.\n\nSee the Debezium 3.5 connector references for [MariaDB](https://debezium.io/documentation/reference/3.5/connectors/mariadb.html),\n[MongoDB](https://debezium.io/documentation/reference/3.5/connectors/mongodb.html),\n[MySQL](https://debezium.io/documentation/reference/3.5/connectors/mysql.html),\n[Oracle](https://debezium.io/documentation/reference/3.5/connectors/oracle.html),\n[PostgreSQL](https://debezium.io/documentation/reference/3.5/connectors/postgresql.html),\nand [SQL Server](https://debezium.io/documentation/reference/3.5/connectors/sqlserver.html).\n\n\nA reset clears saved positions without changing `snapshot.mode` or flushing the target\nRedis database. If you need to reload existing rows, select a mode that takes a data\nsnapshot. A snapshot can overwrite target records, but it does not remove stale records\nfor rows that are absent from the source. See\n[Recover from unavailable source log history](https://redis.io/docs/latest/integrate/redis-data-integration/troubleshooting#unavailable-source-history)."},{"id":"targets","title":"Targets","role":"content","text":"Use this section to provide the connection details for the target Redis\ndatabase. RDI supports one target database. Name the target `target`.\nIn the `connection` section, you can specify the\n`type` of the target database, which must be `redis`, along with\nconnection details such as `host`, `port`, and credentials (`user` and `password`).\nIf you use [TLS](https://en.wikipedia.org/wiki/Transport_Layer_Security)/\nor [mTLS](https://en.wikipedia.org/wiki/Mutual_authentication#mTLS) to connect\nto the target database, you must specify the CA certificate (for TLS),\nand the client certificate and private key (for mTLS) in `cacert`, `cert`, and `key`.\nNote that these certificates **must** be references to secrets\nthat you should set as described in [Set secrets](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/deploy#set-secrets)\n(it is not possible to include these certificates as plain text in the file)."},{"id":"processors","title":"Processors","role":"content","text":"The `processors` section selects the stream processor and configures its behavior. The default\nis the Flink processor, which is what we recommend. To run a pipeline on the classic processor instead,\nset `type` to `classic`:\n\n[code example]\n\nSee [Differences between the classic and Flink processors](https://redis.io/docs/latest/integrate/redis-data-integration/architecture/classic-vs-flink)\nand [Migrate from the classic processor to the Flink processor](https://redis.io/docs/latest/integrate/redis-data-integration/installation/migration-classic-to-flink)\nfor existing pipelines."},{"id":"tune-classic-processor-performance","title":"Tune Classic processor performance","role":"content","text":"The Classic processor uses the top-level batch, queue, initial-sync, and stream\npolling properties. Larger batches can improve throughput but use more memory and\ncan increase latency while RDI waits for a batch to fill.\n\n[code example]"},{"id":"tune-flink-processor-performance","title":"Tune Flink processor performance","role":"content","text":"The Flink processor uses `processors.advanced` for batch behavior, parallelism,\nand memory. Don't use Classic queue and initial-sync properties to tune Flink.\n\n[code example]\n\nFor Kubernetes installations, the number of available task slots is the number\nof TaskManager replicas multiplied by `taskmanager.numberOfTaskSlots`. When you\nomit `parallelism.default`, Flink uses the available task slots. Adding task slots\ncan increase initial snapshot throughput. Size `taskmanager.memory.process.size`\nfor the work done by each TaskManager, especially when jobs use transformations.\n\nThe `advanced.source.batch.size`, `advanced.source.batch.timeout.ms`, and\n`advanced.target.batch.size` properties override their top-level aliases when\nboth forms are present. Change other Flink settings only when instructed by Redis\nsupport. See the [configuration file reference](https://redis.io/docs/latest/integrate/redis-data-integration/reference/config-yaml-reference#processorsadvanced)\nfor all Flink processor properties."},{"id":"choose-the-redis-data-type","title":"Choose the Redis data type","role":"content","text":"Set `target_data_type` to `hash` (the default) or `json`. The `json` option\nrequires JSON support in the target database. A job file can override this\nsetting for its output.\n\n[code example]\n\nSee [Job files](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples)\nfor the data types available to job outputs."},{"id":"confirm-writes-reached-a-replica","title":"Confirm writes reached a replica","role":"content","text":"Use these properties only when target database replication is enabled and a\nhealthy replica is available:\n\n[code example]\n\nFor the Flink processor, the corresponding properties under\n`processors.advanced.target` take priority over these top-level properties.\n\nSee also the\n[RDI configuration file reference](https://redis.io/docs/latest/integrate/redis-data-integration/reference/config-yaml-reference#processors)\nfor full details of the other available properties."},{"id":"extended-configuration-example","title":"Extended configuration example","role":"content","text":"The example below is the configuration that\n[`redis-di scaffold`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-scaffold)\ngenerates for a MySQL source named `mysql`, with every property documented inline. Uncomment what\nyou need and delete the rest. See the\n[configuration file reference](https://redis.io/docs/latest/integrate/redis-data-integration/reference/config-yaml-reference)\nfor every supported property.\n\n[code example]"}],"examples":[{"id":"start-with-a-minimal-file-ex0","language":"yaml","code":"sources:\n  mysql:\n    type: cdc\n    logging:\n      level: info\n    connection:\n      type: mysql\n      host: <DB_HOST>\n      port: 3306\n      user: ${MYSQL_DB_USERNAME}\n      password: ${MYSQL_DB_PASSWORD}\n\ntargets:\n  target:\n    connection:\n      type: redis\n      host: <TARGET_DB_HOST>\n      port: 6379\n      password: ${TARGET_DB_PASSWORD}\n\nprocessors:\n  target_data_type: hash","section_id":"start-with-a-minimal-file"},{"id":"build-the-file-with-an-ai-assistant-ex0","language":"text","code":"Help me create a valid Redis Data Integration (RDI) config.yaml file.\n\nUse only these pages as sources for configuration properties and behavior:\n- https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config/\n- https://redis.io/docs/latest/integrate/redis-data-integration/reference/config-yaml-reference/\n- https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/\n\nDo not invent property names. If a requested property is not documented, tell me.\nUse ${NAME} secret references for credentials and certificates. Do not include secret\nvalues in the file. Configure one target Redis database named `target`. Always use the\nFlink processor by setting `processors.type` to `flink`.\n\nAsk me for the following information one question at a time:\n1. Source database type, host, and port.\n2. Databases or schemas to capture.\n3. Tables and columns to capture, including keys for tables without a primary key\n   or unique constraint.\n4. Whether the initial snapshot needs a row filter.\n5. Target Redis host and port, and whether the connection uses TLS or mTLS.\n6. Redis hash or JSON output.\n\nAfter I answer, generate config.yaml. Then list the required secrets and link me to\nthe documented commands to set the secrets and deploy the pipeline.","section_id":"build-the-file-with-an-ai-assistant"},{"id":"choose-a-snapshot-mode-ex0","language":"yaml","code":"sources:\n  mysql:\n    type: cdc\n    connection:\n      type: mysql\n      host: <your-source-host>\n      port: 3306\n      user: ${MYSQL_DB_USERNAME}\n      password: ${MYSQL_DB_PASSWORD}\n    advanced:\n      source:\n        snapshot.mode: initial\n\ntargets:\n  target:\n    connection:\n      type: redis\n      host: <your-target-host>\n      port: 6379\n      password: ${TARGET_DB_PASSWORD}","section_id":"choose-a-snapshot-mode"},{"id":"choose-a-snapshot-mode-ex1","language":"mermaid","code":"%%{init: {\"sequence\": {\"actorMargin\": 15, \"diagramMarginX\": 5, \"width\": 90, \"mirrorActors\": false}}}%%\nsequenceDiagram\n    participant Source as Source<br/>database\n    participant RDI as RDI<br/>collector\n    participant Redis as Target<br/>Redis\n\n    Source->>RDI: Capture<br/>row 42\n    RDI->>Redis: Write<br/>row 42\n    Note over RDI: Collection stops\n    Note over Source: Delete row 42\n    Note over Source,RDI: Required log<br/>history expires\n    RDI->>Source: Restart with<br/>when_needed\n    Note over Source,RDI: Saved position unavailable<br/>New snapshot\n    Source->>RDI: Current rows<br/>(without row 42)\n    RDI->>Redis: Write snapshot<br/>rows\n    Note over Redis: Row 42 is stale<br/>Delete missed","section_id":"choose-a-snapshot-mode"},{"id":"processors-ex0","language":"yaml","code":"processors:\n  type: classic","section_id":"processors"},{"id":"tune-classic-processor-performance-ex0","language":"yaml","code":"processors:\n  type: classic\n  read_batch_size: 2000\n  read_batch_timeout_ms: 100\n  write_batch_size: 200\n  enable_async_processing: true\n  batch_queue_size: 3\n  ack_queue_size: 10\n  initial_sync_processes: 4\n  idle_sleep_time_ms: 200\n  idle_streams_check_interval_ms: 1000\n  busy_streams_check_interval_ms: 5000","section_id":"tune-classic-processor-performance"},{"id":"tune-flink-processor-performance-ex0","language":"yaml","code":"processors:\n  type: flink\n  advanced:\n    source:\n      batch.size: 2000\n      batch.timeout.ms: 100\n      discovery.interval.ms: 1000\n    target:\n      batch.size: 200\n      flush.interval.ms: 100\n    flink:\n      taskmanager.numberOfTaskSlots: 1\n      taskmanager.memory.process.size: 2048m\n    resources:\n      taskManager:\n        replicas: 2","section_id":"tune-flink-processor-performance"},{"id":"choose-the-redis-data-type-ex0","language":"yaml","code":"processors:\n  target_data_type: hash","section_id":"choose-the-redis-data-type"},{"id":"confirm-writes-reached-a-replica-ex0","language":"yaml","code":"processors:\n  wait_enabled: true\n  wait_timeout: 1000\n  retry_on_replica_failure: true","section_id":"confirm-writes-reached-a-replica"},{"id":"extended-configuration-example-ex0","language":"yaml","code":"# Configuration of the RDI sources, targets, and processor. Commented-out properties are optional, and where a default\n# exists the value shown is that default.\n# For an introduction to the pipeline configuration, see\n# https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config/\n# For a reference of every configuration property, see\n# https://redis.io/docs/latest/integrate/redis-data-integration/reference/config-yaml-reference/\n\n# Source databases that are used to capture changes from. Each key is a unique source name.\nsources:\n  mysql:\n    # Type of the source collector. Use `cdc` for Debezium, `flink` for Spanner, or `riotx` for Snowflake.\n    type: cdc\n\n    # Log verbosity of the source collector, one of `trace`, `debug`, `info`, `warn`, or `error`.\n    logging:\n      level: info\n\n    # Connection to the source database.\n    connection:\n      type: mysql\n      # Hostname or IP address of the source database server, for example `localhost`.\n      host: <DB_HOST>\n      # Port that the source database server listens on.\n      port: 3306\n      # User and password are resolved from the source database secret.\n      user: ${MYSQL_DB_USERNAME}\n      password: ${MYSQL_DB_PASSWORD}\n\n    # Databases to capture from the source database. When omitted, all databases are captured.\n    # databases:\n      # - <DATABASE_NAME>\n\n    # Tables to capture from the source database, keyed by table name. A table with no properties, or with an empty\n    # mapping, captures all of its columns. If only one database is listed above, table names can omit the\n    # database prefix.\n    # tables:\n      # <DATABASE_NAME>.<TABLE_NAME1>: {}\n      # <DATABASE_NAME>.<TABLE_NAME2>:\n        # Columns to capture. When omitted, all columns are captured.\n        # columns:\n          # - <COLUMN_NAME>\n        # Columns that form a unique identifier. Only needed when the table has no primary key or unique constraint.\n        # keys:\n          # - <COLUMN_NAME>\n\n    # Advanced properties that override the collector defaults. Only needed for non-standard tuning.\n    # advanced:\n      # Properties of the RDI Collector stream writer. See the full list in\n      # https://debezium.io/documentation/reference/stable/operations/debezium-server.html#_redis_stream\n      # sink:\n        # Number of records that the sink writes to Redis in a single batch. Raise it, together with the source\n        # `max.batch.size` and `max.queue.size`, for higher snapshot throughput.\n        # redis.batch.size: 2048\n\n        # Back pressure on the RDI streams. The sink stops writing once the used memory of the RDI database reaches\n        # `redis.memory.threshold.percentage` of its `maxmemory`, or of `redis.memory.limit.mb` when that is set.\n        # redis.memory.limit.mb: 0\n        # redis.memory.threshold.percentage: 85\n\n        # Whether every write to the RDI streams is verified to have reached a replica shard, which prevents losing\n        # captured changes when the primary shard fails over. It costs write latency, and it has no effect unless the\n        # RDI database is replicated. `redis.wait.retry.enabled` keeps retrying when the verification times out.\n        # redis.wait.enabled: false\n        # redis.wait.timeout.ms: 1000\n        # redis.wait.retry.enabled: false\n        # redis.wait.retry.delay.ms: 1000\n\n      # Properties of the source database connection and of Debezium. See the full list for your source database in\n      # https://debezium.io/documentation/reference/stable/connectors/\n      # source:\n        # When and whether the collector takes an initial snapshot of the source database.\n        # snapshot.mode: initial\n\n        # Performance tuning of the Debezium collector. Rows read from the source database are buffered in a queue of\n        # `max.queue.size` records, serialized by `record.processing.threads` worker threads, and passed on in batches\n        # of `max.batch.size` records, which the sink then writes to Redis in batches of `sink.redis.batch.size`.\n        # Lower this to 2 or 1 when fewer CPUs are available to the collector.\n        # record.processing.threads: 4\n\n        # Interval between two polls of the source database for new changes in milliseconds. Lower it to 100 or below\n        # for lower CDC latency.\n        # poll.interval.ms: 500\n\n        # Raise both sizes, together with `sink.redis.batch.size`, for higher snapshot throughput, and keep the queue\n        # about four times the batch. A larger queue needs a larger heap, so raise `advanced.resources.memory` and set\n        # `-Xmx` in `advanced.java_options` accordingly.\n        # max.batch.size: 2048\n        # max.queue.size: 8192\n\n        # Restrict the snapshot of a table to the rows that the given statement selects. This affects the snapshot only,\n        # for example to skip orders that are still pending.\n        # snapshot.select.statement.overrides: <DATABASE_NAME>.<TABLE_NAME>\n        # snapshot.select.statement.overrides.<DATABASE_NAME>.<TABLE_NAME>: <SELECT_STATEMENT>\n\n        # Let the client retrieve the public key of the server for the TLS handshake. Uncomment when the source\n        # database user authenticates with `caching_sha2_password` over a connection that is not encrypted.\n        # database.allowPublicKeyRetrieval: true\n\n      # Properties of the Quarkus runtime that hosts Debezium Server. See the full list in\n      # https://quarkus.io/guides/all-config\n      # quarkus:\n        # banner.enabled: \"false\"\n\n# Target Redis databases that the processed records are written to. RDI currently only supports a single target that\n# must be named `target`.\ntargets:\n  target:\n    # Connection to the target Redis database.\n    connection:\n      type: redis\n      # Hostname or IP address of the target Redis database, for example `localhost`.\n      host: <TARGET_DB_HOST>\n      # Port that the target Redis database listens on.\n      port: 6379\n      # User of the target Redis database, uncomment when not using the default user.\n      # user: ${TARGET_DB_USERNAME}\n      # Password of the target Redis database.\n      password: ${TARGET_DB_PASSWORD}\n      # TLS configuration, uncomment to connect securely. The key and the certificate must be set together, and the key\n      # password only when the key is protected by one.\n      # cacert: ${TARGET_DB_CACERT}\n      # cert: ${TARGET_DB_CERT}\n      # key: ${TARGET_DB_KEY}\n      # key_password: ${TARGET_DB_KEY_PASSWORD}\n\n# Settings that control how the processor writes the captured records to the targets.\nprocessors:\n  # Processor implementation to run, one of `classic` or `flink`.\n  # The default is `flink`, which is the recommended processor.\n  # See https://redis.io/docs/latest/integrate/redis-data-integration/faq/#which-processor-should-i-use\n  # type: flink\n\n  # Maximum number of records read from the source streams in a single batch.\n  # read_batch_size: 2000\n\n  # Maximum time in milliseconds to wait for a read batch to fill before processing it.\n  # read_batch_timeout_ms: 100\n\n  # Maximum number of records written to a target database in a single batch. Must not exceed read_batch_size.\n  # write_batch_size: 200\n\n  # Whether batches are processed asynchronously, which improves throughput and reduces latency. Applies to the classic\n  # processor only.\n  # enable_async_processing: true\n\n  # Maximum number of batches queued for processing. Applies to the classic processor only.\n  # batch_queue_size: 3\n\n  # Maximum number of batches queued for asynchronous acknowledgement. Applies to the classic processor only.\n  # ack_queue_size: 10\n\n  # Whether incoming records are deduplicated. Applies to the classic processor only.\n  # dedup: false\n\n  # Maximum number of entries kept in the deduplication set. Applies to the classic processor only.\n  # dedup_max_size: 1024\n\n  # How failed records are handled. `ignore` drops them, and `dlq` writes them to the dead letter queue.\n  # error_handling: dlq\n\n  # Maximum number of messages stored per dead letter queue stream.\n  # dlq_max_messages: 1000\n\n  # Data type that the records are stored as. `hash` writes a Redis hash, and `json` writes a RedisJSON document, which\n  # requires the RedisJSON module in the target database.\n  # target_data_type: hash\n\n  # How existing JSON documents are updated. `replace` overwrites the whole document, and `merge` merges the incoming\n  # fields into it.\n  # json_update_strategy: replace\n\n  # Whether JSON documents are merged with the native JSON.MERGE command rather than with Lua scripts. Applies to the\n  # classic processor only.\n  # use_native_json_merge: true\n\n  # Number of parallel processes that perform the initial synchronization. Applies to the classic processor only.\n  # initial_sync_processes: 4\n\n  # Time in milliseconds to sleep between batches while idle. Applies to the classic processor only.\n  # idle_sleep_time_ms: 200\n\n  # Time in milliseconds between two checks for new streams while idle. Applies to the classic processor only.\n  # idle_streams_check_interval_ms: 1000\n\n  # Time in milliseconds between two checks for new streams while busy. Applies to the classic processor only.\n  # busy_streams_check_interval_ms: 5000\n\n  # Maximum number of attempts for a failed write to a target database before giving up.\n  # retry_max_attempts: 5\n\n  # Initial delay in milliseconds before the first retry of a failed write.\n  # retry_initial_delay_ms: 1000\n\n  # Maximum delay in milliseconds between two retries of a failed write.\n  # retry_max_delay_ms: 10000\n\n  # Whether every write is verified to have reached the replica shards of the target database. Enable this only when the\n  # target database is replicated and a healthy replica is available.\n  # wait_enabled: false\n\n  # Maximum time in milliseconds to wait for the replica write verification.\n  # wait_timeout: 1000\n\n  # Whether a write is retried until the replica verification succeeds, rather than given up on after the first failure.\n  # retry_on_replica_failure: true\n\n  # Logging settings of the processor. Applies to the Flink processor only.\n  # logging:\n    # Log verbosity of the processor, one of `trace`, `debug`, `info`, `warn`, or `error`.\n    # level: info","section_id":"extended-configuration-example"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/prepare-dbs/aws-aurora-rds/aws-aur-mysql","title":"Prepare AWS Aurora MySQL/AWS RDS MySQL for RDI","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/aws-aurora-rds/aws-aur-mysql/","summary":"Enable CDC features in your source databases","aliases":["/integrate/redis-data-integration/ingest/data-pipelines/prepare-dbs/aws-aurora-rds/aws-aur-mysql/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-17T11:46:55+01:00","page_type":"content","content_hash":"ef66d1ad130d5d1bddaf1eb261604988183e28596e3137458a493714707242a8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Follow the steps in the sections below to prepare an [AWS Aurora MySQL](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/CHAP_GettingStartedAurora.CreatingConnecting.Aurora.html) or [AWS RDS MySQL](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_GettingStarted.CreatingConnecting.MySQL.html) database.\n\nSelect the steps for your database type.\n\n**AWS Aurora MySQL:**\n\n[code example]"},{"id":"add-an-aurora-reader-node","title":"Add an Aurora reader node","role":"content","text":"RDI requires that your Aurora MySQL database has at least one replica or reader node. \n\nTo add a reader node to an existing database, select **Add reader** from the **Actions** menu of the database and [add a reader node](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/aurora-replicas-adding.html).\n\nYou can also create one during database creation by selecting **Create an Aurora Replica or Reader node in a different AZ (recommended for scaled availability)** under **Availability & durability > Multi-AZ deployment**."},{"id":"aurora-create-and-apply-parameter-group","title":"Create and apply parameter group","role":"content","text":"RDI requires some changes to database parameters. On AWS Aurora, you change these parameters via a parameter group.\n\n[code example]\n\n1. <a id=\"aurora-create-a-parameter-group\"></a>\n    In the [Relational Database Service (RDS) console](https://console.aws.amazon.com/rds/), navigate to **Parameter groups**.\n    \n    If you have no existing parameter group,\n    [create a new parameter group](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/USER_WorkingWithParamGroups.CreatingCluster.html)\n    with the following settings:\n\n    | Name | Value |\n    | :-- | :-- |\n    | **Parameter group name**  | Enter a suitable parameter group name, like `rdi-mysql` |\n    | **Description**  | (Optional) Enter a description for the parameter group |\n    | **Engine Type**  | Choose **Aurora MySQL**.  |\n    | **Parameter group family**  | Choose **aurora-mysql8.0**. |\n    | **Type**  | Select **DB Parameter Group**. |\n\n    Select **Create** to create the parameter group.\n\n    If you *do* have an existing parameter group, select it and then either:\n\n    -   Select **Edit** from **Parameter group actions** to \n        [modify the parameter group](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/USER_WorkingWithParamGroups.ModifyingCluster.html)\n        with the settings shown in the table above.\n    -   Select **Copy** from **Parameter group actions** to\n        [copy the existing parameter group](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/USER_WorkingWithParamGroups.CopyingCluster.html)\n        and then modify the copy with the settings shown in the table above.\n\n1. <a id=\"aurora-apply-the-parameter-group\"></a>\n    Ensure that the parameter group you have just created or modified is selected\n    and then select **Edit**. Change the following parameters:\n\n    | Name | Value |\n    | :-- | :-- |\n    | `binlog_format`  | `ROW` |\n    | `binlog_row_image`  | `FULL` |\n    | `gtid_mode`  | `ON` |\n    | `enforce_gtid_consistency`  | `ON` |\n\n    Select **Save Changes** to apply the changes to the parameter group.\n\n1. <a id=\"aurora-apply-the-parameter-group-to-the-database\"></a>\n    Go back to your target database on the RDS console, select **Modify** and then scroll down to **Additional Configuration**. Set the **DB Cluster Parameter Group** to the group you just created.\n\n    Select **Save changes** to apply the parameter group to the new database.\n\n1. <a id=\"aurora-reboot-the-database-instance\"></a>\n    Reboot your database instance. See [Rebooting a DB instance within an Aurora cluster](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/aurora-reboot-db-instance.html) for more information.\n\n<a id=\"aurora-create-debezium-user\"></a>"},{"id":"create-debezium-user","title":"Create Debezium user","role":"content","text":"The Debezium connector needs a user account to connect to MySQL. This\nuser must have appropriate permissions on all databases where you want Debezium\nto capture changes.\n\n1. Connect to your database as an admin user and create a new user for the connector:\n\n    [code example]\n\n    Replace `<username>` and `<password>` with a username and password for the new user.\n\n    The `%` means that the user can connect from any client. If you want to restrict the user to connect only from the RDI host, replace `%` with the IP address of the RDI host.\n\n1. Grant the user the necessary permissions:\n\n    [code example]\n\n    Replace `<username>` with the username of the Debezium user.\n\n    You can also grant SELECT permissions for specific tables only. The other permissions are global and cannot be restricted to specific tables.\n\n    [code example]\n\n1. Finalize the user's permissions:\n\n    [code example]\n\n**AWS RDS MySQL:**\n\n[code example]"},{"id":"rds-create-and-apply-parameter-group","title":"Create and apply parameter group","role":"content","text":"RDI requires some changes to database parameters. On AWS RDS, you change these parameters via a parameter group.\n\n[code example]\n\n1. <a id=\"rds-create-a-parameter-group\"></a>\n    In the [Relational Database Service (RDS) console](https://console.aws.amazon.com/rds/), navigate to **Parameter groups**.\n    \n    If you have no existing parameter group,\n    [create a new parameter group](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/USER_WorkingWithParamGroups.CreatingCluster.html)\n    with the following settings:\n\n    | Name | Value |\n    | :-- | :-- |\n    | **Parameter group name**  | Enter a suitable parameter group name, like `rdi-mysql` |\n    | **Description**  | (Optional) Enter a description for the parameter group |\n    | **Engine Type**  | Choose **MySQL Community**.  |\n    | **Parameter group family**  | Choose **mysql8.0**. |\n\n    Select **Create** to create the parameter group.\n\n    If you *do* have an existing parameter group, select it and then either:\n\n    -   Select **Edit** from **Parameter group actions** to \n        [modify the parameter group](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/USER_WorkingWithParamGroups.ModifyingCluster.html)\n        with the settings shown in the table above.\n    -   Select **Copy** from **Parameter group actions** to\n        [copy the existing parameter group](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/USER_WorkingWithParamGroups.CopyingCluster.html)\n        and then modify the copy with the settings shown in the table above.\n\n1. <a id=\"rds-apply-the-parameter-group\"></a>\n    Ensure that the parameter group you have just created or modified is selected\n    and then select **Edit**. Change the following parameters:\n\n    | Name | Value |\n    | :-- | :-- |\n    | `binlog_format`  | `ROW` |\n    | `binlog_row_image`  | `FULL` |\n\n    Select **Save Changes** to apply the changes to the parameter group.\n\n1. <a id=\"rds-apply-the-parameter-group-to-the-database\"></a>\n    Go back to your target database on the RDS console, select **Modify** and then scroll down to **Additional Configuration**. Set the **DB Cluster Parameter Group** to the group you just created.\n\n    Select **Save changes** to apply the parameter group to the new database.\n\n1. <a id=\"rds-reboot-the-database-instance\"></a>\n    Reboot your database instance. See [Rebooting a DB instance](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_RebootInstance.html) for more information.\n\n<a id=\"rds-create-debezium-user\"></a>"},{"id":"create-debezium-user-1","title":"Create Debezium user","role":"content","text":"The Debezium connector needs a user account to connect to MySQL. This\nuser must have appropriate permissions on all databases where you want Debezium\nto capture changes.\n\n1. Connect to your database as an admin user and create a new user for the connector:\n\n    [code example]\n\n    Replace `<username>` and `<password>` with a username and password for the new user.\n\n    The `%` means that the user can connect from any client. If you want to restrict the user to connect only from the RDI host, replace `%` with the IP address of the RDI host.\n\n1. Grant the user the necessary permissions:\n\n    [code example]\n\n    Replace `<username>` with the username of the Debezium user.\n\n    You can also grant SELECT permissions for specific tables only. The other permissions are global and cannot be restricted to specific tables.\n\n    [code example]\n\n1. Finalize the user's permissions:\n\n    [code example]"}],"examples":[{"id":"overview-ex0","language":"checklist {id=\"auroramysql\" nointeractive=\"true\" }","code":"- [ ] [Add an Aurora reader node](#add-an-aurora-reader-node)\n- [ ] [Create and apply parameter group](#aurora-create-and-apply-parameter-group)\n- [ ] [Create Debezium user](#aurora-create-debezium-user)","section_id":"overview"},{"id":"aurora-create-and-apply-parameter-group-ex0","language":"checklist {id=\"auroramysql-param-group\" nointeractive=\"true\" }","code":"- [ ] [Create/modify a parameter group](#aurora-create-a-parameter-group)\n- [ ] [Apply the parameter group](#aurora-apply-the-parameter-group)\n- [ ] [Apply the parameter group to the database](#aurora-apply-the-parameter-group-to-the-database)\n- [ ] [Reboot the database instance](#aurora-reboot-the-database-instance)","section_id":"aurora-create-and-apply-parameter-group"},{"id":"create-debezium-user-ex0","language":"sql","code":"CREATE USER '<username>'@'%' IDENTIFIED BY '<password>';","section_id":"create-debezium-user"},{"id":"create-debezium-user-ex1","language":"sql","code":"GRANT SELECT, RELOAD, SHOW DATABASES, REPLICATION SLAVE, REPLICATION CLIENT, LOCK TABLES ON *.* TO '<username>'@'%';","section_id":"create-debezium-user"},{"id":"create-debezium-user-ex2","language":"sql","code":"GRANT RELOAD, SHOW DATABASES, REPLICATION SLAVE, REPLICATION CLIENT, LOCK TABLES ON *.* TO '<username>'@'%';\n    GRANT SELECT ON <database>.<table> TO '<username>'@'%';","section_id":"create-debezium-user"},{"id":"create-debezium-user-ex3","language":"sql","code":"FLUSH PRIVILEGES;","section_id":"create-debezium-user"},{"id":"create-debezium-user-ex4","language":"checklist {id=\"rds-mysql-list\" nointeractive=\"true\" }","code":"- [ ] [Create and apply parameter group](#rds-create-and-apply-parameter-group)\n- [ ] [Create Debezium user](#rds-create-debezium-user)","section_id":"create-debezium-user"},{"id":"rds-create-and-apply-parameter-group-ex0","language":"checklist {id=\"rds-mysql-param-group\" nointeractive=\"true\" }","code":"- [ ] [Create/modify a parameter group](#rds-create-a-parameter-group)\n- [ ] [Apply the parameter group](#rds-apply-the-parameter-group)\n- [ ] [Apply the parameter group to the database](#rds-apply-the-parameter-group-to-the-database)\n- [ ] [Reboot the database instance](#rds-reboot-the-database-instance)","section_id":"rds-create-and-apply-parameter-group"},{"id":"create-debezium-user-1-ex0","language":"sql","code":"CREATE USER '<username>'@'%' IDENTIFIED BY '<password>';","section_id":"create-debezium-user-1"},{"id":"create-debezium-user-1-ex1","language":"sql","code":"GRANT SELECT, RELOAD, SHOW DATABASES, REPLICATION SLAVE, REPLICATION CLIENT, LOCK TABLES ON *.* TO '<username>'@'%';","section_id":"create-debezium-user-1"},{"id":"create-debezium-user-1-ex2","language":"sql","code":"GRANT RELOAD, SHOW DATABASES, REPLICATION SLAVE, REPLICATION CLIENT, LOCK TABLES ON *.* TO '<username>'@'%';\n    GRANT SELECT ON <database>.<table> TO '<username>'@'%';","section_id":"create-debezium-user-1"},{"id":"create-debezium-user-1-ex3","language":"sql","code":"FLUSH PRIVILEGES;","section_id":"create-debezium-user-1"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/prepare-dbs/aws-aurora-rds/aws-aur-pgsql","title":"Prepare AWS Aurora PostgreSQL/AWS RDS PostgreSQL for RDI","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/aws-aurora-rds/aws-aur-pgsql/","summary":"Prepare AWS Aurora PostgreSQL databases to work with RDI","aliases":["/integrate/redis-data-integration/ingest/data-pipelines/prepare-dbs/aws-aur-pgsql/","/integrate/redis-data-integration/ingest/data-pipelines/prepare-dbs/aws-aurora-rds/aws-aur-pgsql/","/integrate/redis-data-integration/data-pipelines/prepare-dbs/aws-aur-pgsql/"],"tags":["docs","integrate","rs","rc","rdi"],"last_updated":"2026-10-19T20:38:06+03:00","page_type":"content","content_hash":"69984067ed335cf7eaec8c249871f788a8f8be75041ddd892126b465af23a52c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Follow the steps in the sections below to prepare an\n[AWS Aurora PostgreSQL](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/CHAP_GettingStartedAurora.CreatingConnecting.AuroraPostgreSQL.html) or [AWS RDS PostgreSQL](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_GettingStarted.CreatingConnecting.PostgreSQL.html)\ndatabase to work with RDI.\n\n[code example]"},{"id":"create-and-apply-parameter-group","title":"Create and apply parameter group","role":"content","text":"RDI requires some changes to database parameters. On AWS RDS and AWS Aurora, you change these parameters via a parameter group.\n\n[code example]\n\n1. <a id=\"create-a-parameter-group\"></a>\n    In the [Relational Database Service (RDS) console](https://console.aws.amazon.com/rds/), navigate to **Parameter groups**.\n    \n    If you have no existing parameter group,\n    [create a new parameter group](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/USER_WorkingWithParamGroups.CreatingCluster.html)\n    with the following settings:\n\n    | Name | Value |\n    | :-- | :-- |\n    | **Parameter group name**  | Enter a suitable parameter group name, like `rdi-aurora-pg` or `rdi-rds-pg` |\n    | **Description**  | (Optional) Enter a description for the parameter group |\n    | **Engine Type**  | Choose **Aurora PostgreSQL** for Aurora PostgreSQL or **PostgreSQL** for AWS RDS PostgreSQL. |\n    | **Parameter group family**  | Choose **aurora-postgresql15** for Aurora PostgreSQL or **postgresql13** for AWS RDS PostgreSQL. |\n\n    Select **Create** to create the parameter group.\n\n    If you *do* have an existing parameter group, select it and then either:\n\n    -   Select **Edit** from **Parameter group actions** to \n        [modify the parameter group](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/USER_WorkingWithParamGroups.ModifyingCluster.html)\n        with the settings shown in the table above.\n    -   Select **Copy** from **Parameter group actions** to\n        [copy the existing parameter group](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/USER_WorkingWithParamGroups.CopyingCluster.html)\n        and then modify the copy with the settings shown in the table above.\n\n1. <a id=\"apply-the-parameter-group\"></a>\n    Ensure that the parameter group you have just created or modified is selected\n    and then select **Edit**. Change the following parameter:\n\n    | Name | Value |\n    | :-- | :-- |\n    | `rds.logical_replication`  | `1` |\n\n    Select **Save Changes** to apply the changes to the parameter group.\n\n1. <a id=\"apply-the-parameter-group-to-the-database\"></a>\n    Go back to your database on the RDS console, select **Modify** and then scroll down to **Additional Configuration**. Set the **DB Cluster Parameter Group** to the group you just created. \n\n    Select **Save changes** to apply the parameter group to your database.\n\n1. <a id=\"reboot-the-database-instance\"></a>\n    Reboot your database instance. See [Rebooting a DB instance within an Aurora cluster](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/aurora-reboot-db-instance.html) or [Rebooting a DB instance (RDS)](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_RebootInstance.html) for more information."},{"id":"create-debezium-user","title":"Create Debezium user","role":"content","text":"The Debezium connector needs a user account to connect to PostgreSQL. This\nuser must have appropriate permissions on all databases where you want Debezium\nto capture changes.\n\n[code example]\n\n1. <a id=\"connect-to-postgresql-as-the-postgres-user\"></a>\n    Connect to PostgreSQL as the `postgres` user and create a new user for the connector:\n\n    [code example]\n\n    Replace `<username>` and `<password>` with a username and password for the new user.\n\n1. <a id=\"grant-the-user-the-necessary-replication-permissions\"></a>\n    Grant the user the necessary replication permissions:\n\n    [code example]\n\n    Replace `<username>` with the username of the Debezium user.\n\n1. <a id=\"grant-the-user-access-to-the-database\"></a>\n    Connect to your database as the `postgres` user and grant the new user access to one or more schemas in the database:\n\n    [code example]\n\n    Replace `<username>` with the username of the Debezium user and `<schema>` with the schema name.\n\n1. <a id=\"allow-connection-from-debezium-user\"></a>\n    Connect to your database as the `postgres` user and allow the Debezium user to connect to the database:\n\n    [code example]\n\n    Replace `<database>` with the name of the database and `<username>` with the username of the Debezium user.\n\n1. <a id=\"grant-usage-to-schema\"></a>\n    Connect to your database as the `postgres` user and grant the new user usage on the schema:\n\n    [code example]\n\n    Replace `<schema>` with the schema name and `<username>` with the username of the Debezium user.\n\n1. <a id=\"grant-privileges-for-the-future\"></a>\n    For example, your database setup might use three roles:\n\n    - `postgres`: the administrator setting up RDI.\n    - `app_migrations`: the role that creates application tables.\n    - `debezium`: the user RDI uses to read those tables.\n\n    These names are examples. Use your existing roles.\n\n    The administrator can grant Debezium access to existing tables, so the initial snapshot works. Later, `app_migrations` creates a new table. Defaults set for `postgres` do not give Debezium access to that table, so reading it can fail.\n\n    Set the defaults for the role that creates the tables. In this example, use `app_migrations` as `<table_creator>` and `debezium` as the Debezium user.\n\n    Connect as the table creator and grant the Debezium user access to future tables:\n\n    [code example]\n\n    Replace `<table_creator>` with the role that runs `CREATE TABLE`, often your migration role. Replace `<schema>` with the schema name and `<username>` with the Debezium user. An administrator with permission to change that role's defaults can also run this command.\n\n    Repeat for each table creator and schema. This command covers future tables only. Keep the grants for existing tables.\n\n1. <a id=\"create-publication\"></a>\n    Connect to your database as the `postgres` user and create a publication containing\n    only the tables RDI should capture. First [check their replica identities](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/postgresql#check-replica-identity-and-choose-a-publication-mode).\n    Replace the example table names with your selected tables:\n\n    [code example]\n\n    Set `publication.name` to `rdi_publication` and `publication.autocreate.mode` to\n    `disabled` under the source's advanced source settings. On Redis Cloud, enter these\n    values under [**Collector properties**](https://redis.io/docs/latest/operate/rc/rdi/setup#postgresql-publications).\n    Update the publication's table list when you change the tables RDI captures.\n\n    Alternatively, let RDI manage the publication with `publication.autocreate.mode: filtered`\n    after granting the [required ownership and creation permissions](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/postgresql#set-privileges-for-debezium-to-create-postgresql-publications-with-pgoutput).\n\n    Use `FOR ALL TABLES` only when you intend to publish every current and future table.\n    An application `UPDATE` or `DELETE` on any published table without replica identity can\n    fail, even if RDI does not capture that table. If you already created an all-tables\n    publication, follow [Replace an existing all-tables publication](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/postgresql#replace-an-existing-all-tables-publication)."}],"examples":[{"id":"overview-ex0","language":"checklist {id=\"aurorapostgresql\" nointeractive=\"true\" }","code":"- [ ] [Create and apply parameter group](#create-and-apply-parameter-group)\n- [ ] [Create Debezium user](#create-debezium-user)","section_id":"overview"},{"id":"create-and-apply-parameter-group-ex0","language":"checklist {id=\"aurorapostgresql-param-group\" nointeractive=\"true\" }","code":"- [ ] [Create/modify a parameter group](#create-a-parameter-group)\n- [ ] [Apply the parameter group](#apply-the-parameter-group)\n- [ ] [Apply the parameter group to the database](#apply-the-parameter-group-to-the-database)\n- [ ] [Reboot the database instance](#reboot-the-database-instance)","section_id":"create-and-apply-parameter-group"},{"id":"create-debezium-user-ex0","language":"checklist {id=\"aurorapostgresql-create-debezium-user\" nointeractive=\"true\" }","code":"- [ ] [Connect to PostgreSQL as the `postgres` user](#connect-to-postgresql-as-the-postgres-user)\n- [ ] [Grant the user the necessary replication permissions](#grant-the-user-the-necessary-replication-permissions)\n- [ ] [Grant the user access to the database](#grant-the-user-access-to-the-database)","section_id":"create-debezium-user"},{"id":"create-debezium-user-ex1","language":"sql","code":"CREATE ROLE <username> WITH LOGIN PASSWORD '<password>' VALID UNTIL 'infinity';","section_id":"create-debezium-user"},{"id":"create-debezium-user-ex2","language":"sql","code":"GRANT rds_replication TO <username>;","section_id":"create-debezium-user"},{"id":"create-debezium-user-ex3","language":"sql","code":"GRANT SELECT ON ALL TABLES IN SCHEMA <schema> TO <username>;","section_id":"create-debezium-user"},{"id":"create-debezium-user-ex4","language":"sql","code":"GRANT CONNECT ON DATABASE <database> TO <username>;","section_id":"create-debezium-user"},{"id":"create-debezium-user-ex5","language":"sql","code":"GRANT USAGE ON SCHEMA <schema> TO <username>;","section_id":"create-debezium-user"},{"id":"create-debezium-user-ex6","language":"sql","code":"ALTER DEFAULT PRIVILEGES FOR ROLE <table_creator> IN SCHEMA <schema>\n        GRANT SELECT ON TABLES TO <username>;","section_id":"create-debezium-user"},{"id":"create-debezium-user-ex7","language":"sql","code":"CREATE PUBLICATION rdi_publication\n    FOR TABLE public.customers, public.orders;","section_id":"create-debezium-user"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/prepare-dbs/aws-aurora-rds/aws-rds-sqlserver","title":"Prepare Microsoft SQL Server on AWS RDS for RDI","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/aws-aurora-rds/aws-rds-sqlserver/","summary":"Enable CDC features in your source databases","aliases":["/integrate/redis-data-integration/ingest/data-pipelines/prepare-dbs/aws-aurora-rds/aws-rds-sqlserver/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-18T16:39:32+01:00","page_type":"content","content_hash":"197aa80cc661236967a138b9c42eeceb20128ff5f78b2564f4a173964e178fec","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Follow the steps in the sections below to prepare a [Microsoft SQL Server on AWS RDS](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_GettingStarted.CreatingConnecting.SQLServer.html) database to work with RDI.\n\n> [!NOTE]\n> Change Data Capture (CDC) is not supported on SQL Server Express Edition. Only the Standard, Enterprise, and Developer editions support CDC and are supported by RDI.\n\n[code example]"},{"id":"create-the-debezium-user","title":"Create the Debezium user","role":"content","text":"The Debezium connector needs a user account to connect to SQL Server. This\nuser must have appropriate permissions on all databases where you want Debezium\nto capture changes.\n\n[code example]\n\n1. <a id=\"connect-to-sql-server-as-an-admin-user\"></a>\n    Connect to your database as an admin user and create a new user for the connector:\n\n    [code example]\n\n    Replace `<username>` and `<password>` with a username and password for the new user and replace `<database>` with the name of your database.\n\n1. <a id=\"grant-the-user-the-necessary-permissions\"></a>\n    Grant the user the necessary permissions:\n\n    [code example]\n\n    Replace `<username>` with the username of the Debezium user and replace `<database>` with the name of your database."},{"id":"enable-cdc-on-the-database","title":"Enable CDC on the database","role":"content","text":"Change Data Capture (CDC) must be enabled for the database and for each table you want to capture.\n\n[code example]\n\n1. <a id=\"enable-cdc-for-the-database\"></a>\n    Enable CDC for the database by running the following command:\n\n    [code example]\n\n    Replace `<database>` with the name of your database.\n\n1. <a id=\"enable-cdc-for-each-table-you-want-to-capture\"></a>\n    Enable CDC for each table you want to capture by running the following commands:\n\n    [code example]\n\n    Replace `<database>` with the name of your database, `<schema>` with the name of the schema containing the table, `<table>` with the name of the table, and `<role>` with the name of a new role that will be created to manage access to the CDC data. \n\n    > [!NOTE]\n    > The value for `@role_name` can’t be a fixed database role, such as `db_datareader`. \n    > Specifying a new name will create a corresponding database role that has full access to the\n    > captured change data.\n\n1. <a id=\"add-the-debezium-user-to-the-cdc-role\"></a>\n    Add the Debezium user to the CDC role:\n\n    [code example]\n\n    Replace `<role>` with the name of the role you created in the previous step and replace `<username>` with the username of the Debezium user."}],"examples":[{"id":"overview-ex0","language":"checklist {id=\"rds-sqlserver-list\" nointeractive=\"true\" }","code":"- [ ] [Create the Debezium user](#create-the-debezium-user)\n- [ ] [Enable CDC on the database](#enable-cdc-on-the-database)","section_id":"overview"},{"id":"create-the-debezium-user-ex0","language":"checklist {id=\"rds-sqlserver-create-debezium-user\" nointeractive=\"true\" }","code":"- [ ] [Connect to SQL Server as an admin user](#connect-to-sql-server-as-an-admin-user)\n- [ ] [Grant the user the necessary permissions](#grant-the-user-the-necessary-permissions)","section_id":"create-the-debezium-user"},{"id":"create-the-debezium-user-ex1","language":"sql","code":"USE master\n    GO\n    CREATE LOGIN <username> WITH PASSWORD = '<password>'\n    GO\n    USE <database>\n    GO\n    CREATE USER <username> FOR LOGIN <username>\n    GO","section_id":"create-the-debezium-user"},{"id":"create-the-debezium-user-ex2","language":"sql","code":"USE master\n    GO\n    GRANT VIEW SERVER STATE TO <username>\n    GO\n    USE <database>\n    GO\n    EXEC sp_addrolemember N'db_datareader', N'<username>'\n    GO","section_id":"create-the-debezium-user"},{"id":"enable-cdc-on-the-database-ex0","language":"checklist {id=\"rds-sqlserver-enable-cdc\" nointeractive=\"true\" }","code":"- [ ] [Enable CDC for the database](#enable-cdc-for-the-database)\n- [ ] [Enable CDC for each table you want to capture](#enable-cdc-for-each-table-you-want-to-capture)\n- [ ] [Add the Debezium user to the CDC role](#add-the-debezium-user-to-the-cdc-role)","section_id":"enable-cdc-on-the-database"},{"id":"enable-cdc-on-the-database-ex1","language":"sql","code":"EXEC msdb.dbo.rds_cdc_enable_db '<database>'\n    GO","section_id":"enable-cdc-on-the-database"},{"id":"enable-cdc-on-the-database-ex2","language":"sql","code":"USE <database>\n    GO\n    EXEC sys.sp_cdc_enable_table\n    @source_schema = N'<schema>',\n    @source_name   = N'<table>', \n    @role_name     = N'<role>',\n    @supports_net_changes = 0\n    GO","section_id":"enable-cdc-on-the-database"},{"id":"enable-cdc-on-the-database-ex3","language":"sql","code":"USE <database>\n    GO\n    EXEC sp_addrolemember N'<role>', N'<username>'\n    GO","section_id":"enable-cdc-on-the-database"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/prepare-dbs/aws-aurora-rds","title":"Prepare AWS RDS and Aurora databases for RDI","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/aws-aurora-rds/","summary":"Learn how to prepare AWS RDS and Aurora databases for RDI.","aliases":["/integrate/redis-data-integration/ingest/data-pipelines/prepare-dbs/aws-aurora-rds/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2025-06-16T11:53:30-04:00","children":[{"id":"integrate/redis-data-integration/data-pipelines/prepare-dbs/aws-aurora-rds/aws-aur-pgsql","summary":"Prepare AWS Aurora PostgreSQL databases to work with RDI","title":"Prepare AWS Aurora PostgreSQL/AWS RDS PostgreSQL for RDI","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/aws-aurora-rds/aws-aur-pgsql/"},{"id":"integrate/redis-data-integration/data-pipelines/prepare-dbs/aws-aurora-rds/aws-aur-mysql","summary":"Enable CDC features in your source databases","title":"Prepare AWS Aurora MySQL/AWS RDS MySQL for RDI","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/aws-aurora-rds/aws-aur-mysql/"},{"id":"integrate/redis-data-integration/data-pipelines/prepare-dbs/aws-aurora-rds/aws-rds-sqlserver","summary":"Enable CDC features in your source databases","title":"Prepare Microsoft SQL Server on AWS RDS for RDI","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/aws-aurora-rds/aws-rds-sqlserver/"}],"page_type":"content","content_hash":"974423335ccb04e4b340c375f3c4007e9d3f228cf1aad3095bd2d47a0285013c","sections":[{"id":"content","title":"Content","role":"content","text":"You can use RDI with databases on [AWS Relational Database Service (RDS)](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Welcome.html) and [AWS Aurora](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/CHAP_AuroraOverview.html).\n\nThe pages in this section give detailed instructions to get your source\ndatabase ready for Debezium to use:"}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/prepare-dbs/gcp-cloud-sql-mysql","title":"Prepare Google Cloud SQL for MySQL for RDI","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/gcp-cloud-sql-mysql/","summary":"Prepare a Google Cloud SQL for MySQL database to work with RDI","tags":["docs","integrate","rs","rdi"],"last_updated":"2026-10-05T11:23:44+03:00","page_type":"content","content_hash":"bc44c9a9ae62c5a269ec2f8916d2cd5a9bae254e8c5ef08652f557f1ba7dd55f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Google Cloud SQL for MySQL](https://cloud.google.com/sql/docs/mysql) is a\nmanaged MySQL service on Google Cloud. RDI captures changes from an instance of Cloud SQL\nfor MySQL through its binary log (binlog), in the same way it would for a\nself-managed MySQL server.\n\n> [!NOTE]\n> This page describes Cloud SQL for MySQL configured for a self-managed RDI\n> deployment (VM or Kubernetes). The integration was validated with RDI 2.0.0\n> and Cloud SQL for MySQL 8.0 on a Regional (high availability) instance, using\n> a public IP connection with encrypted connections enforced. See\n> [RDI in Redis Cloud](https://redis.io/docs/latest/integrate/redis-data-integration#rdi-in-redis-cloud)\n> for the source databases that the managed service supports.\n\nCloud SQL differs from a self-managed MySQL source in the following ways:\n\n- You can't edit the MySQL configuration file. You enable binary logging as an\n  instance setting and set other server variables as Cloud SQL\n  [database flags](https://cloud.google.com/sql/docs/mysql/flags).\n- Cloud SQL already sets `binlog_format` to `ROW` and enables GTIDs, so you don't\n  need to change them.\n- Cloud SQL can enforce encrypted connections. If it does, you must configure RDI\n  to connect over TLS (Transport Layer Security), or the connection fails with an\n  error that looks like a credentials problem.\n- On a Regional instance, a failover moves the primary to another zone. RDI\n  reconnects automatically, as described in\n  [Failover on Regional instances](#failover-on-regional-instances).\n\nThe following checklist summarizes the steps to prepare Cloud SQL for MySQL to work with RDI:\n\n[code example]"},{"id":"1-enable-binary-logging","title":"1. Enable binary logging","role":"content","text":"RDI reads changes from the MySQL binlog, which Cloud SQL only writes when binary\nlogging is enabled. Binary logging requires automated backups.\n\nTo enable binary logging when you create an instance, pass `--enable-bin-log` to\n[`gcloud sql instances create`](https://cloud.google.com/sdk/gcloud/reference/sql/instances/create).\nThe following example shows how to do this and also sets the binlog row image flag from\n[step 2](#2-set-the-binlog-row-image) and enforces encrypted connections:\n\n[code example]\n\nTo enable binary logging on an existing instance, run:\n\n[code example]\n\nIn the Google Cloud console, binary logging is part of\n[point-in-time recovery](https://cloud.google.com/sql/docs/mysql/backup-recovery/pitr).\nYou must therefore enable point-in-time recovery for the instance to enable binary logging."},{"id":"2-set-the-binlog-row-image","title":"2. Set the binlog row image","role":"content","text":"RDI requires the full row image. Set the `binlog_row_image` database flag to\n`full`:\n\n[code example]\n\nThe flag value must be lowercase. Cloud SQL rejects `binlog_row_image=FULL` (that is with `FULL` in uppercase)\nwith the error `FULL was not an expected string`. MySQL reports the value as\n`FULL` after you set it.\n\n> [!WARNING]\n> The `--database-flags` option replaces all the database flags that are\n> currently set on the instance. If the instance already has other flags, include\n> them in the same command. Changing database flags can restart the instance. See\n> [Configure database flags](https://cloud.google.com/sql/docs/mysql/flags) for\n> details.\n\nYou can also set the flag in the Google Cloud console, in the **Flags** section\nwhen you edit the instance."},{"id":"3-check-the-binlog-settings","title":"3. Check the binlog settings","role":"content","text":"Connect to the instance with the\n[MySQL CLI client](https://dev.mysql.com/doc/refman/8.0/en/mysql.html) and run\nthe following query:\n\n[code example]\n\nCheck that the result has the following values:\n\n| Variable | Value |\n| :-- | :-- |\n| `log_bin` | `ON` |\n| `binlog_format` | `ROW` |\n| `binlog_row_image` | `FULL` |\n| `gtid_mode` | `ON` |\n| `binlog_row_value_options` | Empty (not `PARTIAL_JSON`) |\n\nSee\n[Check `binlog_row_value_options`](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/my-sql-mariadb#6-check-binlog_row_value_options)\nto learn why `PARTIAL_JSON` is a problem."},{"id":"4-allow-network-access-from-rdi","title":"4. Allow network access from RDI","role":"content","text":"If RDI connects to the instance's public IP address, add the public egress IP\naddress of the RDI host or Kubernetes cluster to the instance's\n[authorized networks](https://cloud.google.com/sql/docs/mysql/authorize-networks).\nUse a `/32` CIDR for an individual IPv4 address:\n\n[code example]\n\nThe `--authorized-networks` option replaces the existing list, so include any\nnetworks that are already authorized."},{"id":"5-create-a-cdc-user","title":"5. Create a CDC user","role":"content","text":"The Debezium connector needs a MySQL user account to connect to the instance.\nCreate the user with\n[`gcloud sql users create`](https://cloud.google.com/sdk/gcloud/reference/sql/users/create)\nand set `--host` explicitly:\n\n[code example]\n\nThe value of `%` for `host` lets the user connect from any client address. To restrict the user\nto the RDI host, use that host's IP address instead.\n\nThen connect to the instance as an administrator user, such as `root`, and grant\nthe user the permissions that RDI needs. Use the same host value that you used\nto create the user:\n\n[code example]\n\nNote that the `GRANT` statement might fail with\n`ERROR 1410 (42000): You are not allowed to create a user with GRANT`. This happens when the user\ndoesn't exist with that host, since Cloud SQL's administrator user can't create a user\nimplicitly with `GRANT`. Check the user's host with\n`gcloud sql users list --instance=<instance-name>`, and either grant to the host\nshown there or recreate the user with the `--host` value you want."},{"id":"6-configure-rdi","title":"6. Configure RDI","role":"content","text":"Store the CDC user's credentials as RDI secrets. Pass the source name with\n`--db` (the source in the following example is named `mysql`):\n\n[code example]\n\nAdd a MySQL source to `config.yaml`. If the instance enforces encrypted\nconnections, set `database.ssl.mode` to `required` (or a stricter mode) in the\nsource's `advanced.source` section:\n\n[code example]\n\nIf you don't set `database.ssl.mode` and the instance only accepts encrypted\nconnections, the pipeline deployment fails with an error like\n`Access denied for user '<username>'@'<ip-address>' (using password: YES)`. This\nerror looks like a credentials problem, but Cloud SQL is actually rejecting the\nunencrypted connection.\n\nThe `required` mode encrypts the connection but doesn't verify the server\ncertificate. To verify the certificate as well, set the source's `CACERT` secret\nto the instance's server CA certificate and choose a verifying mode, as described in\n[Connect over TLS or mTLS](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/my-sql-mariadb#7-connect-over-tls-or-mtls)."},{"id":"failover-on-regional-instances","title":"Failover on Regional instances","role":"content","text":"A Regional (high availability) Cloud SQL instance has a standby in a second\nzone. During a failover, Cloud SQL promotes the standby to primary and keeps the\nsame IP address. Expect the following behavior when this happens:\n\n- Clients, including your application, can't connect to the instance for up to\n  about a minute while the failover runs. Use connection retry logic in your\n  application to handle this.\n- RDI doesn't need a restart or any manual step. The RDI collector detects that\n  its binlog connection is no longer working, reconnects to the new primary, and\n  then captures the changes that were committed while it was disconnected.\n- Expect about one minute of extra change data capture (CDC) lag after the\n  instance becomes available again, while the collector detects the lost\n  connection and catches up."}],"examples":[{"id":"overview-ex0","language":"checklist {id=\"cloudsqlmysqllist\"}","code":"- [ ] [Enable binary logging](#1-enable-binary-logging)\n- [ ] [Set the binlog row image](#2-set-the-binlog-row-image)\n- [ ] [Check the binlog settings](#3-check-the-binlog-settings)\n- [ ] [Allow network access from RDI](#4-allow-network-access-from-rdi)\n- [ ] [Create a CDC user](#5-create-a-cdc-user)\n- [ ] [Configure RDI](#6-configure-rdi)","section_id":"overview"},{"id":"1-enable-binary-logging-ex0","language":"bash","code":"gcloud sql instances create <instance-name> \\\n  --database-version=MYSQL_8_0 \\\n  --region=<region> \\\n  --tier=<machine-tier> \\\n  --availability-type=REGIONAL \\\n  --enable-bin-log \\\n  --database-flags=binlog_row_image=full \\\n  --ssl-mode=ENCRYPTED_ONLY","section_id":"1-enable-binary-logging"},{"id":"1-enable-binary-logging-ex1","language":"bash","code":"gcloud sql instances patch <instance-name> --enable-bin-log","section_id":"1-enable-binary-logging"},{"id":"2-set-the-binlog-row-image-ex0","language":"bash","code":"gcloud sql instances patch <instance-name> --database-flags=binlog_row_image=full","section_id":"2-set-the-binlog-row-image"},{"id":"3-check-the-binlog-settings-ex0","language":"sql","code":"SHOW GLOBAL VARIABLES WHERE Variable_name IN\n  ('log_bin', 'binlog_format', 'binlog_row_image', 'gtid_mode',\n   'binlog_row_value_options');","section_id":"3-check-the-binlog-settings"},{"id":"4-allow-network-access-from-rdi-ex0","language":"bash","code":"gcloud sql instances patch <instance-name> --authorized-networks=<rdi-egress-ip>/32","section_id":"4-allow-network-access-from-rdi"},{"id":"5-create-a-cdc-user-ex0","language":"bash","code":"gcloud sql users create <username> \\\n  --instance=<instance-name> \\\n  --host=% \\\n  --password=<password>","section_id":"5-create-a-cdc-user"},{"id":"5-create-a-cdc-user-ex1","language":"sql","code":"GRANT SELECT, RELOAD, SHOW DATABASES, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO '<username>'@'%';\nFLUSH PRIVILEGES;","section_id":"5-create-a-cdc-user"},{"id":"6-configure-rdi-ex0","language":"bash","code":"redis-di set-secret USERNAME --db mysql <username>\nredis-di set-secret PASSWORD --db mysql <password>","section_id":"6-configure-rdi"},{"id":"6-configure-rdi-ex1","language":"yaml","code":"sources:\n  mysql:\n    type: cdc\n    connection:\n      type: mysql\n      host: <instance-ip-address>\n      port: 3306\n      user: ${MYSQL_DB_USERNAME}\n      password: ${MYSQL_DB_PASSWORD}\n    databases:\n      - <database-name>\n    advanced:\n      source:\n        database.ssl.mode: required","section_id":"6-configure-rdi"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/prepare-dbs","title":"Prepare source databases","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/","summary":"Enable CDC features in your source databases","aliases":["/integrate/redis-data-integration/ingest/data-pipelines/prepare-dbs/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-10-18T02:02:34+03:00","children":[{"id":"integrate/redis-data-integration/data-pipelines/prepare-dbs/oracle","summary":"Prepare Oracle and Oracle RAC databases to work with RDI","title":"Prepare Oracle and Oracle RAC for RDI","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/oracle/"},{"id":"integrate/redis-data-integration/data-pipelines/prepare-dbs/mongodb","summary":"Prepare MongoDB databases to work with RDI","title":"Prepare MongoDB for RDI","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/mongodb/"},{"id":"integrate/redis-data-integration/data-pipelines/prepare-dbs/my-sql-mariadb","summary":"Prepare MySQL and MariaDB databases to work with RDI","title":"Prepare MySQL/MariaDB for RDI","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/my-sql-mariadb/"},{"id":"integrate/redis-data-integration/data-pipelines/prepare-dbs/postgresql","summary":"Prepare PostgreSQL databases to work with RDI","title":"Prepare PostgreSQL for RDI","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/postgresql/"},{"id":"integrate/redis-data-integration/data-pipelines/prepare-dbs/spanner","summary":"Prepare Google Cloud Spanner databases to work with RDI","title":"Prepare Spanner for RDI","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/spanner/"},{"id":"integrate/redis-data-integration/data-pipelines/prepare-dbs/sql-server","summary":"Prepare SQL Server databases to work with RDI","title":"Prepare SQL Server for RDI","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/sql-server/"},{"id":"integrate/redis-data-integration/data-pipelines/prepare-dbs/aws-aurora-rds","summary":"Learn how to prepare AWS RDS and Aurora databases for RDI.","title":"Prepare AWS RDS and Aurora databases for RDI","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/aws-aurora-rds/"},{"id":"integrate/redis-data-integration/data-pipelines/prepare-dbs/neon","summary":"Prepare Neon databases to work with RDI","title":"Prepare Neon for RDI","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/neon/"},{"id":"integrate/redis-data-integration/data-pipelines/prepare-dbs/supabase","summary":"Prepare a hosted Supabase database to work with RDI","title":"Prepare Supabase for RDI","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/supabase/"},{"id":"integrate/redis-data-integration/data-pipelines/prepare-dbs/gcp-cloud-sql-mysql","summary":"Prepare a Google Cloud SQL for MySQL database to work with RDI","title":"Prepare Google Cloud SQL for MySQL for RDI","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/gcp-cloud-sql-mysql/"},{"id":"integrate/redis-data-integration/data-pipelines/prepare-dbs/snowflake","summary":"Prepare Snowflake databases to work with RDI","title":"Prepare Snowflake for RDI","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/snowflake/"}],"page_type":"content","content_hash":"2590c91da08491735d4957677bb2cf06475fd6c32b14d33adb833a7de51eac7b","sections":[{"id":"content","title":"Content","role":"content","text":"Each database uses a different mechanism to track changes to its data and\ngenerally, these mechanisms are not switched on by default.\nRDI's Debezium collector uses these mechanisms for change data capture (CDC),\nso you must prepare each source database before you can use it with RDI.\n\nPlan how much change history your source needs to retain during interruptions.\nSee [Understand CDC offsets and source retention](https://redis.io/docs/latest/integrate/redis-data-integration/cdc-offsets-and-retention)\nfor a conceptual guide and examples for each source family.\n\nA pipeline can capture from more than one source database, and you must prepare each of them\nseparately. Follow the page for each source's database type. See\n[Multiple sources in one pipeline](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/multiple-sources)\nto learn how to configure several sources in one pipeline.\n\nRDI supports the following source databases:\n\n| Database | Versions | AWS RDS  Versions | GCP SQL Versions |\n| :-- | :-- | :-- | :-- |\n| Oracle | 19c, 21c, 23ai (LogMiner only) | 19c, 21c | - |\n| MariaDB | 10.5, 11.4.x, 11.7.x | 10.4 to 10.11 | - |\n| MongoDB | 6.0, 7.0, 8.0 | - | - |\n| MySQL | 5.7, 8.0.x, 8.4.x, 9.0, 9.1 | 8.0.x | 8.0 |\n| PostgreSQL | 10, 11, 12, 13, 14, 15, 16, 17, 18 | 11, 12, 13, 14, 15, 16, 17, 18 | 15 |\n| Supabase (uses PostgreSQL) | 10, 11, 12, 13, 14, 15, 16, 17  | - | - |\n| SQL Server | 2017, 2019, 2022 | 2016, 2017, 2019, 2022 | 2019 |\n| Spanner | - | - | All versions |\n| AlloyDB for PostgreSQL | 14.2, 15.7 | - | 14.2, 15.7 |\n| AWS Aurora/PostgreSQL | 15 | 15 | - |\n| Neon | 14, 15, 16, 17 | - | - |\n| Snowflake (preview) | - | - | - |\n\n\nThe pages in this section give detailed instructions to get your source\ndatabase ready for Debezium to use:"}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/prepare-dbs/mongodb","title":"Prepare MongoDB for RDI","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/mongodb/","summary":"Prepare MongoDB databases to work with RDI","tags":["docs","integrate","rs","rdi"],"last_updated":"2026-10-18T02:44:31+03:00","page_type":"content","content_hash":"80b02b74a62099b7aea935c1a4e92d5f9c0075936dd712cfe1678efd6413f026","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This guide describes the steps required to prepare a MongoDB database as a source for Redis Data Integration (RDI) pipelines."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"- **MongoDB version:** 6.0 or later (replica set, sharded cluster, or MongoDB Atlas).\n- **User privileges:** You must have a MongoDB user with sufficient privileges to read the oplog and collections, and to use change streams.\n- **Network access:** The RDI Collector must be able to connect to all MongoDB nodes in your deployment.\n\n> [!NOTE]\n> The MongoDB connector is not capable of monitoring the changes of a standalone MongoDB server, since standalone servers do not have an oplog. The connector will work if the standalone server is converted to a replica set with one member."},{"id":"summary","title":"Summary","role":"content","text":"The following table summarizes the considerations to prepare a MongoDB database for RDI.\n\n| Requirement         | Description                                                                 |\n|---------------------|-----------------------------------------------------------------------------|\n| MongoDB Topology    | Replica Set, Sharded Cluster, or MongoDB Atlas                              |\n| User Roles          | readAnyDatabase, clusterMonitor                                             |\n| Oplog               | Sufficient size for snapshot and streaming                                  |\n| Pre/Post Images     | Enable when the pipeline needs old document values, including custom keys and set output types |\n| Connection String   | Must include all hosts, replicaSet (if applicable), authSource, credentials |\n| MongoDB Atlas       | **[SSL required](https://debezium.io/documentation/reference/stable/connectors/mongodb.html#mongodb-property-mongodb-ssl-enabled)**, provide root CA as the source's `CACERT` secret in RDI       |\n| MongoDB mTLS        | X.509 authentication requires source TLS secrets and MongoDB SSL properties |\n| Network             | RDI Collector must reach all MongoDB nodes on required ports                |\n\nThe following checklist shows the steps to prepare a MongoDB database for RDI,\nwith links to the sections that explain the steps in full detail.\nYou may find it helpful to track your progress with the checklist as you\ncomplete each step.\n\n[code example]"},{"id":"1-configure-oplog-size","title":"1. Configure oplog size","role":"content","text":"The Debezium MongoDB connector relies on the [oplog](https://www.mongodb.com/docs/manual/core/replica-set-oplog/) to capture changes from a replica set. The oplog is a fixed-size, capped collection. When it reaches its maximum size, it overwrites the oldest entries. If the connector is stopped and restarted, it attempts to resume from its last recorded position in the oplog. If that position has been overwritten, the connector may fail to start and report an invalid resume token error.\n\nTo prevent this, ensure the oplog retains enough history for Debezium to resume streaming after interruptions. You can do this by:\n\n- **Increasing the oplog size:** Set the oplog size based on your workload, ensuring it can store more than the peak number of oplog entries generated per hour.\n- **Setting a minimum oplog retention period (MongoDB 4.4+):** Configure MongoDB to retain oplog entries for a minimum number of hours, guaranteeing availability even if the oplog reaches its maximum size. This is generally preferred, but for high-throughput clusters nearing capacity, you may need to increase the oplog size instead.\n\nFor detailed guidance, see the Debezium [oplog configuration documentation](https://debezium.io/documentation/reference/stable/connectors/mongodb.html#mongodb-optimal-oplog-config)."},{"id":"2-create-a-mongodb-user-for-rdi","title":"2. Create a MongoDB user for RDI","role":"content","text":"Create a user with the following roles on the source database:\n- `readAnyDatabase` (optional) OR grant `read` for the specific database(s) you will use with RDI\n- `clusterMonitor`\n\nExample:\n[code example]"},{"id":"3-connection-string-format","title":"3. Connection string format","role":"content","text":"The RDI Collector requires a MongoDB connection string that includes all relevant hosts and authentication details.\n\nThe credential references come from the source name, which is `mongodb` in the examples on\nthis page. See\n[Multiple sources in one pipeline](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/multiple-sources)\nfor the source naming rules.\n\nExample (Replica Set):\n[code example]\nExample (Sharded Cluster):\n[code example]\n- For Atlas, adjust the connection string accordingly (see example below).\n- Set `replicaSet` and `authSource` as appropriate for your deployment.\n\n<a id=\"4-enable-change-streams-and-prepost-images-only-if-using-a-custom-key\"></a>"},{"id":"4-enable-pre-images-when-required","title":"4. Enable pre-images when required","role":"content","text":"RDI uses MongoDB change streams for ongoing replication, with both default and custom Redis keys. Change streams are available on replica sets, sharded clusters, and MongoDB Atlas.\n\nA **pre-image** contains a document's values before an update or deletion. For example, if a custom Redis key uses an email address, changing that address requires RDI to remove the old Redis key and write the new one. The pre-image supplies the old address. A **post-image** contains the document after an insert or update; a deleted document has no post-image.\n\nJobs that use custom output keys or set output types require pre-images. Enable document pre- and post-images on the relevant collections with this command. It enables the extra document images, not change streams themselves:\n\n[code example]\n\nAlso set `capture.mode` in the source's advanced settings to a mode ending in `_with_pre_image`, such as `change_streams_update_full_with_pre_image`. Enabling images on the collection alone does not make the collector request them. See the [source configuration reference](https://redis.io/docs/latest/integrate/redis-data-integration/reference/config-yaml-reference#sourcesadvancedsource-advanced-source-settings).\n\nKeep required pre-images available for the lag and interruptions you need to tolerate. A change event can remain available after its pre-image is gone. See [MongoDB history and old values](https://redis.io/docs/latest/integrate/redis-data-integration/cdc-offsets-and-retention#mongodb-account-for-write-volume-and-old-values) and [MongoDB pre- and post-images](https://www.mongodb.com/docs/manual/changestreams/#change-streams-with-document-pre--and-post-images)."},{"id":"5-mongodb-atlas-specific-requirements","title":"5. MongoDB Atlas specific requirements","role":"content","text":"MongoDB Atlas only supports secure connections via SSL.\nThe root CA certificate for MongoDB Atlas must be added as the source's `CACERT` secret in RDI.\n\n- Download the MongoDB Atlas root CA certificate.\n- In RDI, add this certificate with\n  `redis-di set-secret CACERT --db mongodb /path/to/atlas-ca.crt`.\n- Ensure that the `mongodb.ssl.enabled: true` setting is present in your RDI configuration for the corresponding source DB collector.\n\nExample connection string for Atlas:\n[code example]"},{"id":"6-self-hosted-mongodb-mtls-and-x509-authentication","title":"6. Self-hosted MongoDB mTLS and X.509 authentication","role":"content","text":"For self-hosted MongoDB deployments that require TLS, set the source's `CACERT` secret to\nthe source CA certificate. For X.509 client certificate authentication, also set the\nsource's `CERT` and `KEY` secrets. See\n[Set secrets](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/deploy#set-secrets)\nfor the full list of source database TLS and mTLS secrets.\n\nWhen you use MongoDB X.509 authentication, enable TLS in the source `advanced.source` section:\n\n[code example]\n\nRDI builds the keystore that presents the client certificate to MongoDB from the source's\n`CERT` and `KEY` secrets, and configures the collector to use it.\n\n> [!NOTE]\n> Do not set `mongodb.ssl.keystore` or `mongodb.ssl.keystore.password` yourself. RDI\n> manages the keystore, so these properties are not only unnecessary, they are rejected when you\n> deploy the pipeline.\n\nFor X.509 authentication, the MongoDB connection string must also include the\nrequired authentication options, such as `authMechanism=MONGODB-X509` and\n`authSource=%24external`."},{"id":"7-network-and-security","title":"7. Network and security","role":"content","text":"- Ensure the RDI Collector can connect to all MongoDB nodes on the required ports (default: 27017, or as provided by Atlas).\n- If using TLS/SSL, provide the necessary certificates and connection options in the connection string."},{"id":"8-configuration-is-complete","title":"8. Configuration is complete","role":"content","text":"Once you have followed the steps above, your MongoDB database is ready for Debezium to use."},{"id":"see-also","title":"See also","role":"related","text":"- [MongoDB Replica Set Documentation](https://www.mongodb.com/docs/manual/replication/)\n- [MongoDB Sharded Cluster Documentation](https://www.mongodb.com/docs/manual/sharding/)\n- [MongoDB Change Streams](https://www.mongodb.com/docs/manual/changeStreams/)\n- [MongoDB User Management](https://www.mongodb.com/docs/manual/core/security-users/)\n- [Debezium MongoDB Connector Documentation](https://debezium.io/documentation/reference/stable/connectors/mongodb.html)\n- [MongoDB Atlas SSL Setup](https://debezium.io/documentation/reference/stable/connectors/mongodb.html#mongodb-in-the-cloud)"}],"examples":[{"id":"summary-ex0","language":"checklist {id=\"mongodblist\"}","code":"- [ ] [Configure oplog size](#1-configure-oplog-size)\n- [ ] [Create a MongoDB user for RDI](#2-create-a-mongodb-user-for-rdi)\n- [ ] [Connection string format](#3-connection-string-format)\n- [ ] [Enable pre-images when required](#4-enable-pre-images-when-required)\n- [ ] [MongoDB Atlas specific requirements](#5-mongodb-atlas-specific-requirements)\n- [ ] [Self-hosted MongoDB mTLS and X.509 authentication](#6-self-hosted-mongodb-mtls-and-x509-authentication)\n- [ ] [Network and security](#7-network-and-security)","section_id":"summary"},{"id":"2-create-a-mongodb-user-for-rdi-ex0","language":"javascript","code":"use admin;\ndb.createUser({\n  user: \"rdi_user\",\n  pwd: \"rdi_password\",\n  roles: [\n     // You can have multiple read roles, one per database.\n    { role: \"read\", db: \"your_database\" },\n    // Use the role below if you don't want to grant the `read` role for each database.\n    // { role: \"readAnyDatabase\", db: \"admin\" },\n    { role: \"clusterMonitor\", db: \"admin\" }\n  ]\n});","section_id":"2-create-a-mongodb-user-for-rdi"},{"id":"3-connection-string-format-ex0","language":"plaintext","code":"mongodb://${MONGODB_DB_USERNAME}:${MONGODB_DB_PASSWORD}@host1:27017,host2:27017,host3:27017/?replicaSet=rs0&authSource=admin","section_id":"3-connection-string-format"},{"id":"3-connection-string-format-ex1","language":"plaintext","code":"mongodb://${MONGODB_DB_USERNAME}:${MONGODB_DB_PASSWORD}@host:30000","section_id":"3-connection-string-format"},{"id":"4-enable-pre-images-when-required-ex0","language":"javascript","code":"db.runCommand({\n  collMod: \"your_collection\",\n  changeStreamPreAndPostImages: { enabled: true }\n});","section_id":"4-enable-pre-images-when-required"},{"id":"5-mongodb-atlas-specific-requirements-ex0","language":"plaintext","code":"mongodb+srv://${MONGODB_DB_USERNAME}:${MONGODB_DB_PASSWORD}@cluster0.mongodb.net/?authSource=admin","section_id":"5-mongodb-atlas-specific-requirements"},{"id":"6-self-hosted-mongodb-mtls-and-x509-authentication-ex0","language":"yaml","code":"advanced:\n  source:\n    mongodb.ssl.enabled: true","section_id":"6-self-hosted-mongodb-mtls-and-x509-authentication"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/prepare-dbs/my-sql-mariadb","title":"Prepare MySQL/MariaDB for RDI","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/my-sql-mariadb/","summary":"Prepare MySQL and MariaDB databases to work with RDI","aliases":["/integrate/redis-data-integration/ingest/data-pipelines/prepare-dbs/my-sql-mariadb/","/integrate/redis-data-integration/ingest/data-transformation/prepare-dbs/my-sql-mariadb/","/integrate/redis-data-integration/ingest/installation/prepare-dbs/my-sql-mariadb/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-10-05T11:23:44+03:00","page_type":"content","content_hash":"4661a7d05771de73bd34bfeaf952956c0a21c355a7039b4fb7154d93f5e1f037","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The following checklist summarizes the steps to prepare a MySQL or MariaDB\ndatabase for RDI, with links to the sections that explain the steps in\nfull detail. You may find it helpful to track your progress with the\nchecklist as you complete each step.\n\n[code example]"},{"id":"1-create-a-cdc-user","title":"1. Create a CDC user","role":"content","text":"The Debezium connector needs a user account to connect to MySQL/MariaDB. This\nuser must have appropriate permissions on all databases where you want Debezium\nto capture changes.\n\nRun the [MySQL CLI client](https://dev.mysql.com/doc/refman/8.3/en/mysql.html)\nand then run the following commands:\n\n[code example]\n\n1.  <a id=\"create-the-cdc-user\"></a>\n    Create the CDC user:\n\n    [code example]\n\n1.  <a id=\"grant-the-user-the-necessary-permissions\"></a>\n    Grant the required permissions to the user:\n\n    [code example]\n\n1.  <a id=\"finalize-the-users-permissions\"></a>\n    Finalize the user's permissions:\n\n    [code example]"},{"id":"2-enable-the-binlog","title":"2. Enable the binlog","role":"content","text":"You must enable binary logging for MySQL replication. The binary logs record transaction\nupdates so that replication tools can propagate changes. You will need administrator\nprivileges to do this.\n\nFirst, you should check whether the `log-bin` option is already set to `ON`, using\nthe following query:\n\n[code example]\n\nIf `log-bin` is `OFF` then add the following properties to your\nserver configuration file:\n\n[code example]\n\nFor MariaDB, also add the following server configuration settings:\n\n[code example]\n\nRDI doesn't support binary log compression, so you must set\n`log_bin_compress` to `0`. For MariaDB 11.4 and later, you must also set\n`binlog_legacy_event_pos` to `1` to prevent RDI collector crash loops after\nthe MariaDB server restarts.\n\nYou can run the query above again to check that `log-bin` is now `ON`.\n\n> [!NOTE]\n> If you are using [Amazon RDS for MySQL](https://aws.amazon.com/rds/mysql/) then\n> you must enable automated backups for your database before it can use binary logging.\n> If you don't enable automated backups first then the settings above will have no\n> effect.\n\n&nbsp;\n\n> [!NOTE]\n> If you are using [Google Cloud SQL for MySQL](https://cloud.google.com/sql/docs/mysql),\n> you enable binary logging and set `binlog_row_image` through Cloud SQL instead of the\n> server configuration file. See\n> [Prepare Google Cloud SQL for MySQL for RDI](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/gcp-cloud-sql-mysql)\n> for the full setup."},{"id":"3-enable-gtids","title":"3. Enable GTIDs","role":"content","text":"*Global transaction identifiers (GTIDs)* uniquely identify the transactions that occur\non a server within a cluster. You don't strictly need to use them with a Debezium MySQL\nconnector, but you might find it helpful to enable them.\nUse GTIDs to simplify replication and to confirm that the primary and replica servers are\nconsistent.\n\nGTIDs are available in MySQL 5.6.5 and later. See the\n[MySQL documentation about GTIDs](https://dev.mysql.com/doc/refman/8.0/en/replication-options-gtids.html#option_mysqld_gtid-mode) for more information.\n\nFollow the steps below to enable GTIDs. You will need access to the MySQL configuration file\nto do this.\n\n[code example]\n\n1.  <a id=\"enable-gtid_mode\"></a>\n    Enable `gtid_mode`:\n\n    [code example]\n\n1.  <a id=\"enable-enforce_gtid_consistency\"></a>\n    Enable `enforce_gtid_consistency`:\n\n    [code example]\n\n1.  <a id=\"confirm-the-changes\"></a>\n    Confirm the changes:\n\n    [code example]"},{"id":"4-configure-session-timeouts","title":"4. Configure session timeouts","role":"content","text":"RDI captures an initial *snapshot* of the source database when it begins\nthe CDC process (see the\n[architecture overview](https://redis.io/docs/latest/integrate/redis-data-integration/architecture#overview)\nfor more information). If your database is large then the connection could time out\nwhile RDI is reading the data for the snapshot. You can prevent this using the\n`interactive_timeout` and `wait_timeout` settings in your MySQL configuration file:\n\n[code example]"},{"id":"5-enable-query-log-events","title":"5. Enable query log events","role":"content","text":"If you want to see the original SQL statement for each binlog event then you should\nenable `binlog_rows_query_log_events` (MySQL configuration) or\n`binlog_annotate_row_events` (MariaDB configuration):\n\n[code example]\n\nThis option is available in MySQL 5.6 and later."},{"id":"6-check-binlog_row_value_options","title":"6. Check `binlog_row_value_options`","role":"content","text":"You should check the value of the `binlog_row_value_options` variable\nto ensure it is not set to `PARTIAL_JSON`. If it *is* set to\n`PARTIAL_JSON` then Debezium might not be able to see `UPDATE` events.\n\nCheck the current value of the variable with the following command:\n\n[code example]\n\nIf the value is `PARTIAL_JSON` then you should unset the variable:\n\n[code example]"},{"id":"7-connect-over-tls-or-mtls","title":"7. Connect over TLS or mTLS","role":"content","text":"To connect to the source database over TLS, set the source's `CACERT` secret to the source CA\ncertificate. For mutual TLS, also set the source's `CERT` and `KEY` secrets. See\n[Set secrets](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/deploy#set-secrets)\nfor the full list of source database TLS and mTLS secrets.\n\nSelect the TLS mode in the source `advanced.source` section, for example:\n\n[code example]\n\nRDI builds the keystore that presents the client certificate to the source database from the\nsource's `CERT` and `KEY` secrets, and configures the collector to use it.\n\n> [!NOTE]\n> Do not set `database.ssl.keystore` or `database.ssl.keystore.password` yourself. RDI\n> manages the keystore, so these properties are not only unnecessary, they are rejected when you\n> deploy the pipeline."},{"id":"8-configuration-is-complete","title":"8. Configuration is complete","role":"content","text":"After following the steps above, your MySQL/MariaDB database is ready\nfor Debezium to use."}],"examples":[{"id":"overview-ex0","language":"checklist {id=\"mysqlmariadblist\"}","code":"- [ ] [Create a CDC user](#1-create-a-cdc-user)\n- [ ] [Enable the binlog](#2-enable-the-binlog)\n- [ ] [Enable GTIDs](#3-enable-gtids)\n- [ ] [Configure session timeouts](#4-configure-session-timeouts)\n- [ ] [Enable query log events](#5-enable-query-log-events)\n- [ ] [Check binlog_row_value_options](#6-check-binlog_row_value_options)\n- [ ] [Connect over TLS or mTLS](#7-connect-over-tls-or-mtls)","section_id":"overview"},{"id":"1-create-a-cdc-user-ex0","language":"checklist {id=\"mysqlmariadb-create-cdc-user\" nointeractive=\"true\" }","code":"- [ ] [Create the CDC user](#create-the-cdc-user)\n- [ ] [Grant the user the necessary permissions](#grant-the-user-the-necessary-permissions)\n- [ ] [Finalize the user's permissions](#finalize-the-users-permissions)","section_id":"1-create-a-cdc-user"},{"id":"1-create-a-cdc-user-ex1","language":"sql","code":"mysql> CREATE USER 'user'@'localhost' IDENTIFIED BY 'password';","section_id":"1-create-a-cdc-user"},{"id":"1-create-a-cdc-user-ex2","language":"sql","code":"# MySQL <v8.0\n    mysql> GRANT SELECT, RELOAD, SHOW DATABASES, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'user' IDENTIFIED BY 'password';\n\n    # MySQL v8.0 and above\n    mysql> GRANT SELECT, RELOAD, SHOW DATABASES, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'user'@'localhost';","section_id":"1-create-a-cdc-user"},{"id":"1-create-a-cdc-user-ex3","language":"sql","code":"mysql> FLUSH PRIVILEGES;","section_id":"1-create-a-cdc-user"},{"id":"2-enable-the-binlog-ex0","language":"sql","code":"// for MySql 5.x\nmysql> SELECT variable_value as \"BINARY LOGGING STATUS (log-bin) ::\"\nFROM information_schema.global_variables WHERE variable_name='log_bin';\n// for MySql 8.x\nmysql> SELECT variable_value as \"BINARY LOGGING STATUS (log-bin) ::\"\nFROM performance_schema.global_variables WHERE variable_name='log_bin';","section_id":"2-enable-the-binlog"},{"id":"2-enable-the-binlog-ex1","language":"plaintext","code":"server-id         = 223344 # Querying variable is called server_id, e.g. SELECT variable_value FROM information_schema.global_variables WHERE variable_name='server_id';\nlog_bin                     = mysql-bin\nbinlog_format               = ROW\nbinlog_row_image            = FULL\nbinlog_expire_logs_seconds  = 864000","section_id":"2-enable-the-binlog"},{"id":"2-enable-the-binlog-ex2","language":"plaintext","code":"log_bin_compress            = 0\n\n# Required for MariaDB 11.4 and later.\nbinlog_legacy_event_pos     = 1","section_id":"2-enable-the-binlog"},{"id":"3-enable-gtids-ex0","language":"checklist {id=\"mysqlmariadb-enable-gtids\" nointeractive=\"true\" }","code":"- [ ] [Enable gtid_mode](#enable-gtid_mode)\n- [ ] [Enable enforce_gtid_consistency](#enable-enforce_gtid_consistency)\n- [ ] [Confirm the changes](#confirm-the-changes)","section_id":"3-enable-gtids"},{"id":"3-enable-gtids-ex1","language":"sql","code":"mysql> gtid_mode=ON","section_id":"3-enable-gtids"},{"id":"3-enable-gtids-ex2","language":"sql","code":"mysql> enforce_gtid_consistency=ON","section_id":"3-enable-gtids"},{"id":"3-enable-gtids-ex3","language":"sql","code":"mysql> show global variables like '%GTID%';\n    \n    >>> Result:\n\n    +--------------------------+-------+\n    | Variable_name            | Value |\n    +--------------------------+-------+\n    | enforce_gtid_consistency | ON    |\n    | gtid_mode                | ON    |\n    +--------------------------+-------+","section_id":"3-enable-gtids"},{"id":"4-configure-session-timeouts-ex0","language":"plaintext","code":"mysql> interactive_timeout=<duration-in-seconds>\nmysql> wait_timeout=<duration-in-seconds>","section_id":"4-configure-session-timeouts"},{"id":"5-enable-query-log-events-ex0","language":"plaintext","code":"mysql> binlog_rows_query_log_events=ON\n\nmariadb> binlog_annotate_row_events=ON","section_id":"5-enable-query-log-events"},{"id":"6-check-binlog_row_value_options-ex0","language":"sql","code":"mysql> show global variables where variable_name = 'binlog_row_value_options';\n\n>>> Result:\n\n+--------------------------+-------+\n| Variable_name            | Value |\n+--------------------------+-------+\n| binlog_row_value_options |       |\n+--------------------------+-------+","section_id":"6-check-binlog_row_value_options"},{"id":"6-check-binlog_row_value_options-ex1","language":"sql","code":"mysql> set @@global.binlog_row_value_options=\"\" ;","section_id":"6-check-binlog_row_value_options"},{"id":"7-connect-over-tls-or-mtls-ex0","language":"yaml","code":"advanced:\n  source:\n    database.ssl.mode: verify_ca","section_id":"7-connect-over-tls-or-mtls"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/prepare-dbs/neon","title":"Prepare Neon for RDI","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/neon/","summary":"Prepare Neon databases to work with RDI","tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-18T16:39:32+01:00","page_type":"content","content_hash":"8cc24119551ce8849572f792fa38479e566d5ad305da09da0f585f92846ad804","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Neon](https://neon.com/) is a serverless PostgreSQL platform. To use Neon as a source database for Redis Data Integration (RDI), you must enable logical replication for the compute endpoint that serves your branch, create a dedicated replication role, grant it the minimum required privileges, and allow inbound network traffic from the RDI connector host.\n\nThe following checklist summarizes the steps to prepare a Neon database for\nRDI, with links to the sections that explain the steps in full detail. You may\nfind it helpful to track your progress with the checklist as you complete each\nstep.\n\n[code example]"},{"id":"1-enable-logical-replication-in-neon","title":"1. Enable logical replication in Neon","role":"content","text":"To capture changes from Neon, the compute endpoint for your branch must have\nlogical replication enabled. This sets the PostgreSQL `wal_level` parameter to\n`logical` for that compute.\n\n1. Sign in to the Neon Console and select the project that hosts the database\n   you want to use with RDI.\n1. On the **Branches** page, choose the branch you want to capture from.\n1. Either create a new compute endpoint for that branch or edit an existing\n   one.\n1. In the compute configuration, enable **Logical replication**.\n1. Save the changes and wait for the compute to restart if required.\n\nAfter the compute is running, connect with a SQL client and confirm that\nlogical replication is enabled by running:\n\n[code example]\n\nThe query should return `logical`.\n\n> [!NOTE]\n> Enabling logical replication increases the volume of write-ahead log (WAL)\n> data that Neon retains. Monitor the project for any impact on storage usage and\n> cost."},{"id":"2-create-a-replication-role-for-rdi","title":"2. Create a replication role for RDI","role":"content","text":"It is strongly recommended to create a dedicated database role for the\nconnection that RDI uses, rather than reusing an existing superuser or\napplication role.\n\nConnect to the Neon database as a user with sufficient privileges and create a\nrole similar to the following (replace the identifiers with values that match\nyour environment):\n\n[code example]\n\nThis role:\n\n- can sign in to the database (`LOGIN`)\n- can consume logical replication streams (`REPLICATION`)\n- does not have superuser privileges"},{"id":"3-grant-database-and-table-privileges","title":"3. Grant database and table privileges","role":"content","text":"The replication role must be able to connect to the database, access the\nschemas you want to capture, and read from the tables in those schemas.\n\nRun commands like the following, replacing `mydb` and `rdi_replication` with\nyour database name and replication role:\n\n[code example]\n\nIf you use multiple schemas or need access to sequences, repeat the `GRANT`\nand `ALTER DEFAULT PRIVILEGES` statements for each schema and include\n`SEQUENCES` where appropriate."},{"id":"4-allow-inbound-traffic-to-neon","title":"4. Allow inbound traffic to Neon","role":"content","text":"RDI connects to Neon over the public Internet using the connection string for\nyour compute endpoint. You must ensure that Neon allows inbound traffic from\nthe IP addresses that the RDI connector uses.\n\n1. In the Neon Console, open the project that contains your database.\n1. Go to the **Settings** or **Networking** section for the project and locate\n   the IP allow list or **Allowed IPs** configuration.\n1. Add the public IP address or address range that the RDI connector uses to\n   reach Neon. For production systems, restrict this to the smallest set of\n   IPs possible.\n1. Save the configuration.\n\nFor development or testing, you can temporarily allow access from your own\nclient machine or a broader IP range, but for production environments you\nshould always restrict access to known connector IP addresses.\n\nYou will also need the Neon connection string for use when you configure the\nsource in RDI. In the Neon Console, copy the PostgreSQL connection URI for the\ncompute endpoint you prepared above. It has the form:\n\n[code example]\n\nUse this URI, together with the replication role you created, when you set up\nthe Neon source connection in RDI."}],"examples":[{"id":"overview-ex0","language":"checklist {id=\"neonlist\"}","code":"- [ ] [Enable logical replication in Neon](#1-enable-logical-replication-in-neon)\n- [ ] [Create a replication role for RDI](#2-create-a-replication-role-for-rdi)\n- [ ] [Grant database and table privileges](#3-grant-database-and-table-privileges)\n- [ ] [Allow inbound traffic to Neon](#4-allow-inbound-traffic-to-neon)","section_id":"overview"},{"id":"1-enable-logical-replication-in-neon-ex0","language":"sql","code":"SHOW wal_level;","section_id":"1-enable-logical-replication-in-neon"},{"id":"2-create-a-replication-role-for-rdi-ex0","language":"sql","code":"CREATE ROLE rdi_replication\n  WITH LOGIN REPLICATION PASSWORD 'Strong_Password';","section_id":"2-create-a-replication-role-for-rdi"},{"id":"3-grant-database-and-table-privileges-ex0","language":"sql","code":"GRANT CONNECT ON DATABASE mydb TO rdi_replication;\n\nGRANT USAGE ON SCHEMA public TO rdi_replication;\nGRANT SELECT ON ALL TABLES IN SCHEMA public TO rdi_replication;\n\nALTER DEFAULT PRIVILEGES IN SCHEMA public\n  GRANT SELECT ON TABLES TO rdi_replication;","section_id":"3-grant-database-and-table-privileges"},{"id":"4-allow-inbound-traffic-to-neon-ex0","language":"text","code":"postgresql://<user>:<password>@<host>:5432/<database>?sslmode=require","section_id":"4-allow-inbound-traffic-to-neon"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/prepare-dbs/oracle","title":"Prepare Oracle and Oracle RAC for RDI","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/oracle/","summary":"Prepare Oracle and Oracle RAC databases to work with RDI","aliases":["/integrate/redis-data-integration/ingest/data-pipelines/prepare-dbs/oracle/","/integrate/redis-data-integration/ingest/data-transformation/prepare-dbs/oracle/","/integrate/redis-data-integration/ingest/installation/prepare-dbs/oracle/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-10-02T14:54:03+01:00","page_type":"content","content_hash":"3e3d8713589c5aacf009b9d10abe125c135ad610ee63527802b1d2cf39714d0e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Oracle provides two main systems that Debezium can use to capture data changes:\n\n- [LogMiner](#logminer)\n- [XStream](#xstream)\n\nThe sections below explain how to configure each system for use with Debezium and RDI.\nThe checklists summarize the steps you should follow to configure each system.\nYou may find it helpful to use them to track your progress as you work through the steps.\n\n**LogMiner**\n\n[code example]\n\n**XStream**\n\n[code example]\n\n**Optional: XMLTYPE Support**\n\n[code example]"},{"id":"logminer","title":"LogMiner","role":"content","text":"Follow the steps below to configure\n[LogMiner](https://docs.oracle.com/en/database/oracle/oracle-database/19/sutil/oracle-logminer-utility.html)\nand prepare your database for use with RDI."},{"id":"1-configure-oracle-logminer","title":"1. Configure Oracle LogMiner","role":"content","text":"The following example shows the configuration for Oracle LogMiner.\n\n> [!NOTE]\n> [Amazon RDS for Oracle](https://aws.amazon.com/rds/oracle/)\n> doesn't let you execute the commands\n> in the example below or let you log in as `sysdba`. See the\n> separate example below to [configure Amazon RDS for Oracle](#config-aws).\n\n[code example]\n\n#### Configure Amazon RDS for Oracle {#config-aws}\n\nAWS provides its own set of commands to configure LogMiner.\n\n> [!NOTE]\n> Before executing these commands,\n> you must enable backups on your Oracle AWS RDS instance.\n\nCheck that Oracle has backups enabled with the following command:\n\n[code example]\n\nThe `LOG_MODE` should be set to `ARCHIVELOG`. If it isn't then you\nshould reboot your Oracle AWS RDS instance.\n\nOnce `LOG_MODE` is correctly set to ARCHIVELOG, execute the following\ncommands to complete the LogMiner configuration. The first command enables\narchive logging and the second adds [supplemental logging](#supp-logging).\n\n[code example]"},{"id":"supp-logging","title":"2. Enable supplemental logging","role":"content","text":"You must enable supplemental logging for the tables you want to capture or\nfor the entire database. This lets Debezium capture the state of\ndatabase rows before and after changes occur. \n\nThe following example shows how to configure supplemental logging for all columns\nin a single table called `inventory.customers`:\n\n[code example]\n\n> [!NOTE]\n> If you enable supplemental logging for *all* table columns, you will\n> probably see the size of the Oracle redo logs increase dramatically. Avoid this\n> by using supplemental logging only when you need it.  \n\nYou must also enable minimal supplemental logging at the database level with\nthe following command:\n\n[code example]"},{"id":"3-check-the-redo-log-sizing","title":"3. Check the redo log sizing","role":"content","text":"Before you use the Debezium connector, you should check with your\ndatabase administrator that there are enough\nredo logs with enough capacity to store the data dictionary for your\ndatabase. In general, the size of the data dictionary increases with the number\nof tables and columns in the database. If you don't have enough capacity in\nthe logs then you might see performance problems with both the database and\nthe Debezium connector."},{"id":"4-set-the-archive-log-destination","title":"4. Set the Archive log destination","role":"content","text":"You can configure up to 31 different destinations for archive logs\n(you must have administrator privileges to do this). You can set parameters for\neach destination to specify its purpose, such as log shipping for physical\nstandbys, or external storage to allow for extended log retention. Oracle reports\ndetails about archive log destinations in the `V$ARCHIVE_DEST_STATUS` view.\n\nThe Debezium Oracle connector only uses destinations that have a status of\n`VALID` and a type of `LOCAL`. If you only have one destination with these\nsettings then Debezium will use it automatically.\nIf you have more than one destination with these settings,\nthen you should consult your database administrator about which one to\nchoose for Debezium.\n\nUse the `log.mining.archive.destination.name` property in the connector configuration\nto select the archive log destination for Debezium.\n\nFor example, suppose you have two archive destinations, `LOG_ARCHIVE_DEST_2` and\n`LOG_ARCHIVE_DEST_3`, and they both have status set to `VALID` and type set to\n`LOCAL`. Debezium could use either of these destinations, so you must select one\nof them explicitly in the configuration. To select `LOG_ARCHIVE_DEST_3`, you would\nuse the following setting:\n\n[code example]"},{"id":"create-dbz-user","title":"5. Create a user for the connector","role":"content","text":"The Debezium Oracle connector must run as an Oracle LogMiner user with specific permissions.\n\nTypically, when you create the Oracle account for the connector, \nyou grant the account a level of access that permits the connector to detect changes from all tables in the database. \nHowever, in some environments, security policies might prohibit you from granting such a broad level of access.\n\nThe following example shows some SQL that creates an Oracle user account for the connector in a multi-tenant database model. \nThe grant settings in the example permit the Debezium user to access all user tables in the database.\n\nTo comply with security policies, you can modify the `SELECT ANY TABLE` and `FLASHBACK ANY TABLE` grants \nso that the connector can access only those tables that you intend to capture.\n\nDo not modify other grants, such as the `SELECT ANY TRANSACTION` grant, \nor the set of `SELECT ON V_$` grants, which provide access to dynamic performance views (`V_$`). \nThese grants are required for the connector to function.\n\n> [!NOTE]\n> To prevent data loss, if you restrict the scope of the SELECT and FLASHBACK grants, \n> be sure that the modified scope is compatible with the settings in the connector’s include configuration. \n> The privileges that you set for the account must permit reading from all of the tables that you want the connector to capture.\n\n> [!NOTE]\n> This example uses `ORCLCDB` as the container database (CDB) name and `ORCLPDB1` as the pluggable database (PDB) name. Replace these with the CDB and PDB names from your own environment.\n\n[code example]\n\n| Role name                | Description                                                                                                                                                                                                                                                                                                                                                                                            |\n|--------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| CREATE SESSION           | Enables the connector to connect to Oracle.                                                                                                                                                                                                                                                                                                                                                            |\n| SET CONTAINER            | Enables the connector to switch between pluggable databases. This is only required when the Oracle installation has container database support (CDB) enabled.                                                                                                                                                                                                                                          |\n| SELECT ON V_$DATABASE    | Enables the connector to read the V_$DATABASE table.                                                                                                                                                                                                                                                                                                                                                    |\n| FLASHBACK ANY TABLE      | Enables the connector to perform Flashback queries, which is how the connector performs the initial snapshot of data. Optionally, rather than granting FLASHBACK permission on all tables, you can grant the FLASHBACK privilege for specific tables only.                                                                                                                                             |\n| SELECT ANY TABLE         | Enables the connector to read any table. Optionally, rather than granting SELECT permission on all tables, you can grant the SELECT privilege for specific tables only.                                                                                                                                                                                                                                |\n| SELECT_CATALOG_ROLE      | Enables the connector to read the data dictionary, which is needed by Oracle LogMiner sessions.                                                                                                                                                                                                                                                                                                        |\n| EXECUTE_CATALOG_ROLE     | Enables the connector to write the data dictionary into the Oracle redo logs, which is needed to track schema changes.                                                                                                                                                                                                                                                                                 |\n| SELECT ANY TRANSACTION   | Enables the snapshot process to perform a Flashback snapshot query against any transaction so that the connector can read past changes from LogMiner. When FLASHBACK ANY TABLE is granted, this should also be granted. This grant is optional for Oracle 19c and later. In those later releases, the connector obtains the required privileges through the EXECUTE_CATALOG_ROLE and LOGMINING grants. |\n| LOGMINING                | This role was added in newer versions of Oracle as a way to grant full access to Oracle LogMiner and its packages. On older versions of Oracle that don’t have this role, you can ignore this grant.                                                                                                                                                                                                   |\n| CREATE TABLE             | Enables the connector to create its flush table in its default tablespace. The flush table allows the connector to explicitly control flushing of the LGWR internal buffers to disk.                                                                                                                                                                                                                   |\n| LOCK ANY TABLE           | Enables the connector to lock tables during schema snapshot. If snapshot locks are explicitly disabled via configuration, this grant can be safely ignored.                                                                                                                                                                                                                                            |\n| CREATE SEQUENCE          | Enables the connector to create a sequence in its default tablespace.                                                                                                                                                                                                                                                                                                                                  |\n| EXECUTE ON DBMS_LOGMNR   | Enables the connector to run methods in the DBMS_LOGMNR package. This is required to interact with Oracle LogMiner. On newer versions of Oracle this is granted via the LOGMINING role but on older versions, this must be explicitly granted.                                                                                                                                                         |\n| EXECUTE ON DBMS_LOGMNR_D | Enables the connector to run methods in the DBMS_LOGMNR_D package. This is required to interact with Oracle LogMiner. On newer versions of Oracle this is granted via the LOGMINING role but on older versions, this must be explicitly granted.                                                                                                                                                       |\n| SELECT ON V_$…​.          | Enables the connector to read these tables. The connector must be able to read information about the Oracle redo and archive logs, and the current transaction state, to prepare the Oracle LogMiner session. Without these grants, the connector cannot operate.                                                                                                                                      |\n\n#### Limiting privileges\n\nThe privileges granted in the example above are convenient,\nbut you may prefer to restrict them further to improve security. In particular,\nyou might want to prevent the Debezium user from creating tables, or\nselecting or locking any table.\n\nThe Debezium user needs the `CREATE TABLE` privilege to create the\n`LOG_MINING_FLUSH` table when it connects for the first\ntime. After this point, it doesn't need to create any more tables,\nso you can safely revoke this privilege with the following command:\n\n[code example]\n\n[The example above](#create-dbz-user) grants the `SELECT ANY TABLE` and\n`FLASHBACK ANY TABLE` privileges for convenience, but only the tables synced to RDI\nand the `V_$XXX` tables strictly need these privileges.\nYou can replace the `GRANT SELECT ANY TABLE` command with explicit\ncommands for each table. For example, you would use commands like the\nfollowing for the tables in our sample\n[`chinook`](https://github.com/Redislabs-Solution-Architects/rdi-quickstart-postgres)\ndatabase. (Note that Oracle 19c requires you to run a separate `GRANT`\ncommand for each table individually.)\n\n[code example]\n\nSimilarly, instead of `GRANT FLASHBACK ANY TABLE`, you would use the following\ncommands:\n\n[code example]\n\nThe `LOCK` privilege is automatically granted by the `SELECT`\nprivilege, so you can omit this command if you have granted `SELECT`\non specific tables.\n\n#### Revoking existing privileges\n\nIf you initially set the Debezium user's privileges on all tables,\nbut you now want to restrict them, you can revoke the existing\nprivileges before resetting them as described in the\n[Limiting privileges](#limiting-privileges) section.\n\nUse the following commands to revoke and reset the `SELECT` privileges:\n\n[code example]\n\nThe equivalent commands for `FLASHBACK` are:\n\n[code example]\n\nThe `SELECT` privilege automatically includes the `LOCK`\nprivilege, so when you grant `SELECT` for specific tables\nyou should also revoke `LOCK` on all tables:\n\n[code example]"},{"id":"logminer-complete","title":"6. Configuration is complete","role":"content","text":"Once you have followed the steps above, your Oracle database is ready\nfor Debezium to use."},{"id":"xstream","title":"XStream","role":"content","text":"[XStream](https://docs.oracle.com/en/database/oracle/oracle-database/19/xstrm/introduction-to-xstream.html#GUID-5939CB6C-8BA9-4594-8F96-B0453D246722)\nis a set of database components and APIs to communicate change data to and\nfrom an Oracle database. RDI specifically uses\n[XStream Out](https://docs.oracle.com/en/database/oracle/oracle-database/19/xstrm/xstream-out.html)\nto capture changes.\n\nFollow the steps in the sections below to configure XStream to work with\nDebezium and RDI.\n\n> [!NOTE]\n> You should run all database commands shown below as the `sysdba` user.\n\n[code example]"},{"id":"1-configure-recovery-area","title":"1. Configure recovery area","role":"content","text":"**non-RAC (single-instance):**\n\nCreate a directory for the recovery area on the Oracle host (or Oracle container if you are using a containerized Oracle).\n\nFor single-instance (non-RAC) deployments on local disk, you can use:\n\n[code example]\n\n  [code example]\n\n**Oracle RAC (multitenant environment):**\n\nFor Oracle RAC (cluster) environments, the Fast Recovery Area (FRA) must be on **shared storage** (ASM or a shared filesystem).\n\n- **If using ASM (recommended for RAC):**\n\n  [code example]\n\n  Replace `+FRA` with the ASM disk group used for your FRA. No `mkdir` is required; ASM manages the storage.\n\n- **If using a shared filesystem (NFS/OCFS2/etc.):**\n\n  [code example]\n\n  The path you use must:\n\n  - Be on shared storage visible from all RAC nodes\n  - Be mounted at the **same path** on all nodes\n  - Be writable by the Oracle user\n\n  Create the directory once on the shared filesystem (it will be visible from all nodes):\n\n  [code example]"},{"id":"2-enable-goldengate-replication","title":"2. Enable GoldenGate replication","role":"content","text":"Check if `enable_goldengate_replication` is already set to `true`:\n\n**Container database (CDB):**\n\nFor a CDB, check the parameter in the root container:\n\n[code example]\n\nIf it is not set to `true`, you need to set it and **restart the database**:\n\n[code example]\n\n**Non-container database (Non-CDB):**\n\nFor a non-CDB, check the parameter in the database instance:\n\n[code example]\n\nIf it is not set to `true`, you need to set it and **restart the database**:\n\n[code example]"},{"id":"3-configure-xstream","title":"3. Configure XStream","role":"content","text":"Use the following SQL commands to configure XStream (using the\n[`chinook`](https://github.com/Redislabs-Solution-Architects/rdi-quickstart-postgres/tree/main)\nschema as an example):\n\n**non-RAC (single-instance):**\n\n[code example]\n\n**Oracle RAC (multitenant environment):**\n\n[code example]"},{"id":"4-enable-supplemental-logging","title":"4. Enable supplemental logging","role":"content","text":"**Container database (CDB):**\n\nEnable supplemental logging for the tables you want to capture or\nfor the entire database. This lets Debezium capture the state of\ndatabase rows before and after changes occur.\n\n[code example]\n\n> [!NOTE]\n> The example above uses `orclpdb1` as the PDB name. Replace it with the name of the pluggable database (PDB) that you use in your own environment.\n\n**Non-container database (Non-CDB):**\n\nEnable supplemental logging for the tables you want to capture or\nfor the entire database. This lets Debezium capture the state of\ndatabase rows before and after changes occur.\n\n[code example]\n\n\n\n> [!NOTE]\n> You must configure supplemental logging explicitly for each table as shown \n> above. Otherwise, only the original sync will be performed and no change data will be \n> captured for the table."},{"id":"5-create-xstream-users","title":"5. Create XStream users","role":"content","text":"**Container database (CDB):**\n\n> [!NOTE]\n> The XStream user examples below use `ORCLCDB` as the CDB name and `ORCLPDB1`/`orclpdb1` as the PDB name in file paths and `ALTER SESSION SET CONTAINER` commands. Replace these with the CDB and PDB names from your own Oracle deployment.\n\nCreate an XStream administrator user with the following SQL:\n\n[code example]\n\nThen, create the XStream user:\n\n[code example]\n\n> [!NOTE]\n> If you are using the\n> [Debezium XStream documentation](https://debezium.io/documentation/reference/stable/connectors/oracle.html#creating-xstream-users-for-the-connector),\n> you should note that it misses out the last two GRANT statements shown above:\n>\n> [code example]\n>\n> However, without these, no tables can be read by Debezium, so neither the initial snapshot nor any subsequent updates will produce any data.\n\n**Non-container database (Non-CDB):**\n\n> [!NOTE]\n> The non-CDB architecture is deprecated in Oracle Database 12c and discontinued in Oracle Database 20c.\n\nFor a non-container (non-CDB) Oracle database:\n\n- You have a single database with no containers.\n- Users are regular database users without the `C##` prefix.\n- Application data is stored directly in the database.\n- You do not use `ALTER SESSION SET CONTAINER` statements or `CONTAINER=ALL` clauses.\n\n**Create XStream users and tablespaces (non-CDB)**<br/>\n\nRun the following script as the `sys` user:\n\n[code example]\n\nThis script creates:\n\n- The `xstream_adm_tbs` tablespace and `dbzadmin` XStream administrator user.\n- The `xstream_tbs` tablespace and `dbzxsuser` XStream connect user."},{"id":"6-create-an-xstream-outbound-server","title":"6. Create an XStream outbound server","role":"content","text":"Create the outbound server with the following SQL.\n\n**Container database (CDB):**\n\n> [!NOTE]\n> In this example, `ORCLCDB` is the CDB service name and `orclpdb1` is the PDB name. Replace them with the appropriate service and PDB names for your own environment.\n\nNote that you must connect as the `c##dbzadmin` user created in the previous step,\nnot the `sys` user:\n\n[code example]\n\n[code example]\n\n-\n\n**Non-container database (Non-CDB):**\n\nNote that you must connect as the `dbzadmin` user created in the previous step,\nnot the `sys` user:\n\n[code example]\n\n[code example]"},{"id":"7-add-a-custom-docker-image-for-the-debezium-server","title":"7. Add a custom Docker image for the Debezium server","role":"content","text":"To support XStream connector, you must create a custom [Docker](https://www.docker.com/) image that includes the required Instant Client package libraries for Linux x64 from the Oracle website.\n\n1.  On the Docker machine, download the Instant Client package for Linux x64 from the Oracle website\n\n    [code example]\n\n1.  Unzip it to the `./dbz-ora` directory:\n\n    [code example]\n\n1.  Create a `Dockerfile` in the `./dbz-ora` directory with the following contents:\n\n    [code example]\n\n1.  Create the custom image:\n\n    [code example]\n\n1.  Add the image to the K3s image registry using the following commands:\n\n    [code example]"},{"id":"8-make-rdi-use-the-custom-image-vm-installation","title":"8. Make RDI use the custom image (VM installation)","role":"content","text":"Edit the `rdi-operator` configmap:\n\n[code example]\n\nIn the editor, find the collector section and change the image settings:\n\n[code example]\n\nSave the configmap. Once it is saved, the operator will restart automatically and will apply the changes.\n\n> [!NOTE]\n> After upgrading to another RDI version,\n> the changes to the configmap will be lost. You must repeat the above steps after each upgrade."},{"id":"9-enable-the-oracle-configuration-in-rdi","title":"9. Enable the Oracle configuration in RDI","role":"content","text":"Finally, you must update your `config.yaml` file to enable XStream.\nThe example below shows the relevant parts of the `sources` section:\n\n[code example]\n\n> [!NOTE]\n> The values `ORCLCDB` and `ORCLPDB1` in the example above are sample CDB and PDB names. Set `database.dbname` and `database.pdb.name` to the CDB and PDB names for your own Oracle database.\n\nSee the\n[Debezium Oracle documentation](https://debezium.io/documentation/reference/stable/connectors/oracle.html#oracle-connector-properties)\nfor a full list of properties you can use in the `advanced.source` subsection."},{"id":"xstream-complete","title":"10. Configuration is complete","role":"content","text":"After you have followed the steps above, your Oracle database is ready\nfor Debezium to use."},{"id":"support-for-oracle-xmltype-columns-optional","title":"Support for Oracle XMLTYPE columns (optional)","role":"compatibility","text":"If your Oracle database contains tables with columns of type\n[`XMLTYPE`](https://docs.oracle.com/en/database/oracle/oracle-database/21/arpls/XMLTYPE.html),\nyou must configure additional libraries for Debezium Server to process these columns correctly."},{"id":"create-a-custom-debezium-server-image","title":"Create a custom Debezium Server image","role":"content","text":"To support `XMLTYPE` columns, you must create a custom [Docker](https://www.docker.com/) image\nthat includes the required Oracle XML libraries.\n\n1. Download the required libraries from Maven Central:\n\n   [code example]\n\n2. Create a `Dockerfile` in the same directory:\n\n   [code example]\n\n3. Build the custom image:\n\n   [code example]\n\n4. Add the image to your K3s image repository:\n\n   [code example]"},{"id":"configure-rdi-for-xmltype-support","title":"Configure RDI for XMLTYPE support","role":"configuration","text":"In your RDI configuration file, set the `lob.enabled` property to `true` in the\n`advanced.source` section:\n\n[code example]\n\n> [!NOTE]\n> The XMLTYPE configuration example uses `ORCLCDB` as the CDB name and `ORCLPDB1` as the PDB name. Replace these with your actual CDB and PDB names when configuring XMLTYPE support."},{"id":"test-xmltype-support","title":"Test XMLTYPE support","role":"content","text":"You can create a test table to verify that `XMLTYPE` columns work correctly\n(using the\n[`CHINOOK`](https://github.com/Redislabs-Solution-Architects/rdi-quickstart-postgres/tree/main)\nschema as an example):\n\n[code example]\n\nAfter you run an initial\n[snapshot](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines#pipeline-lifecycle),\nthe XML data appears in your Redis target database:\n\n![](https://redis.io/docs/latest/images/rdi/ingest/xmltype-example.webp)"},{"id":"faq","title":"FAQ","role":"content","text":""},{"id":"how-does-cdb-differ-from-non-cdb","title":"How does CDB differ from Non-CDB?","role":"content","text":"**CDB (Container Database – multitenant)**\n\n- Root container `CDB$ROOT` plus one or more pluggable databases (PDBs).\n- Common users (for example `C##DBZADMIN`, `C##DBZXSUSER`) exist across all containers.\n- Application data lives in PDBs.\n- Many XStream-related operations use `CONTAINER=ALL` and `ALTER SESSION SET CONTAINER`.\n\n**Non-CDB (traditional)**\n\n- Single database, no containers.\n- Users are regular database users without the `C##` prefix (for example `DBZADMIN`, `DBZXSUSER`).\n- Application data lives directly in that single database.\n- No `CONTAINER=ALL` or `ALTER SESSION SET CONTAINER` clauses are required.\n\nFrom an RDI/Debezium point of view, both models are supported; the main differences are in the user names you create and whether you need container-related clauses in the SQL."},{"id":"how-does-single-instance-differ-from-rac","title":"How does single-instance differ from RAC?","role":"content","text":"**Single-instance**\n\n- One Oracle instance running on one server.\n- You run the LogMiner/XStream setup scripts once.\n- All configuration (users, tablespaces, outbound server, logging) is stored in that database instance.\n\n**RAC (Real Application Clusters)**\n\n- Multiple instances (nodes) share the same database and storage.\n- You still run the XStream setup only once on any one node.\n- All nodes share the same:\n  - Database files\n  - Archive logs\n  - XStream outbound server\n  - Capture process\n\nImportant RAC points:\n\n- Archive log mode is enabled at the database level and applies to all nodes.\n- Tablespaces you create are in shared storage and visible from all nodes.\n- Users you create exist in the shared database and can connect through any node.\n- Debezium/RDI can connect to any RAC node or to a SCAN address; it does not need a specific node."},{"id":"in-rac-setups-do-you-need-to-execute-steps-on-all-nodes","title":"In RAC setups, do you need to execute steps on all nodes?","role":"content","text":"No. The XStream configuration is database-level, so you execute the setup once on any node:\n\n- Enable archive log mode: once (affects the whole database).\n- Create XStream tablespaces: once (on shared storage).\n- Create XStream users: once (in the shared database).\n- Create the XStream outbound server: once (visible from all nodes).\n- Enable supplemental logging: once (database-wide).\n\nYou may run client connection tests from multiple nodes, but the actual administrative steps only need to be executed once."},{"id":"why-are-two-xstream-tablespaces-required","title":"Why are two XStream tablespaces required?","role":"content","text":"XStream is not just reading your tables - it's storing its own data:\n\n**Customer's existing tablespace**:\n\n[code example]\n\n**XStream tablespaces**:\n\n[code example]\n\n#### Real Example: What Gets Stored Where\n\nScenario: Customer has CUSTOMERS table\n\nCustomer's existing tablespace (USERS):\n[code example]\n\nXStream tablespaces (NEW):\n[code example]\n\n#### Size Comparison\n\nCustomer's Application Tablespaces:\n\n[code example]\n\nXStream Tablespaces:\n\n[code example]\n\n#### General Recommendations\n\nThis guide uses two separate tablespaces:\n\n- `xstream_adm_tbs` – XStream administrator tablespace.\n- `xstream_tbs` – XStream user (connect) tablespace.\n\nThey serve different purposes and follow the principle of least privilege.\n\n**1. Separation of concerns and security**\n\n- *Administrator tablespace (`xstream_adm_tbs`)*\n  - Used by the XStream admin user (for example `C##DBZADMIN` or `DBZADMIN`).\n  - Stores XStream metadata and control structures:\n    - Capture process metadata\n    - Outbound server configuration\n    - Queue tables and queues\n    - Other XStream internal objects\n  - Belongs to a highly privileged user that can create and manage XStream objects.\n\n- *User tablespace (`xstream_tbs`)*\n  - Used by the XStream connect user (for example `C##DBZXSUSER` or `DBZXSUSER`).\n  - Lower-privileged account used by Debezium/RDI to read changes.\n  - Keeps operational data and temporary objects separate from admin metadata.\n\n**2. Resource management**\n\nHaving two tablespaces lets you:\n\n- Monitor space usage separately for admin vs user workloads.\n- Allow for different growth patterns (admin metadata typically grows more slowly).\n- Apply separate quotas per user/tablespace.\n- Tune storage parameters differently for each tablespace if needed.\n\n**3. Operational benefits**\n\n- Backup and recovery:\n  - You can back up or restore admin and user data independently.\n  - Critical XStream metadata is isolated in its own tablespace.\n- Maintenance and troubleshooting:\n  - You can perform maintenance on one tablespace without affecting the other.\n  - Easier to diagnose which part of the XStream setup is consuming space.\n- Security and auditing:\n  - Clear separation of administrative vs operational data.\n  - Easier to audit which users access which objects.\n\n**4. Can you use a single tablespace?**\n\nTechnically you could place both users in a single tablespace, but this is not recommended for production:\n\n- It weakens separation of duties and least-privilege design.\n- It makes monitoring and capacity planning harder.\n- It mixes admin metadata and application-facing data in one place.\n\nOracle and Debezium best practices recommend separating admin and user workloads into different tablespaces."},{"id":"what-is-the-xstream-outbound-server","title":"What is the XStream outbound server?","role":"content","text":"The XStream outbound server is the component that streams database changes out of Oracle in a supported way. It:\n\n- Captures committed changes (INSERT, UPDATE, DELETE) from redo logs.\n- Converts them into logical change records (LCRs).\n- Exposes an API that external consumers such as Debezium/RDI use.\n\n**High-level architecture**\n\n[code example]\n\n**Key components**\n\n- *Redo logs*: Contain the physical change records for the database.\n- *Capture process*: Mines redo logs and turns them into logical change records.\n- *Queue*: Temporarily stores captured changes.\n- *XStream outbound server* (for example `DBZXOUT`):\n  - Provides the streaming interface for consumers.\n  - Applies filters (schemas/tables) configured when you create it.\n- *Connect user* (for example `C##DBZXSUSER` or `DBZXSUSER`):\n  - The user Debezium/RDI connects as.\n  - Reads changes from the outbound server.\n\n**Why it is needed**\n\nWithout an XStream outbound server:\n\n- There is no supported, structured API for consuming change events.\n- You would have to parse redo logs directly, which is complex and unsupported.\n- You lose built-in filtering, transaction grouping, and restart/resume semantics.\n\nWith an XStream outbound server:\n\n- Changes are available in near real time.\n- You can capture only selected schemas/tables.\n- Debezium/RDI uses Oracle’s supported XStream API.\n- Transactions are preserved and grouped correctly.\n- Offsets/positions allow clean resume after restarts.\n- Multiple consumers can be attached to the same outbound server if needed."}],"examples":[{"id":"overview-ex0","language":"checklist {id=\"oraclelogminerlist\"}","code":"- [ ] [Configure Oracle LogMiner](#1-configure-oracle-logminer)\n- [ ] [Enable supplemental logging](#supp-logging)\n- [ ] [Check the redo log sizing](#3-check-the-redo-log-sizing)\n- [ ] [Set the Archive log destination](#4-set-the-archive-log-destination)\n- [ ] [Create a user for the connector](#create-dbz-user)","section_id":"overview"},{"id":"overview-ex1","language":"checklist {id=\"oraclexstreamlist\"}","code":"- [ ] [Configure recovery area](#1-configure-recovery-area)\n- [ ] [Enable GoldenGate replication](#2-enable-goldengate-replication)\n- [ ] [Configure XStream](#3-configure-xstream)\n- [ ] [Enable supplemental logging](#4-enable-supplemental-logging)\n- [ ] [Create XStream users](#5-create-xstream-users)\n- [ ] [Create an XStream outbound server](#6-create-an-xstream-outbound-server)\n- [ ] [Add a custom Docker image for the Debezium server](#7-add-a-custom-docker-image-for-the-debezium-server)\n- [ ] [Make RDI use the custom image](#8-make-rdi-use-the-custom-image-vm-installation)\n- [ ] [Enable the Oracle configuration in RDI](#9-enable-the-oracle-configuration-in-rdi)","section_id":"overview"},{"id":"overview-ex2","language":"checklist {id=\"oraclexmltypelist\"}","code":"- [ ] [Create a custom Debezium Server image](#create-a-custom-debezium-server-image)\n- [ ] [Configure RDI for XMLTYPE support](#configure-rdi-for-xmltype-support)\n- [ ] [Test XMLTYPE support](#test-xmltype-support)","section_id":"overview"},{"id":"1-configure-oracle-logminer-ex0","language":"sql","code":"ORACLE_SID=ORACLCDB dbz_oracle sqlplus /nolog\n\nCONNECT sys/top_secret AS SYSDBA\nalter system set db_recovery_file_dest_size = 10G;\nalter system set db_recovery_file_dest = '/opt/oracle/oradata/recovery_area' scope=spfile;\n-- ======================================================================================================================================================\n-- !!!IMPORTANT!!!: \n-- In order to avoid Oracle downtime, please check if the LOG_MODE on your database is already set to `ARCHIVELOG` before executing the following commands: \n-- SELECT log_mode FROM v$database;\n-- If the LOG_MODE is already `ARCHIVELOG`, then you can skip the rest of the commands in this script\n-- ======================================================================================================================================================\nshutdown immediate\nstartup mount\nalter database archivelog;\nalter database open;\n-- You should now see \"Database log mode: Archive Mode\"\narchive log list\n\nexit;","section_id":"1-configure-oracle-logminer"},{"id":"1-configure-oracle-logminer-ex1","language":"sql","code":"SQL> SELECT LOG_MODE FROM V$DATABASE;\n\nLOG_MODE\n------------\nARCHIVELOG","section_id":"1-configure-oracle-logminer"},{"id":"1-configure-oracle-logminer-ex2","language":"sql","code":"exec rdsadmin.rdsadmin_util.set_configuration('archivelog retention hours',24);\nexec rdsadmin.rdsadmin_util.alter_supplemental_logging('ADD');","section_id":"1-configure-oracle-logminer"},{"id":"supp-logging-ex0","language":"sql","code":"ALTER TABLE inventory.customers ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;","section_id":"supp-logging"},{"id":"supp-logging-ex1","language":"sql","code":"ALTER DATABASE ADD SUPPLEMENTAL LOG DATA;","section_id":"supp-logging"},{"id":"4-set-the-archive-log-destination-ex0","language":"json","code":"{\n    \"log.mining.archive.destination.name\": \"LOG_ARCHIVE_DEST_3\"\n}","section_id":"4-set-the-archive-log-destination"},{"id":"create-dbz-user-ex0","language":"sql","code":"sqlplus sys/top_secret@//localhost:1521/ORCLCDB as sysdba\nCREATE TABLESPACE logminer_tbs DATAFILE '/opt/oracle/oradata/ORCLCDB/logminer_tbs.dbf'\n    SIZE 25M REUSE AUTOEXTEND ON MAXSIZE UNLIMITED;\nexit;\n\nsqlplus sys/top_secret@//localhost:1521/ORCLPDB1 as sysdba\nCREATE TABLESPACE logminer_tbs DATAFILE '/opt/oracle/oradata/ORCLCDB/ORCLPDB1/logminer_tbs.dbf'\n    SIZE 25M REUSE AUTOEXTEND ON MAXSIZE UNLIMITED;\nexit;\n\nsqlplus sys/top_secret@//localhost:1521/ORCLCDB as sysdba\n\nCREATE USER c##dbzuser IDENTIFIED BY dbz\n    DEFAULT TABLESPACE logminer_tbs\n    QUOTA UNLIMITED ON logminer_tbs\n    CONTAINER=ALL;\n\nGRANT CREATE SESSION TO c##dbzuser CONTAINER=ALL;\nGRANT SET CONTAINER TO c##dbzuser CONTAINER=ALL;\nGRANT SELECT ON V_$DATABASE to c##dbzuser CONTAINER=ALL;\n\n-- See `Limiting privileges` below if the privileges\n-- granted by these two commands raise security concerns.\nGRANT FLASHBACK ANY TABLE TO c##dbzuser CONTAINER=ALL;\nGRANT SELECT ANY TABLE TO c##dbzuser CONTAINER=ALL;\n-- \n\nGRANT SELECT_CATALOG_ROLE TO c##dbzuser CONTAINER=ALL;\nGRANT EXECUTE_CATALOG_ROLE TO c##dbzuser CONTAINER=ALL;\nGRANT SELECT ANY TRANSACTION TO c##dbzuser CONTAINER=ALL;\nGRANT LOGMINING TO c##dbzuser CONTAINER=ALL;\n\n-- See `Limiting privileges` below if the privileges\n-- granted by these two commands raise security concerns.\nGRANT CREATE TABLE TO c##dbzuser CONTAINER=ALL;\nGRANT LOCK ANY TABLE TO c##dbzuser CONTAINER=ALL;\n-- \n\nGRANT CREATE SEQUENCE TO c##dbzuser CONTAINER=ALL;\n\nGRANT EXECUTE ON DBMS_LOGMNR TO c##dbzuser CONTAINER=ALL;\nGRANT EXECUTE ON DBMS_LOGMNR_D TO c##dbzuser CONTAINER=ALL;\n\nGRANT SELECT ON V_$LOG TO c##dbzuser CONTAINER=ALL;\nGRANT SELECT ON V_$LOG_HISTORY TO c##dbzuser CONTAINER=ALL;\nGRANT SELECT ON V_$LOGMNR_LOGS TO c##dbzuser CONTAINER=ALL;\nGRANT SELECT ON V_$LOGMNR_CONTENTS TO c##dbzuser CONTAINER=ALL;\nGRANT SELECT ON V_$LOGMNR_PARAMETERS TO c##dbzuser CONTAINER=ALL;\nGRANT SELECT ON V_$LOGFILE TO c##dbzuser CONTAINER=ALL;\nGRANT SELECT ON V_$ARCHIVED_LOG TO c##dbzuser CONTAINER=ALL;\nGRANT SELECT ON V_$ARCHIVE_DEST_STATUS TO c##dbzuser CONTAINER=ALL;\nGRANT SELECT ON V_$TRANSACTION TO c##dbzuser CONTAINER=ALL;\n\nGRANT SELECT ON V_$MYSTAT TO c##dbzuser CONTAINER=ALL;\nGRANT SELECT ON V_$STATNAME TO c##dbzuser CONTAINER=ALL;\n\nexit;","section_id":"create-dbz-user"},{"id":"create-dbz-user-ex1","language":"sql","code":"REVOKE CREATE TABLE FROM c##dbzuser container=all;","section_id":"create-dbz-user"},{"id":"create-dbz-user-ex2","language":"sql","code":"GRANT SELECT ON chinook.album TO c##dbzuser;\nGRANT SELECT ON chinook.artist TO c##dbzuser;\nGRANT SELECT ON chinook.customer TO c##dbzuser;\n...","section_id":"create-dbz-user"},{"id":"create-dbz-user-ex3","language":"sql","code":"GRANT FLASHBACK ON chinook.album TO c##dbzuser;\nGRANT FLASHBACK ON chinook.artist TO c##dbzuser;\nGRANT FLASHBACK ON chinook.customer TO c##dbzuser;\n...","section_id":"create-dbz-user"},{"id":"create-dbz-user-ex4","language":"sql","code":"REVOKE SELECT ANY TABLE FROM c##dbzuser container=all;\nALTER SESSION SET container=orclpdb1;\n\nGRANT SELECT ON chinook.album TO c##dbzuser;\n-- ...etc","section_id":"create-dbz-user"},{"id":"create-dbz-user-ex5","language":"sql","code":"REVOKE FLASHBACK ANY TABLE FROM c##dbzuser container=all;\nALTER SESSION SET container=orclpdb1;\nGRANT FLASHBACK ON chinook.album TO c##dbzuser;","section_id":"create-dbz-user"},{"id":"create-dbz-user-ex6","language":"sql","code":"REVOKE LOCK ANY TABLE FROM c##dbzuser container=all;","section_id":"create-dbz-user"},{"id":"xstream-ex0","language":"sql","code":"sqlplus sys/<PASSWORD> as sysdba","section_id":"xstream"},{"id":"1-configure-recovery-area-ex0","language":"bash","code":"mkdir -p /opt/oracle/oradata/recovery_area","section_id":"1-configure-recovery-area"},{"id":"1-configure-recovery-area-ex1","language":"sql","code":"ALTER SYSTEM SET db_recovery_file_dest_size = 10G; -- Adjust size as needed\n  ALTER SYSTEM SET db_recovery_file_dest = '/opt/oracle/oradata/recovery_area' SCOPE=BOTH;","section_id":"1-configure-recovery-area"},{"id":"1-configure-recovery-area-ex2","language":"sql","code":"ALTER SYSTEM SET db_recovery_file_dest_size = 10G; -- Adjust size as needed\n  ALTER SYSTEM SET db_recovery_file_dest = '+FRA' SCOPE=BOTH;","section_id":"1-configure-recovery-area"},{"id":"1-configure-recovery-area-ex3","language":"sql","code":"ALTER SYSTEM SET db_recovery_file_dest_size = 10G; -- Adjust size as needed\n  ALTER SYSTEM SET db_recovery_file_dest = '/u02/oradata/recovery_area' SCOPE=BOTH;","section_id":"1-configure-recovery-area"},{"id":"1-configure-recovery-area-ex4","language":"bash","code":"mkdir -p /u02/oradata/recovery_area\n  chown oracle:oinstall /u02/oradata/recovery_area\n  chmod 755 /u02/oradata/recovery_area","section_id":"1-configure-recovery-area"},{"id":"2-enable-goldengate-replication-ex0","language":"sql","code":"SELECT VALUE FROM V$PARAMETER WHERE NAME = 'enable_goldengate_replication';","section_id":"2-enable-goldengate-replication"},{"id":"2-enable-goldengate-replication-ex1","language":"sql","code":"ALTER SYSTEM SET enable_goldengate_replication=true SCOPE=BOTH CONTAINER=ALL;","section_id":"2-enable-goldengate-replication"},{"id":"2-enable-goldengate-replication-ex2","language":"sql","code":"SELECT VALUE FROM V$PARAMETER WHERE NAME = 'enable_goldengate_replication';","section_id":"2-enable-goldengate-replication"},{"id":"2-enable-goldengate-replication-ex3","language":"sql","code":"ALTER SYSTEM SET enable_goldengate_replication=true SCOPE=BOTH;","section_id":"2-enable-goldengate-replication"},{"id":"3-configure-xstream-ex0","language":"sql","code":"-- ======================================================================================================================================================\n-- !!!IMPORTANT!!!: \n-- In order to avoid Oracle downtime, please check if the LOG_MODE on your database is already set to `ARCHIVELOG` before executing the following commands: \n-- SELECT log_mode FROM v$database;\n-- If the LOG_MODE is already `ARCHIVELOG`, then you can skip the rest of the commands in this script\n-- ======================================================================================================================================================\nSQL> shutdown immediate\nDatabase closed.\nDatabase dismounted.\nORACLE instance shut down.\nSQL> startup mount\nORACLE instance started.\n\nTotal System Global Area 1476391776 bytes\nFixed Size     9134944 bytes\nVariable Size 1006632960 bytes\nDatabase Buffers   452984832 bytes\nRedo Buffers     7639040 bytes\nDatabase mounted.\nSQL> alter database archivelog;\nSQL> alter database open;\nSQL> archive log list\nDatabase log mode       Archive Mode\nAutomatic archival       Enabled\nArchive destination       USE_DB_RECOVERY_FILE_DEST\nOldest online log sequence     10\nNext log sequence to archive   12\nCurrent log sequence       12\n\n-- Confirm the database is in ARCHIVELOG mode.\nSQL> SELECT LOG_MODE FROM V$DATABASE;","section_id":"3-configure-xstream"},{"id":"3-configure-xstream-ex1","language":"sql","code":"-- ======================================================================================================================================================\n-- !!!IMPORTANT!!!: \n-- In order to avoid Oracle downtime, please check if the LOG_MODE on your database is already set to `ARCHIVELOG` before executing the following commands: \n-- SELECT log_mode FROM v$database;\n-- If the LOG_MODE is already `ARCHIVELOG`, then you can skip the rest of the commands in this script\n-- ======================================================================================================================================================\n\n-- Stop all database instances\nSQL> srvctl stop database -d <db_Name>\n\n-- Start the database in mount state.\nSQL> srvctl start database -d <db_Name> -o mount\n\n-- Enable archive log mode\nSQL> alter database archivelog;\n\n-- Restart all database instances.\nSQL> srvctl stop database -d <db_Name>\nSQL> srvctl start database -d <db_Name>\n\n-- Confirm the database is in ARCHIVELOG mode.\nSQL> SELECT LOG_MODE FROM V$DATABASE;","section_id":"3-configure-xstream"},{"id":"4-enable-supplemental-logging-ex0","language":"sql","code":"SQL> ALTER DATABASE ADD SUPPLEMENTAL LOG DATA;\n\nSQL> alter session set container=orclpdb1;\n\nSQL> ALTER TABLE CHINOOK.ALBUM ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;\nSQL> ALTER TABLE CHINOOK.ARTIST ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;\nSQL> ALTER TABLE CHINOOK.CUSTOMER ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;\nSQL> ALTER TABLE CHINOOK.EMPLOYEE ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;\nSQL> ALTER TABLE CHINOOK.GENRE ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;\nSQL> ALTER TABLE CHINOOK.INVOICE ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;\nSQL> ALTER TABLE CHINOOK.INVOICELINE ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;\nSQL> ALTER TABLE CHINOOK.MEDIATYPE ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;\nSQL> ALTER TABLE CHINOOK.PLAYLIST ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;\nSQL> ALTER TABLE CHINOOK.PLAYLISTTRACK ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;\nSQL> ALTER TABLE CHINOOK.TRACK ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;","section_id":"4-enable-supplemental-logging"},{"id":"4-enable-supplemental-logging-ex1","language":"sql","code":"SQL> ALTER DATABASE ADD SUPPLEMENTAL LOG DATA;\n\nSQL> ALTER TABLE CHINOOK.ALBUM ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;\nSQL> ALTER TABLE CHINOOK.ARTIST ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;\nSQL> ALTER TABLE CHINOOK.CUSTOMER ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;\nSQL> ALTER TABLE CHINOOK.EMPLOYEE ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;\nSQL> ALTER TABLE CHINOOK.GENRE ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;\nSQL> ALTER TABLE CHINOOK.INVOICE ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;\nSQL> ALTER TABLE CHINOOK.INVOICELINE ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;\nSQL> ALTER TABLE CHINOOK.MEDIATYPE ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;\nSQL> ALTER TABLE CHINOOK.PLAYLIST ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;\nSQL> ALTER TABLE CHINOOK.PLAYLISTTRACK ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;\nSQL> ALTER TABLE CHINOOK.TRACK ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;","section_id":"4-enable-supplemental-logging"},{"id":"5-create-xstream-users-ex0","language":"sql","code":"sqlplus sys/<PASSWORD> as sysdba\n\nSQL> ALTER SESSION SET CONTAINER=CDB$ROOT;\nSQL> CREATE TABLESPACE xstream_adm_tbs DATAFILE '/opt/oracle/oradata/ORCLCDB/xstream_adm_tbs.dbf'\n    SIZE 25M REUSE AUTOEXTEND ON MAXSIZE UNLIMITED;\n\nSQL> alter session set container=orclpdb1;\n\nSQL> CREATE TABLESPACE xstream_adm_tbs DATAFILE '/opt/oracle/oradata/ORCLCDB/ORCLPDB1/xstream_adm_tbs.dbf'\n    SIZE 25M REUSE AUTOEXTEND ON MAXSIZE UNLIMITED;\n\nSQL> alter session set container=cdb$root;\n\nSQL> CREATE USER c##dbzadmin IDENTIFIED BY dbz\n    DEFAULT TABLESPACE xstream_adm_tbs\n    QUOTA UNLIMITED ON xstream_adm_tbs\n    CONTAINER=ALL;\n\nSQL> GRANT CREATE SESSION, SET CONTAINER TO c##dbzadmin CONTAINER=ALL;\n\nSQL> BEGIN\n     DBMS_XSTREAM_AUTH.GRANT_ADMIN_PRIVILEGE(\n        grantee                 => 'c##dbzadmin',\n        privilege_type          => 'CAPTURE',\n        grant_select_privileges => TRUE,\n        container               => 'ALL'\n     );\n  END;\n  /","section_id":"5-create-xstream-users"},{"id":"5-create-xstream-users-ex1","language":"sql","code":"sqlplus sys/<PASSWORD> as sysdba\n\nSQL> CREATE TABLESPACE xstream_tbs DATAFILE '/opt/oracle/oradata/ORCLCDB/xstream_tbs.dbf'\n    SIZE 25M REUSE AUTOEXTEND ON MAXSIZE UNLIMITED;\n\nSQL> alter session set container=orclpdb1;\n\nSQL> CREATE TABLESPACE xstream_tbs DATAFILE '/opt/oracle/oradata/ORCLCDB/ORCLPDB1/xstream_tbs.dbf'\n    SIZE 25M REUSE AUTOEXTEND ON MAXSIZE UNLIMITED;\n\nSQL> alter session set container=cdb$root;\n\nSQL> CREATE USER c##dbzxsuser IDENTIFIED BY dbz\n    DEFAULT TABLESPACE xstream_tbs\n    QUOTA UNLIMITED ON xstream_tbs\n    CONTAINER=ALL;\n\nSQL> GRANT CREATE SESSION TO c##dbzxsuser CONTAINER=ALL;\nSQL> GRANT SET CONTAINER TO c##dbzxsuser CONTAINER=ALL;\nSQL> GRANT SELECT ON V_$DATABASE to c##dbzxsuser CONTAINER=ALL;\nSQL> GRANT FLASHBACK ANY TABLE TO c##dbzxsuser CONTAINER=ALL;\nSQL> GRANT SELECT_CATALOG_ROLE TO c##dbzxsuser CONTAINER=ALL;\nSQL> GRANT EXECUTE_CATALOG_ROLE TO c##dbzxsuser CONTAINER=ALL;\nSQL> GRANT SELECT ANY TABLE TO c##dbzxsuser CONTAINER=ALL;\nSQL> GRANT LOCK ANY TABLE TO c##dbzxsuser CONTAINER=ALL;","section_id":"5-create-xstream-users"},{"id":"5-create-xstream-users-ex2","language":"sql","code":"> GRANT SELECT ANY TABLE TO c##dbzxsuser CONTAINER=ALL;\n> GRANT LOCK ANY TABLE TO c##dbzxsuser CONTAINER=ALL;\n>","section_id":"5-create-xstream-users"},{"id":"5-create-xstream-users-ex3","language":"sql","code":"-- =====================================================\n-- Create XStream Users - FOR NON-CDB ENVIRONMENT\n-- =====================================================\n-- This script creates the XStream administrator and user accounts\n-- Run as: sqlplus sys/<PASSWORD> as sysdba\n-- =====================================================\n\n-- Step 1: Create XStream Administrator Tablespace\nCREATE TABLESPACE xstream_adm_tbs DATAFILE '/u01/app/oracle/oradata/ORCL/xstream_adm_tbs.dbf'\n  SIZE 25M REUSE AUTOEXTEND ON MAXSIZE UNLIMITED;\n\nPROMPT Tablespace xstream_adm_tbs created\n\n-- Step 2: Create XStream Administrator User\nCREATE USER dbzadmin IDENTIFIED BY dbz\n  DEFAULT TABLESPACE xstream_adm_tbs\n  QUOTA UNLIMITED ON xstream_adm_tbs;\n\nGRANT CREATE SESSION TO dbzadmin;\n\nPROMPT User dbzadmin created\n\n-- Step 3: Grant XStream Admin Privileges\nBEGIN\n  DBMS_XSTREAM_AUTH.GRANT_ADMIN_PRIVILEGE(\n    grantee                 => 'dbzadmin',\n    privilege_type          => 'CAPTURE',\n    grant_select_privileges => TRUE\n  );\nEND;\n/\n\nPROMPT XStream admin privileges granted to dbzadmin\n\n-- Step 4: Create XStream User Tablespace\nCREATE TABLESPACE xstream_tbs DATAFILE '/u01/app/oracle/oradata/ORCL/xstream_tbs.dbf'\n  SIZE 25M REUSE AUTOEXTEND ON MAXSIZE UNLIMITED;\n\nPROMPT Tablespace xstream_tbs created\n\n-- Step 5: Create XStream User\nCREATE USER dbzxsuser IDENTIFIED BY dbz\n  DEFAULT TABLESPACE xstream_tbs\n  QUOTA UNLIMITED ON xstream_tbs;\n\nPROMPT User dbzxsuser created\n\n-- Step 6: Grant necessary privileges to XStream User\nGRANT CREATE SESSION TO dbzxsuser;\nGRANT SELECT ON V_$DATABASE TO dbzxsuser;\nGRANT FLASHBACK ANY TABLE TO dbzxsuser;\nGRANT SELECT_CATALOG_ROLE TO dbzxsuser;\nGRANT EXECUTE_CATALOG_ROLE TO dbzxsuser;\nGRANT SELECT ANY TABLE TO dbzxsuser;\nGRANT LOCK ANY TABLE TO dbzxsuser;\n\nPROMPT Privileges granted to dbzxsuser\n\n-- Verification: Check users were created\nSELECT username, account_status, default_tablespace \nFROM dba_users \nWHERE username IN ('DBZADMIN', 'DBZXSUSER');\n\nPROMPT\nPROMPT =====================================================\nPROMPT XStream Users Created Successfully!\nPROMPT =====================================================\nPROMPT Created users:\nPROMPT   - dbzadmin (XStream Administrator)\nPROMPT   - dbzxsuser (XStream User)\nPROMPT\nPROMPT Next step:\nPROMPT Run 03_create_xstream_outbound.sql to create outbound server\nPROMPT =====================================================","section_id":"5-create-xstream-users"},{"id":"6-create-an-xstream-outbound-server-ex0","language":"bash","code":"sqlplus c##dbzadmin/dbz@localhost:1521/ORCLCDB","section_id":"6-create-an-xstream-outbound-server"},{"id":"6-create-an-xstream-outbound-server-ex1","language":"sql","code":"-- =====================================================\n-- Create XStream Outbound Server\n-- =====================================================\n-- This script creates the XStream outbound server for CDC\n-- Run as: sqlplus c##dbzadmin/dbz@localhost:1521/ORCLCDB\n-- =====================================================\n\n-- Step 1: Create XStream Outbound Server\nDECLARE\n  tables  DBMS_UTILITY.UNCL_ARRAY;\n  schemas DBMS_UTILITY.UNCL_ARRAY;\nBEGIN\n  tables(1)  := NULL;\n  schemas(1) := 'C##DBZUSER';\n  \n  DBMS_XSTREAM_ADM.CREATE_OUTBOUND(\n    server_name           => 'dbzxout',\n    source_container_name => 'XEPDB1',\n    table_names           => tables,\n    schema_names          => schemas\n  );\nEND;\n/\n\n-- Step 2: Configure XStream User to connect to outbound server\n-- This must be run as sys user\nCONNECT sys/oracle AS SYSDBA\n\nBEGIN\n  DBMS_XSTREAM_ADM.ALTER_OUTBOUND(\n    server_name  => 'dbzxout',\n    connect_user => 'c##dbzxsuser'\n  );\nEND;\n/\n\n-- Verification: Check outbound server was created\nSELECT server_name, source_database, capture_name, capture_user, connect_user, queue_owner, queue_name\nFROM dba_xstream_outbound;\n\n-- Verification: Check capture process\nSELECT capture_name, status, capture_type, source_database\nFROM dba_capture;\n\nPROMPT\nPROMPT =====================================================\nPROMPT XStream Outbound Server Created Successfully!\nPROMPT =====================================================\nPROMPT Server name: dbzxout\nPROMPT Schema: C##DBZUSER\nPROMPT PDB: XEPDB1\nPROMPT =====================================================","section_id":"6-create-an-xstream-outbound-server"},{"id":"6-create-an-xstream-outbound-server-ex2","language":"bash","code":"sqlplus dbzadmin/dbz@localhost:1521/ORCL","section_id":"6-create-an-xstream-outbound-server"},{"id":"6-create-an-xstream-outbound-server-ex3","language":"sql","code":"-- =====================================================\n-- Create XStream Outbound Server - FOR NON-CDB ENVIRONMENT\n-- =====================================================\n-- This script creates the XStream outbound server for CDC\n-- Run as: sqlplus dbzadmin/dbz@localhost:1521/ORCL\n-- =====================================================\n\n-- Step 1: Create XStream Outbound Server\nDECLARE\n  tables  DBMS_UTILITY.UNCL_ARRAY;\n  schemas DBMS_UTILITY.UNCL_ARRAY;\nBEGIN\n  tables(1)  := NULL;\n  schemas(1) := 'chinook';  -- Replace with actual schema name\n  \n  DBMS_XSTREAM_ADM.CREATE_OUTBOUND(\n    server_name     => 'dbzxout',\n    table_names     => tables,\n    schema_names    => schemas\n  );\nEND;\n/\n\nPROMPT XStream outbound server created\n\n-- Step 2: Configure XStream User to connect to outbound server\n-- This must be run as sys user\nCONNECT sys/<PASSWORD> AS SYSDBA\n\nBEGIN\n  DBMS_XSTREAM_ADM.ALTER_OUTBOUND(\n    server_name  => 'dbzxout',\n    connect_user => 'dbzxsuser'\n  );\nEND;\n/\n\nPROMPT Connect user configured\n\n-- Verification: Check outbound server was created\nSELECT server_name, source_database, capture_name, capture_user, connect_user, queue_owner, queue_name\nFROM dba_xstream_outbound;\n\n-- Verification: Check capture process\nSELECT capture_name, status, capture_type, source_database\nFROM dba_capture;\n\nPROMPT\nPROMPT =====================================================\nPROMPT XStream Outbound Server Created Successfully!\nPROMPT =====================================================\nPROMPT Server name: dbzxout\nPROMPT =====================================================","section_id":"6-create-an-xstream-outbound-server"},{"id":"7-add-a-custom-docker-image-for-the-debezium-server-ex0","language":"bash","code":"wget https://download.oracle.com/otn_software/linux/instantclient/2380000/instantclient-basic-linux.x64-23.8.0.25.04.zip","section_id":"7-add-a-custom-docker-image-for-the-debezium-server"},{"id":"7-add-a-custom-docker-image-for-the-debezium-server-ex1","language":"bash","code":"unzip instantclient-basic-linux.x64-23.8.0.25.04.zip -d ./dbz-ora","section_id":"7-add-a-custom-docker-image-for-the-debezium-server"},{"id":"7-add-a-custom-docker-image-for-the-debezium-server-ex2","language":"docker","code":"FROM debezium/server:3.0.8.Final\n\n    USER root\n\n    RUN microdnf -y install libaio \\\n    && microdnf clean all \\\n    && mkdir -p /opt/oracle/instant_client \\\n    && rm -f /debezium/lib/ojdbc11*.jar\n\n    COPY instantclient_23_8/* /opt/oracle/instant_client\n\n    USER jboss\n\n    COPY instantclient_23_8/xstreams.jar /debezium/lib\n    COPY instantclient_23_8/ojdbc11.jar /debezium/lib\n\n    ENV LD_LIBRARY_PATH=/opt/oracle/instant_client","section_id":"7-add-a-custom-docker-image-for-the-debezium-server"},{"id":"7-add-a-custom-docker-image-for-the-debezium-server-ex3","language":"bash","code":"docker build -t dbz-ora dbz-ora","section_id":"7-add-a-custom-docker-image-for-the-debezium-server"},{"id":"7-add-a-custom-docker-image-for-the-debezium-server-ex4","language":"bash","code":"docker tag dbz-ora quay.io/debezium/server:3.0.8.Final\n    docker image save quay.io/debezium/server:3.0.8.Final -o dbz3.0.8-xstream-linux-amd.tar\n    sudo k3s ctr images import dbz3.0.8-xstream-linux-amd.tar all","section_id":"7-add-a-custom-docker-image-for-the-debezium-server"},{"id":"8-make-rdi-use-the-custom-image-vm-installation-ex0","language":"bash","code":"kubectl edit configmap rdi-operator -n rdi","section_id":"8-make-rdi-use-the-custom-image-vm-installation"},{"id":"8-make-rdi-use-the-custom-image-vm-installation-ex1","language":"yaml","code":"collector:\n        image:\n          pullPolicy: IfNotPresent\n          registry: docker.io # change this to `quay.io`\n          repository: redislabs/debezium-server # Change this to `debezium/server`\n          tag: 3.0.8.Final-rdi.1 # Change this to `3.0.8.Final`","section_id":"8-make-rdi-use-the-custom-image-vm-installation"},{"id":"9-enable-the-oracle-configuration-in-rdi-ex0","language":"yaml","code":"sources:\n  oracle:\n    type: cdc\n    logging:\n      level: info\n    connection:\n      type: oracle\n      host: host.docker.internal\n      port: 1521\n      user: ${ORACLE_DB_USERNAME}\n      password: ${ORACLE_DB_PASSWORD}\n    advanced:\n      source:\n        database.dbname: ORCLCDB\n        database.pdb.name: ORCLPDB1\n        database.connection.adapter: xstream\n        database.out.server.name: dbzxout","section_id":"9-enable-the-oracle-configuration-in-rdi"},{"id":"create-a-custom-debezium-server-image-ex0","language":"bash","code":"mkdir xml\n   cd xml\n   wget https://repo.maven.apache.org/maven2/com/oracle/database/xml/xdb/19.27.0.0/xdb-19.27.0.0.jar\n   wget https://repo.maven.apache.org/maven2/com/oracle/database/xml/xmlparserv2/19.27.0.0/xmlparserv2-19.27.0.0.jar\n   mv xdb-19.27.0.0.jar xdb.jar\n   mv xmlparserv2-19.27.0.0.jar xmlparserv2.jar","section_id":"create-a-custom-debezium-server-image"},{"id":"create-a-custom-debezium-server-image-ex1","language":"dockerfile","code":"FROM quay.io/debezium/server:3.0.8.Final\n\n   USER root\n\n   COPY xdb.jar /debezium/lib\n   COPY xmlparserv2.jar /debezium/lib","section_id":"create-a-custom-debezium-server-image"},{"id":"create-a-custom-debezium-server-image-ex2","language":"bash","code":"cd ..\n   docker build -t dbz-xml xml\n   docker tag dbz-xml quay.io/debezium/server:3.0.8.Final\n   docker image save quay.io/debezium/server:3.0.8.Final -o dbz3.0.8-xml-linux-amd.tar","section_id":"create-a-custom-debezium-server-image"},{"id":"create-a-custom-debezium-server-image-ex3","language":"bash","code":"sudo k3s ctr images import dbz3.0.8-xml-linux-amd.tar all","section_id":"create-a-custom-debezium-server-image"},{"id":"configure-rdi-for-xmltype-support-ex0","language":"yaml","code":"sources:\n  oracle:\n    type: cdc\n    logging:\n      level: info\n    connection:\n      type: oracle\n      host: oracle\n      port: 1521\n      user: ${ORACLE_DB_USERNAME}\n      password: ${ORACLE_DB_PASSWORD}\n      database: ORCLCDB\n    advanced:\n      source:\n        database.pdb.name: ORCLPDB1\n        lob.enabled: true","section_id":"configure-rdi-for-xmltype-support"},{"id":"test-xmltype-support-ex0","language":"sql","code":"CREATE TABLE tab1 (\n  xmlid INT NOT NULL,\n  col1  SYS.XMLTYPE,\n  CONSTRAINT PK_tab1 PRIMARY KEY (xmlid)\n);\n\nDECLARE\n  v_xml   SYS.XMLTYPE;\n  v_doc   CLOB;\nBEGIN\n  -- XMLTYPE created from a CLOB\n  v_doc := '<?xml version=\"1.0\"?>' || Chr(10) || ' <TABLE_NAME>MY_TABLE</TABLE_NAME>';\n  v_xml := SYS.XMLTYPE.createXML(v_doc);\n\n  INSERT INTO tab1 (xmlid, col1) VALUES (1, v_xml);\n\n  -- XMLTYPE created from a query\n  SELECT SYS_XMLGEN(table_name)\n  INTO   v_xml\n  FROM   user_tables\n  WHERE  rownum = 1;\n\n  INSERT INTO tab1 (xmlid, col1) VALUES (2, v_xml);\n\n  COMMIT;\nEND;\n/\n\nALTER TABLE CHINOOK.TAB1 ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;","section_id":"test-xmltype-support"},{"id":"why-are-two-xstream-tablespaces-required-ex0","language":"mermaid","code":"graph TB\n    USERS[\"<b>USERS</b><br/><br/>- CUSTOMERS table<br/>- ORDERS table<br/>- PRODUCTS table<br/>- TRANSACTIONS table<br/><br/>(Application data)\"]","section_id":"why-are-two-xstream-tablespaces-required"},{"id":"why-are-two-xstream-tablespaces-required-ex1","language":"mermaid","code":"graph TB\n    ADM[\"<b>XSTREAM_ADM_TBS</b><br/><br/>- Capture process metadata<br/>- Outbound server config<br/>- Checkpoint information<br/>- Position tracking\"]\n    USER_TS[\"<b>XSTREAM_TBS</b><br/><br/>- LCR Queue change records<br/>- Buffered changes<br/>- Transaction state\"]","section_id":"why-are-two-xstream-tablespaces-required"},{"id":"why-are-two-xstream-tablespaces-required-ex2","language":"sql","code":"-- Application data\nSELECT * FROM MY_APP.CUSTOMERS;\n-- Returns: customer_id, name, address, balance, etc.","section_id":"why-are-two-xstream-tablespaces-required"},{"id":"why-are-two-xstream-tablespaces-required-ex3","language":"sql","code":"-- XStream metadata (in XSTREAM_ADM_TBS)\nSELECT capture_name, status, start_scn, checkpoint_scn \nFROM dba_capture;\n-- Returns: dbzxout_capture, ENABLED, 12345678, 12345690\n\n-- XStream queue (in XSTREAM_TBS)\nSELECT queue_name, enqueue_time, dequeue_time, state\nFROM dba_queues;\n-- Returns: dbzxout_queue, <timestamp>, <timestamp>, READY","section_id":"why-are-two-xstream-tablespaces-required"},{"id":"why-are-two-xstream-tablespaces-required-ex4","language":"sql","code":"-- Typical size: Large\nSELECT tablespace_name, \n       ROUND(SUM(bytes)/1024/1024/1024, 2) AS size_gb\nFROM dba_data_files\nWHERE tablespace_name = 'USERS'\nGROUP BY tablespace_name;\n\n-- Result: 50 GB, 100 GB, 500 GB, etc.","section_id":"why-are-two-xstream-tablespaces-required"},{"id":"why-are-two-xstream-tablespaces-required-ex5","language":"sql","code":"-- Typical size: Small\nSELECT tablespace_name, \n       ROUND(SUM(bytes)/1024/1024, 2) AS size_mb\nFROM dba_data_files\nWHERE tablespace_name IN ('XSTREAM_ADM_TBS', 'XSTREAM_TBS')\nGROUP BY tablespace_name;\n\n-- Result: \n-- XSTREAM_ADM_TBS: 100-500 MB\n-- XSTREAM_TBS: 500 MB - 2 GB (depends on queue size)","section_id":"why-are-two-xstream-tablespaces-required"},{"id":"what-is-the-xstream-outbound-server-ex0","language":"mermaid {width=\"100%\"}","code":"graph LR\n    RedoLogs[\"<b>Redo Logs</b>\"]\n    Capture[\"<b>Capture Process</b>\"]\n    Queue[\"<b>Queue</b>\"]\n    Outbound[\"<b>XStream Outbound<br/>Server</b>\"]\n    Consumer[\"<b>Debezium/RDI</b>\"]\n\n    RedoLogs --> Capture\n    Capture --> Queue\n    Queue --> Outbound\n    Outbound --> Consumer","section_id":"what-is-the-xstream-outbound-server"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/prepare-dbs/postgresql","title":"Prepare PostgreSQL for RDI","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/postgresql/","summary":"Prepare PostgreSQL databases to work with RDI","aliases":["/integrate/redis-data-integration/ingest/data-pipelines/prepare-dbs/postgresql/","/integrate/redis-data-integration/ingest/data-transformation/prepare-dbs/postgresql/","/integrate/redis-data-integration/ingest/installation/prepare-dbs/postgresql/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-10-19T20:38:06+03:00","page_type":"content","content_hash":"cc69b8635ccef0fab3b14e6dc741d2529137d5b08093a7ab2f7deb366f960110","sections":[{"id":"overview","title":"Overview","role":"overview","text":"PostgreSQL supports several\n[logical decoding plug-ins](https://wiki.postgresql.org/wiki/Logical_Decoding_Plugins)\nto enable CDC. If you don't want to use the native `pgoutput` logical replication stream support\nthen you must install your preferred plug-in into the PostgreSQL server. Once you have done this,\nyou must enable a replication slot, and configure a user with privileges to perform the replication.\n\nIf you are using a service like [Heroku Postgres](https://www.heroku.com/postgres) to host\nyour database then this might restrict the plug-ins you can use. If you can't use your preferred\nplug-in then could try the `pgoutput` decoder if you are using PostgreSQL 10 or above.\nIf this doesn't work for you then you won't be able to use RDI with your database.\n\nThe following checklist summarizes the steps to prepare a PostgreSQL\ndatabase for RDI, with links to the sections that explain the steps in\nfull detail. You may find it helpful to track your progress with the\nchecklist as you complete each step.\n\n[code example]"},{"id":"amazon-rds-for-postgresql","title":"Amazon RDS for PostgreSQL","role":"content","text":"Follow the steps below to enable CDC with [Amazon RDS for PostgreSQL](https://aws.amazon.com/rds/postgresql/):\n\n[code example]\n\n1.  <a id=\"1-set-the-instance-parameter-rdslogicalreplication-to-1\"></a>\n    Set the instance parameter `rds.logical_replication` to 1.\n\n1.  <a id=\"2-check-that-the-wal_level-parameter-is-set-to-logical\"></a>\n    Check that the `wal_level` parameter is set to `logical` by running the query `SHOW wal_level`\n    as the database RDS master user. The parameter might not have this value in multi-zone replication\n    setups. You can't change the value manually but it should change automatically when you set the\n    `rds.logical_replication` parameter to 1. If it doesn't change then you probably just need to\n    restart your database instance. You can restart manually or wait until a restart occurs\n    during your maintenance window.\n\n1.  <a id=\"3-set-the-debezium-pluginname-parameter-to-pgoutput\"></a>\n    Set the Debezium `plugin.name` parameter to `pgoutput`.\n\n1.  <a id=\"4-initiate-logical-replication-from-an-aws-account-that-has-the-rdsreplication-role\"></a>\n    Initiate logical replication from an AWS account that has the `rds_replication` role. The role grants\n    permissions to manage logical slots and to stream data using logical slots. By default, only the master user account on AWS has the `rds_replication` role on Amazon RDS, but if you have administrator privileges,\n    you can grant the role to other accounts using a query like the following:\n\n    [code example]\n\n    To enable accounts other than the master account to create an initial snapshot, you must grant `SELECT`\n    permission to the accounts on the tables to be captured. See the documentation about\n    [security for PostgreSQL logical replication](https://www.postgresql.org/docs/current/logical-replication-security.html)\n    for more information."},{"id":"azure-database-for-postgresql","title":"Azure Database for PostgreSQL","role":"content","text":"If you are using [Azure Database for PostgreSQL](https://azure.microsoft.com/en-us/services/postgresql/) you need to \nmanually set the `wal_level` parameter in the Azure portal for your PostgreSQL server. Go to the `server parameters` \nsection, search for `wal_level` and set it to logical. Then save and restart the server."},{"id":"install-the-logical-decoding-output-plug-in","title":"Install the logical decoding output plug-in","role":"setup","text":"As of PostgreSQL 9.4, the only way to read changes to the write-ahead-log is to\n[install a logical decoding output plug-in](https://debezium.io/documentation/reference/stable/postgres-plugins.html).\nThese plug-ins are written in C using PostgreSQL-specific APIs, as described in the\n[PostgreSQL documentation](https://www.postgresql.org/docs/current/logicaldecoding-output-plugin.html). \nThe PostgreSQL connector uses one of Debezium’s supported logical decoding\nplug-ins to receive change events from the database in either the default\n[`pgoutput`](https://github.com/postgres/postgres/blob/master/src/backend/replication/pgoutput/pgoutput.c) format (supplied with PostgreSQL) or the\n[`Protobuf`](https://github.com/protocolbuffers/protobuf) format. \nSee the\n[decoderbufs Protobuf plug-in documentation](https://github.com/debezium/postgres-decoderbufs)\nfor more details about how to compile it and also its requirements and limitations.\n\nFor simplicity, Debezium also provides a container image that compiles and installs the plug-ins\non top of the upstream PostgreSQL server image. Use this image as an example of the steps\ninvolved in the installation.\n\n> [!NOTE]\n>  The Debezium logical decoding plug-ins have been tested on Linux machines, but if you are\n> using Windows or other operating systems, the installation steps might be different from\n> those listed here."},{"id":"plug-in-differences","title":"Plug-in differences","role":"content","text":"Plug-ins don't all behave in exactly the same way. All of them refresh information about\nthe database schema when they detect that it has changed, but the `pgoutput` plug-in is\nmore \"eager\" than some other plug-ins to do this. For example, `pgoutput` will refresh\nwhen it detects a change to the default value of a column but other plug-ins won't\nnotice this until another, more significant change happens (such as adding a new table\ncolumn).\n\nThe Debezium project maintains a\n[Java class](https://github.com/debezium/debezium/blob/main/debezium-connector-postgres/src/test/java/io/debezium/connector/postgresql/DecoderDifferences.java) that tracks the known differences between plug-ins."},{"id":"configure-the-postgresql-server","title":"Configure the PostgreSQL server","role":"configuration","text":"If you want to use a logical decoding plug-in other than the default `pgoutput` then\nyou must first configure it in the `postgresql.conf` file. Set the `shared_preload_libraries`\nparameter to load your plug-in at startup. For example, to load the `decoderbufs`\nplug-in, you would add the following line:\n\n[code example]\n\nAdd the line below to configure the replication slot (for any plug-in).\nThis instructs the server to use logical decoding with the write-ahead log.\n\n[code example]\n\nYou can also set other PostgreSQL streaming replication parameters if you need them.\nFor example, you can use `max_wal_senders` and `max_replication_slots` to increase\nthe number of connectors that can access the sending server concurrently.\nThe\n[configuration parameters](https://www.postgresql.org/docs/current/runtime-config-replication.html#RUNTIME-CONFIG-REPLICATION-SENDER)\ndocumentation describes all the parameters you can use.\n\nIf you are using a `synchronous_commit` setting other than `on`, then you should set `wal_writer_delay`\nto a value of about 10 milliseconds to ensure a low latency for change events. If you don't set this then\nthe default value of about 200 milliseconds will apply.\n\n> [!NOTE]\n> This guide summarizes the operation of the PostgreSQL write-ahead log, but we strongly\n> recommend you consult the [PostgreSQL write-ahead log](https://www.postgresql.org/docs/current/wal-configuration.html)\n> documentation to get a better understanding."},{"id":"manage-wal-retention","title":"Manage WAL retention","role":"content","text":"Logical replication slots retain write-ahead log (WAL) needed by their readers,\nincluding during an RDI outage. Retained WAL can grow until it fills the disk.\nConfigured limits or slot invalidation can also prevent RDI from resuming.\nSee [Understand CDC offsets and source retention](https://redis.io/docs/latest/integrate/redis-data-integration/cdc-offsets-and-retention#postgresql-preserve-wal-and-slot-state)\nfor the conceptual explanation.\n\nChoose controls for your PostgreSQL version:\n\n| PostgreSQL version | Retention controls |\n| :-- | :-- |\n| 10–12 | `wal_keep_segments` specifies a **minimum** number of retained segments, not a maximum for a replication slot. These versions do not have `max_slot_wal_keep_size`. |\n| 13–17 | `wal_keep_size` replaces `wal_keep_segments` and still specifies a **minimum**. `max_slot_wal_keep_size` limits how much WAL a slot can retain at checkpoint time. Its default, `-1`, allows unlimited retention. |\n| 18 | The same size controls apply. `idle_replication_slot_timeout` can also invalidate an inactive slot at a checkpoint. Its default, `0`, disables this timeout. |\n\nSee the [PostgreSQL 13 release notes](https://www.postgresql.org/docs/release/13.0/)\nand [PostgreSQL 18 replication settings](https://www.postgresql.org/docs/18/runtime-config-replication.html)\nfor these version differences. Managed services can apply additional limits.\n\nCheck the settings available on your server:\n\n[code example]\n\nMonitor slot state, retained WAL, and free disk together. The following query works\nacross these versions. Status fields that a version does not provide return `NULL`.\n\n[code example]\n\n`retained_bytes` measures the log distance from the slot's oldest required position;\nit is not the total disk usage of the WAL directory. Size retention for peak writes\nand the interruption you need to tolerate. A small `wal_keep_size` does not cap\nslot storage. A finite slot limit can protect disk space at the cost of losing\nrequired history. Increasing the limit after that history is removed does not\nrestore it. Do not drop or recreate an RDI slot as a routine disk-cleanup step.\n\nThe slot's `confirmed_flush_lsn` is the position the collector has acknowledged.\nPostgreSQL starts logical streaming at the requested position or\n`confirmed_flush_lsn`, whichever is later. Moving only RDI's saved offset backward\ndoes not rewind the slot, even if older WAL remains on disk. Keeping unread history\nfor normal resume is different from replaying already acknowledged changes.\nSee the [PostgreSQL replication protocol](https://www.postgresql.org/docs/17/protocol-replication.html)."},{"id":"set-up-permissions","title":"Set up permissions","role":"content","text":"The Debezium connector needs a database user that has the REPLICATION and LOGIN roles so that it\ncan perform replications. By default, a superuser has these roles but for security reasons, you\nshould give the minimum necessary permissions to the Debezium user rather than full superuser\npermissions.\n\nIf you have administrator privileges then you can create a role for your Debezium user\nusing a query like the following. Note that these are the *minimum* permissions the user\nneeds to perform replications, but you might also need to grant other permissions.\n\n[code example]"},{"id":"check-replica-identity-and-choose-a-publication-mode","title":"Check replica identity and choose a publication mode","role":"content","text":"Before you start RDI, check the tables that RDI will capture and choose how PostgreSQL\nshould publish their changes.\n\nPostgreSQL needs a [replica identity](https://www.postgresql.org/docs/current/logical-replication-publication.html)\nto identify the old row when it publishes an `UPDATE` or `DELETE`. By default, PostgreSQL\nuses the table's primary key. A table without a primary key has no usable replica identity\nunless you configure one. Inserts do not need a replica identity.\n\nRun the following query while connected to the database that RDI uses. You do not put the\ndatabase name in the query; select that database in your SQL editor or connection settings.\nThe query checks all non-system schemas. To check only one schema, uncomment the marked line\nand replace `<schema_name>` with your schema name.\n\n[code example]\n\nIf this query returns a table that will be published, add a primary key that is not `DEFERRABLE` where possible. A deferrable primary key cannot serve as the replica identity.\nYou can also configure a suitable unique index as the replica identity:\n\n[code example]\n\nAs a fallback, you can record the full old row in the write-ahead log:\n\n[code example]\n\n`REPLICA IDENTITY FULL` can increase write-ahead log volume and reduce performance.\nUse it when no suitable key is available or when RDI needs the full old row, such as\n[denormalizing child tables with nesting](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/data-denormalization#using-nesting).\nThe `keys` setting in an RDI table definition creates a key for the Redis record.\nIt does not create a PostgreSQL primary key or replica identity."},{"id":"understand-which-row-values-are-available","title":"Understand which row values are available","role":"content","text":"Replica identity controls the old row values available to logical decoding for updates\nand deletes. With the default identity, PostgreSQL uses the primary key; it does not\nprovide a complete old row. `REPLICA IDENTITY FULL` records all old column values.\n\nUpdate events are not generally limited to changed fields. However, PostgreSQL can omit\nunchanged large values stored through The Oversized-Attribute Storage Technique (TOAST)\nwhen they are not part of the replica identity. Debezium represents unavailable values\nwith a placeholder. With `FULL`, these values are available in the event's old and new row\nimages. See [Debezium's TOAST guidance](https://debezium.io/documentation/reference/3.0/connectors/postgresql.html#postgresql-toasted-values).\n\nFor example, nesting a child record requires its `parent_key` to identify the parent\nRedis document, including when the child is deleted. Set `REPLICA IDENTITY FULL` on\nPostgreSQL child tables as required by the [nesting instructions](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/data-denormalization#using-nesting)."},{"id":"choose-a-publication-mode","title":"Choose a publication mode","role":"content","text":"The `publication.autocreate.mode` setting controls how RDI and Debezium manage the\nPostgreSQL publication:\n\n| Mode | Behavior | Recommended use |\n| --- | --- | --- |\n| **`all_tables` (default)** | Creates a `FOR ALL TABLES` publication when the publication does not exist. If it already exists, RDI uses the existing publication. | Use when all tables in the database should be published, after checking their replica identities. |\n| `filtered` | Creates or updates the publication to contain the tables configured for the RDI source. | Recommended when RDI captures selected tables. RDI manages the publication when the pipeline is redeployed with a changed table filter. |\n| `disabled` | Does not create or change the publication. The publication must already exist. | Use when a database administrator manages the publication. |\n\nThe `all_tables` mode can publish changes for tables that are not listed in the RDI\npipeline, including tables created in the future. If an application later updates or\ndeletes a row in a published table without a replica identity, PostgreSQL can reject\nthat application write. For this reason,\n`filtered` is the recommended mode for a pipeline that captures only selected tables.\n\nThe `filtered` mode requires permission to create the publication and ownership rights on\nthe tables it manages. Use a separate publication for each source with different table\nfilters, so one source does not change another source's publication.\n\nFor a customer-managed publication, create an explicit table list and disable automatic\npublication management. Replace the example table names with the tables you want to capture:\n\n[code example]\n\nThen set the following properties under the PostgreSQL source in your RDI `config.yaml`:\n\n[code example]\n\nFor RDI on Redis Cloud, enter these properties under **Collector properties** in the\nsource configuration. See [Configure PostgreSQL publications on Redis Cloud](https://redis.io/docs/latest/operate/rc/rdi/setup#postgresql-publications).\nWith `disabled`, update the publication's table list yourself when changing the source's\nselected tables. Selecting a table in RDI does not add it to a customer-managed publication."},{"id":"replace-an-existing-all-tables-publication","title":"Replace an existing all-tables publication","role":"content","text":"Changing the RDI table selection does not narrow a `FOR ALL TABLES` publication.\nChanging `publication.autocreate.mode` to `filtered` also does not convert it:\nPostgreSQL rejects attempts to set, add, or remove individual tables in that publication.\n\nHave your database administrator plan the change with the owners of every consumer of\nthe publication:\n\n1. Check existing publications in the source database:\n\n    [code example]\n\n1. Identify every consumer of the broad publication and coordinate a maintenance window.\n1. Stop the affected RDI sources before replacing the publication.\n1. Replace the broad publication with an explicit table-list publication. You can recreate\n   it under the same name, or create a new publication and update each consumer's publication\n   name. For RDI, use `publication.autocreate.mode: disabled` for an administrator-managed\n   publication. On Redis Cloud, contact support if the running source's collector properties\n   cannot be edited in the console.\n1. Remove the old broad publication only after coordinating all consumers. Leaving it in\n   place still subjects its tables to replica-identity requirements, even if RDI uses a\n   different publication or is stopped.\n1. Resume the affected sources and verify inserts, updates, and deletes reach Redis.\n\nKeep the replication slots and saved RDI offsets during this change. Do not reset the\npipeline or drop its slot as a publication repair. If other consumers still need the broad\npublication, configure replica identity on its affected tables instead of removing it."},{"id":"postgresql-publication-error-faq","title":"PostgreSQL publication error FAQ","role":"content","text":"#### I get this PostgreSQL error. What should I do?\n\nYou may see the following error when an application updates a published table without a\nreplica identity:\n\n[code example]\n\nCheck whether the table should be captured by RDI. If it should, add a primary key or\nconfigure a suitable replica identity. As a fallback, run:\n\n[code example]\n\nIf the table should not be captured, remove it from an explicit table-list publication or\nuse `filtered` with the required permissions. For an existing `FOR ALL TABLES` publication,\nfollow [Replace an existing all-tables publication](#replace-an-existing-all-tables-publication).\nCheck all publications containing the table: another publication that publishes updates or\ndeletes can still require replica identity. After changing RDI configuration, redeploy the\npipeline so RDI applies it."},{"id":"set-privileges-for-debezium-to-create-postgresql-publications-with-pgoutput","title":"Set privileges for Debezium to create PostgreSQL publications with `pgoutput`","role":"content","text":"The Debezium user needs specific permissions to work with the `pgoutput` plug-in.\nThe plug-in captures change events from the\n[*publications*](https://www.postgresql.org/docs/current/logical-replication-publication.html)\nthat PostgreSQL produces for your chosen source tables. A publication contains change events from\none or more tables that are filtered using criteria from a *publication specification*.\n\nIf you have administrator privileges, you can create the publication specification\nmanually or you can grant the Debezium user the privileges to create the specification\nautomatically. The required privileges are:\n\n-   Replication privileges in the database to add the table to a publication.\n-   `CREATE` privileges on the database to add publications.\n-   `SELECT` privileges on the tables to copy the initial table data. Table owners\n    automatically have `SELECT` permission for the table.\n\nTo add a table to a publication, the user must be an owner of the table. However, in\nthis case, the source table already exists, so you must use a PostgreSQL replication\ngroup to share ownership between the Debezium user and the original owner. Configure\nthe replication group using the following commands:\n\n[code example]\n\n1.  <a id=\"1-create-the-replication-group\"></a>\n    Create the replication group (the name `replication_group` here is\n    just an example):\n\n    [code example]\n\n1.  <a id=\"2-add-the-original-owner-of-the-table-to-the-group\"></a>\n    Add the original owner of the table to the group:\n\n    [code example]\n\n1.  <a id=\"3-add-the-debezium-replication-user-to-the-group\"></a>\n    Add the Debezium replication user to the group:\n\n    [code example]\n\n1.  <a id=\"4-transfer-ownership-of-the-table-to-the-replication-group\"></a>\n    Transfer ownership of the table to `replication_group`:\n\n    [code example]\n\nIf you want Debezium to create or update a publication for the tables configured in the RDI\nsource, set `publication.autocreate.mode` to `filtered`. See the\n[Debezium documentation for `publication.autocreate.mode`](https://debezium.io/documentation/reference/stable/connectors/postgresql.html#postgresql-publication-autocreate-mode)\nto learn more about this setting. If a database administrator manages an existing\npublication, use `disabled` instead, as described in [Choose a publication mode](#choose-a-publication-mode)."},{"id":"configure-postgresql-for-replication-with-the-debezium-connector-host","title":"Configure PostgreSQL for replication with the Debezium connector host","role":"configuration","text":"You must configure the database to allow replication with the host that runs\nthe PostgreSQL Debezium connector. To do this, add an entry to the\nhost-based authentication file, `pg_hba.conf`, for each client that needs to\nuse replication. For example, to enable replication for `<youruser>` locally,\non the server machine, you would add a line like the following:\n\n[code example]\n\nTo allow `<youruser>` on localhost to receive replication changes using IPV4,\nadd the line:\n\n[code example]\n\nTo allow `<youruser>` on localhost to receive replication changes using IPV6,\nadd the line:\n\n[code example]\n\nFind out more from the PostgreSQL pages about\n[`pg_hba.conf`](https://www.postgresql.org/docs/10/auth-pg-hba-conf.html)\nand\n[network address types](https://www.postgresql.org/docs/current/datatype-net-types.html)."},{"id":"supported-postgresql-topologies","title":"Supported PostgreSQL topologies","role":"compatibility","text":"You can use the Debezium PostgreSQL connector with a standalone PostgreSQL server or\nwith a cluster of servers.\nFor versions 12 and below, PostgreSQL supports logical replication slots on only primary servers.\nThis means that Debezium can only connect to a primary server for CDC and the connection will\nstop if this server fails. If the same server is promoted to primary when service resumes\nthen you can simply restart the Debezium connector. However, if a different server is\npromoted to primary, then you must reconfigure Debezium to use the new server\nbefore restarting. Also, make sure the new server has the correct plug-in and configuration\nfor Debezium."}],"examples":[{"id":"overview-ex0","language":"checklist {id=\"postgreslist\"}","code":"- [ ] [Install the logical decoding output plug-in](#install-the-logical-decoding-output-plug-in)\n- [ ] [Configure the PostgreSQL server](#configure-the-postgresql-server)\n- [ ] [Set up permissions](#set-up-permissions)\n- [ ] [Check replica identity and choose a publication mode](#check-replica-identity-and-choose-a-publication-mode)\n- [ ] [Set privileges for Debezium to create PostgreSQL publications with pgoutput](#set-privileges-for-debezium-to-create-postgresql-publications-with-pgoutput)\n- [ ] [Configure PostgreSQL for replication with the Debezium connector host](#configure-postgresql-for-replication-with-the-debezium-connector-host)","section_id":"overview"},{"id":"amazon-rds-for-postgresql-ex0","language":"checklist {id=\"postgresawslist\" nointeractive=\"true\" }","code":"- [ ] [Set the instance parameter rds.logical_replication to 1](#1-set-the-instance-parameter-rdslogicalreplication-to-1)\n- [ ] [Check that the wal_level parameter is set to logical](#2-check-that-the-wal_level-parameter-is-set-to-logical)\n- [ ] [Set the Debezium plugin.name parameter to pgoutput](#3-set-the-debezium-pluginname-parameter-to-pgoutput)\n- [ ] [Initiate logical replication from an AWS account that has the rds_replication role](#4-initiate-logical-replication-from-an-aws-account-that-has-the-rdsreplication-role)","section_id":"amazon-rds-for-postgresql"},{"id":"amazon-rds-for-postgresql-ex1","language":"sql","code":"GRANT rds_replication TO <my_user>","section_id":"amazon-rds-for-postgresql"},{"id":"configure-the-postgresql-server-ex0","language":"plaintext","code":"# MODULES\nshared_preload_libraries = 'decoderbufs'","section_id":"configure-the-postgresql-server"},{"id":"configure-the-postgresql-server-ex1","language":"plaintext","code":"# REPLICATION\nwal_level = logical","section_id":"configure-the-postgresql-server"},{"id":"manage-wal-retention-ex0","language":"sql","code":"SELECT name, setting, unit\nFROM pg_settings\nWHERE name IN (\n    'wal_keep_segments', 'wal_keep_size',\n    'max_slot_wal_keep_size', 'idle_replication_slot_timeout'\n);","section_id":"manage-wal-retention"},{"id":"manage-wal-retention-ex1","language":"sql","code":"SELECT slot_name, active, restart_lsn, confirmed_flush_lsn,\n       pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) AS retained_bytes,\n       to_jsonb(s)->>'wal_status' AS wal_status,\n       to_jsonb(s)->>'invalidation_reason' AS invalidation_reason\nFROM pg_replication_slots AS s\nWHERE slot_type = 'logical';","section_id":"manage-wal-retention"},{"id":"set-up-permissions-ex0","language":"sql","code":"CREATE ROLE <name> REPLICATION LOGIN;","section_id":"set-up-permissions"},{"id":"check-replica-identity-and-choose-a-publication-mode-ex0","language":"sql","code":"SELECT n.nspname AS schema_name,\n       c.relname AS table_name,\n       CASE c.relreplident\n           WHEN 'd' THEN 'default'\n           WHEN 'n' THEN 'nothing'\n           WHEN 'f' THEN 'full'\n           WHEN 'i' THEN 'index'\n       END AS replica_identity\nFROM pg_class AS c\nJOIN pg_namespace AS n ON n.oid = c.relnamespace\nWHERE c.relkind = 'r'\n  AND n.nspname NOT IN ('pg_catalog', 'information_schema')\n  -- Optional: uncomment and replace <schema_name> to check one schema only.\n  -- AND n.nspname = '<schema_name>'\n  AND (\n      c.relreplident = 'n'\n      OR (\n          c.relreplident IN ('d', 'i')\n          AND NOT EXISTS (\n              SELECT 1\n              FROM pg_index AS i\n              WHERE i.indrelid = c.oid\n                AND i.indislive\n                AND i.indisvalid\n                AND i.indisunique\n                AND i.indimmediate\n                AND i.indpred IS NULL\n                AND CASE WHEN c.relreplident = 'd'\n                         THEN i.indisprimary\n                         ELSE i.indisreplident\n                    END\n          )\n      )\n  )\nORDER BY n.nspname, c.relname;","section_id":"check-replica-identity-and-choose-a-publication-mode"},{"id":"check-replica-identity-and-choose-a-publication-mode-ex1","language":"sql","code":"ALTER TABLE <table_name> REPLICA IDENTITY USING INDEX <unique_index_name>;","section_id":"check-replica-identity-and-choose-a-publication-mode"},{"id":"check-replica-identity-and-choose-a-publication-mode-ex2","language":"sql","code":"ALTER TABLE <table_name> REPLICA IDENTITY FULL;","section_id":"check-replica-identity-and-choose-a-publication-mode"},{"id":"choose-a-publication-mode-ex0","language":"sql","code":"CREATE PUBLICATION rdi_publication\nFOR TABLE public.customers, public.orders;","section_id":"choose-a-publication-mode"},{"id":"choose-a-publication-mode-ex1","language":"yaml","code":"sources:\n  <source_name>:\n    advanced:\n      source:\n        publication.name: rdi_publication\n        publication.autocreate.mode: disabled","section_id":"choose-a-publication-mode"},{"id":"replace-an-existing-all-tables-publication-ex0","language":"sql","code":"SELECT pubname, puballtables, pubupdate, pubdelete\n    FROM pg_publication;","section_id":"replace-an-existing-all-tables-publication"},{"id":"postgresql-publication-error-faq-ex0","language":"text","code":"SQLSTATE 55000\nERROR: cannot update table \"example_table\" because it does not have a replica identity and publishes updates\nHINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE.","section_id":"postgresql-publication-error-faq"},{"id":"postgresql-publication-error-faq-ex1","language":"sql","code":"ALTER TABLE <table_name> REPLICA IDENTITY FULL;","section_id":"postgresql-publication-error-faq"},{"id":"set-privileges-for-debezium-to-create-postgresql-publications-with-pgoutput-ex0","language":"checklist {id=\"postgrespgoutputlist\" nointeractive=\"true\" }","code":"- [ ] [Create the replication group](#1-create-the-replication-group)\n- [ ] [Add the original owner of the table to the group](#2-add-the-original-owner-of-the-table-to-the-group)\n- [ ] [Add the Debezium replication user to the group](#3-add-the-debezium-replication-user-to-the-group)\n- [ ] [Transfer ownership of the table to the replication group](#4-transfer-ownership-of-the-table-to-the-replication-group)","section_id":"set-privileges-for-debezium-to-create-postgresql-publications-with-pgoutput"},{"id":"set-privileges-for-debezium-to-create-postgresql-publications-with-pgoutput-ex1","language":"sql","code":"CREATE ROLE replication_group;","section_id":"set-privileges-for-debezium-to-create-postgresql-publications-with-pgoutput"},{"id":"set-privileges-for-debezium-to-create-postgresql-publications-with-pgoutput-ex2","language":"sql","code":"GRANT replication_group TO original_owner;","section_id":"set-privileges-for-debezium-to-create-postgresql-publications-with-pgoutput"},{"id":"set-privileges-for-debezium-to-create-postgresql-publications-with-pgoutput-ex3","language":"sql","code":"GRANT replication_group TO replication_user;","section_id":"set-privileges-for-debezium-to-create-postgresql-publications-with-pgoutput"},{"id":"set-privileges-for-debezium-to-create-postgresql-publications-with-pgoutput-ex4","language":"sql","code":"ALTER TABLE table_name OWNER TO replication_group;","section_id":"set-privileges-for-debezium-to-create-postgresql-publications-with-pgoutput"},{"id":"configure-postgresql-for-replication-with-the-debezium-connector-host-ex0","language":"plaintext","code":"local   replication     <youruser>                          trust","section_id":"configure-postgresql-for-replication-with-the-debezium-connector-host"},{"id":"configure-postgresql-for-replication-with-the-debezium-connector-host-ex1","language":"plaintext","code":"host    replication     <youruser>  127.0.0.1/32            trust","section_id":"configure-postgresql-for-replication-with-the-debezium-connector-host"},{"id":"configure-postgresql-for-replication-with-the-debezium-connector-host-ex2","language":"plaintext","code":"host    replication     <youruser>  ::1/128                 trust","section_id":"configure-postgresql-for-replication-with-the-debezium-connector-host"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/prepare-dbs/snowflake","title":"Prepare Snowflake for RDI","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/snowflake/","summary":"Prepare Snowflake databases to work with RDI","tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-18T16:39:32+01:00","page_type":"content","content_hash":"89016adad30e6a72d199b15f7b9f25a72bfba11a6c89f74d5b0dc30e907d3977","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This guide describes the steps required to prepare a Snowflake database as a source for Redis Data Integration (RDI) pipelines.\n\nDuring both the [snapshot](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines#pipeline-lifecycle) and\n[Change data capture (CDC)](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines#pipeline-lifecycle)\nphases, RDI uses [Snowflake Streams](https://docs.snowflake.com/en/user-guide/streams) to read data from the monitored\ntables. For the initial snapshot, RDI creates the stream with `SHOW_INITIAL_ROWS = TRUE` so it can read the current\ntable contents before continuing with ongoing CDC. RDI automatically creates and manages the required streams."},{"id":"setup","title":"Setup","role":"setup","text":"The following checklist shows the steps to prepare a Snowflake database for RDI,\nwith links to the sections that explain the steps in full detail.\nYou may find it helpful to track your progress with the checklist as you\ncomplete each step.\n\n> [!NOTE]\n> Snowflake is only supported with RDI deployed on Kubernetes/Helm. RDI VM mode does not support Snowflake as a source database.\n\n[code example]"},{"id":"1-set-up-snowflake-permissions","title":"1. Set up Snowflake permissions","role":"content","text":"The following are the minimum runtime permissions for the RDI role to read the source tables and create the Snowflake\nobjects RDI uses for CDC:\n\n- `USAGE`, `OPERATE` on the warehouse used for RDI reads\n- `USAGE` on the source database and source schema\n- `SELECT` on the source tables\n- `USAGE` on the CDC schema used by RDI\n- `CREATE STREAM`, `CREATE TABLE` on the CDC schema used by RDI\n\nIf you configure `cdcDatabase` and `cdcSchema`, grant the CDC permissions there. Otherwise, grant them in the source\nschema. If your Snowflake setup requires it, also grant any additional cross-database privileges needed for the CDC\nschema to reference the source tables.\n\n> [!NOTE]\n> RDI manages the Snowflake streams it uses for snapshot and CDC. The collector creates the stream in the configured CDC\n> schema and later issues `CREATE OR REPLACE STREAM` statements to keep the stream aligned with the expected offset, so\n> the RDI role must be able to create and own those stream objects in the CDC schema.\n>\n> There is one stricter bootstrap requirement for the first stream created on a source table: if Snowflake change\n> tracking is not already enabled on that table, only the table owner can create that initial stream. If the source\n> tables are not owned by the RDI role, ask a Snowflake administrator or table owner to enable change tracking first:\n>\n> [code example]\n\nGrant the required permissions to your RDI user:\n\n[code example]\n\nIf you use centralized grant management, you can also add future grants in the CDC schema so newly created tables and\nstreams automatically receive the desired privileges. These grants are optional and are not part of the minimum runtime\npermissions:\n\n[code example]"},{"id":"2-configure-authentication","title":"2. Configure authentication","role":"content","text":"RDI supports two authentication methods for Snowflake. You must configure one of these methods."},{"id":"password-authentication","title":"Password authentication","role":"content","text":"Use standard username and password credentials. Store these securely using Kubernetes secrets (see step 3).\n\n> [!NOTE]\n> Many Snowflake accounts require MFA for password-based sign-ins. If you want to use password authentication for RDI,\n> configure the Snowflake user as a service user that is allowed to authenticate non-interactively. Otherwise, use\n> private key authentication instead. For more information, see the Snowflake\n> [MFA rollout documentation](https://docs.snowflake.com/en/user-guide/security-mfa-rollout)."},{"id":"private-key-authentication","title":"Private key authentication","role":"content","text":"For enhanced security, use key-pair authentication:\n\n1. Generate a private key:\n\n    [code example]\n\n1. Generate the public key:\n\n    [code example]\n\n1. Register the public key with your Snowflake user:\n\n    [code example]"},{"id":"3-set-up-secrets","title":"3. Set up secrets","role":"content","text":"Before deploying the RDI pipeline, configure the necessary secrets with\n[`redis-di set-secret`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-set-secret).\nPass the source name with `--db` (the source configured in the following example is named `snowflake` for demonstration purposes)."},{"id":"password-authentication-1","title":"Password authentication","role":"content","text":"[code example]"},{"id":"private-key-authentication-1","title":"Private key authentication","role":"content","text":"Omit the password and set the private key from its file instead:\n\n[code example]\n\nIf the `.p8` file is protected by a passphrase, you must also set that passphrase:\n\n[code example]\n\nRDI stores the private key in the source's TLS secret, `snowflake-db-ssl`, and the RIOTX\ncollector reads it from there. See\n[Set secrets](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/deploy#set-secrets)\nfor the full secret reference, including how to create these secrets with `kubectl` instead."},{"id":"4-configure-rdi-for-snowflake","title":"4. Configure RDI for Snowflake","role":"content","text":"Use the following example configuration in your `config.yaml` file:\n\n[code example]\n\n> [!NOTE]\n> Snowflake uses one configured `database` and one or more source-level `schemas`. In the `tables` section, specify each\n> table as `SCHEMA.table`. Even when you configure only one schema, explicit `SCHEMA.table` names are recommended for\n> clarity."},{"id":"snowflake-connection-properties","title":"Snowflake connection properties","role":"content","text":"| Property      | Type   | Required | Description                                                    |\n|---------------|--------|----------|----------------------------------------------------------------|\n| `type`        | string | Yes      | Must be `\"snowflake\"`                                          |\n| `url`         | string | Yes      | JDBC URL: `jdbc:snowflake://<account>.snowflakecomputing.com/` |\n| `user`        | string | Yes      | Snowflake user                                                 |\n| `password`    | string | No*      | Snowflake password                                             |\n| `database`    | string | Yes      | Snowflake database name                                        |\n| `warehouse`   | string | Yes      | Snowflake warehouse name                                       |\n| `role`        | string | No       | Snowflake role name                                            |\n| `cdcDatabase` | string | No       | Database for CDC streams (if different from source)            |\n| `cdcSchema`   | string | No       | Schema for CDC streams (if different from source)              |\n\n* Either `password` or private key authentication is required. See [Configure authentication](#2-configure-authentication) for details."},{"id":"snowflake-source-properties","title":"Snowflake source properties","role":"content","text":"| Property   | Type   | Required | Description                                                      |\n|------------|--------|----------|------------------------------------------------------------------|\n| `schemas`  | array  | Yes      | Schema names to capture from                                     |\n| `tables`   | object | Yes      | Tables to capture, keyed as `SCHEMA.table`                       |"},{"id":"advanced-configuration-options","title":"Advanced configuration options","role":"content","text":"Configure under `sources.<name>.advanced.riotx`:\n\n| Property       | Type    | Default     | Description                                  |\n|----------------|---------|-------------|----------------------------------------------|\n| `poll`         | string  | `\"30s\"`     | Polling interval for stream changes          |\n| `snapshot`     | string  | `\"INITIAL\"` | Snapshot mode: `INITIAL` or `NEVER`          |\n| `streamPrefix` | string  | `\"data:\"`   | Prefix for the Redis stream written by RDI   |\n| `streamLimit`  | integer | -           | Maximum stream length (XTRIM MAXLEN)         |\n| `keyColumns`   | array   | -           | Stable source columns to use as message keys |\n| `clearOffset`  | boolean | `false`     | Clear existing offset on start               |\n| `count`        | integer | `0`         | Limit records per poll (0 = unlimited)       |\n\nFor reliable update and delete handling, define `keyColumns` with a stable business key or surrogate key when possible."},{"id":"troubleshooting","title":"Troubleshooting","role":"errors","text":""},{"id":"connection-issues","title":"Connection issues","role":"content","text":"**Error: \"Failed to connect to Snowflake\"**\n\n- Verify the account URL is correct (format: `<account>.snowflakecomputing.com`)\n- Check network connectivity to Snowflake\n- Verify the warehouse is running and accessible\n- Check firewall rules allow outbound HTTPS (port 443)\n\n**Error: \"Authentication failed\"**\n\n- For password auth: verify username and password are correct\n- For key-pair auth: verify the private key matches the public key registered in Snowflake\n- Ensure the user has appropriate permissions\n\n**Error: \"Warehouse not found\"**\n\n- Verify the warehouse name is correct\n- Ensure the user has USAGE permission on the warehouse\n\n**Error: \"Network policy is required\"**\n\nIf the collector logs show an error like the following:\n\n[code example]\n\nYour Snowflake account enforces a network policy, so you must whitelist the RDI egress IP addresses in Snowflake.\n\nThe following example creates a network policy and applies it to the RDI user. Replace the example IP addresses with your actual RDI egress IPs and replace `\"USERNAME\"` with your RDI user:\n\n[code example]"},{"id":"cdc-issues","title":"CDC issues","role":"content","text":"**No data appearing in Redis**\n\n1. Verify Snowflake Streams exist in the CDC schema:\n\n    [code example]\n\n1. Check the polling interval configuration\n1. Verify Redis connection is working\n1. Check the collector logs:\n\n    [code example]\n\n**Stale or missing changes**\n\n- Snowflake Streams depend on Snowflake change tracking and retention settings\n- If the collector was offline longer than the available retention window, changes may be lost\n- Consider using `clearOffset: true` to restart from current state"},{"id":"performance-tuning","title":"Performance tuning","role":"performance","text":"**High Snowflake warehouse usage**\n\n- Increase `poll` interval (e.g., `\"60s\"` or `\"120s\"`)\n- Use a dedicated warehouse for CDC operations\n- Each poll first calls Snowflake's `SYSTEM$STREAM_HAS_DATA` function to check whether the stream has new data. This\n  check does not start the warehouse; warehouse compute starts only when RDI reads rows from the stream.\n\n**Redis memory concerns**\n\n- Set `streamLimit` to cap stream length\n- Use `count` to limit records per poll batch\n\n**Initial snapshot too slow**\n\n- Use `snapshot: \"NEVER\"` to skip initial snapshot\n- Pre-load data using other methods if needed"},{"id":"enable-debug-logging","title":"Enable debug logging","role":"content","text":"Enable debug logging in the source configuration:\n\n[code example]\n\nView collector logs:\n\n[code example]"},{"id":"5-configuration-is-complete","title":"5. Configuration is complete","role":"content","text":"Once you have followed the steps above, your Snowflake database is ready for RDI to use."},{"id":"see-also","title":"See also","role":"related","text":"- [Snowflake Streams Documentation](https://docs.snowflake.com/en/user-guide/streams)\n- [Snowflake Key Pair Authentication](https://docs.snowflake.com/en/user-guide/key-pair-auth)\n- [Snowflake MFA rollout documentation](https://docs.snowflake.com/en/user-guide/security-mfa-rollout)\n- [RDI Deployment Guide](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/deploy)"}],"examples":[{"id":"setup-ex0","language":"checklist {id=\"snowflakelist\"}","code":"- [ ] [Set up Snowflake permissions](#1-set-up-snowflake-permissions)\n- [ ] [Configure authentication](#2-configure-authentication)\n- [ ] [Set up secrets](#3-set-up-secrets)\n- [ ] [Configure RDI for Snowflake](#4-configure-rdi-for-snowflake)","section_id":"setup"},{"id":"1-set-up-snowflake-permissions-ex0","language":"sql","code":"> ALTER TABLE MYDB.PUBLIC.customers SET CHANGE_TRACKING = TRUE;\n> ALTER TABLE MYDB.PUBLIC.orders SET CHANGE_TRACKING = TRUE;\n>","section_id":"1-set-up-snowflake-permissions"},{"id":"1-set-up-snowflake-permissions-ex1","language":"sql","code":"-- Grant usage on the warehouse\nGRANT USAGE, OPERATE ON WAREHOUSE COMPUTE_WH TO ROLE rdi_role;\n\n-- Grant usage on the source database and schema\nGRANT USAGE ON DATABASE MYDB TO ROLE rdi_role;\nGRANT USAGE ON SCHEMA MYDB.PUBLIC TO ROLE rdi_role;\n\n-- Grant SELECT on tables to capture\nGRANT SELECT ON TABLE MYDB.PUBLIC.customers TO ROLE rdi_role;\nGRANT SELECT ON TABLE MYDB.PUBLIC.orders TO ROLE rdi_role;\n\n-- Grant permissions on the schema RDI uses for CDC objects\nGRANT USAGE ON SCHEMA MYDB.RDI_CDC TO ROLE rdi_role;\nGRANT CREATE STREAM, CREATE TABLE ON SCHEMA MYDB.RDI_CDC TO ROLE rdi_role;\n\n-- Assign the role to your RDI user\nGRANT ROLE rdi_role TO USER rdi_user;","section_id":"1-set-up-snowflake-permissions"},{"id":"1-set-up-snowflake-permissions-ex2","language":"sql","code":"GRANT SELECT ON FUTURE TABLES IN SCHEMA MYDB.RDI_CDC TO ROLE rdi_role;\nGRANT SELECT ON FUTURE STREAMS IN SCHEMA MYDB.RDI_CDC TO ROLE rdi_role;","section_id":"1-set-up-snowflake-permissions"},{"id":"private-key-authentication-ex0","language":"bash","code":"openssl genrsa 2048 | openssl pkcs8 -topk8 -inform PEM -out rsa_key.p8 -nocrypt","section_id":"private-key-authentication"},{"id":"private-key-authentication-ex1","language":"bash","code":"openssl rsa -in rsa_key.p8 -pubout -out rsa_key.pub","section_id":"private-key-authentication"},{"id":"private-key-authentication-ex2","language":"sql","code":"ALTER USER rdi_user SET RSA_PUBLIC_KEY='<public_key_content>';","section_id":"private-key-authentication"},{"id":"password-authentication-1-ex0","language":"bash","code":"redis-di set-secret USERNAME --db snowflake your_username\nredis-di set-secret PASSWORD --db snowflake your_password","section_id":"password-authentication-1"},{"id":"private-key-authentication-1-ex0","language":"bash","code":"redis-di set-secret USERNAME --db snowflake your_username\nredis-di set-secret KEY --db snowflake --file /path/to/rsa_key.p8","section_id":"private-key-authentication-1"},{"id":"private-key-authentication-1-ex1","language":"bash","code":"redis-di set-secret KEY_PASSWORD --db snowflake your_passphrase","section_id":"private-key-authentication-1"},{"id":"4-configure-rdi-for-snowflake-ex0","language":"yaml","code":"sources:\n  snowflake:\n    type: riotx\n    connection:\n      type: snowflake\n      url: \"jdbc:snowflake://myaccount.snowflakecomputing.com/\"\n      user: \"${SNOWFLAKE_DB_USERNAME}\"\n      password: \"${SNOWFLAKE_DB_PASSWORD}\"  # Omit for key-pair auth\n      database: \"MYDB\"\n      warehouse: \"COMPUTE_WH\"\n      # role: \"RDI_ROLE\"                 # Optional: Snowflake role\n      # cdcDatabase: \"CDC_DB\"            # Optional: Separate database for CDC streams\n      # cdcSchema: \"CDC_SCHEMA\"          # Optional: Separate schema for CDC streams\n    schemas:\n      - PUBLIC\n    tables:\n      PUBLIC.customers: {}\n      PUBLIC.orders: {}\n    advanced:\n      riotx:\n        poll: \"30s\"\n        snapshot: \"INITIAL\"              # Or \"NEVER\" to skip initial snapshot\n        # streamPrefix: \"data:\"          # Optional: Redis stream prefix\n        # streamLimit: 100000            # Optional: Max stream length\n        # keyColumns:                    # Recommended: stable key columns\n        #   - \"id\"\n        # clearOffset: false             # Optional: Clear offset on start\n\ntargets:\n  target:\n    connection:\n      type: redis\n      host: ${TARGET_DB_HOST}\n      port: ${TARGET_DB_PORT}\n      user: ${TARGET_DB_USERNAME}\n      password: ${TARGET_DB_PASSWORD}\n\nprocessors:\n  target_data_type: json","section_id":"4-configure-rdi-for-snowflake"},{"id":"connection-issues-ex0","language":"plaintext","code":"Failed to open new session for user: USERNAME, host: <account>.snowflakecomputing.com. Error: Fail : Network policy is required.\nFailed to initialize pool: Fail : Network policy is required.","section_id":"connection-issues"},{"id":"connection-issues-ex1","language":"sql","code":"USE ROLE SECURITYADMIN;\n\nCREATE NETWORK POLICY rdi_policy\n  ALLOWED_IP_LIST = (\n    '203.0.113.10/32',\n    '203.0.113.20/32',\n    '198.51.100.30/32',\n    '198.51.100.40/32'\n  );\n\nALTER USER \"USERNAME\"\nSET NETWORK_POLICY = rdi_policy;","section_id":"connection-issues"},{"id":"cdc-issues-ex0","language":"sql","code":"SHOW STREAMS IN SCHEMA my_cdc_database.my_cdc_schema;","section_id":"cdc-issues"},{"id":"cdc-issues-ex1","language":"bash","code":"kubectl get deployments -n rdi | grep riotx-collector\n    kubectl logs -n rdi deployment/<riotx-collector-deployment>","section_id":"cdc-issues"},{"id":"enable-debug-logging-ex0","language":"yaml","code":"sources:\n  snowflake:\n    type: riotx\n    logging:\n      level: debug\n    # ... rest of configuration","section_id":"enable-debug-logging"},{"id":"enable-debug-logging-ex1","language":"bash","code":"kubectl get deployments -n rdi | grep riotx-collector\nkubectl logs -n rdi deployment/<riotx-collector-deployment> -f","section_id":"enable-debug-logging"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/prepare-dbs/spanner","title":"Prepare Spanner for RDI","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/spanner/","summary":"Prepare Google Cloud Spanner databases to work with RDI","aliases":["/integrate/redis-data-integration/ingest/data-pipelines/prepare-dbs/spanner/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-18T16:39:32+01:00","page_type":"content","content_hash":"109b10ef2c0de06c4d56b48f387672f9bfc3c49e6ee10dfbef4e00cb320bf420","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Google Cloud Spanner requires specific configuration to enable change data capture (CDC) with RDI.\nRDI operates in two phases with Spanner: snapshot (initial sync) and streaming. During the snapshot\nphase, RDI uses the JDBC driver to connect directly to Spanner and read the current state of the\ndatabase. In the streaming phase, RDI uses [Spanner's Change Streams](https://cloud.google.com/spanner/docs/change-streams) to capture changes related to\nthe monitored schemas and tables.\n\n> [!NOTE]\n> Spanner is only supported with RDI deployed on Kubernetes/Helm. RDI VM mode does not support Spanner as a source database.\n\nThe following checklist summarizes the steps to prepare a Spanner\ndatabase for RDI, with links to the sections that explain the steps in\nfull detail. You may find it helpful to track your progress with the\nchecklist as you complete each step.\n\n[code example]"},{"id":"1-prepare-for-snapshot","title":"1. Prepare for snapshot","role":"content","text":"During the snapshot phase, RDI executes multiple transactions to capture data at an exact point \nin time that remains consistent across all queries. This is achieved using a Spanner feature called \n[Timestamp bounds with exact staleness](https://cloud.google.com/spanner/docs/timestamp-bounds#exact_staleness). \n\nThis feature relies on the \n[version_retention_period](https://cloud.google.com/spanner/docs/reference/rest/v1/projects.instances.databases#Database.FIELDS.version_retention_period), \nwhich is set to one hour by default. Depending on the database tier, the volume of data to be \ningested into RDI, and the load on the database, this setting may need to be increased. You can \nupdate it using [this method](https://cloud.google.com/spanner/docs/use-pitr#set-period)."},{"id":"2-prepare-for-streaming","title":"2. Prepare for streaming","role":"content","text":"To enable streaming, you must create a change stream in Spanner at the database level. Use the \noption `value_capture_type = 'NEW_ROW_AND_OLD_VALUES'` to capture both the previous and updated \nrow values.\n\nBe sure to specify only the tables you want to ingest from and, optionally, the specific columns \nyou're interested in. Here's an example using Google SQL syntax:\n\n[code example]\n\nRefer to the [official documentation](https://cloud.google.com/spanner/docs/change-streams/manage#googlesql) \nfor more details, including additional configuration options and dialect-specific syntax."},{"id":"3-create-a-service-account","title":"3. Create a service account","role":"content","text":"To allow RDI to access the Spanner instance, you'll need to create a service account with the\nappropriate permissions. By default, RDI uses Google Cloud Workload Identity authentication. In this case RDI will assume the [service account is assigned to the GKE cluster](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity#enable_on_clusters_and_node_pools). Alternatively, you can provide the\nservice account credentials as a Kubernetes secret (see step 4 for details).\n\n[code example]\n\n1. <a id=\"create-the-service-account\"></a>\n   Create the service account\n\n    [code example]\n\n1. <a id=\"grant-required-roles\"></a>\n   Grant required roles:\n\n    **Database Reader** (read access to Spanner data):\n\n    [code example]\n\n    **Database User** (query execution and metadata access):\n\n    [code example]\n\n    **Viewer** (viewing instance and database configuration):\n\n    [code example]\n\n1. <a id=\"download-the-service-account-key\"></a>\n   Download the service account key:\n\n    Save the credentials locally so they can be used later by RDI:\n\n    [code example]"},{"id":"authentication-methods","title":"Authentication methods","role":"security","text":"RDI supports two authentication methods for accessing Spanner:\n\n1. **Workload Identity (default)**: The service account is assigned to the GKE cluster, and RDI\n   automatically uses the cluster's identity to authenticate. This is the recommended approach\n   as it's more secure and doesn't require managing credential files.\n\n2. **Service account credentials file**: You provide the service account key file as a Kubernetes\n   secret. This method requires setting `use_credentials_file: true` in your RDI configuration."},{"id":"4-set-up-secrets-optional","title":"4. Set up secrets (optional)","role":"content","text":"Before deploying the RDI pipeline, you need to configure the necessary secrets for the target\ndatabase. Set them with\n[`redis-di set-secret`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-set-secret),\nas described in\n[Set secrets](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/deploy#set-secrets).\nA Spanner source needs no credentials secret of its own, because it authenticates through\nWorkload Identity or a service account credentials file.\n\n**Optional**: If you prefer to use a service account credentials file instead of Workload Identity\nauthentication, you'll need to create a Spanner-specific secret named\n`<source>-db-credentials`, where `<source>` is the name of the source in `config.yaml`.\nThe example below assumes a source named `spanner`, so its corresponding secret is\n`spanner-db-credentials`. See\n[Multiple sources in one pipeline](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/multiple-sources)\nfor the source naming rules.\n\nThis secret should contain the service account key file generated during the Spanner setup phase.\nUse the command below to create it:\n\n[code example]\n\nBe sure to adjust the file path (`~/spanner-reader-account.json`) if your service account key is\nstored elsewhere.\n\n> [!NOTE]\n> `redis-di set-secret` has no key for this secret, so you must\n> create it with `kubectl`. A secret created that way must also be labeled so that the RDI operator\n> discovers it as a pipeline secret. See\n> [Set secrets for K8s/Helm deployment using Kubectl command](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/deploy#set-secrets-for-k8shelm-deployment-using-kubectl-command) for more information.\n\n> [!NOTE]\n> If you create the `spanner-db-credentials` secret, you must also set `use_credentials_file: true`\n> in your RDI configuration to use the credentials file instead of Workload Identity authentication."},{"id":"5-configure-rdi-for-spanner","title":"5. Configure RDI for Spanner","role":"content","text":"When configuring your RDI pipeline for Spanner, use the following example configuration in your \n`config.yaml` file:\n\n[code example]\n\nMake sure to replace the relevant connection details with your own for both the Spanner and target \nRedis databases."},{"id":"6-additional-kubernetes-configuration","title":"6. Additional Kubernetes configuration","role":"content","text":"In your `rdi-values.yaml` file for Kubernetes deployment, make sure to configure the `dataPlane` \nsection like this:\n\n[code example]"},{"id":"7-configuration-is-complete","title":"7. Configuration is complete","role":"content","text":"Once you have followed the steps above, your Google Spanner database is ready for RDI to use."}],"examples":[{"id":"overview-ex0","language":"checklist {id=\"spannerlist\"}","code":"- [ ] [Prepare for snapshot](#1-prepare-for-snapshot)\n- [ ] [Prepare for streaming](#2-prepare-for-streaming)\n- [ ] [Create a service account](#3-create-a-service-account)\n- [ ] [Set up secrets (optional)](#4-set-up-secrets-optional)\n- [ ] [Configure RDI for Spanner](#5-configure-rdi-for-spanner)\n- [ ] [Additional Kubernetes configuration](#6-additional-kubernetes-configuration)","section_id":"overview"},{"id":"2-prepare-for-streaming-ex0","language":"sql","code":"CREATE CHANGE STREAM change_stream_table1_and_table2\n  FOR table1, table2\n  OPTIONS (\n    value_capture_type = 'NEW_ROW_AND_OLD_VALUES'\n  );","section_id":"2-prepare-for-streaming"},{"id":"3-create-a-service-account-ex0","language":"checklist {id=\"spanner-service-account\" nointeractive=\"true\" }","code":"- [ ] [Create the service account](#create-the-service-account)\n- [ ] [Grant required roles](#grant-required-roles)\n- [ ] [Download the service account key](#download-the-service-account-key)","section_id":"3-create-a-service-account"},{"id":"3-create-a-service-account-ex1","language":"bash","code":"gcloud iam service-accounts create spanner-reader-account \\\n        --display-name=\"Spanner Reader Service Account\" \\\n        --description=\"Service account for reading from Spanner databases\" \\\n        --project=YOUR_PROJECT_ID","section_id":"3-create-a-service-account"},{"id":"3-create-a-service-account-ex2","language":"bash","code":"gcloud projects add-iam-policy-binding YOUR_PROJECT_ID \\\n        --member=\"serviceAccount:spanner-reader-account@YOUR_PROJECT_ID.iam.gserviceaccount.com\" \\\n        --role=\"roles/spanner.databaseReader\"","section_id":"3-create-a-service-account"},{"id":"3-create-a-service-account-ex3","language":"bash","code":"gcloud projects add-iam-policy-binding YOUR_PROJECT_ID \\\n        --member=\"serviceAccount:spanner-reader-account@YOUR_PROJECT_ID.iam.gserviceaccount.com\" \\\n        --role=\"roles/spanner.databaseUser\"","section_id":"3-create-a-service-account"},{"id":"3-create-a-service-account-ex4","language":"bash","code":"gcloud projects add-iam-policy-binding YOUR_PROJECT_ID \\\n        --member=\"serviceAccount:spanner-reader-account@YOUR_PROJECT_ID.iam.gserviceaccount.com\" \\\n        --role=\"roles/spanner.viewer\"","section_id":"3-create-a-service-account"},{"id":"3-create-a-service-account-ex5","language":"bash","code":"gcloud iam service-accounts keys create ~/spanner-reader-account.json \\\n        --iam-account=spanner-reader-account@YOUR_PROJECT_ID.iam.gserviceaccount.com \\\n        --project=YOUR_PROJECT_ID","section_id":"3-create-a-service-account"},{"id":"4-set-up-secrets-optional-ex0","language":"bash","code":"kubectl create secret generic spanner-db-credentials --namespace=rdi \\\n--from-file=gcp-service-account.json=~/spanner-reader-account.json \\\n--save-config --dry-run=client -o yaml | kubectl apply -f -","section_id":"4-set-up-secrets-optional"},{"id":"5-configure-rdi-for-spanner-ex0","language":"yaml","code":"sources:\n  spanner:\n    type: flink\n    connection:\n      type: spanner\n      project_id: your-project-id\n      instance_id: your-spanner-instance\n      database_id: your-spanner-database\n      # use_credentials_file: false  # Default: uses Workload Identity. Set to true to use service account credentials file instead\n      change_streams:\n        change_stream_all:\n          {}\n          # retention_hours: 24\n    # schemas:\n    #  - DEFAULT\n    # tables:\n    #   products: {}\n    #   orders: {}\n    #   order_items: {}\n    # logging:\n    #   level: debug\n    # advanced:\n    #   source:\n    #     spanner.change.stream.retention.hours: 24\n    #     spanner.fetch.timeout.milliseconds: 20000\n    #     spanner.dialect: POSTGRESQL\n    #   flink:\n    #     jobmanager.rpc.port: 7123\n    #     jobmanager.memory.process.size: 1024m\n    #     taskmanager.numberOfTaskSlots: 3\n    #     taskmanager.rpc.port: 7122\n    #     taskmanager.memory.process.size: 2g\n    #     blob.server.port: 7124\n    #     rest.port: 8082\n    #     parallelism.default: 4\n    #     restart-strategy.type: fixed-delay\n    #     restart-strategy.fixed-delay.attempts: 3\ntargets:\n  target:\n    connection:\n      type: redis\n      host: ${HOST_IP}\n      port: 12000\n      user: ${TARGET_DB_USERNAME}\n      password: ${TARGET_DB_PASSWORD}\nprocessors:\n  target_data_type: hash","section_id":"5-configure-rdi-for-spanner"},{"id":"6-additional-kubernetes-configuration-ex0","language":"yaml","code":"operator:\n  dataPlane:\n    flinkCollector:\n      enabled: true\n      jobManager:\n        ingress:\n          enabled: true\n          className: traefik # Replace with your ingress controller\n          hosts:\n            - hostname # Replace with your desired ingress hostname","section_id":"6-additional-kubernetes-configuration"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/prepare-dbs/sql-server","title":"Prepare SQL Server for RDI","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/sql-server/","summary":"Prepare SQL Server databases to work with RDI","aliases":["/integrate/redis-data-integration/ingest/data-pipelines/prepare-dbs/sql-server/","/integrate/redis-data-integration/ingest/data-transformation/prepare-dbs/sql-server/","/integrate/redis-data-integration/ingest/installation/prepare-dbs/sql-server/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-10-09T12:24:46+03:00","page_type":"content","content_hash":"64d6b86d9ee10c243a091ed24033f97b3f80fc5c68f943a603999ebc267497c2","sections":[{"id":"overview","title":"Overview","role":"overview","text":"To prepare your SQL Server database for Debezium, you must first create a dedicated Debezium user,\nrun a script to enable CDC globally, and then separately enable CDC for each table you want to\ncapture. You need administrator privileges to do this.\n\nOnce you enable CDC, it captures all of the INSERT, UPDATE, and DELETE operations\non your chosen tables. The Debezium connector can then emit these events to RDI.\nRDI only reads from the source database; it captures changes through the CDC tables\nand never modifies the source data.\n\nThe following checklist summarizes the steps to prepare a SQL Server\ndatabase for RDI, with links to the sections that explain the steps in\nfull detail. You may find it helpful to track your progress with the\nchecklist as you complete each step.\n\n[code example]"},{"id":"1-create-a-debezium-user","title":"1. Create a Debezium user","role":"content","text":"It is strongly recommended to create a dedicated Debezium user for the connection between RDI\nand the source database. When using an existing user, ensure that the required \npermissions are granted and that the user is added to the CDC role.\n\n[code example]\n\n1. <a id=\"create-the-debezium-user\"></a>\n  Create the Debezium user with the Transact-SQL below:\n\n    [code example]\n\n    Replace `MyUser`, `My_Password` and `MyDB` with your chosen values.\n\n1. <a id=\"grant-the-user-the-necessary-permissions\"></a>\n  Grant the user the necessary permissions:\n\n    [code example]"},{"id":"2-enable-cdc-on-the-database","title":"2. Enable CDC on the database","role":"content","text":"There are two system stored procedures to enable CDC (you need\nadministrator privileges to run these). Use `sys.sp_cdc_enable_db`\nto enable CDC for the whole database and then `sys.sp_cdc_enable_table` to enable CDC for individual tables. \n\nBefore running the procedures, ensure that:\n\n- You are a member of the `sysadmin` fixed server role for the SQL Server.\n- You are a `db_owner` of the database.\n- The SQL Server Agent is running.\n\nThen, assuming your database is called `MyDB`, run the script below to enable CDC:\n\n[code example]\n\n> [!NOTE]\n> For SQL Server on AWS RDS, you must use a different stored procedure:\n> [code example]\n\nWhen you enable CDC for the database, it creates a schema called `cdc` and also\na CDC user, metadata tables, and other system objects."},{"id":"3-enable-cdc-for-the-tables-you-want-to-capture","title":"3. Enable CDC for the tables you want to capture","role":"content","text":"[code example]\n\n1. <a id=\"enable-cdc-on-the-tables-you-want-to-capture\"></a>\n    You must also enable CDC on the tables you want Debezium to capture using the\n    following commands (again, you need administrator privileges for this):\n\n    [code example]\n\n    Repeat this for every table you want to capture.\n\n    > [!NOTE]\n    > The value for `@role_name` can’t be a fixed database role, such as `db_datareader`. \n    > Specifying a new name will create a corresponding database role that has full access to the\n    > captured change data.\n\n1. <a id=\"add-the-debezium-user-to-the-cdc-role\"></a>\n    Add the Debezium user to the CDC role:\n\n    [code example]"},{"id":"4-check-that-you-have-access-to-the-cdc-table","title":"4. Check that you have access to the CDC table","role":"content","text":"You can use another stored procedure `sys.sp_cdc_help_change_data_capture`\nto query the CDC information for the database and check you have enabled\nit correctly. To do this, connect as the Debezium user you created previously (`MyUser`).\n\n[code example]\n\n1. <a id=\"run-the-stored-procedure-to-query-the-cdc-configuration\"></a>\n  Run the `sys.sp_cdc_help_change_data_capture` stored procedure to query\n    the CDC configuration. For example, if your database was called `MyDB` then you would\n    run the following:\n\n    [code example]\n\n1. <a id=\"check-the-results\"></a>\n    The query returns configuration information for each table in the database that\n    has CDC enabled and that contains change data that you are authorized to\n    access. If the result is empty then you should check that you have privileges\n    to access both the capture instance and the CDC tables."},{"id":"troubleshooting","title":"Troubleshooting","role":"errors","text":"If no CDC is happening then it might mean that SQL Server Agent is down. You can check for this using the SQL query shown below:\n\n[code example]\n\nIf the query returns a result of 0, you need to need to start SQL Server Agent using the following commands:\n\n[code example]"},{"id":"5-using-sql-server-with-custom-data-fields","title":"5. Using SQL Server with custom data fields","role":"content","text":"When using custom data types, you must ensure that the Debezium user has the necessary permissions to access \nthose types. You can grant these permissions using the following SQL commands:\n\n[code example]\n\nIf you don't grant these permissions you may encounter errors like:\n`\"The column \"Account\" is referenced as PRIMARY KEY, but a matching column is not defined in table \"DB.dbo.MyTable\"!\"`"},{"id":"sql-server-capture-job-agent-configuration-parameters","title":"SQL Server capture job agent configuration parameters","role":"content","text":"In SQL Server, the parameters that control the behavior of the capture job agent\nare defined in the SQL Server table `msdb.dbo.cdc_jobs`. If you experience performance\nproblems while running the capture job agent then you can adjust the capture jobs\nsettings to reduce CPU load. To do this, run the `sys.sp_cdc_change_job` stored procedure\nwith your new parameter values.\n\n> [!NOTE]\n> A full guide to configuring the SQL Server capture job agent parameters\n> is outside the scope of the Redis documentation.\n\nThe following parameters are the most important ones for modifying the capture agent behavior\nof the Debezium SQL Server connector:\n\n* `pollinginterval`: This specifies the number of seconds that the capture agent\n  waits between log scan cycles. A higher value reduces the load on the database\n  host, but increases latency.  A value of 0 specifies no wait between scans.\n  The default value is 5.\n* `maxtrans`: This specifies the maximum number of transactions to process during\n  each log scan cycle. After the capture job processes the specified number of\n  transactions, it pauses for the length of time that `pollinginterval` specifies\n  before the next scan begins. A lower value reduces the load on the database host,\n  but increases latency. The default value is 500.\n* `maxscans`: This specifies a limit on the number of scan cycles that the capture\n  job can attempt when capturing the full contents of the database transaction log.\n  If the continuous parameter is set to 1, the job pauses for the length of time\n  that the `pollinginterval` specifies before it resumes scanning. A lower values\n  reduces the load on the database host, but increases latency. The default value is 10.\n\nSee the SQL Server documentation for more information about capture agent parameters."},{"id":"sql-server-on-azure","title":"SQL Server on Azure","role":"content","text":"RDI can capture changes from Microsoft SQL Server hosted on Azure. The preparation\nsteps are similar to the on-premises instructions above, but Azure adds extra\nrequirements for authentication, networking, and (for Azure SQL Database) the\ndatabase service tier. Use the checklist below to track the additional steps.\n\n[code example]"},{"id":"supported-azure-sql-products","title":"Supported Azure SQL products","role":"compatibility","text":"| Product | Supported | Notes |\n| --- | --- | --- |\n| Azure SQL Database (single database or elastic pool) | Yes | Supported on any service tier in the vCore-based purchasing model. In the DTU-based purchasing model, CDC requires the S3 tier or higher — it is not supported on Basic, S0, S1, or S2. |\n| Azure SQL Managed Instance | Yes | Behaves like on-premises SQL Server. The SQL Server Agent is available and the on-premises CDC procedures apply unchanged. |\n| SQL Server on an Azure VM | Yes | Treat as on-premises — follow the [main SQL Server instructions](#1-create-a-debezium-user). The Azure-specific guidance below does not apply. |\n| Azure Synapse Analytics, Microsoft Fabric SQL database | No | These products do not support the SQL Server CDC features that Debezium relies on. |"},{"id":"configure-network-access","title":"Configure network access","role":"configuration","text":"The RDI connector must be able to reach the Azure SQL endpoint on the configured port\n(TCP 1433 by default; set via the `port` field in your RDI source configuration).\n\n- **Public endpoint**: add a server-level or database-level firewall rule that allows\n  the public outbound IP address of the host running the RDI connector. See Microsoft's\n  [Azure SQL firewall configuration](https://learn.microsoft.com/en-us/azure/azure-sql/database/firewall-configure)\n  documentation for details.\n- **Private endpoint or VNet integration** (recommended for production): expose the\n  Azure SQL server through a\n  [private endpoint](https://learn.microsoft.com/en-us/azure/azure-sql/database/private-endpoint-overview)\n  on the same VNet as the RDI connector — for example, when RDI runs on Azure\n  Kubernetes Service.\n\nAzure SQL rejects unencrypted connections, so the RDI connection must always use TLS.\nThis is enforced by the [connector source settings](#configure-the-rdi-source-for-azure-sql)\ndescribed below."},{"id":"enable-cdc-on-the-database-azure","title":"Enable CDC on the database","role":"content","text":"The procedure depends on which Azure SQL product you are using.\n\n#### Azure SQL Database\n\nYou must be a member of the `db_owner` role on the database — Azure SQL Database has\nno `sysadmin` server role.\n\n> [!WARNING]\n> The identity used to enable CDC must match the type of identity that\n> created the database. If the database was created by a Microsoft Entra user, CDC must\n> be enabled (and later disabled) by a Microsoft Entra user; SQL logins cannot manage\n> CDC on it. The same restriction applies in reverse for databases created by SQL\n> logins.\n\nConnect to the user database and run:\n\n[code example]\n\nThis creates the `cdc` schema, the `cdc` database user, the CDC metadata tables, and\nother system objects in your database. Do not modify or drop these objects manually.\nThen enable CDC on each table you want to capture, using the same\n`sys.sp_cdc_enable_table` procedure described in the\n[on-premises instructions](#3-enable-cdc-for-the-tables-you-want-to-capture).\n\nCDC service-tier requirements differ between purchasing models:\n\n- **vCore-based purchasing model**: CDC is supported on any service tier, including\n  General Purpose.\n- **DTU-based purchasing model**: CDC requires the S3 tier or higher. It is not\n  supported on Basic, S0, S1, or S2.\n\nIf `sys.sp_cdc_enable_db` returns an error such as `Change data capture is not supported for this edition of SQL Server`,\nscale the database up before retrying.\n\n> [!NOTE]\n> Capture and cleanup run automatically on Azure SQL Database — there is no\n> SQL Server Agent. The internal scheduler runs the capture process every 20 seconds and\n> the cleanup process every hour, with a default change-data retention period of three\n> days. The capture cadence — the `pollinginterval` parameter described in the\n> [SQL Server capture job agent configuration parameters](#sql-server-capture-job-agent-configuration-parameters)\n> section — is fixed on Azure SQL Database and cannot be tuned. The `maxtrans` and\n> `maxscans` parameters from that section can still be adjusted via `sp_cdc_change_job`.\n\nEnabling CDC increases transaction log usage on Azure SQL Database because it disables\nthe aggressive log truncation behavior of Accelerated Database Recovery. You may need\nto scale the database to a higher service tier to provide enough transaction log\nthroughput for your workload combined with CDC. After a local or geo-replication\nfailover, CDC continues to operate automatically on the new primary; no manual\nreconfiguration is required.\n\nFor more information about CDC on Azure SQL Database, see Microsoft's\n[Change Data Capture with Azure SQL Database](https://learn.microsoft.com/en-us/azure/azure-sql/database/change-data-capture-overview?view=azuresql)\nguide.\n\n#### Reducing end-to-end latency on Azure SQL Database\n\nBecause the capture cadence on Azure SQL Database is fixed at ~20 seconds and\ncannot be tuned, the CDC step alone can add up to that much latency to your\nend-to-end change-propagation time. If your workload needs lower latency, you\ncan supplement the automatic Azure scheduler with an external worker that\nperiodically calls the `sys.sp_cdc_scan` stored procedure. The automatic\nscheduler continues to run; each manual call adds an extra CDC log scan in\nbetween, lowering the effective capture cadence to roughly the worker's\npolling interval.\n\nEach call runs one CDC log scan, bounded by the `maxtrans` and `maxscans`\nparameters covered in\n[SQL Server capture job agent configuration parameters](#sql-server-capture-job-agent-configuration-parameters).\nOn Azure SQL Database, `pollinginterval` and `continuous` do not apply, but\n`maxtrans` and `maxscans` remain tunable via `sp_cdc_change_job`. For low and\nmoderate change volumes the defaults are usually fine — each call drains the\npending transactions. For high-volume workloads, raise `maxtrans` and\n`maxscans` if a single call cannot keep up with the change rate.\n\nThis is a customer-operated workaround for an Azure platform limitation, not a\nRedis-supplied component. It does not apply to Azure SQL Managed Instance,\nSQL Server on Azure VM, or on-premises SQL Server — those use SQL Server Agent\nand the tunable `pollinginterval` parameter described in\n[SQL Server capture job agent configuration parameters](#sql-server-capture-job-agent-configuration-parameters).\n\n> [!WARNING]\n> Run **only one** instance of the scan worker per source database.\n> `sys.sp_cdc_scan` holds an exclusive log-reader lock for the duration of each\n> call; concurrent callers fail rather than running in parallel, so additional\n> replicas add no throughput and only generate error noise.\n\n##### Requirements\n\n- A database identity with permission to execute `sys.sp_cdc_scan`. This\n  requires `db_owner` and is **more privilege than the Debezium user needs**, so\n  create a separate login dedicated to the scan worker rather than reusing the\n  RDI source credentials.\n- A single-replica runtime (a Kubernetes Deployment with `replicas: 1`, a\n  systemd unit, a serverless cron with `maxConcurrency: 1`, or equivalent).\n- Network access from the worker to the Azure SQL endpoint on TCP 1433.\n\n##### Scan loop\n\nThe worker repeatedly opens a connection (or holds a long-lived one), runs\n`EXEC sys.sp_cdc_scan;` with a bounded command timeout, sleeps for the\nconfigured interval, and handles two expected error classes:\n\n- **Scan already in progress** — `sys.sp_cdc_scan` cannot run while another\n  CDC log scan is active, either the Azure-internal scheduler's scan or a\n  previous call from this worker that has not yet returned. The procedure\n  returns a SQL error in this state. The error message has been observed to\n  contain `sp_replcmds` (the underlying log-reader procedure), but the exact\n  wording is not contractual — match by whatever signature your client\n  surfaces, then log the occurrence and continue. Do not back off.\n- **Connection or transport errors** — close and reopen the connection with\n  exponential backoff before the next attempt.\n\nThe example below shows the loop in pseudocode:\n\n[code example]\n\n##### Choosing the interval\n\nThe scan interval directly trades end-to-end latency against source-database\nload — each call reads the transaction log. Pick the largest interval that\nmeets your latency target:\n\n| Interval | Approximate CDC-step latency | Typical use |\n| --- | --- | --- |\n| No worker | Up to ~20s | Azure SQL Database default; the automatic scheduler runs every ~20s. |\n| 5s  | Around 5s | Workload tolerates ~5s end-to-end. |\n| 2s  | Around 2s under low to moderate load; can be higher under heavy write volume | Latency-sensitive workloads. Confirm the achieved latency under your own workload before relying on it. |\n\nIntervals below 1s are not recommended — each call has a fixed cost on the\nsource database and the marginal latency improvement is small.\n\n> [!WARNING]\n> CDC scans consume regular database resources. Every call reads\n> the transaction log, competing with the workload for CPU, memory, and log I/O.\n> An aggressive interval can degrade the source database, especially on lower\n> service tiers or under high write volume. Microsoft provides no SLA on CDC\n> freshness on Azure SQL Database; treat measured end-to-end latency under your\n> own workload as the source of truth, not the configured interval. If scans\n> start falling behind, raise the service tier, raise `maxtrans` and `maxscans`,\n> or relax the interval.\n\n##### Example Kubernetes deployment\n\nA minimal single-replica deployment skeleton — adapt the image, namespace, and\nsecret reference to your environment:\n\n[code example]\n\nThe secret referenced by `envFrom` must provide the credentials of the\n`db_owner` identity created for the scan worker — not the RDI source\ncredentials.\n\n##### Verifying the workaround\n\nAfter the worker has been running for a few minutes, confirm that the\neffective scan cadence has dropped to the worker's interval by querying the\n`sys.dm_cdc_log_scan_sessions` dynamic management view. This DMV records both\nthe automatic scheduler's scans and the worker's manual scans, so the gap\nbetween successive `start_time` values should now match the worker's interval:\n\n[code example]\n\nTo check the commit time of the latest change captured for a specific table,\nmap the highest captured LSN back to a time using `sys.fn_cdc_map_lsn_to_time`:\n\n[code example]\n\nThe difference between that value and the current time is an upper bound on\nhow stale the captured stream is for that table.\n\nYou can also confirm that end-to-end change propagation through RDI now meets\nyour latency target by measuring `<change committed in source> → <change visible in Redis>`\non a representative table.\n\n#### Azure SQL Managed Instance\n\nFollow the on-premises instructions for\n[enabling CDC on the database](#2-enable-cdc-on-the-database) and\n[enabling CDC on the tables you want to capture](#3-enable-cdc-for-the-tables-you-want-to-capture).\nThe procedures are identical on Managed Instance."},{"id":"create-a-database-user-for-debezium","title":"Create a database user for Debezium","role":"content","text":"RDI only reads from the source database, so the Debezium user only needs read access.\nDo not grant `db_datawriter` or any other write permissions.\n\nYou can authenticate to Azure SQL using either SQL authentication or Microsoft Entra ID.\nMicrosoft Entra authentication with a service principal is the validated path for RDI;\nuse it for production deployments. SQL authentication is supported and is the simplest\noption for development or proof-of-concept setups.\n\n#### Option A: SQL authentication\n\nFollow the on-premises instructions for\n[creating the Debezium user](#1-create-a-debezium-user), with one Azure-specific\nchange: on Azure SQL Database, omit the `master`-database step and create a contained\nuser in the user database. Connect to your user database as the server admin and run:\n\n[code example]\n\nAfter enabling CDC on the tables you want to capture, add the user to the CDC role:\n\n[code example]\n\n> [!NOTE]\n> Use `VIEW DATABASE STATE` rather than `VIEW SERVER STATE`. The server-scoped\n> permission does not exist on Azure SQL Database.\n\n#### Option B: Microsoft Entra service principal\n\n1. **Register an application in Microsoft Entra ID.**\n    In the Azure portal, go to **Microsoft Entra ID > App registrations > New registration**.\n    Note the **Application (client) ID** — you'll use it as the RDI `user` value. Create\n    a client secret under **Certificates & secrets** and note its value — you'll use it\n    as the RDI `password` value.\n\n1. **Set a Microsoft Entra admin on the Azure SQL logical server.**\n    In the Azure portal, open the logical SQL server and set a Microsoft Entra admin (a\n    user or group you can sign in as). You will connect as this admin to create the\n    contained user in the next step. (The permission to create contained users mapped\n    to Microsoft Entra principals can also be delegated to other database principals;\n    see Microsoft's [Microsoft Entra authentication for Azure SQL](https://learn.microsoft.com/en-us/azure/azure-sql/database/authentication-aad-overview)\n    documentation.)\n\n1. **Create a contained database user for the service principal.**\n    Connect to the user database as the Microsoft Entra admin (for example, using\n    `sqlcmd -G` or Azure Data Studio) and run:\n\n    [code example]\n\n    After enabling CDC on the tables you want to capture, add the principal to the CDC role:\n\n    [code example]\n\n    > [!NOTE]\n    > `<sp-display-name>` is the **display name** of the app registration — the\n    > value shown in the **Name** column on the **App registrations** page — not its client\n    > ID. The client ID is used by the RDI connector (see the next section), but the\n    > database user must be created from the display name. If the display name is not\n    > unique in your Microsoft Entra tenant (display names are not guaranteed unique),\n    > disambiguate by adding the `WITH OBJECT_ID = '<sp-object-id>'` clause to the\n    > `CREATE USER` statement."},{"id":"configure-the-rdi-source-for-azure-sql","title":"Configure the RDI source for Azure SQL","role":"configuration","text":"Use a `cdc` source with `type: sqlserver`. The example below shows the validated\nconfiguration for Azure SQL Database with Microsoft Entra service-principal\nauthentication:\n\n[code example]\n\nThe properties under `advanced.source` are passed straight through to the underlying\nDebezium SQL Server connector and JDBC driver. The Azure-specific values are:\n\n| Property | Purpose | Value for Azure SQL Database |\n| --- | --- | --- |\n| `driver.authentication` | Selects the JDBC Microsoft Entra authentication mode. | `ActiveDirectoryServicePrincipal` (validated). See [other Microsoft Entra authentication modes](#other-microsoft-entra-authentication-modes) for alternatives. |\n| `database.encrypt` | Enforces TLS on the JDBC connection. | `\"true\"`. Azure SQL rejects unencrypted connections. |\n| `database.trustServerCertificate` | If `true`, the driver skips certificate validation. | `\"false\"`. Azure SQL presents a valid certificate; never disable validation in production. |\n| `database.hostNameInCertificate` | Tells the JDBC driver which hostname pattern to expect in the server's TLS certificate. Set explicitly when the certificate's subject does not match the connection hostname directly. | `\"*.database.windows.net\"` (used in the RDI-validated configuration to match Azure SQL's wildcard certificate). |\n| `database.applicationIntent` | When set to `ReadOnly`, routes the connection to a read-only replica on tiers that support [read scale-out](https://learn.microsoft.com/en-us/azure/azure-sql/database/read-scale-out). | `ReadOnly`. Recommended because RDI only reads. On tiers where Azure SQL read scale-out is available (Business Critical and Hyperscale), this routes the RDI read connection to a read-only replica. On General Purpose, which has no read scale-out, the setting has no effect. |\n| `snapshot.mode` | The Debezium snapshot strategy. | `initial`. Captures a snapshot of the existing rows, then streams subsequent changes from the CDC tables. |\n\nFor SQL authentication, omit the `driver.authentication` line and set\n`${SQLSERVER_DB_USERNAME}` and `${SQLSERVER_DB_PASSWORD}` to the SQL user's credentials.\nKeep the other Azure-specific properties.\n\n#### Secret mapping\n\nFor Microsoft Entra service-principal authentication, the source's credentials must\nprovide:\n\n| Config reference | Value |\n| --- | --- |\n| `${SQLSERVER_DB_USERNAME}` | The service principal's **Application (client) ID** (a GUID). |\n| `${SQLSERVER_DB_PASSWORD}` | The service principal's **client secret**. |\n\nSet them with the source name, which is `sqlserver` for the source in this example:\n\n[code example]\n\n> [!WARNING]\n> The username value is the client ID (a GUID), but the contained\n> database user created in the previous section uses the service principal's **display\n> name**. These are two different identifiers for the same principal — mixing them up is\n> the most common cause of `Login failed for user '<token-identified principal>'` errors\n> at connection time.\n\n#### Other Microsoft Entra authentication modes\n\nThe Microsoft JDBC driver supports several other Microsoft Entra modes. The following\nare technically usable but are not currently validated by RDI — check with Redis\nsupport before using them in production:\n\n- **`ActiveDirectoryServicePrincipalCertificate`** — service principal authenticated by\n  a certificate instead of a secret. Useful when organizational policy forbids\n  long-lived shared secrets.\n- **`ActiveDirectoryManagedIdentity`** — for RDI installations running on an Azure\n  resource (such as an Azure VM or Azure Kubernetes Service node) that has a system-\n  or user-assigned managed identity.\n\nThe deprecated `ActiveDirectoryPassword` mode and the interactive\n`ActiveDirectoryInteractive` mode are not suitable for a server-side connector and are\nnot supported.\n\nSee Microsoft's\n[Connect using Microsoft Entra authentication](https://learn.microsoft.com/en-us/sql/connect/jdbc/connecting-using-azure-active-directory-authentication?view=sql-server-ver17)\nfor the full list of modes and their connection-string syntax."},{"id":"verify-the-connection","title":"Verify the connection","role":"content","text":"Connect to the database as the Debezium user (the SQL user or the Microsoft Entra\nservice principal) and run `sys.sp_cdc_help_change_data_capture` to confirm that the\nuser can see the captured tables. The query is the same as for\n[on-premises SQL Server](#4-check-that-you-have-access-to-the-cdc-table).\n\nYou can also confirm the database-level and table-level CDC state directly from the\ncatalog views:\n\n[code example]"},{"id":"troubleshooting-1","title":"Troubleshooting","role":"errors","text":"- **`Login failed for user '<token-identified principal>'`** — the contained database\n  user was not created for this service principal, or it was created with the wrong\n  identifier. Verify that the `CREATE USER ... FROM EXTERNAL PROVIDER` statement used\n  the service principal's display name, and that `${SQLSERVER_DB_USERNAME}` contains its\n  client ID. Query `sys.database_principals` on the source database to see which\n  principals exist.\n- **`SSL Server certificate validation failed` or hostname mismatch** —\n  `database.hostNameInCertificate` is missing or has the wrong value. For Azure SQL\n  Database, set it to `\"*.database.windows.net\"` to match the wildcard certificate.\n- **`Change data capture is not supported for this edition of SQL Server`** — the\n  Azure SQL Database is on an unsupported service tier. In the DTU purchasing model,\n  scale up to S3 or higher. In the vCore purchasing model, CDC is supported on all\n  tiers, so check that you are connecting to a standard Azure SQL Database (CDC is\n  not supported on Azure SQL Edge or other variants).\n- **Connection timeouts** — the RDI connector's source IP is not allowed by the Azure\n  SQL firewall, or the private endpoint is not reachable from the connector's network.\n  Verify firewall rules in the Azure portal and that DNS resolves to the expected\n  (public or private) endpoint."},{"id":"handling-changes-to-the-schema","title":"Handling changes to the schema","role":"content","text":"RDI can't adapt automatically when you change the schema of a CDC table in SQL Server. The existing capture instance continues to use its captured column structure. For example, adding a column can leave RDI streaming changes without that column instead of reporting an error. Create a new capture instance to capture the updated schema. See Debezium's [SQL Server schema evolution](https://debezium.io/documentation/reference/stable/connectors/sqlserver.html#sqlserver-schema-evolution) documentation for more information.\n\nIf you have administrator privileges, use this online procedure to update the capture instance while RDI is running. During the interval between changing the source schema and creating the new capture instance, events still use the old captured column structure. A short interval or low write traffic does not guarantee correct capture. If you cannot tolerate events that omit the new column, [update the schema offline](#update-the-schema-offline).\n\n[code example]\n\n1. <a id=\"make-your-changes-to-the-source-table-schema\"></a>\n    Make your changes to the source table schema.\n\n1. <a id=\"create-a-new-capture-table-for-the-updated-source-table\"></a>\n  Create a new capture table for the updated source table by running the `sys.sp_cdc_enable_table` stored\n    procedure with a new, unique value for the parameter `@capture_instance`. For example, if the old value\n    was `dbo_MyTable`, you could replace it with `dbo_MyTable_v2` (you can see the existing values by running\n    stored procedure `sys.sp_cdc_help_change_data_capture`):\n\n    [code example]\n\n1. <a id=\"drop-the-old-capture-table\"></a>\n    When Debezium starts streaming from the new capture table and you no longer need the old captured changes, drop the old capture table by running the `sys.sp_cdc_disable_table` stored procedure with the parameter `@capture_instance` set to the old capture instance name, `dbo_MyTable`:\n\n    Disabling the old instance deletes its change table and captured history. Rewinding an offset cannot recover changes that existed only in that capture table, even if SQL Server still retains them in the transaction log. If you need those changes for recovery or replay, retain the old instance until they are no longer needed. Retaining the instance does not prevent CDC cleanup from removing expired changes. Check the [CDC retention and capture instance limits](https://learn.microsoft.com/en-us/sql/relational-databases/track-changes/about-change-data-capture-sql-server). SQL Server permits only two capture instances per source table, so retaining the old instance also affects the next schema migration.\n\n    [code example]\n\n> [!NOTE]\n> Verify the values of the new or changed columns in the captured events after the migration. Continued streaming or a matching row count does not prove that the updated columns were captured."},{"id":"update-the-schema-offline","title":"Update the schema offline","role":"content","text":"Use this procedure to adapt Debezium's [offline schema update procedure](https://debezium.io/documentation/reference/stable/connectors/sqlserver.html#offline-schema-updates) to RDI. Keep the pipeline running and stop only the SQL Server source collector, so the processor and other source collectors remain available. Replace `<pipeline-name>` with your pipeline name and `<source-name>` with the SQL Server source name from `config.yaml`.\n\n1. Suspend application writes to the tables captured by the SQL Server source collector.\n\n1. Wait for the final committed changes to reach the target Redis database. Use [`redis-di describe`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-describe) to check that the source's tables have zero pending records, no errors, and no new rejected records. Also verify the expected final changes in the target. Zero pending records alone does not prove that SQL Server's capture job has captured the final writes.\n\n    [code example]\n\n1. Stop the SQL Server source collector with [`redis-di stop`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-stop). Wait for the command to complete successfully before changing the schema.\n\n    [code example]\n\n1. [Apply the schema changes](#make-your-changes-to-the-source-table-schema) while application writes remain suspended.\n\n1. [Create a new capture instance](#create-a-new-capture-table-for-the-updated-source-table) with a unique name. Keep the old instance until RDI has switched to the new one and you no longer need its captured history.\n\n1. If your pipeline selects specific columns or references changed column names in transformations, update its configuration and [deploy it](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/deploy) while application writes remain suspended.\n\n1. Restart the SQL Server source collector with [`redis-di start`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-start).\n\n    [code example]\n\n1. Check `redis-di describe <pipeline-name>` for source connectivity and errors before resuming application writes. Starting a source collector does not start a stopped pipeline.\n\n1. Resume application writes.\n\n1. Verify that changes made after the migration include the new or changed column values in the target as intended by your transformation jobs.\n\n1. [Drop the old capture instance](#drop-the-old-capture-table) only after RDI streams from the new instance and you no longer need the old captured history."}],"examples":[{"id":"overview-ex0","language":"checklist {id=\"sqlserverlist\"}","code":"- [ ] [Create a Debezium user](#1-create-a-debezium-user)\n- [ ] [Enable CDC on the database](#2-enable-cdc-on-the-database)\n- [ ] [Enable CDC for the tables you want to capture](#3-enable-cdc-for-the-tables-you-want-to-capture)\n- [ ] [Check that you have access to the CDC table](#4-check-that-you-have-access-to-the-cdc-table)","section_id":"overview"},{"id":"1-create-a-debezium-user-ex0","language":"checklist {id=\"sqlserver-create-debezium-user\" nointeractive=\"true\" }","code":"- [ ] [Create the Debezium user](#create-the-debezium-user)\n- [ ] [Grant the user the necessary permissions](#grant-the-user-the-necessary-permissions)","section_id":"1-create-a-debezium-user"},{"id":"1-create-a-debezium-user-ex1","language":"sql","code":"USE master\n    GO\n    CREATE LOGIN MyUser WITH PASSWORD = 'My_Password'\n    GO\n    USE MyDB\n    GO\n    CREATE USER MyUser FOR LOGIN MyUser\n    GO","section_id":"1-create-a-debezium-user"},{"id":"1-create-a-debezium-user-ex2","language":"sql","code":"USE master\n    GO\n    GRANT VIEW SERVER STATE TO MyUser\n    GO\n    USE MyDB\n    GO\n    EXEC sp_addrolemember N'db_datareader', N'MyUser'\n    GO","section_id":"1-create-a-debezium-user"},{"id":"2-enable-cdc-on-the-database-ex0","language":"sql","code":"USE MyDB\nGO\nEXEC sys.sp_cdc_enable_db\nGO","section_id":"2-enable-cdc-on-the-database"},{"id":"2-enable-cdc-on-the-database-ex1","language":"sql","code":"> EXEC msdb.dbo.rds_cdc_enable_db 'Chinook'\n> GO\n>","section_id":"2-enable-cdc-on-the-database"},{"id":"3-enable-cdc-for-the-tables-you-want-to-capture-ex0","language":"checklist {id=\"sqlserver-enable-cdc-tables\" nointeractive=\"true\" }","code":"- [ ] [Enable CDC on the tables you want to capture](#enable-cdc-on-the-tables-you-want-to-capture)\n- [ ] [Add the Debezium user to the CDC role](#add-the-debezium-user-to-the-cdc-role)","section_id":"3-enable-cdc-for-the-tables-you-want-to-capture"},{"id":"3-enable-cdc-for-the-tables-you-want-to-capture-ex1","language":"sql","code":"USE MyDB\n    GO\n\n    EXEC sys.sp_cdc_enable_table\n    @source_schema = N'dbo',\n    @source_name   = N'MyTable', \n    @role_name     = N'MyRole',  \n    @supports_net_changes = 0\n    GO","section_id":"3-enable-cdc-for-the-tables-you-want-to-capture"},{"id":"3-enable-cdc-for-the-tables-you-want-to-capture-ex2","language":"sql","code":"USE MyDB\n    GO\n    EXEC sp_addrolemember N'MyRole', N'MyUser'\n    GO","section_id":"3-enable-cdc-for-the-tables-you-want-to-capture"},{"id":"4-check-that-you-have-access-to-the-cdc-table-ex0","language":"checklist {id=\"sqlserver-check-cdc-table\" nointeractive=\"true\" }","code":"- [ ] [Run the stored procedure to query the CDC configuration](#run-the-stored-procedure-to-query-the-cdc-configuration)\n- [ ] [Check the results](#check-the-results)","section_id":"4-check-that-you-have-access-to-the-cdc-table"},{"id":"4-check-that-you-have-access-to-the-cdc-table-ex1","language":"sql","code":"USE MyDB;\n    GO\n    EXEC sys.sp_cdc_help_change_data_capture\n    GO","section_id":"4-check-that-you-have-access-to-the-cdc-table"},{"id":"troubleshooting-ex0","language":"sql","code":"IF EXISTS (SELECT 1 \n           FROM master.dbo.sysprocesses \n           WHERE program_name = N'SQLAgent - Generic Refresher')\nBEGIN\n  SELECT @@SERVERNAME AS 'InstanceName', 1 AS 'SQLServerAgentRunning'\nEND\nELSE \nBEGIN\n  SELECT @@SERVERNAME AS 'InstanceName', 0 AS 'SQLServerAgentRunning'\nEND","section_id":"troubleshooting"},{"id":"troubleshooting-ex1","language":"sql","code":"EXEC xp_servicecontrol N'START',N'SQLServerAGENT';\nGO","section_id":"troubleshooting"},{"id":"5-using-sql-server-with-custom-data-fields-ex0","language":"sql","code":"USE <db-name>;\n\nGRANT REFERENCES ON TYPE::<schema>.<type-name> TO <db-user>;","section_id":"5-using-sql-server-with-custom-data-fields"},{"id":"sql-server-on-azure-ex0","language":"checklist {id=\"sqlserverazurelist\"}","code":"- [ ] [Confirm your Azure SQL product and service tier](#supported-azure-sql-products)\n- [ ] [Configure network access](#configure-network-access)\n- [ ] [Enable CDC on the database](#enable-cdc-on-the-database-azure)\n- [ ] [Create a database user for Debezium](#create-a-database-user-for-debezium)\n- [ ] [Configure the RDI source for Azure SQL](#configure-the-rdi-source-for-azure-sql)\n- [ ] [Verify the connection](#verify-the-connection)","section_id":"sql-server-on-azure"},{"id":"enable-cdc-on-the-database-azure-ex0","language":"sql","code":"EXEC sys.sp_cdc_enable_db\nGO","section_id":"enable-cdc-on-the-database-azure"},{"id":"enable-cdc-on-the-database-azure-ex1","language":"text","code":"loop until shutdown:\n    start = now()\n    try:\n        EXEC sys.sp_cdc_scan        # command timeout: 30s\n        log(\"scan_ok\", now() - start)\n    catch SqlException identifying \"scan already active\":\n        log(\"scan_already_running\", now() - start)\n    catch any other exception as e:\n        log(\"scan_error\", e)\n        reconnect with exponential backoff\n    sleep(max(0, interval - (now() - start)))","section_id":"enable-cdc-on-the-database-azure"},{"id":"enable-cdc-on-the-database-azure-ex2","language":"yaml","code":"apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: azure-sql-cdc-scan-worker\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: azure-sql-cdc-scan-worker\n  template:\n    metadata:\n      labels:\n        app: azure-sql-cdc-scan-worker\n    spec:\n      containers:\n        - name: worker\n          image: <your-registry>/<your-scan-worker-image>:<tag>\n          env:\n            - name: SQL_HOST\n              value: <server-name>.database.windows.net\n            - name: SQL_DATABASE\n              value: <database-name>\n            - name: SCAN_INTERVAL_MS\n              value: \"2000\"\n          envFrom:\n            - secretRef:\n                name: <scan-worker-db-secret>","section_id":"enable-cdc-on-the-database-azure"},{"id":"enable-cdc-on-the-database-azure-ex3","language":"sql","code":"-- Recent CDC log-scan sessions (manual and automatic combined)\nSELECT TOP (10)\n  session_id, start_time, end_time, duration, scan_phase,\n  latency, tran_count, last_commit_cdc_time\nFROM sys.dm_cdc_log_scan_sessions\nWHERE session_id > 0\nORDER BY session_id DESC","section_id":"enable-cdc-on-the-database-azure"},{"id":"enable-cdc-on-the-database-azure-ex4","language":"sql","code":"-- Replace <capture-instance> with the capture instance name shown by\n-- sys.sp_cdc_help_change_data_capture\nSELECT sys.fn_cdc_map_lsn_to_time(MAX(__$start_lsn)) AS latest_captured_commit_time\nFROM cdc.<capture-instance>_CT","section_id":"enable-cdc-on-the-database-azure"},{"id":"create-a-database-user-for-debezium-ex0","language":"sql","code":"CREATE USER <username> WITH PASSWORD = '<password>'\nGO\nALTER ROLE db_datareader ADD MEMBER <username>\nGO\nGRANT VIEW DATABASE STATE TO <username>\nGO","section_id":"create-a-database-user-for-debezium"},{"id":"create-a-database-user-for-debezium-ex1","language":"sql","code":"EXEC sp_addrolemember N'<cdc-role>', N'<username>'\nGO","section_id":"create-a-database-user-for-debezium"},{"id":"create-a-database-user-for-debezium-ex2","language":"sql","code":"CREATE USER [<sp-display-name>] FROM EXTERNAL PROVIDER\n    GO\n    ALTER ROLE db_datareader ADD MEMBER [<sp-display-name>]\n    GO\n    GRANT VIEW DATABASE STATE TO [<sp-display-name>]\n    GO","section_id":"create-a-database-user-for-debezium"},{"id":"create-a-database-user-for-debezium-ex3","language":"sql","code":"EXEC sp_addrolemember N'<cdc-role>', N'<sp-display-name>'\n    GO","section_id":"create-a-database-user-for-debezium"},{"id":"configure-the-rdi-source-for-azure-sql-ex0","language":"yaml","code":"sources:\n  sqlserver:\n    type: cdc\n    connection:\n      type: sqlserver\n      host: <server-name>.database.windows.net\n      port: 1433\n      database: <database-name>\n      user: ${SQLSERVER_DB_USERNAME}\n      password: ${SQLSERVER_DB_PASSWORD}\n    logging:\n      level: info\n    schemas:\n      - dbo\n    tables:\n      <table-name>:\n        columns:\n          - <column-1>\n          - <column-2>\n        keys:\n          - <column-1>\n    advanced:\n      source:\n        driver.authentication: ActiveDirectoryServicePrincipal\n        database.encrypt: \"true\"\n        database.hostNameInCertificate: \"*.database.windows.net\"\n        database.trustServerCertificate: \"false\"\n        database.applicationIntent: ReadOnly\n        snapshot.mode: initial","section_id":"configure-the-rdi-source-for-azure-sql"},{"id":"configure-the-rdi-source-for-azure-sql-ex1","language":"bash","code":"redis-di set-secret USERNAME --db sqlserver <client-id>\nredis-di set-secret PASSWORD --db sqlserver <client-secret>","section_id":"configure-the-rdi-source-for-azure-sql"},{"id":"verify-the-connection-ex0","language":"sql","code":"-- Check whether CDC is enabled on the database\nSELECT name, is_cdc_enabled FROM sys.databases WHERE name = '<database-name>'\nGO\n\n-- Check which tables in the current database have CDC enabled\nSELECT name, is_tracked_by_cdc FROM sys.tables WHERE is_tracked_by_cdc = 1\nGO","section_id":"verify-the-connection"},{"id":"handling-changes-to-the-schema-ex0","language":"checklist {id=\"sqlserver-schema-changes\" nointeractive=\"true\" }","code":"- [ ] [Make your changes to the source table schema](#make-your-changes-to-the-source-table-schema)\n- [ ] [Create a new capture table for the updated source table](#create-a-new-capture-table-for-the-updated-source-table)\n- [ ] [Drop the old capture table](#drop-the-old-capture-table)","section_id":"handling-changes-to-the-schema"},{"id":"handling-changes-to-the-schema-ex1","language":"sql","code":"EXEC sys.sp_cdc_enable_table\n    @source_schema    = N'dbo',\n    @source_name      = N'MyTable',\n    @role_name        = N'MyRole',\n    @capture_instance = N'dbo_MyTable_v2',\n    @supports_net_changes = 0\n    GO","section_id":"handling-changes-to-the-schema"},{"id":"handling-changes-to-the-schema-ex2","language":"sql","code":"EXEC sys.sp_cdc_disable_table\n    @source_schema    = N'dbo',\n    @source_name      = N'MyTable',\n    @capture_instance = N'dbo_MyTable'\n    GO","section_id":"handling-changes-to-the-schema"},{"id":"update-the-schema-offline-ex0","language":"bash","code":"redis-di describe <pipeline-name>","section_id":"update-the-schema-offline"},{"id":"update-the-schema-offline-ex1","language":"bash","code":"redis-di stop <pipeline-name> --source <source-name>","section_id":"update-the-schema-offline"},{"id":"update-the-schema-offline-ex2","language":"bash","code":"redis-di start <pipeline-name> --source <source-name>","section_id":"update-the-schema-offline"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/prepare-dbs/supabase","title":"Prepare Supabase for RDI","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/supabase/","summary":"Prepare a hosted Supabase database to work with RDI","tags":["docs","integrate","rs","rdi"],"last_updated":"2026-10-18T03:56:52+03:00","page_type":"content","content_hash":"dbde3d9a1cbe843ee7858439d9fbe6598381afd510d5094b33ad262e08e4d7d4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Supabase](https://supabase.com/docs/guides/database/overview) is a hosted\nPostgreSQL platform. RDI can connect to a hosted Supabase project\nthrough any direct PostgreSQL endpoint as long as it is reachable from the RDI\ndeployment and supports logical replication.\n\n> [!NOTE]\n> RDI supports hosted Supabase projects running an\n> [RDI-supported PostgreSQL version](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs).\n> The integration was validated with RDI 1.19.0 and hosted Supabase PostgreSQL\n> 17.6. For self-hosted Supabase deployments, follow the general\n> [PostgreSQL preparation guide](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/postgresql).\n> This page describes Supabase setup for a self-managed RDI deployment. For the\n> managed service, see\n> [Prepare source database: Supabase](https://redis.io/docs/latest/operate/rc/rdi/setup#supabase).\n\nSupabase differs from a typical self-managed PostgreSQL source in the following\nways:\n\n- You can't edit `postgresql.conf` or `pg_hba.conf` directly. Supabase enables\n  logical replication and manages these settings for you.\n- You must use the direct database endpoint for logical replication because\n  [Supavisor connection pooler endpoints don't support logical replication](https://supabase.com/docs/guides/database/replication/manual-replication-faq#which-connection-string-should-be-used).\n- The direct endpoint uses IPv6 unless you enable the Supabase dedicated IPv4\n  add-on. Enable the add-on if your RDI deployment can't connect over IPv6.\n- Supabase can enforce TLS and provides a CA certificate that RDI can use to\n  validate the database certificate.\n- Supabase Row Level Security (RLS) can restrict the rows visible during the\n  initial snapshot.\n\nThe following checklist summarizes the setup:\n\n[code example]"},{"id":"1-create-or-select-a-supabase-project","title":"1. Create or select a Supabase project","role":"content","text":"Create a project in the [Supabase dashboard](https://supabase.com/dashboard)\nor select an existing project. You can find its PostgreSQL version in the\nSupabase dashboard or run the following query in the SQL editor:\n\n[code example]"},{"id":"2-configure-direct-network-access","title":"2. Configure direct network access","role":"content","text":"For a public connection, select **Connect** in the Supabase dashboard and copy\nthe **Direct connection** hostname. It has the following form:\n\n[code example]\n\nYou should generally use port `5432`, but you can use a private hostname or\naddress instead if you have configured private connectivity between the RDI\ndeployment and Supabase.\nDon't use a Supavisor transaction or session pooler connection string because\nthese endpoints don't support logical replication.\n\nSupabase direct connections use IPv6 by default. If your RDI deployment\ndoesn't have IPv6 egress, enable the\n[dedicated IPv4 add-on](https://supabase.com/docs/guides/platform/ipv4-address).\nThe add-on requires a paid Supabase plan.\n\nIf you use the public endpoint and enable\n[Supabase Network Restrictions](https://supabase.com/docs/guides/platform/network-restrictions),\nadd the public egress address of the RDI host or cluster to the allowlist. Use\na `/32` CIDR for an individual IPv4 address. For private connectivity, make\nsure the RDI host or cluster can resolve and route to the private endpoint."},{"id":"3-create-a-dedicated-rdi-role","title":"3. Create a dedicated RDI role","role":"content","text":"In the Supabase SQL editor, create a dedicated login for RDI. Replace the\nexample name and password with your own values:\n\n[code example]\n\n> [!WARNING]\n> Don't use the Supabase `postgres` administrator account for the RDI connection.\n> The RDI role's credentials provide continuous access to captured data, so grant\n> the role only the permissions it needs."},{"id":"4-grant-access-to-source-tables","title":"4. Grant access to source tables","role":"content","text":"The RDI role needs to connect to the database and read every table included in\nthe initial snapshot. For example:\n\n[code example]\n\nRepeat the schema grants for every schema you want RDI to capture.\n\nIf RLS is enabled on a source table, the initial snapshot only contains rows\nvisible to the RDI role. To capture all rows, define appropriate RLS policies\nfor the role or grant `BYPASSRLS`:\n\n[code example]\n\n`BYPASSRLS` applies to every table in the database. Grant it only to a\ndedicated RDI role and protect that role's credentials."},{"id":"create-a-publication","title":"Create a publication","role":"content","text":"By default, RDI uses the PostgreSQL `pgoutput` logical decoding plug-in, a\npublication named `dbz_publication`, and a replication slot named `debezium`.\nThese defaults work with Supabase if the RDI role has permission to create the\npublication and manage its source tables.\n\nIt is recommended that a database administrator create a publication\ncontaining only the tables RDI should capture:\n\n[code example]\n\nCreating the publication explicitly avoids granting table ownership or broad\npublication-creation permissions to the RDI role and limits the publication's\ntable scope. Before creating it, [check replica identity](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/postgresql#check-replica-identity-and-choose-a-publication-mode)\nfor every included table. Updates and deletes on published tables require a usable replica\nidentity. Update the publication's table list when you add or remove captured tables;\n`publication.autocreate.mode: disabled` leaves that list under your control."},{"id":"5-configure-tls","title":"5. Configure TLS","role":"content","text":"In the Supabase dashboard, go to\n[**Database settings** > **SSL configuration**](https://supabase.com/docs/guides/platform/ssl-enforcement):\n\n1. Enable **Enforce SSL on incoming connections**.\n1. Download the Supabase CA certificate.\n\nStore the database username, password, and CA certificate as RDI secrets. Pass the\nsource name with `--db` (the source configured in the example below is named `supabase`):\n\n[code example]\n\nRDI verifies that the direct endpoint hostname matches the certificate."},{"id":"6-configure-rdi","title":"6. Configure RDI","role":"content","text":"Add a PostgreSQL source to `config.yaml`. Replace the project reference and\ntable names with your values:\n\n[code example]\n\nUse a unique replication slot name for each active pipeline that connects to\nthe project."},{"id":"7-monitor-replication-slots","title":"7. Monitor replication slots","role":"content","text":"RDI creates a logical replication slot that retains write-ahead log (WAL)\nrecords while the pipeline is stopped or disconnected. Use a query like the\nfollowing to monitor inactive slots and retained WAL to prevent unexpected\nstorage growth:\n\n[code example]\n\n[Supabase requires logical replication slots to be removed](https://supabase.com/docs/guides/platform/upgrading)\nbefore a PostgreSQL major-version upgrade. Before upgrading:\n\n1. Stop the RDI pipeline.\n1. Record the pipeline configuration and slot name.\n1. Drop the RDI replication slot.\n1. Upgrade the Supabase project.\n1. Reset and start the RDI pipeline to create a new slot and initial snapshot.\n\nAllow time for the new initial snapshot to complete, and monitor the pipeline\nuntil pending records return to zero."}],"examples":[{"id":"overview-ex0","language":"checklist {id=\"supabaselist\"}","code":"- [ ] [Create or select a Supabase project](#1-create-or-select-a-supabase-project)\n- [ ] [Configure direct network access](#2-configure-direct-network-access)\n- [ ] [Create a dedicated RDI role](#3-create-a-dedicated-rdi-role)\n- [ ] [Grant access to source tables](#4-grant-access-to-source-tables)\n- [ ] [Configure TLS](#5-configure-tls)\n- [ ] [Configure RDI](#6-configure-rdi)\n- [ ] [Monitor replication slots](#7-monitor-replication-slots)","section_id":"overview"},{"id":"1-create-or-select-a-supabase-project-ex0","language":"sql","code":"SELECT version();","section_id":"1-create-or-select-a-supabase-project"},{"id":"2-configure-direct-network-access-ex0","language":"text","code":"db.<project-ref>.supabase.co","section_id":"2-configure-direct-network-access"},{"id":"3-create-a-dedicated-rdi-role-ex0","language":"sql","code":"CREATE ROLE rdi_replication\n  WITH LOGIN REPLICATION PASSWORD '<strong-password>';","section_id":"3-create-a-dedicated-rdi-role"},{"id":"4-grant-access-to-source-tables-ex0","language":"sql","code":"GRANT CONNECT ON DATABASE postgres TO rdi_replication;\n\nGRANT USAGE ON SCHEMA public TO rdi_replication;\nGRANT SELECT ON ALL TABLES IN SCHEMA public TO rdi_replication;\n\nALTER DEFAULT PRIVILEGES IN SCHEMA public\n  GRANT SELECT ON TABLES TO rdi_replication;","section_id":"4-grant-access-to-source-tables"},{"id":"4-grant-access-to-source-tables-ex1","language":"sql","code":"ALTER ROLE rdi_replication BYPASSRLS;","section_id":"4-grant-access-to-source-tables"},{"id":"create-a-publication-ex0","language":"sql","code":"CREATE PUBLICATION rdi_publication\n  FOR TABLE public.customers, public.orders;","section_id":"create-a-publication"},{"id":"5-configure-tls-ex0","language":"bash","code":"redis-di set-secret USERNAME --db supabase rdi_replication\nredis-di set-secret PASSWORD --db supabase '<strong-password>'\nredis-di set-secret CACERT --db supabase /path/to/prod-ca-2021.crt","section_id":"5-configure-tls"},{"id":"6-configure-rdi-ex0","language":"yaml","code":"sources:\n  supabase:\n    type: cdc\n    connection:\n      type: postgresql\n      host: db.<project-ref>.supabase.co\n      port: 5432\n      database: postgres\n      user: ${SUPABASE_DB_USERNAME}\n      password: ${SUPABASE_DB_PASSWORD}\n    schemas:\n      - public\n    tables:\n      public.customers: {}\n      public.orders: {}\n    advanced:\n      source:\n        plugin.name: pgoutput\n        publication.name: rdi_publication\n        publication.autocreate.mode: disabled\n        slot.name: rdi_supabase","section_id":"6-configure-rdi"},{"id":"7-monitor-replication-slots-ex0","language":"sql","code":"SELECT\n  slot_name,\n  active,\n  restart_lsn,\n  confirmed_flush_lsn\nFROM pg_replication_slots;","section_id":"7-monitor-replication-slots"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/rejected-records","title":"Rejected records","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/rejected-records/","summary":"Learn how RDI stores records that cannot be processed.","tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-18T16:39:51+01:00","page_type":"content","content_hash":"cbd705239a2e53d572fd65d167d1ff7b2f0dd0e94a0c67f9dbe2b13cc6b7e89d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Data Integration (RDI) sends records that it cannot process to a dead letter\nqueue (DLQ). In the Redis Cloud UI, these records are called **rejected records**.\n\nRejected records help you understand which source tables are affected and why\nspecific records could not continue through the pipeline. They are intended for\ntroubleshooting and support, not for normal pipeline operation."},{"id":"when-records-are-rejected","title":"When records are rejected","role":"content","text":"RDI can reject a record when it cannot safely transform or write the change event.\nCommon causes include:\n\n- The incoming change event is malformed or missing required metadata.\n- A transformation job fails while processing the record.\n- A target write fails, for example because the target key already has an incompatible data type.\n\nBy default, RDI stores rejected records instead of silently dropping them. You can\nchange this behavior with the `processors.error_handling` setting. See the\n[pipeline configuration file](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config)\nfor more information."},{"id":"how-rdi-stores-rejected-records","title":"How RDI stores rejected records","role":"content","text":"RDI stores rejected records in the RDI database as capped Redis streams. Each DLQ\nstream corresponds to a source table and tracks the records rejected for that\ntable.\n\nA DLQ stream is named after the source data stream it corresponds to, with a `dlq:` prefix:\n\n[code example]\n\nAbove, `<qualified_table_name>` is the qualified table name: `<database>.<table>`\nfor MySQL and MariaDB, `<database>.<collection>` for MongoDB, `<schema>.<table>` for Oracle,\nPostgreSQL, Snowflake, and Spanner, and `<database>.<schema>.<table>` for SQL Server.\n\nFor example, rejected records for the `public.users` table of a source named `postgresql`\nare stored in `dlq:data:{rdi}:postgresql.public.users`, and those for the `dbo.users` table of\nthe `inventory` database of a source named `sqlserver` are stored in\n`dlq:data:{rdi}:sqlserver.inventory.dbo.users`.\n\nThe dead-letter queue endpoints and the CLI report table names are in the same form,\nso you can easily attribute a rejected record to the source it came from. Sources that existed\nbefore the upgrade to RDI v2.0.0 or above do not have the source segment\nin the key because these earlier RDI versions did not support multiple sources.\n\nSome RDI versions or configurations can use a hash-tagged variant such as\n`dlq:{data:rdi:<qualified_table_name>}`. To find all DLQ streams in the\nRDI database, scan for stream keys that start with `dlq:`.\n\nThe maximum number of records stored per DLQ stream is controlled by\n`processors.dlq_max_messages`. When the stream reaches the configured limit,\nolder entries are evicted as newer entries are added."},{"id":"what-to-inspect","title":"What to inspect","role":"content","text":"Start with the rejected count for each affected table, then inspect a sample\nrecord from the table with the highest count or the table that matters most to\nyour application.\n\nUseful fields include:\n\n- The affected table.\n- The rejection time.\n- The rejected operation. RDI stores this as an `opcode` value such as `c`,\n  `u`, `d`, `r`, `t`, or `m`. See [Using the operation code](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-opcode-example)\n  for the operation labels.\n- The rejection reason.\n- The transformation job or operation, when the failure happened during transformation.\n\n> [!NOTE]\n> Rejected records can contain source data when you inspect them directly in the\n> RDI database. Treat DLQ contents as sensitive customer data. The Redis Cloud UI\n> uses the RDI DLQ API and shows a sanitized set of troubleshooting metadata. It\n> does not show the original record payload or every field stored in the\n> corresponding DLQ stream."},{"id":"resolve-rejected-records","title":"Resolve rejected records","role":"content","text":"Use the rejection reason to identify the likely fix:\n\n- If a transformation job failed, update the job configuration and deploy the pipeline change.\n- If target writes failed because of incompatible existing keys, update the target data or key mapping.\n- If records are malformed, inspect the source connector and source database change data capture configuration.\n\nRDI does not automatically replay records from the DLQ after you fix the cause.\nIf you need existing source data to be processed again, reset the pipeline after\napplying the fix. See [Reset data pipeline](https://redis.io/docs/latest/operate/rc/rdi/view-edit#reset-data-pipeline)\nfor Redis Cloud, or use the appropriate self-managed RDI reset workflow."},{"id":"cli-and-api-access","title":"CLI and API access","role":"content","text":"For self-managed RDI, use the [`redis-di list-dlqs`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list-dlqs)\ncommand to see the dead-letter queues and the\n[`redis-di list-dlq-records`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list-dlq-records)\ncommand (also available as `redis-di get-rejected`) to inspect the rejected records of a queue.\n\nFor Redis Cloud RDI, connect to the RDI database and inspect the corresponding\nDLQ streams directly when you need details that are not shown in the Redis Cloud\nUI.\n\nRDI API v2 also includes DLQ inspection endpoints. See the\n[API reference](https://redis.io/docs/latest/integrate/redis-data-integration/reference/api-reference)\nfor endpoint details."}],"examples":[{"id":"how-rdi-stores-rejected-records-ex0","language":"text","code":"dlq:data:{rdi}:<source>.<qualified_table_name>","section_id":"how-rdi-stores-rejected-records"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/supported-types","title":"Supported data types by source","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/supported-types/","summary":"Learn about supported data types for each source database.","tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-18T16:39:51+01:00","page_type":"content","content_hash":"aa04d61746189f87768095c8502d888fda84d2047c08c9d44c4337378f82b6eb","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This page describes the source data types that RDI captures for the\n[Oracle](#oracle), [MySQL/MariaDB](#mysql-and-mariadb),\n[PostgreSQL](#postgresql-supabase-and-alloydb), [SQL Server](#sql-server),\n[MongoDB](#mongodb), and [Spanner](#spanner) source databases, and how they are\nrepresented in Redis. There are also some\n[cross-cutting considerations](#cross-cutting-considerations) that apply to all\nsource databases."},{"id":"how-rdi-captures-and-represents-data","title":"How RDI captures and represents data","role":"content","text":"For most source databases, RDI uses an embedded\n[Debezium](https://debezium.io/) connector as its change data capture (CDC)\n*collector*. RDI ships a Debezium 3.x–based collector, so the collector-level\nmappings on this page follow the\n[Debezium connector reference](https://debezium.io/documentation/reference/).\n[Google Cloud Spanner](#spanner) is the exception: it uses a Flink-based collector\nthat reads Spanner change streams rather than Debezium (see the\n[Spanner section](#spanner) for details).\n\n> [!NOTE]\n> **RDI does not always pass the collector value through unchanged.** RDI's\n> processors normalize several Debezium logical types before they reach your jobs and\n> Redis, and drop a few that they cannot represent. The collector-level representations\n> in the tables below are therefore the *input* to RDI's processing, not always the\n> final Redis value — the per-type notes and [cross-cutting considerations](#cross-cutting-considerations)\n> call out where RDI transforms or drops a value.\n\nIt helps to think of the data flow in two layers:\n\n1. **What the collector emits.** Debezium converts each source column to a\n   Kafka Connect value with a *literal type* (for example, `STRING`, `INT64`,\n   `BYTES`, `STRUCT`) and an optional *semantic type* (for example,\n   `io.debezium.time.MicroTimestamp`). Several of these conversions are controlled\n   by connector properties such as\n   [`decimal.handling.mode`](#decimal-and-numeric-values),\n   [`binary.handling.mode`](#binary-values), and\n   [`time.precision.mode`](#temporal-values). The tables below show the\n   representation that each connector produces with its **default** settings.\n2. **How RDI writes it to Redis.** RDI's processors take the collector value,\n   normalize some logical types (and drop a few unsupported ones), then write each\n   record to a Redis [Hash](https://redis.io/docs/latest/develop/data-types/hashes) (the default)\n   or, if you set `target_data_type: json`, to a\n   [JSON](https://redis.io/docs/latest/develop/data-types/json) document. For Hash targets,\n   every field value is stored as a string; for JSON targets, numbers and booleans\n   are stored as native JSON values.\n\nWhen you need to reformat a value in a job, see\n[Formatting date and time values](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/formatting-date-and-time-values)\nfor worked examples."},{"id":"setting-collector-properties","title":"Setting collector properties","role":"configuration","text":"Where the sections below recommend a Debezium property (for example,\n`decimal.handling.mode` or `lob.enabled`), set it in the `advanced.source` block of\nthe source in your pipeline `config.yaml` file. These properties are passed\nthrough to the underlying Debezium connector. For example:\n\n[code example]\n\nSee [Pipeline configuration](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config)\nfor more about the `advanced` section."},{"id":"quick-configuration-summary","title":"Quick configuration summary","role":"content","text":"The lists below summarize the extra configuration you may need for each source\ndatabase. Each database has its own section with full detail.\n\n[**Oracle**](#oracle)\n\n- Enable [supplemental logging](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/oracle) on the tables/schemas you capture.\n- Set `lob.enabled: true` if you need `CLOB`, `NCLOB`, `BLOB`, or `XMLTYPE`.\n- Choose `binary.handling.mode` for `RAW`/`BLOB` (default is `bytes`).\n- `decimal.handling.mode` defaults to `string` in RDI; set `double` for numeric `NUMBER`/`DECIMAL` values.\n- Avoid unsupported types (`LONG`, `LONG RAW`, `BFILE`, `UROWID`, `VECTOR`, UDTs, spatial) or cast them upstream.\n\n[**MySQL/MariaDB**](#mysql-and-mariadb)\n\n- Enable the binary log in **ROW** mode.\n- Choose `decimal.handling.mode` to balance precision against convenience.\n- For `BOOLEAN`/`TINYINT(1)` fidelity, consider the `TinyIntOneToBooleanConverter`.\n- Be aware that spatial and `VECTOR` types arrive as structured values, not scalars.\n\n[**PostgreSQL/Supabase/AlloyDB**](#postgresql-supabase-and-alloydb)\n\n- Ensure WAL/logical replication settings match the connector's needs.\n- `decimal.handling.mode` defaults to `string` in RDI; set `double` for numeric values.\n- Use a RedisJSON target to get the most value from `JSON`/`JSONB`.\n\n[**SQL Server**](#sql-server)\n\n- Enable CDC at both the database and table level.\n- Choose `decimal.handling.mode` for `MONEY`/`DECIMAL` precision.\n- Choose `time.precision.mode` if you need predictable temporal precision.\n\n[**MongoDB**](#mongodb)\n\n- Ensure a replica set and change streams are configured.\n- Use a RedisJSON target to preserve document structure.\n- Choose `capture.mode` to control whether updates include the full document.\n\n[**Spanner**](#spanner)\n\n- Spanner uses the Flink-based collector (not Debezium) and is supported only on Kubernetes/Helm."},{"id":"cross-cutting-considerations","title":"Cross-cutting considerations","role":"content","text":"The settings below apply to all of the Debezium-based source connectors. They are\nthe most common cause of \"the value in Redis doesn't look like the value in my\ndatabase\", so review them before reading the per-database sections."},{"id":"decimal-and-numeric-values","title":"Decimal and numeric values","role":"content","text":"`DECIMAL`, `NUMERIC`, `MONEY`, and similar types are controlled by\n`decimal.handling.mode`. **RDI's effective default is `string`** — the RDI collector\ntemplates set `debezium.source.decimal.handling.mode=string` before your\n`advanced.source` overrides apply, so decimals reach Redis as readable strings rather\nthan Debezium's own `precise` binary default:\n\n| `decimal.handling.mode` | Representation                                                              |\n|-------------------------|----------------------------------------------------------------------------|\n| `string` (RDI default)  | The exact decimal as a `STRING`.                                            |\n| `double`                | A `FLOAT64` number (may lose precision for very large/precise values).      |\n| `precise`               | A Kafka Connect `Decimal` (`BYTES`) — a base64-encoded, scaled binary value. This is Debezium's default but not RDI's. |\n\nLeave the default (`string`) for exact decimal fidelity, or set\n`decimal.handling.mode: double` if you want numeric values and can accept double\nprecision."},{"id":"temporal-values","title":"Temporal values","role":"content","text":"Temporal types are controlled by `time.precision.mode`. The default is `adaptive`\nfor Oracle, PostgreSQL, and SQL Server, and `adaptive_time_microseconds` for MySQL\nand MariaDB. In adaptive modes, the precision of the emitted value depends on the\ncolumn's declared precision:\n\n- `DATE` columns are emitted as **days since epoch** (an `INT32`), *not*\n  milliseconds at midnight.\n- `TIME`/`DATETIME`/`TIMESTAMP` columns are emitted as milliseconds, **microseconds**,\n  or **nanoseconds** since epoch (or since midnight for time-of-day types) depending\n  on their precision. For example, an Oracle `TIMESTAMP(6)` or a PostgreSQL\n  `timestamp` is emitted as microseconds.\n\nRDI's processors may normalize these Debezium temporal types before they reach your\njobs and Redis (for example, converting a `Date` to epoch milliseconds). See\n[Formatting date and time values](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/formatting-date-and-time-values)\nfor worked examples of converting temporal values in an RDI job."},{"id":"time-zones","title":"Time zones","role":"content","text":"Time zone–aware types (for example, Oracle `TIMESTAMP WITH TIME ZONE`, PostgreSQL\n`timestamptz`, SQL Server `datetimeoffset`, and MySQL `TIMESTAMP`) are **not**\nconverted to epoch milliseconds. They are emitted as **ISO 8601 strings**\n(semantic type `io.debezium.time.ZonedTimestamp`), normalized to UTC/GMT — for\nexample, `2025-06-07T10:15:00.000000Z`."},{"id":"binary-values","title":"Binary values","role":"content","text":"Binary columns are controlled by `binary.handling.mode`. The default is **`bytes`**\n(raw bytes), *not* base64. The options are:\n\n- `bytes` (default) — raw byte array.\n- `base64` — base64-encoded string.\n- `base64-url-safe` — URL-safe base64 string.\n- `hex` — hex string.\n\nSet `binary.handling.mode: base64` (or `hex`) if your consumers expect an encoded\nstring rather than raw bytes. Make sure your consumer understands the encoding you\nchoose."},{"id":"large-objects-lobs-and-unavailable-values","title":"Large objects (LOBs) and unavailable values","role":"content","text":"When a connector captures large objects (for example, Oracle `CLOB`/`BLOB`), an\nupdate event never contains the value of an *unchanged* LOB column. Instead, the\ncolumn contains a placeholder. The default placeholder is `__debezium_unavailable_value`,\nwhich you can change with `advanced.source.unavailable.value.placeholder` on the\nsource (the Helm chart exposes this as `processor.lob.placeholder`). RDI skips these\nplaceholder values rather than writing them to Redis as user data."},{"id":"nullability","title":"Nullability","role":"content","text":"- **Redis Hashes**: null values are not stored (the field is absent).\n- **RedisJSON**: null values become JSON `null`. Note that if you use the native\n  `JSON.MERGE` command (the default from RDI 1.15.0, controlled by\n  `use_native_json_merge`), merging a `null` value *removes* the field rather than\n  storing it, following [RFC 7396](https://datatracker.ietf.org/doc/html/rfc7396).\n  See [Pipeline configuration](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config)."},{"id":"structured-values-structs-arrays-and-maps","title":"Structured values (structs, arrays, and maps)","role":"content","text":"Some source types are emitted as Kafka Connect `STRUCT`, `ARRAY`, or `MAP` values\nrather than scalars — for example, spatial types (a struct of `srid` + `wkb`) and\nvector types (an array of floats).\n\n**RDI does not support every complex logical type.** In particular, RDI treats\n`io.debezium.data.Bits` (from `BIT(>1)`/`BIT VARYING`) and the interval logical types\nas **unsupported**: the classic processor maps them to `None` and the Flink processor\nremoves the field (via `RemovalConverter`), so they do not reach Redis. Other\nstructured values (spatial `Geometry`, pgvector) pass through to RDI's processors,\nbut how they are rendered into a Redis Hash or JSON document is noted per type below."},{"id":"oracle","title":"Oracle","role":"content","text":"RDI captures Oracle changes via the\n[Debezium Oracle connector](https://debezium.io/documentation/reference/3.0/connectors/oracle.html).\nSee [Prepare Oracle for RDI](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/oracle)\nfor the required supplemental-logging setup."},{"id":"supported-types","title":"Supported types","role":"compatibility","text":"| Oracle type | Collector representation (default settings) | Notes |\n|-------------|---------------------------------------------|-------|\n| `NUMBER`, `DECIMAL`, `NUMERIC`, `INT`, `INTEGER`, `SMALLINT` | Kafka Connect `Decimal` (`BYTES`) | Controlled by `decimal.handling.mode`. See [Decimal and numeric values](#decimal-and-numeric-values). |\n| `NUMBER(p,*)`, `FLOAT`, `REAL`, `DOUBLE PRECISION` | `VariableScaleDecimal` (`STRUCT`) | Variable-scale decimal. |\n| `BINARY_FLOAT` | `FLOAT32` | |\n| `BINARY_DOUBLE` | `FLOAT64` | |\n| `CHAR`, `VARCHAR`, `VARCHAR2`, `NCHAR`, `NVARCHAR2` | `STRING` | UTF-8 preserved. |\n| `DATE` | `Timestamp` (`INT64`, ms since epoch) | |\n| `TIMESTAMP(0-3)` | `Timestamp` (ms) | Precision depends on the column; see [Temporal values](#temporal-values). |\n| `TIMESTAMP(4-6)` | `MicroTimestamp` (µs) | A bare `TIMESTAMP` defaults to precision 6 (microseconds). |\n| `TIMESTAMP(7-9)` | `NanoTimestamp` (ns) | |\n| `TIMESTAMP WITH TIME ZONE` | `ZonedTimestamp` (`STRING`, ISO 8601) | See [Time zones](#time-zones). |\n| `TIMESTAMP WITH LOCAL TIME ZONE` | `ZonedTimestamp` (`STRING`, UTC) | |\n| `INTERVAL YEAR TO MONTH`, `INTERVAL DAY TO SECOND` | `MicroDuration` (`INT64`) | **Not supported by RDI** — interval types are dropped before reaching Redis. See [Structured values](#structured-values-structs-arrays-and-maps). |\n| `CLOB`, `NCLOB` | `STRING` | Requires `lob.enabled: true`. |\n| `BLOB` | `BYTES` | Requires `lob.enabled: true`; encoded per `binary.handling.mode`. |\n| `RAW` | `BYTES` | Encoded per `binary.handling.mode`. |\n| `XMLTYPE` | `Xml` (`STRING`) | **Incubating** in Debezium. Requires `lob.enabled: true` and a non-hybrid mining strategy. |\n| `ROWID` | `STRING` | Supported in LogMiner mode only; not exposed when using XStream. |"},{"id":"configuration-notes","title":"Configuration notes","role":"configuration","text":"- **LOBs**: set `lob.enabled: true` (default `false`) to capture `CLOB`, `NCLOB`,\n  `BLOB`, and `XMLTYPE`. You cannot use the *hybrid* mining strategy with\n  `lob.enabled: true` — use `online_catalog` or `redo_log_catalog` instead.\n- **Extended strings**: if the database parameter `max_string_size` is `EXTENDED`,\n  set `lob.enabled: true` to capture `VARCHAR2`/`NVARCHAR2` values over 4000 bytes\n  and `RAW` values over 2000 bytes.\n- **XMLTYPE**: requires `lob.enabled: true` and a non-hybrid mining strategy\n  (`online_catalog` or `redo_log_catalog`). The connector emits the XML as text\n  (`STRING`). XMLTYPE support also requires the Oracle **XDB library** and the\n  **`xmlparserv2`** dependency in addition to the standard `ojdbc11.jar` driver. If\n  the runtime selects Oracle's `xmlparserv2` SAX parser, you may need to set the JVM\n  option `-Djavax.xml.parsers.SAXParserFactory=com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl`."},{"id":"not-captured","title":"Not captured","role":"content","text":"The Debezium Oracle connector does not support `LONG`, `LONG RAW`, `BFILE`,\n`UROWID`, `VECTOR`, the native Oracle 23 `BOOLEAN` column type, user-defined/object\ntypes (objects, `REF`, `VARRAY`, nested tables), or Oracle spatial types. Cast these\nto a supported type upstream if you need them. A `NumberOneToBooleanConverter` is\navailable to map `NUMBER(1)` columns to booleans."},{"id":"mysql-and-mariadb","title":"MySQL and MariaDB","role":"content","text":"RDI captures both `mysql` and `mariadb` sources with the\n[Debezium MySQL connector](https://debezium.io/documentation/reference/stable/connectors/mysql.html)\n(`io.debezium.connector.mysql.MySqlConnector`) — it does not use Debezium's separate\nMariaDB connector. The mappings below therefore apply to both source types. See\n[Prepare MySQL/MariaDB for RDI](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/my-sql-mariadb)\nfor setup. Enable the binary log in **ROW** mode."},{"id":"supported-types-1","title":"Supported types","role":"compatibility","text":"| MySQL/MariaDB type | Collector representation (default settings) | Notes |\n|--------------------|---------------------------------------------|-------|\n| `TINYINT`, `SMALLINT`, `MEDIUMINT`, `INT`, `BIGINT` | `INT8`/`INT16`/`INT32`/`INT64` | |\n| `BIT(1)` | `BOOLEAN` | A single bit is mapped to a boolean. |\n| `BIT(>1)` | `Bits` (`BYTES`) | **Not supported by RDI** — dropped before reaching Redis. See [Structured values](#structured-values-structs-arrays-and-maps). |\n| `DECIMAL`, `NUMERIC` | Kafka Connect `Decimal` (`BYTES`) | Controlled by `decimal.handling.mode`. See [Decimal and numeric values](#decimal-and-numeric-values). |\n| `FLOAT(0-23)`, `REAL` | `FLOAT32` | |\n| `FLOAT(24-53)`, `DOUBLE` | `FLOAT64` | |\n| `CHAR`, `VARCHAR`, `TINYTEXT`, `TEXT`, `MEDIUMTEXT`, `LONGTEXT` | `STRING` | |\n| `BINARY`, `VARBINARY`, `TINYBLOB`, `BLOB`, `MEDIUMBLOB`, `LONGBLOB` | `BYTES` | Encoded per `binary.handling.mode`. Up to 2 GB; use the claim-check pattern for large values. |\n| `DATE` | `Date` (days since epoch) | See [Temporal values](#temporal-values). |\n| `TIME` | `MicroTime` (µs since midnight) | Default `time.precision.mode` is `adaptive_time_microseconds`. |\n| `DATETIME` | `Timestamp`/`MicroTimestamp` by precision | |\n| `TIMESTAMP` | `ZonedTimestamp` (`STRING`, ISO 8601, UTC) | Not epoch ms. See [Time zones](#time-zones). |\n| `YEAR` | `io.debezium.time.Year` (`INT32`) | |\n| `BOOLEAN`, `BOOL` | `BOOLEAN` | During snapshots the connector sees `TINYINT(1)`; use `TinyIntOneToBooleanConverter` for consistent boolean fidelity. |\n| `ENUM` | `io.debezium.data.Enum` (`STRING`) | The `allowed` schema parameter lists the permitted values. |\n| `SET` | `io.debezium.data.EnumSet` (`STRING`) | Comma-separated selected values. |\n| `JSON` | `io.debezium.data.Json` (`STRING`) | Parsed into a nested structure on a RedisJSON target. |\n| `VECTOR` | `ARRAY (FLOAT32)`, `io.debezium.data.FloatVector` | See [Structured values](#structured-values-structs-arrays-and-maps). |\n| Spatial: `GEOMETRY`, `POINT`, `LINESTRING`, `POLYGON`, `MULTIPOINT`, `MULTILINESTRING`, `MULTIPOLYGON`, `GEOMETRYCOLLECTION` | `io.debezium.data.geometry.Geometry` (`STRUCT`) | A struct with `srid` (`INT32`) and `wkb` (`BYTES`, Well-Known Binary). |"},{"id":"booleans","title":"Booleans","role":"content","text":"MySQL and MariaDB both represent `BOOLEAN`/`BOOL` as `TINYINT(1)`. Because RDI uses\nthe MySQL connector for both, the connector may report these columns as `TINYINT(1)`\nrather than `BOOLEAN` (especially during snapshots). Use the\n`TinyIntOneToBooleanConverter` for consistent boolean fidelity."},{"id":"postgresql-supabase-and-alloydb","title":"PostgreSQL, Supabase, and AlloyDB","role":"content","text":"RDI captures PostgreSQL changes via the\n[Debezium PostgreSQL connector](https://debezium.io/documentation/reference/3.0/connectors/postgresql.html)\nusing logical replication. **Supabase** and **AlloyDB** are PostgreSQL-compatible\nand use the same connector, so the mappings below apply to all three. See\n[Prepare PostgreSQL for RDI](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/postgresql)."},{"id":"supported-types-2","title":"Supported types","role":"compatibility","text":"| PostgreSQL type | Collector representation (default settings) | Notes |\n|-----------------|---------------------------------------------|-------|\n| `SMALLINT`, `INTEGER`, `BIGINT` | `INT16`/`INT32`/`INT64` | `SMALLSERIAL`/`SERIAL`/`BIGSERIAL` map the same as their integer base. |\n| `OID` | `INT64` | |\n| `NUMERIC`, `DECIMAL` | Kafka Connect `Decimal` (`BYTES`), or `VariableScaleDecimal` when unscaled | Controlled by `decimal.handling.mode`. See [Decimal and numeric values](#decimal-and-numeric-values). |\n| `MONEY` | Kafka Connect `Decimal` (`BYTES`) | Scale set by `money.fraction.digits`. |\n| `REAL` | `FLOAT32` | |\n| `DOUBLE PRECISION` | `FLOAT64` | |\n| `BOOLEAN` | `BOOLEAN` | |\n| `BIT(1)` | `BOOLEAN` | |\n| `BIT(>1)`, `BIT VARYING` | `Bits` (`BYTES`) | **Not supported by RDI** — dropped before reaching Redis. See [Structured values](#structured-values-structs-arrays-and-maps). |\n| `CHAR`, `VARCHAR`, `TEXT`, `CITEXT` | `STRING` | |\n| `BYTEA` | `BYTES` | Encoded per `binary.handling.mode`. Requires `bytea_output = hex` in PostgreSQL. |\n| `DATE` | `Date` (days since epoch) | See [Temporal values](#temporal-values). |\n| `TIME` | `MicroTime` (µs since midnight) | |\n| `TIME WITH TIME ZONE` (`TIMETZ`) | `ZonedTime` (`STRING`, GMT) | For example, `07:15:00Z`. |\n| `TIMESTAMP` | `MicroTimestamp` (µs since epoch) | See [Temporal values](#temporal-values). |\n| `TIMESTAMP WITH TIME ZONE` (`TIMESTAMPTZ`) | `ZonedTimestamp` (`STRING`, GMT) | See [Time zones](#time-zones). |\n| `INTERVAL` | `MicroDuration` (`INT64`) | **Not supported by RDI** — interval types are dropped before reaching Redis. See [Structured values](#structured-values-structs-arrays-and-maps). |\n| `UUID` | `io.debezium.data.Uuid` (`STRING`) | |\n| `INET`, `CIDR`, `MACADDR`, `MACADDR8` | `STRING` | |\n| `JSON`, `JSONB` | `io.debezium.data.Json` (`STRING`) | Parsed into a nested structure on a RedisJSON target. |\n| `HSTORE` | `io.debezium.data.Json` (`STRING`) | Default `hstore.handling.mode` is `json` (for example, `{\"key\":\"val\"}`); set `map` for a `MAP` value. |\n| `XML` | `io.debezium.data.Xml` (`STRING`) | |\n| `LTREE` | `io.debezium.data.Ltree` (`STRING`) | |\n| `TSVECTOR` | `io.debezium.data.Tsvector` (`STRING`) | |\n| Range types (`INT4RANGE`, `INT8RANGE`, `NUMRANGE`, `TSRANGE`, `TSTZRANGE`, `DATERANGE`) | `STRING` | |\n| `ENUM` | `io.debezium.data.Enum` (`STRING`) | |\n| pgvector `VECTOR` | `ARRAY (FLOAT64)`, `io.debezium.data.DoubleVector` | Supabase and AlloyDB commonly enable pgvector. |\n| pgvector `HALFVEC` | `ARRAY (FLOAT32)`, `io.debezium.data.FloatVector` | |\n| pgvector `SPARSEVEC` | `STRUCT`, `io.debezium.data.SparseVector` | `dimensions` (`INT16`) + `vector` (`MAP(INT16, FLOAT64)`). |\n| PostGIS `GEOMETRY` | `io.debezium.data.geometry.Geometry` (`STRUCT`) | `srid` (`INT32`) + `wkb` (`BYTES`). |\n| PostGIS `GEOGRAPHY` | `io.debezium.data.geometry.Geography` (`STRUCT`) | |\n| Native `POINT` | `io.debezium.data.geometry.Point` (`STRUCT`) | Two `FLOAT64` fields (`x`, `y`). |\n\nDomain types (user-defined types based on an underlying type) are captured using\ntheir base type's representation.\n\nThe Debezium 3.0 PostgreSQL reference does not explicitly document how native array\ncolumns (for example, `int[]` or `text[]`) are captured — the Kafka Connect `ARRAY`\nliteral type is used in the reference only for the pgvector types above. In practice,\nthe connector represents arrays of supported primitive types as `ARRAY` values, but\nthis is not stated in the reference."},{"id":"not-captured-1","title":"Not captured","role":"content","text":"The connector does not capture the native geometric types `LINE`, `LSEG`, `BOX`,\n`PATH`, `POLYGON`, and `CIRCLE`, or true composite/row types. Cast these upstream if\nyou need them."},{"id":"sql-server","title":"SQL Server","role":"content","text":"RDI captures SQL Server changes via the\n[Debezium SQL Server connector](https://debezium.io/documentation/reference/3.0/connectors/sqlserver.html).\nCDC must be enabled at both the database and table level. See\n[Prepare SQL Server for RDI](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/sql-server)."},{"id":"supported-types-3","title":"Supported types","role":"compatibility","text":"| SQL Server type | Collector representation (default settings) | Notes |\n|-----------------|---------------------------------------------|-------|\n| `TINYINT`, `SMALLINT`, `INT`, `BIGINT` | `INT16`/`INT16`/`INT32`/`INT64` | |\n| `BIT` | `BOOLEAN` | |\n| `DECIMAL`, `NUMERIC` | Kafka Connect `Decimal` (`BYTES`) | Controlled by `decimal.handling.mode`. See [Decimal and numeric values](#decimal-and-numeric-values). |\n| `MONEY`, `SMALLMONEY` | Kafka Connect `Decimal` (`BYTES`) | |\n| `REAL` | `FLOAT32` | |\n| `FLOAT[(N)]` | `FLOAT64` | |\n| `CHAR`, `VARCHAR`, `NCHAR`, `NVARCHAR`, `TEXT`, `NTEXT` | `STRING` | |\n| `XML` | `io.debezium.data.Xml` (`STRING`) | |\n| `DATE` | `Date` (days since epoch) | Not \"ms at midnight\". See [Temporal values](#temporal-values). |\n| `TIME(0-3)` | `Time` (ms since midnight) | |\n| `TIME(4-6)` | `MicroTime` (µs since midnight) | |\n| `TIME(7)` | `NanoTime` (ns since midnight) | |\n| `DATETIME`, `SMALLDATETIME` | `Timestamp` (ms since epoch) | |\n| `DATETIME2(0-3)` | `Timestamp` (ms) | |\n| `DATETIME2(4-6)` | `MicroTimestamp` (µs) | |\n| `DATETIME2(7)` | `NanoTimestamp` (ns) | |\n| `DATETIMEOFFSET` | `ZonedTimestamp` (`STRING`, GMT) | See [Time zones](#time-zones). |\n| `BINARY`, `VARBINARY` | `BYTES` | Encoded per `binary.handling.mode` (default `bytes`). Not in the reference's mapping tables, but handled via the `binary.handling.mode` property. |"},{"id":"types-requiring-confirmation","title":"Types requiring confirmation","role":"content","text":"The previous version of this page documented `UNIQUEIDENTIFIER`,\n`ROWVERSION`/`TIMESTAMP` (the row-version column type), `sql_variant`, `hierarchyid`,\n`IMAGE`, and the spatial types (`geometry`, `geography`) for SQL Server. None of these\nappear in the Debezium 3.0 SQL Server connector reference's data type mapping tables.\n\nNote that *absence from the reference's tables does not necessarily mean a type is\nunsupported* — `BINARY` and `VARBINARY`, for example, are handled via the\n`binary.handling.mode` property even though they have no mapping-table row. So these\ntypes should be confirmed empirically rather than assumed unsupported."},{"id":"mongodb","title":"MongoDB","role":"content","text":"RDI captures MongoDB changes via the\n[Debezium MongoDB connector](https://debezium.io/documentation/reference/3.0/connectors/mongodb.html),\nwhich works differently from the relational connectors. See\n[Prepare MongoDB for RDI](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/mongodb)."},{"id":"what-the-collector-emits","title":"What the collector emits","role":"content","text":"The MongoDB connector does **not** map each BSON field to a separate typed value.\nInstead, it emits the whole document as a **single JSON string** using MongoDB\n[extended JSON, strict mode](https://www.mongodb.com/docs/manual/reference/mongodb-extended-json/).\nBSON values appear inside that string using extended-JSON wrappers, for example:\n\n| BSON type | Extended-JSON representation |\n|-----------|------------------------------|\n| `ObjectId` | `{\"$oid\": \"596e275826f08b2730779e1f\"}` |\n| `Int32` / `Int64` | `1234` / `{\"$numberLong\": \"1234\"}` |\n| `Double` | a JSON number |\n| `Decimal128` | `{\"$numberDecimal\": \"...\"}` |\n| `Date` | `{\"$date\": ...}` |\n| `Timestamp` (BSON) | `{\"$timestamp\": {\"t\": ..., \"i\": ...}}` |\n| `Binary` | `{\"$binary\": \"...\", \"$type\": \"00\"}` |\n| `Boolean` | `true` / `false` |\n| `Null` | `null` |\n| Regular expression | `{\"$regularExpression\": {\"pattern\": \"...\", \"options\": \"...\"}}` |\n| JavaScript | `{\"$code\": \"...\"}` |\n| `MinKey` / `MaxKey` | `{\"$minKey\": 1}` / `{\"$maxKey\": 1}` |\n\nThe document's `_id` is placed in the change event **key** (as an extended-JSON\nstring). It can be any BSON type — it is only a 24-character hex value when it is an\n`ObjectId`.\n\nWhat is available for updates depends on `capture.mode`:\n\n- A *create* event always includes the full document.\n- An *update* event includes the full document only when `capture.mode` is\n  `change_streams_update_full`; otherwise it includes only the changed fields\n  (`updatedFields`/`removedFields`). A `*_with_pre_image` mode is required to include\n  the prior document state.\n\nDocuments larger than the 16 MB BSON limit require `oversize.handling.mode` (and\nMongoDB 6.0.9+)."},{"id":"how-rdi-maps-it-to-redis","title":"How RDI maps it to Redis","role":"content","text":"RDI parses the collector's JSON string and writes the result to your Redis target:\n\n- With a RedisJSON target, the document structure (nested objects and arrays) is\n  preserved.\n- With a Hash target, nested objects and arrays are stored as stringified JSON.\n- RDI typically derives the Redis key (in whole or in part) from the document's\n  `_id`."},{"id":"spanner","title":"Spanner","role":"content","text":"RDI supports [Google Cloud Spanner](https://cloud.google.com/spanner) as a source,\nbut **Spanner does not use Debezium**. During the snapshot phase RDI reads Spanner\ndirectly over JDBC, and during streaming it consumes\n[Spanner change streams](https://cloud.google.com/spanner/docs/change-streams) via a\nFlink-based collector (`type: flink`). Spanner is supported only when RDI is deployed\non Kubernetes/Helm. See\n[Prepare Spanner for RDI](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/spanner)\nfor setup.\n\nBecause Spanner uses a different collector, its data type handling is not governed by\nthe Debezium settings described elsewhere on this page. There is no Debezium or Flink\ntype-mapping reference to consult: Flink is only the stream-processing runtime, and\nneither Flink core nor Flink CDC provides a Spanner connector. The representation of\neach value comes from Spanner itself."},{"id":"supported-types-4","title":"Supported types","role":"compatibility","text":"During the streaming phase, values arrive in Spanner's\n[change stream record format](https://cloud.google.com/spanner/docs/change-streams/details),\nin which each value is JSON-encoded according to the Spanner\n[`TypeCode`](https://cloud.google.com/spanner/docs/reference/rest/v1/Type) reference\n(the same encoding that the record's `column_types` metadata points to). The table\nbelow uses GoogleSQL type names; the PostgreSQL dialect uses different type names\n(for example, `bigint`, `bytea`, `timestamptz`, `jsonb`) but the same value encoding.\n\n| Spanner type (GoogleSQL) | Change-stream representation |\n|--------------------------|------------------------------|\n| `BOOL` | JSON `true`/`false`. |\n| `INT64` | A `STRING` in decimal format (not a JSON number). |\n| `FLOAT32`, `FLOAT64` | A JSON number, or the strings `\"NaN\"`, `\"Infinity\"`, `\"-Infinity\"`. |\n| `NUMERIC` | A `STRING` in decimal or scientific notation. |\n| `STRING` | A `STRING`. |\n| `BYTES` | A base64-encoded `STRING` (RFC 4648). |\n| `JSON` | A JSON-formatted `STRING` (RFC 7159). |\n| `TIMESTAMP` | A `STRING` in RFC 3339 format, time zone `Z` (UTC). |\n| `DATE` | A `STRING` in RFC 3339 date format. |\n| `UUID` | A lower-case hexadecimal `STRING` (RFC 9562). |\n| `ENUM` | A `STRING` in decimal format. |\n| `ARRAY` | A JSON list of elements encoded per the element type. |\n| `STRUCT` | A JSON list of field values encoded per the field types. |\n\nThis table shows the **raw change-stream representation** only. RDI's Spanner\ncollector parses some of these values before they become change events — for\nexample, the string-encoded `INT64` values are parsed to numbers — and the snapshot\nphase reads through the Spanner JDBC path, which may not match the change-stream\nencoding. The RDI job/target representation is therefore not always identical to the\nraw encoding above."}],"examples":[{"id":"setting-collector-properties-ex0","language":"yaml","code":"sources:\n  my-source:\n    # ...connection details...\n    advanced:\n      source:\n        decimal.handling.mode: double\n        binary.handling.mode: base64","section_id":"setting-collector-properties"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/transform-examples/caching-expression-results","title":"Caching expression results","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/caching-expression-results/","summary":"","tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-18T16:39:51+01:00","page_type":"content","content_hash":"be07c7b4d7630c84a6f6463e78be17001d005fc985b06a416f2d2b6c4e6a8e5a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The Flink processor can cache the result of any expression that\nproduces a value (for example, an\n[`add_field`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation/add_field)\nexpression, a [`map`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation/map)\nexpression, the arguments to a\n[`redis.lookup`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation/lookup),\nor a custom output `key`/`expire` expression). Caching is useful when\nthe same expression is evaluated repeatedly with the same input field\nvalues, for example when many incoming records share a common foreign\nkey.\n\n> [!NOTE]\n> Caching is supported only by the **Flink processor**. The\n> classic processor silently ignores `cache:` blocks."},{"id":"the-cache-block","title":"The `cache:` block","role":"content","text":"You enable caching by adding a `cache:` block next to the expression\nyou want to cache. Cache keys are derived from the values of the input\nfields referenced by the expression, not from the full record. See\n[`cache`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation/cache)\nfor the full property list.\n\n| Property      | Type      | Description                                                    | Default |\n| ------------- | --------- | -------------------------------------------------------------- | ------- |\n| `enabled`     | `boolean` | Set to `true` to enable caching.                               | `false` |\n| `max_size`    | `integer` | Maximum number of entries kept in the cache. Must be positive. | `1000`  |\n| `ttl_seconds` | `integer` | Time-to-live for each entry, in seconds. Must be positive.     | `60`    |"},{"id":"caching-an-add_field-expression","title":"Caching an `add_field` expression","role":"content","text":"The example below adds a `country` field whose value is derived from\n`country_code` and `country_name`. When the same combination of input\nvalues appears repeatedly (for example, many customers from the same\ncountry), caching the result avoids re-evaluating the expression.\n\n[code example]"},{"id":"caching-a-map-expression","title":"Caching a `map` expression","role":"content","text":"[code example]"},{"id":"caching-redislookup-arguments-and-results","title":"Caching `redis.lookup` arguments and results","role":"content","text":"`redis.lookup` supports two independent caches. The `cache:` block\ncaches the *argument* expressions (the JMESPath or SQL expressions\nthat produce the Redis command arguments). The `lookup_cache:` block\ncaches the *result* of the Redis command itself, keyed by the\nresolved arguments. Both blocks accept the same properties as the\n`cache:` block above.\n\n[code example]"},{"id":"caching-key-and-expire-output-expressions","title":"Caching `key` and `expire` output expressions","role":"content","text":"A `cache:` block can also be added to the\n[output `key` and `expire` expressions](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples)\nwhen those are dynamic. The properties are the same as above.\n\n[code example]"}],"examples":[{"id":"caching-an-add_field-expression-ex0","language":"yaml","code":"name: Cached country field\nsource:\n  schema: dbo\n  table: customer\ntransform:\n  - uses: add_field\n    with:\n      field: country\n      language: sql\n      expression: country_code || ' - ' || UPPER(country_name)\n      cache:\n        enabled: true\n        max_size: 500\n        ttl_seconds: 300","section_id":"caching-an-add_field-expression"},{"id":"caching-a-map-expression-ex0","language":"yaml","code":"name: Cached map expression\nsource:\n  table: customer\ntransform:\n  - uses: map\n    with:\n      language: jmespath\n      expression: |\n        {\n          \"CustomerId\": customer_id,\n          \"Country\": country_code\n        }\n      cache:\n        enabled: true","section_id":"caching-a-map-expression"},{"id":"caching-redislookup-arguments-and-results-ex0","language":"yaml","code":"name: Cached lookup\nsource:\n  table: order\ntransform:\n  - uses: redis.lookup\n    with:\n      connection: target\n      cmd: HGETALL\n      args:\n        - concat(['customer:', customer_id])\n      language: jmespath\n      field: customer\n      cache:\n        enabled: true\n        ttl_seconds: 60\n      lookup_cache:\n        enabled: true\n        max_size: 10000\n        ttl_seconds: 300","section_id":"caching-redislookup-arguments-and-results"},{"id":"caching-key-and-expire-output-expressions-ex0","language":"yaml","code":"name: Cached key expression\nsource:\n  table: order\noutput:\n  - uses: redis.write\n    with:\n      data_type: hash\n      key:\n        expression: concat(['order:', order_id])\n        language: jmespath\n        cache:\n          enabled: true","section_id":"caching-key-and-expire-output-expressions"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/transform-examples/formatting-date-and-time-values","title":"Formatting date and time values","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/formatting-date-and-time-values/","summary":"","tags":["docs","integrate","rs","rdi"],"last_updated":"2026-01-29T09:45:40+02:00","page_type":"content","content_hash":"05f903fd32ab9fc9c2ea0d6dcbb2e944e92d45224855185a3ff42bfa24ada4c1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The way you format date and time values depends on the source database, the data type of the field, and how it is represented in the incoming record. Below are some examples for different databases and data types."},{"id":"oracle","title":"Oracle","role":"content","text":"Oracle supports the following date and time data types:\n\n- `DATE` - represented by Debezium as a 64-bit integer representing the milliseconds since epoch\n  [code example]\n- `TIMESTAMP` - the value is represented by Debezium as a 64-bit integer and depends on the number of decimal places of precision of the column, representing fractions of a second. For example, if the column is defined as `TIMESTAMP(6)`, there are six decimal places and so the value is represented as microseconds since epoch (since there are 10^6 microseconds in each second).\nYou can format it similarly to `DATE`, but you need to divide the value by the appropriate factor based on the precision.\n\n- `TIMESTAMP WITH TIME ZONE` - the value is represented as a string containing the timestamp and time zone.\n\n- `TIMESTAMP WITH LOCAL TIME ZONE` - the value is represented as a string containing the timestamp and local time zone.\n\n  SQLite supports both `TIMESTAMP WITH TIME ZONE` and `TIMESTAMP WITH LOCAL TIME ZONE`. You can format them using the `STRFTIME` function.\n\n  [code example]\n\n----"},{"id":"sql-server","title":"SQL Server","role":"content","text":"SQL Server supports the following date and time data types:\n\n- `date` - represented by Debezium as number of days since epoch (1970-01-01). You can multiply the value by 86400 (the number of seconds in a day) to convert it to seconds since epoch and then use the `STRFTIME` or `DATE` functions to format it.\n  [code example]\n\n- `datetime`, `smalldatetime` - represented by Debezium as number of milliseconds since epoch. Divide the value by 1000 to convert it to seconds since epoch and then use the `STRFTIME` function to format it.\n  [code example]\n\n- `datetime2` - similar to `datetime` but with higher precision. For `datetime2(0-3)`, the representation is the same as for `datetime`. For `datetime2(4-6)`, it is the number of microseconds since epoch. For `datetime2(7)`, it is the number of nanoseconds since epoch. To convert to another time unit, you can use the same approach as for `datetime` but you need to divide by 1000, 1000000 or 1000000000 depending on the precision.\n\n- `time` - the number of milliseconds since midnight.\n  [code example]\n\n- `datetimeoffset` - represented as a timestamp with timezone information (for example, `2025-05-27T15:21:42.864Z` or `2025-01-02T14:45:30.123+05:00`). \n  [code example]\n\n\n\n\n\n\n\n\n----"},{"id":"postgresql","title":"PostgreSQL","role":"content","text":"PostgreSQL supports the following date and time data types:\n\n- `date` - represented by Debezium as number of days since epoch (1970-01-01). You can multiply the value by 86400 (the number of seconds in a day) to convert it to seconds since epoch and then use the `STRFTIME` or `DATE` functions to format it.\n  [code example]\n\n- `time` - the time of microseconds since midnight.\n  [code example]\n\n- `time with time zone` - a string representation of the time with timezone information, where the timezone is GMT (for example, `07:15:00Z`).\n  [code example]\n\n- `timestamp` - represented by Debezium as a 64-bit integer containing the microseconds since epoch. You can use the `STRFTIME` function to format it.\n  [code example]\n\n- `timestamp with time zone` - represented by Debezium as a string containing the timestamp with time zone information, where the timezone is GMT (for example, `2025-06-07T10:15:00.000000Z`).\n  [code example]"}],"examples":[{"id":"oracle-ex0","language":"yaml","code":"name: Format Oracle DATE field\n  transform:\n  - uses: add_field\n    with:\n      fields:\n        - field: formatted_date\n          language: sql\n          # Date is stored as a Unix timestamp in milliseconds so you need to\n          # divide it by 1000 to convert it to seconds.\n          expression: STRFTIME('%Y-%m-%d %H:%M:%S', DATE / 1000, 'unixepoch')\n          # Example: 1749047572000 is transformed to 2025-06-04 14:32:52","section_id":"oracle"},{"id":"oracle-ex1","language":"yaml","code":"name: Format Oracle TIMESTAMP WITH TIME ZONE\n  transform:\n    - uses: add_field\n      with:\n        fields:\n          - field: seconds_since_epoch\n            language: sql\n            # Convert the timestamp with local time zone to seconds since epoch.\n            expression: STRFTIME('%s', TIMESTAMP_FIELD)\n\n          - field: date_from_timestamp\n            language: sql\n            # Convert the timestamp with local time zone to date and time.\n            expression: STRFTIME('%Y-%m-%d %H:%M:%S', TIMESTAMP_FIELD)","section_id":"oracle"},{"id":"sql-server-ex0","language":"yaml","code":"name: Format SQL Server date field\n  transform:\n    - uses: add_field\n      with:\n        fields:\n          - field: with_default_date_format\n            language: sql\n            # Uses the default DATE format\n            expression: DATE(event_date * 86400, 'unixepoch')\n\n          - field: with_custom_date_format\n            language: sql\n            # Uses the default DATE format\n            expression: STRFTIME('%Y/%m/%d', event_date * 86400, 'unixepoch')","section_id":"sql-server"},{"id":"sql-server-ex1","language":"yaml","code":"name: Format SQL Server datetime field\n  transform:\n    - uses: add_field\n      with:\n        fields:\n          - field: formatted_datetime\n            language: sql\n            expression: STRFTIME('%Y-%m-%d %H:%M:%S', event_datetime / 1000, 'unixepoch')","section_id":"sql-server"},{"id":"sql-server-ex2","language":"yaml","code":"name: Format SQL Server time field\n  transform:\n    - uses: add_field\n      with:\n        fields:\n          - field: formatted_time\n            language: sql\n            expression: TIME(event_time, 'unixepoch', 'utc')","section_id":"sql-server"},{"id":"sql-server-ex3","language":"yaml","code":"name: Format SQL Server datetimeoffset field\n  transform:\n  - uses: add_field\n    with:\n      fields:\n        - field: formatted_datetimeoffset\n          language: sql\n          expression: STRFTIME('%Y-%m-%d %H:%M:%S', event_datetimeoffset)","section_id":"sql-server"},{"id":"postgresql-ex0","language":"yaml","code":"name: Format PostgreSQL date field\n    transform:\n        - uses: add_field\n          with:\n            fields:\n              - field: with_default_date_format\n                language: sql\n                # Uses the default DATE format\n                expression: DATE(event_date * 86400, 'unixepoch')","section_id":"postgresql"},{"id":"postgresql-ex1","language":"yaml","code":"name: Format PostgreSQL time field\n  transform:\n    - uses: add_field\n      with:\n        fields:\n          - field: formatted_time\n            language: sql\n            # Divide by 1000000 to convert microseconds to seconds\n            expression: TIME(event_time / 1000000, 'unixepoch', 'utc')","section_id":"postgresql"},{"id":"postgresql-ex2","language":"yaml","code":"name: Format PostgreSQL time with time zone\n  transform:\n    - uses: add_field\n      with:\n        fields:\n          - field: formatted_time_with_tz\n            language: sql\n            expression: STRFTIME('%H:%M:%S', event_time_with_time_zone)","section_id":"postgresql"},{"id":"postgresql-ex3","language":"yaml","code":"name: Format PostgreSQL timestamp field\n  transform:\n    - uses: add_field\n      with:\n        fields:\n          - field: formatted_timestamp\n            language: sql\n            # Divide by 1000000 to convert microseconds to seconds\n            expression: STRFTIME('%Y-%m-%d %H:%M:%S', event_timestamp / 1000000, 'unixepoch')","section_id":"postgresql"},{"id":"postgresql-ex4","language":"yaml","code":"name: Format PostgreSQL timestamp with time zone\n  transform:\n    - uses: add_field\n      with:\n        fields:\n          - field: formatted_timestamp_with_tz\n            language: sql\n            # Divide by 1000000 to convert microseconds to seconds\n            expression: STRFTIME('%Y-%m-%d %H:%M:%S', event_timestamp_with_time_zone)","section_id":"postgresql"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/transform-examples","title":"Job files","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/","summary":"Learn how to configure job files for data transformation.","aliases":["/integrate/redis-data-integration/ingest/data-pipelines/transform-examples/","/integrate/redis-data-integration/ingest/data-transformation/transform-examples/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-18T16:39:51+01:00","children":[{"id":"integrate/redis-data-integration/data-pipelines/transform-examples/redis-row-format","summary":"","title":"Row Format","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-row-format/"},{"id":"integrate/redis-data-integration/data-pipelines/transform-examples/redis-sql-case-example","summary":"","title":"Using SQL CASE","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-sql-case-example/"},{"id":"integrate/redis-data-integration/data-pipelines/transform-examples/redis-hash-example","summary":"","title":"Write to a Redis hash","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-hash-example/"},{"id":"integrate/redis-data-integration/data-pipelines/transform-examples/redis-json-example","summary":"","title":"Write to a Redis JSON document","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-json-example/"},{"id":"integrate/redis-data-integration/data-pipelines/transform-examples/redis-set-example","summary":"","title":"Write to a Redis set","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-set-example/"},{"id":"integrate/redis-data-integration/data-pipelines/transform-examples/redis-sorted-set-example","summary":"","title":"Write to a Redis sorted set","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-sorted-set-example/"},{"id":"integrate/redis-data-integration/data-pipelines/transform-examples/redis-stream-example","summary":"","title":"Write to a Redis stream","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-stream-example/"},{"id":"integrate/redis-data-integration/data-pipelines/transform-examples/redis-string-example","summary":"","title":"Write to a Redis string","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-string-example/"},{"id":"integrate/redis-data-integration/data-pipelines/transform-examples/redis-add-field-example","summary":"","title":"Add new fields to a key","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-add-field-example/"},{"id":"integrate/redis-data-integration/data-pipelines/transform-examples/formatting-date-and-time-values","summary":"","title":"Formatting date and time values","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/formatting-date-and-time-values/"},{"id":"integrate/redis-data-integration/data-pipelines/transform-examples/redis-lookup-example","summary":"","title":"Reading Redis data with redis.lookup","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-lookup-example/"},{"id":"integrate/redis-data-integration/data-pipelines/transform-examples/remapping-the-output","summary":"","title":"Remapping the fields in the output","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/remapping-the-output/"},{"id":"integrate/redis-data-integration/data-pipelines/transform-examples/redis-remove-field-example","summary":"","title":"Remove fields from a key","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-remove-field-example/"},{"id":"integrate/redis-data-integration/data-pipelines/transform-examples/map-example","summary":"","title":"Restructure JSON or hash objects","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/map-example/"},{"id":"integrate/redis-data-integration/data-pipelines/transform-examples/redis-expiration-example","summary":"","title":"Set custom expiration times / TTL","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-expiration-example/"},{"id":"integrate/redis-data-integration/data-pipelines/transform-examples/redis-set-key-name","summary":"","title":"Set the key name in the target database","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-set-key-name/"},{"id":"integrate/redis-data-integration/data-pipelines/transform-examples/caching-expression-results","summary":"","title":"Caching expression results","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/caching-expression-results/"},{"id":"integrate/redis-data-integration/data-pipelines/transform-examples/redis-opcode-example","summary":"","title":"Using the operation code","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-opcode-example/"},{"id":"integrate/redis-data-integration/data-pipelines/transform-examples/redis-write-same-key","summary":"","title":"Write to the same key from multiple jobs","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-write-same-key/"},{"id":"integrate/redis-data-integration/data-pipelines/transform-examples/redis-writing-to-multiple-keys","summary":"","title":"Writing to multiple keys","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-writing-to-multiple-keys/"}],"page_type":"content","content_hash":"3ecf7031dbd1073f8a18ea854b5b909edabff95e191b61d06a18d768eba48d85","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can optionally supply one or more job files that specify how you want to\ntransform the captured data before writing it to the target.\nEach job file contains a YAML\nconfiguration that controls the transformation for a particular table from the source\ndatabase. You can also add a `default-job.yaml` file to provide\na default transformation for tables that don't have a specific job file of their own.\n\nThe job files have a structure like the following example. This configures a default\njob that:\n\n- Writes the data to a Redis hash\n- Adds a field `app_code` to the hash with a value of `foo`\n- Adds a prefix of `aws` and a suffix of `gcp` to the key\n\n[code example]\n\nThe main sections of these files are:\n\n- `source`: This is a mandatory section that specifies the data items that you want to \n  use. You can add the following properties here:\n  - `server_name`: The name of the source whose records this job processes, as it appears\n    in the `sources` section of\n    [config.yaml](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config).\n    This is required when the pipeline has more than one source but is optional in a pipeline with a single source. See\n    [Multiple sources in one pipeline](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/multiple-sources) for more information.\n    For a source that existed before RDI supported multiple sources, use `rdi` instead, as described in\n    [Existing names are kept after an upgrade](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/multiple-sources#existing-names-are-kept-after-an-upgrade).\n  - `db`: Database name (optional). This refers to a database name you supplied in\n    [config.yaml](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config).\n  - `schema`: Database schema (optional). This refers to a schema name you supplied in\n    [config.yaml](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config).\n  - `table`: Database table name. This refers to a table name you supplied in `config.yaml`. The default\n  job doesn't apply to a specific table, so use \"*\" in place of the table name for this job only.\n  - `row_format`: Format of the data to be transformed. This can take the values `partial` (default) to\n  use only the payload data, or `full` to use the complete change record. See the `transform` section below\n  for details of the extra data you can access when you use the `full` option.\n  - `case_insensitive`: This applies to the `server_name`, `db`, `schema`, and `table` properties\n  and is set to `true` by default. Set it to `false` if you need to use case-sensitive values for these\n  properties.\n\n  With the\n  [Flink processor](https://redis.io/docs/latest/integrate/redis-data-integration/architecture/classic-vs-flink),\n  `server_name`, `db`, `schema`, and `table` each accept either a single name or a list of names.\n  An entry prefixed with `regex:` selects all names that match the regular expression (note that this\n  must match the whole name, not just a substring). For example, `server_name: [mysql, postgresql]` selects two sources,\n  and `table: \"regex:orders_[0-9]+\"` selects every numbered `orders` table. A job processes the\n  records that match all of the properties it sets, so a single job can process multiple tables,\n  potentially from different sources, databases, or schemas.\n\n  The classic processor takes a single name in each of these properties. It rejects a list or a\n  `regex:` entry when you deploy the pipeline, so you should use one job per table instead.\n\n- `transform`: This is an optional section describing the transformation that the pipeline\n  applies to the data before writing it to the target. The `uses` property specifies a\n  *transformation block* that will use the parameters supplied in the `with` section. See the \n  [data transformation reference](https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation)\n  for more details about the supported transformation blocks. See also the\n  [JMESPath custom functions](https://redis.io/docs/latest/integrate/redis-data-integration/reference/jmespath-custom-functions)\n  reference and [JMESPath functions proposal](https://jmespath.org/proposals/functions.html) for \n  full details of the functions available for the `expression` field.\n  You can test your transformation logic using the\n  [dry run](https://redis.io/docs/latest/integrate/redis-data-integration/reference/api-reference#tag/secure/operation/job_dry_run_api_v1_pipelines_jobs_dry_run_post)\n  feature in the API.\n\n  > [!NOTE]\n  > If you set `row_format` to `full` under the `source` settings, you can access extra data from the\n  > change record in the transformation:\n  > - Use the `key` object to access the attributes of the key. For example, `key.id` will give you the value of the `id` column as long as it is part of the primary key.\n  > - Use `before.<FIELD_NAME>` to get the value of a field *before* it was updated in the source database\n  > - Use `after.<FIELD_NAME>` to get the value of a field *after* it was updated in the source database\n  > - Use `after.<FIELD_NAME>` when adding new fields during transformations\n  >\n  > See [Row Format](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-row-format#full) for a more detailed explanation of the full format.\n \n- `output`: This is a mandatory section to specify the data structure(s) that\n  RDI will write to\n  the target along with the text pattern for the key(s) that will access it.\n  Note that you can map one record to more than one key in Redis or nest\n  a record as a field of a JSON structure (see\n  [Data denormalization](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/data-denormalization)\n  for more information about nesting). You can add the following properties in the `output` section:\n  - `uses`: This must have the value `redis.write` to specify writing to a Redis data\n  structure. You can add more than one block of this type in the same job.\n  - `with`:\n    - `connection`: Connection name as defined in `config.yaml` (by default, the connection named `target` is used).\n    - `data_type`: Target data structure when writing data to Redis. The supported types are `hash`, `json`, `set`,\n   `sorted_set`, `stream` and `string`.\n    - `key`: This lets you override the default key for the data structure with custom logic:\n      - `expression`: Expression to generate the key.\n      - `language`: Expression language, which must be `jmespath` or `sql`.\n    - `expire`: Positive integer value or SQL/JMESPath expression indicating a number of seconds\n      for the key to expire. If you don't specify this property, the key will never expire.\n      See [Set custom expiration times / TTL](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-expiration-example) for more information and examples.\n\n> [!NOTE]\n> In a job file, the `transform` section is optional, but if you don't specify\n> a `transform`, you must specify custom key logic in `output.with.key`. You can include\n> both of these sections if you want both a custom transform and a custom key.\n\nAnother example below shows how you can rename the `fname` field to `first_name` in the table `emp`\nusing the\n[`rename_field`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation/rename_field) block. It also demonstrates how you can set the key of this record instead of relying on\nthe default logic.\n\n[code example]\n\nSee the\n[RDI configuration file](https://redis.io/docs/latest/integrate/redis-data-integration/reference/config-yaml-reference)\nreference for full details about the\navailable source, transform, and target configuration options and see\nalso the\n[data transformation reference](https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation)\nfor details of all the available transformation blocks.\n\n> [!NOTE]\n> When using the `sql` option as language for the expressions keep in mind that RDI uses the SQL syntax and\n> functions supported by SQLite and those may differ from the ANSI-SQL ones. You can find more details in SQLite's\n> [official documentation](https://sqlite.org/lang.html)."},{"id":"examples","title":"Examples","role":"example","text":"The pages listed below show examples of typical job files for different use cases."}],"examples":[{"id":"overview-ex0","language":"yaml","code":"name: Default job with prefix and suffix\nsource:\n  table: \"*\"\n  row_format: full\ntransform:\n  - uses: add_field\n    with:\n      fields:\n        - field: after.app_code\n          expression: \"`foo`\"\n          language: jmespath\noutput:\n  - uses: redis.write\n    with:\n      data_type: hash\n      key:\n        expression: concat(['aws', '#', table, '#', keys(key)[0], '#', values(key)[0], '#gcp'])\n        language: jmespath","section_id":"overview"},{"id":"overview-ex1","language":"yaml","code":"name: Rename field example\nsource:\n  server_name: sqlserver\n  schema: dbo\n  table: emp\ntransform:\n  - uses: rename_field\n    with:\n      from_field: fname\n      to_field: first_name\noutput:\n  - uses: redis.write\n    with:\n      connection: target\n      key:\n        expression: concat(['emp:fname:',fname,':lname:',lname])\n        language: jmespath","section_id":"overview"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/transform-examples/map-example","title":"Restructure JSON or hash objects","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/map-example/","summary":"","tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-18T16:39:51+01:00","page_type":"content","content_hash":"f86f62ec4498ea01845f97dfabe19efeced26c6f3645f8f891463f3b240a37ef","sections":[{"id":"overview","title":"Overview","role":"overview","text":"By default, RDI adds fields to\n[hash](https://redis.io/docs/latest/develop/data-types/hashes) or\n[JSON](https://redis.io/docs/latest/develop/data-types/json) objects in the target\ndatabase that closely match the columns of the source table.\nIf you just want to limit the set fields in the output and/or rename some of them, you can use the\n[`output mapping`](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/remapping-the-output) configuration option.\n\nFor situations where you want to create a new object structure with multiple levels or use calculations for the field values, you can use the\n[`map`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation/map)\ntransformation, as described in the following sections."},{"id":"creating-multilevel-json-objects","title":"Creating multilevel JSON objects","role":"content","text":"You can use the `map` transformation to create a new structure for the output data, which can include nested objects and calculated fields. The `map` transformation allows you to define a new structure using an expression language, such as SQL or JavaScript.\n\n[code example]\n\n\nThe example above creates a new JSON object with the following structure:\n - A top-level `id` field that is the same as the `employeeid` field in the source table.\n - A `name` field that is a concatenation of the `firstname` and `lastname` fields, with the `lastname` converted to uppercase.\n - An `address` subobject that contains the `address`, `city`, `state`, `postalcode`, and `country` fields.\n - A `contact` subobject that contains the `phone` field and a modified version of the `email` field, where the '@' sign and dots are replaced with '_at_' and '_dot_' respectively.\n\nThe `output` section of the file configures the job to write\nto a JSON object with a custom key. Note that in the `output` section, you must refer to\nfields defined in the `map` transformation, so we use the new name `id`\nfor the key instead of `employeeid`.\n\n\n\nIf you query one of the new JSON objects, you see output like the following:\n\n[code example]\n\nFormatted in the usual JSON style, the output looks like the sample below:\n\n[code example]"},{"id":"creating-hash-structure","title":"Creating hash structure","role":"content","text":"This example creates a new [hash](https://redis.io/docs/latest/develop/data-types/hashes)\nobject structure for items from the `track` table. Here, the `map` transformation uses\n[SQL](https://en.wikipedia.org/wiki/SQL) for the expression because this is often\nmore suitable for hashes or \"flat\"\nJSON objects without subobjects or arrays. The expression renames some of the fields.\nIt also calculates more human-friendly representations for the track duration (originally\nstored in the `milliseconds` field) and the storage size (originally stored in the\n`bytes` field).\n\nThe full example is shown below:\n\n[code example]\n\nIf you query the data for one of the `track` hash objects, you see output\nlike the following:\n\n[code example]"}],"examples":[{"id":"creating-multilevel-json-objects-ex0","language":"yaml","code":"name: Create multilevel employee JSON\nsource:\n  db: chinook\n  table: employee\n\ntransform:\n  - uses: map\n    with:\n      expression: |\n        {\n          \"id\": employeeid,\n          \"name\": concat([firstname, ' ', upper(lastname)]),\n          \"address\": {\n            \"street\": address,\n            \"city\": city,\n            \"state\": state,\n            \"postalCode\": postalcode,\n            \"country\": country\n          },\n          \"contact\": {\n            \"phone\": phone,\n            \"safeEmail\": replace(replace(email, '@', '_at_'), '.', '_dot_')\n          }\n        }\n      language: jmespath\n\noutput:\n  - uses: redis.write\n    with:\n      data_type: json\n      key:\n        expression: concat(['emp:', id])\n        language: jmespath","section_id":"creating-multilevel-json-objects"},{"id":"creating-multilevel-json-objects-ex1","language":"bash","code":"> JSON.GET emp:1 $\n\"[{\\\"id\\\":1,\\\"name\\\":\\\"Andrew ADAMS\\\",\\\"address\\\":{\\\"street\\\":\\\"11120 Jasper Ave NW\\\",\\\"city\\\":\\\"Edmonton\\\",\\\"state\\\":\\\"AB\\\",\\\"postalCode\\\":\\\"T5K 2N1\\\",\\\"country\\\":\\\"Canada\\\"},\\\"contact\\\":{\\\"phone\\\":\\\"+1 (780) 428-9482\\\",\\\"safeEmail\\\":\\\"andrew_at_chinookcorp_dot_com\\\"}}]\"","section_id":"creating-multilevel-json-objects"},{"id":"creating-multilevel-json-objects-ex2","language":"json","code":"{\n  \"id\": 1,\n  \"name\": \"Andrew ADAMS\",\n  \"address\": {\n    \"street\": \"11120 Jasper Ave NW\",\n    \"city\": \"Edmonton\",\n    \"state\": \"AB\",\n    \"postalCode\": \"T5K 2N1\",\n    \"country\": \"Canada\"\n  },\n  \"contact\": {\n    \"phone\": \"+1 (780) 428-9482\",\n    \"safeEmail\": \"andrew_at_chinookcorp_dot_com\"\n  }\n}","section_id":"creating-multilevel-json-objects"},{"id":"creating-hash-structure-ex0","language":"yaml","code":"name: Create track hash with calculated fields\nsource:\n  db: chinook\n  table: track\ntransform:\n  - uses: map\n    with:\n      expression:\n          id: trackid\n          name: name\n          duration: concat(floor(milliseconds / 60000), ':', floor(mod(milliseconds / 1000, 60)))\n          storagesize: concat(round(bytes / 1048576.0, 2), 'MB')\n      language: sql\noutput:\n  - uses: redis.write\n    with:\n      connection: target\n      data_type: hash\n      key:\n        expression: concat('track:', id)\n        language: sql","section_id":"creating-hash-structure"},{"id":"creating-hash-structure-ex1","language":"bash","code":"> hgetall track:16\n1) \"id\"\n2) \"16\"\n3) \"name\"\n4) \"Dog Eat Dog\"\n5) \"duration\"\n6) \"3:35.0\"\n7) \"storagesize\"\n8) \"6.71MB\"","section_id":"creating-hash-structure"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/transform-examples/redis-add-field-example","title":"Add new fields to a key","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-add-field-example/","summary":"","tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-18T16:39:51+01:00","page_type":"content","content_hash":"f1cc0ff0c8a2cb119a06718421db1130ffb6d8525131c46e44253833ff07199f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"By default, RDI adds fields to\n[hash](https://redis.io/docs/latest/develop/data-types/hashes) or\n[JSON](https://redis.io/docs/latest/develop/data-types/json) objects in the target\ndatabase that match the columns of the source table.\nThe examples below show how to add extra fields to the target data with the\n[`add_field`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation/add_field) transformation."},{"id":"add-a-single-field","title":"Add a single field","role":"content","text":"The first example adds a single field to the data.\nThe `source` section selects the `customer` table of the\n[`chinook`](https://github.com/Redislabs-Solution-Architects/rdi-quickstart-postgres)\ndatabase (the optional `db` value here corresponds to the\n`sources.<source-name>.connection.database` value defined in\n[`config.yaml`](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config)).\n\nIn the `transform` section, the `add_field` transformation adds an extra field called `localphone`\nto the object, which is created by removing the country and area code from the `phone`\nfield with the\n[JMESPath](https://redis.io/docs/latest/integrate/redis-data-integration/reference/jmespath-custom-functions) function `regex_replace()`.\nYou can also specify `sql` as the `language` if you prefer to create the new\nfield with an [SQL](https://en.wikipedia.org/wiki/SQL) expression.\n\nThe `output` section specifies `hash` as the `data_type` to write to the target, which\noverrides the default setting of `target_data_type` defined in `config.yaml`. Also, the\n`output.with.key` section specifies a custom key format of the form `cust:<id>` where\nthe `id` part is generated by the `uuid()` function.\n\nThe full example is shown below:\n\n[code example]\n\nIf you queried the generated target data from the default transformation\nusing [`redis-cli`](https://redis.io/docs/latest/develop/tools/cli), you would\nsee something like the following:\n\n[code example]\n\nUsing the job file above, the data also includes the new `localphone` field:\n\n[code example]"},{"id":"add-multiple-fields","title":"Add multiple fields","role":"content","text":"The `add_field` transformation can also add multiple fields at the same time\nif you specify them under a `fields` subsection. The example below adds two\nfields to the `track` objects. The first new field, `seconds`, is created using a SQL\nexpression to calculate the duration of the track in seconds from the\n`milliseconds` field.\nThe second new field, `composerlist`, adds a JSON array using the `split()` function\nto split the `composer` string field wherever it contains a comma.\n\n[code example]\n\nYou can query the target database to see the new fields in\nthe JSON object:\n\n[code example]"},{"id":"using-add_field-with-remove_field","title":"Using `add_field` with `remove_field`","role":"content","text":"You can use the `add_field` and\n[`remove_field`](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-remove-field-example)\ntransformations together to completely replace fields from the source. For example,\nif you add a new `fullname` field, you might not need the separate `firstname` and\n`lastname` fields. You can remove them with a job file like the following:\n\n[code example]"}],"examples":[{"id":"add-a-single-field-ex0","language":"yaml","code":"name: Add local phone field to customer\nsource:\n  db: chinook\n  table: customer\ntransform:\n  - uses: add_field\n    with:\n      expression: regex_replace(phone, '\\+[0-9]+ (\\([0-9]+\\) )?', '')\n      field: localphone\n      language: jmespath\noutput:\n  - uses: redis.write\n    with:\n      connection: target\n      data_type: hash\n      key:\n        expression: concat(['cust:', uuid()])\n        language: jmespath","section_id":"add-a-single-field"},{"id":"add-a-single-field-ex1","language":"plaintext","code":"1) \"customerid\"\n 2) \"27\"\n 3) \"firstname\"\n 4) \"Patrick\"\n 5) \"lastname\"\n 6) \"Gray\"\n.\n.\n17) \"phone\"\n18) \"+1 (520) 622-4200\"\n.\n.","section_id":"add-a-single-field"},{"id":"add-a-single-field-ex2","language":"plaintext","code":"1) \"customerid\"\n 2) \"27\"\n 3) \"firstname\"\n 4) \"Patrick\"\n 5) \"lastname\"\n 6) \"Gray\"\n .\n .\n23) \"localphone\"\n24) \"622-4200\"","section_id":"add-a-single-field"},{"id":"add-multiple-fields-ex0","language":"yaml","code":"name: Add multiple fields to track\nsource:\n  db: chinook\n  table: track\ntransform:\n  - uses: add_field\n    with:\n      fields:\n        - expression: floor(milliseconds / 1000)\n          field: seconds\n          language: sql\n        - expression: split(composer)\n          field: composerlist\n          language: jmespath\noutput:\n  - uses: redis.write\n    with:\n      connection: target\n      data_type: json\n      key:\n        expression: concat(['track:', trackid])\n        language: jmespath","section_id":"add-multiple-fields"},{"id":"add-multiple-fields-ex1","language":"bash","code":"> JSON.GET track:1 $\n\n\"[{\\\"trackid\\\":1,\\\"name\\\":\\\"For Those About To Rock (We Salute You)\\\",\\\"albumid\\\":1,\\\"mediatypeid\\\":1,\\\"genreid\\\":1,\\\"composer\\\":\\\"Angus Young, Malcolm Young, Brian Johnson\\\",\\\"milliseconds\\\":343719,\\\"bytes\\\":11170334,\\\"unitprice\\\":\\\"0.99\\\",\\\"seconds\\\":343,\\\"composerlist\\\":[\\\"Angus Young\\\",\\\" Malcolm Young\\\",\\\" Brian Johnson\\\"]}]\"","section_id":"add-multiple-fields"},{"id":"using-add_field-with-remove_field-ex0","language":"yaml","code":"name: Add fullname and remove separate name fields\nsource:\n  db: chinook\n  table: customer\ntransform:\n  - uses: add_field\n    with:\n      expression: concat(firstname, ' ', lastname)\n      field: fullname\n      language: sql\n  - uses: remove_field\n    with:\n      fields:\n        - field: firstname\n        - field: lastname\noutput:\n  - uses: redis.write\n    with:\n      connection: target\n      data_type: hash\n      key:\n        expression: concat(['cust:', customerid])\n        language: jmespath","section_id":"using-add_field-with-remove_field"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/transform-examples/redis-expiration-example","title":"Set custom expiration times / TTL","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-expiration-example/","summary":"","tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-18T16:39:51+01:00","page_type":"content","content_hash":"abed8a453a8a2e73426d44d896581cfc98d151e068d4daeb2d03abc524489a06","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can configure custom key expiration times (TTL) for keys written to Redis by using the `expire` parameter in the `output` section of the job file. This parameter specifies the duration, in seconds, that a newly created key will remain in Redis before being automatically deleted. If the `expire` parameter is not provided, the keys will persist indefinitely.\n\nThere are two ways to set the expiration time:\n\n- as a static value\n- as a dynamic value using a JMESPath or SQL expression"},{"id":"static-expiration-time","title":"Static expiration time","role":"content","text":"The following example sets the expiration time to 100 seconds for all keys:\n\n[code example]"},{"id":"dynamic-expiration-time","title":"Dynamic expiration time","role":"content","text":"You can use a JMESPath or SQL expression to set the expiration time dynamically when it is based on a field in the source data. For example, you can set the expiration time to the value of a `ttl` field in the source data:\n\n[code example]"},{"id":"dynamic-expiration-time-based-on-a-date-datetime-or-timestamp-field","title":"Dynamic expiration time based on a date, datetime, or timestamp field","role":"content","text":"In some cases, you can also set the expiration time based on a field that contains a date, datetime, or timestamp value, but it depends on the source database and the data types it supports. See the examples below for your specific source database and data type.\n\nThere are two main approaches you can use to set the expiration time based on a date, datetime, or timestamp field:\n\n- For values representing an elapsed time since epoch start (in milliseconds, for example), you have to convert the value to seconds since epoch and then subtract the current time (also in seconds since epoch). The difference between the two is the time until expiration.\n\n    [code example]\n\n- For values matching the subset of ISO 8601 supported by SQLite (for example, `2023-10-01T12:00:00`, `2023-10-01T12:00:00Z`, or `2025-06-05T13:40:14.784000+02:00`), you can use the `STRFTIME` function to convert the value to seconds since epoch and subtract the current time in seconds since epoch from it.\n\n  [code example]\n\nFor more examples of how to manipulate date and time values, see [Formatting date and time values](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/formatting-date-and-time-values)."}],"examples":[{"id":"static-expiration-time-ex0","language":"yaml","code":"name: Static expiration example\noutput:\n  - uses: redis.write\n    with:\n      data_type: hash\n      expire: 100","section_id":"static-expiration-time"},{"id":"dynamic-expiration-time-ex0","language":"yaml","code":"name: Dynamic expiration from field\noutput:\n  - uses: redis.write\n    with:\n      data_type: hash\n      expire:\n        expression: ttl\n        language: jmespath","section_id":"dynamic-expiration-time"},{"id":"dynamic-expiration-time-based-on-a-date-datetime-or-timestamp-field-ex0","language":"yaml","code":"name: Expiration from timestamp in milliseconds\n    output:\n        - uses: redis.write\n          with:\n            data_type: hash\n            expire:\n              # To set the expiration time to a date field, convert the value to\n              # seconds (e.g. divide it by 1000 if the fields has milliseconds precision)\n              # and subtract the current time in seconds since epoch.\n              expression: EXPIRES_TIMESTAMP / 1000 - STRFTIME('%s', 'now')\n              language: sql","section_id":"dynamic-expiration-time-based-on-a-date-datetime-or-timestamp-field"},{"id":"dynamic-expiration-time-based-on-a-date-datetime-or-timestamp-field-ex1","language":"yaml","code":"name: Expiration from ISO 8601 datetime\n  output:\n    - uses: redis.write\n      with:\n        data_type: hash\n        expire:\n          language: sql\n          expression: STRFTIME('%s', EXPIRATION_TS) - STRFTIME('%s', 'now')","section_id":"dynamic-expiration-time-based-on-a-date-datetime-or-timestamp-field"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/transform-examples/redis-hash-example","title":"Write to a Redis hash","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-hash-example/","summary":"","aliases":["/integrate/redis-data-integration/ingest/data-pipelines/transform-examples/redis-hash-example/","/integrate/redis-data-integration/data-transformation/examples/redis-hash-example/","/integrate/redis-data-integration/ingest/data-transformation/examples/redis-hash-example/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"a4f2bc50c1597ed8e4a2abd2adb20abf2d17e1d0c65f493ee842360a63853ecd","sections":[{"id":"content","title":"Content","role":"content","text":"In the following example, the data is captured from the source table named `invoice` and is written to the Redis database as hash keys. The `connection` is an optional parameter that refers to the corresponding connection name defined in `config.yaml`. \nWhen you specify the `data_type` parameter for the job, it overrides the system-wide setting `target_data_type` defined in `config.yaml`. \n\nIn this case, the result will be Redis hashes with key names based on the key expression (for example, `invoice_id:1`) and with an expiration of 100 seconds.\nIf you don't supply an `expire` parameter, the keys will never expire. \n\n[code example]"}],"examples":[{"id":"content-ex0","language":"yaml","code":"name: Write invoice to hash\nsource:\n  schema: public\n  table: invoice\noutput:\n  - uses: redis.write\n    with:\n      connection: target\n      data_type: hash\n      key:\n        expression: concat(['invoice_id:', InvoiceId])\n        language: jmespath\n      expire: 100","section_id":"content"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/transform-examples/redis-json-example","title":"Write to a Redis JSON document","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-json-example/","summary":"","aliases":["/integrate/redis-data-integration/ingest/data-pipelines/transform-examples/redis-json-example/","/integrate/redis-data-integration/data-transformation/examples/redis-json-example/","/integrate/redis-data-integration/ingest/data-transformation/examples/redis-json-example/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-18T16:39:51+01:00","page_type":"content","content_hash":"d5cf072fc2d1a58942aa6f1dd6ce025a4534a8a84cc3291878b171cca32ccb99","sections":[{"id":"content","title":"Content","role":"content","text":"> [!NOTE]\n> You must enable the [RedisJSON](https://redis.io/docs/latest/develop/data-types/json) module in the target Redis\n> database to use this feature.\n\nIn the example below, the data is captured from the source table named `invoice` and is written to the Redis database as a JSON document. The `connection` is an optional parameter that refers to the corresponding connection name defined in `config.yaml`. When you specify the `data_type` parameter for the job, it overrides the system-wide setting `target_data_type` defined in `config.yaml`. \n\nAnother optional parameter, `on_update`, specifies the writing strategy. You can set this to either `replace` (the default) or `merge`. This affects the way the document is written to the target. Replacing the document will overwrite it completely, while merging will update it with the fields captured in the source, keeping the rest of the document intact. The `replace` option is usually more performant, while with `merge` other jobs and applications can set extra fields in the same JSON documents.\n\nIn this case, the result will be Redis JSON documents with key names based on the key expression (for example, `invoice_id:1`) and with an expiration of 100 seconds. If you don't supply an `expire` parameter, the keys will never expire.    \n\n[code example]"}],"examples":[{"id":"content-ex0","language":"yaml","code":"name: Write invoice to JSON\nsource:\n  schema: public\n  table: invoice\noutput:\n  - uses: redis.write\n    with:\n      connection: target\n      data_type: json\n      key:\n        expression: concat(['invoice_id:', InvoiceId])\n        language: jmespath\n      on_update: replace\n      expire: 100","section_id":"content"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/transform-examples/redis-lookup-example","title":"Reading Redis data with redis.lookup","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-lookup-example/","summary":"","tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-18T16:39:51+01:00","page_type":"content","content_hash":"9a7ae171277a7f45564be965b6b46208d86fa2cf156b1bb1d9ec7bbc5e9325bc","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can use the\n[`redis.lookup`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation/lookup)\ntransformation to read existing data from Redis during the `transform` stage of a\njob. This lets you enrich an incoming record with values that are already present\nin the target database.\n\nFor example, a pipeline for the Chinook database might read an `artist` record\nthat is already stored in Redis and use `redis.lookup` in an `album` table job to\nadd selected artist details to each album record before writing it to the target\ndatabase.\n\n> [!WARNING]\n> Do not rely on `redis.lookup` to *denormalize* data that RDI writes from another\n> table in the **same pipeline**. RDI can't guarantee that the looked-up data will be\n> present or up to date when the lookup runs, for the following reasons:\n>\n> - **Snapshot order isn't guaranteed.** During the initial snapshot, RDI can't\n>   guarantee that the table you look up is ingested before the table that depends\n>   on it. If a dependent job runs before the referenced key has been written, the\n>   lookup misses.\n> - **Change (CDC) order isn't guaranteed.** If a parent and child record are\n>   inserted or updated at around the same time, RDI has no way to order these\n>   events, so the lookup can still miss.\n> - **Parent updates don't refresh existing keys.** Even if the lookup succeeds,\n>   updating the source record later does *not* update the keys that already copied\n>   its values. The denormalized data becomes stale.\n>\n> The only case where `redis.lookup` is safe for enrichment is when you can guarantee\n> that the looked-up data is present in the target database *independently* of the\n> RDI pipeline (for example, a reference table that is loaded and maintained\n> separately).\n>\n> To denormalize data that RDI ingests, use a supported technique instead. See\n> [Data denormalization](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/data-denormalization)\n> for one-to-one joins (using `merge`) and one-to-many joins (using nesting)."},{"id":"reading-a-hash-field","title":"Reading a hash field","role":"content","text":"The `redis.lookup` transformation works by executing a Redis command and adding the\nresult to the record. You specify the command and its arguments in the\n`transform` configuration with the `cmd` and `args` properties. For example, the\nfollowing transformation job uses the\n[`HGET`](https://redis.io/docs/latest/commands/hget) command to read the `name` field from an\nartist [hash](https://redis.io/docs/latest/develop/data-types/hashes) and adds it to the\nalbum record under the `artist` field. A particularly important thing to note\nhere is that the `args` elements are all interpreted as [JMESPath](https://jmespath.org/)\nexpressions, but YAML syntax allows for each element to be a quoted string. This means that\nyou must *double quote* any string arguments that you want to be treated as\nliteral strings (as with `name` below), otherwise JMESPath will try to interpret\nthem as field names, which will generally give the wrong result. Specifically, use\na different quote character for the outer quotes and the inner quotes.\n\n[code example]\n\nBefore the lookup runs, the album hash object contains only the `artistid` field to\nreference the artist:\n\n[code example]\n\nAfter running the job specified above, querying one of the album hash objects shows the\nextra `artist` field obtained by looking up the artist with the `artistid`:\n\n[code example]"},{"id":"embedding-a-json-document","title":"Embedding a JSON document","role":"content","text":"If you are using [JSON](https://redis.io/docs/latest/develop/data-types/json) objects,\nyou can read the whole of one object and embed it\nas a field of another. The following example shows how to do this using a temporary field\nto hold the result of the `redis.lookup` command. It then uses\n[`add_field`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation/add_field)\nto insert the new field and\n[`remove_field`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation/remove_field)\nto remove the temporary field and the now-redundant `artistid` field before writing the album object.\n\n[code example]\n\nAfter running this job, the album JSON object includes the artist object\nin a new `artist` field:\n\n[code example]"}],"examples":[{"id":"reading-a-hash-field-ex0","language":"yaml","code":"source:\n  table: album\ntransform:\n  - uses: redis.lookup\n    with:\n      connection: target\n      cmd: HGET\n      args:\n        - concat(['artist:artistid:', artistid])\n        - '`name`'\n      language: jmespath\n      field: artist\noutput:\n  - uses: redis.write\n    with:\n      connection: target\n      data_type: hash\n      key:\n        expression: concat(['album:albumid:', albumid])\n        language: jmespath","section_id":"reading-a-hash-field"},{"id":"reading-a-hash-field-ex1","language":"bash","code":"> hgetall album:albumid:1\n1) \"albumid\"\n2) \"1\"\n3) \"title\"\n4) \"For Those About To Rock We Salute You\"\n5) \"artistid\"\n6) \"1\"","section_id":"reading-a-hash-field"},{"id":"reading-a-hash-field-ex2","language":"bash","code":"> hgetall album:albumid:1\n1) \"albumid\"\n2) \"1\"\n3) \"title\"\n4) \"For Those About To Rock We Salute You\"\n5) \"artistid\"\n6) \"1\"\n7) \"artist\"\n8) \"AC/DC\"","section_id":"reading-a-hash-field"},{"id":"embedding-a-json-document-ex0","language":"yaml","code":"source:\n  table: album\ntransform:\n  - uses: redis.lookup\n    with:\n      connection: target\n      cmd: JSON.GET\n      args:\n        - concat(['artist:artistid:', artistid])\n      language: jmespath\n      field: artiststring\n  - uses: add_field\n    with:\n      field: artist\n      language: jmespath\n      expression: json_parse(artiststring)\n  - uses: remove_field\n    with:\n      fields:\n        - field: artistid\n        - field: artiststring\noutput:\n  - uses: redis.write\n    with:\n      connection: target\n      data_type: json\n      key:\n        expression: concat(['album:albumid:', albumid])\n        language: jmespath","section_id":"embedding-a-json-document"},{"id":"embedding-a-json-document-ex1","language":"json","code":"{\n  \"albumid\": 239,\n  \"title\": \"War\",\n  \"artist\": {\n    \"artistid\": 150,\n    \"name\": \"U2\"\n  }\n}","section_id":"embedding-a-json-document"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/transform-examples/redis-opcode-example","title":"Using the operation code","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-opcode-example/","summary":"","tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-18T16:39:51+01:00","page_type":"content","content_hash":"34d59e214394315b83fbef6a8994bb858786816ebdf6071b98d64a188693ecf3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The operation code (`opcode`) is a metadata field that indicates the type of operation that generated the change in the source database. It can be useful for tracking changes and understanding the context of the data being processed.\n\nThe opcode is only available in the [full row format](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-row-format#full), and can be accessed in the `transform` and `output` sections of the job file.\n\nIt has one of the following values:\n\n| Opcode | Operation | Notes |\n|--------|-----------|-------|\n| `c` | Create row |  |\n| `u` | Update row |  |\n| `d` | Delete row |  |\n| `r` | Read row (snapshot) | Applies only to snapshots. |\n| `t` | Truncate table | PostgreSQL specific. |\n| `m` | Message event | PostgreSQL specific. |\n\n\nYou can add the value of the operation code to the output, and also use it in a conditional expression to modify the behavior of the job. The following examples demonstrate the different use-cases."},{"id":"adding-the-operation-code-to-the-output","title":"Adding the operation code to the output","role":"content","text":"Use the `add_field` transformation to add a new field that contains the value of the `opcode` field from the source data. Note that the fields must be prefixed with `after` to be included in the output.\n\n\n[code example]"},{"id":"filtering-operation-by-output-code","title":"Filtering operation by output code.","role":"content","text":"In some cases you may want to ignore certain operations (for example, you may not be interested in deletions). Use the `filter` transformation to filter out any operations you don't need to process.\n\n[code example]"},{"id":"modifying-the-output-based-on-the-operation-code","title":"Modifying the output based on the operation code","role":"content","text":"The previous example filters out specific operations, but you can also modify the output based on the operation code. For example, you can add a new field that tracks the status of the record based on the operation code.\n\nNote that when a source record is deleted, you must modify the value of the `opcode` field if you want to prevent the corresponding record in the target database from being removed automatically.\n\n[code example]"}],"examples":[{"id":"adding-the-operation-code-to-the-output-ex0","language":"yaml","code":"name: Add operation code to employee\nsource:\n  schema: public\n  table: employee\n  row_format: full\n\ntransform:\n  # add the operation code to the data\n  - uses: add_field\n    with:\n      field: after.operation_code\n      expression: opcode\n      language: jmespath","section_id":"adding-the-operation-code-to-the-output"},{"id":"filtering-operation-by-output-code-ex0","language":"yaml","code":"name: Filter out delete operations\nsource:\n  schema: public\n  table: employee\n  row_format: full\n\ntransform:\n  - uses: filter\n    with:\n      expression: opcode != 'd'\n      language: jmespath","section_id":"filtering-operation-by-output-code"},{"id":"modifying-the-output-based-on-the-operation-code-ex0","language":"yaml","code":"name: Track status based on operation code\nsource:\n  schema: public\n  table: employee\n  row_format: full\n\ntransform:\n  - uses: add_field\n    with:\n      fields:\n        # Here you set the value of the field based on the value of the opcode field\n        - field: after.status\n          expression: opcode == 'd' && 'inactive' || 'active'\n          language: jmespath\n\n        # You have to change the value of the opcode field to prevent deletion\n        - field: opcode\n          expression: opcode == 'd' && 'u' || opcode\n          language: jmespath","section_id":"modifying-the-output-based-on-the-operation-code"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/transform-examples/redis-remove-field-example","title":"Remove fields from a key","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-remove-field-example/","summary":"","tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-18T16:39:51+01:00","page_type":"content","content_hash":"09429549fd395cf06907d24b689928a1420ab94325d9f4a29a0ea31334ee5e12","sections":[{"id":"overview","title":"Overview","role":"overview","text":"By default, RDI adds fields to\n[hash](https://redis.io/docs/latest/develop/data-types/hashes) or\n[JSON](https://redis.io/docs/latest/develop/data-types/json) objects in the target\ndatabase for each of the columns of the source table.\nThe examples below show how to omit some of those fields from the target data with the\n[`remove_field`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation/remove_field) transformation."},{"id":"remove-a-single-field","title":"Remove a single field","role":"content","text":"The first example removes a single field from the data.\nThe `source` section selects the `employee` table of the\n[`chinook`](https://github.com/Redislabs-Solution-Architects/rdi-quickstart-postgres)\ndatabase (the optional `db` field here corresponds to the\n`sources.<source-name>.connection.database` field defined in\n[`config.yaml`](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config)).\n\nIn the `transform` section, the `remove_field` transformation removes the\n`hiredate` field.\n\nThe `output` section specifies `hash` as the `data_type` to write to the target, which\noverrides the default setting of `target_data_type` defined in `config.yaml`. Also, the\n`output.with.key` section specifies a custom key format of the form `emp:<employeeid>`.\nNote that any fields you remove in the `transform` section are not available for\nthe key calculation in the `output` section.\n\nThe full example is shown below:\n\n[code example]\n\nIf you queried the generated target data from the default transformation\nusing [`redis-cli`](https://redis.io/docs/latest/develop/tools/cli), you would\nsee something like the following:\n\n[code example]\n\nUsing the job file above, the data omits the `hiredate` field:\n\n[code example]"},{"id":"remove-multiple-fields","title":"Remove multiple fields","role":"content","text":"The `remove_field` transformation can also remove multiple fields at the same time\nif you specify them under a `fields` subsection. The example below is similar\nto the previous one but also removes the `birthdate` field: \n\n[code example]\n\nIf you query the data, you can see that it also omits the\n`birthdate` field:\n\n[code example]"},{"id":"using-remove_field-with-add_field","title":"Using `remove_field` with `add_field`","role":"content","text":"The `remove_field` transformation is very useful in combination with\n[`add_field`](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-add-field-example).\nFor example, if you use `add_field` to concatenate a person's first\nand last names, you may not need separate `firstname` and `lastname`\nfields, so you can use `remove_field` to omit them.\nSee [Using `add_field` with `remove_field`](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-add-field-example#using-add_field-with-remove_field)\nfor an example of how to do this."}],"examples":[{"id":"remove-a-single-field-ex0","language":"yaml","code":"name: Remove hiredate from employee\nsource:\n  db: chinook\n  table: employee\ntransform:\n  - uses: remove_field\n    with:\n      field: hiredate\noutput:\n  - uses: redis.write\n    with:\n      connection: target\n      data_type: hash\n      key:\n        expression: concat(['emp:', employeeid])\n        language: jmespath","section_id":"remove-a-single-field"},{"id":"remove-a-single-field-ex1","language":"bash","code":"> hgetall emp:8\n 1) \"employeeid\"\n 2) \"8\"\n 3) \"lastname\"\n 4) \"Callahan\"\n 5) \"firstname\"\n 6) \"Laura\"\n 7) \"title\"\n 8) \"IT Staff\"\n 9) \"reportsto\"\n10) \"6\"\n11) \"birthdate\"\n12) \"-62467200000000\"\n13) \"hiredate\"\n14) \"1078358400000000\"\n15) \"address\"\n16) \"923 7 ST NW\"\n.\n.","section_id":"remove-a-single-field"},{"id":"remove-a-single-field-ex2","language":"bash","code":"> hgetall emp:8\n 1) \"employeeid\"\n 2) \"8\"\n 3) \"lastname\"\n 4) \"Callahan\"\n 5) \"firstname\"\n 6) \"Laura\"\n 7) \"title\"\n 8) \"IT Staff\"\n 9) \"reportsto\"\n10) \"6\"\n11) \"birthdate\"\n12) \"-62467200000000\"\n13) \"address\"\n14) \"923 7 ST NW\"\n.\n.","section_id":"remove-a-single-field"},{"id":"remove-multiple-fields-ex0","language":"yaml","code":"name: Remove multiple date fields from employee\nsource:\n  db: chinook\n  table: employee\ntransform:\n  - uses: remove_field\n    with:\n      fields:\n        - field: hiredate\n        - field: birthdate\noutput:\n  - uses: redis.write\n    with:\n      connection: target\n      data_type: hash\n      key:\n        expression: concat(['emp:', employeeid])\n        language: jmespath","section_id":"remove-multiple-fields"},{"id":"remove-multiple-fields-ex1","language":"bash","code":"> hgetall emp:8\n 1) \"employeeid\"\n 2) \"8\"\n 3) \"lastname\"\n 4) \"Callahan\"\n 5) \"firstname\"\n 6) \"Laura\"\n 7) \"title\"\n 8) \"IT Staff\"\n 9) \"reportsto\"\n10) \"6\"\n11) \"address\"\n12) \"923 7 ST NW\"\n.\n.","section_id":"remove-multiple-fields"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/transform-examples/redis-row-format","title":"Row Format","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-row-format/","summary":"","tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-18T16:39:51+01:00","page_type":"content","content_hash":"e81ba0da7a162ee54f3fde01e1b700c925a5eece4d560f46dec145da27a78284","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The RDI pipelines support two separate row formats which you can specify in the `source` section of the job file:\n\n- `partial` - (Default) Contains the current value of the row only.\n- `full` - Contains all information available for the row, including the key, the before and after values, and the operation code.\n\nThe `full` row format is useful when you want to access the metadata associated with the row, such as the operation code, and the before and after values.\nThe structure of the data passed to the `transform` and `output` sections is different depending on the row format you choose. Consider which row format you are using when you reference keys.\nThe following two examples demonstrate the difference between the two row formats."},{"id":"default-row-format","title":"Default row format","role":"content","text":"With the default row format, the input value is a JSON object containing the current value of the row, and fields can be referenced directly by their name.\n\nUsage example:\n\n[code example]"},{"id":"full","title":"Full row format","role":"content","text":"With `row_format: full` the input value is a JSON object with the following structure:\n\n- `key` - An object containing the attributes of the primary key. For example, `key.id` will give you the value of the `id` column as long as it is part of the primary key.\n- `before` - An object containing the previous value of the row.\n- `after` - An object containing the current value of the row.\n- `opcode` - The operation code. See [Using the operation code](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-opcode-example) for more information about the possible opcode values and how to use them.\n- `db` - The database name.\n- `table` - The table name.\n- `schema` - The schema name. \n \nNote: The `db` and `schema` fields are database-specific and may not be available in all databases. For example, MySQL doesn't use `schema` and uses `db` as the database name.\n\n\nUsage example:\n\n[code example]"},{"id":"important-notes-when-using-row_format-full","title":"Important notes when using `row_format: full`","role":"content","text":"- The `before` object will be `null` for `insert` and `create` operations, and the `after` object will be `null` for `delete` operations. If you are building the output key manually, you should account for this and ensure that you are not trying to access fields from a `null` object, as shown in the example below:\n\n    [code example]\n\n    Please note that you should not use `key` in combination with `row_format: full` and more than one output, as the `key` object will be overwritten by the previous output. This is a known limitation of the current implementation and is subject to change in future versions.\n\n\n- The final result of the processing (which is what will be stored in the output) is the value of the `after` object. This means you must reference the fields using the `after` prefix unless you change the output structure in a transformation step. Also, when you add new fields, you must prefix them with `after.` to ensure that they are added to the correct part of the output:\n\n    [code example]\n\n- When using the `full` row format, you should prepend all data fields in transformation and key calculations with `before` or `after` depending on whether you want to access the previous or current value of the row. The only exception to this is the `mapping` section where only the `after` values are available, so you should use the column/value name without the `after` prefix."}],"examples":[{"id":"default-row-format-ex0","language":"yaml","code":"name: Default row format example\nsource:\n  table: addresses\ntransform:\n  - uses: add_field\n    with:\n      field: city_state\n      expression: concat([CITY, ', ', STATE])\n      language: jmespath\n  - uses: add_field\n    with:\n      field: op_code_value\n      # Operation code is not available in standard row format\n      # so the following expression will result in `op_code - None`\n      expression: concat(['op_code', ' - ', opcode])\n      language: jmespath\noutput:\n  - uses: redis.write\n    with:\n      data_type: hash\n      key:\n        expression: concat(['addresses', '#', ID])\n        language: jmespath","section_id":"default-row-format"},{"id":"full-ex0","language":"yaml","code":"name: Full row format example\nsource:\n  table: addresses\n  row_format: full\ntransform:\n  - uses: add_field\n    with:\n      # opcode is only available in full row format and can be used in the transformations\n      field: after.op_code_value\n      expression: address\n      language: jmespath\n  - uses: add_field\n    with:\n      field: after.city_state\n      # Note that we need to use the `after` prefix to access the current value of the row\n      # or `before` to access the previous value\n      expression: concat([after.CITY, ', ', after.STATE])\n      language: jmespath\noutput:\n  - uses: redis.write\n    with:\n      data_type: hash\n      key:\n        # There are different ways to express the key\n        # If the `ID` column is the primary key the following expressions\n        # are equivalent - `key.ID`, `after.ID`, `values(key)[0]`\n        expression: concat(['addresses-full', '#', values(key)[0]])\n        language: jmespath","section_id":"full"},{"id":"important-notes-when-using-row_format-full-ex0","language":"yaml","code":"name: Handle delete operations with full row format\n    source:\n      table: addresses\n      row_format: full\n\n    output:\n      - uses: redis.write\n        with:\n          key:\n            language: jmespath\n            # The following pattern will fail for delete operations. In those cases `after` is null, the resulting key will\n            # be 'addresses:None' and the key won't be removed from the target\n            # expression: concat(['addresses:', after.ID])\n\n            # This pattern works for all operations, by using the ID from the `after` object if it is available,\n            # and falling back to the ID from the `before` object if not.\n            expression: concat(['addresses:', after.ID || before.ID])\n\n            # Another option is to use the ID from the `key` object\n            # expression: concat(['addresses:', values(key)[0]])","section_id":"important-notes-when-using-row_format-full"},{"id":"important-notes-when-using-row_format-full-ex1","language":"yaml","code":"name: Add fields with full row format\n    source:\n      table: addresses\n      row_format: full\n\n    transform:\n      - uses: add_field\n        with:\n          field: after.city_state  # use this to add the new field to the final output\n          # field: city_state  # use this if you need a temporary field in the transformation steps, but not in the final output\n          expression: concat([after.CITY, ', ', after.STATE])\n          language: jmespath","section_id":"important-notes-when-using-row_format-full"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/transform-examples/redis-set-example","title":"Write to a Redis set","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-set-example/","summary":"","aliases":["/integrate/redis-data-integration/ingest/data-pipelines/transform-examples/redis-set-example/","/integrate/redis-data-integration/data-transformation/examples/redis-set-example/","/integrate/redis-data-integration/ingest/data-transformation/examples/redis-set-example/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"936f8ea653f7d066584c0663f1c99769425ebf33bd0191b2ce9a2337a0ef3a68","sections":[{"id":"content","title":"Content","role":"content","text":"In the example below, data is captured from the source table named `invoice` and is written to a Redis set. The `connection` is an optional parameter that refers to the corresponding connection name defined in `config.yaml`. When you specify the\n`data_type` parameter for the job, it overrides the system-wide setting `target_data_type` defined in `config.yaml`.\n\nWhen writing to a set, you must supply an extra argument, `member`, which specifies the field that will be written. In this case, the result will be a Redis set with key names based on the key expression (for example, `invoices:Germany`, `invoices:USA`) and with an expiration of 100 seconds. If you don't supply an `expire` parameter, the keys will never expire.    \n\n[code example]"}],"examples":[{"id":"content-ex0","language":"yaml","code":"name: Write invoices to set by country\nsource:\n  schema: public\n  table: invoice\noutput:\n  - uses: redis.write\n    with:\n      connection: target\n      data_type: set\n      key:\n        expression: concat(['invoices:', BillingCountry])\n        language: jmespath\n      args:\n        member: InvoiceId\n      expire: 100","section_id":"content"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/transform-examples/redis-set-key-name","title":"Set the key name in the target database","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-set-key-name/","summary":"","aliases":["/integrate/redis-data-integration/ingest/data-pipelines/transform-examples/redis-set-key/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-01-29T09:45:40+02:00","page_type":"content","content_hash":"b8115d312f7ac509089a1ddb29e40f1cc99fcc904e1e18ff3f5431c7090703f6","sections":[{"id":"understanding-default-key-names","title":"Understanding Default Key Names","role":"overview","text":"When RDI synchronizes data from your primary database to Redis, it automatically generates key names based on a specific pattern.\nBy default, RDI creates keys using the following format:\n\n* **Single primary key**: `tablename:primarykeyname:primarykeyvalue`\n* **Composite primary keys**: `tablename:key1name:key1value:key2name:key2value`\n\nExamples\n\n* For a table named `employee` with primary key `employeeid`, a record with `employeeid=1` will have the key `employee:employeeid:1`\n* For a table named `orders` with composite primary keys `orderid` and `customerid`, a record with `orderid=1` and `customerid=2` will have the key `orders:orderid:1:customerid:2`"},{"id":"customizing-key-names","title":"Customizing Key Names","role":"content","text":"While the default key naming convention works for many use cases, you may need custom key formats to:\n\n* Match existing Redis key patterns\n* Create more concise key names\n* Implement application-specific naming schemes\n* Optimize for specific access patterns\n\nTo customize key names, use the `key` section within the `redis.write` output configuration. This section requires two parameters:\n\n* `expression`: Defines the custom key format using a supported expression language\n* `language`: Specifies the expression language to use (`jmespath` or `sql`)"},{"id":"example","title":"Example","role":"example","text":"[code example]"},{"id":"special-considerations-for-full-row-format","title":"Special Considerations for Full Row Format","role":"content","text":"When working with the full row format, you need to handle key generation differently to ensure proper behavior across all operation types (create, update, and delete). You must reference attributes correctly to ensure consistent key generation, especially for delete operations where the \"after\" state is empty. The example below demonstrates how to handle this:\n\n[code example]"},{"id":"summary","title":"Summary","role":"content","text":"Proper key naming is essential for effective data organization in Redis. RDI provides:\n\n1. **Default key naming** that follows a consistent pattern based on table and primary key information\n2. **Custom key naming** through the `key` section with `expression` and `language` parameters\n3. **Special handling for full row format** to ensure consistent key generation across all operation types\n\nBy understanding and utilizing these options, you can ensure your Redis keys are optimally structured for your specific use case, making data retrieval more efficient and your Redis implementation more maintainable."}],"examples":[{"id":"example-ex0","language":"yaml","code":"name: Custom key name for customers\nsource:\n  db: inventory\n  table: customers\noutput:\n  - uses: redis.write\n    with:\n      key:\n        expression: concat(['customers', '#', id])\n        language: jmespath","section_id":"example"},{"id":"special-considerations-for-full-row-format-ex0","language":"yaml","code":"name: Custom key with full row format\nsource:\n  db: inventory\n  table: customers\n  row_format: full\noutput:\n  - uses: redis.write\n    with:\n      key:\n        # Here we use the operation code to determine the value of the key to ensure that\n        # delete operations will result in the correct key being deleted\n        expression: concat(['customers', '#', opcode == 'd' && before.id || after.id])\n        language: jmespath","section_id":"special-considerations-for-full-row-format"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/transform-examples/redis-sorted-set-example","title":"Write to a Redis sorted set","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-sorted-set-example/","summary":"","aliases":["/integrate/redis-data-integration/ingest/data-pipelines/transform-examples/redis-sorted-set-example/","/integrate/redis-data-integration/data-transformation/examples/redis-sorted-set-example/","/integrate/redis-data-integration/ingest/data-transformation/examples/redis-sorted-set-example/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"2e49825a275227f22da0208e895c798b30ba0e63a28efd6f1be2078263de7b87","sections":[{"id":"content","title":"Content","role":"content","text":"In the example below, data is captured from the source table named `invoice` and is written to a Redis sorted set. The `connection` is an optional parameter that refers to the corresponding connection name defined in `config.yaml`. When\nyou specify the `data_type` parameter for the job, it overrides the system-wide setting `target_data_type` defined in `config.yaml`.\n\nWhen writing to sorted sets, you must provide two additional arguments, `member` and `score`. These specify the field names that will be used as a member and a score to add an element to a sorted set. In this case, the result will be a Redis sorted set named `invoices:sorted` based on the key expression and with an expiration of 100 seconds for each set member. If you don't supply an `expire` parameter, the keys will never expire.\n\n[code example]\n\nSince sorted sets in Redis are inherently sorted, you can easily get the top N invoices by total invoice amount using the command below (the range 0..9 gets the top 10 invoices):\n\n[code example]"}],"examples":[{"id":"content-ex0","language":"yaml","code":"name: Write invoices to sorted set by total\nsource:\n  schema: public\n  table: invoice\noutput:\n  - uses: redis.write\n    with:\n      connection: target\n      data_type: sorted_set\n      key:\n        expression: \"`invoices:sorted`\"\n        language: jmespath\n      args:\n        score: Total\n        member: InvoiceId\n      expire: 100","section_id":"content"},{"id":"content-ex1","language":"plaintext","code":"ZREVRANGE invoices:sorted 0 9 WITHSCORES","section_id":"content"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/transform-examples/redis-sql-case-example","title":"Using SQL CASE","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-sql-case-example/","summary":"","aliases":["/integrate/redis-data-integration/ingest/data-pipelines/transform-examples/redis-sql-case-example/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-01-29T09:45:40+02:00","page_type":"content","content_hash":"448a139a118b6cd6f182c54f976ef1285d5ea722da1301df753c52268239e3e0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The [`CASE`](https://www.w3schools.com/sql/sql_case.asp) statement allows you to specify conditions and return different values based on those conditions. You can use it both to create new fields or filter existing data."},{"id":"using-sql-case-to-create-a-new-field","title":"Using SQL CASE to create a new field","role":"content","text":"The example below demonstrates how to use the `CASE` statement to create a new field called `Market` based on the value of the `BillingCountry` field in the `Invoice` table. The new field categorizes countries into regions such as \"North America\" and \"Europe\".\n\n[code example]"},{"id":"using-sql-case-to-filter-data","title":"Using SQL CASE to filter data","role":"content","text":"You can also use the `CASE` statement to filter data based on specific conditions. The example below demonstrates how to filter the `Invoice` table to include only invoices from the USA and Canada that have a `Total` value above their country-specific threshold.\n\nBecause the `Total` field is a Decimal in the source table, it is represented as a string in Debezium and so you must cast it to `REAL` to compare it numerically in the `CASE` statement. Without this cast, it will be compared as a string value, which will give the wrong result.\n\n[code example]"}],"examples":[{"id":"using-sql-case-to-create-a-new-field-ex0","language":"yaml","code":"name: Add market field using SQL CASE\nsource:\n  table: Invoice\n\ntransform:\n  - uses: add_field\n    with:\n      field: \"Market\"\n      language: sql\n      expression: |\n        CASE\n          WHEN BillingCountry = 'USA' THEN 'North America'\n          WHEN BillingCountry = 'Canada' THEN 'North America'\n          WHEN BillingCountry = 'UK' THEN 'Europe'\n          WHEN BillingCountry = 'France' THEN 'Europe'\n          ELSE 'Other'\n        END","section_id":"using-sql-case-to-create-a-new-field"},{"id":"using-sql-case-to-filter-data-ex0","language":"yaml","code":"name: Filter invoices by country and total\nsource:\n  table: Invoice\n\ntransform:\n  - uses: filter\n    with:\n      language: sql\n      expression: |\n        CASE\n          WHEN BillingCountry = 'USA' AND CAST(Total AS REAL) > 11.99 THEN True\n          WHEN BillingCountry = 'Canada' AND CAST(Total AS REAL) > 9.99 THEN True\n          ELSE False\n        END","section_id":"using-sql-case-to-filter-data"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/transform-examples/redis-stream-example","title":"Write to a Redis stream","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-stream-example/","summary":"","aliases":["/integrate/redis-data-integration/ingest/data-pipelines/transform-examples/redis-stream-example/","/integrate/redis-data-integration/data-transformation/examples/redis-stream-example/","/integrate/redis-data-integration/ingest/data-transformation/examples/redis-stream-example/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"74899ce9651d84e04e35961b2365f9ea3c91b393b198f161f8d4ca59bcea2ee6","sections":[{"id":"content","title":"Content","role":"content","text":"In the example below, data is captured from the source table named `invoice` and is written to a Redis stream. The `connection` is an optional parameter that refers to the corresponding connection name defined in `config.yaml`.\nWhen you specify the `data_type` parameter for the job, it overrides the system-wide setting `target_data_type` defined in `config.yaml`.\n\nWhen writing to streams, you can use the optional parameter `mapping` to limit the number of fields sent in a message and to provide aliases for them. If you don't use the `mapping` parameter, all fields captured in the source will be passed as the message payload. \n\nNote that streams are different from other data structures because existing messages are never updated or deleted. Any operation in the source will generate a new message with the corresponding operation code (`op_code` field) that is automatically added to the message payload. \n\nIn this case, the result will be a Redis stream with the name based on the key expression (for example, `invoice:events`) and with an expiration of 100 seconds for the whole stream. If you don't supply an `expire` parameter, the keys will never expire. \n\nIn the example, only three original fields are passed in the message payload: `InvoiceId` (as `message_id`), `BillingCountry` (as `country`), `Total` (as `Total`, no alias provided) and `op_code`, which is implicitly added to all messages sent to streams.    \n\n[code example]"}],"examples":[{"id":"content-ex0","language":"yaml","code":"name: Write invoice events to stream\nsource:\n  schema: public\n  table: invoice\noutput:\n  - uses: redis.write\n    with:\n      connection: target\n      data_type: stream\n      key:\n        expression: \"`invoice:events`\"\n        language: jmespath\n      mapping:\n        - InvoiceId: message_id\n        - BillingCountry: country\n        - Total\n      expire: 100","section_id":"content"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/transform-examples/redis-string-example","title":"Write to a Redis string","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-string-example/","summary":"","aliases":["/integrate/redis-data-integration/ingest/data-pipelines/transform-examples/redis-string-example/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-18T16:39:51+01:00","page_type":"content","content_hash":"b8b86daea02ca18e10f8066628b5a25b1be5efb299a1430cbc714014f7d0afdd","sections":[{"id":"content","title":"Content","role":"content","text":"The string data type is useful for capturing a string representation of a single column from\na source table.\n\nIn the example job below, the `title` column is captured from the `album` table in the source.\nThe `title` is then written to the Redis target database as a string under a custom key of the\nform `AlbumTitle:42`, where the `42` is the primary key value of the table (the `albumid` column).\n\nThe `connection` is an optional parameter that refers to the corresponding connection name defined in\n[`config.yaml`](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config). \nWhen you specify the `data_type` parameter for the job, it overrides the system-wide setting `target_data_type` defined in `config.yaml`. Here, the `string` data type also requires an `args` subsection\nwith a `value` argument that specifies the column you want to capture from the source table.\n\nThe optional `expire` parameter sets the length of time, in seconds, that a new key will\npersist for after it is created (here, it is 86400 seconds, which equals one day).\nAfter this time, the key will be deleted automatically.\nIf you don't supply an `expire` parameter, the keys will never expire. \n\n[code example]"}],"examples":[{"id":"content-ex0","language":"yaml","code":"name: Write album title to string\nsource:\n  table: album\n  row_format: full\noutput:\n  - uses: redis.write\n    with:\n      connection: target\n      data_type: string\n      key:\n        expression: concat(['AlbumTitle:', values(key)[0]])\n        language: jmespath\n      args:\n        value: title\n      expire: 86400","section_id":"content"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/transform-examples/redis-write-same-key","title":"Write to the same key from multiple jobs","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-write-same-key/","summary":"","tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-18T16:39:51+01:00","page_type":"content","content_hash":"20a1c33d947cddf58db1847dd413a96ac341676051a234545157e2679e9b28fd","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Use this pattern when two or more jobs write related source entities, such as\n`customer` and `address`, to the same Redis JSON document.\n\nWhen multiple jobs write to the same Redis key, a delete event from any of the\nsource entities can delete the key from the target. To work around this, use\n[`row_format: full`](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-row-format#full)\nso the job can inspect the\n[`opcode`](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-opcode-example),\nconvert delete events into update events before writing to Redis, and write JSON\ndocuments with `on_update: merge`.\n\n> [!NOTE]\n> Use the same key expression in all jobs that write to the shared Redis key. For\n> delete events, read key values from `before` or `key` because `after` is `null`."},{"id":"customer-job","title":"Customer job","role":"content","text":"[code example]"},{"id":"address-job","title":"Address job","role":"content","text":"For delete events from the `addresses` table, this job sets all fields to `null`\nto instruct RDI to remove them from the target JSON document. This behavior is\navailable in RDI 1.15.0 or later when native JSON merge is enabled and the target\ndatabase uses RedisJSON 2.6.0 or later.\n\n[code example]"}],"examples":[{"id":"customer-job-ex0","language":"yaml","code":"# jobs/customer.yaml\nname: customers\n\nsource:\n  table: customers\n\noutput:\n  - uses: redis.write\n    with:\n      data_type: json\n      on_update: merge\n      key:\n        expression: concat(['customer:', id])\n        language: jmespath","section_id":"customer-job"},{"id":"address-job-ex0","language":"yaml","code":"# jobs/addresses.yaml\nname: addresses\n\nsource:\n  table: addresses\n  row_format: full\n\ntransform:\n  - uses: add_field\n    with:\n      fields:\n        # For create/update records, we take the new values as is.\n        # If the record is a deletion, we set all fields to null.\n        - field: after\n          expression: |\n            (opcode != 'd' && after) \n              || \n            from_entries(to_entries(before)[].{key: key, value: `null`})\n          language: jmespath\n\n        # Treat deletes as updates so that we can use the same output configuration\n        - field: opcode\n          expression: opcode == 'd' && 'u' || opcode\n          language: jmespath\n\n  # If you have overlapping field names (for example, FK and PK have the same name, or both tables have\n  # a field called \"id\"), you may want to remove the field from the after object to prevent it\n  # from overwriting the PK.\n  - uses: remove_field\n    with:\n      field: after.id\n\noutput:\n  - uses: redis.write\n    with:\n      data_type: json\n      on_update: merge\n      key:\n        expression: concat(['customer:', after.customer_id || before.customer_id])\n        language: jmespath","section_id":"address-job"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/transform-examples/redis-writing-to-multiple-keys","title":"Writing to multiple keys","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-writing-to-multiple-keys/","summary":"","tags":["docs","integrate","rs","rdi"],"last_updated":"2026-01-29T09:45:40+02:00","page_type":"content","content_hash":"66542191445845ffd9f0c3c51b2ea6f2afb5c3071e32a398af8ac8c26c86929c","sections":[{"id":"content","title":"Content","role":"content","text":"If you want to write results to multiple keys, you can do so by defining multiple `redis.write` subsections in the `output` section of the job file. Each instance of `redis.write` can specify a different key, data format, and other parameters. For example, you can create two different keys for the same data, one with a default key format and another with a custom key format and mapping.\n\n[code example]"}],"examples":[{"id":"content-ex0","language":"yaml","code":"name: Write to multiple keys with different formats\noutput:\n  - uses: redis.write\n    with:\n      # Setting data_type to JSON and using the default key format\n      data_type: json\n\n  - uses: redis.write\n    with:\n      data_type: json\n\n      # Defining a custom key format\n      key:\n        language: jmespath\n        expression: concat(['events-simplified:id:', id])\n\n      # And defining a custom mapping\n      mapping:\n        - id: id\n        - name: name\n        - location: location","section_id":"content"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/data-pipelines/transform-examples/remapping-the-output","title":"Remapping the fields in the output","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/remapping-the-output/","summary":"","tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-18T16:39:51+01:00","page_type":"content","content_hash":"41f87731a23cf9070e47618bb76dbda2fd9f0bbb237c35acb4394b9b76dff48f","sections":[{"id":"content","title":"Content","role":"content","text":"Sometimes, you may want to remap the fields in the output of a data pipeline. You can do this by defining a `mapping` section in the output configuration.\n\n[code example]\n\nThe example above remaps the `CustomerId` field to `id`, `FirstName` to `first_name`, and `LastName` to `last_name` in the output. This allows you to customize the field names in the Redis data store according to your application's requirements.\nYou can also use `mapping` to include only the fields you need in the output and exclude the rest.\n\nMapping only allows you to rename fields and limit the output to specific fields and define a single level structure. To create nested structures and/or perform operations on the field values you can use the [map transformation](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/map-example)."}],"examples":[{"id":"content-ex0","language":"yaml","code":"name: Remap customer fields\nsource:\n  table: Customer\n\noutput:\n  - uses: redis.write\n    with:\n      data_type: hash\n      mapping:\n        - CustomerId: id\n        - FirstName: first_name\n        - LastName: last_name","section_id":"content"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/faq","title":"FAQ","url":"https://redis.io/docs/latest/integrate/redis-data-integration/faq/","summary":"Find answers to common questions about RDI","aliases":["/integrate/redis-data-integration/ingest/faq/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-10-09T12:24:46+03:00","page_type":"content","content_hash":"68c7a4e031e4b0b8702933eb46c5e078928b969ffcca63be62c96b403577a65a","sections":[{"id":"which-license-does-rdi-use","title":"Which license does RDI use?","role":"overview","text":"You must purchase a commercial license for RDI with Redis Enterprise. This includes two extra\nRedis Enterprise shards (primary and replica) for the staging database."},{"id":"how-does-rdi-track-data-changes-in-the-source-database","title":"How does RDI track data changes in the source database?","role":"content","text":"RDI uses change data capture (CDC) mechanisms that are specific to each of the\nsupported source databases:\n\n- **Oracle**: RDI uses `LogMiner` to read Oracle's `redo logs` and `archive logs`,\n  or, alternatively, `XStream`.\n- **MySQL/MariaDB**: RDI uses `binary log` (binlog) replication to capture all commits.\n- **PostgreSQL**: RDI uses the `pgoutput` logical decoding plugin. The same\n  applies to the PostgreSQL-compatible databases that RDI supports, including\n  Supabase, AlloyDB for PostgreSQL, Amazon Aurora/RDS for PostgreSQL, and Neon.\n- **SQL Server**: RDI uses the database's built-in CDC feature.\n- **MongoDB**: RDI uses `change streams` to read the `oplog`. The source must be\n  a replica set, sharded cluster, or MongoDB Atlas deployment, because a\n  standalone MongoDB server has no oplog.\n- **Google Cloud Spanner**: RDI uses `Spanner change streams` for the streaming\n  phase and the JDBC driver for the initial snapshot. Spanner is supported only\n  when RDI is deployed on Kubernetes with Helm.\n- **Snowflake** (preview): RDI uses `Snowflake Streams`. Snowflake is supported\n  only when RDI is deployed on Kubernetes with Helm.\n\nFor the complete list of supported source databases and versions, see\n[Prepare source databases](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs)."},{"id":"how-much-data-can-rdi-process","title":"How much data can RDI process?","role":"content","text":"RDI uses the concept of *processing units*. Each processing unit uses 1 CPU core and can process\nabout 10,000 records per second, assuming the records have a size of about 1KB each. This throughput\nmight change slightly depending on the number of columns, the number of data transformations,\nand the speed of the network. Typically, one processing unit is enough for RDI to deal with the\ntraffic from a relational database."},{"id":"can-rdi-work-with-any-redis-database","title":"Can RDI work with any Redis database?","role":"content","text":"No. RDI is designed and tested to work only with Redis Enterprise. The staging database can\nonly use version 6.4 or above. The target Redis database can be of any version and can be a\nreplica of an Active-Active replication setup or an Auto tiering database."},{"id":"can-i-use-active-active-for-the-rdi-database","title":"Can I use Active-Active for the RDI database?","role":"content","text":"Yes, starting with RDI 1.16.0, you can use Active-Active for the RDI database. This is\nsupported whether or not you also run a disaster recovery (DR) setup for RDI.\n\nIf you have two RDI instances sharing a single RDI database then they will use that database for leader election, so\nthey need no other lease mechanism. This is how high availability (HA) works for VM\ninstallations. See\n[Installing with High Availability](https://redis.io/docs/latest/integrate/redis-data-integration/installation/install-vm#installing-with-high-availability).\n\nIn a DR setup, each site runs its own RDI instance against its local instance of the\nActive-Active RDI database, so leader election needs an external lease. Google Cloud Storage\n(GCS) is currently the only supported lease mechanism, and you can configure it only for\n[Helm based installations](https://redis.io/docs/latest/integrate/redis-data-integration/installation/install-k8s).\n\n**Important:** Use a DR setup only when both sites capture changes from the same source\ndatabase server. Both RDI instances must point at that same server, not at a replica of it."},{"id":"can-i-run-multiple-rdi-installations-in-the-same-kubernetes-cluster","title":"Can I run multiple RDI installations in the same Kubernetes cluster?","role":"content","text":"No. Only one RDI installation is supported per Kubernetes cluster, even if\nyou install into different namespaces. If you need more than one RDI\ndeployment, use separate Kubernetes clusters. See\n[Install on Kubernetes](https://redis.io/docs/latest/integrate/redis-data-integration/installation/install-k8s)\nfor installation details."},{"id":"can-one-pipeline-capture-from-several-source-databases","title":"Can one pipeline capture from several source databases?","role":"content","text":"Yes. Add one entry per source to the `sources` section of `config.yaml`. Each source has its\nown collector, which captures change records independently of the other sources. See\n[Multiple sources in one pipeline](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/multiple-sources) for more information."},{"id":"can-rdi-automatically-track-changes-to-the-source-database-schema","title":"Can RDI automatically track changes to the source database schema?","role":"content","text":"If you don't configure RDI to capture a specific set of tables in the schema then it will\ndetect any new tables when they are added. Similarly, RDI will capture new table columns\nand changes to column names unless you configure it for a specific set of columns.\nBear in mind that the Redis keys in the target database will change to reflect the\nnew or renamed tables and columns.\n\nSQL Server is an exception to automatic column handling. You must create a new CDC capture instance after a schema change. Until you do, events can continue to use the old captured column structure and omit a newly added column. Follow [Handling changes to the SQL Server schema](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/sql-server#handling-changes-to-the-schema) for the migration procedure and its capture history limits."},{"id":"rdi-oom","title":"Should I be concerned when the log says RDI is out of memory?","role":"content","text":"Sometimes the Debezium log will contain a message saying that RDI is out of\nmemory. This is not an error but an informative message to say that RDI\nis applying *backpressure* to Debezium. See\n[Backpressure mechanism](https://redis.io/docs/latest/integrate/redis-data-integration/architecture#backpressure-mechanism)\nin the Architecture guide for more information."},{"id":"what-happens-when-rdi-cant-write-to-the-target-redis-database","title":"What happens when RDI can't write to the target Redis database?","role":"content","text":"RDI will keep attempting to write the changes to the target and will also attempt\nto reconnect to it, if necessary. While the target is disconnected, RDI\nwill keep capturing change events from the source database and adding them to its\nstreams in the staging database. This continues until the staging database gets\nlow on space to store new events. When RDI detects this, it applies a \"back pressure\"\nmechanism to capture data from the source less frequently, which reduces the risk of running\nout of space altogether. The systems that the source databases use to record changes can\nretain the change data for at least a few hours, and RDI can catch up with the\nchanges as soon as the target connection recovers or the staging database has\nmore space available."},{"id":"what-does-rdi-do-if-the-data-is-corrupted-or-invalid","title":"What does RDI do if the data is corrupted or invalid?","role":"content","text":"The collector reports the data to RDI in a structured JSON format. If\nthe structure of the JSON data is invalid or if there is a fatal bug in the transformation\njob then RDI can't transform the data. When this happens, RDI will store the original data\nin a \"dead letter queue\" along with a message to say why it was rejected. The dead letter\nqueue is stored as a capped stream in the RDI staging database. You can see its contents\nwith Redis Insight or with the\n[`redis-di list-dlq-records`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list-dlq-records)\ncommand from the CLI.\n\nSee [Rejected records](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/rejected-records) for more information about DLQ."},{"id":"can-i-use-rdi-without-persistence-enabled","title":"Can I use RDI without persistence enabled?","role":"content","text":"By default, RDI requires persistence to be enabled on the RDI database. This ensures that RDI can recover both its configuration and the last known state if the cluster crashes.\n\nIf you don't have permissions to use persistence due to compliance or other reasons, you can disable\nthe persistence check on the RDI database (Helm installation only). If you do this, RDI will not be\nable to recover from a crash, and you will have to perform a new deploy to reinitialize the pipeline.\n\nTo disable the persistence check, set the `aofRequired` value to `false` in the `operator.prerequisiteChecks`\nsection of the `values.yaml` file.\n\n[code example]\n\nThis option is available in RDI 1.16.2 and later."},{"id":"which-processor-should-i-use","title":"Which processor should I use?","role":"content","text":"RDI ships with two stream processor implementations: the *classic*\nprocessor and the *Flink* processor. Both are fully supported for\nproduction on VM and Kubernetes installations. The Flink processor\nis generally available as of RDI 1.19.0 and is enabled per pipeline.\n\nThe Flink processor delivers significantly higher snapshot throughput,\nlower end-to-end latency, horizontal scaling, and Flink checkpointing\non top of the same at-least-once delivery guarantees as the classic\nprocessor. It also adds optional expression and `redis.lookup` result\ncaching.\n\n**We strongly recommend using the Flink processor** for new pipelines and\nmigrating existing pipelines to it, to benefit from these improvements. It is\nthe default, so a pipeline whose `config.yaml` does not set a processor type\nuses it. The *classic* processor remains a fully supported choice for now.\nIt may be deprecated in a future release.\n\nTo switch a pipeline to the classic processor, set\n[`processors.type`](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config#processors)\nto `classic`. You can do that per pipeline without changing the others.\n\nSee\n[Differences between the classic and Flink processors](https://redis.io/docs/latest/integrate/redis-data-integration/architecture/classic-vs-flink)\nfor a side-by-side comparison and\n[Migrate from the classic processor to the Flink processor](https://redis.io/docs/latest/integrate/redis-data-integration/installation/migration-classic-to-flink)\nfor a step-by-step migration guide."}],"examples":[{"id":"can-i-use-rdi-without-persistence-enabled-ex0","language":"yaml","code":"operator:\n  prerequisiteChecks:\n    aofRequired: false","section_id":"can-i-use-rdi-without-persistence-enabled"}]}
{"schema_version":2,"id":"integrate/redis-data-integration","title":"Redis Data Integration","url":"https://redis.io/docs/latest/integrate/redis-data-integration/","summary":"","aliases":["/integrate/redis-data-integration/ingest"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-10-18T02:02:34+03:00","children":[{"id":"integrate/redis-data-integration/when-to-use","summary":"Understand when (and when not) to use RDI.","title":"When to use RDI","url":"https://redis.io/docs/latest/integrate/redis-data-integration/when-to-use/"},{"id":"integrate/redis-data-integration/quick-start-guide","summary":"Get started with a simple pipeline example","title":"Quickstart","url":"https://redis.io/docs/latest/integrate/redis-data-integration/quick-start-guide/"},{"id":"integrate/redis-data-integration/installation","summary":"Learn how to install and upgrade RDI","title":"Install and upgrade","url":"https://redis.io/docs/latest/integrate/redis-data-integration/installation/"},{"id":"integrate/redis-data-integration/architecture","summary":"Discover the main components of RDI","title":"Architecture","url":"https://redis.io/docs/latest/integrate/redis-data-integration/architecture/"},{"id":"integrate/redis-data-integration/cdc-offsets-and-retention","summary":"Learn why RDI can lose access to source change history during an interruption and how to plan for recovery.","title":"Understand CDC offsets and source retention","url":"https://redis.io/docs/latest/integrate/redis-data-integration/cdc-offsets-and-retention/"},{"id":"integrate/redis-data-integration/data-pipelines","summary":"Learn how to configure RDI for data capture and transformation.","title":"Data pipelines","url":"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/"},{"id":"integrate/redis-data-integration/observability","summary":"Learn how to monitor RDI","title":"Observability","url":"https://redis.io/docs/latest/integrate/redis-data-integration/observability/"},{"id":"integrate/redis-data-integration/faq","summary":"Find answers to common questions about RDI","title":"FAQ","url":"https://redis.io/docs/latest/integrate/redis-data-integration/faq/"},{"id":"integrate/redis-data-integration/troubleshooting","summary":"Solve and report simple problems with RDI","title":"Troubleshooting","url":"https://redis.io/docs/latest/integrate/redis-data-integration/troubleshooting/"},{"id":"integrate/redis-data-integration/reference","summary":"View reference material for Redis Data Integration","title":"Reference","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/"},{"id":"integrate/redis-data-integration/release-notes","summary":"","title":"Release notes","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/"},{"id":"integrate/redis-data-integration/rdi-archive","summary":"Describes where to view the preview version for RDI products","title":"Preview version","url":"https://redis.io/docs/latest/integrate/redis-data-integration/rdi-archive/"}],"page_type":"content","content_hash":"93fbe89db4be78c1ffd5de278e341ddd2d147859074a7bb6bbf8e439412d2dca","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Data Integration (RDI) keeps your Redis cache in sync with a primary system-of-record database in near real time.\n\nRDI's purpose is to help Redis customers sync Redis Enterprise with live data from their slow disk based databases in order to:\n\n- Meet the required speed and scale of read queries and provide an excellent and predictable user experience.\n- Save resources and time when building pipelines and coding data transformations.\n- Reduce the total cost of ownership by saving money on expensive database read replicas.\n\nIf you use a relational database as the system of record for your app,\nyou may eventually find\nthat its performance doesn't scale well as your userbase grows. It may be\nacceptable for a few thousand users but for a few million, it can become a\nmajor problem. If you don't have the option of abandoning the relational\ndatabase, you should consider using a fast\ndatabase, such as Redis, to cache data from read queries. Since read queries\nare typically many times more common than writes, the cache will greatly\nimprove performance and let your app scale without a major redesign.\n\nRDI keeps a Redis cache up to date with changes in the primary database, using a\n[*Change Data Capture (CDC)*](https://en.wikipedia.org/wiki/Change_data_capture) mechanism.\nIt also lets you *transform* the data from relational tables into convenient\nand fast data structures that match your app's requirements. You specify the\ntransformations using a configuration system, so no coding is necessary. RDI supports both standard Redis databases and [Active-Active (CRDB)](https://redis.io/active-active/) replication targets."},{"id":"rdi-in-redis-cloud","title":"RDI in Redis Cloud","role":"content","text":"RDI is also available as a fully managed service on Redis Cloud, removing the need to install or maintain the underlying infrastructure. Redis manages the compute, scaling, and upgrades for you. You define the source connection and pipeline configuration using the Redis Cloud console.\n\nThe Cloud service currently supports AWS-hosted source databases (Amazon RDS, Amazon Aurora, and Amazon EC2), as well as MongoDB Atlas and Snowflake, writing to a Redis Cloud Pro target database.\n\nSee [Data Integration](https://redis.io/docs/latest/operate/rc/rdi) in the Redis Cloud documentation for\nfull setup instructions, prerequisites, and a quick start guide."},{"id":"features","title":"Features","role":"content","text":"RDI provides enterprise-grade streaming data pipelines with the following features:\n\n- **Near realtime pipeline** - The CDC system captures changes in very short time intervals,\n  then ships and processes them in *micro-batches* to provide near real time updates to Redis.\n- **At least once guarantee** - RDI will deliver any change to the selected data set at least\n  once to the target Redis database.\n- **Data integrity** - RDI keeps the data change order per source table or unique key.\n- **High availability** - All stateless components have hot failover or quick automatic recovery.\n  RDI state is always highly available using Redis Enterprise replication.\n- **Easy to install and operate** - Use a self-documenting command line interface (CLI)\n  for all installation and day-two operations.\n- **No coding needed** - Create and test your pipelines using Redis Insight.\n- **Data-in-transit encryption** - RDI never persists data to disk. All data in-flight is\n  protected using TLS or mTLS connections.\n- **Observability - Metrics** - RDI collects data processing counters at source table granularity\n  along with data processing performance metrics. These are available via GUI, CLI and \n  [Prometheus](https://prometheus.io/) endpoints.\n- **Observability - logs** - RDI saves rotating logs to a single folder. They are in a JSON format,\n  so you can collect and process them with your favorite observability tool.\n- **Backpressure mechanism** - RDI is designed to backoff writing data when the cache gets\n  disconnected, which prevents cascading failure. Since the change data is persisted in the source\n  database and Redis is very fast, RDI can easily catch up with missed changes after a short period of\n  disconnection. See [Backpressure mechanism](https://redis.io/docs/latest/integrate/redis-data-integration/architecture#backpressure-mechanism) for more information.\n- **Recovering from full failure** - If the cache fails or gets disconnected for a long time,\n  RDI can reconstruct the cache data in Redis using a full snapshot of the defined dataset.\n- **High throughput** - Because RDI uses Redis for staging and writes to Redis as a target,\n  it has very high throughput. With a single processor core and records of about 1KB in size,\n  RDI processes around 10,000 records per second. While taking the initial full *snapshot* of\n  the source database, RDI automatically scales to a configurable number of processing units,\n  to fill the cache as fast as possible."},{"id":"when-to-use-rdi","title":"When to use RDI","role":"content","text":"RDI is highly configurable but it is not intended to be a general\nsolution for all data integration tasks. See\n[When to use RDI](https://redis.io/docs/latest/integrate/redis-data-integration/when-to-use)\nto find out if your use case is a good fit for RDI's features."},{"id":"supported-source-databases","title":"Supported source databases","role":"compatibility","text":"RDI can capture data from any of the following sources:\n\n| Database | Versions | AWS RDS  Versions | GCP SQL Versions |\n| :-- | :-- | :-- | :-- |\n| Oracle | 19c, 21c, 23ai (LogMiner only) | 19c, 21c | - |\n| MariaDB | 10.5, 11.4.x, 11.7.x | 10.4 to 10.11 | - |\n| MongoDB | 6.0, 7.0, 8.0 | - | - |\n| MySQL | 5.7, 8.0.x, 8.4.x, 9.0, 9.1 | 8.0.x | 8.0 |\n| PostgreSQL | 10, 11, 12, 13, 14, 15, 16, 17, 18 | 11, 12, 13, 14, 15, 16, 17, 18 | 15 |\n| Supabase (uses PostgreSQL) | 10, 11, 12, 13, 14, 15, 16, 17  | - | - |\n| SQL Server | 2017, 2019, 2022 | 2016, 2017, 2019, 2022 | 2019 |\n| Spanner | - | - | All versions |\n| AlloyDB for PostgreSQL | 14.2, 15.7 | - | 14.2, 15.7 |\n| AWS Aurora/PostgreSQL | 15 | 15 | - |\n| Neon | 14, 15, 16, 17 | - | - |\n| Snowflake (preview) | - | - | - |"},{"id":"continue-learning-with-redis-university","title":"Continue learning with Redis University","role":"content","text":"* [Redis Data Integration Lab](https://university.redis.io/course/2qa1u1ss21vsy5?tab=details)"},{"id":"documentation","title":"Documentation","role":"content","text":"Learn why a saved capture position can become unusable in\n[Understand CDC offsets and source retention](https://redis.io/docs/latest/integrate/redis-data-integration/cdc-offsets-and-retention).\n\nLearn more about RDI from the other pages in this section:"}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/installation/ha-test","title":"Test HA failover","url":"https://redis.io/docs/latest/integrate/redis-data-integration/installation/ha-test/","summary":"Learn how to perform HA failover testing for Redis Data Integration (RDI) to ensure high availability and reliability of your data integration setup.","tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-18T16:39:21+01:00","page_type":"content","content_hash":"a88a39077f12252ac6477ad2e876643fe57ecb1a0c25397f1eb5a6915c3be937","sections":[{"id":"setup","title":"Setup","role":"setup","text":"1. Ensure that RDI is up and running on both primary and secondary nodes.\n   Run the following command and verify and that each instance should show healthy and running `rdi-api` and `rdi-operator` pods.\n[code example]\n\n2. Identify the leader node (this is the one that has a running collector pod). A\n   collector is named after its source, so the example above shows `collector-mysql` for a\n   source named `mysql`.\n\n   You can also ask each node about the pipeline directly. On the follower, the pipeline\n   stands by:\n\n   [code example]\n\n   [`redis-di describe`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-describe)\n   shows `Status: standby` on the follower, and\n   [`redis-di info`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-info)\n   shows `Leader Election Mode: follower`."},{"id":"performing-the-ha-failover-testing","title":"Performing the HA Failover Testing","role":"content","text":"To perform HA, you can simulate a connection failure between the leader and the RDI database by blocking the network traffic. You can do this by running the following commands on the leader node:\n\n1. Identify the RDI database IP (replace `<hostname>` with your own hostname):\n[code example]\n\n2. For each of the IPs returned by the above command, run the following command to block the traffic:\n\n[code example]\n\n\nThe default configuration for the leader lock is 60 seconds, so it may take up to 2 minutes for the failover to occur.\nMeanwhile you can follow the logs of the operator to see the failover process:\n\n[code example]\n\nIn about 10 seconds you will start seeing log entries from the leader saying that it could not acquire the leadership.\nWhen the leader lock expires, the second node will acquire the leadership and you will see log entries from the second node indicating that it has become the leader.\n\nOnce the failover completes, the two nodes swap their pipeline phases: the pipeline has\n`Active` status on the new leader and `Standby` status on the old one."},{"id":"cleanup","title":"Cleanup","role":"content","text":"To clean up after the test, remove the `iptables` rule that you added to block the traffic:\n\n```sudo iptables -D FORWARD -d <databse_ip> -j DROP```\n\nUse `sudo iptables -S | grep <database_ip>` to verify that the rule has been removed."}],"examples":[{"id":"setup-ex0","language":"plaintext","code":"kubectl -n rdi get pods\n\n# Example output:\nNAME                                   READY   STATUS      RESTARTS      AGE\ncollector-api-577d95bfd8-5wbg6         1/1     Running     0             12m\ncollector-mysql-95f45bcf7-vwn5l        1/1     Running     0             12m\nfluentd-zq2lc                          1/1     Running     0             72m\nlogrotate-29530445-j729x               0/1     Completed   0             14m\nlogrotate-29530450-dprr2               0/1     Completed   0             9m40s\nlogrotate-29530455-mfmzw               0/1     Completed   0             4m40s\nprocessor-f66655469-h7nw2              1/1     Running     0             12m\nrdi-api-f75df6796-qwqjw                1/1     Running     0             72m\nrdi-metrics-exporter-d57cdf8c8-wjzb5   1/1     Running     0             72m\nrdi-operator-7f7f6c7dfd-5qmjd          1/1     Running     0             71m\nrdi-reloader-77df5f7854-lwmvz          1/1     Running     0             71m","section_id":"setup"},{"id":"setup-ex1","language":"plaintext","code":"kubectl -n rdi get pipeline default -o jsonpath='{.status.phase}'\n\n   # Example output on the follower:\n   Standby\n\n   # Example output on the leader:\n   Active","section_id":"setup"},{"id":"performing-the-ha-failover-testing-ex0","language":"plaintext","code":"dig +short <hostname>\n\n# Example:\n# dig +short my.redis.hostname.com\n\n# Example output:\n54.78.220.161","section_id":"performing-the-ha-failover-testing"},{"id":"performing-the-ha-failover-testing-ex1","language":"plaintext","code":"sudo iptables -I FORWARD -d <database_ip> -j DROP\n\n# With the IP from the example above, the command would be:\nsudo iptables -I FORWARD -d 54.78.220.161 -j DROP","section_id":"performing-the-ha-failover-testing"},{"id":"performing-the-ha-failover-testing-ex2","language":"plaintext","code":"kubectl -n rdi logs rdi-operator-7f7f6c7dfd-5qmjd -f","section_id":"performing-the-ha-failover-testing"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/installation","title":"Install and upgrade","url":"https://redis.io/docs/latest/integrate/redis-data-integration/installation/","summary":"Learn how to install and upgrade RDI","aliases":["/integrate/redis-data-integration/ingest/installation/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-18T16:39:21+01:00","children":[{"id":"integrate/redis-data-integration/installation/reqsummary","summary":"Requirements and recommendations for RDI installations.","title":"Requirements summary","url":"https://redis.io/docs/latest/integrate/redis-data-integration/installation/reqsummary/"},{"id":"integrate/redis-data-integration/installation/install-vm","summary":"Learn how to install RDI on one or more VMs","title":"Install on VMs","url":"https://redis.io/docs/latest/integrate/redis-data-integration/installation/install-vm/"},{"id":"integrate/redis-data-integration/installation/install-k8s","summary":"Learn how to install RDI on Kubernetes","title":"Install on Kubernetes","url":"https://redis.io/docs/latest/integrate/redis-data-integration/installation/install-k8s/"},{"id":"integrate/redis-data-integration/installation/upgrade","summary":"Learn how to upgrade an existing RDI installation","title":"Upgrading RDI","url":"https://redis.io/docs/latest/integrate/redis-data-integration/installation/upgrade/"},{"id":"integrate/redis-data-integration/installation/migration-classic-to-flink","summary":"Learn how to migrate an existing RDI pipeline from the classic processor to the Apache Flink-based processor.","title":"Migrate from the classic processor to the Flink processor","url":"https://redis.io/docs/latest/integrate/redis-data-integration/installation/migration-classic-to-flink/"},{"id":"integrate/redis-data-integration/installation/ha-test","summary":"Learn how to perform HA failover testing for Redis Data Integration (RDI) to ensure high availability and reliability of your data integration setup.","title":"Test HA failover","url":"https://redis.io/docs/latest/integrate/redis-data-integration/installation/ha-test/"}],"page_type":"content","content_hash":"80f633b5e4be1d19be2dff38c17bec640c760710c86344d4eb3baaa3a3b304ce","sections":[{"id":"content","title":"Content","role":"content","text":"The guides in this section explain the options you have for installing and upgrading RDI on your own servers. See the [Redis Cloud RDI guide](https://redis.io/docs/latest/operate/rc/rdi) to \nlearn how to set up RDI for a cloud database.\nBefore you use RDI, you must also configure each of your source databases to enable CDC. See the\n[Prepare source databases](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs)\nsection to learn how to do this."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/installation/install-k8s","title":"Install on Kubernetes","url":"https://redis.io/docs/latest/integrate/redis-data-integration/installation/install-k8s/","summary":"Learn how to install RDI on Kubernetes","tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-18T16:39:21+01:00","page_type":"content","content_hash":"9a2082cae2b65fc53d75ab94a52b4cb7b00a0ca4ac8deb21692ebc84014dbd24","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This guide explains how to use the RDI [Helm chart](https://helm.sh/docs/topics/charts/)\nto install on [Kubernetes](https://kubernetes.io/) (K8s). You can also\n[Install RDI on VMs](https://redis.io/docs/latest/integrate/redis-data-integration/installation/install-vm).\n\nThe installation creates the following K8s objects:\n\n-   A K8s [namespace](https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/) named `rdi`.\n    You can also use a different namespace name if you prefer.\n-   [Deployments](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) and \n    [services](https://kubernetes.io/docs/concepts/services-networking/service/) for the \n    [RDI operator](https://redis.io/docs/latest/integrate/redis-data-integration/architecture#how-rdi-is-deployed),\n    [metrics exporter](https://redis.io/docs/latest/integrate/redis-data-integration/observability), and API server.\n-   A [service account](https://kubernetes.io/docs/concepts/security/service-accounts/) \n    and [RBAC resources](https://kubernetes.io/docs/reference/access-authn-authz/rbac) for the RDI operator.\n-   A [ConfigMap](https://kubernetes.io/docs/concepts/configuration/configmap/) with RDI database details.\n-   [Secrets](https://kubernetes.io/docs/concepts/configuration/secret/)\n    with the RDI database credentials and TLS certificates.\n-   Other optional K8s resources such as [ingresses](https://kubernetes.io/docs/concepts/services-networking/ingress/) \n    that can be enabled depending on your K8s environment and needs.\n\nYou can use this installation on [OpenShift](https://docs.openshift.com/) and other K8s distributions\nincluding cloud providers' K8s managed clusters.\n\nYou can configure the RDI Helm chart to pull the RDI images from [dockerhub](https://hub.docker.com/u/redis) \nor from your own [private image registry](#using-a-private-image-registry)."},{"id":"before-you-install","title":"Before you install","role":"content","text":"Complete the following steps before installing the RDI Helm chart:\n\n-   [Create the RDI database](#create-the-rdi-database) on your Redis Enterprise cluster.\n\n-   Create a [user](https://redis.io/docs/latest/operate/rs/security/access-control/create-users)\n    for the RDI database if you prefer not to use the default password (see\n    [Access control](https://redis.io/docs/latest/operate/rs/security/access-control) for\n    more information).\n\n-   Download the RDI Helm chart tar file from the\n    [Redis download center](https://redis-enterprise-software-downloads.s3.amazonaws.com/redis-di/rdi-.tgz) (in the *Modules, Tools & Integration* category) .\n\n    [code example]\n\n-   If you want to use a private image registry,\n    [prepare it with the RDI images](#using-a-private-image-registry).\n\n-   [Download the RDI CLI](#download-the-rdi-cli), which you use to deploy and manage pipelines."},{"id":"create-the-rdi-database","title":"Create the RDI database","role":"content","text":"RDI uses a database on your Redis Enterprise cluster to store its state\ninformation. Use the Redis Enterprise Cluster Manager UI to create the RDI database with the following\nrequirements:\n\n* Redis Enterprise v6.4 or greater for the cluster.\n* For production, 250MB RAM with one primary and one replica is recommended, but for the\n  quickstart or for development, 125MB and a single shard is sufficient.\n* If you are deploying RDI for a production environment then secure this database with a password\n  and TLS.\n* Set the database's\n  [eviction policy](https://redis.io/docs/latest/operate/rs/databases/memory-performance/eviction-policy) to `noeviction`. Note that you can't set this using\n  [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin),\n  so you must either do it using the admin UI or with the following\n  [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api)\n  command:\n\n  [code example]\n* Set the database's\n  [data persistence](https://redis.io/docs/latest/operate/rs/databases/configure/database-persistence)\n  to AOF - fsync every 1 sec. Note that you can't set this using\n  [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin),\n  so you must either do it using the admin UI or with the following\n  [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api)\n  commands:\n\n  [code example]\n If you don't have permissions to use AOF persistence, please check the [Using RDI without persistence](https://redis.io/docs/latest/integrate/redis-data-integration/faq#can-i-use-rdi-without-persistence-enabled) section in the FAQ.\n\n* **Ensure that the RDI database is not clustered.** RDI will not work correctly if the\n  RDI database is clustered (but note that the target database *can* be clustered without\n  any problems).\n\n  When you create the RDI database, expand the **Clustering** section and make sure the\n  **Sharding** option is *unchecked* (as shown below).\n\n  ![The Sharding option is unchecked in the Clustering section of the create database form.](https://redis.io/docs/latest/images/rdi/ingest/RDIClusterSetting.webp)\n\n  You can check if your RDI database is clustered from its **Configuration** tab in the\n  Cluster Manager UI. In the **Clustering** section, **Sharding** should be set to **Disabled**,\n  as shown in the following screenshot:\n\n  ![The Clustering section of the database Configuration tab shows Sharding: Disabled.](https://redis.io/docs/latest/images/rdi/ingest/RDICheckUnclustered.webp)\n\n  If you find the database has been clustered by mistake, you must create a new database with\n  sharding disabled before continuing with the RDI installation.\n\n\nYou should then provide the details of this database in the [`values.yaml`](#the-valuesyaml-file)\nfile as described below."},{"id":"using-a-private-image-registry","title":"Using a private image registry","role":"content","text":"Add the RDI images from [dockerhub](https://hub.docker.com/u/redis) to your local registry.\nYou need the following RDI images with tags matching the RDI version you want to install:\n\n-   [redis/rdi-api](https://hub.docker.com/r/redis/rdi-api)\n-   [redis/rdi-operator](https://hub.docker.com/r/redis/rdi-operator)\n-   [redis/rdi-monitor](https://hub.docker.com/r/redis/rdi-monitor)\n-   [redis/rdi-processor](https://hub.docker.com/r/redis/rdi-processor)\n-   [redis/rdi-collector-api](https://hub.docker.com/r/redis/rdi-collector-api)\n-   [redis/rdi-collector-initializer](https://hub.docker.com/r/redis/rdi-collector-initializer)\n\nIf you plan to use the Flink processor for any of your pipelines, you'll also need:\n\n-   [redis/rdi-flink-processor](https://hub.docker.com/r/redis/rdi-flink-processor)\n-   [redis/rdi-metrics-aggregator](https://hub.docker.com/r/redis/rdi-metrics-aggregator)\n\nIf you plan to use the Flink processor exclusively, the `redis/rdi-processor`\nand `redis/rdi-monitor` images are not required.\n\nIf you plan to use Spanner as a source for your pipeline, you'll also need\n[redis/rdi-flink-collector](https://hub.docker.com/r/redis/rdi-flink-collector).\n\nIf you plan to use Snowflake as a source for any of your pipelines, you'll also need\n[riotx/riotx:v1.12.0](https://hub.docker.com/r/riotx/riotx):\n[RIOT-X](https://redis.github.io/riotx/), a data ingestion and replication tool for Redis.\n\nIn addition, the RDI Helm chart uses the following 3rd party images:\n\n-   [redislabs/debezium-server:3.5.0.Final-rdi.3](https://hub.docker.com/r/redislabs/debezium-server),\n    based on `quay.io/debezium/server/3.5.0.Final` with minor modifications:\n    [Debezium](https://debezium.io/), an open source distributed platform for change data capture.\n-   [redis/reloader:v1.4.13](https://hub.docker.com/r/redis/reloader), originally `ghcr.io/stakater/reloader:v1.4.13`:\n    [Reloader](https://github.com/stakater/Reloader), a K8s controller to watch changes to ConfigMaps\n    and Secrets and do rolling upgrades.\n\nThe example below shows how to specify the registry and image pull secret in your\n[`rdi-values.yaml`](#the-valuesyaml-file) file for the Helm chart:\n\n[code example]\n\nTo pull images from a private image registry, you must provide the image pull secret and in some cases also set the permissions. Follow the links below to learn how to use a private registry with:\n\n-   [Rancher](https://ranchermanager.docs.rancher.com/how-to-guides/new-user-guides/kubernetes-resources-setup/kubernetes-and-docker-registries#using-a-private-registry)\n-   [OpenShift](https://docs.openshift.com/container-platform/4.17/openshift_images/managing_images/using-image-pull-secrets.html)\n-   [Amazon Elastic Kubernetes Service (EKS)](https://docs.aws.amazon.com/AmazonECR/latest/userguide/ECR_on_EKS.html)\n-   [Google Kubernetes Engine (GKE)](https://cloud.google.com/artifact-registry/docs/pull-cached-dockerhub-images)\n-   [Azure Kubernetes Service (AKS)](https://learn.microsoft.com/en-us/azure/aks/cluster-container-registry-integration?tabs=azure-cli)"},{"id":"download-the-rdi-cli","title":"Download the RDI CLI","role":"content","text":"You manage RDI with the [`redis-di` CLI](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli),\nwhich you use to deploy pipelines, set secrets, and inspect status. Unlike the VM installation, which\nbundles the CLI, a Kubernetes installation requires you to download it separately from the Redis\ndownload center.\n\nThe CLI is currently built for the following platforms. Download the binary that matches the operating\nsystem and architecture of the machine you will run it from. On Linux and macOS, if you are not sure\nwhich to choose, run `uname -sm`: `Linux x86_64` is Linux amd64, `Linux aarch64` is Linux arm64,\n`Darwin x86_64` is macOS on Intel, and `Darwin arm64` is macOS on Apple silicon.\n\n| Platform | Download |\n| :-- | :-- |\n| Linux, x86-64 (amd64) | [`redis-di`](https://redis-enterprise-software-downloads.s3.amazonaws.com/redis-di/cli//bin/linux-amd64/redis-di) |\n| Linux, ARM64 (aarch64) | [`redis-di`](https://redis-enterprise-software-downloads.s3.amazonaws.com/redis-di/cli//bin/linux-arm64/redis-di) |\n| macOS, Intel (amd64) | [`redis-di`](https://redis-enterprise-software-downloads.s3.amazonaws.com/redis-di/cli//bin/darwin-amd64/redis-di) |\n| macOS, Apple silicon (arm64) | [`redis-di`](https://redis-enterprise-software-downloads.s3.amazonaws.com/redis-di/cli//bin/darwin-arm64/redis-di) |\n| Windows, x86-64 (amd64) | [`redis-di.exe`](https://redis-enterprise-software-downloads.s3.amazonaws.com/redis-di/cli//bin/windows-amd64/redis-di.exe) |\n\nFor example, to download the CLI for Linux amd64, make it executable, and put it on your `PATH`:\n\n[code example]\n\n> [!NOTE]\n> The macOS and Windows binaries are not currently signed or notarized, so the operating\n> system may block them the first time you run them. On macOS, allow the binary to run in\n> **System Settings > Privacy & Security**, or remove the quarantine attribute with\n> `xattr -d com.apple.quarantine ./redis-di`, and then run it again. On Windows, if Microsoft Defender\n> SmartScreen blocks it, choose **More info > Run anyway**."},{"id":"supported-versions-of-kubernetes-and-openshift","title":"Supported versions of Kubernetes and OpenShift","role":"compatibility","text":"RDI only supports versions of Kubernetes and OpenShift that have\nnot yet reached end-of-life (EOL). See the\n[Kubernetes](https://kubernetes.io/releases/) and\n[OpenShift](https://access.redhat.com/support/policy/updates/openshift)\nlifecycle pages for the latest updates."},{"id":"install-the-rdi-helm-chart","title":"Install the RDI Helm chart","role":"setup","text":"1.  Scaffold the default `values.yaml` file from the chart into a local\n    `rdi-values.yaml` file:\n\n    [code example]\n\n1.  Open the `rdi-values.yaml` file you just created, change or add the appropriate\n    values for your installation, and delete the values you have not changed to \n    use their default values. \n    See [The `values.yaml` file](#the-valuesyaml-file) for more details.\n\n1.  Run the `helm upgrade --install` command:\n\n    [code example]\n\n    > [!NOTE]\n    > The above command will install RDI in a namespace called\n    > `rdi`. If you want to use a different namespace, pass the option\n    > `-n <custom-namespace>` to the `helm install` command instead.\n\n    &nbsp;\n\n    > [!WARNING]\n    > Only one RDI installation is supported per Kubernetes\n    > cluster. Installing RDI into multiple namespaces in the same cluster is\n    > not supported and will fail. If you need more than one RDI deployment,\n    > use separate Kubernetes clusters."},{"id":"the-valuesyaml-file","title":"The `values.yaml` file","role":"content","text":"The [`values.yaml`](https://helm.sh/docs/topics/charts/#templates-and-values) file inside the\nHelm chart contains the values you can set for the RDI Helm installation.\nSee the comments by each value for more information about the values you may need to add or change \ndepending on your use case.\n\nAt a minimum, you must set the values of `connection.host`, `connection.port`, and `connection.password`\nto enable the basic connection to the RDI database. \nYou must also set `api.jwtKey`, RDI uses this value to encrypt the\n[JSON web token (JWT)](https://jwt.io/) token used by RDI API. Best practice is\nto generate a value containing 32 random bytes of data (equivalent to 256\nbits) and then encode this value as ASCII characters. Use the following\ncommand to generate the random key from the\n[`urandom` special file](https://en.wikipedia.org/wiki//dev/random):\n\n[code example]\n\nIf you use TLS to connect to the RDI database, you must set the\nCA certificate content in `connection.ssl.cacert` (for TLS). In addition, if you\nalso use mTLS, you must set the client certificate and private key contents in\n`connection.ssl.cert`, and `connection.ssl.key`. \n\n-   You can add the certificate content directly in the `rdi-values.yaml` file\n    as follows:\n\n    [code example]\n\n-   Alternatively, you can use the `--set-file` argument to set these values to\n    the content of your certificate files as follows:\n\n    [code example]\n\n> [!NOTE]\n> Please see [these docs](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/spanner#6-additional-kubernetes-configuration) if this RDI installation is for use with GCP Spanner.\n\nIf you are deploying to [OpenShift](https://docs.openshift.com/), you must\nset `global.openshift` to `true`:\n\n[code example]\n\nSet `global.securityContext.runAsUser` and\n`global.securityContext.runAsGroup` to the appropriate values for your\nOpenShift environment.\n\n[code example]\n\n> [!WARNING]\n> The default OpenShift Security Context Constraints (SCCs)\n> will not allow RDI to run if `global.securityContext.runAsUser`\n> and `global.securityContext.runAsGroup` have their default values of `1000`.\n> You must edit your `rdi-values.yaml` file to ensure these values are\n> in the valid range for your OpenShift environment.\n>\n> Use the following [OpenShift CLI](https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/cli_tools/openshift-cli-oc) command\n> to find the user and group ranges for your project:\n>\n> [code example]"},{"id":"configure-the-flink-processor","title":"Configure the Flink processor","role":"configuration","text":"RDI ships with two stream processor implementations: the *classic* processor and the\ndefault [Apache Flink](https://flink.apache.org/)-based *Flink* processor.\nSee\n[Stream processor implementations](https://redis.io/docs/latest/integrate/redis-data-integration/architecture#stream-processor-implementations)\nfor an overview of the differences and\n[Differences between the classic and Flink processors](https://redis.io/docs/latest/integrate/redis-data-integration/architecture/classic-vs-flink)\nfor a side-by-side comparison.\n\nTo configure the Flink processor at the Helm chart level, add the\n`operator.dataPlane.flinkProcessor` block to your `rdi-values.yaml` file. The\nsnippet below shows a few of the most commonly adjusted values. See the\n`flinkProcessor` block in the Helm chart's `values.yaml` for the full set of\nsupported values.\n\n[code example]\n\nConfiguring the Flink processor at the Helm chart level only sets the values\nthat the operator will use when deploying the JobManager and TaskManager workloads.\nA pipeline runs on the Flink processor unless its `config.yaml` sets\n[`processors.type`](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config#processors)\nto `classic`. Fine-tune the Flink runtime\nthrough the `processors.advanced` section of `config.yaml` (see the\n[configuration reference](https://redis.io/docs/latest/integrate/redis-data-integration/reference/config-yaml-reference#processors)).\n\nFor migrating existing pipelines to the Flink processor, see\n[Migrate from the classic processor to the Flink processor](https://redis.io/docs/latest/integrate/redis-data-integration/installation/migration-classic-to-flink)."},{"id":"check-the-installation","title":"Check the installation","role":"content","text":"To verify the status of the K8s deployment, run the following command:\n\n[code example]\n\nThe output looks like the following. Check that the `rdi` release is listed.\nWith RDI 1.8.0 or later, check that the `default` release is also listed.\n\n[code example]\n\nAlso, check that all pods have `Running` status:\n\n[code example]\n\nYou can verify that the RDI API works by running\n[`redis-di info`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-info)\nagainst it, or by adding a connection to the RDI API server to\n[Redis Insight](https://redis.io/docs/latest/develop/tools/insight/rdi-connector)."},{"id":"using-ingress-controllers","title":"Using ingress controllers","role":"content","text":"You must ensure that an appropriate\n[ingress controller](https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/)\nis available in your K8s cluster to expose the RDI API service via the K8s\n[`Ingress`](https://kubernetes.io/docs/concepts/services-networking/ingress/)\nresource. Follow the documentation of your cloud provider or of\nthe ingress controller to install the controller correctly."},{"id":"using-the-nginx-ingress-controller-on-aks","title":"Using the `nginx` ingress controller on AKS","role":"content","text":"On AKS, if you want to use the open source\n[`nginx`](https://nginx.org/)\n[ingress controller](https://github.com/kubernetes/ingress-nginx/blob/main/README.md#readme)\nrather than the\n[AKS application routing add-on](https://learn.microsoft.com/en-us/azure/aks/app-routing),\nfollow the AKS documentation for\n[creating an unmanaged ingress controller](https://learn.microsoft.com/en-us/troubleshoot/azure/azure-kubernetes/load-bal-ingress-c/create-unmanaged-ingress-controller?tabs=azure-cli).\nSpecifically, ensure that one or both of the following Helm chart values is set:\n\n- `controller.service.annotations.\"service\\.beta\\.kubernetes\\.io/azure-load-balancer-health-probe-request-path\"=/healthz`\n- `controller.service.externalTrafficPolicy=Local`"},{"id":"prepare-your-source-databases","title":"Prepare your source databases","role":"content","text":"Before deploying a pipeline, you must configure each source database to enable CDC. See the\n[Prepare source databases](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs)\nsection to learn how to do this. A pipeline can capture from more than one source database,\nso you must prepare each one each source separately. See\n[Multiple sources in one pipeline](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/multiple-sources) for details."},{"id":"deploy-a-pipeline","title":"Deploy a pipeline","role":"content","text":"When the Helm installation is complete and you have prepared your source databases for CDC,\nyou are ready to start using RDI. See the guides on how to\n[configure](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines) and\n[deploy](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/deploy)\nRDI pipelines for more information. You can also configure and deploy a pipeline\nusing [Redis Insight](https://redis.io/docs/latest/develop/tools/insight). See\n[RDI in Redis Insight](https://redis.io/docs/latest/develop/tools/insight/rdi-connector)\nfor full details on how to connect to RDI and deploy pipelines."},{"id":"uninstall-rdi","title":"Uninstall RDI","role":"content","text":"If you want to remove your RDI K8s installation, first run\nthe following commands. (If you installed RDI into a custom namespace then\nreplace `rdi` with the name of your namespace.)\n\n[code example]\n\n> [!NOTE]\n> The line `kubectl delete pipeline default -n rdi` is only needed for RDI 1.8.0 or above.\n\nIf you also want to delete the keys from your RDI database, connect to it with\n[`redis-cli`](https://redis.io/docs/latest/develop/tools/cli) and run a\n[`FLUSHALL`](https://redis.io/docs/latest/commands/flushall) command."}],"examples":[{"id":"before-you-install-ex0","language":"bash","code":"export RDI_VERSION=\n    wget https://redis-enterprise-software-downloads.s3.amazonaws.com/redis-di/rdi-$RDI_VERSION.tgz","section_id":"before-you-install"},{"id":"create-the-rdi-database-ex0","language":"bash","code":"curl -v -k -d '{\"eviction_policy\": \"noeviction\"}' \\\n    -u '<USERNAME>:<PASSWORD>' \\\n    -H \"Content-Type: application/json\" \\\n    -X PUT https://<CLUSTER_FQDN>:9443/v1/bdbs/<BDB_UID>","section_id":"create-the-rdi-database"},{"id":"create-the-rdi-database-ex1","language":"bash","code":"curl -v -k -d '{\"data_persistence\":\"aof\"}' \\\n    -u '<USERNAME>:<PASSWORD>' \\\n    -H \"Content-Type: application/json\" \n    -X PUT https://<CLUSTER_FQDN>:9443/v1/bdbs/<BDB_UID>\n  curl -v -k -d '{\"aof_policy\":\"appendfsync-every-sec\"}' \\\n    -u '<USERNAME>:<PASSWORD>' \\\n    -H \"Content-Type: application/json\" \\\n    -X PUT https://<CLUSTER_FQDN>:9443/v1/bdbs/<BDB_UID>","section_id":"create-the-rdi-database"},{"id":"using-a-private-image-registry-ex0","language":"yaml","code":"global:\n  # Global image settings.\n  # If using a private image registry, update the default values accordingly.\n  image:\n    registry: your-registry\n    repository: your-repository # If different from \"redis\"\n  \n  # Image pull secrets to be used when using a private image registry.\n  imagePullSecrets:\n    - name: your-secret-name\n\n# ...\n\n# Configuration of the reloader.\nreloader:\n  reloader:\n    # ...\n    deployment:\n      image:\n        name: my-registry.com/my-repo/reloader\n        #...","section_id":"using-a-private-image-registry"},{"id":"download-the-rdi-cli-ex0","language":"bash","code":"export RDI_VERSION=\nwget https://redis-enterprise-software-downloads.s3.amazonaws.com/redis-di/cli/$RDI_VERSION/bin/linux-amd64/redis-di\nchmod +x redis-di\nsudo mv redis-di /usr/local/bin/","section_id":"download-the-rdi-cli"},{"id":"install-the-rdi-helm-chart-ex0","language":"bash","code":"helm show values rdi-<tag>.tgz > rdi-values.yaml","section_id":"install-the-rdi-helm-chart"},{"id":"install-the-rdi-helm-chart-ex1","language":"bash","code":"helm upgrade --install rdi rdi-<tag>.tgz -f rdi-values.yaml -n rdi --create-namespace","section_id":"install-the-rdi-helm-chart"},{"id":"the-valuesyaml-file-ex0","language":"bash","code":"head -c 32 /dev/urandom | base64","section_id":"the-valuesyaml-file"},{"id":"the-valuesyaml-file-ex1","language":"yaml","code":"connection:\n      ssl:\n        enabled: true\n        cacert: |\n          -----BEGIN CERTIFICATE-----\n          ...\n          -----END CERTIFICATE-----\n        cert: |\n          -----BEGIN CERTIFICATE-----\n          ...\n          -----END CERTIFICATE-----\n        key: |\n          -----BEGIN PRIVATE KEY-----\n          ...\n          -----END PRIVATE KEY-----","section_id":"the-valuesyaml-file"},{"id":"the-valuesyaml-file-ex2","language":"bash","code":"helm upgrade --install rdi rdi-<tag>.tar.gz -f rdi-values.yaml -n rdi --create-namespace \\\n      --set connection.ssl.enabled=true \\\n      --set-file connection.ssl.cacert=<path-to-CA-certificate> \\\n      --set-file connection.ssl.cert=<path-to-client-certificate> \\\n      --set-file connection.ssl.key=<path-to-client-key>","section_id":"the-valuesyaml-file"},{"id":"the-valuesyaml-file-ex3","language":"yaml","code":"global:\n  # Indicates whether the deployment is intended for an OpenShift environment.\n  openShift: true","section_id":"the-valuesyaml-file"},{"id":"the-valuesyaml-file-ex4","language":"yaml","code":"global:\n  # Container default security context.\n  # ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container\n  securityContext:\n    runAsNonRoot: true\n    # On OpenShift, user and group 1000 are usually not allowed.\n    # If using OpenShift, set runAsUser and runAsGroup to values in your project's user and group ranges.\n    # You can examine the latter via `oc get projects <rid-project-name> -o yaml | grep \"openshift.io/sa.scc\"`\n    runAsUser: 1000701234\n    runAsGroup: 1000701234\n    allowPrivilegeEscalation: false","section_id":"the-valuesyaml-file"},{"id":"the-valuesyaml-file-ex5","language":"bash","code":"> oc get projects <rid-project-name> -o yaml | grep \"openshift.io/sa.scc\"\n>","section_id":"the-valuesyaml-file"},{"id":"configure-the-flink-processor-ex0","language":"yaml","code":"operator:\n  dataPlane:\n    flinkProcessor:\n      jobManager:\n        # JobManager pod resources.\n        cpu: 0.1\n        memory: 1024\n      taskManager:\n        # TaskManager pod resources.\n        cpu: 1\n        memory: 2048\n        # Number of parallel task slots per TaskManager pod.\n        # Total parallelism is `replicas * numberOfTaskSlots`.\n        numberOfTaskSlots: 1","section_id":"configure-the-flink-processor"},{"id":"check-the-installation-ex0","language":"bash","code":"helm list -n rdi","section_id":"check-the-installation"},{"id":"check-the-installation-ex1","language":"plaintext","code":"NAME   \tNAMESPACE\tREVISION\tUPDATED         STATUS  \tCHART         \tAPP VERSION\ndefault\trdi      \t1       \t2025-05-08 ... \tdeployed\tpipeline-0.1.0\t<tag>\nrdi    \trdi      \t3       \t2025-05-08 ...\tdeployed\trdi-1.0.0","section_id":"check-the-installation"},{"id":"check-the-installation-ex2","language":"bash","code":"kubectl get pod -n rdi\n\nNAME                      READY  STATUS  \tRESTARTS  AGE\ncollector-api-<id>        1/1    Running  0         29m\nrdi-api-<id>              1/1 \t Running \t0      \t  29m\nrdi-operator-<id>         1/1 \t Running \t0      \t  29m\nrdi-reloader-<id>         1/1 \t Running \t0      \t  29m","section_id":"check-the-installation"},{"id":"uninstall-rdi-ex0","language":"bash","code":"kubectl delete pipeline default -n rdi\nhelm uninstall rdi -n rdi\nkubectl delete namespace rdi","section_id":"uninstall-rdi"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/installation/install-vm","title":"Install on VMs","url":"https://redis.io/docs/latest/integrate/redis-data-integration/installation/install-vm/","summary":"Learn how to install RDI on one or more VMs","tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-18T16:39:21+01:00","page_type":"content","content_hash":"4f25438f4cb3758a5a2f2f4fec991c693d78fbaa6321932ef5a797f4444979ce","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This guide explains how to install Redis Data Integration (RDI) on one or more VMs and integrate it with\nyour source databases. You can also\n[Install RDI on Kubernetes](https://redis.io/docs/latest/integrate/redis-data-integration/installation/install-k8s).\n\n> [!NOTE]\n> We recommend you always use the latest version, which is RDI v."},{"id":"create-the-rdi-database","title":"Create the RDI database","role":"content","text":"RDI uses a database on your Redis Enterprise cluster to store its state\ninformation. Use the Redis Enterprise Cluster Manager UI to create the RDI database with the following\nrequirements:\n\n* Redis Enterprise v6.4 or greater for the cluster.\n* For production, 250MB RAM with one primary and one replica is recommended, but for the\n  quickstart or for development, 125MB and a single shard is sufficient.\n* If you are deploying RDI for a production environment then secure this database with a password\n  and TLS.\n* Set the database's\n  [eviction policy](https://redis.io/docs/latest/operate/rs/databases/memory-performance/eviction-policy) to `noeviction`. Note that you can't set this using\n  [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin),\n  so you must either do it using the admin UI or with the following\n  [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api)\n  command:\n\n  [code example]\n* Set the database's\n  [data persistence](https://redis.io/docs/latest/operate/rs/databases/configure/database-persistence)\n  to AOF - fsync every 1 sec. Note that you can't set this using\n  [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin),\n  so you must either do it using the admin UI or with the following\n  [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api)\n  commands:\n\n  [code example]\n If you don't have permissions to use AOF persistence, please check the [Using RDI without persistence](https://redis.io/docs/latest/integrate/redis-data-integration/faq#can-i-use-rdi-without-persistence-enabled) section in the FAQ.\n\n* **Ensure that the RDI database is not clustered.** RDI will not work correctly if the\n  RDI database is clustered (but note that the target database *can* be clustered without\n  any problems).\n\n  When you create the RDI database, expand the **Clustering** section and make sure the\n  **Sharding** option is *unchecked* (as shown below).\n\n  ![The Sharding option is unchecked in the Clustering section of the create database form.](https://redis.io/docs/latest/images/rdi/ingest/RDIClusterSetting.webp)\n\n  You can check if your RDI database is clustered from its **Configuration** tab in the\n  Cluster Manager UI. In the **Clustering** section, **Sharding** should be set to **Disabled**,\n  as shown in the following screenshot:\n\n  ![The Clustering section of the database Configuration tab shows Sharding: Disabled.](https://redis.io/docs/latest/images/rdi/ingest/RDICheckUnclustered.webp)\n\n  If you find the database has been clustered by mistake, you must create a new database with\n  sharding disabled before continuing with the RDI installation."},{"id":"hardware-sizing","title":"Hardware sizing","role":"content","text":"RDI is mainly CPU and network bound. \nEach of the RDI VMs should have at least:\n\n* **CPU**: A minimum of 4 CPU cores. You should consider adding\n  2-6 extra cores on top of this if your dataset is big and you want to ingest the\n  baseline snapshot as fast as possible.\n* **RAM**: 8GB \n* **Disk**: On top of the OS footprint,\n  RDI requires 20GB in the `/var` folder and 1GB in the `/opt` folder (to\n  store the log files). This allows space for upgrades.\n* **Network interface**: 10GB or more."},{"id":"vm-installation-requirements","title":"VM Installation Requirements","role":"content","text":"You would normally install RDI on two VMs for High Availability (HA) but you can also install\njust one VM if you don't need this. For example, you might not need HA during\ndevelopment and testing.\n\n> [!NOTE]\n> You can't install RDI on a host where a Redis Enterprise cluster\n> is also installed, due to incompatible network rules. If you want to install RDI on a\n> host that you have previously used for Redis Enterprise then you must\n> use [`iptables`](https://www.netfilter.org/projects/iptables/index.html) to\n> \"clean\" the host before installation with the following command line:\n>\n> [code example]\n>\n> You may encounter problems if you use `iptables` v1.6.1 and earlier in\n> `nftables` mode. Use `iptables` versions later than v1.6.1 or enable the `iptables`\n> legacy mode with the following commands:\n>\n> [code example]\n>\n> Also, `iptables` versions 1.8.0-1.8.4 have known issues that can prevent RDI\n> from working, especially on RHEL 8. Ideally, use `iptables` v1.8.8, which is\n> known to work correctly with RDI.\n\nThe supported OS versions for RDI are:\n\n* RHEL 8 or 9\n* Ubuntu 20.04, 22.04, or 24.04\n\nYou must run the RDI installer as a privileged user because it installs\n[containerd](https://containerd.io/) and registers services. However, you don't\nneed any special privileges to run RDI processes for normal operation.\n\nRDI has a few\nrequirements for cloud VMs that you must implement before running the\nRDI installer, or else installation will fail. The following sections\ngive full pre-installation instructions for [RHEL](#firewall-rhel) and\n[Ubuntu](#firewall-ubuntu)."},{"id":"firewall-rhel","title":"RHEL","role":"content","text":"We recommend you turn off\n[`firewalld`](https://firewalld.org/documentation/)\nbefore installation using the command:\n\n[code example]\n\nHowever, if you do need to use `firewalld`, you must add the following rules:\n\n[code example]\n\nIf you have `nm-cloud-setup.service` enabled, you must disable it and reboot the\nnode with the following commands:\n\n[code example]"},{"id":"firewall-ubuntu","title":"Ubuntu","role":"content","text":"We recommend you turn off\n[Uncomplicated Firewall](https://wiki.ubuntu.com/UncomplicatedFirewall) (`ufw`)\nbefore installation with the command:\n\n[code example]\n\nHowever, if you do need to use `ufw`, you must add the following rules:\n\n[code example]"},{"id":"installation-steps","title":"Installation steps","role":"setup","text":"Follow the steps below for each of your VMs.\n\n> [!NOTE]\n> RDI installs executables by default in the `/var` partition, so you must\n> ensure it is mounted without the `noexec` option. Use the following command to\n> find any partitions mounted with the `noexec` option:\n>\n> [code example]\n>\n> If your `/var` partition is listed in the output from this command, you must remount\n> it without the `noexec` option. See\n> [Using the mount command](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/5/html/deployment_guide/chap-using_the_mount_command) in the Red Hat documentation to learn how to remount a partition.\n\n1.  Download the RDI installer from the\n    [Redis download center](https://redis-enterprise-software-downloads.s3.amazonaws.com/redis-di/rdi-installation-.tar.gz)\n    (from the *Modules, Tools & Integration* category) and extract it to your preferred installation\n    folder.\n\n    [code example]\n\n1.  Go to the installation folder:\n\n    [code example]\n\n1.  Run the `install.sh` script as a privileged user:\n\n    [code example]\n\n    > [!NOTE]\n    > RDI uses [K3s](https://k3s.io/) as part of its implementation.\n    > By default, the installer installs K3s in the `/var/lib` directory,\n    > but this might be a problem if you have limited space in `/var`\n    > or your company policy forbids you to install there. You can\n    > select a different directory for the K3s installation using the\n    > `--installation-dir` option with `install.sh`:\n    >\n    > [code example]\n\n    **Advanced**: You can also pass custom K3s parameters to the installer using the\n    `INSTALL_K3S_EXEC` environment variable. For example, to set the kubeconfig file \n    permissions to be readable by all users:\n\n    [code example]\n\n    You can combine multiple K3s options in the `INSTALL_K3S_EXEC` variable. See the\n    [K3s documentation](https://docs.k3s.io/installation/configuration) for a full list of \n    available options.\n\n    > [!WARNING]\n    > Only modify K3s parameters if you understand exactly what you are changing \n    > and why. Incorrect K3s configuration can cause RDI installation to fail or result in an \n    > unstable deployment. \n    \n\nThe RDI installer collects all necessary configuration details and alerts you to potential issues, \noffering options to abort, apply fixes, or provide additional information. \nOnce complete, it guides you through creating secrets and setting up your pipeline.\n\n> [!NOTE]\n> It is strongly recommended to specify a hostname rather than an IP address for\n> connecting to your RDI database, for the following reasons:\n>\n> -   Any DNS resolution issues will be detected during the installation rather than\n>     later during pipeline deployment.\n> -   If you use TLS, your RDI database CA certificate must contain the hostname you specified\n>     either as a common name (CN) or as a subject alternative name (SAN). CA certificates\n>     usually don't contain IP addresses.\n\n> [!NOTE]\n> If you specify `localhost` as the address of the RDI database server during\n> installation then the connection will fail if the actual IP address changes for the local\n> VM. For this reason, we recommend that you don't use `localhost` for the address. However,\n> if you do encounter this problem, you can fix it using the following commands on the VM\n> that is running RDI itself:\n>\n> [code example]\n\nAfter the installation is finished, RDI is ready for use."},{"id":"supply-cloud-dns-information","title":"Supply cloud DNS information","role":"content","text":"> [!NOTE]\n> This section is only relevant if you are installing RDI\n> on VMs in a cloud environment.\n\nIf you are using [Amazon Route 53](https://aws.amazon.com/route53/),\n[Google Cloud DNS](https://cloud.google.com/dns?hl=en), or\n[Azure DNS](https://azure.microsoft.com/en-gb/products/dns)\nthen you must supply the installer with the nameserver IP address\nduring installation. The table below\nshows the appropriate IP address for each cloud provider:\n\n| Platform | Nameserver IP |\n| :-- | :-- |\n| [Amazon Route 53](https://aws.amazon.com/route53/) | 169.254.169.253 |\n| [Google Cloud DNS](https://cloud.google.com/dns?hl=en) | 169.254.169.254 |\n| [Azure DNS](https://azure.microsoft.com/en-gb/products/dns) | 168.63.129.16 |\n\nIf you are using Route 53, you should first check that your VPC\nis configured to allow it. See\n[DNS attributes in your VPC](https://docs.aws.amazon.com/vpc/latest/userguide/AmazonDNS-concepts.html#vpc-dns-support)\nin the Amazon docs for more information."},{"id":"installing-with-high-availability","title":"Installing with High Availability","role":"setup","text":"To install RDI with High Availability (HA), perform the [Installation steps](#installation-steps)\non two different VMs. The first VM will automatically become the active (primary) instance, \nwhile the second VM will become the passive (secondary) one. \nWhen starting the RDI installation on the second VM, the installer will detect that the RDI\ndatabase is already in use and ask you to confirm that you intend to install RDI with HA.\n\nAfter the installation is complete, you must set the source and target database secrets\non both VMs as described in [Deploy a pipeline](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/deploy). If you use `redis-di` to deploy your configuration, you only need to do this on one of the VMs, not both.\n\nIn a High Availability setup, the RDI pipeline is only active on the primary instance (VM).\nThe two RDI instances will use the RDI database for leader election. If the primary instance fails \nto renew the lease in the RDI database, it will lose the leadership and a failover to the secondary instance\nwill take place. After the failover, the secondary instance will become the primary one, \nand the RDI pipeline will be active on that VM.\n\nThe secondary instance keeps the pipeline and its configuration, and reports it with the\n`standby` status, but runs none of its collectors or its stream processor, and it does not\ntouch the pipeline's keys in the RDI database. A standby pipeline is read-only, so\nrunning [`redis-di describe`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-describe)\nagainst the secondary instance works and shows `Status: standby`. Attempting to change a standby pipeline\nfails with `423 Locked` and a message saying that the pipeline is standing by because this instance is\ncurrently a leader election follower. Operations such as deploy, start, stop, reset, and delete\ncan only be performed against the primary instance.\n\nYou can still set pipeline secrets for an instance when it is on standby. Secrets are maintained per\ninstance and are not replicated between the two, because they can legitimately differ in some cases.\n\nYou may find it useful to trigger a failover deliberately to check that RDI is correctly configured to handle it. See [Test HA failover](https://redis.io/docs/latest/integrate/redis-data-integration/installation/ha-test) to learn how to do this."},{"id":"prepare-your-source-databases","title":"Prepare your source databases","role":"content","text":"Before deploying a pipeline, you must configure each source database to enable CDC. See the\n[Prepare source databases](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs)\nsection to learn how to do this. A pipeline can capture from more than one source database,\nand you must prepare each one separately. See\n[Multiple sources in one pipeline](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/multiple-sources) for more information."},{"id":"deploy-a-pipeline","title":"Deploy a pipeline","role":"content","text":"When the installation is complete, and you have prepared your source databases for CDC,\nyou are ready to start using RDI. See the guides on how to\n[configure](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines) and\n[deploy](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/deploy)\nRDI pipelines for more information. You can also configure and deploy a pipeline\nusing [Redis Insight](https://redis.io/docs/latest/develop/tools/insight). See\n[RDI in Redis Insight](https://redis.io/docs/latest/develop/tools/insight/rdi-connector)\nfor full details on how to connect to RDI and deploy pipelines.\n\n> [!NOTE]\n> The [`redis-di` CLI](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli)\n> is bundled with the VM installer, so it is already available on the VM where RDI is installed. If you\n> prefer to run it from your own laptop or desktop instead, you can\n> [download it separately](https://redis.io/docs/latest/integrate/redis-data-integration/installation/install-k8s#download-the-rdi-cli)\n> for your platform."},{"id":"configure-the-flink-processor","title":"Configure the Flink processor","role":"configuration","text":"RDI ships with two stream processor implementations: the *classic* processor and the\ndefault [Apache Flink](https://flink.apache.org/)-based *Flink* processor.\nSee\n[Stream processor implementations](https://redis.io/docs/latest/integrate/redis-data-integration/architecture#stream-processor-implementations)\nfor an overview of the differences and\n[Differences between the classic and Flink processors](https://redis.io/docs/latest/integrate/redis-data-integration/architecture/classic-vs-flink)\nfor a side-by-side comparison.\n\nA pipeline runs on the Flink processor unless its `config.yaml` sets\n[`processors.type`](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config#processors)\nto `classic`. Fine-tune the Flink runtime\nthrough the `processors.advanced` section of `config.yaml` (see the\n[configuration reference](https://redis.io/docs/latest/integrate/redis-data-integration/reference/config-yaml-reference#processors)).\n\nFor migrating existing pipelines to the Flink processor, see\n[Migrate from the classic processor to the Flink processor](https://redis.io/docs/latest/integrate/redis-data-integration/installation/migration-classic-to-flink)."},{"id":"uninstall-rdi","title":"Uninstall RDI","role":"content","text":"If you want to remove your RDI installation, go to the installation folder and run\nthe uninstall script as a privileged user:\n\n[code example]\n\nThe script will ask if you are sure before proceeding:\n\n[code example]\n\nIf you type anything other than \"y\" here, the script will abort without making any changes\nto RDI or your source databases."}],"examples":[{"id":"create-the-rdi-database-ex0","language":"bash","code":"curl -v -k -d '{\"eviction_policy\": \"noeviction\"}' \\\n    -u '<USERNAME>:<PASSWORD>' \\\n    -H \"Content-Type: application/json\" \\\n    -X PUT https://<CLUSTER_FQDN>:9443/v1/bdbs/<BDB_UID>","section_id":"create-the-rdi-database"},{"id":"create-the-rdi-database-ex1","language":"bash","code":"curl -v -k -d '{\"data_persistence\":\"aof\"}' \\\n    -u '<USERNAME>:<PASSWORD>' \\\n    -H \"Content-Type: application/json\" \n    -X PUT https://<CLUSTER_FQDN>:9443/v1/bdbs/<BDB_UID>\n  curl -v -k -d '{\"aof_policy\":\"appendfsync-every-sec\"}' \\\n    -u '<USERNAME>:<PASSWORD>' \\\n    -H \"Content-Type: application/json\" \\\n    -X PUT https://<CLUSTER_FQDN>:9443/v1/bdbs/<BDB_UID>","section_id":"create-the-rdi-database"},{"id":"vm-installation-requirements-ex0","language":"bash","code":">  sudo iptables-save | awk '/^[*]/ { print $1 } \n>                      /^:[A-Z]+ [^-]/ { print $1 \" ACCEPT\" ; }\n>                      /COMMIT/ { print $0; }' | sudo iptables-restore\n>","section_id":"vm-installation-requirements"},{"id":"vm-installation-requirements-ex1","language":"bash","code":"> sudo update-alternatives --set iptables /usr/sbin/iptables-legacy\n> sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy\n>","section_id":"vm-installation-requirements"},{"id":"firewall-rhel-ex0","language":"bash","code":"sudo systemctl disable firewalld --now","section_id":"firewall-rhel"},{"id":"firewall-rhel-ex1","language":"bash","code":"sudo firewall-cmd --permanent --add-port=443/tcp # RDI API\nsudo firewall-cmd --permanent --add-port=6443/tcp # kube-apiserver\nsudo firewall-cmd --permanent --zone=trusted --add-source=10.42.0.0/16 # Kubernetes pods\nsudo firewall-cmd --permanent --zone=trusted --add-source=10.43.0.0/16 # Kubernetes services\nsudo firewall-cmd --reload","section_id":"firewall-rhel"},{"id":"firewall-rhel-ex2","language":"bash","code":"sudo systemctl disable nm-cloud-setup.service nm-cloud-setup.timer\nsudo reboot","section_id":"firewall-rhel"},{"id":"firewall-ubuntu-ex0","language":"bash","code":"sudo ufw disable","section_id":"firewall-ubuntu"},{"id":"firewall-ubuntu-ex1","language":"bash","code":"sudo ufw allow 443/tcp # RDI API\nsudo ufw allow 6443/tcp # kube-apiserver\nsudo ufw allow from 10.42.0.0/16 to any # Kubernetes pods\nsudo ufw allow from 10.43.0.0/16 to any # Kubernetes services\nsudo ufw reload","section_id":"firewall-ubuntu"},{"id":"installation-steps-ex0","language":"bash","code":"> mount | grep noexec\n>","section_id":"installation-steps"},{"id":"installation-steps-ex1","language":"bash","code":"export RDI_VERSION=\n    wget https://redis-enterprise-software-downloads.s3.amazonaws.com/redis-di/rdi-installation-$RDI_VERSION.tar.gz\n    tar -xvf rdi-installation-$RDI_VERSION.tar.gz","section_id":"installation-steps"},{"id":"installation-steps-ex2","language":"bash","code":"cd rdi_install/$RDI_VERSION","section_id":"installation-steps"},{"id":"installation-steps-ex3","language":"bash","code":"sudo ./install.sh","section_id":"installation-steps"},{"id":"installation-steps-ex4","language":"bash","code":"> sudo ./install.sh --installation-dir <custom-installation-directory>\n    >","section_id":"installation-steps"},{"id":"installation-steps-ex5","language":"bash","code":"sudo INSTALL_K3S_EXEC='--write-kubeconfig-mode=644' ./install.sh","section_id":"installation-steps"},{"id":"installation-steps-ex6","language":"bash","code":"> sudo k3s kubectl delete nodes --all\n> sudo service k3s restart\n>","section_id":"installation-steps"},{"id":"uninstall-rdi-ex0","language":"bash","code":"sudo ./uninstall.sh","section_id":"uninstall-rdi"},{"id":"uninstall-rdi-ex1","language":"plaintext","code":"This will uninstall RDI and its dependencies, are you sure? [y, N]","section_id":"uninstall-rdi"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/installation/migration-classic-to-flink","title":"Migrate from the classic processor to the Flink processor","url":"https://redis.io/docs/latest/integrate/redis-data-integration/installation/migration-classic-to-flink/","summary":"Learn how to migrate an existing RDI pipeline from the classic processor to the Apache Flink-based processor.","tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-18T16:39:21+01:00","page_type":"content","content_hash":"bbd4eb2fbe4152f9cab7f276ab02b1a77e95c9334d46343509f6d7f60adc3c5c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"RDI ships with two stream processor implementations. The *classic*\nprocessor is implemented in Python. The default *Flink* processor is built on top of\n[Apache Flink](https://flink.apache.org/). Both run on VM and Kubernetes\ninstallations. The Flink processor can achieve much higher throughput\nduring snapshots, scales horizontally by changing the number of TaskManager replicas,\nand uses Flink checkpointing for fault tolerance. See [Stream processor implementations](https://redis.io/docs/latest/integrate/redis-data-integration/architecture#stream-processor-implementations)\nfor an overview.\n\nThe classic processor is the default in RDI 1.19.0. The Flink processor is the\ndefault starting with RDI 2.0.0.\n\nThis page describes how to migrate an existing pipeline from the classic\nprocessor to the Flink processor. The steps are the same on VMs and Kubernetes,\nexcept for the optional Helm-level tuning in [Step 1](#step-1-configure-the-flink-processor-at-the-helm-chart-level-kubernetes),\nwhich applies to Kubernetes only."},{"id":"before-you-migrate","title":"Before you migrate","role":"content","text":"This procedure migrates the pipeline processor on RDI 1.19.0. It does not\nupgrade RDI.\n\nBefore you start, save your existing configuration and jobs. Wait for the\ninitial snapshot to finish. Interrupting it causes the snapshot to restart\nfrom the beginning.\n\n> [!WARNING]\n> Switching processors with records still in the RDI input streams can leave\n> records unprocessed. Stop the collector and let the classic processor empty\n> the streams before switching.\n\nConfirm that your pipeline is compatible with the Flink processor:\n\n-   `JSON.MERGE` semantics differ from the classic processor's Lua-based merge\n    when null values are involved (see\n    [`use_native_json_merge`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/config-yaml-reference#processors-data-processing-configuration)).\n    The Flink processor always uses the native `JSON.MERGE` command when the\n    target database supports it.\n-   Ensure your Kubernetes cluster or VM has enough capacity for the Flink JobManager\n    and TaskManager pods (see\n    [Configure the Flink processor](https://redis.io/docs/latest/integrate/redis-data-integration/installation/install-k8s#configure-the-flink-processor)\n    for the default sizing)."},{"id":"step-1-configure-the-flink-processor-at-the-helm-chart-level-kubernetes","title":"Step 1: Configure the Flink processor at the Helm chart level (Kubernetes)","role":"content","text":"This step applies to **Kubernetes** installations only. On VM installations,\ncontinue with [Step 2](#step-2-disable-source-collection).\n\nThe Flink processor is always available — no opt-in is required at the Helm\nchart level. The defaults are sized for typical workloads, so you can skip\nthis step if you don't need to override them. To adjust the JobManager and\nTaskManager defaults, add an `operator.dataPlane.flinkProcessor` block to\nyour `rdi-values.yaml` file and run `helm upgrade` as described in\n[Configure the Flink processor](https://redis.io/docs/latest/integrate/redis-data-integration/installation/install-k8s#configure-the-flink-processor).\nExisting pipelines continue to run on the classic processor until you switch\nthem in [Step 4](#step-4-switch-processors-and-resume-collection).\n\nFor VM installations, skip this step. You can configure per-pipeline Flink\nresources in [Step 6](#step-6-tune-the-flink-processor-optional)."},{"id":"step-2-disable-source-collection","title":"Step 2: Disable source collection","role":"content","text":"In your existing `config.yaml`, add `active: false` under the source and set\n`processors.type` to `classic`. Use your existing source name and preserve\nall other source, target, processor, and job settings. This example shows\nonly the fields to change:\n\n[code example]\n\nDeploy the complete configuration directory, including the existing jobs:\n\n[code example]\n\nWait for the deployment to finish and the source collector to stop. Keep\nthe pipeline active so the classic processor can process the remaining input records.\n\nApplications can continue writing to the source database while collection\nis disabled. When the collector restarts, it resumes from the saved source\nposition and processes those changes."},{"id":"step-3-wait-for-the-input-streams-to-empty","title":"Step 3: Wait for the input streams to empty","role":"content","text":"After the collector has stopped, wait for every input stream to have a length\nof `0` in three complete checks, five seconds apart. An error, missing\nstatistics, or an unexpectedly empty stream list does not count as `0`.\nDo not include DLQ streams. Use any of the following methods."},{"id":"check-with-redis-di","title":"Check with `redis-di`","role":"content","text":"Run:\n\n[code example]\n\nIn the **Statistics** table, the **Pending** value for each classic processor\nstream is its current length. Confirm that every input stream is listed."},{"id":"check-with-redis-commands","title":"Check with Redis commands","role":"content","text":"Connect an authenticated Redis client to the **RDI database** that stores\nthe pipeline's input streams, not the target database. Find the input stream\nkeys with\n[`SCAN`](https://redis.io/docs/latest/commands/scan):\n\n[code example]\n\nIf the returned cursor is not `0`, pass it to the next command:\n\n[code example]\n\nRepeat with each new cursor until the returned cursor is `0`, even if an\nintermediate result contains no keys.\n\nFor every input stream returned, run\n[`XLEN`](https://redis.io/docs/latest/commands/xlen):\n\n[code example]\n\nRun a complete `SCAN` and all `XLEN` commands in each of the three checks."},{"id":"step-4-switch-processors-and-resume-collection","title":"Step 4: Switch processors and resume collection","role":"content","text":"After the drain check passes, remove the source's `active: false` setting\nfrom the existing `config.yaml` and set\n[`processors.type`](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config#processors)\nto `flink` (or remove this property, since `flink` is the default):\n\n[code example]\n\nRDI 1.19.x requires this setting because its default processor is `classic`.\nOn RDI 2.0.0 the default processor is `flink`, so this setting is no longer\nrequired.\n\nKeep the remaining configuration and jobs, then redeploy the complete\nconfiguration directory:\n\n[code example]\n\nWait for the classic processor to terminate and the Flink JobManager and\nTaskManager workloads to become healthy. Confirm that collection resumes\nfrom the saved source position and changes committed during the pause reach\nthe target. Verify new inserts, updates, and deletes. The processor migration\nis complete after these checks pass."},{"id":"step-5-adapt-deprecated-and-classic-only-properties","title":"Step 5: Adapt deprecated and classic-only properties","role":"content","text":"Some `processors` properties are no-ops, classic-only, or have moved to\n`processors.advanced` for the Flink processor. The following table lists the\nproperties that need attention when migrating.\n\n| Property | Action when migrating to Flink |\n| :-- | :-- |\n| `on_failed_retry_interval` | No-op. Remove. |\n| `duration` | No-op. Use `read_batch_timeout_ms` instead. |\n| `dedup`, `dedup_max_size`, `dedup_strategy` | Classic-only. Remove. |\n| `enable_async_processing`, `batch_queue_size`, `ack_queue_size` | Classic-only. Remove. |\n| `initial_sync_processes` | Classic-only. Configure parallelism through `advanced.flink.taskmanager.numberOfTaskSlots` and `advanced.resources.taskManager.replicas` instead. |\n| `idle_streams_check_interval_ms`, `busy_streams_check_interval_ms` | Classic-only. Use `processors.advanced.source.discovery.interval.ms` for a single discovery interval. |\n| `idle_sleep_time_ms` | Classic-only. Remove. |\n| `use_native_json_merge` | Classic-only. The Flink processor always uses `JSON.MERGE` when the target supports it. |\n\nThe classic processor silently ignores `processors.advanced`,\nand the Flink processor silently ignores classic-only top-level properties, so keeping\nboth top-level properties and their `processors.advanced` equivalents lets\nyou switch back without further edits."},{"id":"step-6-tune-the-flink-processor-optional","title":"Step 6: Tune the Flink processor (optional)","role":"content","text":"Fine-tune the Flink processor through the `processors.advanced` section.\nFor example:\n\n[code example]\n\nSee the\n[`processors.advanced` reference](https://redis.io/docs/latest/integrate/redis-data-integration/reference/config-yaml-reference#processorsadvanced-advanced-configuration)\nfor the full set of available properties."},{"id":"step-7-update-observability","title":"Step 7: Update observability","role":"content","text":"The Flink processor exposes Prometheus metrics directly\nfrom the Flink JobManager and TaskManager pods.\nSee\n[Flink processor metrics](https://redis.io/docs/latest/integrate/redis-data-integration/observability#flink-processor-metrics)\nfor the `ServiceMonitor` configuration and the available metrics."},{"id":"rolling-back","title":"Rolling back","role":"content","text":"To revert a pipeline to the classic processor, set `processors.type` to\n`classic` and redeploy the pipeline. This setting is required on RDI 2.0.0,\nwhere the default is `flink`. The classic processor silently ignores\n`processors.advanced`, so you don't need to remove it before switching back."}],"examples":[{"id":"step-2-disable-source-collection-ex0","language":"yaml","code":"sources:\n  <existing-source-name>:\n    active: false\nprocessors:\n  type: classic","section_id":"step-2-disable-source-collection"},{"id":"step-2-disable-source-collection-ex1","language":"bash","code":"redis-di deploy --dir <pipeline-config-directory>","section_id":"step-2-disable-source-collection"},{"id":"check-with-redis-di-ex0","language":"bash","code":"redis-di describe","section_id":"check-with-redis-di"},{"id":"check-with-redis-commands-ex0","language":"text","code":"SCAN 0 MATCH data:{rdi}:* COUNT 1000 TYPE stream","section_id":"check-with-redis-commands"},{"id":"check-with-redis-commands-ex1","language":"text","code":"SCAN <returned-cursor> MATCH data:{rdi}:* COUNT 1000 TYPE stream","section_id":"check-with-redis-commands"},{"id":"check-with-redis-commands-ex2","language":"text","code":"XLEN <input-stream-key>","section_id":"check-with-redis-commands"},{"id":"step-4-switch-processors-and-resume-collection-ex0","language":"yaml","code":"processors:\n  type: flink","section_id":"step-4-switch-processors-and-resume-collection"},{"id":"step-4-switch-processors-and-resume-collection-ex1","language":"bash","code":"redis-di deploy --dir <pipeline-config-directory>","section_id":"step-4-switch-processors-and-resume-collection"},{"id":"step-6-tune-the-flink-processor-optional-ex0","language":"yaml","code":"processors:\n  type: flink\n  advanced:\n    source:\n      # Time between checks for new input streams.\n      discovery.interval.ms: 1000\n    flink:\n      # Number of parallel task slots per TaskManager pod.\n      taskmanager.numberOfTaskSlots: 2\n      # Total memory budget for each TaskManager JVM process.\n      taskmanager.memory.process.size: 4096m\n    resources:\n      taskManager:\n        # Number of TaskManager pods.\n        replicas: 2","section_id":"step-6-tune-the-flink-processor-optional"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/installation/reqsummary","title":"Requirements summary","url":"https://redis.io/docs/latest/integrate/redis-data-integration/installation/reqsummary/","summary":"Requirements and recommendations for RDI installations.","tags":["docs","integrate","rs","rdi"],"last_updated":"2025-05-01T16:36:01+01:00","page_type":"content","content_hash":"905db0b00db80abbaa13f06459b0d13c2d833a9524f38e7d0a6e687696bd66f8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The sections below summarize the software and hardware requirements for\nan RDI installation."},{"id":"hardware-requirements-for-vm-installation","title":"Hardware requirements for VM installation","role":"content","text":"* **CPU**: A minimum of 4 CPU cores. You should consider adding\n  2-6 extra cores on top of this if your dataset is big and you want to ingest the\n  baseline snapshot as fast as possible.\n* **RAM**: 8GB \n* **Disk**: On top of the OS footprint,\n  RDI requires 20GB in the `/var` folder and 1GB in the `/opt` folder (to\n  store the log files). This allows space for upgrades.\n* **Network interface**: 10GB or more."},{"id":"os-requirements-for-vm-installation","title":"OS requirements for VM installation","role":"content","text":"* RHEL 8 or 9\n* Ubuntu 20.04, 22.04, or 24.04"},{"id":"kubernetesopenshift-supported-versions","title":"Kubernetes/OpenShift supported versions","role":"content","text":"RDI only supports versions of Kubernetes and OpenShift that have\nnot yet reached end-of-life (EOL). See the\n[Kubernetes](https://kubernetes.io/releases/) and\n[OpenShift](https://access.redhat.com/support/policy/updates/openshift)\nlifecycle pages for the latest updates."},{"id":"rdi-database-requirements","title":"RDI database requirements","role":"content","text":"* Redis Enterprise v6.4 or greater for the cluster.\n* For production, 250MB RAM with one primary and one replica is recommended, but for the\n  quickstart or for development, 125MB and a single shard is sufficient.\n* If you are deploying RDI for a production environment then secure this database with a password\n  and TLS.\n* Set the database's\n  [eviction policy](https://redis.io/docs/latest/operate/rs/databases/memory-performance/eviction-policy) to `noeviction`. Note that you can't set this using\n  [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin),\n  so you must either do it using the admin UI or with the following\n  [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api)\n  command:\n\n  [code example]\n* Set the database's\n  [data persistence](https://redis.io/docs/latest/operate/rs/databases/configure/database-persistence)\n  to AOF - fsync every 1 sec. Note that you can't set this using\n  [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin),\n  so you must either do it using the admin UI or with the following\n  [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api)\n  commands:\n\n  [code example]\n If you don't have permissions to use AOF persistence, please check the [Using RDI without persistence](https://redis.io/docs/latest/integrate/redis-data-integration/faq#can-i-use-rdi-without-persistence-enabled) section in the FAQ.\n\n* **Ensure that the RDI database is not clustered.** RDI will not work correctly if the\n  RDI database is clustered (but note that the target database *can* be clustered without\n  any problems).\n\n  When you create the RDI database, expand the **Clustering** section and make sure the\n  **Sharding** option is *unchecked* (as shown below).\n\n  ![The Sharding option is unchecked in the Clustering section of the create database form.](https://redis.io/docs/latest/images/rdi/ingest/RDIClusterSetting.webp)\n\n  You can check if your RDI database is clustered from its **Configuration** tab in the\n  Cluster Manager UI. In the **Clustering** section, **Sharding** should be set to **Disabled**,\n  as shown in the following screenshot:\n\n  ![The Clustering section of the database Configuration tab shows Sharding: Disabled.](https://redis.io/docs/latest/images/rdi/ingest/RDICheckUnclustered.webp)\n\n  If you find the database has been clustered by mistake, you must create a new database with\n  sharding disabled before continuing with the RDI installation."},{"id":"supported-source-databases","title":"Supported source databases","role":"compatibility","text":"| Database | Versions | AWS RDS  Versions | GCP SQL Versions |\n| :-- | :-- | :-- | :-- |\n| Oracle | 19c, 21c, 23ai (LogMiner only) | 19c, 21c | - |\n| MariaDB | 10.5, 11.4.x, 11.7.x | 10.4 to 10.11 | - |\n| MongoDB | 6.0, 7.0, 8.0 | - | - |\n| MySQL | 5.7, 8.0.x, 8.4.x, 9.0, 9.1 | 8.0.x | 8.0 |\n| PostgreSQL | 10, 11, 12, 13, 14, 15, 16, 17, 18 | 11, 12, 13, 14, 15, 16, 17, 18 | 15 |\n| Supabase (uses PostgreSQL) | 10, 11, 12, 13, 14, 15, 16, 17  | - | - |\n| SQL Server | 2017, 2019, 2022 | 2016, 2017, 2019, 2022 | 2019 |\n| Spanner | - | - | All versions |\n| AlloyDB for PostgreSQL | 14.2, 15.7 | - | 14.2, 15.7 |\n| AWS Aurora/PostgreSQL | 15 | 15 | - |\n| Neon | 14, 15, 16, 17 | - | - |\n| Snowflake (preview) | - | - | - |"}],"examples":[{"id":"rdi-database-requirements-ex0","language":"bash","code":"curl -v -k -d '{\"eviction_policy\": \"noeviction\"}' \\\n    -u '<USERNAME>:<PASSWORD>' \\\n    -H \"Content-Type: application/json\" \\\n    -X PUT https://<CLUSTER_FQDN>:9443/v1/bdbs/<BDB_UID>","section_id":"rdi-database-requirements"},{"id":"rdi-database-requirements-ex1","language":"bash","code":"curl -v -k -d '{\"data_persistence\":\"aof\"}' \\\n    -u '<USERNAME>:<PASSWORD>' \\\n    -H \"Content-Type: application/json\" \n    -X PUT https://<CLUSTER_FQDN>:9443/v1/bdbs/<BDB_UID>\n  curl -v -k -d '{\"aof_policy\":\"appendfsync-every-sec\"}' \\\n    -u '<USERNAME>:<PASSWORD>' \\\n    -H \"Content-Type: application/json\" \\\n    -X PUT https://<CLUSTER_FQDN>:9443/v1/bdbs/<BDB_UID>","section_id":"rdi-database-requirements"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/installation/upgrade","title":"Upgrading RDI","url":"https://redis.io/docs/latest/integrate/redis-data-integration/installation/upgrade/","summary":"Learn how to upgrade an existing RDI installation","tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-18T16:39:21+01:00","page_type":"content","content_hash":"f6b9679b4c5a5cee5e47873735e09332be42a455eb45231ca2928c12fc99967c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> Before upgrading to RDI 2.0.0, review the\n> [processor default change](#upgrading-to-rdi-200)."},{"id":"upgrading-a-vm-installation","title":"Upgrading a VM installation","role":"content","text":"Follow the steps below to upgrade an existing\n[VM installation](https://redis.io/docs/latest/integrate/redis-data-integration/installation/install-vm)\nof RDI:\n\n1.  Download the RDI installer from the [Redis download center](https://redis-enterprise-software-downloads.s3.amazonaws.com/redis-di/rdi-installation-.tar.gz)\n    (in the *Modules, Tools & Integration* category) and extract it to your\n    preferred installation folder.\n\n    [code example]\n\n1.  Go to the installation folder:\n\n    [code example]\n\n1.  Run the `upgrade.sh` script as a privileged user. Note that you must pass\n    your RDI password to the script unless the password is empty.\n\n    [code example]"},{"id":"recovering-from-failure-during-a-vm-upgrade","title":"Recovering from failure during a VM upgrade","role":"content","text":"If the previous version is v1.4.4 or later, go to the `rdi_install/<PREVIOUS_VERSION>`\ndirectory and run `sudo ./upgrade.sh` to revert to that version, as described in the section\n[Upgrading a VM installation](#upgrading-a-vm-installation) above.\n\nIf the version you are replacing is earlier than v1.4.4, follow these steps. These steps restore and\nrun the CLI binary of the previous RDI version, which still provided the `redis-di upgrade` command.\n(On current versions, upgrades are performed with the `upgrade.sh` script as described above, and\n`redis-di upgrade` is no longer a CLI command.)\n\n1.  Run `redis-di --version` to check the current version.\n\n    If the version is the new one, copy the previous version\n    of the RDI CLI to `/usr/local/bin` with the following command:\n    \n    [code example]\n\n1.  Check that the CLI version is correct by running `redis-di --version`.\n\n    Then, go to the `rdi_install/<PREVIOUS_VERSION>` directory and run the\n    following command;\n\n    [code example]\n\n> [!NOTE]\n> If the `collector` or `processor` pods are not in the `Running` state after\n> the upgrade, you must run `redis-di deploy` and check again that they are both in the\n> `Running` state."},{"id":"upgrading-a-vm-installation-with-high-availability","title":"Upgrading a VM installation with High Availability","role":"content","text":"If there is an active pipeline, upgrade RDI on the active VM first. \nThis will cause a short pipeline downtime of up to two minutes. \nAfterwards, upgrade RDI on the passive VM. This will not cause any downtime.\n\n> [!WARNING]\n> When upgrading from RDI < 1.8.0 to RDI >= 1.8.0 in a VM HA setup, both RDI instances may incorrectly consider themselves active after the upgrade. This occurs because the upgrade process doesn't change the cluster id value from its default `cluster-1`, causing both clusters to assume they are the active cluster.\n>\n> **Symptoms:**\n>\n> - The upgraded passive node will start collector and processor components\n> - Collector may enter a crash loop as it fails to connect to the source\n> - Both clusters will restart in a loop\n>\n> **Workaround:**\n>\n> After upgrading, manually set a unique cluster ID for one of the installations (preferably on the passive instance):\n>\n> 1. Locate the RDI configuration file on the VM host. The file is typically located at `/etc/rdi/rdi-sys-config.yaml`.\n> 2. Open the configuration file in a text editor. For example:\n>\n>    [code example]"},{"id":"upgrading-a-kubernetes-installation","title":"Upgrading a Kubernetes installation","role":"content","text":"Follow the steps below to upgrade an existing\n[Kubernetes](https://redis.io/docs/latest/integrate/redis-data-integration/installation/install-k8s)\ninstallation of RDI:\n\n1.  If you are using a private registry, pull the new versions of all images listed in \n    [Using a private image registry](https://redis.io/docs/latest/integrate/redis-data-integration/installation/install-k8s#using-a-private-image-registry)\n    and add them to your local registry.\n\n1.  Download the RDI Helm chart tar file from the [Redis download center](https://redis-enterprise-software-downloads.s3.amazonaws.com/redis-di/rdi-.tgz)\n    (in the *Modules, Tools & Integration* category).\n\n    [code example]\n\n1.  Adapt your `rdi-values.yaml` file to any changes in the new RDI version if needed.\n    See also [Upgrading to RDI 1.8.0 or later from an earlier version](#upgrading-to-rdi-180-or-later-from-an-earlier-version). \n    Before making any changes, save your existing `rdi-values.yaml` if you need to revert \n    to the old RDI version for any reason.\n\n1.  Run the `helm upgrade` command:\n    \n    [code example]\n\nNote that you don't need to\n[deploy](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/deploy)\nthe RDI configuration again after this step."},{"id":"upgrading-to-rdi-180-or-later-from-an-earlier-version","title":"Upgrading to RDI 1.8.0 or later from an earlier version","role":"content","text":"When upgrading to RDI 1.8.0 or later from an earlier version\nyou must adapt your `rdi-values.yaml` file to the following changes:\n\n-   All collector and processor values that were previously under `collector`,\n    `collectorSourceMetricsExporter`, and `processor` have been moved to\n    `operator.dataPlane.collector` and `operator.dataPlane.processor`.\n-   `global.collectorApiEnabled` has been moved to `operator.dataPlane.collectorApi.enabled`,\n    and is now a boolean value, not `\"0\"` or `\"1\"`.\n-   `api.authEnabled` is also now a boolean value, not `\"0\"` or `\"1\"`.\n-   The following values have been deprecated: `rdiMetricsExporter.service.protocol`,\n    `rdiMetricsExporter.service.port`, `rdiMetricsExporter.serviceMonitor.path`,\n    `api.service.name`."},{"id":"verifying-the-upgrade","title":"Verifying the upgrade","role":"content","text":"Check that all pods have `Running` status:\n\n[code example]\n\nIf you find that the upgrade did not work as expected for any reason, \nthen run the `helm upgrade` command again (as described in the section\n[Upgrading a Kubernetes installation](#upgrading-a-kubernetes-installation) above),\nbut this time with the previous version you were upgrading from, and using\nyour saved `rdi-values.yaml` for that version. This will restore your previous working state.\n\n> [!NOTE]\n> Downgrading from RDI 1.8.0 or later to an earlier version using `helm upgrade`\n> will not work. If you need to perform such an upgrade, uninstall RDI completely first as\n> described in [Uninstall RDI](https://redis.io/docs/latest/integrate/redis-data-integration/installation/install-k8s#uninstall-rdi),\n> and then install the old version."},{"id":"upgrading-to-rdi-200","title":"Upgrading to RDI 2.0.0","role":"content","text":"RDI 2.0.0 changes several behaviors that affect a pipeline upgraded from RDI 1.19.x or earlier."},{"id":"the-flink-processor-becomes-the-default","title":"The Flink processor becomes the default","role":"content","text":"RDI 2.0.0 changes the default processor from `classic` to `flink`. This default\napplies when the pipeline's `config.yaml` omits `processors.type`.\n\nFor an existing pipeline that uses the classic processor, choose one of these options before upgrading:\n\n- To migrate to the Flink processor, first follow\n  [Migrate from the classic processor to the Flink processor](https://redis.io/docs/latest/integrate/redis-data-integration/installation/migration-classic-to-flink)\n  on RDI 1.19.0. This stops the collector and drains the input streams before\n  switching processors. Then upgrade RDI.\n- To keep the classic processor, set `processors.type: classic` in the\n  pipeline's `config.yaml` and deploy it before upgrading.\n\nIf your pipeline already uses `processors.type: flink`, no processor change\nis needed. Continue with the upgrade instructions for your installation."},{"id":"clearing-a-pipeline-replaces-deleting-it","title":"Clearing a pipeline replaces deleting it","role":"content","text":"`DELETE /api/v2/pipelines/{name}` no longer empties a pipeline, and `redis-di delete` no longer\nworks. Clear the configuration with `redis-di deploy --empty`, or with\n`PUT /api/v2/pipelines/{name}` and an empty configuration, instead. See\n[Clear a pipeline](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/deploy#clear-a-pipeline).\n\nClearing a pipeline is not the same as resetting it or flushing the target database. Clearing\nremoves the configuration and the pipeline's data from the RDI database, while resetting keeps the\nconfiguration and re-snapshots the sources; neither one deletes any records from the target\ndatabase."},{"id":"redeploying-a-configuration-after-clearing-a-pipeline","title":"Redeploying a configuration after clearing a pipeline","role":"content","text":"A configuration of an upgraded pipeline still references the names from before the upgrade, so\ndeploying it again after clearing the pipeline fails. See\n[Redeploying a configuration after clearing a pipeline](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/multiple-sources#redeploying-a-configuration-after-clearing-a-pipeline)\nfor what you have to change, with a before and after example."},{"id":"api-changes","title":"API changes","role":"content","text":"RDI 2.0.0 changes several API v2 query parameters and response fields. Most of them affect only\nan API client, and are listed in the\n[RDI 2.0.0 release notes](https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-2-0-0).\n\nThe one to check for is the metric collections endpoint, which now keys `data_streams.streams`\nby the source-qualified table name, such as `mysql.inventory.addresses`, instead of by the Redis\nstream name. Update any dashboard or script that reads the previous form. See\n[Observability](https://redis.io/docs/latest/integrate/redis-data-integration/observability) for the metrics RDI reports."},{"id":"enabling-the-flink-processor","title":"Enabling the Flink processor","role":"content","text":"The\n[Apache Flink](https://flink.apache.org/)-based stream processor is\nfully supported on both VM and Kubernetes installations after upgrading to\nRDI 1.19.0. Once the upgrade completes, it is always available —\nno opt-in is required, and the defaults are sized for typical workloads.\n\n> [!WARNING]\n> The Flink processor is the default as of RDI 2.0.0.\n> Upgrading to that release or later moves a pipeline whose `config.yaml` does not set\n> [`processors.type`](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config#processors)\n> onto the Flink processor when you next deploy it. To keep such a pipeline on the classic\n> processor, set `processors.type` to `classic` before you upgrade.\n\nOn Kubernetes, to override the Flink processor defaults, add an\n`operator.dataPlane.flinkProcessor` block to your `rdi-values.yaml` file as\ndescribed in\n[Configure the Flink processor](https://redis.io/docs/latest/integrate/redis-data-integration/installation/install-k8s#configure-the-flink-processor).\nOn VMs, see\n[Configure the Flink processor](https://redis.io/docs/latest/integrate/redis-data-integration/installation/install-vm#configure-the-flink-processor).\nFor the per-pipeline migration steps, see\n[Migrate from the classic processor to the Flink processor](https://redis.io/docs/latest/integrate/redis-data-integration/installation/migration-classic-to-flink)."},{"id":"what-happens-during-the-upgrade","title":"What happens during the upgrade?","role":"content","text":"The upgrade process replaces the current RDI components with their new versions:\n\n-   Firstly, the control plane components are replaced. At this point, the pipeline\n    is still active but monitoring will be disconnected.\n-   Secondly, the pipeline data plane components are replaced.\n    If a pipeline is active while upgrading, the `collector` and `processor`\n    pods will be restarted. The pipeline will pause for up to two minutes but it \n    will catch up very quickly after restarting. \n    The pipeline data and state are both stored in Redis, so data will not\n    be lost during the upgrade."}],"examples":[{"id":"upgrading-a-vm-installation-ex0","language":"bash","code":"export RDI_VERSION=\n    wget https://redis-enterprise-software-downloads.s3.amazonaws.com/redis-di/rdi-installation-$RDI_VERSION.tar.gz\n    tar -xvf rdi-installation-$RDI_VERSION.tar.gz","section_id":"upgrading-a-vm-installation"},{"id":"upgrading-a-vm-installation-ex1","language":"bash","code":"cd rdi_install/$RDI_VERSION","section_id":"upgrading-a-vm-installation"},{"id":"upgrading-a-vm-installation-ex2","language":"bash","code":"sudo ./upgrade.sh --rdi-password <redis-rdi-password>","section_id":"upgrading-a-vm-installation"},{"id":"recovering-from-failure-during-a-vm-upgrade-ex0","language":"bash","code":"sudo cp rdi_install/<PREVIOUS_VERSION>/deps/rdi-cli/<OS>/redis-di usr/local/bin","section_id":"recovering-from-failure-during-a-vm-upgrade"},{"id":"recovering-from-failure-during-a-vm-upgrade-ex1","language":"bash","code":"sudo redis-di upgrade --rdi-host <RDI_REDIS_HOST> --rdi-port <RDI_REDIS_PORT>","section_id":"recovering-from-failure-during-a-vm-upgrade"},{"id":"upgrading-a-vm-installation-with-high-availability-ex0","language":"bash","code":">    sudo nano /etc/rdi/rdi-sys-config.yaml\n>","section_id":"upgrading-a-vm-installation-with-high-availability"},{"id":"upgrading-a-kubernetes-installation-ex0","language":"bash","code":"export RDI_VERSION=\n    wget https://redis-enterprise-software-downloads.s3.amazonaws.com/redis-di/rdi-$RDI_VERSION.tgz","section_id":"upgrading-a-kubernetes-installation"},{"id":"upgrading-a-kubernetes-installation-ex1","language":"bash","code":"helm upgrade --install rdi rdi-<tag>.tar.gz -f rdi-values.yaml -n rdi","section_id":"upgrading-a-kubernetes-installation"},{"id":"verifying-the-upgrade-ex0","language":"bash","code":"kubectl get all -n rdi","section_id":"verifying-the-upgrade"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/observability","title":"Observability","url":"https://redis.io/docs/latest/integrate/redis-data-integration/observability/","summary":"Learn how to monitor RDI","aliases":["/integrate/redis-data-integration/ingest/observability/","/integrate/redis-data-integration/ingest/data-transformation/observability/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-18T16:39:42+01:00","page_type":"content","content_hash":"bd65012eb8b37d01e0bf0cfa338ae6f817a1337dde04e1be0b1759f90da750cd","sections":[{"id":"overview","title":"Overview","role":"overview","text":"RDI reports metrics about its operation using\n[Prometheus exporter endpoints](https://prometheus.io/docs/instrumenting/exporters/).\nYou can connect to the endpoints with\n[Prometheus](https://prometheus.io/docs/prometheus/latest/getting_started/)\nto query the metrics and plot simple graphs or with\n[Grafana](https://grafana.com/) to produce more complex visualizations and\ndashboards.\n\nRDI exposes the following endpoints:\n- **Collector metrics**: CDC collector performance and connectivity\n- **Stream processor metrics**: Data processing performance and throughput. The exposed metrics depend on the [stream processor implementation](https://redis.io/docs/latest/integrate/redis-data-integration/architecture#stream-processor-implementations) used by the pipeline:\n  - The classic processor exposes the metrics described in [Stream processor metrics](#stream-processor-metrics) through the `rdi-metrics-exporter` service.\n  - The Flink processor exposes the metrics described in [Flink processor metrics](#flink-processor-metrics) directly from its JobManager and TaskManager pods. The `rdi-metrics-exporter` service is not deployed for Flink-based pipelines.\n- **Operator metrics**: Kubernetes operator health and Pipeline resource states\n\nThe sections below explain these sets of metrics in more detail.\nSee the\n[architecture overview](https://redis.io/docs/latest/integrate/redis-data-integration/architecture#overview)\nfor an introduction to these concepts.\n\n> [!NOTE]\n> If you don't use Prometheus or Grafana, you can still see\n> RDI metrics with the RDI monitoring screen in Redis Insight or with the\n> [`redis-di describe`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-describe)\n> command from the CLI."},{"id":"accessing-the-metrics","title":"Accessing the metrics","role":"content","text":"The way you access the metrics endpoints depends on whether you are using a VM installation or a Helm installation for RDI. The sections below describe the correct approach for each installation type."},{"id":"vm-installation","title":"VM Installation","role":"content","text":"You can use the CLI to determine the metrics endpoints instead of assembling them yourself.\nThe `Components` section of\n[`redis-di describe`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-describe)\nhas a `Metrics Endpoints` column listing each component's externally-reachable metrics URLs,\nso a pipeline with several sources shows the URL of every collector.\n\nRDI discovers these endpoints from each component's `Ingress` resources, so a component\nwithout an ingress has none, and the list is empty until the ingress controller assigns an\naddress to the ingress.\n\nFor VM installations, the metrics are available by default on the following endpoints:\n- Collector metrics: `https://<RDI_HOST>:<RDI_HTTPS_PORT>/collector-<source>/metrics`, where `<source>` is\n  the source name from `config.yaml`. Each source has its own collector, so a pipeline with\n  several sources has one endpoint per source.\n- Stream processor metrics: `https://<RDI_HOST>:<RDI_HTTPS_PORT>/processor/metrics` (Flink processor)\n  or `https://<RDI_HOST>:<RDI_HTTPS_PORT>/metrics` (classic processor).\n- Operator metrics: `https://<RDI_HOST>:<RDI_HTTPS_PORT>/operator/metrics`\n\nAbove, `<RDI_HTTPS_PORT>` is the HTTPS port you specified when installing RDI, or 443 if you kept the default.\n\nPlease note that for RDI versions prior to 1.16.0 the collector metrics are not accessible."},{"id":"helm-installation","title":"Helm installation","role":"content","text":"For Helm installations, the metrics are available via autodiscovery in the K8s cluster. Follow the steps below to use them:\n1. Make sure you have the Prometheus Operator installed in your K8s cluster (see the\n   [Prometheus Operator installation guide](https://prometheus-operator.dev/docs/getting-started/installation/) for more information about this).\n\n2. Update your values.yaml file to enable metrics for the operator, collector and stream processor components.\n\n    - For the collector, update the `collector` section, under the `dataPlane` section:\n        [code example]\n\n    - For the stream processor, update the `rdiMetricsExporter` section:\n        [code example]\n\n    - For the operator, update the `operator` section:\n        [code example]\n\n    - For the Flink processor, enable the JobManager and TaskManager `ServiceMonitor` resources under `operator.dataPlane.flinkProcessor`:\n        [code example]\n\n> [!NOTE]\n> The Prometheus service discovery loop runs at regular intervals. This means that after deploying or updating RDI with the above configuration, it may take a few minutes for Prometheus to discover the new ServiceMonitors and start scraping metrics from the RDI components."},{"id":"collector-metrics","title":"Collector metrics","role":"content","text":"These metrics are divided into three groups:\n\n- **Pipeline state**: metrics about the pipeline mode and connectivity\n- **Data flow counters**: counters for data breakdown per source table\n- **Processing performance**: processing speed of RDI micro batches\n\nThe following table lists all collector metrics and their descriptions:\n\n| Metric | Type | Description | Alerting Recommendations |\n|:--|:--|:--|:--|\n| **Schema History Metrics** | | | |\n| `ChangesApplied` | Counter | Total number of schema changes applied during recovery and runtime | Informational - monitor for trends |\n| `ChangesRecovered` | Counter | Number of changes that were read during the recovery phase | Informational - monitor for trends |\n| `MilliSecondsSinceLastAppliedChange` | Gauge | Number of milliseconds since the last change was applied | Informational - monitor for trends |\n| `MilliSecondsSinceLastRecoveredChange` | Gauge | Number of milliseconds since the last change was recovered from the history store | Informational - monitor for trends |\n| `RecoveryStartTime` | Gauge | Time in epoch milliseconds when recovery started (-1 if not applicable) | Informational - monitor for trends |\n| **Connection and State Metrics** | | | |\n| `Connected` | Gauge | Whether the collector is currently connected to the database (1=connected, 0=disconnected) | **Critical Alert**: Alert if value = 0 (disconnected) |\n| **Queue Metrics** | | | |\n| `CurrentQueueSizeInBytes` | Gauge | Current size of the collector's internal queue in bytes | Informational - monitor for trends |\n| `MaxQueueSizeInBytes` | Gauge | Maximum configured size of the collector's internal queue in bytes | Informational - use for capacity planning |\n| `QueueRemainingCapacity` | Gauge | Remaining capacity of the collector's internal queue | Informational - monitor for trends |\n| `QueueTotalCapacity` | Gauge | Total capacity of the collector's internal queue | Informational - use for capacity planning |\n| **Streaming Performance Metrics** | | | |\n| `MilliSecondsBehindSource` | Gauge | Number of milliseconds the collector is behind the source database (-1 if not applicable) | Informational - monitor for trends and business SLA requirements |\n| `MilliSecondsSinceLastEvent` | Gauge | Number of milliseconds since the collector processed the most recent event (-1 if not applicable) | Informational - monitor for trends in active systems |\n| `NumberOfCommittedTransactions` | Counter | Number of committed transactions processed by the collector | Informational - monitor for trends |\n| `NumberOfEventsFiltered` | Counter | Number of events filtered by include/exclude list rules | Informational - monitor for trends |\n| **Event Counters** | | | |\n| `TotalNumberOfCreateEventsSeen` | Counter | Total number of CREATE (INSERT) events seen by the collector | Informational - monitor for trends |\n| `TotalNumberOfDeleteEventsSeen` | Counter | Total number of DELETE events seen by the collector | Informational - monitor for trends |\n| `TotalNumberOfEventsSeen` | Counter | Total number of events seen by the collector | Informational - monitor for trends |\n| `TotalNumberOfUpdateEventsSeen` | Counter | Total number of UPDATE events seen by the collector | Informational - monitor for trends |\n| `NumberOfErroneousEvents` | Counter | Number of events that caused errors during processing | **Critical Alert**: Alert if > 0 (indicates processing failures) |\n| **Snapshot Metrics** | | | |\n| `RemainingTableCount` | Gauge | Number of tables remaining to be processed during snapshot | Informational - monitor snapshot progress |\n| `RowsScanned` | Counter | Number of rows scanned per table during snapshot (reported per table) | Informational - monitor snapshot progress |\n| `SnapshotAborted` | Gauge | Whether the snapshot was aborted (1=aborted, 0=not aborted) | **Critical Alert**: Alert if value = 1 (snapshot failed) |\n| `SnapshotCompleted` | Gauge | Whether the snapshot completed successfully (1=completed, 0=not completed) | Informational - monitor snapshot completion |\n| `SnapshotDurationInSeconds` | Gauge | Total duration of the snapshot process in seconds | Informational - monitor for performance trends |\n| `SnapshotPaused` | Gauge | Whether the snapshot is currently paused (1=paused, 0=not paused) | Informational - monitor snapshot state |\n| `SnapshotPausedDurationInSeconds` | Gauge | Total time the snapshot was paused in seconds | Informational - monitor snapshot state |\n| `SnapshotRunning` | Gauge | Whether a snapshot is currently running (1=running, 0=not running) | Informational - monitor snapshot state |\n| `TotalTableCount` | Gauge | Total number of tables included in the snapshot | Informational - use for progress calculation |\n\n> [!NOTE]\n> Many metrics include context labels that specify the phase (`snapshot` or `streaming`), database name, and other contextual information. Metrics with a value of `-1` typically indicate that the measurement is not applicable in the current state."},{"id":"stream-processor-metrics","title":"Stream processor metrics","role":"content","text":"The metrics in this section are reported by the *classic* stream processor and\nexposed through the `rdi-metrics-exporter` service. For pipelines that use\nthe [Flink processor](https://redis.io/docs/latest/integrate/redis-data-integration/architecture#stream-processor-implementations),\nsee [Flink processor metrics](#flink-processor-metrics) instead.\n\nRDI reports metrics during the two main phases of the ingest pipeline, the *snapshot*\nphase and the *change data capture (CDC)* phase. (See the\n[pipeline lifecycle](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines)\ndocs for more information). The table below shows the full set of metrics that\nRDI reports with their descriptions.\n\n| Metric Name | Metric Type | Metric Description | Alerting Recommendations |\n|-------------|-------------|--------------------|-----------------------|\n| `incoming_records_total` | Counter | Total number of incoming records processed by the system | Informational - monitor for trends |\n| `incoming_records_created` | Gauge | Timestamp when the incoming records counter was created | Informational - no alerting needed |\n| `processed_records_total` | Counter | Total number of records that have been successfully processed | Informational - monitor for trends |\n| `rejected_records_total` | Counter | Total number of records that were rejected during processing | **Critical Alert**: Alert if > 0 (indicates processing failures) |\n| `filtered_records_total` | Counter | Total number of records that were filtered out during processing | Informational - monitor for trends |\n| `rdi_engine_state` | Gauge | Current state of the RDI engine with labels for `state` (e.g., STARTED, RUNNING) and `sync_mode` (e.g., SNAPSHOT, STREAMING) | **Critical Alert**: Alert if state indicates failure or error condition |\n| `rdi_version_info` | Gauge | Version information for RDI components with labels for `cli` and `engine` versions | Informational - use for version tracking |\n| `monitor_time_elapsed_total` | Counter | Total time elapsed (in seconds) since monitoring started | Informational - use for uptime tracking |\n| `monitor_time_elapsed_created` | Gauge | Timestamp when the monitor time elapsed counter was created | Informational - no alerting needed |\n| `rdi_incoming_entries` | Gauge | Count of incoming events by `data_source` and `operation` type (pending, inserted, updated, deleted, filtered, rejected) | Informational - monitor for trends, alert only on \"rejected\" > 0 |\n| `rdi_stream_event_latency_ms` | Gauge | Latency in milliseconds of the oldest event in each data stream, labeled by `data_source` | Informational - monitor based on business SLA requirements |\n| **Processor Performance Total Metrics** | | | |\n| `rdi_processed_batches_total` | Counter | Total number of processed batches | Informational - use for data ingestion and load tracking |\n| `rdi_processor_batch_size_total` | Counter | Total batch size across all processed batches | Informational - use for throughput analysis |\n| `rdi_processor_read_time_ms_total` | Counter | Total read time in milliseconds across all batches | Informational - use for performance analysis |\n| `rdi_processor_transform_time_ms_total` | Counter | Total transform time in milliseconds across all batches | Informational - use for performance analysis |\n| `rdi_processor_write_time_ms_total` | Counter | Total write time in milliseconds across all batches | Informational - use for performance analysis |\n| `rdi_processor_process_time_ms_total` | Counter | Total process time in milliseconds across all batches | Informational - use for performance analysis |\n| `rdi_processor_ack_time_ms_total` | Counter | Total acknowledgment time in milliseconds across all batches | Informational - use for performance analysis |\n| `rdi_processor_total_time_ms_total` | Counter | Sum of the total `read_time`, `process_time` and `ack_time` values in milliseconds across all batches | Informational - use for performance analysis |\n| `rdi_processor_rec_per_sec_total` | Gauge | Total records per second across all batches | Informational - use for throughput analysis |\n| **Processor Performance Last Batch Metrics** | | | |\n| `rdi_processor_batch_size_last` | Gauge | Last batch size processed | Informational - use for real-time monitoring |\n| `rdi_processor_read_time_ms_last` | Gauge | Last batch read time in milliseconds | Informational - use for real-time performance monitoring |\n| `rdi_processor_transform_time_ms_last` | Gauge | Last batch transform time in milliseconds | Informational - use for real-time performance monitoring |\n| `rdi_processor_write_time_ms_last` | Gauge | Last batch write time in milliseconds | Informational - use for real-time performance monitoring |\n| `rdi_processor_process_time_ms_last` | Gauge | Last batch process time in milliseconds | Informational - use for real-time performance monitoring |\n| `rdi_processor_ack_time_ms_last` | Gauge | Last batch acknowledgment time in milliseconds | Informational - use for real-time performance monitoring |\n| `rdi_processor_total_time_ms_last` | Gauge | Last batch total time in milliseconds | Informational - use for real-time performance monitoring |\n| `rdi_processor_rec_per_sec_last` | Gauge | Last batch records per second | Informational - use for real-time throughput monitoring |\n\n> [!NOTE]\n> **Additional information about stream processor metrics:**\n>\n> - Where the metric name has the `rdi_` prefix, this will be replaced by the Kubernetes namespace name if you supplied a custom name during installation. The prefix is always `rdi_` for VM installations.\n> - Metrics with the `_created` suffix are automatically generated by Prometheus for counters and gauges to track when they were first created.\n> - The `rdi_incoming_entries` metric provides a detailed breakdown for each data source by operation type. The `data_source` label identifies the data stream the value belongs to, as the stream's key without the `data:` prefix (for example, `{rdi}:mysql.inventory.customers`). The key contains the source name, so you can break the metric down per source. Sources that existed before the upgrade to a version that supports multiple sources do not have the source segment in the key.\n> - The `rdi_stream_event_latency_ms` metric helps monitor data freshness and processing delays.\n> - The processor performance metrics are divided into two categories:\n>   - **Total metrics**: Accumulate values across all processed batches for historical analysis\n>   - **Last batch metrics**: Show real-time performance data for the most recently processed batch"},{"id":"flink-processor-metrics","title":"Flink processor metrics","role":"content","text":"The Flink processor exposes Prometheus metrics directly from its JobManager\nand TaskManager pods. The `rdi-metrics-exporter` service is not deployed for\nFlink-based pipelines, and the metrics described in\n[Stream processor metrics](#stream-processor-metrics) are not available.\n\nThe full set of metrics returned by the Flink processor is large and includes\nevery metric emitted by the underlying Flink runtime (job, task, operator,\nJVM, network, and connector metrics). See the\n[Flink metrics documentation](https://nightlies.apache.org/flink/flink-docs-release-2.0/docs/ops/metrics/)\nfor the full reference of Flink-emitted metrics, and the\n[Flink Prometheus reporter](https://nightlies.apache.org/flink/flink-docs-release-2.0/docs/deployment/metric_reporters/#prometheus)\ndocs for the naming scheme.\n\nConfigure Prometheus to scrape these metrics by enabling the JobManager and\nTaskManager `ServiceMonitor` resources under `operator.dataPlane.flinkProcessor`,\nas shown in [Helm installation](#helm-installation) above."},{"id":"useful-metrics","title":"Useful metrics","role":"content","text":"In addition to the standard Flink metrics, the Flink processor emits a small\nset of RDI-specific metrics that cover record counters, source/target\nconnectivity, and stream backlog. These metrics, together with a curated\nsubset of native Flink metrics, are surfaced through the\n[RDI API v2 metric collections endpoint](https://redis.io/docs/latest/integrate/redis-data-integration/reference/api-reference)\nand are the recommended starting point for dashboards and alerts.\n\n**RDI-emitted metrics** (per pipeline):\n\n| Metric | Description |\n|---|---|\n| `flink_jobmanager_job_operator_coordinator_stream_type_rdiRecords` | Per-stream record counters. Labels: `stream`, `type` (one of `incoming`, `inserted`, `updated`, `deleted`, `filtered`, `rejected`). |\n| `flink_jobmanager_job_operator_coordinator_enumerator_stream_type_rdiRecords` | Per-stream backlog and freshness. Labels: `stream`, `type` (`pending` for stream length, `lastArrival` for the epoch-millisecond timestamp of the last entry). |\n| `flink_taskmanager_job_task_operator_rdi_connected` | Source or target connection status (`1` = connected, `0` = disconnected). Filter by `operator_name` equal to `Source:_source` for the source and matching the regex `.*:target:_Writer$` for target writers; treat the source or target as connected if any subtask reports `1`. |\n| `flink_taskmanager_job_task_operator_rdi_lastModified` | Epoch-millisecond timestamp of the last successful write to the target Redis database. Filter by `operator_name` matching `.*:target:_Writer$` and take the maximum across subtasks. |\n| `flink_taskmanager_job_task_operator_pendingAck` | Number of records emitted by the source but awaiting checkpoint completion before being acknowledged. Sum across subtasks. |\n\n**Native Flink metrics** used by the API:\n\n| Metric | Description |\n|---|---|\n| `flink_taskmanager_job_task_operator_numRecordsInPerSecond` | Per-operator throughput. For source throughput, filter by `operator_name` equal to `Source:_source` and sum across subtasks. For sink throughput, filter by `operator_name` matching `.*:target:_Writer$` and sum across subtasks and across all target writers. |\n| `flink_taskmanager_job_task_busyTimeMsPerSecond` | Time the task spends actively processing records (ms/s). Average across subtasks of the main chained task; exclude the `dlq:_Writer` task. |\n| `flink_taskmanager_job_task_idleTimeMsPerSecond` | Time the task spends waiting for input (ms/s). Average across subtasks of the main chained task; exclude the `dlq:_Writer` task. |\n| `flink_taskmanager_job_task_backPressuredTimeMsPerSecond` | Time the task spends back-pressured because the downstream cannot keep up (ms/s). Average across subtasks of the main chained task; exclude the `dlq:_Writer` task. |\n| `flink_jobmanager_job_lastCheckpointDuration` | Duration of the most recent checkpoint (ms). |\n| `flink_jobmanager_job_lastCheckpointSize` | Persisted size of the most recent checkpoint (bytes). |\n| `flink_jobmanager_job_numberOfCompletedCheckpoints` | Total number of completed checkpoints. |\n| `flink_jobmanager_job_numberOfFailedCheckpoints` | Total number of failed checkpoints. |\n| `flink_jobmanager_job_<status>Time` | Time spent in each job state (ms), where `<status>` is one of `running`, `restarting`, `failing`, `cancelling`, `initializing`, `created`, or `deploying`. The metric for the current state is non-zero; all others are zero. Use this to derive both the current job status and the time spent in it. |\n| `flink_jobmanager_job_numRestarts` | Total number of job restarts since submission. |\n\n> [!NOTE]\n> Flink runtime metric names follow Flink's own naming scheme rather\n> than the `rdi_` prefix used by the classic processor. When you build\n> dashboards that should work for both processors, query the two metric sets\n> separately."},{"id":"operator-metrics","title":"Operator metrics","role":"content","text":"The RDI operator exposes Prometheus metrics at the `/metrics` endpoint to monitor the health and state of the operator itself and the Pipeline resources it manages.\n\nThe endpoint for operator metrics is `https://<RDI_HOST>/operator/metrics` (or the operator service endpoint in Kubernetes environments)."},{"id":"operator-metric-types","title":"Operator metric types","role":"content","text":"Most of the metrics exposed by the RDI operator are standard controller-runtime [metrics](https://book.kubebuilder.io/reference/metrics-reference).\nThe metrics that are relevant for RDI operations are listed in the table below:\n\n| Metric Name | Metric Type | Metric Description | Alerting Recommendations |\n|-------------|-------------|-------------------|-------------------------|\n| `rdi_operator_pipeline_phase` | Gauge | Current phase of each Pipeline resource with labels for `namespace`, `name`, and `phase` (Active, Inactive, Pending, Resetting, Error) | **Critical Alert**: Alert if the phase is \"Error\" for periods longer than 2 minutes |\n| `rdi_operator_is_leader` | Gauge | Leadership status of the operator instance (1 = leader, 0 = not leader) with label for `instance_id` | Informational - monitor to ensure that the correct RDI instance is the leader in HA or DR deployments |"},{"id":"understanding-operator-metrics","title":"Understanding operator metrics","role":"content","text":"**Pipeline phase tracking**: The `rdi_operator_pipeline_phase` metric helps you monitor the lifecycle state of each RDI Pipeline resource. Each pipeline reports its current phase (Active, Inactive, Pending, Resetting, or Error) as a gauge value of `1`, while all other phases for that pipeline are set to `0`. This allows you to track phase transitions and identify pipelines that are stuck in error states.\n\n**Leader election**: In high availability (HA) or disaster recovery (DR) deployments with multiple RDI instances, the `rdi_operator_is_leader` metric indicates which RDI instance is actively managing Pipeline resources. Only one RDI instance should have a value of `1` at any time, while all other instances should report `0`. This metric is useful for troubleshooting leader election issues in HA or DR deployments."},{"id":"accessing-operator-metrics","title":"Accessing operator metrics","role":"content","text":"In Kubernetes deployments, you can configure Prometheus to scrape operator metrics by enabling the Prometheus ServiceMonitor in your Helm values:\n\n[code example]\n**Note:** The ServiceMonitor resources must be labelled correctly for metrics to be auto-scraped by Prometheus. The correct label is configured in Prometheus, by default it is `release: prometheus`.\nYou can also expose the metrics endpoint externally using an Ingress:\n\n[code example]\n\nThen access metrics at `https://operator.example.com/operator/metrics`."},{"id":"recommended-alerting-strategy","title":"Recommended alerting strategy","role":"content","text":"The alerting strategy described in the sections below focuses on system failures and data integrity issues that require immediate attention. Most other metrics are informational, so you should monitor them for trends rather than trigger alerts."},{"id":"critical-alerts-immediate-response-required","title":"Critical alerts (immediate response required)","role":"content","text":"These are the only alerts that require immediate action:\n\n**Collector alerts:**\n- `Connected = 0`: Database connectivity has been lost. RDI cannot function without a database connection.\n- `NumberOfErroneousEvents > 0`: Errors are occurring during data processing. This indicates data corruption or processing failures.\n- `SnapshotAborted = 1`: The snapshot process has failed, so the initial sync is incomplete.\n\n**Processor alerts:**\n- `rejected_records_total > 0`: Records are being rejected. This indicates data quality issues or processing failures.\n- `rdi_engine_state`: Alert only if the state indicates a clear failure condition (not just \"not running\").\n\n**Operator alerts:**\n- `rdi_operator_pipeline_phase` with `phase=\"Error\"` for more than 2 minutes: A Pipeline resource has entered an error state and requires investigation.\n- No leader in HA or DR setups: If both RDI instances report `rdi_operator_is_leader = 0` for more than 2 minutes, the RDI pipeline is not active. \n- Multiple leaders in HA or DR setups: If both RDI instances report `rdi_operator_is_leader = 1`, RDI is in a \"split brain\" state."},{"id":"important-monitoring-but-not-alerts","title":"Important monitoring (but not alerts)","role":"content","text":"You should monitor these metrics on dashboards and review them regularly, but they don't require automated alerts:\n\n- **Queue metrics**: Queue utilization can vary widely and hitting 0% or 100% capacity may be normal during certain operations.\n- **Latency metrics**: Lag and processing times depend heavily on business requirements and normal operational patterns.\n- **Event counters**: Event rates naturally vary based on application usage patterns.\n- **Snapshot progress**: Snapshot duration and progress depend on data size, so you should typically monitor them manually.\n- **Schema changes**: Schema change frequency is highly application-dependent."},{"id":"key-principles-for-rdi-alerting","title":"Key principles for RDI alerting","role":"content","text":"- **Alert on failures, not performance**: Focus alerts on system failures rather than performance degradation.\n- **Business context matters**: Latency and throughput requirements vary significantly between organizations.\n- **Establish baselines first**: Monitor metrics for weeks before you set any threshold-based alerts.\n- **Avoid alert fatigue**: If you see too many non-critical alerts, you are less likely to take truly critical issues seriously.\n- **Use dashboards for trends**: Most metrics are better suited for dashboard monitoring than alerting"},{"id":"monitoring-best-practices","title":"Monitoring best practices","role":"content","text":"- **Dashboard-first approach**: Use Grafana dashboards to visualize trends and patterns.\n- **Baseline establishment**: Monitor your specific workload for 2-4 weeks before you consider adding more alerts.\n- **Business SLA alignment**: Only create alerts for metrics that directly impact your business SLA requirements.\n- **Manual review**: Don't use automated alerts to review metric trends. Instead, schedule regular business reviews to check them manually."},{"id":"rdi-logs","title":"RDI logs","role":"content","text":"RDI uses [fluentd](https://www.fluentd.org/) and\n[logrotate](https://linux.die.net/man/8/logrotate) to ship and rotate logs\nfor its Kubernetes (K8s) components.\nSo whenever a containerized component is removed by the RDI operator process or by K8s,\nthe logs are available for you to inspect.\nBy default, RDI stores logs in the host VM file system at `/opt/rdi/logs`.\nThe logs are recorded at the minimum `INFO` level and get rotated when they reach a size of 100MB.\nRDI retains the last five log rotated files by default.\nLogs are in a straightforward text format, which lets you analyze them with several different observability tools.\nYou can change the default log settings using the\n[`redis-di configure-rdi`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-configure-rdi)\ncommand."},{"id":"dump-support-package","title":"Dump support package","role":"content","text":"If you ever need to send a comprehensive set of forensics data to Redis support then you should\nrun the\n[`redis-di dump-support-package`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-dump-support-package)\ncommand from the CLI. See\n[Troubleshooting](https://redis.io/docs/latest/integrate/redis-data-integration/troubleshooting#dump-support-package)\nfor more information."}],"examples":[{"id":"helm-installation-ex0","language":"yaml","code":"dataPlane:\n          collector:\n            # Enable service monitor\n            serviceMonitor:\n              enabled: true\n\n              # Make sure to label the ServiceMonitor so that Prometheus can discover it\n              labels:\n                release: prometheus","section_id":"helm-installation"},{"id":"helm-installation-ex1","language":"yaml","code":"rdiMetricsExporter:\n          # Enable service monitor\n          serviceMonitor:\n            enabled: true\n\n            # Make sure to label the ServiceMonitor so that Prometheus can discover it\n            labels:\n              release: prometheus","section_id":"helm-installation"},{"id":"helm-installation-ex2","language":"yaml","code":"operator:\n          prometheus:\n            enabled: true\n            labels:\n              release: prometheus\n          metrics:\n            enabled: true","section_id":"helm-installation"},{"id":"helm-installation-ex3","language":"yaml","code":"operator:\n          dataPlane:\n            flinkProcessor:\n              jobManager:\n                serviceMonitor:\n                  enabled: true\n                  labels:\n                    release: prometheus\n              taskManager:\n                serviceMonitor:\n                  enabled: true\n                  labels:\n                    release: prometheus","section_id":"helm-installation"},{"id":"accessing-operator-metrics-ex0","language":"yaml","code":"operator:\n  prometheus:\n    enabled: true\n    labels:\n      release: prometheus","section_id":"accessing-operator-metrics"},{"id":"accessing-operator-metrics-ex1","language":"yaml","code":"operator:\n  ingress:\n    enabled: true\n    hosts:\n      - operator.example.com\n    pathPrefix: \"\"","section_id":"accessing-operator-metrics"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/quick-start-guide","title":"Quickstart","url":"https://redis.io/docs/latest/integrate/redis-data-integration/quick-start-guide/","summary":"Get started with a simple pipeline example","aliases":["/integrate/redis-data-integration/ingest/quick-start-guide/"],"tags":["redis-di"],"last_updated":"2026-10-02T14:54:03+01:00","page_type":"content","content_hash":"c1e850027a8fa283bae8a52cba4652bb700d1f9a48adfc88a69e57f6d1e54a18","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"In this tutorial you will learn how to install RDI and set up a pipeline to ingest live data from a [PostgreSQL](https://www.postgresql.org/) database into a Redis database."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"- A Redis Enterprise database that will serve as the pipeline target. The dataset that will be ingested is\n  quite small in size, so a single shard database should be enough. RDI also needs to maintain its\n  own database on the cluster to store state information. *This requires Redis Enterprise v6.4 or greater*.\n- [Redis Insight](https://redis.io/docs/latest/develop/tools/insight)\n  to edit your pipeline\n- A virtual machine (VM) with one of the following operating systems:  \n  * RHEL 8 or 9\n* Ubuntu 20.04, 22.04, or 24.04"},{"id":"overview","title":"Overview","role":"overview","text":"The following diagram shows the structure of the pipeline we will create (see\nthe [architecture overview](https://redis.io/docs/latest/integrate/redis-data-integration/architecture#overview) to learn how the pipeline works):\n\n![](https://redis.io/docs/latest/images/rdi/ingest/ingest-qsg.webp)\n\nHere, the RDI *collector* tracks changes in PostgreSQL and writes them to streams in the \nRDI database in Redis. The *stream processor* then reads data records from the RDI\ndatabase streams, processes them, and writes them to the target."},{"id":"install-postgresql","title":"Install PostgreSQL","role":"setup","text":"We provide a [Docker](https://www.docker.com/) image for an example PostgreSQL\ndatabase that we will use for the tutorial. Follow the\n[instructions on our Github page](https://github.com/Redislabs-Solution-Architects/rdi-quickstart-postgres/tree/main)\nto download the image and start serving the database. The database, which is\ncalled `chinook`, has the [schema and data](https://www.kaggle.com/datasets/samaxtech/chinook-music-store-data?select=schema_diagram.png) for an imaginary online music store\nand is already set up for the RDI collector to use."},{"id":"install-rdi","title":"Install RDI","role":"setup","text":"Install RDI using the instructions in the\n[VM installation guide](https://redis.io/docs/latest/integrate/redis-data-integration/installation/install-vm).\n\nRDI will create the pipeline template for your chosen source database type at\n`/opt/rdi/config`. You will need this pathname later when you prepare the pipeline for deployment\n(see [Prepare the pipeline](#prepare-the-pipeline) below).\n\nAt the end of the installation, RDI CLI will prompt you to set the access secrets\nfor both the source PostgreSQL database and the target Redis database. RDI needs these to\nrun the pipeline.\n\nUse the Redis Enterprise Cluster Manager UI to create the RDI database with the following requirements:\n\n* Redis Enterprise v6.4 or greater for the cluster.\n* For production, 250MB RAM with one primary and one replica is recommended, but for the\n  quickstart or for development, 125MB and a single shard is sufficient.\n* If you are deploying RDI for a production environment then secure this database with a password\n  and TLS.\n* Set the database's\n  [eviction policy](https://redis.io/docs/latest/operate/rs/databases/memory-performance/eviction-policy) to `noeviction`. Note that you can't set this using\n  [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin),\n  so you must either do it using the admin UI or with the following\n  [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api)\n  command:\n\n  [code example]\n* Set the database's\n  [data persistence](https://redis.io/docs/latest/operate/rs/databases/configure/database-persistence)\n  to AOF - fsync every 1 sec. Note that you can't set this using\n  [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin),\n  so you must either do it using the admin UI or with the following\n  [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api)\n  commands:\n\n  [code example]\n If you don't have permissions to use AOF persistence, please check the [Using RDI without persistence](https://redis.io/docs/latest/integrate/redis-data-integration/faq#can-i-use-rdi-without-persistence-enabled) section in the FAQ.\n\n* **Ensure that the RDI database is not clustered.** RDI will not work correctly if the\n  RDI database is clustered (but note that the target database *can* be clustered without\n  any problems).\n\n  When you create the RDI database, expand the **Clustering** section and make sure the\n  **Sharding** option is *unchecked* (as shown below).\n\n  ![The Sharding option is unchecked in the Clustering section of the create database form.](https://redis.io/docs/latest/images/rdi/ingest/RDIClusterSetting.webp)\n\n  You can check if your RDI database is clustered from its **Configuration** tab in the\n  Cluster Manager UI. In the **Clustering** section, **Sharding** should be set to **Disabled**,\n  as shown in the following screenshot:\n\n  ![The Clustering section of the database Configuration tab shows Sharding: Disabled.](https://redis.io/docs/latest/images/rdi/ingest/RDICheckUnclustered.webp)\n\n  If you find the database has been clustered by mistake, you must create a new database with\n  sharding disabled before continuing with the RDI installation."},{"id":"prepare-the-pipeline","title":"Prepare the pipeline","role":"content","text":"During the installation, RDI placed the pipeline templates at `/opt/rdi/config`.\nIf you go to that folder and run the `ll` command, you will see the pipeline\nconfiguration file, `config.yaml`, and the `jobs` folder (see the page about\n[Pipelines](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines) for more information). Use Redis Insight to open\nthe `config.yaml` file and then edit the following settings:\n\n- Set the `host` to `localhost` and the `port` to 5432.\n- Under `tables`, specify the `Track` table from the source database.\n- Add the details of your target database to the `target` section.\n\nAt this point, the pipeline is ready to deploy.\n\nThis tutorial uses one source database, but a pipeline can capture from several source databases\ninto the same Redis target. See\n[Multiple sources in one pipeline](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/multiple-sources) for more information."},{"id":"create-context","title":"Create a context (optional)","role":"content","text":"To manage and inspect RDI, you can use the\n[`redis-di`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli)\nCLI tool, which has several commands for different purposes. Most of these commands connect to\nthe RDI API, which you specify with the `--api-url` option. You can avoid typing this and the other\nconnection options repeatedly by saving them in a *context*.\n\nWhen you activate a context, its saved connection options are used automatically whenever\nyou use `redis-di`. If you have more than one RDI installation, you can create a context\nfor each of them and select the one you want to be active using its unique name.\n\nTo create a context, use the\n[`redis-di set-context`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-set-context)\ncommand. For a VM installation, the API has the same hostname or IP address as your RDI VM and uses\nthe default HTTPS port 443:\n\n[code example]\n\nYou can save a few other options, such as a CA certificate (`--cacert`) if the API uses a private\ncertificate (see the\n[reference page](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-set-context)\nfor details). When you have created a context, use\n[`redis-di use-context`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-use-context)\nto activate it:\n\n[code example]\n\nThere are also subcommands to\n[list](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list-contexts)\nand [delete](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-delete-context)\ncontexts."},{"id":"deploy-the-pipeline","title":"Deploy the pipeline","role":"content","text":"You can deploy the pipeline with the following command:\n\n[code example]\n\nwhere the path is the one you supplied earlier during the installation. (You may also need\nto supply the `--api-url` option if you are not using a\n[context](#create-context) as described above.) RDI first\nvalidates your pipeline and then deploys it if the configuration is correct.\n\nYou can also use [Redis Insight](https://redis.io/docs/latest/develop/tools/insight/rdi-connector)\nto deploy the pipeline, by adding a connection to the RDI API\nendpoint (which has the same hostname or IP address as your RDI VM and uses the default HTTPS port 443) and then clicking the **Deploy** button.\n\nOnce the pipeline is running, you can use Redis Insight to view the data flow using the\npipeline metrics. You can also connect to your target database to see the keys that RDI has written there.\n\nSee [Deploy a pipeline](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/deploy)\nfor more information about deployment settings."},{"id":"view-rdis-response-to-data-changes","title":"View RDI's response to data changes","role":"content","text":"Once the pipeline has loaded a *snapshot* of all the existing data from the source,\nit enters *change data capture (CDC)* mode (see the\n[architecture overview](https://redis.io/docs/latest/integrate/redis-data-integration/architecture#overview)\nand the\n[ingest pipeline lifecycle](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines#pipeline-lifecycle)\nfor more information\n).\n\nTo see the RDI pipeline working in CDC mode:\n \n- Create a simulated load on the source database\n  (see [Generating load on the database](https://github.com/Redislabs-Solution-Architects/rdi-quickstart-postgres?tab=readme-ov-file#generating-load-on-the-database)\n  to learn how to do this).\n- Run\n  [`redis-di describe`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-describe)\n  to see the flow of records. To watch it update live, pair the command with `watch`, for example\n  `watch -n 1 redis-di describe`.\n- Use [Redis Insight](https://redis.io/docs/latest/develop/tools/insight) to look at the data in the target database."}],"examples":[{"id":"install-rdi-ex0","language":"bash","code":"curl -v -k -d '{\"eviction_policy\": \"noeviction\"}' \\\n    -u '<USERNAME>:<PASSWORD>' \\\n    -H \"Content-Type: application/json\" \\\n    -X PUT https://<CLUSTER_FQDN>:9443/v1/bdbs/<BDB_UID>","section_id":"install-rdi"},{"id":"install-rdi-ex1","language":"bash","code":"curl -v -k -d '{\"data_persistence\":\"aof\"}' \\\n    -u '<USERNAME>:<PASSWORD>' \\\n    -H \"Content-Type: application/json\" \n    -X PUT https://<CLUSTER_FQDN>:9443/v1/bdbs/<BDB_UID>\n  curl -v -k -d '{\"aof_policy\":\"appendfsync-every-sec\"}' \\\n    -u '<USERNAME>:<PASSWORD>' \\\n    -H \"Content-Type: application/json\" \\\n    -X PUT https://<CLUSTER_FQDN>:9443/v1/bdbs/<BDB_UID>","section_id":"install-rdi"},{"id":"create-context-ex0","language":"bash","code":"redis-di set-context <unique-context-name> --api-url https://<host> --user <user>","section_id":"create-context"},{"id":"create-context-ex1","language":"bash","code":"redis-di use-context <context name>","section_id":"create-context"},{"id":"deploy-the-pipeline-ex0","language":"bash","code":"redis-di deploy --dir <path to pipeline folder>","section_id":"deploy-the-pipeline"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/rdi-archive","title":"Preview version","url":"https://redis.io/docs/latest/integrate/redis-data-integration/rdi-archive/","summary":"Describes where to view the preview version for RDI products","tags":["docs","operate","rc"],"last_updated":"2026-06-04T10:40:15+01:00","page_type":"content","content_hash":"55fef5601de39dd089ad8f71fe3dc26aabf5bfcdfa33c7ebba891a3983122419","sections":[{"id":"content","title":"Content","role":"content","text":"RDI is now in general availability but you can still access an\n[archived version of the docs for the preview version](https://docs.redis.com/rdi-preview/rdi/)\nif you need to refer to them. Note that these docs will not be updated and\ninformation in the current docs supersedes the content of the preview docs."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/api-migration","title":"Migrate from RDI API v1 to v2","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/api-migration/","summary":"Migrate RDI API clients from API v1 to API v2","aliases":["/integrate/redis-data-integration/ingest/reference/api-migration/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-18T16:47:14+01:00","page_type":"content","content_hash":"5cd7555cdc2346c9b833728dbcb095646e3665371e12a6ddf5132b5f84f7a6f3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"RDI API v1 is deprecated as of RDI 1.19.0. Existing v1 endpoints remain available for backward compatibility, but Redis recommends moving all integrations to API v2. API v1 will not be extended with new RDI features and may be removed in a future RDI version. See the [RDI API reference](https://redis.io/docs/latest/integrate/redis-data-integration/reference/api-reference) for the current request and response schemas."},{"id":"what-changes-in-api-v2","title":"What changes in API v2","role":"content","text":"API v2 uses the pipeline resource to represent the current state of a pipeline. Operations update that resource and return its current state, so applications no longer need to poll a separate action ID. API v2 scopes related operations under a pipeline name in the request path.\n\n> [!NOTE]\n> RDI 1.19.0 supports only one pipeline, which must be named `default`. Support for other pipeline names will be added in a future version.\n\nThe API version is part of the URL. Update `/api/v1` requests to use `/api/v2` where a corresponding v2 endpoint is available. You should also review the request and response models, because they can differ between versions."},{"id":"api-v1-and-multiple-sources","title":"API v1 and multiple sources","role":"content","text":"API v1 handles single-source pipelines only, so\nyou must use API v2 to manage [a pipeline with several sources](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/multiple-sources). In particular:\n\n- `POST /api/v1/pipelines`, `PATCH /api/v1/pipelines`, and `POST /api/v1/pipelines/sources/dry-run` reject a configuration with more than one source with `422 Unprocessable Content` and `Only a single source per pipeline is supported`.\n- `PUT /api/v1/secrets`, `PUT /api/v1/secrets/{secret_name}`, and the v1 source management endpoints only handle a pipeline whose single source still uses the legacy `source` names, which covers a pipeline created before RDI supported multiple sources and one that you create through v1. They reject any other pipeline with `Use API v2 to manage sources and secrets of this pipeline.`\n- `GET /api/v1/status` and `GET /api/v1/monitoring/statistics` report the first source of the pipeline only."},{"id":"endpoint-mapping","title":"Endpoint mapping","role":"content","text":"| API v1 | API v2 |\n| --- | --- |\n| `GET /api/v1/pipelines` | `GET /api/v2/pipelines` |\n| `POST /api/v1/pipelines` | `POST /api/v2/pipelines` |\n| `PATCH /api/v1/pipelines` | `PATCH /api/v2/pipelines/{name}` |\n| `GET /api/v1/status` | `GET /api/v2/pipelines/{name}/status` |\n| `POST /api/v1/pipelines/start` | `POST /api/v2/pipelines/{name}/start` |\n| `POST /api/v1/pipelines/stop` | `POST /api/v2/pipelines/{name}/stop` |\n| `POST /api/v1/pipelines/reset` | `POST /api/v2/pipelines/{name}/reset` |\n| `GET /api/v1/monitoring/statistics` | `GET /api/v2/pipelines/{name}/metric-collections/{collection_name}` |\n| `GET /api/v1/pipelines/config/schemas` | `GET /api/v2/schemas/config` |\n| `GET /api/v1/pipelines/jobs/functions` | `GET /api/v2/functions` |\n| `GET /api/v1/pipelines/jobs/schemas` | `GET /api/v2/schemas/jobs` |\n| `PUT /api/v1/pipelines/sources` and source subresources | `PATCH /api/v2/pipelines/{name}` with `sources` in the payload |\n| `PUT /api/v1/pipelines/targets` and target subresources | `PATCH /api/v2/pipelines/{name}` with `targets` in the payload |\n| `PUT /api/v1/pipelines/processors` and `PUT /api/v1/pipelines/processors/{prop}` | `PATCH /api/v2/pipelines/{name}` with `processors` in the payload |\n| `PUT /api/v1/secrets` and `PUT /api/v1/secrets/{secret_name}` | `POST`, `PUT`, or `DELETE /api/v2/pipelines/{name}/secrets[/{key}]` with the `db` query parameter |\n| `POST`, `PUT`, or `DELETE /api/v1/pipelines/secret-providers/{name}` | `PATCH /api/v2/pipelines/{name}` with `secret-providers` in the payload |\n| Source metadata, schemas, databases, tables, and columns endpoints | `GET /api/v2/pipelines/{name}/source-schemas/{source_name}` with the appropriate filters |\n| `POST /api/v1/pipelines/sources/dry-run` | `POST /api/v2/pipelines?dry_run=true` |\n| `POST /api/v1/pipelines/targets/dry-run` | `POST /api/v2/pipelines?dry_run=true` |\n| `POST /api/v1/pipelines/undeploy` | `PUT /api/v2/pipelines/{name}` with an empty configuration |\n| `POST /api/v1/trace/start` | `POST /api/v2/pipelines/{name}/traces` |\n\nAPI v2 also adds endpoints for DLQ inspection, target flushing, metric collections, and API information. See the [API reference](https://redis.io/docs/latest/integrate/redis-data-integration/reference/api-reference) for the complete list."},{"id":"v1-endpoints-without-a-v2-equivalent","title":"v1 endpoints without a v2 equivalent","role":"content","text":"Most v1 endpoints have a v2 replacement. However, the following endpoints remain available under v1 because the current API v2 design does not define a corresponding endpoint:\n\n| v1 endpoint | Notes |\n| --- | --- |\n| `GET /api/v1/me` | Returns the authenticated user. |\n| `GET /api/v1/pipelines/strategies` | Returns pipeline strategies. |\n| `POST /api/v1/login` | API v2 continues to use this endpoint for authentication. |\n| `GET /api/v1/pipelines/config/templates/ingest/{db_type}` | Used by the CLI to scaffold pipeline configuration and will remain available. |\n| `GET /api/v1/pipelines/jobs/templates/ingest` | Used to scaffold jobs and will remain available. |"},{"id":"replace-action-polling-with-pipeline-status-polling","title":"Replace action polling with pipeline-status polling","role":"content","text":"In API v1, a pipeline operation returns an action ID. The client then repeatedly requests that action until it finishes:\n\n[code example]\n\nWith API v2, you should instead include the pipeline name in the operation URL to read its status. The operation response contains the current pipeline state. You can check the pipeline's status by polling its status endpoint, as shown below:\n\n[code example]\n\nNote:\n\n- The successful state depends on the operation. For a stop, reset, update, or create operation, wait for the corresponding successful state returned by the API. For example, wait for `stopped` instead of `started` for a stop operation.\n- Only accept a terminal status when `current` is `true`. When it is `false`, the status is outdated, so continue polling.\n- Always handle `error` as a failed operation. Use the status values documented by the API response for the operation you are performing to check for errors.\n- Do not expect an action ID from API v2."},{"id":"migration-steps","title":"Migration steps","role":"content","text":"1. Find the applications, scripts, and SDKs in your environment that use `/api/v1`.\n2. Add the pipeline name to each v2 request. The only pipeline in 1.19.0 is always named `default`.\n3. Check the pipeline response, or call `GET /api/v2/pipelines/{name}/status`, instead of polling an action ID.\n4. Use `POST /api/v2/pipelines`, `PUT /api/v2/pipelines/{name}`, or `PATCH /api/v2/pipelines/{name}` to update source, target, processor, and secret-provider settings as needed. When using `PATCH`, omit the configuration sections that you do not want to change.\n5. Change secret requests to address a secret by a database-independent key and a `db` parameter. Where a v1 request set `SOURCE_DB_PASSWORD`, a v2 request sets the `PASSWORD` key with `db` naming the source, and `TARGET_DB_PASSWORD` becomes the `PASSWORD` key with `db=target`. See [Set secrets](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/deploy#set-secrets). The environment variable that `config.yaml` references is unchanged for a source that existed before RDI supported multiple sources; see [Existing names are kept after an upgrade](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/multiple-sources#existing-names-are-kept-after-an-upgrade).\n6. Use `GET /api/v2/pipelines/{name}/metric-collections/{collection_name}` for monitoring and `GET /api/v2/pipelines/{name}/source-schemas/{source_name}` for source metadata.\n7. Test creating, updating, validating, starting, stopping, resetting, and deleting a pipeline on a non-production RDI 1.19.0 or later installation before updating production applications.\n\nAuthentication and the API base URL do not change. The migration requires updates to the endpoint paths, pipeline scoping, request models, and operation status handling."}],"examples":[{"id":"replace-action-polling-with-pipeline-status-polling-ex0","language":"bash","code":"# Start a pipeline with API v1\naction=$(curl -sS -X POST \"$RDI_URL/api/v1/pipelines/start\" \\\n  -H \"Authorization: Bearer $RDI_TOKEN\" \\\n  | jq -r '.action_id')\n\n# Poll the action until it completes\ncurl -sS \"$RDI_URL/api/v1/actions/$action\" \\\n  -H \"Authorization: Bearer $RDI_TOKEN\"","section_id":"replace-action-polling-with-pipeline-status-polling"},{"id":"replace-action-polling-with-pipeline-status-polling-ex1","language":"bash","code":"pipeline=default\n\n# Start the pipeline with API v2\ncurl -sS -X POST \"$RDI_URL/api/v2/pipelines/$pipeline/start\" \\\n  -H \"Authorization: Bearer $RDI_TOKEN\"\n\n# Poll the pipeline status while the operation is in progress\nwhile true; do\n  status=$(curl -sS \"$RDI_URL/api/v2/pipelines/$pipeline/status\" \\\n    -H \"Authorization: Bearer $RDI_TOKEN\")\n  phase=$(printf '%s' \"$status\" | jq -r '.status')\n  current=$(printf '%s' \"$status\" | jq -r '.current')\n  printf '%s\\n' \"$status\"\n\n  case \"$current:$phase\" in\n    true:started|true:error) break ;;\n    *) sleep 2 ;;\n  esac\ndone","section_id":"replace-action-polling-with-pipeline-status-polling"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/api-reference","title":"Redis Data Integration API","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/api-reference/","summary":"","aliases":["/integrate/redis-data-integration/reference/api/api-reference/"],"tags":[],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"index","sections":[],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/cli","title":"CLI reference","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/","summary":"Reference for the RDI CLI commands","aliases":["/integrate/redis-data-integration/ingest/reference/cli/","/integrate/redis-data-integration/reference/cli/redis-di-install/","/integrate/redis-data-integration/reference/cli/redis-di-upgrade/","/integrate/redis-data-integration/reference/cli/redis-di-monitor/","/integrate/redis-data-integration/reference/cli/redis-di-trace/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-18T16:41:35+01:00","children":[{"id":"integrate/redis-data-integration/reference/cli/previous-cli-comparison","summary":"Compare the RDI CLI introduced in version 1.19.0 with the previous CLI","title":"Compare the previous and current RDI CLI","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/previous-cli-comparison/"},{"id":"integrate/redis-data-integration/reference/cli/redis-di","summary":"Command line tool to manage Redis Data Integration","title":"redis-di","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di/"},{"id":"integrate/redis-data-integration/reference/cli/redis-di-completion","summary":"Generates a shell autocompletion script","title":"redis-di completion","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-completion/"},{"id":"integrate/redis-data-integration/reference/cli/redis-di-configure-rdi","summary":"Configures the RDI database connection","title":"redis-di configure-rdi","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-configure-rdi/"},{"id":"integrate/redis-data-integration/reference/cli/redis-di-delete","summary":"Deleting a pipeline is not supported","title":"redis-di delete","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-delete/"},{"id":"integrate/redis-data-integration/reference/cli/redis-di-delete-context","summary":"Deletes a context","title":"redis-di delete-context","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-delete-context/"},{"id":"integrate/redis-data-integration/reference/cli/redis-di-delete-secret","summary":"Deletes a secret of a pipeline","title":"redis-di delete-secret","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-delete-secret/"},{"id":"integrate/redis-data-integration/reference/cli/redis-di-deploy","summary":"Deploys a pipeline with the specified configuration","title":"redis-di deploy","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-deploy/"},{"id":"integrate/redis-data-integration/reference/cli/redis-di-describe","summary":"Describes a pipeline with its status","title":"redis-di describe","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-describe/"},{"id":"integrate/redis-data-integration/reference/cli/redis-di-describe-context","summary":"Describes a context","title":"redis-di describe-context","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-describe-context/"},{"id":"integrate/redis-data-integration/reference/cli/redis-di-describe-job","summary":"Describes a job of a pipeline","title":"redis-di describe-job","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-describe-job/"},{"id":"integrate/redis-data-integration/reference/cli/redis-di-describe-secret","summary":"Describes a secret of a pipeline","title":"redis-di describe-secret","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-describe-secret/"},{"id":"integrate/redis-data-integration/reference/cli/redis-di-dump-support-package","summary":"Dumps the RDI support package","title":"redis-di dump-support-package","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-dump-support-package/"},{"id":"integrate/redis-data-integration/reference/cli/redis-di-get","summary":"Gets a pipeline","title":"redis-di get","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-get/"},{"id":"integrate/redis-data-integration/reference/cli/redis-di-get-dlq","summary":"Gets a dead-letter queue of a pipeline","title":"redis-di get-dlq","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-get-dlq/"},{"id":"integrate/redis-data-integration/reference/cli/redis-di-get-job","summary":"Gets a job of a pipeline","title":"redis-di get-job","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-get-job/"},{"id":"integrate/redis-data-integration/reference/cli/redis-di-get-metric-collection","summary":"Gets a metric collection of a pipeline","title":"redis-di get-metric-collection","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-get-metric-collection/"},{"id":"integrate/redis-data-integration/reference/cli/redis-di-get-secret","summary":"Gets a secret of a pipeline","title":"redis-di get-secret","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-get-secret/"},{"id":"integrate/redis-data-integration/reference/cli/redis-di-info","summary":"Displays information about the RDI deployment","title":"redis-di info","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-info/"},{"id":"integrate/redis-data-integration/reference/cli/redis-di-list","summary":"Lists all pipelines","title":"redis-di list","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list/"},{"id":"integrate/redis-data-integration/reference/cli/redis-di-list-contexts","summary":"Lists all contexts","title":"redis-di list-contexts","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list-contexts/"},{"id":"integrate/redis-data-integration/reference/cli/redis-di-list-dlq-records","summary":"Lists the rejected records of a dead-letter queue","title":"redis-di list-dlq-records","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list-dlq-records/"},{"id":"integrate/redis-data-integration/reference/cli/redis-di-list-dlqs","summary":"Lists the dead-letter queues of a pipeline","title":"redis-di list-dlqs","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list-dlqs/"},{"id":"integrate/redis-data-integration/reference/cli/redis-di-list-jobs","summary":"Lists the jobs of a pipeline","title":"redis-di list-jobs","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list-jobs/"},{"id":"integrate/redis-data-integration/reference/cli/redis-di-list-metric-collections","summary":"Lists the metric collections of a pipeline","title":"redis-di list-metric-collections","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list-metric-collections/"},{"id":"integrate/redis-data-integration/reference/cli/redis-di-list-secrets","summary":"Lists the secrets of a pipeline","title":"redis-di list-secrets","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list-secrets/"},{"id":"integrate/redis-data-integration/reference/cli/redis-di-reset","summary":"Resets a pipeline","title":"redis-di reset","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-reset/"},{"id":"integrate/redis-data-integration/reference/cli/redis-di-scaffold","summary":"Generates pipeline configuration files","title":"redis-di scaffold","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-scaffold/"},{"id":"integrate/redis-data-integration/reference/cli/redis-di-set-context","summary":"Creates or updates a context","title":"redis-di set-context","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-set-context/"},{"id":"integrate/redis-data-integration/reference/cli/redis-di-set-secret","summary":"Creates or updates a secret of a pipeline","title":"redis-di set-secret","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-set-secret/"},{"id":"integrate/redis-data-integration/reference/cli/redis-di-start","summary":"Starts a pipeline","title":"redis-di start","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-start/"},{"id":"integrate/redis-data-integration/reference/cli/redis-di-stop","summary":"Stops a pipeline","title":"redis-di stop","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-stop/"},{"id":"integrate/redis-data-integration/reference/cli/redis-di-use-context","summary":"Sets a context to be the active one","title":"redis-di use-context","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-use-context/"}],"page_type":"content","content_hash":"091dcf19237f2165719eea8b933e02e42bbe5d94705f981ab8cdd1013e211f40","sections":[{"id":"overview","title":"Overview","role":"overview","text":"`redis-di` is the command line tool that manages Redis Data Integration (RDI).\nIt is a thin client over the RDI REST API, so it works the same way for all\ninstallation types: [VM](https://redis.io/docs/latest/integrate/redis-data-integration/installation/install-vm),\n[Kubernetes](https://redis.io/docs/latest/integrate/redis-data-integration/installation/install-k8s), and Redis Cloud.\nUse it to deploy pipelines, manage secrets, inspect status and metrics, and read rejected records.\n\n> [!NOTE]\n> RDI 1.19.0 introduced the current API-based CLI. If you are moving from an earlier RDI version,\n> see [Compare the previous and current RDI CLI](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/previous-cli-comparison)\n> for the connection, context, command, and output changes."},{"id":"connecting-to-the-api","title":"Connecting to the API","role":"content","text":"Most commands connect to the RDI API, which you specify with the `--api-url` option (or the\n`RDI_API_URL` environment variable). Because the API is served over HTTPS, you can also supply\n`--cacert` to trust a private or self-signed certificate, or `--insecure` to skip TLS verification.\n\nThe CLI supports three authentication modes, selected by the credentials you provide:\n\n- **User authentication** (JWT): when you set a `--user`, the CLI logs in with that user and a\n  password from `--password`, the `RDI_PASSWORD` environment variable, or an interactive prompt.\n  This is the usual mode for VM and Kubernetes installations.\n- **Redis Cloud authentication**: when you set an `--account-key`, the CLI authenticates to the\n  Redis Cloud API gateway with that account key and a user key from `--user-key`, the `RDI_USER_KEY`\n  environment variable, or an interactive prompt. This is the mode for RDI running in Redis Cloud.\n- **No authentication**: when you set neither a user nor an account key, the CLI connects without\n  authenticating, which is the mode to use when authentication is disabled in the API.\n\nSetting both `--user` and `--account-key` is an error, as is setting both `--cacert` and `--insecure`.\nPasswords and user keys are secrets and are never stored on disk."},{"id":"contexts","title":"Contexts","role":"content","text":"Instead of passing the connection options on every command, you can save them in a _context_.\nContexts are stored in a `~/.redis-di` file that holds a map of named contexts and the active one,\nsimilar to a `kubeconfig` file. Each context sets an `api-url`, an optional `user` or `account-key`,\nand either a `cacert` or `insecure: true`. Secrets (the password and user key) are never stored, so\nyou still supply them per session.\n\n[code example]\n\nUse the [`set-context`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-set-context)\nand [`use-context`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-use-context)\ncommands to create and select contexts rather than editing the file by hand."},{"id":"commands","title":"Commands","role":"syntax","text":"Pipeline-scoped commands take the pipeline name as an optional positional argument that defaults to\n`default`, for example `redis-di start [pipeline]`. Sub-resource commands (for a secret, DLQ, or job)\ntake their own key or name as the positional argument and target the pipeline with the `-p` / `--pipeline`\noption, which also defaults to `default`.\n\nThere are two command options that select a particular database of the pipeline. The secret commands take `--db` with\na source name or `target` to address that database's secrets, and the `start`, `stop`, and `reset` commands take\n`--source` with a source name to act on that source alone. See\n[Multiple sources in one pipeline](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/multiple-sources) for more information.\n\nThe commands group as follows:\n\n- **Information**: [`info`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-info).\n- **Pipelines**: [`list`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list),\n  [`get`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-get),\n  [`describe`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-describe) (alias `status`),\n  [`deploy`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-deploy) (alias `set`),\n  [`start`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-start),\n  [`stop`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-stop), and\n  [`reset`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-reset).\n- **Secrets**: [`list-secrets`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list-secrets),\n  [`get-secret`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-get-secret),\n  [`describe-secret`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-describe-secret),\n  [`set-secret`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-set-secret), and\n  [`delete-secret`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-delete-secret).\n- **Dead-letter queues**: [`list-dlqs`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list-dlqs),\n  [`get-dlq`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-get-dlq), and\n  [`list-dlq-records`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list-dlq-records) (alias `get-rejected`).\n- **Jobs**: [`list-jobs`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list-jobs),\n  [`get-job`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-get-job), and\n  [`describe-job`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-describe-job).\n- **Metric collections**: [`list-metric-collections`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list-metric-collections) and\n  [`get-metric-collection`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-get-metric-collection).\n- **Scaffolding**: [`scaffold`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-scaffold)\n- **Contexts**: [`list-contexts`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list-contexts),\n  [`describe-context`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-describe-context),\n  [`set-context`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-set-context),\n  [`use-context`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-use-context), and\n  [`delete-context`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-delete-context).\n\nOn VM installations, the CLI also exposes the\n[`configure-rdi`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-configure-rdi) and\n[`dump-support-package`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-dump-support-package)\nadministration commands.\n\nSee the [`redis-di`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di) page for the\nglobal options that apply to every command."},{"id":"output-formats","title":"Output formats","role":"returns","text":"The `list` and `get` commands print an aligned, column-based table by default. Pass `-o` / `--output`\nwith `json` or `yaml` to emit the underlying data instead, which is useful for scripting and for tools\nsuch as `jq`. The `describe` commands always print a human-readable, sectioned layout."}],"examples":[{"id":"contexts-ex0","language":"yaml","code":"# ~/.redis-di\ncurrent-context: prod\ncontexts:\n  prod:\n    api-url: https://rdi.example.com\n    user: default\n    cacert: /etc/rdi/ingress-ca.crt\n  dev:\n    api-url: https://localhost:8443\n    insecure: true","section_id":"contexts"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/cli/previous-cli-comparison","title":"Compare the previous and current RDI CLI","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/previous-cli-comparison/","summary":"Compare the RDI CLI introduced in version 1.19.0 with the previous CLI","tags":["redis-di"],"last_updated":"2026-09-18T16:41:35+01:00","page_type":"content","content_hash":"72d508327be73c3f88b47a47b859552923a4e3721db4b0647fc2a2d0857d8930","sections":[{"id":"overview","title":"Overview","role":"overview","text":"RDI 1.19.0 replaces the previous Python-based `redis-di` CLI with a Go-based CLI.\nThe executable name stays the same, and several command names are unchanged, but the\ncurrent CLI is not a drop-in replacement for the previous connection settings or contexts.\n\nThe most important difference is what the CLI connects to. The previous CLI connected\ndirectly to the RDI database and the Kubernetes API. The current CLI is a thin client over\nthe RDI REST API."},{"id":"compare-the-clis","title":"Compare the CLIs","role":"content","text":"| Area | Previous CLI (before RDI 1.19.0) | Current CLI (RDI 1.19.0 and later) |\n| :--- | :------------------------------- | :---------------------------------- |\n| Implementation | Python application | Self-contained Go binary |\n| Installation types | VM installations | VM, Kubernetes, and Redis Cloud installations |\n| Connection | RDI database and Kubernetes API | RDI REST API |\n| Pipeline scope | The `default` pipeline | Multiple named pipelines; defaults to `default` |\n| Connection options | `--rdi-host`, `--rdi-port`, `--rdi-user`, `--rdi-password`, and RDI database TLS options | `--api-url`, `--user`, `--password`, and API TLS options; Redis Cloud also supports `--account-key` and `--user-key` |\n| Contexts | A list of RDI database connections in `~/.redis-di`, with an `is_active` field on each entry | A map of API connections in `~/.redis-di`, with one `current-context` |\n| Output | Human-readable tables | Compact tables and sectioned descriptions; `list` and `get` commands also support JSON and YAML |\n| Secrets | `set-secret` on VM installations; `rdi-secret.sh` for Kubernetes | Create, inspect, update, and delete operations through `redis-di` on every installation type |"},{"id":"update-the-connection-options","title":"Update the connection options","role":"content","text":"Replace the RDI database address with the RDI API URL. For example, a previous CLI\ninvocation such as:\n\n[code example]\n\nbecomes:\n\n[code example]\n\nThe current CLI does not accept the previous RDI database connection options on\nAPI-based commands. Update scripts and environment variables as follows:\n\n| Previous setting | Current setting |\n| :--------------- | :-------------- |\n| `--rdi-host` and `--rdi-port` | `--api-url` or `RDI_API_URL` |\n| `--rdi-user` or `RDI_REDIS_USERNAME` | `--user` or `RDI_USER` |\n| `--rdi-password` or `RDI_REDIS_PASSWORD` | `--password` or `RDI_PASSWORD` |\n| `--rdi-cacert` | `--cacert` or `RDI_CACERT` |\n| `--rdi-key`, `--rdi-cert`, `--rdi-key-password` | No equivalent; the CLI authenticates to the API instead of the RDI database |\n| `--rdi-namespace` or `RDI_NAMESPACE` | No equivalent; pipeline operations go through the API |\n\nFor Redis Cloud, use `--account-key` with `--user-key` instead of `--user` with\n`--password`. See the [CLI reference overview](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli#connecting-to-the-api)\nfor all authentication modes."},{"id":"recreate-contexts","title":"Recreate contexts","role":"content","text":"Both CLIs use `~/.redis-di`, but the file formats and stored connection details are\ndifferent. Previous contexts contain RDI database and Kubernetes connection details and\ncannot be used as API contexts.\n\nThe standard VM [`upgrade.sh`](https://redis.io/docs/latest/integrate/redis-data-integration/installation/upgrade#upgrading-a-vm-installation)\nflow preserves the previous context for the VM administration commands and creates a\ndefault API context. If you replace the CLI independently and still have a previous-format\n`~/.redis-di` file, back it up before creating current contexts:\n\n[code example]\n\nThe context commands also changed meaning:\n\n| Task | Previous CLI | Current CLI |\n| :--- | :----------- | :---------- |\n| Create a context | `redis-di add-context <name> [connection options]` | `redis-di set-context <name> [connection options]` |\n| Select the active context | `redis-di set-context <name>` | `redis-di use-context <name>` |\n| Update a context | Recreate the context | `redis-di set-context <name> [options to update]` |\n| Remove all contexts | `redis-di delete-all-contexts` | Delete contexts individually with `redis-di delete-context <name>` |\n\nPasswords and Redis Cloud user keys are not saved in current contexts. Supply them with an\nenvironment variable, a command option, or the interactive prompt."},{"id":"update-commands","title":"Update commands","role":"content","text":"Pipeline lifecycle commands keep their previous names. They now take an optional pipeline\nname that defaults to `default`:\n\n[code example]\n\nOther common tasks changed as follows:\n\n| Task | Previous CLI | Current CLI |\n| :--- | :----------- | :---------- |\n| Inspect pipeline status | `redis-di status` | `redis-di describe [pipeline]`; `status` remains an alias |\n| Continuously refresh status | `redis-di status --live` | `watch -n 1 redis-di describe [pipeline]` |\n| List pipelines | Not available | `redis-di list` |\n| Get a pipeline | Not available | `redis-di get [pipeline]` |\n| Clear a pipeline | Not available | `redis-di deploy --empty` |\n| Inspect rejected records | `redis-di get-rejected [options]` | `redis-di list-dlqs`, then `redis-di list-dlq-records <dlq>`; `get-rejected` remains an alias |\n| Inspect jobs | `redis-di list-jobs` and `redis-di describe-job <job>` | The same commands, with `--pipeline <pipeline>` for a non-default pipeline |\n| Manage secrets | `redis-di set-secret <key> <value>` or `rdi-secret.sh` | `list-secrets`, `get-secret`, `describe-secret`, `set-secret`, and `delete-secret` |\n| Install or upgrade RDI on a VM | `redis-di install` or `redis-di upgrade` | Run `install.sh` or `upgrade.sh` from the VM installation package |\n| Install or upgrade RDI on Kubernetes | Not available | Use the RDI Helm chart |\n| Trace pipeline records | `redis-di trace` | Removed |\n| Create the RDI database | `redis-di create` | Removed; use the installation workflow |\n\nOn VM installations, `configure-rdi` and `dump-support-package` remain available through\n`redis-di`. They are not available with a standalone CLI or on Kubernetes, Redis Cloud,\nmacOS, or Windows.\n\nThe current `describe` output is sourced from the API. It does not include the connected\nRedis client list or Debezium offsets that the previous `status` command read directly.\nThe `--live`, `--page-number`, `--page-size`, and `--ingested-only` status options were\nremoved."},{"id":"update-scripts-that-read-cli-output","title":"Update scripts that read CLI output","role":"content","text":"The current CLI uses a compact, kubectl-style table for list operations and a sectioned,\nhuman-readable layout for `describe` operations. Scripts that parse the previous bordered\ntables must be updated.\n\nFor list and get operations, prefer machine-readable output instead of parsing a table:\n\n[code example]\n\nCommand results are written to standard output and diagnostics are written to standard\nerror, so you can redirect or pipe results without including log messages."},{"id":"verify-the-migration","title":"Verify the migration","role":"content","text":"After updating your contexts and commands:\n\n1. Run `redis-di --version` and verify that it reports version 1.19.0 or later.\n1. Run `redis-di info` to verify the API connection and RDI version.\n1. Run `redis-di list` to verify that the expected pipelines are visible.\n1. Run `redis-di describe [pipeline]` to verify pipeline status and metrics.\n1. Run `redis-di help <command>` to review changed options before updating automation."}],"examples":[{"id":"update-the-connection-options-ex0","language":"bash","code":"redis-di status \\\n  --rdi-host <rdi-database-host> \\\n  --rdi-port <rdi-database-port> \\\n  --rdi-user <user> \\\n  --rdi-password <password>","section_id":"update-the-connection-options"},{"id":"update-the-connection-options-ex1","language":"bash","code":"redis-di describe \\\n  --api-url https://<rdi-api-host> \\\n  --user <user> \\\n  --password <password>","section_id":"update-the-connection-options"},{"id":"recreate-contexts-ex0","language":"bash","code":"mv ~/.redis-di ~/.redis-di.pre-1.19\n\nredis-di set-context <context-name> \\\n  --api-url https://<rdi-api-host> \\\n  --user <user>\nredis-di use-context <context-name>","section_id":"recreate-contexts"},{"id":"update-commands-ex0","language":"bash","code":"redis-di deploy [pipeline] --dir <path>\nredis-di start [pipeline]\nredis-di stop [pipeline]\nredis-di reset [pipeline]","section_id":"update-commands"},{"id":"update-scripts-that-read-cli-output-ex0","language":"bash","code":"redis-di list -o json\nredis-di get <pipeline> -o yaml\nredis-di list-secrets -o json","section_id":"update-scripts-that-read-cli-output"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/cli/redis-di","title":"redis-di","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di/","summary":"Command line tool to manage Redis Data Integration","aliases":["/integrate/redis-data-integration/ingest/reference/cli/redis-di/"],"tags":["redis-di"],"last_updated":"2026-09-18T16:41:35+01:00","page_type":"content","content_hash":"0e5d59013b579150f2db7450bfb24123e54d60f0cdba370c6faf8f45525a60f3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"`redis-di` is the command line tool that manages Redis Data Integration (RDI). It is a thin client\nover the RDI API and works the same way for VM, Kubernetes, and Redis Cloud installations. See the\n[CLI reference overview](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli) for an\nintroduction to connecting, authentication, and contexts."},{"id":"usage","title":"Usage","role":"syntax","text":"[code example]\n\nRun `redis-di help` (or `redis-di --help`) to list every command, and `redis-di help <command>`\n(or `redis-di <command> --help`) to print the usage, flags, and arguments for a single command."},{"id":"global-options","title":"Global options","role":"content","text":"These options apply to every command. Each one can also be set through an `RDI_`-prefixed environment\nvariable, for example `RDI_API_URL`, `RDI_USER`, or `RDI_PASSWORD`. Setting a secret such as the\npassword through an environment variable keeps it out of your shell history.\n\n| Option            | Environment variable | Description                                                                                                     |\n| :---------------- | :------------------- | :-------------------------------------------------------------------------------------------------------------- |\n| `--api-url`       | `RDI_API_URL`        | RDI API base URL.                                                                                               |\n| `--user`          | `RDI_USER`           | User for API (JWT) authentication.                                                                              |\n| `--password`      | `RDI_PASSWORD`       | Password for API (JWT) authentication. Prompted for if a user is set and no password is supplied.               |\n| `--account-key`   | `RDI_ACCOUNT_KEY`    | Redis Cloud account key for API authentication.                                                                 |\n| `--user-key`      | `RDI_USER_KEY`       | Redis Cloud user key for API authentication. Prompted for if an account key is set and no user key is supplied. |\n| `--cacert`        | `RDI_CACERT`         | CA certificate that verifies the API ingress.                                                                   |\n| `--insecure`      | `RDI_INSECURE`       | Skip TLS verification of the API ingress (insecure). Mutually exclusive with `--cacert`.                        |\n| `--context`       | `RDI_CONTEXT`        | Context to use instead of the active one.                                                                       |\n| `--log-level`     | `RDI_LOG_LEVEL`      | Log level: `TRACE`, `DEBUG`, `INFO`, `WARNING`, or `ERROR` (default `INFO`).                                    |\n| `-v`, `--verbose` |                      | Enable verbose logging, equivalent to `--log-level DEBUG`.                                                      |\n| `--version`       |                      | Print the version and build metadata and exit.                                                                  |\n| `-h`, `--help`    |                      | Print help for the CLI or a command.                                                                            |\n\n> [!NOTE]\n> Setting both `--user` and `--account-key` is an error, because they select mutually exclusive\n> authentication modes. Setting both `--cacert` and `--insecure` is also an error."},{"id":"commands","title":"Commands","role":"syntax","text":"| Command                                                                                                                        | Description                                                              |\n| :----------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------- |\n| [`info`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-info)                                       | Displays information about the RDI deployment                            |\n| [`list`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list)                                       | Lists all pipelines                                                      |\n| [`get`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-get)                                         | Gets a pipeline                                                          |\n| [`describe`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-describe)                               | Describes a pipeline with its status (alias `status`)                    |\n| [`deploy`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-deploy)                                   | Deploys a pipeline with the specified configuration (alias `set`)        |\n| [`start`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-start)                                     | Starts a pipeline                                                        |\n| [`stop`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-stop)                                       | Stops a pipeline                                                         |\n| [`reset`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-reset)                                     | Resets a pipeline                                                        |\n| [`list-secrets`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list-secrets)                       | Lists the secrets of a pipeline                                          |\n| [`get-secret`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-get-secret)                           | Gets a secret of a pipeline                                              |\n| [`describe-secret`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-describe-secret)                 | Describes a secret of a pipeline                                         |\n| [`set-secret`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-set-secret)                           | Creates or updates a secret of a pipeline                                |\n| [`delete-secret`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-delete-secret)                     | Deletes a secret of a pipeline                                           |\n| [`list-dlqs`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list-dlqs)                             | Lists the dead-letter queues of a pipeline                               |\n| [`get-dlq`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-get-dlq)                                 | Gets a dead-letter queue of a pipeline                                   |\n| [`list-dlq-records`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list-dlq-records)               | Lists the rejected records of a dead-letter queue (alias `get-rejected`) |\n| [`list-jobs`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list-jobs)                             | Lists the jobs of a pipeline                                             |\n| [`get-job`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-get-job)                                 | Gets a job of a pipeline                                                 |\n| [`describe-job`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-describe-job)                       | Describes a job of a pipeline                                            |\n| [`list-metric-collections`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list-metric-collections) | Lists the metric collections of a pipeline                               |\n| [`get-metric-collection`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-get-metric-collection)     | Gets a metric collection of a pipeline                                   |\n| [`scaffold`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-scaffold)                               | Generates pipeline configuration files                                   |\n| [`list-contexts`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list-contexts)                     | Lists all contexts                                                       |\n| [`describe-context`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-describe-context)               | Describes a context                                                      |\n| [`set-context`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-set-context)                         | Creates or updates a context                                             |\n| [`use-context`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-use-context)                         | Sets a context to be the active one                                      |\n| [`delete-context`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-delete-context)                   | Deletes a context                                                        |\n| [`completion`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-completion)                           | Generates a shell autocompletion script                                  |\n\nOn VM installations, the CLI also exposes the\n[`configure-rdi`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-configure-rdi),\n[`dump-support-package`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-dump-support-package),\nand `admin` administration commands."}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"redis-di [command]","section_id":"usage"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/cli/redis-di-completion","title":"redis-di completion","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-completion/","summary":"Generates a shell autocompletion script","tags":["redis-di"],"last_updated":"2026-07-13T11:31:59+03:00","page_type":"content","content_hash":"b12680c1cfde940d3587eeecfccf0ccce1260185c32dcffd74bb0228928430c7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Generates an autocompletion script for `redis-di` for the specified shell. Supported shells are\n`bash`, `zsh`, `fish`, and `powershell`."},{"id":"usage","title":"Usage","role":"syntax","text":"[code example]\n\nRun `redis-di completion <shell> --help` for the per-shell installation instructions."},{"id":"example","title":"Example","role":"example","text":"To load completions into the current `bash` session:\n\n[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"redis-di completion [bash|zsh|fish|powershell]","section_id":"usage"},{"id":"example-ex0","language":"bash","code":"source <(redis-di completion bash)","section_id":"example"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/cli/redis-di-configure-rdi","title":"redis-di configure-rdi","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-configure-rdi/","summary":"Configures the RDI database connection","tags":["redis-di"],"last_updated":"2026-07-16T09:05:11+01:00","page_type":"content","content_hash":"8cc642ed9c2d22cddffc65216ca8f1e4f56dc2644ad38bf847aec0bf1d0b31ae","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Configures the connection credentials for the RDI database. This is an administration command that is\navailable only on VM installations, where `redis-di` forwards it to the bundled `rdi-admin` tool."},{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"| Option               | Description                                                                              |\n| :------------------- | :--------------------------------------------------------------------------------------- |\n| `-l`, `--log-level`  | Log level: `TRACE`, `DEBUG`, `INFO`, `WARNING`, `ERROR`, or `CRITICAL` (default `INFO`). |\n| `--rdi-namespace`    | RDI Kubernetes namespace (default `rdi`).                                                |\n| `--rdi-host`         | Host or IP of the RDI database (required).                                               |\n| `--rdi-port`         | Port of the RDI database, `1`–`65535` (required).                                        |\n| `--rdi-user`         | RDI database username.                                                                   |\n| `--rdi-password`     | RDI database password.                                                                   |\n| `--rdi-key`          | Private key file to authenticate with.                                                   |\n| `--rdi-cert`         | Client certificate file to authenticate with.                                            |\n| `--rdi-cacert`       | CA certificate file to verify with.                                                      |\n| `--rdi-key-password` | Password for unlocking an encrypted private key.                                         |\n| `--rdi-log-level`    | Log level for the RDI components.                                                        |"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"redis-di configure-rdi [OPTIONS]","section_id":"usage"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/cli/redis-di-delete","title":"redis-di delete","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-delete/","summary":"Deleting a pipeline is not supported","aliases":["/integrate/redis-data-integration/ingest/reference/cli/redis-di-delete/"],"tags":["redis-di"],"last_updated":"2026-09-18T16:41:35+01:00","page_type":"content","content_hash":"c48fb42bc4cfcf88cf144774bc511382a22429d7966c652049d8b2454f39451f","sections":[{"id":"content","title":"Content","role":"content","text":"Deleting a pipeline is currently not supported, and the `redis-di delete` command is no longer\navailable.\n\nTo stop a pipeline and discard its configuration, deploy an empty configuration instead:\n\n[code example]\n\nSee [Clear a pipeline](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/deploy#clear-a-pipeline)\nfor what this removes and what it keeps, and\n[`redis-di deploy`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-deploy)\nfor the command's options."}],"examples":[{"id":"content-ex0","language":"bash","code":"redis-di deploy --empty","section_id":"content"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/cli/redis-di-delete-context","title":"redis-di delete-context","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-delete-context/","summary":"Deletes a context","aliases":["/integrate/redis-data-integration/ingest/reference/cli/redis-di-delete-context/"],"tags":["redis-di"],"last_updated":"2026-09-18T16:41:35+01:00","page_type":"content","content_hash":"b3dacaccfa2ab96f50462ae00f01bc752de0431324421cf7e65304471f12880c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Deletes a context from the `~/.redis-di` context file. Because this is destructive, the command asks\nfor confirmation unless you pass `--force`."},{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"| Option    | Description                   |\n| :-------- | :---------------------------- |\n| `--force` | Skip the confirmation prompt. |\n\nThis command also accepts the\n[global options](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di#global-options)."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"redis-di delete-context <name> [flags]","section_id":"usage"},{"id":"example-ex0","language":"bash","code":"redis-di delete-context dev --force","section_id":"example"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/cli/redis-di-delete-secret","title":"redis-di delete-secret","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-delete-secret/","summary":"Deletes a secret of a pipeline","tags":["redis-di"],"last_updated":"2026-09-18T16:41:35+01:00","page_type":"content","content_hash":"ce2eb01f28a6a8c64c809e4f44532eba9ea16769553374dbed317947c8bd225e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Deletes a secret of a pipeline. Because this is destructive, the command asks for confirmation unless\nyou pass `--force`."},{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"| Option             | Description                                                                       |\n| :----------------- | :-------------------------------------------------------------------------------- |\n| `-p`, `--pipeline` | Pipeline to target (default `default`).                                           |\n| `--db`             | Database the secret belongs to: a source name, or `target`.                       |\n| `--force`          | Skip the confirmation prompt.                                                     |\n| `--wait`           | Wait for the pipeline to reach the expected state (default `true`).               |\n| `--timeout`        | Maximum time to wait for the pipeline to reach the expected state (default `2m`). |\n\nThis command also accepts the\n[global options](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di#global-options)."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"redis-di delete-secret <key> [flags]","section_id":"usage"},{"id":"example-ex0","language":"bash","code":"redis-di delete-secret CACERT --db mysql --force","section_id":"example"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/cli/redis-di-deploy","title":"redis-di deploy","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-deploy/","summary":"Deploys a pipeline with the specified configuration","aliases":["/integrate/redis-data-integration/ingest/reference/cli/redis-di-deploy/"],"tags":["redis-di"],"last_updated":"2026-09-18T16:41:35+01:00","page_type":"content","content_hash":"bfc69ddd5510c702113f6abcd14f18851863ece6c4ea91b0d6c61f761d7aba32","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Deploys a pipeline, creating it or updating it from the configuration in the `--dir` directory. The\nAPI validates the configuration and rejects an invalid one. By default, the command starts the\npipeline after deploying and waits for it to reach the expected state. `set` is an alias for this\ncommand.\n\nPass `--empty` instead of `--dir` to deploy an empty configuration, which clears the pipeline. See\n[Clear a pipeline](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/deploy#clear-a-pipeline) for\nwhat clearing a pipeline removes and what it keeps."},{"id":"usage","title":"Usage","role":"syntax","text":"[code example]\n\nThe pipeline name is an optional argument that defaults to `default`."},{"id":"options","title":"Options","role":"parameters","text":"| Option              | Description                                                                          |\n| :------------------ | :----------------------------------------------------------------------------------- |\n| `--dir`             | Directory containing the pipeline configuration (default `.`).                       |\n| `--empty`           | Deploy an empty configuration instead of loading one from a directory. Cannot be combined with `--dir`. |\n| `--dry-run`         | Validate the configuration without deploying.                                        |\n| `--validate-tables` | Validate the configuration against the source and target databases (default `true`). |\n| `--validate-cdc`    | Validate the CDC configuration of the source databases.                              |\n| `--start`           | Start the pipeline after deploying (default `true`, or `false` with `--empty`).      |\n| `--wait`            | Wait for the pipeline to reach the expected state (default `true`).                  |\n| `--timeout`         | Maximum time to wait for the pipeline to reach the expected state (default `2m`).    |\n\nThis command also accepts the\n[global options](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di#global-options)."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"redis-di deploy [pipeline] [flags]","section_id":"usage"},{"id":"example-ex0","language":"bash","code":"# Deploy the configuration in the current directory\nredis-di deploy\n\n# Validate a configuration folder without deploying it\nredis-di deploy --dir /opt/rdi/config --dry-run\n\n# Clear the pipeline by deploying an empty configuration\nredis-di deploy --empty","section_id":"example"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/cli/redis-di-describe","title":"redis-di describe","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-describe/","summary":"Describes a pipeline with its status","aliases":["/integrate/redis-data-integration/reference/cli/redis-di-status/"],"tags":["redis-di"],"last_updated":"2026-09-18T16:41:35+01:00","page_type":"content","content_hash":"e91d972fd269058a608f9cf14c9c20b7b5938167b8455e3e1b59cef55e9ae0a0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Describes a pipeline, combining its configuration with its runtime status, components, errors, and\nmetrics in a human-readable, sectioned layout. `status` is an alias for this command.\n\nThe RDI version is not shown here, because it is a property of the API connection rather than of the\npipeline; use [`info`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-info)\nto see it."},{"id":"usage","title":"Usage","role":"syntax","text":"[code example]\n\nThe pipeline name is an optional argument that defaults to `default`."},{"id":"options","title":"Options","role":"parameters","text":"This command takes only the\n[global options](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di#global-options)."},{"id":"example","title":"Example","role":"example","text":"[code example]\n\nTo watch the status update live, pair the command with `watch`:\n\n[code example]\n\nThe output has a section for each part of the pipeline. The example below shows a pipeline with\ntwo sources, `mysql` and `postgresql`:\n\n[code example]\n\nThe `Metrics Endpoints` column shows the externally-reachable metrics URLs of each component,\nwhich RDI discovers from the component's `Ingress` resources. It is empty for a component that\nhas no ingress, or while the ingress controller has not assigned an address yet. See\n[Accessing the metrics](https://redis.io/docs/latest/integrate/redis-data-integration/observability#accessing-the-metrics) for more information."}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"redis-di describe [pipeline] [flags]","section_id":"usage"},{"id":"example-ex0","language":"bash","code":"redis-di describe\nredis-di status my-pipeline","section_id":"example"},{"id":"example-ex1","language":"bash","code":"watch -n 1 redis-di describe","section_id":"example"},{"id":"example-ex2","language":"plaintext","code":"Name:     default\nActive:   yes\nStatus:   started\nCurrent:  yes\n\nSources:\n  Name        Type  Db Type     Connection              Sync Mode  Connected\n  ----        ----  -------     ----------              ---------  ---------\n  mysql       cdc   mysql       <mysql-host>:3306       streaming  yes\n  postgresql  cdc   postgresql  <postgresql-host>:5432  streaming  yes\n\nTargets:\n  Name    Db Type  Connection                 Connected\n  ----    -------  ----------                 ---------\n  target  redis    <redis-target-host>:12000  yes\n\nProcessor:\n  Type:  flink\n\nJobs:\n  Name                Server Name  Db / Schema  Table      Transformations  Outputs  Connections\n  ----                -----------  -----------  -----      ---------------  -------  -----------\n  billing_job         postgresql   public       customers  1                1        target\n  customers_hash_job  mysql        inventory    customers  0                1        target\n  orders_job          mysql        inventory    orders     1                1        target\n\nComponents:\n  Name                  Type                Version    Status   Metrics Endpoints\n  ----                  ----                -------    ------   -----------------\n  collector-api         collector-api       <version>  started\n  collector-mysql       debezium-collector  <version>  started  https://<rdi-host>:443/collector-mysql/metrics\n  collector-postgresql  debezium-collector  <version>  started  https://<rdi-host>:443/collector-postgresql/metrics\n  flink-processor       flink-processor     <version>  started  https://<rdi-host>:443/processor/metrics\n\nStatistics:\n  Name                         Incoming  Pending  Inserted  Updated  Deleted  Filtered  Rejected  Last Arrival\n  ----                         --------  -------  --------  -------  -------  --------  --------  ------------\n  mysql.inventory.customers    4         0        4         0        0        0         0         2026-06-18T13:42:44Z\n  mysql.inventory.orders       12        0        12        0        0        0         0         2026-06-18T13:42:51Z\n  postgresql.public.customers  7         0        7         0        0        0         0         2026-06-18T13:42:49Z","section_id":"example"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/cli/redis-di-describe-context","title":"redis-di describe-context","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-describe-context/","summary":"Describes a context","tags":["redis-di"],"last_updated":"2026-09-18T16:41:35+01:00","page_type":"content","content_hash":"c6d3d3b1003572bcc4823586dd06c23e7fac068bc8f31126f71221e49dac1631","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Describes a single context from the `~/.redis-di` context file, showing its API connection details.\nSee the [CLI reference overview](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli#contexts)\nfor more about contexts."},{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"This command takes only the\n[global options](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di#global-options)."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"redis-di describe-context <name> [flags]","section_id":"usage"},{"id":"example-ex0","language":"bash","code":"redis-di describe-context prod","section_id":"example"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/cli/redis-di-describe-job","title":"redis-di describe-job","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-describe-job/","summary":"Describes a job of a pipeline","aliases":["/integrate/redis-data-integration/ingest/reference/cli/redis-di-describe-job/"],"tags":["redis-di"],"last_updated":"2026-09-18T16:41:35+01:00","page_type":"content","content_hash":"277a93f45e6cf4b02d1503578d50717d864dc7b18c4d2eb2051d1795253f47ad","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Describes a single job of a pipeline, printing its source properties followed by tables that\nsummarize its transformations and outputs."},{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"| Option             | Description                             |\n| :----------------- | :-------------------------------------- |\n| `-p`, `--pipeline` | Pipeline to target (default `default`). |\n\nThis command also accepts the\n[global options](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di#global-options)."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"redis-di describe-job <name> [flags]","section_id":"usage"},{"id":"example-ex0","language":"bash","code":"redis-di describe-job customers_hash_job","section_id":"example"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/cli/redis-di-describe-secret","title":"redis-di describe-secret","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-describe-secret/","summary":"Describes a secret of a pipeline","tags":["redis-di"],"last_updated":"2026-09-18T16:41:35+01:00","page_type":"content","content_hash":"95041c5f1f5593154bec622e48667706a10b00fe78eb988d06acfeccb0db96ca","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Describes a single secret of a pipeline. The API never returns secret values, so the output shows\nonly the key and whether it is set, not the stored value."},{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"| Option             | Description                                                 |\n| :----------------- | :---------------------------------------------------------- |\n| `-p`, `--pipeline` | Pipeline to target (default `default`).                     |\n| `--db`             | Database the secret belongs to: a source name, or `target`. |\n\nThis command also accepts the\n[global options](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di#global-options)."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"redis-di describe-secret <key> [flags]","section_id":"usage"},{"id":"example-ex0","language":"bash","code":"redis-di describe-secret PASSWORD --db target","section_id":"example"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/cli/redis-di-dump-support-package","title":"redis-di dump-support-package","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-dump-support-package/","summary":"Dumps the RDI support package","aliases":["/integrate/redis-data-integration/ingest/reference/cli/redis-di-dump-support-package/"],"tags":["redis-di"],"last_updated":"2026-09-18T16:41:35+01:00","page_type":"content","content_hash":"494a7b1f3e79f16e320970a2dd5c9ae91c01e13fc7778578029b610f93689bee","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Dumps a comprehensive set of RDI forensics data that you can send to Redis support (see\n[Dump support package](https://redis.io/docs/latest/integrate/redis-data-integration/troubleshooting#dump-support-package)).\nThis is an administration command that is available only on VM installations, where `redis-di`\nforwards it to the bundled `rdi-admin` tool."},{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"| Option               | Description                                                                              |\n| :------------------- | :--------------------------------------------------------------------------------------- |\n| `-l`, `--log-level`  | Log level: `TRACE`, `DEBUG`, `INFO`, `WARNING`, `ERROR`, or `CRITICAL` (default `INFO`). |\n| `--rdi-namespace`    | RDI Kubernetes namespace (default `rdi`).                                                |\n| `--rdi-host`         | Host or IP of the RDI database (required).                                               |\n| `--rdi-port`         | Port of the RDI database, `1`–`65535` (required).                                        |\n| `--rdi-user`         | RDI database username.                                                                   |\n| `--rdi-password`     | RDI database password.                                                                   |\n| `--rdi-key`          | Private key file to authenticate with.                                                   |\n| `--rdi-cert`         | Client certificate file to authenticate with.                                            |\n| `--rdi-cacert`       | CA certificate file to verify with.                                                      |\n| `--rdi-key-password` | Password for unlocking an encrypted private key.                                         |\n| `--dir`              | Directory where the support file is generated (default `.`).                             |\n| `--dump-rejected`    | Dump rejected records.                                                                   |\n| `--log-days`         | Number of days to look back for log files (default `2`).                                 |"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"redis-di dump-support-package [OPTIONS]","section_id":"usage"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/cli/redis-di-get","title":"redis-di get","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-get/","summary":"Gets a pipeline","tags":["redis-di"],"last_updated":"2026-09-18T16:41:35+01:00","page_type":"content","content_hash":"03fb402c493a0f3f9780522faf03afb3e6c69594ec7652272a4d30a016a44f8a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Gets a single pipeline and prints it in the compact `list` table format. Use\n[`describe`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-describe)\nfor the full pipeline view with its status and metrics."},{"id":"usage","title":"Usage","role":"syntax","text":"[code example]\n\nThe pipeline name is an optional argument that defaults to `default`."},{"id":"options","title":"Options","role":"parameters","text":"| Option           | Description                                          |\n| :--------------- | :--------------------------------------------------- |\n| `-o`, `--output` | Output format: `table` (default), `json`, or `yaml`. |\n\nThis command also accepts the\n[global options](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di#global-options)."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"redis-di get [pipeline] [flags]","section_id":"usage"},{"id":"example-ex0","language":"bash","code":"redis-di get\nredis-di get my-pipeline -o yaml","section_id":"example"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/cli/redis-di-get-dlq","title":"redis-di get-dlq","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-get-dlq/","summary":"Gets a dead-letter queue of a pipeline","tags":["redis-di"],"last_updated":"2026-09-18T16:41:35+01:00","page_type":"content","content_hash":"73ac7b07f34423ba2c9a490dd5c0120a8f19e22cb3e095220cd1da32be27ec11","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Gets a single dead-letter queue (DLQ) of a pipeline and prints it in the compact `list-dlqs` table\nformat.\n\nThe name is the DLQ's source-qualified table name, `<source>.<qualified_table_name>`, as\nreported by [`list-dlqs`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list-dlqs)."},{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"| Option             | Description                                          |\n| :----------------- | :--------------------------------------------------- |\n| `-p`, `--pipeline` | Pipeline to target (default `default`).              |\n| `-o`, `--output`   | Output format: `table` (default), `json`, or `yaml`. |\n\nThis command also accepts the\n[global options](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di#global-options)."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"redis-di get-dlq <name> [flags]","section_id":"usage"},{"id":"example-ex0","language":"bash","code":"redis-di get-dlq mysql.inventory.customers","section_id":"example"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/cli/redis-di-get-job","title":"redis-di get-job","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-get-job/","summary":"Gets a job of a pipeline","tags":["redis-di"],"last_updated":"2026-09-18T16:41:35+01:00","page_type":"content","content_hash":"a995b18798dc7c3dfad0cc0501b019cf91b2f28983ecc6da6206b75c43a9c608","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Gets a single job of a pipeline and prints it in the compact `list-jobs` table format. Use\n[`describe-job`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-describe-job)\nfor the full job view with its transformations and outputs."},{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"| Option             | Description                                          |\n| :----------------- | :--------------------------------------------------- |\n| `-p`, `--pipeline` | Pipeline to target (default `default`).              |\n| `-o`, `--output`   | Output format: `table` (default), `json`, or `yaml`. |\n\nThis command also accepts the\n[global options](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di#global-options)."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"redis-di get-job <name> [flags]","section_id":"usage"},{"id":"example-ex0","language":"bash","code":"redis-di get-job customers_hash_job","section_id":"example"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/cli/redis-di-get-metric-collection","title":"redis-di get-metric-collection","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-get-metric-collection/","summary":"Gets a metric collection of a pipeline","tags":["redis-di"],"last_updated":"2026-09-18T16:41:35+01:00","page_type":"content","content_hash":"aa77a90ed7a9525876ff15d8b04cfecc4134a0d70cf9f7ed200cedd7c89d8ad8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Gets a single metric collection of a pipeline, returning its raw metric data. This command is most\nuseful with `-o json` or `-o yaml` for scripting and for tools such as `jq`. Use\n[`list-metric-collections`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list-metric-collections)\nto see the available collections."},{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"| Option             | Description                                          |\n| :----------------- | :--------------------------------------------------- |\n| `-p`, `--pipeline` | Pipeline to target (default `default`).              |\n| `-o`, `--output`   | Output format: `table` (default), `json`, or `yaml`. |\n\nThis command also accepts the\n[global options](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di#global-options)."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"redis-di get-metric-collection <name> [flags]","section_id":"usage"},{"id":"example-ex0","language":"bash","code":"redis-di get-metric-collection processor_metrics -o json\nredis-di get-metric-collection collector-mysql_metrics -o json","section_id":"example"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/cli/redis-di-get-secret","title":"redis-di get-secret","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-get-secret/","summary":"Gets a secret of a pipeline","tags":["redis-di"],"last_updated":"2026-09-18T16:41:35+01:00","page_type":"content","content_hash":"77822b3b2b4386bf4feca2cf18f3403ca504ff238e339154ef67557cf866ce85","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Gets a single secret of a pipeline and prints it in the compact `list-secrets` table format. The API\nnever returns secret values, so the output shows only the key and whether it is set, not the stored\nvalue."},{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"| Option             | Description                                                 |\n| :----------------- | :---------------------------------------------------------- |\n| `-p`, `--pipeline` | Pipeline to target (default `default`).                     |\n| `--db`             | Database the secret belongs to: a source name, or `target`. |\n| `-o`, `--output`   | Output format: `table` (default), `json`, or `yaml`.        |\n\nThis command also accepts the\n[global options](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di#global-options)."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"redis-di get-secret <key> [flags]","section_id":"usage"},{"id":"example-ex0","language":"bash","code":"redis-di get-secret USERNAME --db mysql","section_id":"example"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/cli/redis-di-info","title":"redis-di info","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-info/","summary":"Displays information about the RDI deployment","tags":["redis-di"],"last_updated":"2026-09-18T16:41:35+01:00","page_type":"content","content_hash":"986a20569392d7fa8ec12f5b90d6052b29452eea57ec241c6c090431015346fe","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Displays information about the RDI deployment, such as the RDI version reported by the API."},{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"This command takes only the\n[global options](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di#global-options)."}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"redis-di info [flags]","section_id":"usage"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/cli/redis-di-list","title":"redis-di list","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list/","summary":"Lists all pipelines","tags":["redis-di"],"last_updated":"2026-09-18T16:41:35+01:00","page_type":"content","content_hash":"8405537e8c8d0ad19bb057609855c6e7903ff98389612f72094e5ec76fc31457","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Lists all pipelines with their status in a compact table."},{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"| Option           | Description                                          |\n| :--------------- | :--------------------------------------------------- |\n| `-o`, `--output` | Output format: `table` (default), `json`, or `yaml`. |\n\nThis command also accepts the\n[global options](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di#global-options)."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"redis-di list [flags]","section_id":"usage"},{"id":"example-ex0","language":"bash","code":"redis-di list\nredis-di list -o json","section_id":"example"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/cli/redis-di-list-contexts","title":"redis-di list-contexts","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list-contexts/","summary":"Lists all contexts","aliases":["/integrate/redis-data-integration/ingest/reference/cli/redis-di-list-contexts/"],"tags":["redis-di"],"last_updated":"2026-09-18T16:41:35+01:00","page_type":"content","content_hash":"1a8ecb279be796f8c2c38f66037729b9c09dc5209f4840e8f3190fdf8c36a9dc","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Lists all contexts from the `~/.redis-di` context file and indicates which one is active. See the\n[CLI reference overview](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli#contexts)\nfor more about contexts."},{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"This command takes only the\n[global options](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di#global-options)."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"redis-di list-contexts [flags]","section_id":"usage"},{"id":"example-ex0","language":"bash","code":"redis-di list-contexts","section_id":"example"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/cli/redis-di-list-dlq-records","title":"redis-di list-dlq-records","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list-dlq-records/","summary":"Lists the rejected records of a dead-letter queue","aliases":["/integrate/redis-data-integration/reference/cli/redis-di-get-rejected/"],"tags":["redis-di"],"last_updated":"2026-09-18T16:41:35+01:00","page_type":"content","content_hash":"3fd16147c88a34692c01a969088e175f0c3946b2a10337c6f9eed295c20e2cc9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Lists the rejected records of a single dead-letter queue (DLQ), taking the queue name as an argument\nand paging with `--limit`, `--offset`, and `--sort-order`. The operation code is shown by name\n(create, update, delete, read). `get-rejected` is an alias for this command.\n\nUse [`list-dlqs`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list-dlqs)\nto see all the pipeline's dead-letter queues and their record counts.\n\nThe DLQ name is its source-qualified table name, `<source>.<qualified_table_name>`, as\nreported by [`list-dlqs`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list-dlqs)."},{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"| Option             | Description                                                         |\n| :----------------- | :------------------------------------------------------------------ |\n| `-p`, `--pipeline` | Pipeline to target (default `default`).                             |\n| `--limit`          | Maximum number of records to return (default `20`).                 |\n| `--offset`         | Number of records to skip (default `0`).                            |\n| `--sort-order`     | Sort order: `asc` (oldest first) or `desc` (newest first, default). |\n| `-o`, `--output`   | Output format: `table` (default), `json`, or `yaml`.                |\n\nThe following options are kept for backward compatibility with the `get-rejected` command and are\ndeprecated:\n\n| Deprecated option | Use instead                       |\n| :---------------- | :-------------------------------- |\n| `--dlq-name`      | Pass the DLQ name as an argument. |\n| `--max-records`   | `--limit`                         |\n| `--oldest`        | `--sort-order asc`                |\n\nThis command also accepts the\n[global options](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di#global-options)."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"redis-di list-dlq-records <dlq-name> [flags]","section_id":"usage"},{"id":"example-ex0","language":"bash","code":"# Newest 20 rejected records of a queue\nredis-di list-dlq-records mysql.inventory.customers\n\n# Oldest 100 records, as JSON\nredis-di list-dlq-records mysql.inventory.customers --limit 100 --sort-order asc -o json","section_id":"example"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/cli/redis-di-list-dlqs","title":"redis-di list-dlqs","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list-dlqs/","summary":"Lists the dead-letter queues of a pipeline","tags":["redis-di"],"last_updated":"2026-09-18T16:41:35+01:00","page_type":"content","content_hash":"558570d282622a3dfd49b2802e77821286525971cc8710b1e0a2677dbeffa957","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Lists the dead-letter queues (DLQs) of a pipeline with their record counts. A DLQ holds the records\nthat RDI rejected. Use\n[`list-dlq-records`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list-dlq-records)\nto read the records of a single queue.\n\nThere is one queue per source table, named `<source>.<qualified_table_name>`, where `<source>`\nis the source name from `config.yaml` and `<qualified_table_name>` is the qualified table name, \nsuch as `public.users` for PostgreSQL or `inventory.dbo.users` for SQL Server. See\n[Rejected records](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/rejected-records#how-rdi-stores-rejected-records)\nfor the form each database uses."},{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"| Option             | Description                                          |\n| :----------------- | :--------------------------------------------------- |\n| `-p`, `--pipeline` | Pipeline to target (default `default`).              |\n| `-o`, `--output`   | Output format: `table` (default), `json`, or `yaml`. |\n\nThis command also accepts the\n[global options](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di#global-options)."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"redis-di list-dlqs [flags]","section_id":"usage"},{"id":"example-ex0","language":"bash","code":"redis-di list-dlqs","section_id":"example"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/cli/redis-di-list-jobs","title":"redis-di list-jobs","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list-jobs/","summary":"Lists the jobs of a pipeline","aliases":["/integrate/redis-data-integration/ingest/reference/cli/redis-di-list-jobs/"],"tags":["redis-di"],"last_updated":"2026-09-18T16:41:35+01:00","page_type":"content","content_hash":"4089e4d20c6273080647d392a07c504c49b14b92603753eec853b403e7cb4105","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Lists the jobs of a pipeline, one row per job with its source, its transformation and output counts,\nand the target connections of its outputs. Use\n[`describe-job`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-describe-job)\nfor the full view of a single job."},{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"| Option             | Description                                          |\n| :----------------- | :--------------------------------------------------- |\n| `-p`, `--pipeline` | Pipeline to target (default `default`).              |\n| `-o`, `--output`   | Output format: `table` (default), `json`, or `yaml`. |\n\nThis command also accepts the\n[global options](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di#global-options)."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"redis-di list-jobs [flags]","section_id":"usage"},{"id":"example-ex0","language":"bash","code":"redis-di list-jobs","section_id":"example"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/cli/redis-di-list-metric-collections","title":"redis-di list-metric-collections","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list-metric-collections/","summary":"Lists the metric collections of a pipeline","tags":["redis-di"],"last_updated":"2026-09-18T16:41:35+01:00","page_type":"content","content_hash":"793f0fe1386251225077b853d9ed759587b8c38ae391012e55f82d5d2428bb67","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Lists the metric collections of a pipeline. Metric collections hold the raw component metrics that\nthe [`describe`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-describe)\ncommand summarizes in its Statistics and Performance sections. This command is most useful with\n`-o json` or `-o yaml` for scripting and for tools such as `jq`."},{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"| Option             | Description                                          |\n| :----------------- | :--------------------------------------------------- |\n| `-p`, `--pipeline` | Pipeline to target (default `default`).              |\n| `-o`, `--output`   | Output format: `table` (default), `json`, or `yaml`. |\n\nThis command also accepts the\n[global options](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di#global-options)."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"redis-di list-metric-collections [flags]","section_id":"usage"},{"id":"example-ex0","language":"bash","code":"redis-di list-metric-collections","section_id":"example"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/cli/redis-di-list-secrets","title":"redis-di list-secrets","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-list-secrets/","summary":"Lists the secrets of a pipeline","tags":["redis-di"],"last_updated":"2026-09-18T16:41:35+01:00","page_type":"content","content_hash":"2ce4ecfa64471f0a7a46cd47b41ce5787f55498e104af24ba74991b5087a1be3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Lists the secrets of a pipeline. The API never returns secret values, so the output shows only the\nsecret keys and whether each one is set, not the stored values."},{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"| Option             | Description                                                         |\n| :----------------- | :------------------------------------------------------------------ |\n| `-p`, `--pipeline` | Pipeline to target (default `default`).                             |\n| `--db`             | List only the secrets of this database: a source name, or `target`. |\n| `-o`, `--output`   | Output format: `table` (default), `json`, or `yaml`.                |\n\nThis command also accepts the\n[global options](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di#global-options)."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"redis-di list-secrets [flags]","section_id":"usage"},{"id":"example-ex0","language":"bash","code":"redis-di list-secrets\nredis-di list-secrets --db mysql\nredis-di list-secrets -p my-pipeline","section_id":"example"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/cli/redis-di-reset","title":"redis-di reset","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-reset/","summary":"Resets a pipeline","aliases":["/integrate/redis-data-integration/ingest/reference/cli/redis-di-reset/"],"tags":["redis-di"],"last_updated":"2026-10-09T11:27:50+03:00","page_type":"content","content_hash":"0e9ce07b4aaf0831d73963919100ec0bc71570e3799deb068a902b11f3787bf8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Clears a pipeline's saved source positions without changing its configured snapshot mode\nor flushing the target Redis database. With the default `initial` mode, the collector\ntakes a new data snapshot and then resumes change data capture (CDC). Other modes can\nskip existing rows or omit ongoing change capture. See\n[Choose a snapshot mode](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config#choose-a-snapshot-mode).\n\nBy default, the command waits for the pipeline to reach a terminal state before returning.\n\nWith `--source`, only the specified source is reset. See\n[Start, stop, and reset a single source](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/multiple-sources#start-stop-and-reset-a-single-source) for more information."},{"id":"usage","title":"Usage","role":"syntax","text":"[code example]\n\nThe pipeline name is an optional argument that defaults to `default`."},{"id":"options","title":"Options","role":"parameters","text":"| Option      | Description                                                                       |\n| :---------- | :-------------------------------------------------------------------------------- |\n| `--source`  | Target only this source instead of the whole pipeline.                            |\n| `--wait`    | Wait for the pipeline to reach the expected state (default `true`).               |\n| `--timeout` | Maximum time to wait for the pipeline to reach the expected state (default `2m`). |\n\nThis command also accepts the\n[global options](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di#global-options)."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"redis-di reset [pipeline] [flags]","section_id":"usage"},{"id":"example-ex0","language":"bash","code":"redis-di reset\n\n# Reset only source mysql\nredis-di reset --source mysql","section_id":"example"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/cli/redis-di-scaffold","title":"redis-di scaffold","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-scaffold/","summary":"Generates pipeline configuration files","aliases":["/integrate/redis-data-integration/ingest/reference/cli/redis-di-scaffold/"],"tags":["redis-di"],"last_updated":"2026-09-18T16:41:35+01:00","page_type":"content","content_hash":"93bbaa24bd951cc02ab1b1b1be937540fe141707857d89067b623826c5aaabc1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Generates a starter pipeline configuration for the given source database type. With `--dir`, the\ncommand writes a `config.yaml` file and a `jobs/job.yaml` file into that directory, prompting\nbefore overwriting existing files unless `--force` is set. Without `--dir`, it prints the\nconfiguration to standard output."},{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"| Option          | Description                                                                                                                     |\n| :-------------- | :------------------------------------------------------------------------------------------------------------------------------ |\n| `--db-type`     | Source database type (required): `mariadb`, `mongodb`, `mysql`, `oracle`, `postgresql`, `snowflake`, `sqlserver`, or `spanner`. |\n| `--db-flavor`   | Source database flavor: `mongodb-atlas`, `mongodb-replica-set`, or `mongodb-sharded-cluster`.                                   |\n| `--source-name` | Name of the generated source; defaults to the database type.                                                                    |\n| `--dir`         | Directory to write the configuration and job files to; prints the configuration to standard output when omitted.                |\n| `--force`       | Skip the confirmation prompt when overwriting an existing file.                                                                 |\n\nThis command also accepts the\n[global options](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di#global-options)."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"redis-di scaffold [flags]","section_id":"usage"},{"id":"example-ex0","language":"bash","code":"# Print a PostgreSQL configuration to stdout\nredis-di scaffold --db-type postgresql\n\n# Write a MySQL configuration into a directory\nredis-di scaffold --db-type mysql --dir /opt/rdi/config\n\n# Name the generated source 'inventory' instead of 'mysql'\nredis-di scaffold --db-type mysql --source-name inventory --dir /opt/rdi/config","section_id":"example"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/cli/redis-di-set-context","title":"redis-di set-context","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-set-context/","summary":"Creates or updates a context","aliases":["/integrate/redis-data-integration/reference/cli/redis-di-add-context/","/integrate/redis-data-integration/reference/cli/redis-di-delete-all-contexts/","/integrate/redis-data-integration/ingest/reference/cli/redis-di-set-context/"],"tags":["redis-di"],"last_updated":"2026-09-18T16:41:35+01:00","page_type":"content","content_hash":"79941a734616475c434f1b65481fffd9373f1cc61e3a78b51e7af59da2c1dd9b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Creates or updates a context in the `~/.redis-di` context file. A context stores an API connection\nso you don't have to pass the connection options on every command. `set-context` merges only the\noptions you give on the command line, preserving the rest, and does not change which context is\nactive; use [`use-context`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-use-context)\nfor that. See the\n[CLI reference overview](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli#contexts)\nfor more about contexts.\n\nSecrets (the password and the Redis Cloud user key) are never stored in a context, so `set-context`\nrejects `--password` and `--user-key`."},{"id":"usage","title":"Usage","role":"syntax","text":"[code example]\n\nThe connection is set from the global options `--api-url`, `--user`, `--account-key`, `--cacert`,\nand `--insecure`."},{"id":"options","title":"Options","role":"parameters","text":"| Option                | Description                                                                                   |\n| :-------------------- | :-------------------------------------------------------------------------------------------- |\n| `--unset-user`        | Clear the stored user, so the context authenticates without a user.                           |\n| `--unset-account-key` | Clear the stored account key, so the context authenticates without a Redis Cloud account key. |\n\nThis command also accepts the\n[global options](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di#global-options)."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"redis-di set-context <name> [flags]","section_id":"usage"},{"id":"example-ex0","language":"bash","code":"# Create or update a context for a VM or Kubernetes installation\nredis-di set-context prod --api-url https://rdi.example.com --user default --cacert /etc/rdi/ingress-ca.crt\n\n# Create or update a context that skips TLS verification\nredis-di set-context dev --api-url https://localhost:8443 --insecure","section_id":"example"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/cli/redis-di-set-secret","title":"redis-di set-secret","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-set-secret/","summary":"Creates or updates a secret of a pipeline","aliases":["/integrate/redis-data-integration/ingest/reference/cli/redis-di-set-secret/"],"tags":["redis-di"],"last_updated":"2026-09-18T16:41:35+01:00","page_type":"content","content_hash":"c0367a8452209c9681c43c7f360fec87b3ebaf68528c2b0fcd7e6c38e560ce74","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Creates or updates a secret of a pipeline. Secrets hold the credentials and certificates that the\npipeline uses to connect to its source and target databases (see\n[Set secrets](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/deploy#set-secrets)\nfor the secret keys). Every secret belongs to one database, which you name with `--db`: a source, or\n`target`. RDI combines the key and the database into the environment variable that `config.yaml`\nreferences, so `PASSWORD` with `--db mysql` becomes `${MYSQL_DB_PASSWORD}`. For a source that\nexisted before RDI supported multiple sources, the variable is still named `SOURCE_DB_PASSWORD`; see\n[Existing names are kept after an upgrade](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/multiple-sources#existing-names-are-kept-after-an-upgrade).\n\nThe secret value comes from the `[value]` argument, the `--file` option, or the `--literal` option.\nIf you provide none of these on an interactive terminal, the command prompts for the value without\nechoing it."},{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"| Option             | Description                                                                       |\n| :----------------- | :-------------------------------------------------------------------------------- |\n| `-p`, `--pipeline` | Pipeline to target (default `default`).                                           |\n| `--db`             | Database the secret belongs to: a source name, or `target`.                       |\n| `--file`           | Read the secret value from the file at this path.                                 |\n| `--literal`        | Use this literal string as the secret value.                                      |\n| `--wait`           | Wait for the pipeline to reach the expected state (default `true`).               |\n| `--timeout`        | Maximum time to wait for the pipeline to reach the expected state (default `2m`). |\n\nThis command also accepts the\n[global options](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di#global-options)."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"redis-di set-secret <key> [value] [flags]","section_id":"usage"},{"id":"example-ex0","language":"bash","code":"# Value from an argument\nredis-di set-secret USERNAME --db mysql myuser\n\n# Value from a file (for example, a certificate)\nredis-di set-secret CACERT --db mysql --file /path/to/myca.crt\n\n# Value read from an interactive prompt\nredis-di set-secret PASSWORD --db mysql\n\n# Target database credentials\nredis-di set-secret PASSWORD --db target mypassword","section_id":"example"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/cli/redis-di-start","title":"redis-di start","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-start/","summary":"Starts a pipeline","aliases":["/integrate/redis-data-integration/ingest/reference/cli/redis-di-start/"],"tags":["redis-di"],"last_updated":"2026-09-18T16:41:35+01:00","page_type":"content","content_hash":"2ee0ced9a54d42a2721eceaf20e38c9d6dbcf2b47114d4b42751c649088bc603","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Starts a pipeline. By default, the command waits for the pipeline to reach the `started` state before\nreturning."},{"id":"usage","title":"Usage","role":"syntax","text":"[code example]\n\nThe pipeline name is an optional argument that defaults to `default`."},{"id":"options","title":"Options","role":"parameters","text":"| Option      | Description                                                                       |\n| :---------- | :-------------------------------------------------------------------------------- |\n| `--source`  | Target only this source instead of the whole pipeline.                            |\n| `--wait`    | Wait for the pipeline to reach the expected state (default `true`).               |\n| `--timeout` | Maximum time to wait for the pipeline to reach the expected state (default `2m`). |\n\nThis command also accepts the\n[global options](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di#global-options)."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"redis-di start [pipeline] [flags]","section_id":"usage"},{"id":"example-ex0","language":"bash","code":"redis-di start\nredis-di start my-pipeline --wait=false\n\n# Start only source mysql. Does not start the pipeline if it's currently stopped.\nredis-di start --source mysql","section_id":"example"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/cli/redis-di-stop","title":"redis-di stop","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-stop/","summary":"Stops a pipeline","aliases":["/integrate/redis-data-integration/ingest/reference/cli/redis-di-stop/"],"tags":["redis-di"],"last_updated":"2026-09-18T16:41:35+01:00","page_type":"content","content_hash":"f1b3345c64e108d0528f171e085b3ea2d122f2c25940f8f5aa7254bab0663792","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Stops a pipeline. By default, the command waits for the pipeline to reach the `stopped` state before\nreturning."},{"id":"usage","title":"Usage","role":"syntax","text":"[code example]\n\nThe pipeline name is an optional argument that defaults to `default`."},{"id":"options","title":"Options","role":"parameters","text":"| Option      | Description                                                                       |\n| :---------- | :-------------------------------------------------------------------------------- |\n| `--source`  | Target only this source instead of the whole pipeline.                            |\n| `--wait`    | Wait for the pipeline to reach the expected state (default `true`).               |\n| `--timeout` | Maximum time to wait for the pipeline to reach the expected state (default `2m`). |\n\nThis command also accepts the\n[global options](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di#global-options)."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"redis-di stop [pipeline] [flags]","section_id":"usage"},{"id":"example-ex0","language":"bash","code":"redis-di stop\n\n# Stop only source mysql\nredis-di stop --source mysql","section_id":"example"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/cli/redis-di-use-context","title":"redis-di use-context","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-use-context/","summary":"Sets a context to be the active one","tags":["redis-di"],"last_updated":"2026-09-18T16:41:35+01:00","page_type":"content","content_hash":"8f4a8a340dd82158e526a92e43d0b3b56c32fa96313a821902f721401b888a38","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Sets a context in the `~/.redis-di` context file to be the active one, so its connection details are\nused by subsequent commands. Create or update a context with\n[`set-context`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-set-context)."},{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"This command takes only the\n[global options](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di#global-options)."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"redis-di use-context <name> [flags]","section_id":"usage"},{"id":"example-ex0","language":"bash","code":"redis-di use-context prod","section_id":"example"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/config-yaml-reference","title":"Redis Data Integration configuration file","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/config-yaml-reference/","summary":"Redis Data Integration configuration file reference","aliases":["/integrate/redis-data-integration/ingest/reference/config-yaml-reference/"],"tags":["redis-di"],"last_updated":"2026-10-09T12:52:02+03:00","page_type":"content","content_hash":"1bdad068ae1eefb24121a72fef1babc78aa371c2a727187827955a95f0a60048","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Configuration file for Redis Data Integration (RDI) source collectors and target connections.\n\n\n**Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|[**sources**](#sources)<br/>(Source collectors)|`object`|Source collectors that capture changes from upstream databases. Each key is a unique source identifier; the value configures one collector.<br/>||\n|[**targets**](#targets)<br/>(Target connections)|`object`|Target Redis databases where processed records are written. Each key is a target identifier; the value configures the connection.<br/>||\n|[**processors**](#processors)<br/>(Data processing configuration)|`object`, `null`|Settings that control how data is processed, including batch sizes, error handling, and performance tuning.<br/>||\n|[**secret\\-providers**](#secret-providers)<br/>(Secret providers)|`object`|External secret providers used to resolve `${...}` references in the configuration.<br/>||\n|[**metadata**](#metadata)<br/>(Pipeline metadata)|`object`|Optional metadata describing this pipeline, such as a display name and description.<br/>||\n\n**Additional Properties:** not allowed  \n<a name=\"sources\"></a>"},{"id":"sources-source-collectors","title":"sources: Source collectors","role":"content","text":"Source collectors that capture changes from upstream databases. Each key is a unique source identifier; the value configures one collector.\n\n\n**Properties** (key: `.*`)\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|[**connection**](#sourcesconnection)<br/>(Source database connection)|`object`|Connection configuration for a non-Redis source database. The exact set of properties depends on the database type.<br/>|no|\n|**name**<br/>(Source name)|`string`|Human-readable name for the source collector. Maximum 100 characters.<br/>Maximal Length: `100`<br/>|no|\n|**type**<br/>(Collector type)|`string`|Type of the source collector. Use `cdc` (default) for change data capture using [Debezium](https://debezium.io/). Use `flink` for Spanner change streams using the Apache Flink-based collector. Use `riotx` for Snowflake CDC using [RIOT-X](https://redis.github.io/riotx/).<br/>Default: `\"cdc\"`<br/>Enum: `\"cdc\"`, `\"flink\"`, `\"riotx\"`<br/>|yes|\n|**active**<br/>(Collector enabled)|`boolean`|When `true`, the collector runs; when `false`, the collector is disabled and produces no events.<br/>Default: `true`<br/>|no|\n|[**logging**](#sourceslogging)<br/>(Logging configuration)|`object`|Logging settings for this source collector.<br/>|no|\n|[**tables**](#sourcestables)<br/>(Tables to capture)|`object`|Tables to capture from the source database, keyed by table name. The value configures column selection and key handling for that table.<br/>|no|\n|[**schemas**](#sourcesschemas)<br/>(Schema names)|`string[]`|Schema names to capture from the source database. Maps to the underlying connector's `schema.include.list`.<br/>|no|\n|[**databases**](#sourcesdatabases)<br/>(Database names)|`string[]`|Database names to capture from the source database. Maps to the underlying connector's `database.include.list`. Applies only to MySQL, MariaDB, and MongoDB connections.<br/>|no|\n|[**advanced**](#sourcesadvanced)<br/>(Advanced configuration)|`object`|Advanced configuration that overrides the underlying engine's defaults. Only required for non-standard tuning.<br/>|no|\n\n\n**Property Name Pattern:** `^[a-z]([a-z0-9-]*[a-z0-9])?$`  \n<a name=\"sourcesconnection\"></a>"},{"id":"sourcesconnection-source-database-connection","title":"sources\\.connection: Source database connection","role":"content","text":"Connection configuration for a non-Redis source database. The exact set of properties depends on the database type.\n\n\n**Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|[**SQL database**](#sourcesconnectionsqldatabase)<br/>(SQL database)|`object`|Connection configuration for a supported SQL database.<br/>||\n|[**MongoDB**](#sourcesconnectionmongodb)|`object`|Connection configuration for a MongoDB database.<br/>|yes|\n|[**Spanner**](#sourcesconnectionspanner)|`object`|Connection configuration for a Google Cloud Spanner database.<br/>|yes|\n|[**Snowflake**](#sourcesconnectionsnowflake)|`object`|Connection configuration for a Snowflake database.<br/>|yes|\n\n**Example**\n\n[code example]\n\n<a name=\"sourcesconnectionsqldatabase\"></a>\n#### sources\\.connection\\.SQL database: SQL database\n\nConnection configuration for a supported SQL database.\n\n\n**Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|**type**<br/>(Database type)|`string`|SQL database engine.<br/>Enum: `\"mariadb\"`, `\"mysql\"`, `\"oracle\"`, `\"postgresql\"`, `\"sqlserver\"`<br/>||\n|**host**<br/>(Database host)|`string`|Hostname or IP address of the SQL database server.<br/>||\n|**port**<br/>(Database port)|`integer`|Network port on which the SQL database server is listening.<br/>Minimum: `1`<br/>Maximum: `65535`<br/>||\n|**database**<br/>(Database name)|`string`|Name of the database to connect to.<br/>Minimal Length: `1`<br/>||\n|**user**<br/>(Database user)|`string`|Username for authentication to the SQL database.<br/>||\n|**password**<br/>(Database password)|`string`|Password for authentication to the SQL database.<br/>||\n\n**Additional Properties:** not allowed  \n**Example**\n\n[code example]\n\n**Example**\n\n[code example]\n\n<a name=\"sourcesconnectionmongodb\"></a>\n#### sources\\.connection\\.MongoDB: MongoDB\n\nConnection configuration for a MongoDB database.\n\n\n**Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|**type**<br/>(Database type)|`string`|Database type identifier. Always `mongodb` for this connection.<br/>Constant Value: `\"mongodb\"`<br/>|yes|\n|**connection\\_string**|`string`|MongoDB connection URI including host, port, and any connection options.<br/>|yes|\n|**user**<br/>(MongoDB user)|`string`|Username for authentication to MongoDB.<br/>|no|\n|**password**<br/>(MongoDB password)|`string`|Password for authentication to MongoDB.<br/>|no|\n|**database**<br/>(MongoDB databases)|`string`|Comma-separated list of MongoDB databases to monitor.<br/>|no|\n\n**Additional Properties:** not allowed  \n**Example**\n\n[code example]\n\n<a name=\"sourcesconnectionspanner\"></a>\n#### sources\\.connection\\.Spanner: Spanner\n\nConnection configuration for a Google Cloud Spanner database.\n\n\n**Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|**type**<br/>(Database type)|`string`|Database type identifier. Always `spanner` for this connection.<br/>Constant Value: `\"spanner\"`<br/>|yes|\n|**project\\_id**<br/>(Spanner project ID)|`string`|Google Cloud project ID that hosts the Spanner instance.<br/>|yes|\n|**instance\\_id**<br/>(Spanner instance ID)|`string`|Spanner instance identifier within the project.<br/>|yes|\n|**database\\_id**<br/>(Spanner database ID)|`string`|Spanner database identifier within the instance.<br/>|yes|\n|**emulator\\_host**<br/>(Spanner emulator host)|`string`|Host and port of the Spanner emulator. Used for local development; leave unset against real Spanner.<br/>|no|\n|**use\\_credentials\\_file**|`boolean`|When `true`, RDI authenticates using a service account credentials file; when `false`, it uses application default credentials.<br/>Default: `false`<br/>|no|\n|[**change\\_streams**](#sourcesconnectionspannerchange_streams)<br/>(Change streams configuration)|`object`|Spanner change streams to capture, keyed by change stream name.<br/>|yes|\n\n**Additional Properties:** not allowed  \n**Example**\n\n[code example]\n\n<a name=\"sourcesconnectionspannerchange_streams\"></a>\n##### sources\\.connection\\.Spanner\\.change\\_streams: Change streams configuration\n\nSpanner change streams to capture, keyed by change stream name.\n\n\n**Additional Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|[**Additional Properties**](#sourcesconnectionspannerchange_streamsadditionalproperties)|`object`, `null`|||\n\n**Minimal Properties:** 1  \n<a name=\"sourcesconnectionspannerchange_streamsadditionalproperties\"></a>\n###### sources\\.connection\\.Spanner\\.change\\_streams\\.additionalProperties: object,null\n\n**Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|**retention\\_period\\_hours**<br/>(Change stream retention period hours)|`integer`, `string`|Retention period for the change stream, in hours.<br/>Pattern: `^\\${.*}$`<br/>Minimum: `1`<br/>||\n\n**Additional Properties:** not allowed  \n<a name=\"sourcesconnectionsnowflake\"></a>\n#### sources\\.connection\\.Snowflake: Snowflake\n\nConnection configuration for a Snowflake database.\n\n\n**Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|**type**<br/>(Database type)|`string`|Database type identifier. Always `snowflake` for this connection.<br/>Constant Value: `\"snowflake\"`<br/>|yes|\n|**url**<br/>(JDBC URL)|`string`|Snowflake JDBC connection URL, for example `jdbc:snowflake://account.snowflakecomputing.com/`.<br/>|yes|\n|**user**<br/>(Snowflake user)|`string`|Username for authentication to Snowflake.<br/>|yes|\n|**password**<br/>(Snowflake password)|`string`|Password for authentication to Snowflake. For key-pair authentication, omit this field and provide the private key via the `<source>-db-ssl` secret (`client.key` field).<br/>|no|\n|**database**<br/>(Snowflake database)|`string`|Name of the Snowflake database to connect to.<br/>|yes|\n|**warehouse**<br/>(Snowflake warehouse)|`string`|Name of the Snowflake warehouse used for compute.<br/>|yes|\n|**role**<br/>(Snowflake role)|`string`|Snowflake role used for the connection.<br/>|no|\n|**cdcDatabase**<br/>(CDC database)|`string`|Database hosting the CDC streams. Defaults to the main `database` if not set.<br/>|no|\n|**cdcSchema**<br/>(CDC schema)|`string`|Schema hosting the CDC streams. Defaults to the main schema if not set.<br/>|no|\n\n**Additional Properties:** not allowed  \n**Example**\n\n[code example]\n\n<a name=\"sourceslogging\"></a>"},{"id":"sourceslogging-logging-configuration","title":"sources\\.logging: Logging configuration","role":"content","text":"Logging settings for this source collector.\n\n\n**Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|**level**<br/>(Logging level)|`string`|Log verbosity for the source collector.<br/>Default: `\"info\"`<br/>Enum: `\"trace\"`, `\"debug\"`, `\"info\"`, `\"warn\"`, `\"error\"`<br/>||\n\n**Additional Properties:** not allowed  \n**Example**\n\n[code example]\n\n<a name=\"sourcestables\"></a>"},{"id":"sourcestables-tables-to-capture","title":"sources\\.tables: Tables to capture","role":"content","text":"Tables to capture from the source database, keyed by table name. The value configures column selection and key handling for that table.\n\n\n**Additional Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|[**Additional Properties**](#sourcestablesadditionalproperties)|`object`, `null`|||\n\n**Minimal Properties:** 1  \n<a name=\"sourcestablesadditionalproperties\"></a>\n#### sources\\.tables\\.additionalProperties: object,null\n\n**Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|**snapshot\\_sql**|`string`|Custom SQL statement used during the initial snapshot, giving fine-grained control over the data captured.<br/>||\n|[**columns**](#sourcestablesadditionalpropertiescolumns)<br/>(Columns to capture)|`string[]`|List of specific columns to capture for changes. If not specified, all columns will be captured. For RIOTX Snowflake sources, this is rendered as per-table `--table-columns` projection. Note: This property cannot be used for MongoDB connections<br/>||\n|[**exclude\\_columns**](#sourcestablesadditionalpropertiesexclude_columns)<br/>(Columns to exclude)|`string[]`|Specific columns to exclude from capture. When omitted, no columns are excluded. Only supported for MongoDB connections.<br/>||\n|[**keys**](#sourcestablesadditionalpropertieskeys)<br/>(Message keys)|`string[]`|Optional list of columns to use as a composite unique identifier. For RIOTX Snowflake sources, this is rendered as per-table `--table-keys`. Only required when the table lacks a primary key or unique constraint. Must form a unique combination of fields<br/>||\n\n**Additional Properties:** not allowed  \n<a name=\"sourcestablesadditionalpropertiescolumns\"></a>\n##### sources\\.tables\\.additionalProperties\\.columns\\[\\]: Columns to capture\n\nList of specific columns to capture for changes. If not specified, all columns will be captured. For RIOTX Snowflake sources, this is rendered as per-table `--table-columns` projection. Note: This property cannot be used for MongoDB connections\n\n\n<a name=\"sourcestablesadditionalpropertiesexclude_columns\"></a>\n##### sources\\.tables\\.additionalProperties\\.exclude\\_columns\\[\\]: Columns to exclude\n\nSpecific columns to exclude from capture. When omitted, no columns are excluded. Only supported for MongoDB connections.\n\n\n<a name=\"sourcestablesadditionalpropertieskeys\"></a>\n##### sources\\.tables\\.additionalProperties\\.keys\\[\\]: Message keys\n\nOptional list of columns to use as a composite unique identifier. For RIOTX Snowflake sources, this is rendered as per-table `--table-keys`. Only required when the table lacks a primary key or unique constraint. Must form a unique combination of fields\n\n\n<a name=\"sourcesschemas\"></a>"},{"id":"sourcesschemas-schema-names","title":"sources\\.schemas\\[\\]: Schema names","role":"content","text":"Schema names to capture from the source database. Maps to the underlying connector's `schema.include.list`.\n\n\n<a name=\"sourcesdatabases\"></a>"},{"id":"sourcesdatabases-database-names","title":"sources\\.databases\\[\\]: Database names","role":"content","text":"Database names to capture from the source database. Maps to the underlying connector's `database.include.list`. Applies only to MySQL, MariaDB, and MongoDB connections.\n\n\n<a name=\"sourcesadvanced\"></a>"},{"id":"sourcesadvanced-advanced-configuration","title":"sources\\.advanced: Advanced configuration","role":"content","text":"Advanced configuration that overrides the underlying engine's defaults. Only required for non-standard tuning.\n\n\n**Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|[**sink**](#sourcesadvancedsink)<br/>(RDI Collector stream writer configuration)|`object`|Advanced configuration properties for the RDI Collector stream writer connection and behaviour. **Applies to the `cdc` and `flink` collector types.**<br/>||\n|[**source**](#sourcesadvancedsource)<br/>(Advanced source settings)|`object`|Advanced configuration properties for the source database connection and CDC behavior. **Applies to the `cdc` and `flink` collector types.**<br/>||\n|[**quarkus**](#sourcesadvancedquarkus)<br/>(Quarkus runtime settings)|`object`|Advanced configuration properties for the Quarkus runtime that hosts Debezium Server. **Only applies to the `cdc` collector type.** See the [Debezium Server documentation](https://debezium.io/documentation/reference/stable/operations/debezium-server.html) for runtime configuration options. When using a property from that page, omit the `quarkus.` prefix.<br/>||\n|[**flink**](#sourcesadvancedflink)<br/>(Advanced Flink settings)|`object`|Advanced configuration properties forwarded to the Flink runtime that hosts the collector. Any property listed in the [Flink configuration documentation](https://nightlies.apache.org/flink/flink-docs-release-2.0/docs/deployment/config/) can be set here and will override the RDI default. **Only applies to the `flink` collector type.**<br/>||\n|[**resources**](#sourcesadvancedresources)<br/>(Collector resource settings)|`object`|Compute resources allocated to the collector. **Only applies to the `cdc` collector type.**<br/>||\n|[**riotx**](#sourcesadvancedriotx)<br/>(Advanced RIOT\\-X settings)|`object`|Advanced configuration properties for the RIOT-X Snowflake collector. **Only applies to the `riotx` collector type.**<br/>||\n|**java\\_options**<br/>(Advanced Java options)|`string`|These Java options will be passed to the command line command when launching the source collector. **Only applies to the `cdc` collector type.**<br/>||\n|[**diagnostics**](#sourcesadvanceddiagnostics)<br/>(Collector diagnostics)|`object`|Opt-in JVM diagnostics for the source collector. Each artifact is written to the `/opt/rdi/diagnostics` directory on the host and included in the `dump-support-package` bundle. **Only applies to the `cdc` collector type on VM installations, and is ignored otherwise.**<br/>||\n\n**Additional Properties:** not allowed  \n**Minimal Properties:** 1  \n**Example**\n\n[code example]\n\n<a name=\"sourcesadvancedsink\"></a>\n#### sources\\.advanced\\.sink: RDI Collector stream writer configuration\n\nAdvanced configuration properties for the RDI Collector stream writer connection and behaviour. **Applies to the `cdc` and `flink` collector types.**<br/><br/>For the `cdc` collector type, see the full list of properties at [Debezium Server — Redis Stream sink](https://debezium.io/documentation/reference/stable/operations/debezium-server.html#_redis_stream). When using a property from that page, omit the `debezium.sink.` prefix.<br/><br/>**The properties listed below only apply to the `flink` collector type.**\n\n\n**Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|**redis\\.batch\\.size**<br/>(Sink batch size)|`integer`|Maximum number of records the collector sink writes to Redis in a single batch.<br/>Default: `1000`<br/>Minimum: `1`<br/>||\n|**redis\\.flush\\.interval\\.ms**<br/>(Sink flush interval)|`integer`|Maximum time in milliseconds the collector sink waits to fill a batch before flushing it to Redis.<br/>Default: `100`<br/>Minimum: `1`<br/>||\n|**redis\\.connection\\.timeout\\.ms**<br/>(Sink connection timeout)|`integer`|Connection timeout in milliseconds for the target Redis client used by the collector sink.<br/>Default: `2000`<br/>Minimum: `1`<br/>||\n|**redis\\.socket\\.timeout\\.ms**<br/>(Sink socket timeout)|`integer`|Socket read/write timeout in milliseconds for the target Redis client used by the collector sink.<br/>Default: `2000`<br/>Minimum: `1`<br/>||\n|**redis\\.retry\\.max\\.attempts**<br/>(Sink retry max attempts)|`integer`|Maximum number of retry attempts for failed Redis operations.<br/>Default: `5`<br/>Minimum: `1`<br/>||\n|**redis\\.retry\\.initial\\.delay\\.ms**<br/>(Sink retry initial delay)|`integer`|Initial delay in milliseconds before the first retry of a failed Redis operation.<br/>Default: `100`<br/>Minimum: `1`<br/>||\n|**redis\\.retry\\.max\\.delay\\.ms**<br/>(Sink retry max delay)|`integer`|Maximum delay in milliseconds between retry attempts for failed Redis operations.<br/>Default: `3000`<br/>Minimum: `1`<br/>||\n|**redis\\.retry\\.backoff\\.multiplier**<br/>(Sink retry backoff multiplier)|`number`|Exponential backoff multiplier between retry attempts for failed Redis operations.<br/>Default: `2`<br/>Minimum: `1`<br/>||\n|**redis\\.oom\\.retry\\.initial\\.delay\\.ms**<br/>(Sink OOM retry initial delay)|`integer`|Initial delay in milliseconds before the first retry after a Redis out-of-memory error.<br/>Default: `1000`<br/>Minimum: `1`<br/>||\n|**redis\\.oom\\.retry\\.max\\.delay\\.ms**<br/>(Sink OOM retry max delay)|`integer`|Maximum delay in milliseconds between retry attempts after a Redis out-of-memory error.<br/>Default: `10000`<br/>Minimum: `1`<br/>||\n|**redis\\.oom\\.retry\\.backoff\\.multiplier**<br/>(Sink OOM retry backoff multiplier)|`number`|Exponential backoff multiplier between retry attempts after a Redis out-of-memory error.<br/>Default: `2`<br/>Minimum: `1`<br/>||\n|**redis\\.wait\\.enabled**<br/>(Sink replica wait enabled)|`boolean`|When `true`, the collector verifies that each write has been replicated to the configured number of Redis replica shards before acknowledging it.<br/>Default: `false`<br/>||\n|**redis\\.wait\\.timeout\\.ms**<br/>(Sink replica wait timeout)|`integer`|Maximum time in milliseconds to wait for replica write acknowledgements.<br/>Default: `1000`<br/>Minimum: `1`<br/>||\n|**redis\\.wait\\.write\\.timeout\\.ms**<br/>(Sink replica wait timeout)|`integer`|Maximum time in milliseconds to wait for replica write acknowledgements.<br/>Default: `1000`<br/>Minimum: `1`<br/>||\n|**redis\\.wait\\.retry\\.enabled**<br/>(Sink replica wait retry enabled)|`boolean`|When `true`, the collector keeps retrying a write until replica acknowledgement succeeds; when `false`, it gives up after the first failure.<br/>Default: `false`<br/>||\n|**redis\\.wait\\.retry\\.delay\\.ms**<br/>(Sink replica wait retry delay)|`integer`|Delay in milliseconds between replica wait retry attempts.<br/>Default: `1000`<br/>Minimum: `1`<br/>||\n\n**Additional Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|**Additional Properties**|`string`, `number`, `boolean`|||\n\n**Minimal Properties:** 1  \n**Example**\n\n[code example]\n\n<a name=\"sourcesadvancedsource\"></a>\n#### sources\\.advanced\\.source: Advanced source settings\n\nAdvanced configuration properties for the source database connection and CDC behavior. **Applies to the `cdc` and `flink` collector types.**<br/><br/>For the `cdc` collector type, available properties depend on the source database — refer to the relevant Debezium connector documentation: [MySQL](https://debezium.io/documentation/reference/stable/connectors/mysql.html), [MariaDB](https://debezium.io/documentation/reference/stable/connectors/mariadb.html), [PostgreSQL](https://debezium.io/documentation/reference/stable/connectors/postgresql.html), [Oracle](https://debezium.io/documentation/reference/stable/connectors/oracle.html), [SQL Server](https://debezium.io/documentation/reference/stable/connectors/sqlserver.html), [Db2](https://debezium.io/documentation/reference/stable/connectors/db2.html), [MongoDB](https://debezium.io/documentation/reference/stable/connectors/mongodb.html). When using a property from those pages, omit the `debezium.source.` prefix.<br/><br/>**The named properties below cover the most commonly tuned settings: `spanner.*` properties apply to the `flink` collector type, all others apply to the `cdc` collector type. Any other property from the Debezium documentation can still be set as a free-form key-value pair, except for `topic.prefix`, which RDI derives from the source name, and the keystore properties, which RDI configures from a source's TLS secret.**\n\n\n**Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|**record\\.processing\\.threads**<br/>(Record processing threads)|`integer`|Controls how many worker threads process captured records before they are written downstream.<br/>Minimum: `1`<br/>||\n|**snapshot\\.mode**<br/>(Snapshot mode)|`string`|Determines when a Debezium collector snapshots selected source data. Available modes and restart behavior depend on the source connector. A reset clears the saved offset without changing this mode or flushing the target. A new snapshot can leave stale target records after missed deletes. See [Choose a snapshot mode](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config#choose-a-snapshot-mode) for the mode comparison.<br/>Default: `\"initial\"`<br/>Enum: `\"always\"`, `\"initial\"`, `\"initial_only\"`, `\"never\"`, `\"no_data\"`, `\"when_needed\"`<br/>||\n|**snapshot\\.max\\.threads**<br/>(Snapshot max threads)|`integer`|Sets the maximum number of threads used while taking the initial snapshot.<br/>Default: `1`<br/>Minimum: `1`<br/>||\n|**poll\\.interval\\.ms**<br/>(Poll interval ms)|`integer`|Defines how often the collector polls the source for new changes.<br/>Default: `500`<br/>Minimum: `1`<br/>||\n|**snapshot\\.fetch\\.size**<br/>(Snapshot fetch size)|`integer`|Defines how many rows are fetched per batch during the initial snapshot.<br/>Minimum: `0`<br/>||\n|**max\\.batch\\.size**<br/>(Max batch size)|`integer`|Caps how many records are processed together in a single batch.<br/>Default: `2048`<br/>Minimum: `1`<br/>||\n|**max\\.queue\\.size**<br/>(Max queue size)|`integer`|Limits how many records can be buffered in memory before processing catches up.<br/>Default: `8192`<br/>Minimum: `1`<br/>||\n|**heartbeat\\.interval\\.ms**<br/>(Heartbeat interval ms)|`integer`|Sets how often heartbeat events are emitted to keep change tracking active. Use 0 to disable them.<br/>Default: `0`<br/>Minimum: `0`<br/>||\n|**heartbeat\\.action\\.query**<br/>(Heartbeat action query)|`string`|SQL query executed on the source whenever a heartbeat is emitted.<br/>||\n|**database\\.trustServerCertificate**<br/>(Trust SQL Server certificate)|`boolean`|Controls whether the RDI collector skips validation of the SQL Server TLS certificate. Enable this only when the server certificate cannot be validated through a trusted certificate authority.<br/>Default: `false`<br/>||\n|**driver\\.applicationIntent**<br/>(SQL Server application intent)|`string`|Declares the SQL Server workload type for the RDI collector. Set to `ReadOnly` when connecting to an Always On read-only replica. CDC must be enabled on the primary SQL Server node.<br/>Default: `\"ReadWrite\"`<br/>Enum: `\"ReadOnly\"`, `\"ReadWrite\"`<br/>||\n|**lob\\.enabled**<br/>(Lob enabled)|`boolean`|Determines whether large object columns are included in change capture.<br/>Default: `false`<br/>||\n|**gtid\\.source\\.includes**<br/>(GTID source includes)|`string`|Restricts MySQL GTID processing to the listed source UUIDs.<br/>||\n|**publication\\.autocreate\\.mode**<br/>(Publication autocreate mode)|`string`|Controls whether and how the PostgreSQL publication is created or updated automatically.<br/>Default: `\"all_tables\"`<br/>Enum: `\"all_tables\"`, `\"filtered\"`, `\"disabled\"`<br/>||\n|**publication\\.name**<br/>(Publication name)|`string`|Sets the PostgreSQL logical replication publication name used by the collector.<br/>Default: `\"dbz_publication\"`<br/>||\n|**slot\\.name**<br/>(Slot name)|`string`|Sets the PostgreSQL replication slot name the collector reads from.<br/>Default: `\"debezium\"`<br/>||\n|**capture\\.mode**<br/>(MongoDB capture mode)|`string`|Determines whether an update event carries the full document and its pre-image. Jobs that set an output key, and the set output types, need a `*_with_pre_image` mode and pre-images on the collections.<br/>Default: `\"change_streams_update_full\"`<br/>Enum: `\"change_streams\"`, `\"change_streams_update_full\"`, `\"change_streams_update_full_with_pre_image\"`, `\"change_streams_with_pre_image\"`<br/>||\n|**capture\\.mode\\.full\\.update\\.type**<br/>(MongoDB full update type)|`string`|Determines how the collector fetches the full document for an update event. `lookup` queries the source per event and can yield a null `after`; `post_image` reads the post-image and needs MongoDB 6.0.<br/>Default: `\"lookup\"`<br/>Enum: `\"lookup\"`, `\"post_image\"`<br/>||\n|**cursor\\.oversize\\.handling\\.mode**<br/>(MongoDB oversize event handling)|`string`|Determines what happens to a change event above the MongoDB 16 MB limit: `fail` stops the collector, `skip` drops it, `split` reassembles it. The limit covers the document and its pre-image.<br/>Default: `\"fail\"`<br/>Enum: `\"fail\"`, `\"skip\"`, `\"split\"`<br/>||\n|**cursor\\.oversize\\.skip\\.threshold**<br/>(MongoDB oversize skip threshold)|`integer`|Maximum size in bytes of the document, and of its pre-image, for which change events are processed. Only applies when `cursor.oversize.handling.mode` is `skip`, which requires a value above 0.<br/>Default: `0`<br/>Minimum: `0`<br/>||\n|**mongodb\\.authsource**<br/>(MongoDB authentication database)|`string`|Database that holds the credentials the collector authenticates with. Only applies to the connection's `user` and `password`; an `authSource` in `connection_string` takes precedence.<br/>Default: `\"admin\"`<br/>||\n|**mongodb\\.ssl\\.invalid\\.hostname\\.allowed**<br/>(Allow invalid MongoDB hostname)|`boolean`|Controls whether the RDI collector skips hostname verification of the MongoDB TLS certificate. Enable this only when the certificate cannot cover the hostnames the replica set advertises.<br/>Default: `false`<br/>||\n|**mongodb\\.connect\\.timeout\\.ms**<br/>(MongoDB connect timeout)|`integer`|Time in milliseconds the driver waits before it aborts a new connection attempt. The `connectTimeoutMS` option of `connection_string` takes precedence.<br/>Default: `10000`<br/>Minimum: `0`<br/>||\n|**mongodb\\.socket\\.timeout\\.ms**<br/>(MongoDB socket timeout)|`integer`|Time in milliseconds a send or receive on the socket may take before it times out, where 0 disables the timeout. The `socketTimeoutMS` option of `connection_string` takes precedence.<br/>Default: `0`<br/>Minimum: `0`<br/>||\n|**mongodb\\.server\\.selection\\.timeout\\.ms**<br/>(MongoDB server selection timeout)|`integer`|Time in milliseconds the driver waits to select a server before it fails with an error. The `serverSelectionTimeoutMS` option of `connection_string` takes precedence.<br/>Default: `30000`<br/>Minimum: `0`<br/>||\n|**mongodb\\.heartbeat\\.frequency\\.ms**<br/>(MongoDB heartbeat frequency)|`integer`|Interval in milliseconds at which the cluster monitor reaches each MongoDB server. The `heartbeatFrequencyMS` option of `connection_string` takes precedence.<br/>Default: `10000`<br/>Minimum: `0`<br/>||\n|**spanner\\.version\\.retention\\.period\\.hours**<br/>(Spanner version retention period)|`integer`|Retention period in hours for Spanner change stream versions. Determines how far back the collector can resume after an outage.<br/>Default: `1`<br/>Minimum: `1`<br/>||\n|**spanner\\.fetch\\.timeout\\.ms**<br/>(Spanner fetch timeout)|`integer`|Timeout in milliseconds for a single change stream fetch request to Spanner.<br/>Default: `500`<br/>Minimum: `1`<br/>||\n|**spanner\\.fetch\\.heartbeat\\.ms**<br/>(Spanner fetch heartbeat interval)|`integer`|Interval in milliseconds at which Spanner sends heartbeat records when no data changes are available.<br/>Default: `100`<br/>Minimum: `1`<br/>||\n|**spanner\\.max\\.rows\\.per\\.partition**<br/>(Spanner max rows per partition)|`integer`|Maximum number of rows the collector reads from a single Spanner change stream partition before yielding.<br/>Default: `10000`<br/>Minimum: `1`<br/>||\n|**spanner\\.dialect**<br/>(Spanner SQL dialect)|`string`|SQL dialect of the Spanner database. Use `GOOGLESQL` for Google Standard SQL or `POSTGRESQL` for the PostgreSQL interface.<br/>Default: `\"GOOGLESQL\"`<br/>Enum: `\"GOOGLESQL\"`, `\"POSTGRESQL\"`<br/>||\n\n**Additional Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|**Additional Properties**|`string`, `number`, `boolean`|||\n\n**Minimal Properties:** 1  \n**Example**\n\n[code example]\n\n<a name=\"sourcesadvancedquarkus\"></a>\n#### sources\\.advanced\\.quarkus: Quarkus runtime settings\n\nAdvanced configuration properties for the Quarkus runtime that hosts Debezium Server. **Only applies to the `cdc` collector type.** See the [Debezium Server documentation](https://debezium.io/documentation/reference/stable/operations/debezium-server.html) for runtime configuration options. When using a property from that page, omit the `quarkus.` prefix.\n\n\n**Additional Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|**Additional Properties**|`string`, `number`, `boolean`|||\n\n**Minimal Properties:** 1  \n<a name=\"sourcesadvancedflink\"></a>\n#### sources\\.advanced\\.flink: Advanced Flink settings\n\nAdvanced configuration properties forwarded to the Flink runtime that hosts the collector. Any property listed in the [Flink configuration documentation](https://nightlies.apache.org/flink/flink-docs-release-2.0/docs/deployment/config/) can be set here and will override the RDI default. **Only applies to the `flink` collector type.**<br/><br/>The properties listed below are the ones most likely to require adjustment. **Changing any other Flink property is not recommended unless instructed by Redis support.**\n\n\n**Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|**parallelism\\.default**<br/>(Default parallelism)|`integer`|Default parallelism for Flink jobs and operators. When unset, Flink uses the number of available task slots across all task managers (`taskManager.replicas × taskmanager.numberOfTaskSlots`). See [parallel execution](https://nightlies.apache.org/flink/flink-docs-release-2.0/docs/dev/datastream/execution/parallel/).<br/>Minimum: `1`<br/>||\n|**taskmanager\\.numberOfTaskSlots**<br/>(Task slots per task manager)|`integer`|Number of parallel task slots per task manager pod. Each slot can run one parallel pipeline instance, so this caps the parallelism a single task manager can absorb. See [task slots and resources](https://nightlies.apache.org/flink/flink-docs-release-2.0/docs/concepts/flink-architecture/#task-slots-and-resources).<br/>Default: `1`<br/>Minimum: `1`<br/>||\n|**taskmanager\\.memory\\.process\\.size**<br/>(Task manager process memory)|`string`|Total memory budget for each task manager JVM process, expressed with a unit suffix such as `2048m` or `4g`. See [task manager memory configuration](https://nightlies.apache.org/flink/flink-docs-release-2.0/docs/deployment/memory/mem_setup_tm/).<br/>||\n\n**Additional Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|**Additional Properties**|`string`, `number`, `boolean`|||\n\n**Minimal Properties:** 1  \n**Example**\n\n[code example]\n\n<a name=\"sourcesadvancedresources\"></a>\n#### sources\\.advanced\\.resources: Collector resource settings\n\nCompute resources allocated to the collector. **Only applies to the `cdc` collector type.**\n\n\n**Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|**cpu**<br/>(CPU resource value)|`string`|CPU request for the collector container, for example `1` or `500m`.<br/>||\n|**memory**<br/>(Memory resource value)|`string`|Memory request for the collector container, for example `1024Mi` or `2Gi`.<br/>||\n\n**Additional Properties:** not allowed  \n**Minimal Properties:** 1  \n<a name=\"sourcesadvancedriotx\"></a>\n#### sources\\.advanced\\.riotx: Advanced RIOT\\-X settings\n\nAdvanced configuration properties for the RIOT-X Snowflake collector. **Only applies to the `riotx` collector type.**\n\n\n**Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|**poll**<br/>(Polling interval)|`string`|Interval between polls for new stream changes, for example `30s` or `PT30S`.<br/>Default: `\"30s\"`<br/>||\n|**snapshot**<br/>(Snapshot mode)|`string`|Initial-load behavior. `INITIAL` performs a one-time snapshot before streaming; `NEVER` skips the snapshot.<br/>Default: `\"INITIAL\"`<br/>Enum: `\"INITIAL\"`, `\"NEVER\"`<br/>||\n|**streamPrefix**<br/>(Redis stream key prefix)|`string`|Prefix used when constructing Redis stream keys, for example `data:`.<br/>Default: `\"data:\"`<br/>||\n|**streamLimit**<br/>(Maximum stream length)|`integer`|Maximum number of entries kept in each Redis stream before older entries are trimmed.<br/>Minimum: `1`<br/>||\n|[**keyColumns**](#sourcesadvancedriotxkeycolumns)<br/>(Key columns)|`string[]`|Deprecated RIOTX global fallback list of columns to use as message keys for every captured table. Prefer `tables.<schema.table>.keys`<br/>||\n|**clearOffset**<br/>(Clear existing offset)|`boolean`|When `true`, the stored offset is cleared on startup, forcing a fresh read.<br/>Default: `false`<br/>||\n|**count**<br/>(Record count limit)|`integer`|Maximum number of records to process. Set to `0` for unlimited.<br/>Default: `0`<br/>Minimum: `0`<br/>||\n\n**Additional Properties:** not allowed  \n**Minimal Properties:** 1  \n**Example**\n\n[code example]\n\n<a name=\"sourcesadvancedriotxkeycolumns\"></a>\n##### sources\\.advanced\\.riotx\\.keyColumns\\[\\]: Key columns\n\nDeprecated RIOTX global fallback list of columns to use as message keys for every captured table. Prefer `tables.<schema.table>.keys`\n\n\n<a name=\"sourcesadvanceddiagnostics\"></a>\n#### sources\\.advanced\\.diagnostics: Collector diagnostics\n\nOpt-in JVM diagnostics for the source collector. Each artifact is written to the `/opt/rdi/diagnostics` directory on the host and included in the `dump-support-package` bundle. **Only applies to the `cdc` collector type on VM installations, and is ignored otherwise.**\n\n\n**Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|[**heap\\_dump**](#sourcesadvanceddiagnosticsheap_dump)<br/>(Heap dump on out\\-of\\-memory)|`object`|Writes a heap dump if the JVM runs out of memory, then exits so the collector restarts cleanly.<br/>||\n|[**java\\_flight\\_recorder**](#sourcesadvanceddiagnosticsjava_flight_recorder)<br/>(Java Flight Recorder recording)|`object`|Records a Java Flight Recorder profile of the collector at startup. RDI controls `filename` and `dumponexit`, and defaults `settings=profile`, `duration=5m`, and `maxsize=128m` unless set under `options`.<br/>||\n\n**Additional Properties:** not allowed  \n**Minimal Properties:** 1  \n**Example**\n\n[code example]\n\n<a name=\"sourcesadvanceddiagnosticsheap_dump\"></a>\n##### sources\\.advanced\\.diagnostics\\.heap\\_dump: Heap dump on out\\-of\\-memory\n\nWrites a heap dump if the JVM runs out of memory, then exits so the collector restarts cleanly.\n\n\n**Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|**enabled**<br/>(Enable heap dump)|`boolean`|When `true`, the collector writes a heap dump if the JVM runs out of memory, then exits so it restarts cleanly.<br/>Default: `false`<br/>||\n\n**Additional Properties:** not allowed  \n**Minimal Properties:** 1  \n**Example**\n\n[code example]\n\n<a name=\"sourcesadvanceddiagnosticsjava_flight_recorder\"></a>\n##### sources\\.advanced\\.diagnostics\\.java\\_flight\\_recorder: Java Flight Recorder recording\n\nRecords a Java Flight Recorder profile of the collector at startup. RDI controls `filename` and `dumponexit`, and defaults `settings=profile`, `duration=5m`, and `maxsize=128m` unless set under `options`.\n\n\n**Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|**enabled**<br/>(Enable flight recording)|`boolean`|When `true`, starts a Java Flight Recorder recording when the collector starts.<br/>Default: `false`<br/>||\n|**options**<br/>(Flight Recorder options)|`string`|Comma-separated `-XX:StartFlightRecording` options, for example `duration=10m,maxsize=256m,path-to-gc-roots=true`.<br/>||\n\n**Additional Properties:** not allowed  \n**Minimal Properties:** 1  \n**Example**\n\n[code example]\n\n<a name=\"targets\"></a>"},{"id":"targets-target-connections","title":"targets: Target connections","role":"content","text":"Target Redis databases where processed records are written. Each key is a target identifier; the value configures the connection.\n\n\n**Properties** (key: `.*`)\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|[**connection**](#targetsconnection)<br/>(Database connection)|`object`|Connection configuration for a Redis database.<br/>|yes|\n|**name**<br/>(Target name)|`string`|Human-readable name for the target connection. Maximum 100 characters.<br/>Maximal Length: `100`<br/>|no|\n\n\n<a name=\"targetsconnection\"></a>"},{"id":"targetsconnection-database-connection","title":"targets\\.connection: Database connection","role":"content","text":"Connection configuration for a Redis database.\n\n\n**Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|**type**<br/>(Database type)||Database type identifier. Always `redis` for this connection.<br/>Constant Value: `\"redis\"`<br/>|yes|\n|**host**<br/>(Database host)|`string`|Hostname or IP address of the Redis server.<br/>|yes|\n|**port**<br/>(Database port)|`integer`|Network port on which the Redis server is listening.<br/>Minimum: `1`<br/>Maximum: `65535`<br/>|yes|\n|**user**<br/>(Database user)|`string`|Username for authentication to the Redis database.<br/>|no|\n|**password**<br/>(Database password)|`string`|Password for authentication to the Redis database.<br/>|no|\n|**key**<br/>(Private key file)|`string`|Path to the private key file used for SSL/TLS client authentication.<br/>|no|\n|**key\\_password**<br/>(Private key password)|`string`|Password used to decrypt the private key file.<br/>|no|\n|**cert**<br/>(Client certificate)|`string`|Path to the client certificate file used for SSL/TLS client authentication.<br/>|no|\n|**cacert**<br/>(CA certificate)|`string`|Path to the Certificate Authority (CA) certificate file used to verify the server's TLS certificate.<br/>|no|\n\n**Additional Properties:** not allowed  \n**Minimal Properties:** 3  \n**If property *key* is defined**, property/ies *cert* is/are required.  \n**If property *cert* is defined**, property/ies *key* is/are required.  \n**If property *key_password* is defined**, property/ies *key* is/are required.  \n<a name=\"processors\"></a>"},{"id":"processors-data-processing-configuration","title":"processors: Data processing configuration","role":"content","text":"Settings that control how data is processed, including batch sizes, error handling, and performance tuning.\n\n\n**Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|**type**<br/>(Processor type)|`string`|Processor implementation to run. Use `flink` (default) for the Apache Flink-based processor. Use `classic` for the classic processor.<br/>Default: `\"flink\"`<br/>Enum: `\"classic\"`, `\"flink\"`<br/>||\n|**read\\_batch\\_size**|`integer`, `string`|Maximum number of records read from the source streams in a single batch.<br/>Default: `2000`<br/>Pattern: `^\\${.*}$`<br/>Minimum: `1`<br/>||\n|**read\\_batch\\_timeout\\_ms**<br/>(Read batch timeout)|`integer`|Maximum time in milliseconds to wait for a batch to fill before processing it.<br/>Default: `100`<br/>Minimum: `1`<br/>||\n|**duration**<br/>(Batch duration limit)|`integer`, `string`|(DEPRECATED)<br/>This property has no effect; use `read_batch_timeout_ms` instead.<br/>Default: `100`<br/>Pattern: `^\\${.*}$`<br/>Minimum: `1`<br/>||\n|**write\\_batch\\_size**|`integer`, `string`|Maximum number of records written to the target Redis database in a single batch.<br/>Default: `200`<br/>Pattern: `^\\${.*}$`<br/>Minimum: `1`<br/>||\n|**enable\\_async\\_processing**|`boolean`|When `true`, the processor handles batches asynchronously to improve throughput. **Classic processor only.**<br/>Default: `true`<br/>||\n|**batch\\_queue\\_size**|`integer`|Maximum number of batches queued for processing. **Classic processor only.**<br/>Default: `3`<br/>Minimum: `1`<br/>||\n|**ack\\_queue\\_size**|`integer`|Maximum number of batches queued for asynchronous acknowledgement. **Classic processor only.**<br/>Default: `10`<br/>Minimum: `1`<br/>||\n|**dedup**<br/>(Enable deduplication)|`boolean`|When `true`, the processor deduplicates incoming records. **Classic processor only.**<br/>Default: `false`<br/>||\n|**dedup\\_max\\_size**<br/>(Deduplication set size)|`integer`|Maximum number of entries kept in the deduplication set. **Classic processor only.**<br/>Default: `1024`<br/>Minimum: `1`<br/>||\n|**dedup\\_strategy**<br/>(Deduplication strategy)|`string`|(DEPRECATED)<br/>This property has no effect — the only supported strategy is `ignore`. Remove it from the configuration. **Classic processor only.**<br/>Default: `\"ignore\"`<br/>Enum: `\"reject\"`, `\"ignore\"`<br/>||\n|**error\\_handling**<br/>(Error handling strategy)|`string`|Strategy for handling failed records. `ignore` silently drops them; `dlq` writes them to the dead-letter queue.<br/>Default: `\"dlq\"`<br/>||\n|**dlq\\_max\\_messages**<br/>(DLQ message limit)|`integer`, `string`|Maximum number of messages stored per dead-letter queue stream.<br/>Default: `1000`<br/>Pattern: `^\\${.*}$`<br/>Minimum: `1`<br/>||\n|**target\\_data\\_type**<br/>(Target Redis data type)|`string`|Data type used to store target records in Redis. `hash` writes a Redis Hash; `json` writes a RedisJSON document and requires the RedisJSON module.<br/>Default: `\"hash\"`<br/>||\n|**json\\_update\\_strategy**|`string`|Strategy for updating existing JSON documents in Redis. `replace` overwrites the entire document; `merge` merges incoming fields into it.<br/>Default: `\"replace\"`<br/>||\n|**use\\_native\\_json\\_merge**<br/>(Use native JSON merge from RedisJSON module)|`boolean`|Controls whether JSON merge operations use the native `JSON.MERGE` command (when `true`) or Lua scripts (when `false`). Introduced in RDI 1.15.0. The native command provides 2x performance improvement but handles null values differently:<br/><br/>**Previous behavior (Lua merge)**: When merging `{\"field1\": \"value1\", \"field2\": \"value2\"}` with `{\"field2\": null, \"field3\": \"value3\"}`, the result was `{\"field1\": \"value1\", \"field2\": null, \"field3\": \"value3\"}` (null value is preserved).<br/><br/>**New behavior (JSON.MERGE)**: The same merge produces `{\"field1\": \"value1\", \"field3\": \"value3\"}` (null value removes the field, following [RFC 7396](https://datatracker.ietf.org/doc/html/rfc7396)).<br/><br/>**Note**: The native `JSON.MERGE` command requires RedisJSON 2.6.0 or higher. If the target database has an older version of RedisJSON, RDI automatically falls back to Lua-based merge operations regardless of this setting.<br/><br/>**Impact**: If your application logic distinguishes between a field with a `null` value and a missing field, you may need to adjust your data handling. This follows the JSON Merge Patch RFC standard but differs from the previous Lua implementation. Set to `false` to revert to the previous Lua-based merge behavior if needed.<br/><br/>The Flink processor always uses the native `JSON.MERGE` command when the target database supports it. **Classic processor only.**<br/>Default: `true`<br/>||\n|**initial\\_sync\\_processes**|`integer`, `string`|Number of parallel processes used to perform the initial data synchronization. For the Flink processor, parallelism is controlled by Flink properties instead. **Classic processor only.**<br/>Default: `4`<br/>Pattern: `^\\${.*}$`<br/>Minimum: `1`<br/>Maximum: `32`<br/>||\n|**idle\\_sleep\\_time\\_ms**<br/>(Idle sleep interval)|`integer`, `string`|Time in milliseconds to sleep between processing batches when idle. **Classic processor only.**<br/>Default: `200`<br/>Pattern: `^\\${.*}$`<br/>Minimum: `1`<br/>Maximum: `999999`<br/>||\n|**idle\\_streams\\_check\\_interval\\_ms**<br/>(Idle streams check interval)|`integer`, `string`|Time in milliseconds between checks for new streams when the processor is idle. For the Flink processor, use `processors.advanced.source.discovery.interval.ms` instead to configure a single discovery interval regardless of load. **Classic processor only.**<br/>Default: `1000`<br/>Pattern: `^\\${.*}$`<br/>Minimum: `1`<br/>Maximum: `999999`<br/>||\n|**busy\\_streams\\_check\\_interval\\_ms**<br/>(Busy streams check interval)|`integer`, `string`|Time in milliseconds between checks for new streams when the processor is busy. For the Flink processor, use `processors.advanced.source.discovery.interval.ms` instead to configure a single discovery interval regardless of load. **Classic processor only.**<br/>Default: `5000`<br/>Pattern: `^\\${.*}$`<br/>Minimum: `1`<br/>Maximum: `999999`<br/>||\n|**retry\\_max\\_attempts**<br/>(Maximum retry attempts)|`integer`, `string`|Maximum number of attempts for a failed write to the target Redis database before giving up.<br/>Default: `5`<br/>Pattern: `^\\${.*}$`<br/>Minimum: `1`<br/>||\n|**retry\\_initial\\_delay\\_ms**<br/>(Initial retry delay)|`integer`, `string`|Initial delay in milliseconds before the first retry of a failed write.<br/>Default: `1000`<br/>Pattern: `^\\${.*}$`<br/>Minimum: `1`<br/>Maximum: `999999`<br/>||\n|**retry\\_max\\_delay\\_ms**<br/>(Maximum retry delay)|`integer`, `string`|Maximum delay in milliseconds between retry attempts.<br/>Default: `10000`<br/>Pattern: `^\\${.*}$`<br/>Minimum: `1`<br/>Maximum: `999999`<br/>||\n|**wait\\_enabled**<br/>(Enable replica wait)|`boolean`|When `true`, RDI verifies that each write has been replicated to the target database's replica shards before acknowledging it. Enable this only when target database replication is enabled and a healthy replica is available. For the Flink processor, `processors.advanced.target.wait.enabled` takes priority.<br/>Default: `false`<br/>||\n|**wait\\_timeout**<br/>(Replica wait timeout)|`integer`, `string`|Maximum time in milliseconds to wait for replica write verification on the target database.<br/>Default: `1000`<br/>Pattern: `^\\${.*}$`<br/>Minimum: `1`<br/>||\n|**retry\\_on\\_replica\\_failure**|`boolean`|When `true`, RDI keeps retrying a write until replica replication is confirmed; when `false`, it gives up after the first failure.<br/>Default: `true`<br/>||\n|**on\\_failed\\_retry\\_interval**<br/>(Retry interval on failure)|`integer`, `string`|(DEPRECATED)<br/>This property has no effect; remove it from the configuration.<br/>Default: `5`<br/>Pattern: `^\\${.*}$`<br/>Minimum: `1`<br/>||\n|[**logging**](#processorslogging)<br/>(Logging configuration)|`object`|Logging settings for the processor. **Flink processor only.**<br/>||\n|[**advanced**](#processorsadvanced)<br/>(Advanced configuration)|`object`|Advanced configuration for fine-tuning the processor. **All properties under `advanced` apply to the Flink processor only and are silently ignored by the classic processor.**<br/>||\n\n**Additional Properties:** not allowed  \n<a name=\"processorslogging\"></a>"},{"id":"processorslogging-logging-configuration","title":"processors\\.logging: Logging configuration","role":"content","text":"Logging settings for the processor. **Flink processor only.**\n\n\n**Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|**level**<br/>(Logging level)|`string`|Log verbosity for the processor.<br/>Default: `\"info\"`<br/>Enum: `\"trace\"`, `\"debug\"`, `\"info\"`, `\"warn\"`, `\"error\"`<br/>||\n\n**Additional Properties:** not allowed  \n**Example**\n\n[code example]\n\n<a name=\"processorsadvanced\"></a>"},{"id":"processorsadvanced-advanced-configuration","title":"processors\\.advanced: Advanced configuration","role":"content","text":"Advanced configuration for fine-tuning the processor. **All properties under `advanced` apply to the Flink processor only and are silently ignored by the classic processor.**\n\n\n**Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|[**source**](#processorsadvancedsource)<br/>(Advanced source settings)|`object`|Advanced configuration properties for the source Redis client and streams reader. **Flink processor only.**<br/>||\n|[**target**](#processorsadvancedtarget)<br/>(Advanced target settings)|`object`|Advanced configuration properties for the target Redis client and sink. **Flink processor only.**<br/>||\n|[**dlq**](#processorsadvanceddlq)<br/>(Advanced DLQ settings)|`object`|Advanced configuration properties for the DLQ Redis client and sink. **Flink processor only.**<br/>||\n|[**processor**](#processorsadvancedprocessor)<br/>(Advanced processor settings)|`object`|Advanced configuration properties for the processor. **Flink processor only.**<br/>||\n|[**flink**](#processorsadvancedflink)<br/>(Advanced Flink settings)|`object`|Advanced configuration properties forwarded to the underlying Flink runtime. Any property listed in the [Flink configuration documentation](https://nightlies.apache.org/flink/flink-docs-release-2.0/docs/deployment/config/) can be set here and will override the RDI default. **Flink processor only.**<br/>||\n|[**resources**](#processorsadvancedresources)<br/>(Advanced resource settings)|`object`|Compute resources allocated to the Flink job, such as the number of task manager pods. **Flink processor only.**<br/>||\n\n**Additional Properties:** not allowed  \n**Minimal Properties:** 1  \n**Example**\n\n[code example]\n\n<a name=\"processorsadvancedsource\"></a>\n#### processors\\.advanced\\.source: Advanced source settings\n\nAdvanced configuration properties for the source Redis client and streams reader. **Flink processor only.**\n\n\n**Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|**stream\\.name\\.pattern**<br/>(Source stream name pattern)|`string`|Glob pattern used to discover input streams in the source Redis database, for example `data:*`.<br/>Default: `\"data:*\"`<br/>||\n|**discovery\\.interval\\.ms**<br/>(Stream discovery interval)|`integer`|Time in milliseconds between checks for new input streams. Replaces the classic `processors.idle_streams_check_interval_ms` and `processors.busy_streams_check_interval_ms` properties.<br/>Default: `1000`<br/>Minimum: `0`<br/>||\n|**batch\\.size**<br/>(Source batch size)|`integer`|Maximum number of records the source operator reads in a single batch. Alias for `processors.read_batch_size`; takes priority when both are set.<br/>Default: `2000`<br/>Minimum: `1`<br/>||\n|**batch\\.timeout\\.ms**<br/>(Source batch timeout)|`integer`|Maximum time in milliseconds to wait for a source batch to fill before processing. Alias for `processors.read_batch_timeout_ms`; takes priority when both are set.<br/>Default: `100`<br/>Minimum: `1`<br/>||\n|**connection\\.timeout\\.ms**<br/>(Source connection timeout)|`integer`|Connection timeout in milliseconds for the source Redis client.<br/>Default: `2000`<br/>Minimum: `1`<br/>||\n|**socket\\.timeout\\.ms**<br/>(Source socket timeout)|`integer`|Socket read/write timeout in milliseconds for the source Redis client.<br/>Default: `2000`<br/>Minimum: `1`<br/>||\n|**retry\\.max\\.attempts**<br/>(Source retry max attempts)|`integer`|Maximum number of retry attempts for failed source Redis operations.<br/>Default: `5`<br/>Minimum: `1`<br/>||\n|**retry\\.initial\\.delay\\.ms**<br/>(Source retry initial delay)|`integer`|Initial delay in milliseconds before the first retry of a failed source Redis operation.<br/>Default: `100`<br/>Minimum: `1`<br/>||\n|**retry\\.max\\.delay\\.ms**<br/>(Source retry max delay)|`integer`|Maximum delay in milliseconds between retry attempts for source Redis operations.<br/>Default: `3000`<br/>Minimum: `1`<br/>||\n|**retry\\.backoff\\.multiplier**<br/>(Source retry backoff multiplier)|`number`|Exponential backoff multiplier between retry attempts for source Redis operations.<br/>Default: `2`<br/>Minimum: `1`<br/>||\n\n**Additional Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|**Additional Properties**|`string`, `number`, `boolean`|||\n\n**Minimal Properties:** 1  \n**Example**\n\n[code example]\n\n<a name=\"processorsadvancedtarget\"></a>\n#### processors\\.advanced\\.target: Advanced target settings\n\nAdvanced configuration properties for the target Redis client and sink. **Flink processor only.**\n\n\n**Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|**batch\\.size**<br/>(Target sink batch size)|`integer`|Maximum number of records the target sink writes in a single batch. Alias for `processors.write_batch_size`; takes priority when both are set.<br/>Default: `200`<br/>Minimum: `1`<br/>||\n|**flush\\.interval\\.ms**<br/>(Target sink flush interval)|`integer`|Maximum time in milliseconds the target sink waits to fill a batch before flushing it to Redis.<br/>Default: `100`<br/>Minimum: `1`<br/>||\n|**connection\\.timeout\\.ms**<br/>(Target connection timeout)|`integer`|Connection timeout in milliseconds for the target Redis client.<br/>Default: `2000`<br/>Minimum: `1`<br/>||\n|**socket\\.timeout\\.ms**<br/>(Target socket timeout)|`integer`|Socket read/write timeout in milliseconds for the target Redis client.<br/>Default: `2000`<br/>Minimum: `1`<br/>||\n|**retry\\.max\\.attempts**<br/>(Target retry max attempts)|`integer`|Maximum number of retry attempts for failed target Redis operations. Alias for `processors.retry_max_attempts`; takes priority when both are set.<br/>Default: `5`<br/>Minimum: `1`<br/>||\n|**retry\\.initial\\.delay\\.ms**<br/>(Target retry initial delay)|`integer`|Initial delay in milliseconds before the first retry of a failed target Redis operation. Alias for `processors.retry_initial_delay_ms`; takes priority when both are set.<br/>Default: `1000`<br/>Minimum: `1`<br/>||\n|**retry\\.max\\.delay\\.ms**<br/>(Target retry max delay)|`integer`|Maximum delay in milliseconds between retry attempts for target Redis operations. Alias for `processors.retry_max_delay_ms`; takes priority when both are set.<br/>Default: `10000`<br/>Minimum: `1`<br/>||\n|**retry\\.backoff\\.multiplier**<br/>(Target retry backoff multiplier)|`number`|Exponential backoff multiplier between retry attempts for target Redis operations.<br/>Default: `2`<br/>Minimum: `1`<br/>||\n|**wait\\.enabled**<br/>(Target replica wait enabled)|`boolean`|When `true`, RDI verifies that each write has been replicated to the target database's replica shards before acknowledging it. Enable this only when target database replication is enabled and a healthy replica is available. Alias for `processors.wait_enabled`; takes priority when both are set.<br/>Default: `false`<br/>||\n|**wait\\.write\\.timeout\\.ms**<br/>(Target replica wait timeout)|`integer`|Maximum time in milliseconds to wait for target replica write verification. Alias for `processors.wait_timeout`; takes priority when both are set.<br/>Default: `1000`<br/>Minimum: `1`<br/>||\n|**wait\\.retry\\.enabled**<br/>(Target replica wait retry enabled)|`boolean`|When `true`, RDI keeps retrying a target write until replica replication is confirmed; when `false`, it gives up after the first failure. Alias for `processors.retry_on_replica_failure`; takes priority when both are set. When enabled, the Flink processor retries indefinitely. Failed checkpoints can restart the job, after which the retries resume. The classic processor retries once.<br/>Default: `true`<br/>||\n|**wait\\.retry\\.delay\\.ms**<br/>(Target replica wait retry delay)|`integer`|Delay in milliseconds between target replica wait retry attempts.<br/>Default: `1000`<br/>Minimum: `1`<br/>||\n\n**Additional Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|**Additional Properties**|`string`, `number`, `boolean`|||\n\n**Minimal Properties:** 1  \n**Example**\n\n[code example]\n\n<a name=\"processorsadvanceddlq\"></a>\n#### processors\\.advanced\\.dlq: Advanced DLQ settings\n\nAdvanced configuration properties for the DLQ Redis client and sink. **Flink processor only.**\n\n\n**Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|**max\\.len**<br/>(DLQ sink max length)|`integer`|Maximum number of messages stored per dead letter queue stream. Alias for `processors.dlq_max_messages`; takes priority when both are set.<br/>Default: `1000`<br/>Minimum: `1`<br/>||\n|**batch\\.size**<br/>(DLQ sink batch size)|`integer`|Maximum number of records the DLQ sink writes in a single batch.<br/>Default: `100`<br/>Minimum: `1`<br/>||\n|**flush\\.interval\\.ms**<br/>(DLQ sink flush interval)|`integer`|Maximum time in milliseconds the DLQ sink waits to fill a batch before flushing it to Redis.<br/>Default: `100`<br/>Minimum: `1`<br/>||\n|**connection\\.timeout\\.ms**<br/>(DLQ connection timeout)|`integer`|Connection timeout in milliseconds for the DLQ Redis client.<br/>Default: `2000`<br/>Minimum: `1`<br/>||\n|**socket\\.timeout\\.ms**<br/>(DLQ socket timeout)|`integer`|Socket read/write timeout in milliseconds for the DLQ Redis client.<br/>Default: `2000`<br/>Minimum: `1`<br/>||\n|**retry\\.max\\.attempts**<br/>(DLQ retry max attempts)|`integer`|Maximum number of retry attempts for failed DLQ Redis operations.<br/>Default: `1`<br/>Minimum: `1`<br/>||\n|**retry\\.initial\\.delay\\.ms**<br/>(DLQ retry initial delay)|`integer`|Initial delay in milliseconds before the first retry of a failed DLQ Redis operation.<br/>Default: `100`<br/>Minimum: `1`<br/>||\n|**retry\\.max\\.delay\\.ms**<br/>(DLQ retry max delay)|`integer`|Maximum delay in milliseconds between retry attempts for DLQ Redis operations.<br/>Default: `3000`<br/>Minimum: `1`<br/>||\n|**retry\\.backoff\\.multiplier**<br/>(DLQ retry backoff multiplier)|`number`|Exponential backoff multiplier between retry attempts for DLQ Redis operations.<br/>Default: `2`<br/>Minimum: `1`<br/>||\n|**wait\\.enabled**<br/>(DLQ replica wait enabled)|`boolean`|When `true`, RDI verifies that each DLQ write has been replicated to the DLQ database's replica shards before acknowledging it.<br/>Default: `false`<br/>||\n|**wait\\.write\\.timeout\\.ms**<br/>(DLQ replica wait timeout)|`integer`|Maximum time in milliseconds to wait for DLQ replica write verification.<br/>Default: `1000`<br/>Minimum: `1`<br/>||\n|**wait\\.retry\\.enabled**<br/>(DLQ replica wait retry enabled)|`boolean`|When `true`, RDI keeps retrying a DLQ write until replica replication is confirmed; when `false`, it gives up after the first failure.<br/>Default: `false`<br/>||\n|**wait\\.retry\\.delay\\.ms**<br/>(DLQ replica wait retry delay)|`integer`|Delay in milliseconds between DLQ replica wait retry attempts.<br/>Default: `1000`<br/>Minimum: `1`<br/>||\n\n**Additional Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|**Additional Properties**|`string`, `number`, `boolean`|||\n\n**Minimal Properties:** 1  \n**Example**\n\n[code example]\n\n<a name=\"processorsadvancedprocessor\"></a>\n#### processors\\.advanced\\.processor: Advanced processor settings\n\nAdvanced configuration properties for the processor. **Flink processor only.**\n\n\n**Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|**default\\.data\\.type**<br/>(Default target data type)|`string`|Data type to use in Redis when not overridden per job: `hash` for Redis Hash, `json` for RedisJSON. Alias for `processors.target_data_type`; takes priority when both are set.<br/>Default: `\"hash\"`<br/>Enum: `\"hash\"`, `\"json\"`<br/>||\n|**default\\.json\\.update\\.strategy**<br/>(Default JSON update strategy)|`string`|Strategy for updating JSON data in Redis: `replace` to overwrite the entire JSON object, `merge` to merge new data with the existing JSON object. Alias for `processors.json_update_strategy`; takes priority when both are set.<br/>Default: `\"replace\"`<br/>Enum: `\"replace\"`, `\"merge\"`<br/>||\n|**dlq\\.enabled**<br/>(Enable DLQ)|`boolean`|When `true`, rejected messages are stored in the dead-letter queue; when `false`, errors are silently skipped. Alias for `processors.error_handling`; takes priority when both are set.<br/>Default: `true`<br/>||\n\n**Additional Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|**Additional Properties**|`string`, `number`, `boolean`|||\n\n**Minimal Properties:** 1  \n**Example**\n\n[code example]\n\n<a name=\"processorsadvancedflink\"></a>\n#### processors\\.advanced\\.flink: Advanced Flink settings\n\nAdvanced configuration properties forwarded to the underlying Flink runtime. Any property listed in the [Flink configuration documentation](https://nightlies.apache.org/flink/flink-docs-release-2.0/docs/deployment/config/) can be set here and will override the RDI default. **Flink processor only.**<br/><br/>The properties listed below are the ones most likely to require adjustment. **Changing any other Flink property is not recommended unless instructed by Redis support.**\n\n\n**Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|**parallelism\\.default**<br/>(Default parallelism)|`integer`|Default parallelism for jobs and operators. When unset, Flink uses the number of available task slots across all task managers (`taskManager.replicas × taskmanager.numberOfTaskSlots`). Increase to fan out work across more task slots; see [parallel execution](https://nightlies.apache.org/flink/flink-docs-release-2.0/docs/dev/datastream/execution/parallel/).<br/>Minimum: `1`<br/>||\n|**taskmanager\\.numberOfTaskSlots**<br/>(Task slots per task manager)|`integer`|Number of parallel task slots per task manager pod. Each slot can run one parallel pipeline instance, so this caps the parallelism a single task manager can absorb. See [task slots and resources](https://nightlies.apache.org/flink/flink-docs-release-2.0/docs/concepts/flink-architecture/#task-slots-and-resources).<br/>Default: `1`<br/>Minimum: `1`<br/>||\n|**taskmanager\\.memory\\.process\\.size**<br/>(Task manager process memory)|`string`|Total memory budget for each task manager JVM process (heap + managed + network + metaspace + JVM overhead), expressed with a unit suffix such as `2048m` or `4g`. See [task manager memory configuration](https://nightlies.apache.org/flink/flink-docs-release-2.0/docs/deployment/memory/mem_setup_tm/).<br/>Default: `\"2048m\"`<br/>||\n\n**Additional Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|**Additional Properties**|`string`, `number`, `boolean`|||\n\n**Minimal Properties:** 1  \n**Example**\n\n[code example]\n\n<a name=\"processorsadvancedresources\"></a>\n#### processors\\.advanced\\.resources: Advanced resource settings\n\nCompute resources allocated to the Flink job, such as the number of task manager pods. **Flink processor only.**\n\n\n**Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|[**taskManager**](#processorsadvancedresourcestaskmanager)<br/>(Task manager resource settings)|`object`|Resource settings for Flink task manager pods.<br/>||\n\n**Additional Properties:** not allowed  \n**Minimal Properties:** 1  \n**Example**\n\n[code example]\n\n<a name=\"processorsadvancedresourcestaskmanager\"></a>\n##### processors\\.advanced\\.resources\\.taskManager: Task manager resource settings\n\nResource settings for Flink task manager pods.\n\n\n**Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|**replicas**<br/>(Task manager replicas)|`integer`|Number of Flink task manager pods to run.<br/>Minimum: `1`<br/>||\n\n**Additional Properties:** not allowed  \n**Minimal Properties:** 1  \n<a name=\"secret-providers\"></a>"},{"id":"secret-providers-secret-providers","title":"secret\\-providers: Secret providers","role":"content","text":"External secret providers used to resolve `${...}` references in the configuration.\n\n\n**Properties** (key: `.*`)\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|**type**<br/>(Provider type)|`string`|Secret provider backend. `aws` uses AWS Secrets Manager; `vault` uses HashiCorp Vault.<br/>Enum: `\"aws\"`, `\"vault\"`<br/>|yes|\n|[**parameters**](#secret-providersparameters)<br/>(Provider parameters)|`object`|Configuration parameters for the secret provider.<br/>|yes|\n\n\n<a name=\"secret-providersparameters\"></a>"},{"id":"secret-providersparameters-provider-parameters","title":"secret\\-providers\\.parameters: Provider parameters","role":"content","text":"Configuration parameters for the secret provider.\n\n\n**Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|[**objects**](#secret-providersparametersobjects)<br/>(Secrets objects array)|`object[]`|Secret objects to fetch from the provider.<br/>|yes|\n\n**Example**\n\n[code example]\n\n<a name=\"secret-providersparametersobjects\"></a>\n#### secret\\-providers\\.parameters\\.objects\\[\\]: Secrets objects array\n\nSecret objects to fetch from the provider.\n\n\n**Items: Secret object**\n\n**No properties.**\n\n**Example**\n\n[code example]\n\n<a name=\"metadata\"></a>"},{"id":"metadata-pipeline-metadata","title":"metadata: Pipeline metadata","role":"content","text":"Optional metadata describing this pipeline, such as a display name and description.\n\n\n**Properties**\n\n|Name|Type|Description|Required|\n|----|----|-----------|--------|\n|**name**<br/>(Pipeline name)|`string`|Human-readable name for the pipeline. Maximum 100 characters.<br/>Maximal Length: `100`<br/>||\n|**description**<br/>(Pipeline description)|`string`|Free-form description of what the pipeline does. Maximum 500 characters.<br/>Maximal Length: `500`<br/>||\n|**revision**<br/>(Pipeline revision)|`integer`|Pipeline revision number. Must be a non-negative integer.<br/>Minimum: `0`<br/>||\n|[**tags**](#metadatatags)<br/>(Pipeline tags)|`string[]`|Array of pipeline tags. Each tag must be a string of up to 50 characters, containing only alphanumeric characters, dots, dashes, or underscores, and must start and end with an alphanumeric character. Tags must be unique.<br/>||\n\n**Additional Properties:** not allowed  \n<a name=\"metadatatags\"></a>"},{"id":"metadatatags-pipeline-tags","title":"metadata\\.tags\\[\\]: Pipeline tags","role":"content","text":"Array of pipeline tags. Each tag must be a string of up to 50 characters, containing only alphanumeric characters, dots, dashes, or underscores, and must start and end with an alphanumeric character. Tags must be unique.\n\n\n**Unique Items:** yes"}],"examples":[{"id":"sourcesconnection-source-database-connection-ex0","language":"yaml","code":"SQL database:\n  hr:\n    type: postgresql\n    host: localhost\n    port: 5432\n    database: postgres\n    user: postgres\n    password: postgres\nMongoDB:\n  mongodb-source:\n    type: mongodb\n    connection_string: mongodb://localhost:27017/?replicaSet=rs0\n    user: debezium\n    password: dbz\n    database: db1,db2\nSpanner:\n  spanner-source:\n    type: spanner\n    project_id: example-12345\n    instance_id: example\n    database_id: example\n    change_streams:\n      change_stream_all:\n        retention_period_hours: 24\nSnowflake:\n  snowflake:\n    type: snowflake\n    url: jdbc:snowflake://myaccount.snowflakecomputing.com/\n    user: myuser\n    password: mypassword\n    database: MYDB\n    warehouse: COMPUTE_WH","section_id":"sourcesconnection-source-database-connection"},{"id":"sourcesconnection-source-database-connection-ex1","language":"yaml","code":"hr:\n  type: postgresql\n  host: localhost\n  port: 5432\n  database: postgres\n  user: postgres\n  password: postgres","section_id":"sourcesconnection-source-database-connection"},{"id":"sourcesconnection-source-database-connection-ex2","language":"yaml","code":"my-oracle:\n  type: oracle\n  host: 172.17.0.4\n  port: 1521\n  user: c##dbzuser\n  password: dbz","section_id":"sourcesconnection-source-database-connection"},{"id":"sourcesconnection-source-database-connection-ex3","language":"yaml","code":"mongodb-source:\n  type: mongodb\n  connection_string: mongodb://localhost:27017/?replicaSet=rs0\n  user: debezium\n  password: dbz\n  database: db1,db2","section_id":"sourcesconnection-source-database-connection"},{"id":"sourcesconnection-source-database-connection-ex4","language":"yaml","code":"spanner-source:\n  type: spanner\n  project_id: example-12345\n  instance_id: example\n  database_id: example\n  change_streams:\n    change_stream_all:\n      retention_period_hours: 24","section_id":"sourcesconnection-source-database-connection"},{"id":"sourcesconnection-source-database-connection-ex5","language":"yaml","code":"snowflake:\n  type: snowflake\n  url: jdbc:snowflake://myaccount.snowflakecomputing.com/\n  user: myuser\n  password: mypassword\n  database: MYDB\n  warehouse: COMPUTE_WH","section_id":"sourcesconnection-source-database-connection"},{"id":"sourceslogging-logging-configuration-ex0","language":"yaml","code":"level: info","section_id":"sourceslogging-logging-configuration"},{"id":"sourcesadvanced-advanced-configuration-ex0","language":"yaml","code":"sink:\n  redis.batch.size: 1000\n  redis.flush.interval.ms: 100\n  redis.connection.timeout.ms: 2000\n  redis.socket.timeout.ms: 2000\n  redis.retry.max.attempts: 5\n  redis.retry.initial.delay.ms: 100\n  redis.retry.max.delay.ms: 3000\n  redis.retry.backoff.multiplier: 2\n  redis.oom.retry.initial.delay.ms: 1000\n  redis.oom.retry.max.delay.ms: 10000\n  redis.oom.retry.backoff.multiplier: 2\n  redis.wait.enabled: false\n  redis.wait.timeout.ms: 1000\n  redis.wait.write.timeout.ms: 1000\n  redis.wait.retry.enabled: false\n  redis.wait.retry.delay.ms: 1000\nsource:\n  snapshot.mode: initial\n  snapshot.max.threads: 1\n  poll.interval.ms: 500\n  max.batch.size: 2048\n  max.queue.size: 8192\n  heartbeat.interval.ms: 0\n  database.trustServerCertificate: false\n  driver.applicationIntent: ReadWrite\n  lob.enabled: false\n  publication.autocreate.mode: all_tables\n  publication.name: dbz_publication\n  slot.name: debezium\n  capture.mode: change_streams_update_full\n  capture.mode.full.update.type: lookup\n  cursor.oversize.handling.mode: fail\n  cursor.oversize.skip.threshold: 0\n  mongodb.authsource: admin\n  mongodb.ssl.invalid.hostname.allowed: false\n  mongodb.connect.timeout.ms: 10000\n  mongodb.socket.timeout.ms: 0\n  mongodb.server.selection.timeout.ms: 30000\n  mongodb.heartbeat.frequency.ms: 10000\n  spanner.version.retention.period.hours: 1\n  spanner.fetch.timeout.ms: 500\n  spanner.fetch.heartbeat.ms: 100\n  spanner.max.rows.per.partition: 10000\n  spanner.dialect: GOOGLESQL\nquarkus: {}\nflink:\n  taskmanager.numberOfTaskSlots: 1\nresources: {}\nriotx:\n  poll: 30s\n  snapshot: INITIAL\n  streamPrefix: 'data:'\n  clearOffset: false\n  count: 0\ndiagnostics:\n  heap_dump:\n    enabled: false\n  java_flight_recorder:\n    enabled: false","section_id":"sourcesadvanced-advanced-configuration"},{"id":"sourcesadvanced-advanced-configuration-ex1","language":"yaml","code":"redis.batch.size: 1000\nredis.flush.interval.ms: 100\nredis.connection.timeout.ms: 2000\nredis.socket.timeout.ms: 2000\nredis.retry.max.attempts: 5\nredis.retry.initial.delay.ms: 100\nredis.retry.max.delay.ms: 3000\nredis.retry.backoff.multiplier: 2\nredis.oom.retry.initial.delay.ms: 1000\nredis.oom.retry.max.delay.ms: 10000\nredis.oom.retry.backoff.multiplier: 2\nredis.wait.enabled: false\nredis.wait.timeout.ms: 1000\nredis.wait.write.timeout.ms: 1000\nredis.wait.retry.enabled: false\nredis.wait.retry.delay.ms: 1000","section_id":"sourcesadvanced-advanced-configuration"},{"id":"sourcesadvanced-advanced-configuration-ex2","language":"yaml","code":"snapshot.mode: initial\nsnapshot.max.threads: 1\npoll.interval.ms: 500\nmax.batch.size: 2048\nmax.queue.size: 8192\nheartbeat.interval.ms: 0\ndatabase.trustServerCertificate: false\ndriver.applicationIntent: ReadWrite\nlob.enabled: false\npublication.autocreate.mode: all_tables\npublication.name: dbz_publication\nslot.name: debezium\ncapture.mode: change_streams_update_full\ncapture.mode.full.update.type: lookup\ncursor.oversize.handling.mode: fail\ncursor.oversize.skip.threshold: 0\nmongodb.authsource: admin\nmongodb.ssl.invalid.hostname.allowed: false\nmongodb.connect.timeout.ms: 10000\nmongodb.socket.timeout.ms: 0\nmongodb.server.selection.timeout.ms: 30000\nmongodb.heartbeat.frequency.ms: 10000\nspanner.version.retention.period.hours: 1\nspanner.fetch.timeout.ms: 500\nspanner.fetch.heartbeat.ms: 100\nspanner.max.rows.per.partition: 10000\nspanner.dialect: GOOGLESQL","section_id":"sourcesadvanced-advanced-configuration"},{"id":"sourcesadvanced-advanced-configuration-ex3","language":"yaml","code":"taskmanager.numberOfTaskSlots: 1","section_id":"sourcesadvanced-advanced-configuration"},{"id":"sourcesadvanced-advanced-configuration-ex4","language":"yaml","code":"poll: 30s\nsnapshot: INITIAL\nstreamPrefix: 'data:'\nclearOffset: false\ncount: 0","section_id":"sourcesadvanced-advanced-configuration"},{"id":"sourcesadvanced-advanced-configuration-ex5","language":"yaml","code":"heap_dump:\n  enabled: false\njava_flight_recorder:\n  enabled: false","section_id":"sourcesadvanced-advanced-configuration"},{"id":"sourcesadvanced-advanced-configuration-ex6","language":"yaml","code":"enabled: false","section_id":"sourcesadvanced-advanced-configuration"},{"id":"sourcesadvanced-advanced-configuration-ex7","language":"yaml","code":"enabled: false","section_id":"sourcesadvanced-advanced-configuration"},{"id":"processorslogging-logging-configuration-ex0","language":"yaml","code":"level: info","section_id":"processorslogging-logging-configuration"},{"id":"processorsadvanced-advanced-configuration-ex0","language":"yaml","code":"source:\n  stream.name.pattern: data:*\n  discovery.interval.ms: 1000\n  batch.size: 2000\n  batch.timeout.ms: 100\n  connection.timeout.ms: 2000\n  socket.timeout.ms: 2000\n  retry.max.attempts: 5\n  retry.initial.delay.ms: 100\n  retry.max.delay.ms: 3000\n  retry.backoff.multiplier: 2\ntarget:\n  batch.size: 200\n  flush.interval.ms: 100\n  connection.timeout.ms: 2000\n  socket.timeout.ms: 2000\n  retry.max.attempts: 5\n  retry.initial.delay.ms: 1000\n  retry.max.delay.ms: 10000\n  retry.backoff.multiplier: 2\n  wait.enabled: false\n  wait.write.timeout.ms: 1000\n  wait.retry.enabled: true\n  wait.retry.delay.ms: 1000\ndlq:\n  max.len: 1000\n  batch.size: 100\n  flush.interval.ms: 100\n  connection.timeout.ms: 2000\n  socket.timeout.ms: 2000\n  retry.max.attempts: 1\n  retry.initial.delay.ms: 100\n  retry.max.delay.ms: 3000\n  retry.backoff.multiplier: 2\n  wait.enabled: false\n  wait.write.timeout.ms: 1000\n  wait.retry.enabled: false\n  wait.retry.delay.ms: 1000\nprocessor:\n  default.data.type: hash\n  default.json.update.strategy: replace\n  dlq.enabled: true\nflink:\n  taskmanager.numberOfTaskSlots: 1\n  taskmanager.memory.process.size: 2048m\nresources:\n  taskManager: {}","section_id":"processorsadvanced-advanced-configuration"},{"id":"processorsadvanced-advanced-configuration-ex1","language":"yaml","code":"stream.name.pattern: data:*\ndiscovery.interval.ms: 1000\nbatch.size: 2000\nbatch.timeout.ms: 100\nconnection.timeout.ms: 2000\nsocket.timeout.ms: 2000\nretry.max.attempts: 5\nretry.initial.delay.ms: 100\nretry.max.delay.ms: 3000\nretry.backoff.multiplier: 2","section_id":"processorsadvanced-advanced-configuration"},{"id":"processorsadvanced-advanced-configuration-ex2","language":"yaml","code":"batch.size: 200\nflush.interval.ms: 100\nconnection.timeout.ms: 2000\nsocket.timeout.ms: 2000\nretry.max.attempts: 5\nretry.initial.delay.ms: 1000\nretry.max.delay.ms: 10000\nretry.backoff.multiplier: 2\nwait.enabled: false\nwait.write.timeout.ms: 1000\nwait.retry.enabled: true\nwait.retry.delay.ms: 1000","section_id":"processorsadvanced-advanced-configuration"},{"id":"processorsadvanced-advanced-configuration-ex3","language":"yaml","code":"max.len: 1000\nbatch.size: 100\nflush.interval.ms: 100\nconnection.timeout.ms: 2000\nsocket.timeout.ms: 2000\nretry.max.attempts: 1\nretry.initial.delay.ms: 100\nretry.max.delay.ms: 3000\nretry.backoff.multiplier: 2\nwait.enabled: false\nwait.write.timeout.ms: 1000\nwait.retry.enabled: false\nwait.retry.delay.ms: 1000","section_id":"processorsadvanced-advanced-configuration"},{"id":"processorsadvanced-advanced-configuration-ex4","language":"yaml","code":"default.data.type: hash\ndefault.json.update.strategy: replace\ndlq.enabled: true","section_id":"processorsadvanced-advanced-configuration"},{"id":"processorsadvanced-advanced-configuration-ex5","language":"yaml","code":"taskmanager.numberOfTaskSlots: 1\ntaskmanager.memory.process.size: 2048m","section_id":"processorsadvanced-advanced-configuration"},{"id":"processorsadvanced-advanced-configuration-ex6","language":"yaml","code":"taskManager: {}","section_id":"processorsadvanced-advanced-configuration"},{"id":"secret-providersparameters-provider-parameters-ex0","language":"yaml","code":"objects:\n  - {}","section_id":"secret-providersparameters-provider-parameters"},{"id":"secret-providersparameters-provider-parameters-ex1","language":"yaml","code":"- {}","section_id":"secret-providersparameters-provider-parameters"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/data-transformation/add_field","title":"add_field","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation/add_field/","summary":"Add fields to a record","aliases":["/integrate/redis-data-integration/ingest/reference/data-transformation/add_field/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-18T16:47:14+01:00","page_type":"content","content_hash":"3813b4a042f50bcfb03927fb39332c1e7c45bfbef361ca7939b47cda9880e52f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Add fields to a record\n\n**Option 1 (alternative):**\nAdd multiple fields\n\n**Properties**\n\n| Name                         | Type       | Description | Required |\n| ---------------------------- | ---------- | ----------- | -------- |\n| [**fields**](#option1fields) | `object[]` | Fields<br/> | yes      |\n\n**Additional Properties:** not allowed\n\n**Example**\n\n[code example]\n\n**Option 2 (alternative):**\nAdd one field\n\n**Properties**\n\n| Name           | Type     | Description                                                                                                                                                                                           | Required |\n| -------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |\n| **field**      | `string` | Field<br/>                                                                                                                                                                                            | yes      |\n| **expression** | `string` | Expression<br/>                                                                                                                                                                                       | yes      |\n| **language**   | `string` | Language<br/>Enum: `\"jmespath\"`, `\"sql\"`<br/>                                                                                                                                                         | yes      |\n| **cache**      | `object` | Cache the result of the field expression. See [`cache`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation/cache) for the property list. **Flink processor only.**<br/> | no       |\n\n**Additional Properties:** not allowed\n\n**Example**\n\n[code example]\n\n<a name=\"option1fields\"></a>"},{"id":"option-1-fields-array","title":"Option 1: fields\\[\\]: array","role":"parameters","text":"Fields\n\n**Items**\n\n**Item Properties**\n\n| Name           | Type     | Description                                                                                                                                                                                           | Required |\n| -------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |\n| **field**      | `string` | Field<br/>                                                                                                                                                                                            | yes      |\n| **expression** | `string` | Expression<br/>                                                                                                                                                                                       | yes      |\n| **language**   | `string` | Language<br/>Enum: `\"jmespath\"`, `\"sql\"`<br/>                                                                                                                                                         | yes      |\n| **cache**      | `object` | Cache the result of the field expression. See [`cache`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation/cache) for the property list. **Flink processor only.**<br/> | no       |\n\n**Item Additional Properties:** not allowed\n\n**Example**\n\n[code example]"}],"examples":[{"id":"overview-ex0","language":"yaml","code":"source:\n  schema: dbo\n  table: emp\ntransform:\n  - uses: add_field\n    with:\n      fields:\n        - field: name.full_name\n          language: jmespath\n          expression: concat([name.fname, ' ', name.lname])\n        - field: name.fname_upper\n          language: jmespath\n          expression: upper(name.fname)","section_id":"overview"},{"id":"overview-ex1","language":"yaml","code":"source:\n  schema: dbo\n  table: emp\ntransform:\n  - uses: add_field\n    with:\n      field: country\n      language: sql\n      expression: country_code || ' - ' || UPPER(country_name)","section_id":"overview"},{"id":"option-1-fields-array-ex0","language":"yaml","code":"- {}","section_id":"option-1-fields-array"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/data-transformation/cache","title":"cache","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation/cache/","summary":"Cache the result of an expression or lookup","tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-18T16:47:14+01:00","page_type":"content","content_hash":"95e3732826cf7b4880c7e5829c6b42db74a5ca004305ff1ad7e8708f683fa9a1","sections":[{"id":"content","title":"Content","role":"content","text":"Cache the result of an expression or lookup. Caching avoids re-evaluating\nthe expression or re-querying Redis when the same input field values\nappear repeatedly. Cache keys are derived from the values of the input\nfields referenced by the expression, not from the full record.\n\nThe `cache:` block can be added to the following transformations and\noutput expressions:\n\n- The expression in [`add_field`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation/add_field) (single-field and per-item form).\n- The expression in [`filter`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation/filter).\n- The expression in [`map`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation/map).\n- The argument expressions in [`redis.lookup`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation/lookup). The same block accepts a `lookup_cache:` variant that caches the lookup result returned by Redis.\n- The dynamic `key` and `expire` expressions of the `redis.write` output.\n\n**Flink processor only.** The classic processor silently ignores `cache:` blocks.\n\n**Properties**\n\n| Name            | Type      | Description                                                    | Required | Default |\n| --------------- | --------- | -------------------------------------------------------------- | -------- | ------- |\n| **enabled**     | `boolean` | Set to `true` to enable caching.                               | no       | `false` |\n| **max_size**    | `integer` | Maximum number of entries kept in the cache. Must be positive. | no       | `1000`  |\n| **ttl_seconds** | `integer` | Time-to-live for each entry, in seconds. Must be positive.     | no       | `60`    |\n\n**Additional Properties:** not allowed\n\n**Example**\n\n[code example]\n\nSee\n[Caching expression results](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/caching-expression-results)\nfor additional examples."}],"examples":[{"id":"content-ex0","language":"yaml","code":"source:\n  schema: dbo\n  table: customer\ntransform:\n  - uses: add_field\n    with:\n      field: country\n      language: sql\n      expression: country_code || ' - ' || UPPER(country_name)\n      cache:\n        enabled: true\n        max_size: 500\n        ttl_seconds: 300","section_id":"content"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/data-transformation/filter","title":"filter","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation/filter/","summary":"Filter records","aliases":["/integrate/redis-data-integration/ingest/reference/data-transformation/filter/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-18T16:47:14+01:00","page_type":"content","content_hash":"5e7af2ab9792111a132d20336cc9d6a393e844f7f795f6fae80ca2b13d105b8b","sections":[{"id":"content","title":"Content","role":"content","text":"Filter records\n\n**Properties**\n\n| Name           | Type     | Description                                                                                                                                                                                            | Required |\n| -------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------- |\n| **expression** | `string` | Expression<br/>                                                                                                                                                                                        | yes      |\n| **language**   | `string` | Language<br/>Enum: `\"jmespath\"`, `\"sql\"`<br/>                                                                                                                                                          | yes      |\n| **cache**      | `object` | Cache the result of the filter expression. See [`cache`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation/cache) for the property list. **Flink processor only.**<br/> | no       |\n\n**Additional Properties:** not allowed\n\n**Example**\n\n[code example]"}],"examples":[{"id":"content-ex0","language":"yaml","code":"source:\n  schema: dbo\n  table: emp\ntransform:\n  - uses: filter\n    with:\n      language: sql\n      expression: age>20","section_id":"content"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/data-transformation","title":"Data transformation reference","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation/","summary":"View reference material for RDI data transformations","aliases":["/integrate/redis-data-integration/ingest/reference/data-transformation/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2025-02-27T10:48:04Z","children":[{"id":"integrate/redis-data-integration/reference/data-transformation/add_field","summary":"Add fields to a record","title":"add_field","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation/add_field/"},{"id":"integrate/redis-data-integration/reference/data-transformation/filter","summary":"Filter records","title":"filter","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation/filter/"},{"id":"integrate/redis-data-integration/reference/data-transformation/map","summary":"Map a record into a new output based on expressions","title":"map","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation/map/"},{"id":"integrate/redis-data-integration/reference/data-transformation/lookup","summary":"Lookup data from Redis using the given command and key","title":"redis.lookup","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation/lookup/"},{"id":"integrate/redis-data-integration/reference/data-transformation/remove_field","summary":"Remove fields","title":"remove_field","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation/remove_field/"},{"id":"integrate/redis-data-integration/reference/data-transformation/rename_field","summary":"Rename fields. All other fields remain unchanged.","title":"rename_field","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation/rename_field/"}],"page_type":"index","sections":[],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/data-transformation/lookup","title":"redis.lookup","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation/lookup/","summary":"Lookup data from Redis using the given command and key","tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-18T16:47:14+01:00","page_type":"content","content_hash":"526f107b974fb6fbfb05c42bbfe394de5b722eceb1e608e5109dca0010c4f1dc","sections":[{"id":"overview","title":"Overview","role":"overview","text":"**Properties**\n\n| Name                | Type       | Description                                                                                                                                                                                                                                                          | Required |\n| ------------------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |\n| **connection**      | `string`   | Connection name                                                                                                                                                                                                                                                      | yes      |\n| **cmd**             | `string`   | The command to execute                                                                                                                                                                                                                                               | yes      |\n| [**args**](#args)   | `string[]` | Redis command arguments                                                                                                                                                                                                                                              | yes      |\n| **language**        | `string`   | Language<br/>Enum: `\"jmespath\"`, `\"sql\"`<br/>                                                                                                                                                                                                                        | yes      |\n| **field**           | `string`   | The target field to write the result to<br/>                                                                                                                                                                                                                         | yes      |\n| **cache**           | `object`   | Cache the result of the argument expressions. See [`cache`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation/cache) for the property list. **Flink processor only.**<br/>                                                            | no       |\n| **lookup_cache**    | `object`   | Cache the lookup results returned by Redis across batches, keyed by the resolved command arguments. Uses the same property list as [`cache`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation/cache). **Flink processor only.**<br/> | no       |\n| [**batch**](#batch) | `object`   | Override the default batching behavior for `redis.lookup` lookups. **Flink processor only.**<br/>                                                                                                                                                                    | no       |\n\n**Additional Properties:** not allowed\n\n**Items**\n\n**Item Type:** `string`"},{"id":"batch","title":"batch: object","role":"content","text":"`redis.lookup` lookups are always batched and executed through a single Redis pipeline per batch. The processor flushes a batch when either the size or the timeout limit is reached. The defaults are sensible for most pipelines; add the `batch:` block only when you need to override them. **Flink processor only.**\n\n**Properties**\n\n| Name           | Type      | Description                                                                              | Required | Default |\n| -------------- | --------- | ---------------------------------------------------------------------------------------- | -------- | ------- |\n| **size**       | `integer` | Maximum number of lookups in a single batch. Must be positive.                           | no       | `200`   |\n| **timeout_ms** | `integer` | Maximum time in milliseconds to wait before flushing a non-full batch. Must be positive. | no       | `100`   |\n\n**Additional Properties:** not allowed\n**Example**\n\nRead a hash field:\n\n[code example]"},{"id":"args","title":"args\\[\\]: Redis command arguments","role":"content","text":"The list of expressions that produce arguments."}],"examples":[{"id":"batch-ex0","language":"yaml","code":"source:\n  table: album\ntransform:\n  - uses: redis.lookup\n    with:\n      connection: target\n      cmd: HGET\n      args:\n        - concat(['artist:artistid:', artistid])\n        - \"`name`\"\n      language: jmespath\n      field: artist\noutput:\n  - uses: redis.write\n    with:\n      connection: target\n      data_type: hash\n      key:\n        expression: concat(['album:albumid:', albumid])\n        language: jmespath","section_id":"batch"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/data-transformation/map","title":"map","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation/map/","summary":"Map a record into a new output based on expressions","aliases":["/integrate/redis-data-integration/ingest/reference/data-transformation/map/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-18T16:47:14+01:00","page_type":"content","content_hash":"f8ca4b463b1c1fed11fcd110d9c75bfa1cdfd19e1c42e55a3deb3782af914861","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Map a record into a new output based on expressions\n\n**Properties**\n\n| Name                          | Type               | Description                                                                                                                                                                                         | Required |\n| ----------------------------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |\n| [**expression**](#expression) | `object`, `string` | Expression<br/>                                                                                                                                                                                     | yes      |\n| **language**                  | `string`           | Language<br/>Enum: `\"jmespath\"`, `\"sql\"`<br/>                                                                                                                                                       | yes      |\n| **cache**                     | `object`           | Cache the result of the map expression. See [`cache`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation/cache) for the property list. **Flink processor only.**<br/> | no       |\n\n**Additional Properties:** not allowed\n\n**Example**\n\n[code example]\n\n**Example**\n\n[code example]\n\n<a name=\"expression\"></a>"},{"id":"expression-object","title":"expression: object","role":"content","text":"Expression\n\n**No properties.**"}],"examples":[{"id":"overview-ex0","language":"yaml","code":"source:\n  schema: dbo\n  table: emp\ntransform:\n  - uses: map\n    with:\n      expression:\n        first_name: first_name\n        last_name: last_name\n        greeting: >-\n          'Hello ' || CASE WHEN gender = 'F' THEN 'Ms.' WHEN gender = 'M' THEN 'Mr.'\n          ELSE 'N/A' END || ' ' || full_name\n        country: country\n        full_name: full_name\n      language: sql","section_id":"overview"},{"id":"overview-ex1","language":"yaml","code":"source:\n  table: customer\ntransform:\n  - uses: map\n    with:\n      expression: |\n        {\n          \"CustomerId\": customer_id,\n          \"FirstName\": first_name,\n          \"LastName\": last_name,\n          \"Company\": company,\n          \"Location\":\n          {\n            \"Street\": address,\n            \"City\": city,\n            \"State\": state,\n            \"Country\": country,\n            \"PostalCode\": postal_code\n          },\n          \"Phone\": phone,\n          \"Fax\": fax,\n          \"Email\": email\n        }\n      language: jmespath","section_id":"overview"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/data-transformation/remove_field","title":"remove_field","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation/remove_field/","summary":"Remove fields","aliases":["/integrate/redis-data-integration/ingest/reference/data-transformation/remove_field/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2025-06-16T16:03:18+03:00","page_type":"content","content_hash":"3f398800a67633cbbd3726fb99cfe6e67491b300373e3a31f117dd19ceed1bf4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Remove fields\n\n**Option 1 (alternative):**\nRemove multiple fields\n\n**Properties**\n\n| Name                         | Type       | Description | Required |\n| ---------------------------- | ---------- | ----------- | -------- |\n| [**fields**](#option1fields) | `object[]` | Fields<br/> | yes      |\n\n**Additional Properties:** not allowed\n\n**Example**\n\n[code example]\n\n**Option 2 (alternative):**\nRemove one field\n\n**Properties**\n\n| Name      | Type     | Description | Required |\n| --------- | -------- | ----------- | -------- |\n| **field** | `string` | Field<br/>  | yes      |\n\n**Additional Properties:** not allowed  \n**Example**\n\n[code example]\n\n<a name=\"option1fields\"></a>"},{"id":"option-1-fields-array","title":"Option 1: fields\\[\\]: array","role":"parameters","text":"Fields\n\n**Items**\n\n**Item Properties**\n\n| Name      | Type     | Description | Required |\n| --------- | -------- | ----------- | -------- |\n| **field** | `string` | Field<br/>  | yes      |\n\n**Item Additional Properties:** not allowed\n\n**Example**\n\n[code example]"}],"examples":[{"id":"overview-ex0","language":"yaml","code":"source:\n  schema: dbo\n  table: emp\ntransform:\n  - uses: remove_field\n    with:\n      fields:\n        - field: credit_card\n        - field: name.mname","section_id":"overview"},{"id":"overview-ex1","language":"yaml","code":"source:\n  schema: dbo\n  table: emp\ntransform:\n  - uses: remove_field\n    with:\n      field: credit_card","section_id":"overview"},{"id":"option-1-fields-array-ex0","language":"yaml","code":"- {}","section_id":"option-1-fields-array"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/data-transformation/rename_field","title":"rename_field","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation/rename_field/","summary":"Rename fields. All other fields remain unchanged.","aliases":["/integrate/redis-data-integration/ingest/reference/data-transformation/rename_field/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2025-06-16T16:03:18+03:00","page_type":"content","content_hash":"af7192384066afe655f841510dd0d2a917917939f04598e241cbdfe0c393ae50","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Rename fields. All other fields remain unchanged.\n\n**Option 1 (alternative):**\nRename multiple fields\n\n**Properties**\n\n| Name                         | Type       | Description | Required |\n| ---------------------------- | ---------- | ----------- | -------- |\n| [**fields**](#option1fields) | `object[]` | Fields<br/> | yes      |\n\n**Additional Properties:** not allowed  \n**Example**\n\n[code example]\n\n**Option 2 (alternative):**\nRename one field\n\n**Properties**\n\n| Name           | Type     | Description     | Required |\n| -------------- | -------- | --------------- | -------- |\n| **from_field** | `string` | From field<br/> | yes      |\n| **to_field**   | `string` | To field<br/>   | yes      |\n\n**Additional Properties:** not allowed\n\n**Example**\n\n[code example]\n\n<a name=\"option1fields\"></a>"},{"id":"option-1-fields-array","title":"Option 1: fields\\[\\]: array","role":"parameters","text":"Fields\n\n**Items**\n\n**Item Properties**\n\n| Name           | Type     | Description     | Required |\n| -------------- | -------- | --------------- | -------- |\n| **from_field** | `string` | From field<br/> | yes      |\n| **to_field**   | `string` | To field<br/>   | yes      |\n\n**Item Additional Properties:** not allowed\n\n**Example**\n\n[code example]"}],"examples":[{"id":"overview-ex0","language":"yaml","code":"source:\n  schema: dbo\n  table: emp\ntransform:\n  - uses: rename_field\n    with:\n      fields:\n        - from_field: name.lname\n          to_field: name.last_name\n        - from_field: name.fname\n          to_field: name.first_name","section_id":"overview"},{"id":"overview-ex1","language":"yaml","code":"source:\n  schema: dbo\n  table: emp\ntransform:\n  - uses: rename_field\n    with:\n      from_field: name.lname\n      to_field: name.last_name","section_id":"overview"},{"id":"option-1-fields-array-ex0","language":"yaml","code":"- {}","section_id":"option-1-fields-array"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference","title":"Reference","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/","summary":"View reference material for Redis Data Integration","aliases":["/integrate/redis-data-integration/ingest/reference/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-18T16:47:14+01:00","children":[{"id":"integrate/redis-data-integration/reference/config-yaml-reference","summary":"Redis Data Integration configuration file reference","title":"Redis Data Integration configuration file","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/config-yaml-reference/"},{"id":"integrate/redis-data-integration/reference/jmespath-custom-functions","summary":"JMESPath custom function reference","title":"JMESPath custom functions","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/jmespath-custom-functions/"},{"id":"integrate/redis-data-integration/reference/cli","summary":"Reference for the RDI CLI commands","title":"CLI reference","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/"},{"id":"integrate/redis-data-integration/reference/data-transformation","summary":"View reference material for RDI data transformations","title":"Data transformation reference","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation/"},{"id":"integrate/redis-data-integration/reference/api-migration","summary":"Migrate RDI API clients from API v1 to API v2","title":"Migrate from RDI API v1 to v2","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/api-migration/"},{"id":"integrate/redis-data-integration/reference/api-reference","summary":"","title":"Redis Data Integration API","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/api-reference/"}],"page_type":"content","content_hash":"f41b63c074b4e00cf5e93fbde4aeec9630daa309a84587ed6779b9a74390af55","sections":[{"id":"content","title":"Content","role":"content","text":"For API clients, [RDI API v1 is deprecated as of RDI 1.19.0](https://redis.io/docs/latest/integrate/redis-data-integration/reference/api-migration).\nUse [RDI API v2](https://redis.io/docs/latest/integrate/redis-data-integration/reference/api-reference) for new integrations and migrate existing clients. API v1 will not be extended with new RDI features and may be removed in a future RDI version."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/reference/jmespath-custom-functions","title":"JMESPath custom functions","url":"https://redis.io/docs/latest/integrate/redis-data-integration/reference/jmespath-custom-functions/","summary":"JMESPath custom function reference","aliases":["/integrate/redis-data-integration/ingest/reference/jmespath-custom-functions/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-05-07T19:51:40Z","page_type":"content","content_hash":"fa30a14d85cd20681498cfa4b5defd24713032b2c6c3f29d1ed8c89e192be06c","sections":[{"id":"content","title":"Content","role":"content","text":"See also the [JMESPath functions proposal](https://jmespath.org/proposals/functions.html)\nfor a full description of the function syntax and a list of built-in functions.\n\n| Function             | Description                                                                                                                         | Example                                                                                                                                                                                                  | Comments                                                                                                                                                         |\n| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `base64_decode`      | Decodes a base64(RFC 4648) encoded string                                                                                           | Input: `{\"encoded\": \"SGVsbG8gV29ybGQh\"}` <br /> Expression: `base64_decode(encoded)` <br /> Output: `Hello World!`                                                                                       |                                                                                                                                                                  |\n| `capitalize`         | Capitalizes all the words in the string                                                                                             | Input: `{\"name\": \"john doe\"}` <br /> Expression: `capitalize(name)` <br /> Output: `John Doe`                                                                                                            |                                                                                                                                                                  |\n| `concat`             | Concatenates an array of variables or literals                                                                                      | Input: `{\"fname\": \"john\", \"lname\": \"doe\"}` <br /> Expression: `concat([fname, ' ' ,lname])` <br /> Output: `john doe`                                                                                    | This is equivalent to the more verbose built-in expression: `' '.join([fname,lname])`                                                                            |\n| `filter_entries`     | Filters entries in a dictionary (object) using the given JMESPath predicate                                                         | Input: `{ \"name\": \"John\", \"age\": 30, \"country\": \"US\", \"score\": 15}` <br /> Expression: `` filter_entries(@, `key == 'name' \\|\\| key == 'age'`)``<br /> Output:`{\"name\": \"John\", \"age\": 30 }`             |                                                                                                                                                                  |\n| `from_entries`       | Converts an array of objects with `key` and `value` properties into a single object                                                 | Input: `[{\"key\": \"name\", \"value\": \"John\"}, {\"key\": \"age\", \"value\": 30}, {\"key\": \"city\", \"value\": null}]` <br /> Expression: `from_entries(@)` <br /> Output: `{\"name\": \"John\", \"age\": 30, \"city\": null}` |                                                                                                                                                                  |\n| `hash`               | Calculates a hash using the `hash_name` hash function and returns its hexadecimal representation                                    | Input: `{\"some_str\": \"some_value\"}` <br /> Expression: `hash(some_str, &#96;sha1&#96;)` <br /> Output: `8c8181715...`                                                                                    | Supported algorithms: sha1 (default), sha256, md5, sha384, sha3_384, blake2b, sha512, sha3_224, sha224, sha3_256, sha3_512, blake2s                              |\n| `in`                 | Checks if an element matches any value in a list of values                                                                          | Input: `{\"el\": \"b\"}` <br /> Expression: `in(el, `[\"a\", \"b\", \"c\"]`)` <br /> Output: `True`                                                                                                                |                                                                                                                                                                  |\n| `left`               | Returns a specified number of characters from the start of a given text string                                                      | Input: `{\"greeting\": \"hello world!\"}` <br /> Expression: `left(greeting, `5`)` <br /> Output: `hello`                                                                                                    |                                                                                                                                                                  |\n| `lower`              | Converts all uppercase characters in a string into lowercase characters                                                             | Input: `{\"fname\": \"John\"}` <br /> Expression: `lower(fname)` <br /> Output: `john`                                                                                                                       |                                                                                                                                                                  |\n| `mid`                | Returns a specified number of characters from the middle of a given text string                                                     | Input: `{\"greeting\": \"hello world!\"}` <br /> Expression: `mid(greeting, `4`, `3`)` <br /> Output: `o w`                                                                                                  |                                                                                                                                                                  |\n| `json_parse`         | Returns parsed object from the given JSON string                                                                                    | Input: `{\"data\": '{\"greeting\": \"hello world!\"}'}` <br /> Expression: `json_parse(data)` <br /> Output: `{\"greeting\": \"hello world!\"}`                                                                    |                                                                                                                                                                  |\n| `regex_replace`      | Replaces a string that matches a regular expression                                                                                 | Input: `{\"text\": \"Banana Bannnana\"}` <br /> Expression: `regex_replace(text, 'Ban\\w+', 'Apple Apple')` <br /> Output: `Apple Apple`                                                                      |                                                                                                                                                                  |\n| `replace`            | Replaces all the occurrences of a substring with a new one                                                                          | Input: `{\"sentence\": \"one four three four!\"}` <br /> Expression: `replace(sentence, 'four', 'two')` <br /> Output: `one two three two!`                                                                  |                                                                                                                                                                  |\n| `right`              | Returns a specified number of characters from the end of a given text string                                                        | Input: `{\"greeting\": \"hello world!\"}` <br /> Expression: `right(greeting, `6`)` <br /> Output: `world!`                                                                                                  |                                                                                                                                                                  |\n| `split`              | Splits a string into a list of strings after breaking the given string by the specified delimiter (comma by default)                | Input: `{\"departments\": \"finance,hr,r&d\"}` <br /> Expression: `split(departments)` <br /> Output: `['finance', 'hr', 'r&d']`                                                                             | Default delimiter is comma - a different delimiter can be passed to the function as the second argument, for example: `split(departments, ';')`                  |\n| `time_delta_days`    | Returns the number of days between a given `dt` and now (positive) or the number of days that have passed from now (negative)       | Input: `{\"dt\": '2021-10-06T18:56:16.701670+00:00'}` <br /> Expression: `time_delta_days(dt)` <br /> Output: `365`                                                                                        | If `dt` is a string, ISO datetime (2011-11-04T00:05:23+04:00, for example) is assumed. If `dt` is a number, Unix timestamp (1320365123, for example) is assumed. |\n| `time_delta_seconds` | Returns the number of seconds between a given `dt` and now (positive) or the number of seconds that have passed from now (negative) | Input: `{\"dt\": '2021-10-06T18:56:16.701670+00:00'}` <br /> Expression: `time_delta_days(dt)` <br /> Output: `31557600`                                                                                   | If `dt` is a string, ISO datetime (2011-11-04T00:05:23+04:00, for example) is assumed. If `dt` is a number, Unix timestamp (1320365123, for example) is assumed. |\n| `to_entries`         | Converts a given object into an array of objects with `key` and `value` properties                                                  | Input: `{\"name\": \"John\", \"age\": 30, \"city\": null}` <br /> Expression: `to_entries(@)` <br /> Output: `[{\"key\": \"name\", \"value\": \"John\"}, {\"key\": \"age\", \"value\": 30}, {\"key\": \"city\", \"value\": null}]`   |                                                                                                                                                                  |\n| `upper`              | Converts all lowercase characters in a string into uppercase characters                                                             | Input: `{\"fname\": \"john\"}` <br /> Expression: `upper(fname)` <br /> Output: `JOHN`                                                                                                                       |                                                                                                                                                                  |\n| `uuid`               | Generates a random UUID4 and returns it as a string in standard format                                                              | Input: None <br /> Expression: `uuid()` <br /> Output: `3264b35c-ff5d-44a8-8bc7-9be409dac2b7`                                                                                                            |                                                                                                                                                                  |\n| `xml_to_dict`        | Converts an XML string to a dictionary                                                                                              | Input: `{\"xml\": \"<root><name>John</name><age>30</age></root>\"}` <br /> Expression: `xml_to_dict(xml)` <br /> Output: `{\"root\": {\"name\": \"John\", \"age\": \"30\"}}`                                           | Returns `null` if the input is `null`; returns an empty string if the input is an empty string                                                                   |"}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/release-notes","title":"Release notes","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/","summary":"","aliases":["/integrate/redis-data-integration/ingest/release-notes/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2024-09-10T15:32:02+01:00","children":[{"id":"integrate/redis-data-integration/release-notes/rdi-2-0-0","summary":"Multiple sources in one pipeline, Flink as the default processor, and simpler source mTLS configuration. Database-scoped secrets, per-source reset and cleanup, expanded API diagnostics, and security updates.","title":"Redis Data Integration release notes 2.0.0 (September 2026)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-2-0-0/"},{"id":"integrate/redis-data-integration/release-notes/rdi-1-19-1","summary":"Improved Flink processor recovery, logging, and security.","title":"Redis Data Integration release notes 1.19.1 (August 2026)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-19-1/"},{"id":"integrate/redis-data-integration/release-notes/rdi-1-19-0","summary":"New redis-di CLI for VM, Kubernetes, and Redis Cloud installations. New Redis sorted set and stream outputs for the Flink processor, expanded Snowflake configuration, and Flink processor support for offline VM installations. Improved pipeline status, secret updates, support packages, and error handling.","title":"Redis Data Integration release notes 1.19.0 (July 2026)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-19-0/"},{"id":"integrate/redis-data-integration/release-notes/rdi-1-18-1","summary":"Debezium 3.5.0 upgrade with default collector image updates, Oracle connector compatibility updates, and Microsoft Entra ID authentication support.","title":"Redis Data Integration release notes 1.18.1 (May 2026)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-18-1/"},{"id":"integrate/redis-data-integration/release-notes/rdi-1-18-0","summary":"New Flink-based stream processor for Kubernetes deployments (Preview), with horizontal scaling, Flink checkpointing, and significantly higher throughput. Preview for Snowflake source support for Helm installations, including multi-schema capture and system truststore support. New API v2 endpoints for DLQ inspection, flushing the target database, and CDC-readiness validation. Better deployment reliability, new validation and resource controls, and security refreshes across core images.","title":"Redis Data Integration release notes 1.18.0 (April 2026)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-18-0/"},{"id":"integrate/redis-data-integration/release-notes/rdi-1-16-2","summary":"Maintenance release focusing on allowing the use of RDI with Redis Enterprise databases that have AOF disabled.","title":"Redis Data Integration release notes 1.16.2 (March 2026)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-16-2/"},{"id":"integrate/redis-data-integration/release-notes/rdi-1-16-1","summary":"Maintenance release focusing on TLS certificate handling fixes and API v2 pipeline patching. Includes security hardening for the collector API through dependency upgrades (Spring Boot, Netty, Kafka clients, MySQL connector, Logback, Jetty, Spring Security, Spring Framework).","title":"Redis Data Integration release notes 1.16.1 (February 2026)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-16-1/"},{"id":"integrate/redis-data-integration/release-notes/rdi-1-16-0","summary":"Enhanced disaster recovery for Cloud Spanner with Redis Active-Active, plus better performance and resilience in the Spanner collector. New RDI API v2 dry-run, info, and metrics endpoints for safer rollouts and improved observability. Improved deployment, installation, and troubleshooting, including disk space preflight checks, and broader stability fixes.","title":"Redis Data Integration release notes 1.16.0 (January 2026)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-16-0/"},{"id":"integrate/redis-data-integration/release-notes/rdi-1-15-1","summary":"Fixes support package generation and masking to simplify troubleshooting and log collection. Improves processor throughput during initial sync via parallel processing and new stream tuning parameters. Adds an advanced configuration option to pass custom Java options to the Debezium collector.","title":"Redis Data Integration release notes 1.15.1 (November 2025)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-15-1/"},{"id":"integrate/redis-data-integration/release-notes/rdi-1-15-0","summary":"Flink collector for Spanner enabled by default for improved user experience. Enhanced high availability with configurable leader election and standby mode. Support for sharded RDI Redis databases. Improved configuration validation and monitoring capabilities. Better resource management and security enhancements.","title":"Redis Data Integration release notes 1.15.0 (October 2025)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-15-0/"},{"id":"integrate/redis-data-integration/release-notes/rdi-1-14-1","summary":"New RDI API v2 with enhanced pipeline management. Improved Oracle RAC support with configuration scaffolding. Enhanced metrics and monitoring capabilities. Better TLS/mTLS support across components.","title":"Redis Data Integration release notes 1.14.1 (August 2025)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-14-1/"},{"id":"integrate/redis-data-integration/release-notes/rdi-1-14-0","summary":"New RDI API v2 with enhanced pipeline management. Improved Oracle RAC support with configuration scaffolding. Enhanced metrics and monitoring capabilities. Better TLS/mTLS support across components.","title":"Redis Data Integration release notes 1.14.0 (August 2025)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-14-0/"},{"id":"integrate/redis-data-integration/release-notes/rdi-1-12-3","summary":"Fixed operator excessive CPU usage and log flooding. Fixed reset jobs not being able to pull images from private registries.","title":"Redis Data Integration release notes 1.12.3 (July 2025)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-12-3/"},{"id":"integrate/redis-data-integration/release-notes/rdi-1-12-2","summary":"RDI Collector API now properly supports MariaDB. Extend adding custom labels and annotations to all resources in the RDI Helm chart.","title":"Redis Data Integration release notes 1.12.2 (July 2025)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-12-2/"},{"id":"integrate/redis-data-integration/release-notes/rdi-1-12-1","summary":"Update documentation on pipelines endpoint. Fix deploy pipeline validation issues. /tables API can now group tables by schema. Fix mongo tables endpoint. Fix Oracle /metadata and /columns filters being case sensitive.","title":"Redis Data Integration release notes 1.12.1 (June 2025)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-12-1/"},{"id":"integrate/redis-data-integration/release-notes/rdi-1-12-0","summary":"Enhanced CLI command for HA topology information. Automatic DLQ routing for key errors. Enhanced transformation job validation in the PATCH and POST /api/v1/pipelines endpoints. Added `xml_to_dict` transformation function.","title":"Redis Data Integration release notes 1.12.0 (June 2025)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-12-0/"},{"id":"integrate/redis-data-integration/release-notes/rdi-1-10-0","summary":"Added support for MongoDB as a source, with a scaffold template and enhanced configuration validation. Implemented automatic deletion of pods affected by secret rotation.","title":"Redis Data Integration release notes 1.10.0 (May 2025)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-10-0/"},{"id":"integrate/redis-data-integration/release-notes/rdi-1-8-0","summary":"Enhanced RDI operator for better pipeline orchestration, resiliency, observability and flexibility; External collector support; Labels and annotations to RDI data plane pods; Custom Debezium image; Calculated TTL for target database.","title":"Redis Data Integration release notes 1.8.0 (May 2025)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-8-0/"},{"id":"integrate/redis-data-integration/release-notes/rdi-1-6-7","summary":"Installation on Kubernetes with a Helm chart. Improvements for installation on VMs.","title":"Redis Data Integration release notes 1.6.7 (May 2025)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-6-7/"},{"id":"integrate/redis-data-integration/release-notes/rdi-1-6-6","summary":"Installation on Kubernetes with a Helm chart. Improvements for installation on VMs.","title":"Redis Data Integration release notes 1.6.6 (April 2025)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-6-6/"},{"id":"integrate/redis-data-integration/release-notes/rdi-1-6-5","summary":"Installation on Kubernetes with a Helm chart. Improvements for installation on VMs.","title":"Redis Data Integration release notes 1.6.5 (April 2025)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-6-5/"},{"id":"integrate/redis-data-integration/release-notes/rdi-1-6-4","summary":"Installation on Kubernetes with a Helm chart. Improvements for installation on VMs.","title":"Redis Data Integration release notes 1.6.4 (April 2025)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-6-4/"},{"id":"integrate/redis-data-integration/release-notes/rdi-1-6-3","summary":"Installation on Kubernetes with a Helm chart. Improvements for installation on VMs.","title":"Redis Data Integration release notes 1.6.3 (March 2025)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-6-3/"},{"id":"integrate/redis-data-integration/release-notes/rdi-1-6-2","summary":"Installation on Kubernetes with a Helm chart. Improvements for installation on VMs.","title":"Redis Data Integration release notes 1.6.2 (March 2025)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-6-2/"},{"id":"integrate/redis-data-integration/release-notes/rdi-1-6-1","summary":"Installation on Kubernetes with a Helm chart. Improvements for installation on VMs.","title":"Redis Data Integration release notes 1.6.1 (March 2025)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-6-1/"},{"id":"integrate/redis-data-integration/release-notes/rdi-1-6-0","summary":"Installation on Kubernetes with a Helm chart. Improvements for installation on VMs.","title":"Redis Data Integration release notes 1.6.0 (February 2025)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-6-0/"},{"id":"integrate/redis-data-integration/release-notes/rdi-1-4-4","summary":"Installation on Kubernetes with a Helm chart. Improvements for installation on VMs.","title":"Redis Data Integration release notes 1.4.4 (January 2025)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-4-4/"},{"id":"integrate/redis-data-integration/release-notes/rdi-1-4-3","summary":"Installation on Kubernetes with a Helm chart. Improvements for installation on VMs.","title":"Redis Data Integration release notes 1.4.3 (December 2024)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-4-3/"},{"id":"integrate/redis-data-integration/release-notes/rdi-1-4-2","summary":"Installation on Kubernetes with a Helm chart. Improvements for installation on VMs.","title":"Redis Data Integration release notes 1.4.2 (November 2024)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-4-2/"},{"id":"integrate/redis-data-integration/release-notes/rdi-1-4-1","summary":"Installation on Kubernetes with a Helm chart. Improvements for installation on VMs.","title":"Redis Data Integration release notes 1.4.1 (November 2024)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-4-1/"},{"id":"integrate/redis-data-integration/release-notes/rdi-1-4-0","summary":"Installation on Kubernetes with a Helm chart. Improvements for installation on VMs.","title":"Redis Data Integration release notes 1.4.0 (October 2024)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-4-0/"},{"id":"integrate/redis-data-integration/release-notes/rdi-1-2-8","summary":"API server with a set of APIs to support Redis Insight with creating, testing, deploying & monitoring RDI pipelines. RDI Installer verifies all components are running at the end of installation.","title":"Redis Data Integration release notes 1.2.8 (August 2024)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-2-8/"},{"id":"integrate/redis-data-integration/release-notes/rdi-1-2","summary":"API server with a set of APIs to support Redis Insight with creating, testing, deploying & monitoring RDI pipelines. RDI Installer verifies all components are running at the end of installation.","title":"Redis Data Integration release notes 1.2 (June 2024)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-2/"},{"id":"integrate/redis-data-integration/release-notes/rdi-1-0","summary":"Changes to the processing mode. Simple installation. Silent installation. Pipeline orchestration. Logging. Monitoring. High availability mechanism.","title":"Redis Data Integration release notes 1.0 (June 2024)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-0/"}],"page_type":"content","content_hash":"8c755c91bc7e3cd6c40da3a4fef375290e872b7cc7704dc5b436f84507d8c17c","sections":[{"id":"content","title":"Content","role":"content","text":"Here's what changed recently in Redis Data Integration (RDI):\n\n\n| Version (Release date) | Major changes |\n| --- | --- |\n| [2.0.0 (September 2026)](https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-2-0-0/) | Multiple sources in one pipeline, Flink as the default processor, and simpler source mTLS configuration. Database-scoped secrets, per-source reset and cleanup, expanded API diagnostics, and security updates. |\n| [1.19.1 (August 2026)](https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-19-1/) | Improved Flink processor recovery, logging, and security. |\n| [1.19.0 (July 2026)](https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-19-0/) | New redis-di CLI for VM, Kubernetes, and Redis Cloud installations. New Redis sorted set and stream outputs for the Flink processor, expanded Snowflake configuration, and Flink processor support for offline VM installations. Improved pipeline status, secret updates, support packages, and error handling. |\n| [1.18.1 (May 2026)](https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-18-1/) | Debezium 3.5.0 upgrade with default collector image updates, Oracle connector compatibility updates, and Microsoft Entra ID authentication support. |\n| [1.18.0 (April 2026)](https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-18-0/) | New Flink-based stream processor for Kubernetes deployments (Preview), with horizontal scaling, Flink checkpointing, and significantly higher throughput. Preview for Snowflake source support for Helm installations, including multi-schema capture and system truststore support. New API v2 endpoints for DLQ inspection, flushing the target database, and CDC-readiness validation. Better deployment reliability, new validation and resource controls, and security refreshes across core images. |\n| [1.16.2 (March 2026)](https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-16-2/) | Maintenance release focusing on allowing the use of RDI with Redis Enterprise databases that have AOF disabled. |\n| [1.16.1 (February 2026)](https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-16-1/) | Maintenance release focusing on TLS certificate handling fixes and API v2 pipeline patching. Includes security hardening for the collector API through dependency upgrades (Spring Boot, Netty, Kafka clients, MySQL connector, Logback, Jetty, Spring Security, Spring Framework). |\n| [1.16.0 (January 2026)](https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-16-0/) | Enhanced disaster recovery for Cloud Spanner with Redis Active-Active, plus better performance and resilience in the Spanner collector. New RDI API v2 dry-run, info, and metrics endpoints for safer rollouts and improved observability. Improved deployment, installation, and troubleshooting, including disk space preflight checks, and broader stability fixes. |\n| [1.15.1 (November 2025)](https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-15-1/) | Fixes support package generation and masking to simplify troubleshooting and log collection. Improves processor throughput during initial sync via parallel processing and new stream tuning parameters. Adds an advanced configuration option to pass custom Java options to the Debezium collector. |\n| [1.15.0 (October 2025)](https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-15-0/) | Flink collector for Spanner enabled by default for improved user experience. Enhanced high availability with configurable leader election and standby mode. Support for sharded RDI Redis databases. Improved configuration validation and monitoring capabilities. Better resource management and security enhancements. |\n| [1.14.1 (August 2025)](https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-14-1/) | New RDI API v2 with enhanced pipeline management. Improved Oracle RAC support with configuration scaffolding. Enhanced metrics and monitoring capabilities. Better TLS/mTLS support across components. |\n| [1.14.0 (August 2025)](https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-14-0/) | New RDI API v2 with enhanced pipeline management. Improved Oracle RAC support with configuration scaffolding. Enhanced metrics and monitoring capabilities. Better TLS/mTLS support across components. |\n| [1.12.3 (July 2025)](https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-12-3/) | Fixed operator excessive CPU usage and log flooding. Fixed reset jobs not being able to pull images from private registries. |\n| [1.12.2 (July 2025)](https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-12-2/) | RDI Collector API now properly supports MariaDB. Extend adding custom labels and annotations to all resources in the RDI Helm chart. |\n| [1.12.1 (June 2025)](https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-12-1/) | Update documentation on pipelines endpoint. Fix deploy pipeline validation issues. /tables API can now group tables by schema. Fix mongo tables endpoint. Fix Oracle /metadata and /columns filters being case sensitive. |\n| [1.12.0 (June 2025)](https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-12-0/) | Enhanced CLI command for HA topology information. Automatic DLQ routing for key errors. Enhanced transformation job validation in the PATCH and POST /api/v1/pipelines endpoints. Added `xml_to_dict` transformation function. |\n| [1.10.0 (May 2025)](https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-10-0/) | Added support for MongoDB as a source, with a scaffold template and enhanced configuration validation. Implemented automatic deletion of pods affected by secret rotation. |\n| [1.8.0 (May 2025)](https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-8-0/) | Enhanced RDI operator for better pipeline orchestration, resiliency, observability and flexibility; External collector support; Labels and annotations to RDI data plane pods; Custom Debezium image; Calculated TTL for target database. |\n| [1.6.7 (May 2025)](https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-6-7/) | Installation on Kubernetes with a Helm chart. Improvements for installation on VMs. |\n| [1.6.6 (April 2025)](https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-6-6/) | Installation on Kubernetes with a Helm chart. Improvements for installation on VMs. |\n| [1.6.5 (April 2025)](https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-6-5/) | Installation on Kubernetes with a Helm chart. Improvements for installation on VMs. |\n| [1.6.4 (April 2025)](https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-6-4/) | Installation on Kubernetes with a Helm chart. Improvements for installation on VMs. |\n| [1.6.3 (March 2025)](https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-6-3/) | Installation on Kubernetes with a Helm chart. Improvements for installation on VMs. |\n| [1.6.2 (March 2025)](https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-6-2/) | Installation on Kubernetes with a Helm chart. Improvements for installation on VMs. |\n| [1.6.1 (March 2025)](https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-6-1/) | Installation on Kubernetes with a Helm chart. Improvements for installation on VMs. |\n| [1.6.0 (February 2025)](https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-6-0/) | Installation on Kubernetes with a Helm chart. Improvements for installation on VMs. |\n| [1.4.4 (January 2025)](https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-4-4/) | Installation on Kubernetes with a Helm chart. Improvements for installation on VMs. |\n| [1.4.3 (December 2024)](https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-4-3/) | Installation on Kubernetes with a Helm chart. Improvements for installation on VMs. |\n| [1.4.2 (November 2024)](https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-4-2/) | Installation on Kubernetes with a Helm chart. Improvements for installation on VMs. |\n| [1.4.1 (November 2024)](https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-4-1/) | Installation on Kubernetes with a Helm chart. Improvements for installation on VMs. |\n| [1.4.0 (October 2024)](https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-4-0/) | Installation on Kubernetes with a Helm chart. Improvements for installation on VMs. |\n| [1.2.8 (August 2024)](https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-2-8/) | API server with a set of APIs to support Redis Insight with creating, testing, deploying & monitoring RDI pipelines. RDI Installer verifies all components are running at the end of installation. |\n| [1.2 (June 2024)](https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-2/) | API server with a set of APIs to support Redis Insight with creating, testing, deploying & monitoring RDI pipelines. RDI Installer verifies all components are running at the end of installation. |\n| [1.0 (June 2024)](https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-0/) | Changes to the processing mode. Simple installation. Silent installation. Pipeline orchestration. Logging. Monitoring. High availability mechanism. |"}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/release-notes/rdi-1-0","title":"Redis Data Integration release notes 1.0 (June 2024)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-0/","summary":"Changes to the processing mode. Simple installation. Silent installation. Pipeline orchestration. Logging. Monitoring. High availability mechanism.","aliases":["/integrate/redis-data-integration/ingest/release-notes/rdi-1-0/"],"tags":["docs","operate","rs"],"last_updated":"2024-09-10T15:32:02+01:00","page_type":"content","content_hash":"2841c8140d6809a222c140db023df89366cdf521f951eed1d32c620038dc1d9d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is the first General Availability version of Redis Data Integration (RDI).\n\nRDI’s mission is to help Redis customers sync Redis Enterprise with live data from their slow disk-based databases to:\n\n- Meet the required speed and scale of read queries and provide an excellent and predictable user experience.\n- Save resources and time when building pipelines and coding data transformations.\n- Reduce the total cost of ownership by saving money on expensive database read replicas.\n\nRDI keeps the Redis cache up to date with changes in the primary database, using a\n[_Change Data Capture (CDC)_](https://en.wikipedia.org/wiki/Change_data_capture) mechanism.\nIt also lets you _transform_ the data from relational tables into convenient\nand fast data structures that match your app's requirements. You specify the\ntransformations using a configuration system, so no coding is required."},{"id":"headlines","title":"Headlines","role":"content","text":"- Changes to the processing mode: The preview versions of RDI processed data inside the Redis Enterprise database using the shard CPU. The GA version moves the processing of data outside the cluster. RDI is now deployed on VMs or on Kubernetes (K8s).\n- Simple installation: RDI ships with all of its dependencies. A simple interactive installer provides a streamlined process that takes a few minutes.\n- Silent installation: RDI can be installed by software using a script and an input file.\n- Pipeline orchestration: The preview versions of RDI required you to manually install and configure the Debezium server. In this version, we add support for source database configuration to the pipeline configuration and orchestration of all pipeline components including the Debezium server (RDI Collector).\n- Logging: All RDI component logs are now shipped to a central folder and get rotated by RDI's logging mechanism.\n- Monitoring: RDI comes with two Prometheus exporters, one For Debezium Server and one for RDI's pipeline data processing.\n- High availability mechanism: The preview versions of RDI used an external clustering dependency to provide active-passive deployment of the Debezium server. The GA version has a Redis-based built-in fail-over mechanism between an active VM and a passive VM. Kubernetes deployments rely on K8s probes that are included in RDI components."},{"id":"limitations","title":"Limitations","role":"limits","text":"- RDI can write data to a Redis Active-Active database. However, it doesn't support writing data to two or more Active-Active replicas. Writing data from RDI to several Active-Active replicas could easily harm data integrity as RDI is not synchronous with the source database commits.\n- RDI write-behind (which is currently in preview) should not be used on the same data set that RDI ingest is writing to Redis. This would either cause an infinite loop or would harm the data integrity, since both ingest and write-behind are asynchronous, eventually-consistent processes."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/release-notes/rdi-1-10-0","title":"Redis Data Integration release notes 1.10.0 (May 2025)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-10-0/","summary":"Added support for MongoDB as a source, with a scaffold template and enhanced configuration validation. Implemented automatic deletion of pods affected by secret rotation.","tags":["docs","operate","rs"],"last_updated":"2026-09-18T16:47:10+01:00","page_type":"content","content_hash":"082e98498f1816d1a8a0008fbb0f8489cb4991d27e8f5bcd93f7589066680245","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This minor release replaces the 1.8.0 release.\n\nRDI’s mission is to help Redis customers sync Redis Enterprise with live data from their slow disk-based databases to:\n\n- Meet the required speed and scale of read queries and provide an excellent and predictable user experience.\n- Save resources and time when building pipelines and coding data transformations.\n- Reduce the total cost of ownership by saving money on expensive database read replicas.\n\nRDI keeps the Redis cache up to date with changes in the primary database, using a [_Change Data Capture (CDC)_](https://en.wikipedia.org/wiki/Change_data_capture) mechanism.\nIt also lets you _transform_ the data from relational tables into convenient and fast data structures that match your app's requirements. You specify the transformations using a configuration system, so no coding is required."},{"id":"headlines","title":"Headlines","role":"content","text":"- Added support for MongoDB as a source, including MongoDB 6.0 and 7.0, for both on-premises and MongoDB Atlas deployments.\n- Introduced a MongoDB scaffold template and enhanced configuration validation.\n- Implemented automatic deletion of pods affected by secret rotation to ensure updated secrets are applied."},{"id":"detailed-changes","title":"Detailed changes","role":"content","text":"**Other Features, Improvements and Enhancements**\n\nThe `redis-di scaffold` cli command can now scaffold MongoDB configuration. It can also scaffold different flavors:\n  - `--db-flavor mongodb-atlas`\n  - `--db-flavor mongodb-replica-set`\n  - `--db-flavor mongodb-sharded-cluster`"},{"id":"limitations","title":"Limitations","role":"limits","text":"RDI can write data to a Redis Active-Active database. However, it doesn't support writing data to two or more Active-Active replicas. Writing data from RDI to several Active-Active replicas could easily harm data integrity as RDI is not synchronous with the source database commits."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/release-notes/rdi-1-12-0","title":"Redis Data Integration release notes 1.12.0 (June 2025)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-12-0/","summary":"Enhanced CLI command for HA topology information. Automatic DLQ routing for key errors. Enhanced transformation job validation in the PATCH and POST /api/v1/pipelines endpoints. Added `xml_to_dict` transformation function.","tags":["docs","operate","rs"],"last_updated":"2026-09-18T16:47:10+01:00","page_type":"content","content_hash":"210e5c61e40a3480bb110116b8ad9b792be9e1280ab40f4bfe0659d84177ee93","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This minor release replaces the 1.10.0 release.\n\nRDI’s mission is to help Redis customers sync Redis Enterprise with live data from their slow disk-based databases to:\n\n- Meet the required speed and scale of read queries and provide an excellent and predictable user experience.\n- Save resources and time when building pipelines and coding data transformations.\n- Reduce the total cost of ownership by saving money on expensive database read replicas.\n\nRDI keeps the Redis cache up to date with changes in the primary database, using a [_Change Data Capture (CDC)_](https://en.wikipedia.org/wiki/Change_data_capture) mechanism.\nIt also lets you _transform_ the data from relational tables into convenient and fast data structures that match your app's requirements. You specify the transformations using a configuration system, so no coding is required."},{"id":"headlines","title":"Headlines","role":"content","text":"- Enhanced CLI status command to display RDI High Availability (HA) topology information.\n- Improved error handling with automatic DLQ routing for wrong Redis key errors.\n- Enhanced transformation job validation in the `PATCH` and `POST` `/api/v1/pipelines`\n  endpoints with better error reporting.\n- Added `xml_to_dict` transformation function for converting XML strings to JSON objects."},{"id":"detailed-changes","title":"Detailed changes","role":"content","text":"**CLI and Monitoring**\n\n- The `redis-di status` command now displays High Availability (HA) information for cluster status visibility.\n- Fixed `rdi_version_info` metric to export periodically instead of only at startup.\n- Status API: Fixed issue - pipeline status not shifting from snapshot to streaming after restart or reset\n\n**Validation and Error Handling**\n\n- Static configuration and job schema validation provides clearer error messages.\n- Pipeline endpoints validate source and job configurations against database metadata, preventing mismatches:\n  - Error in table names or table doesn't exist.\n  - More than one job per table.\n- Automatic DLQ routing for wrong Redis key errors.\n- Support for secret rotation. Pods are recreated when a secret rotates.\n- RDI validates that its Redis database is not sharded when RDI starts.\n\n**Database Connectivity**\n\n- MySQL connector filters system tables for cleaner metadata.\n- Collector API supports multiple database and schema entries for multi-database scenarios.\n- `/api/v1/pipelines/{source_name}/metadata` endpoint supports table name filtering.\n- `/api/v1/pipelines/{source_name}/tables` endpoint supports table name filtering.\n\n**API and Infrastructure**\n\n- Pipeline endpoints (source, targets, secret-providers) support key deletion via `PATCH` operations.\n- Helm chart supports lists of ingress hosts.\n- Operator handles missing SPCPS CRD gracefully."},{"id":"limitations","title":"Limitations","role":"limits","text":"RDI can write data to a Redis Active-Active database. However, it doesn't support writing data to two or more Active-Active replicas. Writing data from RDI to several Active-Active replicas could easily harm data integrity as RDI is not synchronous with the source database commits."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/release-notes/rdi-1-12-1","title":"Redis Data Integration release notes 1.12.1 (June 2025)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-12-1/","summary":"Update documentation on pipelines endpoint. Fix deploy pipeline validation issues. /tables API can now group tables by schema. Fix mongo tables endpoint. Fix Oracle /metadata and /columns filters being case sensitive.","tags":["docs","operate","rs"],"last_updated":"2026-09-18T16:47:10+01:00","page_type":"content","content_hash":"8a9a28a0819b4d37cb43d758a7cf4cfb04705c7348b5adb6c180ff54d9d4f60f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This maintenance release replaces the 1.12.0 release.\n\nRDI’s mission is to help Redis customers sync Redis Enterprise with live data from their slow disk-based databases to:\n\n- Meet the required speed and scale of read queries and provide an excellent and predictable user experience.\n- Save resources and time when building pipelines and coding data transformations.\n- Reduce the total cost of ownership by saving money on expensive database read replicas.\n\nRDI keeps the Redis cache up to date with changes in the primary database, using a [_Change Data Capture (CDC)_](https://en.wikipedia.org/wiki/Change_data_capture) mechanism.\nIt also lets you _transform_ the data from relational tables into convenient and fast data structures that match your app's requirements. You specify the transformations using a configuration system, so no coding is required."},{"id":"headlines","title":"Headlines","role":"content","text":"- Update documentation on pipelines endpoint.\n- Fix deploy pipeline validation issues.\n- `/tables` API now groups tables by schema if supported; otherwise, by database catalog.\n- Fix mongo tables endpoint.\n- Fix Oracle `/metadata` and `/columns` filters being case sensitive."},{"id":"limitations","title":"Limitations","role":"limits","text":"RDI can write data to a Redis Active-Active database. However, it doesn't support writing data to two or more Active-Active replicas. Writing data from RDI to several Active-Active replicas could easily harm data integrity as RDI is not synchronous with the source database commits."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/release-notes/rdi-1-12-2","title":"Redis Data Integration release notes 1.12.2 (July 2025)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-12-2/","summary":"RDI Collector API now properly supports MariaDB. Extend adding custom labels and annotations to all resources in the RDI Helm chart.","tags":["docs","operate","rs"],"last_updated":"2026-09-18T16:47:10+01:00","page_type":"content","content_hash":"0be5c3ee69b9d2101680690cd37935f94f444b29428fdacf59f4a0a70b5d231d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This maintenance release replaces the 1.12.1 release.\n\nRDI’s mission is to help Redis customers sync Redis Enterprise with live data from their slow disk-based databases to:\n\n- Meet the required speed and scale of read queries and provide an excellent and predictable user experience.\n- Save resources and time when building pipelines and coding data transformations.\n- Reduce the total cost of ownership by saving money on expensive database read replicas.\n\nRDI keeps the Redis cache up to date with changes in the primary database, using a [_Change Data Capture (CDC)_](https://en.wikipedia.org/wiki/Change_data_capture) mechanism.\nIt also lets you _transform_ the data from relational tables into convenient and fast data structures that match your app's requirements. You specify the transformations using a configuration system, so no coding is required."},{"id":"headlines","title":"Headlines","role":"content","text":"-   RDI Collector API now properly supports MariaDB.\n-   Extend adding custom labels and annotations to all resources in the RDI Helm chart. Includes the webhook certificate management jobs."},{"id":"limitations","title":"Limitations","role":"limits","text":"RDI can write data to a Redis Active-Active database. However, it doesn't support writing data to two or more Active-Active replicas. Writing data from RDI to several Active-Active replicas could easily harm data integrity as RDI is not synchronous with the source database commits."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/release-notes/rdi-1-12-3","title":"Redis Data Integration release notes 1.12.3 (July 2025)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-12-3/","summary":"Fixed operator excessive CPU usage and log flooding. Fixed reset jobs not being able to pull images from private registries.","tags":["docs","operate","rs"],"last_updated":"2026-09-18T16:47:10+01:00","page_type":"content","content_hash":"79d4c2ae089744b9aaf2f82130da9eec93377e3474c61a9949b1abbdc4379d6a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This maintenance release replaces the 1.12.2 release.\n\nRDI’s mission is to help Redis customers sync Redis Enterprise with live data from their slow disk-based databases to:\n\n- Meet the required speed and scale of read queries and provide an excellent and predictable user experience.\n- Save resources and time when building pipelines and coding data transformations.\n- Reduce the total cost of ownership by saving money on expensive database read replicas.\n\nRDI keeps the Redis cache up to date with changes in the primary database, using a [_Change Data Capture (CDC)_](https://en.wikipedia.org/wiki/Change_data_capture) mechanism.\nIt also lets you _transform_ the data from relational tables into convenient and fast data structures that match your app's requirements. You specify the transformations using a configuration system, so no coding is required."},{"id":"headlines","title":"Headlines","role":"content","text":"-   Fixed operator excessive CPU usage and log flooding caused by annotation conflict between\n    PipelineRelease and Kubernetes deployment controllers.\n-   Fixed reset jobs not being able to pull images from private registries due to missing\n    image pull secrets."},{"id":"limitations","title":"Limitations","role":"limits","text":"RDI can write data to a Redis Active-Active database. However, it doesn't support writing data to two or more Active-Active replicas. Writing data from RDI to several Active-Active replicas could easily harm data integrity as RDI is not synchronous with the source database commits."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/release-notes/rdi-1-14-0","title":"Redis Data Integration release notes 1.14.0 (August 2025)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-14-0/","summary":"New RDI API v2 with enhanced pipeline management. Improved Oracle RAC support with configuration scaffolding. Enhanced metrics and monitoring capabilities. Better TLS/mTLS support across components.","tags":["docs","operate","rs"],"last_updated":"2025-08-18T16:28:57+03:00","page_type":"content","content_hash":"79243e0e486f163d158a84d390fc74a43742948b83caa057f071357ea7ae77e7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"RDI’s mission is to help Redis customers sync Redis Enterprise with live data from their slow disk-based databases to:\n\n- Meet the required speed and scale of read queries and provide an excellent and predictable user experience.\n- Save resources and time when building pipelines and coding data transformations.\n- Reduce the total cost of ownership by saving money on expensive database read replicas.\n\nRDI keeps the Redis cache up to date with changes in the primary database, using a [_Change Data Capture (CDC)_](https://en.wikipedia.org/wiki/Change_data_capture) mechanism.\nIt also lets you _transform_ the data from relational tables into convenient and fast data structures that match your app's requirements. You specify the transformations using a configuration system, so no coding is required."},{"id":"whats-new-in-1140","title":"What's New in 1.14.0","role":"content","text":"- Added support for Google Cloud Spanner databases:\n  - Compatible with both Google SQL and PostgreSQL dialects\n  - Secure data collection through Apache Flink with built-in TLS/mTLS support\n- Fixing: MariaDB TLS connection not working when using TLS to the RDI database\n- Introduced RDI operator metrics for better monitoring and troubleshooting of the work of the operator\n- The `redis-di status` command now shows a detailed breakdown of the processor processing times:\n  - Separate `Transform time` metric showing how long data transformations take\n  - Distinct `Write time` metric indicating Redis write operation duration"},{"id":"limitations","title":"Limitations","role":"limits","text":"RDI can write data to a Redis Active-Active database. However, it doesn't support writing data to two or more Active-Active replicas. Writing data from RDI to several Active-Active replicas could easily harm data integrity as RDI is not synchronous with the source database commits."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/release-notes/rdi-1-14-1","title":"Redis Data Integration release notes 1.14.1 (August 2025)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-14-1/","summary":"New RDI API v2 with enhanced pipeline management. Improved Oracle RAC support with configuration scaffolding. Enhanced metrics and monitoring capabilities. Better TLS/mTLS support across components.","tags":["docs","operate","rs"],"last_updated":"2026-09-18T16:47:10+01:00","page_type":"content","content_hash":"2fed622f15350a0ba1bd7645706f25ff61e40ff90edd21f711556596d859bd86","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This maintenance release replaces the 1.14.0 release.\n\nRDI’s mission is to help Redis customers sync Redis Enterprise with live data from their slow disk-based databases to:\n\n- Meet the required speed and scale of read queries and provide an excellent and predictable user experience.\n- Save resources and time when building pipelines and coding data transformations.\n- Reduce the total cost of ownership by saving money on expensive database read replicas.\n\nRDI keeps the Redis cache up to date with changes in the primary database, using a [_Change Data Capture (CDC)_](https://en.wikipedia.org/wiki/Change_data_capture) mechanism.\nIt also lets you _transform_ the data from relational tables into convenient and fast data structures that match your app's requirements. You specify the transformations using a configuration system, so no coding is required."},{"id":"whats-new-in-1141","title":"What's New in 1.14.1","role":"content","text":"- Support for Google Cloud Workload Identity authentication when a service account is assigned to the GKE cluster\n- Fixed RDI API job validation that was incorrectly failing when schemas are not explicitly specified in source configuration, even though the configuration was valid"},{"id":"limitations","title":"Limitations","role":"limits","text":"RDI can write data to a Redis Active-Active database. However, it doesn't support writing data to two or more Active-Active replicas. Writing data from RDI to several Active-Active replicas could easily harm data integrity as RDI is not synchronous with the source database commits."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/release-notes/rdi-1-15-0","title":"Redis Data Integration release notes 1.15.0 (October 2025)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-15-0/","summary":"Flink collector for Spanner enabled by default for improved user experience. Enhanced high availability with configurable leader election and standby mode. Support for sharded RDI Redis databases. Improved configuration validation and monitoring capabilities. Better resource management and security enhancements.","tags":["docs","operate","rs"],"last_updated":"2026-09-18T16:47:10+01:00","page_type":"content","content_hash":"12e68be3ddfaa65fc40c336b4f754afed4c0364179e68a5ae4d0a5918c4d5854","sections":[{"id":"overview","title":"Overview","role":"overview","text":"RDI's mission is to help Redis customers sync Redis Enterprise with live data from their slow disk-based databases to:\n\n- Meet the required speed and scale of read queries and provide an excellent and predictable user experience.\n- Save resources and time when building pipelines and coding data transformations.\n- Reduce the total cost of ownership by saving money on expensive database read replicas.\n\nRDI keeps the Redis cache up to date with changes in the primary database, using a [_Change Data Capture (CDC)_](https://en.wikipedia.org/wiki/Change_data_capture) mechanism.\nIt also lets you _transform_ the data from relational tables into convenient and fast data structures that match your app's requirements. You specify the transformations using a configuration system, so no coding is required."},{"id":"whats-new-in-1150","title":"What's New in 1.15.0","role":"content","text":"> [!WARNING]\n> **Breaking change when using JSON with `json_update_strategy: merge`**\n>\n> RDI now uses the native `JSON.MERGE` command instead of Lua scripts for JSON merge operations. While this provides significant performance improvements (2x faster), there is a **functional difference** in how null values are handled:\n>\n> - **Previous behavior (Lua merge)**: When merging `{\"field1\": \"value1\", \"field2\": \"value2\"}` with `{\"field2\": null, \"field3\": \"value3\"}`, the result was `{\"field1\": \"value1\", \"field2\": null, \"field3\": \"value3\"}` (null value is preserved)\n> - **New behavior (JSON.MERGE)**: The same merge produces `{\"field1\": \"value1\", \"field3\": \"value3\"}` (null value removes the field, following [RFC 7396](https://datatracker.ietf.org/doc/html/rfc7396))\n>\n> **Impact**: If your application logic distinguishes between a field with a `null` value and a missing field, you may need to adjust your data handling. This follows the JSON Merge Patch RFC standard but differs from the previous Lua implementation.\n>\n> **Configuration**: You can control this behavior using the `use_native_json_merge` property in the processors section of your configuration. Set it to `false` to revert to the previous Lua-based merge behavior if needed.\n\n- **Native JSON merge for improved performance**: RDI now automatically uses the native `JSON.MERGE` command from RedisJSON 2.6.0+ instead of Lua scripts for JSON merge operations, providing 2x performance improvement. This feature is enabled by default and can be controlled via the `use_native_json_merge` property in the processors section of the configuration. **Note**: If the target Redis database has RedisJSON version lower than 2.6.0, the processor will automatically revert to using the Lua-based merge implementation.\n- **Support for sharded Redis databases**: RDI now supports writing to multi-sharded Redis Enterprise databases for the RDI database, resolving cross-slot violations when reading from streams.\n- **Enhanced processor performance metrics**: Detailed performance metrics are now exposed through the metrics exporter and statistics endpoint, with separate tracking for transformation time and write time.\n- **Resource management improvements**: Collector and processor pods now support configurable resource requests, limits, and node affinity/tolerations for better cluster resource utilization.\n  - The `collector` defaults to 1 CPU and 1024Mi memory (requests), with limits of 4 CPUs and 4096Mi memory.\n  - The `processor` defaults to 1 CPU and 512Mi memory (requests), with limits of 4 CPUs and 3072Mi memory.\n\n- **Leadership status monitoring**: New metrics expose leadership status and pipeline phase information for better monitoring of HA deployments.\n  - The `rdi_operator_is_leader` metric tracks the current leadership status of the operator: `1` indicates the instance is the leader, `0` indicates it is not the leader.\n  - The `rdi_operator_pipeline_phase` metric tracks the current phase of the pipeline. Phase indicates the current pipeline phase, must be one of `Active`, `Inactive`, `Resetting`, `Pending`, or `Error`.\n- **Improved configuration validation**: More rigid validation for `config.yaml` and `jobs.yaml` files helps catch configuration errors earlier in the deployment process.\n- **Custom K3s installation options**: The installer now supports passing custom arguments to K3s installation for more flexible on-premises deployments.\n  - Example: `sudo INSTALL_K3S_EXEC='--write-kubeconfig-mode=644' ./install.sh`\n- **Workload Identity authentication**: Added support for Google Cloud Workload Identity authentication to Google Cloud Storage (GCS), eliminating the need for service account JSON files if using GSC-based leader election.\n- Exposed new processor performance metrics showing transformation and write times separately\n  - `{namespace}_processor_process_time_ms_total` - Total time spent in the processor (transform + write)\n  - `{namespace}_processor_transform_time_ms_total` - Time spent transforming data\n  - `{namespace}_processor_write_time_ms_total` - Time spent writing data to Redis\n- Exposed all existing processor metrics that were only available through the RDI CLI status and the API statistics endpoint.\n- Enhanced statistics endpoint with new metrics for transform and process time"},{"id":"bug-fixes-and-stability-improvements","title":"Bug Fixes and Stability Improvements","role":"content","text":"- **Fixed task reconciliation errors**: Resolved \"The ID argument cannot be a complete ID because xadd-id-uniqueness-mode is strict\" errors during task reconciliation when using an RDI Redis database with strict XADD id uniqueness mode.\n- **Fixed Debezium unavailable values**: Addressed issues where `__debezium_unavailable_value` was appearing in Redis data.\n- **Improved operator stability**: Disabled operator webhooks by default to simplify deployments and reduce potential issues."},{"id":"limitations","title":"Limitations","role":"limits","text":"RDI can write data to a Redis Active-Active database. However, it doesn't support writing data to two or more Active-Active replicas. Writing data from RDI to several Active-Active replicas could easily harm data integrity as RDI is not synchronous with the source database commits."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/release-notes/rdi-1-15-1","title":"Redis Data Integration release notes 1.15.1 (November 2025)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-15-1/","summary":"Fixes support package generation and masking to simplify troubleshooting and log collection. Improves processor throughput during initial sync via parallel processing and new stream tuning parameters. Adds an advanced configuration option to pass custom Java options to the Debezium collector.","tags":["docs","operate","rs"],"last_updated":"2026-09-18T16:47:10+01:00","page_type":"content","content_hash":"9c5e542e2a695d10a26ed7f20767b028c308a4ecc3eaa818bbe2ec0bdcbfb11c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This maintenance release replaces the 1.15.0 release.\n\nRDI’s mission is to help Redis customers sync Redis Enterprise with live data from their slow disk-based databases to:\n\n- Meet the required speed and scale of read queries and provide an excellent and predictable user experience.\n- Save resources and time when building pipelines and coding data transformations.\n- Reduce the total cost of ownership by saving money on expensive database read replicas.\n\nRDI keeps the Redis cache up to date with changes in the primary database, using a [_Change Data Capture (CDC)_](https://en.wikipedia.org/wiki/Change_data_capture) mechanism.\nIt also lets you _transform_ the data from relational tables into convenient and fast data structures that match your app's requirements. You specify the transformations using a configuration system, so no coding is required."},{"id":"whats-new-in-1151","title":"What's New in 1.15.1","role":"content","text":"- Fix dump support package problems\n  - Fix improper namespace passing to the kubectl command\n  - Mask passwords from sensitive configs\n  - Dump masked pipeline information\n  - Mask and dump audit logs collected from RDI DB rdi:tasks streams\n- Processor performance improvements - Increase throughput during initial snapshot by ~50% during initial sync\n  - Added parameter - `read_batch_timeout_ms` to the processor configuration to control the timeout for reading from the RDI streams. The default is 100ms.\n  - `read_batch_size` is now functional. In previous versions, the processor would always read a batch size of 2000 from the stream, regardless of the `read_batch_size` value.\n- Added parameter `sources.advanced.java_options` to the `config.yaml` file to pass additional options to the Debezium collector command line e.g. increasing heap size."},{"id":"limitations","title":"Limitations","role":"limits","text":"RDI can write data to a Redis Active-Active database. However, it doesn't support writing data to two or more Active-Active replicas. Writing data from RDI to several Active-Active replicas could easily harm data integrity as RDI is not synchronous with the source database commits."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/release-notes/rdi-1-16-0","title":"Redis Data Integration release notes 1.16.0 (January 2026)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-16-0/","summary":"Enhanced disaster recovery for Cloud Spanner with Redis Active-Active, plus better performance and resilience in the Spanner collector. New RDI API v2 dry-run, info, and metrics endpoints for safer rollouts and improved observability. Improved deployment, installation, and troubleshooting, including disk space preflight checks, and broader stability fixes.","tags":["docs","operate","rs"],"last_updated":"2026-01-20T10:45:26Z","page_type":"content","content_hash":"e42a68721b02a88735ab6239e91fe58d022fdca862759e31614ae4d5ddc00c08","sections":[{"id":"overview","title":"Overview","role":"overview","text":"RDI's mission is to help Redis customers sync Redis Enterprise with live data from their slow disk-based databases to:\n\n- Meet the required speed and scale of read queries and provide an excellent and predictable user experience.\n- Save resources and time when building pipelines and coding data transformations.\n- Reduce the total cost of ownership by saving money on expensive database read replicas.\n\nRDI keeps the Redis cache up to date with changes in the primary database, using a [_Change Data Capture (CDC)_](https://en.wikipedia.org/wiki/Change_data_capture) mechanism.\nIt also lets you _transform_ the data from relational tables into convenient and fast data structures that match your app's requirements. You specify the transformations using a configuration system, so no coding is required."},{"id":"whats-new-in-1160","title":"What's New in 1.16.0","role":"content","text":"- **External secrets mount status tracking**: The operator tracks and surfaces the status of external secrets mounts, helping you diagnose issues with secrets providers more quickly.\n- **Improved metrics exposure for VM installs**: RDI annotates ingresses during VM-based installations so that metrics are automatically exposed for monitoring systems.\n- **Debezium upgrade**: Upgraded Debezium to version 3.3.1, bringing connector fixes and performance improvements from the upstream project.\n- **Installer preflight disk space checks**: The installer now checks for sufficient disk space and `noexe` permissions on the `/tmp` directory before starting the installation, reducing failures due to insufficient capacity."},{"id":"rdi-api-changes","title":"RDI API Changes","role":"content","text":"- **Dry-run support in API v2**: RDI API v2 endpoints now supports dry-run mode, allowing you to validate pipeline configuration and connectivity without applying changes. They also now return validation errors instead of generic failures when the request body is missing or invalid.\n- **Expanded API v2 observability and metadata**: RDI API v2 now exposes additional information and metrics:\n  - New `/info` endpoint that returns the RDI version. This endpoint does not require authentication and can be used to check RDI availability.\n  - New `/metric-collections` endpoints that describe available metric collections.\n  - Pipeline `/api/v2/pipelines/{name}` and status `/api/v2/pipelines/{name}/status` responses now include component-level information.\n  - Metric collection selection is now based on component type instead of name.\n- **API correctness and compatibility improvements**: Ensured backwards compatibility between API v1 and v2 and fixed issues where API v2 source/target dry-run endpoints could return HTTP 500 for certain invalid requests.\n- **Environment variable handling for monitoring statistics**: Fixed an issue where `/api/v1/monitoring/statistics` could return HTTP 500 when the target database port was specified as an environment variable."},{"id":"spanner-integration-with-flink-collector","title":"Spanner Integration with Flink Collector","role":"content","text":"- **Enhanced Spanner disaster recovery with Redis Active-Active**: When using Cloud Spanner as a CDC source with a Redis Active-Active database, the Flink collector now handles long-running workloads and failover scenarios more reliably, improving the overall ability to handle disaster recovery scenarios.\n- **Spanner client resource management**: Spanner clients are now reused per database instead of being created repeatedly, eliminating a memory leak and reducing connection churn on Spanner.\n- **Flink collector memory tuning**: Increased the default process and heap size for the Flink collector and tuned memory fractions for better performance and stability.\n- **Resilient Redis operations by the Flink collector**: The Flink collector now uses configurable connection and socket timeouts, as well as retries with exponential backoff for Redis operations, for improved resilience. It also supports waiting for a configurable number of replicas when writing for improved durability, and handles Redis out-of-memory errors as backpressure instead of failures."},{"id":"bug-fixes-and-stability-improvements","title":"Bug Fixes and Stability Improvements","role":"content","text":"- **Improved support package generation**: Fixed several issues in the support package dump process so that diagnostic bundles are generated reliably, including rotated logs. Added a new `--log-days` option to specify the number of days of logs to collect.\n- **Fixed log level configuration**: The `redis-di configure` command now correctly applies the `rdi-log-level` setting.\n- **Enhanced processor error handling**: The processor now fails with a clear error when it cannot connect to the target database after a timeout and writes such errors to the pod termination log to expose them.\n- **Improved pipeline error detection**: Pipelines now transition to an `Error` status when deployments are not available after 2 minutes, exposing failures sooner.\n- **Fixed empty password handling**: Resolved issues with RDI commands when using empty passwords.\n- **Operator namespace isolation**: The RDI operator now only reconciles resources within the RDI namespace, improving performance and isolation. It also no longer requires cluster-level permissions for any resources, reducing security concerns for enterprise Kubernetes deployments."},{"id":"limitations","title":"Limitations","role":"limits","text":"RDI can write data to a Redis Active-Active database. However, it doesn't support writing data to two or more Active-Active replicas. Writing data from RDI to several Active-Active replicas could easily harm data integrity as RDI is not synchronous with the source database commits."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/release-notes/rdi-1-16-1","title":"Redis Data Integration release notes 1.16.1 (February 2026)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-16-1/","summary":"Maintenance release focusing on TLS certificate handling fixes and API v2 pipeline patching. Includes security hardening for the collector API through dependency upgrades (Spring Boot, Netty, Kafka clients, MySQL connector, Logback, Jetty, Spring Security, Spring Framework).","tags":["docs","operate","rs"],"last_updated":"2026-09-18T16:47:10+01:00","page_type":"content","content_hash":"6e33e6247fe035b9a6ebeb9f95c4b8306a7a79ba7260cfdd24b147c03f4ca6e0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This maintenance release replaces the 1.16.0 release.\n\nRDI's mission is to help Redis customers sync Redis Enterprise with live data from their slow disk-based databases to:\n\n- Meet the required speed and scale of read queries and provide an excellent and predictable user experience.\n- Save resources and time when building pipelines and coding data transformations.\n- Reduce the total cost of ownership by saving money on expensive database read replicas.\n\nRDI keeps the Redis cache up to date with changes in the primary database, using a [_Change Data Capture (CDC)_](https://en.wikipedia.org/wiki/Change_data_capture) mechanism.\nIt also lets you _transform_ the data from relational tables into convenient and fast data structures that match your app's requirements. You specify the transformations using a configuration system, so no coding is required."},{"id":"whats-new-in-1161","title":"What's New in 1.16.1","role":"content","text":"- **Improved CA certificate chain handling from external secrets**: When importing CA certificates from external secrets mounts, RDI now correctly loads the full certificate chain instead of only the first certificate. This ensures proper trust configuration for TLS connections that rely on intermediate CAs.\n- **More flexible API v2 pipeline patching**: Fixed API v2 pipeline PATCH behavior so that you can remove tables and table properties by sending `null` values. This aligns the API with the documented semantics for partial updates and makes it easier to clean up pipeline configurations.\n- **Security hardening for collector API dependencies**: Updated the collector API to newer, supported versions of key dependencies to address multiple CVEs and improve overall security posture, including:\n  - Spring Boot (to 3.3.11), which also brings updated Spring Framework and Spring Security components.\n  - Netty (to 4.1.125.Final).\n  - Kafka clients (to 3.9.1).\n  - MySQL Connector/J (to 8.2.0).\n  - Logback (to 1.5.18) and Jetty (to 12.0.19) via the Spring Boot upgrade."},{"id":"limitations","title":"Limitations","role":"limits","text":"RDI can write data to a Redis Active-Active database. However, it doesn't support writing data to two or more Active-Active replicas. Writing data from RDI to several Active-Active replicas could easily harm data integrity as RDI is not synchronous with the source database commits."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/release-notes/rdi-1-16-2","title":"Redis Data Integration release notes 1.16.2 (March 2026)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-16-2/","summary":"Maintenance release focusing on allowing the use of RDI with Redis Enterprise databases that have AOF disabled.","tags":["docs","operate","rs"],"last_updated":"2026-09-18T16:47:10+01:00","page_type":"content","content_hash":"25a4fd02e647c80efa3a0f0ede49b8787ebddd7f9d344e40058616735a9ef71c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This maintenance release replaces the 1.16.1 release.\n\nRDI's mission is to help Redis customers sync Redis Enterprise with live data from their slow disk-based databases to:\n\n- Meet the required speed and scale of read queries and provide an excellent and predictable user experience.\n- Save resources and time when building pipelines and coding data transformations.\n- Reduce the total cost of ownership by saving money on expensive database read replicas.\n\nRDI keeps the Redis cache up to date with changes in the primary database, using a [_Change Data Capture (CDC)_](https://en.wikipedia.org/wiki/Change_data_capture) mechanism.\nIt also lets you _transform_ the data from relational tables into convenient and fast data structures that match your app's requirements. You specify the transformations using a configuration system, so no coding is required."},{"id":"whats-new-in-1162","title":"What's New in 1.16.2","role":"content","text":"- **New configuration option to disable AOF check:** A new prerequisiteChecks section is introduced in the operator section of the Helm values file. You can use it to disable the AOF prerequisite check when running RDI against a Redis RDI database that does not have AOF enabled. Please note that disabling this check without understanding the implications may lead to data loss in certain failure scenarios, so it should be used with caution."},{"id":"limitations","title":"Limitations","role":"limits","text":"RDI can write data to a Redis Active-Active database. However, it doesn't support writing data to two or more Active-Active replicas. Writing data from RDI to several Active-Active replicas could easily harm data integrity as RDI is not synchronous with the source database commits."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/release-notes/rdi-1-18-0","title":"Redis Data Integration release notes 1.18.0 (April 2026)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-18-0/","summary":"New Flink-based stream processor for Kubernetes deployments (Preview), with horizontal scaling, Flink checkpointing, and significantly higher throughput. Preview for Snowflake source support for Helm installations, including multi-schema capture and system truststore support. New API v2 endpoints for DLQ inspection, flushing the target database, and CDC-readiness validation. Better deployment reliability, new validation and resource controls, and security refreshes across core images.","tags":["docs","operate","rs"],"last_updated":"2026-09-18T16:47:10+01:00","page_type":"content","content_hash":"d13107672d223fd810ffb3637cd0f6525df4463742d589669c94747c16732d85","sections":[{"id":"whats-new-in-1180","title":"What's New in 1.18.0","role":"overview","text":""},{"id":"compatibility-notes","title":"Compatibility Notes","role":"compatibility","text":"- **`rdi-metrics-exporter` moved to the data plane**: The `rdi-metrics-exporter` is now deployed by the pipeline Helm chart (managed by the operator) instead of the main RDI Helm chart. Helm values previously under the top-level `rdiMetricsExporter:` block must be moved under `operator.dataPlane.metricsExporter:` in your custom values file. During the upgrade, there will be a brief (seconds) gap in Prometheus scraping that does not affect the data path. The exporter is not deployed for pipelines using the new Flink processor."},{"id":"flink-processor","title":"Flink Processor","role":"content","text":"- **New Flink-based stream processor for Kubernetes (Preview)**: RDI 1.18.0 introduces a new stream processor implementation built on [Apache Flink](https://flink.apache.org/) alongside the existing classic processor. The Flink processor delivers significantly higher snapshot throughput, lower end-to-end latency, horizontal scaling across TaskManager replicas and task slots, and Flink checkpointing on top of the same at-least-once delivery guarantees. The Flink processor is available as a Preview and is not yet supported for production use; we encourage you to try it on new, non-production pipelines and share feedback so we can prioritize improvements before general availability. Regular preview terms apply. To enable it, set `processors.type: flink` in your pipeline configuration. The Flink processor is available for Kubernetes deployments only and currently supports the `hash` and `json` target data types. See [Classic vs. Flink processor](https://redis.io/docs/latest/integrate/redis-data-integration/architecture/classic-vs-flink) for a full comparison and [Migrate from the classic processor to the Flink processor](https://redis.io/docs/latest/integrate/redis-data-integration/installation/migration-classic-to-flink) for the step-by-step migration guide."},{"id":"snowflake-and-source-integration","title":"Snowflake and Source Integration","role":"content","text":"- **Snowflake source support for Helm installations (Preview)**: RDI now supports [Snowflake](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/snowflake) as a source in Helm-based installations, including capture from multiple schemas in a single pipeline. Regular preview terms apply. Snowflake sources are not yet supported for VM installations. RDI can also use well-known root CA certificates from the system truststore, reducing the need for manual certificate configuration for cloud-hosted source databases.\n- **Collector resource reservation controls**: A new `sources.advanced.resources` section lets you control memory and CPU reservation for the collector.\n- **CDC-readiness validation in API v2**: RDI API v2 can optionally validate whether MariaDB, MySQL, PostgreSQL, SQL Server, Oracle, and MongoDB sources are ready for CDC as part of pipeline validation. This is available on pipeline create, update, and patch requests by using the `validate_cdc` query parameter, including dry-run requests. Spanner and Snowflake sources are not supported for this validation. The validation is available through API v2 only; the CLI and Redis Insight do not expose it yet."},{"id":"rdi-api","title":"RDI API","role":"content","text":"- **DLQ inspection endpoints in API v2**: RDI API v2 now exposes endpoints to inspect Dead Letter Queue (DLQ) data programmatically:\n  - `GET /api/v2/pipelines/{name}/dlqs` lists tables that currently have DLQ records with their counts.\n  - `GET /api/v2/pipelines/{name}/dlqs/{full_table_name}` returns the DLQ count for a specific table.\n  - `GET /api/v2/pipelines/{name}/dlqs/{full_table_name}/records` returns DLQ records for a specific table with pagination, sort order control, and optional field projection.\n- **Flush target endpoint in API v2**: Added `POST /api/v2/pipelines/{name}/flush-target/{target_name}` so you can flush a target Redis database through the API."},{"id":"operations-and-reliability","title":"Operations and Reliability","role":"content","text":"- **Optional AOF prerequisite check disablement**: For Helm installations, the `operator.prerequisiteChecks` section in the Helm values file lets you disable the AOF prerequisite check when the RDI database does not have AOF enabled. AOF should only be disabled after careful consideration, because disabling the check can lead to data loss in some failure scenarios. See [Can I use RDI without persistence enabled?](https://redis.io/docs/latest/integrate/redis-data-integration/faq#can-i-use-rdi-without-persistence-enabled).\n- **More reliable deploy task completion**: Fixed an issue where the operator could mark a deploy task as completed before the new pipeline was fully deployed, which could lead to incorrect pipeline status reporting.\n- **Fewer pipeline component restarts**: Fixed an issue where deploying a changed pipeline configuration would cause pipeline components that were not affected by the change to be restarted as well.\n- **Safer collector API property handling**: Fixed an issue in the collector API when connection property maps contained null values or resolved to null. Such cases are now rejected with a clear error.\n- **Reloader image configuration for Helm installations**: The Helm chart's bundled Reloader controller, which watches ConfigMaps and Secrets and triggers rolling upgrades when they change, now defaults to `docker.io/redis/reloader` and can be configured explicitly with `reloader.reloader.deployment.image.name`. This is especially useful for private-registry and mirrored-image deployments."},{"id":"security-updates","title":"Security Updates","role":"security","text":"- **Security updates across RDI images**: Updated third-party images, dependencies, and base packages to remove Critical and High CVEs in RDI images, including the operator, collector API, Fluentd, and Reloader images."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/release-notes/rdi-1-18-1","title":"Redis Data Integration release notes 1.18.1 (May 2026)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-18-1/","summary":"Debezium 3.5.0 upgrade with default collector image updates, Oracle connector compatibility updates, and Microsoft Entra ID authentication support.","tags":["docs","operate","rs"],"last_updated":"2026-05-07T22:53:38+03:00","page_type":"content","content_hash":"d06907ae509ee88bb03db47b01cb764338dc7aac88d8fda4532581440dba5aa8","sections":[{"id":"whats-new-in-1181","title":"What's New in 1.18.1","role":"overview","text":""},{"id":"improvements","title":"Improvements","role":"content","text":"- **Debezium upgrade**: RDI now uses Debezium 3.5.0 by default and updates the default collector image to `docker.io/redislabs/debezium-server:3.5.0.Final-rdi.1`. This update includes Oracle connector compatibility updates and Microsoft Entra ID authentication support."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/release-notes/rdi-1-19-0","title":"Redis Data Integration release notes 1.19.0 (July 2026)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-19-0/","summary":"New redis-di CLI for VM, Kubernetes, and Redis Cloud installations. New Redis sorted set and stream outputs for the Flink processor, expanded Snowflake configuration, and Flink processor support for offline VM installations. Improved pipeline status, secret updates, support packages, and error handling.","tags":["docs","operate","rs"],"last_updated":"2026-09-18T16:47:10+01:00","page_type":"content","content_hash":"f37be335177c374aea6953d89e1dda43b44d6b7bc0337d287852f849600c58ab","sections":[{"id":"whats-new-in-1190","title":"What's New in 1.19.0","role":"overview","text":""},{"id":"compatibility-notes","title":"Compatibility Notes","role":"compatibility","text":"- **RDI API v1 deprecated**: RDI API v1 is deprecated as of RDI 1.19.0. Existing API v1 endpoints remain available for backward compatibility, but new integrations should use RDI API v2. API v1 may be removed in a future RDI version. See the [RDI API migration guide](https://redis.io/docs/latest/integrate/redis-data-integration/reference/api-migration).\n- **Unified JSON merge behavior for the Flink processor**: JSON merge behavior is now the same regardless of the target Redis version. Previously, targets without native `JSON.MERGE` support (RedisJSON below 2.6) used a fallback path that preserved null leaf values as JSON nulls, while targets with native support deleted them. Both paths now delete null leaf values. This also differs from the classic processor, which still writes JSON nulls in its fallback path.\n- **`redis/rdi-cli` image and `rdi-secret.sh` retired**: The `redis/rdi-cli` Docker image is no longer built or published, and the `rdi-secret.sh` script has been removed. Manage RDI secrets with the `redis-di` CLI secret commands instead."},{"id":"new-features","title":"New Features","role":"content","text":"- **New `redis-di` CLI**: The `redis-di` command-line tool has been rewritten in Go as a thin client over the RDI REST API. The previous Python CLI talked directly to the RDI Redis database and the Kubernetes API and supported only VM installations; the new CLI works the same way across all installation types: VMs, Kubernetes (Helm), and Redis Cloud. It adds full create/read/update/delete support for pipelines and secrets, new commands for dead letter queues, jobs, and metric collections, and kubectl-style output with optional JSON and YAML formats for scripting. It is multi-pipeline aware, supports both the classic and Flink processors, and authenticates to the API with a JWT (user and password), a Redis Cloud account and user key, or without credentials when API authentication is disabled. The CLI ships as a single self-contained static binary, cross-compiled for `linux/amd64`, `linux/arm64`, `darwin/amd64`, `darwin/arm64`, and `windows/amd64`, and is available from the Redis download center, with the Linux binaries also bundled in the VM installer. The new secret commands replace the `rdi-secret.sh` script, and the `redis/rdi-cli` Docker image is no longer needed (see Compatibility Notes above).\n- **Table-level key and column configuration for Snowflake**: Each table for a Snowflake source now accepts `keys` to select keys and `columns` to project columns.\n- **Snowflake encrypted keypair support**: RDI now supports encrypted Snowflake keypairs for Snowflake sources.\n- **Pipeline metadata `revision` and `tags`**: Pipeline `config.metadata` now accepts an optional `revision` (non-negative integer) and `tags` (array of unique slug-like strings, up to 50 characters each), letting clients mark config states they care about.\n- **Flink processor support for Redis strings and Redis sets**: `redis.write` outputs now accept `data_type: string` and `data_type: set`.\n- **Flink processor support for Redis sorted sets**: `redis.write` outputs now accept `data_type: sorted_set`, using `args.member` and `args.score` to emit `ZADD` and `ZREM` commands.\n- **Flink processor support for Redis streams**: `redis.write` outputs now accept `data_type: stream`, appending each change event to the target stream via `XADD`.\n- **Flink processor images in the offline installer**: The offline VM installation package now bundles the Flink processor and metrics aggregator images, so the Flink processor can be used on such installs. The installer disk space requirement was raised to account for the larger image set.\n- **Per-component replica count in pipeline status**: `GET /api/v2/pipelines/{name}/status` now reports a `replicas` field for each component, giving the number of ready replicas.\n- **Support package covers the Flink processor**: `dump-support-package` now collects Flink processor diagnostics, including per-stream ingest statistics from the counters hash and metrics from the Flink processor ingress.\n- **Operator leader-election mode in API v2 info**: `GET /api/v2/info` now reports the operator leader-election mode (disabled, leader, or follower) in addition to the API version."},{"id":"improvements","title":"Improvements","role":"content","text":"- **Source connection error propagation (\"Test Source\")**: Source database connection failures now propagate through collector-api to the `GET /api/v2/pipelines/{name}/source-schemas/{source}` endpoint with the underlying cause named, instead of a generic error.\n- **Merged Flink processor status**: `GET /api/v2/pipelines/{name}/status` and the metric collection endpoints now report the Flink JobManager and TaskManager as a single `flink-processor` component instead of two separate entries, with status reflecting the worst case of both parts.\n- **Secret updates reflected in pipeline status**: After a source or target database secret is updated, the pipeline reports as not current and non-terminal until every consuming component has restarted with the new secret, so clients can wait for a secret change to take effect the same way they wait for any other change."},{"id":"bug-fixes","title":"Bug Fixes","role":"content","text":"- **Support package log parameters no longer dropped**: Collected component logs now retain their structured key-value parameters (for example, the operator's reconcile context) instead of showing only the timestamp, level, and message.\n- **Pipeline deletion resets state without removing the Pipeline resource**: Deleting a pipeline through API v2 now replaces the Pipeline with an empty inactive configuration and triggers reset cleanup instead of deleting the Kubernetes resource. This avoids unstable operator reconciliation when the operator restarts and removes persisted pipeline runtime data from the RDI database.\n- **Orphaned Redis keys on UPDATE with key change (Flink processor)**: When the key calculated by a custom key expression changes as part of an UPDATE, the Flink processor now removes the entry under the previous key in addition to writing the new one.\n- **Nested JSON outputs ignored `removedFields` on UPDATE (Flink processor)**: Nested JSON outputs (`redis.write` with `data_type: json` and a `nest` block) now apply MongoDB `$unset` removals on UPDATE events, deleting the corresponding top-level and nested fields from the parent JSON document instead of leaving stale values behind.\n- **No RDI API restarts when target database credentials change**: Rotating the target database password or TLS certificates no longer causes the RDI API to restart, eliminating brief request interruptions during credential updates.\n- **Accurate pipeline status during runtime issues**: The pipeline status reported by the API no longer reflects a stale user-requested operation (such as `starting`, `stopping`, or `resetting`) once that operation has completed. Subsequent transitions to `pending` caused by runtime conditions, such as a source database becoming unreachable, are now reported correctly.\n- **Transient deploy-time pipeline errors suppressed**: The pipeline no longer briefly reports a \"Secret provider configuration error\" during deploy while the Secrets Store CSI driver is still mounting volumes. Volume mount warnings are now reported only after the same failure has persisted for at least 30 seconds.\n- **Updating an external secret no longer leaves any component on the stale secret**: Changing an external secret now restarts the affected components so the new secret is mounted, and the pipeline reports `updating` until the restart completes. Previously these components kept the previous secret until restarted for another reason.\n- **Over-counted `incoming` metric across snapshot/Debezium transitions (Flink processor)**: The first record after a snapshot-to-Debezium (or Debezium-to-snapshot) transition was counted twice in the per-stream `incoming` metric; it is now counted once.\n- **Multiline custom snapshot SQL**: A custom snapshot SQL statement containing line breaks (for example, a multi-line `SELECT` entered in the UI) no longer causes saving the pipeline to fail with a server error.\n- **Fixed schema introspection failing for tables/columns with special regex characters in their names**: Table and column names containing Java regex special characters (for example, `emp_1_{06s}`) are now escaped before being written to Debezium's `table.include.list`, `column.include.list`, and `field.exclude.list` properties, which prevented fetching source schema metadata.\n- **JSON target type mismatch left records pending instead of rejected (Flink processor)**: When a JSON target write hit a key that already held a non-JSON value, RedisJSON reported a different error than the classic Redis WRONGTYPE message, so the record was not rejected and the batch stayed pending. The processor now recognizes the RedisJSON variant and rejects the record to the DLQ.\n- **MongoDB oplog checks against managed Atlas tiers**: The MongoDB Debezium oplog size and oplog access checks no longer fail when the source is a managed Atlas tier (such as free or flex) that restricts access to the reserved `local` database. They are skipped and omitted from the validation summary instead of failing, since the connector can still capture changes.\n- **Binary private key secrets corrupted (for example, PostgreSQL PKCS#8 keys)**: Setting a secret from a non-text file, such as a PostgreSQL client key in PKCS#8 DER (`.pk8`) form, no longer corrupts the key. Binary secret files are now base64-encoded end to end through a new `binary_file` secret type; plain text secrets are unaffected."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/release-notes/rdi-1-19-1","title":"Redis Data Integration release notes 1.19.1 (August 2026)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-19-1/","summary":"Improved Flink processor recovery, logging, and security.","tags":["docs","operate","rs"],"last_updated":"2026-08-14T11:05:02+03:00","page_type":"content","content_hash":"8ebebce69b2e8738dbcdde6ef6bfd7b1989420fac8b3d431dfd911f584362ecb","sections":[{"id":"whats-new-in-1191","title":"What's New in 1.19.1","role":"overview","text":""},{"id":"improvements","title":"Improvements","role":"content","text":"- **API response body logging**: The RDI API now logs response bodies alongside request payloads. Bodies are logged at DEBUG level, while request and response metadata stays at INFO, and large bodies are truncated to keep logs manageable."},{"id":"bug-fixes","title":"Bug Fixes","role":"content","text":"- **Stream could stop being processed after a Flink processor restart**: A narrow timing window around a checkpoint could leave a stream permanently unassigned to any reader after a restart (for example, following a TaskManager failure). This would silently halt ingestion for that stream until the pipeline was redeployed. The Flink processor now confirms with every reader before treating a stream as no longer needed."},{"id":"security-updates","title":"Security Updates","role":"security","text":"- **Hardened API log redaction**: Ensured secrets, credentials, tokens, and connection strings are masked in the request and response bodies the RDI API logs, with login and pipeline secret payloads fully redacted.\n- **Flink processor and collector security updates**: Resolved `CVE-2024-57699`, `CVE-2025-12183`, `CVE-2025-27820`, `CVE-2025-55163`, `CVE-2025-66566`, `CVE-2025-68973`, `CVE-2026-35194`, `CVE-2026-42198`, `CVE-2026-42583`, `CVE-2026-44249`, `CVE-2026-45416`, `CVE-2026-45447`, `CVE-2026-50010`, `CVE-2026-54291`, `CVE-2026-54512`, `CVE-2026-54513`, `CVE-2026-59901`, and `GHSA-r7wm-3cxj-wff9`.\n- **Monitor security updates**: Resolved `CVE-2024-6345` and `CVE-2025-47273`.\n- **Collector API security updates**: Resolved `CVE-2025-66566`, `CVE-2026-10050`, `CVE-2026-40973`, `CVE-2026-40983`, `CVE-2026-40984`, `CVE-2026-42198`, `CVE-2026-42579`, `CVE-2026-42583`, `CVE-2026-44249`, `CVE-2026-45416`, `CVE-2026-45674`, `CVE-2026-47691`, `CVE-2026-50010`, `CVE-2026-54291`, `CVE-2026-54512`, `CVE-2026-54513`, `CVE-2026-59901`, and `GHSA-r7wm-3cxj-wff9`.\n- **Shared utilities security updates**: Resolved `CVE-2026-21441` and `CVE-2026-33154`.\n- **Operator security updates**: Resolved `CVE-2026-24051`, `CVE-2026-33186`, `CVE-2026-35469`, `CVE-2026-39821`, `CVE-2026-39829`, `CVE-2026-39830`, `CVE-2026-39831`, `CVE-2026-39832`, `CVE-2026-39833`, `CVE-2026-39834`, `CVE-2026-42508`, `CVE-2026-46595`, `CVE-2026-46597`, `CVE-2026-46680`, `CVE-2026-50151`, `CVE-2026-50163`, `CVE-2026-53488`, and `GHSA-hrxh-6v49-42gf`.\n- **Collector initializer security update**: Upgraded jq to resolve `CVE-2026-32316`, `CVE-2026-33947`, `CVE-2026-33948`, `CVE-2026-39979`, `CVE-2026-40612`, `CVE-2026-41256`, `CVE-2026-41257`, `CVE-2026-43894`, `CVE-2026-43895`, `CVE-2026-43896`, `CVE-2026-44777`, `CVE-2026-47770`, `CVE-2026-49839`, and `CVE-2026-54679`.\n- **Fluentd security updates**: Resolved 20 unique CVEs compared with the previous image, including 1 Critical and 11 High findings: `CVE-2025-46394`, `CVE-2025-60876`, `CVE-2025-6442`, `CVE-2026-7383`, `CVE-2026-9076`, `CVE-2026-22184`, `CVE-2026-28387`, `CVE-2026-28388`, `CVE-2026-28389`, `CVE-2026-28390`, `CVE-2026-31789`, `CVE-2026-31790`, `CVE-2026-34180`, `CVE-2026-34182`, `CVE-2026-42766`, `CVE-2026-42767`, `CVE-2026-42770`, `CVE-2026-45445`, `CVE-2026-45446`, and `CVE-2026-45447`.\n- **Classic processor security updates**: Resolved `CVE-2024-6345`, `CVE-2025-47273`, and `CVE-2025-67221`.\n- **API security updates**: Resolved `CVE-2024-53981`, `CVE-2024-6345`, `CVE-2025-47273`, `CVE-2025-62727`, `CVE-2026-24486`, `CVE-2026-32597`, `CVE-2026-42561`, `CVE-2026-48522`, `CVE-2026-48523`, `CVE-2026-48524`, `CVE-2026-48525`, `CVE-2026-48526`, `CVE-2026-48710`, `CVE-2026-53539`, and `CVE-2026-54283`.\n- **Metrics aggregator security updates**: Resolved `CVE-2024-6345`, `CVE-2025-47273`, `CVE-2025-62727`, `CVE-2025-66418`, `CVE-2025-66471`, `CVE-2026-23490`, `CVE-2026-26007`, `CVE-2026-30922`, `CVE-2026-32597`, `CVE-2026-48710`, and `CVE-2026-54283`."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/release-notes/rdi-1-2","title":"Redis Data Integration release notes 1.2 (June 2024)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-2/","summary":"API server with a set of APIs to support Redis Insight with creating, testing, deploying & monitoring RDI pipelines. RDI Installer verifies all components are running at the end of installation.","aliases":["/integrate/redis-data-integration/ingest/release-notes/rdi-1-2/"],"tags":["docs","operate","rs"],"last_updated":"2024-09-10T15:32:02+01:00","page_type":"content","content_hash":"0918d085437a19bb4dc54ad1837ab05fce78d3c714641d26453fb5393b26307b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> This minor release replaces the 1.0 release.\n\nRDI’s mission is to help Redis customers sync Redis Enterprise with live data from their slow disk-based databases to:\n\n- Meet the required speed and scale of read queries and provide an excellent and predictable user experience.\n- Save resources and time when building pipelines and coding data transformations.\n- Reduce the total cost of ownership by saving money on expensive database read replicas.\n\nRDI keeps the Redis cache up to date with changes in the primary database, using a\n[_Change Data Capture (CDC)_](https://en.wikipedia.org/wiki/Change_data_capture) mechanism.\nIt also lets you _transform_ the data from relational tables into convenient\nand fast data structures that match your app's requirements. You specify the\ntransformations using a configuration system, so no coding is required."},{"id":"headlines","title":"Headlines","role":"content","text":"- API server with a set of APIs to support Redis Insight with creating, testing, deploying & monitoring RDI pipelines.\n- RDI Installer verifies all components are running at the end of installation."},{"id":"fixed-bugs","title":"Fixed Bugs","role":"content","text":"- Support for source database TLS & mTLS was fixed. Certificate and file names used with the redis-di set-secret command can have any file names\n- Mismatch between Reloader version provided and the one required."},{"id":"limitations","title":"Limitations","role":"limits","text":"- RDI can write data to a Redis Active-Active database. However, it doesn't support writing data to two or more Active-Active replicas. Writing data from RDI to several Active-Active replicas could easily harm data integrity as RDI is not synchronous with the source database commits.\n- RDI write-behind (which is currently in preview) should not be used on the same data set that RDI ingest is writing to Redis. This would either cause an infinite loop or would harm the data integrity, since both ingest and write-behind are asynchronous, eventually-consistent processes."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/release-notes/rdi-1-2-8","title":"Redis Data Integration release notes 1.2.8 (August 2024)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-2-8/","summary":"API server with a set of APIs to support Redis Insight with creating, testing, deploying & monitoring RDI pipelines. RDI Installer verifies all components are running at the end of installation.","aliases":["/integrate/redis-data-integration/ingest/release-notes/rdi-1-2-8/"],"tags":["docs","operate","rs"],"last_updated":"2024-09-10T15:32:02+01:00","page_type":"content","content_hash":"c618a35622947bc72444a0daeaa6cdd01ed6ff40c6a41bdf49ebdd01a6760b19","sections":[{"id":"overview","title":"Overview","role":"overview","text":"# Redis Data Integration 1.2.8 GA\n\n> This maintenance release replaces the 1.2 release.\n\nRDI’s mission is to help Redis customers sync Redis Enterprise with live data from their slow disk-based databases to:\n\n- Meet the required speed and scale of read queries and provide an excellent and predictable user experience.\n- Save resources and time when building pipelines and coding data transformations.\n- Reduce the total cost of ownership by saving money on expensive database read replicas.\n\nRDI keeps the Redis cache up to date with changes in the primary database, using a [_Change Data Capture (CDC)_](https://en.wikipedia.org/wiki/Change_data_capture) mechanism.\nIt also lets you _transform_ the data from relational tables into convenient and fast data structures that match your app's requirements. You specify the transformations using a configuration system, so no coding is required."},{"id":"headlines","title":"Headlines","role":"content","text":"- API server with a set of APIs to support Redis Insight with creating, testing, deploying & monitoring RDI pipelines.\n- RDI Installer verifies all components are running at the end of installation."},{"id":"fixed-bugs","title":"Fixed Bugs","role":"content","text":"- Fixed issue with [CoreDNS](https://coredns.io/) configuration. Previously, it could only resolve from the host DNS.\n- Fixed incorrect validation schema for jobs in [Redis Insight](https://redis.io/docs/latest/develop/connect/insight/).\n- The pipeline advanced settings were missing when downloading the pipeline from RDI but this is now fixed."},{"id":"limitations","title":"Limitations","role":"limits","text":"- RDI can write data to a Redis Active-Active database. However, it doesn't support writing data to two or more Active-Active replicas. Writing data from RDI to several Active-Active replicas could easily harm data integrity as RDI is not synchronous with the source database commits.\n- RDI write-behind (which is currently in preview) should not be used on the same data set that RDI ingest is writing to Redis. This would either cause an infinite loop or would harm the data integrity, since both ingest and write-behind are asynchronous, eventually-consistent processes."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/release-notes/rdi-1-4-0","title":"Redis Data Integration release notes 1.4.0 (October 2024)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-4-0/","summary":"Installation on Kubernetes with a Helm chart. Improvements for installation on VMs.","tags":["docs","operate","rs"],"last_updated":"2026-09-18T16:47:10+01:00","page_type":"content","content_hash":"adddff9635ab587793cfe5b13561460f08944e03be0cbfd3e9acf5707a8c3449","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> This is a GA version of Redis Data Integration (RDI) that improves the installation of RDI.\n\nRDI’s mission is to help Redis customers sync Redis Enterprise with live data from their slow disk-based databases to:\n\n- Meet the required speed and scale of read queries and provide an excellent and predictable user experience.\n- Save resources and time when building pipelines and coding data transformations.\n- Reduce the total cost of ownership by saving money on expensive database read replicas.\n\nRDI keeps the Redis cache up to date with changes in the primary database, using a [_Change Data Capture (CDC)_](https://en.wikipedia.org/wiki/Change_data_capture) mechanism.\nIt also lets you _transform_ the data from relational tables into convenient and fast data structures that match your app's requirements. You specify the transformations using a configuration system, so no coding is required."},{"id":"headlines","title":"Headlines","role":"content","text":"-   Installation on [Kubernetes](https://redis.io/docs/latest/integrate/redis-data-integration/installation/install-k8s) using a [Helm chart](https://helm.sh/docs/). You can install on [OpenShift](https://docs.openshift.com/) or other flavours of K8s using Helm.\n\n- Improvements for installation on VMs:\n\t- Installer checks if the OS firewall is enabled on Ubuntu and RHEL.\n\t- Installer verifies DNS resolution from RDI components.\n\t- Installer provides log lines from components that failed during RDI deployment if a problem occurs.\n\t- Improved verification of RDI installation.\n\t- Installer verifies if the RDI database is in use by another instance of RDI.\n\t- Installer checks and warns if any [`iptables`](https://www.netfilter.org/projects/iptables/index.html) rules are set.\n\t- Improved message when RDI tries to connect to its Redis database with invalid TLS keys."},{"id":"limitations","title":"Limitations","role":"limits","text":"RDI can write data to a Redis Active-Active database. However, it doesn't support writing data to two or more Active-Active replicas. Writing data from RDI to several Active-Active replicas could easily harm data integrity as RDI is not synchronous with the source database commits."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/release-notes/rdi-1-4-1","title":"Redis Data Integration release notes 1.4.1 (November 2024)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-4-1/","summary":"Installation on Kubernetes with a Helm chart. Improvements for installation on VMs.","tags":["docs","operate","rs"],"last_updated":"2026-09-18T16:47:10+01:00","page_type":"content","content_hash":"fd3081382fe83e5526a1370e2c9dd33d1449aa589db4bc791fa9903414b4cc26","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> This maintenance release replaces the 1.4.0 release.\n\nRDI’s mission is to help Redis customers sync Redis Enterprise with live data from their slow disk-based databases to:\n\n- Meet the required speed and scale of read queries and provide an excellent and predictable user experience.\n- Save resources and time when building pipelines and coding data transformations.\n- Reduce the total cost of ownership by saving money on expensive database read replicas.\n\nRDI keeps the Redis cache up to date with changes in the primary database, using a [_Change Data Capture (CDC)_](https://en.wikipedia.org/wiki/Change_data_capture) mechanism.\nIt also lets you _transform_ the data from relational tables into convenient and fast data structures that match your app's requirements. You specify the transformations using a configuration system, so no coding is required."},{"id":"headlines","title":"Headlines","role":"content","text":"-   Installation on [Kubernetes](https://redis.io/docs/latest/integrate/redis-data-integration/installation/install-k8s) using a [Helm chart](https://helm.sh/docs/). You can install on [OpenShift](https://docs.openshift.com/) or other flavours of K8s using Helm.\n\n- Improvements for installation on VMs:\n\t- Installer checks if the OS firewall is enabled on Ubuntu and RHEL.\n\t- Installer verifies DNS resolution from RDI components.\n\t- Installer provides log lines from components that failed during RDI deployment if a problem occurs.\n\t- Improved verification of RDI installation.\n\t- Installer verifies if the RDI database is in use by another instance of RDI.\n\t- Installer checks and warns if any [`iptables`](https://www.netfilter.org/projects/iptables/index.html) rules are set.\n\t- Improved message when RDI tries to connect to its Redis database with invalid TLS keys."},{"id":"issues-fixed","title":"Issues fixed","role":"content","text":"- **RDSC-2806**: Remove incorrectly created deployment.\n- **RDSC-2792**: Disable `kubectl run` checks.\n- **RDSC-2782**: Fix `coredns` issue."},{"id":"limitations","title":"Limitations","role":"limits","text":"RDI can write data to a Redis Active-Active database. However, it doesn't support writing data to two or more Active-Active replicas. Writing data from RDI to several Active-Active replicas could easily harm data integrity as RDI is not synchronous with the source database commits."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/release-notes/rdi-1-4-2","title":"Redis Data Integration release notes 1.4.2 (November 2024)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-4-2/","summary":"Installation on Kubernetes with a Helm chart. Improvements for installation on VMs.","tags":["docs","operate","rs"],"last_updated":"2026-09-18T16:47:10+01:00","page_type":"content","content_hash":"8b216f785c0c69db08e245c26553afab9c8a0e6af3317b79f6743000e195e507","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> This maintenance release replaces the 1.4.1 release.\n\nRDI’s mission is to help Redis customers sync Redis Enterprise with live data from their slow disk-based databases to:\n\n- Meet the required speed and scale of read queries and provide an excellent and predictable user experience.\n- Save resources and time when building pipelines and coding data transformations.\n- Reduce the total cost of ownership by saving money on expensive database read replicas.\n\nRDI keeps the Redis cache up to date with changes in the primary database, using a [_Change Data Capture (CDC)_](https://en.wikipedia.org/wiki/Change_data_capture) mechanism.\nIt also lets you _transform_ the data from relational tables into convenient and fast data structures that match your app's requirements. You specify the transformations using a configuration system, so no coding is required."},{"id":"headlines","title":"Headlines","role":"content","text":"-   Installation on [Kubernetes](https://redis.io/docs/latest/integrate/redis-data-integration/installation/install-k8s) using a [Helm chart](https://helm.sh/docs/). You can install on [OpenShift](https://docs.openshift.com/) or other flavours of K8s using Helm.\n\n- Improvements for installation on VMs:\n\t- Installer checks if the OS firewall is enabled on Ubuntu and RHEL.\n\t- Installer verifies DNS resolution from RDI components.\n\t- Installer provides log lines from components that failed during RDI deployment if a problem occurs.\n\t- Improved verification of RDI installation.\n\t- Installer verifies if the RDI database is in use by another instance of RDI.\n\t- Installer checks and warns if any [`iptables`](https://www.netfilter.org/projects/iptables/index.html) rules are set.\n\t- Improved message when RDI tries to connect to its Redis database with invalid TLS keys."},{"id":"issues-fixed","title":"Issues fixed","role":"content","text":"- **RDSC-2802**: Reintroduce checks for DNS resolution and network connectivity from inside a K8s pod.\n- **RDSC-2804**: RDI installation previously failed if the user specified a non-default HTTPS port.\n  The user can now specify the port correctly."},{"id":"limitations","title":"Limitations","role":"limits","text":"RDI can write data to a Redis Active-Active database. However, it doesn't support writing data to two or more Active-Active replicas. Writing data from RDI to several Active-Active replicas could easily harm data integrity as RDI is not synchronous with the source database commits."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/release-notes/rdi-1-4-3","title":"Redis Data Integration release notes 1.4.3 (December 2024)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-4-3/","summary":"Installation on Kubernetes with a Helm chart. Improvements for installation on VMs.","tags":["docs","operate","rs"],"last_updated":"2026-09-18T16:47:10+01:00","page_type":"content","content_hash":"f7937bfb9840568946fe6f2e5c939ed5cbdf6af60c3fc397f142a2ee962c9403","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> This maintenance release replaces the 1.4.2 release.\n\nRDI’s mission is to help Redis customers sync Redis Enterprise with live data from their slow disk-based databases to:\n\n- Meet the required speed and scale of read queries and provide an excellent and predictable user experience.\n- Save resources and time when building pipelines and coding data transformations.\n- Reduce the total cost of ownership by saving money on expensive database read replicas.\n\nRDI keeps the Redis cache up to date with changes in the primary database, using a [_Change Data Capture (CDC)_](https://en.wikipedia.org/wiki/Change_data_capture) mechanism.\nIt also lets you _transform_ the data from relational tables into convenient and fast data structures that match your app's requirements. You specify the transformations using a configuration system, so no coding is required."},{"id":"headlines","title":"Headlines","role":"content","text":"-   Installation on [Kubernetes](https://redis.io/docs/latest/integrate/redis-data-integration/installation/install-k8s) using a [Helm chart](https://helm.sh/docs/). You can install on [OpenShift](https://docs.openshift.com/) or other flavours of K8s using Helm.\n\n- Improvements for installation on VMs:\n\t- Installer checks if the OS firewall is enabled on Ubuntu and RHEL.\n\t- Installer verifies DNS resolution from RDI components.\n\t- Installer provides log lines from components that failed during RDI deployment if a problem occurs.\n\t- Improved verification of RDI installation.\n\t- Installer verifies if the RDI database is in use by another instance of RDI.\n\t- Installer checks and warns if any [`iptables`](https://www.netfilter.org/projects/iptables/index.html) rules are set.\n\t- Improved message when RDI tries to connect to its Redis database with invalid TLS keys."},{"id":"issues-fixed","title":"Issues fixed","role":"content","text":"- **RDSC-2963**: Helm chart `rdiSysSecret` does not create an empty secret if you are not using a password.\n- **RDSC-2729**: Use Debezium Server 2.7.3 and remove Prometheus.\n- **RDSC-2333**: Ensure the installer creates the context file correctly.\n- **RDSC-2806**: Remove incorrectly created deployment.\n- **RDSC-2729**: Fix `processors` `null` instead of `{}`.\n- **RDSC-2905**: Fix DNS check with multiple IP addresses.\n- **RDSC-2845**: RDI Helm chart release is set with `tag: 0.0.0`, but this should be the current release."},{"id":"limitations","title":"Limitations","role":"limits","text":"RDI can write data to a Redis Active-Active database. However, it doesn't support writing data to two or more Active-Active replicas. Writing data from RDI to several Active-Active replicas could easily harm data integrity as RDI is not synchronous with the source database commits."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/release-notes/rdi-1-4-4","title":"Redis Data Integration release notes 1.4.4 (January 2025)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-4-4/","summary":"Installation on Kubernetes with a Helm chart. Improvements for installation on VMs.","tags":["docs","operate","rs"],"last_updated":"2026-09-18T16:47:10+01:00","page_type":"content","content_hash":"373f508e785279b82b9ad397a64ef760fe32496d4874a29af00f888280562f95","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> This maintenance release replaces the 1.4.3 release.\n\nRDI’s mission is to help Redis customers sync Redis Enterprise with live data from their slow disk-based databases to:\n\n- Meet the required speed and scale of read queries and provide an excellent and predictable user experience.\n- Save resources and time when building pipelines and coding data transformations.\n- Reduce the total cost of ownership by saving money on expensive database read replicas.\n\nRDI keeps the Redis cache up to date with changes in the primary database, using a [_Change Data Capture (CDC)_](https://en.wikipedia.org/wiki/Change_data_capture) mechanism.\nIt also lets you _transform_ the data from relational tables into convenient and fast data structures that match your app's requirements. You specify the transformations using a configuration system, so no coding is required."},{"id":"headlines","title":"Headlines","role":"content","text":"-   Installation on [Kubernetes](https://redis.io/docs/latest/integrate/redis-data-integration/installation/install-k8s) using a [Helm chart](https://helm.sh/docs/). You can install on [OpenShift](https://docs.openshift.com/) or other flavours of K8s using Helm.\n\n- Improvements for installation on VMs:\n  - Adding an `upgrade.sh` script\n  - Uninstall script removes RDI CLI\n  - Adding an ingress to the `collector-source` metrics exporter for VM installs\n  - Fix RDI version issues during upgrade\n  - Ensure `KUBECONFIG` is set during upgrade\n  - Upgrade [`datayoga`](https://github.com/datayoga-io/datayoga) to 1.127.0 - add_field block was rejected for one or more records but the entire batch was marked as rejected. making troubleshooting difficult\n  - Fix installer not setting `RDI_REDIS_SSL` properly if SSL is enabled"},{"id":"issues-fixed","title":"Issues fixed","role":"content","text":"- **RDSC-3103**: If a record's transformation is rejected, the entire Processor batch ends up in DLQ\n- **RDSC-3130**: Failed to install 1.4.3, but successful install with 1.2.8\n- **RDSC-3141**: There is no way to reach metrics when running RDI on VM\n- **RDSC-3142**: `redis-di upgrade` fails with error message - CRITICAL - Error while attempting to upgrade RDI: Could not get the current version of the RDI instance\n- **RDSC-3143**: RDI on VM upgrade error message - Get \"http://localhost:8080/openapi/v2?timeout=32s\": dial tcp 127.0.0.1:8080: connect: connection refused\n- **RDSC-3156**: Add script to execute `redis-di upgrade` correctly"},{"id":"limitations","title":"Limitations","role":"limits","text":"RDI can write data to a Redis Active-Active database. However, it doesn't support writing data to two or more Active-Active replicas. Writing data from RDI to several Active-Active replicas could easily harm data integrity as RDI is not synchronous with the source database commits."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/release-notes/rdi-1-6-0","title":"Redis Data Integration release notes 1.6.0 (February 2025)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-6-0/","summary":"Installation on Kubernetes with a Helm chart. Improvements for installation on VMs.","tags":["docs","operate","rs"],"last_updated":"2026-09-18T16:47:10+01:00","page_type":"content","content_hash":"d107eaa62d0fa7830db13643b00225a6fd1c0c73427f96b24613cc5c032e0b76","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> This maintenance release replaces the 1.4.4 release.\n\nRDI’s mission is to help Redis customers sync Redis Enterprise with live data from their slow disk-based databases to:\n\n- Meet the required speed and scale of read queries and provide an excellent and predictable user experience.\n- Save resources and time when building pipelines and coding data transformations.\n- Reduce the total cost of ownership by saving money on expensive database read replicas.\n\nRDI keeps the Redis cache up to date with changes in the primary database, using a [_Change Data Capture (CDC)_](https://en.wikipedia.org/wiki/Change_data_capture) mechanism.\nIt also lets you _transform_ the data from relational tables into convenient and fast data structures that match your app's requirements. You specify the transformations using a configuration system, so no coding is required."},{"id":"headlines","title":"Headlines","role":"content","text":"- RDI now requires the RDI database to have the following properties set, otherwise\n  RDI will not start:\n  - `maxmemory_policy`: `noeviction`,\n  - `aof_enabled`: `1`\n\n- Allow RDI to run in any K8s namespace\n\n- Fix metadata API to support Oracle and SQL Server\n\n- Added [denormalisation lookup block](https://redis.io/docs/latest/integrate/redis-data-integration/reference/data-transformation/lookup)\n\n- Many bug fixes"},{"id":"limitations","title":"Limitations","role":"limits","text":"RDI can write data to a Redis Active-Active database. However, it doesn't support writing data to two or more Active-Active replicas. Writing data from RDI to several Active-Active replicas could easily harm data integrity as RDI is not synchronous with the source database commits."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/release-notes/rdi-1-6-1","title":"Redis Data Integration release notes 1.6.1 (March 2025)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-6-1/","summary":"Installation on Kubernetes with a Helm chart. Improvements for installation on VMs.","tags":["docs","operate","rs"],"last_updated":"2026-09-18T16:47:10+01:00","page_type":"content","content_hash":"dbe62df6f64e7b916f0f3b9b551930982eb31f7826ade91ca62ef7e46b2f8e83","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> This release replaces the 1.6.0 release.\n\nRDI’s mission is to help Redis customers sync Redis Enterprise with live data from their slow disk-based databases to:\n\n- Meet the required speed and scale of read queries and provide an excellent and predictable user experience.\n- Save resources and time when building pipelines and coding data transformations.\n- Reduce the total cost of ownership by saving money on expensive database read replicas.\n\nRDI keeps the Redis cache up to date with changes in the primary database, using a [_Change Data Capture (CDC)_](https://en.wikipedia.org/wiki/Change_data_capture) mechanism.\nIt also lets you _transform_ the data from relational tables into convenient and fast data structures that match your app's requirements. You specify the transformations using a configuration system, so no coding is required."},{"id":"headlines","title":"Headlines","role":"content","text":"- Fixed missing metrics and incorrect status for SQL Server.\n- Improved Redis Insight autocomplete using schema description property.\n- Restricted Redis connections to targets and non-Redis connections to sources.\n- Enhanced scaffolded `config.yaml` with examples for each database type.\n- VM Installer: Captured output of all sub-processes.\n- Improved error message for critical errors when sending commands to Redis cluster fails.\n- Fixed issue where pipeline status did not return to streaming after stopping and starting.\n- Added RDI API logging, validation examples, and bug fixes.\n- Disabled strict parsing of collector application properties to allow overriding.\n- Fixed HTTP 500 error in `GET /pipelines/sources/{source}/columns` when passing tables parameter.\n- VM setup: Fixed incorrect output status and incomplete upgrades in `upgrade.sh` script.\n- Ensured `collector-api` service force upgrades when `Operator` restarts.\n- Corrected typos in CLI option help messages.\n- Generated and published [API reference page](https://redis.io/docs/latest/integrate/redis-data-integration/reference/api-reference).\n- Resolved error in `collector-api` when Source secret contains special characters.\n- Fixed RDI 1.6.0 installation failure for HA due to `rdi-operator` deployment timeout.\n- Added support for downloading Debezium image from a private registry.\n- VM Installer now supports Ubuntu 22.04 and Ubuntu 24.04.\n- Resolved performance degradation in 1.6.0 that caused the initial sync to be 3-5 times slower."},{"id":"limitations","title":"Limitations","role":"limits","text":"RDI can write data to a Redis Active-Active database. However, it doesn't support writing data to two or more Active-Active replicas. Writing data from RDI to several Active-Active replicas could easily harm data integrity as RDI is not synchronous with the source database commits."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/release-notes/rdi-1-6-2","title":"Redis Data Integration release notes 1.6.2 (March 2025)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-6-2/","summary":"Installation on Kubernetes with a Helm chart. Improvements for installation on VMs.","tags":["docs","operate","rs"],"last_updated":"2025-03-24T11:41:03+02:00","page_type":"content","content_hash":"eea2aa4f3759e82a97f020eaaa11f22e9222bbef915427a86b7d43088002e080","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> This maintenance release replaces the 1.6.1 release.\n\nRDI’s mission is to help Redis customers sync Redis Enterprise with live data from their slow disk-based databases to:\n\n- Meet the required speed and scale of read queries and provide an excellent and predictable user experience.\n- Save resources and time when building pipelines and coding data transformations.\n- Reduce the total cost of ownership by saving money on expensive database read replicas.\n\nRDI keeps the Redis cache up to date with changes in the primary database, using a [_Change Data Capture (CDC)_](https://en.wikipedia.org/wiki/Change_data_capture) mechanism.\nIt also lets you _transform_ the data from relational tables into convenient and fast data structures that match your app's requirements. You specify the transformations using a configuration system, so no coding is required."},{"id":"headlines","title":"Headlines","role":"content","text":"- Fix: With an RDI namespace that contains dashes, `rdi-metrics-exporter` crashes immediately upon startup\n- Fix reported security vulnerabilities\n- Fix: Connection to PostgreSQL from Debezium fails with \"could not read SSL key file\" when using mTLS"},{"id":"fixes--improvements","title":"Fixes & Improvements","role":"content","text":"- **Resolved startup crash in `rdi-metrics-exporter`**\n  Fixed an issue where the exporter would crash on startup if the RDI namespace included dashes (e.g., `my-namespace`) that are not allowed in prometheus labels.\n\n- **Security Vulnerabilities Patched**\n  Addressed the following reported CVEs:\n\n  - CVE-2019-14250\n  - CVE-2019-17543\n  - CVE-2023-32665\n  - CVE-2024-52533\n  - CVE-2020-17049\n  - CVE-2024-47874\n\n- **Improved mTLS compatibility in `collector-initializer`**\n  DER-formatted keys are now skipped during initialization, resolving a PostgreSQL connection error with Debezium (`could not read SSL key file`) when using mTLS."},{"id":"limitations","title":"Limitations","role":"limits","text":"RDI can write data to a Redis Active-Active database. However, it doesn't support writing data to two or more Active-Active replicas. Writing data from RDI to several Active-Active replicas could easily harm data integrity as RDI is not synchronous with the source database commits."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/release-notes/rdi-1-6-3","title":"Redis Data Integration release notes 1.6.3 (March 2025)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-6-3/","summary":"Installation on Kubernetes with a Helm chart. Improvements for installation on VMs.","tags":["docs","operate","rs"],"last_updated":"2025-03-26T16:03:04Z","page_type":"content","content_hash":"3bb5206ec09a594c421a84873e2ce199de12e582950700ba8ad15572c8304337","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> This maintenance release replaces the 1.6.2 release.\n\nRDI’s mission is to help Redis customers sync Redis Enterprise with live data from their slow disk-based databases to:\n\n- Meet the required speed and scale of read queries and provide an excellent and predictable user experience.\n- Save resources and time when building pipelines and coding data transformations.\n- Reduce the total cost of ownership by saving money on expensive database read replicas.\n\nRDI keeps the Redis cache up to date with changes in the primary database, using a [_Change Data Capture (CDC)_](https://en.wikipedia.org/wiki/Change_data_capture) mechanism.\nIt also lets you _transform_ the data from relational tables into convenient and fast data structures that match your app's requirements. You specify the transformations using a configuration system, so no coding is required."},{"id":"headlines","title":"Headlines","role":"content","text":"- Fix: RDI 1.6.2 VM Installer not working on RHEL-8 OS."},{"id":"limitations","title":"Limitations","role":"limits","text":"RDI can write data to a Redis Active-Active database. However, it doesn't support writing data to two or more Active-Active replicas. Writing data from RDI to several Active-Active replicas could easily harm data integrity as RDI is not synchronous with the source database commits."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/release-notes/rdi-1-6-4","title":"Redis Data Integration release notes 1.6.4 (April 2025)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-6-4/","summary":"Installation on Kubernetes with a Helm chart. Improvements for installation on VMs.","tags":["docs","operate","rs"],"last_updated":"2025-04-04T09:50:57+01:00","page_type":"content","content_hash":"766d338d059ec6fda883db5c1bd40234f8ae6a4355538c1fb5297a4f85b98aa9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> This maintenance release replaces the 1.6.3 release.\n\nRDI’s mission is to help Redis customers sync Redis Enterprise with live data from their slow disk-based databases to:\n\n- Meet the required speed and scale of read queries and provide an excellent and predictable user experience.\n- Save resources and time when building pipelines and coding data transformations.\n- Reduce the total cost of ownership by saving money on expensive database read replicas.\n\nRDI keeps the Redis cache up to date with changes in the primary database, using a [_Change Data Capture (CDC)_](https://en.wikipedia.org/wiki/Change_data_capture) mechanism.\nIt also lets you _transform_ the data from relational tables into convenient and fast data structures that match your app's requirements. You specify the transformations using a configuration system, so no coding is required."},{"id":"headlines","title":"Headlines","role":"content","text":"- Feature: Add support for Debezium 3.0.8"},{"id":"limitations","title":"Limitations","role":"limits","text":"RDI can write data to a Redis Active-Active database. However, it doesn't support writing data to two or more Active-Active replicas. Writing data from RDI to several Active-Active replicas could easily harm data integrity as RDI is not synchronous with the source database commits."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/release-notes/rdi-1-6-5","title":"Redis Data Integration release notes 1.6.5 (April 2025)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-6-5/","summary":"Installation on Kubernetes with a Helm chart. Improvements for installation on VMs.","tags":["docs","operate","rs"],"last_updated":"2025-04-09T12:49:51+01:00","page_type":"content","content_hash":"65a077f617bdb201fe05f3e247472e5ea02a96c41007be6475f9fecdfcbd34b4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> This maintenance release replaces the 1.6.4 release.\n\nRDI’s mission is to help Redis customers sync Redis Enterprise with live data from their slow disk-based databases to:\n\n- Meet the required speed and scale of read queries and provide an excellent and predictable user experience.\n- Save resources and time when building pipelines and coding data transformations.\n- Reduce the total cost of ownership by saving money on expensive database read replicas.\n\nRDI keeps the Redis cache up to date with changes in the primary database, using a [_Change Data Capture (CDC)_](https://en.wikipedia.org/wiki/Change_data_capture) mechanism.\nIt also lets you _transform_ the data from relational tables into convenient and fast data structures that match your app's requirements. You specify the transformations using a configuration system, so no coding is required."},{"id":"headlines","title":"Headlines","role":"content","text":"- Vulnerabilities: Resolve vulnerabilities in the `collector-api` component.\n  - CVE-2025-27363\n  - CVE-2021-23336\n  - CVE-2025-27113\n  - CVE-2024-52533\n  - CVE-2025-1632\n  - CVE-2024-47535"},{"id":"limitations","title":"Limitations","role":"limits","text":"RDI can write data to a Redis Active-Active database. However, it doesn't support writing data to two or more Active-Active replicas. Writing data from RDI to several Active-Active replicas could easily harm data integrity as RDI is not synchronous with the source database commits."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/release-notes/rdi-1-6-6","title":"Redis Data Integration release notes 1.6.6 (April 2025)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-6-6/","summary":"Installation on Kubernetes with a Helm chart. Improvements for installation on VMs.","tags":["docs","operate","rs"],"last_updated":"2026-09-18T16:47:10+01:00","page_type":"content","content_hash":"e821bbcad4c92d04c64f3b50b17966275acddce2b42251c74d13aac784573cfa","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> This maintenance release replaces the 1.6.5 release.\n\nRDI’s mission is to help Redis customers sync Redis Enterprise with live data from their slow disk-based databases to:\n\n- Meet the required speed and scale of read queries and provide an excellent and predictable user experience.\n- Save resources and time when building pipelines and coding data transformations.\n- Reduce the total cost of ownership by saving money on expensive database read replicas.\n\nRDI keeps the Redis cache up to date with changes in the primary database, using a [_Change Data Capture (CDC)_](https://en.wikipedia.org/wiki/Change_data_capture) mechanism.\nIt also lets you _transform_ the data from relational tables into convenient and fast data structures that match your app's requirements. You specify the transformations using a configuration system, so no coding is required."},{"id":"headlines","title":"Headlines","role":"content","text":"- Fixed a Redis Insight issue when accessing the RDI API `/strategies` endpoint.\n- Added a new script to the RDI Helm chart for managing the source and target database secrets. See\n  [Deploy a pipeline](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/deploy)\n  for more information.\n- Fixed an issue when connecting from Redis Insight to the RDI API `/login` endpoint with a non-default user.\n- Updated the RDI API `/schemas` endpoint to return a list of databases for MySQL and MariaDB, instead of an empty list of schemas."},{"id":"limitations","title":"Limitations","role":"limits","text":"RDI can write data to a Redis Active-Active database. However, it doesn't support writing data to two or more Active-Active replicas. Writing data from RDI to several Active-Active replicas could easily harm data integrity as RDI is not synchronous with the source database commits."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/release-notes/rdi-1-6-7","title":"Redis Data Integration release notes 1.6.7 (May 2025)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-6-7/","summary":"Installation on Kubernetes with a Helm chart. Improvements for installation on VMs.","tags":["docs","operate","rs"],"last_updated":"2025-05-06T11:36:24+01:00","page_type":"content","content_hash":"cb6b8dcb8b3389bd367c67b4c8c2d3b4df7a09f578c205092454a4092aa2ffd3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> This maintenance release replaces the 1.6.6 release.\n\nRDI’s mission is to help Redis customers sync Redis Enterprise with live data from their slow disk-based databases to:\n\n- Meet the required speed and scale of read queries and provide an excellent and predictable user experience.\n- Save resources and time when building pipelines and coding data transformations.\n- Reduce the total cost of ownership by saving money on expensive database read replicas.\n\nRDI keeps the Redis cache up to date with changes in the primary database, using a [_Change Data Capture (CDC)_](https://en.wikipedia.org/wiki/Change_data_capture) mechanism.\nIt also lets you _transform_ the data from relational tables into convenient and fast data structures that match your app's requirements. You specify the transformations using a configuration system, so no coding is required."},{"id":"headlines","title":"Headlines","role":"content","text":"- Update the RDI API `/tables` and `/metadata` endpoints to filter the results by schema for MySQL and \n  MariaDB. Schema and Database are the same for these databases."},{"id":"limitations","title":"Limitations","role":"limits","text":"RDI can write data to a Redis Active-Active database. However, it doesn't support writing data to two or more Active-Active replicas. Writing data from RDI to several Active-Active replicas could easily harm data integrity as RDI is not synchronous with the source database commits."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/release-notes/rdi-1-8-0","title":"Redis Data Integration release notes 1.8.0 (May 2025)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-1-8-0/","summary":"Enhanced RDI operator for better pipeline orchestration, resiliency, observability and flexibility; External collector support; Labels and annotations to RDI data plane pods; Custom Debezium image; Calculated TTL for target database.","tags":["docs","operate","rs"],"last_updated":"2026-09-18T16:47:10+01:00","page_type":"content","content_hash":"feb9384df7e84ec666cabdc58cf2650147849d999547c98641be7d2fd69a6387","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This minor release replaces the 1.6.7 release.\n\nRDI’s mission is to help Redis customers sync Redis Enterprise with live data from their slow disk-based databases to:\n\n- Meet the required speed and scale of read queries and provide an excellent and predictable user experience.\n- Save resources and time when building pipelines and coding data transformations.\n- Reduce the total cost of ownership by saving money on expensive database read replicas.\n\nRDI keeps the Redis cache up to date with changes in the primary database, using a [_Change Data Capture (CDC)_](https://en.wikipedia.org/wiki/Change_data_capture) mechanism.\nIt also lets you _transform_ the data from relational tables into convenient and fast data structures that match your app's requirements. You specify the transformations using a configuration system, so no coding is required."},{"id":"headlines","title":"Headlines","role":"content","text":"- Enhanced RDI operator for better pipeline orchestration, resiliency, observability, and flexibility. It \n  will also enable many new features in the near future.\n- You can now use an external collector that is not managed by RDI but writes into RDI streams\n  (Debezium compatible).\n- You can now add labels and annotations to RDI data plane pods, for example to control service\n  mesh features.\n- RDI now uses a custom image of Debezium (based on `3.0.8.Final`) to address known vulnerabilities.\n- Added support for calculated TTL for target database keys via `expire` expressions."},{"id":"detailed-changes","title":"Detailed changes","role":"content","text":""},{"id":"helm-chart-changes","title":"Helm chart changes","role":"content","text":"- All collector and processor values that were previously under `collector`, `collectorSourceMetricsExporter`, and `processor` have been moved to `operator.dataPlane.collector` and `operator.dataPlane.processor`.\n- `global.collectorApiEnabled` has been moved to `operator.dataPlane.collectorApi.enabled`, and is now a boolean value (`true` or `false`), not `\"0\"` or `\"1\"`.\n- `api.authEnabled` is also now a boolean value, not `\"0\"` or `\"1\"`.\n- The following values have been deprecated: `rdiMetricsExporter.service.protocol`, `rdiMetricsExporter.service.port`, `rdiMetricsExporter.serviceMonitor.path`, `api.service.name`\n- You can now add custom labels and annotations to all RDI components.\n- You can now disable the creation of the RDI system secrets."},{"id":"operator-improvements","title":"Operator Improvements","role":"content","text":"The RDI operator has been significantly enhanced in the following areas:\n\n- **Resilience**: The operator now always maintains the desired pipeline state. Manual changes or random disruptions are reverted automatically.\n- **Automatic recovery**: When a configuration issue is resolved, the entire pipeline starts automatically, eliminating the need for manual redeployment.\n- **Consistency**: A pipeline that has been stopped with `stop` will remain stopped after `deploy` or `reset`, until explicitly started again.\n- **Enhanced configuration**: You can now configure data plane components in ways that were previously not supported, such as adding labels and annotations.\n- **External collector support**: No collector resources are created for sources of type `external`.\n- **Enhanced troubleshooting**: You can now gain extra insight into the pipeline state by examining the `Pipeline` and `PipelineRelease` custom K8s resources."},{"id":"other-features-improvements-and-enhancements","title":"Other Features, Improvements and Enhancements","role":"content","text":"- Added `expire` expression for target output in transformation jobs.\n- Addressed security vulnerabilities: TLS certificate hostname verification is now ON by default.\n- Improved Helm default values while preserving `values.yaml` formatting.\n- Enhanced Helm values and templates for better configuration.\n- Added a script to create or update secrets when using Helm (`rdi-secret.sh` in the Helm zip file).\n- Improved validation schema and ensured backward compatibility.\n- Fixed compatibility issues with newer versions of `requests` and `urllib3`.\n- Improved error messages for JSON schema validation.\n- Improved PostgreSQL documentation for mTLS.\n- Added timestamps to the `status` command.\n- Fixed issues with `primary_key` and `unique_constraint` attributes in Oracle metadata.\n- Added `capture.mode` to MongoDB scaffolding.\n- Improved Helm TLS setup for RDI database connections.\n- Enhanced error handling and validation for transformation jobs.\n- Improved documentation for supported platforms and configurations."},{"id":"fixes","title":"Fixes","role":"content","text":"- Fixed HTTP 500 error when querying columns with tables parameter.\n- Improved Helm TLS setup for RDI database connections.\n- Fixed keystore overwrite when using mTLS on both source and RDI DBs in the collector."},{"id":"limitations","title":"Limitations","role":"limits","text":"RDI can write data to a Redis Active-Active database. However, it doesn't support writing data to two or more Active-Active replicas. Writing data from RDI to several Active-Active replicas could easily harm data integrity as RDI is not synchronous with the source database commits."},{"id":"known-issues","title":"Known Issues","role":"content","text":""},{"id":"high-availability-upgrade-issue","title":"High Availability upgrade issue","role":"content","text":"When upgrading from RDI < 1.8.0 to RDI >= 1.8.0 in an HA setup, both RDI instances may incorrectly consider themselves active after the upgrade. This occurs because the upgrade process doesn't update the `rdi:ha:lock` value from the legacy `cluster-1` identifier, causing both clusters to assume they are the active cluster.\n\n**Symptoms:**\n\n- The upgraded passive node will start collector and processor components\n- Collector may enter a crash loop as it fails to connect to the source\n- Both clusters will restart in a loop\n\n**Workaround:**\n\nAfter upgrading, manually set a unique cluster ID for one of the installations by editing the configmap:\n\n[code example]\n\nThen add the following line to distinguish between the clusters:\n\n[code example]"}],"examples":[{"id":"high-availability-upgrade-issue-ex0","language":"bash","code":"kubectl edit cm -n rdi rdi-sys-config","section_id":"high-availability-upgrade-issue"},{"id":"high-availability-upgrade-issue-ex1","language":"bash","code":"RDI_CLUSTER_ID: cluster-2","section_id":"high-availability-upgrade-issue"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/release-notes/rdi-2-0-0","title":"Redis Data Integration release notes 2.0.0 (September 2026)","url":"https://redis.io/docs/latest/integrate/redis-data-integration/release-notes/rdi-2-0-0/","summary":"Multiple sources in one pipeline, Flink as the default processor, and simpler source mTLS configuration. Database-scoped secrets, per-source reset and cleanup, expanded API diagnostics, and security updates.","tags":["docs","operate","rs"],"last_updated":"2026-09-18T16:47:10+01:00","page_type":"content","content_hash":"411dd6cfd26631eb052ad97b775262d2dc75c3b2e0b353a3e3379ce71efbc1ac","sections":[{"id":"whats-new-in-200","title":"What's New in 2.0.0","role":"overview","text":""},{"id":"breaking-changes","title":"Breaking Changes","role":"content","text":"- **The Flink processor is the default processor**: A pipeline whose `processors` section does not set `type` now deploys the Flink processor instead of the classic one. Set `processors.type` to `classic` to keep deploying the classic processor.\n- **API v1 pipeline actions apply immediately instead of queuing**:\n  - `POST /pipelines`, `PATCH /pipelines`, `/pipelines/undeploy`, `/pipelines/sources/*`, `/pipelines/targets/*`, `/pipelines/processors/*`, `/pipelines/secret-providers/*`, `/pipelines/start`, `/pipelines/stop`, `/pipelines/reset` now update the Pipeline resource directly instead of posting to an internal task queue. Kubernetes errors are reported with their original status codes, rather than a generic 500.\n  - A request now applies its changes immediately rather than waiting behind earlier in-flight requests, so the most recent request wins if several are issued in quick succession without waiting for each to complete. Polling `GET /actions/{action_id}` for an action id that a later request has superseded now returns an unknown action error instead of that action's own status.\n  - `POST /pipelines` and `PATCH /pipelines` now also validate target connectivity similarly to their API v2 counterparts, and surface collector API failures as a `422`, `502`, `503`, or `504` error response. A request that previously succeeded despite an unreachable target now fails with a validation error.\n- **API v1 trace endpoint returns Not Implemented**: `POST /trace/start` now immediately returns `501 Not Implemented` instead of accepting the request and queuing a trace that would never run.\n- **API v2 pipeline DELETE no longer empties the pipeline**: `DELETE /api/v2/pipelines/{name}` no longer replaces the pipeline with an empty, inactive one. It now returns `404` when the named pipeline doesn't exist, and `422` for the `default` pipeline, since deleting it isn't supported. Use `redis-di deploy --empty`, or `PUT /api/v2/pipelines/{name}` with an empty configuration, to clear a pipeline instead.\n- **API v2 pipeline creation no longer validates against the Collector API**: `POST /pipelines` no longer accepts the `validate_tables` and `validate_cdc` query parameters, and never validates a new pipeline's tables or CDC setup against the Collector API. That validation could never succeed: the Collector API is rendered by the pipeline being created, so it cannot exist yet for the request creating it. `PUT /pipelines/{name}` keeps both parameters, but doesn't use them when creating a new pipeline.\n- **API v2 pipeline responses omit optional fields that have no value**: The pipeline, pipeline status, create, update, patch, start, stop, and reset responses no longer contain optional fields with null values; such fields are left out instead. This affects `status_changed_at` and an error's `remediation`. The OpenAPI schema is unchanged, since neither field was ever required, so clients generated from it are unaffected; a client that reads either key directly has to treat it as absent rather than null.\n- **`topic.prefix` is no longer accepted in `advanced.source`**: The `topic.prefix` property is now rejected in a source's `advanced.source` section, since RDI derives the topic prefix from the source name. Remove the property from your configuration and set each job's `server_name` to the derived prefix: the source name, `rdi` for the source of an upgraded single-source pipeline handled by the Debezium collector, and the Spanner instance ID for the source of such an upgraded pipeline handled by the Flink collector.\n- **API v2 metric collections data stream names contain the qualified table name**: The `data_streams.streams` keys of a metric collection are now the source-qualified table name (`mysql.inventory.addresses`), matching what the dead-letter queue endpoints return, instead of the Redis stream name (`{rdi}:inventory.addresses`). A client that uses the previous form has to be updated. API v1 statistics are unchanged.\n- **Cassandra is no longer a source database type**: `cassandra` has been removed from the source database types that `redis-di scaffold`, the configuration template endpoint, and `rdi-admin install` offer, and a source whose connection type is `cassandra` no longer passes validation. RDI never actually supported Cassandra as source, since the Debezium Cassandra connector has to run on each Cassandra node."},{"id":"new-features","title":"New Features","role":"content","text":"- **Multiple sources in one pipeline**: A pipeline can ingest data from several source databases, of the same or different types, into one Redis target. Each source has its own name, connection settings, credentials, and collector. Use API v2 or the `redis-di` CLI to manage multi-source pipelines; API v1 supports only single-source pipelines. To reset one source while preserving the other sources' internal data, use the optional `source` query parameter on `POST /pipelines/{name}/reset` or the `redis-di reset --source` flag. Source reset temporarily stops and restarts the whole pipeline and does not support `external` sources. See [Multiple sources in one pipeline](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/multiple-sources).\n- **Transformation jobs can select several tables (Flink processor)**: A job's `server_name`, `db`, `schema`, and `table` source selectors now accept a list of values in addition to a single one, and an entry prefixed with `regex:` is matched as an anchored regular expression, so one job can handle many tables. An entry without the prefix is matched literally, so existing jobs are unaffected, including table names containing regular expression characters. Two jobs whose selectors select the same table are rejected. Only the Flink processor supports this syntax, so a job using it is rejected for the classic processor.\n- **Database-scoped pipeline secret keys**: Pipeline secrets in API v2 and the `redis-di` CLI now use database-independent keys (`USERNAME`, `PASSWORD`, `CACERT`, `CERT`, `KEY`, `KEY_PASSWORD`) together with a `db` parameter (`--db` in the CLI) that specifies the database the secret belongs to: a source name, or `target`. The previous scope-prefixed keys (`SOURCE_DB_*`, `TARGET_DB_*`) are still accepted for single-source and target secrets, used without the `db` parameter.\n- **Richer validation errors from API v1 pipeline configuration endpoints**: A `422` response from `/pipelines/undeploy`, `/pipelines/sources/*`, `/pipelines/targets/*`, `/pipelines/processors/*`, and `/pipelines/secret-providers/*` may now include an `errors` array with structured per-field detail, in addition to the existing `detail` message, matching the shape already returned by `POST /pipelines` and `PATCH /pipelines`. Existing clients that only read `detail` are unaffected.\n- **Pipeline components report the source they are associated with**: Each collector component in an API v2 pipeline or pipeline status response now has a `source` field specifying the pipeline source it is associated with. It is empty for components that are not per-source, such as the processor and the Collector API.\n- **A follower installation keeps its pipelines instead of deleting them on leadership loss**: In a high-availability RDI deployment with leader election enabled, the installation that is not currently active now keeps its Pipeline resources and reports them with a new `standby` status, instead of deleting them and recreating them on leadership acquisition. Mutating a pipeline (create, update, patch, delete, start, stop, reset, or start/stop a source) on a standby installation now returns `503 Service Unavailable`.\n- **Scaffolded configurations use a descriptive source name**: `redis-di scaffold` and the configuration template endpoint now name the generated source after the database type (for example `mysql`). A custom name can be passed with the new `--source-name` flag (`source_name` query parameter).\n- **TypeScript SDK published to npmjs**: The RDI API TypeScript SDK is now publicly available on npmjs as `@rdi-ui/sdk` under the MIT license, so it no longer requires access to the internal GitHub Packages registry: `npm add @rdi-ui/sdk`.\n- **More collector diagnostics in API v2 metric collections**: The `GET /pipelines/{name}/metric-collections` endpoint now reports the full set of numeric Debezium collector metrics per source: event counts by operation, filtered and erroneous events, queue capacity and byte usage, the last processed transaction id, MySQL and MariaDB binlog health counters and GTID set, MongoDB primary elections, and Oracle LogMiner SCN, lag, performance, and error metrics.\n- **Pipeline components report their externally-reachable endpoints**: Each component in an API v2 pipeline or pipeline status response now has an `external_endpoints` field listing the URLs at which that component is reachable from outside the cluster.\n- **Source mTLS without Debezium keystore settings**: A MySQL, MariaDB, or MongoDB source no longer requires setting `database.ssl.keystore`/`mongodb.ssl.keystore` and the matching password in `advanced.source` for mTLS to work correctly.\n- **Removing a source from a pipeline deletes its internal RDI data**: Removing a source with `PUT` or `PATCH /pipelines/{name}` now deletes its internal RDI data, including its change streams, offsets, schema history, dead letter queue entries, statistics, deduplication state, and record counters. That data used to be kept indefinitely, and only resetting the pipeline removed it. Records already written to the target Redis database are not deleted.\n- **`redis-di deploy --empty` clears a pipeline's configuration**: The new `--empty` flag, mutually exclusive with `--dir`, deploys an empty configuration instead of loading one from a directory. An empty pipeline is left stopped unless `--start` is passed explicitly. See [Clear a pipeline](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/deploy#clear-a-pipeline)."},{"id":"bug-fixes","title":"Bug Fixes","role":"content","text":"- **Collector API startup on OpenShift**: Fixed a regression introduced in RDI 1.19.1 that caused pod creation to be rejected by standard OpenShift security policies when `global.openShift` is enabled, while preserving source certificate permissions.\n- **RDI database client certificates isolated from source certificates**: The Debezium collector now presents its RDI database client certificate from a dedicated keystore instead of the shared default keystore, so the source database and RDI database client identities can no longer interfere with each other under mTLS.\n- **Nested processor properties displayed correctly in `redis-di describe`**: Nested objects and arrays in a processor's advanced properties are now shown as JSON instead of a raw internal format, so the `redis-di describe` output is valid and easy to read.\n- **Bulk inserts after a snapshot no longer restart the Flink processor**: Fixed an issue where a bulk insert arriving just after a snapshot could lead to a Flink job restart with `Have records for a split that was not registered` during the transition to CDC.\n- **Streams continue processing after a Flink processor restart**: A narrow timing window around a checkpoint could leave a stream permanently unassigned to any reader after a restart (for example following a TaskManager failure), silently halting ingestion for that stream until the pipeline was redeployed. The processor now confirms with every reader before treating a stream as no longer needed.\n- **Unreachable source database reported as a validation error**: When RDI cannot list the source tables during pipeline validation, for example because the source database refuses the connection, the failure is now reported as a `422` validation error in the `errors` array, instead of a `422`, `502`, or `504` response carrying only a `detail` message. An unavailable collector API is still reported as a `503` response so the request can be retried.\n- **Redis configuration template requests return `501 Not Implemented`**: `GET /pipelines/config/templates/ingest/redis` now returns the documented `501 Not Implemented`, instead of a `200` response with an incorrect configuration. Redis cannot be used as a pipeline source.\n- **A database flavor that does not match the database type is rejected**: `GET /pipelines/config/templates/ingest/{db_type}` now returns a `422` response when `db_flavor` names a MongoDB flavor and `db_type` is not `mongodb`, instead of a `200` response with an incorrect configuration.\n- **Clearing a whole configuration section with `PATCH` is rejected**: `PATCH /pipelines/{name}` now returns a `422` validation error when `processors`, `sources`, or `targets` is set to null. Remove a section's entries individually, as in `{\"sources\": {\"mysql\": null}}`, or replace the whole configuration with `PUT`.\n- **A missing pipeline's 404 error message contains its name**: A `404` response for a pipeline that doesn't exist is now `Pipeline '<name>' not found`, instead of `Pipeline not found`, consistent with the other error messages."},{"id":"improvements","title":"Improvements","role":"content","text":"- **API response body logging**: The RDI API now logs response bodies alongside request payloads. Bodies are logged at DEBUG level, while request and response metadata stays at INFO, and large bodies are truncated to keep logs manageable.\n- **Documented scaffolded configurations**: The configuration that `redis-di scaffold`, the configuration template endpoint, and `rdi-admin install` generate now documents every property it offers, with a description above each one, links to the pipeline configuration documentation, and the default value where one exists. It also covers Snowflake and it no longer suggests properties that the source database does not support. The target port now defaults to `6379` rather than a placeholder, so the generated configuration passes schema validation before any property is filled in. The per-database example configurations that `rdi-admin install` wrote alongside it (`config.yaml.mysql.example` and its siblings) have been removed, since the generated configuration and the documentation now cover the same ground.\n- **Documented transformation job template**: The job template that `GET /pipelines/jobs/templates/ingest` returns now documents the properties of the source, transform, and output blocks, with a description above each one and links to the transformation documentation, instead of naming a handful of them in a bare skeleton. It is a valid job as returned, with only the mandatory properties left uncommented. The endpoint accepts a new optional `source_name` query parameter that specifies the source the job reads from, matching the parameter of the configuration template endpoint. `rdi-admin install` now writes the same template to `jobs/job.yaml`, named after the source it scaffolds, in place of the example jobs it used to copy there.\n- **Debezium collector processes captured records on four threads**: The Debezium collector now sets `record.processing.threads` to 4 rather than letting Debezium size the pool by itself, which raises snapshot throughput considerably. Lower it through a source's `advanced.source.record.processing.threads` when fewer CPUs are available to the collector.\n- **Faster pipeline reset**: Resetting a pipeline is faster and no longer creates a Kubernetes Job."},{"id":"security","title":"Security","role":"security","text":"- **Hardened API log redaction**: Ensured secrets, credentials, tokens, and connection strings are masked in the request and response bodies the RDI API logs, with login and pipeline secret payloads fully redacted.\n- **Flink processor and collector security updates**: Resolved `CVE-2024-57699`, `CVE-2025-12183`, `CVE-2025-27820`, `CVE-2025-55163`, `CVE-2025-66566`, `CVE-2025-68973`, `CVE-2025-69720`, `CVE-2026-5260`, `CVE-2026-8376`, `CVE-2026-8925`, `CVE-2026-11822`, `CVE-2026-11824`, `CVE-2026-15146`, `CVE-2026-33845`, `CVE-2026-35194`, `CVE-2026-42010`, `CVE-2026-42013`, `CVE-2026-42198`, `CVE-2026-42496`, `CVE-2026-42583`, `CVE-2026-44249`, `CVE-2026-45416`, `CVE-2026-45447`, `CVE-2026-50010`, `CVE-2026-50813`, `CVE-2026-54291`, `CVE-2026-54399`, `CVE-2026-54428`, `CVE-2026-54512`, `CVE-2026-54513`, `CVE-2026-58472`, `CVE-2026-59901`, and `GHSA-r7wm-3cxj-wff9`.\n- **Monitor security updates**: Resolved `CVE-2024-6345` and `CVE-2025-47273`.\n- **Collector API security updates**: Resolved `CVE-2025-66566`, `CVE-2026-10050`, `CVE-2026-40973`, `CVE-2026-40983`, `CVE-2026-40984`, `CVE-2026-42198`, `CVE-2026-42579`, `CVE-2026-42583`, `CVE-2026-44249`, `CVE-2026-45416`, `CVE-2026-45674`, `CVE-2026-47691`, `CVE-2026-50010`, `CVE-2026-54291`, `CVE-2026-54512`, `CVE-2026-54513`, `CVE-2026-59901`, and `GHSA-r7wm-3cxj-wff9`.\n- **Shared utilities security updates**: Resolved `CVE-2026-21441` and `CVE-2026-33154`.\n- **Operator security updates**: Resolved `CVE-2026-24051`, `CVE-2026-33186`, `CVE-2026-35469`, `CVE-2026-39821`, `CVE-2026-39829`, `CVE-2026-39830`, `CVE-2026-39831`, `CVE-2026-39832`, `CVE-2026-39833`, `CVE-2026-39834`, `CVE-2026-42508`, `CVE-2026-46595`, `CVE-2026-46597`, `CVE-2026-46680`, `CVE-2026-50151`, `CVE-2026-50163`, `CVE-2026-53488`, and `GHSA-hrxh-6v49-42gf`.\n- **Collector initializer security update**: Upgraded jq to resolve `CVE-2026-32316`, `CVE-2026-33947`, `CVE-2026-33948`, `CVE-2026-39979`, `CVE-2026-40612`, `CVE-2026-41256`, `CVE-2026-41257`, `CVE-2026-43894`, `CVE-2026-43895`, `CVE-2026-43896`, `CVE-2026-44777`, `CVE-2026-47770`, `CVE-2026-49839`, and `CVE-2026-54679`.\n- **Fluentd security updates**: Resolved 20 unique CVEs compared with the previous image, including 1 Critical and 11 High findings: `CVE-2025-46394`, `CVE-2025-60876`, `CVE-2025-6442`, `CVE-2026-7383`, `CVE-2026-9076`, `CVE-2026-22184`, `CVE-2026-28387`, `CVE-2026-28388`, `CVE-2026-28389`, `CVE-2026-28390`, `CVE-2026-31789`, `CVE-2026-31790`, `CVE-2026-34180`, `CVE-2026-34182`, `CVE-2026-42766`, `CVE-2026-42767`, `CVE-2026-42770`, `CVE-2026-45445`, `CVE-2026-45446`, and `CVE-2026-45447`.\n- **Classic processor security updates**: Resolved `CVE-2024-6345`, `CVE-2025-47273`, and `CVE-2025-67221`.\n- **API security updates**: Resolved `CVE-2024-53981`, `CVE-2024-6345`, `CVE-2025-47273`, `CVE-2025-62727`, `CVE-2026-24486`, `CVE-2026-32597`, `CVE-2026-42561`, `CVE-2026-48522`, `CVE-2026-48523`, `CVE-2026-48524`, `CVE-2026-48525`, `CVE-2026-48526`, `CVE-2026-48710`, `CVE-2026-53539`, and `CVE-2026-54283`.\n- **Metrics aggregator security updates**: Resolved `CVE-2024-6345`, `CVE-2025-47273`, `CVE-2025-62727`, `CVE-2025-66418`, `CVE-2025-66471`, `CVE-2026-23490`, `CVE-2026-26007`, `CVE-2026-30922`, `CVE-2026-32597`, `CVE-2026-48710`, and `CVE-2026-54283`."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-data-integration/troubleshooting","title":"Troubleshooting","url":"https://redis.io/docs/latest/integrate/redis-data-integration/troubleshooting/","summary":"Solve and report simple problems with RDI","aliases":["/integrate/redis-data-integration/ingest/troubleshooting/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-10-13T23:41:57+03:00","page_type":"content","content_hash":"e7b0112e47772c6a4da455ec008a997193d98e03c97fb61aebd000149b7246ba","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The following sections explain how you can get extra information from\nRedis Data Integration (RDI) to help you solve problems that you may encounter. Redis support may\nalso ask you to provide this information to help you resolve issues."},{"id":"install-debug","title":"Debug information during installation","role":"content","text":"If the installer fails with an error, then try installing again with the\nlog level set to `DEBUG`:\n\n[code example]\n\nThis gives you more detail about the installation steps and can often\nhelp you to pinpoint the source of the error."},{"id":"rdi-logs","title":"RDI logs","role":"content","text":"By default, RDI records the following logs in the host VM file system at\n`/opt/rdi/logs` (or whichever path you specified during installation);\n\n| Filename | Phase |\n| :-- | :-- |\n| `rdi_collector-collector-initializer.log` | Initializing the collector. |\n| `rdi_collector-debezium-ssl-init.log` | Establishing the connector SSL connections to the source and RDI database (if you are using SSL). |\n| `rdi_collector-collector-source.log` | Collector [change data capture (CDC)](https://redis.io/docs/latest/integrate/redis-data-integration/architecture) operations. |\n| `rdi_rdi-rdi-operator.log` | Main [RDI control plane](https://redis.io/docs/latest/integrate/redis-data-integration/architecture#how-rdi-is-deployed) component. |\n| `rdi_processor-processor.log` | RDI stream processing. |\n\nLogs are recorded at the minimum `INFO` level in a simple format that\nlog analysis tools can use.\n\n> [!NOTE]\n> Often during the initial sync phase, the collector source log will contain a message\n> saying RDI is out of\n> memory. This is not an error but an informative message to say that RDI\n> is applying *backpressure* to the collector. See\n> [Backpressure mechanism](https://redis.io/docs/latest/integrate/redis-data-integration/architecture#backpressure-mechanism)\n> in the Architecture guide for more information."},{"id":"unavailable-source-history","title":"Recover from unavailable source log history","role":"content","text":"If the collector cannot resume from its saved position, check the collector source log\nfor unavailable or expired source log history. This differs from a missing offset,\nwhich is expected on first deployment or after a reset.\n\nWith `initial` mode, the collector can fail rather than start a new snapshot when the saved\nposition is unavailable. With `when_needed` mode, it will automatically snapshot the current\nselected data and resume streaming. A new snapshot after a restart can therefore\nindicate that the source history was no longer available. See\n[Choose a snapshot mode](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config#choose-a-snapshot-mode)\nfor the connector-specific behavior.\n\nPossible causes include:\n\n- The collector was stopped long enough for the source change log to expire.\n- A configuration change made the collector connect to another server with the same\n  data but different offsets.\n- A cleanup process removed the change log while RDI was running.\n- A crash and restore left the source without the saved offset.\n\nBefore you use a new snapshot to recover, check for\n[missed deletes and stale target records](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config#missed-deletes).\nA snapshot does not replay lost history or delete target records that are absent from\nthe source. Resetting the pipeline also leaves target records in place.\n\nIf the saved position is unavailable, work with Redis support to plan a target cleanup\nor rebuild before relying on the target as a complete copy. Check whether the target\ncontains data from other sources or applications before deleting any records. To reload\nexisting source rows, configure a mode that takes a data snapshot, then\n[reset the pipeline](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/deploy#reset-a-pipeline).\nReview the source's log retention so the required history remains available during\nfuture interruptions."},{"id":"dump-support-package","title":"Dump support package","role":"content","text":"If you need to send a comprehensive set of forensics data to Redis support,\nrun the\n[`redis-di dump-support-package`](https://redis.io/docs/latest/integrate/redis-data-integration/reference/cli/redis-di-dump-support-package)\ncommand from the CLI.\n\nThis command gathers the following data:\n\n- All the internal RDI components and their status\n- All internal RDI configuration\n- List of secret names used by RDI components (but not the secrets themselves)\n- RDI logs\n- RDI component versions\n- Text of the `config.yaml` file\n- Text of the Job configuration files\n- Rejected records along with the reason for their rejection (should not exist in production)"}],"examples":[{"id":"install-debug-ex0","language":"bash","code":"./install.sh --log-level DEBUG","section_id":"install-debug"}]}
{"schema_version":2,"id":"integrate/redis-data-integration/when-to-use","title":"When to use RDI","url":"https://redis.io/docs/latest/integrate/redis-data-integration/when-to-use/","summary":"Understand when (and when not) to use RDI.","tags":["docs","integrate","rs","rdi"],"last_updated":"2026-10-02T10:31:14+01:00","page_type":"content","content_hash":"98f3112283fe8b09bd8e081e08519a363ddefba979b7df0541665212d6cafa4f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"RDI is designed to support apps that must use a disk-based database as the system of record\nbut must also be fast and scalable. This is a common requirement for mobile and web\napps with a rapidly-growing number of users; the performance of the main database is fine at first\nbut it will soon struggle to handle the increasing demand without a cache."},{"id":"guidelines-for-using-rdi","title":"Guidelines for using RDI","role":"content","text":"Use the information in the sections below to determine whether RDI is a good fit for your architecture.\n\n"},{"id":"when-to-use-rdi","title":"When to use RDI","role":"content","text":"RDI is a good fit when:\n\n- You want your app/micro-services to read from Redis to scale reads at speed.\n- You want to transfer data to Redis from one or more source databases.\n- You must use a slow database as the system of record for the app.\n- The app must always *write* its data to the slow database.\n- Your app can tolerate *eventual* consistency of data in the Redis cache.\n- You want a self-managed solution or AWS based solution.\n- The source data changes frequently in small increments.\n- The source database has no more than 20K changes per second.\n- RDI throughput during [full sync](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines#pipeline-lifecycle)\n  stays below 60K records per second, assuming an average record size of 1KB and a pipeline without transformations.\n- RDI throughput during [CDC](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines#pipeline-lifecycle)\n  stays below 20K records per second, assuming an average record size of 1KB and a pipeline without transformations.\n- The total data size is no larger than 200GB, so a full sync completes in under an hour without exceeding the throughput\n  limits above. RDI can ingest larger datasets, but it will take longer than an hour.\n- You don’t need to perform join operations on the data from several tables\n  into a [nested Redis JSON object](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/data-denormalization#joining-one-to-many-relationships).\n- RDI supports the [data transformations](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples) you need for your app.\n- Your data caching needs are too complex or demanding to implement and maintain yourself.\n- Your database administrator has reviewed RDI's requirements for the source database and\n  confirmed that they are acceptable.\n\n\n> [!NOTE]\n> The throughput and data-size limits above apply to the\n> [Flink processor](https://redis.io/docs/latest/integrate/redis-data-integration/architecture/classic-vs-flink),\n> which RDI 1.18.0 introduced and which is the default processor from RDI 2.0.0.\n> The classic processor supports about half of each limit."},{"id":"when-not-to-use-rdi","title":"When not to use RDI","role":"content","text":"RDI is not a good fit when:\n\n- You are migrating an existing data set into Redis only once.\n- Your app needs *immediate* cache consistency (or a hard limit on latency) rather\n  than *eventual* consistency.\n- You need *transactional* consistency between the source and target databases.\n- The app must *write* data to the Redis cache, which then updates the source database\n  (write-behind/write-through patterns).\n- Your data set will only ever be small.\n- Your data is updated by some batch or ETL process with long and large transactions - RDI will fail\n  processing these changes.\n- You need complex stream processing of data (aggregations, sliding window processing, complex \n  custom logic).\n- You need to write data to multiple targets from the same pipeline (Redis supports other\n  ways to replicate data across Redis databases such as replicaOf and Active-Active).\n- Your database administrator has rejected RDI's requirements for the source database."},{"id":"decision-tree-for-using-rdi","title":"Decision tree for using RDI","role":"content","text":"Use the decision tree below to determine whether RDI is a good fit for your architecture:\n\n\n[code example]"}],"examples":[{"id":"decision-tree-for-using-rdi-ex0","language":"decision-tree {id=\"when-to-use-rdi\"}","code":"id: when-to-use-rdi\nscope: rdi\nindentWidth: 25\nrootQuestion: cacheTarget\nquestions:\n    cacheTarget:\n        text: |\n            Do you want to use Redis as the target database?\n        whyAsk: |\n            RDI is specifically designed to keep Redis in sync with a primary database. If you don't need Redis as a cache, RDI is not the right tool.\n        answers:\n            no:\n                value: \"No\"\n                outcome:\n                    label: \"❌ RDI only works with Redis as the target database\"\n                    id: noRedisCache\n                    sentiment: \"negative\"\n            yes:\n                value: \"Yes\"\n                nextQuestion: deployment\n    deployment:\n        text: |\n            Do you want a self-managed solution or an AWS-based solution?\n        whyAsk: |\n            RDI is available as a self-managed solution or as an AWS-based managed service. If you need a different deployment model, RDI may not be suitable.\n        answers:\n            no:\n                value: \"No\"\n                outcome:\n                    label: \"⚠️ Check deployment options to see if RDI is suitable for your needs before proceeding\"\n                    id: deploymentMismatch\n                    sentiment: \"indeterminate\"\n            yes:\n                value: \"Yes\"\n                nextQuestion: consistency\n    consistency:\n        text: |\n            Can your app tolerate eventual consistency in the Redis cache?\n        whyAsk: |\n            RDI provides eventual consistency, not immediate consistency. If your app needs real-time cache consistency or hard latency limits, RDI is not suitable.\n        answers:\n            no:\n                value: \"No\"\n                outcome:\n                    label: \"⚠️ Check that RDI's performance meets your latency requirements before proceeding (RDI can't guarantee *immediate* consistency)\"\n                    id: needsImmediate\n                    sentiment: \"indeterminate\"\n            yes:\n                value: \"Yes\"\n                nextQuestion: systemOfRecord\n    systemOfRecord:\n        text: |\n            Does your app always *write* to the source database and not to Redis?\n        whyAsk: |\n            RDI requires the source database to be the authoritative source of truth. If your app writes to Redis first, RDI won't work.\n        answers:\n            no:\n                value: \"No\"\n                outcome:\n                    label: \"❌ RDI doesn't support syncing data from Redis back to the source database\"\n                    id: notSystemOfRecord\n                    sentiment: \"negative\"\n            yes:\n                value: \"Yes\"\n                nextQuestion: dataChangePattern\n    dataChangePattern:\n        text: |\n            Does your source data change frequently in small increments?\n        whyAsk: |\n            RDI captures changes from the database transaction log. Large batch transactions or ETL processes can cause RDI to fail.\n        answers:\n            no:\n                value: \"No\"\n                outcome:\n                    label: \"⚠️ Check that RDI can handle your data change pattern before proceeding (RDI will fail with batch/ETL processes and transactions beyond a certain size)\"\n                    id: batchProcessing\n                    sentiment: \"indeterminate\"\n            yes:\n                value: \"Yes\"\n                nextQuestion: changeRate\n    changeRate:\n        text: |\n            Are there fewer than 20K changes per second in the source database?\n        whyAsk: |\n            RDI has throughput limits. Exceeding these limits will cause processing failures and data loss.\n        answers:\n            no:\n                value: \"No\"\n                outcome:\n                    label: \"⚠️ RDI is fast but there are practical limits on throughput - check that RDI can handle your change rate before proceeding\"\n                    id: exceedsChangeRate\n                    sentiment: \"indeterminate\"\n            yes:\n                value: \"Yes\"\n                nextQuestion: dataSize\n    dataSize:\n        text: |\n            Is your total data size smaller than 200GB?\n        whyAsk: |\n            RDI has practical limits on the total data size it can manage, based\n            on the throughput requirements for full sync.\n        answers:\n            no:\n                value: \"No\"\n                outcome:\n                    label: \"⚠️ RDI might be unacceptably slow during the full-sync phase. Check that performance will be acceptable for your needs\"\n                    id: dataTooLarge\n                    sentiment: \"indeterminate\"\n            yes:\n                value: \"Yes\"\n                nextQuestion: joins\n    joins:\n        text: |\n            Do you need to perform join operations on data from several tables into a nested Redis JSON object?\n        whyAsk: |\n            RDI has limitations with complex join operations. If you need to combine data from multiple tables into nested structures, you may need custom transformations.\n        answers:\n            yes:\n                value: \"Yes\"\n                outcome:\n                    label: \"⚠️ RDI may not be suitable - complex joins are not well supported, so check that RDI's data transformations will meet your needs\"\n                    id: complexJoins\n                    sentiment: \"indeterminate\"\n            no:\n                value: \"No\"\n                nextQuestion: transformations\n    transformations:\n        text: |\n            Does RDI support the data transformations you need for your app?\n        whyAsk: |\n            RDI provides built-in transformations, but if you need custom logic beyond what RDI supports, you may need a different approach.\n        answers:\n            no:\n                value: \"No\"\n                outcome:\n                    label: \"⚠️ RDI supports a wide range of data transformations, but doesn't support free-form code execution. Check that RDI's data transformations will meet your needs\"\n                    id: unsupportedTransformations\n                    sentiment: \"indeterminate\"\n            yes:\n                value: \"Yes\"\n                nextQuestion: adminReview\n    adminReview:\n        text: |\n            Has your database administrator reviewed RDI's requirements for the source database\n            and confirmed they are acceptable?\n        whyAsk: |\n            RDI has specific requirements for the source database (binary logging, permissions, etc.). Your DBA must confirm these are acceptable before proceeding.\n        answers:\n            no:\n                value: \"No\"\n                outcome:\n                    label: \"⚠️ RDI has requirements that might conflict with practical considerations for your database (such as security policies). Check with your DBA before proceeding\"\n                    id: adminReviewNeeded\n                    sentiment: \"indeterminate\"\n            yes:\n                value: \"Yes\"\n                outcome:\n                    label: \"✅ RDI is a good fit for your use case\"\n                    id: goodFit\n                    sentiment: \"positive\"","section_id":"decision-tree-for-using-rdi"}]}
{"schema_version":2,"id":"integrate/redis-mcp/client-conf","title":"Configure client apps","url":"https://redis.io/docs/latest/integrate/redis-mcp/client-conf/","summary":"Configure client apps to use the Redis MCP server.","aliases":["/integrate/redis-mcp/ciient-conf/"],"tags":["docs","integrate","rs"],"last_updated":"2026-09-17T14:55:03+01:00","page_type":"content","content_hash":"fea47094f2158a31dfea8569b8adce70da20f2dca8d1c8abf8c6a09fb7303107","sections":[{"id":"overview","title":"Overview","role":"overview","text":"When you have [installed](https://redis.io/docs/latest/integrate/redis-mcp/install)\nthe Redis MCP server, you must also configure your client app to use it.\nThe sections below describe the ways you can do this."},{"id":"smithery","title":"Smithery","role":"content","text":"[Smithery](https://smithery.ai/) provides a searchable repository of scripts\nthat add configurations for many MCP services to client apps.\nThe easiest way to configure your client is to use the\n[Smithery tool for Redis MCP](https://smithery.ai/server/@redis/mcp-redis).\n\nWhen you select your client from the **Install** bar on the Redis MCP page,\nyou will see a command line that you can copy and paste into a terminal.\nRunning this command will configure your client app to use Redis MCP. (Note\nthat you must have [Node.js](https://nodejs.org/en) installed to run\nthe Smithery scripts.) For example, the command line for\n[Claude Desktop](https://claude.ai/download) is\n\n[code example]\n\nThe script will prompt you for the information required to connect to\nyour Redis database."},{"id":"manual-configuration","title":"Manual configuration","role":"content","text":"You can also add the configuration for Redis MCP to your client app\nmanually. The exact method varies from client to client but the\nbasic approach is similar in each case. The sections below\ngive the general configuration details for some common MCP client tools:\n\n-   [Augment Code](#augment-code)\n-   [Claude Desktop](#claude-desktop)\n-   [OpenAI Agents](#openai-agents)\n-   [VS Code with GitHub Copilot](#vs-code-with-github-copilot)"},{"id":"augment-code","title":"Augment Code","role":"content","text":"Redis supports Augment Code's\n[Easy MCP](https://docs.augmentcode.com/setup-augment/mcp#easy-mcp%3A-one-click-integrations)\nfeature in [VSCode](https://docs.augmentcode.com/setup-augment/mcp#getting-started-with-easy-mcp)\nand the [JetBrains IDEs](https://docs.augmentcode.com/jetbrains/setup-augment/mcp#getting-started-with-easy-mcp)\nto install the server in seconds:\n\n1.  Open the Augment settings panel and\n    navigate to the **MCP** pane.\n1.  Click the \"+\" button next to Redis in the\n    **Easy MCP installation** list and enter the connection details for your Redis database.\n1.  Click **Install** to start using Redis MCP.\n\nIf you need to supply environment variables or command line parameters:\n\n1.  Click the **Add MCP** button underneath the list of Easy MCP integrations.\n1.  Enter `Redis` in the name field and paste the appropriate command line in the\n    command field (see \n    [Configuration](https://redis.io/docs/latest/integrate/redis-mcp/install#configuration) for\n    more information about the available command line options).\n1.  Click the **+ Variable** button to add any environment variables that you need.\n1.  Click **Add** to add the server."},{"id":"claude-desktop","title":"Claude Desktop","role":"content","text":"First, locate the configuration file by selecting **Settings** from the menu, then selecting the **Developer** tab, and then clicking the **Edit Config** button. Open this JSON file and add your settings as shown below to run Redis MCP with [`uvx`](https://docs.astral.sh/uv/guides/tools/#running-tools):\n\n[code example]\n\nYou can also optionally set the environment for the command shell here in the\n`env` section:\n\n[code example]\n\nIf you are using\n[Docker](https://redis.io/docs/latest/integrate/redis-mcp/install#install-using-docker)\nto deploy the server, change the `command` and `args` sections of the\nconfiguration as shown below:\n\n[code example]\n\nFor more information about using MCP with Claude Desktop, see\n[Connect to Local MCP Servers](https://modelcontextprotocol.io/quickstart/user)."},{"id":"openai-agents","title":"OpenAI Agents","role":"content","text":"The\n[`redis_assistant.py`](https://github.com/redis/mcp-redis/blob/main/examples/redis_assistant.py)\nfile in the [`mcp-redis`](https://github.com/redis/mcp-redis) repository contains an\nexample of how to configure OpenAI Agents to use Redis MCP.\n\nTo use this example, install the `openai-agents` library, ensure\nyou have exported the OpenAI token in the `OPENAI_API_KEY` environment variable\nand run the `redis_assistant.py` script:\n\n[code example]\n\nSee the\n[OpenAI Agents SDK documentation](https://openai.github.io/openai-agents-python/mcp/)\nfor more information about using MCP servers."},{"id":"vs-code-with-github-copilot","title":"VS Code with GitHub Copilot","role":"content","text":"To use Redis MCP with VS Code, first add the following to your `settings.json`\nfile to enable the\n[agent mode tools](https://code.visualstudio.com/docs/copilot/chat/chat-agent-mode):\n\n[code example]\n\nThen, add the following lines to `settings.json` to run Redis MCP\nwith [`uvx`](https://docs.astral.sh/uv/guides/tools/#running-tools):\n\n[code example]\n\nYou can also add\n[environment variables](https://redis.io/docs/latest/integrate/redis-mcp/install#environment-variables) \nin the `env` section of the configuration:\n\n[code example]\n\nSee\n[Use MCP servers in VS Code](https://code.visualstudio.com/docs/copilot/chat/mcp-servers)\nfor more information."},{"id":"redis-cloud-mcp","title":"Redis Cloud MCP","role":"content","text":"If you are using\n[Redis Cloud MCP](https://redis.io/docs/latest/integrate/redis-mcp/install#redis-cloud-mcp),\nthe configuration is similar to [basic MCP](#manual-configuration), but with a\nfew differences. Set the client to run the server using the `node` command, as shown \nin the example for Claude Desktop below:\n\n[code example]\n\nHere, the environment includes the Redis Cloud API key and API secret key\n(see [Redis Cloud REST API](https://redis.io/docs/latest/operate/rc/api) for more\ninformation).\n\nIf you are deploying Redis Cloud MCP with Docker, use a configuration like\nthe following to launch the server with the `docker` command:\n\n[code example]"}],"examples":[{"id":"smithery-ex0","language":"bash","code":"npx -y @smithery/cli@latest install @redis/mcp-redis --client claude","section_id":"smithery"},{"id":"claude-desktop-ex0","language":"json","code":"{\n    \"mcpServers\": {\n        .\n        .\n        \"redis-mcp-server\": {\n            \"type\": \"stdio\",\n            \"command\": \"uvx\",\n            \"args\": [\n                \"--from\", \"redis-mcp-server@latest\",\n                \"redis-mcp-server\",\n                \"--url\", \"redis://localhost:6379/0\"\n            ]\n        }\n    }\n        .\n        .\n  }","section_id":"claude-desktop"},{"id":"claude-desktop-ex1","language":"json","code":"\"redis\": {\n    \"type\": \"stdio\",\n    \"command\": \"uvx\",\n    \"args\": [\n        \"--from\", \"redis-mcp-server@latest\",\n        \"redis-mcp-server\",\n        \"--url\", \"redis://localhost:6379/0\"\n    ],\n    \"env\": {\n        \"REDIS_HOST\": \"<your_redis_database_hostname>\",\n        \"REDIS_PORT\": \"<your_redis_database_port>\",\n        \"REDIS_PWD\": \"<your_redis_database_password>\",\n        \"REDIS_SSL\": True|False,\n        \"REDIS_CA_PATH\": \"<your_redis_ca_path>\",\n        \"REDIS_CLUSTER_MODE\": True|False\n    }\n}","section_id":"claude-desktop"},{"id":"claude-desktop-ex2","language":"json","code":"\"redis\": {\n    \"command\": \"docker\",\n    \"args\": [\"run\",\n            \"--rm\",\n            \"--name\",\n            \"redis-mcp-server\",\n            \"-i\",\n            \"-e\", \"REDIS_HOST=<redis_hostname>\",\n            \"-e\", \"REDIS_PORT=<redis_port>\",\n            \"-e\", \"REDIS_USERNAME=<redis_username>\",\n            \"-e\", \"REDIS_PWD=<redis_password>\",\n            \"mcp-redis\"]\n}","section_id":"claude-desktop"},{"id":"openai-agents-ex0","language":"bash","code":"pip install openai-agents\nexport OPENAI_API_KEY=<your_openai_api_key>\npython3 redis_assistant.py","section_id":"openai-agents"},{"id":"vs-code-with-github-copilot-ex0","language":"json","code":"{\n  \"chat.agent.enabled\": true\n}","section_id":"vs-code-with-github-copilot"},{"id":"vs-code-with-github-copilot-ex1","language":"json","code":".\n  .\n\"mcp\": {\n    \"servers\": {\n        \"Redis MCP Server\": {\n        \"type\": \"stdio\",\n        \"command\": \"uvx\", \n        \"args\": [\n            \"--from\", \"redis-mcp-server@latest\",\n            \"redis-mcp-server\",\n            \"--url\", \"redis://localhost:6379/0\"\n        ]\n        },\n    }\n},\n  .\n  .","section_id":"vs-code-with-github-copilot"},{"id":"vs-code-with-github-copilot-ex2","language":"json","code":"\"Redis MCP Server\": {\n    .\n    .\n  \"env\": {\n    \"REDIS_HOST\": \"<your_redis_database_hostname>\",\n    \"REDIS_PORT\": \"<your_redis_database_port>\",\n    \"REDIS_USERNAME\": \"<your_redis_database_username>\",\n    \"REDIS_PWD\": \"<your_redis_database_password>\",\n      .\n      .\n  }\n}","section_id":"vs-code-with-github-copilot"},{"id":"redis-cloud-mcp-ex0","language":"json","code":"{\n  \"mcpServers\": {\n    \"mcp-redis-cloud\": {\n      \"command\": \"node\",\n      \"args\": [\"--experimental-fetch\", \"<absolute_path_to_project_root>/dist/index.js\"],\n      \"env\": {\n        \"API_KEY\": \"<redis_cloud_api_key>\",\n        \"SECRET_KEY\": \"<redis_cloud_api_secret_key>\"\n      }\n    }\n  }\n}","section_id":"redis-cloud-mcp"},{"id":"redis-cloud-mcp-ex1","language":"json","code":"{\n  \"mcpServers\": {\n    \"redis-cloud\": {\n      \"command\": \"docker\",\n      \"args\": [\n        \"run\",\n        \"-i\",\n        \"--rm\",\n        \"-e\",\n        \"API_KEY=<your_redis_cloud_api_key>\",\n        \"-e\",\n        \"SECRET_KEY=<your_redis_cloud_api_secret_key>\",\n        \"mcp/redis-cloud\"\n      ]\n    }\n  }\n}","section_id":"redis-cloud-mcp"}]}
{"schema_version":2,"id":"integrate/redis-mcp","title":"Redis MCP","url":"https://redis.io/docs/latest/integrate/redis-mcp/","summary":"Access a Redis server using any MCP client.","tags":["docs","integrate","rs"],"last_updated":"2025-05-19T15:51:49+01:00","children":[{"id":"integrate/redis-mcp/install","summary":"Install and configure the Redis MCP server.","title":"Install","url":"https://redis.io/docs/latest/integrate/redis-mcp/install/"},{"id":"integrate/redis-mcp/client-conf","summary":"Configure client apps to use the Redis MCP server.","title":"Configure client apps","url":"https://redis.io/docs/latest/integrate/redis-mcp/client-conf/"}],"page_type":"content","content_hash":"526000de3723b88a48c2cd68696240322e0303ab363232f8f3d3cbb409fa81c9","sections":[{"id":"content","title":"Content","role":"content","text":"The [Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction)\nis a standard that lets AI agents access data and perform actions. Using MCP,\nyour server can publish a set of commands that are usable by any MCP-compatible\nclient app (such as [Claude Desktop](https://claude.ai/download) or\n[VS Code](https://code.visualstudio.com/)). These commands can retrieve\nwhatever data you wish to provide and you can also let the agent make\nchanges to the data. For example, you could publish a feed of news items that\nan agent can use in its responses, and also let the agent add the user's\ncomments to those items.\n\nRedis MCP is a general-purpose implementation that lets agents read, write, and\nquery data in Redis and also has some basic commands to manage the Redis\nserver. With this enabled, you can use an LLM client as a very high-level\ninterface to Redis. Add, query, and analyze any Redis data set directly from\nan LLM chat using instructions and questions like the following:\n\n- \"Store the entire conversation in the 'recent_chats' stream\"\n- \"Cache this item\"\n- \"How many keys does my database have?\"\n- \"What is user:1's email?\"\n\nSee the other pages in this section to learn how to set up and use Redis MCP.\nSee also the [GitHub repository](https://github.com/redis/mcp-redis) for\nthe latest changes."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-mcp/install","title":"Install","url":"https://redis.io/docs/latest/integrate/redis-mcp/install/","summary":"Install and configure the Redis MCP server.","tags":["docs","integrate","rs"],"last_updated":"2026-09-17T14:55:03+01:00","page_type":"content","content_hash":"b54a40ef2c719eaa084869704a746471dada6cf94d355cb6a81999ab1dcee798","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The MCP server runs separately from Redis, so you will need a\nRedis server for it to connect to. See [Redis Cloud](https://redis.io/docs/latest/operate/rc)\nor [Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack) to learn\nhow to get a test server active within minutes.\n\nWhen you have a Redis server available, use the instructions below to install and\nconfigure the Redis MCP server."},{"id":"quick-start-with-pypi","title":"Quick Start with PyPI","role":"content","text":"The easiest way to use the Redis MCP Server is with\n[`uvx`](https://docs.astral.sh/uv/guides/tools/),\nwhich lets you run it from its [PyPI package](https://pypi.org/project/redis-mcp-server/)\n(see the `uv`\n[installation instructions](https://github.com/astral-sh/uv?tab=readme-ov-file#installation)\nfor more information.)\n\n[code example]"},{"id":"run-from-github","title":"Run from GitHub","role":"content","text":"Running from [PyPI](#quick-start-with-pypi)\nis the recommended way to use the Redis MCP server, but\nyou can also run it from a GitHub branch or a tagged release with `uvx`:\n\n[code example]"},{"id":"install-the-server-from-source","title":"Install the server from source","role":"setup","text":"You can also run Redis MCP from source, which may be useful if you want to\ncontribute to the project.\n\nClone Redis MCP from the\n[Github repository](https://github.com/redis/mcp-redis) using the following\ncommand:\n\n[code example]\n\nYou will also need the [`uv`](https://github.com/astral-sh/uv) packaging\ntool to set up the server. See the `uv`\n[installation instructions](https://github.com/astral-sh/uv?tab=readme-ov-file#installation)\nfor more information.\n\nWhen you have installed `uv`, go to the `mcp-redis` folder that you cloned and\nenter the following commands to initialize the MCP server code:\n\n[code example]"},{"id":"install-using-docker","title":"Install using Docker","role":"setup","text":"You can use the [`mcp/redis`](https://hub.docker.com/r/mcp/redis)\nimage to run Redis MCP with [Docker](https://www.docker.com/).\nAlternatively, use the following\ncommand to build the Docker image with the `Dockerfile` in the\n`mcp/redis` folder:\n\n[code example]"},{"id":"configuration","title":"Configuration","role":"configuration","text":"The default settings for MCP assume a Redis server is running on the\nlocal machine, with the default port and no security arrangements.\nYou can use environment variables to change these settings\n(see [Environment variables](#environment-variables) below for the full list).\nFor example, for a `bash` shell, use\n\n[code example]\n\nfrom the command line or the `.bashrc` file to set the username you want\nto connect with.\n\nAlternatively, you can use a `.env` file in your project folder to set the\nenvironment variables as key-value pairs, one per line:\n\n[code example]\n\nSee the [`.env.example` file](https://github.com/redis/mcp-redis/blob/main/.env.example)\nin the repository for the full list of variables and their default values.\n\nYou can also set the configuration using command-line arguments, which\nmay be useful if you only want to change a few settings from the defaults\n(see [Command line options](#command-line-options) below for the full list\nof options).\n\n[code example]\n\n> [!NOTE]\n> The command-line options take precedence over the environment variables."},{"id":"environment-variables","title":"Environment variables","role":"content","text":"The full set of environment variables is shown in the table below. The\n`REDIS_ENTRAID_XXX` variables (used for EntraID authentication to\nAzure Managed Redis) are described in the\n[EntraID authentication](#entraid-authentication) section. \n\n| Name                 | Description                 | Default Value |\n|----------------------|-----------------------------|---------------|\n| `REDIS_HOST`         | Redis IP or hostname   | `\"127.0.0.1\"` |\n| `REDIS_PORT`         | Redis port         | `6379`        |\n| `REDIS_DB`           | Database | 0 |\n| `REDIS_USERNAME`     | Database username   | `\"default\"`   |\n| `REDIS_PWD`          | Database password       | \"\"            |\n| `REDIS_SSL`          | Enables or disables SSL/TLS    | `False`       |\n| `REDIS_CA_PATH`      | CA certificate for verifying server   | None  |\n| `REDIS_SSL_KEYFILE`  | Client's private key file for client authentication       | None          |\n| `REDIS_SSL_CERTFILE` | Client's certificate file for client authentication       | None          |\n| `REDIS_CERT_REQS`    | Whether the client should verify the server's certificate | `\"required\"`  |\n| `REDIS_CA_CERTS`     | Path to the trusted CA certificates file                  | None          |\n| `REDIS_CLUSTER_MODE` | Enable Redis Cluster mode                                 | `False`       |\n| `REDIS_ENTRAID_AUTH_FLOW`               | Authentication flow type                                  | None (EntraID disabled)              |\n| `REDIS_ENTRAID_CLIENT_ID`               | Service Principal client ID                               | None                                 |\n| `REDIS_ENTRAID_CLIENT_SECRET`           | Service Principal client secret                           | None                                 |\n| `REDIS_ENTRAID_TENANT_ID`               | Azure tenant ID                                           | None                                 |\n| `REDIS_ENTRAID_IDENTITY_TYPE`           | Managed identity type                                     | `\"system_assigned\"`                  |\n| `REDIS_ENTRAID_USER_ASSIGNED_CLIENT_ID` | User-assigned managed identity client ID                  | None                                 |\n| `REDIS_ENTRAID_SCOPES`                  | OAuth scopes for Default Azure Credential                | `\"https://redis.azure.com/.default\"` |\n| `REDIS_ENTRAID_RESOURCE`                | Azure Redis resource identifier                          | `\"https://redis.azure.com/\"`         |"},{"id":"command-line-options","title":"Command line options","role":"syntax","text":"The full set of command line options is shown in the table below:\n\n| Name                 | Description     | Default Value |\n|----------------------|-----------------|---------------|\n| `--url`              | Redis URL (`redis://user:pass@host:port/db`)   |  |\n| `--host`             | Redis IP or hostname   | `\"127.0.0.1\"` |\n| `--port`             | Redis port         | `6379`        |\n| `--db`               | Database | 0 |\n| `--username`         | Database username   | `\"default\"`   |\n| `--password`         | Database password       | |\n| `--ssl`              | Enables or disables SSL/TLS    | `False`       |\n| `--ssl-ca-path`          | CA certificate for verifying server   | None  |\n| `--ssl-keyfile`      | Client's private key file for client authentication   |    |\n| `--ssl-certfile`     | Client's certificate file for client authentication   |   |\n| `--ssl-cert-reqs`        | Whether the client should verify the server's certificate   | `\"required\"`  |\n| `--ssl-ca-certs`         | Path to the trusted CA certificates file   |   |\n| `--cluster-mode`     | Enable Redis Cluster mode    | `False`       |"},{"id":"entraid-authentication","title":"EntraID authentication","role":"content","text":"The Redis MCP Server supports EntraID (Azure Active Directory) authentication\nfor Azure Managed Redis, enabling OAuth-based authentication with automatic token management. This uses redis-py's [redis-entra-id](https://redis.io/docs/latest/develop/clients/redis-py/amr) library to offer:\n\n- **Automatic token renewal** - Refreshes tokens in the background with no manual intervention\n- **Graceful fallback** - Falls back to standard Redis authentication when EntraID is not configured\n- **Multiple auth flows** - Supports Service Principal, Managed Identity, and Default Azure Credential\n- **Enterprise capabilities** - Designed for Azure Managed Redis with centralized identity management\n\nUse the `REDIS_ENTRAID_XXX` environment variables to configure EntraID authentication.\nSome common configurations are:\n\n- **Local development with Azure CLI**:\n  [code example]\n- **Production usage with Service Principal**:\n\n  [code example]\n- **Azure-hosted application with Managed Identity**:\n\n  [code example]"},{"id":"redis-cloud-mcp","title":"Redis Cloud MCP","role":"content","text":"A separate MCP server is available for\n[Redis Cloud](https://redis.io/docs/latest/operate/rc). This has features\nspecific to Redis Cloud, including subscription management and\nbilling details. For example, you can use questions and instructions\nlike the following:\n\n-   \"Create a new Redis database in AWS\"\n-   \"What are my current subscriptions?\"\n-   \"Help me choose the right Redis database for my e-commerce application\"\n\n> [!NOTE]\n> By itself, the Redis Cloud MCP server only provides admin\n> features and does not let you interact\n> with Redis databases. You can still use the main MCP server for interaction\n> with a Cloud database, but you must install it separately.\n\nYou will need [Node.js](https://nodejs.org/en) installed to run Redis Cloud MCP.\nClone the GitHub repository using the following command:\n\n[code example]\n\nGo into the `mcp-redis-cloud` folder and install the dependencies:\n\n[code example]\n\nThe server is now ready for use.\n\nYou can also deploy Redis Cloud MCP using Docker. Build the image\nusing the `Dockerfile` in the repository folder with the following\ncommand:\n\n[code example]"},{"id":"next-steps","title":"Next steps","role":"content","text":"When you have installed the server, you will need a MCP client to\nconnect to it and use its services. See\n[Configure client apps](https://redis.io/docs/latest/integrate/redis-mcp/client-conf)\nfor more information."}],"examples":[{"id":"quick-start-with-pypi-ex0","language":"bash","code":"# Run with Redis URI\nuvx --from redis-mcp-server@latest redis-mcp-server --url redis://localhost:6379/0\n\n# Run with Redis URI and SSL \nuvx --from redis-mcp-server@latest redis-mcp-server --url \"rediss://<USERNAME>:<PASSWORD>@<HOST>:<PORT>?ssl_cert_reqs=required&ssl_ca_certs=<PATH_TO_CERT>\"\n\n# Run with individual parameters\nuvx --from redis-mcp-server@latest redis-mcp-server --host localhost --port 6379 --password mypassword\n\n# See all options\nuvx --from redis-mcp-server@latest redis-mcp-server --help","section_id":"quick-start-with-pypi"},{"id":"run-from-github-ex0","language":"bash","code":"# Run with Redis URI\nuvx --from git+https://github.com/redis/mcp-redis.git redis-mcp-server --url redis://localhost:6379/0\n\n# Run with Redis URI and SSL \nuvx --from git+https://github.com/redis/mcp-redis.git redis-mcp-server --url \"rediss://<USERNAME>:<PASSWORD>@<HOST>:<PORT>?ssl_cert_reqs=required&ssl_ca_certs=<PATH_TO_CERT>\"\n\n# Run with individual parameters\nuvx --from git+https://github.com/redis/mcp-redis.git redis-mcp-server --host localhost --port 6379 --password mypassword\n\n# See all options\nuvx --from git+https://github.com/redis/mcp-redis.git redis-mcp-server --help","section_id":"run-from-github"},{"id":"install-the-server-from-source-ex0","language":"bash","code":"git clone https://github.com/redis/mcp-redis.git","section_id":"install-the-server-from-source"},{"id":"install-the-server-from-source-ex1","language":"bash","code":"cd mcp-redis\n\nuv venv\nsource .venv/bin/activate\nuv sync\n\n# Run with CLI interface\nuv run redis-mcp-server --help\n\n# Or run the main file directly (uses environment variables)\nuv run src/main.py","section_id":"install-the-server-from-source"},{"id":"install-using-docker-ex0","language":"plaintext","code":"docker build -t mcp-redis .","section_id":"install-using-docker"},{"id":"configuration-ex0","language":"bash","code":"export REDIS_USERNAME=\"my_username\"","section_id":"configuration"},{"id":"configuration-ex1","language":"plaintext","code":"REDIS_HOST=your_redis_host\nREDIS_PORT=6379\n    .\n    .","section_id":"configuration"},{"id":"configuration-ex2","language":"bash","code":"# Basic Redis connection\nuvx --from redis-mcp-server@latest redis-mcp-server \\\n  --host localhost \\\n  --port 6379 \\\n  --password mypassword\n\n# Using Redis URI (simpler)\nuvx --from redis-mcp-server@latest redis-mcp-server \\\n  --url redis://user:pass@localhost:6379/0\n\n# SSL connection\nuvx --from redis-mcp-server@latest redis-mcp-server \\\n  --url rediss://user:pass@redis.example.com:6379/0\n\n# See all available options\nuvx --from redis-mcp-server@latest redis-mcp-server --help","section_id":"configuration"},{"id":"entraid-authentication-ex0","language":"bash","code":"# Login with Azure CLI\n  az login\n\n  # Configure MCP server\n  export REDIS_ENTRAID_AUTH_FLOW=default_credential\n  export REDIS_URL=redis://your-azure-redis.redis.cache.windows.net:6379","section_id":"entraid-authentication"},{"id":"entraid-authentication-ex1","language":"bash","code":"export REDIS_ENTRAID_AUTH_FLOW=service_principal\n  export REDIS_ENTRAID_CLIENT_ID=your-app-client-id\n  export REDIS_ENTRAID_CLIENT_SECRET=your-app-secret\n  export REDIS_ENTRAID_TENANT_ID=your-tenant-id\n  export REDIS_URL=redis://your-azure-redis.redis.cache.windows.net:6379","section_id":"entraid-authentication"},{"id":"entraid-authentication-ex2","language":"bash","code":"export REDIS_ENTRAID_AUTH_FLOW=managed_identity\n  export REDIS_ENTRAID_IDENTITY_TYPE=system_assigned\n  export REDIS_URL=redis://your-azure-redis.redis.cache.windows.net:6379","section_id":"entraid-authentication"},{"id":"redis-cloud-mcp-ex0","language":"bash","code":"git clone https://github.com/redis/mcp-redis-cloud.git","section_id":"redis-cloud-mcp"},{"id":"redis-cloud-mcp-ex1","language":"bash","code":"npm run build","section_id":"redis-cloud-mcp"},{"id":"redis-cloud-mcp-ex2","language":"bash","code":"docker build -t mcp/redis-cloud .","section_id":"redis-cloud-mcp"}]}
{"schema_version":2,"id":"integrate/redis-py","title":"Python client for Redis","url":"https://redis.io/docs/latest/integrate/redis-py/","summary":"Learn how to build with Redis and Python","tags":["docs","integrate","oss","rs","rc"],"last_updated":"2026-09-17T14:54:53+01:00","children":[],"page_type":"content","content_hash":"4319a5323bafa7237aa996d593198d30fbdf5f032baa2563bd87db7637f3dcf1","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"Connect your Python application to a Redis database using the redis-py client library."},{"id":"overview","title":"Overview","role":"overview","text":"redis-py is the recommended Python client for Redis, providing a comprehensive interface for all Redis commands and features. It offers both synchronous and asynchronous APIs, making it suitable for a wide range of Python applications from simple scripts to high-performance web applications."},{"id":"key-features","title":"Key Features","role":"content","text":"- **Complete Redis Support**: Full coverage of all Redis commands and data types\n- **Async/Await Support**: Native asyncio support for high-performance applications\n- **Connection Pooling**: Efficient connection management for multi-threaded applications\n- **Redis Cluster**: Built-in support for Redis Cluster deployments\n- **Sentinel Support**: High availability with Redis Sentinel integration\n- **Pipeline Support**: Command batching for improved performance\n- **Pub/Sub**: Real-time messaging with Redis publish/subscribe\n- **Framework Integration**: Seamless integration with Django, Flask, [FastAPI](https://redis.io/docs/latest/integrate/fastapi), and other Python frameworks"},{"id":"getting-started","title":"Getting Started","role":"setup","text":"Refer to the complete [Python guide](https://redis.io/docs/latest/develop/clients/redis-py) to install, connect, and use redis-py."}],"examples":[]}
{"schema_version":2,"id":"integrate/redis-rb","title":"Ruby client for Redis","url":"https://redis.io/docs/latest/integrate/redis-rb/","summary":"Learn how to build with Redis and Ruby","tags":["docs","integrate","oss","rs","rc"],"last_updated":"2026-09-17T14:54:53+01:00","children":[],"page_type":"content","content_hash":"a463a774b9bd5e82850dd4f31e0ebb36ec0a5a373e237583eb1eb66e12717ca9","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"Connect your Ruby application to a Redis database using the redis-rb client library."},{"id":"overview","title":"Overview","role":"overview","text":"redis-rb is the recommended Ruby client for Redis, providing a simple and intuitive interface for interacting with Redis from Ruby applications. It supports all Redis commands and features, including connection pooling, pipelining, and Pub/Sub."},{"id":"key-features","title":"Key Features","role":"content","text":"- **Full Redis Support**: Complete coverage of Redis commands and data types\n- **Connection Pooling**: Efficient connection management for concurrent applications\n- **Pipelining**: Batch commands for improved performance\n- **Pub/Sub**: Real-time messaging with Redis publish/subscribe\n- **Sentinel Support**: High availability with Redis Sentinel integration\n- **Redis Cluster**: Built-in support for Redis Cluster deployments"},{"id":"getting-started","title":"Getting Started","role":"setup","text":"Refer to the complete [Ruby guide](https://redis.io/docs/latest/develop/clients/ruby) to install, connect, and use redis-rb with detailed examples."}],"examples":[]}
{"schema_version":2,"id":"integrate/redisinsight","title":"Redis Insight","url":"https://redis.io/docs/latest/integrate/redisinsight/","summary":"Visual client tool for creating, managing, and analyzing Redis databases","tags":["docs","integrate","oss","rs","rc"],"last_updated":"2026-09-28T17:31:37-07:00","children":[],"page_type":"content","content_hash":"57b4dc494b7b65b675bc6a1455b5aff9433cc5ed8d75afcac95469ebb3620f0f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"<div class=\"max-w-xs\">\n  \n</div>\n\nRedis Insight is a powerful tool for visualizing and optimizing data in Redis, making real-time application development easier and more fun than ever before. Redis Insight lets you do both GUI- and CLI-based interactions in a fully-featured desktop GUI client."},{"id":"key-features","title":"Key Features","role":"content","text":"- **Visual data browser**: Browse, filter and visualize your key-value Redis data structures\n- **Real-time profiler**: Analyze every command sent to Redis in real time\n- **Advanced CLI**: Intelligent command auto-complete and complex data visualization support\n- **Database analysis**: Optimize performance and memory usage of your Redis database\n- **Redis Copilot**: AI-powered developer assistant for learning and data exploration"},{"id":"installation","title":"Installation","role":"setup","text":"Redis Insight is available for multiple platforms:\n\n- [Install on Desktop](https://redis.io/docs/latest/operate/redisinsight/install/install-on-desktop) - Windows, macOS, Linux\n- [Install on Docker](https://redis.io/docs/latest/operate/redisinsight/install/install-on-docker) - Container deployment\n- [Install on Kubernetes](https://redis.io/docs/latest/operate/redisinsight/install/install-on-k8s) - K8s clusters\n- [Install on AWS](https://redis.io/docs/latest/operate/redisinsight/install/install-on-aws) - Amazon Web Services"},{"id":"getting-started","title":"Getting Started","role":"setup","text":"Refer to the complete [Redis Insight guide](https://redis.io/docs/latest/develop/tools/insight) to install, connect, and use Redis Insight for data visualization and database management."}],"examples":[]}
{"schema_version":2,"id":"integrate/redisom-for-java","title":"RedisOM for Java","url":"https://redis.io/docs/latest/integrate/redisom-for-java/","summary":"Learn how to build with Redis Stack and Spring","aliases":["/connect/clients/om-clients/stack-spring/","/develop/connect/clients/om-clients/stack-spring/"],"tags":["docs","integrate","oss","rs","rc"],"last_updated":"2026-09-17T14:54:53+01:00","children":[],"page_type":"content","content_hash":"2643ae0fc5bc7923fa0eb8dd0e24f6f86200e7de3e2217c5aeccd224906cdd73","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Stack provides a seamless and straightforward way to use different data models and functionality from Redis, including a document store, a time series data database, probabilistic data structures, and a full-text search engine.\n\nRedis Stack is supported by several client libraries, including Node.js, Java, and Python, so that developers can use their preferred language. We'll be using one of the Redis Stack supporting libraries; [Redis OM Spring](https://github.com/redis/redis-om-spring).\nRedis OM Spring provides a robust repository and custom object-mapping abstractions built on the powerful Spring Data Redis (SDR) framework."},{"id":"what-youll-need","title":"What you’ll need:","role":"content","text":"* Redis Stack: See [Install Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack)\n* [Redis Insight](https://redis.io/docs/latest/develop/tools/insight)\n* Your favorite browser\n* Java 11 or greater"},{"id":"spring-boot-scaffold-with-spring-initializer","title":"Spring Boot scaffold with Spring Initializer","role":"content","text":"We’ll start by creating a skeleton app using the [Spring Initializer](https://start.spring.io), open your browser to https://start.spring.io and let's configure our skeleton application as follows:\n\n* We’ll use a Maven-based build (check Maven checkbox)\n* And version **`2.6.4`** of Spring Boot which is the current version supported by Redis OM Spring\n* Group: **`com.redis.om`**\n* Artifact: **`skeleton`**\n* Name: **`skeleton`**\n* Description: Skeleton App for Redis OM Spring\n* Package Name: **`com.redis.om.skeleton`**\n* Packaging: JAR\n* Java: **`11`**\n* Dependencies: **`web`**, **`devtools`** and **`lombok`**.\n\nThe `web` (Spring Web) gives us the ability to build RESTful applications using Spring MVC. With `devtools` we get fast application restarts and reloads. And `lombok` reduces boilerplate code like getters and setters.\n\n![Spring Initializer](./images/001_stack_spring.png \"Spring Initializer\")\n\nClick `Generate` and download the ZIP file, unzip it and load the Maven project into your IDE of choice."},{"id":"adding-redis-om-spring","title":"Adding Redis OM Spring","role":"content","text":"Open the Maven `pom.xml` and between the `<dependencies>` and `<build>` sections we’ll add the snapshots repositories so that we can get to latest SNAPSHOT release of redis-om-spring:\n\n\n<repositories>\n  <repository>\n    <id>snapshots-repo</id>\n    <url>https://s01.oss.sonatype.org/content/repositories/snapshots/</url>\n  </repository>\n</repositories>\n\n\nAnd then in the `<dependencies>` section add version `0.3.0` of Redis OM Spring:\n\n\n<dependency>\n  <groupId>com.redis.om</groupId>\n  <artifactId>redis-om-spring</artifactId>\n  <version>0.3.0-SNAPSHOT</version>\n</dependency>"},{"id":"adding-swagger","title":"Adding Swagger","role":"content","text":"We'll use the Swagger UI to test our web services endpoint. To add Swagger 2 to a Spring REST web service, using the Springfox implementation add the following dependencies to the POM:\n\n\n<dependency>\n  <groupId>io.springfox</groupId>\n  <artifactId>springfox-boot-starter</artifactId>\n  <version>3.0.0</version>\n</dependency>\n<dependency>\n  <groupId>io.springfox</groupId>\n  <artifactId>springfox-swagger-ui</artifactId>\n  <version>3.0.0</version>\n</dependency>\n\n\nLet's add Swagger Docker Bean to the Spring App class:\n\n\n@Bean\npublic Docket api() {\n  return new Docket(DocumentationType.SWAGGER_2)\n      .select()\n      .apis(RequestHandlerSelectors.any())\n      .paths(PathSelectors.any())\n      .build();\n}\n\n\nWhich will pick up any HTTP endpoints exposed by our application. Add to your app's property file (src/main/resources/application.properties):\n\n\nspring.mvc.pathmatch.matching-strategy=ANT_PATH_MATCHER\n\n\nAnd finally, to enable Swagger on the application, we need to use the `EnableSwagger2` annotation, by\nannotating the main application class:\n\n\n@EnableSwagger2\n@SpringBootApplication\npublic class SkeletonApplication {\n  // ...\n}"},{"id":"creating-the-domain","title":"Creating the Domain","role":"content","text":"Our domain will be fairly simple; `Person`s that have `Address`es. Let's start with the `Person` entity:\n\n\npackage com.redis.om.skeleton.models;\n\nimport java.util.Set;\n\nimport org.springframework.data.annotation.Id;\nimport org.springframework.data.geo.Point;\n\nimport com.redis.om.spring.annotations.Document;\nimport com.redis.om.spring.annotations.Indexed;\nimport com.redis.om.spring.annotations.Searchable;\n\nimport lombok.AccessLevel;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NonNull;\nimport lombok.RequiredArgsConstructor;\n\n@RequiredArgsConstructor(staticName = \"of\")\n@AllArgsConstructor(access = AccessLevel.PROTECTED)\n@Data\n@Document\npublic class Person {\n  // Id Field, also indexed\n  @Id\n  @Indexed\n  private String id;\n\n  // Indexed for exact text matching\n  @Indexed @NonNull\n  private String firstName;\n\n  @Indexed @NonNull\n  private String lastName;\n\n  //Indexed for numeric matches\n  @Indexed @NonNull\n  private Integer age;\n\n  //Indexed for Full Text matches\n  @Searchable @NonNull\n  private String personalStatement;\n\n  //Indexed for Geo Filtering\n  @Indexed @NonNull\n  private Point homeLoc;\n\n  // Nest indexed object\n  @Indexed @NonNull\n  private Address address;\n\n  @Indexed @NonNull\n  private Set<String> skills;\n}\n\n\nThe `Person` class has the following properties:\n\n* `id`: An autogenerated `String` using [ULIDs](https://github.com/ulid/spec)\n* `firstName`: A `String` representing their first or given name.\n* `lastName`: A `String` representing their last or surname.\n* `age`: An `Integer` representing their age in years.\n* `personalStatement`: A `String` representing a personal text statement containing facts or other biographical information.\n* `homeLoc`: A `org.springframework.data.geo.Point` representing the geo coordinates.\n* `address`: An entity of type `Address` representing the Person's postal address.\n* `skills`: A `Set<String>` representing a collection of Strings representing skills the Person possesses."},{"id":"document","title":"@Document","role":"content","text":"The `Person` class (`com.redis.om.skeleton.models.Person`) is annotated with `@Document` (`com.redis.om.spring.annotations.Document`), which is marks the object as a Redis entity to be persisted as a JSON document by the appropriate type of repository."},{"id":"indexed-and-searchable","title":"@Indexed and @Searchable","role":"content","text":"The fields `id`, `firstName`, `lastName`, `age`, `homeLoc`, `address`, and `skills` are all annotated\nwith `@Indexed` (`com.redis.om.spring.annotations.Indexed`). On entities annotated with `@Document` Redis OM Spring will scan the fields and add an appropriate search index field to the schema for the entity. For example, for the `Person` class\nan index named `com.redis.om.skeleton.models.PersonIdx` will be created on application startup. In the index schema, a search field will be added for each `@Indexed` annotated property. RediSearch, the underlying search engine powering searches, supports Text (full-text searches), Tag (exact-match searches), Numeric (range queries), Geo (geographic range queries), and Vector (vector queries) fields. For `@Indexed` fields, the appropriate search field (Tag, Numeric, or Geo) is selected based on the property's data type.\n\nFields marked as `@Searchable` (`com.redis.om.spring.annotations.Searchable`) such as `personalStatement` in `Person` are reflected as Full-Text search fields in the search index schema."},{"id":"nested-field-search-features","title":"Nested Field Search Features","role":"content","text":"The embedded class `Address` (`com.redis.om.skeleton.models.Address`) has several properties annotated with `@Indexed`  and `@Searchable`, which will generate search index fields in Redis. The scanning of these fields is triggered by the `@Indexed` annotation on the `address` property in the `Person` class:\n\n\npackage com.redis.om.skeleton.models;\n\nimport com.redis.om.spring.annotations.Indexed;\nimport com.redis.om.spring.annotations.Searchable;\n\nimport lombok.Data;\nimport lombok.NonNull;\nimport lombok.RequiredArgsConstructor;\n\n@Data\n@RequiredArgsConstructor(staticName = \"of\")\npublic class Address {\n\n  @NonNull\n  @Indexed\n  private String houseNumber;\n\n  @NonNull\n  @Searchable(nostem = true)\n  private String street;\n\n  @NonNull\n  @Indexed\n  private String city;\n\n  @NonNull\n  @Indexed\n  private String state;\n\n  @NonNull\n  @Indexed\n  private String postalCode;\n\n  @NonNull\n  @Indexed\n  private String country;\n}"},{"id":"spring-data-repositories","title":"Spring Data Repositories","role":"content","text":"With the model in place now, we need to create the bridge between the models and the Redis, a Spring Data Repository. Like other Spring Data Repositories, Redis OM Spring data repository's goal is to reduce the boilerplate code required to implement data access significantly. Create a Java interface like:\n\n\npackage com.redis.om.skeleton.models.repositories;\n\nimport com.redis.om.skeleton.models.Person;\nimport com.redis.om.spring.repository.RedisDocumentRepository;\n\npublic interface PeopleRepository extends RedisDocumentRepository<Person,String> {\n\n}\n\n\nThat's really all we need to get all the CRUD and Paging/Sorting functionality. The\n`RedisDocumentRepository` (`com.redis.om.spring.repository.RedisDocumentRepository`) extends `PagingAndSortingRepository` (`org.springframework.data.repository.PagingAndSortingRepository`) which extends CrudRepository to provide additional methods to retrieve entities using the pagination and sorting."},{"id":"enableredisdocumentrepositories","title":"@EnableRedisDocumentRepositories","role":"content","text":"Before we can fire up the application, we need to enable our Redis Document repositories. Like most\nSpring Data projects, Redis OM Spring provides an annotation to do so; the `@EnableRedisDocumentRepositories`. We annotate the main application class:\n\n\n@EnableRedisDocumentRepositories(basePackages = \"com.redis.om.skeleton.*\")\n@EnableSwagger2\n@SpringBootApplication\npublic class SkeletonApplication {"},{"id":"crud-with-repositories","title":"CRUD with Repositories","role":"content","text":"With the repositories enabled, we can use our repo; let's put in some data to see the object mapping in action. Let’s create `CommandLineRunner` that will execute on application startup:\n\n\npublic class SkeletonApplication {\n\n @Bean\n CommandLineRunner loadTestData(PeopleRepository repo) {\n   return args -> {\n     repo.deleteAll();\n\n     String thorSays = \"The Rabbit Is Correct, And Clearly The Smartest One Among You.\";\n\n     // Serendipity, 248 Seven Mile Beach Rd, Broken Head NSW 2481, Australia\n     Address thorsAddress = Address.of(\"248\", \"Seven Mile Beach Rd\", \"Broken Head\", \"NSW\", \"2481\", \"Australia\");\n\n     Person thor = Person.of(\"Chris\", \"Hemsworth\", 38, thorSays, new Point(153.616667, -28.716667), thorsAddress, Set.of(\"hammer\", \"biceps\", \"hair\", \"heart\"));\n\n     repo.save(thor);\n   };\n }\n\n\nIn the `loadTestData` method, we will take an instance of the `PeopleRepository` (thank you, Spring, for Dependency Injection!). Inside the returned lambda, we will first call the repo’s `deleteAll` method, which will ensure that we have clean data on each application reload.\n\nWe create a `Person` object using the Lombok generated builder method and then save it using the repo’s `save` method."},{"id":"keeping-tabs-with-redis-insight","title":"Keeping tabs with Redis Insight","role":"content","text":"Let’s launch Redis Insight and connect to the localhost at port 6379. With a clean Redis Stack install, we can use the built-in CLI to check the keys in the system:\n\n![Redis Insight](./images/002_stack_spring.png \"Redis Insight\")\n\nFor a small amount of data, you can use the `keys` command (for any significant amount of data, use `scan`):\n\n\nkeys *\n\n\nIf you want to keep an eye on the commands issued against the server, Redis Insight provides a\nprofiler. If you click the \"profile\" button at the bottom of the screen, it should reveal the profiler window, and there you can start the profiler by clicking on the \"Start Profiler\" arrow.\n\nLet's start our Spring Boot application by using the Maven command:\n\n\n./mvnw spring-boot:run\n\n\nOn Redis Insight, if the application starts correctly, you should see a barrage of commands fly by on the profiler:\n\n![Redis Insight](./images/003_stack_spring.png \"Redis Insight\")\n\nNow we can inspect the newly loaded data by simply refreshing the \"Keys\" view:\n\n![Redis Insight](./images/004_stack_spring.png \"Redis Insight\")\n\nYou should now see two keys; one for the JSON document for \"Thor\" and one for the Redis Set that Spring Data Redis (and Redis OM Spring) use to maintain the list of primary keys for an entity.\n\nYou can select any of the keys on the key list to reveal their contents on the details panel. For JSON documents, we get a nice tree-view:\n\n![Redis Insight](./images/005_stack_spring.png \"Redis Insight\")\n\nSeveral Redis commands were executed on application startup. Let’s break them down so that we can understand what's transpired."},{"id":"index-creation","title":"Index Creation","role":"content","text":"The first one is a call to [`FT.CREATE`](https://redis.io/docs/latest/commands/ft.create), which happens after Redis OM Spring scanned the `@Document` annotations. As you can see, since it encountered the annotation on `Person`, it creates the  `PersonIdx` index.\n\n\n\"FT.CREATE\"\n  \"com.redis.om.skeleton.models.PersonIdx\" \"ON\" \"JSON\"\n  \"PREFIX\" \"1\" \"com.redis.om.skeleton.models.Person:\"\n\"SCHEMA\"\n  \"$.id\" \"AS\" \"id\" \"TAG\"\n  \"$.firstName\" \"AS\" \"firstName\" \"TAG\"\n  \"$.lastName\" \"AS\" \"lastName\" \"TAG\"\n  \"$.age\" \"AS\" \"age\" \"NUMERIC\"\n  \"$.personalStatement\" \"AS\" \"personalStatement\" \"TEXT\"\n  \"$.homeLoc\" \"AS\" \"homeLoc\" \"GEO\"\n  \"$.address.houseNumber\" \"AS\" \"address_houseNumber\" \"TAG\"\n  \"$.address.street\" \"AS\" \"address_street\" \"TEXT\" \"NOSTEM\"\n  \"$.address.city\" \"AS\" \"address_city\" \"TAG\"\n  \"$.address.state\" \"AS\" \"address_state\" \"TAG\"\n  \"$.address.postalCode\" \"AS\" \"address_postalCode\" \"TAG\"\n  \"$.address.country\" \"AS\" \"address_country\" \"TAG\"\n  \"$.skills[*]\" \"AS\" \"skills\""},{"id":"cleaning-the-person-repository","title":"Cleaning the Person Repository","role":"content","text":"The next set of commands are generated by the call to `repo.deleteAll()`:\n\n\n\"DEL\" \"com.redis.om.skeleton.models.Person\"\n\"KEYS\" \"com.redis.om.skeleton.models.Person:*\"\n\n\nThe first call clears the set of Primary Keys that Spring Data Redis maintains (and therefore Redis OM Spring), the second call collects all the keys to delete them, but there are none to delete on this first load of the data."},{"id":"saving-person-entities","title":"Saving Person Entities","role":"content","text":"The next repo call is `repo.save(thor)` that triggers the following sequence:\n\n\n\"SISMEMBER\" \"com.redis.om.skeleton.models.Person\" \"01FYANFH68J6WKX2PBPX21RD9H\"\n\"EXISTS\" \"com.redis.om.skeleton.models.Person:01FYANFH68J6WKX2PBPX21RD9H\"\n\"JSON.SET\" \"com.redis.om.skeleton.models.Person:01FYANFH68J6WKX2PBPX21RD9H\" \".\" \"{\"id\":\"01FYANFH68J6WKX2PBPX21RD9H\",\"firstName\":\"Chris\",\"lastName\":\"Hemsworth\",\"age\":38,\"personalStatement\":\"The Rabbit Is Correct, And Clearly The Smartest One Among You.\",\"homeLoc\":\"153.616667,-28.716667\",\"address\":{\"houseNumber\":\"248\",\"street\":\"Seven Mile Beach Rd\",\"city\":\"Broken Head\",\"state\":\"NSW\",\"postalCode\":\"2481\",\"country\":\"Australia\"},\"skills\":[\"biceps\",\"hair\",\"heart\",\"hammer\"]}\n\"SADD\" \"com.redis.om.skeleton.models.Person\" \"01FYANFH68J6WKX2PBPX21RD9H\"\n\n\nLet's break it down:\n\n* The first call uses the generated ULID to check if the id is in the set of primary keys (if it is, it’ll be removed)\n* The second call checks if JSON document exists (if it is, it’ll be removed)\n* The third call uses the [`JSON.SET`](https://redis.io/docs/latest/commands/json.set) command to save the JSON payload\n* The last call adds the primary key of the saved document to the set of primary keys\n\nNow that we’ve seen the repository in action via the `.save` method, we know that the trip from Java to Redis work. Now let’s add some more data to make the interactions more interesting:\n\n\n@Bean\nCommandLineRunner loadTestData(PeopleRepository repo) {\n  return args -> {\n    repo.deleteAll();\n\n    String thorSays = \"The Rabbit Is Correct, And Clearly The Smartest One Among You.\";\n    String ironmanSays = \"Doth mother know you weareth her drapes?\";\n    String blackWidowSays = \"Hey, fellas. Either one of you know where the Smithsonian is? I’m here to pick up a fossil.\";\n    String wandaMaximoffSays = \"You Guys Know I Can Move Things With My Mind, Right?\";\n    String gamoraSays = \"I Am Going To Die Surrounded By The Biggest Idiots In The Galaxy.\";\n    String nickFurySays = \"Sir, I’m Gonna Have To Ask You To Exit The Donut\";\n\n    // Serendipity, 248 Seven Mile Beach Rd, Broken Head NSW 2481, Australia\n    Address thorsAddress = Address.of(\"248\", \"Seven Mile Beach Rd\", \"Broken Head\", \"NSW\", \"2481\", \"Australia\");\n\n    // 11 Commerce Dr, Riverhead, NY 11901\n    Address ironmansAddress = Address.of(\"11\", \"Commerce Dr\", \"Riverhead\", \"NY\",  \"11901\", \"US\");\n\n    // 605 W 48th St, New York, NY 10019\n    Address blackWidowAddress = Address.of(\"605\", \"48th St\", \"New York\", \"NY\", \"10019\", \"US\");\n\n    // 20 W 34th St, New York, NY 10001\n    Address wandaMaximoffsAddress = Address.of(\"20\", \"W 34th St\", \"New York\", \"NY\", \"10001\", \"US\");\n\n    // 107 S Beverly Glen Blvd, Los Angeles, CA 90024\n    Address gamorasAddress = Address.of(\"107\", \"S Beverly Glen Blvd\", \"Los Angeles\", \"CA\", \"90024\", \"US\");\n\n    // 11461 Sunset Blvd, Los Angeles, CA 90049\n    Address nickFuryAddress = Address.of(\"11461\", \"Sunset Blvd\", \"Los Angeles\", \"CA\", \"90049\", \"US\");\n\n    Person thor = Person.of(\"Chris\", \"Hemsworth\", 38, thorSays, new Point(153.616667, -28.716667), thorsAddress, Set.of(\"hammer\", \"biceps\", \"hair\", \"heart\"));\n    Person ironman = Person.of(\"Robert\", \"Downey\", 56, ironmanSays, new Point(40.9190747, -72.5371874), ironmansAddress, Set.of(\"tech\", \"money\", \"one-liners\", \"intelligence\", \"resources\"));\n    Person blackWidow = Person.of(\"Scarlett\", \"Johansson\", 37, blackWidowSays, new Point(40.7215259, -74.0129994), blackWidowAddress, Set.of(\"deception\", \"martial_arts\"));\n    Person wandaMaximoff = Person.of(\"Elizabeth\", \"Olsen\", 32, wandaMaximoffSays, new Point(40.6976701, -74.2598641), wandaMaximoffsAddress, Set.of(\"magic\", \"loyalty\"));\n    Person gamora = Person.of(\"Zoe\", \"Saldana\", 43, gamoraSays, new Point(-118.399968, 34.073087), gamorasAddress, Set.of(\"skills\", \"martial_arts\"));\n    Person nickFury = Person.of(\"Samuel L.\", \"Jackson\", 73, nickFurySays, new Point(-118.4345534, 34.082615), nickFuryAddress, Set.of(\"planning\", \"deception\", \"resources\"));\n\n    repo.saveAll(List.of(thor, ironman, blackWidow, wandaMaximoff, gamora, nickFury));\n  };\n}\n\n\nWe have 6 People in the database now; since we’re using the devtools in Spring, the app should have reloaded, and the database reseeded with new data. Press enter the key pattern input box in Redis Insight to refresh the view. Notice that we used the repository’s `saveAll` to save several objects in bulk.\n\n![Redis Insight](./images/006_stack_spring.png \"Redis Insight\")"},{"id":"web-service-endpoints","title":"Web Service Endpoints","role":"content","text":"Before we beef up the repository with more interesting queries, let’s create a controller so that we can test our queries using the Swagger UI:\n\n\npackage com.redis.om.skeleton.controllers;\n\nimport com.redis.om.skeleton.models.Person;\nimport com.redis.om.skeleton.models.repositories.PeopleRepository;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/api/v1/people\")\npublic class PeopleControllerV1 {\n @Autowired\n PeopleRepository repo;\n\n @GetMapping(\"all\")\n Iterable<Person> all() {\n   return repo.findAll();\n }\n}\n\n\nIn this controller, we inject a repository and use one of the CRUD methods, `findAll()`, to return all the `Person` documents in the database.\n\nIf we navigate to http://localhost:8080/swagger-ui/ you should see the Swagger UI:\n\n![SwaggerUI](./images/007_stack_spring.png \"SwaggerUI\")\n\nWe can see the `/all` method from our people-controller-v-1, expanding that you should see:\n\n![SwaggerUI](./images/008_stack_spring.png \"SwaggerUI\")\n\nAnd if you select \"Try it out\" and then \"Execute,\" you should see the resulting JSON array containing all People documents in the database:\n\n![SwaggerUI](./images/009_stack_spring.png \"SwaggerUI\")\n\nLet’s also add the ability to retrieve a Person by its id by using the repo’s findById method:\n\n\n@GetMapping(\"{id}\")\nOptional<Person> byId(@PathVariable String id) {\n  return repo.findById(id);\n}\n\n\nRefreshing the Swagger UI, we should see the newly added endpoint. We can grab an id using the [`SRANDMEMBER`](https://redis.io/docs/latest/commands/srandmember) command on the Redis Insight CLI like this:\n\n\nSRANDMEMBER com.redis.om.skeleton.models.Person\n\n\nPlugging the resulting ID in the Swagger UI, we can get the corresponding  JSON document:\n\n![SwaggerUI](./images/010_stack_spring.png \"SwaggerUI\")"},{"id":"custom-repository-finders","title":"Custom Repository Finders","role":"content","text":"Now that we tested quite a bit of the CRUD functionality, let's add some custom finders to our repository. We’ll start with a finder over a numeric range, on the `age` property of `Person`:\n\n\npublic interface PeopleRepository extends RedisDocumentRepository<Person,String> {\n // Find people by age range\n Iterable<Person> findByAgeBetween(int minAge, int maxAge);\n}\n\n\nAt runtime, the repository method `findByAgeBetween` is fulfilled by the framework, so all you need to do is declare it, and Redis OM Spring will handle the querying and mapping of the results. The property or properties to be used are picked after the key phrase \"findBy\". The \"Between\" keyword is the predicate that tells the query builder what operation to use.\n\nTo test it on the Swagger UI, let’s add a corresponding method to the controller:\n\n\n@GetMapping(\"age_between\")\nIterable<Person> byAgeBetween( //\n    @RequestParam(\"min\") int min, //\n    @RequestParam(\"max\") int max) {\n  return repo.findByAgeBetween(min, max);\n}\n\n\nRefreshing the UI, we can see the new endpoint. Let’s try it with some data:\n\n![SwaggerUI](./images/011_stack_spring.png \"SwaggerUI\")\n\nInvoke the endpoint with the value `30` for `min` and `37` for `max` we get two hits;\n\"Scarlett Johansson\" and \"Elizabeth Olsen\" are the only two people with ages between 30 and 37.\n\n![SwaggerUI](./images/012_stack_spring.png \"SwaggerUI\")\n\nIf we look at the Redis Insight Profiler, we can see the resulting query, which is a range query on the index numeric field `age`:\n\n![Redis Insight](./images/013_stack_spring.png \"Redis Insight Profiler\")\n\nWe can also create query methods with more than one property. For example, if we wanted to do a query by first and last names, we would declare a repository method like:\n\n\n// Find people by their first and last name\nIterable<Person> findByFirstNameAndLastName(String firstName, String lastName);\n\n\nLet’s add a corresponding controller method:\n\n\n@GetMapping(\"name\")\nIterable<Person> byFirstNameAndLastName(@RequestParam(\"first\") String firstName, //\n    @RequestParam(\"last\") String lastName) {\n  return repo.findByFirstNameAndLastName(firstName, lastName);\n}\n\n\nOnce again, we can refresh the swagger UI and test the newly created endpoint:\n\n![SwaggerUI](./images/014_stack_spring.png \"SwaggerUI\")\n\nExecuting the request with the first name `Robert` and last name `Downey`, we get:\n\n![SwaggerUI](./images/015_stack_spring.png \"SwaggerUI\")\n\nAnd the resulting query on Redis Insight:\n\n![Redis Insight](./images/016_stack_spring.png \"Redis Insight Profiler\")\n\nNow let’s try a Geospatial query. The `homeLoc` property is a Geo Point, and by using the \"Near\" predicate in our method declaration, we can get a finder that takes a point and a radius around that point to search:\n\n\n// Draws a circular geofilter around a spot and returns all people in that\n// radius\nIterable<Person> findByHomeLocNear(Point point, Distance distance);\nAnd the corresponding controller method:\n\n@GetMapping(\"homeloc\")\nIterable<Person> byHomeLoc(//\n    @RequestParam(\"lat\") double lat, //\n    @RequestParam(\"lon\") double lon, //\n    @RequestParam(\"d\") double distance) {\n  return repo.findByHomeLocNear(new Point(lon, lat), new Distance(distance, Metrics.MILES));\n}\n\n\nRefreshing the Swagger US, we should now see the `byHomeLoc` endpoint. Let’s see which of the Avengers live within 10 miles of Suffolk Park Pub in South Wales, Australia... hmmm.\n\n![SwaggerUI](./images/017_stack_spring.png \"SwaggerUI\")\n\nExecuting the request, we get the record for Chris Hemsworth:\n\n![SwaggerUI](./images/018_stack_spring.png \"SwaggerUI\")\n\nand in Redis Insight we can see the backing query:\n\n![Redis Insight](./images/019_stack_spring.png \"Redis Insight Profiler\")\n\nLet’s try a full-text search query against the `personalStatement` property. To do so, we prefix our query method with the word `search` as shown below:\n\n\n// Performs full-text search on a person’s personal Statement\nIterable<Person> searchByPersonalStatement(String text);\n\n\nAnd the corresponding controller method:\n\n\n@GetMapping(\"statement\")\nIterable<Person> byPersonalStatement(@RequestParam(\"q\") String q) {\n  return repo.searchByPersonalStatement(q);\n}\n\n\nOnce again, we can try it on the Swagger UI with the text \"mother\":\n\n![SwaggerUI](./images/020_stack_spring.png \"SwaggerUI\")\n\nWhich results in a single hit, the record for Robert Downey Jr.:\n\n![SwaggerUI](./images/021_stack_spring.png \"SwaggerUI\")\n\nNotice that you can pass a query string like \"moth*\" with wildcards if needed\n\n![SwaggerUI](./images/022_stack_spring.png \"SwaggerUI\")"},{"id":"nested-object-searches","title":"Nested object searches","role":"content","text":"You’ve noticed that the `address` object in `Person` is mapped as a JSON object. If we want to search by address fields, we use an underscore to access the nested fields. For example, if we wanted to find a Person by their city, the method signature would be:\n\n\n// Performing a tag search on city\nIterable<Person> findByAddress_City(String city);\n\n\nLet’s add the matching controller method so that we can test it:\n\n\n@GetMapping(\"city\")\nIterable<Person> byCity(@RequestParam(\"city\") String city) {\n  return repo.findByAddress_City(city);\n}\n\n\nLet’s test the byCity endpoint:\n\n![SwaggerUI](./images/023_stack_spring.png \"SwaggerUI\")\n\nAs expected, we should get two hits; Scarlett Johansson and Elizabeth Olsen, both with addresses in Nee York:\n\n![SwaggerUI](./images/024_stack_spring.png \"SwaggerUI\")\n\nThe skills set is indexed as tag search. To find a Person with any of the skills in a provided list, we can add a repository method like:\n\n\n// Search Persons that have one of multiple skills (OR condition)\nIterable<Person> findBySkills(Set<String> skills);\n\n\nAnd the corresponding controller method:\n\n\n@GetMapping(\"skills\")\nIterable<Person> byAnySkills(@RequestParam(\"skills\") Set<String> skills) {\n  return repo.findBySkills(skills);\n}\n\n\nLet's test the endpoint with the value \"deception\":\n\n![SwaggerUI](./images/025_stack_spring.png \"SwaggerUI\")\n\nThe search returns the records for Scarlett Johansson and Samuel L. Jackson:\n\n![SwaggerUI](./images/026_stack_spring.png \"SwaggerUI\")\n\nWe can see the backing query using a tag search:\n\n![Redis Insight](./images/027_stack_spring.png \"Redis Insight Profiler\")"},{"id":"fluid-searching-with-entity-streams","title":"Fluid Searching with Entity Streams","role":"content","text":"Redis OM Spring Entity Streams provides a Java 8 Streams interface to Query Redis JSON documents using Redis Stack. Entity Streams allow you to process data in a typesafe declarative way similar to SQL statements. Streams can be used to express a query as a chain of operations.\n\nEntity Streams in Redis OM Spring provide the same semantics as Java 8 streams. Streams can be made of Redis Mapped entities (`@Document`) or one or more properties of an Entity. Entity Streams progressively build the query until a terminal operation is invoked (such as `collect`). Whenever a Terminal operation is applied to a Stream, the Stream cannot accept additional operations to its pipeline, which means that the Stream is started.\n\nLet’s start with a simple example, a Spring `@Service` which includes `EntityStream` to query for instances of the mapped class `Person`:\n\n\npackage com.redis.om.skeleton.services;\n\nimport java.util.stream.Collectors;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport com.redis.om.skeleton.models.Person;\nimport com.redis.om.skeleton.models.Person$;\nimport com.redis.om.spring.search.stream.EntityStream;\n\n@Service\npublic class PeopleService {\n  @Autowired\n  EntityStream entityStream;\n\n  // Find all people\n  public Iterable<Person> findAllPeople(int minAge, int maxAge) {\n    return entityStream //\n        .of(Person.class) //\n        .collect(Collectors.toList());\n  }\n\n}\n\n\nThe `EntityStream` is injected into the `PeopleService` using `@Autowired`. We can then get a stream for `Person` objects by using `entityStream.of(Person.class)`. The stream represents the equivalent of a `SELECT * FROM Person` on a relational database. The call to `collect` will then execute the underlying query and return a collection of all `Person` objects in Redis."},{"id":"entity-meta-model","title":"Entity Meta-model","role":"content","text":"You’re provided with a generated meta-model to produce more elaborate queries, a class with the same name as your model but ending with a dollar sign. In the\nexample below, our entity model is `Person`; therefore, we get a meta-model named `Person$`. With the meta-model, you have access to the\nunderlying search engine field operations. For example, we have an `age` property which is an integer. Therefore our meta-model has an `AGE` property with\nnumeric operations we can use with the stream’s `filter` method such as `between`.\n\n\n// Find people by age range\npublic Iterable<Person> findByAgeBetween(int minAge, int maxAge) {\n  return entityStream //\n      .of(Person.class) //\n      .filter(Person$.AGE.between(minAge, maxAge)) //\n      .sorted(Person$.AGE, SortOrder.ASC) //\n      .collect(Collectors.toList());\n}\n\n\nIn this example, we also use the Streams `sorted` method to declare that our stream will be sorted by the `Person$.AGE` in `ASC`ending order.\n\nTo \"AND\" property expressions we can chain multiple `.filter` statements. For example, to recreate\nthe finder by first and last name we can use an Entity Stream in the following way:\n\n\n// Find people by their first and last name\npublic Iterable<Person> findByFirstNameAndLastName(String firstName, String lastName) {\n  return entityStream //\n      .of(Person.class) //\n      .filter(Person$.FIRST_NAME.eq(firstName)) //\n      .filter(Person$.LAST_NAME.eq(lastName)) //\n      .collect(Collectors.toList());\n}\n\n\nIn this article, we explored how Redis OM Spring provides a couple of APIs to tap into the power of Redis Stack’s document database and search features from Spring Boot application. We’ll explore other Redis Stack features via Redis OM Spring in future articles"}],"examples":[]}
{"schema_version":2,"id":"integrate/redisom-for-net","title":"RedisOM for .NET","url":"https://redis.io/docs/latest/integrate/redisom-for-net/","summary":"Learn how to build with Redis Stack and .NET","aliases":["/connect/clients/om-clients/stack-dotnet/","/develop/connect/clients/om-clients/stack-dotnet/"],"tags":["docs","integrate","oss","rs","rc"],"last_updated":"2026-08-10T10:40:34+01:00","children":[],"page_type":"content","content_hash":"0942ab2244455ee82ac955a76a8481a9a1e22d0151047d78c003a105f2fe572c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Redis OM .NET](https://github.com/redis/redis-om-dotnet) is a purpose-built library for handling documents in Redis Stack. In this tutorial, we'll build a simple ASP.NET Core Web-API app for performing CRUD operations on a simple Person & Address model, and we'll accomplish all of this with Redis OM .NET."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* [.NET 6 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/6.0)\n* Any IDE for writing .NET (Visual Studio, Rider, Visual Studio Code).\n* RediSearch must be installed as part of your Redis Stack configuration.\n* Optional: Docker Desktop for running redis-stack in docker for local testing."},{"id":"skip-to-the-code","title":"Skip to the code","role":"content","text":"If you want to skip this tutorial and just jump straight into code, all the source code is available in [GitHub](https://github.com/redis-developer/redis-om-dotnet-skeleton-app)"},{"id":"run-redis-stack","title":"Run Redis Stack","role":"content","text":"There are a variety of ways to run Redis Stack. One way is to use the docker image:\n\n[code example]"},{"id":"create-the-project","title":"Create the project","role":"content","text":"To create the project, just run:\n\n[code example]\n\nThen open the `Redis.OM.Skeleton.csproj` file in your IDE of choice."},{"id":"configure-the-app","title":"Configure the app","role":"configuration","text":"Add a `REDIS_CONNECTION_STRING` field to your `appsettings.json` file to configure the application. Set that connection string to be the URI of your Redis instance. If using the docker command mentioned earlier, your connection string will be `redis://localhost:6379`."},{"id":"connection-string-specification","title":"Connection string specification","role":"content","text":"The specification for Redis URIs is located [here](https://www.iana.org/assignments/uri-schemes/prov/redis). You can use `:password@host:port` or `default:password@host:port` for connection strings that do not include `username`."},{"id":"create-the-model","title":"Create the model","role":"content","text":"Make sure to add the `Redis.OM` package to your project. This package makes it easy to create models and query your Redis domain objects.\n\n[code example]\n\nNow it's time to create the `Person`/`Address` model that the app will use for storing/retrieving people. Create a new directory called `Model` and add the files `Address.cs` and `Person.cs` to it. In `Address.cs`, add the following:\n\n[code example]\n\nHere, you'll notice that except `StreetName`, marked as `Searchable`, all the fields are decorated with the `Indexed` attribute. These attributes (`Searchable` and `Indexed`) tell Redis OM that you want to be able to use those fields in queries when querying your documents in Redis Stack. `Address` will not be a Document itself, so the top-level class is not decorated with anything; instead, the `Address` model will be embedded in our `Person` model.\n\nTo that end, add the following to `Person.cs`\n\n[code example]\n\nThere are a few things to take note of here: \n\n1. `[Document(StorageType = StorageType.Json, Prefixes = new []{\"Person\"})]` Indicates that the data type that Redis OM will use to store the document in Redis is JSON and that the prefix for the keys for the Person class will be `Person`.\n\n2. `[Indexed(CascadeDepth = 1)] Address? Address { get; set; }` is one of two ways you can index an embedded object with Redis OM. This way instructs the index to cascade to the objects in the object graph, `CascadeDepth` of 1 means that it will traverse just one level, indexing the object as if it were building the index from scratch. The other method uses the `JsonPath` property of the individual indexed fields you want to search for. This more surgical approach limits the size of the index.\n\n3. the `Id` property is marked as a `RedisIdField`. This denotes the field as one that will be used to generate the document's key name when it's stored in Redis."},{"id":"create-the-index","title":"Create the Index","role":"content","text":"With the model built, the next step is to create the index in Redis. The most correct way to manage this is to spin the index creation out into a Hosted Service, which will run when the app spins up.\nCreate a `HostedServices` directory and add `IndexCreationService.cs` to that. In that file, add the following, which will create the index on startup.\n\n[code example]\n\nNext, add the following to `Program.cs` to register the service on startup:\n\n[code example]"},{"id":"inject-the-redisconnectionprovider","title":"Inject the RedisConnectionProvider","role":"content","text":"Redis OM uses the `RedisConnectionProvider` class to handle connections to Redis and provides the classes you can use to interact with Redis. To use it, simply inject an instance of the RedisConnectionProvider into your app. In your `Program.cs` file, add:\n\n[code example]\n\nThis will pull your connection string out of the config and initialize the provider. The provider will now be available in your controllers/services to use."},{"id":"create-the-peoplecontroller","title":"Create the PeopleController","role":"content","text":"The final puzzle piece is to write the actual API controller for our People API. In the `controllers` directory, add the file `PeopleController.cs`, the skeleton of the `PeopleController`class will be:\n\n[code example]"},{"id":"inject-the-redisconnectionprovider-1","title":"Inject the RedisConnectionProvider","role":"content","text":"To interact with Redis, inject the RedisConnectionProvider. During this dependency injection, pull out a `RedisCollection<Person>` instance, which will allow a fluent interface for querying documents in Redis.\n\n[code example]"},{"id":"add-route-for-creating-a-person","title":"Add route for creating a Person","role":"content","text":"The first route to add to the API is a POST request for creating a person, using the `RedisCollection`, it's as simple as calling `InsertAsync`, passing in the person object:\n\n\n[code example]"},{"id":"add-route-to-filter-by-age","title":"Add route to filter by age","role":"content","text":"The first filter route to add to the API will let the user filter by a minimum and maximum age. Using the LINQ interface available to the `RedisCollection`, this is a simple operation:\n\n[code example]"},{"id":"filter-by-geolocation","title":"Filter by GeoLocation","role":"content","text":"Redis OM has a `GeoLoc` data structure, an instance of which is indexed by the `Address` model, with the `RedisCollection`, it's possible to find all objects with a radius of particular position using the `GeoFilter` method along with the field you want to filter:\n\n\n[code example]"},{"id":"filter-by-exact-string","title":"Filter by exact string","role":"content","text":"When a string property in your model is marked as `Indexed`, e.g. `FirstName` and `LastName`, Redis OM can perform exact text matches against them. For example, the following two routes filter by `PostalCode` and name demonstrate exact string matches.\n\n[code example]"},{"id":"filter-with-a-full-text-search","title":"Filter with a full-text search","role":"content","text":"When a property in the model is marked as `Searchable`, like `StreetAddress` and `PersonalStatement`, you can perform a full-text search, see the filters for the `PersonalStatement` and `StreetAddress`:\n\n\n[code example]"},{"id":"filter-by-array-membership","title":"Filter by array membership","role":"content","text":"When a string array or list is marked as `Indexed`, Redis OM can filter all the records containing a given string using the `Contains` method of the array or list. For example, our `Person` model has a list of skills you can query by adding the following route.\n\n[code example]"},{"id":"updating-a-person","title":"Updating a person","role":"content","text":"Updating a document in Redis Stack with Redis OM can be done by first materializing the person object, making your desired changes, and then calling `Save` on the collection. The collection is responsible for keeping track of updates made to entities materialized in it; therefore, it will track and apply any updates you make in it. For example, add the following route to update the age of a Person given their Id:\n\n\n[code example]"},{"id":"delete-a-person","title":"Delete a person","role":"content","text":"Deleting a document from Redis can be done with `Unlink`. All that's needed is to call Unlink, passing in the key name. Given an id, we can reconstruct the key name using the prefix and the id:\n\n\n[code example]"},{"id":"run-the-app","title":"Run the app","role":"content","text":"All that's left to do now is to run the app and test it. You can do so by running `dotnet run`, the app is now exposed on port 5000, and there should be a swagger UI that you can use to play with the API at http://localhost:5000/swagger. There's a couple of scripts, along with some data files, to insert some people into Redis using the API in the [GitHub repo](https://github.com/redis-developer/redis-om-dotnet-skeleton-app/tree/main/data)"},{"id":"viewing-data-in-with-redis-insight","title":"Viewing data in with Redis Insight","role":"content","text":"You can either install the Redis Insight GUI or use the Redis Insight GUI running on http://localhost:8001/.\n\nYou can view the data by following these steps:\n\n1. Accept the EULA\n\n![Accept EULA](./images/Accept_EULA.png)\n\n2. Click the Add Redis Database button\n\n![Add Redis Database Button](./images/Add_Redis_Database_button.png)\n\n3. Enter your hostname and port name for your redis server. If you are using the docker image, this is `localhost` and `6379` and give your database an alias\n\n![Configure Redis Insight Database](./images/Configure_Redis_Insight_Database.png)\n\n4. Click `Add Redis Database.`"},{"id":"resources","title":"Resources","role":"content","text":"The source code for this tutorial can be found in [GitHub](https://github.com/redis-developer/redis-om-dotnet-skeleton-app)."}],"examples":[{"id":"run-redis-stack-ex0","language":"plaintext","code":"docker run -d -p 6379:6379 -p 8001:8001 redis/redis-stack","section_id":"run-redis-stack"},{"id":"create-the-project-ex0","language":"bash","code":"dotnet new webapi -n Redis.OM.Skeleton --no-https --kestrelHttpPort 5000","section_id":"create-the-project"},{"id":"create-the-model-ex0","language":"bash","code":"dotnet add package Redis.OM","section_id":"create-the-model"},{"id":"create-the-model-ex1","language":"csharp","code":"using Redis.OM.Modeling;\n\nnamespace Redis.OM.Skeleton.Model;\n\npublic class Address\n{\n    [Indexed]\n    public int? StreetNumber { get; set; }\n    \n    [Indexed]\n    public string? Unit { get; set; }\n    \n    [Searchable]\n    public string? StreetName { get; set; }\n    \n    [Indexed]\n    public string? City { get; set; }\n    \n    [Indexed]\n    public string? State { get; set; }\n    \n    [Indexed]\n    public string? PostalCode { get; set; }\n    \n    [Indexed]\n    public string? Country { get; set; }\n    \n    [Indexed]\n    public GeoLoc Location { get; set; }\n}","section_id":"create-the-model"},{"id":"create-the-model-ex2","language":"csharp","code":"using Redis.OM.Modeling;\n\nnamespace Redis.OM.Skeleton.Model;\n\n[Document(StorageType = StorageType.Json, Prefixes = new []{\"Person\"})]\npublic class Person\n{    \n    [RedisIdField] [Indexed]public string? Id { get; set; }\n    \n    [Indexed] public string? FirstName { get; set; }\n\n    [Indexed] public string? LastName { get; set; }\n    \n    [Indexed] public int Age { get; set; }\n    \n    [Searchable] public string? PersonalStatement { get; set; }\n    \n    [Indexed] public string[] Skills { get; set; } = Array.Empty<string>();    \n    \n    [Indexed(CascadeDepth = 1)] public Address? Address { get; set; }\n    \n}","section_id":"create-the-model"},{"id":"create-the-index-ex0","language":"csharp","code":"using Redis.OM.Skeleton.Model;\n\nnamespace Redis.OM.Skeleton.HostedServices;\n\npublic class IndexCreationService : IHostedService\n{\n    private readonly RedisConnectionProvider _provider;\n    public IndexCreationService(RedisConnectionProvider provider)\n    {\n        _provider = provider;\n    }\n    \n    public async Task StartAsync(CancellationToken cancellationToken)\n    {\n        await _provider.Connection.CreateIndexAsync(typeof(Person));\n    }\n\n    public Task StopAsync(CancellationToken cancellationToken)\n    {\n        return Task.CompletedTask;\n    }\n}","section_id":"create-the-index"},{"id":"create-the-index-ex1","language":"csharp","code":"builder.Services.AddHostedService<IndexCreationService>();","section_id":"create-the-index"},{"id":"inject-the-redisconnectionprovider-ex0","language":"csharp","code":"builder.Services.AddSingleton(new RedisConnectionProvider(builder.Configuration[\"REDIS_CONNECTION_STRING\"]));","section_id":"inject-the-redisconnectionprovider"},{"id":"create-the-peoplecontroller-ex0","language":"csharp","code":"using Microsoft.AspNetCore.Mvc;\nusing Redis.OM.Searching;\nusing Redis.OM.Skeleton.Model;\n\nnamespace Redis.OM.Skeleton.Controllers;\n\n[ApiController]\n[Route(\"[controller]\")]\npublic class PeopleController : ControllerBase\n{\n\n}","section_id":"create-the-peoplecontroller"},{"id":"inject-the-redisconnectionprovider-1-ex0","language":"csharp","code":"private readonly RedisCollection<Person> _people;\nprivate readonly RedisConnectionProvider _provider;\npublic PeopleController(RedisConnectionProvider provider)\n{\n    _provider = provider;\n    _people = (RedisCollection<Person>)provider.RedisCollection<Person>();\n}","section_id":"inject-the-redisconnectionprovider-1"},{"id":"add-route-for-creating-a-person-ex0","language":"csharp","code":"[HttpPost]\npublic async Task<Person> AddPerson([FromBody] Person person)\n{\n    await _people.InsertAsync(person);\n    return person;\n}","section_id":"add-route-for-creating-a-person"},{"id":"add-route-to-filter-by-age-ex0","language":"csharp","code":"[HttpGet(\"filterAge\")]\npublic IList<Person> FilterByAge([FromQuery] int minAge, [FromQuery] int maxAge)\n{        \n    return _people.Where(x => x.Age >= minAge && x.Age <= maxAge).ToList();\n}","section_id":"add-route-to-filter-by-age"},{"id":"filter-by-geolocation-ex0","language":"csharp","code":"[HttpGet(\"filterGeo\")]\npublic IList<Person> FilterByGeo([FromQuery] double lon, [FromQuery] double lat, [FromQuery] double radius, [FromQuery] string unit)\n{\n    return _people.GeoFilter(x => x.Address!.Location, lon, lat, radius, Enum.Parse<GeoLocDistanceUnit>(unit)).ToList();\n}","section_id":"filter-by-geolocation"},{"id":"filter-by-exact-string-ex0","language":"csharp","code":"[HttpGet(\"filterName\")]\npublic IList<Person> FilterByName([FromQuery] string firstName, [FromQuery] string lastName)\n{\n    return _people.Where(x => x.FirstName == firstName && x.LastName == lastName).ToList();\n}\n\n[HttpGet(\"postalCode\")]\npublic IList<Person> FilterByPostalCode([FromQuery] string postalCode)\n{\n    return _people.Where(x => x.Address!.PostalCode == postalCode).ToList();\n}","section_id":"filter-by-exact-string"},{"id":"filter-with-a-full-text-search-ex0","language":"csharp","code":"[HttpGet(\"fullText\")]\npublic IList<Person> FilterByPersonalStatement([FromQuery] string text){\n    return _people.Where(x => x.PersonalStatement == text).ToList();\n}\n\n[HttpGet(\"streetName\")]\npublic IList<Person> FilterByStreetName([FromQuery] string streetName)\n{\n    return _people.Where(x => x.Address!.StreetName == streetName).ToList();\n}","section_id":"filter-with-a-full-text-search"},{"id":"filter-by-array-membership-ex0","language":"csharp","code":"[HttpGet(\"skill\")]\npublic IList<Person> FilterBySkill([FromQuery] string skill)\n{\n    return _people.Where(x => x.Skills.Contains(skill)).ToList();\n}","section_id":"filter-by-array-membership"},{"id":"updating-a-person-ex0","language":"csharp","code":"[HttpPatch(\"updateAge/{id}\")]\npublic IActionResult UpdateAge([FromRoute] string id, [FromBody] int newAge)\n{\n    foreach (var person in _people.Where(x => x.Id == id))\n    {\n        person.Age = newAge;\n    }\n    _people.Save();\n    return Accepted();\n}","section_id":"updating-a-person"},{"id":"delete-a-person-ex0","language":"csharp","code":"[HttpDelete(\"{id}\")]\npublic IActionResult DeletePerson([FromRoute] string id)\n{\n    _provider.Connection.Unlink($\"Person:{id}\");\n    return NoContent();\n}","section_id":"delete-a-person"}]}
{"schema_version":2,"id":"integrate/redisom-for-node-js","title":"RedisOM for Node.js","url":"https://redis.io/docs/latest/integrate/redisom-for-node-js/","summary":"Learn how to build with Redis Stack and Node.js","aliases":["/develop/connect/clients/om-clients/stack-node/"],"tags":["docs","integrate","oss","rs","rc"],"last_updated":"2026-09-17T14:54:53+01:00","children":[],"page_type":"content","content_hash":"1bd27909820a885003e19bcde159bab326b9b02e603104245ac0c01551974d35","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This tutorial will show you how to build a simple, RESTful API that reads, writes, and finds data on persons (including first name, last name, and  age) using Node.js and Redis Stack. You'll also add a simple location tracking feature for a bit of extra interest. You'll be using [Express](https://expressjs.com/) and [Redis OM](https://github.com/redis/redis-om-node) to do this."},{"id":"what-is-redis-om","title":"What is Redis OM?","role":"content","text":"Redis OM is an object-mapping library that provides high-level abstractions for working with Redis as a document database. You can use Redis OM to map Redis data types,specifically Hashes and JSON documents, to objects in your preferred programming language.\n\nRedis OM leverages the JSON and Redis Search features included in Redis Stack, enabling you to:\n\n- **Model your data** as objects with schemas and validation\n- **Store and retrieve** complex data structures seamlessly\n- **Search and query** your data using full-text search, numeric ranges, and geospatial queries\n- **Index your data** automatically for optimal query performance\n\nWith Redis OM, you work with familiar object-oriented patterns while Redis Stack handles the underlying data storage, indexing, and querying efficiently.\n\n> [!NOTE]\n> Redis Stack was replaced by Redis Open Source as of version 8.0."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Like anything software-related, you need to have some dependencies installed before you can get started:\n\n- [Node.js 14.8+](https://nodejs.org/en/): In this tutorial, we're using JavaScript's top-level `await` feature which was introduced in Node 14.8. So, make sure you are using that version or later.\n- [Redis Stack](https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-stack): You need a version of Redis Stack, either running locally on your machine or [in the cloud](https://redis.com/try-free/?utm_source=redisio&utm_medium=referral&utm_campaign=2023-09-try_free&utm_content=cu-redis_cloud_users).\n- [Redis Insight](https://redis.com/redis-enterprise/redis-insight/): We'll use this to look inside Redis and make sure our code is doing what we think it's doing."},{"id":"starter-code","title":"Starter code","role":"content","text":"We're not going to code this completely from scratch. Instead, we've provided some starter code for you. Go ahead and clone it to a folder of your convenience:\n\n    git clone git@github.com:redis-developer/express-redis-om-workshop.git\n\nNow that you have the starter code, let's explore it a bit. Opening up `server.js` in the root we see that we have a simple Express app that uses [*Dotenv*](https://www.npmjs.com/package/dotenv) for configuration and [Swagger UI Express](https://www.npmjs.com/package/swagger-ui-express) for testing our API:\n\n\nimport 'dotenv/config'\n\nimport express from 'express'\nimport swaggerUi from 'swagger-ui-express'\nimport YAML from 'yamljs'\n\n/* create an express app and use JSON */\nconst app = new express()\napp.use(express.json())\n\n/* set up swagger in the root */\nconst swaggerDocument = YAML.load('api.yaml')\napp.use('/', swaggerUi.serve, swaggerUi.setup(swaggerDocument))\n\n/* start the server */\napp.listen(8080)\n\n\nAlongside this is `api.yaml`, which defines the API we're going to build and provides the information Swagger UI Express needs to render its UI. You don't need to mess with it unless you want to add some additional routes.\n\nThe `persons` folder has some JSON files and a shell script. The JSON files are sample persons—all musicians because fun—that you can load into the API to test it. The shell script—`load-data.sh`—will load all the JSON files into the API using `curl`.\n\nThere are two empty folders, `om` and `routers`. The `om` folder is where all the Redis OM code will go. The `routers` folder will hold code for all of our Express routes."},{"id":"configure-and-run","title":"Configure and run","role":"configuration","text":"The starter code is perfectly runnable if a bit thin. Let's configure and run it to make sure it works before we move on to writing actual code. First, get all the dependencies:\n\n    npm install\n\nThen, set up a `.env` file in the root that Dotenv can make use of. There's a `sample.env` file in the root that you can copy and modify:\n\n    cp sample.env .env\n\nThe contents of `.env` looks like this:\n\n\n# Put your local Redis Stack URL here. Want to run in the\n# cloud instead? Sign up at https://redis.com/try-free/.\nREDIS_URL=redis://localhost:6379\n\n\nThere's a good chance this is already correct. However, if you need to change the `REDIS_URL` for your particular environment (e.g., you're running Redis Stack in the cloud), this is the time to do it. Once done, you should be able to run the app:\n\n    npm start\n\nNavigate to `http://localhost:8080` and check out the client that Swagger UI Express has created. None of it *works* yet because we haven't implemented any of the routes. But, you can try them out and watch them fail!\n\nThe starter code runs. Let's add some Redis OM to it so it actually *does* something!"},{"id":"setting-up-a-client","title":"Setting up a Client","role":"configuration","text":"First things first, let's set up a **client**. The `Client` class is the thing that knows how to talk to Redis on behalf of Redis OM. One option is to put our client in its own file and export it. This ensures that the application has one and only one instance of `Client` and thus only one connection to Redis Stack. Since Redis and JavaScript are both (more or less) single-threaded, this works neatly.\n\nLet's create our first file. In the `om` folder add a file called `client.js` and add the following code:\n\n\nimport { Client } from 'redis-om'\n\n/* pulls the Redis URL from .env */\nconst url = process.env.REDIS_URL\n\n/* create and open the Redis OM Client */\nconst client = await new Client().open(url)\n\nexport default client\n\n\n> Remember that _top-level await_ stuff we mentioned earlier? There it is!\n\nNote that we are getting our Redis URL from an environment variable. It was put there by Dotenv and read from our `.env` file. If we didn't have the `.env` file or have a `REDIS_URL` property in our `.env` file, this code would gladly read this value from the *actual* environment variables.\n\nAlso note that the `.open()` method conveniently returns `this`. This `this` (can I say *this* again? I just did!) lets us chain the instantiation of the client with the opening of the client. If this isn't to your liking, you could always write it like this:\n\n\n/* create and open the Redis OM Client */\nconst client = new Client()\nawait client.open(url)"},{"id":"entity-schema-and-repository","title":"Entity, Schema, and Repository","role":"content","text":"Now that we have a client that's connected to Redis, we need to start mapping some persons. To do that, we need to define an `Entity` and a `Schema`. Let's start by creating a file named `person.js` in the `om` folder and importing `client` from `client.js` and the `Entity` and `Schema` classes from Redis OM:\n\n\nimport { Entity, Schema } from 'redis-om'\nimport client from './client.js'"},{"id":"entity","title":"Entity","role":"content","text":"Next, we need to define an **entity**. An `Entity` is the class that holds you data when you work with it—the thing being mapped to. It is what you create, read, update, and delete. Any class that extends `Entity` is an entity. We'll define our `Person` entity with a single line:\n\n\n/* our entity */\nclass Person extends Entity {}"},{"id":"schema","title":"Schema","role":"content","text":"A **schema** defines the fields on your entity, their types, and how they are mapped internally to Redis. By default, entities map to JSON documents. Let's create our `Schema` in `person.js`:\n\n\n/* create a Schema for Person */\nconst personSchema = new Schema(Person, {\n  firstName: { type: 'string' },\n  lastName: { type: 'string' },\n  age: { type: 'number' },\n  verified: { type: 'boolean' },\n  location: { type: 'point' },\n  locationUpdated: { type: 'date' },\n  skills: { type: 'string[]' },\n  personalStatement: { type: 'text' }\n})\n\n\nWhen you create a `Schema`, it modifies the `Entity` class you handed it (`Person` in our case) adding getters and setters for the properties you define. The type those getters and setters accept and return are defined with the type parameter as shown above. Valid values are: `string`, `number`, `boolean`, `string[]`, `date`, `point`, and `text`.\n\nThe first three do exactly what you think—they define a property that is a [`String`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String), a [`Number`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number), or a [`Boolean`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean). `string[]` does what you'd think as well, specifically defining an [`Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) of strings.\n\n`date` is a little different, but still more or less what you'd expect. It defines a property that returns a [`Date`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) and can be set using not only a `Date` but also a `String` containing an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date or a `Number` with the [UNIX epoch time](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#the_ecmascript_epoch_and_timestamps) in *milliseconds*.\n\nA `point` defines a point somewhere on the globe as a longitude and a latitude. It creates a property that returns and accepts a simple object with the properties of `longitude` and `latitude`. Like this:\n\n\nlet point = { longitude: 12.34, latitude: 56.78 }\n\n\nA `text` field is a lot like a `string`. If you're just reading and writing objects, they are identical. But if you want to *search* on them, they are very, very different. We'll talk about search more later, but the tl;dr is that `string` fields can only be matched on their whole value—no partial matches—and are best for keys while `text` fields have full-text search enabled on them and are optimized for human-readable text."},{"id":"repository","title":"Repository","role":"content","text":"Now we have all the pieces that we need to create a **repository**. A `Repository` is the main interface into Redis OM. It gives us the methods to read, write, and remove a specific `Entity`. Create a `Repository` in `person.js` and make sure it's exported as you'll need it when we start implementing out API:\n\n\n/* use the client to create a Repository just for Persons */\nexport const personRepository = new Repository(personSchema, client)\n\n\nWe're almost done with setting up our repository. But we still need to create an index or we won't be able to search. We do that by calling `.createIndex()`. If an index already exists and it's identical, this function won't do anything. If it's different, it'll drop it and create a new one. Add a call to `.createIndex()` to `person.js`:\n\n\n/* create the index for Person */\nawait personRepository.createIndex()\n\n\nThat's all we need for `person.js` and all we need to start talking to Redis using Redis OM. Here's the code in its entirety:\n\n\nimport { Entity, Schema } from 'redis-om'\nimport client from './client.js'\n\n/* our entity */\nclass Person extends Entity {}\n\n/* create a Schema for Person */\nconst personSchema = new Schema(Person, {\n  firstName: { type: 'string' },\n  lastName: { type: 'string' },\n  age: { type: 'number' },\n  verified: { type: 'boolean' },\n  location: { type: 'point' },\n  locationUpdated: { type: 'date' },\n  skills: { type: 'string[]' },\n  personalStatement: { type: 'text' }\n})\n\n/* use the client to create a Repository just for Persons */\nexport const personRepository = client.fetchRepository(personSchema)\n\n/* create the index for Person */\nawait personRepository.createIndex()\n\n\nNow, let's add some routes in Express."},{"id":"set-up-the-person-router","title":"Set up the Person Router","role":"content","text":"Let's create a truly RESTful API with the CRUD operations mapping to PUT, GET, POST, and DELETE respectively. We're going to do this using [Express Routers](https://expressjs.com/en/4x/api.html#router) as this makes our code nice and tidy. Create a file called `person-router.js` in the `routers` folder and in it import `Router` from Express and `personRepository` from `person.js`. Then create and export a `Router`:\n\n\nimport { Router } from 'express'\nimport { personRepository } from '../om/person.js'\n\nexport const router = Router()\n\n\nImports and exports done, let's bind the router to our Express app. Open up `server.js` and import the `Router` we just created:\n\n\n/* import routers */\nimport { router as personRouter } from './routers/person-router.js'\n\n\nThen add the `personRouter` to the Express app:\n\n\n/* bring in some routers */\napp.use('/person', personRouter)\n\n\nYour `server.js` should now look like this:\n\n\nimport 'dotenv/config'\n\nimport express from 'express'\nimport swaggerUi from 'swagger-ui-express'\nimport YAML from 'yamljs'\n\n/* import routers */\nimport { router as personRouter } from './routers/person-router.js'\n\n/* create an express app and use JSON */\nconst app = new express()\napp.use(express.json())\n\n/* bring in some routers */\napp.use('/person', personRouter)\n\n/* set up swagger in the root */\nconst swaggerDocument = YAML.load('api.yaml')\napp.use('/', swaggerUi.serve, swaggerUi.setup(swaggerDocument))\n\n/* start the server */\napp.listen(8080)\n\n\nNow we can add our routes to create, read, update, and delete persons. Head back to the `person-router.js` file so we can do just that."},{"id":"creating-a-person","title":"Creating a Person","role":"content","text":"We'll create a person first as you need to have persons in Redis before you can do any of the reading, writing, or removing of them. Add the PUT route below. This route will call `.createAndSave()` to create a `Person` from the request body and immediately save it to the Redis:\n\n\nrouter.put('/', async (req, res) => {\n  const person = await personRepository.createAndSave(req.body)\n  res.send(person)\n})\n\n\nNote that we are also returning the newly created `Person`. Let's see what that looks like by actually calling our API using the Swagger UI. Go to http://localhost:8080 in your browser and try it out. The default request body in Swagger will be fine for testing. You should see a response that looks like this:\n\n\n{\n  \"entityId\": \"01FY9MWDTWW4XQNTPJ9XY9FPMN\",\n  \"firstName\": \"Rupert\",\n  \"lastName\": \"Holmes\",\n  \"age\": 75,\n  \"verified\": false,\n  \"location\": {\n    \"longitude\": 45.678,\n    \"latitude\": 45.678\n  },\n  \"locationUpdated\": \"2022-03-01T12:34:56.123Z\",\n  \"skills\": [\n    \"singing\",\n    \"songwriting\",\n    \"playwriting\"\n  ],\n  \"personalStatement\": \"I like piña coladas and walks in the rain\"\n}\n\n\nThis is exactly what we handed it with one exception: the `entityId`. Every entity in Redis OM has an entity ID which is—as you've probably guessed—the unique ID of that entity. It was randomly generated when we called `.createAndSave()`. Yours will be different, so make note of it.\n\nYou can see this newly created JSON document in Redis with Redis Insight. Go ahead and launch Redis Insight and you should see a key with a name like `Person:01FY9MWDTWW4XQNTPJ9XY9FPMN`. The `Person` bit of the key was derived from the class name of our entity and the sequence of letters and numbers is our generated entity ID. Click on it to take a look at the JSON document you've created.\n\nYou'll also see a key named `Person:index:hash`. That's a unique value that Redis OM uses to see if it needs to recreate the index or not when `.createIndex()` is called. You can safely ignore it."},{"id":"reading-a-person","title":"Reading a Person","role":"content","text":"Create down, let's add a GET route to read this newly created `Person`:\n\n\nrouter.get('/:id', async (req, res) => {\n  const person = await personRepository.fetch(req.params.id)\n  res.send(person)\n})\n\n\nThis code extracts a parameter from the URL used in the route—the `entityId` that we received previously. It uses the `.fetch()` method on the `personRepository` to retrieve a `Person` using that `entityId`. Then, it returns that `Person`.\n\nLet's go ahead and test that in Swagger as well. You should get back exactly the same response. In fact, since this is a simple GET, we should be able to just load the URL into our browser. Test that out too by navigating to http://localhost:8080/person/01FY9MWDTWW4XQNTPJ9XY9FPMN, replacing the entity ID with your own.\n\nNow that we can read and write, let's implement the *REST* of the HTTP verbs. REST... get it?"},{"id":"updating-a-person","title":"Updating a Person","role":"content","text":"Let's add the code to update a person using a POST route:\n\n\nrouter.post('/:id', async (req, res) => {\n\n  const person = await personRepository.fetch(req.params.id)\n\n  person.firstName = req.body.firstName ?? null\n  person.lastName = req.body.lastName ?? null\n  person.age = req.body.age ?? null\n  person.verified = req.body.verified ?? null\n  person.location = req.body.location ?? null\n  person.locationUpdated = req.body.locationUpdated ?? null\n  person.skills = req.body.skills ?? null\n  person.personalStatement = req.body.personalStatement ?? null\n\n  await personRepository.save(person)\n\n  res.send(person)\n})\n\n\nThis code fetches the `Person` from the `personRepository` using the `entityId` just like our previous route did. However, now we change all the properties based on the properties in the request body. If any of them are missing, we set them to `null`. Then, we call `.save()` and return the changed `Person`.\n\nLet's test this in Swagger too, why not? Make some changes. Try removing some of the fields. What do you get back when you read it after you've changed it?"},{"id":"deleting-a-person","title":"Deleting a Person","role":"content","text":"Deletion—my favorite! Remember kids, deletion is 100% compression. The route that deletes is just as straightforward as the one that reads, but much more destructive:\n\n\nrouter.delete('/:id', async (req, res) => {\n  await personRepository.remove(req.params.id)\n  res.send({ entityId: req.params.id })\n})\n\n\nI guess we should probably test this one out too. Load up Swagger and exercise the route. You should get back JSON with the entity ID you just removed:\n\n\n{\n  \"entityId\": \"01FY9MWDTWW4XQNTPJ9XY9FPMN\"\n}\n\n\nAnd just like that, it's gone!"},{"id":"all-the-crud","title":"All the CRUD","role":"content","text":"Do a quick check with what you've written so far. Here's what should be the totality of your `person-router.js` file:\n\n\nimport { Router } from 'express'\nimport { personRepository } from '../om/person.js'\n\nexport const router = Router()\n\nrouter.put('/', async (req, res) => {\n  const person = await personRepository.createAndSave(req.body)\n  res.send(person)\n})\n\nrouter.get('/:id', async (req, res) => {\n  const person = await personRepository.fetch(req.params.id)\n  res.send(person)\n})\n\nrouter.post('/:id', async (req, res) => {\n\n  const person = await personRepository.fetch(req.params.id)\n\n  person.firstName = req.body.firstName ?? null\n  person.lastName = req.body.lastName ?? null\n  person.age = req.body.age ?? null\n  person.verified = req.body.verified ?? null\n  person.location = req.body.location ?? null\n  person.locationUpdated = req.body.locationUpdated ?? null\n  person.skills = req.body.skills ?? null\n  person.personalStatement = req.body.personalStatement ?? null\n\n  await personRepository.save(person)\n\n  res.send(person)\n})\n\nrouter.delete('/:id', async (req, res) => {\n  await personRepository.remove(req.params.id)\n  res.send({ entityId: req.params.id })\n})"},{"id":"preparing-to-search","title":"Preparing to search","role":"content","text":"CRUD completed, let's do some searching. In order to search, we need data to search over. Remember that `persons` folder with all the JSON documents and the `load-data.sh` shell script? Its time has arrived. Go into that folder and run the script:\n\n    cd persons\n    ./load-data.sh\n\nYou should get a rather verbose response containing the JSON response from the API and the names of the files you loaded. Like this:\n\n\n{\"entityId\":\"01FY9Z4RRPKF4K9H78JQ3K3CP3\",\"firstName\":\"Chris\",\"lastName\":\"Stapleton\",\"age\":43,\"verified\":true,\"location\":{\"longitude\":-84.495,\"latitude\":38.03},\"locationUpdated\":\"2022-01-01T12:00:00.000Z\",\"skills\":[\"singing\",\"football\",\"coal mining\"],\"personalStatement\":\"There are days that I can walk around like I'm alright. And I pretend to wear a smile on my face. And I could keep the pain from comin' out of my eyes. But sometimes, sometimes, sometimes I cry.\"} <- chris-stapleton.json\n{\"entityId\":\"01FY9Z4RS2QQVN4XFYSNPKH6B2\",\"firstName\":\"David\",\"lastName\":\"Paich\",\"age\":67,\"verified\":false,\"location\":{\"longitude\":-118.25,\"latitude\":34.05},\"locationUpdated\":\"2022-01-01T12:00:00.000Z\",\"skills\":[\"singing\",\"keyboard\",\"blessing\"],\"personalStatement\":\"I seek to cure what's deep inside frightened of this thing that I've become\"} <- david-paich.json\n{\"entityId\":\"01FY9Z4RSD7SQMSWDFZ6S4M5MJ\",\"firstName\":\"Ivan\",\"lastName\":\"Doroschuk\",\"age\":64,\"verified\":true,\"location\":{\"longitude\":-88.273,\"latitude\":40.115},\"locationUpdated\":\"2022-01-01T12:00:00.000Z\",\"skills\":[\"singing\",\"dancing\",\"friendship\"],\"personalStatement\":\"We can dance if we want to. We can leave your friends behind. 'Cause your friends don't dance and if they don't dance well they're no friends of mine.\"} <- ivan-doroschuk.json\n{\"entityId\":\"01FY9Z4RSRZFGQ21BMEKYHEVK6\",\"firstName\":\"Joan\",\"lastName\":\"Jett\",\"age\":63,\"verified\":false,\"location\":{\"longitude\":-75.273,\"latitude\":40.003},\"locationUpdated\":\"2022-01-01T12:00:00.000Z\",\"skills\":[\"singing\",\"guitar\",\"black eyeliner\"],\"personalStatement\":\"I love rock n' roll so put another dime in the jukebox, baby.\"} <- joan-jett.json\n{\"entityId\":\"01FY9Z4RT25ABWYTW6ZG7R79V4\",\"firstName\":\"Justin\",\"lastName\":\"Timberlake\",\"age\":41,\"verified\":true,\"location\":{\"longitude\":-89.971,\"latitude\":35.118},\"locationUpdated\":\"2022-01-01T12:00:00.000Z\",\"skills\":[\"singing\",\"dancing\",\"half-time shows\"],\"personalStatement\":\"What goes around comes all the way back around.\"} <- justin-timberlake.json\n{\"entityId\":\"01FY9Z4RTD9EKBDS2YN9CRMG1D\",\"firstName\":\"Kerry\",\"lastName\":\"Livgren\",\"age\":72,\"verified\":false,\"location\":{\"longitude\":-95.689,\"latitude\":39.056},\"locationUpdated\":\"2022-01-01T12:00:00.000Z\",\"skills\":[\"poetry\",\"philosophy\",\"songwriting\",\"guitar\"],\"personalStatement\":\"All we are is dust in the wind.\"} <- kerry-livgren.json\n{\"entityId\":\"01FY9Z4RTR73HZQXK83JP94NWR\",\"firstName\":\"Marshal\",\"lastName\":\"Mathers\",\"age\":49,\"verified\":false,\"location\":{\"longitude\":-83.046,\"latitude\":42.331},\"locationUpdated\":\"2022-01-01T12:00:00.000Z\",\"skills\":[\"rapping\",\"songwriting\",\"comics\"],\"personalStatement\":\"Look, if you had, one shot, or one opportunity to seize everything you ever wanted, in one moment, would you capture it, or just let it slip?\"} <- marshal-mathers.json\n{\"entityId\":\"01FY9Z4RV2QHH0Z1GJM5ND15JE\",\"firstName\":\"Rupert\",\"lastName\":\"Holmes\",\"age\":75,\"verified\":true,\"location\":{\"longitude\":-2.518,\"latitude\":53.259},\"locationUpdated\":\"2022-01-01T12:00:00.000Z\",\"skills\":[\"singing\",\"songwriting\",\"playwriting\"],\"personalStatement\":\"I like piña coladas and taking walks in the rain.\"} <- rupert-holmes.json\n\n\nA little messy, but if you don't see this, then it didn't work!\n\nNow that we have some data, let's add another router to hold the search routes we want to add. Create a file named `search-router.js` in the routers folder and set it up with imports and exports just like we did in `person-router.js`:\n\n\nimport { Router } from 'express'\nimport { personRepository } from '../om/person.js'\n\nexport const router = Router()\n\n\nImport the `Router` into `server.js` the same way we did for the `personRouter`:\n\n\n/* import routers */\nimport { router as personRouter } from './routers/person-router.js'\nimport { router as searchRouter } from './routers/search-router.js'\n\n\nThen add the `searchRouter` to the Express app:\n\n\n/* bring in some routers */\napp.use('/person', personRouter)\napp.use('/persons', searchRouter)\n\n\nRouter bound, we can now add some routes."},{"id":"search-all-the-things","title":"Search all the things","role":"content","text":"We're going to add a plethora of searches to our new `Router`. But the first will be the easiest as it's just going to return everything. Go ahead and add the following code to `search-router.js`:\n\n\nrouter.get('/all', async (req, res) => {\n  const persons = await personRepository.search().return.all()\n  res.send(persons)\n})\n\n\nHere we see how to start and finish a search. Searches start just like CRUD operations start—on a `Repository`. But instead of calling `.createAndSave()`, `.fetch()`, `.save()`, or `.remove()`, we call `.search()`. And unlike all those other methods, `.search()` doesn't end there. Instead, it allows you to build up a query (which you'll see in the next example) and then resolve it with a call to `.return.all()`.\n\nWith this new route in place, go into the Swagger UI and exercise the `/persons/all` route. You should see all of the folks you added with the shell script as a JSON array.\n\nIn the example above, the query is not specified—we didn't build anything up. If you do this, you'll just get everything. Which is what you want sometimes. But not most of the time. It's not really searching if you just return everything. So let's add a route that lets us find persons by their last name. Add the following code:\n\n\nrouter.get('/by-last-name/:lastName', async (req, res) => {\n  const lastName = req.params.lastName\n  const persons = await personRepository.search()\n    .where('lastName').equals(lastName).return.all()\n  res.send(persons)\n})\n\n\nIn this route, we're specifying a field we want to filter on and a value that it needs to equal. The field name in the call to `.where()` is the name of the field specified in our schema. This field was defined as a `string`, which matters because the type of the field determines the methods that are available query it.\n\nIn the case of a `string`, there's just `.equals()`, which will query against the value of the entire string. This is aliased as `.eq()`, `.equal()`, and `.equalTo()` for your convenience. You can even add a little more syntactic sugar with calls to `.is` and `.does` that really don't do anything but make your code pretty. Like this:\n\n\nconst persons = await personRepository.search().where('lastName').is.equalTo(lastName).return.all()\nconst persons = await personRepository.search().where('lastName').does.equal(lastName).return.all()\n\n\nYou can also invert the query with a call to `.not`:\n\n\nconst persons = await personRepository.search().where('lastName').is.not.equalTo(lastName).return.all()\nconst persons = await personRepository.search().where('lastName').does.not.equal(lastName).return.all()\n\n\nIn all these cases, the call to `.return.all()` executes the query we build between it and the call to `.search()`. We can search on other field types as well. Let's add some routes to search on a `number` and a `boolean` field:\n\n\nrouter.get('/old-enough-to-drink-in-america', async (req, res) => {\n  const persons = await personRepository.search()\n    .where('age').gte(21).return.all()\n  res.send(persons)\n})\n\nrouter.get('/non-verified', async (req, res) => {\n  const persons = await personRepository.search()\n    .where('verified').is.not.true().return.all()\n  res.send(persons)\n})\n\n\nThe `number` field is filtering persons by age where the age is great than or equal to 21. Again, there are aliases and syntactic sugar:\n\n\nconst persons = await personRepository.search().where('age').is.greaterThanOrEqualTo(21).return.all()\n\n\nBut there are also more ways to query:\n\n\nconst persons = await personRepository.search().where('age').eq(21).return.all()\nconst persons = await personRepository.search().where('age').gt(21).return.all()\nconst persons = await personRepository.search().where('age').gte(21).return.all()\nconst persons = await personRepository.search().where('age').lt(21).return.all()\nconst persons = await personRepository.search().where('age').lte(21).return.all()\nconst persons = await personRepository.search().where('age').between(21, 65).return.all()\n\n\nThe `boolean` field is searching for persons by their verification status. It already has some of our syntactic sugar in it. Note that this query will match a missing value or a false value. That's why I specified `.not.true()`. You can also call `.false()` on boolean fields as well as all the variations of `.equals`.\n\n\nconst persons = await personRepository.search().where('verified').true().return.all()\nconst persons = await personRepository.search().where('verified').false().return.all()\nconst persons = await personRepository.search().where('verified').equals(true).return.all()\n\n\n> So, we've created a few routes and I haven't told you to test them. Maybe you have anyhow. If so, good for you, you rebel. For the rest of you, why don't you go ahead and test them now with Swagger? And, going forward, just test them when you want. Heck, create some routes of your own using the provided syntax and try those out too. Don't let me tell you how to live your life.\n\nOf course, querying on just one field is never enough. Not a problem, Redis OM can handle `.and()` and `.or()` like in this route:\n\n\nrouter.get('/verified-drinkers-with-last-name/:lastName', async (req, res) => {\n  const lastName = req.params.lastName\n  const persons = await personRepository.search()\n    .where('verified').is.true()\n      .and('age').gte(21)\n      .and('lastName').equals(lastName).return.all()\n  res.send(persons)\n})\n\n\nHere, I'm just showing the syntax for `.and()` but, of course, you can also use `.or()`."},{"id":"full-text-search","title":"Full-text search","role":"content","text":"If you've defined a field with a type of `text` in your schema, you can perform full-text searches against it. The way a `text` field is searched is different from how a `string` is searched. A `string` can only be compared with `.equals()` and must match the entire string. With a `text` field, you can look for words within the string.\n\nA `text` field is optimized for human-readable text, like an essay or song lyrics. It's pretty clever. It understands that certain words (like *a*, *an*, or *the*) are common and ignores them. It understands how words are grammatically similar and so if you search for *give*, it matches *gives*, *given*, *giving*, and *gave* too. And it ignores punctuation.\n\nLet's add a route that does full-text search against our `personalStatement` field:\n\n\nrouter.get('/with-statement-containing/:text', async (req, res) => {\n  const text = req.params.text\n  const persons = await personRepository.search()\n    .where('personalStatement').matches(text)\n      .return.all()\n  res.send(persons)\n})\n\n\nNote the use of the `.matches()` function. This is the only one that works with `text` fields. It takes a string that can be one or more words—space-delimited—that you want to query for. Let's try it out. In Swagger, use this route to search for the word \"walk\". You should get the following results:\n\n\n[\n  {\n    \"entityId\": \"01FYC7CTR027F219455PS76247\",\n    \"firstName\": \"Rupert\",\n    \"lastName\": \"Holmes\",\n    \"age\": 75,\n    \"verified\": true,\n    \"location\": {\n      \"longitude\": -2.518,\n      \"latitude\": 53.259\n    },\n    \"locationUpdated\": \"2022-01-01T12:00:00.000Z\",\n    \"skills\": [\n      \"singing\",\n      \"songwriting\",\n      \"playwriting\"\n    ],\n    \"personalStatement\": \"I like piña coladas and taking walks in the rain.\"\n  },\n  {\n    \"entityId\": \"01FYC7CTNBJD9CZKKWPQEZEW14\",\n    \"firstName\": \"Chris\",\n    \"lastName\": \"Stapleton\",\n    \"age\": 43,\n    \"verified\": true,\n    \"location\": {\n      \"longitude\": -84.495,\n      \"latitude\": 38.03\n    },\n    \"locationUpdated\": \"2022-01-01T12:00:00.000Z\",\n    \"skills\": [\n      \"singing\",\n      \"football\",\n      \"coal mining\"\n    ],\n    \"personalStatement\": \"There are days that I can walk around like I'm alright. And I pretend to wear a smile on my face. And I could keep the pain from comin' out of my eyes. But sometimes, sometimes, sometimes I cry.\"\n  }\n]\n\n\nNotice how the word \"walk\" is matched for Rupert Holmes' personal statement that contains \"walks\" *and* matched for Chris Stapleton's that contains \"walk\". Now search \"walk raining\". You'll see that this returns Rupert's entry only even though the exact text of neither of these words is found in his personal statement. But they are *grammatically* related so it matched them. This is called stemming and it's a pretty cool feature of Redis Stack that Redis OM exploits.\n\nAnd if you search for \"a rain walk\" you'll *still* match Rupert's entry even though the word \"a\" is not in the text. Why? Because it's a common word that's not very helpful with searching. These common words are called stop words and this is another cool feature of Redis Stack that Redis OM just gets for free."},{"id":"searching-the-globe","title":"Searching the globe","role":"content","text":"Redis Stack, and therefore Redis OM, both support searching by geographic location. You specify a point in the globe, a radius, and the units for that radius and it'll gleefully return all the entities therein. Let's add a route to do just that:\n\n\nrouter.get('/near/:lng,:lat/radius/:radius', async (req, res) => {\n  const longitude = Number(req.params.lng)\n  const latitude = Number(req.params.lat)\n  const radius = Number(req.params.radius)\n\n  const persons = await personRepository.search()\n    .where('location')\n      .inRadius(circle => circle\n          .longitude(longitude)\n          .latitude(latitude)\n          .radius(radius)\n          .miles)\n        .return.all()\n\n  res.send(persons)\n})\n\n\nThis code looks a little different than the others because the way we define the circle we want to search is done with a function that is passed into the `.inRadius` method:\n\n\ncircle => circle.longitude(longitude).latitude(latitude).radius(radius).miles\n\n\nAll this function does is accept an instance of a [`Circle`](https://github.com/redis/redis-om-node/blob/main/docs/classes/Circle.md) that has been initialized with default values. We override those values by calling various builder methods to define the origin of our search (i.e. the longitude and latitude), the radius, and the units that radius is measured in. Valid units are `miles`, `meters`, `feet`, and `kilometers`.\n\nLet's try the route out. I know we can find Joan Jett at around longitude -75.0 and latitude 40.0, which is in eastern Pennsylvania. So use those coordinates with a radius of 20 miles. You should receive in response:\n\n\n[\n  {\n    \"entityId\": \"01FYC7CTPKYNXQ98JSTBC37AS1\",\n    \"firstName\": \"Joan\",\n    \"lastName\": \"Jett\",\n    \"age\": 63,\n    \"verified\": false,\n    \"location\": {\n      \"longitude\": -75.273,\n      \"latitude\": 40.003\n    },\n    \"locationUpdated\": \"2022-01-01T12:00:00.000Z\",\n    \"skills\": [\n      \"singing\",\n      \"guitar\",\n      \"black eyeliner\"\n    ],\n    \"personalStatement\": \"I love rock n' roll so put another dime in the jukebox, baby.\"\n  }\n]\n\n\nTry widening the radius and see who else you can find."},{"id":"adding-location-tracking","title":"Adding location tracking","role":"content","text":"We're getting toward the end of the tutorial here, but before we go, I'd like to add that location tracking piece that I mentioned way back in the beginning. This next bit of code should be easily understood if you've gotten this far as it's not really doing anything I haven't talked about already.\n\nAdd a new file called `location-router.js` in the `routers` folder:\n\n\nimport { Router } from 'express'\nimport { personRepository } from '../om/person.js'\n\nexport const router = Router()\n\nrouter.patch('/:id/location/:lng,:lat', async (req, res) => {\n\n  const id = req.params.id\n  const longitude = Number(req.params.lng)\n  const latitude = Number(req.params.lat)\n\n  const locationUpdated = new Date()\n\n  const person = await personRepository.fetch(id)\n  person.location = { longitude, latitude }\n  person.locationUpdated = locationUpdated\n  await personRepository.save(person)\n\n  res.send({ id, locationUpdated, location: { longitude, latitude } })\n})\n\n\nHere we're calling `.fetch()` to fetch a person, we're updating some values for that person—the `.location` property with our longitude and latitude and the `.locationUpdated` property with the current date and time. Easy stuff.\n\nTo use this `Router`, import it in `server.js`:\n\n\n/* import routers */\nimport { router as personRouter } from './routers/person-router.js'\nimport { router as searchRouter } from './routers/search-router.js'\nimport { router as locationRouter } from './routers/location-router.js'\n\n\nAnd bind the router to a path:\n\n\n/* bring in some routers */\napp.use('/person', personRouter, locationRouter)\napp.use('/persons', searchRouter)\n\n\nAnd that's that. But this just isn't enough to satisfy. It doesn't show you anything new, except maybe the usage of a `date` field. And, it's not really location *tracking*. It just shows where these people last were, no history. So let's add some!.\n\nTo add some history, we're going to use a [Redis Stream](https://redis.io/docs/latest/develop/data-types/streams). Streams are a big topic but don't worry if you’re not familiar with them, you can think of them as being sort of like a log file stored in a Redis key where each entry represents an event. In our case, the event would be the person moving about or checking in or whatever.\n\nBut there's a problem. Redis OM doesn’t support Streams even though Redis Stack does. So how do we take advantage of them in our application? By using [Node Redis](https://github.com/redis/node-redis). Node Redis is a low-level Redis client for Node.js that gives you access to all the Redis commands and data types. Internally, Redis OM is creating and using a Node Redis connection. You can use that connection too. Or rather, Redis OM can be *told* to use the connection you are using. Let me show you how."},{"id":"using-node-redis","title":"Using Node Redis","role":"content","text":"Open up `client.js` in the `om` folder. Remember how we created a Redis OM `Client` and then called `.open()` on it?\n\n\nconst client = await new Client().open(url)\n\n\nWell, the `Client` class also has a `.use()` method that takes a Node Redis connection. Modify `client.js` to open a connection to Redis using Node Redis and then `.use()` it:\n\n\nimport { Client } from 'redis-om'\nimport { createClient } from 'redis'\n\n/* pulls the Redis URL from .env */\nconst url = process.env.REDIS_URL\n\n/* create a connection to Redis with Node Redis */\nexport const connection = createClient({ url })\nawait connection.connect()\n\n/* create a Client and bind it to the Node Redis connection */\nconst client = await new Client().use(connection)\n\nexport default client\n\n\nAnd that's it. Redis OM is now using the `connection` you created. Note that we are exporting both the `client` *and* the `connection`. Got to export the `connection` if we want to use it in our newest route."},{"id":"storing-location-history-with-streams","title":"Storing location history with Streams","role":"content","text":"To add an event to a Stream we need to use the [XADD](https://redis.io/docs/latest/commands/xadd) command. Node Redis exposes that as `.xAdd()`. So, we need to add a call to `.xAdd()` in our route. Modify `location-router.js` to import our `connection`:\n\n\nimport { connection } from '../om/client.js'\n\n\nAnd then in the route itself add a call to `.xAdd()`:\n\n\n  ...snip...\n  const person = await personRepository.fetch(id)\n  person.location = { longitude, latitude }\n  person.locationUpdated = locationUpdated\n  await personRepository.save(person)\n\n  let keyName = `${person.keyName}:locationHistory`\n  await connection.xAdd(keyName, '*', person.location)\n  ...snip...\n\n\n`.xAdd()` takes a key name, an event ID, and a JavaScript object containing the keys and values that make up the event, i.e. the event data. For the key name, we're building a string using the `.keyName` property that `Person` inherited from `Entity` (which will return something like `Person:01FYC7CTPKYNXQ98JSTBC37AS1`) combined with a hard-coded value. We're passing in `*` for our event ID, which tells Redis to just generate it based on the current time and previous event ID. And we're passing in the location—with properties of longitude and latitude—as our event data.\n\nNow, whenever this route is exercised, the longitude and latitude will be logged and the event ID will encode the time. Go ahead and use Swagger to move Joan Jett around a few times.\n\nNow, go into Redis Insight and take a look at the Stream. You'll see it there in the list of keys but if you click on it, you'll get a message saying that \"This data type is coming soon!\". If you don't get this message, congratulations, you live in the future! For us here in the past, we'll just issue the raw command instead:\n\n    XRANGE Person:01FYC7CTPKYNXQ98JSTBC37AS1:locationHistory - +\n\nThis tells Redis to get a range of values from a Stream stored in the given the key name—`Person:01FYC7CTPKYNXQ98JSTBC37AS1:locationHistory` in our example. The next values are the starting event ID and the ending event ID. `-` is the beginning of the Stream. `+` is the end. So this returns everything in the Stream:\n\n    1) 1) \"1647536562911-0\"\n      2) 1) \"longitude\"\n          2) \"45.678\"\n          3) \"latitude\"\n          4) \"45.678\"\n    2) 1) \"1647536564189-0\"\n      2) 1) \"longitude\"\n          2) \"45.679\"\n          3) \"latitude\"\n          4) \"45.679\"\n    3) 1) \"1647536565278-0\"\n      2) 1) \"longitude\"\n          2) \"45.680\"\n          3) \"latitude\"\n          4) \"45.680\"\n\nAnd just like that, we're tracking Joan Jett."},{"id":"wrap-up","title":"Wrap-up","role":"content","text":"So, now you know how to use Express + Redis OM to build an API backed by Redis Stack. And, you've got yourself some pretty decent started code in the process. Good deal! If you want to learn more, you can check out the [documentation](https://github.com/redis/redis-om-node) for Redis OM. It covers the full breadth of Redis OM's features.\n\nAnd thanks for taking the time to work through this. I sincerely hope you found it useful. If you have any questions, the [Redis Discord server](https://discord.gg/redis) is by far the best place to get them answered. Join the server and ask away!"}],"examples":[]}
{"schema_version":2,"id":"integrate/redisom-for-python","title":"RedisOM for Python","url":"https://redis.io/docs/latest/integrate/redisom-for-python/","summary":"Learn how to build with Redis Stack and Python","aliases":["/connect/clients/om-clients/stack-python/","/develop/connect/clients/om-clients/stack-python/"],"tags":["docs","integrate","oss","rs","rc"],"last_updated":"2026-09-17T14:54:53+01:00","children":[],"page_type":"content","content_hash":"3ef7e558b914e179527bdac35aa06eee1d014a72528e213f4526115d3f1f4f35","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"[Redis OM Python](https://github.com/redis/redis-om-python) is a Redis client that provides high-level abstractions for managing document data in Redis. This tutorial shows you how to get up and running with Redis OM Python, Redis Stack, and the [Flask](https://flask.palletsprojects.com/) micro-framework.\n\nWe'd love to see what you build with Redis Stack and Redis OM.  [Join the Redis community on Discord](https://discord.gg/redis) to chat with us about all things Redis OM and Redis Stack.  Read more about Redis OM Python [our announcement blog post](https://redis.com/blog/introducing-redis-om-for-python/)."},{"id":"overview","title":"Overview","role":"overview","text":"This application, an API built with Flask and a simple domain model, demonstrates common data manipulation patterns using Redis OM. \n\nOur entity is a Person, with the following JSON representation:\n\n[code example]\n\nWe'll let Redis OM handle generation of unique IDs, which it does using [ULIDs](https://github.com/ulid/spec).  Redis OM will also handle creation of unique Redis key names for us, as well as saving and retrieving entities from JSON documents stored in a Redis Stack database."},{"id":"getting-started","title":"Getting Started","role":"setup","text":""},{"id":"requirements","title":"Requirements","role":"content","text":"To run this application you'll need:\n\n* [git](https://git-scm.com/download) - to clone the repo to your machine. \n* [Python 3.9 or higher](https://www.python.org/downloads/).\n* A [Redis Stack](https://redis.io) database, or Redis with the [Search and Query](https://redis.io/docs/latest/develop/ai/search-and-query) and [JSON](https://redis.io/docs/latest/develop/data-types/json) features installed. We've provided a `docker-compose.yml` for this. You can also [sign up for a free 30Mb database with Redis Cloud](https://redis.com/try-free/?utm_source=redisio&utm_medium=referral&utm_campaign=2023-09-try_free&utm_content=cu-redis_cloud_users) - be sure to check the Redis Stack option when creating your cloud database.\n* [curl](https://curl.se/), or [Postman](https://www.postman.com/) - to send HTTP requests to the application.  We'll provide examples using curl in this document.\n* Optional: [Redis Insight](https://redis.com/redis-enterprise/redis-insight/), a free data visualization and database management tool for Redis.  When downloading Redis Insight, be sure to select version 2.x or use the version that comes with Redis Stack."},{"id":"get-the-source-code","title":"Get the Source Code","role":"content","text":"Clone the repository from GitHub:\n\n[code example]"},{"id":"start-a-redis-stack-database-or-configure-your-redis-cloud-credentials","title":"Start a Redis Stack Database, or Configure your Redis Cloud Credentials","role":"content","text":"Next, we'll get a Redis Stack database up and running.  If you're using Docker:\n\n[code example]\n\nIf you're using Redis Cloud, you'll need the hostname, port number, and password for your database.  Use these to set the `REDIS_OM_URL` environment variable like this:\n\n[code example]\n\n(This step is not required when working with Docker as the Docker container runs Redis on `localhost` port `6379` with no password, which is the default connection that Redis OM uses.)\n\nFor example if your Redis Cloud database is at port `9139` on host `enterprise.redis.com` and your password is `5uper53cret` then you'd set `REDIS_OM_URL` as follows:\n\n[code example]"},{"id":"create-a-python-virtual-environment-and-install-the-dependencies","title":"Create a Python Virtual Environment and Install the Dependencies","role":"content","text":"Create a Python virtual environment, and install the project dependencies which are [Flask](https://pypi.org/project/Flask/), [Requests](https://pypi.org/project/requests/) (used only in the data loader script) and [Redis OM](https://pypi.org/project/redis-om/):\n\n[code example]"},{"id":"start-the-flask-application","title":"Start the Flask Application","role":"content","text":"Let's start the Flask application in development mode, so that Flask will restart the server for you each time you save code changes in `app.py`:\n\n[code example]\n\nIf all goes well, you should see output similar to this:\n\n[code example]\n\nYou're now up and running, and ready to perform CRUD operations on data with Redis, Search and Query, JSON and Redis OM for Python! To make sure the server's running, point your browser at `http://127.0.0.1:5000/`, where you can expect to see the application's basic home page:\n\n![screenshot](./images/python_server_running.png)"},{"id":"load-the-sample-data","title":"Load the Sample Data","role":"content","text":"We've provided a small amount of sample data (it's in `data/people.json`.  The Python script `dataloader.py` loads each person into Redis by posting the data to the application's create a new person endpoint.  Run it like this:\n\n[code example]\n\nMake sure to take a copy of the output of the data loader, as your IDs will differ from those used in the tutorial.  To follow along, substitute your IDs for the ones shown above.  e.g. whenever we are working with Kareem Khan, change `01FX8RMR7T60ANQTS4P9NKPKX8` for the ID that your data loader assigned to Kareem in your Redis database."},{"id":"problems","title":"Problems?","role":"content","text":"If the Flask server fails to start, take a look at its output.  If you see log entries similar to this:\n\n[code example]\n\nthen you need to start the Redis Docker container if using Docker, or set the `REDIS_OM_URL` environment variable if using Redis Cloud.\n\nIf you've set the `REDIS_OM_URL` environment variable, and the code errors with something like this on startup:\n\n[code example]\n\nthen you'll need to check that you used the correct hostname, port, password and format when setting `REDIS_OM_URL`.\n\nIf the data loader fails to post the sample data into the application, make sure that the Flask application is running **before** running the data loader."},{"id":"create-read-update-and-delete-data","title":"Create, Read, Update and Delete Data","role":"content","text":"Let's create and manipulate some instances of our data model in Redis.  Here we'll look at how to call the Flask API with curl (you could also use Postman), how the code works, and how the data's stored in Redis."},{"id":"building-a-person-model-with-redis-om","title":"Building a Person Model with Redis OM","role":"content","text":"Redis OM allows us to model entities using Python classes, and the [Pydantic](https://pypi.org/project/pydantic/) framework.  Our person model is contained in the file `person.py`.  Here's some notes about how it works:\n\n* We declare a class `Person` which extends a Redis OM class `JsonModel`.  This tells Redis OM that we want to store these entities in Redis as JSON documents.\n* We then declare each field in our model, specifying the data type and whether or not we want to index on that field.  For example, here's the `age` field, which we've declared as a positive integer that we want to index on:\n\n[code example]\n\n* The `skills` field is a list of strings, declared thus:\n\n[code example]\n\n* For the `personal_statement` field, we don't want to index on the field's value, as it's a free text sentence rather than a single word or digit. For this, we'll tell Redis OM that we want to be able to perform full text searches on the values:\n\n[code example]\n\n* `address` works differently from the other fields.  Note that in our JSON representation of the model, address is an object rather than a string or numerical field.  With Redis OM, this is modeled as a second class, which extends the Redis OM `EmbeddedJsonModel` class:\n\n[code example]\n\n* Fields in an `EmbeddedJsonModel` are defined in the same way, so our class contains a field definition for each data item in the address.\n\n* Not every field in our JSON is present in every address, Redis OM allows us to declare a field as optional so long as we don't index it:\n\n[code example]\n\n* We can also set a default value for a field... let's say country should be \"United Kingdom\" unless otherwise specified:\n\n[code example]\n\n* Finally, to add the embedded address object to our Person model, we declare a field of type `Address` in the Person class:\n\n[code example]"},{"id":"adding-new-people","title":"Adding New People","role":"content","text":"The function `create_person` in `app.py` handles the creation of a new person in Redis.  It expects a JSON object that adheres to our Person model's schema.  The code to then create a new Person object with that data and save it in Redis is simple:\n\n[code example]\n\nWhen a new Person instance is created, Redis OM assigns it a unique ULID primary key, which we can access as `.pk`.  We return that to the caller, so that they know the ID of the object they just created.\n\nPersisting the object to Redis is then simply a matter of calling `.save()` on it.\n\nTry it out... with the server running, add a new person using curl:\n\n[code example]\n\nRunning the above curl command will return the unique ULID ID assigned to the newly created person. For example `01FX8SSSDN7PT9T3N0JZZA758G`."},{"id":"examining-the-data-in-redis","title":"Examining the data in Redis","role":"content","text":"Let's take a look at what we just saved in Redis.  Using Redis Insight or redis-cli, connect to the database and look at the value stored at key `:person.Person:01FX8SSSDN7PT9T3N0JZZA758G`.  This is stored as a JSON document in Redis, so if using redis-cli you'll need the following command:\n\n[code example]\n\nIf you're using Redis Insight, the browser will render the key value for you when you click on the key name:\n\n![Data in Redis Insight](./images/python_insight_explore_person.png)\n\nWhen storing data as JSON in Redis, we can update and retrieve the whole document, or just parts of it.  For example, to retrieve only the person's address and first skill, use the following command (Redis Insight users should use the built in redis-cli for this):\n\n[code example]\n\nFor more information on the JSON Path syntax used to query JSON documents in Redis, see the [documentation](https://redis.io/docs/latest/develop/data-types/json/path#jsonpath-syntax)."},{"id":"find-a-person-by-id","title":"Find a Person by ID","role":"content","text":"If we know a person's ID, we can retrieve their data.  The function `find_by_id` in `app.py` receives an ID as its parameter, and asks Redis OM to retrieve and populate a Person object using the ID and the Person `.get` class method:\n\n[code example]\n\nThe `.dict()` method converts our Person object to a Python dictionary that Flask then returns to the caller.\n\nNote that if there is no Person with the supplied ID in Redis, `get` will throw a `NotFoundError`.\n\nTry this out with curl, substituting `01FX8SSSDN7PT9T3N0JZZA758G` for the ID of a person that you just created in your database:\n\n[code example]\n\nThe server responds with a JSON object containing the user's data:\n\n[code example]"},{"id":"find-people-with-matching-first-and-last-name","title":"Find People with Matching First and Last Name","role":"content","text":"Let's find all the people who have a given first and last name...  This is handled by the function `find_by_name` in `app.py`.\n\nHere, we're using Person's `find` class method that's provided by Redis OM.  We pass it a search query, specifying that we want to find people whose `first_name` field contains the value of the `first_name` parameter passed to `find_by_name` AND whose `last_name` field contains the value of the `last_name` parameter:\n\n[code example]\n\n`.all()` tells Redis OM that we want to retrieve all matching people.\n\nTry this out with curl as follows:\n\n[code example]\n\n**Note:** First and last name are case sensitive.\n\nThe server responds with an object containing `results`, an array of matches:\n\n[code example]"},{"id":"find-people-within-a-given-age-range","title":"Find People within a Given Age Range","role":"content","text":"It's useful to be able to find people that fall into a given age range... the function `find_in_age_range` in `app.py` handles this as follows...\n\nWe'll again use Person's `find` class method, this time passing it a minimum and maximum age, specifying that we want results where the `age` field is between those values only:\n\n[code example]\n\nNote that we can also use `.sort_by` to specify which field we want our results sorted by.\n\nLet's find everyone between 30 and 47 years old, sorted by age:\n\n[code example]\n\nThis returns a `results` object containing an array of matches:\n\n[code example]"},{"id":"find-people-in-a-given-city-with-a-specific-skill","title":"Find People in a Given City with a Specific Skill","role":"content","text":"Now, we'll try a slightly different sort of query.  We want to find all of the people that live in a given city AND who also have a certain skill.  This requires a search over both the `city` field which is a string, and the `skills` field, which is an array of strings.\n\nEssentially we want to say \"Find me all the people whose city is `city` AND whose skills array CONTAINS `desired_skill`\", where `city` and `desired_skill` are the parameters to the `find_matching_skill` function in `app.py`.  Here's the code for that:\n\n[code example]\n\nThe `<<` operator here is used to indicate \"in\" or \"contains\".\n\nLet's find all the guitar players in Sheffield:\n\n[code example]\n\n**Note:** `Sheffield` is case sensitive.\n\nThe server returns a `results` array containing matching people:\n\n[code example]"},{"id":"find-people-using-full-text-search-on-their-personal-statements","title":"Find People using Full Text Search on their Personal Statements","role":"content","text":"Each person has a `personal_statement` field, which is a free text string containing a couple of sentences about them.  We chose to index this in a way that makes it full text searchable, so let's see how to use this now.  The code for this is in the function `find_matching_statements` in `app.py`.\n\nTo search for people who have the value of the parameter `search_term` in their `personal_statement` field, we use the `%` operator:\n\n[code example]\n\nLet's find everyone who talks about \"play\" in their personal statement.\n\n[code example]\n\nThe server responds with a `results` array of matching people:\n\n[code example]\n\nNote that we get results including matches for \"play\", \"plays\" and \"playing\"."},{"id":"update-a-persons-age","title":"Update a Person's Age","role":"content","text":"As well as retrieving information from Redis, we'll also want to update a Person's data from time to time.  Let's see how to do that with Redis OM for Python.\n\nThe function `update_age` in `app.py` accepts two parameters: `id` and `new_age`.  Using these, we first retrieve the person's data from Redis and create a new object with it:\n\n[code example]\n\nAssuming we find the person, let's update their age and save the data back to Redis:\n\n[code example]\n\nLet's change Kareem Khan's age from 27 to 28:\n\n[code example]\n\nThe server responds with `ok`."},{"id":"delete-a-person","title":"Delete a Person","role":"content","text":"If we know a person's ID, we can delete them from Redis without first having to load their data into a Person object. In the function `delete_person` in `app.py`, we call the `delete` class method on the Person class to do this:\n\n[code example]\n\nLet's delete Dan Harris, the person with ID `01FX8RMR8545RWW4DYCE5MSZA1`:\n\n[code example]\n\nThe server responds with an `ok` response regardless of whether the ID provided existed in Redis."},{"id":"setting-an-expiry-time-for-a-person","title":"Setting an Expiry Time for a Person","role":"configuration","text":"This is an example of how to run arbitrary Redis commands against instances of a model saved in Redis.  Let's see how we can set the time to live (TTL) on a person, so that Redis will expire the JSON document after a configurable number of seconds have passed.\n\nThe function `expire_by_id` in `app.py` handles this as follows.  It takes two parameters: `id` - the ID of a person to expire, and `seconds` - the number of seconds in the future to expire the person after.  This requires us to run the Redis [`EXPIRE`](https://redis.io/docs/latest/commands/expire) command against the person's key.  To do this, we need to access the Redis connection from the `Person` model like so:\n\n[code example]\n\nLet's set the person with ID `01FX8RMR82D091TC37B45RCWY3` to expire in 600 seconds:\n\n[code example]\n\nUsing `redis-cli`, you can check that the person now has a TTL set with the Redis `expire` command:\n\n[code example]\n\nThis shows that Redis will expire the key 584 seconds from now.\n\nYou can use the `.db()` function on your model class to get at the underlying redis-py connection whenever you want to run lower level Redis commands.  For more details, see the [redis-py documentation](https://redis.readthedocs.io/en/stable/)."},{"id":"shutting-down-redis-docker","title":"Shutting Down Redis (Docker)","role":"content","text":"If you're using Docker, and want to shut down the Redis container when you are finished with the application, use `docker-compose down`:\n\n[code example]"}],"examples":[{"id":"overview-ex0","language":"json","code":"{\n  \"first_name\": \"A string, the person's first or given name\",\n  \"last_name\": \"A string, the person's last or surname\",\n  \"age\": 36,\n  \"address\": {\n    \"street_number\": 56,\n    \"unit\": \"A string, optional unit number e.g. B or 1\",\n    \"street_name\": \"A string, name of the street they live on\",\n    \"city\": \"A string, name of the city they live in\",\n    \"state\": \"A string, state, province or county that they live in\",\n    \"postal_code\": \"A string, their zip or postal code\",\n    \"country\": \"A string, country that they live in.\"\n  },\n  \"personal_statement\": \"A string, free text personal statement\",\n  \"skills\": [\n    \"A string: a skill the person has\",\n    \"A string: another still that the person has\"\n  ]\n}","section_id":"overview"},{"id":"get-the-source-code-ex0","language":"bash","code":"$ git clone https://github.com/redis-developer/redis-om-python-flask-skeleton-app.git\n$ cd redis-om-python-flask-skeleton-app","section_id":"get-the-source-code"},{"id":"start-a-redis-stack-database-or-configure-your-redis-cloud-credentials-ex0","language":"bash","code":"$ docker-compose up -d\nCreating network \"redis-om-python-flask-skeleton-app_default\" with the default driver\nCreating redis_om_python_flask_starter ... done","section_id":"start-a-redis-stack-database-or-configure-your-redis-cloud-credentials"},{"id":"start-a-redis-stack-database-or-configure-your-redis-cloud-credentials-ex1","language":"bash","code":"$ export REDIS_OM_URL=redis://default:<password>@<host>:<port>","section_id":"start-a-redis-stack-database-or-configure-your-redis-cloud-credentials"},{"id":"start-a-redis-stack-database-or-configure-your-redis-cloud-credentials-ex2","language":"bash","code":"$ export REDIS_OM_URL=redis://default:5uper53cret@enterprise.redis.com:9139","section_id":"start-a-redis-stack-database-or-configure-your-redis-cloud-credentials"},{"id":"create-a-python-virtual-environment-and-install-the-dependencies-ex0","language":"bash","code":"$ python3 -m venv venv\n$ . ./venv/bin/activate\n$ pip install -r requirements.txt","section_id":"create-a-python-virtual-environment-and-install-the-dependencies"},{"id":"start-the-flask-application-ex0","language":"bash","code":"$ export FLASK_ENV=development\n$ flask run","section_id":"start-the-flask-application"},{"id":"start-the-flask-application-ex1","language":"bash","code":"$ flask run\n * Environment: development\n * Debug mode: on\n * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)\n * Restarting with stat\n * Debugger is active!\n * Debugger PIN: XXX-XXX-XXX","section_id":"start-the-flask-application"},{"id":"load-the-sample-data-ex0","language":"bash","code":"$ python dataloader.py\nCreated person Robert McDonald with ID 01FX8RMR7NRS45PBT3XP9KNAZH\nCreated person Kareem Khan with ID 01FX8RMR7T60ANQTS4P9NKPKX8\nCreated person Fernando Ortega with ID 01FX8RMR7YB283BPZ88HAG066P\nCreated person Noor Vasan with ID 01FX8RMR82D091TC37B45RCWY3\nCreated person Dan Harris with ID 01FX8RMR8545RWW4DYCE5MSZA1","section_id":"load-the-sample-data"},{"id":"problems-ex0","language":"py","code":"raise ConnectionError(self._error_message(e))\nredis.exceptions.ConnectionError: Error 61 connecting to localhost:6379. Connection refused.","section_id":"problems"},{"id":"problems-ex1","language":"py","code":"raise ConnectionError(self._error_message(e))\nredis.exceptions.ConnectionError: Error 8 connecting to enterprise.redis.com:9139. nodename nor servname provided, or not known.","section_id":"problems"},{"id":"building-a-person-model-with-redis-om-ex0","language":"py","code":"age: PositiveInt = Field(index=True)","section_id":"building-a-person-model-with-redis-om"},{"id":"building-a-person-model-with-redis-om-ex1","language":"py","code":"skills: List[str] = Field(index=True)","section_id":"building-a-person-model-with-redis-om"},{"id":"building-a-person-model-with-redis-om-ex2","language":"py","code":"personal_statement: str = Field(index=True, full_text_search=True)","section_id":"building-a-person-model-with-redis-om"},{"id":"building-a-person-model-with-redis-om-ex3","language":"py","code":"class Address(EmbeddedJsonModel):\n    # field definitions...","section_id":"building-a-person-model-with-redis-om"},{"id":"building-a-person-model-with-redis-om-ex4","language":"py","code":"unit: Optional[str] = Field(index=False)","section_id":"building-a-person-model-with-redis-om"},{"id":"building-a-person-model-with-redis-om-ex5","language":"py","code":"country: str = Field(index=True, default=\"United Kingdom\")","section_id":"building-a-person-model-with-redis-om"},{"id":"building-a-person-model-with-redis-om-ex6","language":"py","code":"address: Address","section_id":"building-a-person-model-with-redis-om"},{"id":"adding-new-people-ex0","language":"py","code":"new_person = Person(**request.json)\n  new_person.save()\n  return new_person.pk","section_id":"adding-new-people"},{"id":"adding-new-people-ex1","language":"bash","code":"curl --location --request POST 'http://127.0.0.1:5000/person/new' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{\n    \"first_name\": \"Joanne\",\n    \"last_name\": \"Peel\",\n    \"age\": 36,\n    \"personal_statement\": \"Music is my life, I love gigging and playing with my band.\",\n    \"address\": {\n      \"street_number\": 56,\n      \"unit\": \"4A\",\n      \"street_name\": \"The Rushes\",\n      \"city\": \"Birmingham\",\n      \"state\": \"West Midlands\",\n      \"postal_code\": \"B91 6HG\",\n      \"country\": \"United Kingdom\"\n    },\n    \"skills\": [\n      \"synths\",\n      \"vocals\",\n      \"guitar\"\n    ]\n}'","section_id":"adding-new-people"},{"id":"examining-the-data-in-redis-ex0","language":"bash","code":"$ redis-cli\n127.0.0.1:6379> json.get :person.Person:01FX8SSSDN7PT9T3N0JZZA758G","section_id":"examining-the-data-in-redis"},{"id":"examining-the-data-in-redis-ex1","language":"bash","code":"$ redis-cli\n127.0.0.1:6379> json.get :person.Person:01FX8SSSDN7PT9T3N0JZZA758G $.address $.skills[0]\n\"{\\\"$.skills[0]\\\":[\\\"synths\\\"],\\\"$.address\\\":[{\\\"pk\\\":\\\"01FX8SSSDNRDSRB3HMVH00NQTT\\\",\\\"street_number\\\":56,\\\"unit\\\":\\\"4A\\\",\\\"street_name\\\":\\\"The Rushes\\\",\\\"city\\\":\\\"Birmingham\\\",\\\"state\\\":\\\"West Midlands\\\",\\\"postal_code\\\":\\\"B91 6HG\\\",\\\"country\\\":\\\"United Kingdom\\\"}]}\"","section_id":"examining-the-data-in-redis"},{"id":"find-a-person-by-id-ex0","language":"py","code":"try:\n      person = Person.get(id)\n      return person.dict()\n  except NotFoundError:\n      return {}","section_id":"find-a-person-by-id"},{"id":"find-a-person-by-id-ex1","language":"bash","code":"curl --location --request GET 'http://localhost:5000/person/byid/01FX8SSSDN7PT9T3N0JZZA758G'","section_id":"find-a-person-by-id"},{"id":"find-a-person-by-id-ex2","language":"json","code":"{\n  \"address\": {\n    \"city\": \"Birmingham\",\n    \"country\": \"United Kingdom\",\n    \"pk\": \"01FX8SSSDNRDSRB3HMVH00NQTT\",\n    \"postal_code\": \"B91 6HG\",\n    \"state\": \"West Midlands\",\n    \"street_name\": \"The Rushes\",\n    \"street_number\": 56,\n    \"unit\": null\n  },\n  \"age\": 36,\n  \"first_name\": \"Joanne\",\n  \"last_name\": \"Peel\",\n  \"personal_statement\": \"Music is my life, I love gigging and playing with my band.\",\n  \"pk\": \"01FX8SSSDN7PT9T3N0JZZA758G\",\n  \"skills\": [\n    \"synths\",\n    \"vocals\",\n    \"guitar\"\n  ]\n}","section_id":"find-a-person-by-id"},{"id":"find-people-with-matching-first-and-last-name-ex0","language":"py","code":"people = Person.find(\n      (Person.first_name == first_name) &\n      (Person.last_name == last_name)\n  ).all()","section_id":"find-people-with-matching-first-and-last-name"},{"id":"find-people-with-matching-first-and-last-name-ex1","language":"bash","code":"curl --location --request GET 'http://127.0.0.1:5000/people/byname/Kareem/Khan'","section_id":"find-people-with-matching-first-and-last-name"},{"id":"find-people-with-matching-first-and-last-name-ex2","language":"json","code":"{\n  \"results\": [\n    {\n      \"address\": {\n        \"city\": \"Sheffield\",\n        \"country\": \"United Kingdom\",\n        \"pk\": \"01FX8RMR7THMGA84RH8ZRQRRP9\", \n        \"postal_code\": \"S1 5RE\",\n        \"state\": \"South Yorkshire\",\n        \"street_name\": \"The Beltway\",\n        \"street_number\": 1,\n        \"unit\": \"A\"\n      },\n      \"age\": 27,\n      \"first_name\": \"Kareem\",\n      \"last_name\": \"Khan\",\n      \"personal_statement\":\"I'm Kareem, a multi-instrumentalist and singer looking to join a new rock band.\",\n      \"pk\":\"01FX8RMR7T60ANQTS4P9NKPKX8\",\n      \"skills\": [\n        \"drums\",\n        \"guitar\",\n        \"synths\"\n      ]\n    }\n  ]\n}","section_id":"find-people-with-matching-first-and-last-name"},{"id":"find-people-within-a-given-age-range-ex0","language":"py","code":"people = Person.find(\n      (Person.age >= min_age) &\n      (Person.age <= max_age)\n  ).sort_by(\"age\").all()","section_id":"find-people-within-a-given-age-range"},{"id":"find-people-within-a-given-age-range-ex1","language":"bash","code":"curl --location --request GET 'http://127.0.0.1:5000/people/byage/30/47'","section_id":"find-people-within-a-given-age-range"},{"id":"find-people-within-a-given-age-range-ex2","language":"json","code":"{\n  \"results\": [\n    {\n      \"address\": {\n        \"city\": \"Sheffield\",\n        \"country\": \"United Kingdom\",\n        \"pk\": \"01FX8RMR7NW221STN6NVRDPEDT\",\n        \"postal_code\": \"S12 2MX\",\n        \"state\": \"South Yorkshire\",\n        \"street_name\": \"Main Street\",\n        \"street_number\": 9,\n        \"unit\": null\n      },\n      \"age\": 35,\n      \"first_name\": \"Robert\",\n      \"last_name\": \"McDonald\",\n      \"personal_statement\": \"My name is Robert, I love meeting new people and enjoy music, coding and walking my dog.\",\n      \"pk\": \"01FX8RMR7NRS45PBT3XP9KNAZH\",\n      \"skills\": [\n        \"guitar\",\n        \"piano\",\n        \"trombone\"\n      ]\n    },\n    {\n      \"address\": {\n        \"city\": \"Birmingham\",\n        \"country\": \"United Kingdom\",\n        \"pk\": \"01FX8SSSDNRDSRB3HMVH00NQTT\",\n        \"postal_code\": \"B91 6HG\",\n        \"state\": \"West Midlands\",\n        \"street_name\": \"The Rushes\",\n        \"street_number\": 56,\n        \"unit\": null\n      },\n      \"age\": 36,\n      \"first_name\": \"Joanne\",\n      \"last_name\": \"Peel\",\n      \"personal_statement\": \"Music is my life, I love gigging and playing with my band.\",\n      \"pk\": \"01FX8SSSDN7PT9T3N0JZZA758G\",\n      \"skills\": [\n        \"synths\",\n        \"vocals\",\n        \"guitar\"\n      ]\n    },\n    {\n      \"address\": {\n        \"city\": \"Nottingham\",\n        \"country\": \"United Kingdom\",\n        \"pk\": \"01FX8RMR82DDJ90CW8D1GM68YZ\",\n        \"postal_code\": \"NG1 1AA\",\n        \"state\": \"Nottinghamshire\",\n        \"street_name\": \"Broadway\",\n        \"street_number\": 12,\n        \"unit\": \"A-1\"\n      },\n      \"age\": 37,\n      \"first_name\": \"Noor\",\n      \"last_name\": \"Vasan\",\n      \"personal_statement\": \"I sing and play the guitar, I enjoy touring and meeting new people on the road.\",\n      \"pk\": \"01FX8RMR82D091TC37B45RCWY3\",\n      \"skills\": [\n        \"vocals\",\n        \"guitar\"\n      ]\n    },\n    {\n      \"address\": {\n        \"city\": \"San Diego\",\n        \"country\": \"United States\",\n        \"pk\": \"01FX8RMR7YCDAVSWBMWCH2B07G\",\n        \"postal_code\": \"92102\",\n        \"state\": \"California\",\n        \"street_name\": \"C Street\",\n        \"street_number\": 1299,\n        \"unit\": null\n      },\n      \"age\": 43,\n      \"first_name\": \"Fernando\",\n      \"last_name\": \"Ortega\",\n      \"personal_statement\": \"I'm in a really cool band that plays a lot of cover songs.  I'm the drummer!\",\n      \"pk\": \"01FX8RMR7YB283BPZ88HAG066P\",\n      \"skills\": [\n        \"clarinet\",\n        \"oboe\",\n        \"drums\"\n      ]\n    }\n  ]\n}","section_id":"find-people-within-a-given-age-range"},{"id":"find-people-in-a-given-city-with-a-specific-skill-ex0","language":"py","code":"people = Person.find(\n      (Person.skills << desired_skill) &\n      (Person.address.city == city)\n  ).all()","section_id":"find-people-in-a-given-city-with-a-specific-skill"},{"id":"find-people-in-a-given-city-with-a-specific-skill-ex1","language":"bash","code":"curl --location --request GET 'http://127.0.0.1:5000/people/byskill/guitar/Sheffield'","section_id":"find-people-in-a-given-city-with-a-specific-skill"},{"id":"find-people-in-a-given-city-with-a-specific-skill-ex2","language":"json","code":"{\n  \"results\": [\n    {\n      \"address\": {\n        \"city\": \"Sheffield\",\n        \"country\": \"United Kingdom\",\n        \"pk\": \"01FX8RMR7THMGA84RH8ZRQRRP9\",\n        \"postal_code\": \"S1 5RE\",\n        \"state\": \"South Yorkshire\",\n        \"street_name\": \"The Beltway\",\n        \"street_number\": 1,\n        \"unit\": \"A\"\n      },\n      \"age\": 28,\n      \"first_name\": \"Kareem\",\n      \"last_name\": \"Khan\",\n      \"personal_statement\": \"I'm Kareem, a multi-instrumentalist and singer looking to join a new rock band.\",\n      \"pk\": \"01FX8RMR7T60ANQTS4P9NKPKX8\",\n      \"skills\": [\n        \"drums\",\n        \"guitar\",\n        \"synths\"\n      ]\n    },\n    {\n      \"address\": {\n        \"city\": \"Sheffield\",\n        \"country\": \"United Kingdom\",\n        \"pk\": \"01FX8RMR7NW221STN6NVRDPEDT\",\n        \"postal_code\": \"S12 2MX\",\n        \"state\": \"South Yorkshire\",\n        \"street_name\": \"Main Street\",\n        \"street_number\": 9,\n        \"unit\": null\n      },\n      \"age\": 35,\n      \"first_name\": \"Robert\",\n      \"last_name\": \"McDonald\",\n      \"personal_statement\": \"My name is Robert, I love meeting new people and enjoy music, coding and walking my dog.\",\n      \"pk\": \"01FX8RMR7NRS45PBT3XP9KNAZH\",\n      \"skills\": [\n        \"guitar\",\n        \"piano\",\n        \"trombone\"\n      ]\n    }\n  ]\n}","section_id":"find-people-in-a-given-city-with-a-specific-skill"},{"id":"find-people-using-full-text-search-on-their-personal-statements-ex0","language":"py","code":"Person.find(Person.personal_statement % search_term).all()","section_id":"find-people-using-full-text-search-on-their-personal-statements"},{"id":"find-people-using-full-text-search-on-their-personal-statements-ex1","language":"bash","code":"curl --location --request GET 'http://127.0.0.1:5000/people/bystatement/play'","section_id":"find-people-using-full-text-search-on-their-personal-statements"},{"id":"find-people-using-full-text-search-on-their-personal-statements-ex2","language":"json","code":"{\n  \"results\": [\n    { \n      \"address\": {\n        \"city\": \"San Diego\",\n        \"country\": \"United States\",\n        \"pk\": \"01FX8RMR7YCDAVSWBMWCH2B07G\",\n        \"postal_code\": \"92102\",\n        \"state\": \"California\",\n        \"street_name\": \"C Street\",\n        \"street_number\": 1299,\n        \"unit\": null\n      },\n      \"age\": 43,\n      \"first_name\": \"Fernando\",\n      \"last_name\": \"Ortega\",\n      \"personal_statement\": \"I'm in a really cool band that plays a lot of cover songs.  I'm the drummer!\",\n      \"pk\": \"01FX8RMR7YB283BPZ88HAG066P\",\n      \"skills\": [\n        \"clarinet\",\n        \"oboe\",\n        \"drums\"\n      ]\n    }, {\n      \"address\": {\n        \"city\": \"Nottingham\",\n        \"country\": \"United Kingdom\",\n        \"pk\": \"01FX8RMR82DDJ90CW8D1GM68YZ\",\n        \"postal_code\": \"NG1 1AA\",\n        \"state\": \"Nottinghamshire\",\n        \"street_name\": \"Broadway\",\n        \"street_number\": 12,\n        \"unit\": \"A-1\"\n      },\n      \"age\": 37,\n      \"first_name\": \"Noor\",\n      \"last_name\": \"Vasan\",\n      \"personal_statement\": \"I sing and play the guitar, I enjoy touring and meeting new people on the road.\",\n      \"pk\": \"01FX8RMR82D091TC37B45RCWY3\",\n      \"skills\": [\n        \"vocals\",\n        \"guitar\"\n      ]\n    },\n    {\n      \"address\": {\n        \"city\": \"Birmingham\",\n        \"country\": \"United Kingdom\",\n        \"pk\": \"01FX8SSSDNRDSRB3HMVH00NQTT\",\n        \"postal_code\": \"B91 6HG\",\n        \"state\": \"West Midlands\",\n        \"street_name\": \"The Rushes\",\n        \"street_number\": 56,\n        \"unit\": null\n      },\n      \"age\": 36,\n      \"first_name\": \"Joanne\",\n      \"last_name\": \"Peel\",\n      \"personal_statement\": \"Music is my life, I love gigging and playing with my band.\",\n      \"pk\": \"01FX8SSSDN7PT9T3N0JZZA758G\",\n      \"skills\": [\n        \"synths\",\n        \"vocals\",\n        \"guitar\"\n      ]\n    }\n  ]\n}","section_id":"find-people-using-full-text-search-on-their-personal-statements"},{"id":"update-a-persons-age-ex0","language":"py","code":"try:\n      person = Person.get(id)\n\n  except NotFoundError:\n      return \"Bad request\", 400","section_id":"update-a-persons-age"},{"id":"update-a-persons-age-ex1","language":"py","code":"person.age = new_age\n  person.save()","section_id":"update-a-persons-age"},{"id":"update-a-persons-age-ex2","language":"bash","code":"curl --location --request POST 'http://127.0.0.1:5000/person/01FX8RMR7T60ANQTS4P9NKPKX8/age/28'","section_id":"update-a-persons-age"},{"id":"delete-a-person-ex0","language":"py","code":"Person.delete(id)","section_id":"delete-a-person"},{"id":"delete-a-person-ex1","language":"bash","code":"curl --location --request POST 'http://127.0.0.1:5000/person/01FX8RMR8545RWW4DYCE5MSZA1/delete'","section_id":"delete-a-person"},{"id":"setting-an-expiry-time-for-a-person-ex0","language":"py","code":"person_to_expire = Person.get(id)\n  Person.db().expire(person_to_expire.key(), seconds)","section_id":"setting-an-expiry-time-for-a-person"},{"id":"setting-an-expiry-time-for-a-person-ex1","language":"bash","code":"curl --location --request POST 'http://localhost:5000/person/01FX8RMR82D091TC37B45RCWY3/expire/600'","section_id":"setting-an-expiry-time-for-a-person"},{"id":"setting-an-expiry-time-for-a-person-ex2","language":"bash","code":"127.0.0.1:6379> ttl :person.Person:01FX8RMR82D091TC37B45RCWY3\n(integer) 584","section_id":"setting-an-expiry-time-for-a-person"},{"id":"shutting-down-redis-docker-ex0","language":"bash","code":"$ docker-compose down\nStopping redis_om_python_flask_starter ... done\nRemoving redis_om_python_flask_starter ... done\nRemoving network redis-om-python-flask-skeleton-app_default","section_id":"shutting-down-redis-docker"}]}
{"schema_version":2,"id":"integrate/redisvl","title":"RedisVL","url":"https://redis.io/docs/latest/integrate/redisvl/","summary":"This is the Redis vector library (RedisVL).","aliases":["/integrate/redisvl/api","/integrate/redisvl/api/cache","/integrate/redisvl/api/filter","/integrate/redisvl/api/query","/integrate/redisvl/api/schema","/integrate/redisvl/api/searchindex","/integrate/redisvl/api/vectorizer","/integrate/redisvl/overview","/integrate/redisvl/overview/cli","/integrate/redisvl/user-guide","/integrate/redisvl/user-guide/get-started","/integrate/redisvl/user-guide/json-v-hashes","/integrate/redisvl/user-guide/query-filter","/integrate/redisvl/user-guide/semantic-caching","/integrate/redisvl/user-guide/vectorizers"],"tags":["docs","integrate","stack","oss","rs","rc","oss","clients"],"last_updated":"2026-09-17T14:55:03+01:00","children":[],"page_type":"content","content_hash":"9ea80602a605280c11294de435e57e9a375c83b99804aa3fea5e3a2107e8e56a","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"RedisVL provides a powerful, dedicated Python client library for using Redis as a vector database. Leverage Redis's speed, reliability, and vector-based semantic search capabilities to supercharge your application."},{"id":"overview","title":"Overview","role":"overview","text":"RedisVL (Redis Vector Library) is a Python client library specifically designed for building AI applications with Redis as a vector database. It provides high-level abstractions for vector search, semantic caching, and AI-powered applications while leveraging Redis's performance and reliability."},{"id":"key-features","title":"Key Features","role":"content","text":"- **Vector Search**: High-performance similarity search with multiple distance metrics\n- **Semantic Caching**: Intelligent caching for AI model responses and embeddings\n- **Schema Management**: Declarative schema definition for vector indexes\n- **Multiple Vectorizers**: Built-in support for OpenAI, Hugging Face, and custom embeddings\n- **Query Filtering**: Advanced filtering capabilities for precise search results\n- **Real-time Updates**: Live vector index updates and real-time search\n- **Python Integration**: Native Python API with pandas and NumPy compatibility\n- **Production Ready**: Enterprise-grade performance and reliability with Redis"},{"id":"getting-started","title":"Getting Started","role":"setup","text":"Refer to the complete [RedisVL documentation](https://redis.io/docs/latest/develop/ai/redisvl) for installation, setup, and usage examples."}],"examples":[]}
{"schema_version":2,"id":"integrate/riot/docs","title":"Documentation","url":"https://redis.io/docs/latest/integrate/riot/docs/","summary":"RIOT-X documentation","tags":[],"last_updated":"2026-04-08T06:23:12-07:00","page_type":"content","content_hash":"c07836c3df7dd96358594f882fabdb424dd68ac1ee461feb768610008008b493","sections":[{"id":"content","title":"Content","role":"content","text":"Full documentation for RIOT-X is available at [redis.github.io/riotx](https://redis.github.io/riotx/)."}],"examples":[]}
{"schema_version":2,"id":"integrate/riot","title":"RIOT-X","url":"https://redis.io/docs/latest/integrate/riot/","summary":"Redis Input/Output Tools","tags":["docs","integrate","stack","oss","rs","rc","oss"],"last_updated":"2026-04-08T06:23:12-07:00","children":[{"id":"integrate/riot/install","summary":"Install RIOT-X on macOS, Linux, Windows, and Docker","title":"Install","url":"https://redis.io/docs/latest/integrate/riot/install/"},{"id":"integrate/riot/quick-start","summary":"RIOT-X getting started guide","title":"Getting started","url":"https://redis.io/docs/latest/integrate/riot/quick-start/"},{"id":"integrate/riot/docs","summary":"RIOT-X documentation","title":"Documentation","url":"https://redis.io/docs/latest/integrate/riot/docs/"}],"page_type":"content","content_hash":"af95a49d7bbaa7eb44dd4298034c99897eab2970c7431d93aad959c6f3e74930","sections":[{"id":"content","title":"Content","role":"content","text":"Redis Input/Output Tools (RIOT-X) is a command-line utility designed to help you get data in and out of Redis.\n\nRIOT-X supports many different sources and targets:\n\n* [Redis](https://redis.github.io/riotx/replication/overview.html) (snapshot and live replication)\n* [Files](https://redis.github.io/riotx/files.html) (CSV, JSON, XML, and others)\n* [Relational databases](https://redis.github.io/riotx/databases/drivers.html) (MySQL, Snowflake, Oracle, and others)\n* [Data generators](https://redis.github.io/riotx/generators/generate.html) (Redis data structures and Faker)\n\nFull documentation is available at [redis.github.io/riotx](https://redis.github.io/riotx/)."}],"examples":[]}
{"schema_version":2,"id":"integrate/riot/install","title":"Install","url":"https://redis.io/docs/latest/integrate/riot/install/","summary":"Install RIOT-X on macOS, Linux, Windows, and Docker","tags":[],"last_updated":"2026-04-08T06:23:12-07:00","page_type":"content","content_hash":"4cb6447d77b90536109fa5b01f7a02954d7009b3baf5f73b38df7c13637b2fcc","sections":[{"id":"overview","title":"Overview","role":"overview","text":"RIOT-X can be installed in different ways depending on your environment and preference."},{"id":"macos-and-linux-via-homebrew","title":"macOS and Linux via Homebrew","role":"content","text":"[code example]"},{"id":"windows-via-scoop","title":"Windows via Scoop","role":"content","text":"[code example]"},{"id":"docker","title":"Docker","role":"content","text":"[code example]"},{"id":"manual-installation-on-all-supported-platforms","title":"Manual installation on all supported platforms","role":"content","text":"Download the pre-compiled binary from [RIOT-X Releases](https://github.com/redis/riotx-dist/releases), uncompress, and copy to the desired location.\n\nFull installation documentation is available [here](https://redis.github.io/riotx/quick-start/install.html)."}],"examples":[{"id":"macos-and-linux-via-homebrew-ex0","language":"plaintext","code":"brew install redis/tap/riotx","section_id":"macos-and-linux-via-homebrew"},{"id":"windows-via-scoop-ex0","language":"plaintext","code":"scoop bucket add redis https://github.com/redis/scoop.git\nscoop install riotx","section_id":"windows-via-scoop"},{"id":"docker-ex0","language":"plaintext","code":"docker run riotx/riotx [OPTIONS] [COMMAND]","section_id":"docker"}]}
{"schema_version":2,"id":"integrate/riot/quick-start","title":"Getting started","url":"https://redis.io/docs/latest/integrate/riot/quick-start/","summary":"RIOT-X getting started guide","tags":[],"last_updated":"2026-09-17T14:54:53+01:00","page_type":"content","content_hash":"8459eea6151e58d10e1f7e8d19d35d999be25c11cc17d9e0603b335296f2dc1b","sections":[{"id":"content","title":"Content","role":"content","text":"You can launch RIOT-X with the following command:\n\n[code example]\n\nThis will show usage help, which you can also get by running:\n\n[code example]\n\nUsage help is available on any command and subcommand:\n\n[code example]\n\nRedis Command Help\n\n[code example]\n\n> [!TIP]\n> Run the following command to give riotx TAB completion in the current shell:\n>\n> `source <(riotx generate-completion)`\n\nFull documentation is available at [redis.github.io/riotx](https://redis.github.io/riotx/)."}],"examples":[{"id":"content-ex0","language":"plaintext","code":"riotx","section_id":"content"},{"id":"content-ex1","language":"plaintext","code":"riotx --help","section_id":"content"},{"id":"content-ex2","language":"plaintext","code":"riotx COMMAND --help","section_id":"content"},{"id":"content-ex3","language":"plaintext","code":"riotx file-import file.json json.set --help","section_id":"content"}]}
{"schema_version":2,"id":"integrate/rust-redis","title":"Rust client for Redis","url":"https://redis.io/docs/latest/integrate/rust-redis/","summary":"Learn how to build with Redis and Rust","tags":["docs","integrate","oss","rs","rc"],"last_updated":"2025-09-23T14:03:20-07:00","children":[],"page_type":"content","content_hash":"2e717adaca3dccbef14c6491baf95fb8335a72040c05d9855b92f6fafa5d6b45","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"Connect your Rust application to a Redis database using the redis-rs client library."},{"id":"overview","title":"Overview","role":"overview","text":"redis-rs is a high-performance Redis client for Rust that leverages Rust's memory safety and performance characteristics. It provides both synchronous and asynchronous APIs with excellent integration into the Rust ecosystem."},{"id":"key-features","title":"Key Features","role":"content","text":"- **Memory Safety**: Rust's ownership system prevents memory leaks and data races\n- **High Performance**: Zero-cost abstractions and efficient memory usage\n- **Async Support**: Tokio integration for asynchronous, non-blocking operations\n- **Type Safety**: Strong typing with compile-time error checking\n- **Connection Pooling**: Efficient connection management for concurrent applications\n- **Pipeline Support**: Command batching for improved throughput\n- **Cluster Support**: Redis Cluster client for distributed setups\n- **Comprehensive API**: Full Redis command coverage with idiomatic Rust interfaces"},{"id":"getting-started","title":"Getting Started","role":"setup","text":"Refer to the [Rust Redis documentation](https://docs.rs/redis/latest/redis/) to install, connect, and use redis-rs."}],"examples":[]}
{"schema_version":2,"id":"integrate/spring-framework-cache/cache","title":"Use Redis with the Spring cache abstraction","url":"https://redis.io/docs/latest/integrate/spring-framework-cache/cache/","summary":"Plug Redis into your Spring application as a cache with minimal effort","tags":["docs","integrate","stack","oss","rs","rc","oss","client"],"last_updated":"2026-07-07T11:17:35+01:00","page_type":"content","content_hash":"0b29104491c68ea5b698f4e6916cfede6869f4be651d3669457e219fee7300db","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Spring Data Redis implements the Spring framework's cache abstraction for Redis, which allows you to plug Redis into your Spring application with minimal effort.\n\nSpring's cache abstraction applies cache-aside to methods, reducing executions by storing and reusing results. When a method is invoked, the abstraction checks if it's been called with the same arguments before. If so, it returns the cached result. If not, it invokes the method, caches the result, and returns it. This way, costly methods are invoked less often. Further details are in the [Spring cache abstraction documentation](https://docs.spring.io/spring-framework/reference/integration/cache.html)."},{"id":"get-started","title":"Get started","role":"content","text":"In a nutshell, you need to perform the following steps to use Redis as your cache storage:\n\n1. [Configure the cache storage](https://docs.spring.io/spring-framework/reference/integration/cache/store-configuration.html) by using the [Redis cache manager](https://docs.spring.io/spring-data/redis/reference/redis/redis-cache.html) that is part of Spring Data.\n2. Annotate a repository with your `@CacheConfig`.\n3. Use the `@Cacheable` annotation on a repository method to cache the results of that method.\n\nHere is an example:\n\n[code example]"},{"id":"further-readings","title":"Further readings","role":"content","text":"Please read the Spring framework's documentation to learn more about how to use the Redis cache abstraction for Spring:\n\n* [Spring cache abstraction](https://docs.spring.io/spring-framework/reference/integration/cache.html)\n* [Spring cache store configuration](https://docs.spring.io/spring-framework/reference/integration/cache/store-configuration.html)\n* [Spring Data Redis Cache](https://docs.spring.io/spring-data/redis/reference/redis/redis-cache.html)"}],"examples":[{"id":"get-started-ex0","language":"java","code":"@CacheConfig(\"books\")\npublic class BookRepositoryImpl implements BookRepository {\n\n    @Cacheable\n    public Book findBook(ISBN isbn) {...}\n}","section_id":"get-started"}]}
{"schema_version":2,"id":"integrate/spring-framework-cache/geo-failover","title":"Client-side geographic failover","url":"https://redis.io/docs/latest/integrate/spring-framework-cache/geo-failover/","summary":"Configure resilient, failover-aware Redis connections in a Spring Data Redis application.","tags":["docs","integrate","stack","oss","rs","rc","oss","client"],"last_updated":"2026-09-17T14:54:53+01:00","page_type":"content","content_hash":"3cbe6a9c4a1a011978c19842f6b8e951ff675e7043bb68a9301810d1f8b46970","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Client-side geographic failover](https://redis.io/docs/latest/develop/clients/failover)\nimproves the availability of your connections to Redis: if the endpoint your\napplication is using fails or becomes too slow, the client automatically switches\nto another healthy endpoint, and then fails back when the preferred endpoint\nrecovers. See [Client-side geographic failover](https://redis.io/docs/latest/develop/clients/failover)\nfor an overview of the concepts, including failover, failback, and health checks.\n\nSpring Data Redis does not yet expose this feature through its own configuration.\nHowever, Spring Data Redis works with both the [Lettuce](https://redis.io/docs/latest/develop/clients/lettuce)\nand [Jedis](https://redis.io/docs/latest/develop/clients/jedis) clients, and both\nprovide a `MultiDbClient` failover API. You can therefore use failover as a workaround:\nconfigure a failover-aware client as a Spring bean, then use it throughout your\napplication. This page shows how to do this with either client. Select the tab for\nthe client you use."},{"id":"requirements","title":"Requirements","role":"content","text":"- [Spring Data Redis 4.1.x](https://github.com/spring-projects/spring-data-redis/releases/tag/4.1.0) or later.\n- Two or more Redis endpoints that can serve your application. These can be two standalone Redis servers or two [Active-Active](https://redis.io/docs/latest/operate/rs/databases/active-active) database endpoints in different locations.\n- To use failover with **Lettuce**, ensure your project uses a Lettuce version that provides the failover API (`io.lettuce.core.failover`). Spring Data Redis pulls in Lettuce transitively, so you may need to override the managed Lettuce version to one that includes it.\n- To use failover with **Jedis**, use Jedis 7 or later (which provides `MultiDbClient`) and add the `resilience4j` dependencies. See the [Jedis client-side geographic failover](https://redis.io/docs/latest/develop/clients/jedis/failover) page for the full dependency list."},{"id":"configure-a-failover-aware-client","title":"Configure a failover-aware client","role":"configuration","text":"Define a Spring [configuration class](https://docs.spring.io/spring-framework/reference/core/beans/java/configuration-annotation.html)\nthat creates a `MultiDbClient` with one entry for each endpoint and exposes it as a\nbean. The `weight` orders the endpoints: the client tries the highest-weighted\nhealthy endpoint first and uses lower-weighted endpoints only while the preferred\none is unhealthy.\n\n**Lettuce:**\n\n[code example]\n\nWith Lettuce, the `MultiDbClient` produces a `StatefulRedisMultiDbConnection`, which\noffers the same synchronous, asynchronous, and reactive APIs as an ordinary Lettuce\nconnection.\n\n**Jedis:**\n\n[code example]\n\nWith Jedis, `MultiDbClient` implements the standard Redis commands directly (like\n`RedisClient`) and handles connection management and failover transparently. See the\n[Jedis client-side geographic failover](https://redis.io/docs/latest/develop/clients/jedis/failover)\npage for the required `resilience4j` dependencies and the full set of configuration\noptions.\n\n\n\nThe examples above hard-code the endpoints and weights for clarity. In a real\napplication, you would typically supply them from an external property source,\nsuch as `application.yaml`, using\n[Spring Boot configuration properties](https://docs.spring.io/spring-boot/reference/features/external-config.html)."},{"id":"use-the-client","title":"Use the client","role":"content","text":"Inject the failover-aware bean wherever you need it. Your code does not need to know\nabout the failover behavior: every command runs against the endpoint that the client\ncurrently considers healthy, so if the active endpoint fails, the next command is\nserved by another endpoint without any change to your code.\n\n**Lettuce:**\n\n[code example]\n\n**Jedis:**\n\n[code example]\n\n\n\nThis approach wires the failover-aware client (or its connection) directly as a bean,\nrather than through Spring Data Redis's `RedisConnectionFactory` and `RedisTemplate`.\nAny component that issues commands through this bean is routed to the currently healthy\nendpoint. Note that, by default, a command already in flight when a failover occurs may\nfail rather than being retried on the new endpoint; see the client-specific pages below\nfor the retry options."},{"id":"observe-failover","title":"Observe failover","role":"content","text":"To make failover transitions visible, register a listener that runs on each switch.\n\n**Lettuce:**\n\nSubscribe to Lettuce's event bus when you create the client. The `DatabaseSwitchEvent`\nfires each time the client switches endpoint, and the `AllDatabasesUnhealthyEvent`\nfires when no endpoint is available. The example below logs these events, so declare\nan SLF4J logger field on the configuration class:\n\n[code example]\n\nThen subscribe to the events in the `failoverClient()` bean method, before returning\nthe client:\n\n[code example]\n\n**Jedis:**\n\nRegister a `databaseSwitchListener` on the `MultiDbClient` builder. The example below\nlogs each switch, so declare an SLF4J logger field on the configuration class:\n\n[code example]\n\nThen add the listener when building the client in the `failoverClient()` bean method:\n\n[code example]"},{"id":"health-checks-and-failback","title":"Health checks and failback","role":"content","text":"The examples above use the default health check strategy (`PingStrategy`), which\nperiodically sends a [`PING`](https://redis.io/docs/latest/commands/ping) command to each\nendpoint. Both clients also support a lag-aware strategy for Active-Active databases\nand custom strategies, and both enable failback to the higher-weighted endpoint by\ndefault. For the full set of failover, health check, and failback options, see the\nclient-specific documentation:\n\n- [Lettuce client-side geographic failover](https://redis.io/docs/latest/develop/clients/lettuce/failover)\n- [Jedis client-side geographic failover](https://redis.io/docs/latest/develop/clients/jedis/failover)"}],"examples":[{"id":"configure-a-failover-aware-client-ex0","language":"java","code":"import io.lettuce.core.RedisURI;\nimport io.lettuce.core.failover.MultiDbClient;\nimport io.lettuce.core.failover.api.DatabaseConfig;\nimport io.lettuce.core.failover.api.InitializationPolicy;\nimport io.lettuce.core.failover.api.MultiDbOptions;\nimport io.lettuce.core.failover.api.StatefulRedisMultiDbConnection;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport java.util.List;\n\n@Configuration\npublic class MultiDbCacheConfig {\n\n    // The higher weight is tried first; the lower-weighted node is used\n    // only while the primary is unhealthy.\n    private static final String PRIMARY_URI = \"redis://localhost:6379\";\n    private static final float PRIMARY_WEIGHT = 1.0f;\n\n    private static final String SECONDARY_URI = \"redis://localhost:6380\";\n    private static final float SECONDARY_WEIGHT = 0.5f;\n\n    @Bean(destroyMethod = \"shutdown\")\n    public MultiDbClient failoverClient() {\n        // No healthCheckStrategySupplier() is set, so the default PingStrategy\n        // is used: a periodic PING with the library defaults.\n        DatabaseConfig primary   = DatabaseConfig.builder(RedisURI.create(PRIMARY_URI)).weight(PRIMARY_WEIGHT).build();\n        DatabaseConfig secondary = DatabaseConfig.builder(RedisURI.create(SECONDARY_URI)).weight(SECONDARY_WEIGHT).build();\n\n        // ONE_AVAILABLE lets the client start as long as at least one node is\n        // up. Failback to the higher-weighted node is enabled by default.\n        MultiDbOptions options = MultiDbOptions.builder()\n                .initializationPolicy(InitializationPolicy.BuiltIn.ONE_AVAILABLE)\n                .build();\n\n        MultiDbClient client = MultiDbClient.create(List.of(primary, secondary), options);\n        return client;\n    }\n\n    @Bean(destroyMethod = \"close\")\n    public StatefulRedisMultiDbConnection<String, String> failoverConnection(MultiDbClient failoverClient) {\n        // Same surface as a normal StatefulRedisConnection (sync/async/reactive),\n        // but routing and failover are handled inside the connection.\n        return failoverClient.connect();\n    }\n}","section_id":"configure-a-failover-aware-client"},{"id":"configure-a-failover-aware-client-ex1","language":"java","code":"import redis.clients.jedis.DefaultJedisClientConfig;\nimport redis.clients.jedis.HostAndPort;\nimport redis.clients.jedis.JedisClientConfig;\nimport redis.clients.jedis.MultiDbClient;\nimport redis.clients.jedis.MultiDbConfig;\nimport redis.clients.jedis.MultiDbConfig.DatabaseConfig;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class MultiDbCacheConfig {\n\n    @Bean(destroyMethod = \"close\")\n    public MultiDbClient failoverClient() {\n        JedisClientConfig config = DefaultJedisClientConfig.builder().build();\n\n        HostAndPort primary = new HostAndPort(\"localhost\", 6379);\n        HostAndPort secondary = new HostAndPort(\"localhost\", 6380);\n\n        // The higher weight is tried first; the lower-weighted node is used\n        // only while the primary is unhealthy. The default PingStrategy health\n        // check is used, and failback is enabled by default.\n        MultiDbConfig multiConfig = MultiDbConfig.builder()\n                .database(DatabaseConfig.builder(primary, config).weight(1.0f).build())\n                .database(DatabaseConfig.builder(secondary, config).weight(0.5f).build())\n                .build();\n\n        return MultiDbClient.builder()\n                .multiDbConfig(multiConfig)\n                .build();\n    }\n}","section_id":"configure-a-failover-aware-client"},{"id":"use-the-client-ex0","language":"java","code":"import io.lettuce.core.SetArgs;\nimport io.lettuce.core.api.sync.RedisCommands;\nimport io.lettuce.core.failover.api.StatefulRedisMultiDbConnection;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class CacheController {\n\n    private final RedisCommands<String, String> redis;\n\n    public CacheController(StatefulRedisMultiDbConnection<String, String> connection) {\n        // .sync() is the same API as on a plain StatefulRedisConnection;\n        // the multi-db connection just transparently routes the command.\n        this.redis = connection.sync();\n    }\n\n    @GetMapping(\"/api/cache/{key}\")\n    public String get(@PathVariable String key) {\n        String value = redis.get(key);          // runs against the active endpoint\n        if (value == null) {\n            value = expensiveLookup(key);        // compute the value on a miss\n            redis.set(key, value, SetArgs.Builder.ex(60));   // SET with a 60s TTL\n        }\n        return value;\n    }\n\n    private static String expensiveLookup(String key) {\n        // ... your application logic ...\n        return \"value-for-\" + key;\n    }\n}","section_id":"use-the-client"},{"id":"use-the-client-ex1","language":"java","code":"import redis.clients.jedis.MultiDbClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class CacheController {\n\n    private final MultiDbClient redis;\n\n    public CacheController(MultiDbClient redis) {\n        // MultiDbClient implements the standard Redis commands, like RedisClient,\n        // and routes each command to the currently healthy endpoint.\n        this.redis = redis;\n    }\n\n    @GetMapping(\"/api/cache/{key}\")\n    public String get(@PathVariable String key) {\n        String value = redis.get(key);          // runs against the active endpoint\n        if (value == null) {\n            value = expensiveLookup(key);        // compute the value on a miss\n            redis.setex(key, 60, value);         // SET with a 60s TTL\n        }\n        return value;\n    }\n\n    private static String expensiveLookup(String key) {\n        // ... your application logic ...\n        return \"value-for-\" + key;\n    }\n}","section_id":"use-the-client"},{"id":"observe-failover-ex0","language":"java","code":"private static final Logger log = LoggerFactory.getLogger(MultiDbCacheConfig.class);","section_id":"observe-failover"},{"id":"observe-failover-ex1","language":"java","code":"import io.lettuce.core.failover.event.AllDatabasesUnhealthyEvent;\nimport io.lettuce.core.failover.event.DatabaseSwitchEvent;\n\nclient.getResources().eventBus().get()\n        .filter(e -> e instanceof DatabaseSwitchEvent)\n        .cast(DatabaseSwitchEvent.class)\n        .subscribe(e -> log.warn(\"Redis failover: {} -> {} (reason={})\",\n                e.getFromDb(), e.getToDb(), e.getReason()));\n\nclient.getResources().eventBus().get()\n        .filter(e -> e instanceof AllDatabasesUnhealthyEvent)\n        .cast(AllDatabasesUnhealthyEvent.class)\n        .subscribe(e -> log.error(\"All Redis nodes unhealthy after {} attempts: {}\",\n                e.getFailedAttempts(), e.getUnhealthyDatabases()));","section_id":"observe-failover"},{"id":"observe-failover-ex2","language":"java","code":"private static final Logger log = LoggerFactory.getLogger(MultiDbCacheConfig.class);","section_id":"observe-failover"},{"id":"observe-failover-ex3","language":"java","code":"return MultiDbClient.builder()\n        .multiDbConfig(multiConfig)\n        .databaseSwitchListener(event ->\n                log.warn(\"Redis failover: switched to {} (reason={})\",\n                        event.getDatabaseName(), event.getReason()))\n        .build();","section_id":"observe-failover"}]}
{"schema_version":2,"id":"integrate/spring-framework-cache","title":"Spring Data Redis","url":"https://redis.io/docs/latest/integrate/spring-framework-cache/","summary":"Plug Redis into your Spring application with minimal effort","tags":["docs","integrate","stack","oss","rs","rc","oss","client"],"last_updated":"2026-09-17T14:54:53+01:00","children":[{"id":"integrate/spring-framework-cache/cache","summary":"Plug Redis into your Spring application as a cache with minimal effort","title":"Use Redis with the Spring cache abstraction","url":"https://redis.io/docs/latest/integrate/spring-framework-cache/cache/"},{"id":"integrate/spring-framework-cache/geo-failover","summary":"Configure resilient, failover-aware Redis connections in a Spring Data Redis application.","title":"Client-side geographic failover","url":"https://redis.io/docs/latest/integrate/spring-framework-cache/geo-failover/"}],"page_type":"content","content_hash":"adf3fbd8e3880729936058e2957c2d024b6d23c5650fe7096904c5b3909a6c23","sections":[{"id":"content","title":"Content","role":"content","text":"[Spring Data Redis](https://spring.io/projects/spring-data-redis) integrates Redis with the [Spring framework](https://spring.io/projects/spring-framework), letting you plug Redis into your Spring application with minimal effort. It works with the [Lettuce](https://redis.io/docs/latest/develop/clients/lettuce) and [Jedis](https://redis.io/docs/latest/develop/clients/jedis) clients, so Spring applications can take advantage of those clients' connection features as well as Spring's own abstractions.\n\nThe pages in this section describe recipes for using Redis from Spring Data Redis:\n\n- [Use Redis with the Spring cache abstraction](https://redis.io/docs/latest/integrate/spring-framework-cache/cache) shows how to use Redis as the storage for Spring's cache abstraction.\n- [Client-side geographic failover](https://redis.io/docs/latest/integrate/spring-framework-cache/geo-failover) shows how to configure resilient connections that automatically fail over between Redis endpoints."}],"examples":[]}
{"schema_version":2,"id":"integrate/terraform-provider-for-redis-cloud/get-started","title":"Get started with Terraform","url":"https://redis.io/docs/latest/integrate/terraform-provider-for-redis-cloud/get-started/","summary":"Shows how to install the Redis Cloud provider and create a subscription.","tags":["docs","integrate","rc"],"last_updated":"2026-09-17T14:54:42+01:00","page_type":"content","content_hash":"fa64a72bac2010b2d899f0db23238aabcdfb5199d27be17148157f54cb9027b4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Here, you'll learn how to use the [Redis Cloud Terraform Provider](https://redis.io/docs/latest/integrate/terraform-provider-for-redis-cloud) to create a subscription and a database."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"1. [Install Terraform](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli).\n\n1. [Create a Redis Cloud account](https://redis.io/docs/latest/operate/rc/rc-quickstart#create-an-account) if you do not have one already.\n\n1. [Enable the Redis Cloud API](https://redis.io/docs/latest/operate/rc/api/get-started/enable-the-api).\n\n1. Get your Redis Cloud [API keys](https://redis.io/docs/latest/operate/rc/api/get-started/manage-api-keys). Set them to the following environment variables:\n\n    - Set `REDISCLOUD_ACCESS_KEY` to your API account key.\n    - Set `REDISCLOUD_SECRET_KEY` to your API user key.\n\n1. Set a [payment method](https://redis.io/docs/latest/operate/rc/billing-and-payments#add-payment-method)."},{"id":"install-the-redis-cloud-provider","title":"Install the Redis Cloud provider","role":"setup","text":"1. Create a file to contain the Terraform configuration called `main.tf`.\n\n1. Go to the [Redis Cloud Terraform Registry](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/).\n\n1. Select **Use Provider** and copy the Terraform code located there. Paste the code into `main.tf` and save the file.\n\n   [code example]\n   \n1. Run `terraform init`."},{"id":"create-a-redis-cloud-subscription-with-terraform","title":"Create a Redis Cloud subscription with Terraform","role":"content","text":"In your Terraform configuration file, you can add resources and data sources to plan and create subscriptions and databases. See the [Redis Cloud Terraform Registry documentation](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs) for more info about the resources and data sources you can use as part of the Redis Cloud provider.\n\nThe steps in this section show you how to plan and create a Redis Cloud Pro subscription with one database.\n\n1. Use the [`rediscloud_payment_method`](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/data-sources/rediscloud_payment_method) data source to get the payment method ID.\n\n    [code example]\n   \n   Example:\n\n   [code example]\n   \n1. Define a [`rediscloud_subscription`](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/resources/rediscloud_subscription) resource to create the subscription.\n\n    [code example]\n\n   Example:\n\n   [code example]\n\n1. Define a [`rediscloud_subscription_database`](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/resources/rediscloud_subscription_database) resource to create a database.\n\n    [code example]\n   \n   Example:\n\n   [code example]\n\n2. Run `terraform plan` to check for any syntax errors.\n\n    [code example]\n\n3. Run `terraform apply` to apply the changes and enter `yes` to confirm when prompted.\n\n    This will take some time. You will see messages in your terminal while the subscription and database are being created:\n\n   [code example]\n\n   When provisioning is complete, you will see a message in your terminal:   \n\n   [code example]\n\n   View the [Redis Cloud console](https://cloud.redis.io/) to verify your subscription and database creation.\n\n4. If you want to remove these sample resources, run `terraform destroy`."},{"id":"more-info","title":"More info","role":"content","text":"- [Redis Cloud Terraform Registry](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs)\n- [Terraform documentation](https://developer.hashicorp.com/terraform/docs)\n- [Terraform configuration syntax](https://developer.hashicorp.com/terraform/language/syntax/configuration)"}],"examples":[{"id":"install-the-redis-cloud-provider-ex0","language":"text","code":"provider \"rediscloud\" {\n   }\n\n   # Example resource configuration\n   resource \"rediscloud_subscription\" \"example\" {\n      # ...\n   }","section_id":"install-the-redis-cloud-provider"},{"id":"create-a-redis-cloud-subscription-with-terraform-ex0","language":"text","code":"# Get credit card details\n    data \"rediscloud_payment_method\" \"card\" {\n        card_type = \"<Card type>\"\n        last_four_numbers = \"<Last four numbers on the card>\"\n    }","section_id":"create-a-redis-cloud-subscription-with-terraform"},{"id":"create-a-redis-cloud-subscription-with-terraform-ex1","language":"text","code":"data \"rediscloud_payment_method\" \"card\" {\n      card_type = \"Visa\"\n      last_four_numbers = \"5625\"\n   }","section_id":"create-a-redis-cloud-subscription-with-terraform"},{"id":"create-a-redis-cloud-subscription-with-terraform-ex2","language":"text","code":"# Create a subscription\n    resource \"rediscloud_subscription\" \"subscription-resource\" {\n            name = \"subscription-name\"\n            payment_method_id = data.rediscloud_payment_method.card.id # If you want to pay with a marketplace account, replace this line with payment_method = 'marketplace'.\n            memory_storage = \"ram\"\n\n            # Specify the cloud provider information here\n            cloud_provider {\n                    provider = \"<Cloud provider>\"\n                    region {\n                            region = \"<region>\"\n                            networking_deployment_cidr = \"<CIDR>\"\n                    }\n            }\n\n            #Define the average database specification for databases in the subscription\n            creation_plan {\n                    memory_limit_in_gb = 2\n                    quantity = 1\n                    replication = true\n                    throughput_measurement_by = \"operations-per-second\"\n                    throughput_measurement_value = 20000\n            }\n    }","section_id":"create-a-redis-cloud-subscription-with-terraform"},{"id":"create-a-redis-cloud-subscription-with-terraform-ex3","language":"text","code":"resource \"rediscloud_subscription\" \"subscription-resource\" {\n        name = \"redis-docs-sub\"\n        payment_method_id = data.rediscloud_payment_method.card.id # If you want to pay with a marketplace account, replace this line with payment_method = 'marketplace'.\n        memory_storage = \"ram\"\n\n        cloud_provider {\n                provider = \"GCP\"\n                region {\n                        region = \"us-west1\"\n                        networking_deployment_cidr = \"192.168.0.0/24\"\n                }\n        }\n\n        creation_plan {\n                memory_limit_in_gb = 2\n                quantity = 1\n                replication = true\n                throughput_measurement_by = \"operations-per-second\"\n                throughput_measurement_value = 20000\n                modules = [\"RedisJSON\"]\n        }\n   }","section_id":"create-a-redis-cloud-subscription-with-terraform"},{"id":"create-a-redis-cloud-subscription-with-terraform-ex4","language":"text","code":"# Create a Database\n    resource \"rediscloud_subscription_database\" \"database-resource\" {\n        subscription_id = rediscloud_subscription.subscription-resource.id\n        name = \"database-name\"\n        memory_limit_in_gb = 2\n        data_persistence = \"aof-every-write\"\n        throughput_measurement_by = \"operations-per-second\"\n        throughput_measurement_value = 20000\n        replication = true\n\n        alert {\n        name = \"dataset-size\"\n        value = 40\n        }\n        depends_on = [rediscloud_subscription.subscription-resource]\n\n    }","section_id":"create-a-redis-cloud-subscription-with-terraform"},{"id":"create-a-redis-cloud-subscription-with-terraform-ex5","language":"text","code":"resource \"rediscloud_subscription_database\" \"database-resource\" {\n       subscription_id = rediscloud_subscription.subscription-resource.id\n       name = \"redis-docs-db\"\n       memory_limit_in_gb = 2\n       data_persistence = \"aof-every-write\"\n       throughput_measurement_by = \"operations-per-second\"\n       throughput_measurement_value = 20000\n       replication = true\n\n       modules = [\n       {\n            name = \"RedisJSON\"\n       }\n       ]\n\n       alert {\n       name = \"dataset-size\"\n       value = 40\n       }\n      depends_on = [rediscloud_subscription.subscription-resource]\n\n   }","section_id":"create-a-redis-cloud-subscription-with-terraform"},{"id":"create-a-redis-cloud-subscription-with-terraform-ex6","language":"sh","code":"$ terraform plan\n    data.rediscloud_payment_method.card: Reading...\n    data.rediscloud_payment_method.card: Read complete after 1s [id=8859]\n\n    Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following\n    symbols:\n    + create\n\n    Terraform will perform the following actions:\n\n        # rediscloud_subscription.subscription-resource will be created\n        + resource \"rediscloud_subscription\" \"subscription-resource\" {\n            [...]\n        }\n\n        # rediscloud_subscription_database.database-resource will be created\n        + resource \"rediscloud_subscription_database\" \"database-resource\" {\n            [...]\n        }\n    \n    Plan: 2 to add, 0 to change, 0 to destroy.","section_id":"create-a-redis-cloud-subscription-with-terraform"},{"id":"create-a-redis-cloud-subscription-with-terraform-ex7","language":"text","code":"rediscloud_subscription.subscription-resource: Creating...\n   rediscloud_subscription.subscription-resource: Still creating... [10s elapsed]\n   rediscloud_subscription.subscription-resource: Still creating... [20s elapsed]\n   rediscloud_subscription.subscription-resource: Still creating... [30s elapsed]","section_id":"create-a-redis-cloud-subscription-with-terraform"},{"id":"create-a-redis-cloud-subscription-with-terraform-ex8","language":"text","code":"Apply complete! Resources: 2 added, 0 changed, 0 destroyed.","section_id":"create-a-redis-cloud-subscription-with-terraform"}]}
{"schema_version":2,"id":"integrate/terraform-provider-for-redis-cloud","title":"Terraform provider for Redis Cloud","url":"https://redis.io/docs/latest/integrate/terraform-provider-for-redis-cloud/","summary":"","tags":["docs","integrate","rc"],"last_updated":"2026-09-17T14:54:42+01:00","children":[{"id":"integrate/terraform-provider-for-redis-cloud/get-started","summary":"Shows how to install the Redis Cloud provider and create a subscription.","title":"Get started with Terraform","url":"https://redis.io/docs/latest/integrate/terraform-provider-for-redis-cloud/get-started/"}],"page_type":"content","content_hash":"4d2fa5bcefb29c6ae4f5bb93dbac9a0bf6134166a84dfa5a33da41c5db3b2c9f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Terraform](https://developer.hashicorp.com/terraform) is an open source automation tool developed by Hashicorp that allows you to easily provision infrastructure as code.\n\nRedis develops and maintains a [Terraform provider for Redis Cloud](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest). The Redis Cloud Terraform provider allows many of the same actions as found in the [Redis Cloud API](https://redis.io/docs/latest/operate/rc/api).\n\nSee [Get started with Terraform](https://redis.io/docs/latest/integrate/terraform-provider-for-redis-cloud/get-started) for an example of how to use the Terraform provider."},{"id":"data-sources-and-resources","title":"Data sources and Resources","role":"content","text":"The Terraform provider represents API actions as data sources and resources. Data sources are read-only and allow you to get information, while resources allow you to create and manage infrastructure.\n\nThe Redis Cloud Terraform provider allows for the following data sources:\n\n- Redis Cloud Pro:\n  - [Subscriptions](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/data-sources/rediscloud_subscription)\n  - [Databases](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/data-sources/rediscloud_database)\n  - [Database capabilities](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/data-sources/rediscloud_database_modules)\n  - [VPC peering connections](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/data-sources/rediscloud_subscription_peerings)\n  - [Cloud accounts](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/data-sources/rediscloud_cloud_account)\n  - [Supported persistence options](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/data-sources/rediscloud_data_persistence)\n  - [AWS Transit Gateways](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/data-sources/rediscloud_transit_gateway)\n  - Google Cloud Private Service Connect [Services](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/data-sources/rediscloud_private_service_connect) and [Endpoints](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/data-sources/rediscloud_private_service_connect_endpoints)\n- Redis Cloud Essentials:\n  - [Plans](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/data-sources/rediscloud_essentials_plan)\n  - [Subscriptions](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/resources/rediscloud_essentials_subscription)\n  - [Databases](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/resources/rediscloud_essentials_database)\n- Active-Active:\n  - [Subscriptions](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/resources/rediscloud_active_active_subscription)\n  - [Databases](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/resources/rediscloud_active_active_subscription_database)\n  - [AWS Transit Gateways](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/data-sources/rediscloud_active_active_transit_gateway)\n  - Google Cloud Private Service Connect [services](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/data-sources/rediscloud_active_active_private_service_connect) and [endpoints](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/data-sources/rediscloud_active_active_private_service_connect_endpoints)\n- [Payment methods](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/data-sources/rediscloud_payment_method)\n- [Supported cloud provider regions](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/data-sources/rediscloud_regions)\n- ACL [roles](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/data-sources/rediscloud_acl_role), [rules](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/data-sources/rediscloud_acl_rule), and [users](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/data-sources/rediscloud_acl_user)\n\nIt also allows you to create and manage the following resources:\n\n- Redis Cloud Pro:\n  - [Subscriptions](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/resources/rediscloud_subscription)\n  - [Databases](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/resources/rediscloud_subscription_database)\n    - **NOTE**: Upgrade your Terraform provider to version 1.8.1 to create databases with Search and Query.\n  - [VPC peering connections](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/resources/rediscloud_subscription_peering)\n  - [Cloud accounts](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/resources/rediscloud_cloud_account)\n  - [AWS Transit Gateway attachments](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/resources/rediscloud_transit_gateway_attachment)\n  - Google Cloud Private Service Connect [connections](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/resources/rediscloud_private_service_connect), [endpoints](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/resources/rediscloud_private_service_connect_endpoint) and [endpoint acceptors](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/resources/rediscloud_private_service_connect_endpoint_accepter)\n- Redis Cloud Essentials:\n  - [Subscriptions](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/resources/rediscloud_essentials_subscription)\n  - [Databases](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/resources/rediscloud_essentials_database)\n- Active-Active:\n  - [Subscriptions](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/resources/rediscloud_active_active_subscription)\n  - [Databases](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/resources/rediscloud_active_active_subscription_database)\n  - [Regions](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/resources/rediscloud_active_active_subscription_regions)\n  - [VPC peering connections](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/resources/rediscloud_active_active_subscription_peering)\n  - [AWS Transit Gateway attachments](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/resources/rediscloud_active_active_transit_gateway_attachment)\n  - Google Cloud Private Service Connect [connections](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/resources/rediscloud_active_active_private_service_connect), [endpoints](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/resources/rediscloud_active_active_private_service_connect_endpoint) and [endpoint acceptors](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/resources/rediscloud_active_active_private_service_connect_endpoint_accepter)\n- ACL [rules](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/resources/rediscloud_acl_rule), [roles](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/resources/rediscloud_acl_role), and [users](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs/resources/rediscloud_acl_user)"},{"id":"more-info","title":"More info","role":"content","text":"- [Get started with Terraform](https://redis.io/docs/latest/integrate/terraform-provider-for-redis-cloud/get-started)\n- [Redis Cloud Terraform Registry](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest/docs)\n- [Terraform documentation](https://developer.hashicorp.com/terraform/docs)\n- [Terraform configuration syntax](https://developer.hashicorp.com/terraform/language/syntax/configuration)"}],"examples":[]}
{"schema_version":2,"id":"integrate/uptrace-with-redis-enterprise","title":"Uptrace with Redis Software","url":"https://redis.io/docs/latest/integrate/uptrace-with-redis-enterprise/","summary":"To collect, view, and monitor metrics data from your databases and other cluster components, you can connect Uptrace to your Redis Software cluster using OpenTelemetry Collector.","tags":["docs","integrate","rs"],"last_updated":"2026-10-02T14:54:03+01:00","children":[],"page_type":"content","content_hash":"c51fe987147dc281b8d14480f7e092cda63be554674a2a392a2c886f674edb24","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Uptrace is an [open source APM tool](https://uptrace.dev/get/open-source-apm.html) that supports distributed tracing, metrics, and logs. You can use it to monitor applications and set up automatic alerts to receive notifications.\n\nUptrace uses OpenTelemetry to collect and export telemetry data from software applications such as Redis. OpenTelemetry is an open source observability framework that aims to provide a single standard for all types of observability signals such as traces, metrics, and logs.\n\nWith OpenTelemetry Collector, you can receive, process, and export telemetry data to any [OpenTelemetry backend](https://uptrace.dev/blog/opentelemetry-backend.html). You can also use Collector to scrape Prometheus metrics provided by Redis and then export those metrics to Uptrace.\n\nYou can use Uptrace to:\n\n- Collect and display data metrics not available in the [admin console](https://redis.io/docs/latest/operate/rs/references/metrics).\n- Use prebuilt dashboard templates maintained by the Uptrace community.\n- Set up automatic alerts and receive notifications via email, Slack, Telegram, and others.\n- Monitor your app performance and logs using [OpenTelemetry tracing](https://uptrace.dev/opentelemetry/distributed-tracing.html).\n\n![](https://redis.io/docs/latest/images/rs/uptrace-redis-nodes.png)"},{"id":"install-collector-and-uptrace","title":"Install Collector and Uptrace","role":"setup","text":"Because installing OpenTelemetry Collector and Uptrace can take some time, you can use the [docker-compose](https://github.com/uptrace/uptrace/tree/master/example/redis-enterprise) example that also comes with Redis Software cluster.\n\nAfter you download the Docker example, you can edit the following configuration files in the `uptrace/example/redis-enterprise` directory before you start the Docker containers:\n\n- `otel-collector.yaml` - Configures `/etc/otelcol-contrib/config.yaml` in the OpenTelemetry Collector container.\n- `uptrace.yml` - Configures`/etc/uptrace/uptrace.yml` in the Uptrace container.\n\nYou can also install OpenTelemetry and Uptrace from scratch using the following guides:\n\n- [Getting started with OpenTelemetry Collector](https://uptrace.dev/opentelemetry/collector.html)\n- [Getting started with Uptrace](https://uptrace.dev/get/get-started.html)\n\nAfter you install Uptrace, you can access the Uptrace UI at [http://localhost:14318/](http://localhost:14318/)."},{"id":"scrape-prometheus-metrics","title":"Scrape Prometheus metrics","role":"content","text":"Redis Software cluster exposes a Prometheus scraping endpoint on `http://localhost:8070/`. You can scrape that endpoint by adding the following lines to the OpenTelemetry Collector config:\n\n[code example]\n\nNext, you can export the collected metrics to Uptrace using OpenTelemetry protocol (OTLP):\n\n[code example]\n\nDon't forget to restart the Collector and then check logs for any errors:\n\n[code example]\n\nYou can also check the full OpenTelemetry Collector config [here](https://github.com/uptrace/uptrace/blob/master/example/redis-enterprise/otel-collector.yaml)."},{"id":"view-metrics","title":"View metrics","role":"content","text":"When metrics start arriving to Uptrace, you should see a couple of dashboards in the Metrics tab. In total, Uptrace should create 3 dashboards for Redis Software metrics:\n\n- \"Redis: Nodes\" dashboard displays a list of cluster nodes. You can select a node to view its metrics.\n\n- \"Redis: Databases\" displays a list of Redis databases in all cluster nodes. To find a specific database, you can use filters or sort the table by columns.\n\n- \"Redis: Shards\" contains a list of shards that you have in all cluster nodes. You can filter or sort shards and select a shard for more details."},{"id":"monitor-metrics","title":"Monitor metrics","role":"content","text":"To start monitoring metrics, you need to create metrics monitors using Uptrace UI:\n\n- Open \"Alerts\" -> \"Monitors\".\n- Click \"Create monitor\" -> \"Create metrics monitor\".\n\nFor example, the following monitor uses the `group by node` expression to create an alert whenever an individual Redis shard is down:\n\n[code example]\n\nYou can also create queries with more complex expressions.\n\nFor example, the following monitors create an alert when the keyspace hit rate is lower than 75% or memory fragmentation is too high:\n\n[code example]\n\nYou can learn more about the query language [here](https://uptrace.dev/get/querying-metrics.html)."}],"examples":[{"id":"scrape-prometheus-metrics-ex0","language":"yaml","code":"# /etc/otelcol-contrib/config.yaml\n\nprometheus_simple/cluster1:\n  collection_interval: 10s\n  endpoint: \"localhost:8070\" # Redis Cluster endpoint\n  metrics_path: \"/\"\n  tls:\n    insecure: false\n    insecure_skip_verify: true\n    min_version: \"1.0\"","section_id":"scrape-prometheus-metrics"},{"id":"scrape-prometheus-metrics-ex1","language":"yaml","code":"# /etc/otelcol-contrib/config.yaml\n\nreceivers:\n  otlp:\n    protocols:\n      grpc:\n      http:\n\nexporters:\n  otlp/uptrace:\n    # Uptrace is accepting metrics on this port\n    endpoint: localhost:14317\n    headers: { \"uptrace-dsn\": \"http://project1_secret_token@localhost:14317/1\" }\n    tls: { insecure: true }\n\nservice:\n  pipelines:\n    traces:\n      receivers: [otlp]\n      processors: [batch]\n      exporters: [otlp/uptrace]\n    metrics:\n      receivers: [otlp, prometheus_simple/cluster1]\n      processors: [batch]\n      exporters: [otlp/uptrace]\n    logs:\n      receivers: [otlp]\n      processors: [batch]\n      exporters: [otlp/uptrace]","section_id":"scrape-prometheus-metrics"},{"id":"scrape-prometheus-metrics-ex2","language":"shell","code":"docker-compose logs otel-collector\n\n# or\n\nsudo journalctl -u otelcol-contrib -f","section_id":"scrape-prometheus-metrics"},{"id":"monitor-metrics-ex0","language":"yaml","code":"monitors:\n  - name: Redis shard is down\n    metrics:\n      - redis_up as $redis_up\n    query:\n      - group by cluster # monitor each cluster,\n      - group by bdb # each database,\n      - group by node # and each shard\n      - $redis_up\n    min_allowed_value: 1\n    # shard should be down for 5 minutes to trigger an alert\n    for_duration: 5m","section_id":"monitor-metrics"},{"id":"monitor-metrics-ex1","language":"yaml","code":"monitors:\n  - name: Redis read hit rate < 75%\n    metrics:\n      - redis_keyspace_read_hits as $hits\n      - redis_keyspace_read_misses as $misses\n    query:\n      - group by cluster\n      - group by bdb\n      - group by node\n      - $hits / ($hits + $misses) as hit_rate\n    min_allowed_value: 0.75\n    for_duration: 5m\n\n  - name: Memory fragmentation is too high\n    metrics:\n      - redis_used_memory as $mem_used\n      - redis_mem_fragmentation_ratio as $fragmentation\n    query:\n      - group by cluster\n      - group by bdb\n      - group by node\n      - where $mem_used > 32mb\n      - $fragmentation\n    max_allowed_value: 3\n    for_duration: 5m","section_id":"monitor-metrics"}]}
{"schema_version":2,"id":"integrate/vercel-redis","title":"Redis with Vercel","url":"https://redis.io/docs/latest/integrate/vercel-redis/","summary":"Integrate Redis with Vercel for fast, scalable web applications","tags":["docs","integrate","rc"],"last_updated":"2026-09-17T14:54:42+01:00","children":[],"page_type":"content","content_hash":"e81dc47a7ad54fc76e3aaa2d6c15e2336506b052ec0a3fda7de847be57028bf2","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"Integrate Redis with your Vercel applications to add fast caching, session management, and real-time features to your web applications."},{"id":"overview","title":"Overview","role":"overview","text":"Redis integration with Vercel enables you to add high-performance data storage and caching to your serverless applications. Perfect for Next.js applications deployed on Vercel, Redis provides fast data access and real-time capabilities."},{"id":"key-features","title":"Key Features","role":"content","text":"- **Serverless Optimization**: Optimized for Vercel's serverless environment\n- **Edge Caching**: Global data caching for improved performance\n- **Session Management**: Persistent session storage across serverless functions\n- **Real-time Features**: WebSocket support for live updates and notifications\n- **API Acceleration**: Cache API responses for faster load times\n- **Environment Integration**: Seamless integration with Vercel environment variables\n- **Auto-scaling**: Scales automatically with your Vercel application traffic\n- **Global Distribution**: Redis databases close to Vercel's edge locations"},{"id":"getting-started","title":"Getting Started","role":"setup","text":"Learn how to [integrate Redis with Vercel](https://redis.io/docs/latest/operate/rc/cloud-integrations/vercel) for optimal performance."}],"examples":[]}
{"schema_version":2,"id":"integrate/write-behind/architecture","title":"Write-behind architecture","url":"https://redis.io/docs/latest/integrate/write-behind/architecture/","summary":"Discover the main components of Write-behind","aliases":["/integrate/redis-data-integration/write-behind/architecture/","/integrate/redis-data-integration/write behind/architecture/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-17T14:54:02+01:00","page_type":"content","content_hash":"6220a1380dd4ec53ea8308987a582e8b5288048af24ef323eeac61c4d15047e1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Write-behind lets you integrate Redis Enterprise (as the source of changes to data) and downstream databases or datastores.\nWrite-behind captures any changes to a selected set of key patterns in a Redis keyspace and asynchronously writes them in small batches to the downstream database. This means that your app doesn't need to handle the data remodeling or manage the connection with the downstream database.\n\nWrite-behind can normalize a key in Redis to several records in one or more tables at the target.\nTo learn more about write-behind declarative jobs and normalization, see the\n[write-behind quick start guide](https://redis.io/docs/latest/integrate/write-behind/quickstart/write-behind-guide)."},{"id":"write-behind-topology","title":"Write-behind topology","role":"content","text":"Write-behind's CLI and engine are shipped as one product that can run both ingest and write-behind pipelines.\nHowever, the two different types of pipeline have different topologies.\n\nThe Write-behind engine is installed on the Redis database that contains the application data and not on a separate staging Redis database. The Write-behind data streams and its control plane add only a small footprint of a few hundred MB to the Redis database. In the write-behind topology, Write-behind processes data in parallel on each shard and establishes a single connection from each shard to the downstream database."},{"id":"model-translation","title":"Model translation","role":"content","text":"Write-behind can track changes to the following Redis types:\n\n- [Hash](https://redis.io/docs/latest/develop/data-types/hashes)\n- [JSON](https://redis.io/docs/latest/develop/data-types/json)\n- [Set](https://redis.io/docs/latest/develop/data-types/sets)\n- [Sorted Set](https://redis.io/docs/latest/develop/data-types/sorted-sets)\n\nUnlike the ingest scenario, write-behind has no default behavior for model translation. You must always\ncreate a declarative job to specify the mapping between Redis keys and target database records.\nThe job configuration has `keys` and `mapping` sections that help make this an easy task."},{"id":"write-behind-components","title":"Write-behind components","role":"content","text":""},{"id":"write-behind-cli","title":"Write-behind CLI","role":"content","text":"Write-behind's Python-based CLI is highly intuitive to use and performs validations to help you avoid mistakes.\nThe CLI makes it easy to set up Write-behind and manage it over its entire lifecycle."},{"id":"write-behind-engine","title":"Write-behind Engine","role":"content","text":"Write-behind uses Redis Gears as its runtime environment. The Gears and Write-behind engine logic are installed\non all source Redis Enterprise database shards, but only primary shards process events and handle the pipeline.\n\nThe Write-behind Engine reads Redis change events from Redis Streams (one for each tracked key-pattern),\nprocesses them, and translates them to SQL or whatever other language the target database uses.\n\nWrite-behind writes changes to the target in small batches using transactions. Write-behind guarantees\n*at-least once* delivery. If any network problems, disconnections, or other temporary failures occur,\nWrite-behind will keep attempting to write the changes to the target. If a hard reject occurs, Write-behind keeps the reject\nrecord and the reason in a *dead letter queue (DLQ)*."},{"id":"write-behind-configuration","title":"Write-behind configuration","role":"content","text":"The Write-behind configuration is persisted at the cluster level. The configuration is written by the CLI\n[`deploy`](https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-deploy)\ncommand, which saves all changes to the configuration file. This mechanism allows for automatic configuration of new shards\nwhenever you need them, and it can survive shard and node failure."}],"examples":[]}
{"schema_version":2,"id":"integrate/write-behind/configuration-guide","title":"Write-behind configuration guide","url":"https://redis.io/docs/latest/integrate/write-behind/configuration-guide/","summary":"Configure write-behind to your database","aliases":["/integrate/redis-data-integration/write-behind/configuration-guide/","/integrate/redis-data-integration/write behind/configuration-guide/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-17T14:54:02+01:00","page_type":"content","content_hash":"c87bda1c8c0dfb6fa1e0ccc55c6bcf5021e6448557f834fee0efb915a8a88bd4","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"This guide shows you how to configure write-behind target connections."},{"id":"overview","title":"Overview","role":"overview","text":"Write-behind target connections are the connections established between a Write-behind instance and a target database in a\n[write-behind scenario](https://redis.io/docs/latest/integrate/write-behind/quickstart/write-behind-guide).\nWrite-behind is used to replicate changes captured in a Write-behind-enabled Redis Enterprise database to a target database. \nThe connections must be configured in the `config.yaml` before deploying any jobs and must follow one of the formats shown below. Multiple connections can be specified in the `connections` section."},{"id":"for-relational-datastores","title":"For relational datastores","role":"content","text":"[code example]"},{"id":"for-non-relational-datastores","title":"For non-relational datastores","role":"content","text":"[code example]"},{"id":"microsoft-sql-server","title":"Microsoft SQL Server","role":"content","text":"Microsoft SQL Server supports different authentication mechanisms (SQL Server Authentication and Integrated Windows Authentication) and protocols (NTLM and Kerberos). Write-behind can use all of them. However, systems that use Kerberos may require some additional configuration."},{"id":"account-permissions","title":"Account permissions","role":"content","text":"To enable Write-behind to work with a SQL Server database, check that the account you specify was assigned at least the `db_datawriter` role."},{"id":"sql-server-authentication","title":"SQL Server authentication","role":"content","text":"To use SQL Server authentication mode, create a user with login credentials and then assign the necessary permissions for the target database to that user.\n\n[code example]"},{"id":"windows-authentication","title":"Windows authentication","role":"content","text":"To use Windows authentication mode, you need to create a Windows or Active Directory account that has the necessary permissions to access the target database, and is able to log into SQL Server. The Linux machine hosting Write-behind can be configured to support the NTLM authentication protocol. \n\nFor NTLM:\n\n[code example]\n\n> Note: User must be specified with the domain name for Windows Authentication to work correctly.\n\nAfter you configure the Write-behind connection and deploy the write-behind job, run the following SQL query to have the operator check if Write-behind is using the expected authentication mechanism and protocol. Note: this operation may require the `sysadmin` role.\n\n[code example]\n\nThe results indicate which `auth_scheme` is used by each session and may take values `SQL`, `NTLM`, and `Kerberos`."}],"examples":[{"id":"for-relational-datastores-ex0","language":"yaml","code":"connections:\n    my-sql-datastore:\n        type: <db-type>     # mysql | oracle | postgresql | sqlserver\n        host: <db-host>     # IP address or FQDN of a database host and instance\n        port: <db-port>     # database port\n        database: <db-name> # name of the database\n        user: <db-user>         # database user\n        password: <db-password> # database password\n        # connect_args:     # optional connection parameters passed to the driver - these are driver specific\n        # query_args:       # optional parameters for SQL query execution - typically not required for Write-behind operation","section_id":"for-relational-datastores"},{"id":"for-non-relational-datastores-ex0","language":"yaml","code":"connections:\n    my-nosql-datastore:\n        type: <db-type>     # cassandra\n        hosts: <db-hosts>   # array of IP addresses or host names of a datastore nodes\n        port: <db-port>     # database port\n        database: <db-name> # name of the database\n        user: <db-user>         # database user\n        password: <db-password> # database password","section_id":"for-non-relational-datastores"},{"id":"sql-server-authentication-ex0","language":"yaml","code":"connections:\n    mssql2019-sqlauth:\n        type: sqlserver\n        host: ip-10-0-0-5.internal\n        port: 1433\n        database: rdi_wb_database\n        user: rdi_user\n        password: secret","section_id":"sql-server-authentication"},{"id":"windows-authentication-ex0","language":"yaml","code":"connections:\n    mssql2019-ntlm:\n        type: sqlserver\n        host: ip-10-0-0-5.internal\n        port: 1433\n        database: rdi_wb_database\n        user: MYDOMAIN\\rdi_service_account  # company-domain\\service-account\n        password: secret                    # NTLM requires to provide a password","section_id":"windows-authentication"},{"id":"windows-authentication-ex1","language":"sql","code":"SELECT session_id, auth_scheme FROM sys.dm_exec_connections;","section_id":"windows-authentication"}]}
{"schema_version":2,"id":"integrate/write-behind/data-transformation/data-transformation-pipeline","title":"Data transformation pipeline","url":"https://redis.io/docs/latest/integrate/write-behind/data-transformation/data-transformation-pipeline/","summary":"Learn how to transform data to Redis types","aliases":["/integrate/redis-data-integration/data-transformation/data-transformation-pipeline/","/integrate/redis-data-integration/ingest/data-pipelines/data-transformation-pipeline/","/integrate/redis-data-integration/write-behind/data-transformation/data-transformation-pipeline/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-10-02T14:54:03+01:00","page_type":"content","content_hash":"7faf9122ed6e57ce843ed5e394272b6444059269f717048bf21128bbf39d9758","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Write-behind's data transformation capabilities allow users to transform their data beyond the default translation of source types to Redis types. The transformation involves no coding. Instead, it is described in a set of human readable YAML files, one per source table.\n\nThe ingested format and types are different from one source to another. Currently, the only supported source is [Debezium](https://debezium.io/). The first transformation from Debezium types to native JSON with Redis types is done automatically without any need for user instructions. Then, this JSON is passed on to the user defined transformation pipeline.\n\nEach job describes the transformation logic to perform on data from a single source. The source is typically a database table or collection and is specified as the full name of this table/collection. The job may include filtering logic to skip data that matches a condition. Other logical steps in the job will transform the data into the desired output that will be stored in Redis as hashes or JSON.\n\n![](https://redis.io/docs/latest/images/rdi/data-transformation-pipeline.png)"},{"id":"default-job","title":"Default job","role":"content","text":"In situations where there is a need to perform a transformation on all ingested records without creating a specific job for specific tables, the default job is used. The transformation associated with this job will be applied to all tables that lack their own explicitly defined jobs. The default job must have a table name of \"*\", and only one instance of this type of job is permitted.\n\nFor example, the default job can streamline tasks such as adding a prefix or postfix to Redis keys, or adding fields to new hashes and JSONs without customizing each source table.\n\nCurrently, the default job is supported for ingest pipelines only."},{"id":"example","title":"Example","role":"example","text":"This example demonstrates the process of adding an `app_code` field with a value of `foo` using the [add_field](https://redis.io/docs/latest/integrate/write-behind/reference/data-transformation-block-types/add_field) block to all tables that lack explicitly defined jobs. Additionally, it appends an `aws` prefix and a `gcp` postfix to every generated hash key.\n\ndefault.yaml\n[code example]"},{"id":"jobs","title":"Jobs","role":"content","text":"Each job is defined in a separate YAML file. All of these files will be uploaded to Write-behind using the `deploy` command.\nFor more information, see [deploy configuration](#deploy-configuration)). If you are using the\n[scaffold](https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-scaffold) command,\nplace the job files in the `jobs` folder."},{"id":"job-yaml-structure","title":"Job YAML structure","role":"content","text":"#### Fields\n\n- `source`:\n\n  This section describes the table that the job operates on:\n\n  - `server_name`: logical server name (optional). Corresponds to the `debezium.source.topic.prefix` property specified in Debezium Server's `application.properties` config file\n  - `db`: database name (optional)\n  - `schema`: database schema (optional)\n  - `table`: database table name\n  - `row_format`: format of the data to be transformed: `data_only` (default) - only payload, full - complete change record\n\n> Note: Any reference to the properties `server_name`, `db`, `schema`, and `table` will be treated by default as case insensitive. This can be changed by setting `case_insensitive` to `false`.\n\n> Cassandra only: In Cassandra, a `keyspace` is roughly the equivalent to a `schema` in other databases. Write-behind uses the `schema` property declared in a job file to match the `keyspace` attribute of the incoming change record.\n\n> MongoDB only: In MongoDB, a `replica set` is a cluster of shards with data and can be regarded as roughly equivalent to a `schema` in a relational database. A MongoDB `collection` is similar to a `table` in other databases. Write-behind uses the `schema` and `table` properties declared in a job file to match the `replica set` and `collection` attributes of the incoming change record, respectively.\n\n- `transform`:\n\n  This section includes a series of blocks that define how the data will be transformed.\n  For more information, see\n  [supported blocks](https://redis.io/docs/latest/integrate/write-behind/reference/data-transformation-block-types)\n  and [JMESPath custom functions](https://redis.io/docs/latest/integrate/write-behind/reference/jmespath-custom-functions).\n\n- `output`:\n\n  This section defines the output targets for processed data:\n\n  - Cassandra:\n    - `uses`: `cassandra.write`: write into a Cassandra data store\n    - `with`:\n      - `connection`: connection name\n      - `keyspace`: keyspace\n      - `table`: target table\n      - `keys`: array of key columns\n      - `mapping`: array of mapping columns\n      - `opcode_field`: the name of the field in the payload that holds the operation (c - create, d - delete, u - update) for this record in the database\n  - Redis:\n    - `uses`: `redis.write`: write to a Redis data structure. Multiple blocks of this type are allowed in the same job\n    - `with`:\n      - `connection`: connection name as defined in `config.yaml` (by default, the connection named 'target' is used)\n      - `data_type`: target data structure when writing data to Redis (hash, json, set and stream are supported values)\n      - `key`: this allows you to override the key of the record by applying custom logic:\n        - `expression`: expression to execute\n        - `language`: expression language, JMESPath or SQL\n      - `expire`: positive integer value indicating a number of seconds for the key to expire. If not set, the key will never expire\n  - SQL:\n    - `uses`: `relational.write`: write into a SQL-compatible data store\n    - `with`:\n      - `connection`: connection name\n      - `schema`: schema\n      - `table`: target table name\n      - `keys`: array of key columns\n      - `mapping`: array of mapping columns\n      - `opcode_field`: the name of the field in the payload that holds the operation (c - create, d - delete, u - update) for this record in the database\n\n#### Notes\n\n- `source` is required.\n- Either `transform`, `key`, or both should be specified.\n\n#### Using key in transformations\n\nTo access the Redis key (for example in a write-behind job) you will need to take the following steps:\n\n- Set `row_format: full` to allow access to the key that is part of the full data entry.\n- Use the expression `key.key` to get the Redis key as a string.\n\n#### Before and after values\n\nUpdate events typically report `before` and `after` sections, providing access to the data state before and after the update. \nTo access the \"before\" values explicitly, you will need to:\n\n- Set `row_format: full` to allow access to the key that is part of the full data entry.\n- Use the `before.<FIELD_NAME>` pattern."},{"id":"example-1","title":"Example","role":"example","text":"This example shows how to rename the `fname` field to `first_name` in the table `emp` using the `rename_field` block. It also demonstrates how to set the key of this record instead of relying on the default logic.\n\nredislabs.dbo.emp.yaml\n\n[code example]"},{"id":"deploy-configuration","title":"Deploy configuration","role":"content","text":"In order to deploy your jobs to the remote Write-behind database, run:\n\n[code example]"},{"id":"deploy-configuration-on-kubernetes","title":"Deploy configuration on Kubernetes","role":"content","text":"If the Write-behind CLI is deployed as a pod in a Kubernetes cluster, perform these steps to deploy your jobs:\n\n- Create a ConfigMap from the YAML files in your `jobs` folder:\n\n  [code example]\n\n- Deploy your jobs:\n\n  [code example]\n\n> Note: A delay occurs between creating/modifying the ConfigMap and its availability in the `redis-di-cli` pod. Wait around 30 seconds before running the `redis-di deploy` command.\n\nYou have two options to update the ConfigMap:\n\n- For smaller changes, you can edit the ConfigMap directly with this command:\n\n  [code example]\n\n- For bigger changes such as adding another job file, edit the files in your local `jobs` folder and then run this command:\n\n  [code example]\n\n> Note: You need to run `kubectl exec -it pod/redis-di-cli -- redis-di deploy` after updating the ConfigMap with either option."}],"examples":[{"id":"example-ex0","language":"yaml","code":"source:\n  table: \"*\"\n  row_format: full\ntransform:\n  - uses: add_field\n    with:\n      fields:\n        - field: after.app_code\n          expression: \"`foo`\"\n          language: jmespath\noutput:\n  - uses: redis.write\n    with:\n      data_type: hash\n      key:\n        expression: concat(['aws', '#', table, '#', keys(key)[0], '#', values(key)[0], '#gcp'])\n        language: jmespath","section_id":"example"},{"id":"example-1-ex0","language":"yaml","code":"source:\n  server_name: redislabs\n  schema: dbo\n  table: emp\ntransform:\n  - uses: rename_field\n    with:\n      from_field: fname\n      to_field: first_name\noutput:\n  - uses: redis.write\n    with:\n      connection: target\n      key:\n        expression: concat(['emp:fname:',fname,':lname:',lname])\n        language: jmespath","section_id":"example-1"},{"id":"deploy-configuration-ex0","language":"bash","code":"redis-di deploy","section_id":"deploy-configuration"},{"id":"deploy-configuration-on-kubernetes-ex0","language":"bash","code":"kubectl create configmap redis-di-jobs --from-file=jobs/","section_id":"deploy-configuration-on-kubernetes"},{"id":"deploy-configuration-on-kubernetes-ex1","language":"bash","code":"kubectl exec -it pod/redis-di-cli -- redis-di deploy","section_id":"deploy-configuration-on-kubernetes"},{"id":"deploy-configuration-on-kubernetes-ex2","language":"bash","code":"kubectl edit configmap redis-di-jobs","section_id":"deploy-configuration-on-kubernetes"},{"id":"deploy-configuration-on-kubernetes-ex3","language":"bash","code":"kubectl create configmap redis-di-jobs --from-file=jobs/ --dry-run=client -o yaml | kubectl apply -f -","section_id":"deploy-configuration-on-kubernetes"}]}
{"schema_version":2,"id":"integrate/write-behind/data-transformation","title":"Data transformation","url":"https://redis.io/docs/latest/integrate/write-behind/data-transformation/","summary":"","aliases":["/integrate/redis-data-integration/data-transformation/","/integrate/redis-data-integration/write-behind/data-transformation/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-10-02T14:54:03+01:00","children":[{"id":"integrate/write-behind/data-transformation/data-transformation-pipeline","summary":"Learn how to transform data to Redis types","title":"Data transformation pipeline","url":"https://redis.io/docs/latest/integrate/write-behind/data-transformation/data-transformation-pipeline/"},{"id":"integrate/write-behind/data-transformation/transformation-examples","summary":"See examples of write-behind transform job configurations","title":"Write-behind transformation examples","url":"https://redis.io/docs/latest/integrate/write-behind/data-transformation/transformation-examples/"}],"page_type":"content","content_hash":"75ba6675cc45beecb170dd342b80b9f2b5ee3981824448b3600246e485315836","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The key functionality that Write-behind performs is mapping the data coming from [Debezium Server](https://debezium.io/documentation/reference/stable/operations/debezium-server.html) (representing a Source Database row data or row state change) into a Redis key with a value of [Hash](https://redis.io/docs/latest/develop/data-types/hashes) or [JSON](https://redis.io/docs/latest/develop/data-types/json).\nThere are two types of data transformations in Write-behind:\n\n1. By default, each source row is converted into one hash or one JSON key in Redis.\n  This conversion uses the Debezium schema-based conversion. The incoming data includes the schema and Write-behind uses a set of handlers to automatically convert each source column to a Redis Hash field or JSON type based on the Debezium type in the schema. See\n  [data type conversion](https://redis.io/docs/latest/integrate/write-behind/reference/data-types-conversion)\n  for a full reference on these conversions.\n\n1. If the user wants to add or modify this default mapping, Write-behind provides declarative data transformations. These transformations are represented in YAML files. Each file contains a job, which is a set of transformations per source table. See\n[declarative transformations](https://redis.io/docs/latest/integrate/write-behind/data-transformation/data-transformation-pipeline) for more information.\n\n![](https://redis.io/docs/latest/images/rdi/data-transformation-flow.png)"},{"id":"more-info","title":"More info","role":"content","text":""}],"examples":[]}
{"schema_version":2,"id":"integrate/write-behind/data-transformation/transformation-examples/foreach-example","title":"Write-behind foreach example","url":"https://redis.io/docs/latest/integrate/write-behind/data-transformation/transformation-examples/foreach-example/","summary":"","aliases":["/integrate/redis-data-integration/data-transformation/examples/foreach-example/","/integrate/redis-data-integration/ingest/data-transformation/examples/foreach-example/","/integrate/redis-data-integration/write behind/foreach-example/","/integrate/redis-data-integration/write-behind/foreach-example/","/integrate/redis-data-integration/write-behind/data-transformation/transformation-examples/foreach-example/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"ee1cc762d25a8dac2a8d4eef0dbb546ae827b044504a665623bf86a94f5cf923","sections":[{"id":"content","title":"Content","role":"content","text":"The `foreach` section is used to explode a list of objects or arrays to rows in a selected target.\nThe `foreach` expression is structured as `<field_name>:<JMESPath expression>`.\nThe following example uses the `add_field` transformation to prepare the input JSON to the desired structure. Then, it applies `foreach` to write each `order` object as a relational database record using `keys` and `mapping`.\nIn this example, the `JMESPath` function `to_string` is used to flatten an array of objects, `specs`, to a string.\n\n[code example]"}],"examples":[{"id":"content-ex0","language":"yaml","code":"source:\n  redis:\n    key_pattern: orderdetail:*\n    trigger: write-behind\n    exclude_commands: [\"json.del\"]\ntransform:\n  - uses: add_field\n    with:\n      fields:\n        - field: my_orders\n          language: jmespath\n          expression: |\n            orders[*].{\n              code: code\n              periodStartTime: periodStartTime\n              periodEndTime: periodEndTime\n              specs: to_string(specs)\n            }\noutput:\n  - uses: relational.write\n    with:\n      connection: mssql\n      schema: dbo\n      table: OrderMaster\n      keys:\n        - Code: orderDetail.code\n      mapping:\n        - DeliveryDate: orderDetail.deliveryDate\n        - ProductCode: orderDetail.productCode\n        - CountryCode: orderDetail.countryCode\n  - uses: relational.write\n    with:\n      connection: mssql\n      schema: dbo\n      table: Order\n      foreach: \"order: my_orders[]\"\n      keys:\n        - Code: order.code\n      mapping:\n        - OrderDetailCode: orderDetail.code\n        - PeriodStartTime: order.periodStartTime\n        - PeriodEndTime: order.periodEndTime\n        - Specs: order.specs","section_id":"content"}]}
{"schema_version":2,"id":"integrate/write-behind/data-transformation/transformation-examples","title":"Write-behind transformation examples","url":"https://redis.io/docs/latest/integrate/write-behind/data-transformation/transformation-examples/","summary":"See examples of write-behind transform job configurations","aliases":["/integrate/redis-data-integration/write-behind/data-transformation/transformation-examples/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","children":[{"id":"integrate/write-behind/data-transformation/transformation-examples/foreach-example","summary":"","title":"Write-behind foreach example","url":"https://redis.io/docs/latest/integrate/write-behind/data-transformation/transformation-examples/foreach-example/"},{"id":"integrate/write-behind/data-transformation/transformation-examples/write-behind-to-redis-example","summary":"","title":"Write-behind to Redis Enterprise target example","url":"https://redis.io/docs/latest/integrate/write-behind/data-transformation/transformation-examples/write-behind-to-redis-example/"}],"page_type":"index","sections":[],"examples":[]}
{"schema_version":2,"id":"integrate/write-behind/data-transformation/transformation-examples/write-behind-to-redis-example","title":"Write-behind to Redis Enterprise target example","url":"https://redis.io/docs/latest/integrate/write-behind/data-transformation/transformation-examples/write-behind-to-redis-example/","summary":"","aliases":["/integrate/redis-data-integration/data-transformation/examples/write-behind-to-redis-example/","/integrate/redis-data-integration/ingest/data-transformation/examples/write-behind-to-redis-example/","/integrate/redis-data-integration/write behind/write-behind-to-redis-example/","/integrate/redis-data-integration/write-behind/write-behind-to-redis-example/","/integrate/redis-data-integration/write-behind/data-transformation/transformation-examples/write-behind-to-redis-example/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"11b386443b2cc458eb49251989abb72eca5adbdd53fb6c62db82957abe808716","sections":[{"id":"content","title":"Content","role":"content","text":"The `redis.write` block can be used in the `output` section of the write-behind job in order to enable writing data to the Redis Enterprise target database. Multiple blocks can be used at the same time to write data to different data types. The following example captures data modified in the `address:*` keyspace, then creates a new JSON-like string field named `json_addr`,  and, finally, writes the results to multiple keys in target Redis Enteprise database:\n\n[code example]\n\nRun the following command in the source Redis Enterprise database to test the job:\n\n[code example]\n\nThe result is five keys will be created in the target Redis Enterprise database (hash, JSON, set, sorted set, and stream):\n\n[code example]"}],"examples":[{"id":"content-ex0","language":"yaml","code":"source:\n  redis:\n    trigger: write-behind\n    key_pattern: address:*\ntransform:\n  - uses: add_field\n    with:\n      fields:\n        - field: \"json_addr\"\n          expression: concat(['{\"city\":', city, ', \"zip\":', zip, '}'])\n          language: jmespath\noutput:\n  - uses: redis.write\n    with:\n      data_type: hash\n      connection: target\n      key:\n        expression: concat(['addr:org_id:', org_id, ':hash'])\n        language: jmespath\n  - uses: redis.write\n    with:\n      data_type: json\n      key:\n        expression: concat(['addr:org_id:', org_id, ':json'])\n        language: jmespath\n      on_update: merge\n  - uses: redis.write\n    with:\n      data_type: set\n      key:\n        expression: concat(['addresses:', country])\n        language: jmespath\n      args:\n        member: json_addr\n  - uses: redis.write\n    with:\n      data_type: sorted_set\n      key:\n        expression: concat(['addrs_withscores:', country])\n        language: jmespath\n      args:\n        score: zip\n        member: json_addr\n  - uses: redis.write\n    with:\n      data_type: stream\n      key:\n        expression: \"`addresses:events`\"\n        language: jmespath\n      mapping:\n        - org_id: message_id\n        - zip: zip_code\n        - country","section_id":"content"},{"id":"content-ex1","language":"shell","code":"127.0.0.1:12005> hset address:1 city Austin zip 78901 org_id 1 country USA","section_id":"content"},{"id":"content-ex2","language":"shell","code":"127.0.0.1:12000> keys *\n1) \"addr:org_id:1:hash\"\n2) \"addr:org_id:1:json\"\n3) \"addresses:USA\"\n4) \"addrs_withscores:USA\"\n5) \"addresses:events\"","section_id":"content"}]}
{"schema_version":2,"id":"integrate/write-behind/installation","title":"Installation","url":"https://redis.io/docs/latest/integrate/write-behind/installation/","summary":"Learn how to install Write-behind","aliases":["/integrate/redis-data-integration/write-behind/installation/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","children":[{"id":"integrate/write-behind/installation/install-rdi-cli","summary":"Install Write-behind CLI","title":"Install Write-behind CLI","url":"https://redis.io/docs/latest/integrate/write-behind/installation/install-rdi-cli/"},{"id":"integrate/write-behind/installation/install-redis-gears","summary":"Install and set up RedisGears for a Write-behind deployment","title":"Install RedisGears for Redis Data Integration","url":"https://redis.io/docs/latest/integrate/write-behind/installation/install-redis-gears/"}],"page_type":"index","sections":[],"examples":[]}
{"schema_version":2,"id":"integrate/write-behind/installation/install-rdi-cli","title":"Install Write-behind CLI","url":"https://redis.io/docs/latest/integrate/write-behind/installation/install-rdi-cli/","summary":"Install Write-behind CLI","aliases":["/integrate/redis-data-integration/installation/install-rdi-cli/","/integrate/redis-data-integration/write behind/installation/install-rdi-cli/","/integrate/redis-data-integration/write-behind/installation/install-rdi-cli/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"07b5bad5ed71d963c4729dcd7660a32f7ecc856ed6894489b4195f83001cd197","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The following installation instructions install the Write-behind CLI on a local workstation.\n\nWrite-behind installation is done via the Write-behind CLI. The CLI should have network access to the Redis Enterprise cluster API (port 9443 by default)."},{"id":"download-write-behind-cli","title":"Download Write-behind CLI","role":"content","text":"#### Ubuntu 20.04\n\n[code example]\n\n#### Ubuntu 18.04\n\n[code example]\n\n#### RHEL 8\n\n[code example]\n\n#### RHEL 7\n\n[code example]"},{"id":"install-write-behind-cli","title":"Install Write-behind CLI","role":"setup","text":"Unpack the downloaded `redis-di.tar.gz` into the `/usr/local/bin/` directory:\n\n[code example]\n\n> Note: Non-root users should unpack to a directory with write permission and run `redis-di` directly from it."}],"examples":[{"id":"download-write-behind-cli-ex0","language":"bash","code":"wget https://qa-onprem.s3.amazonaws.com/redis-di//redis-di-ubuntu20.04-.tar.gz -O /tmp/redis-di.tar.gz","section_id":"download-write-behind-cli"},{"id":"download-write-behind-cli-ex1","language":"bash","code":"wget https://qa-onprem.s3.amazonaws.com/redis-di//redis-di-ubuntu18.04-.tar.gz -O /tmp/redis-di.tar.gz","section_id":"download-write-behind-cli"},{"id":"download-write-behind-cli-ex2","language":"bash","code":"wget https://qa-onprem.s3.amazonaws.com/redis-di//redis-di-rhel8-.tar.gz -O /tmp/redis-di.tar.gz","section_id":"download-write-behind-cli"},{"id":"download-write-behind-cli-ex3","language":"bash","code":"wget https://qa-onprem.s3.amazonaws.com/redis-di//redis-di-rhel7-.tar.gz -O /tmp/redis-di.tar.gz","section_id":"download-write-behind-cli"},{"id":"install-write-behind-cli-ex0","language":"bash","code":"sudo tar xvf /tmp/redis-di.tar.gz -C /usr/local/bin/","section_id":"install-write-behind-cli"}]}
{"schema_version":2,"id":"integrate/write-behind/installation/install-redis-gears","title":"Install RedisGears for Redis Data Integration","url":"https://redis.io/docs/latest/integrate/write-behind/installation/install-redis-gears/","summary":"Install and set up RedisGears for a Write-behind deployment","aliases":["/integrate/redis-data-integration/installation/install-redis-gears/","/integrate/redis-data-integration/write behind/installation/install-redis-gears/","/integrate/redis-data-integration/write-behind/installation/install-redis-gears/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-09-17T14:54:02+01:00","page_type":"content","content_hash":"cfddfa9df0e63b1ea5387233b7ab9d52a30803622dd741e661f917d1fade5d3e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Write-behind requires that [RedisGears](https://redis.com/modules/redis-gears) module with the [Python plugin](https://docs.redis.com/latest/modules/redisgears/python/) is installed on the Redis Enterprise cluster.\n\nThe Python plugin can be installed explicitly or alongside with the [JVM plugin](https://docs.redis.com/latest/modules/redisgears/jvm/) if the latter is needed on the cluster for other purposes.\n\nUse the [`redis-di create`](https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-create) command in Write-behind CLI to install RedisGears."},{"id":"download-redisgears","title":"Download RedisGears","role":"content","text":"Download RedisGears based on the Linux distribution of where Redis Enterprise is installed."},{"id":"ubuntu-2004","title":"Ubuntu 20.04","role":"content","text":"[code example]"},{"id":"ubuntu-1804","title":"Ubuntu 18.04","role":"content","text":"[code example]"},{"id":"rhel8","title":"RHEL8","role":"content","text":"[code example]"},{"id":"rhel7","title":"RHEL7","role":"content","text":"[code example]"}],"examples":[{"id":"ubuntu-2004-ex0","language":"bash","code":"curl -s --tlsv1.3 https://redismodules.s3.amazonaws.com/redisgears/redisgears.Linux-ubuntu20.04-x86_64.-withdeps.zip -o /tmp/redis-gears.zip","section_id":"ubuntu-2004"},{"id":"ubuntu-1804-ex0","language":"bash","code":"curl -s --tlsv1.3 https://redismodules.s3.amazonaws.com/redisgears/redisgears.Linux-ubuntu18.04-x86_64.-withdeps.zip -o /tmp/redis-gears.zip","section_id":"ubuntu-1804"},{"id":"rhel8-ex0","language":"bash","code":"curl -s https://redismodules.s3.amazonaws.com/redisgears/redisgears.Linux-rhel8-x86_64.-withdeps.zip -o /tmp/redis-gears.zip","section_id":"rhel8"},{"id":"rhel7-ex0","language":"bash","code":"curl -s https://redismodules.s3.amazonaws.com/redisgears/redisgears.Linux-rhel7-x86_64.-withdeps.zip -o /tmp/redis-gears.zip","section_id":"rhel7"}]}
{"schema_version":2,"id":"integrate/write-behind/monitoring-guide","title":"Monitoring guide","url":"https://redis.io/docs/latest/integrate/write-behind/monitoring-guide/","summary":"Monitor Write-behind engine and data processing jobs","aliases":["/integrate/redis-data-integration/write-behind/monitoring-guide/","/integrate/redis-data-integration/monitoring-guide/","/integrate/redis-data-integration/write behind/monitoring-guide/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-10-02T14:54:03+01:00","page_type":"content","content_hash":"c55f5215b85291b7478d1a05bdd2a818ceb2e5459e26856ede8b381f69f3d9e6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Write-behind accumulates operating statistics that you can:\n\n- Observe and analyze to discover various types of problems.\n- Use for optimization purposes."},{"id":"console-metrics","title":"Console metrics","role":"content","text":"Write-behind can display its operating metrics in the console using the\n[`redis-di status`](https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-status)\ncommand. The command provides information about the current Write-behind engine status, target database configuration, and processing statistics broken down by stream. This tool is intended to be used by Operator to get the current snapshot of the system as well as monitoring ongoing data processing (when used in live mode)."},{"id":"prometheus-integration","title":"Prometheus integration","role":"content","text":"Write-behind allows collecting and exporting its metrics to [Prometheus](https://prometheus.io/) and visualizing them in [Grafana](https://grafana.com/). Operator can start the built-in exporter using the\n[`redis-di monitor`](https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-monitor)\ncommand. The diagram describes this flow and the components involved:\n\n![](https://redis.io/docs/latest/images/rdi/monitoring-diagram.png)\n\n> Note: The host names and ports above are examples only and can be changed as needed."},{"id":"test-write-behind-metrics-exporter","title":"Test Write-behind metrics exporter","role":"content","text":"Start the Write-behind metrics exporter using the command below:\n\n[code example]\n\n> Note: The default port for the exporter is `9121`. If you need to change it, use the `--exporter-port` option. The default metrics collection interval is 5 seconds. If you need to change it, use the `--collect-interval` option.\n\nThen navigate to `http://localhost:9121/` to see the exported metrics. You should be able to see the following metric:\n\n[code example]\n\n> Note: The actual value of the metric above can be 0 if you haven't started Write-behind engine yet (in which case, the `state` label should indicate that as well). You must have the Write-behind database created and configured before observing any metrics. If you are not seeing it or getting an error value instead, this indicates that the Write-behind database is not properly configured."},{"id":"configure-prometheus","title":"Configure Prometheus","role":"configuration","text":"Next, configure the Prometheus scraper. Edit the `prometheus.yml` file to add the following scraper config:\n\n[code example]\n\n> Notes:\n\n- Make sure the `targets` value above points to the host and port you configured to run the Write-behind metrics exporter.\n- The `scrape_interval` setting in Prometheus should be the same or higher than the `collect_interval` setting for the exporter. For example, if the `collect_interval` is set to 5 seconds, the `scrape_interval` should also be set to 5 seconds or more. If the `scrape_interval` is set to less than the `collect_interval`, Prometheus will scrape the exporter before it has a chance to collect and refresh metrics, and you will see duplicated values in Prometheus."},{"id":"test-the-prometheus-scraper","title":"Test the Prometheus scraper","role":"content","text":"After the scraper config is added to the Prometheus configuration, you should now be able to navigate to `http://<HOSTNAME>:9090/graph` (replace `<HOSTNAME>` with a valid Prometheus hostname or IP address).\n\nExplore Write-behind metrics using the [expression browser](https://prometheus.io/docs/visualization/browser/).\n\nIn the expression box, type in a metric name (for example, `rdi_engine_state`) and select `Enter` or the `Execute` button to see the following result:\n\n[code example]\n\n> Note: You may see more than just one Write-behind metric if Write-behind engine has already processed some data. If you do not see any metrics, check your scraper job configuration in Prometheus."},{"id":"use-grafana-to-analyze-metrics","title":"Use Grafana to analyze metrics","role":"content","text":"Optionally, you may deploy the sample Grafana dashboard to monitor the status of Write-behind engine and its individual jobs. Follow these steps to import the dashboard:\n\n1. Download the **dashboard file** to your local machine.\n\n1. Log into Grafana and navigate to the list of dashboards, then choose **New > Import**:\n\n![](https://redis.io/docs/latest/images/rdi/monitoring-grafana-new-dash.png)\n\n1. On the next screen, select **Upload JSON file** and upload the file you downloaded in step 1. Make sure you select the data source that is connected to the Write-behind metrics exporter:\n\n![](https://redis.io/docs/latest/images/rdi/monitoring-grafana-dash-configure.png)\n\n1. Select **Import** and make sure you choose the jobs to monitor from the drop-down list (this will be empty if you don't have any jobs running yet):\n\n![](https://redis.io/docs/latest/images/rdi/monitoring-grafana-dash-running.png)"},{"id":"write-behind-metrics","title":"Write-behind metrics","role":"content","text":"This list shows exported Write-behind metrics along with their descriptions:\n\n| Metric Name                 | Labels                                                                                                     | Values               | Description                                                                                                                                                                                                        |\n| --------------------------- | ---------------------------------------------------------------------------------------------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| rdi_engine_state            | {status=RUNNING \\| STOPPED, sync_mode=SNAPSHOT \\| STREAMING \\| UNKNOWN}                                    | 0, 1                 | Status of Write-behind engine. 0 - Write-behind engine is stopped, 1 - Write-behind engine is running. Sync mode label indicates the last reported ingest synchronization mode.                                                               |\n| rdi_incoming_entries        | {data_source=`<stream name>`, operation=pending \\| inserted \\| updated \\| deleted \\| filtered \\| rejected} | `<count of records>` | Counters, indicating the number of operations performed for each stream.                                                                                                                                           |\n| rdi_stream_event_latency_ms | {data_source=`<stream name>`}                                                                              | 0 - &infin;          | Latency calculated for each stream. Indicates the time in milliseconds the first available record has spent in the stream waiting to be processed by Write-behind engine. If no records pending it will always return zero. |"}],"examples":[{"id":"test-write-behind-metrics-exporter-ex0","language":"bash","code":"redis-di monitor","section_id":"test-write-behind-metrics-exporter"},{"id":"test-write-behind-metrics-exporter-ex1","language":"plaintext","code":"rdi_engine_state{state=\"RUNNING\",sync_mode=\"UNKNOWN\"} 1.0","section_id":"test-write-behind-metrics-exporter"},{"id":"configure-prometheus-ex0","language":"yaml","code":"scrape_configs:\n  # scrape Write-behind metrics exporter\n  - job_name: rdi-exporter\n    static_configs:\n      - targets: [\"redis-exporter:9121\"]","section_id":"configure-prometheus"},{"id":"test-the-prometheus-scraper-ex0","language":"plaintext","code":"rdi_engine_state{instance=\"redis-exporter:9121\", job=\"rdi-exporter\", status=\"RUNNING\", sync_mode=\"UNKNOWN\"} 1","section_id":"test-the-prometheus-scraper"}]}
{"schema_version":2,"id":"integrate/write-behind/quickstart/write-behind-guide","title":"Quickstart","url":"https://redis.io/docs/latest/integrate/write-behind/quickstart/write-behind-guide/","summary":"Get started creating a write-behind pipeline","aliases":["/integrate/redis-data-integration/quickstart/write-behind-guide/","/integrate/redis-data-integration/write behind/quickstart/write-behind-guide/","/integrate/redis-data-integration/write-behind/quickstart/write-behind-guide/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-10-02T14:54:03+01:00","page_type":"content","content_hash":"15a68b7e82d48043ee1c87956d36505b886ca332f17d3753d84b9187439bf75e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This guide takes you through the creation of a write-behind pipeline."},{"id":"concepts","title":"Concepts","role":"content","text":"**Write-behind** is a processing pipeline used to synchronize data in a Redis database with a downstream data store.\nYou can think about it as a pipeline that starts with change data capture (CDC) events for a Redis database and then filters, transforms, and maps the data to the target data store data structures.\n\nThe **target** data store to which the write-behind pipeline connects and writes data.\n\nThe write-behind pipeline is composed of one or more **jobs**. Each job is responsible for capturing change for one key pattern in Redis and mapping it to one or more tables in the downstream data store. Each job is defined in a YAML file.\n\n![](https://redis.io/docs/latest/images/rdi/redis-di-write-behind.png)"},{"id":"supported-data-stores","title":"Supported data stores","role":"compatibility","text":"Write-behind currently supports these target data stores:\n\n| Data Store |\n| ---------- |\n| Cassandra  |\n| MariaDB    |\n| MySQL      |\n| Oracle     |\n| PostgreSQL |\n| Redis Enterprise     |\n| SQL Server |"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"The only prerequisite for running Write-behind is [Redis Gears Python](https://redis.com/modules/redis-gears/) >= 1.2.6 installed on the Redis Enterprise Cluster and enabled for the database you want to mirror to the downstream data store.\nFor more information, see\n[RedisGears installation](https://redis.io/docs/latest/integrate/write-behind/installation/install-redis-gears)."},{"id":"preparing-the-write-behind-pipeline","title":"Preparing the write-behind pipeline","role":"content","text":"- Install [Write-behind CLI](https://redis.io/docs/latest/integrate/write-behind/installation/install-rdi-cli) on a Linux host that has connectivity to your Redis Enterprise Cluster.\n- Run the [`configure`](https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-configure) command to install the Write-behind Engine on your Redis database, if you have not used this Redis database with Write-behind before.\n- Run the [`scaffold`](https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-scaffold) command with the type of data store you want to use, for example:\n\n  [code example]\n\n  This creates a template `config.yaml` file and a folder named `jobs` under the current directory.\n  You can specify any folder name with `--dir` or use the `--preview config.yaml` option, if your Write-behind CLI is deployed inside a Kubernetes (K8s) pod, to get the `config.yaml` template to the terminal.\n\n- Add the connections required for downstream targets in the `connections` section of `config.yaml`, for example:\n\n  [code example]\n\n  This is the first section of the `config.yaml` file and typically the only one you'll need to edit. The `connections` section is designed to have many target connections. In the previous example, there are two downstream connections named `my-postgres` and `my-mysql`.\n\n  To obtain a secured connection using TLS, you can add more `connect_args` or `query_args`, depending on the specific target database terminology, to the connection definition.\n\n  The name can be any arbitrary name as long as it is:\n\n  - Unique for this Write-behind engine\n  - Referenced correctly by the jobs in the respective YAML files\n\nIn order to prepare the pipeline, fill in the correct information for the target data store. Secrets can be provided using a reference to a secret ([see below](#how-to-provide-targets-secrets)) or by specifying a path.\n\nThe `applier` section has information about the batch size and frequency used to write data to the target.\n\nSome of the `applier` attributes such as `target_data_type`, `wait_enabled`, and `retry_on_replica_failure` are specific for the Write-behind ingest pipeline and can be ignored."},{"id":"write-behind-jobs","title":"Write-behind jobs","role":"content","text":"Write-behind jobs are a mandatory part of the write-behind pipeline configuration.\nUnder the `jobs` directory (parallel to `config.yaml`) you should have a job definition in a YAML file for every key pattern you want to write to a downstream database table.\n\nThe YAML file can be named using the destination table name or another naming convention, but it has to have a unique name.\n\nJob definition has the following structure:\n\n[code example]"},{"id":"source-section","title":"Source section","role":"content","text":"The `source` section describes the source of data in the pipeline.\n\nThe `redis` section is common for every pipeline initiated by an event in Redis, such as applying changes to data. In the case of write-behind, it has the information required to activate a pipeline dealing with changes to data. It includes the following attributes:\n\n- The `key_pattern` attribute specifies the pattern of Redis keys to listen on. The pattern must correspond to keys that are of Hash or JSON type.\n\n- The `exclude_commands` attribute specifies which commands to ignore. For example, if you listen on a key pattern with Hash values, you can exclude the `HDEL` command so no data deletions will propagate to the downstream database. If you don't specify this attribute, Write-behind acts on all relevant commands.\n- The `trigger` attribute is mandatory and must be set to `write-behind`.\n\n- The `row_format` attribute can be used with the value `full` to receive both the `before` and `after` sections of the payload. Note that for write-behind events the `before` value of the key is never provided.\n\n > Note: Write-behind does not support the [`expired`](https://redis.io/docs/latest/develop/pubsub/keyspace-notifications#events-generated-by-different-commands) event. Therefore, keys that are expired in Redis will not be deleted from the target database automatically.\n> Notes: The `redis` attribute is a breaking change replacing the `keyspace` attribute. The `key_pattern` attribute replaces the `pattern` attribute. The `exclude_commands` attributes replaces the `exclude-commands` attribute. If you upgrade to version 0.105 and beyond, you must edit your existing jobs and redeploy them."},{"id":"output-section","title":"Output section","role":"returns","text":"The `output` section is critical. It specifies a reference to a connection from the `config.yaml` `connections` section:\n\n- The `uses` attribute specifies the type of **writer** Write-behind will use to prepare and write the data to the target.\n  In this example, it is `relational.write`, a writer that translates the data into a SQL statement with the specific dialect of the downstream relational database.\n  For a full list of supported writers, see\n  [data transformation block types](https://redis.io/docs/latest/integrate/write-behind/reference/data-transformation-block-types).\n\n- The `schema` attribute specifies the schema/database to use (different database have different names for schema in the object hierarchy).\n\n- The `table` attribute specifies the downstream table to use.\n\n- The `keys` section specifies the field(s) in the table that are the unique constraints in that table.\n\n- The `mapping` section is used to map database columns to Redis fields with different names or to expressions. The mapping can be all Redis data fields or a subset of them.\n\n> Note: The columns used in `keys` will be automatically included, so there's no need to repeat them in the `mapping` section."},{"id":"apply-filters-and-transformations-to-write-behind","title":"Apply filters and transformations to write-behind","role":"content","text":"The Write-behind jobs can apply filters and transformations to the data before it is written to the target. Specify the filters and transformations under the `transform` section.\n\n#### Filters\n\nUse filters to skip some of the data and not apply it to target.\nFilters can apply simple or complex expressions that take as arguments the Redis entry key, fields, and even the change op code (create, delete, update, etc.).\nSee [Filter](https://redis.io/docs/latest/integrate/write-behind/reference/data-transformation-block-types/filter) for more information.\n\n#### Transformations\n\nTransformations manipulate the data in one of the following ways:\n\n- Renaming a field\n- Adding a field\n- Removing a field\n- Mapping source fields to use in output\n\nTo learn more about transformations, see\n[data transformation pipeline](https://redis.io/docs/latest/integrate/write-behind/data-transformation/data-transformation-pipeline)."},{"id":"provide-targets-secrets","title":"Provide target's secrets","role":"content","text":"The target's secrets (such as TLS certificates) can be read from a path on the Redis node's file system. This allows the consumption of secrets injected from secret stores."},{"id":"deploy-the-write-behind-pipeline","title":"Deploy the write-behind pipeline","role":"content","text":"To start the pipeline, run the\n[`deploy`](https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-deploy) command:\n\n[code example]\n\nYou can check that the pipeline is running, receiving, and writing data using the\n[`status`](https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-status) command:\n\n[code example]"},{"id":"monitor-the-write-behind-pipeline","title":"Monitor the write-behind pipeline","role":"content","text":"The Write-behind pipeline collects the following metrics:\n\n| Metric Description                  | Metric in [Prometheus](https://prometheus.io/)                                                      |\n| ----------------------------------- | --------------------------------------------------------------------------------------------------- |\n| Total incoming events by stream     | Calculated as a Prometheus DB query: `sum(pending, rejected, filtered, inserted, updated, deleted)` |\n| Created incoming events by stream   | `rdi_metrics_incoming_entries{data_source:\"…\",operation=\"inserted\"}`                                |\n| Updated incoming events by stream   | `rdi_metrics_incoming_entries{data_source:\"…\",operation=\"updated\"}`                                 |\n| Deleted incoming events by stream   | `rdi_metrics_incoming_entries{data_source:\"…\",operation=\"deleted\"}`                                 |\n| Filtered incoming events by stream  | `rdi_metrics_incoming_entries{data_source:\"…\",operation=\"filtered\"}`                                |\n| Malformed incoming events by stream | `rdi_metrics_incoming_entries{data_source:\"…\",operation=\"rejected\"}`                                |\n| Total events per stream (snapshot)  | `rdi_metrics_stream_size{data_source:\"\"}`                                                           |\n| Time in stream (snapshot)           | `rdi_metrics_stream_last_latency_ms{data_source:\"…\"}`                                               |\n\nTo use the metrics you can either:\n\n- Run the [`status`](https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-status) command:\n\n  [code example]\n\n- Scrape the metrics using Write-behind's Prometheus exporter"},{"id":"upgrading","title":"Upgrading","role":"content","text":"If you need to upgrade Write-behind, you should use the\n[`upgrade`](https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-upgrade) command that provides for a zero downtime upgrade:\n\n[code example]"}],"examples":[{"id":"preparing-the-write-behind-pipeline-ex0","language":"bash","code":"redis-di scaffold --strategy write_behind --dir . --db-type mysql","section_id":"preparing-the-write-behind-pipeline"},{"id":"preparing-the-write-behind-pipeline-ex1","language":"yaml","code":"connections:\n    my-postgres:\n      type: postgresql\n      host: 172.17.0.3\n      port: 5432\n      database: postgres\n      user: postgres\n      password: postgres\n      #query_args:\n      # sslmode: verify-ca\n      # sslrootcert: /opt/work/ssl/ca.crt\n      # sslkey: /opt/work/ssl/client.key\n      # sslcert: /opt/work/ssl/client.crt\n    my-mysql:\n      type: mysql\n      host: 172.17.0.4\n      port: 3306\n      database: test\n      user: test\n      password: test\n      #connect_args:\n      # ssl_ca: /opt/ssl/ca.crt\n      # ssl_cert: /opt/ssl/client.crt\n      # ssl_key: /opt/ssl/client.key","section_id":"preparing-the-write-behind-pipeline"},{"id":"write-behind-jobs-ex0","language":"yaml","code":"source:\n  redis:\n    key_pattern: emp:*\n    trigger: write-behind\n    exclude_commands: [\"json.del\"]\ntransform:\n  - uses: rename_field\n    with:\n      from_field: after.country\n      to_field: after.my_country\noutput:\n  - uses: relational.write\n    with:\n      connection: my-connection\n      schema: my-schema\n      table: my-table\n      keys:\n        - first_name\n        - last_name\n      mapping:\n        - first_name\n        - last_name\n        - address\n        - gender","section_id":"write-behind-jobs"},{"id":"deploy-the-write-behind-pipeline-ex0","language":"bash","code":"redis-di deploy","section_id":"deploy-the-write-behind-pipeline"},{"id":"deploy-the-write-behind-pipeline-ex1","language":"bash","code":"redis-di status","section_id":"deploy-the-write-behind-pipeline"},{"id":"monitor-the-write-behind-pipeline-ex0","language":"bash","code":"redis-di status","section_id":"monitor-the-write-behind-pipeline"},{"id":"upgrading-ex0","language":"bash","code":"redis-di upgrade ...","section_id":"upgrading"}]}
{"schema_version":2,"id":"integrate/write-behind/reference/cli","title":"CLI","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/","summary":"Write-behind CLI reference","aliases":["/integrate/redis-data-integration/write-behind/reference/cli/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2024-09-10T16:00:43+01:00","children":[{"id":"integrate/write-behind/reference/cli/redis-di","summary":"A command line tool to manage & configure Write-behind","title":"redis-di","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di/"},{"id":"integrate/write-behind/reference/cli/redis-di-add-context","summary":"Adds a new context","title":"redis-di add-context","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-add-context/"},{"id":"integrate/write-behind/reference/cli/redis-di-configure","summary":"Configures the Write-behind Database so it is ready to process data","title":"redis-di configure","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-configure/"},{"id":"integrate/write-behind/reference/cli/redis-di-create","summary":"Creates the Write-behind Database instance","title":"redis-di create","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-create/"},{"id":"integrate/write-behind/reference/cli/redis-di-delete","summary":"Deletes Write-behind database permanently","title":"redis-di delete","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-delete/"},{"id":"integrate/write-behind/reference/cli/redis-di-delete-all-contexts","summary":"Deletes all contexts","title":"redis-di delete-all-contexts","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-delete-all-contexts/"},{"id":"integrate/write-behind/reference/cli/redis-di-delete-context","summary":"Deletes a context","title":"redis-di delete-context","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-delete-context/"},{"id":"integrate/write-behind/reference/cli/redis-di-deploy","summary":"Deploys the Write-behind configurations including target","title":"redis-di deploy","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-deploy/"},{"id":"integrate/write-behind/reference/cli/redis-di-describe-job","summary":"Describes a transformation engine's job","title":"redis-di describe-job","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-describe-job/"},{"id":"integrate/write-behind/reference/cli/redis-di-dump-support-package","summary":"Dumps Write-behind support package","title":"redis-di dump-support-package","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-dump-support-package/"},{"id":"integrate/write-behind/reference/cli/redis-di-get-rejected","summary":"Returns all the stored rejected entries","title":"redis-di get-rejected","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-get-rejected/"},{"id":"integrate/write-behind/reference/cli/redis-di-list-contexts","summary":"Lists all saved contexts","title":"redis-di list-contexts","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-list-contexts/"},{"id":"integrate/write-behind/reference/cli/redis-di-list-jobs","summary":"Lists transformation engine's jobs","title":"redis-di list-jobs","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-list-jobs/"},{"id":"integrate/write-behind/reference/cli/redis-di-monitor","summary":"Monitors Write-behind by collecting metrics and exporting to Prometheus","title":"redis-di monitor","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-monitor/"},{"id":"integrate/write-behind/reference/cli/redis-di-reset","summary":"Resets the pipeline into initial full sync mode","title":"redis-di reset","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-reset/"},{"id":"integrate/write-behind/reference/cli/redis-di-scaffold","summary":"Generates configuration files for Write-behind and Debezium (when ingesting data to Redis)","title":"redis-di scaffold","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-scaffold/"},{"id":"integrate/write-behind/reference/cli/redis-di-set-context","summary":"Sets a context to be the active one","title":"redis-di set-context","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-set-context/"},{"id":"integrate/write-behind/reference/cli/redis-di-set-secret","summary":"Writes a secret to Redis secret store","title":"redis-di set-secret","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-set-secret/"},{"id":"integrate/write-behind/reference/cli/redis-di-start","summary":"Starts the pipeline","title":"redis-di start","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-start/"},{"id":"integrate/write-behind/reference/cli/redis-di-status","summary":"Displays the status of the pipeline end to end","title":"redis-di status","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-status/"},{"id":"integrate/write-behind/reference/cli/redis-di-stop","summary":"Stops the pipeline","title":"redis-di stop","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-stop/"},{"id":"integrate/write-behind/reference/cli/redis-di-trace","summary":"Starts a trace session for troubleshooting data transformation","title":"redis-di trace","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-trace/"},{"id":"integrate/write-behind/reference/cli/redis-di-upgrade","summary":"Upgrades Write-behind Engine without losing data or downtime","title":"redis-di upgrade","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-upgrade/"}],"page_type":"index","sections":[],"examples":[]}
{"schema_version":2,"id":"integrate/write-behind/reference/cli/redis-di","title":"redis-di","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di/","summary":"A command line tool to manage & configure Write-behind","aliases":["/integrate/redis-data-integration/write-behind/reference/cli/redis-di/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"fcc3e8c729da2aecb5321b13d6b26a3dd7989198a118d385536803da4269b6ba","sections":[{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"- `version`:\n\n  - Type: BOOL\n  - Default: `false`\n  - Usage: `--version`\n\n  Show the version and exit.\n\n- `help`:\n\n  - Type: BOOL\n  - Default: `false`\n  - Usage: `--help`\n\n  Show this message and exit."},{"id":"cli-help","title":"CLI help","role":"content","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"Usage: redis-di [OPTIONS] COMMAND [ARGS]...","section_id":"usage"},{"id":"cli-help-ex0","language":"plaintext","code":"Usage: redis-di [OPTIONS] COMMAND [ARGS]...\n\n  A command line tool to manage & configure Write-behind\n\nOptions:\n  --version  Show the version and exit.\n  --help     Show this message and exit.\n\nCommands:\n  add-context           Adds a new context\n  configure             Configures the Write-behind Database so it is ready to...\n  create                Creates the Write-behind Database instance\n  delete                Deletes Write-behind database permanently\n  delete-all-contexts   Deletes all contexts\n  delete-context        Deletes a context\n  deploy                Deploys the Write-behind configurations including target\n  describe-job          Describes a transformation engine's job\n  dump-support-package  Dumps Write-behind support package\n  get-rejected          Returns all the stored rejected entries\n  list-contexts         Lists all saved contexts\n  list-jobs             Lists transformation engine's jobs\n  monitor               Monitors Write-behind by collecting metrics and exporting...\n  reset                 Resets the pipeline into initial full sync mode\n  scaffold              Generates configuration files for Write-behind and...\n  set-context           Sets a context to be the active one\n  set-secret            Writes a secret to Redis secret store\n  start                 Starts the pipeline\n  status                Displays the status of the pipeline end to end\n  stop                  Stops the pipeline\n  trace                 Starts a trace session for troubleshooting data...\n  upgrade               Upgrades Write-behind Engine without losing data or downtime","section_id":"cli-help"}]}
{"schema_version":2,"id":"integrate/write-behind/reference/cli/redis-di-add-context","title":"redis-di add-context","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-add-context/","summary":"Adds a new context","aliases":["/integrate/redis-data-integration/write-behind/reference/cli/redis-di-add-context/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"8ab0e8eebc4e89ac9c9056723b45e5cb43fa42950abb091ceeb03d62c874af01","sections":[{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"- `context_name` (REQUIRED):\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `context-name`\n\n- `loglevel`:\n\n  - Type: Choice(['DEBUG', 'INFO', 'WARN', 'ERROR', 'CRITICAL'])\n  - Default: `info`\n  - Usage: `--loglevel\n-log-level`\n\n- `cluster_host` (REQUIRED):\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--cluster-host`\n\n  Host/IP of Redis Enterprise Cluster (service name in case of k8s)\n\n- `cluster_api_port` (REQUIRED):\n\n  - Type: <IntRange 1000<=x<=65535>\n  - Default: `9443`\n  - Usage: `--cluster-api-port`\n\n  API Port of Redis Enterprise Cluster\n\n- `cluster_user` (REQUIRED):\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--cluster-user`\n\n  Redis Enterprise Cluster username with either DB Member, Cluster Member or Cluster Admin roles\n\n- `rdi_host` (REQUIRED):\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-host`\n\n  Host/IP of Write-behind Database\n\n- `rdi_port` (REQUIRED):\n\n  - Type: <IntRange 1000<=x<=65535>\n  - Default: `none`\n  - Usage: `--rdi-port`\n\n  Port of Write-behind Database\n\n- `rdi_key`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-key`\n\n  Private key file to authenticate with\n\n- `rdi_cert`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-cert`\n\n  Client certificate file to authenticate with\n\n- `rdi_cacert`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-cacert`\n\n  CA certificate file to verify with\n\n- `help`:\n\n  - Type: BOOL\n  - Default: `false`\n  - Usage: `--help`\n\n  Show this message and exit."},{"id":"cli-help","title":"CLI help","role":"content","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"Usage: redis-di add-context [OPTIONS] CONTEXT_NAME","section_id":"usage"},{"id":"cli-help-ex0","language":"plaintext","code":"Usage: redis-di add-context [OPTIONS] CONTEXT_NAME\n\n  Adds a new context\n\nOptions:\n  -log-level, --loglevel [DEBUG|INFO|WARN|ERROR|CRITICAL]\n                                  [default: INFO]\n  --cluster-host TEXT             Host/IP of Redis Enterprise Cluster (service\n                                  name in case of k8s)  [required]\n  --cluster-api-port INTEGER RANGE\n                                  API Port of Redis Enterprise Cluster\n                                  [default: 9443; 1000<=x<=65535; required]\n  --cluster-user TEXT             Redis Enterprise Cluster username with\n                                  either DB Member, Cluster Member or Cluster\n                                  Admin roles  [required]\n  --rdi-host TEXT                 Host/IP of Write-behind Database  [required]\n  --rdi-port INTEGER RANGE        Port of Write-behind Database  [1000<=x<=65535;\n                                  required]\n  --rdi-key TEXT                  Private key file to authenticate with\n  --rdi-cert TEXT                 Client certificate file to authenticate with\n  --rdi-cacert TEXT               CA certificate file to verify with\n  --help                          Show this message and exit.","section_id":"cli-help"}]}
{"schema_version":2,"id":"integrate/write-behind/reference/cli/redis-di-configure","title":"redis-di configure","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-configure/","summary":"Configures the Write-behind Database so it is ready to process data","aliases":["/integrate/redis-data-integration/reference/cli/redis-di-configure/","/integrate/redis-data-integration/write-behind/reference/cli/redis-di-configure/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"8ce614226fbc64ec9213e91e40f84181001fc05343995a53a9d633451ca1a85a","sections":[{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"- `loglevel`:\n\n  - Type: Choice(['DEBUG', 'INFO', 'WARN', 'ERROR', 'CRITICAL'])\n  - Default: `info`\n  - Usage: `--loglevel\n-log-level`\n\n- `rdi_host` (REQUIRED):\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-host`\n\n  Host/IP of Write-behind Database\n\n- `rdi_port` (REQUIRED):\n\n  - Type: <IntRange 1000<=x<=65535>\n  - Default: `none`\n  - Usage: `--rdi-port`\n\n  Port of Write-behind Database\n\n- `rdi_password`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-password`\n\n  Write-behind Database Password\n\n- `rdi_key`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-key`\n\n  Private key file to authenticate with\n\n- `rdi_cert`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-cert`\n\n  Client certificate file to authenticate with\n\n- `rdi_cacert`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-cacert`\n\n  CA certificate file to verify with\n\n- `rdi_key_password`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-key-password`\n\n  Password for unlocking an encrypted private key\n\n- `help`:\n\n  - Type: BOOL\n  - Default: `false`\n  - Usage: `--help`\n\n  Show this message and exit."},{"id":"cli-help","title":"CLI help","role":"content","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"Usage: redis-di configure [OPTIONS]","section_id":"usage"},{"id":"cli-help-ex0","language":"plaintext","code":"Usage: redis-di configure [OPTIONS]\n\n  Configures the Write-behind Database so it is ready to process data\n\nOptions:\n  -log-level, --loglevel [DEBUG|INFO|WARN|ERROR|CRITICAL]\n                                  [default: INFO]\n  --rdi-host TEXT                 Host/IP of Write-behind Database  [required]\n  --rdi-port INTEGER RANGE        Port of Write-behind Database  [1000<=x<=65535;\n                                  required]\n  --rdi-password TEXT             Write-behind Database Password\n  --rdi-key TEXT                  Private key file to authenticate with\n  --rdi-cert TEXT                 Client certificate file to authenticate with\n  --rdi-cacert TEXT               CA certificate file to verify with\n  --rdi-key-password TEXT         Password for unlocking an encrypted private\n                                  key\n  --help                          Show this message and exit.","section_id":"cli-help"}]}
{"schema_version":2,"id":"integrate/write-behind/reference/cli/redis-di-create","title":"redis-di create","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-create/","summary":"Creates the Write-behind Database instance","aliases":["/integrate/redis-data-integration/reference/cli/redis-di-create/","/integrate/redis-data-integration/write-behind/reference/cli/redis-di-create/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"75c9c74dd8d48880475034e091b7b7f0cd726ef4729182a7e1bdd18342fe86a4","sections":[{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"- `loglevel`:\n\n  - Type: Choice(['DEBUG', 'INFO', 'WARN', 'ERROR', 'CRITICAL'])\n  - Default: `info`\n  - Usage: `--loglevel\n-log-level`\n\n- `silent`:\n\n  - Type: BOOL\n  - Default: `false`\n  - Usage: `--silent`\n\n  Silent install. Do not prompt to enter missing parameters\n\n- `no_configure`:\n\n  - Type: BOOL\n  - Default: `false`\n  - Usage: `--no-configure`\n\n  Do not install Write-behind Engine to the Write-behind Database\n\n- `cluster_host` (REQUIRED):\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--cluster-host`\n\n  Host/IP of Redis Enterprise Cluster (service name in case of k8s)\n\n- `cluster_api_port` (REQUIRED):\n\n  - Type: <IntRange 1000<=x<=65535>\n  - Default: `9443`\n  - Usage: `--cluster-api-port`\n\n  API Port of Redis Enterprise Cluster\n\n- `cluster_user` (REQUIRED):\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--cluster-user`\n\n  Redis Enterprise Cluster username with either DB Member, Cluster Member or Cluster Admin roles\n\n- `cluster_password`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--cluster-password`\n\n  Redis Enterprise Cluster Password\n\n- `rdi_port`:\n\n  - Type: <IntRange 1000<=x<=65535>\n  - Default: `none`\n  - Usage: `--rdi-port`\n\n  Port for the new Write-behind Database\n\n- `rdi_password`:\n\n  - Type: STRING\n  - Default: ``\n  - Usage: `--rdi-password`\n\n  Password for the new Write-behind Database (alphanumeric characters with zero or more of the following: ! & # $ ^ < > -)\n\n- `rdi_memory`:\n\n  - Type: <IntRange x>=30>\n  - Default: `100`\n  - Usage: `--rdi-memory`\n\n  Memory for Write-behind Database (in MB)\n\n- `rdi_shards`:\n\n  - Type: <IntRange x>=1>\n  - Default: `1`\n  - Usage: `--rdi-shards`\n\n  Number of database server-side shards\n\n- `replication`:\n\n  - Type: BOOL\n  - Default: `false`\n  - Usage: `--replication`\n\n  In-memory database replication\n\n- `redisgears_module`:\n\n  - Type: STRING\n  - Default: ``\n  - Usage: `--redisgears-module`\n\n  RedisGears module file\n\n- `with_rejson`:\n\n  - Type: BOOL\n  - Default: `false`\n  - Usage: `--with-rejson`\n\n  Include ReJSON in the Write-behind Database\n\n- `help`:\n\n  - Type: BOOL\n  - Default: `false`\n  - Usage: `--help`\n\n  Show this message and exit."},{"id":"cli-help","title":"CLI help","role":"content","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"Usage: redis-di create [OPTIONS]","section_id":"usage"},{"id":"cli-help-ex0","language":"plaintext","code":"Usage: redis-di create [OPTIONS]\n\n  Creates the Write-behind Database instance\n\nOptions:\n  -log-level, --loglevel [DEBUG|INFO|WARN|ERROR|CRITICAL]\n                                  [default: INFO]\n  --silent                        Silent install. Do not prompt to enter\n                                  missing parameters\n  --no-configure                  Do not install Write-behind Engine to the Write-behind\n                                  Database\n  --cluster-host TEXT             Host/IP of Redis Enterprise Cluster (service\n                                  name in case of k8s)  [required]\n  --cluster-api-port INTEGER RANGE\n                                  API Port of Redis Enterprise Cluster\n                                  [default: 9443; 1000<=x<=65535; required]\n  --cluster-user TEXT             Redis Enterprise Cluster username with\n                                  either DB Member, Cluster Member or Cluster\n                                  Admin roles  [required]\n  --cluster-password TEXT         Redis Enterprise Cluster Password\n  --rdi-port INTEGER RANGE        Port for the new Write-behind Database\n                                  [1000<=x<=65535]\n  --rdi-password TEXT             Password for the new Write-behind Database\n                                  (alphanumeric characters with zero or more\n                                  of the following: ! & # $ ^ < > -)\n  --rdi-memory INTEGER RANGE      Memory for Write-behind Database (in MB)  [x>=30]\n  --rdi-shards INTEGER RANGE      Number of database server-side shards\n                                  [x>=1]\n  --replication                   In-memory database replication\n  --redisgears-module TEXT        RedisGears module file\n  --help                          Show this message and exit.","section_id":"cli-help"}]}
{"schema_version":2,"id":"integrate/write-behind/reference/cli/redis-di-delete","title":"redis-di delete","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-delete/","summary":"Deletes Write-behind database permanently","aliases":["/integrate/redis-data-integration/write-behind/reference/cli/redis-di-delete/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"9c58ea5ed643f82587b224feae722e40512c714270f295ddd5850af2231fc91f","sections":[{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"- `loglevel`:\n\n  - Type: Choice(['DEBUG', 'INFO', 'WARN', 'ERROR', 'CRITICAL'])\n  - Default: `info`\n  - Usage: `--loglevel\n-log-level`\n\n- `cluster_host` (REQUIRED):\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--cluster-host`\n\n  Host/IP of Redis Enterprise Cluster (service name in case of k8s)\n\n- `cluster_api_port` (REQUIRED):\n\n  - Type: <IntRange 1000<=x<=65535>\n  - Default: `9443`\n  - Usage: `--cluster-api-port`\n\n  API Port of Redis Enterprise Cluster\n\n- `cluster_user` (REQUIRED):\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--cluster-user`\n\n  Redis Enterprise Cluster username with either DB Member, Cluster Member or Cluster Admin roles\n\n- `cluster_password`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--cluster-password`\n\n  Redis Enterprise Cluster Password\n\n- `rdi_host` (REQUIRED):\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-host`\n\n  Host/IP of Write-behind Database\n\n- `rdi_port` (REQUIRED):\n\n  - Type: <IntRange 1000<=x<=65535>\n  - Default: `none`\n  - Usage: `--rdi-port`\n\n  Port of Write-behind Database\n\n- `rdi_password`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-password`\n\n  Write-behind Database Password\n\n- `rdi_key`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-key`\n\n  Private key file to authenticate with\n\n- `rdi_cert`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-cert`\n\n  Client certificate file to authenticate with\n\n- `rdi_cacert`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-cacert`\n\n  CA certificate file to verify with\n\n- `rdi_key_password`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-key-password`\n\n  Password for unlocking an encrypted private key\n\n- `force`:\n\n  - Type: BOOL\n  - Default: `false`\n  - Usage: `--force\n-f`\n\n  Force operation. skips verification prompts\n\n- `help`:\n\n  - Type: BOOL\n  - Default: `false`\n  - Usage: `--help`\n\n  Show this message and exit."},{"id":"cli-help","title":"CLI help","role":"content","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"Usage: redis-di delete [OPTIONS]","section_id":"usage"},{"id":"cli-help-ex0","language":"plaintext","code":"Usage: redis-di delete [OPTIONS]\n\n  Deletes Write-behind database permanently\n\nOptions:\n  -log-level, --loglevel [DEBUG|INFO|WARN|ERROR|CRITICAL]\n                                  [default: INFO]\n  --cluster-host TEXT             Host/IP of Redis Enterprise Cluster (service\n                                  name in case of k8s)  [required]\n  --cluster-api-port INTEGER RANGE\n                                  API Port of Redis Enterprise Cluster\n                                  [default: 9443; 1000<=x<=65535; required]\n  --cluster-user TEXT             Redis Enterprise Cluster username with\n                                  either DB Member, Cluster Member or Cluster\n                                  Admin roles  [required]\n  --cluster-password TEXT         Redis Enterprise Cluster Password\n  --rdi-host TEXT                 Host/IP of Write-behind Database  [required]\n  --rdi-port INTEGER RANGE        Port of Write-behind Database  [1000<=x<=65535;\n                                  required]\n  --rdi-password TEXT             Write-behind Database Password\n  --rdi-key TEXT                  Private key file to authenticate with\n  --rdi-cert TEXT                 Client certificate file to authenticate with\n  --rdi-cacert TEXT               CA certificate file to verify with\n  --rdi-key-password TEXT         Password for unlocking an encrypted private\n                                  key\n  -f, --force                     Force operation. skips verification prompts\n  --help                          Show this message and exit.","section_id":"cli-help"}]}
{"schema_version":2,"id":"integrate/write-behind/reference/cli/redis-di-delete-all-contexts","title":"redis-di delete-all-contexts","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-delete-all-contexts/","summary":"Deletes all contexts","aliases":["/integrate/redis-data-integration/write-behind/reference/cli/redis-di-delete-all-contexts/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"6a0e4e246c7f2eaaf9a2fc104dc74ad82cdc5b4bf105198c75c59e54a707ea11","sections":[{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"- `loglevel`:\n\n  - Type: Choice(['DEBUG', 'INFO', 'WARN', 'ERROR', 'CRITICAL'])\n  - Default: `info`\n  - Usage: `--loglevel\n-log-level`\n\n- `force`:\n\n  - Type: BOOL\n  - Default: `false`\n  - Usage: `--force\n-f`\n\n  Force operation. skips verification prompts\n\n- `help`:\n\n  - Type: BOOL\n  - Default: `false`\n  - Usage: `--help`\n\n  Show this message and exit."},{"id":"cli-help","title":"CLI help","role":"content","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"Usage: redis-di delete-all-contexts [OPTIONS]","section_id":"usage"},{"id":"cli-help-ex0","language":"plaintext","code":"Usage: redis-di delete-all-contexts [OPTIONS]\n\n  Deletes all contexts\n\nOptions:\n  -log-level, --loglevel [DEBUG|INFO|WARN|ERROR|CRITICAL]\n                                  [default: INFO]\n  -f, --force                     Force operation. skips verification prompts\n  --help                          Show this message and exit.","section_id":"cli-help"}]}
{"schema_version":2,"id":"integrate/write-behind/reference/cli/redis-di-delete-context","title":"redis-di delete-context","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-delete-context/","summary":"Deletes a context","aliases":["/integrate/redis-data-integration/write-behind/reference/cli/redis-di-delete-context/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"e2022edfa79ffa86ce3e0148c6d0a507168313fe821010ee65a964f4df5c935d","sections":[{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"- `loglevel`:\n\n  - Type: Choice(['DEBUG', 'INFO', 'WARN', 'ERROR', 'CRITICAL'])\n  - Default: `info`\n  - Usage: `--loglevel\n-log-level`\n\n- `context_name` (REQUIRED):\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `context-name`\n\n- `force`:\n\n  - Type: BOOL\n  - Default: `false`\n  - Usage: `--force\n-f`\n\n  Force operation. skips verification prompts\n\n- `help`:\n\n  - Type: BOOL\n  - Default: `false`\n  - Usage: `--help`\n\n  Show this message and exit."},{"id":"cli-help","title":"CLI help","role":"content","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"Usage: redis-di delete-context [OPTIONS] CONTEXT_NAME","section_id":"usage"},{"id":"cli-help-ex0","language":"plaintext","code":"Usage: redis-di delete-context [OPTIONS] CONTEXT_NAME\n\n  Deletes a context\n\nOptions:\n  -log-level, --loglevel [DEBUG|INFO|WARN|ERROR|CRITICAL]\n                                  [default: INFO]\n  -f, --force                     Force operation. skips verification prompts\n  --help                          Show this message and exit.","section_id":"cli-help"}]}
{"schema_version":2,"id":"integrate/write-behind/reference/cli/redis-di-deploy","title":"redis-di deploy","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-deploy/","summary":"Deploys the Write-behind configurations including target","aliases":["/integrate/redis-data-integration/write-behind/reference/cli/redis-di-deploy/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"73865d1fbfdfeb1d1cec1ce8d84b1e967cc3233219caf56373f370dd2a04fb9a","sections":[{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"- `loglevel`:\n\n  - Type: Choice(['DEBUG', 'INFO', 'WARN', 'ERROR', 'CRITICAL'])\n  - Default: `info`\n  - Usage: `--loglevel\n-log-level`\n\n- `rdi_host` (REQUIRED):\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-host`\n\n  Host/IP of Write-behind Database\n\n- `rdi_port` (REQUIRED):\n\n  - Type: <IntRange 1000<=x<=65535>\n  - Default: `none`\n  - Usage: `--rdi-port`\n\n  Port of Write-behind Database\n\n- `rdi_password`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-password`\n\n  Write-behind Database Password\n\n- `rdi_key`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-key`\n\n  Private key file to authenticate with\n\n- `rdi_cert`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-cert`\n\n  Client certificate file to authenticate with\n\n- `rdi_cacert`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-cacert`\n\n  CA certificate file to verify with\n\n- `rdi_key_password`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-key-password`\n\n  Password for unlocking an encrypted private key\n\n- `directory`:\n\n  - Type: STRING\n  - Default: `.`\n  - Usage: `--dir`\n\n  Directory containing Write-behind configuration\n\n- `help`:\n\n  - Type: BOOL\n  - Default: `false`\n  - Usage: `--help`\n\n  Show this message and exit."},{"id":"cli-help","title":"CLI help","role":"content","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"Usage: redis-di deploy [OPTIONS]","section_id":"usage"},{"id":"cli-help-ex0","language":"plaintext","code":"Usage: redis-di deploy [OPTIONS]\n\n  Deploys the Write-behind configurations including target\n\nOptions:\n  -log-level, --loglevel [DEBUG|INFO|WARN|ERROR|CRITICAL]\n                                  [default: INFO]\n  --rdi-host TEXT                 Host/IP of Write-behind Database  [required]\n  --rdi-port INTEGER RANGE        Port of Write-behind Database  [1000<=x<=65535;\n                                  required]\n  --rdi-password TEXT             Write-behind Database Password\n  --rdi-key TEXT                  Private key file to authenticate with\n  --rdi-cert TEXT                 Client certificate file to authenticate with\n  --rdi-cacert TEXT               CA certificate file to verify with\n  --rdi-key-password TEXT         Password for unlocking an encrypted private\n                                  key\n  --dir TEXT                      Directory containing Write-behind configuration\n                                  [default: .]\n  --help                          Show this message and exit.","section_id":"cli-help"}]}
{"schema_version":2,"id":"integrate/write-behind/reference/cli/redis-di-describe-job","title":"redis-di describe-job","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-describe-job/","summary":"Describes a transformation engine's job","aliases":["/integrate/redis-data-integration/write-behind/reference/cli/redis-di-describe-job/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"8e11f006cbde64e7d40907c4e1da89ea3686832dc8fdf6909c8dccceaf6e332b","sections":[{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"- `loglevel`:\n\n  - Type: Choice(['DEBUG', 'INFO', 'WARN', 'ERROR', 'CRITICAL'])\n  - Default: `info`\n  - Usage: `--loglevel\n-log-level`\n\n- `job_name` (REQUIRED):\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `job-name`\n\n- `rdi_host` (REQUIRED):\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-host`\n\n  Host/IP of Write-behind Database\n\n- `rdi_port` (REQUIRED):\n\n  - Type: <IntRange 1000<=x<=65535>\n  - Default: `none`\n  - Usage: `--rdi-port`\n\n  Port of Write-behind Database\n\n- `rdi_password`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-password`\n\n  Write-behind Database Password\n\n- `rdi_key`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-key`\n\n  Private key file to authenticate with\n\n- `rdi_cert`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-cert`\n\n  Client certificate file to authenticate with\n\n- `rdi_cacert`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-cacert`\n\n  CA certificate file to verify with\n\n- `rdi_key_password`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-key-password`\n\n  Password for unlocking an encrypted private key\n\n- `help`:\n\n  - Type: BOOL\n  - Default: `false`\n  - Usage: `--help`\n\n  Show this message and exit."},{"id":"cli-help","title":"CLI help","role":"content","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"Usage: redis-di describe-job [OPTIONS] JOB_NAME","section_id":"usage"},{"id":"cli-help-ex0","language":"plaintext","code":"Usage: redis-di describe-job [OPTIONS] JOB_NAME\n\n  Describes a transformation engine's job\n\nOptions:\n  -log-level, --loglevel [DEBUG|INFO|WARN|ERROR|CRITICAL]\n                                  [default: INFO]\n  --rdi-host TEXT                 Host/IP of Write-behind Database  [required]\n  --rdi-port INTEGER RANGE        Port of Write-behind Database  [1000<=x<=65535;\n                                  required]\n  --rdi-password TEXT             Write-behind Database Password\n  --rdi-key TEXT                  Private key file to authenticate with\n  --rdi-cert TEXT                 Client certificate file to authenticate with\n  --rdi-cacert TEXT               CA certificate file to verify with\n  --rdi-key-password TEXT         Password for unlocking an encrypted private\n                                  key\n  --help                          Show this message and exit.","section_id":"cli-help"}]}
{"schema_version":2,"id":"integrate/write-behind/reference/cli/redis-di-dump-support-package","title":"redis-di dump-support-package","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-dump-support-package/","summary":"Dumps Write-behind support package","aliases":["/integrate/redis-data-integration/write-behind/reference/cli/redis-di-dump-support-package/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"644b255fdfae4290ac47624ccb2d7a30f95dc34f4ddcd85cad8e633a6f4c14d8","sections":[{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"- `loglevel`:\n\n  - Type: Choice(['DEBUG', 'INFO', 'WARN', 'ERROR', 'CRITICAL'])\n  - Default: `info`\n  - Usage: `--loglevel\n-log-level`\n\n- `rdi_host` (REQUIRED):\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-host`\n\n  Host/IP of Write-behind Database\n\n- `rdi_port` (REQUIRED):\n\n  - Type: <IntRange 1000<=x<=65535>\n  - Default: `none`\n  - Usage: `--rdi-port`\n\n  Port of Write-behind Database\n\n- `rdi_password`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-password`\n\n  Write-behind Database Password\n\n- `rdi_key`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-key`\n\n  Private key file to authenticate with\n\n- `rdi_cert`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-cert`\n\n  Client certificate file to authenticate with\n\n- `rdi_cacert`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-cacert`\n\n  CA certificate file to verify with\n\n- `rdi_key_password`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-key-password`\n\n  Password for unlocking an encrypted private key\n\n- `directory`:\n\n  - Type: STRING\n  - Default: `.`\n  - Usage: `--dir`\n\n  Directory where the support file should be generated\n\n- `dump_rejected`:\n\n  - Type: INT\n  - Default: `none`\n  - Usage: `--dump-rejected`\n\n  Dumps rejected records\n\n- `trace_timeout`:\n\n  - Type: <IntRange 1<=x<=600>\n  - Default: `none`\n  - Usage: `--trace-timeout`\n\n  Stops the trace after exceeding this timeout (in seconds)\n\n- `max_change_records`:\n\n  - Type: <IntRange x>=1>\n  - Default: `10`\n  - Usage: `--max-change-records`\n\n  Maximum traced change records per shard\n\n- `trace_only_rejected`:\n\n  - Type: BOOL\n  - Default: `false`\n  - Usage: `--trace-only-rejected`\n\n  Trace only rejected change records\n\n- `help`:\n\n  - Type: BOOL\n  - Default: `false`\n  - Usage: `--help`\n\n  Show this message and exit."},{"id":"cli-help","title":"CLI help","role":"content","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"Usage: redis-di dump-support-package [OPTIONS]","section_id":"usage"},{"id":"cli-help-ex0","language":"plaintext","code":"Usage: redis-di dump-support-package [OPTIONS]\n\n  Dumps Write-behind support package\n\nOptions:\n  -log-level, --loglevel [DEBUG|INFO|WARN|ERROR|CRITICAL]\n                                  [default: INFO]\n  --rdi-host TEXT                 Host/IP of Write-behind Database  [required]\n  --rdi-port INTEGER RANGE        Port of Write-behind Database  [1000<=x<=65535;\n                                  required]\n  --rdi-password TEXT             Write-behind Database Password\n  --rdi-key TEXT                  Private key file to authenticate with\n  --rdi-cert TEXT                 Client certificate file to authenticate with\n  --rdi-cacert TEXT               CA certificate file to verify with\n  --rdi-key-password TEXT         Password for unlocking an encrypted private\n                                  key\n  --dir TEXT                      Directory where the support file should be\n                                  generated  [default: .]\n  --dump-rejected INTEGER         Dumps rejected records\n  --trace-timeout INTEGER RANGE   Stops the trace after exceeding this timeout\n                                  (in seconds)  [1<=x<=600]\n  --max-change-records INTEGER RANGE\n                                  Maximum traced change records per shard\n                                  [x>=1]\n  --trace-only-rejected           Trace only rejected change records\n  --help                          Show this message and exit.","section_id":"cli-help"}]}
{"schema_version":2,"id":"integrate/write-behind/reference/cli/redis-di-get-rejected","title":"redis-di get-rejected","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-get-rejected/","summary":"Returns all the stored rejected entries","aliases":["/integrate/redis-data-integration/write-behind/reference/cli/redis-di-get-rejected/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"f4e16d4627c3046ca0385886f9e7aba60e36d7406a55a40dbff2de242c398472","sections":[{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"- `loglevel`:\n\n  - Type: Choice(['DEBUG', 'INFO', 'WARN', 'ERROR', 'CRITICAL'])\n  - Default: `info`\n  - Usage: `--loglevel\n-log-level`\n\n- `rdi_host` (REQUIRED):\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-host`\n\n  Host/IP of Write-behind Database\n\n- `rdi_port` (REQUIRED):\n\n  - Type: <IntRange 1000<=x<=65535>\n  - Default: `none`\n  - Usage: `--rdi-port`\n\n  Port of Write-behind Database\n\n- `rdi_password`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-password`\n\n  Write-behind Database Password\n\n- `rdi_key`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-key`\n\n  Private key file to authenticate with\n\n- `rdi_cert`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-cert`\n\n  Client certificate file to authenticate with\n\n- `rdi_cacert`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-cacert`\n\n  CA certificate file to verify with\n\n- `rdi_key_password`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-key-password`\n\n  Password for unlocking an encrypted private key\n\n- `max_records`:\n\n  - Type: <IntRange x>=1>\n  - Default: `none`\n  - Usage: `--max-records`\n\n  Maximum rejected records per DLQ\n\n- `oldest`:\n\n  - Type: BOOL\n  - Default: `false`\n  - Usage: `--oldest\n-o`\n\n  Displays the oldest rejected records. If omitted, most resent records will be retrieved\n\n- `dlq_name`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--dlq-name`\n\n  Only prints the rejected records for the specified DLQ (Dead Letter Queue) name\n\n- `help`:\n\n  - Type: BOOL\n  - Default: `false`\n  - Usage: `--help`\n\n  Show this message and exit."},{"id":"cli-help","title":"CLI help","role":"content","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"Usage: redis-di get-rejected [OPTIONS]","section_id":"usage"},{"id":"cli-help-ex0","language":"plaintext","code":"Usage: redis-di get-rejected [OPTIONS]\n\n  Returns all the stored rejected entries\n\nOptions:\n  -log-level, --loglevel [DEBUG|INFO|WARN|ERROR|CRITICAL]\n                                  [default: INFO]\n  --rdi-host TEXT                 Host/IP of Write-behind Database  [required]\n  --rdi-port INTEGER RANGE        Port of Write-behind Database  [1000<=x<=65535;\n                                  required]\n  --rdi-password TEXT             Write-behind Database Password\n  --rdi-key TEXT                  Private key file to authenticate with\n  --rdi-cert TEXT                 Client certificate file to authenticate with\n  --rdi-cacert TEXT               CA certificate file to verify with\n  --rdi-key-password TEXT         Password for unlocking an encrypted private\n                                  key\n  --max-records INTEGER RANGE     Maximum rejected records per DLQ  [x>=1]\n  -o, --oldest                    Displays the oldest rejected records. If\n                                  omitted, most resent records will be\n                                  retrieved\n  --dlq-name TEXT                 Only prints the rejected records for the\n                                  specified DLQ (Dead Letter Queue) name\n  --help                          Show this message and exit.","section_id":"cli-help"}]}
{"schema_version":2,"id":"integrate/write-behind/reference/cli/redis-di-list-contexts","title":"redis-di list-contexts","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-list-contexts/","summary":"Lists all saved contexts","aliases":["/integrate/redis-data-integration/write-behind/reference/cli/redis-di-list-contexts/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"716c4b98290f7634130703410eba45396080b310c54f1c8d9f027be33c009144","sections":[{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"- `help`:\n\n  - Type: BOOL\n  - Default: `false`\n  - Usage: `--help`\n\n  Show this message and exit."},{"id":"cli-help","title":"CLI help","role":"content","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"Usage: redis-di list-contexts [OPTIONS]","section_id":"usage"},{"id":"cli-help-ex0","language":"plaintext","code":"Usage: redis-di list-contexts [OPTIONS]\n\n  Lists all saved contexts\n\nOptions:\n  --help  Show this message and exit.","section_id":"cli-help"}]}
{"schema_version":2,"id":"integrate/write-behind/reference/cli/redis-di-list-jobs","title":"redis-di list-jobs","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-list-jobs/","summary":"Lists transformation engine's jobs","aliases":["/integrate/redis-data-integration/write-behind/reference/cli/redis-di-list-jobs/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"5d4a0fa01cd18391ad969765370373580b4011a2222f4c59383520eb6d769352","sections":[{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"- `loglevel`:\n\n  - Type: Choice(['DEBUG', 'INFO', 'WARN', 'ERROR', 'CRITICAL'])\n  - Default: `info`\n  - Usage: `--loglevel\n-log-level`\n\n- `rdi_host` (REQUIRED):\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-host`\n\n  Host/IP of Write-behind Database\n\n- `rdi_port` (REQUIRED):\n\n  - Type: <IntRange 1000<=x<=65535>\n  - Default: `none`\n  - Usage: `--rdi-port`\n\n  Port of Write-behind Database\n\n- `rdi_password`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-password`\n\n  Write-behind Database Password\n\n- `rdi_key`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-key`\n\n  Private key file to authenticate with\n\n- `rdi_cert`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-cert`\n\n  Client certificate file to authenticate with\n\n- `rdi_cacert`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-cacert`\n\n  CA certificate file to verify with\n\n- `rdi_key_password`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-key-password`\n\n  Password for unlocking an encrypted private key\n\n- `help`:\n\n  - Type: BOOL\n  - Default: `false`\n  - Usage: `--help`\n\n  Show this message and exit."},{"id":"cli-help","title":"CLI help","role":"content","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"Usage: redis-di list-jobs [OPTIONS]","section_id":"usage"},{"id":"cli-help-ex0","language":"plaintext","code":"Usage: redis-di list-jobs [OPTIONS]\n\n  Lists transformation engine's jobs\n\nOptions:\n  -log-level, --loglevel [DEBUG|INFO|WARN|ERROR|CRITICAL]\n                                  [default: INFO]\n  --rdi-host TEXT                 Host/IP of Write-behind Database  [required]\n  --rdi-port INTEGER RANGE        Port of Write-behind Database  [1000<=x<=65535;\n                                  required]\n  --rdi-password TEXT             Write-behind Database Password\n  --rdi-key TEXT                  Private key file to authenticate with\n  --rdi-cert TEXT                 Client certificate file to authenticate with\n  --rdi-cacert TEXT               CA certificate file to verify with\n  --rdi-key-password TEXT         Password for unlocking an encrypted private\n                                  key\n  --help                          Show this message and exit.","section_id":"cli-help"}]}
{"schema_version":2,"id":"integrate/write-behind/reference/cli/redis-di-monitor","title":"redis-di monitor","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-monitor/","summary":"Monitors Write-behind by collecting metrics and exporting to Prometheus","aliases":["/integrate/redis-data-integration/write-behind/reference/cli/redis-di-monitor/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"36c8dd8f6e989ec0844e35c2bb1ec3862bc98c57546041ee30c882b31d0d52bb","sections":[{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"- `loglevel`:\n\n  - Type: Choice(['DEBUG', 'INFO', 'WARN', 'ERROR', 'CRITICAL'])\n  - Default: `info`\n  - Usage: `--loglevel\n-log-level`\n\n- `rdi_host` (REQUIRED):\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-host`\n\n  Host/IP of Write-behind Database\n\n- `rdi_port` (REQUIRED):\n\n  - Type: <IntRange 1000<=x<=65535>\n  - Default: `none`\n  - Usage: `--rdi-port`\n\n  Port of Write-behind Database\n\n- `rdi_password`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-password`\n\n  Write-behind Database Password\n\n- `rdi_key`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-key`\n\n  Private key file to authenticate with\n\n- `rdi_cert`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-cert`\n\n  Client certificate file to authenticate with\n\n- `rdi_cacert`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-cacert`\n\n  CA certificate file to verify with\n\n- `rdi_key_password`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-key-password`\n\n  Password for unlocking an encrypted private key\n\n- `exporter_port`:\n\n  - Type: <IntRange 1000<=x<=65535>\n  - Default: `9121`\n  - Usage: `--exporter-port`\n\n  HTTP port to start the exporter on\n\n- `collect_interval`:\n\n  - Type: <IntRange 1<=x<=60>\n  - Default: `5`\n  - Usage: `--collect-interval`\n\n  Metrics collection interval (seconds)\n\n- `help`:\n\n  - Type: BOOL\n  - Default: `false`\n  - Usage: `--help`\n\n  Show this message and exit."},{"id":"cli-help","title":"CLI help","role":"content","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"Usage: redis-di monitor [OPTIONS]","section_id":"usage"},{"id":"cli-help-ex0","language":"plaintext","code":"Usage: redis-di monitor [OPTIONS]\n\n  Monitors Write-behind by collecting metrics and exporting to Prometheus\n\nOptions:\n  -log-level, --loglevel [DEBUG|INFO|WARN|ERROR|CRITICAL]\n                                  [default: INFO]\n  --rdi-host TEXT                 Host/IP of Write-behind Database  [required]\n  --rdi-port INTEGER RANGE        Port of Write-behind Database  [1000<=x<=65535;\n                                  required]\n  --rdi-password TEXT             Write-behind Database Password\n  --rdi-key TEXT                  Private key file to authenticate with\n  --rdi-cert TEXT                 Client certificate file to authenticate with\n  --rdi-cacert TEXT               CA certificate file to verify with\n  --rdi-key-password TEXT         Password for unlocking an encrypted private\n                                  key\n  --exporter-port INTEGER RANGE   HTTP port to start the exporter on\n                                  [1000<=x<=65535]\n  --collect-interval INTEGER RANGE\n                                  Metrics collection interval (seconds)\n                                  [1<=x<=60]\n  --help                          Show this message and exit.","section_id":"cli-help"}]}
{"schema_version":2,"id":"integrate/write-behind/reference/cli/redis-di-reset","title":"redis-di reset","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-reset/","summary":"Resets the pipeline into initial full sync mode","aliases":["/integrate/redis-data-integration/write-behind/reference/cli/redis-di-reset/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"6a8294de64943b058785a672b73c2ce6acf5622042defc076413ff132b74ce2b","sections":[{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"- `loglevel`:\n\n  - Type: Choice(['DEBUG', 'INFO', 'WARN', 'ERROR', 'CRITICAL'])\n  - Default: `info`\n  - Usage: `--loglevel\n-log-level`\n\n- `rdi_host` (REQUIRED):\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-host`\n\n  Host/IP of Write-behind Database\n\n- `rdi_port` (REQUIRED):\n\n  - Type: <IntRange 1000<=x<=65535>\n  - Default: `none`\n  - Usage: `--rdi-port`\n\n  Port of Write-behind Database\n\n- `rdi_password`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-password`\n\n  Write-behind Database Password\n\n- `rdi_key`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-key`\n\n  Private key file to authenticate with\n\n- `rdi_cert`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-cert`\n\n  Client certificate file to authenticate with\n\n- `rdi_cacert`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-cacert`\n\n  CA certificate file to verify with\n\n- `rdi_key_password`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-key-password`\n\n  Password for unlocking an encrypted private key\n\n- `force`:\n\n  - Type: BOOL\n  - Default: `false`\n  - Usage: `--force\n-f`\n\n  Force operation. skips verification prompts\n\n- `help`:\n\n  - Type: BOOL\n  - Default: `false`\n  - Usage: `--help`\n\n  Show this message and exit."},{"id":"cli-help","title":"CLI help","role":"content","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"Usage: redis-di reset [OPTIONS]","section_id":"usage"},{"id":"cli-help-ex0","language":"plaintext","code":"Usage: redis-di reset [OPTIONS]\n\n  Resets the pipeline into initial full sync mode\n\nOptions:\n  -log-level, --loglevel [DEBUG|INFO|WARN|ERROR|CRITICAL]\n                                  [default: INFO]\n  --rdi-host TEXT                 Host/IP of Write-behind Database  [required]\n  --rdi-port INTEGER RANGE        Port of Write-behind Database  [1000<=x<=65535;\n                                  required]\n  --rdi-password TEXT             Write-behind Database Password\n  --rdi-key TEXT                  Private key file to authenticate with\n  --rdi-cert TEXT                 Client certificate file to authenticate with\n  --rdi-cacert TEXT               CA certificate file to verify with\n  --rdi-key-password TEXT         Password for unlocking an encrypted private\n                                  key\n  -f, --force                     Force operation. skips verification prompts\n  --help                          Show this message and exit.","section_id":"cli-help"}]}
{"schema_version":2,"id":"integrate/write-behind/reference/cli/redis-di-scaffold","title":"redis-di scaffold","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-scaffold/","summary":"Generates configuration files for Write-behind and Debezium (when ingesting data to Redis)","aliases":["/integrate/redis-data-integration/write-behind/reference/cli/redis-di-scaffold/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"bc7df2c5c3f09d41a0b2797543718e228f6c2e3df831cee7346e343e54e04619","sections":[{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"- `loglevel`:\n\n  - Type: Choice(['DEBUG', 'INFO', 'WARN', 'ERROR', 'CRITICAL'])\n  - Default: `info`\n  - Usage: `--loglevel\n-log-level`\n\n- `db_type` (REQUIRED):\n\n  - Type: Choice([<DbType.MYSQL: 'mysql'>, <DbType.ORACLE: 'oracle'>, <DbType.POSTGRESQL: 'postgresql'>, <DbType.REDIS: 'redis'>, <DbType.SQLSERVER: 'sqlserver'>])\n  - Default: `none`\n  - Usage: `--db-type`\n\n  DB type\n\n- `strategy`:\n\n  - Type: Choice([<Strategy.INGEST: 'ingest'>, <Strategy.WRITE_BEHIND: 'write_behind'>])\n  - Default: `ingest`\n  - Usage: `--strategy`\n\n  Strategy\n\n  Output to directory or stdout\n\n- `directory`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--dir`\n\n  Directory containing Write-behind configuration\n\n- `preview`:\n\n  - Type: Choice(['debezium/application.properties', 'config.yaml'])\n  - Default: `none`\n  - Usage: `--preview`\n\n  Print the content of specified config file to CLI output\n\n- `help`:\n\n  - Type: BOOL\n  - Default: `false`\n  - Usage: `--help`\n\n  Show this message and exit."},{"id":"cli-help","title":"CLI help","role":"content","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"Usage: redis-di scaffold [OPTIONS]","section_id":"usage"},{"id":"cli-help-ex0","language":"plaintext","code":"Usage: redis-di scaffold [OPTIONS]\n\n  Generates configuration files for Write-behind and Debezium (when ingesting data to\n  Redis)\n\nOptions:\n  -log-level, --loglevel [DEBUG|INFO|WARN|ERROR|CRITICAL]\n                                  [default: INFO]\n  --db-type [mysql|oracle|postgresql|redis|sqlserver]\n                                  DB type  [required]\n  --strategy [ingest|write_behind]\n                                  Strategy  [default: ingest]\n  Output formats: [mutually_exclusive, required]\n                                  Output to directory or stdout\n    --dir TEXT                    Directory containing Write-behind configuration\n    --preview [debezium/application.properties|config.yaml]\n                                  Print the content of specified config file\n                                  to CLI output\n  --help                          Show this message and exit.","section_id":"cli-help"}]}
{"schema_version":2,"id":"integrate/write-behind/reference/cli/redis-di-set-context","title":"redis-di set-context","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-set-context/","summary":"Sets a context to be the active one","aliases":["/integrate/redis-data-integration/write-behind/reference/cli/redis-di-set-context/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"2836e15611f88bf6b58b9a6520bc4b95d8626a6bc681cf2ba9546e89a0e6ee74","sections":[{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"- `loglevel`:\n\n  - Type: Choice(['DEBUG', 'INFO', 'WARN', 'ERROR', 'CRITICAL'])\n  - Default: `info`\n  - Usage: `--loglevel\n-log-level`\n\n- `context_name` (REQUIRED):\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `context-name`\n\n- `help`:\n\n  - Type: BOOL\n  - Default: `false`\n  - Usage: `--help`\n\n  Show this message and exit."},{"id":"cli-help","title":"CLI help","role":"content","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"Usage: redis-di set-context [OPTIONS] CONTEXT_NAME","section_id":"usage"},{"id":"cli-help-ex0","language":"plaintext","code":"Usage: redis-di set-context [OPTIONS] CONTEXT_NAME\n\n  Sets a context to be the active one\n\nOptions:\n  -log-level, --loglevel [DEBUG|INFO|WARN|ERROR|CRITICAL]\n                                  [default: INFO]\n  --help                          Show this message and exit.","section_id":"cli-help"}]}
{"schema_version":2,"id":"integrate/write-behind/reference/cli/redis-di-set-secret","title":"redis-di set-secret","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-set-secret/","summary":"Writes a secret to Redis secret store","aliases":["/integrate/redis-data-integration/write-behind/reference/cli/redis-di-set-secret/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"f398d6530b8bcb14a90900d96605af62ad1794b296ea7caf308d03dc48009fa2","sections":[{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"- `loglevel`:\n\n  - Type: Choice(['DEBUG', 'INFO', 'WARN', 'ERROR', 'CRITICAL'])\n  - Default: `info`\n  - Usage: `--loglevel\n-log-level`\n\n- `cluster_host` (REQUIRED):\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--cluster-host`\n\n  Host/IP of Redis Enterprise Cluster (service name in case of k8s)\n\n- `cluster_api_port` (REQUIRED):\n\n  - Type: <IntRange 1000<=x<=65535>\n  - Default: `9443`\n  - Usage: `--cluster-api-port`\n\n  API Port of Redis Enterprise Cluster\n\n- `cluster_user` (REQUIRED):\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--cluster-user`\n\n  Redis Enterprise Cluster username with either DB Member, Cluster Member or Cluster Admin roles\n\n- `cluster_password`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--cluster-password`\n\n  Redis Enterprise Cluster Password\n\n- `rdi_host` (REQUIRED):\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-host`\n\n  Host/IP of Write-behind Database\n\n- `rdi_port` (REQUIRED):\n\n  - Type: <IntRange 1000<=x<=65535>\n  - Default: `none`\n  - Usage: `--rdi-port`\n\n  Port of Write-behind Database\n\n- `rdi_password`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-password`\n\n  Write-behind Database Password\n\n- `rdi_key`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-key`\n\n  Private key file to authenticate with\n\n- `rdi_cert`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-cert`\n\n  Client certificate file to authenticate with\n\n- `rdi_cacert`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-cacert`\n\n  CA certificate file to verify with\n\n- `rdi_key_password`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-key-password`\n\n  Password for unlocking an encrypted private key\n\n- `secret_name` (REQUIRED):\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--secret-name`\n\n  The name of the secret\n\n- `secret_value` (REQUIRED):\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--secret-value`\n\n  The value of the secret\n\n- `help`:\n\n  - Type: BOOL\n  - Default: `false`\n  - Usage: `--help`\n\n  Show this message and exit."},{"id":"cli-help","title":"CLI help","role":"content","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"Usage: redis-di set-secret [OPTIONS]","section_id":"usage"},{"id":"cli-help-ex0","language":"plaintext","code":"Usage: redis-di set-secret [OPTIONS]\n\n  Writes a secret to Redis secret store\n\nOptions:\n  -log-level, --loglevel [DEBUG|INFO|WARN|ERROR|CRITICAL]\n                                  [default: INFO]\n  --cluster-host TEXT             Host/IP of Redis Enterprise Cluster (service\n                                  name in case of k8s)  [required]\n  --cluster-api-port INTEGER RANGE\n                                  API Port of Redis Enterprise Cluster\n                                  [default: 9443; 1000<=x<=65535; required]\n  --cluster-user TEXT             Redis Enterprise Cluster username with\n                                  either DB Member, Cluster Member or Cluster\n                                  Admin roles  [required]\n  --cluster-password TEXT         Redis Enterprise Cluster Password\n  --rdi-host TEXT                 Host/IP of Write-behind Database  [required]\n  --rdi-port INTEGER RANGE        Port of Write-behind Database  [1000<=x<=65535;\n                                  required]\n  --rdi-password TEXT             Write-behind Database Password\n  --rdi-key TEXT                  Private key file to authenticate with\n  --rdi-cert TEXT                 Client certificate file to authenticate with\n  --rdi-cacert TEXT               CA certificate file to verify with\n  --rdi-key-password TEXT         Password for unlocking an encrypted private\n                                  key\n  --secret-name TEXT              The name of the secret  [required]\n  --secret-value TEXT             The value of the secret  [required]\n  --help                          Show this message and exit.","section_id":"cli-help"}]}
{"schema_version":2,"id":"integrate/write-behind/reference/cli/redis-di-start","title":"redis-di start","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-start/","summary":"Starts the pipeline","aliases":["/integrate/redis-data-integration/write-behind/reference/cli/redis-di-start/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"230a2b471a7a8126a06da85cec5eb518a9efd968adcae123421a2d78421a2373","sections":[{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"- `loglevel`:\n\n  - Type: Choice(['DEBUG', 'INFO', 'WARN', 'ERROR', 'CRITICAL'])\n  - Default: `info`\n  - Usage: `--loglevel\n-log-level`\n\n- `rdi_host` (REQUIRED):\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-host`\n\n  Host/IP of Write-behind Database\n\n- `rdi_port` (REQUIRED):\n\n  - Type: <IntRange 1000<=x<=65535>\n  - Default: `none`\n  - Usage: `--rdi-port`\n\n  Port of Write-behind Database\n\n- `rdi_password`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-password`\n\n  Write-behind Database Password\n\n- `rdi_key`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-key`\n\n  Private key file to authenticate with\n\n- `rdi_cert`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-cert`\n\n  Client certificate file to authenticate with\n\n- `rdi_cacert`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-cacert`\n\n  CA certificate file to verify with\n\n- `rdi_key_password`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-key-password`\n\n  Password for unlocking an encrypted private key\n\n- `help`:\n\n  - Type: BOOL\n  - Default: `false`\n  - Usage: `--help`\n\n  Show this message and exit."},{"id":"cli-help","title":"CLI help","role":"content","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"Usage: redis-di start [OPTIONS]","section_id":"usage"},{"id":"cli-help-ex0","language":"plaintext","code":"Usage: redis-di start [OPTIONS]\n\n  Starts the pipeline\n\nOptions:\n  -log-level, --loglevel [DEBUG|INFO|WARN|ERROR|CRITICAL]\n                                  [default: INFO]\n  --rdi-host TEXT                 Host/IP of Write-behind Database  [required]\n  --rdi-port INTEGER RANGE        Port of Write-behind Database  [1000<=x<=65535;\n                                  required]\n  --rdi-password TEXT             Write-behind Database Password\n  --rdi-key TEXT                  Private key file to authenticate with\n  --rdi-cert TEXT                 Client certificate file to authenticate with\n  --rdi-cacert TEXT               CA certificate file to verify with\n  --rdi-key-password TEXT         Password for unlocking an encrypted private\n                                  key\n  --help                          Show this message and exit.","section_id":"cli-help"}]}
{"schema_version":2,"id":"integrate/write-behind/reference/cli/redis-di-status","title":"redis-di status","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-status/","summary":"Displays the status of the pipeline end to end","aliases":["/integrate/redis-data-integration/write-behind/reference/cli/redis-di-status/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"be17a6c5fc3e9cfd245ef301030076da340fdf2b0594571d21a98ed20ba7a460","sections":[{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"- `loglevel`:\n\n  - Type: Choice(['DEBUG', 'INFO', 'WARN', 'ERROR', 'CRITICAL'])\n  - Default: `info`\n  - Usage: `--loglevel\n-log-level`\n\n- `rdi_host` (REQUIRED):\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-host`\n\n  Host/IP of Write-behind Database\n\n- `rdi_port` (REQUIRED):\n\n  - Type: <IntRange 1000<=x<=65535>\n  - Default: `none`\n  - Usage: `--rdi-port`\n\n  Port of Write-behind Database\n\n- `rdi_password`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-password`\n\n  Write-behind Database Password\n\n- `rdi_key`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-key`\n\n  Private key file to authenticate with\n\n- `rdi_cert`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-cert`\n\n  Client certificate file to authenticate with\n\n- `rdi_cacert`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-cacert`\n\n  CA certificate file to verify with\n\n- `rdi_key_password`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-key-password`\n\n  Password for unlocking an encrypted private key\n\n- `live`:\n\n  - Type: BOOL\n  - Default: `false`\n  - Usage: `--live\n-l`\n\n  Live data flow monitoring\n\n- `page_number`:\n\n  - Type: <IntRange x>=1>\n  - Default: `none`\n  - Usage: `--page-number\n-p`\n\n  Set the page number (live mode only)\n\n- `page_size`:\n\n  - Type: <IntRange x>=1>\n  - Default: `none`\n  - Usage: `--page-size\n-s`\n\n  Set the page size (live mode only)\n\n- `ingested_only`:\n\n  - Type: BOOL\n  - Default: `false`\n  - Usage: `--ingested-only\n-i`\n\n  Display ingested data streams (live mode only)\n\n- `help`:\n\n  - Type: BOOL\n  - Default: `false`\n  - Usage: `--help`\n\n  Show this message and exit."},{"id":"cli-help","title":"CLI help","role":"content","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"Usage: redis-di status [OPTIONS]","section_id":"usage"},{"id":"cli-help-ex0","language":"plaintext","code":"Usage: redis-di status [OPTIONS]\n\n  Displays the status of the pipeline end to end\n\nOptions:\n  -log-level, --loglevel [DEBUG|INFO|WARN|ERROR|CRITICAL]\n                                  [default: INFO]\n  --rdi-host TEXT                 Host/IP of Write-behind Database  [required]\n  --rdi-port INTEGER RANGE        Port of Write-behind Database  [1000<=x<=65535;\n                                  required]\n  --rdi-password TEXT             Write-behind Database Password\n  --rdi-key TEXT                  Private key file to authenticate with\n  --rdi-cert TEXT                 Client certificate file to authenticate with\n  --rdi-cacert TEXT               CA certificate file to verify with\n  --rdi-key-password TEXT         Password for unlocking an encrypted private\n                                  key\n  -l, --live                      Live data flow monitoring\n  -p, --page-number INTEGER RANGE\n                                  Set the page number (live mode only)  [x>=1]\n  -s, --page-size INTEGER RANGE   Set the page size (live mode only)  [x>=1]\n  -i, --ingested-only             Display ingested data streams (live mode\n                                  only)\n  --help                          Show this message and exit.","section_id":"cli-help"}]}
{"schema_version":2,"id":"integrate/write-behind/reference/cli/redis-di-stop","title":"redis-di stop","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-stop/","summary":"Stops the pipeline","aliases":["/integrate/redis-data-integration/write-behind/reference/cli/redis-di-stop/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"3fec19b5877c0c1002f7d7ec0dfde2e1364d866cdee0bbd7ac0dfc66baa24aa5","sections":[{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"- `loglevel`:\n\n  - Type: Choice(['DEBUG', 'INFO', 'WARN', 'ERROR', 'CRITICAL'])\n  - Default: `info`\n  - Usage: `--loglevel\n-log-level`\n\n- `rdi_host` (REQUIRED):\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-host`\n\n  Host/IP of Write-behind Database\n\n- `rdi_port` (REQUIRED):\n\n  - Type: <IntRange 1000<=x<=65535>\n  - Default: `none`\n  - Usage: `--rdi-port`\n\n  Port of Write-behind Database\n\n- `rdi_password`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-password`\n\n  Write-behind Database Password\n\n- `rdi_key`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-key`\n\n  Private key file to authenticate with\n\n- `rdi_cert`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-cert`\n\n  Client certificate file to authenticate with\n\n- `rdi_cacert`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-cacert`\n\n  CA certificate file to verify with\n\n- `rdi_key_password`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-key-password`\n\n  Password for unlocking an encrypted private key\n\n- `help`:\n\n  - Type: BOOL\n  - Default: `false`\n  - Usage: `--help`\n\n  Show this message and exit."},{"id":"cli-help","title":"CLI help","role":"content","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"Usage: redis-di stop [OPTIONS]","section_id":"usage"},{"id":"cli-help-ex0","language":"plaintext","code":"Usage: redis-di stop [OPTIONS]\n\n  Stops the pipeline\n\nOptions:\n  -log-level, --loglevel [DEBUG|INFO|WARN|ERROR|CRITICAL]\n                                  [default: INFO]\n  --rdi-host TEXT                 Host/IP of Write-behind Database  [required]\n  --rdi-port INTEGER RANGE        Port of Write-behind Database  [1000<=x<=65535;\n                                  required]\n  --rdi-password TEXT             Write-behind Database Password\n  --rdi-key TEXT                  Private key file to authenticate with\n  --rdi-cert TEXT                 Client certificate file to authenticate with\n  --rdi-cacert TEXT               CA certificate file to verify with\n  --rdi-key-password TEXT         Password for unlocking an encrypted private\n                                  key\n  --help                          Show this message and exit.","section_id":"cli-help"}]}
{"schema_version":2,"id":"integrate/write-behind/reference/cli/redis-di-trace","title":"redis-di trace","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-trace/","summary":"Starts a trace session for troubleshooting data transformation","aliases":["/integrate/redis-data-integration/write-behind/reference/cli/redis-di-trace/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"53bde8ce31df6cb6d76a568d6d67f9694eb42003b8aebc251e9bbc0ae2d88bb3","sections":[{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"- `loglevel`:\n\n  - Type: Choice(['DEBUG', 'INFO', 'WARN', 'ERROR', 'CRITICAL'])\n  - Default: `info`\n  - Usage: `--loglevel\n-log-level`\n\n- `rdi_host` (REQUIRED):\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-host`\n\n  Host/IP of Write-behind Database\n\n- `rdi_port` (REQUIRED):\n\n  - Type: <IntRange 1000<=x<=65535>\n  - Default: `none`\n  - Usage: `--rdi-port`\n\n  Port of Write-behind Database\n\n- `rdi_password`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-password`\n\n  Write-behind Database Password\n\n- `rdi_key`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-key`\n\n  Private key file to authenticate with\n\n- `rdi_cert`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-cert`\n\n  Client certificate file to authenticate with\n\n- `rdi_cacert`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-cacert`\n\n  CA certificate file to verify with\n\n- `rdi_key_password`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-key-password`\n\n  Password for unlocking an encrypted private key\n\n- `max_change_records`:\n\n  - Type: <IntRange x>=1>\n  - Default: `10`\n  - Usage: `--max-change-records`\n\n  Maximum traced change records per shard\n\n- `timeout` (REQUIRED):\n\n  - Type: <IntRange 1<=x<=600>\n  - Default: `20`\n  - Usage: `--timeout`\n\n  Stops the trace after exceeding this timeout (in seconds)\n\n- `trace_only_rejected`:\n\n  - Type: BOOL\n  - Default: `false`\n  - Usage: `--trace-only-rejected`\n\n  Trace only rejected change records\n\n- `help`:\n\n  - Type: BOOL\n  - Default: `false`\n  - Usage: `--help`\n\n  Show this message and exit."},{"id":"cli-help","title":"CLI help","role":"content","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"Usage: redis-di trace [OPTIONS]","section_id":"usage"},{"id":"cli-help-ex0","language":"plaintext","code":"Usage: redis-di trace [OPTIONS]\n\n  Starts a trace session for troubleshooting data transformation\n\nOptions:\n  -log-level, --loglevel [DEBUG|INFO|WARN|ERROR|CRITICAL]\n                                  [default: INFO]\n  --rdi-host TEXT                 Host/IP of Write-behind Database  [required]\n  --rdi-port INTEGER RANGE        Port of Write-behind Database  [1000<=x<=65535;\n                                  required]\n  --rdi-password TEXT             Write-behind Database Password\n  --rdi-key TEXT                  Private key file to authenticate with\n  --rdi-cert TEXT                 Client certificate file to authenticate with\n  --rdi-cacert TEXT               CA certificate file to verify with\n  --rdi-key-password TEXT         Password for unlocking an encrypted private\n                                  key\n  --max-change-records INTEGER RANGE\n                                  Maximum traced change records per shard\n                                  [x>=1]\n  --timeout INTEGER RANGE         Stops the trace after exceeding this timeout\n                                  (in seconds)  [default: 20; 1<=x<=600;\n                                  required]\n  --trace-only-rejected           Trace only rejected change records\n  --help                          Show this message and exit.","section_id":"cli-help"}]}
{"schema_version":2,"id":"integrate/write-behind/reference/cli/redis-di-upgrade","title":"redis-di upgrade","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/redis-di-upgrade/","summary":"Upgrades Write-behind Engine without losing data or downtime","aliases":["/integrate/redis-data-integration/write-behind/reference/cli/redis-di-upgrade/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"b1b5082fe16802de2b2408ca6df8a0aa8c5480766fee435867c73a772ea3c9c9","sections":[{"id":"usage","title":"Usage","role":"syntax","text":"[code example]"},{"id":"options","title":"Options","role":"parameters","text":"- `loglevel`:\n\n  - Type: Choice(['DEBUG', 'INFO', 'WARN', 'ERROR', 'CRITICAL'])\n  - Default: `info`\n  - Usage: `--loglevel\n-log-level`\n\n- `cluster_host` (REQUIRED):\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--cluster-host`\n\n  Host/IP of Redis Enterprise Cluster (service name in case of k8s)\n\n- `cluster_api_port` (REQUIRED):\n\n  - Type: <IntRange 1000<=x<=65535>\n  - Default: `9443`\n  - Usage: `--cluster-api-port`\n\n  API Port of Redis Enterprise Cluster\n\n- `cluster_user` (REQUIRED):\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--cluster-user`\n\n  Redis Enterprise Cluster username with either DB Member, Cluster Member or Cluster Admin roles\n\n- `cluster_password`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--cluster-password`\n\n  Redis Enterprise Cluster Password\n\n- `rdi_host` (REQUIRED):\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-host`\n\n  Host/IP of Write-behind Database\n\n- `rdi_port` (REQUIRED):\n\n  - Type: <IntRange 1000<=x<=65535>\n  - Default: `none`\n  - Usage: `--rdi-port`\n\n  Port of Write-behind Database\n\n- `rdi_password`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-password`\n\n  Write-behind Database Password\n\n- `rdi_key`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-key`\n\n  Private key file to authenticate with\n\n- `rdi_cert`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-cert`\n\n  Client certificate file to authenticate with\n\n- `rdi_cacert`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-cacert`\n\n  CA certificate file to verify with\n\n- `rdi_key_password`:\n\n  - Type: STRING\n  - Default: `none`\n  - Usage: `--rdi-key-password`\n\n  Password for unlocking an encrypted private key\n\n- `force`:\n\n  - Type: BOOL\n  - Default: `false`\n  - Usage: `--force\n-f`\n\n  Force upgrade/downgrade. skips verification prompts\n\n- `help`:\n\n  - Type: BOOL\n  - Default: `false`\n  - Usage: `--help`\n\n  Show this message and exit."},{"id":"cli-help","title":"CLI help","role":"content","text":"[code example]"}],"examples":[{"id":"usage-ex0","language":"plaintext","code":"Usage: redis-di upgrade [OPTIONS]","section_id":"usage"},{"id":"cli-help-ex0","language":"plaintext","code":"Usage: redis-di upgrade [OPTIONS]\n\n  Upgrades Write-behind Engine without losing data or downtime\n\nOptions:\n  -log-level, --loglevel [DEBUG|INFO|WARN|ERROR|CRITICAL]\n                                  [default: INFO]\n  --cluster-host TEXT             Host/IP of Redis Enterprise Cluster (service\n                                  name in case of k8s)  [required]\n  --cluster-api-port INTEGER RANGE\n                                  API Port of Redis Enterprise Cluster\n                                  [default: 9443; 1000<=x<=65535; required]\n  --cluster-user TEXT             Redis Enterprise Cluster username with\n                                  either DB Member, Cluster Member or Cluster\n                                  Admin roles  [required]\n  --cluster-password TEXT         Redis Enterprise Cluster Password\n  --rdi-host TEXT                 Host/IP of Write-behind Database  [required]\n  --rdi-port INTEGER RANGE        Port of Write-behind Database  [1000<=x<=65535;\n                                  required]\n  --rdi-password TEXT             Write-behind Database Password\n  --rdi-key TEXT                  Private key file to authenticate with\n  --rdi-cert TEXT                 Client certificate file to authenticate with\n  --rdi-cacert TEXT               CA certificate file to verify with\n  --rdi-key-password TEXT         Password for unlocking an encrypted private\n                                  key\n  -f, --force                     Force upgrade/downgrade. skips verification\n                                  prompts\n  --help                          Show this message and exit.","section_id":"cli-help"}]}
{"schema_version":2,"id":"integrate/write-behind/reference/config-yaml-reference","title":"Write-behind configuration file","url":"https://redis.io/docs/latest/integrate/write-behind/reference/config-yaml-reference/","summary":"Write-behind configuration file reference","aliases":["/integrate/redis-data-integration/write-behind/reference/config-yaml-reference/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2025-11-24T14:25:56+02:00","page_type":"content","content_hash":"de0006c27cb1b57953a39ba756d92671d5b935f6d18c2357643b4ea73ec44a17","sections":[{"id":"write-behind-configuration-file","title":"Write-behind Configuration File","role":"overview","text":"**Properties**\n\n| Name                                                                                       | Type             | Description | Required |\n| ------------------------------------------------------------------------------------------ | ---------------- | ----------- | -------- |\n| [**applier**](#applier)<br/>(Configuration details of Write-behind Applier Gear) | `object`, `null` |             |          |\n| [**connections**](#connections)                                                            | `object`         |             |          |\n\n<a name=\"applier\"></a>"},{"id":"applier-configuration-details-of-write-behind-applier-gear","title":"applier: Configuration details of Write-behind Applier Gear","role":"content","text":"**Properties**\n\n| Name                                                                                                                                 | Type                | Description                                                                                                                                                                                                      | Required |\n| ------------------------------------------------------------------------------------------------------------------------------------ | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------------ | --- |\n| **on_failed_retry_interval**<br/>(Interval \\(in seconds\\) on which to perform retry on failure)                                      | `integer`, `string` | Default: `5`<br/>Pattern: `^\\${.*}$`<br/>Minimum: `1`<br/>                                                                                                                                                       |          |\n| **read_batch_size**<br/>(The batch size for reading data from source database)                                                       | `integer`, `string` | Default: `2000`<br/>Pattern: `^\\${.*}$`<br/>Minimum: `1`<br/>                                                                                                                                                    |          |\n| **debezium_lob_encoded_placeholder**<br/>(Enable Debezium LOB placeholders)                                                          | `string`            | Default: `\"X19kZWJleml1bV91bmF2YWlsYWJsZV92YWx1ZQ==\"`<br/>                                                                                                                                                       |          |\n| **dedup**<br/>(Enable deduplication mechanism)                                                                                       | `boolean`           | Default: `false`<br/>                                                                                                                                                                                            |          |\n| **dedup_max_size**<br/>(Max size of the deduplication set)                                                                           | `integer`           | Default: `1024`<br/>Minimum: `1`<br/>                                                                                                                                                                            |          |\n| **duration**<br/>(Time \\(in ms\\) after which data will be read from stream even if read_batch_size was not reached)                  | `integer`, `string` | Default: `100`<br/>Pattern: `^\\${.*}$`<br/>Minimum: `1`<br/>                                                                                                                                                     |          |\n| **write_batch_size**<br/>(The batch size for writing data to target Redis database\\. Should be less or equal to the read_batch_size) | `integer`, `string` | Default: `200`<br/>Pattern: `^\\${.*}$`<br/>Minimum: `1`<br/>                                                                                                                                                     |          |\n| **error_handling**<br/>(Error handling strategy: ignore \\- skip, dlq \\- store rejected messages in a dead letter queue)              | `string`            | Default: `\"dlq\"`<br/>Pattern: ``^\\${.\\*}$                                                                                                                                                                        | ignore   | dlq``<br/>   |     |\n| **dlq_max_messages**<br/>(Dead letter queue max messages per stream)                                                                 | `integer`, `string` | Default: `1000`<br/>Pattern: `^\\${.*}$`<br/>Minimum: `1`<br/>                                                                                                                                                    |          |\n| **target_data_type**<br/>(Target data type: hash/json \\- RedisJSON module must be in use in the target DB)                           | `string`            | Default: `\"hash\"`<br/>Pattern: ``^\\${.\\*}$                                                                                                                                                                       | hash     | json``<br/>  |     |\n| **json_update_strategy**<br/>(Target update strategy: replace/merge \\- RedisJSON module must be in use in the target DB)             | `string`            | (DEPRECATED)<br/>Property 'json_update_strategy' will be deprecated in future releases. Use 'on_update' job-level property to define the json update strategy.<br/>Default: `\"replace\"`<br/>Pattern: ``^\\${.\\*}$ | replace  | merge``<br/> |     |\n| **initial_sync_processes**<br/>(Number of processes Write-behind Engine creates to process the initial sync with the source)                  | `integer`, `string` | Default: `4`<br/>Pattern: `^\\${.*}$`<br/>Minimum: `1`<br/>Maximum: `32`<br/>                                                                                                                                     |          |\n| **wait_enabled**<br/>(Checks if the data has been written to the replica shard of the target database)                                                      | `boolean`           | Default: `false`<br/>                                                                                                                                                                                            |          |\n| **wait_timeout**<br/>(Timeout in milliseconds when checking write to the replica shard of the target database)                                              | `integer`, `string` | Default: `1000`<br/>Pattern: `^\\${.*}$`<br/>Minimum: `1`<br/>                                                                                                                                                    |          |\n| **retry_on_replica_failure**<br/>(Ensures that the data has been written to the replica shard and keeps retrying if not)             | `boolean`           | Default: `true`<br/>                                                                                                                                                                                             |          |\n\n**Additional Properties:** not allowed  \n<a name=\"connections\"></a>"},{"id":"connections-connections","title":"connections: Connections","role":"content","text":"**Properties (Pattern)**\n\n| Name                     | Type | Description | Required |\n| ------------------------ | ---- | ----------- | -------- |\n| **\\.\\***                 |      |             |          |\n| **additionalProperties** |      |             |          |"}],"examples":[]}
{"schema_version":2,"id":"integrate/write-behind/reference/data-transformation-block-types/add_field","title":"add_field","url":"https://redis.io/docs/latest/integrate/write-behind/reference/data-transformation-block-types/add_field/","summary":"Add fields to a record","aliases":["/integrate/redis-data-integration/reference/data-transformation-block-types/add_field/","/integrate/redis-data-integration/write-behind/reference/data-transformation-block-types/add_field/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"b820b5d4f6bc43f82b3e06a4cbc90b18c3bafe73191081b98ac79ca58b97f6bf","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Add fields to a record\n\n**Option 1 (alternative):**\nAdd multiple fields\n\n**Properties**\n\n| Name                         | Type       | Description | Required |\n| ---------------------------- | ---------- | ----------- | -------- |\n| [**fields**](#option1fields) | `object[]` | Fields<br/> | yes      |\n\n**Additional Properties:** not allowed\n\n**Example**\n\n[code example]\n\n**Option 2 (alternative):**\nAdd one field\n\n**Properties**\n\n| Name           | Type     | Description                                   | Required |\n| -------------- | -------- | --------------------------------------------- | -------- |\n| **field**      | `string` | Field<br/>                                    | yes      |\n| **expression** | `string` | Expression<br/>                               | yes      |\n| **language**   | `string` | Language<br/>Enum: `\"jmespath\"`, `\"sql\"`<br/> | yes      |\n\n**Additional Properties:** not allowed\n\n**Example**\n\n[code example]\n\n<a name=\"option1fields\"></a>"},{"id":"option-1-fields-array","title":"Option 1: fields\\[\\]: array","role":"parameters","text":"Fields\n\n**Items**\n\n**Item Properties**\n\n| Name           | Type     | Description                                   | Required |\n| -------------- | -------- | --------------------------------------------- | -------- |\n| **field**      | `string` | Field<br/>                                    | yes      |\n| **expression** | `string` | Expression<br/>                               | yes      |\n| **language**   | `string` | Language<br/>Enum: `\"jmespath\"`, `\"sql\"`<br/> | yes      |\n\n**Item Additional Properties:** not allowed\n\n**Example**\n\n[code example]"}],"examples":[{"id":"overview-ex0","language":"yaml","code":"source:\n  server_name: redislabs\n  schema: dbo\n  table: emp\ntransform:\n  - uses: add_field\n    with:\n      fields:\n        - field: name.full_name\n          language: jmespath\n          expression: concat([name.fname, ' ', name.lname])\n        - field: name.fname_upper\n          language: jmespath\n          expression: upper(name.fname)","section_id":"overview"},{"id":"overview-ex1","language":"yaml","code":"source:\n  server_name: redislabs\n  schema: dbo\n  table: emp\ntransform:\n  - uses: add_field\n    with:\n      field: country\n      language: sql\n      expression: country_code || ' - ' || UPPER(country_name)","section_id":"overview"},{"id":"option-1-fields-array-ex0","language":"yaml","code":"- {}","section_id":"option-1-fields-array"}]}
{"schema_version":2,"id":"integrate/write-behind/reference/data-transformation-block-types/cassandra_write","title":"cassandra.write","url":"https://redis.io/docs/latest/integrate/write-behind/reference/data-transformation-block-types/cassandra_write/","summary":"Write into a Cassandra data store","aliases":["/integrate/redis-data-integration/reference/data-transformation-block-types/cassandra_write/","/integrate/redis-data-integration/write-behind/reference/data-transformation-block-types/cassandra_write/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"b57c8feacced0adaa46c955e521a41d18dc7571f71f00944e31b004b4d800ed6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Write into a Cassandra data store\n\n**Properties**\n\n| Name                                                   | Type     | Description                                                                                                                   | Required |\n| ------------------------------------------------------ | -------- | ----------------------------------------------------------------------------------------------------------------------------- | -------- |\n| **connection**<br/>(The connection to use for loading) | `string` | Logical connection name as defined in the connections.yaml<br/>                                                               | yes      |\n| **keyspace**                                           | `string` | Keyspace<br/>                                                                                                                 | yes      |\n| **table**<br/>(The target table name)                  | `string` | Target table name<br/>                                                                                                        | yes      |\n| [**keys**](#keys)<br/>(Business keys)                  | `array`  |                                                                                                                               | yes      |\n| [**mapping**](#mapping)<br/>(Fields to write)          | `array`  |                                                                                                                               | yes      |\n| **opcode_field**                                       | `string` | Name of the field in the payload that holds the operation (c - create, d - delete, u - update) for this record in the DB<br/> | yes      |\n\n**Additional Properties:** not allowed\n\n**Example**\n\n[code example]\n\n<a name=\"keys\"></a>"},{"id":"keys-business-keys","title":"keys\\[\\]: Business keys","role":"content","text":"**Items: name of column**\n\n**No properties.**\n\n**Example**\n\n[code example]\n\n<a name=\"mapping\"></a>"},{"id":"mapping-fields-to-write","title":"mapping\\[\\]: Fields to write","role":"content","text":"**Items: name of column**\n\n**No properties.**\n\n**Example**\n\n[code example]"}],"examples":[{"id":"overview-ex0","language":"yaml","code":"id: load_snowflake\ntype: relational.write\nproperties:\n  connection: eu_datalake\n  table: employees\n  schema: dbo\n  load_strategy: APPEND","section_id":"overview"},{"id":"keys-business-keys-ex0","language":"yaml","code":"- fname\n- lname: last_name","section_id":"keys-business-keys"},{"id":"mapping-fields-to-write-ex0","language":"yaml","code":"- fname\n- lname: last_name\n- address\n- gender","section_id":"mapping-fields-to-write"}]}
{"schema_version":2,"id":"integrate/write-behind/reference/data-transformation-block-types/filter","title":"filter","url":"https://redis.io/docs/latest/integrate/write-behind/reference/data-transformation-block-types/filter/","summary":"Filter records","aliases":["/integrate/redis-data-integration/reference/data-transformation-block-types/filter/","/integrate/redis-data-integration/write-behind/reference/data-transformation-block-types/filter/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"e6cc4a776efe5ec270ba0d791b63bf84078b3f68c49037e95062bc15598f96c0","sections":[{"id":"content","title":"Content","role":"content","text":"Filter records\n\n**Properties**\n\n| Name           | Type     | Description                                   | Required |\n| -------------- | -------- | --------------------------------------------- | -------- |\n| **expression** | `string` | Expression<br/>                               | yes      |\n| **language**   | `string` | Language<br/>Enum: `\"jmespath\"`, `\"sql\"`<br/> | yes      |\n\n**Additional Properties:** not allowed\n\n**Example**\n\n[code example]"}],"examples":[{"id":"content-ex0","language":"yaml","code":"source:\n  server_name: redislabs\n  schema: dbo\n  table: emp\ntransform:\n  - uses: filter\n    with:\n      language: sql\n      expression: age>20","section_id":"content"}]}
{"schema_version":2,"id":"integrate/write-behind/reference/data-transformation-block-types","title":"Data transformation block types","url":"https://redis.io/docs/latest/integrate/write-behind/reference/data-transformation-block-types/","summary":"Data transformation block type reference","aliases":["/integrate/redis-data-integration/write-behind/reference/data-transformation-block-types/","/integrate/redis-data-integration/reference/data-transformation-block-types/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","children":[{"id":"integrate/write-behind/reference/data-transformation-block-types/add_field","summary":"Add fields to a record","title":"add_field","url":"https://redis.io/docs/latest/integrate/write-behind/reference/data-transformation-block-types/add_field/"},{"id":"integrate/write-behind/reference/data-transformation-block-types/cassandra_write","summary":"Write into a Cassandra data store","title":"cassandra.write","url":"https://redis.io/docs/latest/integrate/write-behind/reference/data-transformation-block-types/cassandra_write/"},{"id":"integrate/write-behind/reference/data-transformation-block-types/filter","summary":"Filter records","title":"filter","url":"https://redis.io/docs/latest/integrate/write-behind/reference/data-transformation-block-types/filter/"},{"id":"integrate/write-behind/reference/data-transformation-block-types/key","summary":"Set the Redis key for this data entry","title":"key","url":"https://redis.io/docs/latest/integrate/write-behind/reference/data-transformation-block-types/key/"},{"id":"integrate/write-behind/reference/data-transformation-block-types/map","summary":"Map a record into a new output based on expressions","title":"map","url":"https://redis.io/docs/latest/integrate/write-behind/reference/data-transformation-block-types/map/"},{"id":"integrate/write-behind/reference/data-transformation-block-types/redis_write","summary":"Write to a Redis Enterprise database","title":"redis.write","url":"https://redis.io/docs/latest/integrate/write-behind/reference/data-transformation-block-types/redis_write/"},{"id":"integrate/write-behind/reference/data-transformation-block-types/relational_write","summary":"Write into a SQL-compatible data store","title":"relational.write","url":"https://redis.io/docs/latest/integrate/write-behind/reference/data-transformation-block-types/relational_write/"},{"id":"integrate/write-behind/reference/data-transformation-block-types/remove_field","summary":"Remove fields","title":"remove_field","url":"https://redis.io/docs/latest/integrate/write-behind/reference/data-transformation-block-types/remove_field/"},{"id":"integrate/write-behind/reference/data-transformation-block-types/rename_field","summary":"Rename fields. All other fields remain unchanged.","title":"rename_field","url":"https://redis.io/docs/latest/integrate/write-behind/reference/data-transformation-block-types/rename_field/"}],"page_type":"index","sections":[],"examples":[]}
{"schema_version":2,"id":"integrate/write-behind/reference/data-transformation-block-types/key","title":"key","url":"https://redis.io/docs/latest/integrate/write-behind/reference/data-transformation-block-types/key/","summary":"Set the Redis key for this data entry","aliases":["/integrate/redis-data-integration/reference/data-transformation-block-types/key/","/integrate/redis-data-integration/write-behind/reference/data-transformation-block-types/key/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"0326c448d4f1c6296ee824d434208d6b0ea40a3f3580ab56b60c4916637f1b09","sections":[{"id":"content","title":"Content","role":"content","text":"Set the Redis key for this data entry\n\n**Properties**\n\n| Name           | Type     | Description                                   | Required |\n| -------------- | -------- | --------------------------------------------- | -------- |\n| **expression** | `string` | Expression<br/>                               | yes      |\n| **language**   | `string` | Language<br/>Enum: `\"jmespath\"`, `\"sql\"`<br/> | yes      |\n\n**Additional Properties:** not allowed\n\n**Example**\n\n[code example]"}],"examples":[{"id":"content-ex0","language":"yaml","code":"source:\n  server_name: redislabs\n  schema: dbo\n  table: emp\nkey:\n  expression: concat([InvoiceId, '.', CustomerId])\n  language: jmespath","section_id":"content"}]}
{"schema_version":2,"id":"integrate/write-behind/reference/data-transformation-block-types/map","title":"map","url":"https://redis.io/docs/latest/integrate/write-behind/reference/data-transformation-block-types/map/","summary":"Map a record into a new output based on expressions","aliases":["/integrate/redis-data-integration/reference/data-transformation-block-types/map/","/integrate/redis-data-integration/write-behind/reference/data-transformation-block-types/map/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"9d9d8810b335aa88a6647567c874a8365689ff209793bae506f6149a48b6089f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Map a record into a new output based on expressions\n\n**Properties**\n\n| Name                          | Type               | Description                                   | Required |\n| ----------------------------- | ------------------ | --------------------------------------------- | -------- |\n| [**expression**](#expression) | `object`, `string` | Expression<br/>                               | yes      |\n| **language**                  | `string`           | Language<br/>Enum: `\"jmespath\"`, `\"sql\"`<br/> | yes      |\n\n**Additional Properties:** not allowed\n\n**Example**\n\n[code example]\n\n**Example**\n\n[code example]\n\n<a name=\"expression\"></a>"},{"id":"expression-object","title":"expression: object","role":"content","text":"Expression\n\n**No properties.**"}],"examples":[{"id":"overview-ex0","language":"yaml","code":"source:\n  server_name: redislabs\n  schema: dbo\n  table: emp\ntransform:\n  - uses: map\n    with:\n      expression:\n        first_name: first_name\n        last_name: last_name\n        greeting: >-\n          'Hello ' || CASE WHEN gender = 'F' THEN 'Ms.' WHEN gender = 'M' THEN 'Mr.'\n          ELSE 'N/A' END || ' ' || full_name\n        country: country\n        full_name: full_name\n      language: sql","section_id":"overview"},{"id":"overview-ex1","language":"yaml","code":"source:\n  table: customer\ntransform:\n  - uses: map\n    with:\n      expression: |\n        {\n          \"CustomerId\": customer_id,\n          \"FirstName\": first_name,\n          \"LastName\": last_name,\n          \"Company\": company,\n          \"Location\":\n          {\n            \"Street\": address,\n            \"City\": city,\n            \"State\": state,\n            \"Country\": country,\n            \"PostalCode\": postal_code\n          },\n          \"Phone\": phone,\n          \"Fax\": fax,\n          \"Email\": email\n        }\n      language: jmespath","section_id":"overview"}]}
{"schema_version":2,"id":"integrate/write-behind/reference/data-transformation-block-types/redis_write","title":"redis.write","url":"https://redis.io/docs/latest/integrate/write-behind/reference/data-transformation-block-types/redis_write/","summary":"Write to a Redis Enterprise database","aliases":["/integrate/redis-data-integration/reference/data-transformation-block-types/redis_write/","/integrate/redis-data-integration/write-behind/reference/data-transformation-block-types/redis_write/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"515b14907517085f3b911034e5e36d14c898be593d47e2445e187c13fe33ecec","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Write to a Redis Enterprise database\n\n**Properties**\n\n| Name                    | Type      | Description                                                                                                                                                                    | Required |\n| ----------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------- |\n| **connection**          | `string`  | Name of Redis connection specified in `config.yaml`.<br/>Defaults to connection named `target`.                                                                                | no       |\n| **data_type**<br/>      | `string`  | Type of Redis target data structure.<br/>Enum: `hash`(default), `json`, `set`, `sorted_set`, `stream`, `string`.<br/>Takes precedence over system property `target_data_type`. | no       |\n| [**nest**](#nest)       | `object`  | Nest (embed) object within a different key.<br/>If nesting is specified, the following parameters are ignored: `key`, `args` and `on_update`.                                  | no       |\n| **key**                 | `object`  | Definition of the target Redis key.<br/>                                                                                                                                       | yes      |\n| &#x221F; **expression** | `string`  | Expression used to calculate the target key.                                                                                                                                   | yes      |\n| &#x221F; **language**   | `string`  | Language used to define the expression.<br/>Enum: `jmespath`, `sql`.                                                                                                           | yes      |\n| [**args**](#args)       | `object`  | Arguments for modifying the target key.<br/>Specific to the data type.                                                                                                         | no       |\n| **mapping**             | `array`   | Array of fields (or `field: alias` pairs) to be written to a Redis key.<br/>Supported for hashes, json documents and streams only.                                             | no       |\n| **on_update**           | `string`  | Target key update strategy<br/>Enum: `merge`, `replace` (default).                                                                                                             | no       |\n| **expire**              | `integer` | TTL in seconds for the modified key to expire.<br/>If not specified (or `expire: 0`), the target key will never expire.                                                        | no       |\n\n> Notes:\n\n- Job parameters always override system properties. In particular, `data_type` will override `target_data_type` and `on_update` will override `json_update_strategy` properties respectively.\n- Mapping for **JSON documents** supports nested paths (e.g. `path.to.field`) which results in creating a nested element in Redis key. When a dot is used in a field name, it must be escaped with a backslash (e.g. `path\\.to\\.field`). Nested paths are not supported for hashes and streams.\n- For **strings** Write-behind will automatically assume `on_update: replace` regardless of what was declared in the job file. Appends and increments are not currently supported.\n- For **streams** Write-behind will ignore `on_update` property since they are append only.\n\n> Notes:\n\n- Job parameters always override system properties. In particular, `data_type` will override `target_data_type` and `on_update` will override `json_update_strategy` properties respectively.\n- Mapping for JSON documents supports nested paths (e.g. `path.to.field`) which results in creating a nested element in Redis key. When a dot is used in a field name, it must be escaped with a backslash (e.g. `path\\.to\\.field`). Nested paths are not supported for hashes and streams.\n\n**Example**\n\n[code example]\n\n<a name=\"args\"></a>"},{"id":"args-object","title":"args: object","role":"content","text":"Arguments for modifying the target key\n\n**Properties**\n\n| Name       | Type     | Description                                                                                    | Required |\n| ---------- | -------- | ---------------------------------------------------------------------------------------------- | -------- |\n| **score**  | `string` | Field name used as a score for sorted sets.<br/>Valid for sorted sets only.                    | yes      |\n| **member** | `string` | Field name used as a member for sets and sorted sets.<br/>Valid for sets and sorted sets only. | yes      |\n| **value**  | `string` | Field name used as a value for strings.<br/>Valid for strings only.                            | yes      |\n\n<a name=\"nest\"></a>"},{"id":"nest-object","title":"nest: object","role":"content","text":"Nest (embed) object within a different key\n\n**Properties**\n\n| Name                     | Type     | Description                                                                                                                                                                                                    | Required |\n| ------------------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |\n| **parent**               | `object` | Parent object definition.                                                                                                                                                                                      | yes      |\n| &#x221F; **server_name** | `string` | Server name.                                                                                                                                                                                                   | no       |\n| &#x221F; **schema**      | `string` | Schema name.                                                                                                                                                                                                   | no       |\n| &#x221F; **table**       | `string` | Parent table.                                                                                                                                                                                                  | yes      |\n| **parent_key**           | `string` | Field name used to identify the parent key (usually FK).                                                                                                                                                       | yes      |\n| **child_key**            | `string` | Optional field name used to identify the parent key value (if different from **parent_key**) in the child record. If not specified, then the field name defined in **parent_key** is used to lookup the value. | no       |\n| **nesting_key**          | `string` | Field name used to create the nesting key (usually PK).                                                                                                                                                        | yes      |\n| **path**                 | `string` | Path, where the nested object should reside in a parent document.<br/>Must start with the root (e.g. `$.<children-elements-here>`)                                                                             | yes      |\n| **structure**            | `string` | Data structure used to represent the object in a parent document (`map` is the only supported value).                                                                                                          | no       |\n\n> Notes:\n\n- When `nest` object is defined, Write-behind will automatically assume `data_type: json` and `on_update: merge` regardless of what was declared in the job file.\n- Nesting job cannot be used together with any of the these properties: `key`, `args`. The key is automatically calculated based on the following template: `<parent_table>:<parent_key>:<parent_key.value | child_key.value>`.\n- When `expire` is specified, it will be applied to the **parent** key. Therefore all nested objects will expire together with the parent key.\n\n**Example**\n\n[code example]\n\n> Note: In the example above `child_key` is not needed, because the FK in the child table `InvoiceLine` is defined using the same field name `InvoiceId` as in the parent table `Invoice`. If instead a FK was defined differently (e.g. InvoiceLine.ParentInvoivceId = Invoice.InvoiceId), then `child_key` parameter would be required to describe this relationship in the child job."}],"examples":[{"id":"overview-ex0","language":"yaml","code":"source:\n  server_name: chinook\n  schema: public\n  table: invoice\noutput:\n  # this block will use the default connection: target - since no explicit connection is specified,\n  # the data will be written in a JSON format as the data_type: json is specified for the block\n  - uses: redis.write\n    with:\n      data_type: json\n      key:\n        expression: concat(['invoice_id:', InvoiceId])\n        language: jmespath\n      mapping: # only the fields listed below will be written to a JSON document\n        - InvoiceId: id # this will create an element with a different name\n        - InvoiceDate: date\n        - BillingAddress: address.primary.street # this will create a nested element in the JSON document\n        - BillingCity: \"address.primary.city name\" # this will create a nested element with a space in the name\n        - BillingState: address.primary.state\n        - BillingPostalCode: \"address.primary.zip\\\\.code\" # this will create a nested element with a dot in the name\n        - Total # this will create an element with the same name as the original field\n      on_update: merge\n  # this block will use the explicitly specified connection: target1 - it must be defined in config.yaml\n  # the data will be written to the corresponding Redis set, based on a value of the key expression\n  - uses: redis.write\n    with:\n      connection: target\n      data_type: set\n      key:\n        expression: concat(['invoices:', BillingCountry])\n        language: jmespath\n      args:\n        member: InvoiceId\n  # this block will use the explicitly specified connection: target1 - it must be defined in config.yaml\n  # the data will be written to the Redis sorted set named invoices:sorted as specified in the key expression\n  - uses: redis.write\n    with:\n      connection: target1\n      data_type: sorted_set\n      key:\n        expression: \"`invoices:sorted`\"\n        language: jmespath\n      args:\n        score: Total\n        member: InvoiceId\n  # this block will use the specified connection: target2 - this, again, has to be defined in config.yaml\n  # the data will be written to a Redis stream named invoice:events as specified in the key expression\n  - uses: redis.write\n    with:\n      connection: target2\n      data_type: stream\n      key:\n        expression: \"`invoice:events`\"\n        language: jmespath\n      mapping: # only the fields listed below will be written to a stream message, with two of them renamed as message_id and country\n        - InvoiceId: message_id\n        - BillingCountry: country\n        - Total\n  # this block will use the default connection: target - since no explicit connection is specified,\n  # the data will be written to a Redis string as the data_type: string is specified for the block\n  - uses: redis.write\n    with:\n      data_type: string\n      key:\n        expression: concat(['Invoice:', InvoiceId])\n        language: jmespath\n      args:\n        value: Total # only the Total field will be written to a string\n      expire: 100 # the key will expire in 100 seconds","section_id":"overview"},{"id":"nest-object-ex0","language":"yaml","code":"source:\n  server_name: chinook\n  schema: public\n  table: InvoiceLine\noutput:\n  - uses: redis.write\n    with:\n      nest:\n        parent:\n          # server_name: chinook\n          # schema: public\n          table: Invoice\n        nesting_key: InvoiceLineId\n        parent_key: InvoiceId\n        # child_key: ParentInvoiceId\n        path: $.InvoiceLineItems\n        # structure: map","section_id":"nest-object"}]}
{"schema_version":2,"id":"integrate/write-behind/reference/data-transformation-block-types/relational_write","title":"relational.write","url":"https://redis.io/docs/latest/integrate/write-behind/reference/data-transformation-block-types/relational_write/","summary":"Write into a SQL-compatible data store","aliases":["/integrate/redis-data-integration/reference/data-transformation-block-types/relational_write/","/integrate/redis-data-integration/write-behind/reference/data-transformation-block-types/relational_write/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"e1e0951ae4069941a8d316fd415f861cc3e86ce768731033e89b5fbeb0ab59ee","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Write into a SQL-compatible data store\n\n**Properties**\n\n| Name                                                                                                                                                      | Type     | Description                                                                                                                                                          | Required |\n| --------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |\n| **connection**<br/>(The connection to use for loading)                                                                                                    | `string` | Logical connection name as defined in the connections.yaml<br/>                                                                                                      | yes      |\n| **schema**<br/>(The table schema of the target table)                                                                                                     | `string` | If left blank, the default schema of this connection will be used as defined in the connections.yaml<br/>                                                            | yes      |\n| **table**<br/>(The target table name)                                                                                                                     | `string` | Target table name<br/>                                                                                                                                               | yes      |\n| [**keys**](#keys)<br/>(Business keys to use in case of \\`load_strategy\\` is UPSERT or working with \\`opcode_field\\`)                                      | `array`  |                                                                                                                                                                      | no       |\n| [**mapping**](#mapping)<br/>(Fields to write)                                                                                                             | `array`  |                                                                                                                                                                      | no       |\n| **foreach**<br/>(Split a column into multiple records with a JMESPath expression)                                                                         | `string` | Use a JMESPath expression to split a column into multiple records. The expression should be in the format column: expression.<br/>Pattern: `^(?!:).*:.*(?<!:)$`<br/> | no       |\n| **opcode_field**                                                                                                                                          | `string` | Name of the field in the payload that holds the operation (c - create, d - delete, u - update) for this record in the DB<br/>                                        | no       |\n| **load_strategy**                                                                                                                                         | `string` | type of target<br/>Default: `\"APPEND\"`<br/>Enum: `\"APPEND\"`, `\"REPLACE\"`, `\"UPSERT\"`, `\"TYPE2\"`<br/>                                                                 | no       |\n| **active_record_indicator**                                                                                                                               | `string` | Used for `TYPE2` load_strategy. An SQL expression used to identify which rows are active<br/>                                                                        | no       |\n| [**inactive_record_mapping**](#inactive_record_mapping)<br/>(Used for \\`TYPE2\\` load_strategy\\. The columns mapping to use to close out an active record) | `array`  | A list of columns to use. Use any valid SQL expression for the source. If 'target' is omitted, will default to the name of the source column<br/>Default: <br/>      | no       |\n\n**Additional Properties:** not allowed\n\n**No properties.**\n\n**Not [required1]:**\n**No properties.**\n\n**Example**\n\n[code example]\n\n<a name=\"keys\"></a>"},{"id":"keys-business-keys-to-use-in-case-of-load_strategy-is-upsert-or-working-with-opcode_field","title":"keys\\[\\]: Business keys to use in case of \\`load_strategy\\` is UPSERT or working with \\`opcode_field\\`","role":"content","text":"**Items: name of column**\n\n**No properties.**\n\n**Example**\n\n[code example]\n\n<a name=\"mapping\"></a>"},{"id":"mapping-fields-to-write","title":"mapping\\[\\]: Fields to write","role":"content","text":"**Items: name of column**\n\n**No properties.**\n\n**Example**\n\n[code example]\n\n<a name=\"inactive_record_mapping\"></a>"},{"id":"inactive_record_mapping-used-for-type2-load_strategy-the-columns-mapping-to-use-to-close-out-an-active-record","title":"inactive_record_mapping\\[\\]: Used for \\`TYPE2\\` load_strategy\\. The columns mapping to use to close out an active record","role":"content","text":"A list of columns to use. Use any valid SQL expression for the source. If 'target' is omitted, will default to the name of the source column\n\n**No properties.**\n\n**Example**\n\n[code example]"}],"examples":[{"id":"overview-ex0","language":"yaml","code":"id: load_snowflake\ntype: relational.write\nproperties:\n  connection: eu_datalake\n  table: employees\n  schema: dbo\n  load_strategy: APPEND","section_id":"overview"},{"id":"keys-business-keys-to-use-in-case-of-load_strategy-is-upsert-or-working-with-opcode_field-ex0","language":"yaml","code":"- fname\n- lname: last_name","section_id":"keys-business-keys-to-use-in-case-of-load_strategy-is-upsert-or-working-with-opcode_field"},{"id":"mapping-fields-to-write-ex0","language":"yaml","code":"- fname\n- lname: last_name\n- address\n- gender","section_id":"mapping-fields-to-write"},{"id":"inactive_record_mapping-used-for-type2-load_strategy-the-columns-mapping-to-use-to-close-out-an-active-record-ex0","language":"yaml","code":"- source: CURRENT_DATE\n  target: deletedAt\n- source: \"'Y'\"\n  target: is_active","section_id":"inactive_record_mapping-used-for-type2-load_strategy-the-columns-mapping-to-use-to-close-out-an-active-record"}]}
{"schema_version":2,"id":"integrate/write-behind/reference/data-transformation-block-types/remove_field","title":"remove_field","url":"https://redis.io/docs/latest/integrate/write-behind/reference/data-transformation-block-types/remove_field/","summary":"Remove fields","aliases":["/integrate/redis-data-integration/reference/data-transformation-block-types/remove_field/","/integrate/redis-data-integration/write-behind/reference/data-transformation-block-types/remove_field/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"ac864bbc61929ae96327be27a52fb4f1536e6389bf5d6edaf743ec8d4e93484c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Remove fields\n\n**Option 1 (alternative):**\nRemove multiple fields\n\n**Properties**\n\n| Name                         | Type       | Description | Required |\n| ---------------------------- | ---------- | ----------- | -------- |\n| [**fields**](#option1fields) | `object[]` | Fields<br/> | yes      |\n\n**Additional Properties:** not allowed\n\n**Example**\n\n[code example]\n\n**Option 2 (alternative):**\nRemove one field\n\n**Properties**\n\n| Name      | Type     | Description | Required |\n| --------- | -------- | ----------- | -------- |\n| **field** | `string` | Field<br/>  | yes      |\n\n**Additional Properties:** not allowed  \n**Example**\n\n[code example]\n\n<a name=\"option1fields\"></a>"},{"id":"option-1-fields-array","title":"Option 1: fields\\[\\]: array","role":"parameters","text":"Fields\n\n**Items**\n\n**Item Properties**\n\n| Name      | Type     | Description | Required |\n| --------- | -------- | ----------- | -------- |\n| **field** | `string` | Field<br/>  | yes      |\n\n**Item Additional Properties:** not allowed\n\n**Example**\n\n[code example]"}],"examples":[{"id":"overview-ex0","language":"yaml","code":"source:\n  server_name: redislabs\n  schema: dbo\n  table: emp\ntransform:\n  - uses: remove_field\n    with:\n      fields:\n        - field: credit_card\n        - field: name.mname","section_id":"overview"},{"id":"overview-ex1","language":"yaml","code":"source:\n  server_name: redislabs\n  schema: dbo\n  table: emp\ntransform:\n  - uses: remove_field\n    with:\n      field: credit_card","section_id":"overview"},{"id":"option-1-fields-array-ex0","language":"yaml","code":"- {}","section_id":"option-1-fields-array"}]}
{"schema_version":2,"id":"integrate/write-behind/reference/data-transformation-block-types/rename_field","title":"rename_field","url":"https://redis.io/docs/latest/integrate/write-behind/reference/data-transformation-block-types/rename_field/","summary":"Rename fields. All other fields remain unchanged.","aliases":["/integrate/redis-data-integration/reference/data-transformation-block-types/rename_field/","/integrate/redis-data-integration/write-behind/reference/data-transformation-block-types/rename_field/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"6ead62a9c31c36e0530da0c288dbdb647753daaeef88ab9bbf033b1743b74f7f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Rename fields. All other fields remain unchanged.\n\n**Option 1 (alternative):**\nRename multiple fields\n\n**Properties**\n\n| Name                         | Type       | Description | Required |\n| ---------------------------- | ---------- | ----------- | -------- |\n| [**fields**](#option1fields) | `object[]` | Fields<br/> | yes      |\n\n**Additional Properties:** not allowed  \n**Example**\n\n[code example]\n\n**Option 2 (alternative):**\nRename one field\n\n**Properties**\n\n| Name           | Type     | Description     | Required |\n| -------------- | -------- | --------------- | -------- |\n| **from_field** | `string` | From field<br/> | yes      |\n| **to_field**   | `string` | To field<br/>   | yes      |\n\n**Additional Properties:** not allowed\n\n**Example**\n\n[code example]\n\n<a name=\"option1fields\"></a>"},{"id":"option-1-fields-array","title":"Option 1: fields\\[\\]: array","role":"parameters","text":"Fields\n\n**Items**\n\n**Item Properties**\n\n| Name           | Type     | Description     | Required |\n| -------------- | -------- | --------------- | -------- |\n| **from_field** | `string` | From field<br/> | yes      |\n| **to_field**   | `string` | To field<br/>   | yes      |\n\n**Item Additional Properties:** not allowed\n\n**Example**\n\n[code example]"}],"examples":[{"id":"overview-ex0","language":"yaml","code":"source:\n  server_name: redislabs\n  schema: dbo\n  table: emp\ntransform:\n  - uses: rename_field\n    with:\n      fields:\n        - from_field: name.lname\n          to_field: name.last_name\n        - from_field: name.fname\n          to_field: name.first_name","section_id":"overview"},{"id":"overview-ex1","language":"yaml","code":"source:\n  server_name: redislabs\n  schema: dbo\n  table: emp\ntransform:\n  - uses: rename_field\n    with:\n      from_field: name.lname\n      to_field: name.last_name","section_id":"overview"},{"id":"option-1-fields-array-ex0","language":"yaml","code":"- {}","section_id":"option-1-fields-array"}]}
{"schema_version":2,"id":"integrate/write-behind/reference/data-types-conversion","title":"Data type conversion","url":"https://redis.io/docs/latest/integrate/write-behind/reference/data-types-conversion/","summary":"Data type conversion reference","aliases":["/integrate/redis-data-integration/write-behind/reference/data-types-conversion/","/integrate/redis-data-integration/reference/data-types-conversion/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"f20c679303d1540e268a7c3cd8a153ddf17612473c4adbe476eed5f37f5d2802","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This document describes default conversions of data types of supported databases into target redis data types."},{"id":"general-ansi-sql-data-types","title":"General ANSI SQL data types","role":"content","text":"| Source data type                                                                          | Target data type for HASH                                                                                                                                                                                                                                                                                           | Example for HASH                                                                                                                                                                                                                                                             |\n| :---------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| array                                                                                     | not supported                                                                                                                                                                                                                                                                                                       |                                                                                                                                                                                                                                                                              |\n| bigint                                                                                    | string                                                                                                                                                                                                                                                                                                              | 2147483648 will be saved as '2147483648'                                                                                                                                                                                                                                     |\n| binary                                                                                    | bytes string <br />bytes or base64-encoded String, or a hex-encoded String, based on the connector configuration property setting `binary.handling.mode`                                                                                                                                                            | When `binary.handling.mode = bytes` the string `'hello'` will be added to the table as the binary string `'0x68656C6C6F'`,will be converted by Debezium to `'aGVsbG8AAAAAAAAAAAAAAAAAAAA='` and will be stored in Redis target database as: `'aGVsbG8AAAAAAAAAAAAAAAAAAAA='` |\n| bit string                                                                                | not supported                                                                                                                                                                                                                                                                                                       |                                                                                                                                                                                                                                                                              |\n| blob                                                                                      | bytes string <br />bytes or base64-encoded String, or a hex-encoded String, based on the connector configuration property setting `binary.handling.mode`                                                                                                                                                            | When `binary.handling.mode = bytes` the binary image file ,which was loaded into the table ,will be converted by Debezium to bytes and will be stored in Redis target database as bytes string                                                                               |\n| boolean                                                                                   | string                                                                                                                                                                                                                                                                                                              | The boolean value: false will be converted by the Applier to 0 and will be saved in Redis target database as the string '0'                                                                                                                                                  |\n| char                                                                                      | string                                                                                                                                                                                                                                                                                                              | When PostgreSQL data type is char(14) 'hello world' will be saved as 'hello world &ensp; '                                                                                                                                                                                   |\n| date                                                                                      | string <br />Mapped to ms.microsec since epoch                                                                                                                                                                                                                                                                      | PG field value: `'2021-01-29'` will be converted by Debezium to the int `18656` (number of dates since epoch), which will be converted to number of ms since epoch and will be stored in Redis target database as: `'1611878400000'`                                         |\n| integer                                                                                   | string                                                                                                                                                                                                                                                                                                              | `2147483640` will be saved as `'2147483640'`                                                                                                                                                                                                                                 |\n| interval                                                                                  | not supported                                                                                                                                                                                                                                                                                                       |                                                                                                                                                                                                                                                                              |\n| null                                                                                      |                                                                                                                                                                                                                                                                                                                     | The field with `null` value will be sent by Debezium as `null` and will not be stored in Redis target database                                                                                                                                                               |\n| numeric                                                                                   | string <br />Debezium configuration parameter `decimal.handling.mode` determines how the connector maps decimal values.When `decimal.handling.mode = 'precision'` the binary string,recieved by Debezium,will be converted to its corresponding numeric value and will be stored in Redis target database as string | PG field value: `4521398.56` will be converted by Debezium to binary string `'GvMbUA=='`, which will be converted to numeric value and will be stored in Redis target database as: `'4521398.56'`                                                                            |\n| smallint                                                                                  | string                                                                                                                                                                                                                                                                                                              | 32767 will be saved as '32767'                                                                                                                                                                                                                                               |\n| text                                                                                      | string                                                                                                                                                                                                                                                                                                              | 'This is a very long text for the PostgreSQL text column'                                                                                                                                                                                                                    |\n| time                                                                                      | string <br />mapped to number of seconds past midnight                                                                                                                                                                                                                                                              | `'14:23:46'` will be converted to `'51826000000'` sec                                                                                                                                                                                                                        |\n| PostgreSQL, Oracle, Cassandra: timestamp <br /> MySQL, SQL Server: datetime               | string <br />mapped to ms.microsec since epoch. <br /> SQL Server datetime format: `YYYY-MM-DD hh:mm:ss[.nnn]`, <br />range: `1753-01-01` through `9999-12-31`                                                                                                                                                      | PG field value: `'2018-06-20 15:13:16.945104'` will be converted by Debezium to `'1529507596945104'`(micro seconds) and will be stored in Redis target database as `'1529507596945.104'`                                                                                     |\n| PosrgreSQL: timestamptz <br />Oracle: timestamp with local timezone<br />MySQL: timestamp | string <br />converted to UTC and stored as number of ms.microsec since epoch                                                                                                                                                                                                                                       |                                                                                                                                                                                                                                                                              |\n| timetz                                                                                    | string <br />converted to UTC and stored as number of seconds past midnight                                                                                                                                                                                                                                         | PG field value: `'14:23:46.12345'` will be converted by Debezium to the string `'12:23:46.12345Z'` and will be stored in Redis target database as: `'44638.345'`                                                                                                             |\n| varbinary                                                                                 | bytes string <br />bytes or base64-encoded String, or a hex-encoded String, based on the connector configuration property setting `binary.handling.mode`                                                                                                                                                            | When `binary.handling.mode = bytes` the string `'hello'` will be added to the table as the binary string `'0x68656C6C6F'`,will be converted by Debezium to `'aGVsbG8='` and will be stored in Redis target database as: `'aGVsbG8='`                                         |\n| varchar                                                                                   | string                                                                                                                                                                                                                                                                                                              | 'hello world' will be saved as 'hello world'                                                                                                                                                                                                                                 |\n| xml                                                                                       | string                                                                                                                                                                                                                                                                                                              |\n\n| Source data type                                                                          | Target data type for JSON                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 | Example for JSON                                                                                                                                                                                                                                                             |\n| :---------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| array                                                                                     | not supported                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |                                                                                                                                                                                                                                                                              |\n| bigint                                                                                    | number                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    | 2147483648 will be saved as 2147483648                                                                                                                                                                                                                                       |\n| binary                                                                                    | bytes string                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              | When `binary.handling.mode = bytes` the string `'hello'` will be added to the table as the binary string `'0x68656C6C6F'`,will be converted by Debezium to `'aGVsbG8AAAAAAAAAAAAAAAAAAAA='` and will be stored in Redis target database as: `'aGVsbG8AAAAAAAAAAAAAAAAAAAA='` |\n| bit string                                                                                | not supported                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |                                                                                                                                                                                                                                                                              |\n| blob                                                                                      | bytes string                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              | When `binary.handling.mode = bytes` the binary image file ,which was loaded into the table ,will be converted by Debezium to bytes and will be stored in Redis target database as bytes string                                                                               |\n| boolean                                                                                   | boolean                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   | The boolean value:true will be saved in Redis target database as boolean data type with value True                                                                                                                                                                           |\n| char                                                                                      | string                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    | When PostgreSQL data type is char(14) 'hello world' will be saved as 'hello world &ensp; '                                                                                                                                                                                   |\n| date                                                                                      | number <br />Mapped to ms.microsec since epoch                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            | PG field value: `2021-01-29` will be converted by Debezium to the int 18656 (number of dates since epoch), which will be converted to number of ms since epoch and will be stored in Redis target database as: `1611878400000`                                               |\n| integer                                                                                   | number                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    | `2147483640` will be saved as `2147483640`                                                                                                                                                                                                                                   |\n| interval                                                                                  | not supported                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |                                                                                                                                                                                                                                                                              |\n| null                                                                                      |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           | The field with `null` value will be sent by Debezium as `null` and will be stored in Redis target database with the value `null`                                                                                                                                             |\n| numeric                                                                                   | Debezium configuration parameter `decimal.handling.mode` determines how the connector maps decimal values. When `decimal.handling.mode = 'precision'` the binary string received by Debezium will be converted to its corresponding numeric value and will be stored in Redis target database db as number. When `decimal.handling.mode = 'string'` the string ,received by Debezium, will be stored in Redis target database as string. When `decimal.handling.mode = 'double'` the double value received by Debezium will be stored in Redis target database as number. | `decimal.handling.mode = string`: PG field value: `4521398.56` will be recieved by Debezium as the string `'4521398.56'`, and will be stored in Redis target database as the string: '`4521398.56`' .                                                                        |\n| smallint                                                                                  | number                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    | 32767 will be saved as 32767                                                                                                                                                                                                                                                 |\n| text                                                                                      | string                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |                                                                                                                                                                                                                                                                              |\n| time                                                                                      | number number<br /> mapped to the number of seconds past midnight                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         | `'14:23:46'` will be converted to `51826000000` sec                                                                                                                                                                                                                          |\n| PostgreSQL, Oracle, Cassandra: timestamp <br /> MySQL, SQL Server: datetime               | decimal <br />mapped to ms.microsec since epoch. <br /> SQL Server datetime format: `YYYY-MM-DD hh:mm:ss[.nnn]`, <br />range: `1753-01-01` through `9999-12-31`                                                                                                                                                                                                                                                                                                                                                                                                           | PG field value: `'2018-06-20 15:13:16.945104'` will be converted by Debezium to `'1529507596945104'`(micro seconds) and will be stored in Redis target database as `1529507596945.104                                                                                        |\n| PosrgreSQL: timestamptz <br />Oracle: timestamp with local timezone<br />MySQL: timestamp | number <br />converted to UTC and stored as number of ms.microsec since epoch                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |                                                                                                                                                                                                                                                                              |\n| timetz                                                                                    | number <br />converted to UTC and stored as number of ms.microsec since epoch                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             | PG field value: `'14:23:46.12345'` will be converted by Debezium to the string `'12:23:46.12345Z'` and will be stored in Redis target database as: `44638.345`                                                                                                               |\n| varbinary                                                                                 | bytes string <br />bytes or base64-encoded String, or a hex-encoded String, based on the connector configuration property setting `binary.handling.mode`                                                                                                                                                                                                                                                                                                                                                                                                                  | When `binary.handling.mode = bytes` the string `'hello'` will be added to the table as the binary string `'0x68656C6C6F'`,will be converted by Debezium to `'aGVsbG8='` and will be stored in Redis target database as: `'aGVsbG8='`                                         |\n| varchar                                                                                   | string                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    | 'hello world' will be saved as 'hello world’                                                                                                                                                                                                                                 |\n| xml                                                                                       | string                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |                                                                                                                                                                                                                                                                              |"},{"id":"cassandra-data-types","title":"Cassandra data types","role":"content","text":"| Source data type      | Target data type for HASH                    | Example for HASH                                                                                                                                                                                                                                      |\n| :-------------------- | :------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| ascii                 | string                                       |                                                                                                                                                                                                                                                       |\n| counter (64-bit long) | string                                       | `2`                                                                                                                                                                                                                                                   |\n| date                  | not supported                                |                                                                                                                                                                                                                                                       |\n| decimal               | not supported                                |                                                                                                                                                                                                                                                       |\n| double                | string                                       | '1.007308023'                                                                                                                                                                                                                                         |\n| duration              | not supported                                |                                                                                                                                                                                                                                                       |\n| float                 | string                                       | The number `-3.4E+38` will be received by debezium as `-3.4E38`, will be converted by the Applier to `-340000000000000000000000000000000000000` and will be saved in Redis target database as `'-340000000000000000000000000000000000000'`            |\n| frozen                | string                                       | `{'10.10.11.1', '10.10.10.1', '10.10.12.1'}` will be saved in Redis target database as a string: `'{'10.10.11.1', '10.10.10.1', '10.10.12.1'}'`                                                                                                       |\n| frozen udt            | string                                       | `{'city': 'City','street': 'Street','streetno': 2,'zipcode': '02-212'}` will be saved in Redis target database as a string: `'{'city': 'City','street': 'Street','streetno': 2,'zipcode': '02-212'}'`                                                 |\n| inet                  | string <br />IPv4 and IPv6 network addresses | The IP address `4.35.221.243` will be converted by debezium to `'/4.35.221.243'` and will be saved in Redis target database as `'/4.35.221.243'`                                                                                                      |\n| list                  | string                                       | The list `['New York', 'Paris','London','New York']` will be sent by debezium as array of strings: `['New York', 'Paris','London','New York']` and will be saved in Redis target database as the string `\"['New York', 'Paris','London','New York']\"` |\n| map                   | string                                       | `{'fruit' : 'Apple', 'band' : 'Beatles'}` will be saved in Redis target database as a string: `'{'fruit' : 'Apple', 'band' : 'Beatles'}'`                                                                                                             |\n| set                   | string                                       | The set `{'y','n'}` will be saved in Redis target database as the string `'{'y','n'}'                                                                                                                                                                 |\n| tinyint               | string                                       |                                                                                                                                                                                                                                                       |\n| uuid                  | string                                       | b9980b96-a85b-411c-b8e7-4be55c123793                                                                                                                                                                                                                  |\n| tuple                 | string                                       | The tuple `{ \"field1\": 1, \"field2\": \"testing tuple\", \"field3\": 2 }` will be saved in Redis target database as a string: `' { \"field1\": 1, \"field2\": \"testing tuple\", \"field3\": 2 }'`                                                                  |\n| varint                | not supported                                |                                                                                                                                                                                                                                                       |\n\n| Source data type      | Target data type for JSON                    | Example for JSON                                                                                                                                                                                                                                      |\n| :-------------------- | :------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| ascii                 | string                                       |                                                                                                                                                                                                                                                       |\n| counter (64-bit long) | number                                       | 2                                                                                                                                                                                                                                                     |\n| date                  | not supported                                |                                                                                                                                                                                                                                                       |\n| decimal               | not supported                                |                                                                                                                                                                                                                                                       |\n| double                | number                                       | 1.007308023                                                                                                                                                                                                                                           |\n| duration              | not supported                                |                                                                                                                                                                                                                                                       |\n| float                 | number                                       | The number -3.4E+38 will be received by debezium as `-3.4E38` and will be saved in Redis target database as `-1e+38`                                                                                                                                  |\n| frozen                | array                                        | `{'10.10.11.1', '10.10.10.1', '10.10.12.1'}` will be saved in Redis target database as an array: `{'/10.10.11.1', '/10.10.10.1', '/10.10.12.1'}`                                                                                                      |\n| frozen udt            | object                                       | `{'city': 'City','street': 'Street','streetno': 2,'zipcode': '02-212'}` will be saved in Redis target database as string will be saved in Redis target database as an object: `{'city': 'City','street': 'Street','streetno': 2,'zipcode': '02-212'}` |\n| inet                  | string <br />IPv4 and IPv6 network addresses | The IP address `4.35.221.243` will be converted by debezium to `'/4.35.221.243'` and will be saved in Redis target database as `'/4.35.221.243'`                                                                                                      |\n| list                  | array                                        | The list `['New York', 'Paris','London','New York']` will be sent by debezium as array of strings: `['New York', 'Paris','London','New York']` and will be saved in Redis target database as an array: `['New York', 'Paris','London','New York']`    |\n| map                   | object                                       | `{'fruit' : 'Apple', 'band' : 'Beatles'}` will be saved in Redis target database as an object: `{'fruit' : 'Apple', 'band' : 'Beatles'}`                                                                                                              |\n| set                   | array                                        | The set `{'y','n'}` will be saved in Redis target database as an array: `{'y','n'}`                                                                                                                                                                   |\n| tinyint               |                                              |                                                                                                                                                                                                                                                       |\n| uuid                  | string                                       | b9980b96-a85b-411c-b8e7-4be55c123793                                                                                                                                                                                                                  |\n| tuple                 | object                                       | The tuple `{ \"field1\": 1, \"field2\": \"testing tuple\", \"field3\": 2 }` will be saved in Redis target database as an object: `{ \"field1\": 1, \"field2\": \"testing tuple\", \"field3\": 2 }`                                                                    |\n| varint                | not supported                                |                                                                                                                                                                                                                                                       |"},{"id":"mysql-and-mariadb-data-types","title":"MySQL and MariaDB data types","role":"content","text":"| Source data type   | Source data type for HASH | Example for Hash                                                                                                                                                                                       |\n| ------------------ | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| enum               | string                    | MySQL enum value `'cat'` will be stored in Redis target database as `'cat'`                                                                                                                            |\n| geometry           | not supported             |                                                                                                                                                                                                        |\n| geometrycollection | not supported             |                                                                                                                                                                                                        |\n| json               | string                    | {\"pid\": 102, \"name\": \"name2\"}                                                                                                                                                                          |\n| linestring         | not supported             |                                                                                                                                                                                                        |\n| multilinestring    | not supported             |                                                                                                                                                                                                        |\n| multipoint         | not supported             |                                                                                                                                                                                                        |\n| multypolygon       | not supported             |                                                                                                                                                                                                        |\n| polygon            | not supported             |                                                                                                                                                                                                        |\n| set                | string                    | `'1,2,3'` will be stored in Redis target database as: `'1,2,3'`                                                                                                                                        |\n| year               | string                    | The value `'55'` will be stored in the database as `2055` and will be sent by Debezium as int32 data type with value `2055`. It will be stored in Redis target database as the string `'2055'` as well |\n\n| Source data type   | Target data type for JSON | Example for JSON                                                                                                                                                                                     |\n| ------------------ | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| enum               | string                    | MySQL enum value `'cat'` will be stored in Redis target database as `'cat'`                                                                                                                          |\n| geometry           | not supported             |                                                                                                                                                                                                      |\n| geometrycollection | not supported             |                                                                                                                                                                                                      |\n| json               | object                    | {\"pid\": 102, \"name\": \"name2\"}                                                                                                                                                                        |\n| linestring         | not supported             |                                                                                                                                                                                                      |\n| multilinestring    | not supported             |                                                                                                                                                                                                      |\n| multipoint         | not supported             |                                                                                                                                                                                                      |\n| multypolygon       | not supported             |                                                                                                                                                                                                      |\n| polygon            | not supported             |                                                                                                                                                                                                      |\n| set                | string                    | `'1,2,3'` will be stored in Redis target database as: `'1,2,3'`                                                                                                                                      |\n| year               | number                    | The value `'55'` will be stored in the database as `2055` and will be sent by Debezium as int32 data type with value `2055`. It will be stored in Redis target database as the number `2055` as well |"},{"id":"oracle-data-types","title":"Oracle data types","role":"content","text":"| Source data type                                                                   | Source data type for HASH                                       | Example for Hash                                                                                                                                                                                                     |\n| ---------------------------------------------------------------------------------- | --------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| bfile                                                                              | not supported                                                   |                                                                                                                                                                                                                      |\n| binary_double                                                                      | string                                                          | '1.7E+308'                                                                                                                                                                                                           |\n| binary_float                                                                       | string                                                          | '3.40282E38'                                                                                                                                                                                                         |\n| clob                                                                               | string                                                          | large block of text                                                                                                                                                                                                  |\n| float,real,double precision <br /> real = FLOAT(63),double precision = FLOAT(126). | string                                                          | The value `-3.402E+38` will be saved in Redis target database as the string `'-340200000000000000000000000000000000000'` when Debezium configuration parameter `decimal.handling.mode = 'double'`                    |\n| long raw                                                                           | not supported                                                   |                                                                                                                                                                                                                      |\n| nchar                                                                              | string - is Unicode data type that can store Unicode characters | The string `'testing hebrew שלום'` will be stored in Redis target database as '`testing hebrew \\xd7\\xa9\\xd7\\x9c\\xd7\\x95\\xd7\\x9d`&ensp; &ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;'                                   |\n| nclob                                                                              | not supported                                                   |                                                                                                                                                                                                                      |\n| number(p,s)                                                                        | string                                                          | '10385274000.32'                                                                                                                                                                                                     |\n| nvarchar                                                                           | string - is Unicode data type that can store Unicode characters | The string `testing hebrew שלום'` will be stored in Redis target database as '`testing hebrew \\xd7\\xa9\\xd7\\x9c\\xd7\\x95\\xd7\\x9d`'                                                                                     |\n| raw                                                                                | not supported                                                   |                                                                                                                                                                                                                      |\n| rowid                                                                              | string                                                          | AAAR1QAAOAAAACFAAA                                                                                                                                                                                                   |\n| timestamp with tz                                                                  | string                                                          | `'2021-12-30 14:23:46'` will be converted by Debezium to the string `'2021-12-30T14:23:46+02:00'` and will be stored in Redis target database as the string: `'1611878400000'` which is the number of ms since epoch |\n| urowid                                                                             | not supported                                                   |\n\n| Source data type                                                                   | Target data type for JSON                                       | Example for JSON                                                                                                                                                                                                   |\n| ---------------------------------------------------------------------------------- | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| bfile                                                                              | not supported                                                   |                                                                                                                                                                                                                    |\n| binary_double                                                                      | number                                                          | 1.7E+308                                                                                                                                                                                                           |\n| binary_float                                                                       | number                                                          | 3.40282E38                                                                                                                                                                                                         |\n| clob                                                                               | string                                                          | large block of text                                                                                                                                                                                                |\n| float,real,double precision <br /> real = FLOAT(63),double precision = FLOAT(126). | number/string                                                   | the value `-3.402E+38` will be saved in Redis target database as the number `-340200000000000000000000000000000000000` when Debezium configuration parameter `decimal.handling.mode = 'double'`                    |\n| long raw                                                                           | not supported                                                   |                                                                                                                                                                                                                    |\n| nchar                                                                              | string - is Unicode data type that can store Unicode characters | The string `'testing hebrew שלום'` will be stored in Redis target database as '`testing hebrew \\xd7\\xa9\\xd7\\x9c\\xd7\\x95\\xd7\\x9d`&ensp; &ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;'                                 |\n| nclob                                                                              | not supported                                                   |                                                                                                                                                                                                                    |\n| number(p,s)                                                                        | number                                                          | 10385274000.32                                                                                                                                                                                                     |\n| nvarchar                                                                           | string - is Unicode data type that can store Unicode characters | The string `testing hebrew שלום'` will be stored in Redis target database as '`testing hebrew \\xd7\\xa9\\xd7\\x9c\\xd7\\x95\\xd7\\x9d`'                                                                                   |\n| raw                                                                                | not supported                                                   |                                                                                                                                                                                                                    |\n| rowid                                                                              | string                                                          | AAAR1QAAOAAAACFAAA                                                                                                                                                                                                 |\n| timestamp with tz                                                                  | number                                                          | `'2021-12-30 14:23:46'` will be converted by Debezium to the string `'2021-12-30T14:23:46+02:00'` and will be stored in Redis target database as the number: `1611878400000` which is the number of ms since epoch |\n| urowid                                                                             | not supported                                                   |                                                                                                                                                                                                                    |"},{"id":"postgresql-data-types","title":"PostgreSQL data types","role":"content","text":"| Source data type | Source data type for HASH                     | Example for Hash                                                                                                                                                                                                                                                                                                                                         |\n| ---------------- | --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| box              | not supported                                 |                                                                                                                                                                                                                                                                                                                                                          |\n| cidr             | string<br />IPv4 and IPv6 networks addresses. | '4.35.221.243/32'                                                                                                                                                                                                                                                                                                                                        |\n| circle           | not supported                                 |                                                                                                                                                                                                                                                                                                                                                          |\n| domain           | string                                        |                                                                                                                                                                                                                                                                                                                                                          |\n| hstore           | string                                        | '{\"pages\":\"368\",\"author\":\"Katherine Dunn\",\"category\":\"fiction\"}'                                                                                                                                                                                                                                                                                         |\n| inet             | string<br />IPv4 and IPv6 network addresses   | '4.35.221.243'                                                                                                                                                                                                                                                                                                                                           |\n| json             | string                                        | \"{\"guid\":&nbsp; \"9c36adc1-7fb5-4d5b-83b4-90356a46061a\",<br />\"name\": \"Angela Barton\",<br />\"is_active\": null,<br />\"company\": \"Magnafone\"<br />\"address\": \\\"178 Howard Place, Gulf, Washington,702\",<br />\"registered\": \"2009-11-07T08:53:22 +08:00\",<br />\"latitude\": 19.793713,<br />\"longitude\": 86.513373,<br />\"tags\": [\"enim\",\"aliquip\",\"qui\\\" ]}\" |\n| line             | not supported                                 |                                                                                                                                                                                                                                                                                                                                                          |\n| macaddr          | string<br /> mac addresses                    | '08:00:2b:01:02:03'                                                                                                                                                                                                                                                                                                                                      |\n| money            | string                                        | When `decimal.handling.mode = 'double'` the money value `-8793780.01` will be received by Debezium as `-8793780.01` with double data type, and will be stored in Redis target database as the string'`-8793780.01`'                                                                                                                                      |\n| path             | not supported                                 |                                                                                                                                                                                                                                                                                                                                                          |\n| point            | not supported                                 |                                                                                                                                                                                                                                                                                                                                                          |\n| polygon          | not supported                                 |                                                                                                                                                                                                                                                                                                                                                          |\n| uuid             | string                                        | 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'                                                                                                                                                                                                                                                                                                                   |\n|                  |                                               |                                                                                                                                                                                                                                                                                                                                                          |\n\n| Source data type | Target data type for JSON                     | Example for JSON                                                                                                                                                                                                                                                                                                                                       |\n| ---------------- | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| box              | not supported                                 |                                                                                                                                                                                                                                                                                                                                                        |\n| cidr             | string<br />IPv4 and IPv6 networks addresses. | '4.35.221.243/32'                                                                                                                                                                                                                                                                                                                                      |\n| circle           | not supported                                 |                                                                                                                                                                                                                                                                                                                                                        |\n| domain           | string                                        |                                                                                                                                                                                                                                                                                                                                                        |\n| hstore           | string                                        | '{\"pages\":\"368\",\"author\":\"Katherine Dunn\",\"category\":\"fiction\"}                                                                                                                                                                                                                                                                                        |\n| inet             | string<br />IPv4 and IPv6 network addresses   | '4.35.221.243'                                                                                                                                                                                                                                                                                                                                         |\n| json             | object                                        | {\"guid\":&nbsp; \"9c36adc1-7fb5-4d5b-83b4-90356a46061a\",<br />\"name\": \"Angela Barton\",<br />\"is_active\": null,<br />\"company\": \"Magnafone\"<br />\"address\": \\\"178 Howard Place, Gulf, Washington,702\",<br />\"registered\": \"2009-11-07T08:53:22 +08:00\",<br />\"latitude\": 19.793713,<br />\"longitude\": 86.513373,<br />\"tags\": [\"enim\",\"aliquip\",\"qui\\\" ]} |\n| line             | not supported                                 |                                                                                                                                                                                                                                                                                                                                                        |\n| macaddr          | string <br /> mac addresses                   | '08:00:2b:01:02:03'                                                                                                                                                                                                                                                                                                                                    |\n| money            | string                                        | When `decimal.handling.mode = 'double'` the money value `-8793780.01` will be received by Debezium as `-8793780.01`, with double data type and will be stored in Redis target database as the number `-8793780.01`                                                                                                                                     |\n| path             | not supported                                 |                                                                                                                                                                                                                                                                                                                                                        |\n| point            | not supported                                 |                                                                                                                                                                                                                                                                                                                                                        |\n| polygon          | not supported                                 |                                                                                                                                                                                                                                                                                                                                                        |\n| uuid             | string                                        | 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'                                                                                                                                                                                                                                                                                                                 |\n|                  |                                               |                                                                                                                                                                                                                                                                                                                                                        |"},{"id":"sql-server-data-types","title":"SQL server data types","role":"content","text":"| Source data type        | Source data type for HASH                                                                                                                                                             | Example for Hash                                                                                                                                                                                                                                                                                                          |\n| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| bit                     | string                                                                                                                                                                                | When value `>0` it will be converted by Debezium to true and will be saved in Redis target database as '`1`' while when value `= 0` it will be converted by Debezium to false and will be saved in Redis target database as '`0`'                                                                                         |\n| datetime2               | string <br />Represents the number of milliseconds since the epoch, and does not include timezone information.                                                                        | When Debezium configuration parameter `time.precision.mode='connect'`, the value '`2018-06-20 15:13:16.945104`' will be converted by Debezium to the value '`1529507596945104`' and will be saved in Redis target database database as the string `'1529507596945.104`'                                                   |\n| datetimeoffset          | string                                                                                                                                                                                | When Debezium configuration parameter `decimal.handling.mode = 'precision'`, the datetimeoffset datatype value '`12-10-25 12:32:10 +01:00`' will be converted to the string '`2025-12-10T12:32:10+01:00'` and will be saved in Redis target database as `1765366330000`                                                   |\n| decimal,float,real      | string <br /> range of values: decimal-10^38 +1 to 10^38,float-1.79E+308 to -2.23E-308, 0 and 2.23E-308 to 1.79E+308,real:- 3.40E + 38 to -1.18E - 38, 0 and 1.18E - 38 to 3.40E + 38 | When Debezium configuration parameter `decimal.handling.mode = 'precision'` the value '`-3.402E+38`' will be converted by Debezium to the binary string '`/wAP3QCzc/wpiIGe8AAAAAA=`' and will be saved in Redis target database as the string '`-340200000000000000000000000000000000000`'                                |\n| image                   | string<br /> Variable-length binary data from 0 through 2,147,483,647 bytes.                                                                                                          |                                                                                                                                                                                                                                                                                                                           |\n| money                   | string <br /> range of values: -922,337,203,685,477,5808 to 922,337,203,685,477.5807                                                                                                  | When Debezium configuration parameter `decimal.handling.mode = 'precision'` the value `922337203685477.5807` will be converted by Debezium to the binary '`f/////////8=`' string and will be saved in Redis target database as the string '`922337203685477.5807`'                                                        |\n| nchar                   | string - fixed-size string data , Unicode data type that can store Unicode characters                                                                                                 |                                                                                                                                                                                                                                                                                                                           |\n| nvarchar                | string - variable-size string data, Unicode data type that can store Unicode characters                                                                                               |                                                                                                                                                                                                                                                                                                                           |\n| numeric                 | string <br /> range of values - 10^38 +1 to 10^38                                                                                                                                     | When Debezium configuration parameter `time.precision.mode = 'connect'` and `decimal.handling.mode = 'precision'` , the value `1.00E +33` will be converted by Debezium to the binary string `'SztMqFqGw1MAAAAAAAAAAA=='` and will be saved in Redis target database as the string `'1000000000000000000000000000000000'` |\n| rowversion              | string <br />data type that exposes automatically generated, unique binary numbers within a database. rowversion is generally used as a mechanism for version-stamping table rows.    | 0x00000000000007D0                                                                                                                                                                                                                                                                                                        |\n| smalldatetime           | string <br />represents the number of milliseconds past the epoch, and does not include timezone information.                                                                         | `'2018-06-20 15:13:16`' will be converted by Debezium to `1529507580000` ms past the epoch and will be saved in Write-behind as the string '`1529507580000`'. <br />number of seconds: 16 will be not be included in the convertion and will not be saved in Redis target database                              |\n| smallmoney              | string <br />range of values: - 214,748.3648 to 214,748.3647                                                                                                                          | When Debezium configuration parameter `decimal.handling.mode = 'string'` the value `-214748.3648` will be converted by Debezium to the string `'-214748.3648'` and will be saved in Redis target database as '`-214748.3648`'                                                                                             |\n| Spatial Geometry Types  | not supported                                                                                                                                                                         |                                                                                                                                                                                                                                                                                                                           |\n| Spatial Geography Types | not supported                                                                                                                                                                         |                                                                                                                                                                                                                                                                                                                           |\n| table                   | not supported                                                                                                                                                                         |                                                                                                                                                                                                                                                                                                                           |\n| text                    | Variable-length Unicode data                                                                                                                                                          |                                                                                                                                                                                                                                                                                                                           |\n| uniqueidentifier        | string                                                                                                                                                                                | 06BEEF00-F859-406B-9A60-0A56AB81A97                                                                                                                                                                                                                                                                                       |\n\n| Source data type        | Target data type for JSON                                                                                                                                                          | Example for JSON                                                                                                                                                                                                                                                                                                       |\n| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| bit                     | boolean                                                                                                                                                                            | When value `>0` it will be converted by Debezium to true and will be saved in Redis target database as `True` while when value `= 0` it will be converted by Debezium to false and will be saved in Redis target database as `False`                                                                                   |\n| datetime2               | number <br /> Represents the number of milliseconds since the epoch, and does not include timezone                                                                                 | When Debezium configuration parameter `time.precision.mode='connect'`, the value '`2018-06-20 15:13:16.945104`' will be converted by Debezium to the value '`1529507596945104`' and will be saved in Redis target database database as the number `1529507596945.104`                                                  |\n| datetimeoffset          | number                                                                                                                                                                             | When Debezium configuration parameter `decimal.handling.mode = 'precision'`, the datetimeoffset datatype value '`12-10-25 12:32:10 +01:00`' will be converted to the string '`2025-12-10T12:32:10+01:00'` and will be saved in Redis target database as the string `1765366330000`                                     |\n| decimal,float,real      | number/string                                                                                                                                                                      | When Debezium configuration parameter `decimal.handling.mode = 'precision'` the value '`-3.402E+38`' will be converted by Debezium to the binary string '`/wAP3QCzc/wpiIGe8AAAAAA=`' and will be saved in Redis target database as the number `-340200000000000000000000000000000000000`                               |\n| image                   | string                                                                                                                                                                             |                                                                                                                                                                                                                                                                                                                        |\n| money                   | number/string depending on the value of decimal.handling.mode                                                                                                                      | When Debezium configuration parameter `decimal.handling.mode = 'precision'` the value `922337203685477.5807` will be converted by Debezium to the binary '`f/////////8=`' string and will be saved in Redis target database as the number `922337203685477.5807`                                                       |\n| nchar                   | string                                                                                                                                                                             |                                                                                                                                                                                                                                                                                                                        |\n| nvarchar                | string - variable-size string data, Unicode data type that can store Unicode characters                                                                                            |                                                                                                                                                                                                                                                                                                                        |\n| numeric                 | number                                                                                                                                                                             | When Debezium configuration parameter `time.precision.mode = 'connect'` and `decimal.handling.mode = 'precision'` , the value `1.00E +33` will be converted by Debezium to the binary string `'SztMqFqGw1MAAAAAAAAAAA=='` and will be saved in Redis target database as the number`1000000000000000000000000000000000` |\n| rowversion              | string <br />data type that exposes automatically generated, unique binary numbers within a database. rowversion is generally used as a mechanism for version-stamping table rows. | 0x00000000000007D0                                                                                                                                                                                                                                                                                                     |\n| smalldatetime           | number <br />represents the number of milliseconds past the epoch, and does not include timezone information.                                                                      | `'2018-06-20 15:13:16`' will be converted by Debezium to `1529507580000` ms past the epoch and will be saved in Write-behind as the number `1529507580000`. <br />number of seconds: 16 will be not be included in the convertion and will not be saved in Redis target database                             |\n| smallmoney              | number                                                                                                                                                                             | When Debezium configuration parameter `decimal.handling.mode = 'string'` the value `-214748.3648` will be converted by Debezium to the string `'-214748.3648'` and will be saved in Redis target database as '`-214748.3648`'                                                                                          |\n| Spatial Geometry Types  | not supported                                                                                                                                                                      |                                                                                                                                                                                                                                                                                                                        |\n| Spatial Geography Types | not supported                                                                                                                                                                      |                                                                                                                                                                                                                                                                                                                        |\n| table                   | not supported                                                                                                                                                                      |                                                                                                                                                                                                                                                                                                                        |\n| text                    |                                                                                                                                                                                    |                                                                                                                                                                                                                                                                                                                        |\n| uniqueidentifier        | string                                                                                                                                                                             | 06BEEF00-F859-406B-9A60-0A56AB81A97                                                                                                                                                                                                                                                                                    |\n\n\\* fields with \"not supported\" data type will not appear in target hash."}],"examples":[]}
{"schema_version":2,"id":"integrate/write-behind/reference/debezium/cassandra","title":"Write-behind configuration for cassandra","url":"https://redis.io/docs/latest/integrate/write-behind/reference/debezium/cassandra/","summary":"Describes the `application.properties` settings that configure Debezium Server for cassandra","aliases":["/integrate/redis-data-integration/reference/debezium/cassandra/","/integrate/redis-data-integration/write-behind/reference/debezium/cassandra/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"2704e9013082b6c2a568d97438d85f5dd68346b1835eae20f490d23ab095a9ad","sections":[{"id":"applicationproperties","title":"application.properties","role":"overview","text":"[code example]"}],"examples":[{"id":"applicationproperties-ex0","language":"properties","code":"debezium.sink.type=redis\ndebezium.sink.redis.message.format=extended\ndebezium.sink.redis.address=<RDI_HOST>:<RDI_PORT>\n# Comment the following line if not using a password for Write-behind.\ndebezium.sink.redis.password=<RDI_PASSWORD>\ndebezium.sink.redis.memory.limit.mb=80\n# Redis SSL/TLS\n#debezium.sink.redis.ssl.enabled=true\n# When Redis is configured with a replica shard, these properties allow to verify that the data has been written to the replica.\n#debezium.sink.redis.wait.enabled=true\n#debezium.sink.redis.wait.timeout.ms=1000\n#debezium.sink.redis.wait.retry.enabled=true\n#debezium.sink.redis.wait.retry.delay.ms=1000\n#debezium.source.database.history.redis.ssl.enabled=true\n# Location of the Java keystore file containing an application process' own certificate and private key.\n#javax.net.ssl.keyStore=<KEY_STORE_FILE_PATH>\n# Password to access the private key from the keystore file specified by javax.net.ssl.keyStore. This password is used twice: To unlock the keystore file (store password), and To decrypt the private key stored in the keystore (key password).\n#javax.net.ssl.keyStorePassword=<KEY_STORE_PASSWORD>\n# Location of the Java keystore file containing the collection of CA certificates trusted by this application process (trust store).\n#javax.net.ssl.trustStore=<TRUSE_STORE_FILE_PATH>\n# Password to unlock the keystore file (store password) specified by javax.net.ssl.trustStore.\n#javax.net.ssl.trustStorePassword=<TRUST_STORE_PASSWORD>\n\ndebezium.source.connector.class=io.debezium.connector.cassandra.Cassandra4Connector\ndebezium.source.snapshot.consistency=ONE\ndebezium.source.offset.storage=io.debezium.storage.redis.offset.RedisOffsetBackingStore\ndebezium.source.topic.prefix=<SOURCE_LOGICAL_SERVER_NAME>\ndebezium.source.cassandra.node.id=<NAME_SOURCE_NODE>\ndebezium.source.cassandra.hosts=<SOURCE_DB_HOST>\ndebezium.source.cassandra.port=<SOURCE_DB_PORT>\ndebezium.source.cassandra.config=<ABSOLUTE_PATH_CONFIG_FILE>\ndebezium.source.commit.log.relocation.dir=<RELOCATION_DIR>\ndebezium.source.commit.log.real.time.processing.enabled=true\ndebezium.source.commit.marked.complete.poll.interval.ms=1000\ndebezium.source.http.port=8040\n\n# Whether to include the detailed schema information generated by Debezium in each record written to RDI.\n# Note: Including the schema reduces the initial sync throughput and is not recommended for large data sets.\ndebezium.source.key.converter.schemas.enable=false\ndebezium.source.value.converter.schemas.enable=false\n# When detailed schema information is excluded, handle decimal numeric types as strings.\ndebezium.source.decimal.handling.mode=string\n\ndebezium.transforms=AddPrefix\ndebezium.transforms.AddPrefix.type=org.apache.kafka.connect.transforms.RegexRouter\ndebezium.transforms.AddPrefix.regex=.*\ndebezium.transforms.AddPrefix.replacement=data:$0\n\n# Logging\n# Uncomment the following lines if running Debezium Server as a Java standalone process (non-containerized).\n#quarkus.log.file.enable=true\n#quarkus.log.file.path=<LOG_FILE_PATH>\n#quarkus.log.file.rotation.max-file-size=100M\n#quarkus.log.file.rotation.rotate-on-boot=true\n#quarkus.log.file.rotation.file-suffix=.yyyy-MM-dd.gz\n#quarkus.log.file.rotation.max-backup-index=3\n\n# The default minimum log level for every log category, change only quarkus.log.level when needed.\nquarkus.log.min-level=TRACE\n# The default log level for every log category.\nquarkus.log.level=INFO\n# Determine whether to enable the JSON console formatting extension, which disables \"normal\" console formatting.\nquarkus.log.console.json=false\n# The port on which Debezium exposes Microprofile Health endpoint and other exposed status information.\nquarkus.http.port=8088","section_id":"applicationproperties"}]}
{"schema_version":2,"id":"integrate/write-behind/reference/debezium","title":"Debezium Server configuration file","url":"https://redis.io/docs/latest/integrate/write-behind/reference/debezium/","summary":"Application properties settings used to configure Debezim Server for source database servers","aliases":["/integrate/redis-data-integration/write-behind/reference/debezium/","/integrate/redis-data-integration/reference/debezium/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","children":[{"id":"integrate/write-behind/reference/debezium/cassandra","summary":"Describes the `application.properties` settings that configure Debezium Server for cassandra","title":"Write-behind configuration for cassandra","url":"https://redis.io/docs/latest/integrate/write-behind/reference/debezium/cassandra/"},{"id":"integrate/write-behind/reference/debezium/mongodb","summary":"Describes the `application.properties` settings that configure Debezium Server for mongodb","title":"Write-behind configuration for mongodb","url":"https://redis.io/docs/latest/integrate/write-behind/reference/debezium/mongodb/"},{"id":"integrate/write-behind/reference/debezium/mysql","summary":"Describes the `application.properties` settings that configure Debezium Server for mysql","title":"Write-behind configuration for mysql","url":"https://redis.io/docs/latest/integrate/write-behind/reference/debezium/mysql/"},{"id":"integrate/write-behind/reference/debezium/oracle","summary":"Describes the `application.properties` settings that configure Debezium Server for oracle","title":"Write-behind configuration for oracle","url":"https://redis.io/docs/latest/integrate/write-behind/reference/debezium/oracle/"},{"id":"integrate/write-behind/reference/debezium/postgresql","summary":"Describes the `application.properties` settings that configure Debezium Server for postgresql","title":"Write-behind configuration for postgresql","url":"https://redis.io/docs/latest/integrate/write-behind/reference/debezium/postgresql/"},{"id":"integrate/write-behind/reference/debezium/sqlserver","summary":"Describes the `application.properties` settings that configure Debezium Server for sqlserver","title":"Write-behind configuration for sqlserver","url":"https://redis.io/docs/latest/integrate/write-behind/reference/debezium/sqlserver/"}],"page_type":"content","content_hash":"a234d1367fce8c9caeab093ffcf8252beba81dc67aa10f563ddb1c30e9d211fd","sections":[{"id":"content","title":"Content","role":"content","text":"The `application.properties` file configures Debezium Server configuration to support source databases. It contains sections that define the sink connector (Redis) configuration and the source connector configuration.\nThis file needs to be saved in the host running Debezium Server.\n\nThe following topics describe `application.properties` for specific database servers:"}],"examples":[]}
{"schema_version":2,"id":"integrate/write-behind/reference/debezium/mongodb","title":"Write-behind configuration for mongodb","url":"https://redis.io/docs/latest/integrate/write-behind/reference/debezium/mongodb/","summary":"Describes the `application.properties` settings that configure Debezium Server for mongodb","aliases":["/integrate/redis-data-integration/reference/debezium/mongodb/","/integrate/redis-data-integration/write-behind/reference/debezium/mongodb/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"ac73e3a3338e4fe404ce5828441a57bbeef1071c4e17739effd8fc3e8b6ee718","sections":[{"id":"applicationproperties","title":"application.properties","role":"overview","text":"[code example]"}],"examples":[{"id":"applicationproperties-ex0","language":"properties","code":"debezium.sink.type=redis\ndebezium.sink.redis.message.format=extended\ndebezium.sink.redis.address=<RDI_HOST>:<RDI_PORT>\n# Comment the following line if not using a password for Write-behind.\ndebezium.sink.redis.password=<RDI_PASSWORD>\ndebezium.sink.redis.memory.limit.mb=80\n# Redis SSL/TLS\n#debezium.sink.redis.ssl.enabled=true\n# When Redis is configured with a replica shard, these properties allow to verify that the data has been written to the replica.\n#debezium.sink.redis.wait.enabled=true\n#debezium.sink.redis.wait.timeout.ms=1000\n#debezium.sink.redis.wait.retry.enabled=true\n#debezium.sink.redis.wait.retry.delay.ms=1000\n#debezium.source.database.history.redis.ssl.enabled=true\n# Location of the Java keystore file containing an application process' own certificate and private key.\n#javax.net.ssl.keyStore=<KEY_STORE_FILE_PATH>\n# Password to access the private key from the keystore file specified by javax.net.ssl.keyStore. This password is used twice: To unlock the keystore file (store password), and To decrypt the private key stored in the keystore (key password).\n#javax.net.ssl.keyStorePassword=<KEY_STORE_PASSWORD>\n# Location of the Java keystore file containing the collection of CA certificates trusted by this application process (trust store).\n#javax.net.ssl.trustStore=<TRUSE_STORE_FILE_PATH>\n# Password to unlock the keystore file (store password) specified by javax.net.ssl.trustStore.\n#javax.net.ssl.trustStorePassword=<TRUST_STORE_PASSWORD>\n\ndebezium.source.connector.class=io.debezium.connector.mongodb.MongoDbConnector\ndebezium.source.mongodb.hosts=<REPLICATION_SET_NAME>/<MONGO_DB_HOST>:<MONGO_DB_PORT>\ndebezium.source.mongodb.connection.mode=replica_set\ndebezium.source.mongodb.user=<MONGO_DB_USER>\ndebezium.source.mongodb.password=<MONGO_DB_PASSWORD>\ndebezium.source.offset.storage=io.debezium.storage.redis.offset.RedisOffsetBackingStore\ndebezium.source.topic.prefix=<SOURCE_LOGICAL_SERVER_NAME>\n\ndebezium.source.offset.flush.interval.ms=1000\ndebezium.source.tombstones.on.delete=false\ndebezium.source.schema.history.internal=io.debezium.storage.redis.history.RedisSchemaHistory\n\n# Important: Do NOT use `include` and `exclude` database lists at the same time, use either `include` or `exclude`.\n# An optional, comma-separated list of regular expressions that match database names to be monitored.\n# By default, all databases are monitored.\n#debezium.source.database.include.list=<DB_NAME1>,<DB_NAME2>...\n# An optional, comma-separated list of regular expressions that match database names for which you do not want to capture changes.\n#debezium.source.database.exclude.list=<DB_NAME1>,<DB_NAME2>...\n# Important: Do NOT use `include` and `exclude` collection lists at the same time, use either `include` or `exclude`.\n# An optional, comma-separated list of regular expressions that match collection names to be monitored.\n#debezium.source.collection.include.list=<DB_NAME.COLLECTION_NAME1>,<DB_NAME.COLLECTION_NAME2>...\n# An optional, comma-separated list of regular expressions that match collection names for which you do not want to capture changes.\n#debezium.source.collection.exclude.list=<DB_NAME.COLLECTION_NAME1>,<DB_NAME.COLLECTION_NAME2>...\n\n#An optional, comma-separated list of regular expressions that match field names for which you do not want to capture changes.\n#debezium.source.field_exclude_list=<DB_NAME.COLLECTION_NAME1.FIELD_NAME1>,<DB_NAME.COLLECTION_NAME.FIELD_NAME2>\n\n# Whether to include the detailed schema information generated by Debezium in each record written to RDI.\n# Note: Including the schema reduces the initial sync throughput and is not recommended for large data sets.\ndebezium.source.key.converter.schemas.enable=false\ndebezium.source.value.converter.schemas.enable=false\n# When detailed schema information is excluded, handle decimal numeric types as strings.\ndebezium.source.decimal.handling.mode=string\n\ndebezium.transforms=AddPrefix\ndebezium.transforms.AddPrefix.type=org.apache.kafka.connect.transforms.RegexRouter\ndebezium.transforms.AddPrefix.regex=.*\ndebezium.transforms.AddPrefix.replacement=data:$0\n\n# Logging\n# Uncomment the following lines if running Debezium Server as a Java standalone process (non-containerized).\n#quarkus.log.file.enable=true\n#quarkus.log.file.path=<LOG_FILE_PATH>\n#quarkus.log.file.rotation.max-file-size=100M\n#quarkus.log.file.rotation.rotate-on-boot=true\n#quarkus.log.file.rotation.file-suffix=.yyyy-MM-dd.gz\n#quarkus.log.file.rotation.max-backup-index=3\n\n# The default minimum log level for every log category, change only quarkus.log.level when needed.\nquarkus.log.min-level=TRACE\n# The default log level for every log category.\nquarkus.log.level=INFO\n# Determine whether to enable the JSON console formatting extension, which disables \"normal\" console formatting.\nquarkus.log.console.json=false\n# The port on which Debezium exposes Microprofile Health endpoint and other exposed status information.\nquarkus.http.port=8088","section_id":"applicationproperties"}]}
{"schema_version":2,"id":"integrate/write-behind/reference/debezium/mysql","title":"Write-behind configuration for mysql","url":"https://redis.io/docs/latest/integrate/write-behind/reference/debezium/mysql/","summary":"Describes the `application.properties` settings that configure Debezium Server for mysql","aliases":["/integrate/redis-data-integration/reference/debezium/mysql/","/integrate/redis-data-integration/write-behind/reference/debezium/mysql/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"778da3eed7bb59a6c7a89504241053770343d6e6d1506a4d336311b0de1ced68","sections":[{"id":"applicationproperties","title":"application.properties","role":"overview","text":"[code example]"}],"examples":[{"id":"applicationproperties-ex0","language":"properties","code":"debezium.sink.type=redis\ndebezium.sink.redis.message.format=extended\ndebezium.sink.redis.address=<RDI_HOST>:<RDI_PORT>\n# Comment the following line if not using a password for Write-behind.\ndebezium.sink.redis.password=<RDI_PASSWORD>\ndebezium.sink.redis.memory.limit.mb=80\n# Redis SSL/TLS\n#debezium.sink.redis.ssl.enabled=true\n# When Redis is configured with a replica shard, these properties allow to verify that the data has been written to the replica.\n#debezium.sink.redis.wait.enabled=true\n#debezium.sink.redis.wait.timeout.ms=1000\n#debezium.sink.redis.wait.retry.enabled=true\n#debezium.sink.redis.wait.retry.delay.ms=1000\n#debezium.source.database.history.redis.ssl.enabled=true\n# Location of the Java keystore file containing an application process' own certificate and private key.\n#javax.net.ssl.keyStore=<KEY_STORE_FILE_PATH>\n# Password to access the private key from the keystore file specified by javax.net.ssl.keyStore. This password is used twice: To unlock the keystore file (store password), and To decrypt the private key stored in the keystore (key password).\n#javax.net.ssl.keyStorePassword=<KEY_STORE_PASSWORD>\n# Location of the Java keystore file containing the collection of CA certificates trusted by this application process (trust store).\n#javax.net.ssl.trustStore=<TRUSE_STORE_FILE_PATH>\n# Password to unlock the keystore file (store password) specified by javax.net.ssl.trustStore.\n#javax.net.ssl.trustStorePassword=<TRUST_STORE_PASSWORD>\n\ndebezium.source.connector.class=io.debezium.connector.mysql.MySqlConnector\n# A numeric ID of this database client, which must be unique across all currently-running database processes in the MySQL cluster.\ndebezium.source.database.server.id=1\ndebezium.source.offset.storage=io.debezium.storage.redis.offset.RedisOffsetBackingStore\ndebezium.source.topic.prefix=<SOURCE_LOGICAL_SERVER_NAME>\n\ndebezium.source.database.hostname=<SOURCE_DB_HOST>\ndebezium.source.database.port=<SOURCE_DB_PORT>\ndebezium.source.database.user=<SOURCE_DB_USER>\ndebezium.source.database.password=<SOURCE_DB_PASSWORD>\ndebezium.source.include.schema.changes=false\n# Determines whether the connector should omit publishing change events when there are no modifications in the included columns.\ndebezium.source.skip.messages.without.change=true\ndebezium.source.offset.flush.interval.ms=1000\ndebezium.source.tombstones.on.delete=false\ndebezium.source.schema.history.internal=io.debezium.storage.redis.history.RedisSchemaHistory\n\n# Important: Do NOT use `include` and `exclude` database lists at the same time, use either `include` or `exclude`.\n# An optional, comma-separated list of regular expressions that match database names to be monitored.\n# By default, all databases are monitored.\n#debezium.source.database.include.list=<DB_NAME1>,<DB_NAME2>...\n# An optional, comma-separated list of regular expressions that match database names for which you do not want to capture changes.\n#debezium.source.database.exclude.list=<DB_NAME1>,<DB_NAME2>...\n# Important: Do NOT use `include` and `exclude` table lists at the same time, use either `include` or `exclude`.\n# An optional, comma-separated list of regular expressions that match fully-qualified table identifiers of tables whose changes you want to capture.\n#debezium.source.table.include.list=<DB_NAME.TABLE_NAME1>,<DB_NAME.TABLE_NAME2>...\n# An optional, comma-separated list of regular expressions that match fully-qualified table identifiers for tables whose changes you do not want to capture.\n#debezium.source.table.exclude.list=<DB_NAME.TABLE_NAME1>,<DB_NAME.TABLE_NAME2>...\n\n# Important: Do NOT use include and exclude column lists at the same time, use either include or exclude.\n# An optional, comma-separated list of regular expressions that match the fully-qualified names of columns to include in change event record values.\n#debezium.source.column.include.list=<DB_NAME.TABLE_NAME.COLUMN_NAME1>,<DB_NAME.TABLE_NAME.COLUMN_NAME2>...\n# An optional, comma-separated list of regular expressions that match the fully-qualified names of columns to exclude from change event record values.\n#debezium.source.column.exclude.list=<DB_NAME.TABLE_NAME.COLUMN_NAME1>,<DB_NAME.TABLE_NAME.COLUMN_NAME2>...\n\n# Records only DDL statements that are relevant to tables whose changes are being captured by Debezium.\n# In case of changing the captured tables, run `redis-di reset`.\ndebezium.source.schema.history.internal.store.only.captured.tables.ddl=true\n\n# Whether to include the detailed schema information generated by Debezium in each record written to RDI.\n# Note: Including the schema reduces the initial sync throughput and is not recommended for large data sets.\ndebezium.source.key.converter.schemas.enable=false\ndebezium.source.value.converter.schemas.enable=false\n# When detailed schema information is excluded, handle decimal numeric types as strings.\ndebezium.source.decimal.handling.mode=string\n\ndebezium.transforms=AddPrefix\ndebezium.transforms.AddPrefix.type=org.apache.kafka.connect.transforms.RegexRouter\ndebezium.transforms.AddPrefix.regex=.*\ndebezium.transforms.AddPrefix.replacement=data:$0\n\n# Logging\n# Uncomment the following lines if running Debezium Server as a Java standalone process (non-containerized).\n#quarkus.log.file.enable=true\n#quarkus.log.file.path=<LOG_FILE_PATH>\n#quarkus.log.file.rotation.max-file-size=100M\n#quarkus.log.file.rotation.rotate-on-boot=true\n#quarkus.log.file.rotation.file-suffix=.yyyy-MM-dd.gz\n#quarkus.log.file.rotation.max-backup-index=3\n\n# The default minimum log level for every log category, change only quarkus.log.level when needed.\nquarkus.log.min-level=TRACE\n# The default log level for every log category.\nquarkus.log.level=INFO\n# Determine whether to enable the JSON console formatting extension, which disables \"normal\" console formatting.\nquarkus.log.console.json=false\n# The port on which Debezium exposes Microprofile Health endpoint and other exposed status information.\nquarkus.http.port=8088","section_id":"applicationproperties"}]}
{"schema_version":2,"id":"integrate/write-behind/reference/debezium/oracle","title":"Write-behind configuration for oracle","url":"https://redis.io/docs/latest/integrate/write-behind/reference/debezium/oracle/","summary":"Describes the `application.properties` settings that configure Debezium Server for oracle","aliases":["/integrate/redis-data-integration/reference/debezium/oracle/","/integrate/redis-data-integration/write-behind/reference/debezium/oracle/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"5dfb940cfc7e1aaca7a6d207ce0a31b5c67ae516bfcdb9ad60056c0272a12292","sections":[{"id":"applicationproperties","title":"application.properties","role":"overview","text":"[code example]"}],"examples":[{"id":"applicationproperties-ex0","language":"properties","code":"debezium.sink.type=redis\ndebezium.sink.redis.message.format=extended\ndebezium.sink.redis.address=<RDI_HOST>:<RDI_PORT>\n# Comment the following line if not using a password for Write-behind.\ndebezium.sink.redis.password=<RDI_PASSWORD>\ndebezium.sink.redis.memory.limit.mb=80\n# Redis SSL/TLS\n#debezium.sink.redis.ssl.enabled=true\n# When Redis is configured with a replica shard, these properties allow to verify that the data has been written to the replica.\n#debezium.sink.redis.wait.enabled=true\n#debezium.sink.redis.wait.timeout.ms=1000\n#debezium.sink.redis.wait.retry.enabled=true\n#debezium.sink.redis.wait.retry.delay.ms=1000\n#debezium.source.database.history.redis.ssl.enabled=true\n# Location of the Java keystore file containing an application process' own certificate and private key.\n#javax.net.ssl.keyStore=<KEY_STORE_FILE_PATH>\n# Password to access the private key from the keystore file specified by javax.net.ssl.keyStore. This password is used twice: To unlock the keystore file (store password), and To decrypt the private key stored in the keystore (key password).\n#javax.net.ssl.keyStorePassword=<KEY_STORE_PASSWORD>\n# Location of the Java keystore file containing the collection of CA certificates trusted by this application process (trust store).\n#javax.net.ssl.trustStore=<TRUSE_STORE_FILE_PATH>\n# Password to unlock the keystore file (store password) specified by javax.net.ssl.trustStore.\n#javax.net.ssl.trustStorePassword=<TRUST_STORE_PASSWORD>\n\ndebezium.source.connector.class=io.debezium.connector.oracle.OracleConnector\ndebezium.source.log.mining.strategy=online_catalog\ndebezium.source.log.mining.transaction.retention.ms=180000\n# This mode creates a JDBC query that filters not only operation types at the database level, but also schema, table, and username include/exclude lists.\ndebezium.source.log.mining.query.filter.mode=in\n# The name of the Oracle Pluggable Database that the connector captures changes from.\n# For non-CDB installation, do not specify this property.\n#debezium.source.database.pdb.name=ORCLPDB1\n# Enables capturing and serialization of large object (CLOB, NCLOB, and BLOB) column values in change events.\n#debezium.source.lob.enabled=true\n# Specifies the constant that the connector provides to indicate that the original value is unchanged and not provided by the database.\n#debezium.source.unavailable.value.placeholder=__debezium_unavailable_value\ndebezium.source.offset.storage=io.debezium.storage.redis.offset.RedisOffsetBackingStore\ndebezium.source.topic.prefix=<SOURCE_LOGICAL_SERVER_NAME>\ndebezium.source.database.dbname=<SOURCE_DB_NAME>\n\ndebezium.source.database.hostname=<SOURCE_DB_HOST>\ndebezium.source.database.port=<SOURCE_DB_PORT>\ndebezium.source.database.user=<SOURCE_DB_USER>\ndebezium.source.database.password=<SOURCE_DB_PASSWORD>\ndebezium.source.include.schema.changes=false\n# Determines whether the connector should omit publishing change events when there are no modifications in the included columns.\ndebezium.source.skip.messages.without.change=true\ndebezium.source.offset.flush.interval.ms=1000\ndebezium.source.tombstones.on.delete=false\ndebezium.source.schema.history.internal=io.debezium.storage.redis.history.RedisSchemaHistory\n\n# Important: Do NOT use `include` and `exclude` table lists at the same time, use either `include` or `exclude`.\n# An optional, comma-separated list of regular expressions that match fully-qualified table identifiers of tables whose changes you want to capture.\n#debezium.source.table.include.list=<SCHEMA_NAME.TABLE_NAME1>,<SCHEMA_NAME.TABLE_NAME2>...\n# An optional, comma-separated list of regular expressions that match fully-qualified table identifiers for tables whose changes you do not want to capture.\n#debezium.source.table.exclude.list=<SCHEMA_NAME.TABLE_NAME1>,<SCHEMA_NAME.TABLE_NAME2>...\n\n# Important: Do NOT use include and exclude column lists at the same time, use either include or exclude.\n# An optional, comma-separated list of regular expressions that match the fully-qualified names of columns to include in change event record values.\n#debezium.source.column.include.list=<SCHEMA_NAME.TABLE_NAME.COLUMN_NAME1>,<SCHEMA_NAME.TABLE_NAME.COLUMN_NAME2>...\n# An optional, comma-separated list of regular expressions that match the fully-qualified names of columns to exclude from change event record values.\n#debezium.source.column.exclude.list=<SCHEMA_NAME.TABLE_NAME.COLUMN_NAME1>,<SCHEMA_NAME.TABLE_NAME.COLUMN_NAME2>...\n\n# Records only DDL statements that are relevant to tables whose changes are being captured by Debezium.\n# In case of changing the captured tables, run `redis-di reset`.\ndebezium.source.schema.history.internal.store.only.captured.tables.ddl=true\n\n# Whether to include the detailed schema information generated by Debezium in each record written to RDI.\n# Note: Including the schema reduces the initial sync throughput and is not recommended for large data sets.\ndebezium.source.key.converter.schemas.enable=false\ndebezium.source.value.converter.schemas.enable=false\n# When detailed schema information is excluded, handle decimal numeric types as strings.\ndebezium.source.decimal.handling.mode=string\n\ndebezium.transforms=AddPrefix\ndebezium.transforms.AddPrefix.type=org.apache.kafka.connect.transforms.RegexRouter\ndebezium.transforms.AddPrefix.regex=.*\ndebezium.transforms.AddPrefix.replacement=data:$0\n\n# Logging\n# Uncomment the following lines if running Debezium Server as a Java standalone process (non-containerized).\n#quarkus.log.file.enable=true\n#quarkus.log.file.path=<LOG_FILE_PATH>\n#quarkus.log.file.rotation.max-file-size=100M\n#quarkus.log.file.rotation.rotate-on-boot=true\n#quarkus.log.file.rotation.file-suffix=.yyyy-MM-dd.gz\n#quarkus.log.file.rotation.max-backup-index=3\n\n# The default minimum log level for every log category, change only quarkus.log.level when needed.\nquarkus.log.min-level=TRACE\n# The default log level for every log category.\nquarkus.log.level=INFO\n# Determine whether to enable the JSON console formatting extension, which disables \"normal\" console formatting.\nquarkus.log.console.json=false\n# The port on which Debezium exposes Microprofile Health endpoint and other exposed status information.\nquarkus.http.port=8088","section_id":"applicationproperties"}]}
{"schema_version":2,"id":"integrate/write-behind/reference/debezium/postgresql","title":"Write-behind configuration for postgresql","url":"https://redis.io/docs/latest/integrate/write-behind/reference/debezium/postgresql/","summary":"Describes the `application.properties` settings that configure Debezium Server for postgresql","aliases":["/integrate/redis-data-integration/reference/debezium/postgresql/","/integrate/redis-data-integration/write-behind/reference/debezium/postgresql/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"a4b0e14d608ccb0b1442053742e3d123e06ca1f55af8c40b1f4c75b9823514d2","sections":[{"id":"applicationproperties","title":"application.properties","role":"overview","text":"[code example]"}],"examples":[{"id":"applicationproperties-ex0","language":"properties","code":"debezium.sink.type=redis\ndebezium.sink.redis.message.format=extended\ndebezium.sink.redis.address=<RDI_HOST>:<RDI_PORT>\n# Comment the following line if not using a password for Write-behind.\ndebezium.sink.redis.password=<RDI_PASSWORD>\ndebezium.sink.redis.memory.limit.mb=80\n# Redis SSL/TLS\n#debezium.sink.redis.ssl.enabled=true\n# When Redis is configured with a replica shard, these properties allow to verify that the data has been written to the replica.\n#debezium.sink.redis.wait.enabled=true\n#debezium.sink.redis.wait.timeout.ms=1000\n#debezium.sink.redis.wait.retry.enabled=true\n#debezium.sink.redis.wait.retry.delay.ms=1000\n#debezium.source.database.history.redis.ssl.enabled=true\n# Location of the Java keystore file containing an application process' own certificate and private key.\n#javax.net.ssl.keyStore=<KEY_STORE_FILE_PATH>\n# Password to access the private key from the keystore file specified by javax.net.ssl.keyStore. This password is used twice: To unlock the keystore file (store password), and To decrypt the private key stored in the keystore (key password).\n#javax.net.ssl.keyStorePassword=<KEY_STORE_PASSWORD>\n# Location of the Java keystore file containing the collection of CA certificates trusted by this application process (trust store).\n#javax.net.ssl.trustStore=<TRUSE_STORE_FILE_PATH>\n# Password to unlock the keystore file (store password) specified by javax.net.ssl.trustStore.\n#javax.net.ssl.trustStorePassword=<TRUST_STORE_PASSWORD>\n\ndebezium.source.connector.class=io.debezium.connector.postgresql.PostgresConnector\ndebezium.source.plugin.name=pgoutput\ndebezium.source.offset.storage=io.debezium.storage.redis.offset.RedisOffsetBackingStore\ndebezium.source.topic.prefix=<SOURCE_LOGICAL_SERVER_NAME>\ndebezium.source.database.dbname=<SOURCE_DB_NAME>\n\ndebezium.source.database.hostname=<SOURCE_DB_HOST>\ndebezium.source.database.port=<SOURCE_DB_PORT>\ndebezium.source.database.user=<SOURCE_DB_USER>\ndebezium.source.database.password=<SOURCE_DB_PASSWORD>\ndebezium.source.include.schema.changes=false\n# Determines whether the connector should omit publishing change events when there are no modifications in the included columns.\n# This property takes effect when the `REPLICA IDENTITY` of the table is set to `FULL`.\ndebezium.source.skip.messages.without.change=true\ndebezium.source.offset.flush.interval.ms=1000\ndebezium.source.tombstones.on.delete=false\ndebezium.source.schema.history.internal=io.debezium.storage.redis.history.RedisSchemaHistory\n\n# Important: Do NOT use `include` and `exclude` table lists at the same time, use either `include` or `exclude`.\n# An optional, comma-separated list of regular expressions that match fully-qualified table identifiers of tables whose changes you want to capture.\n#debezium.source.table.include.list=<SCHEMA_NAME.TABLE_NAME1>,<SCHEMA_NAME.TABLE_NAME2>...\n# An optional, comma-separated list of regular expressions that match fully-qualified table identifiers for tables whose changes you do not want to capture.\n#debezium.source.table.exclude.list=<SCHEMA_NAME.TABLE_NAME1>,<SCHEMA_NAME.TABLE_NAME2>...\n\n# Important: Do NOT use include and exclude column lists at the same time, use either include or exclude.\n# An optional, comma-separated list of regular expressions that match the fully-qualified names of columns to include in change event record values.\n#debezium.source.column.include.list=<SCHEMA_NAME.TABLE_NAME.COLUMN_NAME1>,<SCHEMA_NAME.TABLE_NAME.COLUMN_NAME2>...\n# An optional, comma-separated list of regular expressions that match the fully-qualified names of columns to exclude from change event record values.\n#debezium.source.column.exclude.list=<SCHEMA_NAME.TABLE_NAME.COLUMN_NAME1>,<SCHEMA_NAME.TABLE_NAME.COLUMN_NAME2>...\n\n# Whether to include the detailed schema information generated by Debezium in each record written to RDI.\n# Note: Including the schema reduces the initial sync throughput and is not recommended for large data sets.\ndebezium.source.key.converter.schemas.enable=false\ndebezium.source.value.converter.schemas.enable=false\n# When detailed schema information is excluded, handle decimal numeric types as strings.\ndebezium.source.decimal.handling.mode=string\n\ndebezium.transforms=AddPrefix\ndebezium.transforms.AddPrefix.type=org.apache.kafka.connect.transforms.RegexRouter\ndebezium.transforms.AddPrefix.regex=.*\ndebezium.transforms.AddPrefix.replacement=data:$0\n\n# Logging\n# Uncomment the following lines if running Debezium Server as a Java standalone process (non-containerized).\n#quarkus.log.file.enable=true\n#quarkus.log.file.path=<LOG_FILE_PATH>\n#quarkus.log.file.rotation.max-file-size=100M\n#quarkus.log.file.rotation.rotate-on-boot=true\n#quarkus.log.file.rotation.file-suffix=.yyyy-MM-dd.gz\n#quarkus.log.file.rotation.max-backup-index=3\n\n# The default minimum log level for every log category, change only quarkus.log.level when needed.\nquarkus.log.min-level=TRACE\n# The default log level for every log category.\nquarkus.log.level=INFO\n# Determine whether to enable the JSON console formatting extension, which disables \"normal\" console formatting.\nquarkus.log.console.json=false\n# The port on which Debezium exposes Microprofile Health endpoint and other exposed status information.\nquarkus.http.port=8088","section_id":"applicationproperties"}]}
{"schema_version":2,"id":"integrate/write-behind/reference/debezium/sqlserver","title":"Write-behind configuration for sqlserver","url":"https://redis.io/docs/latest/integrate/write-behind/reference/debezium/sqlserver/","summary":"Describes the `application.properties` settings that configure Debezium Server for sqlserver","aliases":["/integrate/redis-data-integration/reference/debezium/sqlserver/","/integrate/redis-data-integration/write-behind/reference/debezium/sqlserver/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"929fc3b4494425839cb90e9d63ca0fb49f82882bc92baada4a06005ea34b9404","sections":[{"id":"applicationproperties","title":"application.properties","role":"overview","text":"[code example]"}],"examples":[{"id":"applicationproperties-ex0","language":"properties","code":"debezium.sink.type=redis\ndebezium.sink.redis.message.format=extended\ndebezium.sink.redis.address=<RDI_HOST>:<RDI_PORT>\n# Comment the following line if not using a password for Write-behind.\ndebezium.sink.redis.password=<RDI_PASSWORD>\ndebezium.sink.redis.memory.limit.mb=80\n# Redis SSL/TLS\n#debezium.sink.redis.ssl.enabled=true\n# When Redis is configured with a replica shard, these properties allow to verify that the data has been written to the replica.\n#debezium.sink.redis.wait.enabled=true\n#debezium.sink.redis.wait.timeout.ms=1000\n#debezium.sink.redis.wait.retry.enabled=true\n#debezium.sink.redis.wait.retry.delay.ms=1000\n#debezium.source.database.history.redis.ssl.enabled=true\n# Location of the Java keystore file containing an application process' own certificate and private key.\n#javax.net.ssl.keyStore=<KEY_STORE_FILE_PATH>\n# Password to access the private key from the keystore file specified by javax.net.ssl.keyStore. This password is used twice: To unlock the keystore file (store password), and To decrypt the private key stored in the keystore (key password).\n#javax.net.ssl.keyStorePassword=<KEY_STORE_PASSWORD>\n# Location of the Java keystore file containing the collection of CA certificates trusted by this application process (trust store).\n#javax.net.ssl.trustStore=<TRUSE_STORE_FILE_PATH>\n# Password to unlock the keystore file (store password) specified by javax.net.ssl.trustStore.\n#javax.net.ssl.trustStorePassword=<TRUST_STORE_PASSWORD>\n\ndebezium.source.connector.class=io.debezium.connector.sqlserver.SqlServerConnector\ndebezium.source.database.names=<SOURCE_DB_NAME>\ndebezium.source.database.encrypt=false\ndebezium.source.offset.storage=io.debezium.storage.redis.offset.RedisOffsetBackingStore\ndebezium.source.topic.prefix=<SOURCE_LOGICAL_SERVER_NAME>\n\ndebezium.source.database.hostname=<SOURCE_DB_HOST>\ndebezium.source.database.port=<SOURCE_DB_PORT>\ndebezium.source.database.user=<SOURCE_DB_USER>\ndebezium.source.database.password=<SOURCE_DB_PASSWORD>\ndebezium.source.include.schema.changes=false\n# Determines whether the connector should omit publishing change events when there are no modifications in the included columns.\ndebezium.source.skip.messages.without.change=true\ndebezium.source.offset.flush.interval.ms=1000\ndebezium.source.tombstones.on.delete=false\ndebezium.source.schema.history.internal=io.debezium.storage.redis.history.RedisSchemaHistory\n\n# Important: Do NOT use `include` and `exclude` table lists at the same time, use either `include` or `exclude`.\n# An optional, comma-separated list of regular expressions that match fully-qualified table identifiers of tables whose changes you want to capture.\n#debezium.source.table.include.list=<SCHEMA_NAME.TABLE_NAME1>,<SCHEMA_NAME.TABLE_NAME2>...\n# An optional, comma-separated list of regular expressions that match fully-qualified table identifiers for tables whose changes you do not want to capture.\n#debezium.source.table.exclude.list=<SCHEMA_NAME.TABLE_NAME1>,<SCHEMA_NAME.TABLE_NAME2>...\n\n# Important: Do NOT use include and exclude column lists at the same time, use either include or exclude.\n# An optional, comma-separated list of regular expressions that match the fully-qualified names of columns to include in change event record values.\n#debezium.source.column.include.list=<SCHEMA_NAME.TABLE_NAME.COLUMN_NAME1>,<SCHEMA_NAME.TABLE_NAME.COLUMN_NAME2>...\n# An optional, comma-separated list of regular expressions that match the fully-qualified names of columns to exclude from change event record values.\n#debezium.source.column.exclude.list=<SCHEMA_NAME.TABLE_NAME.COLUMN_NAME1>,<SCHEMA_NAME.TABLE_NAME.COLUMN_NAME2>...\n\n# Whether to include the detailed schema information generated by Debezium in each record written to RDI.\n# Note: Including the schema reduces the initial sync throughput and is not recommended for large data sets.\ndebezium.source.key.converter.schemas.enable=false\ndebezium.source.value.converter.schemas.enable=false\n# When detailed schema information is excluded, handle decimal numeric types as strings.\ndebezium.source.decimal.handling.mode=string\n\ndebezium.transforms=AddPrefix\ndebezium.transforms.AddPrefix.type=org.apache.kafka.connect.transforms.RegexRouter\ndebezium.transforms.AddPrefix.regex=.*\ndebezium.transforms.AddPrefix.replacement=data:$0\n\n# Logging\n# Uncomment the following lines if running Debezium Server as a Java standalone process (non-containerized).\n#quarkus.log.file.enable=true\n#quarkus.log.file.path=<LOG_FILE_PATH>\n#quarkus.log.file.rotation.max-file-size=100M\n#quarkus.log.file.rotation.rotate-on-boot=true\n#quarkus.log.file.rotation.file-suffix=.yyyy-MM-dd.gz\n#quarkus.log.file.rotation.max-backup-index=3\n\n# The default minimum log level for every log category, change only quarkus.log.level when needed.\nquarkus.log.min-level=TRACE\n# The default log level for every log category.\nquarkus.log.level=INFO\n# Determine whether to enable the JSON console formatting extension, which disables \"normal\" console formatting.\nquarkus.log.console.json=false\n# The port on which Debezium exposes Microprofile Health endpoint and other exposed status information.\nquarkus.http.port=8088","section_id":"applicationproperties"}]}
{"schema_version":2,"id":"integrate/write-behind/reference","title":"Write-behind reference","url":"https://redis.io/docs/latest/integrate/write-behind/reference/","summary":"Collects reference material for Write-behind","aliases":["/integrate/redis-data-integration/write-behind/reference/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2024-09-10T16:00:43+01:00","children":[{"id":"integrate/write-behind/reference/cli","summary":"Write-behind CLI reference","title":"CLI","url":"https://redis.io/docs/latest/integrate/write-behind/reference/cli/"},{"id":"integrate/write-behind/reference/data-types-conversion","summary":"Data type conversion reference","title":"Data type conversion","url":"https://redis.io/docs/latest/integrate/write-behind/reference/data-types-conversion/"},{"id":"integrate/write-behind/reference/config-yaml-reference","summary":"Write-behind configuration file reference","title":"Write-behind configuration file","url":"https://redis.io/docs/latest/integrate/write-behind/reference/config-yaml-reference/"},{"id":"integrate/write-behind/reference/data-transformation-block-types","summary":"Data transformation block type reference","title":"Data transformation block types","url":"https://redis.io/docs/latest/integrate/write-behind/reference/data-transformation-block-types/"},{"id":"integrate/write-behind/reference/jmespath-custom-functions","summary":"JMESPath custom function reference","title":"JMESPath custom functions","url":"https://redis.io/docs/latest/integrate/write-behind/reference/jmespath-custom-functions/"},{"id":"integrate/write-behind/reference/debezium","summary":"Application properties settings used to configure Debezim Server for source database servers","title":"Debezium Server configuration file","url":"https://redis.io/docs/latest/integrate/write-behind/reference/debezium/"}],"page_type":"index","sections":[],"examples":[]}
{"schema_version":2,"id":"integrate/write-behind/reference/jmespath-custom-functions","title":"JMESPath custom functions","url":"https://redis.io/docs/latest/integrate/write-behind/reference/jmespath-custom-functions/","summary":"JMESPath custom function reference","aliases":["/integrate/redis-data-integration/write-behind/reference/jmespath-custom-functions/"],"tags":["docs","integrate","rs","rdi"],"last_updated":"2024-09-10T15:38:09+01:00","page_type":"content","content_hash":"0cc0698b042571514d966343a080ac07b09748f215ab39815b55309df9b2a63a","sections":[{"id":"content","title":"Content","role":"content","text":"| Function             | Description                                                                                                                         | Example                                                                                                                                                                                                  | Comments                                                                                                                                                         |\n| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `base64_decode`      | Decodes a base64(RFC 4648) encoded string                                                                                           | Input: `{\"encoded\": \"SGVsbG8gV29ybGQh\"}` <br /> Expression: `base64_decode(encoded)` <br /> Output: `Hello World!`                                                                                       |                                                                                                                                                                  |\n| `capitalize`         | Capitalizes all the words in the string                                                                                             | Input: `{\"name\": \"john doe\"}` <br /> Expression: `capitalize(name)` <br /> Output: `John Doe`                                                                                                            |                                                                                                                                                                  |\n| `concat`             | Concatenates an array of variables or literals                                                                                      | Input: `{\"fname\": \"john\", \"lname\": \"doe\"}` <br /> Expression: `concat([fname, ' ' ,lname])` <br /> Output: `john doe`                                                                                    | This is equivalent to the more verbose built-in expression: `' '.join([fname,lname])`                                                                            |\n| `filter_entries`     | Filters entries in a dictionary (object) using the given JMESPath predicate                                                         | Input: `{ \"name\": \"John\", \"age\": 30, \"country\": \"US\", \"score\": 15}` <br /> Expression: `` filter_entries(@, `key == 'name' \\|\\| key == 'age'`)``<br /> Output:`{\"name\": \"John\", \"age\": 30 }`             |                                                                                                                                                                  |\n| `from_entries`       | Converts an array of objects with `key` and `value` properties into a single object                                                 | Input: `[{\"key\": \"name\", \"value\": \"John\"}, {\"key\": \"age\", \"value\": 30}, {\"key\": \"city\", \"value\": null}]` <br /> Expression: `from_entries(@)` <br /> Output: `{\"name\": \"John\", \"age\": 30, \"city\": null}` |                                                                                                                                                                  |\n| `hash`               | Calculates a hash using the `hash_name` hash function and returns its hexadecimal representation                                    | Input: `{\"some_str\": \"some_value\"}` <br /> Expression: `hash(some_str, &#96;sha1&#96;)` <br /> Output: `8c818171573b03feeae08b0b4ffeb6999e3afc05`                                                        | Supported algorithms: sha1 (default), sha256, md5, sha384, sha3_384, blake2b, sha512, sha3_224, sha224, sha3_256, sha3_512, blake2s                              |\n| `in`                 | Checks if an element matches any value in a list of values                                                                          | Input: `{\"el\": \"b\"}` <br /> Expression: `in(el, `[\"a\", \"b\", \"c\"]`)` <br /> Output: `True`                                                                                                                |                                                                                                                                                                  |\n| `left`               | Returns a specified number of characters from the start of a given text string                                                      | Input: `{\"greeting\": \"hello world!\"}` <br /> Expression: `left(greeting, `5`)` <br /> Output: `hello`                                                                                                    |                                                                                                                                                                  |\n| `lower`              | Converts all uppercase characters in a string into lowercase characters                                                             | Input: `{\"fname\": \"John\"}` <br /> Expression: `lower(fname)` <br /> Output: `john`                                                                                                                       |                                                                                                                                                                  |\n| `mid`                | Returns a specified number of characters from the middle of a given text string                                                     | Input: `{\"greeting\": \"hello world!\"}` <br /> Expression: `mid(greeting, `4`, `3`)` <br /> Output: `o w`                                                                                                  |                                                                                                                                                                  |\n| `json_parse`         | Returns parsed object from the given json string                                                                                    | Input: `{\"data\": '{\"greeting\": \"hello world!\"}'}` <br /> Expression: `parse_json(data)` <br /> Output: `{\"greeting\": \"hello world!\"}`                                                                    |                                                                                                                                                                  |\n| `regex_replace`      | Replaces a string that matches a regular expression                                                                                 | Input: `{\"text\": \"Banana Bannnana\"}` <br /> Expression: `regex_replace(text, 'Ban\\w+', 'Apple Apple')` <br /> Output: `Apple Apple`                                                                      |                                                                                                                                                                  |\n| `replace`            | Replaces all the occurrences of a substring with a new one                                                                          | Input: `{\"sentence\": \"one four three four!\"}` <br /> Expression: `replace(sentence, 'four', 'two')` <br /> Output: `one two three two!`                                                                  |                                                                                                                                                                  |\n| `right`              | Returns a specified number of characters from the end of a given text string                                                        | Input: `{\"greeting\": \"hello world!\"}` <br /> Expression: `right(greeting, `6`)` <br /> Output: `world!`                                                                                                  |                                                                                                                                                                  |\n| `split`              | Splits a string into a list of strings after breaking the given string by the specified delimiter (comma by default)                | Input: `{\"departments\": \"finance,hr,r&d\"}` <br /> Expression: `split(departments)` <br /> Output: `['finance', 'hr', 'r&d']`                                                                             | Default delimiter is comma - a different delimiter can be passed to the function as the second argument, for example: `split(departments, ';')`                  |\n| `time_delta_days`    | Returns the number of days between a given `dt` and now (positive) or the number of days that have passed from now (negative)       | Input: `{\"dt\": '2021-10-06T18:56:16.701670+00:00'}` <br /> Expression: `time_delta_days(dt)` <br /> Output: `365`                                                                                        | If `dt` is a string, ISO datetime (2011-11-04T00:05:23+04:00, for example) is assumed. If `dt` is a number, Unix timestamp (1320365123, for example) is assumed. |\n| `time_delta_seconds` | Returns the number of seconds between a given `dt` and now (positive) or the number of seconds that have passed from now (negative) | Input: `{\"dt\": '2021-10-06T18:56:16.701670+00:00'}` <br /> Expression: `time_delta_days(dt)` <br /> Output: `31557600`                                                                                   | If `dt` is a string, ISO datetime (2011-11-04T00:05:23+04:00, for example) is assumed. If `dt` is a number, Unix timestamp (1320365123, for example) is assumed. |\n| `to_entries`         | Converts a given object into an array of objects with `key` and `value` properties                                                  | Input: `{\"name\": \"John\", \"age\": 30, \"city\": null}` <br /> Expression: `to_entries(@)` <br /> Output: `[{\"key\": \"name\", \"value\": \"John\"}, {\"key\": \"age\", \"value\": 30}, {\"key\": \"city\", \"value\": null}]`   |                                                                                                                                                                  |\n| `upper`              | Converts all lowercase characters in a string into uppercase characters                                                             | Input: `{\"fname\": \"john\"}` <br /> Expression: `upper(fname)` <br /> Output: `JOHN`                                                                                                                       |                                                                                                                                                                  |\n| `uuid`               | Generates a random UUID4 and returns it as a string in standard format                                                              | Input: None <br /> Expression: `uuid()` <br /> Output: `3264b35c-ff5d-44a8-8bc7-9be409dac2b7`                                                                                                            |                                                                                                                                                                  |"}],"examples":[]}
{"schema_version":2,"id":"operate/featureform/configure-auth","title":"Configure authentication and RBAC","url":"https://redis.io/docs/latest/operate/featureform/configure-auth/","summary":"Configure deployment-wide authentication and RBAC for Redis Feature Form.","aliases":["/operate/featureform/auth/"],"tags":["docs","operate","featureform"],"last_updated":"2026-08-26T14:40:32-07:00","page_type":"content","content_hash":"c8db0add3c84f00427620abfaf63e407f8922d8ec9748d3a35554bcc02883e0f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Feature Form authenticates users and services through an external OIDC identity provider, then enforces built-in role-based access control (RBAC) on their actions. Authentication is a deployment-wide concern configured at install time; authorization is per-workspace (with a small number of deployment-scoped exceptions) and managed at runtime through role bindings.\n\nOpenID Connect (OIDC) is a standard identity layer that lets Feature Form delegate authentication to a separate identity provider (IdP). You operate an IdP yourself (such as Keycloak) or use a managed service (such as Okta); Feature Form reads the resulting JSON Web Tokens (JWTs) to enforce its own role checks.\n\nA [workspace](https://redis.io/docs/latest/develop/ai/featureform/concepts#workspaces) isolates resources; RBAC bindings control who can act on it.\n\nThe sequence:\n\n1. [Configure OIDC at deploy time](#configure-oidc-at-deploy-time)\n2. [Register a CLI client](#register-a-cli-client)\n3. [Sign in with the CLI](#sign-in-with-the-cli)\n4. [Pick built-in roles for users and groups](#built-in-roles)\n5. [Provision the first global admin](#provision-the-first-global-admin)\n6. [Set up service accounts for non-human identities](#service-accounts-and-machine-credentials)\n7. [Read the audit log](#audit)"},{"id":"authentication","title":"Authentication","role":"security","text":""},{"id":"configure-oidc-at-deploy-time","title":"Configure OIDC at deploy time","role":"configuration","text":"Set Feature Form's OIDC parameters in the Helm chart's `auth` block. `auth.oidcClientID` is the audience that Feature Form requires in access tokens. `auth.oidcCLIClientID` is the separate client that users sign in through.\n\n[code example]\n\nConfigure the IdP to include the `featureform-api` audience in access tokens issued to `featureform-cli`. Otherwise, login can succeed at the IdP while Feature Form rejects the token.\n\nUse a unique, stable `auth.deploymentID` for each environment. See [Deploy](https://redis.io/docs/latest/operate/featureform/deploy) for naming and lifecycle guidance.\n\nFor deployments where internal services reach the IdP at a different URL than external clients, use `oidcDiscoveryURL`, `oidcPublicIssuerURL`, and `oidcPublicDiscoveryURL` to split the discovery and issuer endpoints. The `oidcSkipIssuerCheck: true` flag disables issuer-claim validation. Use it only during local development.\n\nFeature Form reads role information from JWT claims on each request. It checks the following claims, in order, for matches against built-in role IDs:\n\n- `featureform_roles` (string or array)\n- `roles` (string or array)\n- `role` (string)\n- `realm_access.roles` (array; Keycloak convention)\n\nIf any of those claims contain `global_admin`, Feature Form treats the user as a global admin for that token's lifetime without a database binding. This is the typical way operators bootstrap the first admin — see [Provision the first global admin](#provision-the-first-global-admin)."},{"id":"register-a-cli-client","title":"Register a CLI client","role":"content","text":"Register a dedicated CLI client with the IdP. The client settings must match the login methods advertised through `auth.oidcCLILoginMethods`.\n\n| Login method | IdP client requirements | Helm settings |\n| --- | --- | --- |\n| `device_code` | Use a public or native client with client authentication disabled. Enable the OAuth 2.0 Device Authorization Grant. No client secret or redirect URI is used. | Set `auth.oidcCLIClientID` and `auth.oidcCLILoginMethods: \"device_code\"`. |\n| `authorization_code_pkce` | Enable the Authorization Code Grant and Proof Key for Code Exchange (PKCE) with the SHA-256 (`S256`) challenge method. Register the exact redirect URI used by the CLI. A public client is recommended. | Set `auth.oidcCLIClientID`, `auth.oidcCLILoginMethods: \"authorization_code_pkce\"`, and `auth.oidcCLIRedirectURI`. |\n\nThe IdP must allow every scope in `auth.oidcCLIScopes`. The default scopes are `openid profile offline_access`. The `offline_access` scope lets the CLI request a refresh token. Remove it when the IdP doesn't support offline access; users must sign in again after their access token expires.\n\nIf the IdP requires a confidential client for PKCE, provide its secret only at login through `FEATUREFORM_LOGIN_CLIENT_SECRET` or `--login-client-secret`. Don't put the CLI client secret in Helm values. The CLI doesn't persist the secret or the refresh token from that login.\n\nIn the Helm values file for the Feature Form release, use these settings to configure PKCE instead of device authorization:\n\n[code example]"},{"id":"sign-in-with-the-cli","title":"Sign in with the CLI","role":"content","text":"The `ff auth` commands handle login, session inspection, and token retrieval:\n\n[code example]\n\nPick a login method based on your environment:\n\n- **device-code** for headless terminals or shared shells where a local browser callback can't be reached.\n- **authorization_code_pkce** when a local browser callback works (typical for developer desktops).\n\nFeature Form lists the methods in `auth.oidcCLILoginMethods`. The CLI selects the first advertised method it supports unless you pass `--login-method`. It doesn't retry another method when the IdP rejects the selected method.\n\nUsername and password login is not recommended for production environments. For production automation, use a service account instead of storing a user's password.\n\nCLI sessions are stored in the CLI's local config (per profile). To skip interactive login entirely, set `FEATUREFORM_TOKEN` to a valid access token, or configure a service account with client credentials (see [Service accounts and machine credentials](#service-accounts-and-machine-credentials))."},{"id":"troubleshoot-cli-login","title":"Troubleshoot CLI login","role":"errors","text":"Inspect the authentication metadata provided by Feature Form through a reachable REST endpoint:\n\n[code example]\n\nIf the REST API isn't public, port-forward the REST API service and query `http://localhost:8080/api/v1/auth/metadata` instead:\n\n[code example]\n\nConfirm that `cli_client_id`, `supported_login_methods`, `scopes`, and `audience` match the IdP client. For PKCE, also confirm `cli_redirect_uri`. Then inspect the IdP discovery document:\n\n[code example]\n\nFor device authorization, the document must contain `device_authorization_endpoint`. For PKCE, it must contain `authorization_endpoint` and `token_endpoint`, and have SHA-256 support in `code_challenge_methods_supported` when that field is present.\n\n| Symptom | Check and action |\n| --- | --- |\n| `invalid_client` | Confirm `auth.oidcCLIClientID` exactly matches the IdP client. For a public client, turn off client authentication. For confidential PKCE, supply the current secret at login. |\n| `unauthorized_client` | Enable the grant required by the selected method: Device Authorization Grant for `device_code`, or Authorization Code Grant with PKCE for `authorization_code_pkce`. |\n| Feature Form doesn't show the requested login method | Set `auth.oidcCLILoginMethods` to a method supported by both the CLI and IdP, upgrade the release, and inspect the Feature Form metadata again. |\n| Redirect URI mismatch | Make `auth.oidcCLIRedirectURI` exactly match a redirect URI registered for the IdP client, including its scheme, host, port, path, and query parameters. |\n| `invalid_scope` | Allow every value in `auth.oidcCLIScopes` at the IdP, or remove unsupported optional scopes. |"},{"id":"rbac","title":"RBAC","role":"content","text":""},{"id":"built-in-roles","title":"Built-in roles","role":"content","text":"Feature Form has five built-in RBAC roles. The role ID in the left column is the literal string used in `ff rbac grant`.\n\nEach built-in role is a fixed set of finer-grained permissions—the underlying checks the authorization service runs on each request. For the full permission catalog, see [Reference > Permissions](https://redis.io/docs/latest/develop/ai/featureform/reference#permissions).\n\n| Role ID | Scope | What it grants |\n| --- | --- | --- |\n| `viewer` | Workspace | Read-only access to workspace metadata, the resource graph, the catalog, providers, and serving metadata. |\n| `operator` | Workspace | Everything a viewer has, plus writing providers, planning and applying changes, reading served features, and controlling scheduler workflows. |\n| `workspace_admin` | Workspace | Full administration of a single workspace—membership, audit, updates, deletion—plus everything an operator has. |\n| `global_admin` | Global | Workspace creation, plus full administration across every workspace in the deployment. |\n| `model` | Resource-constrained | Read access to a specific set of feature views, training sets, and serving data—nothing else. Used for model-team service accounts. |"},{"id":"role-scopes","title":"Role scopes","role":"content","text":"Every role applies at a defined breadth—deployment-wide, a single workspace, or a specific set of resources within a workspace. Feature Form has three scopes, and each role works at exactly one:\n\n- **Global** — Deployment-wide actions, such as creating workspaces. Only `global_admin` operates at this scope.\n- **Workspace** — Actions inside a single workspace: providers, secret providers, apply, graph, catalog, serving metadata, and audit. A binding at this scope applies to one workspace only — grant the role again on each workspace a user needs.\n- **Resource-constrained** — A narrower form of workspace scope that limits a binding to a specific set of resources. Used for the `model` role, which only sees serving and training-set reads for the resources it was bound to.\n\nA binding pairs a role with a scope and a user, group, or service account. For example: \"Alice has `workspace_admin` on workspace `7f2e4d8c-…`\" or \"the `payments-team` group has `global_admin`.\""},{"id":"provision-the-first-global-admin","title":"Provision the first global admin","role":"content","text":"A fresh Feature Form deployment has no role bindings in its database. There is no dedicated Helm value for an initial admin, so plan your IdP claim mapping before installing. To get the first global admin in place, choose one of two paths:\n\n**Map an IdP claim to `global_admin` (recommended for production).** Configure your IdP to issue a `featureform_roles` claim that contains `global_admin` for the appropriate user or group. Feature Form treats those tokens as global admin without a database binding, so the first admin can sign in and start granting roles to others immediately.\n\n**Bind manually after the first login.** A user with no role can still authenticate; they just can't do anything yet. From a host that already has an access token for a privileged account, run:\n\n[code example]\n\nThis option requires that *some* identity already has `global_admin`, which makes it suitable only for redirecting access from a temporary IdP-claim admin to a database-bound one, or for environments where you can run `ff` commands with a bootstrap token issued out-of-band."},{"id":"service-accounts-and-machine-credentials","title":"Service accounts and machine credentials","role":"content","text":"Non-human identities—CI runners, model-serving processes, batch jobs—authenticate with a service account that holds a public key registered with Feature Form. Feature Form supports Ed25519 keys.\n\nThe end-to-end setup:\n\n1. Register a service-account principal in your IdP.\n2. Create a machine credential that registers the principal's public key with Feature Form.\n3. Grant the service account a workspace role.\n4. Use the resulting token in your automation.\n\nCreate a credential for a service account inside a workspace:\n\n[code example]\n\nThe `ff machine-credential` command also has subcommands for `list`, `get`, `rotate`, `revoke`, and `usage` (for audit-style usage records). All of them require the `machine_credential.write` or `machine_credential.read` permission on the target workspace.\n\nGrant the service account a workspace role the same way you would a user — use `--service-account <id>` instead of `--user <id>`:\n\n[code example]"},{"id":"audit","title":"Audit","role":"content","text":"Feature Form records authorization-relevant events in an audit log. List events with:\n\n[code example]\n\nUseful filters:\n\n- `--workspace <id>` — scope to one workspace.\n- `--global` — only deployment-scoped events. Requires `global_admin`.\n- `--principal-id <id>` — events for a specific user, group, or service account.\n- `--event-type <type>` — filter by event name.\n\nCommon event types include:\n\n- `workspace.create`, `workspace.update`, `workspace.delete`\n- `rbac.grant`, `rbac.revoke`\n- `provider.write`, `secret_provider.write`\n- `apply.write`\n- `machine_credential.create`, `machine_credential.rotate`, `machine_credential.revoke`\n\nEach event includes the scope, workspace ID (if applicable), actor ID, event type, and creation timestamp. Reading the log requires the `audit.read` permission; deployment-scope reads additionally require `global_admin`."},{"id":"next-steps","title":"Next steps","role":"content","text":"- [Deploy](https://redis.io/docs/latest/operate/featureform/deploy) — chart acquisition and install with OIDC enabled.\n- [Manage workspaces](https://redis.io/docs/latest/develop/ai/featureform/manage-workspace) — create workspaces, grant roles, and verify bindings.\n- [Register providers](https://redis.io/docs/latest/develop/ai/featureform/register-providers) — connect Postgres, Redis, S3, and other backends after auth is set up.\n- [Concepts](https://redis.io/docs/latest/develop/ai/featureform/concepts) — background on workspaces and the resource graph.\n- [Quickstart](https://redis.io/docs/latest/develop/ai/featureform/quickstart) — end-to-end verification."}],"examples":[{"id":"configure-oidc-at-deploy-time-ex0","language":"yaml","code":"auth:\n  enabled: true\n  oidcIssuerURL: \"https://idp.example.com/realms/featureform\"\n  oidcClientID: \"featureform-api\"\n\n  oidcCLIClientID: \"featureform-cli\"\n  oidcCLIScopes: \"openid profile offline_access\"\n  oidcCLILoginMethods: \"device_code\"\n  deploymentID: \"acme-featureform-prod-us-west-2\"","section_id":"configure-oidc-at-deploy-time"},{"id":"register-a-cli-client-ex0","language":"yaml","code":"auth:\n  oidcCLIClientID: \"featureform-cli\"\n  oidcCLILoginMethods: \"authorization_code_pkce\"\n  oidcCLIRedirectURI: \"http://localhost:8080/callback\"","section_id":"register-a-cli-client"},{"id":"sign-in-with-the-cli-ex0","language":"bash","code":"# Interactive login. Uses the first method advertised by Feature Form.\nff auth login\n\n# Force a specific flow.\nff auth login --login-method device_code\nff auth login --login-method authorization_code_pkce\n\n# Inspect the current session.\nff auth status\nff auth whoami\n\n# Print the active access token (for use in tools that don't\n# integrate with the CLI session).\nff auth token\n\n# Clear the local session. Does not revoke tokens on the IdP.\nff auth logout","section_id":"sign-in-with-the-cli"},{"id":"troubleshoot-cli-login-ex0","language":"bash","code":"curl --fail --silent --show-error \\\n  https://api.example.com/api/v1/auth/metadata | python3 -m json.tool","section_id":"troubleshoot-cli-login"},{"id":"troubleshoot-cli-login-ex1","language":"bash","code":"kubectl --namespace <namespace> port-forward \\\n  service/featureform-featureform-rest 8080:8080","section_id":"troubleshoot-cli-login"},{"id":"troubleshoot-cli-login-ex2","language":"bash","code":"curl --fail --silent --show-error \\\n  https://idp.example.com/realms/featureform/.well-known/openid-configuration \\\n  | python3 -m json.tool","section_id":"troubleshoot-cli-login"},{"id":"provision-the-first-global-admin-ex0","language":"bash","code":"ff rbac grant global_admin --global --user <user-principal-id>","section_id":"provision-the-first-global-admin"},{"id":"service-accounts-and-machine-credentials-ex0","language":"bash","code":"ff machine-credential create ci-runner-key \\\n  --workspace <workspace-id> \\\n  --service-account <service-account-principal-id> \\\n  --public-key \"<key material>\" \\\n  --algorithm Ed25519","section_id":"service-accounts-and-machine-credentials"},{"id":"service-accounts-and-machine-credentials-ex1","language":"bash","code":"ff rbac grant operator \\\n  --workspace <workspace-id> \\\n  --service-account ci-runner","section_id":"service-accounts-and-machine-credentials"},{"id":"audit-ex0","language":"bash","code":"ff audit list \\\n  --workspace <workspace-id> \\\n  --event-type workspace.delete \\\n  --page-size 50","section_id":"audit"}]}
{"schema_version":2,"id":"operate/featureform/deploy","title":"Deploy","url":"https://redis.io/docs/latest/operate/featureform/deploy/","summary":"Deploy and operate Feature Form on Kubernetes.","tags":["docs","operate","featureform"],"last_updated":"2026-10-05T16:11:44+01:00","page_type":"content","content_hash":"c352ef64f1aed27b7ab170aec4f6f8568978d20b3c95382539a4a1d7fb955beb","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Install Redis Feature Form on Kubernetes with the Helm chart, durable PostgreSQL state, OpenID Connect (OIDC) authentication, and a license key."},{"id":"install","title":"Install","role":"setup","text":"Prerequisites: For production environments, use an external PostgreSQL database and keep credentials and the license key in Kubernetes Secrets.\n\nTo get started:\n\n1. Get the chart.\n2. Create the namespace and Secrets.\n3. Create a values file.\n4. Render and install the release.\n5. Verify the deployment.\n6. Install and connect the `ff` command-line interface (CLI)."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"- Kubernetes 1.27+.\n- Helm 3.14+.\n- Network access to the chart and image repositories. Configure `imagePullSecrets` if your cluster requires registry credentials.\n- An OIDC issuer URL, an API audience, and a CLI client configured for device authorization. See [Register a CLI client](https://redis.io/docs/latest/operate/featureform/configure-auth#register-a-cli-client).\n- An external PostgreSQL database for production state. Its role must be able to create and alter Feature Form tables during migrations.\n- A Feature Form license key from your Redis account team.\n- A public domain name and Transport Layer Security (TLS) certificate for each externally exposed endpoint."},{"id":"1-get-the-chart","title":"1. Get the chart","role":"content","text":"The Feature Form Helm chart is published as an Open Container Initiative (OCI) artifact on Docker Hub:\n\n[code example]\n\nInstall the chart directly from this path. Always pin `--version` to the Feature Form version you intend to run."},{"id":"2-create-the-namespace-and-secrets","title":"2. Create the namespace and Secrets","role":"content","text":"Create the release namespace before creating referenced Secrets:\n\n[code example]\n\nUse your normal secret-management process to create these Secrets in the same namespace as Feature Form:\n\n- `featureform-postgres`, with a `POSTGRES_URL` key containing the PostgreSQL connection URL. Require TLS according to your database policy.\n- `featureform-license`, with the license key stored in `license.key`.\n\nThe Secret names and local file paths in these examples aren't required. If you choose different Secret names or keys, update the matching `postgres.*` and `license.*` settings in the values file. You can also choose a different values filename and pass it to Helm with `--values`.\n\nFor example, create the PostgreSQL Secret from a restricted environment file whose entry is `POSTGRES_URL=<postgres-connection-url>`:\n\n[code example]\n\nCreate the license Secret from a restricted key file:\n\n[code example]\n\nCreate an image-pull Secret as well if your cluster requires registry authentication.\n\nAdd this entry when you create `values-production.yaml`:\n\n[code example]"},{"id":"3-create-a-values-file","title":"3. Create a values file","role":"content","text":"Create `values-production.yaml` and set `auth.deploymentID` to a stable, lowercase ASCII identifier for this Feature Form environment, such as `acme-featureform-prod-us-west-2`. Keep the value the same across replicas and upgrades, and use a different value for each environment.\n\nFeature Form accepts any value that isn't empty after trimming whitespace. Changing it requires CLI users to sign in again.\n\n[code example]\n\nIn `values-production.yaml`, replace the ingress class and gRPC backend annotation placeholders with the values required by your maintained ingress controller. The controller must support gRPC backends and TLS termination.\n\n`auth.publicRestEndpoint` and `auth.publicGrpcEndpoint` show the public endpoints through authentication discovery. Use addresses reachable by your CLI users. If internal services reach the identity provider through a different URL, configure the internal and public OIDC URLs as described in [Configure authentication and role-based access control](https://redis.io/docs/latest/operate/featureform/configure-auth)."},{"id":"4-render-and-install-the-release","title":"4. Render and install the release","role":"content","text":"Render the chart before changing the cluster:\n\n[code example]\n\nInstall the release and wait for its workloads to become ready:\n\n[code example]"},{"id":"5-verify-the-deployment","title":"5. Verify the deployment","role":"content","text":"Confirm the server rollout, services, and PostgreSQL migration init container:\n\n[code example]\n\nConfirm that the `migrate` init container completed with exit code `0` for every server pod:\n\n[code example]\n\nEach line must end with `Completed` and `0`. If the status fields are empty, wait for the init container to finish and run the command again.\n\nIf migration fails, inspect its logs before restarting the pod:\n\n[code example]\n\nVerify the mounted license. Inspect the printed `Status:` value rather than relying only on the command's exit status:\n\n[code example]\n\nThen verify REST readiness through the public endpoint:\n\n[code example]\n\nThe readiness endpoint checks the server and registered scheduler dependencies. It doesn't prove that every external provider is healthy."},{"id":"6-install-and-connect-the-ff-cli","title":"6. Install and connect the `ff` CLI","role":"content","text":"The Feature Form CLI ships as the `redis-featureform` package on PyPI. **Don't run `pip install featureform`** — that installs an unrelated upstream project. Install it in a virtual environment and pin it to the deployment version:\n\n[code example]\n\nWhen you expose the gRPC endpoint publicly, connect the CLI through that endpoint. gRPC is the CLI default, but the explicit transport makes the saved profile clear:\n\n[code example]\n\nIf you expose only REST, log in through that endpoint instead:\n\n[code example]\n\nIf neither endpoint is public, forward the gRPC service from one terminal:\n\n[code example]\n\nThen log in from another terminal:\n\n[code example]\n\nAfter login, verify the authenticated principal and the selected endpoint:\n\n[code example]\n\nLogin creates or updates the `production` profile and makes it active for subsequent commands. `ff ping` returns a failure status when the selected endpoint is unavailable."},{"id":"configure-production-state","title":"Configure production state","role":"configuration","text":"Use external PostgreSQL so Feature Form state survives restarts and remains consistent across server replicas. Protect the database with your normal TLS, availability, monitoring, backup, and restore controls. Back up the database before upgrading Feature Form.\n\nThis database stores durable Feature Form application state. Register production data providers separately after deploying Feature Form.\n\nBefore starting the server, the chart runs an init container that applies all pending schema migrations. The database role must be able to create and alter the Feature Form schema. A migration failure keeps the server pod from becoming ready."},{"id":"configure-external-access","title":"Configure external access","role":"configuration","text":"All services default to `ClusterIP`. Choose one exposure model:\n\n- Use `rest.ingress.*`, `grpc.ingress.*`, and `dashboard.ingress.*` for separate hosts.\n- Use `ingress.*` for one host with chart-managed paths for the API and dashboard.\n- Use the service `type=LoadBalancer` values when your platform terminates TLS at a load balancer.\n\nDon't combine `ingress.*` with service-specific ingress settings. The chart rejects that configuration.\n\nTerminate TLS at the ingress controller or load balancer. Use `grpc.ingress.*` only with a controller that supports gRPC backends. If ingress isn't suitable, use `grpc.service.type=LoadBalancer` and `rest.service.type=LoadBalancer`."},{"id":"dashboard-requirements","title":"Dashboard requirements","role":"content","text":"Enabling the dashboard requires:\n\n- `dashboard.enabled=true`.\n- `dashboard.publicAPIURL`, a REST ingress host, or unified `ingress.*` configuration.\n- A resolvable dashboard authentication URL.\n- An OIDC client secret and dashboard session secret. Use `dashboard.auth.existingSecret` for production.\n\nThe dashboard Secret keys default to `FEATUREFORM_OIDC_CLIENT_SECRET` and `FEATUREFORM_DASHBOARD_AUTH_SECRET`."},{"id":"configure-availability-and-capacity","title":"Configure availability and capacity","role":"configuration","text":"The chart defaults to one server replica. For multiple replicas, use PostgreSQL state. Configure resource requests before enabling horizontal autoscaling. Each server replica also runs scheduler workers, so adding replicas increases both API and job-processing capacity.\n\nThis example sets resource boundaries, two or more replicas, and a PodDisruptionBudget:\n\n[code example]\n\nHorizontal Pod Autoscaling requires the Kubernetes resource metrics API. Use `server.nodeSelector`, `server.tolerations`, `server.affinity`, or `server.topologySpreadConstraints` when your cluster requires workload placement controls.\n\nBy default, each server process starts two effective scheduler workers. To bound per-replica job concurrency, use `scheduler.workerCount` and `scheduler.workerMaxInflight`. Tune these values with server replica count and downstream provider capacity; increasing API replicas also increases the number of workers that can claim jobs."},{"id":"configure-kubernetes-secret-access","title":"Configure Kubernetes Secret access","role":"configuration","text":"No token is needed to consume Secrets already referenced by pod environment variables or volumes. The chart doesn't mount a Kubernetes service-account token by default.\n\nIf you register a Kubernetes secret provider for server-side secret resolution, enable the token mount and grant the Feature Form service account `get` access to the required Secret objects. For Secrets in the release namespace:\n\n[code example]\n\n> [!NOTE]\n> These `rbac.*` values configure Kubernetes permissions. Feature Form application roles are configured separately in [Configure authentication and role-based access control](https://redis.io/docs/latest/operate/featureform/configure-auth)."},{"id":"configure-observability","title":"Configure observability","role":"configuration","text":"The server exports metrics and traces through OpenTelemetry Protocol (OTLP). Because the Helm chart disables both signals by default, set the endpoint and enable the signals you want. Configure an external collector using an authority without a URL scheme:\n\n[code example]\n\nFeature Form writes server logs to standard output and standard error. Collect them with your Kubernetes logging system."},{"id":"upgrade-and-roll-back","title":"Upgrade and roll back","role":"content","text":"Before upgrading:\n\n1. Back up the Feature Form PostgreSQL database.\n2. Confirm the license key is valid for the target release.\n3. Render the target chart version with the current values file.\n4. Upgrade with an explicit `--version`, `--wait`, and `--timeout`.\n5. Verify the migration init container, server rollout, license status, REST readiness, and `ff ping`.\n\nKeep the server, dashboard, chart, and `ff` versions aligned. Replacing an external license Secret doesn't restart the server; roll out the server deployment after updating the key.\n\nHelm rollback restores chart-managed resources, but it doesn't reverse PostgreSQL migrations or restore externally managed Secrets. Preserve the database backup, previous license key, image version, and Helm revision until the rollback window closes."},{"id":"troubleshoot-installation","title":"Troubleshoot installation","role":"errors","text":"- **Helm reports missing authentication values.** Set `auth.enabled=true`, `auth.oidcIssuerURL`, `auth.oidcClientID`, and a stable `auth.deploymentID`.\n- **A pod shows `ImagePullBackOff`.** Confirm registry network access and configure `imagePullSecrets` when credentials are required.\n- **The migration init container fails.** Check PostgreSQL reachability, TLS settings, credentials, and the database role's schema permissions.\n- **The server reports a missing or invalid license.** Check the Secret name and `license.key` entry, then inspect `featureformctl license status` without printing the key.\n- **OIDC discovery or login fails.** Verify the internal and public issuer URLs, client IDs, redirect URIs, and endpoint reachability.\n- **The dashboard fails chart validation.** Supply its public API URL, authentication URL, OIDC client secret, and session secret.\n- **Ingress values conflict.** Configure either unified `ingress.*` or service-specific ingress settings, not both."},{"id":"next-steps","title":"Next steps","role":"content","text":"- [Configure authentication and role-based access control](https://redis.io/docs/latest/operate/featureform/configure-auth) — set up OIDC and grant roles.\n- [Manage workspaces](https://redis.io/docs/latest/develop/ai/featureform/manage-workspace) — create your first workspace.\n- [Register providers](https://redis.io/docs/latest/develop/ai/featureform/register-providers) — connect production data infrastructure.\n- [Quickstart](https://redis.io/docs/latest/develop/ai/featureform/quickstart) — verify the install end to end."}],"examples":[{"id":"1-get-the-chart-ex0","language":"text","code":"oci://registry-1.docker.io/redisfeatureform/featureform","section_id":"1-get-the-chart"},{"id":"2-create-the-namespace-and-secrets-ex0","language":"bash","code":"kubectl create namespace <namespace>","section_id":"2-create-the-namespace-and-secrets"},{"id":"2-create-the-namespace-and-secrets-ex1","language":"bash","code":"kubectl --namespace <namespace> create secret generic featureform-postgres \\\n  --from-env-file=<path-to-postgres-secret-env-file>","section_id":"2-create-the-namespace-and-secrets"},{"id":"2-create-the-namespace-and-secrets-ex2","language":"bash","code":"kubectl --namespace <namespace> create secret generic featureform-license \\\n  --from-file=license.key=<path-to-license-key-file>","section_id":"2-create-the-namespace-and-secrets"},{"id":"2-create-the-namespace-and-secrets-ex3","language":"yaml","code":"imagePullSecrets:\n  - name: <registry-secret-name>","section_id":"2-create-the-namespace-and-secrets"},{"id":"3-create-a-values-file-ex0","language":"yaml","code":"stateBackend: postgres\n\nauth:\n  enabled: true\n  oidcIssuerURL: \"https://idp.example.com/realms/featureform\"\n  oidcClientID: \"featureform-api\"\n  oidcCLIClientID: \"featureform-cli\"\n  oidcCLIScopes: \"openid profile offline_access\"\n  oidcCLILoginMethods: \"device_code\"\n  deploymentID: \"<stable-deployment-id>\"\n  publicRestEndpoint: \"https://api.example.com\"\n  publicGrpcEndpoint: \"grpc.example.com:443\"\n\npostgres:\n  url: \"\"\n  secretName: featureform-postgres\n  secretKey: POSTGRES_URL\n\nlicense:\n  existingSecret: featureform-license\n  secretKey: license.key\n\nrest:\n  ingress:\n    enabled: true\n    className: \"<ingress-class-name>\"\n    hosts:\n      - host: api.example.com\n        paths:\n          - path: /\n            pathType: Prefix\n    tls:\n      - secretName: featureform-api-tls\n        hosts:\n          - api.example.com\n\ngrpc:\n  ingress:\n    enabled: true\n    className: \"<ingress-class-name>\"\n    annotations:\n      \"<grpc-backend-annotation>\": \"<grpc-backend-value>\"\n    hosts:\n      - host: grpc.example.com\n        paths:\n          - path: /\n            pathType: ImplementationSpecific\n    tls:\n      - secretName: featureform-grpc-tls\n        hosts:\n          - grpc.example.com","section_id":"3-create-a-values-file"},{"id":"4-render-and-install-the-release-ex0","language":"bash","code":"helm template featureform \\\n  oci://registry-1.docker.io/redisfeatureform/featureform \\\n  --version <featureform-version> \\\n  --namespace <namespace> \\\n  --values values-production.yaml \\\n  > /tmp/featureform-rendered.yaml","section_id":"4-render-and-install-the-release"},{"id":"4-render-and-install-the-release-ex1","language":"bash","code":"helm upgrade --install featureform \\\n  oci://registry-1.docker.io/redisfeatureform/featureform \\\n  --version <featureform-version> \\\n  --namespace <namespace> \\\n  --values values-production.yaml \\\n  --wait \\\n  --timeout 10m","section_id":"4-render-and-install-the-release"},{"id":"5-verify-the-deployment-ex0","language":"bash","code":"kubectl --namespace <namespace> rollout status \\\n  deployment/featureform-featureform-server\nkubectl --namespace <namespace> get pods\nkubectl --namespace <namespace> get services","section_id":"5-verify-the-deployment"},{"id":"5-verify-the-deployment-ex1","language":"bash","code":"kubectl --namespace <namespace> get pods \\\n  --selector app.kubernetes.io/instance=featureform,app.kubernetes.io/component=server \\\n  --output jsonpath='{range .items[*]}{.metadata.name}{\"\\t\"}{range .status.initContainerStatuses[?(@.name==\"migrate\")]}{.state.terminated.reason}{\"\\t\"}{.state.terminated.exitCode}{\"\\n\"}{end}{end}'","section_id":"5-verify-the-deployment"},{"id":"5-verify-the-deployment-ex2","language":"bash","code":"kubectl --namespace <namespace> logs <server-pod> --container migrate","section_id":"5-verify-the-deployment"},{"id":"5-verify-the-deployment-ex3","language":"bash","code":"kubectl --namespace <namespace> exec \\\n  deployment/featureform-featureform-server \\\n  -- featureformctl license status","section_id":"5-verify-the-deployment"},{"id":"5-verify-the-deployment-ex4","language":"bash","code":"curl --fail --silent --show-error https://api.example.com/health/ready","section_id":"5-verify-the-deployment"},{"id":"6-install-and-connect-the-ff-cli-ex0","language":"bash","code":"python3 -m venv .venv\nsource .venv/bin/activate\npip install redis-featureform==<featureform-version>\nff version --client-only","section_id":"6-install-and-connect-the-ff-cli"},{"id":"6-install-and-connect-the-ff-cli-ex1","language":"bash","code":"ff --server grpc.example.com:443 --transport grpc \\\n  auth login --profile production","section_id":"6-install-and-connect-the-ff-cli"},{"id":"6-install-and-connect-the-ff-cli-ex2","language":"bash","code":"ff --server https://api.example.com --transport rest \\\n  auth login --profile production","section_id":"6-install-and-connect-the-ff-cli"},{"id":"6-install-and-connect-the-ff-cli-ex3","language":"bash","code":"kubectl --namespace <namespace> port-forward \\\n  service/featureform-featureform-grpc 9090:9090","section_id":"6-install-and-connect-the-ff-cli"},{"id":"6-install-and-connect-the-ff-cli-ex4","language":"bash","code":"ff --server localhost:9090 --transport grpc \\\n  auth login --profile production","section_id":"6-install-and-connect-the-ff-cli"},{"id":"6-install-and-connect-the-ff-cli-ex5","language":"bash","code":"ff auth status\nff auth whoami\nff ping","section_id":"6-install-and-connect-the-ff-cli"},{"id":"configure-availability-and-capacity-ex0","language":"yaml","code":"server:\n  resources:\n    requests:\n      cpu: <server-cpu-request>\n      memory: <server-memory-request>\n    limits:\n      cpu: <server-cpu-limit>\n      memory: <server-memory-limit>\n  autoscaling:\n    enabled: true\n    minReplicas: 2\n    maxReplicas: <maximum-server-replicas>\n    targetCPUUtilizationPercentage: 80\n\npodDisruptionBudget:\n  enabled: true\n  minAvailable: 1","section_id":"configure-availability-and-capacity"},{"id":"configure-kubernetes-secret-access-ex0","language":"yaml","code":"serviceAccount:\n  automountServiceAccountToken: true\n\nrbac:\n  create: true\n  rules:\n    - apiGroups: [\"\"]\n      resources: [\"secrets\"]\n      resourceNames: [\"<provider-secret-name>\"]\n      verbs: [\"get\"]","section_id":"configure-kubernetes-secret-access"},{"id":"configure-observability-ex0","language":"yaml","code":"observability:\n  otlpEndpoint: \"otel-collector.telemetry.svc.cluster.local:4317\"\n  tracingEnabled: true\n  metricsEnabled: true\n  serviceName: featureform\n  serviceVersion: \"<featureform-version>\"\n  environment: production\n  logLevel: info\n  traceSampleRate: 0.1","section_id":"configure-observability"}]}
{"schema_version":2,"id":"operate/featureform","title":"Redis Feature Form","url":"https://redis.io/docs/latest/operate/featureform/","summary":"Deploy and operate Feature Form on Kubernetes.","tags":["docs","operate","featureform"],"last_updated":"2026-07-23T16:30:51-05:00","children":[{"id":"operate/featureform/deploy","summary":"Deploy and operate Feature Form on Kubernetes.","title":"Deploy","url":"https://redis.io/docs/latest/operate/featureform/deploy/"},{"id":"operate/featureform/configure-auth","summary":"Configure deployment-wide authentication and RBAC for Redis Feature Form.","title":"Configure authentication and RBAC","url":"https://redis.io/docs/latest/operate/featureform/configure-auth/"}],"page_type":"content","content_hash":"973743fe6d9b46decab30815a7589571715c52d401313020b4b5bc9ebe5775f0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Deploy and operate Feature Form on Kubernetes: prepare your environment, install the Helm chart, configure authentication, and verify the platform.\n\nThis section is for platform engineers and admins. Building features in Feature Form rather than deploying it? See the [develop docs](https://redis.io/docs/latest/develop/ai/featureform).\n\nStart with [Deploy](https://redis.io/docs/latest/operate/featureform/deploy) for the install steps, and [Configure authentication and RBAC](https://redis.io/docs/latest/operate/featureform/configure-auth) for OIDC and roles.\n\nThe default install path is cloud-agnostic. For Terraform examples, contact your Redis account team."},{"id":"deployment-model","title":"Deployment model","role":"content","text":"The Helm chart deploys:\n\n- One shared API server deployment that exposes both REST and gRPC.\n- Separate services for REST and gRPC.\n- An optional dashboard.\n- Optional addons: bundled Postgres state, bundled provider infrastructure (Postgres, Redis, MinIO, LocalStack), and observability (Prometheus, Grafana).\n\nAuthentication is required. Standard installs use [OIDC](https://redis.io/docs/latest/operate/featureform/configure-auth)."},{"id":"decisions-youll-make-during-install","title":"Decisions you'll make during install","role":"content","text":"- **State backend.** External PostgreSQL (the durable default), the chart's bundled state Postgres, or memory state (CI and local only — not durable). See [Choose auth and state values](https://redis.io/docs/latest/operate/featureform/deploy#2-choose-auth-and-state-values).\n- **Exposure.** Internal-only services, per-service ingresses, unified ingress, or direct `LoadBalancer` services. See [Configure external access](https://redis.io/docs/latest/operate/featureform/deploy#configure-external-access).\n- **Helm profile.** Base chart, or one of four bundled profiles for memory state, provider stack, observability, or both. See [Pick the base chart or a profile](https://redis.io/docs/latest/operate/featureform/deploy#3-pick-the-base-chart-or-a-profile).\n\nIf workspaces or resources seem to be missing after a restart, memory state is usually the cause — REST and gRPC see different in-memory data."}],"examples":[]}
{"schema_version":2,"id":"operate","title":"Redis products","url":"https://redis.io/docs/latest/operate/","summary":"Products, services, and tools to operate a Redis database.","tags":[],"last_updated":"2026-09-29T14:36:35-07:00","children":[{"id":"operate/rs","summary":"The self-managed, enterprise-grade version of Redis.","title":"Redis Software","url":"https://redis.io/docs/latest/operate/rs/"},{"id":"operate/rc","summary":"The fastest way to set up Redis - a fully managed Redis database on major public cloud services.","title":"Redis Cloud","url":"https://redis.io/docs/latest/operate/rc/"},{"id":"operate/kubernetes","summary":"Deploy and manage Redis Enterprise on Kubernetes with the Redis Enterprise operator.","title":"Redis Enterprise for Kubernetes","url":"https://redis.io/docs/latest/operate/kubernetes/"},{"id":"operate/oss_and_stack","summary":"Operate Redis Open Source. Redis 8 in Redis Open Source replaces Redis Stack.","title":"Redis Open Source","url":"https://redis.io/docs/latest/operate/oss_and_stack/"},{"id":"operate/iris","summary":"Deploy and operate Redis Iris context engine services.","title":"Redis Iris context engine","url":"https://redis.io/docs/latest/operate/iris/"},{"id":"operate/radar","summary":"Monitor the status of every Redis cluster you run from one place.","title":"Redis Radar","url":"https://redis.io/docs/latest/operate/radar/"},{"id":"operate/featureform","summary":"Deploy and operate Feature Form on Kubernetes.","title":"Redis Feature Form","url":"https://redis.io/docs/latest/operate/featureform/"},{"id":"operate/redisinsight","summary":"Install and manage Redis Insight","title":"Redis Insight","url":"https://redis.io/docs/latest/operate/redisinsight/"},{"id":"operate/rdi","summary":"Keep Redis in sync with a primary database in near real time.","title":"Redis Data Integration (RDI)","url":"https://redis.io/docs/latest/operate/rdi/"}],"page_type":"content","content_hash":"794d7739118853931e71ce41b6fd9e0d9f553c2cdaa9760030266e0f3270cf2a","sections":[{"id":"core-redis-deployment-options","title":"Core Redis deployment options","role":"overview","text":"Choose one based on where you want Redis to run and how much of its deployment and management you want to handle yourself.\n\n<div class=\"flex flex-col gap-4 my-6\">\n  <div class=\"relative transition text-redis-ink-900 hover:text-red-900 focus-within:text-red-900 bg-white hover:bg-red-50/50 focus-within:bg-red-50/50 border border-redis-pen-800 focus-within:ring-red-200 focus-within:ring-[3px] focus-within:outline-none bg-clip-padding rounded-md group\">\n    <a class=\"absolute inset-0 z-0 outline-0\" href=\"https://redis.io/docs/latest/operate/rs/installing-upgrading\"><span class=\"sr-only\">Read more</span></a>\n    <div class=\"relative z-10 pointer-events-none\">\n      <div class=\"flex flex-row items-center gap-2 uppercase font-mono text-xs border-b border-redis-pen-800 px-6 py-2\">\n        <span class=\"h-3 w-3 rounded-full bg-yellow-bubble border border-redis-pen-600 flex-shrink-0\"></span>\n        <span>Redis Software</span>\n      </div>\n      <ol class=\"flex flex-wrap items-center gap-x-2 gap-y-2 list-none m-0 px-6 py-4 text-sm text-redis-pen-600 font-geist\">\n        <li class=\"flex items-center gap-2 my-0 pl-0\"><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/operate/rs/installing-upgrading\">Install</a><span aria-hidden=\"true\">→</span></li>\n        <li class=\"flex items-center gap-2 my-0 pl-0\"><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/operate/rs/clusters/new-cluster-setup\">Set up cluster</a><span aria-hidden=\"true\">→</span></li>\n        <li class=\"flex items-center gap-2 my-0 pl-0\"><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/operate/rs/databases/create\">Create database</a><span aria-hidden=\"true\">→</span></li>\n        <li class=\"flex items-center gap-2 my-0 pl-0\"><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/operate/rs/databases/connect\">Connect</a></li>\n      </ol>\n    </div>\n  </div>\n  <div class=\"relative transition text-redis-ink-900 hover:text-red-900 focus-within:text-red-900 bg-white hover:bg-red-50/50 focus-within:bg-red-50/50 border border-redis-pen-800 focus-within:ring-red-200 focus-within:ring-[3px] focus-within:outline-none bg-clip-padding rounded-md group\">\n    <a class=\"absolute inset-0 z-0 outline-0\" href=\"https://redis.io/docs/latest/operate/rc/rc-quickstart\"><span class=\"sr-only\">Read more</span></a>\n    <div class=\"relative z-10 pointer-events-none\">\n      <div class=\"flex flex-row items-center gap-2 uppercase font-mono text-xs border-b border-redis-pen-800 px-6 py-2\">\n        <span class=\"h-3 w-3 rounded-full bg-blue-bubble border border-redis-pen-600 flex-shrink-0\"></span>\n        <span>Redis Cloud</span>\n      </div>\n      <ol class=\"flex flex-wrap items-center gap-x-2 gap-y-2 list-none m-0 px-6 py-4 text-sm text-redis-pen-600 font-geist\">\n        <li class=\"flex items-center gap-2 my-0 pl-0\"><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/operate/rc/rc-quickstart\">Create account & connect</a><span aria-hidden=\"true\">→</span></li>\n        <li class=\"flex items-center gap-2 my-0 pl-0\"><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/operate/iris\">Enable Iris</a><span aria-hidden=\"true\">→</span></li>\n        <li class=\"flex items-center gap-2 my-0 pl-0\"><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/operate/rc/rdi\">Enable Data Integration</a><span aria-hidden=\"true\">→</span></li>\n        <li class=\"flex items-center gap-2 my-0 pl-0\"><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/operate/rc/radar\">Monitor with Radar</a></li>\n      </ol>\n    </div>\n  </div>\n  <div class=\"relative transition text-redis-ink-900 hover:text-red-900 focus-within:text-red-900 bg-white hover:bg-red-50/50 focus-within:bg-red-50/50 border border-redis-pen-800 focus-within:ring-red-200 focus-within:ring-[3px] focus-within:outline-none bg-clip-padding rounded-md group\">\n    <a class=\"absolute inset-0 z-0 outline-0\" href=\"https://redis.io/docs/latest/operate/kubernetes/architecture\"><span class=\"sr-only\">Read more</span></a>\n    <div class=\"relative z-10 pointer-events-none\">\n      <div class=\"flex flex-row items-center gap-2 uppercase font-mono text-xs border-b border-redis-pen-800 px-6 py-2\">\n        <span class=\"h-3 w-3 rounded-full bg-gray-bubble border border-redis-pen-600 flex-shrink-0\"></span>\n        <span>Redis for Kubernetes</span>\n      </div>\n      <ol class=\"flex flex-wrap items-center gap-x-2 gap-y-2 list-none m-0 px-6 py-4 text-sm text-redis-pen-600 font-geist\">\n        <li class=\"flex items-center gap-2 my-0 pl-0\"><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/operate/kubernetes/architecture\">Architecture</a><span aria-hidden=\"true\">→</span></li>\n        <li class=\"flex items-center gap-2 my-0 pl-0\"><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/operate/kubernetes/deployment\">Deploy Redis for Kubernetes</a><span aria-hidden=\"true\">→</span></li>\n        <li class=\"flex items-center gap-2 my-0 pl-0\"><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/operate/kubernetes/reference\">API Reference</a></li>\n      </ol>\n    </div>\n  </div>\n  <div class=\"relative transition text-redis-ink-900 hover:text-red-900 focus-within:text-red-900 bg-white hover:bg-red-50/50 focus-within:bg-red-50/50 border border-redis-pen-800 focus-within:ring-red-200 focus-within:ring-[3px] focus-within:outline-none bg-clip-padding rounded-md group\">\n    <a class=\"absolute inset-0 z-0 outline-0\" href=\"https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack\"><span class=\"sr-only\">Read more</span></a>\n    <div class=\"relative z-10 pointer-events-none\">\n      <div class=\"flex flex-row items-center gap-2 uppercase font-mono text-xs border-b border-redis-pen-800 px-6 py-2\">\n        <span class=\"h-3 w-3 rounded-full bg-purple-bubble border border-redis-pen-600 flex-shrink-0\"></span>\n        <span>Redis Open Source</span>\n      </div>\n      <ol class=\"flex flex-wrap items-center gap-x-2 gap-y-2 list-none m-0 px-6 py-4 text-sm text-redis-pen-600 font-geist\">\n        <li class=\"flex items-center gap-2 my-0 pl-0\"><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack\">Install Redis 8 in Redis Open Source</a><span aria-hidden=\"true\">→</span></li>\n        <li class=\"flex items-center gap-2 my-0 pl-0\"><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/operate/oss_and_stack/management\">Manage Redis</a></li>\n      </ol>\n    </div>\n  </div>\n</div>"},{"id":"ai-and-data-services","title":"AI and data services","role":"content","text":"Services available on cloud or self-managed infrastructure for AI applications. Redis Iris context engine gives AI agents memory, semantic caching, and governed access to business data. Redis Feature Form defines, manages, and serves machine learning features. Redis Data Integration (RDI) keeps Redis in sync with a primary database in near real time, so your applications work with current data.\n\n<div class=\"flex flex-col gap-4 my-6\">\n  <div class=\"relative transition text-redis-ink-900 hover:text-red-900 focus-within:text-red-900 bg-white hover:bg-red-50/50 focus-within:bg-red-50/50 border border-redis-pen-800 focus-within:ring-red-200 focus-within:ring-[3px] focus-within:outline-none bg-clip-padding rounded-md group\">\n    <a class=\"absolute inset-0 z-0 outline-0\" href=\"https://redis.io/docs/latest/operate/iris\"><span class=\"sr-only\">Read more</span></a>\n    <div class=\"relative z-10 pointer-events-none\">\n      <div class=\"flex flex-row items-center gap-2 uppercase font-mono text-xs border-b border-redis-pen-800 px-6 py-2\">\n        <span class=\"h-3 w-3 rounded-full bg-indigo-bubble border border-redis-pen-600 flex-shrink-0\"></span>\n        <span>Redis Iris context engine</span>\n      </div>\n      <ol class=\"flex flex-wrap items-center gap-x-2 gap-y-2 list-none m-0 px-6 py-4 text-sm text-redis-pen-600 font-geist\">\n        <li class=\"flex items-center gap-2 my-0 pl-0\"><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/operate/iris\">Overview</a><span aria-hidden=\"true\">→</span></li>\n        <li class=\"flex items-center gap-2 my-0 pl-0\"><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/operate/iris/agent-memory\">Enable Agent Memory</a><span aria-hidden=\"true\">→</span></li>\n        <li class=\"flex items-center gap-2 my-0 pl-0\"><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/operate/iris/context-retriever\">Enable Context Retriever</a><span aria-hidden=\"true\">→</span></li>\n        <li class=\"flex items-center gap-2 my-0 pl-0\"><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/operate/iris/langcache\">Enable LangCache</a></li>\n      </ol>\n    </div>\n  </div>\n  <div class=\"relative transition text-redis-ink-900 hover:text-red-900 focus-within:text-red-900 bg-white hover:bg-red-50/50 focus-within:bg-red-50/50 border border-redis-pen-800 focus-within:ring-red-200 focus-within:ring-[3px] focus-within:outline-none bg-clip-padding rounded-md group\">\n    <a class=\"absolute inset-0 z-0 outline-0\" href=\"https://redis.io/docs/latest/operate/featureform\"><span class=\"sr-only\">Read more</span></a>\n    <div class=\"relative z-10 pointer-events-none\">\n      <div class=\"flex flex-row items-center gap-2 uppercase font-mono text-xs border-b border-redis-pen-800 px-6 py-2\">\n        <span class=\"h-3 w-3 rounded-full bg-blue-gray-bubble border border-redis-pen-600 flex-shrink-0\"></span>\n        <span>Redis Feature Form</span>\n      </div>\n      <ol class=\"flex flex-wrap items-center gap-x-2 gap-y-2 list-none m-0 px-6 py-4 text-sm text-redis-pen-600 font-geist\">\n        <li class=\"flex items-center gap-2 my-0 pl-0\"><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/operate/featureform\">Feature Form overview</a><span aria-hidden=\"true\">→</span></li>\n        <li class=\"flex items-center gap-2 my-0 pl-0\"><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/operate/featureform/deploy\">Deploy Feature Form</a><span aria-hidden=\"true\">→</span></li>\n        <li class=\"flex items-center gap-2 my-0 pl-0\"><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/operate/featureform/configure-auth\">Configure authentication</a></li>\n      </ol>\n    </div>\n  </div>\n  <div class=\"relative transition text-redis-ink-900 hover:text-red-900 focus-within:text-red-900 bg-white hover:bg-red-50/50 focus-within:bg-red-50/50 border border-redis-pen-800 focus-within:ring-red-200 focus-within:ring-[3px] focus-within:outline-none bg-clip-padding rounded-md group\">\n    <a class=\"absolute inset-0 z-0 outline-0\" href=\"https://redis.io/docs/latest/integrate/redis-data-integration/\"><span class=\"sr-only\">Read more</span></a>\n    <div class=\"relative z-10 pointer-events-none\">\n      <div class=\"flex flex-row items-center gap-2 uppercase font-mono text-xs border-b border-redis-pen-800 px-6 py-2\">\n        <span class=\"h-3 w-3 rounded-full bg-white-bubble border border-redis-pen-600 flex-shrink-0\"></span>\n        <span>Redis Data Integration</span>\n      </div>\n      <ol class=\"flex flex-wrap items-center gap-x-2 gap-y-2 list-none m-0 px-6 py-4 text-sm text-redis-pen-600 font-geist\">\n        <li class=\"flex items-center gap-2 my-0 pl-0\"><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/integrate/redis-data-integration/\">RDI overview</a><span aria-hidden=\"true\">→</span></li>\n        <li class=\"flex items-center gap-2 my-0 pl-0\"><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/integrate/redis-data-integration/installation\">Install RDI</a><span aria-hidden=\"true\">→</span></li>\n        <li class=\"flex items-center gap-2 my-0 pl-0\"><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines\">RDI pipelines</a></li>\n      </ol>\n    </div>\n  </div>\n</div>"},{"id":"fleet-management-and-developer-tools","title":"Fleet management and developer tools","role":"content","text":"Use Redis Radar to monitor the status of every Redis cluster you run from one place. Use Redis Insight as a developer workbench to browse, query, and analyze your Redis data.\n\n<div class=\"flex flex-col gap-4 my-6\">\n  <div class=\"relative transition text-redis-ink-900 hover:text-red-900 focus-within:text-red-900 bg-white hover:bg-red-50/50 focus-within:bg-red-50/50 border border-redis-pen-800 focus-within:ring-red-200 focus-within:ring-[3px] focus-within:outline-none bg-clip-padding rounded-md group\">\n    <a class=\"absolute inset-0 z-0 outline-0\" href=\"https://redis.io/docs/latest/operate/radar\"><span class=\"sr-only\">Read more</span></a>\n    <div class=\"relative z-10 pointer-events-none\">\n      <div class=\"flex flex-row items-center gap-2 uppercase font-mono text-xs border-b border-redis-pen-800 px-6 py-2\">\n        <span class=\"h-3 w-3 rounded-full bg-green-bubble border border-redis-pen-600 flex-shrink-0\"></span>\n        <span>Redis Radar</span>\n      </div>\n      <ol class=\"flex flex-wrap items-center gap-x-2 gap-y-2 list-none m-0 px-6 py-4 text-sm text-redis-pen-600 font-geist\">\n        <li class=\"flex items-center gap-2 my-0 pl-0\"><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/operate/radar\">Radar overview</a><span aria-hidden=\"true\">→</span></li>\n        <li class=\"flex items-center gap-2 my-0 pl-0\"><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/operate/radar/install\">Install Radar</a><span aria-hidden=\"true\">→</span></li>\n        <li class=\"flex items-center gap-2 my-0 pl-0\"><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/operate/radar/connect\">Connect clusters</a><span aria-hidden=\"true\">→</span></li>\n        <li class=\"flex items-center gap-2 my-0 pl-0\"><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/operate/radar/monitor\">Monitor</a></li>\n      </ol>\n    </div>\n  </div>\n  <div class=\"relative transition text-redis-ink-900 hover:text-red-900 focus-within:text-red-900 bg-white hover:bg-red-50/50 focus-within:bg-red-50/50 border border-redis-pen-800 focus-within:ring-red-200 focus-within:ring-[3px] focus-within:outline-none bg-clip-padding rounded-md group\">\n    <a class=\"absolute inset-0 z-0 outline-0\" href=\"https://redis.io/docs/latest/operate/redisinsight/install\"><span class=\"sr-only\">Read more</span></a>\n    <div class=\"relative z-10 pointer-events-none\">\n      <div class=\"flex flex-row items-center gap-2 uppercase font-mono text-xs border-b border-redis-pen-800 px-6 py-2\">\n        <span class=\"h-3 w-3 rounded-full bg-red-bubble border border-redis-pen-600 flex-shrink-0\"></span>\n        <span>Redis Insight</span>\n      </div>\n      <ol class=\"flex flex-wrap items-center gap-x-2 gap-y-2 list-none m-0 px-6 py-4 text-sm text-redis-pen-600 font-geist\">\n        <li class=\"flex items-center gap-2 my-0 pl-0\"><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/downloads/#insight\">Download Redis Insight</a><span aria-hidden=\"true\">→</span></li>\n        <li class=\"flex items-center gap-2 my-0 pl-0\"><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/operate/redisinsight/install\">Install</a><span aria-hidden=\"true\">→</span></li>\n        <li class=\"flex items-center gap-2 my-0 pl-0\"><a class=\"pointer-events-auto no-underline hover:underline\" href=\"https://redis.io/docs/latest/develop/tools/insight\">Use</a></li>\n      </ol>\n    </div>\n  </div>\n</div>"},{"id":"product-features","title":"Product features","role":"content","text":""},{"id":"high-availability-and-durability","title":"High availability and durability","role":"content","text":"<div class=\"overflow-x-auto\">\n\n| | <nobr> Redis</nobr> Cloud | <nobr> Redis</nobr> Software | <nobr> Redis</nobr> Open Source | <nobr> Redis for</nobr> Kubernetes |\n|:-----------|:--------------|:-----------|:--------------|:--------------|\n| Clustering | [Clustering](https://redis.io/docs/latest/operate/rc/databases/configuration/clustering) | [Clustering](https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering) | [Scale with Redis Cluster](https://redis.io/docs/latest/operate/oss_and_stack/management/scaling) | [Redis Enterprise clusters (REC)](https://redis.io/docs/latest/operate/kubernetes/re-clusters) |\n| Replication | [Replication](https://redis.io/docs/latest/operate/rc/databases/configuration/high-availability) | [Replication](https://redis.io/docs/latest/operate/rs/databases/durability-ha/replication) | [Replication](https://redis.io/docs/latest/operate/oss_and_stack/management/replication) | [Create replica databases](https://redis.io/docs/latest/operate/kubernetes/re-databases/replica-redb/)|\n| Active-Active geo-distribution | [Active-Active Redis](https://redis.io/docs/latest/operate/rc/databases/active-active) | [Active-Active Redis](https://redis.io/docs/latest/operate/rs/databases/active-active) |  | [Active-Active databases](https://redis.io/docs/latest/operate/kubernetes/active-active/) |\n| Rolling upgrades | [Upgrade database version](https://redis.io/docs/latest/operate/rc/databases/version-management/upgrade-version) | [Upgrade Redis Software](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading) |  | [Upgrade Redis for K8s](https://redis.io/docs/latest/operate/kubernetes/upgrade/) |\n| Redis Flex/Auto tiering | [Create a Redis Flex database](https://redis.io/docs/latest/operate/rc/databases/create-database/create-flex-database) | [Redis Flex](https://redis.io/docs/latest/operate/rs/databases/flash) |  | [Redis Flex](https://redis.io/docs/latest/operate/kubernetes/flex/) |\n| Persistence | [Data persistence](https://redis.io/docs/latest/operate/rc/databases/configuration/data-persistence) | [Persistence](https://redis.io/docs/latest/operate/rs/databases/configure/database-persistence) | [Persistence](https://redis.io/docs/latest/operate/oss_and_stack/management/replication) | [Persistence volumes](https://redis.io/docs/latest/operate/kubernetes/recommendations/persistent-volumes/)|\n| Recovery | Automatic | [Recover cluster](https://redis.io/docs/latest/operate/rs/clusters/cluster-recovery) | [Manual failover](https://redis.io/docs/latest/operate/oss_and_stack/management/scaling#manual-failover) | [Cluster recovery](https://redis.io/docs/latest/operate/kubernetes/re-clusters/cluster-recovery/) |\n| Backups | [Back up a database](https://redis.io/docs/latest/operate/rc/databases/back-up-data) | [Schedule backups](https://redis.io/docs/latest/operate/rs/databases/import-export/schedule-backups) | [Persistence](https://redis.io/docs/latest/operate/oss_and_stack/management/replication) | [REDB spec.backup](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api/#specbackup) |\n\n</div>"},{"id":"logging-and-monitoring","title":"Logging and monitoring","role":"content","text":"<div class=\"overflow-x-auto\">\n\n| | <nobr> Redis</nobr> Cloud | <nobr> Redis</nobr> Software | <nobr> Redis</nobr> Open Source | <nobr> Redis for</nobr> Kubernetes |\n|:-----------|:--------------|:-----------|:--------------|:--------------|\n| Monitoring | [Monitor performance](https://redis.io/docs/latest/operate/rc/databases/monitor-performance) | [Monitoring](https://redis.io/docs/latest/operate/rs/monitoring) | [INFO](https://redis.io/docs/latest/commands/info), [MONITOR](https://redis.io/docs/latest/commands/monitor), and [LATENCY DOCTOR](https://redis.io/docs/latest/commands/latency-doctor)<br/>[Analysis with Redis Insight](https://redis.io/docs/latest/develop/tools/insight#database-analysis) | [Export metrics to Prometheus](https://redis.io/docs/latest/operate/kubernetes/re-clusters/connect-prometheus-operator/) |\n| Logging | [System logs](https://redis.io/docs/latest/operate/rc/logs-reports/system-logs) | [Logging](https://redis.io/docs/latest/operate/rs/clusters/logging) | `/var/log/redis/redis.log`<br/>[SLOWLOG](https://redis.io/docs/latest/commands/slowlog)<br/>[Keyspace notifications](https://redis.io/docs/latest/develop/pubsub/keyspace-notifications) | [Logs](https://redis.io/docs/latest/operate/kubernetes/logs/) |\n| Alerts | [Alerts](https://redis.io/docs/latest/operate/rc/databases/monitor-performance#configure-metric-alerts) | [Alerts and events](https://redis.io/docs/latest/operate/rs/clusters/logging/alerts-events) | [Pub/sub with Redis Sentinel](https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel#pubsub-messages) | [REDB alertSettings](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api/#specalertsettings) |\n| Support | [Contact support](https://redis.io/support/) | [Create support package](https://redis.io/docs/latest/operate/rs/installing-upgrading/creating-support-package) |  | [Contact support](https://redis.io/support/) |\n\n</div>"},{"id":"security","title":"Security","role":"security","text":"<div class=\"overflow-x-auto\">\n\n| | <nobr> Redis</nobr> Cloud | <nobr> Redis</nobr> Software | <nobr> Redis</nobr> Open Source | <nobr><div class=\"h-3 w-3 rounded-md border border-redis-pen-600 inline-block mr-1\" style=\"background-color: #8A99A0\"></div> Redis for</nobr> Kubernetes |\n|:-----------|:--------------|:-----------|:--------------|:--------------|\n| Transport Layer Security (TLS) | [TLS](https://redis.io/docs/latest/operate/rc/security/database-security/tls-ssl) | [TLS](https://redis.io/docs/latest/operate/rs/security/encryption/tls) | [TLS](https://redis.io/docs/latest/operate/oss_and_stack/management/security/encryption) | [REDB tlsMode](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api/#spec) |\n| Role-based access control (RBAC) | [Role-based access control](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/role-based-access-control) | [Access control](https://redis.io/docs/latest/operate/rs/security/access-control) | [Access control list](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl) | [REC credentials](https://redis.io/docs/latest/operate/kubernetes/security/authentication/manage-rec-credentials/) |\n| Lightweight Directory Access Protocol (LDAP) |  | [LDAP authentication](https://redis.io/docs/latest/operate/rs/security/access-control/ldap) |  | [Enable LDAP](https://redis.io/docs/latest/operate/kubernetes/security/authentication/ldap/) |\n| Single sign-on (SSO) | [SAML SSO](https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso) |  |  |  |\n| Self-signed certificates |  | [Certificates](https://redis.io/docs/latest/operate/rs/security/certificates) | [Certificate configuration](https://redis.io/docs/latest/operate/oss_and_stack/management/security/encryption#certificate-configuration) | [REC certificates](https://redis.io/docs/latest/operate/kubernetes/security/certificates/manage-rec-certificates/) |\n| Internode encryption | [Encryption at rest](https://redis.io/docs/latest/operate/rc/security/encryption-at-rest) | [Internode encryption](https://redis.io/docs/latest/operate/rs/security/encryption/internode-encryption) |  | [Enable internode encryption](https://redis.io/docs/latest/operate/kubernetes/security/certificates/internode-encryption/) |\n| Auditing |  | [Audit events](https://redis.io/docs/latest/operate/rs/security/audit-events) | [Keyspace notifications](https://redis.io/docs/latest/develop/pubsub/keyspace-notifications) |  |\n\n</div>"}],"examples":[]}
{"schema_version":2,"id":"operate/iris/agent-memory/create-service","title":"Create an Agent Memory service","url":"https://redis.io/docs/latest/operate/iris/agent-memory/create-service/","summary":"Create an Agent Memory service in Redis Cloud to store working and long-term memory for AI agents.","aliases":["/operate/rc/context-engine/agent-memory/create-service/","/operate/rc/agent-memory/create-service/"],"tags":["docs","operate","iris"],"last_updated":"2026-10-05T16:11:44+01:00","page_type":"content","content_hash":"20813bc2538b5fec537469d80654e336e573e6bb4f7667c9a6b7aaba8a992f90","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Agent Memory provides a persistent, structured memory layer that AI agents can use to store, retrieve, and manage contextual data across interactions. This guide walks you through creating and configuring an Agent Memory service in Redis Cloud."},{"id":"prerequisites-and-limitations","title":"Prerequisites and limitations","role":"content","text":"To create a Redis Agent Memory service, you will need a Redis Cloud database. If you don't have one, see [Create a database](https://redis.io/docs/latest/operate/rc/databases/create-database).\n\n\nDuring public preview, Redis Agent Memory doesn't support [Redis Flex](https://redis.io/docs/latest/operate/rc/databases/create-database/create-flex-database) databases, databases using [AWS PrivateLink](https://redis.io/docs/latest/operate/rc/security/aws-privatelink) connectivity, [Active-Active](https://redis.io/docs/latest/operate/rc/databases/active-active) databases, or databases with the [default user](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/default-user) turned off."},{"id":"create-an-agent-memory-service","title":"Create an Agent Memory service","role":"content","text":"From the [Redis Cloud console](https://cloud.redis.io/), select **Agent Memory** from the left-hand menu. \n\nIf you have not already created an Agent Memory service, you'll see a page with an introduction to Agent Memory. Otherwise, select **New service** to go to the Agent Memory introduction page.\n\n![The New service button.](https://redis.io/docs/latest/images/rc/langcache-new-service.png)\n\nFrom here: \n\n- Select **Quick create** to create a Redis Agent Memory service with default settings using your Free 30MB database. If you haven't created a Free database yet, Redis Cloud will create one and set up the Redis Agent Memory service for you.\n\n    ![The Quick create button.](https://redis.io/docs/latest/images/rc/langcache-quick-create.png)\n\n    After Redis Cloud creates your Agent Memory service, a window containing your Agent Memory service key will appear. Select **Copy** to copy the key to your clipboard.\n\n    ![The Agent Memory service key window. Use the Copy button to save the service key to the clipboard.](https://redis.io/docs/latest/images/rc/agent-memory-service-key.png)\n\n    > [!WARNING]\n    > This is the only time the value of the user key is available. Save it to a secure location before closing the dialog box.<br/><br/>\n    >\n    > If you lose the service key value, you will need to [generate a new service key](https://redis.io/docs/latest/operate/iris/agent-memory/view-service#replace-service-api-key) to be able to use the Agent Memory API.\n\n    After Redis Cloud creates your service, [continue with the REST API quickstart](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/rest-api-quickstart).\n\n- If you want to customize your Redis Agent Memory service, select **Create custom**.\n\n    ![The Create custom button to create an Agent Memory service.](https://redis.io/docs/latest/images/rc/agent-memory-custom-service.png)\n\n    This takes you to the **Create Agent Memory Service** page. This page is divided into the following sections:\n\n    1. The [General settings](#general-settings) section defines basic properties of your service.\n    1. The [Memory configuration](#memory-configuration) section allows you to define the time-to-live (TTL) of your agent's memories, set how often memories are extracted, and control automatic summarization of session memory.\n    1. The [AI model and credentials](#ai-model-and-credentials) section allows you to use your own model provider key and select the models used by the service.\n    1. The [Memory types & extraction](#memory-types-and-extraction) section allows you to define custom memory types with their own extraction strategies.\n    1. The [Sensitive-data exclusions](#sensitive-data-exclusions) section allows you to keep sensitive content out of long-term memory."},{"id":"general-settings","title":"General settings","role":"content","text":"The **General settings** section defines basic properties of your service.\n\n![The General settings section.](https://redis.io/docs/latest/images/rc/agent-memory-general-settings.png)\n\n| Setting name          |Description|\n|:----------------------|:----------|\n| **Service name** | Enter a name for your Agent Memory service. We recommend you use a name that describes your service's purpose. |\n| **Select database** | Select the Redis Cloud database to use for this service from the list. |\n| **User for this service** | The [database access user](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/role-based-access-control) to use for this service. Agent Memory only supports the [`default` user](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/default-user) during public preview. |"},{"id":"memory-configuration","title":"Memory configuration","role":"content","text":"The **Memory configuration** section allows you to define the time-to-live (TTL) of your agent's memories, set how often memories are extracted, and control automatic summarization of session memory.\n\n![The Memory configuration section.](https://redis.io/docs/latest/images/rc/agent-memory-memory-configuration.png)\n\n| Setting name          |Description|\n|:----------------------|:----------|\n| **Short-term TTL** | Defines the time-to-live (TTL) of your agent's **short-term memory** (also known as **session memory**). You can define this TTL in seconds, minutes, hours, or days. Default: 1 hour |\n| **Long-term TTL** | Defines the time-to-live (TTL) of your agent's **long-term memory**. You can define this TTL in seconds, minutes, hours, or days. Default: 365 days |\n| **Extraction cadence** | How often the extraction pipeline runs while a session is active. Leave this blank to use the default of 5 minutes, or set a value between 60 and 600 seconds to override it. |\n\n#### Automatic summarization\n\nAutomatic summarization keeps your agent sharp during long conversations by compressing older messages in a session into a summary, while keeping the most recent messages in full. This helps control the size of session memory without losing important context.\n\nUse the **Automatic summarization** toggle to enable or disable this behavior. When it is enabled, you can configure the following settings:\n\n| Setting name          |Description|\n|:----------------------|:----------|\n| **Summarize after (messages)** | The number of messages a session can hold before older messages are summarized. When a session exceeds this threshold, the oldest messages beyond the **Keep most recent** count are compressed into a summary. |\n| **Keep most recent (messages)** | The number of most recent messages that are always kept in full and never summarized. |\n\nFor example, with **Summarize after** set to 20 and **Keep most recent** set to 10, once a session reaches 20 messages, the oldest 10 messages are summarized automatically and the 10 most recent are kept in full."},{"id":"ai-model-and-credentials","title":"AI model and credentials","role":"content","text":"The **AI model and credentials (optional)** section controls the credentials and models that Agent Memory uses for memory extraction, session summarization, and long-term-memory search.\n\nSelect **Use Redis in-built keys** to use Redis-managed model credentials. This is the default and requires no additional configuration. To use your provider account, select **Bring my own key**, then select the provider, LLM model, embedding model, and add your API key.\n\nLLM model options can change. Use the console to see the current choices; values shown in the screenshot are examples.\n\n![The AI model and credentials section with Bring my own key selected. The section contains the provider, API key, LLM model, and embedding model settings.](https://redis.io/docs/latest/images/rc/agent-memory-ai-model-create.png)\n\nThe same provider key is used for both model types. The credential source, provider, and embedding model are fixed after you create the service. For configuration, key rotation, security, and troubleshooting guidance, see [Configure AI models for Agent Memory](https://redis.io/docs/latest/operate/iris/agent-memory/model-configuration)."},{"id":"memory-types-and-extraction","title":"Memory types & extraction","role":"content","text":"The **Memory types & extraction** section allows you to define custom long-term memory types with structured fields and an optional extraction strategy. Each enabled type runs independently.\n\n![The Memory types & extraction section.](https://redis.io/docs/latest/images/rc/agent-memory-memory-types.png)\n\n#### Custom memory types\n\nIn addition to the built-in memory types, you can define **custom memory types** to capture structured, domain-specific information from your agent's conversations. Each custom type describes a category of information you want to extract, the fields that make up that information, and an optional extraction strategy that tells the extraction pipeline how to populate it.\n\nYou can define up to **3 custom memory types**. Once you reach this limit, the **Add type** button is disabled.\n\nTo add a custom memory type, select **Add type** and configure the following settings:\n\n| Setting name          |Description|\n|:----------------------|:----------|\n| **Name** | A unique name for the custom memory type. Must start with a letter and contain only letters, numbers, hyphens, or underscores (1–64 characters). The name must be unique within the service and cannot match a built-in memory type (`semantic`, `episodic`, `message`, or `session_summary_view`). |\n| **Description** | A short description of what the memory type represents (1–200 characters). |\n| **Fields** | The structured fields that make up the memory type. See [Fields](#fields) below. |\n\n##### Fields\n\nEach custom memory type can have one or more fields that define its structured attributes. For each field, configure the following:\n\n| Setting name          |Description|\n|:----------------------|:----------|\n| **Name** | The field name. Follows the same rules as the memory type name: must start with a letter and contain only letters, numbers, hyphens, or underscores. |\n| **Type** | The field's data type. Choose from `str`, `int`, `float`, `bool`, `list[str]`, `list[float]`, or `object`. |\n| **Description** | A description of the field (1–200 characters). This description is used to guide extraction, so make it clear and specific. |\n\n##### Extraction strategy\n\nEach custom memory type can have an **extraction strategy** that controls how the extraction pipeline populates it from session messages.\n\n| Setting name          |Description|\n|:----------------------|:----------|\n| **Extraction prompt** | A natural-language prompt (up to 10,000 characters) that instructs the extraction pipeline how to identify and extract this memory type from a conversation. |\n| **Enabled** | Whether the extraction strategy is active. Enabled by default. Disable it to keep the type defined without extracting new memories for it. |"},{"id":"sensitive-data-exclusions","title":"Sensitive-data exclusions","role":"content","text":"The **Sensitive-data exclusions** section lets you define information that should not be kept in long-term memory. It offers three mechanisms that you enable independently:\n\n- [Built-in detectors](#built-in-detectors) match common identifiers, such as payment card numbers, using patterns Redis maintains.\n- [Custom detectors](#custom-detectors) match patterns you write yourself, such as internal reference numbers.\n- [Semantic exclusions](#semantic-exclusions) describe what to exclude in plain language, catching concepts that a pattern cannot.\n\nDetector matches are applied deterministically. Semantic exclusions only steer the extraction model, so they are advisory.\n\n> [!NOTE]\n> Sensitive-data exclusions are an early-stage feature, enabled for selected accounts. If you want to try them, contact your Redis representative or [contact sales](https://redis.io/contact/).\n\n![The Sensitive-data exclusions section.](https://redis.io/docs/latest/images/rc/agent-memory-sensitive-data-exclusions.png)\n\nExclusions are applied when long-term memories are created from a session. Session memory is unaffected: events and their summaries are kept as sent. Exclusions are also not applied to long-term memories your application creates directly through the API or an SDK.\n\n#### Built-in detectors\n\nBuilt-in detectors match common identifiers using patterns Redis maintains and validates. They are disabled by default. Use the **Built-in detectors** toggle to enable them, then select the detectors this service should exclude and choose what happens [on match](#on-match).\n\n| Detector | Matches |\n|:---------|:--------|\n| **Credit card number** | Payment card numbers, covering Visa, Mastercard, American Express, Discover, JCB, Diners Club and UnionPay. Maestro is not covered. Digits may be separated by spaces or hyphens. |\n| **Email address** | Email addresses. Addresses containing non-ASCII characters are not matched. |\n| **IP address** | IPv4 and IPv6 addresses. Loopback addresses and CIDR network ranges are not matched, and some abbreviated IPv6 forms are missed. |\n| **Phone number** | Phone numbers for the US, Canada, the UK, Germany, Israel, India, and Brazil. Numbers written with a country code, parentheses, or separators are matched on their own. A plain run of digits is matched only when the surrounding text mentions a phone number. |\n| **US Social Security number** | US Social Security numbers. Written in the usual 123-45-6789 form they are matched on their own. Other layouts are matched only when the surrounding text mentions a Social Security number. |\n\nEach description states what the detector gives up, because selecting one is a choice about false positives: a false match redacts or discards a memory you wanted to keep.\n\n#### Custom detectors\n\nCustom detectors match patterns you write yourself, such as tenant IDs, internal account numbers, or tokens. They are disabled by default. Use the **Custom detectors** toggle to enable them, then select **Add detector** and configure the following settings:\n\n| Setting name          |Description|\n|:----------------------|:----------|\n| **Name** | A name for the detector, unique within the service. Must start with a letter and contain only letters, digits, underscores, or dashes (1-64 characters). A match is reported under this name. |\n| **Pattern** | A regular expression, up to 512 characters. Patterns use [RE2 syntax](https://github.com/google/re2/wiki/Syntax), which does not support backreferences or lookaround. |\n| **On match** | What happens to a memory the pattern matches. See [On match](#on-match). |\n| **Status** | Whether the detector is applied. Turn it off to keep the pattern without applying it. |\n\nYou can define up to 32 custom detectors.\n\n#### On match {#on-match}\n\nEvery detector, built-in or custom, decides what happens to a memory it matches:\n\n| Action | Description |\n|:-------|:------------|\n| **Redact match** | Replaces the matched text with `[REDACTED]` and keeps the rest of the memory. This is the default. |\n| **Drop memory** | Discards the whole memory. |\n\nBuilt-in and custom detectors are evaluated together. When a memory matches several detectors that choose different actions, the memory is dropped.\n\n#### Semantic exclusions\n\nSemantic exclusions let you describe, in plain language, what should not be kept. They catch concepts that a fixed pattern cannot, such as secrets or account recovery codes. They are disabled by default. Use the **Semantic exclusions** toggle to enable them, then describe what to exclude.\n\n| Setting name          |Description|\n|:----------------------|:----------|\n| **Semantic exclusions** | Whether semantic exclusions are applied when memories are extracted. Disabled by default. |\n| **Exclusion prompt** | A plain-language description, up to 2,000 characters, of what should never be kept in long-term memory. Required when semantic exclusions are enabled. |\n\nFor example, a prompt might tell the pipeline never to keep passwords, access tokens, recovery codes, payment card information, or booking confirmation codes, and to keep only a general memory when a conversation includes them.\n\nWhere useful context remains, the extraction model prefers a generalized memory that omits the excluded details rather than dropping the memory entirely. For example, \"The user's card ending 4242 was declined\" can become \"The user had a payment failure\".\n\n> [!WARNING]\n> Semantic exclusions are **advisory**. They steer the extraction model, but they do not guarantee that sensitive content is excluded, and sensitive session content still reaches the extraction model provider. Do not rely on semantic exclusions as your only control for regulated or highly sensitive data."},{"id":"create-service","title":"Create service","role":"content","text":"When you are done setting the details of your Agent Memory service, select **Create** to create it.\n\n![Use the Create button to create an Agent Memory service.](https://redis.io/docs/latest/images/rc/button-access-management-user-key-create.png)\n\nA window containing your Agent Memory service key will appear. Select **Copy** to copy the key to your clipboard.\n\n![The Agent Memory service key window. Use the Copy button to save the service key to the clipboard.](https://redis.io/docs/latest/images/rc/agent-memory-service-key.png)\n\n> [!WARNING]\n> This is the only time the value of the user key is available. Save it to a secure location before closing the dialog box.<br/><br/>\n>\n> If you lose the service key value, you will need to [generate a new service key](https://redis.io/docs/latest/operate/iris/agent-memory/view-service#replace-service-api-key) to be able to use the Redis Agent Memory API.\n\nIf an error occurs, verify that your database is active. For help, [contact support](https://redis.io/support/)."},{"id":"next-steps","title":"Next steps","role":"content","text":"After Redis Cloud creates your service, [continue with the REST API quickstart](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/rest-api-quickstart).\n\nTo generate a conversational agent that connects to your service, open the [AI agent builder](https://redis.io/docs/latest/develop/ai/agent-builder) and select **Redis Iris Conversational Assistant**.\n\nYou can also [view and edit the service](https://redis.io/docs/latest/operate/iris/agent-memory/view-service)."}],"examples":[]}
{"schema_version":2,"id":"operate/iris/agent-memory","title":"Redis Agent Memory","url":"https://redis.io/docs/latest/operate/iris/agent-memory/","summary":"Deploy and operate Redis Agent Memory on Redis Cloud or your own infrastructure.","tags":["docs","operate","iris"],"last_updated":"2026-10-02T17:27:32-07:00","children":[{"id":"operate/iris/agent-memory/create-service","summary":"Create an Agent Memory service in Redis Cloud to store working and long-term memory for AI agents.","title":"Create an Agent Memory service","url":"https://redis.io/docs/latest/operate/iris/agent-memory/create-service/"},{"id":"operate/iris/agent-memory/model-configuration","summary":"Configure the AI models and provider API key used by an Agent Memory service on Redis Cloud.","title":"Configure AI models for Agent Memory","url":"https://redis.io/docs/latest/operate/iris/agent-memory/model-configuration/"},{"id":"operate/iris/agent-memory/view-service","summary":"View and manage your Agent Memory service in Redis Cloud.","title":"View and manage Agent Memory service","url":"https://redis.io/docs/latest/operate/iris/agent-memory/view-service/"},{"id":"operate/iris/agent-memory/self-managed","summary":"Deploy, configure, secure, and operate Redis Agent Memory on a self-managed Kubernetes cluster.","title":"Self-managed Redis Agent Memory","url":"https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/"}],"page_type":"content","content_hash":"e879847c02b7488d39e54efc7f4ed78ad94e69f34e0facff1eb68768598740d8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Agent Memory provides persistent session memory and long-term memory for AI agents and applications.\n\n![The Agent Memory page in the Redis Cloud console, with Quick create and Create custom service options.](https://redis.io/docs/latest/images/rc/agent-memory-get-started.png)"},{"id":"deployment-options","title":"Deployment options","role":"content","text":""},{"id":"redis-cloud","title":"Redis Cloud","role":"content","text":"Use Redis Agent Memory as a managed service on Redis Cloud.\n\n[Create a Redis Agent Memory service](https://redis.io/docs/latest/operate/iris/agent-memory/create-service), [configure its AI models](https://redis.io/docs/latest/operate/iris/agent-memory/model-configuration), or [view and manage an existing service](https://redis.io/docs/latest/operate/iris/agent-memory/view-service)."},{"id":"self-managed","title":"Self-managed","role":"content","text":"Deploy, configure, secure, and operate Redis Agent Memory on your own Kubernetes infrastructure.\n\n[Open the self-managed Redis Agent Memory documentation](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed)"}],"examples":[]}
{"schema_version":2,"id":"operate/iris/agent-memory/model-configuration","title":"Configure AI models for Agent Memory","url":"https://redis.io/docs/latest/operate/iris/agent-memory/model-configuration/","summary":"Configure the AI models and provider API key used by an Agent Memory service on Redis Cloud.","tags":["docs","operate","iris"],"last_updated":"2026-10-05T16:11:44+01:00","page_type":"content","content_hash":"10ddae669558e658e537202ba1344cdeb5dc949e4330ca5687a956c47f0c0238","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can use Redis-managed credentials for the AI models used by an Agent Memory service, or bring  your own model provider API key."},{"id":"choose-who-supplies-the-model-credentials","title":"Choose who supplies the model credentials","role":"content","text":"Agent Memory uses two AI models:\n\n| Model | Purpose |\n| --- | --- |\n| Large language model (LLM) | Extracts long-term memories from sessions and summarizes session messages. |\n| Embedding model | Creates vector embeddings used to find relevant long-term memories. |\n\nWhen you create a service, choose one of these credential sources:\n\n| Credential source | Behavior |\n| --- | --- |\n| **Use Redis in-built keys** | Redis manages the model credentials. You do not select models or provide a model provider API key. |\n| **Bring my own key** | You select the provider and models. Agent Memory uses your provider API key and provider account for both LLM and embedding calls. |\n\nAvailable LLM model choices can change. Use the Redis Cloud console to see the current choices. Provider and model values shown in this guide's screenshots are examples."},{"id":"prepare-your-provider-account","title":"Prepare your provider account","role":"content","text":"Before you use your own key, make sure:\n\n- The provider API key is active.\n- The provider account has enough quota for LLM and embedding calls.\n- The provider account can use the models that you select.\n- You understand the provider's billing, service terms, and data-handling policies. The selected LLM processes session content used for extraction and summarization. The embedding model processes the text needed to create embeddings for long-term-memory storage and search.\n\n> [!NOTE]\n> The model provider API key is different from an Agent Memory service API key. Applications use a service API key to call the Agent Memory API. Agent Memory uses the model provider API key to call the selected AI models."},{"id":"use-your-own-key-when-you-create-a-service","title":"Use your own key when you create a service","role":"content","text":"To configure your own model provider key:\n\n1. Start the [custom service creation flow](https://redis.io/docs/latest/operate/iris/agent-memory/create-service).\n\n1. In **AI model and credentials (optional)**, select **Bring my own key**.\n\n1. Select a **Provider**.\n\n1. Enter the provider **API key**.\n\n1. Select an **LLM model**.\n\n1. Select an **Embedding model**.\n\n1. Complete the other service settings, then select **Create**.\n\n![The AI model and credentials section with Bring my own key selected. The section contains the provider, API key, LLM model, and embedding model settings.](https://redis.io/docs/latest/images/rc/agent-memory-ai-model-create.png)\n\nThe key is used for both the LLM and embedding calls. Redis stores the key securely and does not show it again after you create the service.\n\n> [!WARNING]\n> The credential source, provider, and embedding model are fixed after you create the service. A service created with Redis-managed credentials cannot later use your provider key. A service created with your key cannot switch to Redis-managed credentials."},{"id":"view-the-current-model-configuration","title":"View the current model configuration","role":"content","text":"To view the configuration:\n\n1. In the Redis Cloud console, select **Agent Memory**.\n\n1. Select the service.\n\n1. On the **Configuration** tab, find **AI model and credentials**.\n\n![The AI model and credentials section for an existing service. It shows the provider, embedding model, LLM model, and a User-managed API key status.](https://redis.io/docs/latest/images/rc/agent-memory-ai-model-details.png)\n\nThe **User-managed** status confirms that the service uses your provider key. Redis does not display or return the saved value."},{"id":"change-the-llm-model-or-rotate-the-key","title":"Change the LLM model or rotate the key","role":"content","text":"You can change the LLM model and replace the provider API key. You cannot change the provider or embedding model.\n\nTo update the service:\n\n1. On the service **Configuration** tab, select **Edit**.\n\n1. To change the LLM model, select another **LLM model**.\n\n1. To rotate the provider key, enter a new value in **Replace API key**. Leave this field empty to keep the current key.\n\n1. Select **Save**.\n\n![The AI model and credentials section in edit mode. The provider and embedding model are read-only. The LLM model and Replace API key settings are editable.](https://redis.io/docs/latest/images/rc/agent-memory-ai-model-edit.png)\n\nAfter the update completes, new operations use the replacement key. Operations that were already in progress can continue to use the previous key. The replacement applies to both LLM and embedding calls."},{"id":"redis-protects-the-provider-key","title":"Redis protects the provider key","role":"content","text":"- Redis stores the provider key securely.\n- The key is write-only. Redis does not display or return it after saving.\n- The key does not appear in the service's model configuration.\n- Deleting the Agent Memory service also deletes the stored provider key.\n- A service created with a provider key does not fall back to Redis-managed credentials."},{"id":"resolve-provider-key-and-quota-errors","title":"Resolve provider key and quota errors","role":"content","text":"Model-backed operations can fail when the provider rejects the key or reports that the model quota was exceeded.\n\nFor synchronous API operations, Agent Memory returns these conditions as `424 Failed Dependency` responses:\n\n| Error | Meaning | What to do |\n| --- | --- | --- |\n| **Invalid Model Credentials** | The selected model credentials are missing or invalid. | Confirm that the provider key is active. If necessary, [replace the key](#change-the-llm-model-or-rotate-the-key), then retry the operation. |\n| **Model Quota Exceeded** | The provider reported that the selected model account exceeded its model quota. | Check the provider account's quota and billing status. Resolve any problem, then retry the operation. |\n\nFailures in asynchronous extraction or summarization might not appear in the API call that added the session messages. If expected memories or summaries do not appear:\n\n1. Confirm that the provider key is still active.\n\n1. Confirm that the provider does not report that the model quota was exceeded.\n\n1. If the key is valid and quota is available, [contact support](https://redis.io/support/)."}],"examples":[]}
{"schema_version":2,"id":"operate/iris/agent-memory/self-managed/api-examples","title":"Self-managed API examples","url":"https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/api-examples/","summary":"Use curl examples with the Redis Agent Memory self-managed Control Plane and Data Plane APIs.","aliases":["/develop/ai/context-engine/agent-memory/self-managed/api-examples/"],"tags":["docs","operate","iris"],"last_updated":"2026-10-06T11:56:03+03:00","page_type":"content","content_hash":"5f4b37b9e6152ab90401828d724d27dfaa377b10c3eb0b8443694ad0d95b1804","sections":[{"id":"overview","title":"Overview","role":"overview","text":"These examples show self-managed Control Plane, Identity Service, and Data Plane requests.\n\nThey assume either an auth-disabled private Data Plane or agent-key auth\nconfigured as described in [Authentication and authorization](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/authentication).\n\nFor the complete shared Data Plane schema, see the\n[Redis Agent Memory API](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/api-reference).\nFor the self-managed admin schema, see the\n[Control Plane API reference](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/control-plane-api-reference)."},{"id":"control-plane-api-examples","title":"Control Plane API examples","role":"content","text":"Set variables:\n\n[code example]\n\nList stores:\n\n[code example]\n\nCreate a store:\n\n[code example]\n\nResponse:\n\n[code example]\n\nList the built-in sensitive-data detectors a store may select:\n\n[code example]\n\nResponse:\n\n[code example]\n\nRead detector IDs from this endpoint rather than copying them from documentation. The catalog is compiled into the server, so it is never empty, and `catalogVersion` identifies the generation a result came from.\n\nCreate a store with sensitive-data exclusions:\n\n[code example]\n\nExclusions fields:\n\n| Field | Notes |\n| ----- | ----- |\n| `enabled` | Required. Gates all three mechanisms. Turning it off stops enforcement without discarding what you configured. |\n| `builtInDetectors.enabled` | Required. |\n| `builtInDetectors.detectors[].id` | Required. A detector ID from `/v1/detectors`. |\n| `customDetectors.enabled` | Required. |\n| `customDetectors.detectors[].name` | Required. 1-64 characters, starting with a letter, followed by letters, digits, underscores, or dashes. Unique within the store, and not a built-in detector ID. |\n| `detectors[].enabled` | Required on every built-in and custom detector. |\n| `matcher.kind` | Required. Use `regex`. |\n| `matcher.regex.pattern` | Required when `kind` is `regex`. 1-512 characters, using [RE2 syntax](https://github.com/google/re2/wiki/Syntax), so lookaround is unavailable. Rejected if it does not compile, or if it can match without consuming text. |\n| `action` | Optional on any detector. Use `redact` to replace the matched text or `drop` to discard the memory. Defaults to `redact`. |\n| `semantic.enabled` | Required. |\n| `semantic.prompt` | Required when `semantic.enabled` is `true`. Up to 2,000 characters. |\n\nA store may define at most 32 custom detectors. Update an existing store's policy by sending `longTermMemoryExclusions` on a store update.\n\nFor what each mechanism does, how a match is handled, and which memory paths exclusions apply to, see [exclude sensitive data from automatic extraction](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/developer-guide#exclude-sensitive-data-from-automatic-extraction)."},{"id":"identity-service-api-examples","title":"Identity Service API examples","role":"content","text":"Manage agent keys with the Identity Service, using the Identity Service control token.\n\nSet variables:\n\n[code example]\n\nMint an agent key:\n\n[code example]\n\nResponse:\n\n[code example]\n\nAgent-key fields:\n\n| Field | Notes |\n| ----- | ----- |\n| Endpoint | Use `/v1/api-keys`. |\n| `name` | Required. |\n| `grants` | Required. |\n| `tenant` | Your tenant ID. Use the same tenant on every grant of a key. Any string is accepted. Use `1` if you manage stores with the Control Plane admin token. |\n| `product` | Use `memory`. |\n| `resourceType` | Use `mem-store`. |\n| `resourceId` | Set to the store ID. |\n| `actions` | Use `read`, `write`, or both. |\n| `expiresAt` | Optional. Unix seconds after which the key stops authenticating. `0` or omitted means never. |\n| Token | Returned only when you mint or rotate a key. Store it immediately. |\n\nList agent keys:\n\n[code example]\n\nGet one agent key:\n\n[code example]\n\nUpdate an agent key's grants:\n\n[code example]\n\nRotate an agent key:\n\n[code example]\n\nResponse:\n\n[code example]\n\nRotation returns a new `keyId` with the new token. Use the new `keyId` for later updates,\nrotations, and revocations. The old key stays valid for `graceSeconds`, which defaults to 3600 and\nhas a chart maximum of 604800 (`identityService.apiKeys.maxRotateGraceSeconds`). `oldExpiresAt` is\nwhen the old key stops working. Revoking the old `keyId` ends its grace period early.\n\nRevoke an agent key:\n\n[code example]\n\nA revoked key stops working within up to 5 minutes."},{"id":"data-plane-api-examples","title":"Data Plane API examples","role":"content","text":"Set variables:\n\n[code example]\n\nFor auth-disabled deployments, omit the `Authorization` header and rely on the\ndeployment's hosting controls."},{"id":"write-a-session-event","title":"Write a session event","role":"content","text":"[code example]\n\nRequest body fields:\n\n| Field | Notes |\n| ----- | ----- |\n| `actorId` | Required. |\n| `role` | Required. Use `USER`, `ASSISTANT`, or `SYSTEM`. |\n| `content` | Required. For text, use a content object such as `{\"text\": \"...\"}`. |\n| `createdAt` | Required. Use an RFC 3339 timestamp, for example `2026-06-25T18:00:00Z`. |\n| `sessionId` | Optional. If omitted, Redis Agent Memory generates one. |"},{"id":"read-session-memory","title":"Read session memory","role":"content","text":"[code example]"},{"id":"list-sessions","title":"List sessions","role":"content","text":"List sessions for a specific owner:\n\n[code example]\n\nList all sessions:\n\n[code example]\n\n`filterOwnerId` and `includeAll` are mutually exclusive."},{"id":"create-long-term-memories-directly","title":"Create long-term memories directly","role":"content","text":"[code example]"},{"id":"search-long-term-memory","title":"Search long-term memory","role":"content","text":"[code example]"}],"examples":[{"id":"control-plane-api-examples-ex0","language":"bash","code":"CP_URL=\"http://localhost:9100\"\nRAM_ADMIN_TOKEN=\"<admin-token>\"","section_id":"control-plane-api-examples"},{"id":"control-plane-api-examples-ex1","language":"bash","code":"curl -sS \"$CP_URL/v1/stores\" \\\n  -H \"Authorization: Bearer $RAM_ADMIN_TOKEN\"","section_id":"control-plane-api-examples"},{"id":"control-plane-api-examples-ex2","language":"bash","code":"curl -sS -X POST \"$CP_URL/v1/stores\" \\\n  -H \"Authorization: Bearer $RAM_ADMIN_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"my-store\"\n  }'","section_id":"control-plane-api-examples"},{"id":"control-plane-api-examples-ex3","language":"json","code":"{\n  \"storeId\": \"<store-id>\"\n}","section_id":"control-plane-api-examples"},{"id":"control-plane-api-examples-ex4","language":"bash","code":"curl -sS \"$CP_URL/v1/detectors\" \\\n  -H \"Authorization: Bearer $RAM_ADMIN_TOKEN\"","section_id":"control-plane-api-examples"},{"id":"control-plane-api-examples-ex5","language":"json","code":"{\n  \"catalogVersion\": \"1.0.0\",\n  \"detectors\": [\n    {\n      \"id\": \"credit-card\",\n      \"name\": \"Credit card number\",\n      \"description\": \"Payment card numbers, covering Visa, Mastercard, American Express, Discover, JCB, Diners Club and UnionPay. Maestro is not covered. Digits may be separated by spaces or hyphens.\"\n    }\n  ]\n}","section_id":"control-plane-api-examples"},{"id":"control-plane-api-examples-ex6","language":"bash","code":"curl -sS -X POST \"$CP_URL/v1/stores\" \\\n  -H \"Authorization: Bearer $RAM_ADMIN_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"my-store\",\n    \"longTermMemoryExclusions\": {\n      \"enabled\": true,\n      \"builtInDetectors\": {\n        \"enabled\": true,\n        \"detectors\": [\n          { \"id\": \"credit-card\", \"enabled\": true, \"action\": \"drop\" },\n          { \"id\": \"ip-address\", \"enabled\": true, \"action\": \"redact\" }\n        ]\n      },\n      \"customDetectors\": {\n        \"enabled\": true,\n        \"detectors\": [\n          {\n            \"name\": \"internal-case-reference\",\n            \"enabled\": true,\n            \"action\": \"redact\",\n            \"matcher\": {\n              \"kind\": \"regex\",\n              \"regex\": { \"pattern\": \"CASE-REF-[0-9]{6}\" }\n            }\n          }\n        ]\n      },\n      \"semantic\": {\n        \"enabled\": true,\n        \"prompt\": \"Never keep a customer's payment card number in long-term memory.\"\n      }\n    }\n  }'","section_id":"control-plane-api-examples"},{"id":"identity-service-api-examples-ex0","language":"bash","code":"IDS_URL=\"http://localhost:9200\"\nIDS_CONTROL_TOKEN=\"<identity-service-control-token>\"","section_id":"identity-service-api-examples"},{"id":"identity-service-api-examples-ex1","language":"bash","code":"curl -sS -X POST \"$IDS_URL/v1/api-keys\" \\\n  -H \"Authorization: Bearer $IDS_CONTROL_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"my-agent-key\",\n    \"grants\": [\n      {\n        \"tenant\": \"<your-tenant-id>\",\n        \"product\": \"memory\",\n        \"resourceType\": \"mem-store\",\n        \"resourceId\": \"<store-id>\",\n        \"actions\": [\"read\", \"write\"]\n      }\n    ]\n  }'","section_id":"identity-service-api-examples"},{"id":"identity-service-api-examples-ex2","language":"json","code":"{\n  \"keyId\": \"0123456789abcdef0123456789abcdef\",\n  \"token\": \"<agent-key>\",\n  \"createdAt\": 1780000000\n}","section_id":"identity-service-api-examples"},{"id":"identity-service-api-examples-ex3","language":"bash","code":"curl -sS \"$IDS_URL/v1/api-keys\" \\\n  -H \"Authorization: Bearer $IDS_CONTROL_TOKEN\"","section_id":"identity-service-api-examples"},{"id":"identity-service-api-examples-ex4","language":"bash","code":"curl -sS \"$IDS_URL/v1/api-keys/<key-id>\" \\\n  -H \"Authorization: Bearer $IDS_CONTROL_TOKEN\"","section_id":"identity-service-api-examples"},{"id":"identity-service-api-examples-ex5","language":"bash","code":"curl -sS -X PATCH \"$IDS_URL/v1/api-keys/<key-id>\" \\\n  -H \"Authorization: Bearer $IDS_CONTROL_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"grants\": [\n      {\n        \"tenant\": \"<your-tenant-id>\",\n        \"product\": \"memory\",\n        \"resourceType\": \"mem-store\",\n        \"resourceId\": \"<store-id>\",\n        \"actions\": [\"read\", \"write\"]\n      }\n    ]\n  }'","section_id":"identity-service-api-examples"},{"id":"identity-service-api-examples-ex6","language":"bash","code":"curl -sS -X POST \"$IDS_URL/v1/api-keys/<key-id>/rotate?graceSeconds=3600\" \\\n  -H \"Authorization: Bearer $IDS_CONTROL_TOKEN\"","section_id":"identity-service-api-examples"},{"id":"identity-service-api-examples-ex7","language":"json","code":"{\n  \"createdAt\": 1780000000,\n  \"keyId\": \"fedcba9876543210fedcba9876543210\",\n  \"oldExpiresAt\": 1780003600,\n  \"token\": \"<new-agent-key>\"\n}","section_id":"identity-service-api-examples"},{"id":"identity-service-api-examples-ex8","language":"bash","code":"curl -sS -X DELETE \"$IDS_URL/v1/api-keys/<key-id>\" \\\n  -H \"Authorization: Bearer $IDS_CONTROL_TOKEN\"","section_id":"identity-service-api-examples"},{"id":"data-plane-api-examples-ex0","language":"bash","code":"DP_URL=\"http://localhost:9000\"\nSTORE_ID=\"<store-id>\"\nRAM_AGENT_KEY=\"<agent-key>\"","section_id":"data-plane-api-examples"},{"id":"write-a-session-event-ex0","language":"bash","code":"curl -sS -X POST \"$DP_URL/v1/stores/$STORE_ID/session-memory/events\" \\\n  -H \"Authorization: Bearer $RAM_AGENT_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"sessionId\": \"session-001\",\n    \"actorId\": \"user-001\",\n    \"role\": \"USER\",\n    \"content\": [\n      {\n        \"text\": \"What is the capital of France?\"\n      }\n    ],\n    \"createdAt\": \"2026-06-25T18:00:00Z\"\n  }'","section_id":"write-a-session-event"},{"id":"read-session-memory-ex0","language":"bash","code":"curl -sS \"$DP_URL/v1/stores/$STORE_ID/session-memory/session-001\" \\\n  -H \"Authorization: Bearer $RAM_AGENT_KEY\"","section_id":"read-session-memory"},{"id":"list-sessions-ex0","language":"bash","code":"curl -sS \"$DP_URL/v1/stores/$STORE_ID/session-memory?filterOwnerId=user-001\" \\\n  -H \"Authorization: Bearer $RAM_AGENT_KEY\"","section_id":"list-sessions"},{"id":"list-sessions-ex1","language":"bash","code":"curl -sS \"$DP_URL/v1/stores/$STORE_ID/session-memory?includeAll=true\" \\\n  -H \"Authorization: Bearer $RAM_AGENT_KEY\"","section_id":"list-sessions"},{"id":"create-long-term-memories-directly-ex0","language":"bash","code":"curl -sS -X POST \"$DP_URL/v1/stores/$STORE_ID/long-term-memory\" \\\n  -H \"Authorization: Bearer $RAM_AGENT_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"memories\": [\n      {\n        \"id\": \"pref-email-updates\",\n        \"text\": \"User prefers email updates.\",\n        \"memoryType\": \"semantic\",\n        \"ownerId\": \"user-001\",\n        \"namespace\": \"preferences\",\n        \"topics\": [\"communications\"]\n      }\n    ]\n  }'","section_id":"create-long-term-memories-directly"},{"id":"search-long-term-memory-ex0","language":"bash","code":"curl -sS -X POST \"$DP_URL/v1/stores/$STORE_ID/long-term-memory/search\" \\\n  -H \"Authorization: Bearer $RAM_AGENT_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"text\": \"How should we contact this user?\",\n    \"filter\": {\n      \"ownerId\": {\n        \"eq\": \"user-001\"\n      }\n    },\n    \"limit\": 5\n  }'","section_id":"search-long-term-memory"}]}
{"schema_version":2,"id":"operate/iris/agent-memory/self-managed/authentication","title":"Authentication and authorization","url":"https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/authentication/","summary":"Configure Redis Agent Memory self-managed Control Plane authentication, Data Plane auth modes, worker callbacks, and gateway integration.","aliases":["/develop/ai/context-engine/agent-memory/self-managed/authentication/"],"tags":["docs","operate","iris"],"last_updated":"2026-10-21T16:40:24+03:00","page_type":"content","content_hash":"b326d867d862495bf1bdbcc1955b3ba5e1a05b5ca74cf4393f3cfa31998b1cbe","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Self-managed Redis Agent Memory uses separate authentication models for the Control\nPlane and Data Plane.\n\nThe Control Plane uses an admin token for management endpoints. The Data Plane checks each agent\nkey with the Identity Service, using the Data Plane service credential, and enforces store-level\ngrants."},{"id":"credentials","title":"Credentials","role":"content","text":"| Credential | Who presents it to whom | Secret / key | Auto-generated? | What it unlocks |\n| --- | --- | --- | --- | --- |\n| Control Plane admin token | Operator → Control Plane (`:9100`) | `redis-agent-memory-controlplane-admin-token` / `token` | Yes (`controlplane.adminToken.autoGenerate`) | Store management: `/v1/stores`, `/v1/detectors` |\n| Control Plane internal token | Identity Service → Control Plane | `memory-internal-token` / `token` in the walkthrough | You provision it for both releases | Checking that the store in a grant exists. Set `controlplane.internalToken.existingSecret` on Memory and `productValidation.products.memory.credential.existingSecret` on Identity Service. |\n| Identity Service control token | Operator → Identity Service (`:9200`) | `ids-control-token` / `token` in the walkthrough | Supplied through `control.existingSecret` on Identity Service; that chart can also generate one | Creating, listing, updating, rotating and revoking agent keys |\n| Data Plane service credential | Data Plane → Identity Service | `memory-dp-credential` / `token` in the walkthrough | You provision it for both releases | Checking agent keys for `memory`. Set `identityService.credential.existingSecret` on Memory and register it in `runtime.serviceCredentials[]` on Identity Service. |\n| Agent key | Application → Data Plane (`:9000`) | None; returned once when minted or rotated | No | The Data Plane operations its grants allow |\n\nGenerated token Secrets are retained across upgrades and uninstalls (`helm.sh/resource-policy: keep`).\nUse existing Secrets for shared credentials and for reproducible deployment automation.\nSecret names and keys must agree between releases. See\n[Secret rotation](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/operations#secret-rotation)."},{"id":"control-plane-admin-token","title":"Control Plane admin token","role":"content","text":"Control Plane management endpoints require:\n\n[code example]\n\nProduction deployments should read the token from a mounted Secret file:\n\n[code example]\n\nThe Control Plane reads the token file on each request, so rotating the Secret\ndoes not require a Control Plane redeploy."},{"id":"data-plane-auth-modes","title":"Data Plane auth modes","role":"content","text":"The Data Plane defaults to agent-key authentication whenever no auth method is set."},{"id":"agent-key-authentication","title":"Agent-key authentication","role":"content","text":"The [`deploy` walkthrough](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/deploy) uses agent-key\nauthentication from the start. In the chart-rendered configuration, the chart writes the agent-key\nintrospection settings from `dataplane.identityServiceBaseURL` and `identityService.credential`.\nThese must point to the standalone release and its registered `memory-dp` credential.\n\nA bring-your-own (BYO) Data Plane configuration needs the `auth.agent_keys.introspection` block,\nincluding a BYO configuration without `auth`. See\n[Bring-your-own (BYO) configuration](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/configuration#bring-your-own-byo-configuration).\nThe `MEM_AUTH_METHOD` environment variable overrides the method.\n\nClients send agent keys as Bearer credentials:\n\n[code example]\n\nTreat agent keys as opaque credentials. Do not parse their contents."},{"id":"auth-disabled-data-plane","title":"Auth-disabled Data Plane","role":"security","text":"Use auth-disabled mode for development and testing only:\n\n[code example]\n\nThese are application configuration fields; put them under `memory` in Helm values.\nThe Data Plane refuses `auth.method: none` unless `profile` is exactly `dev`.\nChart-rendered configuration fails to render otherwise. With a supplied configuration Secret,\nthe same rule is enforced at process startup.\n\n> [!WARNING]\n> Do not expose an auth-disabled Data Plane to untrusted callers. In auth-disabled\n> mode, Redis Agent Memory does not authenticate or authorize Data Plane requests; any\n> caller that can reach the API can read or write memory for configured stores."},{"id":"store-authorization-and-grants","title":"Store authorization and grants","role":"content","text":"For agent-key requests, Redis Agent Memory checks both identity and resource\nauthorization:\n\n1. The Data Plane asks the Identity Service whether the key is valid, and caches the answer per pod.\n2. The key has a grant for the requested store resource.\n3. The grant includes the permission required by the operation.\n\nBecause of the cache, a revoked key stops working within up to 5 minutes (180 seconds soft, 300\nseconds hard). If the Identity Service can't be reached, keys that aren't cached get `503`.\n\nA grant names the tenant, product, store, and actions:\n\n[code example]\n\nUse the installation's tenant on every grant, for example `default`. Set Memory's `tenantId`\nand Identity Service's `runtime.apiKeyTenant.singleTenantID` to that same value. With a rendered\nControl Plane configuration, `tenantId` sets `auth.admin_tenant_id`, so admin-token stores belong\nto that tenant too. With a supplied Control Plane configuration Secret, set `auth.admin_tenant_id`\nyourself. Neither release checks the other release's tenant setting.\n\nGrant actions:\n\n| Action | Meaning |\n| --- | --- |\n| `read` | Read and search memory data. |\n| `write` | Mutate memory data. `write` implies `read`. |\n| `full` | `full` implies `write`. |\n\nOperation mapping:\n\n| Required permission | Data Plane operations |\n| --- | --- |\n| `read` | List sessions, get session memory, get session event, get session property, search long-term memory, get long-term memory. |\n| `write` | Add/delete session events, delete session memory, set session summary, set session property, create/update/delete long-term memory. |"},{"id":"worker-callbacks","title":"Worker callbacks","role":"content","text":"Redis Agent Memory workers consume background jobs and call the Data Plane to read\nsession events and write extracted long-term memories.\n\nThe [`deploy` values](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/deploy#3-create-ram-valuesyaml)\nalready enable worker callbacks. Each platform also needs the setup in\n[Prepare worker identity for your platform](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/deploy#prepare-worker-identity-for-your-platform).\n\nFor deployments where Redis Agent Memory Data Plane auth is enabled, workers should\nauthenticate with Kubernetes projected service-account tokens. The Helm\n`workerAuth.enabled` preset creates or uses a worker ServiceAccount and mounts a\nprojected token into the worker pod. The Data Plane must also be configured to\nvalidate and authorize that token through `auth.worker_identity`.\n\nMinimal worker-auth values:\n\n[code example]\n\nMatching Data Plane config:\n\n[code example]\n\nWorker identity access is controlled by `auth.worker_identity.subjects`.\nConfigure worker identity to:\n\n- Trust one or more exact Kubernetes service-account subjects.\n- Validate worker tokens by issuer, JWKS URI, audience, and signing algorithm.\n- Map each trusted subject to a Redis Agent Memory `Principal` with roles, scopes,\n  and resource grants.\n- Grant store access with resource keys such as `mem-store:<store-id>`.\n- Use `mem-store:*` for a shared worker identity, or use narrower store grants\n  and separate worker ServiceAccounts for stronger isolation.\n- Grant `read`, `write`, or `full`; `write` implies `read`, and `full` implies\n  `write`.\n\nThe Helm ServiceAccount/token settings only provide the Kubernetes credential.\nRedis Agent Memory authorization still comes from the server-side\n`auth.worker_identity` subject grants. With agent-key authentication, you must configure worker\nidentity."},{"id":"gateway-and-identity-provider-integration","title":"Gateway and identity provider integration","role":"content","text":"Use a gateway when it owns external authentication and coarse policy. For\nexample, a gateway can authenticate callers through an identity provider before\nit forwards requests to Redis Agent Memory.\n\nIf the gateway also owns the standard `Authorization` header, forward the Agent\nMemory key in `X-Api-Key`:\n\n[code example]\n\nRedis Agent Memory uses `X-Api-Key` as the Redis Agent Memory credential when present. The\ngateway token is still available to the gateway, but Redis Agent Memory authorizes the\nrequest from the server-side grants attached to the Redis Agent Memory key.\n\nGateway rules:\n\n- The gateway owns external authentication and perimeter policy.\n- Redis Agent Memory owns store-level authorization.\n- Redis Agent Memory keys are stored and forwarded by trusted infrastructure or\n  trusted applications.\n- Callers must not be able to bypass the gateway and reach the Data Plane\n  directly unless they also present a valid Redis Agent Memory credential."}],"examples":[{"id":"control-plane-admin-token-ex0","language":"http","code":"Authorization: Bearer <admin-token>","section_id":"control-plane-admin-token"},{"id":"control-plane-admin-token-ex1","language":"yaml","code":"profile: prod\n\nauth:\n  type: admin-token\n  admin_token:\n    token_file: /etc/controlplane-onprem/admin/token","section_id":"control-plane-admin-token"},{"id":"agent-key-authentication-ex0","language":"http","code":"Authorization: Bearer <agent-key>","section_id":"agent-key-authentication"},{"id":"auth-disabled-data-plane-ex0","language":"yaml","code":"profile: dev\nauth:\n  method: none","section_id":"auth-disabled-data-plane"},{"id":"store-authorization-and-grants-ex0","language":"json","code":"{\n  \"tenant\": \"<your-tenant-id>\",\n  \"product\": \"memory\",\n  \"resourceType\": \"mem-store\",\n  \"resourceId\": \"<store-id>\",\n  \"actions\": [\"read\", \"write\"]\n}","section_id":"store-authorization-and-grants"},{"id":"worker-callbacks-ex0","language":"yaml","code":"workerAuth:\n  enabled: true\n\n# Optional customization when the default worker ServiceAccount is not suitable.\nworker:\n  serviceAccount:\n    name: \"\"        # set to use an existing ServiceAccount\n    create: false   # set true to create a dedicated ServiceAccount without the preset\n    annotations: {}\n    token:\n      audience: redis-agent-memory\n      expirationSeconds: 3600\n      mountPath: /var/run/secrets/redis-agent-memory-worker\n      fileName: token","section_id":"worker-callbacks"},{"id":"worker-callbacks-ex1","language":"yaml","code":"dataplane_client:\n  base_url: http://redis-agent-memory:9000\n  auth:\n    disabled: false\n    type: service_account_token\n    token_file: /var/run/secrets/redis-agent-memory-worker/token\n\nauth:\n  method: agent_key\n  worker_identity:\n    enabled: true\n    issuer: <service-account-issuer>\n    jwks_uri: \"https://kubernetes.default.svc/openid/v1/jwks\"\n    audience:\n      - redis-agent-memory\n    subjects:\n      - subject: \"system:serviceaccount:<namespace>:redis-agent-memory-worker\"\n        user_id: \"redis-agent-memory-worker\"\n        roles:\n          - operator\n        resources:\n          \"mem-store:*\":\n            permissions:\n              - write","section_id":"worker-callbacks"},{"id":"gateway-and-identity-provider-integration-ex0","language":"http","code":"Authorization: Bearer <gateway-token>\nX-Api-Key: <ram-agent-key>","section_id":"gateway-and-identity-provider-integration"}]}
{"schema_version":2,"id":"operate/iris/agent-memory/self-managed/configuration","title":"Configuration","url":"https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/configuration/","summary":"Configure the Redis Agent Memory Data Plane, Control Plane, and Identity Service from Helm values, or bring your own configuration files.","aliases":["/develop/ai/context-engine/agent-memory/self-managed/data-plane-configuration/","/operate/iris/agent-memory/self-managed/data-plane-configuration/"],"tags":["docs","operate","iris"],"last_updated":"2026-10-21T18:51:10+03:00","page_type":"content","content_hash":"e620c11ae64308f9b1648f8f6f878c5199c3142240b153137ae987094cac1bc9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The Memory chart renders Data Plane and Control Plane configuration from Helm values\n(`config.render: true` and `controlplane.config.render: true`). Identity Service uses its own\nrelease and `values-ids.yaml`. Credentials stay in existing Secrets. For a complete\nexample, see the values in [step 3 of Deploy](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/deploy#3-create-ram-valuesyaml).\n\nTo supply complete configuration files in your own Secrets instead, see\n[Bring-your-own (BYO) configuration](#bring-your-own-byo-configuration)."},{"id":"where-each-setting-goes","title":"Where each setting goes","role":"content","text":"| Values | Configures |\n| --- | --- |\n| `shared` | Settings common to the Data Plane and the Control Plane, such as `databases`. The chart merges them under `memory` and `controlplane.configData`. |\n| `memory` | The Data Plane and worker configuration file. |\n| `controlplane.configData` | The Control Plane configuration file. |\n| `dataplane.identityServiceBaseURL`, `identityService.credential.*`, `identityService.allowInsecureTransport` | Memory's connection to the standalone Identity Service. These values do not configure that service. |\n| Separate `values-ids.yaml` | Identity Service's `metadata`, `control`, `runtime`, `productValidation`, and optional `subjectGrants` values. |\n| Overlay Secret (`secrets.secretName`, key `overlay.yaml`) | `metadata.urls`, `metadata.namespace`, `databases.\"1\".urls`, `background_jobs.redis.urls`, and provider `api_key` values. The Data Plane, the workers, and the Control Plane all read it. |"},{"id":"data-plane-settings","title":"Data Plane settings","role":"content","text":"Data Plane settings go under `memory`, or at the top level of a BYO Data Plane configuration file.\n\n| Setting | Purpose |\n| --- | --- |\n| `metadata.urls` | Required. Metadata Redis, which holds store records. `metadata.namespace` sets the key namespace (default `iris:memory`). |\n| `databases` | Required. Store Redis, as `databases.\"1\".urls`. |\n| `embedding.models.default_embedding_model`, `embedding.models.dimensions` | Required. The embedding model and its vector size. The Control Plane's `embedding.dimensions` must match. |\n| `auth` | Data Plane authentication. Defaults to `agent_key`. |\n| `inference_providers` | Required when any `llm` block is set. Each entry holds a provider's `endpoint`. |\n| `server` | Data Plane bind address and port. |\n| `license.license_path` | Path where the license Secret is mounted. |\n| `request_region` | Region used for background work routing. |\n| `background_jobs.redis` | Job Redis connection used by background workers. |\n| `background_jobs.idempotency` | Enable execution idempotency and extraction serialization for production workers. Requires `enabled: true` and `urls`; it can share Job Redis. |\n| `embedders_connection_details` | Embedding provider endpoint and credentials. |\n| `dataplane_client` | Worker callback client configuration. |\n| `promote_session_memory` | Promotion strategy and LLM connection used by workers. |\n\nAn `llm` block names its provider with `llm.provider`, which must match an `inference_providers`\nentry. The endpoint goes in `inference_providers.<name>.endpoint`, not in the `llm` block.\n\nIn the chart-rendered configuration, the chart writes the agent-key introspection settings for you:\nit owns `auth.agent_keys.introspection.base_url`, `credential`, and `allow_insecure_transport`, and\nrefuses hand-set values in either `memory.auth` or `shared.auth`. Cache tuning under\n`auth.agent_keys.introspection.cache` is still allowed. Set `dataplane.identityServiceBaseURL`\nand `identityService.credential.existingSecret`; optional `secretKey` defaults to `token`, and\n`existingSecretChecksum` triggers a rollout after credential rotation. Set `memory.auth.method: agent_key`\nexplicitly when combining agent keys with worker or other authentication methods.\n\nThe runtime endpoint must use `https://`. An `http://` endpoint is accepted only when\n`identityService.allowInsecureTransport: true` is set and the FIPS profile is not enabled.\nSubjects using the `identity_service` authority also need this connection for grant lookup,\neven when another authentication method is selected."},{"id":"bring-your-own-byo-configuration","title":"Bring-your-own (BYO) configuration","role":"content","text":"With BYO configuration, you supply each complete configuration file in your own Secret, and the\nchart injects nothing into it. Add the keys the chart would otherwise write:\n\n- **Data Plane**: the `auth.agent_keys.introspection` block.\n- **Control Plane**: `auth.internal_token.token_file: /etc/controlplane-onprem/internal/token`\n  and `auth.admin_tenant_id` matching the installation's tenant.\n\nWith `config.existingSecret`, the chart does not inspect the supplied file. Rendering cannot\ncatch missing introspection settings or an invalid development profile in that file; startup\nvalidation still applies. Connection Secrets must still be named in Helm values so they are mounted.\n\nThe Data Plane defaults to `agent_key` whenever no auth method is set. A BYO Data Plane configuration\nwithout `auth` therefore needs the `auth.agent_keys.introspection` block. For development only,\n`auth.method: none` requires exact `profile: dev`.\nThe `MEM_AUTH_METHOD` environment variable overrides the method."},{"id":"data-plane-configuration-file","title":"Data Plane configuration file","role":"content","text":"Create `memory-dataplane.config.yaml`:\n\n[code example]\n\nThe `auth.worker_identity` and `dataplane_client.auth` settings let workers call the Data Plane\nunder agent-key authentication. They need `workerAuth.enabled: true` in the values, and the platform\nsetup in [worker identity](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/deploy#prepare-worker-identity-for-your-platform).\n\nCreate the Data Plane config Secret with the key `memory-dataplane.config.yaml`:\n\n[code example]"},{"id":"control-plane-configuration-file","title":"Control Plane configuration file","role":"content","text":"Create `controlplane-onprem.config.yaml`:\n\n[code example]\n\nThe Control Plane only needs `embedding.dimensions`, to create each store's vector index. Configure\nthe embedding provider, model, and credentials in the Data Plane configuration.\n\nCreate the Control Plane config Secret:\n\n[code example]"},{"id":"helm-values","title":"Helm values","role":"content","text":"In `ram-values.yaml`, replace `config.render`, `shared`, `memory`, `controlplane.config.render`, and\n`controlplane.configData` with the two Secrets. The chart fails at install time if both\n`config.existingSecret` and `config.render` are set.\n\n[code example]"},{"id":"connect-releases-across-namespaces","title":"Connect releases across namespaces","role":"content","text":"For another namespace, resolve the standalone Service using its full address, such as\n`ids-iris-identity-service.<identity-namespace>.svc:9200`, and the Memory Control Plane as\n`redis-agent-memory-controlplane.<memory-namespace>.svc:9100`. Configure their TLS frontends\nfor the two connection URLs. With a naming override, use the rendered Service name.\n\nPods can mount Secrets only from their own namespace. Provision the same runtime credential\nand internal token values in both namespaces, plus each namespace's trust bundle. Allow Memory\nto reach Identity Service and Identity Service to reach the Control Plane, including any\nTLS proxies. See [Network policy](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/operations#network-policy)."},{"id":"optional-playbook-extraction","title":"Optional Playbook extraction","role":"parameters","text":"On the Identity Service release, allow Memory subject grants for Playbook extraction:\n\n[code example]\n\nAlso add `subject-grant-lookup` to the `memory-dp` runtime credential's `allowedOperations`\nalongside `api-key-introspect`. These are Identity Service values, not Memory's `identityService`\nvalues. `read_all_owners` is a subject-grant action; do not add it to agent-key grants."}],"examples":[{"id":"data-plane-configuration-file-ex0","language":"yaml","code":"server:\n  host: 0.0.0.0\n  port: 9000\n\nlicense:\n  license_path: /etc/redis-agent-memory/license\ndefault_extraction_strategy: instruct\n\nrequest_region:\n  default: eu1\n\nbackground_jobs:\n  idempotency:\n    enabled: true\n    urls:\n      - redis://<job-redis-host>:6379\n  redis:\n    enabled: true\n    queue_prefix: ram\n    urls:\n      - redis://<job-redis-host>:6379\n    worker_regions:\n      - eu1\n\nmetadata:\n  urls:\n    - redis://<metadata-redis-host>:6379\n  namespace: iris:memory\n\ndatabases:\n  \"1\":\n    urls:\n      - redis://<store-redis-host>:6379\n\nauth:\n  method: agent_key\n  agent_keys:\n    introspection:\n      base_url: https://<identity-service-tls-endpoint>\n      product: memory\n      credential:\n        token_file: /etc/identity-service/runtime/memory-dp/token\n  worker_identity:\n    enabled: true\n    issuer: <service-account-issuer>\n    jwks_uri: https://kubernetes.default.svc/openid/v1/jwks\n    audience:\n      - redis-agent-memory\n    subjects:\n      - subject: \"system:serviceaccount:<namespace-name>:redis-agent-memory-worker\"\n        user_id: redis-agent-memory-worker\n        roles:\n          - operator\n        resources:\n          \"mem-store:*\":\n            permissions:\n              - write\n\nembedding:\n  provider: openai\n  models:\n    default_embedding_model: text-embedding-3-large\n    dimensions: 3072\n\nembedders_connection_details:\n  openai:\n    base_url: https://api.openai.com\n    credentials:\n      type: static\n      api_key: \"<embedding-api-key>\"\n\ninference_providers:\n  openai:\n    endpoint:\n      base_url: https://api.openai.com/v1\n      timeout: 30s\n      auth_format: bearer\n\ndataplane_client:\n  base_url: http://redis-agent-memory:9000\n  auth:\n    disabled: false\n    type: service_account_token\n    token_file: /var/run/secrets/redis-agent-memory-worker/token\n\npromote_session_memory:\n  strategies:\n    instruct:\n      llm:\n        provider: openai\n        credentials:\n          type: static\n          api_key: \"<promotion-llm-api-key>\"\n        models:\n          default_chat_model: gpt-4o","section_id":"data-plane-configuration-file"},{"id":"data-plane-configuration-file-ex1","language":"bash","code":"kubectl -n <namespace-name> create secret generic ram-config \\\n  --from-file=memory-dataplane.config.yaml=./memory-dataplane.config.yaml","section_id":"data-plane-configuration-file"},{"id":"control-plane-configuration-file-ex0","language":"yaml","code":"profile: prod\n\nauth:\n  type: admin-token\n  admin_token:\n    token_file: /etc/controlplane-onprem/admin/token\n  internal_token:\n    token_file: /etc/controlplane-onprem/internal/token\n  admin_tenant_id: default\n\nlicense:\n  license_path: /etc/redis-agent-memory/license\n\nmetadata:\n  urls:\n    - redis://<metadata-redis-host>:6379\n  namespace: iris:memory\n\ndatabases:\n  \"1\":\n    urls:\n      - redis://<store-redis-host>:6379\n\nembedding:\n  dimensions: 3072","section_id":"control-plane-configuration-file"},{"id":"control-plane-configuration-file-ex1","language":"bash","code":"kubectl -n <namespace-name> create secret generic ram-controlplane-config \\\n  --from-file=controlplane-onprem.config.yaml=./controlplane-onprem.config.yaml","section_id":"control-plane-configuration-file"},{"id":"helm-values-ex0","language":"yaml","code":"tenantId: default\nconfig:\n  existingSecret: ram-config\nidentityService:\n  credential:\n    existingSecret: memory-dp-credential\ncontrolplane:\n  internalToken:\n    existingSecret: memory-internal-token\n  config:\n    existingSecret: ram-controlplane-config","section_id":"helm-values"},{"id":"optional-playbook-extraction-ex0","language":"yaml","code":"subjectGrants:\n  allowed:\n    - product: memory\n      resourceType: mem-store\n      actions: [read, read_all_owners]","section_id":"optional-playbook-extraction"}]}
{"schema_version":2,"id":"operate/iris/agent-memory/self-managed/control-plane-api-reference","title":"Redis Agent Memory Control Plane API reference","url":"https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/control-plane-api-reference/","summary":"","aliases":["/develop/ai/context-engine/agent-memory/self-managed/control-plane-api-reference/"],"tags":[],"last_updated":"2026-08-03T15:16:58+02:00","page_type":"index","sections":[],"examples":[]}
{"schema_version":2,"id":"operate/iris/agent-memory/self-managed/deploy","title":"Deploy Redis Agent Memory","url":"https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/deploy/","summary":"Install Redis Agent Memory with chart-rendered configuration and agent-key authentication, then make one authenticated call.","aliases":["/develop/ai/context-engine/agent-memory/self-managed/control-plane/","/develop/ai/context-engine/agent-memory/self-managed/deploy-control-plane/","/operate/iris/agent-memory/self-managed/deploy-control-plane/","/develop/ai/context-engine/agent-memory/self-managed/install-k8s/","/develop/ai/context-engine/agent-memory/self-managed/deploy-static/","/operate/iris/agent-memory/self-managed/deploy-static/"],"tags":["docs","operate","iris"],"last_updated":"2026-10-21T18:51:10+03:00","page_type":"content","content_hash":"42dd2c525189a52bc35339c4c694cdd1c0f34ca9edebbdd5e49e52104d954ed6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"These steps install Redis Agent Memory with chart-rendered configuration and agent-key\nauthentication, and end with one authenticated call to the Data Plane.\n\nInstall Identity Service as release `ids`, then Memory as release `redis-agent-memory`.\nThese steps use Memory chart `0.8.0` and Identity Service chart `0.10.0` in one namespace.\nYou create the shared credentials and Identity Service control token; Memory generates its\nControl Plane admin token. See [Credentials](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/authentication#credentials).\n\nBefore installing, provide two Transport Layer Security (TLS) endpoints through your hosting\nenvironment: one forwarding to `ids-iris-identity-service:9200`, and one forwarding to\n`redis-agent-memory-controlplane:9100`. Neither chart terminates TLS. Replace the endpoint\nplaceholders with addresses reachable from the calling pods. Obtain their certificate authority\n(CA) certificates as a PEM bundle named `service-ca.crt`; use your organization's trust bundle\nif the endpoints use public certificates.\n\nBefore you begin, review the [prerequisites](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/prerequisites)."},{"id":"isolated-lab-without-tls","title":"Isolated lab without TLS","role":"content","text":"For a network-isolated lab without the Federal Information Processing Standards (FIPS) profile,\nyou can use the in-cluster HTTP Services instead of TLS frontends. In `ram-values.yaml`, change:\n\n[code example]\n\nIn `values-ids.yaml`, change the validator URL to\n`productValidation.products.memory.baseURL: http://redis-agent-memory-controlplane:9100`.\nKeep the other settings in both files. Tokens travel over plaintext HTTP on these two hops;\nuse TLS for production. FIPS refuses this configuration.\n\nFor this lab option, skip `service-ca.crt` and the initial `ram-ca` creation in step 2, and omit\n`tls` from `values-ids.yaml`. For self-managed worker identity, still create `ram-ca` from the\ncluster CA as described in [Prepare worker identity](#prepare-worker-identity-for-your-platform).\nOn EKS and AKS, omit Memory's `tls` block too unless another connection requires a private CA."},{"id":"1-create-the-namespace","title":"1. Create the namespace","role":"content","text":"[code example]"},{"id":"2-create-secrets","title":"2. Create Secrets","role":"content","text":"[code example]\n\nBoth releases mount `memory-dp-credential` and `memory-internal-token`. You own these Secrets;\nneither release generates them in this procedure. The runtime credential lets Memory check\nagent keys. The internal token lets Identity Service validate grants against Memory's Control\nPlane. The `ids-control-token` Secret is separate and is used only by Identity Service.\n\n`overlay.yaml` holds the Redis addresses and provider credentials. The Data Plane, the workers, and\nthe Control Plane all mount it:\n\n[code example]\n\n`ids-metadata.yaml` tells the Identity Service where Metadata Redis is:\n\n[code example]"},{"id":"install-identity-service-first","title":"Install Identity Service first","role":"setup","text":"Save this as `values-ids.yaml`:\n\n[code example]\n\n[code example]\n\nThe Memory Control Plane does not exist yet. Configure its endpoint now, but wait until both\nreleases and the TLS routes are ready before minting keys: Identity Service calls the Control\nPlane to validate each store grant."},{"id":"prepare-worker-identity-for-your-platform","title":"Prepare worker identity for your platform","role":"content","text":"With agent-key authentication, workers call the Data Plane with their projected service account\ntoken, and the Data Plane checks that token against the cluster's JSON Web Key Set (JWKS). The\nchart's defaults disagree here: the Data Plane defaults to `agent_key` authentication, while\n`workerAuth.enabled` defaults to `false`. With `agent_key`, you must configure worker identity. The\nvalues in [step 3](#3-create-ram-valuesyaml) do this.\n\nThe Data Plane fetches the JWKS without credentials and trusts only the\ncertificate authority (CA) bundle in its image. What you set up depends on where the cluster's\nissuer is served:\n\n| Cluster | `issuer` | `jwks_uri` | Extra steps | Tested |\n|---------|----------|------------|-------------|--------|\n| Self-managed (kind, kubeadm, on-premises) | from `kubectl get --raw /.well-known/openid-configuration` (kind: `https://kubernetes.default.svc.cluster.local`) | `https://kubernetes.default.svc/openid/v1/jwks` | `ram-ca` Secret containing the cluster CA + `tls.caCertSecret: ram-ca` + ClusterRoleBinding (cluster admin's decision) | Yes, on kind |\n| Amazon Elastic Kubernetes Service (EKS) | `aws eks describe-cluster --name <cluster> --query cluster.identity.oidc.issuer --output text` | the `jwks_uri` field of `<issuer>/.well-known/openid-configuration` | Data Plane egress to the issuer host, or the `com.amazonaws.<region>.oidc-eks` PrivateLink endpoint with private DNS. The hostname differs for IPv6 clusters and AWS China, so copy it, don't type it | No |\n| Azure Kubernetes Service (AKS), OpenID Connect (OIDC) issuer enabled | `az aks show -n <cluster> -g <resource-group> --query oidcIssuerProfile.issuerUrl -o tsv` (exact, including the trailing slash) | the `jwks_uri` field of `<issuer>.well-known/openid-configuration` | Data Plane egress. The issuer is on by default only for new Standard clusters on 1.34+ and on AKS Automatic. `--enable-oidc-issuer` on an existing cluster restarts the API server and cannot be undone | No |\n| Google Kubernetes Engine (GKE) | as self-managed | as self-managed | as self-managed, because anonymous access to the GKE `jwks` endpoint is not verified | No |\n| Air-gapped (any) | as self-managed | as self-managed | as self-managed | Yes, on kind |\n\nAlways take `jwks_uri` from the discovery document. Don't build it from the issuer, because the AKS\npath differs. On EKS and AKS, replace `issuer` and `jwks_uri` in the values and skip\nthe rest of this section. With TLS service endpoints, keep `tls.caCertSecret: ram-ca`\nfor their trust bundle. For the HTTP-only lab option, omit this block unless another connection\nrequires a private CA.\n\nOn self-managed, GKE, and air-gapped clusters, add the cluster CA to the service CA bundle\nso the Data Plane also trusts the API server certificate:\n\n[code example]\n\nFor the HTTP-only lab option, use `cp cluster-ca.crt ram-ca.crt` instead of the `cat` command;\nthere is no service CA file to combine.\n\nThe Data Plane also needs to read the JWKS without logging in. Allowing that is the cluster\nadmin's decision:\n\n[code example]\n\nThis binding grants only `get` on `/.well-known/openid-configuration` and `/openid/v1/jwks`, which\nserve issuer metadata and public signing keys, not secrets. It applies to unauthenticated callers\nacross the whole cluster. It has no effect where the API server disables anonymous authentication.\nOn those clusters, there is no working setup for `agent_key` with workers."},{"id":"3-create-ram-valuesyaml","title":"3. Create `ram-values.yaml`","role":"content","text":"[code example]\n\n- `<chat-model>` is any chat model the endpoint serves.\n- `controlplane.configData.embedding.dimensions` must equal `memory.embedding.models.dimensions`.\n- `background_jobs.idempotency.enabled` prevents successful jobs from running again after\n  redelivery and serializes extraction for the same session. The walkthrough enables it for\n  workers and supplies its Redis URL through the overlay. Keep it enabled in production;\n  without it, workers warn that duplicate memories remain possible. This is separate from\n  queue submission deduplication.\n- `<service-account-issuer>` is the cluster's issuer. Read it with:\n\n  [code example]\n\n- `tls.caCertSecret` names the CA bundle from [step 2](#2-create-secrets).\n- The chart writes the agent-key introspection settings and mounts `memory-dp-credential` at\n  `/etc/identity-service/runtime/memory-dp/token`. Do not hand-set the introspection endpoint or credential under `memory.auth` or `shared.auth`.\n- `tenantId: default` matches `runtime.apiKeyTenant.singleTenantID` in `values-ids.yaml`.\n  The Control Plane creates admin-token stores in this tenant.\n- The published Memory chart defaults `image.tag` and `controlplane.image.tag` to `0.8.0`;\n  the example pins them explicitly. Identity Service uses its own chart's application version,\n  `0.10.0`, independently.\n\n\nNo external model provider? To try the API with local models, see [Evaluate locally](#evaluate-locally)."},{"id":"before-you-install-check-your-values","title":"Before you install: check your values","role":"content","text":"Check each value in `ram-values.yaml` before you install. If one is wrong, you see the symptom in\nits row. The rows are checklist items CL-1 to CL-6.\n\n| Row | Check | Symptom | Fix |\n| --- | --- | --- | --- |\n| <a id=\"cl-1\"></a>CL-1 | `memory.license.license_path` is set | Server and worker pods `CrashLoopBackOff`; log: `initialize license enforcement: license validation failed; refusing to start: invalid license format` | Set `memory.license.license_path: /etc/redis-agent-memory/license` |\n| <a id=\"cl-2\"></a>CL-2 | `memory.promote_session_memory.strategies.instruct.llm` is set | Worker pods `CrashLoopBackOff` (server stays Ready); log: `promote_session_memory: Strategies: strategies.instruct.llm is required.` | Add the `instruct.llm` block |\n| <a id=\"cl-3\"></a>CL-3 | Each `llm.provider` names an `inference_providers` entry | Server and worker pods `CrashLoopBackOff`; log: `panic: config validation failed: promote_session_memory.strategies.instruct.llm: provider \"<name>\" is not defined in inference_providers (defined: <names>)` | Add `memory.inference_providers.<name>` or fix the name |\n| <a id=\"cl-4\"></a>CL-4 | `memory.dataplane_client.base_url` is `http://redis-agent-memory:9000` | Pods Ready, but no memories appear; worker log: `getting session memory: Get \"http://<wrong-host>:9000/v1/stores/<store-id>/session-memory/...\"` … `dial tcp: lookup <wrong-host>` (the rest depends on the cluster DNS) | Set `http://redis-agent-memory:9000` |\n| <a id=\"cl-5\"></a>CL-5 | Any overridden image tags exist in the chosen registry | Pods `ImagePullBackOff`, event: `Failed to pull image \"redislabs/agent-memory:<tag>\": … not found` | Use the published defaults or valid mirrored tags for each release |\n| <a id=\"cl-6\"></a>CL-6 | `auth.worker_identity` matches the worker (issuer, audience, subject with your namespace) | Pods Ready, but no memories appear; worker log: `memory-dataplane API error: status 401`; the Data Plane logs `401` on `GET /v1/stores/<store-id>/session-memory/<session-id>` with no reason | Fix `issuer` from `/.well-known/openid-configuration` and the namespace in `subject`. The same symptom appears when the Data Plane cannot fetch the cluster JWKS, so also check [worker identity for your platform](#prepare-worker-identity-for-your-platform) |"},{"id":"4-install","title":"4. Install","role":"content","text":"[code example]\n\nOn small clusters, install without `--atomic --wait`, as shown, and watch pod status.\n\nIf you want Helm to wait, set an explicit timeout that matches the environment:\n\n[code example]"},{"id":"5-verify-pods-and-health","title":"5. Verify pods and health","role":"content","text":"[code example]\n\nThe server, worker, and `redis-agent-memory-controlplane` pods must be Ready.\nCheck the separate Identity Service release too:\n\n[code example]\n\nWait for its pod to be Ready. With TLS frontends, verify that both configured endpoints route\nto their Services before creating a key. For the HTTP-only lab option, verify that both\nin-cluster Service addresses are reachable from the calling pods.\n\nRun each `port-forward` in this guide in its own terminal. Port-forward the Data Plane and check its\nhealth. The health check doesn't need an agent key:\n\n[code example]\n\nThe response is:\n\n[code example]"},{"id":"6-create-a-store","title":"6. Create a store","role":"content","text":"Use the Control Plane admin token:\n\n[code example]\n\nThe response carries `storeId`."},{"id":"7-mint-an-agent-key","title":"7. Mint an agent key","role":"content","text":"Use the Identity Service control token:\n\n[code example]\n\nUse the same tenant as `tenantId` and `runtime.apiKeyTenant.singleTenantID`, here `default`.\nThe response's `token` is the agent key. It is shown only once."},{"id":"8-make-one-authenticated-call","title":"8. Make one authenticated call","role":"content","text":"[code example]\n\nThe first long-term memories appear up to 5 minutes after the session events, because promotion\nruns at the end of a clock-aligned 300-second window. To shorten the wait, set\n`extractionCadence.activeIntervalSeconds` (60–600) on the store. No API triggers promotion."},{"id":"evaluate-locally","title":"Evaluate locally","role":"content","text":"This setup runs with no external model provider. The `noop` embedder produces meaningless vectors,\nand a small local model produces rough extractions. Use it to try the API, not to judge quality or\nfor production.\n\nSave this Deployment and Service as `ollama.yaml`. Replace `<ollama-version>` with 0.13.3 or later:\n\n[code example]\n\nDeploy it in the release namespace:\n\n[code example]\n\nThe first start downloads about 2 GB.\n\nEdit the existing `ram-values.yaml`: replace the complete `memory.embedding`,\n`memory.embedders_connection_details`, `memory.inference_providers`, and\n`memory.promote_session_memory` maps with the corresponding maps in this snippet. Also update\n`controlplane.configData.embedding.dimensions`. Keep the other settings, including authentication,\nworker callbacks, and `background_jobs.idempotency`.\n\nApply these edits to the existing file. Passing the snippet as another `-f` file merges the maps\nand retains old OpenAI entries.\n\n[code example]\n\n`api_key: ollama` is a placeholder. The client requires a key, and Ollama ignores it.\n\nReplace `overlay.yaml` with the Redis-only overlay, removing the complete\n`embedders_connection_details` and `promote_session_memory` blocks:\n\n[code example]\n\nIf Memory is already installed, update the `ram-secrets` Secret from this file, upgrade with\nthe edited values, and restart the Data Plane, workers, and Control Plane so they reload the\noverlay. See [Secret rotation](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/operations#secret-rotation).\n\nUse this provider switch only in a disposable evaluation installation. Changing the settings\ndoes not migrate existing stores or their vector indexes. After switching, create a new store\nand agent key using steps 6–7 for local evaluation. Existing stores retain their original\nembedding dimensions and cannot be reused with this example."},{"id":"next-steps","title":"Next steps","role":"content","text":"- [API examples](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/api-examples) for the full API.\n- [Operations](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/operations) for key rotation."}],"examples":[{"id":"isolated-lab-without-tls-ex0","language":"yaml","code":"dataplane:\n  identityServiceBaseURL: http://ids-iris-identity-service:9200\nidentityService:\n  allowInsecureTransport: true\n  credential:\n    existingSecret: memory-dp-credential","section_id":"isolated-lab-without-tls"},{"id":"1-create-the-namespace-ex0","language":"bash","code":"kubectl create namespace <namespace-name>","section_id":"1-create-the-namespace"},{"id":"2-create-secrets-ex0","language":"bash","code":"kubectl -n <namespace-name> create secret generic ram-license \\\n  --from-file=license=./ram-license.key\nkubectl -n <namespace-name> create secret generic ram-secrets \\\n  --from-file=overlay.yaml=./overlay.yaml\nkubectl -n <namespace-name> create secret generic ram-ids-metadata \\\n  --from-file=metadata.yaml=./ids-metadata.yaml\nkubectl -n <namespace-name> create secret generic memory-dp-credential \\\n  --from-literal=token=\"$(openssl rand -hex 32)\"\nkubectl -n <namespace-name> create secret generic memory-internal-token \\\n  --from-literal=token=\"$(openssl rand -hex 32)\"\nkubectl -n <namespace-name> create secret generic ids-control-token \\\n  --from-literal=token=\"$(openssl rand -hex 32)\"\nkubectl -n <namespace-name> create secret generic ram-ca \\\n  --from-file=ca.crt=./service-ca.crt --save-config","section_id":"2-create-secrets"},{"id":"2-create-secrets-ex1","language":"yaml","code":"metadata:\n  urls: [\"redis://<metadata-redis-host>:6379\"]\ndatabases:\n  \"1\":\n    urls: [\"redis://<store-redis-host>:6379\"]\nbackground_jobs:\n  redis:\n    urls: [\"redis://<job-redis-host>:6379\"]\n  idempotency:\n    urls: [\"redis://<job-redis-host>:6379\"]\nembedders_connection_details:\n  openai:\n    credentials:\n      api_key: <openai-api-key>\npromote_session_memory:\n  strategies:\n    instruct:\n      llm:\n        credentials:\n          api_key: <openai-api-key>","section_id":"2-create-secrets"},{"id":"2-create-secrets-ex2","language":"yaml","code":"metadata:\n  urls: [\"redis://<metadata-redis-host>:6379\"]","section_id":"2-create-secrets"},{"id":"install-identity-service-first-ex0","language":"yaml","code":"metadata:\n  existingSecret: ram-ids-metadata\ncontrol:\n  existingSecret: ids-control-token\n  autoGenerate: false\nruntime:\n  unscopedGrantsProduct: memory\n  apiKeyTenant:\n    singleTenantID: default\n  serviceCredentials:\n    - name: memory-dp\n      subject: memory-dp\n      allowedOperations: [api-key-introspect]\n      allowedProducts: [memory]\n      existingSecret: memory-dp-credential\n      secretKey: token\nproductValidation:\n  products:\n    memory:\n      baseURL: https://<control-plane-tls-endpoint>\n      credential:\n        existingSecret: memory-internal-token\n        secretKey: token\ntls:\n  caCertSecret: ram-ca","section_id":"install-identity-service-first"},{"id":"install-identity-service-first-ex1","language":"bash","code":"helm repo add redis-ai https://helm.redis.io/ai\nhelm repo update redis-ai\nhelm install ids redis-ai/iris-identity-service \\\n  --version 0.10.0 \\\n  --namespace <namespace-name> \\\n  -f values-ids.yaml","section_id":"install-identity-service-first"},{"id":"prepare-worker-identity-for-your-platform-ex0","language":"bash","code":"kubectl -n <namespace-name> get configmap kube-root-ca.crt -o jsonpath=\"{.data.ca\\.crt}\" >./cluster-ca.crt\ncat service-ca.crt cluster-ca.crt > ram-ca.crt\nkubectl -n <namespace-name> create secret generic ram-ca \\\n  --from-file=ca.crt=./ram-ca.crt --dry-run=client -o yaml | kubectl apply -f -","section_id":"prepare-worker-identity-for-your-platform"},{"id":"prepare-worker-identity-for-your-platform-ex1","language":"bash","code":"kubectl create clusterrolebinding redis-agent-memory-oidc-discovery \\\n  --clusterrole=system:service-account-issuer-discovery --group=system:unauthenticated","section_id":"prepare-worker-identity-for-your-platform"},{"id":"3-create-ram-valuesyaml-ex0","language":"yaml","code":"tenantId: default\ndataplane:\n  identityServiceBaseURL: https://<identity-service-tls-endpoint>\nidentityService:\n  credential:\n    existingSecret: memory-dp-credential\n    secretKey: token\nlicense:\n  existingSecret: ram-license\nconfig:\n  render: true\nsecrets:\n  secretName: ram-secrets\nshared:\n  databases:\n    \"1\":\n      name: default\nworkerAuth:\n  enabled: true\nmemory:\n  license:\n    license_path: /etc/redis-agent-memory/license\n  default_extraction_strategy: instruct\n  embedding:\n    provider: openai\n    models:\n      default_embedding_model: text-embedding-3-small\n      dimensions: 1536\n  embedders_connection_details:\n    openai:\n      base_url: https://api.openai.com\n      credentials:\n        type: static\n  inference_providers:\n    openai:\n      endpoint:\n        base_url: https://api.openai.com/v1\n        timeout: 30s\n        auth_format: bearer\n  promote_session_memory:\n    strategies:\n      instruct:\n        llm:\n          provider: openai\n          credentials:\n            type: static\n          models:\n            default_chat_model: <chat-model>\n  background_jobs:\n    idempotency:\n      enabled: true\n    redis:\n      enabled: true\n      queue_prefix: ram\n      worker_regions: [default]\n  request_region:\n    default: default\n  dataplane_client:\n    base_url: http://redis-agent-memory:9000\n    auth:\n      disabled: false\n      type: service_account_token\n      token_file: /var/run/secrets/redis-agent-memory-worker/token\n  auth:\n    method: agent_key\n    worker_identity:\n      enabled: true\n      issuer: <service-account-issuer>\n      jwks_uri: https://kubernetes.default.svc/openid/v1/jwks\n      audience:\n        - redis-agent-memory\n      subjects:\n        - subject: \"system:serviceaccount:<namespace-name>:redis-agent-memory-worker\"\n          user_id: redis-agent-memory-worker\n          roles:\n            - operator\n          resources:\n            \"mem-store:*\":\n              permissions:\n                - write\n  session_summarisation:\n    enabled: false\n  session_summary_view:\n    enabled: false\nimage:\n  tag: \"0.8.0\"\ncontrolplane:\n  internalToken:\n    existingSecret: memory-internal-token\n  image:\n    tag: \"0.8.0\"\n  config:\n    render: true\n  configData:\n    profile: prod\n    auth:\n      type: admin-token\n      admin_token:\n        token_file: /etc/controlplane-onprem/admin/token\n    license:\n      license_path: /etc/redis-agent-memory/license\n    embedding:\n      dimensions: 1536\ntls:\n  caCertSecret: ram-ca","section_id":"3-create-ram-valuesyaml"},{"id":"3-create-ram-valuesyaml-ex1","language":"bash","code":"kubectl get --raw /.well-known/openid-configuration | jq -r .issuer","section_id":"3-create-ram-valuesyaml"},{"id":"4-install-ex0","language":"bash","code":"helm repo add redis-ai https://helm.redis.io/ai\nhelm repo update redis-ai\nhelm install redis-agent-memory redis-ai/redis-agent-memory \\\n  --version 0.8.0 \\\n  --namespace <namespace-name> \\\n  -f ram-values.yaml\nkubectl -n <namespace-name> get pods -w","section_id":"4-install"},{"id":"4-install-ex1","language":"bash","code":"helm install redis-agent-memory redis-ai/redis-agent-memory \\\n  --version 0.8.0 \\\n  --namespace <namespace-name> \\\n  -f ram-values.yaml \\\n  --wait \\\n  --timeout 15m","section_id":"4-install"},{"id":"5-verify-pods-and-health-ex0","language":"bash","code":"kubectl -n <namespace-name> get pods -l app.kubernetes.io/name=redis-agent-memory","section_id":"5-verify-pods-and-health"},{"id":"5-verify-pods-and-health-ex1","language":"bash","code":"kubectl -n <namespace-name> get pods -l app.kubernetes.io/instance=ids","section_id":"5-verify-pods-and-health"},{"id":"5-verify-pods-and-health-ex2","language":"bash","code":"kubectl -n <namespace-name> port-forward svc/redis-agent-memory 9000:9000\ncurl http://localhost:9000/health/readiness","section_id":"5-verify-pods-and-health"},{"id":"5-verify-pods-and-health-ex3","language":"json","code":"{\"status\":\"ready\"}","section_id":"5-verify-pods-and-health"},{"id":"6-create-a-store-ex0","language":"bash","code":"kubectl -n <namespace-name> port-forward svc/redis-agent-memory-controlplane 9100:9100\nRAM_ADMIN_TOKEN=$(kubectl -n <namespace-name> get secret \\\n  redis-agent-memory-controlplane-admin-token -o jsonpath='{.data.token}' | base64 -d)\ncurl -sS -X POST http://localhost:9100/v1/stores \\\n  -H \"Authorization: Bearer $RAM_ADMIN_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"name\": \"my-store\"}'","section_id":"6-create-a-store"},{"id":"7-mint-an-agent-key-ex0","language":"bash","code":"kubectl -n <namespace-name> port-forward svc/ids-iris-identity-service 9200:9200\nIDS_CONTROL_TOKEN=$(kubectl -n <namespace-name> get secret \\\n  ids-control-token -o jsonpath='{.data.token}' | base64 -d)\ncurl -sS -X POST http://localhost:9200/v1/api-keys \\\n  -H \"Authorization: Bearer $IDS_CONTROL_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"my-agent-key\",\n    \"grants\": [\n      {\n        \"tenant\": \"default\",\n        \"product\": \"memory\",\n        \"resourceType\": \"mem-store\",\n        \"resourceId\": \"<store-id>\",\n        \"actions\": [\"read\", \"write\"]\n      }\n    ]\n  }'","section_id":"7-mint-an-agent-key"},{"id":"8-make-one-authenticated-call-ex0","language":"bash","code":"curl -sS -X POST \"http://localhost:9000/v1/stores/<store-id>/session-memory/events\" \\\n  -H \"Authorization: Bearer <agent-key>\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"sessionId\": \"session-001\",\n    \"actorId\": \"user-001\",\n    \"role\": \"USER\",\n    \"content\": [{\"text\": \"What is the capital of France?\"}],\n    \"createdAt\": \"2026-06-25T18:00:00Z\"\n  }'","section_id":"8-make-one-authenticated-call"},{"id":"evaluate-locally-ex0","language":"yaml","code":"apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: ollama\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: ollama\n  template:\n    metadata:\n      labels:\n        app: ollama\n    spec:\n      containers:\n        - name: ollama\n          image: ollama/ollama:<ollama-version>   # 0.13.3 or later\n          env:\n            - name: OLLAMA_CONTEXT_LENGTH\n              value: \"8192\"\n          command: [\"/bin/sh\", \"-c\"]\n          args: [\"ollama serve & until ollama list >/dev/null 2>&1; do sleep 1; done; ollama pull qwen2.5:3b; wait\"]\n          ports:\n            - containerPort: 11434\n          resources:\n            requests:\n              cpu: \"2\"\n              memory: 4Gi\n          volumeMounts:\n            - name: models\n              mountPath: /root/.ollama\n      volumes:\n        - name: models\n          emptyDir: {}\n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: ollama\nspec:\n  selector:\n    app: ollama\n  ports:\n    - port: 11434\n      targetPort: 11434","section_id":"evaluate-locally"},{"id":"evaluate-locally-ex1","language":"bash","code":"kubectl -n <namespace-name> apply -f ollama.yaml","section_id":"evaluate-locally"},{"id":"evaluate-locally-ex2","language":"yaml","code":"memory:\n  embedding:\n    provider: noop\n    models:\n      default_embedding_model: noop\n      dimensions: 384\n  embedders_connection_details:\n    noop:\n      protocol: noop\n  inference_providers:\n    ollama:\n      protocol: openai\n      endpoint:\n        base_url: http://ollama:11434/v1\n        timeout: 300s\n      http_client:\n        timeout: 300s\n  promote_session_memory:\n    strategies:\n      instruct:\n        llm:\n          provider: ollama\n          credentials:\n            type: static\n            api_key: ollama\n          models:\n            default_chat_model: qwen2.5:3b\ncontrolplane:\n  configData:\n    embedding:\n      dimensions: 384","section_id":"evaluate-locally"},{"id":"evaluate-locally-ex3","language":"yaml","code":"metadata:\n  urls: [\"redis://<metadata-redis-host>:6379\"]\ndatabases:\n  \"1\":\n    urls: [\"redis://<store-redis-host>:6379\"]\nbackground_jobs:\n  redis:\n    urls: [\"redis://<job-redis-host>:6379\"]\n  idempotency:\n    urls: [\"redis://<job-redis-host>:6379\"]","section_id":"evaluate-locally"}]}
{"schema_version":2,"id":"operate/iris/agent-memory/self-managed","title":"Self-managed Redis Agent Memory","url":"https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/","summary":"Deploy, configure, secure, and operate Redis Agent Memory on a self-managed Kubernetes cluster.","aliases":["/develop/ai/context-engine/agent-memory/self-managed/"],"tags":["docs","operate","iris"],"last_updated":"2026-10-21T18:51:10+03:00","children":[{"id":"operate/iris/agent-memory/self-managed/prerequisites","summary":"Review software, Redis, network, Secret, image, and sizing prerequisites for self-managed Redis Agent Memory.","title":"Self-managed Redis Agent Memory prerequisites","url":"https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/prerequisites/"},{"id":"operate/iris/agent-memory/self-managed/configuration","summary":"Configure the Redis Agent Memory Data Plane, Control Plane, and Identity Service from Helm values, or bring your own configuration files.","title":"Configuration","url":"https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/configuration/"},{"id":"operate/iris/agent-memory/self-managed/deploy","summary":"Install Redis Agent Memory with chart-rendered configuration and agent-key authentication, then make one authenticated call.","title":"Deploy Redis Agent Memory","url":"https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/deploy/"},{"id":"operate/iris/agent-memory/self-managed/authentication","summary":"Configure Redis Agent Memory self-managed Control Plane authentication, Data Plane auth modes, worker callbacks, and gateway integration.","title":"Authentication and authorization","url":"https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/authentication/"},{"id":"operate/iris/agent-memory/self-managed/api-examples","summary":"Use curl examples with the Redis Agent Memory self-managed Control Plane and Data Plane APIs.","title":"Self-managed API examples","url":"https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/api-examples/"},{"id":"operate/iris/agent-memory/self-managed/control-plane-api-reference","summary":"","title":"Redis Agent Memory Control Plane API reference","url":"https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/control-plane-api-reference/"},{"id":"operate/iris/agent-memory/self-managed/operations","summary":"Operate Redis Agent Memory with backups, secret rotation, updates, FIPS posture, and network policy.","title":"Operations","url":"https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/operations/"},{"id":"operate/iris/agent-memory/self-managed/reference","summary":"Review self-managed Redis Agent Memory configuration, troubleshooting guidance, and reference links.","title":"Configuration and troubleshooting","url":"https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/reference/"}],"page_type":"content","content_hash":"817512a5163d03249a6f30986de2da67469684c942c0ed2e021c9030a32e8e7d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Agent Memory provides persistent memory for AI agents and\napplications. Applications write conversation events and long-term memories to\nRedis Agent Memory, then query Redis Agent Memory for relevant context before calling an LLM.\n\nThis guide covers deployment, configuration, security, validation, API examples,\nand operations for self-managed Redis Agent Memory. To install, start with\n[Prerequisites](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/prerequisites), then follow\n[Deploy](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/deploy).\n\nThe [Redis Agent Memory API](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/api-reference)\nis the shared Data Plane API for Redis Cloud and self-managed deployments. The\n[Control Plane API reference](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/control-plane-api-reference)\ndocuments the self-managed admin endpoints for stores."},{"id":"what-you-are-deploying","title":"What you are deploying","role":"content","text":"The `redis-agent-memory` chart `0.8.0` deploys the Data Plane, workers, and Control Plane.\nInstall the `iris-identity-service` chart `0.10.0` as a separate release first, then connect\nMemory to it. Memory does not deploy Identity Service resources or include it as a subchart.\nYou provide the Redis databases. Follow [Deploy](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/deploy)\nto configure both releases.\n\n| Component | Purpose | Default service |\n| --- | --- | --- |\n| Redis Agent Memory Data Plane | Store-scoped runtime memory API. | `redis-agent-memory:9000` |\n| Redis Agent Memory worker | Background promotion, summarization, and forgetting work. | No public service |\n| Redis Agent Memory Control Plane | Admin API for creating and managing stores. | `redis-agent-memory-controlplane:9100` |\n| Identity Service (separate release) | Mints, rotates, revokes, and checks agent keys. | `ids-iris-identity-service:9200` for release `ids` |\n| Store Redis | Holds session memory, long-term memory, indexes, and TTL data. | Customer-provided |\n| Job Redis | Holds background work for Redis Agent Memory workers. | Customer-provided |\n| Metadata Redis | Holds Control Plane store records. The Identity Service keeps agent-key records in the Metadata Redis named in its own metadata Secret. | Customer-provided |"},{"id":"how-the-components-work-together","title":"How the components work together","role":"content","text":"The Data Plane handles runtime memory requests. The Control Plane handles store administration,\nand the Identity Service handles agent keys.\n\n| Flow | Caller | Service | Backing Redis |\n| --- | --- | --- | --- |\n| Store administration | Platform admin | Redis Agent Memory Control Plane | Metadata Redis |\n| Agent-key administration | Platform admin | Identity Service | Identity Service Metadata Redis |\n| Runtime memory requests | Agent, app, or gateway | Redis Agent Memory Data Plane | Store Redis |\n| Background memory processing | Redis Agent Memory worker | Redis Agent Memory Data Plane | Job Redis and Store Redis |\n\n1. Platform admins create stores with the Control Plane and mint agent keys with the Identity Service.\n1. The Control Plane keeps store records in Metadata Redis. The Identity Service keeps agent-key\n   records in its own Metadata Redis.\n1. Agents and applications call the Data Plane with a store ID and an agent key.\n1. The Data Plane checks each agent key by calling the Identity Service, then reads or writes memory\n   in Store Redis.\n1. Workers use Job Redis for background work and write generated memory to Store Redis."},{"id":"api-surfaces","title":"API surfaces","role":"content","text":"Memory data APIs are scoped to a store, the logical isolation boundary for memory data.\nService liveness and readiness endpoints are not store-scoped.\n\nWhen agent-key authentication is enabled, the grants on each agent key determine which stores and\nactions the key can access.\n\n| API surface | Endpoint prefix | Purpose |\n| --- | --- | --- |\n| Session memory | `/v1/stores/{storeId}/session-memory` | Ordered conversation events, session metadata, and session lifecycle operations. |\n| Long-term memory | `/v1/stores/{storeId}/long-term-memory` | Searchable facts, preferences, summaries, and custom memory records. |\n| Namespaces | `/v1/stores/{storeId}/namespaces` | Namespace resources within a store. |\n| Model Context Protocol (MCP) | `/v1/stores/{storeId}/mcp` | The MCP streamable-HTTP endpoint for a store. |\n| Store health | `/v1/stores/{storeId}/health` | Read-only health for store-scoped features. |\n| Service health | `/health/liveness`, `/health/readiness` | Process liveness and readiness to serve requests; neither requires an agent key. |\n| Control Plane | `/v1/stores`, `/v1/detectors` | Self-managed administration for stores, and the catalog of built-in sensitive-data detectors. |\n| Identity Service | `/v1/api-keys` | Self-managed administration for agent keys. |\n\nSession memory keeps conversation continuity within a session. Long-term memory\nprovides searchable context across sessions.\n\nLong-term memory `memoryType` is an open identifier. When omitted on create,\nRedis Agent Memory stores the record as `semantic`; built-in names include `semantic`,\n`episodic`, `message`, and `session_summary_view`."}],"examples":[]}
{"schema_version":2,"id":"operate/iris/agent-memory/self-managed/operations","title":"Operations","url":"https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/operations/","summary":"Operate Redis Agent Memory with backups, secret rotation, updates, FIPS posture, and network policy.","aliases":["/develop/ai/context-engine/agent-memory/self-managed/operations/"],"tags":["docs","operate","iris"],"last_updated":"2026-10-21T18:51:10+03:00","page_type":"content","content_hash":"6b5177c3ee94adeea89999856e5c1353ea0710beb5af9ad3c3b3fb2eca9ad1ec","sections":[{"id":"backups","title":"Backups","role":"overview","text":"- Back up Metadata Redis. Losing it removes Control Plane store records.\n- Back up the Identity Service Metadata Redis, which may be a separate\n  database. Losing it removes agent-key records.\n- Back up Store Redis according to the customer's memory-retention policy.\n- Back up Job Redis if background job replay or delayed-job preservation is\n  required by the deployment's recovery policy.\n- Back up any external secret manager material used to recreate Kubernetes\n  Secrets.\n- For Job Redis, use persistent storage where supported and a non-volatile /\n  `noeviction` policy. OOM can still lose jobs or leave worker state invalid;\n  capacity alerts and compatibility checks should make that caveat visible.\n- For Metadata Redis and the Identity Service Metadata Redis, use persistent\n  storage and an eviction policy that does not evict store or agent-key records\n  under memory pressure."},{"id":"secret-rotation","title":"Secret rotation","role":"content","text":"Use the [Identity Service port-forward and control token](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/deploy#7-mint-an-agent-key)\nfrom the deployment procedure to rotate agent keys:\n\n[code example]\n\nThe response contains the new token. Store it immediately; tokens are returned\nonly when a key is minted or rotated.\n\nRotation returns a new `keyId` with the new token. Use the new `keyId` for later\nupdates, rotations, and revocations. The old key stays valid for `graceSeconds`,\nwhich defaults to 3600 and has a chart maximum of 604800\n(`apiKeys.maxRotateGraceSeconds` on the Identity Service release). The response adds\n`oldExpiresAt`, the time the old key stops working. Revoking the old `keyId`\nends its grace period early. For the full request and response, see\n[API examples](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/api-examples#identity-service-api-examples).\n\nRotate the Control Plane admin token by updating `redis-agent-memory-controlplane-admin-token`.\nThe Control Plane reads the token on use, so changing the token value does not\nrequire a Control Plane redeploy.\n\n[code example]\n\nThe runtime credential and Control Plane internal token are shared by both releases.\nWhen rotating either, keep the values equal in every namespace that mounts them, update the\ncorresponding checksums, and roll the affected workloads on both releases:\n\n| Credential | Memory checksum value | Identity Service checksum value |\n| --- | --- | --- |\n| `memory-dp-credential` | `identityService.credential.existingSecretChecksum` | `existingSecretChecksum` on the matching `runtime.serviceCredentials[]` entry |\n| `memory-internal-token` | `controlplane.internalToken.existingSecretChecksum` | `productValidation.products.memory.credential.existingSecretChecksum` |\n\nFor example, rotate `memory-dp-credential` in the walkthrough's namespace:\n\n[code example]\n\nIn the walkthrough, `memory-dp` is entry `0` in `runtime.serviceCredentials`. With another list,\nupdate the matching entry. Persist the checksum in both values files for later upgrades, and\nverify the affected Deployments are Ready. For the internal token, use the corresponding paths\nin the table. For multiple namespaces, provision the new token value in each namespace.\n\nCoordinate rotation: mismatched runtime credentials reject authentication, and mismatched\ninternal tokens prevent grant validation. Identity Service's operator control token is separate;\nits checksum is `control.existingSecretChecksum` on that release.\n\nRotate the Redis Agent Memory license by updating the license Secret and changing\n`license.existingSecretChecksum` so Helm rolls the Data Plane, worker, and\nControl Plane pods.\nRedis Agent Memory reads and validates the license file during process startup; updating only\nthe Secret data is not sufficient.\n\n[code example]\n\nCalculate the new SHA-256 checksum. This value is used by Helm values to roll\npods after the license Secret changes; it is not used to validate Secret\nintegrity.\n\n**Linux:**\n\n[code example]\n\n**macOS:**\n\n[code example]\n\n\n\n[code example]\n\nApply the updated values and verify the workloads rolled:\n\n[code example]\n\nFor immutable license Secrets, create a new Secret name instead, then update\nboth `license.existingSecret` and `license.existingSecretChecksum`.\n\nConfig that the chart renders from your values rolls the pods on its own when\nyou run `helm upgrade`. If you bring your own config Secrets, rotate the shared\nData Plane config by updating the config Secret and changing\n`config.existingSecretChecksum`, and rotate the Control Plane config by updating\nthe config Secret and changing `controlplane.config.existingSecretChecksum`.\n\nIf you change an overlay Secret (`secrets.*`) in place, restart the\ndeployments, because the chart does not track the Secret contents:\n\n[code example]"},{"id":"updates","title":"Updates","role":"content","text":"Memory and Identity Service have independent release versions. Upgrade each with its own\nvalues file and verify their shared connection contract afterward. A Memory upgrade does not\nupgrade Identity Service. The published pair documented here is Memory `0.8.0` and Identity\nService `0.10.0`.\n\nFor updates within a supported release path:\n\n1. Update chart version and image tags.\n2. Recalculate Secret checksums for changed files.\n3. Run `helm upgrade`.\n4. Verify pod rollout and health endpoints.\n\nExample:\n\n[code example]\n\nOn small clusters, avoid `--atomic` unless the timeout and capacity are known to\nbe sufficient."},{"id":"upgrade-from-a-bundled-identity-service","title":"Upgrade from a bundled Identity Service","role":"content","text":"Direct `helm upgrade` from an earlier chart with bundled Identity Service, including Memory\n`0.7.0`, to `0.8.0` is unsupported. Use a fresh installation. The following manual migration\noutline is untested guidance, not a supported upgrade procedure:\n\n1. Back up store and agent-key metadata. Preserve the existing runtime credential, Control Plane\n   internal token, metadata Redis addresses, and key namespaces.\n2. Provision the shared Secrets with those existing values and configure a standalone Identity\n   Service to use the same agent-key metadata. Do not let two independent releases manage one Secret.\n3. Retain the tenant used by existing stores and keys. Earlier admin-token stores used tenant `1`;\n   set `tenantId` and `runtime.apiKeyTenant.singleTenantID` accordingly, or migrate metadata first.\n4. Plan the replacement of the old Memory release and removal of bundled resources with Redis.\n   Keep backups and retained Secrets until the new installation and existing keys are verified."},{"id":"chart-tests","title":"Chart tests","role":"content","text":"The chart can render optional `helm test` resources when `tests.enabled=true`.\n\nEnable and run the basic chart test:\n\n[code example]\n\nTo run the API smoke test, also provide a configured store ID:\n\n[code example]\n\nUse the smoke test only for auth-disabled Data Plane deployments or for\nenvironments where the in-cluster test path is allowed."},{"id":"fips-oriented-posture","title":"FIPS-oriented posture","role":"content","text":"Both releases support an opt-in Federal Information Processing Standards (FIPS) posture.\nSet `security.profile: fips` or its fallback `global.security.profile: fips` on each release.\nA value passed only to Memory does not configure Identity Service.\n\nIn `ram-values.yaml`:\n\n[code example]\n\nIn `values-ids.yaml`:\n\n[code example]\n\nThe Memory chart refuses `identityService.allowInsecureTransport: true` under FIPS.\nIdentity Service refuses to start with a non-HTTPS product validator URL under FIPS.\nNeither chart terminates Transport Layer Security (TLS). Your hosting environment supplies\nboth endpoints through a service mesh, ingress, or TLS proxy. Memory trusts its runtime\nendpoint through its own `tls.caCertSecret`; Identity Service trusts the validator endpoint\nthrough that release's `tls.caCertSecret`, each in addition to system certificate authorities.\n\nMemory also ships a `values-fips.yaml` overlay. Apply it only to Memory, and configure the\nIdentity Service release's posture separately:\n\n[code example]\n\nBoth charts set `GODEBUG=fips140=on` on their application containers and enable\nFIPS-oriented runtime checks.\n\nThis is not a formal FIPS 140 compliance or validation claim. Treat it as a\ndeployment posture and guardrail that must still be reviewed against the\ncustomer's compliance boundary.\n\nWhen the posture is active, the Data Plane and worker reject config that:\n\n- enables `skip_verify` on outbound HTTP clients;\n- sets `allow_insecure_transport` on any authentication block, even a disabled one; or\n- uses non-`rediss://` URLs for Redis connections covered by the posture.\n\nThe Control Plane runs under the same posture and rejects non-`rediss://`\n`metadata.urls` or `databases.\"1\".urls`.\n\nThe Identity Service sends the Control Plane internal token on every grant validation. Under\nthe posture it refuses to start unless each product validator URL uses `https://`.\nConfigure that URL on the Identity Service release, not under Memory's `identityService`.\n\nThe Redis Agent Memory API listener itself speaks HTTP inside the cluster. Edge TLS termination\nis owned by the hosting environment, such as ingress, service mesh, or external\nload balancer. Outbound TLS to Redis, embedding providers, LLM providers, and\nworker callback endpoints is configured through Redis Agent Memory config and is covered by the\nposture checks.\n\nVerify the runtime posture with:\n\n[code example]\n\nUse the chart-generated Deployment to enable the FIPS-oriented posture. Do not\noverride the container command to configure it."},{"id":"network-policy","title":"Network policy","role":"content","text":"For auth-disabled Data Plane deployments, restrict access to trusted callers.\nFor agent-key deployments behind a gateway, prevent direct bypass paths unless\nthe direct caller also has a valid Redis Agent Memory credential.\n\nThe chart includes `networkpolicy.reference.yaml` at the chart root as a\nreference manifest. Get it with\n`helm pull redis-ai/redis-agent-memory --version 0.8.0 --untar`. It is not templated because allowed callers are\nenvironment-specific.\n\nCustomize the placeholders before applying it:\n\n- `<namespace>`: namespace where Redis Agent Memory is installed.\n- `<release-name>`: Helm release name. This guide uses `redis-agent-memory`.\n  `fullnameOverride` fixes Memory's default resource names to `redis-agent-memory`.\n  Naming overrides change rendered resource names, but the\n  `app.kubernetes.io/instance` selector remains the Helm release name.\n- `<caller-namespace>` and caller pod labels: ingress controller, service mesh\n  gateway, application pod, or approved internal caller allowed to call Redis Agent Memory.\n\nThe reference policy default-denies ingress to Redis Agent Memory chart pods, then allows TCP\ntraffic to server pods on port `9000` from approved callers and the worker\nDeployment. It also includes a Control Plane stanza for port `9100`, which\nalways applies.\n\nThe reference includes Control Plane ingress from the separate Identity Service release.\nSet `<identity-service-namespace>` and `<identity-service-release-name>` to its namespace and\nrelease name (`ids` in the walkthrough). It does not manage ingress for Identity Service itself.\nUnder default-deny, allow Data Plane requests to that release on port `9200`, and Identity\nService requests to the Control Plane on port `9100`. If TLS proxies sit between them, allow\nthe proxy ports and actual source pods too. Add corresponding egress rules when egress is restricted.\n\nReview the manifest against the customer's CNI,\ningress path, and service mesh behavior before production use."}],"examples":[{"id":"secret-rotation-ex0","language":"bash","code":"IDS_URL=http://localhost:9200\ncurl -sS -X POST \"$IDS_URL/v1/api-keys/<key-id>/rotate?graceSeconds=3600\" \\\n  -H \"Authorization: Bearer $IDS_CONTROL_TOKEN\"","section_id":"secret-rotation"},{"id":"secret-rotation-ex1","language":"bash","code":"kubectl -n <namespace-name> create secret generic redis-agent-memory-controlplane-admin-token \\\n  --from-literal=token='<new-admin-token>' \\\n  --dry-run=client \\\n  -o yaml | kubectl apply -f -","section_id":"secret-rotation"},{"id":"secret-rotation-ex2","language":"bash","code":"umask 077\nopenssl rand -hex 32 | tr -d '\\n' > memory-dp-token\nkubectl -n <namespace-name> create secret generic memory-dp-credential \\\n  --from-file=token=./memory-dp-token --save-config --dry-run=client -o yaml | kubectl apply -f -\nRUNTIME_CREDENTIAL_CHECKSUM=\"$(shasum -a 256 ./memory-dp-token | awk '{print $1}')\"\n\nhelm upgrade ids redis-ai/iris-identity-service \\\n  --version 0.10.0 \\\n  --namespace <namespace-name> \\\n  -f values-ids.yaml \\\n  --set-string \"runtime.serviceCredentials[0].existingSecretChecksum=$RUNTIME_CREDENTIAL_CHECKSUM\"\nhelm upgrade redis-agent-memory redis-ai/redis-agent-memory \\\n  --version 0.8.0 \\\n  --namespace <namespace-name> \\\n  -f ram-values.yaml \\\n  --set-string \"identityService.credential.existingSecretChecksum=$RUNTIME_CREDENTIAL_CHECKSUM\"","section_id":"secret-rotation"},{"id":"secret-rotation-ex3","language":"bash","code":"kubectl -n <namespace-name> create secret generic ram-license \\\n  --from-file=license=./license \\\n  --dry-run=client \\\n  -o yaml | kubectl apply -f -","section_id":"secret-rotation"},{"id":"secret-rotation-ex4","language":"bash","code":"LICENSE_CHECKSUM=\"$(sha256sum ./license | awk '{print $1}')\"","section_id":"secret-rotation"},{"id":"secret-rotation-ex5","language":"bash","code":"LICENSE_CHECKSUM=\"$(shasum -a 256 ./license | awk '{print $1}')\"","section_id":"secret-rotation"},{"id":"secret-rotation-ex6","language":"yaml","code":"license:\n  existingSecret: ram-license\n  existingSecretChecksum: \"<new-license-checksum>\"","section_id":"secret-rotation"},{"id":"secret-rotation-ex7","language":"bash","code":"helm upgrade redis-agent-memory redis-ai/redis-agent-memory \\\n  --version <chart-version> \\\n  --namespace <namespace-name> \\\n  -f ram-values.yaml\n\nkubectl -n <namespace-name> rollout status deploy/redis-agent-memory\nkubectl -n <namespace-name> rollout status deploy/redis-agent-memory-worker\nkubectl -n <namespace-name> rollout status deploy/redis-agent-memory-controlplane","section_id":"secret-rotation"},{"id":"secret-rotation-ex8","language":"bash","code":"kubectl -n <namespace-name> rollout restart deployment","section_id":"secret-rotation"},{"id":"updates-ex0","language":"bash","code":"helm upgrade redis-agent-memory redis-ai/redis-agent-memory \\\n  --version <chart-version> \\\n  --namespace <namespace-name> \\\n  -f ram-values.yaml","section_id":"updates"},{"id":"chart-tests-ex0","language":"bash","code":"helm upgrade --install redis-agent-memory redis-ai/redis-agent-memory \\\n  --version <chart-version> \\\n  --namespace <namespace-name> \\\n  -f ram-values.yaml \\\n  --set tests.enabled=true\n\nhelm test redis-agent-memory --namespace <namespace-name>","section_id":"chart-tests"},{"id":"chart-tests-ex1","language":"bash","code":"helm upgrade --install redis-agent-memory redis-ai/redis-agent-memory \\\n  --version <chart-version> \\\n  --namespace <namespace-name> \\\n  -f ram-values.yaml \\\n  --set tests.enabled=true \\\n  --set tests.smoke.enabled=true \\\n  --set tests.smoke.storeId=<store-id>\n\nhelm test redis-agent-memory --namespace <namespace-name> --logs","section_id":"chart-tests"},{"id":"fips-oriented-posture-ex0","language":"yaml","code":"security:\n  profile: fips\ndataplane:\n  identityServiceBaseURL: https://<identity-service-tls-endpoint>\ntls:\n  caCertSecret: ram-ca","section_id":"fips-oriented-posture"},{"id":"fips-oriented-posture-ex1","language":"yaml","code":"security:\n  profile: fips\nproductValidation:\n  products:\n    memory:\n      baseURL: https://<control-plane-tls-endpoint>\n      credential:\n        existingSecret: memory-internal-token\ntls:\n  caCertSecret: ram-ca","section_id":"fips-oriented-posture"},{"id":"fips-oriented-posture-ex2","language":"bash","code":"helm pull redis-ai/redis-agent-memory --version 0.8.0 --untar\nhelm upgrade redis-agent-memory redis-ai/redis-agent-memory \\\n  --version 0.8.0 \\\n  --namespace <namespace-name> \\\n  -f ram-values.yaml \\\n  -f redis-agent-memory/values-fips.yaml\n\nhelm upgrade ids redis-ai/iris-identity-service \\\n  --version 0.10.0 \\\n  --namespace <namespace-name> \\\n  -f values-ids.yaml","section_id":"fips-oriented-posture"},{"id":"fips-oriented-posture-ex3","language":"bash","code":"kubectl -n <namespace-name> get deploy redis-agent-memory \\\n  -o jsonpath='{.spec.template.spec.containers[0].env[?(@.name==\"GODEBUG\")].value}'\n\nkubectl -n <namespace-name> logs deploy/redis-agent-memory | grep -i 'FIPS security profile'","section_id":"fips-oriented-posture"}]}
{"schema_version":2,"id":"operate/iris/agent-memory/self-managed/prerequisites","title":"Self-managed Redis Agent Memory prerequisites","url":"https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/prerequisites/","summary":"Review software, Redis, network, Secret, image, and sizing prerequisites for self-managed Redis Agent Memory.","aliases":["/develop/ai/context-engine/agent-memory/self-managed/prerequisites/","/develop/ai/context-engine/agent-memory/self-managed/plan-deployment/","/operate/iris/agent-memory/self-managed/plan-deployment/"],"tags":["docs","operate","iris"],"last_updated":"2026-10-21T18:51:10+03:00","page_type":"content","content_hash":"c1ca077d103e68fa18acc6368869c27d4880504171f447ef8c74564ce104e9d0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Agent Memory is distributed as container images on Docker Hub plus a Helm\nchart in the Redis AI Helm repository. The chart deploys the Redis Agent Memory Data\nPlane, Redis Agent Memory workers, and the Redis Agent Memory Control Plane. Install Identity\nService separately with the `iris-identity-service` chart `0.10.0` before connecting Memory chart `0.8.0`.\n\nYou provide the Redis databases, provider credentials, Kubernetes exposure, and\nlicense material used by the deployment.\n\n> [!NOTE]\n> This guide is for system administrators deploying Redis Agent Memory on a self-managed\n> Kubernetes cluster."},{"id":"what-you-need","title":"What you need","role":"content","text":"| Item | Where it comes from |\n| ---- | ------------------- |\n| Container images | Docker Hub: `redislabs/agent-memory`, `redislabs/agent-memory-control-plane`, and `redislabs/iris-identity-service` |\n| Helm charts | `redis-agent-memory` `0.8.0` and `iris-identity-service` `0.10.0` in the Redis AI Helm repository |\n| Redis databases | You provide Store Redis, Job Redis, and Metadata Redis |\n| License key | Contact your Redis representative or [contact sales](https://redis.io/contact/). |\n| Provider credentials | You provide embedding provider credentials and, when worker features are enabled, promotion or summarization LLM credentials |"},{"id":"required-software","title":"Required software","role":"content","text":"| Software | Minimum version | Purpose |\n| -------- | --------------- | ------- |\n| Kubernetes | 1.23+ | Orchestration |\n| kubectl | 1.23+ | Kubernetes CLI |\n| Helm | 3.x | Package manager |"},{"id":"redis-databases","title":"Redis databases","role":"content","text":"The Helm chart does not deploy Redis databases. Provision the Redis databases\noutside the Redis Agent Memory chart and pass their URLs in the overlay Secret.\nThe Data Plane and the Control Plane both read the overlay, so they use the same\nMetadata Redis and Store Redis.\n\nStore Redis must support Search and JSON capabilities because Redis Agent Memory\ncreates JSON and vector search indexes for memory data. Job Redis and Metadata\nRedis do not need those capabilities when they are deployed as separate Redis\ndatabases.\n\n| Redis database | Required | Key in the overlay Secret | Purpose |\n| --- | --- | --- | --- |\n| Store Redis | Always | `databases.\"1\".urls` | Session memory JSON, long-term memory hashes, RediSearch indexes, vectors, and TTL-managed data. |\n| Metadata Redis | Always | `metadata.urls`, and optionally `metadata.namespace` (default `iris:memory`) | Store records. |\n| Job Redis | Always | `background_jobs.redis.urls` and, when execution idempotency is enabled, `background_jobs.idempotency.urls` | Background work, retry state, delayed jobs, and execution markers. The two settings can use the same Redis. |\n\nThe Identity Service reads its Metadata Redis from `metadata.urls` in its own metadata Secret and\nkeeps agent-key records there. The Data Plane checks agent keys by calling the Identity Service.\n\nFor a lab deployment, the Redis roles can point to the same Redis endpoint if it has the required\nmodules and capacity. For production, separate Store Redis, Job Redis, and Metadata Redis when\npossible so memory data, background work, and control metadata can be scaled, backed up, and\noperated independently.\n\nFor Job Redis, use a non-evicting policy such as `noeviction` or\n`volatile-ttl`."},{"id":"metadata-redis-durability","title":"Metadata Redis durability","role":"content","text":"Metadata Redis is small compared with Store Redis, but it is operationally\ncritical. Use persistent storage, Redis authentication, network isolation, and\nTLS where required.\n\nIn FIPS-oriented deployments, Redis URLs covered by the posture must use\n`rediss://`. The Identity Service and Control Plane also need `https://`\nendpoints that your hosting environment provides. See\n[FIPS-oriented posture](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/operations#fips-oriented-posture). Avoid eviction of metadata keys. Losing Metadata Redis removes Control\nPlane store records, and losing the Identity Service Metadata Redis removes agent-key records."},{"id":"network-access","title":"Network access","role":"content","text":"- **Connected install:** the cluster must be able to pull images from\n  `docker.io` and reach the Redis AI Helm repository.\n- **Air-gapped install:** mirror the images into an internal registry and use a\n  chart package or locally downloaded chart.\n- **Runtime access:** Redis Agent Memory pods must reach the Redis databases and any\n  embedding or LLM provider endpoints used by the deployment.\n- **Identity Service:** the Data Plane must reach its runtime endpoint. Identity Service must\n  reach the Memory Control Plane's grant validator and its own Metadata Redis. Provide TLS\n  endpoints and trust for both service hops; see [Deploy](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/deploy).\n- **Data Plane exposure:** use NetworkPolicy, ingress, gateway, service mesh,\n  private load balancer, or equivalent controls to restrict API access."},{"id":"credentials-and-secrets","title":"Credentials and Secrets","role":"content","text":"When `config.render: true` is set, as in the walkthrough, the chart renders configuration from\nHelm values. This option defaults to `false`. Credentials stay in an overlay Secret. Create the following Secrets for the\n[two-release installation](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/deploy#2-create-secrets):\n\n| Secret | Default key | Holds |\n| --- | --- | --- |\n| License Secret | `license` | The Redis Agent Memory license key. |\n| Overlay Secret | `overlay.yaml` | Redis URLs and provider credentials. |\n| Identity Service metadata Secret | `metadata.yaml` | The Identity Service's Metadata Redis URL. |\n\nAlso create `memory-dp-credential` and `memory-internal-token`, shared by both releases,\nand `ids-control-token`, read only by Identity Service. The walkthrough supplies a `ram-ca`\ncertificate bundle for service trust. Memory generates its Control Plane admin token unless\nyou provide one. See [Credentials](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/authentication#credentials).\n\nTo supply a complete configuration file in your own Secret instead, see\n[Bring-your-own (BYO) configuration](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/configuration#bring-your-own-byo-configuration)."},{"id":"release-artifacts-and-image-tags","title":"Release artifacts and image tags","role":"content","text":"The published Memory chart `0.8.0` defaults `image.tag` and `controlplane.image.tag` to `0.8.0`.\nIdentity Service chart `0.10.0` defaults its image to its own application version, `0.10.0`.\nSet tags explicitly when pinning images or using a mirror. The releases have independent versions.\n\nBoth charts are published at `https://helm.redis.io/ai` as `redis-ai/redis-agent-memory`\nand `redis-ai/iris-identity-service`. Their standard images are\n`redislabs/agent-memory:0.8.0`, `redislabs/agent-memory-control-plane:0.8.0`, and\n`redislabs/iris-identity-service:0.10.0`."},{"id":"air-gapped-and-private-registry-installs","title":"Air-gapped and private registry installs","role":"content","text":"Mirror these images into your internal registry:\n\n| Image | Needed |\n| --- | --- |\n| `redislabs/agent-memory:<ram-version>` | Always (Data Plane and workers) |\n| `redislabs/agent-memory-control-plane:<ram-version>` | Always |\n| `redislabs/iris-identity-service:<ids-version>` | On the separate Identity Service release, required for default agent-key authentication |\n| `replicated/troubleshoot:0.131.0` | Support-bundle health checks, on by default (`supportPackage.*`) |\n| `hibiken/asynqmon:0.7.2` | Optional, with `controlplane.queueMonitor` |\n| `python:3.13-alpine` | Optional, with `tests.smoke` |\n| `curlimages/curl:8.17.0` | Optional, for `helm test` on the Identity Service release (`tests.image` in `values-ids.yaml`) |\n\nFor this release, `<ram-version>` is `0.8.0` and `<ids-version>` is `0.10.0`.\nInspect each package with `helm show chart redis-ai/redis-agent-memory --version 0.8.0`\nand `helm show chart redis-ai/iris-identity-service --version 0.10.0`. Memory does not pin\nor install the Identity Service chart.\n\n[code example]\n\nIf the registry requires authentication, create an image pull Secret:\n\n[code example]\n\nAdd the registry settings to `ram-values.yaml`:\n\n[code example]\n\nConfigure the separate Identity Service release in `values-ids.yaml`:\n\n[code example]\n\nEach release reads its own `imagePullSecrets`, falling back to `global.imagePullSecrets`.\nValues passed to Memory do not configure Identity Service. Omit pull Secrets if the registry\ndoes not require authentication.\n\nFor air-gapped deployments, also set:\n\n[code example]\n\nMemory's `airgap.enabled: true` checks Memory's own image repositories. It does not validate\nthe separate Identity Service release. Mirror that release's image and chart dependencies\nseparately, and confirm that every configured repository is reachable from the cluster."},{"id":"system-requirements","title":"System requirements","role":"content","text":"Default values for Memory and the separate Identity Service chart:\n\n| Component | Default | Purpose |\n| --------- | ------- | ------- |\n| Redis Agent Memory server | 2 replicas with autoscaling enabled and a minimum of 2 | Data Plane API traffic |\n| Redis Agent Memory worker | 2 replicas with autoscaling enabled and a minimum of 2 | Background promotion, summarization, and forgetting jobs |\n| Redis Agent Memory Control Plane | 1 replica | Admin API for stores |\n| Identity Service | 1 replica; requests `100m` CPU and `128Mi` memory, limits `500m` CPU and `512Mi` memory | Agent-key minting, rotation, revocation, and checks |\n\nDuring a rolling update, Kubernetes may temporarily run old and new pods at the\nsame time. A small two-node test cluster can run out of CPU during install or\nupgrade.\n\nFor realistic validation, use at least three nodes or enough CPU headroom for\nthe maximum rolling-update overlap.\n\nFor a constrained lab cluster, reduce replicas and autoscaling explicitly:\n\n[code example]\n\nDo not use reduced replica counts as the production HA recommendation."},{"id":"helm-values-to-review","title":"Helm values to review","role":"content","text":"The walkthroughs use `redis-agent-memory` as the Helm release name. Resource names are fixed to\n`redis-agent-memory*` by `fullnameOverride`, whatever the release name, so install one release per\nnamespace.\n\n| Area | Values | Use when |\n| --- | --- | --- |\n| Image | `image.repository`, `image.tag`, `imagePullSecrets` | Selecting a release, private registry, or mirrored image. |\n| Air-gapped installs | `airgap.enabled` | Validating a disconnected or private-registry install. |\n| Chart-rendered configuration | `config.render`, `shared`, `memory`, `controlplane.configData` | Setting the Data Plane and Control Plane configuration from Helm values. |\n| Credentials overlay | `secrets.*` | Naming the overlay Secret and its key, or layering more overlay Secrets. |\n| API server capacity | `server.resources`, `server.autoscaling.*` | Tuning request capacity or memory footprint. |\n| Worker capacity | `worker.resources`, `worker.autoscaling.*` | Tuning background job throughput. |\n| Scheduling | `server.nodeSelector`, `worker.nodeSelector`, `server.affinity`, `worker.affinity`, `server.tolerations`, `worker.tolerations` | Controlling pod placement. |\n| Networking | `service.type`, `ingress.*` | Exposing Redis Agent Memory outside the cluster. |\n| Naming | `fullnameOverride` | Keeping resource names at `redis-agent-memory*`. |\n| Service account | `serviceAccount.*` | Matching customer namespace security policy. |\n| Worker authentication | `workerAuth.*`, `worker.serviceAccount.*`, `worker.serviceAccount.token.*` | Giving Redis Agent Memory workers a Kubernetes projected service-account token for authenticated Data Plane callbacks. |\n| Private certificate authority (CA) | `tls.caCertSecret` | Trusting a private CA for outbound connections, such as the cluster CA for [worker identity](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/deploy#prepare-worker-identity-for-your-platform). |\n| Secret rollouts | `license.existingSecretChecksum`, `config.existingSecretChecksum`, `controlplane.config.existingSecretChecksum` | Rolling pods after externally managed Secret changes. |\n| Control Plane | `controlplane.image.*`, `controlplane.configData`, `controlplane.adminToken.*` | Mirroring the Control Plane image, setting its configuration, or bringing your own admin token. |\n| Control Plane internal token | `controlplane.internalToken.*` | Bringing your own token for Identity Service checks against the Control Plane. |\n| Identity Service connection | `dataplane.identityServiceBaseURL`, `identityService.credential.*`, `identityService.allowInsecureTransport` | Connecting to the separately installed runtime endpoint. The credential Secret is registered on that release. |\n| Identity Service configuration | Separate `values-ids.yaml`: `metadata`, `control`, `runtime`, `productValidation`, `subjectGrants` | Configuring the Identity Service release, not Memory. |\n| Shared fallbacks | `global.security.profile`, `global.imagePullSecrets`, `global.tls.caCertSecret` | Providing fallback values within each release. Pass shared values to both releases; local values take precedence. |\n| Security profile | `security.profile` or `global.security.profile` | Selecting the [FIPS-oriented posture](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/operations#fips-oriented-posture); configure it separately on each release. |\n| Support bundles | `supportPackage.*` | Configuring support-bundle collection and its health checks. |\n| Preflight checks | `preflight.*` | Rendering the preflight checks spec as a ConfigMap. |\n| Helm tests | `tests.*` | Running [chart tests](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/operations#chart-tests) with `helm test`. |\n\nDo not use floating image tags in production."}],"examples":[{"id":"air-gapped-and-private-registry-installs-ex0","language":"bash","code":"docker pull redislabs/agent-memory:<ram-version>\ndocker tag redislabs/agent-memory:<ram-version> \\\n  registry.example.com/redislabs/agent-memory:<ram-version>\ndocker push registry.example.com/redislabs/agent-memory:<ram-version>\n\ndocker pull redislabs/agent-memory-control-plane:<ram-version>\ndocker tag redislabs/agent-memory-control-plane:<ram-version> \\\n  registry.example.com/redislabs/agent-memory-control-plane:<ram-version>\ndocker push registry.example.com/redislabs/agent-memory-control-plane:<ram-version>\n\ndocker pull redislabs/iris-identity-service:<ids-version>\ndocker tag redislabs/iris-identity-service:<ids-version> \\\n  registry.example.com/redislabs/iris-identity-service:<ids-version>\ndocker push registry.example.com/redislabs/iris-identity-service:<ids-version>","section_id":"air-gapped-and-private-registry-installs"},{"id":"air-gapped-and-private-registry-installs-ex1","language":"bash","code":"kubectl -n <namespace-name> create secret docker-registry ram-registry \\\n  --docker-server=registry.example.com \\\n  --docker-username=<username> \\\n  --docker-password=<password>","section_id":"air-gapped-and-private-registry-installs"},{"id":"air-gapped-and-private-registry-installs-ex2","language":"yaml","code":"image:\n  repository: registry.example.com/redislabs/agent-memory\n\ncontrolplane:\n  image:\n    repository: registry.example.com/redislabs/agent-memory-control-plane\n\nglobal:\n  imagePullSecrets:\n    - name: ram-registry","section_id":"air-gapped-and-private-registry-installs"},{"id":"air-gapped-and-private-registry-installs-ex3","language":"yaml","code":"image:\n  repository: registry.example.com/redislabs/iris-identity-service\n  tag: \"0.10.0\"\ntests:\n  image: registry.example.com/curlimages/curl:8.17.0\nglobal:\n  imagePullSecrets:\n    - name: ram-registry","section_id":"air-gapped-and-private-registry-installs"},{"id":"air-gapped-and-private-registry-installs-ex4","language":"yaml","code":"airgap:\n  enabled: true","section_id":"air-gapped-and-private-registry-installs"},{"id":"system-requirements-ex0","language":"yaml","code":"server:\n  replicaCount: 1\n  autoscaling:\n    enabled: false\n\nworker:\n  replicaCount: 1\n  autoscaling:\n    enabled: false","section_id":"system-requirements"}]}
{"schema_version":2,"id":"operate/iris/agent-memory/self-managed/reference","title":"Configuration and troubleshooting","url":"https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/reference/","summary":"Review self-managed Redis Agent Memory configuration, troubleshooting guidance, and reference links.","aliases":["/develop/ai/context-engine/agent-memory/self-managed/reference/"],"tags":["docs","operate","iris"],"last_updated":"2026-10-21T18:51:10+03:00","page_type":"content","content_hash":"fa42b0924002ebf0e54c509b24212ae0141d5b3aeeda0592ee689e7bbaa6b54f","sections":[{"id":"configuration-reference","title":"Configuration reference","role":"configuration","text":"Use these files to configure a self-managed deployment:\n\n| File | Purpose |\n| --- | --- |\n| `ram-values.yaml` | Memory configuration and its connection to Identity Service. See [step 3](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/deploy#3-create-ram-valuesyaml). |\n| `values-ids.yaml` | Separate Identity Service release configuration, including runtime credential registration, tenant, and Memory grant validation. |\n| `overlay.yaml` | Redis addresses and provider credentials. The Data Plane, the workers, and the Control Plane all mount it. |\n| `ids-metadata.yaml` | Tells the Identity Service where Metadata Redis is. |\n| `license` | Redis Agent Memory license file provided by Redis. |"},{"id":"external-secret-managers","title":"External secret managers","role":"content","text":"If you use an external secret manager, expose the license, config, admin-token,\nIdentity Service metadata, Identity Service control token, Data Plane service credential,\nand Control Plane internal token material as Kubernetes Secrets. Set `existingSecret` on the\nowning release. Shared credentials must have matching contents in both releases' namespaces.\n\nDirect CSI file mounts that bypass Kubernetes Secrets are not supported for the\nRedis Agent Memory license, Data Plane config, Control Plane config, or Control Plane\nadmin-token paths.\n\nFor Secrets Store CSI Driver, use sync-to-Kubernetes-Secret\n(`SecretProviderClass.secretObjects`). If the Control Plane consumes\nCSI-synced Secrets, make sure a Control Plane pod also mounts the corresponding\n`SecretProviderClass` volume so the synced Secret exists while the pod runs."},{"id":"troubleshooting","title":"Troubleshooting","role":"errors","text":"| Symptom | Likely cause | Fix |\n| --- | --- | --- |\n| `helm search repo redis-ai/redis-agent-memory --versions` returns no results | Helm repo not added/updated, or the chart version has not been published to the repo yet | Run `helm repo add`, `helm repo update`, or install from the chart package provided by Redis. |\n| Rendering fails because `identityService` has an additional property | Memory values still use the removed bundled contract, such as `identityService.enabled` or `identityService.runtime` | Move service configuration to `values-ids.yaml`. Memory accepts only `identityService.credential` and `identityService.allowInsecureTransport`; remove `externalIdentityService` too. |\n| Rendering fails at `/externalIdentityService` with `'not' failed` | Values contain the removed external-mode block | Remove `externalIdentityService` and use `dataplane.identityServiceBaseURL` plus `identityService.credential` for the standalone connection. |\n| Rendering fails with `requires dataplane.identityServiceBaseURL` | Agent-key authentication or an `identity_service` subject needs the standalone runtime endpoint | Set `dataplane.identityServiceBaseURL` and `identityService.credential.existingSecret`. See [Deploy](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/deploy). |\n| Rendering fails with `requires identityService.credential.existingSecret` | The endpoint is configured but the runtime credential Secret is missing | Name the existing Secret also registered on Identity Service as `memory-dp`. |\n| Rendering fails with `must be https://` or `identityService.allowInsecureTransport=true is refused under the fips security profile` | The runtime endpoint uses HTTP without an allowed exception, or insecure transport is enabled under FIPS | Use an HTTPS endpoint. An HTTP exception requires `identityService.allowInsecureTransport: true` and is refused under FIPS. |\n| Rendering fails with `is chart-owned` for an introspection field | An endpoint, credential, or transport flag was hand-set under `memory.auth` or `shared.auth` | Use `dataplane.identityServiceBaseURL` and `identityService.credential` for chart-rendered configuration; cache settings remain configurable. |\n| Rendering or startup refuses `auth.method: none` | Authentication is disabled outside the development profile | Use agent-key authentication. For isolated development only, set both `memory.profile: dev` and `memory.auth.method: none`; supplied configuration files use `profile: dev` and `auth.method: none`. |\n| Docker pull fails for the configured image tag | Image tag is wrong or has not been published to the configured registry | Use the image tag listed for the release on Docker Hub or provided by Redis. |\n| Server and worker pods `CrashLoopBackOff`; log: `license validation failed; refusing to start: invalid license format` | `memory.license.license_path` is not set | See [CL-1](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/deploy#cl-1). |\n| Worker pods `CrashLoopBackOff`; log: `strategies.instruct.llm is required` | The `instruct.llm` block is missing | See [CL-2](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/deploy#cl-2). |\n| Pods `CrashLoopBackOff`; log: `provider \"<name>\" is not defined in inference_providers` | An `llm.provider` doesn't name an `inference_providers` entry | See [CL-3](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/deploy#cl-3). |\n| Worker callback cannot resolve the Data Plane host | `memory.dataplane_client.base_url` is wrong | See [CL-4](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/deploy#cl-4). |\n| Pod is stuck in `ImagePullBackOff` or `ErrImagePull` | Cluster cannot pull the configured image, image tag is wrong, registry requires credentials, or `imagePullSecrets` is missing/wrong | Verify `image.repository`, `image.tag`, registry reachability, and `imagePullSecrets`; use the Redis Agent Memory release image tag. Configure Identity Service's image and pull Secrets on its own release. See [CL-5](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/deploy#cl-5). |\n| Worker callbacks rejected with `401` | `auth.worker_identity` doesn't match the worker, or the Data Plane cannot fetch the cluster JWKS | See [CL-6](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/deploy#cl-6). |\n| `helm install --atomic --wait` times out and rolls back | Cluster is small or image pull/startup takes longer than Helm's default timeout | Install without `--atomic --wait`, or set a longer `--timeout` and ensure enough cluster capacity. |\n| Pods are pending during install or upgrade | CPU/memory capacity is insufficient for default replicas and rollout overlap | Add nodes/headroom or lower replicas for test deployments. |\n| Data Plane health fails | Pod not ready, config invalid, Redis unavailable, or license invalid | Check pod logs and call `/health/liveness` and `/health/readiness`. |\n| Data Plane fails with `agent_keys.introspection.base_url is required` | Bring-your-own (BYO) Data Plane config uses agent-key auth, the default when no auth method is set, without the `auth.agent_keys.introspection` block | Add the `auth.agent_keys.introspection` block. Only isolated development can use `auth.method: none` with `profile: dev`. See [Configuration](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/configuration#bring-your-own-byo-configuration). |\n| Data Plane fails with `removed metadata configuration keys: …` | Config uses metadata keys that version 0.7.0 removed, such as `metadata.source` or `metadata.live` | Configure `metadata.urls`, `metadata.namespace`, and `databases`, then create stores through the Control Plane. |\n| Pod fails to start in FIPS posture | A Redis URL is not `rediss://`, an outbound HTTP client uses `skip_verify: true`, an authentication block sets `allow_insecure_transport: true`, or the Identity Service logs `product_validation.<product>.base_url must be an https URL in FIPS profile` | Update Redis URLs, HTTP client config, authentication blocks, and the Identity Service product validator URL to satisfy the posture checks. The error names the exact config path. |\n| Agent receives `401` | Key is missing, revoked, expired, or wrong | Check `Authorization` / `X-Api-Key` and key status. |\n| Agent receives `503` | Identity Service is unreachable, or the Data Plane's miss budget for rejected keys is exhausted | Check that the Identity Service pod is Ready and reachable from the Data Plane. |\n| Agent receives `403` | Key exists but lacks the required store grant or action | Update grants with `PATCH http://localhost:9200/v1/api-keys/{keyId}` and the Identity Service control token. |\n| Store created by the Control Plane is not visible to the Data Plane | Control Plane and Data Plane point at different Metadata Redis URLs or namespaces | Make `metadata.urls` / `metadata.namespace` match on the Control Plane and the Data Plane. |\n| Agent key is rejected by the Data Plane | Data Plane cannot reach the Identity Service, the key was rotated or revoked, or the key secret is wrong | Check Identity Service reachability and, with BYO configuration only, `auth.agent_keys.introspection`. After a rotate or revoke, pods can disagree for up to 5 minutes. Send the latest credential returned by mint or rotate. |\n| Minting an agent key returns `400` for a grant | Unknown store ID, mismatched tenant, missing `product: \"memory\"`, or invalid grant shape | Create the store first, match `tenantId` with Identity Service's `runtime.apiKeyTenant.singleTenantID`, and verify its `productValidation.products.memory` endpoint and internal-token Secret match Memory. With BYO Control Plane config, also set `auth.internal_token.token_file: /etc/controlplane-onprem/internal/token`. Use `product: \"memory\"`, `resourceType: \"mem-store\"`, and `actions: [\"read\"]`, `[\"write\"]`, or both. |\n| Worker jobs fail after agent-key auth is enabled | Worker callback request has no accepted credential, the projected token is not mounted, or `auth.worker_identity` does not trust the worker subject/audience/issuer | Enable `workerAuth`, set `dataplane_client.auth.type=service_account_token`, and configure `auth.worker_identity.subjects` with the worker ServiceAccount subject and required store grants. |\n| Gateway path succeeds but direct external path also works | Data Plane is reachable outside the intended gateway path | Add NetworkPolicy, ingress, service mesh, or load balancer controls. |\n| NetworkPolicy blocks expected traffic | Placeholder namespace, release name, or caller selectors were not customized correctly | Check the Helm release label `app.kubernetes.io/instance`, caller namespace, and caller pod labels. |"},{"id":"references","title":"References","role":"related","text":"| Need | Reference |\n| --- | --- |\n| Helm chart values | `values.yaml` at the chart root (`helm pull redis-ai/redis-agent-memory --version 0.8.0 --untar`) |\n| Identity Service chart values | `helm show values redis-ai/iris-identity-service --version 0.10.0` |\n| FIPS values overlay | `values-fips.yaml` at the chart root |\n| NetworkPolicy reference | `networkpolicy.reference.yaml` at the chart root |\n| Chart README | `README.md` at the chart root |\n| Redis Agent Memory API reference | [Redis Agent Memory API](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/api-reference) |\n| Control Plane API reference | [Control Plane API reference](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/control-plane-api-reference) |\n| Redis Agent Memory Data Plane image tags | [Docker Hub: redislabs/agent-memory](https://hub.docker.com/r/redislabs/agent-memory/tags) |\n| Redis Agent Memory Control Plane image tags | [Docker Hub: redislabs/agent-memory-control-plane](https://hub.docker.com/r/redislabs/agent-memory-control-plane/tags) |\n| Identity Service image tags | [Docker Hub: redislabs/iris-identity-service](https://hub.docker.com/r/redislabs/iris-identity-service/tags) |"}],"examples":[]}
{"schema_version":2,"id":"operate/iris/agent-memory/view-service","title":"View and manage Agent Memory service","url":"https://redis.io/docs/latest/operate/iris/agent-memory/view-service/","summary":"View and manage your Agent Memory service in Redis Cloud.","aliases":["/operate/rc/context-engine/agent-memory/view-service/","/operate/rc/agent-memory/view-service/"],"tags":["docs","operate","iris"],"last_updated":"2026-10-05T16:11:44+01:00","page_type":"content","content_hash":"ebd5924eb1ae49551c3f8dbbbde59ee0420fd7b0d4edd87c5a7e9e8539ee4938","sections":[{"id":"overview","title":"Overview","role":"overview","text":"After you have [created your first Agent Memory service](https://redis.io/docs/latest/operate/iris/agent-memory/create-service), selecting **Agent Memory** from the Redis Cloud Console menu will take you to the **Agent Memory Services** page.\n\nThis page displays a list of all Agent Memory services associated with your account.\n\n![The Agent Memory service in the Agent Memory service list.](https://redis.io/docs/latest/images/rc/agent-memory-service-list.png)\n\nSelect your Agent Memory service from the list to view the service's details."},{"id":"configuration-tab","title":"Configuration tab","role":"configuration","text":"The **Configuration** tab lets you view the details of your Agent Memory service. It contains the following sections:\n\n- The **General settings** section provides the connection details and general settings for your Agent Memory service.\n- The **Memory configuration** section provides the service settings for your Redis Agent Memory service.\n- The **AI model and credentials** section shows the model configuration for a service that uses a customer-managed provider key.\n- The **Memory types & extraction** section shows any custom memory types defined for your service.\n- The **Sensitive-data exclusions** section shows the sensitive-data exclusions configured for your service.\n- The **Actions** section lets you flush or delete your Redis Agent Memory service.\n\nSome of these settings can be changed after service creation. To do so, select the **Edit** button."},{"id":"general-settings","title":"General settings","role":"content","text":"The **General settings** section provides the connection details and general settings for your Agent Memory service.\n\n![The General settings for the Agent Memory service.](https://redis.io/docs/latest/images/rc/agent-memory-view-general.png)\n\n| Setting name          |Description|\n|:----------------------|:----------|\n| **Service name** | The name of your agent memory service. _(Editable)_ |\n| **Database** | The name of the database your service uses. |\n| **Store ID** | The unique ID of your Agent Memory store. |\n| **Endpoint** | The base URL for any Agent Memory requests. |\n\nSelect the **Copy** button next to the Store ID and API Base URL to copy them to the clipboard.\n\nFollow the [Redis Agent Memory REST API quickstart](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/rest-api-quickstart) to use the connection information and API key."},{"id":"memory-configuration","title":"Memory configuration","role":"content","text":"The **Memory configuration** section shows the time-to-live (TTL) for memory storage, the extraction cadence, and the automatic summarization settings for session memory.\n\n![The Memory configuration section for the Agent Memory service.](https://redis.io/docs/latest/images/rc/agent-memory-view-memory-configuration.png)\n\n| Setting name          |Description|\n|:----------------------|:----------|\n| **Short-term TTL** | The time-to-live (TTL) of your agent's **short-term memory** (also known as **session memory**). _(Editable)_ |\n| **Long-term TTL** | The time-to-live (TTL) of your agent's **long-term memory**. _(Editable)_ |\n| **Extraction cadence** | How often the extraction pipeline runs while a session is active. Defaults to 5 minutes; can be set to a value between 60 and 600 seconds. _(Editable)_ |\n| **Automatic summarization** | Whether older messages in a session are automatically compressed into a summary to keep session memory concise during long conversations. _(Editable)_ |\n| **Summarize after (messages)** | The number of messages a session can hold before older messages are summarized. Shown only when automatic summarization is enabled. _(Editable)_ |\n| **Keep most recent (messages)** | The number of most recent messages that are always kept in full and never summarized. Shown only when automatic summarization is enabled. _(Editable)_ |"},{"id":"ai-model-and-credentials","title":"AI model and credentials","role":"content","text":"The **AI model and credentials** section appears when the service uses your model provider key.\n\nProvider and model values shown in the screenshot are examples.\n\n![The AI model and credentials section for an existing service. It shows the provider, embedding model, LLM model, and a User-managed API key status.](https://redis.io/docs/latest/images/rc/agent-memory-ai-model-details.png)\n\nThe provider and embedding model are read-only. You can change the LLM model or replace the provider key. For update steps, key rotation behavior, security, and troubleshooting guidance, see [Configure AI models for Agent Memory](https://redis.io/docs/latest/operate/iris/agent-memory/model-configuration)."},{"id":"memory-types-and-extraction","title":"Memory types & extraction","role":"content","text":"The **Memory types & extraction** section shows any [custom memory types](https://redis.io/docs/latest/operate/iris/agent-memory/create-service#custom-memory-types) defined for the service, each listed with its name, fields, and extraction strategy.\n\n![The Memory types & extraction section for the Agent Memory service.](https://redis.io/docs/latest/images/rc/agent-memory-view-memory-types.png)\n\n#### Custom memory types\n\nBecause a custom memory type's structure is fixed after creation, only some settings can be changed when you edit the service:\n\n| Setting name          |Description|\n|:----------------------|:----------|\n| **Name** | The name of the custom memory type. _(Read-only)_ |\n| **Fields** | The fields that make up the custom memory type, with their names, types, and descriptions. _(Read-only)_ |\n| **Extraction prompt** | The extraction strategy prompt for the custom memory type. _(Editable)_ |\n| **Enabled** | Whether the extraction strategy is active. _(Editable)_ |\n\nTo change a custom memory type's name or fields, you must create a new service. You can, however, add a new custom memory type when editing the service, up to the limit of 3 custom memory types."},{"id":"sensitive-data-exclusions","title":"Sensitive-data exclusions","role":"content","text":"The **Sensitive-data exclusions** section shows the [sensitive-data exclusions](https://redis.io/docs/latest/operate/iris/agent-memory/create-service#sensitive-data-exclusions) configured for the service.\n\n> [!NOTE]\n> Sensitive-data exclusions are an early-stage feature, enabled for selected accounts. If you want to try them, contact your Redis representative or [contact sales](https://redis.io/contact/).\n\n![The Sensitive-data exclusions section for the Agent Memory service.](https://redis.io/docs/latest/images/rc/agent-memory-view-sensitive-data-exclusions.png)\n\n| Setting name          |Description|\n|:----------------------|:----------|\n| **Built-in detectors** | Whether built-in detectors are applied, with each selected detector and the action it takes on a match. Only selected detectors are listed. _(Editable)_ |\n| **Custom detectors** | Whether custom detectors are applied, with each detector's name, pattern, action on a match, and status. _(Editable)_ |\n| **Semantic exclusions** | Whether semantic exclusions are applied when memories are extracted. _(Editable)_ |\n| **Exclusion prompt** | The plain-language description of what should never be kept in long-term memory. Required when semantic exclusions are enabled. _(Editable)_ |\n\nTurning semantic exclusions off keeps the exclusion prompt you saved, so you can turn them back on later without retyping it.\n\nChanging exclusions affects memories extracted after you save. Memories already in long-term memory are not re-evaluated, and there is no way to remove only the memories that match your exclusions. The only way to clear memories stored before you configured exclusions is to [flush the service](#flush-memory-entries), which permanently erases all of the service's stored memory data.\n\n> [!WARNING]\n> Semantic exclusions are **advisory**. They steer the extraction model, but they do not guarantee that sensitive content is excluded. Do not rely on semantic exclusions as your only control for regulated or highly sensitive data."},{"id":"actions","title":"Actions","role":"content","text":"The **Actions** section lets you flush or delete your Agent Memory service.\n\n![The actions for the Agent Memory service.](https://redis.io/docs/latest/images/rc/agent-memory-view-actions.png)\n\n#### Flush memory entries\n\nFlushing the service completely erases all stored memory data while preserving the service configuration and the search index used by the service.\n\nTo flush the service:\n\n1. Select **Flush**.\n\n1. A confirmation dialog will appear. Select **Flush** again to confirm.\n\nFlushing the service is permanent and cannot be undone, and will result in empty memory retrieval results until new memory is stored.\n\n#### Delete service\n\nDeleting your Agent Memory service permanently deletes all associated memory data, the service configuration, the Agent Memory search index, and any stored model provider key. It also immediately terminates all API keys associated with the service. Data stored in other indexes within the same database will remain unaffected.\n\nTo delete your Agent Memory service:\n\n1. Select **Delete**.\n\n1. A confirmation dialog will appear. Select the checkbox to confirm that you want to delete the service.\n\n1. Select **Delete** again to confirm.\n\nDeleting the Agent Memory service is permanent and cannot be undone."},{"id":"metrics-tab","title":"Metrics tab","role":"content","text":"The **Metrics** tab provides a series of graphs showing performance data for your Agent Memory service.\n\n| Metric | Description |\n|--------|-------------|\n| Short-term Memory Latency | The average time to process a Short-term memory (or Session memory) lookup request.  |\n| Long-term Memory Latency | The average time to process a Long-term memory lookup request. |"},{"id":"api-keys-tab","title":"API keys tab","role":"content","text":"The **API keys** tab shows a list of all API keys for your service.\n\n![The actions for the Agent Memory service.](https://redis.io/docs/latest/images/rc/agent-memory-view-api-keys.png)\n\nHere, you can generate a new API key or remove any keys that are no longer in use. You can generate or remove service API keys at any time."},{"id":"replace-service-api-key","title":"Generate a new service API key","role":"content","text":"To generate a new service key:\n\n1. Select **New API key**.\n\n    ![The New API key button.](https://redis.io/docs/latest/images/rc/agent-memory-new-api-key.png)\n\n1. Enter a new name for your API key.\n\n    ![The Add API key window.](https://redis.io/docs/latest/images/rc/agent-memory-add-api-key.png)\n\n1. Select **Generate key** to generate your new API key.\n\n1. The new key will appear in a dialog box. Select **Copy** to copy the key to the clipboard.\n\n    ![The Agent Memory service key window. Use the Copy button to save the service key to the clipboard.](https://redis.io/docs/latest/images/rc/agent-memory-service-key.png)\n\n    > [!WARNING]\n    > This is the only time the value of the user key is available. Save it to a secure location before closing the dialog box. <br/><br/>\n    >\n    > If you lose the service key value, you will need to generate a new key again."},{"id":"delete-api-key","title":"Delete API key","role":"content","text":"To delete an API key, select the **Delete API key** button next to the old key.\n\n![Delete button.](https://redis.io/docs/latest/images/rc/icon-delete-lb.png)"}],"examples":[]}
{"schema_version":2,"id":"operate/iris/context-retriever/create-service","title":"Create a Context Retriever service","url":"https://redis.io/docs/latest/operate/iris/context-retriever/create-service/","summary":"Create a Context Retriever service in Redis Cloud to expose schema-first retrieval tools to AI agents.","aliases":["/operate/rc/context-engine/context-retriever/create-service/"],"tags":["docs","operate","iris"],"last_updated":"2026-10-05T16:11:44+01:00","page_type":"content","content_hash":"864ffcd0b4673fd9c08e4593c56d21fedfac213f06d9fc1c61c819278f0a6cd7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Context Retriever helps teams expose operational context to AI agents through schema-first retrieval. This guide walks you through creating and configuring a Context Retriever service in Redis Cloud."},{"id":"prerequisites-and-limitations","title":"Prerequisites and limitations","role":"content","text":"To create a Redis Context Retriever service, you will need a Redis Cloud database that already has relevant data. If you don't have one, see [Create a database](https://redis.io/docs/latest/operate/rc/databases/create-database).\n\n> [!NOTE]\n> Redis Context Retriever does not support the following databases during public preview:\n> - [Redis Flex](https://redis.io/docs/latest/operate/rc/databases/create-database/create-flex-database) databases\n> - [Active-Active](https://redis.io/docs/latest/operate/rc/databases/active-active) databases"},{"id":"create-a-context-retriever-service","title":"Create a Context Retriever service","role":"content","text":"From the [Redis Cloud console](https://cloud.redis.io/), select **Context Retriever** from the left-hand menu.\n\nIf you have not already created a Context Retriever service, you'll see a page with an introduction to Context Retriever. Otherwise, select **New service** to go to the Context Retriever introduction page.\n\n![The New service button.](https://redis.io/docs/latest/images/rc/langcache-new-service.png)\n\nFrom here, you can either:\n\n- Select **Get started** to follow a step-by-step guide using the `ctxctl` CLI, which is installed with the [Context Surfaces Python Client](https://pypi.org/project/redis-context-retriever/). \n- Select **Create custom service** to manually configure your own context retriever settings.\n\nFor this guide, select **Create custom service**."},{"id":"general-settings","title":"General settings","role":"content","text":"The **General settings** section defines basic properties of your service.\n\n![The General settings section.](https://redis.io/docs/latest/images/rc/context-retriever-general-settings.png)\n\n| Setting name | Description |\n|:-------------|:------------|\n| **Service name** | Enter a name for your Context Retriever service. We recommend you use a name that describes your service's purpose. |\n| **Select database** | Select the Redis Cloud database to use for this service from the list. |\n| **Description** | Enter a description for your context retriever. |\n\nSelect **Entities** to continue."},{"id":"define-entities","title":"Define entities","role":"content","text":"In the **Define Entities** step, you'll define the objects that Context Retriever will use to generate retrieval tools. \n\n![The Entities section.](https://redis.io/docs/latest/images/rc/context-retriever-define-entities.png)\n\nSelect **Add Entity** to add an entity.\n\n![The Entity table with the Entity Name, Key template, and description.](https://redis.io/docs/latest/images/rc/context-retriever-define-entities-table.png)\n\n- In the **Entity name** field, enter the name of one of the business objects that is in your database. \n\n    For example, your database might have a bunch of product information with keys like `product:1` or `product:2`. In this case, the entity name would be \"Product\".\n\n- In the **Key Template** field, enter the key template for your entity. Use `{id}` to denote where in the key pattern the ID is located.\n\n    For the example above, the key template would be `product:{id}`. \n\n- You can also enter a description to describe the entity.\n\n- Select the checkmark to confirm the entity.\n\nAfter you are done adding all of the entities, select **Fields** to continue."},{"id":"configure-fields","title":"Configure fields","role":"configuration","text":"In the **Configure fields** step, you'll define the fields of each entity and the relationships between them.\n\nFrom here, you can either:\n\n- Select **Auto-detect fields** to scan your database automatically using a model that will detect the fields for you. \n\n    You'll need to agree to let the model scan your key names and schemas to automatically populate the fields. \n\n    > [!NOTE]\n    > If you use Auto-detect fields, make sure that the fields and relationships that the model generates are accurate. \n\n- Select **Manually enter fields** to manually enter the fields yourself.\n\n![The Configure fields step.](https://redis.io/docs/latest/images/rc/context-retriever-fields.png)\n\nExpand each entry to view the currently defined fields for that entry. Select **Add field** to add a field.\n\n![The Add field table.](https://redis.io/docs/latest/images/rc/context-retriever-add-field.png)\n\n- Enter the field name in the **Field** cell.\n- Select the **PK** checkbox if the field is a primary key. You must have at least one primary key. \n- If the field is related to any other defined entities, select it from the **Related Entity** dropdown. This is usually set if the field contains primary keys of another entity. \n- Select the field's **Type** from the dropdown.\n- In the **Index** cell, select one or more index types to enable searching and filtering on that field.\n- You can also add a description in the **Description** cell.\n\nAfter you set all fields for all of your entities, select **Create** to create your Context Retriever service."},{"id":"next-steps","title":"Next steps","role":"content","text":"After your service is created, you can call the MCP tools Context Retriever exposes from your agent. See the [Context Surfaces Python Client](https://pypi.org/project/redis-context-retriever/) for more information on how to call your tools.\n\nYou can also [view your service](https://redis.io/docs/latest/operate/iris/context-retriever/view-service)."}],"examples":[]}
{"schema_version":2,"id":"operate/iris/context-retriever","title":"Redis Context Retriever on Redis Cloud","url":"https://redis.io/docs/latest/operate/iris/context-retriever/","summary":"Expose schema-first retrieval tools from your Redis Cloud data to AI agents.","tags":["docs","operate","iris"],"last_updated":"2026-10-02T17:27:32-07:00","children":[{"id":"operate/iris/context-retriever/create-service","summary":"Create a Context Retriever service in Redis Cloud to expose schema-first retrieval tools to AI agents.","title":"Create a Context Retriever service","url":"https://redis.io/docs/latest/operate/iris/context-retriever/create-service/"},{"id":"operate/iris/context-retriever/view-service","summary":"View and manage your Context Retriever service in Redis Cloud.","title":"View and manage Context Retriever service","url":"https://redis.io/docs/latest/operate/iris/context-retriever/view-service/"},{"id":"operate/iris/context-retriever/view-admin-keys","summary":"View and manage Context Retriever admin keys in Redis Cloud.","title":"View and manage Context Retriever admin keys","url":"https://redis.io/docs/latest/operate/iris/context-retriever/view-admin-keys/"},{"id":"operate/iris/context-retriever/self-managed","summary":"Install and run Redis Context Retriever on a self-managed Kubernetes cluster using Helm.","title":"Install Context Retriever (self-managed)","url":"https://redis.io/docs/latest/operate/iris/context-retriever/self-managed/"}],"page_type":"content","content_hash":"4c6608ba210ed8e871987df3ec244ee5c19a3166926ef68ba1b94c760ad8e9c9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Context Retriever helps teams expose operational context to AI agents through schema-first retrieval. It models the entities, fields, keys, and relationships that matter to an agent workflow, then presents that context through a governed tool surface the agent can call at runtime. Context Retriever helps an AI agent understand what business objects exist, how they connect, and which paths are safe to use.\n\n![The Context Retriever page in the Redis Cloud console, with Create with CLI and Create custom service options.](https://redis.io/docs/latest/images/rc/context-retriever-get-started.png)\n\nWhen you set up Redis Context Retriever, you model the objects that matter to your agent workflow and connect the relationships between them. You can do this through the UI, the [Context Surfaces Python Client](https://pypi.org/project/redis-context-retriever/), or the `ctxctl` CLI (available when you install the Python client). Context Retriever uses those relationships to automatically create and deploy retrieval tools from your entity model.\n\nWhen an agent needs context during execution, it calls the MCP tools Context Retriever exposes. Instead of guessing which tool to use or generating SQL, the agent follows the defined entity paths and gets back structured, live, operational context.\n\nFor more details, see the [Redis Context Retriever overview](https://redis.io/docs/latest/develop/ai/context-engine/context-retriever)."},{"id":"get-started-with-context-retriever-on-redis-cloud","title":"Get started with Context Retriever on Redis Cloud","role":"content","text":"To set up a Redis Context Retriever on Redis Cloud, you need a database on Redis Cloud that already has relevant data. If you use a relational database, use [Redis Data Integration (RDI)](https://redis.io/docs/latest/operate/rc/rdi) to ingest data into a Redis Cloud database.\n\nWhen you have a database, [Create a context retriever service](https://redis.io/docs/latest/operate/iris/context-retriever/create-service) for your database on Redis Cloud.\n\nAfter you set up Context Retriever, you can [view your service](https://redis.io/docs/latest/operate/iris/context-retriever/view-service). See the [Context Surfaces Python Client](https://pypi.org/project/redis-context-retriever/) for more information on how to call your tools."},{"id":"get-started-with-self-managed-context-retriever","title":"Get started with self-managed Context Retriever","role":"content","text":"To deploy Context Retriever on your own Kubernetes infrastructure instead of Redis Cloud, see [self-managed Context Retriever](https://redis.io/docs/latest/operate/iris/context-retriever/self-managed)."}],"examples":[]}
{"schema_version":2,"id":"operate/iris/context-retriever/self-managed","title":"Install Context Retriever (self-managed)","url":"https://redis.io/docs/latest/operate/iris/context-retriever/self-managed/","summary":"Install and run Redis Context Retriever on a self-managed Kubernetes cluster using Helm.","aliases":["/develop/ai/context-engine/context-retriever/install/"],"tags":["docs","operate","iris"],"last_updated":"2026-09-25T11:04:30-07:00","children":[],"page_type":"content","content_hash":"d31f398704bbbc41096da117c06308c3c3c0826d5fa9c1086a4c1769969b635c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Context Retriever is distributed as container images on Docker Hub plus a Helm chart shipped in the Redis Enterprise Helm repository. Installation pulls the images from Docker Hub (or your own mirror) and deploys the chart against a Redis database you provide.\n\n> [!NOTE]\n> This guide is for system administrators deploying Context Retriever on a self-managed Kubernetes cluster."},{"id":"what-you-need","title":"What you need","role":"content","text":"| Item | Where it comes from |\n| ---- | ------------------- |\n| Container images | Docker Hub: `redislabs/context-retriever` (MCP) and `redislabs/context-retriever-admin` (Admin) |\n| Helm chart | `redis-context-retriever` chart in the Redis Enterprise Helm repository (`ai/charts/redis-context-retriever`) |\n| Redis database | **You provide it** — see prerequisites below |\n| License key | Contact your Redis representative or [contact sales](https://redis.io/contact/). The key starts with `CS-LICENSE.` |"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":""},{"id":"required-software","title":"Required software","role":"content","text":"| Software | Minimum version | Purpose |\n| -------- | --------------- | ------- |\n| Kubernetes | 1.28+ | Orchestration |\n| kubectl | 1.28+ | Kubernetes CLI |\n| Helm | 3.x | Package manager |"},{"id":"redis-database-bring-your-own","title":"Redis database (bring your own)","role":"content","text":"Context Retriever connects to a Redis database that you provide; it does not ship one. The database must have the Search and JSON capabilities enabled (RediSearch and RedisJSON)."},{"id":"network-access","title":"Network access","role":"content","text":"- **Connected install:** the cluster must be able to pull from `docker.io`.\n- **Air-gapped install:** mirror the two images into your internal registry first — see [Air-Gapped Installation](#air-gapped-installation)."},{"id":"system-requirements","title":"System requirements","role":"content","text":"| Component | CPU | Memory |\n| --------- | --- | ------ |\n| Admin Server | 250m – 1 core | 256Mi – 1Gi |\n| MCP Server | 250m – 1 core | 256Mi – 1Gi |"},{"id":"installation","title":"Installation","role":"setup","text":""},{"id":"add-the-helm-repository","title":"Add the Helm repository","role":"content","text":"Add the Redis Enterprise Helm repository (the same source as the other Redis Enterprise AI charts) and list the available chart versions:\n\n[code example]\n\nSet the version you want to install:\n\n[code example]"},{"id":"create-a-namespace","title":"Create a namespace","role":"content","text":"[code example]"},{"id":"create-secrets","title":"Create secrets","role":"content","text":"#### Admin database secret\n\n[code example]\n\n#### License key secret\n\n[code example]"},{"id":"install-the-chart","title":"Install the chart","role":"setup","text":"Install the chart, pointing it at your Redis database with `--set redis.addr` — **this is required; the chart will not render without it.**\n\n[code example]\n\nThe chart's image repositories default to `redislabs/context-retriever-admin` and `redislabs/context-retriever`; the images are pulled from Docker Hub."},{"id":"retrieve-the-admin-api-key","title":"Retrieve the admin API key","role":"content","text":"On first startup an admin API key is generated and written to a file in the admin pod:\n\n[code example]\n\n> [!WARNING]\n> Save this key immediately. It is only available on first startup and cannot be recovered if lost (see [Troubleshooting](#lost-admin-api-key))."},{"id":"install-the-cli-optional","title":"Install the CLI (optional)","role":"setup","text":"The CLI ships on PyPI:\n\n[code example]"},{"id":"verify","title":"Verify","role":"content","text":"Check that both services are healthy:\n\n[code example]\n\nIf you installed the CLI, you can also list the configured surfaces:\n\n[code example]"},{"id":"air-gapped-installation","title":"Air-Gapped Installation","role":"content","text":"Context Retriever supports air-gapped clusters by mirroring the published images into your own OCI registry and installing from a locally downloaded copy of the Helm chart — there is no separate air-gapped bundle.\n\nMirror the images and download the chart (steps 1 and 2) on a host with internet access, then transfer them into your air-gapped environment before installing (step 3).\n\n> [!NOTE]\n> These commands assume the image tag matches the chart version (`$CR_VERSION`). If the images use a different tag, check the chart defaults with `helm show values redis-ai/redis-context-retriever` and set `admin.image.tag` and `mcp.image.tag` accordingly.\n\n1. Mirror both images to your internal registry:\n\n   [code example]\n\n2. Download and unpack the Helm chart for the version you are installing:\n\n   [code example]\n\n3. In the air-gapped environment, create an image pull secret for your registry and install from the local chart directory, pointing the image repositories at your registry:\n\n   [code example]\n\n> [!NOTE]\n> If your registry allows unauthenticated pulls, skip creating `regcred` and remove the `--set 'imagePullSecrets[0].name=regcred'` line from the command above.\n\nYour Redis database is separate and must likewise be reachable from the air-gapped cluster."},{"id":"production-notes","title":"Production notes","role":"content","text":"| Area | Recommendation |\n| ---- | -------------- |\n| Redis | Use a highly available Redis database. Set `redis.addr` to its endpoint, and `redis.tlsEnabled=true` if it terminates TLS. |\n| Replicas | Run more than one replica of each service: `--set admin.replicaCount=2 --set mcp.replicaCount=2`. |\n| Encryption key | Store `SECRET_ENCRYPTION_KEY` securely. It is required to decrypt stored credentials and has no recovery path. |"},{"id":"trust-a-custom-redis-ca-certificate","title":"Trust a custom Redis CA certificate","role":"content","text":"If your Redis presents a TLS certificate signed by a private or self-signed certificate authority (CA) — common with Redis Enterprise — Context Retriever must trust that CA, or the connection fails certificate verification. Use `redis.tlsCA.existingSecrets` to mount one or more CA certificates; the chart adds them to the system trust store of both the Admin and MCP services, so they are trusted automatically with no application configuration. The image's built-in public CA bundle stays trusted — your CAs are added on top.\n\n> [!NOTE]\n> This requires `redis.tlsEnabled: true`, and each CA Secret must already exist in the release namespace before you install or upgrade."},{"id":"create-a-secret-with-the-ca-certificate","title":"Create a Secret with the CA certificate","role":"content","text":"[code example]\n\nThe `--from-file=<key>=<path>` syntax sets the Secret key explicitly — `ca.crt` in this example. If you omit the key (`--from-file=/path/to/file`), kubectl uses the file's basename instead. If you use a different key name (for example, `--from-file=root.pem=...`), set `key` accordingly in the values below."},{"id":"reference-the-secret-in-your-values","title":"Reference the Secret in your values","role":"related","text":"[code example]"},{"id":"values","title":"Values","role":"content","text":"| Key | Default | Description |\n| --- | --- | --- |\n| `redis.tlsCA.existingSecrets` | `[]` | CA certificates to trust for Redis TLS. Empty mounts nothing (the default). |\n| `redis.tlsCA.existingSecrets[].secretName` | — | **Required.** Name of a pre-created Secret holding the CA certificate in PEM format. |\n| `redis.tlsCA.existingSecrets[].key` | `ca.crt` | Key within the Secret whose value is the PEM certificate. |\n\nCertificates are mounted read-only and picked up automatically; the default empty list mounts nothing, so existing installs are unaffected. This affects only Context Retriever's own connection to Redis."},{"id":"configuration-reference","title":"Configuration reference","role":"configuration","text":""},{"id":"environment-variables-set-by-the-chart","title":"Environment variables (set by the chart)","role":"content","text":"| Variable | Description | Required |\n| -------- | ----------- | -------- |\n| `AUTH_MODE` | `local` for self-managed (API-key auth only) | Yes |\n| `REDIS_ADDR` | Redis database endpoint | Yes |\n| `REDIS_USERNAME` | Redis ACL username (Redis 6+). Omit to authenticate as the default user. | No |\n| `REDIS_PASSWORD` | Redis password | If your DB requires auth |\n| `SECRET_ENCRYPTION_KEY` | AES-256 key (base64) for stored secrets | Yes |\n| `LICENSE_KEY` | License key string (`CS-LICENSE...`) | Yes |\n| `LOG_LEVEL` | `debug`, `info`, `warn`, `error` | No |"},{"id":"key-helm-values","title":"Key Helm values","role":"content","text":"[code example]"},{"id":"troubleshooting","title":"Troubleshooting","role":"errors","text":""},{"id":"pod-not-starting","title":"Pod not starting","role":"content","text":"[code example]\n\nCommon causes: license secret missing/invalid; Redis unreachable or missing the Search/JSON capabilities; `SECRET_ENCRYPTION_KEY` not set; `redis.addr` not provided (the chart fails to render with a clear error if it is empty)."},{"id":"license-validation-failed","title":"License validation failed","role":"content","text":"[code example]"},{"id":"lost-admin-api-key","title":"Lost admin API key","role":"content","text":"> [!WARNING]\n> There is no recovery mechanism by design.\n\nIf the key is lost and the pod has restarted, wipe and reinstall:\n\n[code example]"},{"id":"cleanup","title":"Cleanup","role":"content","text":"[code example]"},{"id":"support","title":"Support","role":"compatibility","text":"Contact Redis support with your license ID, the release version, `kubectl version`, and sanitized diagnostic logs:\n\n[code example]"}],"examples":[{"id":"add-the-helm-repository-ex0","language":"bash","code":"helm repo add redis-ai https://helm.redis.io/ai\nhelm repo update redis-ai\nhelm search repo redis-ai/redis-context-retriever --versions","section_id":"add-the-helm-repository"},{"id":"add-the-helm-repository-ex1","language":"bash","code":"export CR_VERSION=\"<version-from-the-helm-search-output>\"","section_id":"add-the-helm-repository"},{"id":"create-a-namespace-ex0","language":"bash","code":"export NS=context-retriever\nkubectl create namespace $NS","section_id":"create-a-namespace"},{"id":"create-secrets-ex0","language":"bash","code":"# SECRET_ENCRYPTION_KEY encrypts stored credentials at rest. It must be a\n# base64-encoded 32-byte (AES-256) key. Generate one and back it up — if lost,\n# encrypted data cannot be recovered.\nSECRET_ENCRYPTION_KEY=$(openssl rand -base64 32)\nADMIN_REDIS_PASSWORD=\"<the password of the default user for the admin database>\"\n\nkubectl create secret generic cr-config \\\n  --namespace $NS \\\n  --from-literal=REDIS_PASSWORD=\"$ADMIN_REDIS_PASSWORD\" \\\n  --from-literal=SECRET_ENCRYPTION_KEY=\"$SECRET_ENCRYPTION_KEY\"","section_id":"create-secrets"},{"id":"create-secrets-ex1","language":"bash","code":"export LICENSE_KEY_FILE_PATH=\"<the path to your license key>\"\n\n# License key from Redis (starts with CS-LICENSE.)\nkubectl create secret generic cr-license \\\n  --namespace $NS \\\n  --from-file=LICENSE_KEY=\"$LICENSE_KEY_FILE_PATH\"","section_id":"create-secrets"},{"id":"install-the-chart-ex0","language":"bash","code":"helm install cr redis-ai/redis-context-retriever \\\n  --version $CR_VERSION \\\n  --namespace $NS \\\n  --set redis.addr=\"<your-redis-host>:<port>\" \\\n  --set secrets.existingSecret=cr-config \\\n  --set license.existingSecret=cr-license\n\nkubectl wait --for=condition=ready pod \\\n  -l app.kubernetes.io/name=redis-context-retriever \\\n  -n $NS \\\n  --timeout=180s","section_id":"install-the-chart"},{"id":"retrieve-the-admin-api-key-ex0","language":"bash","code":"ADMIN_POD=$(kubectl get pods -n $NS \\\n  -l app.kubernetes.io/component=admin \\\n  -o jsonpath='{.items[0].metadata.name}')\n\nkubectl exec -n $NS \"$ADMIN_POD\" -- cat /opt/initialAdminKey.txt","section_id":"retrieve-the-admin-api-key"},{"id":"install-the-cli-optional-ex0","language":"bash","code":"pip install redis-context-retriever\n\nctxctl config set default_output_format table --profile onprem\nexport CTX_PROFILE=onprem\n\nctxctl config set api_url \"<admin_service_ingress_url>\"\nctxctl config set mcp_url \"<mcp_service_ingress_url>\"","section_id":"install-the-cli-optional"},{"id":"verify-ex0","language":"bash","code":"curl -sf http://<admin_service_ingress_url>/health | jq\ncurl -sf http://<mcp_service_ingress_url>/health | jq","section_id":"verify"},{"id":"verify-ex1","language":"bash","code":"ctxctl surface list","section_id":"verify"},{"id":"air-gapped-installation-ex0","language":"bash","code":"for img in context-retriever context-retriever-admin; do\n     docker pull \"redislabs/${img}:${CR_VERSION}\"\n     docker tag  \"redislabs/${img}:${CR_VERSION}\" \"registry.internal.example.com/${img}:${CR_VERSION}\"\n     docker push \"registry.internal.example.com/${img}:${CR_VERSION}\"\n   done","section_id":"air-gapped-installation"},{"id":"air-gapped-installation-ex1","language":"bash","code":"helm repo add redis-ai https://helm.redis.io/ai\n   helm repo update redis-ai\n   helm pull redis-ai/redis-context-retriever --version \"$CR_VERSION\" --untar\n   # unpacks to ./redis-context-retriever/","section_id":"air-gapped-installation"},{"id":"air-gapped-installation-ex2","language":"bash","code":"kubectl create secret docker-registry regcred \\\n     --namespace $NS \\\n     --docker-server=registry.internal.example.com \\\n     --docker-username=\"<user>\" --docker-password=\"<pass>\"\n\n   helm install cr ./redis-context-retriever \\\n     --namespace $NS \\\n     --set redis.addr=\"<your-redis-host>:<port>\" \\\n     --set secrets.existingSecret=cr-config \\\n     --set license.existingSecret=cr-license \\\n     --set admin.image.repository=registry.internal.example.com/context-retriever-admin \\\n     --set mcp.image.repository=registry.internal.example.com/context-retriever \\\n     --set admin.image.tag=\"${CR_VERSION}\" \\\n     --set mcp.image.tag=\"${CR_VERSION}\" \\\n     --set 'imagePullSecrets[0].name=regcred'","section_id":"air-gapped-installation"},{"id":"create-a-secret-with-the-ca-certificate-ex0","language":"bash","code":"kubectl create secret generic my-redis-ca \\\n  --from-file=ca.crt=/path/to/redis-ca.pem \\\n  --namespace $NS","section_id":"create-a-secret-with-the-ca-certificate"},{"id":"reference-the-secret-in-your-values-ex0","language":"yaml","code":"redis:\n  tlsEnabled: true\n  tlsCA:\n    existingSecrets:\n      - secretName: my-redis-ca          # key defaults to \"ca.crt\"\n      - secretName: another-redis-ca     # list as many CAs as you need\n        key: tls.crt                     # override when the Secret uses another key","section_id":"reference-the-secret-in-your-values"},{"id":"key-helm-values-ex0","language":"yaml","code":"## the defaults are commented\n# authMode: local\n#\n# admin:\n#   replicaCount: 1\n#   image:\n#     repository: redislabs/context-retriever-admin\n#     tag: '' # set to the release version\n#   service: { type: NodePort, port: 8080, nodePort: 30080 }\n#\n# mcp:\n#   replicaCount: 1\n#   image:\n#     repository: redislabs/context-retriever\n#     tag: '' # set to the release version\n#   service: { type: NodePort, port: 8081, nodePort: 30081 }\n\nredis:\n  addr: '<admin db address>'\n  # username: ''      # optional Redis ACL username (Redis 6+); omit to use the default user\n  tlsEnabled: false\n\nsecrets:\n  existingSecret: cr-config\n\nlicense:\n  existingSecret: cr-license\n\n# imagePullSecrets: [] # set for a private/mirror registry","section_id":"key-helm-values"},{"id":"pod-not-starting-ex0","language":"bash","code":"kubectl describe pod -n $NS -l app.kubernetes.io/component=admin\nkubectl logs -n $NS -l app.kubernetes.io/component=admin","section_id":"pod-not-starting"},{"id":"license-validation-failed-ex0","language":"bash","code":"kubectl get secret cr-license -n $NS\nkubectl logs -n $NS -l app.kubernetes.io/component=admin | grep -i license","section_id":"license-validation-failed"},{"id":"lost-admin-api-key-ex0","language":"bash","code":"kubectl delete namespace $NS\n# then reinstall from the beginning and save the new key","section_id":"lost-admin-api-key"},{"id":"cleanup-ex0","language":"bash","code":"helm uninstall cr -n $NS\nkubectl delete namespace $NS","section_id":"cleanup"},{"id":"support-ex0","language":"bash","code":"kubectl get pods -n $NS\nkubectl logs -n $NS -l app.kubernetes.io/component=admin --tail=100\ncurl -s http://<admin-host>:8080/health | jq '.license'","section_id":"support"}]}
{"schema_version":2,"id":"operate/iris/context-retriever/view-admin-keys","title":"View and manage Context Retriever admin keys","url":"https://redis.io/docs/latest/operate/iris/context-retriever/view-admin-keys/","summary":"View and manage Context Retriever admin keys in Redis Cloud.","aliases":["/operate/rc/context-engine/context-retriever/view-admin-keys/"],"tags":["docs","operate","iris"],"last_updated":"2026-10-05T16:11:44+01:00","page_type":"content","content_hash":"8ca8d50f6dbf9843959e76a34dad3367fdea348287c5a2e712cd21300c28a6fd","sections":[{"id":"overview","title":"Overview","role":"overview","text":"After you have [created your first Context Retriever service](https://redis.io/docs/latest/operate/iris/context-retriever/create-service), you can view and manage your admin keys from the **Admin keys** tab in the **Context Retriever** section of the Redis Cloud console.\n\nA Context Retriever **admin key** authorizes administrative operations against Context Retriever in your Redis Cloud account, such as creating, updating, or deleting services and their entity models. You can use it with the [Context Surfaces Python Client](https://pypi.org/project/redis-context-retriever/) and `cxtctl` CLI to create an Agent key and call your tools."},{"id":"admin-keys-tab","title":"Admin keys tab","role":"content","text":"From the [Redis Cloud console](https://cloud.redis.io/), select **Context Retriever** from the left-hand menu, then select the **Admin keys** tab.\n\n![The Admin keys tab showing the list of admin keys.](https://redis.io/docs/latest/images/rc/context-retriever-admin-keys-list.png)\n\nThis tab shows a list of all admin keys associated with Context Retriever in your account. Here, you can generate a new admin key or remove any keys that are no longer in use. You can generate or remove admin keys at any time.\n\n| Column | Description |\n|:-------|:------------|\n| **Name** | The name you assigned to the admin key when you created it. |\n| **Value** | An abbreviated value of the admin key. |\n| **Created** | The date and time the admin key was generated. |"},{"id":"generate-a-new-admin-key","title":"Generate a new admin key","role":"content","text":"To generate a new admin key:\n\n1. Select **New admin key**.\n\n    ![The New admin key button.](https://redis.io/docs/latest/images/rc/context-retriever-new-admin-key.png)\n\n1. Enter a name for your admin key.\n\n    ![The Add admin key window.](https://redis.io/docs/latest/images/rc/context-retriever-add-admin-key.png)\n\n1. Select **Generate key** to generate your new admin key.\n\n1. The new key will appear in a dialog box. Select **Copy** to copy the key to the clipboard.\n\n    ![The Context Retriever admin key window. Use the Copy button to save the admin key to the clipboard.](https://redis.io/docs/latest/images/rc/context-retriever-admin-key.png)\n\n    > [!WARNING]\n    > This is the only time the value of the admin key is available. Save it to a secure location before closing the dialog box.<br/><br/>\n    >\n    > If you lose the admin key value, you will need to generate a new admin key."},{"id":"delete-an-admin-key","title":"Delete an admin key","role":"content","text":"To delete an admin key, select the **Delete admin key** button next to the key you want to remove.\n\n![Delete button.](https://redis.io/docs/latest/images/rc/icon-delete-lb.png)\n\nDeleting an admin key immediately revokes any administrative operations that rely on it. This action cannot be undone."}],"examples":[]}
{"schema_version":2,"id":"operate/iris/context-retriever/view-service","title":"View and manage Context Retriever service","url":"https://redis.io/docs/latest/operate/iris/context-retriever/view-service/","summary":"View and manage your Context Retriever service in Redis Cloud.","aliases":["/operate/rc/context-engine/context-retriever/view-service/"],"tags":["docs","operate","iris"],"last_updated":"2026-10-02T14:53:52+01:00","page_type":"content","content_hash":"1e99f40acaa58a84ec12b54a8936f6438c298c61b9ce246bb6c02e5118333b23","sections":[{"id":"overview","title":"Overview","role":"overview","text":"After you have [created your first Context Retriever service](https://redis.io/docs/latest/operate/iris/context-retriever/create-service), selecting **Context Retriever** from the Redis Cloud Console menu will take you to the **Context Retriever Services** page.\n\nThis page displays a list of all Context Retriever services associated with your account.\n\n![The Context Retriever service in the Context Retriever service list.](https://redis.io/docs/latest/images/rc/context-retriever-service-list.png)\n\nSelect your Context Retriever service from the list to view the service's details."},{"id":"overview-tab","title":"Overview tab","role":"overview","text":"The **Overview** tab lets you view the details of your Context Retriever service. It contains the following sections:\n\n- The **Details** section provides the connection details and general settings for your Context Retriever service.\n- The **Entities** section shows the entities, fields, and relationships that Context Retriever exposes as tools.\n- The **Tools** section shows which tools are available to Agents.\n- The **Actions** section lets you delete your Context Retriever service."},{"id":"details","title":"Details","role":"content","text":"The **Details** section provides general settings for your Context Retriever service.\n\n![The General settings for the Context Retriever service.](https://redis.io/docs/latest/images/rc/context-retriever-view-details.png)\n\n| Setting name | Description |\n|:-------------|:------------|\n| **Description** | The description of your Context Retriever service.  |\n| **Created** | The creation date for your service. |\n| **Updated** | The date your service was last updated. |"},{"id":"entities","title":"Entities","role":"content","text":"The **Entities** section shows the entities, fields, and relationships that Context Retriever uses to generate retrieval tools.\n\n![The Entities section for the Context Retriever service.](https://redis.io/docs/latest/images/rc/context-retriever-view-entities.png)\n\nExpand each entity to view its fields, primary keys, related entities, types, and indexes."},{"id":"tools","title":"Tools","role":"content","text":"The **Tools** section shows the tools that are available to your Agents.\n\n![The Entities section for the Context Retriever service.](https://redis.io/docs/latest/images/rc/context-retriever-view-tools.png)\n\nYou can use your Agents to call your tools. For more information, see the [Context Surfaces Python Client](https://pypi.org/project/redis-context-retriever/)"},{"id":"actions","title":"Actions","role":"content","text":"The **Actions** section lets you delete your Context Retriever service.\n\n![The actions for the Context Retriever service.](https://redis.io/docs/latest/images/rc/context-retriever-view-actions.png)\n\n#### Delete service\n\nDeleting your Context Retriever service permanently removes the service configuration, its entity model, and the generated tools. It also immediately terminates all API keys associated with the service. Data stored in the underlying Redis Cloud database remains unaffected.\n\nTo delete your Context Retriever service:\n\n1. Select **Delete**.\n\n1. A confirmation dialog will appear. Select the checkbox to confirm that you want to delete the service.\n\n1. Select **Delete** again to confirm.\n\nDeleting the Context Retriever service is permanent and cannot be undone."}],"examples":[]}
{"schema_version":2,"id":"operate/iris","title":"Redis Iris context engine","url":"https://redis.io/docs/latest/operate/iris/","summary":"Deploy and operate Redis Iris context engine services.","tags":["docs","operate","iris"],"last_updated":"2026-10-15T14:32:20-07:00","children":[{"id":"operate/iris/agent-memory","summary":"Deploy and operate Redis Agent Memory on Redis Cloud or your own infrastructure.","title":"Redis Agent Memory","url":"https://redis.io/docs/latest/operate/iris/agent-memory/"},{"id":"operate/iris/context-retriever","summary":"Expose schema-first retrieval tools from your Redis Cloud data to AI agents.","title":"Redis Context Retriever on Redis Cloud","url":"https://redis.io/docs/latest/operate/iris/context-retriever/"},{"id":"operate/iris/langcache","summary":"Store LLM responses for AI applications in Redis Cloud.","title":"Semantic caching with LangCache on Redis Cloud","url":"https://redis.io/docs/latest/operate/iris/langcache/"}],"page_type":"content","content_hash":"503985aef729b5c6c8faea72d1eba02509d19cb501af14b27596862fefd7bd1d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Iris context engine provides managed and self-managed services for building AI applications with persistent memory, semantic caching, and governed access to business data.\n\n![The Redis Iris overview page in the Redis Cloud console, with cards for Data Pipelines, Context Retriever, Agent Memory, and LangCache.](https://redis.io/docs/latest/images/rc/iris-overview.png)\n\nUse this section to deploy, configure, and operate Redis Iris services. Developer guides and API integration documentation remain under [Develop with Redis](https://redis.io/docs/latest/develop/ai/context-engine).\n\n<div class=\"grid grid-cols-1 md:grid-cols-3 gap-6 my-8\">\n  \n  \n</div>"},{"id":"deployment-options","title":"Deployment options","role":"content","text":"Redis Iris services are available as managed services on Redis Cloud. All Redis Iris services are also available for self-managed deployment."},{"id":"redis-cloud","title":"Redis Cloud","role":"content","text":"Create and manage Redis Iris services through the Redis Cloud console without deploying the supporting infrastructure yourself.\n\n- [Create a Redis Agent Memory service](https://redis.io/docs/latest/operate/iris/agent-memory/create-service)\n- [Create a Redis Context Retriever service](https://redis.io/docs/latest/operate/iris/context-retriever/create-service)"},{"id":"self-managed","title":"Self-managed","role":"content","text":"Deploy Redis Iris services on Kubernetes when you need to operate them on your own infrastructure.\n\n- [Self-managed Redis Agent Memory](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed)\n- [Self-managed Redis Context Retriever](https://redis.io/docs/latest/operate/iris/context-retriever/self-managed)"}],"examples":[]}
{"schema_version":2,"id":"operate/iris/langcache/create-service","title":"Create a LangCache service","url":"https://redis.io/docs/latest/operate/iris/langcache/create-service/","summary":"","aliases":["/operate/rc/langcache/create-service","/operate/rc/context-engine/langcache/create-service/"],"tags":["docs","operate","iris"],"last_updated":"2026-10-05T16:11:44+01:00","page_type":"content","content_hash":"86ba0b792814ec997a6f98c6a21a71787bb299d94d52c93b068e7db52dbd83d4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis LangCache provides vector search capabilities and efficient caching for AI-powered applications. This guide walks you through creating and configuring a LangCache service in Redis Cloud."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"To create a LangCache service, you will need:\n\n- A Redis Cloud database. If you don't have one, see [Create a database](https://redis.io/docs/latest/operate/rc/databases/create-database).\n\n    > [!NOTE]\n    > LangCache does not support the following databases during public preview:\n    > - Databases with a [CIDR allow list](https://redis.io/docs/latest/operate/rc/security/cidr-whitelist)\n    > - [Active-Active](https://redis.io/docs/latest/operate/rc/databases/active-active) databases\n    > - Databases with the [default user](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/default-user) turned off\n\n- An API key for your embedding provider, if not using the default Redis embedding provider. LangCache supports [OpenAI](https://platform.openai.com/api-keys) and the Redis embedding provider during public preview."},{"id":"create-a-langcache-service","title":"Create a LangCache service","role":"content","text":"From the [Redis Cloud console](https://cloud.redis.io/), select **LangCache** from the left-hand menu. \n\nIf you have not already created a LangCache service, you'll see a page with an introduction to LangCache. Otherwise, select **New service** to go to the LangCache introduction page.\n\n![The New service button.](https://redis.io/docs/latest/images/rc/langcache-new-service.png)\n\nFrom here: \n\n- Select **Quick create** to create a LangCache service with default settings using your Free 30MB database. If you haven't created a Free database yet, Redis Cloud will create one and set up the LangCache service for you.\n\n    ![The Quick create button.](https://redis.io/docs/latest/images/rc/langcache-quick-create.png)\n\n    After Redis Cloud creates your LangCache service, a window containing your LangCache service key will appear. Select **Copy** to copy the key to your clipboard. \n\n    ![The LangCache service key window. Use the Copy button to save the service key to the clipboard.](https://redis.io/docs/latest/images/rc/langcache-service-key.png)\n\n    > [!WARNING]\n    > This is the only time the value of the user key is available. Save it to a secure location before closing the dialog box.<br/><br/>\n    >\n    > If you lose the service key value, you will need to [generate a new service key](https://redis.io/docs/latest/operate/iris/langcache/view-edit-cache#replace-service-api-key) to be able to use the LangCache API.\n\n    After your cache is created, you can [use the LangCache API](https://redis.io/docs/latest/operate/iris/langcache/use-langcache) from your client app.\n\n    You can also [view and edit the cache](https://redis.io/docs/latest/operate/iris/langcache/view-edit-cache) and [monitor the cache's performance](https://redis.io/docs/latest/operate/iris/langcache/monitor-cache).\n\n- If you want to customize your LangCache service, select **Create custom service**.\n\n    ![The Create custom service button.](https://redis.io/docs/latest/images/rc/langcache-custom-service.png)\n\n    This takes you to the **Create LangCache service** page. The page is divided into the following sections:\n\n    1. The [General settings](#general-settings) section defines basic properties of your service.\n    1. The [Embedding settings](#embedding-settings) section defines the embedding model used by your service.\n    1. The [Attributes settings](#attributes-settings) section allows you to define attributes for your service."},{"id":"general-settings","title":"General settings","role":"content","text":"The **General settings** section defines basic properties of your service.\n\n![The General settings section.](https://redis.io/docs/latest/images/rc/langcache-general-settings.png)\n\n| Setting name          |Description|\n|:----------------------|:----------|\n| **Service name** | Enter a name for your LangCache service. We recommend you use a name that describes your service's purpose. |\n| **Select database** | Select the Redis Cloud database to use for this service from the list. |\n| **TTL** | The time to live (TTL) for cache entries, in milliseconds. Default: `No expiration` - items in the cache will remain until manually removed. |\n| **User for this service** | The [database access user](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/role-based-access-control) to use for this service. LangCache only supports the [`default` user](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/default-user) during public preview. |"},{"id":"embedding-settings","title":"Embedding settings","role":"content","text":"The **Embedding settings** section defines the embedding model used by your service.\n\n![The Embedding settings section.](https://redis.io/docs/latest/images/rc/langcache-embedding-settings.png)\n\nRequired settings depend on the embedding provider you select. \n\n| Setting name          |Description|\n|:----------------------|:----------|\n| **Embedding Provider** | The embedding provider to use for your service. Select between `Redis`, `OpenAI`, and `Bring your own`. <br/>Any embedding provider you use must support the [OpenAI embeddings API](https://platform.openai.com/docs/api-reference/embeddings). |\n| **Embedding provider API key** | Enter your embedding provider's API key *(`OpenAI` and `Bring your own` embedding providers only)* |\n| **Embedding provider URL** | Enter your embedding provider's API URL *(`Bring your own` embedding provider only)* |\n| **Model** | Select or enter the embedding model to use for your service. |\n| **Similarity threshold** | Set the minimum similarity score required to consider a cached response a match. Range: `0.5` to `1.0`. Default: `0.85`<br/><br/>A higher value means more precise matches, but if it's too high, you will compromise on the number of matches and may lose relevant matches. A lower value means more matches, but may include less relevant matches. We recommend starting between `0.8` and `0.9` and then fine-tuning based on your results. |"},{"id":"attributes-settings","title":"Attributes settings","role":"content","text":"Attributes provide powerful scoping capabilities for your LangCache operations. Think of them as tags or labels that help you organize and manage your cached data with precision.\n\nThe **Attributes settings** section allows you to define attributes for your service. It is collapsed by default.\n\n![The Attributes settings section.](https://redis.io/docs/latest/images/rc/langcache-attribute-settings.png)\n\nLangCache allows you to define up to 5 custom attributes that align with your specific use case. To add a new attribute:\n\n1. Select **Add attribute**.\n\n    ![The Add attribute button.](https://redis.io/docs/latest/images/rc/langcache-add-attribute.png)\n\n1. Give your custom attribute a descriptive name and select the check mark button to save it.\n\n    ![The custom attributes section. Select the Confirm add attribute button to save your attribute.](https://redis.io/docs/latest/images/rc/langcache-custom-attributes.png)\n\nAfter you save your custom attribute, it will appear in the list of custom attributes. Use the **Delete** button to remove it.\n\n![Select the Delete button to delete the selected attribute.](https://redis.io/docs/latest/images/rc/icon-delete-lb.png)\n\nYou can also select **Add attribute** again to add an additional attribute.\n\n![The Add attribute button.](https://redis.io/docs/latest/images/rc/langcache-add-attribute.png)"},{"id":"create-service","title":"Create service","role":"content","text":"When you are done setting the details of your LangCache service, select **Create** to create it.\n\n![Use the Create button to create a LangCache service.](https://redis.io/docs/latest/images/rc/button-access-management-user-key-create.png)\n\nA window containing your LangCache service key will appear. Select **Copy** to copy the key to your clipboard. \n\n![The LangCache service key window. Use the Copy button to save the service key to the clipboard.](https://redis.io/docs/latest/images/rc/langcache-service-key.png)\n\n> [!WARNING]\n> This is the only time the value of the user key is available. Save it to a secure location before closing the dialog box.<br/><br/>\n>\n> If you lose the service key value, you will need to [generate a new service key](https://redis.io/docs/latest/operate/iris/langcache/view-edit-cache#replace-service-api-key) to be able to use the LangCache API.\n\nYou'll be taken to your LangCache service's **Configuration** page. You'll also be able to see your LangCache service in the LangCache service list.\n\n![The LangCache service in the LangCache service list.](https://redis.io/docs/latest/images/rc/langcache-service-list.png)\n\nIf an error occurs, verify that:\n- Your database is active.\n- You have provided a valid OpenAI API key.\n- You have provided valid values for all the required fields.\n\nFor help, [contact support](https://redis.io/support/)."},{"id":"next-steps","title":"Next steps","role":"content","text":"After your cache is created, you can [use the LangCache API](https://redis.io/docs/latest/operate/iris/langcache/use-langcache) from your client app.\n\nYou can also [view and edit the cache](https://redis.io/docs/latest/operate/iris/langcache/view-edit-cache) and [monitor the cache's performance](https://redis.io/docs/latest/operate/iris/langcache/monitor-cache)."}],"examples":[]}
{"schema_version":2,"id":"operate/iris/langcache","title":"Semantic caching with LangCache on Redis Cloud","url":"https://redis.io/docs/latest/operate/iris/langcache/","summary":"Store LLM responses for AI applications in Redis Cloud.","aliases":["/operate/rc/langcache"],"tags":["docs","operate","iris"],"last_updated":"2026-10-02T17:27:32-07:00","children":[{"id":"operate/iris/langcache/create-service","summary":"","title":"Create a LangCache service","url":"https://redis.io/docs/latest/operate/iris/langcache/create-service/"},{"id":"operate/iris/langcache/use-langcache","summary":"","title":"Use the LangCache API on Redis Cloud","url":"https://redis.io/docs/latest/operate/iris/langcache/use-langcache/"},{"id":"operate/iris/langcache/view-edit-cache","summary":"","title":"View and edit LangCache service","url":"https://redis.io/docs/latest/operate/iris/langcache/view-edit-cache/"},{"id":"operate/iris/langcache/monitor-cache","summary":"","title":"Monitor a LangCache service","url":"https://redis.io/docs/latest/operate/iris/langcache/monitor-cache/"},{"id":"operate/iris/langcache/self-managed","summary":"Deploy, configure, secure, and operate LangCache on a self-managed Kubernetes cluster.","title":"Self-managed LangCache","url":"https://redis.io/docs/latest/operate/iris/langcache/self-managed/"}],"page_type":"content","content_hash":"bd72e2096848f33bac22c6e185a403d3504e0e67a2215d11590ebe6469d94499","sections":[{"id":"overview","title":"Overview","role":"overview","text":"LangCache is a semantic caching service available as a REST API that stores LLM responses for fast and cheaper retrieval, built on the Redis vector database. By using semantic caching, you can significantly reduce API costs and lower the average latency of your generative AI applications.\n\n![The LangCache page in the Redis Cloud console, with Quick create and Create custom service options.](https://redis.io/docs/latest/images/rc/langcache-get-started.png)\n\nFor more information about how LangCache works, see the [LangCache overview](https://redis.io/docs/latest/develop/ai/context-engine/langcache)."},{"id":"get-started-with-langcache-on-redis-cloud","title":"Get started with LangCache on Redis Cloud","role":"content","text":"To set up LangCache on Redis Cloud:\n\n1. [Create a database](https://redis.io/docs/latest/operate/rc/databases/create-database) on Redis Cloud.\n2. [Create a LangCache service](https://redis.io/docs/latest/operate/iris/langcache/create-service) for your database on Redis Cloud.\n3. [Use the LangCache API](https://redis.io/docs/latest/operate/iris/langcache/use-langcache) from your client app.\n\nAfter you set up LangCache, you can [view and edit the cache](https://redis.io/docs/latest/operate/iris/langcache/view-edit-cache) and [monitor the cache's performance](https://redis.io/docs/latest/operate/iris/langcache/monitor-cache).\n\nSee also our [Redis LangCache setup](https://www.youtube.com/watch?v=UOGhMZlZLko)\ntutorial video for advice on how to get started."},{"id":"get-started-with-self-managed-langcache","title":"Get started with self-managed LangCache","role":"content","text":"To deploy LangCache on your own Kubernetes infrastructure instead of Redis Cloud, see [self-managed LangCache](https://redis.io/docs/latest/operate/iris/langcache/self-managed)."},{"id":"llm-cost-reduction-with-langcache","title":"LLM cost reduction with LangCache","role":"content","text":"LangCache reduces your LLM costs by caching responses and avoiding repeated API calls. When a response is served from cache, you don’t pay for output tokens. Input token costs are typically offset by embedding and storage costs.\n\nFor every cached response, you'll save the output token cost. To calculate your monthly savings with LangCache, you can use the following formula:\n\n[code example]\n\nThe more requests you serve from LangCache, the more you save, because you’re not paying to regenerate the output.\n\nHere’s an example:\n- Monthly LLM spend: $200\n- Percentage of output tokens in your spend: 60%\n- Cost of output tokens: $200 × 60% = $120\n- Cache hit rate: 50%\n- Estimated savings: $120 × 50% = $60/month\n\n> [!NOTE]\n> The formula and numbers above provide a rough estimate of your monthly savings. Actual savings will vary depending on your usage.\n\nYou can also use the [LangCache savings calculator](https://redis.io/calculator/langcache/) to estimate your annual savings with LangCache."}],"examples":[{"id":"llm-cost-reduction-with-langcache-ex0","language":"bash","code":"Est. monthly savings with LangCache = \n    (Monthly output token costs) × (Cache hit rate)","section_id":"llm-cost-reduction-with-langcache"}]}
{"schema_version":2,"id":"operate/iris/langcache/monitor-cache","title":"Monitor a LangCache service","url":"https://redis.io/docs/latest/operate/iris/langcache/monitor-cache/","summary":"","aliases":["/operate/rc/langcache/monitor-cache","/operate/rc/context-engine/langcache/monitor-cache/"],"tags":["docs","operate","iris"],"last_updated":"2026-10-02T14:53:52+01:00","page_type":"content","content_hash":"4c940878d5d1450b6e98bf87a75d21bb5540cd3618ca40f6ccfb9e27a2d8eb24","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can monitor a LangCache service's performance from the **Metrics** tab of the service's page.\n\n![The metrics tab of the LangCache service's page.](https://redis.io/docs/latest/images/rc/langcache-metrics.png)\n\nThe **Metrics** tab provides a series of graphs showing performance data for your LangCache service.\n\nYou can switch between hourly, daily, and weekly stats using the **Hour**, **Day**, and **Week** buttons at the top of the page. Each graph also includes minimum, average, maximum, and latest values."},{"id":"langcache-metrics-reference","title":"LangCache metrics reference","role":"content","text":""},{"id":"cache-hit-ratio","title":"Cache hit ratio","role":"content","text":"The percentage of requests that were successfully served from the cache without needing to call the LLM API. A healthy cache will generally show an increasing hit ratio over time as it becomes more populated by cached responses.\n\nTo optimize your cache hit ratio:\n\n- Tune similarity thresholds to capture more semantically related queries.\n- Analyze recurring query patterns to fine-tune your embedding strategies.\n- Test different embedding models to understand their impact on cache hit rates.\n\nA higher cache hit ratio does not always mean better performance. If the cache is too lenient in its similarity matching, it may return irrelevant responses, leading to a higher cache hit rate but poorer overall performance."},{"id":"cache-search-requests","title":"Cache search requests","role":"content","text":"The number of read attempts against the cache at the specified time. This metric can help you understand the load on your cache and identify periods of high or low activity."},{"id":"cache-latency","title":"Cache latency","role":"content","text":"The average time to process a cache lookup request. This metric can help you identify performance bottlenecks and optimize your cache configuration.\n\nCache latency is highly dependent on embedding model performance, since the cache must generate embeddings for each request in order to compare them to the cached responses.\n\nHigh cache latency may indicate one of the following:\n\n- Inefficient embedding generation from the embedding provider\n- Large cache requiring longer comparison times\n- Network latency between the cache and embedding provider\n- Resource constraints"}],"examples":[]}
{"schema_version":2,"id":"operate/iris/langcache/self-managed/api-examples","title":"Self-managed API examples","url":"https://redis.io/docs/latest/operate/iris/langcache/self-managed/api-examples/","summary":"Use curl examples with the LangCache self-managed Control Plane, Identity Service, and Data Plane APIs.","tags":["docs","operate","iris"],"last_updated":"2026-10-06T11:56:03+03:00","page_type":"content","content_hash":"67bbcc786ec60303436a89b4247fd84afbdfb6f80cb103dfceca4f9890d8f455","sections":[{"id":"overview","title":"Overview","role":"overview","text":"These examples show self-managed Control Plane, Identity Service, and Data\nPlane requests. They assume agent-key authentication as described in\n[Authentication and authorization](https://redis.io/docs/latest/operate/iris/langcache/self-managed/authentication).\n\nFor the complete shared Data Plane schema, see the\n[LangCache API](https://redis.io/docs/latest/develop/ai/context-engine/langcache/api-reference).\nFor the self-managed admin schema, see the\n[Control Plane API reference](https://redis.io/docs/latest/operate/iris/langcache/self-managed/control-plane-api-reference)."},{"id":"control-plane-api-examples","title":"Control Plane API examples","role":"content","text":"Set variables:\n\n[code example]\n\nList caches:\n\n[code example]\n\nCreate a cache:\n\n[code example]\n\nResponse:\n\n[code example]\n\n`databaseId` must match an entry in the Control Plane's configured\n`databases` registry. `attributes` lists the attribute names the cache accepts; every\nattribute you send must be listed here. `defaultSearchThreshold` is a float between 0 and 1.\n`defaultTtlMillis` accepts `-1` or `0` for no expiration, or a positive\nnumber of milliseconds.\n\nGet a cache:\n\n[code example]\n\nResponse fields include `status` (`PROVISIONING`, `READY`, or\n`UNAVAILABLE`), the deployment's `embeddingProvider`/`embeddingModel`/\n`embeddingDimensions`, and the resolved `databaseName`.\n\nUpdate a cache:\n\n[code example]\n\nFlush a cache's entries without deleting the cache:\n\n[code example]\n\nDelete a cache:\n\n[code example]\n\nList the deployment's configured embedding providers and models:\n\n[code example]"},{"id":"identity-service-api-examples","title":"Identity Service API examples","role":"content","text":"Set variables:\n\n[code example]\n\nMint an agent key scoped to one cache:\n\n[code example]\n\nResponse:\n\n[code example]\n\nUse the same tenant on every grant of a key. Any string is accepted. Use `1` if you manage caches with the Control Plane admin token.\n\nRotate it later:\n\n[code example]\n\nRotation returns a new `keyId` with the new token. For the grace period and which `keyId` to\nuse afterward, see [Secret rotation](https://redis.io/docs/latest/operate/iris/langcache/self-managed/operations#secret-rotation)."},{"id":"data-plane-api-examples","title":"Data Plane API examples","role":"content","text":"Set variables:\n\n[code example]"},{"id":"set-a-cache-entry","title":"Set a cache entry","role":"content","text":"[code example]"},{"id":"search-for-a-cached-response","title":"Search for a cached response","role":"content","text":"[code example]"},{"id":"delete-a-specific-entry","title":"Delete a specific entry","role":"content","text":"[code example]"},{"id":"delete-entries-matching-attributes","title":"Delete entries matching attributes","role":"content","text":"[code example]"},{"id":"flush-all-entries-in-a-cache","title":"Flush all entries in a cache","role":"content","text":"[code example]"},{"id":"check-cache-health","title":"Check cache health","role":"content","text":"[code example]\n\nFor the full request and response schema for cache entries (set, search,\ndelete, flush), see the\n[LangCache API reference](https://redis.io/docs/latest/develop/ai/context-engine/langcache/api-reference).\nThat shared reference does not yet cover cache health; the example above\nreflects the same Data Plane API."}],"examples":[{"id":"control-plane-api-examples-ex0","language":"bash","code":"CP_URL=\"http://localhost:9100\"\nLC_ADMIN_TOKEN=\"<admin-token>\"","section_id":"control-plane-api-examples"},{"id":"control-plane-api-examples-ex1","language":"bash","code":"curl -sS \"$CP_URL/v1/caches\" \\\n  -H \"Authorization: Bearer $LC_ADMIN_TOKEN\"","section_id":"control-plane-api-examples"},{"id":"control-plane-api-examples-ex2","language":"bash","code":"curl -sS -X POST \"$CP_URL/v1/caches\" \\\n  -H \"Authorization: Bearer $LC_ADMIN_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"my-cache\",\n    \"databaseId\": \"cache-primary\",\n    \"defaultSearchThreshold\": 0.9,\n    \"defaultTtlMillis\": -1,\n    \"attributes\": [\"topic\"]\n  }'","section_id":"control-plane-api-examples"},{"id":"control-plane-api-examples-ex3","language":"json","code":"{\n  \"cacheId\": \"0123456789abcdef0123456789abcdef\"\n}","section_id":"control-plane-api-examples"},{"id":"control-plane-api-examples-ex4","language":"bash","code":"curl -sS \"$CP_URL/v1/caches/<cache-id>\" \\\n  -H \"Authorization: Bearer $LC_ADMIN_TOKEN\"","section_id":"control-plane-api-examples"},{"id":"control-plane-api-examples-ex5","language":"bash","code":"curl -sS -X PATCH \"$CP_URL/v1/caches/<cache-id>\" \\\n  -H \"Authorization: Bearer $LC_ADMIN_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"defaultSearchThreshold\": 0.85\n  }'","section_id":"control-plane-api-examples"},{"id":"control-plane-api-examples-ex6","language":"bash","code":"curl -sS -X DELETE \"$CP_URL/v1/caches/<cache-id>/entries\" \\\n  -H \"Authorization: Bearer $LC_ADMIN_TOKEN\"","section_id":"control-plane-api-examples"},{"id":"control-plane-api-examples-ex7","language":"bash","code":"curl -sS -X DELETE \"$CP_URL/v1/caches/<cache-id>?flush=true\" \\\n  -H \"Authorization: Bearer $LC_ADMIN_TOKEN\"","section_id":"control-plane-api-examples"},{"id":"control-plane-api-examples-ex8","language":"bash","code":"curl -sS \"$CP_URL/v1/embedding-providers\" \\\n  -H \"Authorization: Bearer $LC_ADMIN_TOKEN\"","section_id":"control-plane-api-examples"},{"id":"identity-service-api-examples-ex0","language":"bash","code":"IDS_URL=\"http://localhost:9200\"\nIDS_CONTROL_TOKEN=\"<identity-service-control-token>\"","section_id":"identity-service-api-examples"},{"id":"identity-service-api-examples-ex1","language":"bash","code":"curl -sS -X POST \"$IDS_URL/v1/api-keys\" \\\n  -H \"Authorization: Bearer $IDS_CONTROL_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"my-agent-key\",\n    \"grants\": [\n      {\n        \"tenant\": \"<your-tenant-id>\",\n        \"product\": \"langcache\",\n        \"resourceType\": \"lc-cache\",\n        \"resourceId\": \"<cache-id>\",\n        \"actions\": [\"read\", \"write\"]\n      }\n    ]\n  }'","section_id":"identity-service-api-examples"},{"id":"identity-service-api-examples-ex2","language":"json","code":"{\n  \"keyId\": \"0123456789abcdef0123456789abcdef\",\n  \"token\": \"<agent-key>\",\n  \"createdAt\": 1780000000\n}","section_id":"identity-service-api-examples"},{"id":"identity-service-api-examples-ex3","language":"bash","code":"curl -sS -X POST \"$IDS_URL/v1/api-keys/<key-id>/rotate?graceSeconds=3600\" \\\n  -H \"Authorization: Bearer $IDS_CONTROL_TOKEN\"","section_id":"identity-service-api-examples"},{"id":"data-plane-api-examples-ex0","language":"bash","code":"DP_URL=\"http://localhost:9000\"\nCACHE_ID=\"<cache-id>\"\nLC_AGENT_KEY=\"<agent-key>\"","section_id":"data-plane-api-examples"},{"id":"set-a-cache-entry-ex0","language":"bash","code":"curl -sS -X POST \"$DP_URL/v1/caches/$CACHE_ID/entries\" \\\n  -H \"Authorization: Bearer $LC_AGENT_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"prompt\": \"What is the capital of France?\",\n    \"response\": \"The capital of France is Paris.\"\n  }'","section_id":"set-a-cache-entry"},{"id":"search-for-a-cached-response-ex0","language":"bash","code":"curl -sS -X POST \"$DP_URL/v1/caches/$CACHE_ID/entries/search\" \\\n  -H \"Authorization: Bearer $LC_AGENT_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"prompt\": \"What'\"'\"'s the capital city of France?\"\n  }'","section_id":"search-for-a-cached-response"},{"id":"delete-a-specific-entry-ex0","language":"bash","code":"curl -sS -X DELETE \"$DP_URL/v1/caches/$CACHE_ID/entries/<entry-id>\" \\\n  -H \"Authorization: Bearer $LC_AGENT_KEY\"","section_id":"delete-a-specific-entry"},{"id":"delete-entries-matching-attributes-ex0","language":"bash","code":"curl -sS -X DELETE \"$DP_URL/v1/caches/$CACHE_ID/entries\" \\\n  -H \"Authorization: Bearer $LC_AGENT_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"attributes\": {\n      \"topic\": \"geography\"\n    }\n  }'","section_id":"delete-entries-matching-attributes"},{"id":"flush-all-entries-in-a-cache-ex0","language":"bash","code":"curl -sS -X POST \"$DP_URL/v1/caches/$CACHE_ID/flush\" \\\n  -H \"Authorization: Bearer $LC_AGENT_KEY\"","section_id":"flush-all-entries-in-a-cache"},{"id":"check-cache-health-ex0","language":"bash","code":"curl -sS \"$DP_URL/v1/caches/$CACHE_ID/health\" \\\n  -H \"Authorization: Bearer $LC_AGENT_KEY\"","section_id":"check-cache-health"}]}
{"schema_version":2,"id":"operate/iris/langcache/self-managed/authentication","title":"Authentication and authorization","url":"https://redis.io/docs/latest/operate/iris/langcache/self-managed/authentication/","summary":"Configure LangCache self-managed Control Plane authentication and Data Plane agent-key authentication through the Identity Service.","tags":["docs","operate","iris"],"last_updated":"2026-10-06T12:03:53+03:00","page_type":"content","content_hash":"28dd60a007685e4063c22167ced51e7932e877599c862b9ee17591f24e47c0cf","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The Data Plane always authenticates by introspecting agent keys against an\nIdentity Service. There is no auth-disabled or static-token mode for\nself-managed LangCache."},{"id":"credentials","title":"Credentials","role":"content","text":"| Credential | Who presents it to whom | Secret / key | Auto-generated? | What it unlocks |\n| --- | --- | --- | --- | --- |\n| Control Plane admin token | Operator → Control Plane (`:9100`) | `langcache-controlplane-admin-token` / `token` | Yes (`controlplane.adminToken.autoGenerate`) | Cache management: `/v1/caches` |\n| Control Plane internal token | Identity Service → Control Plane | `langcache-controlplane-internal-token` / `token` | Yes (`controlplane.internalToken.autoGenerate`) | Checking that the cache in a grant exists. Chart-managed; you handle it only with bring-your-own (BYO) configuration or an external Identity Service |\n| Identity Service control token | Operator → Identity Service (`:9200`) | `langcache-identity-service-control-token` / `token` | Yes (`identityService.bundled.controlToken.autoGenerate`) | Creating, listing, updating, rotating and revoking agent keys |\n| Data Plane service credential | Data Plane → Identity Service | `langcache-identity-service-dp-credential` / `token` | Yes (`identityService.bundled.runtime.dataplaneCredential.autoGenerate`) | Checking agent keys for `langcache`. Chart-managed; you handle it only with bring-your-own (BYO) configuration or an external Identity Service |\n| Agent key | Application → Data Plane (`:9000`) | None; returned once when minted or rotated | No | The cache operations its grants allow |"},{"id":"control-plane-admin-token","title":"Control Plane admin token","role":"content","text":"Control Plane management endpoints require:\n\n[code example]\n\nBy default, `controlplane.adminToken.autoGenerate: true` mints this token\ninto a chart-managed Secret on first install (stable across upgrades).\nRetrieve it:\n\n[code example]\n\nTo bring your own token instead:\n\n[code example]\n\n[code example]"},{"id":"control-plane-internal-token","title":"Control Plane internal token","role":"content","text":"The internal token authenticates calls to the Control Plane's internal\ngrant-validation endpoint (`/internal/v1/grants/validate`). The Identity\nService calls this endpoint to confirm that a grant naming a LangCache cache\nresource is valid before it lets an agent key carry that grant.\n\nLike the admin token, it defaults to `controlplane.internalToken.autoGenerate: true`\nand is retrievable the same way:\n\n[code example]\n\nIn bundled Identity Service mode, the chart wires this token to the\nIdentity Service's `product_validation.langcache.credential` automatically.\nIn external mode, you must give this token to the Identity Service's owner\n(see [External Identity Service](#external-identity-service)).\n\nThe admin token and internal token must be different. The chart refuses the\nsame Secret and key, but doesn't detect identical values in different Secrets."},{"id":"identity-service-modes","title":"Identity Service modes","role":"content","text":"You must choose either Bundled Identity Service or External Identity Service at install time."},{"id":"bundled-identity-service","title":"Bundled Identity Service","role":"content","text":"`identityService.mode: bundled` renders the Identity Service\nDeployment and Service, auto-generates its control token and the Data\nPlane's own runtime introspection credential, and wires everything together\nautomatically:\n\n[code example]\n\nRetrieve the auto-generated Identity Service Control admin token (used for\n`/v1/api-keys` calls, not the Data Plane's own runtime credential):\n\n[code example]\n\nThe chart also auto-generates a separate credential the Data Plane itself\nuses to call the Identity Service's introspection endpoint (scoped to\n`api-key-introspect` on product `langcache` only):\n\n[code example]"},{"id":"external-identity-service","title":"External Identity Service","role":"content","text":"`identityService.mode: external` renders no Identity Service workload at\nall — use this when your suite already runs one, for example alongside\nself-managed Redis Agent Memory:\n\n[code example]\n\nThe `langcache-dp-ids-credential` is minted out of band by the suite-level\nIdentity Service owner, scoped to `api-key-introspect` on product\n`langcache`. You must also ask that owner to configure the external\nIdentity Service's own `product_validation.langcache` against this\nrelease's Control Plane internal Service\n(`http://langcache-controlplane.<namespace-name>.svc.cluster.local:9100`) and\nthis release's `controlplane.internalToken` Secret. The owner needs a copy of\n`langcache-controlplane-internal-token` in the Identity Service's namespace;\nthis chart has no way to reach into an Identity Service it doesn't own."},{"id":"minting-and-managing-agent-keys","title":"Minting and managing agent keys","role":"content","text":"Mint, list, update, revoke, and rotate agent keys directly against the\nIdentity Service (not the LangCache Control Plane). In bundled mode, first\nport-forward the Identity Service and read its control token, as in\n[Verify the deployment](https://redis.io/docs/latest/operate/iris/langcache/self-managed/deploy#verify-the-deployment).\n\n[code example]\n\nUse the same tenant on every grant of a key. Any string is accepted. Use `1` if you manage caches with the Control Plane admin token.\n\nThe response contains the new credential. Store it immediately; credentials\nare returned only when a key is minted or rotated.\n\nThe Data Plane caches each key check, so a revoked key stops working within\nup to 5 minutes (180 seconds soft, 300 seconds hard,\n`identityService.bundled.runtime.cache.*`). For rotation, see\n[Operations](https://redis.io/docs/latest/operate/iris/langcache/self-managed/operations).\n\nGrant actions:\n\n| Action | Meaning |\n| --- | --- |\n| `read` | Read and search cache entries. |\n| `write` | Mutate cache entries. `write` implies `read`. |\n| `full` | Full cache access through the grant. `full` implies `write`. This is a resource permission, not a substitute for the Control Plane admin token; it doesn't grant access to Control Plane administration APIs. |\n\nClients send agent keys as Bearer credentials to the Data Plane:\n\n[code example]\n\nTreat agent keys as opaque credentials. Do not parse their contents."},{"id":"cache-authorization","title":"Cache authorization","role":"content","text":"For agent-key requests, the Data Plane checks both identity and resource\nauthorization through the Identity Service:\n\n1. The key exists and its secret validates.\n2. The key has a grant for the requested cache resource, keyed as\n   `lc-cache:<cache-id>`.\n3. The grant includes the permission required by the operation."},{"id":"gateway-and-identity-provider-integration","title":"Gateway and identity provider integration","role":"content","text":"Use a gateway when it owns external authentication and coarse policy. For\nexample, a gateway can authenticate callers through an identity provider\nbefore it forwards requests to LangCache.\n\nGateway rules:\n\n- The gateway owns external authentication and perimeter policy.\n- LangCache owns cache-level authorization through the Identity Service.\n- LangCache agent keys are stored and forwarded by trusted infrastructure or\n  trusted applications.\n- Callers must not be able to bypass the gateway and reach the Data Plane\n  directly unless they also present a valid LangCache agent key."},{"id":"next-steps","title":"Next steps","role":"content","text":"With an admin token, internal token, and agent key in hand, see\n[API examples](https://redis.io/docs/latest/operate/iris/langcache/self-managed/api-examples)\nto create a cache and start calling the Data Plane, or\n[Operations](https://redis.io/docs/latest/operate/iris/langcache/self-managed/operations)\nto rotate these credentials going forward."}],"examples":[{"id":"control-plane-admin-token-ex0","language":"http","code":"Authorization: Bearer <admin-token>","section_id":"control-plane-admin-token"},{"id":"control-plane-admin-token-ex1","language":"bash","code":"kubectl -n <namespace-name> get secret langcache-controlplane-admin-token \\\n  -o jsonpath=\"{.data.token}\" | base64 -d","section_id":"control-plane-admin-token"},{"id":"control-plane-admin-token-ex2","language":"bash","code":"kubectl -n <namespace-name> create secret generic langcache-controlplane-admin-token \\\n  --from-literal=token='<admin-token>'","section_id":"control-plane-admin-token"},{"id":"control-plane-admin-token-ex3","language":"yaml","code":"controlplane:\n  adminToken:\n    existingSecret: langcache-controlplane-admin-token\n    autoGenerate: false","section_id":"control-plane-admin-token"},{"id":"control-plane-internal-token-ex0","language":"bash","code":"kubectl -n <namespace-name> get secret langcache-controlplane-internal-token \\\n  -o jsonpath=\"{.data.token}\" | base64 -d","section_id":"control-plane-internal-token"},{"id":"bundled-identity-service-ex0","language":"yaml","code":"identityService:\n  mode: bundled\n  bundled:\n    metadata:\n      existingSecret: ids-metadata","section_id":"bundled-identity-service"},{"id":"bundled-identity-service-ex1","language":"bash","code":"kubectl -n <namespace-name> get secret langcache-identity-service-control-token \\\n  -o jsonpath=\"{.data.token}\" | base64 -d","section_id":"bundled-identity-service"},{"id":"bundled-identity-service-ex2","language":"bash","code":"kubectl -n <namespace-name> get secret langcache-identity-service-dp-credential \\\n  -o jsonpath=\"{.data.token}\" | base64 -d","section_id":"bundled-identity-service"},{"id":"external-identity-service-ex0","language":"yaml","code":"identityService:\n  mode: external\n  external:\n    baseURL: https://suite-identity-service.example.com\n    credential:\n      existingSecret: langcache-dp-ids-credential\n      secretKey: token","section_id":"external-identity-service"},{"id":"minting-and-managing-agent-keys-ex0","language":"bash","code":"IDS_URL=\"http://localhost:9200\"\nIDS_CONTROL_TOKEN=\"<identity-service-control-token>\"\n\ncurl -sS -X POST \"$IDS_URL/v1/api-keys\" \\\n  -H \"Authorization: Bearer $IDS_CONTROL_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"my-agent-key\",\n    \"grants\": [\n      {\n        \"tenant\": \"<your-tenant-id>\",\n        \"product\": \"langcache\",\n        \"resourceType\": \"lc-cache\",\n        \"resourceId\": \"<cache-id>\",\n        \"actions\": [\"read\", \"write\"]\n      }\n    ]\n  }'","section_id":"minting-and-managing-agent-keys"},{"id":"minting-and-managing-agent-keys-ex1","language":"http","code":"Authorization: Bearer <agent-key>","section_id":"minting-and-managing-agent-keys"}]}
{"schema_version":2,"id":"operate/iris/langcache/self-managed/configuration","title":"Configuration","url":"https://redis.io/docs/latest/operate/iris/langcache/self-managed/configuration/","summary":"Configure the LangCache Data Plane, Control Plane, and Identity Service through Helm values and config overlay Secrets.","tags":["docs","operate","iris"],"last_updated":"2026-10-06T01:06:35+03:00","page_type":"content","content_hash":"7504e93c084a266f188f7a0caca24bd206ce6d8e8598c0cee37086e7d7d5a206","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `langcache` chart splits configuration into two layers for the Data\nPlane, the Control Plane, and (in bundled mode) the Identity Service:\n\n- **Non-secret structure**, set as Helm values (`dataplane.configData`,\n  `controlplane.configData`, `identityService.bundled.configData`) and\n  rendered into a ConfigMap by default.\n- **Redis URLs, the database registry, and the embedding credential**,\n  which never go in `values.yaml` or a rendered ConfigMap. Each component\n  reads its own pre-created overlay Secret, deep-merged over its rendered\n  base config at container startup. The chart passes each overlay as an\n  additional `--config` flag, so later files win.\n\nYou always create the overlay Secrets yourself; the chart only tells each\ncomponent where to mount and read them."},{"id":"data-plane-overlay","title":"Data Plane overlay","role":"content","text":"Create `dp-overlay.yaml`. Provide Metadata Redis and, when\n`dataplane.embedding.credentials.type: static`, the embedding credential.\nThe Data Plane has no database registry of its own — it resolves each\ncache's Cache Redis target from the `databaseUrls` the Control Plane already\npersisted in Metadata Redis at cache-creation time.\n\n[code example]\n\n[code example]\n\nPoint the chart at it, alongside the public (non-secret) embedding facts:\n\n[code example]"},{"id":"control-plane-overlay","title":"Control Plane overlay","role":"content","text":"Create `cp-overlay.yaml`. Provide the same Metadata Redis as the Data Plane,\nplus the `databases` registry — one entry per Cache Redis target, keyed by a\nlogical ID you choose. The Control Plane never receives an embedding\ncredential; it only needs the public provider/model/dimensions contract\nthat the chart renders from `dataplane.embedding.*`.\n\n[code example]\n\n[code example]\n\n[code example]\n\nThe `databases` map must use the same logical IDs your operators will pass\nas `databaseId` when creating caches through the Control Plane API. The\nchart derives the Control Plane's `embedders` config from\n`dataplane.embedding.provider` and `dataplane.embedding.models.*`. On-prem\ncache creation uses that single provider/model/dimensions contract and\ndoes not accept per-cache embedding credentials."},{"id":"identity-service-metadata-bundled-mode-only","title":"Identity Service metadata (bundled mode only)","role":"content","text":"When `identityService.mode: bundled` is set, the bundled Identity\nService needs its own Metadata Redis connection. This connection can be the same Redis\ninstance as the Control Plane's Metadata Redis, in a separate keyspace (records use the\nfixed key prefix `iris:apikey`).\n\n[code example]\n\n[code example]\n\n[code example]\n\nIf you use `identityService.mode: external` instead, there is no Identity\nService overlay to create here; see\n[Authentication and authorization](https://redis.io/docs/latest/operate/iris/langcache/self-managed/authentication)."},{"id":"multiple-overlay-secrets","title":"Multiple overlay Secrets","role":"content","text":"`dataplane.secrets.additionalSecrets` and\n`controlplane.secrets.additionalSecrets` accept a list of extra pre-created\nSecret names, layered in order after the primary overlay (later wins). Use\nthis to split, for example, Redis connection details from the embedding\ncredential across separately rotated Secrets."},{"id":"treat-overlay-content-as-sensitive","title":"Treat overlay content as sensitive","role":"content","text":"Store `dp-overlay.yaml`, `cp-overlay.yaml`, and `ids-metadata.yaml` outside\nyour values files and outside git, the same as any other credential\nmaterial."},{"id":"next-steps","title":"Next steps","role":"content","text":"With your overlay Secrets and values ready, continue to\n[Deploy self-managed LangCache](https://redis.io/docs/latest/operate/iris/langcache/self-managed/deploy)."}],"examples":[{"id":"data-plane-overlay-ex0","language":"yaml","code":"metadata:\n  urls:\n    - rediss://default:<password>@metadata-redis:6380\n\nembedding:\n  credentials:\n    api_key: \"<embedding-api-key>\"","section_id":"data-plane-overlay"},{"id":"data-plane-overlay-ex1","language":"bash","code":"kubectl -n <namespace-name> create secret generic dp-overlay \\\n  --from-file=overlay.yaml=./dp-overlay.yaml","section_id":"data-plane-overlay"},{"id":"data-plane-overlay-ex2","language":"yaml","code":"dataplane:\n  secrets:\n    secretName: dp-overlay\n  embedding:\n    provider: openai\n    endpoint:\n      baseURL: https://api.openai.com/v1\n    credentials:\n      type: static\n    models:\n      defaultEmbeddingModel: text-embedding-3-small\n      dimensions: 1536","section_id":"data-plane-overlay"},{"id":"control-plane-overlay-ex0","language":"yaml","code":"metadata:\n  urls:\n    - rediss://default:<password>@metadata-redis:6380\n\ndatabases:\n  cache-primary:\n    name: cache-primary\n    urls:\n      - rediss://default:<password>@cache-primary:6380","section_id":"control-plane-overlay"},{"id":"control-plane-overlay-ex1","language":"bash","code":"kubectl -n <namespace-name> create secret generic cp-overlay \\\n  --from-file=overlay.yaml=./cp-overlay.yaml","section_id":"control-plane-overlay"},{"id":"control-plane-overlay-ex2","language":"yaml","code":"controlplane:\n  secrets:\n    secretName: cp-overlay\n  configData:\n    profile: prod","section_id":"control-plane-overlay"},{"id":"identity-service-metadata-bundled-mode-only-ex0","language":"yaml","code":"metadata:\n  urls:\n    - rediss://default:<password>@metadata-redis:6380","section_id":"identity-service-metadata-bundled-mode-only"},{"id":"identity-service-metadata-bundled-mode-only-ex1","language":"bash","code":"kubectl -n <namespace-name> create secret generic ids-metadata \\\n  --from-file=metadata.yaml=./ids-metadata.yaml","section_id":"identity-service-metadata-bundled-mode-only"},{"id":"identity-service-metadata-bundled-mode-only-ex2","language":"yaml","code":"identityService:\n  mode: bundled\n  bundled:\n    metadata:\n      existingSecret: ids-metadata","section_id":"identity-service-metadata-bundled-mode-only"}]}
{"schema_version":2,"id":"operate/iris/langcache/self-managed/control-plane-api-reference","title":"LangCache Control Plane API reference","url":"https://redis.io/docs/latest/operate/iris/langcache/self-managed/control-plane-api-reference/","summary":"","tags":[],"last_updated":"2026-09-10T16:06:39+03:00","page_type":"content","content_hash":"4269953bdaac60e4edaa4009e4d5a543e086d7ac92ee42fb6dd88e2071dfedd5","sections":[{"id":"content","title":"Content","role":"content","text":"All Control Plane API requests require the admin bearer token:\n\n[code example]"}],"examples":[{"id":"content-ex0","language":"http","code":"Authorization: Bearer <admin-token>","section_id":"content"}]}
{"schema_version":2,"id":"operate/iris/langcache/self-managed/deploy","title":"Deploy self-managed LangCache","url":"https://redis.io/docs/latest/operate/iris/langcache/self-managed/deploy/","summary":"Deploy self-managed LangCache with the langcache Helm chart.","tags":["docs","operate","iris"],"last_updated":"2026-10-06T12:03:53+03:00","page_type":"content","content_hash":"3f4b77bd94c6bcc54dff16fd9257e6bf28d05d268c4b2b14257feb847af20112","sections":[{"id":"overview","title":"Overview","role":"overview","text":"One `helm install` of the `langcache` chart deploys the Data Plane, the\nControl Plane, and either a bundled Identity Service or a connection to an\nexternal Identity Service. There is no separate lighter-weight install\npath; every self-managed LangCache deployment uses the Data Plane, Control\nPlane, and one Identity Service mode.\n\nBefore you begin, review [prerequisites](https://redis.io/docs/latest/operate/iris/langcache/self-managed/prerequisites)\nand prepare the config overlays described in\n[Configuration](https://redis.io/docs/latest/operate/iris/langcache/self-managed/configuration).\n\nYou use three credentials along the way: the Control Plane admin token, the Identity Service\ncontrol token, and an agent key. The chart generates the first two. For what each credential\nunlocks, see [Credentials](https://redis.io/docs/latest/operate/iris/langcache/self-managed/authentication#credentials)."},{"id":"choose-an-identity-service-mode","title":"Choose an Identity Service mode","role":"content","text":"Decide before you install:\n\n| Mode | Use when | Values |\n| --- | --- | --- |\n| Bundled | This is your first LangCache install, or your suite doesn't already run an Identity Service. | `identityService.mode: bundled` |\n| External | Your suite already runs an Identity Service (for example, alongside self-managed Redis Agent Memory) and you want LangCache to share it. | `identityService.mode: external` |\n\nThis guide uses bundled mode. For external mode, see\n[Authentication and authorization](https://redis.io/docs/latest/operate/iris/langcache/self-managed/authentication#external-identity-service)\nfor the values and the coordination required with the Identity Service's\nowner."},{"id":"create-the-namespace","title":"Create the namespace","role":"content","text":"[code example]"},{"id":"create-the-required-secrets","title":"Create the required Secrets","role":"content","text":"Create the license Secret, shared by the Data Plane and Control Plane:\n\n[code example]\n\nCreate the config overlay Secrets described in\n[Configuration](https://redis.io/docs/latest/operate/iris/langcache/self-managed/configuration):\n\n[code example]"},{"id":"create-helm-values","title":"Create Helm values","role":"content","text":"Create `langcache-values.yaml`:\n\n[code example]\n\nThis is a minimal complete install. `controlplane.adminToken`,\n`controlplane.internalToken`, and `identityService.bundled.controlToken`\nall default to `autoGenerate: true`, so the chart mints those tokens for\nyou on first install; see\n[Authentication and authorization](https://redis.io/docs/latest/operate/iris/langcache/self-managed/authentication)\nto retrieve them, or set `existingSecret` to bring your own.\n\nThe chart renders the Control Plane's embedding contract from\n`dataplane.embedding.*`, so set the provider, model, and dimensions only\nunder `dataplane.embedding`."},{"id":"before-you-install-check-your-values","title":"Before you install: check your values","role":"content","text":"Check each item before you install and before you mint a key. If one is wrong, you see the symptom\nin its row. The rows are checklist items LC-CL-1 to LC-CL-3.\n\n| Row | Check | Symptom | Fix |\n| --- | --- | --- | --- |\n| <a id=\"lc-cl-1\"></a>LC-CL-1 | No image tag is set, or it exists on Docker Hub (`0.0.1` is the only published tag) | With `--atomic --wait`: `Error: INSTALLATION FAILED: release langcache failed, and has been uninstalled due to atomic being set: context deadline exceeded` after the wait times out; `kubectl get events` keeps `Failed to pull image \"redislabs/iris-langcache-data:1.0.0\": … not found` (also `-control`, `iris-identity-service`) | Remove `*.image.tag` |\n| <a id=\"lc-cl-2\"></a>LC-CL-2 | The Identity Service is reachable before you mint a key | `curl: (7) Failed to connect to localhost port 9200 after 0 ms: Couldn't connect to server` | Port-forward `svc/langcache-identity-service 9200:9200`, as in [Verify the deployment](#verify-the-deployment) |\n| <a id=\"lc-cl-3\"></a>LC-CL-3 | Every attribute you send was listed in `attributes` when the cache was created | `400`, `\"detail\":\"attributes: no attributes are configured for this cache.\"` | Create the cache with the attribute names, for example `\"attributes\": [\"topic\"]` |"},{"id":"install-the-chart","title":"Install the chart","role":"setup","text":"Add the Helm repository when installing from the public repository:\n\n[code example]\n\nInstall with `langcache` as the Helm release name:\n\n[code example]\n\nIf you installed from a chart package or a local checkout instead, replace\n`redis-ai/langcache --version 0.0.1` with the chart path (for\nexample `.` from the chart's own root directory).\n\nOn small clusters, install without `--atomic --wait`, then watch pod\nstatus:\n\n[code example]"},{"id":"verify-the-deployment","title":"Verify the deployment","role":"content","text":"[code example]\n\nPort-forward the Data Plane:\n\n[code example]\n\n[code example]\n\nPort-forward the Control Plane:\n\n[code example]\n\nRetrieve the auto-generated admin token, then create your first cache:\n\n[code example]\n\n[code example]\n\nPort-forward the Identity Service (bundled mode):\n\n[code example]\n\nRetrieve the auto-generated Identity Service control token:\n\n[code example]\n\nFor the full self-managed admin API schema, see the\n[Control Plane API reference](https://redis.io/docs/latest/operate/iris/langcache/self-managed/control-plane-api-reference).\n\nNext, mint an agent key through the Identity Service and start calling the\nData Plane; see\n[Authentication and authorization](https://redis.io/docs/latest/operate/iris/langcache/self-managed/authentication)\nand [API examples](https://redis.io/docs/latest/operate/iris/langcache/self-managed/api-examples)."},{"id":"update","title":"Update","role":"content","text":"[code example]"},{"id":"next-steps","title":"Next steps","role":"content","text":"- [Authentication and authorization](https://redis.io/docs/latest/operate/iris/langcache/self-managed/authentication) to mint agent keys and configure the Identity Service mode you chose.\n- [API examples](https://redis.io/docs/latest/operate/iris/langcache/self-managed/api-examples) to start calling the Data Plane.\n- [Operations](https://redis.io/docs/latest/operate/iris/langcache/self-managed/operations) for backups, secret rotation, and FIPS posture."}],"examples":[{"id":"create-the-namespace-ex0","language":"bash","code":"kubectl create namespace <namespace-name>","section_id":"create-the-namespace"},{"id":"create-the-required-secrets-ex0","language":"bash","code":"kubectl -n <namespace-name> create secret generic langcache-license \\\n  --from-file=license=./langcache.key","section_id":"create-the-required-secrets"},{"id":"create-the-required-secrets-ex1","language":"bash","code":"kubectl -n <namespace-name> create secret generic dp-overlay \\\n  --from-file=overlay.yaml=./dp-overlay.yaml\nkubectl -n <namespace-name> create secret generic cp-overlay \\\n  --from-file=overlay.yaml=./cp-overlay.yaml\nkubectl -n <namespace-name> create secret generic ids-metadata \\\n  --from-file=metadata.yaml=./ids-metadata.yaml","section_id":"create-the-required-secrets"},{"id":"create-helm-values-ex0","language":"yaml","code":"dataplane:\n  license:\n    existingSecret: langcache-license\n  secrets:\n    secretName: dp-overlay\n  embedding:\n    provider: openai\n    endpoint:\n      baseURL: https://api.openai.com/v1\n    credentials:\n      type: static\n    models:\n      defaultEmbeddingModel: text-embedding-3-small\n      dimensions: 1536\n\ncontrolplane:\n  secrets:\n    secretName: cp-overlay\n  configData:\n    profile: prod\n\nidentityService:\n  mode: bundled\n  bundled:\n    metadata:\n      existingSecret: ids-metadata","section_id":"create-helm-values"},{"id":"install-the-chart-ex0","language":"bash","code":"helm repo add redis-ai https://helm.redis.io/ai\nhelm repo update redis-ai\nhelm search repo redis-ai/langcache --versions","section_id":"install-the-chart"},{"id":"install-the-chart-ex1","language":"bash","code":"helm install langcache redis-ai/langcache \\\n  --version 0.0.1 \\\n  --namespace <namespace-name> \\\n  -f langcache-values.yaml \\\n  --atomic --wait","section_id":"install-the-chart"},{"id":"install-the-chart-ex2","language":"bash","code":"kubectl -n <namespace-name> get pods -w","section_id":"install-the-chart"},{"id":"verify-the-deployment-ex0","language":"bash","code":"kubectl -n <namespace-name> rollout status deployment/langcache\nkubectl -n <namespace-name> rollout status deployment/langcache-controlplane\nkubectl -n <namespace-name> rollout status deployment/langcache-identity-service","section_id":"verify-the-deployment"},{"id":"verify-the-deployment-ex1","language":"bash","code":"kubectl -n <namespace-name> port-forward svc/langcache 9000:9000","section_id":"verify-the-deployment"},{"id":"verify-the-deployment-ex2","language":"bash","code":"curl http://localhost:9000/health","section_id":"verify-the-deployment"},{"id":"verify-the-deployment-ex3","language":"bash","code":"kubectl -n <namespace-name> port-forward svc/langcache-controlplane 9100:9100","section_id":"verify-the-deployment"},{"id":"verify-the-deployment-ex4","language":"bash","code":"kubectl -n <namespace-name> get secret langcache-controlplane-admin-token \\\n  -o jsonpath=\"{.data.token}\" | base64 -d","section_id":"verify-the-deployment"},{"id":"verify-the-deployment-ex5","language":"bash","code":"curl -sS -X POST http://localhost:9100/v1/caches \\\n  -H \"Authorization: Bearer <admin-token>\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"my-cache\",\n    \"databaseId\": \"cache-primary\",\n    \"defaultSearchThreshold\": 0.9,\n    \"defaultTtlMillis\": -1,\n    \"attributes\": []\n  }'","section_id":"verify-the-deployment"},{"id":"verify-the-deployment-ex6","language":"bash","code":"kubectl -n <namespace-name> port-forward svc/langcache-identity-service 9200:9200","section_id":"verify-the-deployment"},{"id":"verify-the-deployment-ex7","language":"bash","code":"kubectl -n <namespace-name> get secret langcache-identity-service-control-token -o jsonpath=\"{.data.token}\" | base64 -d","section_id":"verify-the-deployment"},{"id":"update-ex0","language":"bash","code":"helm upgrade langcache redis-ai/langcache \\\n  --version <chart-version> \\\n  --namespace <namespace-name> \\\n  -f langcache-values.yaml \\\n  --atomic --wait","section_id":"update"}]}
{"schema_version":2,"id":"operate/iris/langcache/self-managed","title":"Self-managed LangCache","url":"https://redis.io/docs/latest/operate/iris/langcache/self-managed/","summary":"Deploy, configure, secure, and operate LangCache on a self-managed Kubernetes cluster.","tags":["docs","operate","iris"],"last_updated":"2026-10-05T16:11:44+01:00","children":[{"id":"operate/iris/langcache/self-managed/prerequisites","summary":"Review software, Redis, network, Secret, image, and sizing prerequisites for self-managed LangCache.","title":"Self-managed LangCache prerequisites","url":"https://redis.io/docs/latest/operate/iris/langcache/self-managed/prerequisites/"},{"id":"operate/iris/langcache/self-managed/configuration","summary":"Configure the LangCache Data Plane, Control Plane, and Identity Service through Helm values and config overlay Secrets.","title":"Configuration","url":"https://redis.io/docs/latest/operate/iris/langcache/self-managed/configuration/"},{"id":"operate/iris/langcache/self-managed/deploy","summary":"Deploy self-managed LangCache with the langcache Helm chart.","title":"Deploy self-managed LangCache","url":"https://redis.io/docs/latest/operate/iris/langcache/self-managed/deploy/"},{"id":"operate/iris/langcache/self-managed/authentication","summary":"Configure LangCache self-managed Control Plane authentication and Data Plane agent-key authentication through the Identity Service.","title":"Authentication and authorization","url":"https://redis.io/docs/latest/operate/iris/langcache/self-managed/authentication/"},{"id":"operate/iris/langcache/self-managed/api-examples","summary":"Use curl examples with the LangCache self-managed Control Plane, Identity Service, and Data Plane APIs.","title":"Self-managed API examples","url":"https://redis.io/docs/latest/operate/iris/langcache/self-managed/api-examples/"},{"id":"operate/iris/langcache/self-managed/control-plane-api-reference","summary":"","title":"LangCache Control Plane API reference","url":"https://redis.io/docs/latest/operate/iris/langcache/self-managed/control-plane-api-reference/"},{"id":"operate/iris/langcache/self-managed/operations","summary":"Operate self-managed LangCache with backups, secret rotation, updates, FIPS posture, and support bundles.","title":"Operations","url":"https://redis.io/docs/latest/operate/iris/langcache/self-managed/operations/"},{"id":"operate/iris/langcache/self-managed/reference","summary":"Review self-managed LangCache configuration, troubleshooting guidance, and reference links.","title":"Configuration and troubleshooting","url":"https://redis.io/docs/latest/operate/iris/langcache/self-managed/reference/"}],"page_type":"content","content_hash":"7ed13b01ebde145d55823222e90183975f54508e0343cd5658d6c4e88ce5888b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"LangCache is a semantic caching service that stores LLM responses for faster,\ncheaper retrieval. Applications send prompts to LangCache, which either returns a\ncached response for a semantically similar prior prompt or calls out to your\nembedding provider and stores a new entry when there is no match.\n\nThis guide covers deployment, configuration, security, and operations for\nself-managed LangCache.\n\nThe [LangCache API](https://redis.io/docs/latest/develop/ai/context-engine/langcache/api-reference)\nis the shared Data Plane API for Redis Cloud and self-managed deployments. The\n[Control Plane API reference](https://redis.io/docs/latest/operate/iris/langcache/self-managed/control-plane-api-reference)\ndocuments the self-managed admin endpoints for caches.\n\n> [!NOTE]\n> Self-managed LangCache is available as a private preview. You need a license\n> key to deploy it. Contact your Redis representative or\n> [contact sales](https://redis.io/contact/)."},{"id":"what-you-are-deploying","title":"What you are deploying","role":"content","text":"One `helm install` of the `langcache` chart always creates the Data Plane and\nthe Control Plane, plus either a bundled or an external Identity Service.\nThere is no lighter-weight install of only the Data Plane for self-managed\nLangCache. Every cache is created and managed through the Control Plane, and\nevery Data Plane request is authenticated by the Identity Service.\n\n| Component | Purpose | Default service |\n| --- | --- | --- |\n| LangCache Data Plane | Cache-scoped runtime API for set, search, and flush. | `langcache:9000` |\n| LangCache Control Plane | Admin API for creating and managing caches. | `langcache-controlplane:9100` |\n| Identity Service | Issues and validates the agent keys the Data Plane requires. Bundled by the chart (default) or an external instance your suite already runs. | `langcache-identity-service:9200` (bundled mode) |\n| Cache Redis | Holds cache entries and RediSearch vector indexes. Registered by ID in the Control Plane's database registry — the Data Plane has no database registry of its own. | Customer-provided |\n| Metadata Redis | Holds Control Plane cache records. Can be the same Redis instance as Cache Redis, in a separate keyspace. | Customer-provided |"},{"id":"how-the-components-work-together","title":"How the components work together","role":"content","text":"1. Platform admins use the Control Plane to create and manage caches,\n   selecting a Cache Redis target by `databaseId` from the Control Plane's\n   own database registry.\n1. The Control Plane writes cache records to Metadata Redis, including the\n   resolved Redis URLs for that cache, and synchronously provisions the\n   RediSearch vector index in Cache Redis.\n1. Platform admins mint agent keys through the Identity Service, granting\n   `lc-cache:<cache-id>` permissions.\n1. Agents and applications call the Data Plane with a cache ID and an agent\n   key.\n1. The Data Plane introspects the key against the Identity Service, reads\n   the cache's metadata (including its Redis URLs) from Metadata Redis, and\n   reads or writes entries in Cache Redis."},{"id":"api-surfaces","title":"API surfaces","role":"content","text":"All Data Plane APIs are scoped to a cache. A cache is the logical isolation\nboundary for cached entries.\n\n| API surface | Endpoint prefix | Purpose |\n| --- | --- | --- |\n| Cache entries | `/v1/caches/{cacheId}/entries` | Set, search, and delete cached entries. |\n| Cache flush | `/v1/caches/{cacheId}/flush` | Flush all entries in a cache. |\n| Cache health | `/v1/caches/{cacheId}/health` | Cache-scoped health status. |\n| Control Plane | `/v1/caches`, `/v1/embedding-providers` | Self-managed administration for caches. |\n| Identity Service | `/v1/api-keys` | Mint, list, update, revoke, and rotate agent keys and their cache grants. |\n\nThe [LangCache API](https://redis.io/docs/latest/develop/ai/context-engine/langcache/api-reference)\nreference does not yet document cache health; for that, use\n[API examples](https://redis.io/docs/latest/operate/iris/langcache/self-managed/api-examples)\nuntil the shared schema is updated.\n\nStart with [prerequisites](https://redis.io/docs/latest/operate/iris/langcache/self-managed/prerequisites),\nthen follow [Deploy self-managed LangCache](https://redis.io/docs/latest/operate/iris/langcache/self-managed/deploy)."}],"examples":[]}
{"schema_version":2,"id":"operate/iris/langcache/self-managed/operations","title":"Operations","url":"https://redis.io/docs/latest/operate/iris/langcache/self-managed/operations/","summary":"Operate self-managed LangCache with backups, secret rotation, updates, FIPS posture, and support bundles.","tags":["docs","operate","iris"],"last_updated":"2026-10-06T01:06:35+03:00","page_type":"content","content_hash":"5bf2b21f757d75fc0c46a380dae37a50d5b4123965a18b144f07d9af2fb7e3d8","sections":[{"id":"backups","title":"Backups","role":"overview","text":"- Back up Cache Redis according to your cache-retention policy. LangCache\n  can rebuild the RediSearch index from existing entries, but losing the\n  underlying hashes loses cached responses.\n- Back up Metadata Redis. Losing metadata removes Control Plane cache\n  records — including the `databaseUrls` the Data Plane depends on to reach\n  Cache Redis.\n- Back up the Identity Service's own metadata Redis (bundled mode). Losing\n  it removes agent-key and grant records.\n- Back up any external secret manager material used to recreate the config\n  overlay, license, and token Secrets."},{"id":"secret-rotation","title":"Secret rotation","role":"content","text":"The chart cannot see the contents of Secrets you bring yourself\n(`existingSecret` values), so it can't roll pods automatically when you\nupdate one. Every rotatable Secret has a matching `existingSecretChecksum`\nvalue: update the Secret, then bump the checksum and run `helm upgrade` to\nforce a rollout.\n\nRotate the config overlay Secrets (Redis URLs, database registry, embedding\ncredential):\n\n[code example]\n\n[code example]\n\nRotate the license the same way, using `dataplane.license.existingSecretChecksum`.\n\n**Linux:**\n\n[code example]\n\n**macOS:**\n\n[code example]\n\n\n\nApply the updated values and verify the workloads rolled:\n\n[code example]\n\nRotating an auto-generated token (admin token, internal token, Identity\nService control token, or the Data Plane's Identity Service runtime\ncredential) is different: those Secrets are Helm-managed, not\n`existingSecret`, so there is no checksum to bump. The chart looks up the\nexisting Secret on every `helm upgrade` and keeps its value stable unless\nthe Secret is gone, so either set `autoGenerate: false` and supply a new\n`existingSecret`, or delete the underlying Secret (it carries a\n`helm.sh/resource-policy: keep` annotation, so `helm uninstall` won't do\nthis for you) and let the next `helm upgrade` mint a fresh one.\n\nRotate agent keys minted for LangCache caches through the Identity Service;\nsee [API examples](https://redis.io/docs/latest/operate/iris/langcache/self-managed/api-examples#identity-service-api-examples).\nRotation returns a **new** `keyId` with the new token. Use the new `keyId` for later update,\nrotate, and revoke calls. The old key stays valid for `graceSeconds` (default 3600, maximum\n604800), and the response includes `oldExpiresAt`. Revoking the old `keyId` ends its grace\nperiod early."},{"id":"updates","title":"Updates","role":"content","text":"For every update:\n\n1. Update chart version and image tags.\n2. Recalculate `existingSecretChecksum` values for any changed overlay or\n   license Secrets.\n3. Run `helm upgrade`.\n4. Verify pod rollout and health endpoints.\n\n[code example]\n\nOn small clusters, avoid `--atomic` unless the timeout and capacity are\nknown to be sufficient."},{"id":"helm-tests","title":"Helm tests","role":"content","text":"The chart can render `helm test` resources when `tests.enabled: true`. This\nrenders the shared security-profile check and the minimal RBAC it needs:\n\n[code example]\n\n`tests.smoke.enabled: true` independently gates an additional smoke test\nthat proves authenticated set/search/delete of one uniquely generated cache\nentry. It expects a `READY` cache and a valid agent key to already\nexist — create the cache through the Control Plane and mint the key\nthrough the Identity Service first, then store the key's plaintext token\nin a Secret and reference it:\n\n[code example]"},{"id":"fips-oriented-posture","title":"FIPS-oriented posture","role":"content","text":"In a valid `security.profile: fips` deployment, the chart sets\n`GODEBUG=fips140=on` on the Data Plane and Control Plane containers:\n\n[code example]\n\nUnder this posture, the chart:\n\n- refuses to render with `identityService.mode: bundled` — the bundled\n  Identity Service's in-cluster Service has no TLS termination of its own,\n  so its address is always `http://`, which the profile forbids. Use\n  `identityService.mode: external` with a TLS-fronted Identity Service\n  instead.\n- refuses `identityService.external.baseURL` unless it is `https://`.\n  `identityService.external.allowInsecureTransport: true` is a real opt-out\n  outside `fips`, but is not honored under `fips`.\n\nThis is not a formal FIPS 140 compliance or validation claim. Treat it as an\nopt-in deployment posture and guardrail that must still be reviewed against\nyour compliance boundary."},{"id":"support-bundles-and-preflight","title":"Support bundles and preflight","role":"compatibility","text":"`supportPackage.enabled: true` (the default) ships a namespace-scoped\n[Troubleshoot](https://troubleshoot.sh) spec as a ConfigMap. Collect a\nbundle with:\n\n[code example]\n\nThe bundle excludes Secret contents, license data, Redis URLs,\nadmin/internal/runtime credentials, API-key material, prompts, responses,\nvectors, and cache records — see the redactor spec shipped in the same\nnamespace (`langcache-support-redactors`) for the exact rules.\n\n`preflight.enabled: true` (the default) ships a cluster preflight check as\nboth a ConfigMap and a standalone file in the chart package for `kubectl preflight`. Pull\nthe chart and run it before you install:\n\n[code example]"},{"id":"network-policy","title":"Network policy","role":"content","text":"For every Identity Service mode, prevent callers from bypassing your\nintended access path (gateway, ingress, or trusted-internal-only) and\nreaching the Data Plane, Control Plane, or bundled Identity Service Service\ndirectly. Write a NetworkPolicy for your cluster's CNI that default-denies\ningress to the `langcache`, `langcache-controlplane`, and (bundled mode)\n`langcache-identity-service` Services, then allow TCP traffic on their\nrespective ports (`9000`, `9100`, `9200`) from approved callers only."},{"id":"see-also","title":"See also","role":"related","text":"- [Configuration](https://redis.io/docs/latest/operate/iris/langcache/self-managed/configuration) for the Redis roles being backed up and rotated here.\n- [Authentication and authorization](https://redis.io/docs/latest/operate/iris/langcache/self-managed/authentication) for how the tokens and agent keys rotated above are used.\n- [Configuration and troubleshooting](https://redis.io/docs/latest/operate/iris/langcache/self-managed/reference) for symptoms and fixes if a rotation or update doesn't take effect."}],"examples":[{"id":"secret-rotation-ex0","language":"bash","code":"kubectl -n <namespace-name> create secret generic dp-overlay \\\n  --from-file=overlay.yaml=./dp-overlay.yaml \\\n  --dry-run=client -o yaml | kubectl apply -f -\nkubectl -n <namespace-name> create secret generic cp-overlay \\\n  --from-file=overlay.yaml=./cp-overlay.yaml \\\n  --dry-run=client -o yaml | kubectl apply -f -","section_id":"secret-rotation"},{"id":"secret-rotation-ex1","language":"yaml","code":"dataplane:\n  secrets:\n    secretName: dp-overlay\n    existingSecretChecksum: \"<new-dp-overlay-checksum>\"\ncontrolplane:\n  secrets:\n    secretName: cp-overlay\n    existingSecretChecksum: \"<new-cp-overlay-checksum>\"","section_id":"secret-rotation"},{"id":"secret-rotation-ex2","language":"bash","code":"sha256sum ./dp-overlay.yaml | awk '{print $1}'","section_id":"secret-rotation"},{"id":"secret-rotation-ex3","language":"bash","code":"shasum -a 256 ./dp-overlay.yaml | awk '{print $1}'","section_id":"secret-rotation"},{"id":"secret-rotation-ex4","language":"bash","code":"helm upgrade langcache redis-ai/langcache \\\n  --version <chart-version> \\\n  --namespace <namespace-name> \\\n  -f langcache-values.yaml\n\nkubectl -n <namespace-name> rollout status deployment/langcache\nkubectl -n <namespace-name> rollout status deployment/langcache-controlplane","section_id":"secret-rotation"},{"id":"updates-ex0","language":"bash","code":"helm upgrade langcache redis-ai/langcache \\\n  --version <chart-version> \\\n  --namespace <namespace-name> \\\n  -f langcache-values.yaml \\\n  --atomic --wait","section_id":"updates"},{"id":"helm-tests-ex0","language":"bash","code":"helm upgrade --install langcache redis-ai/langcache \\\n  --version <chart-version> \\\n  --namespace <namespace-name> \\\n  -f langcache-values.yaml \\\n  --set tests.enabled=true\n\nhelm test langcache --logs","section_id":"helm-tests"},{"id":"helm-tests-ex1","language":"yaml","code":"tests:\n  enabled: true\n  smoke:\n    enabled: true\n    cacheID: <the cache ID you created>\n    apiKey:\n      existingSecret: langcache-smoke-key","section_id":"helm-tests"},{"id":"fips-oriented-posture-ex0","language":"yaml","code":"security:\n  profile: fips","section_id":"fips-oriented-posture"},{"id":"support-bundles-and-preflight-ex0","language":"bash","code":"kubectl support-bundle --namespace <namespace-name> --load-cluster-specs \\\n  -l troubleshoot.sh/kind=support-bundle","section_id":"support-bundles-and-preflight"},{"id":"support-bundles-and-preflight-ex1","language":"bash","code":"helm pull redis-ai/langcache --version 0.0.1 --untar\nkubectl preflight langcache/support/langcache-preflight.yaml","section_id":"support-bundles-and-preflight"}]}
{"schema_version":2,"id":"operate/iris/langcache/self-managed/prerequisites","title":"Self-managed LangCache prerequisites","url":"https://redis.io/docs/latest/operate/iris/langcache/self-managed/prerequisites/","summary":"Review software, Redis, network, Secret, image, and sizing prerequisites for self-managed LangCache.","tags":["docs","operate","iris"],"last_updated":"2026-10-06T12:03:53+03:00","page_type":"content","content_hash":"135c1c937928b2f222a8cdb823d992d6d5a279c0183de1eb490b1c1f1984011a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"LangCache self-managed is distributed as container images on Docker Hub plus\nthe `langcache` Helm chart. One `helm install` of the chart deploys the\nLangCache Data Plane, the LangCache Control Plane, and either a bundled\nIdentity Service or a connection to an external Identity Service.\n\nYou provide the Redis databases, embedding provider credentials, Kubernetes\nexposure, and license material used by the deployment.\n\n> [!NOTE]\n> This guide is for system administrators deploying LangCache on a self-managed\n> Kubernetes cluster."},{"id":"what-you-need","title":"What you need","role":"content","text":"| Item | Where it comes from |\n| ---- | ------------------- |\n| Container images | `redislabs/iris-langcache-data`, `redislabs/iris-langcache-control`, and (bundled Identity Service) `redislabs/iris-identity-service` on Docker Hub |\n| Helm chart | `langcache` chart, published to `https://helm.redis.io/ai` (the same repository as the self-managed Redis Agent Memory chart), or a chart package provided by Redis. |\n| Redis databases | You provide Metadata Redis and one or more Cache Redis databases |\n| License key | Contact your Redis representative or [contact sales](https://redis.io/contact/). |\n| Provider credentials | You provide embedding provider credentials (currently an OpenAI-compatible provider) |"},{"id":"required-software","title":"Required software","role":"content","text":"| Software | Minimum version | Purpose |\n| -------- | --------------- | ------- |\n| Kubernetes | 1.23+ | Orchestration; the chart renders an `autoscaling/v2` HorizontalPodAutoscaler |\n| kubectl | 1.23+ | Kubernetes CLI |\n| Helm | 3.x | Package manager |"},{"id":"redis-databases","title":"Redis databases","role":"content","text":"The Helm chart does not deploy Redis databases. Provision them outside the\nchart and register them through the Control Plane's and Data Plane's config\noverlays (see [Configuration](https://redis.io/docs/latest/operate/iris/langcache/self-managed/configuration)).\n\nCache Redis must support RediSearch with vector search, because LangCache\ncreates a RediSearch vector index per cache. Metadata Redis does not need\nthat capability.\n\n| Redis database | Required | Registered in | Purpose |\n| --- | --- | --- | --- |\n| Metadata Redis | Always | Both the Data Plane's and Control Plane's config overlays (same URLs, same keyspace) | Cache records written by the Control Plane, read by the Data Plane. |\n| Cache Redis (one or more) | Always | The Control Plane's config overlay only, as a `databases` registry entry keyed by a logical `databaseId` | Cache entry hashes and RediSearch vector indexes. The Data Plane has no database registry of its own — it resolves each cache's Redis URLs from the metadata the Control Plane already persisted at cache-creation time. |\n| Identity Service metadata Redis (bundled mode only) | When `identityService.mode: bundled` | The bundled Identity Service's own config overlay | Agent-key and grant records. Can be the same Redis instance as Metadata Redis, in a separate keyspace (records use the fixed key prefix `iris:apikey`). |\n\nFor a lab deployment, these Redis roles can point at the same Redis endpoint\nif it has the required modules and capacity. For production, separate them\nso cache data and control metadata can be scaled, backed up, and operated\nindependently."},{"id":"metadata-redis-durability","title":"Metadata Redis durability","role":"content","text":"Metadata Redis is small compared with Cache Redis, but it is operationally\ncritical. Use persistent storage, Redis authentication, network isolation,\nand TLS where required. Avoid eviction of metadata keys; losing metadata\nremoves Control Plane cache records."},{"id":"network-access","title":"Network access","role":"content","text":"- **Connected install:** the cluster must be able to pull the LangCache and\n  Identity Service images from Docker Hub (or your mirrored registry) and\n  reach `https://helm.redis.io/ai`.\n- **Air-gapped install:** mirror the images into an internal registry and\n  use a locally downloaded chart package.\n- **Runtime access:** LangCache pods must reach the Redis databases and the\n  embedding provider endpoint used by the deployment. The Data Plane must\n  also reach the Identity Service (bundled or external); the Control Plane\n  must reach Metadata Redis and every registered Cache Redis database.\n- **Data Plane exposure:** use NetworkPolicy, ingress, gateway, service mesh,\n  private load balancer, or equivalent controls to restrict API access."},{"id":"credentials-and-secrets","title":"Credentials and Secrets","role":"content","text":"The chart never puts Redis URLs, the database registry, or the embedding\ncredential in `values.yaml` or a rendered ConfigMap. Each of the Data Plane,\nControl Plane, and bundled Identity Service reads its own pre-created\noverlay Secret, deep-merged over its rendered base config at runtime. See\n[Configuration](https://redis.io/docs/latest/operate/iris/langcache/self-managed/configuration)\nfor the overlay content each component expects.\n\n| Secret | Required when | Default key |\n| --- | --- | --- |\n| LangCache license Secret | Always | `license` |\n| Data Plane config overlay Secret | Always | `overlay.yaml` |\n| Control Plane config overlay Secret | Always | `overlay.yaml` |\n| Identity Service metadata Secret | `identityService.mode: bundled` | `metadata.yaml` |\n| Control Plane admin token | Auto-generated by default, or bring your own | `token` |\n| Control Plane internal (grant-validation) token | Auto-generated by default, or bring your own | `token` |\n| Identity Service control token (bundled mode) | Auto-generated by default, or bring your own | `token` |\n| Data Plane's Identity Service runtime credential (external mode) | `identityService.mode: external` | `token`, minted by the suite-level Identity Service owner |"},{"id":"release-artifacts-and-image-tags","title":"Release artifacts and image tags","role":"content","text":"LangCache self-managed image tags use the release SemVer value, for example:\n\n[code example]\n\nUse the image tags listed for the release on Docker Hub or provided by\nRedis. Do not use floating image tags in production."},{"id":"air-gapped-and-private-registry-installs","title":"Air-gapped and private registry installs","role":"content","text":"Mirror the published images into your internal registry:\n\n[code example]\n\nIf the registry requires authentication, create an image pull Secret and\nreference it from `imagePullSecrets` in your values file:\n\n[code example]\n\n[code example]"},{"id":"system-requirements","title":"System requirements","role":"content","text":"Default chart values:\n\n| Component | Default | Purpose |\n| --------- | ------- | ------- |\n| LangCache Data Plane | 2 replicas with autoscaling enabled (2–10) | Data Plane API traffic |\n| LangCache Control Plane | 1 replica, no autoscaling | Admin API for caches |\n| Identity Service (bundled mode) | 1 replica | Agent-key issuance and introspection |\n\nThe chart preflight requires 1.5 CPU and 2.5 Gi of allocatable capacity for the default\nfootprint. Install one release per namespace: the default `fullnameOverride: langcache` fixes\nthe resource names.\n\nDuring a rolling update, Kubernetes may temporarily run old and new pods at\nthe same time. A small test cluster can run out of CPU during install or\nupgrade; size for the maximum rolling-update overlap, or reduce replicas\nexplicitly for a lab install."},{"id":"helm-values-to-review","title":"Helm values to review","role":"content","text":"The walkthroughs in this guide assume the chart's default\n`fullnameOverride: langcache`, which fixes the rendered resource names to\n`langcache` (Data Plane), `langcache-controlplane`, and\n`langcache-identity-service` (bundled mode). If you change it, update the\nrelease-derived names in the verification commands throughout this guide.\n\n| Area | Values | Use when |\n| --- | --- | --- |\n| Images | `dataplane.image.*`, `controlplane.image.*`, `identityService.bundled.image.*`, `imagePullSecrets` | Selecting a release or private registry image. |\n| Data Plane capacity | `dataplane.resources`, `dataplane.autoscaling.*` | Tuning request capacity or memory footprint. |\n| Networking | `dataplane.service.*`, `dataplane.ingress.*` | Exposing LangCache outside the cluster. |\n| Security posture | `security.profile` | Opting into the FIPS-oriented posture. |\n| Identity Service mode | `identityService.mode` (`bundled` or `external`) | Choosing whether this release runs its own Identity Service or joins one the suite already runs. |\n| Config overlays | `dataplane.secrets.*`, `controlplane.secrets.*`, `identityService.bundled.metadata.*` | Pointing the chart at your pre-created overlay Secrets. |\n| Private certificate authority (CA) | `tls.caCertSecret`, `tls.caCertKey` | Trusting a private CA for `rediss://` Redis, the embedding provider, or an external Identity Service. |\n| Agent-key check cache | `identityService.bundled.runtime.cache.*` | Tuning how long the Data Plane caches agent-key checks. |\n| Rotation | `*.existingSecretChecksum` fields throughout | Rolling pods after an externally managed Secret changes. |"},{"id":"next-steps","title":"Next steps","role":"content","text":"Continue to [Configuration](https://redis.io/docs/latest/operate/iris/langcache/self-managed/configuration)\nto prepare the Data Plane, Control Plane, and Identity Service overlay\nSecrets, then [Deploy self-managed LangCache](https://redis.io/docs/latest/operate/iris/langcache/self-managed/deploy)."}],"examples":[{"id":"release-artifacts-and-image-tags-ex0","language":"yaml","code":"dataplane:\n  image:\n    repository: redislabs/iris-langcache-data\n    tag: \"0.0.1\"\ncontrolplane:\n  image:\n    repository: redislabs/iris-langcache-control\n    tag: \"0.0.1\"\nidentityService:\n  bundled:\n    image:\n      repository: redislabs/iris-identity-service\n      tag: \"0.0.1\"","section_id":"release-artifacts-and-image-tags"},{"id":"air-gapped-and-private-registry-installs-ex0","language":"bash","code":"for image in iris-langcache-data iris-langcache-control iris-identity-service; do\n  docker pull redislabs/$image:0.0.1\n  docker tag redislabs/$image:0.0.1 \\\n    registry.example.com/redislabs/$image:0.0.1\n  docker push registry.example.com/redislabs/$image:0.0.1\ndone","section_id":"air-gapped-and-private-registry-installs"},{"id":"air-gapped-and-private-registry-installs-ex1","language":"bash","code":"kubectl -n <namespace-name> create secret docker-registry langcache-registry \\\n  --docker-server=registry.example.com \\\n  --docker-username=<username> \\\n  --docker-password=<password>","section_id":"air-gapped-and-private-registry-installs"},{"id":"air-gapped-and-private-registry-installs-ex2","language":"yaml","code":"imagePullSecrets:\n  - name: langcache-registry","section_id":"air-gapped-and-private-registry-installs"}]}
{"schema_version":2,"id":"operate/iris/langcache/self-managed/reference","title":"Configuration and troubleshooting","url":"https://redis.io/docs/latest/operate/iris/langcache/self-managed/reference/","summary":"Review self-managed LangCache configuration, troubleshooting guidance, and reference links.","tags":["docs","operate","iris"],"last_updated":"2026-10-06T01:06:35+03:00","page_type":"content","content_hash":"65d92e48ffa7e2824ad2ae20013102e734b3d001ce034a39447c174893daa5b0","sections":[{"id":"configuration-reference","title":"Configuration reference","role":"configuration","text":"Use these files to configure a self-managed deployment:\n\n| File | Purpose |\n| --- | --- |\n| `langcache-values.yaml` | Helm values for images, replicas, services, security posture, Identity Service mode, and non-secret config structure. |\n| `dp-overlay.yaml` | Data Plane's Metadata Redis URLs and (if static) embedding credential, deep-merged over the rendered config at startup. |\n| `cp-overlay.yaml` | Control Plane's Metadata Redis URLs and Cache Redis database registry, deep-merged over the rendered config at startup. |\n| `ids-metadata.yaml` | Bundled Identity Service's own Metadata Redis URLs. |\n| `langcache.key` | LangCache license file provided by Redis. |"},{"id":"external-secret-managers","title":"External secret managers","role":"content","text":"If you use an external secret manager, expose the license, overlay, and\ntoken material to the chart as Kubernetes Secrets and set the chart's\n`existingSecret` values to those Secret names."},{"id":"troubleshooting","title":"Troubleshooting","role":"errors","text":"| Symptom | Likely cause | Fix |\n| --- | --- | --- |\n| Docker pull fails for the configured image tag | Image tag is wrong or not published yet | Use the image tag listed for the release on Docker Hub or provided by Redis. |\n| Pod is stuck in `ImagePullBackOff` or `ErrImagePull` | Cluster cannot pull the configured image, image tag is wrong, registry requires credentials, or `imagePullSecrets` is missing/wrong | Verify `dataplane.image.*`/`controlplane.image.*`/`identityService.bundled.image.*`, registry reachability, and `imagePullSecrets`. |\n| `helm install --atomic --wait` times out and rolls back | Cluster is small or image pull/startup takes longer than Helm's default timeout | Install without `--atomic --wait`, or set a longer `--timeout` and ensure enough cluster capacity. |\n| Chart fails to render with `identityService.mode: bundled` and `security.profile: fips` | The FIPS posture forbids the bundled Identity Service's unencrypted in-cluster address | Use `identityService.mode: external` with a TLS-fronted Identity Service. |\n| Data Plane health fails | Pod not ready, overlay Secret missing/invalid, or Redis unavailable | Check pod logs and call `/health`, `/health/liveness`, and `/health/readiness`. |\n| Cache search or set requests fail with an index error | The RediSearch vector index for the cache was never provisioned, or Cache Redis does not support RediSearch with vector search | Check Control Plane cache status (`GET /v1/caches/{cacheId}`) and Cache Redis modules. |\n| Control Plane `CreateCache` returns `424` | Cache Redis for the resolved `databaseId` is unreachable or does not satisfy LangCache's Redis module requirements | Check the `databases.<id>.urls` connectivity and Redis modules in `cp-overlay.yaml`. |\n| Control Plane `CreateCache` returns `400` | A required field is missing, or a field fails validation — for example `databaseId` doesn't match `^[A-Za-z0-9-]+$`, `defaultSearchThreshold` is outside 0–1, or `attributes` has more than 5 entries | Check the request body against [Control Plane API reference](https://redis.io/docs/latest/operate/iris/langcache/self-managed/control-plane-api-reference). `CreateCache` has no embedding-related fields at all; the embedding provider, model, and dimensions always come from the deployment-wide contract, not the request. |\n| Agent receives `401` | Missing, malformed, revoked, expired, or invalid agent key | Check the `Authorization` header and key status through the Identity Service. |\n| Agent receives `503` | The Data Plane cannot reach the Identity Service | Check Data Plane connectivity to the Identity Service (bundled Service or `identityService.external.baseURL`). |\n| Agent receives `403` | Key exists but lacks the required `lc-cache:<cache-id>` grant or action | Update grants through the Identity Service's `/v1/api-keys/{keyId}` endpoint. |\n| Cache created by the Control Plane is not visible to the Data Plane | Data Plane and Control Plane overlays point at different Metadata Redis URLs | Make `dp-overlay.yaml` and `cp-overlay.yaml` use the same `metadata.urls`. |\n| `helm upgrade` doesn't roll a pod after rotating an overlay Secret | The matching `existingSecretChecksum` value wasn't bumped | Recalculate the SHA-256 checksum of the overlay file and set the corresponding `*.existingSecretChecksum` value. |\n| External Identity Service rejects LangCache's introspection calls | The Data Plane service credential is missing from the external Identity Service's `runtime.service_credentials`, or lacks `api-key-introspect` on `langcache` | Ask the Identity Service owner to add the credential with that scope; see [Authentication and authorization](https://redis.io/docs/latest/operate/iris/langcache/self-managed/authentication#external-identity-service). |\n| Minting or updating an agent key with a `langcache` grant fails (external Identity Service) | The external Identity Service's `product_validation.langcache` isn't configured against this release's Control Plane internal Service and `internalToken` | Ask the Identity Service owner to configure that product entry; see [Authentication and authorization](https://redis.io/docs/latest/operate/iris/langcache/self-managed/authentication#external-identity-service). |\n| `langcache-controlplane` restarts and `helm install --atomic --wait` fails with `context deadline exceeded`; log: `panic: database registry validation failed: database \"<id>\" (\"<name>\"): probe store db: ping redis: …` | A Cache Redis in the Control Plane's `databases` registry is unreachable | Check each `databases.<id>.urls` in `cp-overlay.yaml`. |\n| NetworkPolicy blocks expected traffic | The chart ships no sample policy, and your policy's selectors don't match the LangCache pods or their callers | Check your policy's selectors, the Helm release label `app.kubernetes.io/instance`, caller namespace, and caller pod labels. |\n| `helm install --atomic --wait` fails with `context deadline exceeded`; `kubectl get events` shows `Failed to pull image \"redislabs/iris-langcache-data:1.0.0\": … not found` | An image tag is set that isn't published | See [LC-CL-1](https://redis.io/docs/latest/operate/iris/langcache/self-managed/deploy#lc-cl-1). |\n| `curl: (7) Failed to connect to localhost port 9200 after 0 ms: Couldn't connect to server` | The Identity Service isn't port-forwarded | See [LC-CL-2](https://redis.io/docs/latest/operate/iris/langcache/self-managed/deploy#lc-cl-2). |\n| `400`, `\"detail\":\"attributes: no attributes are configured for this cache.\"` | The request sends an attribute the cache wasn't created with | See [LC-CL-3](https://redis.io/docs/latest/operate/iris/langcache/self-managed/deploy#lc-cl-3). |"},{"id":"references","title":"References","role":"related","text":"| Need | Reference |\n| --- | --- |\n| Helm chart repository | `https://helm.redis.io/ai`, chart `langcache` |\n| Helm chart values and README | `langcache/helm/` in the LangCache source repository, or the synced copy in `RedisLabs/redis-enterprise-helm` at `ai/charts/langcache` |\n| Container images | Docker Hub: [redislabs/iris-langcache-data](https://hub.docker.com/r/redislabs/iris-langcache-data/tags), [redislabs/iris-langcache-control](https://hub.docker.com/r/redislabs/iris-langcache-control/tags), [redislabs/iris-identity-service](https://hub.docker.com/r/redislabs/iris-identity-service/tags) |\n| LangCache API reference (Data Plane) | [LangCache API](https://redis.io/docs/latest/develop/ai/context-engine/langcache/api-reference) |\n| Control Plane API reference | [Control Plane API reference](https://redis.io/docs/latest/operate/iris/langcache/self-managed/control-plane-api-reference) |\n| LangCache overview | [LangCache overview](https://redis.io/docs/latest/develop/ai/context-engine/langcache) |\n| License key | Contact your Redis representative or [contact sales](https://redis.io/contact/) |"}],"examples":[]}
{"schema_version":2,"id":"operate/iris/langcache/use-langcache","title":"Use the LangCache API on Redis Cloud","url":"https://redis.io/docs/latest/operate/iris/langcache/use-langcache/","summary":"","aliases":["/operate/rc/langcache/use-langcache","/operate/rc/context-engine/langcache/use-langcache/"],"tags":["docs","operate","iris"],"last_updated":"2026-08-05T19:15:04+02:00","page_type":"content","content_hash":"33cd3a66f6c583f52963fc4a701ac659dc324d04597ed8c91c3693a62d1d035b","sections":[{"id":"content","title":"Content","role":"content","text":"You can use the [LangCache API and SDK](https://redis.io/docs/latest/develop/ai/context-engine/langcache/api-examples) from your client app to store and retrieve LLM, RAG, or agent responses. \n\nTo access the LangCache API, you need:\n\n- LangCache API base URL\n- LangCache service API key\n- Cache ID\n\nFor LangCache on Redis Cloud, the base URL and cache ID are available in the LangCache service's **Configuration** page in the [**Connectivity** section](https://redis.io/docs/latest/operate/iris/langcache/view-edit-cache#connectivity).\n\nThe LangCache API key is only available immediately after you create the LangCache service. If you lost this value, you will need to [replace the service API key](https://redis.io/docs/latest/operate/iris/langcache/view-edit-cache#replace-service-api-key) to be able to use the LangCache API.\n\nWhen you call the API, you need to pass the LangCache API key in the `Authorization` header as a Bearer token and the Cache ID as the `cacheId` path parameter. \n\nSee the [LangCache API and SDK examples](https://redis.io/docs/latest/develop/ai/context-engine/langcache/api-examples) for more information on how to use the LangCache API."}],"examples":[]}
{"schema_version":2,"id":"operate/iris/langcache/view-edit-cache","title":"View and edit LangCache service","url":"https://redis.io/docs/latest/operate/iris/langcache/view-edit-cache/","summary":"","aliases":["/operate/rc/langcache/view-edit-cache","/operate/rc/context-engine/langcache/view-edit-cache/"],"tags":["docs","operate","iris"],"last_updated":"2026-10-05T16:11:44+01:00","page_type":"content","content_hash":"2fd28facfa24f51abc82b1c3b9d7ecd900d4fc2d9d9defad98c640d5ca019600","sections":[{"id":"overview","title":"Overview","role":"overview","text":"After you have [created your first LangCache service](https://redis.io/docs/latest/operate/iris/langcache/create-service), selecting **LangCache** from the Redis Cloud Console menu will take you to the **LangCache Services** page.\n\nThis page displays a list of all LangCache services associated with your account.\n\n![The LangCache service in the LangCache service list.](https://redis.io/docs/latest/images/rc/langcache-service-list.png)\n\nSelect your LangCache service from the list to view the service's details."},{"id":"configuration-tab","title":"Configuration tab","role":"configuration","text":"The **Configuration** tab lets you view the details of your LangCache service. It contains the following sections:\n\n- The **Connectivity** section provides the connection details for your LangCache service.\n- The **General** section provides the cache settings for your LangCache service.\n- The **Actions** section lets you flush or delete your LangCache service."},{"id":"connectivity","title":"Connectivity","role":"content","text":"The **Connectivity** section provides the connection details for your LangCache service. \n\n![The connectivity settings for the LangCache service.](https://redis.io/docs/latest/images/rc/langcache-view-connectivity.png)\n\n| Setting name          |Description|\n|:----------------------|:----------|\n| **API Key** | The Bearer token for your LangCache API requests. |\n| **Cache ID** | The unique ID of your LangCache service. |\n| **URL List** | A list of base URLs for LangCache API requests. Use the closest URL for the best performance. |\n\nSelect the **Copy** button next to the Cache ID and API Base URL to copy them to the clipboard. If you lost the API key value or need to rotate the key, you can [generate a new service API key](#replace-service-api-key) at any time.\n\nSee [use the LangCache API](https://redis.io/docs/latest/operate/iris/langcache/use-langcache) for more information on how to use these values.\n\n#### Generate a new service API key {#replace-service-api-key}\n\nThe API key is only available immediately after you create the LangCache service. If you lost this value, or need to rotate the key, you can generate a new service key at any time.\n\nTo generate a new service key:\n\n1. Select **Generate key**.\n\n    ![The Generate Key button.](https://redis.io/docs/latest/images/rc/langcache-replace-key.png)\n\n1. A confirmation dialog will appear. Select **Confirm** to confirm.\n\n1. The new key will appear in a dialog box. Select **Copy** to copy the key to the clipboard.\n\n    ![The LangCache service key window. Use the Copy button to save the service key to the clipboard.](https://redis.io/docs/latest/images/rc/langcache-service-key.png)\n\n    > [!WARNING]\n    > After you generate a new service key, calls to the LangCache API with the old key will fail. <br/><br/>\n    >\n    > This is the only time the value of the user key is available. Save it to a secure location before closing the dialog box. <br/><br/>\n    >\n    > If you lose the service key value, you will need to generate a new key again."},{"id":"general","title":"General","role":"content","text":"The **General** section provides configuration details for your LangCache service.\n\n![The general settings for the LangCache service.](https://redis.io/docs/latest/images/rc/langcache-view-general.png)\n\n| Setting name          |Description|\n|:----------------------|:----------|\n| **Service name** | The name of the LangCache service. |\n| **Database** | The database that stores your cache data. |\n| **Similarity threshold** | The minimum similarity score required to consider a cached response a match. _(Editable)_ |\n| **TTL** | The number of seconds to cache entries before they expire. _(Editable)_ |\n| **Embedding Provider** | The embedding provider used for your service. |\n| **Embedding Model** | The embedding model used for your service. |\n| **Embedding Model API key** | The obfuscated API key for the embedding provider. |\n\nSome of these settings can be changed after cache creation. To do so, select the **Edit** button."},{"id":"attributes","title":"Attributes","role":"content","text":"The **Attributes** section provides the custom attributes defined for your LangCache service.\n\n![The custom attributes for the LangCache service.](https://redis.io/docs/latest/images/rc/langcache-view-attributes.png)\n\nYou can not edit custom attributes after cache creation."},{"id":"actions","title":"Actions","role":"content","text":"The **Actions** section lets you flush or delete your LangCache service.\n\n![The actions for the LangCache service.](https://redis.io/docs/latest/images/rc/langcache-view-actions.png)\n\n#### Flush cache\n\nFlushing the cache completely erases all cached data while preserving the service configuration and the search index used by the cache.\n\nTo flush the cache:\n\n1. Select **Flush**.\n\n1. A confirmation dialog will appear. Select **Flush** again to confirm.\n\nFlushing the cache is permanent and cannot be undone, and will result in cache misses until the cache is repopulated.\n\n#### Delete service\n\nDeleting your LangCache service permanently deletes all associated cached data, the service configuration, and the LangCache search index. It also immediately terminates all API keys associated with the service. Data stored in other indexes within the same database will remain unaffected.\n\nTo delete your LangCache service:\n\n1. Select **Delete**.\n\n1. A confirmation dialog will appear. Select the checkbox to confirm that you want to delete the service.\n\n1. Select **Delete** again to confirm.\n\nDeleting the LangCache service is permanent and cannot be undone."},{"id":"metrics-tab","title":"Metrics tab","role":"content","text":"The **Metrics** tab provides a series of graphs showing performance data for your LangCache service. See [Monitor a LangCache service](https://redis.io/docs/latest/operate/iris/langcache/monitor-cache) for more information."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/active-active/create-aa-crdb-cli","title":"Create Active-Active databases with crdb-cli","url":"https://redis.io/docs/latest/operate/kubernetes/active-active/create-aa-crdb-cli/","summary":"This section shows how to set up an Active-Active Redis Enterprise database on Kubernetes using the Redis Enterprise Software operator.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","page_type":"content","content_hash":"7c9854ce063656d62041da434af93b40199be5ff337ec67ff0be7684712a4d5f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n>  Versions 6.4.2 and later support the Active-Active database controller. This controller allows you to create Redis Enterprise Active-Active databases (REAADB) and Redis Enterprise remote clusters (RERC) with custom resources. We recommend using the [REAADB method for creating Active-Active databases](https://redis.io/docs/latest/operate/kubernetes/active-active/create-reaadb).\n\nOn Kubernetes, Redis Enterprise [Active-Active](https://redis.io/docs/latest/operate/rs/databases/active-active) databases provide read-and-write access to the same dataset from different Kubernetes clusters. For more general information about Active-Active, see the [Redis Enterprise Software docs](https://redis.io/docs/latest/operate/rs/databases/active-active).\n\nCreating an Active-Active database requires routing [network access](https://redis.io/docs/latest/operate/kubernetes/networking) between two Redis Enterprise clusters residing in different Kubernetes clusters. Without the proper access configured for each cluster, syncing between the databases instances will fail.\n\nThis process consists of:\n\n1. Documenting values to be used in later steps. It's important these values are correct and consistent.\n1. Editing the Redis Enterprise cluster (REC) spec file to include the `ActiveActive` section. This will be slightly different depending on the K8s distribution you are using.\n1. Creating the database with the `crdb-cli` command. These values must match up with values in the REC resource spec."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before creating Active-Active databases, you'll need admin access to two or more working Kubernetes clusters that each have:\n\n- Routing for external access with an [ingress resources](https://redis.io/docs/latest/operate/kubernetes/networking/ingress) (or [route resources](https://redis.io/docs/latest/operate/kubernetes/networking/routes) on OpenShift).\n- A working [Redis Enterprise cluster (REC)](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api) with a unique name.\n- Enough memory resources available for the database (see [hardware requirements](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/hardware-requirements)).\n\n> [!NOTE]\n>  The `activeActive` field and the `ingressOrRouteSpec` field cannot coexist in the same REC. If you configured your ingress via the `ingressOrRouteSpec` field in the REC, create your Active-Active database with the RedisEnterpriseActiveActiveDatabase (REAADB) custom resource."},{"id":"document-required-parameters","title":"Document required parameters","role":"content","text":"The most common mistake when setting up Active-Active databases is incorrect or inconsistent parameter values. The values listed in the resource file must match those used in the crdb-cli command.\n\n- **Database name** `<db-name>`:\n  - Description: Combined with ingress suffix to create the Active-Active database hostname\n  - Format: string\n  - Example value: `myaadb`\n  - How you get it: you choose\n  - The database name requirements are:\n       - Maximum of 63 characters\n       - Only letter, number, or hyphen (-) characters\n       - Starts with a letter; ends with a letter or digit.\n       - Database name is not case-sensitive\n\nYou'll need the following information for each participating Redis Enterprise cluster (REC):\n\n> [!NOTE]\n> You'll need to create DNS aliases to resolve your API hostname `<api-hostname>`,`<ingress-suffix>`, `<replication-hostname>` to the IP address for the ingress controller’s LoadBalancer (or routes in Openshift) for each database. To avoid entering multiple DNS records, you can use a wildcard in your alias (such as *.ijk.example.com).\n\n- **REC hostname** `<rec-hostname>`:\n  - Description: Hostname used to identify your Redis Enterprise cluster in the `crdb-cli` command. This MUST be different from other participating clusters.\n  - Format: `<rec-name>.<namespace>.svc.cluster.local`\n  - Example value: `rec01.ns01.svc.cluster.local`\n  - How to get it: List all your Redis Enterprise clusters\n      [code example]\n- **API hostname** `<api-hostname>`:\n  - Description: Hostname used to access the Redis Enterprise cluster API from outside the K8s cluster\n  - Format: string\n  - Example value: `api.ijk.example.com`\n- **Ingress suffix** `<ingress-suffix>`:\n  - Description: Combined with database name to create the Active-Active database hostname\n  - Format: string\n  - Example value: `-cluster.ijk.example.com`\n- [**REC admin credentials**](https://redis.io/docs/latest/operate/kubernetes/security/authentication/manage-rec-credentials) `<username> <password>`:\n  - Description: Admin username and password for the REC stored in a secret\n  - Format: string\n  - Example value: username: `user@example.com`, password: `something`\n  - How to get them:\n    [code example]\n- **Replication hostname** `<replication-hostname>`:\n  - Description: Hostname used inside the ingress for the database\n  - Format: `<db-name><ingress-suffix>`\n  - Example value: `myaadb-cluster.ijk.example.com`\n  - How to get it: Combine `<db-name>` and `<ingress-suffix`> values you documented above.\n- **Replication endpoint** `<replication-endpoint>`:\n  - Description: Endpoint used externally to contact the database\n  - Format: `<db-name><ingress-suffix>:443`\n  - Example value: `myaadb-cluster.ijk.example.com:443`\n  - How to get it:`<replication-hostname>:443`"},{"id":"add-activeactive-section-to-the-rec-resource-file","title":"Add `activeActive` section to the REC resource file","role":"content","text":"From inside your K8s cluster, edit your Redis Enterprise cluster (REC) resource to add the following to the `spec` section. Do this for each participating cluster.\n\n The operator uses the API hostname (`<api-hostname>`) to create an ingress to the Redis Enterprise cluster's API; this only happens once per cluster. Every time a new Active-Active database instance is created on this cluster, the operator creates a new ingress route to the database with the ingress suffix (`<ingress-suffix>`). The hostname for each new database will be in the format <nobr>`<db-name><ingress-suffix>`</nobr>."},{"id":"using-ingress-controller","title":"Using ingress controller","role":"content","text":"1. If your cluster uses an [ingress controller](https://redis.io/docs/latest/operate/kubernetes/networking/ingress), add the following to the `spec` section of your REC resource file.\n\n  Nginx:\n\n  [code example]\n\nHAproxy:\n\n  [code example]\n\n2. After the changes are saved and applied, you can verify a new ingress was created for the API.\n\n    [code example]\n\n3. Verify you can access the API from outside the K8s cluster.\n\n    [code example]\n\n    If the API call fails, create a DNS alias that resolves your API hostname (`<api-hostname>`) to the IP address for the ingress controller's LoadBalancer.\n\n4. Make sure you have DNS aliases for each database that resolve your API hostname `<api-hostname>`,`<ingress-suffix>`, `<replication-hostname>` to the IP address of the ingress controller’s LoadBalancer. To avoid entering multiple DNS records, you can use a wildcard in your alias (such as `*.ijk.example.com`).\n\n#### If using Istio Gateway and VirtualService\n\nNo changes are required to the REC spec if you are using [Istio](https://redis.io/docs/latest/operate/kubernetes/networking/istio-ingress) in place of an ingress controller. The `activeActive` section added above creates ingress resources. The two custom resources used to configure Istio (Gateway and VirtualService) replace the need for ingress resources.\n\n> [!WARNING]\n> These custom resources are not controlled by the operator and will need to be configured and maintained manually.\n\nFor each cluster, verify the VirtualService resource has two `- match:` blocks in the `tls` section. The hostname under `sniHosts:` should match your `<replication-hostname>`."},{"id":"using-openshift-routes","title":"Using OpenShift routes","role":"content","text":"1. Make sure your Redis Enterprise cluster (REC) has a different name (`<rec-name.namespace>`) than any other participating clusters. If not, you'll need to manually rename the REC or move it to a different namespace.\n            You can check your new REC name with:\n    [code example]\n\n    If the rec name was modified, reapply [scc.yaml](https://github.com/RedisLabs/redis-enterprise-k8s-docs/blob/master/openshift/scc.yaml) to the namespace to reestablish security privileges.\n    \n    [code example]\n\n    Releases before 6.4.2-6 use the earlier version of the SCC, named `redis-enterprise-scc`.\n\n1. Make sure you have DNS aliases for each database that resolve your API hostname `<api-hostname>`,`<ingress-suffix>`, `<replication-hostname>` to the route IP address. To avoid entering multiple DNS records, you can use a wildcard in your alias (such as `*.ijk.example.com`).\n\n1. If your cluster uses [OpenShift routes](https://redis.io/docs/latest/operate/kubernetes/networking/routes), add the following to the `spec` section of your Redis Enterprise cluster (REC) resource file.\n\n      [code example]\n\n1. Make sure you have DNS aliases that resolve to the routes IP for both the API hostname (`<api-hostname>`) and the replication hostname (`<replication-hostname>`) for each database. To avoid entering each database individually, you can use a wildcard in your alias (such as `*.ijk.example.com`).\n\n1. After the changes are saved and applied, you can see that a new route was created for the API.\n\n    [code example]"},{"id":"create-an-active-active-database-with-crdb-cli","title":"Create an Active-Active database with `crdb-cli`","role":"content","text":"The `crdb-cli` command can be run from any Redis Enterprise pod hosted on any participating K8s cluster. You'll need the values for the [required parameters](https://redis.io/docs/latest/operate/kubernetes/active-active/create-aa-crdb-cli#document-required-parameters) for each Redis Enterprise cluster.\n\n[code example]\n\nTo create a database that syncs between more than two instances, add additional `--instance` arguments.\n\nSee the [`crdb-cli` reference](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli) for more options."},{"id":"test-your-database","title":"Test your database","role":"content","text":"The easiest way to test your Active-Active database is to set a key-value pair in one database and retrieve it from the other.\n\nYou can connect to your databases with the instructions in [Manage databases](https://redis.io/docs/latest/operate/kubernetes/re-databases/db-controller#connect-to-a-database). Set a test key with `SET foo bar` in the first database. If your Active-Active deployment is working properly, when connected to your second database, `GET foo` should output `bar`."}],"examples":[{"id":"document-required-parameters-ex0","language":"bash","code":"kubectl get rec","section_id":"document-required-parameters"},{"id":"document-required-parameters-ex1","language":"sh","code":"kubectl get secret <rec-name> \\\n      -o jsonpath='{.data.username}' | base64 --decode\n    kubectl get secret <rec-name> \\\n      -o jsonpath='{.data.password}' | base64 --decode","section_id":"document-required-parameters"},{"id":"using-ingress-controller-ex0","language":"sh","code":"activeActive:\n    apiIngressUrl: <api-hostname>\n    dbIngressSuffix: <ingress-suffix>\n    ingressAnnotations:\n       kubernetes.io/ingress.class: nginx\n      nginx.ingress.kubernetes.io/backend-protocol: HTTPS\n      nginx.ingress.kubernetes.io/ssl-passthrough: \"true\"\n    method: ingress","section_id":"using-ingress-controller"},{"id":"using-ingress-controller-ex1","language":"sh","code":"activeActive:\n    apiIngressUrl: <api-hostname>\n    dbIngressSuffix: <ingress-suffix>\n    ingressAnnotations:\n      kubernetes.io/ingress.class: haproxy\n       ingress.kubernetes.io/ssl-passthrough: \"true\"\n    method: ingress","section_id":"using-ingress-controller"},{"id":"using-ingress-controller-ex2","language":"sh","code":"$ kubectl get ingress\n    NAME   HOSTS                            ADDRESS                                 PORTS   AGE\n    rec01  api.abc.cde.example.com  225161f845b278-111450635.us.cloud.com   80      24h","section_id":"using-ingress-controller"},{"id":"using-ingress-controller-ex3","language":"sh","code":"curl -k -L -i -u <username>:<password> https://<api-hostname>/v1/cluster","section_id":"using-ingress-controller"},{"id":"using-openshift-routes-ex0","language":"sh","code":"oc get rec -o jsonpath='{.items[0].metadata.name}'","section_id":"using-openshift-routes"},{"id":"using-openshift-routes-ex1","language":"sh","code":"oc apply -f scc.yaml\n    oc adm policy add-scc-to-group redis-enterprise-scc-v2  system:serviceaccounts:<namespace>","section_id":"using-openshift-routes"},{"id":"using-openshift-routes-ex2","language":"sh","code":"activeActive:\n        apiIngressUrl: <api-hostname>\n        dbIngressSuffix: <ingress-suffix>\n        method: openShiftRoute","section_id":"using-openshift-routes"},{"id":"using-openshift-routes-ex3","language":"sh","code":"$ oc get route\n    NAME    HOST/PORT                       PATH    SERVICES  PORT  TERMINATION   WILDCARD\n    rec01   api-openshift.apps.abc.example.com rec01   api             passthrough   None","section_id":"using-openshift-routes"},{"id":"create-an-active-active-database-with-crdb-cli-ex0","language":"sh","code":"crdb-cli crdb create \\\n  --name <db-name> \\\n  --memory-size <mem-size> \\\n  --encryption yes \\\n  --instance fqdn=<rec-hostname-01>,url=https://<api-hostname-01>,username=<username-01>,password=<password-01>,replication_endpoint=<replication-endpoint-01>,replication_tls_sni=<replication-hostname-01> \\\n  --instance fqdn=<rec-hostname-02>,url=https://<api-hostname-02>,username=<username-02>,password=<password-02>,replication_endpoint=<replication-endpoint-02>,replication_tls_sni=<replication-hostname-02>","section_id":"create-an-active-active-database-with-crdb-cli"}]}
{"schema_version":2,"id":"operate/kubernetes/active-active/create-reaadb","title":"Create Active-Active database (REAADB)","url":"https://redis.io/docs/latest/operate/kubernetes/active-active/create-reaadb/","summary":"","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","page_type":"content","content_hash":"122343f072c62b43347cda38271fb944b3987c8d8abe8712f6305160e572e686","sections":[{"id":"prerequisites","title":"Prerequisites","role":"overview","text":"To create an Active-Active database, make sure you've completed all the following steps and have gathered the information listed below each step.\n\n1. Configure the [admission controller and ValidatingWebhook](https://redis.io/docs/latest/operate/kubernetes/deployment/quick-start#enable-the-admission-controller/).\n   > [!NOTE]\n   > These are installed and enabled by default on clusters created via the OpenShift OperatorHub.\n\n2. Create two or more [RedisEnterpriseCluster (REC) custom resources](https://redis.io/docs/latest/operate/kubernetes/deployment/quick-start#create-a-redis-enterprise-cluster-rec) with enough [memory resources](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/hardware-requirements).\n   * Name of each REC (`<rec-name>`)\n   * Namespace for each REC (`<rec-namespace>`)\n\n3. Configure the REC [`ingressOrRoutes` field](https://redis.io/docs/latest/operate/kubernetes/networking/ingressorroutespec) and [create DNS records](https://redis.io/docs/latest/operate/kubernetes/networking/ingressorroutespec#configure-dns/).\n   * REC API hostname (`api-<rec-name>-<rec-namespace>.<subdomain>`)\n   * Database hostname suffix (`.db-<rec-name>-<rec-namespace>.<subdomain>`)\n\n4. [Prepare participating clusters](https://redis.io/docs/latest/operate/kubernetes/active-active/prepare-clusters)\n   * RERC name (`<rerc-name`>)\n   * RERC secret name (`redis-enterprise-<rerc-name>`)\n\nFor a list of example values used throughout this article, see the [Example values](#example-values) section."},{"id":"create-rerc","title":"Create RERC custom resources","role":"content","text":"1. Create a `RedisEnterpriseRemoteCluster` (RERC) custom resource file for each participating Redis Enterprise cluster (REC).\n\nBelow are examples of RERC resources for two participating clusters. Substitute your own values to create your own resource.\n\nExample RERC (`rerc-ohare`) for the REC named `rec-chicago` in the namespace `ns-illinois`:\n\n\n\nExample RERC (`rerc-raegan`) for the REC named `rec-arlington` in the namespace `ns-virginia`:\n\n\n\nFor more details on RERC fields, see the [RERC API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_remote_cluster_api).\n\n2. Create a Redis Enterprise remote cluster from each RERC custom resource file.\n  \n[code example]\n\n3. Check the status of your RERC. If `STATUS` is `Active` and `SPEC STATUS` is `Valid`, then your configurations are correct.\n  \n[code example]\n\nThe output should look similar to:\n\n[code example]\n  \nIn case of errors, review the RERC custom resource events and the Redis Enterprise operator logs."},{"id":"create-reaadb","title":"Create the REAADB","role":"content","text":"1. Create a `RedisEnterpriseActiveActiveDatabase` (REAADB) custom resource file meeting the naming requirements and listing the names of the RERC custom resources created in the last step.\n\nNaming requirements:\n* less than 63 characters\n* contains only lowercase letters, numbers, or hyphens\n* starts with a letter\n* ends with a letter or digit\n* **must be unique across all participating clusters**\n\n   The admission controller prevents duplicate database names when databases are created via the Kubernetes operator. Ensure database names are unique across all participating clusters to avoid service deletion and database unavailability.\n\nExample REAADB named `reaadb-boeing` linked to the REC named `rec-chicago` with two participating clusters and a global database configuration with shard count set to 3:\n\n\n\n> [!NOTE]\n> Sharding is disabled on Active-Active databases created with a `shardCount` of 1. Sharding cannot be enabled after database creation. \n\nFor more details on RERC fields, see the [RERC API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_remote_cluster_api).\n\n2. Create a Redis Enterprise Active-Active database from the REAADB custom resource file.\n  \n[code example]\n\n3. Check the status of your RERC. If `STATUS` is `Active` and `SPEC STATUS` is `Valid`, your configurations are correct.\n  \n[code example]\n\nThe output should look similar to:\n\n[code example]\n  \nIn case of errors, review the REAADB custom resource events and the Redis Enterprise operator logs.\n\nTo add tags to an Active-Active database and expose them as metric labels, see [Enrich database metrics with tags](https://redis.io/docs/latest/operate/kubernetes/re-databases/enrich-metrics-with-tags)."},{"id":"example-values","title":"Example values","role":"example","text":"This article uses the example values listed below. You can also find them in the [YAML examples](https://redis.io/docs/latest/operate/kubernetes/reference/yaml/active-active) section.\n\nExample cluster 1:\n\n* REC name: `rec-chicago`\n* REC namespace: `ns-illinois`\n* RERC name: `rerc-ohare`\n* RERC secret name: `redis-enterprise-rerc-ohare`\n* API FQDN: `api-rec-chicago-ns-illinois.example.com`\n* DB FQDN suffix: `.db-rec-chicago-ns-illinois.example.com`\n\nExample cluster 2:\n\n* REC name: `rec-arlington`\n* REC namespace: `ns-virginia`\n* RERC name: `rerc-raegan`\n* RERC secret name: `redis-enterprise-rerc-reagan`\n* API FQDN: `api-rec-arlington-ns-virginia.example.com`\n* DB FQDN suffix: `.db-rec-arlington-ns-virginia.example.com`"}],"examples":[{"id":"create-rerc-ex0","language":"sh","code":"kubectl create -f <rerc-file>","section_id":"create-rerc"},{"id":"create-rerc-ex1","language":"sh","code":"kubectl get rerc <rerc-name>","section_id":"create-rerc"},{"id":"create-rerc-ex2","language":"sh","code":"kubectl get rerc rerc-ohare\n\nNAME        STATUS   SPEC STATUS   LOCAL\nrerc-ohare   Active   Valid         true","section_id":"create-rerc"},{"id":"create-reaadb-ex0","language":"sh","code":"kubectl create -f <reaadb-file>","section_id":"create-reaadb"},{"id":"create-reaadb-ex1","language":"sh","code":"kubectl get reaadb <reaadb-name>","section_id":"create-reaadb"},{"id":"create-reaadb-ex2","language":"sh","code":"kubectl get reaadb reaadb-boeing\n\nNAME              STATUS   SPEC STATUS   LINKED REDBS   REPLICATION STATUS\nreaadb-boeing     active   Valid                        up","section_id":"create-reaadb"}]}
{"schema_version":2,"id":"operate/kubernetes/active-active/edit-clusters","title":"Edit participating clusters for Active-Active database","url":"https://redis.io/docs/latest/operate/kubernetes/active-active/edit-clusters/","summary":"Steps to add or remove a participating cluster to an existing Active-Active database with Redis Enterprise for Kubernetes.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","page_type":"content","content_hash":"8d3789934f026939c8cbf2275b971fb1a78c079d258c77f0d26eb2a645ae1561","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This feature is supported for general availability in releases 6.4.2-6 and later. Some of these features were available as a preview in 6.4.2-4 and 6.4.2-5. Please upgrade to 6.4.2-6 for the full set of general availability features and bug fixes. and later."},{"id":"add-a-participating-cluster","title":"Add a participating cluster","role":"content","text":"Use the following steps to add a participating cluster to an existing Redis Enterprise Active-Active database (REAADB) for Kubernetes."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"To prepare the Redis Enterprise cluster (REC) to participate in an Active-Active database, perform the following tasks from [Prepare participating clusters](https://redis.io/docs/latest/operate/kubernetes/active-active/prepare-clusters):\n\n- Make sure the cluster meets the hardware and naming requirements.\n- Enable the Active-Active controllers.\n- Configure external routing.\n- Configure `ValidatingWebhookConfiguration`."},{"id":"collect-rec-credentials","title":"Collect REC credentials","role":"content","text":"To communicate with other clusters, all participating clusters need access to the admin credentials for all other clusters.\n\n1. Get the REC credentials secret for the new participating cluster.\n\n    [code example]\n\n    This example shows an admin credentials secret for an REC named `rec-boston`:\n\n    [code example]\n\n1. Create a secret for the new participating cluster named `redis-enterprise-<rerc>` and add the username and password.\n\n    The example below shows a secret file for a remote cluster named `rerc-logan` .\n\n    [code example]\n\n1. Apply the file of collected secrets to every participating REC.\n\n    [code example]\n\n If the admin credentials for any of the clusters change, update and reapply the file to all clusters."},{"id":"create-rerc","title":"Create RERC","role":"content","text":"1. From one of the existing participating clusters, create a `RedisEnterpriseRemoteCluster` (RERC) custom resource for the new participating cluster.\n\n    This example (rerc-logan) shows an RERC custom resource for an REC named `rec-boston` in the namespace `ns-massachusetts`. \n\n    [code example]\n\n1. Create the RERC custom resource.\n\n    [code example]\n\n1. Check the status of the newly created RERC custom resource.\n\n    [code example]\n\n    The output should look like this:\n\n    [code example]"},{"id":"edit-reaadb-spec","title":"Edit REAADB spec","role":"content","text":"1. Patch the REAADB spec to add the new RERC name to the `participatingClusters`, replacing `<reaadb-name>` and `<rerc-name>` with your own values.\n\n    [code example]\n\n1. View the REAADB `participatingClusters` status to verify the cluster was added.\n\n    [code example]\n\n    The output should look like this:\n\n    [code example]"},{"id":"remove-a-participating-cluster","title":"Remove a participating cluster","role":"content","text":"1. On an existing participating cluster,remove the desired cluster from the `participatingCluster` section of the REAADB spec.\n\n    [code example]\n\n1. On each of the other participating clusters, verify the status is `active` and the spec status is `Valid` and the cluster was removed.\n\n   [code example]\n   \n    The output should look like this:\n    \n    [code example]\n\n1. On the removed participating cluster, list all REAADB resources on the cluster to verify they were deleted.\n\n    [code example]\n\n1. List the status of all the participating clusters.\n\n[code example]"}],"examples":[{"id":"collect-rec-credentials-ex0","language":"sh","code":"kubectl get secret -o yaml <rec-name>","section_id":"collect-rec-credentials"},{"id":"collect-rec-credentials-ex1","language":"yaml","code":"apiVersion: v1\n    data:\n      password: ABcdef12345\n      username: GHij56789\n    kind: Secret\n    metadata:\n      name: rec-boston\n    type: Opaque","section_id":"collect-rec-credentials"},{"id":"collect-rec-credentials-ex2","language":"yaml","code":"apiVersion: v1\n    data:\n      password: ABcdef12345\n      username: GHij56789\n    kind: Secret\n    metadata:\n      name: redis-enterprise-rerc-logan\n    type: Opaque","section_id":"collect-rec-credentials"},{"id":"collect-rec-credentials-ex3","language":"sh","code":"kubectl apply -f <rec-secret-file>","section_id":"collect-rec-credentials"},{"id":"create-rerc-ex0","language":"yaml","code":"apiVersion: app.redislabs.com/v1alpha1\n    kind: RedisEnterpriseRemoteCluster\n    metadata:\n      name: rerc-logan\n    spec:\n      recName: rec-boston\n      recNamespace: ns-massachusetts\n      apiFqdnUrl: test-example-api-rec-boston-ns-massachusetts.example.com\n      dbFqdnSuffix: .example-cluster-rec-boston-ns-massachusetts.example.com\n      secretName: redis-enterprise-rerc-logan","section_id":"create-rerc"},{"id":"create-rerc-ex1","language":"sh","code":"kubectl create -f <new-RERC-file>","section_id":"create-rerc"},{"id":"create-rerc-ex2","language":"sh","code":"kubectl get rerc <RERC-name>","section_id":"create-rerc"},{"id":"create-rerc-ex3","language":"sh","code":"NAME        STATUS   SPEC STATUS   LOCAL\n    rerc-logan   Active   Valid         true","section_id":"create-rerc"},{"id":"edit-reaadb-spec-ex0","language":"sh","code":"kubectl patch reaadb <reaadb-name> < --type merge --patch '{\"spec\": {\"participatingClusters\": [{\"name\": \"<rerc-name>\"}]}}'","section_id":"edit-reaadb-spec"},{"id":"edit-reaadb-spec-ex1","language":"sh","code":"kubectl get reaadb <reaadb-name> -o=jsonpath='{.status.participatingClusters}'","section_id":"edit-reaadb-spec"},{"id":"edit-reaadb-spec-ex2","language":"sh","code":"[{\"id\":1,\"name\":\"rerc-ohare\"},{\"id\":2,\"name\":\"rerc-reagan\"},{\"id\":3,\"name\":\"rerc-logan\"}]","section_id":"edit-reaadb-spec"},{"id":"remove-a-participating-cluster-ex0","language":"sh","code":"kubectl edit reaadb <reaadb-name>","section_id":"remove-a-participating-cluster"},{"id":"remove-a-participating-cluster-ex1","language":"sh","code":"kubectl get reaadb <reaadb-name> -o=jsonpath='{.status}'","section_id":"remove-a-participating-cluster"},{"id":"remove-a-participating-cluster-ex2","language":"sh","code":"{... ,\"participatingClusters\":[{\"id\":1,\"name\":\"rerc1\"},{\"id\":2,\"name\":\"rerc2\"}],\"redisEnterpriseCluster\":\"rec1\",\"specStatus\":\"Valid\",\"status\":\"active\"}","section_id":"remove-a-participating-cluster"},{"id":"remove-a-participating-cluster-ex3","language":"sh","code":"kubectl get reaadb -o jsonpath='{range .items[*]}{.metadata.name}{\"\\n\"}{end}'","section_id":"remove-a-participating-cluster"},{"id":"remove-a-participating-cluster-ex4","language":"sh","code":"kubectl get reaadb <reaadb-name> -o=jsonpath='{.status.participatingClusters}'","section_id":"remove-a-participating-cluster"}]}
{"schema_version":2,"id":"operate/kubernetes/active-active/edit-rerc","title":"Edit Redis Enterprise remote clusters","url":"https://redis.io/docs/latest/operate/kubernetes/active-active/edit-rerc/","summary":"Edit the configuration details of an existing RERC with Redis Enterprise for Kubernetes.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","page_type":"content","content_hash":"72afc76360e753a5ed1735ccd9604362ea69e98221391f64b1efdffc662f3675","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This feature is supported for general availability in releases 6.4.2-6 and later. Some of these features were available as a preview in 6.4.2-4 and 6.4.2-5. Please upgrade to 6.4.2-6 for the full set of general availability features and bug fixes. and later.\n\nBefore a RedisEnterpriseCluster (REC) can participate in an Active-Active database, it needs an accompanying RedisEnterpriseRemoteCluster (RERC) custom resource. The RERC contains details allowing the REC to link to the RedisEnterpriseActiveActiveDatabase (REAADB). The RERC resource is listed in the REAADB resource to become a participating cluster for the Active-Active database.\n\nThe RERC controller periodically connects to the local REC endpoint via its external address, to ensure it’s setup correctly. For this to work, the external load balancer must support [NAT hairpinning](https://en.wikipedia.org/wiki/Network_address_translation#NAT_loopback). In some cloud environments, this may involve disabling IP preservation for the load balancer target groups.\n\nFor more details, see the [RERC API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_remote_cluster_api)."},{"id":"edit-rerc","title":"Edit RERC","role":"content","text":"Use the `kubectl patch rerc <rerc-name> --type merge --patch` command to patch the local RERC custom resource with your changes. For a full list of available fields, see the [RERC API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_remote_cluster_api).\n\nThe following example edits the `dbFqdnSuffix` field for the RERC named `rerc-ohare`.\n\n[code example]"},{"id":"update-rerc-secret","title":"Update RERC secret","role":"content","text":"If the credentials are changed or updated for a REC participating cluster, you need to manually edit the RERC secret and apply it to all participating clusters.\n\n1. On the local cluster, update the secret with new credentials and name it with the following convention:  `redis-enterprise-<rerc-name>`.\n\n    A secret for a remote cluster named `rerc-ohare` would be similar to the following:\n\n    [code example]\n\n1. Apply the file.\n\n    [code example]\n\n1. Watch the RERC to verify the status is \"Active\" and the spec status is \"Valid.\"\n\n      [code example]\n\n    The output should look like this:\n  \n      [code example]\n      \n    To troubleshoot invalid configurations, view the RERC custom resource events and the [Redis Enterprise operator logs](https://redis.io/docs/latest/operate/kubernetes/logs).\n\n1. Verify the status of each REAADB using that RERC is \"Active\" and the spec status is \"Valid.\"\n\n    [code example]\n    The output should look similar to:\n\n      [code example]\n\n    To troubleshoot invalid configurations, view the RERC custom resource events and the [Redis Enterprise operator logs](https://redis.io/docs/latest/operate/kubernetes/logs).\n\n1. Repeat the above steps on all other participating clusters."}],"examples":[{"id":"edit-rerc-ex0","language":"sh","code":"kubectl patch rerc rerc-ohare --type merge --patch \\\n'{\"spec\":{\"dbFqdnSuffix\": \".example2-cluster-rec-chicago-ns-illinois.example.com\"}}'","section_id":"edit-rerc"},{"id":"update-rerc-secret-ex0","language":"yaml","code":"apiVersion: v1\n    data:\n      password: PHNvbWUgcGFzc3dvcmQ+\n      username: PHNvbWUgdXNlcj4\n    kind: Secret\n    metadata:\n      name: redis-enterprise-rerc-ohare\n    type: Opaque","section_id":"update-rerc-secret"},{"id":"update-rerc-secret-ex1","language":"sh","code":"kubectl apply -f <secret-file>","section_id":"update-rerc-secret"},{"id":"update-rerc-secret-ex2","language":"sh","code":"kubectl get rerc <rerc-name>","section_id":"update-rerc-secret"},{"id":"update-rerc-secret-ex3","language":"sh","code":"NAME        STATUS   SPEC STATUS   LOCAL\n        rerc-ohare   Active   Valid         true","section_id":"update-rerc-secret"},{"id":"update-rerc-secret-ex4","language":"sh","code":"kubectl get reaadb reaadb-boeing","section_id":"update-rerc-secret"},{"id":"update-rerc-secret-ex5","language":"sh","code":"NAME              STATUS   SPEC STATUS   LINKED REDBS   REPLICATION STATUS\n      reaadb-boeing     active   Valid                        up","section_id":"update-rerc-secret"}]}
{"schema_version":2,"id":"operate/kubernetes/active-active/global-config","title":"Set global database configurations","url":"https://redis.io/docs/latest/operate/kubernetes/active-active/global-config/","summary":"The REAADB contains the field '.spec.globalConfigurations' and through this the database configurations are set.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","page_type":"content","content_hash":"df57c05ddc6976f4323b29c849a120c1a75bae7720817a8232ea0bc9909966be","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The Redis Enterprise Active-Active database (REAADB) custom resource contains the field `.spec.globalConfigurations`. This field sets configurations for the Active-Active database across all participating clusters, such as memory size, shard count, and the global database secrets.\n\nThe [REAADB API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_active_active_database_api) contains a full list of available fields."},{"id":"edit-global-configurations","title":"Edit global configurations","role":"content","text":"1. Edit or patch the REAADB custom resource with your global configuration changes.\n\n    The example command below patches the REAADB named `reaadb-boeing` to set the global memory size to 200MB:\n\n    [code example]\n\n1. Verify the status is `active` and the spec status is `Valid`.\n\n    This example shows the status for the `reaadb-boeing` database.\n\n    [code example]\n\n1. View the global configurations on each participating cluster to verify they are synced.\n\n    [code example]"},{"id":"edit-global-configuration-secrets","title":"Edit global configuration secrets","role":"content","text":"This section edits the secrets under the REAADB `.spec.globalConfigurations` section. For more information and all available fields, see the [REAADB API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_active_active_database_api).\n\n1. On an existing participating cluster, generate a YAML file containing the database secret with the relevant data.\n\n    This example shoes a secret named `my-db-secret` with the password `my-password` encoded in base 64.\n\n    [code example]\n\n1. Apply the secret file from the previous step, substituting your own value for `<db-secret-file>`.\n\n    [code example]\n\n1. Patch the REAADB custom resource to specify the database secret, substituting your own values for `<reaadb-name>` and `<secret-name>`.\n\n    [code example]\n\n1. Check the REAADB status for an `active` status and `Valid` spec status.\n\n    [code example]\n\n1. On each other participating cluster, check the secret status.\n\n    [code example]\n\n    The output should show the status as `Invalid`.\n\n    [code example]\n\n1. Sync the secret on each participating cluster.\n\n    [code example]\n\n1. Repeat the previous two steps on every participating cluster."},{"id":"configure-role-permissions","title":"Configure role permissions","role":"configuration","text":"You can configure role-based access control (RBAC) permissions for Active-Active databases using the `rolesPermissions` field in the REAADB `.spec.globalConfigurations` section. The role permissions configuration is propagated across all participating clusters, but the underlying roles and Redis ACLs must be created on each cluster.\n\nYou can define those roles and ACLs as Kubernetes resources with the [`RedisEnterpriseRole`](https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-roles) and [`RedisEnterpriseACL`](https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-acls) custom resources, or create them directly through the Redis Enterprise admin console or REST API. For an overview of the Kubernetes-native model, see [Access control](https://redis.io/docs/latest/operate/kubernetes/security/access-control).\n\n> [!NOTE]\n> You must manually create the specified roles and Redis ACLs on all participating clusters before configuring role permissions. The operator only propagates the role permissions configuration—it does not create the underlying roles and ACLs. If roles or ACLs are missing on any cluster, the operator will log errors and dispatch an Event associated with the REAADB object until they are manually created."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before configuring role permissions:\n\n1. Create the required roles and Redis ACLs on all participating clusters, either as [`RedisEnterpriseRole`](https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-roles) and [`RedisEnterpriseACL`](https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-acls) resources or through the Redis Enterprise admin console or REST API.\n2. Ensure role and ACL names match exactly across all clusters (names are case-sensitive).\n3. Verify that roles and ACLs are properly configured on each cluster.\n\n> [!WARNING]\n> The operator does not automatically create or synchronize roles and ACLs across clusters. You are responsible for manually creating identical roles and ACLs on each participating cluster."},{"id":"add-role-permissions-to-reaadb","title":"Add role permissions to REAADB","role":"content","text":"1. Create or update your REAADB custom resource to include `rolesPermissions` in the global configurations.\n\n    Example REAADB with role permissions:\n\n    [code example]\n\n    Replace `<role-name>` and `<acl-name>` with the exact names of your Redis Enterprise role and ACL.\n\n2. Apply the REAADB custom resource:\n\n    [code example]\n\n    Alternatively, patch an existing REAADB to add role permissions:\n\n    [code example]\n\n3. After the REAADB is active and its replication status is \"Up\", verify role permissions are applied to the local database using the Redis Enterprise REST API. See [Database requests](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#get-bdbs) for details."},{"id":"troubleshooting-role-permissions","title":"Troubleshooting role permissions","role":"errors","text":"If you encounter issues with role permissions:\n\n- **Missing role or ACL errors**: Manually create the specified roles and ACLs on all participating clusters with exact name matches. The operator cannot create these automatically.\n- **Permission propagation failures**: Verify that the roles and ACLs are properly configured and accessible on each cluster. Remember that you must manually create identical roles and ACLs on every participating cluster.\n- **Case sensitivity issues**: Verify that role and ACL names match exactly, including capitalization, across all clusters.\n\nFor more details on the `rolesPermissions` field structure, see the [REAADB API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_active_active_database_api#specglobalconfigurationsrolespermissions)."}],"examples":[{"id":"edit-global-configurations-ex0","language":"sh","code":"kubectl patch reaadb reaadb-boeing --type merge --patch \\\n    '{\"spec\": {\"globalConfigurations\": {\"memorySize\": \"200mb\"}}}'","section_id":"edit-global-configurations"},{"id":"edit-global-configurations-ex1","language":"sh","code":"kubectl get reaadb reaadb-boeing\n\n    NAME             STATUS   SPEC STATUS   GLOBAL CONFIGURATIONS REDB   LINKED REDBS\n    reaadb-boeing   active   Valid","section_id":"edit-global-configurations"},{"id":"edit-global-configurations-ex2","language":"sh","code":"kubectl get reaadb <reaadb-name> -o yaml","section_id":"edit-global-configurations"},{"id":"edit-global-configuration-secrets-ex0","language":"yaml","code":"apiVersion: v1\n     data:\n      password: bXktcGFzcw\n    kind: Secret\n    metadata:\n      name: my-db-secret\n    type: Opaque","section_id":"edit-global-configuration-secrets"},{"id":"edit-global-configuration-secrets-ex1","language":"sh","code":"kubectl apply -f <db-secret-file>","section_id":"edit-global-configuration-secrets"},{"id":"edit-global-configuration-secrets-ex2","language":"sh","code":"kubectl patch reaadb <reaadb-name> --type merge --patch \\\n    '{\"spec\": {\"globalConfigurations\": {\"databaseSecretName\": \"secret-name\"}}}'","section_id":"edit-global-configuration-secrets"},{"id":"edit-global-configuration-secrets-ex3","language":"sh","code":"kubectl get reaadb <reaadb-name>\n\n    NAME             STATUS   SPEC STATUS   GLOBAL CONFIGURATIONS REDB   LINKED REDBS\n    reaadb-boeing   active   Valid","section_id":"edit-global-configuration-secrets"},{"id":"edit-global-configuration-secrets-ex4","language":"sh","code":"kubectl get reaadb <reaadb-name> -o=jsonpath='{.status.secretsStatus}'","section_id":"edit-global-configuration-secrets"},{"id":"edit-global-configuration-secrets-ex5","language":"sh","code":"[{\"name\":\"my-db-secret\",\"status\":\"Invalid\"}]","section_id":"edit-global-configuration-secrets"},{"id":"edit-global-configuration-secrets-ex6","language":"sh","code":"kubectl apply -f <db-secret-file>","section_id":"edit-global-configuration-secrets"},{"id":"add-role-permissions-to-reaadb-ex0","language":"yaml","code":"apiVersion: app.redislabs.com/v1alpha1\n    kind: RedisEnterpriseActiveActiveDatabase\n    metadata:\n      name: reaadb-boeing\n    spec:\n      globalConfigurations:\n        databaseSecretName: <my-secret>\n        memorySize: 200MB\n        shardCount: 3\n        rolesPermissions:\n          - role: <role-name>\n            acl: <acl-name>\n            type: redis-enterprise\n      participatingClusters:\n        - name: rerc-ohare\n        - name: rerc-reagan","section_id":"add-role-permissions-to-reaadb"},{"id":"add-role-permissions-to-reaadb-ex1","language":"sh","code":"kubectl apply -f <reaadb-file>","section_id":"add-role-permissions-to-reaadb"},{"id":"add-role-permissions-to-reaadb-ex2","language":"sh","code":"kubectl patch reaadb <reaadb-name> --type merge --patch \\\n    '{\"spec\": {\"globalConfigurations\": {\"rolesPermissions\": [{\"role\": \"<role-name>\", \"acl\": \"<acl-name>\", \"type\": \"redis-enterprise\"}]}}}'","section_id":"add-role-permissions-to-reaadb"}]}
{"schema_version":2,"id":"operate/kubernetes/active-active/global-db-secret","title":"Set global database secret","url":"https://redis.io/docs/latest/operate/kubernetes/active-active/global-db-secret/","summary":"The REAADB contains the field '.spec.globalConfigurations' to set the global database secret.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","page_type":"content","content_hash":"98173971606148a84a32f85a4fe797f1213fc47cfcbb10da90dcd18e6ef7af9b","sections":[{"id":"set-global-database-secret","title":"Set global database secret","role":"overview","text":"One of the fields available for `globalConfigurations` is `databaseSecretName` which can point to a secret containing the database password. To set the database secret name and sync the data to all participating clusters, follow the steps below.\n\nTo edit other global configruations, see [global configuration](https://redis.io/docs/latest/operate/kubernetes/active-active/global-config)\n\n1. On an existing participating cluster, generate a YAML file containing the database secret with the database password.\n\n  This example shoes a secret named `my-db-secret` with the password `my-password` encoded in base 64.\n\n  [code example]\n\n1. Apply the secret file from the previous step, substituting your own value for `<db-secret-file>`.\n\n    [code example]\n\n1. Patch the REAADB custom resource to specify the database secret, substituting your own values for `<reaadb-name>` and `<secret-name>`.\n\n    [code example]\n\n1. Check the REAADB status for an `active` status and `Valid` spec status.\n\n    [code example]\n\n1. On each other participating cluster, check the secret status.\n\n    ``sh\n    kubectl get reaadb <reaadb-name> -o=jsonpath='{.status.secretsStatus}'\n    [code example]sh\n    [{\"name\":\"my-db-secret\",\"status\":\"Invalid\"}]\n    [code example]sh\n    kubectl apply -f <db-secret-file>\n    ```\n\n1. Repeat the previous two steps on every participating cluster."}],"examples":[{"id":"set-global-database-secret-ex0","language":"yaml","code":"apiVersion: v1\n  data:\n    password: bXktcGFzcw\n  kind: Secret\n  metadata:\n    name: my-db-secret\n  type: Opaque","section_id":"set-global-database-secret"},{"id":"set-global-database-secret-ex1","language":"sh","code":"kubectl apply -f <db-secret-file>","section_id":"set-global-database-secret"},{"id":"set-global-database-secret-ex2","language":"sh","code":"kubectl patch reaadb <reaadb-name> --type merge --patch \\\n    '{\"spec\": {\"globalConfigurations\": {\"databaseSecretName\": \"secret-name\"}}}'","section_id":"set-global-database-secret"},{"id":"set-global-database-secret-ex3","language":"sh","code":"kubectl get reaadb <reaadb-name>\n\n    NAME             STATUS   SPEC STATUS   GLOBAL CONFIGURATIONS REDB   LINKED REDBS\n    example-aadb-1   active   Valid","section_id":"set-global-database-secret"},{"id":"set-global-database-secret-ex4","language":"plaintext","code":"The output should show the status as `Invalid`.","section_id":"set-global-database-secret"},{"id":"set-global-database-secret-ex5","language":"plaintext","code":"1. Sync the secret on each participating cluster.","section_id":"set-global-database-secret"}]}
{"schema_version":2,"id":"operate/kubernetes/active-active","title":"Active-Active databases","url":"https://redis.io/docs/latest/operate/kubernetes/active-active/","summary":"Create and manage Active-Active Redis Enterprise databases across multiple Kubernetes clusters.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","children":[{"id":"operate/kubernetes/active-active/prepare-clusters","summary":"Prepare your participating RECs to be part of an Active-Active database deployment.","title":"Prepare participating clusters","url":"https://redis.io/docs/latest/operate/kubernetes/active-active/prepare-clusters/"},{"id":"operate/kubernetes/active-active/create-reaadb","summary":"","title":"Create Active-Active database (REAADB)","url":"https://redis.io/docs/latest/operate/kubernetes/active-active/create-reaadb/"},{"id":"operate/kubernetes/active-active/edit-clusters","summary":"Steps to add or remove a participating cluster to an existing Active-Active database with Redis Enterprise for Kubernetes.","title":"Edit participating clusters for Active-Active database","url":"https://redis.io/docs/latest/operate/kubernetes/active-active/edit-clusters/"},{"id":"operate/kubernetes/active-active/global-config","summary":"The REAADB contains the field '.spec.globalConfigurations' and through this the database configurations are set.","title":"Set global database configurations","url":"https://redis.io/docs/latest/operate/kubernetes/active-active/global-config/"},{"id":"operate/kubernetes/active-active/global-db-secret","summary":"The REAADB contains the field '.spec.globalConfigurations' to set the global database secret.","title":"Set global database secret","url":"https://redis.io/docs/latest/operate/kubernetes/active-active/global-db-secret/"},{"id":"operate/kubernetes/active-active/edit-rerc","summary":"Edit the configuration details of an existing RERC with Redis Enterprise for Kubernetes.","title":"Edit Redis Enterprise remote clusters","url":"https://redis.io/docs/latest/operate/kubernetes/active-active/edit-rerc/"},{"id":"operate/kubernetes/active-active/create-aa-crdb-cli","summary":"This section shows how to set up an Active-Active Redis Enterprise database on Kubernetes using the Redis Enterprise Software operator.","title":"Create Active-Active databases with crdb-cli","url":"https://redis.io/docs/latest/operate/kubernetes/active-active/create-aa-crdb-cli/"}],"page_type":"content","content_hash":"67345cf54b4284d9f94d8b807e5628e628a7e3efb2bac8ebbccbe0b3b1d5452c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise [Active-Active](https://redis.io/docs/latest/operate/rs/databases/active-active) databases on Kubernetes provide read and write access to the same dataset from different Kubernetes clusters. This enables globally distributed applications with local read and write access, automatic conflict resolution, and seamless failover capabilities.\n\nActive-Active databases use multi-master replication to keep data synchronized across participating clusters, allowing applications to read and write data locally while maintaining global consistency."},{"id":"active-active-setup-methods","title":"Active-Active setup methods","role":"content","text":"There are two methods for creating an Active-Active database with Redis Enterprise for Kubernetes:\n\n- The `RedisEnterpriseActiveActiveDatabase` (REAADB) custom resource is available for versions 6.4.2 and later.\n- The `crdb-cli` method is available for versions 6.4.2 or earlier.\n\n\nWe recommend creating new Active-Active databases using the RedisEnterpriseActiveActiveDatabase (REAADB) custom resource. This allows you to manage your Active-Active database with the operator and ensures you have the latest features and functionality."},{"id":"active-active-controller-method","title":"Active-Active controller method","role":"content","text":"Versions 6.4.2-6 or later fully support the Active-Active controller. Some of these features were available as a preview in 6.4.2-4 and 6.4.2-5. Please upgrade to 6.4.2-6 for the full set of general availability features and bug fixes.\n\nThis setup method includes the following steps:\n\n1. Gather REC credentials and [prepare participating clusters](https://redis.io/docs/latest/operate/kubernetes/active-active/prepare-clusters).\n2. Create [`RedisEnterpriseRemoteCluster` (RERC)](https://redis.io/docs/latest/operate/kubernetes/active-active/create-reaadb#create-rerc) resources.\n3. Create [`RedisEnterpriseActiveActiveDatabase` (REAADB)](https://redis.io/docs/latest/operate/kubernetes/active-active/create-reaadb#create-reaadb) resource."},{"id":"crdb-cli-method","title":"`crdb-cli` method","role":"content","text":"For versions 6.4.2 or earlier, this Active-Active setup method includes the following steps:\n\n1. Install and configure an ingress.\n2. Gather configuration details.\n3. Add the `ActiveActive` field to the REC spec.\n4. Create the database with the `crdb-cli` tool."},{"id":"redis-enterprise-active-active-controller-for-kubernetes","title":"Redis Enterprise Active-Active controller for Kubernetes","role":"content","text":"> [!NOTE]\n> These features are supported for general availability in releases 6.4.2-6 and later.\n\n[Active-Active](https://redis.io/docs/latest/operate/rs/databases/active-active) databases give you read-and-write access to Redis Enterprise clusters (REC) in different Kubernetes clusters or namespaces. Active-Active deployments managed by the Redis Enterprise operator require two additional custom resources: Redis Enterprise Active-Active database (REAADB) and Redis Enterprise remote cluster (RERC).\n\nTo create an Active-Active Redis Enterprise deployment for Kubernetes with these new features, first [prepare participating clusters](https://redis.io/docs/latest/operate/kubernetes/active-active/prepare-clusters) then [create an Active-Active database](https://redis.io/docs/latest/operate/kubernetes/active-active/create-reaadb)."},{"id":"reaadb-custom-resource","title":"REAADB custom resource","role":"content","text":"Redis Enterprise Active-Active database (REAADB) contains a link to the RERC for each participating cluster, and provides configuration and status to the management plane.\n\nFor a full list of fields and options, see the [REAADB API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_active_active_database_api).\n\nFor examples, see the [YAML examples](https://redis.io/docs/latest/operate/kubernetes/reference/yaml/active-active) section."},{"id":"rerc-custom-resource","title":"RERC custom resource","role":"content","text":"Redis Enterprise remote cluster (RERC) custom resource contains configuration details for all the participating clusters.\n\nFor a full list of fields and options, see the [RERC API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_remote_cluster_api).\n\nFor examples, see the [YAML examples](https://redis.io/docs/latest/operate/kubernetes/reference/yaml/active-active) section."},{"id":"manage-certificates","title":"Manage certificates","role":"content","text":"The operator automates Active-Active certificate updates. When you update the proxy or syncer certificate secret on a participating cluster's REC, the operator detects the change and propagates the new certificate to the other participating clusters.\n\nFor details, see [Manage REC certificates](https://redis.io/docs/latest/operate/kubernetes/security/certificates/manage-rec-certificates) and [cert-manager integration](https://redis.io/docs/latest/operate/kubernetes/security/certificates/cert-manager)."},{"id":"limitations","title":"Limitations","role":"limits","text":"* Existing Redis databases cannot be migrated to a REAADB. (DOC-3594)\n* Admission is not blocking REAADB with `shardCount` which exceeds license quota. (RED-96301)\n    Workaround: Fix the problems with the REAADB and reapply.\n* The `<rec-name>/<rec-namespace>` value must be unique for each RERC resource. (RED-96302)\n* Only global database options are supported, no support for specifying configuration per location.\n* No support for migration from old (`crdb-cli`) Active-Active database method to new Active-Active controller.\n* No support for REAADB with participating clusters co-located within the same Kubernetes cluster, except for a single designated local participating cluster.\n* Active-Active databases cannot be configured as [Redis Flex](https://redis.io/docs/latest/operate/kubernetes/flex) deployments."},{"id":"more-info","title":"More info","role":"content","text":"For more general information about Active-Active, see the [Redis Enterprise Software docs](https://redis.io/docs/latest/operate/rs/databases/active-active)."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/active-active/prepare-clusters","title":"Prepare participating clusters","url":"https://redis.io/docs/latest/operate/kubernetes/active-active/prepare-clusters/","summary":"Prepare your participating RECs to be part of an Active-Active database deployment.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","page_type":"content","content_hash":"c05a3af0ee47af8630606dcc958c3fc87a4772e9f9eb7be186fc09bd77b96c9a","sections":[{"id":"prepare-participating-clusters","title":"Prepare participating clusters","role":"overview","text":"Before you prepare your clusters to participate in an Active-Active database, make sure you've completed all the following steps and have gathered the information listed below each step.\n\n1. Configure the [admission controller and ValidatingWebhook](https://redis.io/docs/latest/operate/kubernetes/deployment/quick-start#enable-the-admission-controller/).\n\n2. Create two or more [RedisEnterpriseCluster (REC) custom resources](https://redis.io/docs/latest/operate/kubernetes/deployment/quick-start#create-a-redis-enterprise-cluster-rec) with enough [memory resources](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/hardware-requirements).\n   * Name of each REC (`<rec-name>`)\n   * Namespace for each REC (`<rec-namespace>`)\n\n3. Configure the REC [`ingressOrRoutes` field](https://redis.io/docs/latest/operate/kubernetes/networking/ingressorroutespec) and [create DNS records](https://redis.io/docs/latest/operate/kubernetes/networking/ingressorroutespec#configure-dns/).\n   * REC API hostname (`api-<rec-name>-<rec-namespace>.<subdomain>`)\n   * Database hostname suffix (`.db-<rec-name>-<rec-namespace>.<subdomain>`)\n\nNext you'll [collect credentials](#collect-rec-credentials) for your participating clusters and create secrets for the RedisEnterprsieRemoteCluster (RERC) to use.\n\nFor a list of example values used throughout this article, see the [Example values](#example-values) section."},{"id":"collect-rec-credentials","title":"Collect REC credentials","role":"content","text":"To communicate with other clusters, all participating clusters will need access to the admin credentials for all other clusters.\n\n1. Create a file to hold the admin credentials for all participating RECs (such as `all-rec-secrets.yaml`).\n\n1. Within that file, create a new secret for each participating cluster named `redis-enterprise-<rerc-name>`.\n\n    The example below shows a file (`all-rec-secrets.yaml`) holding secrets for two participating clusters:\n\n    [code example]\n\n1. Get the REC credentials secret for each participating cluster.\n\n    [code example]\n\n    The admin credentials secret for an REC named `rec-chicago` would be similar to this:\n\n    [code example]\n\n    > [!NOTE]\n    > The `username` and `password` values should be base64 encoded, not plain text.\n\n1. Add the username and password to the new secret for that REC and namespace.\n\n    This example shows the collected secrets file (`all-rec-secrets.yaml`) for `rerc-ohare` (representing `rec-chicago` in namespace `ns-illinois`) and `rerc-reagan` (representing `rec-arlington` in namespace `ns-virginia`).\n\n    [code example]\n\n1. Apply the file of collected secrets to every participating REC.\n\n    [code example]\n\n   If the admin credentials for any of the clusters changes, the file will need to be updated and reapplied to all clusters."},{"id":"next-steps","title":"Next steps","role":"content","text":"Now you are ready to [create your Redis Enterprise Active-Active database](https://redis.io/docs/latest/operate/kubernetes/active-active/create-reaadb)."},{"id":"example-values","title":"Example values","role":"example","text":"This article uses the example values listed below. They can also be found in the [YAML examples](https://redis.io/docs/latest/operate/kubernetes/reference/yaml/active-active) section.\n\nExample cluster 1:\n\n* REC name: `rec-chicago`\n* REC namespace: `ns-illinois`\n* RERC name: `rerc-ohare`\n* RERC secret name: `redis-enterprise-rerc-ohare`\n* API FQDN: `api-rec-chicago-ns-illinois.example.com`\n* DB FQDN suffix: `.db-rec-chicago-ns-illinois.example.com`\n\nExample cluster 2:\n\n* REC name: `rec-arlington`\n* REC namespace: `ns-virginia`\n* RERC name: `rerc-raegan`\n* RERC secret name: `redis-enterprise-rerc-reagan`\n* API FQDN: `api-rec-arlington-ns-virginia.example.com`\n* DB FQDN suffix: `.db-rec-arlington-ns-virginia.example.com`"}],"examples":[{"id":"collect-rec-credentials-ex0","language":"yaml","code":"apiVersion: v1\n    data:\n      password: \n      username: \n    kind: Secret\n    metadata:\n      name: redis-enterprise-rerc-ohare\n    type: Opaque\n\n    ---\n\n    apiVersion: v1\n    data:\n      password: \n      username: \n    kind: Secret\n    metadata:\n      name: redis-enterprise-rerc-reagan\n    type: Opaque","section_id":"collect-rec-credentials"},{"id":"collect-rec-credentials-ex1","language":"sh","code":"kubectl get secret -o yaml <rec-name>","section_id":"collect-rec-credentials"},{"id":"collect-rec-credentials-ex2","language":"yaml","code":"apiVersion: v1\n    data:\n      password: ABcdef12345\n      username: GHij56789\n    kind: Secret\n    metadata:\n      name: rec-chicago\n    type: Opaque","section_id":"collect-rec-credentials"},{"id":"collect-rec-credentials-ex3","language":"yaml","code":"apiVersion: v1\n    data:\n      password: ABcdef12345\n      username: GHij56789\n    kind: Secret\n    metadata:\n      name: redis-enterprise-rerc-ohare\n    type: Opaque\n\n    ---\n\n    apiVersion: v1\n    data:\n      password: KLmndo123456\n      username: PQrst789010\n    kind: Secret\n    metadata:\n      name: redis-enterprise-rerc-reagan\n    type: Opaque","section_id":"collect-rec-credentials"},{"id":"collect-rec-credentials-ex4","language":"sh","code":"kubectl apply -f <all-rec-secrets-file>","section_id":"collect-rec-credentials"}]}
{"schema_version":2,"id":"operate/kubernetes/architecture/deployment-options","title":"Flexible deployment options","url":"https://redis.io/docs/latest/operate/kubernetes/architecture/deployment-options/","summary":"Redis Enterprise for Kubernetes allows you to deploy to multiple namespaces. This article describes flexible deployment options you can use to meet your specific needs.","aliases":["/operate/kubernetes/deployment/deployment-options/"],"tags":["docs","operate","kubernetes"],"last_updated":"2026-10-02T11:15:31+01:00","page_type":"content","content_hash":"faed7ed417a3ee93d5818ef1af5c676630501edd020f44b30001ead205e91c65","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can deploy Redis Enterprise for Kubernetes in several different ways depending on your database needs.\n\nMultiple RedisEnterpriseDatabase (REDB) resources can be associated with a single Redis Enterprise cluster resource (REC) even if they reside in different namespaces.\n\nThe Redis Enterprise cluster (REC) custom resource must reside in the same namespace as the Redis Enterprise operator."},{"id":"single-rec-and-single-namespace-one-to-one","title":"Single REC and single namespace (one-to-one)","role":"content","text":"The standard and simplest deployment deploys your Redis Enterprise databases (REDB) in the same namespace as the Redis Enterprise cluster (REC). No additional configuration is required for this, since there is no communication required to cross namespaces. See [Deploy Redis Enterprise for Kubernetes](https://redis.io/docs/latest/operate/kubernetes/deployment/quick-start).\n\n![](https://redis.io/docs/latest/images/k8s/k8s-deploy-one-to-one.png)"},{"id":"single-rec-and-multiple-namespaces-one-to-many","title":"Single REC and multiple namespaces (one-to-many)","role":"content","text":"Multiple Redis Enterprise databases (REDB) spread across multiple namespaces within the same K8s cluster can be associated with the same Redis Enterprise cluster (REC). See [Manage databases in multiple namespaces](https://redis.io/docs/latest/operate/kubernetes/re-clusters/multi-namespace) for more information.\n\n![](https://redis.io/docs/latest/images/k8s/k8s-deploy-one-to-many.png)"},{"id":"multiple-recs-and-multiple-namespaces-many-to-many","title":"Multiple RECs and multiple namespaces (many-to-many)","role":"content","text":"A single Kubernetes cluster can contain multiple Redis Enterprise clusters (REC), as long as they reside in different namespaces. Each namespace can host only one REC and each operator can only manage one REC.\n\nYou have the flexibility to create databases in separate namespaces, or in the same namespace as the REC, or combine any of the supported deployment options above. This configuration is geared towards use cases that require multiple Redis Enterprise clusters with greater isolation or different cluster configurations.\n\nSee [Manage databases in multiple namespaces](https://redis.io/docs/latest/operate/kubernetes/re-clusters/multi-namespace) for more information.\n\n\n![](https://redis.io/docs/latest/images/k8s/k8s-deploy-many-to-many-new.png)"},{"id":"unsupported-deployment-patterns","title":"Unsupported deployment patterns","role":"content","text":""},{"id":"cross-cluster-operations","title":"Cross-cluster operations","role":"content","text":"Redis Enterprise for Kubernetes does not support operations that cross Kubernetes clusters. Redis Enterprise clusters (REC) work inside a single K8s cluster. Crossing clusters could result in functional and security issues.\n\n![](https://redis.io/docs/latest/images/k8s/k8s-deploy-cross-namespaces.png)"},{"id":"multiple-recs-in-one-namespace","title":"Multiple RECs in one namespace","role":"content","text":"Redis Enterprise for Kubernetes does not support multiple Redis Enterprise clusters (REC) in the same namespace. Creating more than one REC in the same namespace will result in errors.\n\n![](https://redis.io/docs/latest/images/k8s/k8s-deploy-multicluster-antipattern.png)"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/architecture","title":"Redis Enterprise for Kubernetes architecture","url":"https://redis.io/docs/latest/operate/kubernetes/architecture/","summary":"Overview of the architecture and components of Redis Enterprise for Kubernetes.","aliases":["/operate/kubernetes/architecture/operator/","/operate/kubernetes/architecture/operator-architecture/","/operate/kubernetes/architecture/operator-architecture"],"tags":["docs","operate","kubernetes"],"last_updated":"2026-10-02T11:15:31+01:00","children":[{"id":"operate/kubernetes/architecture/deployment-options","summary":"Redis Enterprise for Kubernetes allows you to deploy to multiple namespaces. This article describes flexible deployment options you can use to meet your specific needs.","title":"Flexible deployment options","url":"https://redis.io/docs/latest/operate/kubernetes/architecture/deployment-options/"}],"page_type":"content","content_hash":"171c1f19b9da78bbed19884d8e18166ee5ce03dda3302374b2c8f57bc76acc7e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise for Kubernetes gives you the speed and durability of [Redis Enterprise](https://redis.io/redis-enterprise/advantages/), with the flexibility and ease of [Kubernetes (K8s)](https://kubernetes.io/). Redis Enterprise for Kubernetes uses the Kubernetes operator pattern and custom controllers to bring the best of Redis Enterprise to the Kubernetes platform."},{"id":"lifecycle","title":"Lifecycle","role":"content","text":"Redis Enterprise for Kubernetes is compatible with [CNCF-conformant](https://www.cncf.io/training/certification/software-conformance/) Kubernetes platforms. The operator follows standard Kubernetes APIs and practices and is designed to run consistently across certified Kubernetes environments.\n\nEach version of Redis Enterprise for Kubernetes is tested to ensure the version of Redis Enterprise works with the [supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) at the time. Both the Kubernetes version and the Redis Enterprise version must be supported for the operator to function correctly. We encourage you to upgrade Redis Enterprise for Kubernetes frequently, not only to get the benefit of enhancements and bug fixes, but to keep your software supported.\n\nSupported platforms are listed in the [release notes](https://redis.io/docs/latest/operate/kubernetes/release-notes) and in the [supported platforms reference.](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions)"},{"id":"architecture","title":"Architecture","role":"content","text":"The image below illustrates the components of a single namespace, three node deployment.\n\n![](https://redis.io/docs/latest/images/k8s/k8s-arch-v4.png)"},{"id":"operator","title":"Operator","role":"content","text":"An [operator](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/) is a custom extension of the Kubernetes API designed to manage complex, stateful applications and their components. This operator pattern is commonly used by databases and other applications to extend the cluster's behavior without changing its underlying code. Kubernetes.io/docs has a great explanation of the [operator pattern](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/).\n\nThe operator is a deployment that runs within a [namespace](https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/) and uses [controllers](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/#custom-controllers) to manage [custom resources (CRs)](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/), ensuring these resources are continuously monitored and maintained.\n\nWhen the operator is installed, the following resources are created:\n\n- [service account](https://kubernetes.io/docs/concepts/security/service-accounts/) under which the operator will run\n- set of [roles](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#role-and-clusterrole) to define the privileges necessary for the operator to perform its tasks\n- set of [role bindings](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#rolebinding-and-clusterrolebinding) to authorize the service account\n- [CustomResourceDefinition (CRD)](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/#customresourcedefinitions) for each Redis Enterprise custom resource\n- the operator deployment"},{"id":"namespace","title":"Namespace","role":"content","text":"The Redis Enterprise [operator](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/) is deployed within a [namespace](https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/). Each namespace can host only one operator and one [RedisEnterpriseCluster (REC)](#redisenterprisecluster-rec). Namespaces create logical boundaries between resources, allowing organization and security. Some resources are limited to a namespace, while others are cluster-wide.\n\nRedis Enterprise for Kubernetes also supports [multi-namespace deployments](https://redis.io/docs/latest/operate/kubernetes/architecture/deployment-options), meaning the operator can monitor other namespaces (that host applications) for custom resources and apply any changes."},{"id":"custom-resources","title":"Custom resources","role":"content","text":"Kubernetes [custom resources (CRs)](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/) are commonly used by databases and other applications to extend the cluster's behavior without changing its underlying code. [Custom resources (CRs)](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/) extend the Kubernetes API, enabling users to manage Redis databases the Kubernetes way. Custom resources are created and managed using YAML configuration files.\n\nThis [declarative configuration approach](https://kubernetes.io/docs/tasks/manage-kubernetes-objects/declarative-config/) allows you to specify the desired state for your resources, and the operator makes the necessary changes to achieve that state. This simplifies [installation](https://redis.io/docs/latest/operate/kubernetes/deployment), [upgrades](https://redis.io/docs/latest/operate/kubernetes/upgrade), and [scaling](https://redis.io/docs/latest/operate/kubernetes/recommendations/sizing-on-kubernetes) both vertically and horizontally.\n\nThe operator continuously monitors CRs for changes, automatically reconciling any differences between the desired state you specified in your YAML configuration file, and the actual state of your resources. Custom resources can also reside in separate namespaces from the operator managing them, such as in [multi-namespace installations](https://redis.io/docs/latest/operate/kubernetes/re-clusters/multi-namespace)."},{"id":"custom-resource-definitions","title":"Custom resource definitions","role":"content","text":"A [custom resource definition (CRD)](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/#customresourcedefinitions) is a cluster-wide resource that specifies which settings can be configured via custom resource files. Any setting not defined by the CRD is not managed by the operator. You can still make changes to these unmanaged settings using standard Redis Enterprise Software methods.\n\nFor settings managed by the operator, any changes made outside of the CR YAML files (e.g., through the management UI) will be overwritten by the operator. Ensure that all operator-managed settings are updated using the CR YAML files to prevent conflicts."},{"id":"redisenterprisecluster-rec","title":"RedisEnterpriseCluster REC","role":"content","text":"A Redis Enterprise cluster is a set of Redis Enterprise nodes pooling resources. Each node is capable of running multiple Redis instances ([shards](https://redis.io/docs/latest/operate/rs/references/terminology)).\n\n![](https://redis.io/docs/latest/images/k8s/k8s-node-arch.png)\n\nA Redis cluster is created and managed by the [RedisEnterpriseCluster (REC)](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api) [custom resource](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/). Changes to the REC configuration file prompt the operator to make changes to the cluster. The REC is required for both standard databases ([REDB](#redisenterprisedatabase-redb)) and Active-Active databases ([REAADB](#redisenterpriseactiveactivedatabase-reaadb)).\n\nSee the [RedisEnterpriseCluster API Reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api) for a full list of fields and settings."},{"id":"redisenterprisedatabase-redb","title":"RedisEnterpriseDatabase REDB","role":"content","text":"A Redis Enterprise database is a logical entity that manages your entire dataset across multiple Redis instances. A Redis instance is a single-threaded database process ([commonly referred to as a shard](https://redis.io/docs/latest/operate/rs/references/terminology)).\n\nRedis databases are created and managed by the [RedisEnterpriseDatabase (REDB)](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api) [custom resource (CR)](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/). Changes to the REDB YAML configuration file prompt the operator to make changes to the database.\n\nAn operator can manage a database in the same namespace, or a different namespace. See [\"Flexible deployment\"](https://redis.io/docs/latest/operate/kubernetes/architecture/deployment-options) options and [\"Manage databases in multiple namespaces\"](https://redis.io/docs/latest/operate/kubernetes/re-clusters/multi-namespace) for more information.\n\nSee the [RedisEnterpriseDatabase (REDB) API Reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api) for a full list of fields and settings."},{"id":"security","title":"Security","role":"security","text":"Redis Enterprise for Kubernetes uses [secrets](https://kubernetes.io/docs/concepts/configuration/secret/) to manage your cluster credentials, cluster certificates, and client certificates. You can configure [LDAP](https://redis.io/docs/latest/operate/kubernetes/security/authentication/ldap) and [internode encryption](https://redis.io/docs/latest/operate/kubernetes/security/certificates/internode-encryption) using the [RedisEnterpriseCluster (REC)](#redisenterprisecluster-rec) spec."},{"id":"rec-credentials","title":"REC credentials","role":"content","text":"Redis Enterprise for Kubernetes uses the [RedisEnterpriseCluster (REC)](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api) [custom resource](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/) to create a Redis Enterprise cluster. During creation it generates random credentials for the operator to use. The credentials are saved in a Kubernetes (K8s) [secret](https://kubernetes.io/docs/concepts/configuration/secret/). The secret name defaults to the name of the cluster.\n\nSee [Manage REC credentials](https://redis.io/docs/latest/operate/kubernetes/security/authentication/manage-rec-credentials) for more details."},{"id":"rec-certificates","title":"REC certificates","role":"content","text":"By default, Redis Enterprise Software for Kubernetes generates TLS certificates for the cluster during creation. These self-signed certificates are generated on the first node of each Redis Enterprise cluster (REC) and are copied to all other nodes in the cluster.\n\nSee [Manage REC certificates](https://redis.io/docs/latest/operate/kubernetes/security/certificates/manage-rec-certificates) for more details."},{"id":"client-certificates","title":"Client certificates","role":"content","text":"For each client certificate you want to use, you need to create a [Kubernetes secret](https://kubernetes.io/docs/concepts/configuration/secret/) to hold it. You can then reference that secret in your [Redis Enterprise database (REDB)](#redisenterprisedatabase-redb) custom resource.\n\nSee [Add client certificates](https://redis.io/docs/latest/operate/kubernetes/security/certificates/add-client-certificates) for more details."},{"id":"storage","title":"Storage","role":"content","text":"[Persistent storage is mandatory for Redis Enterprise.](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/persistent-ephemeral-storage) Redis Enterprise for Kubernetes [requires network-attached storage](https://en.wikipedia.org/wiki/Network-attached_storage).\n\nRedis Enterprise for Kubernetes uses [PersistentVolumeClaims (PVC)](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#expanding-persistent-volumes-claims) to manage storage resources. The PVC is an abstract representation of the [PersistentVolume (PV)](https://kubernetes.io/docs/concepts/storage/persistent-volumes) resources used by your Redis pods. PVCs are created by the Redis Enterprise operator and used by the [RedisEnterpriseCluster (REC)](#redisenterprisecluster-rec).\n\nPVCs are created with a specific size and [can be expanded](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#expanding-persistent-volumes-claims), if the underlying [storage class](https://kubernetes.io/docs/concepts/storage/storage-classes/) supports it."},{"id":"redis-flex","title":"Redis Flex","role":"content","text":"Redis Enterprise Software for Kubernetes supports [Redis Flex](https://redis.io/docs/latest/operate/kubernetes/flex) (previously known as Redis on Flash), which extends your node memory to use both RAM and flash storage. SSDs (solid state drives) can store infrequently used (warm) keys and values while frequently used (hot) keys and values are stored in RAM. This improves performance and lowers costs for large datasets.\n\nNVMe (non-volatile memory express) SSDs are strongly recommended to achieve the best performance."},{"id":"networking","title":"Networking","role":"content","text":"By default, Kubernetes doesn't allow you to access your Redis database from outside your K8s cluster. Redis Enterprise for Kubernetes supports several ways to route external traffic to your [Redis Enterprise cluster (REC)](#redisenterprisecluster-rec):\n\n- Ingress controllers [HAProxy](https://haproxy-ingress.github.io/) and [NGINX](https://kubernetes.github.io/ingress-nginx/) require an `ingress` API resource.\n- [Istio](https://istio.io/latest/docs/setup/getting-started/) requires `Gateway` and `VirtualService` API resources.\n- OpenShift uses [routes](https://redis.io/docs/latest/operate/kubernetes/networking/routes) to route external traffic.\n\nThe [Active-Active databases](#active-active-databases) require one of above routing methods to be configured in the REC with the [ingressOrRouteSpec field](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api#specingressorroutespec)."},{"id":"services-rigger","title":"Services Rigger","role":"content","text":"The services rigger is responsible for creating and updating services related to database objects. It identifies database objects within the cluster and creates services in accordance with [`redisEnterpriseCluster.Spec.servicesRiggerSpec` setting](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api#specservicesriggerspec) to allow access to those databases. By default, each database has two services, a `cluster_ip` Service with the same name as the database and a `headless` Service with the same name as the database suffixed with `-headless`. It also creates other types of Services such as Ingress Services or OpenshiftRoutes (defined in `redisEnterpriseCluster.Spec.ingressOrRouteSpec`) meant to provide access to REAADB objects.\n\nYou can view a list of services with the `kubectl get services` command.\n\nKubernetes is a dynamic environment, with nodes and pods changing as needed. The services rigger monitors the cluster for these changes and updates the database services to ensure reliable communication with the databases."},{"id":"active-active-databases","title":"Active-Active databases","role":"content","text":"On Kubernetes, Redis Enterprise [Active-Active](https://redis.io/docs/latest/operate/rs/databases/active-active) databases provide read and write access to the same dataset from different Kubernetes clusters. Creating an Active-Active database requires routing [network access](https://redis.io/docs/latest/operate/kubernetes/networking) between two Redis Enterprise clusters residing in different Kubernetes clusters. Without the proper access configured for each cluster, syncing between the databases instances will fail.The admission controller is also required for Active-Active databases on Kubernetes, to validate changes to the custom resources.\n\nFor more details and installation information, see [Active-Active databases](https://redis.io/docs/latest/operate/kubernetes/active-active). For more general information about Active-Active, see the [Redis Enterprise Software docs](https://redis.io/docs/latest/operate/rs/databases/active-active)."},{"id":"redisenterpriseremotecluster-rerc","title":"RedisEnterpriseRemoteCluster RERC","role":"content","text":"The [RedisEnterpriseRemoteCluster (RERC)](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_remote_cluster_api) contains details allowing the REC to link to the RedisEnterpriseActiveActiveDatabase (REAADB). The RERC resource is listed in the [REAADB](#redisenterpriseactiveactivedatabase-reaadb) resource to become a participating cluster for the Active-Active database.\n\nSee the [RERC API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_remote_cluster_api) for a full list of fields and settings."},{"id":"redisenterpriseactiveactivedatabase-reaadb","title":"RedisEnterpriseActiveActiveDatabase REAADB","role":"content","text":"The RedisEnterpriseActiveActiveDatabase (REAADB) resource creates and manages a database that spans more than one Kubernetes cluster. An REAADB requires [external routing](#networking), at least two [RECs](#redisenterprisecluster-rec), and at least two [RERCs](#redisenterpriseremotecluster-rerc).\n\nSee the [REAADB API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_active_active_database_api) for a full list of fields and settings."},{"id":"metrics","title":"Metrics","role":"content","text":"To collect  metrics data from your databases and Redis Enterprise cluster (REC), you can [connect your Prometheus](https://redis.io/docs/latest/operate/kubernetes/re-clusters/connect-prometheus-operator) server to an endpoint exposed on your REC. Redis Enterprise for Kubernetes creates a dedicated service to expose the `prometheus` port (8070) for data collection. A custom resource called `ServiceMonitor` allows the [Prometheus operator](https://github.com/prometheus-operator/prometheus-operator/tree/main/Documentation) to connect to this port and collect data from Redis Enterprise."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/deployment/container-images","title":"Use a private registry for container images","url":"https://redis.io/docs/latest/operate/kubernetes/deployment/container-images/","summary":"This section details how the Redis Enterprise Software and Kubernetes operator images can be configured to be pulled from a variety of sources. This page describes how to configure alternate private repositories for images, plus some techniques for handling public repositories with rate limiting.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T16:14:52+01:00","page_type":"content","content_hash":"058ccb53986c0169ff6b179dc41ed1db25b8287a49d20d282de70a4bc6d203ce","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise Software, its Kubernetes operator, and the Service Rigger\nare all distributed as separate container images.\nYour Kubernetes deployment will pull these images as needed.\nYou can control where these images are\npulled from within the operator deployment and also via the\nRedis Enterprise custom resources.\n\nThe operator image also includes the admission controller, which runs as part of the operator container and provides validation for Redis Enterprise database resources.\n\nFor security reasons (e.g., in air-gapped environments), you may want to pull the images\nfrom a public registry once and then push them to a private registry under\nyour control.\n\n> [!WARNING]\n> It is very important that the images you are pushing to the private registry have the same exact version tag as the original images. \n\nFurthermore, because [Docker rate limits public pulls](https://www.docker.com/blog/scaling-docker-to-serve-millions-more-developers-network-egress/),\nyou may want to consider pulling images from a\nprivate registry to avoid deployment failures when you hit your DockerHub rate limit.\n\nThe information below will help you track and configure where your deployments pull container images.\n\n> [!NOTE]\n> **IMPORTANT**\n> * Each version of the Redis Enterprise operator is mapped to a specific version of Redis Enterprise Software. The semantic versions always match (for example, 7.22.0), although the specific release numbers may be different (for example, 7.22.0-7 is the operator version for Redis Enterprise Software 7.22.0-28).\n> * A specific operator version only supports a specific Redis Enterprise version. Other combinations of operator and Redis Enterprise versions are **not supported**."},{"id":"find-container-sources","title":"Find container sources","role":"content","text":"Every pod in your deployed application has a source registry. Any image not prefixed by a registry domain name (e.g., \"gcr.io\") will pull from the default registry for the Kubernetes cluster (i.e., DockerHub). You can use the commands below to discover the pull sources for the images on your cluster.\n\nTo list all the images used by your cluster:\n\n[code example]\n\nTo specifically determine the pull source for the Redis Enterprise operator itself, run the following command:\n\n[code example]\n\nYou can limit this command to specific namespaces by replacing the `--all-namespaces` parameter with\na set of `-n {namespace}` parameters, where each `{namespace}` is a specific\nnamespace of interest on your cluster."},{"id":"create-a-private-container-registry","title":"Create a private container registry","role":"content","text":"You can set up a private container registry in a couple of ways:\n\n* On-premise via [Docker registry](https://docs.docker.com/registry/deploying/), [Red Hat Quay](https://www.redhat.com/en/technologies/cloud-computing/quay), or other providers\n* Cloud provider based registries (e.g., Azure Container Registry, Google Container Registry, etc.).\n\nOnce you have set up a private container registry, you will identify the container registry using:\n\n* A domain name\n* A port (optional)\n* A repository path (optional)"},{"id":"push-images-to-a-private-container-registry","title":"Push images to a private container registry","role":"content","text":"Important images for a Redis Enterprise Software deployment include:\n\n* Redis Enterprise Software\n* Bootstrapping a Redis Enterprise cluster node (in the operator image)\n* The Service Rigger\n* The Redis Enterprise Software operator (which also includes the admission controller)\n\nYou will need to push all these images to your private container registry. In general,\nto push the images you must:\n\n 1. [Pull](https://docs.docker.com/engine/reference/commandline/pull/) the various images locally for the Redis Enterprise Software, the Service Rigger, and the operator.\n 2. Tag the local images with the private container registry, repository, and version tag.\n 3. [Push](https://docs.docker.com/engine/reference/commandline/push/) the newly tagged images.\n\nThe example below shows the commands for pushing the images for Redis Enterprise Software and its operator to a private container registry:\n\n[code example]"},{"id":"configure-deployments-to-use-a-private-container-registry","title":"Configure deployments to use a private container registry","role":"configuration","text":"Once you push your images to your private container registry, you need to\nconfigure your deployments to use that registry for Redis Enterprise Software and operator\ndeployments. The operator container image is configured directly by the operator deployment\nbundle. The Redis Enterprise cluster pod (RS and bootstrapper) and Service Rigger\nimages are configured in the Redis Enterprise custom resource.\n\nDepending on your Kubernetes platform, your private container registry may\nrequire authentication. If you do need authentication, add a [pull secret](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/) to your namespace. Then you'll need to configure Kubernetes and the operator to use the pull secret. The two following sections have examples of adding the `imagePullSecrets` to the operator deployment and `pullSecrets` to the cluster custom resource."},{"id":"specify-the-operator-image-source","title":"Specify the operator image source","role":"content","text":"The operator bundle contains the operator deployment and the reference to the operator image (`redislabs/operator`). This image includes both the operator functionality and the admission controller. To use a private container registry, you must\nchange this image reference in your operator deployment file **before** you deploy the operator. If you apply this change to modify an existing operator deployment, the operator's pod will restart.\n\nIn the operator deployment file, 'containers:image' should point to the same repository and tag you used when [pushing](https://redis.io/docs/latest/operate/kubernetes/deployment/container-images#push-images-to-a-private-container-registry) to the private container registry:\n\n[code example]\n\nThe example below specifies a 7.22.0-7 operator image in a Google Container Registry:\n\n[code example]\n\nIf your container registry requires a pull secret, configure `imagePullSecrets` on the operator deployment:\n\n[code example]"},{"id":"specify-the-redis-enterprise-cluster-images-source","title":"Specify the Redis Enterprise cluster images source","role":"content","text":"A Redis Enterprise cluster managed by the operator consists of three\ncontainer images:\n\n* **`redislabs/redis`**: the Redis Enterprise Software container image\n* **`redislabs/operator`**: the bootstrapper is packaged within the operator container image\n* **`redislabs/k8s-controller`**: the Service Rigger container image\n\nBy default, a new Redis Enterprise cluster is created using the\ncontainer images listed above. These container images are pulled from the K8s cluster's default\ncontainer registry.\n\nTo [pull](https://docs.docker.com/engine/reference/commandline/pull/) the Redis Enterprise container images from\na private container registry, you must specify them in the\nRedis Enterprise cluster custom resource.\n\nAdd the following sections to the `spec` section of your RedisEnterpriseCluster resource file:\n\n * **`redisEnterpriseImageSpec`**: controls the Redis Enterprise Software container image. *The version should match the RS version associated with the operator version*.\n * **`bootstrapperImageSpec`**\": controls the bootstrapper container image. *The version must match the operator version*.\n * **`redisEnterpriseServicesRiggerImageSpec`**: controls the Service Rigger container image. *The version must match the operator version*.\n\nThe REC custom resource example below pulls all three container images from a GCR private registry:\n\n[code example]\n\nIf your private container registry requires pull secrets, you must add `pullSecrets`\nto the `spec` section:\n\n[code example]"},{"id":"admission-controller","title":"Admission controller","role":"content","text":"The admission controller is included as part of the operator container image and does not require a separate container image. When you configure a private container registry for the operator image, the admission controller functionality is automatically included.\n\nThe admission controller runs within the operator pod and provides validation for Redis Enterprise database resources. It exposes an HTTPS endpoint on port 8443 that Kubernetes uses to validate resource configurations before they are applied to the cluster.\n\nFor more information about configuring the admission controller, see [Enable the admission controller](https://redis.io/docs/latest/operate/kubernetes/deployment/quick-start#enable-the-admission-controller)."},{"id":"rate-limiting-with-dockerhub","title":"Rate limiting with DockerHub","role":"content","text":"Docker has [rate limits for image pulls](https://www.docker.com/blog/scaling-docker-to-serve-millions-more-developers-network-egress/).\nAnonymous users are allowed a certain number of pulls every 6 hours. For authenticated users, the limit is larger.\nThese rate limits may affect your Kubernetes cluster in a number of ways:\n\n* The cluster nodes will likely be treated as a single anonymous user.\n* The number of pulls during a deployment might exceed the rate limit for other deployment dependencies, including the operator, Redis Enterprise Software, or other non-Redis pods.\n* Pull failures may prevent your deployment from downloading the required images in a timely manner. Delays here can affect the stability of deployments used by the Redis Enterprise operator.\n\nFor these reasons, you should seriously consider where your images\nare pulled from to **avoid failures caused by rate limiting**. The easiest solution\nis to push the required images to a private container registry under your control."}],"examples":[{"id":"find-container-sources-ex0","language":"sh","code":"kubectl get pods --all-namespaces -o jsonpath=\"{..image}\" |tr -s '[[:space:]]' '\\n' | uniq -c","section_id":"find-container-sources"},{"id":"find-container-sources-ex1","language":"sh","code":"kubectl get pods --all-namespaces -o jsonpath=\"{..image}\" |tr -s '[[:space:]]' '\\n' | uniq -c | grep redislabs","section_id":"find-container-sources"},{"id":"push-images-to-a-private-container-registry-ex0","language":"sh","code":"PRIVATE_REPO=...your repo...\nRS_VERSION=7.22.0-28\nOPERATOR_VERSION=7.22.0-7\ndocker pull redislabs/redis:${RS_VERSION}\ndocker pull redislabs/operator:${OPERATOR_VERSION}\ndocker pull redislabs/k8s-controller:${OPERATOR_VERSION}\ndocker tag redislabs/redis:${RS_VERSION} ${PRIVATE_REPO}/redislabs/redis:${RS_VERSION}\ndocker tag redislabs/operator:${OPERATOR_VERSION} ${PRIVATE_REPO}/redislabs/operator:${OPERATOR_VERSION}\ndocker tag redislabs/k8s-controller:${OPERATOR_VERSION} ${PRIVATE_REPO}/redislabs/k8s-controller:${OPERATOR_VERSION}\ndocker push ${PRIVATE_REPO}/redislabs/redis:${RS_VERSION}\ndocker push ${PRIVATE_REPO}/redislabs/operator:${OPERATOR_VERSION}\ndocker push ${PRIVATE_REPO}/redislabs/k8s-controller:${OPERATOR_VERSION}","section_id":"push-images-to-a-private-container-registry"},{"id":"specify-the-operator-image-source-ex0","language":"sh","code":"${PRIVATE_REPO}/redislabs/operator:${OPERATOR_VERSION}","section_id":"specify-the-operator-image-source"},{"id":"specify-the-operator-image-source-ex1","language":"YAML","code":"apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: redis-enterprise-operator\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      name: redis-enterprise-operator\n  template:\n    metadata:\n      labels:\n        name: redis-enterprise-operator\n    spec:\n      serviceAccountName: redis-enterprise-operator\n      containers:\n        - name: redis-enterprise-operator\n          image: gcr.io/yourproject/redislabs/operator:7.22.0-7\n...","section_id":"specify-the-operator-image-source"},{"id":"specify-the-operator-image-source-ex2","language":"YAML","code":"spec:\n  template:\n    spec:\n      imagePullSecrets:\n      - name: regcred","section_id":"specify-the-operator-image-source"},{"id":"specify-the-redis-enterprise-cluster-images-source-ex0","language":"YAML","code":"apiVersion: app.redislabs.com/v1\nkind: RedisEnterpriseCluster\nmetadata:\n  name: rec\nspec:\n  nodes: 3\n  redisEnterpriseImageSpec:\n    imagePullPolicy: IfNotPresent\n    repository: gcr.io/yourproject/redislabs/redis\n    versionTag: 7.22.0-28\n  bootstrapperImageSpec:\n    imagePullPolicy: IfNotPresent\n    repository: gcr.io/yourproject/redislabs/operator\n    versionTag: 7.22.0-7\n  redisEnterpriseServicesRiggerImageSpec:\n    imagePullPolicy: IfNotPresent\n    repository: gcr.io/yourproject/redislabs/k8s-controller\n    versionTag: 7.22.0-7","section_id":"specify-the-redis-enterprise-cluster-images-source"},{"id":"specify-the-redis-enterprise-cluster-images-source-ex1","language":"YAML","code":"apiVersion: app.redislabs.com/v1\nkind: RedisEnterpriseCluster\nmetadata:\n  name: rec\nspec:\n  nodes: 3\n  pullSecrets:\n    - name: regcred\n  redisEnterpriseImageSpec:\n    imagePullPolicy: IfNotPresent\n    repository: gcr.io/yourproject/redislabs/redis\n    versionTag: 7.22.0-28\n  bootstrapperImageSpec:\n    imagePullPolicy: IfNotPresent\n    repository: gcr.io/yourproject/redislabs/operator\n    versionTag: 7.22.0-7\n  redisEnterpriseServicesRiggerImageSpec:\n    imagePullPolicy: IfNotPresent\n    repository: gcr.io/yourproject/redislabs/k8s-controller\n    versionTag: 7.22.0-7","section_id":"specify-the-redis-enterprise-cluster-images-source"}]}
{"schema_version":2,"id":"operate/kubernetes/deployment/helm","title":"Install Redis Enterprise Helm chart","url":"https://redis.io/docs/latest/operate/kubernetes/deployment/helm/","summary":"Install Redis Enterprise for Kubernetes version 7.8.6 using Helm charts.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T16:14:52+01:00","page_type":"content","content_hash":"ce6821734bdd28d277b1bbc7df11e5aad6904c91b980c924fb123b5737108b0a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Helm charts provide a simple way to install the Redis Enterprise for Kubernetes operator in just a few steps. For more information about Helm, go to [https://helm.sh/docs/](https://helm.sh/docs/)."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"- A [supported distribution](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) of Kubernetes.\n- At least three worker nodes.\n- [Kubernetes client (kubectl)](https://kubernetes.io/docs/tasks/tools/).\n- [Helm 3.10 or later](https://helm.sh/docs/intro/install/)\n    or 3.18 for migrating from a non-Helm installation.\n\nIf you suspect your file descriptor limits are below 100,000, you must either manually increase limits or [Allow automatic resource adjustment](https://redis.io/docs/latest/operate/kubernetes/security/allow-resource-adjustment). Most major cloud providers and standard container runtime configurations set default file descriptor limits well above the minimum required by Redis Enterprise. In these environments, you can safely run without enabling automatic resource adjustment."},{"id":"example-values","title":"Example values","role":"example","text":"The steps below use the following placeholders to indicate command line parameters you must provide:\n\n- `<release-name>` is the name you give a specific installation of the Helm chart (example: `my-redis-enterprise-operator`)\n- `<chart-version>` is the version of the Helm chart you are installing (example: `7.8.6-2`). Verify that the version you specify is listed in the [Redis Helm repository](https://helm.redis.io/). Using an invalid version number causes installation failures.\n- `<namespace-name>` is the name of the new namespace the Redis operator will run in (example: `ns1`)\n- `<path-to-chart>` is the filepath to the Helm chart, if it is stored in a local directory (example: `/home/charts/redis-enterprise-operator`)"},{"id":"install-the-operator","title":"Install the operator","role":"setup","text":"Below are several ways to install the operator using Helm. To create the REC in the same step, see [Create the REC at install time](#create-the-rec-at-install-time).\n\n- [Install from the Redis Helm repository](#install-from-the-redis-helm-repository)\n- [Install from local directory](#install-from-local-directory)\n- [Specify values during install](#specify-values-during-install)\n- [Install with values file](#install-with-values-file)"},{"id":"install-from-the-redis-helm-repository","title":"Install from the Redis Helm repository","role":"setup","text":"1. Add the Redis repository.\n\n   [code example]\n\n2. Install the Helm chart into a new namespace.\n\n[code example]\n\nTo install with Openshift, add `--set openshift.mode=true`.\n\nTo monitor the installation add the `--debug` flag. The installation runs several jobs synchronously and may take a few minutes to complete."},{"id":"install-from-local-directory","title":"Install from local directory","role":"setup","text":"If you need a local copy of the chart, for example to inspect it or customize values, use one of the following methods.\n\n**Pull the packaged chart (recommended).** `helm pull` fetches the exact packaged chart the Helm repository serves for the version you specify, matching what `helm install` from the repository would deploy.\n\n1. Add the Redis repository, if you haven't already.\n\n   [code example]\n\n2. Pull and unpack the chart. This creates a `redis-enterprise-operator` directory in your current location.\n\n   [code example]\n\n3. Install the Helm chart from the local directory.\n\n   [code example]\n\n**Download the source code.** Alternatively, download the chart source from GitHub.\n\n1. Find the latest release on the [redis-enterprise-k8s-docs](https://github.com/RedisLabs/redis-enterprise-k8s-docs/releases) repo and download the `tar.gz` source code into a local directory.\n\n2. Install the Helm chart from your local directory.\n\n   [code example]\n\nTo install with Openshift, add `--set openshift.mode=true`.\n\nTo monitor the installation add the `--debug` flag. The installation runs several jobs synchronously and may take a few minutes to complete."},{"id":"specify-values-during-install","title":"Specify values during install","role":"content","text":"1. View configurable values with `helm show values redis/redis-enterprise-operator`.\n\n2. Install the Helm chart, overriding specific value defaults using `--set`.\n\n[code example]"},{"id":"install-with-values-file","title":"Install with values file","role":"setup","text":"1. View configurable values with `helm show values redis/redis-enterprise-operator`.\n\n2. Create a YAML file to specify the values you want to configure.\n\n3. Install the chart with the `--values` option.\n\n[code example]"},{"id":"create-the-rec-at-install-time","title":"Create the REC at install time","role":"content","text":"The chart can create the `RedisEnterpriseCluster` (REC) custom resource at install time, so a single `helm install` deploys both the operator and the cluster. To enable this, set `cluster.create` to `true` and define the cluster under `cluster.spec` in your values file. The `spec` field accepts any field from the `RedisEnterpriseCluster` CRD. For the full list, see the [RedisEnterpriseCluster API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api).\n\n> [!NOTE]\n> `cluster.create` defaults to `false`. When `false`, the chart installs only the operator and you create the REC yourself.\n\n1. Create a values file with a `cluster` section. For example:\n\n    [code example]\n\n2. Install the chart with the values file.\n\n    [code example]\n\n    The chart runs a Kubernetes Job to create the REC. The Job deletes itself on success.\n\n3. Confirm the operator deployment is running.\n\n    [code example]\n\n4. Wait for the REC to reach the `Running` state. Initialization takes about a minute.\n\n    [code example]\n\nTo update or uninstall an REC created by the chart, see [Update an REC created by the chart](#update-an-rec-created-by-the-chart) and [Uninstall the REC and operator together](#uninstall-the-rec-and-operator-together)."},{"id":"upgrade-the-chart","title":"Upgrade the chart","role":"content","text":"To upgrade an existing Helm chart installation:\n\n[code example]\n\nYou can also upgrade from a local directory:\n\n[code example]\n\nFor example, to upgrade a chart with the release name `my-redis-enterprise` from the chart's root directory:\n\n[code example]\n\nTo upgrade with OpenShift, add `--set openshift.mode=true`.\n\nThe upgrade process automatically updates the operator and its components, including the Custom Resource Definitions (CRDs). The CRDs are versioned and update only if the new version is higher than the existing version.\n\nAfter you upgrade the operator, you might need to upgrade your Redis Enterprise clusters, depending on the Redis software version bundled with the operator. For detailed information about the upgrade process, see [Redis Enterprise for Kubernetes upgrade documentation](https://redis.io/docs/latest/operate/kubernetes/upgrade/).\n\n> [!NOTE]\n> If your databases use user-defined modules (custom non-bundled modules), you must take additional steps during the upgrade process. See [Upgrade with user-defined modules](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#user-defined-modules) for details.\n\nFor more information and options when upgrading charts, see [helm upgrade](https://helm.sh/docs/helm/helm_upgrade/)."},{"id":"update-an-rec-created-by-the-chart","title":"Update an REC created by the chart","role":"content","text":"If you used `cluster.create: true`, change REC settings by editing `cluster.spec` in your values file and running `helm upgrade`.\n\n1. Update your values file. For example, to add labels:\n\n    [code example]\n\n2. Apply the changes.\n\n    [code example]\n\n    Some REC fields trigger a StatefulSet rolling update. Others, such as `extraLabels`, take effect immediately.\n\n3. Confirm the change.\n\n    [code example]"},{"id":"uninstall","title":"Uninstall","role":"content","text":"1. Delete any custom resources managed by the operator. See [Delete custom resources](https://redis.io/docs/latest/operate/kubernetes/re-clusters/delete-custom-resources) for detailed steps. You must delete custom resources in the correct order to avoid errors.\n\n2. Uninstall the Helm chart.\n\n[code example]\n\nThis removes all Kubernetes resources associated with the chart and deletes the release.\n\n> [!NOTE]\n> Custom Resource Definitions (CRDs) installed by the chart are not removed during chart uninstallation. To remove them manually after uninstalling the chart, run `kubectl delete crds -l app=redis-enterprise`."},{"id":"uninstall-the-rec-and-operator-together","title":"Uninstall the REC and operator together","role":"content","text":"When `cluster.create` is `true`, `helm uninstall` removes both the REC and the operator. You don't need to delete the REC first.\n\n[code example]\n\nConfirm all resources are gone.\n\n[code example]\n\n> [!NOTE]\n> This applies only to RECs the chart created. If you created the REC outside the chart, follow [Delete custom resources](https://redis.io/docs/latest/operate/kubernetes/re-clusters/delete-custom-resources) before you run `helm uninstall`."},{"id":"migrate-from-a-non-helm-installation","title":"Migrate from a non-Helm installation","role":"content","text":"To migrate an existing non-Helm installation of the Redis Enterprise operator to a Helm-based installation:\n\n1. [Upgrade](https://redis.io/docs/latest/operate/kubernetes/upgrade) your existing Redis Enterprise operator to match the version of the Helm chart you want to install. Use the same non-Helm method you used for the original installation.\n\n2. [Install](#install-the-operator) the Helm chart adding the `--take-ownership` flag:\n\n   [code example]\n\n   - The `--take-ownership` flag is available with Helm versions 3.18 or later.\n   - This flag is only needed for the first installation of the chart. Subsequent upgrades don't require this flag.\n   - Use the `helm install` command, not `helm upgrade`.\n\n3. Delete the old `ValidatingWebhookConfiguration` object from the previous non-Helm installation:\n\n   [code example]\n\n   This step is only needed when the `admission.limitToNamespace` chart value is set to `true` (the default). In this case, the webhook object installed by the chart is named `redis-enterprise-admission-<namespace>`, and the original webhook object, named `redis-enterprise-admission`, becomes redundant. If `admission.limitToNamespace` is set to `false`, the webhook installed by the chart is named `redis-enterprise-admission`, and the existing webhook object is reused."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"- Custom resources other than the REC must still be created using the standard process. The chart doesn't manage them.\n- The chart doesn't include configuration options for multiple namespaces, rack-awareness, and Vault integration. The steps for configuring these options remain the same.\n- The chart has had limited testing in advanced setups, including Active-Active configurations, air-gapped deployments, and IPv6/dual-stack environments."}],"examples":[{"id":"install-from-the-redis-helm-repository-ex0","language":"sh","code":"helm repo add redis https://helm.redis.io","section_id":"install-from-the-redis-helm-repository"},{"id":"install-from-the-redis-helm-repository-ex1","language":"sh","code":"helm install <release-name> redis/redis-enterprise-operator \\\n    --version <chart-version> \\\n    --namespace <namespace-name> \\\n    --create-namespace","section_id":"install-from-the-redis-helm-repository"},{"id":"install-from-local-directory-ex0","language":"sh","code":"helm repo add <repo-name> https://helm.redis.io","section_id":"install-from-local-directory"},{"id":"install-from-local-directory-ex1","language":"sh","code":"helm pull <repo-name>/redis-enterprise-operator --version <chart-version> --untar","section_id":"install-from-local-directory"},{"id":"install-from-local-directory-ex2","language":"sh","code":"helm install <release-name> ./redis-enterprise-operator \\\n       --namespace <namespace-name> \\\n       --create-namespace","section_id":"install-from-local-directory"},{"id":"install-from-local-directory-ex3","language":"sh","code":"helm install <release-name> <path-to-chart> \\\n       --namespace <namespace-name> \\\n       --create-namespace","section_id":"install-from-local-directory"},{"id":"specify-values-during-install-ex0","language":"sh","code":"helm install <release-name> redis/redis-enterprise-operator \\\n    --version <chart-version> \\\n    --namespace <namespace-name> \\\n    --create-namespace\n    --set <key1>=<value1> \\\n    --set <key2>=<value2>","section_id":"specify-values-during-install"},{"id":"install-with-values-file-ex0","language":"sh","code":"helm install <release-name> redis/redis-enterprise-operator \\\n    --version <chart-version> \\\n    --namespace <namespace-name> \\\n    --create-namespace \\\n    --values <path-to-values-file>","section_id":"install-with-values-file"},{"id":"create-the-rec-at-install-time-ex0","language":"yaml","code":"cluster:\n      create: true\n      spec:\n        nodes: 3\n        redisEnterpriseImageSpec:\n          repository: redislabs/redis\n        redisEnterpriseServicesRiggerImageSpec:\n          repository: redislabs/k8s-controller\n        bootstrapperImageSpec:\n          repository: redislabs/operator","section_id":"create-the-rec-at-install-time"},{"id":"create-the-rec-at-install-time-ex1","language":"sh","code":"helm install <release-name> redis/redis-enterprise-operator \\\n        --version <chart-version> \\\n        --namespace <namespace-name> \\\n        --create-namespace \\\n        --values <path-to-values-file>","section_id":"create-the-rec-at-install-time"},{"id":"create-the-rec-at-install-time-ex2","language":"sh","code":"kubectl get deployment redis-enterprise-operator --namespace <namespace-name>","section_id":"create-the-rec-at-install-time"},{"id":"create-the-rec-at-install-time-ex3","language":"sh","code":"kubectl get rec --namespace <namespace-name>","section_id":"create-the-rec-at-install-time"},{"id":"upgrade-the-chart-ex0","language":"sh","code":"helm upgrade <release-name> redis/redis-enterprise-operator --version <chart-version>","section_id":"upgrade-the-chart"},{"id":"upgrade-the-chart-ex1","language":"sh","code":"helm upgrade <release-name> <path-to-chart>","section_id":"upgrade-the-chart"},{"id":"upgrade-the-chart-ex2","language":"sh","code":"helm upgrade my-redis-enterprise .","section_id":"upgrade-the-chart"},{"id":"update-an-rec-created-by-the-chart-ex0","language":"yaml","code":"cluster:\n      create: true\n      spec:\n        nodes: 3\n        redisEnterpriseImageSpec:\n          repository: redislabs/redis\n        redisEnterpriseServicesRiggerImageSpec:\n          repository: redislabs/k8s-controller\n        bootstrapperImageSpec:\n          repository: redislabs/operator\n        extraLabels:\n          environment: production","section_id":"update-an-rec-created-by-the-chart"},{"id":"update-an-rec-created-by-the-chart-ex1","language":"sh","code":"helm upgrade <release-name> redis/redis-enterprise-operator \\\n        --namespace <namespace-name> \\\n        --values <path-to-values-file>","section_id":"update-an-rec-created-by-the-chart"},{"id":"update-an-rec-created-by-the-chart-ex2","language":"sh","code":"kubectl get rec <rec-name> --namespace <namespace-name> -o yaml","section_id":"update-an-rec-created-by-the-chart"},{"id":"uninstall-ex0","language":"sh","code":"helm uninstall <release-name>","section_id":"uninstall"},{"id":"uninstall-the-rec-and-operator-together-ex0","language":"sh","code":"helm uninstall <release-name> --namespace <namespace-name>","section_id":"uninstall-the-rec-and-operator-together"},{"id":"uninstall-the-rec-and-operator-together-ex1","language":"sh","code":"kubectl get all --namespace <namespace-name>","section_id":"uninstall-the-rec-and-operator-together"},{"id":"migrate-from-a-non-helm-installation-ex0","language":"sh","code":"helm install <release-name> redis/redis-enterprise-operator --take-ownership","section_id":"migrate-from-a-non-helm-installation"},{"id":"migrate-from-a-non-helm-installation-ex1","language":"sh","code":"kubectl delete validatingwebhookconfiguration redis-enterprise-admission","section_id":"migrate-from-a-non-helm-installation"}]}
{"schema_version":2,"id":"operate/kubernetes/deployment","title":"Deployment","url":"https://redis.io/docs/latest/operate/kubernetes/deployment/","summary":"Deploy Redis Enterprise for Kubernetes using the Redis Enterprise operator on various Kubernetes distributions.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T16:14:52+01:00","children":[{"id":"operate/kubernetes/deployment/quick-start","summary":"How to install Redis Enterprise Software for Kubernetes.","title":"Deploy Redis Enterprise Software for Kubernetes","url":"https://redis.io/docs/latest/operate/kubernetes/deployment/quick-start/"},{"id":"operate/kubernetes/deployment/helm","summary":"Install Redis Enterprise for Kubernetes version 7.8.6 using Helm charts.","title":"Install Redis Enterprise Helm chart","url":"https://redis.io/docs/latest/operate/kubernetes/deployment/helm/"},{"id":"operate/kubernetes/deployment/openshift","summary":"A quick introduction to the steps necessary to get a Redis Enterprise cluster installed in your OpenShift Kubernetes cluster","title":"Deploy Redis Enterprise for Kubernetes with OpenShift","url":"https://redis.io/docs/latest/operate/kubernetes/deployment/openshift/"},{"id":"operate/kubernetes/deployment/container-images","summary":"This section details how the Redis Enterprise Software and Kubernetes operator images can be configured to be pulled from a variety of sources. This page describes how to configure alternate private repositories for images, plus some techniques for handling public repositories with rate limiting.","title":"Use a private registry for container images","url":"https://redis.io/docs/latest/operate/kubernetes/deployment/container-images/"}],"page_type":"content","content_hash":"649365e07ef8d68c78d4650236129f5319c364389899b4b4f765db17ef194bb5","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Deploy Redis Enterprise for Kubernetes by using the Redis Enterprise operator. The operator provides a simple way to deploy and manage Redis Enterprise clusters on various Kubernetes distributions, both on-premises and in the cloud.\n\nThe Redis Enterprise operator uses custom resource definitions (CRDs) to manage Redis Enterprise clusters (REC) and databases (REDB) as native Kubernetes resources. This approach enables GitOps workflows and Kubernetes-native operations."},{"id":"quick-start","title":"Quick start","role":"content","text":"Get started quickly with a basic Redis Enterprise deployment:\n\n- [Deploy Redis Enterprise for Kubernetes](https://redis.io/docs/latest/operate/kubernetes/deployment/quick-start) - Step-by-step guide for most Kubernetes distributions\n- [Deploy on OpenShift](https://redis.io/docs/latest/operate/kubernetes/deployment/openshift) - Specific instructions for OpenShift environments"},{"id":"deployment-methods","title":"Deployment methods","role":"content","text":"Choose the deployment method that best fits your environment:\n\n- [Deploy with Helm](https://redis.io/docs/latest/operate/kubernetes/deployment/helm) - Use Helm charts for simplified deployment and management\n- [Deploy with operator bundle](https://redis.io/docs/latest/operate/kubernetes/deployment/quick-start) - Direct deployment using kubectl and operator manifests"},{"id":"container-images","title":"Container images","role":"content","text":"Understand the container images used by the Redis Enterprise operator:\n\n- [Container images](https://redis.io/docs/latest/operate/kubernetes/deployment/container-images) - Details about Redis Enterprise container images and registries"},{"id":"compatibility","title":"Compatibility","role":"compatibility","text":"Before installing, verify compatibility with your environment:\n\n- [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) - Check which Redis Enterprise operator version supports your Kubernetes distribution"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before deploying Redis Enterprise for Kubernetes, ensure you have:\n\n- A Kubernetes cluster running a [supported distribution](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions)\n- Minimum of three worker nodes for high availability\n- Kubernetes client (kubectl) configured to access your cluster\n- Access to container registries (DockerHub, Red Hat Container Catalog, or private registry)\n- Sufficient resources as outlined in [sizing recommendations](https://redis.io/docs/latest/operate/kubernetes/recommendations/sizing-on-kubernetes)"},{"id":"next-steps","title":"Next steps","role":"content","text":"After deployment, you can:\n\n- [Create a Redis Enterprise cluster (REC)](https://redis.io/docs/latest/operate/kubernetes/re-clusters)\n- [Create Redis Enterprise databases (REDB)](https://redis.io/docs/latest/operate/kubernetes/re-databases)\n- [Configure networking](https://redis.io/docs/latest/operate/kubernetes/networking)\n- [Set up security](https://redis.io/docs/latest/operate/kubernetes/security)"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/deployment/openshift","title":"Deploy Redis Enterprise for Kubernetes with OpenShift","url":"https://redis.io/docs/latest/operate/kubernetes/deployment/openshift/","summary":"A quick introduction to the steps necessary to get a Redis Enterprise cluster installed in your OpenShift Kubernetes cluster","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T16:14:52+01:00","children":[{"id":"operate/kubernetes/deployment/openshift/openshift-cli","summary":"Redis Enterprise for Kubernetes and cluster can be installed via CLI tools OpenShift","title":"Deployment with OpenShift CLI for Redis Enterprise for Kubernetes","url":"https://redis.io/docs/latest/operate/kubernetes/deployment/openshift/openshift-cli/"},{"id":"operate/kubernetes/deployment/openshift/openshift-operatorhub","summary":"OpenShift provides the OperatorHub where you can install the Redis Enterprise operator from the administrator user interface.","title":"Deploy Redis Enterprise with OpenShift OperatorHub","url":"https://redis.io/docs/latest/operate/kubernetes/deployment/openshift/openshift-operatorhub/"}],"page_type":"content","content_hash":"38c3c1ccccdd77c6715fa9cf06629594a13ccb94d7f32ee0b5433ea65a50d8a0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The deployment of Redis Enterprise clusters is managed with the Redis Enterprise operator that you deploy in the namespace for your project.\nTo create a database that your application\nworkloads can use:\n\n1. Install the Redis Enterprise operator.\n\n1. Create a Redis Enterprise CRD to describe your desired cluster.\n\n1. The operator reads this cluster description and deploys the various components on your K8s cluster.\n\n1. Once running, use the Redis Enterprise cluster to create a database.\n\n1. The operator automatically exposes the new database as a K8s service."},{"id":"for-openshift-via-the-operatorhub","title":"For OpenShift via the OperatorHub","role":"content","text":"To [create a database on an OpenShift 4.x cluster via the OperatorHub](https://redis.io/docs/latest/operate/kubernetes/deployment/openshift/openshift-operatorhub) you only need to have the [OpenShift 4.x cluster installed](https://docs.openshift.com/container-platform/4.3/welcome/index.html) with at least three nodes that each meet the [minimum requirements for a development installation](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/hardware-requirements)."},{"id":"for-openshift-via-the-cli","title":"For OpenShift via the CLI","role":"content","text":"To [create a database on an OpenShift cluster via the CLI](https://redis.io/docs/latest/operate/kubernetes/deployment/openshift/openshift-cli), you need:\n\n1. An [OpenShift cluster installed](https://docs.openshift.com/container-platform/4.3/welcome/index.html) with at least three nodes that each meet the [minimum requirements for a development installation](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/hardware-requirements).\n1. The [kubectl package installed](https://kubernetes.io/docs/tasks/tools/install-kubectl/) at version 1.9 or higher\n1. The [OpenShift cli installed](https://docs.openshift.com/container-platform/4.2/cli_reference/openshift_cli/getting-started-cli.html)"},{"id":"version-compatibility","title":"Version compatibility","role":"compatibility","text":"To see which version of Redis Enterprise for Kubernetes supports your OpenShift version, see [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions)."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/deployment/openshift/openshift-cli","title":"Deployment with OpenShift CLI for Redis Enterprise for Kubernetes","url":"https://redis.io/docs/latest/operate/kubernetes/deployment/openshift/openshift-cli/","summary":"Redis Enterprise for Kubernetes and cluster can be installed via CLI tools OpenShift","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T16:14:52+01:00","page_type":"content","content_hash":"e683b67a5ace8046dc21237e8d6f8023bd9a839517b2f1cf0de0235449516cc1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Use these steps to set up a Redis Enterprise Software cluster with OpenShift."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"- [OpenShift cluster](https://docs.openshift.com/container-platform/4.8/installing/index.html) with at least 3 nodes (each meeting the [minimum requirements for a development installation](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/hardware-requirements))\n- [OpenShift CLI](https://docs.openshift.com/container-platform/latest/cli_reference/openshift_cli/getting-started-cli.html)\n\nTo see which version of Redis Enterprise for Kubernetes supports your OpenShift version, see [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions).\n\n> [!NOTE]\n> If you suspect your file descriptor limits are below 100,000, you must either manually increase limits or [Allow automatic resource adjustment](https://redis.io/docs/latest/operate/kubernetes/security/allow-resource-adjustment). Most major cloud providers and standard container runtime configurations set default file descriptor limits well above the minimum required by Redis Enterprise. In these environments, you can safely run without enabling automatic resource adjustment."},{"id":"deploy-the-operator","title":"Deploy the operator","role":"content","text":"1. Create a new project.\n\n    [code example]\n\n1. Verify the newly created project. \n\n    [code example]\n\n1. Get the deployment files.\n\n    [code example]\n\n1. Deploy the OpenShift operator bundle.\n\n    If you are using version 6.2.18-41 or earlier, you must [apply the security context constraint](#install-security-context-constraint) before the operator bundle.\n\n    [code example]\n\n    > [!WARNING]\n    > Changes to the `openshift.bundle.yaml` file can cause unexpected results.\n    >\n\n1. Verify that your `redis-enterprise-operator` deployment is running.\n\n    [code example]\n\n    A typical response looks like this:\n\n    [code example]\n\n    > [!WARNING]\n    > DO NOT modify or delete the StatefulSet created during the deployment process. Doing so could destroy your Redis Enterprise cluster (REC).\n    >"},{"id":"security-context-constraints","title":"Security context constraints","role":"security","text":"Versions 7.22.0-6 and later run in without permissions to [allow automatic resource adjustment](https://redis.io/docs/latest/operate/kubernetes/security/allow-resource-adjustment). If you use the recommended default security constraints, remove the existing `redis-enterprise-scc-v2` SCC and unbind it from the REC service account after upgrading."},{"id":"create-a-redis-enterprise-cluster-custom-resource","title":"Create a Redis Enterprise cluster custom resource","role":"content","text":"1. Download the `RedisEnterpriseCluster` resource file ([rec.yaml](https://github.com/RedisLabs/redis-enterprise-k8s-docs/blob/master/examples/v1/rec.yaml)).\n\n    You can rename the file to `<your_cluster_name>.yaml`, but it is not required. Examples below use `rec.yaml`. [Options for Redis Enterprise clusters](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api) has more info about the Redis Enterprise cluster (REC) custom resource, or see the [Redis Enterprise cluster API](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api) for a full list of options.\n\n    > [!NOTE]\n    > If you suspect your file descriptor limits are below 100,000, you must either manually increase limits or [Allow automatic resource adjustment](https://redis.io/docs/latest/operate/kubernetes/security/allow-resource-adjustment). Most major cloud providers and standard container runtime configurations set default file descriptor limits well above the minimum required by Redis Enterprise. In these environments, you can safely run without enabling automatic resource adjustment.\n    >\n\n    The REC name cannot be changed after cluster creation.\n\n    > [!NOTE]\n    > Each Redis Enterprise cluster requires at least 3 nodes. Single-node RECs are not supported.\n    >\n\n2. Apply the custom resource file to create your Redis Enterprise cluster.\n\n    > [!NOTE]\n    > If you enabled automatic resource adjustment in your configuration, this step will trigger the operator to apply elevated capabilities. Ensure your security context allows it.\n    >\n\n    [code example]\n\n    The operator typically creates the REC within a few minutes.\n\n1. Check the cluster status.\n\n    [code example]\n\n    You should receive a response similar to the following:\n\n    [code example]"},{"id":"configure-the-admission-controller","title":"Configure the admission controller","role":"configuration","text":"1. Verify the `admission-tls` secret exists.\n\n    [code example]\n  \n    The output should look similar to\n  \n    [code example]\n\n1. Save the certificate to a local environment variable.\n\n    [code example]\n\n1. Create a Kubernetes validating webhook, replacing `<namespace>` with the namespace where the REC was installed.\n\n    The `webhook.yaml` template can be found in [redis-enterprise-k8s-docs/admission](https://github.com/RedisLabs/redis-enterprise-k8s-docs/tree/master/admission)\n\n    [code example]\n\n1. Create a patch file for the Kubernetes validating webhook.\n\n    [code example]\n\n1. Patch the webhook with the certificate.\n\n    [code example]"},{"id":"limit-the-webhook-to-relevant-namespaces","title":"Limit the webhook to relevant namespaces","role":"limits","text":"If not limited, the webhook intercepts requests from all namespaces. If you have several REC objects in your Kubernetes cluster, limit the webhook to the relevant namespaces. If you aren't using multiple namespaces, skip this step.\n\n1. Verify your namespace is labeled and the label is unique to this namespace, as shown in the next example.\n\n    [code example]\n\n1. Patch the webhook spec with the `namespaceSelector` field.\n\n    [code example]\n\n1. Apply the patch.\n\n    [code example]\n\n    > [!NOTE]\n    > For releases before 6.4.2-4, use this command instead:\n    >\n    > [code example]\n    >\n    > The 6.4.2-4 release introduces a new `ValidatingWebhookConfiguration` to replace `redb-admission`. See the [6.4.2-4 release notes](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases).\n    >"},{"id":"verify-admission-controller-installation","title":"Verify admission controller installation","role":"content","text":"Apply an invalid resource as shown below to force the admission controller to reject it. If it applies successfully, the admission controller is not installed correctly.\n\n[code example]\n\nYou should see this error from the admission controller webhook `redisenterprise.admission.redislabs`.\n  \n[code example]"},{"id":"create-a-redis-enterprise-database-custom-resource","title":"Create a Redis Enterprise database custom resource","role":"content","text":"The operator uses the instructions in the Redis Enterprise database (REDB) custom resources to manage databases on the Redis Enterprise cluster.\n\n1. Create a `RedisEnterpriseDatabase` custom resource.\n\n    This example creates a test database. For production databases, see [create a database](https://redis.io/docs/latest/operate/kubernetes/re-databases/db-controller#create-a-database) and [RedisEnterpriseDatabase API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api).\n\n    [code example]\n\n1. Apply the newly created REDB resource.\n\n    [code example]"},{"id":"more-info","title":"More info","role":"content","text":"- [Redis Enterprise cluster API](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api)\n- [Redis Enterprise database API](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api)"}],"examples":[{"id":"deploy-the-operator-ex0","language":"sh","code":"oc new-project <your-project-name>","section_id":"deploy-the-operator"},{"id":"deploy-the-operator-ex1","language":"sh","code":"oc project <your-project-name>","section_id":"deploy-the-operator"},{"id":"deploy-the-operator-ex2","language":"sh","code":"git clone https://github.com/RedisLabs/redis-enterprise-k8s-docs","section_id":"deploy-the-operator"},{"id":"deploy-the-operator-ex3","language":"sh","code":"oc apply -f openshift.bundle.yaml","section_id":"deploy-the-operator"},{"id":"deploy-the-operator-ex4","language":"sh","code":"oc get deployment","section_id":"deploy-the-operator"},{"id":"deploy-the-operator-ex5","language":"sh","code":"NAME                        READY   UP-TO-DATE   AVAILABLE   AGE\n    redis-enterprise-operator   1/1     1            1           0m36s","section_id":"deploy-the-operator"},{"id":"create-a-redis-enterprise-cluster-custom-resource-ex0","language":"sh","code":"oc apply -f rec.yaml","section_id":"create-a-redis-enterprise-cluster-custom-resource"},{"id":"create-a-redis-enterprise-cluster-custom-resource-ex1","language":"sh","code":"oc get pod","section_id":"create-a-redis-enterprise-cluster-custom-resource"},{"id":"create-a-redis-enterprise-cluster-custom-resource-ex2","language":"sh","code":"NAME                             | READY | STATUS  | RESTARTS | AGE |\n    | -------------------------------- | ----- | ------- | -------- | --- |\n    | rec-name-0              | 2/2   | Running | 0        | 1m  |\n    | rec-name-1              | 2/2   | Running | 0        | 1m  |\n    | rec-name-2              | 2/2   | Running | 0        | 1m  |\n    | rec-name-controller-x-x | 1/1   | Running | 0        | 1m  |\n    | Redis-enterprise-operator-x-x    | 1/1   | Running | 0        | 5m  |","section_id":"create-a-redis-enterprise-cluster-custom-resource"},{"id":"configure-the-admission-controller-ex0","language":"sh","code":"kubectl get secret admission-tls","section_id":"configure-the-admission-controller"},{"id":"configure-the-admission-controller-ex1","language":"sh","code":"NAME            TYPE     DATA   AGE\n    admission-tls   Opaque   2      2m43s","section_id":"configure-the-admission-controller"},{"id":"configure-the-admission-controller-ex2","language":"sh","code":"CERT=`kubectl get secret admission-tls -o jsonpath='{.data.cert}'`","section_id":"configure-the-admission-controller"},{"id":"configure-the-admission-controller-ex3","language":"sh","code":"sed 's/OPERATOR_NAMESPACE/<namespace>/g' webhook.yaml | kubectl create -f -","section_id":"configure-the-admission-controller"},{"id":"configure-the-admission-controller-ex4","language":"sh","code":"cat > modified-webhook.yaml <<EOF\n    webhooks:\n    - name: redisenterprise.admission.redislabs\n      clientConfig:\n       caBundle: $CERT\n    EOF","section_id":"configure-the-admission-controller"},{"id":"configure-the-admission-controller-ex5","language":"sh","code":"kubectl patch ValidatingWebhookConfiguration \\\n        redis-enterprise-admission --patch \"$(cat modified-webhook.yaml)\"","section_id":"configure-the-admission-controller"},{"id":"limit-the-webhook-to-relevant-namespaces-ex0","language":"sh","code":"apiVersion: v1\n    kind: Namespace\n    metadata:\n      labels:\n       namespace-name: staging\n    name: staging","section_id":"limit-the-webhook-to-relevant-namespaces"},{"id":"limit-the-webhook-to-relevant-namespaces-ex1","language":"sh","code":"cat > modified-webhook.yaml <<EOF\n    webhooks:\n    - name: redisenterprise.admission.redislabs\n      namespaceSelector:\n       matchLabels:\n         namespace-name: staging\n    EOF","section_id":"limit-the-webhook-to-relevant-namespaces"},{"id":"limit-the-webhook-to-relevant-namespaces-ex2","language":"sh","code":"oc patch ValidatingWebhookConfiguration \\\n      redis-enterprise-admission --patch \"$(cat modified-webhook.yaml)\"","section_id":"limit-the-webhook-to-relevant-namespaces"},{"id":"limit-the-webhook-to-relevant-namespaces-ex3","language":"sh","code":"> oc patch ValidatingWebhookConfiguration \\\n    >   redb-admission --patch \"$(cat modified-webhook.yaml)\"\n    >","section_id":"limit-the-webhook-to-relevant-namespaces"},{"id":"verify-admission-controller-installation-ex0","language":"sh","code":"oc apply -f - << EOF\napiVersion: app.redislabs.com/v1alpha1\nkind: RedisEnterpriseDatabase\nmetadata:\n  name: redis-enterprise-database\nspec:\n  evictionPolicy: illegal\nEOF","section_id":"verify-admission-controller-installation"},{"id":"verify-admission-controller-installation-ex1","language":"sh","code":"Error from server: error when creating \"STDIN\": admission webhook \"redisenterprise.admission.redislabs\" denied the request: eviction_policy: u'illegal' is not one of [u'volatile-lru', u'volatile-ttl', u'volatile-random', u'allkeys-lru', u'allkeys-random', u'noeviction', u'volatile-lfu', u'allkeys-lfu']","section_id":"verify-admission-controller-installation"},{"id":"create-a-redis-enterprise-database-custom-resource-ex0","language":"sh","code":"cat << EOF > /tmp/redis-enterprise-database.yml\n    apiVersion: app.redislabs.com/v1alpha1\n    kind: RedisEnterpriseDatabase\n    metadata:\n      name: redis-enterprise-database\n    spec:\n      memorySize: 100MB\n    EOF","section_id":"create-a-redis-enterprise-database-custom-resource"},{"id":"create-a-redis-enterprise-database-custom-resource-ex1","language":"sh","code":"oc apply -f /tmp/redis-enterprise-database.yml","section_id":"create-a-redis-enterprise-database-custom-resource"}]}
{"schema_version":2,"id":"operate/kubernetes/deployment/openshift/openshift-operatorhub","title":"Deploy Redis Enterprise with OpenShift OperatorHub","url":"https://redis.io/docs/latest/operate/kubernetes/deployment/openshift/openshift-operatorhub/","summary":"OpenShift provides the OperatorHub where you can install the Redis Enterprise operator from the administrator user interface.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T16:14:52+01:00","page_type":"content","content_hash":"e78a821c31062340f8336ecab508d725940cbff6c88c2a49f624745f0fc547fe","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can deploy Redis Enterprise for Kubernetes from the Red Hat OpenShift CLI. You can also use a UI, [OperatorHub](https://docs.openshift.com/container-platform/4.11/operators/index.html) (Red Hat) to install operators and create custom resources.\n\n> [!NOTE]\n> If you suspect your file descriptor limits are below 100,000, you must either manually increase limits or [Allow automatic resource adjustment](https://redis.io/docs/latest/operate/kubernetes/security/allow-resource-adjustment). Most major cloud providers and standard container runtime configurations set default file descriptor limits well above the minimum required by Redis Enterprise. In these environments, you can safely run without enabling automatic resource adjustment.\n\nTo see which version of Redis Enterprise for Kubernetes supports your OpenShift version, see [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions)."},{"id":"install-the-redis-enterprise-operator","title":"Install the Redis Enterprise operator","role":"setup","text":"> [!WARNING]\n>  If using version 6.2.18-41 or earlier, [Install the security context constraint](#install-security-context-constraint) before installing the operator. \n\n1. Select **Operators > OperatorHub**.\n\n2. Search for _Redis Enterprise_ in the search dialog and select the **Redis Enterprise Operator provided by Redis** marked as **Certified**.\n\n    By default, the image is pulled from Red Hat's registry.\n\n3. On the **Install Operator** page, specify the namespace for the operator.\n\n    Only one namespace per operator is supported.\n\n4. Update the **channel** with the version you're installing.\n\n    For more information about specific versions, see the [release notes](https://redis.io/docs/latest/operate/kubernetes/release-notes).\n\n5. Choose an approval strategy.\n\n    Use **Manual** for production systems to ensure the operator is only upgraded by approval.\n\n6. Select **Install** and approve the install plan.\n\n   You can monitor the subscription status in **Operators > Installed Operators**.\n\n> [!WARNING]\n> DO NOT modify or delete the StatefulSet created during the deployment process. Doing so could destroy your Redis Enterprise cluster (REC)."},{"id":"security-context-constraints","title":"Security context constraints","role":"security","text":"Versions 7.22.0-6 and later run in without permissions to [allow automatic resource adjustment](https://redis.io/docs/latest/operate/kubernetes/security/allow-resource-adjustment). If you use the recommended default security constraints, remove the existing `redis-enterprise-scc-v2` SCC and unbind it from the REC service account after upgrading."},{"id":"create-redis-enterprise-custom-resources","title":"Create Redis Enterprise custom resources","role":"content","text":"The **Installed Operators**->**Operator details** page shows the provided APIs: **RedisEnterpriseCluster** and **RedisEnterpriseDatabase**. You can select **Create instance** to create custom resources using the OperatorHub interface.\n\n\nUse the YAML view to create a custom resource file or let OperatorHub generate the YAML file for you by specifying your configuration options in the form view.\n\n> [!NOTE]\n> If you suspect your file descriptor limits are below 100,000, you must either manually increase limits or [Allow automatic resource adjustment](https://redis.io/docs/latest/operate/kubernetes/security/allow-resource-adjustment). Most major cloud providers and standard container runtime configurations set default file descriptor limits well above the minimum required by Redis Enterprise. In these environments, you can safely run without enabling automatic resource adjustment.\n\n<note> The REC name cannot be changed after cluster creation.</note>\n\nFor more information on creating and maintaining Redis Enterprise custom resources, see [Redis Enterprise clusters (REC)](https://redis.io/docs/latest/operate/kubernetes/re-clusters) and [Redis Enterprise databases (REDB)](https://redis.io/docs/latest/operate/kubernetes/re-databases)."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/deployment/quick-start","title":"Deploy Redis Enterprise Software for Kubernetes","url":"https://redis.io/docs/latest/operate/kubernetes/deployment/quick-start/","summary":"How to install Redis Enterprise Software for Kubernetes.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T16:14:52+01:00","page_type":"content","content_hash":"2ce2de102497352ae007b241c480a955ae791302aa1b348ebb0cdc6b44e2d6ca","sections":[{"id":"overview","title":"Overview","role":"overview","text":"To deploy Redis Enterprise Software for Kubernetes and start your Redis Enterprise cluster (REC), you need to do the following:\n\n- Create a new namespace in your Kubernetes cluster.\n- Download the operator bundle.\n- Apply the operator bundle and verify it's running.\n- Create a Redis Enterprise cluster (REC).\n\nThis guide works with most supported Kubernetes distributions. If you're using OpenShift, see [Redis Enterprise on OpenShift](https://redis.io/docs/latest/operate/kubernetes/deployment/openshift). For details on what is currently supported, see [supported distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions)."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"To deploy Redis Enterprise for Kubernetes, you'll need:\n\n- Kubernetes cluster in a [supported distribution](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions)\n- minimum of three worker nodes\n- Kubernetes client (kubectl)\n- access to DockerHub, RedHat Container Catalog, or a private repository that can hold the required images.\n\nIf you suspect your file descriptor limits are below 100,000, you must either manually increase limits or [Allow automatic resource adjustment](https://redis.io/docs/latest/operate/kubernetes/security/allow-resource-adjustment). Most major cloud providers and standard container runtime configurations set default file descriptor limits well above the minimum required by Redis Enterprise. In these environments, you can safely run without enabling automatic resource adjustment.\n\n> [!NOTE]\n> If you are applying version 7.8.2-6 or above, check if the [OS](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-2-releases/7-8-2-6-nov24/#breaking-changes) installed on the node is supported."},{"id":"create-a-new-namespace","title":"Create a new namespace","role":"content","text":"**Important:** Each namespace can only contain one Redis Enterprise cluster. Multiple RECs with different operator versions can coexist on the same Kubernetes cluster, as long as they are in separate namespaces.\n\nThroughout this guide, each command is applied to the namespace in which the Redis Enterprise cluster operates.\n\n1. Create a new namespace\n\n    [code example]\n\n1. Change the namespace context to make the newly created namespace default for future commands.\n\n    [code example]\n\nYou can use an existing namespace as long as it does not contain any existing Redis Enterprise cluster resources. It's best practice to create a new namespace to make sure there are no Redis Enterprise resources that could interfere with the deployment."},{"id":"install-the-operator","title":"Install the operator","role":"setup","text":"Redis Enterprise for Kubernetes bundle is published as a container image. A list of required images is available in the [release notes](https://redis.io/docs/latest/operate/kubernetes/release-notes) for each version.\n\nThe operator [definition and reference materials](https://github.com/RedisLabs/redis-enterprise-k8s-docs) are available on GitHub. The operator definitions are [packaged as a single generic YAML file](https://github.com/RedisLabs/redis-enterprise-k8s-docs/blob/master/bundle.yaml).\n\n> [!NOTE]\n> If you do not pull images from DockerHub or another public registry, you need to use a [private container registry](https://redis.io/docs/latest/operate/kubernetes/deployment/container-images#manage-image-sources)."},{"id":"download-the-operator-bundle","title":"Download the operator bundle","role":"content","text":"Pull the latest version of the operator bundle:\n\n[code example]\n\n  If you need a different release, replace `VERSION` with a specific release tag.\n\n  Check version tags listed with the [operator releases on GitHub](https://github.com/RedisLabs/redis-enterprise-k8s-docs/releases) or by [using the GitHub API](https://docs.github.com/en/rest/reference/repos#releases) to ensure the version of the bundle is correct."},{"id":"deploy-the-operator-bundle","title":"Deploy the operator bundle","role":"content","text":"Apply the operator bundle in your REC namespace:\n\n[code example]\n\n  You should see a result similar to this:\n\n  [code example]\n\n> [!WARNING]\n> DO NOT modify or delete the StatefulSet created during the deployment process. Doing so could destroy your Redis Enterprise cluster (REC).\n\n#### Verify the operator is running\n\nCheck the operator deployment to verify it's running in your namespace:\n\n[code example]\n\nYou should see a result similar to this:\n\n[code example]"},{"id":"create-a-redis-enterprise-cluster-rec","title":"Create a Redis Enterprise cluster (REC)","role":"content","text":"A Redis Enterprise cluster (REC) is created from a `RedisEnterpriseCluster` custom resource\nthat contains cluster specifications.\n\nThe following example creates a minimal Redis Enterprise cluster. See the [RedisEnterpriseCluster API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api) for more information on the various options available.\n\n> [!NOTE]\n> If you suspect your file descriptor limits are below 100,000, you must either manually increase limits or [Allow automatic resource adjustment](https://redis.io/docs/latest/operate/kubernetes/security/allow-resource-adjustment). Most major cloud providers and standard container runtime configurations set default file descriptor limits well above the minimum required by Redis Enterprise. In these environments, you can safely run without enabling automatic resource adjustment.\n\n1. Create a file that defines a Redis Enterprise cluster with three nodes.\n\n    > [!NOTE]\n    > The REC name (`my-rec` in this example) cannot be changed after cluster creation.\n    >\n\n    [code example]\n\n   This will request a cluster with three Redis Enterprise nodes using the default requests (i.e., 2 CPUs and 4GB of memory per node).\n\n   To test with a larger configuration, use the example below to add node resources to the `spec` section of your test cluster (`my-rec.yaml`).\n\n    [code example]\n\n    > [!NOTE]\n    > Each cluster must have at least 3 nodes. Single-node RECs are not supported.\n    >\n\n    See the [Redis Enterprise hardware requirements](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/hardware-requirements) for more information on sizing Redis Enterprise node resource requests.\n  \n    > [!NOTE]\n    > If you enabled automatic resource adjustment in your configuration, this step will trigger the operator to apply elevated capabilities. Ensure your security context allows it.\n    >\n\n1. Apply your custom resource file in the same namespace as `my-rec.yaml`.\n\n    [code example]\n\n   You should see a result similar to this:\n\n   [code example]\n\n1. You can verify the creation of the cluster with:\n\n    [code example]\n\n    You should see a result similar to this:\n\n    [code example]\n\n    At this point, the operator will go through the process of creating various services and pod deployments.\n\n    You can track the progress by examining the StatefulSet associated with the cluster:\n\n    [code example]\n\n    or by looking at the status of all of the resources in your namespace:\n\n    [code example]"},{"id":"enable-the-admission-controller","title":"Enable the admission controller","role":"content","text":"The admission controller dynamically validates REDB resources configured by the operator. It is strongly recommended that you use the admission controller on your Redis Enterprise Cluster (REC). The admission controller only needs to be configured once per operator deployment.\n\nAs part of the REC creation process, the operator stores the admission controller certificate in a Kubernetes secret called `admission-tls`. You may have to wait a few minutes after creating your REC to see the secret has been created.\n\n1. Verify the `admission-tls` secret exists.\n\n    [code example]\n  \n    The output should look similar to\n  \n    [code example]\n\n1. Save the certificate to a local environment variable.\n\n    [code example]\n\n1. Create a Kubernetes validating webhook, replacing `<namespace>` with the namespace where the REC was installed.\n\n    The `webhook.yaml` template can be found in [redis-enterprise-k8s-docs/admission](https://github.com/RedisLabs/redis-enterprise-k8s-docs/tree/master/admission)\n\n    [code example]\n\n1. Create a patch file for the Kubernetes validating webhook.\n\n    [code example]\n\n1. Patch the webhook with the certificate.\n\n    [code example]"},{"id":"webhook","title":"Limit the webhook to the relevant namespaces","role":"limits","text":"The operator bundle includes a webhook file. The webhook will intercept requests from all namespaces unless you edit it to target a specific namespace. You can do this by adding the `namespaceSelector` section to the webhook spec to target a label on the namespace.\n\n1. Make sure the namespace has a unique `namespace-name` label.\n\n    [code example]\n\n1. Patch the webhook to add the `namespaceSelector` section.\n\n    [code example]\n\n1. Apply the patch.\n\n    [code example]"},{"id":"verify-the-admission-controller-is-working","title":"Verify the admission controller is working","role":"content","text":"1. Verify the admission controller is installed correctly by applying an invalid resource. This should force the admission controller to correct it.\n\n    [code example]\n\n    You should see your request was denied by the `admission webhook \"redisenterprise.admission.redislabs\"`.\n\n    [code example]"},{"id":"add-user-defined-modules-optional","title":"Add user-defined modules (optional)","role":"content","text":"If you plan to create databases that use user-defined modules (custom non-bundled modules), you must add them to the REC custom resource before creating the databases.\n\nSee [User-defined modules](https://redis.io/docs/latest/operate/kubernetes/re-databases/modules#user-defined-modules) for detailed instructions on adding and configuring custom modules."},{"id":"create-a-redis-enterprise-database-redb","title":"Create a Redis Enterprise Database (REDB)","role":"content","text":"You can create multiple databases within the same namespace as your REC or in other namespaces.\n\nSee [manage Redis Enterprise databases for Kubernetes](https://redis.io/docs/latest/operate/kubernetes/re-databases/db-controller) to create a new REDB."}],"examples":[{"id":"create-a-new-namespace-ex0","language":"sh","code":"kubectl create namespace <rec-namespace>","section_id":"create-a-new-namespace"},{"id":"create-a-new-namespace-ex1","language":"sh","code":"kubectl config set-context --current --namespace=<rec-namespace>","section_id":"create-a-new-namespace"},{"id":"download-the-operator-bundle-ex0","language":"sh","code":"VERSION=`curl --silent https://api.github.com/repos/RedisLabs/redis-enterprise-k8s-docs/releases/latest | grep tag_name | awk -F'\"' '{print $4}'`","section_id":"download-the-operator-bundle"},{"id":"deploy-the-operator-bundle-ex0","language":"sh","code":"kubectl apply -f https://raw.githubusercontent.com/RedisLabs/redis-enterprise-k8s-docs/$VERSION/bundle.yaml","section_id":"deploy-the-operator-bundle"},{"id":"deploy-the-operator-bundle-ex1","language":"sh","code":"role.rbac.authorization.k8s.io/redis-enterprise-operator created\n  serviceaccount/redis-enterprise-operator created\n  rolebinding.rbac.authorization.k8s.io/redis-enterprise-operator created\n  customresourcedefinition.apiextensions.k8s.io/redisenterpriseclusters.app.redislabs.com configured\n  customresourcedefinition.apiextensions.k8s.io/redisenterprisedatabases.app.redislabs.com configured\n  deployment.apps/redis-enterprise-operator created","section_id":"deploy-the-operator-bundle"},{"id":"deploy-the-operator-bundle-ex2","language":"sh","code":"kubectl get deployment redis-enterprise-operator","section_id":"deploy-the-operator-bundle"},{"id":"deploy-the-operator-bundle-ex3","language":"sh","code":"NAME                        READY   UP-TO-DATE   AVAILABLE   AGE\nredis-enterprise-operator   1/1     1            1           0m36s","section_id":"deploy-the-operator-bundle"},{"id":"create-a-redis-enterprise-cluster-rec-ex0","language":"sh","code":"cat <<EOF > my-rec.yaml\n    apiVersion: \"app.redislabs.com/v1\"\n    kind: \"RedisEnterpriseCluster\"\n    metadata:\n      name: my-rec\n    spec:\n      nodes: 3\n    EOF","section_id":"create-a-redis-enterprise-cluster-rec"},{"id":"create-a-redis-enterprise-cluster-rec-ex1","language":"sh","code":"redisEnterpriseNodeResources:\n      limits:\n        cpu: 2000m\n        memory: 16Gi\n      requests:\n        cpu: 2000m\n        memory: 16Gi","section_id":"create-a-redis-enterprise-cluster-rec"},{"id":"create-a-redis-enterprise-cluster-rec-ex2","language":"sh","code":"kubectl apply -f my-rec.yaml","section_id":"create-a-redis-enterprise-cluster-rec"},{"id":"create-a-redis-enterprise-cluster-rec-ex3","language":"sh","code":"redisenterprisecluster.app.redislabs.com/my-rec created","section_id":"create-a-redis-enterprise-cluster-rec"},{"id":"create-a-redis-enterprise-cluster-rec-ex4","language":"sh","code":"kubectl get rec","section_id":"create-a-redis-enterprise-cluster-rec"},{"id":"create-a-redis-enterprise-cluster-rec-ex5","language":"sh","code":"NAME           AGE\n    my-rec   1m","section_id":"create-a-redis-enterprise-cluster-rec"},{"id":"create-a-redis-enterprise-cluster-rec-ex6","language":"sh","code":"kubectl rollout status sts/my-rec","section_id":"create-a-redis-enterprise-cluster-rec"},{"id":"create-a-redis-enterprise-cluster-rec-ex7","language":"sh","code":"kubectl get all","section_id":"create-a-redis-enterprise-cluster-rec"},{"id":"enable-the-admission-controller-ex0","language":"sh","code":"kubectl get secret admission-tls","section_id":"enable-the-admission-controller"},{"id":"enable-the-admission-controller-ex1","language":"sh","code":"NAME            TYPE     DATA   AGE\n    admission-tls   Opaque   2      2m43s","section_id":"enable-the-admission-controller"},{"id":"enable-the-admission-controller-ex2","language":"sh","code":"CERT=`kubectl get secret admission-tls -o jsonpath='{.data.cert}'`","section_id":"enable-the-admission-controller"},{"id":"enable-the-admission-controller-ex3","language":"sh","code":"sed 's/OPERATOR_NAMESPACE/<namespace>/g' webhook.yaml | kubectl create -f -","section_id":"enable-the-admission-controller"},{"id":"enable-the-admission-controller-ex4","language":"sh","code":"cat > modified-webhook.yaml <<EOF\n    webhooks:\n    - name: redisenterprise.admission.redislabs\n      clientConfig:\n       caBundle: $CERT\n    EOF","section_id":"enable-the-admission-controller"},{"id":"enable-the-admission-controller-ex5","language":"sh","code":"kubectl patch ValidatingWebhookConfiguration \\\n        redis-enterprise-admission --patch \"$(cat modified-webhook.yaml)\"","section_id":"enable-the-admission-controller"},{"id":"webhook-ex0","language":"sh","code":"apiVersion: v1\n    kind: Namespace\n    metadata:\n      name: example-ns\n      labels:\n        namespace-name: example-ns","section_id":"webhook"},{"id":"webhook-ex1","language":"sh","code":"cat > modified-webhook.yaml <<EOF\n    webhooks:\n    - name: redisenterprise.admission.redislabs\n      namespaceSelector:\n        matchLabels:\n          namespace-name: example-ns\n    EOF","section_id":"webhook"},{"id":"webhook-ex2","language":"sh","code":"kubectl patch ValidatingWebhookConfiguration \\\n      redis-enterprise-admission --patch \"$(cat modified-webhook.yaml)\"","section_id":"webhook"},{"id":"verify-the-admission-controller-is-working-ex0","language":"sh","code":"kubectl apply -f - << EOF\n    apiVersion: app.redislabs.com/v1alpha1\n    kind: RedisEnterpriseDatabase\n    metadata:\n      name: redis-enterprise-database\n    spec:\n      evictionPolicy: illegal\n    EOF","section_id":"verify-the-admission-controller-is-working"},{"id":"verify-the-admission-controller-is-working-ex1","language":"sh","code":"Error from server: error when creating \"STDIN\": admission webhook \"redisenterprise.admission.redislabs\" denied the request: eviction_policy: u'illegal' is not one of [u'volatile-lru', u'volatile-ttl', u'volatile-random', u'allkeys-lru', u'allkeys-random', u'noeviction', u'volatile-lfu', u'allkeys-lfu']","section_id":"verify-the-admission-controller-is-working"}]}
{"schema_version":2,"id":"operate/kubernetes/flex/auto-tiering","title":"Use Auto Tiering on Kubernetes","url":"https://redis.io/docs/latest/operate/kubernetes/flex/auto-tiering/","summary":"Deploy a cluster with Auto Tiering on Kubernetes.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T16:14:52+01:00","page_type":"content","content_hash":"064639f86274446c5cdbc026bc0049795cb8e0d43abf00413aba1f0b42aece44","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This page applies to Redis database version 7.4 and earlier. If you use version 8.0 or later, see [Redis Flex](https://redis.io/docs/latest/operate/kubernetes/re-clusters/redis-flex)."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Redis Enterprise Software for Kubernetes supports using Auto Tiering (previously known as Redis on Flash), which extends your node memory to use both RAM and flash storage. SSDs (solid state drives) can store infrequently used (warm) values while your keys and frequently used (hot) values are still stored in RAM. This improves performance and lowers costs for large datasets.\n\n> [!NOTE]\n> NVMe (non-volatile memory express) SSDs are strongly recommended to achieve the best performance.\n\nBefore creating your Redis clusters or databases, these SSDs must be:\n\n- [locally attached to worker nodes in your Kubernetes cluster](https://kubernetes.io/docs/concepts/storage/volumes/#local)\n- formatted and mounted on the nodes that will run Redis Enterprise pods\n- dedicated to Auto Tiering and not shared with other parts of the database, (e.g. durability, binaries)\n- [provisioned as local persistent volumes](https://kubernetes.io/docs/concepts/storage/volumes/#local)\n  - You can use a [local volume provisioner](https://github.com/kubernetes-sigs/sig-storage-local-static-provisioner/blob/master/README.md) to do this [dynamically](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#dynamic)\n- a [StorageClass](https://kubernetes.io/docs/concepts/storage/storage-classes/#local) resource with a unique name\n\nFor more information on node storage, see [Node persistent and ephemeral storage](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/persistent-ephemeral-storage)."},{"id":"create-a-redis-enterprise-cluster","title":"Create a Redis Enterprise cluster","role":"content","text":"To deploy a Redis Enterprise cluster (REC) with Auto Tiering, you'll need to specify the following in the `redisOnFlashSpec` section of your [REC custom resource](https://redis.io/docs/latest/operate/kubernetes/7.22/reference/api/redis_enterprise_cluster_api):\n\n- enable Auto Tiering (`enabled: true`)\n- flash storage driver (`bigStoreDriver`)\n  - `rocksdb` or `speedb`(default)\n- storage class name (`storageClassName`)\n- minimal flash disk size (`flashDiskSize`)\n\n> [!NOTE]\n>  Clusters upgraded to version 7.2.4-2 from an earlier version will change the `bigStoreDriver` (previously called `flashStorageEngine`) to the new default `speedb`, regardless of previous configuration. \n\n> [!WARNING]\n> Switching between storage engines (`speedb` and `rocksdb`) requires guidance by Redis Support or your Account Manager.\n\n> [!WARNING]\n> PVC expansion is not supported when using Auto Tiering. Do not enable `enablePersistentVolumeResize` in the REC `persistentSpec` if you are using `redisOnFlashSpec` as this will result in conflicts. \n\nHere is an example of an REC custom resource with these attributes:\n\n[code example]"},{"id":"create-a-redis-enterprise-database","title":"Create a Redis Enterprise database","role":"content","text":"By default, any new database will use RAM only. To create a Redis Enterprise database (REDB) that can use flash storage, specify the following in the `redisEnterpriseCluster` section of the REDB custom resource definition:\n\n- `isRof: true` enables Auto Tiering\n- `rofRamSize` defines the RAM capacity for the database\n\nBelow is an example REDB custom resource:\n\n[code example]\n\n> [!NOTE]\n> This example defines both `memorySize` and `rofRamSize`. When using Auto Tiering, `memorySize` refers to the total combined memory size (RAM + flash) allocated for the database. `rofRamSize` specifies only the RAM capacity for the database. `rofRamSize` must be at least 10% of `memorySize`."}],"examples":[{"id":"create-a-redis-enterprise-cluster-ex0","language":"YAML","code":"apiVersion: app.redislabs.com/v1\nkind: RedisEnterpriseCluster\nmetadata:\n  name: \"rec\"\nspec:\n  \n  nodes: 3\n  redisOnFlashSpec:\n    enabled: true\n    bigStoreDriver: speedb\n    storageClassName: local-scsi\n    flashDiskSize: 100G","section_id":"create-a-redis-enterprise-cluster"},{"id":"create-a-redis-enterprise-database-ex0","language":"YAML","code":"apiVersion: app.redislabs.com/v1alpha1\nkind: RedisEnterpriseDatabase\nmetadata:\n  name: autoteiring-redb\nspec:\n  redisEnterpriseCluster:\n    name: rec\n  isRof: true\n  memorySize: 2GB\n  rofRamSize: 0.5GB","section_id":"create-a-redis-enterprise-database"}]}
{"schema_version":2,"id":"operate/kubernetes/flex/get-started","title":"Get started with Flex on Kubernetes","url":"https://redis.io/docs/latest/operate/kubernetes/flex/get-started/","summary":"Configure Flex on your Redis Enterprise cluster for Kubernetes.","aliases":["/operate/kubernetes/re-clusters/redis-flex/"],"tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T16:14:52+01:00","page_type":"content","content_hash":"4fb73284ea2236b23705079a327820bb3a521c09f0e880f7d23b26fc29f624f1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This page applies to Redis database version 7.4 and earlier using Auto Tiering. If you use version 8.0 or later, see [Redis Flex](https://redis.io/docs/latest/operate/kubernetes/flex/).\n\nFlex extends your database capacity by combining RAM and flash (SSD) storage. This tiered architecture keeps frequently accessed (hot) data in RAM for sub-millisecond latency while storing less active (warm) data on flash to reduce costs and increase capacity."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before you begin, verify that you have:\n\n- Redis Enterprise for Kubernetes version 8.0.2-2 or later installed\n- A running `RedisEnterpriseCluster` (REC) resource\n- Redis database version 7.4 or later\n- Locally attached NVMe SSDs on your worker nodes\n- A StorageClass configured for flash storage with a unique name\n\nFor hardware requirements and sizing guidelines, see [Plan your deployment](https://redis.io/docs/latest/operate/kubernetes/flex/plan)."},{"id":"configure-the-rec-for-flex","title":"Configure the REC for Flex","role":"configuration","text":"To enable Flex, configure your `RedisEnterpriseCluster` (REC) resource with flash storage settings. Add the [`redisOnFlashSpec`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api#specredisonflashspec) section to your REC specification.\n\nKey fields in `redisOnFlashSpec`:\n\n| Field                   | Description                                      |\n|-------------------------|--------------------------------------------------|\n| `enabled`               | Set to `true` to enable Flex.                    |\n| `flashStorageClassName` | Name of the StorageClass for flash storage.      |\n| `flashDiskSize`         | Size of the flash storage per node.              |\n| `storageEngine`         | Storage engine. Set to `speedb` for Flex.        |\n\nFor all available fields, see the [REC API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api#specredisonflashspec).\n\n> [!WARNING]\n> PVC expansion is not supported when using Flex. Do not enable `enablePersistentVolumeResize` in the REC `persistentSpec` if you are using `redisOnFlashSpec` as this will result in conflicts.\n\n1. Create a REC specification file with flash storage settings similar to the following example:\n\n    [code example]\n\n1. Apply the REC configuration:\n\n    [code example]\n\n1. Verify that the cluster is ready:\n\n    [code example]"},{"id":"create-a-flex-database","title":"Create a Flex database","role":"content","text":"After you configure the cluster, create a `RedisEnterpriseDatabase` (REDB) resource with Flex enabled.\n\nKey fields in the REDB:\n\n| Field        | Description                                            |\n|--------------|--------------------------------------------------------|\n| `memorySize` | Total database size (RAM + flash).                     |\n| `isRof`      | Set to `true` to enable Flex for this database.        |\n| `rofRamSize` | Amount of RAM allocated to the database.               |\n\nFor all available fields, see the [REDB API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api).\n\n1. Create a database specification file with `isRof` set to `true` similar to the following example:\n\n    [code example]\n\n1. Apply the REDB:\n\n    [code example]\n\n1. Verify the database status:\n\n    [code example]"},{"id":"verify-flex-is-active","title":"Verify Flex is active","role":"content","text":"To confirm that Flex is working:\n\n1. Check the database status:\n\n    [code example]\n\n2. Look for `isRof: true` in the status section.\n\n3. Connect to the database and verify that data operations work correctly."},{"id":"next-steps","title":"Next steps","role":"content","text":"- [Scale your deployment](https://redis.io/docs/latest/operate/kubernetes/flex/scale): Learn how to scale volume, throughput, and infrastructure.\n- [Plan your deployment](https://redis.io/docs/latest/operate/kubernetes/flex/plan): Review sizing guidelines and best practices."}],"examples":[{"id":"configure-the-rec-for-flex-ex0","language":"yaml","code":"apiVersion: app.redislabs.com/v1\n    kind: RedisEnterpriseCluster\n    metadata:\n      name: rec\n    spec:\n      nodes: 3\n      redisOnFlashSpec:\n        enabled: true\n        flashStorageClassName: local-flash\n        flashDiskSize: 100Gi\n        storageEngine: speedb","section_id":"configure-the-rec-for-flex"},{"id":"configure-the-rec-for-flex-ex1","language":"sh","code":"kubectl apply -f rec.yaml","section_id":"configure-the-rec-for-flex"},{"id":"configure-the-rec-for-flex-ex2","language":"sh","code":"kubectl get rec","section_id":"configure-the-rec-for-flex"},{"id":"create-a-flex-database-ex0","language":"yaml","code":"apiVersion: app.redislabs.com/v1alpha1\n    kind: RedisEnterpriseDatabase\n    metadata:\n      name: flex-db\n    spec:\n      memorySize: 10Gi\n      isRof: true\n      rofRamSize: 2Gi","section_id":"create-a-flex-database"},{"id":"create-a-flex-database-ex1","language":"sh","code":"kubectl apply -f redb.yaml","section_id":"create-a-flex-database"},{"id":"create-a-flex-database-ex2","language":"sh","code":"kubectl get redb","section_id":"create-a-flex-database"},{"id":"verify-flex-is-active-ex0","language":"sh","code":"kubectl get redb <database-name> -o yaml","section_id":"verify-flex-is-active"}]}
{"schema_version":2,"id":"operate/kubernetes/flex","title":"Flex on Kubernetes","url":"https://redis.io/docs/latest/operate/kubernetes/flex/","summary":"Extend Redis databases with flash storage for large-scale, cost-efficient deployments on Kubernetes.","aliases":["/operate/kubernetes/re-clusters/redis-flex/"],"tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T16:14:52+01:00","children":[{"id":"operate/kubernetes/flex/plan","summary":"Hardware requirements, sizing guidelines, and limitations for Redis Flex on Kubernetes.","title":"Plan a Redis Flex deployment on Kubernetes","url":"https://redis.io/docs/latest/operate/kubernetes/flex/plan/"},{"id":"operate/kubernetes/flex/get-started","summary":"Configure Flex on your Redis Enterprise cluster for Kubernetes.","title":"Get started with Flex on Kubernetes","url":"https://redis.io/docs/latest/operate/kubernetes/flex/get-started/"},{"id":"operate/kubernetes/flex/scale","summary":"Scaling strategies and methods for Redis Flex deployments on Kubernetes.","title":"Scale Redis Flex on Kubernetes","url":"https://redis.io/docs/latest/operate/kubernetes/flex/scale/"},{"id":"operate/kubernetes/flex/auto-tiering","summary":"Deploy a cluster with Auto Tiering on Kubernetes.","title":"Use Auto Tiering on Kubernetes","url":"https://redis.io/docs/latest/operate/kubernetes/flex/auto-tiering/"}],"page_type":"content","content_hash":"4f28f319a46221d5e48ca68822106f888156f165a811e59e458603df41a0b2d4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Flex extends your database capacity by combining RAM and flash (SSD) storage. This tiered architecture keeps frequently accessed (hot) data in RAM for sub-millisecond latency while storing less active (warm) data on flash to reduce costs and increase capacity.\n\nFlex databases work with your existing Redis applications and the Redis API without modification."},{"id":"how-flex-works","title":"How Flex works","role":"content","text":""},{"id":"automatic-data-tiering","title":"Automatic data tiering","role":"content","text":"Flex moves data between RAM and flash based on access patterns:\n\n- Frequently accessed data stays in high-speed RAM.\n- Less active data moves to cost-efficient flash storage.\n- Data accessed from flash promotes back to RAM automatically.\n\nRedis uses an [LRU (least recently used)](https://redis.io/docs/latest/develop/reference/eviction#apx-lru) eviction policy to manage data placement. When memory pressure increases, Flex identifies cold objects, transfers them to flash, and frees RAM for new or frequently accessed keys.\n\nThis process requires no application changes. Your existing Redis commands work across both storage tiers."},{"id":"utilization-aware-ram-population","title":"Utilization-aware RAM population","role":"content","text":"Flex uses a hybrid memory model that balances performance with predictable behavior as the dataset grows. The goal is to maximize hot data in RAM while maintaining stable performance across all utilization levels.\n\nFlex considers both the configured RAM percentage and current dataset utilization:\n\n- When database utilization is below 50%, Flex uses up to 50% of configured RAM for hot data.\n- Above 50% utilization, Flex uses both RAM and flash proportionally, following the configured RAM-to-flash ratio.\n\nThis ensures:\n\n- A stable performance curve across all utilization levels\n- No sudden drop-off in RAM hit-rate as the database fills\n- Predictable throughput and latency, especially for large deployments"},{"id":"storage-engine","title":"Storage engine","role":"content","text":"Flex uses [Speedb](https://docs.speedb.io), a high-performance key-value storage engine optimized for flash drives:\n\n- Redis handles all data operations in memory.\n- Speedb manages the flash storage layer.\n\nThis design delivers predictable latency and throughput as datasets grow beyond RAM limits."},{"id":"compatibility","title":"Compatibility","role":"compatibility","text":"Flex is compatible with the Redis API and supports all Redis data types, including JSON and probabilistic data structures (Bloom filters, Count-Min Sketch, Top-K).\n\nThe following features are not yet supported with Flex:\n\n- Redis Query Engine (RQE)\n- Time series\n- Active-Active"},{"id":"when-to-use-flex","title":"When to use Flex","role":"content","text":"Use Flex when you need to:\n\n- Run Redis at terabyte scale while maintaining high throughput and sub-10 ms latency\n- Power real-time feature stores for machine learning applications\n- Operate large distributed caches with elastic scaling and consistent performance under heavy load\n- Reduce infrastructure costs by combining high-speed RAM with cost-efficient flash storage\n\n> [!WARNING]\n> Flex does not replace long-term data persistence. For workloads that require durability and recovery across restarts or failures, use Redis persistence features like [AOF (Append-Only File)](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence#append-only-file), [RDB snapshots](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence#snapshotting), or both. For more information, see [Database persistence](https://redis.io/docs/latest/operate/rs/databases/configure/database-persistence)."},{"id":"flex-and-auto-tiering","title":"Flex and Auto Tiering","role":"content","text":"Flex replaces [Auto Tiering](https://redis.io/docs/latest/operate/kubernetes/7.22/re-clusters/auto-tiering) (formerly known as Redis on Flash). Redis Enterprise selects the implementation based on your database version:\n\n| Redis database version | Operator version | Redis Flex | Auto Tiering |\n|------------------------|------------------|------------|--------------|\n| 8.0 and later | 8.0.2-2 and later | <span title=\"Supported\">&#x2705;</span> | <span title=\"Not supported\">&#x274c;</span> |\n| 7.4 | 7.8.2-6 and later | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 7.2 and earlier | 7.22.2-22 and earlier | <span title=\"Not supported\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> |\n\nFor Redis Enterprise for Kubernetes version 7.22.2-22 or earlier, see [Auto Tiering](https://redis.io/docs/latest/operate/kubernetes/7.22/re-clusters/auto-tiering)."},{"id":"differences-between-flex-and-auto-tiering","title":"Differences between Flex and Auto Tiering","role":"content","text":"- Key and value offloading\n  - Auto Tiering offloads only values to flash while keys remain in RAM.\n  - Flex offloads both keys and values, which increases dataset density per node and reduces RAM consumption.\n- RAM requirements\n  - Auto Tiering requires enough RAM to hold all keys.\n  - Flex has no requirement to keep keys in RAM. Minimum RAM is based on performance goals, not key count. The minimum RAM-to-flash ratio is 10%, which enables high flash utilization without compromising architecture.\n- RAM population strategy\n  - Auto Tiering fills all available RAM before offloading data to flash. This maximizes hot-data performance but can cause non-linear performance changes at high utilization.\n  - Flex uses utilization-aware RAM population. \n- Storage engine\n  - Auto Tiering uses either RocksDB or Speedb as the storage engine.\n  - Flex uses Speedb only.\n- Hot data caching and dataset size\n  - Auto Tiering is limited by key memory overhead, which restricts maximum dataset size.\n  - Flex is more efficient because RAM holds hot keys and values, supporting larger datasets.\n- Performance consistency\n  - Auto Tiering depends on RAM pressure and key count.\n  - Flex provides higher RAM hit-rate and more stable latency."},{"id":"next-steps","title":"Next steps","role":"content","text":"- [Plan your deployment](https://redis.io/docs/latest/operate/kubernetes/flex/plan): Review hardware requirements, sizing guidelines, and limitations.\n- [Get started](https://redis.io/docs/latest/operate/kubernetes/flex/get-started): Configure Flex on your cluster.\n- [Scale your deployment](https://redis.io/docs/latest/operate/kubernetes/flex/scale): Learn scaling strategies and best practices."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/flex/plan","title":"Plan a Redis Flex deployment on Kubernetes","url":"https://redis.io/docs/latest/operate/kubernetes/flex/plan/","summary":"Hardware requirements, sizing guidelines, and limitations for Redis Flex on Kubernetes.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T16:14:52+01:00","page_type":"content","content_hash":"5066abb0b5b83c646cd70d4db191c7e0b2556f2c0ca53024e7bc2fdf294a46be","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Review the hardware requirements, sizing guidelines, and known limitations before you deploy Redis Flex."},{"id":"version-requirements","title":"Version requirements","role":"compatibility","text":"Redis Flex requires:\n\n- Redis Enterprise for Kubernetes version 8.0.2-2 or later\n- Redis database version 8.2 or later"},{"id":"hardware-requirements","title":"Hardware requirements","role":"content","text":""},{"id":"flash-storage-requirements","title":"Flash storage requirements","role":"content","text":"Your SSDs must meet these requirements:\n\n- **Locally attached** to worker nodes in your Kubernetes cluster. Network-attached storage (NAS), storage area networks (SAN), and cloud block storage (like AWS EBS) are not supported.\n- **Dedicated** to Redis Flex. Don't share Flash storage with other database components like durability, binaries, or persistence.\n- **Formatted and mounted** on the nodes that run Redis Enterprise pods.\n- **Provisioned as local persistent volumes**. You can use a [local volume provisioner](https://github.com/kubernetes-sigs/sig-storage-local-static-provisioner) for dynamic provisioning.\n- **Configured with a StorageClass** resource with a unique name.\n\nFor more information about storage configuration, see [Kubernetes local volumes](https://kubernetes.io/docs/concepts/storage/volumes/#local)."},{"id":"flash-capacity-sizing","title":"Flash capacity sizing","role":"content","text":"Provision Flash capacity that exceeds your total database size. The extra space accounts for:\n\n- Write buffers\n- Space amplification\n- Operational overhead"},{"id":"recommended-ssd-types","title":"Recommended SSD types","role":"content","text":"For best performance, use NVMe SSDs:\n\n| SSD type              | Recommendation                 |\n|-----------------------|--------------------------------|\n| NVMe Gen 5 or Gen 4   | Recommended                    |\n| NVMe Gen 3            | Supported (lower performance)  |"},{"id":"sizing-guidelines","title":"Sizing guidelines","role":"content","text":""},{"id":"shard-fundamentals","title":"Shard fundamentals","role":"content","text":"Redis Flex databases consist of shards—independent data partitions that handle a portion of the dataset and request load. Each shard runs as a self-contained Redis process with dedicated memory, CPU, and Flash resources."},{"id":"recommended-shard-size","title":"Recommended shard size","role":"content","text":"Use these standard building blocks for capacity planning:\n\n- **50 GB per shard**: Standard capacity unit\n- **1 vCPU per shard**: Baseline compute allocation\n\nFor example, a 1 TB dataset requires 20 shards (20 × 50 GB)."},{"id":"ram-to-flash-ratio","title":"RAM-to-flash ratio","role":"content","text":"The RAM-to-flash ratio directly affects throughput and latency:\n\n- More RAM: Higher throughput and lower latency\n- Less RAM: Lower throughput, higher latency, and lower cost"},{"id":"module-compatibility","title":"Module compatibility","role":"content","text":""},{"id":"supported","title":"Supported","role":"compatibility","text":"Redis Flex supports:\n\n- All standard Redis data types\n- Redis JSON\n- Redis probabilistic data structures (Bloom filters, Count-Min Sketch, Top-K)"},{"id":"not-supported","title":"Not supported","role":"content","text":"Redis Flex doesn't support:\n\n- Redis Query Engine (RQE)\n- RedisTimeSeries\n- Active-Active databases"},{"id":"best-practices","title":"Best practices","role":"content","text":"- Store small keys and values (under 10 KB) when possible. Large objects reduce performance, and objects larger than 4 GB can't be stored in Flash.\n- Enable replication before populating the database with data. If you enable replication after the database is created, recovery can fail because required persistence files might be missing.\n- Increase RAM percentage if 99th-percentile latency exceeds your target or cache hit rates drop.\n- Decrease RAM percentage if memory utilization is high but latency remains stable to lower cost and improve efficiency.\n- Re-evaluate the ratio of RAM to Flash periodically as dataset size and access patterns evolve."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"- **Flash storage**: Must be locally attached. Network storage isn't supported.\n- **Active-Active**: Not supported with Flex.\n- **PVC expansion**: Not supported with `redisOnFlashSpec`. Don't enable `enablePersistentVolumeResize` in the REC `persistentSpec`.\n- **Maximum object size**: Keys or values larger than 4 GB remain in RAM only.\n\nAvoid Flex in the following situations:\n\n- Your working set is significantly smaller than the total dataset and access is biased toward recent data (high RAM hit rate).\n- Your workload uses very long key names, broad or random access patterns, or a very large working set. These patterns reduce Flex benefits and increase flash I/O."},{"id":"deprecated-fields","title":"Deprecated fields","role":"content","text":"The `flashStorageEngine` field is deprecated. Use `bigStoreDriver` instead."},{"id":"next-steps","title":"Next steps","role":"content","text":"- [Get started](https://redis.io/docs/latest/operate/kubernetes/flex/get-started): Configure Redis Flex on your cluster.\n- [Scale your deployment](https://redis.io/docs/latest/operate/kubernetes/flex/scale): Learn scaling strategies."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/flex/scale","title":"Scale Redis Flex on Kubernetes","url":"https://redis.io/docs/latest/operate/kubernetes/flex/scale/","summary":"Scaling strategies and methods for Redis Flex deployments on Kubernetes.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T16:14:52+01:00","page_type":"content","content_hash":"13d3e8b22bd3a9332100e64951e400f4e73a67a90d017e9d9c42b4dca8fd5094","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This guide shows you how to scale Flex databases on Kubernetes to meet changing workload demands."},{"id":"choose-a-scaling-strategy","title":"Choose a scaling strategy","role":"content","text":"Use the following table to determine the best scaling strategy for your Flex deployment:\n\n| Goal | Recommended action |\n|------|--------------------|\n| Increase data capacity only without adding CPU | [Increase `memorySize` and decrease RAM percentage](#decrease-ram-to-flash-ratio) |\n| Increase throughput only | [Add shards and vCPU](#add-shards-or-nodes) |\n| Increase data capacity and throughput | [Add shards](#add-shards) |\n| Improve latency under higher load | [Increase RAM percentage](#increase-ram-to-flash-ratio) |\n| Reduce cost while maintaining performance | [Tune RAM-to-flash ratio](#decrease-ram-to-flash-ratio) |"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before you scale a Flex deployment, verify that your cluster has sufficient resources, such as memory, disk, and vCPU."},{"id":"scale-volume","title":"Scale volume","role":"content","text":"If your dataset requires more capacity while maintaining performance, you can prepare a Flex database to store more data using one of the following options:\n\n1. Increase `memorySize` and [add shards](#add-shards).\n\n1. Increase `memorySize` and [decrease the RAM-to-flash ratio](#decrease-ram-to-flash-ratio)."},{"id":"add-shards","title":"Add shards","role":"content","text":"You can add more shards to expand dataset capacity while maintaining the existing RAM-to-flash ratio. Throughput capacity also typically increases as a result of additional shards and infrastructure. This strategy is recommended when the dataset size and traffic are expected to grow together.\n\nBefore you increase the dataset capacity and add shards, you need to add more RAM and vCPUs to handle the increased number of shards.\n\nTo increase the dataset capacity and shards:\n\n1. Edit your REDB custom resource:\n\n    [code example]\n\n1. Increase `memorySize` in the `spec` section.\n\n1. Increase `shardCount` in the `spec` section.\n\n1. Save and close the file."},{"id":"decrease-ram-to-flash-ratio","title":"Decrease RAM-to-flash ratio","role":"content","text":"You can allocate more data to the flash tier to increase the database capacity while keeping the same amount of RAM, shards, and vCPU. This strategy is recommended when scaling for volume only and SSD resources are underutilized. This increases capacity without adding CPU or RAM but can lower RAM hit-rate and increase p99 latency; monitor metrics before and after the change.\n\nTo increase the dataset capacity and decrease the RAM-to-flash ratio:\n\n1. Edit your REDB custom resource:\n\n    [code example]\n\n1. Increase `memorySize` in the `spec` section.\n\n1. Decrease `rofRamSize` in the `redisOnFlashSpec` section.\n\n1. Save and close the file."},{"id":"scale-throughput","title":"Scale throughput","role":"content","text":"If your workload's read/write rate increases and latency starts to rise, you can prepare the database to handle more traffic using one of the following strategies:\n\n1. [Add shards or nodes](#add-shards-or-nodes).\n\n1. [Increase the RAM-to-flash ratio](#increase-ram-to-flash-ratio)."},{"id":"add-shards-or-nodes","title":"Add shards or nodes","role":"content","text":"You can add more shards or nodes to distribute traffic and increase throughput without changing the RAM-to-flash ratio. Dataset size capacity also typically increases as a result of additional shards and infrastructure. This strategy is recommended when the dataset size and traffic are expected to grow together.\n\nBefore you add shards or nodes, you need to add more RAM and vCPUs to handle the increased number of shards or nodes. This increases capacity and potential throughput but requires more RAM, vCPUs, and a rebalance operation.\n\nTo add shards:\n\n1. Edit your REDB custom resource:\n\n    [code example]\n\n1. Increase `shardCount` in the `spec` section.\n\n1. Save and close the file.\n\nTo add nodes to the cluster, increase the `nodes` count in your RedisEnterpriseCluster (REC) custom resource:\n\n[code example]"},{"id":"increase-ram-to-flash-ratio","title":"Increase RAM-to-flash ratio","role":"content","text":"To improve throughput and lower latency, you can expand the in-memory tier to serve a higher proportion of requests directly from RAM. This strategy is recommended when low latency is your primary goal and you don't need to increase the dataset size.\n\nBefore increasing the RAM-to-flash ratio, you might need to add more nodes to accommodate additional RAM. This improves throughput and lowers latency by serving more requests from RAM, at the cost of higher RAM usage.\n\nTo increase the RAM-to-flash ratio:\n\n1. Edit your REDB custom resource:\n\n    [code example]\n\n1. Increase `rofRamSize` in the `redisOnFlashSpec` section.\n\n1. Save and close the file."},{"id":"scale-infrastructure","title":"Scale infrastructure","role":"content","text":"You can increase or adjust the underlying resources supporting the database, such as CPU, memory, and disk.\n\nFor Flex deployments on Kubernetes, ensure the cluster has sufficient physical resources before scaling. The cluster requires:\n\n- Enough RAM to support the desired in-memory dataset size.\n\n- Enough SSD capacity for flash-tier data.\n\n- Adequate vCPU to support increased shard count or throughput.\n\n> [!WARNING]\n> Scaling operations will fail or underperform if the underlying cluster is resource-constrained.\n\nPVC expansion is not supported with `redisOnFlashSpec`. Plan flash storage capacity upfront and don't enable `enablePersistentVolumeResize` in the REC `persistentSpec`.\n\nSee Flex [hardware requirements](https://redis.io/docs/latest/operate/kubernetes/flex/plan#hardware-requirements) for more information."}],"examples":[{"id":"add-shards-ex0","language":"sh","code":"kubectl edit redb <database-name>","section_id":"add-shards"},{"id":"decrease-ram-to-flash-ratio-ex0","language":"sh","code":"kubectl edit redb <database-name>","section_id":"decrease-ram-to-flash-ratio"},{"id":"add-shards-or-nodes-ex0","language":"sh","code":"kubectl edit redb <database-name>","section_id":"add-shards-or-nodes"},{"id":"add-shards-or-nodes-ex1","language":"sh","code":"kubectl edit rec <cluster-name>","section_id":"add-shards-or-nodes"},{"id":"increase-ram-to-flash-ratio-ex0","language":"sh","code":"kubectl edit redb <database-name>","section_id":"increase-ram-to-flash-ratio"}]}
{"schema_version":2,"id":"operate/kubernetes","title":"Redis Enterprise for Kubernetes","url":"https://redis.io/docs/latest/operate/kubernetes/","summary":"Deploy and manage Redis Enterprise on Kubernetes with the Redis Enterprise operator.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T16:14:52+01:00","children":[{"id":"operate/kubernetes/architecture","summary":"Overview of the architecture and components of Redis Enterprise for Kubernetes.","title":"Redis Enterprise for Kubernetes architecture","url":"https://redis.io/docs/latest/operate/kubernetes/architecture/"},{"id":"operate/kubernetes/deployment","summary":"Deploy Redis Enterprise for Kubernetes using the Redis Enterprise operator on various Kubernetes distributions.","title":"Deployment","url":"https://redis.io/docs/latest/operate/kubernetes/deployment/"},{"id":"operate/kubernetes/upgrade","summary":"Upgrade Redis Enterprise operator, clusters, and databases on Kubernetes.","title":"Upgrade Redis Enterprise for Kubernetes","url":"https://redis.io/docs/latest/operate/kubernetes/upgrade/"},{"id":"operate/kubernetes/re-clusters","summary":"Create and manage Redis Enterprise clusters (REC) on Kubernetes using the Redis Enterprise operator.","title":"Redis Enterprise clusters (REC)","url":"https://redis.io/docs/latest/operate/kubernetes/re-clusters/"},{"id":"operate/kubernetes/re-databases","summary":"Create and manage Redis Enterprise databases (REDB) on Kubernetes using the Redis Enterprise operator.","title":"Redis Enterprise databases (REDB)","url":"https://redis.io/docs/latest/operate/kubernetes/re-databases/"},{"id":"operate/kubernetes/active-active","summary":"Create and manage Active-Active Redis Enterprise databases across multiple Kubernetes clusters.","title":"Active-Active databases","url":"https://redis.io/docs/latest/operate/kubernetes/active-active/"},{"id":"operate/kubernetes/networking","summary":"Configure networking and external access for Redis Enterprise clusters and databases on Kubernetes.","title":"Networking","url":"https://redis.io/docs/latest/operate/kubernetes/networking/"},{"id":"operate/kubernetes/flex","summary":"Extend Redis databases with flash storage for large-scale, cost-efficient deployments on Kubernetes.","title":"Flex on Kubernetes","url":"https://redis.io/docs/latest/operate/kubernetes/flex/"},{"id":"operate/kubernetes/security","summary":"Configure security settings for Redis Software clusters and databases on Kubernetes.","title":"Security","url":"https://redis.io/docs/latest/operate/kubernetes/security/"},{"id":"operate/kubernetes/logs","summary":"Access and manage Redis Enterprise logs on Kubernetes for monitoring and troubleshooting.","title":"Logs","url":"https://redis.io/docs/latest/operate/kubernetes/logs/"},{"id":"operate/kubernetes/recommendations","summary":"Best practices and configuration recommendations for Redis Enterprise on Kubernetes deployments.","title":"Configuration recommendations","url":"https://redis.io/docs/latest/operate/kubernetes/recommendations/"},{"id":"operate/kubernetes/reference","summary":"Resources to help you manage Redis Enterprise custom resources on Kubernetes.","title":"Reference","url":"https://redis.io/docs/latest/operate/kubernetes/reference/"},{"id":"operate/kubernetes/release-notes","summary":"Redis Enterprise Software for Kubernetes operator release notes.","title":"Redis Enterprise Software for Kubernetes release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/"},{"id":"operate/kubernetes/kubernetes-archive","summary":"Describes where to view the archive of Redis Enterprise for Kubernetes documentation.","title":"Archive","url":"https://redis.io/docs/latest/operate/kubernetes/kubernetes-archive/"}],"page_type":"content","content_hash":"a673180d66e3cefa7c915b5641649f8dc0779a3c279441eb2bf06f7bfc6e3c47","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise for Kubernetes brings Redis Enterprise to Kubernetes environments through the Redis Enterprise operator. You can deploy, scale, and manage Redis Enterprise clusters and databases by using native Kubernetes resources and workflows.\n\nRedis Enterprise for Kubernetes provides all the enterprise features of Redis Software:\n\n- Linear scalability with Redis clustering\n- High availability with automatic failover\n- Active-Active geo-distribution\n- Redis Flex for cost optimization\n- Enterprise-grade security and encryption\n- 24/7 support\n\nThe Redis Enterprise operator simplifies deployment and management by providing custom resource definitions (CRDs) for Redis Enterprise clusters (REC) and databases (REDB). This approach enables GitOps workflows and Kubernetes-native operations.\n\nRedis Enterprise for Kubernetes is compatible with [CNCF-conformant](https://www.cncf.io/training/certification/software-conformance/) Kubernetes platforms. The operator follows standard Kubernetes APIs and practices and is designed to run consistently across certified Kubernetes environments."},{"id":"get-started","title":"Get started","role":"content","text":"Deploy Redis Enterprise on your Kubernetes cluster and create your first database.\n\n- [Quick start deployment](https://redis.io/docs/latest/operate/kubernetes/deployment/quick-start)\n- [Deploy with Helm](https://redis.io/docs/latest/operate/kubernetes/deployment/helm)\n- [Deploy on OpenShift](https://redis.io/docs/latest/operate/kubernetes/deployment/openshift)\n- [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions)"},{"id":"redis-enterprise-clusters-rec","title":"Redis Enterprise clusters (REC)","role":"content","text":"Create and manage [Redis Enterprise clusters](https://redis.io/docs/latest/operate/kubernetes/re-clusters) on Kubernetes.\n\n- [Connect to admin console](https://redis.io/docs/latest/operate/kubernetes/re-clusters/connect-to-admin-console)\n- [Redis Flex](https://redis.io/docs/latest/operate/kubernetes/flex)\n- [Multi-namespace deployment](https://redis.io/docs/latest/operate/kubernetes/re-clusters/multi-namespace)\n- [Cluster recovery](https://redis.io/docs/latest/operate/kubernetes/re-clusters/cluster-recovery)\n- [REC API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api)"},{"id":"redis-enterprise-databases-redb","title":"Redis Enterprise databases (REDB)","role":"content","text":"Create and manage [Redis Enterprise databases](https://redis.io/docs/latest/operate/kubernetes/re-databases) using Kubernetes resources.\n\n- [Database controller](https://redis.io/docs/latest/operate/kubernetes/re-databases/db-controller)\n- [Create replica databases](https://redis.io/docs/latest/operate/kubernetes/re-databases/replica-redb)\n- [REDB API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api)"},{"id":"active-active-databases","title":"Active-Active databases","role":"content","text":"Set up globally distributed [Active-Active databases](https://redis.io/docs/latest/operate/kubernetes/active-active) across multiple Kubernetes clusters.\n\n- [Prepare participating clusters](https://redis.io/docs/latest/operate/kubernetes/active-active/prepare-clusters)\n- [Create Active-Active database](https://redis.io/docs/latest/operate/kubernetes/active-active/create-reaadb)\n- [Global configuration](https://redis.io/docs/latest/operate/kubernetes/active-active/global-config)\n- [REAADB API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_active_active_database_api)\n- [Remote cluster API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_remote_cluster_api)"},{"id":"security","title":"Security","role":"security","text":"Manage [secure connections](https://redis.io/docs/latest/operate/kubernetes/security) and access control for your Redis Enterprise deployment.\n\n- [Access control](https://redis.io/docs/latest/operate/kubernetes/security/access-control)\n- [Manage REC credentials](https://redis.io/docs/latest/operate/kubernetes/security/authentication/manage-rec-credentials)\n- [Manage REC certificates](https://redis.io/docs/latest/operate/kubernetes/security/certificates/manage-rec-certificates)\n- [Internode encryption](https://redis.io/docs/latest/operate/kubernetes/security/certificates/internode-encryption)\n- [LDAP authentication](https://redis.io/docs/latest/operate/kubernetes/security/authentication/ldap)"},{"id":"reference","title":"Reference","role":"related","text":"Use the Kubernetes API and command-line tools to manage your Redis Enterprise deployment.\n\n- [Redis Enterprise cluster API (REC)](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api)\n- [Redis Enterprise database API (REDB)](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api)\n- [Active-Active database API (REAADB)](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_active_active_database_api)\n- [Remote cluster API (RERC)](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_remote_cluster_api)"},{"id":"logs--monitoring","title":"Logs & monitoring","role":"content","text":"Monitor and troubleshoot your Redis Enterprise deployment.\n\n- [Collect logs](https://redis.io/docs/latest/operate/kubernetes/logs/collect-logs)\n- [Connect to Prometheus operator](https://redis.io/docs/latest/operate/kubernetes/re-clusters/connect-prometheus-operator)"},{"id":"upgrade","title":"Upgrade","role":"content","text":"Keep your Redis Enterprise deployment up to date.\n\n- [Upgrade Redis cluster](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster)\n- [Upgrade with OpenShift CLI](https://redis.io/docs/latest/operate/kubernetes/upgrade/openshift-cli)\n- [Upgrade with OLM](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-olm)"},{"id":"release-notes","title":"Release notes","role":"content","text":"Stay informed about new features, enhancements, and fixes.\n\n- [Release notes](https://redis.io/docs/latest/operate/kubernetes/release-notes)"},{"id":"related-info","title":"Related info","role":"related","text":"- [Redis Enterprise Software](https://redis.io/docs/latest/operate/rs)\n- [Redis Cloud](https://redis.io/docs/latest/operate/rc)\n- [Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack)\n- [Glossary](https://redis.io/docs/latest/glossary)"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/kubernetes-archive","title":"Archive","url":"https://redis.io/docs/latest/operate/kubernetes/kubernetes-archive/","summary":"Describes where to view the archive of Redis Enterprise for Kubernetes documentation.","tags":["docs","operate","kubernetes"],"last_updated":"2026-10-19T14:13:26-05:00","page_type":"content","content_hash":"983d19ea4c904d3c10c71f8bf8ca86e6adb460c4d5318004f31ac3b1138228b3","sections":[{"id":"content","title":"Content","role":"content","text":"The Redis Enterprise for Kubernetes documentation is versioned. You are looking at the documentation for the latest release.\n\n- View the [8.2.0 docs](https://redis.io/docs/latest/operate/kubernetes/8.2.0/) for Redis Software for Kubernetes versions 8.2.0-12 through 8.2.0-18.\n- View the [8.0.18 docs](https://redis.io/docs/latest/operate/kubernetes/8.0.18/) for Redis Enterprise for Kubernetes versions 8.0.18-11 and 8.0.20-21.\n- View the [8.0 docs](https://redis.io/docs/latest/operate/kubernetes/8.0/) for Redis Enterprise for Kubernetes versions 8.0.2-2 through 8.0.10-23.\n- View the [7.22 docs](https://redis.io/docs/latest/operate/kubernetes/7.22/) for Redis Enterprise for Kubernetes versions 7.22.0-7 through 7.22.2-40.\n- View the [7.8.6 docs](https://redis.io/docs/latest/operate/kubernetes/7.8.6/) for Redis Enterprise for Kubernetes versions 7.8.6-1 through 7.8.6-16.\n- View the [7.8.4 docs](https://redis.io/docs/latest/operate/kubernetes/7.8.4/) for Redis Enterprise for Kubernetes versions 7.8.2-6, 7.8.4-8, and 7.8.4-9.\n- View the [7.4.6 docs](https://redis.io/docs/latest/operate/kubernetes/7.4.6/) for Redis Enterprise for Kubernetes versions 7.4.6-2 and 7.4.6-6.\n\nOlder versions of Redis Enterprise for Kubernetes documentation are available on the archived web site:\n\n- [Redis Enterprise for Kubernetes v7.4 documentation archive](https://docs.redis.com/7.4/kubernetes/) \n\n- [Redis Enterprise for Kubernetes v7.2 documentation archive](https://docs.redis.com/7.2/kubernetes/)  \n\n- [Redis Enterprise for Kubernetes v6.x documentation archive](https://docs.redis.com/6.4/kubernetes/)"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/logs/collect-logs","title":"Collect logs","url":"https://redis.io/docs/latest/operate/kubernetes/logs/collect-logs/","summary":"Run the log collector script to package relevant logs into a tar.gz file to send to Redis Support for help troubleshooting your Kubernetes environment.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T16:14:52+01:00","page_type":"content","content_hash":"d466d95727982a015439bae26b4408f28c078ac47e41e5156ea80f199e05d3fc","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The Redis Enterprise cluster (REC) log collector script ([`log_collector.py`](https://github.com/RedisLabs/redis-enterprise-k8s-docs/blob/master/log_collector/log_collector.py)) creates and fills a directory with the relevant logs for your environment. These logs will help the support team with troubleshooting.\n\nThe log collector tool has two modes:\n\n- **restricted** collects only resources and logs created by the operator and Redis Enterprise deployments\n- **all** collects everything from your environment\n\n> [!NOTE]\n>  This script requires Python 3.11 or later."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before running the log collector, ensure you have the appropriate RBAC permissions configured. See [Log collector RBAC examples](https://redis.io/docs/latest/operate/kubernetes/reference/yaml/log-collector-rbac) for detailed RBAC configuration instructions."},{"id":"collect-logs","title":"Collect logs","role":"content","text":"1. Download the latest [`log_collector.py`](https://github.com/RedisLabs/redis-enterprise-k8s-docs/blob/master/log_collector/log_collector.py) file.\n\n1. Ensure your `kubectl` or `oc` CLI is configured to access the Kubernetes cluster you want to collect logs from.\n\n1. Have a K8s administrator run the script on the system that runs your `kubectl` or `oc` commands.\n\n    [code example]"},{"id":"options","title":"Options","role":"parameters","text":"You can run `log_collector.py` with the following options:\n\n| Option | Description |\n|--------|-------------|\n| `-n`, `--namespace` | Sets the namespace(s) to collect from. Can be set to a single namespace, or multiple namespaces (comma-separated). When left empty, will use the current context's namespace from kubeconfig. |\n| `-o`, `--output_dir` | Sets the output directory. Defaults to current working directory. |\n| `-a`, `--logs_from_all_pods` | Collect logs from all pods in the selected namespace(s), and otherwise collect only from the operator and pods run by the operator. |\n| `-t`, `--timeout` | Time to wait for external commands to finish execution (Linux only). Defaults to 180s. Specify 0 to disable timeout. |\n| `--k8s_cli` | The K8s cli client to use (kubectl/oc/auto-detect). Defaults to auto-detect (chooses between 'kubectl' and 'oc'). Full paths can also be used. |\n| `-m`, `--mode` | Controls which resources are collected. In 'restricted' mode, only resources associated with the operator and have the label 'app=redis-enterprise' are collected. In 'all' mode, all resources are collected. Defaults to 'restricted' mode. |\n| `--collect_istio` | Collect data from istio-system namespace to debug potential problems related to istio ingress method. |\n| `--collect_empty_files` | Collect empty log files for missing resources. |\n| `--helm_release_name` | Collect resources related to the given Helm release name. |\n| `--collect_rbac_resources` | Temporary development flag. Collect all role based access control related custom resources. |\n| `-h`, `--help` | Show help message and exit. |\n\n> [!NOTE]\n>  If you get an error because the yaml module is not found, install the pyYAML module with `pip install pyyaml`.\n\n1. Upload the resulting `tar.gz` file containing all the logs to [Redis Support](https://support.redislabs.com/)."}],"examples":[{"id":"collect-logs-ex0","language":"bash","code":"python log_collector.py","section_id":"collect-logs"}]}
{"schema_version":2,"id":"operate/kubernetes/logs","title":"Logs","url":"https://redis.io/docs/latest/operate/kubernetes/logs/","summary":"Access and manage Redis Enterprise logs on Kubernetes for monitoring and troubleshooting.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T16:14:52+01:00","children":[{"id":"operate/kubernetes/logs/collect-logs","summary":"Run the log collector script to package relevant logs into a tar.gz file to send to Redis Support for help troubleshooting your Kubernetes environment.","title":"Collect logs","url":"https://redis.io/docs/latest/operate/kubernetes/logs/collect-logs/"}],"page_type":"content","content_hash":"92e142eddbbbd6078e35bda06f59aebeeeed52e198332894ef8b0a1394f59898","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Access and manage Redis Enterprise logs on Kubernetes for monitoring, troubleshooting, and debugging your Redis Enterprise deployment. Logs provide valuable insights into cluster operations, database performance, and system health."},{"id":"log-collection-and-access","title":"Log collection and access","role":"content","text":"Learn how to collect and access logs from your Redis Enterprise deployment:\n\n- [Collect logs](https://redis.io/docs/latest/operate/kubernetes/logs/collect-logs) - Methods for collecting logs from Redis Enterprise pods and containers"},{"id":"log-storage-and-access","title":"Log storage and access","role":"content","text":"Each Redis Enterprise container stores its logs under `/var/opt/redislabs/log`. When using persistent storage, this path is automatically mounted to the `redis-enterprise-storage` volume, making logs accessible through sidecar containers or external log collection tools.\n\nFor example, in the REC (Redis Enterprise Cluster) spec you can add a sidecar container, such as a busybox, and mount the logs to there:\n\n[code example]\n\nNow the logs can be accessed from in the sidecar. For example by running\n\n```kubectl exec -it <pod-name> -c busybox -- tail home/logs/supervisord.log```\n\nThe sidecar container is user determined and can be used to format, process and share logs in a specified format and protocol."}],"examples":[{"id":"log-storage-and-access-ex0","language":"yaml","code":"sideContainersSpec:\n  - name: busybox\n    image: busybox\n    args:\n      - /bin/sh\n      - -c\n      - while true; do echo \"hello\"; sleep 1; done\n\n    volumeMounts:\n    - name: redis-enterprise-storage\n      mountPath: /home/logs\n      subPath: logs","section_id":"log-storage-and-access"}]}
{"schema_version":2,"id":"operate/kubernetes/networking/cluster-aware-clients","title":"Enable cluster-aware clients (OSS Cluster API)","url":"https://redis.io/docs/latest/operate/kubernetes/networking/cluster-aware-clients/","summary":"Improve performance with cluster-aware clients by enabling the OSS Cluster API for your Redis Enterprise for Kubernetes database.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T16:14:52+01:00","page_type":"content","content_hash":"da893d7d37bca36a5283798331816f01de704fde8004ef714988937db69a6fec","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The OSS Cluster API improves performance by allowing cluster-aware Redis clients to discover database topology and route requests directly. This feature supports cluster-aware clients running on the same Kubernetes cluster (internal) and cluster-aware clients outside the Kubernetes cluster (external).\n\n> [!NOTE]\n> Enabling external access for OSS Cluster API creates a separate LoadBalancer service for each Redis Enterprise pod in addition to the LoadBalancer service for the cluster endpoint. LoadBalancers are resources that can significantly increase operational costs; plan your infrastructure budget accordingly."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"- RedisEnterpriseCluster (REC) running version 8.0.10-21 or later.\n- Proxy policy is set to `all-master-shards` or `all-nodes`.\n- Modules used by the database (if any) are bundled modules.\n- The database is not an Active-Active database."},{"id":"limitations","title":"Limitations","role":"limits","text":"- OSS Cluster databases with external access must be created and managed using the RedisEnterpriseDatabase (REDB) custom resource. You cannot create or configure these databases using the Redis Enterprise REST API directly."},{"id":"enable-oss-cluster-api","title":"Enable OSS Cluster API","role":"content","text":"To enable cluster-aware clients, edit the REC and REDB custom resources with the following fields."},{"id":"edit-rec","title":"Edit REC (RedisEnterpriseCluster)","role":"content","text":"1. Edit the RedisEnterpriseCluster (REC) custom resource to add the `ossClusterSettings` section to the `spec` section.\n\nSet `externalAccessType` to `LoadBalancer` and add any `serviceAnnotations`, if required by your service provider.\n\n[code example]\n\n2. If you are using internal clients (within the Kubernetes cluster), you can specify their IP ranges in the `podCIDRs` field to improve performance. Clients from these ranges are routed directly to the Redis Enterprise pods, without going through the load balancer.\n\nThe following is an example `podCIDRs` field using example values; replace with your own unique CIDRs.\n\n[code example]"},{"id":"edit-redb","title":"Edit REDB (RedisEnterpriseDatabase)","role":"content","text":"3. Edit the REDB custom resource to add the following fields and values to the `spec` section.\n\nSet `enableExternalAccess: true` to allow external clients to connect. This provisions a LoadBalancer service for each Redis Enterprise pod running a node, which increases infrastructure costs.\n\n[code example]"},{"id":"connect-clients","title":"Connect clients","role":"content","text":"Use the `kubectl get svc` command to view the services created by the OSS Cluster API. It should look similar to this example:\n\n[code example]\n\nTo connect an external client (outside the Kubernetes cluster), use the `EXTERNAL-IP` address for the `oss-cluster-load-balancer` service.\n\nTo connect an internal client (listed in the `podCIDRs` field), use the `CLUSTER-IP` address for the `oss-cluster` service."}],"examples":[{"id":"edit-rec-ex0","language":"yaml","code":"ossClusterSettings:\n    externalAccessType: LoadBalancer\n    loadBalancer:\n      serviceAnnotations:","section_id":"edit-rec"},{"id":"edit-rec-ex1","language":"yaml","code":"podCIDRs:\n  - \"192.0.2.0/24\"\n  - \"198.51.100.0/24\"\n  - \"203.0.113.0/24\"","section_id":"edit-rec"},{"id":"edit-redb-ex0","language":"yaml","code":"ossCluster: true\n\n  ossClusterSettings:\n    enableExternalAccess: true","section_id":"edit-redb"},{"id":"connect-clients-ex0","language":"sh","code":"NAME                           TYPE           CLUSTER-IP       EXTERNAL-IP      PORT(S)              AGE\nadmission                      ClusterIP      10.0.1.10        <none>           443/TCP              3h28m\noss-cluster                    ClusterIP      10.0.1.20        <none>           11712/TCP            3h6m\noss-cluster-headless           ClusterIP      None             <none>           11712/TCP            3h6m\noss-cluster-load-balancer      LoadBalancer   10.0.1.30        203.0.113.10     11712:30245/TCP      3h6m\nrec                            ClusterIP      10.0.1.40        <none>           9443/TCP,8001/TCP    3h27m\nrec-cluster                    LoadBalancer   10.0.1.50        203.0.113.20     11712:32559/TCP      3h6m\nrec-1-lb                       LoadBalancer   10.0.1.60        203.0.113.30     11712:31976/TCP      3h6m\nrec-2-lb                       LoadBalancer   10.0.1.70        203.0.113.40     11712:31972/TCP      3h6m\nrec-oss                        ClusterIP      None             <none>           8070/TCP             3h27m\nrec-ui                         ClusterIP      10.0.1.80        <none>           8443/TCP             3h27m","section_id":"connect-clients"}]}
{"schema_version":2,"id":"operate/kubernetes/networking/database-connectivity","title":"Database connectivity","url":"https://redis.io/docs/latest/operate/kubernetes/networking/database-connectivity/","summary":"Connect applications to Redis Enterprise databases in Kubernetes clusters with in-cluster and external access patterns.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T16:14:52+01:00","page_type":"content","content_hash":"8e769e0a888516856182e75dec1039c860c34ad926503764f9110d61705d2663","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Connecting applications to Redis Enterprise databases in Kubernetes involves understanding service discovery, credentials management, and access patterns. This guide covers the essential connectivity aspects unique to Kubernetes deployments."},{"id":"service-types-and-access-patterns","title":"Service types and access patterns","role":"content","text":"When you create a RedisEnterpriseDatabase (REDB), the Redis Enterprise operator automatically creates Kubernetes services to route traffic to your database. Understanding these service types is crucial for proper connectivity."},{"id":"default-service-creation","title":"Default service creation","role":"content","text":"By default, the operator creates two services for each database:\n\n- ClusterIP service - Provides a stable cluster-internal IP address.\n- Headless service - Enables direct pod-to-pod communication and service discovery.\n\nBoth services are created in the same namespace as your database and follow predictable naming conventions."},{"id":"service-types","title":"Service types","role":"content","text":"Redis Enterprise supports three service types for database access:\n\n| Service Type | Rigger Spec Exact Name | Access Scope | Use Case |\n|--------------|--------------|--------------|----------|\n| `ClusterIP` | `cluster_ip` | Cluster-internal only | Applications running within the same Kubernetes cluster |\n| `headless` | `headless` | Cluster-internal only | Direct pod access, service discovery, StatefulSet scenarios |\n| `LoadBalancer` | `load_balancer` | External access | Applications outside the Kubernetes cluster |\n\nTo configure the service type, use the `databaseServiceType` field in your REC's `servicesRiggerSpec`.\n\nFor example, to enable the three Service options, you may use:\n[code example]"},{"id":"in-cluster-database-access","title":"In-cluster database access","role":"content","text":"For applications running within your Kubernetes cluster, use the automatically created services to connect to your databases."},{"id":"retrieve-connection-information","title":"Retrieve connection information","role":"content","text":"Database connection details are stored in a Kubernetes secret maintained by the database controller. This secret contains:\n\n- Database port (`port`)\n- Service names (`service_names`) \n- Database password (`password`)\n\n1. Get the secret name from your database resource:\n\n   [code example]\n\n   The secret name typically follows the pattern `redb-<database-name>`.\n\n2. Retrieve the database port:\n\n   [code example]\n\n3. Get available service names:\n\n   [code example]\n\n4. Retrieve the database password:\n\n   [code example]"},{"id":"service-naming-and-dns-resolution","title":"Service naming and DNS resolution","role":"content","text":"Services follow standard Kubernetes DNS naming conventions:\n\n- Service FQDN: `<service-name>.<namespace>.svc.cluster.local`\n- Short name: `<service-name>` (within the same namespace)\n\nFor a database named `mydb` in namespace `production`, the service names would be:\n- ClusterIP service: `mydb.production.svc.cluster.local`\n- Headless service: `mydb-headless.production.svc.cluster.local`"},{"id":"connect-from-within-the-cluster","title":"Connect from within the cluster","role":"content","text":"Use any service name from the `service_names` list to connect:\n\n[code example]\n\nThen authenticate with the retrieved password:\n\n[code example]"},{"id":"external-database-access","title":"External database access","role":"content","text":"To access databases from outside the Kubernetes cluster, you need to configure external routing. Currently supported methods for external access are ingress controllers or OpenShift routes."},{"id":"ingress-controllers","title":"Ingress controllers","role":"content","text":"Redis Enterprise for Kubernetes only supports the following ingress controllers for external database access:\n\n- HAProxy Ingress - Built-in SSL passthrough support.\n- Istio Gateway - Service mesh integration with advanced traffic management.\n- Ingress-NGINX - SSL passthrough is off by default; start the controller with `--enable-ssl-passthrough`. The community `kubernetes/ingress-nginx` project is retired (maintenance ended March 2026); existing deployments only.\n\nSee [Ingress routing](https://redis.io/docs/latest/operate/kubernetes/networking/ingress) for detailed configuration steps."},{"id":"openshift-routes","title":"OpenShift routes","role":"content","text":"OpenShift users can leverage routes for external access:\n\n[code example]\n\nSee [OpenShift routes](https://redis.io/docs/latest/operate/kubernetes/networking/routes) for complete setup instructions."},{"id":"service-ports-and-configuration","title":"Service ports and configuration","role":"content","text":""},{"id":"default-port-behavior","title":"Default port behavior","role":"content","text":"- Redis Enterprise databases use dynamic port allocation.\n- Port numbers are assigned automatically during database creation.\n- The actual port is stored in the database secret."},{"id":"custom-port-configuration","title":"Custom port configuration","role":"content","text":"You can specify custom ports using the `databasePort` field in your REDB specification:\n\n[code example]\n\nCustom ports replace the default service port and are reflected in the database secret."},{"id":"credentials-and-secrets-management","title":"Credentials and secrets management","role":"content","text":""},{"id":"database-secrets-structure","title":"Database secrets structure","role":"content","text":"Each database has an associated Kubernetes secret containing connection details:\n\n[code example]"},{"id":"using-secrets-in-applications","title":"Using secrets in applications","role":"content","text":"Reference database secrets in your application deployments:\n\n[code example]"},{"id":"rotate-the-database-password","title":"Rotate the database password","role":"content","text":"To rotate a database password, update the `password` key in the database secret. See [Change the REDB password](https://redis.io/docs/latest/operate/kubernetes/security/authentication/manage-redb-credentials#change-the-redb-password)."},{"id":"default-user-configuration","title":"Default user configuration","role":"content","text":"By default, databases create a default user with full access. You can disable this behavior:\n\n[code example]\n\nWhen `defaultUser` is disabled, the database secret is not created, and you must configure custom authentication."},{"id":"connection-examples","title":"Connection examples","role":"content","text":""},{"id":"python-application","title":"Python application","role":"content","text":"[code example]"},{"id":"nodejs-application","title":"Node.js application","role":"content","text":"[code example]"},{"id":"troubleshooting-connectivity","title":"Troubleshooting connectivity","role":"errors","text":""},{"id":"common-issues","title":"Common issues","role":"content","text":"1. Connection refused - Verify service names and ports from the database secret.\n2. Authentication failed - Check password encoding and special characters.\n3. DNS resolution - Ensure applications use correct service FQDNs.\n4. Network policies - Verify Kubernetes network policies allow traffic."},{"id":"debugging-steps","title":"Debugging steps","role":"content","text":"1. Verify service creation:\n   [code example]\n\n2. Check service endpoints:\n   [code example]\n\n3. Test connectivity from within cluster:\n   [code example]"},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Ingress routing](https://redis.io/docs/latest/operate/kubernetes/networking/ingress) - Configure external access with ingress controllers\n- [OpenShift routes](https://redis.io/docs/latest/operate/kubernetes/networking/routes) - External access using OpenShift routes  \n- [Database controller](https://redis.io/docs/latest/operate/kubernetes/re-databases/db-controller) - Database lifecycle management\n- [Security](https://redis.io/docs/latest/operate/kubernetes/security) - TLS configuration and access control"}],"examples":[{"id":"service-types-ex0","language":"plaintext","code":"{\"spec\":{\"servicesRiggerSpec\":{\"databaseServiceType\":\"cluster_ip,headless,load_balancer\"}}}","section_id":"service-types"},{"id":"retrieve-connection-information-ex0","language":"sh","code":"kubectl get redb <database-name> -o jsonpath=\"{.spec.databaseSecretName}\"","section_id":"retrieve-connection-information"},{"id":"retrieve-connection-information-ex1","language":"sh","code":"kubectl get secret redb-<database-name> -o jsonpath=\"{.data.port}\" | base64 --decode","section_id":"retrieve-connection-information"},{"id":"retrieve-connection-information-ex2","language":"sh","code":"kubectl get secret redb-<database-name> -o jsonpath=\"{.data.service_names}\" | base64 --decode","section_id":"retrieve-connection-information"},{"id":"retrieve-connection-information-ex3","language":"sh","code":"kubectl get secret redb-<database-name> -o jsonpath=\"{.data.password}\" | base64 --decode","section_id":"retrieve-connection-information"},{"id":"connect-from-within-the-cluster-ex0","language":"sh","code":"redis-cli -h <service-name> -p <port>","section_id":"connect-from-within-the-cluster"},{"id":"connect-from-within-the-cluster-ex1","language":"sh","code":"auth <password>","section_id":"connect-from-within-the-cluster"},{"id":"openshift-routes-ex0","language":"yaml","code":"apiVersion: route.openshift.io/v1\nkind: Route\nmetadata:\n  name: redis-route\nspec:\n  to:\n    kind: Service\n    name: <database-service-name>\n  port:\n    targetPort: redis\n  tls:\n    termination: passthrough","section_id":"openshift-routes"},{"id":"custom-port-configuration-ex0","language":"yaml","code":"apiVersion: app.redislabs.com/v1alpha1\nkind: RedisEnterpriseDatabase\nmetadata:\n  name: mydb\nspec:\n  memorySize: 256MB\n  databasePort: 6379","section_id":"custom-port-configuration"},{"id":"database-secrets-structure-ex0","language":"yaml","code":"apiVersion: v1\nkind: Secret\nmetadata:\n  name: redb-<database-name>\ntype: Opaque\ndata:\n  port: <base64-encoded-port>\n  service_names: <base64-encoded-service-list>\n  password: <base64-encoded-password>","section_id":"database-secrets-structure"},{"id":"using-secrets-in-applications-ex0","language":"yaml","code":"apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: my-app\nspec:\n  template:\n    spec:\n      containers:\n      - name: app\n        image: my-app:latest\n        env:\n        - name: REDIS_HOST\n          value: \"<service-name>\"\n        - name: REDIS_PORT\n          valueFrom:\n            secretKeyRef:\n              name: redb-<database-name>\n              key: port\n        - name: REDIS_PASSWORD\n          valueFrom:\n            secretKeyRef:\n              name: redb-<database-name>\n              key: password","section_id":"using-secrets-in-applications"},{"id":"default-user-configuration-ex0","language":"yaml","code":"apiVersion: app.redislabs.com/v1alpha1\nkind: RedisEnterpriseDatabase\nmetadata:\n  name: mydb\nspec:\n  memorySize: 256MB\n  defaultUser: false","section_id":"default-user-configuration"},{"id":"python-application-ex0","language":"python","code":"import redis\nimport base64\nimport os\n\n# Read from Kubernetes secret (mounted as environment variables)\nhost = os.getenv('REDIS_HOST')\nport = int(os.getenv('REDIS_PORT'))\npassword = os.getenv('REDIS_PASSWORD')\n\n# Create Redis connection\nr = redis.Redis(host=host, port=port, password=password, decode_responses=True)\n\n# Test connection\nr.ping()","section_id":"python-application"},{"id":"nodejs-application-ex0","language":"javascript","code":"const redis = require('redis');\n\nconst client = redis.createClient({\n  host: process.env.REDIS_HOST,\n  port: process.env.REDIS_PORT,\n  password: process.env.REDIS_PASSWORD\n});\n\nclient.on('connect', () => {\n  console.log('Connected to Redis');\n});","section_id":"nodejs-application"},{"id":"debugging-steps-ex0","language":"sh","code":"kubectl get services -l app=redis-enterprise","section_id":"debugging-steps"},{"id":"debugging-steps-ex1","language":"sh","code":"kubectl get endpoints <service-name>","section_id":"debugging-steps"},{"id":"debugging-steps-ex2","language":"sh","code":"kubectl run redis-test --image=redis:latest -it --rm -- redis-cli -h <service-name> -p <port>","section_id":"debugging-steps"}]}
{"schema_version":2,"id":"operate/kubernetes/networking","title":"Networking","url":"https://redis.io/docs/latest/operate/kubernetes/networking/","summary":"Configure networking and external access for Redis Enterprise clusters and databases on Kubernetes.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T16:14:52+01:00","children":[{"id":"operate/kubernetes/networking/database-connectivity","summary":"Connect applications to Redis Enterprise databases in Kubernetes clusters with in-cluster and external access patterns.","title":"Database connectivity","url":"https://redis.io/docs/latest/operate/kubernetes/networking/database-connectivity/"},{"id":"operate/kubernetes/networking/ingress","summary":"Configure an ingress controller to access your Redis Enterprise databases from outside the Kubernetes cluster.","title":"Configure Ingress for external routing","url":"https://redis.io/docs/latest/operate/kubernetes/networking/ingress/"},{"id":"operate/kubernetes/networking/routes","summary":"","title":"Use OpenShift routes for external database access","url":"https://redis.io/docs/latest/operate/kubernetes/networking/routes/"},{"id":"operate/kubernetes/networking/istio-ingress","summary":"Configure Istio as an ingress controller for access to your Redis Enterprise databases from outside the Kubernetes cluster.","title":"Configure Istio for external routing","url":"https://redis.io/docs/latest/operate/kubernetes/networking/istio-ingress/"},{"id":"operate/kubernetes/networking/ingressorroutespec","summary":"","title":"Establish external routing on the RedisEnterpriseCluster","url":"https://redis.io/docs/latest/operate/kubernetes/networking/ingressorroutespec/"},{"id":"operate/kubernetes/networking/cluster-aware-clients","summary":"Improve performance with cluster-aware clients by enabling the OSS Cluster API for your Redis Enterprise for Kubernetes database.","title":"Enable cluster-aware clients (OSS Cluster API)","url":"https://redis.io/docs/latest/operate/kubernetes/networking/cluster-aware-clients/"}],"page_type":"content","content_hash":"99d70f821ac15561b561e836b7f799f78f4ec7e1d10ec2317b7123e21b9dd468","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Configure networking and external access for your Redis Enterprise deployment on Kubernetes. By default, Kubernetes doesn't allow external access to your Redis databases. Redis Enterprise for Kubernetes provides several methods to route external traffic to your clusters and databases."},{"id":"database-connectivity","title":"Database connectivity","role":"content","text":"Connect applications to your Redis Enterprise databases:\n\n- [Database connectivity](https://redis.io/docs/latest/operate/kubernetes/networking/database-connectivity) - Comprehensive guide to in-cluster and external database access, service discovery, and credentials management."},{"id":"external-routing-methods","title":"External routing methods","role":"content","text":"Choose the appropriate method for your environment to enable external access:\n\n- [Ingress routing](https://redis.io/docs/latest/operate/kubernetes/networking/ingress) - Use HAProxy ingress controller with `ingress` API resources (the community NGINX ingress controller is retired)\n- [Istio ingress routing](https://redis.io/docs/latest/operate/kubernetes/networking/istio-ingress) - Use Istio service mesh with `Gateway` and `VirtualService` API resources\n- [OpenShift routes](https://redis.io/docs/latest/operate/kubernetes/networking/routes) - Use OpenShift-specific route resources for external traffic"},{"id":"automatic-ingress-configuration","title":"Automatic ingress configuration","role":"content","text":"For Active-Active databases, configure automatic ingress creation:\n\n- [REC external routing](https://redis.io/docs/latest/operate/kubernetes/networking/ingressorroutespec) - Use `ingressOrRouteSpec` field in RedisEnterpriseCluster (REC) for automatic ingress creation"},{"id":"ingressorroutespec-for-active-active-databases","title":"`ingressOrRouteSpec` for Active-Active databases","role":"content","text":"Versions 6.4.2 or later of Redis Enterprise for Kubernetes include a feature for ingress configuration. The `ingressOrRouteSpec` field is available in the RedisEnterpriseCluster spec to automatically create an Ingress (or route) for the API service and databases (REAADB) on that REC. See [REC external routing](https://redis.io/docs/latest/operate/kubernetes/networking/ingressorroutespec) for more details.\n\nThis feature only supports automatic Ingress creation for Active-Active databases created and managed with the RedisEnterpriseActiveActiveDatabase (REAADB) custom resource. Use with the standard Redis Enterprise database (REDB) is not currently supported."},{"id":"oss-cluster-api-support","title":"OSS Cluster API support","role":"content","text":"[OSS Cluster API](https://redis.io/docs/latest/operate/kubernetes/networking/cluster-aware-clients) is supported for both internal and external clients:\n\n- **Internal clients** running on pods within the same Kubernetes cluster can connect directly to Redis Enterprise pods using pod IPs.\n- **External clients** outside the Kubernetes cluster can connect through LoadBalancer services when `enableExternalAccess: true` is configured in the database specification. This creates a separate LoadBalancer service for each Redis Enterprise pod. Note that LoadBalancers are resources that can significantly increase operational costs.\n\nSee [Enable cluster-aware clients (OSS Cluster API)](https://redis.io/docs/latest/operate/kubernetes/networking/cluster-aware-clients) for configuration details."},{"id":"rec-domain-name","title":"REC domain name","role":"content","text":"The RedisEnterpriseCluster does not support custom domain names. Domain names for the REC are in the following format: `<rec-name>.<namespace>.svc.cluster.local`."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/networking/ingress","title":"Configure Ingress for external routing","url":"https://redis.io/docs/latest/operate/kubernetes/networking/ingress/","summary":"Configure an ingress controller to access your Redis Enterprise databases from outside the Kubernetes cluster.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T16:14:52+01:00","page_type":"content","content_hash":"0dd3d5a77c75c561964b98f7950b4330f5d5124008115337c971cff8c1cdbcb2","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!WARNING]\n> The community [Ingress-NGINX controller](https://github.com/kubernetes/ingress-nginx) (`kubernetes/ingress-nginx`) is retired. Best-effort maintenance ended in March 2026 and the project no longer ships releases, bug fixes, or security updates. If you are not already using it, use HAProxy or Istio as shown below."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before creating an Ingress, you'll need:\n\n - A RedisEnterpriseDatabase (REDB) with TLS enabled for client connections\n - An Ingress controller with `ssl-passthrough` enabled. Options include:\n    - [HAProxy Ingress](https://haproxy-ingress.github.io/docs/getting-started/)\n    - [Ingress-NGINX Controller](https://kubernetes.github.io/ingress-nginx/deploy/) (retired; existing deployments only)\n    - To use Istio for your Ingress resources, see [Configure Istio for external routing](https://redis.io/docs/latest/operate/kubernetes/networking/istio-ingress)\n\n> [!NOTE]\n> Make sure your Ingress controller has `ssl-passthrough` enabled. It is on by default for HAProxy and off by default for Ingress-NGINX. For Ingress-NGINX, start the controller with the `--enable-ssl-passthrough` flag. See the [Ingress-NGINX TLS guide](https://kubernetes.github.io/ingress-nginx/user-guide/tls/#ssl-passthrough) for details."},{"id":"create-an-ingress-resource","title":"Create an Ingress resource","role":"content","text":"1. Retrieve the hostname of your Ingress controller's `LoadBalancer` service.\n\n    [code example]\n\n    Below is example output for an HAProxy running on a K8s cluster hosted by AWS.  \n\n    [code example]\n\n1. Choose the hostname you will use to access your database (this value will be represented in this article with `<my-db-hostname>`).  \n\n1. Create a DNS entry that resolves your chosen database hostname to the IP address for the Ingress controller's LoadBalancer.  \n\n1. Create the Ingress resource YAML file.\n\n    [code example]\n\n    Set `ingressClassName` to match the `IngressClass` your controller installed. The deprecated `kubernetes.io/ingress.class` annotation is no longer accepted by recent controller versions, so use `ingressClassName` instead.\n\n    For HAProxy, add the following annotation:\n\n    [code example]\n\n    For Ingress-NGINX, add the following annotation:\n\n    [code example]\n\n    The `ssl-passthrough` annotation is required to allow access to the database. The exact format depends on your Ingress controller and any additional customizations. See [Ingress-NGINX annotations](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/) and [HAProxy Ingress configuration keys](https://haproxy-ingress.github.io/docs/configuration/keys/) for current annotation formats."},{"id":"test-your-external-access","title":"Test your external access","role":"content","text":"To test your external access to the database, you need a client that supports [TLS](https://en.wikipedia.org/wiki/Transport_Layer_Security) and [SNI](https://en.wikipedia.org/wiki/Server_Name_Indication).  \n\n#### Test your access with Openssl  \n\n1. Get the default CA certificate from the `redis-enterprise-node` container on any of the Redis Enterprise pods.  \n\n    [code example]  \n\n1. Run the following `openssl` command, substituting your own values for `<my-db-hostname>`.  \n\n    [code example]  \n\n    If you are connected to the database, you will receive `PONG` back, as shown below:  \n\n    [code example]  \n\n#### Test your access with Python  \n\nYou can use the code below to test your access with Python, substituting your own values for `<my-db-hostname>` and `<file-path>`.  \n\n[code example]\n\nYour output should look something like this:  \n\n[code example]"}],"examples":[{"id":"create-an-ingress-resource-ex0","language":" sh","code":"$ kubectl get svc <haproxy-ingress | ingress-ngnix-controller> \\\n                        -n <ingress-ctrl-namespace>","section_id":"create-an-ingress-resource"},{"id":"create-an-ingress-resource-ex1","language":" sh","code":"NAME              TYPE           CLUSTER-IP    EXTERNAL-IP                                                              PORT(S)                      AGE   \n    haproxy-ingress   LoadBalancer   10.43.62.53   a56e24df8c6173b79a63d5da54fd9cff-676486416.us-east-1.elb.amazonaws.com   80:30610/TCP,443:31597/TCP   21m","section_id":"create-an-ingress-resource"},{"id":"create-an-ingress-resource-ex2","language":" YAML","code":"apiVersion: networking.k8s.io/v1\n    kind: Ingress\n    metadata:\n      name: rec-ingress\n      annotations:\n        <controller-specific-annotations-below>\n    spec:\n      ingressClassName: <haproxy | nginx>\n      rules:\n      - host: <my-db-hostname>\n        http:\n          paths:\n          - path: /\n            pathType: ImplementationSpecific\n            backend:\n              service:\n                name: <db-name>\n                port:\n                  name: redis","section_id":"create-an-ingress-resource"},{"id":"create-an-ingress-resource-ex3","language":" YAML","code":"ingress.kubernetes.io/ssl-passthrough: \"true\"","section_id":"create-an-ingress-resource"},{"id":"create-an-ingress-resource-ex4","language":" YAML","code":"nginx.ingress.kubernetes.io/ssl-passthrough: \"true\"","section_id":"create-an-ingress-resource"},{"id":"test-your-external-access-ex0","language":" sh","code":"$ kubectl exec -it <pod-name> -c redis-enterprise-node \\\n                    -- cat /etc/opt/redislabs/proxy_cert.pem","section_id":"test-your-external-access"},{"id":"test-your-external-access-ex1","language":" sh","code":"$ openssl s_client \\\n      -connect <my-db-hostname>:443 \\\n      -crlf -CAfile ./proxy_cert.pem \\\n      -servername <my-db-hostname>","section_id":"test-your-external-access"},{"id":"test-your-external-access-ex2","language":" sh","code":"...\n     Verify return code: 0 (ok)\n    ---\n\n    PING\n    +PONG","section_id":"test-your-external-access"},{"id":"test-your-external-access-ex3","language":" python","code":"import redis\n\nr = redis.Redis(host='<my-db-hostname>',\n              port=443, db=0, ssl=True,\n              ssl_ca_certs='/<file-path>/proxy_cert.pem')\n\n\nprint(r.info())","section_id":"test-your-external-access"},{"id":"test-your-external-access-ex4","language":" sh","code":"$ /Users/example-user/Documents/Projects/test_client/venv/bin/python \\\n    /Users/example-user/Documents/Projects/test_client/test_ssl.py\n{\n    'redis_version': '5.0.5',\n    'redis_git_sha1': 0,\n    'redis_git_dirty': 0,\n    'redis_build_id': 0,\n    'redis_mode': 'standalone',\n    'os': 'Linux 4.14.154-128.181.amzn2.x86_64 x86_64',\n    'arch_bits': 64,\n    'multiplexing_api': 'epoll',\n    'gcc_version': '7.4.0',\n    'process_id': 1,\n    'run_id': '3ce7721b096517057d28791aab555ed8ac02e1de',\n    'tcp_port': 10811,\n    'uptime_in_seconds': 316467,\n    'uptime_in_days': 3,\n    'hz': 10,\n    'lru_clock': 0,\n    'config_file': '',\n    'connected_clients': 1,\n    'client_longest_output_list': 0,\n    'client_biggest_input_buf': 0,\n    'blocked_clients': 0,\n    'used_memory': 12680016,\n    'used_memory_human': '12.9M',\n    'used_memory_rss': 12680016,\n    'used_memory_peak': 13452496,\n    'used_memory_peak_human': '12.82M',\n    'used_memory_lua': 151552,\n    'mem_fragmentation_ratio': 1,\n    'mem_allocator': 'jemalloc-5.1.0',\n    'loading': 0,\n    'rdb_changes_since_last_save': 0,\n    'rdb_bgsave_in_progress': 0,\n    'rdb_last_save_time': 1577753916,\n    'rdb_last_bgsave_status': 'ok',\n    'rdb_last_bgsave_time_sec': 0,\n    'rdb_current_bgsave_time_sec': -1,\n    'aof_enabled': 0,\n    'aof_rewrite_in_progress': 0,\n    'aof_rewrite_scheduled': 0,\n    'aof_last_rewrite_time_sec': -1,\n    'aof_current_rewrite_time_sec': -1,\n    'aof_last_bgrewrite_status': 'ok',\n    'aof_last_write_status': 'ok',\n    'total_connections_received': 4,\n    'total_commands_processed': 6,\n    'instantaneous_ops_per_sec': 14,\n    'total_net_input_bytes': 0,\n    'total_net_output_bytes': 0,\n    'instantaneous_input_kbps': 0.0,\n    'instantaneous_output_kbps': 0.0,\n    'rejected_connections': 0,\n    'sync_full': 1,\n    'sync_partial_ok': 0,\n    'sync_partial_err': 0,\n    'expired_keys': 0,\n    'evicted_keys': 0,\n    'keyspace_hits': 0,\n    'keyspace_misses': 0,\n    'pubsub_channels': 0,\n    'pubsub_patterns': 0,\n    'latest_fork_usec': 0,\n    'migrate_cached_sockets': 0,\n    'role': 'master',\n    'connected_slaves': 1,\n    'slave0': {\n        'ip': '0.0.0.0',\n        'port': 0,\n        'state': 'online',\n        'offset': 0,\n        'lag': 0\n    },\n    'master_repl_offset': 0,\n    'repl_backlog_active': 0,\n    'repl_backlog_size': 1048576,\n    'repl_backlog_first_byte_offset': 0,\n    'repl_backlog_histlen': 0,\n    'used_cpu_sys': 0.0,\n    'used_cpu_user': 0.0,\n    'used_cpu_sys_children': 0.0,\n    'used_cpu_user_children': 0.0,\n    'cluster_enabled': 0\n}\n\nProcess finished with exit code 0","section_id":"test-your-external-access"}]}
{"schema_version":2,"id":"operate/kubernetes/networking/ingressorroutespec","title":"Establish external routing on the RedisEnterpriseCluster","url":"https://redis.io/docs/latest/operate/kubernetes/networking/ingressorroutespec/","summary":"","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T16:14:52+01:00","page_type":"content","content_hash":"94af732b2e5f422fc964d3f7914b9584c675891a3c457297d5879af3c6248540","sections":[{"id":"overview","title":"Overview","role":"overview","text":"An Ingress is an API resource that provides a standardized and flexible way to manage external access to services running within a Kubernetes cluster.\n\n> [!WARNING]\n> The community [Ingress-NGINX controller](https://github.com/kubernetes/ingress-nginx) (`kubernetes/ingress-nginx`) is retired. Best-effort maintenance ended in March 2026 and the project no longer ships releases, bug fixes, or security updates. If you are not already using it, use HAProxy or Istio."},{"id":"install-ingress-controller","title":"Install Ingress controller","role":"setup","text":"Redis Enterprise for Kubernetes supports the Ingress controllers below:\n* [HAProxy](https://haproxy-ingress.github.io/)\n* [Istio](https://istio.io/latest/docs/setup/getting-started/)\n* [Ingress-NGINX](https://kubernetes.github.io/ingress-nginx/) (retired; existing deployments only)\n\nOpenShift users can use [routes](https://redis.io/docs/latest/operate/kubernetes/networking/routes) instead of an Ingress.\n\nInstall your chosen Ingress controller, making sure `ssl-passthrough` is enabled. It is on by default for HAProxy and off by default for Ingress-NGINX. For Ingress-NGINX, start the controller with the `--enable-ssl-passthrough` flag."},{"id":"configure-dns","title":"Configure DNS","role":"configuration","text":"1. Choose the API hostname and database hostname suffix you will use, replacing `<placeholders>` with your own values. The recommended formats are:\n\n     * REC API hostname (`apiFqdnUrl`): `api-<rec-name>-<rec-namespace>.<subdomain>`\n     * Database hostname suffix (`dbFqdnSuffix`): `.db-<rec-name>-<rec-namespace>.<subdomain>`\n\n     The operator appends each database name to `dbFqdnSuffix` to build the per-database hostname. For example, a database named `mydb` with the suffix above resolves to `mydb.db-<rec-name>-<rec-namespace>.<subdomain>`. For the wildcard DNS record, replace the database name with `*`, which resolves to `*.db-<rec-name>-<rec-namespace>.<subdomain>`.\n\n1. Retrieve the `EXTERNAL-IP` of your Ingress controller's `LoadBalancer` service.\n\n     [code example]\n\n     Below is example output for an HAProxy ingress controller running on a K8s cluster hosted by AWS.  \n\n     [code example]\n\n1. Create DNS records to resolve your chosen REC API hostname and database hostname to the `EXTERNAL-IP` found in the previous step."},{"id":"edit-the-rec-spec","title":"Edit the REC spec","role":"content","text":"Edit the RedisEnterpriseCluster (REC) spec to add the `ingressOrRouteSpec` field, replacing `<placeholders>` below with your own values."},{"id":"nginx-or-haproxy-ingress-controllers","title":"NGINX or HAproxy ingress controllers","role":"content","text":"* Define the REC API hostname (`apiFqdnUrl`) and database hostname suffix (`dbFqdnSuffix`) you chose when configuring DNS.\n* Set `method` to `ingress`.\n* Set `ssl-passthrough` to \"true\".\n* Add any additional annotations required for your ingress controller. See [NGINX docs](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/) or [HAproxy docs](https://haproxy-ingress.github.io/docs/configuration/keys/) for more information.\n\n[code example]\n\nSet `<ingress-controller>` to `haproxy` or `nginx`. The operator validates that `ingressAnnotations` includes `kubernetes.io/ingress.class` and is the only annotation key it requires; add any other controller-specific annotations alongside it."},{"id":"openshift-routes","title":"OpenShift routes","role":"content","text":"* Define the REC API hostname (`apiFqdnUrl`) and database hostname suffix (`dbFqdnSuffix`) you chose when configuring DNS.\n* Set `method` to `openShiftRoute`.\n\n[code example]\n\nOpenShift routes do not require any `ingressAnnotations` in the `ingressOrRouteSpec`."}],"examples":[{"id":"configure-dns-ex0","language":" sh","code":"$ kubectl get svc <haproxy-ingress | ingress-ngnix-controller> \\\n                         -n <ingress-ctrl-namespace>","section_id":"configure-dns"},{"id":"configure-dns-ex1","language":" sh","code":"NAME              TYPE           CLUSTER-IP    EXTERNAL-IP                                                              PORT(S)                      AGE   \n     haproxy-ingress   LoadBalancer   10.43.62.53   a56e24df8c6173b79a63d5da54fd9cff-676486416.us-east-1.elb.amazonaws.com   80:30610/TCP,443:31597/TCP   21m","section_id":"configure-dns"},{"id":"nginx-or-haproxy-ingress-controllers-ex0","language":"sh","code":"kubectl patch rec <rec-name> --type merge --patch \"{\\\"spec\\\": \\\n    {\\\"ingressOrRouteSpec\\\": \\\n      {\\\"apiFqdnUrl\\\": \\\"api-<rec-name>-<rec-namespace>.example.com\\\", \\\n      \\\"dbFqdnSuffix\\\": \\\".db-<rec-name>-<rec-namespace>.example.com\\\", \\\n      \\\"ingressAnnotations\\\": \\\n       {\\\"kubernetes.io/ingress.class\\\": \\\"<ingress-controller>\\\", \\\n       \\\"<ingress-controller-annotation>/ssl-passthrough\\\": \\\"true\\\"}, \\\n      \\\"method\\\": \\\"ingress\\\"}}}\"","section_id":"nginx-or-haproxy-ingress-controllers"},{"id":"openshift-routes-ex0","language":"sh","code":"kubectl patch rec <rec-name> --type merge --patch \"{\\\"spec\\\": \\\n     {\\\"ingressOrRouteSpec\\\": \\\n     {\\\"apiFqdnUrl\\\": \\\"api-<rec-name>-<rec-namespace>.example.com\\\" \\ \n     \\\"dbFqdnSuffix\\\": \\\".db-<rec-name>-<rec-namespace>.example.com\\\", \\\n     \\\"method\\\": \\\"openShiftRoute\\\"}}}\"","section_id":"openshift-routes"}]}
{"schema_version":2,"id":"operate/kubernetes/networking/istio-ingress","title":"Configure Istio for external routing","url":"https://redis.io/docs/latest/operate/kubernetes/networking/istio-ingress/","summary":"Configure Istio as an ingress controller for access to your Redis Enterprise databases from outside the Kubernetes cluster.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T16:14:52+01:00","page_type":"content","content_hash":"f9f44c5610d253d964c5f9c17a57c66933b228c842811f9b64249ad89af8780c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise for Kubernetes has the ability to use an Istio Ingress gateway as an alternative to NGINX or HaProxy Ingress controllers.\n\nIstio can also understand Ingress resources, but using that mechanism takes away the advantages and options that the native Istio resources provide. Istio offers its own configuration methods using custom resources.\n\nTo configure Istio to work with the Redis Kubernetes operator, we will use two custom resources: a `Gateway` and a `VirtualService`. Then you'll be able to establish external access to your database.\n\n> [!NOTE]\n> When using Istio ingress for Redis on Kubernetes, avoid partial wildcard hostnames in your REC configuration. Use only valid FQDNs or leftmost wildcards for Istio compatibility and stable external routing. For more information, see [Istio Ingress Fails for Redis on Kubernetes with Partial Wildcard Hostnames](https://support.redislabs.com/hc/en-us/articles/30446436267666-Istio-Ingress-Fails-for-Redis-on-Kubernetes-with-Partial-Wildcard-Hostnames)."},{"id":"install-and-configure-istio-for-redis-enterprise","title":"Install and configure Istio for Redis Enterprise","role":"setup","text":"1. [Download](https://istio.io/latest/docs/setup/getting-started/) and [install](https://istio.io/latest/docs/setup/getting-started/) Istio (see instructions from Istio's [Getting Started](https://istio.io/latest/docs/setup/getting-started/) guide).\n\n    Once the installation is complete, all the deployments, pods, and services will be deployed in a namespace called `istio-system`. This namespace contains a `LoadBalancer` type service called `service/istio-ingressgateway` that exposes the external IP address.\n\n1. Find the `EXTERNAL-IP` for the `istio-ingressgateway` service.\n\n    [code example]\n\n1. Create a DNS entry that resolves your chosen database hostname (or a wildcard `*` followed by your domain) to the Istio `EXTERNAL-IP`. Use this hostname to access your database from outside the cluster.\n\n    In this example, any hostname that ends with `.istio.k8s.my.example.com` will resolve to the Istio LoadBalancer's external IP of `10.145.78.91`. Substitute your own values accordingly.\n\n1. Verify the record was created successfully.\n\n    [code example]\n\n    Look in the `ANSWER SECTION` for the record you just created.\n\n    [code example]"},{"id":"create-custom-resources","title":"Create custom resources","role":"content","text":""},{"id":"gateway-custom-resource","title":"`Gateway` custom resource","role":"content","text":"1. On a different namespace from `istio-system`, create a `Gateway` custom resource file (`redis-gateway.yaml` in this example).\n\n    - Replace `.istio.k8s.my.example.com` with the domain that matches your DNS record.\n    - Replace `<selector-label>` with the label set on your Istio ingress gateway pod. A default `istioctl install` deploys the gateway with label `istio: ingressgateway`.\n    - TLS passthrough mode is required to allow secure access to the database.\n\n    [code example]\n\n1. Apply the `Gateway` custom resource file to create the Ingress gateway.\n\n    [code example]\n\n1. Verify the gateway was created successfully.\n\n      [code example]"},{"id":"virtualservice-custom-resource","title":"`VirtualService` custom resource","role":"content","text":"1. On a different namespace than `istio-system`, create the `VirtualService` custom resource file (`redis-vs.yaml` in this example).\n\n    [code example]\n\n    This creates both a route to contact the API server on the REC (`rec1`) and a route to contact one of the databases (`db1`).\n\n    - Replace `.istio.k8s.my.example.com` with the domain that matches your DNS record.\n    - The gateway's metadata name must be similar to the gateway's spec name (`redis-gateway` in this example).\n1. Apply `VirtualService` custom resource file to create the virtual service.\n\n    [code example]\n\n1. Verify the virtual service was created successfully.\n\n    [code example]\n\n1. [Deploy the operator](https://redis.io/docs/latest/operate/kubernetes/deployment/quick-start), Redis Enterprise Cluster (REC), and Redis Enterprise Database (REDB) on the same namespace as the gateway and virtual service."},{"id":"test-your-external-access-to-the-database","title":"Test your external access to the database","role":"content","text":"To [test your external access](https://redis.io/docs/latest/operate/kubernetes/networking/ingress) to the database, you need a client that supports [TLS](https://en.wikipedia.org/wiki/Transport_Layer_Security) and [SNI](https://en.wikipedia.org/wiki/Server_Name_Indication).\n\nSee [Test your access with Openssl](https://redis.io/docs/latest/operate/kubernetes/networking/ingress#test-your-access-with-openssl) or [Test your access with Python](https://redis.io/docs/latest/operate/kubernetes/networking/ingress#test-your-access-with-python) for more info."}],"examples":[{"id":"install-and-configure-istio-for-redis-enterprise-ex0","language":"sh","code":"kubectl get svc istio-ingressgateway -n istio-system\n\n    NAME                   TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)                                                                      AGE\n    istio-ingressgateway   LoadBalancer   10.34.67.89   10.145.78.91   15021:12345/TCP,80:67891/TCP,443:23456/TCP,31400:78901/TCP,15443:10112/TCP   3h8m","section_id":"install-and-configure-istio-for-redis-enterprise"},{"id":"install-and-configure-istio-for-redis-enterprise-ex1","language":"sh","code":"dig api.istio.k8s.my.example.com","section_id":"install-and-configure-istio-for-redis-enterprise"},{"id":"install-and-configure-istio-for-redis-enterprise-ex2","language":"sh","code":";; ANSWER SECTION:\n    api.istio.k8s.my.example.com 0 IN    A       10.145.78.91","section_id":"install-and-configure-istio-for-redis-enterprise"},{"id":"gateway-custom-resource-ex0","language":"yaml","code":"apiVersion: networking.istio.io/v1beta1\n    kind: Gateway\n    metadata:\n      name: redis-gateway\n    spec:\n      selector:\n        istio: <selector-label>\n      servers:\n      - hosts:\n        - '*.istio.k8s.my.example.com'\n        port:\n          name: tls\n          number: 443\n          protocol: TLS\n        tls:\n          mode: PASSTHROUGH","section_id":"gateway-custom-resource"},{"id":"gateway-custom-resource-ex1","language":"sh","code":"kubectl apply -f redis-gateway.yaml","section_id":"gateway-custom-resource"},{"id":"gateway-custom-resource-ex2","language":"sh","code":"kubectl get gateway\n\n      NAME            AGE\n      redis-gateway   3h33m","section_id":"gateway-custom-resource"},{"id":"virtualservice-custom-resource-ex0","language":"yaml","code":"apiVersion: networking.istio.io/v1beta1\n    kind: VirtualService\n    metadata:\n      name: redis-vs\n    spec:\n      gateways:\n      - redis-gateway\n      hosts:\n      - \"*.istio.k8s.my.example.com\"\n      tls:\n      - match:\n        - port: 443\n          sniHosts:\n          - api.istio.k8s.my.example.com\n        route:\n        - destination:\n            host: rec1\n            port:\n              number: 9443\n      - match:\n        - port: 443\n          sniHosts:\n          - db1.istio.k8s.my.example.com\n        route:\n        - destination:\n            host: db1","section_id":"virtualservice-custom-resource"},{"id":"virtualservice-custom-resource-ex1","language":"sh","code":"kubectl apply -f redis-vs.yaml","section_id":"virtualservice-custom-resource"},{"id":"virtualservice-custom-resource-ex2","language":"sh","code":"kubectl get vs\n\n    NAME       GATEWAYS            HOSTS                              AGE\n    redis-vs   [\"redis-gateway\"]   [\"*.istio.k8s.my.example.com\"]   3h33m","section_id":"virtualservice-custom-resource"}]}
{"schema_version":2,"id":"operate/kubernetes/networking/routes","title":"Use OpenShift routes for external database access","url":"https://redis.io/docs/latest/operate/kubernetes/networking/routes/","summary":"","tags":["docs","operate","kubernetes"],"last_updated":"2026-04-28T13:01:21-05:00","page_type":"content","content_hash":"07c482ed56beeb2e22ce853d72ec60b421de35b00591a94d2276b13694f3f781","sections":[{"id":"overview","title":"Overview","role":"overview","text":"OpenShift routes allow requests to be routed to the database or cluster API from outside the cluster. For more information about routes, see [OpenShift documentation](https://docs.openshift.com/container-platform/4.13/networking/routes/route-configuration.html)."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"* Before you can connect to your database from outside the cluster, you'll need the root CA certificate of the DMC Proxy server to validate the server certificate.\n\n  By default, the DMC Proxy uses a self-signed certificate.  You can retrieve it from the Redis Enterprise admin console, or from the machine at `/etc/opt/redislabs/proxy_cert.pem` (default location), and save it as a file (for example, named \"ca.pem\") on the client machine.\n\n* Your database also needs TLS encryption enabled."},{"id":"create-openshift-route","title":"Create OpenShift route","role":"content","text":"1. Select the **Networking/Routes** section of the OpenShift web console.\n\n1. Select **Create route** and fill out the following fields:\n\n   * **Name**: Choose any name you want as the first part of your generated hostname\n   * **Hostname**: Leave blank\n   * **Path**: Leave blank\n   * **Service**: Select the service for the database you want to access\n   * **TLS Termination**: Choose \"passthrough\"\n   * **Insecure Traffic**: Select \"None\"\n\n1. Select **Create**.\n\n1. Find the hostname for your new route. After route creation, it appears in the \"Host\" field.\n\n1. Verify you have a DNS entry to resolve the hostname for your new route to the cluster's load balancer."},{"id":"access-database","title":"Access database","role":"content","text":"Access the database from outside the cluster using `redis-cli` or `openssl`.\n\nTo connect with `redis-cli`:\n  \n   [code example]\n\nReplace the `<hostname>` value with the hostname for your new route.\n\nTo connect with `openssl`:\n\n   [code example]"}],"examples":[{"id":"access-database-ex0","language":"sh","code":"redis-cli -h <hostname> -p 443 --tls --cacert ./ca.pem --sni <hostname>","section_id":"access-database"},{"id":"access-database-ex1","language":"sh","code":"openssl s_client -connect <hostname>:443 -crlf -CAfile ./ca.pem -servername <hostname>","section_id":"access-database"}]}
{"schema_version":2,"id":"operate/kubernetes/re-clusters/cluster-recovery","title":"Recover a Redis Enterprise cluster on Kubernetes","url":"https://redis.io/docs/latest/operate/kubernetes/re-clusters/cluster-recovery/","summary":"This task describes how to recover a Redis Enterprise cluster on Kubernetes.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","page_type":"content","content_hash":"3ae1d0d70458c5554d184c4a06ea32bfc11deb2793dd6eaa4f8dc56cb7181f61","sections":[{"id":"overview","title":"Overview","role":"overview","text":"When a Redis Enterprise cluster loses contact with more than half of its nodes either because of failed nodes or network split,\nthe cluster stops responding to client connections.\nWhen this happens, you must recover the cluster to restore the connections.\n\nYou can also perform cluster recovery to reset cluster nodes, to troubleshoot issues, or in a case of active/passive failover.\n\nThe Redis Enterprise for Kubernetes automates these recovery steps:\n\n1. Recreates a fresh Redis Enterprise cluster\n1. Mounts the persistent storage with the recovery files from the original cluster to the nodes of the new cluster\n1. Recovers the cluster configuration on the first node in the new cluster\n1. Joins the remaining nodes to the new cluster.\n\n> [!WARNING]\n> Redis Enterprise for Kubernetes 7.2.4-2 introduces a new limitation. You cannot recover or upgrade your cluster if there are databases with old module versions or manually uploaded modules. See the [Redis Enterprise Software 7.2.4 known limitations](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52#cluster-recovery-with-manually-uploaded-modules) for more details.\n\n> [!NOTE]\n> If your cluster uses user-defined modules, the recovery process doesn't block on module validation errors (such as URL or credential issues). The cluster can recover successfully, and you can resolve any module configuration issues after recovery is complete. See [User-defined modules](https://redis.io/docs/latest/operate/kubernetes/re-databases/modules#user-defined-modules) for more information."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"- For cluster recovery, the cluster must be [deployed with persistence](https://redis.io/docs/latest/operate/kubernetes/recommendations/persistent-volumes)."},{"id":"recover-a-cluster","title":"Recover a cluster","role":"content","text":"1. Edit the REC resource to set the `clusterRecovery` flag to `true`.\n\n    [code example]\n\n\n1. Wait for the cluster to recover until it is in the \"Running\" state.\n\n    To see the state of the cluster, run:\n\n    [code example]\n\n1. To recover the database, see [Recover a failed database](https://redis.io/docs/latest/operate/rs/databases/recover)."}],"examples":[{"id":"recover-a-cluster-ex0","language":"sh","code":"kubectl patch rec <cluster-name> -n <rec_namespace> --type merge --patch '{\"spec\":{\"clusterRecovery\":true}}'","section_id":"recover-a-cluster"},{"id":"recover-a-cluster-ex1","language":"sh","code":"watch \"kubectl describe rec | grep State\"","section_id":"recover-a-cluster"}]}
{"schema_version":2,"id":"operate/kubernetes/re-clusters/connect-prometheus-operator","title":"Connect the Prometheus operator to Redis Enterprise for Kubernetes","url":"https://redis.io/docs/latest/operate/kubernetes/re-clusters/connect-prometheus-operator/","summary":"This article describes how to configure a Prometheus operator custom resource to allow it to export metrics from Redis Enterprise for Kubernetes.","tags":["docs","operate","kubernetes"],"last_updated":"2026-10-05T10:55:35+01:00","page_type":"content","content_hash":"4e4a6765a036a380e0e4f2c836fd4b6e3344eb8e45907aefbc73208604e739ec","sections":[{"id":"overview","title":"Overview","role":"overview","text":"To collect  metrics data from your databases and Redis Enterprise cluster (REC), you can connect your [Prometheus](https://prometheus.io/) server to an endpoint exposed on your REC. Redis Enterprise for Kubernetes creates a dedicated service to expose the `prometheus` port (8070) for data collection. A custom resource called `ServiceMonitor` allows the [Prometheus operator](https://github.com/prometheus-operator/prometheus-operator/tree/main/Documentation) to connect to this port and collect data from Redis Enterprise.\n\nTo expose database tags as metric labels so you can filter and group metrics by attributes such as environment or team, see [Enrich database metrics with tags](https://redis.io/docs/latest/operate/kubernetes/re-databases/enrich-metrics-with-tags)."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before connecting Redis Enterprise to Prometheus on your Kubernetes cluster, make sure you've done the following:\n\n- [Deploy Redis Enterprise for Kubernetes](https://redis.io/docs/latest/operate/kubernetes/deployment/quick-start) (version 6.2.10-4 or newer)\n- [Deploy the Prometheus operator](https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/getting-started/introduction.md) (version 0.19.0 or newer)\n- [Create a Redis Enterprise cluster](https://redis.io/docs/latest/operate/kubernetes/deployment/quick-start#create-a-redis-enterprise-cluster-rec)"},{"id":"create-a-servicemonitor-custom-resource","title":"Create a `ServiceMonitor` custom resource","role":"content","text":"Below is an example `ServiceMonitor` custom resource file. By specifying the service label (`app: redis.io/service=prom-metrics`) in the `selector.matchLabels` section, you can point the Prometheus operator to the correct Redis Enterprise service (`<rec_name>-prom`).\n\nYou'll need to configure the following fields to connect Prometheus to Redis Enterprise:\n\n| Section | Field | Value |\n|---|---|---|\n| `spec.endpoints` | `port` | Name of exposed port (`prometheus`) |\n| `spec.namespaceSelector` | `matchNames` | Namespace for your REC |\n| `spec.selector` | `matchLabels` | REC service label (`app: redis.io/service=prom-metrics`) |\n\nApply the file in the same namespace as your Redis Enterprise cluster (REC).\n\n> [!NOTE]\n> If Redis Enterprise and Prometheus are deployed in different namespaces, you'll also need to add the [`serviceMonitorNamespaceSelector`](https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/api-reference/api.md) field to your Prometheus resource. See the [Prometheus operator documentation](https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/user-guides/running-exporters.md) for more details on cross-namespace `ServiceMonitor` configuration.\n\n\n[code example]\n\nFor more info about configuring the `ServiceMonitor` resource, see the [`ServiceMonitorSpec` API documentation](https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/api-reference/api.md#monitoring.coreos.com/v1.ServiceMonitorSpec)."},{"id":"more-info","title":"More info","role":"content","text":"- github.com/prometheus-operator\n  - [Getting started](https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/getting-started/introduction.md)\n  - [Running exporters](https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/user-guides/running-exporters.md)\n  - [Troubleshooting ServiceMonitor changes](https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/platform/troubleshooting.md#troubleshooting-servicemonitor-changes)\n- redis.io/docs\n  - [Metrics in Prometheus](https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise/prometheus-metrics-definitions)\n  - [Monitoring and metrics](https://redis.io/docs/latest/operate/rs/monitoring)"}],"examples":[{"id":"create-a-servicemonitor-custom-resource-ex0","language":"YAML","code":"apiVersion: monitoring.coreos.com/v1\nkind: ServiceMonitor\nmetadata:\n  name: redis-enterprise\nspec:\n  endpoints:\n  - interval: 15s\n    port: prometheus\n    scheme: https\n    tlsConfig:\n      insecureSkipVerify: true\n  namespaceSelector:\n    matchNames:\n    - <your_REC_namespace>\n  selector:\n    matchLabels:\n      redis.io/service: prom-metrics","section_id":"create-a-servicemonitor-custom-resource"}]}
{"schema_version":2,"id":"operate/kubernetes/re-clusters/connect-to-admin-console","title":"Connect to the admin console","url":"https://redis.io/docs/latest/operate/kubernetes/re-clusters/connect-to-admin-console/","summary":"Connect to the Redis Enterprise admin console to manage your Redis Enterprise cluster.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","page_type":"content","content_hash":"ef87f06e763f40bcd9d683cabece1b1af00766ae010e77738043c2c9f1d1b3ac","sections":[{"id":"content","title":"Content","role":"content","text":"The username and password for the Redis Enterprise Software [admin console](https://redis.io/docs/latest/operate/rs) are stored in a Kubernetes [secret](https://kubernetes.io/docs/concepts/configuration/secret/). After retrieving your credentials, you can use port forwarding to connect to the admin console.\n\n> [!NOTE]\n> There are several methods for accessing the admin console. Port forwarding is the simplest, but not the most efficient method for long-term use. You could also use a load balancer service or Ingress. \n\n1. Switch to the namespace with your Redis Enterprise cluster (REC).\n\n    [code example]\n\n1. Find your cluster name from your list of secrets.\n\n    [code example]\n\n    In this example, the cluster name is `rec`.\n\n1. Extract and decode your credentials from the secret.\n\n    [code example]\n\n1. Find the port for the REC UI service in the `spec:ports` section of the service definition file.\n\n    [code example]\n\n    > [!NOTE]\n    > The default port is 8443.\n\n1. Use `kubectl port-forward` to forward your local port to the service port.\n\n    [code example]\n\n1. View the admin console from a web browser on your local machine at `https://localhost:8443`."}],"examples":[{"id":"content-ex0","language":"sh","code":"kubectl config set-context --current --namespace=<namespace-of-rec>","section_id":"content"},{"id":"content-ex1","language":"sh","code":"kubectl get secret","section_id":"content"},{"id":"content-ex2","language":"sh","code":"kubectl get secret <cluster-name> -o jsonpath='{.data.username}' | base64 --decode\n    kubectl get secret <cluster-name> -o jsonpath='{.data.password}' | base64 --decode","section_id":"content"},{"id":"content-ex3","language":"sh","code":"kubectl get service/<cluster-name>-ui -o yaml","section_id":"content"},{"id":"content-ex4","language":"sh","code":"kubectl port-forward service/<cluster-name>-ui <local-port>:<service-port>","section_id":"content"}]}
{"schema_version":2,"id":"operate/kubernetes/re-clusters/delete-custom-resources","title":"Delete custom resources","url":"https://redis.io/docs/latest/operate/kubernetes/re-clusters/delete-custom-resources/","summary":"This article explains how to delete Redis Enterprise clusters and Redis Enterprise databases from your Kubernetes environment.","aliases":["/operate/kubernetes/delete-custom-resources/"],"tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","page_type":"content","content_hash":"bebe6e8b5afcb6cc0d33160fa0a63b9a2d96881d30b65165746e092d4c3a192d","sections":[{"id":"multi-namespace-management","title":"Multi-namespace management","role":"overview","text":"Before you can delete a namespace, you must remove the Redis custom resources ([REDB](#delete-a-database-redb), [REC](#delete-a-redis-enterprise-cluster-rec), and [operator](#delete-operator-from-one-namespace)) residing within a namespace before you can delete that namespace. Deleting namespace containing an REDB may cause errors or data loss."},{"id":"delete-a-database-redb","title":"Delete a database (REDB)","role":"content","text":"To delete a database managed by the Redis Enterprise Kubernetes operator, run `kubectl delete redb <your-db-name>` from your K8s cluster.\n\nWhen you delete a database, your data and the REDB custom resource are also deleted."},{"id":"delete-a-redis-enterprise-cluster-rec","title":"Delete a Redis Enterprise cluster (REC)","role":"content","text":"To delete a Redis Enterprise cluster managed by the operator:\n\n1. Delete all the databases in your cluster.\n\n2. Run `kubectl delete rec <your-rec-name>` from your K8s cluster.\n\nWhen you delete your cluster, your databases and the REC custom resource are also deleted. However, persistent volume claims (PVCs) for your cluster are not deleted in the process. If you want to delete your PVCs, you'll have to delete them manually."},{"id":"delete-the-operator","title":"Delete the operator","role":"content","text":"There are two ways to delete the operator. You can delete the entire operator bundle, but this will also delete any cluster-wide resources created by the operator. To only delete resources within a specific namespace and leave other cluster-wide resources intact, you need to delete each resource manually.\n\nWhether you decide to delete the entire bundle or just resources from a specific namespace, it's important to delete the operator ConfigMap (`operator-environment-config`).\n\n[code example]"},{"id":"delete-operator-bundle","title":"Delete operator bundle","role":"content","text":"To delete the operator from your K8s cluster, you can delete the operator bundle with:\n\n- `kubectl delete -f bundle.yaml` for non-OpenShift K8s deployments\n- `kubectl delete -f openshift.bundle.yaml` for OpenShift deployments \n\nThis will remove the operator and its custom resource definitions (CRDs) from your K8s cluster.\n\n> [!WARNING]\n>  The Redis Enterprise CRDs are non-namespaced resources, meaning they are shared across your entire K8s cluster. Deleting CRDs in one namespace will delete custom resources in every other namespace across the K8s cluster."},{"id":"delete-operator-from-one-namespace","title":"Delete operator from one namespace","role":"content","text":"If you have Redis Enterprise clusters running in different namespaces on the same K8s cluster, deleting the entire operator bundle might cause data loss.\n\nTo safely delete the operator from one namespace without affecting the others, delete the operator files individually, excluding the CRD files:\n\n[code example]\n\nYou will also need to remove [the `namespaceSelector` section from the validating webhook](https://redis.io/docs/latest/operate/kubernetes/deployment/quick-start#webhook)."},{"id":"delete-an-active-active-database-reaadb","title":"Delete an Active-Active database (REAADB)","role":"content","text":"1. On one of the existing participating clusters, delete the REEADB (substituting `<reaadb-name>` with your database name).\n\n  [code example]\n\n2. Verify the REAADB no longer exists. \n\n  [code example]"},{"id":"delete-a-remote-cluster-rerc","title":"Delete a remote cluster (RERC)","role":"content","text":"1. Verify the RERC you want to delete isn't listed as a participating cluster in any REAADB resources.\n\n    If an RERC is still listed as a participating cluster in any database, the deletion will be blocked.\n\n2. On one of the existing participating clusters, delete the RERC (substituting `<rerc-name>` with your database name).\n\n  [code example]\n\n3. Verify the RERC no longer exists.\n\n  [code example]"},{"id":"troubleshoot-redb-deletion","title":"Troubleshoot REDB deletion","role":"errors","text":"The operator attaches a finalizer to the Redis Enterprise database (REDB) object. This makes sure the database is deleted before the REDB custom resource is removed from the K8s cluster.\n\nIf the operator isn't running, or some other fatal error occurs, the finalizer isn't removed automatically by the operator. In this case, you won't be able to delete your REDB resource.\n\nIf this happens, you can remove the finalizer manually.\n\n> [!WARNING]\n>  If you remove the finalizer manually, there is no guarantee that the underlying REDB has been deleted. This may cause resource issues and require manual intervention. \n\n[code example]"},{"id":"troubleshoot-rec-deletion","title":"Troubleshoot REC deletion","role":"errors","text":"The operator attaches a finalizer to the Redis Enterprise cluster (REC) object. This makes sure the Redis cluster is deleted before the REC custom resource is removed from the K8s cluster.\n\nIf the operator isn't running, or some other fatal error occurs, the finalizer isn't removed automatically by the operator. In this case, you won't be able to delete your REC resource.\n\nIf this happens, you can remove the finalizer manually.\n\n> [!WARNING]\n>  If you remove the finalizer manually, there is no guarantee that the underlying REC has been deleted. This may cause resource issues and require manual intervention. \n\n[code example]"}],"examples":[{"id":"delete-the-operator-ex0","language":"sh","code":"kubectl delete cm operator-environment-config","section_id":"delete-the-operator"},{"id":"delete-operator-from-one-namespace-ex0","language":"sh","code":"kubectl delete -f role.yaml\nkubectl delete -f role_binding.yaml\nkubectl delete -f service_account.yaml\nkubectl delete -f admission-service.yaml\nkubectl delete -f operator.yaml","section_id":"delete-operator-from-one-namespace"},{"id":"delete-an-active-active-database-reaadb-ex0","language":"sh","code":"kubectl delete reaadb <reaadb-name>","section_id":"delete-an-active-active-database-reaadb"},{"id":"delete-an-active-active-database-reaadb-ex1","language":"sh","code":"kubectl get reaadb -o=jsonpath='{range .items[*]}{.metadata.name}'","section_id":"delete-an-active-active-database-reaadb"},{"id":"delete-a-remote-cluster-rerc-ex0","language":"sh","code":"kubectl delete rerc <rerc-name>","section_id":"delete-a-remote-cluster-rerc"},{"id":"delete-a-remote-cluster-rerc-ex1","language":"sh","code":"kubectl get rerc -o=jsonpath='{range .items[*]}{.metadata.name}'","section_id":"delete-a-remote-cluster-rerc"},{"id":"troubleshoot-redb-deletion-ex0","language":"sh","code":"kubectl patch redb <your-db-name> --type=json -p \\\n    '[{\"op\":\"remove\",\"path\":\"/metadata/finalizers\",\"value\":\"finalizer.redisenterprisedatabases.app.redislabs.com\"}]'","section_id":"troubleshoot-redb-deletion"},{"id":"troubleshoot-rec-deletion-ex0","language":"sh","code":"kubectl patch rec <your-rec-name> --type=json -p \\\n    '[{\"op\":\"remove\",\"path\":\"/metadata/finalizers\",\"value\":\"redbfinalizer.redisenterpriseclusters.app.redislabs.com\"}]'","section_id":"troubleshoot-rec-deletion"}]}
{"schema_version":2,"id":"operate/kubernetes/re-clusters/expand-pvc","title":"Expand PersistentVolumeClaim (PVC)","url":"https://redis.io/docs/latest/operate/kubernetes/re-clusters/expand-pvc/","summary":"Expand your persistent volume claim by editing the REC.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","page_type":"content","content_hash":"9b38c1dc20090e0aef8ec7192092fdd52385dea70c8d9eceb6eec39d966d32d3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This article outlines steps to increase the size of the persistent volume claim for your Redis Enterprise cluster (REC).\n\n> [!NOTE]\n>  This feature is only supported in versions 7.4.2-12 and above. \n\n[PersistentVolumeClaims (PVC)](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#expanding-persistent-volumes-claims) are created by the Redis Enterprise operator and used by the RedisEnterpriseCluster (REC). PVCs are created with a specific size and [can be expanded](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#expanding-persistent-volumes-claims) with the following steps, if the underlying [storage class](https://kubernetes.io/docs/concepts/storage/storage-classes/) supports it.\n\nThis process involves deleting and recreating the REC StatefulSet with a larger persistent volume size. The pods owned by the StatefulSet are not restarted or affected by the deletion and recreation process, except when they are left without an owner momentarily.\n\n> [!NOTE]\n> Shrinking (reducing the size) of your PVC is not allowed. This process only allows you to expand (size up) your PVC."},{"id":"default-pvc-size","title":"Default PVC size","role":"content","text":"By default, if you omit [`spec.persistentSpec.volumeSize`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api#specpersistentspec), the operator allocates a persistent volume that is five times (5x) the Redis Enterprise node memory request defined in [`spec.redisEnterpriseNodeResources.requests.memory`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api#specredisenterprisenoderesources) (per node). This 5x ratio is the recommended minimum capacity.\n\n- If you set [`spec.persistentSpec.volumeSize`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api#specpersistentspec) explicitly, that exact size is used and the 5x default does not apply.\n- Changing node memory requests does not automatically resize existing PVCs. Use the procedure below to expand the PVC if you want to maintain the 5x ratio after changing memory.\n- If you omit [`spec.redisEnterpriseNodeResources.requests.memory`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api#specredisenterprisenoderesources), the operator uses its default memory request (4Gi). With `volumeSize` omitted, this results in a default PVC size of approximately 20Gi per node (5 × 4Gi). See [sizing on Kubernetes](https://redis.io/docs/latest/operate/kubernetes/recommendations/sizing-on-kubernetes) for defaults.\n\n- See the [volume size recommendations](https://redis.io/docs/latest/operate/kubernetes/recommendations/persistent-volumes#volume-size) and [hardware requirements](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/hardware-requirements) for guidance."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"> [!WARNING]\n> Do not change any other REC fields related to the StatefulSet while resizing is in progress.\n\n- PVC expansion must be supported and enabled by the StorageClass and underlying storage driver of the REC PVCs.\n  - The relevant StorageClass is the one associated with the REC PVCs. The StorageClass for existing PVCs cannot be changed.\n- The StorageClass must be configured with `allowVolumeExpansion: true`.\n- Your storage driver must support online expansion.\n- We highly recommend you backup your databases before beginning this PVC expansion process.\n\n> [!WARNING]\n>  OpenShift users should be aware that (`ClusterResourceQuota`) can limit the PVC expansion. Check your quota before resizing using `oc describe clusterresourcequota <quota-name>`.\n\n> [!WARNING]\n>  PVC expansion is not supported when using Redis Flex (previously Redis on Flash). Do not enable `enablePersistentVolumeResize` if your REC uses `redisOnFlashSpec` as this will result in conflicts."},{"id":"expand-rec-pvc","title":"Expand REC PVC","role":"content","text":"1. Enable the REC persistent volume resize flag.\n\n    [code example]\n\n1. Set the value of `volumeSize` to your desired size.\n\n    [code example]\n\n1. Apply the changes to the REC, replacing `<your-rec.yaml>` with the name of your REC.\n\n  [code example]\n\nAfter applying the REC changes, the PVCs will begin to expand to the new size.\n\nOnce all the PVCs finish the resizing process, the operator will delete and recreate the StatefulSet with the new volume size."},{"id":"track-progress","title":"Track progress","role":"content","text":"You can track the progress by monitoring the status of the REC and PersistentVolumeClaim objects.\n\nThe REC status will correspond to the status of one or more PVCs, and will reflect if the resizing is successful or failed.\n\nWhile the resizing is in progress, the status will be:\n\n[code example]\n\nWhen the resizing is complete, the status becomes Provisioned and the new volume size is available for use by the REC pods.\n\n[code example]"},{"id":"troubleshooting","title":"Troubleshooting","role":"errors","text":"If an error occurs during this process:\n\n- Examine the status and events of the REC and PVC objects.\n\n  [code example]\n\n  [code example]\n\n- Examine the logs of the operator pods.\n\n  [code example]"}],"examples":[{"id":"expand-rec-pvc-ex0","language":"YAML","code":"spec:\n        persistentSpec:\n          enablePersistentVolumeResize: true","section_id":"expand-rec-pvc"},{"id":"expand-rec-pvc-ex1","language":"YAML","code":"spec:\n        persistentSpec:\n          enablePersistentVolumeResize: true\n          volumeSize: <new-size>Gi","section_id":"expand-rec-pvc"},{"id":"expand-rec-pvc-ex2","language":"sh","code":"kubectl apply -f <your-rec.yaml>","section_id":"expand-rec-pvc"},{"id":"track-progress-ex0","language":"yaml","code":"status:\n  persistenceStatus:\n    status: Resizing\n    succeeded: 2/3","section_id":"track-progress"},{"id":"track-progress-ex1","language":"yaml","code":"status:\n  persistenceStatus:\n    status: Provisioned\n    succeeded: 3/3","section_id":"track-progress"},{"id":"troubleshooting-ex0","language":"sh","code":"kubectl describe pvc","section_id":"troubleshooting"},{"id":"troubleshooting-ex1","language":"sh","code":"kubectl get events","section_id":"troubleshooting"},{"id":"troubleshooting-ex2","language":"sh","code":"kubectl logs <operator_pod_name>","section_id":"troubleshooting"}]}
{"schema_version":2,"id":"operate/kubernetes/re-clusters","title":"Redis Enterprise clusters (REC)","url":"https://redis.io/docs/latest/operate/kubernetes/re-clusters/","summary":"Create and manage Redis Enterprise clusters (REC) on Kubernetes using the Redis Enterprise operator.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","children":[{"id":"operate/kubernetes/re-clusters/connect-to-admin-console","summary":"Connect to the Redis Enterprise admin console to manage your Redis Enterprise cluster.","title":"Connect to the admin console","url":"https://redis.io/docs/latest/operate/kubernetes/re-clusters/connect-to-admin-console/"},{"id":"operate/kubernetes/re-clusters/multi-namespace","summary":"Redis Enterprise for Kubernetes allows you to deploy to multiple namespaces within your Kubernetes cluster. This article shows you how to configure your Redis Enterprise cluster to connect to databases in multiple namespaces","title":"Manage databases in multiple namespaces","url":"https://redis.io/docs/latest/operate/kubernetes/re-clusters/multi-namespace/"},{"id":"operate/kubernetes/re-clusters/cluster-recovery","summary":"This task describes how to recover a Redis Enterprise cluster on Kubernetes.","title":"Recover a Redis Enterprise cluster on Kubernetes","url":"https://redis.io/docs/latest/operate/kubernetes/re-clusters/cluster-recovery/"},{"id":"operate/kubernetes/re-clusters/delete-custom-resources","summary":"This article explains how to delete Redis Enterprise clusters and Redis Enterprise databases from your Kubernetes environment.","title":"Delete custom resources","url":"https://redis.io/docs/latest/operate/kubernetes/re-clusters/delete-custom-resources/"},{"id":"operate/kubernetes/re-clusters/expand-pvc","summary":"Expand your persistent volume claim by editing the REC.","title":"Expand PersistentVolumeClaim (PVC)","url":"https://redis.io/docs/latest/operate/kubernetes/re-clusters/expand-pvc/"},{"id":"operate/kubernetes/re-clusters/connect-prometheus-operator","summary":"This article describes how to configure a Prometheus operator custom resource to allow it to export metrics from Redis Enterprise for Kubernetes.","title":"Connect the Prometheus operator to Redis Enterprise for Kubernetes","url":"https://redis.io/docs/latest/operate/kubernetes/re-clusters/connect-prometheus-operator/"}],"page_type":"content","content_hash":"1612bc80c3aa8fc41019941c885a1ce24c822187bd29aa41061f4176076381e7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"A Redis Enterprise cluster (REC) is a custom Kubernetes resource that represents a Redis Enterprise cluster deployment. The Redis Enterprise operator manages the lifecycle of REC resources, including deployment, scaling, upgrades, and recovery operations.\n\nREC resources define the cluster configuration, including node specifications, storage requirements, security settings, and networking configuration. After you deploy the cluster, it provides a foundation for creating and managing Redis Enterprise databases (REDB)."},{"id":"cluster-management","title":"Cluster management","role":"content","text":"Manage your Redis Enterprise cluster lifecycle and configuration:\n\n- [Connect to admin console](https://redis.io/docs/latest/operate/kubernetes/re-clusters/connect-to-admin-console) - Access the Redis Enterprise web UI for cluster management\n- [Multi-namespace deployment](https://redis.io/docs/latest/operate/kubernetes/re-clusters/multi-namespace) - Deploy clusters across multiple Kubernetes namespaces\n- [Delete custom resources](https://redis.io/docs/latest/operate/kubernetes/re-clusters/delete-custom-resources) - Safely remove REC and related resources"},{"id":"storage-and-performance","title":"Storage and performance","role":"content","text":"Optimize storage and performance for your Redis Enterprise cluster:\n\n- [Redis Flex](https://redis.io/docs/latest/operate/kubernetes/flex) - Configure automatic data tiering between RAM and flash storage\n- [Expand PVC](https://redis.io/docs/latest/operate/kubernetes/re-clusters/expand-pvc) - Expand persistent volume claims for additional storage"},{"id":"monitoring-and-observability","title":"Monitoring and observability","role":"content","text":"Monitor cluster health and performance:\n\n- [Connect to Prometheus operator](https://redis.io/docs/latest/operate/kubernetes/re-clusters/connect-prometheus-operator) - Integrate with Prometheus for metrics collection and monitoring"},{"id":"call-home-client","title":"Call home client","role":"content","text":"The call home client sends health or error data from your deployment(s) back to Redis. You can disable it by adding the following to your REC specification:\n\n[code example]\n\n> [!NOTE]\n> The REST API approach used for Redis Software deployments will have no effect on Kubernetes deployments. You must use the REC specification method shown above."},{"id":"recovery-and-troubleshooting","title":"Recovery and troubleshooting","role":"content","text":"Handle cluster recovery and troubleshooting scenarios:\n\n- [Cluster recovery](https://redis.io/docs/latest/operate/kubernetes/re-clusters/cluster-recovery) - Recover from cluster failures and restore operations"},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Redis Enterprise databases (REDB)](https://redis.io/docs/latest/operate/kubernetes/re-databases) - Create and manage databases on your cluster\n- [Security](https://redis.io/docs/latest/operate/kubernetes/security) - Configure security settings for your cluster\n- [Networking](https://redis.io/docs/latest/operate/kubernetes/networking) - Set up networking and ingress for cluster access\n- [REC API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api) - Complete API specification for REC resources"}],"examples":[{"id":"call-home-client-ex0","language":"yaml","code":"spec:\n  usageMeter:\n    callHomeClient:\n      disabled: true","section_id":"call-home-client"}]}
{"schema_version":2,"id":"operate/kubernetes/re-clusters/multi-namespace","title":"Manage databases in multiple namespaces","url":"https://redis.io/docs/latest/operate/kubernetes/re-clusters/multi-namespace/","summary":"Redis Enterprise for Kubernetes allows you to deploy to multiple namespaces within your Kubernetes cluster. This article shows you how to configure your Redis Enterprise cluster to connect to databases in multiple namespaces","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","page_type":"content","content_hash":"478f8568d4a5b3fdb28c79270010cae87e672d6fe40f79eb8ac028e233d14a32","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Multiple Redis Enterprise database resources (REDBs) can be associated with a single Redis Enterprise cluster resource (REC) even if they reside in different namespaces.\n\nTo learn more about designing a multi-namespace Redis Enterprise cluster, see [flexible deployment options](https://redis.io/docs/latest/operate/kubernetes/architecture/deployment-options).\n\n> [!NOTE]\n> Multi-namespace installations now support Active-Active databases (REAADB) with certain configuration requirements. For details, see [Multi-namespace Active-Active databases](#multi-namespace-active-active-databases)."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before configuring a multi-namespace deployment, you must have a running [Redis Enterprise cluster (REC)](https://redis.io/docs/latest/operate/kubernetes/deployment/quick-start). See more information in the [deployment](https://redis.io/docs/latest/operate/kubernetes/deployment) section."},{"id":"create-role-and-role-binding-for-managed-namespaces","title":"Create role and role binding for managed namespaces","role":"content","text":"Both the operator and the RedisEnterpriseCluster (REC) resource need access to each namespace the REC will manage. For each **managed** namespace, create a `consumer_role.yaml` and `consumer_role_binding.yaml` file within the managed namespace, as shown in the examples below.\n\n> [!NOTE]\n> These will need to be reapplied each time you [upgrade](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster). \n\nReplace `<rec-namespace>` with the namespace the REC resides in.\nReplace `<service-account-name>` with your own value (defaults to the REC name).\n\n`consumer_role.yaml` example: \n\n\n\n`consumer_role_binding.yaml` example:\n\n\n\n> [!NOTE]\n> **Alternative approach**: Instead of creating individual `Role` objects for each namespace, you can create a single `ClusterRole` and bind it with multiple `RoleBinding` objects. This reduces the number of objects and simplifies role management.\n>\n> To use this approach:\n> 1. Change `kind: Role` to `kind: ClusterRole` in the role definition above\n> 2. Change `roleRef.kind: Role` to `roleRef.kind: ClusterRole` in the role binding definition above\n> 3. Apply the ClusterRole once globally, then apply a RoleBinding in each managed namespace\n\nApply the files, replacing `<managed-namespace>` with your own values:\n\n[code example]\n\n> [!NOTE]\n> If the REC is configured to watch a namespace without setting the role and role binding permissions, or a namespace that is not yet created, the operator will fail and halt normal operations."},{"id":"update-redis-enterprise-operator-configmap","title":"Update Redis Enterprise operator ConfigMap","role":"content","text":"There are two methods of updating the operator ConfigMap (`operator-environment-config`) to specify which namespaces to manage.\n\n- Method 1: Configure the operator to watch for a namespace label and add this label to managed namespaces (available in versions 6.4.2-4 or later).\n- Method 2: Configure the operator with an explicit list of namespaces to manage.\n\nYou can create this ConfigMap manually before deployment, or it will be created automatically after the operator was deployed.\n\n> [!WARNING]\n> Only configure the operator to watch a namespace after the namespace is created and configured with the role/role_binding as explained above. If configured to watch a namespace without setting those permissions or a namespace that is not created yet, the operator will fail and not perform normal operations."},{"id":"method-1-namespace-label-available-in-versions-642-4-or-later","title":"Method 1: Namespace label (available in versions 6.4.2-4 or later)","role":"content","text":"1. Create the `cluster_role_binding.yaml` and `cluster_role.yaml` files. Replace the `<rec-namespace>` with the namespace the Redis Enterprise cluster (REC) resides in.\n\n  `operator_cluster_role.yaml` example:\n\n\n\n  `operator_cluster_role_binding.yaml` example:\n\n\n\n2. Apply the files.\n\n  [code example]\n\n3. Patch the ConfigMap in the REC namespace (`<rec-namespace>`) to identify the managed namespaces with your label (`<label-name>`).\n\n  [code example]\n\n4. For each managed namespace, apply the same label. Replace `<managed-namespace>` with the namespace the REC will manage. Replace `<label-name>` with the value used in the previous step. If you specify a value for `<label-value>`, both the label name and value in managed namespaces must match to be detected by the operator. If the `<label-value>` is empty, only the label name needs to match on managed namespaces and the value is disregarded.\n\n\n  [code example]\n\n> [!NOTE]\n> The operator restarts when it detects a namespace label was added or removed."},{"id":"method-2-explicit-namespace-list","title":"Method 2: Explicit namespace list","role":"content","text":"Patch the `operator-environment-config` in the REC namespace with a new environment variable (`REDB_NAMESPACES`).\n\n[code example]"},{"id":"multi-namespace-active-active-databases","title":"Multi-namespace Active-Active databases","role":"content","text":"You can also deploy `RedisEnterpriseActiveActiveDatabase` (REAADB) objects in consumer namespaces separate from the Redis Enterprise operator or cluster (REC) namespace.\n\nTo do this:\n\n1. Configure each participating cluster’s operator to watch the relevant consumer namespace. See [multi-namespace operator setup](#update-redis-enterprise-operator-configmap).\n2. Ensure all Active-Active prerequisites are met as described in [Configure Active-Active](https://redis.io/docs/latest/operate/kubernetes/active-active/create-reaadb).\n3. In your REAADB custom resource, specify the target consumer namespace using `metadata.namespace`. For each participating cluster, use the `namespace` field under `spec.participatingClusters` to indicate the namespace where the REAADB should be deployed.\n4. If you are using a [global database secret](https://redis.io/docs/latest/operate/kubernetes/active-active/global-db-secret), deploy the secret in each consumer namespace.\n\n> [!NOTE]\n> Apply the REAADB object to only one Kubernetes cluster. Based on the specified participating clusters and namespaces, the operator automatically creates the necessary resources in the other clusters.\n\nFor example:\n\n[code example]\n\n> [!WARNING]\n> Configure the operator to watch a namespace only after the namespace exists and the required `Role` and `RoleBinding` resources have been applied. If the operator is configured to watch a namespace that lacks these permissions or does not exist, it will fail and halt normal operations."}],"examples":[{"id":"create-role-and-role-binding-for-managed-namespaces-ex0","language":"sh","code":"kubectl apply -f consumer_role.yaml -n <managed-namespace>\nkubectl apply -f consumer_role_binding.yaml -n <managed-namespace>","section_id":"create-role-and-role-binding-for-managed-namespaces"},{"id":"method-1-namespace-label-available-in-versions-642-4-or-later-ex0","language":"sh","code":"kubectl apply -f operator_cluster_role.yaml\n  kubectl apply -f operator_cluster_role_binding.yaml","section_id":"method-1-namespace-label-available-in-versions-642-4-or-later"},{"id":"method-1-namespace-label-available-in-versions-642-4-or-later-ex1","language":"sh","code":"kubectl patch ConfigMap/operator-environment-config \\\n  -n <rec-namespace> \\\n  --type merge \\\n  -p '{\"data\": {\"REDB_NAMESPACES_LABEL\": \"<label-name>\"}}'","section_id":"method-1-namespace-label-available-in-versions-642-4-or-later"},{"id":"method-1-namespace-label-available-in-versions-642-4-or-later-ex2","language":"sh","code":"kubectl label namespace <managed-namespace> <label-name>=<label-value>","section_id":"method-1-namespace-label-available-in-versions-642-4-or-later"},{"id":"method-2-explicit-namespace-list-ex0","language":"sh","code":"kubectl patch ConfigMap/operator-environment-config \\ \n-n <rec-namespace> \\\n--type merge \\\n-p '{\"data\":{\"REDB_NAMESPACES\": \"<comma,separated,list,of,namespaces,to,watch\"}}'","section_id":"method-2-explicit-namespace-list"},{"id":"multi-namespace-active-active-databases-ex0","language":"yaml","code":"apiVersion: app.redislabs.com/v1alpha1\nkind: RedisEnterpriseActiveActiveDatabase\nmetadata:\n  name: consumer-reaadb\n  namespace: consumer-namespace-main\nspec:\n  participatingClusters:\n    - name: participating-cluster-main\n    - name: participating-cluster-peer\n      namespace: consumer-namespace-peer\n  globalConfigurations:\n    <your global configurations>","section_id":"multi-namespace-active-active-databases"}]}
{"schema_version":2,"id":"operate/kubernetes/re-databases/db-controller","title":"Manage Redis Enterprise databases for Kubernetes","url":"https://redis.io/docs/latest/operate/kubernetes/re-databases/db-controller/","summary":"This section describes how the database controller provides the ability to create, manage, and use databases via a database custom resource.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T16:14:52+01:00","page_type":"content","content_hash":"ccb9f2fb3d24f0b99e2c54194c184fadf246f408bb7e52b73094ecf83380fcce","sections":[{"id":"redis-enterprise-database-redb-lifecycle","title":"Redis Enterprise database (REDB) lifecycle","role":"overview","text":"A Redis Enterprise database (REDB) is created with a custom resource file. The custom resource defines the size, name, and other specifications for the REDB. The database is created when you apply the custom resource file.\n\nThe database controller in Redis Enterprise for Kubernetes:\n\n- Discovers the custom resource\n- Makes sure the REDB is created in the same namespace as the Redis Enterprise cluster (REC)\n- Maintains consistency between the custom resource and the REDB\n\nThe database controller recognizes the new custom resource and validates the specification.\nIf valid, the controller combines the values specified in\nthe custom resource with default values to create a full specification. It then uses this full specification to create the\ndatabase on the specified Redis Enterprise cluster (REC).\n\n> [!NOTE]\n> The admission controller prevents duplicate database names when databases are created via the Kubernetes operator.\n\nOnce the database is created, it is exposed with the same service mechanisms by the service rigger for the Redis Enterprise cluster.\nIf the database [custom resource is deleted](https://redis.io/docs/latest/operate/kubernetes/re-clusters/delete-custom-resources), the database and its services are deleted from the cluster."},{"id":"flexible-deployment-options","title":"Flexible deployment options","role":"content","text":"Databases in multiple namespaces can be managed by the same operator. To learn more, see [Manage databases in multiple namespaces](https://redis.io/docs/latest/operate/kubernetes/re-clusters/multi-namespace).\n\nTo learn more about designing a multi-namespace Redis Enterprise cluster, see [flexible deployment options](https://redis.io/docs/latest/operate/kubernetes/architecture/deployment-options)."},{"id":"create-a-database","title":"Create a database","role":"content","text":"Your Redis Enterprise database custom resource must be of the `kind: RedisEnterpriseDatabase` and have values for `name` and `memorySize`. All other values are optional and will be defaults if not specified.\n\n1. Create a file (in this example mydb.yaml) that contains your database custom resource.\n\n    [code example]\n\n    To create a REDB in a different namespace from your REC, you need to specify the cluster with `redisEnterpriseCluster` in the `spec` section of your RedisEnterpriseDatabase custom resource.\n\n     [code example]\n\n1. Apply the file in the namespace you want your database to be in.\n\n    [code example]\n\n1. Check the status of your database.\n\n    [code example]\n\n    When the status is `active`, the database is ready to use."},{"id":"modify-a-database","title":"Modify a database","role":"content","text":"The custom resource defines the properties of the database.\nTo change the database, you can edit your original specification and apply the change or use `kubectl edit`.\n\nTo modify the database:\n\n1. Edit the definition:\n\n    [code example]\n\n1. Change the specification (only properties in `spec` section) and save the changes.  \n    For more details, see [Options for Redis Enterprise databases](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api). \n\n1. Monitor the status to see when the changes take effect:\n\n    [code example]\n\n    When the status is `active`, the database is ready for use.\n\nTo add tags to a database and expose them as metric labels, see [Enrich database metrics with tags](https://redis.io/docs/latest/operate/kubernetes/re-databases/enrich-metrics-with-tags)."},{"id":"delete-a-database","title":"Delete a database","role":"content","text":"The database exists as long as the custom resource exists.\nIf you delete the custom resource, the database controller deletes the database.\nThe database controller removes the database and its services from the cluster.\n\nTo delete a database, run:\n\n[code example]"},{"id":"connect-to-a-database","title":"Connect to a database","role":"content","text":"After the database controller creates a database, services for accessing the database are automatically created in the same namespace. Connection information is stored in a Kubernetes [secret](https://kubernetes.io/docs/concepts/configuration/secret/) maintained by the database controller.\n\nFor comprehensive information about connecting to your database, including:\n\n- Service types and access patterns\n- Retrieving connection information from secrets\n- In-cluster and external access methods\n- Connection examples and troubleshooting\n\nSee [Database connectivity](https://redis.io/docs/latest/operate/kubernetes/networking/database-connectivity)."}],"examples":[{"id":"create-a-database-ex0","language":"YAML","code":"apiVersion: app.redislabs.com/v1alpha1\n    kind: RedisEnterpriseDatabase\n    metadata:\n      name: mydb\n    spec:\n      memorySize: 1GB","section_id":"create-a-database"},{"id":"create-a-database-ex1","language":"YAML","code":"redisEnterpriseCluster:\n       name: rec","section_id":"create-a-database"},{"id":"create-a-database-ex2","language":"sh","code":"kubectl apply -f mydb.yaml","section_id":"create-a-database"},{"id":"create-a-database-ex3","language":"sh","code":"kubectl get redb mydb -o jsonpath=\"{.status.status}\"","section_id":"create-a-database"},{"id":"modify-a-database-ex0","language":"sh","code":"kubectl edit redb mydb","section_id":"modify-a-database"},{"id":"modify-a-database-ex1","language":"sh","code":"kubectl get redb mydb -o jsonpath=\"{.status.status}\"","section_id":"modify-a-database"},{"id":"delete-a-database-ex0","language":"sh","code":"kubectl delete redb mydb","section_id":"delete-a-database"}]}
{"schema_version":2,"id":"operate/kubernetes/re-databases/enrich-metrics-with-tags","title":"Enrich database metrics with tags","url":"https://redis.io/docs/latest/operate/kubernetes/re-databases/enrich-metrics-with-tags/","summary":"Expose Redis Enterprise database tags as metric labels so you can filter dashboards and alerts by attributes such as environment, team, or application.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T16:14:52+01:00","page_type":"content","content_hash":"fefb4e7b38aeb76da34252fa18c501865e9bdeefcaa7600b736b72a6e2417906","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Database tags are key-value attributes that you attach to your databases. You can expose selected tag keys as metric labels. Your monitoring stack can then filter and group metrics by those attributes. For example, you can build dashboards scoped to a team or environment and route alerts with context already attached.\n\nTag-derived metrics are controlled in two places:\n\n- **Cluster (REC):** An administrator uses [`spec.metricsConfig`](#configure-the-cluster) on the Redis Enterprise cluster (REC) to turn tag-derived metrics on or off and choose which tag keys are eligible.\n- **Database (REDB / REAADB):** Application teams set tag values on individual databases with [`spec.tags`](#tag-a-database) (REDB) or [`spec.globalConfigurations.tags`](#tag-an-active-active-database) (REAADB).\n\nA tag becomes a metric label only when both of these are true:\n\n- The tag key is in the cluster's allowlist.\n- The tag is set with a value on the database.\n\nKeys that aren't in the allowlist never appear as labels, even if they're set on a database."},{"id":"how-it-works","title":"How it works","role":"content","text":"1. On the REC, set `metricsConfig.exposeDatabaseTags` to `true`. In `metricsConfig.metricsTagKeysExposed`, list the tag keys you want available for metrics.\n2. On each database, set the tag values with `spec.tags` (REDB) or `spec.globalConfigurations.tags` (REAADB).\n3. The operator syncs the cluster configuration and the database tags to Redis Enterprise. Only keys in the allowlist that are present on a database appear as metric labels.\n\nThe REC defines the metrics configuration. The operator overwrites any changes made directly through the Redis Enterprise REST API."},{"id":"configure-the-cluster","title":"Configure the cluster","role":"configuration","text":"Add `metricsConfig` to the REC `spec` to turn tag-derived metrics on and choose which tag keys are eligible:\n\n[code example]\n\nIn this example, only the `env` and `team` tag keys can appear as metric labels. A tag such as `app` is not exposed even if it is set on a database, because it is not in the allowlist.\n\n`metricsConfig` supports the following fields. All fields are optional. The operator applies the listed default when a field is unset.\n\n| Field | Type | Default | Description |\n|---|---|---|---|\n| `exposeDatabaseTags` | boolean | `false` | Enables or disables tag-derived metrics for the cluster. When `true`, the tag keys listed in `metricsTagKeysExposed` can be used as metric labels. When `false`, no database tag keys are exposed as labels, even if they are listed. |\n| `metricsTagKeysExposed` | array of strings | `[]` | Allowlist of database tag keys eligible to be exposed as metric labels. Keys not listed here are never exposed, even if present on a database. |\n| `maxRequestsInFlight` | integer | `2` | Maximum number of concurrent metrics scrape requests. Set to `0` to disable the limit. |\n| `keyDistributionEnabled` | boolean | `false` | When `true`, export Prometheus histogram metrics for key size and key item distributions. |\n| `keySizeBuckets` | string | `\"\"` | Comma-separated bucket boundaries for key size distribution histograms, for example `\"128M,512M\"`. |\n| `keyItemsBuckets` | string | `\"\"` | Comma-separated bucket boundaries for key item distribution histograms, for example `\"1M,8M\"`. |\n| `localStorageMaxSizeMb` | integer | `1024` | Maximum size, in MB, for local metrics storage. |\n| `localStorageRetentionDays` | integer | `8` | Number of days to retain local metrics data. |\n\nTag key allowlist entries (`metricsTagKeysExposed`) must follow these rules:\n\n- Keys are lowercased on the cluster.\n- Each key must be 1–64 characters and cannot be whitespace only.\n- Keys may contain ASCII letters, digits, spaces, and the characters `-`, `_`, `.`, `+`, `@`, and `:`.\n- Duplicate keys are rejected. The operator rejects the resource if the same key appears more than once, ignoring case.\n- A maximum of 50 keys is allowed."},{"id":"tag-a-database","title":"Tag a database","role":"content","text":"Set tags on a Redis Enterprise database (REDB) with `spec.tags`, a set of key-value pairs:\n\n[code example]\n\nThe operator merges these tags into the database's tags on the cluster. With the cluster configuration shown above, only `env` and `team` can appear as metric labels. The `app` tag is stored on the database but not exposed in metrics.\n\n`spec.tags` is optional. For details on how tags are merged, updated, and removed, see [Managed tags and deletion behavior](#managed-tags-and-deletion-behavior)."},{"id":"tag-an-active-active-database","title":"Tag an Active-Active database","role":"content","text":"For an Active-Active database (REAADB), set tags under `spec.globalConfigurations.tags`, also a set of key-value pairs:\n\n[code example]\n\nThe operator applies these tags to the Active-Active database's default configuration and to every participating cluster's instance. The cluster-level allowlist still applies. Configure `metricsConfig` on each participating REC to add the tag keys you want exposed to the allowlist."},{"id":"managed-tags-and-deletion-behavior","title":"Managed tags and deletion behavior","role":"content","text":"The operator tracks the tag keys it manages for each database. This lets it apply changes, including deletions, without disturbing tags set outside Kubernetes.\n\n- **External tags are preserved.** Tags that exist on the database but were never declared in `spec.tags` (or `spec.globalConfigurations.tags`) are left in place.\n- **The custom resource wins on collision.** If a key exists both on the database and in the custom resource, the operator applies the value from the custom resource.\n- **`status.managedTags` records what the operator owns.** After a successful sync, the operator records the tag keys it applied in `status.managedTags`. This record lets it tell a key you removed apart from a key it never managed.\n- **Removing a key deletes it.** If you remove a key that was recorded in `status.managedTags`, the operator removes that tag from the database on the next reconciliation. Keys that were never in `status.managedTags`, such as tags set only through the Redis Enterprise REST API, aren't deleted.\n- **Reserved keys are rejected.** Keys the operator reserves for its own internal use cannot be set in the spec. For REDB, these are `managed_by`, `redb_name`, `redb_namespace`, `db_service_port`, and `oss_cluster_access`. For REAADB, they also include `redb_resource`, `redb_resource_name`, `global_configurations_spec`, `replication_endpoint_port`, `rerc_name`, and the operator's internal secret-tracking keys (`redis.io/db-sec-name`, `redis.io/db-sec-latest`, `redis.io/certs-latest`, `redis.io/bu-sec-name`, and `redis.io/bu-sec-latest`). The admission controller rejects a custom resource that uses one.\n\n> [!NOTE]\n> When you upgrade to a release with this feature, `status.managedTags` starts empty for databases that already existed. The operator does not drop existing tags. It reapplies only the keys currently in the spec, so the upgrade has no effect on databases that never used tags."},{"id":"view-tagged-metrics-in-prometheus","title":"View tagged metrics in Prometheus","role":"content","text":"For instructions on connecting Prometheus to Redis Enterprise for Kubernetes, see [Export metrics to Prometheus](https://redis.io/docs/latest/operate/kubernetes/re-clusters/connect-prometheus-operator)."},{"id":"limitations","title":"Limitations","role":"limits","text":"- **Allowlist size.** The cluster allows at most 50 tag keys in `metricsTagKeysExposed`. The operator doesn't cap the list itself; the cluster enforces the limit. Keep the allowlist small: each exposed key adds a label dimension to the affected metrics, which increases cardinality in your monitoring stack.\n- **When changes take effect.** Changes to `metricsConfig`, `spec.tags`, and `spec.globalConfigurations.tags` apply on the operator's next reconciliation of the resource. No pod or cluster restart is required. Exposed tag labels appear in metrics on the next scrape after the cluster applies the change."}],"examples":[{"id":"configure-the-cluster-ex0","language":"YAML","code":"apiVersion: app.redislabs.com/v1alpha1\nkind: RedisEnterpriseCluster\nmetadata:\n  name: rec\nspec:\n  nodes: 3\n  metricsConfig:\n    exposeDatabaseTags: true\n    metricsTagKeysExposed:\n      - env\n      - team","section_id":"configure-the-cluster"},{"id":"tag-a-database-ex0","language":"YAML","code":"apiVersion: app.redislabs.com/v1alpha1\nkind: RedisEnterpriseDatabase\nmetadata:\n  name: orders-cache\nspec:\n  memorySize: 1GB\n  tags:\n    env: prod\n    team: payments\n    app: orders-service","section_id":"tag-a-database"},{"id":"tag-an-active-active-database-ex0","language":"YAML","code":"apiVersion: app.redislabs.com/v1alpha1\nkind: RedisEnterpriseActiveActiveDatabase\nmetadata:\n  name: orders-aa\nspec:\n  globalConfigurations:\n    tags:\n      env: prod\n      team: payments","section_id":"tag-an-active-active-database"}]}
{"schema_version":2,"id":"operate/kubernetes/re-databases","title":"Redis Enterprise databases (REDB)","url":"https://redis.io/docs/latest/operate/kubernetes/re-databases/","summary":"Create and manage Redis Enterprise databases (REDB) on Kubernetes using the Redis Enterprise operator.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T16:14:52+01:00","children":[{"id":"operate/kubernetes/re-databases/db-controller","summary":"This section describes how the database controller provides the ability to create, manage, and use databases via a database custom resource.","title":"Manage Redis Enterprise databases for Kubernetes","url":"https://redis.io/docs/latest/operate/kubernetes/re-databases/db-controller/"},{"id":"operate/kubernetes/re-databases/modules","summary":"Deploy Redis Enterprise databases with modules using the Redis Enterprise operator for Kubernetes.","title":"Configure modules","url":"https://redis.io/docs/latest/operate/kubernetes/re-databases/modules/"},{"id":"operate/kubernetes/re-databases/enrich-metrics-with-tags","summary":"Expose Redis Enterprise database tags as metric labels so you can filter dashboards and alerts by attributes such as environment, team, or application.","title":"Enrich database metrics with tags","url":"https://redis.io/docs/latest/operate/kubernetes/re-databases/enrich-metrics-with-tags/"},{"id":"operate/kubernetes/re-databases/replica-redb","summary":"How to create and automate database replicas using the database controller","title":"Create replica databases on Kubernetes","url":"https://redis.io/docs/latest/operate/kubernetes/re-databases/replica-redb/"}],"page_type":"content","content_hash":"dddf31528f8e3995d299bd662df1a9a42d25881945eace16cc1d7b8e7ca82d6d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"A Redis Enterprise database (REDB) is a custom Kubernetes resource that represents a Redis database running on a Redis Enterprise cluster. The Redis Enterprise operator manages REDB resources and handles database creation, configuration, scaling, and lifecycle operations.\n\nREDB resources define database specifications including memory limits, persistence settings, security configurations, networking options, and Redis modules. You can deploy databases on existing Redis Enterprise clusters (REC) and manage them by using standard Kubernetes tools and workflows."},{"id":"database-management","title":"Database management","role":"content","text":"Create and manage Redis Enterprise databases on your cluster:\n\n- [Database controller](https://redis.io/docs/latest/operate/kubernetes/re-databases/db-controller) - Understand how the database controller manages REDB resources and database lifecycle"},{"id":"replication-and-high-availability","title":"Replication and high availability","role":"content","text":"Set up database replication for high availability and disaster recovery:\n\n- [Create replica databases](https://redis.io/docs/latest/operate/kubernetes/re-databases/replica-redb) - Configure replica databases for read scaling and disaster recovery scenarios"},{"id":"advanced-database-configurations","title":"Advanced database configurations","role":"content","text":"Explore advanced database features and configurations:\n\n- [Active-Active databases](https://redis.io/docs/latest/operate/kubernetes/active-active) - Set up globally distributed Active-Active databases across multiple Kubernetes clusters"},{"id":"database-connectivity","title":"Database connectivity","role":"content","text":"Connect applications to your Redis Enterprise databases:\n\n- [Database connectivity](https://redis.io/docs/latest/operate/kubernetes/networking/database-connectivity) - Comprehensive guide to in-cluster and external database access, service discovery, and credentials management.\n- [Manage REDB passwords](https://redis.io/docs/latest/operate/kubernetes/security/authentication/manage-redb-credentials) - Retrieve and rotate Redis Enterprise database (REDB) passwords.\n- [Networking](https://redis.io/docs/latest/operate/kubernetes/networking) - Configure ingress, routes, and service exposure for database access\n- [Security](https://redis.io/docs/latest/operate/kubernetes/security) - Set up TLS, authentication, and access control for secure database connections"},{"id":"monitoring-and-troubleshooting","title":"Monitoring and troubleshooting","role":"content","text":"Monitor database performance and troubleshoot issues:\n\n- [Logs](https://redis.io/docs/latest/operate/kubernetes/logs) - Collect and analyze database logs for troubleshooting\n- [Connect to Prometheus operator](https://redis.io/docs/latest/operate/kubernetes/re-clusters/connect-prometheus-operator) - Monitor database metrics with Prometheus"},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Redis Enterprise clusters (REC)](https://redis.io/docs/latest/operate/kubernetes/re-clusters) - Manage the underlying cluster infrastructure\n- [REDB API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api) - Complete API specification for REDB resources\n- [Active-Active database API](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_active_active_database_api) - API reference for Active-Active databases\n- [Remote cluster API](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_remote_cluster_api) - API reference for remote cluster configurations"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/re-databases/modules","title":"Configure modules","url":"https://redis.io/docs/latest/operate/kubernetes/re-databases/modules/","summary":"Deploy Redis Enterprise databases with modules using the Redis Enterprise operator for Kubernetes.","tags":["docs","operate","kubernetes"],"last_updated":"2026-10-13T20:31:34+05:30","page_type":"content","content_hash":"7da10e16df6a8e1e0a9dfd472695f8701fd6e5954486f5f66ad867d71e59a50b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise modules extend Redis functionality with additional data types, commands, and capabilities. Redis Enterprise versions 8.0.2 and later include several bundled modules that are automatically enabled for compatible database types. You can also add user-defined modules for additional functionality."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before you begin, verify the following:\n\n- [Redis Enterprise operator deployed](https://redis.io/docs/latest/operate/kubernetes/deployment/quick-start) in your Kubernetes cluster\n- [Redis Enterprise cluster (REC)](https://redis.io/docs/latest/operate/kubernetes/re-clusters) running and in a healthy state"},{"id":"bundled-modules","title":"Bundled modules","role":"content","text":"Redis Enterprise includes several bundled modules that extend Redis functionality with additional data types, commands, and capabilities. Starting with Redis Enterprise version 8.0.2, these modules are automatically included and immediately available for use."},{"id":"available-bundled-modules","title":"Available bundled modules","role":"content","text":"| Module | Name | Description | Capabilities |\n|--------|------|-------------|--------------|\n| **[RediSearch](https://redis.io/docs/latest/develop/ai/search-and-query)** | `search` | Full-text search and secondary indexing | Query, aggregation, full-text search, vector similarity search |\n| **[RedisJSON](https://redis.io/docs/latest/develop/data-types/json)** | `ReJSON` | JSON data type support | Store, update, and query JSON documents |\n| **[RedisTimeSeries](https://redis.io/docs/latest/develop/data-types/timeseries)** | `timeseries` | Time series data structures | Ingest and query time series data with downsampling and aggregation |\n| **[RedisBloom](https://redis.io/docs/latest/develop/data-types/probabilistic)** | `bf` | Probabilistic data structures | Bloom filters, Cuckoo filters, Count-Min Sketch, Top-K |\n\n> [!NOTE]\n> When configuring databases with modules, use the `NAME` field (for example, `search` or `ReJSON`) instead of the `DISPLAY_NAME` field."},{"id":"automatic-enablement-in-redis-8-and-later","title":"Automatic enablement in Redis 8 and later","role":"content","text":"For databases created with or upgraded to Redis version 8 or later, bundled modules are automatically enabled based on the database type. You don't need to specify them in the `spec.moduleList` field unless you want to use a specific version.\n\n| Database type | Automatically enabled capabilities |\n|---------------|------------------------------------|\n| RAM-only | [Redis Search](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search)<br />[JSON](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json)<br />[Time series](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/timeseries)<br />[Probabilistic](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/bloom)  |\n| Flash-enabled ([Redis Flex](https://redis.io/docs/latest/operate/rs/databases/flash)) | [JSON](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json)<br />[Probabilistic](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/bloom) |\n| [Active-Active](https://redis.io/docs/latest/operate/rs/databases/active-active)<sup>[1](#enabled-modules-table-note-1)</sup> | [Redis Search](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/search-active-active)<br />[JSON](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json) |\n\n1. <a name=\"enabled-modules-table-note-1\"></a>Upgrading existing Active-Active databases to Redis version 8 does not automatically enable these capabilities. Only new Active-Active databases created with Redis version 8 enable these capabilities by default.\n\nFor databases using Redis versions earlier than 8, explicitly specify bundled modules in the `spec.moduleList` field when you create the database."},{"id":"check-available-module-versions","title":"Check available module versions","role":"content","text":"To see which bundled module versions are available in your cluster, run the following command:\n\n[code example]\n\nThis command displays all modules (both bundled and user-defined) installed in the cluster and their available versions."},{"id":"bundled-vs-user-defined-modules","title":"Bundled vs. user-defined modules","role":"content","text":"The following table shows the key differences between bundled and user-defined modules:\n\n| Aspect | Bundled modules | User-defined modules |\n|--------|----------------|---------------------|\n| **Installation** | Pre-installed with Redis Enterprise | Must be added to the REC spec |\n| **Availability** | Immediately available | Available after you add them to the REC |\n| **Versions** | Bundled with the Redis Enterprise version | Specified by URL in the REC spec |\n| **Examples** | RediSearch, RedisJSON, RedisTimeSeries, RedisBloom | RedisGears, custom modules |\n| **Redis 8 and later behavior** | Automatically enabled for compatible database types | Must be explicitly specified |"},{"id":"user-defined-modules","title":"User-defined modules","role":"content","text":"User-defined modules are custom Redis modules that extend Redis functionality beyond the bundled modules. User-defined modules can include third-party modules like RedisGears or custom in-house modules developed for specific use cases.\n\n**Limitations:**\n\n- **Active-Active databases**: User-defined modules are not supported with Active-Active databases. Only bundled modules (RediSearch, RedisJSON, RedisTimeSeries, RedisBloom) can be used with Active-Active databases.\n\n- **Redis on Flash**: User-defined modules are fully supported with Redis on Flash databases."},{"id":"add-user-defined-modules-to-the-rec","title":"Add user-defined modules to the REC","role":"content","text":"To use user-defined modules with your databases, first add them to the Redis Enterprise cluster (REC) custom resource. This enables the operator to validate the modules and make them available for database creation.\n\n> [!WARNING]\n> Add user-defined modules to the REC **before** you create any databases that use them. The admission controller validates that modules exist in the REC before allowing REDB creation.\n\n1. Edit your REC custom resource:\n\n    [code example]\n\n2. Add the `userDefinedModules` section to the `spec`:\n\n    [code example]\n\n3. If your module repository requires authentication, create a secret with your credentials:\n\n    [code example]\n\nIf you use [HashiCorp Vault integration](https://redis.io/docs/latest/operate/kubernetes/security/vault), store the module credentials in Vault instead of creating a Kubernetes secret. The `credentialsSecret` field will reference the secret name in Vault."},{"id":"module-naming-requirements","title":"Module naming requirements","role":"content","text":"The `name` field in the REC spec must match either the `module_name` or `display_name` value from the module's manifest file (`module.json` inside the module zip file). This enables the operator to validate the module.\n\nFor example, if your module manifest contains the following:\n\n[code example]\n\nYou can use either `\"rg\"` or `\"RedisGears\"` as the `name` value in your REC spec.\n\n> [!NOTE]\n> If the names don't match, the operator can't validate the module. This can lead to preventable errors during database creation or upgrades."},{"id":"edit-user-defined-modules","title":"Edit user-defined modules","role":"content","text":"To modify the user-defined modules list, complete the following steps:\n\n1. Edit the REC custom resource:\n\n    [code example]\n\n1. Update the `userDefinedModules` section:\n   - **Add new modules**: Append them to the list.\n   - **Update module URLs**: Change the `url` field for existing modules.\n   - **Update credentials**: Change the `credentialsSecret` reference.\n\n1. Save your changes. The operator validates and applies the updates.\n\n> [!WARNING]\n> Don't remove modules that are currently in use by any database. The operator rejects the change and puts the REC into an error state.\n\n> [!NOTE]\n> Changes to the `userDefinedModules` list trigger a rolling restart of the Redis Enterprise cluster pods. Plan module updates during a maintenance window to minimize potential impact on your databases."},{"id":"verify-user-defined-modules","title":"Verify user-defined modules","role":"content","text":"After you add user-defined modules to the REC, verify that they're available:\n\n[code example]\n\nYou can also check the REC status for validation errors:\n\n[code example]\n\nLook for events or status messages related to module validation in the output."},{"id":"upgrade-with-modules","title":"Upgrade with modules","role":"content","text":"The upgrade process differs depending on whether you use bundled modules or user-defined modules."},{"id":"module-version-selection","title":"Module version selection","role":"content","text":"When multiple versions of a module are available in the cluster, Redis Enterprise selects the appropriate version based on the `compatible_redis_version` field in the module's manifest file (`module.json`). This field must match the Redis OSS version that the database is using.\n\nFor example, if your database uses Redis 7.2, Redis Enterprise selects the module version whose `compatible_redis_version` is `7.2`. If no matching version is found, the module cannot be loaded."},{"id":"upgrade-with-bundled-modules","title":"Upgrade with bundled modules","role":"content","text":"For databases using bundled modules (RediSearch, RedisJSON, RedisTimeSeries, RedisBloom):\n\n- **Redis 8 and later**: Bundled modules are automatically enabled and upgraded when you upgrade the database to Redis version 8 or later. You don't need to take any additional action. The module version is automatically selected based on the database's Redis version.\n\n- **Redis versions earlier than 8**: Bundled modules are upgraded automatically when you upgrade the Redis Enterprise cluster. The bundled module versions are tied to the Redis Enterprise version, and the appropriate version is selected based on the database's Redis version."},{"id":"upgrade-with-user-defined-modules","title":"Upgrade with user-defined modules","role":"content","text":"For databases using user-defined modules, you must take additional steps during cluster upgrades:\n\n1. Set `spec.upgradeSpec.autoUpgradeRedisEnterprise` to `false` in your REC before upgrading.\n\n1. Add or update the `userDefinedModules` list in the REC spec with the new module versions before or during the cluster upgrade. Ensure that the new module versions include a `compatible_redis_version` field that matches the Redis version your databases will use after the upgrade.\n\n1. After the cluster upgrade completes, you can re-enable `autoUpgradeRedisEnterprise` if desired.\n\nFor detailed upgrade instructions, see the following:\n\n- [Upgrade a Redis Enterprise cluster (REC)](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster)\n- [Upgrade Redis Enterprise on OpenShift](https://redis.io/docs/latest/operate/kubernetes/upgrade/openshift-cli)"},{"id":"troubleshooting","title":"Troubleshooting","role":"errors","text":"This section covers common issues you might encounter when working with user-defined modules."},{"id":"module-validation-errors","title":"Module validation errors","role":"content","text":"Module validation errors occur when the operator can't validate a user-defined module. Common causes include incorrect URLs, authentication failures, or invalid module manifests.\n\n**Symptoms:**\n\n- REC status shows validation errors\n- Events indicate module download or validation failures\n- Databases fail to create with module-related errors\n\n**Diagnosis:**\n\nCheck the REC status for validation errors:\n\n[code example]\n\nLook for error messages related to modules in the Events section.\n\n**Resolution:**\n\n1. **Verify the module URL is accessible:**\n\n    [code example]\n\n2. **Check credentials secret exists and has correct values:**\n\n    [code example]\n\n3. **Verify the module manifest (`module.json`) is valid:**\n\n    Download the module zip file and check that it contains a valid `module.json` file with required fields: `module_name`, `display_name`, `semantic_version`, `commands`, and `compatible_redis_version`.\n\n4. **Ensure the `name` field in the REC spec matches the module manifest:**\n\n    The `name` must match either `module_name` or `display_name` from the module's `module.json` file. See [Module naming requirements](#module-naming-requirements) for details."},{"id":"bootstrap-failures","title":"Bootstrap failures","role":"content","text":"Bootstrap failures occur when the Redis Enterprise cluster fails to start due to module-related issues.\n\n**Symptoms:**\n\n- REC pods fail to reach Running state\n- Operator logs show module-related errors during bootstrap\n- Cluster remains in a non-ready state\n\n**Diagnosis:**\n\nCheck the operator logs:\n\n[code example]\n\nCheck the REC pod logs:\n\n[code example]\n\n**Resolution:**\n\n1. **Remove problematic modules from the REC spec:**\n\n    Edit the REC and remove or comment out the problematic module from the `userDefinedModules` list:\n\n    [code example]\n\n2. **Wait for the cluster to recover:**\n\n    [code example]\n\n3. **Fix the module configuration and re-add it:**\n\n    After the cluster is running, correct the module URL, credentials, or manifest issues, then add the module back to the REC spec."},{"id":"module-not-found-errors","title":"Module not found errors","role":"content","text":"Module not found errors occur when you try to create a database that uses a module that isn't defined in the REC.\n\n**Symptoms:**\n\n- REDB creation fails with admission webhook errors\n- Error message indicates the module is not found in the REC\n- Database remains in a pending or failed state\n\n**Diagnosis:**\n\nCheck the REDB creation error:\n\n[code example]\n\nVerify which modules are defined in the REC:\n\n[code example]\n\n**Resolution:**\n\n1. **Add the missing module to the REC:**\n\n    See [Add user-defined modules to the REC](#add-user-defined-modules-to-the-rec) for detailed instructions.\n\n2. **Wait for the module to be validated:**\n\n    [code example]\n\n    Look for successful validation in the Events section.\n\n3. **Retry database creation:**\n\n    After the module is available in the REC, the database creation should succeed automatically, or you can delete and recreate the REDB."},{"id":"related-information","title":"Related information","role":"related","text":"- [Redis modules documentation](https://redis.io/docs/latest/develop/reference/modules) - Official Redis modules documentation\n- [REDB API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api) - Complete API specification for REDB resources\n- [REAADB API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_active_active_database_api) - API reference for Active-Active databases"},{"id":"redis-software-documentation","title":"Redis Software documentation","role":"content","text":"- [Add modules to a cluster](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/add-module-to-cluster) - Install module packages on Redis Enterprise Software clusters\n- [Enable modules for a database](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/add-module-to-database) - Add modules to databases in Redis Enterprise Software\n- [Upgrade modules](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/upgrade-module) - Upgrade module versions in Redis Enterprise Software\n- [Module lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules-lifecycle) - Module versioning and end-of-life schedule"}],"examples":[{"id":"check-available-module-versions-ex0","language":"bash","code":"kubectl get rec <cluster-name> -o jsonpath='{.status.modules}' | jq","section_id":"check-available-module-versions"},{"id":"add-user-defined-modules-to-the-rec-ex0","language":"sh","code":"kubectl edit rec <cluster-name>","section_id":"add-user-defined-modules-to-the-rec"},{"id":"add-user-defined-modules-to-the-rec-ex1","language":"yaml","code":"spec:\n      userDefinedModules:\n        - name: \"custom-module\"\n          source:\n            https:\n              url: \"https://modules.company.com/custom-module-v1.0.zip\"\n              credentialsSecret: \"module-repo-creds\"","section_id":"add-user-defined-modules-to-the-rec"},{"id":"add-user-defined-modules-to-the-rec-ex2","language":"sh","code":"kubectl create secret generic module-repo-creds \\\n      --from-literal=username=<your-username> \\\n      --from-literal=password=<your-password>","section_id":"add-user-defined-modules-to-the-rec"},{"id":"module-naming-requirements-ex0","language":"json","code":"{\n  \"module_name\": \"rg\",\n  \"display_name\": \"RedisGears\",\n  \"semantic_version\": \"1.2.5\"\n}","section_id":"module-naming-requirements"},{"id":"edit-user-defined-modules-ex0","language":"sh","code":"kubectl edit rec <cluster-name>","section_id":"edit-user-defined-modules"},{"id":"verify-user-defined-modules-ex0","language":"sh","code":"kubectl get rec <cluster-name> -o jsonpath='{.spec.userDefinedModules}' | jq","section_id":"verify-user-defined-modules"},{"id":"verify-user-defined-modules-ex1","language":"sh","code":"kubectl describe rec <cluster-name>","section_id":"verify-user-defined-modules"},{"id":"module-validation-errors-ex0","language":"sh","code":"kubectl describe rec <cluster-name>","section_id":"module-validation-errors"},{"id":"module-validation-errors-ex1","language":"sh","code":"curl -I <module-url>","section_id":"module-validation-errors"},{"id":"module-validation-errors-ex2","language":"sh","code":"kubectl get secret <credentials-secret-name> -o yaml","section_id":"module-validation-errors"},{"id":"bootstrap-failures-ex0","language":"sh","code":"kubectl logs -l name=redis-enterprise-operator -n <namespace>","section_id":"bootstrap-failures"},{"id":"bootstrap-failures-ex1","language":"sh","code":"kubectl logs <rec-pod-name> -n <namespace>","section_id":"bootstrap-failures"},{"id":"bootstrap-failures-ex2","language":"sh","code":"kubectl edit rec <cluster-name>","section_id":"bootstrap-failures"},{"id":"bootstrap-failures-ex3","language":"sh","code":"kubectl get rec <cluster-name> -w","section_id":"bootstrap-failures"},{"id":"module-not-found-errors-ex0","language":"sh","code":"kubectl describe redb <database-name>","section_id":"module-not-found-errors"},{"id":"module-not-found-errors-ex1","language":"sh","code":"kubectl get rec <cluster-name> -o jsonpath='{.spec.userDefinedModules}' | jq","section_id":"module-not-found-errors"},{"id":"module-not-found-errors-ex2","language":"sh","code":"kubectl describe rec <cluster-name>","section_id":"module-not-found-errors"}]}
{"schema_version":2,"id":"operate/kubernetes/re-databases/replica-redb","title":"Create replica databases on Kubernetes","url":"https://redis.io/docs/latest/operate/kubernetes/re-databases/replica-redb/","summary":"How to create and automate database replicas using the database controller","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T16:14:52+01:00","page_type":"content","content_hash":"595209275f55dff40cc54fdd89999eb3b78797291f4bbb1b3828467269c4db4e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can configure a replica of a database by creating an item in\nthe [`replicaSources`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api#specreplicasources) section of the RedisEnterpriseDatabase (REDB) custom resource.\n\n> [!NOTE]\n> If the source cluster is reached through an ingress route (for example, an OpenShift route or any TLS pass-through that relies on SNI), set [`tlsSniName`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api#specreplicasources) on each `replicaSources` item to the SNI hostname configured on the ingress. Without it, the syncer cannot route TLS to the source database's proxy and replication fails.\n\nA secret must be created with the `stringData` section containing the replica source URI as follows:\n\nCreate a secret with the replica source URI listed in the `stringData` field as follows:\n\n[code example]\n\nThe replica source URL can be retrieved by going to \"UI > database > configuration > Press the button Get Replica of source URL\"\nin the administrative UI. But, this information can also be retrieved directly from\nthe REST API as well.\n\nA replica of database CR simply uses the secret in the `replicaSources` section:\n\n[code example]\n\nIn the above, `name-of-replica` database will be created as a replica of the\nsource database as long as the source database exists on the source cluster\nand the secret contains the correct replica source URL for that database."},{"id":"retrieving-the-replica-source-url-via-kubectl","title":"Retrieving the replica source URL via kubectl","role":"content","text":"You will need `kubectl`, `curl`, and `jq` installed for this procedure.\n\n1. Set your metadata:\n\n   [code example]\n\n1. Retrieve the cluster authentication:\n\n   [code example]\n\n1. Forward the port of the REST API service for your source cluster:\n\n   [code example]\n\n1. Request the information from the REST API:\n\n   [code example]\n\n   Note: URI now contains the replica source URI.\n\n1. Construct the secret for the replica:\n\n   [code example]\n\n1. Create the replica database:\n\n   [code example]"},{"id":"automating-the-creation-via-a-job","title":"Automating the creation via a job","role":"content","text":"The following procedure uses a ConfigMap and a Job to construct the replica\nsource URL secret from the source database and configure the target database.\n\nThere are four parameters:\n\n- `source` - the name of the source database\n- `cluster` - the name of the cluster for the source database\n- `target` - the name of the target database\n- `targetCluster` - the name of the cluster for the target database\n\nThese parameters can be set by:\n\n[code example]\n\nwhere \"name-of-...\" is replaced with the database source, source cluster,\ndatabase target, and target cluster names.\n\nThe Job and ConfigMap below, when submitted, will create the secret and\nreplica database:\n\n[code example]"}],"examples":[{"id":"overview-ex0","language":"yaml","code":"apiVersion: v1\nkind: Secret\nmetadata:\n   name: my-replica-source\nstringData:\n   uri: <replica-source-uri-goes-here>","section_id":"overview"},{"id":"overview-ex1","language":"yaml","code":"apiVersion: app.redislabs.com/v1alpha1\nkind: RedisEnterpriseDatabase\nmetadata:\n   name: name-of-replica\nspec:\n   redisEnterpriseCluster:\n      name: name-of-cluster\n   replicaSources:\n   - replicaSourceType: SECRET\n     replicaSourceName: my-replica-source\n     # Required when the source cluster is reached via an ingress route\n     # tlsSniName: source-db.example.com","section_id":"overview"},{"id":"retrieving-the-replica-source-url-via-kubectl-ex0","language":"js","code":"CLUSTER_NAME=test\n   SOURCE_DB=db1\n   TARGET_DB=db2\n   TARGET_CLUSTER_NAME=test","section_id":"retrieving-the-replica-source-url-via-kubectl"},{"id":"retrieving-the-replica-source-url-via-kubectl-ex1","language":"js","code":"CLUSTER_USER=`kubectl get secret/${CLUSTER_NAME} -o json | jq -r .data.username | base64 -d`\n   CLUSTER_PASSWORD=`kubectl get secret/${CLUSTER_NAME} -o json | jq -r .data.password | base64 -d`","section_id":"retrieving-the-replica-source-url-via-kubectl"},{"id":"retrieving-the-replica-source-url-via-kubectl-ex2","language":"sh","code":"kubectl port-forward pod/${CLUSTER_NAME}-0 9443","section_id":"retrieving-the-replica-source-url-via-kubectl"},{"id":"retrieving-the-replica-source-url-via-kubectl-ex3","language":"js","code":"JQ='.[] | select(.name==\"'\n   JQ+=\"${SOURCE_DB}\"\n   JQ+='\") | (\"redis://admin:\" + .authentication_admin_pass + \"@\"+.name+\":\"+(.endpoints[0].port|tostring))'\n   URI=`curl -sf -k -u \"$CLUSTER_USER:$CLUSTER_PASSWORD\" \"https://localhost:9443/v1/bdbs?fields=uid,name,endpoints,authentication_admin_pass\" | jq \"$JQ\" | sed 's/\"//g'`","section_id":"retrieving-the-replica-source-url-via-kubectl"},{"id":"retrieving-the-replica-source-url-via-kubectl-ex4","language":"yaml","code":"cat << EOF > secret.yaml\n   apiVersion: v1\n   kind: Secret\n   metadata:\n     name: ${SOURCE_DB}-url\n   stringData:\n     uri: ${URI}\n   EOF\n   kubectl apply -f secret.yaml","section_id":"retrieving-the-replica-source-url-via-kubectl"},{"id":"retrieving-the-replica-source-url-via-kubectl-ex5","language":"yaml","code":"cat << EOF > target.yaml\n   apiVersion: app.redislabs.com/v1alpha1\n   kind: RedisEnterpriseDatabase\n   metadata:\n     name: ${TARGET_DB}\n   spec:\n     redisEnterpriseCluster:\n       name: ${TARGET_CLUSTER_NAME}\n     replicaSources:\n     - replicaSourceType: SECRET\n       replicaSourceName: ${SOURCE_DB}-url\n       # Required when the source cluster is reached via an ingress route\n       # tlsSniName: source-db.example.com\n   EOF\n   kubectl apply -f target.yaml","section_id":"retrieving-the-replica-source-url-via-kubectl"},{"id":"automating-the-creation-via-a-job-ex0","language":"sh","code":"kubectl create configmap replica-of-database-parameters \\\n--from-literal=source=name-of-source \\\n--from-literal=cluster=name-of-cluster \\\n--from-literal=target=name-of-target \\\n--from-literal=targetCluster=name-of-cluster","section_id":"automating-the-creation-via-a-job"},{"id":"automating-the-creation-via-a-job-ex1","language":"yaml","code":"apiVersion: batch/v1\nkind: Job\nmetadata:\n  name: replica-of-database\nspec:\n  backoffLimit: 4\n  template:\n    spec:\n      serviceAccountName: redis-enterprise-operator\n      restartPolicy: Never\n      volumes:\n        - name: scripts\n          configMap:\n            name: replica-of-database\n      containers:\n        - name: createdb\n          image: debian:stable-slim\n          env:\n            - name: MY_NAMESPACE\n              valueFrom:\n                fieldRef:\n                  fieldPath: metadata.namespace\n            - name: SCRIPT\n              value: create.sh\n            - name: SOURCE_DB\n              valueFrom:\n                configMapKeyRef:\n                  name: replica-of-database-parameters\n                  key: source\n            - name: TARGET_DB\n              valueFrom:\n                configMapKeyRef:\n                  name: replica-of-database-parameters\n                  key: target\n            - name: CLUSTER_SERVICE\n              value: .svc.cluster.local\n            - name: CLUSTER_NAME\n              valueFrom:\n                configMapKeyRef:\n                  name: replica-of-database-parameters\n                  key: cluster\n            - name: CLUSTER_PORT\n              value: \"9443\"\n            - name: TARGET_CLUSTER_NAME\n              valueFrom:\n                configMapKeyRef:\n                  name: replica-of-database-parameters\n                  key: targetCluster\n          volumeMounts:\n            - mountPath: /opt/scripts/\n              name: scripts\n          command:\n            - /bin/bash\n            - -c\n            - |\n              apt-get update; apt-get install -y curl jq apt-transport-https gnupg2\n              apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 6A030B21BA07F4FB\n              curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -\n              echo \"deb https://apt.kubernetes.io/ kubernetes-xenial main\" | tee -a /etc/apt/sources.list.d/kubernetes.list\n              apt-get update\n              apt-get install -y kubectl\n              bash /opt/scripts/$SCRIPT\n---\napiVersion: v1\nkind: ConfigMap\nmetadata:\n  name: replica-of-database\ndata:\n  create.sh: |\n    CLUSTER_USER=`kubectl get secret/${CLUSTER_NAME} -o json | jq -r .data.username | base64 -d`\n    CLUSTER_PASSWORD=`kubectl get secret/${CLUSTER_NAME} -o json | jq -r .data.password | base64 -d`\n    CLUSTER_HOST=${CLUSTER_NAME}.${MY_NAMESPACE}${CLUSTER_SERVICE}\n    JQ='.[] | select(.name==\"'\n    JQ+=\"${SOURCE_DB}\"\n    JQ+='\") | (\"redis://admin:\" +  .authentication_admin_pass + \"@\"+.endpoints[0].dns_name+\":\"+(.endpoints[0].port|tostring))'\n    URI=`curl -sf -k -u \"$CLUSTER_USER:$CLUSTER_PASSWORD\" \"https://${CLUSTER_HOST}:${CLUSTER_PORT}/v1/bdbs?fields=uid,name,endpoints,authentication_admin_pass\" | jq \"$JQ\" | sed 's/\"//g'`\n    echo \"URL: ${URL}\"\n    echo \"\"\n    cat << EOF > /tmp/secret.yaml\n    apiVersion: v1\n    kind: Secret\n    metadata:\n      name: ${SOURCE_DB}-url\n    stringData:\n      uri: ${URI}\n    EOF\n    cat /tmp/secret.yaml\n    cat << EOF > /tmp/target.yaml\n    apiVersion: app.redislabs.com/v1alpha1\n    kind: RedisEnterpriseDatabase\n    metadata:\n      name: ${TARGET_DB}\n    spec:\n      redisEnterpriseCluster:\n        name: ${TARGET_CLUSTER_NAME}\n      replicaSources:\n      - replicaSourceType: SECRET\n        replicaSourceName: ${SOURCE_DB}-url\n        # Required when the source cluster is reached via an ingress route\n        # tlsSniName: source-db.example.com\n    EOF\n    echo \"---\"\n    cat /tmp/target.yaml\n    echo \"\"\n    kubectl -n ${MY_NAMESPACE} apply -f /tmp/secret.yaml\n    kubectl -n ${MY_NAMESPACE} apply -f /tmp/target.yaml","section_id":"automating-the-creation-via-a-job"}]}
{"schema_version":2,"id":"operate/kubernetes/recommendations","title":"Configuration recommendations","url":"https://redis.io/docs/latest/operate/kubernetes/recommendations/","summary":"Best practices and configuration recommendations for Redis Enterprise on Kubernetes deployments.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T16:14:52+01:00","children":[{"id":"operate/kubernetes/recommendations/node-resources","summary":"Recommendations on managing node resources and setting eviction thresholds with Redis Enterprise for Kubernetes.","title":"Manage node resources","url":"https://redis.io/docs/latest/operate/kubernetes/recommendations/node-resources/"},{"id":"operate/kubernetes/recommendations/persistent-volumes","summary":"This section covers details about how persistent volumes are sized and specified for Redis Enterprise cluster deployments.","title":"Use persistent volumes in Redis Enterprise clusters","url":"https://redis.io/docs/latest/operate/kubernetes/recommendations/persistent-volumes/"},{"id":"operate/kubernetes/recommendations/sizing-on-kubernetes","summary":"This section provides information about sizing and scaling Redis Enterprise in a Kubernetes deployment.","title":"Size and scale a Redis Enterprise cluster deployment on Kubernetes","url":"https://redis.io/docs/latest/operate/kubernetes/recommendations/sizing-on-kubernetes/"},{"id":"operate/kubernetes/recommendations/node-selection","summary":"This section provides information about how Redis Enterprise cluster pods can be scheduled to only be placed on specific nodes or node pools.","title":"Control node selection","url":"https://redis.io/docs/latest/operate/kubernetes/recommendations/node-selection/"},{"id":"operate/kubernetes/recommendations/pod-stability","summary":"This section provides information about how you can use quality of service, priority class, eviction thresholds and resource monitoring to maintain cluster node pod stability.","title":"Manage pod stability","url":"https://redis.io/docs/latest/operate/kubernetes/recommendations/pod-stability/"}],"page_type":"content","content_hash":"2c25c34d8554875ce690e300f65da8f8348b1da566a7e7fecc50134b7d921357","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Follow these best practices and configuration recommendations to optimize your Redis Enterprise deployment on Kubernetes for performance, reliability, and scalability."},{"id":"infrastructure-recommendations","title":"Infrastructure recommendations","role":"content","text":"Configure your Kubernetes infrastructure for optimal Redis Enterprise performance:\n\n- [Node resources](https://redis.io/docs/latest/operate/kubernetes/recommendations/node-resources) - CPU, memory, and resource allocation recommendations\n- [Node selection](https://redis.io/docs/latest/operate/kubernetes/recommendations/node-selection) - Best practices for selecting and configuring Kubernetes nodes\n- [Persistent volumes](https://redis.io/docs/latest/operate/kubernetes/recommendations/persistent-volumes) - Storage configuration and persistent volume recommendations"},{"id":"deployment-recommendations","title":"Deployment recommendations","role":"content","text":"Optimize your Redis Enterprise deployment configuration:\n\n- [Sizing on Kubernetes](https://redis.io/docs/latest/operate/kubernetes/recommendations/sizing-on-kubernetes) - Guidelines for sizing clusters and databases\n- [Pod stability](https://redis.io/docs/latest/operate/kubernetes/recommendations/pod-stability) - Ensure stable pod operations and prevent disruptions"},{"id":"performance-optimization","title":"Performance optimization","role":"performance","text":"Configure your deployment for optimal performance and reliability based on your workload requirements and infrastructure capabilities."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/recommendations/node-resources","title":"Manage node resources","url":"https://redis.io/docs/latest/operate/kubernetes/recommendations/node-resources/","summary":"Recommendations on managing node resources and setting eviction thresholds with Redis Enterprise for Kubernetes.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-29T08:55:30-05:00","page_type":"content","content_hash":"9d6d683a59fc340cc24de3f6ad53120f08472bfe2e13f9a155d32da967451fc3","sections":[{"id":"node-eviction-thresholds","title":"Node eviction thresholds","role":"overview","text":"Kubernetes uses [node-pressure eviction](https://kubernetes.io/docs/concepts/scheduling-eviction/node-pressure-eviction/) to terminate pods and free up node resources. Redis Enterprise for Kubernetes works best when you use the recommendations below.\n\nEviction thresholds are managed by [kubelet arguments](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/).\n\nRedis recommends setting a high [soft eviction threshold](https://kubernetes.io/docs/concepts/scheduling-eviction/node-pressure-eviction/#soft-eviction-thresholds). This changes the node condition early enough to alert the administrator.\n\nWe also recommend setting the [`eviction-soft-grace-period`](https://kubernetes.io/docs/concepts/scheduling-eviction/node-pressure-eviction/#soft-eviction-thresholds) high enough for the administrator to scale the cluster.\n\nFor more information about configuring eviction thresholds on specific platforms, see [Freeing node resources](https://docs.openshift.com/container-platform/4.9/nodes/nodes/nodes-nodes-garbage-collection.html) for OpenShift or [Cluster architecture](https://cloud.google.com/kubernetes-engine/docs/concepts/cluster-architecture#node_allocatable) for GKE."},{"id":"monitor-node-conditions","title":"Monitor node conditions","role":"content","text":"Redis recommends monitoring the node conditions. If both `MemoryPressure` and `DiskPressure` are true, the eviction threshold was met.\n\n[code example]\n\nFor more information about monitoring node conditions, see [Node conditions](https://kubernetes.io/docs/concepts/scheduling-eviction/node-pressure-eviction/#node-conditions) on [kubernetes.io](https://kubernetes.io/docs/home/)."},{"id":"transparent-huge-pages","title":"Transparent Huge Pages","role":"content","text":"Redis recommends disabling Transparent Huge Pages (THP) on every Kubernetes node that hosts Redis Software pods. THP causes a latency penalty and increases memory usage, and Redis logs a warning at startup when it is enabled.\n\nThe operator does not manage this setting. Kubernetes has no API object for kernel tunables, so you configure THP on the nodes themselves.\n\nCheck the current setting on your nodes and disable THP where it is enabled. For instructions, see your platform's documentation. For background on how THP affects Redis, see [Latency induced by transparent huge pages](https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/latency#latency-induced-by-transparent-huge-pages)."},{"id":"resource-quotas","title":"Resource quotas","role":"content","text":"Kubernetes uses the `ResourceQuota` object to limit resource consumption per namespace. This lets you limit the number of objects created by a namespace or the amount of compute resources consumed by a namespace. \n\nThe resource settings for Redis Enterprise for Kubernetes are defined in the `operator.yaml` and the [`RedisEnterpriseCluster`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api#spec) custom resource.\n\nThe following settings are the minimum workloads for the operator to function.\n\n[code example]\nFor more details on using resource quotas, see the [Kubernetes documentation](https://kubernetes.io/docs/concepts/policy/resource-quotas/)."}],"examples":[{"id":"monitor-node-conditions-ex0","language":"sh","code":"> kubectl get nodes -o jsonpath='{range .items[*]}name:{.metadata.name}{\"\\t\"}MemoryPressure:{.status.conditions[?(@.type == \"MemoryPressure\")].status}{\"\\t\"}DiskPressure:{.status.conditions[?(@.type == \"DiskPressure\")].status}{\"\\n\"}{end}'\nname:gke-55d1ac88-213c\tMemoryPressure:False\tDiskPressure:False\nname:gke-55d1ac88-vrpp\tMemoryPressure:False\tDiskPressure:False\nname:gke-7253cc19-42g0\tMemoryPressure:False\tDiskPressure:False","section_id":"monitor-node-conditions"},{"id":"resource-quotas-ex0","language":"yaml","code":"resources:\n    limits:\n      cpu: 0.5\n      memory: 256Mi\n    requests:\n      cpu: 0.5\n      memory: 256Mi","section_id":"resource-quotas"}]}
{"schema_version":2,"id":"operate/kubernetes/recommendations/node-selection","title":"Control node selection","url":"https://redis.io/docs/latest/operate/kubernetes/recommendations/node-selection/","summary":"This section provides information about how Redis Enterprise cluster pods can be scheduled to only be placed on specific nodes or node pools.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T16:14:52+01:00","page_type":"content","content_hash":"a3140d97a04a88518f31a58e9ee9a89021448cabbb0728306124a84a9fddcb07","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Kubernetes clusters often include nodes with different CPU and memory profiles. You control where Redis Enterprise cluster (REC) pods run by setting fields in the REC custom resource (CRD).\n\nA Redis Enterprise cluster (REC) runs as a StatefulSet. The Kubernetes scheduler assigns nodes when you create or resize the cluster, or when a pod restarts.\n\nUse these options to control pod placement:"},{"id":"use-node-selectors","title":"Use node selectors","role":"content","text":"The [`nodeSelector`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api#spec) field matches the Kubernetes [`nodeSelector`](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) syntax.\nLabel the nodes you want to target. For example, if nodes 'n1' and 'n2' are labeled with `memory=high`:\n\n[code example]\n\nThe Redis Enterprise cluster CRD can request to be scheduled on these nodes:\n\n[code example]\n\nThe operator copies [`nodeSelector`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api#spec) into the pod spec. The scheduler places pods only on nodes that match the selector."},{"id":"use-node-pools","title":"Use node pools","role":"content","text":"Node pools group similar nodes. Providers label nodes by pool.\n\nUse [`nodeSelector`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api#spec) to target a pool by label. For example, on GKE:\n\n[code example]"},{"id":"provider-resources","title":"Provider resources","role":"content","text":"Cloud providers label nodes by pool. See links below for specific documentation.\n\n- GKE:\n  - [Create and manage cluster and node pool labels](https://cloud.google.com/kubernetes-engine/docs/how-to/creating-managing-labels)\n  - [Update node labels and taints for existing node pools](https://cloud.google.com/kubernetes-engine/docs/how-to/update-existing-nodepools)\n- AKS:\n  - [Use labels in an AKS cluster](https://learn.microsoft.com/en-us/azure/aks/use-labels)\n  - [Manage node pools in AKS](https://learn.microsoft.com/en-us/azure/aks/manage-node-pools)\n- EKS:\n  - [Create a managed node group with labels (AWS CLI)](https://docs.aws.amazon.com/cli/latest/reference/eks/create-nodegroup.html)\n  - [Update a managed node group to add labels (AWS CLI)](https://docs.aws.amazon.com/cli/latest/reference/eks/update-nodegroup-config.html)"},{"id":"use-node-taints","title":"Use node taints","role":"content","text":"Use node taints and pod tolerations to control REC pod scheduling. Set tolerations with [`spec.podTolerations`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api#specpodtolerations) (standard [Kubernetes tolerations](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/#concepts)).\n\nExample: on a single node pool, reserve nodes n1–n3 for REC by adding taints:\n\n[code example]\n\nThis blocks pods unless they tolerate the `db=rec` taint.\n\nThen add a matching toleration to the REC:\n\n[code example]\n\nA set of taints can also handle more complex use cases.\nFor example, a `role=test` or `role=dev` taint can be used to designate a node as dedicated for testing or development workloads via pod tolerations."},{"id":"use-pod-anti-affinity","title":"Use pod anti-affinity","role":"content","text":"By default, REC node pods are not scheduled on the same node within the same cluster:\n\n[code example]\n\nEach pod has these labels. `redis.io/cluster` is your cluster name.\n\nModify this rule to widen or narrow placement. For example, remove the `redis.io/cluster` label to prevent pods from different clusters from sharing a node:\n\n[code example]\n\nTo avoid co-locating with other database workloads, label those pods `local/role: database` and add anti-affinity to keep one database per node:\n\n[code example]\n\nKubernetes will not schedule two pods with label `local/role: database` on the same node."},{"id":"enable-rack-awareness","title":"Enable rack awareness","role":"content","text":"Enable rack-zone awareness to improve availability during rack or zone failures.\n\n> [!NOTE]\n> When creating your rack-zone ID, there are some constraints to consider; see [rack-zone awareness](https://redis.io/docs/latest/operate/rs/clusters/configure/rack-zone-awareness#rack-zone-id-rules) for more info. \n\nConfigure it with [`spec.rackAwarenessNodeLabel`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api#spec) in the REC."},{"id":"choose-a-node-label","title":"Choose a node label","role":"content","text":"The most common label used for rack-zone awareness is topology.kubernetes.io/zone, a standard Kubernetes label that shows the zone a node runs in. Many Kubernetes platforms add this label to nodes by default, as noted in the [Kubernetes documentation](https://kubernetes.io/docs/setup/best-practices/multiple-zones/#nodes-are-labeled).\n\nIf your platform doesn’t set this label automatically, you can use any custom label that describes the node’s topology (such as rack, zone, or region)."},{"id":"label-all-eligible-nodes","title":"Label all eligible nodes","role":"content","text":"> [!WARNING]\n> All eligible nodes **must** have the label for rack awareness to work. The operator requires every node that might run Redis Enterprise pods to be labeled. If any nodes are missing the label, reconciliation fails.\n\nEligible nodes are nodes where REC pods can run. By default, this means all worker nodes. You can limit eligibility with [`spec.nodeSelector`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api#spec).\n\nGive each eligible node a label value that reflects its rack, zone, or region.\n\nCheck node label values:\n\n[code example]"},{"id":"enable-the-cluster-role","title":"Enable the cluster role","role":"content","text":"Grant the operator read access to node labels with a ClusterRole and ClusterRoleBinding.\n\nClusterRole:\n\n\n\nBind to the `redis-enterprise-operator` service account:\n\n\n\nApply these files with `kubectl apply`. For example:\n\n[code example]\n\nAfter you apply the role and binding, you can configure rack awareness."},{"id":"configure-rack-awareness","title":"Configure rack awareness","role":"configuration","text":"Set [`spec.rackAwarenessNodeLabel`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api#spec) to the node label to use:\n\n\n\n> [!NOTE]\n> When you set [`spec.rackAwarenessNodeLabel`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api#spec), the operator sets the anti-affinity `topologyKey` to that label unless you define [`spec.podAntiAffinity`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api#specpodantiaffinity). If you define both, make sure `topologyKey` matches your node label."},{"id":"rack-awareness-limitations","title":"Rack awareness limitations","role":"content","text":"> [!WARNING]\n> **Pod restart distribution maintenance**: When rack awareness is enabled, node pods and shards are initially deployed based on rack constraints to ensure proper distribution across zones. However, Redis Enterprise does not automatically maintain this distribution when node pods are restarted.\n>\n> After pod restarts, the rack awareness policy may be violated, requiring manual intervention to restore proper shard distribution. While Redis Enterprise provides tools to identify shards that need to be moved to restore correct rack distribution, it does not provide automated orchestration to perform these moves.\n\n**Important considerations for production deployments:**\n\n- At scale, manual shard redistribution can become operationally challenging\n- This limitation is particularly important for edge deployments where automated recovery is preferred\n- Plan for operational procedures to handle rack awareness policy violations after pod restarts\n- Consider monitoring tools to detect when rack awareness constraints are violated"}],"examples":[{"id":"use-node-selectors-ex0","language":"sh","code":"kubectl label nodes n1 memory=high\nkubectl label nodes n2 memory=high","section_id":"use-node-selectors"},{"id":"use-node-selectors-ex1","language":"yaml","code":"apiVersion: app.redislabs.com/v1\nkind: RedisEnterpriseCluster\nmetadata:\n  name: rec\nspec:\n  nodes: 3\n  nodeSelector:\n     memory: high","section_id":"use-node-selectors"},{"id":"use-node-pools-ex0","language":"yaml","code":"apiVersion: app.redislabs.com/v1\nkind: RedisEnterpriseCluster\nmetadata:\n  name: rec\nspec:\n  nodes: 3\n  nodeSelector:\n     cloud.google.com/gke-nodepool: 'high-memory'","section_id":"use-node-pools"},{"id":"use-node-taints-ex0","language":"sh","code":"kubectl taint nodes n1 db=rec:NoSchedule\nkubectl taint nodes n2 db=rec:NoSchedule\nkubectl taint nodes n3 db=rec:NoSchedule","section_id":"use-node-taints"},{"id":"use-node-taints-ex1","language":"yaml","code":"apiVersion: app.redislabs.com/v1\nkind: RedisEnterpriseCluster\nmetadata:\n  name: rec\nspec:\n  nodes: 3\n  podTolerations:\n  - key: db\n    operator: Equal\n    value: rec\n    effect: NoSchedule","section_id":"use-node-taints"},{"id":"use-pod-anti-affinity-ex0","language":"yaml","code":"podAntiAffinity:\n  requiredDuringSchedulingIgnoredDuringExecution:\n  - labelSelector:\n      matchLabels:\n        app: redis-enterprise\n        redis.io/cluster: rec\n        redis.io/role: node\n    topologyKey: kubernetes.io/hostname","section_id":"use-pod-anti-affinity"},{"id":"use-pod-anti-affinity-ex1","language":"yaml","code":"apiVersion: app.redislabs.com/v1\nkind: RedisEnterpriseCluster\nmetadata:\n  name: rec\nspec:\n  nodes: 3\n  podAntiAffinity:\n    requiredDuringSchedulingIgnoredDuringExecution:\n    - labelSelector:\n        matchLabels:\n          app: redis-enterprise\n          redis.io/role: node\n      topologyKey: kubernetes.io/hostname","section_id":"use-pod-anti-affinity"},{"id":"use-pod-anti-affinity-ex2","language":"yaml","code":"apiVersion: app.redislabs.com/v1\nkind: RedisEnterpriseCluster\nmetadata:\n  name: rec\nspec:\n  nodes: 3\n  extraLabels:\n     local/role: database\n  podAntiAffinity:\n    requiredDuringSchedulingIgnoredDuringExecution:\n    - labelSelector:\n        matchLabels:\n          local/role: database\n          app: redis-enterprise\n          redis.io/cluster: rec\n          redis.io/role: node\n      topologyKey: kubernetes.io/hostname","section_id":"use-pod-anti-affinity"},{"id":"label-all-eligible-nodes-ex0","language":"sh","code":"kubectl get nodes -o custom-columns=\"name:metadata.name\",\"rack\\\\zone:metadata.labels.topology\\.kubernetes\\.io/zone\"\n\nname                                            rack\\zone\nip-10-0-x-a.eu-central-1.compute.internal    eu-central-1a\nip-10-0-x-b.eu-central-1.compute.internal    eu-central-1a\nip-10-0-x-c.eu-central-1.compute.internal    eu-central-1b\nip-10-0-x-d.eu-central-1.compute.internal    eu-central-1b","section_id":"label-all-eligible-nodes"},{"id":"enable-the-cluster-role-ex0","language":"sh","code":"kubectl apply -f rack-aware-cluster-role.yaml\nkubectl apply -f rack-aware-cluster-role-binding.yaml","section_id":"enable-the-cluster-role"}]}
{"schema_version":2,"id":"operate/kubernetes/recommendations/persistent-volumes","title":"Use persistent volumes in Redis Enterprise clusters","url":"https://redis.io/docs/latest/operate/kubernetes/recommendations/persistent-volumes/","summary":"This section covers details about how persistent volumes are sized and specified for Redis Enterprise cluster deployments.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T16:14:52+01:00","page_type":"content","content_hash":"5bc8fafa9878fdcc02aaa8eb414be3b08b9e909c08a270748b8b3ef4648ced28","sections":[{"id":"storage-types","title":"Storage types","role":"overview","text":"Redis Enterprise for Kubernetes can only use storage classes supported by block storage. Block storage is mounted at the Kubernetes node level and utilizes EXT4 or XFS file systems. It can be sourced from enterprise-grade SANs or cloud environments such as EBS, Azure Managed Disks, or GCP persistent disks.\n\n> [!WARNING]\n> NFS, NFS-like, and multi-read-write/shared storage options are not supported. These types of storage are often slow and can cause locking behaviors that are incompatible with the requirements of database storage."},{"id":"rec-persistentspec-field","title":"REC `persistentSpec` field","role":"content","text":"To deploy a Redis Enterprise cluster with Redis Enterprise operator the\nspec should include a *persistentSpec* section, in the\n*redis-enterprise-cluster.yaml* file:\n\n    spec:\n      nodes: 3\n      persistentSpec:\n       enabled: true\n       storageClassName: \"standard\"\n       volumeSize: \"23Gi\" #optional\n\nPersistence storage is a requirement for production deployments.\n\n> [!NOTE]\n> For **production deployments** of Redis Enterprise Cluster on Kubernetes,\n> the Redis Enterprise Cluster (REC) must be deployed with persistence enabled.\n> The REC deployment files in the [Kubernetes documentation](https://github.com/RedisLabs/redis-enterprise-k8s-docs) contain this declaration by default."},{"id":"volume-size","title":"Volume size","role":"content","text":"*volumeSize* is an optional definition. By default, if the definition is\nomitted, operator allocates five times (5x) the amount of memory (RAM)\ndefined for nodes (see example below), which is the recommended\npersistent storage size as described in the [Hardware\nrequirements](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/hardware-requirements) article.\n\nTo explicitly specify the persistent storage size, use the *volumeSize*\nproperty as described in the example above.\n\nPersistent volume claims can be expanded, but not reduced after creation. See [Expand PersistentVolumeClaim (PVC)](https://redis.io/docs/latest/operate/kubernetes/re-clusters/expand-pvc) for details.\n\n> [!NOTE]\n> We recommend that you omit the volumeSize definition from the REC declaration\n> so that the Redis Enterprise Cluster deployment on Kubernetes use the default volume size."},{"id":"storage-class-name","title":"Storage class name","role":"content","text":"*storageClassName* determines the Storage Class resource, which is\ndefined by the Kubernetes cluster administrator, to be used for\npersistent storage.\n\nDifferent Kubernetes distributions and different deployments use\ndifferent Storage Class resources.\n\nIn order to determine the Storage Class resources available for your K8s\ndeployment, use the following command:\n\n    kubectl get StorageClass\n\nTypically, AWS provides \"gp3\" as the Storage Class name while GKE uses \"standard.\"\nAzure provides two Storage Classes: \"default\" using HDDs, and \"managed-premium\" using SSDs.\n\nBelow is an example of a response to the command.\n\n|                         |                                                         |\n| ----------------------- | ------------------------------------------------------- |\n| *Name:*                 | *gp3*                                                   |\n| *IsDefaultClass:*       | *Yes*                                                   |\n| *Annotations:*          | *storageclass.beta.kubernetes.io/is-default-class=true* |\n| *Provisioner:*          | *kubernetes.io/aws-ebs*                                 |\n| *Parameters:*           | *encrypted=false,kmsKeyId=,type=gp3*                    |\n| *AllowVolumeExpansion:* | *\\<unset\\>*                                             |\n| *MountOptions:*         | *\\<none\\>*                                              |\n| *ReclaimPolicy:*        | *Delete*                                                |\n| *VolumeBindingMode:*    | *Immediate*                                             |\n| *Events:*               | *\\<none\\>*                                              |\n\n> [!NOTE]\n> storageClassName must be specified for this deployment type.\n\n> [!WARNING]\n> The storage class cannot be changed after deployment. Trying to change this value after deployment could result in unexpected and potentially damaging behavior.\n\nExample of the redisEnterpriseNodeResources definition:\n\n    redisEnterpriseNodeResources:\n      limits:\n        cpu: \"4000m\"\n        memory: 4Gi\n      requests:\n        cpu: \"4000m\"\n        memory: 4Gi"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/recommendations/pod-stability","title":"Manage pod stability","url":"https://redis.io/docs/latest/operate/kubernetes/recommendations/pod-stability/","summary":"This section provides information about how you can use quality of service, priority class, eviction thresholds and resource monitoring to maintain cluster node pod stability.","tags":["docs","operate","kubernetes"],"last_updated":"2023-12-14T15:27:49+01:00","page_type":"content","content_hash":"a405678f5dc571e1abef7a5deb6cb2afcd928bdf78af21f4113a3b928ce115f5","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Kubernetes clusters manage the allocation of system resources and can evict pods to release system resources.\nHere are some ways that you can configure the Redis Enterprise node pods to maintain pod stability:"},{"id":"guaranteed-quality-of-service","title":"Guaranteed quality of service","role":"content","text":"A running pod has a quality of service measure assigned to it that is\none of three [quality of service classes](https://kubernetes.io/docs/tasks/configure-pod-container/quality-service-pod/):\nGuaranteed, Burstable, and Best Effort.\nYou can assure the Guaranteed class is assigned to the Redis Enterprise node pods\nby following the right guidelines.\n\nTo get a Guaranteed quality of service class assigned:\n\n * Every container in the pod must have a memory limit and a memory request, and they must be the same.\n * Every container in the pod must have a CPU limit and a CPU request, and they must be the same.\n\nIf resources limits and requests are not specified in the Redis Enterprise CRD,\nthese requirements are met in the default version created by the operator. of your Redis Enterprise cluster CRD,\nOtherwise, you must set the limits and requests to the same value for memory and CPU in the `redisEnterpriseNodeResources`\nsection of the CRD.\n\nSidecar containers also impact the quality of service class assignment for the pod.\n\nTo check the quality of service class of any running Redis Enterprise node pod, run:\n\n[code example]\n\nwhere `rec-0` is the name of one of the pods associated with the Redis Enterprise cluster."},{"id":"using-priority-to-protect-from-preemption","title":"Using priority to protect from preemption","role":"content","text":"When a Redis Enterprise node pod is scheduled, it can be assigned a\n[priority class](https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/)\nwith the `priorityClassName` property. This property value is the name of\na priority class that must already exist within the cluster.\n\nA sufficiently high priority will prevent other workloads with a lower\npriority from preempting the scheduling of Redis Enterprise Nodes. Similarly,\na high value may also prevent eviction when lower priority workloads\nare deployed on the same cluster.\n\nThe successful use of this strategy involves first creating a priority class with a very large priority value:\n\n[code example]\n\nThen, you refer to the priority class by name in your Redis Enterprise cluster CRD:\n\n[code example]\n\nAlternatively, you can also [disable preemption entirely](https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#how-to-disable-preemption)."},{"id":"managing-eviction-thresholds","title":"Managing eviction thresholds","role":"content","text":"Eviction thresholds are typically managed by kubelet arguments.\nYou can set the thresholds:\n\n- On OpenShift - In the [config file](https://docs.openshift.com/container-platform/3.11/admin_guide/out_of_resource_handling.html#out-of-resource-create-config).\n- On GKE - In the [managed settings](https://cloud.google.com/kubernetes-engine/docs/concepts/cluster-architecture#node_allocatable).\n\nWe recommend that you:\n\n * Set the [soft eviction threshold](https://kubernetes.io/docs/tasks/administer-cluster/out-of-resource/#soft-eviction-thresholds)\n   to be higher than the [hard eviction threshold](https://kubernetes.io/docs/tasks/administer-cluster/out-of-resource/#hard-eviction-thresholds).\n   The high soft threshold makes the node condition change earlier, and alerts the administrator.\n * Set `eviction-max-pod-grace-period` high enough to allow the RS pods to migrate the Redis databases before the pods are force killed.\n * Set the `eviction-soft-grace-period` high enough that the administrator (or a k8s auto-scaling mechanism) scales k8s up or out."},{"id":"monitoring-for-memory-and-disk-usage","title":"Monitoring for memory and disk usage","role":"content","text":"We recommend that you monitor the node for MemoryPressure and DiskPressure.\nWhen both of these conditions are true, then an [eviction threshold](https://kubernetes.io/docs/tasks/administer-cluster/out-of-resource/#eviction-thresholds)\nis met and the pod is evicted.\n\nTo retrieve the flags, run the command:\n\n[code example]"}],"examples":[{"id":"guaranteed-quality-of-service-ex0","language":"sh","code":"kubectl get pod rec-0 --o jsonpath=\"{.status.qosClass}\"","section_id":"guaranteed-quality-of-service"},{"id":"using-priority-to-protect-from-preemption-ex0","language":"yaml","code":"apiVersion: scheduling.k8s.io/v1\nkind: PriorityClass\nmetadata:\n  name: redis-enterprise-priority\nvalue: 1000000000\nglobalDefault: false\ndescription: \"This priority class should be used for Redis Enterprise pods only.\"","section_id":"using-priority-to-protect-from-preemption"},{"id":"using-priority-to-protect-from-preemption-ex1","language":"yaml","code":"apiVersion: app.redislabs.com/v1\nkind: RedisEnterpriseCluster\nmetadata:\n  name: example-redisenterprisecluster\nspec:\n  size: 3\n  priorityClassName: \"redis-enterprise-priority\"","section_id":"using-priority-to-protect-from-preemption"},{"id":"monitoring-for-memory-and-disk-usage-ex0","language":"sh","code":"$kubectl get nodes -o jsonpath='{range .items[*]}name:{.metadata.name}{\"\\t\"}MemoryPressure:{.status.conditions[?(@.type == \"MemoryPressure\")].status}{\"\\t\"}DiskPressure:{.status.conditions[?(@.type == \"DiskPressure\")].status}{\"\\n\"}{end}'\n\nname:gke-55d1ac88-213c\tMemoryPressure:False\tDiskPressure:False\nname:gke-55d1ac88-vrpp\tMemoryPressure:False\tDiskPressure:False\nname:gke-7253cc19-42g0\tMemoryPressure:False\tDiskPressure:False","section_id":"monitoring-for-memory-and-disk-usage"}]}
{"schema_version":2,"id":"operate/kubernetes/recommendations/sizing-on-kubernetes","title":"Size and scale a Redis Enterprise cluster deployment on Kubernetes","url":"https://redis.io/docs/latest/operate/kubernetes/recommendations/sizing-on-kubernetes/","summary":"This section provides information about sizing and scaling Redis Enterprise in a Kubernetes deployment.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T16:14:52+01:00","page_type":"content","content_hash":"28b6fefa349758dcf26e0e9b104fbe30c87ea3f491bdd5e5d944653f224a7608","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The following article reviews the mechanism and methods available for sizing\nand scaling a Redis Enterprise cluster deployment.\n\nFor minimum and recommended sizing, always follow the sizing guidelines\ndetailed in the [Redis Enterprise hardware requirements](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/hardware-requirements)."},{"id":"sizing-and-scaling-cluster-nodes","title":"Sizing and scaling cluster nodes","role":"content","text":""},{"id":"setting-the-number-of-cluster-nodes","title":"Setting the number of cluster nodes","role":"configuration","text":"Define the number of cluster nodes in redis-enterprise-cluster.yaml file.\n\n    spec:\n\n        nodes: 3\n\nThe number of nodes in the cluster must be an uneven number\nequal to or greater than 3. Refer to the article [Highly Available Redis](https://redislabs.com/redis-enterprise/technology/highly-available-redis/)\nfor a detailed explanation on this topic.\n\nSet the number of cluster nodes during deployment\nby editing the redis-enterprise-cluster.yaml file and\napplying the file by running:\n\n    kubectl apply -f redis-enterprise-cluster.yaml"},{"id":"scaling-out","title":"Scaling out","role":"content","text":"To scale out a Redis Enterprise Cluster deployment, increase the number of nodes\nin the spec. For example, to scale the cluster out from 3 nodes to 5 nodes,\nedit the redis-enterprise-cluster.yaml file with the following:\n\n    spec:\n\n        nodes: 5\n\nTo apply the new cluster configuration run:\n\n    kubectl apply -f redis-enterprise-cluster.yaml\n\n> [!WARNING]\n>  Decreasing the number of nodes is not supported."},{"id":"sizing-compute-resources","title":"Sizing compute resources","role":"content","text":"To set the compute resources required for each node,\nuse the redisEnterpriseNodeResources spec field.\n\nUnder redisEnterpriseNodeResources spec, set the following fields\naccording to the provided guidelines.\n\n- limits – specifies the maximum compute resources for a Redis node\n- requests – specifies the minimum compute resources for a Redis node\n\nFor example:\n\n    redisEnterpriseNodeResources:\n\n        limits:\n\n        cpu: \"2000m\"\n\n        memory: 4Gi\n\n        requests:\n\n        cpu: \"2000m\"\n\n        memory: 4Gi\n\nThe default values, if unspecified, are 2 cores (2000m) and 4GB (4Gi).\n\nSet the compute resources for cluster nodes during deployment\nby editing the redis-enterprise-cluster.yaml file and\napplying the file by running:\n\n    kubectl apply -f redis-enterprise-cluster.yaml"},{"id":"scaling-up-node-compute-resources","title":"Scaling up node compute resources","role":"content","text":"To scale up nodes in an existing Redis Enterprise Cluster deployment,\nadjust the cpu and memory parameters in the spec. For example,\nto scale nodes up to the recommended amount of compute resources,\nedit the redis-enterprise-cluster.yaml file with the following:\n\nredisEnterpriseNodeResources:\n\n    limits:\n\n        cpu: \"8000m\"\n\n        memory: 30Gi\n\n    requests\n\n        cpu: \"8000m\"\n\n        memory: 30Gi\n\nThen, apply the file by running:\n\n    kubectl apply -f redis-enterprise-cluster.yaml\n\n> [!WARNING]\n> When adjusting compute resources, make sure the ratio of persistent volume size and the new memory size are in accordance to the [Hardware\n> requirements](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/hardware-requirements). \n>\n> Persistent volume claims can be expanded, but not reduced after creation. See [Expand PersistentVolumeClaim (PVC)](https://redis.io/docs/latest/operate/kubernetes/re-clusters/expand-pvc) for details."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/reference/api","title":"API reference","url":"https://redis.io/docs/latest/operate/kubernetes/reference/api/","summary":"Reference documentation for Redis Enterprise operator APIs","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","children":[{"id":"operate/kubernetes/reference/api/redis_enterprise_active_active_database_api","summary":"","title":"RedisEnterpriseActiveActiveDatabase API Reference","url":"https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_active_active_database_api/"},{"id":"operate/kubernetes/reference/api/redis_enterprise_acl_api","summary":"","title":"RedisEnterpriseACL API Reference","url":"https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_acl_api/"},{"id":"operate/kubernetes/reference/api/redis_enterprise_cluster_api","summary":"","title":"RedisEnterpriseCluster API Reference","url":"https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api/"},{"id":"operate/kubernetes/reference/api/redis_enterprise_cluster_role_api","summary":"","title":"RedisEnterpriseClusterRole API Reference","url":"https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_role_api/"},{"id":"operate/kubernetes/reference/api/redis_enterprise_cluster_role_binding_api","summary":"","title":"RedisEnterpriseClusterRoleBinding API Reference","url":"https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_role_binding_api/"},{"id":"operate/kubernetes/reference/api/redis_enterprise_database_api","summary":"","title":"RedisEnterpriseDatabase API Reference","url":"https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api/"},{"id":"operate/kubernetes/reference/api/redis_enterprise_remote_cluster_api","summary":"","title":"RedisEnterpriseRemoteCluster API Reference","url":"https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_remote_cluster_api/"},{"id":"operate/kubernetes/reference/api/redis_enterprise_role_api","summary":"","title":"RedisEnterpriseRole API Reference","url":"https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_role_api/"},{"id":"operate/kubernetes/reference/api/redis_enterprise_role_binding_api","summary":"","title":"RedisEnterpriseRoleBinding API Reference","url":"https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_role_binding_api/"},{"id":"operate/kubernetes/reference/api/redis_enterprise_user_api","summary":"","title":"RedisEnterpriseUser API Reference","url":"https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_user_api/"}],"page_type":"content","content_hash":"9fbb33e1ddd7a1787a06767ecd8118dc47a10fedfca575375c4173131a514aa8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The Redis Enterprise operator provides Kubernetes custom resource definitions (CRDs) that let you manage Redis Enterprise clusters and databases declaratively. This section contains the complete API reference for all operator resources."},{"id":"api-versions-and-stability","title":"API versions and stability","role":"content","text":"The operator uses different API versions to indicate stability and feature maturity:\n\n- **`app.redislabs.com/v1`** - Stable APIs for production use\n- **`app.redislabs.com/v1alpha1`** - Alpha APIs that may change in future releases"},{"id":"custom-resources","title":"Custom resources","role":"content","text":"| Resource | API Version | Purpose |\n|----------|-------------|---------|\n| [RedisEnterpriseCluster (REC)](redis_enterprise_cluster_api) | `v1` | Manages Redis Enterprise cluster deployments |\n| [RedisEnterpriseDatabase (REDB)](redis_enterprise_database_api) | `v1alpha1` | Creates and configures Redis databases |\n| [RedisEnterpriseActiveActiveDatabase (REAADB)](redis_enterprise_active_active_database_api) | `v1alpha1` | Sets up active-active databases across clusters |\n| [RedisEnterpriseRemoteCluster (RERC)](redis_enterprise_remote_cluster_api) | `v1alpha1` | Defines remote cluster connections for active-active |\n| [RedisEnterpriseUser (REUSER)](redis_enterprise_user_api) | `v1alpha1` | Defines users for cluster access control |\n| [RedisEnterpriseRole (REROLE)](redis_enterprise_role_api) | `v1alpha1` | Defines a role scoped to selected databases |\n| [RedisEnterpriseRoleBinding (REROLEBINDING)](redis_enterprise_role_binding_api) | `v1alpha1` | Binds users to a RedisEnterpriseRole |\n| [RedisEnterpriseClusterRole (RECROLE)](redis_enterprise_cluster_role_api) | `v1alpha1` | Defines a cluster-scoped role |\n| [RedisEnterpriseClusterRoleBinding (RECROLEBINDING)](redis_enterprise_cluster_role_binding_api) | `v1alpha1` | Binds users to a RedisEnterpriseClusterRole |\n| [RedisEnterpriseACL (REACL)](redis_enterprise_acl_api) | `v1alpha1` | Defines an access control list (ACL) for a database |"},{"id":"working-with-the-apis","title":"Working with the APIs","role":"content","text":""},{"id":"using-kubectl","title":"Using kubectl","role":"content","text":"Manage all resources using standard `kubectl` commands:\n\n[code example]"},{"id":"resource-relationships","title":"Resource relationships","role":"content","text":"- Create a `RedisEnterpriseCluster` (REC) first to provide the Redis Enterprise infrastructure\n- Create `RedisEnterpriseDatabase` (REDB) resources within a cluster to provision individual databases\n- Use `RedisEnterpriseActiveActiveDatabase` (REAADB) with `RedisEnterpriseRemoteCluster (RERC)` resources to define participating clusters\n\nFor complete YAML configuration examples, see the [YAML examples](https://redis.io/docs/latest/operate/kubernetes/reference/yaml) section."}],"examples":[{"id":"using-kubectl-ex0","language":"bash","code":"# List all Redis Enterprise clusters\nkubectl get rec\n\n# Get detailed information about a specific database\nkubectl describe redb my-database\n\n# Apply a configuration from a YAML file\nkubectl apply -f my-redis-config.yaml","section_id":"using-kubectl"}]}
{"schema_version":2,"id":"operate/kubernetes/reference/api/redis_enterprise_acl_api","title":"RedisEnterpriseACL API Reference","url":"https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_acl_api/","summary":"","tags":["docs","operate","kubernetes"],"last_updated":"2026-07-21T12:02:16-05:00","page_type":"content","content_hash":"a43f121dd9348dcccf8da0a5ca4c1f1eff3fa72084737d55be16dbb97e48e3dd","sections":[{"id":"overview","title":"Overview","role":"overview","text":"apiVersion:\n\n\n- [app.redislabs.com/v1alpha1](#appredislabscomv1alpha1)\n\n\n\n\n# app.redislabs.com/v1alpha1\n\n\n\n\nRedisEnterpriseACL represents an access control list definition for a Redis database.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n      <td>apiVersion</td>\n      <td>string</td>\n      <td>app.redislabs.com/v1alpha1</td>\n      <td>true</td>\n      </tr>\n      <tr>\n      <td>kind</td>\n      <td>string</td>\n      <td>RedisEnterpriseACL</td>\n      <td>true</td>\n      </tr>\n      <tr>\n      <td><a href=\"https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#objectmeta-v1-meta\">metadata</a></td>\n      <td>object</td>\n      <td>Refer to the Kubernetes API documentation for the fields of the `metadata` field.</td>\n      <td>true</td>\n      </tr><tr>\n        <td><a href=\"#spec\">spec</a></td>\n        <td>object</td>\n        <td>\n          RedisEnterpriseACLSpec defines the desired state of RedisEnterpriseACL<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#status\">status</a></td>\n        <td>object</td>\n        <td>\n          RedisEnterpriseACLStatus defines the observed state of RedisEnterpriseACL<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"spec","title":"spec","role":"content","text":"<sup><sup>[↩ Parent](#)</sup></sup>\n\nRedisEnterpriseACLSpec defines the desired state of RedisEnterpriseACL\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>acl</td>\n        <td>string</td>\n        <td>\n          Redis ACL rule<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"status","title":"status","role":"content","text":"<sup><sup>[↩ Parent](#)</sup></sup>\n\nRedisEnterpriseACLStatus defines the observed state of RedisEnterpriseACL\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>observedGeneration</td>\n        <td>integer</td>\n        <td>\n          The most recent generation of this resource that was observed and acted upon by the controller.<br/>\n          <br/>\n            <i>Format</i>: int64<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>uid</td>\n        <td>string</td>\n        <td>\n          The internal UID of the Redis ACL object defined in the Redis Enterprise Cluster.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/reference/api/redis_enterprise_active_active_database_api","title":"RedisEnterpriseActiveActiveDatabase API Reference","url":"https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_active_active_database_api/","summary":"","tags":["docs","operate","kubernetes"],"last_updated":"2026-07-21T12:02:16-05:00","page_type":"content","content_hash":"6034034cc085dcf0ee175538ecf13359c8e3a0d64894806322b2b0bed68c85f7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"apiVersion:\n\n\n- [app.redislabs.com/v1alpha1](#appredislabscomv1alpha1)\n\n\n\n\n# app.redislabs.com/v1alpha1\n\n\n\n\nThe schema for the redisenterpriseactiveactivedatabase API.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n      <td>apiVersion</td>\n      <td>string</td>\n      <td>app.redislabs.com/v1alpha1</td>\n      <td>true</td>\n      </tr>\n      <tr>\n      <td>kind</td>\n      <td>string</td>\n      <td>RedisEnterpriseActiveActiveDatabase</td>\n      <td>true</td>\n      </tr>\n      <tr>\n      <td><a href=\"https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#objectmeta-v1-meta\">metadata</a></td>\n      <td>object</td>\n      <td>Refer to the Kubernetes API documentation for the fields of the `metadata` field.</td>\n      <td>true</td>\n      </tr><tr>\n        <td><a href=\"#spec\">spec</a></td>\n        <td>object</td>\n        <td>\n          Defines the desired state of RedisEnterpriseActiveActiveDatabase.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#status\">status</a></td>\n        <td>object</td>\n        <td>\n          Defines the observed state of RedisEnterpriseActiveActiveDatabase.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"spec","title":"spec","role":"content","text":"<sup><sup>[↩ Parent](#)</sup></sup>\n\nDefines the desired state of RedisEnterpriseActiveActiveDatabase.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td><a href=\"#specparticipatingclusters\">participatingClusters</a></td>\n        <td>[]object</td>\n        <td>\n          List of participating cluster specifications and configurations.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td><a href=\"#specglobalconfigurations\">globalConfigurations</a></td>\n        <td>object</td>\n        <td>\n          Global configurations for the Active-Active database. Contains the global properties for each participating cluster database within the Active-Active database.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specredisenterprisecluster\">redisEnterpriseCluster</a></td>\n        <td>object</td>\n        <td>\n          Connection to the Redis Enterprise Cluster.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specparticipatingclusters","title":"spec.participatingClusters[]","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nSpecifies the configuration for a participating cluster in the Active-Active database.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>name</td>\n        <td>string</td>\n        <td>\n          Name of the remote cluster custom resource to link.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>externalReplicationPort</td>\n        <td>integer</td>\n        <td>\n          Port number for the replication endpoint. Use this field when you use LoadBalancers for synchronization between Active-Active replicas and need to specify the port number that the LoadBalancer listens on.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>namespace</td>\n        <td>string</td>\n        <td>\n          Namespace where the REAADB object is deployed within the corresponding participating cluster. Ensure that the Redis Enterprise operator is configured to watch this namespace in the corresponding cluster and that the required RBAC configuration is properly set up. For more information about setting up multiple namespaces, see https://redis.io/docs/latest/operate/kubernetes/re-clusters/multi-namespace/. If you don't specify a namespace, the REAADB is deployed to the REC's namespace in the corresponding cluster.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specglobalconfigurations","title":"spec.globalConfigurations","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nGlobal configurations for the Active-Active database. Contains the global properties for each participating cluster database within the Active-Active database.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td><a href=\"#specglobalconfigurationsactiveactive\">activeActive</a></td>\n        <td>object</td>\n        <td>\n          Connection/ association to the Active-Active database.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specglobalconfigurationsalertsettings\">alertSettings</a></td>\n        <td>object</td>\n        <td>\n          Settings for database alerts.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specglobalconfigurationsauditing\">auditing</a></td>\n        <td>object</td>\n        <td>\n          Database auditing configuration.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specglobalconfigurationsbackup\">backup</a></td>\n        <td>object</td>\n        <td>\n          Target for automatic database backups.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>clientAuthenticationCertificates</td>\n        <td>[]string</td>\n        <td>\n          The Secrets containing TLS Client Certificate to use for Authentication<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specglobalconfigurationsconnectionsettings\">connectionSettings</a></td>\n        <td>object</td>\n        <td>\n          Connection-related settings such as proxy connections and scheduling policy.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>dataInternodeEncryption</td>\n        <td>boolean</td>\n        <td>\n          Internode encryption (INE) setting. An optional boolean setting, overriding a similar cluster-wide policy. If set to False, INE is guaranteed to be turned off for this DB (regardless of cluster-wide policy). If set to True, INE will be turned on, unless the capability is not supported by the DB ( in such a case we will get an error and database creation will fail). If left unspecified, will be disabled if internode encryption is not supported by the DB (regardless of cluster default). Deleting this property after explicitly setting its value shall have no effect.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>databasePort</td>\n        <td>integer</td>\n        <td>\n          Database port number. TCP port on which the database is available. Will be generated automatically if omitted. can not be changed after creation<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>databaseSecretName</td>\n        <td>string</td>\n        <td>\n          The name of the secret that holds the password to the database (redis databases only). If secret does not exist, it will be created. To define the password, create an opaque secret and set the name in the spec. The password will be taken from the value of the 'password' key. Use an empty string as value within the secret to disable authentication for the database. Notes - For Active-Active databases this secret will not be automatically created, and also, memcached databases must not be set with a value, and a secret/password will not be automatically created for them. Use the memcachedSaslSecretName field to set authentication parameters for memcached databases.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>databaseServicePort</td>\n        <td>integer</td>\n        <td>\n          A custom port to be exposed by the database services. Can be be modified/added/removed after REDB creation. If set, it'll replace the default service port (namely, databasePort or defaultRedisPort).<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>defaultUser</td>\n        <td>boolean</td>\n        <td>\n          Is connecting with a default user allowed?  If disabled, the DatabaseSecret will not be created or updated<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>evictionPolicy</td>\n        <td>string</td>\n        <td>\n          Database eviction policy. See more https://redis.io/docs/latest/rs/administering/database-operations/eviction-policy/<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>isRof</td>\n        <td>boolean</td>\n        <td>\n          Whether it is an RoF database or not. Applicable only for databases of type \"REDIS\". Assumed to be false if left blank.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>memcachedSaslSecretName</td>\n        <td>string</td>\n        <td>\n          Credentials used for binary authentication in memcached databases. The credentials should be saved as an opaque secret and the name of that secret should be configured using this field. For username, use 'username' as the key and the actual username as the value. For password, use 'password' as the key and the actual password as the value. Note that connections are not encrypted.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>memorySize</td>\n        <td>string</td>\n        <td>\n          memory size of database. use formats like 100MB, 0.1GB. minimum value in 100MB. When redis on flash (RoF) is enabled, this value refers to RAM+Flash memory, and it must not be below 1GB.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specglobalconfigurationsmoduleslist\">modulesList</a></td>\n        <td>[]object</td>\n        <td>\n          List of modules associated with the database. The list of valid modules for the specific cluster can be retrieved from the status of the REC object. Use the \"name\" and \"versions\" fields for the specific module configuration. If specifying an explicit version for a module, automatic modules versions upgrade must be disabled by setting the '.upgradeSpec.upgradeModulesToLatest' field in the REC to 'false'. Note that the option to specify module versions is deprecated, and will be removed in future releases.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>ossCluster</td>\n        <td>boolean</td>\n        <td>\n          OSS Cluster mode option. Note that not all client libraries support OSS cluster mode.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specglobalconfigurationsossclustersettings\">ossClusterSettings</a></td>\n        <td>object</td>\n        <td>\n          Additional OSS cluster settings that may be provided to tweak OSS cluster behavior.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>persistence</td>\n        <td>enum</td>\n        <td>\n          Database on-disk persistence policy<br/>\n          <br/>\n            <i>Enum</i>: disabled, aofEverySecond, aofAlways, snapshotEvery1Hour, snapshotEvery6Hour, snapshotEvery12Hour<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>proxyPolicy</td>\n        <td>string</td>\n        <td>\n          The policy used for proxy binding to the endpoint. Supported proxy policies are: single/all-master-shards/all-nodes When left blank, the default value will be chosen according to the value of ossCluster - single if disabled, all-master-shards when enabled<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>rackAware</td>\n        <td>boolean</td>\n        <td>\n          Whether database should be rack aware. This improves availability - more information: https://redis.io/docs/latest/rs/concepts/high-availability/rack-zone-awareness/<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specglobalconfigurationsredisenterprisecluster\">redisEnterpriseCluster</a></td>\n        <td>object</td>\n        <td>\n          Connection to Redis Enterprise Cluster<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>redisVersion</td>\n        <td>string</td>\n        <td>\n          Redis OSS version. Version can be specified via <major.minor> prefix, or via channels - for existing databases - Upgrade Redis OSS version. For new databases - the version which the database will be created with. If set to 'major' - will always upgrade to the most recent major Redis version. If set to 'latest' - will always upgrade to the most recent Redis version. Depends on 'redisUpgradePolicy' - if you want to set the value to 'latest' for some databases, you must set redisUpgradePolicy on the cluster before. Possible values are 'major' or 'latest' When using upgrade - make sure to backup the database before. This value is used only for database type 'redis'. Note - Specifying Redis version is currently not supported for Active-Active database.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specglobalconfigurationsreplicasources\">replicaSources</a></td>\n        <td>[]object</td>\n        <td>\n          What databases to replicate from<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>replication</td>\n        <td>boolean</td>\n        <td>\n          In-memory database replication. When enabled, database will have replica shard for every master - leading to higher availability. Defaults to false.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>resp3</td>\n        <td>boolean</td>\n        <td>\n          Whether this database supports RESP3 protocol. Note - Deleting this property after explicitly setting its value shall have no effect. Please view the corresponding field in RS doc for more info.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>rofRamRatio</td>\n        <td>integer</td>\n        <td>\n          RAM allocation ratio for Redis Flex (v2) databases as a percentage of total data size. Valid range 0-100. When omitted, RS uses the default value of 50%. Controls how much RAM is allocated per unit of data (e.g., 30% means 3MB RAM per 10MB data). RAM grows proportionally with data until rofRamSize limit is reached (if specified). Only applicable when isRof=true and Redis version >= 8.0 (BigStore v2 - Redis Flex).<br/>\n          <br/>\n            <i>Minimum</i>: 0<br/>\n            <i>Maximum</i>: 100<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>rofRamSize</td>\n        <td>string</td>\n        <td>\n          The size of the RAM portion of an RoF database. Similarly to \"memorySize\" use formats like 100MB, 0.1GB It must be at least 10% of combined memory size (RAM+Flash), as specified by \"memorySize\".<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specglobalconfigurationsrolespermissions\">rolesPermissions</a></td>\n        <td>[]object</td>\n        <td>\n          List of Redis Enteprise ACL and Role bindings to apply<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>searchOnBigstore</td>\n        <td>boolean</td>\n        <td>\n          Enables search module indexing on flash storage for Redis Flex (v2) databases. Only applicable when isRof=true and Redis version >= 8.6. Defaults to false.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>shardCount</td>\n        <td>integer</td>\n        <td>\n          Number of database server-side shards<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>shardingEnabled</td>\n        <td>boolean</td>\n        <td>\n          Toggles database sharding for REAADBs (Active Active databases) and enabled by default. This field is blocked for REDB (non-Active Active databases) and sharding is toggled via the shardCount field - when shardCount is 1 this is disabled otherwise enabled.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>shardsPlacement</td>\n        <td>string</td>\n        <td>\n          Control the density of shards - should they reside on as few or as many nodes as possible. Available options are \"dense\" or \"sparse\". If left unset, defaults to \"dense\".<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>tags</td>\n        <td>map[string]string</td>\n        <td>\n          Tags to apply to the Active-Active database, as a map of key to value. The same set is merged into the BDB tags array of the default config and every participating-cluster instance. On key collision with an existing BDB tag, the value here wins. Removing a key after it was previously synced (recorded in status.managedTags) removes it from every BDB. Operator-reserved keys (managed_by, redb_name, redb_namespace, db_service_port, oss_cluster_access, redb_resource, redb_resource_name, global_configurations_spec, replication_endpoint_port, rerc_name, and the secret-tracking keys redis.io/db-sec-name, redis.io/db-sec-latest, redis.io/certs-latest, redis.io/bu-sec-name, redis.io/bu-sec-latest) are not allowed and will be rejected.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>tlsMode</td>\n        <td>enum</td>\n        <td>\n          Require SSL authenticated and encrypted connections to the database. enabled - all incoming connections to the Database must use SSL. disabled - no incoming connection to the Database should use SSL. replica_ssl - databases that replicate from this one need to use SSL.<br/>\n          <br/>\n            <i>Enum</i>: disabled, enabled, replica_ssl<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>type</td>\n        <td>enum</td>\n        <td>\n          The type of the database.<br/>\n          <br/>\n            <i>Enum</i>: redis, memcached<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specglobalconfigurationsupgradespec\">upgradeSpec</a></td>\n        <td>object</td>\n        <td>\n          Specifications for DB upgrade.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specglobalconfigurationsactiveactive","title":"spec.globalConfigurations.activeActive","role":"content","text":"<sup><sup>[↩ Parent](#specglobalconfigurations)</sup></sup>\n\nConnection/ association to the Active-Active database.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>name</td>\n        <td>string</td>\n        <td>\n          The the corresponding Active-Active database name, Redis Enterprise Active Active Database custom resource name, this Resource is associated with. In case this resource is created manually at the active active database creation this field must be filled via the user, otherwise, the operator will assign this field automatically. Note: this feature is currently unsupported.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>participatingClusterName</td>\n        <td>string</td>\n        <td>\n          The corresponding participating cluster name, Redis Enterprise Remote Cluster custom resource name, in the Active-Active database, In case this resource is created manually at the active active database creation this field must be filled via the user, otherwise, the operator will assign this field automatically. Note: this feature is currently unsupported.<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specglobalconfigurationsalertsettings","title":"spec.globalConfigurations.alertSettings","role":"content","text":"<sup><sup>[↩ Parent](#specglobalconfigurations)</sup></sup>\n\nSettings for database alerts.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td><a href=\"#specglobalconfigurationsalertsettingsbdb_backup_delayed\">bdb_backup_delayed</a></td>\n        <td>object</td>\n        <td>\n          Periodic backup has been delayed for longer than specified threshold value [minutes]<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specglobalconfigurationsalertsettingsbdb_crdt_src_high_syncer_lag\">bdb_crdt_src_high_syncer_lag</a></td>\n        <td>object</td>\n        <td>\n          Active-active source - sync lag is higher than specified threshold value [seconds]<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specglobalconfigurationsalertsettingsbdb_crdt_src_syncer_connection_error\">bdb_crdt_src_syncer_connection_error</a></td>\n        <td>object</td>\n        <td>\n          Active-active source - sync has connection error while trying to connect replica source<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specglobalconfigurationsalertsettingsbdb_crdt_src_syncer_general_error\">bdb_crdt_src_syncer_general_error</a></td>\n        <td>object</td>\n        <td>\n          Active-active source - sync encountered in general error<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specglobalconfigurationsalertsettingsbdb_high_latency\">bdb_high_latency</a></td>\n        <td>object</td>\n        <td>\n          Latency is higher than specified threshold value [micro-sec]<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specglobalconfigurationsalertsettingsbdb_high_throughput\">bdb_high_throughput</a></td>\n        <td>object</td>\n        <td>\n          Throughput is higher than specified threshold value [requests / sec.]<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specglobalconfigurationsalertsettingsbdb_long_running_action\">bdb_long_running_action</a></td>\n        <td>object</td>\n        <td>\n          An alert for state-machines that are running for too long<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specglobalconfigurationsalertsettingsbdb_low_throughput\">bdb_low_throughput</a></td>\n        <td>object</td>\n        <td>\n          Throughput is lower than specified threshold value [requests / sec.]<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specglobalconfigurationsalertsettingsbdb_proxy_cert_expiring_soon\">bdb_proxy_cert_expiring_soon</a></td>\n        <td>object</td>\n        <td>\n          Proxy certificate will expire in less than specified threshold value [days]<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specglobalconfigurationsalertsettingsbdb_ram_dataset_overhead\">bdb_ram_dataset_overhead</a></td>\n        <td>object</td>\n        <td>\n          Dataset RAM overhead of a shard has reached the threshold value [% of its RAM limit]<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specglobalconfigurationsalertsettingsbdb_ram_values\">bdb_ram_values</a></td>\n        <td>object</td>\n        <td>\n          Percent of values kept in a shard's RAM is lower than [% of its key count]<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specglobalconfigurationsalertsettingsbdb_replica_src_high_syncer_lag\">bdb_replica_src_high_syncer_lag</a></td>\n        <td>object</td>\n        <td>\n          Replica-of source - sync lag is higher than specified threshold value [seconds]<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specglobalconfigurationsalertsettingsbdb_replica_src_syncer_connection_error\">bdb_replica_src_syncer_connection_error</a></td>\n        <td>object</td>\n        <td>\n          Replica-of source - sync has connection error while trying to connect replica source<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specglobalconfigurationsalertsettingsbdb_shard_num_ram_values\">bdb_shard_num_ram_values</a></td>\n        <td>object</td>\n        <td>\n          Number of values kept in a shard's RAM is lower than [values]<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specglobalconfigurationsalertsettingsbdb_size\">bdb_size</a></td>\n        <td>object</td>\n        <td>\n          Dataset size has reached the threshold value [% of the memory limit]<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specglobalconfigurationsalertsettingsbdb_backup_delayed","title":"spec.globalConfigurations.alertSettings.bdb_backup_delayed","role":"content","text":"<sup><sup>[↩ Parent](#specglobalconfigurationsalertsettings)</sup></sup>\n\nPeriodic backup has been delayed for longer than specified threshold value [minutes]\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Alert enabled or disabled<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>threshold</td>\n        <td>string</td>\n        <td>\n          Threshold for alert going on/off<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specglobalconfigurationsalertsettingsbdb_crdt_src_high_syncer_lag","title":"spec.globalConfigurations.alertSettings.bdb_crdt_src_high_syncer_lag","role":"content","text":"<sup><sup>[↩ Parent](#specglobalconfigurationsalertsettings)</sup></sup>\n\nActive-active source - sync lag is higher than specified threshold value [seconds]\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Alert enabled or disabled<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>threshold</td>\n        <td>string</td>\n        <td>\n          Threshold for alert going on/off<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specglobalconfigurationsalertsettingsbdb_crdt_src_syncer_connection_error","title":"spec.globalConfigurations.alertSettings.bdb_crdt_src_syncer_connection_error","role":"content","text":"<sup><sup>[↩ Parent](#specglobalconfigurationsalertsettings)</sup></sup>\n\nActive-active source - sync has connection error while trying to connect replica source\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Alert enabled or disabled<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>threshold</td>\n        <td>string</td>\n        <td>\n          Threshold for alert going on/off<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specglobalconfigurationsalertsettingsbdb_crdt_src_syncer_general_error","title":"spec.globalConfigurations.alertSettings.bdb_crdt_src_syncer_general_error","role":"content","text":"<sup><sup>[↩ Parent](#specglobalconfigurationsalertsettings)</sup></sup>\n\nActive-active source - sync encountered in general error\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Alert enabled or disabled<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>threshold</td>\n        <td>string</td>\n        <td>\n          Threshold for alert going on/off<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specglobalconfigurationsalertsettingsbdb_high_latency","title":"spec.globalConfigurations.alertSettings.bdb_high_latency","role":"content","text":"<sup><sup>[↩ Parent](#specglobalconfigurationsalertsettings)</sup></sup>\n\nLatency is higher than specified threshold value [micro-sec]\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Alert enabled or disabled<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>threshold</td>\n        <td>string</td>\n        <td>\n          Threshold for alert going on/off<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specglobalconfigurationsalertsettingsbdb_high_throughput","title":"spec.globalConfigurations.alertSettings.bdb_high_throughput","role":"content","text":"<sup><sup>[↩ Parent](#specglobalconfigurationsalertsettings)</sup></sup>\n\nThroughput is higher than specified threshold value [requests / sec.]\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Alert enabled or disabled<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>threshold</td>\n        <td>string</td>\n        <td>\n          Threshold for alert going on/off<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specglobalconfigurationsalertsettingsbdb_long_running_action","title":"spec.globalConfigurations.alertSettings.bdb_long_running_action","role":"content","text":"<sup><sup>[↩ Parent](#specglobalconfigurationsalertsettings)</sup></sup>\n\nAn alert for state-machines that are running for too long\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Alert enabled or disabled<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>threshold</td>\n        <td>string</td>\n        <td>\n          Threshold for alert going on/off<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specglobalconfigurationsalertsettingsbdb_low_throughput","title":"spec.globalConfigurations.alertSettings.bdb_low_throughput","role":"content","text":"<sup><sup>[↩ Parent](#specglobalconfigurationsalertsettings)</sup></sup>\n\nThroughput is lower than specified threshold value [requests / sec.]\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Alert enabled or disabled<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>threshold</td>\n        <td>string</td>\n        <td>\n          Threshold for alert going on/off<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specglobalconfigurationsalertsettingsbdb_proxy_cert_expiring_soon","title":"spec.globalConfigurations.alertSettings.bdb_proxy_cert_expiring_soon","role":"content","text":"<sup><sup>[↩ Parent](#specglobalconfigurationsalertsettings)</sup></sup>\n\nProxy certificate will expire in less than specified threshold value [days]\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Alert enabled or disabled<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>threshold</td>\n        <td>string</td>\n        <td>\n          Threshold for alert going on/off<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specglobalconfigurationsalertsettingsbdb_ram_dataset_overhead","title":"spec.globalConfigurations.alertSettings.bdb_ram_dataset_overhead","role":"content","text":"<sup><sup>[↩ Parent](#specglobalconfigurationsalertsettings)</sup></sup>\n\nDataset RAM overhead of a shard has reached the threshold value [% of its RAM limit]\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Alert enabled or disabled<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>threshold</td>\n        <td>string</td>\n        <td>\n          Threshold for alert going on/off<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specglobalconfigurationsalertsettingsbdb_ram_values","title":"spec.globalConfigurations.alertSettings.bdb_ram_values","role":"content","text":"<sup><sup>[↩ Parent](#specglobalconfigurationsalertsettings)</sup></sup>\n\nPercent of values kept in a shard's RAM is lower than [% of its key count]\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Alert enabled or disabled<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>threshold</td>\n        <td>string</td>\n        <td>\n          Threshold for alert going on/off<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specglobalconfigurationsalertsettingsbdb_replica_src_high_syncer_lag","title":"spec.globalConfigurations.alertSettings.bdb_replica_src_high_syncer_lag","role":"content","text":"<sup><sup>[↩ Parent](#specglobalconfigurationsalertsettings)</sup></sup>\n\nReplica-of source - sync lag is higher than specified threshold value [seconds]\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Alert enabled or disabled<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>threshold</td>\n        <td>string</td>\n        <td>\n          Threshold for alert going on/off<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specglobalconfigurationsalertsettingsbdb_replica_src_syncer_connection_error","title":"spec.globalConfigurations.alertSettings.bdb_replica_src_syncer_connection_error","role":"content","text":"<sup><sup>[↩ Parent](#specglobalconfigurationsalertsettings)</sup></sup>\n\nReplica-of source - sync has connection error while trying to connect replica source\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Alert enabled or disabled<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>threshold</td>\n        <td>string</td>\n        <td>\n          Threshold for alert going on/off<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specglobalconfigurationsalertsettingsbdb_shard_num_ram_values","title":"spec.globalConfigurations.alertSettings.bdb_shard_num_ram_values","role":"content","text":"<sup><sup>[↩ Parent](#specglobalconfigurationsalertsettings)</sup></sup>\n\nNumber of values kept in a shard's RAM is lower than [values]\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Alert enabled or disabled<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>threshold</td>\n        <td>string</td>\n        <td>\n          Threshold for alert going on/off<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specglobalconfigurationsalertsettingsbdb_size","title":"spec.globalConfigurations.alertSettings.bdb_size","role":"content","text":"<sup><sup>[↩ Parent](#specglobalconfigurationsalertsettings)</sup></sup>\n\nDataset size has reached the threshold value [% of the memory limit]\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Alert enabled or disabled<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>threshold</td>\n        <td>string</td>\n        <td>\n          Threshold for alert going on/off<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specglobalconfigurationsauditing","title":"spec.globalConfigurations.auditing","role":"content","text":"<sup><sup>[↩ Parent](#specglobalconfigurations)</sup></sup>\n\nDatabase auditing configuration.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>dbConnsAuditing</td>\n        <td>boolean</td>\n        <td>\n          Deprecated: use auditMode instead.\nEnables auditing of database connection and authentication events.\nWhen enabled, connection, authentication, and disconnection events are tracked and sent\nto the configured audit listener (configured at the cluster level).\nThe cluster-level auditing configuration must be set before enabling this on a database.\nSetting dbConnsAuditing to true is equivalent to auditMode \"connection\".\nIf both dbConnsAuditing and auditMode are set, auditMode takes precedence.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specglobalconfigurationsbackup","title":"spec.globalConfigurations.backup","role":"content","text":"<sup><sup>[↩ Parent](#specglobalconfigurations)</sup></sup>\n\nTarget for automatic database backups.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td><a href=\"#specglobalconfigurationsbackupabs\">abs</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specglobalconfigurationsbackupftp\">ftp</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specglobalconfigurationsbackupgcs\">gcs</a></td>\n        <td>object</td>\n        <td>\n          GoogleStorage<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>interval</td>\n        <td>integer</td>\n        <td>\n          Backup Interval in seconds. Specifies the time interval in seconds at which periodic backup is performed.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>intervalOffset</td>\n        <td>integer</td>\n        <td>\n          Backup Interval Offset in seconds. Specifies a time offset in seconds at which the periodic backup job starts. This can only be used if the backup interval is 24 hours (86400 seconds) or 12 hours (43200 seconds). The offset is relative to 00:00 UTC for 24-hour mode, and 00:00 + 12:00 UTC for 12-hour mode. If not specified, a random starting time (offset) is automatically chosen. The offset must be less than the backup interval.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specglobalconfigurationsbackupmount\">mount</a></td>\n        <td>object</td>\n        <td>\n          MountPointStorage<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specglobalconfigurationsbackups3\">s3</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specglobalconfigurationsbackupsftp\">sftp</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specglobalconfigurationsbackupswift\">swift</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specglobalconfigurationsbackupabs","title":"spec.globalConfigurations.backup.abs","role":"content","text":"<sup><sup>[↩ Parent](#specglobalconfigurationsbackup)</sup></sup>\n\n\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>absSecretName</td>\n        <td>string</td>\n        <td>\n          The name of the secret that holds ABS credentials. The secret must contain the keys \"AccountName\" and \"AccountKey\", and these must hold the corresponding credentials<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>container</td>\n        <td>string</td>\n        <td>\n          Azure Blob Storage container name.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>subdir</td>\n        <td>string</td>\n        <td>\n          Optional. Azure Blob Storage subdir under container.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specglobalconfigurationsbackupftp","title":"spec.globalConfigurations.backup.ftp","role":"content","text":"<sup><sup>[↩ Parent](#specglobalconfigurationsbackup)</sup></sup>\n\n\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>url</td>\n        <td>string</td>\n        <td>\n          a URI of the \"ftps://[USER[:PASSWORD]@]HOST[:PORT]/PATH[/]\" format<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specglobalconfigurationsbackupgcs","title":"spec.globalConfigurations.backup.gcs","role":"content","text":"<sup><sup>[↩ Parent](#specglobalconfigurationsbackup)</sup></sup>\n\nGoogleStorage\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>bucketName</td>\n        <td>string</td>\n        <td>\n          Google Storage bucket name.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>gcsSecretName</td>\n        <td>string</td>\n        <td>\n          The name of the secret that holds the Google Cloud Storage credentials. The secret must contain the keys \"CLIENT_ID\", \"PRIVATE_KEY\", \"PRIVATE_KEY_ID\", \"CLIENT_EMAIL\" and these must hold the corresponding credentials. The keys should correspond to the values in the key JSON.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>subdir</td>\n        <td>string</td>\n        <td>\n          Optional. Google Storage subdir under bucket.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specglobalconfigurationsbackupmount","title":"spec.globalConfigurations.backup.mount","role":"content","text":"<sup><sup>[↩ Parent](#specglobalconfigurationsbackup)</sup></sup>\n\nMountPointStorage\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>path</td>\n        <td>string</td>\n        <td>\n          Path to the local mount point. You must create the mount point on all nodes, and the redislabs:redislabs user must have read and write permissions on the local mount point.<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specglobalconfigurationsbackups3","title":"spec.globalConfigurations.backup.s3","role":"content","text":"<sup><sup>[↩ Parent](#specglobalconfigurationsbackup)</sup></sup>\n\n\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>awsSecretName</td>\n        <td>string</td>\n        <td>\n          The name of the secret that holds the AWS credentials. The secret must contain the keys \"AWS_ACCESS_KEY_ID\" and \"AWS_SECRET_ACCESS_KEY\", and these must hold the corresponding credentials.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>bucketName</td>\n        <td>string</td>\n        <td>\n          Amazon S3 bucket name.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>regionName</td>\n        <td>string</td>\n        <td>\n          Optional. Amazon S3 region name. If not specified, the region is auto-detected using a HEAD request to the bucket. For AWS GovCloud or other regions where auto-detection may not work, specify the region explicitly (e.g., \"us-gov-east-1\").<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>subdir</td>\n        <td>string</td>\n        <td>\n          Optional. Amazon S3 subdir under bucket.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specglobalconfigurationsbackupsftp","title":"spec.globalConfigurations.backup.sftp","role":"content","text":"<sup><sup>[↩ Parent](#specglobalconfigurationsbackup)</sup></sup>\n\n\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>sftpSecretName</td>\n        <td>string</td>\n        <td>\n          The name of the secret that holds SFTP credentials. The secret must contain the \"Key\" key, which is the SSH private key for connecting to the sftp server.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>sftp_url</td>\n        <td>string</td>\n        <td>\n          SFTP url<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specglobalconfigurationsbackupswift","title":"spec.globalConfigurations.backup.swift","role":"content","text":"<sup><sup>[↩ Parent](#specglobalconfigurationsbackup)</sup></sup>\n\n\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>auth_url</td>\n        <td>string</td>\n        <td>\n          Swift service authentication URL.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>container</td>\n        <td>string</td>\n        <td>\n          Swift object store container for storing the backup files.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>swiftSecretName</td>\n        <td>string</td>\n        <td>\n          The name of the secret that holds Swift credentials. The secret must contain the keys \"Key\" and \"User\", and these must hold the corresponding credentials: service access key and service user name (pattern for the latter does not allow special characters &,<,>,\")<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>prefix</td>\n        <td>string</td>\n        <td>\n          Optional. Prefix (path) of backup files in the swift container.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specglobalconfigurationsconnectionsettings","title":"spec.globalConfigurations.connectionSettings","role":"content","text":"<sup><sup>[↩ Parent](#specglobalconfigurations)</sup></sup>\n\nConnection-related settings such as proxy connections and scheduling policy.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>connectionLimitType</td>\n        <td>enum</td>\n        <td>\n          Connections limit type.<br/>\n          <br/>\n            <i>Enum</i>: per-thread, per-shard<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>connectionSchedulingPolicy</td>\n        <td>enum</td>\n        <td>\n          Controls how server-side connections are used when forwarding traffic to shards. Values: cmp: Closest to max_pipelined policy. Pick the connection with the most pipelined commands that has not reached the max_pipelined limit. mru: Try to use most recently used connections. spread: Try to use all connections. mnp: Minimal pipeline policy. Pick the connection with the least pipelined commands.<br/>\n          <br/>\n            <i>Enum</i>: cmp, mru, spread, mnp<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>globalMaxDedicatedConnections</td>\n        <td>integer</td>\n        <td>\n          Defines the maximum number of dedicated server connections for a given database. The total number across all workers. The default is 0 for unlimited. Defaults to 0, which means unlimited.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>internalConnections</td>\n        <td>integer</td>\n        <td>\n          The number of internal proxy connections. Defaults to 5 if unspecified.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>minDedicatedConnections</td>\n        <td>integer</td>\n        <td>\n          Number of dedicated server connections the DMC has per worker per shard. Defaults to 2 if unspecified.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specglobalconfigurationsmoduleslist","title":"spec.globalConfigurations.modulesList[]","role":"content","text":"<sup><sup>[↩ Parent](#specglobalconfigurations)</sup></sup>\n\nRedis Enterprise module (see https://redis.io/docs/latest/develop/reference/modules/)\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>name</td>\n        <td>string</td>\n        <td>\n          The name of the module, e.g. \"search\" or \"ReJSON\". The complete list of modules available in the cluster can be retrieved from the '.status.modules' field in the REC.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>config</td>\n        <td>string</td>\n        <td>\n          Module command line arguments e.g. VKEY_MAX_ENTITY_COUNT 30 30<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>version</td>\n        <td>string</td>\n        <td>\n          The semantic version of the module, e.g. '1.6.12'. Optional for REDB, must be set for REAADB. Note that this field is deprecated, and will be removed in future releases.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specglobalconfigurationsossclustersettings","title":"spec.globalConfigurations.ossClusterSettings","role":"content","text":"<sup><sup>[↩ Parent](#specglobalconfigurations)</sup></sup>\n\nAdditional OSS cluster settings that may be provided to tweak OSS cluster behavior.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enableExternalAccess</td>\n        <td>boolean</td>\n        <td>\n          Toggles whether this database supports external access in OSS cluster mode. When enabled, advertised database topology includes the external endpoints for the Redis Enterprise nodes hosting the database shards. The external access mechanism (e.g., LoadBalancer services) is configured via the ossClusterSettings.externalAccessType field of the RedisEnterpriseCluster. When external access is enabled, the corresponding database secret will have the list of primary shard IPs in the oss_startup_nodes field. Currently enabling OSS cluster API in conjunction with external access is not supported for active active databases.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specglobalconfigurationsredisenterprisecluster","title":"spec.globalConfigurations.redisEnterpriseCluster","role":"content","text":"<sup><sup>[↩ Parent](#specglobalconfigurations)</sup></sup>\n\nConnection to Redis Enterprise Cluster\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>name</td>\n        <td>string</td>\n        <td>\n          The name of the Redis Enterprise Cluster where the database should be stored.<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specglobalconfigurationsreplicasources","title":"spec.globalConfigurations.replicaSources[]","role":"content","text":"<sup><sup>[↩ Parent](#specglobalconfigurations)</sup></sup>\n\n\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>replicaSourceName</td>\n        <td>string</td>\n        <td>\n          The name of the resource from which the source database URI is derived. The type of resource must match the type specified in the ReplicaSourceType field.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>replicaSourceType</td>\n        <td>string</td>\n        <td>\n          The type of resource from which the source database URI is derived. If set to 'SECRET', the source database URI is derived from the secret named in the ReplicaSourceName field. The secret must have a key named 'uri' that defines the URI of the source database in the form of 'redis://...'. The type of secret (kubernetes, vault, ...) is determined by the secret mechanism used by the underlying REC object. If set to 'REDB', the source database URI is derived from the RedisEnterpriseDatabase resource named in the ReplicaSourceName field.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>clientKeySecret</td>\n        <td>string</td>\n        <td>\n          Secret that defines the client certificate and key used by the syncer in the target database cluster. The secret must have 2 keys in its map: \"cert\" which is the PEM encoded certificate, and \"key\" which is the PEM encoded private key.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>compression</td>\n        <td>integer</td>\n        <td>\n          GZIP compression level (0-6) to use for replication.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>serverCertSecret</td>\n        <td>string</td>\n        <td>\n          Secret that defines the server certificate used by the proxy in the source database cluster. The secret must have 1 key in its map: \"cert\" which is the PEM encoded certificate.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>tlsSniName</td>\n        <td>string</td>\n        <td>\n          TLS SNI name to use for the replication link.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specglobalconfigurationsrolespermissions","title":"spec.globalConfigurations.rolesPermissions[]","role":"content","text":"<sup><sup>[↩ Parent](#specglobalconfigurations)</sup></sup>\n\nRedis Enterprise Role and ACL Binding\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>acl</td>\n        <td>string</td>\n        <td>\n          Acl Name of RolePermissionType (note: use exact name of the ACL from the Redis Enterprise ACL list, case sensitive)<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>role</td>\n        <td>string</td>\n        <td>\n          Role Name of RolePermissionType (note: use exact name of the role from the Redis Enterprise role list, case sensitive)<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>type</td>\n        <td>enum</td>\n        <td>\n          Type of Redis Enterprise Database Role Permission. Currently, only \"redis-enterprise\" is supported, which uses roles and ACLs defined within Redis Enterprise directly.<br/>\n          <br/>\n            <i>Enum</i>: redis-enterprise<br/>\n            <i>Default</i>: redis-enterprise<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specglobalconfigurationsupgradespec","title":"spec.globalConfigurations.upgradeSpec","role":"content","text":"<sup><sup>[↩ Parent](#specglobalconfigurations)</sup></sup>\n\nSpecifications for DB upgrade.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>upgradeModulesToLatest</td>\n        <td>boolean</td>\n        <td>\n          Upgrades the modules to the latest version that supports the DB version during a DB upgrade action, to upgrade the DB version view the 'redisVersion' field. Note - This field is currently not supported for Active-Active databases.<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specredisenterprisecluster","title":"spec.redisEnterpriseCluster","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nConnection to the Redis Enterprise Cluster.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>name</td>\n        <td>string</td>\n        <td>\n          The name of the Redis Enterprise Cluster where the database should be stored.<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"status","title":"status","role":"content","text":"<sup><sup>[↩ Parent](#)</sup></sup>\n\nDefines the observed state of RedisEnterpriseActiveActiveDatabase.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>clusterCertificatesGeneration</td>\n        <td>integer</td>\n        <td>\n          Certificate generation number from the participating cluster's REC.Status.CertificatesStatus.Generation. The operator monitors this field to detect when proxy or syncer certificates are updated on the local participating cluster. When the operator detects a change, it automatically runs a CRDB force update (equivalent to 'crdb-cli crdb update --force'), which synchronizes the certificate changes to all participating clusters and prevents sync issues. This eliminates the manual step of running crdb-cli commands when you rotate certificates in Active-Active deployments on Kubernetes.<br/>\n          <br/>\n            <i>Format</i>: int64<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>guid</td>\n        <td>string</td>\n        <td>\n          GUID of the Active-Active database.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>lastTaskUid</td>\n        <td>string</td>\n        <td>\n          UID of the last Active-Active database task.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>linkedRedbs</td>\n        <td>[]string</td>\n        <td>\n          List of linked REDBs.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>managedTags</td>\n        <td>[]string</td>\n        <td>\n          List of user-declared tag keys (from spec.globalConfigurations.tags) that were last successfully reconciled to the Active-Active database's BDBs (default config and every participating-cluster instance). Used to detect tag deletions on the next reconcile: keys present here but absent from the current spec.globalConfigurations.tags are removed from all BDB tag sets. Operator-internal keys (managed_by, redb_resource, global_configurations_spec, etc.) are not tracked here.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#statusparticipatingclusters\">participatingClusters</a></td>\n        <td>[]object</td>\n        <td>\n          List of participating cluster statuses.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>redisEnterpriseCluster</td>\n        <td>string</td>\n        <td>\n          Name of the Redis Enterprise Cluster that this resource is associated with.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>replicationStatus</td>\n        <td>enum</td>\n        <td>\n          Overall replication status.<br/>\n          <br/>\n            <i>Enum</i>: up, down<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#statussecretsstatus\">secretsStatus</a></td>\n        <td>[]object</td>\n        <td>\n          Status of the secrets.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>specStatus</td>\n        <td>string</td>\n        <td>\n          Indicates whether the desired specification is valid.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>status</td>\n        <td>string</td>\n        <td>\n          Status of the Active-Active database. This status doesn't include the replication link (data path) status. To view the replication link status, see the ReplicationStatus field or status.replicationStatus on the custom resource.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"statusparticipatingclusters","title":"status.participatingClusters[]","role":"content","text":"<sup><sup>[↩ Parent](#status)</sup></sup>\n\nThe status of a participating cluster.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>name</td>\n        <td>string</td>\n        <td>\n          Name of the linked remote cluster custom resource.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>id</td>\n        <td>integer</td>\n        <td>\n          ID of the instance in the Active-Active database.<br/>\n          <br/>\n            <i>Format</i>: int64<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>replicationStatus</td>\n        <td>enum</td>\n        <td>\n          Replication status of the participating cluster.<br/>\n          <br/>\n            <i>Enum</i>: up, down<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"statussecretsstatus","title":"status.secretsStatus[]","role":"content","text":"<sup><sup>[↩ Parent](#status)</sup></sup>\n\nThe status of a secret.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>name</td>\n        <td>string</td>\n        <td>\n          Name of the secret.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>status</td>\n        <td>enum</td>\n        <td>\n          Status of the secret.<br/>\n          <br/>\n            <i>Enum</i>: Valid, Invalid<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/reference/api/redis_enterprise_cluster_api","title":"RedisEnterpriseCluster API Reference","url":"https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api/","summary":"","tags":["docs","operate","kubernetes"],"last_updated":"2026-07-21T12:02:16-05:00","page_type":"content","content_hash":"d8eea5770b4c4ee6c83f4a38207a36041a606bc19dea3839bf3c969e59cd5516","sections":[{"id":"overview","title":"Overview","role":"overview","text":"apiVersion:\n\n\n- [app.redislabs.com/v1](#appredislabscomv1)\n\n\n\n\n# app.redislabs.com/v1\n\n\n\n\nRedisEnterpriseCluster is the Schema for the redisenterpriseclusters API\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n      <td>apiVersion</td>\n      <td>string</td>\n      <td>app.redislabs.com/v1</td>\n      <td>true</td>\n      </tr>\n      <tr>\n      <td>kind</td>\n      <td>string</td>\n      <td>RedisEnterpriseCluster</td>\n      <td>true</td>\n      </tr>\n      <tr>\n      <td><a href=\"https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#objectmeta-v1-meta\">metadata</a></td>\n      <td>object</td>\n      <td>Refer to the Kubernetes API documentation for the fields of the `metadata` field.</td>\n      <td>true</td>\n      </tr><tr>\n        <td><a href=\"#spec\">spec</a></td>\n        <td>object</td>\n        <td>\n          RedisEnterpriseClusterSpec defines the desired state of RedisEnterpriseCluster<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#status\">status</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"spec","title":"spec","role":"content","text":"<sup><sup>[↩ Parent](#)</sup></sup>\n\nRedisEnterpriseClusterSpec defines the desired state of RedisEnterpriseCluster\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td><a href=\"#specaccesscontrol\">accessControl</a></td>\n        <td>object</td>\n        <td>\n          Access control configuration for the cluster.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specactiveactive\">activeActive</a></td>\n        <td>object</td>\n        <td>\n          Specification for ActiveActive setup. At most one of ingressOrRouteSpec or activeActive fields can be set at the same time.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>antiAffinityAdditionalTopologyKeys</td>\n        <td>[]string</td>\n        <td>\n          Additional antiAffinity terms in order to support installation on different zones/vcenters<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specauditing\">auditing</a></td>\n        <td>object</td>\n        <td>\n          Cluster-level configuration for auditing database connection and authentication events. Includes both the audit listener connection parameters and the default policy for new databases.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specbackup\">backup</a></td>\n        <td>object</td>\n        <td>\n          Cluster-wide backup configurations<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specbootstrapperimagespec\">bootstrapperImageSpec</a></td>\n        <td>object</td>\n        <td>\n          Specification for Bootstrapper container image<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specbootstrapperresources\">bootstrapperResources</a></td>\n        <td>object</td>\n        <td>\n          Compute resource requirements for bootstrapper containers<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#speccertificates\">certificates</a></td>\n        <td>object</td>\n        <td>\n          RS Cluster Certificates. Used to modify the certificates used by the cluster. See the \"RSClusterCertificates\" struct described above to see the supported certificates.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>clusterCredentialSecretName</td>\n        <td>string</td>\n        <td>\n          Name or path of the secret containing cluster credentials. Defaults to the cluster name if left blank. For Kubernetes secrets (default): Can be customized to any valid secret name, or left blank to use the cluster name. The secret can be pre-created with 'username' and 'password' fields, or otherwise it will be automatically created with a default username and auto-generated password. On running clusters, this field can be changed to point to a different existing secret. The new secret must exist, contain valid 'username' and 'password' fields, and the credentials must work with the RS cluster. For Vault secrets: Can be customized with the path of the secret within Vault. The secret must be pre-created in Vault.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>clusterCredentialSecretRole</td>\n        <td>string</td>\n        <td>\n          Used only if ClusterCredentialSecretType is vault, to define vault role to be used.  If blank, defaults to \"redis-enterprise-operator\"<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>clusterCredentialSecretType</td>\n        <td>enum</td>\n        <td>\n          Type of Secret to use for ClusterCredential, Vault, Kuberetes,... If left blank, will default ot kubernetes secrets<br/>\n          <br/>\n            <i>Enum</i>: vault, kubernetes<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>clusterRecovery</td>\n        <td>boolean</td>\n        <td>\n          ClusterRecovery initiates cluster recovery when set to true. Note that this field is cleared automatically after the cluster is recovered<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#speccontainertimezone\">containerTimezone</a></td>\n        <td>object</td>\n        <td>\n          Container timezone configuration. While the default timezone on all containers is UTC, this setting can be used to set the timezone on services rigger/bootstrapper/RS containers. You can either propagate the hosts timezone to RS pods or set it manually via timezoneName.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>createServiceAccount</td>\n        <td>boolean</td>\n        <td>\n          Creates a service account for Redis Enterprise.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>dataInternodeEncryption</td>\n        <td>boolean</td>\n        <td>\n          Internode encryption (INE) cluster wide policy. An optional boolean setting. Specifies if INE should be on/off for new created REDBs. May be overridden for specific REDB via similar setting, please view the similar setting for REDB for more info.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>dmcExternalCBAAuthentication</td>\n        <td>boolean</td>\n        <td>\n          Whether Certificate-Based Authentication (CBA) for client connections to databases is delegated to an external gRPC authentication service, instead of being handled locally inside DMC (the data-path management proxy). If omitted, this field is evaluated as false. Possible values: true/false<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>encryptPkeys</td>\n        <td>boolean</td>\n        <td>\n          Private key encryption Possible values: true/false<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>enforceIPv4</td>\n        <td>boolean</td>\n        <td>\n          Sets ENFORCE_IPV4 environment variable<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specextraenvvars\">extraEnvVars</a></td>\n        <td>[]object</td>\n        <td>\n          ADVANCED USAGE: use carefully. Add environment variables to RS StatefulSet's containers.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>extraLabels</td>\n        <td>map[string]string</td>\n        <td>\n          Labels that the user defines for their convenience<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#spechostaliases\">hostAliases</a></td>\n        <td>[]object</td>\n        <td>\n          Adds hostAliases entries to the Redis Enterprise pods<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specingressorroutespec\">ingressOrRouteSpec</a></td>\n        <td>object</td>\n        <td>\n          Access configurations for the Redis Enterprise cluster and databases. At most one of ingressOrRouteSpec or activeActive fields can be set at the same time.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specldap\">ldap</a></td>\n        <td>object</td>\n        <td>\n          Cluster-level LDAP configuration, such as server addresses, protocol, authentication and query settings.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>license</td>\n        <td>string</td>\n        <td>\n          Redis Enterprise License<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>licenseSecretName</td>\n        <td>string</td>\n        <td>\n          K8s secret or Vault Secret Name/Path to use for Cluster License. When left blank, the license is read from the \"license\" field. Note that you can't specify non-empty values in both \"license\" and \"licenseSecretName\", only one of these fields can be used to pass the license string. The license needs to be stored under the key \"license\".<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specmetricsconfig\">metricsConfig</a></td>\n        <td>object</td>\n        <td>\n          Cluster-level configuration for metrics. Currently controls which database tag keys may be exposed as labels on Redis Software metrics.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>nodeSelector</td>\n        <td>map[string]string</td>\n        <td>\n          Node selector for scheduling pods on specific nodes. This applies to all pods managed by the operator: Redis Enterprise nodes, Services Rigger, and Call Home Client.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>nodes</td>\n        <td>integer</td>\n        <td>\n          Number of Redis Enterprise nodes (pods)<br/>\n          <br/>\n            <i>Format</i>: int32<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specocspconfiguration\">ocspConfiguration</a></td>\n        <td>object</td>\n        <td>\n          An API object that represents the cluster's OCSP configuration. To enable OCSP, the cluster's proxy certificate should contain the OCSP responder URL.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specossclustersettings\">ossClusterSettings</a></td>\n        <td>object</td>\n        <td>\n          Cluster-level configuration for OSS cluster mode databases.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specpersistentspec\">persistentSpec</a></td>\n        <td>object</td>\n        <td>\n          Persistent storage configuration for Redis Enterprise cluster.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>podAnnotations</td>\n        <td>map[string]string</td>\n        <td>\n          annotations for the service rigger and redis enterprise pods<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specpodantiaffinity\">podAntiAffinity</a></td>\n        <td>object</td>\n        <td>\n          Override for the default anti-affinity rules of the Redis Enterprise pods. More info: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#an-example-of-a-pod-that-uses-pod-affinity<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specpodstartingpolicy\">podStartingPolicy</a></td>\n        <td>object</td>\n        <td>\n          Mitigation setting for STS pods stuck in \"ContainerCreating\"<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specpodtolerations\">podTolerations</a></td>\n        <td>[]object</td>\n        <td>\n          Tolerations that are added to all managed pods. More information: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>priorityClassName</td>\n        <td>string</td>\n        <td>\n          Adds the priority class to pods managed by the operator<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specpullsecrets\">pullSecrets</a></td>\n        <td>[]object</td>\n        <td>\n          PullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images. If specified, these secrets will be passed to individual puller implementations for them to use. More info: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>rackAwarenessNodeLabel</td>\n        <td>string</td>\n        <td>\n          Node label that specifies rack ID - if specified, will create rack aware cluster. Rack awareness requires node label must exist on all nodes. Additionally, operator needs a special cluster role with permission to list nodes.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specredisenterpriseadditionalpodspecattributes\">redisEnterpriseAdditionalPodSpecAttributes</a></td>\n        <td>object</td>\n        <td>\n          ADVANCED USAGE USE AT YOUR OWN RISK -\nspecify pod attributes that are required for the statefulset - Redis Enterprise pods.\nPod attributes managed by the operator might override these settings.\nAlso make sure the attributes are supported by the K8s version running on the cluster -\nthe operator does not validate that.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>redisEnterpriseIPFamily</td>\n        <td>enum</td>\n        <td>\n          Reserved, future use, only for use if instructed by Redis. IPFamily dictates what IP family to choose for pods' internal and external communication.<br/>\n          <br/>\n            <i>Enum</i>: IPv4, IPv6<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specredisenterpriseimagespec\">redisEnterpriseImageSpec</a></td>\n        <td>object</td>\n        <td>\n          Specification for Redis Enterprise container image<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specredisenterprisenoderesources\">redisEnterpriseNodeResources</a></td>\n        <td>object</td>\n        <td>\n          Compute resource requirements for Redis Enterprise containers<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>redisEnterprisePodAnnotations</td>\n        <td>map[string]string</td>\n        <td>\n          annotations for redis enterprise pod<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specredisenterpriseservicesconfiguration\">redisEnterpriseServicesConfiguration</a></td>\n        <td>object</td>\n        <td>\n          RS Cluster optional services settings<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specredisenterpriseservicesriggerimagespec\">redisEnterpriseServicesRiggerImageSpec</a></td>\n        <td>object</td>\n        <td>\n          Specification for Services Rigger container image<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specredisenterpriseservicesriggerresources\">redisEnterpriseServicesRiggerResources</a></td>\n        <td>object</td>\n        <td>\n          Compute resource requirements for Services Rigger pod<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>redisEnterpriseTerminationGracePeriodSeconds</td>\n        <td>integer</td>\n        <td>\n          The TerminationGracePeriodSeconds value for the (STS created) REC pods<br/>\n          <br/>\n            <i>Format</i>: int64<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specredisenterprisevolumemounts\">redisEnterpriseVolumeMounts</a></td>\n        <td>[]object</td>\n        <td>\n          additional volume mounts within the redis enterprise containers. More info: https://kubernetes.io/docs/concepts/storage/volumes/<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specredisonflashspec\">redisOnFlashSpec</a></td>\n        <td>object</td>\n        <td>\n          Auto Tiering (Redis on Flash) configuration. When provided, the cluster can create Auto Tiering databases.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>redisUpgradePolicy</td>\n        <td>enum</td>\n        <td>\n          Redis upgrade policy to be set on the Redis Enterprise cluster. Possible values: major/latest This value is used by the cluster to choose the Redis version of the database when an upgrade is performed. The Redis Enterprise cluster includes multiple versions of OSS Redis that can be used for databases.<br/>\n          <br/>\n            <i>Enum</i>: major, latest<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>resp3Default</td>\n        <td>boolean</td>\n        <td>\n          Whether databases will turn on RESP3 compatibility upon database upgrade. Note - Deleting this property after explicitly setting its value shall have no effect. Please view the corresponding field in RS doc for more info.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specsecuritycontext\">securityContext</a></td>\n        <td>object</td>\n        <td>\n          The security configuration that will be applied to RS pods.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>serviceAccountName</td>\n        <td>string</td>\n        <td>\n          Name of the service account to use for Redis Enterprise.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specservices\">services</a></td>\n        <td>object</td>\n        <td>\n          Customization options for operator-managed service resources created for Redis Enterprise clusters and databases<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specservicesriggerspec\">servicesRiggerSpec</a></td>\n        <td>object</td>\n        <td>\n          Specification for service rigger<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specsidecontainersspec\">sideContainersSpec</a></td>\n        <td>[]object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specslaveha\">slaveHA</a></td>\n        <td>object</td>\n        <td>\n          Slave high availability mechanism configuration.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specsso\">sso</a></td>\n        <td>object</td>\n        <td>\n          Cluster-level SSO configuration for authentication to the Cluster Manager UI.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>uiAnnotations</td>\n        <td>map[string]string</td>\n        <td>\n          Additional annotations for the Redis Enterprise UI service. These annotations override overlapping global annotations set under spec.services.servicesAnnotations. The specified annotations will not override annotations that already exist and didn't originate from the operator, except for the 'redis.io/last-keys' annotation which is reserved.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>uiServiceType</td>\n        <td>enum</td>\n        <td>\n          Service type for exposing the Redis Enterprise UI (https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types).<br/>\n          <br/>\n            <i>Enum</i>: ClusterIP, NodePort, LoadBalancer, ExternalName<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specupgradespec\">upgradeSpec</a></td>\n        <td>object</td>\n        <td>\n          Redis Enterprise upgrade configuration<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specusagemeter\">usageMeter</a></td>\n        <td>object</td>\n        <td>\n          The configuration of the usage meter.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>usageReportFields</td>\n        <td>enum</td>\n        <td>\n          Controls which fields are included in the usage (call-home) report. \"all\" also includes node count and hostname; \"restricted\" sends the reduced report. Default is \"restricted\". Should not be changed unless instructed by Redis support.<br/>\n          <br/>\n            <i>Enum</i>: all, restricted<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specuserdefinedmodules\">userDefinedModules</a></td>\n        <td>[]object</td>\n        <td>\n          List of user-defined modules to be downloaded and installed during cluster bootstrap The modules on the list will be downloaded on cluster creation, upgrade, scale-out and recovery and installed on all nodes. Note that changing this field for a running cluster will trigger a rolling update.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>username</td>\n        <td>string</td>\n        <td>\n          Username for the admin user of Redis Enterprise<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>vaultCASecret</td>\n        <td>string</td>\n        <td>\n          K8s secret name containing Vault's CA cert - defaults to \"vault-ca-cert\"<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specvolumes\">volumes</a></td>\n        <td>[]object</td>\n        <td>\n          additional volumes<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specaccesscontrol","title":"spec.accessControl","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nAccess control configuration for the cluster.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td><a href=\"#specaccesscontrolpolicy\">policy</a></td>\n        <td>object</td>\n        <td>\n          Policy settings for access control behavior.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specaccesscontrolpolicy","title":"spec.accessControl.policy","role":"content","text":"<sup><sup>[↩ Parent](#specaccesscontrol)</sup></sup>\n\nPolicy settings for access control behavior.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>allowREDBRolesPermissions</td>\n        <td>boolean</td>\n        <td>\n          Controls whether the deprecated REDB spec.rolesPermissions field is reconciled. When enabled (default): both spec.rolesPermissions and RedisEnterpriseRole objects are reconciled. When disabled: only RedisEnterpriseRole objects are reconciled, spec.rolesPermissions is ignored.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specactiveactive","title":"spec.activeActive","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nSpecification for ActiveActive setup. At most one of ingressOrRouteSpec or activeActive fields can be set at the same time.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>apiIngressUrl</td>\n        <td>string</td>\n        <td>\n          RS API URL<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>dbIngressSuffix</td>\n        <td>string</td>\n        <td>\n          DB ENDPOINT SUFFIX - will be used to set the db host. ingress <db name><db ingress suffix> Creates a host name so it should be unique if more than one db is created on the cluster with the same name<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>method</td>\n        <td>enum</td>\n        <td>\n          Used to distinguish between different platforms implementation<br/>\n          <br/>\n            <i>Enum</i>: openShiftRoute, ingress<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>ingressAnnotations</td>\n        <td>map[string]string</td>\n        <td>\n          Used for ingress controllers such as ha-proxy or nginx in GKE<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specauditing","title":"spec.auditing","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nCluster-level configuration for auditing database connection and authentication events. Includes both the audit listener connection parameters and the default policy for new databases.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td><a href=\"#specauditingconfig\">config</a></td>\n        <td>object</td>\n        <td>\n          Configuration for the audit listener connection<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>dbConnsAuditing</td>\n        <td>boolean</td>\n        <td>\n          Cluster-wide default policy for database connection auditing. When set to true, connection auditing will be enabled by default for all new databases. Existing databases are not affected and can override this setting individually.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specauditingconfig","title":"spec.auditing.config","role":"content","text":"<sup><sup>[↩ Parent](#specauditing)</sup></sup>\n\nConfiguration for the audit listener connection\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>auditAddress</td>\n        <td>string</td>\n        <td>\n          TCP/IP address or file path where audit notifications will be sent. For TCP protocol: IP address of the audit listener. For local protocol: file path for audit output (development/testing only).<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>auditProtocol</td>\n        <td>enum</td>\n        <td>\n          Protocol used to send audit notifications. Valid values: \"TCP\" or \"local\". For production systems, use \"TCP\". \"local\" is for development/testing only.<br/>\n          <br/>\n            <i>Enum</i>: TCP, local<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>auditPort</td>\n        <td>integer</td>\n        <td>\n          Port number where audit notifications will be sent (TCP protocol only).<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>auditReconnectInterval</td>\n        <td>integer</td>\n        <td>\n          Interval in seconds between attempts to reconnect to the audit listener.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>auditReconnectMaxAttempts</td>\n        <td>integer</td>\n        <td>\n          Maximum number of attempts to reconnect to the audit listener. Set to 0 for infinite attempts.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specbackup","title":"spec.backup","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nCluster-wide backup configurations\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td><a href=\"#specbackups3\">s3</a></td>\n        <td>object</td>\n        <td>\n          Configurations for backups to s3 and s3-compatible storage<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specbackups3","title":"spec.backup.s3","role":"content","text":"<sup><sup>[↩ Parent](#specbackup)</sup></sup>\n\nConfigurations for backups to s3 and s3-compatible storage\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>caCertificateSecretName</td>\n        <td>string</td>\n        <td>\n          Secret name that holds the S3 CA certificate, which contains the TLS certificate mapped to the key in the secret 'cert'<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>url</td>\n        <td>string</td>\n        <td>\n          Specifies the URL for S3 export and import<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specbootstrapperimagespec","title":"spec.bootstrapperImageSpec","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nSpecification for Bootstrapper container image\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>digestHash</td>\n        <td>string</td>\n        <td>\n          The digest hash of the container image to pull. When specified, the container image is pulled according to the digest hash instead of the image tag. The versionTag field must also be specified with the image tag matching this digest hash. Note: This field is only supported for OLM deployments.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>imagePullPolicy</td>\n        <td>string</td>\n        <td>\n          The image pull policy to be applied to the container image. One of Always, Never, IfNotPresent.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>repository</td>\n        <td>string</td>\n        <td>\n          The repository (name) of the container image to be deployed.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>versionTag</td>\n        <td>string</td>\n        <td>\n          The tag of the container image to be deployed.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specbootstrapperresources","title":"spec.bootstrapperResources","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nCompute resource requirements for bootstrapper containers\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td><a href=\"#specbootstrapperresourcesclaims\">claims</a></td>\n        <td>[]object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>limits</td>\n        <td>map[string]int or string</td>\n        <td>\n          Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>requests</td>\n        <td>map[string]int or string</td>\n        <td>\n          Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specbootstrapperresourcesclaims","title":"spec.bootstrapperResources.claims[]","role":"content","text":"<sup><sup>[↩ Parent](#specbootstrapperresources)</sup></sup>\n\n\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>name</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"speccertificates","title":"spec.certificates","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nRS Cluster Certificates. Used to modify the certificates used by the cluster. See the \"RSClusterCertificates\" struct described above to see the supported certificates.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>apiCertificateSecretName</td>\n        <td>string</td>\n        <td>\n          Secret name to use for cluster's API certificate. The secret must have the following keys - A key named 'certificate'/'cert'/'tls.crt' with the value of the certificate in PEM format. A key named 'key'/'tls.key' with the value of the private key. Optionally, a key named 'ca.crt', containing the public certificate of the root CA. If present, the root CA certificate is appended to the certificate provided in the 'tls.crt' (or equivalent) key, to form a full certificate chain. Otherwise, the certificate in 'tls.crt' must include a full certificate chain inline. This key is typically populated by the cert-manager when it has access to the root certificate. Otherwise, it could be added manually. If left blank, a cluster-provided certificate will be used.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>cmCertificateSecretName</td>\n        <td>string</td>\n        <td>\n          Secret name to use for cluster's CM (Cluster Manager) certificate. The secret must have the following keys - A key named 'certificate'/'cert'/'tls.crt' with the value of the certificate in PEM format. A key named 'key'/'tls.key' with the value of the private key. Optionally, a key named 'ca.crt', containing the public certificate of the root CA. If present, the root CA certificate is appended to the certificate provided in the 'tls.crt' (or equivalent) key, to form a full certificate chain. Otherwise, the certificate in 'tls.crt' must include a full certificate chain inline. This key is typically populated by the cert-manager when it has access to the root certificate. Otherwise, it could be added manually. If left blank, a cluster-provided certificate will be used.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>cpInternodeEncryptionCertificateSecretName</td>\n        <td>string</td>\n        <td>\n          Secret name to use for cluster's Control Plane Internode Encryption (CPINE) certificate. The secret must have the following keys - A key named 'certificate'/'cert'/'tls.crt' with the value of the certificate in PEM format. A key named 'key'/'tls.key' with the value of the private key. Optionally, a key named 'ca.crt', containing the public certificate of the root CA. If present, the root CA certificate is appended to the certificate provided in the 'tls.crt' (or equivalent) key, to form a full certificate chain. Otherwise, the certificate in 'tls.crt' must include a full certificate chain inline. This key is typically populated by the cert-manager when it has access to the root certificate. Otherwise, it could be added manually. If left blank, a cluster-provided certificate will be used.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>dpInternodeEncryptionCertificateSecretName</td>\n        <td>string</td>\n        <td>\n          Secret name to use for cluster's Data Plane Internode Encryption (DPINE) certificate. The secret must have the following keys - A key named 'certificate'/'cert'/'tls.crt' with the value of the certificate in PEM format. A key named 'key'/'tls.key' with the value of the private key. Optionally, a key named 'ca.crt', containing the public certificate of the root CA. If present, the root CA certificate is appended to the certificate provided in the 'tls.crt' (or equivalent) key, to form a full certificate chain. Otherwise, the certificate in 'tls.crt' must include a full certificate chain inline. This key is typically populated by the cert-manager when it has access to the root certificate. Otherwise, it could be added manually. If left blank, a cluster-provided certificate will be used.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>ldapClientCertificateSecretName</td>\n        <td>string</td>\n        <td>\n          Secret name to use for cluster's LDAP client certificate. The secret must have the following keys - A key named 'certificate'/'cert'/'tls.crt' with the value of the certificate in PEM format. A key named 'key'/'tls.key' with the value of the private key. Optionally, a key named 'ca.crt', containing the public certificate of the root CA. If present, the root CA certificate is appended to the certificate provided in the 'tls.crt' (or equivalent) key, to form a full certificate chain. Otherwise, the certificate in 'tls.crt' must include a full certificate chain inline. This key is typically populated by the cert-manager when it has access to the root certificate. Otherwise, it could be added manually. If left blank, LDAP client certificate authentication will be disabled.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>metricsExporterCertificateSecretName</td>\n        <td>string</td>\n        <td>\n          Secret name to use for cluster's Metrics Exporter certificate. The secret must have the following keys - A key named 'certificate'/'cert'/'tls.crt' with the value of the certificate in PEM format. A key named 'key'/'tls.key' with the value of the private key. Optionally, a key named 'ca.crt', containing the public certificate of the root CA. If present, the root CA certificate is appended to the certificate provided in the 'tls.crt' (or equivalent) key, to form a full certificate chain. Otherwise, the certificate in 'tls.crt' must include a full certificate chain inline. This key is typically populated by the cert-manager when it has access to the root certificate. Otherwise, it could be added manually. If left blank, a cluster-provided certificate will be used.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>proxyCertificateSecretName</td>\n        <td>string</td>\n        <td>\n          Secret name to use for cluster's Proxy certificate. The secret must have the following keys - A key named 'certificate'/'cert'/'tls.crt' with the value of the certificate in PEM format. A key named 'key'/'tls.key' with the value of the private key. Optionally, a key named 'ca.crt', containing the public certificate of the root CA. If present, the root CA certificate is appended to the certificate provided in the 'tls.crt' (or equivalent) key, to form a full certificate chain. Otherwise, the certificate in 'tls.crt' must include a full certificate chain inline. This key is typically populated by the cert-manager when it has access to the root certificate. Otherwise, it could be added manually. If left blank, a cluster-provided certificate will be used. Note - For Active-Active databases (REAADB), certificate updates are automatically reconciled. When you update this secret, the operator detects the change and automatically executes a CRDB force update (equivalent to 'crdb-cli crdb update --force'), which synchronizes the certificate changes to all participating clusters, eliminating the need for manual intervention.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>ssoIssuerCertificateSecretName</td>\n        <td>string</td>\n        <td>\n          Secret name to use for the SSO Identity Provider (IdP) certificate. This is the public certificate from your SAML Identity Provider used to verify SAML assertions. The secret must contain a single field named 'certificate'/'cert'/'tls.crt' (no 'key' field needed for IdP cert). This certificate must be configured as part of the SSO setup, before SSO can be enabled for the cluster. Note - While IdP metadata XML may contain the certificate, Redis Enterprise Server does not use it from there, so the certificate must be provided separately via this secret.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>ssoServiceCertificateSecretName</td>\n        <td>string</td>\n        <td>\n          Secret name to use for the SSO Service Provider (SP) certificate. This certificate is used by the cluster to sign SAML requests and encrypt SAML responses, and it must be configured as part of the SSO setup, before SSO can be enabled for the cluster. The secret must have the following keys - A key named 'certificate'/'cert'/'tls.crt' with the value of the certificate in PEM format. A key named 'key'/'tls.key' with the value of the private key. Optionally, a key named 'ca.crt', containing the public certificate of the root CA. If present, the root CA certificate is appended to the certificate provided in the 'tls.crt' (or equivalent) key, to form a full certificate chain. Otherwise, the certificate in 'tls.crt' must include a full certificate chain inline. This key is typically populated by the cert-manager when it has access to the root certificate. Otherwise, it could be added manually. If left blank, SSO will not be configured.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>syncerCertificateSecretName</td>\n        <td>string</td>\n        <td>\n          Secret name to use for cluster's Syncer certificate. The secret must have the following keys - A key named 'certificate'/'cert'/'tls.crt' with the value of the certificate in PEM format. A key named 'key'/'tls.key' with the value of the private key. Optionally, a key named 'ca.crt', containing the public certificate of the root CA. If present, the root CA certificate is appended to the certificate provided in the 'tls.crt' (or equivalent) key, to form a full certificate chain. Otherwise, the certificate in 'tls.crt' must include a full certificate chain inline. This key is typically populated by the cert-manager when it has access to the root certificate. Otherwise, it could be added manually. If left blank, a cluster-provided certificate will be used. Note - For Active-Active databases (REAADB), certificate updates are automatically reconciled. When you update this secret, the operator detects the change and automatically executes a CRDB force update (equivalent to 'crdb-cli crdb update --force'), which synchronizes the certificate changes to all participating clusters, eliminating the need for manual intervention.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"speccontainertimezone","title":"spec.containerTimezone","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nContainer timezone configuration. While the default timezone on all containers is UTC, this setting can be used to set the timezone on services rigger/bootstrapper/RS containers. You can either propagate the hosts timezone to RS pods or set it manually via timezoneName.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>propagateHost</td>\n        <td>object</td>\n        <td>\n          Identifies that container timezone should be in sync with the host, this option mounts a hostPath volume onto RS pods that could be restricted in some systems.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>timezoneName</td>\n        <td>string</td>\n        <td>\n          POSIX-style timezone name as a string to be passed as EnvVar to RE pods, e.g. \"Europe/London\".<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specextraenvvars","title":"spec.extraEnvVars[]","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\n\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>name</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>value</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specextraenvvarsvaluefrom\">valueFrom</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"spechostaliases","title":"spec.hostAliases[]","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nHostAlias holds the mapping between IP and hostnames that will be injected as an entry in the pod's hosts file.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>hostnames</td>\n        <td>[]string</td>\n        <td>\n          Hostnames for the above IP address.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>ip</td>\n        <td>string</td>\n        <td>\n          IP address of the host file entry.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specingressorroutespec","title":"spec.ingressOrRouteSpec","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nAccess configurations for the Redis Enterprise cluster and databases. At most one of ingressOrRouteSpec or activeActive fields can be set at the same time.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>apiFqdnUrl</td>\n        <td>string</td>\n        <td>\n          RS API URL<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>dbFqdnSuffix</td>\n        <td>string</td>\n        <td>\n          DB ENDPOINT SUFFIX - will be used to set the db host ingress <db name><db fqdn suffix>. Creates a host name so it should be unique if more than one db is created on the cluster with the same name<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>method</td>\n        <td>enum</td>\n        <td>\n          Used to distinguish between different platforms implementation.<br/>\n          <br/>\n            <i>Enum</i>: openShiftRoute, ingress, istio<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>ingressAnnotations</td>\n        <td>map[string]string</td>\n        <td>\n          Additional annotations to set on ingress resources created by the operator<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specldap","title":"spec.ldap","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nCluster-level LDAP configuration, such as server addresses, protocol, authentication and query settings.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td><a href=\"#specldapauthenticationquery\">authenticationQuery</a></td>\n        <td>object</td>\n        <td>\n          Configuration of authentication queries, mapping between the username, provided to the cluster for authentication, and the LDAP Distinguished Name.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td><a href=\"#specldapauthorizationquery\">authorizationQuery</a></td>\n        <td>object</td>\n        <td>\n          Configuration of authorization queries, mapping between a user's Distinguished Name and its group memberships.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>protocol</td>\n        <td>enum</td>\n        <td>\n          Specifies the LDAP protocol to use. One of: LDAP, LDAPS, STARTTLS.<br/>\n          <br/>\n            <i>Enum</i>: LDAP, LDAPS, STARTTLS<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td><a href=\"#specldapservers\">servers</a></td>\n        <td>[]object</td>\n        <td>\n          One or more LDAP servers. If multiple servers are specified, they must all share an identical organization tree structure.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>bindCredentialsSecretName</td>\n        <td>string</td>\n        <td>\n          Name of a secret within the same namespace, holding the credentials used to communicate with the LDAP server for authentication queries. The secret must have a key named 'dn' with the Distinguished Name of the user to execute the query, and 'password' with its password. If left blank, credentials-based authentication is disabled.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>caCertificateSecretName</td>\n        <td>string</td>\n        <td>\n          Name of a secret within the same namespace, holding a PEM-encoded CA certificate for validating the TLS connection to the LDAP server. The secret must have a key named 'cert' with the certificate data. This field is applicable only when the protocol is LDAPS or STARTTLS.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>cacheTTLSeconds</td>\n        <td>integer</td>\n        <td>\n          The maximum TTL of cached entries.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>cba</td>\n        <td>boolean</td>\n        <td>\n          Whether to allow LDAP as an identity source for certificate-based authentication. Disabled by default.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>cbaIdentityOid</td>\n        <td>string</td>\n        <td>\n          The certificate subject OID to use when CBA identity source is set to SubjectOID.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>cbaIdentitySource</td>\n        <td>enum</td>\n        <td>\n          The certificate subject identity source to use for LDAP lookup. Applicable only when CBA is enabled. One of SubjectCN, SubjectOID.<br/>\n          <br/>\n            <i>Enum</i>: SubjectCN, SubjectOID<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>directoryTimeoutSeconds</td>\n        <td>integer</td>\n        <td>\n          The connection timeout to the LDAP server when authenticating a user, in seconds<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>enabledForControlPlane</td>\n        <td>boolean</td>\n        <td>\n          Whether to enable LDAP for control plane access. Disabled by default.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>enabledForDataPlane</td>\n        <td>boolean</td>\n        <td>\n          Whether to enable LDAP for data plane access. Disabled by default.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specldapauthenticationquery","title":"spec.ldap.authenticationQuery","role":"content","text":"<sup><sup>[↩ Parent](#specldap)</sup></sup>\n\nConfiguration of authentication queries, mapping between the username, provided to the cluster for authentication, and the LDAP Distinguished Name.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td><a href=\"#specldapauthenticationqueryquery\">query</a></td>\n        <td>object</td>\n        <td>\n          Configuration for a search query. Mutually exclusive with the 'template' field. The substring '%u' in the query filter will be replaced with the username.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>template</td>\n        <td>string</td>\n        <td>\n          Configuration for a template query. Mutually exclusive with the 'query' field. The substring '%u' will be replaced with the username, e.g., 'cn=%u,ou=dev,dc=example,dc=com'.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specldapauthenticationqueryquery","title":"spec.ldap.authenticationQuery.query","role":"content","text":"<sup><sup>[↩ Parent](#specldapauthenticationquery)</sup></sup>\n\nConfiguration for a search query. Mutually exclusive with the 'template' field. The substring '%u' in the query filter will be replaced with the username.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>base</td>\n        <td>string</td>\n        <td>\n          The Distinguished Name of the entry at which to start the search, e.g., 'ou=dev,dc=example,dc=com'.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>filter</td>\n        <td>string</td>\n        <td>\n          An RFC-4515 string representation of the filter to apply in the search. For an authentication query, the substring '%u' will be replaced with the username, e.g., '(cn=%u)'. For an authorization query, the substring '%D' will be replaced with the user's Distinguished Name, e.g., '(members=%D)'.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>scope</td>\n        <td>enum</td>\n        <td>\n          The search scope for an LDAP query. One of: BaseObject, SingleLevel, WholeSubtree<br/>\n          <br/>\n            <i>Enum</i>: BaseObject, SingleLevel, WholeSubtree<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specldapauthorizationquery","title":"spec.ldap.authorizationQuery","role":"content","text":"<sup><sup>[↩ Parent](#specldap)</sup></sup>\n\nConfiguration of authorization queries, mapping between a user's Distinguished Name and its group memberships.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>attribute</td>\n        <td>string</td>\n        <td>\n          Configuration for an attribute query. Mutually exclusive with the 'query' field. Holds the name of an attribute of the LDAP user entity that contains a list of the groups that the user belongs to, e.g., 'memberOf'.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specldapauthorizationqueryquery\">query</a></td>\n        <td>object</td>\n        <td>\n          Configuration for a search query. Mutually exclusive with the 'attribute' field. The substring '%D' in the query filter will be replaced with the user's Distinguished Name.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specldapauthorizationqueryquery","title":"spec.ldap.authorizationQuery.query","role":"content","text":"<sup><sup>[↩ Parent](#specldapauthorizationquery)</sup></sup>\n\nConfiguration for a search query. Mutually exclusive with the 'attribute' field. The substring '%D' in the query filter will be replaced with the user's Distinguished Name.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>base</td>\n        <td>string</td>\n        <td>\n          The Distinguished Name of the entry at which to start the search, e.g., 'ou=dev,dc=example,dc=com'.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>filter</td>\n        <td>string</td>\n        <td>\n          An RFC-4515 string representation of the filter to apply in the search. For an authentication query, the substring '%u' will be replaced with the username, e.g., '(cn=%u)'. For an authorization query, the substring '%D' will be replaced with the user's Distinguished Name, e.g., '(members=%D)'.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>scope</td>\n        <td>enum</td>\n        <td>\n          The search scope for an LDAP query. One of: BaseObject, SingleLevel, WholeSubtree<br/>\n          <br/>\n            <i>Enum</i>: BaseObject, SingleLevel, WholeSubtree<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specldapservers","title":"spec.ldap.servers[]","role":"content","text":"<sup><sup>[↩ Parent](#specldap)</sup></sup>\n\nAddress of an LDAP server.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>host</td>\n        <td>string</td>\n        <td>\n          Host name of the LDAP server<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>port</td>\n        <td>integer</td>\n        <td>\n          Port number of the LDAP server. If unspecified, defaults to 389 for LDAP and STARTTLS protocols, and 636 for LDAPS protocol.<br/>\n          <br/>\n            <i>Format</i>: int32<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specmetricsconfig","title":"spec.metricsConfig","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nCluster-level configuration for metrics. Currently controls which database tag keys may be exposed as labels on Redis Software metrics.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>exposeDatabaseTags</td>\n        <td>boolean</td>\n        <td>\n          Master switch for tag-derived metrics. When true, database tag keys listed in metricsTagKeysExposed may be used as labels by metrics consumers. When false, no database tag keys are exposed as metric labels, even if listed in metricsTagKeysExposed. Defaults to false if unset.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>keyDistributionEnabled</td>\n        <td>boolean</td>\n        <td>\n          When true, emit Prometheus classic histogram metrics for key size/items distributions. Defaults to false if unset.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>keyItemsBuckets</td>\n        <td>string</td>\n        <td>\n          Comma-separated bucket boundaries for key items distribution histograms (e.g., \"1M,8M\"). Defaults to empty string if unset.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>keySizeBuckets</td>\n        <td>string</td>\n        <td>\n          Comma-separated bucket boundaries for key size distribution histograms (e.g., \"128M,512M\"). Defaults to empty string if unset.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>localStorageMaxSizeMb</td>\n        <td>integer</td>\n        <td>\n          Maximum size in MB for local metrics storage. Defaults to 1024 if unset.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>localStorageRetentionDays</td>\n        <td>integer</td>\n        <td>\n          Number of days to retain local metrics data. Defaults to 8 if unset.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>maxRequestsInFlight</td>\n        <td>integer</td>\n        <td>\n          Maximum number of concurrent master_aggregator scrape requests. Set to 0 to disable the limit. Defaults to 2 if unset.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>metricsTagKeysExposed</td>\n        <td>[]string</td>\n        <td>\n          Allowlist of database tag keys that may be exposed as metric labels when exposeDatabaseTags is true. Keys not listed here are never exposed as metric labels even if they appear on a database. Keys are lowercased on the cluster, must be 1-64 characters and not whitespace-only, and may contain ASCII letters, digits, spaces, '-', '_', '.', '+', '@', and ':'. At most 50 keys are allowed. Defaults to empty list if unset.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specocspconfiguration","title":"spec.ocspConfiguration","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nAn API object that represents the cluster's OCSP configuration. To enable OCSP, the cluster's proxy certificate should contain the OCSP responder URL.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>ocspFunctionality</td>\n        <td>boolean</td>\n        <td>\n          Whether to enable/disable OCSP mechanism for the cluster.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>queryFrequency</td>\n        <td>integer</td>\n        <td>\n          Determines the interval (in seconds) in which the control plane will poll the OCSP responder for a new status for the server certificate. Minimum value is 60. Maximum value is 86400.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>recoveryFrequency</td>\n        <td>integer</td>\n        <td>\n          Determines the interval (in seconds) in which the control plane will poll the OCSP responder for a new status for the server certificate when the current staple is invalid. Minimum value is 60. Maximum value is 86400.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>recoveryMaxTries</td>\n        <td>integer</td>\n        <td>\n          Determines the maximum number for the OCSP recovery attempts. After max number of tries passed, the control plane will revert back to the regular frequency. Minimum value is 1. Maximum value is 100.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>responseTimeout</td>\n        <td>integer</td>\n        <td>\n          Determines the time interval (in seconds) for which the request waits for a response from the OCSP responder. Minimum value is 1. Maximum value is 60.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specossclustersettings","title":"spec.ossClusterSettings","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nCluster-level configuration for OSS cluster mode databases.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>externalAccessType</td>\n        <td>enum</td>\n        <td>\n          Specifies the mechanism for enabling external access to OSS cluster databases. When unset or set to \"Disabled\", external access is not allowed for any OSS cluster databases. When set to a specific mechanism (e.g., \"LoadBalancer\"), that mechanism is used to provide external access. Note: Individual databases must still enable external access via their ossClusterSettings.enableExternalAccess field.<br/>\n          <br/>\n            <i>Enum</i>: LoadBalancer, Disabled<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specossclustersettingsloadbalancer\">loadBalancer</a></td>\n        <td>object</td>\n        <td>\n          Configuration for LoadBalancer services created to assign public IPs for Redis Enterprise cluster nodes.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>podCIDRs</td>\n        <td>[]string</td>\n        <td>\n          A list of Kubernetes pod CIDR ranges from which pod IPs are allocated. Supports both IPv4 (e.g., \"10.30.0.0/16\") and IPv6 addresses. This field should only be configured when OSS cluster databases need to be accessed from both internal and external clients. When configured, internal communication can reach pods directly using their pod IPs, bypassing the external access mechanism (e.g., load balancer services) for improved performance. IMPORTANT: For this feature to work correctly, the entire data path must preserve the client source IP address. This is required because the Redis server uses the client's source IP to construct the CLUSTER SHARDS/SLOTS response - returning pod IPs for internal clients (matching podCIDRs) or load balancer addresses for external clients. On cloud platforms, this typically requires configuring the load balancer to preserve source IPs.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specossclustersettingsloadbalancer","title":"spec.ossClusterSettings.loadBalancer","role":"content","text":"<sup><sup>[↩ Parent](#specossclustersettings)</sup></sup>\n\nConfiguration for LoadBalancer services created to assign public IPs for Redis Enterprise cluster nodes.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>externalTrafficPolicy</td>\n        <td>enum</td>\n        <td>\n          ExternalTrafficPolicy specifies the externalTrafficPolicy for LoadBalancer services created for Redis Enterprise cluster nodes. Choose \"Local\" to configure the LoadBalancer to only route traffic to the single worker node hosting the Redis Enterprise cluster node for that service. Choose \"Cluster\" to route traffic to any worker node, providing a more stable behavior during failovers, but with increased overhead due to additional hop. Defaults to \"Local\" when podCIDRs is configured, and \"Cluster\" otherwise.<br/>\n          <br/>\n            <i>Enum</i>: Cluster, Local<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>serviceAnnotations</td>\n        <td>map[string]string</td>\n        <td>\n          Additional annotations to set on LoadBalancer services created for Redis Enterprise cluster nodes. These annotations are merged with global service annotations from spec.services.servicesAnnotations.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specpersistentspec","title":"spec.persistentSpec","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nPersistent storage configuration for Redis Enterprise cluster.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enablePersistentVolumeResize</td>\n        <td>boolean</td>\n        <td>\n          Whether to enable PersistentVolumes resize. Disabled by default. Read the instruction in pvc_expansion readme carefully before using this feature.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Whether to add persistent volume to Redis Enterprise pods<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>storageClassName</td>\n        <td>string</td>\n        <td>\n          Storage class for persistent volume in Redis Enterprise pods. Leave empty to use the default. If using the default this way, make sure the Kubernetes Cluster has a default Storage Class configured. This can be done by running a `kubectl get storageclass` and see if one of the Storage Classes' names contains a `(default)` mark.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>volumeSize</td>\n        <td>int or string</td>\n        <td>\n          By default, if you omit spec.persistentSpec.volumeSize, the operator allocates a persistent volume that is five times (5x) the Redis Enterprise node memory request defined in spec.redisEnterpriseNodeResources.requests.memory (per node). This 5x ratio is the recommended minimum capacity. To enable resizing after creating the cluster, see the Expand PVC docs: https://redis.io/docs/latest/operate/kubernetes/re-clusters/expand-pvc/<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specpodantiaffinity","title":"spec.podAntiAffinity","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nOverride for the default anti-affinity rules of the Redis Enterprise pods. More info: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#an-example-of-a-pod-that-uses-pod-affinity\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td><a href=\"#specpodantiaffinitypreferredduringschedulingignoredduringexecution\">preferredDuringSchedulingIgnoredDuringExecution</a></td>\n        <td>[]object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specpodantiaffinityrequiredduringschedulingignoredduringexecution\">requiredDuringSchedulingIgnoredDuringExecution</a></td>\n        <td>[]object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specpodstartingpolicy","title":"spec.podStartingPolicy","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nMitigation setting for STS pods stuck in \"ContainerCreating\"\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Enables detection and mitigation of pod startup issues.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>startingThresholdSeconds</td>\n        <td>integer</td>\n        <td>\n          Time in seconds to wait before taking action on a pod stuck during startup. Set to 0 to disable.<br/>\n          <br/>\n            <i>Format</i>: int32<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specpodtolerations","title":"spec.podTolerations[]","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\n\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>effect</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>key</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>operator</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>tolerationSeconds</td>\n        <td>integer</td>\n        <td>\n          <br/>\n          <br/>\n            <i>Format</i>: int64<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>value</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specpullsecrets","title":"spec.pullSecrets[]","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\n\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>name</td>\n        <td>string</td>\n        <td>\n          Secret name<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specredisenterpriseadditionalpodspecattributes","title":"spec.redisEnterpriseAdditionalPodSpecAttributes","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nADVANCED USAGE USE AT YOUR OWN RISK -\nspecify pod attributes that are required for the statefulset - Redis Enterprise pods.\nPod attributes managed by the operator might override these settings.\nAlso make sure the attributes are supported by the K8s version running on the cluster -\nthe operator does not validate that.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>activeDeadlineSeconds</td>\n        <td>integer</td>\n        <td>\n          <br/>\n          <br/>\n            <i>Format</i>: int64<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specredisenterpriseadditionalpodspecattributesaffinity\">affinity</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>automountServiceAccountToken</td>\n        <td>boolean</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specredisenterpriseadditionalpodspecattributesdnsconfig\">dnsConfig</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>dnsPolicy</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>enableServiceLinks</td>\n        <td>boolean</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specredisenterpriseadditionalpodspecattributesephemeralcontainers\">ephemeralContainers</a></td>\n        <td>[]object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specredisenterpriseadditionalpodspecattributeshostaliases\">hostAliases</a></td>\n        <td>[]object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>hostIPC</td>\n        <td>boolean</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>hostNetwork</td>\n        <td>boolean</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>hostPID</td>\n        <td>boolean</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>hostUsers</td>\n        <td>boolean</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>hostname</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>hostnameOverride</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specredisenterpriseadditionalpodspecattributesimagepullsecrets\">imagePullSecrets</a></td>\n        <td>[]object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specredisenterpriseadditionalpodspecattributesinitcontainers\">initContainers</a></td>\n        <td>[]object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>nodeName</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>nodeSelector</td>\n        <td>map[string]string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specredisenterpriseadditionalpodspecattributesos\">os</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>overhead</td>\n        <td>map[string]int or string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>preemptionPolicy</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>priority</td>\n        <td>integer</td>\n        <td>\n          <br/>\n          <br/>\n            <i>Format</i>: int32<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>priorityClassName</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specredisenterpriseadditionalpodspecattributesreadinessgates\">readinessGates</a></td>\n        <td>[]object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specredisenterpriseadditionalpodspecattributesresourceclaims\">resourceClaims</a></td>\n        <td>[]object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specredisenterpriseadditionalpodspecattributesresources\">resources</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>restartPolicy</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>runtimeClassName</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>schedulerName</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specredisenterpriseadditionalpodspecattributesschedulinggates\">schedulingGates</a></td>\n        <td>[]object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specredisenterpriseadditionalpodspecattributessecuritycontext\">securityContext</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>serviceAccount</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>serviceAccountName</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>setHostnameAsFQDN</td>\n        <td>boolean</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>shareProcessNamespace</td>\n        <td>boolean</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>subdomain</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>terminationGracePeriodSeconds</td>\n        <td>integer</td>\n        <td>\n          <br/>\n          <br/>\n            <i>Format</i>: int64<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specredisenterpriseadditionalpodspecattributestolerations\">tolerations</a></td>\n        <td>[]object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specredisenterpriseadditionalpodspecattributestopologyspreadconstraints\">topologySpreadConstraints</a></td>\n        <td>[]object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specredisenterpriseadditionalpodspecattributesvolumes\">volumes</a></td>\n        <td>[]object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specredisenterpriseadditionalpodspecattributesworkloadref\">workloadRef</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specredisenterpriseimagespec","title":"spec.redisEnterpriseImageSpec","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nSpecification for Redis Enterprise container image\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>digestHash</td>\n        <td>string</td>\n        <td>\n          The digest hash of the container image to pull. When specified, the container image is pulled according to the digest hash instead of the image tag. The versionTag field must also be specified with the image tag matching this digest hash. Note: This field is only supported for OLM deployments.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>imagePullPolicy</td>\n        <td>string</td>\n        <td>\n          The image pull policy to be applied to the container image. One of Always, Never, IfNotPresent.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>repository</td>\n        <td>string</td>\n        <td>\n          The repository (name) of the container image to be deployed.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>versionTag</td>\n        <td>string</td>\n        <td>\n          The tag of the container image to be deployed.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specredisenterprisenoderesources","title":"spec.redisEnterpriseNodeResources","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nCompute resource requirements for Redis Enterprise containers\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td><a href=\"#specredisenterprisenoderesourcesclaims\">claims</a></td>\n        <td>[]object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>limits</td>\n        <td>map[string]int or string</td>\n        <td>\n          Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>requests</td>\n        <td>map[string]int or string</td>\n        <td>\n          Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specredisenterprisenoderesourcesclaims","title":"spec.redisEnterpriseNodeResources.claims[]","role":"content","text":"<sup><sup>[↩ Parent](#specredisenterprisenoderesources)</sup></sup>\n\n\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>name</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specredisenterpriseservicesconfiguration","title":"spec.redisEnterpriseServicesConfiguration","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nRS Cluster optional services settings\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td><a href=\"#specredisenterpriseservicesconfigurationcmserver\">cmServer</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specredisenterpriseservicesconfigurationcrdbcoordinator\">crdbCoordinator</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specredisenterpriseservicesconfigurationcrdbworker\">crdbWorker</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specredisenterpriseservicesconfigurationmdnsserver\">mdnsServer</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specredisenterpriseservicesconfigurationpdnsserver\">pdnsServer</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specredisenterpriseservicesconfigurationsaslauthd\">saslauthd</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specredisenterpriseservicesconfigurationstatsarchiver\">statsArchiver</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specredisenterpriseservicesconfigurationcmserver","title":"spec.redisEnterpriseServicesConfiguration.cmServer","role":"content","text":"<sup><sup>[↩ Parent](#specredisenterpriseservicesconfiguration)</sup></sup>\n\n\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>operatingMode</td>\n        <td>enum</td>\n        <td>\n          Whether to enable/disable the CM server<br/>\n          <br/>\n            <i>Enum</i>: enabled, disabled<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specredisenterpriseservicesconfigurationcrdbcoordinator","title":"spec.redisEnterpriseServicesConfiguration.crdbCoordinator","role":"content","text":"<sup><sup>[↩ Parent](#specredisenterpriseservicesconfiguration)</sup></sup>\n\n\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>operatingMode</td>\n        <td>enum</td>\n        <td>\n          Whether to enable/disable the crdb coordinator process<br/>\n          <br/>\n            <i>Enum</i>: enabled, disabled<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specredisenterpriseservicesconfigurationcrdbworker","title":"spec.redisEnterpriseServicesConfiguration.crdbWorker","role":"content","text":"<sup><sup>[↩ Parent](#specredisenterpriseservicesconfiguration)</sup></sup>\n\n\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>operatingMode</td>\n        <td>enum</td>\n        <td>\n          Whether to enable/disable the crdb worker processes<br/>\n          <br/>\n            <i>Enum</i>: enabled, disabled<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specredisenterpriseservicesconfigurationmdnsserver","title":"spec.redisEnterpriseServicesConfiguration.mdnsServer","role":"content","text":"<sup><sup>[↩ Parent](#specredisenterpriseservicesconfiguration)</sup></sup>\n\n\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>operatingMode</td>\n        <td>enum</td>\n        <td>\n          Whether to enable/disable the Multicast DNS server<br/>\n          <br/>\n            <i>Enum</i>: enabled, disabled<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specredisenterpriseservicesconfigurationpdnsserver","title":"spec.redisEnterpriseServicesConfiguration.pdnsServer","role":"content","text":"<sup><sup>[↩ Parent](#specredisenterpriseservicesconfiguration)</sup></sup>\n\n\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>operatingMode</td>\n        <td>enum</td>\n        <td>\n          Deprecated: The PDNS Server is now disabled by the operator. This field will be ignored.<br/>\n          <br/>\n            <i>Enum</i>: enabled, disabled<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specredisenterpriseservicesconfigurationsaslauthd","title":"spec.redisEnterpriseServicesConfiguration.saslauthd","role":"content","text":"<sup><sup>[↩ Parent](#specredisenterpriseservicesconfiguration)</sup></sup>\n\n\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>operatingMode</td>\n        <td>enum</td>\n        <td>\n          Whether to enable/disable the saslauthd service<br/>\n          <br/>\n            <i>Enum</i>: enabled, disabled<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specredisenterpriseservicesconfigurationstatsarchiver","title":"spec.redisEnterpriseServicesConfiguration.statsArchiver","role":"content","text":"<sup><sup>[↩ Parent](#specredisenterpriseservicesconfiguration)</sup></sup>\n\n\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>operatingMode</td>\n        <td>enum</td>\n        <td>\n          Whether to enable/disable the stats archiver service<br/>\n          <br/>\n            <i>Enum</i>: enabled, disabled<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specredisenterpriseservicesriggerimagespec","title":"spec.redisEnterpriseServicesRiggerImageSpec","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nSpecification for Services Rigger container image\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>digestHash</td>\n        <td>string</td>\n        <td>\n          The digest hash of the container image to pull. When specified, the container image is pulled according to the digest hash instead of the image tag. The versionTag field must also be specified with the image tag matching this digest hash. Note: This field is only supported for OLM deployments.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>imagePullPolicy</td>\n        <td>string</td>\n        <td>\n          The image pull policy to be applied to the container image. One of Always, Never, IfNotPresent.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>repository</td>\n        <td>string</td>\n        <td>\n          The repository (name) of the container image to be deployed.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>versionTag</td>\n        <td>string</td>\n        <td>\n          The tag of the container image to be deployed.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specredisenterpriseservicesriggerresources","title":"spec.redisEnterpriseServicesRiggerResources","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nCompute resource requirements for Services Rigger pod\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td><a href=\"#specredisenterpriseservicesriggerresourcesclaims\">claims</a></td>\n        <td>[]object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>limits</td>\n        <td>map[string]int or string</td>\n        <td>\n          Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>requests</td>\n        <td>map[string]int or string</td>\n        <td>\n          Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specredisenterprisevolumemounts","title":"spec.redisEnterpriseVolumeMounts[]","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\n\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>mountPath</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>name</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>mountPropagation</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>readOnly</td>\n        <td>boolean</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>subPath</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>subPathExpr</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specredisonflashspec","title":"spec.redisOnFlashSpec","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nAuto Tiering (Redis on Flash) configuration. When provided, the cluster can create Auto Tiering databases.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          <br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>storageClassName</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>bigStoreDriver</td>\n        <td>enum</td>\n        <td>\n          <br/>\n          <br/>\n            <i>Enum</i>: rocksdb, speedb<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>flashDiskSize</td>\n        <td>int or string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>flashStorageEngine</td>\n        <td>enum</td>\n        <td>\n          <br/>\n          <br/>\n            <i>Enum</i>: rocksdb<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specsecuritycontext","title":"spec.securityContext","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nThe security configuration that will be applied to RS pods.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td><a href=\"#specsecuritycontextfips\">fips</a></td>\n        <td>object</td>\n        <td>\n          FIPS-140 compliance mode settings.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specsecuritycontextreadonlyrootfilesystempolicy\">readOnlyRootFilesystemPolicy</a></td>\n        <td>object</td>\n        <td>\n          Policy controlling whether to enable read-only root filesystem for the Redis Enterprise software containers. Note that certain filesystem paths remain writable through mounted volumes to ensure proper functionality.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specsecuritycontextresourcelimits\">resourceLimits</a></td>\n        <td>object</td>\n        <td>\n          Settings pertaining to resource limits management by the Redis Enterprise node container.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specsecuritycontextfips","title":"spec.securityContext.fips","role":"content","text":"<sup><sup>[↩ Parent](#specsecuritycontext)</sup></sup>\n\nFIPS-140 compliance mode settings.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Toggles whether FIPS-140 compliance mode is enabled or not. Defaults to false.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specsecuritycontextreadonlyrootfilesystempolicy","title":"spec.securityContext.readOnlyRootFilesystemPolicy","role":"content","text":"<sup><sup>[↩ Parent](#specsecuritycontext)</sup></sup>\n\nPolicy controlling whether to enable read-only root filesystem for the Redis Enterprise software containers. Note that certain filesystem paths remain writable through mounted volumes to ensure proper functionality.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Whether to enable read-only root filesystem for the Redis Enterprise software containers. Default is false.<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specsecuritycontextresourcelimits","title":"spec.securityContext.resourceLimits","role":"content","text":"<sup><sup>[↩ Parent](#specsecuritycontext)</sup></sup>\n\nSettings pertaining to resource limits management by the Redis Enterprise node container.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>allowAutoAdjustment</td>\n        <td>boolean</td>\n        <td>\n          Allow Redis Enterprise to adjust resource limits, like max open file descriptors, of its data plane processes. When this option is enabled, the SYS_RESOURCE capability is added to the Redis Enterprise pods, and their allowPrivilegeEscalation field is set. Turned off by default.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specservices","title":"spec.services","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nCustomization options for operator-managed service resources created for Redis Enterprise clusters and databases\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td><a href=\"#specservicesapiservice\">apiService</a></td>\n        <td>object</td>\n        <td>\n          Customization options for the REC API service.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>servicesAnnotations</td>\n        <td>map[string]string</td>\n        <td>\n          Global additional annotations to set on service resources created by the operator. The specified annotations will not override annotations that already exist and didn't originate from the operator.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specservicesapiservice","title":"spec.services.apiService","role":"content","text":"<sup><sup>[↩ Parent](#specservices)</sup></sup>\n\nCustomization options for the REC API service.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>type</td>\n        <td>enum</td>\n        <td>\n          Type of service to create for the REC API service. Defaults to ClusterIP service, if not specified otherwise.<br/>\n          <br/>\n            <i>Enum</i>: ClusterIP, NodePort, LoadBalancer<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specservicesriggerspec","title":"spec.servicesRiggerSpec","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nSpecification for service rigger\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>databaseServicePortPolicy</td>\n        <td>enum</td>\n        <td>\n          databaseServicePortPolicy instructs how to determine the service ports for REDB services. Defaults to DatabasePortForward, if not specified otherwise. Note - Regardless whether this flag is set or not, if an REDB/REAADB configured with databaseServicePort that would be the port exposed by the Service. DatabasePortForward - The service port will be the same as the database port. RedisDefaultPort - The service port will be the default Redis port (6379).<br/>\n          <br/>\n            <i>Enum</i>: DatabasePortForward, RedisDefaultPort<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>databaseServiceType</td>\n        <td>string</td>\n        <td>\n          Service types for access to databases. should be a comma separated list. The possible values are cluster_ip, headless and load_balancer.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specservicesriggerspecextraenvvars\">extraEnvVars</a></td>\n        <td>[]object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>podAnnotations</td>\n        <td>map[string]string</td>\n        <td>\n          annotations for the service rigger pod<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>serviceNaming</td>\n        <td>enum</td>\n        <td>\n          Used to determine how to name the services created automatically when a database is created. When bdb_name is used, the database name will be also used for the service name. When redis-port is used, the service will be named redis-<port>.<br/>\n          <br/>\n            <i>Enum</i>: bdb_name, redis-port<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specservicesriggerspecservicesriggeradditionalpodspecattributes\">servicesRiggerAdditionalPodSpecAttributes</a></td>\n        <td>object</td>\n        <td>\n          ADVANCED USAGE USE AT YOUR OWN RISK - specify pod attributes that are required for the rigger deployment pod.\nPod attributes managed by the operator might override these settings (Containers, serviceAccountName,\nImagePullSecrets, nodeSelector, PriorityClassName, PodSecurityContext).\npodTolerations are merged with tolerations defined here.\nAlso make sure the attributes are supported by the K8s version running on the cluster -\nthe operator does not validate that.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specservicesriggerspecextraenvvars","title":"spec.servicesRiggerSpec.extraEnvVars[]","role":"content","text":"<sup><sup>[↩ Parent](#specservicesriggerspec)</sup></sup>\n\nEnvVar represents an environment variable present in a Container. More info: https://kubernetes.io/docs/tasks/inject-data-application/environment-variable-expose-pod-information/\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>name</td>\n        <td>string</td>\n        <td>\n          Name of the environment variable.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>value</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specservicesriggerspecextraenvvarsvaluefrom\">valueFrom</a></td>\n        <td>object</td>\n        <td>\n          Source for the environment variable's value. Cannot be used if value is not empty.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specservicesriggerspecextraenvvarsvaluefrom","title":"spec.servicesRiggerSpec.extraEnvVars[].valueFrom","role":"content","text":"<sup><sup>[↩ Parent](#specservicesriggerspecextraenvvars)</sup></sup>\n\nSource for the environment variable's value. Cannot be used if value is not empty.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td><a href=\"#specservicesriggerspecextraenvvarsvaluefromconfigmapkeyref\">configMapKeyRef</a></td>\n        <td>object</td>\n        <td>\n          Selects a key of a ConfigMap.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specservicesriggerspecextraenvvarsvaluefromfieldref\">fieldRef</a></td>\n        <td>object</td>\n        <td>\n          Selects a field of the pod<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specservicesriggerspecextraenvvarsvaluefromresourcefieldref\">resourceFieldRef</a></td>\n        <td>object</td>\n        <td>\n          Selects a resource of the container: only resources limits and requests are currently supported.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specservicesriggerspecextraenvvarsvaluefromsecretkeyref\">secretKeyRef</a></td>\n        <td>object</td>\n        <td>\n          Selects a key of a secret in the pod's namespace<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specservicesriggerspecextraenvvarsvaluefromconfigmapkeyref","title":"spec.servicesRiggerSpec.extraEnvVars[].valueFrom.configMapKeyRef","role":"content","text":"<sup><sup>[↩ Parent](#specservicesriggerspecextraenvvarsvaluefrom)</sup></sup>\n\nSelects a key of a ConfigMap.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>key</td>\n        <td>string</td>\n        <td>\n          The key to select.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>name</td>\n        <td>string</td>\n        <td>\n          Name of the referent<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>optional</td>\n        <td>boolean</td>\n        <td>\n          Specify whether the ConfigMap or its key must be defined<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specservicesriggerspecextraenvvarsvaluefromfieldref","title":"spec.servicesRiggerSpec.extraEnvVars[].valueFrom.fieldRef","role":"content","text":"<sup><sup>[↩ Parent](#specservicesriggerspecextraenvvarsvaluefrom)</sup></sup>\n\nSelects a field of the pod\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>fieldPath</td>\n        <td>string</td>\n        <td>\n          Path of the field to select in the specified API version.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>apiVersion</td>\n        <td>string</td>\n        <td>\n          Version of the schema the FieldPath is written in terms of, defaults to \"v1\".<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specservicesriggerspecextraenvvarsvaluefromresourcefieldref","title":"spec.servicesRiggerSpec.extraEnvVars[].valueFrom.resourceFieldRef","role":"content","text":"<sup><sup>[↩ Parent](#specservicesriggerspecextraenvvarsvaluefrom)</sup></sup>\n\nSelects a resource of the container: only resources limits and requests are currently supported.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>resource</td>\n        <td>string</td>\n        <td>\n          Required: resource to select<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>containerName</td>\n        <td>string</td>\n        <td>\n          Container name: required for volumes, optional for env vars<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>divisor</td>\n        <td>int or string</td>\n        <td>\n          Specifies the output format of the exposed resources, defaults to \"1\"<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specservicesriggerspecextraenvvarsvaluefromsecretkeyref","title":"spec.servicesRiggerSpec.extraEnvVars[].valueFrom.secretKeyRef","role":"content","text":"<sup><sup>[↩ Parent](#specservicesriggerspecextraenvvarsvaluefrom)</sup></sup>\n\nSelects a key of a secret in the pod's namespace\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>key</td>\n        <td>string</td>\n        <td>\n          The key of the secret to select from.  Must be a valid secret key.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>name</td>\n        <td>string</td>\n        <td>\n          Name of the referent<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>optional</td>\n        <td>boolean</td>\n        <td>\n          Specify whether the Secret or its key must be defined<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specservicesriggerspecservicesriggeradditionalpodspecattributes","title":"spec.servicesRiggerSpec.servicesRiggerAdditionalPodSpecAttributes","role":"content","text":"<sup><sup>[↩ Parent](#specservicesriggerspec)</sup></sup>\n\nADVANCED USAGE USE AT YOUR OWN RISK - specify pod attributes that are required for the rigger deployment pod.\nPod attributes managed by the operator might override these settings (Containers, serviceAccountName,\nImagePullSecrets, nodeSelector, PriorityClassName, PodSecurityContext).\npodTolerations are merged with tolerations defined here.\nAlso make sure the attributes are supported by the K8s version running on the cluster -\nthe operator does not validate that.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>activeDeadlineSeconds</td>\n        <td>integer</td>\n        <td>\n          <br/>\n          <br/>\n            <i>Format</i>: int64<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specservicesriggerspecservicesriggeradditionalpodspecattributesaffinity\">affinity</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>automountServiceAccountToken</td>\n        <td>boolean</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specservicesriggerspecservicesriggeradditionalpodspecattributesdnsconfig\">dnsConfig</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>dnsPolicy</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>enableServiceLinks</td>\n        <td>boolean</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specservicesriggerspecservicesriggeradditionalpodspecattributesephemeralcontainers\">ephemeralContainers</a></td>\n        <td>[]object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specservicesriggerspecservicesriggeradditionalpodspecattributeshostaliases\">hostAliases</a></td>\n        <td>[]object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>hostIPC</td>\n        <td>boolean</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>hostNetwork</td>\n        <td>boolean</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>hostPID</td>\n        <td>boolean</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>hostUsers</td>\n        <td>boolean</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>hostname</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>hostnameOverride</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specservicesriggerspecservicesriggeradditionalpodspecattributesimagepullsecrets\">imagePullSecrets</a></td>\n        <td>[]object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specservicesriggerspecservicesriggeradditionalpodspecattributesinitcontainers\">initContainers</a></td>\n        <td>[]object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>nodeName</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>nodeSelector</td>\n        <td>map[string]string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specservicesriggerspecservicesriggeradditionalpodspecattributesos\">os</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>overhead</td>\n        <td>map[string]int or string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>preemptionPolicy</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>priority</td>\n        <td>integer</td>\n        <td>\n          <br/>\n          <br/>\n            <i>Format</i>: int32<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>priorityClassName</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specservicesriggerspecservicesriggeradditionalpodspecattributesreadinessgates\">readinessGates</a></td>\n        <td>[]object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specservicesriggerspecservicesriggeradditionalpodspecattributesresourceclaims\">resourceClaims</a></td>\n        <td>[]object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specservicesriggerspecservicesriggeradditionalpodspecattributesresources\">resources</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>restartPolicy</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>runtimeClassName</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>schedulerName</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specservicesriggerspecservicesriggeradditionalpodspecattributesschedulinggates\">schedulingGates</a></td>\n        <td>[]object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specservicesriggerspecservicesriggeradditionalpodspecattributessecuritycontext\">securityContext</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>serviceAccount</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>serviceAccountName</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>setHostnameAsFQDN</td>\n        <td>boolean</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>shareProcessNamespace</td>\n        <td>boolean</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>subdomain</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>terminationGracePeriodSeconds</td>\n        <td>integer</td>\n        <td>\n          <br/>\n          <br/>\n            <i>Format</i>: int64<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specservicesriggerspecservicesriggeradditionalpodspecattributestolerations\">tolerations</a></td>\n        <td>[]object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specservicesriggerspecservicesriggeradditionalpodspecattributestopologyspreadconstraints\">topologySpreadConstraints</a></td>\n        <td>[]object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specservicesriggerspecservicesriggeradditionalpodspecattributesvolumes\">volumes</a></td>\n        <td>[]object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specservicesriggerspecservicesriggeradditionalpodspecattributesworkloadref\">workloadRef</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specsidecontainersspec","title":"spec.sideContainersSpec[]","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\n\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>name</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>args</td>\n        <td>[]string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>command</td>\n        <td>[]string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specsidecontainersspecenv\">env</a></td>\n        <td>[]object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specsidecontainersspecenvfrom\">envFrom</a></td>\n        <td>[]object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>image</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>imagePullPolicy</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specsidecontainersspeclifecycle\">lifecycle</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specsidecontainersspeclivenessprobe\">livenessProbe</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specsidecontainersspecports\">ports</a></td>\n        <td>[]object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specsidecontainersspecreadinessprobe\">readinessProbe</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specsidecontainersspecresources\">resources</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specsidecontainersspecsecuritycontext\">securityContext</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specsidecontainersspecstartupprobe\">startupProbe</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>stdin</td>\n        <td>boolean</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>stdinOnce</td>\n        <td>boolean</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>terminationMessagePath</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>terminationMessagePolicy</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>tty</td>\n        <td>boolean</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specsidecontainersspecvolumedevices\">volumeDevices</a></td>\n        <td>[]object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specsidecontainersspecvolumemounts\">volumeMounts</a></td>\n        <td>[]object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>workingDir</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specslaveha","title":"spec.slaveHA","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nSlave high availability mechanism configuration.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>slaveHAGracePeriod</td>\n        <td>integer</td>\n        <td>\n          Grace period in seconds between node failure and when the high availability mechanism starts relocating shards. Set to 0 to not affect cluster configuration.<br/>\n          <br/>\n            <i>Format</i>: int32<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specsso","title":"spec.sso","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nCluster-level SSO configuration for authentication to the Cluster Manager UI.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Enables SSO for Cluster Manager authentication. SSO requires the following configuration - Service Provider certificate (spec.certificates.ssoServiceCertificateSecretName), Identity Provider certificate (spec.certificates.ssoIssuerCertificateSecretName), IdP metadata or manual issuer configuration (spec.sso.saml.idpMetadataSecretName or spec.sso.saml.issuer), and Base address for Service Provider URLs (auto-determined from UI service or set via spec.sso.saml.serviceProvider.baseAddress).<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td><a href=\"#specssosaml\">saml</a></td>\n        <td>object</td>\n        <td>\n          SAML-based SSO configuration. Currently,SAML is the only supported SSO protocol.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>enforceSSO</td>\n        <td>boolean</td>\n        <td>\n          Enforces SSO-only authentication for the Cluster Manager. When true, local username/password authentication is disabled for non-admin users. When false (default), both SSO and local authentication are available.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specssosaml","title":"spec.sso.saml","role":"content","text":"<sup><sup>[↩ Parent](#specsso)</sup></sup>\n\nSAML-based SSO configuration. Currently,SAML is the only supported SSO protocol.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>idpMetadataSecretName</td>\n        <td>string</td>\n        <td>\n          Name of a secret in the same namespace that contains the Identity Provider (IdP) metadata XML. The secret must contain a key named 'idp_metadata' with the IdP metadata XML content. The XML can be plain text or base64-encoded; the operator handles encoding as needed. Obtain this metadata from your SAML Identity Provider (e.g., Okta or Azure AD). This is the recommended configuration method, as it's less error-prone. Either idpMetadataSecretName or issuer must be specified. If both are provided, idpMetadataSecretName takes precedence and issuer is ignored.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specssosamlissuer\">issuer</a></td>\n        <td>object</td>\n        <td>\n          Manual Identity Provider (IdP) configuration. Use this when IdP metadata XML is unavailable. Either idpMetadataSecretName or issuer must be specified. If both are provided, idpMetadataSecretName takes precedence and issuer is ignored.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specssosamlserviceprovider\">serviceProvider</a></td>\n        <td>object</td>\n        <td>\n          Service Provider (SP) configuration.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>spMetadataSecretName</td>\n        <td>string</td>\n        <td>\n          Name of a secret where the operator stores the Service Provider (SP) metadata XML. The operator creates this secret with a key named 'sp_metadata' that contains the base64-encoded SP metadata XML. Upload this metadata to your Identity Provider. If not specified, defaults to \"<cluster-name>-sso-sp-metadata\". If not specified, the Service Provider metadata isn't stored in a K8s secret, but can still be obtained directly from the cluster's UI and/or API. Note: This secret is only created when the cluster is configured to use Kubernetes secrets (spec.clusterCredentialSecretType is unset or set to \"kubernetes\"). When using Vault secrets, the operator does not create this secret. Users can obtain the SP metadata directly from the Redis Enterprise Server API endpoint: GET /v1/cluster/sso/saml/metadata/sp and store it in Vault themselves if needed.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specssosamlissuer","title":"spec.sso.saml.issuer","role":"content","text":"<sup><sup>[↩ Parent](#specssosaml)</sup></sup>\n\nManual Identity Provider (IdP) configuration. Use this when IdP metadata XML is unavailable. Either idpMetadataSecretName or issuer must be specified. If both are provided, idpMetadataSecretName takes precedence and issuer is ignored.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>entityID</td>\n        <td>string</td>\n        <td>\n          Identity Provider entity ID (issuer identifier). Example: \"urn:sso:example:idp\" or \"https://idp.example.com\".<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>loginURL</td>\n        <td>string</td>\n        <td>\n          Identity Provider SSO login URL where SAML authentication requests are sent. Example: \"https://idp.example.com/sso/saml\".<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>logoutURL</td>\n        <td>string</td>\n        <td>\n          Identity Provider single logout URL where SAML logout requests are sent.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specssosamlserviceprovider","title":"spec.sso.saml.serviceProvider","role":"content","text":"<sup><sup>[↩ Parent](#specssosaml)</sup></sup>\n\nService Provider (SP) configuration.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>baseAddress</td>\n        <td>string</td>\n        <td>\n          Base address used to construct Service Provider (SP) URLs, such as the ACS URL and SLO URL. Format: [<scheme>://]<hostname>[:<port>]. Examples: \"https://redis-ui.example.com:9443\" (recommended - explicit scheme), \"redis-ui.example.com:9443\" (defaults to https://), \"http://redis-ui.example.com:9443\" (NOT recommended for production). If the scheme is not specified, the operator automatically prepends \"https://\". WARNING: Using \"http://\" is NOT recommended for production environments as it transmits sensitive SAML assertions in plaintext. Only use \"http://\" for testing/development purposes. If set, this value is used to construct the SP URLs. If unset, the base address is automatically determined from the REC Cluster Manager UI service: - If the UI service type is LoadBalancer (configured via spec.uiServiceType), the load balancer address is used. - Otherwise, the cluster-internal DNS name is used (e.g., rec-ui.svc.cluster.local). - The port defaults to 8443 if not specified. Usage guidelines: - For LoadBalancer services: Leave this field blank to use the default REC UI service, or set it explicitly to the LoadBalancer address for custom services. - For Ingress: Set this to the ingress hostname and port (typically 443), e.g., \"https://redis-ui.example.com:443\".<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specupgradespec","title":"spec.upgradeSpec","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nRedis Enterprise upgrade configuration\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>autoUpgradeRedisEnterprise</td>\n        <td>boolean</td>\n        <td>\n          Enables automatic Redis Enterprise upgrades when the operator is upgraded.<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specusagemeter","title":"spec.usageMeter","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nThe configuration of the usage meter.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td><a href=\"#specusagemetercallhomeclient\">callHomeClient</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specusagemetercallhomeclient","title":"spec.usageMeter.callHomeClient","role":"content","text":"<sup><sup>[↩ Parent](#specusagemeter)</sup></sup>\n\n\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>cronExpression</td>\n        <td>string</td>\n        <td>\n          Cron expression for scheduling the call home CronJob (e.g., \"0 */6 * * *\"). If not specified, the CALL_HOME_CLIENT_CRON_SCHEDULE environment variable is used, or the default value of \"0 23 * * *\" (23:00 UTC daily). Changing defaults is not recommended.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>disabled</td>\n        <td>boolean</td>\n        <td>\n          Whether to disable the call home client. Enabled by default.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specusagemetercallhomeclientimagespec\">imageSpec</a></td>\n        <td>object</td>\n        <td>\n          Image specification<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>interval</td>\n        <td>string</td>\n        <td>\n          Interval between call home reports (e.g., \"1h\", \"30m\"). Passed as --interval flag to the call home client binary. If not specified, the CALL_HOME_CLIENT_INTERVAL environment variable is used, or the default value of 24h. Changing defaults is not recommended.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>proxySecretName</td>\n        <td>string</td>\n        <td>\n          if needed, add proxy details in secret. the name of the proxy secret in the secret, can send the following keys: proxy-url, proxy-username, proxy-password (the url includes the proxy port).<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specusagemetercallhomeclientresources\">resources</a></td>\n        <td>object</td>\n        <td>\n          Compute resource requirements for Call Home Client pod<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specusagemetercallhomeclients3target\">s3Target</a></td>\n        <td>object</td>\n        <td>\n          S3-compatible storage target for call home data upload. When enabled, call home data will be uploaded to this S3 target only. Before using this feature, please coordinate with Redis.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specusagemetercallhomeclientimagespec","title":"spec.usageMeter.callHomeClient.imageSpec","role":"content","text":"<sup><sup>[↩ Parent](#specusagemetercallhomeclient)</sup></sup>\n\nImage specification\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>digestHash</td>\n        <td>string</td>\n        <td>\n          The digest hash of the container image to pull. When specified, the container image is pulled according to the digest hash instead of the image tag. The versionTag field must also be specified with the image tag matching this digest hash. Note: This field is only supported for OLM deployments.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>imagePullPolicy</td>\n        <td>string</td>\n        <td>\n          The image pull policy to be applied to the container image. One of Always, Never, IfNotPresent.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>repository</td>\n        <td>string</td>\n        <td>\n          The repository (name) of the container image to be deployed.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>versionTag</td>\n        <td>string</td>\n        <td>\n          The tag of the container image to be deployed.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specusagemetercallhomeclientresources","title":"spec.usageMeter.callHomeClient.resources","role":"content","text":"<sup><sup>[↩ Parent](#specusagemetercallhomeclient)</sup></sup>\n\nCompute resource requirements for Call Home Client pod\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td><a href=\"#specusagemetercallhomeclientresourcesclaims\">claims</a></td>\n        <td>[]object</td>\n        <td>\n          Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable. It can only be set for containers.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>limits</td>\n        <td>map[string]int or string</td>\n        <td>\n          Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>requests</td>\n        <td>map[string]int or string</td>\n        <td>\n          Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specusagemetercallhomeclientresourcesclaims","title":"spec.usageMeter.callHomeClient.resources.claims[]","role":"content","text":"<sup><sup>[↩ Parent](#specusagemetercallhomeclientresources)</sup></sup>\n\nResourceClaim references one entry in PodSpec.ResourceClaims.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>name</td>\n        <td>string</td>\n        <td>\n          Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container.<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specusagemetercallhomeclients3target","title":"spec.usageMeter.callHomeClient.s3Target","role":"content","text":"<sup><sup>[↩ Parent](#specusagemetercallhomeclient)</sup></sup>\n\nS3-compatible storage target for call home data upload. When enabled, call home data will be uploaded to this S3 target only. Before using this feature, please coordinate with Redis.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>bucket</td>\n        <td>string</td>\n        <td>\n          S3 bucket name. Required when S3Target is enabled.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>credentialsSecretName</td>\n        <td>string</td>\n        <td>\n          Name of the Kubernetes secret containing S3 credentials. The secret must contain keys \"access-key\" and \"secret-key\". Optional keys - \"session-token\" (for AWS STS), \"ca-cert\" (for custom CA). The credentials must have s3:PutObject permission on the target bucket.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Whether S3 upload is enabled. When true, call home data will be uploaded to the specified S3 target only.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>endpoint</td>\n        <td>string</td>\n        <td>\n          S3-compatible endpoint URL.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>prefix</td>\n        <td>string</td>\n        <td>\n          S3 object key prefix/subfolder for uploaded files (e.g., \"reports/2025\"). If specified, files will be uploaded to s3://bucket/prefix/filename.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>region</td>\n        <td>string</td>\n        <td>\n          AWS region for the S3 bucket (e.g., \"us-east-1\").<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>url</td>\n        <td>string</td>\n        <td>\n          Full S3 URL including bucket (e.g., \"https://bucket.s3.region.amazonaws.com\" or \"s3://bucket/prefix\").<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specuserdefinedmodules","title":"spec.userDefinedModules[]","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nUserDefinedModule represents a user-defined Redis module to be downloaded and installed during bootstrap\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>name</td>\n        <td>string</td>\n        <td>\n          Name of the module<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td><a href=\"#specuserdefinedmodulessource\">source</a></td>\n        <td>object</td>\n        <td>\n          Source location for downloading the module<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specuserdefinedmodulessource","title":"spec.userDefinedModules[].source","role":"content","text":"<sup><sup>[↩ Parent](#specuserdefinedmodules)</sup></sup>\n\nSource location for downloading the module\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td><a href=\"#specuserdefinedmodulessourcehttp\">http</a></td>\n        <td>object</td>\n        <td>\n          HTTP source configuration for downloading the module via HTTP<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specuserdefinedmodulessourcehttps\">https</a></td>\n        <td>object</td>\n        <td>\n          HTTPS source configuration for downloading the module via HTTPS<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specuserdefinedmodulessourcehttp","title":"spec.userDefinedModules[].source.http","role":"content","text":"<sup><sup>[↩ Parent](#specuserdefinedmodulessource)</sup></sup>\n\nHTTP source configuration for downloading the module via HTTP\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>url</td>\n        <td>string</td>\n        <td>\n          URL to download the module from (must use http:// scheme)<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>credentialsSecret</td>\n        <td>string</td>\n        <td>\n          Name of the Kubernetes secret containing credentials for downloading the module, if needed. The secret must contain 'username' and 'password' keys.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specuserdefinedmodulessourcehttps","title":"spec.userDefinedModules[].source.https","role":"content","text":"<sup><sup>[↩ Parent](#specuserdefinedmodulessource)</sup></sup>\n\nHTTPS source configuration for downloading the module via HTTPS\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>url</td>\n        <td>string</td>\n        <td>\n          URL to download the module from (must use https:// scheme)<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>credentialsSecret</td>\n        <td>string</td>\n        <td>\n          Name of the Kubernetes secret containing credentials for downloading the module, if needed. The secret must contain 'username' and 'password' keys.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specvolumes","title":"spec.volumes[]","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nVolume represents a named volume in a pod that may be accessed by any container in the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>name</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td><a href=\"#specvolumesawselasticblockstore\">awsElasticBlockStore</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specvolumesazuredisk\">azureDisk</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specvolumesazurefile\">azureFile</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specvolumescephfs\">cephfs</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specvolumescinder\">cinder</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specvolumesconfigmap\">configMap</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specvolumescsi\">csi</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specvolumesdownwardapi\">downwardAPI</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specvolumesemptydir\">emptyDir</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specvolumesfc\">fc</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specvolumesflexvolume\">flexVolume</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specvolumesflocker\">flocker</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specvolumesgcepersistentdisk\">gcePersistentDisk</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specvolumesgitrepo\">gitRepo</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specvolumesglusterfs\">glusterfs</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specvolumeshostpath\">hostPath</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specvolumesiscsi\">iscsi</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specvolumesnfs\">nfs</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specvolumespersistentvolumeclaim\">persistentVolumeClaim</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specvolumesphotonpersistentdisk\">photonPersistentDisk</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specvolumesportworxvolume\">portworxVolume</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specvolumesprojected\">projected</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specvolumesquobyte\">quobyte</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specvolumesrbd\">rbd</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specvolumesscaleio\">scaleIO</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specvolumessecret\">secret</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specvolumesstorageos\">storageos</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specvolumesvspherevolume\">vsphereVolume</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"status","title":"status","role":"content","text":"<sup><sup>[↩ Parent](#)</sup></sup>\n\n\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td><a href=\"#statusbundleddatabaseversions\">bundledDatabaseVersions</a></td>\n        <td>[]object</td>\n        <td>\n          Versions of open source databases bundled by Redis Enterprise Software - please note that in order to use a specific version it should be supported by the ‘upgradePolicy’ - ‘major’ or ‘latest’ according to the desired version (major/minor)<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#statuscertificatesstatus\">certificatesStatus</a></td>\n        <td>object</td>\n        <td>\n          Stores information about cluster certificates and their update process. In Active-Active databases, this is used to detect updates to the certificates, and trigger synchronization across the participating clusters.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>clusterCredentialSecretName</td>\n        <td>string</td>\n        <td>\n          The name of the secret containing cluster credentials currently in use by the cluster. This field tracks the current credential secret name and is updated when the secret name changes.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>ingressOrRouteMethodStatus</td>\n        <td>string</td>\n        <td>\n          The ingressOrRouteSpec/ActiveActive spec method that exist<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#statuslicensestatus\">licenseStatus</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#statusmanagedapis\">managedAPIs</a></td>\n        <td>object</td>\n        <td>\n          Indicates cluster APIs that are being managed by the operator. This only applies to cluster APIs which are optionally-managed by the operator, such as cluster LDAP configuration. Most other APIs are automatically managed by the operator, and are not listed here.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#statusmodules\">modules</a></td>\n        <td>[]object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#statusocspstatus\">ocspStatus</a></td>\n        <td>object</td>\n        <td>\n          An API object that represents the cluster's OCSP status<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#statuspersistencestatus\">persistenceStatus</a></td>\n        <td>object</td>\n        <td>\n          The status of the Persistent Volume Claims that are used for Redis Enterprise cluster persistence. The status will correspond to the status of one or more of the PVCs (failed/resizing if one of them is in resize or failed to resize)<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>redisEnterpriseIPFamily</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>specStatus</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>state</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"statusbundleddatabaseversions","title":"status.bundledDatabaseVersions[]","role":"content","text":"<sup><sup>[↩ Parent](#status)</sup></sup>\n\n\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>dbType</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>version</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td><a href=\"#statusbundleddatabaseversionsfeaturesupport\">featureSupport</a></td>\n        <td>object</td>\n        <td>\n          Feature support flags for this database version<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>major</td>\n        <td>boolean</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"statusbundleddatabaseversionsfeaturesupport","title":"status.bundledDatabaseVersions[].featureSupport","role":"content","text":"<sup><sup>[↩ Parent](#statusbundleddatabaseversions)</sup></sup>\n\nFeature support flags for this database version\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>activeActive</td>\n        <td>boolean</td>\n        <td>\n          Indicates whether this version supports Active-Active (CRDB) databases<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>flex</td>\n        <td>boolean</td>\n        <td>\n          Indicates whether this version supports Redis on Flash (Flex)<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"statuscertificatesstatus","title":"status.certificatesStatus","role":"content","text":"<sup><sup>[↩ Parent](#status)</sup></sup>\n\nStores information about cluster certificates and their update process. In Active-Active databases, this is used to detect updates to the certificates, and trigger synchronization across the participating clusters.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>generation</td>\n        <td>integer</td>\n        <td>\n          Generation stores the version of the cluster's Proxy and Syncer certificate secrets. This generation counter is automatically incremented when proxy or syncer certificates are updated. In Active-Active databases (REAADB), the operator monitors this field to detect certificate changes and automatically triggers a CRDB force update (equivalent to 'crdb-cli crdb update --force'), which synchronizes the certificate changes to all participating clusters, eliminating the need for manual intervention to maintain sync.<br/>\n          <br/>\n            <i>Format</i>: int64<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>updateStatus</td>\n        <td>string</td>\n        <td>\n          The status of the cluster's certificates update<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"statuslicensestatus","title":"status.licenseStatus","role":"content","text":"<sup><sup>[↩ Parent](#status)</sup></sup>\n\n\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>activationDate</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>expirationDate</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>features</td>\n        <td>[]string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>flashShards</td>\n        <td>integer</td>\n        <td>\n          <br/>\n          <br/>\n            <i>Format</i>: int32<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>flashShardsLimit</td>\n        <td>integer</td>\n        <td>\n          <br/>\n          <br/>\n            <i>Format</i>: int32<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>licenseState</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>owner</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>ramShards</td>\n        <td>integer</td>\n        <td>\n          <br/>\n          <br/>\n            <i>Format</i>: int32<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>ramShardsLimit</td>\n        <td>integer</td>\n        <td>\n          <br/>\n          <br/>\n            <i>Format</i>: int32<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>shardsLimit</td>\n        <td>integer</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>shardsUsage</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"statusmanagedapis","title":"status.managedAPIs","role":"content","text":"<sup><sup>[↩ Parent](#status)</sup></sup>\n\nIndicates cluster APIs that are being managed by the operator. This only applies to cluster APIs which are optionally-managed by the operator, such as cluster LDAP configuration. Most other APIs are automatically managed by the operator, and are not listed here.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>ldap</td>\n        <td>boolean</td>\n        <td>\n          Indicate whether cluster LDAP configuration is managed by the operator. When this is enabled, the operator will reconcile the cluster LDAP configuration according to the '.spec.ldap' field in the RedisEnterpriseCluster resource.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"statusmodules","title":"status.modules[]","role":"content","text":"<sup><sup>[↩ Parent](#status)</sup></sup>\n\n\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>displayName</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>name</td>\n        <td>string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>versions</td>\n        <td>[]string</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"statusocspstatus","title":"status.ocspStatus","role":"content","text":"<sup><sup>[↩ Parent](#status)</sup></sup>\n\nAn API object that represents the cluster's OCSP status\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>certStatus</td>\n        <td>string</td>\n        <td>\n          Indicates the proxy certificate status - GOOD/REVOKED/UNKNOWN.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>nextUpdate</td>\n        <td>string</td>\n        <td>\n          The time at or before which newer information will be available about the status of the certificate (if available)<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>producedAt</td>\n        <td>string</td>\n        <td>\n          The time at which the OCSP responder signed this response.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>responderUrl</td>\n        <td>string</td>\n        <td>\n          The OCSP responder url from which this status came from.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>revocationTime</td>\n        <td>string</td>\n        <td>\n          The time at which the certificate was revoked or placed on hold.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>thisUpdate</td>\n        <td>string</td>\n        <td>\n          The most recent time at which the status being indicated is known by the responder to have been correct.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"statuspersistencestatus","title":"status.persistenceStatus","role":"content","text":"<sup><sup>[↩ Parent](#status)</sup></sup>\n\nThe status of the Persistent Volume Claims that are used for Redis Enterprise cluster persistence. The status will correspond to the status of one or more of the PVCs (failed/resizing if one of them is in resize or failed to resize)\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>status</td>\n        <td>string</td>\n        <td>\n          The current status of the PVCs<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>succeeded</td>\n        <td>string</td>\n        <td>\n          The number of PVCs that are provisioned with the expected size<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/reference/api/redis_enterprise_cluster_role_api","title":"RedisEnterpriseClusterRole API Reference","url":"https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_role_api/","summary":"","tags":["docs","operate","kubernetes"],"last_updated":"2026-07-21T12:02:16-05:00","page_type":"content","content_hash":"2ee43f7661916b4cd9ce1ee016c3dec80bd2bc6cd581741a17ea379f855c5c23","sections":[{"id":"overview","title":"Overview","role":"overview","text":"apiVersion:\n\n\n- [app.redislabs.com/v1alpha1](#appredislabscomv1alpha1)\n\n\n\n\n# app.redislabs.com/v1alpha1\n\n\n\n\nRedisEnterpriseClusterRole is a cluster role definition. It grants users cluster-scoped access across the Redis Enterprise Cluster.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n      <td>apiVersion</td>\n      <td>string</td>\n      <td>app.redislabs.com/v1alpha1</td>\n      <td>true</td>\n      </tr>\n      <tr>\n      <td>kind</td>\n      <td>string</td>\n      <td>RedisEnterpriseClusterRole</td>\n      <td>true</td>\n      </tr>\n      <tr>\n      <td><a href=\"https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#objectmeta-v1-meta\">metadata</a></td>\n      <td>object</td>\n      <td>Refer to the Kubernetes API documentation for the fields of the `metadata` field.</td>\n      <td>true</td>\n      </tr><tr>\n        <td><a href=\"#spec\">spec</a></td>\n        <td>object</td>\n        <td>\n          RedisEnterpriseClusterRoleSpec defines the desired state of a RedisEnterpriseClusterRole.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#status\">status</a></td>\n        <td>object</td>\n        <td>\n          RedisEnterpriseRoleStatus defines the observed state of a Redis Enterprise role object. This status type is used by both RedisEnterpriseClusterRole and RedisEnterpriseRole which share the same status structure.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"spec","title":"spec","role":"content","text":"<sup><sup>[↩ Parent](#)</sup></sup>\n\nRedisEnterpriseClusterRoleSpec defines the desired state of a RedisEnterpriseClusterRole.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td><a href=\"#specacl\">acl</a></td>\n        <td>object</td>\n        <td>\n          ACL defines the data access permissions granted by this cluster role.\nUsers with this role can access any database in the Redis Enterprise Cluster,\nsubject to the permissions granted by the referenced ACL.\nReferences a RedisEnterpriseACL (kind RedisEnterpriseACL or empty) or an\ninternal Redis Enterprise ACL by name (kind 'redis_acl').<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>managementRole</td>\n        <td>enum</td>\n        <td>\n          ManagementRole is the named set of Redis Enterprise management permissions assigned to this cluster role. If omitted, the role uses the None management role.<br/>\n          <br/>\n            <i>Enum</i>: Admin, ClusterMember, ClusterViewer, DBMember, DBViewer, UserManager, None<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specacl","title":"spec.acl","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nACL defines the data access permissions granted by this cluster role.\nUsers with this role can access any database in the Redis Enterprise Cluster,\nsubject to the permissions granted by the referenced ACL.\nReferences a RedisEnterpriseACL (kind RedisEnterpriseACL or empty) or an\ninternal Redis Enterprise ACL by name (kind 'redis_acl').\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>name</td>\n        <td>string</td>\n        <td>\n          Name of the referent.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>kind</td>\n        <td>enum</td>\n        <td>\n          Kind of the referent.<br/>\n          <br/>\n            <i>Enum</i>: , RedisEnterpriseACL, redis_acl<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"status","title":"status","role":"content","text":"<sup><sup>[↩ Parent](#)</sup></sup>\n\nRedisEnterpriseRoleStatus defines the observed state of a Redis Enterprise role object. This status type is used by both RedisEnterpriseClusterRole and RedisEnterpriseRole which share the same status structure.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>observedGeneration</td>\n        <td>integer</td>\n        <td>\n          The most recent generation of this resource that was observed and acted upon by the controller.<br/>\n          <br/>\n            <i>Format</i>: int64<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>uid</td>\n        <td>string</td>\n        <td>\n          The internal UID of the Role object defined in the Redis Enterprise Cluster.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/reference/api/redis_enterprise_cluster_role_binding_api","title":"RedisEnterpriseClusterRoleBinding API Reference","url":"https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_role_binding_api/","summary":"","tags":["docs","operate","kubernetes"],"last_updated":"2026-07-21T12:02:16-05:00","page_type":"content","content_hash":"a6dfa97032d6d379a64ee6b3aa6e296b3cd4055f9edec3926a0afc7e709b3455","sections":[{"id":"overview","title":"Overview","role":"overview","text":"apiVersion:\n\n\n- [app.redislabs.com/v1alpha1](#appredislabscomv1alpha1)\n\n\n\n\n# app.redislabs.com/v1alpha1\n\n\n\n\nRedisEnterpriseClusterRoleBinding binds users to a RedisEnterpriseClusterRole. It grants the users the cluster access defined by that role.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n      <td>apiVersion</td>\n      <td>string</td>\n      <td>app.redislabs.com/v1alpha1</td>\n      <td>true</td>\n      </tr>\n      <tr>\n      <td>kind</td>\n      <td>string</td>\n      <td>RedisEnterpriseClusterRoleBinding</td>\n      <td>true</td>\n      </tr>\n      <tr>\n      <td><a href=\"https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#objectmeta-v1-meta\">metadata</a></td>\n      <td>object</td>\n      <td>Refer to the Kubernetes API documentation for the fields of the `metadata` field.</td>\n      <td>true</td>\n      </tr><tr>\n        <td><a href=\"#spec\">spec</a></td>\n        <td>object</td>\n        <td>\n          RedisEnterpriseRoleBindingSpec defines the desired state of a RedisEnterpriseClusterRoleBinding or RedisEnterpriseRoleBinding.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>status</td>\n        <td>object</td>\n        <td>\n          RedisEnterpriseRoleBindingStatus defines the observed state of a RedisEnterpriseClusterRoleBinding or RedisEnterpriseRoleBinding.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"spec","title":"spec","role":"content","text":"<sup><sup>[↩ Parent](#)</sup></sup>\n\nRedisEnterpriseRoleBindingSpec defines the desired state of a RedisEnterpriseClusterRoleBinding or RedisEnterpriseRoleBinding.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td><a href=\"#specroleref\">roleRef</a></td>\n        <td>object</td>\n        <td>\n          RoleRef holds a reference to the role object being bound. For RedisEnterpriseClusterRoleBinding, this references a RedisEnterpriseClusterRole in the same namespace (kind RedisEnterpriseClusterRole or empty) or an internal Redis Enterprise role by name (kind 'role'). For RedisEnterpriseRoleBinding, this references a RedisEnterpriseRole in the same namespace (kind RedisEnterpriseRole or empty) or an internal Redis Enterprise role by name (kind 'role').<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td><a href=\"#specsubjects\">subjects</a></td>\n        <td>[]object</td>\n        <td>\n          Subjects holds references to the objects the role applies to. Each subject is a reference to a RedisEnterpriseUser object within the same namespace. The 'kind' field can be left empty, or set to RedisEnterpriseUser. A maximum of 100 subjects may be specified; to bind more, create additional bindings referencing the same role.<br/>\n          <br/>\n            <i>Validations</i>:<li>self.all(s1, self.exists_one(s2,\n  (has(s1.kind) && s1.kind != '' ? s1.kind : 'RedisEnterpriseUser') ==\n  (has(s2.kind) && s2.kind != '' ? s2.kind : 'RedisEnterpriseUser') &&\n  s1.name == s2.name))\n: subjects must be unique by kind and name</li>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specroleref","title":"spec.roleRef","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nRoleRef holds a reference to the role object being bound. For RedisEnterpriseClusterRoleBinding, this references a RedisEnterpriseClusterRole in the same namespace (kind RedisEnterpriseClusterRole or empty) or an internal Redis Enterprise role by name (kind 'role'). For RedisEnterpriseRoleBinding, this references a RedisEnterpriseRole in the same namespace (kind RedisEnterpriseRole or empty) or an internal Redis Enterprise role by name (kind 'role').\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>name</td>\n        <td>string</td>\n        <td>\n          Name of the referent.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>kind</td>\n        <td>enum</td>\n        <td>\n          Kind of the referent.<br/>\n          <br/>\n            <i>Enum</i>: , RedisEnterpriseClusterRole, role<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specsubjects","title":"spec.subjects[]","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nA reference to another object.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>name</td>\n        <td>string</td>\n        <td>\n          Name of the referent.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>kind</td>\n        <td>enum</td>\n        <td>\n          Kind of the referent.<br/>\n          <br/>\n            <i>Enum</i>: , RedisEnterpriseUser<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/reference/api/redis_enterprise_database_api","title":"RedisEnterpriseDatabase API Reference","url":"https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api/","summary":"","tags":["docs","operate","kubernetes"],"last_updated":"2026-07-21T12:02:16-05:00","page_type":"content","content_hash":"2a6611f974cd6f4d54c30d8c4a47e0a801eb778e4d86cc9822c62a0f7da2a65b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"apiVersion:\n\n\n- [app.redislabs.com/v1alpha1](#appredislabscomv1alpha1)\n\n\n\n\n# app.redislabs.com/v1alpha1\n\n\n\n\nRedisEnterpriseDatabase is the Schema for the redisenterprisedatabases API\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n      <td>apiVersion</td>\n      <td>string</td>\n      <td>app.redislabs.com/v1alpha1</td>\n      <td>true</td>\n      </tr>\n      <tr>\n      <td>kind</td>\n      <td>string</td>\n      <td>RedisEnterpriseDatabase</td>\n      <td>true</td>\n      </tr>\n      <tr>\n      <td><a href=\"https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#objectmeta-v1-meta\">metadata</a></td>\n      <td>object</td>\n      <td>Refer to the Kubernetes API documentation for the fields of the `metadata` field.</td>\n      <td>true</td>\n      </tr><tr>\n        <td><a href=\"#spec\">spec</a></td>\n        <td>object</td>\n        <td>\n          RedisEnterpriseDatabaseSpec defines the desired state of RedisEnterpriseDatabase<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#status\">status</a></td>\n        <td>object</td>\n        <td>\n          RedisEnterpriseDatabaseStatus defines the observed state of RedisEnterpriseDatabase<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"spec","title":"spec","role":"content","text":"<sup><sup>[↩ Parent](#)</sup></sup>\n\nRedisEnterpriseDatabaseSpec defines the desired state of RedisEnterpriseDatabase\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td><a href=\"#specactiveactive\">activeActive</a></td>\n        <td>object</td>\n        <td>\n          Connection/ association to the Active-Active database.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specalertsettings\">alertSettings</a></td>\n        <td>object</td>\n        <td>\n          Settings for database alerts<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specauditing\">auditing</a></td>\n        <td>object</td>\n        <td>\n          Database auditing configuration.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specbackup\">backup</a></td>\n        <td>object</td>\n        <td>\n          Target for automatic database backups.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>clientAuthenticationCertificates</td>\n        <td>[]string</td>\n        <td>\n          The Secrets containing TLS Client Certificate to use for Authentication<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specconnectionsettings\">connectionSettings</a></td>\n        <td>object</td>\n        <td>\n          Connection-related settings such as proxy connections and scheduling policy.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>dataInternodeEncryption</td>\n        <td>boolean</td>\n        <td>\n          Internode encryption (INE) setting. An optional boolean setting, overriding a similar cluster-wide policy. If set to False, INE is guaranteed to be turned off for this DB (regardless of cluster-wide policy). If set to True, INE will be turned on, unless the capability is not supported by the DB ( in such a case we will get an error and database creation will fail). If left unspecified, will be disabled if internode encryption is not supported by the DB (regardless of cluster default). Deleting this property after explicitly setting its value shall have no effect.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>databasePort</td>\n        <td>integer</td>\n        <td>\n          TCP port assigned to the database within the Redis Enterprise cluster. Must be unique across all databases in the Redis Enterprise cluster. Will be generated automatically if omitted. can not be changed after creation<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>databaseSecretName</td>\n        <td>string</td>\n        <td>\n          Name of the secret containing the database password (Redis databases only). The secret is created automatically if it does not exist. The password is stored under the \"password\" key in the secret. If creating the secret manually, create an opaque secret with the password under the \"password\" key. To disable authentication, set the value of the \"password\" key in the secret to an empty string. Note: For Active-Active databases, this secret is not created automatically. For memcached databases, use memcachedSaslSecretName instead.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>databaseServicePort</td>\n        <td>integer</td>\n        <td>\n          A custom port to be exposed by the database Services. Can be modified/added/removed after REDB creation. If set, it'll replace the default service port (namely, databasePort or defaultRedisPort).<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>defaultUser</td>\n        <td>boolean</td>\n        <td>\n          Allows connections with the default user. When disabled, the DatabaseSecret is not created or updated.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>evictionPolicy</td>\n        <td>string</td>\n        <td>\n          Database eviction policy. See https://redis.io/docs/latest/operate/rs/databases/memory-performance/eviction-policy/<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>isRof</td>\n        <td>boolean</td>\n        <td>\n          Enables Auto Tiering (formerly Redis on Flash) for Redis databases only. Defaults to false.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>memcachedSaslSecretName</td>\n        <td>string</td>\n        <td>\n          Name of the secret containing credentials for memcached database authentication. Store credentials in an opaque secret with \"username\" and \"password\" keys. Note: Connections are not encrypted.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>memorySize</td>\n        <td>string</td>\n        <td>\n          Memory size for the database using formats like 100MB or 0.1GB. Minimum value is 100MB. For Auto Tiering (formerly Redis on Flash), this value represents RAM+Flash memory and must be at least 1GB.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specmoduleslist\">modulesList</a></td>\n        <td>[]object</td>\n        <td>\n          List of modules associated with the database. The list of valid modules for the specific cluster can be retrieved from the status of the REC object. Use the \"name\" and \"versions\" fields for the specific module configuration. If specifying an explicit version for a module, automatic modules versions upgrade must be disabled by setting the '.upgradeSpec.upgradeModulesToLatest' field in the REC to 'false'. Note that the option to specify module versions is deprecated, and will be removed in future releases. for Redis version 8 and above, bundled modules are enabled automatically, so there is no need to specify them<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>ossCluster</td>\n        <td>boolean</td>\n        <td>\n          Enables OSS cluster mode for this database. By default, advertised database topology includes the internal endpoints (pod IPs) for the Redis Enterprise nodes hosting the database shards. To enable external access, configure ossClusterSettings.enableExternalAccess for this RedisEnterpriseDatabase as well as ossClusterSettings.externalAccessType for the RedisEnterpriseCluster. Note: Not all client libraries support OSS cluster mode.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specossclustersettings\">ossClusterSettings</a></td>\n        <td>object</td>\n        <td>\n          Additional OSS cluster mode settings.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>persistence</td>\n        <td>enum</td>\n        <td>\n          Database persistence policy for on-disk storage.<br/>\n          <br/>\n            <i>Enum</i>: disabled, aofEverySecond, aofAlways, snapshotEvery1Hour, snapshotEvery6Hour, snapshotEvery12Hour<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>proxyPolicy</td>\n        <td>string</td>\n        <td>\n          Proxy policy for the database. Supported proxy policies are: single/all-master-shards/all-nodes When left blank, the default value will be chosen according to the value of ossCluster - single if disabled, all-master-shards when enabled<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>rackAware</td>\n        <td>boolean</td>\n        <td>\n          Enables rack awareness for improved availability. See https://redis.io/docs/latest/operate/rs/clusters/configure/rack-zone-awareness/<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specredisenterprisecluster\">redisEnterpriseCluster</a></td>\n        <td>object</td>\n        <td>\n          Connection to the Redis Enterprise Cluster.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>redisVersion</td>\n        <td>string</td>\n        <td>\n          Redis OSS version. Version can be specified via <major.minor> prefix, or via channels - for existing databases - Upgrade Redis OSS version. For new databases - the version which the database will be created with. If set to 'major' - will always upgrade to the most recent major Redis version. If set to 'latest' - will always upgrade to the most recent Redis version. Depends on 'redisUpgradePolicy' - if you want to set the value to 'latest' for some databases, you must set redisUpgradePolicy on the cluster before. Possible values are 'major' or 'latest' When using upgrade - make sure to backup the database before. This value is used only for database type 'redis'<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specreplicasources\">replicaSources</a></td>\n        <td>[]object</td>\n        <td>\n          What databases to replicate from<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>replication</td>\n        <td>boolean</td>\n        <td>\n          Enables in-memory database replication for higher availability. Creates a replica shard for every master shard. Defaults to false.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>resp3</td>\n        <td>boolean</td>\n        <td>\n          Whether this database supports RESP3 protocol. Note - Deleting this property after explicitly setting its value shall have no effect. Please view the corresponding field in RS doc for more info.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>rofRamRatio</td>\n        <td>integer</td>\n        <td>\n          RAM allocation ratio for Redis Flex (v2) databases as a percentage of total data size. Valid range 0-100. When omitted, RS uses the default value of 50%. Controls how much RAM is allocated per unit of data (e.g., 30% means 3MB RAM per 10MB data). RAM grows proportionally with data until rofRamSize limit is reached (if specified). Only applicable when isRof=true and Redis version >= 8.0 (BigStore v2 - Redis Flex).<br/>\n          <br/>\n            <i>Minimum</i>: 0<br/>\n            <i>Maximum</i>: 100<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>rofRamSize</td>\n        <td>string</td>\n        <td>\n          The size of the RAM portion of an Auto Tiering (formerly Redis on Flash) database. Similarly to \"memorySize\" use formats like 100MB, 0.1GB. It must be at least 10% of combined memory size (RAM and Flash), as specified by \"memorySize\".<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specrolespermissions\">rolesPermissions</a></td>\n        <td>[]object</td>\n        <td>\n          Deprecated: List of Redis Enterprise ACL and Role bindings to apply. This field is deprecated and is ignored when the cluster's spec.accessControl.policy.allowREDBRolesPermissions is set to false.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>searchOnBigstore</td>\n        <td>boolean</td>\n        <td>\n          Enables search module indexing on flash storage for Redis Flex (v2) databases. Only applicable when isRof=true and Redis version >= 8.6. Defaults to false.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>shardCount</td>\n        <td>integer</td>\n        <td>\n          Number of database server-side shards.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>shardingEnabled</td>\n        <td>boolean</td>\n        <td>\n          Toggles database sharding for REAADBs (Active Active databases) and enabled by default. This field is blocked for REDB (non-Active Active databases) and sharding is toggled via the shardCount field - when shardCount is 1 this is disabled otherwise enabled.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>shardsPlacement</td>\n        <td>enum</td>\n        <td>\n          Shard placement strategy: \"dense\" or \"sparse\". dense: Shards reside on as few nodes as possible. sparse: Shards are distributed across as many nodes as possible.<br/>\n          <br/>\n            <i>Enum</i>: dense, sparse<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>tags</td>\n        <td>map[string]string</td>\n        <td>\n          Tags to apply to the database, as a map of key to value. Tags are merged into the BDB's tags on the cluster. If a key here collides with an existing BDB tag, the value from spec.tags wins. Removing a key from spec.tags after it was previously synced (recorded in status.managedTags) will remove it from the BDB. Operator-reserved keys (managed_by, redb_name, redb_namespace, db_service_port, oss_cluster_access) are not allowed and will be rejected.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>tlsMode</td>\n        <td>enum</td>\n        <td>\n          Require TLS authenticated and encrypted connections to the database. enabled - all client and replication connections to the Database must use TLS. disabled - no incoming connection to the Database should use TLS. replica_ssl - databases that replicate from this one need to use TLS.<br/>\n          <br/>\n            <i>Enum</i>: disabled, enabled, replica_ssl<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>type</td>\n        <td>enum</td>\n        <td>\n          Database type: redis or memcached.<br/>\n          <br/>\n            <i>Enum</i>: redis, memcached<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specupgradespec\">upgradeSpec</a></td>\n        <td>object</td>\n        <td>\n          Specifications for DB upgrade.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specactiveactive","title":"spec.activeActive","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nConnection/ association to the Active-Active database.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>name</td>\n        <td>string</td>\n        <td>\n          The the corresponding Active-Active database name, Redis Enterprise Active Active Database custom resource name, this Resource is associated with. In case this resource is created manually at the active active database creation this field must be filled via the user, otherwise, the operator will assign this field automatically. Note: this feature is currently unsupported.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>participatingClusterName</td>\n        <td>string</td>\n        <td>\n          The corresponding participating cluster name, Redis Enterprise Remote Cluster custom resource name, in the Active-Active database, In case this resource is created manually at the active active database creation this field must be filled via the user, otherwise, the operator will assign this field automatically. Note: this feature is currently unsupported.<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specalertsettings","title":"spec.alertSettings","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nSettings for database alerts\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td><a href=\"#specalertsettingsbdb_backup_delayed\">bdb_backup_delayed</a></td>\n        <td>object</td>\n        <td>\n          Periodic backup has been delayed for longer than specified threshold value [minutes]. -Note threshold is commented (allow string/int/float and support backwards compatibility) but is required<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specalertsettingsbdb_crdt_src_high_syncer_lag\">bdb_crdt_src_high_syncer_lag</a></td>\n        <td>object</td>\n        <td>\n          Active-active source - sync lag is higher than specified threshold value [seconds] -Note threshold is commented (allow string/int/float and support backwards compatibility) but is required<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specalertsettingsbdb_crdt_src_syncer_connection_error\">bdb_crdt_src_syncer_connection_error</a></td>\n        <td>object</td>\n        <td>\n          Active-active source - sync has connection error while trying to connect replica source -Note threshold is commented (allow string/int/float and support backwards compatibility) but is required<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specalertsettingsbdb_crdt_src_syncer_general_error\">bdb_crdt_src_syncer_general_error</a></td>\n        <td>object</td>\n        <td>\n          Active-active source - sync encountered in general error -Note threshold is commented (allow string/int/float and support backwards compatibility) but is required<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specalertsettingsbdb_high_latency\">bdb_high_latency</a></td>\n        <td>object</td>\n        <td>\n          Latency is higher than specified threshold value [micro-sec] -Note threshold is commented (allow string/int/float and support backwards compatibility) but is required<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specalertsettingsbdb_high_throughput\">bdb_high_throughput</a></td>\n        <td>object</td>\n        <td>\n          Throughput is higher than specified threshold value [requests / sec.] -Note threshold is commented (allow string/int/float and support backwards compatibility) but is required<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specalertsettingsbdb_long_running_action\">bdb_long_running_action</a></td>\n        <td>object</td>\n        <td>\n          An alert for state-machines that are running for too long -Note threshold is commented (allow string/int/float and support backwards compatibility) but is required<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specalertsettingsbdb_low_throughput\">bdb_low_throughput</a></td>\n        <td>object</td>\n        <td>\n          Throughput is lower than specified threshold value [requests / sec.] -Note threshold is commented (allow string/int/float and support backwards compatibility) but is required<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specalertsettingsbdb_proxy_cert_expiring_soon\">bdb_proxy_cert_expiring_soon</a></td>\n        <td>object</td>\n        <td>\n          Proxy certificate will expire in less than specified threshold value [days]<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specalertsettingsbdb_ram_dataset_overhead\">bdb_ram_dataset_overhead</a></td>\n        <td>object</td>\n        <td>\n          Dataset RAM overhead of a shard has reached the threshold value [% of its RAM limit] -Note threshold is commented (allow string/int/float and support backwards compatibility) but is required<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specalertsettingsbdb_ram_values\">bdb_ram_values</a></td>\n        <td>object</td>\n        <td>\n          Percent of values kept in a shard's RAM is lower than [% of its key count] -Note threshold is commented (allow string/int/float and support backwards compatibility) but is required<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specalertsettingsbdb_replica_src_high_syncer_lag\">bdb_replica_src_high_syncer_lag</a></td>\n        <td>object</td>\n        <td>\n          Replica-of source - sync lag is higher than specified threshold value [seconds] -Note threshold is commented (allow string/int/float and support backwards compatibility) but is required<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specalertsettingsbdb_replica_src_syncer_connection_error\">bdb_replica_src_syncer_connection_error</a></td>\n        <td>object</td>\n        <td>\n          Replica-of source - sync has connection error while trying to connect replica source -Note threshold is commented (allow string/int/float and support backwards compatibility) but is required<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specalertsettingsbdb_shard_num_ram_values\">bdb_shard_num_ram_values</a></td>\n        <td>object</td>\n        <td>\n          Number of values kept in a shard's RAM is lower than [values] -Note threshold is commented (allow string/int/float and support backwards compatibility) but is required<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specalertsettingsbdb_size\">bdb_size</a></td>\n        <td>object</td>\n        <td>\n          Dataset size has reached the threshold value [% of the memory limit] expected fields: -Note threshold is commented (allow string/int/float and support backwards compatibility) but is required<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specalertsettingsbdb_backup_delayed","title":"spec.alertSettings.bdb_backup_delayed","role":"content","text":"<sup><sup>[↩ Parent](#specalertsettings)</sup></sup>\n\nPeriodic backup has been delayed for longer than specified threshold value [minutes]. -Note threshold is commented (allow string/int/float and support backwards compatibility) but is required\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Alert enabled or disabled<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specalertsettingsbdb_crdt_src_high_syncer_lag","title":"spec.alertSettings.bdb_crdt_src_high_syncer_lag","role":"content","text":"<sup><sup>[↩ Parent](#specalertsettings)</sup></sup>\n\nActive-active source - sync lag is higher than specified threshold value [seconds] -Note threshold is commented (allow string/int/float and support backwards compatibility) but is required\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Alert enabled or disabled<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specalertsettingsbdb_crdt_src_syncer_connection_error","title":"spec.alertSettings.bdb_crdt_src_syncer_connection_error","role":"content","text":"<sup><sup>[↩ Parent](#specalertsettings)</sup></sup>\n\nActive-active source - sync has connection error while trying to connect replica source -Note threshold is commented (allow string/int/float and support backwards compatibility) but is required\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Alert enabled or disabled<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specalertsettingsbdb_crdt_src_syncer_general_error","title":"spec.alertSettings.bdb_crdt_src_syncer_general_error","role":"content","text":"<sup><sup>[↩ Parent](#specalertsettings)</sup></sup>\n\nActive-active source - sync encountered in general error -Note threshold is commented (allow string/int/float and support backwards compatibility) but is required\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Alert enabled or disabled<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specalertsettingsbdb_high_latency","title":"spec.alertSettings.bdb_high_latency","role":"content","text":"<sup><sup>[↩ Parent](#specalertsettings)</sup></sup>\n\nLatency is higher than specified threshold value [micro-sec] -Note threshold is commented (allow string/int/float and support backwards compatibility) but is required\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Alert enabled or disabled<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specalertsettingsbdb_high_throughput","title":"spec.alertSettings.bdb_high_throughput","role":"content","text":"<sup><sup>[↩ Parent](#specalertsettings)</sup></sup>\n\nThroughput is higher than specified threshold value [requests / sec.] -Note threshold is commented (allow string/int/float and support backwards compatibility) but is required\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Alert enabled or disabled<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specalertsettingsbdb_long_running_action","title":"spec.alertSettings.bdb_long_running_action","role":"content","text":"<sup><sup>[↩ Parent](#specalertsettings)</sup></sup>\n\nAn alert for state-machines that are running for too long -Note threshold is commented (allow string/int/float and support backwards compatibility) but is required\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Alert enabled or disabled<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specalertsettingsbdb_low_throughput","title":"spec.alertSettings.bdb_low_throughput","role":"content","text":"<sup><sup>[↩ Parent](#specalertsettings)</sup></sup>\n\nThroughput is lower than specified threshold value [requests / sec.] -Note threshold is commented (allow string/int/float and support backwards compatibility) but is required\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Alert enabled or disabled<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specalertsettingsbdb_proxy_cert_expiring_soon","title":"spec.alertSettings.bdb_proxy_cert_expiring_soon","role":"content","text":"<sup><sup>[↩ Parent](#specalertsettings)</sup></sup>\n\nProxy certificate will expire in less than specified threshold value [days]\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Alert enabled or disabled<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specalertsettingsbdb_ram_dataset_overhead","title":"spec.alertSettings.bdb_ram_dataset_overhead","role":"content","text":"<sup><sup>[↩ Parent](#specalertsettings)</sup></sup>\n\nDataset RAM overhead of a shard has reached the threshold value [% of its RAM limit] -Note threshold is commented (allow string/int/float and support backwards compatibility) but is required\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Alert enabled or disabled<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specalertsettingsbdb_ram_values","title":"spec.alertSettings.bdb_ram_values","role":"content","text":"<sup><sup>[↩ Parent](#specalertsettings)</sup></sup>\n\nPercent of values kept in a shard's RAM is lower than [% of its key count] -Note threshold is commented (allow string/int/float and support backwards compatibility) but is required\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Alert enabled or disabled<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specalertsettingsbdb_replica_src_high_syncer_lag","title":"spec.alertSettings.bdb_replica_src_high_syncer_lag","role":"content","text":"<sup><sup>[↩ Parent](#specalertsettings)</sup></sup>\n\nReplica-of source - sync lag is higher than specified threshold value [seconds] -Note threshold is commented (allow string/int/float and support backwards compatibility) but is required\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Alert enabled or disabled<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specalertsettingsbdb_replica_src_syncer_connection_error","title":"spec.alertSettings.bdb_replica_src_syncer_connection_error","role":"content","text":"<sup><sup>[↩ Parent](#specalertsettings)</sup></sup>\n\nReplica-of source - sync has connection error while trying to connect replica source -Note threshold is commented (allow string/int/float and support backwards compatibility) but is required\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Alert enabled or disabled<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specalertsettingsbdb_shard_num_ram_values","title":"spec.alertSettings.bdb_shard_num_ram_values","role":"content","text":"<sup><sup>[↩ Parent](#specalertsettings)</sup></sup>\n\nNumber of values kept in a shard's RAM is lower than [values] -Note threshold is commented (allow string/int/float and support backwards compatibility) but is required\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Alert enabled or disabled<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specalertsettingsbdb_size","title":"spec.alertSettings.bdb_size","role":"content","text":"<sup><sup>[↩ Parent](#specalertsettings)</sup></sup>\n\nDataset size has reached the threshold value [% of the memory limit] expected fields: -Note threshold is commented (allow string/int/float and support backwards compatibility) but is required\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Alert enabled or disabled<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specauditing","title":"spec.auditing","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nDatabase auditing configuration.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>auditMode</td>\n        <td>enum</td>\n        <td>\n          Scope used for database auditing.\n\"disabled\" - auditing is disabled.\n\"connection\" - audit connection, authentication and disconnection events only.\n\"connection_and_crud\" - audit connection events and data operations (CRUD).\nThe cluster-level auditing configuration must be set before enabling auditing on a database.\nDefaults to \"disabled\" when not set; removing the auditing configuration disables auditing.<br/>\n          <br/>\n            <i>Enum</i>: disabled, connection, connection_and_crud<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>dbConnsAuditing</td>\n        <td>boolean</td>\n        <td>\n          Deprecated: use auditMode instead.\nEnables auditing of database connection and authentication events.\nWhen enabled, connection, authentication, and disconnection events are tracked and sent\nto the configured audit listener (configured at the cluster level).\nThe cluster-level auditing configuration must be set before enabling this on a database.\nSetting dbConnsAuditing to true is equivalent to auditMode \"connection\".\nIf both dbConnsAuditing and auditMode are set, auditMode takes precedence.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specauditingsourceipfilter\">sourceIpFilter</a></td>\n        <td>object</td>\n        <td>\n          Filters data operation auditing events by client source IP address.\nOnly relevant when auditMode is \"connection_and_crud\".\nNote: source IP filtering may not work as expected for Redis clients running outside\nof the Kubernetes cluster. The operator does not automatically customize the\nLoadBalancer service or ingress/route to preserve the client source IP, so connections\nmay be seen with a translated (NAT'd) address. If you choose to use this filter, it is\nyour responsibility to ensure source IP preservation is configured and to verify that\nauditing behaves as expected in your environment.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specauditingusernamefilter\">usernameFilter</a></td>\n        <td>object</td>\n        <td>\n          Filters data operation auditing events by client usernames.\nOnly relevant when auditMode is \"connection_and_crud\".<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specauditingsourceipfilter","title":"spec.auditing.sourceIpFilter","role":"content","text":"<sup><sup>[↩ Parent](#specauditing)</sup></sup>\n\nFilters data operation auditing events by client source IP address.\nOnly relevant when auditMode is \"connection_and_crud\".\nNote: source IP filtering may not work as expected for Redis clients running outside\nof the Kubernetes cluster. The operator does not automatically customize the\nLoadBalancer service or ingress/route to preserve the client source IP, so connections\nmay be seen with a translated (NAT'd) address. If you choose to use this filter, it is\nyour responsibility to ensure source IP preservation is configured and to verify that\nauditing behaves as expected in your environment.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>cidrRanges</td>\n        <td>[]string</td>\n        <td>\n          List of CIDR ranges (IPv4 and/or IPv6) to filter by.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Whether the source IP filter is active.\nWhen enabled, at least one entry must be provided across ipAddresses and cidrRanges.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>filterType</td>\n        <td>enum</td>\n        <td>\n          Filter type to use for the source IP filter.\n\"inclusive\" - only audit events from the listed addresses/ranges.\n\"exclusive\" - audit events from all addresses/ranges except the listed ones.\nThe combined number of ipAddresses and cidrRanges may not exceed 512.<br/>\n          <br/>\n            <i>Enum</i>: inclusive, exclusive<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>ipAddresses</td>\n        <td>[]string</td>\n        <td>\n          List of IP addresses (IPv4 and/or IPv6) to filter by.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specauditingusernamefilter","title":"spec.auditing.usernameFilter","role":"content","text":"<sup><sup>[↩ Parent](#specauditing)</sup></sup>\n\nFilters data operation auditing events by client usernames.\nOnly relevant when auditMode is \"connection_and_crud\".\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Whether the username filter is active.\nWhen enabled, usernames must contain at least one entry.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>filterType</td>\n        <td>enum</td>\n        <td>\n          Filter type to use for the username filter.\n\"inclusive\" - only audit events from the listed usernames.\n\"exclusive\" - audit events from all usernames except the listed ones.<br/>\n          <br/>\n            <i>Enum</i>: inclusive, exclusive<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>usernames</td>\n        <td>[]string</td>\n        <td>\n          List of usernames to filter by.\nEach username must be 1-255 printable ASCII characters and may not contain\nthe characters '&', '<', '>' or '\"'. At most 512 usernames are allowed.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specbackup","title":"spec.backup","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nTarget for automatic database backups.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td><a href=\"#specbackupabs\">abs</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specbackupftp\">ftp</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specbackupgcs\">gcs</a></td>\n        <td>object</td>\n        <td>\n          GoogleStorage<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>interval</td>\n        <td>integer</td>\n        <td>\n          Backup Interval in seconds. Specifies the time interval in seconds at which periodic backup is performed.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>intervalOffset</td>\n        <td>integer</td>\n        <td>\n          Backup Interval Offset in seconds. Specifies a time offset in seconds at which the periodic backup job starts. This can only be used if the backup interval is 24 hours (86400 seconds) or 12 hours (43200 seconds). The offset is relative to 00:00 UTC for 24-hour mode, and 00:00 + 12:00 UTC for 12-hour mode. If not specified, a random starting time (offset) is automatically chosen. The offset must be less than the backup interval.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specbackupmount\">mount</a></td>\n        <td>object</td>\n        <td>\n          MountPointStorage<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specbackups3\">s3</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specbackupsftp\">sftp</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specbackupswift\">swift</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specbackupabs","title":"spec.backup.abs","role":"content","text":"<sup><sup>[↩ Parent](#specbackup)</sup></sup>\n\n\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>absSecretName</td>\n        <td>string</td>\n        <td>\n          The name of the K8s secret that holds ABS credentials. The secret must contain the keys \"AccountName\" and \"AccountKey\", and these must hold the corresponding credentials<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>container</td>\n        <td>string</td>\n        <td>\n          Azure Blob Storage container name.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>subdir</td>\n        <td>string</td>\n        <td>\n          Optional. Azure Blob Storage subdir under container.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specbackupftp","title":"spec.backup.ftp","role":"content","text":"<sup><sup>[↩ Parent](#specbackup)</sup></sup>\n\n\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>url</td>\n        <td>string</td>\n        <td>\n          a URI of the ftps://[USER[:PASSWORD]@]HOST[:PORT]/PATH[/]<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specbackupgcs","title":"spec.backup.gcs","role":"content","text":"<sup><sup>[↩ Parent](#specbackup)</sup></sup>\n\nGoogleStorage\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>bucketName</td>\n        <td>string</td>\n        <td>\n          Google Storage bucket name.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>gcsSecretName</td>\n        <td>string</td>\n        <td>\n          The name of the K8s secret that holds the Google Cloud Storage credentials. The secret must contain the keys \"CLIENT_ID\", \"PRIVATE_KEY\", \"PRIVATE_KEY_ID\", \"CLIENT_EMAIL\" and these must hold the corresponding credentials. The keys should correspond to the values in the key JSON.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>subdir</td>\n        <td>string</td>\n        <td>\n          Optional. Google Storage subdir under bucket.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specbackupmount","title":"spec.backup.mount","role":"content","text":"<sup><sup>[↩ Parent](#specbackup)</sup></sup>\n\nMountPointStorage\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>path</td>\n        <td>string</td>\n        <td>\n          Path to the local mount point. You must create the mount point on all nodes, and the redislabs:redislabs user must have read and write permissions on the local mount point.<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specbackups3","title":"spec.backup.s3","role":"content","text":"<sup><sup>[↩ Parent](#specbackup)</sup></sup>\n\n\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>awsSecretName</td>\n        <td>string</td>\n        <td>\n          The name of the K8s secret that holds the AWS credentials. The secret must contain the keys \"AWS_ACCESS_KEY_ID\" and \"AWS_SECRET_ACCESS_KEY\", and these must hold the corresponding credentials.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>bucketName</td>\n        <td>string</td>\n        <td>\n          Amazon S3 bucket name.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>regionName</td>\n        <td>string</td>\n        <td>\n          Optional. Amazon S3 region name. If not specified, the region is auto-detected using a HEAD request to the bucket. For AWS GovCloud or other regions where auto-detection may not work, specify the region explicitly (e.g., \"us-gov-east-1\").<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>subdir</td>\n        <td>string</td>\n        <td>\n          Optional. Amazon S3 subdir under bucket.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specbackupsftp","title":"spec.backup.sftp","role":"content","text":"<sup><sup>[↩ Parent](#specbackup)</sup></sup>\n\n\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>sftpSecretName</td>\n        <td>string</td>\n        <td>\n          The name of the K8s secret that holds SFTP credentials. The secret must contain the \"Key\" key, which is the SSH private key for connecting to the sftp server.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>sftp_url</td>\n        <td>string</td>\n        <td>\n          SFTP url<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specbackupswift","title":"spec.backup.swift","role":"content","text":"<sup><sup>[↩ Parent](#specbackup)</sup></sup>\n\n\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>auth_url</td>\n        <td>string</td>\n        <td>\n          Swift service authentication URL.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>container</td>\n        <td>string</td>\n        <td>\n          Swift object store container for storing the backup files.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>swiftSecretName</td>\n        <td>string</td>\n        <td>\n          The name of the K8s secret that holds Swift credentials. The secret must contain the keys \"Key\" and \"User\", and these must hold the corresponding credentials: service access key and service user name (pattern for the latter does not allow special characters &,<,>,\")<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>prefix</td>\n        <td>string</td>\n        <td>\n          Optional. Prefix (path) of backup files in the swift container.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specconnectionsettings","title":"spec.connectionSettings","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nConnection-related settings such as proxy connections and scheduling policy.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>connectionLimitType</td>\n        <td>enum</td>\n        <td>\n          Connections limit type.<br/>\n          <br/>\n            <i>Enum</i>: per-thread, per-shard<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>connectionSchedulingPolicy</td>\n        <td>enum</td>\n        <td>\n          Controls how server-side connections are used when forwarding traffic to shards. Values: cmp: Closest to max_pipelined policy. Pick the connection with the most pipelined commands that has not reached the max_pipelined limit. mru: Try to use most recently used connections. spread: Try to use all connections. mnp: Minimal pipeline policy. Pick the connection with the least pipelined commands.<br/>\n          <br/>\n            <i>Enum</i>: cmp, mru, spread, mnp<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>globalMaxDedicatedConnections</td>\n        <td>integer</td>\n        <td>\n          Defines the maximum number of dedicated server connections for a given database. The total number across all workers. The default is 0 for unlimited. Defaults to 0, which means unlimited.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>internalConnections</td>\n        <td>integer</td>\n        <td>\n          The number of internal proxy connections. Defaults to 5 if unspecified.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>minDedicatedConnections</td>\n        <td>integer</td>\n        <td>\n          Number of dedicated server connections the DMC has per worker per shard. Defaults to 2 if unspecified.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specmoduleslist","title":"spec.modulesList[]","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nRedis Enterprise module (see https://redis.io/docs/latest/develop/reference/modules/)\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>name</td>\n        <td>string</td>\n        <td>\n          The name of the module, e.g. \"search\" or \"ReJSON\". The complete list of modules available in the cluster can be retrieved from the '.status.modules' field in the REC.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>config</td>\n        <td>string</td>\n        <td>\n          Module command line arguments e.g. VKEY_MAX_ENTITY_COUNT 30 30<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>version</td>\n        <td>string</td>\n        <td>\n          The semantic version of the module, e.g. '1.6.12'. Optional for REDB, must be set for REAADB. Note that this field is deprecated, and will be removed in future releases.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specossclustersettings","title":"spec.ossClusterSettings","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nAdditional OSS cluster mode settings.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enableExternalAccess</td>\n        <td>boolean</td>\n        <td>\n          Toggles whether this database supports external access in OSS cluster mode. When enabled, advertised database topology includes the external endpoints for the Redis Enterprise nodes hosting the database shards. The external access mechanism (e.g., LoadBalancer services) is configured via the ossClusterSettings.externalAccessType field of the RedisEnterpriseCluster. When external access is enabled, the corresponding database secret will have the list of primary shard IPs in the oss_startup_nodes field. Currently enabling OSS cluster API in conjunction with external access is not supported for active active databases.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specredisenterprisecluster","title":"spec.redisEnterpriseCluster","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nConnection to the Redis Enterprise Cluster.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>name</td>\n        <td>string</td>\n        <td>\n          The name of the Redis Enterprise Cluster where the database should be stored.<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specreplicasources","title":"spec.replicaSources[]","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\n\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>replicaSourceName</td>\n        <td>string</td>\n        <td>\n          The name of the resource from which the source database URI is derived. The type of resource must match the type specified in the ReplicaSourceType field.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>replicaSourceType</td>\n        <td>string</td>\n        <td>\n          The type of resource from which the source database URI is derived. If set to 'SECRET', the source database URI is derived from the secret named in the ReplicaSourceName field. The secret must have a key named 'uri' that defines the URI of the source database in the form of 'redis://...'. The type of secret (kubernetes, vault, ...) is determined by the secret mechanism used by the underlying REC object. If set to 'REDB', the source database URI is derived from the RedisEnterpriseDatabase resource named in the ReplicaSourceName field.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>clientKeySecret</td>\n        <td>string</td>\n        <td>\n          Secret that defines the client certificate and key used by the syncer in the target database cluster. The secret must the following keys in it's data: - A key named 'cert'/'certificate'/'tls.crt' which is the PEM encoded certificate - A key named 'key'/'tls.key' which is the PEM encoded private key. - Optionally, a key named 'ca.crt', containing the public certificate of the root CA. If present, the root CA certificate is appended to the certificate provided in the 'tls.crt' (or equivalent) key, to form a full certificate chain. Otherwise, the certificate in 'cert'/'certificate'/'tls.crt' must include a full certificate chain inline. This key is typically populated by the cert-manager when it has access to the root certificate. Otherwise, it could be added manually.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>compression</td>\n        <td>integer</td>\n        <td>\n          GZIP compression level (0-6) to use for replication.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>serverCertSecret</td>\n        <td>string</td>\n        <td>\n          Secret that defines the server certificate used by the proxy in the source database cluster. The secret must have 1 key in its map named 'cert'/'certificate'/'tls.crt' which is the PEM encoded certificate.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>tlsSniName</td>\n        <td>string</td>\n        <td>\n          TLS SNI name to use for the replication link.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specrolespermissions","title":"spec.rolesPermissions[]","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nRedis Enterprise Role and ACL Binding\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>acl</td>\n        <td>string</td>\n        <td>\n          Acl Name of RolePermissionType<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>role</td>\n        <td>string</td>\n        <td>\n          Role Name of RolePermissionType<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>type</td>\n        <td>enum</td>\n        <td>\n          Type of Redis Enterprise Database Role Permission. Currently, only \"redis-enterprise\" is supported, which uses roles and ACLs defined within Redis Enterprise directly.<br/>\n          <br/>\n            <i>Enum</i>: redis-enterprise<br/>\n            <i>Default</i>: redis-enterprise<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specupgradespec","title":"spec.upgradeSpec","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nSpecifications for DB upgrade.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>upgradeModulesToLatest</td>\n        <td>boolean</td>\n        <td>\n          DEPRECATED Upgrades the modules to the latest version that supports the DB version during a DB upgrade action, to upgrade the DB version view the 'redisVersion' field. Notes - All modules must be without specifying the version. in addition, This field is currently not supported for Active-Active databases. The default is true<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"status","title":"status","role":"content","text":"<sup><sup>[↩ Parent](#)</sup></sup>\n\nRedisEnterpriseDatabaseStatus defines the observed state of RedisEnterpriseDatabase\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td><a href=\"#statusactiveactive\">activeActive</a></td>\n        <td>object</td>\n        <td>\n          Connection/ association to the Active-Active database.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#statusbackupinfo\">backupInfo</a></td>\n        <td>object</td>\n        <td>\n          Information on the database's periodic backup<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>bigstoreVersion</td>\n        <td>integer</td>\n        <td>\n          BigStore version for Redis on Flash databases (1 for Auto Tiering, 2 for Redis Flex). Read-only field populated from RS.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>createdTime</td>\n        <td>string</td>\n        <td>\n          Time when the database was created<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>databaseUID</td>\n        <td>string</td>\n        <td>\n          Database UID provided by redis enterprise<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#statusinternalendpoints\">internalEndpoints</a></td>\n        <td>[]object</td>\n        <td>\n          Endpoints listed internally by the Redis Enterprise Cluster. Can be used to correlate a ReplicaSourceStatus entry.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>lastActionStatus</td>\n        <td>string</td>\n        <td>\n          Status of the last action done by operator on this database<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>lastActionUid</td>\n        <td>string</td>\n        <td>\n          UID of the last action done by operator on this database<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>lastUpdated</td>\n        <td>string</td>\n        <td>\n          Time when the database was last updated<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>managedFields</td>\n        <td>[]string</td>\n        <td>\n          List of optional spec fields the operator has taken ownership of after the user configured them at least once, so removing such a field resets the BDB to its default instead of leaving the inherited value in place.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>managedTags</td>\n        <td>[]string</td>\n        <td>\n          List of user-declared tag keys (from spec.tags) that were last successfully reconciled to the BDB. Used to detect tag deletions on the next reconcile: keys present here but absent from the current spec.tags are removed from the BDB. Operator-internal keys (managed_by, redb_name, etc.) are not tracked here.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>observedGeneration</td>\n        <td>integer</td>\n        <td>\n          The generation (built in update counter of K8s) of the REDB resource that was fully acted upon, meaning that all changes were handled and sent as an API call to the Redis Enterprise Cluster (REC). This field value should equal the current generation when the resource changes were handled. Note: the lastActionStatus field tracks actions handled asynchronously by the Redis Enterprise Cluster.<br/>\n          <br/>\n            <i>Format</i>: int64<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>redisEnterpriseCluster</td>\n        <td>string</td>\n        <td>\n          The Redis Enterprise Cluster Object this Resource is associated with<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#statusreplicasourcestatuses\">replicaSourceStatuses</a></td>\n        <td>[]object</td>\n        <td>\n          ReplicaSource statuses<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#statusrolespermissions\">rolesPermissions</a></td>\n        <td>[]object</td>\n        <td>\n          The set of role-permission tuples associated with this database.\nPopulated from spec.rolesPermissions (deprecated), RedisEnterpriseRole objects whose\nscopes reference this database, and RedisEnterpriseClusterRole objects (whose ACLs\napply to every database in the Redis Enterprise Cluster).<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>shardStatuses</td>\n        <td>map[string]integer</td>\n        <td>\n          Aggregated statuses of shards<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>specStatus</td>\n        <td>string</td>\n        <td>\n          Whether the desired specification is valid<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>status</td>\n        <td>string</td>\n        <td>\n          The status of the database<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>version</td>\n        <td>string</td>\n        <td>\n          Database compatibility version<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"statusactiveactive","title":"status.activeActive","role":"content","text":"<sup><sup>[↩ Parent](#status)</sup></sup>\n\nConnection/ association to the Active-Active database.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>name</td>\n        <td>string</td>\n        <td>\n          The the corresponding Active-Active database name, Redis Enterprise Active Active Database custom resource name, this Resource is associated with. In case this resource is created manually at the active active database creation this field must be filled via the user, otherwise, the operator will assign this field automatically. Note: this feature is currently unsupported.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>participatingClusterName</td>\n        <td>string</td>\n        <td>\n          The corresponding participating cluster name, Redis Enterprise Remote Cluster custom resource name, in the Active-Active database, In case this resource is created manually at the active active database creation this field must be filled via the user, otherwise, the operator will assign this field automatically. Note: this feature is currently unsupported.<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"statusbackupinfo","title":"status.backupInfo","role":"content","text":"<sup><sup>[↩ Parent](#status)</sup></sup>\n\nInformation on the database's periodic backup\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>backupFailureReason</td>\n        <td>string</td>\n        <td>\n          Reason of last failed backup process<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>backupHistory</td>\n        <td>integer</td>\n        <td>\n          Backup history retention policy (number of days, 0 is forever)<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>backupInterval</td>\n        <td>integer</td>\n        <td>\n          Interval in seconds in which automatic backup will be initiated<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>backupIntervalOffset</td>\n        <td>integer</td>\n        <td>\n          Offset (in seconds) from round backup interval when automatic backup will be initiated (should be less than backup_interval)<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>backupProgressPercentage</td>\n        <td>integer</td>\n        <td>\n          Database scheduled periodic backup progress (percentage)<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>backupStatus</td>\n        <td>string</td>\n        <td>\n          Status of scheduled periodic backup process<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>lastBackupTime</td>\n        <td>string</td>\n        <td>\n          Time of last successful backup<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"statusinternalendpoints","title":"status.internalEndpoints[]","role":"content","text":"<sup><sup>[↩ Parent](#status)</sup></sup>\n\n\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>host</td>\n        <td>string</td>\n        <td>\n          Hostname assigned to the database<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>port</td>\n        <td>integer</td>\n        <td>\n          Database port name<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"statusreplicasourcestatuses","title":"status.replicaSourceStatuses[]","role":"content","text":"<sup><sup>[↩ Parent](#status)</sup></sup>\n\n\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>endpointHost</td>\n        <td>string</td>\n        <td>\n          The internal host name of the replica source database. Can be used as an identifier. See the internalEndpoints list on the REDB status.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>lag</td>\n        <td>integer</td>\n        <td>\n          Lag in millisec between source and destination (while synced).<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>lastError</td>\n        <td>string</td>\n        <td>\n          Last error encountered when syncing from the source.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>lastUpdate</td>\n        <td>string</td>\n        <td>\n          Time when we last receive an update from the source.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>rdbSize</td>\n        <td>integer</td>\n        <td>\n          The source’s RDB size to be transferred during the syncing phase.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>rdbTransferred</td>\n        <td>integer</td>\n        <td>\n          Number of bytes transferred from the source’s RDB during the syncing phase.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>status</td>\n        <td>string</td>\n        <td>\n          Sync status of this source<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"statusrolespermissions","title":"status.rolesPermissions[]","role":"content","text":"<sup><sup>[↩ Parent](#status)</sup></sup>\n\nRolePermissionStatus is an observed role-ACL binding on the database.\nRole kind: RedisEnterpriseRole, RedisEnterpriseClusterRole, or role; ACL kind:\nRedisEnterpriseACL or redis_acl.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td><a href=\"#statusrolespermissionsacl\">acl</a></td>\n        <td>object</td>\n        <td>\n          Reference to the ACL.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td><a href=\"#statusrolespermissionsrole\">role</a></td>\n        <td>object</td>\n        <td>\n          Reference to the role.<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"statusrolespermissionsacl","title":"status.rolesPermissions[].acl","role":"content","text":"<sup><sup>[↩ Parent](#statusrolespermissions)</sup></sup>\n\nReference to the ACL.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>kind</td>\n        <td>string</td>\n        <td>\n          Kind of the referent.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>name</td>\n        <td>string</td>\n        <td>\n          Name of the referent.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"statusrolespermissionsrole","title":"status.rolesPermissions[].role","role":"content","text":"<sup><sup>[↩ Parent](#statusrolespermissions)</sup></sup>\n\nReference to the role.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>kind</td>\n        <td>string</td>\n        <td>\n          Kind of the referent.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>name</td>\n        <td>string</td>\n        <td>\n          Name of the referent.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/reference/api/redis_enterprise_remote_cluster_api","title":"RedisEnterpriseRemoteCluster API Reference","url":"https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_remote_cluster_api/","summary":"","tags":["docs","operate","kubernetes"],"last_updated":"2026-04-24T15:18:45-05:00","page_type":"content","content_hash":"5804ed513665780877a8f7da671fcae3c69d8ad61f87e842d9915ff57b2ca5de","sections":[{"id":"overview","title":"Overview","role":"overview","text":"apiVersion:\n\n\n- [app.redislabs.com/v1alpha1](#appredislabscomv1alpha1)\n\n\n\n\n# app.redislabs.com/v1alpha1\n\n\n\n\nRepresents a remote participating cluster.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n      <td>apiVersion</td>\n      <td>string</td>\n      <td>app.redislabs.com/v1alpha1</td>\n      <td>true</td>\n      </tr>\n      <tr>\n      <td>kind</td>\n      <td>string</td>\n      <td>RedisEnterpriseRemoteCluster</td>\n      <td>true</td>\n      </tr>\n      <tr>\n      <td><a href=\"https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#objectmeta-v1-meta\">metadata</a></td>\n      <td>object</td>\n      <td>Refer to the Kubernetes API documentation for the fields of the `metadata` field.</td>\n      <td>true</td>\n      </tr><tr>\n        <td><a href=\"#spec\">spec</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#status\">status</a></td>\n        <td>object</td>\n        <td>\n          <br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"spec","title":"spec","role":"content","text":"<sup><sup>[↩ Parent](#)</sup></sup>\n\n\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>apiFqdnUrl</td>\n        <td>string</td>\n        <td>\n          URL of the cluster. Used for the Active-Active database URL.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>recName</td>\n        <td>string</td>\n        <td>\n          Name of the REC that this RERC points to.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>recNamespace</td>\n        <td>string</td>\n        <td>\n          Namespace of the REC that this RERC points to.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>apiPort</td>\n        <td>integer</td>\n        <td>\n          Port number of the cluster's URL. Used for connectivity and synchronization.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>dbFqdnSuffix</td>\n        <td>string</td>\n        <td>\n          Database URL suffix. Used for the Active-Active database replication endpoint and replication endpoint SNI.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>secretName</td>\n        <td>string</td>\n        <td>\n          Name of the secret containing cluster credentials. Must use the following format: \"redis-enterprise-<RERC name>\".<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"status","title":"status","role":"content","text":"<sup><sup>[↩ Parent](#)</sup></sup>\n\n\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>internalObservedSecretResourceVersion</td>\n        <td>string</td>\n        <td>\n          Observed secret resource version. For internal use only.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>local</td>\n        <td>boolean</td>\n        <td>\n          Indicates whether this object represents a local or remote cluster.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>observedGeneration</td>\n        <td>integer</td>\n        <td>\n          Most recent generation observed for this RERC. Corresponds to the RERC's generation, which is updated by the API Server.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>specStatus</td>\n        <td>string</td>\n        <td>\n          Indicates whether the desired specification is valid.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>status</td>\n        <td>string</td>\n        <td>\n          Status of the remote cluster.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/reference/api/redis_enterprise_role_api","title":"RedisEnterpriseRole API Reference","url":"https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_role_api/","summary":"","tags":["docs","operate","kubernetes"],"last_updated":"2026-07-21T12:02:16-05:00","page_type":"content","content_hash":"1334a687c5292aeae4e5dcba8100137a3c975ea136f354d4cb66113048d294f7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"apiVersion:\n\n\n- [app.redislabs.com/v1alpha1](#appredislabscomv1alpha1)\n\n\n\n\n# app.redislabs.com/v1alpha1\n\n\n\n\nRedisEnterpriseRole is a scoped role definition. It grants users access to selected databases within a Redis Enterprise Cluster.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n      <td>apiVersion</td>\n      <td>string</td>\n      <td>app.redislabs.com/v1alpha1</td>\n      <td>true</td>\n      </tr>\n      <tr>\n      <td>kind</td>\n      <td>string</td>\n      <td>RedisEnterpriseRole</td>\n      <td>true</td>\n      </tr>\n      <tr>\n      <td><a href=\"https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#objectmeta-v1-meta\">metadata</a></td>\n      <td>object</td>\n      <td>Refer to the Kubernetes API documentation for the fields of the `metadata` field.</td>\n      <td>true</td>\n      </tr><tr>\n        <td><a href=\"#spec\">spec</a></td>\n        <td>object</td>\n        <td>\n          RedisEnterpriseRoleSpec defines the desired state of a RedisEnterpriseRole.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#status\">status</a></td>\n        <td>object</td>\n        <td>\n          RedisEnterpriseRoleStatus defines the observed state of a Redis Enterprise role object. This status type is used by both RedisEnterpriseClusterRole and RedisEnterpriseRole which share the same status structure.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"spec","title":"spec","role":"content","text":"<sup><sup>[↩ Parent](#)</sup></sup>\n\nRedisEnterpriseRoleSpec defines the desired state of a RedisEnterpriseRole.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td><a href=\"#specscopes\">scopes</a></td>\n        <td>[]object</td>\n        <td>\n          Scopes define the databases to which this role applies.\nEach scope references a RedisEnterpriseDatabase by name or selector (kind\nRedisEnterpriseDatabase or empty), or an internal Redis Enterprise database\nby name (kind 'bdb', name only).\nFor 'bdb' scopes the role grants management access only; its ACL is not bound\nto that database's permissions.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td><a href=\"#specacl\">acl</a></td>\n        <td>object</td>\n        <td>\n          ACL defines the data access permissions granted by this scoped role.\nReferences a RedisEnterpriseACL (kind RedisEnterpriseACL or empty) or an\ninternal Redis Enterprise ACL by name (kind 'redis_acl').<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>managementRole</td>\n        <td>enum</td>\n        <td>\n          ManagementRole is the named set of Redis Enterprise management permissions assigned to this scoped role. RedisEnterpriseRole supports only database-scoped management roles. If omitted, the role uses the None management role.<br/>\n          <br/>\n            <i>Enum</i>: DBMember, DBViewer, None<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specscopes","title":"spec.scopes[]","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nA reference to another object(s), either directly by name, or via a label selector.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>kind</td>\n        <td>enum</td>\n        <td>\n          Kind of the referent.<br/>\n          <br/>\n            <i>Enum</i>: , RedisEnterpriseDatabase, bdb<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>name</td>\n        <td>string</td>\n        <td>\n          Name of the referent.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specscopesselector\">selector</a></td>\n        <td>object</td>\n        <td>\n          Selector based on label attached to the object.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specscopesselector","title":"spec.scopes[].selector","role":"content","text":"<sup><sup>[↩ Parent](#specscopes)</sup></sup>\n\nSelector based on label attached to the object.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td><a href=\"#specscopesselectormatchexpressions\">matchExpressions</a></td>\n        <td>[]object</td>\n        <td>\n          matchExpressions is a list of label selector requirements. The requirements are ANDed.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>matchLabels</td>\n        <td>map[string]string</td>\n        <td>\n          matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specscopesselectormatchexpressions","title":"spec.scopes[].selector.matchExpressions[]","role":"content","text":"<sup><sup>[↩ Parent](#specscopesselector)</sup></sup>\n\nA label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>key</td>\n        <td>string</td>\n        <td>\n          key is the label key that the selector applies to.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>operator</td>\n        <td>string</td>\n        <td>\n          operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>values</td>\n        <td>[]string</td>\n        <td>\n          values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specacl","title":"spec.acl","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nACL defines the data access permissions granted by this scoped role.\nReferences a RedisEnterpriseACL (kind RedisEnterpriseACL or empty) or an\ninternal Redis Enterprise ACL by name (kind 'redis_acl').\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>name</td>\n        <td>string</td>\n        <td>\n          Name of the referent.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>kind</td>\n        <td>enum</td>\n        <td>\n          Kind of the referent.<br/>\n          <br/>\n            <i>Enum</i>: , RedisEnterpriseACL, redis_acl<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"status","title":"status","role":"content","text":"<sup><sup>[↩ Parent](#)</sup></sup>\n\nRedisEnterpriseRoleStatus defines the observed state of a Redis Enterprise role object. This status type is used by both RedisEnterpriseClusterRole and RedisEnterpriseRole which share the same status structure.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>observedGeneration</td>\n        <td>integer</td>\n        <td>\n          The most recent generation of this resource that was observed and acted upon by the controller.<br/>\n          <br/>\n            <i>Format</i>: int64<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>uid</td>\n        <td>string</td>\n        <td>\n          The internal UID of the Role object defined in the Redis Enterprise Cluster.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/reference/api/redis_enterprise_role_binding_api","title":"RedisEnterpriseRoleBinding API Reference","url":"https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_role_binding_api/","summary":"","tags":["docs","operate","kubernetes"],"last_updated":"2026-07-21T12:02:16-05:00","page_type":"content","content_hash":"fd6adc1f561414bd9592b44616c3670847a704ec69ba2f0039c97839d67f2c92","sections":[{"id":"overview","title":"Overview","role":"overview","text":"apiVersion:\n\n\n- [app.redislabs.com/v1alpha1](#appredislabscomv1alpha1)\n\n\n\n\n# app.redislabs.com/v1alpha1\n\n\n\n\nRedisEnterpriseRoleBinding binds users to a RedisEnterpriseRole. It grants the users the scoped access defined by that role.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n      <td>apiVersion</td>\n      <td>string</td>\n      <td>app.redislabs.com/v1alpha1</td>\n      <td>true</td>\n      </tr>\n      <tr>\n      <td>kind</td>\n      <td>string</td>\n      <td>RedisEnterpriseRoleBinding</td>\n      <td>true</td>\n      </tr>\n      <tr>\n      <td><a href=\"https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#objectmeta-v1-meta\">metadata</a></td>\n      <td>object</td>\n      <td>Refer to the Kubernetes API documentation for the fields of the `metadata` field.</td>\n      <td>true</td>\n      </tr><tr>\n        <td><a href=\"#spec\">spec</a></td>\n        <td>object</td>\n        <td>\n          RedisEnterpriseRoleBindingSpec defines the desired state of a RedisEnterpriseClusterRoleBinding or RedisEnterpriseRoleBinding.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>status</td>\n        <td>object</td>\n        <td>\n          RedisEnterpriseRoleBindingStatus defines the observed state of a RedisEnterpriseClusterRoleBinding or RedisEnterpriseRoleBinding.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"spec","title":"spec","role":"content","text":"<sup><sup>[↩ Parent](#)</sup></sup>\n\nRedisEnterpriseRoleBindingSpec defines the desired state of a RedisEnterpriseClusterRoleBinding or RedisEnterpriseRoleBinding.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td><a href=\"#specroleref\">roleRef</a></td>\n        <td>object</td>\n        <td>\n          RoleRef holds a reference to the role object being bound. For RedisEnterpriseClusterRoleBinding, this references a RedisEnterpriseClusterRole in the same namespace (kind RedisEnterpriseClusterRole or empty) or an internal Redis Enterprise role by name (kind 'role'). For RedisEnterpriseRoleBinding, this references a RedisEnterpriseRole in the same namespace (kind RedisEnterpriseRole or empty) or an internal Redis Enterprise role by name (kind 'role').<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td><a href=\"#specsubjects\">subjects</a></td>\n        <td>[]object</td>\n        <td>\n          Subjects holds references to the objects the role applies to. Each subject is a reference to a RedisEnterpriseUser object within the same namespace. The 'kind' field can be left empty, or set to RedisEnterpriseUser. A maximum of 100 subjects may be specified; to bind more, create additional bindings referencing the same role.<br/>\n          <br/>\n            <i>Validations</i>:<li>self.all(s1, self.exists_one(s2,\n  (has(s1.kind) && s1.kind != '' ? s1.kind : 'RedisEnterpriseUser') ==\n  (has(s2.kind) && s2.kind != '' ? s2.kind : 'RedisEnterpriseUser') &&\n  s1.name == s2.name))\n: subjects must be unique by kind and name</li>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specroleref","title":"spec.roleRef","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nRoleRef holds a reference to the role object being bound. For RedisEnterpriseClusterRoleBinding, this references a RedisEnterpriseClusterRole in the same namespace (kind RedisEnterpriseClusterRole or empty) or an internal Redis Enterprise role by name (kind 'role'). For RedisEnterpriseRoleBinding, this references a RedisEnterpriseRole in the same namespace (kind RedisEnterpriseRole or empty) or an internal Redis Enterprise role by name (kind 'role').\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>name</td>\n        <td>string</td>\n        <td>\n          Name of the referent.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>kind</td>\n        <td>enum</td>\n        <td>\n          Kind of the referent.<br/>\n          <br/>\n            <i>Enum</i>: , RedisEnterpriseRole, role<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specsubjects","title":"spec.subjects[]","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nA reference to another object.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>name</td>\n        <td>string</td>\n        <td>\n          Name of the referent.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>kind</td>\n        <td>enum</td>\n        <td>\n          Kind of the referent.<br/>\n          <br/>\n            <i>Enum</i>: , RedisEnterpriseUser<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/reference/api/redis_enterprise_user_api","title":"RedisEnterpriseUser API Reference","url":"https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_user_api/","summary":"","tags":["docs","operate","kubernetes"],"last_updated":"2026-07-21T12:02:16-05:00","page_type":"content","content_hash":"1bb2b3cd4fac5e0a40b10231966a2046a9425ecd1b12c5cca72daeca4df4f6fc","sections":[{"id":"overview","title":"Overview","role":"overview","text":"apiVersion:\n\n\n- [app.redislabs.com/v1alpha1](#appredislabscomv1alpha1)\n\n\n\n\n# app.redislabs.com/v1alpha1\n\n\n\n\nRedisEnterpriseUser represents a user definition within a Redis Enterprise Cluster.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n      <td>apiVersion</td>\n      <td>string</td>\n      <td>app.redislabs.com/v1alpha1</td>\n      <td>true</td>\n      </tr>\n      <tr>\n      <td>kind</td>\n      <td>string</td>\n      <td>RedisEnterpriseUser</td>\n      <td>true</td>\n      </tr>\n      <tr>\n      <td><a href=\"https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#objectmeta-v1-meta\">metadata</a></td>\n      <td>object</td>\n      <td>Refer to the Kubernetes API documentation for the fields of the `metadata` field.</td>\n      <td>true</td>\n      </tr><tr>\n        <td><a href=\"#spec\">spec</a></td>\n        <td>object</td>\n        <td>\n          RedisEnterpriseUserSpec defines the desired state of a RedisEnterpriseUser.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#status\">status</a></td>\n        <td>object</td>\n        <td>\n          RedisEnterpriseUserStatus defines the observed state of a RedisEnterpriseUser.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"spec","title":"spec","role":"content","text":"<sup><sup>[↩ Parent](#)</sup></sup>\n\nRedisEnterpriseUserSpec defines the desired state of a RedisEnterpriseUser.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td><a href=\"#specalerts\">alerts</a></td>\n        <td>object</td>\n        <td>\n          Email alerts settings for this user. User alerts settings are effective only when cluster alerts settings are configured.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>authMethod</td>\n        <td>enum</td>\n        <td>\n          AuthMethod selects how the user authenticates to Redis Enterprise. 'Basic' (the default) authenticates with Basic Auth, using a password sourced from passwordSecrets. 'Certificate' authenticates with a client certificate whose subject matches certificateSubjectLine; such users carry no password (passwordSecrets must be empty). Certificate users additionally require certificate-based authentication to be configured at the cluster level.<br/>\n          <br/>\n            <i>Enum</i>: Basic, Certificate<br/>\n            <i>Default</i>: Basic<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>certificateSubjectLine</td>\n        <td>string</td>\n        <td>\n          CertificateSubjectLine is the certificate subject line (RFC2253) used to match the client certificate for certificate-auth users. Required when authMethod is 'Certificate'; must not be set otherwise.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>email</td>\n        <td>string</td>\n        <td>\n          The email address for the user. Optional (Redis Enterprise accepts an empty email). For password users, changing the email after creation is permitted only while spec.passwordSecrets contains exactly one entry: to change the email of a user with two password secrets (Rotatable rotation in progress), first reduce passwordSecrets to a single entry, change the email, then add the second secret back. Certificate-auth users carry no password, so this restriction does not apply to them.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>passwordMode</td>\n        <td>enum</td>\n        <td>\n          Defines how passwords are managed for this user. Single: Only a single password secret can be specified at a time. Passwords changes can be done by mutating the secret in-place, or by replacing the secret reference to point to another secret. Rotatable: Allows for one or two password secrets to be specified simultaneously. The secrets are required to be immutable, and the operator automatically sets their 'immutable: true' attribute (K8s secrets only) if not already set. Vault secrets are assumed to be used as immutable and are not made immutable automatically. Password changes are typically done by adding a new password secret, and later removing the old one when it's no longer needed. When empty, Single behavior is used. Must not be set for certificate-auth users (authMethod: Certificate), which carry no password.<br/>\n          <br/>\n            <i>Enum</i>: Single, Rotatable<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specpasswordsecrets\">passwordSecrets</a></td>\n        <td>[]object</td>\n        <td>\n          List of one or more secrets holding the user's password. Each secret must have a key named 'password' with the user's desired password. If multiple secrets are specified, then the passwords from all of them can be used for authenticating as the user. When the user signin status is 'Locked', the user won't be able to authenticate with Redis Enterprise API or UI, and must be unlocked by another user with Admin-role access. To unlock the user, first set a new password via the 'passwordSecrets' field, and then follow the password reset instructions in the following link: https://redis.io/docs/latest/operate/rs/security/access-control/manage-users/login-lockout/#unlock-locked-user-accounts<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>username</td>\n        <td>string</td>\n        <td>\n          The username associated with the user. The username must be unique within the Redis Enterprise cluster, and include only ASCII characters except &,<,>,\". If not specified, a default username is assigned and can be discovered via the status section of the RedisEnterpriseUser.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specalerts","title":"spec.alerts","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nEmail alerts settings for this user. User alerts settings are effective only when cluster alerts settings are configured.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Master toggle for email alerts.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td><a href=\"#specalertsclusteralerts\">clusterAlerts</a></td>\n        <td>object</td>\n        <td>\n          UserClusterAlertSettings specifies cluster alerts settings for a user.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#specalertsdatabasealerts\">databaseAlerts</a></td>\n        <td>object</td>\n        <td>\n          UserDatabaseAlertSettings specifies database alerts settings for a user.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specalertsclusteralerts","title":"spec.alerts.clusterAlerts","role":"content","text":"<sup><sup>[↩ Parent](#specalerts)</sup></sup>\n\nUserClusterAlertSettings specifies cluster alerts settings for a user.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>enabled</td>\n        <td>boolean</td>\n        <td>\n          Determines whether cluster alerts are enabled for the user.<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"specalertsdatabasealerts","title":"spec.alerts.databaseAlerts","role":"content","text":"<sup><sup>[↩ Parent](#specalerts)</sup></sup>\n\nUserDatabaseAlertSettings specifies database alerts settings for a user.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td><a href=\"#specalertsdatabasealertsdatabases\">databases</a></td>\n        <td>[]object</td>\n        <td>\n          The databases for which alerts are enabled for this user. If not specified, defaults to all databases.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specalertsdatabasealertsdatabases","title":"spec.alerts.databaseAlerts.databases[]","role":"content","text":"<sup><sup>[↩ Parent](#specalertsdatabasealerts)</sup></sup>\n\nA reference to another object.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>kind</td>\n        <td>string</td>\n        <td>\n          Kind of the referent.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>name</td>\n        <td>string</td>\n        <td>\n          Name of the referent.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"specpasswordsecrets","title":"spec.passwordSecrets[]","role":"content","text":"<sup><sup>[↩ Parent](#spec)</sup></sup>\n\nA reference to a Secret object.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>name</td>\n        <td>string</td>\n        <td>\n          Name of the Secret.<br/>\n        </td>\n        <td>true</td>\n      </tr></tbody>\n</table>"},{"id":"status","title":"status","role":"content","text":"<sup><sup>[↩ Parent](#)</sup></sup>\n\nRedisEnterpriseUserStatus defines the observed state of a RedisEnterpriseUser.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td><a href=\"#statusconditions\">conditions</a></td>\n        <td>[]object</td>\n        <td>\n          Conditions represent the latest available observations of the user's state.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>observedGeneration</td>\n        <td>integer</td>\n        <td>\n          The most recent generation of this resource that was observed and acted upon by the controller.<br/>\n          <br/>\n            <i>Format</i>: int64<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>passwordIssueDate</td>\n        <td>string</td>\n        <td>\n          The date in which the password was set.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#statuspasswordsecrets\">passwordSecrets</a></td>\n        <td>[]object</td>\n        <td>\n          List of secrets holding the user's password, with their versions.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td><a href=\"#statusroles\">roles</a></td>\n        <td>[]object</td>\n        <td>\n          List of roles assigned to the user.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>rolesDisplay</td>\n        <td>string</td>\n        <td>\n          Formatted string representation of the roles assigned to the user.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>signinStatus</td>\n        <td>string</td>\n        <td>\n          Sign-in status for the user. When set to 'Locked', the user won't be able to authenticate with Redis Enterprise API or UI, and must be unlocked by another user with Admin-role access. To unlock the user, set a new password via the 'passwordSecrets' field, and then follow the password reset instructions in the following link: https://redis.io/docs/latest/operate/rs/security/access-control/manage-users/login-lockout/#unlock-locked-user-accounts<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>uid</td>\n        <td>string</td>\n        <td>\n          The internal UID of the User object defined in the Redis Enterprise Cluster.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>username</td>\n        <td>string</td>\n        <td>\n          The effective username assigned for the user.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"statusconditions","title":"status.conditions[]","role":"content","text":"<sup><sup>[↩ Parent](#status)</sup></sup>\n\nCondition contains details for one aspect of the current state of this API Resource.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>lastTransitionTime</td>\n        <td>string</td>\n        <td>\n          lastTransitionTime is the last time the condition transitioned from one status to another.<br/>\n          <br/>\n            <i>Format</i>: date-time<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>status</td>\n        <td>enum</td>\n        <td>\n          status of the condition, one of True, False, Unknown.<br/>\n          <br/>\n            <i>Enum</i>: True, False, Unknown<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>type</td>\n        <td>string</td>\n        <td>\n          type of condition in CamelCase or in foo.example.com/CamelCase.<br/>\n        </td>\n        <td>true</td>\n      </tr><tr>\n        <td>message</td>\n        <td>string</td>\n        <td>\n          message is a human readable message indicating details about the transition.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>observedGeneration</td>\n        <td>integer</td>\n        <td>\n          observedGeneration represents the .metadata.generation that the condition was set based upon.<br/>\n          <br/>\n            <i>Format</i>: int64<br/>\n            <i>Minimum</i>: 0<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>reason</td>\n        <td>string</td>\n        <td>\n          reason contains a programmatic identifier indicating the reason for the condition's last transition.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"statuspasswordsecrets","title":"status.passwordSecrets[]","role":"content","text":"<sup><sup>[↩ Parent](#status)</sup></sup>\n\nA versioned reference to a Secret object.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>name</td>\n        <td>string</td>\n        <td>\n          Name of the Secret.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>version</td>\n        <td>string</td>\n        <td>\n          Version of the Secret.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"},{"id":"statusroles","title":"status.roles[]","role":"content","text":"<sup><sup>[↩ Parent](#status)</sup></sup>\n\nA reference to another object.\n\n<table>\n    <thead>\n        <tr>\n            <th>Name</th>\n            <th>Type</th>\n            <th>Description</th>\n            <th>Required</th>\n        </tr>\n    </thead>\n    <tbody><tr>\n        <td>kind</td>\n        <td>string</td>\n        <td>\n          Kind of the referent.<br/>\n        </td>\n        <td>false</td>\n      </tr><tr>\n        <td>name</td>\n        <td>string</td>\n        <td>\n          Name of the referent.<br/>\n        </td>\n        <td>false</td>\n      </tr></tbody>\n</table>"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/reference","title":"Reference","url":"https://redis.io/docs/latest/operate/kubernetes/reference/","summary":"Resources to help you manage Redis Enterprise custom resources on Kubernetes.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","children":[{"id":"operate/kubernetes/reference/supported_k8s_distributions","summary":"Support matrix for the current Redis Software for Kubernetes operator","title":"Supported Kubernetes distributions","url":"https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions/"},{"id":"operate/kubernetes/reference/api","summary":"Reference documentation for Redis Enterprise operator APIs","title":"API reference","url":"https://redis.io/docs/latest/operate/kubernetes/reference/api/"},{"id":"operate/kubernetes/reference/yaml","summary":"Example YAML files for deploying Redis Enterprise on Kubernetes with different configurations.","title":"YAML examples","url":"https://redis.io/docs/latest/operate/kubernetes/reference/yaml/"}],"page_type":"content","content_hash":"cac7b07d18dcd35a55cf2ff14073a8748d84a411213f7d10eb4db14a18b43044","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This reference documentation covers Redis Enterprise custom resources, API specifications, and practical instructions for creating, configuring, and managing Redis Enterprise deployments on Kubernetes."},{"id":"work-with-custom-resources","title":"Work with custom resources","role":"content","text":"Redis Enterprise for Kubernetes uses custom resources to manage clusters and databases. Use standard Kubernetes tools to create, modify, and delete these resources."},{"id":"create-custom-resources","title":"Create custom resources","role":"content","text":"Use `kubectl apply` with YAML manifests to create custom resources:\n\n[code example]"},{"id":"view-custom-resources","title":"View custom resources","role":"content","text":"Use these commands to list and inspect existing custom resources:\n\n[code example]"},{"id":"modify-custom-resources","title":"Modify custom resources","role":"content","text":"Edit the YAML manifest and reapply to update custom resources:\n\n[code example]"},{"id":"yaml-examples","title":"YAML examples","role":"content","text":"Find complete YAML examples for common deployment scenarios:\n\n- [YAML examples](https://redis.io/docs/latest/operate/kubernetes/reference/yaml) - Ready-to-use YAML configurations for different deployment types"},{"id":"example-categories","title":"Example categories","role":"example","text":"- [Basic deployment](https://redis.io/docs/latest/operate/kubernetes/reference/yaml/basic-deployment) - Essential YAML files for simple Redis Enterprise deployment\n- [Rack awareness](https://redis.io/docs/latest/operate/kubernetes/reference/yaml/rack-awareness) - YAML examples for rack-aware deployments across availability zones\n- [Active-Active](https://redis.io/docs/latest/operate/kubernetes/reference/yaml/active-active) - YAML examples for Active-Active databases across multiple clusters\n- [Multi-namespace](https://redis.io/docs/latest/operate/kubernetes/reference/yaml/multi-namespace) - YAML examples for deploying across multiple namespaces"},{"id":"api-reference","title":"API reference","role":"content","text":"Review complete API specifications for all Redis Enterprise custom resources:"},{"id":"core-resources","title":"Core resources","role":"content","text":"- [Redis Enterprise cluster API (REC)](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api) - Manage Redis Enterprise clusters\n- [Redis Enterprise database API (REDB)](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api) - Manage Redis databases"},{"id":"active-active-resources","title":"Active-Active resources","role":"content","text":"- [Active-Active database API (REAADB)](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_active_active_database_api) - Manage Active-Active databases\n- [Remote cluster API (RERC)](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_remote_cluster_api) - Configure remote cluster connections"},{"id":"access-control-resources","title":"Access control resources","role":"content","text":"- [RedisEnterpriseUser API (REUSER)](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_user_api) - Manage users for access control\n- [RedisEnterpriseRole API (REROLE)](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_role_api) - Define roles scoped to selected databases\n- [RedisEnterpriseRoleBinding API (REROLEBINDING)](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_role_binding_api) - Bind users to a scoped role\n- [RedisEnterpriseClusterRole API (RECROLE)](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_role_api) - Define cluster-scoped roles\n- [RedisEnterpriseClusterRoleBinding API (RECROLEBINDING)](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_role_binding_api) - Bind users to a cluster-scoped role\n- [RedisEnterpriseACL API (REACL)](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_acl_api) - Define access control lists"},{"id":"compatibility","title":"Compatibility","role":"compatibility","text":"Check supported Kubernetes distributions and versions:\n\n- [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) - Compatible Kubernetes platforms and versions"},{"id":"best-practices","title":"Best practices","role":"content","text":"Follow these best practices when working with custom resources:\n\n- **Use version control**: Store your YAML manifests in version control systems\n- **Validate before applying**: Use `kubectl apply --dry-run=client` to validate changes\n- **Monitor resource status**: Check resource status after you apply changes\n- **Follow naming conventions**: Use consistent naming for easier management\n- **Document configurations**: Add annotations and labels to describe resource purpose"}],"examples":[{"id":"create-custom-resources-ex0","language":"bash","code":"kubectl apply -f my-redis-cluster.yaml\nkubectl apply -f my-redis-database.yaml","section_id":"create-custom-resources"},{"id":"view-custom-resources-ex0","language":"bash","code":"# List Redis Enterprise clusters\nkubectl get rec\n\n# List Redis Enterprise databases\nkubectl get redb\n\n# List Active-Active databases\nkubectl get reaadb\n\n# List remote clusters\nkubectl get rerc\n\n# Get detailed information about a specific resource\nkubectl describe rec my-cluster\nkubectl describe redb my-database","section_id":"view-custom-resources"},{"id":"modify-custom-resources-ex0","language":"bash","code":"# Edit and apply updated manifest\nkubectl apply -f updated-redis-cluster.yaml\n\n# Or edit directly (not recommended for production)\nkubectl edit rec my-cluster\nkubectl edit redb my-database","section_id":"modify-custom-resources"}]}
{"schema_version":2,"id":"operate/kubernetes/reference/supported_k8s_distributions","title":"Supported Kubernetes distributions","url":"https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions/","summary":"Support matrix for the current Redis Software for Kubernetes operator","tags":["docs","operate","kubernetes"],"last_updated":"2026-07-21T12:02:16-05:00","page_type":"content","content_hash":"8fee2cdd781661a9ced89eafb24d38df5f7cd576b84aad9ed0d17cf273d4b15f","sections":[{"id":"kubernetes-version-support","title":"Kubernetes version support","role":"overview","text":"Redis Software for Kubernetes is compatible with [CNCF-conformant](https://www.cncf.io/training/certification/software-conformance/) Kubernetes platforms. The operator follows standard Kubernetes APIs and practices and is designed to run consistently across certified Kubernetes environments.\n\nRedis tests Redis Software for Kubernetes on the following platforms:\n\n- Red Hat OpenShift\n- Google Kubernetes Engine (GKE)\n- Rancher Kubernetes Engine (RKE)\n- Azure Kubernetes Service (AKS)\n- Amazon Elastic Kubernetes Service (EKS)\n\nRedis also supports other CNCF-conformant distributions that run a supported Kubernetes version, even without explicit testing.\n\nThe tables below use the following status icons:\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – This Kubernetes version is supported for this operator version.\n\n<span title=\"Deprecation warning\">&#x26a0;&#xfe0f;</span> Deprecated – Support for this Kubernetes version will be removed in a future release.\n\n<span title=\"X icon\">&#x274c;</span> End of life – No longer supported."},{"id":"latest-release","title":"Latest release","role":"content","text":"Operator release **8.2.0-12** supports the following Kubernetes and OpenShift versions.\n\n| OpenShift | Kubernetes | Support status |\n|---|---|---|\n| &mdash; | 1.36 | <span title=\"Supported\">&#x2705;</span> Supported |\n| 4.22 | 1.35 | <span title=\"Supported\">&#x2705;</span> Supported |\n| 4.21 | 1.34 | <span title=\"Supported\">&#x2705;</span> Supported |\n| 4.20 | 1.33 | <span title=\"Supported\">&#x2705;</span> Supported |\n| 4.19 | 1.32 | <span title=\"Deprecation warning\">&#x26a0;&#xfe0f;</span> Deprecated |"},{"id":"version-history","title":"Version history","role":"history","text":"The following table lists the Kubernetes versions supported by every operator version. Operator versions run from newest (left) to oldest (right).\n\n#### Table part 1 of 6, rows 1-10\n\n|  | Redis operator | **<nobr>8.2.0-12</nobr>** | **<nobr>8.0.20-21</nobr>** | **<nobr>8.0.18-11</nobr>** | **<nobr>8.0.10-15</nobr>** | **<nobr>8.0.6-6</nobr>** |\n| --- | --- | --- | --- | --- | --- | --- |\n|  |  | July 2026 | May 2026 | March 2026 | Jan 2026 | Dec 2025 |\n| OpenShift | Kubernetes |  |  |  |  |  |\n| &mdash; | 1.36 | <span title=\"Supported\">&#x2705;</span> |  |  |  |  |\n| 4.22 | 1.35 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |\n| 4.21 | 1.34 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 4.20 | 1.33 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 4.19 | 1.32 | <span title=\"Deprecation warning\">&#x26a0;&#xfe0f;</span> | <span title=\"Deprecation warning\">&#x26a0;&#xfe0f;</span> | <span title=\"Deprecation warning\">&#x26a0;&#xfe0f;</span> | <span title=\"Deprecation warning\">&#x26a0;&#xfe0f;</span> | <span title=\"Deprecation warning\">&#x26a0;&#xfe0f;</span> |\n| 4.18 | 1.31 | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\">&#x26a0;&#xfe0f;</span> | <span title=\"Deprecation warning\">&#x26a0;&#xfe0f;</span> | <span title=\"Deprecation warning\">&#x26a0;&#xfe0f;</span> | <span title=\"Deprecation warning\">&#x26a0;&#xfe0f;</span> |\n| 4.17 | 1.30 |  |  |  |  |  |\n| 4.16 | 1.29 |  |  |  |  |  |\n\n#### Table part 1 of 6, rows 11-20\n\n|  | Redis operator | **<nobr>8.2.0-12</nobr>** | **<nobr>8.0.20-21</nobr>** | **<nobr>8.0.18-11</nobr>** | **<nobr>8.0.10-15</nobr>** | **<nobr>8.0.6-6</nobr>** |\n| --- | --- | --- | --- | --- | --- | --- |\n| 4.15 | 1.28 |  |  |  |  |  |\n| 4.14 | 1.27 |  |  |  |  |  |\n| 4.13 | 1.26 |  |  |  |  |  |\n| 4.12 | 1.25 |  |  |  |  |  |\n| 4.11 | 1.24 |  |  |  |  |  |\n| 4.10 | 1.23 |  |  |  |  |  |\n| 4.09 | 1.22 |  |  |  |  |  |\n| 4.08 | 1.21 |  |  |  |  |  |\n| 4.07 | 1.20 |  |  |  |  |  |\n| 4.06 | 1.19 |  |  |  |  |  |\n\n#### Table part 1 of 6, rows 21-23\n\n|  | Redis operator | **<nobr>8.2.0-12</nobr>** | **<nobr>8.0.20-21</nobr>** | **<nobr>8.0.18-11</nobr>** | **<nobr>8.0.10-15</nobr>** | **<nobr>8.0.6-6</nobr>** |\n| --- | --- | --- | --- | --- | --- | --- |\n| 4.05 | 1.18 |  |  |  |  |  |\n| 4.04 | 1.17 |  |  |  |  |  |\n| 4.03 | 1.16 |  |  |  |  |  |\n\n#### Table part 2 of 6, rows 1-10\n\n|  | **<nobr>8.0.2-2</nobr>** | **<nobr>7.22.2-21</nobr>** | **<nobr>7.22.0-15</nobr>** | **<nobr>7.22.0-7</nobr>** | **<nobr>7.8.6-1</nobr>** | **<nobr>7.8.4-9</nobr>** |\n| --- | --- | --- | --- | --- | --- | --- |\n|  | Oct 2025 | Oct 2025 | July 2025 | April 2025 | March 2025 | March 2025 |\n| OpenShift |  |  |  |  |  |  |\n| &mdash; |  |  |  |  |  |  |\n| 4.22 |  |  |  |  |  |  |\n| 4.21 | <span title=\"Supported\">&#x2705;</span> |  |  |  |  |  |\n| 4.20 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |  |\n| 4.19 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |\n| 4.18 | <span title=\"Deprecation warning\">&#x26a0;&#xfe0f;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 4.17 | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\">&#x26a0;&#xfe0f;</span> | <span title=\"Deprecation warning\">&#x26a0;&#xfe0f;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 4.16 |  |  |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\">&#x26a0;&#xfe0f;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n#### Table part 2 of 6, rows 11-20\n\n|  | **<nobr>8.0.2-2</nobr>** | **<nobr>7.22.2-21</nobr>** | **<nobr>7.22.0-15</nobr>** | **<nobr>7.22.0-7</nobr>** | **<nobr>7.8.6-1</nobr>** | **<nobr>7.8.4-9</nobr>** |\n| --- | --- | --- | --- | --- | --- | --- |\n| 4.15 |  |  |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\">&#x26a0;&#xfe0f;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 4.14 |  |  |  |  |  | <span title=\"X icon\">&#x274c;</span> |\n| 4.13 |  |  |  |  |  |  |\n| 4.12 |  |  |  |  |  |  |\n| 4.11 |  |  |  |  |  |  |\n| 4.10 |  |  |  |  |  |  |\n| 4.09 |  |  |  |  |  |  |\n| 4.08 |  |  |  |  |  |  |\n| 4.07 |  |  |  |  |  |  |\n| 4.06 |  |  |  |  |  |  |\n\n#### Table part 2 of 6, rows 21-23\n\n|  | **<nobr>8.0.2-2</nobr>** | **<nobr>7.22.2-21</nobr>** | **<nobr>7.22.0-15</nobr>** | **<nobr>7.22.0-7</nobr>** | **<nobr>7.8.6-1</nobr>** | **<nobr>7.8.4-9</nobr>** |\n| --- | --- | --- | --- | --- | --- | --- |\n| 4.05 |  |  |  |  |  |  |\n| 4.04 |  |  |  |  |  |  |\n| 4.03 |  |  |  |  |  |  |\n\n#### Table part 3 of 6, rows 1-10\n\n|  | **<nobr>7.8.4-8</nobr>** | **<nobr>7.8.2-6</nobr>** | **<nobr>7.4.6-2</nobr>** | **<nobr>7.4.2-12</nobr>** | **<nobr>7.4.2-2</nobr>** | **<nobr>7.2.4-12</nobr>** |\n| --- | --- | --- | --- | --- | --- | --- |\n|  | Feb 2025 | Nov 2024 | July 2024 | May 2024 | March 2024 | Dec 2023 |\n| OpenShift |  |  |  |  |  |  |\n| &mdash; |  |  |  |  |  |  |\n| 4.22 |  |  |  |  |  |  |\n| 4.21 |  |  |  |  |  |  |\n| 4.20 |  |  |  |  |  |  |\n| 4.19 |  |  |  |  |  |  |\n| 4.18 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |  |  |\n| 4.17 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |  |\n| 4.16 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |\n\n#### Table part 3 of 6, rows 11-20\n\n|  | **<nobr>7.8.4-8</nobr>** | **<nobr>7.8.2-6</nobr>** | **<nobr>7.4.6-2</nobr>** | **<nobr>7.4.2-12</nobr>** | **<nobr>7.4.2-2</nobr>** | **<nobr>7.2.4-12</nobr>** |\n| --- | --- | --- | --- | --- | --- | --- |\n| 4.15 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 4.14 | <span title=\"X icon\">&#x274c;</span> | <span title=\"X icon\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 4.13 |  |  | <span title=\"Deprecation warning\">&#x26a0;&#xfe0f;</span> | <span title=\"Deprecation warning\">&#x26a0;&#xfe0f;</span> | <span title=\"Deprecation warning\">&#x26a0;&#xfe0f;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 4.12 |  |  |  |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 4.11 |  |  |  |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 4.10 |  |  |  |  |  |  |\n| 4.09 |  |  |  |  |  |  |\n| 4.08 |  |  |  |  |  |  |\n| 4.07 |  |  |  |  |  |  |\n| 4.06 |  |  |  |  |  |  |\n\n#### Table part 3 of 6, rows 21-23\n\n|  | **<nobr>7.8.4-8</nobr>** | **<nobr>7.8.2-6</nobr>** | **<nobr>7.4.6-2</nobr>** | **<nobr>7.4.2-12</nobr>** | **<nobr>7.4.2-2</nobr>** | **<nobr>7.2.4-12</nobr>** |\n| --- | --- | --- | --- | --- | --- | --- |\n| 4.05 |  |  |  |  |  |  |\n| 4.04 |  |  |  |  |  |  |\n| 4.03 |  |  |  |  |  |  |\n\n#### Table part 4 of 6, rows 1-10\n\n|  | **<nobr>7.2.4-7</nobr>** | **<nobr>7.2.4-2</nobr>** | **<nobr>6.4.2-8</nobr>** | **<nobr>6.4.2-6</nobr>** | **<nobr>6.4.2-5</nobr>** | **<nobr>6.4.2-4</nobr>** |\n| --- | --- | --- | --- | --- | --- | --- |\n|  | Oct 2023 | Aug 2023 | July 2023 | June 2023 | April 2023 | March 2023 |\n| OpenShift |  |  |  |  |  |  |\n| &mdash; |  |  |  |  |  |  |\n| 4.22 |  |  |  |  |  |  |\n| 4.21 |  |  |  |  |  |  |\n| 4.20 |  |  |  |  |  |  |\n| 4.19 |  |  |  |  |  |  |\n| 4.18 |  |  |  |  |  |  |\n| 4.17 |  |  |  |  |  |  |\n| 4.16 |  |  |  |  |  |  |\n\n#### Table part 4 of 6, rows 11-20\n\n|  | **<nobr>7.2.4-7</nobr>** | **<nobr>7.2.4-2</nobr>** | **<nobr>6.4.2-8</nobr>** | **<nobr>6.4.2-6</nobr>** | **<nobr>6.4.2-5</nobr>** | **<nobr>6.4.2-4</nobr>** |\n| --- | --- | --- | --- | --- | --- | --- |\n| 4.15 |  |  |  |  |  |  |\n| 4.14 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |\n| 4.13 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 4.12 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 4.11 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 4.10 |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\">&#x26a0;&#xfe0f;</span> | <span title=\"Deprecation warning\">&#x26a0;&#xfe0f;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 4.09 |  |  |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\">&#x26a0;&#xfe0f;</span> | <span title=\"Deprecation warning\">&#x26a0;&#xfe0f;</span> |\n| 4.08 |  |  |  |  |  |  |\n| 4.07 |  |  |  |  |  |  |\n| 4.06 |  |  |  |  |  |  |\n\n#### Table part 4 of 6, rows 21-23\n\n|  | **<nobr>7.2.4-7</nobr>** | **<nobr>7.2.4-2</nobr>** | **<nobr>6.4.2-8</nobr>** | **<nobr>6.4.2-6</nobr>** | **<nobr>6.4.2-5</nobr>** | **<nobr>6.4.2-4</nobr>** |\n| --- | --- | --- | --- | --- | --- | --- |\n| 4.05 |  |  |  |  |  |  |\n| 4.04 |  |  |  |  |  |  |\n| 4.03 |  |  |  |  |  |  |\n\n#### Table part 5 of 6, rows 1-10\n\n|  | **<nobr>6.2.18-41</nobr>** | **<nobr>6.2.18-3</nobr>** | **<nobr>6.2.12-1</nobr>** | **<nobr>6.2.10-45</nobr>** | **<nobr>6.2.10-34</nobr>** | **<nobr>6.2.10-4</nobr>** |\n| --- | --- | --- | --- | --- | --- | --- |\n|  | Jan 2023 | Nov 2022 | Sept 2022 | July 2022 | May 2022 | March 2022 |\n| OpenShift |  |  |  |  |  |  |\n| &mdash; |  |  |  |  |  |  |\n| 4.22 |  |  |  |  |  |  |\n| 4.21 |  |  |  |  |  |  |\n| 4.20 |  |  |  |  |  |  |\n| 4.19 |  |  |  |  |  |  |\n| 4.18 |  |  |  |  |  |  |\n| 4.17 |  |  |  |  |  |  |\n| 4.16 |  |  |  |  |  |  |\n\n#### Table part 5 of 6, rows 11-20\n\n|  | **<nobr>6.2.18-41</nobr>** | **<nobr>6.2.18-3</nobr>** | **<nobr>6.2.12-1</nobr>** | **<nobr>6.2.10-45</nobr>** | **<nobr>6.2.10-34</nobr>** | **<nobr>6.2.10-4</nobr>** |\n| --- | --- | --- | --- | --- | --- | --- |\n| 4.15 |  |  |  |  |  |  |\n| 4.14 |  |  |  |  |  |  |\n| 4.13 |  |  |  |  |  |  |\n| 4.12 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |  |  |\n| 4.11 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |\n| 4.10 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |\n| 4.09 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 4.08 |  |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\">&#x26a0;&#xfe0f;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 4.07 |  |  |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\">&#x26a0;&#xfe0f;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 4.06 |  |  |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\">&#x26a0;&#xfe0f;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n#### Table part 5 of 6, rows 21-23\n\n|  | **<nobr>6.2.18-41</nobr>** | **<nobr>6.2.18-3</nobr>** | **<nobr>6.2.12-1</nobr>** | **<nobr>6.2.10-45</nobr>** | **<nobr>6.2.10-34</nobr>** | **<nobr>6.2.10-4</nobr>** |\n| --- | --- | --- | --- | --- | --- | --- |\n| 4.05 |  |  |  |  |  | <span title=\"Supported\">&#x2705;</span> |\n| 4.04 |  |  |  |  |  |  |\n| 4.03 |  |  |  |  |  |  |\n\n#### Table part 6 of 6, rows 1-10\n\n|  | **<nobr>6.2.8-15</nobr>** | **<nobr>6.2.8-11</nobr>** | **<nobr>6.2.8-2</nobr>** | **<nobr>6.2.4-1</nobr>** |\n| --- | --- | --- | --- | --- |\n|  | Jan 2022 | Jan 2022 | Nov 2021 | Sept 2021 |\n| OpenShift |  |  |  |  |\n| &mdash; |  |  |  |  |\n| 4.22 |  |  |  |  |\n| 4.21 |  |  |  |  |\n| 4.20 |  |  |  |  |\n| 4.19 |  |  |  |  |\n| 4.18 |  |  |  |  |\n| 4.17 |  |  |  |  |\n| 4.16 |  |  |  |  |\n\n#### Table part 6 of 6, rows 11-20\n\n|  | **<nobr>6.2.8-15</nobr>** | **<nobr>6.2.8-11</nobr>** | **<nobr>6.2.8-2</nobr>** | **<nobr>6.2.4-1</nobr>** |\n| --- | --- | --- | --- | --- |\n| 4.15 |  |  |  |  |\n| 4.14 |  |  |  |  |\n| 4.13 |  |  |  |  |\n| 4.12 |  |  |  |  |\n| 4.11 |  |  |  |  |\n| 4.10 |  |  |  |  |\n| 4.09 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |\n| 4.08 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 4.07 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 4.06 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n#### Table part 6 of 6, rows 21-23\n\n|  | **<nobr>6.2.8-15</nobr>** | **<nobr>6.2.8-11</nobr>** | **<nobr>6.2.8-2</nobr>** | **<nobr>6.2.4-1</nobr>** |\n| --- | --- | --- | --- | --- |\n| 4.05 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 4.04 |  |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\">&#x26a0;&#xfe0f;</span> |\n| 4.03 |  |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\">&#x26a0;&#xfe0f;</span> |"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/reference/yaml/active-active","title":"Active-Active examples","url":"https://redis.io/docs/latest/operate/kubernetes/reference/yaml/active-active/","summary":"YAML examples for Active-Active Redis Enterprise databases across multiple Kubernetes clusters.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","page_type":"content","content_hash":"5f83816b5027cb761495fee0bd9e1e62a75ef170fddb3a021c65009daa1ae0d9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This page provides YAML examples for deploying Active-Active Redis Enterprise databases across multiple Kubernetes clusters. Active-Active databases provide multi-master replication with conflict resolution, enabling global distribution and local read/write access.\n\nFor complete deployment instructions, see [Active-Active databases](https://redis.io/docs/latest/operate/kubernetes/active-active)."},{"id":"applying-the-configuration","title":"Applying the configuration","role":"content","text":"To deploy Active-Active databases using these YAML files, follow [Create Active-Active database (REAADB)](https://redis.io/docs/latest/operate/kubernetes/active-active/create-reaadb), which provides detailed instructions for preparing clusters, creating RERC resources, and deploying REAADB configurations."},{"id":"namespace-examples","title":"Namespace examples","role":"content","text":"A namespace is an abstraction used by Kubernetes to support multiple virtual clusters on the same physical cluster.\n\n`ns-illinois.yaml` is used in [Create Active-Active database](https://redis.io/docs/latest/operate/kubernetes/active-active/create-reaadb#example-values).\n\n\n\n`ns-virginia.yaml` is used in [Create Active-Active database](https://redis.io/docs/latest/operate/kubernetes/active-active/create-reaadb#example-values)."},{"id":"rec-examples","title":"REC examples","role":"content","text":"A Redis Enterprise cluster is a collection of Redis Enterprise nodes that pools system resources across nodes and supports multi-tenant database instances.\n\n`rec-chicago.yaml` is used in [Create Active-Active database](https://redis.io/docs/latest/operate/kubernetes/active-active/create-reaadb#prerequisites) and [Create RERC](https://redis.io/docs/latest/operate/kubernetes/active-active/create-reaadb#create-rerc).\n\n\n\n`rec-arlington.yaml` is used in [Create Active-Active database](https://redis.io/docs/latest/operate/kubernetes/active-active/create-reaadb#prerequisites) and [Create RERC](https://redis.io/docs/latest/operate/kubernetes/active-active/create-reaadb#create-rerc)."},{"id":"rerc-examples","title":"RERC examples","role":"content","text":"RedisEnterpriseRemoteCluster represents a remote participating cluster.\n\n`rerc-ohare.yaml` is used in the [Create RERC](https://redis.io/docs/latest/operate/kubernetes/active-active/create-reaadb#create-rerc) section.\n\n\n\n`rerc-raegan.yaml` is used in the [Create RERC](https://redis.io/docs/latest/operate/kubernetes/active-active/create-reaadb#create-rerc) section."},{"id":"rerc-configuration","title":"RERC configuration","role":"content","text":"- [metadata.name](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_remote_cluster_api#appredislabscomv1alpha1): Unique name for this remote cluster reference\n- [spec.recName](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_remote_cluster_api#spec): Name of the remote REC\n- [spec.recNamespace](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_remote_cluster_api#spec): Namespace of the remote REC\n- [spec.apiFqdnUrl](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_remote_cluster_api#spec): API endpoint URL for the remote cluster\n- [spec.dbFqdnSuffix](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_remote_cluster_api#spec): Database hostname suffix for the remote cluster\n- [spec.secretName](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_remote_cluster_api#spec): Secret containing authentication credentials\n\nEdit the values in the downloaded YAML file for your specific setup, updating the remote cluster details, API endpoints, and secret names to match your actual environment."},{"id":"active-active-database-examples","title":"Active-Active database examples","role":"content","text":"Active-Active databases are geo-distributed databases that span multiple Redis Enterprise clusters and use multi-primary replication and conflict-free replicated data types (CRDTs).\n\n`reaadb-boeing.yaml` is used in the [Create Active-Active database](https://redis.io/docs/latest/operate/kubernetes/active-active/create-reaadb#create-reaadb) section."},{"id":"reaadb-configuration","title":"REAADB configuration","role":"content","text":"- [metadata.name](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_active_active_database_api#appredislabscomv1alpha1): Active-Active database name\n- [spec.participatingClusters](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_active_active_database_api#specparticipatingclusters): List of RERC names that participate in this database\n- [spec.globalConfigurations](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_active_active_database_api#specglobalconfigurations): Database settings applied to all participating clusters\n\nEdit the downloaded YAML file to add global database settings such as memory allocation, shard count, replication settings, database secrets, Redis modules, and database-specific Redis configuration."},{"id":"related-documentation","title":"Related documentation","role":"related","text":"- [Active-Active databases (index)](https://redis.io/docs/latest/operate/kubernetes/active-active)\n- [Prepare participating clusters](https://redis.io/docs/latest/operate/kubernetes/active-active/prepare-clusters)\n- [Create Active-Active database (REAADB)](https://redis.io/docs/latest/operate/kubernetes/active-active/create-reaadb)\n- [Edit global configuration](https://redis.io/docs/latest/operate/kubernetes/active-active/global-config)\n- [Sync global database secret](https://redis.io/docs/latest/operate/kubernetes/active-active/global-db-secret)\n- [RERC API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_remote_cluster_api)\n- [REAADB API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_active_active_database_api)\n- [Networking configuration](https://redis.io/docs/latest/operate/kubernetes/networking)"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/reference/yaml/basic-deployment","title":"Basic deployment examples","url":"https://redis.io/docs/latest/operate/kubernetes/reference/yaml/basic-deployment/","summary":"YAML examples for basic Redis Enterprise deployment including RBAC, cluster, and database configurations.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","page_type":"content","content_hash":"fe8a2cf1c1ef59248edd6d1e61e6611977efcec4e3fed076736c6e7c84d929a6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This page provides complete YAML examples for a basic Redis Enterprise deployment on Kubernetes. These examples include all the essential components you need to deploy a Redis Enterprise cluster and create a database.\n\nFor complete deployment instructions, see [Deploy on Kubernetes](https://redis.io/docs/latest/operate/kubernetes/deployment/quick-start)."},{"id":"service-account","title":"Service account","role":"content","text":"The service account provides an identity for the Redis Enterprise operator.\n\n\n\nService account configuration:\n- `name`: The service account name used by the operator\n- `labels`: Standard labels for Redis Enterprise resources"},{"id":"role","title":"Role","role":"content","text":"The Role defines the permissions needed by the Redis Enterprise operator within the namespace.\n\n\n\nRole configuration:\n- `name`: Must match the role name referenced in the role binding\n- `rules`: Comprehensive permissions for managing Redis Enterprise resources\n- `apiGroups`: Includes core Kubernetes APIs and Redis Enterprise custom resources\n\nKey permissions:\n- `app.redislabs.com`: Full access to Redis Enterprise custom resources\n- `secrets`: Manage TLS certificates and database credentials\n- `services`: Create and manage service endpoints\n- `pods`: Monitor and manage Redis Enterprise pods\n- `persistentvolumeclaims`: Manage persistent storage"},{"id":"role-binding","title":"Role binding","role":"content","text":"The RoleBinding connects the service account to the role, granting the necessary permissions.\n\n\n\nRole binding configuration:\n- `subjects.name`: Must match the service account name\n- `roleRef.name`: Must match the role name\n- `namespace`: Apply in the same namespace as other resources"},{"id":"redis-enterprise-cluster","title":"Redis Enterprise cluster","role":"content","text":"The RedisEnterpriseCluster (REC) custom resource defines the cluster specification.\n\n\n\nCluster configuration:\n- `metadata.name`: Cluster name (cannot be changed after creation)\n- `spec.nodes`: Number of Redis Enterprise nodes (minimum 3)\n- `persistentSpec.volumeSize`: Storage size per node\n- `redisEnterpriseNodeResources`: CPU and memory allocation per node\n\nEdit the values in the downloaded YAML file based on your requirements, such as increasing the number of nodes, adjusting storage size, or modifying resource allocation."},{"id":"redis-enterprise-database","title":"Redis Enterprise database","role":"content","text":"The RedisEnterpriseDatabase (REDB) custom resource defines the database specification.\n\n\n\nDatabase configuration:\n- `metadata.name`: Database name\n- `spec.memorySize`: Memory allocation for the database\n- `spec.shardCount`: Number of shards (affects performance and scalability)\n- `spec.replication`: Enable/disable database replication\n\nEdit the values in the downloaded YAML file based on your requirements, such as increasing memory for larger datasets, adding more shards for better performance, enabling replication for high availability, or adding Redis modules."},{"id":"apply-the-configuration","title":"Apply the configuration","role":"content","text":"To deploy these YAML files, follow [Deploy on Kubernetes](https://redis.io/docs/latest/operate/kubernetes/deployment/quick-start), which provides step-by-step instructions for creating namespaces, deploying the operator, and applying these configuration files."},{"id":"related-documentation","title":"Related documentation","role":"related","text":"- [Deploy on Kubernetes](https://redis.io/docs/latest/operate/kubernetes/deployment/quick-start)\n- [REC API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api)\n- [REDB API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api)"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/reference/yaml","title":"YAML examples","url":"https://redis.io/docs/latest/operate/kubernetes/reference/yaml/","summary":"Example YAML files for deploying Redis Enterprise on Kubernetes with different configurations.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","children":[{"id":"operate/kubernetes/reference/yaml/basic-deployment","summary":"YAML examples for basic Redis Enterprise deployment including RBAC, cluster, and database configurations.","title":"Basic deployment examples","url":"https://redis.io/docs/latest/operate/kubernetes/reference/yaml/basic-deployment/"},{"id":"operate/kubernetes/reference/yaml/rack-awareness","summary":"YAML examples for rack-aware Redis Enterprise deployments that distribute nodes across availability zones.","title":"Rack awareness examples","url":"https://redis.io/docs/latest/operate/kubernetes/reference/yaml/rack-awareness/"},{"id":"operate/kubernetes/reference/yaml/active-active","summary":"YAML examples for Active-Active Redis Enterprise databases across multiple Kubernetes clusters.","title":"Active-Active examples","url":"https://redis.io/docs/latest/operate/kubernetes/reference/yaml/active-active/"},{"id":"operate/kubernetes/reference/yaml/multi-namespace","summary":"YAML examples for deploying Redis Enterprise across multiple Kubernetes namespaces.","title":"Multi-namespace examples","url":"https://redis.io/docs/latest/operate/kubernetes/reference/yaml/multi-namespace/"},{"id":"operate/kubernetes/reference/yaml/log-collector-rbac","summary":"YAML examples for configuring RBAC permissions for the Redis Enterprise log collector tool in `restricted` and `all` modes.","title":"Log collector RBAC examples","url":"https://redis.io/docs/latest/operate/kubernetes/reference/yaml/log-collector-rbac/"}],"page_type":"content","content_hash":"5381db374b37b09c23f18b8423cb5be3d4a06d35c685fcbd66320d63b173da66","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This section provides complete YAML examples that cover common Redis Enterprise for Kubernetes deployment scenarios. Each example includes the necessary configuration files and step-by-step instructions for editing and applying them."},{"id":"how-to-use-these-examples","title":"How to use these examples","role":"content","text":""},{"id":"download-and-customize","title":"Download and customize","role":"content","text":"1. Copy the YAML content from the examples below\n2. Save each YAML block to a separate file with a descriptive name\n3. Edit the configuration values to match your environment\n4. Apply the files in the correct order using `kubectl apply`"},{"id":"configuration-storage","title":"Configuration storage","role":"configuration","text":"Redis Enterprise for Kubernetes stores configuration in several places:\n\n- Custom resources: Cluster and database specifications are stored as Kubernetes custom resources (REC, REDB, REAADB, RERC)\n- Secrets: Sensitive data like passwords and certificates are stored in Kubernetes secrets\n- ConfigMaps: Non-sensitive configuration data is stored in ConfigMaps\n- RBAC resources: Permissions are defined through Roles, ClusterRoles, and their bindings"},{"id":"applying-yaml-files","title":"Applying YAML files","role":"content","text":"Apply YAML files using `kubectl apply`:\n\n[code example]"},{"id":"monitoring-deployment","title":"Monitoring deployment","role":"content","text":"Check the status of your resources after applying:\n\n[code example]"},{"id":"example-categories","title":"Example categories","role":"example","text":"- [Basic deployment examples](https://redis.io/docs/latest/operate/kubernetes/reference/yaml/basic-deployment) - Service account, RBAC, cluster, and database configurations\n- [Rack awareness examples](https://redis.io/docs/latest/operate/kubernetes/reference/yaml/rack-awareness) - Rack-aware cluster configuration and required RBAC\n- [Active-Active examples](https://redis.io/docs/latest/operate/kubernetes/reference/yaml/active-active) - Multi-cluster Active-Active database setup\n- [Multi-namespace examples](https://redis.io/docs/latest/operate/kubernetes/reference/yaml/multi-namespace) - Cross-namespace operator and cluster configurations\n- [Log collector RBAC examples](https://redis.io/docs/latest/operate/kubernetes/reference/yaml/log-collector-rbac) - RBAC permissions for log collection in restricted and all modes"},{"id":"best-practices","title":"Best practices","role":"content","text":"- Validate configuration: Use `kubectl apply --dry-run=client` to validate YAML syntax and object schemas before applying\n- Version control: Store your customized YAML files in version control\n- Resource naming: Use consistent, descriptive names for all resources"},{"id":"related-documentation","title":"Related documentation","role":"related","text":"- [Reference](https://redis.io/docs/latest/operate/kubernetes/reference) - Complete API specifications for all custom resources\n- [Deploy Redis Enterprise Software for Kubernetes](https://redis.io/docs/latest/operate/kubernetes/deployment/quick-start) - Step-by-step deployment instructions\n- [Manage databases in multiple namespaces](https://redis.io/docs/latest/operate/kubernetes/re-clusters/multi-namespace) - Detailed multi-namespace setup instructions\n- [Active-Active databases](https://redis.io/docs/latest/operate/kubernetes/active-active) - Active-Active configuration and management"}],"examples":[{"id":"applying-yaml-files-ex0","language":"bash","code":"# Apply a single file\nkubectl apply -f my-config.yaml\n\n# Apply multiple files\nkubectl apply -f rbac/ -f cluster/ -f database/\n\n# Validate files without applying\nkubectl apply --dry-run=client -f my-config.yaml","section_id":"applying-yaml-files"},{"id":"monitoring-deployment-ex0","language":"bash","code":"# Check operator deployment\nkubectl get deployment redis-enterprise-operator\n\n# Check cluster status\nkubectl get rec\nkubectl describe rec <cluster-name>\n\n# Check database status\nkubectl get redb\nkubectl describe redb <database-name>\n\n# View events for troubleshooting\nkubectl get events --sort-by=.metadata.creationTimestamp","section_id":"monitoring-deployment"}]}
{"schema_version":2,"id":"operate/kubernetes/reference/yaml/log-collector-rbac","title":"Log collector RBAC examples","url":"https://redis.io/docs/latest/operate/kubernetes/reference/yaml/log-collector-rbac/","summary":"YAML examples for configuring RBAC permissions for the Redis Enterprise log collector tool in `restricted` and `all` modes.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","page_type":"content","content_hash":"503914d390c15a36cb16283d0c2a44c8a5b66c9839c3f8a83798382d9db16d5e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This page provides YAML examples for configuring RBAC permissions for the Redis Enterprise log collector tool. The log collector requires different permission levels depending on the collection mode you choose.\n\nFor complete log collection instructions, see [Collect logs](https://redis.io/docs/latest/operate/kubernetes/logs/collect-logs)."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"- Install the [Redis Enterprise operator](https://redis.io/docs/latest/operate/kubernetes/deployment)\n- Appropriate permissions to create RBAC resources in target namespaces\n- Understanding of your deployment model (single namespace, multi-namespace, etc.)"},{"id":"collection-modes","title":"Collection modes","role":"content","text":"The log collector has two collection modes that require different RBAC permissions:\n\n- **`restricted` mode** (recommended): Collects only Redis Enterprise resources with minimal security exposure. Default for versions 6.2.18-3+.\n- **`all` mode**: Collects comprehensive cluster information including nodes, storage classes, and operator resources. Use when specifically requested by Redis Support."},{"id":"restricted-mode-rbac","title":"`restricted` mode RBAC","role":"content","text":"The `restricted` mode configuration provides minimal permissions for collecting Redis Enterprise resources only.\n\n\n\n`restricted` mode configuration:\n\n- `Role`: Namespace-scoped permissions for Redis Enterprise resources\n- `ClusterRole`: Cluster-wide permissions for CRDs and basic cluster resources\n- `rules`: Minimal permissions for Redis Enterprise diagnostics\n\nKey permissions:\n\n- `pods, pods/log, pods/exec`: Access to pod information and logs\n- `app.redislabs.com/*`: All Redis Enterprise custom resources\n- `persistentvolumes`: Storage information for troubleshooting"},{"id":"all-mode-rbac","title":"`all` mode RBAC","role":"content","text":"The `all` mode configuration provides comprehensive permissions for collecting detailed cluster information.\n\n\n\n`all` mode configuration:\n\n- `Role`: Extended namespace permissions including operator resources\n- `ClusterRole`: Additional cluster-wide permissions for nodes and storage\n- `rules`: Comprehensive permissions for full cluster diagnostics\n\nAdditional permissions in `all` mode:\n\n- `nodes`: Node information and status\n- `storageclasses, volumeattachments`: Storage system details\n- `operators.coreos.com/*`: OpenShift operator information\n- `networking.istio.io/*`: Istio service mesh resources"},{"id":"apply-the-configuration","title":"Apply the configuration","role":"content","text":""},{"id":"namespace-requirements","title":"Namespace requirements","role":"content","text":"Create the Role and RoleBinding in every namespace where you need to collect logs:\n\n- Single namespace: Apply to the namespace where Redis Enterprise runs\n- Multi-namespace with single REC: Apply to the REC namespace plus each REDB namespace  \n- Multi-namespace with multiple RECs: Apply to each REC namespace\n\nThe ClusterRole and ClusterRoleBinding need to be created only once per cluster.\n\nEdit the values in the downloaded YAML file for your specific setup, updating the namespace references and role binding subjects to match your environment."},{"id":"role-binding-configuration","title":"Role binding configuration","role":"content","text":"The RBAC configurations include both roles and role bindings. The role bindings must reference the user or service account that will execute the log collector:\n\n- User subject: If running the log collector as a specific user, update the `subjects` section in the RoleBinding and ClusterRoleBinding to reference your username\n- Service account: If using a service account, create or reference the appropriate service account in the role bindings"},{"id":"manual-deployment","title":"Manual deployment","role":"content","text":"To apply the RBAC configurations manually:\n\n[code example]"},{"id":"usage","title":"Usage","role":"syntax","text":"After applying the RBAC configuration, run the log collector:\n\n[code example]"},{"id":"security-considerations","title":"Security considerations","role":"security","text":""},{"id":"best-practices","title":"Best practices","role":"content","text":"- Use `restricted` mode unless you specifically need additional cluster information\n- Limit namespace access to only where log collection is needed\n- Handle collected data according to your organization's security policies"},{"id":"secrets-permission-explanation","title":"Secrets permission explanation","role":"content","text":"The RBAC configurations request read access to secrets in the collected namespaces. **Secrets are not collected or included in the log package sent to Redis Support.** This permission is required because:\n\n- The log collector uses Helm commands (`helm list`, `helm get all`) to gather Redis Enterprise Helm chart deployment information\n- Helm stores its deployment metadata in Kubernetes secrets\n- This metadata contains only deployment configuration (not sensitive data)\n\nIf your security policies prohibit secrets access, you can remove the secrets permission from the Role, but this will limit the log collector's ability to gather Helm deployment information."},{"id":"troubleshooting","title":"Troubleshooting","role":"errors","text":""},{"id":"permission-errors","title":"Permission errors","role":"security","text":"- Verify that roles and bindings are applied correctly in the target namespaces\n- Check that the ClusterRole is applied cluster-wide\n- Ensure the service account has proper role bindings"},{"id":"missing-resources","title":"Missing resources","role":"content","text":"- Consider switching to `all` mode if additional cluster resources are needed\n- Verify that custom resource definitions are installed\n- Check that the operator has proper permissions"},{"id":"next-steps","title":"Next steps","role":"content","text":"- [Collect logs guide](https://redis.io/docs/latest/operate/kubernetes/logs/collect-logs)\n- [Basic deployment examples](https://redis.io/docs/latest/operate/kubernetes/reference/yaml/basic-deployment)\n- [Multi-namespace deployment](https://redis.io/docs/latest/operate/kubernetes/reference/yaml/multi-namespace)"},{"id":"related-documentation","title":"Related documentation","role":"related","text":"- [Kubernetes RBAC documentation](https://kubernetes.io/docs/reference/access-authn-authz/rbac/)\n- [Redis Enterprise troubleshooting](https://redis.io/docs/latest/operate/kubernetes/logs)\n- [Operator deployment guide](https://redis.io/docs/latest/operate/kubernetes/deployment)"}],"examples":[{"id":"manual-deployment-ex0","language":"bash","code":"# Apply restricted mode RBAC\nkubectl apply -f log-collector-restricted-rbac.yaml --namespace <namespace>\n\n# Apply all mode RBAC\nkubectl apply -f log-collector-all-rbac.yaml --namespace <namespace>","section_id":"manual-deployment"},{"id":"usage-ex0","language":"bash","code":"# Restricted mode (default for 6.2.18-3+)\npython log_collector.py -m restricted -n <namespace>\n\n# All mode\npython log_collector.py -m all -n <namespace>","section_id":"usage"}]}
{"schema_version":2,"id":"operate/kubernetes/reference/yaml/multi-namespace","title":"Multi-namespace examples","url":"https://redis.io/docs/latest/operate/kubernetes/reference/yaml/multi-namespace/","summary":"YAML examples for deploying Redis Enterprise across multiple Kubernetes namespaces.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","page_type":"content","content_hash":"211d33208e922d14e228d603c9fd31c2512b5b575f4bebcbc40f3d9399762cd4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Multi-namespace deployment lets a single Redis Enterprise operator manage clusters and databases in different namespaces, providing better resource isolation and organization.\n\nMulti-namespace deployment enables:\n- Namespace isolation: Separate Redis Enterprise resources by team, environment, or application\n- Centralized management: Single operator manages multiple namespaces\n- Resource sharing: Efficient use of cluster resources across namespaces\n- Flexible RBAC: Fine-grained permissions per namespace\n\nThis example shows:\n- Operator namespace: `redis-enterprise-operator` (where the operator and REC run)\n- Consumer namespaces: `app-production`, `app-staging` (where REDB resources are created)\n\nFor complete deployment instructions, see [Manage databases in multiple namespaces](https://redis.io/docs/latest/operate/kubernetes/re-clusters/multi-namespace)."},{"id":"operator-service-account","title":"Operator service account","role":"content","text":"Deploy these resources in the namespace where the Redis Enterprise operator runs."},{"id":"operator-cluster-role","title":"Operator cluster role","role":"content","text":"Grant the operator cluster-wide permissions to manage resources across namespaces."},{"id":"operator-cluster-role-binding","title":"Operator cluster role binding","role":"content","text":""},{"id":"consumer-role","title":"Consumer role","role":"content","text":""},{"id":"consumer-role-binding","title":"Consumer role binding","role":"content","text":"Consumer namespace configuration:\n\n- `subjects.name`: Must match the operator service account name\n- `subjects.namespace`: Must be the operator namespace, not the consumer namespace\n- `roleRef.name`: Must match the consumer role name"},{"id":"next-steps","title":"Next steps","role":"content","text":"- [Configure networking across namespaces](https://redis.io/docs/latest/operate/kubernetes/networking)\n- [Set up monitoring for multi-namespace deployment](https://redis.io/docs/latest/operate/kubernetes/re-clusters/connect-prometheus-operator)\n- [Learn about resource management](https://redis.io/docs/latest/operate/kubernetes/recommendations)"},{"id":"related-documentation","title":"Related documentation","role":"related","text":"- [Manage databases in multiple namespaces](https://redis.io/docs/latest/operate/kubernetes/re-clusters/multi-namespace)\n- [RBAC configuration](https://redis.io/docs/latest/operate/kubernetes/security)\n- [Kubernetes namespaces](https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/)"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/reference/yaml/rack-awareness","title":"Rack awareness examples","url":"https://redis.io/docs/latest/operate/kubernetes/reference/yaml/rack-awareness/","summary":"YAML examples for rack-aware Redis Enterprise deployments that distribute nodes across availability zones.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","page_type":"content","content_hash":"6cf3807873aeebce9a6c4e54c2646ef435c3f37ab776a1f542bbd82ba5b1d8a7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This page provides YAML examples for deploying Redis Enterprise with [rack awareness](https://redis.io/docs/latest/operate/kubernetes/recommendations/node-selection#using-rack-awareness). Rack awareness distributes Redis Enterprise nodes and database shards across different availability zones or failure domains to improve high availability and fault tolerance."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"- Label [Kubernetes nodes](https://kubernetes.io/docs/concepts/architecture/nodes/) with zone information\n- Typically uses the standard label `topology.kubernetes.io/zone`\n- Verify node labels: `kubectl get nodes -o custom-columns=\"name:metadata.name\",\"rack\\\\zone:metadata.labels.topology\\.kubernetes\\.io/zone\"`\n- Install the [Redis Enterprise operator](https://redis.io/docs/latest/operate/kubernetes/deployment)\n\nFor complete deployment instructions, see [Deploy on Kubernetes](https://redis.io/docs/latest/operate/kubernetes/deployment)."},{"id":"service-account","title":"Service account","role":"content","text":"The service account for rack-aware deployments is the same as [basic deployments](https://redis.io/docs/latest/operate/kubernetes/reference/yaml/basic-deployment#service-account)."},{"id":"cluster-role","title":"Cluster role","role":"content","text":"Rack awareness requires additional permissions to read [node labels](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/) across the cluster.\n\n\n\nCluster role configuration:\n\n- `name`: ClusterRole name for rack awareness permissions\n- `rules`: Permissions to read nodes and their labels cluster-wide\n- `resources`: Access to `nodes` resource for zone label discovery\n\nKey permissions:\n\n- `nodes`: Read access to discover node zone labels\n- `get, list, watch`: Monitor node changes and zone assignments"},{"id":"cluster-role-binding","title":"Cluster role binding","role":"content","text":"The [ClusterRoleBinding](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#rolebinding-and-clusterrolebinding) grants cluster-wide permissions to the service account.\n\n\n\nCluster role binding configuration:\n\n- `subjects.name`: Must match the service account name\n- `subjects.namespace`: Namespace where the operator is deployed\n- `roleRef.name`: Must match the cluster role name"},{"id":"rack-aware-redis-enterprise-cluster","title":"Rack-aware Redis Enterprise cluster","role":"content","text":"The rack-aware [REC configuration](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api) includes the `rackAwarenessNodeLabel` field.\n\n\n\nRack-aware cluster configuration:\n\n- `metadata.name`: Cluster name (cannot be changed after creation)\n- `spec.rackAwarenessNodeLabel`: Node label used for zone identification\n- `spec.nodes`: Minimum 3 nodes, ideally distributed across zones\n\nEdit the values in the downloaded YAML file based on your environment, such as increasing nodes for better zone distribution, using custom zone labels, adding resource specifications, or enabling persistent storage."},{"id":"common-zone-labels","title":"Common zone labels","role":"content","text":"Different Kubernetes distributions use different zone labels:\n\n- `Standard`: `topology.kubernetes.io/zone`\n- `Legacy`: `failure-domain.beta.kubernetes.io/zone`\n- `Custom`: Your organization's specific labeling scheme\n\nVerify the correct label on your nodes:\n\n[code example]"},{"id":"redis-enterprise-database","title":"Redis Enterprise database","role":"content","text":"Database configuration for rack-aware clusters is the same as [basic deployments](https://redis.io/docs/latest/operate/kubernetes/reference/yaml/basic-deployment#redis-enterprise-database).\n\n**Important**: For rack awareness to be effective, ensure your database has replication enabled. Rack awareness distributes primary and replica shards across zones, so databases without replication will not benefit from zone distribution."},{"id":"apply-the-configuration","title":"Apply the configuration","role":"content","text":"To deploy rack-aware Redis Enterprise clusters, follow [Deploy on Kubernetes](https://redis.io/docs/latest/operate/kubernetes/deployment) and ensure your Kubernetes nodes have proper zone labels. For detailed rack awareness configuration, see the [node selection recommendations](https://redis.io/docs/latest/operate/kubernetes/recommendations/node-selection)."},{"id":"troubleshooting","title":"Troubleshooting","role":"errors","text":""},{"id":"nodes-not-distributed-across-zones","title":"Nodes not distributed across zones","role":"content","text":"- Verify node labels are correct\n- Check that sufficient nodes exist in each zone\n- Ensure the `rackAwarenessNodeLabel` matches actual node labels"},{"id":"cluster-role-permissions-denied","title":"Cluster role permissions denied","role":"content","text":"- Verify the ClusterRole and ClusterRoleBinding are applied\n- Check that the service account name matches in all resources"},{"id":"database-shards-not-distributed","title":"Database shards not distributed","role":"content","text":"- Confirm the cluster has rack awareness enabled\n- **Check that the database has replication enabled** - rack awareness distributes primary/replica pairs across zones\n- Verify the database has multiple shards\n- Ensure sufficient nodes exist across zones"},{"id":"next-steps","title":"Next steps","role":"content","text":"- [Configure Active-Active databases](https://redis.io/docs/latest/operate/kubernetes/reference/yaml/active-active)\n- [Set up multi-namespace deployment](https://redis.io/docs/latest/operate/kubernetes/reference/yaml/multi-namespace)\n- [Learn about database replication](https://redis.io/docs/latest/operate/kubernetes/re-databases/replica-redb)"},{"id":"related-documentation","title":"Related documentation","role":"related","text":"- [Node selection recommendations](https://redis.io/docs/latest/operate/kubernetes/recommendations/node-selection)\n- [REC API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api)\n- [REDB API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api)\n- [Kubernetes node affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/)\n- [Redis Enterprise cluster architecture](https://redis.io/docs/latest/operate/kubernetes/architecture)"}],"examples":[{"id":"common-zone-labels-ex0","language":"bash","code":"kubectl get nodes -o custom-columns=\"name:metadata.name\",\"rack\\\\zone:metadata.labels.topology\\.kubernetes\\.io/zone\"","section_id":"common-zone-labels"}]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12","title":"Redis Enterprise for Kubernetes 7.2.4-12 (Dec 2023) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12/","summary":"This is a maintenance release with a few enhancements/fixes on the Kubernetes product and a new Redis Enterprise container version.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"f6487f83b359b0b670af28dbbd985f5a8b65d7bc0608bc035bc2cdc77402e010","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release with a few enhancements/fixes on the Kubernetes product and a newer Redis Enterprise container version."},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"enhancements","title":"Enhancements","role":"content","text":"- Added to [supported distributions](#supported-distributions)\n- Added support for Redis Enterprise Software 7.2.4-92 (RED-113750)\n- Replaced usage of config maps for readiness check and prestop scripts of Redis Enterprise pods (RED-81443)"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- Fixed issues around REC creation when namespace contains old resources from previous installation (RED-100669) (RED-100668)\n- Fixed constant updates to watched namespaces REDB services (RED-103264)\n- Fixed labeling of old operator configmaps (RED-105517)\n- Removed automatic upgrades of modules during upgrades (RED-106834)\n- Fixed unexpected restart of container during cluster recovery (RED-108511)\n- Fixed timeout of prestop (RED-111471)\n- Fixed deployment issues on TKG (RED-111271)"},{"id":"version-changes","title":"Version changes","role":"compatibility","text":"For a list of fixes related to CVEs, see the [Redis Enterprise 7.2.4-92 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-92#security)."},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"The following changes included in this release affect the upgrade process. Please read carefully before upgrading to 7.2.4-12.\n\n#### Upgrade operating system\n\n> [!WARNING]\n> If your databases use modules, you need to update all nodes in the cluster to Redis Enterprise 7.2.4 or later before upgrading your operating system. See [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os)in the Redis Enterprise Software documentation for more details.\n\n#### terminationGracePeriodSeconds\n\nThe configurable `terminationGracePeriodSeconds` REC field has been added to replace the hard-coded timeout of 15 minutes for a node to be stopped/drained (RED-111471).\n\n#### ValidatingWebhookConfiguration\n\nVersions 6.4.2-4 and later include a new `ValidatingWebhookConfiguration` resource to replace the `redb-admission` webhook resource. To use releases 6.4.2-4 or later, delete the old webhook resource and apply the new file. See [upgrade Redis cluster](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#reapply-webhook) for instructions.\n\n#### OpenShift SCC\n\nVersions 6.4.2-6 and later include a new SCC (`redis-enterprise-scc-v2`) that you need to bind to your service account before upgrading. OpenShift clusters running version 6.2.12 or earlier upgrading to version 6.2.18 or later might get stuck if you skip this step. See [upgrade a Redis Enterprise cluster (REC)](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#before-upgrading) for instructions."},{"id":"upcoming-changes","title":"Upcoming changes","role":"content","text":"- A future release of Redis Enterprise will remove support for RHEL7. We recommend migrating to RHEL8."},{"id":"supported-distributions","title":"Supported distributions","role":"compatibility","text":"The following table shows supported distributions at the time of this release. You can also find this list in [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions).\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – This distribution is supported for this version of Redis Enterprise Software for Kubernetes.\n\n<span title=\"Warning icon\">&#x26A0;&#xFE0F;</span> Deprecated – This distribution is still supported for this version of Redis Enterprise Software for Kubernetes, but support will be removed in a future release.\n\n<span title=\"X icon\">&#x274c;</span> End of life – Support for this distribution ended.\n\nAny distribution not listed below is not supported for production workloads.\n| **Kubernetes version** | **1.23** | **1.24** | **1.25** | **1.26** | **1.27** | **1.28** |\n|---|---|---|---|---|---|---|\n| Community Kubernetes |  | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Amazon EKS | <span title=\"Deprecation warning\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |\n| Azure AKS |  |  | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |\n| Google GKE | <span title=\"X icon\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |\n| Rancher 2.6 | <span title=\"X icon\">&#x274c;</span> | <span title=\"X icon\">&#x274c;</span> |  |  |  |  |\n| Rancher RKE2 | <span title=\"Deprecation warning\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |\n| VMware TKG 2.1 | <span title=\"Deprecation warning\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |  |  |\n| VMware TKG 2.2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |  |\n| VMware TKG 2.3 |  | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |\n| **OpenShift version** | **4.10** | **4.11** | **4.12** | **4.13** | **4.14** |  |\n|  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |\n| **VMware TKGI version** | **1.14** | **1.15** |  |  |  |  |\n|  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |  |  |"},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.2.4-92`\n- **Operator**: `redislabs/operator:7.2.4-12`\n- **Services Rigger**: `redislabs/k8s-controller:7.2.4-12.`\n\n#### OpenShift images\n\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.2.4-92.rhel8-openshift`\n    (or `redislabs/redis-enterprise:7.2.4-92.rhel7-openshift` if upgrading from RHEL 7)\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.2.4-12`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.2.4-12`\n\n#### OLM bundle\n\n**Redis Enterprise operator bundle** : `v7.2.4-12`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"The limitations below are specific to the Redis Enterprise for Kubernetes product. To view limitations for the supported Redis Enterprise Software version, see the [7.2.4 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases#known-limitations)."},{"id":"existing-limitations","title":"Existing limitations","role":"content","text":"- **When modifying the database suffix for an Active-Active database, while the service-rigger is in a terminating state, the services-rigger will delete and create the ingress or route resources in a loop (RED-107687)** Wait until the services- rigger pod has finished to terminate it.\n\n- **REAADB changes might fail with \"gateway timeout\" errors, mostly on OpenShift (RED-103048)** Retry the operation.\n\n\n- **Creating two databases with the same name directly on Redis Enterprise software will cause the service to be deleted and the database will not be available (RED-99997)** Avoid duplicating database names. Database creation via K8s has validation in place to prevent this.\n\n- **Installing the operator bundle produces warning: `Warning: would violate PodSecurity \"restricted: v1.24\"` (RED-97381)** Ignore the warning. This issue is documented as benign on official Red Hat documentation.\n\n- **RERC resources must have a unique name (RED-96302)** The string \"rec-name\"/\"rec-namespace\" must be different from all other participating clusters in the Active-Active database.\n\n- **Admission is not blocking REAADB with `shardCount` which exceeds license quota (RED-96301)** Fix the problems with the REAADB and reapply.\n\n- **Active-Active controller only supports global database options. Configuration specific to location is not supported (RED-86490)**\n\n- **Active-Active setup removal might keep services or routes undeleted (RED-77752)** Delete services or routes manually if you encounter this problem.\n\n- **`autoUpgrade` set to `true` can cause unexpected bdb upgrades when `redisUpgradePolicy` is set to `true` (RED-72351)** Contact support if your deployment is impacted.\n\n- **Following the previous quick start guide version causes issues with creating an REDB due to unrecognized memory field name (RED-69515)** The workaround is to use the newer (current) revision of Deploy Redis Enterprise Software for Kubernetes.\n\n- **PVC size issues when using decimal value in spec (RED-62132)** Make sure you use integer values for the PVC size.\n\n- **REC might report error states on initial startup (RED-61707)** There is no workaround at this time except to ignore the errors.\n\n- **Hashicorp Vault integration - no support for Gesher (RED-55080)** There is no workaround for this issue. Gesher support has been deprecated.\n\n- **REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED-47254)** When REC clusters are deployed on Kubernetes clusters without synchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes.\n\n- **Deleting an OpenShift project with an REC deployed may hang (RED-47192)** When an REC cluster is deployed in a project (namespace) and has REDB resources, the REDB resources must be deleted first before the REC can be deleted. Therefore, until the REDB resources are deleted, the project deletion will hang. The fix is to delete the REDB resources first and the REC second. Then, you can delete the project.\n\n- **Clusters must be named 'rec' in OLM-based deployments (RED-39825)** In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) are bound to a specific service account name (namely, \"rec\"). The workaround is to name the cluster \"rec\".\n\n- **Readiness probe incorrect on failures (RED-39300)** STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure.\n\n- **Internal DNS and Kubernetes DNS may have conflicts (RED-37462)** DNS conflicts are possible between the cluster `mdns_server` and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names.\n\n- **K8s-based 5.4.10 clusters seem to negatively affect existing 5.4.6 clusters (RED-37233)** Upgrade clusters to latest version.\n\n- **Node CPU usage is reported instead of pod CPU usage (RED-36884)** In Kubernetes, the reported node CPU usage is the usage of the Kubernetes worker node hosting the REC pod.\n\n- **An unreachable cluster has status running (RED-32805)** When a cluster is in an unreachable state, the state remains `running` instead of triggering an error.\n\n- **Long cluster names cause routes to be rejected  (RED-25871)** A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name exceeds 63 characters. The workaround is to limit the cluster name to 20 characters or fewer.\n\n- **Cluster CR (REC) errors are not reported after invalid updates (RED-25542)** A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12-03-24","title":"Redis Enterprise for Kubernetes 7.2.4-12 (March 2024) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12-03-24/","summary":"This is a maintenance release to support Redis Enterprise Software version 7.2.4-105.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"224bc1ae3f73785d05fce203020081f56e052ff5de42cfd3a68c2f8ae9f99255","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support Redis Enterprise Software version 7.2.4-105."},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"enhancements","title":"Enhancements","role":"content","text":"- Added support for Redis Enterprise Software 7.2.4-105 (RED-113750)"},{"id":"version-changes","title":"Version changes","role":"compatibility","text":"For a list of fixes related to CVEs, see the [Redis Enterprise 7.2.4-105 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-105#security)."},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"The following changes included in this release affect the upgrade process. Please read carefully before upgrading to 7.2.4-12.\n\n#### Upgrade operating system\n\n> [!WARNING]\n> If your databases use modules, you need to update all nodes in the cluster to Redis Enterprise 7.2.4 or later before upgrading your operating system. See [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os)in the Redis Enterprise Software documentation for more details.\n\n#### terminationGracePeriodSeconds\n\nThe configurable `terminationGracePeriodSeconds` REC field has been added to replace the hard-coded timeout of 15 minutes for a node to be stopped/drained (RED-111471).\n\n#### ValidatingWebhookConfiguration\n\nVersions 6.4.2-4 and later include a new `ValidatingWebhookConfiguration` resource to replace the `redb-admission` webhook resource. To use releases 6.4.2-4 or later, delete the old webhook resource and apply the new file. See [upgrade Redis cluster](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#reapply-webhook) for instructions.\n\n#### OpenShift SCC\n\nVersions 6.4.2-6 and later include a new SCC (`redis-enterprise-scc-v2`) that you need to bind to your service account before upgrading. OpenShift clusters running version 6.2.12 or earlier upgrading to version 6.2.18 or later might get stuck if you skip this step. See [upgrade a Redis Enterprise cluster (REC)](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#before-upgrading) for instructions."},{"id":"upcoming-changes","title":"Upcoming changes","role":"content","text":"- A future release of Redis Enterprise will remove support for RHEL7. We recommend migrating to RHEL8."},{"id":"supported-distributions","title":"Supported distributions","role":"compatibility","text":"The following table shows supported distributions at the time of this release. You can also find this list in [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions).\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – This distribution is supported for this version of Redis Enterprise Software for Kubernetes.\n\n<span title=\"Warning icon\">&#x26A0;&#xFE0F;</span> Deprecated – This distribution is still supported for this version of Redis Enterprise Software for Kubernetes, but support will be removed in a future release.\n\n<span title=\"X icon\">&#x274c;</span> End of life – Support for this distribution ended.\n\nAny distribution not listed below is not supported for production workloads.\n| **Kubernetes version** | **1.23** | **1.24** | **1.25** | **1.26** | **1.27** | **1.28** |\n|---|---|---|---|---|---|---|\n| Community Kubernetes |  | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Amazon EKS | <span title=\"Deprecation warning\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |\n| Azure AKS |  |  | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |\n| Google GKE | <span title=\"X icon\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |\n| Rancher 2.6 | <span title=\"X icon\">&#x274c;</span> | <span title=\"X icon\">&#x274c;</span> |  |  |  |  |\n| Rancher RKE2 | <span title=\"Deprecation warning\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |\n| VMware TKG 2.1 | <span title=\"Deprecation warning\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |  |  |\n| VMware TKG 2.2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |  |\n| VMware TKG 2.3 |  | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |\n| **OpenShift version** | **4.10** | **4.11** | **4.12** | **4.13** | **4.14** |  |\n|  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |\n| **VMware TKGI version** | **1.14** | **1.15** |  |  |  |  |\n|  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |  |  |"},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.2.4-105`\n- **Operator**: `redislabs/operator:7.2.4-12`\n- **Services Rigger**: `redislabs/k8s-controller:7.2.4-12.`\n\n#### OpenShift images\n\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.2.4-105.rhel8-openshift`\n    (or `redislabs/redis-enterprise:7.2.4-105.rhel7-openshift` if upgrading from RHEL 7)\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.2.4-12`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.2.4-12`\n\n#### OLM bundle\n\n**Redis Enterprise operator bundle** : `v7.2.4-12.6`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"The limitations below are specific to the Redis Enterprise for Kubernetes product. To view limitations for the supported Redis Enterprise Software version, see the [7.2.4 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases#known-limitations)."},{"id":"existing-limitations","title":"Existing limitations","role":"content","text":"- **When modifying the database suffix for an Active-Active database, while the service-rigger is in a terminating state, the services-rigger will delete and create the ingress or route resources in a loop (RED-107687)** Wait until the services- rigger pod has finished to terminate it.\n\n- **REAADB changes might fail with \"gateway timeout\" errors, mostly on OpenShift (RED-103048)** Retry the operation.\n\n\n- **Creating two databases with the same name directly on Redis Enterprise software will cause the service to be deleted and the database will not be available (RED-99997)** Avoid duplicating database names. Database creation via K8s has validation in place to prevent this.\n\n- **Installing the operator bundle produces warning: `Warning: would violate PodSecurity \"restricted: v1.24\"` (RED-97381)** Ignore the warning. This issue is documented as benign on official Red Hat documentation.\n\n- **RERC resources must have a unique name (RED-96302)** The string \"rec-name\"/\"rec-namespace\" must be different from all other participating clusters in the Active-Active database.\n\n- **Admission is not blocking REAADB with `shardCount` which exceeds license quota (RED-96301)** Fix the problems with the REAADB and reapply.\n\n- **Active-Active controller only supports global database options. Configuration specific to location is not supported (RED-86490)**\n\n- **Active-Active setup removal might keep services or routes undeleted (RED-77752)** Delete services or routes manually if you encounter this problem.\n\n- **`autoUpgrade` set to `true` can cause unexpected bdb upgrades when `redisUpgradePolicy` is set to `true` (RED-72351)** Contact support if your deployment is impacted.\n\n- **Following the previous quick start guide version causes issues with creating an REDB due to unrecognized memory field name (RED-69515)** The workaround is to use the newer (current) revision of Deploy Redis Enterprise Software for Kubernetes.\n\n- **PVC size issues when using decimal value in spec (RED-62132)** Make sure you use integer values for the PVC size.\n\n- **REC might report error states on initial startup (RED-61707)** There is no workaround at this time except to ignore the errors.\n\n- **Hashicorp Vault integration - no support for Gesher (RED-55080)** There is no workaround for this issue. Gesher support has been deprecated.\n\n- **REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED-47254)** When REC clusters are deployed on Kubernetes clusters without synchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes.\n\n- **Deleting an OpenShift project with an REC deployed may hang (RED-47192)** When an REC cluster is deployed in a project (namespace) and has REDB resources, the REDB resources must be deleted first before the REC can be deleted. Therefore, until the REDB resources are deleted, the project deletion will hang. The fix is to delete the REDB resources first and the REC second. Then, you can delete the project.\n\n- **Clusters must be named 'rec' in OLM-based deployments (RED-39825)** In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) are bound to a specific service account name (namely, \"rec\"). The workaround is to name the cluster \"rec\".\n\n- **Readiness probe incorrect on failures (RED-39300)** STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure.\n\n- **Internal DNS and Kubernetes DNS may have conflicts (RED-37462)** DNS conflicts are possible between the cluster `mdns_server` and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names.\n\n- **K8s-based 5.4.10 clusters seem to negatively affect existing 5.4.6 clusters (RED-37233)** Upgrade clusters to latest version.\n\n- **Node CPU usage is reported instead of pod CPU usage (RED-36884)** In Kubernetes, the reported node CPU usage is the usage of the Kubernetes worker node hosting the REC pod.\n\n- **An unreachable cluster has status running (RED-32805)** When a cluster is in an unreachable state, the state remains `running` instead of triggering an error.\n\n- **Long cluster names cause routes to be rejected  (RED-25871)** A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name exceeds 63 characters. The workaround is to limit the cluster name to 20 characters or fewer.\n\n- **Cluster CR (REC) errors are not reported after invalid updates (RED-25542)** A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12-08-2024","title":"Redis Enterprise for Kubernetes 7.2.4-12 (August 2024) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12-08-2024/","summary":"This is a maintenance release to support Redis Enterprise Software version 7.2.4-109.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"337dc5b0f889c15d1a028310b6492d6d5cfd6b8bb933c86dd03ed6e03596a8c8","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support Redis Enterprise Software version 7.2.4-109."},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"enhancements","title":"Enhancements","role":"content","text":"- Added support for Redis Enterprise Software 7.2.4-109."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":"For a list of fixes related to CVEs, see the [Redis Enterprise 7.2.4-109 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-109)."},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"The following changes included in this release affect the upgrade process. Please read carefully before upgrading to 7.2.4-12.\n\n#### Upgrade operating system\n\n> [!WARNING]\n> If your databases use modules, you need to update all nodes in the cluster to Redis Enterprise 7.2.4 or later before upgrading your operating system. See [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os) in the Redis Enterprise Software documentation for more details.\n\n#### terminationGracePeriodSeconds\n\nThe configurable `terminationGracePeriodSeconds` REC field has been added to replace the hard-coded timeout of 15 minutes for a node to be stopped/drained (RED-111471).\n\n#### ValidatingWebhookConfiguration\n\nVersions 6.4.2-4 and later include a new `ValidatingWebhookConfiguration` resource to replace the `redb-admission` webhook resource. To use releases 6.4.2-4 or later, delete the old webhook resource and apply the new file. See [upgrade Redis cluster](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#reapply-webhook) for instructions.\n\n#### OpenShift SCC\n\nVersions 6.4.2-6 and later include a new SCC (`redis-enterprise-scc-v2`) that you need to bind to your service account before upgrading. OpenShift clusters running version 6.2.12 or earlier upgrading to version 6.2.18 or later might get stuck if you skip this step. See [upgrade a Redis Enterprise cluster (REC)](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#before-upgrading) for instructions."},{"id":"upcoming-changes","title":"Upcoming changes","role":"content","text":"- A future release of Redis Enterprise will remove support for RHEL7. We recommend migrating to RHEL8."},{"id":"supported-distributions","title":"Supported distributions","role":"compatibility","text":"The following table shows supported distributions at the time of this release. You can also find this list in [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions).\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – This distribution is supported for this version of Redis Enterprise Software for Kubernetes.\n\n<span title=\"Warning icon\">&#x26A0;&#xFE0F;</span> Deprecated – This distribution is still supported for this version of Redis Enterprise Software for Kubernetes, but support will be removed in a future release.\n\n<span title=\"X icon\">&#x274c;</span> End of life – Support for this distribution ended.\n\nAny distribution not listed below is not supported for production workloads.\n| **Kubernetes version** | **1.23** | **1.24** | **1.25** | **1.26** | **1.27** | **1.28** |\n|---|---|---|---|---|---|---|\n| Community Kubernetes |  | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Amazon EKS | <span title=\"Deprecation warning\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |\n| Azure AKS |  |  | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |\n| Google GKE | <span title=\"X icon\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |\n| Rancher 2.6 | <span title=\"X icon\">&#x274c;</span> | <span title=\"X icon\">&#x274c;</span> |  |  |  |  |\n| Rancher RKE2 | <span title=\"Deprecation warning\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |\n| VMware TKG 2.1 | <span title=\"Deprecation warning\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |  |  |\n| VMware TKG 2.2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |  |\n| VMware TKG 2.3 |  | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |\n| **OpenShift version** | **4.10** | **4.11** | **4.12** | **4.13** | **4.14** |  |\n|  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |\n| **VMware TKGI version** | **1.14** | **1.15** |  |  |  |  |\n|  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |  |  |"},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.2.4-109`\n- **Operator**: `redislabs/operator:7.2.4-12`\n- **Services Rigger**: `redislabs/k8s-controller:7.2.4-12.`\n\n#### OpenShift images\n\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.2.4-109.rhel8-openshift`\n    (or `redislabs/redis-enterprise:7.2.4-109.rhel7-openshift` if upgrading from RHEL 7)\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.2.4-12`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.2.4-12`\n\n#### OLM bundle\n\n**Redis Enterprise operator bundle** : `v7.2.4-12.11`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"The limitations below are specific to the Redis Enterprise for Kubernetes product. To view limitations for the supported Redis Enterprise Software version, see the [7.2.4 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases#known-limitations)."},{"id":"existing-limitations","title":"Existing limitations","role":"content","text":"- **When modifying the database suffix for an Active-Active database, while the service-rigger is in a terminating state, the services-rigger will delete and create the ingress or route resources in a loop (RED-107687)** Wait until the services- rigger pod has finished to terminate it.\n\n- **REAADB changes might fail with \"gateway timeout\" errors, mostly on OpenShift (RED-103048)** Retry the operation.\n\n\n- **Creating two databases with the same name directly on Redis Enterprise software will cause the service to be deleted and the database will not be available (RED-99997)** Avoid duplicating database names. Database creation via K8s has validation in place to prevent this.\n\n- **Installing the operator bundle produces warning: `Warning: would violate PodSecurity \"restricted: v1.24\"` (RED-97381)** Ignore the warning. This issue is documented as benign on official Red Hat documentation.\n\n- **RERC resources must have a unique name (RED-96302)** The string \"rec-name\"/\"rec-namespace\" must be different from all other participating clusters in the Active-Active database.\n\n- **Admission is not blocking REAADB with `shardCount` which exceeds license quota (RED-96301)** Fix the problems with the REAADB and reapply.\n\n- **Active-Active controller only supports global database options. Configuration specific to location is not supported (RED-86490)**\n\n- **Active-Active setup removal might keep services or routes undeleted (RED-77752)** Delete services or routes manually if you encounter this problem.\n\n- **`autoUpgrade` set to `true` can cause unexpected bdb upgrades when `redisUpgradePolicy` is set to `true` (RED-72351)** Contact support if your deployment is impacted.\n\n- **Following the previous quick start guide version causes issues with creating an REDB due to unrecognized memory field name (RED-69515)** The workaround is to use the newer (current) revision of Deploy Redis Enterprise Software for Kubernetes.\n\n- **PVC size issues when using decimal value in spec (RED-62132)** Make sure you use integer values for the PVC size.\n\n- **REC might report error states on initial startup (RED-61707)** There is no workaround at this time except to ignore the errors.\n\n- **Hashicorp Vault integration - no support for Gesher (RED-55080)** There is no workaround for this issue. Gesher support has been deprecated.\n\n- **REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED-47254)** When REC clusters are deployed on Kubernetes clusters without synchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes.\n\n- **Deleting an OpenShift project with an REC deployed may hang (RED-47192)** When an REC cluster is deployed in a project (namespace) and has REDB resources, the REDB resources must be deleted first before the REC can be deleted. Therefore, until the REDB resources are deleted, the project deletion will hang. The fix is to delete the REDB resources first and the REC second. Then, you can delete the project.\n\n- **Clusters must be named 'rec' in OLM-based deployments (RED-39825)** In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) are bound to a specific service account name (namely, \"rec\"). The workaround is to name the cluster \"rec\".\n\n- **Readiness probe incorrect on failures (RED-39300)** STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure.\n\n- **Internal DNS and Kubernetes DNS may have conflicts (RED-37462)** DNS conflicts are possible between the cluster `mdns_server` and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names.\n\n- **K8s-based 5.4.10 clusters seem to negatively affect existing 5.4.6 clusters (RED-37233)** Upgrade clusters to the latest version.\n\n- **Node CPU usage is reported instead of pod CPU usage (RED-36884)** In Kubernetes, the reported node CPU usage is the usage of the Kubernetes worker node hosting the REC pod.\n\n- **An unreachable cluster has status running (RED-32805)** When a cluster is in an unreachable state, the state remains `running` instead of triggering an error.\n\n- **Long cluster names cause routes to be rejected  (RED-25871)** A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name exceeds 63 characters. The workaround is to limit the cluster name to 20 characters or fewer.\n\n- **Cluster CR (REC) errors are not reported after invalid updates (RED-25542)** A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12-10-24","title":"Redis Enterprise for Kubernetes 7.2.4-12 (October 2024) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12-10-24/","summary":"This is a maintenance release to support Redis Enterprise Software version 7.2.4-117.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"29661bc22346cbd5de977c29929015c2473bb9052108ae417e88d1b3a1d5ef7b","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support Redis Enterprise Software version 7.2.4-117."},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"enhancements","title":"Enhancements","role":"content","text":"- Added support for Redis Enterprise Software 7.2.4-117."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":"For a list of fixes related to CVEs, see the [Redis Enterprise 7.2.4-117 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases)."},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"The following changes included in this release affect the upgrade process. Please read carefully before upgrading to 7.2.4-12.\n\n#### Upgrade operating system\n\n> [!WARNING]\n> If your databases use modules, you need to update all nodes in the cluster to Redis Enterprise 7.2.4 or later before upgrading your operating system. See [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os) in the Redis Enterprise Software documentation for more details.\n\n#### terminationGracePeriodSeconds\n\nThe configurable `terminationGracePeriodSeconds` REC field has been added to replace the hard-coded timeout of 15 minutes for a node to be stopped/drained (RED-111471).\n\n#### ValidatingWebhookConfiguration\n\nVersions 6.4.2-4 and later include a new `ValidatingWebhookConfiguration` resource to replace the `redb-admission` webhook resource. To use releases 6.4.2-4 or later, delete the old webhook resource and apply the new file. See [upgrade Redis cluster](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#reapply-webhook) for instructions.\n\n#### OpenShift SCC\n\nVersions 6.4.2-6 and later include a new SCC (`redis-enterprise-scc-v2`) that you need to bind to your service account before upgrading. OpenShift clusters running version 6.2.12 or earlier upgrading to version 6.2.18 or later might get stuck if you skip this step. See [upgrade a Redis Enterprise cluster (REC)](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#before-upgrading) for instructions."},{"id":"upcoming-changes","title":"Upcoming changes","role":"content","text":"- A future release of Redis Enterprise will remove support for RHEL7. We recommend migrating to RHEL8."},{"id":"supported-distributions","title":"Supported distributions","role":"compatibility","text":"The following table shows supported distributions at the time of this release. You can also find this list in [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions).\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – This distribution is supported for this version of Redis Enterprise Software for Kubernetes.\n\n<span title=\"Warning icon\">&#x26A0;&#xFE0F;</span> Deprecated – This distribution is still supported for this version of Redis Enterprise Software for Kubernetes, but support will be removed in a future release.\n\n<span title=\"X icon\">&#x274c;</span> End of life – Support for this distribution ended.\n\nAny distribution not listed below is not supported for production workloads.\n| **Kubernetes version** | **1.23** | **1.24** | **1.25** | **1.26** | **1.27** | **1.28** |\n|---|---|---|---|---|---|---|\n| Community Kubernetes |  | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Amazon EKS | <span title=\"Deprecation warning\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |\n| Azure AKS |  |  | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |\n| Google GKE | <span title=\"X icon\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |\n| Rancher 2.6 | <span title=\"X icon\">&#x274c;</span> | <span title=\"X icon\">&#x274c;</span> |  |  |  |  |\n| Rancher RKE2 | <span title=\"Deprecation warning\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |\n| VMware TKG 2.1 | <span title=\"Deprecation warning\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |  |  |\n| VMware TKG 2.2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |  |\n| VMware TKG 2.3 |  | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |\n| **OpenShift version** | **4.10** | **4.11** | **4.12** | **4.13** | **4.14** |  |\n|  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |\n| **VMware TKGI version** | **1.14** | **1.15** |  |  |  |  |\n|  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |  |  |"},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.2.4-117`\n- **Operator**: `redislabs/operator:7.2.4-12`\n- **Services Rigger**: `redislabs/k8s-controller:7.2.4-12.`\n\n#### OpenShift images\n\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.2.4-117.rhel8-openshift`\n    (or `redislabs/redis-enterprise:7.2.4-117.rhel7-openshift` if upgrading from RHEL 7)\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.2.4-12`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.2.4-12`\n\n#### OLM bundle\n\n**Redis Enterprise operator bundle** : `v7.2.4-12.22`\n\nThe OLM version v7.2.4-12.22 replaces the earlier v7.2.4-12.21 release for the same Redis software version, providing only upgrade path fixes."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"The limitations below are specific to the Redis Enterprise for Kubernetes product. To view limitations for the supported Redis Enterprise Software version, see the [7.2.4 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases#known-limitations)."},{"id":"existing-limitations","title":"Existing limitations","role":"content","text":"- **When modifying the database suffix for an Active-Active database, while the service-rigger is in a terminating state, the services-rigger will delete and create the ingress or route resources in a loop (RED-107687)** Wait until the services- rigger pod has finished to terminate it.\n\n- **REAADB changes might fail with \"gateway timeout\" errors, mostly on OpenShift (RED-103048)** Retry the operation.\n\n\n- **Creating two databases with the same name directly on Redis Enterprise software will cause the service to be deleted and the database will not be available (RED-99997)** Avoid duplicating database names. Database creation via K8s has validation in place to prevent this.\n\n- **Installing the operator bundle produces warning: `Warning: would violate PodSecurity \"restricted: v1.24\"` (RED-97381)** Ignore the warning. This issue is documented as benign on official Red Hat documentation.\n\n- **RERC resources must have a unique name (RED-96302)** The string \"rec-name\"/\"rec-namespace\" must be different from all other participating clusters in the Active-Active database.\n\n- **Admission is not blocking REAADB with `shardCount` which exceeds license quota (RED-96301)** Fix the problems with the REAADB and reapply.\n\n- **Active-Active controller only supports global database options. Configuration specific to location is not supported (RED-86490)**\n\n- **Active-Active setup removal might keep services or routes undeleted (RED-77752)** Delete services or routes manually if you encounter this problem.\n\n- **`autoUpgrade` set to `true` can cause unexpected bdb upgrades when `redisUpgradePolicy` is set to `true` (RED-72351)** Contact support if your deployment is impacted.\n\n- **Following the previous quick start guide version causes issues with creating an REDB due to unrecognized memory field name (RED-69515)** The workaround is to use the newer (current) revision of Deploy Redis Enterprise Software for Kubernetes.\n\n- **PVC size issues when using decimal value in spec (RED-62132)** Make sure you use integer values for the PVC size.\n\n- **REC might report error states on initial startup (RED-61707)** There is no workaround at this time except to ignore the errors.\n\n- **Hashicorp Vault integration - no support for Gesher (RED-55080)** There is no workaround for this issue. Gesher support has been deprecated.\n\n- **REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED-47254)** When REC clusters are deployed on Kubernetes clusters without synchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes.\n\n- **Deleting an OpenShift project with an REC deployed may hang (RED-47192)** When an REC cluster is deployed in a project (namespace) and has REDB resources, the REDB resources must be deleted first before the REC can be deleted. Therefore, until the REDB resources are deleted, the project deletion will hang. The fix is to delete the REDB resources first and the REC second. Then, you can delete the project.\n\n- **Clusters must be named 'rec' in OLM-based deployments (RED-39825)** In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) are bound to a specific service account name (namely, \"rec\"). The workaround is to name the cluster \"rec\".\n\n- **Readiness probe incorrect on failures (RED-39300)** STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure.\n\n- **Internal DNS and Kubernetes DNS may have conflicts (RED-37462)** DNS conflicts are possible between the cluster `mdns_server` and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names.\n\n- **K8s-based 5.4.10 clusters seem to negatively affect existing 5.4.6 clusters (RED-37233)** Upgrade clusters to the latest version.\n\n- **Node CPU usage is reported instead of pod CPU usage (RED-36884)** In Kubernetes, the reported node CPU usage is the usage of the Kubernetes worker node hosting the REC pod.\n\n- **An unreachable cluster has status running (RED-32805)** When a cluster is in an unreachable state, the state remains `running` instead of triggering an error.\n\n- **Long cluster names cause routes to be rejected  (RED-25871)** A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name exceeds 63 characters. The workaround is to limit the cluster name to 20 characters or fewer.\n\n- **Cluster CR (REC) errors are not reported after invalid updates (RED-25542)** A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12-jan25","title":"Redis Enterprise for Kubernetes 7.2.4-12 (January 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12-jan25/","summary":"This is a maintenance release to support Redis Enterprise Software version 7.2.4-118.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"168a8027da69d13ba6feda8313212fd3a99ca9393305e47e36d17afc2fe0d161","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support Redis Enterprise Software version 7.2.4-118. For information on supported distributions, breaking changes, and limitations, see the previous [7.2.4-12 release notes](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12-10-24)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.2.4-118`\n- **Operator**: `redislabs/operator:7.2.4-12`\n- **Services Rigger**: `redislabs/k8s-controller:7.2.4-12.`\n\n#### OpenShift images\n\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.2.4-118.rhel8-openshift`\n    (or `redislabs/redis-enterprise:7.2.4-118.rhel7-openshift` if upgrading from RHEL 7)\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.2.4-12`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.2.4-12`\n\n#### OLM bundle\n\n**Redis Enterprise operator bundle** : `v7.2.4-12.33`"},{"id":"security","title":"Security","role":"security","text":"For a list of fixes related to CVEs, see the [Redis Enterprise 7.2.4-118 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases)."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12-june25","title":"Redis Enterprise for Kubernetes 7.2.4-12 (June 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12-june25/","summary":"This is a maintenance release to support Redis Enterprise Software version 7.2.4-130.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"897c896627aab0cee3e5ef37dd75cc513e0c429e3288616a491f83a6ef141a14","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support Redis Enterprise Software version 7.2.4-130. For information on supported distributions, breaking changes, and limitations, see the previous [7.2.4-12 release notes](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12-mar25)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.2.4-130`\n- **Operator**: `redislabs/operator:7.2.4-12`\n- **Services Rigger**: `redislabs/k8s-controller:7.2.4-12.`"},{"id":"openshift-images","title":"OpenShift images","role":"content","text":"- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.2.4-130.rhel8-openshift`\n    (or `redislabs/redis-enterprise:7.2.4-130.rhel7-openshift` if upgrading from RHEL 7)\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.2.4-12`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.2.4-12`"},{"id":"olm-bundle","title":"OLM bundle","role":"content","text":"**Redis Enterprise operator bundle** : `v7.2.4-12.39`"},{"id":"security","title":"Security","role":"security","text":"For a list of fixes related to CVEs, see the [Redis Enterprise 7.2.4-130 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases)."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12-mar25","title":"Redis Enterprise for Kubernetes 7.2.4-12 (March 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12-mar25/","summary":"This is a maintenance release to support Redis Enterprise Software version 7.2.4-118.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"7a46d48778f0c369fa54f1b6b89d77e4d6fd88ca6fe72a2d71385e0030478236","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support Redis Enterprise Software version 7.2.4-122. For information on supported distributions, breaking changes, and limitations, see the previous [7.2.4-12 release notes](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12-10-24)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.2.4-122`\n- **Operator**: `redislabs/operator:7.2.4-12`\n- **Services Rigger**: `redislabs/k8s-controller:7.2.4-12.`"},{"id":"openshift-images","title":"OpenShift images","role":"content","text":"- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.2.4-122.rhel8-openshift`\n    (or `redislabs/redis-enterprise:7.2.4-118.rhel7-openshift` if upgrading from RHEL 7)\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.2.4-12`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.2.4-12`"},{"id":"olm-bundle","title":"OLM bundle","role":"content","text":"**Redis Enterprise operator bundle** : `v7.2.4-12.35`"},{"id":"security","title":"Security","role":"security","text":"For a list of fixes related to CVEs, see the [Redis Enterprise 7.2.4-122 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases)."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-2-4-releases/7-2-4-15-october2025","title":"Redis Enterprise for Kubernetes 7.2.4-15 (October 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-15-october2025/","summary":"This is a maintenance release to support Redis Enterprise Software version 7.2.4-144.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"d843c1e846357534eb1cec0096526e3d7d6267aee1b28d8bb7307e7ed5a5d1aa","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support Redis Enterprise Software version 7.2.4-144. For information on supported distributions, breaking changes, and limitations, see the previous [7.2.4-15 release notes](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-15-sep25)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.2.4-144`\n- **Operator**: `redislabs/operator:7.2.4-15`\n- **Services Rigger**: `redislabs/k8s-controller:7.2.4-15`"},{"id":"openshift-images","title":"OpenShift images","role":"content","text":"- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.2.4-144.rhel8-openshift`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.2.4-15`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.2.4-15`"},{"id":"olm-bundle","title":"OLM bundle","role":"content","text":"**Redis Enterprise operator bundle** : `v7.2.4-15.41`"},{"id":"security","title":"Security","role":"security","text":"For a list of fixes related to CVEs, see the [Redis Enterprise 7.2.4 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases)."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-2-4-releases/7-2-4-15-sep25","title":"Redis Enterprise for Kubernetes 7.2.4-15 (September 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-15-sep25/","summary":"This is a maintenance release to support Redis Enterprise Software version 7.2.4-138. RHEL7 support has been removed.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"488eaec46ee087fbc649bb99eb950afd6d07743afba3fde1242e98ffeede7e8b","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support Redis Enterprise Software version 7.2.4-138. For information on supported distributions, breaking changes, and limitations, see the previous [7.2.4-12 release notes](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12-june25)."},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"**RHEL7 support removed**: Redis Enterprise Software 7.2.4-138 removes support for RHEL7. RHEL7-based OpenShift images are no longer available. Migrate to RHEL8-based images before upgrading."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.2.4-138`\n- **Operator**: `redislabs/operator:7.2.4-15`\n- **Services Rigger**: `redislabs/k8s-controller:7.2.4-15`"},{"id":"openshift-images","title":"OpenShift images","role":"content","text":"- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.2.4-138.rhel8-openshift`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.2.4-15`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.2.4-15`"},{"id":"olm-bundle","title":"OLM bundle","role":"content","text":"**Redis Enterprise operator bundle** : `v7.2.4-15.1`"},{"id":"security","title":"Security","role":"security","text":"For a list of fixes related to CVEs, see the [Redis Enterprise 7.2.4-138 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-138#security)."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-2-4-releases/7-2-4-16-feb26","title":"Redis Enterprise for Kubernetes 7.2.4-16 (February 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-16-feb26/","summary":"This is a maintenance release to support Redis Enterprise Software version 7.2.4-153.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"6f37b2e5b89b0b00e52ed9dcf05ce1c5178e10e0839388336860b91dbe13feab","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support Redis Enterprise Software version 7.2.4-153. For information on supported distributions, breaking changes, and limitations, see the previous [7.2.4-15 release notes](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-15-sep25)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.2.4-153`\n- **Operator**: `redislabs/operator:7.2.4-16`\n- **Services Rigger**: `redislabs/k8s-controller:7.2.4-16`"},{"id":"openshift-images","title":"OpenShift images","role":"content","text":"- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.2.4-153.rhel8-openshift`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.2.4-16`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.2.4-16`"},{"id":"olm-bundle","title":"OLM bundle","role":"content","text":"**Redis Enterprise operator bundle** : `v7.2.4-16.2`"},{"id":"security","title":"Security","role":"security","text":"For a list of fixes related to CVEs, see the [Redis Enterprise 7.2.4 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases)."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-2-4-releases/7-2-4-2","title":"Redis Enterprise for Kubernetes release notes 7.2.4-2 (Aug 2023)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-2/","summary":"The primary purpose this release is supporting Redis Enterprise 7.2.4.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"d22663a47802304e31f49d4dbf6c73ae3ec28152724b0bba1a6cf19cc09113f6","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"The primary purpose this release is supporting [Redis Enterprise 7.2.4](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52), which is a major Redis Enterprise Software update. As such, we reduced the scope of changes to support the underlying change of RS 7.2 and a few significant fixes.\n\nThe following are the notable changes:\n\n- Support for [Redis Enterprise 7.2.4](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52)\n\n    - New major version of Redis, 7.2\n    - [Changes to modules and cluster recovery](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52#cluster-recovery-with-manually-uploaded-modules)\n\n> [!WARNING]\n> Redis Enterprise Software 7.2.4-52 includes several issues that also effect Redis Enterprise for Kubernetes and are not listed below. Review the Redis Enterprise Software 7.2.4 [breaking changes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52#breaking-changes) and [known limitations.](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52#known-limitations) \n\n- New default engine, Speedb, for Auto Tiering (formerly Redis on Flash)\n\n- Changes to [supported Kubernetes distributions](#supported-distributions)"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"enhancements","title":"Enhancements","role":"content","text":"- Support for [Redis Enterprise 7.2.4](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52) (RED-73681)\n- New default engine, Speedb, for Auto Tiering (formerly Redis on Flash) (RED-90850)\n- Security context hardening (RED-98447)"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- Fixed: Services or routes from the Active-Active setup might not be removed (RED-77752)\n- Fixed: Admission controller may not alert when patching the REAADB with missing secret or RERC (RED-104463)"},{"id":"api-changes","title":"API changes","role":"content","text":"The following fields were added to the RedisEnterpriseCluster (REC) custom resource definition (CRD):\n\n- Added `status.BundledDatabaseRedisVersions.major` as a Boolean field indicating whether the Redis version is major.\n- Added `spec.RedisOnFlashSpec.bigStoreDriver` to indicate the storage engine used for Auto Tiering (formerly Redis on Flash)."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":"For a list of fixes related to CVEs, see the [Redis Enterprise 7.2.4-52 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52#security)."},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"The following changes included in this release affect the upgrade process. Please read carefully before upgrading to 7.2.4-2.\n\n#### ValidatingWebhookConfiguration\n\nVersions 6.4.2-4 and later include a new `ValidatingWebhookConfiguration` resource to replace the `redb-admission` webhook resource. To use releases 6.4.2-4 or later, delete the old webhook resource and apply the new file. See [upgrade Redis cluster](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#reapply-webhook) for instructions.\n\n#### OpenShift SCC\n\nVersions 6.4.2-6 and later include a new SCC (`redis-enterprise-scc-v2`) that you need to bind to your service account before upgrading. OpenShift clusters running version 6.2.12 or earlier upgrading to version 6.2.18 or later might get stuck if you skip this step. See [upgrade a Redis Enterprise cluster (REC)](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#before-upgrading) for instructions."},{"id":"deprecations","title":"Deprecations","role":"content","text":"- Gesher (advanced admission control configuration support) previously deprecated, is no longer supported.\n- Deprecated `spec.RedisOnFlashSpec.flashStorageEngine` field for the RedisEnterpriseCluster. Use `bigStoreDriver` field to indicate storage engines going forward."},{"id":"supported-distributions","title":"Supported distributions","role":"compatibility","text":"The following table shows supported distributions at the time of this release. You can also find this list in [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions).\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – This distribution is supported for this version of Redis Enterprise Software for Kubernetes.\n\n<span title=\"Warning icon\">&#x26A0;&#xFE0F;</span> Deprecated – This distribution is still supported for this version of Redis Enterprise Software for Kubernetes, but support will be removed in a future release.\n\n<span title=\"X icon\">&#x274c;</span> End of life – Support for this distribution ended.\n\nAny distribution not listed below is not supported for production workloads.\n\n| **Kubernetes version** | **1.22** | **1.23** | **1.24** | **1.25** | **1.26** | **1.27** |\n|---|---|---|---|---|---|---|\n| Community Kubernetes | <span title=\"X icon\">&#x274c;</span> | <span title=\"X icon\">&#x274c;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |\n| Amazon EKS | <span title=\"End of life\">&#x274c;</span> | <span title=\"Deprecation warning\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Azure AKS |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"X icon\">&#x274c;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |\n| Google GKE | <span title=\"X icon\">&#x274c;</span> | <span title=\"Warning icon\">&#x26A0;&#xFE0F;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |\n| Rancher 2.6 | <span title=\"X icon\">&#x274c;</span> | <span title=\"X icon\">&#x274c;</span> | <span title=\"Warning icon\">&#x26A0;&#xFE0F;</span> |  |  |  |\n| Rancher 2.7 |  | <span title=\"Warning icon\">&#x26A0;&#xFE0F;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |  |  |\n| VMware TKG 1.6 | <span title=\"Warning icon\">&#x26A0;&#xFE0F;</span> | <span title=\"Warning icon\">&#x26A0;&#xFE0F;</span> |  |  |  |  |\n| **OpenShift version** | **4.9** | **4.10** | **4.11** | **4.12** | **4.13** |  |\n|  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Warning icon\">&#x26A0;&#xFE0F;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |  |\n| **VMware TKGI version** | **1.13** | **1.14** | **1.15** | **1.16** |  |  |\n|  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Warning icon\">&#x26A0;&#xFE0F;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |  |  |"},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.2.4-52`\n- **Operator**: `redislabs/operator:7.2.4-2`\n- **Services Rigger**: `redislabs/k8s-controller:7.2.4-2.`\n\n#### OpenShift images\n\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.2.4-52.rhel8-openshift`\n    (or `redislabs/redis-enterprise:7.2.4-52.rhel7-openshift` if upgrading from RHEL 7)\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.2.4-2`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.2.4-2`\n\n#### OLM bundle\n\n**Redis Enterprise operator bundle** : `v7.2.4-2`\n\n> [!NOTE]\n> It is not possible to upgrade your OLM deployment to version 7.2.4 from version 6.2.4-8 (November release). Uninstall 6.2.4-8 and perform a fresh install of 7.2.4."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"The limitations below are specific to the Redis Enterprise for Kubernetes product. To view limitations for the supported Redis Enterprise Software version, see the [7.2.4 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases#known-limitations)."},{"id":"new-limitations","title":"New limitations","role":"content","text":"- **REDBs created with versions before 6.2.10-4 will automatically upgrade modules when the cluster is upgraded (RED-106834)**\n\n- **Cannot upgrade or recover clusters if there are databases with manually uploaded modules (RED-107711 RED-106872)** Contact support.\n\n> [!WARNING]\n> In version 7.2.4, old module versions and manually uploaded modules are not persisted. If databases are not upgraded after cluster upgrade, and require cluster recovery afterwards, you'll need to contact Redis support. This issue will be fixed in the next maintenance release by moving the stored location of the modules.\n\n- **When modifying the database suffix for an Active-Active database, while the service-rigger is in a terminating state, the services-rigger will detele and create the ingress or route resources in a loop (RED-107687)** Wait until the services- rigger pod has finished to terminate it."},{"id":"existing-limitations","title":"Existing limitations","role":"content","text":"- **REAADB changes might fail with \"gateway timeout\" errors, mostly on OpenShift (RED-103048)** Retry the operation.\n\n- **Misleading error appears when a service creation has failed due to service already existing \"`provided IP is already allocated`\" (RED-100669)** Delete the service manually.\n\n- **Existing OpenShift route during REC creation might prevent REC from starting (RED-100668)** Delete the route manually.\n\n- **Creating two databases with the same name directly on Redis Enterprise software will cause the service to be deleted and the database will not be available (RED-99997)** Avoid duplicating database names. Database creation via K8s has validation in place to prevent this.\n\n- **Installing the operator bundle produces warning: `Warning: would violate PodSecurity \"restricted: v1.24\"` (RED-97381)** Ignore the warning. This issue is documented as benign on official Red Hat documentation.\n\n- **RERC resources must have a unique name (RED-96302)** The string \"rec-name\"/\"rec-namespace\" must be different from all other participating clusters in the Active-Active database.\n\n- **Admission is not blocking REAADB with `shardCount` which exceeds license quota (RED-96301)** Fix the problems with the REAADB and reapply.\n\n- **Active-Active controller only supports global database options. Configuration specific to location is not supported (RED-86490)**\n\n- **Active-Active setup removal might keep services or routes undeleted (RED-77752)** Delete services or routes manually if you encounter this problem.\n\n- **`autoUpgrade` set to `true` can cause unexpected bdb upgrades when `redisUpgradePolicy` is set to `true` (RED-72351)** Contact support if your deployment is impacted.\n\n- **Following the previous quick start guide version causes issues with creating an REDB due to unrecognized memory field name (RED-69515)** The workaround is to use the newer (current) revision of Deploy Redis Enterprise Software for Kubernetes.\n\n- **PVC size issues when using decimal value in spec (RED-62132)** Make sure you use integer values for the PVC size.\n\n- **REC might report error states on initial startup (RED-61707)** There is no workaround at this time except to ignore the errors.\n\n- **Hashicorp Vault integration - no support for Gesher (RED-55080)** There is no workaround for this issue. Gesher support has been deprecated.\n\n- **REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED-47254)** When REC clusters are deployed on Kubernetes clusters without synchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes.\n\n- **Deleting an OpenShift project with an REC deployed may hang (RED-47192)** When an REC cluster is deployed in a project (namespace) and has REDB resources, the REDB resources must be deleted first before the REC can be deleted. Therefore, until the REDB resources are deleted, the project deletion will hang. The fix is to delete the REDB resources first and the REC second. Then, you can delete the project.\n\n- **Clusters must be named 'rec' in OLM-based deployments (RED-39825)** In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) are bound to a specific service account name (namely, \"rec\"). The workaround is to name the cluster \"rec\".\n\n- **Readiness probe incorrect on failures (RED-39300)** STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure.\n\n- **Internal DNS and Kubernetes DNS may have conflicts (RED-37462)** DNS conflicts are possible between the cluster `mdns_server` and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names.\n\n- **K8s-based 5.4.10 clusters seem to negatively affect existing 5.4.6 clusters (RED-37233)** Upgrade clusters to latest version.\n\n- **Node CPU usage is reported instead of pod CPU usage (RED-36884)** In Kubernetes, the reported node CPU usage is the usage of the Kubernetes worker node hosting the REC pod.\n\n- **An unreachable cluster has status running (RED-32805)** When a cluster is in an unreachable state, the state remains `running` instead of triggering an error.\n\n- **Long cluster names cause routes to be rejected  (RED-25871)** A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name exceeds 63 characters. The workaround is to limit the cluster name to 20 characters or fewer.\n\n- **Cluster CR (REC) errors are not reported after invalid updates (RED-25542)** A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-2-4-releases/7-2-4-7","title":"Redis Enterprise for Kubernetes release notes 7.2.4-7 (Oct 2023)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-7/","summary":"This is a minor maintenance release with a few enhancements/fixes on the Kubernetes product, and a newer Redis Enterprise container version.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"f0a16b23c6f5d5d9f1bc75bfe01942dc13268bbdf2cc6f41e821b20091e536cc","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a minor maintenance release with a few enhancement/fixes on the Kubernetes product, and a newer Redis Enterprise container version.\n\nThe following are the notable changes: \n- Customers who are on RHEL 7 based images and who are already using this version of 7.2 can migrate to RHEL 8 based images when they upgrade.\n    - Previously, you could not migrate a cluster to newer OS version.\n    - All customers should plan to move to RHEL 8 based images. The next major version of Redis Enterprise will not support RHEL 7.\n    - This process requires specific pre-requisites and actions. Updated guides are being developed and will be available soon.\n- Added VMware TKG 2.1 and 2.2 to the [Supported distributions](#supported-distributions) table."},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"enhancements","title":"Enhancements","role":"content","text":"- Support for migration from RHEL7 images to RHEL8 during upgrade (RED-106785)\n- Support for Redis Enterprise Software version 7.2.4-64 (RED-108832)"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- Fixed cluster recovery flow when non default modules are used (RED-107711 RED-106872)"},{"id":"version-changes","title":"Version changes","role":"compatibility","text":"For a list of fixes related to CVEs, see the [Redis Enterprise 7.2.4-64 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-64#security)."},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"The following changes included in this release affect the upgrade process. Please read carefully before upgrading to 7.2.4-7.\n\n#### ValidatingWebhookConfiguration\n\nVersions 6.4.2-4 and later include a new `ValidatingWebhookConfiguration` resource to replace the `redb-admission` webhook resource. To use releases 6.4.2-4 or later, delete the old webhook resource and apply the new file. See [upgrade Redis cluster](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#reapply-webhook) for instructions.\n\n#### OpenShift SCC\n\nVersions 6.4.2-6 and later include a new SCC (`redis-enterprise-scc-v2`) that you need to bind to your service account before upgrading. OpenShift clusters running version 6.2.12 or earlier upgrading to version 6.2.18 or later might get stuck if you skip this step. See [upgrade a Redis Enterprise cluster (REC)](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#before-upgrading) for instructions."},{"id":"upcoming-changes","title":"Upcoming changes","role":"content","text":"- A future release of Redis Enterprise will remove support for RHEL7. We recommend migrating to RHEL8."},{"id":"supported-distributions","title":"Supported distributions","role":"compatibility","text":"The following table shows supported distributions at the time of this release. You can also find this list in [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions).\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – This distribution is supported for this version of Redis Enterprise Software for Kubernetes.\n\n<span title=\"Warning icon\">&#x26A0;&#xFE0F;</span> Deprecated – This distribution is still supported for this version of Redis Enterprise Software for Kubernetes, but support will be removed in a future release.\n\n<span title=\"X icon\">&#x274c;</span> End of life – Support for this distribution ended.\n\nAny distribution not listed below is not supported for production workloads.\n\n| **Kubernetes version** | **1.22** | **1.23** | **1.24** | **1.25** | **1.26** | **1.27** |\n|---|---|---|---|---|---|---|\n| Community Kubernetes | <span title=\"X icon\">&#x274c;</span> | <span title=\"X icon\">&#x274c;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |\n| Amazon EKS | <span title=\"End of life\">&#x274c;</span> | <span title=\"Deprecation warning\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Azure AKS |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"X icon\">&#x274c;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |\n| Google GKE | <span title=\"X icon\">&#x274c;</span> | <span title=\"Warning icon\">&#x26A0;&#xFE0F;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |\n| Rancher 2.6 | <span title=\"X icon\">&#x274c;</span> | <span title=\"X icon\">&#x274c;</span> | <span title=\"Warning icon\">&#x26A0;&#xFE0F;</span> |  |  |  |\n| Rancher 2.7 |  | <span title=\"Warning icon\">&#x26A0;&#xFE0F;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |  |  |\n| VMware TKG 1.6 | <span title=\"Warning icon\">&#x26A0;&#xFE0F;</span> | <span title=\"Warning icon\">&#x26A0;&#xFE0F;</span> |  |  |  |  |\n| VMware TKG 2.1 | <span title=\"Warning icon\">&#x26A0;&#xFE0F;</span> | <span title=\"Warning icon\">&#x26A0;&#xFE0F;</span> | <span title=\"Check mark icon\">&#x2705;</span> |  |  |  |\n| VMware TKG 2.2 |  | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |  |  |\n| **OpenShift version** | **4.9** | **4.10** | **4.11** | **4.12** | **4.13** |  |\n|  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Warning icon\">&#x26A0;&#xFE0F;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |  |\n| **VMware TKGI version** | **1.13** | **1.14** | **1.15** | **1.16** |  |  |\n|  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Warning icon\">&#x26A0;&#xFE0F;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |  |  |"},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.2.4-64`\n- **Operator**: `redislabs/operator:7.2.4-7`\n- **Services Rigger**: `redislabs/k8s-controller:7.2.4-7.`\n\n#### OpenShift images\n\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.2.4-64.rhel8-openshift`\n    (or `redislabs/redis-enterprise:7.2.4-64.rhel7-openshift` if upgrading from RHEL 7)\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.2.4-7`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.2.4-7`\n\n#### OLM bundle\n\n**Redis Enterprise operator bundle** : `v7.2.4-7`\n\n> [!NOTE]\n> It is not possible to upgrade your OLM deployment to version 7.2.4 from version 6.2.4-8 (November release). Uninstall 6.2.4-8 and perform a fresh install of 7.2.4."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"The limitations below are specific to the Redis Enterprise for Kubernetes product. To view limitations for the supported Redis Enterprise Software version, see the [7.2.4 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases#known-limitations)."},{"id":"existing-limitations","title":"Existing limitations","role":"content","text":"- **REDBs created with versions before 6.2.10-4 will automatically upgrade modules when the cluster is upgraded (RED-106834)**\n\n- **When modifying the database suffix for an Active-Active database, while the service-rigger is in a terminating state, the services-rigger will detele and create the ingress or route resources in a loop (RED-107687)** Wait until the services- rigger pod has finished to terminate it.\n\n- **REAADB changes might fail with \"gateway timeout\" errors, mostly on OpenShift (RED-103048)** Retry the operation.\n\n- **Misleading error appears when a service creation has failed due to service already existing \"`provided IP is already allocated`\" (RED-100669)** Delete the service manually.\n\n- **Existing OpenShift route during REC creation might prevent REC from starting (RED-100668)** Delete the route manually.\n\n- **Creating two databases with the same name directly on Redis Enterprise software will cause the service to be deleted and the database will not be available (RED-99997)** Avoid duplicating database names. Database creation via K8s has validation in place to prevent this.\n\n- **Installing the operator bundle produces warning: `Warning: would violate PodSecurity \"restricted: v1.24\"` (RED-97381)** Ignore the warning. This issue is documented as benign on official Red Hat documentation.\n\n- **RERC resources must have a unique name (RED-96302)** The string \"rec-name\"/\"rec-namespace\" must be different from all other participating clusters in the Active-Active database.\n\n- **Admission is not blocking REAADB with `shardCount` which exceeds license quota (RED-96301)** Fix the problems with the REAADB and reapply.\n\n- **Active-Active controller only supports global database options. Configuration specific to location is not supported (RED-86490)**\n\n- **Active-Active setup removal might keep services or routes undeleted (RED-77752)** Delete services or routes manually if you encounter this problem.\n\n- **`autoUpgrade` set to `true` can cause unexpected bdb upgrades when `redisUpgradePolicy` is set to `true` (RED-72351)** Contact support if your deployment is impacted.\n\n- **Following the previous quick start guide version causes issues with creating an REDB due to unrecognized memory field name (RED-69515)** The workaround is to use the newer (current) revision of Deploy Redis Enterprise Software for Kubernetes.\n\n- **PVC size issues when using decimal value in spec (RED-62132)** Make sure you use integer values for the PVC size.\n\n- **REC might report error states on initial startup (RED-61707)** There is no workaround at this time except to ignore the errors.\n\n- **Hashicorp Vault integration - no support for Gesher (RED-55080)** There is no workaround for this issue. Gesher support has been deprecated.\n\n- **REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED-47254)** When REC clusters are deployed on Kubernetes clusters without synchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes.\n\n- **Deleting an OpenShift project with an REC deployed may hang (RED-47192)** When an REC cluster is deployed in a project (namespace) and has REDB resources, the REDB resources must be deleted first before the REC can be deleted. Therefore, until the REDB resources are deleted, the project deletion will hang. The fix is to delete the REDB resources first and the REC second. Then, you can delete the project.\n\n- **Clusters must be named 'rec' in OLM-based deployments (RED-39825)** In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) are bound to a specific service account name (namely, \"rec\"). The workaround is to name the cluster \"rec\".\n\n- **Readiness probe incorrect on failures (RED-39300)** STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure.\n\n- **Internal DNS and Kubernetes DNS may have conflicts (RED-37462)** DNS conflicts are possible between the cluster `mdns_server` and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names.\n\n- **K8s-based 5.4.10 clusters seem to negatively affect existing 5.4.6 clusters (RED-37233)** Upgrade clusters to latest version.\n\n- **Node CPU usage is reported instead of pod CPU usage (RED-36884)** In Kubernetes, the reported node CPU usage is the usage of the Kubernetes worker node hosting the REC pod.\n\n- **An unreachable cluster has status running (RED-32805)** When a cluster is in an unreachable state, the state remains `running` instead of triggering an error.\n\n- **Long cluster names cause routes to be rejected  (RED-25871)** A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name exceeds 63 characters. The workaround is to limit the cluster name to 20 characters or fewer.\n\n- **Cluster CR (REC) errors are not reported after invalid updates (RED-25542)** A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-2-4-releases","title":"7.2.4 release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/","summary":"Support for Redis Enterprise 7.2.4, platform support updates, and bug fixes.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","children":[{"id":"operate/kubernetes/release-notes/7-2-4-releases/7-2-4-16-feb26","summary":"This is a maintenance release to support Redis Enterprise Software version 7.2.4-153.","title":"Redis Enterprise for Kubernetes 7.2.4-16 (February 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-16-feb26/"},{"id":"operate/kubernetes/release-notes/7-2-4-releases/7-2-4-15-october2025","summary":"This is a maintenance release to support Redis Enterprise Software version 7.2.4-144.","title":"Redis Enterprise for Kubernetes 7.2.4-15 (October 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-15-october2025/"},{"id":"operate/kubernetes/release-notes/7-2-4-releases/7-2-4-15-sep25","summary":"This is a maintenance release to support Redis Enterprise Software version 7.2.4-138. RHEL7 support has been removed.","title":"Redis Enterprise for Kubernetes 7.2.4-15 (September 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-15-sep25/"},{"id":"operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12-june25","summary":"This is a maintenance release to support Redis Enterprise Software version 7.2.4-130.","title":"Redis Enterprise for Kubernetes 7.2.4-12 (June 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12-june25/"},{"id":"operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12-mar25","summary":"This is a maintenance release to support Redis Enterprise Software version 7.2.4-118.","title":"Redis Enterprise for Kubernetes 7.2.4-12 (March 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12-mar25/"},{"id":"operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12-jan25","summary":"This is a maintenance release to support Redis Enterprise Software version 7.2.4-118.","title":"Redis Enterprise for Kubernetes 7.2.4-12 (January 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12-jan25/"},{"id":"operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12-10-24","summary":"This is a maintenance release to support Redis Enterprise Software version 7.2.4-117.","title":"Redis Enterprise for Kubernetes 7.2.4-12 (October 2024) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12-10-24/"},{"id":"operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12-08-2024","summary":"This is a maintenance release to support Redis Enterprise Software version 7.2.4-109.","title":"Redis Enterprise for Kubernetes 7.2.4-12 (August 2024) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12-08-2024/"},{"id":"operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12-03-24","summary":"This is a maintenance release to support Redis Enterprise Software version 7.2.4-105.","title":"Redis Enterprise for Kubernetes 7.2.4-12 (March 2024) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12-03-24/"},{"id":"operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12","summary":"This is a maintenance release with a few enhancements/fixes on the Kubernetes product and a new Redis Enterprise container version.","title":"Redis Enterprise for Kubernetes 7.2.4-12 (Dec 2023) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12/"},{"id":"operate/kubernetes/release-notes/7-2-4-releases/7-2-4-7","summary":"This is a minor maintenance release with a few enhancements/fixes on the Kubernetes product, and a newer Redis Enterprise container version.","title":"Redis Enterprise for Kubernetes release notes 7.2.4-7 (Oct 2023)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-7/"},{"id":"operate/kubernetes/release-notes/7-2-4-releases/7-2-4-2","summary":"The primary purpose this release is supporting Redis Enterprise 7.2.4.","title":"Redis Enterprise for Kubernetes release notes 7.2.4-2 (Aug 2023)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-2/"}],"page_type":"content","content_hash":"f35f3968486ee0f9ab7175b92f2fc8f375d5eaf4bae91c67e493feea6b88ac2f","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"The primary purpose this release is supporting [Redis Enterprise 7.2.4](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52), which is a major Redis Enterprise Software update. As such, we reduced the scope of changes to support the underlying change of RS 7.2 and a few significant fixes.\n\nThe following are the notable changes:\n\n- Support for [Redis Enterprise 7.2.4](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52)\n\n    - New major version of Redis, 7.2\n    - [Changes to modules and cluster recovery](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52#cluster-recovery-with-manually-uploaded-modules)\n\n> [!WARNING]\n> Redis Enterprise Software 7.2.4-52 includes several issues that also effect Redis Enterprise for Kubernetes and are not listed below. Review the Redis Enterprise Software 7.2.4 [breaking changes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52#breaking-changes) and [known limitations.](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52#known-limitations)"},{"id":"detailed-release-notes","title":"Detailed release notes","role":"content","text":"| Version (Release date) | Major changes |\n| --- | --- |\n| [7.2.4-16 (February 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-16-feb26/) | This is a maintenance release to support Redis Enterprise Software version 7.2.4-153. |\n| [7.2.4-15 (October 2025)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-15-october2025/) | This is a maintenance release to support Redis Enterprise Software version 7.2.4-144. |\n| [7.2.4-15 (September 2025)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-15-sep25/) | This is a maintenance release to support Redis Enterprise Software version 7.2.4-138. RHEL7 support has been removed. |\n| [7.2.4-12 (June 2025)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12-june25/) | This is a maintenance release to support Redis Enterprise Software version 7.2.4-130. |\n| [7.2.4-12 (March 2025)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12-mar25/) | This is a maintenance release to support Redis Enterprise Software version 7.2.4-118. |\n| [7.2.4-12 (January 2025)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12-jan25/) | This is a maintenance release to support Redis Enterprise Software version 7.2.4-118. |\n| [7.2.4-12 (October 2024)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12-10-24/) | This is a maintenance release to support Redis Enterprise Software version 7.2.4-117. |\n| [7.2.4-12 (August 2024)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12-08-2024/) | This is a maintenance release to support Redis Enterprise Software version 7.2.4-109. |\n| [7.2.4-12 (March 2024)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12-03-24/) | This is a maintenance release to support Redis Enterprise Software version 7.2.4-105. |\n| [7.2.4-12 (Dec 2023)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-12/) | This is a maintenance release with a few enhancements/fixes on the Kubernetes product and a new Redis Enterprise container version. |\n| [7.2.4-7 (Oct 2023)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-7/) | This is a minor maintenance release with a few enhancements/fixes on the Kubernetes product, and a newer Redis Enterprise container version. |\n| [7.2.4-2 (Aug 2023)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/7-2-4-2/) | The primary purpose this release is supporting Redis Enterprise 7.2.4. |"},{"id":"version-changes","title":"Version changes","role":"compatibility","text":"For a list of fixes related to CVEs, see the [Redis Enterprise 7.2.4 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases)."},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"The following changes included in this release affect the upgrade process. Please read carefully before upgrading to 7.2.4-2.\n\n#### ValidatingWebhookConfiguration\n\nVersions 6.4.2-4 and later include a new `ValidatingWebhookConfiguration` resource to replace the `redb-admission` webhook resource. To use releases 6.4.2-4 or later, delete the old webhook resource and apply the new file. See [upgrade Redis cluster](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#reapply-webhook) for instructions.\n\n#### OpenShift SCC\n\nVersions 6.4.2-6 and later include a new SCC (`redis-enterprise-scc-v2`) that you need to bind to your service account before upgrading. OpenShift clusters running version 6.2.12 or earlier upgrading to version 6.2.18 or later might get stuck if you skip this step. See [upgrade a Redis Enterprise cluster (REC)](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#before-upgrading) for instructions."},{"id":"deprecations","title":"Deprecations","role":"content","text":"- Gesher (advanced admission control configuration support) previously deprecated, is no longer supported.\n- Deprecated `spec.RedisOnFlashSpec.flashStorageEngine` field for the RedisEnterpriseCluster. Use `bigStoreDriver` field to indicate storage engines going forward."},{"id":"supported-distributions","title":"Supported distributions","role":"compatibility","text":"The following table shows supported distributions at the time of this release. You can also find this list in [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions).\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – This distribution is supported for this version of Redis Enterprise Software for Kubernetes.\n\n<span title=\"Warning icon\">&#x26A0;&#xFE0F;</span> Deprecated – This distribution is still supported for this version of Redis Enterprise Software for Kubernetes, but support will be removed in a future release.\n\n<span title=\"X icon\">&#x274c;</span> End of life – Support for this distribution ended.\n\nAny distribution not listed below is not supported for production workloads.\n\n| **Kubernetes version** | **1.22** | **1.23** | **1.24** | **1.25** | **1.26** | **1.27** |\n|---|---|---|---|---|---|---|\n| Community Kubernetes | <span title=\"X icon\">&#x274c;</span> | <span title=\"X icon\">&#x274c;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |\n| Amazon EKS | <span title=\"End of life\">&#x274c;</span> | <span title=\"Deprecation warning\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Azure AKS |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"X icon\">&#x274c;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |\n| Google GKE | <span title=\"X icon\">&#x274c;</span> | <span title=\"Warning icon\">&#x26A0;&#xFE0F;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |\n| Rancher 2.6 | <span title=\"X icon\">&#x274c;</span> | <span title=\"X icon\">&#x274c;</span> | <span title=\"Warning icon\">&#x26A0;&#xFE0F;</span> |  |  |  |\n| Rancher 2.7 |  | <span title=\"Warning icon\">&#x26A0;&#xFE0F;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |  |  |\n| VMware TKG 1.6 | <span title=\"Warning icon\">&#x26A0;&#xFE0F;</span> | <span title=\"Warning icon\">&#x26A0;&#xFE0F;</span> |  |  |  |  |\n| **OpenShift version** | **4.9** | **4.10** | **4.11** | **4.12** | **4.13** |  |\n|  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Warning icon\">&#x26A0;&#xFE0F;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |  |\n| **VMware TKGI version** | **1.13** | **1.14** | **1.15** | **1.16** |  |  |\n|  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Warning icon\">&#x26A0;&#xFE0F;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |  |  |"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"The limitations below are specific to the Redis Enterprise for Kubernetes product. To view limitations for the supported Redis Enterprise Software version, see the [7.2.4 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases#known-limitations).\n\n> [!WARNING]\n> Pay particular attention to the [cluster recovery limitation](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52#cluster-recovery-with-manually-uploaded-modules) if you use manually uploaded modules.\n\n- **Cannot upgrade or recover clusters if there are databases with manually uploaded modules (RED-107711 RED-106872)** Contact support.\n\n- **REDBs created with versions before 6.2.10-4 will automatically upgrade modules when the cluster is upgraded (RED-106834)**\n\n- **When modifying the database suffix for an Active-Active database, while the service-rigger is in a terminating state, the services-rigger will detele and create the ingress or route resources in a loop (RED-107687)** Wait until the services- rigger pod has finished to terminate it.\n\n- **REAADB changes might fail with \"gateway timeout\" errors, mostly on OpenShift (RED-103048)** Retry the operation.\n\n- **Misleading error appears when a service creation has failed due to service already existing \"`provided IP is already allocated`\" (RED-100669)** Delete the service manually.\n\n- **Existing OpenShift route during REC creation might prevent REC from starting (RED-100668)** Delete the route manually.\n\n- **Creating two databases with the same name directly on Redis Enterprise software will cause the service to be deleted and the database will not be available (RED-99997)** Avoid duplicating database names. Database creation via K8s has validation in place to prevent this.\n\n- **Installing the operator bundle produces warning: `Warning: would violate PodSecurity \"restricted: v1.24\"` (RED-97381)** Ignore the warning. This issue is documented as benign on official Red Hat documentation.\n\n- **RERC resources must have a unique name (RED-96302)** The string \"rec-name\"/\"rec-namespace\" must be different from all other participating clusters in the Active-Active database.\n\n- **Admission is not blocking REAADB with `shardCount` which exceeds license quota (RED-96301)** Fix the problems with the REAADB and reapply.\n\n- **Active-Active controller only supports global database options. Configuration specific to location is not supported (RED-86490)**\n\n- **Active-Active setup removal might keep services or routes undeleted (RED-77752)** Delete services or routes manually if you encounter this problem.\n\n- **`autoUpgrade` set to `true` can cause unexpected bdb upgrades when `redisUpgradePolicy` is set to `true` (RED-72351)** Contact support if your deployment is impacted.\n\n- **Following the previous quick start guide version causes issues with creating an REDB due to unrecognized memory field name (RED-69515)** The workaround is to use the newer (current) revision of Deploy Redis Enterprise Software for Kubernetes.\n\n- **PVC size issues when using decimal value in spec (RED-62132)** Make sure you use integer values for the PVC size.\n\n- **REC might report error states on initial startup (RED-61707)** There is no workaround at this time except to ignore the errors.\n\n- **Hashicorp Vault integration - no support for Gesher (RED-55080)** There is no workaround for this issue. Gesher support has been deprecated.\n\n- **REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED-47254)** When REC clusters are deployed on Kubernetes clusters without synchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes.\n\n- **Deleting an OpenShift project with an REC deployed may hang (RED-47192)** When an REC cluster is deployed in a project (namespace) and has REDB resources, the REDB resources must be deleted first before the REC can be deleted. Therefore, until the REDB resources are deleted, the project deletion will hang. The fix is to delete the REDB resources first and the REC second. Then, you can delete the project.\n\n- **Clusters must be named 'rec' in OLM-based deployments (RED-39825)** In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) are bound to a specific service account name (namely, \"rec\"). The workaround is to name the cluster \"rec\".\n\n- **Readiness probe incorrect on failures (RED-39300)** STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure.\n\n- **Internal DNS and Kubernetes DNS may have conflicts (RED-37462)** DNS conflicts are possible between the cluster `mdns_server` and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names.\n\n- **K8s-based 5.4.10 clusters seem to negatively affect existing 5.4.6 clusters (RED-37233)** Upgrade clusters to latest version.\n\n- **Node CPU usage is reported instead of pod CPU usage (RED-36884)** In Kubernetes, the reported node CPU usage is the usage of the Kubernetes worker node hosting the REC pod.\n\n- **An unreachable cluster has status running (RED-32805)** When a cluster is in an unreachable state, the state remains `running` instead of triggering an error.\n\n- **Long cluster names cause routes to be rejected  (RED-25871)** A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name exceeds 63 characters. The workaround is to limit the cluster name to 20 characters or fewer.\n\n- **Cluster CR (REC) errors are not reported after invalid updates (RED-25542)** A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-22-0-releases/7-22-0-11-june2025","title":"Redis Enterprise for Kubernetes 7.22.0-11 (June 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-0-releases/7-22-0-11-june2025/","summary":"This is a maintenance release to support Redis Enterprise Software version 7.22.0-95.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"055253d1ef32274c067c1bef3a5d3308a0af222c469759385c0bd8cc389984cd","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support [Redis Enterprise Software version 7.22.0-95](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases). For version changes, supported distributions, and known limitations, see the [release notes for 7.22.0-7 (April 2025)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-0-releases/7-22-0-7-april2025)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.22.0-95`\n- **Operator**: `redislabs/operator:7.22.0-11`\n- **Services Rigger**: `redislabs/k8s-controller:7.22.0-11`\n- **Call Home Client**: `redislabs/re-call-home-client:7.22.0-11`"},{"id":"openshift-downloads","title":"Openshift downloads","role":"content","text":"- **OLM operator bundle**: `v7.22.0-11.2`\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.22.0-95`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.22.0-11`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.22.0-11`\n- **Call Home Client**: `registry.connect.redhat.com/redislabs/call-home-client:7.22.0-11`"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-22-0-releases/7-22-0-15-july2025","title":"Redis Enterprise for Kubernetes 7.22.0-15 (July 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-0-releases/7-22-0-15-july2025/","summary":"Feature release with Helm chart general availability, Kubernetes 1.33 and OpenShift 4.19 support, Redis Software 7.22.0-216 support, and enhancements.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"9263aa2dcb1a2096a96bdb7b8f6ddc843187c2fbe9a6869c743b46e1c5fbbe5e","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"* Helm chart support is now generally available."},{"id":"new-in-this-release","title":"New in this release","role":"content","text":"Redis Enterprise for Kubernetes 7.22.0-15 is a feature release that includes enhancements, platform updates, and support for Redis Software 7.22.0-216."},{"id":"enhancements","title":"Enhancements","role":"content","text":"* Call home - support HTTP proxy configuration (RED-156935)\n* Helm - optimize installation time (RED-153889)\n* Add support for migrating to a Helm-based installation from a non-Helm installation (RED-113099)\n* Support upgrade using Helm (RED-153890)\n* Support any custom port for database service (different from cluster database port) (RED-156946)\n* Hardened the securityContext of init containers created when readOnlyRootFilesystem is enabled\n* Kubernetes platform updates, support for Kubernetes 1.33\n* Support for Redis Software 7.22.0-216 (RED-163025)"},{"id":"resolved-issues","title":"Resolved Issues","role":"content","text":"* Adding `servicesAnnotations` in the REC spec keeps updating the services in a loop (RED-154777, RED-158816)\n* New call home job is not created if the old one is stuck due to pod in `imagePullBackoff` (RED-157526)\n* REAADB ACL roles do not get properly assigned according to `rolesPermissions` in spec (RED-158955)\n* CVE fixes\n* `allowPrivilegeEscalation` is unset in the init container for `readOnlyRootFilesystem` (RED-163637)"},{"id":"api-changes","title":"API changes","role":"content","text":"| CRD | Field | Change | Description |\n| ----- | :---- | :---- | :---- |\n| REAADB | `spec.databaseServicePort` | Add | A custom port to be exposed by the database services. Can be modified/added/removed after REDB creation. If set, it replaces the default service port (namely, `databasePort` or `defaultRedisPort`). |\n| REC | `spec.usageMeterSpec.CallHomeClient.proxySecretName` | Add | If needed, add proxy details in secret. The name of the proxy secret in the secret, can send the following keys: `proxy-url`, `proxy-username`, `proxy-password` (the URL includes the proxy port). |\n| REDB | `spec.databaseServicePort` | Add | A custom port to be exposed by the database services. Can be modified/added/removed after REDB creation. If set, it replaces the default service port (namely, `databasePort` or `defaultRedisPort`). |"},{"id":"rhel9-based-image","title":"RHEL9-based image","role":"content","text":"As of version 7.8.2-6, Redis Enterprise images are based on Red Hat Enterprise Linux 9 (RHEL9). This means upgrades require:\n\n- [Cluster version of 7.4.2-2 or later](https://redis.io/docs/latest/operate/kubernetes/7.4.6/upgrade/).\n- Database version 7.2 or later.\n- RHEL9 compatible binaries for any modules you need.\n\nFor detailed steps, see the relevant upgrade page:\n\n- [OpenShift CLI](https://redis.io/docs/latest/operate/kubernetes/upgrade/openshift-cli)\n- [OpenShift OperatorHub](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-olm)\n- [Kubernetes](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster)"},{"id":"supported-distributions","title":"Supported distributions","role":"compatibility","text":"The following table shows supported distributions at the time of this release. You can also find this list in [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions).\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – This distribution is supported for this version of Redis Enterprise Kubernetes.\n\n<span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> Deprecated – This distribution is still supported for this version of Redis Enterprise Kubernetes, but support will be removed in a future release.\n\n<span title=\"X icon\">&#x274c;</span> End of life – Support for this distribution ended.\n\nAny distribution not listed below is not supported for production workloads.\n\n| Kubernetes version | **1.28** | **1.29** | **1.30** | **1.31** | **1.32** | **1.33** |\n|---|---|---|---|---|---|---|\n| **Community K8s** |  |  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Amazon EKS** |  |  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Azure AKS** |  |  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Google GKE** |  |  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Rancher** | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |  |\n| **OpenShift** | **4.15** | **4.16** | **4.17** | **4.18** | **4.19** |  |\n|  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |\n| **VMware TKGI** | **1.19** | **1.20** | **1.21** |  |  |  |\n|  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |  |"},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.22.0-216`\n- **Operator**: `redislabs/operator:7.22.0-15`\n- **Services Rigger**: `redislabs/k8s-controller:7.22.0-15`\n- **Call Home Client**: `redislabs/re-call-home-client:7.22.0-15`"},{"id":"openshift-downloads","title":"Openshift downloads","role":"content","text":"- **OLM operator bundle**: `7.22.0-15.0`\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.22.0-216`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.22.0-15`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.22.0-15`\n- **Call Home Client**: `registry.connect.redhat.com/redislabs/call-home-client:7.22.0-15`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [7.22.0 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-0-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-22-0-releases/7-22-0-16-august2025","title":"Redis Enterprise for Kubernetes 7.22.0-16 (August 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-0-releases/7-22-0-16-august2025/","summary":"This is a maintenance release to support Redis Enterprise Software version 7.22.0-241.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"f91e99f8576f4ac3511a25595527b8fe5eb002e0d221625722a35274d6307065","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support [Redis Enterprise Software version 7.22.0-241](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-0-241). For version changes, supported distributions, and known limitations, see the [release notes for 7.22.0-15 (July 2025)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-0-releases/7-22-0-15-july2025)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.22.0-241`\n- **Operator**: `redislabs/operator:7.22.0-16`\n- **Services Rigger**: `redislabs/k8s-controller:7.22.0-16`\n- **Call Home Client**: `redislabs/re-call-home-client:7.22.0-16`"},{"id":"openshift-downloads","title":"Openshift downloads","role":"content","text":"- **OLM operator bundle**: `v7.22.0-16.0`\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.22.0-241`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.22.0-16`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.22.0-16`\n- **Call Home Client**: `registry.connect.redhat.com/redislabs/call-home-client:7.22.0-16`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [7.22.0 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-0-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-22-0-releases/7-22-0-17-september2025","title":"Redis Enterprise for Kubernetes 7.22.0-17 (September 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-0-releases/7-22-0-17-september2025/","summary":"This is a maintenance release to support Redis Enterprise Software version 7.22.0-250.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"6181da91126a3608d5386a5a4724c9688b2eb24e2774a0bbfc321fd226b41fb0","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support [Redis Enterprise Software version 7.22.0-250](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-0-250). For version changes, supported distributions, and known limitations, see the [release notes for 7.22.0-15 (July 2025)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-0-releases/7-22-0-15-july2025)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.22.0-250`\n- **Operator**: `redislabs/operator:7.22.0-17`\n- **Services Rigger**: `redislabs/k8s-controller:7.22.0-17`\n- **Call Home Client**: `redislabs/re-call-home-client:7.22.0-17`"},{"id":"openshift-downloads","title":"OpenShift downloads","role":"content","text":"- **OLM operator bundle**: `v7.22.0-17.0`\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.22.0-250`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.22.0-17`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.22.0-17`\n- **Call Home Client**: `registry.connect.redhat.com/redislabs/call-home-client:7.22.0-17`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [7.22.0 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-0-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-22-0-releases/7-22-0-7-april2025","title":"Redis Enterprise for Kubernetes 7.22.0-7 (April 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-0-releases/7-22-0-7-april2025/","summary":"Feature release with Redis Software 7.22.0-28 support, enhancements, and platform updates.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"f7bcc0ff45e67bbcc3b273743e3a161d17b5fe3cfc236867e84b1185a147385a","sections":[{"id":"new-in-the-release","title":"New in the release","role":"overview","text":"Redis Enterprise for Kubernetes 7.22.0-7 is a feature release that includes enhancements, platform updates, and support for Redis Software 7.22.0-28."},{"id":"enhancements","title":"Enhancements","role":"content","text":"- Added support for Redis Software 7.22.0.\n- Enabled support for Active-Active databases (REAADB) in multi-namespace deployments.\n- Defaulted new installations and upgrades to unprivileged mode, which uses a more secure security context without additional Linux capabilities.\n- A new cronjob is created by the operator using the `redislabs/re-call-home-client:7.22.0-7` image.\n- Updated supported Kubernetes platforms."},{"id":"api-changes","title":"API changes","role":"content","text":"| **CRD** | **Field** | **Change** | **Description** |\n|--------|-----------|------------|-----------------|\n| REAADB | `spec.participatingClusters[].namespace` | Added | Specifies the namespace in which the REAADB object will be deployed in the corresponding participating cluster. Make sure that the Redis Enterprise operator is configured to watch this namespace and that the required RBAC configuration is in place. For more information, see [Multi-namespace deployments](https://redis.io/docs/latest/operate/kubernetes/re-clusters/multi-namespace). If no namespace is specified, the REAADB object is deployed to the REC’s namespace in the corresponding cluster. |\n| REC | `spec.usageMeter.callHomeClient` | Added | Configuration for the call home client. For details, see the [REC API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api). |\n| REC | `spec.securityContext.resourceLimits` | Added | Configuration that allows Redis Enterprise to adjust system resource settings. For details, see the [REC API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api). |"},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"By default, the operator no longer sets additional Linux capabilities in the security context. If your deployment requires capabilities such as `SYS_RESOURCE`, see [Allow automatic resource adjustment](https://redis.io/docs/latest/operate/kubernetes/security/allow-resource-adjustment)."},{"id":"rhel9-based-image","title":"RHEL9-based image","role":"content","text":"As of version 7.8.2-6, Redis Enterprise images are based on Red Hat Enterprise Linux 9 (RHEL9). This means upgrades require:\n\n- [Cluster version of 7.4.2-2 or later](https://redis.io/docs/latest/operate/kubernetes/7.4.6/upgrade/).\n- Database version 7.2 or later.\n- RHEL9 compatible binaries for any modules you need.\n\nFor detailed steps, see the relevant upgrade page:\n\n- [OpenShift CLI](https://redis.io/docs/latest/operate/kubernetes/upgrade/openshift-cli)\n- [OpenShift OperatorHub](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-olm)\n- [Kubernetes](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster)"},{"id":"supported-distributions","title":"Supported distributions","role":"compatibility","text":"The following table shows supported distributions at the time of this release. You can also find this list in [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions).\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – This distribution is supported for this version of Redis Enterprise Kubernetes.\n\n<span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> Deprecated – This distribution is still supported for this version of Redis Enterprise Kubernetes, but support will be removed in a future release.\n\n<span title=\"X icon\">&#x274c;</span> End of life – Support for this distribution ended.\n\nAny distribution not listed below is not supported for production workloads.\n\n| Kubernetes version | **1.26** | **1.27** | **1.28** | **1.29** | **1.30** | **1.31** | **1.32** |\n|---|---|---|---|---|---|---|---|\n| **Community K8s** |  |  |  |  | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Amazon EKS** |  |  |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Azure AKS** |  |  |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Google GKE** |  |  |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Rancher** |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |\n| **VMware TKG 2.4** | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> |  |  |  |  |  |\n| **OpenShift** | **4.13** | **4.14** | **4.15** | **4.16** | **4.17** | **4.18** |  |\n|  | <span title=\"X icon\">&#x274c;</span> | <span title=\"X icon\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |\n| **VMware TKGI** | **1.16** | **1.17** | **1.18** | **1.19** | **1.20** |  |  |\n|  |  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |"},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.22.0-28`\n- **Operator**: `redislabs/operator:7.22.0-7`\n- **Services Rigger**: `redislabs/k8s-controller:7.22.0-7`\n- **Call Home Client**: `redislabs/re-call-home-client:7.22.0-7`"},{"id":"openshift-downloads","title":"Openshift downloads","role":"content","text":"- **OLM operator bundle**: `7.22.0-7.0`\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.22.0-28`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.22.0-7`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.22.0-7`\n- **Call Home Client**: `registry.connect.redhat.com/redislabs/call-home-client:7.22.0-7`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [7.22.0 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-0-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-22-0-releases","title":"Redis Enterprise for Kubernetes 7.22.0 release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-0-releases/","summary":"Releases with support for Redis Enterprise Software 7.22.0","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","children":[{"id":"operate/kubernetes/release-notes/7-22-0-releases/7-22-0-17-september2025","summary":"This is a maintenance release to support Redis Enterprise Software version 7.22.0-250.","title":"Redis Enterprise for Kubernetes 7.22.0-17 (September 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-0-releases/7-22-0-17-september2025/"},{"id":"operate/kubernetes/release-notes/7-22-0-releases/7-22-0-16-august2025","summary":"This is a maintenance release to support Redis Enterprise Software version 7.22.0-241.","title":"Redis Enterprise for Kubernetes 7.22.0-16 (August 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-0-releases/7-22-0-16-august2025/"},{"id":"operate/kubernetes/release-notes/7-22-0-releases/7-22-0-15-july2025","summary":"Feature release with Helm chart general availability, Kubernetes 1.33 and OpenShift 4.19 support, Redis Software 7.22.0-216 support, and enhancements.","title":"Redis Enterprise for Kubernetes 7.22.0-15 (July 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-0-releases/7-22-0-15-july2025/"},{"id":"operate/kubernetes/release-notes/7-22-0-releases/7-22-0-11-june2025","summary":"This is a maintenance release to support Redis Enterprise Software version 7.22.0-95.","title":"Redis Enterprise for Kubernetes 7.22.0-11 (June 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-0-releases/7-22-0-11-june2025/"},{"id":"operate/kubernetes/release-notes/7-22-0-releases/7-22-0-7-april2025","summary":"Feature release with Redis Software 7.22.0-28 support, enhancements, and platform updates.","title":"Redis Enterprise for Kubernetes 7.22.0-7 (April 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-0-releases/7-22-0-7-april2025/"}],"page_type":"content","content_hash":"6ead102315245c1168a9339ad285fdc0c3466f4c7bb92bdca06ea5fa0f37fb42","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise for Kubernetes 7.22.0 includes bug fixes, enhancements, and support for Redis Enterprise Software. The latest release is 7.22.0-17 with support for Redis Enterprise Software version 7.22.0-250."},{"id":"detailed-release-notes","title":"Detailed release notes","role":"content","text":"| Version (Release date) | Major changes |\n| --- | --- |\n| [7.22.0-17 (September 2025)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-0-releases/7-22-0-17-september2025/) | This is a maintenance release to support Redis Enterprise Software version 7.22.0-250. |\n| [7.22.0-16 (August 2025)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-0-releases/7-22-0-16-august2025/) | This is a maintenance release to support Redis Enterprise Software version 7.22.0-241. |\n| [7.22.0-15 (July 2025)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-0-releases/7-22-0-15-july2025/) | Feature release with Helm chart general availability, Kubernetes 1.33 and OpenShift 4.19 support, Redis Software 7.22.0-216 support, and enhancements. |\n| [7.22.0-11 (June 2025)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-0-releases/7-22-0-11-june2025/) | This is a maintenance release to support Redis Enterprise Software version 7.22.0-95. |\n| [7.22.0-7 (April 2025)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-0-releases/7-22-0-7-april2025/) | Feature release with Redis Software 7.22.0-28 support, enhancements, and platform updates. |"},{"id":"supported-distributions","title":"Supported distributions","role":"compatibility","text":"The following table shows supported distributions at the time of this release. You can also find this list in [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions).\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – This distribution is supported for this version of Redis Enterprise Kubernetes.\n\n<span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> Deprecated – This distribution is still supported for this version of Redis Enterprise Kubernetes, but support will be removed in a future release.\n\n<span title=\"X icon\">&#x274c;</span> End of life – Support for this distribution ended.\n\nAny distribution not listed below is not supported for production workloads.\n\n| Kubernetes version | **1.28** | **1.29** | **1.30** | **1.31** | **1.32** | **1.33** |\n|---|---|---|---|---|---|---|\n| **Community K8s** |  |  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Amazon EKS** |  |  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Azure AKS** |  |  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Google GKE** |  |  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Rancher** | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |  |\n| **OpenShift** | **4.15** | **4.16** | **4.17** | **4.18** | **4.19** |  |\n|  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |\n| **VMware TKGI** | **1.19** | **1.20** | **1.21** |  |  |  |\n|  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |  |"},{"id":"rhel9-based-image","title":"RHEL9-based image","role":"content","text":"As of version 7.8.2-6, Redis Enterprise images are based on Red Hat Enterprise Linux 9 (RHEL9). This means upgrades require:\n\n- [Cluster version of 7.4.2-2 or later](https://redis.io/docs/latest/operate/kubernetes/7.4.6/upgrade/).\n- Database version 7.2 or later.\n- RHEL9 compatible binaries for any modules you need.\n\nFor detailed steps, see the relevant upgrade page:\n\n- [OpenShift CLI](https://redis.io/docs/latest/operate/kubernetes/upgrade/openshift-cli)\n- [OpenShift OperatorHub](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-olm)\n- [Kubernetes](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster)"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"- **Expired license causes pod readiness failures, blocking recovery and upgrades** <!--RED-185977--> If a pod is stuck during upgrade, manually update the licenses via the REST API.\n\n- **Only upgrades from 7.4.2-2 and later are supported.** If you are using an earlier version, install 7.4.2-2 before upgrading to 7.8.2-6 or later.\n\n- **When changing the REDB field `spec.modulesList`, version might be upgraded to latest, even if a different version is specified.** To prevent the upgrade to latest, set  `spec.upgradeSpec.setModuleToLatest` to `false` before upgrading.\n\n- **PVC expansion is not supported when using Redis on Flash (Auto Tiering) (RED-165770)** Do not enable `enablePersistentVolumeResize` if your REC uses `redisOnFlashSpec` as this will result in conflicts.\n\n- **Istio ingress does not support partial wildcard hostnames** When using Istio ingress for Redis on Kubernetes, avoid partial wildcard hostnames in your REC configuration. Use only valid FQDNs or leftmost wildcards for Istio compatibility and stable external routing. For more information, see [Istio Ingress Fails for Redis on Kubernetes with Partial Wildcard Hostnames](https://support.redislabs.com/hc/en-us/articles/30446436267666-Istio-Ingress-Fails-for-Redis-on-Kubernetes-with-Partial-Wildcard-Hostnames).\n\n- **Missing endpoint for admission endpoint (rare) (RED-119469)** Restart the operator pod.\n\n- **The REDB \"redisVersion\" field can’t be used for memcached databases(RED-119152)**\n\n- **When modifying the database suffix for an Active-Active database, while the service-rigger is in a terminating state, the services-rigger will delete and create the ingress or route resources in a loop (RED-107687)** Wait until the services rigger pod has finished to terminate it.\n\n- **REAADB changes might fail with \"gateway timeout\" errors, mostly on OpenShift (RED-103048)** Retry the operation.\n\n- **Creating two databases with the same name directly on Redis Enterprise software will cause the service to be deleted and the database will not be available (RED-99997)** Avoid duplicating database names. Database creation via K8s has validation in place to prevent this.\n\n- **Installing the operator bundle produces warning: `Warning: would violate PodSecurity \"restricted: v1.24\"` (RED-97381)** Ignore the warning. This issue is documented as benign on official Red Hat documentation.\n\n- **RERC resources must have a unique name (RED-96302)** The string \"rec-name\"/\"rec-namespace\" must be different from all other participating clusters in the Active-Active database.\n\n- **Admission is not blocking REAADB with `shardCount` which exceeds license quota (RED-96301)** Fix the problems with the REAADB and reapply.\n\n- **Active-Active controller only supports global database options. Configuration specific to location is not supported (RED-86490)**\n\n- **Active-Active setup removal might keep services or routes undeleted (RED-77752)** Delete services or routes manually if you encounter this problem.\n\n- **`autoUpgrade` set to `true` can cause unexpected bdb upgrades when `redisUpgradePolicy` is set to `true` (RED-72351)** Contact support if your deployment is impacted.\n\n- **Following the previous quick start guide version causes issues with creating an REDB due to unrecognized memory field name (RED-69515)** The workaround is to use the newer (current) revision of Deploy Redis Enterprise Software for Kubernetes.\n\n- **PVC size issues when using decimal value in spec (RED-62132)** Make sure you use integer values for the PVC size.\n\n- **REC might report error states on initial startup (RED-61707)** There is no workaround at this time except to ignore the errors.\n\n- **Hashicorp Vault integration - no support for Gesher (RED-55080)** There is no workaround for this issue. Gesher support has been deprecated.\n\n- **REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED-47254)** When REC clusters are deployed on Kubernetes clusters without synchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes.\n\n- **Deleting an OpenShift project with an REC deployed may hang (RED-47192)** When an REC cluster is deployed in a project (namespace) and has REDB resources, the REDB resources must be deleted first before the REC can be deleted. Therefore, until the REDB resources are deleted, the project deletion will hang. The fix is to delete the REDB resources first and the REC second. Then, you can delete the project.\n\n- **Clusters must be named 'rec' in OLM-based deployments (RED-39825)** In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) are bound to a specific service account name (namely, \"rec\"). The workaround is to name the cluster \"rec\".\n\n- **Readiness probe incorrect on failures (RED-39300)** STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure.\n\n- **Internal DNS and Kubernetes DNS may have conflicts (RED-37462)** DNS conflicts are possible between the cluster `mdns_server` and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names.\n\n- **K8s-based 5.4.10 clusters seem to negatively affect existing 5.4.6 clusters (RED-37233)** Upgrade clusters to latest version.\n\n- **Node CPU usage is reported instead of pod CPU usage (RED-36884)** In Kubernetes, the reported node CPU usage is the usage of the Kubernetes worker node hosting the REC pod.\n\n- **An unreachable cluster has status running (RED-32805)** When a cluster is in an unreachable state, the state remains `running` instead of triggering an error.\n\n- **Long cluster names cause routes to be rejected  (RED-25871)** A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name exceeds 63 characters. The workaround is to limit the cluster name to 20 characters or fewer.\n\n- **Cluster CR (REC) errors are not reported after invalid updates (RED-25542)** A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-22-2-releases/7-22-2-21-october2025","title":"Redis Enterprise for Kubernetes 7.22.2-21 (October 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/7-22-2-21-october2025/","summary":"This is a maintenance release to support Redis Enterprise Software version 7.22.2-14.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"75b6831d1bfa4810871ba60f931cca764fbb500353047e532fa1e723f8ec0f6d","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support [Redis Enterprise Software version 7.22.2-14](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-14). For version changes, supported distributions, and known limitations, see the [7.22.0 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-0-releases)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.22.2-14`\n- **Operator**: `redislabs/operator:7.22.2-21`\n- **Services Rigger**: `redislabs/k8s-controller:7.22.2-21`\n- **Call Home Client**: `redislabs/re-call-home-client:7.22.2-21`"},{"id":"openshift-downloads","title":"Openshift downloads","role":"content","text":"- **OLM operator bundle**: `v7.22.2-21.1`\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.22.2-14`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.22.2-21`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.22.2-21`\n- **Call Home Client**: `registry.connect.redhat.com/redislabs/call-home-client:7.22.2-21`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [7.22.2 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-22-2-releases/7-22-2-22-october2025","title":"Redis Enterprise for Kubernetes 7.22.2-22 (October 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/7-22-2-22-october2025/","summary":"This is a maintenance release to support Redis Enterprise Software version 7.22.2-20.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"02b75982289dc90e6de3db1a905d610bb52a3c2eab001329c3fe4b809316298d","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support [Redis Enterprise Software version 7.22.2-20](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-20). For version changes, supported distributions, and known limitations, see the [7.22.2 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.22.2-20`\n- **Operator**: `redislabs/operator:7.22.2-22`\n- **Services Rigger**: `redislabs/k8s-controller:7.22.2-22`\n- **Call Home Client**: `redislabs/re-call-home-client:7.22.2-22`"},{"id":"openshift-downloads","title":"Openshift downloads","role":"content","text":"- **OLM operator bundle**: `v7.22.2-22.0`\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.22.2-20`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.22.2-22`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.22.2-22`\n- **Call Home Client**: `registry.connect.redhat.com/redislabs/call-home-client:7.22.2-22`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [7.22.2 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-22-2-releases/7-22-2-31-december2025","title":"Redis Enterprise for Kubernetes 7.22.2-31 (December 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/7-22-2-31-december2025/","summary":"Maintenance release to support Redis Enterprise Software version 7.22.2-41 including INE certificate support.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"1794bfdb9de4d9ac3c5d1d4f81ea6f18229a3990403bddbb6bbaf6aea26ee4cc","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support [Redis Enterprise Software version 7.22.2-41](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-41). For supported distributions known limitations, see the [7.22.2 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases)."},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"enhancements","title":"Enhancements","role":"content","text":"- Added API fields to configure internode encryption certificates"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- Fixed upgrade issues with large clusters (RED-176468)"},{"id":"api-changes","title":"API changes","role":"content","text":"| **CRD** | **Field** | **Change** | **Description** |\n|---|---|---|---|\n| REC | spec.dpInternodeEncryptionCertificateSecretName | Add | Secret name to use for cluster's Data Plane Internode Encryption (DPINE) certificate. If left blank, a cluster-provided certificate will be used. |\n| REC | spec.cpInternodeEncryptionCertificateSecretName | Add | Secret name to use for cluster's Control Plane Internode Encryption (CPINE) certificate. If left blank, a cluster-provided certificate will be used. |"},{"id":"deprecations","title":"Deprecations","role":"content","text":"- The v1alpha1 API version of the RedisEnterpriseCluster (REC) custom resource is deprecated."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.22.2-41`\n- **Operator**: `redislabs/operator:7.22.2-31`\n- **Services Rigger**: `redislabs/k8s-controller:7.22.2-31`\n- **Call Home Client**: `redislabs/re-call-home-client:7.22.2-31`"},{"id":"openshift-downloads","title":"Openshift downloads","role":"content","text":"- **OLM operator bundle**: `v7.22.2-31.1`\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.22.2-41`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.22.2-31`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.22.2-31`\n- **Call Home Client**: `registry.connect.redhat.com/redislabs/call-home-client:7.22.2-31`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"- **Expired license causes pod readiness failures, blocking recovery and upgrades** <!--RED-185977--> If a pod is stuck during upgrade, manually update the licenses via the REST API.\n\nFor all other known limitations, see [7.22.2 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases)."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-22-2-releases/7-22-2-32-january2026","title":"Redis Enterprise for Kubernetes 7.22.2-32 (January 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/7-22-2-32-january2026/","summary":"Maintenance release to support Redis Enterprise Software version 7.22.2-55 with bug and security fixes.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"4f83463b60269ae1d83d6aaf6279f657c684025b2ca8bd194be49f69f3f13bcc","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support Redis Enterprise Software version 7.22.2-55. For supported distributions and known limitations, see the [7.22.2 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.22.2-55`\n- **Operator**: `redislabs/operator:7.22.2-32`\n- **Services Rigger**: `redislabs/k8s-controller:7.22.2-32`\n- **Call Home Client**: `redislabs/re-call-home-client:7.22.2-32`"},{"id":"openshift-downloads","title":"Openshift downloads","role":"content","text":"- **OLM operator bundle**: `v7.22.2-32.1`\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.22.2-55`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.22.2-32`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.22.2-32`\n- **Call Home Client**: `registry.connect.redhat.com/redislabs/call-home-client:7.22.2-32`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"- **Expired license causes pod readiness failures, blocking recovery and upgrades** <!--RED-185977--> If a pod is stuck during upgrade, manually update the licenses via the REST API.\n\nFor all other known limitations, see [7.22.2 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases)."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-22-2-releases/7-22-2-37-feb2026","title":"Redis Enterprise for Kubernetes 7.22.2-37 (February 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/7-22-2-37-feb2026/","summary":"Maintenance release to support Redis Enterprise Software version 7.22.2-79 with bug and security fixes.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"c0225206aa988111b3e3b193981b5b656987c26ef3715c009a98210c95ba8470","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support Redis Enterprise Software version 7.22.2-79. For supported distributions and known limitations, see the [7.22.2 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.22.2-79`\n- **Operator**: `redislabs/operator:7.22.2-37`\n- **Services Rigger**: `redislabs/k8s-controller:7.22.2-37`\n- **Call Home Client**: `redislabs/re-call-home-client:7.22.2-37`"},{"id":"openshift-downloads","title":"Openshift downloads","role":"content","text":"- **OLM operator bundle**: `v7.22.2-37.1`\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.22.2-79`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.22.2-37`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.22.2-37`\n- **Call Home Client**: `registry.connect.redhat.com/redislabs/call-home-client:7.22.2-37`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"- **Expired license causes pod readiness failures, blocking recovery and upgrades** <!--RED-185977--> If a pod is stuck during upgrade, manually update the licenses via the REST API.\n\nFor all other known limitations, see [7.22.2 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases)."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-22-2-releases/7-22-2-38-march2026","title":"Redis Enterprise for Kubernetes 7.22.2-38 (March 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/7-22-2-38-march2026/","summary":"Maintenance release to support Redis Enterprise Software version 7.22.2-93 with bug and security fixes.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"f5a64af24cbe25b7323896739cadf27c906c482262c51620a054d4ceab5a5d27","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support Redis Enterprise Software version 7.22.2-93. For supported distributions and known limitations, see the [7.22.2 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases)."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- Fixed pod readiness failures due to expired license <!--RED-185977-->"},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.22.2-93`\n- **Operator**: `redislabs/operator:7.22.2-38`\n- **Services Rigger**: `redislabs/k8s-controller:7.22.2-38`\n- **Call Home Client**: `redislabs/re-call-home-client:7.22.2-38`"},{"id":"openshift-downloads","title":"Openshift downloads","role":"content","text":"- **OLM operator bundle**: `v7.22.2-38.0`\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.22.2-93`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.22.2-38`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.22.2-38`\n- **Call Home Client**: `registry.connect.redhat.com/redislabs/call-home-client:7.22.2-38`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [7.22.2 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-22-2-releases/7-22-2-39-april2026","title":"Redis Enterprise for Kubernetes 7.22.2-39 (April 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/7-22-2-39-april2026/","summary":"Maintenance release to support Redis Enterprise Software version 7.22.2-116 with bug and security fixes.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"5f3fc6f9487406023642bf8d8153f2dcbd4a8622d513d519059b015dcab27d83","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support Redis Enterprise Software version 7.22.2-116. For supported distributions and known limitations, see the [7.22.2 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.22.2-116`\n- **Operator**: `redislabs/operator:7.22.2-39`\n- **Services Rigger**: `redislabs/k8s-controller:7.22.2-39`\n- **Call Home Client**: `redislabs/re-call-home-client:7.22.2-39`"},{"id":"openshift-downloads","title":"OpenShift downloads","role":"content","text":"- **OLM operator bundle**: `v7.22.2-39.0`\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.22.2-116`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.22.2-39`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.22.2-39`\n- **Call Home Client**: `registry.connect.redhat.com/redislabs/call-home-client:7.22.2-39`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [7.22.2 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-22-2-releases/7-22-2-40-may2026","title":"Redis for Kubernetes 7.22.2-40 (May 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/7-22-2-40-may2026/","summary":"Maintenance release to support Redis Software version 7.22.2-133 with bug and security fixes.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"cdbf5877c5e8d16f4356db6a7580a043269c5bf7b50be2e15945938bdda2760f","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release of Redis for Kubernetes to support Redis Software version 7.22.2-133. For supported distributions and known limitations, see the [7.22.2 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Software**: `redislabs/redis:7.22.2-133`\n- **Operator**: `redislabs/operator:7.22.2-40`\n- **Services Rigger**: `redislabs/k8s-controller:7.22.2-40`\n- **Call Home Client**: `redislabs/re-call-home-client:7.22.2-40`"},{"id":"openshift-downloads","title":"OpenShift downloads","role":"content","text":"- **OLM operator bundle**: `v7.22.2-40.0`\n- **Redis Software**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.22.2-133`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.22.2-40`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.22.2-40`\n- **Call Home Client**: `registry.connect.redhat.com/redislabs/call-home-client:7.22.2-40`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [7.22.2 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-22-2-releases/7-22-2-42-july2026","title":"Redis for Kubernetes 7.22.2-42 (July 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/7-22-2-42-july2026/","summary":"Maintenance release to support Redis Software version 7.22.2-170 with bug and security fixes.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"a2c76054522f04be72274c0936d15ee3b756a7ce57035e5739883d6951a9835d","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release of Redis for Kubernetes to support Redis Software version 7.22.2-170. For supported distributions and known limitations, see the [7.22.2 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Software**: `redislabs/redis:7.22.2-170`\n- **Operator**: `redislabs/operator:7.22.2-42`\n- **Services Rigger**: `redislabs/k8s-controller:7.22.2-42`\n- **Call Home Client**: `redislabs/re-call-home-client:7.22.2-42`"},{"id":"openshift-downloads","title":"OpenShift downloads","role":"content","text":"- **OLM operator bundle**: `v7.22.2-42.0`\n- **Redis Software**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.22.2-170`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.22.2-42`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.22.2-42`\n- **Call Home Client**: `registry.connect.redhat.com/redislabs/call-home-client:7.22.2-42`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [7.22.2 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-22-2-releases/7-22-2-43-august2026","title":"Redis for Kubernetes 7.22.2-43 (August 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/7-22-2-43-august2026/","summary":"Maintenance release to support Redis Software version 7.22.2-179.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"ecc2377a9fb73c3b75782b4d42d3f91b3d3de32cc91dc9050fd6a5a139dd8db1","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release of Redis for Kubernetes to support Redis Software version 7.22.2-179. For supported distributions and known limitations, see the [7.22.2 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Software**: `redislabs/redis:7.22.2-179`\n- **Operator**: `redislabs/operator:7.22.2-43`\n- **Services Rigger**: `redislabs/k8s-controller:7.22.2-43`\n- **Call Home Client**: `redislabs/re-call-home-client:7.22.2-43`"},{"id":"openshift-downloads","title":"OpenShift downloads","role":"content","text":"- **OLM operator bundle**: `v7.22.2-43.0`\n- **Redis Software**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.22.2-179`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.22.2-43`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.22.2-43`\n- **Call Home Client**: `registry.connect.redhat.com/redislabs/call-home-client:7.22.2-43`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"new-limitations","title":"New limitations","role":"content","text":"- **Upgrading directly from Redis for Kubernetes 7.22.2-43 to 8.0.20-26 is not supported in OLM-based deployments** due to an upgrade path limitation between these builds. Upgrade to 8.0.20-27 or later instead.\n\nSee [7.22.2 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases) for information on other known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-22-2-releases/7-22-2-45-september2026","title":"Redis Software for Kubernetes 7.22.2-45 (September 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/7-22-2-45-september2026/","summary":"Maintenance release to support Redis Software version 7.22.2-189.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"0dc6695572389d5331d735cf71281136de15f32ea74ff016c53e4d6b6895f6f1","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release of Redis Software for Kubernetes to support Redis Software version 7.22.2-189. It fixes a high-severity bug with security implications that affected Active-Active databases. For supported distributions and known limitations, see the [7.22.2 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases)."},{"id":"security","title":"Security","role":"security","text":"A high-severity bug with security implications affected Active-Active databases managed by the Redis Software for Kubernetes operator. **Upgrade to operator version 7.22.2-45 or later on this release line to get the fix.**\n\nWhen the operator applied a configuration update to an Active-Active database, it rebuilt and sent the database's entire configuration, but only re-read a credential from its Kubernetes secret if it believed that specific secret had changed. Any update triggered by something else therefore sent the password as empty, and on operator versions before 8.2.0-12, also cleared the mutual TLS (mTLS) client certificates.\n\nEither of the following triggered such an update:\n\n- Any configuration change on the Active-Active database, such as eviction policy, memory size, or backup settings, made by a user or by automation tooling such as GitOps.\n- A modification of the backup secret or a client certificate secret on the cluster, even with no configuration change. The operator re-read the touched secret correctly but wiped the password, because its secret was not the one that changed.\n\nThe impact was two-sided:\n\n- **Availability**: applications connecting with the password failed on every new connection. Existing connections kept working, so the failure looked intermittent.\n- **Security**: the database simultaneously accepted connections with no credentials at all, and with the mTLS variant, accepted clients without certificates. Nothing alerted and the database reported healthy, so a database could run exposed without anyone noticing.\n\nThis affected all released operator versions since the feature was introduced in early 2023. The fix prevents the credential wipe and automatically repairs already-affected databases when you upgrade the operator. No additional steps are required.\n\nThe fix is available in operator versions 7.4.6-11, 7.8.6-20, 7.22.2-45, 8.0.20-27, and 8.2.0-15. <!--RED-212963--> <!--RED-213921-->"},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Software**: `redislabs/redis:7.22.2-189`\n- **Operator**: `redislabs/operator:7.22.2-45`\n- **Services Rigger**: `redislabs/k8s-controller:7.22.2-45`\n- **Call Home Client**: `redislabs/re-call-home-client:7.22.2-45`"},{"id":"openshift-downloads","title":"OpenShift downloads","role":"content","text":"- **OLM operator bundle**: `v7.22.2-45.1`\n- **Redis Software**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.22.2-189`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.22.2-45`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.22.2-45`\n- **Call Home Client**: `registry.connect.redhat.com/redislabs/call-home-client:7.22.2-45`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [7.22.2 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-22-2-releases/7-22-2-47-october2026","title":"Redis Software for Kubernetes 7.22.2-47 (October 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/7-22-2-47-october2026/","summary":"Maintenance release to support Redis Software version 7.22.2-204.","tags":["docs","operate","kubernetes"],"last_updated":"2026-10-19T14:33:06-05:00","page_type":"content","content_hash":"9219c7d8b49e7f47f5c857d46bb3f8bcd7b71b949c032dda8684ce6805d8bb54","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release of Redis Software for Kubernetes to support Redis Software version 7.22.2-204. For supported distributions and known limitations, see the [7.22.2 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Software**: `redislabs/redis:7.22.2-204`\n- **Operator**: `redislabs/operator:7.22.2-47`\n- **Services Rigger**: `redislabs/k8s-controller:7.22.2-47`\n- **Call Home Client**: `redislabs/re-call-home-client:7.22.2-47`"},{"id":"openshift-downloads","title":"OpenShift downloads","role":"content","text":"- **OLM operator bundle**: `v7.22.2-47.1`\n- **Redis Software**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.22.2-204`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.22.2-47`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.22.2-47`\n- **Call Home Client**: `registry.connect.redhat.com/redislabs/call-home-client:7.22.2-47`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [7.22.2 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-22-2-releases","title":"Redis Enterprise for Kubernetes 7.22.2 release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/","summary":"Releases with support for Redis Enterprise Software 7.22.2","tags":["docs","operate","kubernetes"],"last_updated":"2026-10-19T14:33:06-05:00","children":[{"id":"operate/kubernetes/release-notes/7-22-2-releases/7-22-2-47-october2026","summary":"Maintenance release to support Redis Software version 7.22.2-204.","title":"Redis Software for Kubernetes 7.22.2-47 (October 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/7-22-2-47-october2026/"},{"id":"operate/kubernetes/release-notes/7-22-2-releases/7-22-2-45-september2026","summary":"Maintenance release to support Redis Software version 7.22.2-189.","title":"Redis Software for Kubernetes 7.22.2-45 (September 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/7-22-2-45-september2026/"},{"id":"operate/kubernetes/release-notes/7-22-2-releases/7-22-2-43-august2026","summary":"Maintenance release to support Redis Software version 7.22.2-179.","title":"Redis for Kubernetes 7.22.2-43 (August 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/7-22-2-43-august2026/"},{"id":"operate/kubernetes/release-notes/7-22-2-releases/7-22-2-42-july2026","summary":"Maintenance release to support Redis Software version 7.22.2-170 with bug and security fixes.","title":"Redis for Kubernetes 7.22.2-42 (July 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/7-22-2-42-july2026/"},{"id":"operate/kubernetes/release-notes/7-22-2-releases/7-22-2-40-may2026","summary":"Maintenance release to support Redis Software version 7.22.2-133 with bug and security fixes.","title":"Redis for Kubernetes 7.22.2-40 (May 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/7-22-2-40-may2026/"},{"id":"operate/kubernetes/release-notes/7-22-2-releases/7-22-2-39-april2026","summary":"Maintenance release to support Redis Enterprise Software version 7.22.2-116 with bug and security fixes.","title":"Redis Enterprise for Kubernetes 7.22.2-39 (April 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/7-22-2-39-april2026/"},{"id":"operate/kubernetes/release-notes/7-22-2-releases/7-22-2-38-march2026","summary":"Maintenance release to support Redis Enterprise Software version 7.22.2-93 with bug and security fixes.","title":"Redis Enterprise for Kubernetes 7.22.2-38 (March 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/7-22-2-38-march2026/"},{"id":"operate/kubernetes/release-notes/7-22-2-releases/7-22-2-37-feb2026","summary":"Maintenance release to support Redis Enterprise Software version 7.22.2-79 with bug and security fixes.","title":"Redis Enterprise for Kubernetes 7.22.2-37 (February 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/7-22-2-37-feb2026/"},{"id":"operate/kubernetes/release-notes/7-22-2-releases/7-22-2-32-january2026","summary":"Maintenance release to support Redis Enterprise Software version 7.22.2-55 with bug and security fixes.","title":"Redis Enterprise for Kubernetes 7.22.2-32 (January 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/7-22-2-32-january2026/"},{"id":"operate/kubernetes/release-notes/7-22-2-releases/7-22-2-31-december2025","summary":"Maintenance release to support Redis Enterprise Software version 7.22.2-41 including INE certificate support.","title":"Redis Enterprise for Kubernetes 7.22.2-31 (December 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/7-22-2-31-december2025/"},{"id":"operate/kubernetes/release-notes/7-22-2-releases/7-22-2-22-october2025","summary":"This is a maintenance release to support Redis Enterprise Software version 7.22.2-20.","title":"Redis Enterprise for Kubernetes 7.22.2-22 (October 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/7-22-2-22-october2025/"},{"id":"operate/kubernetes/release-notes/7-22-2-releases/7-22-2-21-october2025","summary":"This is a maintenance release to support Redis Enterprise Software version 7.22.2-14.","title":"Redis Enterprise for Kubernetes 7.22.2-21 (October 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/7-22-2-21-october2025/"}],"page_type":"content","content_hash":"7d82dd141f151adaa51c7f9e0ee72af4c554cfc5e9a07892bc6d8590eeede5c4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise for Kubernetes 7.22.2 includes bug fixes, enhancements, and support for Redis Enterprise Software. The latest release is 7.22.2-47 with support for Redis Enterprise Software version 7.22.2-204."},{"id":"detailed-release-notes","title":"Detailed release notes","role":"content","text":"| Version (Release date) | Major changes |\n| --- | --- |\n| [7.22.2-47 (October 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/7-22-2-47-october2026/) | Maintenance release to support Redis Software version 7.22.2-204. |\n| [7.22.2-45 (September 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/7-22-2-45-september2026/) | Maintenance release to support Redis Software version 7.22.2-189. |\n| [7.22.2-43 (August 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/7-22-2-43-august2026/) | Maintenance release to support Redis Software version 7.22.2-179. |\n| [7.22.2-42 (July 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/7-22-2-42-july2026/) | Maintenance release to support Redis Software version 7.22.2-170 with bug and security fixes. |\n| [7.22.2-40 (May 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/7-22-2-40-may2026/) | Maintenance release to support Redis Software version 7.22.2-133 with bug and security fixes. |\n| [7.22.2-39 (April 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/7-22-2-39-april2026/) | Maintenance release to support Redis Enterprise Software version 7.22.2-116 with bug and security fixes. |\n| [7.22.2-38 (March 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/7-22-2-38-march2026/) | Maintenance release to support Redis Enterprise Software version 7.22.2-93 with bug and security fixes. |\n| [7.22.2-37 (February 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/7-22-2-37-feb2026/) | Maintenance release to support Redis Enterprise Software version 7.22.2-79 with bug and security fixes. |\n| [7.22.2-32 (January 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/7-22-2-32-january2026/) | Maintenance release to support Redis Enterprise Software version 7.22.2-55 with bug and security fixes. |\n| [7.22.2-31 (December 2025)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/7-22-2-31-december2025/) | Maintenance release to support Redis Enterprise Software version 7.22.2-41 including INE certificate support. |\n| [7.22.2-22 (October 2025)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/7-22-2-22-october2025/) | This is a maintenance release to support Redis Enterprise Software version 7.22.2-20. |\n| [7.22.2-21 (October 2025)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/7-22-2-21-october2025/) | This is a maintenance release to support Redis Enterprise Software version 7.22.2-14. |"},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"new-limitations","title":"New limitations","role":"content","text":"- **Expired license causes pod readiness failures, blocking recovery and upgrades** <!--RED-185977--> If a pod is stuck during upgrade, manually update the licenses via the REST API."},{"id":"existing-limitations","title":"Existing limitations","role":"content","text":"- **Only upgrades from 7.4.2-2 and later are supported.** If you are using an earlier version, install 7.4.2-2 before upgrading to 7.8.2-6 or later.\n\n- **When changing the REDB field `spec.modulesList`, version might be upgraded to latest, even if a different version is specified.** To prevent the upgrade to latest, set  `spec.upgradeSpec.setModuleToLatest` to `false` before upgrading.\n\n- **PVC expansion is not supported when using Redis on Flash (Auto Tiering) (RED-165770)** Do not enable `enablePersistentVolumeResize` if your REC uses `redisOnFlashSpec` as this will result in conflicts.\n\n- **Istio ingress does not support partial wildcard hostnames** When using Istio ingress for Redis on Kubernetes, avoid partial wildcard hostnames in your REC configuration. Use only valid FQDNs or leftmost wildcards for Istio compatibility and stable external routing. For more information, see [Istio Ingress Fails for Redis on Kubernetes with Partial Wildcard Hostnames](https://support.redislabs.com/hc/en-us/articles/30446436267666-Istio-Ingress-Fails-for-Redis-on-Kubernetes-with-Partial-Wildcard-Hostnames).\n\n- **Missing endpoint for admission endpoint (rare) (RED-119469)** Restart the operator pod.\n\n- **The REDB \"redisVersion\" field can’t be used for memcached databases(RED-119152)**\n\n- **When modifying the database suffix for an Active-Active database, while the service-rigger is in a terminating state, the services-rigger will delete and create the ingress or route resources in a loop (RED-107687)** Wait until the services rigger pod has finished to terminate it.\n\n- **REAADB changes might fail with \"gateway timeout\" errors, mostly on OpenShift (RED-103048)** Retry the operation.\n\n- **Creating two databases with the same name directly on Redis Enterprise software will cause the service to be deleted and the database will not be available (RED-99997)** Avoid duplicating database names. Database creation via K8s has validation in place to prevent this.\n\n- **Installing the operator bundle produces warning: `Warning: would violate PodSecurity \"restricted: v1.24\"` (RED-97381)** Ignore the warning. This issue is documented as benign on official Red Hat documentation.\n\n- **RERC resources must have a unique name (RED-96302)** The string \"rec-name\"/\"rec-namespace\" must be different from all other participating clusters in the Active-Active database.\n\n- **Admission is not blocking REAADB with `shardCount` which exceeds license quota (RED-96301)** Fix the problems with the REAADB and reapply.\n\n- **Active-Active controller only supports global database options. Configuration specific to location is not supported (RED-86490)**\n\n- **Active-Active setup removal might keep services or routes undeleted (RED-77752)** Delete services or routes manually if you encounter this problem.\n\n- **`autoUpgrade` set to `true` can cause unexpected bdb upgrades when `redisUpgradePolicy` is set to `true` (RED-72351)** Contact support if your deployment is impacted.\n\n- **Following the previous quick start guide version causes issues with creating an REDB due to unrecognized memory field name (RED-69515)** The workaround is to use the newer (current) revision of Deploy Redis Enterprise Software for Kubernetes.\n\n- **PVC size issues when using decimal value in spec (RED-62132)** Make sure you use integer values for the PVC size.\n\n- **REC might report error states on initial startup (RED-61707)** There is no workaround at this time except to ignore the errors.\n\n- **Hashicorp Vault integration - no support for Gesher (RED-55080)** There is no workaround for this issue. Gesher support has been deprecated.\n\n- **REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED-47254)** When REC clusters are deployed on Kubernetes clusters without synchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes.\n\n- **Deleting an OpenShift project with an REC deployed may hang (RED-47192)** When an REC cluster is deployed in a project (namespace) and has REDB resources, the REDB resources must be deleted first before the REC can be deleted. Therefore, until the REDB resources are deleted, the project deletion will hang. The fix is to delete the REDB resources first and the REC second. Then, you can delete the project.\n\n- **Clusters must be named 'rec' in OLM-based deployments (RED-39825)** In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) are bound to a specific service account name (namely, \"rec\"). The workaround is to name the cluster \"rec\".\n\n- **Readiness probe incorrect on failures (RED-39300)** STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure.\n\n- **Internal DNS and Kubernetes DNS may have conflicts (RED-37462)** DNS conflicts are possible between the cluster `mdns_server` and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names.\n\n- **K8s-based 5.4.10 clusters seem to negatively affect existing 5.4.6 clusters (RED-37233)** Upgrade clusters to latest version.\n\n- **Node CPU usage is reported instead of pod CPU usage (RED-36884)** In Kubernetes, the reported node CPU usage is the usage of the Kubernetes worker node hosting the REC pod.\n\n- **An unreachable cluster has status running (RED-32805)** When a cluster is in an unreachable state, the state remains `running` instead of triggering an error.\n\n- **Long cluster names cause routes to be rejected  (RED-25871)** A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name exceeds 63 characters. The workaround is to limit the cluster name to 20 characters or fewer.\n\n- **Cluster CR (REC) errors are not reported after invalid updates (RED-25542)** A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence."},{"id":"rhel9-based-image","title":"RHEL9-based image","role":"content","text":"As of version 7.8.2-6, Redis Enterprise images are based on Red Hat Enterprise Linux 9 (RHEL9). This means upgrades require:\n\n- [Cluster version of 7.4.2-2 or later](https://redis.io/docs/latest/operate/kubernetes/7.4.6/upgrade/).\n- Database version 7.2 or later.\n- RHEL9 compatible binaries for any modules you need.\n\nFor detailed steps, see the relevant upgrade page:\n\n- [OpenShift CLI](https://redis.io/docs/latest/operate/kubernetes/upgrade/openshift-cli)\n- [OpenShift OperatorHub](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-olm)\n- [Kubernetes](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster)"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-4-2-releases/7-4-2-03-24","title":"Redis Enterprise for Kubernetes 7.4.2-2 (March 2024) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-2-releases/7-4-2-03-24/","summary":"This is a maintenance release supporting Redis Enterprise Software 7.4.2.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"3427910b0f6144c866b798c31308e70325de7ce274ba12a3c8bec8b08e82234f","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"The primary purpose of this release is to support [Redis Enterprise 7.4.2](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-104), which is a maintenance release of Redis Enterprise Software."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":"For a list of fixes related to CVEs, see the [Redis Enterprise 7.4.2-104 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-104#security)."},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"The following changes included in this release affect the upgrade process. Please read carefully before upgrading.\n\n#### Upgrade operating system\n\n> [!WARNING]\n> If your databases use modules, you need to update all nodes in the cluster to Redis Enterprise 7.2.4 or later before upgrading your operating system. See [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os)in the Redis Enterprise Software documentation for more details.\n\n#### terminationGracePeriodSeconds\n\nThe configurable `terminationGracePeriodSeconds` REC field was added in version 7.2.4-12 to replace the hard-coded timeout of 15 minutes for a node to be stopped or drained (RED-111471).\n\n#### ValidatingWebhookConfiguration\n\nVersions 6.4.2-4 and later include a new `ValidatingWebhookConfiguration` resource to replace the `redb-admission` webhook resource. To use releases 6.4.2-4 or later, delete the old webhook resource and apply the new file. See [upgrade Redis cluster](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#reapply-webhook) for instructions.\n\n#### OpenShift SCC\n\nVersions 6.4.2-6 and later include a new SCC (`redis-enterprise-scc-v2`) that you need to bind to your service account before upgrading. OpenShift clusters running version 6.2.12 or earlier upgrading to version 6.2.18 or later might get stuck if you skip this step. See [upgrade a Redis Enterprise cluster (REC)](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#before-upgrading) for instructions."},{"id":"deprecations","title":"Deprecations","role":"content","text":"- Redis Software for Kubernetes no longer supports the RHEL7 operating system. Migrate to RHEL8 to use this version."},{"id":"supported-distributions","title":"Supported distributions","role":"compatibility","text":"See the [7.4.2 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-2-releases) page for the list of supported distributions."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.4.2-104`\n- **Operator**: `redislabs/operator:7.4.2-2`\n- **Services Rigger**: `redislabs/k8s-controller:7.4.2-2`\n\n#### OpenShift images\n\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.4.2-104.rhel8-openshift`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.4.2-2`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.4.2-2`\n\n#### OLM bundle\n\n**Redis Enterprise operator bundle** : `v7.4.2-2.6`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See the [7.4.2 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-2-releases) page for the list of known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-4-2-releases/7-4-2-12","title":"Redis Enterprise for Kubernetes 7.4.2-12 (May 2024) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-2-releases/7-4-2-12/","summary":"This is a feature release with a new version of Redis Enterprise Software support for persistent volume expansion.","aliases":["/operate/kubernetes/release-notes/7-4-2-12/"],"tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"2459a2065cf8ac279976b2ad6ae8c5d4d5cfea282a32501a519681cb68d335a0","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This release has many enhancements, most notably support for persistent volume expansion in the REC. Also, some essential changes in module handling support newer capabilities prompted by changes in the underlying Redis Enterprise around versioning."},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"enhancements","title":"Enhancements","role":"content","text":"- Support for persistent volume expansion in the REC (RED-84018)\n- Ability to specify Redis version in REDB spec (RED-103945)\n- Updated [supported distributions](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-2-releases#supported-distributions) (RED-119711)\n- Ability to enable modules via REDB without specifying a module version (RED-116082)\n- Dual Stack configuration support (RED-121897)"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- Fixed case of client certificates being updated in a loop (RED-121633)\n- Fixed issues disabling replication on REDB (RED-122721)"},{"id":"api-changes","title":"API changes","role":"content","text":"| **CRD** | **Field** | **Change** | **Description** |\n|---|---|---|---|\n| REC | spec.persistentSpec.enablePersistentVolumeResize | ADD | Set to \"true\" to allow changes in volumeSize after REC creation (for sizing up only) |\n| REC | status.persistenceStatus | ADD | Indicates the status of persistent volume expansion |\n| REC | spec.redisEnterpriseIPFamily | ADD | Configure which IP family to use when Kubernetes cluster has Dual Stack networking enabled  |\n| REDB | spec.moduleList[x].version | CHANGE | Field is no longer mandatory |\n| REDB | spec.upgradeSpec | ADD | New field allows configuration of module upgrade settings |\n| REDB | spec.redisVersion | CHANGE | Now allows setting version numbers (Major.Minor) |"},{"id":"version-changes","title":"Version changes","role":"compatibility","text":"For a list of fixes related to CVEs, see the [Redis Enterprise 7.4.2-129 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-129#security)."},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"The following changes included in this release affect the upgrade process. Please read carefully before upgrading.\n\n#### ValidatingWebhookConfiguration\n\nVersions 6.4.2-4 and later include a new `ValidatingWebhookConfiguration` resource to replace the `redb-admission` webhook resource. To use releases 6.4.2-4 or later, delete the old webhook resource and apply the new file. See [upgrade Redis cluster](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#reapply-webhook) for instructions.\n\n#### OpenShift SCC\n\nVersions 6.4.2-6 and later include a new SCC (`redis-enterprise-scc-v2`) that you need to bind to your service account before upgrading. OpenShift clusters running version 6.2.12 or earlier upgrading to version 6.2.18 or later might get stuck if you skip this step. See [upgrade a Redis Enterprise cluster (REC)](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#before-upgrading) for instructions.\n\n#### Upcoming Changes\n\n- Future Redis Enterprise images will be UBI9-based only, without support for Ubuntu-based images."},{"id":"supported-distributions","title":"Supported distributions","role":"compatibility","text":"The following table shows supported distributions at the time of this release. You can also find this list in [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions).\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – This distribution is supported for this version of Redis Enterprise Software for Kubernetes.\n\n<span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> Deprecated – This distribution is still supported for this version of Redis Enterprise Software for Kubernetes, but support will be removed in a future release.\n\n<span title=\"X icon\">&#x274c;</span> End of life – Support for this distribution ended.\n\nAny distribution not listed below is not supported for production workloads.\n\n| **Kubernetes version** | **1.24** | **1.25** | **1.26** | **1.27** | **1.28** | **1.29** |\n|---|---|---|---|---|---|---|\n| **Community Kubernetes** |  |  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Amazon EKS** |  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |\n| **Azure AKS** |  |  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Google GKE** |  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Rancher RKE2** | <span title=\"X icon\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |\n| **VMware TKG 2.2** | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> |  |  |  |  |\n| **VMware TKG 2.3** | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |  |\n| **VMware TKG 2.4** |  | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |\n| **OpenShift version** | **4.11** | **4.12** | **4.13** | **4.14** | **4.15** |  |\n|  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |\n| **VMware TKGI version** | **1.15** | **1.16** | **1.17** | **1.18** |  |  |\n|  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |"},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.4.2-129`\n- **Operator**: `redislabs/operator:7.4.2-12`\n- **Services Rigger**: `redislabs/k8s-controller:7.4.2-12`"},{"id":"openshift-images","title":"OpenShift images","role":"content","text":"- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.4.2-129.rhel8-openshift`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.4.2-12`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.4.2-12`"},{"id":"olm-bundle","title":"OLM bundle","role":"content","text":"**Redis Enterprise operator bundle** : `v7.4.2-12.0`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"new-limitations","title":"New limitations","role":"content","text":"- **Missing endpoint for admission endpoint (rare) (RED-119469)** Restart the operator pod.\n\n- **PVC expansion is not supported when using Redis on Flash (Auto Tiering) (RED-165770)** Do not enable `enablePersistentVolumeResize` if your REC uses `redisOnFlashSpec` as this will result in conflicts."},{"id":"existing-limitations","title":"Existing limitations","role":"content","text":"- **The REDB \"redisVersion\" field can’t be used for memcached databases(RED-119152)**\n\n- **When modifying the database suffix for an Active-Active database, while the service-rigger is in a terminating state, the services-rigger will delete and create the ingress or route resources in a loop (RED-107687)** Wait until the services rigger pod has finished to terminate it.\n\n- **REAADB changes might fail with \"gateway timeout\" errors, mostly on OpenShift (RED-103048)** Retry the operation.\n\n- **Creating two databases with the same name directly on Redis Enterprise software will cause the service to be deleted and the database will not be available (RED-99997)** Avoid duplicating database names. Database creation via K8s has validation in place to prevent this.\n\n- **Installing the operator bundle produces warning: `Warning: would violate PodSecurity \"restricted: v1.24\"` (RED-97381)** Ignore the warning. This issue is documented as benign on official Red Hat documentation.\n\n- **RERC resources must have a unique name (RED-96302)** The string \"rec-name\"/\"rec-namespace\" must be different from all other participating clusters in the Active-Active database.\n\n- **Admission is not blocking REAADB with `shardCount` which exceeds license quota (RED-96301)** Fix the problems with the REAADB and reapply.\n\n- **Active-Active controller only supports global database options. Configuration specific to location is not supported (RED-86490)**\n\n- **Active-Active setup removal might keep services or routes undeleted (RED-77752)** Delete services or routes manually if you encounter this problem.\n\n- **`autoUpgrade` set to `true` can cause unexpected bdb upgrades when `redisUpgradePolicy` is set to `true` (RED-72351)** Contact support if your deployment is impacted.\n\n- **Following the previous quick start guide version causes issues with creating an REDB due to unrecognized memory field name (RED-69515)** The workaround is to use the newer (current) revision of Deploy Redis Enterprise Software for Kubernetes.\n\n- **PVC size issues when using decimal value in spec (RED-62132)** Make sure you use integer values for the PVC size.\n\n- **REC might report error states on initial startup (RED-61707)** There is no workaround at this time except to ignore the errors.\n\n- **Hashicorp Vault integration - no support for Gesher (RED-55080)** There is no workaround for this issue. Gesher support has been deprecated.\n\n- **REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED-47254)** When REC clusters are deployed on Kubernetes clusters without synchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes.\n\n- **Deleting an OpenShift project with an REC deployed may hang (RED-47192)** When an REC cluster is deployed in a project (namespace) and has REDB resources, the REDB resources must be deleted first before the REC can be deleted. Therefore, until the REDB resources are deleted, the project deletion will hang. The fix is to delete the REDB resources first and the REC second. Then, you can delete the project.\n\n- **Clusters must be named 'rec' in OLM-based deployments (RED-39825)** In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) are bound to a specific service account name (namely, \"rec\"). The workaround is to name the cluster \"rec\".\n\n- **Readiness probe incorrect on failures (RED-39300)** STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure.\n\n- **Internal DNS and Kubernetes DNS may have conflicts (RED-37462)** DNS conflicts are possible between the cluster `mdns_server` and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names.\n\n- **K8s-based 5.4.10 clusters seem to negatively affect existing 5.4.6 clusters (RED-37233)** Upgrade clusters to latest version.\n\n- **Node CPU usage is reported instead of pod CPU usage (RED-36884)** In Kubernetes, the reported node CPU usage is the usage of the Kubernetes worker node hosting the REC pod.\n\n- **An unreachable cluster has status running (RED-32805)** When a cluster is in an unreachable state, the state remains `running` instead of triggering an error.\n\n- **Long cluster names cause routes to be rejected  (RED-25871)** A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name exceeds 63 characters. The workaround is to limit the cluster name to 20 characters or fewer.\n\n- **Cluster CR (REC) errors are not reported after invalid updates (RED-25542)** A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-4-2-releases/7-4-2-2","title":"Redis Enterprise for Kubernetes 7.4.2-2 (Feb 2024) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-2-releases/7-4-2-2/","summary":"The primary purpose of this release is to support Redis Enterprise 7.4.2","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"9e6656944644e285797b26f4c270b5461173b7c461b96043ddeb23637c576e3d","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"The primary purpose of this release is to support [Redis Enterprise 7.4.2](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-54), which is a major update to Redis Enterprise Software. As such, we have limited the scope of changes to focus on supporting RS 7.4 changes, in addition to implementing a few enhancements and major fixes."},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"enhancements","title":"Enhancements","role":"content","text":"- Added RESP3 support in K8s APIs (RED-94601)\n- Added to [supported distributions](#supported-distributions)\n- Added support for Redis Enterprise Software 7.4.2\n- Enhanced resilience of operators to misconfigured REDB namespaces (RED-113908)\n- Both the operator and services rigger are now based on UBI9 (RED-116083)"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- Fixed the operator to prevent reconciliation of non-REDB custom resources in observed namespaces. This eliminates the need for unnecessary permission rules. (RED-87790)\n- Fixed excessive updates to ingresses and services. (RED-109289)\n- Fixed update issues in rigger after the upgrade in multi-namespace environments. (RED-111732)\n- Fixed Active-Active sharding configuration by adding a new field. (RED-112909)\n- Fixed operator CVE. (RED-115157)\n- Fixed a race condition that could have rarely caused the cluster upgrade to break. (RED-118940)"},{"id":"api-changes","title":"API changes","role":"content","text":"| **CRD** | **Field** | **Change** | **Description** |\n|---|---|---|---|\n| REC | resp3Default | Add | Boolean controlling whether Resp3 should be enabled by default |\n| REDB | resp3 | Add | Controls resp3 for specific REDBs |"},{"id":"version-changes","title":"Version changes","role":"compatibility","text":"For a list of fixes related to CVEs, see the [Redis Enterprise 7.4.2-54 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-54#security)."},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"The following changes included in this release affect the upgrade process. Please read carefully before upgrading.\n\n#### Upgrade operating system\n\n> [!WARNING]\n> If your databases use modules, you need to update all nodes in the cluster to Redis Enterprise 7.2.4 or later before upgrading your operating system. See [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os)in the Redis Enterprise Software documentation for more details.\n\n#### terminationGracePeriodSeconds\n\nThe configurable `terminationGracePeriodSeconds` REC field was added in version 7.2.4-12 to replace the hard-coded timeout of 15 minutes for a node to be stopped or drained (RED-111471).\n\n#### ValidatingWebhookConfiguration\n\nVersions 6.4.2-4 and later include a new `ValidatingWebhookConfiguration` resource to replace the `redb-admission` webhook resource. To use releases 6.4.2-4 or later, delete the old webhook resource and apply the new file. See [upgrade Redis cluster](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#reapply-webhook) for instructions.\n\n#### OpenShift SCC\n\nVersions 6.4.2-6 and later include a new SCC (`redis-enterprise-scc-v2`) that you need to bind to your service account before upgrading. OpenShift clusters running version 6.2.12 or earlier upgrading to version 6.2.18 or later might get stuck if you skip this step. See [upgrade a Redis Enterprise cluster (REC)](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#before-upgrading) for instructions."},{"id":"deprecations","title":"Deprecations","role":"content","text":"- Redis Software for Kubernetes no longer supports the RHEL7 operating system. Migrate to RHEL8 to use this version."},{"id":"supported-distributions","title":"Supported distributions","role":"compatibility","text":"See the [7.4.2 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-2-releases) page for the list of supported distributions."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.4.2-54`\n- **Operator**: `redislabs/operator:7.4.2-2`\n- **Services Rigger**: `redislabs/k8s-controller:7.4.2-2`\n\n#### OpenShift images\n\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.4.2-54.rhel8-openshift`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.4.2-2`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.4.2-2`\n\n#### OLM bundle\n\n**Redis Enterprise operator bundle** : `v7.4.2-2`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See the [7.4.2 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-2-releases) page for the list of known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-4-2-releases","title":"Redis Enterprise for Kubernetes 7.4.2 release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-2-releases/","summary":"Releases with support for Redis Enterprise Software 7.4.2.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","children":[{"id":"operate/kubernetes/release-notes/7-4-2-releases/7-4-2-12","summary":"This is a feature release with a new version of Redis Enterprise Software support for persistent volume expansion.","title":"Redis Enterprise for Kubernetes 7.4.2-12 (May 2024) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-2-releases/7-4-2-12/"},{"id":"operate/kubernetes/release-notes/7-4-2-releases/7-4-2-03-24","summary":"This is a maintenance release supporting Redis Enterprise Software 7.4.2.","title":"Redis Enterprise for Kubernetes 7.4.2-2 (March 2024) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-2-releases/7-4-2-03-24/"},{"id":"operate/kubernetes/release-notes/7-4-2-releases/7-4-2-2","summary":"The primary purpose of this release is to support Redis Enterprise 7.4.2","title":"Redis Enterprise for Kubernetes 7.4.2-2 (Feb 2024) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-2-releases/7-4-2-2/"}],"page_type":"content","content_hash":"c13d23114d6a57f6b39c14e43f574596cf09f00dcb5c473f9730b37c1d169637","sections":[{"id":"detailed-release-notes","title":"Detailed release notes","role":"overview","text":"| Version (Release date) | Major changes |\n| --- | --- |\n| [7.4.2-12 (May 2024)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-2-releases/7-4-2-12/) | This is a feature release with a new version of Redis Enterprise Software support for persistent volume expansion. |\n| [7.4.2-2 (March 2024)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-2-releases/7-4-2-03-24/) | This is a maintenance release supporting Redis Enterprise Software 7.4.2. |\n| [7.4.2-2 (Feb 2024)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-2-releases/7-4-2-2/) | The primary purpose of this release is to support Redis Enterprise 7.4.2 |"},{"id":"version-changes","title":"Version changes","role":"compatibility","text":"For a list of fixes related to CVEs, see the [Redis Enterprise 7.4.6-22 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases)."},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"The following changes included in this release affect the upgrade process. Please read carefully before upgrading.\n\n#### Upgrade operating system\n\n> [!WARNING]\n> If your databases use modules, you need to update all nodes in the cluster to Redis Enterprise 7.2.4 or later before upgrading your operating system. See [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os)in the Redis Enterprise Software documentation for more details.\n\n#### terminationGracePeriodSeconds\n\nThe configurable `terminationGracePeriodSeconds` REC field was added in version 7.2.4-12 to replace the hard-coded timeout of 15 minutes for a node to be stopped or drained (RED-111471).\n\n#### ValidatingWebhookConfiguration\n\nVersions 6.4.2-4 and later include a new `ValidatingWebhookConfiguration` resource to replace the `redb-admission` webhook resource. To use releases 6.4.2-4 or later, delete the old webhook resource and apply the new file. See [upgrade Redis cluster](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#reapply-webhook) for instructions.\n\n#### OpenShift SCC\n\nVersions 6.4.2-6 and later include a new SCC (`redis-enterprise-scc-v2`) that you need to bind to your service account before upgrading. OpenShift clusters running version 6.2.12 or earlier upgrading to version 6.2.18 or later might get stuck if you skip this step. See [upgrade a Redis Enterprise cluster (REC)](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#before-upgrading) for instructions."},{"id":"deprecations","title":"Deprecations","role":"content","text":"- Redis Software for Kubernetes no longer supports the RHEL7 operating system. Migrate to RHEL8 to use this version."},{"id":"supported-distributions","title":"Supported distributions","role":"compatibility","text":"The following table shows supported distributions at the time of this release. You can also find this list in [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions).\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – This distribution is supported for this version of Redis Enterprise Software for Kubernetes.\n\n<span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> Deprecated – This distribution is still supported for this version of Redis Enterprise Software for Kubernetes, but support will be removed in a future release.\n\n<span title=\"X icon\">&#x274c;</span> End of life – Support for this distribution ended.\n\nAny distribution not listed below is not supported for production workloads.\n\n| **Kubernetes version** | **1.24** | **1.25** | **1.26** | **1.27** | **1.28** | **1.29** |\n|---|---|---|---|---|---|---|\n| **Community Kubernetes** | <span title=\"X icon\">&#x274c;</span> | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Amazon EKS** | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |\n| **Azure AKS** | <span title=\"X icon\">&#x274c;</span> | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |\n| **Google GKE** | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |\n| **Rancher RKE2** | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |\n| **VMware TKG 2.2** | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> |  |  |  |  |\n| **VMware TKG 2.3** | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |  |\n| **VMware TKG 2.4** |  | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |\n| **OpenShift version** | **4.11** | **4.12** | **4.13** | **4.14** |  |  |\n|  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |\n| **VMware TKGI version** | **1.15** | **1.16** | **1.17** | **1.18** |  |  |\n|  | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"The limitations below are specific to the Redis Enterprise for Kubernetes product. To view limitations for the supported Redis Enterprise Software version, see the [7.4.2 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases#known-limitations)."},{"id":"new-limitations","title":"New limitations","role":"content","text":"- **Missing endpoint for admission endpoint (rare) (RED-119469)** Restart the operator pod.\n- **The REDB \"redisVersion\" field can’t be used for memcached databases(RED-119152)**"},{"id":"existing-limitations","title":"Existing limitations","role":"content","text":"- **When modifying the database suffix for an Active-Active database, while the service-rigger is in a terminating state, the services-rigger will delete and create the ingress or route resources in a loop (RED-107687)** Wait until the services- rigger pod has finished to terminate it.\n\n- **REAADB changes might fail with \"gateway timeout\" errors, mostly on OpenShift (RED-103048)** Retry the operation.\n\n- **Creating two databases with the same name directly on Redis Enterprise software will cause the service to be deleted and the database will not be available (RED-99997)** Avoid duplicating database names. Database creation via K8s has validation in place to prevent this.\n\n- **Installing the operator bundle produces warning: `Warning: would violate PodSecurity \"restricted: v1.24\"` (RED-97381)** Ignore the warning. This issue is documented as benign on official Red Hat documentation.\n\n- **RERC resources must have a unique name (RED-96302)** The string \"rec-name\"/\"rec-namespace\" must be different from all other participating clusters in the Active-Active database.\n\n- **Admission is not blocking REAADB with `shardCount` which exceeds license quota (RED-96301)** Fix the problems with the REAADB and reapply.\n\n- **Active-Active controller only supports global database options. Configuration specific to location is not supported (RED-86490)**\n\n- **Active-Active setup removal might keep services or routes undeleted (RED-77752)** Delete services or routes manually if you encounter this problem.\n\n- **`autoUpgrade` set to `true` can cause unexpected bdb upgrades when `redisUpgradePolicy` is set to `true` (RED-72351)** Contact support if your deployment is impacted.\n\n- **Following the previous quick start guide version causes issues with creating an REDB due to unrecognized memory field name (RED-69515)** The workaround is to use the newer (current) revision of Deploy Redis Enterprise Software for Kubernetes.\n\n- **PVC size issues when using decimal value in spec (RED-62132)** Make sure you use integer values for the PVC size.\n\n- **REC might report error states on initial startup (RED-61707)** There is no workaround at this time except to ignore the errors.\n\n- **Hashicorp Vault integration - no support for Gesher (RED-55080)** There is no workaround for this issue. Gesher support has been deprecated.\n\n- **REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED-47254)** When REC clusters are deployed on Kubernetes clusters without synchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes.\n\n- **Deleting an OpenShift project with an REC deployed may hang (RED-47192)** When an REC cluster is deployed in a project (namespace) and has REDB resources, the REDB resources must be deleted first before the REC can be deleted. Therefore, until the REDB resources are deleted, the project deletion will hang. The fix is to delete the REDB resources first and the REC second. Then, you can delete the project.\n\n- **Clusters must be named 'rec' in OLM-based deployments (RED-39825)** In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) are bound to a specific service account name (namely, \"rec\"). The workaround is to name the cluster \"rec\".\n\n- **Readiness probe incorrect on failures (RED-39300)** STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure.\n\n- **Internal DNS and Kubernetes DNS may have conflicts (RED-37462)** DNS conflicts are possible between the cluster `mdns_server` and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names.\n\n- **K8s-based 5.4.10 clusters seem to negatively affect existing 5.4.6 clusters (RED-37233)** Upgrade clusters to latest version.\n\n- **Node CPU usage is reported instead of pod CPU usage (RED-36884)** In Kubernetes, the reported node CPU usage is the usage of the Kubernetes worker node hosting the REC pod.\n\n- **An unreachable cluster has status running (RED-32805)** When a cluster is in an unreachable state, the state remains `running` instead of triggering an error.\n\n- **Long cluster names cause routes to be rejected  (RED-25871)** A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name exceeds 63 characters. The workaround is to limit the cluster name to 20 characters or fewer.\n\n- **Cluster CR (REC) errors are not reported after invalid updates (RED-25542)** A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-4-6-releases/7-4-6-11-august2026","title":"Redis Software for Kubernetes 7.4.6-11 (August 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/7-4-6-11-august2026/","summary":"This is a maintenance release to support Redis Software version 7.4.6-283.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"97045bfc7b742ce954747c28da8ae75018c2e73ce15145f07b6e858db6a30d65","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support [Redis Software version 7.4.6-283](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases). It fixes a high-severity bug with security implications that affected Active-Active databases. For version changes, supported distributions, and known limitations, see the [release notes for 7.4.6](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases)."},{"id":"security","title":"Security","role":"security","text":"A high-severity bug with security implications affected Active-Active databases managed by the Redis Software for Kubernetes operator. **Upgrade to operator version 7.4.6-11 or later on this release line to get the fix.**\n\nWhen the operator applied a configuration update to an Active-Active database, it rebuilt and sent the database's entire configuration, but only re-read a credential from its Kubernetes secret if it believed that specific secret had changed. Any update triggered by something else therefore sent the password as empty, and on operator versions before 8.2.0-12, also cleared the mutual TLS (mTLS) client certificates.\n\nEither of the following triggered such an update:\n\n- Any configuration change on the Active-Active database, such as eviction policy, memory size, or backup settings, made by a user or by automation tooling such as GitOps.\n- A modification of the backup secret or a client certificate secret on the cluster, even with no configuration change. The operator re-read the touched secret correctly but wiped the password, because its secret was not the one that changed.\n\nThe impact was two-sided:\n\n- **Availability**: applications connecting with the password failed on every new connection. Existing connections kept working, so the failure looked intermittent.\n- **Security**: the database simultaneously accepted connections with no credentials at all, and with the mTLS variant, accepted clients without certificates. Nothing alerted and the database reported healthy, so a database could run exposed without anyone noticing.\n\nThis affected all released operator versions since the feature was introduced in early 2023. The fix prevents the credential wipe and automatically repairs already-affected databases when you upgrade the operator. No additional steps are required.\n\nThe fix is available in operator versions 7.4.6-11, 7.8.6-20, 7.22.2-45, 8.0.20-27, and 8.2.0-15. <!--RED-212963--> <!--RED-213921-->"},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.4.6-283`\n- **Operator**: `redislabs/operator:7.4.6-11`\n- **Services Rigger**: `redislabs/k8s-controller:7.4.6-11`"},{"id":"openshift-images","title":"OpenShift images","role":"content","text":"- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.4.6-283.rhel8-openshift`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.4.6-11`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.4.6-11`"},{"id":"olm-bundle","title":"OLM bundle","role":"content","text":"**Redis Enterprise operator bundle**: `v7.4.6-11.0`"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-4-6-releases/7-4-6-12-october2026","title":"Redis Software for Kubernetes 7.4.6-12 (October 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/7-4-6-12-october2026/","summary":"This is a maintenance release to support Redis Software version 7.4.6-293.","tags":["docs","operate","kubernetes"],"last_updated":"2026-10-09T10:00:27-05:00","page_type":"content","content_hash":"bcc36a971a768e7965f7fc4b0a118ee9d5c0a47f3ffacc7dac16ddd4b13e42c4","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support [Redis Software version 7.4.6-293](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases). For version changes, supported distributions, and known limitations, see the [release notes for 7.4.6](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases)."},{"id":"enhancements","title":"Enhancements","role":"content","text":"- The operator now defers automatic REAADB repairs while a participating cluster is unhealthy or unreachable <!--RED-218555-->"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- Fixed REAADB creation failing with a 406 error when `tlsMode` is `enabled` and the spec doesn't set `clientAuthenticationCertificates` <!--RED-217047-->"},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.4.6-293`\n- **Operator**: `redislabs/operator:7.4.6-12`\n- **Services Rigger**: `redislabs/k8s-controller:7.4.6-12`"},{"id":"openshift-images","title":"OpenShift images","role":"content","text":"- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.4.6-293.rhel8-openshift`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.4.6-12`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.4.6-12`"},{"id":"olm-bundle","title":"OLM bundle","role":"content","text":"**Redis Enterprise operator bundle**: `v7.4.6-12.0`"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-4-6-releases/7-4-6-2","title":"Redis Enterprise for Kubernetes 7.4.6-2 (July 2024) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/7-4-6-2/","summary":"This is a feature release with a new version of Redis Enterprise Software 7.4.6.","aliases":["/operate/kubernetes/release-notes/7-4-6-2,","/operate/kubernetes/release-notes/7-4-6-2/"],"tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"c04ee3611a240d676980e36887a6d60039ae605177252e1e6fd7434a9cd96fd6","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"The primary purpose of this release is to support [Redis Enterprise Software version 7.4.6](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-22). It also includes updates to supported platform distributions."},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"enhancements","title":"Enhancements","role":"content","text":"- Removal of multiple unneeded permissions from RBAC roles used by the operator (RED-123478 RED-125020)\n- Support for non-AWS S3 compatible backup targets in REC (RED-73642)\n- Support for Redis Enterprise 7.4.6-22 (RED-13110)"},{"id":"api-changes","title":"API changes","role":"content","text":"| **CRD** | **Field** | **Change** | **Description** |\n|---|---|---|---|\n| REC | backup.s3.caCertificateSecretName | Add | Secret name that holds the S3 CA certificate, which contains the TLS certificate mapped to the key in the secret 'cert' |\n| REC | backup.s3.url | Add | Specifies the URL for S3 export and import (used for custom S3 compatible servers) |"},{"id":"version-changes","title":"Version changes","role":"compatibility","text":"For a list of fixes related to CVEs, see the [Redis Enterprise 7.2.4-109 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-22)."},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"The following changes included in this release affect the upgrade process. Please read carefully before upgrading.\n\n#### ValidatingWebhookConfiguration\n\nVersions 6.4.2-4 and later include a new `ValidatingWebhookConfiguration` resource to replace the `redb-admission` webhook resource. To use releases 6.4.2-4 or later, delete the old webhook resource and apply the new file. See [upgrade Redis cluster](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#reapply-webhook) for instructions.\n\n#### OpenShift SCC\n\nVersions 6.4.2-6 and later include a new SCC (`redis-enterprise-scc-v2`) that you need to bind to your service account before upgrading. OpenShift clusters running version 6.2.12 or earlier upgrading to version 6.2.18 or later might get stuck if you skip this step. See [upgrade a Redis Enterprise cluster (REC)](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#before-upgrading) for instructions.\n\n#### Upcoming Changes\n\n- Future Redis Enterprise images will be UBI9-based only, without support for Ubuntu-based images."},{"id":"supported-distributions","title":"Supported distributions","role":"compatibility","text":"The following table shows supported distributions at the time of this release. You can also find this list in [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions).\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – This distribution is supported for this version of Redis Enterprise Software for Kubernetes.\n\n<span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> Deprecated – This distribution is still supported for this version of Redis Enterprise Software for Kubernetes, but support will be removed in a future release.\n\n<span title=\"X icon\">&#x274c;</span> End of life – Support for this distribution ended.\n\nAny distribution not listed below is not supported for production workloads.\n\n| **Kubernetes version** | **1.25** | **1.26** | **1.27** | **1.28** | **1.29** | **1.30** |\n|---|---|---|---|---|---|---|\n| **Community Kubernetes** |  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Amazon EKS** | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |\n| **Azure AKS** |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Google GKE** | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Rancher RKE2** | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |\n| **VMware TKG 2.2** | <span title=\"X icon\">&#x274c;</span> |  |  |  |  |  |\n| **VMware TKG 2.3** | <span title=\"X icon\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |  |  |\n| **VMware TKG 2.4** | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |  |\n| **OpenShift version** | **4.12** | **4.13** | **4.14** | **4.15** | **4.16** |  |\n|  | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |\n| **VMware TKGI version** | **1.16** | **1.17** | **1.18** | **1.19** |  |  |\n|  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |"},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.4.6-22`\n- **Operator**: `redislabs/operator:7.4.6-2`\n- **Services Rigger**: `redislabs/k8s-controller:7.4.6-2`"},{"id":"openshift-images","title":"OpenShift images","role":"content","text":"- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.4.6-22.rhel8-openshift`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.4.6-2`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.4.6-2`"},{"id":"olm-bundle","title":"OLM bundle","role":"content","text":"**Redis Enterprise operator bundle** : `v7.4.6-2.0`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"- **Missing endpoint for admission endpoint (rare) (RED-119469)** Restart the operator pod.\n\n- **The REDB \"redisVersion\" field can’t be used for memcached databases(RED-119152)**\n\n- **PVC expansion is not supported when using Redis on Flash (Auto Tiering) (RED-165770)** Do not enable `enablePersistentVolumeResize` if your REC uses `redisOnFlashSpec` as this will result in conflicts.\n\n- **When modifying the database suffix for an Active-Active database, while the service-rigger is in a terminating state, the services-rigger will delete and create the ingress or route resources in a loop (RED-107687)** Wait until the services rigger pod has finished to terminate it.\n\n- **REAADB changes might fail with \"gateway timeout\" errors, mostly on OpenShift (RED-103048)** Retry the operation.\n\n- **Creating two databases with the same name directly on Redis Enterprise software will cause the service to be deleted and the database will not be available (RED-99997)** Avoid duplicating database names. Database creation via K8s has validation in place to prevent this.\n\n- **Installing the operator bundle produces warning: `Warning: would violate PodSecurity \"restricted: v1.24\"` (RED-97381)** Ignore the warning. This issue is documented as benign on official Red Hat documentation.\n\n- **RERC resources must have a unique name (RED-96302)** The string \"rec-name\"/\"rec-namespace\" must be different from all other participating clusters in the Active-Active database.\n\n- **Admission is not blocking REAADB with `shardCount` which exceeds license quota (RED-96301)** Fix the problems with the REAADB and reapply.\n\n- **Active-Active controller only supports global database options. Configuration specific to location is not supported (RED-86490)**\n\n- **Active-Active setup removal might keep services or routes undeleted (RED-77752)** Delete services or routes manually if you encounter this problem.\n\n- **`autoUpgrade` set to `true` can cause unexpected bdb upgrades when `redisUpgradePolicy` is set to `true` (RED-72351)** Contact support if your deployment is impacted.\n\n- **Following the previous quick start guide version causes issues with creating an REDB due to unrecognized memory field name (RED-69515)** The workaround is to use the newer (current) revision of Deploy Redis Enterprise Software for Kubernetes.\n\n- **PVC size issues when using decimal value in spec (RED-62132)** Make sure you use integer values for the PVC size.\n\n- **REC might report error states on initial startup (RED-61707)** There is no workaround at this time except to ignore the errors.\n\n- **Hashicorp Vault integration - no support for Gesher (RED-55080)** There is no workaround for this issue. Gesher support has been deprecated.\n\n- **REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED-47254)** When REC clusters are deployed on Kubernetes clusters without synchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes.\n\n- **Deleting an OpenShift project with an REC deployed may hang (RED-47192)** When an REC cluster is deployed in a project (namespace) and has REDB resources, the REDB resources must be deleted first before the REC can be deleted. Therefore, until the REDB resources are deleted, the project deletion will hang. The fix is to delete the REDB resources first and the REC second. Then, you can delete the project.\n\n- **Clusters must be named 'rec' in OLM-based deployments (RED-39825)** In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) are bound to a specific service account name (namely, \"rec\"). The workaround is to name the cluster \"rec\".\n\n- **Readiness probe incorrect on failures (RED-39300)** STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure.\n\n- **Internal DNS and Kubernetes DNS may have conflicts (RED-37462)** DNS conflicts are possible between the cluster `mdns_server` and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names.\n\n- **K8s-based 5.4.10 clusters seem to negatively affect existing 5.4.6 clusters (RED-37233)** Upgrade clusters to latest version.\n\n- **Node CPU usage is reported instead of pod CPU usage (RED-36884)** In Kubernetes, the reported node CPU usage is the usage of the Kubernetes worker node hosting the REC pod.\n\n- **An unreachable cluster has status running (RED-32805)** When a cluster is in an unreachable state, the state remains `running` instead of triggering an error.\n\n- **Long cluster names cause routes to be rejected  (RED-25871)** A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name exceeds 63 characters. The workaround is to limit the cluster name to 20 characters or fewer.\n\n- **Cluster CR (REC) errors are not reported after invalid updates (RED-25542)** A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-4-6-releases/7-4-6-2-oct24","title":"Redis Enterprise for Kubernetes 7.4.6-2 (October 2024) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/7-4-6-2-oct24/","summary":"This is a maintenance release with a new version of Redis Enterprise Software 7.4.6.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"14cc8048ae372594bfc6dbe70d270873426c841c5fad2a502f1bb80a15676122","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support [Redis Enterprise Software version 7.4.6-77](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-77). For version changes, supported distributions, and known limitations, see the [release notes for 7-4-6-2 (July 2024)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/7-4-6-2)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.4.6-77`\n- **Operator**: `redislabs/operator:7.4.6-2`\n- **Services Rigger**: `redislabs/k8s-controller:7.4.6-2`"},{"id":"openshift-images","title":"OpenShift images","role":"content","text":"- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.4.6-77.rhel8-openshift`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.4.6-2`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.4.6-2`"},{"id":"olm-bundle","title":"OLM bundle","role":"content","text":"**Redis Enterprise operator bundle** : `v7.4.6-2.3`"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-4-6-releases/7-4-6-2-october24","title":"Redis Enterprise for Kubernetes 7.4.6-2 (October 2024) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/7-4-6-2-october24/","summary":"This is a maintenance release with a new version of Redis Enterprise Software 7.4.6.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"fdd423614c5f44ca12655406f27e434ee6fc86346a0c4606c9836bb6fdd9a45e","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support [Redis Enterprise Software version 7.4.6-102](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases). For version changes, supported distributions, and known limitations, see the [release notes for 7-4-6-2 (July 2024)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/7-4-6-2)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.4.6-102`\n- **Operator**: `redislabs/operator:7.4.6-2`\n- **Services Rigger**: `redislabs/k8s-controller:7.4.6-2`"},{"id":"openshift-images","title":"OpenShift images","role":"content","text":"- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.4.6-102.rhel8-openshift`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.4.6-2`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.4.6-2`"},{"id":"olm-bundle","title":"OLM bundle","role":"content","text":"**Redis Enterprise operator bundle** : `v7.4.6-2.4`\n\nThe OLM version v7.4.6-2.4 replaces the earlier v7.4.6-2.3 release for the same Redis software version, providing only upgrade path fixes."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-4-6-releases/7-4-6-6-dec24","title":"Redis Enterprise for Kubernetes 7.4.6-6 (Dec 2024) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/7-4-6-6-dec24/","summary":"This is a maintenance release to support Redis Enterprise Software version 7.4.6-102 and contains a suggested fix needed to upgrade to newer versions.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"6d8ef4e4f8f0a0af15347f12db8d32015b5a438bb6f70a1e21ef12dc49643d3e","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support [Redis Enterprise Software version 7.4.6-102](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-102). This version also contains a suggested fix needed to upgrade to newer versions. For version changes, supported distributions, and known limitations, see the [release notes for 7-4-6-2 (July 2024)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/7-4-6-2)."},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- Fixed service rigger crashing after REC upgrade (RED-144785)"},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.4.6-102`\n- **Operator**: `redislabs/operator:7.4.6-6`\n- **Services Rigger**: `redislabs/k8s-controller:7.4.6-6`"},{"id":"openshift-images","title":"OpenShift images","role":"content","text":"- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.4.6-102.rhel8-openshift`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.4.6-6`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.4.6-6`\n- **OLM operator bundle** : `v7.4.6-6.1`"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-4-6-releases/7-4-6-6-june25","title":"Redis Enterprise for Kubernetes 7.4.6-6 (June 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/7-4-6-6-june25/","summary":"This is a maintenance release to support Redis Enterprise Software version 7.4.6-268.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"aa246024a3751d3f04236588f97d7bd5696f2155b73e1d89b863e6674916bec7","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support [Redis Enterprise Software version 7.4.6-268](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-268). For version changes, supported distributions, and known limitations, see the [release notes for 7.4.6](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.4.6-268`\n- **Operator**: `redislabs/operator:7.4.6-6`\n- **Services Rigger**: `redislabs/k8s-controller:7.4.6-6`"},{"id":"openshift-images","title":"OpenShift images","role":"content","text":"- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.4.6-268.rhel8-openshift`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.4.6-6`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.4.6-6`\n- **OLM operator bundle**: `v7.4.6-6.4`"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-4-6-releases/7-4-6-6-mar25","title":"Redis Enterprise for Kubernetes 7.4.6-6 (March 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/7-4-6-6-mar25/","summary":"This is a maintenance release to support Redis Enterprise Software version 7.4.6-233 (7.4.6-232 for OpenShift).","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"9adbe79a41f370918ebbd9e8a314db98f5ff8b6b935a9139e38a30d406fed3ed","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support [Redis Enterprise Software version 7.4.6-232](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-232). For version changes, supported distributions, and known limitations, see the [release notes for 7.4.6](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases).\n\n> [!WARNING]\n> If you are not using OpenShift, you must use the `7.4.6-233` Redis Enterprise image listed below. The `7.4.6-232` image only supports OpenShift, and may cause the upgrade to fail if used with other distributions.\n>\n> To upgrade successfully, use the correct Redis Enterprise image for your K8s distribution.\n>\n> - OpenShift: `registry.connect.redhat.com/redislabs/redis-enterprise:7.4.6-232.rhel8-openshift`\n> - Non-OpenShift distributions: `redislabs/redis:7.4.6-233`"},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.4.6-233`\n- **Operator**: `redislabs/operator:7.4.6-6`\n- **Services Rigger**: `redislabs/k8s-controller:7.4.6-6`"},{"id":"openshift-images","title":"OpenShift images","role":"content","text":"- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.4.6-232.rhel8-openshift`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.4.6-6`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.4.6-6`\n- **OLM operator bundle**: `v7.4.6-6.3`"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-4-6-releases/7-4-6-7-dec25","title":"Redis Enterprise for Kubernetes 7.4.6-7 (December 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/7-4-6-7-dec25/","summary":"This is a maintenance release to support Redis Enterprise Software version 7.4.6-275.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"5696259ae396ddc52946bd443590064efcaad930ca805a081f97abd7ef4acc6d","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support Redis Enterprise Software version 7.4.6-275. For version changes, supported distributions, and known limitations, see the [release notes for 7.4.6](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.4.6-275`\n- **Operator**: `redislabs/operator:7.4.6-7`\n- **Services Rigger**: `redislabs/k8s-controller:7.4.6-7`"},{"id":"openshift-images","title":"OpenShift images","role":"content","text":"- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.4.6-275.rhel8-openshift`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.4.6-7`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.4.6-7`\n- **OLM operator bundle**: `7.4.6-7.5`"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-4-6-releases/7-4-6-7-sep25","title":"Redis Enterprise for Kubernetes 7.4.6-7 (September 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/7-4-6-7-sep25/","summary":"This is a maintenance release to support Redis Enterprise Software version 7.4.6-272.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"9dddae2c97360558778bb39db669d0aeb7fe024ec783765ec8fb96b730030eb4","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support [Redis Enterprise Software version 7.4.6-272](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-272). For version changes, supported distributions, and known limitations, see the [release notes for 7.4.6](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.4.6-272`\n- **Operator**: `redislabs/operator:7.4.6-7`\n- **Services Rigger**: `redislabs/k8s-controller:7.4.6-7`"},{"id":"openshift-images","title":"OpenShift images","role":"content","text":"- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.4.6-272.rhel8-openshift`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.4.6-7`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.4.6-7`\n- **OLM operator bundle**: `v7.4.6-7.2`"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-4-6-releases/7-4-6-9-feb2026","title":"Redis Enterprise for Kubernetes 7.4.6-9 (February 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/7-4-6-9-feb2026/","summary":"This is a maintenance release to support Redis Enterprise Software version 7.4.6-279.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"2fdbde46497a5a9c9968335bcda8d1d1e0c022e8db65028ae35c5902168d3e48","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support [Redis Enterprise Software version 7.4.6-279](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases). For version changes, supported distributions, and known limitations, see the [release notes for 7.4.6](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.4.6-279`\n- **Operator**: `redislabs/operator:7.4.6-9`\n- **Services Rigger**: `redislabs/k8s-controller:7.4.6-9`"},{"id":"openshift-images","title":"OpenShift images","role":"content","text":"- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.4.6-279.rhel8-openshift`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.4.6-9`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.4.6-9`"},{"id":"olm-bundle","title":"OLM bundle","role":"content","text":"**Redis Enterprise operator bundle**: `v7.4.6-9.0`"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-4-6-releases","title":"Redis Enterprise for Kubernetes 7.4.6 release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/","summary":"Releases with support for Redis Enterprise Software 7.4.6.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","children":[{"id":"operate/kubernetes/release-notes/7-4-6-releases/7-4-6-12-october2026","summary":"This is a maintenance release to support Redis Software version 7.4.6-293.","title":"Redis Software for Kubernetes 7.4.6-12 (October 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/7-4-6-12-october2026/"},{"id":"operate/kubernetes/release-notes/7-4-6-releases/7-4-6-11-august2026","summary":"This is a maintenance release to support Redis Software version 7.4.6-283.","title":"Redis Software for Kubernetes 7.4.6-11 (August 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/7-4-6-11-august2026/"},{"id":"operate/kubernetes/release-notes/7-4-6-releases/7-4-6-9-feb2026","summary":"This is a maintenance release to support Redis Enterprise Software version 7.4.6-279.","title":"Redis Enterprise for Kubernetes 7.4.6-9 (February 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/7-4-6-9-feb2026/"},{"id":"operate/kubernetes/release-notes/7-4-6-releases/7-4-6-7-dec25","summary":"This is a maintenance release to support Redis Enterprise Software version 7.4.6-275.","title":"Redis Enterprise for Kubernetes 7.4.6-7 (December 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/7-4-6-7-dec25/"},{"id":"operate/kubernetes/release-notes/7-4-6-releases/7-4-6-7-sep25","summary":"This is a maintenance release to support Redis Enterprise Software version 7.4.6-272.","title":"Redis Enterprise for Kubernetes 7.4.6-7 (September 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/7-4-6-7-sep25/"},{"id":"operate/kubernetes/release-notes/7-4-6-releases/7-4-6-6-june25","summary":"This is a maintenance release to support Redis Enterprise Software version 7.4.6-268.","title":"Redis Enterprise for Kubernetes 7.4.6-6 (June 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/7-4-6-6-june25/"},{"id":"operate/kubernetes/release-notes/7-4-6-releases/7-4-6-6-mar25","summary":"This is a maintenance release to support Redis Enterprise Software version 7.4.6-233 (7.4.6-232 for OpenShift).","title":"Redis Enterprise for Kubernetes 7.4.6-6 (March 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/7-4-6-6-mar25/"},{"id":"operate/kubernetes/release-notes/7-4-6-releases/7-4-6-6-dec24","summary":"This is a maintenance release to support Redis Enterprise Software version 7.4.6-102 and contains a suggested fix needed to upgrade to newer versions.","title":"Redis Enterprise for Kubernetes 7.4.6-6 (Dec 2024) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/7-4-6-6-dec24/"},{"id":"operate/kubernetes/release-notes/7-4-6-releases/7-4-6-2-october24","summary":"This is a maintenance release with a new version of Redis Enterprise Software 7.4.6.","title":"Redis Enterprise for Kubernetes 7.4.6-2 (October 2024) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/7-4-6-2-october24/"},{"id":"operate/kubernetes/release-notes/7-4-6-releases/7-4-6-2-oct24","summary":"This is a maintenance release with a new version of Redis Enterprise Software 7.4.6.","title":"Redis Enterprise for Kubernetes 7.4.6-2 (October 2024) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/7-4-6-2-oct24/"},{"id":"operate/kubernetes/release-notes/7-4-6-releases/7-4-6-2","summary":"This is a feature release with a new version of Redis Enterprise Software 7.4.6.","title":"Redis Enterprise for Kubernetes 7.4.6-2 (July 2024) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/7-4-6-2/"}],"page_type":"content","content_hash":"75d5f63a2ba2bf934e53936b5c8b290b205ded2e5159c24a6d5017161fa18d80","sections":[{"id":"detailed-release-notes","title":"Detailed release notes","role":"overview","text":"| Version (Release date) | Major changes |\n| --- | --- |\n| [7.4.6-12 (October 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/7-4-6-12-october2026/) | This is a maintenance release to support Redis Software version 7.4.6-293. |\n| [7.4.6-11 (August 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/7-4-6-11-august2026/) | This is a maintenance release to support Redis Software version 7.4.6-283. |\n| [7.4.6-9 (February 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/7-4-6-9-feb2026/) | This is a maintenance release to support Redis Enterprise Software version 7.4.6-279. |\n| [7.4.6-7 (December 2025)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/7-4-6-7-dec25/) | This is a maintenance release to support Redis Enterprise Software version 7.4.6-275. |\n| [7.4.6-7 (September 2025)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/7-4-6-7-sep25/) | This is a maintenance release to support Redis Enterprise Software version 7.4.6-272. |\n| [7.4.6-6 (June 2025)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/7-4-6-6-june25/) | This is a maintenance release to support Redis Enterprise Software version 7.4.6-268. |\n| [7.4.6-6 (March 2025)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/7-4-6-6-mar25/) | This is a maintenance release to support Redis Enterprise Software version 7.4.6-233 (7.4.6-232 for OpenShift). |\n| [7.4.6-6 (Dec 2024)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/7-4-6-6-dec24/) | This is a maintenance release to support Redis Enterprise Software version 7.4.6-102 and contains a suggested fix needed to upgrade to newer versions. |\n| [7.4.6-2 (October 2024)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/7-4-6-2-october24/) | This is a maintenance release with a new version of Redis Enterprise Software 7.4.6. |\n| [7.4.6-2 (October 2024)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/7-4-6-2-oct24/) | This is a maintenance release with a new version of Redis Enterprise Software 7.4.6. |\n| [7.4.6-2 (July 2024)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/7-4-6-2/) | This is a feature release with a new version of Redis Enterprise Software 7.4.6. |"},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"The following changes included in this release affect the upgrade process. Please read carefully before upgrading."},{"id":"validatingwebhookconfiguration","title":"ValidatingWebhookConfiguration","role":"content","text":"Versions 6.4.2-4 and later include a new `ValidatingWebhookConfiguration` resource to replace the `redb-admission` webhook resource. To use releases 6.4.2-4 or later, delete the old webhook resource and apply the new file. See [upgrade Redis cluster](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#reapply-webhook) for instructions."},{"id":"openshift-scc","title":"OpenShift SCC","role":"content","text":"Versions 6.4.2-6 and later include a new SCC (`redis-enterprise-scc-v2`) that you need to bind to your service account before upgrading. If you skip this step, OpenShift clusters running version 6.2.12 or earlier might get stuck when upgrading to version 6.2.18 or later. See [upgrade a Redis Enterprise cluster (REC)](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#before-upgrading) for instructions."},{"id":"upcoming-changes","title":"Upcoming Changes","role":"content","text":"- Future Redis Enterprise images will be UBI9-based only, without support for Ubuntu-based images."},{"id":"security","title":"Security","role":"security","text":"For a list of fixes related to CVEs, see the [Redis Enterprise 7.2.4-109 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases)."},{"id":"supported-distributions","title":"Supported distributions","role":"compatibility","text":"The following table shows supported distributions at the time of this release. You can also find this list in [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions).\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – This distribution is supported for this version of Redis Enterprise Software for Kubernetes.\n\n<span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> Deprecated – This distribution is still supported for this version of Redis Enterprise Software for Kubernetes, but support will be removed in a future release.\n\n<span title=\"X icon\">&#x274c;</span> End of life – Support for this distribution ended.\n\nAny distribution not listed below is not supported for production workloads.\n\n| **Kubernetes version** | **1.25** | **1.26** | **1.27** | **1.28** | **1.29** | **1.30** |\n|---|---|---|---|---|---|---|\n| **Community Kubernetes** |  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Amazon EKS** | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |\n| **Azure AKS** |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Google GKE** | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Rancher RKE2** | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |\n| **VMware TKG 2.2** | <span title=\"X icon\">&#x274c;</span> |  |  |  |  |  |\n| **VMware TKG 2.3** | <span title=\"X icon\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |  |  |\n| **VMware TKG 2.4** | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |  |\n| **OpenShift version** | **4.12** | **4.13** | **4.14** | **4.15** | **4.16** |  |\n|  | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |\n| **VMware TKGI version** | **1.16** | **1.17** | **1.18** | **1.19** |  |  |\n|  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"- **Missing endpoint for admission endpoint (rare) (RED-119469)** Restart the operator pod.\n\n- **The REDB \"redisVersion\" field can’t be used for memcached databases(RED-119152)**\n\n- **When modifying the database suffix for an Active-Active database, while the service-rigger is in a terminating state, the services-rigger will delete and create the ingress or route resources in a loop (RED-107687)** Wait until the services rigger pod has finished to terminate it.\n\n- **REAADB changes might fail with \"gateway timeout\" errors, mostly on OpenShift (RED-103048)** Retry the operation.\n\n- **Creating two databases with the same name directly on Redis Enterprise software will cause the service to be deleted and the database will not be available (RED-99997)** Avoid duplicating database names. Database creation via K8s has validation in place to prevent this.\n\n- **Installing the operator bundle produces warning: `Warning: would violate PodSecurity \"restricted: v1.24\"` (RED-97381)** Ignore the warning. This issue is documented as benign on official Red Hat documentation.\n\n- **RERC resources must have a unique name (RED-96302)** The string \"rec-name\"/\"rec-namespace\" must be different from all other participating clusters in the Active-Active database.\n\n- **Admission is not blocking REAADB with `shardCount` which exceeds license quota (RED-96301)** Fix the problems with the REAADB and reapply.\n\n- **Active-Active controller only supports global database options. Configuration specific to location is not supported (RED-86490)**\n\n- **Active-Active setup removal might keep services or routes undeleted (RED-77752)** Delete services or routes manually if you encounter this problem.\n\n- **`autoUpgrade` set to `true` can cause unexpected bdb upgrades when `redisUpgradePolicy` is set to `true` (RED-72351)** Contact support if your deployment is impacted.\n\n- **Following the previous quick start guide version causes issues with creating an REDB due to unrecognized memory field name (RED-69515)** The workaround is to use the newer (current) revision of Deploy Redis Enterprise Software for Kubernetes.\n\n- **PVC size issues when using decimal value in spec (RED-62132)** Make sure you use integer values for the PVC size.\n\n- **REC might report error states on initial startup (RED-61707)** There is no workaround at this time except to ignore the errors.\n\n- **Hashicorp Vault integration - no support for Gesher (RED-55080)** There is no workaround for this issue. Gesher support has been deprecated.\n\n- **REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED-47254)** When REC clusters are deployed on Kubernetes clusters without synchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes.\n\n- **Deleting an OpenShift project with an REC deployed may hang (RED-47192)** When an REC cluster is deployed in a project (namespace) and has REDB resources, the REDB resources must be deleted first before the REC can be deleted. Therefore, until the REDB resources are deleted, the project deletion will hang. The fix is to delete the REDB resources first and the REC second. Then, you can delete the project.\n\n- **Clusters must be named 'rec' in OLM-based deployments (RED-39825)** In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) are bound to a specific service account name (namely, \"rec\"). The workaround is to name the cluster \"rec\".\n\n- **Readiness probe incorrect on failures (RED-39300)** STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure.\n\n- **Internal DNS and Kubernetes DNS may have conflicts (RED-37462)** DNS conflicts are possible between the cluster `mdns_server` and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names.\n\n- **K8s-based 5.4.10 clusters seem to negatively affect existing 5.4.6 clusters (RED-37233)** Upgrade clusters to latest version.\n\n- **Node CPU usage is reported instead of pod CPU usage (RED-36884)** In Kubernetes, the reported node CPU usage is the usage of the Kubernetes worker node hosting the REC pod.\n\n- **An unreachable cluster has status running (RED-32805)** When a cluster is in an unreachable state, the state remains `running` instead of triggering an error.\n\n- **Long cluster names cause routes to be rejected  (RED-25871)** A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name exceeds 63 characters. The workaround is to limit the cluster name to 20 characters or fewer.\n\n- **Cluster CR (REC) errors are not reported after invalid updates (RED-25542)** A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-8-2-releases/7-8-2-6-jan25","title":"Redis Enterprise for Kubernetes 7.8.2-6 (Jan 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-2-releases/7-8-2-6-jan25/","summary":"Maintenance release to support Redis Enterprise Software version 7.8.2-60.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"5fb8d00ebd96fed7a777e4224aa46960da85bc51735ea93ed8f7254cce1d857f","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support [Redis Enterprise Software version 7.8.2-60](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-2-60). For version changes, supported distributions, and known limitations, see the [release notes for 7.8.2-6 (Nov 2024)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-2-releases/7-8-2-6-nov24)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.8.2-60`\n- **Operator**: `redislabs/operator:7.8.2-6`\n- **Services Rigger**: `redislabs/k8s-controller:7.8.2-6`\n- **OLM operator bundle** : `v7.8.2-6.8`"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-8-2-releases/7-8-2-6-nov24","title":"Redis Enterprise for Kubernetes 7.8.2-6 (Nov 2024) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-2-releases/7-8-2-6-nov24/","summary":"Feature release including support for Redis Software 7.8.2 and RHEL9-based images.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"21218190a12f1abd1303dc0ff963b98ecdc8ef914419fa1724aeb7fb8156b371","sections":[{"id":"new-in-this-release","title":"New in this release","role":"overview","text":""},{"id":"enhancements","title":"Enhancements","role":"content","text":"- Parallel startup and recovery (enabled by default) (RED-117645).\n- Azure marketplate deployment (RED-113029)\n- Support for [Redis Software 7.8.2-34](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-2-34).\n- Improved upgrade validations for modules and Redis database versions (RED-132197).\n- Support for Community Kubernetes 1.31 (RED-13549).\n- New minimal roles required to use the log collector (RED-132686).\n- Support proxy certificate updates for Active-Active databases (RED-122552).\n- Add `directory_timeout_s` field to LDAP configuration (RED-119079).\n- Allow configuring port number for replication endpoints in Active-Active databases (RED-113626)."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- Fixed documentation for REAADB `globalConfigurations` field (RED-138727).\n- Allow log collector execution without `kubectl` installed (RED-131537).\n- Fix recreation issue for rec-bulletin-board config map (RED-130599).\n- Avoid deletion of operator config maps created by users (RED-129214).\n- Fixed errors shown in bootstrapper logs (RED-125776).\n- Limit the number of calls made to Vault (RED-125396).\n- Fixed log collector issue with detecting version (RED-121144)."},{"id":"api-changes","title":"API changes","role":"content","text":"| **CRD** | **Field** | **Change** | **Description** |\n|---|---|---|---|\n| REC | `status.CertificatesUpdateStatus` | Rename | Renamed status field and changed the structure |\n| REC | `status.state` | Add value to enum  | `runningRollingUpgrade`  |\n| REDB | `spec.modulesList` | Field enabled | Don't need to set `ENABLE_ALPHA_FEATURES` to enable. |\n| RERC | `spec.apiPort` | Added |  |\n| REDB | `redb.upgradeSpec.upgradeModulesToLatest` | Changed default  | Default is now `true` |"},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"#### RHEL9-based images\n\n- As of version 7.8.2-6, Redis Enterprise images are based on Red Hat Enterprise Linux 9 (RHEL9). This means upgrades to 7.8.2-6 require:\n\n- Cluster version of 7.4.2-2 or later.\n- Database version 7.2 or later.\n- RHEL9 compatible binaries for any modules you need.\n\nSee [Upgrade Redis Enterprise for Kubernetes](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster) for detailed steps to upgrade to 7.8.2-6."},{"id":"deprecations","title":"Deprecations","role":"content","text":"- Ubuntu-based images are no longer supported.\n- REDB field `upgradeSpec.upgradeModulesToLatest is deprecated.\n- REDB field `spec.modulesList.version` is deprecated."},{"id":"supported-distributions","title":"Supported distributions","role":"compatibility","text":"The following table shows supported distributions at the time of this release. You can also find this list in [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions).\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – This distribution is supported for this version of Redis Enterprise Software for Kubernetes.\n\n<span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> Deprecated – This distribution is still supported for this version of Redis Enterprise Software for Kubernetes, but support will be removed in a future release.\n\n<span title=\"X icon\">&#x274c;</span> End of life – Support for this distribution ended.\n\nAny distribution not listed below is not supported for production workloads.\n\n| **Kubernetes version** | **1.25** | **1.26** | **1.27** | **1.28** | **1.29** | **1.30** | **1.31** |\n|---|---|---|---|---|---|---|---|\n| **Community Kubernetes** |  |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |\n| **Amazon EKS** |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |  |\n| **Azure AKS** |  |  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |  |\n| **Google GKE** |  |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |  |\n| **Rancher RKE2** |  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |  |  |  |\n| **VMware TKG 2.3** |  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> |  |  |  |  |  |\n| **VMware TKG 2.4** |  | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |  |  |  |  |\n| **OpenShift** | **4.12** | **4.13** | **4.14** | **4.15** | **4.16** |  |  |\n| **** | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |  |  |\n| **VMWare TKGI** | **1.16** | **1.17** | **1.18** | **1.19** |  |  |  |\n| **** | <span title=\"X icon\">&#x274c;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |  |  |  |"},{"id":"downloads","title":"Downloads","role":"content","text":"**Redis Enterprise**: `redislabs/redis:7.8.2-34`\n**Operator**: `redislabs/operator:7.8.2-6`\n**Services Rigger**: `redislabs/k8s-controller:7.8.2-6`\n**OLM operator bundle**: `v7.8.2-6.3`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"new-limitations","title":"New limitations","role":"content","text":"- **Only upgrades from 7.4.2-2 and later are supported.** If you are using an earlier version, install 7.4.2-2 before upgrading to 7.8.2-6.\n\n- **When changing the REDB field `spec.modulesList` version might be upgraded to latest even if a different version is specified.** To prevent the upgrade to latest, set  `spec.upgradeSpec.setModuleToLatest` to `false` before upgrading to 7.8.2-6."},{"id":"existing-limitations","title":"Existing limitations","role":"content","text":"- **Missing endpoint for admission endpoint (rare) (RED-119469)** Restart the operator pod.\n\n- **The REDB \"redisVersion\" field can’t be used for memcached databases(RED-119152)**\n\n- **PVC expansion is not supported when using Redis on Flash (Auto Tiering) (RED-165770)** Do not enable `enablePersistentVolumeResize` if your REC uses `redisOnFlashSpec` as this will result in conflicts.\n\n- **When modifying the database suffix for an Active-Active database, while the service-rigger is in a terminating state, the services-rigger will delete and create the ingress or route resources in a loop (RED-107687)** Wait until the services rigger pod has finished to terminate it.\n\n- **REAADB changes might fail with \"gateway timeout\" errors, mostly on OpenShift (RED-103048)** Retry the operation.\n\n- **Creating two databases with the same name directly on Redis Enterprise software will cause the service to be deleted and the database will not be available (RED-99997)** Avoid duplicating database names. Database creation via K8s has validation in place to prevent this.\n\n- **Installing the operator bundle produces warning: `Warning: would violate PodSecurity \"restricted: v1.24\"` (RED-97381)** Ignore the warning. This issue is documented as benign on official Red Hat documentation.\n\n- **RERC resources must have a unique name (RED-96302)** The string \"rec-name\"/\"rec-namespace\" must be different from all other participating clusters in the Active-Active database.\n\n- **Admission is not blocking REAADB with `shardCount` which exceeds license quota (RED-96301)** Fix the problems with the REAADB and reapply.\n\n- **Active-Active controller only supports global database options. Configuration specific to location is not supported (RED-86490)**\n\n- **Active-Active setup removal might keep services or routes undeleted (RED-77752)** Delete services or routes manually if you encounter this problem.\n\n- **`autoUpgrade` set to `true` can cause unexpected bdb upgrades when `redisUpgradePolicy` is set to `true` (RED-72351)** Contact support if your deployment is impacted.\n\n- **Following the previous quick start guide version causes issues with creating an REDB due to unrecognized memory field name (RED-69515)** The workaround is to use the newer (current) revision of Deploy Redis Enterprise Software for Kubernetes.\n\n- **PVC size issues when using decimal value in spec (RED-62132)** Make sure you use integer values for the PVC size.\n\n- **REC might report error states on initial startup (RED-61707)** There is no workaround at this time except to ignore the errors.\n\n- **Hashicorp Vault integration - no support for Gesher (RED-55080)** There is no workaround for this issue. Gesher support has been deprecated.\n\n- **REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED-47254)** When REC clusters are deployed on Kubernetes clusters without synchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes.\n\n- **Deleting an OpenShift project with an REC deployed may hang (RED-47192)** When an REC cluster is deployed in a project (namespace) and has REDB resources, the REDB resources must be deleted first before the REC can be deleted. Therefore, until the REDB resources are deleted, the project deletion will hang. The fix is to delete the REDB resources first and the REC second. Then, you can delete the project.\n\n- **Clusters must be named 'rec' in OLM-based deployments (RED-39825)** In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) are bound to a specific service account name (namely, \"rec\"). The workaround is to name the cluster \"rec\".\n\n- **Readiness probe incorrect on failures (RED-39300)** STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure.\n\n- **Internal DNS and Kubernetes DNS may have conflicts (RED-37462)** DNS conflicts are possible between the cluster `mdns_server` and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names.\n\n- **K8s-based 5.4.10 clusters seem to negatively affect existing 5.4.6 clusters (RED-37233)** Upgrade clusters to latest version.\n\n- **Node CPU usage is reported instead of pod CPU usage (RED-36884)** In Kubernetes, the reported node CPU usage is the usage of the Kubernetes worker node hosting the REC pod.\n\n- **An unreachable cluster has status running (RED-32805)** When a cluster is in an unreachable state, the state remains `running` instead of triggering an error.\n\n- **Long cluster names cause routes to be rejected  (RED-25871)** A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name exceeds 63 characters. The workaround is to limit the cluster name to 20 characters or fewer.\n\n- **Cluster CR (REC) errors are not reported after invalid updates (RED-25542)** A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-8-2-releases","title":"Redis Enterprise for Kubernetes 7.8.2-6 release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-2-releases/","summary":"Releases with support for Redis Enterprise Software 7.8.2","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","children":[{"id":"operate/kubernetes/release-notes/7-8-2-releases/7-8-2-6-jan25","summary":"Maintenance release to support Redis Enterprise Software version 7.8.2-60.","title":"Redis Enterprise for Kubernetes 7.8.2-6 (Jan 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-2-releases/7-8-2-6-jan25/"},{"id":"operate/kubernetes/release-notes/7-8-2-releases/7-8-2-6-nov24","summary":"Feature release including support for Redis Software 7.8.2 and RHEL9-based images.","title":"Redis Enterprise for Kubernetes 7.8.2-6 (Nov 2024) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-2-releases/7-8-2-6-nov24/"}],"page_type":"content","content_hash":"1b7aae0fac955788374f1e45b7b47baa658c3d9b43329f8b7b674e65218e950e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise for Kubernetes 7.8.2-6 is a feature release including support for Redis Software 7.8.2-2."},{"id":"detailed-release-notes","title":"Detailed release notes","role":"content","text":"| Version (Release date) | Major changes |\n| --- | --- |\n| [7.8.2-6 (Jan 2025)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-2-releases/7-8-2-6-jan25/) | Maintenance release to support Redis Enterprise Software version 7.8.2-60. |\n| [7.8.2-6 (Nov 2024)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-2-releases/7-8-2-6-nov24/) | Feature release including support for Redis Software 7.8.2 and RHEL9-based images. |"},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"The following changes included in this release affect the upgrade process. Please read carefully before upgrading."},{"id":"rhel9-based-images","title":"RHEL9-based images","role":"content","text":"- Redis Enterprise images are now based on RHEL9.\n- Ubuntu images are no longer supported.\n\nThis means upgrades to 7.8.2-6 require:\n\n- Cluster version of 7.4.2-2 or later.\n- Database version 7.2 or later.\n- RHEL9 compatible binaries for any modules you need.\n\nSee [Upgrade Redis Enterprise for Kubernetes](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster) for detailed steps to upgrade to 7.8.2-6."},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"new-limitations","title":"New limitations","role":"content","text":"- **Only upgrades from 7.4.2-2 and later are supported.** If you are using an earlier version, install 7.4.2-2 before upgrading to 7.8.2-6.\n\n- **When changing the REDB field `spec.modulesList` version might be upgraded to latest even if a different version is specified.** To prevent the upgrade to latest, set `spec.upgradeSpec.setModuleToLatest` to `false` before upgrading to 7.8.2-6."},{"id":"existing-limitations","title":"Existing limitations","role":"content","text":"- **Missing endpoint for admission endpoint (rare) (RED-119469)** Restart the operator pod.\n\n- **The REDB \"redisVersion\" field can’t be used for memcached databases(RED-119152)**\n\n- **When modifying the database suffix for an Active-Active database, while the service-rigger is in a terminating state, the services-rigger will delete and create the ingress or route resources in a loop (RED-107687)** Wait until the services rigger pod has finished to terminate it.\n\n- **REAADB changes might fail with \"gateway timeout\" errors, mostly on OpenShift (RED-103048)** Retry the operation.\n\n- **Creating two databases with the same name directly on Redis Enterprise software will cause the service to be deleted and the database will not be available (RED-99997)** Avoid duplicating database names. Database creation via K8s has validation in place to prevent this.\n\n- **Installing the operator bundle produces warning: `Warning: would violate PodSecurity \"restricted: v1.24\"` (RED-97381)** Ignore the warning. This issue is documented as benign on official Red Hat documentation.\n\n- **RERC resources must have a unique name (RED-96302)** The string \"rec-name\"/\"rec-namespace\" must be different from all other participating clusters in the Active-Active database.\n\n- **Admission is not blocking REAADB with `shardCount` which exceeds license quota (RED-96301)** Fix the problems with the REAADB and reapply.\n\n- **Active-Active controller only supports global database options. Configuration specific to location is not supported (RED-86490)**\n\n- **Active-Active setup removal might keep services or routes undeleted (RED-77752)** Delete services or routes manually if you encounter this problem.\n\n- **`autoUpgrade` set to `true` can cause unexpected bdb upgrades when `redisUpgradePolicy` is set to `true` (RED-72351)** Contact support if your deployment is impacted.\n\n- **Following the previous quick start guide version causes issues with creating an REDB due to unrecognized memory field name (RED-69515)** The workaround is to use the newer (current) revision of Deploy Redis Enterprise Software for Kubernetes.\n\n- **PVC size issues when using decimal value in spec (RED-62132)** Make sure you use integer values for the PVC size.\n\n- **REC might report error states on initial startup (RED-61707)** There is no workaround at this time except to ignore the errors.\n\n- **Hashicorp Vault integration - no support for Gesher (RED-55080)** There is no workaround for this issue. Gesher support has been deprecated.\n\n- **REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED-47254)** When REC clusters are deployed on Kubernetes clusters without synchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes.\n\n- **Deleting an OpenShift project with an REC deployed may hang (RED-47192)** When an REC cluster is deployed in a project (namespace) and has REDB resources, the REDB resources must be deleted first before the REC can be deleted. Therefore, until the REDB resources are deleted, the project deletion will hang. The fix is to delete the REDB resources first and the REC second. Then, you can delete the project.\n\n- **Clusters must be named 'rec' in OLM-based deployments (RED-39825)** In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) are bound to a specific service account name (namely, \"rec\"). The workaround is to name the cluster \"rec\".\n\n- **Readiness probe incorrect on failures (RED-39300)** STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure.\n\n- **Internal DNS and Kubernetes DNS may have conflicts (RED-37462)** DNS conflicts are possible between the cluster `mdns_server` and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names.\n\n- **K8s-based 5.4.10 clusters seem to negatively affect existing 5.4.6 clusters (RED-37233)** Upgrade clusters to latest version.\n\n- **Node CPU usage is reported instead of pod CPU usage (RED-36884)** In Kubernetes, the reported node CPU usage is the usage of the Kubernetes worker node hosting the REC pod.\n\n- **An unreachable cluster has status running (RED-32805)** When a cluster is in an unreachable state, the state remains `running` instead of triggering an error.\n\n- **Long cluster names cause routes to be rejected  (RED-25871)** A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name exceeds 63 characters. The workaround is to limit the cluster name to 20 characters or fewer.\n\n- **Cluster CR (REC) errors are not reported after invalid updates (RED-25542)** A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-8-4-releases/7-8-4-8-feb25","title":"Redis Enterprise for Kubernetes 7.8.4-8 (Feb 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-4-releases/7-8-4-8-feb25/","summary":"Maintenance release to support Redis Enterprise Software version 7.8.4-66.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"0968c1581c2298218fe79c185bc1b376937aa660688a7c652d1f58ba7f5c47fe","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This release includes bug fixes, enhancements, and support for [Redis Enterprise Software version 7.8.4-66](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-4-66)."},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"enhancements","title":"Enhancements","role":"content","text":"- Support for a read-only file system on Redis Enterprise containers (RED-75139).\n- Support for [Redis Enterprise Software version 7.8.4-66](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-4-66)."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- Fixed log collector handling of STDERR warnings (RED-148292)."},{"id":"api-changes","title":"API changes","role":"content","text":"| **CRD** | **Field** | **Change** | **Description** |\n|---|---|---|---|\n| REC | spec.SecurityContext.readOnlyRootFilesystemPolicy.enabled | Add | Enables the read-only filesystem |"},{"id":"supported-distributions","title":"Supported distributions","role":"compatibility","text":"The following table shows supported distributions at the time of this release. You can also find this list in [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions).\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – This distribution is supported for this version of Redis Enterprise Software for Kubernetes.\n\n<span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> Deprecated – This distribution is still supported for this version of Redis Enterprise Software for Kubernetes, but support will be removed in a future release.\n\n<span title=\"X icon\">&#x274c;</span> End of life – Support for this distribution ended.\n\nAny distribution not listed below is not supported for production workloads.\n\n| **Kubernetes version** | **1.25** | **1.26** | **1.27** | **1.28** | **1.29** | **1.30** | **1.31** |\n|---|---|---|---|---|---|---|---|\n| **Community Kubernetes** |  |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |\n| **Amazon EKS** |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |  |\n| **Azure AKS** |  |  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |  |\n| **Google GKE** |  |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |  |\n| **Rancher RKE2** |  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |  |  |  |\n| **VMware TKG 2.3** |  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> |  |  |  |  |  |\n| **VMware TKG 2.4** |  | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |  |  |  |  |\n| **OpenShift** | **4.12** | **4.13** | **4.14** | **4.15** | **4.16** |  |  |\n| **** | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |  |  |\n| **VMWare TKGI** | **1.16** | **1.17** | **1.18** | **1.19** |  |  |  |\n| **** | <span title=\"X icon\">&#x274c;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |  |  |  |"},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.8.4-66`\n- **Operator**: `redislabs/operator:7.8.4-8`\n- **Services Rigger**: `redislabs/k8s-controller:7.8.4-8`\n- **OLM operator bundle** : `v7.8.4-8.0`"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-8-4-releases/7-8-4-9-mar25","title":"Redis Enterprise for Kubernetes 7.8.4-9 (March 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-4-releases/7-8-4-9-mar25/","summary":"Maintenance release to support Redis Enterprise Software version 7.8.4-95.","aliases":["/operate/kubernetes/release-notes/7-8-4-releases/7-8-4-8-mar25/"],"tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"b66dec678c1b416be29252f0c9009f19209332b58b6bfd9e0e93c25ca4b2c33d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release to support [Redis Enterprise Software version 7.8.4-95](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-4-95) and OpenShift 4.17 and 4.18.\n\nFor version changes and known limitations, see the [release notes for 7.8.2-6 (Nov 2024)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-2-releases/7-8-2-6-nov24)."},{"id":"supported-distributions","title":"Supported distributions","role":"compatibility","text":"The following table shows supported distributions at the time of this release. You can also find this list in [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions).\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – This distribution is supported for this version of Redis Enterprise Software for Kubernetes.\n\n<span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> Deprecated – This distribution is still supported for this version of Redis Enterprise Software for Kubernetes, but support will be removed in a future release.\n\n<span title=\"X icon\">&#x274c;</span> End of life – Support for this distribution ended.\n\nAny distribution not listed below is not supported for production workloads.\n\n| **Kubernetes version** | **1.25** | **1.26** | **1.27** | **1.28** | **1.29** | **1.30** | **1.31** |\n|---|---|---|---|---|---|---|---|\n| **Community Kubernetes** |  |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |\n| **Amazon EKS** |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |  |\n| **Azure AKS** |  |  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |  |\n| **Google GKE** |  |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |  |\n| **Rancher RKE2** |  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |  |  |  |\n| **VMware TKG 2.3** |  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> |  |  |  |  |  |\n| **VMware TKG 2.4** |  | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |  |  |  |  |\n| **OpenShift** | **4.12** | **4.13** | **4.14** | **4.15** | **4.16** | **4.17** | **4.18** |\n| **** | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |\n| **VMWare TKGI** | **1.16** | **1.17** | **1.18** | **1.19** |  |  |  |\n| **** | <span title=\"X icon\">&#x274c;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> | <span title=\"Check mark icon\">&#x2705;</span> |  |  |  |"},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.8.4-95`\n- **Operator**: `redislabs/operator:7.8.4-9`\n- **Services Rigger**: `redislabs/k8s-controller:7.8.4-9`\n- **OLM operator bundle** : `v7.8.4-9.0`"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-8-4-releases","title":"Redis Enterprise for Kubernetes 7.8.4 release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-4-releases/","summary":"Releases with support for Redis Enterprise Software 7.8.4","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","children":[{"id":"operate/kubernetes/release-notes/7-8-4-releases/7-8-4-9-mar25","summary":"Maintenance release to support Redis Enterprise Software version 7.8.4-95.","title":"Redis Enterprise for Kubernetes 7.8.4-9 (March 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-4-releases/7-8-4-9-mar25/"},{"id":"operate/kubernetes/release-notes/7-8-4-releases/7-8-4-8-feb25","summary":"Maintenance release to support Redis Enterprise Software version 7.8.4-66.","title":"Redis Enterprise for Kubernetes 7.8.4-8 (Feb 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-4-releases/7-8-4-8-feb25/"}],"page_type":"content","content_hash":"c47e6fd1fda77ab6ad68c9be803aa97729fba3cebc55a3d197cc939f0f10b0bb","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise for Kubernetes 7.8.4-8 includes bug fixes, enhancements, and support for [Redis Enterprise Software version 7.8.4](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases)."},{"id":"detailed-release-notes","title":"Detailed release notes","role":"content","text":"| Version (Release date) | Major changes |\n| --- | --- |\n| [7.8.4-9 (March 2025)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-4-releases/7-8-4-9-mar25/) | Maintenance release to support Redis Enterprise Software version 7.8.4-95. |\n| [7.8.4-8 (Feb 2025)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-4-releases/7-8-4-8-feb25/) | Maintenance release to support Redis Enterprise Software version 7.8.4-66. |"},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":""},{"id":"rhel9-based-images","title":"RHEL9-based images","role":"content","text":"- As of version 7.8.2-6, Redis Enterprise images are based on Red Hat Enterprise Linux 9 (RHEL9). This means upgrades to 7.8.2-6 require:\n\n- Cluster version of 7.4.2-2 or later.\n- Database version 7.2 or later.\n- RHEL9 compatible binaries for any modules you need.\n\nSee [Upgrade Redis Enterprise for Kubernetes](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster) for detailed steps to upgrade to 7.8.4-8."},{"id":"deprecations","title":"Deprecations","role":"content","text":"- Ubuntu-based images are no longer supported.\n- REDB field `upgradeSpec.upgradeModulesToLatest is deprecated.\n- REDB field `spec.modulesList.version` is deprecated."},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"new-limitations","title":"New limitations","role":"content","text":"- **Specifying `databasePort` causes issues with REAADB synchronization. (RED-149374)**"},{"id":"existing-limitations","title":"Existing limitations","role":"content","text":"- **Only upgrades from 7.4.2-2 and later are supported.** If you are using an earlier version, install 7.4.2-2 before upgrading to 7.8.2-6 or later.\n\n- **When changing the REDB field `spec.modulesList`, version might be upgraded to latest, even if a different version is specified.** To prevent the upgrade to latest, set  `spec.upgradeSpec.setModuleToLatest` to `false` before upgrading to 7.8.2-6.\n\n- **Missing endpoint for admission endpoint (rare) (RED-119469)** Restart the operator pod.\n\n- **The REDB \"redisVersion\" field can’t be used for memcached databases(RED-119152)**\n\n- **When modifying the database suffix for an Active-Active database, while the service-rigger is in a terminating state, the services-rigger will delete and create the ingress or route resources in a loop (RED-107687)** Wait until the services rigger pod has finished to terminate it.\n\n- **REAADB changes might fail with \"gateway timeout\" errors, mostly on OpenShift (RED-103048)** Retry the operation.\n\n- **Creating two databases with the same name directly on Redis Enterprise software will cause the service to be deleted and the database will not be available (RED-99997)** Avoid duplicating database names. Database creation via K8s has validation in place to prevent this.\n\n- **Installing the operator bundle produces warning: `Warning: would violate PodSecurity \"restricted: v1.24\"` (RED-97381)** Ignore the warning. This issue is documented as benign on official Red Hat documentation.\n\n- **RERC resources must have a unique name (RED-96302)** The string \"rec-name\"/\"rec-namespace\" must be different from all other participating clusters in the Active-Active database.\n\n- **Admission is not blocking REAADB with `shardCount` which exceeds license quota (RED-96301)** Fix the problems with the REAADB and reapply.\n\n- **Active-Active controller only supports global database options. Configuration specific to location is not supported (RED-86490)**\n\n- **Active-Active setup removal might keep services or routes undeleted (RED-77752)** Delete services or routes manually if you encounter this problem.\n\n- **`autoUpgrade` set to `true` can cause unexpected bdb upgrades when `redisUpgradePolicy` is set to `true` (RED-72351)** Contact support if your deployment is impacted.\n\n- **Following the previous quick start guide version causes issues with creating an REDB due to unrecognized memory field name (RED-69515)** The workaround is to use the newer (current) revision of Deploy Redis Enterprise Software for Kubernetes.\n\n- **PVC size issues when using decimal value in spec (RED-62132)** Make sure you use integer values for the PVC size.\n\n- **REC might report error states on initial startup (RED-61707)** There is no workaround at this time except to ignore the errors.\n\n- **Hashicorp Vault integration - no support for Gesher (RED-55080)** There is no workaround for this issue. Gesher support has been deprecated.\n\n- **REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED-47254)** When REC clusters are deployed on Kubernetes clusters without synchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes.\n\n- **Deleting an OpenShift project with an REC deployed may hang (RED-47192)** When an REC cluster is deployed in a project (namespace) and has REDB resources, the REDB resources must be deleted first before the REC can be deleted. Therefore, until the REDB resources are deleted, the project deletion will hang. The fix is to delete the REDB resources first and the REC second. Then, you can delete the project.\n\n- **Clusters must be named 'rec' in OLM-based deployments (RED-39825)** In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) are bound to a specific service account name (namely, \"rec\"). The workaround is to name the cluster \"rec\".\n\n- **Readiness probe incorrect on failures (RED-39300)** STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure.\n\n- **Internal DNS and Kubernetes DNS may have conflicts (RED-37462)** DNS conflicts are possible between the cluster `mdns_server` and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names.\n\n- **K8s-based 5.4.10 clusters seem to negatively affect existing 5.4.6 clusters (RED-37233)** Upgrade clusters to latest version.\n\n- **Node CPU usage is reported instead of pod CPU usage (RED-36884)** In Kubernetes, the reported node CPU usage is the usage of the Kubernetes worker node hosting the REC pod.\n\n- **An unreachable cluster has status running (RED-32805)** When a cluster is in an unreachable state, the state remains `running` instead of triggering an error.\n\n- **Long cluster names cause routes to be rejected  (RED-25871)** A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name exceeds 63 characters. The workaround is to limit the cluster name to 20 characters or fewer.\n\n- **Cluster CR (REC) errors are not reported after invalid updates (RED-25542)** A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-8-6-releases/7-8-6-1-april2025","title":"Redis Enterprise for Kubernetes 7.8.6-1 (April 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-1-april2025/","summary":"A maintenance release that includes support for Redis Software 7.8.6-36.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"f879c6dd3c94b4503506366f7854222b5c5cd2422189d2a8360869dbbc435f11","sections":[{"id":"new-in-the-release","title":"New in the release","role":"overview","text":"Redis Enterprise for Kubernetes 7.8.6-1 (April 2025) is a maintenance release that includes support for [Redis Software 7.8.6-36](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases).\n\nFor supported distributions, known limitations, and API changes, see [Redis Enterprise for Kubernetes 7.8.6-1 March 2025 release notes](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-1-march2025)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.8.6-36`\n- **Operator**: `redislabs/operator:7.8.6-1`\n- **Services Rigger**: `redislabs/k8s-controller:7.8.6-1`\n- **OLM operator bundle** : `v7.8.6-1.1`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [7.8.6 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-8-6-releases/7-8-6-1-march2025","title":"Redis Enterprise for Kubernetes 7.8.6-1 (March 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-1-march2025/","summary":"Feature release including enhancements, bug fixes, platform updates, and support for Redis Software 7.8.6-13","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"13babba6cca10e8716f07380e507ef993f6288d742337d6a66b7410123bb8d58","sections":[{"id":"new-in-the-release","title":"New in the release","role":"overview","text":"Redis Enterprise for Kubernetes 7.8.6-1 is a feature release that includes enhancements, bug fixes, platform updates, and support for [Redis Software 7.8.6-13](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-13).\n\nThis release also includes the ability to [install the operator using Helm](https://redis.io/docs/latest/operate/kubernetes/deployment/helm).\n\nSupport for Helm is currently in public preview and is not supported on production workloads. Only new installations of the Redis operator are supported at this time."},{"id":"enhancements","title":"Enhancements","role":"content","text":"- Added support for [Redis Software 7.8.6-13](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-13).\n- Updated supported Kubernetes distributions\n- DatabaseServicePortPolicy allows use of the default Redis port (6379) for database services.\n- Added additional license fields to the REC status.\n- Added validation to prevent unsupported changes to podDisruptionBudget."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- Fixed Sonarcube findings in the log collector (RED-129297).\n- Fixed upgrade failure due to update of the nodeSelector during the upgrade (RED-138445).\n- Fixed issues with Vault plugin connections to Redis Enterprise (RED-139352 RED-144637).\n- Fixed operator crash when a namespace is labeled with a specific label (RED-140344).\n- Fixed failure in Services Rigger when a database uses port 10250 (RED-142673).\n- Reduced CPU priority of the health_check.py script to ensure availability (RED-144360).\n- Fixed broken use case for customizing the Active-Active port. (RED-149374).\n- Fixed API reference for REDB properties (RED-151195).\n- Fixed operator CVE-2024-24786 (RED-151873).\n- Fixed wrong image tag for OpenShift example YAML (RED-152022)."},{"id":"api-changes","title":"API changes","role":"content","text":"| **CRD** | **Field** | **Change** | **Description** |\n|---|---|---|---|\n| REAADB | `spec.externalReplicationPort` | ADD | Specifies the replication endpoint port number for users who use LoadBalancers to synchronize Active-Active replicas and must provide the port that the LoadBalancer listens on. |\n| REC | `status.shardsUsage` | ADD | Total number of shards (both RAM and flash) currently in use under this license. |\n| REC | `status.flashShards` | ADD | Number of flash shards currently in use under this license. |\n| REC | `status.flashShardsLimit` | ADD | Number of flash shards allowed under this license. |\n| REC | `status.ramShards` | ADD | Number of RAM shards currently in use under this license. |\n| REC | `status.ramShardsLimit` | ADD | Number of RAM shards allowed under this license. |\n| REC | `spec.podSecurityPolicyName` | REMOVE | Not supported anymore by supported Kubernetes versions. |\n| REC | `spec.databaseServicePortPolicy` | ADD | Specifies how to determine service ports for REDB services. Defaults to DatabasePortForward if not set. DatabasePortForward - The service port will be the same as the database port. RedisDefaultPort - The service port will be the default Redis port (6379)."},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"Customers who use load balancers for Active-Active replication endpoints and rely on the change introduced in RED-113626 (or the workaround described in the ticket) must set the spec.externalReplicationPort field in REAADB after upgrading. Otherwise, replication will fail (RED-149374)."},{"id":"rhel9-based-image","title":"RHEL9-based image","role":"content","text":"As of version 7.8.2-6, Redis Enterprise images are based on Red Hat Enterprise Linux 9 (RHEL9). This means upgrades require:\n\n- [Cluster version of 7.4.2-2 or later](https://redis.io/docs/latest/operate/kubernetes/7.4.6/upgrade/).\n- Database version 7.2 or later.\n- RHEL9 compatible binaries for any modules you need.\n\nFor detailed steps, see the relevant upgrade page:\n\n- [OpenShift CLI](https://redis.io/docs/latest/operate/kubernetes/upgrade/openshift-cli)\n- [OpenShift OperatorHub](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-olm)\n- [Kubernetes](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster)"},{"id":"supported-distributions","title":"Supported distributions","role":"compatibility","text":"The following table shows supported distributions at the time of this release. You can also find this list in [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions).\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – This distribution is supported for this version of Redis Enterprise Software for Kubernetes.\n\n<span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> Deprecated – This distribution is still supported for this version of Redis Enterprise Software for Kubernetes, but support will be removed in a future release.\n\n<span title=\"X icon\">&#x274c;</span> End of life – Support for this distribution ended.\n\nAny distribution not listed below is not supported for production workloads.\n\n| Kubernetes version | **1.26** | **1.27** | **1.28** | **1.29** | **1.30** | **1.31** | **1.32** |\n|---|---|---|---|---|---|---|---|\n|  |  |  |  |  |  |  |  |\n| **Community K8s** |  |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"X icon\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Amazon EKS** |  |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Azure AKS** |  |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Google GKE** |  |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Rancher** |  | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |  |\n| **VMware TKG 2.4** | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> |  |  |  |  |  |\n| **OpenShift** | **4.13** | **4.14** | **4.15** | **4.16** | **4.17** | **4.18** |  |\n|  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |\n| **VMware TKGI** | **1.16 | **1.17 | **1.18 | **1.19 | **1.2 |  |  |\n|  |  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |"},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/operator:7.8.6-13`\n- **Operator**: `redislabs/redis:7.8.6-1`\n- **Services Rigger**: `redislabs/k8s-controller:7.8.6-1`\n- **OLM operator bundle** : `v7.8.6-1.0`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [7.8.6 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-8-6-releases/7-8-6-1-may2025","title":"Redis Enterprise for Kubernetes 7.8.6-1 (May 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-1-may2025/","summary":"A maintenance release that includes support for Redis Software 7.8.6-60.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"e98d3730f4ce6bf8d12f808cea33d1f1f091c2c367ee5a618dabc1d7a3fb70db","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise for Kubernetes 7.8.6-1 (May 2025) is a maintenance release that includes support for [Redis Software 7.8.6-60](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases).\n\nFor supported distributions, known limitations, and API changes, see [Redis Enterprise for Kubernetes 7.8.6-1 March 2025 release notes](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-1-march2025)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.8.6-60`\n- **Operator**: `redislabs/operator:7.8.6-1`\n- **Services Rigger**: `redislabs/k8s-controller:7.8.6-1`\n- **OLM operator bundle** : `v7.8.6-1.2`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [7.8.6 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-8-6-releases/7-8-6-11-feb2026","title":"Redis Enterprise for Kubernetes 7.8.6-11 (February 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-11-feb2026/","summary":"A maintenance release that includes support for Redis Software 7.8.6-253.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"608da42494843059e6fb617f9f337221c30015521de83397db351b2ac07f9445","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise for Kubernetes 7.8.6-11 (February 2026) is a maintenance release that includes support for [Redis Software 7.8.6-253](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases).\n\nFor supported distributions, known limitations, and API changes, see [Redis Enterprise for Kubernetes 7.8.6-1 March 2025 release notes](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-1-march2025)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.8.6-253`\n- **Operator**: `redislabs/operator:7.8.6-11`\n- **Services Rigger**: `redislabs/k8s-controller:7.8.6-11`\n- **OLM operator bundle** : `v7.8.6-11.0`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [7.8.6 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-8-6-releases/7-8-6-13-march2026","title":"Redis Enterprise for Kubernetes 7.8.6-13 (March 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-13-march2026/","summary":"A maintenance release that includes support for Redis Software 7.8.6-256.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"37f2e68b944d4057e617662cba2f2f38e5b5622a97048db76443048652955eb3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise for Kubernetes 7.8.6-13 (March 2026) is a maintenance release that includes support for [Redis Software 7.8.6-256](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases).\n\nFor supported distributions, known limitations, and API changes, see [Redis Enterprise for Kubernetes 7.8.6-1 March 2025 release notes](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-1-march2025)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.8.6-256`\n- **Operator**: `redislabs/operator:7.8.6-13`\n- **Services Rigger**: `redislabs/k8s-controller:7.8.6-13`\n- **OLM operator bundle** : `v7.8.6-13.0`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [7.8.6 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-8-6-releases/7-8-6-14-april2026","title":"Redis Enterprise for Kubernetes 7.8.6-14 (April 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-14-april2026/","summary":"A maintenance release that includes support for Redis Software 7.8.6-260.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"b33f5f7ea61974c0757a97e6587db89ff4bc09103f6cca78862766b1a3d4181e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise for Kubernetes 7.8.6-14 (April 2026) is a maintenance release that includes support for [Redis Software 7.8.6-260](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases).\n\nFor supported distributions, known limitations, and API changes, see [Redis Enterprise for Kubernetes 7.8.6-1 March 2025 release notes](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-1-march2025)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.8.6-260`\n- **Operator**: `redislabs/operator:7.8.6-14`\n- **Services Rigger**: `redislabs/k8s-controller:7.8.6-14`\n- **OLM operator bundle** : `v7.8.6-14.0`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [7.8.6 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-8-6-releases/7-8-6-15-may2026","title":"Redis Enterprise for Kubernetes 7.8.6-15 (May 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-15-may2026/","summary":"A maintenance release that includes support for Redis Software 7.8.6-263.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"13df891737b9f8ed72daa9773b75920c58e576e28a7aebf384998a63ef249d63","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise for Kubernetes 7.8.6-15 (May 2026) is a maintenance release that includes support for [Redis Software 7.8.6-263](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases).\n\nFor supported distributions, known limitations, and API changes, see [Redis Enterprise for Kubernetes 7.8.6-1 March 2025 release notes](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-1-march2025)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.8.6-263`\n- **Operator**: `redislabs/operator:7.8.6-15`\n- **Services Rigger**: `redislabs/k8s-controller:7.8.6-15`\n- **OLM operator bundle** : `v7.8.6-15.0`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [7.8.6 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-8-6-releases/7-8-6-16-july2026","title":"Redis Enterprise for Kubernetes 7.8.6-16 (July 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-16-july2026/","summary":"A maintenance release that includes support for Redis Software 7.8.6-286.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"88771d18be90592fb4fa19c109c65e84045c4126371b595cb90aa7f236df1df6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise for Kubernetes 7.8.6-16 (July 2026) is a maintenance release that includes support for [Redis Software 7.8.6-286](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases).\n\nFor supported distributions, known limitations, and API changes, see [Redis Enterprise for Kubernetes 7.8.6-1 March 2025 release notes](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-1-march2025)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.8.6-286`\n- **Operator**: `redislabs/operator:7.8.6-16`\n- **Services Rigger**: `redislabs/k8s-controller:7.8.6-16`"},{"id":"openshift-images","title":"OpenShift images","role":"content","text":"- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.8.6-286`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.8.6-16`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.8.6-16`"},{"id":"olm-bundle","title":"OLM bundle","role":"content","text":"**Redis Enterprise operator bundle**: `v7.8.6-16.0`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [7.8.6 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-8-6-releases/7-8-6-19-august2026","title":"Redis Enterprise for Kubernetes 7.8.6-19 (August 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-19-august2026/","summary":"A maintenance release that includes support for Redis Software 7.8.6-303.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"56ff83d3e514d63193c1c2603185963f8a1be1e3c6a981724dcac83eb095b54d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise for Kubernetes 7.8.6-19 (August 2026) is a maintenance release that includes support for [Redis Software 7.8.6-303](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases).\n\nFor supported distributions, known limitations, and API changes, see [Redis Enterprise for Kubernetes 7.8.6-1 March 2025 release notes](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-1-march2025)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.8.6-303`\n- **Operator**: `redislabs/operator:7.8.6-19`\n- **Services Rigger**: `redislabs/k8s-controller:7.8.6-19`"},{"id":"openshift-images","title":"OpenShift images","role":"content","text":"- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.8.6-303`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.8.6-19`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.8.6-19`"},{"id":"olm-bundle","title":"OLM bundle","role":"content","text":"**Redis Enterprise operator bundle**: `v7.8.6-19.0`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [7.8.6 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-8-6-releases/7-8-6-2-june2025","title":"Redis Enterprise for Kubernetes 7.8.6-2 (June 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-2-june2025/","summary":"A maintenance release that includes support for Redis Software 7.8.6-95.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"96ae68f1ec792b417c44aaa63fd186f5b0bbe2899a94688140f0ec1ef7f7063d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise for Kubernetes 7.8.6-2 (June 2025) is a maintenance release that includes support for [Redis Software 7.8.6-95](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases).\n\nFor supported distributions, known limitations, and API changes, see [Redis Enterprise for Kubernetes 7.8.6-1 March 2025 release notes](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-1-march2025)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.8.6-95`\n- **Operator**: `redislabs/operator:7.8.6-2`\n- **Services Rigger**: `redislabs/k8s-controller:7.8.6-2`\n- **OLM operator bundle** : `v7.8.6-2.2`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [7.8.6 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-8-6-releases/7-8-6-20-september2026","title":"Redis Software for Kubernetes 7.8.6-20 (September 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-20-september2026/","summary":"A maintenance release that includes support for Redis Software 7.8.6-315.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"b19f80f49726dadc4a0c3b5c99db5c65def20f3a60d0b57b125f219f6935e064","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software for Kubernetes 7.8.6-20 (September 2026) is a maintenance release that includes support for [Redis Software 7.8.6-315](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases). It fixes a high-severity bug with security implications that affected Active-Active databases.\n\nFor supported distributions, known limitations, and API changes, see [Redis Software for Kubernetes 7.8.6-1 March 2025 release notes](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-1-march2025)."},{"id":"security","title":"Security","role":"security","text":"A high-severity bug with security implications affected Active-Active databases managed by the Redis Software for Kubernetes operator. **Upgrade to operator version 7.8.6-20 or later on this release line to get the fix.**\n\nWhen the operator applied a configuration update to an Active-Active database, it rebuilt and sent the database's entire configuration, but only re-read a credential from its Kubernetes secret if it believed that specific secret had changed. Any update triggered by something else therefore sent the password as empty, and on operator versions before 8.2.0-12, also cleared the mutual TLS (mTLS) client certificates.\n\nEither of the following triggered such an update:\n\n- Any configuration change on the Active-Active database, such as eviction policy, memory size, or backup settings, made by a user or by automation tooling such as GitOps.\n- A modification of the backup secret or a client certificate secret on the cluster, even with no configuration change. The operator re-read the touched secret correctly but wiped the password, because its secret was not the one that changed.\n\nThe impact was two-sided:\n\n- **Availability**: applications connecting with the password failed on every new connection. Existing connections kept working, so the failure looked intermittent.\n- **Security**: the database simultaneously accepted connections with no credentials at all, and with the mTLS variant, accepted clients without certificates. Nothing alerted and the database reported healthy, so a database could run exposed without anyone noticing.\n\nThis affected all released operator versions since the feature was introduced in early 2023. The fix prevents the credential wipe and automatically repairs already-affected databases when you upgrade the operator. No additional steps are required.\n\nThe fix is available in operator versions 7.4.6-11, 7.8.6-20, 7.22.2-45, 8.0.20-27, and 8.2.0-15. <!--RED-212963--> <!--RED-213921-->"},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.8.6-315`\n- **Operator**: `redislabs/operator:7.8.6-20`\n- **Services Rigger**: `redislabs/k8s-controller:7.8.6-20`"},{"id":"openshift-images","title":"OpenShift images","role":"content","text":"- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.8.6-315`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.8.6-20`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.8.6-20`"},{"id":"olm-bundle","title":"OLM bundle","role":"content","text":"**Redis Enterprise operator bundle**: `v7.8.6-20.1`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [7.8.6 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-8-6-releases/7-8-6-22-october2026","title":"Redis Software for Kubernetes 7.8.6-22 (October 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-22-october2026/","summary":"A maintenance release that includes support for Redis Software 7.8.6-331.","tags":["docs","operate","kubernetes"],"last_updated":"2026-10-13T15:08:23-05:00","page_type":"content","content_hash":"575e19b37a76e541883048e9303fd2c80cbfc587051cbf23bf8d2f4c1ba005d0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software for Kubernetes 7.8.6-22 (October 2026) is a maintenance release that includes support for [Redis Software 7.8.6-331](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases).\n\nFor supported distributions, known limitations, and API changes, see [Redis Software for Kubernetes 7.8.6-1 March 2025 release notes](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-1-march2025)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.8.6-331`\n- **Operator**: `redislabs/operator:7.8.6-22`\n- **Services Rigger**: `redislabs/k8s-controller:7.8.6-22`"},{"id":"openshift-images","title":"OpenShift images","role":"content","text":"- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:7.8.6-331`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:7.8.6-22`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:7.8.6-22`"},{"id":"olm-bundle","title":"OLM bundle","role":"content","text":"**Redis Enterprise operator bundle**: `v7.8.6-22.1`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [7.8.6 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-8-6-releases/7-8-6-3-june2025","title":"Redis Enterprise for Kubernetes 7.8.6-3 (June 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-3-june2025/","summary":"A maintenance release that includes support for Redis Software 7.8.6-119.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"d7e06e406cec2d69f0382fbb74aecbbbff5b61b6189a136399df9732cf2533c4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise for Kubernetes 7.8.6-3 (June 2025) is a maintenance release that includes support for [Redis Software 7.8.6-119](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-119).\n\nFor supported distributions, known limitations, and API changes, see [Redis Enterprise for Kubernetes 7.8.6-1 March 2025 release notes](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-1-march2025)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.8.6-119`\n- **Operator**: `redislabs/operator:7.8.6-3`\n- **Services Rigger**: `redislabs/k8s-controller:7.8.6-3`\n- **OLM operator bundle** : `v7.8.6-3.2`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [7.8.6 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-8-6-releases/7-8-6-5-august2025","title":"Redis Enterprise for Kubernetes 7.8.6-5 (August 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-5-august2025/","summary":"A maintenance release that includes support for Redis Software 7.8.6-152.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"95ba653fac6f42d8a7c0f564ed6bb9a3f29c699b9c35fb0e1b4960c91ecb565b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise for Kubernetes 7.8.6-5 (August 2025) is a maintenance release that includes support for [Redis Software 7.8.6-152](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-152).\n\nFor supported distributions, known limitations, and API changes, see [Redis Enterprise for Kubernetes 7.8.6-1 March 2025 release notes](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-1-march2025)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.8.6-152`\n- **Operator**: `redislabs/operator:7.8.6-5`\n- **Services Rigger**: `redislabs/k8s-controller:7.8.6-5`\n- **OLM operator bundle** : `v7.8.6-5.1`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [7.8.6 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-8-6-releases/7-8-6-8-october2025","title":"Redis Enterprise for Kubernetes 7.8.6-8 (October 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-8-october2025/","summary":"A maintenance release that includes support for Redis Software 7.8.6-207.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"cebb247c7742c7061d00ac7f5991e00673f8314d805ca75d8e74ffe219194510","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise for Kubernetes 7.8.6-8 (October 2025) is a maintenance release that includes support for [Redis Software 7.8.6-207](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-207).\n\nFor supported distributions, known limitations, and API changes, see [Redis Enterprise for Kubernetes 7.8.6-1 March 2025 release notes](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-1-march2025)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:7.8.6-207`\n- **Operator**: `redislabs/operator:7.8.6-8`\n- **Services Rigger**: `redislabs/k8s-controller:7.8.6-8`\n- **OLM operator bundle** : `v7.8.6-8.1`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [7.8.6 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/7-8-6-releases","title":"Redis Enterprise for Kubernetes 7.8.6 release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/","summary":"Releases with support for Redis Enterprise Software 7.8.6","tags":["docs","operate","kubernetes"],"last_updated":"2026-10-13T15:08:23-05:00","children":[{"id":"operate/kubernetes/release-notes/7-8-6-releases/7-8-6-22-october2026","summary":"A maintenance release that includes support for Redis Software 7.8.6-331.","title":"Redis Software for Kubernetes 7.8.6-22 (October 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-22-october2026/"},{"id":"operate/kubernetes/release-notes/7-8-6-releases/7-8-6-20-september2026","summary":"A maintenance release that includes support for Redis Software 7.8.6-315.","title":"Redis Software for Kubernetes 7.8.6-20 (September 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-20-september2026/"},{"id":"operate/kubernetes/release-notes/7-8-6-releases/7-8-6-19-august2026","summary":"A maintenance release that includes support for Redis Software 7.8.6-303.","title":"Redis Enterprise for Kubernetes 7.8.6-19 (August 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-19-august2026/"},{"id":"operate/kubernetes/release-notes/7-8-6-releases/7-8-6-16-july2026","summary":"A maintenance release that includes support for Redis Software 7.8.6-286.","title":"Redis Enterprise for Kubernetes 7.8.6-16 (July 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-16-july2026/"},{"id":"operate/kubernetes/release-notes/7-8-6-releases/7-8-6-15-may2026","summary":"A maintenance release that includes support for Redis Software 7.8.6-263.","title":"Redis Enterprise for Kubernetes 7.8.6-15 (May 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-15-may2026/"},{"id":"operate/kubernetes/release-notes/7-8-6-releases/7-8-6-14-april2026","summary":"A maintenance release that includes support for Redis Software 7.8.6-260.","title":"Redis Enterprise for Kubernetes 7.8.6-14 (April 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-14-april2026/"},{"id":"operate/kubernetes/release-notes/7-8-6-releases/7-8-6-13-march2026","summary":"A maintenance release that includes support for Redis Software 7.8.6-256.","title":"Redis Enterprise for Kubernetes 7.8.6-13 (March 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-13-march2026/"},{"id":"operate/kubernetes/release-notes/7-8-6-releases/7-8-6-11-feb2026","summary":"A maintenance release that includes support for Redis Software 7.8.6-253.","title":"Redis Enterprise for Kubernetes 7.8.6-11 (February 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-11-feb2026/"},{"id":"operate/kubernetes/release-notes/7-8-6-releases/7-8-6-8-october2025","summary":"A maintenance release that includes support for Redis Software 7.8.6-207.","title":"Redis Enterprise for Kubernetes 7.8.6-8 (October 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-8-october2025/"},{"id":"operate/kubernetes/release-notes/7-8-6-releases/7-8-6-5-august2025","summary":"A maintenance release that includes support for Redis Software 7.8.6-152.","title":"Redis Enterprise for Kubernetes 7.8.6-5 (August 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-5-august2025/"},{"id":"operate/kubernetes/release-notes/7-8-6-releases/7-8-6-3-june2025","summary":"A maintenance release that includes support for Redis Software 7.8.6-119.","title":"Redis Enterprise for Kubernetes 7.8.6-3 (June 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-3-june2025/"},{"id":"operate/kubernetes/release-notes/7-8-6-releases/7-8-6-2-june2025","summary":"A maintenance release that includes support for Redis Software 7.8.6-95.","title":"Redis Enterprise for Kubernetes 7.8.6-2 (June 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-2-june2025/"},{"id":"operate/kubernetes/release-notes/7-8-6-releases/7-8-6-1-may2025","summary":"A maintenance release that includes support for Redis Software 7.8.6-60.","title":"Redis Enterprise for Kubernetes 7.8.6-1 (May 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-1-may2025/"},{"id":"operate/kubernetes/release-notes/7-8-6-releases/7-8-6-1-april2025","summary":"A maintenance release that includes support for Redis Software 7.8.6-36.","title":"Redis Enterprise for Kubernetes 7.8.6-1 (April 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-1-april2025/"},{"id":"operate/kubernetes/release-notes/7-8-6-releases/7-8-6-1-march2025","summary":"Feature release including enhancements, bug fixes, platform updates, and support for Redis Software 7.8.6-13","title":"Redis Enterprise for Kubernetes 7.8.6-1 (March 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-1-march2025/"}],"page_type":"content","content_hash":"9cabcb6bab4f2c87a992eabfc32e9a5df44172ba2ce48832f46dd1cee2d06bd6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise for Kubernetes 7.8.6 includes bug fixes, enhancements, and support for Redis Enterprise Software. The latest release is 7.8.6-22 with support for Redis Enterprise Software version 7.8.6-331."},{"id":"detailed-release-notes","title":"Detailed release notes","role":"content","text":"| Version (Release date) | Major changes |\n| --- | --- |\n| [7.8.6-22 (October 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-22-october2026/) | A maintenance release that includes support for Redis Software 7.8.6-331. |\n| [7.8.6-20 (September 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-20-september2026/) | A maintenance release that includes support for Redis Software 7.8.6-315. |\n| [7.8.6-19 (August 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-19-august2026/) | A maintenance release that includes support for Redis Software 7.8.6-303. |\n| [7.8.6-16 (July 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-16-july2026/) | A maintenance release that includes support for Redis Software 7.8.6-286. |\n| [7.8.6-15 (May 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-15-may2026/) | A maintenance release that includes support for Redis Software 7.8.6-263. |\n| [7.8.6-14 (April 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-14-april2026/) | A maintenance release that includes support for Redis Software 7.8.6-260. |\n| [7.8.6-13 (March 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-13-march2026/) | A maintenance release that includes support for Redis Software 7.8.6-256. |\n| [7.8.6-11 (February 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-11-feb2026/) | A maintenance release that includes support for Redis Software 7.8.6-253. |\n| [7.8.6-8 (October 2025)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-8-october2025/) | A maintenance release that includes support for Redis Software 7.8.6-207. |\n| [7.8.6-5 (August 2025)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-5-august2025/) | A maintenance release that includes support for Redis Software 7.8.6-152. |\n| [7.8.6-3 (June 2025)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-3-june2025/) | A maintenance release that includes support for Redis Software 7.8.6-119. |\n| [7.8.6-2 (June 2025)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-2-june2025/) | A maintenance release that includes support for Redis Software 7.8.6-95. |\n| [7.8.6-1 (May 2025)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-1-may2025/) | A maintenance release that includes support for Redis Software 7.8.6-60. |\n| [7.8.6-1 (April 2025)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-1-april2025/) | A maintenance release that includes support for Redis Software 7.8.6-36. |\n| [7.8.6-1 (March 2025)](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/7-8-6-1-march2025/) | Feature release including enhancements, bug fixes, platform updates, and support for Redis Software 7.8.6-13 |"},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"Customers who use load balancers for Active-Active replication endpoints and rely on the change introduced in RED-113626 (or the workaround described in the ticket) must set the spec.externalReplicationPort field in REAADB after upgrading. Otherwise, replication will fail (RED-149374)."},{"id":"rhel9-based-image","title":"RHEL9-based image","role":"content","text":"As of version 7.8.2-6, Redis Enterprise images are based on Red Hat Enterprise Linux 9 (RHEL9). This means upgrades require:\n\n- [Cluster version of 7.4.2-2 or later](https://redis.io/docs/latest/operate/kubernetes/7.4.6/upgrade/).\n- Database version 7.2 or later.\n- RHEL9 compatible binaries for any modules you need.\n\nFor detailed steps, see the relevant upgrade page:\n\n- [OpenShift CLI](https://redis.io/docs/latest/operate/kubernetes/upgrade/openshift-cli)\n- [OpenShift OperatorHub](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-olm)\n- [Kubernetes](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster)"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"- **Only upgrades from 7.4.2-2 and later are supported.** If you are using an earlier version, install 7.4.2-2 before upgrading to 7.8.2-6 or later.\n\n- **When changing the REDB field `spec.modulesList`, version might be upgraded to latest, even if a different version is specified.** To prevent the upgrade to latest, set  `spec.upgradeSpec.setModuleToLatest` to `false` before upgrading to 7.8.2-6.\n\n- **Missing endpoint for admission endpoint (rare) (RED-119469)** Restart the operator pod.\n\n- **The REDB \"redisVersion\" field can’t be used for memcached databases(RED-119152)**\n\n- **When modifying the database suffix for an Active-Active database, while the service-rigger is in a terminating state, the services-rigger will delete and create the ingress or route resources in a loop (RED-107687)** Wait until the services rigger pod has finished to terminate it.\n\n- **REAADB changes might fail with \"gateway timeout\" errors, mostly on OpenShift (RED-103048)** Retry the operation.\n\n- **Creating two databases with the same name directly on Redis Enterprise software will cause the service to be deleted and the database will not be available (RED-99997)** Avoid duplicating database names. Database creation via K8s has validation in place to prevent this.\n\n- **Installing the operator bundle produces warning: `Warning: would violate PodSecurity \"restricted: v1.24\"` (RED-97381)** Ignore the warning. This issue is documented as benign on official Red Hat documentation.\n\n- **RERC resources must have a unique name (RED-96302)** The string \"rec-name\"/\"rec-namespace\" must be different from all other participating clusters in the Active-Active database.\n\n- **Admission is not blocking REAADB with `shardCount` which exceeds license quota (RED-96301)** Fix the problems with the REAADB and reapply.\n\n- **Active-Active controller only supports global database options. Configuration specific to location is not supported (RED-86490)**\n\n- **Active-Active setup removal might keep services or routes undeleted (RED-77752)** Delete services or routes manually if you encounter this problem.\n\n- **`autoUpgrade` set to `true` can cause unexpected bdb upgrades when `redisUpgradePolicy` is set to `true` (RED-72351)** Contact support if your deployment is impacted.\n\n- **Following the previous quick start guide version causes issues with creating an REDB due to unrecognized memory field name (RED-69515)** The workaround is to use the newer (current) revision of Deploy Redis Enterprise Software for Kubernetes.\n\n- **PVC size issues when using decimal value in spec (RED-62132)** Make sure you use integer values for the PVC size.\n\n- **REC might report error states on initial startup (RED-61707)** There is no workaround at this time except to ignore the errors.\n\n- **Hashicorp Vault integration - no support for Gesher (RED-55080)** There is no workaround for this issue. Gesher support has been deprecated.\n\n- **REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED-47254)** When REC clusters are deployed on Kubernetes clusters without synchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes.\n\n- **Deleting an OpenShift project with an REC deployed may hang (RED-47192)** When an REC cluster is deployed in a project (namespace) and has REDB resources, the REDB resources must be deleted first before the REC can be deleted. Therefore, until the REDB resources are deleted, the project deletion will hang. The fix is to delete the REDB resources first and the REC second. Then, you can delete the project.\n\n- **Clusters must be named 'rec' in OLM-based deployments (RED-39825)** In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) are bound to a specific service account name (namely, \"rec\"). The workaround is to name the cluster \"rec\".\n\n- **Readiness probe incorrect on failures (RED-39300)** STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure.\n\n- **Internal DNS and Kubernetes DNS may have conflicts (RED-37462)** DNS conflicts are possible between the cluster `mdns_server` and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names.\n\n- **K8s-based 5.4.10 clusters seem to negatively affect existing 5.4.6 clusters (RED-37233)** Upgrade clusters to latest version.\n\n- **Node CPU usage is reported instead of pod CPU usage (RED-36884)** In Kubernetes, the reported node CPU usage is the usage of the Kubernetes worker node hosting the REC pod.\n\n- **An unreachable cluster has status running (RED-32805)** When a cluster is in an unreachable state, the state remains `running` instead of triggering an error.\n\n- **Long cluster names cause routes to be rejected  (RED-25871)** A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name exceeds 63 characters. The workaround is to limit the cluster name to 20 characters or fewer.\n\n- **Cluster CR (REC) errors are not reported after invalid updates (RED-25542)** A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/8-0-10-releases/8-0-10-21-feb2026","title":"Redis Enterprise for Kubernetes 8.0.10-21 (February 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-10-releases/8-0-10-21-feb2026/","summary":"Feature release with OSS Cluster API support for external clients, ARM architecture support, and Redis Software 8.0.10-64.","aliases":["/operate/kubernetes/release-notes/8-0-10-releases/8-0-10-19-february2026/"],"tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"fa85c65f044999e325fc2035acccbc9ae6a5974724a4408622da594798703d74","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise for Kubernetes 8.0.10-21 is a feature release that supports [Redis Software 8.0.10-64\n](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-10-64) and includes new features and enhancements."},{"id":"highlights","title":"Highlights","role":"content","text":"- OSS Cluster API support for external clients\n- ARM architecture support\n- Redis Software 8.0.10-64 support"},{"id":"enhancements","title":"Enhancements","role":"content","text":"- Added OSS Cluster API support for external clients <!--RED-51898-->\n- Added ARM architecture support <!--RED-164742-->\n- Added Vault secret support for user-defined modules <!--RED-178449-->\n- Added `extraLabels` injection to resources created by the services rigger <!--RED-180274-->\n- Added `clusterCredentialSecretName` customization with Kubernetes secrets <!--RED-180513-->\n- REC username default value now derives from the cluster credentials secret <!--RED-180515-->\n- Added Kubernetes 1.35 support on select platforms <!--RED-181443-->\n\n> [!NOTE]\n> When you pull images from container registries such as Docker Hub, ARM support is transparent. The registry automatically serves the correct image based on the node architecture (AMD64 or ARM64)."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- Fixed invalid `containerStatuses` indexing caused by incorrect order assumption <!--RED-175259-->\n- Removed unnecessary update calls from the services rigger to load balancers <!--RED-176881-->\n- Fixed multiple security issues"},{"id":"api-changes","title":"API changes","role":"content","text":"| **CRD** | **Field** | **Change** | **Description** |\n|---|---|---|---|\n| REDB | `spec.ossClusterSettings` | Added | Additional OSS cluster mode settings. |\n| REDB | `spec.ossClusterSettings.enableExternalAccess` | Added | Toggles whether this database supports external access in OSS cluster mode. When enabled, advertised database topology includes the external endpoints for the Redis Enterprise nodes hosting the database shards. The external access mechanism (such as LoadBalancer services) is configured using the `ossClusterSettings.externalAccessType` field of the RedisEnterpriseCluster. When external access is enabled, the corresponding database secret will have the list of primary shard IPs in the `oss_startup_nodes` field. |"},{"id":"supported-distributions","title":"Supported distributions","role":"compatibility","text":"The following table shows supported distributions at the time of this release. You can also find this list in [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions).\n\n<span title=\"Check mark icon\">&#x2705;</span> **Supported** – This distribution is supported for this version of Redis Enterprise Software for Kubernetes.\n\n<span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> **Deprecated** – This distribution is still supported for this version of Redis Enterprise Software for Kubernetes, but support will be removed in a future release.\n\n<span title=\"X icon\">&#x274c;</span> **End of life** – Support for this distribution ended.\n\nAny distribution not listed in the table is not supported for production workloads.\n\n| Kubernetes version | **1.30** | **1.31** | **1.32** | **1.33** | **1.34** | **1.35** |\n|---|---|---|---|---|---|---|\n| **Community K8s** |  |  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Amazon EKS** |  |  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |\n| **Azure AKS** |  |  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |\n| **Google GKE** |  |  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Rancher RKE2** |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |\n| **VMware VKS** |  |  | <span title=\"Supported\">&#x2705;</span> |  |  |  |\n|  |  |  |  |  |  |  |\n| **OpenShift** | **4.17** | **4.18** | **4.19** | **4.20** |  |  |\n| --- | --- | --- | --- | --- | --- |  |\n|  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |\n|  |  |  |  |  |  |  |\n| **VMware TKGI** | **1.21** | **1.22** | **1.23** |  |  |  |\n| --- | --- | --- | --- | --- | --- |  |\n|  | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |  |"},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:8.0.10-64`\n- **Operator**: `redislabs/operator:8.0.10-21`\n- **Services Rigger**: `redislabs/k8s-controller:8.0.10-21`\n- **Callhome client**: `redislabs/re-call-home-client:8.0.10-21`"},{"id":"openshift-downloads","title":"Openshift downloads","role":"content","text":"- **OLM operator bundle**: `8.0.10-21.0`\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:8.0.10-64`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:8.0.10-21`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:8.0.10-21`\n- **Call Home Client**: `registry.connect.redhat.com/redislabs/call-home-client:8.0.10-21`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"new-limitations","title":"New limitations","role":"content","text":"- **SSO configuration does not work with IPv6 or dual-stack (IPv4/IPv6) clusters.** <!--RED-180550-->"},{"id":"existing-limitations","title":"Existing limitations","role":"content","text":"- **Upgrades from versions earlier than 7.4.2-2 are not supported.** If you use an earlier version, upgrade to 7.4.2-2 before upgrading to 8.0.10-21.\n\n- **Missing endpoint for admission endpoint (rare). (RED-119469)** Restart the operator pod.\n\n- **The REDB `redisVersion` field cannot be used for memcached databases. (RED-119152)**\n\n- **Modifying the database suffix for an Active-Active database while the services rigger is terminating causes a loop. (RED-107687)** The services rigger deletes and recreates ingress or route resources repeatedly. Wait for the services rigger pod to finish terminating before modifying the suffix.\n\n- **REAADB changes might fail with \"gateway timeout\" errors, especially on OpenShift. (RED-103048)** Retry the operation.\n\n- **Creating two databases with the same name in the Redis Enterprise Cluster Manager UI deletes the service and makes the database unavailable. (RED-99997)** Avoid duplicate database names. The admission controller prevents duplicate names when you create databases through the Kubernetes operator.\n\n- **Installing the operator bundle produces the warning `Warning: would violate PodSecurity \"restricted: v1.24\"`. (RED-97381)** Ignore this warning. Red Hat documentation identifies this issue as benign.\n\n- **RERC resources must have a unique name. (RED-96302)** The `rec-name/rec-namespace` string must differ from all other participating clusters in the Active-Active database.\n\n- **Admission does not block REAADB resources with a `shardCount` that exceeds the license quota. (RED-96301)** Correct the REAADB configuration and reapply.\n\n- **The Active-Active controller supports only global database options. (RED-86490)** Location-specific configuration is not supported.\n\n- **Removing an Active-Active setup might leave services or routes undeleted. (RED-77752)** Delete the services or routes manually.\n\n- **Setting `autoUpgrade` to `true` can cause unexpected database upgrades when `redisUpgradePolicy` is also `true`. (RED-72351)** Contact support if your deployment is affected.\n\n- **Using the previous quick start guide causes REDB creation issues due to an unrecognized memory field name. (RED-69515)** Use the current version of the Deploy Redis Enterprise Software for Kubernetes guide.\n\n- **PVC size does not work with decimal values. (RED-62132)** Use integer values for the PVC size.\n\n- **REC might report error states on initial startup. (RED-61707)** No workaround exists. Ignore the errors.\n\n- **HashiCorp Vault integration does not support Gesher. (RED-55080)** No workaround exists. Gesher support is deprecated.\n\n- **REC clusters fail to start on Kubernetes clusters with unsynchronized clocks. (RED-47254)** Use NTP to synchronize the underlying Kubernetes nodes.\n\n- **Deleting an OpenShift project with a deployed REC might hang. (RED-47192)** When an REC cluster is deployed in a project (namespace) with REDB resources, you must delete the REDB resources before deleting the REC. The project deletion hangs until you delete the REDB resources. Delete the REDB resources first, then delete the REC, and then delete the project.\n\n- **Clusters must be named \"rec\" in OLM-based deployments. (RED-39825)** When you deploy the operator through OLM, the security context constraints (SCC) are bound to a specific service account name (\"rec\"). The deployment fails if the cluster has a different name.\n\n- **Readiness probe does not correctly report failures. (RED-39300)** The StatefulSet readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure.\n\n- **Internal DNS and Kubernetes DNS might conflict. (RED-37462)** DNS conflicts can occur between the cluster `mdns_server` and Kubernetes DNS. This affects only DNS resolution from within cluster nodes for Kubernetes DNS names.\n\n- **Kubernetes-based 5.4.10 clusters might negatively affect existing 5.4.6 clusters. (RED-37233)** Upgrade your clusters to the latest version.\n\n- **Node CPU usage is reported instead of pod CPU usage. (RED-36884)** The reported CPU usage is for the Kubernetes worker node hosting the REC pod, not the pod itself.\n\n- **An unreachable cluster shows status as running. (RED-32805)** When a cluster is unreachable, the status remains `running` instead of showing an error.\n\n- **Long cluster names cause routes to be rejected. (RED-25871)** A cluster name longer than 20 characters causes route rejection because the host part of the domain name exceeds 63 characters. Limit the cluster name to 20 characters or fewer.\n\n- **Cluster CR (REC) errors are not reported after invalid updates. (RED-25542)** A cluster CR specification error is not reported if you update two or more invalid CR resources in sequence."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/8-0-10-releases/8-0-10-22-feb2026","title":"Redis Enterprise for Kubernetes 8.0.10-22 (February 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-10-releases/8-0-10-22-feb2026/","summary":"This is a maintenance release to support Redis Enterprise Software version 8.0.10-76.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"e50f67f269852e1f21d56c5745906ed38eccfbc1bc988bfa601b905ba05d5262","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support Redis Enterprise Software version 8.0.10-76. For version changes, supported distributions, and known limitations, see the [release notes for 8.0.10-21 (February 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-10-releases/8-0-10-21-feb2026)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:8.0.10-76`\n- **Operator**: `redislabs/operator:8.0.10-22`\n- **Services Rigger**: `redislabs/k8s-controller:8.0.10-22`\n- **Callhome client**: `redislabs/re-call-home-client:8.0.10-22`"},{"id":"openshift-downloads","title":"Openshift downloads","role":"content","text":"- **OLM operator bundle**: `8.0.10-22.0`\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:8.0.10-76`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:8.0.10-22`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:8.0.10-22`\n- **Call Home Client**: `registry.connect.redhat.com/redislabs/call-home-client:8.0.10-22`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [8.0.10 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-10-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/8-0-10-releases/8-0-10-23-feb2026","title":"Redis Enterprise for Kubernetes 8.0.10-23 (February 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-10-releases/8-0-10-23-feb2026/","summary":"This is a maintenance release to support Redis Enterprise Software version 8.0.10-81.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"5e5d4d8a967ff667f331e6821d372e6d5eaae55d8d601d7bceb80a35abb401bb","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support Redis Enterprise Software version 8.0.10-81. For version changes, supported distributions, and known limitations, see the [release notes for 8.0.10-21 (February 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-10-releases/8-0-10-21-feb2026)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:8.0.10-81`\n- **Operator**: `redislabs/operator:8.0.10-23`\n- **Services Rigger**: `redislabs/k8s-controller:8.0.10-23`\n- **Call Home Client**: `redislabs/re-call-home-client:8.0.10-23`"},{"id":"openshift-downloads","title":"OpenShift downloads","role":"content","text":"- **OLM operator bundle**: `8.0.10-23.0`\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:8.0.10-81`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:8.0.10-23`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:8.0.10-23`\n- **Call Home Client**: `registry.connect.redhat.com/redislabs/call-home-client:8.0.10-23`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [8.0.10 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-10-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/8-0-10-releases","title":"Redis Enterprise for Kubernetes 8.0.10 release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-10-releases/","summary":"Releases with support for Redis Enterprise Software 8.0.10","tags":["docs","operate","kubernetes"],"last_updated":"2026-05-07T16:21:12-05:00","children":[{"id":"operate/kubernetes/release-notes/8-0-10-releases/8-0-10-23-feb2026","summary":"This is a maintenance release to support Redis Enterprise Software version 8.0.10-81.","title":"Redis Enterprise for Kubernetes 8.0.10-23 (February 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-10-releases/8-0-10-23-feb2026/"},{"id":"operate/kubernetes/release-notes/8-0-10-releases/8-0-10-22-feb2026","summary":"This is a maintenance release to support Redis Enterprise Software version 8.0.10-76.","title":"Redis Enterprise for Kubernetes 8.0.10-22 (February 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-10-releases/8-0-10-22-feb2026/"},{"id":"operate/kubernetes/release-notes/8-0-10-releases/8-0-10-21-feb2026","summary":"Feature release with OSS Cluster API support for external clients, ARM architecture support, and Redis Software 8.0.10-64.","title":"Redis Enterprise for Kubernetes 8.0.10-21 (February 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-10-releases/8-0-10-21-feb2026/"}],"page_type":"content","content_hash":"480a5bf3278cecae3544477dd1cb2c0daeadd7314bbe06af31b9264f9b32f509","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise for Kubernetes 8.0.10 includes bug fixes, enhancements, and support for Redis Enterprise Software 8.0.10. The latest release is 8.0.10-23 with support for Redis Enterprise Software version 8.0.10-81."},{"id":"detailed-release-notes","title":"Detailed release notes","role":"content","text":"| Version (Release date) | Major changes |\n| --- | --- |\n| [8.0.10-23 (February 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-10-releases/8-0-10-23-feb2026/) | This is a maintenance release to support Redis Enterprise Software version 8.0.10-81. |\n| [8.0.10-22 (February 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-10-releases/8-0-10-22-feb2026/) | This is a maintenance release to support Redis Enterprise Software version 8.0.10-76. |\n| [8.0.10-21 (February 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-10-releases/8-0-10-21-feb2026/) | Feature release with OSS Cluster API support for external clients, ARM architecture support, and Redis Software 8.0.10-64. |"},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"new-limitations","title":"New limitations","role":"content","text":"- **Expired license causes pod readiness failures, blocking recovery and upgrades** <!--RED-185977--> If a pod is stuck during upgrade, manually update the licenses via the REST API.\n\n- **SSO configuration doesn't work with IPv6 or dual stack (IPv4/IPv6) clusters.** <!--RED-180550-->"},{"id":"existing-limitations","title":"Existing limitations","role":"content","text":"- **Only upgrades from 7.4.2-2 and later are supported.** If you are using an earlier version, install 7.4.2-2 before upgrading to 8.0.10-21.\n\n- **Missing endpoint for admission endpoint (rare) (RED-119469)** Restart the operator pod.\n\n- **The REDB \"redisVersion\" field can't be used for memcached databases (RED-119152)**\n\n- **When modifying the database suffix for an Active-Active database, while the service-rigger is in a terminating state, the services-rigger will delete and create the ingress or route resources in a loop (RED-107687)** Wait until the services rigger pod has finished to terminate it.\n\n- **REAADB changes might fail with \"gateway timeout\" errors, mostly on OpenShift (RED-103048)** Retry the operation.\n\n- **Creating two databases with the same name directly in the Redis Enterprise cluster manager UI will cause the service to be deleted and the database will not be available (RED-99997)** Avoid duplicating database names. The admission controller prevents duplicate database names when databases are created via the Kubernetes operator.\n\n- **Installing the operator bundle produces warning: `Warning: would violate PodSecurity \"restricted: v1.24\"` (RED-97381)** Ignore the warning. This issue is documented as benign on official Red Hat documentation.\n\n- **RERC resources must have a unique name (RED-96302)** The string \"rec-name\"/\"rec-namespace\" must be different from all other participating clusters in the Active-Active database.\n\n- **Admission is not blocking REAADB with `shardCount` which exceeds license quota (RED-96301)** Fix the problems with the REAADB and reapply.\n\n- **Active-Active controller only supports global database options. Configuration specific to location is not supported (RED-86490)**\n\n- **Active-Active setup removal might keep services or routes undeleted (RED-77752)** Delete services or routes manually if you encounter this problem.\n\n- **`autoUpgrade` set to `true` can cause unexpected bdb upgrades when `redisUpgradePolicy` is set to `true` (RED-72351)** Contact support if your deployment is impacted.\n\n- **Following the previous quick start guide version causes issues with creating an REDB due to unrecognized memory field name (RED-69515)** The workaround is to use the newer (current) revision of Deploy Redis Enterprise Software for Kubernetes.\n\n- **PVC size issues when using decimal value in spec (RED-62132)** Make sure you use integer values for the PVC size.\n\n- **REC might report error states on initial startup (RED-61707)** There is no workaround at this time except to ignore the errors.\n\n- **Hashicorp Vault integration - no support for Gesher (RED-55080)** There is no workaround for this issue. Gesher support has been deprecated.\n\n- **REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED-47254)** When REC clusters are deployed on Kubernetes clusters without synchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes.\n\n- **Deleting an OpenShift project with an REC deployed may hang (RED-47192)** When an REC cluster is deployed in a project (namespace) and has REDB resources, the REDB resources must be deleted first before the REC can be deleted. Therefore, until the REDB resources are deleted, the project deletion will hang. The fix is to delete the REDB resources first and the REC second. Then, you can delete the project.\n\n- **Clusters must be named 'rec' in OLM-based deployments (RED-39825)** In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) are bound to a specific service account name (namely, \"rec\"). The workaround is to name the cluster \"rec\".\n\n- **Readiness probe incorrect on failures (RED-39300)** STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure.\n\n- **Internal DNS and Kubernetes DNS may have conflicts (RED-37462)** DNS conflicts are possible between the cluster `mdns_server` and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names.\n\n- **K8s-based 5.4.10 clusters seem to negatively affect existing 5.4.6 clusters (RED-37233)** Upgrade clusters to latest version.\n\n- **Node CPU usage is reported instead of pod CPU usage (RED-36884)** In Kubernetes, the reported node CPU usage is the usage of the Kubernetes worker node hosting the REC pod.\n\n- **An unreachable cluster has status running (RED-32805)** When a cluster is in an unreachable state, the state remains `running` instead of triggering an error.\n\n- **Long cluster names cause routes to be rejected  (RED-25871)** A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name exceeds 63 characters. The workaround is to limit the cluster name to 20 characters or fewer.\n\n- **Cluster CR (REC) errors are not reported after invalid updates (RED-25542)** A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/8-0-16-releases/8-0-16-24-march2026","title":"Redis Enterprise for Kubernetes 8.0.16-24 (March 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-16-releases/8-0-16-24-march2026/","summary":"Maintenance release of Redis Enterprise for Kubernetes 8.0.10 with bug fixes and Redis Software 8.0.16-29 support.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"5c517634ee6a91c32eb35471261eabab91f25796dbddcc185168095a573906a4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise for Kubernetes 8.0.16-24 is a maintenance release of [Redis Enterprise for Kubernetes 8.0.10](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-10-releases). \n\nFor version changes, supported distributions, and known limitations, see the [release notes for 8.0.10](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-10-releases)."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- Fixed a UBI9 dependency error that blocked operator image builds <!--RED-187506-->\n- Added testing for OpenShift 4.21 <!--RED-190160-->\n- Fixed Vault cache timeouts <!--RED-189494-->"},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:8.0.16-29`\n- **Operator**: `redislabs/operator:8.0.16-24`\n- **Services Rigger**: `redislabs/k8s-controller:8.0.16-24`\n- **Callhome client**: `redislabs/re-call-home-client:8.0.16-24`"},{"id":"openshift-downloads","title":"Openshift downloads","role":"content","text":"- **OLM operator bundle**: `8.0.16-24.4`\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:8.0.16-29`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:8.0.16-24`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:8.0.16-24`\n- **Call Home Client**: `registry.connect.redhat.com/redislabs/call-home-client:8.0.16-24`"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/8-0-16-releases/8-0-16-25-march2026","title":"Redis Enterprise for Kubernetes 8.0.16-25 (March 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-16-releases/8-0-16-25-march2026/","summary":"Maintenance release of Redis Enterprise for Kubernetes 8.0.10 with Redis Software 8.0.16-64 support.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"ff53d2c2f826ec939528f988caa4a19ee18b054c17365f269c7db1cc090d735b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise for Kubernetes 8.0.16-25 is a maintenance release of [Redis Enterprise for Kubernetes 8.0.10](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-10-releases).\n\nFor version changes, supported distributions, and known limitations, see the [release notes for 8.0.10](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-10-releases)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:8.0.16-33`\n- **Operator**: `redislabs/operator:8.0.16-25`\n- **Services Rigger**: `redislabs/k8s-controller:8.0.16-25`\n- **Call Home Client**: `redislabs/re-call-home-client:8.0.16-25`"},{"id":"openshift-downloads","title":"OpenShift downloads","role":"content","text":"- **OLM operator bundle**: `8.0.16-25.0`\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:8.0.16-33`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:8.0.16-25`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:8.0.16-25`\n- **Call Home Client**: `registry.connect.redhat.com/redislabs/call-home-client:8.0.16-25`"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/8-0-16-releases","title":"Redis Enterprise for Kubernetes 8.0.16 release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-16-releases/","summary":"Releases with support for Redis Enterprise Software 8.0.16","tags":["docs","operate","kubernetes"],"last_updated":"2026-05-07T16:21:12-05:00","children":[{"id":"operate/kubernetes/release-notes/8-0-16-releases/8-0-16-24-march2026","summary":"Maintenance release of Redis Enterprise for Kubernetes 8.0.10 with bug fixes and Redis Software 8.0.16-29 support.","title":"Redis Enterprise for Kubernetes 8.0.16-24 (March 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-16-releases/8-0-16-24-march2026/"},{"id":"operate/kubernetes/release-notes/8-0-16-releases/8-0-16-25-march2026","summary":"Maintenance release of Redis Enterprise for Kubernetes 8.0.10 with Redis Software 8.0.16-64 support.","title":"Redis Enterprise for Kubernetes 8.0.16-25 (March 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-16-releases/8-0-16-25-march2026/"}],"page_type":"content","content_hash":"ee1fb3e662daa8e085c9c4d0283cbaa8a3f75d96bb3902a90e1e40ae12e4f862","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise for Kubernetes 8.0.16-24 is a maintenance release of Redis Enterprise for Kubernetes 8.0.10. The latest release is 8.0.16-24 with support for Redis Enterprise Software version 8.0.16-29."},{"id":"detailed-release-notes","title":"Detailed release notes","role":"content","text":"| Version (Release date) | Major changes |\n| --- | --- |\n| [8.0.16-24 (March 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-16-releases/8-0-16-24-march2026/) | Maintenance release of Redis Enterprise for Kubernetes 8.0.10 with bug fixes and Redis Software 8.0.16-29 support. |\n| [8.0.16-25 (March 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-16-releases/8-0-16-25-march2026/) | Maintenance release of Redis Enterprise for Kubernetes 8.0.10 with Redis Software 8.0.16-64 support. |"},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"new-limitations","title":"New limitations","role":"content","text":"- **Expired license causes pod readiness failures, blocking recovery and upgrades** <!--RED-185977--> If a pod is stuck during upgrade, manually update the licenses via the REST API.\n\n- **SSO configuration doesn't work with IPv6 or dual stack (IPv4/IPv6) clusters.** <!--RED-180550-->"},{"id":"existing-limitations","title":"Existing limitations","role":"content","text":"- **Only upgrades from 7.4.2-2 and later are supported.** If you are using an earlier version, install 7.4.2-2 before upgrading.\n\n- **Missing endpoint for admission endpoint (rare) (RED-119469)** Restart the operator pod.\n\n- **The REDB \"redisVersion\" field can't be used for memcached databases (RED-119152)**\n\n- **When modifying the database suffix for an Active-Active database, while the service-rigger is in a terminating state, the services-rigger will delete and create the ingress or route resources in a loop (RED-107687)** Wait until the services rigger pod has finished to terminate it.\n\n- **REAADB changes might fail with \"gateway timeout\" errors, mostly on OpenShift (RED-103048)** Retry the operation.\n\n- **Creating two databases with the same name directly in the Redis Enterprise cluster manager UI will cause the service to be deleted and the database will not be available (RED-99997)** Avoid duplicating database names. The admission controller prevents duplicate database names when databases are created via the Kubernetes operator.\n\n- **Installing the operator bundle produces warning: `Warning: would violate PodSecurity \"restricted: v1.24\"` (RED-97381)** Ignore the warning. This issue is documented as benign on official Red Hat documentation.\n\n- **RERC resources must have a unique name (RED-96302)** The string \"rec-name\"/\"rec-namespace\" must be different from all other participating clusters in the Active-Active database.\n\n- **Admission is not blocking REAADB with `shardCount` which exceeds license quota (RED-96301)** Fix the problems with the REAADB and reapply.\n\n- **Active-Active controller only supports global database options. Configuration specific to location is not supported (RED-86490)**\n\n- **Active-Active setup removal might keep services or routes undeleted (RED-77752)** Delete services or routes manually if you encounter this problem.\n\n- **`autoUpgrade` set to `true` can cause unexpected bdb upgrades when `redisUpgradePolicy` is set to `true` (RED-72351)** Contact support if your deployment is impacted.\n\n- **Following the previous quick start guide version causes issues with creating an REDB due to unrecognized memory field name (RED-69515)** The workaround is to use the newer (current) revision of Deploy Redis Enterprise Software for Kubernetes.\n\n- **PVC size issues when using decimal value in spec (RED-62132)** Make sure you use integer values for the PVC size.\n\n- **REC might report error states on initial startup (RED-61707)** There is no workaround at this time except to ignore the errors.\n\n- **Hashicorp Vault integration - no support for Gesher (RED-55080)** There is no workaround for this issue. Gesher support has been deprecated.\n\n- **REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED-47254)** When REC clusters are deployed on Kubernetes clusters without synchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes.\n\n- **Deleting an OpenShift project with an REC deployed may hang (RED-47192)** When an REC cluster is deployed in a project (namespace) and has REDB resources, the REDB resources must be deleted first before the REC can be deleted. Therefore, until the REDB resources are deleted, the project deletion will hang. The fix is to delete the REDB resources first and the REC second. Then, you can delete the project.\n\n- **Clusters must be named 'rec' in OLM-based deployments (RED-39825)** In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) are bound to a specific service account name (namely, \"rec\"). The workaround is to name the cluster \"rec\".\n\n- **Readiness probe incorrect on failures (RED-39300)** STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure.\n\n- **Internal DNS and Kubernetes DNS may have conflicts (RED-37462)** DNS conflicts are possible between the cluster `mdns_server` and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names.\n\n- **K8s-based 5.4.10 clusters seem to negatively affect existing 5.4.6 clusters (RED-37233)** Upgrade clusters to latest version.\n\n- **Node CPU usage is reported instead of pod CPU usage (RED-36884)** In Kubernetes, the reported node CPU usage is the usage of the Kubernetes worker node hosting the REC pod.\n\n- **An unreachable cluster has status running (RED-32805)** When a cluster is in an unreachable state, the state remains `running` instead of triggering an error.\n\n- **Long cluster names cause routes to be rejected  (RED-25871)** A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name exceeds 63 characters. The workaround is to limit the cluster name to 20 characters or fewer.\n\n- **Cluster CR (REC) errors are not reported after invalid updates (RED-25542)** A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/8-0-18-releases/8-0-18-11-april2026","title":"Redis Enterprise for Kubernetes 8.0.18-11 (April 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-18-releases/8-0-18-11-april2026/","summary":"Feature release with OSS Cluster API support for external clients, ARM architecture support, and Redis Software 8.0.18-23.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"04e5bb7b7edeac1b4e4bf1879b1b407a861a13046c940cba426563163f88a2ad","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise for Kubernetes 8.0.18-11 is a feature release that supports [Redis Software 8.0.18-23\n](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases) and includes new features, bug fixes, and enhancements."},{"id":"highlights","title":"Highlights","role":"content","text":"- Support for all CNCF certified 1.34 and 1.35 Kubernetes distributions.\n- Support for Redis Software 8.0.18-23.\n- Support for managing TLS secrets through the cert manager.\n- Minimal base image available by adding `.minimal` to the version tag in the REC."},{"id":"enhancements","title":"Enhancements","role":"content","text":"- Added support for REC creation during install using Helm <!--RED-109678-->\n- Built Redis Software container with minimal base image for security <!--RED-79393-->\n- Added support for cert manager to manage TLS secrets <!--RED-141981-->\n- Added Redis Software 8.0.18 support <!--RED-190128-->\n- Allowed ClusterCredentialSecretName to change in an existing cluster <!--RED-181951-->\n- Added support for search on disk <!--RED-182994-->\n- Added region name support for S3 backups <!--RED-186065-->\n- Added proxy connection attribute support <!--RED-182816-->"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- Fixed dynamic script copying issue that was triggering security alerts <!--RED-150023-->\n- Allowed configuration of rack awareness for existing clusters <!--RED-168869-->\n- Fixed issue where RERC edits didn't propagate across clusters if REAADBs were spread across namespaces <!--RED-180702-->\n- Security fixes\n- Fixed operator reconciliation loop causing excessive RBAC updates <!--RED-189582-->\n- Added LDAP CBA field support to the REC API <!--RED-192794-->\n- Fixed issue causing pod readiness failures due to expired license <!--RED-185977-->"},{"id":"api-changes","title":"API changes","role":"content","text":"| **CRD** | **Field** | **Change** | **Description** |\n|---|---|---|---|\n| REAADB | `spec.globalConfigurations.backup.intervalOffset` | Added | Time offset in seconds at which the periodic backup job starts, relative to 00:00 UTC for a 24-hour backup interval or to 12:00 UTC for a 12-hour backup interval. Only valid when the backup interval is 24 hours (86400 seconds) or 12 hours (43200 seconds). The offset must be less than the backup interval. If not specified, a random offset is chosen. |\n| REAADB | `spec.globalConfigurations.backup.s3.regionName` | Added | Amazon S3 region name for the backup bucket. If not specified, the region is auto-detected with a HEAD request to the bucket. Specify the region explicitly for regions where auto-detection might not work, such as AWS GovCloud (for example, `us-gov-east-1`). |\n| REAADB | `spec.globalConfigurations.connectionSettings` | Added | Database connection settings, such as proxy configuration and scheduling policy. |\n| REAADB | `spec.globalConfigurations.searchOnBigstore` | Added | Enables search module indexing on flash storage for Redis Flex databases. Only applies when `isRof=true` and Redis version 8.6 or later. Defaults to `false`. |\n| REC | `spec.cba` | Added | Enables LDAP as an identity source for certificate-based authentication (CBA). Defaults to `false`. |\n| REC | `spec.cbaIdentityOid` | Added | Certificate subject OID to use for CBA identity lookup. |\n| REC | `spec.cbaIdentitySource` | Added | Certificate subject identity source to use for LDAP lookup. One of `SubjectCN` or `SubjectOID`. Applies only when CBA is enabled. |\n| REC | `status.bundledDatabaseVersions[].featureSupport` | Added | Read-only list of feature support flags for this database version. |\n| REDB | `spec.backup.intervalOffset` | Added | Time offset in seconds at which the periodic backup job starts, relative to 00:00 UTC for a 24-hour backup interval or to 12:00 UTC for a 12-hour backup interval. Only valid when the backup interval is 24 hours (86400 seconds) or 12 hours (43200 seconds). The offset must be less than the backup interval. If not specified, a random offset is chosen. |\n| REDB | `spec.backup.s3.regionName` | Added | Amazon S3 region name for the backup bucket. If not specified, the region is auto-detected with a HEAD request to the bucket. Specify the region explicitly for regions where auto-detection might not work, such as AWS GovCloud (for example, `us-gov-east-1`). |\n| REDB | `spec.connectionSettings` | Added | Database connection settings, such as proxy configuration and scheduling policy. |\n| REDB | `spec.searchOnBigstore` | Added | Enables search module indexing on flash storage for Redis Flex databases. Only applies when `isRof=true` and Redis version 8.6 or later. Defaults to `false`. |"},{"id":"supported-distributions","title":"Supported distributions","role":"compatibility","text":"Redis Enterprise for Kubernetes is compatible with [CNCF-conformant](https://www.cncf.io/training/certification/software-conformance/) Kubernetes platforms. The operator follows standard Kubernetes APIs and practices and is designed to run consistently across certified Kubernetes environments.\n\nThe following table shows supported Kubernetes versions at the time of this release. For a list of platforms tested with this release, see [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions).\n\n| Kubernetes | **Redis <nobr>8.0.18-11</nobr>** |\n|---|---|\n| 1.35 | Supported |\n| 1.34 | Supported |\n| 1.33 | Supported |\n| 1.32 | Deprecated|\n| 1.31 | Deprecated |"},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:8.0.18-23.11` (`redislabs/redis:8.0.18-23.11.minimal` for minimal base image)\n- **Operator**: `redislabs/operator:8.0.18-11`\n- **Services Rigger**: `redislabs/k8s-controller:8.0.18-11`\n- **Callhome client**: `redislabs/re-call-home-client:8.0.18-11`"},{"id":"openshift-downloads","title":"Openshift downloads","role":"content","text":"- **OLM operator bundle**: `8.0.18-11.0`\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:8.0.18-23.11` (`registry.connect.redhat.com/redislabs/redis-enterprise:8.0.18-23.11.minimal` for minimal base image)\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:8.0.18-11`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:8.0.18-11`\n- **Call Home Client**: `registry.connect.redhat.com/redislabs/call-home-client:8.0.18-11`\n\n> [!NOTE]\n> When you pull images from container registries such as Docker Hub, ARM support is transparent. The registry automatically serves the correct image based on the node architecture (AMD64 or ARM64)."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"- **SSO configuration does not work with IPv6 or dual-stack (IPv4/IPv6) clusters.** <!--RED-180550-->\n\n- **Upgrades from versions earlier than 7.4.2-2 are not supported.** If you use an earlier version, upgrade to 7.4.2-2 before upgrading to this version.\n\n- **Missing endpoint for admission endpoint (rare).** Restart the operator pod. <!--RED-119469-->\n\n- **The REDB `redisVersion` field cannot be used for memcached databases.** <!--RED-119152-->\n\n- **Modifying the database suffix for an Active-Active database while the services rigger is terminating causes a loop.** The services rigger deletes and recreates ingress or route resources repeatedly. Wait for the services rigger pod to finish terminating before modifying the suffix. <!--RED-107687-->\n\n- **REAADB changes might fail with \"gateway timeout\" errors, especially on OpenShift.** Retry the operation. <!--RED-103048-->\n\n- **Creating two databases with the same name in the Redis Enterprise Cluster Manager UI deletes the service and makes the database unavailable.** Avoid duplicate database names. The admission controller prevents duplicate names when you create databases through the Kubernetes operator. <!--RED-99997-->\n\n- **Installing the operator bundle produces the warning `Warning: would violate PodSecurity \"restricted: v1.24\"`.** Ignore this warning. Red Hat documentation identifies this issue as benign. <!--RED-97381-->\n\n- **RERC resources must have a unique name.** The `rec-name/rec-namespace` string must differ from all other participating clusters in the Active-Active database. <!--RED-96302-->\n\n- **Admission does not block REAADB resources with a `shardCount` that exceeds the license quota.** Correct the REAADB configuration and reapply. <!--RED-96301-->\n\n- **The Active-Active controller supports only global database options.** Location-specific configuration is not supported. <!--RED-86490-->\n\n- **Removing an Active-Active setup might leave services or routes undeleted.** Delete the services or routes manually. <!--RED-77752-->\n\n- **Setting `autoUpgrade` to `true` can cause unexpected database upgrades when `redisUpgradePolicy` is also `true`.** Contact support if your deployment is affected. <!--RED-72351-->\n\n- **Using the previous quick start guide causes REDB creation issues due to an unrecognized memory field name.** Use the current version of the Deploy Redis Enterprise Software for Kubernetes guide. <!--RED-69515-->\n\n- **PVC size does not work with decimal values.** Use integer values for the PVC size. <!--RED-62132-->\n\n- **REC might report error states on initial startup.** No workaround exists. Ignore the errors. <!--RED-61707-->\n\n- **HashiCorp Vault integration does not support Gesher.** No workaround exists. Gesher support is deprecated. <!--RED-55080-->\n\n- **REC clusters fail to start on Kubernetes clusters with unsynchronized clocks.** Use NTP to synchronize the underlying Kubernetes nodes. <!--RED-47254-->\n\n- **Deleting an OpenShift project with a deployed REC might hang.** When an REC cluster is deployed in a project (namespace) with REDB resources, you must delete the REDB resources before deleting the REC. The project deletion hangs until you delete the REDB resources. Delete the REDB resources first, then delete the REC, and then delete the project. <!--RED-47192-->\n\n- **Clusters must be named \"rec\" in OLM-based deployments.** When you deploy the operator through OLM, the security context constraints (SCC) are bound to a specific service account name (\"rec\"). The deployment fails if the cluster has a different name. <!--RED-39825-->\n\n- **Readiness probe does not correctly report failures.** The StatefulSet readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure. <!--RED-39300-->\n\n- **Internal DNS and Kubernetes DNS might conflict.** DNS conflicts can occur between the cluster `mdns_server` and Kubernetes DNS. This affects only DNS resolution from within cluster nodes for Kubernetes DNS names. <!--RED-37462-->\n\n- **Kubernetes-based 5.4.10 clusters might negatively affect existing 5.4.6 clusters.** Upgrade your clusters to the latest version. <!--RED-37233-->\n\n- **Node CPU usage is reported instead of pod CPU usage.** The reported CPU usage is for the Kubernetes worker node hosting the REC pod, not the pod itself. <!--RED-36884-->\n\n- **An unreachable cluster shows status as running.** When a cluster is unreachable, the status remains `running` instead of showing an error. <!--RED-32805-->\n\n- **Long cluster names cause routes to be rejected.** A cluster name longer than 20 characters causes route rejection because the host part of the domain name exceeds 63 characters. Limit the cluster name to 20 characters or fewer. <!--RED-25871-->\n\n- **Cluster CR (REC) errors are not reported after invalid updates.** A cluster CR specification error is not reported if you update two or more invalid CR resources in sequence. <!--RED-25542-->"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/8-0-18-releases/8-0-18-14-may2026","title":"Redis Enterprise for Kubernetes 8.0.18-14 (May 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-18-releases/8-0-18-14-may2026/","summary":"This is a maintenance release to support Redis Enterprise Software version 8.0.18-36.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"e2e4af7870690094db410c9bbc4dfd81b29626d2b0127a0f51c6dc429299fd2d","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support Redis Enterprise Software version 8.0.18-36. For version changes, supported distributions, and known limitations, see the [release notes for 8.0.18-11 (April 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-18-releases/8-0-18-11-april2026)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:8.0.18-36.14` (`redislabs/redis:8.0.18-36.14.minimal` for minimal base image)\n- **Operator**: `redislabs/operator:8.0.18-14`\n- **Services Rigger**: `redislabs/k8s-controller:8.0.18-14`\n- **Call Home Client**: `redislabs/re-call-home-client:8.0.18-14`"},{"id":"openshift-downloads","title":"OpenShift downloads","role":"content","text":"- **OLM operator bundle**: `8.0.18-14.2`\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:8.0.18-36.14` (`registry.connect.redhat.com/redislabs/redis-enterprise:8.0.18-36.14.minimal` for minimal base image)\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:8.0.18-14`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:8.0.18-14`\n- **Call Home Client**: `registry.connect.redhat.com/redislabs/call-home-client:8.0.18-14`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [8.0.18 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-18-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/8-0-18-releases","title":"Redis Enterprise for Kubernetes 8.0.18 release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-18-releases/","summary":"Releases with support for Redis Enterprise Software 8.0.18","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","children":[{"id":"operate/kubernetes/release-notes/8-0-18-releases/8-0-18-14-may2026","summary":"This is a maintenance release to support Redis Enterprise Software version 8.0.18-36.","title":"Redis Enterprise for Kubernetes 8.0.18-14 (May 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-18-releases/8-0-18-14-may2026/"},{"id":"operate/kubernetes/release-notes/8-0-18-releases/8-0-18-11-april2026","summary":"Feature release with OSS Cluster API support for external clients, ARM architecture support, and Redis Software 8.0.18-23.","title":"Redis Enterprise for Kubernetes 8.0.18-11 (April 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-18-releases/8-0-18-11-april2026/"}],"page_type":"content","content_hash":"df42a3a29071e1a9e52da7084709e410eaec4452e40fcbd86da788211d1cfa69","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise for Kubernetes 8.0.18 includes bug fixes, enhancements, and support for Redis Software 8.0.18. The latest release is 8.0.18-14 with support for Redis Software version 8.0.18-36."},{"id":"detailed-release-notes","title":"Detailed release notes","role":"content","text":"| Version (Release date) | Major changes |\n| --- | --- |\n| [8.0.18-14 (May 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-18-releases/8-0-18-14-may2026/) | This is a maintenance release to support Redis Enterprise Software version 8.0.18-36. |\n| [8.0.18-11 (April 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-18-releases/8-0-18-11-april2026/) | Feature release with OSS Cluster API support for external clients, ARM architecture support, and Redis Software 8.0.18-23. |"},{"id":"supported-distributions","title":"Supported distributions","role":"compatibility","text":"Redis Enterprise for Kubernetes is compatible with [CNCF-conformant](https://www.cncf.io/training/certification/software-conformance/) Kubernetes platforms. The operator follows standard Kubernetes APIs and practices and is designed to run consistently across certified Kubernetes environments.\n\nThe following table shows supported Kubernetes versions at the time of this release. For a list of platforms tested with this release, see [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions).\n\n| Kubernetes | **Redis <nobr>8.0.18</nobr>** |\n|---|---|\n| 1.35 | Supported |\n| 1.34 | Supported |\n| 1.33 | Supported |\n| 1.32 | Deprecated |\n| 1.31 | Deprecated |"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"- **SSO configuration does not work with IPv6 or dual-stack (IPv4/IPv6) clusters.** <!--RED-180550-->\n\n- **Upgrades from versions earlier than 7.4.2-2 are not supported.** If you use an earlier version, upgrade to 7.4.2-2 before upgrading to this version.\n\n- **Missing endpoint for admission endpoint (rare).** Restart the operator pod. <!--RED-119469-->\n\n- **The REDB `redisVersion` field cannot be used for memcached databases.** <!--RED-119152-->\n\n- **Modifying the database suffix for an Active-Active database while the services rigger is terminating causes a loop.** The services rigger deletes and recreates ingress or route resources repeatedly. Wait for the services rigger pod to finish terminating before modifying the suffix. <!--RED-107687-->\n\n- **REAADB changes might fail with \"gateway timeout\" errors, especially on OpenShift.** Retry the operation. <!--RED-103048-->\n\n- **Creating two databases with the same name in the Redis Enterprise Cluster Manager UI deletes the service and makes the database unavailable.** Avoid duplicate database names. The admission controller prevents duplicate names when you create databases through the Kubernetes operator. <!--RED-99997-->\n\n- **Installing the operator bundle produces the warning `Warning: would violate PodSecurity \"restricted: v1.24\"`.** Ignore this warning. Red Hat documentation identifies this issue as benign. <!--RED-97381-->\n\n- **RERC resources must have a unique name.** The `rec-name/rec-namespace` string must differ from all other participating clusters in the Active-Active database. <!--RED-96302-->\n\n- **Admission does not block REAADB resources with a `shardCount` that exceeds the license quota.** Correct the REAADB configuration and reapply. <!--RED-96301-->\n\n- **The Active-Active controller supports only global database options.** Location-specific configuration is not supported. <!--RED-86490-->\n\n- **Removing an Active-Active setup might leave services or routes undeleted.** Delete the services or routes manually. <!--RED-77752-->\n\n- **Setting `autoUpgrade` to `true` can cause unexpected database upgrades when `redisUpgradePolicy` is also `true`.** Contact support if your deployment is affected. <!--RED-72351-->\n\n- **Using the previous quick start guide causes REDB creation issues due to an unrecognized memory field name.** Use the current version of the Deploy Redis Enterprise Software for Kubernetes guide. <!--RED-69515-->\n\n- **PVC size does not work with decimal values.** Use integer values for the PVC size. <!--RED-62132-->\n\n- **REC might report error states on initial startup.** No workaround exists. Ignore the errors. <!--RED-61707-->\n\n- **HashiCorp Vault integration does not support Gesher.** No workaround exists. Gesher support is deprecated. <!--RED-55080-->\n\n- **REC clusters fail to start on Kubernetes clusters with unsynchronized clocks.** Use NTP to synchronize the underlying Kubernetes nodes. <!--RED-47254-->\n\n- **Deleting an OpenShift project with a deployed REC might hang.** When an REC cluster is deployed in a project (namespace) with REDB resources, you must delete the REDB resources before deleting the REC. The project deletion hangs until you delete the REDB resources. Delete the REDB resources first, then delete the REC, and then delete the project. <!--RED-47192-->\n\n- **Clusters must be named \"rec\" in OLM-based deployments.** When you deploy the operator through OLM, the security context constraints (SCC) are bound to a specific service account name (\"rec\"). The deployment fails if the cluster has a different name. <!--RED-39825-->\n\n- **Readiness probe does not correctly report failures.** The StatefulSet readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure. <!--RED-39300-->\n\n- **Internal DNS and Kubernetes DNS might conflict.** DNS conflicts can occur between the cluster `mdns_server` and Kubernetes DNS. This affects only DNS resolution from within cluster nodes for Kubernetes DNS names. <!--RED-37462-->\n\n- **Kubernetes-based 5.4.10 clusters might negatively affect existing 5.4.6 clusters.** Upgrade your clusters to the latest version. <!--RED-37233-->\n\n- **Node CPU usage is reported instead of pod CPU usage.** The reported CPU usage is for the Kubernetes worker node hosting the REC pod, not the pod itself. <!--RED-36884-->\n\n- **An unreachable cluster shows status as running.** When a cluster is unreachable, the status remains `running` instead of showing an error. <!--RED-32805-->\n\n- **Long cluster names cause routes to be rejected.** A cluster name longer than 20 characters causes route rejection because the host part of the domain name exceeds 63 characters. Limit the cluster name to 20 characters or fewer. <!--RED-25871-->\n\n- **Cluster CR (REC) errors are not reported after invalid updates.** A cluster CR specification error is not reported if you update two or more invalid CR resources in sequence. <!--RED-25542-->"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/8-0-2-releases/8-0-2-2-october2025","title":"Redis Enterprise for Kubernetes 8.0.2-2 (October 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-2-releases/8-0-2-2-october2025/","summary":"Feature release including support for Redis Software 8.0.2, enhancements, and bug fixes.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"772ec9d57ab686e1213fe5b2e96134b3c90355efbe2e3f2cfd9793ce6275210e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This feature release of Redis Enterprise for Kubernetes supports Redis Software 8.0.2-17 and includes enhancements, platform support changes, and bug fixes.\n\n> [!WARNING]\n> **Custom modules not supported**: If you use custom (user-defined) modules, skip this release. Custom module support will be available in the next release."},{"id":"new-in-this-release","title":"New in this release","role":"content","text":"This release includes enhancements to the Redis Enterprise for Kubernetes operator, and includes features supported by Redis Software 8.0.2-17, and Redis Open Source 8.0 and 8.2.\n\n**Redis Enterprise for Kubernetes 8.0.2-2:**\nThis release includes enhancements to Redis Enterprise for Kubernetes including:\n\n- Support for RS 8.0.2-17\n- Support for the VKS platform (Broadcom/VMWare)\n- Added container probes to Redis Enterprise pods\n- Kubernetes 1.34 support (select platforms)\n- Unique per release CSV/OperatorHub channels\n\n**Redis Software 8.0.2-17:**\n\n- Automatically enabled capabilities (modules) in Redis 8\n- Metrics stream engine\n- Module management enhancements\n- Lag-aware availability API\n- Redis Flex (Redis on Flash version 2)\n\nFor more information about Redis Software 8.0.2-17, see the [Redis Software 8.0.2-17 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-2-17).\n\n**Redis Open Source 8.0 and 8.2:**\n\n- Preview of new vector set data type\n- Redis query engine improvements\n- Performance improvements\n- Enhanced ACLs\n- New hash and streams commands\n\nFor more details, see [What's new in Redis 8.0](https://redis.io/docs/latest/develop/whats-new/8-0) and [What's new in Redis 8.2](https://redis.io/docs/latest/develop/whats-new/8-2), and also the [Redis Open Source 8.0](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.0-release-notes) and [Redis Open Source 8.2](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.2-release-notes) release notes."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- Fixed operator logs to properly reflect field inconsistencies between REC and statefulset (RED-161426)\n- Applied security fixes (RED-162939, RED-163832, RED-165628, RED-168087)\n- Fixed database tags being overridden when defined by Redis Software users (RED-170322)"},{"id":"api-changes","title":"API changes","role":"content","text":"| **CRD** | **Field** | **Change** | **Description** |\n|---|---|---|---|\n| REAADB | `spec.alertSettings.bdb_proxy_cert_expiring_soon` | Add | Enables a new alert in alert settings |\n| REC | | Change | Changed the storage version of the API to v1 (from v1alpha1) |\n| REDB | `spec.alertSettings.bdb_proxy_cert_expiring_soon` | Add | Enables a new alert in alert settings |\n| REDB | `spec.moduleList` | Change | For Redis version 8 and later, bundled modules are enabled automatically, so you don't need to specify them |\n| REDB | `spec.moduleList.[DbModule].version` | Change | Module versions in regular deployments are automatically selected by the Redis Enterprise cluster. Specified module versions are ignored. In Active-Active setups, you must specify the module version. |"},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"modules-in-redis-8","title":"Modules in Redis 8","role":"content","text":"In Redis Enterprise databases created with or upgraded to Redis version 8 or later, bundled modules are enabled automatically, so you don't need to specify them in the `spec.moduleList` field. However, if you want to use a specific version of a bundled module, you can specify it in the `spec.moduleList` field. \n\nThe bundled capabilities (modules) are automatically enabled as follows:\n\n| Database type | Automatically enabled capabilities |\n|---------------|------------------------------------|\n| RAM-only | [Search and query](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search)<br />[JSON](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json)<br />[Time series](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/timeseries)<br />[Probabilistic](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/bloom)  |\n| Flash-enabled ([Redis Flex](https://redis.io/docs/latest/operate/rs/databases/flash)) | [JSON](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json)<br />[Probabilistic](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/bloom) |\n| [Active-Active](https://redis.io/docs/latest/operate/rs/databases/active-active) | [Search and query](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/search-active-active)<br />[JSON](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json) |\n\nFor more information, see [Automatically enabled capabilities in Redis 8](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-2-17#built-in-capabilities-with-redis-8)."},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"- **Custom modules are not supported** with this version. Only the capabilities (modules) listed above are supported in this release. **If you use user-defined modules, don't upgrade to this version.** The Redis Enterprise version used with this operator version includes significant changes to modules, including using built-in modules by default on databases. For more details, see the Redis Software release notes.\n\n- **TKG (TKG) is no longer supported**. Broadcom/VMware doesn't plan to support this platform in the near future. We recommend that you migrate to TKGI/VKS.\n\nFor additional breaking changes related to Redis Software 8.0.2-17, see [Redis 8 breaking changes](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-2-17#redis-8-breaking-changes)."},{"id":"deprecations","title":"Deprecations","role":"content","text":"- The RedisEnterpriseCluster (REC) v1alpha1 API version is deprecated."},{"id":"supported-distributions","title":"Supported distributions","role":"compatibility","text":"The following table shows supported distributions at the time of this release. You can also find this list in [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions).\n\n<span title=\"Check mark icon\">&#x2705;</span> **Supported** – This distribution is supported for this version of Redis Enterprise Software for Kubernetes.\n\n<span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> **Deprecated** – This distribution is still supported for this version of Redis Enterprise Software for Kubernetes, but support will be removed in a future release.\n\n<span title=\"X icon\">&#x274c;</span> **End of life** – Support for this distribution ended.\n\nAny distribution not listed in the table is not supported for production workloads.\n\n|  |  |  |  |  |  |  |  |\n|---|---|---|---|---|---|---|---|\n| Kubernetes version | **1.28** | **1.29** | **1.30** | **1.31** | **1.32** | **1.33** | **1.34** |\n| **Community K8s** |  |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Amazon EKS** |  |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Azure AKS** |  |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Google GKE** |  |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Rancher RKE2** | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |\n| **VMware TKG 2.5** |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"X icon\">&#x274c;</span> |  |  |  |  |\n| **VMware VKS** |  |  |  |  | <span title=\"Supported\">&#x2705;</span> |  |  |\n|  |  |  |  |  |  |  |  |\n| **OpenShift** | **4.15** | **4.16** | **4.17** | **4.18** | **4.19** |  |  |\n| --- | --- | --- | --- | --- | --- | --- | --- |\n|  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |\n|  |  |  |  |  |  |  |  |\n| **VMware TKGI** | **1.19** | **1.20** | **1.21** | **1.22** |  |  |  |\n| --- | --- | --- | --- | --- | --- | --- | --- |\n|  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |  |"},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:8.0.2-17`\n- **Operator**: `redislabs/operator:8.0.2-2`\n- **Services Rigger**: `redislabs/k8s-controller:8.0.2-2`\n- **Callhome client**: `redislabs/re-call-home-client:8.0.2-2`"},{"id":"openshift-downloads","title":"Openshift downloads","role":"content","text":"- **OLM operator bundle**: `8.0.2-2.9`\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:8.0.2-17`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:8.0.2-2`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:8.0.2-2`\n- **Call Home Client**: `registry.connect.redhat.com/redislabs/call-home-client:8.0.2-2`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"- **Only upgrades from 7.4.2-2 and later are supported.** If you are using an earlier version, install 7.4.2-2 before upgrading to 8.0.2-2.\n\n- **You must keep existing moduleList version numbers when upgrading redisVersion.** When upgrading a database's `redisVersion`, you must keep the existing module version numbers in `moduleList` unchanged. If you modify the module versions in `moduleList` during a `redisVersion` upgrade, the upgrade request will fail. The database will automatically use the bundled module versions for the new Redis version, regardless of what versions are specified in `moduleList`. This applies to both REDB and REAADB databases.\n\n- **Custom certificate upload is not supported for internode encryption (RED-173229).** Internode communication between cluster nodes continues to use default self-signed certificates and cannot be configured with customer-provided certificates.\n\n- **Missing endpoint for admission endpoint (rare) (RED-119469)** Restart the operator pod.\n\n- **The REDB \"redisVersion\" field can't be used for memcached databases(RED-119152)**\n\n- **Database suffix changes during service-rigger termination cause resource recreation loops (RED-107687)** If you modify the database suffix for an Active-Active database while the service-rigger is in a terminating state, the service-rigger will continuously delete and recreate ingress or route resources. Wait until the services rigger pod has finished terminating before making suffix changes.\n\n- **REAADB changes might fail with \"gateway timeout\" errors, mostly on OpenShift (RED-103048)** Retry the operation.\n\n- **Creating two databases with the same name directly in the Redis Enterprise cluster manager UI will cause the service to be deleted and the database will not be available (RED-99997)** Avoid duplicating database names. The admission controller prevents duplicate database names when databases are created via the Kubernetes operator.\n\n- **Installing the operator bundle produces warning: `Warning: would violate PodSecurity \"restricted: v1.24\"` (RED-97381)** Ignore the warning. This issue is documented as benign on official Red Hat documentation.\n\n- **RERC resources must have a unique name (RED-96302)** The string \"rec-name\"/\"rec-namespace\" must be different from all other participating clusters in the Active-Active database.\n\n- **Admission is not blocking REAADB with `shardCount` which exceeds license quota (RED-96301)** Fix the problems with the REAADB and reapply.\n\n- **Active-Active controller only supports global database options. Configuration specific to location is not supported (RED-86490)**\n\n- **Active-Active setup removal might keep services or routes undeleted (RED-77752)** Delete services or routes manually if you encounter this problem.\n\n- **`autoUpgrade` set to `true` can cause unexpected bdb upgrades when `redisUpgradePolicy` is set to `true` (RED-72351)** Contact support if your deployment is impacted.\n\n- **Following the previous quick start guide version causes issues with creating an REDB due to unrecognized memory field name (RED-69515)** The workaround is to use the newer (current) revision of Deploy Redis Enterprise Software for Kubernetes.\n\n- **PVC size issues when using decimal value in spec (RED-62132)** Make sure you use integer values for the PVC size.\n\n- **REC might report error states on initial startup (RED-61707)** There is no workaround at this time except to ignore the errors.\n\n- **Hashicorp Vault integration - no support for Gesher (RED-55080)** There is no workaround for this issue. Gesher support has been deprecated.\n\n- **REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED-47254)** When REC clusters are deployed on Kubernetes clusters without synchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes.\n\n- **Deleting an OpenShift project with an REC deployed may hang (RED-47192)** When an REC cluster is deployed in a project (namespace) and has REDB resources, the REDB resources must be deleted first before the REC can be deleted. Therefore, until the REDB resources are deleted, the project deletion will hang. The fix is to delete the REDB resources first and the REC second. Then, you can delete the project.\n\n- **Clusters must be named 'rec' in OLM-based deployments (RED-39825)** In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) are bound to a specific service account name (namely, \"rec\"). The workaround is to name the cluster \"rec\".\n\n- **Readiness probe incorrect on failures (RED-39300)** STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure.\n\n- **Internal DNS and Kubernetes DNS may have conflicts (RED-37462)** DNS conflicts are possible between the cluster `mdns_server` and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names.\n\n- **K8s-based 5.4.10 clusters seem to negatively affect existing 5.4.6 clusters (RED-37233)** Upgrade clusters to latest version.\n\n- **Node CPU usage is reported instead of pod CPU usage (RED-36884)** In Kubernetes, the reported node CPU usage is the usage of the Kubernetes worker node hosting the REC pod.\n\n- **An unreachable cluster has status running (RED-32805)** When a cluster is in an unreachable state, the state remains `running` instead of triggering an error.\n\n- **Long cluster names cause routes to be rejected  (RED-25871)** A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name exceeds 63 characters. The workaround is to limit the cluster name to 20 characters or fewer.\n\n- **Cluster CR (REC) errors are not reported after invalid updates (RED-25542)** A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/8-0-2-releases/8-0-2-6-december2025","title":"Redis Enterprise for Kubernetes 8.0.2-6 (December 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-2-releases/8-0-2-6-december2025/","summary":"Maintenance release including support for Redis Software 8.0.2-41, enhancements, and bug fixes.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"64883048c836dab9a3851e2f83d304d2b6e99d716801fae07bcedda630152ca8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This maintenance release of Redis Enterprise for Kubernetes supports Redis Software 8.0.2-41 and includes enhancements, API changes, and bug fixes.\n\n> [!WARNING]\n> **Custom modules not supported**: If you use custom (user-defined) modules, skip this release. Custom module support will be available in the next release."},{"id":"enhancements","title":"Enhancements","role":"content","text":"- Support for Redis Enterprise Software version 8.0.2-41\n- Added API fields to configure internode encryption (RED-174881)"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- Fixed upgrade issues with large clusters (RED-176468)"},{"id":"api-changes","title":"API changes","role":"content","text":"| **CRD** | **Field** | **Change** | **Description** |\n|---|---|---|---|\n| REC | spec.dpInternodeEncryptionCertificateSecretName | Add | Secret name to use for cluster's Data Plane Internode Encryption (DPINE) certificate. If left blank, a cluster-provided certificate will be used. |\n| REC | spec.cpInternodeEncryptionCertificateSecretName | Add | Secret name to use for cluster's Control Plane Internode Encryption (CPINE) certificate. If left blank, a cluster-provided certificate will be used. |"},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"modules-in-redis-8","title":"Modules in Redis 8","role":"content","text":"In Redis Enterprise databases created with or upgraded to Redis version 8 or later, bundled modules are enabled automatically, so you don't need to specify them in the `spec.moduleList` field. However, if you want to use a specific version of a bundled module, you can specify it in the `spec.moduleList` field. \n\nThe bundled capabilities (modules) are automatically enabled as follows:\n\n| Database type | Automatically enabled capabilities |\n|---------------|------------------------------------|\n| RAM-only | [Search and query](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search)<br />[JSON](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json)<br />[Time series](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/timeseries)<br />[Probabilistic](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/bloom)  |\n| Flash-enabled ([Redis Flex](https://redis.io/docs/latest/operate/rs/databases/flash)) | [JSON](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json)<br />[Probabilistic](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/bloom) |\n| [Active-Active](https://redis.io/docs/latest/operate/rs/databases/active-active) | [Search and query](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/search-active-active)<br />[JSON](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json) |\n\nFor more information, see [Automatically enabled capabilities in Redis 8](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-2-17#built-in-capabilities-with-redis-8)."},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"- **Custom modules are not supported** with this version. Only the capabilities (modules) listed above are supported in this release. **If you use user-defined modules, don't upgrade to this version.** The Redis Enterprise version used with this operator version includes significant changes to modules, including using built-in modules by default on databases. For more details, see the Redis Software release notes.\n\n- **TKG (TKG) is no longer supported**. Broadcom/VMware doesn't plan to support this platform in the near future. We recommend that you migrate to TKGI/VKS.\n\nFor additional breaking changes related to Redis Software 8.0.2-41, see [Redis 8 breaking changes](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-2-17#redis-8-breaking-changes)."},{"id":"deprecations","title":"Deprecations","role":"content","text":"- The RedisEnterpriseCluster (REC) v1alpha1 API version is deprecated."},{"id":"supported-distributions","title":"Supported distributions","role":"compatibility","text":"The following table shows supported distributions at the time of this release. You can also find this list in [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions).\n\n<span title=\"Check mark icon\">&#x2705;</span> **Supported** – This distribution is supported for this version of Redis Enterprise Software for Kubernetes.\n\n<span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> **Deprecated** – This distribution is still supported for this version of Redis Enterprise Software for Kubernetes, but support will be removed in a future release.\n\n<span title=\"X icon\">&#x274c;</span> **End of life** – Support for this distribution ended.\n\nAny distribution not listed in the table is not supported for production workloads.\n\n|  |  |  |  |  |  |  |  |\n|---|---|---|---|---|---|---|---|\n| Kubernetes version | **1.28** | **1.29** | **1.30** | **1.31** | **1.32** | **1.33** | **1.34** |\n| **Community K8s** |  |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Amazon EKS** |  |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Azure AKS** |  |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Google GKE** |  |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Rancher RKE2** | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |\n| **VMware TKG 2.5** |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"X icon\">&#x274c;</span> |  |  |  |  |\n| **VMware VKS** |  |  |  |  | <span title=\"Supported\">&#x2705;</span> |  |  |\n|  |  |  |  |  |  |  |  |\n| **OpenShift** | **4.15** | **4.16** | **4.17** | **4.18** | **4.19** |  |  |\n| --- | --- | --- | --- | --- | --- | --- | --- |\n|  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |\n|  |  |  |  |  |  |  |  |\n| **VMware TKGI** | **1.19** | **1.20** | **1.21** | **1.22** |  |  |  |\n| --- | --- | --- | --- | --- | --- | --- | --- |\n|  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |  |"},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:8.0.2-41`\n- **Operator**: `redislabs/operator:8.0.2-6`\n- **Services Rigger**: `redislabs/k8s-controller:8.0.2-6`\n- **Call Home Client**: `redislabs/re-call-home-client:8.0.2-6`"},{"id":"openshift-downloads","title":"OpenShift downloads","role":"content","text":"- **OLM operator bundle**: `8.0.2-6.1`\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:8.0.2-41`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:8.0.2-6`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:8.0.2-6`\n- **Call Home Client**: `registry.connect.redhat.com/redislabs/call-home-client:8.0.2-6`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"- **Only upgrades from 7.4.2-2 and later are supported.** If you are using an earlier version, install 7.4.2-2 before upgrading to 8.0.2-6.\n\n- **You must keep existing moduleList version numbers when upgrading redisVersion.** When upgrading a database's `redisVersion`, you must keep the existing module version numbers in `moduleList` unchanged. If you modify the module versions in `moduleList` during a `redisVersion` upgrade, the upgrade request will fail. The database will automatically use the bundled module versions for the new Redis version, regardless of what versions are specified in `moduleList`. This applies to both REDB and REAADB databases.\n\n- **Missing endpoint for admission endpoint (rare) (RED-119469)** Restart the operator pod.\n\n- **The REDB \"redisVersion\" field can't be used for memcached databases (RED-119152)**\n\n- **Database suffix changes during service-rigger termination cause resource recreation loops (RED-107687)** If you modify the database suffix for an Active-Active database while the service-rigger is in a terminating state, the service-rigger will continuously delete and recreate ingress or route resources. Wait until the services rigger pod has finished terminating before making suffix changes.\n\n- **REAADB changes might fail with \"gateway timeout\" errors, mostly on OpenShift (RED-103048)** Retry the operation.\n\n- **Creating two databases with the same name directly in the Redis Enterprise cluster manager UI will cause the service to be deleted and the database will not be available (RED-99997)** Avoid duplicating database names. The admission controller prevents duplicate database names when databases are created via the Kubernetes operator.\n\n- **Installing the operator bundle produces warning: `Warning: would violate PodSecurity \"restricted: v1.24\"` (RED-97381)** Ignore the warning. This issue is documented as benign on official Red Hat documentation.\n\n- **RERC resources must have a unique name (RED-96302)** The string \"rec-name\"/\"rec-namespace\" must be different from all other participating clusters in the Active-Active database.\n\n- **Admission is not blocking REAADB with `shardCount` which exceeds license quota (RED-96301)** Fix the problems with the REAADB and reapply.\n\n- **Active-Active controller only supports global database options. Configuration specific to location is not supported (RED-86490)**\n\n- **Active-Active setup removal might keep services or routes undeleted (RED-77752)** Delete services or routes manually if you encounter this problem.\n\n- **`autoUpgrade` set to `true` can cause unexpected bdb upgrades when `redisUpgradePolicy` is set to `true` (RED-72351)** Contact support if your deployment is impacted.\n\n- **Following the previous quick start guide version causes issues with creating an REDB due to unrecognized memory field name (RED-69515)** The workaround is to use the newer (current) revision of Deploy Redis Enterprise Software for Kubernetes.\n\n- **PVC size issues when using decimal value in spec (RED-62132)** Make sure you use integer values for the PVC size.\n\n- **REC might report error states on initial startup (RED-61707)** There is no workaround at this time except to ignore the errors.\n\n- **Hashicorp Vault integration - no support for Gesher (RED-55080)** There is no workaround for this issue. Gesher support has been deprecated.\n\n- **REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED-47254)** When REC clusters are deployed on Kubernetes clusters without synchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes.\n\n- **Deleting an OpenShift project with an REC deployed may hang (RED-47192)** When an REC cluster is deployed in a project (namespace) and has REDB resources, the REDB resources must be deleted first before the REC can be deleted. Therefore, until the REDB resources are deleted, the project deletion will hang. The fix is to delete the REDB resources first and the REC second. Then, you can delete the project.\n\n- **Clusters must be named 'rec' in OLM-based deployments (RED-39825)** In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) are bound to a specific service account name (namely, \"rec\"). The workaround is to name the cluster \"rec\".\n\n- **Readiness probe incorrect on failures (RED-39300)** STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure.\n\n- **Internal DNS and Kubernetes DNS may have conflicts (RED-37462)** DNS conflicts are possible between the cluster `mdns_server` and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names.\n\n- **K8s-based 5.4.10 clusters seem to negatively affect existing 5.4.6 clusters (RED-37233)** Upgrade clusters to latest version.\n\n- **Node CPU usage is reported instead of pod CPU usage (RED-36884)** In Kubernetes, the reported node CPU usage is the usage of the Kubernetes worker node hosting the REC pod.\n\n- **An unreachable cluster has status running (RED-32805)** When a cluster is in an unreachable state, the state remains `running` instead of triggering an error.\n\n- **Long cluster names cause routes to be rejected  (RED-25871)** A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name exceeds 63 characters. The workaround is to limit the cluster name to 20 characters or fewer.\n\n- **Cluster CR (REC) errors are not reported after invalid updates (RED-25542)** A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/8-0-2-releases","title":"Redis Enterprise for Kubernetes 8.0.2 release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-2-releases/","summary":"Releases with support for Redis Enterprise Software 8.0.2","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","children":[{"id":"operate/kubernetes/release-notes/8-0-2-releases/8-0-2-2-october2025","summary":"Feature release including support for Redis Software 8.0.2, enhancements, and bug fixes.","title":"Redis Enterprise for Kubernetes 8.0.2-2 (October 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-2-releases/8-0-2-2-october2025/"},{"id":"operate/kubernetes/release-notes/8-0-2-releases/8-0-2-6-december2025","summary":"Maintenance release including support for Redis Software 8.0.2-41, enhancements, and bug fixes.","title":"Redis Enterprise for Kubernetes 8.0.2-6 (December 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-2-releases/8-0-2-6-december2025/"}],"page_type":"content","content_hash":"34b0bf2e463a8912e6f39654d681d4669e3e76bc455557ca4617d452fbfa2cc3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise for Kubernetes 8.0.2 includes bug fixes, enhancements, and support for Redis Enterprise Software 8.0.2. The latest release is 8.0.2-2 with support for Redis Enterprise Software version 8.0.2."},{"id":"detailed-release-notes","title":"Detailed release notes","role":"content","text":"| Version (Release date) | Major changes |\n| --- | --- |\n| [8.0.2-2 (October 2025)](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-2-releases/8-0-2-2-october2025/) | Feature release including support for Redis Software 8.0.2, enhancements, and bug fixes. |\n| [8.0.2-6 (December 2025)](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-2-releases/8-0-2-6-december2025/) | Maintenance release including support for Redis Software 8.0.2-41, enhancements, and bug fixes. |"},{"id":"rhel9-based-image","title":"RHEL9-based image","role":"content","text":"As of version 7.8.2-6, Redis Enterprise images are based on Red Hat Enterprise Linux 9 (RHEL9). This means upgrades require:\n\n- [Cluster version of 7.4.2-2 or later](https://redis.io/docs/latest/operate/kubernetes/7.4.6/upgrade/).\n- Database version 7.2 or later.\n- RHEL9 compatible binaries for any modules you need.\n\nFor detailed steps, see the relevant upgrade page:\n\n- [OpenShift CLI](https://redis.io/docs/latest/operate/kubernetes/upgrade/openshift-cli)\n- [OpenShift OperatorHub](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-olm)\n- [Kubernetes](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster)"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"- **Expired license causes pod readiness failures, blocking recovery and upgrades** <!--RED-185977--> If a pod is stuck during upgrade, manually update the licenses via the REST API.\n\n- **Only upgrades from 7.4.2-2 and later are supported.** If you are using an earlier version, install 7.4.2-2 before upgrading to 8.0.2-2.\n\n- **Custom certificate upload is not supported for internode encryption (RED-173229).** Internode communication between cluster nodes continues to use default self-signed certificates and cannot be configured with customer-provided certificates.\n\n- **Missing endpoint for admission endpoint (rare) (RED-119469)** Restart the operator pod.\n\n- **The REDB \"redisVersion\" field can't be used for memcached databases(RED-119152)**\n\n- **When modifying the database suffix for an Active-Active database, while the service-rigger is in a terminating state, the services-rigger will delete and create the ingress or route resources in a loop (RED-107687)** Wait until the services rigger pod has finished to terminate it.\n\n- **REAADB changes might fail with \"gateway timeout\" errors, mostly on OpenShift (RED-103048)** Retry the operation.\n\n- **Creating two databases with the same name directly in the Redis Enterprise cluster manager UI will cause the service to be deleted and the database will not be available (RED-99997)** Avoid duplicating database names. The admission controller prevents duplicate database names when databases are created via the Kubernetes operator.\n\n- **Installing the operator bundle produces warning: `Warning: would violate PodSecurity \"restricted: v1.24\"` (RED-97381)** Ignore the warning. This issue is documented as benign on official Red Hat documentation.\n\n- **RERC resources must have a unique name (RED-96302)** The string \"rec-name\"/\"rec-namespace\" must be different from all other participating clusters in the Active-Active database.\n\n- **Admission is not blocking REAADB with `shardCount` which exceeds license quota (RED-96301)** Fix the problems with the REAADB and reapply.\n\n- **Active-Active controller only supports global database options. Configuration specific to location is not supported (RED-86490)**\n\n- **Active-Active setup removal might keep services or routes undeleted (RED-77752)** Delete services or routes manually if you encounter this problem.\n\n- **`autoUpgrade` set to `true` can cause unexpected bdb upgrades when `redisUpgradePolicy` is set to `true` (RED-72351)** Contact support if your deployment is impacted.\n\n- **Following the previous quick start guide version causes issues with creating an REDB due to unrecognized memory field name (RED-69515)** The workaround is to use the newer (current) revision of Deploy Redis Enterprise Software for Kubernetes.\n\n- **PVC size issues when using decimal value in spec (RED-62132)** Make sure you use integer values for the PVC size.\n\n- **REC might report error states on initial startup (RED-61707)** There is no workaround at this time except to ignore the errors.\n\n- **Hashicorp Vault integration - no support for Gesher (RED-55080)** There is no workaround for this issue. Gesher support has been deprecated.\n\n- **REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED-47254)** When REC clusters are deployed on Kubernetes clusters without synchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes.\n\n- **Deleting an OpenShift project with an REC deployed may hang (RED-47192)** When an REC cluster is deployed in a project (namespace) and has REDB resources, the REDB resources must be deleted first before the REC can be deleted. Therefore, until the REDB resources are deleted, the project deletion will hang. The fix is to delete the REDB resources first and the REC second. Then, you can delete the project.\n\n- **Clusters must be named 'rec' in OLM-based deployments (RED-39825)** In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) are bound to a specific service account name (namely, \"rec\"). The workaround is to name the cluster \"rec\".\n\n- **Readiness probe incorrect on failures (RED-39300)** STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure.\n\n- **Internal DNS and Kubernetes DNS may have conflicts (RED-37462)** DNS conflicts are possible between the cluster `mdns_server` and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names.\n\n- **K8s-based 5.4.10 clusters seem to negatively affect existing 5.4.6 clusters (RED-37233)** Upgrade clusters to latest version.\n\n- **Node CPU usage is reported instead of pod CPU usage (RED-36884)** In Kubernetes, the reported node CPU usage is the usage of the Kubernetes worker node hosting the REC pod.\n\n- **An unreachable cluster has status running (RED-32805)** When a cluster is in an unreachable state, the state remains `running` instead of triggering an error.\n\n- **Long cluster names cause routes to be rejected  (RED-25871)** A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name exceeds 63 characters. The workaround is to limit the cluster name to 20 characters or fewer.\n\n- **Cluster CR (REC) errors are not reported after invalid updates (RED-25542)** A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/8-0-20-releases/8-0-20-21-may2026","title":"Redis Enterprise for Kubernetes 8.0.20-21 (May 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-20-releases/8-0-20-21-may2026/","summary":"This is a maintenance release to support Redis Enterprise Software version 8.0.20-19.","tags":["docs","operate","kubernetes"],"last_updated":"2026-10-09T10:01:18-05:00","page_type":"content","content_hash":"bc303b8082a41053a159a5785a7ab659bf1e5efb81bb99f914a7d61ae1ff082f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"**This release has been recalled.** Don't install Redis Software for Kubernetes version 8.0.20-21 or upgrade to it. Use [Redis Software for Kubernetes version 8.0.20-25](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-20-releases/8-0-20-25-july2026) or later instead. Version 8.0.20-25 includes [Redis Software version 8.0.20-68](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-20-68), which fixes an internal certificate issue that can cause upgrades to fail (RS199373)."},{"id":"highlights","title":"Highlights","role":"content","text":"This is a maintenance release to support Redis Enterprise Software version 8.0.20-19. For version changes, supported distributions, and known limitations, see the [release notes for 8.0.18-11 (April 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-18-releases/8-0-18-11-april2026)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:8.0.20-19.21` (`redislabs/redis:8.0.20-19.21.minimal` for minimal base image)\n- **Operator**: `redislabs/operator:8.0.20-21`\n- **Services Rigger**: `redislabs/k8s-controller:8.0.20-21`\n- **Call Home Client**: `redislabs/re-call-home-client:8.0.20-21`"},{"id":"openshift-downloads","title":"OpenShift downloads","role":"content","text":"- **OLM operator bundle**: `8.0.20-21.0`\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:8.0.20-19.21` (`registry.connect.redhat.com/redislabs/redis-enterprise:8.0.20-19.21.minimal` for minimal base image)\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:8.0.20-21`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:8.0.20-21`\n- **Call Home Client**: `registry.connect.redhat.com/redislabs/call-home-client:8.0.20-21`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [8.0.20 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-20-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/8-0-20-releases/8-0-20-23-june2026","title":"Redis Enterprise for Kubernetes 8.0.20-23 (June 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-20-releases/8-0-20-23-june2026/","summary":"This is a maintenance release to support Redis Enterprise Software version 8.0.20-44.","tags":["docs","operate","kubernetes"],"last_updated":"2026-10-09T10:01:18-05:00","page_type":"content","content_hash":"bef665bccf6d282c21d76e060be9ab353e20592f92b85fe73402934d8dec98a4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"**This release has been recalled.** Don't install Redis Software for Kubernetes version 8.0.20-23 or upgrade to it. Use [Redis Software for Kubernetes version 8.0.20-25](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-20-releases/8-0-20-25-july2026) or later instead. Version 8.0.20-25 includes [Redis Software version 8.0.20-68](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-20-68), which fixes an internal certificate issue that can cause upgrades to fail (RS199373)."},{"id":"highlights","title":"Highlights","role":"content","text":"This is a maintenance release to support Redis Enterprise Software version 8.0.20-44. For version changes, supported distributions, and known limitations, see the [release notes for 8.0.18-11 (April 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-18-releases/8-0-18-11-april2026)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:8.0.20-44.23` (`redislabs/redis:8.0.20-44.23.minimal` for minimal base image)\n- **Operator**: `redislabs/operator:8.0.20-23`\n- **Services Rigger**: `redislabs/k8s-controller:8.0.20-23`\n- **Call Home Client**: `redislabs/re-call-home-client:8.0.20-23`"},{"id":"openshift-downloads","title":"OpenShift downloads","role":"content","text":"- **OLM operator bundle**: `8.0.20-23.0`\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:8.0.20-44.23` (`registry.connect.redhat.com/redislabs/redis-enterprise:8.0.20-44.23.minimal` for minimal base image)\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:8.0.20-23`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:8.0.20-23`\n- **Call Home Client**: `registry.connect.redhat.com/redislabs/call-home-client:8.0.20-23`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [8.0.20 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-20-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/8-0-20-releases/8-0-20-25-july2026","title":"Redis Enterprise for Kubernetes 8.0.20-25 (July 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-20-releases/8-0-20-25-july2026/","summary":"This is a maintenance release to support Redis Enterprise Software version 8.0.20-68.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"f358d8bf091d7f47109f5d3c0e2416f4cd5942d6ac4834f0c064db1d2bc5816f","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support Redis Enterprise Software version 8.0.20-68. For version changes, supported distributions, and known limitations, see the [release notes for 8.0.18-11 (April 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-18-releases/8-0-18-11-april2026)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:8.0.20-68.25` (`redislabs/redis:8.0.20-68.25.minimal` for minimal base image)\n- **Operator**: `redislabs/operator:8.0.20-25`\n- **Services Rigger**: `redislabs/k8s-controller:8.0.20-25`\n- **Call Home Client**: `redislabs/re-call-home-client:8.0.20-25`"},{"id":"openshift-downloads","title":"OpenShift downloads","role":"content","text":"- **OLM operator bundle**: `8.0.20-25.0`\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:8.0.20-68.25` (`registry.connect.redhat.com/redislabs/redis-enterprise:8.0.20-68.25.minimal` for minimal base image)\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:8.0.20-25`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:8.0.20-25`\n- **Call Home Client**: `registry.connect.redhat.com/redislabs/call-home-client:8.0.20-25`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [8.0.20 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-20-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/8-0-20-releases/8-0-20-26-august2026","title":"Redis Software for Kubernetes 8.0.20-26 (August 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-20-releases/8-0-20-26-august2026/","summary":"This is a maintenance release to support Redis Software version 8.0.20-96.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"63668a70613dfde5c98b1aafb60b8bbe411b2dc41af200d354280f943c67d7bd","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support [Redis Software 8.0.20](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases) version 8.0.20-96. For version changes, supported distributions, and known limitations, see the [release notes for 8.0.18-11 (April 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-18-releases/8-0-18-11-april2026)."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- Fixed client authentication being turned off and client certificates being cleared when you patched a field other than a certificate field, such as `evictionPolicy`, in the `spec.globalConfigurations` of a REAADB. The operator now preserves the mutual TLS (mTLS) settings on the underlying Active-Active database. REDB resources were not affected <!--RED-212113-->\n\n- Fixed an Active-Active database whose REAADB spec omitted `externalReplicationPort` for a participating cluster. The operator now syncs the port from the existing Active-Active database <!--RED-205147-->\n\n- Fixed an endless \"could not set label\" loop caused by reserved keys in the persistent volume claim (PVC) extra labels annotation <!--RED-189152-->"},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:8.0.20-96.26` (`redislabs/redis:8.0.20-96.26.minimal` for minimal base image)\n- **Operator**: `redislabs/operator:8.0.20-26`\n- **Services Rigger**: `redislabs/k8s-controller:8.0.20-26`\n- **Call Home Client**: `redislabs/re-call-home-client:8.0.20-26`"},{"id":"openshift-downloads","title":"OpenShift downloads","role":"content","text":"- **OLM operator bundle**: `8.0.20-26.2`\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:8.0.20-96.26` (`registry.connect.redhat.com/redislabs/redis-enterprise:8.0.20-96.26.minimal` for minimal base image)\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:8.0.20-26`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:8.0.20-26`\n- **Call Home Client**: `registry.connect.redhat.com/redislabs/call-home-client:8.0.20-26`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"new-limitations","title":"New limitations","role":"content","text":"- **Upgrading directly from Redis for Kubernetes 7.22.2-43 to 8.0.20-26 is not supported in OLM-based deployments** due to an upgrade path limitation between these builds. Upgrade to 8.0.20-27 or later instead.\n\nSee [8.0.20 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-20-releases) for information on other known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/8-0-20-releases/8-0-20-27-september2026","title":"Redis Software for Kubernetes 8.0.20-27 (September 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-20-releases/8-0-20-27-september2026/","summary":"This is a maintenance release to support Redis Software version 8.0.20-108.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"2ea5b3d02f42899474e2e7d0bf3aeaa357489ae7cd2ce6bf208bdee2a84643da","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support [Redis Software 8.0.20](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases) version 8.0.20-108. It fixes a high-severity bug with security implications that affected Active-Active databases. For version changes, supported distributions, and known limitations, see the [release notes for 8.0.18-11 (April 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-18-releases/8-0-18-11-april2026)."},{"id":"security","title":"Security","role":"security","text":"A high-severity bug with security implications affected Active-Active databases managed by the Redis Software for Kubernetes operator. **Upgrade to operator version 8.0.20-27 or later on this release line to get the fix.**\n\nWhen the operator applied a configuration update to an Active-Active database, it rebuilt and sent the database's entire configuration, but only re-read a credential from its Kubernetes secret if it believed that specific secret had changed. Any update triggered by something else therefore sent the password as empty, and on operator versions before 8.2.0-12, also cleared the mutual TLS (mTLS) client certificates.\n\nEither of the following triggered such an update:\n\n- Any configuration change on the Active-Active database, such as eviction policy, memory size, or backup settings, made by a user or by automation tooling such as GitOps.\n- A modification of the backup secret or a client certificate secret on the cluster, even with no configuration change. The operator re-read the touched secret correctly but wiped the password, because its secret was not the one that changed.\n\nThe impact was two-sided:\n\n- **Availability**: applications connecting with the password failed on every new connection. Existing connections kept working, so the failure looked intermittent.\n- **Security**: the database simultaneously accepted connections with no credentials at all, and with the mTLS variant, accepted clients without certificates. Nothing alerted and the database reported healthy, so a database could run exposed without anyone noticing.\n\nThis affected all released operator versions since the feature was introduced in early 2023. The fix prevents the credential wipe and automatically repairs already-affected databases when you upgrade the operator. No additional steps are required.\n\nThe fix is available in operator versions 7.4.6-11, 7.8.6-20, 7.22.2-45, 8.0.20-27, and 8.2.0-15. <!--RED-212963--> <!--RED-213921-->"},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:8.0.20-108.27` (`redislabs/redis:8.0.20-108.27.minimal` for minimal base image)\n- **Operator**: `redislabs/operator:8.0.20-27`\n- **Services Rigger**: `redislabs/k8s-controller:8.0.20-27`\n- **Call Home Client**: `redislabs/re-call-home-client:8.0.20-27`"},{"id":"openshift-downloads","title":"OpenShift downloads","role":"content","text":"- **OLM operator bundle**: `8.0.20-27.1`\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:8.0.20-108.27` (`registry.connect.redhat.com/redislabs/redis-enterprise:8.0.20-108.27.minimal` for minimal base image)\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:8.0.20-27`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:8.0.20-27`\n- **Call Home Client**: `registry.connect.redhat.com/redislabs/call-home-client:8.0.20-27`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [8.0.20 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-20-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/8-0-20-releases/8-0-20-28-october2026","title":"Redis Software for Kubernetes 8.0.20-28 (October 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-20-releases/8-0-20-28-october2026/","summary":"This is a maintenance release to support Redis Software version 8.0.20-127.","tags":["docs","operate","kubernetes"],"last_updated":"2026-10-19T14:11:26-05:00","page_type":"content","content_hash":"2ee33964dfee3de83189f00652de0e8866137ab74699223d91b0fbde36c86262","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support [Redis Software 8.0.20](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases) version 8.0.20-127. For version changes, supported distributions, and known limitations, see the [release notes for 8.0.18-11 (April 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-18-releases/8-0-18-11-april2026)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:8.0.20-127.28` (`redislabs/redis:8.0.20-127.28.minimal` for minimal base image)\n- **Operator**: `redislabs/operator:8.0.20-28`\n- **Services Rigger**: `redislabs/k8s-controller:8.0.20-28`\n- **Call Home Client**: `redislabs/re-call-home-client:8.0.20-28`"},{"id":"openshift-downloads","title":"OpenShift downloads","role":"content","text":"- **OLM operator bundle**: `8.0.20-28.2`\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:8.0.20-127.28` (`registry.connect.redhat.com/redislabs/redis-enterprise:8.0.20-127.28.minimal` for minimal base image)\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:8.0.20-28`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:8.0.20-28`\n- **Call Home Client**: `registry.connect.redhat.com/redislabs/call-home-client:8.0.20-28`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [8.0.20 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-20-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/8-0-20-releases","title":"Redis Enterprise for Kubernetes 8.0.20 release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-20-releases/","summary":"Releases with support for Redis Enterprise Software 8.0.20","tags":["docs","operate","kubernetes"],"last_updated":"2026-10-19T14:11:26-05:00","children":[{"id":"operate/kubernetes/release-notes/8-0-20-releases/8-0-20-28-october2026","summary":"This is a maintenance release to support Redis Software version 8.0.20-127.","title":"Redis Software for Kubernetes 8.0.20-28 (October 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-20-releases/8-0-20-28-october2026/"},{"id":"operate/kubernetes/release-notes/8-0-20-releases/8-0-20-27-september2026","summary":"This is a maintenance release to support Redis Software version 8.0.20-108.","title":"Redis Software for Kubernetes 8.0.20-27 (September 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-20-releases/8-0-20-27-september2026/"},{"id":"operate/kubernetes/release-notes/8-0-20-releases/8-0-20-26-august2026","summary":"This is a maintenance release to support Redis Software version 8.0.20-96.","title":"Redis Software for Kubernetes 8.0.20-26 (August 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-20-releases/8-0-20-26-august2026/"},{"id":"operate/kubernetes/release-notes/8-0-20-releases/8-0-20-25-july2026","summary":"This is a maintenance release to support Redis Enterprise Software version 8.0.20-68.","title":"Redis Enterprise for Kubernetes 8.0.20-25 (July 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-20-releases/8-0-20-25-july2026/"},{"id":"operate/kubernetes/release-notes/8-0-20-releases/8-0-20-23-june2026","summary":"This is a maintenance release to support Redis Enterprise Software version 8.0.20-44.","title":"Redis Enterprise for Kubernetes 8.0.20-23 (June 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-20-releases/8-0-20-23-june2026/"},{"id":"operate/kubernetes/release-notes/8-0-20-releases/8-0-20-21-may2026","summary":"This is a maintenance release to support Redis Enterprise Software version 8.0.20-19.","title":"Redis Enterprise for Kubernetes 8.0.20-21 (May 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-20-releases/8-0-20-21-may2026/"}],"page_type":"content","content_hash":"df69dfaf0421129ab0904a0f737ca344d79f78bbd41ab2df415fcb32fb8ed905","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise for Kubernetes 8.0.20 is a maintenance release of [Redis Enterprise for Kubernetes 8.0.18](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-18-releases). The latest release is 8.0.20-28 with support for Redis Software version 8.0.20-127."},{"id":"detailed-release-notes","title":"Detailed release notes","role":"content","text":"| Version (Release date) | Major changes |\n| --- | --- |\n| [8.0.20-28 (October 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-20-releases/8-0-20-28-october2026/) | This is a maintenance release to support Redis Software version 8.0.20-127. |\n| [8.0.20-27 (September 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-20-releases/8-0-20-27-september2026/) | This is a maintenance release to support Redis Software version 8.0.20-108. |\n| [8.0.20-26 (August 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-20-releases/8-0-20-26-august2026/) | This is a maintenance release to support Redis Software version 8.0.20-96. |\n| [8.0.20-25 (July 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-20-releases/8-0-20-25-july2026/) | This is a maintenance release to support Redis Enterprise Software version 8.0.20-68. |\n| [8.0.20-23 (June 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-20-releases/8-0-20-23-june2026/) | This is a maintenance release to support Redis Enterprise Software version 8.0.20-44. |\n| [8.0.20-21 (May 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-20-releases/8-0-20-21-may2026/) | This is a maintenance release to support Redis Enterprise Software version 8.0.20-19. |"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See the [8.0.18-11 (April 2026) release notes](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-18-releases/8-0-18-11-april2026) for supported distributions and known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/8-0-6-releases/8-0-6-8-december2025","title":"Redis Enterprise for Kubernetes 8.0.6-8 (December 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-6-releases/8-0-6-8-december2025/","summary":"Feature release including support for Redis Software 8.0.6-54, enhancements, and bug fixes.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"77f18166262e03005af049e78d6fac2de44e246285059744f2e8e198a6d6ee4d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise for Kubernetes 8.0.6-8 is a feature release that supports Redis Software [8.0.6-54](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-6-54), and includes enhancements, supported distribution updates and bug fixes."},{"id":"highlights","title":"Highlights","role":"content","text":"- Support for external module repositories (custom modules)\n- Support for Redis Software [8.0.6-54](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-6-54)\n- SAML 2.0 single sign-on (SSO) authentication support for the cluster manager UI"},{"id":"enhancements","title":"Enhancements","role":"content","text":"API support has been added for the following features:\n\n- Database connection auditing<!--RED-166368-->\n- REAADB alerts<!--RED-170896-->\n- User-defined modules <!--RED-168227-->\n- Redis Software [8.0.6-54](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-6-54) <!--RED-172935-->\n- User-defined certificates for [internode encryption](https://redis.io/docs/latest/operate/kubernetes/security/certificates/internode-encryption) <!--RED-173229-->\n- SAML 2.0 single sign-on (SSO) authentication <!--RED-176765-->\n- Redis Flex\n\nSee [API changes](#api-changes) for details."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- Removed 'InsecureRequestWarning: Unverified HTTPS request is being made to host localhost' message that appeared when running the healthcheck script <!--RED-71858-->\n- Fixed bug that blocked image spec updates during active REC upgrades. <!--RED-84166-->\n- Fixed slow reaction time by services-rigger to node failures. <!--RED-174164-->\n- Fixed operator syncing REAADB in a loop when S3 backup is enabled <!--RED-173464-->\n- Security patches"},{"id":"api-changes","title":"API changes","role":"content","text":"| **CRD** | **Field** | **Change** | **Description** |\n|---|---|---|---|\n| REAADB | `spec.globalConfigurations.auditing` | Added | Enable database connection auditing. |\n| REAADB | `spec.globalConfigurations.alertSettings` | Added | Configure alert settings for the active-active database. |\n| REAADB | `spec.globalConfigurations.rofRamRatio` | Added | RAM allocation ratio for Redis Flex (v2) databases as a percentage of total data size. Valid range: 0-100. Default: 50% when omitted. Controls how much RAM is allocated per unit of data (for example, 30% means 3MB RAM per 10MB data). RAM grows proportionally with data until `rofRamSize` limit is reached (if specified). Only applicable when `isRof=true` and Redis version 8.0 or later (BigStore v2 - Redis Flex). |\n| REC | `spec.auditing` | Added | Cluster-level configuration for auditing database connection and authentication events. Includes both the audit listener connection parameters and the default policy for new databases. |\n| REC | `spec.sso` | Added | Cluster-level SSO configuration for authentication to the cluster manager UI. |\n| REC | `spec.userDefinedModules` | Added | List of user-defined modules to be downloaded and installed during cluster bootstrap. The modules on the list will be downloaded on cluster creation, upgrade, scale-out, and recovery and installed on all nodes. Changing this field for a running cluster will trigger a rolling update. |\n| REC | `spec.certificates.ssoServiceCertificateSecretName` | Added | Secret name for the cluster's SSO service certificate. Used for SAML-based SSO authentication to the cluster manager. The secret must contain `name`, `certificate`, and `key` fields (same format as other cluster certificates). If left blank, SSO will not be configured. |\n| REC | `spec.certificates.ssoIssuerCertificateSecretName` | Added | Secret name for the SSO Identity Provider (IdP) certificate. This is the public certificate from your SAML Identity Provider used to verify SAML assertions. The secret must contain `name` and `certificate` fields (no `key` field needed for IdP certificate). This is optional - if using IdP metadata XML, the IdP certificate is included in the metadata. |\n| REDB | `spec.auditing` | Added | Database-level auditing configuration. |\n| REDB | `spec.rofRamRatio` | Added | RAM allocation ratio for Redis Flex (v2) databases as a percentage of total data size. Valid range: 0-100. Default: 50% when omitted. Controls how much RAM is allocated per unit of data (for example, 30% means 3MB RAM per 10MB data). RAM grows proportionally with data until `rofRamSize` limit is reached (if specified). Only applicable when `isRof=true` and Redis version 8.0 or later (BigStore v2 - Redis Flex). |\n| REDB | `status.bigstoreVersion` | Added | BigStore version for Redis on Flash databases (1 for Auto Tiering, 2 for Redis Flex). Read-only field populated from Redis Software. |"},{"id":"supported-distributions","title":"Supported distributions","role":"compatibility","text":"The following table shows supported distributions at the time of this release. You can also find this list in [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions).\n\n<span title=\"Check mark icon\">&#x2705;</span> **Supported** – This distribution is supported for this version of Redis Enterprise Software for Kubernetes.\n\n<span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> **Deprecated** – This distribution is still supported for this version of Redis Enterprise Software for Kubernetes, but support will be removed in a future release.\n\n<span title=\"X icon\">&#x274c;</span> **End of life** – Support for this distribution ended.\n\nAny distribution not listed in the table is not supported for production workloads.\n\n| Kubernetes version | **1.29** | **1.30** | **1.31** | **1.32** | **1.33** | **1.34** |\n|---|---|---|---|---|---|---|\n| **Community K8s** |  |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Amazon EKS** |  |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Azure AKS** |  |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Google GKE** |  |  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Rancher RKE2** | <span title=\"X icon\">&#x274c;</span> | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **VMware TKG 2.5** |  |  |  |  |  |  |\n| **VMware VKS** |  |  |  | <span title=\"Supported\">&#x2705;</span> |  |  |\n|  |  |  |  |  |  |  |\n| **OpenShift** | **4.16** | **4.17** | **4.18** | **4.19** | **4.20** |  |\n| --- | --- | --- | --- | --- | --- | --- |\n|  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |\n|  |  |  |  |  |  |  |\n| **VMware TKGI** | **1.20** | **1.21** | **1.22** | **1.23** |  |  |\n| --- | --- | --- | --- | --- | --- | --- |\n|  | <span title=\"X icon\">&#x274c;</span> | <span title=\"Deprecation warning\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |  |  |"},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:8.0.6-54`\n- **Operator**: `redislabs/operator:8.0.6-8`\n- **Services Rigger**: `redislabs/k8s-controller:8.0.6-8`\n- **Call Home Client**: `redislabs/re-call-home-client:8.0.6-8`"},{"id":"openshift-downloads","title":"OpenShift downloads","role":"content","text":"- **OLM operator bundle**: `8.0.6-8.0`\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:8.0.6-54`\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:8.0.6-8`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:8.0.6-8`\n- **Call Home Client**: `registry.connect.redhat.com/redislabs/call-home-client:8.0.6-8`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"new-limitations","title":"New limitations","role":"content","text":"- **SSO configuration doesn't work with IPv6 or dual stack (IPv4/IPv6) clusters.** <!--RED-180550-->"},{"id":"existing-limitations","title":"Existing limitations","role":"content","text":"- **Only upgrades from 7.4.2-2 and later are supported.** If you are using an earlier version, install 7.4.2-2 before upgrading to 8.0.6-8.\n\n- **Missing endpoint for admission endpoint (rare) (RED-119469)** Restart the operator pod.\n\n- **The REDB \"redisVersion\" field can't be used for memcached databases (RED-119152)**\n\n- **When modifying the database suffix for an Active-Active database, while the service-rigger is in a terminating state, the services-rigger will delete and create the ingress or route resources in a loop (RED-107687)** Wait until the services rigger pod has finished to terminate it.\n\n- **REAADB changes might fail with \"gateway timeout\" errors, mostly on OpenShift (RED-103048)** Retry the operation.\n\n- **Creating two databases with the same name directly in the Redis Enterprise cluster manager UI will cause the service to be deleted and the database will not be available (RED-99997)** Avoid duplicating database names. The admission controller prevents duplicate database names when databases are created via the Kubernetes operator.\n\n- **Installing the operator bundle produces warning: `Warning: would violate PodSecurity \"restricted: v1.24\"` (RED-97381)** Ignore the warning. This issue is documented as benign on official Red Hat documentation.\n\n- **RERC resources must have a unique name (RED-96302)** The string \"rec-name\"/\"rec-namespace\" must be different from all other participating clusters in the Active-Active database.\n\n- **Admission is not blocking REAADB with `shardCount` which exceeds license quota (RED-96301)** Fix the problems with the REAADB and reapply.\n\n- **Active-Active controller only supports global database options. Configuration specific to location is not supported (RED-86490)**\n\n- **Active-Active setup removal might keep services or routes undeleted (RED-77752)** Delete services or routes manually if you encounter this problem.\n\n- **`autoUpgrade` set to `true` can cause unexpected bdb upgrades when `redisUpgradePolicy` is set to `true` (RED-72351)** Contact support if your deployment is impacted.\n\n- **Following the previous quick start guide version causes issues with creating an REDB due to unrecognized memory field name (RED-69515)** The workaround is to use the newer (current) revision of Deploy Redis Enterprise Software for Kubernetes.\n\n- **PVC size issues when using decimal value in spec (RED-62132)** Make sure you use integer values for the PVC size.\n\n- **REC might report error states on initial startup (RED-61707)** There is no workaround at this time except to ignore the errors.\n\n- **Hashicorp Vault integration - no support for Gesher (RED-55080)** There is no workaround for this issue. Gesher support has been deprecated.\n\n- **REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED-47254)** When REC clusters are deployed on Kubernetes clusters without synchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes.\n\n- **Deleting an OpenShift project with an REC deployed may hang (RED-47192)** When an REC cluster is deployed in a project (namespace) and has REDB resources, the REDB resources must be deleted first before the REC can be deleted. Therefore, until the REDB resources are deleted, the project deletion will hang. The fix is to delete the REDB resources first and the REC second. Then, you can delete the project.\n\n- **Clusters must be named 'rec' in OLM-based deployments (RED-39825)** In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) are bound to a specific service account name (namely, \"rec\"). The workaround is to name the cluster \"rec\".\n\n- **Readiness probe incorrect on failures (RED-39300)** STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure.\n\n- **Internal DNS and Kubernetes DNS may have conflicts (RED-37462)** DNS conflicts are possible between the cluster `mdns_server` and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names.\n\n- **K8s-based 5.4.10 clusters seem to negatively affect existing 5.4.6 clusters (RED-37233)** Upgrade clusters to latest version.\n\n- **Node CPU usage is reported instead of pod CPU usage (RED-36884)** In Kubernetes, the reported node CPU usage is the usage of the Kubernetes worker node hosting the REC pod.\n\n- **An unreachable cluster has status running (RED-32805)** When a cluster is in an unreachable state, the state remains `running` instead of triggering an error.\n\n- **Long cluster names cause routes to be rejected  (RED-25871)** A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name exceeds 63 characters. The workaround is to limit the cluster name to 20 characters or fewer.\n\n- **Cluster CR (REC) errors are not reported after invalid updates (RED-25542)** A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/8-0-6-releases","title":"Redis Enterprise for Kubernetes 8.0.6 release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-6-releases/","summary":"Releases with support for Redis Enterprise Software 8.0.6","tags":["docs","operate","kubernetes"],"last_updated":"2026-05-07T16:21:12-05:00","children":[{"id":"operate/kubernetes/release-notes/8-0-6-releases/8-0-6-8-december2025","summary":"Feature release including support for Redis Software 8.0.6-54, enhancements, and bug fixes.","title":"Redis Enterprise for Kubernetes 8.0.6-8 (December 2025) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-6-releases/8-0-6-8-december2025/"}],"page_type":"content","content_hash":"fbaa9b13565daee59bc716ccf802426bef7d528a64dc35c6312800de17686d31","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise for Kubernetes 8.0.6 includes bug fixes, enhancements, and support for Redis Enterprise Software 8.0.6. The latest release is 8.0.6-8 with support for Redis Enterprise Software version 8.0.6-54."},{"id":"detailed-release-notes","title":"Detailed release notes","role":"content","text":"| Version (Release date) | Major changes |\n| --- | --- |\n| [8.0.6-8 (December 2025)](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-6-releases/8-0-6-8-december2025/) | Feature release including support for Redis Software 8.0.6-54, enhancements, and bug fixes. |"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"- **Expired license causes pod readiness failures, blocking recovery and upgrades** <!--RED-185977--> If a pod is stuck during upgrade, manually update the licenses via the REST API.\n\n- **SSO configuration doesn't work with IPv6 or dual stack (IPv4/IPv6) clusters.** <!--RED-180550-->\n\n- **Only upgrades from 7.4.2-2 and later are supported.** If you are using an earlier version, install 7.4.2-2 before upgrading to 8.0.6-8.\n\n- **Missing endpoint for admission endpoint (rare) (RED-119469)** Restart the operator pod.\n\n- **The REDB \"redisVersion\" field can't be used for memcached databases(RED-119152)**\n\n- **When modifying the database suffix for an Active-Active database, while the service-rigger is in a terminating state, the services-rigger will delete and create the ingress or route resources in a loop (RED-107687)** Wait until the services rigger pod has finished to terminate it.\n\n- **REAADB changes might fail with \"gateway timeout\" errors, mostly on OpenShift (RED-103048)** Retry the operation.\n\n- **Creating two databases with the same name directly in the Redis Enterprise cluster manager UI will cause the service to be deleted and the database will not be available (RED-99997)** Avoid duplicating database names. The admission controller prevents duplicate database names when databases are created via the Kubernetes operator.\n\n- **Installing the operator bundle produces warning: `Warning: would violate PodSecurity \"restricted: v1.24\"` (RED-97381)** Ignore the warning. This issue is documented as benign on official Red Hat documentation.\n\n- **RERC resources must have a unique name (RED-96302)** The string \"rec-name\"/\"rec-namespace\" must be different from all other participating clusters in the Active-Active database.\n\n- **Admission is not blocking REAADB with `shardCount` which exceeds license quota (RED-96301)** Fix the problems with the REAADB and reapply.\n\n- **Active-Active controller only supports global database options. Configuration specific to location is not supported (RED-86490)**\n\n- **Active-Active setup removal might keep services or routes undeleted (RED-77752)** Delete services or routes manually if you encounter this problem.\n\n- **`autoUpgrade` set to `true` can cause unexpected bdb upgrades when `redisUpgradePolicy` is set to `true` (RED-72351)** Contact support if your deployment is impacted.\n\n- **Following the previous quick start guide version causes issues with creating an REDB due to unrecognized memory field name (RED-69515)** The workaround is to use the newer (current) revision of Deploy Redis Enterprise Software for Kubernetes.\n\n- **PVC size issues when using decimal value in spec (RED-62132)** Make sure you use integer values for the PVC size.\n\n- **REC might report error states on initial startup (RED-61707)** There is no workaround at this time except to ignore the errors.\n\n- **Hashicorp Vault integration - no support for Gesher (RED-55080)** There is no workaround for this issue. Gesher support has been deprecated.\n\n- **REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED-47254)** When REC clusters are deployed on Kubernetes clusters without synchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes.\n\n- **Deleting an OpenShift project with an REC deployed may hang (RED-47192)** When an REC cluster is deployed in a project (namespace) and has REDB resources, the REDB resources must be deleted first before the REC can be deleted. Therefore, until the REDB resources are deleted, the project deletion will hang. The fix is to delete the REDB resources first and the REC second. Then, you can delete the project.\n\n- **Clusters must be named 'rec' in OLM-based deployments (RED-39825)** In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) are bound to a specific service account name (namely, \"rec\"). The workaround is to name the cluster \"rec\".\n\n- **Readiness probe incorrect on failures (RED-39300)** STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure.\n\n- **Internal DNS and Kubernetes DNS may have conflicts (RED-37462)** DNS conflicts are possible between the cluster `mdns_server` and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names.\n\n- **K8s-based 5.4.10 clusters seem to negatively affect existing 5.4.6 clusters (RED-37233)** Upgrade clusters to latest version.\n\n- **Node CPU usage is reported instead of pod CPU usage (RED-36884)** In Kubernetes, the reported node CPU usage is the usage of the Kubernetes worker node hosting the REC pod.\n\n- **An unreachable cluster has status running (RED-32805)** When a cluster is in an unreachable state, the state remains `running` instead of triggering an error.\n\n- **Long cluster names cause routes to be rejected  (RED-25871)** A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name exceeds 63 characters. The workaround is to limit the cluster name to 20 characters or fewer.\n\n- **Cluster CR (REC) errors are not reported after invalid updates (RED-25542)** A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/8-2-0-releases/8-2-0-12-july2026","title":"Redis Software for Kubernetes 8.2.0-12 (July 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-2-0-releases/8-2-0-12-july2026/","summary":"Feature release with Redis Enterprise RBAC custom resource definitions, REDB metric enrichment through tags, and Redis Software 8.2.0.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"a1c9b1ce765601a7c9e8b4443fba472479c78cabc8801c2dd33d839eda2ba0b3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software for Kubernetes 8.2.0-12 is a feature release that supports [Redis Software 8.2.0](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-2-releases) and includes new features, bug fixes, and enhancements."},{"id":"highlights","title":"Highlights","role":"content","text":"- Support for managing Redis Enterprise role-based access control (RBAC) through new custom resource definitions (CRDs).\n- REDB metric enrichment through tags.\n- Support for Redis Software 8.2.0."},{"id":"enhancements","title":"Enhancements","role":"content","text":"- Added support for Redis Enterprise RBAC through new custom resource definitions <!--RED-147512-->\n- Added REDB metric enrichment through tags <!--RED-160631-->\n- Migrated the operator and services rigger images to UBI9-micro <!--RED-160631-->\n- Added REC support for `dmc_external_cba_authentication` <!--RED-195771-->\n- Added a new Vault plugin with packaged version updates, released separately <!--RED-193207-->\n- On OpenShift, images are now pulled from Red Hat repositories by default <!--RED-193056-->\n- Added data-path command auditing through REDB <!--RED-191516-->\n- Added FIPS 140-3 compliance support <!--RED-192889-->\n- Added Kubernetes 1.36 and OpenShift 4.22 support <!--RED-200904--><!--RED-201027-->\n- Added Redis Software 8.2.0 support <!--RED-200513-->"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- Fixed cases that caused Redis Enterprise \"ghost\" containers <!--RED-185335-->\n- Fixed excessive updates to the Kubernetes API server that caused high logging volume <!--RED-195398-->\n- Removed nonessential users from container images <!--RED-199258-->\n- Security fixes <!--RED-201725-->\n- Fixed an REDB reapply error after recovery <!--RED-200719-->\n- Fixed a memory usage increase in the bootstrapper container <!--RED-200754-->\n- Fixed client authentication being turned off and client certificates being cleared when you patched a field other than a certificate field, such as `evictionPolicy`, in the `spec.globalConfigurations` of a REAADB. The operator now preserves the mutual TLS (mTLS) settings on the underlying Active-Active database. REDB resources were not affected <!--RED-192548-->"},{"id":"api-changes","title":"API changes","role":"content","text":"This release adds the RBAC custom resource definitions and new fields for access control, metrics, auditing, and FIPS compliance. The table below summarizes the notable changes. For the complete field-level reference, see the [API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api).\n\n| **CRD** | **Field** | **Change** | **Description** |\n|---|---|---|---|\n| RedisEnterpriseUser (REUSER) | — | Added | New CRD that defines a user for cluster access control. |\n| RedisEnterpriseRole (REROLE) | — | Added | New CRD that defines a role scoped to selected databases. |\n| RedisEnterpriseRoleBinding (REROLEBINDING) | — | Added | New CRD that binds users to a `RedisEnterpriseRole`. |\n| RedisEnterpriseClusterRole (RECROLE) | — | Added | New CRD that defines a cluster-scoped role. |\n| RedisEnterpriseClusterRoleBinding (RECROLEBINDING) | — | Added | New CRD that binds users to a `RedisEnterpriseClusterRole`. |\n| RedisEnterpriseACL (REACL) | — | Added | New CRD that defines an access control list (ACL) for a database. |\n| REC | `spec.accessControl` | Added | Configures cluster access control, including whether the deprecated REDB `rolesPermissions` field is reconciled. |\n| REC | `spec.dmcExternalCBAAuthentication` | Added | Delegates client certificate-based authentication to an external authentication service. |\n| REC | `spec.metricsConfig` | Added | Configures cluster metrics, including database tag keys exposed as metric labels and key size and items distribution histograms. |\n| REC | `spec.securityContext.fips` | Added | Enables FIPS 140-3 compliance mode. |\n| REDB | `spec.auditing` | Added | Configures data-path command auditing, including audit mode and source IP and username filters. |\n| REDB | `spec.tags` | Added | Map of tags used to enrich exported database metrics. |\n| REDB | `spec.rolesPermissions` | Modified | Deprecated in favor of the RBAC CRDs; still reconciled for backward compatibility. |\n| REAADB | `spec.globalConfigurations.tags` | Added | Map of tags used to enrich exported metrics for the Active-Active database. |"},{"id":"supported-distributions","title":"Supported distributions","role":"compatibility","text":"Redis Software for Kubernetes is compatible with [CNCF-conformant](https://www.cncf.io/training/certification/software-conformance/) Kubernetes platforms. The operator follows standard Kubernetes APIs and practices and is designed to run consistently across certified Kubernetes environments.\n\nThe following table shows supported Kubernetes versions at the time of this release. For a list of platforms tested with this release, see [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions).\n\n| Kubernetes | **Redis <nobr>8.2.0-12</nobr>** |\n|---|---|\n| 1.36 | Supported |\n| 1.35 | Supported |\n| 1.34 | Supported |\n| 1.33 | Supported |\n| 1.32 | Deprecated |"},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:8.2.0-25.12` (`redislabs/redis:8.2.0-25.12.minimal` for minimal base image)\n- **Operator**: `redislabs/operator:8.2.0-12`\n- **Services Rigger**: `redislabs/k8s-controller:8.2.0-12`\n- **Call Home Client**: `redislabs/re-call-home-client:8.2.0-12`"},{"id":"openshift-downloads","title":"OpenShift downloads","role":"content","text":"- **OLM operator bundle**: `8.2.0-12.0`\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:8.2.0-25.12` (`registry.connect.redhat.com/redislabs/redis-enterprise:8.2.0-25.12.minimal` for minimal base image)\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:8.2.0-12`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:8.2.0-12`\n- **Call Home Client**: `registry.connect.redhat.com/redislabs/call-home-client:8.2.0-12`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"- **SSO configuration does not work with IPv6 or dual-stack (IPv4/IPv6) clusters.** <!--RED-180550-->\n\n- **Upgrades from versions earlier than 7.4.2-2 are not supported.** If you use an earlier version, upgrade to 7.4.2-2 before upgrading to this version.\n\n- **Missing endpoint for admission endpoint (rare).** Restart the operator pod. <!--RED-119469-->\n\n- **The REDB `redisVersion` field cannot be used for memcached databases.** <!--RED-119152-->\n\n- **Modifying the database suffix for an Active-Active database while the services rigger is terminating causes a loop.** The services rigger deletes and recreates ingress or route resources repeatedly. Wait for the services rigger pod to finish terminating before modifying the suffix. <!--RED-107687-->\n\n- **REAADB changes might fail with \"gateway timeout\" errors, especially on OpenShift.** Retry the operation. <!--RED-103048-->\n\n- **Creating two databases with the same name in the Redis Enterprise Cluster Manager UI deletes the service and makes the database unavailable.** Avoid duplicate database names. The admission controller prevents duplicate names when you create databases through the Kubernetes operator. <!--RED-99997-->\n\n- **Installing the operator bundle produces the warning `Warning: would violate PodSecurity \"restricted: v1.24\"`.** Ignore this warning. Red Hat documentation identifies this issue as benign. <!--RED-97381-->\n\n- **RERC resources must have a unique name.** The `rec-name/rec-namespace` string must differ from all other participating clusters in the Active-Active database. <!--RED-96302-->\n\n- **Admission does not block REAADB resources with a `shardCount` that exceeds the license quota.** Correct the REAADB configuration and reapply. <!--RED-96301-->\n\n- **The Active-Active controller supports only global database options.** Location-specific configuration is not supported. <!--RED-86490-->\n\n- **Removing an Active-Active setup might leave services or routes undeleted.** Delete the services or routes manually. <!--RED-77752-->\n\n- **Setting `autoUpgrade` to `true` can cause unexpected database upgrades when `redisUpgradePolicy` is also `true`.** Contact support if your deployment is affected. <!--RED-72351-->\n\n- **Using the previous quick start guide causes REDB creation issues due to an unrecognized memory field name.** Use the current version of the Deploy Redis Enterprise Software for Kubernetes guide. <!--RED-69515-->\n\n- **PVC size does not work with decimal values.** Use integer values for the PVC size. <!--RED-62132-->\n\n- **REC might report error states on initial startup.** No workaround exists. Ignore the errors. <!--RED-61707-->\n\n- **HashiCorp Vault integration does not support Gesher.** No workaround exists. Gesher support is deprecated. <!--RED-55080-->\n\n- **REC clusters fail to start on Kubernetes clusters with unsynchronized clocks.** Use NTP to synchronize the underlying Kubernetes nodes. <!--RED-47254-->\n\n- **Deleting an OpenShift project with a deployed REC might hang.** When an REC cluster is deployed in a project (namespace) with REDB resources, you must delete the REDB resources before deleting the REC. The project deletion hangs until you delete the REDB resources. Delete the REDB resources first, then delete the REC, and then delete the project. <!--RED-47192-->\n\n- **Clusters must be named \"rec\" in OLM-based deployments.** When you deploy the operator through OLM, the security context constraints (SCC) are bound to a specific service account name (\"rec\"). The deployment fails if the cluster has a different name. <!--RED-39825-->\n\n- **Readiness probe does not correctly report failures.** The StatefulSet readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure. <!--RED-39300-->\n\n- **Internal DNS and Kubernetes DNS might conflict.** DNS conflicts can occur between the cluster `mdns_server` and Kubernetes DNS. This affects only DNS resolution from within cluster nodes for Kubernetes DNS names. <!--RED-37462-->\n\n- **Kubernetes-based 5.4.10 clusters might negatively affect existing 5.4.6 clusters.** Upgrade your clusters to the latest version. <!--RED-37233-->\n\n- **Node CPU usage is reported instead of pod CPU usage.** The reported CPU usage is for the Kubernetes worker node hosting the REC pod, not the pod itself. <!--RED-36884-->\n\n- **An unreachable cluster shows status as running.** When a cluster is unreachable, the status remains `running` instead of showing an error. <!--RED-32805-->\n\n- **Long cluster names cause routes to be rejected.** A cluster name longer than 20 characters causes route rejection because the host part of the domain name exceeds 63 characters. Limit the cluster name to 20 characters or fewer. <!--RED-25871-->\n\n- **Cluster CR (REC) errors are not reported after invalid updates.** A cluster CR specification error is not reported if you update two or more invalid CR resources in sequence. <!--RED-25542-->"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/8-2-0-releases/8-2-0-13-august2026","title":"Redis Software for Kubernetes 8.2.0-13 (August 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-2-0-releases/8-2-0-13-august2026/","summary":"This is a maintenance release to support Redis Software version 8.2.0-46.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"8b810f94091a75345b77de9b8a2803c3ce3968f8ea216fe093d406dafab0113b","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support [Redis Software 8.2.0](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-2-releases) version 8.2.0-46. For version changes, supported distributions, and known limitations, see the [release notes for 8.2.0-12 (July 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-2-0-releases/8-2-0-12-july2026)."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- Fixed a failure caused by setting AWS load balancer annotations on `ClusterIP` services. These annotations now apply only to pod exposure services, and annotation changes re-create the Cluster Manager UI, API, and Prometheus services consistently <!--RED-209555-->\n\n- Fixed an Active-Active database whose REAADB spec omitted `externalReplicationPort` for a participating cluster. The operator now syncs the port from the existing Active-Active database <!--RED-205147-->\n\n- Fixed an endless \"could not set label\" loop caused by reserved keys in the persistent volume claim (PVC) extra labels annotation <!--RED-189152-->"},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:8.2.0-46.13` (`redislabs/redis:8.2.0-46.13.minimal` for minimal base image)\n- **Operator**: `redislabs/operator:8.2.0-13`\n- **Services Rigger**: `redislabs/k8s-controller:8.2.0-13`\n- **Call Home Client**: `redislabs/re-call-home-client:8.2.0-13`"},{"id":"openshift-downloads","title":"OpenShift downloads","role":"content","text":"- **OLM operator bundle**: `8.2.0-13.0`\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:8.2.0-46.13` (`registry.connect.redhat.com/redislabs/redis-enterprise:8.2.0-46.13.minimal` for minimal base image)\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:8.2.0-13`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:8.2.0-13`\n- **Call Home Client**: `registry.connect.redhat.com/redislabs/call-home-client:8.2.0-13`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [8.2.0 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-2-0-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/8-2-0-releases/8-2-0-15-september2026","title":"Redis Software for Kubernetes 8.2.0-15 (September 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-2-0-releases/8-2-0-15-september2026/","summary":"This is a maintenance release to support Redis Software version 8.2.0-78.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"8c7fba64d4f162b8e81db506184c6e4699eac18be5e4225e6ae2fc5809958dd9","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support [Redis Software 8.2.0](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-2-releases) version 8.2.0-78. It fixes a high-severity bug with security implications that affected Active-Active databases. For version changes, supported distributions, and known limitations, see the [release notes for 8.2.0-12 (July 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-2-0-releases/8-2-0-12-july2026)."},{"id":"security","title":"Security","role":"security","text":"A high-severity bug with security implications affected Active-Active databases managed by the Redis Software for Kubernetes operator. **Upgrade to operator version 8.2.0-15 or later on this release line to get the fix.**\n\nWhen the operator applied a configuration update to an Active-Active database, it rebuilt and sent the database's entire configuration, but only re-read a credential from its Kubernetes secret if it believed that specific secret had changed. Any update triggered by something else therefore sent the password as empty, and on operator versions before 8.2.0-12, also cleared the mutual TLS (mTLS) client certificates.\n\nEither of the following triggered such an update:\n\n- Any configuration change on the Active-Active database, such as eviction policy, memory size, or backup settings, made by a user or by automation tooling such as GitOps.\n- A modification of the backup secret or a client certificate secret on the cluster, even with no configuration change. The operator re-read the touched secret correctly but wiped the password, because its secret was not the one that changed.\n\nThe impact was two-sided:\n\n- **Availability**: applications connecting with the password failed on every new connection. Existing connections kept working, so the failure looked intermittent.\n- **Security**: the database simultaneously accepted connections with no credentials at all, and with the mTLS variant, accepted clients without certificates. Nothing alerted and the database reported healthy, so a database could run exposed without anyone noticing.\n\nThis affected all released operator versions since the feature was introduced in early 2023. The fix prevents the credential wipe and automatically repairs already-affected databases when you upgrade the operator. No additional steps are required.\n\nThe fix is available in operator versions 7.4.6-11, 7.8.6-20, 7.22.2-45, 8.0.20-27, and 8.2.0-15. <!--RED-212963--> <!--RED-213921-->"},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:8.2.0-78.15` (`redislabs/redis:8.2.0-78.15.minimal` for minimal base image)\n- **Operator**: `redislabs/operator:8.2.0-15`\n- **Services Rigger**: `redislabs/k8s-controller:8.2.0-15`\n- **Call Home Client**: `redislabs/re-call-home-client:8.2.0-15`"},{"id":"openshift-downloads","title":"OpenShift downloads","role":"content","text":"- **OLM operator bundle**: `8.2.0-15.1`\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:8.2.0-78.15` (`registry.connect.redhat.com/redislabs/redis-enterprise:8.2.0-78.15.minimal` for minimal base image)\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:8.2.0-15`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:8.2.0-15`\n- **Call Home Client**: `registry.connect.redhat.com/redislabs/call-home-client:8.2.0-15`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [8.2.0 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-2-0-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/8-2-0-releases/8-2-0-18-october2026","title":"Redis Software for Kubernetes 8.2.0-18 (October 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-2-0-releases/8-2-0-18-october2026/","summary":"This is a maintenance release to support Redis Software version 8.2.0-78.","tags":["docs","operate","kubernetes"],"last_updated":"2026-10-02T10:46:00-05:00","page_type":"content","content_hash":"9617d978a711185143bce0f7faede5e3ad0da46f5c53384247fdab46ffd09e37","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support [Redis Software 8.2.0](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-2-releases) version 8.2.0-78. For version changes, supported distributions, and known limitations, see the [release notes for 8.2.0-12 (July 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-2-0-releases/8-2-0-12-july2026)."},{"id":"enhancements","title":"Enhancements","role":"content","text":"- Set `runAsNonRoot: true` at the container-level `securityContext` <!--RED-217695-->"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- Fixed REAADB creation failing with a 406 error when `tlsMode` is `enabled` and the spec doesn't set `clientAuthenticationCertificates` <!--RED-214573-->\n\n- Fixed new REAADBs with `tlsMode: replica_ssl` requiring a client certificate when none is configured <!--RED-215539-->"},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:8.2.0-78.18` (`redislabs/redis:8.2.0-78.18.minimal` for minimal base image)\n- **Operator**: `redislabs/operator:8.2.0-18`\n- **Services Rigger**: `redislabs/k8s-controller:8.2.0-18`\n- **Call Home Client**: `redislabs/re-call-home-client:8.2.0-18`"},{"id":"openshift-downloads","title":"OpenShift downloads","role":"content","text":"- **OLM operator bundle**: `8.2.0-18.0`\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:8.2.0-78.18` (`registry.connect.redhat.com/redislabs/redis-enterprise:8.2.0-78.18.minimal` for minimal base image)\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:8.2.0-18`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:8.2.0-18`\n- **Call Home Client**: `registry.connect.redhat.com/redislabs/call-home-client:8.2.0-18`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [8.2.0 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-2-0-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/8-2-0-releases/8-2-0-19-october2026","title":"Redis Software for Kubernetes 8.2.0-19 (October 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-2-0-releases/8-2-0-19-october2026/","summary":"This is a maintenance release to support Redis Software version 8.2.0-125.","tags":["docs","operate","kubernetes"],"last_updated":"2026-10-20T11:01:39-05:00","page_type":"content","content_hash":"605de0d13bc699471aa17a778e6b0bf948dc63088a7f524ba0cdcb97b8ea75ce","sections":[{"id":"highlights","title":"Highlights","role":"overview","text":"This is a maintenance release to support [Redis Software 8.2.0](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-2-releases) version 8.2.0-125. For version changes, supported distributions, and known limitations, see the [release notes for 8.2.0-12 (July 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-2-0-releases/8-2-0-12-july2026)."},{"id":"downloads","title":"Downloads","role":"content","text":"- **Redis Enterprise**: `redislabs/redis:8.2.0-125.19` (`redislabs/redis:8.2.0-125.19.minimal` for minimal base image)\n- **Operator**: `redislabs/operator:8.2.0-19`\n- **Services Rigger**: `redislabs/k8s-controller:8.2.0-19`\n- **Call Home Client**: `redislabs/re-call-home-client:8.2.0-19`"},{"id":"openshift-downloads","title":"OpenShift downloads","role":"content","text":"- **OLM operator bundle**: `8.2.0-19.1`\n- **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:8.2.0-125.19` (`registry.connect.redhat.com/redislabs/redis-enterprise:8.2.0-125.19.minimal` for minimal base image)\n- **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:8.2.0-19`\n- **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:8.2.0-19`\n- **Call Home Client**: `registry.connect.redhat.com/redislabs/call-home-client:8.2.0-19`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [8.2.0 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-2-0-releases) for information on known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/8-2-0-releases","title":"Redis Software for Kubernetes 8.2.0 release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-2-0-releases/","summary":"Releases with support for Redis Enterprise Software 8.2.0","tags":["docs","operate","kubernetes"],"last_updated":"2026-10-20T11:01:39-05:00","children":[{"id":"operate/kubernetes/release-notes/8-2-0-releases/8-2-0-19-october2026","summary":"This is a maintenance release to support Redis Software version 8.2.0-125.","title":"Redis Software for Kubernetes 8.2.0-19 (October 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-2-0-releases/8-2-0-19-october2026/"},{"id":"operate/kubernetes/release-notes/8-2-0-releases/8-2-0-18-october2026","summary":"This is a maintenance release to support Redis Software version 8.2.0-78.","title":"Redis Software for Kubernetes 8.2.0-18 (October 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-2-0-releases/8-2-0-18-october2026/"},{"id":"operate/kubernetes/release-notes/8-2-0-releases/8-2-0-15-september2026","summary":"This is a maintenance release to support Redis Software version 8.2.0-78.","title":"Redis Software for Kubernetes 8.2.0-15 (September 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-2-0-releases/8-2-0-15-september2026/"},{"id":"operate/kubernetes/release-notes/8-2-0-releases/8-2-0-13-august2026","summary":"This is a maintenance release to support Redis Software version 8.2.0-46.","title":"Redis Software for Kubernetes 8.2.0-13 (August 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-2-0-releases/8-2-0-13-august2026/"},{"id":"operate/kubernetes/release-notes/8-2-0-releases/8-2-0-12-july2026","summary":"Feature release with Redis Enterprise RBAC custom resource definitions, REDB metric enrichment through tags, and Redis Software 8.2.0.","title":"Redis Software for Kubernetes 8.2.0-12 (July 2026) release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-2-0-releases/8-2-0-12-july2026/"}],"page_type":"content","content_hash":"cc5d583a5d1fe7e00b89c294b67856e051dc6e7caa1e9b6eed1864cb75fdb3c5","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software for Kubernetes 8.2.0 includes new features, bug fixes, enhancements, and support for Redis Software 8.2.0. The latest release is 8.2.0-19 with support for Redis Software version 8.2.0-125."},{"id":"detailed-release-notes","title":"Detailed release notes","role":"content","text":"| Version (Release date) | Major changes |\n| --- | --- |\n| [8.2.0-19 (October 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-2-0-releases/8-2-0-19-october2026/) | This is a maintenance release to support Redis Software version 8.2.0-125. |\n| [8.2.0-18 (October 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-2-0-releases/8-2-0-18-october2026/) | This is a maintenance release to support Redis Software version 8.2.0-78. |\n| [8.2.0-15 (September 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-2-0-releases/8-2-0-15-september2026/) | This is a maintenance release to support Redis Software version 8.2.0-78. |\n| [8.2.0-13 (August 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-2-0-releases/8-2-0-13-august2026/) | This is a maintenance release to support Redis Software version 8.2.0-46. |\n| [8.2.0-12 (July 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-2-0-releases/8-2-0-12-july2026/) | Feature release with Redis Enterprise RBAC custom resource definitions, REDB metric enrichment through tags, and Redis Software 8.2.0. |"},{"id":"supported-distributions","title":"Supported distributions","role":"compatibility","text":"Redis Software for Kubernetes is compatible with [CNCF-conformant](https://www.cncf.io/training/certification/software-conformance/) Kubernetes platforms. The operator follows standard Kubernetes APIs and practices and is designed to run consistently across certified Kubernetes environments.\n\nThe following table shows supported Kubernetes versions at the time of this release. For a list of platforms tested with this release, see [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions).\n\n| Kubernetes | **Redis <nobr>8.2.0</nobr>** |\n|---|---|\n| 1.36 | Supported |\n| 1.35 | Supported |\n| 1.34 | Supported |\n| 1.33 | Supported |\n| 1.32 | Deprecated |"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See the [8.2.0-12 (July 2026)](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-2-0-releases/8-2-0-12-july2026) release notes for known limitations."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes","title":"Redis Enterprise Software for Kubernetes release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/","summary":"Redis Enterprise Software for Kubernetes operator release notes.","tags":["docs","operate","kubernetes"],"last_updated":"2024-02-26T13:11:00+01:00","children":[{"id":"operate/kubernetes/release-notes/8-2-0-releases","summary":"Releases with support for Redis Enterprise Software 8.2.0","title":"Redis Software for Kubernetes 8.2.0 release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-2-0-releases/"},{"id":"operate/kubernetes/release-notes/8-0-20-releases","summary":"Releases with support for Redis Enterprise Software 8.0.20","title":"Redis Enterprise for Kubernetes 8.0.20 release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-20-releases/"},{"id":"operate/kubernetes/release-notes/8-0-18-releases","summary":"Releases with support for Redis Enterprise Software 8.0.18","title":"Redis Enterprise for Kubernetes 8.0.18 release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-18-releases/"},{"id":"operate/kubernetes/release-notes/8-0-16-releases","summary":"Releases with support for Redis Enterprise Software 8.0.16","title":"Redis Enterprise for Kubernetes 8.0.16 release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-16-releases/"},{"id":"operate/kubernetes/release-notes/8-0-10-releases","summary":"Releases with support for Redis Enterprise Software 8.0.10","title":"Redis Enterprise for Kubernetes 8.0.10 release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-10-releases/"},{"id":"operate/kubernetes/release-notes/8-0-6-releases","summary":"Releases with support for Redis Enterprise Software 8.0.6","title":"Redis Enterprise for Kubernetes 8.0.6 release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-6-releases/"},{"id":"operate/kubernetes/release-notes/8-0-2-releases","summary":"Releases with support for Redis Enterprise Software 8.0.2","title":"Redis Enterprise for Kubernetes 8.0.2 release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-2-releases/"},{"id":"operate/kubernetes/release-notes/7-22-2-releases","summary":"Releases with support for Redis Enterprise Software 7.22.2","title":"Redis Enterprise for Kubernetes 7.22.2 release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/"},{"id":"operate/kubernetes/release-notes/7-22-0-releases","summary":"Releases with support for Redis Enterprise Software 7.22.0","title":"Redis Enterprise for Kubernetes 7.22.0 release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-0-releases/"},{"id":"operate/kubernetes/release-notes/7-8-6-releases","summary":"Releases with support for Redis Enterprise Software 7.8.6","title":"Redis Enterprise for Kubernetes 7.8.6 release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/"},{"id":"operate/kubernetes/release-notes/7-8-4-releases","summary":"Releases with support for Redis Enterprise Software 7.8.4","title":"Redis Enterprise for Kubernetes 7.8.4 release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-4-releases/"},{"id":"operate/kubernetes/release-notes/7-8-2-releases","summary":"Releases with support for Redis Enterprise Software 7.8.2","title":"Redis Enterprise for Kubernetes 7.8.2-6 release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-2-releases/"},{"id":"operate/kubernetes/release-notes/7-4-6-releases","summary":"Releases with support for Redis Enterprise Software 7.4.6.","title":"Redis Enterprise for Kubernetes 7.4.6 release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/"},{"id":"operate/kubernetes/release-notes/7-4-2-releases","summary":"Releases with support for Redis Enterprise Software 7.4.2.","title":"Redis Enterprise for Kubernetes 7.4.2 release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-2-releases/"},{"id":"operate/kubernetes/release-notes/7-2-4-releases","summary":"Support for Redis Enterprise 7.2.4, platform support updates, and bug fixes.","title":"7.2.4 release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/"},{"id":"operate/kubernetes/release-notes/previous-releases","summary":"Release notes for versions of Redis Enterprise for Kubernetes released more than 18 months ago.","title":"Previous version Redis Enterprise for Kubernetes release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/"}],"page_type":"content","content_hash":"b5cd12ed55e546db0966e1d2bf0821c08340e8fcd0e71171f36d2d63668de7ce","sections":[{"id":"content","title":"Content","role":"content","text":"| Version (Release date) | Major changes |\n| --- | --- |\n| [8.2.0 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-2-0-releases/) | Releases with support for Redis Enterprise Software 8.2.0 |\n| [8.0.20 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-20-releases/) | Releases with support for Redis Enterprise Software 8.0.20 |\n| [8.0.18 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-18-releases/) | Releases with support for Redis Enterprise Software 8.0.18 |\n| [8.0.16 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-16-releases/) | Releases with support for Redis Enterprise Software 8.0.16 |\n| [8.0.10 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-10-releases/) | Releases with support for Redis Enterprise Software 8.0.10 |\n| [8.0.6 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-6-releases/) | Releases with support for Redis Enterprise Software 8.0.6 |\n| [8.0.2 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/8-0-2-releases/) | Releases with support for Redis Enterprise Software 8.0.2 |\n| [7.22.2 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-2-releases/) | Releases with support for Redis Enterprise Software 7.22.2 |\n| [7.22.0 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-22-0-releases/) | Releases with support for Redis Enterprise Software 7.22.0 |\n| [7.8.6 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-6-releases/) | Releases with support for Redis Enterprise Software 7.8.6 |\n| [7.8.4 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-4-releases/) | Releases with support for Redis Enterprise Software 7.8.4 |\n| [7.8.2 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-8-2-releases/) | Releases with support for Redis Enterprise Software 7.8.2 |\n| [7.4.6 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-6-releases/) | Releases with support for Redis Enterprise Software 7.4.6. |\n| [7.4.2 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-4-2-releases/) | Releases with support for Redis Enterprise Software 7.4.2. |\n| [7.2.4 releases](https://redis.io/docs/latest/operate/kubernetes/release-notes/7-2-4-releases/) | Support for Redis Enterprise 7.2.4, platform support updates, and bug fixes. |\n| [Previous versions](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/) | Release notes for versions of Redis Enterprise for Kubernetes released more than 18 months ago. |"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/previous-releases/6-4-2-5","title":"Redis Enterprise for Kubernetes release notes 6.4.2-5 (April 2023)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/6-4-2-5/","summary":"The Redis Enterprise K8s 6.4.2-5 is a maintenance release for version 6.4.2-4.","aliases":["/operate/kubernetes/release-notes/6-4-2-releases/6-4-2-5/"],"tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"9a6faef4f1fb0d7490c3da4c577dda036f309738f794541881bb1dc9c3487fc9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release of Redis Enterprise for Kubernetes 6.4.2-4 that adds supports for Redis Enterprise 6.4.2-61.\n\nNew images and fixes are listed below. Refer to [6.2.4-4 (March 2023)](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-4-2-4) for compatibility notes and known limitations."},{"id":"images","title":"Images","role":"content","text":"* **Redis Enterprise**: `redislabs/redis:6.4.2-61`\n* **Operator**: `redislabs/operator:6.4.2-5`\n* **Services Rigger**: `redislabs/k8s-controller:6.4.2-5`"},{"id":"openshift-images","title":"OpenShift images","role":"content","text":"* **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:6.4.2-61.rhel8-openshift`\n    (or `redislabs/redis-enterprise:6.4.2-61.rhel7-openshift` if upgrading from RHEL 7)\n* **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:6.4.2-5`\n* **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:6.4.2-5`"},{"id":"openshift-olm-bundles","title":"OpenShift OLM bundles","role":"content","text":"* Redis Enterprise operator bundle version: `v6.4.2-5.0`"},{"id":"feature-enhancements","title":"Feature enhancements","role":"content","text":"* Upgraded to support Redis Enterprise 6.4.2-61"},{"id":"bug-fixes","title":"Bug fixes","role":"content","text":"* Fixed bug that can cause Redis cluster to become unresponsive during a Kubernetes upgrade on clusters with more than 9 REC nodes. (RED-93025)"},{"id":"compatibility-notes","title":"Compatibility notes","role":"compatibility","text":"See [Redis Enterprise for Kubernetes release notes 6.4.2-4 (March 2023)](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-4-2-4)."},{"id":"before-upgrading","title":"Before upgrading","role":"content","text":"Be aware the following changes included in this release affect the upgrade process. Please read carefully before upgrading,\n\n* **Supported upgrade paths**\n\n  If you are using a version earlier than 6.2.10-45, you cannot upgrade directly to this release. You must upgrade to 6.2.10-45 before you can upgrade to versions 6.2.18 or later.\n\n* **ValidatingWebhookConfiguration**\n\n  This release uses a new `ValidatingWebhookConfiguration` resource to replace the `redb-admission` webhook resource. To use releases 6.4.2-4 or later, delete the old webhook resource and apply the new file. See [upgrade Redis cluster](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#reapply-webhook) for instructions."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [Redis Enterprise for Kubernetes release notes 6.4.2-4 (March 2023)](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-4-2-4)."},{"id":"security","title":"Security","role":"security","text":"For a list of fixes related to CVEs, see the [Redis Enterprise 6.4.2-61 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-61)."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/previous-releases/6-4-2-6","title":"Redis Enterprise for Kubernetes release notes 6.4.2-6 (June 2023)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/6-4-2-6/","summary":"The Redis Enterprise K8s 6.4.2-6 release supports Redis Enterprise Software 6.4.2 and contains new features and feature improvements.","aliases":["/operate/kubernetes/release-notes/6-4-2-releases/6-4-2-6/"],"tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"ddd2b0341f5a646ac3304ea4dd6e7b3be7c74e20e13de7d3d3742fcd4179d402","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The Redis Enterprise K8s 6.4.2-6 release supports Redis Enterprise Software 6.4.2 and contains new features and feature improvements.\n\nThe key features, bug fixes, and known limitations are described below."},{"id":"images","title":"Images","role":"content","text":"* **Redis Enterprise**: `redislabs/redis:6.4.2-81`\n* **Operator**: `redislabs/operator:6.4.2-6`\n* **Services Rigger**: `redislabs/k8s-controller:6.4.2-6`"},{"id":"openshift-images","title":"OpenShift images","role":"content","text":"* **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:6.4.2-81.rhel8-openshift`\n    (or `redislabs/redis-enterprise:6.4.2-81.rhel7-openshift` if upgrading from RHEL 7)\n* **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:6.4.2-6`\n* **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:6.4.2-6`"},{"id":"openshift-olm-bundles","title":"OpenShift OLM bundles","role":"content","text":"* Redis Enterprise operator bundle version: `v6.4.2-6`"},{"id":"new-features","title":"New features","role":"content","text":"* General availability of the Active-Active database controller for declarative configuration support"},{"id":"feature-improvements","title":"Feature improvements","role":"content","text":"* Active-Active controller improvements include adding support for the following:\n  * Configuration via OLM\n  * Certificates in `globalConfiguration`\n  * Backups in `globalConfiguration`\n  * Modules (preview)\n  * Hashicorp Vault secrets\n  * indication of sync failures to REAADB\n* Added support for separate `podAnnotations` for Redis Enterprise pods to allow setting `appArmor` exceptions\n* Timezone can be passed as environment variable into Redis Enterprise pods\n* REDB namespaces can be labelled without the operator going into `CrashLoopBackoff`\n* Added support for K8s 1.27\n* Added support for K8s 1.25 for EKS\n* Added support for K8s 1.26 for AKS and GKE\n* Improved log collector execution log, filtering out empty files, capturing volume attachments, option to disable RS capture debug info package\n* Added `extraEnvVars` field in REC to support environment variables for RS pods\n* Changed RKE2 to the default for Rancher distributions"},{"id":"fixed-bugs","title":"Fixed bugs","role":"content","text":"* REC in invalid state can't be deleted (RED-78124)\n* OpenShift route not deleted after REC is deleted (RED-94121)\n* REAADB secret status not being updated in source cluster (RED-96296)\n* Invalid REAADB that is not rejected by admission might be deleted after apply (RED-96300)\n* Operator logs an error about missing permissions to nodes on startup (RED-98227)\n* Operator continues to update REC certificates in a loop (RED-98586)\n* Log collector script may crash when trying to write multi-byte characters to file (RED-99869)\n* Log collector fails to include RS debuginfo when old kubectl is used (RED-101170)\n* REC creation fails due to required LDAP configuration in OLM form view (RED-100517)\n* Upgrading to 6.4.2 (or 6.2.18) on OpenShift will fail if pod 0 is not the master node when upgrade starts (RED-102100)\n* Upgrading from 6.2.8-15 (and below) to 6.2.12-1 (and above) may break the databases (RED-102241)"},{"id":"api-changes","title":"API changes","role":"content","text":"The following fields were changed in the [RedisEnterpriseCluster (REC) API](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api):\n  * `containerTimezoneSpec.timezoneName` added to configure timezones on the Redis Enterprise pods\n  * `hostAliases` added for adding an entry to the Redis Enterprise pods' `/etc/hosts`\n  * `redisEnteprisePodAnnotations` added to specify annotations that should only be set on the Redis Enterprise pods\n  * `ingressOrRouteSpec` enabled to use without alpha features enabled\n  * `extraEnvVars` added for advanced users to add environment variables to the Redis Enterprise pods\n  * `ServicesRiggerConfigurationSpec.podAnnotations` added to specify annotations that should only be set on the service rigger pods\n\n* The following custom resources definitions were added:\n  * [`RedisEnterpriseRemoteCluster` (RERC) API](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_remote_cluster_api)\n  * [`RedisEnterpriseActiveActiveDatabase` (REAADB) API](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_active_active_database_api)"},{"id":"compatibility-notes","title":"Compatibility notes","role":"compatibility","text":""},{"id":"deprecated-features","title":"Deprecated features","role":"content","text":"* Support for Gesher, the advanced admission control configuration, is deprecated."},{"id":"supported-distributions","title":"Supported distributions","role":"compatibility","text":"The following table shows supported distributions at the time of this release. You can also find this list in [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions).\n\n\n| **Kubernetes version**  | 1.22       | 1.23       | 1.24       | 1.25       | 1.26       | 1.27       |\n|:------------------------|:----------:|:----------:|:----------:|:----------:|:----------:|:----------:|\n| Community Kubernetes    |            | deprecated | supported  | supported  | supported  | supported* |\n| Amazon EKS              | deprecated | deprecated | supported  | supported* |            |            |\n| Azure AKS               |            | deprecated | supported  | supported  | supported* |\n| Google GKE              | deprecated | deprecated | supported  | supported  | supported* |            |\n| Rancher 2.6             | deprecated | deprecated | supported  |            |            |            |\n| Rancher 2.7             |            | deprecated | supported  |            |            |            |\n| VMware TKG 1.6          | deprecated | deprecated |            |            |            |            |\n| **OpenShift version**   | **4.9**    | **4.10**   | **4.11**   | **4.12**   |            |            |\n|                         |            | deprecated | supported  | supported  |            |            |\n| **VMware TKGI version** | **1.13**   | **1.14**   | **1.15**   |            |            |            |\n|                         | deprecated | deprecated | supported  |            |            |            |\n\n\\* Support added in this release\n\n> [!NOTE]\n>  Redis Enterprise for Kubernetes now uses RKE2 as the default for Rancher distributions."},{"id":"before-upgrading","title":"Before upgrading","role":"content","text":"Be aware the following changes included in this release affect the upgrade process. Please read carefully before upgrading to 6.4.2-6.\n\n* **ValidatingWebhookConfiguration**\n\n  This release uses a new `ValidatingWebhookConfiguration` resource to replace the `redb-admission` webhook resource. To use releases 6.4.2-4 or later, delete the old webhook resource and apply the new file. See [upgrade Redis cluster](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#reapply-webhook) for instructions.\n\n* **OpenShift SCC**\n\n  This release includes a new SCC (`redis-enterprise-scc-v2`) that you need to bind to your service account before upgrading. OpenShift clusters running version 6.2.12 or earlier upgrading to version 6.2.18 or later might get stuck if you skip this step. See [upgrade a Redis Enterprise cluster (REC)](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#before-upgrading) for details."},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"new-limitations","title":"New limitations","role":"content","text":"* **Admission controller may not alert when patching the REAADB with missing secret or RERC (RED-104463)** Remove the secret from the spec and create the missing secret, and then re-patch the REAADB again.\n\n* **REAADB changes might fail with \"gateway timeout\" errors, mostly on OpenShift (RED-103048)** Retry the operation.\n\n* **Misleading error appears when a service creation has failed due to service already existing \"`provided IP is already allocated`\" (RED-100669)** Delete the service manually.\n\n* **Existing OpenShift route during REC creation might prevent REC from starting (RED-100668)** Delete the route manually.\n\n* **Creating two databases with the same name directly on Redis Enterprise software will cause the service to be deleted and the database will not be available (RED-99997)** Avoid duplicating database names. Database creation via K8s has validation in place to prevent this.\n\n* **Installing the operator bundle produces warning: `Warning: would violate PodSecurity \"restricted: v1.24\"` (RED-97381)** Ignore the warning. This issue is documented as benign on official Red Hat documentation."},{"id":"existing-limitations","title":"Existing limitations","role":"content","text":"* **RERC resources must have a unique name (`<rec-name>/<rec-namespace>`) (RED-96302)**\n\n* **Admission is not blocking REAADB with `shardCount` which exceeds license quota. (RED-96301)** Fix the problems with the REAADB and reapply.\n\n* **Active-Active setup removal might keep services or routes undeleted (RED-77752)** Delete services or routes manually if you encounter this problem.\n\n* **`autoUpgrade` set to `true` can cause unexpected bdb upgrades when `redisUpgradePolicy` is set to `true` (RED-72351)** Contact support if your deployment is impacted.\n\n* **Following the previous quick start guide version causes issues with creating an REDB due to unrecognized memory field name (RED-69515)** The workaround is to use the newer (current) revision of the [quick start](https://redis.io/docs/latest/operate/kubernetes/deployment/quick-start).\n\n* **PVC size issues when using decimal value in spec (RED-62132)** Make sure you use integer values for the PVC size.\n\n* **REC might report error states on initial startup (RED-61707)** There is no workaround at this time except to ignore the errors.\n\n* **Hashicorp Vault integration - no support for Gesher (RED-55080)** There is no workaround for this issue. Gesher support has been deprecated.\n\n* **REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED-47254)** When REC clusters are deployed on Kubernetes clusters with unsynchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes.\n\n* **Deleting an OpenShift project with an REC deployed may hang (RED-47192)** When an REC cluster is deployed in a project (namespace) and has REDB resources, the REDB resources must be deleted first before the REC can be deleted. Therefore, until the REDB resources are deleted, the project deletion will hang. The fix is to delete the REDB resources first and the REC second. Then, you can delete the project.\n\n* **Clusters must be named \"rec\" in OLM-based deployments (RED-39825)** In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) are bound to a specific service account name (namely, \"rec\"). The workaround is to name the cluster \"rec\".\n\n* **Readiness probe incorrect on failures (RED-39300)** STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure.\n\n* **Internal DNS and Kubernetes DNS may have conflicts (RED-37462)** DNS conflicts are possible between the cluster `mdns_server` and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names.\n\n* **5.4.10 negatively impacts 5.4.6 (RED-37233)** Kubernetes-based 5.4.10 deployments seem to negatively impact existing 5.4.6 deployments that share a Kubernetes cluster.\n\n* **Node CPU usage is reported instead of pod CPU usage (RED-36884)** In Kubernetes, the reported node CPU usage is the usage of the Kubernetes worker node hosting the REC pod.\n\n* **An unreachable cluster has status running (RED-32805)** When a cluster is in unreachable state, the state remains `running` instead of triggering an error.\n\n* **Long cluster names cause routes to be rejected  (RED-25871)** A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name exceeds 63 characters. The workaround is to limit the cluster name to 20 characters or fewer.\n\n* **Cluster CR (REC) errors are not reported after invalid updates (RED-25542)** A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence."},{"id":"security","title":"Security","role":"security","text":"For a list of fixes related to CVEs, see the [Redis Enterprise 6.4.2-81 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-81)."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/previous-releases/6-4-2-8","title":"Redis Enterprise for Kubernetes release notes 6.4.2-8 (July 2023)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/6-4-2-8/","summary":"The Redis Enterprise K8s 6.4.2-8 release supports Redis Enterprise Software 6.4.2 and contains new features and feature improvements.","aliases":["/operate/kubernetes/release-notes/6-4-2-releases/6-4-2-8/"],"tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"710e35a337a3bdee4f6ae213d437303ac293e4c91ff90642f772d8f01535a8cd","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise for Kubernetes version 6.4.2-8 is a maintenance release of version 6.2.4-6. It includes critical bug fixes and an updated Redis Enterprise image."},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"feature-enhancements","title":"Feature enhancements","role":"content","text":"* New Redis Enterprise software version 6.4.2-94 (RED-105738)"},{"id":"bug-fixes","title":"Bug fixes","role":"content","text":"* Fixed bad handling of certificate chains in the REC controller (RED-105578)"},{"id":"upgrade-considerations","title":"Upgrade considerations","role":"content","text":"Be aware the following changes included in this release affect the upgrade process. Please read carefully before upgrading to 6.4.2-8.\n\n* **ValidatingWebhookConfiguration**\n\n  This release uses a new `ValidatingWebhookConfiguration` resource to replace the `redb-admission` webhook resource. To use releases 6.4.2-4 or later, delete the old webhook resource and apply the new file. See [upgrade Redis cluster](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#reapply-webhook) for instructions.\n\n* **OpenShift SCC**\n\n  This release includes a new SCC (`redis-enterprise-scc-v2`) that you need to bind to your service account before upgrading. OpenShift clusters running version 6.2.12 or earlier upgrading to version 6.2.18 or later might get stuck if you skip this step. See [upgrade a Redis Enterprise cluster (REC)](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#before-upgrading) for expand."},{"id":"compatibility","title":"Compatibility","role":"compatibility","text":""},{"id":"deprecated-features","title":"Deprecated features","role":"content","text":"* Support for Gesher, the advanced admission control configuration, is deprecated."},{"id":"supported-distributions","title":"Supported distributions","role":"compatibility","text":"The following table shows supported distributions at the time of this release. You can also find this list in [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) reflecting the most recent release.\n\n| **Kubernetes version**  | 1.22       | 1.23       | 1.24       | 1.25       | 1.26       | 1.27       |\n|:------------------------|:----------:|:----------:|:----------:|:----------:|:----------:|:----------:|\n| Community Kubernetes    |            | deprecated | supported  | supported  | supported  | supported  |\n| Amazon EKS              | deprecated | deprecated | supported  | supported  |            |            |\n| Azure AKS               |            | deprecated | supported  | supported  | supported  |\n| Google GKE              | deprecated | deprecated | supported  | supported  | supported  |            |\n| Rancher 2.6             | deprecated | deprecated | supported  |            |            |            |\n| Rancher 2.7             |            | deprecated | supported  |            |            |            |\n| VMware TKG 1.6          | deprecated | deprecated |            |            |            |            |\n| **OpenShift version**   | **4.9**    | **4.10**   | **4.11**   | **4.12**   |            |            |\n|                         |            | deprecated | supported  | supported  |            |            |\n| **VMware TKGI version** | **1.13**   | **1.14**   | **1.15**   |            |            |            |\n|                         | deprecated | deprecated | supported  |            |            |            |\n\n\\* Support added in this release\n\n> [!NOTE]\n>  Redis Enterprise for Kubernetes now uses RKE2 as the default for Rancher distributions."},{"id":"images","title":"Images","role":"content","text":"* **Redis Enterprise**: `redislabs/redis:6.4.2-94`\n* **Operator**: `redislabs/operator:6.4.2-8`\n* **Services Rigger**: `redislabs/k8s-controller:6.4.2-8.`\n\n#### OpenShift images\n\n* **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:6.4.2-94.rhel8-openshift`\n    (or `redislabs/redis-enterprise:6.4.2-94.rhel7-openshift` if upgrading from RHEL 7)\n* **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:6.4.2-8`\n* **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:6.4.2-8`\n\n#### OLM bundle\n\n* **Redis Enterprise operator bundle** : `v6.4.2-8`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"* **Air-gapped environments using Gears will get stuck during cluster recovery(RED-112076)** This bug affects Redis Enterprise for Kubernetes versions older than 7.2.4-2."},{"id":"existing-limitations","title":"Existing limitations","role":"content","text":"* **RERC resources must have a unique name (RED-96302)** The string \"rec-name\"/\"rec-namespace\" must be different from all other participating clusters in teh Active-Active database.\n\n* **Admission is not blocking REAADB with `shardCount` which exceeds license quota (RED-96301)** Fix the problems with the REAADB and reapply.\n\n* **Active-Active setup removal might keep services or routes undeleted (RED-77752)** Delete services or routes manually if you encounter this problem.\n\n* **`autoUpgrade` set to `true` can cause unexpected bdb upgrades when `redisUpgradePolicy` is set to `true` (RED-72351)** Contact support if your deployment is impacted.\n\n* **Following the previous quick start guide version causes issues with creating an REDB due to unrecognized memory field name (RED-69515)** The workaround is to use the newer (current) revision of Deploy Redis Enterprise Software for Kubernetes.\n\n* **PVC size issues when using decimal value in spec (RED-62132)** Make sure you use integer values for the PVC size.\n\n* **REC might report error states on initial startup (RED-61707)** There is no workaround at this time except to ignore the errors.\n\n* **Hashicorp Vault integration - no support for Gesher (RED-55080)** There is no workaround for this issue. Gesher support has been deprecated.\n\n* **REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED-47254)** When REC clusters are deployed on Kubernetes clusters without synchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes.\n\n* **Deleting an OpenShift project with an REC deployed may hang (RED-47192)** When an REC cluster is deployed in a project (namespace) and has REDB resources, the REDB resources must be deleted first before the REC can be deleted. Therefore, until the REDB resources are deleted, the project deletion will hang. The fix is to delete the REDB resources first and the REC second. Then, you can delete the project.\n\n* **Clusters must be named 'rec' in OLM-based deployments (RED-39825)** In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) are bound to a specific service account name (namely, \"rec\"). The workaround is to name the cluster \"rec\".\n\n* **Readiness probe incorrect on failures (RED-39300)** STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure.\n\n* **Internal DNS and Kubernetes DNS may have conflicts (RED-37462)** DNS conflicts are possible between the cluster `mdns_server` and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names.\n\n* **Node CPU usage is reported instead of pod CPU usage (RED-36884)** In Kubernetes, the reported node CPU usage is the usage of the Kubernetes worker node hosting the REC pod.\n\n* **An unreachable cluster has status running (RED-32805)** When a cluster is in unreachable state, the state remains `running` instead of triggering an error.\n\n* **Long cluster names cause routes to be rejected  (RED-25871)** A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name exceeds 63 characters. The workaround is to limit the cluster name to 20 characters or fewer.\n\n* **Cluster CR (REC) errors are not reported after invalid updates (RED-25542)** A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence."},{"id":"security","title":"Security","role":"security","text":"For a list of fixes related to CVEs, see the [Redis Enterprise 6.4.2-94 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-94)."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/previous-releases/6-4-2-8-dec23","title":"Redis Enterprise for Kubernetes release notes 6.4.2-8 (Dec 2023)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/6-4-2-8-dec23/","summary":"The Redis Enterprise K8s 6.4.2-8 release supports Redis Enterprise Software 6.4.2 and contains new features and feature improvements.","aliases":["/operate/kubernetes/release-notes/6-4-2-releases/6-4-2-8-dec23/"],"tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"e937ee5a5018e8f027368248e42f6cae98238cf1125868e4320fe186f34ba98a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release of Redis Enterprise for Kubernetes version 6.4.2-8 and includes an updated Redis Enterprise image."},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"feature-enhancements","title":"Feature enhancements","role":"content","text":"* New Redis Enterprise software version 6.4.2-103"},{"id":"upgrade-considerations","title":"Upgrade considerations","role":"content","text":"Be aware the following changes included in this release affect the upgrade process. Please read carefully before upgrading to 6.4.2-8.\n\n* **Upgrade path to versions 7.2.4-2 or later**\n\n  Upgrading from this versions 7.2.4-2 or 7.2.4-7 is not possible using the OpenShift Operator Lifecycle Manager (OLM). OLM users will need to skip to version 7.2.4-10 to upgrade from this release version.\n\n* **ValidatingWebhookConfiguration**\n\n  This release uses a new `ValidatingWebhookConfiguration` resource to replace the `redb-admission` webhook resource. To use releases 6.4.2-4 or later, delete the old webhook resource and apply the new file. See [upgrade Redis cluster](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#reapply-webhook) for instructions.\n\n* **OpenShift SCC**\n\n  This release includes a new SCC (`redis-enterprise-scc-v2`) that you need to bind to your service account before upgrading. OpenShift clusters running version 6.2.12 or earlier upgrading to version 6.2.18 or later might get stuck if you skip this step. See [upgrade a Redis Enterprise cluster (REC)](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#before-upgrading) for expand."},{"id":"compatibility","title":"Compatibility","role":"compatibility","text":"See [6.4.2-8 (July 2023) release notes](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/6-4-2-8#compatibility)."},{"id":"images","title":"Images","role":"content","text":"* **Redis Enterprise**: `redislabs/redis:6.4.2-103`\n* **Operator**: `redislabs/operator:6.4.2-8`\n* **Services Rigger**: `redislabs/k8s-controller:6.4.2-8.`\n\n#### OpenShift images\n\n* **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:6.4.2-103.rhel8-openshift`\n    (or `redislabs/redis-enterprise:6.4.2-103.rhel7-openshift` if upgrading from RHEL 7)\n* **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:6.4.2-8`\n* **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:6.4.2-8`\n\n#### OLM bundle\n\n* **Redis Enterprise operator bundle** : `v6.4.2-8.1`"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [6.4.2-8 (July 2023) release notes](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/6-4-2-8#known-limitations)."},{"id":"security","title":"Security","role":"security","text":"For a list of fixes related to CVEs, see the [Redis Enterprise 6.4.2-103 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-103)."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/previous-releases/6-4-2-8-oct24","title":"Redis Enterprise for Kubernetes release notes 6.4.2-8 (Oct 2024)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/6-4-2-8-oct24/","summary":"The Redis Enterprise K8s 6.4.2-8 release supports Redis Enterprise Software 6.4.2.","aliases":["/operate/kubernetes/release-notes/6-4-2-releases/6-4-2-8-oct24/"],"tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"7dce95f5ad7ca6337b87175a6f64b7d0d4ff0cbd75cea7f8d0f201bc66b18b4c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release of Redis Enterprise for Kubernetes version 6.4.2-8 and includes an updated Redis Enterprise image."},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"feature-enhancements","title":"Feature enhancements","role":"content","text":"* New Redis Enterprise software version 6.4.2-115"},{"id":"upgrade-considerations","title":"Upgrade considerations","role":"content","text":"Be aware the following changes included in this release affect the upgrade process. Please read carefully before upgrading to 6.4.2-8.\n\n* **Upgrade path to versions 7.2.4-2 or later**\n\n  Upgrading from this versions 7.2.4-2 or 7.2.4-7 is not possible using the OpenShift Operator Lifecycle Manager (OLM). OLM users will need to skip to version 7.2.4-10 to upgrade from this release version.\n\n* **ValidatingWebhookConfiguration**\n\n  This release uses a new `ValidatingWebhookConfiguration` resource to replace the `redb-admission` webhook resource. To use releases 6.4.2-4 or later, delete the old webhook resource and apply the new file. See [upgrade Redis cluster](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#reapply-webhook) for instructions.\n\n* **OpenShift SCC**\n\n  This release includes a new SCC (`redis-enterprise-scc-v2`) that you need to bind to your service account before upgrading. OpenShift clusters running version 6.2.12 or earlier upgrading to version 6.2.18 or later might get stuck if you skip this step. See [upgrade a Redis Enterprise cluster (REC)](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#before-upgrading) for more info."},{"id":"compatibility","title":"Compatibility","role":"compatibility","text":"See [6.4.2-8 (July 2023) release notes](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/6-4-2-8#compatibility)."},{"id":"images","title":"Images","role":"content","text":"* **Redis Enterprise**: `redislabs/redis:6.4.2-115`\n* **Operator**: `redislabs/operator:6.4.2-8`\n* **Services Rigger**: `redislabs/k8s-controller:6.4.2-8.`\n\n#### OpenShift images\n\n* **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:6.4.2-115.rhel8-openshift`\n    (or `redislabs/redis-enterprise:6.4.2-115.rhel7-openshift` if upgrading from RHEL 7)\n* **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:6.4.2-8`\n* **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:6.4.2-8`\n\n#### OLM bundle\n\n* **Redis Enterprise operator bundle** : `v6.4.2-8.11`\n\nThe OLM version v6.4.2-8.11 replaces the earlier v6.4.2-8.10 release for the same Redis software version, providing only upgrade path fixes."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [6.4.2-8 (July 2023) release notes](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/6-4-2-8#known-limitations)."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/previous-releases","title":"Previous version Redis Enterprise for Kubernetes release notes","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/","summary":"Release notes for versions of Redis Enterprise for Kubernetes released more than 18 months ago.","tags":["docs","operate","kubernetes"],"last_updated":"2026-06-03T14:51:40-05:00","children":[{"id":"operate/kubernetes/release-notes/previous-releases/6-4-2-8-oct24","summary":"The Redis Enterprise K8s 6.4.2-8 release supports Redis Enterprise Software 6.4.2.","title":"Redis Enterprise for Kubernetes release notes 6.4.2-8 (Oct 2024)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/6-4-2-8-oct24/"},{"id":"operate/kubernetes/release-notes/previous-releases/6-4-2-8-dec23","summary":"The Redis Enterprise K8s 6.4.2-8 release supports Redis Enterprise Software 6.4.2 and contains new features and feature improvements.","title":"Redis Enterprise for Kubernetes release notes 6.4.2-8 (Dec 2023)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/6-4-2-8-dec23/"},{"id":"operate/kubernetes/release-notes/previous-releases/6-4-2-8","summary":"The Redis Enterprise K8s 6.4.2-8 release supports Redis Enterprise Software 6.4.2 and contains new features and feature improvements.","title":"Redis Enterprise for Kubernetes release notes 6.4.2-8 (July 2023)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/6-4-2-8/"},{"id":"operate/kubernetes/release-notes/previous-releases/6-4-2-6","summary":"The Redis Enterprise K8s 6.4.2-6 release supports Redis Enterprise Software 6.4.2 and contains new features and feature improvements.","title":"Redis Enterprise for Kubernetes release notes 6.4.2-6 (June 2023)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/6-4-2-6/"},{"id":"operate/kubernetes/release-notes/previous-releases/6-4-2-5","summary":"The Redis Enterprise K8s 6.4.2-5 is a maintenance release for version 6.4.2-4.","title":"Redis Enterprise for Kubernetes release notes 6.4.2-5 (April 2023)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/6-4-2-5/"},{"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-4-2-4","summary":"The Redis Enterprise K8s 6.4.2-4 release supports Redis Enterprise Software 6.4.2 and contains new features and feature improvements.","title":"Redis Enterprise for Kubernetes release notes 6.4.2-4 (March 2023)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-4-2-4/"},{"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-2-18-41-jan-2023","summary":"This is a maintenance release for 6.2.18 and includes support for Redis Enterprise 6.2.18-72.","title":"Redis Enterprise for Kubernetes release notes 6.2.18-41 (Jan 2023)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-2-18-41-jan-2023/"},{"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-2-18-41","summary":"This is a maintenance release for 6.2.18 and includes bug fixes.","title":"Redis Enterprise for Kubernetes release notes 6.2.18-41 (Dec 2022)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-2-18-41/"},{"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-2-12-1","summary":"Support added for additional distributions, as well as feature improvements and bug fixes.","title":"Redis Enterprise for Kubernetes release notes 6.2.12-1 (Sept 2022)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-2-12-1/"},{"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-2-18-3","summary":"Support added for additional distributions, as well as feature improvements and bug fixes.","title":"Redis Enterprise for Kubernetes release notes 6.2.18-3 (Nov 2022)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-2-18-3/"},{"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-2-10-45-2022-07","summary":"Support added for additional distributions as well as some feature improvements.","title":"Redis Enterprise for Kubernetes release notes 6.2.10-45 (July 2022)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-2-10-45-2022-07/"},{"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-2-10-34-2022-05","summary":"Support for REBD specific upgrade policies, memcached type REDBs, and RHEL8 for OpenShift, as well as feature improvements and bug fixes.","title":"Redis Enterprise for Kubernetes Release Notes 6.2.10-34 (May 2022)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-2-10-34-2022-05/"},{"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-2-10-4-2022-03","summary":"Added support for RS 6.2.10 as well as feature improvements and bug fixes.","title":"Redis Enterprise for Kubernetes Release Notes 6.2.10-4 (March 2022)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-2-10-4-2022-03/"},{"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-2-8-15-2022-01","summary":"Maintenance release with bug fixes","title":"Redis Enterprise for Kubernetes Release Notes 6.2.8-15 (January 2022)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-2-8-15-2022-01/"},{"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-2-8-11-2022-01","summary":"Support for Istio as ingress controller, K8s 1.22 (AKS, kOps, GKE), OpenShift 4.9","title":"Redis Enterprise for Kubernetes Release Notes 6.2.8-11 (January 2022)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-2-8-11-2022-01/"},{"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-2-8-2-2021-11","summary":"Support for RS 6.2.8, certificate management, and Redis upgrade policy.","title":"Redis Enterprise for Kubernetes Release Notes 6.2.8-2 (November 2021)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-2-8-2-2021-11/"},{"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-2-4-1-2021-09","summary":"Support for RS 6.2.4 and OpenShift 4.8. Support for K8s 1.19 (EKS and AKS), K8s 1.20 (Rancher, EKS, AKS), K8s 1.21 (GKE and kOps).","title":"Redis Enterprise for Kubernetes Release Notes 6.2.4-1 (September 2021)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-2-4-1-2021-09/"},{"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-0-20-12","summary":"Support for RS 6.0.20-97, EKS, Hashicorp Vault, and added feature support for OpenShift OLM.","title":"Redis Enterprise for Kubernetes Release Notes 6.0.20-12 (July 2021)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-0-20-12/"},{"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-0-20-4","summary":"Support for 6.0.20-69, OpenShift 4.7, and K8s 1.20. Hashicorp Vault integration with REC and REDB secrets.","title":"Redis Enterprise for Kubernetes Release Notes 6.0.20-4 (May 2021)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-0-20-4/"},{"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-0-12-5","summary":"Support for RS 6.0.12-57, Amazon Kubernetes Service (AKS), and role permissions on custom resources.","title":"Redis Enterprise for Kubernetes Release Notes 6.0.12-5 (February 2021)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-0-12-5/"},{"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-0-8-20","summary":"Support for RS 6.0.8, consumer namespaces, Gesher admission controller proxy, and custom resource validation via schema.","title":"Redis Enterprise for Kubernetes Release Notes 6.0.8-20 (December 2020)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-0-8-20/"},{"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-0-8-1","summary":"Support for RS 6.0.8-28, OpenShift 4.5, K8s 1.18, and Gesher admission controller proxy.","title":"Redis Enterprise for Kubernetes Release Notes 6.0.8-1 (October 2020)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-0-8-1/"},{"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-0-6-24","summary":"Various bug fixes.","title":"Redis Enterprise for Kubernetes Release Notes 6.0.6-24 (August 2020)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-0-6-24/"},{"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-0-6-23","summary":"Support for Redis Enterprise Software 6.0.6-39, Rancher support, new database backup and alert options.","title":"Redis Enterprise for Kubernetes Release Notes 6.0.6-23 (August 2020)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-0-6-23/"},{"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-0-6-11","summary":"Maintenance release; support RS 6.0.6-39 and various bug fixes.","title":"Redis Enterprise for Kubernetes Release Notes 6.0.6-11 (July 2020)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-0-6-11/"},{"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-0-6-6","summary":"Support for RS 6.0.6, new database and admission controllers, various improvements and bug fixes.","title":"Redis Enterprise for Kubernetes Release Notes 6.0.6-6 (June 2020)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-0-6-6/"},{"id":"operate/kubernetes/release-notes/previous-releases/k8s-5-4-14-2","summary":"Support for Redis Enterprise Software 5.4.14, K8s 1.16, and OpenShift 4.3.","title":"Redis Enterprise for Kubernetes Release Notes 5.4.14-2 (March 2020)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-5-4-14-2/"},{"id":"operate/kubernetes/release-notes/previous-releases/k8s-5-4-10-8","summary":"Support for the Redis Enterprise Software 5.4.10 and multiple enhancements.","title":"Redis Enterprise for Kubernetes Release Notes 5.4.10-8 (January 2020)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-5-4-10-8/"}],"page_type":"content","content_hash":"2a98d6c94ca707b3cb974d33f383fbd247fa6f7749c01cfd0c33d8fe80cbc82e","sections":[{"id":"content","title":"Content","role":"content","text":"Below are archived release notes for Redis Enterprise for Kubernetes versions released more than 18 months ago.\n\n\n| Version (Release date) | Major changes |\n| --- | --- |\n| [6.4.2-8 (Oct 2024)](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/6-4-2-8-oct24/) | The Redis Enterprise K8s 6.4.2-8 release supports Redis Enterprise Software 6.4.2. |\n| [6.4.2-8 (Dec 2023)](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/6-4-2-8-dec23/) | The Redis Enterprise K8s 6.4.2-8 release supports Redis Enterprise Software 6.4.2 and contains new features and feature improvements. |\n| [6.4.2-8 (July 2023)](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/6-4-2-8/) | The Redis Enterprise K8s 6.4.2-8 release supports Redis Enterprise Software 6.4.2 and contains new features and feature improvements. |\n| [6.4.2-6 (June 2023)](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/6-4-2-6/) | The Redis Enterprise K8s 6.4.2-6 release supports Redis Enterprise Software 6.4.2 and contains new features and feature improvements. |\n| [6.4.2-5 (April 2023)](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/6-4-2-5/) | The Redis Enterprise K8s 6.4.2-5 is a maintenance release for version 6.4.2-4. |\n| [6.4.2-4 (March 2023)](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-4-2-4/) | The Redis Enterprise K8s 6.4.2-4 release supports Redis Enterprise Software 6.4.2 and contains new features and feature improvements. |\n| [6.2.18-41 (Jan 2023)](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-2-18-41-jan-2023/) | This is a maintenance release for 6.2.18 and includes support for Redis Enterprise 6.2.18-72. |\n| [6.2.18-41 (Dec 2022)](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-2-18-41/) | This is a maintenance release for 6.2.18 and includes bug fixes. |\n| [6.2.12-1 (Sept 2022)](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-2-12-1/) | Support added for additional distributions, as well as feature improvements and bug fixes. |\n| [6.2.18-3 (Nov 2022)](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-2-18-3/) | Support added for additional distributions, as well as feature improvements and bug fixes. |\n| [6.2.10-45 (July 2022)](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-2-10-45-2022-07/) | Support added for additional distributions as well as some feature improvements. |\n| [6.2.10-34 (May 2022)](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-2-10-34-2022-05/) | Support for REBD specific upgrade policies, memcached type REDBs, and RHEL8 for OpenShift, as well as feature improvements and bug fixes. |\n| [6.2.10-4 (March 2022)](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-2-10-4-2022-03/) | Added support for RS 6.2.10 as well as feature improvements and bug fixes. |\n| [6.2.8-15 (January 2022)](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-2-8-15-2022-01/) | Maintenance release with bug fixes |\n| [6.2.8-11 (January 2022)](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-2-8-11-2022-01/) | Support for Istio as ingress controller, K8s 1.22 (AKS, kOps, GKE), OpenShift 4.9 |\n| [6.2.8-2 (November 2021)](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-2-8-2-2021-11/) | Support for RS 6.2.8, certificate management, and Redis upgrade policy. |\n| [6.2.4-1 (September 2021)](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-2-4-1-2021-09/) | Support for RS 6.2.4 and OpenShift 4.8. Support for K8s 1.19 (EKS and AKS), K8s 1.20 (Rancher, EKS, AKS), K8s 1.21 (GKE and kOps). |\n| [6.0.20-12 (July 2021)](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-0-20-12/) | Support for RS 6.0.20-97, EKS, Hashicorp Vault, and added feature support for OpenShift OLM. |\n| [6.0.20-4 (May 2021)](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-0-20-4/) | Support for 6.0.20-69, OpenShift 4.7, and K8s 1.20. Hashicorp Vault integration with REC and REDB secrets. |\n| [6.0.12-5 (February 2021)](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-0-12-5/) | Support for RS 6.0.12-57, Amazon Kubernetes Service (AKS), and role permissions on custom resources. |\n| [6.0.8-20 (December 2020)](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-0-8-20/) | Support for RS 6.0.8, consumer namespaces, Gesher admission controller proxy, and custom resource validation via schema. |\n| [6.0.8-1 (October 2020)](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-0-8-1/) | Support for RS 6.0.8-28, OpenShift 4.5, K8s 1.18, and Gesher admission controller proxy. |\n| [6.0.6-24 (August 2020)](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-0-6-24/) | Various bug fixes. |\n| [6.0.6-23 (August 2020)](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-0-6-23/) | Support for Redis Enterprise Software 6.0.6-39, Rancher support, new database backup and alert options. |\n| [6.0.6-11 (July 2020)](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-0-6-11/) | Maintenance release; support RS 6.0.6-39 and various bug fixes. |\n| [6.0.6-6 (June 2020)](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-0-6-6/) | Support for RS 6.0.6, new database and admission controllers, various improvements and bug fixes. |\n| [5.4.14-2 (March 2020)](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-5-4-14-2/) | Support for Redis Enterprise Software 5.4.14, K8s 1.16, and OpenShift 4.3. |\n| [5.4.10-8 (January 2020)](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-5-4-10-8/) | Support for the Redis Enterprise Software 5.4.10 and multiple enhancements. |"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/previous-releases/k8s-5-4-10-8","title":"Redis Enterprise for Kubernetes Release Notes 5.4.10-8 (January 2020)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-5-4-10-8/","summary":"Support for the Redis Enterprise Software 5.4.10 and multiple enhancements.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"c1a6004a162e48c6ea384181461ea37788e4f7d856c326638ef1c0899920244e","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"[Redis Enterprise for Kubernetes 5.4.10-8](https://github.com/RedisLabs/redis-enterprise-k8s-docs/releases/tag/5.4.10-8) is now available.\nThis release includes Redis Enterprise (RS) version [5.4.10-22](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-10-december-2019) and introduces new feature and bug fixes."},{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release providing support for the Redis Enterprise Software release 5.4.10 and includes multiple enhancements.\n\nFollow these [instructions](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster) for upgrading to this Kubernetes operator release."},{"id":"new-features","title":"New features","role":"content","text":"- All-in-one deployment bundle and documentation enhancements - This release is the easiest to deploy yet, with a new quick start guide and an all-in-one file bundle for deploying the Redis Enterprise Operator. GitHub documentation was enhanced to cover advanced deployment scenarios with the complete reference to the custom resource specifications, guides, and examples.\n\n    > [!NOTE]\n    > Pay special attention to the yaml file naming changes and new yaml files that have been created for this release. These are highlighted in the quick start guide.\n\n- Rack Awareness - Support for the [Redis Software Rack Awareness](https://redis.io/docs/latest/operate/rs/clusters/configure/rack-zone-awareness) feature was introduced to the Kubernetes deployment. It enables deploying nodes to different zones, in a multi-zone Kubernetes cluster. Databases that are rack-aware will have the cluster populate their master shard and replica shards in different nodes, across different zones or failure domains. This enables maintaining data persistence in case of zone failure.\n\n- OLM Support - Redis Enterprise for Kubernetes is now RedHat OpenShift certified and can be effortlessly configured and deployed in Kubernetes clusters supporting OLM, including OpenShift 4.x clusters, with just a few clicks. OLM based deployments do not require Kubernetes cluster administrator rights to deploy the operator.\n\n- Improve cluster nodes' pod scheduling resiliency - Redis Enterprise Cluster pod scheduling is hardened by implementing Kubernetes best practices and providing configuration recommendations to cluster operators. Scheduling resiliency minimizes the chance of cluster node pods eviction or failure to schedule.\nSee the top 4 articles in the new [Additonal Topics](https://github.com/RedisLabs/redis-enterprise-k8s-docs/blob/92a2eff4b8c4ccafac459138f12e5f38acde825c/docs/topics.md#additional-topics) documentation section.\n\n- Update app.redislabs.com API version to stable - We've updated the Redis Enterprise Cluster custom resource API from [alpha to stable](https://kubernetes.io/docs/concepts/overview/kubernetes-api/#api-versioning) to reflect the current state of maturity of our implementation.\n\n[Both versions of the API are supported](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definition-versioning/) by Kubernetes versions that support specifying multiple API versions.\n\nFor legacy Kubernetes versions, deployment files are available in the documentation repository that utilize the alpha version of the API."},{"id":"information","title":"Information","role":"content","text":"- This new release deprecates the Redis Enterprise Operator support for legacy Kubernetes version 1.8."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"- Custom Resource Definition Changes - In order to comply with OLM best practices, a change was required to the Redis Enterprise Cluster Custom Resource Defintion (CRD) to introduce the status sub-resource.\nAs a result of this change, any upgrade from an older release to this release requires a change to the CRD. Once the CRD is updated, all Redis Enterprise Clusters in the same K8s cluster must be updated to the latest release in order to continue operating properly.\n\n- Cluster Recovery -"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/previous-releases/k8s-5-4-14-2","title":"Redis Enterprise for Kubernetes Release Notes 5.4.14-2 (March 2020)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-5-4-14-2/","summary":"Support for Redis Enterprise Software 5.4.14, K8s 1.16, and OpenShift 4.3.","tags":["docs","operate","kubernetes"],"last_updated":"2023-12-14T15:27:49+01:00","page_type":"content","content_hash":"79cd334896b5a640d1c32ff1d3b4553a52f58abbb46526e8cf68d84a50cea1e4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The Redis Enterprise K8s 5.4.14-2 release is a maintenance release providing support for the latest Redis Enterprise Software release 5.4.14 and includes bug fixes as well as the following notable changes:"},{"id":"support-for-k8s-116","title":"Support for K8s 1.16","role":"compatibility","text":"This release now correctly handles the API deprecations, and API version changes that K8s release 1.16 introduced."},{"id":"support-for-openshift-43-improved-olm-support","title":"Support for OpenShift 4.3; improved OLM support","role":"compatibility","text":"The release now supports OpenShift 4.3. A new operator version is now available in OpenShift's Operator Hub and now includes a more comprehensive base template as well as references to documentation and support."},{"id":"changes-to-the-upgrade-process","title":"Changes to the upgrade process","role":"content","text":"When the Operator is upgraded to a new release, it now prevents the Redis Enterprise Cluster nodes from being automatically upgraded, unless autoUpgrade is enabled or the RS image version is explicitly updated in the Redis Enterprise Cluster (REC) spec. The change was introduced to avoid situations where an Operator upgrade initiated a rolling update to the cluster nodes' StatefulSet."},{"id":"deprecated-support-for-k8s-versions-19110","title":"Deprecated support for K8s versions 1.9/1.10","role":"content","text":"This release deprecates support for K8s version 1.9/1.10 and OpenShift 3.9. If you are currently using these releases please contact Redis support for information about migrating to a new K8s release."},{"id":"coming-soon","title":"Coming soon","role":"content","text":"- The Database Custom Resource(CR), which represents Redis Enterprise databases, is in development and planned to be part of an upcoming release. The Database Controller for the Database CR is part of the Redis Enterprise K8s Operator. It is disabled in this release.\n- The next release is planned to support K8s 1.17 and drop support for K8s versions 1.9/1.10."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-0-12-5","title":"Redis Enterprise for Kubernetes Release Notes 6.0.12-5 (February 2021)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-0-12-5/","summary":"Support for RS 6.0.12-57, Amazon Kubernetes Service (AKS), and role permissions on custom resources.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"f2a1b64d8296449100c19d9042895543439969054e51c8a6f69fa3bed2c404f9","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"The Redis Enterprise [K8s 6.0.12-5](https://github.com/RedisLabs/redis-enterprise-k8s-docs/releases/tag/v6.0.12-5) release is a *major release* on top of [6.0.8-20](https://github.com/RedisLabs/redis-enterprise-k8s-docs/releases/tag/v6.0.8-20) providing support for the [Redis Enterprise Software release 6.0.12-57](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-0-12-january-2021/) and includes several enhancements and bug fixes."},{"id":"overview","title":"Overview","role":"overview","text":"This release of the operator provides:\n\n * New features\n * Various bug fixes\n\n\nTo upgrade your deployment to this latest release, see [\"Upgrade a Redis Enterprise cluster (REC) on Kubernetes\"](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster)."},{"id":"images","title":"Images","role":"content","text":"This release includes the following container images:\n * **Redis Enterprise**: redislabs/redis:6.0.12-57 or redislabs/redis:6.0.12-57.rhel7-openshift\n * **Operator and Bootstrapper**: redislabs/operator:6.0.12-5\n * **Services Rigger**: redislabs/k8s-controller:6.0.12-5 or redislabs/services-manager:6.0.12-5 (Red Hat registry)"},{"id":"new-features","title":"New features","role":"content","text":"* [Azure Kubernetes Service](https://azure.microsoft.com/en-us/services/kubernetes-service/) (AKS) is now supported. (RED40323)\n * Database custom resources now support roles permissions (role to Redis ACL bindings). (RED49780)"},{"id":"feature-improvements","title":"Feature Improvements","role":"content","text":"* The license information has been added to the REC status. (RED43078)\n * `kubectl get rec` now displays more information (i.e., added \"NODES\", \"VERSION\", \"STATE\", \"SPEC STATUS\"). (RED46428)\n * The `extraLabels` in the cluster spec now applies to the PV and PVC at creation. (RED48694)\n * Added [Rancher 2.5](https://rancher.com/products/rancher/2.5) support. (RED50211)\n * Added K8s (Kops) 1.19 support. (RED50211)\n * Added [OpenShift 4.6](https://docs.openshift.com/container-platform/4.6/welcome/index.html) support. (RED50495)\n * Database resources can now disable the default database user. (RED50215)\n * REC credentials no longer use environment variables in pods. (RED47969)"},{"id":"important-fixes","title":"Important fixes","role":"content","text":"* Fixed an issue where pods are stuck terminating during teardown. (RED44726)\n * Fixed an issue where pods are stuck terminating during cluster recovery. (RED43846)\n * Changed the operator logs to use human-readable dates and times. (RED39026)\n * Fixed the display of resources in the OLM (OpenShift). (RED48116)\n * Fixed backup configuration issue for GCS without a subdir. (RED49299)"},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"crashloopbackoff-causes-cluster-recovery-to-be-incomplete--red33713","title":"CrashLoopBackOff causes cluster recovery to be incomplete  (RED33713)","role":"content","text":"When a pod's status is `CrashLoopBackOff` and we run the cluster recovery, the process will not complete. The workaround is to delete the crashing pods manually. The recovery process will then continue."},{"id":"long-cluster-names-cause-routes-to-be-rejected--red25871","title":"Long cluster names cause routes to be rejected  (RED25871)","role":"content","text":"A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name will exceed 63 characters. The workaround is to limit cluster name to 20 characters or fewer."},{"id":"cluster-cr-rec-errors-are-not-reported-after-invalid-updates-red25542","title":"Cluster CR (REC) errors are not reported after invalid updates (RED25542)","role":"content","text":"A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence."},{"id":"an-unreachable-cluster-has-status-running-red32805","title":"An unreachable cluster has status running (RED32805)","role":"content","text":"When a cluster is in an unreachable state, the state is still `running` instead of being reported as an error."},{"id":"readiness-probe-incorrect-on-failures-red39300","title":"Readiness probe incorrect on failures (RED39300)","role":"content","text":"STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on the node fails."},{"id":"role-missing-on-replica-sets-red39002","title":"Role missing on replica sets (RED39002)","role":"content","text":"The `redis-enterprise-operator` role is missing permission on replica sets."},{"id":"private-registries-are-not-supported-on-openshift-311-red38579","title":"Private registries are not supported on OpenShift 3.11 (RED38579)","role":"content","text":"Openshift 3.11 does not support DockerHub private registries. This is a known OpenShift issue."},{"id":"internal-dns-and-kubernetes-dns-may-have-conflicts-red37462","title":"Internal DNS and Kubernetes DNS may have conflicts (RED37462)","role":"content","text":"DNS conflicts are possible between the cluster mdns_server and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names."},{"id":"5410-negatively-impacts-546-red37233","title":"5.4.10 negatively impacts 5.4.6 (RED37233)","role":"content","text":"Kubernetes-based 5.4.10 deployments seem to negatively impact existing 5.4.6 deployments that share a Kubernetes cluster."},{"id":"node-cpu-usage-is-reported-instead-of-pod-cpu-usage-red36884","title":"Node CPU usage is reported instead of pod CPU usage (RED36884)","role":"content","text":"In Kubernetes, the node CPU usage we report on is the usage of the Kubernetes worker node hosting the REC pod."},{"id":"clusters-must-be-named-rec-in-olm-based-deployments-red39825","title":"Clusters must be named \"rec\" in OLM-based deployments (RED39825)","role":"content","text":"In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) is bound to a specific service account name (i.e., \"rec\"). The workaround is to name the cluster \"rec\"."},{"id":"master-pod-label-in-rancher-red42896","title":"Master pod label in Rancher (RED42896)","role":"content","text":"The master pod is not always labeled in Rancher."},{"id":"rec-clusters-fail-to-start-on-kubernetes-clusters-with-unsynchronized-clocks-red47254","title":"REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED47254)","role":"content","text":"When REC clusters are deployed on Kubernetes clusters with unsynchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes."},{"id":"deleting-an-openshift-project-with-an-rec-deployed-may-hang-red47192","title":"Deleting an OpenShift project with an REC deployed may hang (RED47192)","role":"content","text":"When a REC cluster is deployed in a project (namespace) and has REDB resources, the\nREDB resources must be deleted first before the REC can be deleted. As such, until the\nREDB resources are deleted, the project deletion will hang. The fix is to delete the\nREDB resources first and the REC second. Afterwards, you may delete the project."},{"id":"rec-extralabels-are-not-applied-to-pvcs-on-k8s-versions-115-or-older-red51921","title":"REC extraLabels are not applied to PVCs on K8s versions 1.15 or older (RED51921)","role":"content","text":"In K8s 1.15 or older, the PVC labels come from the match selectors and not the\nPVC templates. As such, these versions can not support PVC labels. If this feature\nis required, the only fix is to upgrade the K8s cluster to a newer version."},{"id":"compatibility-notes","title":"Compatibility Notes","role":"compatibility","text":"* OpenShift 4.6 and Rancher/kOps 1.19 are now supported.\n * Rancher 2.5 is now supported.\n * AKS (K8s 1.18) is now supported."},{"id":"deprecation-notice","title":"Deprecation notice","role":"content","text":"* OpenShift 4.1, 4.2, and 4.3 are now deprecated.\n * GKE K8s version 1.14 is deprecated.\n * kOps 1.13 and 1.14 are deprecated."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-0-20-12","title":"Redis Enterprise for Kubernetes Release Notes 6.0.20-12 (July 2021)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-0-20-12/","summary":"Support for RS 6.0.20-97, EKS, Hashicorp Vault, and added feature support for OpenShift OLM.","tags":["docs","operate","kubernetes"],"last_updated":"2026-07-07T14:30:36+01:00","page_type":"content","content_hash":"e9854a9e07f6f18ccf6574ee884bfe620b3ffc0737e77ce007d62a14da048734","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The Redis Enterprise [K8s 6.0.20-12](https://github.com/RedisLabs/redis-enterprise-k8s-docs/releases/tag/v6.0.20-12) release is a *major release* on top of [6.0.20-4](https://github.com/RedisLabs/redis-enterprise-k8s-docs/releases/tag/v6.0.20-4) providing support for the [Redis Enterprise Software release 6.0.20-97](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-0-20-april-2021/) and includes several enhancements and bug fixes.\n\nThis release of the operator provides:\n\n* New features\n* Various bug fixes"},{"id":"images","title":"Images","role":"content","text":"This release includes the following container images: \n\n* **Redis Enterprise**: `redislabs/redis:6.0.20-97` or  redislabs/redis:6.0.20-97.rhel7-openshift`\n* **Operator and Bootstrapper**: `redislabs/operator:6.0.20-12`\n* **Services Rigger**: `redislabs/k8s-controller:6.0.20-12` or `redislabs/services-manager:6.0.20-12` (on the Red Hat registry)"},{"id":"new-features","title":"New features","role":"content","text":"* EKS support\n* Feature parity for Openshift OLM; added support for:\n   - REDB\n   - admission\n   - Hashicorp Vault integration\n* Hashicorp Vault integration is now GA\n* Moved Operator environment variables to configmap"},{"id":"important-fixes","title":"Important fixes","role":"content","text":"* Admission controller deployment combined with operator deployment to simplify deployment (RED-52701)\n* Additional verbosity in the `kubectl get redb` command output (RED-55042)\n* Support for Redis Enterprise license storage within secrets; Vault and K8s secrets (RED-55587)\n* Support for manual procedure to replace the REC credentials (RED-56529, RED-56530)\n* Fixed cluster recovery issue - recovery process was not starting (RED-55500)\n* When a pod status is CrashLoopBackOff and we run the cluster recovery, the process will not complete (RED-33713)\n* Master pod is not always labeled in Rancher (fixed in 6.0.8 but was not documented) (RED-42896)"},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"hashicorp-vault-integration---no-support-for-gesher-red-55080","title":"Hashicorp Vault integration - no support for Gesher (RED-55080)","role":"content","text":"There is no workaround at this time"},{"id":"nodes-down-indefinitely-after-the-redis-enterprise-node-container-of-a-rec-pod-is-restarted-53042","title":"Nodes down indefinitely after the redis-enterprise-node container of a REC pod is restarted (53042)","role":"content","text":"In some cases where the Redis Enterprise Cluster container in the Redis Enterprise Cluster(REC) pod is restarted, the REC node remains down. Workaround: restart the pod, while ensuring the majority of REC nodes are available."},{"id":"crashloopbackoff-causes-cluster-recovery-to-be-incomplete--red33713","title":"CrashLoopBackOff causes cluster recovery to be incomplete  (RED33713)","role":"content","text":"When a pod's status is `CrashLoopBackOff` and we run the cluster recovery, the process will not complete. The workaround is to delete the crashing pods manually. The recovery process will then continue."},{"id":"long-cluster-names-cause-routes-to-be-rejected--red25871","title":"Long cluster names cause routes to be rejected  (RED25871)","role":"content","text":"A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name will exceed 63 characters. The workaround is to limit cluster name to 20 characters or fewer."},{"id":"cluster-cr-rec-errors-are-not-reported-after-invalid-updates-red25542","title":"Cluster CR (REC) errors are not reported after invalid updates (RED25542)","role":"content","text":"A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence."},{"id":"an-unreachable-cluster-has-status-running-red32805","title":"An unreachable cluster has status running (RED32805)","role":"content","text":"When a cluster is in an unreachable state, the state is still `running` instead of being reported as an error."},{"id":"readiness-probe-incorrect-on-failures-red39300","title":"Readiness probe incorrect on failures (RED39300)","role":"content","text":"STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on the node fails."},{"id":"role-missing-on-replica-sets-red39002","title":"Role missing on replica sets (RED39002)","role":"content","text":"The `redis-enterprise-operator` role is missing permission on replica sets."},{"id":"private-registries-are-not-supported-on-openshift-311-red38579","title":"Private registries are not supported on OpenShift 3.11 (RED38579)","role":"content","text":"Openshift 3.11 does not support DockerHub private registries. This is a known OpenShift issue."},{"id":"internal-dns-and-kubernetes-dns-may-have-conflicts-red37462","title":"Internal DNS and Kubernetes DNS may have conflicts (RED37462)","role":"content","text":"DNS conflicts are possible between the cluster mdns_server and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names."},{"id":"5410-negatively-impacts-546-red37233","title":"5.4.10 negatively impacts 5.4.6 (RED37233)","role":"content","text":"Kubernetes-based 5.4.10 deployments seem to negatively impact existing 5.4.6 deployments that share a Kubernetes cluster."},{"id":"node-cpu-usage-is-reported-instead-of-pod-cpu-usage-red36884","title":"Node CPU usage is reported instead of pod CPU usage (RED36884)","role":"content","text":"In Kubernetes, the node CPU usage we report on is the usage of the Kubernetes worker node hosting the REC pod."},{"id":"clusters-must-be-named-rec-in-olm-based-deployments-red39825","title":"Clusters must be named \"rec\" in OLM-based deployments (RED39825)","role":"content","text":"In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) is bound to a specific service account name (i.e., \"rec\"). The workaround is to name the cluster \"rec\"."},{"id":"master-pod-label-in-rancher-red42896","title":"Master pod label in Rancher (RED42896)","role":"content","text":"The master pod is not always labeled in Rancher."},{"id":"rec-clusters-fail-to-start-on-kubernetes-clusters-with-unsynchronized-clocks-red47254","title":"REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED47254)","role":"content","text":"When REC clusters are deployed on Kubernetes clusters with unsynchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes."},{"id":"deleting-an-openshift-project-with-an-rec-deployed-may-hang-red47192","title":"Deleting an OpenShift project with an REC deployed may hang (RED47192)","role":"content","text":"When a REC cluster is deployed in a project (namespace) and has REDB resources, the\nREDB resources must be deleted first before the REC can be deleted. As such, until the\nREDB resources are deleted, the project deletion will hang. The fix is to delete the\nREDB resources first and the REC second. Afterwards, you may delete the project."},{"id":"rec-extralabels-are-not-applied-to-pvcs-on-k8s-versions-115-or-older-red51921","title":"REC extraLabels are not applied to PVCs on K8s versions 1.15 or older (RED51921)","role":"content","text":"In K8s 1.15 or older, the PVC labels come from the match selectors and not the\nPVC templates. As such, these versions can not support PVC labels. If this feature\nis required, the only fix is to upgrade the K8s cluster to a newer version."},{"id":"compatibility-notes","title":"Compatibility Notes","role":"compatibility","text":"* EKS is now supported (K8s 1.18)\n* OpenShift 4.4 (previously deprecated) is no longer supported\n* GKE K8s versions 1.15, 1.16 (previously deprecated) is no longer supported\n* VMWare TKGI 1.10 (K8s 1.19) is now supported"},{"id":"deprecation-notice","title":"Deprecation notice","role":"content","text":"* GKE K8s version 1.17 (no longer supported by Google) is deprecated\n* kOps 1.15 is deprecated\n* VMWare TKGI 1.7 (K8s 1.16), VMWare TKGI 1.8 (K8s 1.17) are deprecated (no longer supported by VMWare)"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-0-20-4","title":"Redis Enterprise for Kubernetes Release Notes 6.0.20-4 (May 2021)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-0-20-4/","summary":"Support for 6.0.20-69, OpenShift 4.7, and K8s 1.20. Hashicorp Vault integration with REC and REDB secrets.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"167c0d95f37c18358578ea429daadc71f71a149c341080a7e4fd5d55d8ad696f","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"The Redis Enterprise K8s [6.0.20-4](https://github.com/RedisLabs/redis-enterprise-k8s-docs/releases/tag/v6.0.20-4) release is a *major release* on top of [6.0.8-20](https://github.com/RedisLabs/redis-enterprise-k8s-docs/releases/tag/v6.0.8-20) providing support for the [Redis Enterprise Software release 6.0.20-69](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-0-20-april-2021) and includes several enhancements and bug fixes."},{"id":"overview","title":"Overview","role":"overview","text":"This release of the operator provides:\n * New features\n * Various bug fixes\n\nTo upgrade your deployment to this latest release, see [\"Upgrade a Redis Enterprise cluster (REC) on Kubernetes\"](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster)."},{"id":"images","title":"Images","role":"content","text":"This release includes the following container images:\n * **Redis Enterprise**: redislabs/redis:6.0.20-69 or redislabs/redis:6.0.20-69.rhel7-openshift\n * **Operator and Bootstrapper**: redislabs/operator:6.0.20-4\n * **Services Rigger**: redislabs/k8s-controller:6.0.20-4 or redislabs/services-manager:6.0.20-4 (on the Red Hat registry)"},{"id":"new-features","title":"New features","role":"content","text":"* Support for Openshift 4.7\n * Support for Kubernetes 1.20\n\n ## New preview features\n * Hashicorp Vault integration - REC secret\n * Hashicorp Vault integration - REDB secrets"},{"id":"important-fixes","title":"Important fixes","role":"content","text":"- Fixed upgrade issue with custom container repositories specifying port numbers (RED-53192)\n- REDB controller no longer performs reconciliation until Redis Enterprise software version complies with operator (RED-53194)\n- Removed unused node.js package from Services Rigger image (RED-53536)\n- Fixed operator crash on change of uiServiceType (RED-54621)\n- Avoid excessive logging within RS pod (envoy_access.log) (RED-55525)"},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"openshift-47---v6020-4-cannot-be-deployed-by-olm","title":"OpenShift 4.7 - v6.0.20-4 cannot be deployed by OLM","role":"content","text":"6.0.20-4 does not appear in OLM. Workaround - deploy manually. A future maintenance release will address this."},{"id":"hashicorp-vault-integration---no-support-for-gesher-red-55080","title":"Hashicorp Vault integration - no support for Gesher (RED-55080)","role":"content","text":"There is no workaround at this time"},{"id":"nodes-down-indefinitely-after-the-redis-enterprise-node-container-of-a-rec-pod-is-restarted-53042","title":"Nodes down indefinitely after the redis-enterprise-node container of a REC pod is restarted (53042)","role":"content","text":"In some cases where the Redis Enterprise Cluster container in the Redis Enterprise Cluster(REC) pod is restarted, the REC node remains down. Workaround: restart the pod, while ensuring the majority of REC nodes are available."},{"id":"crashloopbackoff-causes-cluster-recovery-to-be-incomplete--red33713","title":"CrashLoopBackOff causes cluster recovery to be incomplete  (RED33713)","role":"content","text":"When a pod's status is `CrashLoopBackOff` and we run the cluster recovery, the process will not complete. The workaround is to delete the crashing pods manually. The recovery process will then continue."},{"id":"long-cluster-names-cause-routes-to-be-rejected--red25871","title":"Long cluster names cause routes to be rejected  (RED25871)","role":"content","text":"A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name will exceed 63 characters. The workaround is to limit cluster name to 20 characters or fewer."},{"id":"cluster-cr-rec-errors-are-not-reported-after-invalid-updates-red25542","title":"Cluster CR (REC) errors are not reported after invalid updates (RED25542)","role":"content","text":"A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence."},{"id":"an-unreachable-cluster-has-status-running-red32805","title":"An unreachable cluster has status running (RED32805)","role":"content","text":"When a cluster is in an unreachable state, the state is still `running` instead of being reported as an error."},{"id":"readiness-probe-incorrect-on-failures-red39300","title":"Readiness probe incorrect on failures (RED39300)","role":"content","text":"STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on the node fails."},{"id":"role-missing-on-replica-sets-red39002","title":"Role missing on replica sets (RED39002)","role":"content","text":"The `redis-enterprise-operator` role is missing permission on replica sets."},{"id":"private-registries-are-not-supported-on-openshift-311-red38579","title":"Private registries are not supported on OpenShift 3.11 (RED38579)","role":"content","text":"Openshift 3.11 does not support DockerHub private registries. This is a known OpenShift issue."},{"id":"internal-dns-and-kubernetes-dns-may-have-conflicts-red37462","title":"Internal DNS and Kubernetes DNS may have conflicts (RED37462)","role":"content","text":"DNS conflicts are possible between the cluster mdns_server and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names."},{"id":"5410-negatively-impacts-546-red37233","title":"5.4.10 negatively impacts 5.4.6 (RED37233)","role":"content","text":"Kubernetes-based 5.4.10 deployments seem to negatively impact existing 5.4.6 deployments that share a Kubernetes cluster."},{"id":"node-cpu-usage-is-reported-instead-of-pod-cpu-usage-red36884","title":"Node CPU usage is reported instead of pod CPU usage (RED36884)","role":"content","text":"In Kubernetes, the node CPU usage we report on is the usage of the Kubernetes worker node hosting the REC pod."},{"id":"clusters-must-be-named-rec-in-olm-based-deployments-red39825","title":"Clusters must be named \"rec\" in OLM-based deployments (RED39825)","role":"content","text":"In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) is bound to a specific service account name (i.e., \"rec\"). The workaround is to name the cluster \"rec\"."},{"id":"master-pod-label-in-rancher-red42896","title":"Master pod label in Rancher (RED42896)","role":"content","text":"The master pod is not always labeled in Rancher."},{"id":"rec-clusters-fail-to-start-on-kubernetes-clusters-with-unsynchronized-clocks-red47254","title":"REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED47254)","role":"content","text":"When REC clusters are deployed on Kubernetes clusters with unsynchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes."},{"id":"deleting-an-openshift-project-with-an-rec-deployed-may-hang-red47192","title":"Deleting an OpenShift project with an REC deployed may hang (RED47192)","role":"content","text":"When a REC cluster is deployed in a project (namespace) and has REDB resources, the\nREDB resources must be deleted first before the REC can be deleted. As such, until the\nREDB resources are deleted, the project deletion will hang. The fix is to delete the\nREDB resources first and the REC second. Afterwards, you may delete the project."},{"id":"rec-extralabels-are-not-applied-to-pvcs-on-k8s-versions-115-or-older-red51921","title":"REC extraLabels are not applied to PVCs on K8s versions 1.15 or older (RED51921)","role":"content","text":"In K8s 1.15 or older, the PVC labels come from the match selectors and not the\nPVC templates. As such, these versions can not support PVC labels. If this feature\nis required, the only fix is to upgrade the K8s cluster to a newer version."},{"id":"compatability-notes","title":"Compatability Notes","role":"content","text":"- OpenShift 4.7 and Rancher/kOps 1.20 are now supported\n- OpenShift 4.1, 4.2, 4.3 (previously deprecated) are no longer supported\n- GKE K8s version 1.14 (previously deprecated) is no longer supported\n- kOps (upstream K8s) 1.13, 1.14 (previously deprecated) are no longer supported"},{"id":"deprecation-notice","title":"Deprecation notice","role":"content","text":"- OpenShift 4.4 (no longer supported by Red Hat) is deprecated\n- GKE K8s versions 1.15, 1.16 (no longer supported by Google) are deprecated\n- kOps (upstream K8s) 1.15 is deprecated"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-0-6-11","title":"Redis Enterprise for Kubernetes Release Notes 6.0.6-11 (July 2020)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-0-6-11/","summary":"Maintenance release; support RS 6.0.6-39 and various bug fixes.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"13fcd8862734b70212c9aadefb5dcfc08328103c79a44831cc567533add4543f","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"The Redis Enterprise K8s 6.0.6-11 release is a *maintenance release* on top of [6.0.6-6](https://github.com/RedisLabs/redis-enterprise-k8s-docs/releases/tag/v6.0.6-6) providing support for the latest [Redis Enterprise Software release 6.0.6-39](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-0-may-2020/) and includes several bug fixes."},{"id":"overview","title":"Overview","role":"overview","text":"This release of the operator provides:\n\n- Support for the Redis Enterprise Software release 6.0.6-39\n- Various bug fixes\n\nTo upgrade your deployment to this latest release, see [\"Upgrade a Redis Enterprise cluster (REC) on Kubernetes\"](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster)."},{"id":"images","title":"Images","role":"content","text":"- **Redis Enterprise** - redislabs/redis:6.0.6-39 or redislabs/redis:6.0.6-39.rhel7-openshift\n- **Operator** - redislabs/operator:6.0.6-11 or redislabs/operator:6.0.6-11.rhel7\n- **Services Rigger** - redislabs/k8s-controller:6.0.6-11 or redislabs/k8s-controller:6.0.6-11.rhel7"},{"id":"important-fixes","title":"Important fixes","role":"content","text":"- The upgrade process may have failed in certain situations (i.e., with CRDB databases). We now ensure that rlutil runs at bootstrap to complete the upgrade process. (RED43635)\n- The example and default custom resource for the REC in the OLM now correctly uses 'nodes' (RED43847)\n- Fixes for security vulnerabilities in the server rigger image: upgraded to httpd 2.4.42, Kubernetes Python Client 8.0.1, and removed localhost private key. (RED42495)\n- A fix for an internal logging issue that caused errors to incorrectly show up on the operator log when databases are created even though the creation succeeded. (RED43336)"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"- CrashLoopBackOff causes cluster recovery to be incomplete  (RED33713) - When a pod status is CrashLoopBackOff and we run the cluster recovery, the process will not complete. The solution is to delete the crashing pods manually and recovery process will continue.\n\n- Long cluster names cause routes to be rejected  (RED25871) - A cluster name longer than 20 characters will result in a rejected route configuration as the host part of the domain name exceeds 63 characters. The workaround is to limit cluster name to 20 characters or less.\n\n- No cleanup of database services on failures (RED25825) - The service broker doesn't clean up database service bindings when there are failures. The workaround is to manually remove service bindings.\n\n- Server broker errors with two service naming schemes  (RED25547) - The service broker deployment results in an error when two types of service naming schemes are set. You must choose one of the methods if both are set - redis-port is the recommended default.\n\n- Cluster CR (REC) errors are not reported after invalid updates (RED25542) - A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence.\n\n- An unreachable cluster has status running (RED32805) - When a cluster is in an unreachable state the state is still running instead of being reported as an error.\n\n- Readiness probe incorrect on failures (RED39300) - STS Readiness probe doesn't mark a node as not ready when rladmin status on the node fails\n\n- Role missing on replica sets (RED39002) - The redis-enterprise-operator role is missing permission on replica sets.\n\n- Private registries are not supported on OpenShift 3.11 (RED38579) - Openshift 3.11 doesn't support dockerhub private registry. This is a known OpenShift issue.\n\n- Internal DNS and K8s DNS may have conflicts (RED37462) - DNS conflicts are possible between the cluster mdns_server and the K8s DNS. This only impacts DNS resolution from within cluster nodes for K8s DNS names.\n\n- 5.4.10 negatively impacts 5.4.6 (RED37233) - K8S-based 5.4.10 clusters seem to negatively affect existing 5.4.6\n\n- Node CPU usage is reported instead of pod CPU usage (RED36884) - In Kubernetes, the node CPU usage we report on is the usage of the K8S worker node hosting the REC pod.\n\n- Clusters must be named \"rec\" in OLM-based deployments (RED39825) - In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) is bound to a specific service account name (i.e., \"rec\"). The workaround is to name the cluster \"rec\"."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-0-6-23","title":"Redis Enterprise for Kubernetes Release Notes 6.0.6-23 (August 2020)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-0-6-23/","summary":"Support for Redis Enterprise Software 6.0.6-39, Rancher support, new database backup and alert options.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"3f05f44cee2099e2597e9f70888b79ba25c6ae56b8d20bc0432c2c8cf6d178b6","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"The Redis Enterprise K8s 6.0.6-23 release is a *major release* on top of [6.0.6-11](https://github.com/RedisLabs/redis-enterprise-k8s-docs/releases/tag/v6.0.6-11) providing support for the latest [Redis Enterprise Software release 6.0.6-39](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-0-may-2020/) and includes several enhancements (including Rancher support) and bug fixes."},{"id":"overview","title":"Overview","role":"overview","text":"This release of the operator provides:\n\n- Support for the Redis Enterprise Software release 6.0.6-39\n- Support for Rancher\n- Backup options in the database custom resource and controller\n- Alert option in the database custom resource and controller\n- UBI images\n- Various other enhancements and bug fixes\n\nTo upgrade your deployment to this latest release, see [\"Upgrade a Redis Enterprise cluster (REC) on Kubernetes\"](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster)."},{"id":"images","title":"Images","role":"content","text":"- **Redis Enterprise** - redislabs/redis:6.0.6-39 or redislabs/redis:6.0.6-39.rhel7-openshift\n- **Operator** - redislabs/operator:6.0.6-23\n- **Services Rigger** - redislabs/k8s-controller:6.0.6-23 or redislabs/services-manager:6.0.6-23 (on the RedHat registry)"},{"id":"new-features","title":"New features","role":"content","text":"Red Hat UBI base images (RED29651) - The services rigger and operator images are now based on Red Hat UBI base images. Also, the same images are now used in both OpenShift and non-OpenShift environments.\n\n- Rancher support (RED37918) - The operator is now supported on [Rancher](https://rancher.com/) version ([v2.4.5](https://rancher.com/support-maintenance-terms/all-supported-versions/rancher-v2.4.5/)). Note that since this release of the operator doesn't support K8s 1.18, it requires one of the other supported upstread version in this Rancher release, i.e. 1.15.12, 1.16.13 or 1.17.9.\n\n- Database Replica Of support (RED40160) - Support for [Replica Of](https://redis.io/docs/latest/operate/rs/administering/active-passive/) was added to the [DB controller](https://github.com/RedisLabs/redis-enterprise-k8s-docs/blob/master/redis_enterprise_database_api.md#replicasource).\n\n- Database backup configuration (RED40165) - Support for [backup configuration](https://redis.io/docs/latest/operate/rs/databases/import-export/schedule-backups/) was added to the [DB controller spec](https://github.com/RedisLabs/redis-enterprise-k8s-docs/blob/master/redis_enterprise_database_api.md#backupspec).\n\n- Alert configuration (RED40166) - Support for alert configuration was added to the [DB controller spec](https://github.com/RedisLabs/redis-enterprise-k8s-docs/blob/master/redis_enterprise_database_api.md#dbalertssettings).\n\n- Database TLS configuration (RED41758) - Support for TLS authentication configuration was added to the [DB controller spec](https://github.com/RedisLabs/redis-enterprise-k8s-docs/blob/0ec741ffb3d621d371b5eec5b0a045ee7364e50e/redis_enterprise_database_api.md#redisenterprisedatabasespec).\n\n- OpenShift 4.4 support (RED41352) - The operator is now supported on [Openshift 4.4](https://docs.openshift.com/container-platform/4.4/welcome/index.html).\n\n- DB controller resources via the OLM (RED41755) - Support for configuration of DB controller resources was added to the OLM (preview channel)."},{"id":"important-fixes","title":"Important fixes","role":"content","text":"- Openshift OLM upgrade support was fixed. Also, installing past versions was added through dedicated channels. (RED44130)\n- Fixed log_collector failures when pods were not scheduled. (RED45347)\n- Fixed wrong handling of the operator environment variables. Specifically, enabling/disabling the database controller now works correctly. (RED45351)"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"- CrashLoopBackOff causes cluster recovery to be incomplete  (RED33713) - When a pod status is CrashLoopBackOff and we run the cluster recovery, the process will not complete. The solution is to delete the crashing pods manually and recovery process will continue.\n\n- Long cluster names cause routes to be rejected  (RED25871) - A cluster name longer than 20 characters will result in a rejected route configuration as the host part of the domain name exceeds 63 characters. The workaround is to limit cluster name to 20 characters or less.\n\n- Cluster CR (REC) errors are not reported after invalid updates (RED25542) - A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence.\n\n- An unreachable cluster has status running (RED32805) - When a cluster is in an unreachable state the state is still running instead of being reported as an error.\n\n- Readiness probe incorrect on failures (RED39300) - STS Readiness probe doesn't mark a node as not ready when rladmin status on the node fails\n\n- Role missing on replica sets (RED39002) - The redis-enterprise-operator role is missing permission on replica sets.\n\n- Private registries are not supported on OpenShift 3.11 (RED38579) - Openshift 3.11 doesn't support dockerhub private registry. This is a known OpenShift issue.\n\n- Internal DNS and K8s DNS may have conflicts (RED37462) - DNS conflicts are possible between the cluster mdns_server and the K8s DNS. This only impacts DNS resolution from within cluster nodes for K8s DNS names.\n\n- 5.4.10 negatively impacts 5.4.6 (RED37233) - K8S-based 5.4.10 clusters seem to negatively impact existing 5.4.6\n\n- Node CPU usage is reported instead of pod CPU usage (RED36884) - In Kubernetes, the node CPU usage we report on is the usage of the K8S worker node hosting the REC pod.\n\n- Clusters must be named \"rec\" in OLM-based deployments (RED39825) - In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) is bound to a specific service account name (i.e., \"rec\"). The workaround is to name the cluster \"rec\".\n\n- Updating UI service in Rancher (RED45771) - Updating the UI service type may fail in Rancher. When this happens, delete the service manually and the operator will recreate it correctly.\n\n- Master pod label in Rancher (RED42896) - Master pod is not always labeled in Rancher."},{"id":"deprecation-notice","title":"Deprecation notice","role":"content","text":"Support for Kubernetes version 1.11 and 1.12 is deprecated (excludes Openshift 3.11, which continues to be supported)."},{"id":"compatibility-updates","title":"Compatibility updates","role":"compatibility","text":"- Added support for Rancher\n- Added support for Openshift 4.4"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-0-6-24","title":"Redis Enterprise for Kubernetes Release Notes 6.0.6-24 (August 2020)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-0-6-24/","summary":"Various bug fixes.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"e2505650162ec983ccb1319d04bc48b872777f1e28f7fea00c9eba0299337878","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"The Redis Enterprise K8s 6.0.6-24 release is a *maintenance release* on top of [6.0.6-23](https://github.com/RedisLabs/redis-enterprise-k8s-docs/releases/tag/v6.0.6-23) providing support for the latest [Redis Enterprise Software release 6.0.6-39](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-0-may-2020/) and includes several bug fixes."},{"id":"overview","title":"Overview","role":"overview","text":"This release of the operator provides:\n\n * Various bug fixes\n\n\nTo upgrade your deployment to this latest release, see [\"Upgrade a Redis Enterprise cluster (REC) on Kubernetes\"](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster)."},{"id":"images","title":"Images","role":"content","text":"* **Redis Enterprise** - redislabs/redis:6.0.6-39 or redislabs/redis:6.0.6-39.rhel7-openshift\n * **Operator** - redislabs/operator:6.0.6-24\n * **Services Rigger** - redislabs/k8s-controller:6.0.6-24 or redislabs/services-manager:6.0.6-24 (on the RedHat registry)"},{"id":"important-fixes","title":"Important fixes","role":"content","text":"* A fix for database observability where after 24 hours after creation or update, the controller was unable to observe the database (RED46149)\n * A fix for a log collector crash on Windows when pods were not running (RED45477)"},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"crashloopbackoff-causes-cluster-recovery-to-be-incomplete--red33713","title":"CrashLoopBackOff causes cluster recovery to be incomplete  (RED33713)","role":"content","text":"When a pod status is CrashLoopBackOff and we run the cluster recovery, the process will not complete. The workaround is to delete the crashing pods manually and recovery process will continue."},{"id":"long-cluster-names-cause-routes-to-be-rejected--red25871","title":"Long cluster names cause routes to be rejected  (RED25871)","role":"content","text":"A cluster name longer than 20 characters will result in a rejected route configuration as the host part of the domain name exceeds 63 characters. The workaround is to limit cluster name to 20 characters or less."},{"id":"cluster-cr-rec-errors-are-not-reported-after-invalid-updates-red25542","title":"Cluster CR (REC) errors are not reported after invalid updates (RED25542)","role":"content","text":"A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence."},{"id":"an-unreachable-cluster-has-status-running-red32805","title":"An unreachable cluster has status running (RED32805)","role":"content","text":"When a cluster is in an unreachable state the state is still running instead of being reported as an error."},{"id":"readiness-probe-incorrect-on-failures-red39300","title":"Readiness probe incorrect on failures (RED39300)","role":"content","text":"STS Readiness probe doesn't mark a node as not ready when rladmin status on the node fails."},{"id":"role-missing-on-replica-sets-red39002","title":"Role missing on replica sets (RED39002)","role":"content","text":"The redis-enterprise-operator role is missing permission on replica sets."},{"id":"private-registries-are-not-supported-on-openshift-311-red38579","title":"Private registries are not supported on OpenShift 3.11 (RED38579)","role":"content","text":"Openshift 3.11 doesn't support dockerhub private registry. This is a known OpenShift issue."},{"id":"internal-dns-and-kubernetes-dns-may-have-conflicts-red37462","title":"Internal DNS and Kubernetes DNS may have conflicts (RED37462)","role":"content","text":"DNS conflicts are possible between the cluster mdns_server and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names."},{"id":"5410-negatively-impacts-546-red37233","title":"5.4.10 negatively impacts 5.4.6 (RED37233)","role":"content","text":"Kubernetes-based 5.4.10 deployments seem to negatively impact existing 5.4.6 deployments that share a Kubernetes cluster."},{"id":"node-cpu-usage-is-reported-instead-of-pod-cpu-usage-red36884","title":"Node CPU usage is reported instead of pod CPU usage (RED36884)","role":"content","text":"In Kubernetes, the node CPU usage we report on is the usage of the Kubernetes worker node hosting the REC pod."},{"id":"clusters-must-be-named-rec-in-olm-based-deployments-red39825","title":"Clusters must be named \"rec\" in OLM-based deployments (RED39825)","role":"content","text":"In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed with the OLM, the security context constraints (scc) is bound to a specific service account name (i.e., \"rec\"). The workaround is to name the cluster \"rec\"."},{"id":"updating-ui-service-in-rancher-red45771","title":"Updating UI service in Rancher (RED45771)","role":"content","text":"Updating the UI service type may fail in Rancher. When this happens, delete the service manually and the operator will recreate it correctly."},{"id":"master-pod-label-in-rancher-red42896","title":"Master pod label in Rancher (RED42896)","role":"content","text":"Master pod is not always labeled in Rancher."},{"id":"deprecation-notice","title":"Deprecation notice","role":"content","text":"Support for K8s version 1.11 and 1.12 is deprecated (excludes Openshift 3.11, which continues to be supported). Openshift 4.1 and 4.2 are deprecated (already End Of Life by Red Hat)."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-0-6-6","title":"Redis Enterprise for Kubernetes Release Notes 6.0.6-6 (June 2020)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-0-6-6/","summary":"Support for RS 6.0.6, new database and admission controllers, various improvements and bug fixes.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"c5e93aa1fd0856d4d44efead32b2582773d0dd455ba809f8beea83cd6b906be0","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"Redis Enterprise for Kubernetes 6.0.6-6 is now available. This release includes Redis Enterprise (RS) version [6.0](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-0-may-2020) and introduces new features, improvements, and bug fixes."},{"id":"overview","title":"Overview","role":"overview","text":"This release of the operator provides:\n\n- Support for the Redis Enterprise Software release 6.0.6\n- Includes the new database and admission controllers\n- Various improvements and bug fixes\n\nTo upgrade your deployment to this latest release, see [\"Upgrade a Redis Enterprise cluster (REC) on Kubernetes\"](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster).\n\n> [!NOTE]\n> If you are running Active-Active (CRBD) databases on a previous release, do not upgrade to 6.0.6-6 at this time. There is an issue with the upgrade process that is currently being investigated (RED43635). For more information and support please [contact Redis support](https://redislabs.com/company/support/)."},{"id":"images","title":"Images","role":"content","text":"- **Redis Enterprise** - redislabs/redis:6.0.6-6 or redislabs/redis:6.0.6-6.rhel7-openshift\n- **Operator** - redislabs/operator:6.0.6-6 or redislabs/operator:6.0.6-6.rhel7\n- **Services Rigger** - redislabs/k8s-controller:6.0.6-6 or redislabs/k8s-controller:6.0.6-6.rhel7"},{"id":"new-features-and-improvements","title":"New features and improvements","role":"content","text":"- Database controller - A new database controller in the operator provides the ability to create\nand manage databases on a Redis Enterprise cluster via a custom resource (RED36516).\n\n- Admission controller - A new admission controller in the operator provides validation of\ndatabase custom resources (RED36458).\n\n- Pod tolerations - Support for specifying Redis Enterprise cluster node pod tolerations of node taints\nhas been added to the cluster CR (see podTolerations) (RED33069).\n\n- Pod annotations - Support for specifying Redis Enterprise cluster node pod annotations has been\nadded to the cluster CR (see podAnnotations) (RED35613).\n\n- Kubernetes versions - Support for Kubernetes 1.17 was added and versions 1.9 and 1.10 (previously deprecated) are no longer supported (RED41049).\n\n- Improved OLM Experience - The overall user experience and documentation in the OLM (OperatorHub) has been improved (RED37008).\n\n- Resource limits - Resource limits have been added to the recommended operator configuration (RED39572).\n\n- LoadBalancer service type added - The `LoadBalancer` value has been added to the `databaseServiceType` option in `servicesRiggerSpec` (RED43215):\n\n    [code example]"},{"id":"important-fixes","title":"Important fixes","role":"content","text":"- Service creation failure causes cluster setup failure (RED37197)\n- UI service update failure (RED37198)\n- Error shown in OLM deployment: \"The field status.state is invalid\" (RED40278)\n- OLM: StatefulSet not listed as an object owned by the Redis Enterprise Cluster (RED39296)\n- Setting extraLabels in the cluster CR did not label pods on OpenShift (RED39763)\n- log_collector failed to get the pods logs when a namespace wasn't given (RED39292)\n- Role and RoleBinding created or updated using an existing ServiceAccount in REC spec (RED42912)"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"- CrashLoopBackOff pod status and cluster recovery - When a pod status is CrashLoopBackOff and we run the cluster recovery, the process\nwill not complete. The solution is to delete the crashing pods manually and\nrecovery process will continue (RED33713).\n\n- Active-Active (CRDB) - limitation on cluster name length - A cluster name longer than 20 characters will result in a rejected route\nconfiguration as the host part of the domain name exceeds 63 characters. Cluster\nnames must be limited to 20 characters or less (RED25871).\n\n- Active-Active (CRDB) service broker cleanup - The service broker doesn't clean up database service bindings in case of failures.\nThese bindings must be removed manually (RED25825).\n\n- Service broker deployment error - The service broker deployment results in an error when two types of service naming\nschemes are set. Choosing one of the methods will resolve this error (`redis-port`\nis the recommended default) (RED25547).\n\n- Cluster spec invalid errors not reported - A cluster CR specification error is not reported if two or more invalid CR resources are\nupdated in sequence (RED25542).\n\n- Unreachable cluster does not produce an error - When a cluster is in an unreachable state the state is still `running` instead of\nbeing reported as an error (RED32805).\n\n- Readiness probe ignores rladmin failure - STS Readiness probe doesn't mark a node as not ready when rladmin status nodes fails (RED39300).\n\n- Missing permission for role - The redis-enterprise-operator role is missing permission on replicasets (RED39002).\n\n- Openshift 3.11 doesn't support DockerHub private registry - Openshift 3.11 doesn't support DockerHub private registry. This is a known OpenShift\nissue and not addressable by Redis Enterprise for Kubernetes (RED38579).\n\n- Possible DNS conflicts within cluster nodes - DNS conflicts are possible between the cluster mdns_server and the K8s DNS.\nThis only impacts DNS resolution from within cluster node and while using the full fqdn *.cluster.local (RED37462).\n\n- Coexistence of 5.4.10 and 5.4.6 clusters - K8s clusters with Redis Enterprise 5.4.6 clusters are negatively affected by installing a Redis Enterprise 5.4.10 cluster due to changes in CRD (CustomeResourceDefinition) (RED37233).\n\n- Redis Enterprise CPU utilization metric reports at K8s node level rather than at pod level - In Kubernetes, the node CPU usage we report on is the usage of the K8S worker node hosting the REC pod (RED-36884). Pod resource utilization should be measured by K8s-native means rather than through the application.\n\n- Cluster name is limited on OpenShift via OLM - In OLM-deployed operators, the deployment of the cluster will fail if the name\nis not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc)\nis bound to a specific service account name (i.e., \"rec\"). Naming the cluster\n\"rec\" resolves the issue (RED39825)."},{"id":"coming-soon","title":"Coming Soon","role":"content","text":"The following lists features, fixes and changes the Redis team is currently investing in:\n\n- Redis Enterprise and Kubernetes Container Artifacts - tarting from the next release of Redis Enterprise for K8s, new container artifacts will be published using different base images:\n\n    - **Redis Enterprise** - A UBI (RHEL 7) base image will replace the Ubuntu and the RHEL7 base images\n    - **Operator** - An Image built from scratch containing the Golang executable will replace the Ubuntu and the RHEL7 base images\n    - **Services Rigger** - A UBI (RHEL 7) base image will replace the Ubuntu and the RHEL7 base images\n\n- Deprecation notice - he service broker solution is deprecated and will not be supported starting from the next release of the Redis Enterprise Operator for Kubernetes.\n\n- Additional Redis Enterprise Database configuration options in the Database Controller - We're currently investing in the following additional capabilities of the Database Controller:\n    - Support for loading database modules\n    - Support for setting up Alerts, expressed as K8s events\n    - Support for configuring database backup options\n    - Support for Kubernetes 1.18\n    - Support for Rancher K8s distribution"}],"examples":[{"id":"new-features-and-improvements-ex0","language":"yaml","code":"servicesRiggerSpec:\n       databaseServiceType: LoadBalancer","section_id":"new-features-and-improvements"}]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-0-8-1","title":"Redis Enterprise for Kubernetes Release Notes 6.0.8-1 (October 2020)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-0-8-1/","summary":"Support for RS 6.0.8-28, OpenShift 4.5, K8s 1.18, and Gesher admission controller proxy.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"e90762b71c791e8823e3e93433936cf6b47c8b5b8cacf8f3114e8600d0844e9d","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"The Redis Enterprise K8s 6.0.8-1 release is a *major release* on top of [6.0.6-24](https://github.com/RedisLabs/redis-enterprise-k8s-docs/releases/tag/v6.0.6-24) providing support for the latest [Redis Enterprise Software release 6.0.8-28](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-0-8-september-2020/) and includes several enhancements (including OpenShift 4.5 and Kubernetes 1.18 support) and bug fixes."},{"id":"overview","title":"Overview","role":"overview","text":"This release of the operator provides:\n\n * New features\n * New support K8s distributions and platforms\n * Various bug fixes\n\n\nTo upgrade your deployment to this latest release, see [\"Upgrade a Redis Enterprise cluster (REC) on Kubernetes\"](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster)."},{"id":"images","title":"Images","role":"content","text":"* **Redis Enterprise** - redislabs/redis:6.0.8-28 or redislabs/redis:6.0.8-28.rhel7-openshift\n * **Operator** - redislabs/operator:6.0.8-1\n * **Services Rigger** - redislabs/k8s-controller:6.0.8-1 or redislabs/services-manager:6.0.8-1 (Red Hat registry)"},{"id":"new-features","title":"New features","role":"content","text":"* Redis [Modules](https://redislabs.com/redis-enterprise/modules/) can now be [configured](https://github.com/RedisLabs/redis-enterprise-k8s-docs/blob/master/redis_enterprise_database_api.md#dbmodule) in the database custom resource.\n * Support was added for **[OpenShift 4.5](https://docs.openshift.com/container-platform/4.5/release_notes/ocp-4-5-release-notes.html)**\n * Support was added for **Kubernetes 1.18**\n * Added support for the Gesher admission control proxy to provide an administrator the ability to setup delegation to avoid the need for administrator intervention on every namespaced deployed operator."},{"id":"important-fixes","title":"Important fixes","role":"content","text":"* Added the missing Services Rigger health check (RED47062)\n * Fixed failures when updating the ui service type (RED45771)"},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"crashloopbackoff-causes-cluster-recovery-to-be-incomplete--red33713","title":"CrashLoopBackOff causes cluster recovery to be incomplete  (RED33713)","role":"content","text":"When a pod status is CrashLoopBackOff and we run the cluster recovery, the process will not complete. The workaround is to delete the crashing pods manually and recovery process will continue."},{"id":"long-cluster-names-cause-routes-to-be-rejected--red25871","title":"Long cluster names cause routes to be rejected  (RED25871)","role":"content","text":"A cluster name longer than 20 characters will result in a rejected route configuration as the host part of the domain name exceeds 63 characters. The workaround is to limit cluster name to 20 characters or less."},{"id":"cluster-cr-rec-errors-are-not-reported-after-invalid-updates-red25542","title":"Cluster CR (REC) errors are not reported after invalid updates (RED25542)","role":"content","text":"A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence."},{"id":"an-unreachable-cluster-has-status-running-red32805","title":"An unreachable cluster has status running (RED32805)","role":"content","text":"When a cluster is in an unreachable state the state is still running instead of being reported as an error."},{"id":"readiness-probe-incorrect-on-failures-red39300","title":"Readiness probe incorrect on failures (RED39300)","role":"content","text":"STS Readiness probe doesn't mark a node as not ready when rladmin status on the node fails."},{"id":"role-missing-on-replica-sets-red39002","title":"Role missing on replica sets (RED39002)","role":"content","text":"The redis-enterprise-operator role is missing permission on replica sets."},{"id":"private-registries-are-not-supported-on-openshift-311-red38579","title":"Private registries are not supported on OpenShift 3.11 (RED38579)","role":"content","text":"Openshift 3.11 doesn't support dockerhub private registry. This is a known OpenShift issue."},{"id":"internal-dns-and-kubernetes-dns-may-have-conflicts-red37462","title":"Internal DNS and Kubernetes DNS may have conflicts (RED37462)","role":"content","text":"DNS conflicts are possible between the cluster mdns_server and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names."},{"id":"5410-negatively-impacts-546-red37233","title":"5.4.10 negatively impacts 5.4.6 (RED37233)","role":"content","text":"Kubernetes-based 5.4.10 deployments seem to negatively impact existing 5.4.6 deployments that share a Kubernetes cluster."},{"id":"node-cpu-usage-is-reported-instead-of-pod-cpu-usage-red36884","title":"Node CPU usage is reported instead of pod CPU usage (RED36884)","role":"content","text":"In Kubernetes, the node CPU usage we report on is the usage of the Kubernetes worker node hosting the REC pod."},{"id":"master-pod-label-in-rancher-red42896","title":"Master pod label in Rancher (RED42896)","role":"content","text":"Master pod is not always labeled in Rancher."},{"id":"cluster-fail-to-start-for-clusters-with-unsynchronized-clocks-red47254","title":"Cluster fail to start for clusters with unsynchronized clocks (RED47254)","role":"content","text":"When REC clusters are deployed on clusters with unsynchronized clocks, the cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes."},{"id":"errors-in-operator-log-for-redb-status-red44919","title":"Errors in operator log for REDB status (RED44919)","role":"errors","text":"Benign errors are reported in the operator log when using database controller (REDB) (e.g., \"failed to update database status\". These errors can be ignored."},{"id":"compatibility-notes","title":"Compatibility Notes","role":"compatibility","text":"* Support for **OpenShift 4.5** was **added**,\n* Support for **Kubernetes 1.18** was **added**,\n* Support for the previous deprecated **Kubernetes 1.11 and 1.12** has been **removed**."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-0-8-20","title":"Redis Enterprise for Kubernetes Release Notes 6.0.8-20 (December 2020)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-0-8-20/","summary":"Support for RS 6.0.8, consumer namespaces, Gesher admission controller proxy, and custom resource validation via schema.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"3a2f11c3b3c4434929674c9803c7c9c047830a52db16fa7faeff564ccb33fd6a","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"The Redis Enterprise K8s 6.0.8-20 release is a *major release* on top of [6.0.8-1](https://github.com/RedisLabs/redis-enterprise-k8s-docs/releases/tag/v6.0.8-1) providing support for the [Redis Enterprise Software release 6.0.8-30](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-0-8-september-2020/) and includes several enhancements and bug fixes."},{"id":"overview","title":"Overview","role":"overview","text":"This release of the operator provides:\n\n * New features\n * Various bug fixes\n\nTo upgrade your deployment to this latest release, see [\"Upgrade a Redis Enterprise cluster (REC) on Kubernetes\"](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster)."},{"id":"images","title":"Images","role":"content","text":"This release includes the following container images:\n * **Redis Enterprise**: redislabs/redis:6.0.8-30 or redislabs/redis:6.0.8-30.rhel7-openshift\n * **Operator and Bootstrapper**: redislabs/operator:6.0.8-20\n * **Services Rigger**: redislabs/k8s-controller:6.0.8-20 or redislabs/services-manager:6.0.8-20 (Red Hat registry)"},{"id":"new-features","title":"New features","role":"content","text":"* You can now create database custom resources (REDB CRs) in consumer namespaces that are separate from the operator and cluster namespace. You configured the operator deployment to watch specific namespaces for these REDB CRs.\n * The Gesher admission control proxy is now certified by Red Hat.\n * REDB CRs no longer require a Redis Enterprise cluster name. The name will default to the cluster in the context of the operator.\n * REC and REDB CRs are now validated via a schema."},{"id":"important-fixes","title":"Important fixes","role":"content","text":"* The database controller (REDB) no longer generates the following error message:  \"failed to update database status\"\n * Issues with configuring replica-of through the database controller (REDB) and TLS have been fixed. (RED48285)\n * A timeout issue with `rlutil upgrade` was fixed. (RED48700)"},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"crashloopbackoff-causes-cluster-recovery-to-be-incomplete--red33713","title":"CrashLoopBackOff causes cluster recovery to be incomplete  (RED33713)","role":"content","text":"When a pod's status is `CrashLoopBackOff` and we run the cluster recovery, the process will not complete. The workaround is to delete the crashing pods manually. The recovery process will then continue."},{"id":"long-cluster-names-cause-routes-to-be-rejected--red25871","title":"Long cluster names cause routes to be rejected  (RED25871)","role":"content","text":"A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name will exceed 63 characters. The workaround is to limit cluster name to 20 characters or fewer."},{"id":"cluster-cr-rec-errors-are-not-reported-after-invalid-updates-red25542","title":"Cluster CR (REC) errors are not reported after invalid updates (RED25542)","role":"content","text":"A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence."},{"id":"an-unreachable-cluster-has-status-running-red32805","title":"An unreachable cluster has status running (RED32805)","role":"content","text":"When a cluster is in an unreachable state, the state is still `running` instead of being reported as an error."},{"id":"readiness-probe-incorrect-on-failures-red39300","title":"Readiness probe incorrect on failures (RED39300)","role":"content","text":"STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on the node fails."},{"id":"role-missing-on-replica-sets-red39002","title":"Role missing on replica sets (RED39002)","role":"content","text":"The `redis-enterprise-operator` role is missing permission on replica sets."},{"id":"private-registries-are-not-supported-on-openshift-311-red38579","title":"Private registries are not supported on OpenShift 3.11 (RED38579)","role":"content","text":"Openshift 3.11 does not support DockerHub private registries. This is a known OpenShift issue."},{"id":"internal-dns-and-kubernetes-dns-may-have-conflicts-red37462","title":"Internal DNS and Kubernetes DNS may have conflicts (RED37462)","role":"content","text":"DNS conflicts are possible between the cluster mdns_server and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names."},{"id":"5410-negatively-impacts-546-red37233","title":"5.4.10 negatively impacts 5.4.6 (RED37233)","role":"content","text":"Kubernetes-based 5.4.10 deployments seem to negatively impact existing 5.4.6 deployments that share a Kubernetes cluster."},{"id":"node-cpu-usage-is-reported-instead-of-pod-cpu-usage-red36884","title":"Node CPU usage is reported instead of pod CPU usage (RED36884)","role":"content","text":"In Kubernetes, the node CPU usage we report on is the usage of the Kubernetes worker node hosting the REC pod."},{"id":"clusters-must-be-named-rec-in-olm-based-deployments-red39825","title":"Clusters must be named \"rec\" in OLM-based deployments (RED39825)","role":"content","text":"In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) is bound to a specific service account name (i.e., \"rec\"). The workaround is to name the cluster \"rec\"."},{"id":"master-pod-label-in-rancher-red42896","title":"Master pod label in Rancher (RED42896)","role":"content","text":"The master pod is not always labeled in Rancher."},{"id":"rec-clusters-fail-to-start-on-kubernetes-clusters-with-unsynchronized-clocks-red47254","title":"REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED47254)","role":"content","text":"When REC clusters are deployed on Kubernetes clusters with unsynchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-2-10-34-2022-05","title":"Redis Enterprise for Kubernetes Release Notes 6.2.10-34 (May 2022)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-2-10-34-2022-05/","summary":"Support for REBD specific upgrade policies, memcached type REDBs, and RHEL8 for OpenShift, as well as feature improvements and bug fixes.","aliases":["/operate/kubernetes/release-notes/k8s-6-2-10-34-2022-05/","/operate/kubernetes/release-notes/6-2-releases/k8s-6-2-10-34-2022-05/"],"tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"a2ba4b04cab105306a88be5072178ac9e1b1db1588e2c9de323d9cd06f5fc69d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The Redis Enterprise K8s 6.2.10-34 supports the Redis Enterprise Software release 6.2.10 and includes feature improvements and bug fixes.\n\nThe key new features, bug fixes, and known limitations are described below.\n\n> [!WARNING]\n>   **Do not** upgrade to this 6.2.10-34 release if you are an **OpenShift** customer and **also use modules**. Instead, **use the [6.2.10-45](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-2-10-45-2022-07)** release.\n>   \n>   There was a change in 6.2.10-34 to a new RHEL 8 base image for the Redis Server image. Due to binary differences in modules between the two operating systems, you cannot directly update RHEL 7 clusters to RHEL 8 when those clusters host databases using modules.\n>   \n>    Please contact support if you have further questions."},{"id":"images","title":"Images","role":"content","text":"This release includes the following container images:\n\n* **Redis Enterprise**: `redislabs/redis:6.2.10-107` or  `redislabs/redis:6.2.10-107.rhel8-openshift`\n* **Operator**: `redislabs/operator:6.2.10-34`\n* **Services Rigger**: `redislabs/k8s-controller:6.2.10-34` or `redislabs/services-manager:6.2.10-34` (on the Red Hat registry)"},{"id":"new-features","title":"New features","role":"content","text":"* Support database upgrade policy (major/latest) for REDB resources (RED-71028)\n* Support for memcached type databases for REDB (RED-70284)(RED-75269)\n* Use RHEL8 base images for OpenShift deployments (RED-72374)"},{"id":"feature-improvements","title":"Feature improvements","role":"content","text":"* OpenShift 4.10 support (RED-73966)\n* Allow setting host time zone on running containers (RED-56810)\n* AKS 1.23 support (RED-73965)\n* EKS 1.22 support (RED-73972)"},{"id":"fixed-bugs","title":"Fixed bugs","role":"content","text":"* Outdated SCC YAML file (RED-72026) (RED-73341)\n* Admission container startup failure (RED-72081)\n* Admission container restarts due to race condition with config map creation (RED-72268)\n* Incorrect REDB status report during cluster recovery (RED-72944)\n* Invalid REDB spec not always rejected by admission controller (RED-73145)"},{"id":"compatibility-notes","title":"Compatibility notes","role":"compatibility","text":"Below is a table showing supported distributions at the time of this release. See [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for the current list of supported distributions.\n\n| **Kubernetes version**  | **1.19**   | **1.20**   | **1.21**   | **1.22**   | **1.23**   |\n|:------------------------|:----------:|:----------:|:----------:|:----------:|:----------:|\n| Community Kubernetes    | deprecated | deprecated | supported  | supported  | supported* |\n| Amazon EKS              | supported  | supported  | supported  |            |            |\n| Azure AKS               |            |            | supported  | supported  | supported* |\n| Google GKE              | supported  | supported  | supported  | supported  |            |\n| Rancher 2.6             | supported  | supported  | supported  | supported  |            |\n| **OpenShift version**   | **4.6**    | **4.7**    | **4.8**    | **4.9**    | **4.10**   |\n|                         | deprecated | deprecated | supported  | supported  | supported* |\n| **VMware TKGI version** | **1.10**   | **1.11**   | **1.12**   | **1.13**   |            |\n|                         | supported  | supported  | supported* |            |            |\n\n\\* Support added in most recent release"},{"id":"now-supported","title":"Now supported","role":"content","text":"* OpenShift 4.10 is now supported\n* kOps (Community Kubernetes) 1.23 is now supported\n* AKS 1.23 is now supported\n* EKS 1.22 is now supported"},{"id":"deprecated","title":"Deprecated","role":"content","text":"* OpenShift 4.6-4.7 is deprecated\n* kOps (Community Kubernetes) 1.18-1.20 are deprecated\n* GKE 1.19 is deprecated\n* Rancher 2.6 - K8s 1.18 is deprecated\n* AKS 1.20-1.21 are deprecated\n* EKS 1.18-1.19 are deprecated"},{"id":"no-longer-supported","title":"No longer supported","role":"content","text":"* Rancher version 2.5 (previously deprecated) is no longer supported (not supported by SUSE)\n* OpenShift version 3.11 (previously deprecated) is no longer supported."},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"large-clusters","title":"Large clusters","role":"content","text":"On clusters with more than 9 REC nodes, a Kubernetes upgrade can render the Redis cluster unresponsive in some cases. A fix is available in the 6.4.2-5 release. Upgrade your operator version to 6.4.2-5 or later before upgrading your Kubernetes cluster. (RED-93025)\n\n* **Warning** Openshift customers using modules cannot use 6.2.10-34 version\n  > [!WARNING]\n  > **Do not** upgrade to this 6.2.10-34 release if you are an **OpenShift** customer and **also use modules**.\n  >\n  > There was a change in 6.2.10-34 to a new RHEL 8 base image for the Redis Server image. Due to binary differences in modules between the two operating systems, you cannot directly update RHEL 7 clusters to RHEL 8 when those clusters host databases using modules.\n  >\n  > This message will be updated as remediation plans and new steps are available to address this situation. Please contact support if you have further questions.\n\n* Long cluster names cause routes to be rejected  (RED-25871)\n\n  A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name will exceed 63 characters. The workaround is to limit cluster name to 20 characters or less.\n\n* Cluster CR (REC) errors are not reported after invalid updates (RED-25542)\n  \n  A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence.\n\n* An unreachable cluster has status running (RED-32805)\n  \n  When a cluster is in an unreachable state, the state is still `running` instead of being reported as an error.\n\n* Readiness probe incorrect on failures (RED-39300)\n\n  STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure.\n\n* Role missing on replica sets (RED-39002)\n\n  The `redis-enterprise-operator` role is missing permission on replica sets.\n\n* Internal DNS and Kubernetes DNS may have conflicts (RED-37462)\n\n  DNS conflicts are possible between the cluster `mdns_server` and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names.\n\n* 5.4.10 negatively impacts 5.4.6 (RED-37233)\n\n  Kubernetes-based 5.4.10 deployments seem to negatively impact existing 5.4.6 deployments that share a Kubernetes cluster.\n\n* Node CPU usage is reported instead of pod CPU usage (RED-36884)\n\n  In Kubernetes, the node CPU usage we report on is the usage of the Kubernetes worker node hosting the REC pod.\n\n* Clusters must be named \"rec\" in OLM-based deployments (RED-39825)\n\n  In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) are bound to a specific service account name (i.e., \"rec\"). The workaround is to name the cluster \"rec\".\n\n* REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED-47254)\n\n  When REC clusters are deployed on Kubernetes clusters with unsynchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes.\n\n* Deleting an OpenShift project with an REC deployed may hang (RED-47192)\n\n  When a REC cluster is deployed in a project (namespace) and has REDB resources, the REDB resources must be deleted first before the REC can be deleted. As such, until the REDB resources are deleted, the project deletion will hang. The fix is to delete the REDB resources first and the REC second. Afterwards, you may delete the project.\n\n* REC extraLabels are not applied to PVCs on K8s versions 1.15 or older (RED-51921)\n\n  In K8s 1.15 or older, the PVC labels come from the match selectors and not the PVC templates. As such, these versions cannot support PVC labels. If this feature is required, the only fix is to upgrade the K8s cluster to a newer version.\n\n* Hashicorp Vault integration - no support for Gesher (RED-55080)\n\n  There is no workaround at this time.\n\n* REC might report error states on initial startup (RED-61707)\n\n  There is no workaround at this time except to ignore the errors.\n\n* PVC size issues when using decimal value in spec (RED-62132)\n\n  The workaround for this issue is to make sure you use integer values for the PVC size.\n\n* Following old revision of quick start guide causes issues creating an REDB due to unrecognized memory field name (RED-69515)\n\nThe workaround is to use the newer (current) revision of the quick start document available online.\n\n* `autoUpgrade` set to true by operator might cause unexpected bdb upgrades when `redisUpgradePolicy` is set to true (RED-72351)\n\n  Contact support if your deployment is impacted.\n\n* Procedure to update credentials might be problematic on OpenShift when accessing the cluster externally using routes (RS issue)(RED-73251)(RED-75329)\n  To workaround this, access the API from within the K8s cluster."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-2-10-4-2022-03","title":"Redis Enterprise for Kubernetes Release Notes 6.2.10-4 (March 2022)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-2-10-4-2022-03/","summary":"Added support for RS 6.2.10 as well as feature improvements and bug fixes.","aliases":["/operate/kubernetes/release-notes/k8s-6-2-10-4-2022-03-copy/","/operate/kubernetes/release-notes/k8s-6-2-10-4-2022-03/","/operate/kubernetes/release-notes/6-2-releases/k8s-6-2-10-4-2022-03/","/operate/kubernetes/release-notes/k8s-6-2-10-4-2022-03 copy/"],"tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"049cdcab7f77aa8bc01863d289ce4a51913a775bc001da1b224e3744d6e694f1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The Redis Enterprise K8s 6.2.10-4 supports the Redis Enterprise Software release 6.2.10 and includes feature improvements and bug fixes.\n\nThe key new features, bug fixes, and known limitations are described below."},{"id":"images","title":"Images","role":"content","text":"This release includes the following container images:\n\n* **Redis Enterprise**: `redislabs/redis:6.2.10-90` or  `redislabs/redis:6.2.10-90.rhel7-openshift`\n* **Operator**: `redislabs/operator:6.2.10-4`\n* **Services Rigger**: `redislabs/k8s-controller:6.2.10-4` or `redislabs/services-manager:6.2.10-4` (on the Red Hat registry)"},{"id":"feature-improvements","title":"Feature improvements","role":"content","text":"* REDB status - bundled Redis versions for cluster shown in status (RED-44074)\n* OLM (OperatorHub) - example REC contains image references to Red Hat servers (RED-44439)\n* REDB status - added information about database backups (RED-45577)\n* Added dedicated metrics exporter service (service name ending in -prom). Using the service is recommended for gathering Prometheus data about the cluster. Using the service with the UI service is not recommended (RED-61807)\n* REDB - added support for shard_placement\n* RS 6.2.10 support (RED-69142)\n* REDB - control autoUpgrade (RED-71157)"},{"id":"fixed-bugs","title":"Fixed bugs","role":"content","text":"* Cluster no longer refuses wrong sized volume to create clusters (error shown instead) (RED-61284)\n\n* Resolved high CPU usage when many pods are running within the namespace (RED-69682)\n\n* TKGI - support 1.11/1.12 (RED-70579)\n\n* Fixed rack awareness label in documentation (RED-70622)\n\n* Fixed crash in admission container when admission service is missing (RED-70678)\n\n* Services rigger deployment generation no longer increases without changes (RED-70835)"},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"large-clusters","title":"Large clusters","role":"content","text":"On clusters with more than 9 REC nodes, a Kubernetes upgrade can render the Redis cluster unresponsive in some cases. A fix is available in the 6.4.2-5 release. Upgrade your operator version to 6.4.2-5 or later before upgrading your Kubernetes cluster. (RED-93025)"},{"id":"long-cluster-names-cause-routes-to-be-rejected--red-25871","title":"Long cluster names cause routes to be rejected  (RED-25871)","role":"content","text":"A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name will exceed 63 characters. The workaround is to limit cluster name to 20 characters or less."},{"id":"cluster-cr-rec-errors-are-not-reported-after-invalid-updates-red-25542","title":"Cluster CR (REC) errors are not reported after invalid updates (RED-25542)","role":"content","text":"A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence."},{"id":"an-unreachable-cluster-has-status-running-red-32805","title":"An unreachable cluster has status running (RED-32805)","role":"content","text":"When a cluster is in an unreachable state, the state is still `running` instead of being reported as an error."},{"id":"readiness-probe-incorrect-on-failures-red-39300","title":"Readiness probe incorrect on failures (RED-39300)","role":"content","text":"STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure."},{"id":"role-missing-on-replica-sets-red-39002","title":"Role missing on replica sets (RED-39002)","role":"content","text":"The `redis-enterprise-operator` role is missing permission on replica sets."},{"id":"private-registries-are-not-supported-on-openshift-311-red-38579","title":"Private registries are not supported on OpenShift 3.11 (RED-38579)","role":"content","text":"OpenShift 3.11 does not support DockerHub private registries. This is a known OpenShift issue."},{"id":"internal-dns-and-kubernetes-dns-may-have-conflicts-red-37462","title":"Internal DNS and Kubernetes DNS may have conflicts (RED-37462)","role":"content","text":"DNS conflicts are possible between the cluster `mdns_server` and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names."},{"id":"5410-negatively-impacts-546-red-37233","title":"5.4.10 negatively impacts 5.4.6 (RED-37233)","role":"content","text":"Kubernetes-based 5.4.10 deployments seem to negatively impact existing 5.4.6 deployments that share a Kubernetes cluster."},{"id":"node-cpu-usage-is-reported-instead-of-pod-cpu-usage-red-36884","title":"Node CPU usage is reported instead of pod CPU usage (RED-36884)","role":"content","text":"In Kubernetes, the node CPU usage we report on is the usage of the Kubernetes worker node hosting the REC pod."},{"id":"clusters-must-be-named-rec-in-olm-based-deployments-red-39825","title":"Clusters must be named \"rec\" in OLM-based deployments (RED-39825)","role":"content","text":"In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) are bound to a specific service account name (i.e., \"rec\"). The workaround is to name the cluster \"rec\"."},{"id":"rec-clusters-fail-to-start-on-kubernetes-clusters-with-unsynchronized-clocks-red-47254","title":"REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED-47254)","role":"content","text":"When REC clusters are deployed on Kubernetes clusters with unsynchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes."},{"id":"deleting-an-openshift-project-with-an-rec-deployed-may-hang-red-47192","title":"Deleting an OpenShift project with an REC deployed may hang (RED-47192)","role":"content","text":"When a REC cluster is deployed in a project (namespace) and has REDB resources, the\nREDB resources must be deleted first before the REC can be deleted. As such, until the\nREDB resources are deleted, the project deletion will hang. The fix is to delete the\nREDB resources first and the REC second. Afterwards, you may delete the project."},{"id":"rec-extralabels-are-not-applied-to-pvcs-on-k8s-versions-115-or-older-red-51921","title":"REC extraLabels are not applied to PVCs on K8s versions 1.15 or older (RED-51921)","role":"content","text":"In K8s 1.15 or older, the PVC labels come from the match selectors and not the\nPVC templates. As such, these versions cannot support PVC labels. If this feature\nis required, the only fix is to upgrade the K8s cluster to a newer version."},{"id":"hashicorp-vault-integration---no-support-for-gesher-red-55080","title":"Hashicorp Vault integration - no support for Gesher (RED-55080)","role":"content","text":"There is no workaround at this time."},{"id":"rec-might-report-error-states-on-initial-startup-red-61707","title":"REC might report error states on initial startup (RED-61707)","role":"content","text":"There is no workaround at this time except to ignore the errors."},{"id":"pvc-size-issues-when-using-decimal-value-in-spec-red-62132","title":"PVC size issues when using decimal value in spec (RED-62132)","role":"content","text":"The workaround for this issue is to make sure you use integer values for the PVC size."},{"id":"following-old-revision-of-quick-start-guide-causes-issues-creating-an-redb-due-to-unrecognized-memory-field-name-red-69515","title":"Following old revision of quick start guide causes issues creating an REDB due to unrecognized memory field name (RED-69515)","role":"content","text":"The workaround is to use the newer (current) revision of the quick start document available online."},{"id":"autoupgrade-set-to-true-by-operator-might-cause-unexpected-bdb-upgrades-when-redisupgradepolicy-is-set-to-true-red-72351","title":"`autoUpgrade` set to true by operator might cause unexpected bdb upgrades when `redisUpgradePolicy` is set to true (RED-72351)","role":"content","text":"Contact support if your deployment is impacted."},{"id":"compatibility-notes","title":"Compatibility Notes","role":"compatibility","text":"See [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for the full list of supported distributions."},{"id":"now-supported","title":"Now supported","role":"content","text":"* TKGI 1.11 (K8s 1.20) is supported\n* Rancher version 2.6 is supported"},{"id":"deprecated","title":"Deprecated","role":"content","text":"* Rancher version 2.5 support is deprecated"},{"id":"no-longer-supported","title":"No longer supported","role":"content","text":"* Rancher version 2.5 / K8s 1.17 (previously deprecated) is no longer supported (not supported by SUSE)"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-2-10-45-2022-07","title":"Redis Enterprise for Kubernetes release notes 6.2.10-45 (July 2022)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-2-10-45-2022-07/","summary":"Support added for additional distributions as well as some feature improvements.","aliases":["/operate/kubernetes/release-notes/k8s-6-2-10-45-2022-07/","/operate/kubernetes/release-notes/6-2-releases/k8s-6-2-10-45-2022-07/"],"tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"d3c100485cd7480928440c619f1e6afb0a132c0ef2ca024771a6a78514e634f6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The Redis Enterprise K8s 6.2.10-45 supports the Redis Enterprise Software release 6.2.10 and includes feature improvements and bug fixes.\n\nThe key bug fixes and known limitations are described below."},{"id":"images","title":"Images","role":"content","text":"This release includes the following container images:\n\n* **Redis Enterprise**: `redislabs/redis:6.2.10-129` or  `redislabs/redis:6.2.10-129.rhel8-openshift` (or `redislabs/redis:6.2.10-129.rhel7-openshift` if upgrading from a RHEL7 cluster)\n* **Operator**: `redislabs/operator:6.2.10-45`\n* **Services Rigger**: `redislabs/k8s-controller:6.2.10-45` or `redislabs/services-manager:6.2.10-45` (on the Red Hat registry)"},{"id":"feature-improvements","title":"Feature improvements","role":"content","text":"* OpenShift OperatorLifecycleManager support on restricted networks (RED-72968)\n* `log_collector` script uses `oc` command with automatic detection of OpenShift (RED-73215)\n* Operator uses `policy/v1` for `PodDisruptionBudget`(RED-78564)\n* Added support for Kubernetes distributions (see Compatibility notes below)"},{"id":"fixed-bugs","title":"Fixed bugs","role":"content","text":"* Upgrade failures when RHEL7 was used (RED-77890)\n* Log collector failures when Python2 was used (RED-73403)"},{"id":"api-changes","title":"API changes","role":"content","text":"The `digestHash` optional field added to `imageSpec` fields in the REC. This field should be used in disconnected environments using the OperatorLifecycleManager."},{"id":"compatibility-notes","title":"Compatibility notes","role":"compatibility","text":"Below is a table showing supported distributions at the time of this release. See [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for the current list of supported distributions.\n\n| **Kubernetes version**  | 1.19       | 1.20       | 1.21       | 1.22       | 1.23       | 1.24       |\n|:------------------------|:----------:|:----------:|:----------:|:----------:|:----------:|:----------:|\n| Community Kubernetes    |            |            | deprecated | supported  | supported  | supported* |\n| Amazon EKS              | deprecated | deprecated | supported  | supported* |            |\n| Azure AKS               |            |            | deprecated | supported  | supported  |\n| Google GKE              | deprecated | deprecated | supported  | supported  |supported*  |\n| Rancher 2.6             | deprecated | deprecated | supported  | supported  |            |\n| **OpenShift version**   | **4.6**    | **4.7**    | **4.8**    | **4.9**    | **4.10**   |\n|                         |            | deprecated | deprecated | supported  | supported  |\n| **VMware TKGI version** | **1.10**   | **1.11**   | **1.12**   | **1.13**   |            |\n|                         | deprecated | deprecated | supported* | supported* |            |\n\n\\* Support added in this release"},{"id":"support-added","title":"Support added","role":"compatibility","text":"* K8s community version 1.24"},{"id":"deprecated","title":"Deprecated","role":"content","text":"* OpenShift 4.7-4.8\n* Kubernetes 1.20\n* Rancher 2.6 for K8s 1.19-1.20\n* TKGI 1.10-11"},{"id":"no-longer-supported","title":"No longer supported","role":"content","text":"* OpenShift 4.6 (previously deprecated)\n* Kubernetes 1.18-1.19 (previously deprecated)\n* Rancher 2.6 for K8s 1.18 (previously deprecated)\n* AKS 1.20-1.21 (previously deprecated)\n* EKS 1.18-1.19 (previously deprecated)\n* GKE 1.19 (previously deprecated)"},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"large-clusters","title":"Large clusters","role":"content","text":"On clusters with more than 9 REC nodes, a Kubernetes upgrade can render the Redis cluster unresponsive in some cases. A fix is available in the 6.4.2-5 release. Upgrade your operator version to 6.4.2-5 or later before upgrading your Kubernetes cluster. (RED-93025)\n\n* Long cluster names cause routes to be rejected  (RED-25871)\n\n  A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name will exceed 63 characters. The workaround is to limit cluster name to 20 characters or less.\n\n* Cluster CR (REC) errors are not reported after invalid updates (RED-25542)\n  \n  A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence.\n\n* An unreachable cluster has status running (RED-32805)\n  \n  When a cluster is in an unreachable state, the state is still `running` instead of being reported as an error.\n\n* Readiness probe incorrect on failures (RED-39300)\n\n  STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure.\n\n* Role missing on replica sets (RED-39002)\n\n  The `redis-enterprise-operator` role is missing permission on replica sets.\n\n* Private registries are not supported on OpenShift 3.11 (RED-38579)\n\n  OpenShift 3.11 does not support DockerHub private registries. This is a known OpenShift issue.\n\n* Internal DNS and Kubernetes DNS may have conflicts (RED-37462)\n\n  DNS conflicts are possible between the cluster `mdns_server` and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names.\n\n* 5.4.10 negatively impacts 5.4.6 (RED-37233)\n\n  Kubernetes-based 5.4.10 deployments seem to negatively impact existing 5.4.6 deployments that share a Kubernetes cluster.\n\n* Node CPU usage is reported instead of pod CPU usage (RED-36884)\n\n  In Kubernetes, the node CPU usage we report on is the usage of the Kubernetes worker node hosting the REC pod.\n\n* Clusters must be named \"rec\" in OLM-based deployments (RED-39825)\n\n  In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) are bound to a specific service account name (i.e., \"rec\"). The workaround is to name the cluster \"rec\".\n\n* REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED-47254)\n\n  When REC clusters are deployed on Kubernetes clusters with unsynchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes.\n\n* Deleting an OpenShift project with an REC deployed may hang (RED-47192)\n\n  When a REC cluster is deployed in a project (namespace) and has REDB resources, the REDB resources must be deleted first before the REC can be deleted. As such, until the REDB resources are deleted, the project deletion will hang. The fix is to delete the REDB resources first and the REC second. Afterwards, you may delete the project.\n\n* REC extraLabels are not applied to PVCs on K8s versions 1.15 or older (RED-51921)\n\n  In K8s 1.15 or older, the PVC labels come from the match selectors and not the PVC templates. As such, these versions cannot support PVC labels. If this feature is required, the only fix is to upgrade the K8s cluster to a newer version.\n\n* Hashicorp Vault integration - no support for Gesher (RED-55080)\n\n  There is no workaround at this time.\n\n* REC might report error states on initial startup (RED-61707)\n\n  There is no workaround at this time except to ignore the errors.\n\n* PVC size issues when using decimal value in spec (RED-62132)\n\n  The workaround for this issue is to make sure you use integer values for the PVC size.\n\n* Following old revision of quick start guide causes issues creating an REDB due to unrecognized memory field name (RED-69515)\n\nThe workaround is to use the newer (current) revision of the quick start document available online.\n\n* `autoUpgrade` set to true by operator might cause unexpected bdb upgrades when `redisUpgradePolicy` is set to true (RED-72351)\n\n  Contact support if your deployment is impacted.\n\n* Procedure to update credentials might be problematic on OpenShift when accessing the cluster externally using routes (RS issue)(RED-73251)(RED-75329)\n  To workaround this, access the API from within the K8s cluster."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-2-12-1","title":"Redis Enterprise for Kubernetes release notes 6.2.12-1 (Sept 2022)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-2-12-1/","summary":"Support added for additional distributions, as well as feature improvements and bug fixes.","aliases":["/operate/kubernetes/release-notes/k8s-6-2-12-1/","/operate/kubernetes/release-notes/6-2-releases/k8s-6-2-12-1/"],"tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"28366b4532818a3beb6d9b7fde04602ae7bf6bf40df5e7da129cf5e49d287cd5","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The Redis Enterprise K8s 6.2.12-1 supports the Redis Enterprise Software release 6.2.12 and includes feature improvements and bug fixes.\n\nThe key bug fixes and known limitations are described below."},{"id":"images","title":"Images","role":"content","text":"This release includes the following container images:\n\n* **Redis Enterprise**: `redislabs/redis:6.2.12-82` or  `redislabs/redis:6.2.12-82.rhel8-openshift`\n* **Operator**: `redislabs/operator:6.2.12-1`\n* **Services Rigger**: `redislabs/k8s-controller:6.2.12-1` or `redislabs/services-manager:6.2.12-1` (on the Red Hat registry)"},{"id":"feature-improvements","title":"Feature improvements","role":"content","text":"* Redis Enterprise Software 6.2.12 support (RED-83829)\n* Added support for annotations on services created by Redis Enterprise (RED-56245)\n* Support for additional builds of same Redis Software version with same operator version. The list of supported builds will be published. (RED-78757)"},{"id":"bug-fixes","title":"Bug fixes","role":"content","text":"* Fixed Golang related vulnerabilities (RED-79205)\n* Log collector creating larger packages (RED-79650)\n* Log collector crashes when Redis Enterprise cluster is not running (RED-79996)\n* Redis Enterprise Software pods termination process could lead, in some circumstances, to pod shutdown without proper failover of its master shards. This has been fixed so that the failover attempts are retried indefinitely within the grace period, which by default is 1 year. The grace period is controllable via the redisEnterpriseTerminationGracePeriodSeconds parameter in the REC (but note that shutting down a pod without the proper failovers may lead to data loss).(RED-75388)"},{"id":"api-changes","title":"API changes","role":"content","text":"The Redis Enterprise cluster `podSecurityPolicy` is deprecated. This is still supported but will be removed when all K8s versions supporting the feature are removed."},{"id":"compatibility-notes","title":"Compatibility notes","role":"compatibility","text":"Below is a table showing supported distributions at the time of this release. See [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for the current list of supported distributions.\n\n| **Kubernetes version**  | 1.20       | 1.21       | 1.22       | 1.23       | 1.24       |\n|:------------------------|:----------:|:----------:|:----------:|:----------:|:----------:|\n | Community Kubernetes   |            |            | supported  | supported  | supported |\n| Amazon EKS              |            | deprecated | supported  | supported* |\n| Azure AKS               |            |            | supported  | supported  | supported* |\n| Google GKE              |            | deprecated | supported  | supported  | supported* |\n| Rancher 2.6             |            | supported  | supported  | supported* |            |\n| **OpenShift version**   | **4.7**    | **4.8**    | **4.9**    | **4.10**   | **4.11**   |\n|                         | deprecated | deprecated | supported  | supported  | supported* |\n| **VMware TKGI version** | **1.11**   | **1.12**   | **1.13**   | **1.14**   | **1.15**   |\n|                         | deprecated | deprecated | supported* | supported* |            |\n\n\\* Support added in this release"},{"id":"support-added","title":"Support added","role":"compatibility","text":"* Azure AKS 1.24\n* Amazon EKS 1.23\n* Google GKE 1.24\n* OpenShift 4.11\n* Rancher 1.23\n* VMware TKGI 1.14"},{"id":"deprecated","title":"Deprecated","role":"content","text":"* Amazon EKS 1.21\n* Google GKE 1.21\n* OpenShift 4.7\n* OpenShift 4.8\n* VMware TKGI 1.11\n* VMware TKGI 1.12"},{"id":"no-longer-supported","title":"No longer supported","role":"content","text":"* Community Kubernetes 1.21\n* Amazon EKS 1.19, 1.20\n* Azure AKS 1.21\n* Google GKE 1.19, 1.20\n* Rancher 2.6 1.19, 1.20\n* VMware TKGI 1.10"},{"id":"before-upgrading","title":"Before upgrading","role":"content","text":"Be aware the following changes included in this release affect the upgrade process. Please read carefully before upgrading."},{"id":"supported-upgrade-paths","title":"Supported upgrade paths","role":"compatibility","text":"If you are using operator version 6.2.8-15 or earlier, you cannot upgrade directly to operator versions 6.2.12 through 6.4.2-5. You must upgrade to operator version 6.2.10-45 before you can upgrade to operator versions between 6.2.12 and 6.4.2-5. However, upgrades directly to operator version 6.4.2-6 are supported."},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"large-clusters","title":"Large clusters","role":"content","text":"On clusters with more than 9 REC nodes, a Kubernetes upgrade can render the Redis cluster unresponsive in some cases. A fix is available in the 6.4.2-5 release. Upgrade your operator version to 6.4.2-5 or later before upgrading your Kubernetes cluster. (RED-93025)\n\n* Long cluster names cause routes to be rejected  (RED-25871)\n\n  A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name will exceed 63 characters. The workaround is to limit cluster name to 20 characters or less.\n\n* Cluster CR (REC) errors are not reported after invalid updates (RED-25542)\n  \n  A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence.\n\n* An unreachable cluster has status running (RED-32805)\n  \n  When a cluster is in an unreachable state, the state is still `running` instead of being reported as an error.\n\n* Readiness probe incorrect on failures (RED-39300)\n\n  STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure.\n\n* Role missing on replica sets (RED-39002)\n\n  The `redis-enterprise-operator` role is missing permission on replica sets.\n\n* Private registries are not supported on OpenShift 3.11 (RED-38579)\n\n  OpenShift 3.11 does not support DockerHub private registries. This is a known OpenShift issue.\n\n* Internal DNS and Kubernetes DNS may have conflicts (RED-37462)\n\n  DNS conflicts are possible between the cluster `mdns_server` and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names.\n\n* 5.4.10 negatively impacts 5.4.6 (RED-37233)\n\n  Kubernetes-based 5.4.10 deployments seem to negatively impact existing 5.4.6 deployments that share a Kubernetes cluster.\n\n* Node CPU usage is reported instead of pod CPU usage (RED-36884)\n\n  In Kubernetes, the node CPU usage we report on is the usage of the Kubernetes worker node hosting the REC pod.\n\n* Clusters must be named \"rec\" in OLM-based deployments (RED-39825)\n\n  In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) are bound to a specific service account name (i.e., \"rec\"). The workaround is to name the cluster \"rec\".\n\n* REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED-47254)\n\n  When REC clusters are deployed on Kubernetes clusters with unsynchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes.\n\n* Deleting an OpenShift project with an REC deployed may hang (RED-47192)\n\n  When a REC cluster is deployed in a project (namespace) and has REDB resources, the REDB resources must be deleted first before the REC can be deleted. As such, until the REDB resources are deleted, the project deletion will hang. The fix is to delete the REDB resources first and the REC second. Afterwards, you may delete the project.\n\n* REC extraLabels are not applied to PVCs on K8s versions 1.15 or older (RED-51921)\n\n  In K8s 1.15 or older, the PVC labels come from the match selectors and not the PVC templates. As such, these versions cannot support PVC labels. If this feature is required, the only fix is to upgrade the K8s cluster to a newer version.\n\n* Hashicorp Vault integration - no support for Gesher (RED-55080)\n\n  There is no workaround at this time.\n\n* REC might report error states on initial startup (RED-61707)\n\n  There is no workaround at this time except to ignore the errors.\n\n* PVC size issues when using decimal value in spec (RED-62132)\n\n  The workaround for this issue is to make sure you use integer values for the PVC size.\n\n* Following old revision of quick start guide causes issues creating an REDB due to unrecognized memory field name (RED-69515)\n\n  The workaround is to use the newer (current) revision of the quick start document available online.\n\n* `autoUpgrade` set to true by operator might cause unexpected bdb upgrades when `redisUpgradePolicy` is set to true (RED-72351)\n\n  Contact support if your deployment is impacted.\n\n* Procedure to update credentials might be problematic on OpenShift when accessing the cluster externally using routes (RS issue)(RED-73251)(RED-75329)\n  To workaround this, access the API from within the K8s cluster.\n\n* On Windows, `log_collector` doesn't recognize the namespace given with the `-n` flag (RED-83532)\n\n  To workaround this, use a different operating system.\n\n* Active-Active database creation will fail if the ingress class annotation is not exactly \"nginx\" when using Nginx ingress controller (RED-83070)"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-2-18-3","title":"Redis Enterprise for Kubernetes release notes 6.2.18-3 (Nov 2022)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-2-18-3/","summary":"Support added for additional distributions, as well as feature improvements and bug fixes.","aliases":["/operate/kubernetes/release-notes/6-2-18-releases/k8s-6-2-18-41/","/operate/kubernetes/release-notes/6-2-releases/k8s-6-2-18-3/","/operate/kubernetes/release-notes/6-2-18-releases/k8s-6-2-18-3/"],"tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"56085fef38f7240e5530398332a80af0027aba345a297d8ef529ac6db258615c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The Redis Enterprise K8s 6.2.18-3 supports the Redis Enterprise Software release 6.2.18 and includes feature improvements and bug fixes.\n\nThe key bug fixes, new features, and known limitations are described below.\n\n> [!WARNING]\n> If you use NGINX as an ingress controller for Redis Enterprise, **do not upgrade** to the 6.2.18-3 release. Skip this release and upgrade to version [6.2.18-41](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases) instead."},{"id":"images","title":"Images","role":"content","text":"This release includes the following container images:\n\n* **Redis Enterprise**: `redislabs/redis:6.2.18-58` or  `redislabs/redis:6.2.18-58.rhel8-openshift` (or `redislabs/redis:6.2.18-58.rhel7-openshift` if upgrading from RHEL 7)\n* **Operator**: `redislabs/operator:6.2.18-3`\n* **Services Rigger**: `redislabs/k8s-controller:6.2.18-3` or `redislabs/services-manager:6.2.18-3` (on the Red Hat registry)"},{"id":"new-features","title":"New features","role":"content","text":"* Support for [Redis Flex](https://redis.io/docs/latest/operate/kubernetes/flex) (RED-78613)"},{"id":"feature-improvements","title":"Feature improvements","role":"content","text":"* The `podSecurityPolicyName` field in `RedisEnterpriseCluster` resources is now deprecated for Kubernetes versions 1.24 or earlier, and invalid for 1.25 and later. Customers are advised to switch to using the [`PodSecurityAdmission`](https://kubernetes.io/docs/concepts/security/pod-security-admission/) or alternative methods to enforce pod security (RED-81921).\n\n* Added support for VMware [Tanzu Kubernetes Grid (TKG)](https://docs.vmware.com/en/VMware-Tanzu-Kubernetes-Grid/index.html), in addition to [Tanzu Kubernetes Grid Integration Edition (TKGI)](https://techdocs.broadcom.com/us/en/vmware-tanzu/standalone-components/tanzu-kubernetes-grid-integrated-edition/1-25/tkgi/index.html) that was previously and is still supported (RED-65630).\n\n* Added support for PEM encryption through the [Redis Enterprise cluster API](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api)(RED-78613).\n\n* Hardened security context constraints to align with standards for OpenShift 4.11 (RED-83215).\n\n* Changed log collector default to avoid collection of non-Redis Enterprise logs and items (RED-83216).\n\n* Allowed configuration of the Redis Enterprise cluster (REC) service type (RED-84644)."},{"id":"bug-fixes","title":"Bug fixes","role":"content","text":"* Allow any ingress class name annotation when using NGINX ingress controller. This is no longer required to be exactly `nginx` (RED-79205).\n* Fixed log collector handling of namespace parameter on Windows (RED-83532).\n* Fixed issue with updating credentials on Openshift when accessing the cluster externally with routes (RED-73251, RED-75329)."},{"id":"api-changes","title":"API changes","role":"content","text":"The following fields were added to the [Redis Enterprise cluster (REC) API](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api):\n\n* Added `.services.apiService.type` to allow configuration of the API service type.\n* Made `.redisOnFlashSpec` available by default.\n* Made `.ocspConfiguration` available by default for configuring OCSP stapling.\n* Made `.encryptPkeys` available by default for configuring PEM encryption.\n\nThe following fields were added to the [Redis Enterprise database (REDB) API](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api):\n\n* `.isRoF` and `.rofRamSize` added to support the [Redis Flex feature](https://redis.io/docs/latest/operate/kubernetes/flex)."},{"id":"compatibility-notes","title":"Compatibility notes","role":"compatibility","text":"Below is a table showing supported distributions at the time of this release. See [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for the current list of supported distributions.\n\n| **Kubernetes version**  | 1.21       | 1.22       | 1.23       | 1.24       | 1.25       |\n|:------------------------|:----------:|:----------:|:----------:|:----------:|:----------:|\n| Community Kubernetes    |            | supported  | supported  | supported  | supported  |\n| Amazon EKS              | deprecated | supported  | supported  |            |            |\n| Azure AKS               |            | supported  | supported  | supported  |            |\n| Google GKE              | deprecated | supported  | supported  | supported  | supported* |\n| Rancher 2.6             | supported  | supported  | supported  | supported* |            |\n| VMware TKG 1.6          |            | supported* | supported* |            |            |\n| **OpenShift version**   | **4.8**    | **4.9**    | **4.10**   | **4.11**   |            |\n|                         |            | deprecated | supported  | supported* |            |\n| **VMware TKGI version** | **1.12**   | **1.13**   | **1.14**   | **1.15**   |            |\n|                         |            | supported  | supported  | supported* |            |\n\n\\* Support added in this release"},{"id":"before-upgrading","title":"Before upgrading","role":"content","text":"Be aware the following changes included in this release affect the upgrade process. Please read carefully before upgrading."},{"id":"supported-upgrade-paths","title":"Supported upgrade paths","role":"compatibility","text":"If you are using operator version 6.2.8-15 or earlier, you cannot upgrade directly to operator versions 6.2.12 through 6.4.2-5. You must upgrade to operator version 6.2.10-45 before you can upgrade to operator versions between 6.2.12 and 6.4.2-5. However, upgrades directly to operator version 6.4.2-6 are supported."},{"id":"openshift-versions-6212-or-earlier","title":"OpenShift versions 6.2.12 or earlier","role":"content","text":"Due to a change in the SCC, on OpenShift clusters running version 6.2.12 or earlier upgrading to version 6.2.18 or later, where `node:1` is <b>not</b> the master node, the upgrade might get stuck.\n\nFor more info and steps to prevent this issue, see [upgrade a Redis Enterprise cluster (REC)](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#before-upgrading).\n\nOperator version 6.4.2-6 includes a fix for this issue."},{"id":"large-clusters","title":"Large clusters","role":"content","text":"On clusters with more than 9 REC nodes, a Kubernetes upgrade can render the Redis cluster unresponsive in some cases. A fix is available in the 6.4.2-5 release. Upgrade your operator version to 6.4.2-5 or later before upgrading your Kubernetes cluster."},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"large-clusters-1","title":"Large clusters","role":"content","text":"On clusters with more than 9 REC nodes, a Kubernetes upgrade can render the Redis cluster unresponsive in some cases. A fix is available in the 6.4.2-5 release. Upgrade your operator version to 6.4.2-5 or later before upgrading your Kubernetes cluster. (RED-93025)\n\n* Long cluster names cause routes to be rejected  (RED-25871)\n\n  A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name will exceed 63 characters. The workaround is to limit cluster name to 20 characters or less.\n\n* Cluster CR (REC) errors are not reported after invalid updates (RED-25542)\n  \n  A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence.\n\n* An unreachable cluster has status running (RED-32805)\n  \n  When a cluster is in an unreachable state, the state is still `running` instead of being reported as an error.\n\n* Readiness probe incorrect on failures (RED-39300)\n\n  STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure.\n\n* Internal DNS and Kubernetes DNS may have conflicts (RED-37462)\n\n  DNS conflicts are possible between the cluster `mdns_server` and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names.\n\n* 5.4.10 negatively impacts 5.4.6 (RED-37233)\n\n  Kubernetes-based 5.4.10 deployments seem to negatively impact existing 5.4.6 deployments that share a Kubernetes cluster.\n\n* Node CPU usage is reported instead of pod CPU usage (RED-36884)\n\n  In Kubernetes, the node CPU usage we report on is the usage of the Kubernetes worker node hosting the REC pod.\n\n* Clusters must be named \"rec\" in OLM-based deployments (RED-39825)\n\n  In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) are bound to a specific service account name (i.e., \"rec\"). The workaround is to name the cluster \"rec\".\n\n* REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED-47254)\n\n  When REC clusters are deployed on Kubernetes clusters with unsynchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes.\n\n* Deleting an OpenShift project with an REC deployed may hang (RED-47192)\n\n  When a REC cluster is deployed in a project (namespace) and has REDB resources, the REDB resources must be deleted first before the REC can be deleted. As such, until the REDB resources are deleted, the project deletion will hang. The fix is to delete the REDB resources first and the REC second. Afterwards, you may delete the project.\n\n* Hashicorp Vault integration - no support for Gesher (RED-55080)\n\n  There is no workaround at this time.\n\n* REC might report error states on initial startup (RED-61707)\n\n  There is no workaround at this time except to ignore the errors.\n\n* PVC size issues when using decimal value in spec (RED-62132)\n\n  The workaround for this issue is to make sure you use integer values for the PVC size.\n\n* Following old revision of quick start guide causes issues creating an REDB due to unrecognized memory field name (RED-69515)\n\n  The workaround is to use the newer (current) revision of the quick start document available online.\n\n* `autoUpgrade` set to true by operator might cause unexpected bdb upgrades when `redisUpgradePolicy` is set to true (RED-72351)\n\n  Contact support if your deployment is impacted."},{"id":"security","title":"Security","role":"security","text":"For a list of fixes related to CVEs, see the [Redis Enterprise 6.2.18-58 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-18-releases/rs-6-2-18-58#security)."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-2-18-41","title":"Redis Enterprise for Kubernetes release notes 6.2.18-41 (Dec 2022)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-2-18-41/","summary":"This is a maintenance release for 6.2.18 and includes bug fixes.","aliases":["/operate/kubernetes/release-notes/6-2-18-releases/k8s-6-2-18-41/","/operate/kubernetes/release-notes/6-2-releases/k8s-6-2-18-41/"],"tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"8e35ea2790ada65c5d1e9355533bda5531130b3f64ddcfbf723cb2d0868a775b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The Redis Enterprise K8s 6.2.18-41 is a maintenance release which supports the Redis Enterprise Software release 6.2.18 and contains bug fixes.\n\nThe key bug fixes and known limitations are described below.\n\n> [!WARNING]\n>  Due to a change in the SCC, on OpenShift clusters running version 6.2.12 or earlier upgrading to version 6.2.18 or later, where `node:1` is <b>not</b> the master node, the upgrade might get stuck.\n>\n> For more info and steps to prevent this issue, see [upgrade a Redis Enterprise cluster (REC)](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#before-upgrading)."},{"id":"images","title":"Images","role":"content","text":"* **Redis Enterprise**: `redislabs/redis:6.2.18-65`\n* **Operator**: `redislabs/operator:6.2.18-41`\n* **Services Rigger**: `redislabs/k8s-controller:6.2.18-41`"},{"id":"openshift-images","title":"OpenShift images","role":"content","text":"**OLM operator version**: `v6.2.18-41a`\n\n\n* **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:6.2.18-65.rhel8-openshift`\n    (or `redislabs/redis-enterprise:6.2.18-65.rhel7-openshift` if upgrading from RHEL 7)\n* **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:6.2.18-41`\n* **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:6.2.18-41`"},{"id":"bug-fixes","title":"Bug fixes","role":"content","text":"* Fixed issues with incorrect image digest (OLM/OpenShift) (RED-88863)\n* Fixed upgrade issue for using NGINX-based ingress with Active-Active databases. (RED-88882)\n* Upgraded to Redis Software 6.2.18-65 (RED-88985)\n* Fixed upgrade issue with OLM (RED-89170)\n* Removed non-core Python dependencies from the log collector (RED-90108)\n* Fixed issues with running log collector on OLM (RED-90129"},{"id":"compatibility-notes","title":"Compatibility notes","role":"compatibility","text":"Below is a table showing supported distributions at the time of this release. See [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for the current list of supported distributions.\n\n| **Kubernetes version**  | 1.21       | 1.22       | 1.23       | 1.24       | 1.25       |\n|:------------------------|:----------:|:----------:|:----------:|:----------:|:----------:|\n| Community Kubernetes    |            | supported  | supported  | supported  | supported  |\n| Amazon EKS              | deprecated | supported  | supported  |            |            |\n| Azure AKS               |            | supported  | supported  | supported  |            |\n| Google GKE              | deprecated | supported  | supported  | supported  | supported  |\n| Rancher 2.6             | supported  | supported  | supported  | supported  |            |\n| VMware TKG 1.6          |            | supported  | supported  |            |            |\n| **OpenShift version**   | **4.8**    | **4.9**    | **4.10**   | **4.11**   |            |\n|                         |            | deprecated | supported  | supported  |            |\n| **VMware TKGI version** | **1.12**   | **1.13**   | **1.14**   | **1.15**   |            |\n|                         |            | supported  | supported  | supported  |            |\n\n\\* Support added in this release"},{"id":"before-upgrading","title":"Before upgrading","role":"content","text":"Be aware the following changes included in this release affect the upgrade process. Please read carefully before upgrading.\n\n ### Supported upgrade paths\n\n  If you are using a version earlier than 6.2.10-45, you cannot upgrade directly to this release. You must upgrade to 6.2.10-45 before you can upgrade to versions 6.2.18 or later."},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"large-clusters","title":"Large clusters","role":"content","text":"On clusters with more than 9 REC nodes, a Kubernetes upgrade can render the Redis cluster unresponsive in some cases. A fix is available in the 6.4.2-5 release. Upgrade your operator version to 6.4.2-5 or later before upgrading your Kubernetes cluster. (RED-93025)\n\n* Long cluster names cause routes to be rejected  (RED-25871)\n\n  A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name will exceed 63 characters. The workaround is to limit cluster name to 20 characters or less.\n\n* Cluster CR (REC) errors are not reported after invalid updates (RED-25542)\n  \n  A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence.\n\n* An unreachable cluster has status running (RED-32805)\n  \n  When a cluster is in an unreachable state, the state is still `running` instead of being reported as an error.\n\n* Readiness probe incorrect on failures (RED-39300)\n\n  STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure.\n\n* Internal DNS and Kubernetes DNS may have conflicts (RED-37462)\n\n  DNS conflicts are possible between the cluster `mdns_server` and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names.\n\n* 5.4.10 negatively impacts 5.4.6 (RED-37233)\n\n  Kubernetes-based 5.4.10 deployments seem to negatively impact existing 5.4.6 deployments that share a Kubernetes cluster.\n\n* Node CPU usage is reported instead of pod CPU usage (RED-36884)\n\n  In Kubernetes, the node CPU usage we report on is the usage of the Kubernetes worker node hosting the REC pod.\n\n* Clusters must be named \"rec\" in OLM-based deployments (RED-39825)\n\n  In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) are bound to a specific service account name (i.e., \"rec\"). The workaround is to name the cluster \"rec\".\n\n* REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED-47254)\n\n  When REC clusters are deployed on Kubernetes clusters with unsynchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes.\n\n* Deleting an OpenShift project with an REC deployed may hang (RED-47192)\n\n  When a REC cluster is deployed in a project (namespace) and has REDB resources, the REDB resources must be deleted first before the REC can be deleted. As such, until the REDB resources are deleted, the project deletion will hang. The fix is to delete the REDB resources first and the REC second. Afterwards, you may delete the project.\n\n* Hashicorp Vault integration - no support for Gesher (RED-55080)\n\n  There is no workaround at this time.\n\n* REC might report error states on initial startup (RED-61707)\n\n  There is no workaround at this time except to ignore the errors.\n\n* PVC size issues when using decimal value in spec (RED-62132)\n\n  The workaround for this issue is to make sure you use integer values for the PVC size.\n\n* Following old revision of quick start guide causes issues creating an REDB due to unrecognized memory field name (RED-69515)\n\n  The workaround is to use the newer (current) revision of the [quick start document](https://redis.io/docs/latest/operate/kubernetes/deployment/quick-start) available online.\n\n* `autoUpgrade` set to true by operator might cause unexpected bdb upgrades when `redisUpgradePolicy` is set to true (RED-72351)\n\n  Contact support if your deployment is impacted."},{"id":"security","title":"Security","role":"security","text":"For a list of fixes related to CVEs, see the [Redis Enterprise 6.2.18-65 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-18-releases/rs-6-2-18-65#security)."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-2-18-41-jan-2023","title":"Redis Enterprise for Kubernetes release notes 6.2.18-41 (Jan 2023)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-2-18-41-jan-2023/","summary":"This is a maintenance release for 6.2.18 and includes support for Redis Enterprise 6.2.18-72.","aliases":["/operate/kubernetes/release-notes/6-2-18-releases/k8s-6-2-18-41-jan-2023/","/operate/kubernetes/release-notes/6-2-releases/k8s-6-2-18-41-jan-2023/"],"tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"87500be1ecaa3ef22919636e515c1ceb9338471c92dbb47e0a981adecdf22d0b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release of Redis Enterprise for Kubernetes 6.2.18-41 that adds supports for Redis Enterprise 6.2.18-72.\n\nNew images and fixes are listed below. Refer to [6.2.18-41 (Dec 2022](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-2-18-41) for compatibility notes and known limitations.\n\n> [!WARNING]\n>  Due to a change in the SCC, on OpenShift clusters running version 6.2.12 or earlier upgrading to version 6.2.18 or later, where `node:1` is <b>not</b> the master node, the upgrade might get stuck.\n>\n> For more info and steps to prevent this issue, see [upgrade a Redis Enterprise cluster (REC)](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#before-upgrading)."},{"id":"images","title":"Images","role":"content","text":"DockerHub images are available at `docker.io/`.\n\n* **Redis Enterprise**: `redislabs/redis:6.2.18-72`\n* **Operator**: `redislabs/operator:6.2.18-41`\n* **Services Rigger**: `redislabs/k8s-controller:6.2.18-41`"},{"id":"openshift-images","title":"OpenShift images","role":"content","text":"**OLM operator version**: `v6.2.18-41c`\n\n\n* **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:6.2.18-72.rhel8-openshift`\n    (or `registry.connect.redhat.com/redislabs/redis-enterprise:6.2.18-72.rhel7-openshift` if upgrading from RHEL 7)\n* **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:6.2.18-41`\n* **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:6.2.18-41`"},{"id":"feature-enhancements","title":"Feature enhancements","role":"content","text":"* Upgraded to support Redis Enterprise 6.2.18-72"},{"id":"compatibility-notes","title":"Compatibility notes","role":"compatibility","text":"See [Redis Enterprise for Kubernetes release notes 6.2.18-41 (Dec 2022)](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-2-18-41)."},{"id":"before-upgrading","title":"Before upgrading","role":"content","text":"Be aware the following changes included in this release affect the upgrade process. Please read carefully before upgrading.\n\n ### Supported upgrade paths\n\n  If you are using a version earlier than 6.2.10-45, you cannot upgrade directly to this release. You must upgrade to 6.2.10-45 before you can upgrade to versions 6.2.18 or later."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"See [Redis Enterprise for Kubernetes release notes 6.2.18-41 (Dec 2022)](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-2-18-41)."},{"id":"security","title":"Security","role":"security","text":"For a list of fixes related to CVEs, see the [Redis Enterprise 6.2.18-72 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-18-releases/rs-6-2-18-70#security)."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-2-4-1-2021-09","title":"Redis Enterprise for Kubernetes Release Notes 6.2.4-1 (September 2021)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-2-4-1-2021-09/","summary":"Support for RS 6.2.4 and OpenShift 4.8. Support for K8s 1.19 (EKS and AKS), K8s 1.20 (Rancher, EKS, AKS), K8s 1.21 (GKE and kOps).","aliases":["/operate/kubernetes/release-notes/k8s-6-2-4-1-2021-09/","/operate/kubernetes/release-notes/6-2-releases/k8s-6-2-4-1-2021-09/"],"tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"5a528d134f11a9a1885172622fd4b8b96ec95668a102e40c89f648d1484537aa","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The Redis Enterprise [K8s 6.2.4-1](https://github.com/RedisLabs/redis-enterprise-k8s-docs/releases/tag/v6.2.4-1) release provides support for the [Redis Enterprise Software release 6.2.4](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-4-august-2021) and includes several enhancements and bug fixes.\n\nThe key new features, bug fixes, and known limitations are described below."},{"id":"images","title":"Images","role":"content","text":"This release includes the following container images:\n\n* **Redis Enterprise**: `redislabs/redis:6.2.4-55` or  `redislabs/redis:6.2.4-55.rhel7-openshift`\n* **Operator**: `redislabs/operator:6.2.4-1`\n* **Services Rigger**: `redislabs/k8s-controller:6.2.4-1` or `redislabs/services-manager:6.2.4-1` (on the Red Hat registry)"},{"id":"new-features","title":"New features","role":"content","text":"* Internode encryption configuration through K8s custom resources (RED-59699, RED-60318)"},{"id":"feature-improvements","title":"Feature improvements","role":"content","text":"* Support for addition attribute in REDB secret containing comma separated list of service names (RED-48469)\n* Support OpenShift 4.8 (K8s 1.21) (RED-59424)\n* Support K8s 1.21 - GKE (RED-59048)\n* Support K8s 1.21 - kOps (RED-59047)\n* Support K8s 1.19-1.21 - EKS (RED-60287)\n* Support K8s 1.19, 1.20 - AKS (RED-59050)\n* Support K8s 1.20 - Rancher (RED-59049)"},{"id":"fixed-bugs","title":"Fixed bugs","role":"content","text":"* Fixed issue with RS pods not recovering from container failures (RED-53042)\n* Fixed rare problem of Redis Enterprise pod restarting too early while statefulSet was rolling out, causing quorum loss (RED-53042)\n* Improved Github public documentation around using the admission controller with multiple namespaces (RED-59915)\n* Fixed integration issues with HashiCorp Vault enterprise namespaces and custom auth paths (RED-61273)"},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"large-clusters","title":"Large clusters","role":"content","text":"On clusters with more than 9 REC nodes, a Kubernetes upgrade can render the Redis cluster unresponsive in some cases. A fix is available in the 6.4.2-5 release. Upgrade your operator version to 6.4.2-5 or later before upgrading your Kubernetes cluster. (RED-93025)"},{"id":"applying-bundleyaml-generated-a-warning","title":"Applying bundle.yaml generated a warning","role":"content","text":"When applying bundle.yaml on K8s clusters 1.20 and above, the following warning is generated: \n[code example]\nThe warning can be safely ignored. A future release will leverage the updated API version for CustomResourceDefinition, once support for K8s 1.22 is introduced."},{"id":"enabling-the-kubernetes-webhook-generates-a-warning","title":"Enabling the Kubernetes webhook generates a warning","role":"content","text":"When [installing](https://github.com/RedisLabs/redis-enterprise-k8s-docs#installation) and configuring the admission controller webhook (step 5), the following warning is generated: \n[code example]\nThe warning can be safely ignored. A future release will leverage the updated API version for ValidatingWebhookConfiguration, once support for K8s 1.22 is introduced."},{"id":"hashicorp-vault-integration---no-support-for-gesher-red-55080","title":"Hashicorp Vault integration - no support for Gesher (RED-55080)","role":"content","text":"There is no workaround at this time"},{"id":"long-cluster-names-cause-routes-to-be-rejected--red-25871","title":"Long cluster names cause routes to be rejected  (RED-25871)","role":"content","text":"A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name will exceed 63 characters. The workaround is to limit cluster name to 20 characters or fewer."},{"id":"cluster-cr-rec-errors-are-not-reported-after-invalid-updates-red-25542","title":"Cluster CR (REC) errors are not reported after invalid updates (RED-25542)","role":"content","text":"A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence."},{"id":"an-unreachable-cluster-has-status-running-red-32805","title":"An unreachable cluster has status running (RED-32805)","role":"content","text":"When a cluster is in an unreachable state, the state is still `running` instead of being reported as an error."},{"id":"readiness-probe-incorrect-on-failures-red-39300","title":"Readiness probe incorrect on failures (RED-39300)","role":"content","text":"STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on the node fails."},{"id":"role-missing-on-replica-sets-red-39002","title":"Role missing on replica sets (RED-39002)","role":"content","text":"The `redis-enterprise-operator` role is missing permission on replica sets."},{"id":"private-registries-are-not-supported-on-openshift-311-red-38579","title":"Private registries are not supported on OpenShift 3.11 (RED-38579)","role":"content","text":"OpenShift 3.11 does not support DockerHub private registries. This is a known OpenShift issue."},{"id":"internal-dns-and-kubernetes-dns-may-have-conflicts-red-37462","title":"Internal DNS and Kubernetes DNS may have conflicts (RED-37462)","role":"content","text":"DNS conflicts are possible between the cluster `mdns_server` and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names."},{"id":"5410-negatively-impacts-546-red-37233","title":"5.4.10 negatively impacts 5.4.6 (RED-37233)","role":"content","text":"Kubernetes-based 5.4.10 deployments seem to negatively impact existing 5.4.6 deployments that share a Kubernetes cluster."},{"id":"node-cpu-usage-is-reported-instead-of-pod-cpu-usage-red-36884","title":"Node CPU usage is reported instead of pod CPU usage (RED-36884)","role":"content","text":"In Kubernetes, the node CPU usage we report on is the usage of the Kubernetes worker node hosting the REC pod."},{"id":"clusters-must-be-named-rec-in-olm-based-deployments-red-39825","title":"Clusters must be named \"rec\" in OLM-based deployments (RED-39825)","role":"content","text":"In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) are bound to a specific service account name (i.e., \"rec\"). The workaround is to name the cluster \"rec\"."},{"id":"rec-clusters-fail-to-start-on-kubernetes-clusters-with-unsynchronized-clocks-red-47254","title":"REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED-47254)","role":"content","text":"When REC clusters are deployed on Kubernetes clusters with unsynchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes."},{"id":"deleting-an-openshift-project-with-an-rec-deployed-may-hang-red-47192","title":"Deleting an OpenShift project with an REC deployed may hang (RED-47192)","role":"content","text":"When a REC cluster is deployed in a project (namespace) and has REDB resources, the\nREDB resources must be deleted first before the REC can be deleted. As such, until the\nREDB resources are deleted, the project deletion will hang. The fix is to delete the\nREDB resources first and the REC second. Afterwards, you may delete the project."},{"id":"rec-extralabels-are-not-applied-to-pvcs-on-k8s-versions-115-or-older-red-51921","title":"REC extraLabels are not applied to PVCs on K8s versions 1.15 or older (RED-51921)","role":"content","text":"In K8s 1.15 or older, the PVC labels come from the match selectors and not the\nPVC templates. As such, these versions can not support PVC labels. If this feature\nis required, the only fix is to upgrade the K8s cluster to a newer version."},{"id":"rec-might-report-error-states-on-initial-startup-red-61707","title":"REC might report error states on initial startup (RED-61707)","role":"content","text":"There is no workaround at this time except to ignore the errors."},{"id":"pvc-size-issues-when-using-decimal-value-in-spec-red-62132","title":"PVC size issues when using decimal value in spec (RED-62132)","role":"content","text":"The workaround for this issue is to make sure you use integer values for the PVC size."},{"id":"long-rec-names-or-namespace-names-cause-failures-red-62222","title":"Long REC names or namespace names cause failures (RED-62222)","role":"content","text":"The combined length of the REC name and namespace name must be equal to or less than 45 characters."},{"id":"compatibility-notes","title":"Compatibility Notes","role":"compatibility","text":"See [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for the full list of supported distributions."},{"id":"now-supported","title":"Now supported","role":"content","text":"* OpenShift 4.8\n* GKE K8s version 1.21\n* kOps K8s version 1.21\n* EKS K8s versions 1.19-1.21\n* AKS K8s versions 1.19-1.20\n* Rancher K8s version 1.20"},{"id":"no-longer-supported","title":"No longer supported","role":"content","text":"* GKE K8s version 1.17 (previously deprecated)\n* kOps K8s version 1.15 (previously deprecated)"},{"id":"deprecation-notice","title":"Deprecation notice","role":"content","text":"* kOps 1.16 and 1.17 are deprecated\n* VMWare TKGI 1.7 (K8s 1.16), VMWare TKGI 1.8 (K8s 1.17) are deprecated (no longer supported by VMWare)\n* Openshift 3.11 (K8s 1.11) is now deprecated. Redis will continue to support existing deployments for the lifetime of Openshift 3.11, but new deployments are strongly discouraged."}],"examples":[{"id":"applying-bundleyaml-generated-a-warning-ex0","language":"plaintext","code":"Warning: apiextensions.k8s.io/v1beta1 CustomResourceDefinition is deprecated in v1.16+, unavailable in v1.22+; use apiextensions.k8s.io/v1 CustomResourceDefinition","section_id":"applying-bundleyaml-generated-a-warning"},{"id":"enabling-the-kubernetes-webhook-generates-a-warning-ex0","language":"plaintext","code":"Warning: admissionregistration.k8s.io/v1beta1 ValidatingWebhookConfiguration is deprecated in v1.16+, unavailable in v1.22+; use admissionregistration.k8s.io/v1 ValidatingWebhookConfiguration","section_id":"enabling-the-kubernetes-webhook-generates-a-warning"}]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-2-8-11-2022-01","title":"Redis Enterprise for Kubernetes Release Notes 6.2.8-11 (January 2022)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-2-8-11-2022-01/","summary":"Support for Istio as ingress controller, K8s 1.22 (AKS, kOps, GKE), OpenShift 4.9","aliases":["/operate/kubernetes/release-notes/k8s-6-2-8-11-2022-01/","/operate/kubernetes/release-notes/6-2-releases/k8s-6-2-8-11-2022-01/"],"tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"b3fc5150f47536b69b62a9c6ff3706670d1a76be1517bbeda4cc9cb1f9b1ff67","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The Redis Enterprise K8s 6.2.8-11 release provides support for the [Redis Enterprise Software release 6.2.8](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-8-october-2021) and includes several enhancements and bug fixes.\n\nThe key new features, bug fixes, and known limitations are described below."},{"id":"images","title":"Images","role":"content","text":"This release includes the following container images:\n\n* **Redis Enterprise**: `redislabs/redis:6.2.8-53` or  `redislabs/redis:6.2.8-53.rhel7-openshift`\n* **Operator**: `redislabs/operator:6.2.8-11`\n* **Services Rigger**: `redislabs/k8s-controller:6.2.8-11` or `redislabs/services-manager:6.2.8-11` (on the Red Hat registry)"},{"id":"feature-improvements","title":"Feature improvements","role":"content","text":"* Istio gateway/virtual services are supported as ingress controllers. Note that for Active-Active databases, the operator doesn't create ingress rules, and those should be manually configured. The version of Istio that was tested is Istio 1.12.0  (RED-64020)\n* Support for K8s 1.22 (AKS, kOps, GKE) and OpenShift 4.9 (RED-64016)\n* Support for pod termination grace period customization in the REC (advanced use case) (RED-67217)\n* Improved security granularity of SCC configuration steps in documentation (RED-67321)\n* Changed behavior when two databases with the same name are created on the cluster. The operator avoids creating a service for them to prevent possible corruption (RED-64535)\n* Improved documentation about changing cluster credentials when using Hashicorp Vault (RED-65304)"},{"id":"fixed-bugs","title":"Fixed bugs","role":"content","text":"* Upgraded Go dependencies marked as vulnerable (RED-63858, RED-68651)\n* Avoided flooding operator logs with deprecation notices on K8s 1.21 (RED-67544)\n* Fixed log collector utility issues running on Microsoft Windows (RED-67682)\n* Fixed excessive updates to the RS cluster when using Windows line endings for TLS certificates (RED-67874)"},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"large-clusters","title":"Large clusters","role":"content","text":"On clusters with more than 9 REC nodes, a Kubernetes upgrade can render the Redis cluster unresponsive in some cases. A fix is available in the 6.4.2-5 release. Upgrade your operator version to 6.4.2-5 or later before upgrading your Kubernetes cluster. (RED-93025)"},{"id":"upgrading-with-the-bundle-using-kubectl-apply--f-fails-red-69515","title":"Upgrading with the bundle using `kubectl apply -f` fails (RED-69515)","role":"content","text":"Upgrading with the bundle using `kubectl apply -f` fails, giving the following error:\n\n`The CustomResourceDefinition \"redisenterpriseclusters.app.redislabs.com\" is invalid: spec.preserveUnknownFields: Invalid value: true: must be false in order to use defaults in the schema`\n\nWorkaround: Before the upgrade, set the 'spec.preserveUnknownFields` value to false on the REC custom resource. You can use the following command: \n\n[code example]"},{"id":"long-cluster-names-cause-routes-to-be-rejected--red-25871","title":"Long cluster names cause routes to be rejected  (RED-25871)","role":"content","text":"A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name will exceed 63 characters. The workaround is to limit cluster name to 20 characters or less."},{"id":"cluster-cr-rec-errors-are-not-reported-after-invalid-updates-red-25542","title":"Cluster CR (REC) errors are not reported after invalid updates (RED-25542)","role":"content","text":"A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence."},{"id":"an-unreachable-cluster-has-status-running-red-32805","title":"An unreachable cluster has status running (RED-32805)","role":"content","text":"When a cluster is in an unreachable state, the state is still `running` instead of being reported as an error."},{"id":"readiness-probe-incorrect-on-failures-red-39300","title":"Readiness probe incorrect on failures (RED-39300)","role":"content","text":"STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure."},{"id":"role-missing-on-replica-sets-red-39002","title":"Role missing on replica sets (RED-39002)","role":"content","text":"The `redis-enterprise-operator` role is missing permission on replica sets."},{"id":"private-registries-are-not-supported-on-openshift-311-red-38579","title":"Private registries are not supported on OpenShift 3.11 (RED-38579)","role":"content","text":"OpenShift 3.11 does not support DockerHub private registries. This is a known OpenShift issue."},{"id":"internal-dns-and-kubernetes-dns-may-have-conflicts-red-37462","title":"Internal DNS and Kubernetes DNS may have conflicts (RED-37462)","role":"content","text":"DNS conflicts are possible between the cluster `mdns_server` and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names."},{"id":"5410-negatively-impacts-546-red-37233","title":"5.4.10 negatively impacts 5.4.6 (RED-37233)","role":"content","text":"Kubernetes-based 5.4.10 deployments seem to negatively impact existing 5.4.6 deployments that share a Kubernetes cluster."},{"id":"node-cpu-usage-is-reported-instead-of-pod-cpu-usage-red-36884","title":"Node CPU usage is reported instead of pod CPU usage (RED-36884)","role":"content","text":"In Kubernetes, the node CPU usage we report on is the usage of the Kubernetes worker node hosting the REC pod."},{"id":"clusters-must-be-named-rec-in-olm-based-deployments-red-39825","title":"Clusters must be named \"rec\" in OLM-based deployments (RED-39825)","role":"content","text":"In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) are bound to a specific service account name (i.e., \"rec\"). The workaround is to name the cluster \"rec\"."},{"id":"rec-clusters-fail-to-start-on-kubernetes-clusters-with-unsynchronized-clocks-red-47254","title":"REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED-47254)","role":"content","text":"When REC clusters are deployed on Kubernetes clusters with unsynchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes."},{"id":"deleting-an-openshift-project-with-an-rec-deployed-may-hang-red-47192","title":"Deleting an OpenShift project with an REC deployed may hang (RED-47192)","role":"content","text":"When a REC cluster is deployed in a project (namespace) and has REDB resources, the\nREDB resources must be deleted first before the REC can be deleted. As such, until the\nREDB resources are deleted, the project deletion will hang. The fix is to delete the\nREDB resources first and the REC second. Afterwards, you may delete the project."},{"id":"rec-extralabels-are-not-applied-to-pvcs-on-k8s-versions-115-or-older-red-51921","title":"REC extraLabels are not applied to PVCs on K8s versions 1.15 or older (RED-51921)","role":"content","text":"In K8s 1.15 or older, the PVC labels come from the match selectors and not the\nPVC templates. As such, these versions cannot support PVC labels. If this feature\nis required, the only fix is to upgrade the K8s cluster to a newer version."},{"id":"hashicorp-vault-integration---no-support-for-gesher-red-55080","title":"Hashicorp Vault integration - no support for Gesher (RED-55080)","role":"content","text":"There is no workaround at this time"},{"id":"rec-might-report-error-states-on-initial-startup-red-61707","title":"REC might report error states on initial startup (RED-61707)","role":"content","text":"There is no workaround at this time except to ignore the errors."},{"id":"pvc-size-issues-when-using-decimal-value-in-spec-red-62132","title":"PVC size issues when using decimal value in spec (RED-62132)","role":"content","text":"The workaround for this issue is to make sure you use integer values for the PVC size."},{"id":"following-old-revision-of-quick-start-guide-causes-issues-creating-an-redb-due-to-unrecognized-memory-field-name","title":"Following old revision of quick start guide causes issues creating an REDB due to unrecognized memory field name","role":"content","text":"The workaround is to use the newer (current) revision of the quick start document available online."},{"id":"compatibility-notes","title":"Compatibility Notes","role":"compatibility","text":"See [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for the full list of supported distributions."},{"id":"now-supported","title":"Now supported","role":"content","text":"This release adds support for the following:\n\n* K8s 1.22 for GKE, AKS, and kOps\n* OpenShift 4.9 (uses K8s 1.22)"},{"id":"deprecated","title":"Deprecated","role":"content","text":"* Rancher 2.5/K8s 1.17 support is deprecated"},{"id":"no-longer-supported","title":"No longer supported","role":"content","text":"This release removes support for the following:\n\n* Rancher 2.4 (previously deprecated)"}],"examples":[{"id":"upgrading-with-the-bundle-using-kubectl-apply--f-fails-red-69515-ex0","language":"sh","code":"kubectl patch crd redisenterpriseclusters.app.redislabs.com -p $'spec:\\n preserveUnknownFields: false'","section_id":"upgrading-with-the-bundle-using-kubectl-apply--f-fails-red-69515"}]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-2-8-15-2022-01","title":"Redis Enterprise for Kubernetes Release Notes 6.2.8-15 (January 2022)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-2-8-15-2022-01/","summary":"Maintenance release with bug fixes","aliases":["/operate/kubernetes/release-notes/k8s-6-2-8-15-2022-01.md","/operate/kubernetes/release-notes/6-2-releases/k8s-6-2-8-15-2022-01/","/operate/kubernetes/release-notes/k8s-6-2-8-15-2022-01/"],"tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"aa68d4dfb3a0936d3f5a0e2b31aa2d6bae6c97a3e2e7d62999129b7510cabb45","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The Redis Enterprise K8s 6.2.8-15 is a maintenance release for the [Redis Enterprise Software release 6.2.8](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-8-october-2021) and includes bug fixes.\n\nThe key new features, bug fixes, and known limitations are described below."},{"id":"images","title":"Images","role":"content","text":"This release includes the following container images:\n\n* **Redis Enterprise**: `redislabs/redis:6.2.8-64` or  `redislabs/redis:6.2.8-64.rhel7-openshift`\n* **Operator**: `redislabs/operator:6.2.8-15`\n* **Services Rigger**: `redislabs/k8s-controller:6.2.8-15` or `redislabs/services-manager:6.2.8-15` (on the Red Hat registry)"},{"id":"fixed-bugs","title":"Fixed bugs","role":"content","text":"* Upgrading with the bundle using `kubectl apply -f` fails giving error (RED-69570):\n\n    [code example]\n\n* Removed unneeded certificates from the Redis Enterprise Software container (RED-69661, RED-60086)"},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"large-clusters","title":"Large clusters","role":"content","text":"On clusters with more than 9 REC nodes, a Kubernetes upgrade can render the Redis cluster unresponsive in some cases. A fix is available in the 6.4.2-5 release. Upgrade your operator version to 6.4.2-5 or later before upgrading your Kubernetes cluster. (RED-93025)"},{"id":"long-cluster-names-cause-routes-to-be-rejected--red-25871","title":"Long cluster names cause routes to be rejected  (RED-25871)","role":"content","text":"A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name will exceed 63 characters. The workaround is to limit cluster name to 20 characters or less."},{"id":"cluster-cr-rec-errors-are-not-reported-after-invalid-updates-red-25542","title":"Cluster CR (REC) errors are not reported after invalid updates (RED-25542)","role":"content","text":"A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence."},{"id":"an-unreachable-cluster-has-status-running-red-32805","title":"An unreachable cluster has status running (RED-32805)","role":"content","text":"When a cluster is in an unreachable state, the state is still `running` instead of being reported as an error."},{"id":"readiness-probe-incorrect-on-failures-red-39300","title":"Readiness probe incorrect on failures (RED-39300)","role":"content","text":"STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure."},{"id":"role-missing-on-replica-sets-red-39002","title":"Role missing on replica sets (RED-39002)","role":"content","text":"The `redis-enterprise-operator` role is missing permission on replica sets."},{"id":"private-registries-are-not-supported-on-openshift-311-red-38579","title":"Private registries are not supported on OpenShift 3.11 (RED-38579)","role":"content","text":"OpenShift 3.11 does not support DockerHub private registries. This is a known OpenShift issue."},{"id":"internal-dns-and-kubernetes-dns-may-have-conflicts-red-37462","title":"Internal DNS and Kubernetes DNS may have conflicts (RED-37462)","role":"content","text":"DNS conflicts are possible between the cluster `mdns_server` and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names."},{"id":"5410-negatively-impacts-546-red-37233","title":"5.4.10 negatively impacts 5.4.6 (RED-37233)","role":"content","text":"Kubernetes-based 5.4.10 deployments seem to negatively impact existing 5.4.6 deployments that share a Kubernetes cluster."},{"id":"node-cpu-usage-is-reported-instead-of-pod-cpu-usage-red-36884","title":"Node CPU usage is reported instead of pod CPU usage (RED-36884)","role":"content","text":"In Kubernetes, the node CPU usage we report on is the usage of the Kubernetes worker node hosting the REC pod."},{"id":"clusters-must-be-named-rec-in-olm-based-deployments-red-39825","title":"Clusters must be named \"rec\" in OLM-based deployments (RED-39825)","role":"content","text":"In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) are bound to a specific service account name (i.e., \"rec\"). The workaround is to name the cluster \"rec\"."},{"id":"rec-clusters-fail-to-start-on-kubernetes-clusters-with-unsynchronized-clocks-red-47254","title":"REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED-47254)","role":"content","text":"When REC clusters are deployed on Kubernetes clusters with unsynchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes."},{"id":"deleting-an-openshift-project-with-an-rec-deployed-may-hang-red-47192","title":"Deleting an OpenShift project with an REC deployed may hang (RED-47192)","role":"content","text":"When a REC cluster is deployed in a project (namespace) and has REDB resources, the\nREDB resources must be deleted first before the REC can be deleted. As such, until the\nREDB resources are deleted, the project deletion will hang. The fix is to delete the\nREDB resources first and the REC second. Afterwards, you may delete the project."},{"id":"rec-extralabels-are-not-applied-to-pvcs-on-k8s-versions-115-or-older-red-51921","title":"REC extraLabels are not applied to PVCs on K8s versions 1.15 or older (RED-51921)","role":"content","text":"In K8s 1.15 or older, the PVC labels come from the match selectors and not the\nPVC templates. As such, these versions cannot support PVC labels. If this feature\nis required, the only fix is to upgrade the K8s cluster to a newer version."},{"id":"hashicorp-vault-integration---no-support-for-gesher-red-55080","title":"Hashicorp Vault integration - no support for Gesher (RED-55080)","role":"content","text":"There is no workaround at this time"},{"id":"rec-might-report-error-states-on-initial-startup-red-61707","title":"REC might report error states on initial startup (RED-61707)","role":"content","text":"There is no workaround at this time except to ignore the errors."},{"id":"pvc-size-issues-when-using-decimal-value-in-spec-red-62132","title":"PVC size issues when using decimal value in spec (RED-62132)","role":"content","text":"The workaround for this issue is to make sure you use integer values for the PVC size."},{"id":"following-old-revision-of-quick-start-guide-causes-issues-creating-an-redb-due-to-unrecognized-memory-field-name","title":"Following old revision of quick start guide causes issues creating an REDB due to unrecognized memory field name","role":"content","text":"The workaround is to use the newer (current) revision of the quick start document available online."},{"id":"compatibility-notes","title":"Compatibility Notes","role":"compatibility","text":"See [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for the full list of supported distributions."}],"examples":[{"id":"fixed-bugs-ex0","language":"sh","code":"The CustomResourceDefinition \"redisenterpriseclusters.app.redislabs.com\" is invalid: spec.preserveUnknownFields: Invalid value: true: must be false in order to use defaults in the schema.","section_id":"fixed-bugs"}]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-2-8-2-2021-11","title":"Redis Enterprise for Kubernetes Release Notes 6.2.8-2 (November 2021)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-2-8-2-2021-11/","summary":"Support for RS 6.2.8, certificate management, and Redis upgrade policy.","aliases":["/operate/kubernetes/release-notes/k8s-6-2-8-2-2021-11/","/operate/kubernetes/release-notes/6-2-releases/k8s-6-2-8-2-2021-11/"],"tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"25edadf5364e51c3ac7ceeebfa9dbb86a7f51ed9ec9dc026159c1eab1963d5f2","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The Redis Enterprise [K8s 6.2.8-2](https://github.com/RedisLabs/redis-enterprise-k8s-docs/releases/tag/v6.2.8-2) release provides support for the [Redis Enterprise Software release 6.2.8](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-8-october-2021) and includes several enhancements and bug fixes.\n\nThe key new features, bug fixes, and known limitations are described below."},{"id":"images","title":"Images","role":"content","text":"This release includes the following container images:\n\n* **Redis Enterprise**: `redislabs/redis:6.2.8-41` or  `redislabs/redis:6.2.8-41.rhel7-openshift`\n* **Operator**: `redislabs/operator:6.2.8-2`\n* **Services Rigger**: `redislabs/k8s-controller:6.2.8-2` or `redislabs/services-manager:6.2.8-2` (on the Red Hat registry)"},{"id":"new-features","title":"New features","role":"content","text":"* Certificate management capabilities through the Redis Enterprise cluster API (RED-61176)"},{"id":"feature-improvements","title":"Feature improvements","role":"content","text":"* Support for managing database ports through the REDB API (RED-48286)\n* Use explicit object instead of wildcard for roles (RED-38013)\n* Advanced use case support for custom attributes for REC stateful set and rigger pods (RED-56502, RED-62550, RED-63426)\n* Services rigger/K8s controller image changed to ubi-minimal lightweight base image (RED-61866)\n* Support for the Redis upgrade policy setting through the REC API (RED-63000)"},{"id":"fixed-bugs","title":"Fixed bugs","role":"content","text":"* Fixed issue of log rotation not working on OpenShift (RED-64726)"},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"large-clusters","title":"Large clusters","role":"content","text":"On clusters with more than 9 REC nodes, a Kubernetes upgrade can render the Redis cluster unresponsive in some cases. A fix is available in the 6.4.2-5 release. Upgrade your operator version to 6.4.2-5 or later before upgrading your Kubernetes cluster. (RED-93025)"},{"id":"long-cluster-names-cause-routes-to-be-rejected--red-25871","title":"Long cluster names cause routes to be rejected  (RED-25871)","role":"content","text":"A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name will exceed 63 characters. The workaround is to limit cluster name to 20 characters or less."},{"id":"cluster-cr-rec-errors-are-not-reported-after-invalid-updates-red-25542","title":"Cluster CR (REC) errors are not reported after invalid updates (RED-25542)","role":"content","text":"A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence."},{"id":"an-unreachable-cluster-has-status-running-red-32805","title":"An unreachable cluster has status running (RED-32805)","role":"content","text":"When a cluster is in an unreachable state, the state is still `running` instead of being reported as an error."},{"id":"readiness-probe-incorrect-on-failures-red-39300","title":"Readiness probe incorrect on failures (RED-39300)","role":"content","text":"STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure."},{"id":"role-missing-on-replica-sets-red-39002","title":"Role missing on replica sets (RED-39002)","role":"content","text":"The `redis-enterprise-operator` role is missing permission on replica sets."},{"id":"private-registries-are-not-supported-on-openshift-311-red-38579","title":"Private registries are not supported on OpenShift 3.11 (RED-38579)","role":"content","text":"OpenShift 3.11 does not support DockerHub private registries. This is a known OpenShift issue."},{"id":"internal-dns-and-kubernetes-dns-may-have-conflicts-red-37462","title":"Internal DNS and Kubernetes DNS may have conflicts (RED-37462)","role":"content","text":"DNS conflicts are possible between the cluster `mdns_server` and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names."},{"id":"5410-negatively-impacts-546-red-37233","title":"5.4.10 negatively impacts 5.4.6 (RED-37233)","role":"content","text":"Kubernetes-based 5.4.10 deployments seem to negatively impact existing 5.4.6 deployments that share a Kubernetes cluster."},{"id":"node-cpu-usage-is-reported-instead-of-pod-cpu-usage-red-36884","title":"Node CPU usage is reported instead of pod CPU usage (RED-36884)","role":"content","text":"In Kubernetes, the node CPU usage we report on is the usage of the Kubernetes worker node hosting the REC pod."},{"id":"clusters-must-be-named-rec-in-olm-based-deployments-red-39825","title":"Clusters must be named \"rec\" in OLM-based deployments (RED-39825)","role":"content","text":"In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) are bound to a specific service account name (i.e., \"rec\"). The workaround is to name the cluster \"rec\"."},{"id":"rec-clusters-fail-to-start-on-kubernetes-clusters-with-unsynchronized-clocks-red-47254","title":"REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED-47254)","role":"content","text":"When REC clusters are deployed on Kubernetes clusters with unsynchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes."},{"id":"deleting-an-openshift-project-with-an-rec-deployed-may-hang-red-47192","title":"Deleting an OpenShift project with an REC deployed may hang (RED-47192)","role":"content","text":"When a REC cluster is deployed in a project (namespace) and has REDB resources, the\nREDB resources must be deleted first before the REC can be deleted. As such, until the\nREDB resources are deleted, the project deletion will hang. The fix is to delete the\nREDB resources first and the REC second. Afterwards, you may delete the project."},{"id":"rec-extralabels-are-not-applied-to-pvcs-on-k8s-versions-115-or-older-red-51921","title":"REC extraLabels are not applied to PVCs on K8s versions 1.15 or older (RED-51921)","role":"content","text":"In K8s 1.15 or older, the PVC labels come from the match selectors and not the\nPVC templates. As such, these versions can not support PVC labels. If this feature\nis required, the only fix is to upgrade the K8s cluster to a newer version."},{"id":"hashicorp-vault-integration---no-support-for-gesher-red-55080","title":"Hashicorp Vault integration - no support for Gesher (RED-55080)","role":"content","text":"There is no workaround at this time"},{"id":"rec-might-report-error-states-on-initial-startup-red-61707","title":"REC might report error states on initial startup (RED-61707)","role":"content","text":"There is no workaround at this time except to ignore the errors."},{"id":"pvc-size-issues-when-using-decimal-value-in-spec-red-62132","title":"PVC size issues when using decimal value in spec (RED-62132)","role":"content","text":"The workaround for this issue is to make sure you use integer values for the PVC size."},{"id":"compatibility-notes","title":"Compatibility Notes","role":"compatibility","text":"See [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for the full list of supported distributions."},{"id":"no-longer-supported","title":"No longer supported","role":"content","text":"* kOps K8s versions 1.16 and 1.17 (previously deprecated) are no longer supported\n* AKS K8s version 1.18 (previously deprecated) is no longer supported\n* GKE K8s version 1.18 (previously deprecated) is no longer supported\n* VMWare TKGI version 1.7 (K8s 1.16) (previously deprecated) is no longer supported\n* VMWare TKGI version 1.8 (K8s 1.17) (previously deprecated) is no longer supported"},{"id":"deprecation-notice","title":"Deprecation notice","role":"content","text":"* Rancher version 2.4 support is deprecated"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/release-notes/previous-releases/k8s-6-4-2-4","title":"Redis Enterprise for Kubernetes release notes 6.4.2-4 (March 2023)","url":"https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/k8s-6-4-2-4/","summary":"The Redis Enterprise K8s 6.4.2-4 release supports Redis Enterprise Software 6.4.2 and contains new features and feature improvements.","aliases":["/operate/kubernetes/release-notes/6-4-2-releases/k8s-6-4-2-4/"],"tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T13:45:38+01:00","page_type":"content","content_hash":"fbe50f3f17be4ef96f510e84bf24d4ba7fe99e0d65e0f8856e4c2fd9255c6cc7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The Redis Enterprise K8s 6.4.2-4 release supports Redis Enterprise Software 6.4.2 and contains new features and feature improvements.\n\nThe key features, bug fixes, and known limitations are described below."},{"id":"images","title":"Images","role":"content","text":"* **Redis Enterprise**: `redislabs/redis:6.4.2-43`\n* **Operator**: `redislabs/operator:6.4.2-4`\n* **Services Rigger**: `redislabs/k8s-controller:6.4.2-4`"},{"id":"openshift-images","title":"OpenShift images","role":"content","text":"* **Redis Enterprise**: `registry.connect.redhat.com/redislabs/redis-enterprise:6.4.2-43.rhel8-openshift`\n    (or `redislabs/redis-enterprise:6.4.2-43.rhel7-openshift` if upgrading from RHEL 7)\n* **Operator**: `registry.connect.redhat.com/redislabs/redis-enterprise-operator:6.4.2-4`s\n* **Services Rigger**: `registry.connect.redhat.com/redislabs/services-manager:6.4.2-4`"},{"id":"openshift-olm-bundles","title":"OpenShift OLM bundles","role":"content","text":"* Redis Enterprise operator bundle version: `v6.4.2-4.0`"},{"id":"new-features","title":"New features","role":"content","text":"* Cluster-level LDAP configuration support in REC (RED-83533)\n* Public preview support for declarative Active-Active configuration (RED-86470)(RED-86470)"},{"id":"feature-improvements","title":"Feature improvements","role":"content","text":"* New labeling method for multi-namespace REDB (RED-83102)\n* Operator deployment uses built-in SCC in OpenShift (RED-90524)\n* Support for Redis Enterprise 6.4.2-43\n* Support added for K8s 1.26, EKS 1.24, AKS 1.25, Rancher 2.7/1.23-24\n* Improved log collector display and error handling"},{"id":"bug-fixes","title":"Bug fixes","role":"content","text":"* Deployment manifests referenced non-existent redis-enterprise-admission ServiceAccount (RED-90469)\n* CVE-2022-1996 (RED-93026)\n* CVE-2022-28948 (RED-93027)\n* CVE-2021-44716 & CVE-2022-27664 (RED-93028)\n* CVE-2018-20225"},{"id":"api-changes","title":"API changes","role":"content","text":"The following fields were changed in the [Redis Enterprise cluster (REC) API](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api):\n\n* Added `.spec.ldap` for configuring cluster-level LDAP settings\n* Added `.spec.certificates.ldapClientCertificateSecretName` for configuring LDAP client certificate\n* Added `.status.managedAPIs` to track APIs managed by the operator\n\n* The following custom resources definitions were added:\n  * `RedisEnterpriseRemoteCluster` (RERC)\n  * `RedisEnterpriseActiveActiveDatabase` (REAADB)"},{"id":"compatibility-notes","title":"Compatibility notes","role":"compatibility","text":""},{"id":"deprecated-features","title":"Deprecated features","role":"content","text":"* Support for Gesher, the advanced admission control configuration, is deprecated.\n* The `app.redislabs.com/v1alpha1` API version of the RedisEnterpriseCluster (REC) custom resource is deprecated and will be removed in a subsequent release. Customers should switch to use the `app.redislabs.com/v1` API version instead."},{"id":"supported-distributions","title":"Supported distributions","role":"compatibility","text":"The following table shows supported distributions at the time of this release. You can also find this list in [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions).\n\n| **Kubernetes version**  | 1.21       | 1.22       | 1.23       | 1.24       | 1.25       | 1.26       |\n|:------------------------|:----------:|:----------:|:----------:|:----------:|:----------:|:----------:|\n| Community Kubernetes    |            | deprecated | supported  | supported  | supported  | supported* |\n| Amazon EKS              |            | supported  | supported  | supported* |            |            |\n| Azure AKS               |            |            | deprecated | supported  | supported* |            |\n| Google GKE              |            | supported  | supported  | supported  | supported  |            |\n| Rancher 2.6             | deprecated | supported  | supported  | supported  |            |            |\n| Rancher 2.7             |            |            | supported* | supported* |            |            |\n| VMware TKG 1.6          |            | supported  | supported  |            |            |            |\n| **OpenShift version**   | **4.8**    | **4.9**    | **4.10**   | **4.11**   |  **4.12**  |            |\n|                         | deprecated | deprecated | supported  | supported  | supported* |            |\n| **VMware TKGI version** | **1.12**   | **1.13**   | **1.14**   | **1.15**   |            |            |\n|                         |            | deprecated | supported  | supported  |            |            |\n\n\\* Support added in this release"},{"id":"before-upgrading","title":"Before upgrading","role":"content","text":"Be aware the following changes included in this release affect the upgrade process. Please read carefully before upgrading.\n\n* **Supported upgrade paths**\n\n  If you are using a version earlier than 6.2.10-45, you cannot upgrade directly to this release. You must upgrade to 6.2.10-45 before you can upgrade to versions 6.2.18 or later.\n\n* **ValidatingWebhookConfiguration**\n\n  This release uses a new `ValidatingWebhookConfiguration` resource to replace the `redb-admission` webhook resource. To use releases 6.4.2-4 or later, delete the old webhook resource and apply the new file. See [upgrade Redis cluster](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster#reapply-webhook) for instructions."},{"id":"active-active-preview-known-limitations","title":"Active-Active preview known limitations","role":"content","text":"* No support for Hashicorp Vault for storing secrets (RED-95805)\n* No module support (RED-95153)\n* No support for client certificates in secrets (RED-95724)\n* No support for backup configuration (RED-95724)\n* No support for upgrading the database Redis version\n* REAADB secret status isn't updated in source cluster (RED-96296)\n\n  The workaround is to view the secret status in one of the remote clusters.\n* Invalid REAADB is not rejected by admission might get deleted after apply. (RED-96300)\n\n  Fix the problems with the REAADB and reapply. Contact support if you aren't sure why the REAADB is invalid.\n* Admission is not blocking REAADB with `shardCount` which exceeds license quota. (RED-96301)\n\n  Fix the problems with the REAADB and reapply.\n* RERC resources must have a unique name (`<rec-name>/<rec-namespace>`). (RED-96302)\n\n* Only global database options are supported, not support for specifying configuration per location.\n* Can't automatically update the cluster secret via the operator (can be updated manually).\n* No support for migration from old (manual) Active-Active database method to new Active-Active controller."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"* REC creation might fail using form view on OpenShift. (RED-100517)\n\n  To avoid this, use the YAML view. This will be fixed in the next release."},{"id":"large-clusters","title":"Large clusters","role":"content","text":"On clusters with more than 9 REC nodes, a Kubernetes upgrade can render the Redis cluster unresponsive in some cases. A fix is available in the 6.4.2-5 release. Upgrade your operator version to 6.4.2-5 or later before upgrading your Kubernetes cluster. (RED-93025)\n\n* Resharding fails for rack-aware databases with no replication (RED-97971)\n  \n  When a database is configured as [rack-aware](https://redis.io/docs/latest/operate/rs/clusters/configure/rack-zone-awareness) and replication is turned off, the resharding operation fails.\n\n  Workaround:\n    Before resharding your database, [turn off rack awareness](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api). Set the `spec.rackAware` field in your REDB custom resource to `false`.\n    After the resharding process is complete, you can [re-enable rack awareness](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api).\n\n\n* Long cluster names cause routes to be rejected  (RED-25871)\n\n  A cluster name longer than 20 characters will result in a rejected route configuration because the host part of the domain name exceeds 63 characters. The workaround is to limit the cluster name to 20 characters or fewer.\n* Cluster CR (REC) errors are not reported after invalid updates (RED-25542)\n\n  A cluster CR specification error is not reported if two or more invalid CR resources are updated in sequence.\n* An unreachable cluster has status running (RED-32805)\n\n  When a cluster is in unreachable state, the state remains `running` instead of triggering an error.\n* Readiness probe incorrect on failures (RED-39300)\n\n  STS Readiness probe does not mark a node as \"not ready\" when running `rladmin status` on node failure.\n* Internal DNS and Kubernetes DNS may have conflicts (RED-37462)\n\n  DNS conflicts are possible between the cluster `mdns_server` and the K8s DNS. This only impacts DNS resolution from within cluster nodes for Kubernetes DNS names.\n* 5.4.10 negatively impacts 5.4.6 (RED-37233)\n\n  Kubernetes-based 5.4.10 deployments seem to negatively impact existing 5.4.6 deployments that share a Kubernetes cluster.\n* Node CPU usage is reported instead of pod CPU usage (RED-36884)\n\n  In Kubernetes, the reported node CPU usage is the usage of the Kubernetes worker node hosting the REC pod.\n* Clusters must be named \"rec\" in OLM-based deployments (RED-39825)\n\n  In OLM-deployed operators, the deployment of the cluster will fail if the name is not \"rec\". When the operator is deployed via the OLM, the security context constraints (scc) are bound to a specific service account name (namely, \"rec\"). The workaround is to name the cluster \"rec\".\n* REC clusters fail to start on Kubernetes clusters with unsynchronized clocks (RED-47254)\n\n  When REC clusters are deployed on Kubernetes clusters with unsynchronized clocks, the REC cluster does not start correctly. The fix is to use NTP to synchronize the underlying K8s nodes.\n* Deleting an OpenShift project with an REC deployed may hang (RED-47192)\n\n  When an REC cluster is deployed in a project (namespace) and has REDB resources, the REDB resources must be deleted first before the REC can be deleted. Therefore, until the REDB resources are deleted, the project deletion will hang. The fix is to delete the REDB resources first and the REC second. Then, you can delete the project.\n* Hashicorp Vault integration - no support for Gesher (RED-55080)\n\n  There is no workaround at this time.\n* REC might report error states on initial startup (RED-61707)\n\n  There is no workaround at this time except to ignore the errors.\n* PVC size issues when using decimal value in spec (RED-62132)\n\n  The workaround for this issue is to make sure you use integer values for the PVC size.\n* Following the previous quick start guide version causes issues with creating an REDB due to unrecognized memory field name (RED-69515)\n\n  The workaround is to use the newer (current) revision of the [quick start](https://redis.io/docs/latest/operate/kubernetes/deployment/quick-start).\n* `autoUpgrade` set to `true` can cause unexpected bdb upgrades when `redisUpgradePolicy` is set to `true` (RED-72351)\n\n  Contact support if your deployment is impacted.\n\n  ## Security\n\nFor a list of fixes related to CVEs, see the [Redis Enterprise 6.4.2-43 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-43)."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/security/access-control","title":"Access control","url":"https://redis.io/docs/latest/operate/kubernetes/security/access-control/","summary":"Manage Redis Software users, roles, ACLs, and role bindings on Kubernetes with custom resources.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","children":[{"id":"operate/kubernetes/security/access-control/manage-users","summary":"Create and update Redis Software users on Kubernetes with the RedisEnterpriseUser custom resource.","title":"Manage users","url":"https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-users/"},{"id":"operate/kubernetes/security/access-control/manage-roles","summary":"Create RedisEnterpriseRole and RedisEnterpriseClusterRole resources to grant Redis Software permissions on Kubernetes.","title":"Manage roles","url":"https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-roles/"},{"id":"operate/kubernetes/security/access-control/manage-acls","summary":"Create and update RedisEnterpriseACL resources to control Redis data-path permissions on Kubernetes.","title":"Manage ACLs","url":"https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-acls/"},{"id":"operate/kubernetes/security/access-control/manage-bindings","summary":"Assign roles to users with RedisEnterpriseRoleBinding and RedisEnterpriseClusterRoleBinding resources.","title":"Manage role bindings","url":"https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-bindings/"},{"id":"operate/kubernetes/security/access-control/migrate-rolespermissions","summary":"Move database role assignments from the deprecated RedisEnterpriseDatabase.spec.rolesPermissions field to the RBAC CRD model.","title":"Migrate from REDB rolesPermissions","url":"https://redis.io/docs/latest/operate/kubernetes/security/access-control/migrate-rolespermissions/"}],"page_type":"content","content_hash":"353b7b6c7239816bbd7222e40c50d39a4228b70989a74d3143d6d6236a56bbef","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Access control lets you manage Redis Software users, roles, ACLs, and role bindings as Kubernetes custom resources. The operator reconciles each resource into the corresponding Redis Software object, so you can use GitOps workflows and Kubernetes Secrets instead of working only through the Redis Software REST API or Cluster Manager UI."},{"id":"how-access-control-works-on-redis-software-for-kubernetes","title":"How access control works on Redis Software for Kubernetes","role":"content","text":"You declare these `app.redislabs.com/v1alpha1` custom resources:\n\n| Resource | Scope | Purpose |\n| --- | --- | --- |\n| `RedisEnterpriseUser` | — | A Redis Software user, with credentials in a Kubernetes Secret. |\n| `RedisEnterpriseACL` | — | A Redis ACL rule, mapped to a Redis Software ACL object. |\n| `RedisEnterpriseRole` | Database | A management role and/or ACL applied to one or more REDBs selected by `spec.scopes`. |\n| `RedisEnterpriseRoleBinding` | Database | Assigns a `RedisEnterpriseRole` to a user. |\n| `RedisEnterpriseClusterRole` | Cluster | A management role and/or ACL applied across every REDB in the cluster. |\n| `RedisEnterpriseClusterRoleBinding` | Cluster | Assigns a `RedisEnterpriseClusterRole` to a user. |\n\nWhen you apply one of these resources, the operator:\n\n1. Validates the spec.\n2. Creates or updates the matching object in Redis Software.\n3. Reports the resolved Redis Software UID and other state in the resource's `status`.\n4. Emits Kubernetes events on reconciliation problems."},{"id":"roles-and-bindings","title":"Roles and bindings","role":"content","text":"The role and binding CRDs follow the same pattern as Kubernetes' own RBAC: a `Role` paired with a `RoleBinding` for the narrower scope, and a `ClusterRole` paired with a `ClusterRoleBinding` for cluster-wide access. The narrower scope is the unqualified default — that's why `RedisEnterpriseRole` (no qualifier) is the database-scoped kind, while `RedisEnterpriseClusterRole` carries the explicit `Cluster` prefix."},{"id":"database-scope-vs-cluster-scope","title":"Database scope vs. cluster scope","role":"content","text":"| | `RedisEnterpriseRole` | `RedisEnterpriseClusterRole` |\n| --- | --- | --- |\n| Scope | One or more REDBs | Every REDB in the cluster |\n| Selects targets via | `spec.scopes` (REDB name or label selector) — required | No selector; applies cluster-wide |\n| `managementRole` values | `DBMember`, `DBViewer`, `None` | `Admin`, `ClusterMember`, `ClusterViewer`, `DBMember`, `DBViewer`, `UserManager`, `None` |\n| Binding kind | `RedisEnterpriseRoleBinding` | `RedisEnterpriseClusterRoleBinding` |\n\nA `RedisEnterpriseClusterRole` applies to every REDB in the cluster, including REDBs represented by resources in other namespaces. The operator applies the role to each REDB individually, so it doesn't reach databases created directly through the Redis Software REST API without a matching REDB resource."},{"id":"what-a-role-grants","title":"What a role grants","role":"content","text":"Every role carries permissions on two independent planes. Set either, or both:\n\n- **`spec.managementRole`** — Redis Software API and Cluster Manager UI permissions, chosen from the built-in roles listed in the table above. Same set of roles you'd assign in Cluster Manager today.\n- **`spec.acl`** — a single `RedisEnterpriseACL` reference. The ACL controls Redis data-path access (commands, key patterns, categories). To apply different data-path access to different databases, create a separate role for each."},{"id":"how-a-user-gets-permissions","title":"How a user gets permissions","role":"content","text":"`RedisEnterpriseUser.spec` has no role references. Permissions reach a user through a binding:\n\n1. Create a `RedisEnterpriseACL` if you need data-path access.\n2. Create a `RedisEnterpriseRole` or `RedisEnterpriseClusterRole` that sets `managementRole`, references the ACL, or both.\n3. Create a `RedisEnterpriseRoleBinding` or `RedisEnterpriseClusterRoleBinding` whose `roleRef` points at the role and whose `subjects` list includes the user.\n\nThe user's effective roles appear in `status.roles`. A user with no binding gets the Redis Software `none` role so it's never roleless, but it has zero permissions until a binding lands."},{"id":"end-to-end-example","title":"End-to-end example","role":"content","text":"End-to-end: an ACL, a database-scoped role that uses it, a binding that hands the role to a user, and the user itself. All four resources live in the operator namespace.\n\n[code example]\n\nAfter applying this and a Secret named `alice-password` with a `password` key, Alice can sign in to Redis Software with `DBViewer` permissions on the `orders` REDB and run read-only Redis commands on every key in that database."},{"id":"whats-the-same-as-redis-software","title":"What's the same as Redis Software","role":"content","text":"The underlying Redis Software behavior is unchanged. For concepts and reference details, see the existing Redis Software docs:\n\n- [Cluster-scoped role definitions](https://redis.io/docs/latest/operate/rs/security/access-control/create-cluster-roles) — what `Admin`, `ClusterMember`, `ClusterViewer`, and `UserManager` grant.\n- [Database-scoped role definitions](https://redis.io/docs/latest/operate/rs/security/access-control/create-db-roles) — what `DBMember` and `DBViewer` grant.\n- [Combined cluster and database roles](https://redis.io/docs/latest/operate/rs/security/access-control/create-combined-roles) — when a role grants both planes.\n- [Redis ACL syntax](https://redis.io/docs/latest/operate/rs/security/access-control/redis-acl-overview) — rule format for `RedisEnterpriseACL` resources.\n- [Login lockout and unlock](https://redis.io/docs/latest/operate/rs/security/access-control/manage-users/login-lockout) — how locked users are recovered.\n- [Password complexity rules](https://redis.io/docs/latest/operate/rs/security/access-control/manage-passwords/password-complexity-rules) and [password expiration](https://redis.io/docs/latest/operate/rs/security/access-control/manage-passwords/password-expiration) — applied by Redis Software regardless of how the password is delivered.\n- [Default user](https://redis.io/docs/latest/operate/rs/security/access-control/manage-users/default-user) — the built-in cluster admin account."},{"id":"whats-different-on-kubernetes","title":"What's different on Kubernetes","role":"content","text":"- **Resources are declarative.** You define users, roles, ACLs, and bindings in YAML and let the operator apply them. The Cluster Manager UI and REST API still work but are no longer the source of truth.\n- **Role assignment lives on the binding, not the user.** In Redis Software, you assign roles by editing the user. On Kubernetes, you create a separate `RedisEnterpriseRoleBinding` or `RedisEnterpriseClusterRoleBinding`. See [Roles and bindings](#roles-and-bindings).\n- **Database and cluster scope are separate objects.** A `RedisEnterpriseRole` covers database scope and a `RedisEnterpriseClusterRole` covers cluster scope. In Redis Software, a single role object carries both.\n- **Passwords live in Kubernetes Secrets.** Each `RedisEnterpriseUser` references one or more Secrets. A `Rotatable` mode supports two Secrets at once for zero-downtime rotation. The operator marks Kubernetes Secrets immutable to prevent in-place edits."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"Access control resources are reconciled only in the operator namespace. Password Secrets must live in the same namespace, and database scopes resolve to REDBs in that namespace."},{"id":"in-this-section","title":"In this section","role":"content","text":"- [Manage users](https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-users) — create `RedisEnterpriseUser` resources, rotate passwords, recover from lockouts.\n- [Manage roles](https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-roles) — create database and cluster roles with the right scope and management permissions.\n- [Manage ACLs](https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-acls) — create and update `RedisEnterpriseACL` resources used by roles.\n- [Manage role bindings](https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-bindings) — assign roles to users with `RedisEnterpriseRoleBinding` and `RedisEnterpriseClusterRoleBinding`.\n- [Migrate from REDB rolesPermissions](https://redis.io/docs/latest/operate/kubernetes/security/access-control/migrate-rolespermissions) — move from the deprecated `RedisEnterpriseDatabase.spec.rolesPermissions` field to the new CRD model."},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Redis Software for Kubernetes operator API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api) — field-by-field specification for every CRD in the `app.redislabs.com/v1alpha1` group.\n- [Redis databases (REDB)](https://redis.io/docs/latest/operate/kubernetes/re-databases) — the resources that role scopes resolve against."}],"examples":[{"id":"end-to-end-example-ex0","language":"yaml","code":"---\napiVersion: app.redislabs.com/v1alpha1\nkind: RedisEnterpriseACL\nmetadata:\n  name: read-only\nspec:\n  acl: \"+@read ~*\"\n---\napiVersion: app.redislabs.com/v1alpha1\nkind: RedisEnterpriseRole\nmetadata:\n  name: orders-viewer\nspec:\n  managementRole: DBViewer\n  scopes:\n  - name: orders\n  acl:\n    name: read-only\n---\napiVersion: app.redislabs.com/v1alpha1\nkind: RedisEnterpriseRoleBinding\nmetadata:\n  name: alice-orders-viewer\nspec:\n  roleRef:\n    name: orders-viewer\n  subjects:\n  - name: alice\n---\napiVersion: app.redislabs.com/v1alpha1\nkind: RedisEnterpriseUser\nmetadata:\n  name: alice\nspec:\n  email: alice@example.com\n  username: alice\n  passwordSecrets:\n  - name: alice-password","section_id":"end-to-end-example"}]}
{"schema_version":2,"id":"operate/kubernetes/security/access-control/manage-acls","title":"Manage ACLs","url":"https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-acls/","summary":"Create and update RedisEnterpriseACL resources to control Redis data-path permissions on Kubernetes.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","page_type":"content","content_hash":"a2265865f0553e8c7d4a6bbd80b1dbca872d4caf1e7d00d605bc261ccc6557c6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"A `RedisEnterpriseACL` resource holds a Redis ACL rule that controls which commands, keys, and categories a user can access at the Redis data path. The operator reconciles the resource into a Redis Software ACL object that roles can reference.\n\nACLs are reusable: one `RedisEnterpriseACL` can be attached to any number of `RedisEnterpriseRole` or `RedisEnterpriseClusterRole` resources. The role decides which databases the ACL applies to; the ACL itself just defines the rule.\n\nTo grant a user the permissions in an ACL, reference the ACL from a role and bind the role to the user. See [Manage roles](https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-roles) and [Manage role bindings](https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-bindings)."},{"id":"before-you-start","title":"Before you start","role":"content","text":"- Requires Redis Software for Kubernetes operator 8.2.0-12 or later.\n- The `RedisEnterpriseACL` resource must live in the operator namespace.\n- The rule string uses Redis ACL syntax — key patterns, command categories, and explicit commands. See [Redis ACL overview](https://redis.io/docs/latest/operate/rs/security/access-control/redis-acl-overview) for the full syntax."},{"id":"create-an-acl","title":"Create an ACL","role":"content","text":"`spec.acl` is a single Redis ACL rule string.\n\n[code example]\n\nApply the resource and confirm reconciliation:\n\n[code example]\n\n`status.uid` holds the Redis Software ACL UID once the operator has created the object."},{"id":"common-rule-patterns","title":"Common rule patterns","role":"content","text":"| Use case | Rule |\n| --- | --- |\n| Read-only access to all keys | `+@read ~*` |\n| Read and write access to all keys | `+@all ~*` |\n| Read-only access to a key prefix | `+@read ~customer:*` |\n| A specific command set | `+get +set +del ~app:*` |\n| Block dangerous commands | `+@all -@dangerous ~*` |\n\nFor category names (`@read`, `@write`, `@admin`, `@dangerous`, etc.) and the full operator precedence rules, see [Redis ACL overview](https://redis.io/docs/latest/operate/rs/security/access-control/redis-acl-overview)."},{"id":"update-an-acl","title":"Update an ACL","role":"content","text":"Edit `spec.acl` and re-apply. The operator updates the Redis Software ACL object in place, so the change immediately affects every database that uses the ACL through a role.\n\n[code example]\n\n`status.observedGeneration` reaches the resource's `metadata.generation` once the update has been applied.\n\nAn update changes effective permissions for every connected user. Treat ACL changes the same way as role changes: validate them on a non-production cluster first. For a safer rollout, create a new ACL and swap the role reference instead of editing the existing ACL in place."},{"id":"inspect-acl-status","title":"Inspect ACL status","role":"content","text":"The `status` block is minimal:\n\n| Field | Meaning |\n| --- | --- |\n| `uid` | Internal Redis Software ACL UID. Present once reconciliation succeeds. |\n| `observedGeneration` | The `metadata.generation` the operator last acted on. |\n\nTo find roles that reference this ACL, scan the role resources:\n\n[code example]"},{"id":"delete-an-acl","title":"Delete an ACL","role":"content","text":"Delete or modify any roles that reference the ACL first (modify them to no longer reference it), then delete the ACL itself:\n\n[code example]\n\nIf a role still references the ACL, Redis Software may reject the deletion. The operator emits an `RSOperationFailed` event with the underlying message. Remove the reference (or delete the role) and retry."},{"id":"troubleshoot","title":"Troubleshoot","role":"errors","text":"Watch reconciliation events with `kubectl describe redisenterpriseacl <name>`. Common events:\n\n| Event | Meaning |\n| --- | --- |\n| `RSObjectNotFound` | The Redis Software ACL the resource previously resolved against no longer exists. The operator will recreate it on the next reconcile. |\n| `RSOperationFailed` | A Redis Software API call failed. The message typically includes the syntax error or in-use conflict. |\n\nOther things to check:\n\n- **`status.uid` is empty** — The operator hasn't reconciled the ACL yet, or Redis Software rejected the rule. Check the events for an `RSOperationFailed` with the syntax message.\n- **Rule parses but grants nothing** — A common cause is an explicit `-@all` later in the rule overriding earlier `+` clauses. Redis ACL evaluation is order-sensitive; see the [Redis ACL overview](https://redis.io/docs/latest/operate/rs/security/access-control/redis-acl-overview).\n- **Delete is blocked** — A `RedisEnterpriseRole` or `RedisEnterpriseClusterRole` still references the ACL in `spec.acl`. Remove the reference or delete the role first.\n\nFor full field details, see the [`RedisEnterpriseACL`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_acl_api) API reference."},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Redis ACL overview](https://redis.io/docs/latest/operate/rs/security/access-control/redis-acl-overview) — rule syntax, categories, and evaluation order.\n- [Manage roles](https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-roles) — attach ACLs to `RedisEnterpriseRole` and `RedisEnterpriseClusterRole` resources.\n- [Manage role bindings](https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-bindings) — grant the role to a user."}],"examples":[{"id":"create-an-acl-ex0","language":"yaml","code":"apiVersion: app.redislabs.com/v1alpha1\nkind: RedisEnterpriseACL\nmetadata:\n  name: read-only\nspec:\n  acl: \"+@read ~*\"","section_id":"create-an-acl"},{"id":"create-an-acl-ex1","language":"sh","code":"kubectl apply -f read-only-acl.yaml\nkubectl get redisenterpriseacl read-only -o yaml","section_id":"create-an-acl"},{"id":"update-an-acl-ex0","language":"sh","code":"kubectl edit redisenterpriseacl read-only","section_id":"update-an-acl"},{"id":"inspect-acl-status-ex0","language":"sh","code":"kubectl get redisenterpriserole -o yaml | \\\n  yq '.items[] | select(.spec.acl.name == \"read-only\") | .metadata.name'\nkubectl get redisenterpriseclusterrole -o yaml | \\\n  yq '.items[] | select(.spec.acl.name == \"read-only\") | .metadata.name'","section_id":"inspect-acl-status"},{"id":"delete-an-acl-ex0","language":"sh","code":"kubectl delete redisenterpriseacl read-only","section_id":"delete-an-acl"}]}
{"schema_version":2,"id":"operate/kubernetes/security/access-control/manage-bindings","title":"Manage role bindings","url":"https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-bindings/","summary":"Assign roles to users with RedisEnterpriseRoleBinding and RedisEnterpriseClusterRoleBinding resources.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","page_type":"content","content_hash":"7c4253ce69dd4cba6cab368d44fd665cd49323aa6a562d46444b59ff1c5181db","sections":[{"id":"overview","title":"Overview","role":"overview","text":"A role binding assigns a role to one or more users. Redis Software for Kubernetes supports two binding kinds, one for database-scoped roles and one for cluster-scoped roles:\n\n- `RedisEnterpriseRoleBinding` — binds a `RedisEnterpriseRole` (database-scoped) to users.\n- `RedisEnterpriseClusterRoleBinding` — binds a `RedisEnterpriseClusterRole` (cluster-scoped) to users.\n\nBoth have the same two spec fields: `roleRef` points at a single role, and `subjects` lists the users who receive it. A user holds the permissions defined by every role bound to it across all bindings.\n\nFor the conceptual model and a complete end-to-end example, see [Roles and bindings](https://redis.io/docs/latest/operate/kubernetes/security/access-control#roles-and-bindings)."},{"id":"common-patterns","title":"Common patterns","role":"content","text":"Each binding references exactly one role through `roleRef` and lists up to 100 subjects. A user can be the subject of many bindings, so you grant a user multiple roles by creating multiple bindings. Beyond that, the CRD model doesn't enforce a granularity — pick one of these patterns and stick with it across the namespace to make audits and reviews predictable:\n\n- **One binding per role, many subjects** — every user with the role lives in one resource. A single apply changes access for every user at once, which can be either a feature or a hazard depending on the change.\n- **One binding per user-role pair** — most verbose, but each grant is a discrete resource. Useful for attributing changes in GitOps and for scoping Kubernetes RBAC permissions on individual bindings."},{"id":"before-you-start","title":"Before you start","role":"content","text":"- Requires Redis Software for Kubernetes operator 8.2.0-12 or later.\n- The binding resource, the role it references, and any `RedisEnterpriseUser` subjects must all live in the operator namespace.\n- Create the role before the binding when possible. The operator still admits a binding that references a missing role — the binding stays unresolved until the role lands."},{"id":"create-a-database-role-binding","title":"Create a database role binding","role":"content","text":"`RedisEnterpriseRoleBinding` references a `RedisEnterpriseRole` and a list of subjects:\n\n[code example]\n\n`kind` on `roleRef` defaults to `RedisEnterpriseRole` and can be omitted. Subjects default to `RedisEnterpriseUser` the same way.\n\nTo grant the same role to several users, list them as separate subjects:\n\n[code example]"},{"id":"apply-order-doesnt-matter","title":"Apply order doesn't matter","role":"content","text":"The operator admits a binding even when the role it references doesn't exist yet. The binding stays unresolved until the role lands, and the user picks up the role on the next reconcile. This lets `kubectl apply -f manifests/` work regardless of file order.\n\nWhile the role is missing, the affected user's `RolesBound` condition is `False` with reason `RoleNotFound`:\n\n[code example]\n\nThe condition flips to `True` after the missing role exists and reconciles."},{"id":"create-a-cluster-role-binding","title":"Create a cluster role binding","role":"content","text":"`RedisEnterpriseClusterRoleBinding` has the same shape but references a `RedisEnterpriseClusterRole`:\n\n[code example]\n\nA cluster role binding grants the role across every REDB in the cluster, including REDBs represented by resources in other namespaces. Reserve cluster role bindings for administrators who need cluster-wide access."},{"id":"subjects","title":"Subjects","role":"content","text":"Each entry in `spec.subjects` references a `RedisEnterpriseUser` in the operator namespace. `spec.subjects[].kind` can be left empty or set to `RedisEnterpriseUser`, and `name` is the resource's `metadata.name`:\n\n[code example]\n\nA binding can list up to 100 subjects. To grant the role to more users, create additional bindings that reference the same role."},{"id":"update-a-binding","title":"Update a binding","role":"content","text":"Edit `spec.subjects` to add or remove users from the role, or change `roleRef` to point at a different role.\n\n- **Add a user** — append a new entry to `subjects` and apply. The user picks up the role on the next reconcile.\n- **Remove a user** — delete the entry from `subjects`. The user loses the role; other subjects in the binding keep their access.\n- **Replace the role** — change `roleRef.name`. Subjects lose the old role and gain the new one. Consider creating a new binding instead if you need a staged rollout.\n\nA user's effective roles appear in `status.roles` on the `RedisEnterpriseUser`."},{"id":"find-bindings-that-reference-a-role-or-user","title":"Find bindings that reference a role or user","role":"content","text":"Before renaming or deleting a role, find every binding that points at it:\n\n[code example]\n\nTo find every binding that grants a role to a specific user:\n\n[code example]"},{"id":"inspect-binding-status","title":"Inspect binding status","role":"content","text":"The binding's own `status` block is empty. To see whether a binding has actually granted permissions, check the referenced user instead:\n\n[code example]"},{"id":"delete-a-binding","title":"Delete a binding","role":"content","text":"[code example]\n\nRemoving the binding revokes the role from every subject listed in it. If you delete the only binding that granted a user any role, the operator falls back to assigning the `none` role so the user is never roleless."},{"id":"troubleshoot","title":"Troubleshoot","role":"errors","text":"Watch reconciliation events with `kubectl describe redisenterpriserolebinding <name>` (or `redisenterpriseclusterrolebinding`). Since bindings are observed by the user controller, the most useful diagnostic signals appear on the user resource:\n\n- **The user's `RolesBound` condition is `False` with reason `RoleNotFound`** — A binding references this user but points at a role that doesn't exist. Either create the missing role or fix `roleRef.name`. See [Apply order doesn't matter](#apply-order-doesnt-matter).\n- **User has permissions you didn't expect** — Multiple bindings may be granting the same user different roles. Use the recipes in [Find bindings that reference a role or user](#find-bindings-that-reference-a-role-or-user) to list everything that targets the user.\n- **`MissingRoleUIDs` event on a user** — Redis Software has role UIDs assigned to the user that the operator can't trace back to any Kubernetes role resource. This typically means roles were granted directly through the Redis Software REST API or Cluster Manager UI, bypassing the CRD model. Recreate the assignment as a `RedisEnterpriseRoleBinding` (or `RedisEnterpriseClusterRoleBinding`) so the CRD model is the source of truth, then revoke the direct assignment.\n\nFor full field details, see the [`RedisEnterpriseRoleBinding`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_role_binding_api) and [`RedisEnterpriseClusterRoleBinding`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_role_binding_api) API reference."},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Roles and bindings](https://redis.io/docs/latest/operate/kubernetes/security/access-control#roles-and-bindings) — the conceptual model and an end-to-end example.\n- [Manage roles](https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-roles) — create the roles that a binding references.\n- [Manage users](https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-users) — create the users that a binding lists as subjects."}],"examples":[{"id":"create-a-database-role-binding-ex0","language":"yaml","code":"apiVersion: app.redislabs.com/v1alpha1\nkind: RedisEnterpriseRoleBinding\nmetadata:\n  name: alice-orders-viewer\nspec:\n  roleRef:\n    kind: RedisEnterpriseRole\n    name: orders-viewer\n  subjects:\n  - kind: RedisEnterpriseUser\n    name: alice","section_id":"create-a-database-role-binding"},{"id":"create-a-database-role-binding-ex1","language":"yaml","code":"spec:\n  roleRef:\n    name: orders-viewer\n  subjects:\n  - name: alice\n  - name: bob\n  - name: carol","section_id":"create-a-database-role-binding"},{"id":"apply-order-doesnt-matter-ex0","language":"sh","code":"kubectl get redisenterpriseuser alice -o jsonpath='{.status.conditions[?(@.type==\"RolesBound\")]}'","section_id":"apply-order-doesnt-matter"},{"id":"create-a-cluster-role-binding-ex0","language":"yaml","code":"apiVersion: app.redislabs.com/v1alpha1\nkind: RedisEnterpriseClusterRoleBinding\nmetadata:\n  name: ops-admins\nspec:\n  roleRef:\n    kind: RedisEnterpriseClusterRole\n    name: cluster-admin\n  subjects:\n  - name: alice\n  - name: bob","section_id":"create-a-cluster-role-binding"},{"id":"subjects-ex0","language":"yaml","code":"spec:\n  subjects:\n  - kind: RedisEnterpriseUser\n    name: alice\n  - name: bob    # kind defaults to RedisEnterpriseUser","section_id":"subjects"},{"id":"find-bindings-that-reference-a-role-or-user-ex0","language":"sh","code":"kubectl get redisenterpriserolebinding -o json | \\\n  jq '.items[] | select(.spec.roleRef.name == \"orders-viewer\") | .metadata.name'\nkubectl get redisenterpriseclusterrolebinding -o json | \\\n  jq '.items[] | select(.spec.roleRef.name == \"orders-viewer\") | .metadata.name'","section_id":"find-bindings-that-reference-a-role-or-user"},{"id":"find-bindings-that-reference-a-role-or-user-ex1","language":"sh","code":"kubectl get redisenterpriserolebinding -o json | \\\n  jq '.items[] | select(.spec.subjects[]?.name == \"alice\") | .metadata.name'\nkubectl get redisenterpriseclusterrolebinding -o json | \\\n  jq '.items[] | select(.spec.subjects[]?.name == \"alice\") | .metadata.name'","section_id":"find-bindings-that-reference-a-role-or-user"},{"id":"inspect-binding-status-ex0","language":"sh","code":"kubectl get redisenterpriseuser alice -o jsonpath='{.status.roles}'","section_id":"inspect-binding-status"},{"id":"delete-a-binding-ex0","language":"sh","code":"kubectl delete redisenterpriserolebinding alice-orders-viewer","section_id":"delete-a-binding"}]}
{"schema_version":2,"id":"operate/kubernetes/security/access-control/manage-roles","title":"Manage roles","url":"https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-roles/","summary":"Create RedisEnterpriseRole and RedisEnterpriseClusterRole resources to grant Redis Software permissions on Kubernetes.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","page_type":"content","content_hash":"95544d8f8ac0ad2d9003514e7752f2425e0397ad10728658d5796a5de9410c25","sections":[{"id":"overview","title":"Overview","role":"overview","text":"A role defines a reusable set of Redis Software permissions — Cluster Manager and API access, Redis data-path access, or both — that you grant to users by creating a binding. Redis Software for Kubernetes supports two role kinds, one scoped to one or more databases, the other scoped to the entire cluster:\n\n- `RedisEnterpriseRole` — applies to one or more REDBs selected by `spec.scopes`. Use when you want to grant access to a specific database or set of databases.\n- `RedisEnterpriseClusterRole` — applies cluster-wide, across every REDB. Use for administrative access or for permissions you want everywhere.\n\nFor details on how roles and bindings work together, see [Roles and bindings](https://redis.io/docs/latest/operate/kubernetes/security/access-control#roles-and-bindings). To assign a role to a user, see [Manage role bindings](https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-bindings)."},{"id":"before-you-start","title":"Before you start","role":"content","text":"- Requires Redis Software for Kubernetes operator 8.2.0-12 or later.\n- The role resource must live in the operator namespace. Database scopes resolve to REDBs in that namespace.\n- Decide whether you need [management permissions](#choose-a-management-role), [data-path permissions](#attach-an-acl), or both.\n- If the role references one or more `RedisEnterpriseACL` resources, create those first. See [Manage ACLs](https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-acls)."},{"id":"choose-a-management-role","title":"Choose a management role","role":"content","text":"`spec.managementRole` picks a Redis Software built-in role that controls API and Cluster Manager UI permissions. The allowed values differ by CRD:\n\n| CRD | Allowed `managementRole` values |\n| --- | --- |\n| `RedisEnterpriseRole` | `DBMember`, `DBViewer`, `None` |\n| `RedisEnterpriseClusterRole` | `Admin`, `ClusterMember`, `ClusterViewer`, `DBMember`, `DBViewer`, `UserManager`, `None` |\n\n`None` grants no management permissions and is the default when `managementRole` is omitted. For what each Redis Software role grants, see [Cluster-scoped role definitions](https://redis.io/docs/latest/operate/rs/security/access-control/create-cluster-roles) and [Database-scoped role definitions](https://redis.io/docs/latest/operate/rs/security/access-control/create-db-roles)."},{"id":"create-a-database-role","title":"Create a database role","role":"content","text":"A `RedisEnterpriseRole` must reference at least one database in `spec.scopes`. Each scope picks REDBs by name or by label selector — not both."},{"id":"scope-a-role-by-redb-name","title":"Scope a role by REDB name","role":"content","text":"[code example]\n\n`kind` defaults to `RedisEnterpriseDatabase` and can be omitted.\n\nTo scope a role to several databases by name, list each one as its own scope entry:\n\n[code example]\n\nThe role's `spec.acl` applies to every REDB in `spec.scopes`. If a database needs a different ACL, create a separate role for it."},{"id":"scope-a-role-by-label-selector","title":"Scope a role by label selector","role":"content","text":"Use a selector when you want the role to follow a set of REDBs that share labels, rather than naming each one:\n\n[code example]\n\n`selector.matchExpressions` is also supported."},{"id":"scope-rules","title":"Scope rules","role":"content","text":"- At least one entry in `spec.scopes` is required.\n- Each scope must set `name` or `selector`, not both.\n- `scopes[].kind` must be `RedisEnterpriseDatabase` or empty."},{"id":"create-a-cluster-role","title":"Create a cluster role","role":"content","text":"A `RedisEnterpriseClusterRole` has no `scopes` field — it applies across every REDB in the Redis Software cluster.\n\n[code example]\n\nCommon patterns:\n\n- **Read-only operator** — `managementRole: ClusterViewer`, no ACL.\n- **Cluster admin** — `managementRole: Admin`, no ACL. Use sparingly.\n- **Cluster-wide data access** — `managementRole: None` (or omit) with an `acl`. The ACL applies to every REDB in the cluster."},{"id":"attach-an-acl","title":"Attach an ACL","role":"content","text":"Both role kinds carry a single `RedisEnterpriseACL` reference in `spec.acl`. The ACL grants Redis data-path permissions (commands, key patterns, categories) to users who hold the role.\n\n[code example]\n\nRules:\n\n- `acl.kind` must be `RedisEnterpriseACL` or empty.\n- For a `RedisEnterpriseRole`, the ACL applies to every database the role's scopes select. If you need different data-path access for different databases, create a separate role for each.\n- For a `RedisEnterpriseClusterRole`, the ACL applies to every REDB in the cluster.\n\nSet `spec.managementRole` alone, `spec.acl` alone, or both. A role with neither set effectively grants nothing."},{"id":"update-a-role","title":"Update a role","role":"content","text":"`kubectl apply` (or `kubectl edit`) updates the underlying Redis Software role. The operator reconciles changes to:\n\n- `managementRole` — replaces the management permission set on the Redis Software role.\n- `scopes` — re-resolves which REDBs the role attaches to. REDBs that drop out of the scope have the role's permissions removed.\n- `acl` — re-applies the data-path permissions to scoped REDBs (or cluster-wide for cluster roles).\n\n`status.observedGeneration` reaches the resource's `metadata.generation` once the update has been applied."},{"id":"inspect-role-status","title":"Inspect role status","role":"content","text":"The `status` block is intentionally minimal:\n\n| Field | Meaning |\n| --- | --- |\n| `uid` | Internal Redis Software role UID. Present once the role has reconciled successfully. A role must have a `uid` before it can contribute permissions to any database. |\n| `observedGeneration` | The `metadata.generation` the operator last acted on. Compare with `metadata.generation` to confirm the latest spec has been processed. |\n\nTo see which users currently hold the role, list bindings that reference it:\n\n[code example]\n\nFor cluster roles, replace `redisenterpriserolebinding` with `redisenterpriseclusterrolebinding`."},{"id":"delete-a-role","title":"Delete a role","role":"content","text":"Delete any bindings that reference the role first, then delete the role. Find the bindings with the recipes in [Find bindings that reference a role or user](https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-bindings#find-bindings-that-reference-a-role-or-user), delete each by name, then delete the role:\n\n[code example]\n\nIf a binding still references the role at the moment of deletion, Redis Software may reject the delete and the operator emits a `RoleDeletionBlocked` event. Resolve the blocking binding and retry."},{"id":"troubleshoot","title":"Troubleshoot","role":"errors","text":"Watch reconciliation events with `kubectl describe redisenterpriserole <name>` (or `redisenterpriseclusterrole`). Common issues:\n\n- **`status.uid` is empty** — The role hasn't reconciled. Check the events. Common causes: an ACL reference points to a non-existent `RedisEnterpriseACL`, or admission rejected the spec (missing scopes, scope with both `name` and `selector`, wrong `kind`).\n- **Scope selector matches nothing** — A label-selector scope is valid even if no REDB currently matches. The role contributes permissions only to REDBs that match at reconcile time. Add the labels or fix the selector.\n- **Permissions don't reach the database** — Check `status.uid` on the role, the matching REDB's `status.rolesPermissions`, and confirm a binding assigns the role to the user.\n- **`RoleDeletionBlocked`** — A binding still references the role in Redis Software. Delete the binding first.\n\nFor full field details, see the [`RedisEnterpriseRole`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_role_api) and [`RedisEnterpriseClusterRole`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_role_api) API reference."},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Roles and bindings](https://redis.io/docs/latest/operate/kubernetes/security/access-control#roles-and-bindings) — the conceptual model.\n- [Manage role bindings](https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-bindings) — assign a role to a user.\n- [Manage ACLs](https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-acls) — define the data-path permissions a role references.\n- [Manage users](https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-users) — create the users that bindings target.\n- [Migrate from REDB rolesPermissions](https://redis.io/docs/latest/operate/kubernetes/security/access-control/migrate-rolespermissions) — move from the deprecated `RedisEnterpriseDatabase.spec.rolesPermissions` field to the new CRD model."}],"examples":[{"id":"scope-a-role-by-redb-name-ex0","language":"yaml","code":"apiVersion: app.redislabs.com/v1alpha1\nkind: RedisEnterpriseRole\nmetadata:\n  name: orders-viewer\nspec:\n  managementRole: DBViewer\n  scopes:\n  - kind: RedisEnterpriseDatabase\n    name: orders\n  acl:\n    kind: RedisEnterpriseACL\n    name: read-only","section_id":"scope-a-role-by-redb-name"},{"id":"scope-a-role-by-redb-name-ex1","language":"yaml","code":"spec:\n  managementRole: DBViewer\n  scopes:\n  - name: orders\n  - name: customers\n  - name: inventory\n  acl:\n    name: read-only","section_id":"scope-a-role-by-redb-name"},{"id":"scope-a-role-by-label-selector-ex0","language":"yaml","code":"apiVersion: app.redislabs.com/v1alpha1\nkind: RedisEnterpriseRole\nmetadata:\n  name: prod-db-viewer\nspec:\n  managementRole: DBViewer\n  scopes:\n  - selector:\n      matchLabels:\n        environment: production\n  acl:\n    name: read-only","section_id":"scope-a-role-by-label-selector"},{"id":"create-a-cluster-role-ex0","language":"yaml","code":"apiVersion: app.redislabs.com/v1alpha1\nkind: RedisEnterpriseClusterRole\nmetadata:\n  name: support-readonly\nspec:\n  managementRole: ClusterViewer\n  acl:\n    name: read-only","section_id":"create-a-cluster-role"},{"id":"attach-an-acl-ex0","language":"yaml","code":"spec:\n  acl:\n    kind: RedisEnterpriseACL\n    name: read-only    # kind defaults to RedisEnterpriseACL and can be omitted","section_id":"attach-an-acl"},{"id":"inspect-role-status-ex0","language":"sh","code":"kubectl get redisenterpriserolebinding -o yaml | \\\n  yq '.items[] | select(.spec.roleRef.name == \"orders-viewer\")'","section_id":"inspect-role-status"},{"id":"delete-a-role-ex0","language":"sh","code":"kubectl delete redisenterpriserolebinding alice-orders-viewer\nkubectl delete redisenterpriserole orders-viewer","section_id":"delete-a-role"}]}
{"schema_version":2,"id":"operate/kubernetes/security/access-control/manage-users","title":"Manage users","url":"https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-users/","summary":"Create and update Redis Software users on Kubernetes with the RedisEnterpriseUser custom resource.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","page_type":"content","content_hash":"d4c989500f3a081dae04a0418e216989a28047d9ff25dab9eadbda80fef234d9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"A `RedisEnterpriseUser` resource defines a Redis Software user. The operator creates the user in Redis Software and keeps it in sync with the resource. Passwords live in Kubernetes Secrets that the resource references by name.\n\nThis page covers creating users, changing passwords, and recovering locked accounts. To grant a user permissions, see [Manage role bindings](https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-bindings)."},{"id":"before-you-start","title":"Before you start","role":"content","text":"- Requires Redis Software for Kubernetes operator 8.2.0-12 or later.\n- The `RedisEnterpriseUser` resource and every referenced password Secret must live in the operator namespace.\n- Passwords must satisfy the cluster's [password complexity rules](https://redis.io/docs/latest/operate/rs/security/access-control/manage-passwords/password-complexity-rules)."},{"id":"create-a-user","title":"Create a user","role":"content","text":"1. Create a Secret with the password under the key `password`:\n\n    [code example]\n\n2. Create the `RedisEnterpriseUser` resource:\n\n    [code example]\n\n3. Apply the resource and confirm the operator created the user:\n\n    [code example]\n\n    `status.uid` holds the Redis Software user ID once reconciliation succeeds. `status.signinStatus` reports the user's current sign-in state.\n\nThe new user has no permissions until you create a role binding. The operator assigns the Redis Software `none` role so the user is never roleless."},{"id":"required-and-optional-fields","title":"Required and optional fields","role":"content","text":"| Field | Required | Notes |\n| --- | --- | --- |\n| `spec.email` | Yes | Must be unique in the cluster. |\n| `spec.username` | No | Defaults to a generated value. ASCII only, excluding `&`, `<`, `>`, `\"`. The effective value appears in `status.username`. |\n| `spec.passwordSecrets` | Yes | At least one Secret. Each Secret must have a `password` key. |\n| `spec.passwordMode` | No | `Single` (default) or `Rotatable`. See [Choose a password mode](#choose-a-password-mode). |\n| `spec.alerts` | No | Email alert settings. Effective only when [cluster alerts](https://redis.io/docs/latest/operate/rs/clusters/configure/cluster-settings#alert-settings) are configured. |\n\nFor the full schema, see [`RedisEnterpriseUser`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_user_api)."},{"id":"use-a-generated-username","title":"Use a generated username","role":"content","text":"If you omit `spec.username`, the operator assigns one and reports it in `status.username`. Read it with:\n\n[code example]\n\nUse that value when you sign in to Redis Software. To reference the user from a binding's `subjects` list, use the resource name (`metadata.name`) instead — not the generated username."},{"id":"choose-a-password-mode","title":"Choose a password mode","role":"content","text":"`spec.passwordMode` controls how passwords change.\n\n- **`Single`** (default) — Exactly one entry in `passwordSecrets`. To change the password, update the value in the Secret or point the resource at a different Secret. Suitable for interactive users.\n- **`Rotatable`** — One or two entries in `passwordSecrets`. The operator marks each referenced Kubernetes Secret immutable so the password can't be edited in place. Suitable for service accounts that need zero-downtime credential rotation. Vault-backed secrets are treated as immutable but aren't modified by the operator.\n\nYou can't switch modes while the user has two password secrets. Reduce to one secret first."},{"id":"change-a-password","title":"Change a password","role":"content","text":""},{"id":"single-mode","title":"Single mode","role":"content","text":"Edit the Secret value, then re-apply it. The operator detects the new version and updates the user's password.\n\n[code example]\n\nAlternatively, create a new Secret and update `spec.passwordSecrets[0].name` to point at it."},{"id":"rotatable-mode-zero-downtime-rotation","title":"Rotatable mode (zero-downtime rotation)","role":"content","text":"In Rotatable mode the operator makes referenced Kubernetes Secrets immutable, so you rotate by adding a new Secret alongside the old one.\n\n1. Create a new Secret with the next password.\n2. Add it to `spec.passwordSecrets`. Both passwords now authenticate.\n3. Update clients to use the new password.\n4. Remove the old Secret from `spec.passwordSecrets`. Only the new password authenticates.\n5. Delete the old Secret when nothing else references it.\n\n`status.passwordSecrets` lists each active Secret with its resolved version."},{"id":"update-the-email-address","title":"Update the email address","role":"content","text":"You can change `spec.email` only while `passwordSecrets` contains exactly one entry. If a Rotatable rotation is in progress, reduce to a single secret first, change the email, then add the second secret back."},{"id":"configure-email-alerts","title":"Configure email alerts","role":"configuration","text":"Email alerts deliver only when the cluster has alert email settings configured. The user's alerts have two layers: a master `enabled` toggle for the whole user, and a per-category toggle for cluster alerts. Database alerts are configured by listing the databases the user should receive alerts for:\n\n[code example]\n\nOmit `databaseAlerts.databases` to receive alerts for every database."},{"id":"inspect-user-status","title":"Inspect user status","role":"content","text":"The `status` block reports observed state from Redis Software:\n\n| Field | Meaning |\n| --- | --- |\n| `uid` | Internal Redis Software user ID. Appears once the user is reconciled. |\n| `username` | Effective username, including any default the operator assigned. |\n| `roles` / `rolesDisplay` | Roles currently bound to the user. |\n| `signinStatus` | `Unknown`, `Active`, `Locked`, or `PasswordExpired`. |\n| `passwordIssueDate` | When Redis Software last accepted the user's password. |\n| `passwordSecrets` | Each referenced Secret with the resolved version the operator reconciled. |\n| `observedGeneration` | The `metadata.generation` the operator last acted on. Compare with `metadata.generation` to confirm reconciliation has caught up. |\n| `conditions` | The `RolesBound` condition reports whether every bound role resolves. |"},{"id":"recover-a-locked-user","title":"Recover a locked user","role":"content","text":"`status.signinStatus: Locked` means the user failed too many sign-in attempts. The operator skips password changes while the user is locked, so you must update the resource before unlocking — otherwise the operator can later reconcile the old desired password back onto the user.\n\n1. Update the password in the `RedisEnterpriseUser` source of truth: change the referenced Secret value (Single mode) or add a new Secret reference (Rotatable mode).\n2. Follow the [Redis Software unlock procedure](https://redis.io/docs/latest/operate/rs/security/access-control/manage-users/login-lockout#unlock-locked-user-accounts) to reset and unlock the account in the cluster.\n\n`status.signinStatus: PasswordExpired` clears once you set a new password through the resource."},{"id":"delete-a-user","title":"Delete a user","role":"content","text":"Delete every binding that references the user before deleting the user itself. Use the recipes in [Find bindings that reference a role or user](https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-bindings#find-bindings-that-reference-a-role-or-user) to list them, delete each by name, then delete the user:\n\n[code example]\n\nThe operator removes the user from Redis Software. A finalizer keeps the Kubernetes resource until the Redis Software user and any related Secret finalizers are cleaned up; deletion may take longer or stall if the Redis Software API is unavailable.\n\nPassword Secrets aren't deleted — remove them separately when nothing else references them."},{"id":"troubleshoot","title":"Troubleshoot","role":"errors","text":"Watch reconciliation events with `kubectl describe redisenterpriseuser <name>`. Common events:\n\n| Event | Meaning |\n| --- | --- |\n| `PasswordSecretMissing` | A name in `passwordSecrets` doesn't exist in the operator namespace. |\n| `PasswordSecretInvalid` | The Secret exists but has no `password` key, or the value is empty. |\n| `UserPasswordAdded` / `UserPasswordReplaced` / `UserPasswordDeleted` | Normal reconciliation actions. Useful for confirming a rotation step. |\n| `UserLocked` | Password operations are skipped because the user is locked. See [Recover a locked user](#recover-a-locked-user). |\n| `MissingRoleUIDs` | The user has Redis Software role UIDs that no longer map back to a Kubernetes role resource. |\n| `RSObjectNotFound` | A Redis Software object the user previously resolved against is gone. |\n| `RSOperationFailed` | A Redis Software API call failed; check the message for details. |\n\nOther things to check:\n\n- **`status.signinStatus: Unknown`** — The operator hasn't reconciled the user yet, or it can't read the referenced Secret. Check `PasswordSecretMissing` and `PasswordSecretInvalid` events.\n- **`RolesBound` condition is `False` with reason `RoleNotFound`** — A binding references this user but points at a role that doesn't exist. Create the role or fix the binding.\n- **Secret edit rejected** — In Rotatable mode the operator sets `immutable: true` on the Secret. Create a new Secret instead of editing an existing one.\n- **Cluster Manager UI shows a different role than expected** — Roles come from `RedisEnterpriseRoleBinding` and `RedisEnterpriseClusterRoleBinding` resources, not from the user spec. Check the bindings, not the user."},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Manage role bindings](https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-bindings) — assign roles to this user.\n- [Default user](https://redis.io/docs/latest/operate/rs/security/access-control/manage-users/default-user) — the built-in cluster admin account, managed outside the CRD model.\n- [Password complexity rules](https://redis.io/docs/latest/operate/rs/security/access-control/manage-passwords/password-complexity-rules) and [password expiration](https://redis.io/docs/latest/operate/rs/security/access-control/manage-passwords/password-expiration).\n- [`RedisEnterpriseUser` API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_user_api)."}],"examples":[{"id":"create-a-user-ex0","language":"sh","code":"kubectl create secret generic alice-password \\\n      --from-literal=password='S0me-Str0ng-Passw0rd!'","section_id":"create-a-user"},{"id":"create-a-user-ex1","language":"yaml","code":"apiVersion: app.redislabs.com/v1alpha1\n    kind: RedisEnterpriseUser\n    metadata:\n      name: alice\n    spec:\n      email: alice@example.com\n      username: alice\n      passwordSecrets:\n      - name: alice-password","section_id":"create-a-user"},{"id":"create-a-user-ex2","language":"sh","code":"kubectl apply -f alice.yaml\n    kubectl get redisenterpriseuser alice -o yaml","section_id":"create-a-user"},{"id":"use-a-generated-username-ex0","language":"sh","code":"kubectl get redisenterpriseuser alice -o jsonpath='{.status.username}'","section_id":"use-a-generated-username"},{"id":"single-mode-ex0","language":"sh","code":"kubectl create secret generic alice-password \\\n  --from-literal=password='N3w-Str0ng-Passw0rd!' \\\n  --dry-run=client -o yaml | kubectl apply -f -","section_id":"single-mode"},{"id":"configure-email-alerts-ex0","language":"yaml","code":"spec:\n  alerts:\n    enabled: true            # master toggle; required for any alert to deliver\n    clusterAlerts:\n      enabled: true          # opt in to cluster-level alerts\n    databaseAlerts:\n      databases:             # list specific databases, or omit to receive all\n      - name: my-database","section_id":"configure-email-alerts"},{"id":"delete-a-user-ex0","language":"sh","code":"kubectl delete redisenterpriserolebinding alice-orders-viewer\nkubectl delete redisenterpriseuser alice","section_id":"delete-a-user"}]}
{"schema_version":2,"id":"operate/kubernetes/security/access-control/migrate-rolespermissions","title":"Migrate from REDB rolesPermissions","url":"https://redis.io/docs/latest/operate/kubernetes/security/access-control/migrate-rolespermissions/","summary":"Move database role assignments from the deprecated RedisEnterpriseDatabase.spec.rolesPermissions field to the RBAC CRD model.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","page_type":"content","content_hash":"746e9a636943183a62efef07f2dfc8a569574b240ea6c3e41dcf7e0b99d77bf4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"`RedisEnterpriseDatabase.spec.rolesPermissions` assigns Redis Software role and ACL names directly to a database. The field is deprecated, replaced by the RBAC CRDs (`RedisEnterpriseRole`, `RedisEnterpriseACL`, and bindings), which let you manage roles, ACLs, and assignments as Kubernetes resources.\n\nThis page covers moving an existing database from `rolesPermissions` to the CRD model, then disabling the deprecated field at the cluster level."},{"id":"before-you-start","title":"Before you start","role":"content","text":"- Requires Redis Software for Kubernetes operator 8.2.0-12 or later.\n- Every user who currently holds access through `rolesPermissions` must exist as a `RedisEnterpriseUser` resource before you can bind a CRD role to them. If you created users through the Redis Software REST API or Cluster Manager UI, migrate them first. See [Manage users](https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-users).\n- List which Redis Software users hold each role. The CRD inventory only captures the database side of the assignment; the user-to-role mapping lives in Redis Software. Pull it from the Cluster Manager UI or the [Redis Software users REST API](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/user)."},{"id":"how-the-two-sources-interact","title":"How the two sources interact","role":"content","text":"By default, the operator reconciles both sources of database role permissions:\n\n- The deprecated `RedisEnterpriseDatabase.spec.rolesPermissions` list on each REDB.\n- Permissions derived from `RedisEnterpriseRole` resources that scope to the REDB.\n\nThe operator merges and de-duplicates the two sets before sending them to Redis Software. `RedisEnterpriseDatabase.status.rolesPermissions` shows the merged result:\n\n- Entries from the deprecated field use Redis Software's internal role and ACL names.\n- Entries from CRDs reference `RedisEnterpriseRole` and `RedisEnterpriseACL` resources.\n\nYou can run both sources in parallel during migration. Once every database is completely migrated, disable the deprecated field cluster-wide (see [Disable the deprecated field](#disable-the-deprecated-field))."},{"id":"reference-existing-redis-software-objects","title":"Reference existing Redis Software objects","role":"related","text":"You don't have to recreate every database, ACL, and role as a custom resource. When an object was created directly through the Redis Software REST API or Cluster Manager UI, a CRD reference can point at it by name using an internal kind, so you can bring it under CRD-managed bindings without redefining it:\n\n| Field | Internal kind | References |\n| --- | --- | --- |\n| `scopes[].kind` | `bdb` | An internal Redis Software database by name. Name only — no selector. The role grants management access only; its ACL isn't bound to that database. |\n| `acl.kind` | `redis_acl` | An internal Redis Software ACL by name. |\n| `roleRef.kind` | `role` | An internal Redis Software role by name, from a binding. |\n\nThe reference resolves by name against the existing Redis Software object. Use internal references to migrate assignments incrementally, then replace them with full `RedisEnterpriseDatabase`, `RedisEnterpriseACL`, and `RedisEnterpriseRole` resources as you bring each object under the CRD model."},{"id":"migrate-a-database","title":"Migrate a database","role":"content","text":"For each database, capture what `rolesPermissions` currently grants, recreate it as CRDs, verify the result, then remove the deprecated field."},{"id":"1-inventory-the-existing-entries","title":"1. Inventory the existing entries","role":"content","text":"List the `rolesPermissions` entries on every REDB in the operator namespace:\n\n[code example]\n\nEach entry has a `role` (Redis Software role name) and an `acl` (Redis Software ACL name). The `type` is always `redis-enterprise`."},{"id":"2-create-redisenterpriseacl-resources","title":"2. Create RedisEnterpriseACL resources","role":"content","text":"For every distinct ACL name in the inventory, create a `RedisEnterpriseACL` resource that holds the same rule string. See [Manage ACLs](https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-acls).\n\n[code example]\n\nThe resource `metadata.name` doesn't have to match the original Redis Software ACL name, but matching makes the merged `status.rolesPermissions` easier to read."},{"id":"3-create-redisenterpriserole-resources","title":"3. Create RedisEnterpriseRole resources","role":"content","text":"For each role-ACL-REDB combination in the inventory, create a `RedisEnterpriseRole` scoped to the target REDB. See [Manage roles](https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-roles).\n\n[code example]\n\nSet `managementRole` to match the Redis Software role's management level. If the original entry only granted data-path access, omit `managementRole` — it defaults to `None`.\n\nIf the same role-ACL pair applies to more than one REDB, collapse them into a single `RedisEnterpriseRole` with multiple `scopes` entries instead of creating one role per REDB:\n\n[code example]"},{"id":"4-create-bindings-for-affected-users","title":"4. Create bindings for affected users","role":"content","text":"For each user that previously gained access through `rolesPermissions`, create a `RedisEnterpriseRoleBinding` that references the new role. See [Manage role bindings](https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-bindings).\n\n[code example]"},{"id":"5-verify","title":"5. Verify","role":"content","text":"Confirm Redis Software still grants the expected access:\n\n[code example]\n\nThe merged `status.rolesPermissions` shows entries from both sources. Before you remove the deprecated entries, authenticate as one of the affected users and run a Redis command the role allows:\n\n[code example]\n\nReplace the endpoint and port with the values from the REDB's `status.publicService` (or `status.internalService`)."},{"id":"6-remove-the-deprecated-entries","title":"6. Remove the deprecated entries","role":"content","text":"Once verification passes, remove the migrated entries from `spec.rolesPermissions`:\n\n[code example]\n\nAfter the next reconcile, `status.rolesPermissions` should contain only CRD-derived entries. Repeat for every REDB."},{"id":"disable-the-deprecated-field","title":"Disable the deprecated field","role":"content","text":"After every database is completely migrated, switch the cluster policy to ignore `spec.rolesPermissions` so no one can re-introduce it:\n\n[code example]\n\nWith `allowREDBRolesPermissions: false`:\n\n- The operator stops reconciling `spec.rolesPermissions` on every REDB.\n- The operator removes from Redis Software any permissions that came only from `spec.rolesPermissions`.\n- The operator emits a `RolesPermissionsIgnored` event on any REDB that still has the deprecated field set.\n\nThe default is `true` (both sources reconciled). Set the flag back to `true` only as a rollback."},{"id":"roll-back","title":"Roll back","role":"content","text":"To revert to the deprecated field, set `allowREDBRolesPermissions: true` (or omit it) on the `RedisEnterpriseCluster` and re-add the previous entries to each REDB's `spec.rolesPermissions`. CRD-managed permissions continue to work in parallel."},{"id":"troubleshoot","title":"Troubleshoot","role":"errors","text":"- **A user is missing access after you remove the deprecated entries** — A `RedisEnterpriseRole` or binding is missing for that user. Check `RedisEnterpriseUser.status.roles` against the original inventory and add the missing CRDs.\n- **`status.rolesPermissions` is empty after migration** — The new roles haven't reconciled. Each `RedisEnterpriseRole` needs `status.uid` populated before it contributes to a REDB. Check the role's events.\n- **`RolesPermissionsIgnored` event on a REDB after disabling the field** — A `spec.rolesPermissions` entry remains on the REDB. Edit the REDB and remove the field entirely."},{"id":"related-topics","title":"Related topics","role":"related","text":"- [Manage roles](https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-roles) — full details on `RedisEnterpriseRole` and `RedisEnterpriseClusterRole`.\n- [Manage ACLs](https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-acls) — define the data-path permissions roles reference.\n- [Manage role bindings](https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-bindings) — assign the new roles to users.\n- [`RedisEnterpriseDatabase` API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api) — the source schema, including the deprecated `rolesPermissions` field.\n- [`RedisEnterpriseCluster` API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api) — the `accessControl.policy.allowREDBRolesPermissions` flag."}],"examples":[{"id":"1-inventory-the-existing-entries-ex0","language":"sh","code":"kubectl get redisenterprisedatabase -o yaml | \\\n  yq '.items[] | {name: .metadata.name, rolesPermissions: .spec.rolesPermissions}'","section_id":"1-inventory-the-existing-entries"},{"id":"2-create-redisenterpriseacl-resources-ex0","language":"yaml","code":"apiVersion: app.redislabs.com/v1alpha1\nkind: RedisEnterpriseACL\nmetadata:\n  name: read-only\nspec:\n  acl: \"+@read ~*\"","section_id":"2-create-redisenterpriseacl-resources"},{"id":"3-create-redisenterpriserole-resources-ex0","language":"yaml","code":"apiVersion: app.redislabs.com/v1alpha1\nkind: RedisEnterpriseRole\nmetadata:\n  name: orders-viewer\nspec:\n  managementRole: DBViewer\n  scopes:\n  - name: orders\n  acl:\n    name: read-only","section_id":"3-create-redisenterpriserole-resources"},{"id":"3-create-redisenterpriserole-resources-ex1","language":"yaml","code":"spec:\n  managementRole: DBViewer\n  scopes:\n  - name: orders\n  - name: customers\n  - name: inventory\n  acl:\n    name: read-only","section_id":"3-create-redisenterpriserole-resources"},{"id":"4-create-bindings-for-affected-users-ex0","language":"yaml","code":"apiVersion: app.redislabs.com/v1alpha1\nkind: RedisEnterpriseRoleBinding\nmetadata:\n  name: alice-orders-viewer\nspec:\n  roleRef:\n    name: orders-viewer\n  subjects:\n  - name: alice","section_id":"4-create-bindings-for-affected-users"},{"id":"5-verify-ex0","language":"sh","code":"kubectl get redisenterprisedatabase orders -o yaml | \\\n  yq '.status.rolesPermissions'","section_id":"5-verify"},{"id":"5-verify-ex1","language":"sh","code":"redis-cli -h <db-endpoint> -p <db-port> --user alice --pass <password> GET some:key","section_id":"5-verify"},{"id":"6-remove-the-deprecated-entries-ex0","language":"sh","code":"kubectl edit redisenterprisedatabase orders","section_id":"6-remove-the-deprecated-entries"},{"id":"disable-the-deprecated-field-ex0","language":"yaml","code":"apiVersion: app.redislabs.com/v1\nkind: RedisEnterpriseCluster\nmetadata:\n  name: rec\nspec:\n  accessControl:\n    policy:\n      allowREDBRolesPermissions: false","section_id":"disable-the-deprecated-field"}]}
{"schema_version":2,"id":"operate/kubernetes/security/allow-resource-adjustment","title":"Allow automatic resource adjustment","url":"https://redis.io/docs/latest/operate/kubernetes/security/allow-resource-adjustment/","summary":"Enable automatic system resource adjustments for Redis Enterprise to increase file descriptor limits.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","page_type":"content","content_hash":"f941f0c3acbf9400df60ab9f962d5ed29ae51f1bd72285786e8b45c81a8288e3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise for Kubernetes 7.22.0-6 introduces the ability to run with automatic resource adjustment disabled, which drops all capabilities from the Redis Enterprise container and sets `allowPrivilegeEscalation` to `false`. All other security-related settings remain the same as in automatic resource adjustment enabled. Automatic resource adjustment disabled is the default for installations and upgrades of the Redis Enterprise operator for versions 7.22.0-6 and later."},{"id":"default-behavior","title":"Default behavior","role":"content","text":"Automatic resource adjustment is disabled by default for installations and upgrades of the Redis Enterprise operator for versions 7.22.0-6 and later. This default behavior is in effect if REC spec has `allowAutoAdjustment` set to `false` or removed.\n\nIf automatic resource adjustment is disabled, the REC security context looks like this:\n\n[code example]"},{"id":"enable-automatic-resource-adjustment","title":"Enable automatic resource adjustment","role":"content","text":"To allow the Redis Enterprise container to adjust system resource limits automatically, set `allowAutoAdjustment` to `true`. This will grant the container elevated capabilities such as `SYS_RESOURCE`. Note that changing this value on a running cluster will trigger a rolling update.\n\n[code example]\n\nEnabling automatic resource adjustment results in the following security context:\n\n**Note:** Enabling `allowAutoAdjustment` grants the container the `SYS_RESOURCE` capability and permits privilege escalation.\n\n[code example]"},{"id":"openshift-upgrades","title":"OpenShift upgrades","role":"content","text":"If you're upgrading OpenShift to 7.22.0-6, update your existing SCC (security context constraint).\n\nIf running with automatic resource adjustment disabled, remove the custom `redis-enterprise-scc-v2` SCC and unbind it from the REC service account after you complete the upgrade.\n\n[code example]\n\n[code example]\n\nIf running with automatic resource adjustment enabled, manually reapply the [security context constraints (SCC)](https://docs.openshift.com/container-platform/4.8/authentication/managing-security-context-constraints.html) file ([`scc.yaml`](https://redis.io/docs/latest/operate/kubernetes/deployment/openshift/openshift-cli#deploy-the-operator)).\n\n[code example]\n\n[code example]"},{"id":"new-openshift-installations","title":"New OpenShift installations","role":"content","text":"New installations of Redis Enterprise for Kubernetes 7.22.0-6 and later automatically run with automatic resource adjustment disabled, using a built-in `nonroot-v2` which is more secure and less permissive.\n\nTo enable automatic resource adjustment after installation, apply and grant permissions to the `redis-enterprise-scc-v2` SCC.\n\n1. Apply the `scc.yaml` file.\n\n   > [!WARNING]\n   > Do not edit this file.\n\n    [code example]\n\n    You should see the following output:\n\n    [code example]\n\n1. Provide the operator permissions for the pods.\n\n    [code example]"},{"id":"sys_resource","title":"SYS_RESOURCE","role":"content","text":"Some Redis Enterprise processes may require the `SYS_RESOURCE` capability to raise resource limits, such as the maximum number of open file descriptors.\n\nSome Redis Enterprise processes require the ability to open at least 100,000 file descriptors. If the default is lower and `SYS_RESOURCE` is not enabled, these processes may fail."},{"id":"choose-whether-to-enable-automatic-resource-adjustment","title":"Choose whether to enable automatic resource adjustment","role":"content","text":"Use the following guidance to decide whether to enable automatic resource adjustment:\n\n- If you're running on a major cloud provider such as AWS, GKE, or AKS, automatic resource adjustment disabled is likely sufficient.\n- If you're running on-prem or using Kubespray, verify your file descriptor limits. You can:\n  - Configure limits manually and use automatic resource adjustment disabled.\n  - Enable automatic resource adjustment to allow Redis Enterprise to increase limits, which requires privilege escalation.\n\nIf you are already running a Redis Enterprise cluster on Kubernetes, your worker nodes are likely configured correctly. In this case, it is safe to upgrade the operator and use automatic resource adjustment disabled.\n\nBased on our testing, all major cloud providers configure Kubernetes worker nodes with file descriptor limits well above the required minimum. These environments typically work without enabling automatic resource adjustment. The only known exception is clusters created with [Kubespray](https://kubespray.io/#/), which sets default file descriptor limits below the required 100,000. If you use Kubespray with default settings, you must run the operator with automatic resource adjustment enabled."}],"examples":[{"id":"default-behavior-ex0","language":"yaml","code":"securityContext:\n  allowPrivilegeEscalation: false\n  capabilities:\n    drop:\n    - ALL\n  privileged: false\n  readOnlyRootFilesystem: false","section_id":"default-behavior"},{"id":"enable-automatic-resource-adjustment-ex0","language":"yaml","code":"spec:\n  securityContext:\n    resourceLimits:\n      allowAutoAdjustment: true","section_id":"enable-automatic-resource-adjustment"},{"id":"enable-automatic-resource-adjustment-ex1","language":"yaml","code":"securityContext:\n  allowPrivilegeEscalation: true\n  capabilities:\n    add:\n    - SYS_RESOURCE\n    drop:\n    - ALL\n  privileged: false\n  readOnlyRootFilesystem: false","section_id":"enable-automatic-resource-adjustment"},{"id":"openshift-upgrades-ex0","language":"sh","code":"oc delete scc/redis-enterprise-scc-v2","section_id":"openshift-upgrades"},{"id":"openshift-upgrades-ex1","language":"sh","code":"oc adm policy remove-scc-from-user redis-enterprise-scc-v2 -z <service-account-name>","section_id":"openshift-upgrades"},{"id":"openshift-upgrades-ex2","language":"sh","code":"oc apply -f openshift/scc.yaml","section_id":"openshift-upgrades"},{"id":"openshift-upgrades-ex3","language":"sh","code":"oc adm policy add-scc-to-user redis-enterprise-scc-v2 \\\n  system:serviceaccount:<my-project>:<rec-name>","section_id":"openshift-upgrades"},{"id":"new-openshift-installations-ex0","language":"sh","code":"oc apply -f openshift/scc.yaml","section_id":"new-openshift-installations"},{"id":"new-openshift-installations-ex1","language":"sh","code":"securitycontextconstraints.security.openshift.io \"redis-enterprise-scc-v2\" configured","section_id":"new-openshift-installations"},{"id":"new-openshift-installations-ex2","language":"sh","code":"oc adm policy add-scc-to-user redis-enterprise-scc-v2 \\\n      system:serviceaccount:<my-project>:<rec>","section_id":"new-openshift-installations"}]}
{"schema_version":2,"id":"operate/kubernetes/security/authentication/configuration-secrets","title":"Store configuration in Kubernetes Secrets","url":"https://redis.io/docs/latest/operate/kubernetes/security/authentication/configuration-secrets/","summary":"Store Redis Enterprise configuration items in Kubernetes Secrets for automatic updates and secure management.","aliases":["/operate/kubernetes/security/configuration-secrets/"],"tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","page_type":"content","content_hash":"85f7019c006d590d471f7a360c4ac1c1329fb6c2feba81beb26d81e753b61074","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can store Redis Enterprise configuration items in Kubernetes Secrets for automatic updates and secure management. When you update these Secrets, the operator immediately reads the changes and propagates them to the Redis Enterprise Cluster (REC)."},{"id":"license-configuration","title":"License configuration","role":"content","text":"Redis Enterprise clusters require a valid license. You can apply licenses using Kubernetes Secrets (recommended) or embed them directly in the cluster specification."},{"id":"determine-your-cluster-fqdn","title":"Determine your cluster FQDN","role":"content","text":"To configure licensing, you need your Redis Enterprise cluster's fully qualified domain name (FQDN). Use this format: `<REC-name>.<namespace>.svc.cluster.local`\n\nFor example: `my-rec.my-ns.svc.cluster.local`"},{"id":"use-a-kubernetes-secret-recommended","title":"Use a Kubernetes Secret (recommended)","role":"content","text":"1. Create a secret from your license file:\n\n    [code example]\n\n2. Add the secret reference to your REC specification:\n\n    [code example]"},{"id":"embed-license-directly-in-rec-specification","title":"Embed license directly in REC specification","role":"content","text":"Alternatively, you can embed the license directly in the REC YAML:\n\n[code example]\n\n> [!NOTE]\n> You must include the pipe symbol (`|`) after `license:` and maintain proper indentation."},{"id":"cluster-credential-configuration","title":"Cluster credential configuration","role":"content","text":"Redis Enterprise clusters require admin credentials for the operator to manage the cluster. You can configure these credentials using Kubernetes Secrets."},{"id":"default-behavior","title":"Default behavior","role":"content","text":"By default, the operator automatically creates a secret with a random username and auto-generated password during cluster creation. The secret name defaults to the cluster name."},{"id":"customize-the-credential-secret","title":"Customize the credential secret","role":"content","text":"You can customize the credential secret name during cluster creation using the `clusterCredentialSecretName` field in your REC specification. The secret must contain `username` and `password` fields.\n\n> [!NOTE]\n> The `clusterCredentialSecretName` field cannot be changed after cluster creation.\n\nFor detailed instructions, see [Customize the credential secret name](https://redis.io/docs/latest/operate/kubernetes/security/authentication/manage-rec-credentials#customize-the-credential-secret-name)."},{"id":"tls-certificate-configuration","title":"TLS certificate configuration","role":"content","text":"You can store TLS certificates in Kubernetes Secrets to secure communication between clients and Redis Enterprise databases."},{"id":"client-certificates-for-mtls","title":"Client certificates for mTLS","role":"content","text":"1. Create a secret with your client certificate:\n\n    [code example]\n\n2. Add the secret to your REDB using the `clientAuthenticationCertificates` property. See [Add client certificates](https://redis.io/docs/latest/operate/kubernetes/security/certificates/add-client-certificates) for details."},{"id":"service-certificates","title":"Service certificates","role":"content","text":"To configure certificates for proxy, API, or other services, create secrets with certificate and key files:\n\n[code example]"},{"id":"internode-encryption-certificates","title":"Internode encryption certificates","role":"content","text":"You can provide custom certificates for control plane and data plane internode encryption. Create separate secrets for each encryption type:\n\n[code example]\n\n[code example]\n\nReference these secrets in your REC specification under `spec.certificates`. See [Internode encryption](https://redis.io/docs/latest/operate/kubernetes/security/certificates/internode-encryption) for complete configuration details."},{"id":"secrets-and-pem-files-in-redis-enterprise-pods","title":"Secrets and PEM files in Redis Enterprise pods","role":"content","text":"Redis Enterprise pods use Kubernetes Secrets and PEM-encoded certificates and keys for cluster formation, node identity, encrypted communication, and automated recovery. Their presence is expected — not a sign of compromise.\n\nYou create the Secrets. The operator references them and manages their lifecycle (for example, when you rename the credential Secret). TLS, license, and client authentication Secrets are always user-supplied."},{"id":"what-youll-see-in-the-pod","title":"What you'll see in the pod","role":"content","text":"- **Mounted Secret volumes** at operator-managed paths such as:\n  - `/opt/redislabs/credentials` — cluster admin credential Secret.\n  - `/opt/redislabs/proxy` — call-home proxy credentials, when configured.\n- **PEM-encoded certificates and keys** for TLS, internode encryption, and proxy or database endpoints. The operator applies these certificates through the cluster's REST API rather than as Secret volume mounts, and Redis Enterprise writes them to the pod filesystem. Exact paths vary by version and component."},{"id":"what-the-secrets-contain","title":"What the Secrets contain","role":"content","text":"Field names vary by deployment.\n\n- **Cluster admin credentials** — `username` and `password` in the Secret named by `clusterCredentialSecretName`.\n- **License** — `license` field in the Secret named by `licenseSecretName`.\n- **Cluster Certificate Authority (CA)** — `ca.crt` or `ca.pem`. Validates peer certificates for mutual TLS. Optional.\n- **Service TLS certificates and keys** for API, Cluster Manager (CM), metrics exporter, proxy, syncer, and LDAP. Fields: `certificate`, `cert`, or `tls.crt`, plus `key` or `tls.key`. See [Service certificates](#service-certificates).\n- **Client authentication certificates** for databases. Set in the Redis Enterprise database (REDB) `clientAuthenticationCertificates` field."},{"id":"best-practices","title":"Best practices","role":"content","text":"- Store sensitive configuration in Secrets rather than directly in YAML files.\n- Use `--from-file` to avoid manual base64 encoding.\n- Create secrets in the same namespace as your REC or REDB resources.\n- Use descriptive secret names for easy identification.\n- Regularly rotate certificates and update secrets."},{"id":"see-also","title":"See also","role":"related","text":"- [Manage REC credentials](https://redis.io/docs/latest/operate/kubernetes/security/authentication/manage-rec-credentials)\n- [Manage REC certificates](https://redis.io/docs/latest/operate/kubernetes/security/certificates/manage-rec-certificates)\n- [Add client certificates](https://redis.io/docs/latest/operate/kubernetes/security/certificates/add-client-certificates)\n- [Redis Enterprise Cluster API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api)"}],"examples":[{"id":"use-a-kubernetes-secret-recommended-ex0","language":"sh","code":"kubectl -n <namespace> create secret generic rec-license --from-file=license=./license.txt","section_id":"use-a-kubernetes-secret-recommended"},{"id":"use-a-kubernetes-secret-recommended-ex1","language":"yaml","code":"spec:\n      licenseSecretName: rec-license","section_id":"use-a-kubernetes-secret-recommended"},{"id":"embed-license-directly-in-rec-specification-ex0","language":"yaml","code":"spec:\n  nodes: 3\n  license: |\n    ----- LICENSE START -----\n    eai14c/y6XNVykffDQSPUsHKcmpgOFUlmyTBDUEZEz+GLbXAgQFOmxcdbR9J\n    ...remaining license key content...\n    ----- LICENSE END -----","section_id":"embed-license-directly-in-rec-specification"},{"id":"client-certificates-for-mtls-ex0","language":"sh","code":"kubectl -n <namespace> create secret generic client-cert-secret --from-file=cert=<path-to-cert>","section_id":"client-certificates-for-mtls"},{"id":"service-certificates-ex0","language":"sh","code":"kubectl create secret generic <secret-name> \\\n  --from-file=certificate=</PATH/TO/certificate.pem> \\\n  --from-file=key=</PATH/TO/key.pem> \\\n  --from-literal=name=<proxy | api | cm | syncer | metrics_exporter>","section_id":"service-certificates"},{"id":"internode-encryption-certificates-ex0","language":"sh","code":"kubectl create secret generic cp-internode-cert \\\n  --from-file=certificate=</path/to/cp-certificate.pem> \\\n  --from-file=key=</path/to/cp-key.pem> \\\n  --from-literal=name=cp_internode_encryption","section_id":"internode-encryption-certificates"},{"id":"internode-encryption-certificates-ex1","language":"sh","code":"kubectl create secret generic dp-internode-cert \\\n  --from-file=certificate=</path/to/dp-certificate.pem> \\\n  --from-file=key=</path/to/dp-key.pem> \\\n  --from-literal=name=dp_internode_encryption","section_id":"internode-encryption-certificates"}]}
{"schema_version":2,"id":"operate/kubernetes/security/authentication","title":"Authentication","url":"https://redis.io/docs/latest/operate/kubernetes/security/authentication/","summary":"Manage Redis Software cluster credentials, LDAP, SSO, and configuration secrets on Kubernetes.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","children":[{"id":"operate/kubernetes/security/authentication/manage-rec-credentials","summary":"","title":"Manage Redis Enterprise cluster (REC) credentials","url":"https://redis.io/docs/latest/operate/kubernetes/security/authentication/manage-rec-credentials/"},{"id":"operate/kubernetes/security/authentication/manage-redb-credentials","summary":"Retrieve and rotate Redis Enterprise database (REDB) passwords on Kubernetes.","title":"Manage Redis Enterprise database (REDB) passwords","url":"https://redis.io/docs/latest/operate/kubernetes/security/authentication/manage-redb-credentials/"},{"id":"operate/kubernetes/security/authentication/configuration-secrets","summary":"Store Redis Enterprise configuration items in Kubernetes Secrets for automatic updates and secure management.","title":"Store configuration in Kubernetes Secrets","url":"https://redis.io/docs/latest/operate/kubernetes/security/authentication/configuration-secrets/"},{"id":"operate/kubernetes/security/authentication/ldap","summary":"Enable LDAP authentication for Redis Enterprise for Kubernetes.","title":"Enable LDAP authentication","url":"https://redis.io/docs/latest/operate/kubernetes/security/authentication/ldap/"},{"id":"operate/kubernetes/security/authentication/sso","summary":"Enable SAML-based SSO authentication for Redis Enterprise for Kubernetes.","title":"Enable SSO authentication","url":"https://redis.io/docs/latest/operate/kubernetes/security/authentication/sso/"}],"page_type":"content","content_hash":"2f5a7dee180a0b5e07c31f9d356d7153f24f56252e366ffbfd962efd72619b12","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Authentication covers cluster credentials, external identity providers (LDAP and SAML SSO), and configuration secrets. The operator generates the initial cluster admin credentials, applies LDAP and SSO settings from the `RedisEnterpriseCluster` spec, and reads configuration values from Kubernetes Secrets you can update without a cluster restart."},{"id":"how-authentication-works-on-redis-for-kubernetes","title":"How authentication works on Redis for Kubernetes","role":"content","text":"- **Cluster credentials** are auto-generated at install and stored in a Kubernetes Secret named after the REC resource. Retrieve and update them with `kubectl`.\n- **LDAP** is configured on the `RedisEnterpriseCluster` spec. The operator applies the configuration through the Redis Software REST API.\n- **SAML SSO** is enabled on the REC spec. The operator configures the identity provider connection in Redis Software.\n- **Configuration secrets** let you store sensitive configuration items in Kubernetes Secrets that the operator references. Updates to the Secret reconcile automatically."},{"id":"whats-the-same-as-redis-software","title":"What's the same as Redis Software","role":"content","text":"The underlying Redis Software behavior is unchanged. For concepts and reference details, see the existing Redis Software docs:\n\n- [LDAP authentication overview](https://redis.io/docs/latest/operate/rs/security/access-control/ldap) — server requirements, supported attributes, and the LDAP model.\n- [Enable role-based LDAP](https://redis.io/docs/latest/operate/rs/security/access-control/ldap/enable-role-based-ldap) — concepts behind role-based LDAP.\n- [Map LDAP groups to roles](https://redis.io/docs/latest/operate/rs/security/access-control/ldap/map-ldap-groups-to-roles) — group-to-role mapping rules.\n- [SAML single sign-on](https://redis.io/docs/latest/operate/rs/security/access-control/saml-sso) — identity provider requirements and SAML attribute mappings.\n- [Default user](https://redis.io/docs/latest/operate/rs/security/access-control/manage-users/default-user) — what the bootstrap admin account is for."},{"id":"whats-different-on-kubernetes","title":"What's different on Kubernetes","role":"content","text":"- **Initial credentials are auto-generated by default.** If you don't provide them, the operator generates them and stores them in the credentials Secret, which you retrieve after the REC is up. You can instead supply your own Secret name and credentials at cluster creation time.\n- **Change the cluster admin credentials in Redis Software, then mirror them to the Secret.** Update the credentials directly in Redis Software and reflect the new values in the credentials Secret — see [Manage REC credentials](https://redis.io/docs/latest/operate/kubernetes/security/authentication/manage-rec-credentials). Credentials for other users are managed through their `RedisEnterpriseUser` resources and Secrets.\n- **LDAP and SSO configuration is part of the REC spec.** The operator applies it through the Redis Software REST API, so the configuration is source-controlled.\n- **Sensitive values live in Kubernetes Secrets** (or HashiCorp Vault) instead of in Redis Software configuration files."},{"id":"in-this-section","title":"In this section","role":"content","text":"- [Manage REC credentials](https://redis.io/docs/latest/operate/kubernetes/security/authentication/manage-rec-credentials) — retrieve and update the cluster admin credentials Secret.\n- [Manage REDB passwords](https://redis.io/docs/latest/operate/kubernetes/security/authentication/manage-redb-credentials) — retrieve and rotate database passwords through the database Secret.\n- [Configuration secrets](https://redis.io/docs/latest/operate/kubernetes/security/authentication/configuration-secrets) — store config items in Kubernetes Secrets and reconcile updates automatically.\n- [LDAP authentication](https://redis.io/docs/latest/operate/kubernetes/security/authentication/ldap) — configure LDAP for Cluster Manager and database access.\n- [SSO authentication](https://redis.io/docs/latest/operate/kubernetes/security/authentication/sso) — configure SAML single sign-on for the Cluster Manager UI."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/security/authentication/ldap","title":"Enable LDAP authentication","url":"https://redis.io/docs/latest/operate/kubernetes/security/authentication/ldap/","summary":"Enable LDAP authentication for Redis Enterprise for Kubernetes.","aliases":["/operate/kubernetes/security/ldap/"],"tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","page_type":"content","content_hash":"e3bed433ad169fec28fcf42bac1d1eddc85331006a8787659fa58663f416ee0a","sections":[{"id":"ldap-support-for-redis-enterprise-software","title":"LDAP support for Redis Enterprise Software","role":"overview","text":"Redis Enterprise Software supports LDAP authentication and authorization through [role-based access controls](https://redis.io/docs/latest/operate/rs/security/access-control) (RBAC). You can map LDAP groups to [Redis Enterprise roles](https://redis.io/docs/latest/operate/rs/security/access-control) to control access to your database and the Cluster Manager UI. For more details on how LDAP works with Redis Enterprise, see [LDAP authentication](https://redis.io/docs/latest/operate/rs/security/access-control/ldap).\n\nRedis Enterprise for Kubernetes supports enabling and configuring LDAP authentication using the `RedisEnterpriseCluster` (REC) custom resource. Currently, the Redis Enterprise cluster (REC) only supports configuration related to the LDAP server, such as server addresses, connection details, credentials, and query configuration.\n\nTo [map LDAP groups to Redis Enterprise access control roles](https://redis.io/docs/latest/operate/rs/security/access-control/ldap/enable-role-based-ldap), you'll need to use the Redis Enterprise [API](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/ldap_mappings) or [admin console](https://redis.io/docs/latest/operate/rs/security/access-control/ldap/enable-role-based-ldap)."},{"id":"enable-ldap","title":"Enable LDAP","role":"content","text":"To enable LDAP for your REC, use the `.spec.ldap` field in the `RedisEnterpriseCluster` custom resource.\n\nThe following `RedisEnterpriseCluster` example resource enables a basic LDAP configuration:\n\n[code example]\n\nRefer to the `RedisEnterpriseCluster` [API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api#specldap) for full details on the available fields."},{"id":"bind-credentials","title":"Bind credentials","role":"content","text":"For LDAP servers that require authentication for client queries, store the bind credentials in a secret and reference them in the `RedisEnterpriseCluster` custom resource.\n\n1. Create a secret to store the bind credentials.\n    \n    [code example]\n    The secret must:\n    - Reside within the same namespace as the `RedisEnterpriseCluster` custom resource.\n    - Include a `dn` key with the distinguished name for the user performing the query (such as `cn=admin,dc=example,dc=org`).\n    - Include a `password` key with the bind password.\n\n    Replace the `<placeholders>` in the command above with your own values.\n\n1. Reference the secret name in the `.spec.ldap.bindCredentialsSecretName` field of the `RedisEnterpriseCluster` custom resource.\n\n    [code example]"},{"id":"ldaps-or-starttls-protocols","title":"LDAPS or STARTTLS protocols","role":"content","text":"In addition to plain LDAP protocol, Redis Enterprise Software also supports LDAPS and STARTTLS protocols for secure communication with the LDAP server.\n\nTo enable one of these protocols, edit the `spec.ldap.protocol` field in the `RedisEnterpriseCluster` custom resource:\n\n#### Enable `LDAPS`\n\n  [code example]\n\n  Default port: 636\n\n#### Enable `STARTTLS`\n\n  [code example]\n\n  Default port: 389"},{"id":"ca-certificate","title":"CA certificate","role":"content","text":"To use a custom CA certificate for validating the LDAP server certificate, store the CA certificate in a secret and reference the secret in the `RedisEnterpriseCluster` custom resource.\n\n1. Create a secret to hold the CA certificate.\n\n    [code example]\n\n    The secret must:\n    - Reside within the same namespace as the `RedisEnterpriseCluster` custom resource.\n    - Include a `cert` key with a PEM-encoded CA certificate (such as `cacert.pem`).\n\n    Replace the `<placeholders>` in the command above with your own values.\n\n1. Reference the secret name in the `spec.ldap.caCertificateSecretName` field of the `RedisEnterpriseCluster` custom resource.\n\n    [code example]"},{"id":"client-certificates","title":"Client certificates","role":"content","text":"To use an LDAP client certificate, store the certificate in a secret and reference the secret in the `RedisEnterpriseCluster` custom resource.\n\n1. Create a secret to hold the client certificate.\n\n    [code example]\n\n    The secret must:\n    - Reside within the same namespace as the `RedisEnterpriseCluster` custom resource.\n    - Include a `name` key explicitly set to `ldap_client`.\n    - Include a `certificate` key for the public key (such as `cert.pem`).\n    - Include a `key` key for the private key (such as `key.pem`).\n    \n\n    Replace the `<placeholders>` in the command above with your own values.\n\n1. Reference the secret name in the `.spec.certificates.ldapClientCertificateSecretName` field of the `RedisEnterpriseCluster` custom resource, substituting your own values for `<placeholders>`.\n\n    [code example]"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"Redis Enterprise Software can't resolve DNS names with a `.local` suffix.\n  If your LDAP server is in the same Kubernetes cluster and exposed via a Service object, *avoid* addresses such as `openldap.openldap.svc.cluster.local`. Instead, *use short-form addresses* such as `openldap.openldap.svc`."},{"id":"next-steps","title":"Next steps","role":"content","text":"To [map LDAP groups to Redis Enterprise access control roles](https://redis.io/docs/latest/operate/rs/security/access-control/ldap/enable-role-based-ldap), you'll need to use the Redis Enterprise [API](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/ldap_mappings) or [admin console](https://redis.io/docs/latest/operate/rs/security/access-control/ldap/enable-role-based-ldap).\n\nFor more details on how LDAP works with Redis Enterprise, see [LDAP authentication](https://redis.io/docs/latest/operate/rs/security/access-control/ldap)."}],"examples":[{"id":"enable-ldap-ex0","language":"yaml","code":"apiVersion: app.redislabs.com/v1\nkind: RedisEnterpriseCluster\nmetadata:\n  name: rec\nspec:\n  nodes: 3\n  ldap:\n    protocol: LDAP\n    servers:\n    - host: openldap.openldap.svc\n      port: 389\n    bindCredentialsSecretName: ldap-bind-credentials\n    cacheTTLSeconds: 600\n    enabledForControlPlane: true\n    enabledForDataPlane: true\n    authenticationQuery:\n      template: cn=%u,ou=default,dc=example,dc=org\n    authorizationQuery:\n      attribute: memberOf","section_id":"enable-ldap"},{"id":"bind-credentials-ex0","language":"sh","code":"kubectl -n <rec-namespace> create secret generic <bind-secret-name> \\\n        --from-literal=dn='<disinguished-name>' \\\n        --from-literal=password=<password>","section_id":"bind-credentials"},{"id":"bind-credentials-ex1","language":"yaml","code":"spec:\n      ldap:\n        bindCredentialsSecretName: <bind-secret-name>","section_id":"bind-credentials"},{"id":"ldaps-or-starttls-protocols-ex0","language":"yaml","code":"spec:\n        ldap:\n          protocol: LDAPS","section_id":"ldaps-or-starttls-protocols"},{"id":"ldaps-or-starttls-protocols-ex1","language":"yaml","code":"spec:\n        ldap:\n          protocol: STARTTLS","section_id":"ldaps-or-starttls-protocols"},{"id":"ca-certificate-ex0","language":"sh","code":"kubectl -n <rec-namespace> create secret generic <ca-secret-name> \\\n        --from-file=cert=<ca-cert>.pem","section_id":"ca-certificate"},{"id":"ca-certificate-ex1","language":"yaml","code":"spec:\n      ldap:\n        caCertificateSecretName: <ca-secret-name>","section_id":"ca-certificate"},{"id":"client-certificates-ex0","language":"sh","code":"kubectl -n <rec-namespace> create secret generic <client-secret-name> \\\n      --from-literal=name=ldap_client \\\n      --from-file=certificate=<client-cert-file> \\\n      --from-file=key=<private-key-file>","section_id":"client-certificates"},{"id":"client-certificates-ex1","language":"yaml","code":"spec:\n      certificates:\n        ldapClientCertificateSecretName: <client-secret-name>","section_id":"client-certificates"}]}
{"schema_version":2,"id":"operate/kubernetes/security/authentication/manage-rec-credentials","title":"Manage Redis Enterprise cluster (REC) credentials","url":"https://redis.io/docs/latest/operate/kubernetes/security/authentication/manage-rec-credentials/","summary":"","aliases":["/operate/kubernetes/security/manage-rec-credentials/"],"tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","page_type":"content","content_hash":"04a94f76a41083bd3f4f20aa8b948cf995fff8af4498bc8d1710eeed4e1d14fe","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise for Kubernetes uses a custom resource called [`RedisEnterpriseCluster`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api) to create a Redis Enterprise cluster (REC). During creation, it generates random credentials for the operator to use. The credentials are saved in a Kubernetes (K8s) [secret](https://kubernetes.io/docs/concepts/configuration/secret/). The secret name defaults to the cluster name and is specified by the `clusterCredentialSecretName` field in the REC specification."},{"id":"retrieve-the-current-username-and-password","title":"Retrieve the current username and password","role":"content","text":"The credentials can be used to access the Redis Enterprise admin console or the API. Connectivity must be configured to the REC [pods](https://kubernetes.io/docs/concepts/workloads/pods/) using an appropriate service (or port forwarding).\n\n1. Inspect the random username and password created by the operator during creation with the `kubectl get secret` command.\n\n    [code example]\n\n    The command outputs the encoded password and username, similar to the example below.\n\n      [code example]\n\n1. Decode each value with the `echo` and `base64` commands, using the encoded strings from the previous step:\n\n    [code example]\n\n    In this example, the plain text password is `12345678` and the username is `demo@example.com`."},{"id":"change-the-redis-enterprise-cluster-rec-credentials","title":"Change the Redis Enterprise cluster (REC) credentials","role":"content","text":""},{"id":"change-the-rec-password-for-the-current-username","title":"Change the REC password for the current username","role":"content","text":"1. Access a [pod](https://kubernetes.io/docs/concepts/workloads/pods/) running a Redis Enterprise cluster:\n\n    [code example]\n\n1. Add a new password for the existing user:\n\n    [code example]\n\n1. From outside the pod, update the REC credential secret:\n\n    [code example]\n\n1. Wait five minutes for all the components to read the new password from the updated secret. If you proceed to the next step too soon, the account could get locked.\n\n1. Access a pod running a Redis Enterprise cluster again:\n\n    [code example]\n\n1. Remove the previous password to ensure only the new one applies:\n\n    [code example]\n\n> [!NOTE]\n> The username for the K8s secret is the email displayed on the Redis Enterprise admin console."},{"id":"change-both-the-rec-username-and-password","title":"Change both the REC username and password","role":"content","text":"1. [Connect to the admin console](https://redis.io/docs/latest/operate/kubernetes/re-clusters/connect-to-admin-console).\n\n1. [Add another admin user](https://redis.io/docs/latest/operate/rs/security/access-control/create-users) and choose a new password.\n\n1. Specify the new username in the `username` field of your REC custom resource spec.\n\n1. Update the REC credential secret:\n\n    [code example]\n\n1. Wait five minutes for all the components to read the new password from the updated secret. If you proceed to the next step too soon, the account could get locked.\n\n1. Delete the previous admin user from the cluster.\n\n> [!NOTE]\n> The operator may log errors in the time between updating the username in the REC spec and the secret update."},{"id":"update-the-credentials-secret-in-vault","title":"Update the credentials secret in Vault","role":"content","text":"If you store your secrets with HashiCorp Vault, update the secret for the REC credentials with the following key-value pairs:\n\n[code example]\n\nFor more information about Vault integration with the Redis Enterprise Cluster see [Integrating Redis Enterprise for Kubernetes with HashiCorp Vault](https://github.com/RedisLabs/redis-enterprise-k8s-docs/blob/master/vault/README.md)."}],"examples":[{"id":"retrieve-the-current-username-and-password-ex0","language":"sh","code":"kubectl get secret rec -o jsonpath='{.data}'","section_id":"retrieve-the-current-username-and-password"},{"id":"retrieve-the-current-username-and-password-ex1","language":"sh","code":"map[password:MTIzNDU2NzgK username:ZGVtb0BleGFtcGxlLmNvbQo=]","section_id":"retrieve-the-current-username-and-password"},{"id":"retrieve-the-current-username-and-password-ex2","language":"bash","code":"echo MTIzNDU2NzgK | base64 --decode\n    echo ZGVtb0BleGFtcGxlLmNvbQo= | base64 --decode","section_id":"retrieve-the-current-username-and-password"},{"id":"change-the-rec-password-for-the-current-username-ex0","language":"sh","code":"kubectl exec -it <rec-resource-name>-0 -c redis-enterprise-node -- /bin/bash","section_id":"change-the-rec-password-for-the-current-username"},{"id":"change-the-rec-password-for-the-current-username-ex1","language":"bash","code":"REC_USER=\"`cat /opt/redislabs/credentials/username`\" \\\n    REC_PASSWORD=\"`cat /opt/redislabs/credentials/password`\" \\\n    curl -k --request POST \\\n      --url https://localhost:9443/v1/users/password \\\n      -u \"$REC_USER:$REC_PASSWORD\" \\\n      --header 'Content-Type: application/json' \\\n      --data \"{\\\"username\\\":\\\"$REC_USER\\\", \\\n      \\\"new_password\\\":\\\"<NEW PASSWORD>\\\"}\"","section_id":"change-the-rec-password-for-the-current-username"},{"id":"change-the-rec-password-for-the-current-username-ex2","language":"sh","code":"kubectl create secret generic <cluster_secret_name> \\\n      --save-config \\\n      --dry-run=client \\\n      --from-literal=username=<current-username> \\\n      --from-literal=password=<new-password> \\\n      -o yaml | \\\n    kubectl apply -f -","section_id":"change-the-rec-password-for-the-current-username"},{"id":"change-the-rec-password-for-the-current-username-ex3","language":"sh","code":"kubectl exec -it <rec-resource-name>-0 -c redis-enterprise-node -- /bin/bash","section_id":"change-the-rec-password-for-the-current-username"},{"id":"change-the-rec-password-for-the-current-username-ex4","language":"sh","code":"REC_USER=\"`cat /opt/redislabs/credentials/username`\"; \\\n    REC_PASSWORD=\"`cat /opt/redislabs/credentials/password`\"; \\\n    curl -k --request DELETE \\\n      --url https://localhost:9443/v1/users/password \\\n      -u \"$REC_USER:$REC_PASSWORD\" \\\n      --header 'Content-Type: application/json' \\\n      --data \"{\\\"username\\\":\\\"$REC_USER\\\", \\\n      \\\"old_password\\\":\\\"<OLD PASSWORD>\\\"}\"","section_id":"change-the-rec-password-for-the-current-username"},{"id":"change-both-the-rec-username-and-password-ex0","language":"sh","code":"kubectl create secret generic <cluster_secret_name> \\\n      --save-config \\\n      --dry-run=client \\\n      --from-literal=username=<new-username> \\\n      --from-literal=password=<new-password> \\\n      -o yaml | \\\n    kubectl apply -f -","section_id":"change-both-the-rec-username-and-password"},{"id":"update-the-credentials-secret-in-vault-ex0","language":"sh","code":"username:<desired_username>, password:<desired_password>","section_id":"update-the-credentials-secret-in-vault"}]}
{"schema_version":2,"id":"operate/kubernetes/security/authentication/manage-redb-credentials","title":"Manage Redis Enterprise database (REDB) passwords","url":"https://redis.io/docs/latest/operate/kubernetes/security/authentication/manage-redb-credentials/","summary":"Retrieve and rotate Redis Enterprise database (REDB) passwords on Kubernetes.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","page_type":"content","content_hash":"a838617bc5d8ebbdcf3c28045a7df30d1f1e417577f4f3bf47cb3e9456d78ae9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Each [`RedisEnterpriseDatabase`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api) resource has a password stored under the `password` key of the secret named by `spec.databaseSecretName`. If you don't set `databaseSecretName`, the operator creates a secret named `redb-<database-name>` with a random password and updates the REDB spec to reference it.\n\nThe operator reads the `password` key on every reconciliation and applies it to the database, so you rotate the password by updating the secret."},{"id":"retrieve-the-redb-password","title":"Retrieve the REDB password","role":"content","text":"1. Find the secret name for the database:\n\n    [code example]\n\n2. Decode the password:\n\n    [code example]"},{"id":"change-the-redb-password","title":"Change the REDB password","role":"content","text":"> [!NOTE]\n> If the REDB spec sets `defaultUser: false`, the operator does not create or update the database secret. Rotating the secret has no effect in that mode — manage credentials through [access control](https://redis.io/docs/latest/operate/kubernetes/security/access-control) instead, using [`RedisEnterpriseUser`](https://redis.io/docs/latest/operate/kubernetes/security/access-control/manage-users) resources.\n\n1. Base64-encode the new password. Use `echo -n` to avoid encoding a trailing newline:\n\n    [code example]\n\n2. Patch the secret with the encoded value:\n\n    [code example]\n\n    To edit the secret interactively, use `kubectl edit secret <secret-name>` and replace the `password` value.\n\n3. Verify that the operator applied the change. The REDB status moves to `active-change-pending` while the update is in flight and returns to `active` when complete:\n\n    [code example]\n\n    Then test the new password with a Redis client:\n\n    [code example]\n\nTo disable authentication for the default user, set the `password` value to an empty string."},{"id":"impact-on-existing-client-connections","title":"Impact on existing client connections","role":"content","text":"Existing client connections authenticated with the old password remain open — Redis Enterprise does not drop sessions when the password changes. New connections, and any `AUTH` commands issued on existing connections, must use the new password. Coordinate the secret update with your client configuration to avoid authentication errors.\n\n> [!NOTE]\n> For Active-Active databases, the database secret is not created automatically. See [Create a global database secret](https://redis.io/docs/latest/operate/kubernetes/active-active/global-db-secret)."}],"examples":[{"id":"retrieve-the-redb-password-ex0","language":"sh","code":"kubectl get redb <database-name> -o jsonpath=\"{.spec.databaseSecretName}\"","section_id":"retrieve-the-redb-password"},{"id":"retrieve-the-redb-password-ex1","language":"sh","code":"kubectl get secret <secret-name> -o jsonpath=\"{.data.password}\" | base64 --decode","section_id":"retrieve-the-redb-password"},{"id":"change-the-redb-password-ex0","language":"sh","code":"echo -n '<new-password>' | base64","section_id":"change-the-redb-password"},{"id":"change-the-redb-password-ex1","language":"sh","code":"kubectl patch secret <secret-name> -p='{\"data\":{\"password\":\"<base64-encoded-password>\"}}'","section_id":"change-the-redb-password"},{"id":"change-the-redb-password-ex2","language":"sh","code":"kubectl get redb <database-name> -o jsonpath='{.status.status}'","section_id":"change-the-redb-password"},{"id":"change-the-redb-password-ex3","language":"sh","code":"redis-cli -h <service-name> -p <port> -a '<new-password>' PING","section_id":"change-the-redb-password"}]}
{"schema_version":2,"id":"operate/kubernetes/security/authentication/sso","title":"Enable SSO authentication","url":"https://redis.io/docs/latest/operate/kubernetes/security/authentication/sso/","summary":"Enable SAML-based SSO authentication for Redis Enterprise for Kubernetes.","aliases":["/operate/kubernetes/security/sso/"],"tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","page_type":"content","content_hash":"bed1d60fe911b8205f18fe14385b26c8829917714a16c6482251e8eb503da120","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise Software supports SAML 2.0 single sign-on (SSO) for the Cluster Manager UI with both IdP-initiated and SP-initiated authentication. User accounts are automatically created on first sign-in using just-in-time (JIT) provisioning."},{"id":"idp-requirements","title":"IdP requirements","role":"content","text":"Your identity provider must support:\n\n- SAML 2.0 protocol\n- Signed SAML responses\n- HTTP-Redirect binding for SP-initiated SSO\n- HTTP-POST binding for SAML assertions"},{"id":"enable-sso","title":"Enable SSO","role":"content","text":"To enable SSO for your Redis Enterprise cluster (REC), follow these steps to configure SAML authentication."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before enabling SSO, ensure you have:\n\n\n1. An existing Redis Enterprise cluster (REC) deployed in Kubernetes\n\n2. **External access to the Cluster Manager UI** - The Cluster Manager UI must be accessible externally via a LoadBalancer service or Ingress so users can access it from their browser and the identity provider can redirect back after authentication. See [Connect to the admin console](https://redis.io/docs/latest/operate/kubernetes/re-clusters/connect-to-admin-console) for configuration options.\n\n3. A SAML 2.0-compatible identity provider (such as Okta, Azure AD, or similar)\n\n4. Admin access to your identity provider\n\n5. A TLS certificate and private key for the service provider (SP)\n\n> [!WARNING]\n> SSO requires external access to the Cluster Manager UI. Port forwarding is not sufficient for SSO authentication because the identity provider needs to redirect users back to the UI after authentication. You must configure either a LoadBalancer service (via `spec.uiServiceType: LoadBalancer`) or an Ingress controller."},{"id":"step-1-upload-service-provider-certificate-and-private-key","title":"Step 1: Upload Service Provider certificate and private key","role":"content","text":"The Service Provider certificate is used by the cluster to sign SAML requests and encrypt SAML responses.\n\n1. Create a secret with your service provider certificate and private key:\n\n    [code example]\n\n    The secret must:\n    - Reside within the same namespace as the `RedisEnterpriseCluster` custom resource.\n    - Include a `name` key explicitly set to `sso_service`.\n    - Include a `certificate` key with the public certificate in PEM format.\n    - Include a `key` key with the private key in PEM format.\n\n    Replace the `<placeholders>` in the command above with your own values.\n\n2. Configure the service provider certificate in the `RedisEnterpriseCluster` custom resource:\n\n    [code example]\n\n3. Apply the configuration:\n\n    [code example]\n\n#### Configure Service Provider base address (optional)\n\nThe base address is used to construct service provider URLs, such as the Assertion Consumer Service (ACS) URL and Single Logout (SLO) URL.\n\nIf not specified, the base address is automatically determined from the REC Cluster Manager UI service:\n- If the UI service type is `LoadBalancer` (configured via `spec.uiServiceType`), the load balancer address is used.\n- Otherwise, the cluster-internal DNS name is used (for example, `rec-ui.svc.cluster.local`).\n- The port defaults to 8443 if not specified.\n\nTo explicitly set the base address, add it to the `serviceProvider` section:\n\n[code example]\n\nFormat: `[<scheme>://]<hostname>[:<port>]`\n\nExamples:\n- `\"https://redis-ui.example.com:443\"` (recommended - explicit scheme)\n- `\"redis-ui.example.com:443\"` (defaults to https://)\n- `\"http://redis-ui.example.com:9443\"` (NOT recommended for production)\n\n> [!WARNING]\n> Using `http://` is NOT recommended for production environments as it transmits sensitive SAML assertions in plaintext. Only use `http://` for testing or development purposes.\n\n**Usage guidelines:**\n- **For LoadBalancer services:** Leave this field blank to use the default REC UI service, or set it explicitly to the LoadBalancer address for custom services.\n- **For Ingress:** Set this to the ingress hostname and port (typically 443), for example, `\"https://redis-ui.example.com:443\"`."},{"id":"step-2-download-service-provider-metadata","title":"Step 2: Download Service Provider metadata","role":"content","text":"After applying the configuration, retrieve the service provider metadata to use when configuring your identity provider.\n\n#### Option A: Retrieve from Kubernetes secret\n\nIf you configured `spMetadataSecretName` in Step 1, the operator creates a secret with the SP metadata:\n\n[code example]\n\n> [!NOTE]\n> This secret is only created when the cluster is configured to use Kubernetes secrets (`spec.clusterCredentialSecretType` is unset or set to `\"kubernetes\"`). When using Vault secrets, use Option B instead.\n\n#### Option B: Retrieve from the API\n\nYou can obtain the SP metadata directly from the Redis Enterprise Server API:\n\n[code example]\n\nReplace `<rec-pod-name>`, `<username>`, and `<password>` with your cluster details."},{"id":"step-3-set-up-saml-app-in-your-identity-provider","title":"Step 3: Set up SAML app in your identity provider","role":"content","text":"Use the Service Provider metadata from Step 2 to configure a SAML application in your identity provider.\n\n1. Sign in to your identity provider's admin console (for example, Okta, Azure AD, Google Workspace).\n\n2. Create a new SAML 2.0 application or integration.\n\n3. Upload the `sp-metadata.xml` file or manually configure the SAML settings using values from the metadata:\n   - **Entity ID (SP)**: Found in the `entityID` attribute of the metadata\n   - **Assertion Consumer Service (ACS) URL**: Found in the `AssertionConsumerService` element's `Location` attribute\n   - **Single Logout (SLO) URL**: Found in the `SingleLogoutService` element's `Location` attribute (if present)\n\n4. Configure the SAML assertion to include the following attributes:\n   - `email` - User's email address (required)\n   - `firstName` - User's first name (optional)\n   - `lastName` - User's last name (optional)\n   - `redisRoleMapping` - Role mapping for JIT user provisioning (required for new users)\n\nRefer to your identity provider's documentation for specific configuration steps."},{"id":"step-4-download-identity-provider-metadata","title":"Step 4: Download identity provider metadata","role":"content","text":"After configuring the SAML app in your identity provider, download the identity provider metadata and certificate.\n\n1. In your identity provider's admin console, locate the SAML app you created in Step 3.\n\n2. Download the following:\n   - **IdP metadata XML**: Contains the IdP configuration (entity ID, SSO URL, SLO URL)\n   - **IdP certificate**: Public certificate used to verify SAML assertions\n\nSave these files for use in Step 5."},{"id":"step-5-configure-saml-identity-provider-in-redis-enterprise","title":"Step 5: Configure SAML identity provider in Redis Enterprise","role":"content","text":"Now configure the identity provider details in your Redis Enterprise cluster.\n\n1. Create a secret with the Identity Provider certificate:\n\n    [code example]\n\n    The secret must:\n    - Reside within the same namespace as the `RedisEnterpriseCluster` custom resource.\n    - Include a `name` key explicitly set to `sso_issuer`.\n    - Include a `certificate` key with the IdP public certificate in PEM format.\n    - **Not** include a `key` field (only the public certificate is needed).\n\n    Replace `<idp-cert-file>` with the path to your IdP certificate file.\n\n    > [!NOTE]\n    > While IdP metadata XML may contain the certificate, Redis Enterprise Server does not use it from there, so the certificate must be provided separately via this secret.\n\n2. Configure the IdP using one of the following options:\n\n#### Option A: Use IdP metadata XML (recommended)\n\nUsing IdP metadata XML is the recommended approach as it's less error-prone.\n\n1. Create a secret with the IdP metadata:\n\n    [code example]\n\n    The secret must:\n    - Reside within the same namespace as the `RedisEnterpriseCluster` custom resource.\n    - Include an `idp_metadata` key with the IdP metadata XML content.\n    - The XML can be plain text or base64-encoded; the operator handles encoding as needed.\n\n    Replace `<idp-metadata-file>` with the path to your IdP metadata XML file.\n\n2. Update the `RedisEnterpriseCluster` custom resource to add the IdP configuration:\n\n    [code example]\n\n3. Apply the updated configuration:\n\n    [code example]\n\n#### Option B: Manual issuer configuration\n\nIf IdP metadata XML is unavailable, you can manually configure the issuer settings.\n\n1. Update the `RedisEnterpriseCluster` custom resource with the IdP details:\n\n    [code example]\n\n    Replace the values with your identity provider's configuration:\n\n    - `entityID`: Identity Provider entity ID (issuer identifier)\n    - `loginURL`: Identity Provider SSO login URL where SAML authentication requests are sent\n    - `logoutURL`: Identity Provider single logout URL (optional)\n\n2. Apply the updated configuration:\n\n    [code example]\n\n> [!NOTE]\n> If both `idpMetadataSecretName` and `issuer` are provided, `idpMetadataSecretName` takes precedence and `issuer` is ignored."},{"id":"step-6-assign-saml-app-to-users","title":"Step 6: Assign SAML app to users","role":"content","text":"In your identity provider's admin console, assign users to the SAML application you created in Step 3.\n\n1. Navigate to the SAML app in your identity provider.\n\n2. Assign existing users or groups to the application.\n\n3. For new users who will use just-in-time (JIT) provisioning, ensure the `redisRoleMapping` attribute is configured with appropriate Redis Enterprise roles.\n\nRefer to your identity provider's documentation for specific steps on assigning users to applications."},{"id":"step-7-activate-sso","title":"Step 7: Activate SSO","role":"content","text":"Finally, activate SSO by enabling it in the `RedisEnterpriseCluster` custom resource.\n\n1. Update the `RedisEnterpriseCluster` custom resource to enable SSO:\n\n    [code example]\n\n2. Apply the configuration:\n\n    [code example]\n\n3. Test SSO by accessing the Cluster Manager UI and clicking **Sign in with SSO**."},{"id":"complete-example","title":"Complete example","role":"content","text":"Here's a complete example of a `RedisEnterpriseCluster` resource with SSO enabled:\n\n[code example]\n\nRefer to the `RedisEnterpriseCluster` [API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api#specsso) for full details on the available fields."},{"id":"next-steps","title":"Next steps","role":"content","text":"After enabling SSO:\n\n1. Configure users in your identity provider with matching email addresses\n2. Set up the `redisRoleMapping` attribute in your identity provider to assign appropriate roles for new users\n3. Test both IdP-initiated and SP-initiated SSO flows\n\nFor more information about Redis Enterprise Software security, see [Access control](https://redis.io/docs/latest/operate/rs/security/access-control)."}],"examples":[{"id":"step-1-upload-service-provider-certificate-and-private-key-ex0","language":"sh","code":"kubectl -n <rec-namespace> create secret generic sso-service-cert \\\n      --from-literal=name=sso_service \\\n      --from-file=certificate=<sp-cert-file> \\\n      --from-file=key=<sp-key-file>","section_id":"step-1-upload-service-provider-certificate-and-private-key"},{"id":"step-1-upload-service-provider-certificate-and-private-key-ex1","language":"yaml","code":"apiVersion: app.redislabs.com/v1\n    kind: RedisEnterpriseCluster\n    metadata:\n      name: rec\n    spec:\n      nodes: 3\n      certificates:\n        ssoServiceCertificateSecretName: sso-service-cert\n      sso:\n        enabled: false  # SSO not yet active - set to true in Step 7 after completing configuration\n        enforceSSO: false  # Optional: set to true to disable local authentication for non-admin users\n        saml:\n          spMetadataSecretName: sp-metadata  # Optional: store SP metadata in a secret\n          serviceProvider:\n            baseAddress: \"https://redis-ui.example.com:443\"  # Optional: customize base address","section_id":"step-1-upload-service-provider-certificate-and-private-key"},{"id":"step-1-upload-service-provider-certificate-and-private-key-ex2","language":"sh","code":"kubectl apply -f <rec-config-file>.yaml","section_id":"step-1-upload-service-provider-certificate-and-private-key"},{"id":"step-1-upload-service-provider-certificate-and-private-key-ex3","language":"yaml","code":"spec:\n  sso:\n    saml:\n      serviceProvider:\n        baseAddress: \"https://redis-ui.example.com:443\"","section_id":"step-1-upload-service-provider-certificate-and-private-key"},{"id":"step-2-download-service-provider-metadata-ex0","language":"sh","code":"kubectl -n <rec-namespace> get secret sp-metadata -o jsonpath='{.data.sp_metadata}' | base64 -d > sp-metadata.xml","section_id":"step-2-download-service-provider-metadata"},{"id":"step-2-download-service-provider-metadata-ex1","language":"sh","code":"kubectl -n <rec-namespace> exec -it <rec-pod-name> -c redis-enterprise-node -- \\\n  curl -k -u \"<username>:<password>\" \\\n  https://localhost:9443/v1/cluster/sso/saml/metadata/sp > sp-metadata.xml","section_id":"step-2-download-service-provider-metadata"},{"id":"step-5-configure-saml-identity-provider-in-redis-enterprise-ex0","language":"sh","code":"kubectl -n <rec-namespace> create secret generic sso-issuer-cert \\\n      --from-literal=name=sso_issuer \\\n      --from-file=certificate=<idp-cert-file>","section_id":"step-5-configure-saml-identity-provider-in-redis-enterprise"},{"id":"step-5-configure-saml-identity-provider-in-redis-enterprise-ex1","language":"sh","code":"kubectl -n <rec-namespace> create secret generic idp-metadata \\\n      --from-file=idp_metadata=<idp-metadata-file>.xml","section_id":"step-5-configure-saml-identity-provider-in-redis-enterprise"},{"id":"step-5-configure-saml-identity-provider-in-redis-enterprise-ex2","language":"yaml","code":"spec:\n      certificates:\n        ssoServiceCertificateSecretName: sso-service-cert\n        ssoIssuerCertificateSecretName: sso-issuer-cert\n      sso:\n        enabled: false  # SSO not yet active - set to true in Step 7 after completing configuration\n        enforceSSO: false  # Optional: set to true to disable local authentication for non-admin users\n        saml:\n          idpMetadataSecretName: idp-metadata\n          spMetadataSecretName: sp-metadata\n          serviceProvider:\n            baseAddress: \"https://redis-ui.example.com:443\"","section_id":"step-5-configure-saml-identity-provider-in-redis-enterprise"},{"id":"step-5-configure-saml-identity-provider-in-redis-enterprise-ex3","language":"sh","code":"kubectl apply -f <rec-config-file>.yaml","section_id":"step-5-configure-saml-identity-provider-in-redis-enterprise"},{"id":"step-5-configure-saml-identity-provider-in-redis-enterprise-ex4","language":"yaml","code":"spec:\n      certificates:\n        ssoServiceCertificateSecretName: sso-service-cert\n        ssoIssuerCertificateSecretName: sso-issuer-cert\n      sso:\n        enabled: false  # SSO not yet active - set to true in Step 7 after completing configuration\n        enforceSSO: false  # Optional: set to true to disable local authentication for non-admin users\n        saml:\n          issuer:\n            entityID: \"urn:sso:example:idp\"\n            loginURL: \"https://idp.example.com/sso/saml\"\n            logoutURL: \"https://idp.example.com/slo/saml\"  # optional\n          spMetadataSecretName: sp-metadata\n          serviceProvider:\n            baseAddress: \"https://redis-ui.example.com:443\"","section_id":"step-5-configure-saml-identity-provider-in-redis-enterprise"},{"id":"step-5-configure-saml-identity-provider-in-redis-enterprise-ex5","language":"sh","code":"kubectl apply -f <rec-config-file>.yaml","section_id":"step-5-configure-saml-identity-provider-in-redis-enterprise"},{"id":"step-7-activate-sso-ex0","language":"yaml","code":"spec:\n      certificates:\n        ssoServiceCertificateSecretName: sso-service-cert\n        ssoIssuerCertificateSecretName: sso-issuer-cert\n      sso:\n        enabled: true\n        enforceSSO: false  # Set to true to disable local authentication for non-admin users\n        saml:\n          idpMetadataSecretName: idp-metadata\n          spMetadataSecretName: sp-metadata\n          serviceProvider:\n            baseAddress: \"https://redis-ui.example.com:443\"","section_id":"step-7-activate-sso"},{"id":"step-7-activate-sso-ex1","language":"sh","code":"kubectl apply -f <rec-config-file>.yaml","section_id":"step-7-activate-sso"},{"id":"complete-example-ex0","language":"yaml","code":"apiVersion: app.redislabs.com/v1\nkind: RedisEnterpriseCluster\nmetadata:\n  name: rec\nspec:\n  nodes: 3\n  certificates:\n    ssoServiceCertificateSecretName: sso-service-cert\n    ssoIssuerCertificateSecretName: sso-issuer-cert\n  sso:\n    enabled: true\n    enforceSSO: false\n    saml:\n      idpMetadataSecretName: idp-metadata\n      spMetadataSecretName: sp-metadata\n      serviceProvider:\n        baseAddress: \"https://redis-ui.example.com:443\"","section_id":"complete-example"}]}
{"schema_version":2,"id":"operate/kubernetes/security/certificates/add-client-certificates","title":"Add client certificates","url":"https://redis.io/docs/latest/operate/kubernetes/security/certificates/add-client-certificates/","summary":"Add client certificates to your REDB custom resource.","aliases":["/operate/kubernetes/security/add-client-certificates/"],"tags":["docs","operate","kubernetes"],"last_updated":"2026-07-21T12:02:16-05:00","page_type":"content","content_hash":"f3c33e87a7ee6f5b871a6c92e4e2e8134ab421f5c798bae3ac9197f5611a49c5","sections":[{"id":"overview","title":"Overview","role":"overview","text":"For each client certificate you want to use with your database, you need to create a Kubernetes secret to hold it. You can then reference that secret in your Redis Enterprise database (REDB) custom resource spec."},{"id":"create-a-secret-to-hold-the-new-certificate","title":"Create a secret to hold the new certificate","role":"content","text":"1. [Create the secret config file](https://kubernetes.io/docs/tasks/configmap-secret/managing-secret-using-config-file/) with the required fields shown below.\n\n    [code example]\n  \n1. Apply the file to create the secret resource.\n\n    [code example]"},{"id":"edit-the-redb-resource","title":"Edit the REDB resource","role":"content","text":"1. Add the secret name to the REDB custom resource (`redb.yaml`) with the `clientAuthenticationCertificates` property in the `spec` section.\n\n  [code example]"}],"examples":[{"id":"create-a-secret-to-hold-the-new-certificate-ex0","language":"yaml","code":"apiVersion: v1\n    kind: Secret\n    type: Opaque\n    metadata:\n      name: <client-cert-secret>\n      namespace: <your-rec-namespace>\n    data:\n      cert: <client-certificate>","section_id":"create-a-secret-to-hold-the-new-certificate"},{"id":"create-a-secret-to-hold-the-new-certificate-ex1","language":"bash","code":"kubectl apply -f <client-cert-secret>.yaml","section_id":"create-a-secret-to-hold-the-new-certificate"},{"id":"edit-the-redb-resource-ex0","language":"yaml","code":"spec:\n      clientAuthenticationCertificates:\n      - <client-cert-secret>","section_id":"edit-the-redb-resource"}]}
{"schema_version":2,"id":"operate/kubernetes/security/certificates/cert-manager","title":"Integrate cert-manager with Redis for Kubernetes","url":"https://redis.io/docs/latest/operate/kubernetes/security/certificates/cert-manager/","summary":"Automate TLS certificate management for Redis for Kubernetes using cert-manager.","aliases":["/operate/kubernetes/security/cert-manager/"],"tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","page_type":"content","content_hash":"9921299ef0a8713e1317af56e29ef49b3d3309efe0f1139430e9cea7016d4bd1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[cert-manager](https://cert-manager.io/) is a Kubernetes add-on that automates the management and issuance of TLS certificates. The Redis operator integrates with cert-manager, so you can use automatically managed certificates for:\n\n- Redis Enterprise cluster (REC) components (API, CM, proxy, syncer, and others)\n- Database replication with TLS\n- LDAP client authentication\n- SSO/SAML certificates\n\nBenefits of using cert-manager include:\n\n- **Automatic certificate renewal**: cert-manager handles certificate rotation before expiration.\n- **Standardized management**: Use the same certificate management approach across your Kubernetes infrastructure.\n- **Multiple certificate authorities**: Support for Let's Encrypt, private CAs, Vault, and more.\n- **Automatic propagation**: For Active-Active databases, certificate changes automatically sync across all participating clusters.\n\n> [!WARNING]\n> The cert-manager integration uses Kubernetes secrets. It is not compatible with Vault-based secret management (when `clusterCredentialSecretType: vault`). See [HashiCorp Vault integration](https://redis.io/docs/latest/operate/kubernetes/security/vault) for details."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"- Kubernetes cluster with Redis Software for Kubernetes 8.0.18 or later\n- cert-manager v1.19.0 or later installed\n\nIf cert-manager is not already installed, see the [cert-manager installation documentation](https://cert-manager.io/docs/installation/)."},{"id":"how-it-works","title":"How it works","role":"content","text":"cert-manager creates standard Kubernetes TLS secrets with the following fields:\n\n- `tls.crt`: The certificate in PEM format\n- `tls.key`: The private key in PEM format\n- `ca.crt`: The root CA certificate\n\nThe Redis Enterprise operator automatically recognizes these secrets and can use them interchangeably with manually created secrets.\n\n> [!NOTE]\n> If you currently use opaque secrets for your certificates, you can switch to cert-manager's TLS secrets without any additional configuration changes to your Redis resources."},{"id":"supported-secret-formats","title":"Supported secret formats","role":"compatibility","text":"The operator supports multiple field names for backward compatibility:\n\n| Purpose | Accepted field names |\n|---------|---------------------|\n| Certificate | `tls.crt`, `cert`, `certificate` |\n| Private key | `tls.key`, `key` |\n| CA certificate | `ca.crt` |\n\n> [!NOTE]\n> Support for the `tls.crt` and `tls.key` field names requires Redis Software for Kubernetes 8.0.18 or later. For secret requirements on earlier versions, see [Manage REC certificates](https://redis.io/docs/latest/operate/kubernetes/security/certificates/manage-rec-certificates).\n\n> [!NOTE]\n> The `ca.crt` field is automatically appended to the certificate chain when present. cert-manager typically populates this field when it has access to the root certificate."},{"id":"quick-start","title":"Quick start","role":"content","text":"After you install cert-manager and configure an [`Issuer` or `ClusterIssuer`](https://cert-manager.io/docs/concepts/issuer/), create a `Certificate` resource to generate TLS secrets, then reference the secret name in your Redis custom resources."},{"id":"create-a-certificate-and-configure-your-rec","title":"Create a certificate and configure your REC","role":"content","text":"The following example creates a certificate and references the generated secret in a `RedisEnterpriseCluster` resource:\n\n[code example]\n\nThe operator automatically reads the certificate from the `redis-api-tls` secret created by cert-manager. No additional configuration is needed, even if you are migrating from manually created opaque secrets."},{"id":"secure-all-cluster-components","title":"Secure all cluster components","role":"content","text":"Request certificates for each component and reference them in the REC:\n\n[code example]\n\nEach secret name corresponds to a `Certificate` resource managed by cert-manager. For details on these fields, see the [RedisEnterpriseCluster API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api)."},{"id":"database-replication-with-tls","title":"Database replication with TLS","role":"content","text":"For database replication with client certificates:\n\n[code example]"},{"id":"ldap-client-authentication","title":"LDAP client authentication","role":"content","text":"When using LDAP with client certificate authentication:\n\n[code example]\n\nFor more details on LDAP configuration, see [Enable LDAP authentication](https://redis.io/docs/latest/operate/kubernetes/security/authentication/ldap)."},{"id":"active-active-databases-with-automatic-certificate-sync","title":"Active-Active databases with automatic certificate sync","role":"content","text":"For Active-Active databases, certificate updates to proxy or syncer certificates automatically trigger synchronization across all participating clusters:\n\n[code example]\n\nWhen cert-manager renews these certificates, the operator:\n\n1. Detects the secret change.\n1. Updates the certificate generation counter.\n1. Triggers a CRDB force update automatically.\n1. Synchronizes the new certificates to all participating clusters.\n\nNo manual intervention is required."},{"id":"use-production-certificate-authorities","title":"Use production certificate authorities","role":"content","text":""},{"id":"lets-encrypt-and-acme-issuers","title":"Let's Encrypt and ACME issuers","role":"content","text":"You can use Let's Encrypt and other ACME issuers, but not as an out-of-the-box integration. ACME issuers don't populate the `ca.crt` field of the generated TLS secret with the root certificate, so the secret that cert-manager creates doesn't include the full certificate chain.\n\nRedis Software needs the full chain, including the root CA, to trust the certificate. Whenever cert-manager doesn't populate `ca.crt` with the root certificate, supply the full chain yourself:\n\n1. Assemble the full certificate chain, including the root CA certificate.\n1. Create a Kubernetes secret that contains the full chain.\n1. Reference that secret in your Redis custom resource instead of the secret that cert-manager generates.\n\n> [!NOTE]\n> This applies to any issuer that doesn't populate `ca.crt` with the root certificate, not only Let's Encrypt.\n\nFor production environments where the issuer supplies the full chain automatically, such as a private CA or HashiCorp Vault, no extra steps are required."},{"id":"private-ca","title":"Private CA","role":"content","text":"For internal deployments with a private certificate authority:\n\n[code example]\n\nThe `ca-key-pair` secret must contain your CA's certificate and private key."},{"id":"migrate-from-manual-certificate-management","title":"Migrate from manual certificate management","role":"content","text":"If you currently use manually created secrets, you can migrate to cert-manager without downtime.\n\n1. Install cert-manager and create issuers (as shown in previous examples).\n\n1. Create `Certificate` resources with **different secret names** than your current secrets:\n\n    [code example]\n\n1. Update your REC to reference the new secrets:\n\n    [code example]\n\n    The operator updates the cluster with the new certificates. For Active-Active databases, the changes automatically sync to all clusters.\n\n1. After verifying the new certificates work correctly, delete the old manually created secrets:\n\n    [code example]"},{"id":"certificate-renewal-and-monitoring","title":"Certificate renewal and monitoring","role":"content","text":""},{"id":"automatic-renewal","title":"Automatic renewal","role":"content","text":"cert-manager automatically renews certificates before they expire based on the `renewBefore` setting in the `Certificate` spec:\n\n[code example]"},{"id":"monitor-certificate-status","title":"Monitor certificate status","role":"content","text":"Check certificate status:\n\n[code example]\n\nView detailed certificate information:\n\n[code example]"},{"id":"troubleshooting","title":"Troubleshooting","role":"errors","text":""},{"id":"certificate-not-issued","title":"Certificate not issued","role":"content","text":"Check the certificate status and cert-manager logs:\n\n[code example]\n\nCommon issues:\n\n- **Issuer not ready**: Verify your `Issuer` or `ClusterIssuer` is configured correctly.\n- **DNS validation failure**: For ACME issuers, ensure DNS records are correctly configured.\n- **Rate limits**: Let's Encrypt has rate limits. Use the staging environment for testing."},{"id":"operator-not-detecting-certificate-changes","title":"Operator not detecting certificate changes","role":"content","text":"Verify the secret exists and has the correct format:\n\n[code example]\n\nConfirm the secret contains `tls.crt` and `tls.key` fields. Check operator logs:\n\n[code example]"},{"id":"certificate-chain-issues","title":"Certificate chain issues","role":"content","text":"If you encounter certificate chain validation errors:\n\n1. Verify the `ca.crt` field is present in the secret (cert-manager populates this automatically when it has access to the root CA).\n1. If `ca.crt` is not present, ensure the certificate in `tls.crt` includes the full chain inline.\n1. Confirm the certificate chain is in the correct order: leaf certificate first, then intermediates, then root."},{"id":"best-practices","title":"Best practices","role":"content","text":"- **Use appropriate certificate lifetimes**: 90 days with a 15-day renewal window for production. Use shorter lifetimes in development to test renewal.\n- **Configure proper DNS names**: Include all necessary DNS names and SANs in your `Certificate` spec.\n- **Monitor certificate expiration**: Set up alerts for certificate expiration, even with automatic renewal.\n- **Use `ClusterIssuers` for shared issuers**: If multiple namespaces need certificates from the same CA, use `ClusterIssuers` instead of namespace-scoped `Issuers`.\n- **Back up CA private keys**: If using a private CA, ensure the CA private key secret is backed up."},{"id":"see-also","title":"See also","role":"related","text":"- [cert-manager documentation](https://cert-manager.io/docs/)\n- [Manage REC certificates](https://redis.io/docs/latest/operate/kubernetes/security/certificates/manage-rec-certificates)\n- [RedisEnterpriseCluster API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api)\n- [RedisEnterpriseDatabase API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api)\n- [HashiCorp Vault integration](https://redis.io/docs/latest/operate/kubernetes/security/vault)"}],"examples":[{"id":"create-a-certificate-and-configure-your-rec-ex0","language":"yaml","code":"apiVersion: cert-manager.io/v1\nkind: Certificate\nmetadata:\n  name: redis-api-cert\n  namespace: redis-namespace\nspec:\n  secretName: redis-api-tls\n  duration: 2160h # 90 days\n  renewBefore: 360h # 15 days before expiration\n  issuerRef:\n    name: my-issuer\n    kind: ClusterIssuer\n---\napiVersion: app.redislabs.com/v1\nkind: RedisEnterpriseCluster\nmetadata:\n  name: rec\n  namespace: redis-namespace\nspec:\n  nodes: 3\n  certificates:\n    apiCertificateSecretName: redis-api-tls","section_id":"create-a-certificate-and-configure-your-rec"},{"id":"secure-all-cluster-components-ex0","language":"yaml","code":"apiVersion: app.redislabs.com/v1\nkind: RedisEnterpriseCluster\nmetadata:\n  name: rec\nspec:\n  nodes: 3\n  certificates:\n    apiCertificateSecretName: api-tls\n    cmCertificateSecretName: cm-tls\n    proxyCertificateSecretName: proxy-tls\n    syncerCertificateSecretName: syncer-tls\n    metricsExporterCertificateSecretName: metrics-tls","section_id":"secure-all-cluster-components"},{"id":"database-replication-with-tls-ex0","language":"yaml","code":"apiVersion: cert-manager.io/v1\nkind: Certificate\nmetadata:\n  name: replication-client-cert\n  namespace: redis-namespace\nspec:\n  secretName: replication-client-tls\n  issuerRef:\n    name: my-ca-issuer\n    kind: ClusterIssuer\n  commonName: replication-client\n---\napiVersion: app.redislabs.com/v1alpha1\nkind: RedisEnterpriseDatabase\nmetadata:\n  name: target-db\nspec:\n  replicaSources:\n    - replicaSourceType: SECRET\n      replicaSourceName: source-uri-secret\n      clientKeySecret: replication-client-tls","section_id":"database-replication-with-tls"},{"id":"ldap-client-authentication-ex0","language":"yaml","code":"apiVersion: cert-manager.io/v1\nkind: Certificate\nmetadata:\n  name: ldap-client-cert\n  namespace: redis-namespace\nspec:\n  secretName: ldap-client-tls\n  issuerRef:\n    name: ldap-ca-issuer\n    kind: ClusterIssuer\n  commonName: redis-ldap-client\n---\napiVersion: app.redislabs.com/v1\nkind: RedisEnterpriseCluster\nmetadata:\n  name: rec\nspec:\n  nodes: 3\n  certificates:\n    ldapClientCertificateSecretName: ldap-client-tls\n  ldap:\n    protocol: LDAPS\n    servers:\n      - host: ldap.example.com\n        port: 636","section_id":"ldap-client-authentication"},{"id":"active-active-databases-with-automatic-certificate-sync-ex0","language":"yaml","code":"apiVersion: app.redislabs.com/v1\nkind: RedisEnterpriseCluster\nmetadata:\n  name: rec\nspec:\n  nodes: 3\n  certificates:\n    proxyCertificateSecretName: proxy-tls\n    syncerCertificateSecretName: syncer-tls","section_id":"active-active-databases-with-automatic-certificate-sync"},{"id":"private-ca-ex0","language":"yaml","code":"apiVersion: cert-manager.io/v1\nkind: ClusterIssuer\nmetadata:\n  name: private-ca-issuer\nspec:\n  ca:\n    secretName: ca-key-pair","section_id":"private-ca"},{"id":"migrate-from-manual-certificate-management-ex0","language":"yaml","code":"apiVersion: cert-manager.io/v1\n    kind: Certificate\n    metadata:\n      name: new-api-cert\n    spec:\n      secretName: api-tls-new\n      issuerRef:\n        name: my-issuer\n        kind: ClusterIssuer","section_id":"migrate-from-manual-certificate-management"},{"id":"migrate-from-manual-certificate-management-ex1","language":"yaml","code":"apiVersion: app.redislabs.com/v1\n    kind: RedisEnterpriseCluster\n    metadata:\n      name: rec\n    spec:\n      certificates:\n        apiCertificateSecretName: api-tls-new","section_id":"migrate-from-manual-certificate-management"},{"id":"migrate-from-manual-certificate-management-ex2","language":"bash","code":"kubectl delete secret old-api-tls -n redis-namespace","section_id":"migrate-from-manual-certificate-management"},{"id":"automatic-renewal-ex0","language":"yaml","code":"spec:\n  duration: 2160h    # 90 days\n  renewBefore: 360h  # Renew 15 days before expiration","section_id":"automatic-renewal"},{"id":"monitor-certificate-status-ex0","language":"bash","code":"kubectl get certificate -n redis-namespace","section_id":"monitor-certificate-status"},{"id":"monitor-certificate-status-ex1","language":"bash","code":"kubectl describe certificate redis-api-cert -n redis-namespace","section_id":"monitor-certificate-status"},{"id":"certificate-not-issued-ex0","language":"bash","code":"kubectl describe certificate <certificate-name> -n <namespace>\nkubectl logs -n cert-manager deployment/cert-manager","section_id":"certificate-not-issued"},{"id":"operator-not-detecting-certificate-changes-ex0","language":"bash","code":"kubectl get secret <secret-name> -n <namespace> -o yaml","section_id":"operator-not-detecting-certificate-changes"},{"id":"operator-not-detecting-certificate-changes-ex1","language":"bash","code":"kubectl logs -n <operator-namespace> deployment/redis-enterprise-operator","section_id":"operator-not-detecting-certificate-changes"}]}
{"schema_version":2,"id":"operate/kubernetes/security/certificates","title":"Certificates and encryption","url":"https://redis.io/docs/latest/operate/kubernetes/security/certificates/","summary":"Manage TLS certificates, client certificates, and internode encryption for Redis Software on Kubernetes.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","children":[{"id":"operate/kubernetes/security/certificates/manage-rec-certificates","summary":"Install your own certificates to replace the self-signed certificates used by a Redis Enterprise cluster on Kubernetes.","title":"Manage Redis Enterprise cluster (REC) certificates","url":"https://redis.io/docs/latest/operate/kubernetes/security/certificates/manage-rec-certificates/"},{"id":"operate/kubernetes/security/certificates/cert-manager","summary":"Automate TLS certificate management for Redis for Kubernetes using cert-manager.","title":"Integrate cert-manager with Redis for Kubernetes","url":"https://redis.io/docs/latest/operate/kubernetes/security/certificates/cert-manager/"},{"id":"operate/kubernetes/security/certificates/add-client-certificates","summary":"Add client certificates to your REDB custom resource.","title":"Add client certificates","url":"https://redis.io/docs/latest/operate/kubernetes/security/certificates/add-client-certificates/"},{"id":"operate/kubernetes/security/certificates/internode-encryption","summary":"Enable encryption for communication between REC nodes and configure custom certificates.","title":"Enable internode encryption","url":"https://redis.io/docs/latest/operate/kubernetes/security/certificates/internode-encryption/"}],"page_type":"content","content_hash":"6ef0ee4696ba0ed800b4afb372f52409ca895ada79adcf5d9a570816011c3caf","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Certificates and encryption use Kubernetes Secrets and cert-manager integration to provision, distribute, and rotate the TLS certificates that Redis Software relies on. The operator distributes referenced certificates across every cluster node."},{"id":"how-certificates-work-on-redis-for-kubernetes","title":"How certificates work on Redis for Kubernetes","role":"content","text":"- **Cluster certificates** live in Kubernetes Secrets that the `RedisEnterpriseCluster` spec references. The operator distributes them to every cluster node.\n- **cert-manager** can issue and rotate certificates automatically.\n- **Client certificates** live in a Secret that the database references for mutual TLS authentication.\n- **Internode encryption** is configured on the REC spec. The operator places the certificates on each node."},{"id":"whats-the-same-as-redis-software","title":"What's the same as Redis Software","role":"content","text":"The underlying certificate roles, requirements, and TLS behavior are unchanged. For concepts and reference details, see the existing Redis Software docs:\n\n- [Certificate roles and types](https://redis.io/docs/latest/operate/rs/security/certificates) — which certificate is used for what.\n- [Create certificates](https://redis.io/docs/latest/operate/rs/security/certificates/create-certificates) — certificate requirements (SAN, CN, validity).\n- [Update certificates](https://redis.io/docs/latest/operate/rs/security/certificates/updating-certificates) — rotation considerations on Redis Software.\n- [Monitor certificates](https://redis.io/docs/latest/operate/rs/security/certificates/monitor-certificates) — certificate expiration alerts.\n- [Client certificate authentication](https://redis.io/docs/latest/operate/rs/security/certificates/certificate-based-authentication) — how the cluster validates client certificates.\n- [TLS protocols](https://redis.io/docs/latest/operate/rs/security/encryption/tls/tls-protocols) and [ciphers](https://redis.io/docs/latest/operate/rs/security/encryption/tls/ciphers) — protocol and cipher selection.\n- [Enable TLS](https://redis.io/docs/latest/operate/rs/security/encryption/tls/enable-tls) — TLS for management, replication, and client connections.\n- [Internode encryption](https://redis.io/docs/latest/operate/rs/security/encryption/internode-encryption) — purpose and scope.\n- [PEM encryption](https://redis.io/docs/latest/operate/rs/security/encryption/pem-encryption) — encrypted private keys."},{"id":"whats-different-on-kubernetes","title":"What's different on Kubernetes","role":"content","text":"- **You capture certificates in Kubernetes Secrets and reference them declaratively in the REC spec.** The operator applies them to the cluster through the Redis Software REST API — the same way certificates are applied on Redis Software, cluster-wide rather than file-by-file on each node.\n- **cert-manager can issue and rotate certificates automatically**, replacing manual rotation steps."},{"id":"in-this-section","title":"In this section","role":"content","text":"- [Manage REC certificates](https://redis.io/docs/latest/operate/kubernetes/security/certificates/manage-rec-certificates) — configure cluster TLS certificates.\n- [cert-manager integration](https://redis.io/docs/latest/operate/kubernetes/security/certificates/cert-manager) — automate certificate issuance and rotation with cert-manager.\n- [Add client certificates](https://redis.io/docs/latest/operate/kubernetes/security/certificates/add-client-certificates) — enable client certificate authentication for databases.\n- [Internode encryption](https://redis.io/docs/latest/operate/kubernetes/security/certificates/internode-encryption) — enable encryption between cluster nodes."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/security/certificates/internode-encryption","title":"Enable internode encryption","url":"https://redis.io/docs/latest/operate/kubernetes/security/certificates/internode-encryption/","summary":"Enable encryption for communication between REC nodes and configure custom certificates.","aliases":["/operate/kubernetes/security/internode-encryption/"],"tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","page_type":"content","content_hash":"2c791bf0d31a066cdff94dc375cb7e220ae178b65bc270c6a8f3459a547d71c6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Internode encryption provides added security by encrypting communication between nodes in your Redis Enterprise cluster (REC)."},{"id":"enable-internode-encryption","title":"Enable internode encryption","role":"content","text":"Enable internode encryption in the `spec` section of your REC custom resource file.\n\n[code example]\n\nThis change will apply to all databases created in the REC. You can override the cluster-wide setting for individual databases.\n\nEdit your Redis Enterprise database (REDB) custom resource file to disable internode encryption for only that database.\n\n[code example]\n\nTo learn more about internode encryption, see [Internode encryption for Redis Enterprise Software](https://redis.io/docs/latest/operate/rs/security/encryption/internode-encryption)."},{"id":"use-custom-certificates-for-internode-encryption","title":"Use custom certificates for internode encryption","role":"content","text":"By default, Redis Enterprise uses self-signed certificates for internode encryption. You can provide your own certificates for both control plane and data plane internode encryption by storing them in Kubernetes secrets and referencing them in your REC specification."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"- Certificates must be in PEM format\n- You must create the Kubernetes secrets before referencing them in the REC spec\n- Certificates should include the full certificate chain if using a certificate authority"},{"id":"create-secrets-for-internode-encryption-certificates","title":"Create secrets for internode encryption certificates","role":"content","text":"Create Kubernetes secrets to store your internode encryption certificates. You need separate secrets for control plane and data plane encryption.\n\n1. Create a secret for control plane internode encryption:\n\n    [code example]\n\n2. Create a secret for data plane internode encryption:\n\n    [code example]"},{"id":"configure-certificates-in-rec-spec","title":"Configure certificates in REC spec","role":"configuration","text":"Add the certificate secret names to the `certificates` section of your REC specification:\n\n[code example]\n\nYou can configure one or both certificate types. If you don't specify a certificate secret name, the cluster uses a self-signed certificate for that encryption type.\n\nApply the updated REC specification:\n\n[code example]"},{"id":"certificate-rotation","title":"Certificate rotation","role":"content","text":"You can rotate internode encryption certificates using either of these methods:\n\n#### Method 1: Update the existing secret\n\nEdit the certificate data in the existing Kubernetes secret. The operator automatically detects the change and applies the new certificate.\n\n[code example]\n\n#### Method 2: Create a new secret and update the REC spec\n\n1. Create a new secret with the updated certificate:\n\n    [code example]\n\n2. Update the REC specification to reference the new secret:\n\n    [code example]\n\n3. Apply the updated REC specification:\n\n    [code example]"},{"id":"certificate-lifecycle","title":"Certificate lifecycle","role":"content","text":"When you remove a certificate secret reference from the REC specification, the operator does not delete the certificate from the Redis Enterprise cluster. The cluster continues to use the previously configured certificate until you explicitly replace it or the cluster reverts to using a self-signed certificate."},{"id":"more-info","title":"More info","role":"content","text":"- [Manage REC certificates](https://redis.io/docs/latest/operate/kubernetes/security/certificates/manage-rec-certificates) - General certificate management for Redis Enterprise clusters\n- [Configuration secrets](https://redis.io/docs/latest/operate/kubernetes/security/authentication/configuration-secrets) - Best practices for storing configuration in Kubernetes secrets\n- [Internode encryption for Redis Enterprise Software](https://redis.io/docs/latest/operate/rs/security/encryption/internode-encryption) - Detailed information about how internode encryption works"}],"examples":[{"id":"enable-internode-encryption-ex0","language":"yaml","code":"spec:\n    dataInternodeEncryption: true","section_id":"enable-internode-encryption"},{"id":"enable-internode-encryption-ex1","language":"yaml","code":"spec:\n    dataInternodeEncryption: false","section_id":"enable-internode-encryption"},{"id":"create-secrets-for-internode-encryption-certificates-ex0","language":"sh","code":"kubectl create secret generic cp-internode-cert \\\n      --from-file=certificate=</path/to/cp-certificate.pem> \\\n      --from-file=key=</path/to/cp-key.pem> \\\n      --from-literal=name=cp_internode_encryption","section_id":"create-secrets-for-internode-encryption-certificates"},{"id":"create-secrets-for-internode-encryption-certificates-ex1","language":"sh","code":"kubectl create secret generic dp-internode-cert \\\n      --from-file=certificate=</path/to/dp-certificate.pem> \\\n      --from-file=key=</path/to/dp-key.pem> \\\n      --from-literal=name=dp_internode_encryption","section_id":"create-secrets-for-internode-encryption-certificates"},{"id":"configure-certificates-in-rec-spec-ex0","language":"yaml","code":"spec:\n  dataInternodeEncryption: true\n  certificates:\n    cpInternodeEncryptionCertificateSecretName: cp-internode-cert\n    dpInternodeEncryptionCertificateSecretName: dp-internode-cert","section_id":"configure-certificates-in-rec-spec"},{"id":"configure-certificates-in-rec-spec-ex1","language":"sh","code":"kubectl apply -f <rec-file>.yaml","section_id":"configure-certificates-in-rec-spec"},{"id":"certificate-rotation-ex0","language":"sh","code":"kubectl create secret generic cp-internode-cert \\\n  --from-file=certificate=</path/to/new-cp-certificate.pem> \\\n  --from-file=key=</path/to/new-cp-key.pem> \\\n  --from-literal=name=cp_internode_encryption \\\n  --dry-run=client -o yaml | kubectl apply -f -","section_id":"certificate-rotation"},{"id":"certificate-rotation-ex1","language":"sh","code":"kubectl create secret generic cp-internode-cert-new \\\n      --from-file=certificate=</path/to/new-cp-certificate.pem> \\\n      --from-file=key=</path/to/new-cp-key.pem> \\\n      --from-literal=name=cp_internode_encryption","section_id":"certificate-rotation"},{"id":"certificate-rotation-ex2","language":"yaml","code":"spec:\n      certificates:\n        cpInternodeEncryptionCertificateSecretName: cp-internode-cert-new","section_id":"certificate-rotation"},{"id":"certificate-rotation-ex3","language":"sh","code":"kubectl apply -f <rec-file>.yaml","section_id":"certificate-rotation"}]}
{"schema_version":2,"id":"operate/kubernetes/security/certificates/manage-rec-certificates","title":"Manage Redis Enterprise cluster (REC) certificates","url":"https://redis.io/docs/latest/operate/kubernetes/security/certificates/manage-rec-certificates/","summary":"Install your own certificates to replace the self-signed certificates used by a Redis Enterprise cluster on Kubernetes.","aliases":["/operate/kubernetes/security/manage-rec-certificates/"],"tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","page_type":"content","content_hash":"273435420ecccedb7b16186b5354016a02b23b6c3d710dd50751d71bcd2107a0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software for Kubernetes generates self-signed TLS certificates for each new cluster. You can replace any of those certificates with your own.\n\nYou can manage REC certificates in two ways:\n\n- **[Method 1: Manage certificates with the REC custom resource](#method-1-manage-certificates-with-the-rec-custom-resource)** (recommended). Store each certificate in a Kubernetes secret and reference the secret from the REC custom resource. The operator applies the certificate and keeps the cluster in sync with the secret. Use this method whenever the certificate type is exposed in `spec.certificates`.\n- **[Method 2: Manage certificates with the Redis Software REST API](#method-2-manage-certificates-with-the-redis-software-rest-api)**. Call the cluster's REST API directly, bypassing the operator. Use this method only when you need to follow the Redis Software procedure for a cluster that does not define the certificate in `spec.certificates`. The operator overwrites changes made this way if the same certificate is also defined in the REC custom resource.\n\nFor the list of certificates and what each one encrypts, see the [certificates table](https://redis.io/docs/latest/operate/rs/security/certificates)."},{"id":"method-1-manage-certificates-with-the-rec-custom-resource","title":"Method 1: Manage certificates with the REC custom resource","role":"content","text":"This is the Kubernetes-native method. The operator detects changes to a referenced secret and rotates the certificate without manual intervention. You can create the secret manually, or have [cert-manager](https://redis.io/docs/latest/operate/kubernetes/security/certificates/cert-manager) issue and renew it automatically."},{"id":"supported-certificates","title":"Supported certificates","role":"compatibility","text":"The REC custom resource lets you replace these certificates through `spec.certificates`:\n\n| Certificate                        | REC custom resource field                     | Certificate name in Redis Software |\n| ---------------------------------- | --------------------------------------------- | ---------------------------------- |\n| API                                | `apiCertificateSecretName`                    | `api`                              |\n| Cluster Manager UI                 | `cmCertificateSecretName`                     | `cm`                               |\n| Control plane internode encryption | `cpInternodeEncryptionCertificateSecretName`  | `cp_internode_encryption`          |\n| Data plane internode encryption    | `dpInternodeEncryptionCertificateSecretName`  | `dp_internode_encryption`          |\n| LDAP client                        | `ldapClientCertificateSecretName`             | `ldap_client`                      |\n| Metrics exporter                   | `metricsExporterCertificateSecretName`        | `metrics_exporter`                 |\n| Proxy                              | `proxyCertificateSecretName`                  | `proxy`                            |\n| SSO issuer (SAML IdP)              | `ssoIssuerCertificateSecretName`              | `sso_issuer`                       |\n| SSO service (SAML SP)              | `ssoServiceCertificateSecretName`             | `sso_service`                      |\n| Syncer                             | `syncerCertificateSecretName`                 | `syncer`                           |\n\nRotating any of these certificates does not restart REC pods."},{"id":"step-1-create-a-secret-for-the-certificate","title":"Step 1: Create a secret for the certificate","role":"content","text":"Create a Kubernetes [secret](https://kubernetes.io/docs/concepts/configuration/secret/) that holds the PEM-encoded certificate and key:\n\n[code example]\n\nChoose any value for `<secret-name>`; you'll reference it from `spec.certificates` in [Step 2](#step-2-reference-the-secret-in-the-rec-custom-resource). The operator resolves which Redis Software certificate to replace from the REC field name, not from the secret.\n\n#### Supported secret keys\n\nThe operator accepts several key names for the certificate and private key, so you can use either an opaque secret or a [kubernetes.io/tls](https://kubernetes.io/docs/concepts/configuration/secret/#tls-secrets) secret (the format that cert-manager produces):\n\n| Field       | Accepted secret keys                  |\n| ----------- | ------------------------------------- |\n| Certificate | `cert`, `certificate`, or `tls.crt`   |\n| Private key | `key` or `tls.key`                    |\n\n> [!NOTE]\n> On Redis Software for Kubernetes versions older than 8.0.18, also include `--from-literal=name=<certificate-name>` in the `kubectl create secret` command, where `<certificate-name>` is the value from the **Certificate name in Redis Software** column in the [supported certificates](#supported-certificates) table.\n\nFor internode encryption certificates, see [Internode encryption](https://redis.io/docs/latest/operate/kubernetes/security/certificates/internode-encryption) for the full setup, which covers enabling internode encryption alongside the certificate configuration."},{"id":"step-2-reference-the-secret-in-the-rec-custom-resource","title":"Step 2: Reference the secret in the REC custom resource","role":"content","text":"Edit the REC custom resource and add a `certificates` section under `spec`. Include only the fields for the certificates you are replacing; omit the rest.\n\n[code example]\n\nApply the updated REC custom resource:\n\n[code example]\n\nThe operator detects the change and rotates the certificate on the cluster. New client connections use the new certificate; existing connections continue with the old one until they reconnect."},{"id":"step-3-verify-the-rotation-optional","title":"Step 3: Verify the rotation (optional)","role":"content","text":"To confirm that the new certificate is in place, call the Redis Software REST API and list the active cluster certificates:\n\n[code example]"},{"id":"method-2-manage-certificates-with-the-redis-software-rest-api","title":"Method 2: Manage certificates with the Redis Software REST API","role":"content","text":"Use the Redis Software REST API or `rladmin` directly against the cluster, bypassing the operator.\n\n> [!WARNING]\n> If `spec.certificates` in the REC custom resource defines the same certificate, the operator overwrites your API change. Before you update a certificate through the REST API, remove the corresponding field from `spec.certificates`, or apply the same change in both places.\n\nFor the procedure, including the `rladmin` and REST API examples, see [Update certificates](https://redis.io/docs/latest/operate/rs/security/certificates/updating-certificates).\n\nAfter the update, verify the rotation as described in [Step 3](#step-3-verify-the-rotation-optional)."},{"id":"active-active-database-certificate-updates","title":"Active-Active database certificate updates","role":"content","text":"The operator automates certificate updates for [Active-Active](https://redis.io/docs/latest/operate/kubernetes/active-active) databases. When you update the proxy or syncer certificate secret referenced by the REC, the operator detects the change and propagates the new certificate to all participating clusters.\n\nThis automation applies whether you manage the secret directly or with [cert-manager](https://redis.io/docs/latest/operate/kubernetes/security/certificates/cert-manager#active-active-databases-with-automatic-certificate-sync)."},{"id":"more-info","title":"More info","role":"content","text":"- [Update certificates](https://redis.io/docs/latest/operate/rs/security/certificates/updating-certificates)\n- [Install your own certificates](https://redis.io/docs/latest/operate/rs/security/certificates/create-certificates)\n- [Certificates table](https://redis.io/docs/latest/operate/rs/security/certificates)\n- [Glossary/Transport Layer Security (TLS)](https://redis.io/docs/latest/glossary#letter-t)"}],"examples":[{"id":"step-1-create-a-secret-for-the-certificate-ex0","language":"sh","code":"kubectl create secret generic <secret-name> \\\n  --from-file=certificate=</path/to/certificate.pem> \\\n  --from-file=key=</path/to/key.pem>","section_id":"step-1-create-a-secret-for-the-certificate"},{"id":"step-2-reference-the-secret-in-the-rec-custom-resource-ex0","language":"yaml","code":"spec:\n  certificates:\n    apiCertificateSecretName: <apicert-secret-name>\n    cmCertificateSecretName: <cmcert-secret-name>\n    cpInternodeEncryptionCertificateSecretName: <cpine-secret-name>\n    dpInternodeEncryptionCertificateSecretName: <dpine-secret-name>\n    ldapClientCertificateSecretName: <ldapcert-secret-name>\n    metricsExporterCertificateSecretName: <metricscert-secret-name>\n    proxyCertificateSecretName: <proxycert-secret-name>\n    ssoIssuerCertificateSecretName: <ssoissuer-secret-name>\n    ssoServiceCertificateSecretName: <ssoservice-secret-name>\n    syncerCertificateSecretName: <syncercert-secret-name>","section_id":"step-2-reference-the-secret-in-the-rec-custom-resource"},{"id":"step-2-reference-the-secret-in-the-rec-custom-resource-ex1","language":"sh","code":"kubectl apply -f <rec-file>.yaml","section_id":"step-2-reference-the-secret-in-the-rec-custom-resource"},{"id":"step-3-verify-the-rotation-optional-ex0","language":"http","code":"GET /v1/cluster/certificates","section_id":"step-3-verify-the-rotation-optional"}]}
{"schema_version":2,"id":"operate/kubernetes/security/fips","title":"Enable FIPS 140-3 compliance mode","url":"https://redis.io/docs/latest/operate/kubernetes/security/fips/","summary":"Run Redis Software for Kubernetes in FIPS 140-3 compliance mode.","tags":["docs","operate","kubernetes"],"last_updated":"2026-08-06T09:37:08-05:00","page_type":"content","content_hash":"2fa2ebd212ca20718a907fed794ea16ec3f2f7e3c5c123db22173851211a7d2d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can run your Redis Software for Kubernetes cluster in FIPS 140-3 compliance mode, which uses cryptographic libraries based on FIPS 140-3 validated modules and enables the use of FIPS-approved cryptographic algorithms. FIPS mode is available starting with operator version 8.2.0.\n\nWhen you enable FIPS mode, the operator deploys a FIPS-compatible image in place of the standard one."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"FIPS compliance depends on the underlying platform, not only on Redis:\n\n- **FIPS-configured nodes.** A container runs in FIPS mode only if the host node is configured for FIPS. On OpenShift, the cluster must be installed in FIPS mode. Redis does not configure the host; this is the responsibility of your platform.\n- **amd64 architecture.** The FIPS-compatible Redis Software image is built for `amd64` only. There is no arm64 FIPS image."},{"id":"enable-fips-mode","title":"Enable FIPS mode","role":"content","text":"Set `spec.securityContext.fips.enabled` to `true` on the `RedisEnterpriseCluster` (REC):\n\n[code example]\n\nWhen enabled, the operator adds a `.fips` suffix to the image version tag and pulls the matching FIPS-compatible image (for example, `redislabs/redis:<version>.fips`). The field defaults to `false`."},{"id":"pull-the-image-by-digest","title":"Pull the image by digest","role":"content","text":"If you pin the image by digest (`spec.redisEnterpriseImageSpec`) instead of by tag, the operator can't add the `.fips` suffix. In that case, supply a digest that points to a FIPS-compatible image yourself."}],"examples":[{"id":"enable-fips-mode-ex0","language":"yaml","code":"apiVersion: app.redislabs.com/v1\nkind: RedisEnterpriseCluster\nmetadata:\n  name: rec\nspec:\n  securityContext:\n    fips:\n      enabled: true","section_id":"enable-fips-mode"}]}
{"schema_version":2,"id":"operate/kubernetes/security","title":"Security","url":"https://redis.io/docs/latest/operate/kubernetes/security/","summary":"Configure security settings for Redis Software clusters and databases on Kubernetes.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","children":[{"id":"operate/kubernetes/security/authentication","summary":"Manage Redis Software cluster credentials, LDAP, SSO, and configuration secrets on Kubernetes.","title":"Authentication","url":"https://redis.io/docs/latest/operate/kubernetes/security/authentication/"},{"id":"operate/kubernetes/security/access-control","summary":"Manage Redis Software users, roles, ACLs, and role bindings on Kubernetes with custom resources.","title":"Access control","url":"https://redis.io/docs/latest/operate/kubernetes/security/access-control/"},{"id":"operate/kubernetes/security/certificates","summary":"Manage TLS certificates, client certificates, and internode encryption for Redis Software on Kubernetes.","title":"Certificates and encryption","url":"https://redis.io/docs/latest/operate/kubernetes/security/certificates/"},{"id":"operate/kubernetes/security/vault","summary":"Configure HashiCorp Vault as the centralized secret management system for Redis Enterprise for Kubernetes.","title":"Integrate HashiCorp Vault with Redis Enterprise for Kubernetes","url":"https://redis.io/docs/latest/operate/kubernetes/security/vault/"},{"id":"operate/kubernetes/security/allow-resource-adjustment","summary":"Enable automatic system resource adjustments for Redis Enterprise to increase file descriptor limits.","title":"Allow automatic resource adjustment","url":"https://redis.io/docs/latest/operate/kubernetes/security/allow-resource-adjustment/"},{"id":"operate/kubernetes/security/fips","summary":"Run Redis Software for Kubernetes in FIPS 140-3 compliance mode.","title":"Enable FIPS 140-3 compliance mode","url":"https://redis.io/docs/latest/operate/kubernetes/security/fips/"}],"page_type":"content","content_hash":"fa0c9591b854a8ec81fce61cb61c4d97d28e670fd67d949f7ba502a19ab147c9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Configure security settings for Redis for Kubernetes. Security covers access control, cluster credentials, external identity providers, TLS certificates and encryption, and external secret management."},{"id":"access-control","title":"Access control","role":"content","text":"- [Access control](https://redis.io/docs/latest/operate/kubernetes/security/access-control) — manage Redis Software users, roles, ACLs, and role bindings as Kubernetes custom resources."},{"id":"authentication","title":"Authentication","role":"security","text":"- [Authentication](https://redis.io/docs/latest/operate/kubernetes/security/authentication) — manage cluster credentials, LDAP, SAML SSO, and configuration secrets."},{"id":"certificates-and-encryption","title":"Certificates and encryption","role":"content","text":"- [Certificates and encryption](https://redis.io/docs/latest/operate/kubernetes/security/certificates) — provision TLS certificates, integrate cert-manager, add client certificates, and enable internode encryption."},{"id":"secret-management","title":"Secret management","role":"content","text":"- [HashiCorp Vault integration](https://redis.io/docs/latest/operate/kubernetes/security/vault) — use HashiCorp Vault as the centralized secret store for Redis for Kubernetes."},{"id":"resource-management","title":"Resource management","role":"content","text":"- [Allow resource adjustment](https://redis.io/docs/latest/operate/kubernetes/security/allow-resource-adjustment) — enable automatic adjustment of system resources for security compliance."},{"id":"compliance","title":"Compliance","role":"content","text":"- [FIPS compliance](https://redis.io/docs/latest/operate/kubernetes/security/fips) — run your cluster in FIPS 140-3 compliance mode."}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/security/vault","title":"Integrate HashiCorp Vault with Redis Enterprise for Kubernetes","url":"https://redis.io/docs/latest/operate/kubernetes/security/vault/","summary":"Configure HashiCorp Vault as the centralized secret management system for Redis Enterprise for Kubernetes.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T15:03:12+01:00","page_type":"content","content_hash":"69e9e41781658f1401a4c4e26646c4b9af415c042bf17ff7b88a17270e080dad","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can configure HashiCorp Vault as the centralized secret management system for the Redis Enterprise Kubernetes operator, replacing the default Kubernetes secrets. This integration provides enhanced security, centralized secret management, and advanced features like secret rotation and audit logging."},{"id":"what-secrets-are-managed-by-vault","title":"What secrets are managed by Vault?","role":"content","text":"When Vault integration is enabled, all secrets referenced in Redis Enterprise custom resources are retrieved from Vault instead of Kubernetes secrets, including:\n\n| **Category** | **Secret Type** | **API Field** | **Description** |\n|---|---|---|---|\n| **Cluster secrets** |  |  |  |\n|  | [Cluster credentials](https://redis.io/docs/latest/operate/kubernetes/deployment/quick-start) | [`clusterCredentialSecretName`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api#spec) | Authentication credentials for cluster access |\n|  | [License](https://redis.io/docs/latest/operate/kubernetes/deployment/quick-start#install-the-license) | [`licenseSecretName`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api#spec) | Redis Enterprise license key |\n|  | [API certificate](https://redis.io/docs/latest/operate/kubernetes/security/certificates/manage-rec-certificates) | [`apiCertificateSecretName`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api#speccertificates) | TLS certificate for API server |\n|  | [Cluster manager certificate](https://redis.io/docs/latest/operate/kubernetes/security/certificates/manage-rec-certificates) | [`cmCertificateSecretName`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api#speccertificates) | TLS certificate for cluster manager |\n|  | [Metrics exporter certificate](https://redis.io/docs/latest/operate/kubernetes/re-clusters/connect-prometheus-operator) | [`metricsExporterCertificateSecretName`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api#speccertificates) | TLS certificate for metrics exporter |\n|  | [Proxy certificate](https://redis.io/docs/latest/operate/kubernetes/security/certificates/manage-rec-certificates) | [`proxyCertificateSecretName`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api#speccertificates) | TLS certificate for proxy |\n|  | [Syncer certificate](https://redis.io/docs/latest/operate/kubernetes/active-active) | [`syncerCertificateSecretName`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api#speccertificates) | TLS certificate for Active-Active syncer |\n|  | [LDAP client certificate](https://redis.io/docs/latest/operate/kubernetes/security/authentication/ldap) | [`ldapClientCertificateSecretName`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api#speccertificates) | TLS certificate for LDAP client authentication |\n|  | [LDAP bind credentials](https://redis.io/docs/latest/operate/kubernetes/security/authentication/ldap) | [`bindCredentialsSecretName`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api#specldap) | Credentials for authenticating to the LDAP server |\n|  | [CPINE certificate](https://redis.io/docs/latest/operate/kubernetes/security/certificates/manage-rec-certificates) | [`cpInternodeEncryptionCertificateSecretName`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api#speccertificates) | TLS certificate for Control Plane Internode Encryption (CPINE) |\n|  | [DPINE certificate](https://redis.io/docs/latest/operate/kubernetes/security/certificates/manage-rec-certificates) | [`dpInternodeEncryptionCertificateSecretName`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api#speccertificates) | TLS certificate for Data Plane Internode Encryption (DPINE) |\n|  | [SSO service certificate](https://redis.io/docs/latest/operate/kubernetes/security/authentication/sso) | [`ssoServiceCertificateSecretName`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api#speccertificates) | Service Provider (SP) certificate for SAML SSO |\n|  | [SSO issuer certificate](https://redis.io/docs/latest/operate/kubernetes/security/authentication/sso) | [`ssoIssuerCertificateSecretName`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api#speccertificates) | Identity Provider (IdP) public certificate for SAML SSO |\n|  | [SSO IdP metadata](https://redis.io/docs/latest/operate/kubernetes/security/authentication/sso) | [`idpMetadataSecretName`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api#specssosaml) | SAML Identity Provider metadata XML |\n|  | [User-defined module credentials](https://redis.io/docs/latest/operate/kubernetes/re-databases/modules) | [`credentialsSecret`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api#specuserdefinedmodulessourcehttps) | Credentials for downloading user-defined modules from authenticated repositories |\n| **Database secrets** |  |  |  |\n|  | [Database passwords](https://redis.io/docs/latest/operate/kubernetes/networking/database-connectivity#credentials-and-secrets-management) | Various | Passwords for Redis databases |\n|  | [Replica source client TLS key](https://redis.io/docs/latest/operate/kubernetes/re-databases/replica-redb) | [`clientKeySecret`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api#specreplicasources) | Client TLS key for cross-cluster replication |\n|  | [Replica source server certificate](https://redis.io/docs/latest/operate/kubernetes/re-databases/replica-redb) | [`serverCertSecret`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api#specreplicasources) | Server certificate for cross-cluster replication |\n|  | [S3 backup credentials](https://redis.io/docs/latest/operate/kubernetes/re-databases) | [`awsSecretName`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api#specbackups3) | AWS S3 storage credentials for database backups |\n|  | [SFTP backup credentials](https://redis.io/docs/latest/operate/kubernetes/re-databases) | [`sftpSecretName`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api#specbackupsftp) | SFTP storage credentials for database backups |\n|  | [Swift backup credentials](https://redis.io/docs/latest/operate/kubernetes/re-databases) | [`swiftSecretName`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api#specbackupswift) | Swift storage credentials for database backups |\n|  | [Azure Blob backup credentials](https://redis.io/docs/latest/operate/kubernetes/re-databases) | [`absSecretName`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api#specbackupabs) | Azure Blob storage credentials for database backups |\n|  | [Google Cloud backup credentials](https://redis.io/docs/latest/operate/kubernetes/re-databases) | [`gcsSecretName`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api#specbackupgcs) | Google Cloud storage credentials for database backups |\n|  | [Client authentication certificates](https://redis.io/docs/latest/operate/kubernetes/security/certificates/add-client-certificates) | Various | TLS client certificates for authentication |\n| **Other secrets** |  |  |  |\n|  | [Remote cluster secrets](https://redis.io/docs/latest/operate/kubernetes/active-active) | [`secretName`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_remote_cluster_api#spec) | Credentials for Redis Enterprise Remote Cluster (RERC) configurations |\n|  | [Active-Active database secrets](https://redis.io/docs/latest/operate/kubernetes/active-active) | [`globalConfigurations`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_active_active_database_api#specglobalconfigurations) | All secret names specified in REAADB global configurations |\n\n> [!NOTE]\n> The SSO Service Provider (SP) metadata secret (`spMetadataSecretName`) is **not** managed by Vault. This secret is operator-generated and is not written to Vault. To retrieve SP metadata when using Vault, fetch it directly from the Redis Enterprise Server API (`GET /v1/cluster/sso/saml/metadata/sp`).\n\nFor complete details on supported secrets, see the [`RedisEnterpriseCluster` API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api) and [`RedisEnterpriseDatabase` API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api)."},{"id":"secret-path-structure","title":"Secret path structure","role":"content","text":"Vault secrets follow a hierarchical path structure:\n[code example]\n\nDefault example:\n[code example]\n\n> [!NOTE]\n> When using OpenShift, replace `kubectl` commands with `oc` throughout this guide."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before integrating Redis Enterprise operator with HashiCorp Vault, ensure you have the following components properly configured:\n\nHashiCorp Vault Requirements:\n\n- Vault instance: HashiCorp Vault v1.15.2+ with TLS and network connectivity to your Kubernetes cluster\n- Authentication method: Configure Kubernetes authentication method in Vault (see [HashiCorp's Kubernetes Auth documentation](https://developer.hashicorp.com/vault/docs/auth/kubernetes))\n- Secret engine: Enable and configure KV version 2 secret engine\n  - Default mount path: `secret/` (configurable)\n  - Used to store all Redis Enterprise secrets\n  - Supports versioning and metadata\n\nKubernetes Requirements:\n\n- Vault Agent Injector: Deploy the HashiCorp Vault Agent Injector\n  - Enables automatic secret injection into pods\n  - See [Vault Agent Injector tutorial](https://developer.hashicorp.com/vault/tutorials/kubernetes/kubernetes-sidecar)\n- Network access: Ensure Kubernetes cluster can reach Vault\n  - Configure appropriate network policies and firewall rules\n  - Vault typically runs on port 8200 (HTTPS)\n- Service accounts: Proper RBAC configuration for operator service accounts\n\nVault editions:\n\nThis guide supports both Vault Community and Enterprise editions:\n\n- Vault Community: Use all commands without `-namespace` flags or `VAULT_NAMESPACE` parameters\n- Vault Enterprise: Supports namespaces for logical isolation and multi-tenancy (separate from Kubernetes namespaces)\n\nMinimum token TTL:\n\nConfigure Vault token policies with minimum TTL of 1 hour:\n- Prevents frequent token renewal overhead\n- Ensures stable operation during maintenance windows\n- See [Vault token management](https://developer.hashicorp.com/vault/tutorials/tokens/token-management)"},{"id":"deployment-scenarios","title":"Deployment scenarios","role":"content","text":"This guide covers the most common deployment scenario with the following assumptions:\n\n- Vault Enterprise with namespace support (adapt for Community Edition by removing namespace parameters)\n- Multiple Redis Enterprise clusters in the same Kubernetes cluster\n- Namespace isolation using Kubernetes namespace suffixes for Vault configurations\n- Production security with proper RBAC and network policies\n\n> [!NOTE]\n> Multi-cluster considerations: When deploying across multiple Kubernetes clusters with identical namespace names, additional prefixing may be required to avoid Vault path conflicts."},{"id":"configure-the-operator","title":"Configure the operator","role":"configuration","text":"1. Configure Vault policies and roles\n\n   Create a policy that grants the Redis Enterprise operator read access to secrets:\n\n   [code example]\n\n   Parameter explanation:\n   - `<VAULT_NAMESPACE>`: Your Vault Enterprise namespace (omit for Community Edition)\n   - `<K8S_NAMESPACE>`: Kubernetes namespace where Redis Enterprise operator is deployed\n\n   Configure a Vault role that binds the operator's service account to the policy:\n\n   [code example]\n\n   Parameter explanation:\n   - `<AUTH_PATH>`: Kubernetes auth method path in Vault (default: `kubernetes`)\n   - Role name includes namespace for multi-tenant isolation\n\n2. Configure operator environment\n\n   Create a ConfigMap with Vault configuration for the Redis Enterprise operator:\n\n   [code example]\n\n   Apply the configuration:\n\n   [code example]\n\n   Configuration parameters:\n\n   | Parameter | Description | Default | Required |\n   |-----------|-------------|---------|----------|\n   | `CREDENTIAL_TYPE` | Must be set to `\"vault\"` to enable Vault integration | - | Yes |\n   | `VAULT_SERVER_FQDN` | Vault server hostname (e.g., `vault.vault-ns.svc.cluster.local`) | - | Yes |\n   | `VAULT_SERVICE_PORT_HTTPS` | Vault HTTPS port | `8200` | Yes |\n   | `VAULT_SECRET_ROOT` | KV-v2 secret engine mount path | `secret` | Yes |\n   | `VAULT_SECRET_PREFIX` | Prefix for all Redis Enterprise secrets | `redisenterprise` | Yes |\n   | `VAULT_ROLE` | Vault role for operator authentication | `redis-enterprise-operator` | Yes |\n   | `VAULT_AUTH_PATH` | Kubernetes auth method path | `kubernetes` | Yes |\n   | `VAULT_NAMESPACE` | Vault Enterprise namespace | - | Enterprise only |\n   | `VAULT_CACHE_SECRET_EXPIRATION_SECONDS` | Secret cache duration | `120` | No |\n\n   Secret path construction: Secrets are stored at `<VAULT_SECRET_ROOT>/data/<VAULT_SECRET_PREFIX>/<secret-name>`\n\n   <br>\n\n3. Deploy the operator\n\n   Deploy the Redis Enterprise operator following the [standard installation guide](https://redis.io/docs/latest/operate/kubernetes/deployment).\n\n   > [!WARNING]\n   > The operator pod will not be ready until the admission controller secret is stored in Vault (covered in the next step).\n\n   <br>\n\n4. Configure admission controller secret\n\n   Generate and store the admission controller TLS certificate in Vault:\n\n   [code example]\n\n   Copy the certificate file to Vault (if Vault is running in Kubernetes):\n\n   [code example]\n\n   Store the certificate in Vault:\n\n   [code example]\n\n   > [!NOTE]\n   > Once the operator is running with Vault integration, proceed to create Redis Enterprise clusters. Do not create clusters before completing this setup.\n\n   <br>\n\n5. Create Vault CA certificate secret\n\n   Create a Kubernetes secret containing the Certificate Authority certificate used by your Vault instance:\n\n   [code example]\n\n   > [!WARNING]\n   > The Vault server certificate must be signed by the Certificate Authority provided in this secret."},{"id":"create-redis-enterprise-clusters","title":"Create Redis Enterprise clusters","role":"content","text":"1. Generate cluster credentials\n\n   Unlike standard deployments, Vault integration requires manually creating cluster credentials:\n\n   [code example]\n\n   Store credentials in Vault:\n\n   [code example]\n\n   > [!NOTE] Important notes\n   > - The username field in the REC spec is ignored when using Vault\n   > - The username from the Vault secret takes precedence\n   > - Use strong, unique passwords for each cluster\n\n   <br>\n\n2. Create cluster service account role\n\n   Configure a Vault role for the Redis Enterprise cluster's service account:\n\n   [code example]\n\n   <br>\n\n3. Deploy Redis Enterprise cluster\n\n   Create the `RedisEnterpriseCluster` resource with Vault configuration:\n\n   [code example]\n\n   Apply the configuration:\n\n   [code example]\n\n   Key configuration fields:\n\n   | Field | Description | Example |\n   |-------|-------------|---------|\n   | `clusterCredentialSecretName` | Path of the secret in Vault containing cluster credentials. Can be customized during cluster creation but cannot be changed afterward. The secret must be pre-created in Vault. | `rec` |\n   | `clusterCredentialSecretType` | Must be set to `vault` | `vault` |\n   | `clusterCredentialSecretRole` | Vault role for cluster authentication | `redis-enterprise-rec-<K8S_NAMESPACE>` |\n   | `vaultCASecret` | Kubernetes secret containing Vault's CA certificate | `vault-ca-cert` |\n   | `podAnnotations` | Vault agent annotations for pod-level configuration | See example above |"},{"id":"create-redis-enterprise-databases","title":"Create Redis Enterprise databases","role":"content","text":"To create a Redis Enterprise database (REDB) with Vault integration:\n\n1. Create database password in Vault:\n   [code example]\n\n   <br>\n\n2. Create the REDB custom resource:\n   Follow the standard [database creation process](https://redis.io/docs/latest/operate/kubernetes/re-databases). The REC configuration automatically enables Vault integration for all databases.\n\n   <br>\n\n3. Configure additional secrets (optional):\n   Store additional REDB secrets in the path `redisenterprise-<K8S_NAMESPACE>/`. Secrets must comply with the [REDB secrets schema](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api).\n\n> [!NOTE]\n> When using the Redis Enterprise Vault plugin, set `defaultUser: false` and associate users through ACL bindings to the REDB.\n\nFor complete field documentation, see the [Redis Enterprise database API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api)."},{"id":"redis-enterprise-remote-cluster-secrets","title":"Redis Enterprise Remote Cluster secrets","role":"content","text":"The `secretName` field is supported and should be stored in HashiCorp Vault when the Redis Enterprise cluster uses Vault as a secret source."},{"id":"redis-enterprise-active-active-database-secrets","title":"Redis Enterprise Active-Active database secrets","role":"content","text":"REAADB resources include REDB specifications in the `globalConfigurations` field. All secret names specified in these configurations are supported and should be stored in HashiCorp Vault when the Redis Enterprise cluster uses Vault as a secret source."},{"id":"manage-secrets","title":"Manage secrets","role":"content","text":"> [!NOTE]\n> Complete field documentation is available in the [`RedisEnterpriseCluster` API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api) and [`RedisEnterpriseDatabase` API reference](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api)."},{"id":"redis-enterprise-cluster-secrets","title":"Redis Enterprise cluster secrets","role":"content","text":"#### Example REC configuration with all certificates\n\n[code example]\n\nYou can also update certificates using `kubectl patch`:\n\n[code example]"},{"id":"database-secrets","title":"Database secrets","role":"content","text":"#### Database passwords\nStore database passwords in Vault using the database name as the secret key:\n\n[code example]\n\n#### Backup storage credentials\nStore backup storage credentials for Redis Enterprise databases:\n\n[code example]\n\n#### TLS certificates\nStore TLS certificates for database connections:\n\n[code example]"},{"id":"user-defined-module-credentials","title":"User-defined module credentials","role":"content","text":"Store credentials for downloading user-defined modules from authenticated repositories:\n\n[code example]\n\nReference this secret in your REC specification's `userDefinedModules` section. See [Configure modules](https://redis.io/docs/latest/operate/kubernetes/re-databases/modules) for details."},{"id":"troubleshooting","title":"Troubleshooting","role":"errors","text":""},{"id":"common-issues-and-solutions","title":"Common Issues and Solutions","role":"content","text":"#### Operator pod not ready\n\nSymptoms: Operator pod remains in `Pending` or `CrashLoopBackOff` state\n\nCauses and solutions:\n\n1. Missing admission controller secret:\n   [code example]\n\n2. Vault CA certificate issues:\n   [code example]\n\n3. Network connectivity:\n   [code example]\n\n#### Authentication failures\n\nSymptoms: `Failed to authenticate with Vault` errors in operator logs\n\nSolutions:\n\n1. Verify Vault role configuration:\n   [code example]\n\n2. Check service account token:\n   [code example]\n\n#### Secret retrieval failures\n\nSymptoms: `Failed to read Vault secret` errors\n\nSolutions:\n\n1. Verify secret exists:\n   [code example]\n\n2. Check policy permissions:\n   [code example]\n\n3. Validate secret format:\n   [code example]"},{"id":"debugging-commands","title":"Debugging commands","role":"content","text":"Check operator logs:\n[code example]\n\nVerify Vault configuration:\n[code example]\n\nTest Vault authentication:\n[code example]"}],"examples":[{"id":"secret-path-structure-ex0","language":"plaintext","code":"<VAULT_SECRET_ROOT>/<VAULT_SECRET_PREFIX>/<secret-name>","section_id":"secret-path-structure"},{"id":"secret-path-structure-ex1","language":"plaintext","code":"secret/data/redisenterprise-redis-ns/my-cluster\nsecret/data/redisenterprise-redis-ns/my-database-password\nsecret/data/redisenterprise-redis-ns/tls-certificates","section_id":"secret-path-structure"},{"id":"configure-the-operator-ex0","language":"bash","code":"vault policy write -namespace=<VAULT_NAMESPACE> redisenterprise-<K8S_NAMESPACE> - <<EOF\n   path \"secret/data/redisenterprise-<K8S_NAMESPACE>/*\" {\n     capabilities = [\"create\", \"read\", \"update\", \"delete\", \"list\"]\n   }\n   path \"secret/metadata/redisenterprise-<K8S_NAMESPACE>/*\" {\n     capabilities = [\"list\"]\n   }\n   EOF","section_id":"configure-the-operator"},{"id":"configure-the-operator-ex1","language":"bash","code":"vault write -namespace=<VAULT_NAMESPACE> auth/<AUTH_PATH>/role/redis-enterprise-operator-<K8S_NAMESPACE> \\\n           bound_service_account_names=\"redis-enterprise-operator\" \\\n           bound_service_account_namespaces=<K8S_NAMESPACE> \\\n           policies=redisenterprise-<K8S_NAMESPACE>","section_id":"configure-the-operator"},{"id":"configure-the-operator-ex2","language":"yaml","code":"# operator-environment-config.yaml\n   apiVersion: v1\n   kind: ConfigMap\n   metadata:\n     name: operator-environment-config\n     namespace: <K8S_NAMESPACE>\n   data:\n     CREDENTIAL_TYPE: \"vault\"\n     VAULT_SERVER_FQDN: \"<VAULT_FQDN>\"\n     VAULT_SERVICE_PORT_HTTPS: \"8200\"\n     VAULT_SECRET_ROOT: \"secret\"\n     VAULT_SECRET_PREFIX: \"redisenterprise-<K8S_NAMESPACE>\"\n     VAULT_ROLE: \"redis-enterprise-operator-<K8S_NAMESPACE>\"\n     VAULT_AUTH_PATH: \"<AUTH_PATH>\"\n     VAULT_NAMESPACE: \"<VAULT_NAMESPACE>\"\n     VAULT_CACHE_SECRET_EXPIRATION_SECONDS: \"120\"","section_id":"configure-the-operator"},{"id":"configure-the-operator-ex3","language":"bash","code":"kubectl apply -f operator-environment-config.yaml","section_id":"configure-the-operator"},{"id":"configure-the-operator-ex4","language":"bash","code":"kubectl exec -it $(kubectl get pod -l name=redis-enterprise-operator -o jsonpath='{.items[0].metadata.name}') \\\n     -c redis-enterprise-operator -- /usr/local/bin/generate-tls -infer | tail -4 > output.json","section_id":"configure-the-operator"},{"id":"configure-the-operator-ex5","language":"bash","code":"kubectl cp output.json vault-0:/tmp -n vault","section_id":"configure-the-operator"},{"id":"configure-the-operator-ex6","language":"bash","code":"vault kv put -namespace=<VAULT_NAMESPACE> <VAULT_SECRET_ROOT>/redisenterprise-<K8S_NAMESPACE>/admission-tls @output.json","section_id":"configure-the-operator"},{"id":"configure-the-operator-ex7","language":"bash","code":"kubectl create secret generic vault-ca-cert \\\n           --namespace <K8S_NAMESPACE> \\\n           --from-file=vault.ca=<vault-ca-cert-file-path>","section_id":"configure-the-operator"},{"id":"create-redis-enterprise-clusters-ex0","language":"bash","code":"# Generate a secure random password\n   openssl rand -base64 32","section_id":"create-redis-enterprise-clusters"},{"id":"create-redis-enterprise-clusters-ex1","language":"bash","code":"vault kv put -namespace=<VAULT_NAMESPACE> \\\n     <VAULT_SECRET_ROOT>/redisenterprise-<K8S_NAMESPACE>/<REC_NAME> \\\n     username=<YOUR_USERNAME> \\\n     password=<YOUR_PASSWORD>","section_id":"create-redis-enterprise-clusters"},{"id":"create-redis-enterprise-clusters-ex2","language":"bash","code":"vault write -namespace=<VAULT_NAMESPACE> \\\n     auth/<AUTH_PATH>/role/redis-enterprise-rec-<K8S_NAMESPACE> \\\n     bound_service_account_names=<REC_NAME> \\\n     bound_service_account_namespaces=<K8S_NAMESPACE> \\\n     policies=redisenterprise-<K8S_NAMESPACE>","section_id":"create-redis-enterprise-clusters"},{"id":"create-redis-enterprise-clusters-ex3","language":"yaml","code":"apiVersion: app.redislabs.com/v1\n   kind: RedisEnterpriseCluster\n   metadata:\n     name: rec\n     namespace: <K8S_NAMESPACE>\n     labels:\n       app: redis-enterprise\n   spec:\n     nodes: 3\n     clusterCredentialSecretName: rec\n     clusterCredentialSecretType: vault\n     clusterCredentialSecretRole: redis-enterprise-rec-<K8S_NAMESPACE>\n     vaultCASecret: vault-ca-cert\n     podAnnotations:\n       vault.hashicorp.com/auth-path: auth/<AUTH_PATH>\n       vault.hashicorp.com/namespace: <VAULT_NAMESPACE>","section_id":"create-redis-enterprise-clusters"},{"id":"create-redis-enterprise-clusters-ex4","language":"bash","code":"kubectl apply -f redis-enterprise-cluster.yaml","section_id":"create-redis-enterprise-clusters"},{"id":"create-redis-enterprise-databases-ex0","language":"bash","code":"vault kv put -namespace=<VAULT_NAMESPACE> \\\n     <VAULT_SECRET_ROOT>/redisenterprise-<K8S_NAMESPACE>/redb-<DATABASE_NAME> \\\n     password=<DATABASE_PASSWORD>","section_id":"create-redis-enterprise-databases"},{"id":"redis-enterprise-cluster-secrets-ex0","language":"yaml","code":"apiVersion: app.redislabs.com/v1\nkind: RedisEnterpriseCluster\nmetadata:\n  name: rec\n  labels:\n    app: redis-enterprise\nspec:\n  nodes: 3\n  licenseSecretName: <VAULT_SECRET_NAME>\n  clusterCredentialSecretName: <VAULT_SECRET_NAME>\n  certificates:\n    apiCertificateSecretName: <VAULT_SECRET_NAME>\n    cmCertificateSecretName: <VAULT_SECRET_NAME>\n    metricsExporterCertificateSecretName: <VAULT_SECRET_NAME>\n    proxyCertificateSecretName: <VAULT_SECRET_NAME>\n    syncerCertificateSecretName: <VAULT_SECRET_NAME>\n    ldapClientCertificateSecretName: <VAULT_SECRET_NAME>\n    cpInternodeEncryptionCertificateSecretName: <VAULT_SECRET_NAME>\n    dpInternodeEncryptionCertificateSecretName: <VAULT_SECRET_NAME>\n    ssoServiceCertificateSecretName: <VAULT_SECRET_NAME>\n    ssoIssuerCertificateSecretName: <VAULT_SECRET_NAME>\n  ldap:\n    bindCredentialsSecretName: <VAULT_SECRET_NAME>\n  sso:\n    saml:\n      idpMetadataSecretName: <VAULT_SECRET_NAME>\n  # Vault configuration\n  clusterCredentialSecretType: vault\n  clusterCredentialSecretRole: redis-enterprise-rec-<K8S_NAMESPACE>\n  vaultCASecret: vault-ca-cert\n  podAnnotations:\n    vault.hashicorp.com/auth-path: auth/<AUTH_PATH>\n    vault.hashicorp.com/namespace: <VAULT_NAMESPACE>","section_id":"redis-enterprise-cluster-secrets"},{"id":"redis-enterprise-cluster-secrets-ex1","language":"bash","code":"kubectl patch rec rec --type merge --patch '{\"spec\": {\"certificates\": {\"apiCertificateSecretName\": \"<VAULT_SECRET_NAME>\"}}}'","section_id":"redis-enterprise-cluster-secrets"},{"id":"database-secrets-ex0","language":"bash","code":"vault kv put -namespace=<VAULT_NAMESPACE> \\\n  <VAULT_SECRET_ROOT>/redisenterprise-<K8S_NAMESPACE>/<DATABASE_NAME> \\\n  password=<DATABASE_PASSWORD>","section_id":"database-secrets"},{"id":"database-secrets-ex1","language":"bash","code":"vault kv put -namespace=<VAULT_NAMESPACE> \\\n  <VAULT_SECRET_ROOT>/redisenterprise-<K8S_NAMESPACE>/<BACKUP_SECRET_NAME> \\\n  AWS_ACCESS_KEY_ID=<access_key> \\\n  AWS_SECRET_ACCESS_KEY=<secret_key>","section_id":"database-secrets"},{"id":"database-secrets-ex2","language":"bash","code":"vault kv put -namespace=<VAULT_NAMESPACE> \\\n  <VAULT_SECRET_ROOT>/redisenterprise-<K8S_NAMESPACE>/<CERT_SECRET_NAME> \\\n  tls.crt=<certificate_content> \\\n  tls.key=<private_key_content>","section_id":"database-secrets"},{"id":"user-defined-module-credentials-ex0","language":"bash","code":"vault kv put -namespace=<VAULT_NAMESPACE> \\\n  <VAULT_SECRET_ROOT>/redisenterprise-<K8S_NAMESPACE>/<MODULE_CREDENTIALS_SECRET_NAME> \\\n  username=<repository_username> \\\n  password=<repository_password>","section_id":"user-defined-module-credentials"},{"id":"common-issues-and-solutions-ex0","language":"bash","code":"# Check if admission-tls secret exists in Vault\n   vault kv get -namespace=<VAULT_NAMESPACE> <VAULT_SECRET_ROOT>/redisenterprise-<K8S_NAMESPACE>/admission-tls","section_id":"common-issues-and-solutions"},{"id":"common-issues-and-solutions-ex1","language":"bash","code":"# Verify vault-ca-cert secret exists\n   kubectl get secret vault-ca-cert -n <K8S_NAMESPACE>\n\n   # Check certificate content\n   kubectl get secret vault-ca-cert -n <K8S_NAMESPACE> -o jsonpath='{.data.vault\\.ca}' | base64 -d","section_id":"common-issues-and-solutions"},{"id":"common-issues-and-solutions-ex2","language":"bash","code":"# Test Vault connectivity from operator pod\n   kubectl exec -it <operator-pod> -c redis-enterprise-operator -- \\\n     curl -k https://<VAULT_FQDN>:8200/v1/sys/health","section_id":"common-issues-and-solutions"},{"id":"common-issues-and-solutions-ex3","language":"bash","code":"vault read -namespace=<VAULT_NAMESPACE> auth/<AUTH_PATH>/role/redis-enterprise-operator-<K8S_NAMESPACE>","section_id":"common-issues-and-solutions"},{"id":"common-issues-and-solutions-ex4","language":"bash","code":"# Verify service account exists\n   kubectl get serviceaccount redis-enterprise-operator -n <K8S_NAMESPACE>\n\n   # Check token mount\n   kubectl describe pod <operator-pod> -n <K8S_NAMESPACE> | grep -A5 \"Mounts:\"","section_id":"common-issues-and-solutions"},{"id":"common-issues-and-solutions-ex5","language":"bash","code":"vault kv get -namespace=<VAULT_NAMESPACE> <VAULT_SECRET_ROOT>/redisenterprise-<K8S_NAMESPACE>/<secret-name>","section_id":"common-issues-and-solutions"},{"id":"common-issues-and-solutions-ex6","language":"bash","code":"vault policy read -namespace=<VAULT_NAMESPACE> redisenterprise-<K8S_NAMESPACE>","section_id":"common-issues-and-solutions"},{"id":"common-issues-and-solutions-ex7","language":"bash","code":"# Cluster credentials must have 'username' and 'password' keys\n   vault kv get -format=json -namespace=<VAULT_NAMESPACE> <VAULT_SECRET_ROOT>/redisenterprise-<K8S_NAMESPACE>/<cluster-name>","section_id":"common-issues-and-solutions"},{"id":"debugging-commands-ex0","language":"bash","code":"kubectl logs -f deployment/redis-enterprise-operator -n <K8S_NAMESPACE> -c redis-enterprise-operator","section_id":"debugging-commands"},{"id":"debugging-commands-ex1","language":"bash","code":"kubectl get configmap operator-environment-config -n <K8S_NAMESPACE> -o yaml","section_id":"debugging-commands"},{"id":"debugging-commands-ex2","language":"bash","code":"# From within operator pod\nkubectl exec -it <operator-pod> -n <K8S_NAMESPACE> -c redis-enterprise-operator -- \\\n  cat /var/run/secrets/kubernetes.io/serviceaccount/token","section_id":"debugging-commands"}]}
{"schema_version":2,"id":"operate/kubernetes/upgrade","title":"Upgrade Redis Enterprise for Kubernetes","url":"https://redis.io/docs/latest/operate/kubernetes/upgrade/","summary":"Upgrade Redis Enterprise operator, clusters, and databases on Kubernetes.","tags":["docs","operate","kubernetes"],"last_updated":"2026-09-28T16:14:52+01:00","children":[{"id":"operate/kubernetes/upgrade/upgrade-redis-cluster","summary":"This task describes how to upgrade a Redis Enterprise cluster via the operator.","title":"Upgrade Redis Enterprise for Kubernetes","url":"https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster/"},{"id":"operate/kubernetes/upgrade/openshift-cli","summary":"This task describes how to upgrade a Redis Enterprise cluster via OpenShift CLI.","title":"Upgrade Redis Enterprise with OpenShift CLI","url":"https://redis.io/docs/latest/operate/kubernetes/upgrade/openshift-cli/"},{"id":"operate/kubernetes/upgrade/upgrade-olm","summary":"This task describes how to upgrade a Redis Enterprise cluster via OpenShift OperatorHub.","title":"Upgrade Redis Enterprise with OpenShift OperatorHub","url":"https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-olm/"}],"page_type":"content","content_hash":"d7bb836d3cab317fc05f53d35351ee3c5a166637ae1534c5ec1f12c97a72104c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Keep your Redis Enterprise deployment up to date with the latest features, security patches, and bug fixes. The upgrade process involves updating three main components in sequence: the Redis Enterprise operator, Redis Enterprise clusters (REC), and Redis Enterprise databases (REDB)."},{"id":"upgrade-methods","title":"Upgrade methods","role":"content","text":"Choose the appropriate upgrade method for your deployment:\n\n- [Upgrade Redis Enterprise for Kubernetes](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster) - Standard upgrade process for most Kubernetes distributions\n- [Upgrade with OpenShift CLI](https://redis.io/docs/latest/operate/kubernetes/upgrade/openshift-cli) - OpenShift-specific upgrade using CLI tools\n- [Upgrade with OpenShift OperatorHub](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-olm) - Upgrade using OpenShift OperatorHub and OLM\n- [Upgrade with Helm](https://redis.io/docs/latest/operate/kubernetes/deployment/helm#upgrade-the-chart) - Helm-specific upgrade instructions for chart-based deployments"},{"id":"upgrade-process","title":"Upgrade process","role":"content","text":"The upgrade process includes updating three components in order:\n\n1. **Upgrade the Redis Enterprise operator** - Update the operator to the latest version\n2. **Upgrade the Redis Enterprise cluster (REC)** - Update cluster nodes and infrastructure\n3. **Upgrade Redis Enterprise databases (REDB)** - Update database versions and configurations"},{"id":"upgrade-compatibility","title":"Upgrade compatibility","role":"content","text":"When upgrading, both your Kubernetes version and Redis operator version need to be supported at all times.\n\n> [!WARNING]\n> If your current Kubernetes distribution is not [supported](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions), upgrade to a supported distribution before upgrading."},{"id":"rhel9-based-image","title":"RHEL9-based image","role":"content","text":"As of version 7.8.2-6, Redis Enterprise images are based on Red Hat Enterprise Linux 9 (RHEL9). This means upgrades require:\n\n- [Cluster version of 7.4.2-2 or later](https://redis.io/docs/latest/operate/kubernetes/7.4.6/upgrade/).\n- Database version 7.2 or later.\n- RHEL9 compatible binaries for any modules you need.\n\nFor detailed steps, see the relevant upgrade page:\n\n- [OpenShift CLI](https://redis.io/docs/latest/operate/kubernetes/upgrade/openshift-cli)\n- [OpenShift OperatorHub](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-olm)\n- [Kubernetes](https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster)"}],"examples":[]}
{"schema_version":2,"id":"operate/kubernetes/upgrade/openshift-cli","title":"Upgrade Redis Enterprise with OpenShift CLI","url":"https://redis.io/docs/latest/operate/kubernetes/upgrade/openshift-cli/","summary":"This task describes how to upgrade a Redis Enterprise cluster via OpenShift CLI.","tags":["docs","operate","kubernetes"],"last_updated":"2026-10-13T20:31:34+05:30","page_type":"content","content_hash":"460469369a141deda135368e925c62c375665b7c640a5531eb1723d3aeb9f539","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis implements rolling updates for software upgrades in Kubernetes deployments. The upgrade process includes updating three components:\n\n  1. [Upgrade the Redis Enterprise operator](#upgrade-the-operator)\n  1. [Upgrade the Redis Enterprise cluster (REC)](#upgrade-the-redis-enterprise-cluster)\n  1. [Upgrade Redis Enterprise databases (REDB)](#upgrade-databases)"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"The following steps ensure you have the minimum versions of all components necessary to upgrade to 7.8.2-6 or later. **Without these minimum versions, the upgrade will freeze and require manual recovery.**\n\nSee the [troubleshooting](#troubleshooting) section for details on recovering a failed upgrade.\n\n#### Kubernetes version\n\nCheck [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) to make sure your Kubernetes distribution is supported. If not, upgrade your Kubernetes distribution before upgrading the Redis operator.\n\n#### Redis operator version\n\nYour Redis Enterprise clusters must be running version 7.4.2-2 or later before upgrading to 7.8.2-6 or later. See the [7.4 upgrade](https://redis.io/docs/latest/operate/kubernetes/7.4.6/upgrade/openshift-cli/) for detailed steps.\n\n#### Redis database version\n\nYour Redis databases must be running version 7.2 or later before upgrading your cluster version. See [upgrade databases](#upgrade-databases) for detailed steps. You can find your database version in the [REDB `spec.redisVersion` field](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api#spec).\n\n#### User-defined modules\n\nIf your databases use user-defined modules (custom non-bundled modules):\n\n- Set `spec.upgradeSpec.autoUpgradeRedisEnterprise: false` in the REC custom resource before upgrading the operator.\n- Define the user-defined modules in the REC custom resource before upgrading the database.\n- See [Edit `redisEnterpriseImageSpec`](#edit-redisenterpriseimagespec) for more details.\n\nFor more information about user-defined modules, see [User-defined modules](https://redis.io/docs/latest/operate/kubernetes/re-databases/modules#user-defined-modules).\n\n#### RHEL9-compatible modules\n\nUpgrading to Redis operator versions 7.8.2-6 and later  involves migrating your Redis Enterprise nodes to RHEL9 from either Ubuntu 18 or RHEL8. If your databases use modules, you need to manually install modules compatible with RHEL9.\n\nTo see which modules you have installed, run:\n\n[code example]\n\nTo see which modules are currently in use, run:\n\n[code example]\n\nSee [Upgrade modules](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/upgrade-module) for details on how to upgrade modules with the `rladmin` tool.\n\n#### Valid license\n\nUse `kubectl get rec` and verify the `LICENSE STATE` is valid on your REC before you start the upgrade process."},{"id":"upgrade-with-helm-charts","title":"Upgrade with Helm charts","role":"content","text":"If you installed the Redis Enterprise operator using Helm charts on OpenShift, you can upgrade using Helm commands. This method automatically handles the operator upgrade and Custom Resource Definition (CRD) updates.\n\nTo upgrade using Helm on OpenShift:\n\n[code example]\n\nFor example:\n\n[code example]\n\nAfter the Helm upgrade completes, continue with [upgrading the Redis Enterprise cluster](#upgrade-the-redis-enterprise-cluster) and [upgrading databases](#upgrade-databases).\n\nFor detailed Helm upgrade instructions, see [Upgrade the chart](https://redis.io/docs/latest/operate/kubernetes/deployment/helm#upgrade-the-chart)."},{"id":"upgrade-the-operator","title":"Upgrade the operator","role":"content","text":"> [!WARNING]\n> If your databases use user-defined modules, set `spec.upgradeSpec.autoUpgradeRedisEnterprise: false` in the REC custom resource before upgrading the operator."},{"id":"download-the-bundle","title":"Download the bundle","role":"content","text":"Make sure you pull the correct version of the bundle. You can find the version tags\nby checking the [operator releases on GitHub](https://github.com/RedisLabs/redis-enterprise-k8s-docs/releases)\nor by [using the GitHub API](https://docs.github.com/en/rest/reference/repos#releases).\n\nFor OpenShift environments, the name of the bundle is `openshift.bundle.yaml`, and so the `curl` command to run is:\n\n[code example]\n\nIf you need a different release, replace `VERSION` in the above with a specific release tag."},{"id":"apply-the-bundle","title":"Apply the bundle","role":"content","text":"Apply the bundle to deploy the new operator binary. This will also apply any changes in the new release to custom resource definitions, roles, role binding, or operator service accounts.\n\n> [!NOTE]\n> If you are not pulling images from Docker Hub, update the operator image spec to point to your private repository.\n> If you have made changes to the role, role binding, RBAC, or custom resource definition (CRD) in the previous version, merge them with the updated declarations in the new version files.\n\nIf you are using OpenShift, run this instead:\n\n[code example]\n\nAfter running this command, you should see a result similar to this:\n\n[code example]"},{"id":"reapply-webhook","title":"Reapply the admission controller webhook","role":"content","text":"If you have the admission controller enabled, you need to manually reapply the `ValidatingWebhookConfiguration`.\n\n> [!NOTE]\n>  [Versions 6.4.2 and later](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/) uses a new `ValidatingWebhookConfiguration` resource to replace `redb-admission`. To use newer releases, delete the old webhook resource and apply the new file.\n\n1. Delete the existing `ValidatingWebhookConfiguration` on the Kubernetes cluster (named `redb-admission`).\n\n        [code example]\n\n1. Apply the resource from the new file.\n\n        [code example]\n        \n\n1. Verify the `admission-tls` secret exists.\n\n    [code example]\n  \n    The output should look similar to\n  \n    [code example]\n\n1. Save the certificate to a local environment variable.\n\n    [code example]\n\n1. Create a Kubernetes validating webhook, replacing `<namespace>` with the namespace where the REC was installed.\n\n    The `webhook.yaml` template can be found in [redis-enterprise-k8s-docs/admission](https://github.com/RedisLabs/redis-enterprise-k8s-docs/tree/master/admission)\n\n    [code example]\n\n1. Create a patch file for the Kubernetes validating webhook.\n\n    [code example]\n\n1. Patch the webhook with the certificate.\n\n    [code example]"},{"id":"verify-the-operator-is-running","title":"Verify the operator is running","role":"content","text":"You can check your deployment to verify the operator is running in your namespace.\n\n[code example]\n\nYou should see a result similar to this:\n\n[code example]\n\n> [!WARNING]\n> We recommend upgrading the REC as soon as possible after updating the operator. After the operator upgrade completes, the operator suspends the management of the REC and its associated REDBs, until the REC upgrade completes."},{"id":"security-context-constraints","title":"Security context constraints","role":"security","text":"Upgrades to versions 7.22.0-6 and later run in **unprivileged mode** without any additional permissions or capabilities. If you don't specifally require additional capabilities, we recommend you maintain the default unprivileged mode, as its more secure. After upgrading, remove the existing `redis-enterprise-scc-v2` SCC and unbind it from the REC service account.\n\nTo enable automatic resource adjustment, see [Allow automatic resource adjustment > OpenShift upgrades](https://redis.io/docs/latest/operate/kubernetes/security/allow-resource-adjustment#openshift-upgrades)."},{"id":"upgrade-the-redis-enterprise-cluster","title":"Upgrade the Redis Enterprise cluster","role":"content","text":"> [!WARNING]\n> Verify your license is valid before upgrading. Invalid licenses will cause the upgrade to fail.\n>\n> Use `oc get rec` and verify the `LICENSE STATE` is valid on your REC before you start the upgrade process.\n\nThe Redis Enterprise cluster (REC) can be updated automatically or manually. To trigger automatic upgrade of the REC after the operator upgrade completes, set `spec.upgradeSpec.autoUpgradeRedisEnterprise: true` in your REC. If you don't have automatic upgrade enabled, follow the below steps for the manual upgrade.\n\nBefore beginning the upgrade of the Redis Enterprise cluster, check the K8s operator [release notes](https://redis.io/docs/latest/operate/kubernetes/release-notes) to find the Redis Enterprise image tag.\n\nAfter the operator upgrade is complete, you can upgrade Redis Enterprise cluster (REC)."},{"id":"edit-redisenterpriseimagespec","title":"Edit `redisEnterpriseImageSpec`","role":"content","text":"1. Edit the REC custom resource YAML file.\n\n    [code example]\n\n1. Replace the `versionTag:` declaration under `redisEnterpriseImageSpec` with the new version tag.\n\n    [code example]\n\n1. Define any user-defined modules used by databases in the cluster.\n\n    [code example]\n\n  The `name` field must match the `display_name` or `module_name` that appears in the module manifest (for example, \"redisgears\"). This enables the operator to run validation on the user-defined module. If these names don't match, the operator can't run validation on the user-defined module and preventable errors may occur.\n\n  > [!NOTE]\n  > Adding or modifying the `userDefinedModules` list triggers a rolling restart of the Redis Enterprise cluster pods in addition to the rolling upgrade for the version change.\n  >\n\n1. Save the changes to apply."},{"id":"reapply-roles-and-role-bindings","title":"Reapply roles and role bindings","role":"content","text":"If your operator is monitoring multiple namespaces, you'll need to [reapply your role and role bindings](https://redis.io/docs/latest/operate/kubernetes/re-clusters/multi-namespace#create-role-and-role-binding-for-managed-namespaces) for each managed namespace. See [Manage databases in multiple namespaces](https://redis.io/docs/latest/operate/kubernetes/re-clusters/multi-namespace) for more details."},{"id":"monitor-the-upgrade","title":"Monitor the upgrade","role":"content","text":"You can view the state of the REC with `oc get rec`.\n\n  During the upgrade, the state should be `Upgrade`.\n  When the upgrade is complete and the cluster is ready to use, the state will change to `Running`.\n  If the state is `InvalidUpgrade`, there is an error (usually relating to configuration) in the upgrade.\n\n[code example]\n\nTo see the status of the current rolling upgrade, run:\n\n[code example]"},{"id":"upgrade-databases","title":"Upgrade databases","role":"content","text":"After the cluster is upgraded, you can upgrade your databases.\n\nTo upgrade your REDB, specify your new database version in the `spec.redisVersion` field in the REDB or REAADB custom resources. Supported database versions for operator versions include \"7.2\", \"7.4\", \"8.0\", and \"8.2\" (note this value is a string).  \n\nFor Active-Active databases, the `redis.Version` change only needs to be applied on one participating cluster and will automatically propagate to all other participating clusters. All participating clusters must be running operator version 8.0.2-2 or later.\n\nIf your REAADB uses supported modules, keep the existing `moduleList` version numbers unchanged when upgrading `redisVersion`. The database will automatically use the module versions that are bundled with the new Redis version, regardless of what versions are specified in `moduleList`. After the upgrade is complete, you can optionally change the old version numbers from `moduleList`, but this change has no functional impact.\n\n#### Upgrade with user-defined modules\n\nIf a user-defined module is used by any database in the cluster, the module must be defined in the REC custom resource before upgrading the database. See [Add user-defined modules to the REC](https://redis.io/docs/latest/operate/kubernetes/re-databases/modules#add-user-defined-modules-to-the-rec) for detailed instructions.\n\n#### General upgrade notes\n\nNote that if your cluster [`redisUpgradePolicy`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api#spec) or your database [`redisVersion`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api#spec) are set to `major`, you won't be able to upgrade those databases to minor versions. See [Redis upgrade policy](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading#redis-upgrade-policy) for more details."},{"id":"troubleshooting","title":"Troubleshooting","role":"errors","text":"If you start an upgrade without meeting the [prerequisites](#prerequisites), the operator will freeze the upgrade. Check the operator logs for the source of the error. The REDB reconsilliation doesn't work during an upgrade, so you need to apply a manual fix with the Redis Software API (examples below). The updates will also need to be added to the REDB custom resource."},{"id":"invalid-module-version","title":"Invalid module version","role":"content","text":"If the operator logs show an event related to an unsupported module, download the updated module locally, and install it using the `v2/modules` API endpoint.\n\n[code example]\n\nAfter updating the modules with the Redis Software API, update the REDB custom resource to reflect the change."},{"id":"invalid-database-version","title":"Invalid database version","role":"content","text":"If the operator logs show an event related to an incompatible database version, upgrade the database using the Redis Software API.\n\n[code example]\n\nAfter updating the database with the Redis Software API, update the REDB custom resource to reflect the change."}],"examples":[{"id":"prerequisites-ex0","language":"sh","code":"curl -k -u <rec_username>:<rec_password> -X GET https://localhost:9443/v1/modules | jq -r 'map([.module_name, .semantic_version, (.platforms | keys)]) | .[] | .[0] as $name | .[1] as $version | .[2][] | $name + \"-\" + $version + \"-\" + .' | sort","section_id":"prerequisites"},{"id":"prerequisites-ex1","language":"sh","code":"curl -k -u <rec_username>:<rec_password> -X GET https://localhost:9443/v1/bdbs | jq -r '.[].module_list | map(.module_name + \"-\" + .semantic_version) | .[]'","section_id":"prerequisites"},{"id":"upgrade-with-helm-charts-ex0","language":"sh","code":"helm upgrade <release-name> redis/redis-enterprise-operator --version <chart-version> \\\n    --set openshift.mode=true","section_id":"upgrade-with-helm-charts"},{"id":"upgrade-with-helm-charts-ex1","language":"sh","code":"helm upgrade my-redis-enterprise redis/redis-enterprise-operator --version 7.8.2-2 \\\n    --set openshift.mode=true","section_id":"upgrade-with-helm-charts"},{"id":"download-the-bundle-ex0","language":"sh","code":"curl --silent -O https://raw.githubusercontent.com/RedisLabs/redis-enterprise-k8s-docs/$VERSION/openshift.bundle.yaml","section_id":"download-the-bundle"},{"id":"apply-the-bundle-ex0","language":"sh","code":"oc apply -f openshift.bundle.yaml","section_id":"apply-the-bundle"},{"id":"apply-the-bundle-ex1","language":"sh","code":"role.rbac.authorization.k8s.io/redis-enterprise-operator configured\nserviceaccount/redis-enterprise-operator configured\nrolebinding.rbac.authorization.k8s.io/redis-enterprise-operator configured\ncustomresourcedefinition.apiextensions.k8s.io/redisenterpriseclusters.app.redislabs.com configured\ncustomresourcedefinition.apiextensions.k8s.io/redisenterprisedatabases.app.redislabs.com configured\ndeployment.apps/redis-enterprise-operator configured","section_id":"apply-the-bundle"},{"id":"reapply-webhook-ex0","language":"sh","code":"kubectl delete ValidatingWebhookConfiguration redb-admission","section_id":"reapply-webhook"},{"id":"reapply-webhook-ex1","language":"sh","code":"kubectl apply -f deploy/admission/webhook.yaml","section_id":"reapply-webhook"},{"id":"reapply-webhook-ex2","language":"sh","code":"kubectl get secret admission-tls","section_id":"reapply-webhook"},{"id":"reapply-webhook-ex3","language":"sh","code":"NAME            TYPE     DATA   AGE\n    admission-tls   Opaque   2      2m43s","section_id":"reapply-webhook"},{"id":"reapply-webhook-ex4","language":"sh","code":"CERT=`kubectl get secret admission-tls -o jsonpath='{.data.cert}'`","section_id":"reapply-webhook"},{"id":"reapply-webhook-ex5","language":"sh","code":"sed 's/OPERATOR_NAMESPACE/<namespace>/g' webhook.yaml | kubectl create -f -","section_id":"reapply-webhook"},{"id":"reapply-webhook-ex6","language":"sh","code":"cat > modified-webhook.yaml <<EOF\n    webhooks:\n    - name: redisenterprise.admission.redislabs\n      clientConfig:\n       caBundle: $CERT\n    EOF","section_id":"reapply-webhook"},{"id":"reapply-webhook-ex7","language":"sh","code":"kubectl patch ValidatingWebhookConfiguration \\\n        redis-enterprise-admission --patch \"$(cat modified-webhook.yaml)\"","section_id":"reapply-webhook"},{"id":"verify-the-operator-is-running-ex0","language":"sh","code":"oc get deployment/redis-enterprise-operator","section_id":"verify-the-operator-is-running"},{"id":"verify-the-operator-is-running-ex1","language":"sh","code":"NAME                        READY   UP-TO-DATE   AVAILABLE   AGE\nredis-enterprise-operator   1/1     1            1           0m36s","section_id":"verify-the-operator-is-running"},{"id":"edit-redisenterpriseimagespec-ex0","language":"sh","code":"oc edit rec <your-rec.yaml>","section_id":"edit-redisenterpriseimagespec"},{"id":"edit-redisenterpriseimagespec-ex1","language":"YAML","code":"spec:\n      redisEnterpriseImageSpec:\n        imagePullPolicy:  IfNotPresent\n        repository:       redislabs/redis\n        versionTag:       <new-version-tag>","section_id":"edit-redisenterpriseimagespec"},{"id":"edit-redisenterpriseimagespec-ex2","language":"YAML","code":"spec:\n      userDefinedModules:\n        - name: \"custom-module\"\n          source:\n            https:\n              url: \"https://modules.company.com/search-v2.1.zip\"\n              credentialsSecret: \"module-repo-creds\"","section_id":"edit-redisenterpriseimagespec"},{"id":"monitor-the-upgrade-ex0","language":"sh","code":"$ oc get rec\nNAME   NODES   VERSION      STATE     SPEC STATUS   LICENSE STATE   SHARDS LIMIT   LICENSE EXPIRATION DATE   AGE\nrec    3       6.2.10-107   Upgrade   Valid         Valid           4              2022-07-16T13:59:00Z      92m","section_id":"monitor-the-upgrade"},{"id":"monitor-the-upgrade-ex1","language":"sh","code":"oc rollout status sts <REC_name>","section_id":"monitor-the-upgrade"},{"id":"invalid-module-version-ex0","language":"sh","code":"curl -sfk -u <rec_username>:<rec_password> -X POST -F 'module=@<full path to your module>' https://localhost:9443/v2/modules","section_id":"invalid-module-version"},{"id":"invalid-database-version-ex0","language":"sh","code":"curl -sfk -u <rec_username>:<rec_password> -X POST -H \"Content-Type: application/json\" -d '{\"redis_version\": <target redis version>}' https://localhost:9443/v1/bdbs/<BDB UID>/upgrade","section_id":"invalid-database-version"}]}
{"schema_version":2,"id":"operate/kubernetes/upgrade/upgrade-olm","title":"Upgrade Redis Enterprise with OpenShift OperatorHub","url":"https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-olm/","summary":"This task describes how to upgrade a Redis Enterprise cluster via OpenShift OperatorHub.","tags":["docs","operate","kubernetes"],"last_updated":"2026-10-13T20:31:34+05:30","page_type":"content","content_hash":"d76cc254783bb493c488dd3cf3f364171dcc27ab79c6455e036cc80f5639e4c0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis implements rolling updates for software upgrades in Kubernetes deployments. The upgrade process includes updating three components:\n\n  1. [Upgrade the Redis Enterprise operator](#upgrade-the-redis-enterprise-operator)\n  1. [Upgrade the Redis Enterprise cluster (REC)](#upgrade-the-redis-enterprise-cluster)\n  1. [Upgrade Redis Enterprise databases (REDB)](#upgrade-databases)"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"The following steps ensure you have the minimum versions of all components necessary to upgrade. **Without these minimum versions, the upgrade will freeze and require manual recovery.**\n\nSee the [troubleshooting](#troubleshooting) section for details on recovering a failed upgrade.\n\n#### Kubernetes version\n\nCheck [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) to make sure your Kubernetes distribution is supported. If not, upgrade your Kubernetes distribution before upgrading the Redis operator.\n\n#### Redis operator version\n\nYour Redis Enterprise clusters must be running version 7.4.2-2 or later before upgrading to 7.8.2-6. See the [7.4 upgrade](https://redis.io/docs/latest/operate/kubernetes/7.4.6/upgrade/upgrade-olm/) for detailed steps.\n\n#### Redis database version\n\nYour Redis databases must be running version 7.2 or later before upgrading your cluster version. See [upgrade databases](#upgrade-databases) for detailed steps. You can find your database version in the [REDB `spec.redisVersion` field](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api#spec)."},{"id":"user-defined-modules","title":"User-defined modules","role":"content","text":"If your databases use user-defined modules (custom non-bundled modules):\n\n- Set `spec.upgradeSpec.autoUpgradeRedisEnterprise: false` in the REC custom resource before upgrading the operator.\n- Define the user-defined modules in the REC custom resource before upgrading the database.\n- See [Edit `redisEnterpriseImageSpec`](#edit-redisenterpriseimagespec) for more details.\n\nFor more information about user-defined modules, see [User-defined modules](https://redis.io/docs/latest/operate/kubernetes/re-databases/modules#user-defined-modules)."},{"id":"valid-license","title":"Valid license","role":"content","text":"Use `kubectl get rec` and verify the `LICENSE STATE` is valid on your REC before you start the upgrade process."},{"id":"upgrade-the-redis-enterprise-operator","title":"Upgrade the Redis Enterprise operator","role":"content","text":"1. Select the **Redis Enterprise Operator** from the **Operators**>**Installed Operators** page.\n\n2. Select the **Subscription** tab.\n\n3. Verify your **Update approval** is set to \"Manual\".\n\n4. To upgrade to the latest version, set your **Update Channel** to ‘production’. To upgrade to an older version, specify the desired version for your **Update Channel**.\n\n5. Select **Upgrade available** shown under **Upgrade status**.\n\n6. When the \"Review manual InstallPlan\" section appears, select **Preview installPlan** and then **Approve** after reviewing the details. This will start the operator upgrade.\n\nYou can monitor the upgrade from the **Installed Operators** page. A new Redis Enterprise Operator will appear in the list, with the status \"Installing\". OpenShift will delete the old operator, showing the \"Cannot update\" status during deletion."},{"id":"security-context-constraints","title":"Security context constraints","role":"security","text":"Upgrades to versions 7.22.0-6 and later run in **unprivileged mode** without any additional permissions or capabilities. If you don't specifally require additional capabilities, we recommend you maintain the default unprivileged mode, as its more secure. After upgrading, remove the existing `redis-enterprise-scc-v2` SCC and unbind it from the REC service account.\n\nTo enable automatic resource adjustment, see [Allow automatic resource adjustment > OpenShift upgrades](https://redis.io/docs/latest/operate/kubernetes/security/allow-resource-adjustment#openshift-upgrades)."},{"id":"upgrade-the-redis-enterprise-cluster","title":"Upgrade the Redis Enterprise cluster","role":"content","text":"> [!WARNING]\n> Verify your license is valid before upgrading. Invalid licenses will cause the upgrade to fail.\n>\n> Use `oc get rec` and verify the `LICENSE STATE` is valid on your REC before you start the upgrade process.\n\nThe Redis Enterprise cluster (REC) can be updated automatically or manually. To trigger automatic upgrade of the REC after the operator upgrade completes, set `spec.upgradeSpec.autoUpgradeRedisEnterprise: true` in your REC. If you don't have automatic upgrade enabled, follow the below steps for the manual upgrade.\n\nBefore beginning the upgrade of the Redis Enterprise cluster, check the K8s operator [release notes](https://redis.io/docs/latest/operate/kubernetes/release-notes) to find the Redis Enterprise image tag.\n\nAfter the operator upgrade is complete, you can upgrade Redis Enterprise cluster (REC)."},{"id":"edit-redisenterpriseimagespec","title":"Edit `redisEnterpriseImageSpec`","role":"content","text":"1. Edit the REC custom resource YAML file.\n\n    [code example]\n\n1. Replace the `versionTag:` declaration under `redisEnterpriseImageSpec` with the new version tag.\n\n    [code example]\n\n1. Define any user-defined modules used by databases in the cluster.\n\n    [code example]\n\n  The `name` field must match the `display_name` or `module_name` that appears in the module manifest (for example, \"redisgears\"). This enables the operator to run validation on the user-defined module. If these names don't match, the operator can't run validation on the user-defined module and preventable errors may occur.\n\n  > [!NOTE]\n  > Adding or modifying the `userDefinedModules` list triggers a rolling restart of the Redis Enterprise cluster pods in addition to the rolling upgrade for the version change.\n  >\n\n1. Save the changes to apply."},{"id":"reapply-roles-and-role-bindings","title":"Reapply roles and role bindings","role":"content","text":"If your operator is monitoring multiple namespaces, you'll need to [reapply your role and role bindings](https://redis.io/docs/latest/operate/kubernetes/re-clusters/multi-namespace#create-role-and-role-binding-for-managed-namespaces) for each managed namespace. See [Manage databases in multiple namespaces](https://redis.io/docs/latest/operate/kubernetes/re-clusters/multi-namespace) for more details."},{"id":"monitor-the-upgrade","title":"Monitor the upgrade","role":"content","text":"You can view the state of the REC with `oc get rec`.\n\n  During the upgrade, the state should be `Upgrade`.\n  When the upgrade is complete and the cluster is ready to use, the state will change to `Running`.\n  If the state is `InvalidUpgrade`, there is an error (usually relating to configuration) in the upgrade.\n\n[code example]\n\nTo see the status of the current rolling upgrade, run:\n\n[code example]"},{"id":"upgrade-databases","title":"Upgrade databases","role":"content","text":"After the cluster is upgraded, you can upgrade your databases.\n\nTo upgrade your REDB, specify your new database version in the `spec.redisVersion` field in the REDB or REAADB custom resources. Supported database versions for operator versions include \"7.2\", \"7.4\", \"8.0\", and \"8.2\" (note this value is a string).  \n\nFor Active-Active databases, the `redis.Version` change only needs to be applied on one participating cluster and will automatically propagate to all other participating clusters. All participating clusters must be running operator version 8.0.2-2 or later.\n\nIf your REAADB uses supported modules, keep the existing `moduleList` version numbers unchanged when upgrading `redisVersion`. The database will automatically use the module versions that are bundled with the new Redis version, regardless of what versions are specified in `moduleList`. After the upgrade is complete, you can optionally change the old version numbers from `moduleList`, but this change has no functional impact."},{"id":"general-upgrade-notes","title":"General upgrade notes","role":"content","text":"Note that if your cluster [`redisUpgradePolicy`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api#spec) or your database [`redisVersion`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api#spec) are set to `major`, you won't be able to upgrade those databases to minor versions. See [Redis upgrade policy](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading#redis-upgrade-policy) for more details.\n\nThe Redis Enterprise cluster (REC) can be updated automatically or manually. To trigger automatic upgrade of the REC after the operator upgrade completes, set `spec.upgradeSpec.autoUpgradeRedisEnterprise: true` in your REC. If you don't have automatic upgrade enabled, follow the below steps for the manual upgrade."},{"id":"troubleshooting","title":"Troubleshooting","role":"errors","text":"If you start an upgrade without meeting the [prerequisites](#prerequisites), the operator will freeze the upgrade. Check the operator logs for the source of the error. The REDB reconsilliation doesn't work during an upgrade, so you need to apply a manual fix with the Redis Software API (examples below). The updates will also need to be added to the REDB custom resource."},{"id":"invalid-module-version","title":"Invalid module version","role":"content","text":"If the operator logs show an event related to an unsupported module, download the updated module locally, and install it using the `v2/modules` API endpoint.\n\n[code example]\n\nAfter updating the modules with the Redis Software API, update the REDB custom resource to reflect the change."},{"id":"invalid-database-version","title":"Invalid database version","role":"content","text":"If the operator logs show an event related to an incompatible database version, upgrade the database using the Redis Software API.\n\n[code example]\n\nAfter updating the database with the Redis Software API, update the REDB custom resource to reflect the change."}],"examples":[{"id":"edit-redisenterpriseimagespec-ex0","language":"sh","code":"oc edit rec <your-rec.yaml>","section_id":"edit-redisenterpriseimagespec"},{"id":"edit-redisenterpriseimagespec-ex1","language":"YAML","code":"spec:\n      redisEnterpriseImageSpec:\n        imagePullPolicy:  IfNotPresent\n        repository:       redislabs/redis\n        versionTag:       <new-version-tag>","section_id":"edit-redisenterpriseimagespec"},{"id":"edit-redisenterpriseimagespec-ex2","language":"YAML","code":"spec:\n      userDefinedModules:\n        - name: \"custom-module\"\n          source:\n            https:\n              url: \"https://modules.company.com/search-v2.1.zip\"\n              credentialsSecret: \"module-repo-creds\"","section_id":"edit-redisenterpriseimagespec"},{"id":"monitor-the-upgrade-ex0","language":"sh","code":"$ oc get rec\nNAME   NODES   VERSION      STATE     SPEC STATUS   LICENSE STATE   SHARDS LIMIT   LICENSE EXPIRATION DATE   AGE\nrec    3       6.2.10-107   Upgrade   Valid         Valid           4              2022-07-16T13:59:00Z      92m","section_id":"monitor-the-upgrade"},{"id":"monitor-the-upgrade-ex1","language":"sh","code":"oc rollout status sts <REC_name>","section_id":"monitor-the-upgrade"},{"id":"invalid-module-version-ex0","language":"sh","code":"curl -sfk -u <rec_username>:<rec_password> -X POST -F 'module=@<full path to your module>' https://localhost:9443/v2/modules","section_id":"invalid-module-version"},{"id":"invalid-database-version-ex0","language":"sh","code":"curl -sfk -u <rec_username>:<rec_password> -X POST -H \"Content-Type: application/json\" -d '{\"redis_version\": <target redis version>}' https://localhost:9443/v1/bdbs/<BDB UID>/upgrade","section_id":"invalid-database-version"}]}
{"schema_version":2,"id":"operate/kubernetes/upgrade/upgrade-redis-cluster","title":"Upgrade Redis Enterprise for Kubernetes","url":"https://redis.io/docs/latest/operate/kubernetes/upgrade/upgrade-redis-cluster/","summary":"This task describes how to upgrade a Redis Enterprise cluster via the operator.","tags":["docs","operate","kubernetes"],"last_updated":"2026-10-13T20:31:34+05:30","page_type":"content","content_hash":"802f44295d6b8ab969617f4b11916a3dc02fb06794d5a64b88ba2f0c2d023e66","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis implements rolling updates for software upgrades in Kubernetes deployments. The upgrade process includes updating three components:\n\n  1. [Upgrade the Redis Enterprise operator](#upgrade-the-operator)\n  1. [Upgrade the Redis Enterprise cluster (REC)](#upgrade-the-redis-enterprise-cluster-rec)\n  1. [Upgrade Redis Enterprise databases (REDB)](#upgrade-databases)"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before upgrading, ensure you have the minimum versions of all components necessary for your target version. **Without these minimum versions, the upgrade may freeze and require manual recovery.**\n\nSee the [troubleshooting](#troubleshooting) section for details on recovering a failed upgrade."},{"id":"kubernetes-version","title":"Kubernetes version","role":"content","text":"Check [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) to make sure your Kubernetes distribution is supported by your target Redis Enterprise operator version. If not, upgrade your Kubernetes distribution before upgrading the Redis operator."},{"id":"redis-operator-version","title":"Redis operator version","role":"content","text":"Check the [Redis Enterprise for Kubernetes release notes](https://redis.io/docs/latest/operate/kubernetes/release-notes) for the minimum operator version required for your target upgrade. Some versions may require intermediate upgrades."},{"id":"redis-database-version","title":"Redis database version","role":"content","text":"Check the release notes for your target version to determine the minimum Redis database version required. See [upgrade databases](#upgrade-databases) for detailed steps. You can find your database version in the [REDB `spec.redisVersion` field](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api#spec)."},{"id":"user-defined-modules","title":"User-defined modules","role":"content","text":"If your databases use user-defined modules (custom non-bundled modules):\n\n- Set `spec.upgradeSpec.autoUpgradeRedisEnterprise: false` in the REC custom resource before upgrading the operator.\n- Define the user-defined modules in the REC custom resource before upgrading the database.\n- See [Edit `redisEnterpriseImageSpec`](#edit-redisenterpriseimagespec-in-the-rec-spec) for more details.\n\nFor more information about user-defined modules, see [User-defined modules](https://redis.io/docs/latest/operate/kubernetes/re-databases/modules#user-defined-modules)."},{"id":"valid-license","title":"Valid license","role":"content","text":"Use `kubectl get rec` and verify the `LICENSE STATE` is valid on your REC before you start the upgrade process."},{"id":"upgrade-with-helm-charts","title":"Upgrade with Helm charts","role":"content","text":"If you installed the Redis Enterprise operator using Helm charts, you can upgrade using Helm commands. This method automatically handles the operator upgrade and Custom Resource Definition (CRD) updates.\n\nTo upgrade using Helm:\n\n[code example]\n\nFor example:\n\n[code example]\n\nFor OpenShift installations, include the `openshift.mode=true` parameter:\n\n[code example]\n\nAfter the Helm upgrade completes, continue with [upgrading the Redis Enterprise cluster (REC)](#upgrade-the-redis-enterprise-cluster-rec) and [upgrading databases](#upgrade-databases).\n\nFor detailed Helm upgrade instructions, see [Upgrade the chart](https://redis.io/docs/latest/operate/kubernetes/deployment/helm#upgrade-the-chart)."},{"id":"upgrade-the-operator","title":"Upgrade the operator","role":"content","text":"> [!WARNING]\n> If your databases use user-defined modules, set `spec.upgradeSpec.autoUpgradeRedisEnterprise: false` in the REC custom resource before upgrading the operator."},{"id":"download-the-bundle","title":"Download the bundle","role":"content","text":"Make sure you pull the correct version of the bundle. You can find the version tags\nby checking the [operator releases on GitHub](https://github.com/RedisLabs/redis-enterprise-k8s-docs/releases)\nor by [using the GitHub API](https://docs.github.com/en/rest/reference/repos#releases).\n\nYou can download the bundle for the latest release with the following `curl` command:\n\n[code example]\n\nIf you need a different release, replace `VERSION` in the above with a specific release tag."},{"id":"apply-the-bundle","title":"Apply the bundle","role":"content","text":"Apply the bundle to deploy the new operator binary. This will also apply any changes in the new release to custom resource definitions, roles, role binding, or operator service accounts.\n\n> [!NOTE]\n> If you are not pulling images from Docker Hub, update the operator image spec to point to your private repository.\n> If you have made changes to the role, role binding, RBAC, or custom resource definition (CRD) in the previous version, merge them with the updated declarations in the new version files.\n\nUpgrade the bundle and operator with a single command, passing in the bundle YAML file:\n\n[code example]\n\nAfter running this command, you should see a result similar to this:\n\n[code example]"},{"id":"reapply-webhook","title":"Reapply the admission controller webhook","role":"content","text":"If you have the admission controller enabled, you need to manually reapply the `ValidatingWebhookConfiguration`.\n\n> [!NOTE]\n>  [Versions 6.4.2 and later](https://redis.io/docs/latest/operate/kubernetes/release-notes/previous-releases/) uses a new `ValidatingWebhookConfiguration` resource to replace `redb-admission`. To use newer releases, delete the old webhook resource and apply the new file.\n\n1. Delete the existing `ValidatingWebhookConfiguration` on the Kubernetes cluster (named `redb-admission`).\n\n        [code example]\n\n1. Apply the resource from the new file.\n\n        [code example]\n        \n\n1. Verify the `admission-tls` secret exists.\n\n    [code example]\n  \n    The output should look similar to\n  \n    [code example]\n\n1. Save the certificate to a local environment variable.\n\n    [code example]\n\n1. Create a Kubernetes validating webhook, replacing `<namespace>` with the namespace where the REC was installed.\n\n    The `webhook.yaml` template can be found in [redis-enterprise-k8s-docs/admission](https://github.com/RedisLabs/redis-enterprise-k8s-docs/tree/master/admission)\n\n    [code example]\n\n1. Create a patch file for the Kubernetes validating webhook.\n\n    [code example]\n\n1. Patch the webhook with the certificate.\n\n    [code example]"},{"id":"verify-the-operator-is-running","title":"Verify the operator is running","role":"content","text":"You can check your deployment to verify the operator is running in your namespace.\n\n[code example]\n\nYou should see a result similar to this:\n\n[code example]\n\n> [!WARNING]\n> We recommend upgrading the REC as soon as possible after updating the operator. After the operator upgrade completes, the operator suspends the management of the REC and its associated REDBs, until the REC upgrade completes."},{"id":"upgrade-the-redis-enterprise-cluster-rec","title":"Upgrade the Redis Enterprise cluster (REC)","role":"content","text":"The Redis Enterprise cluster (REC) can be updated automatically or manually. To trigger automatic upgrade of the REC after the operator upgrade completes, set `spec.upgradeSpec.autoUpgradeRedisEnterprise: true` in your REC. If you don't have automatic upgrade enabled, follow the below steps for the manual upgrade.\n\n> [!WARNING]\n> Automatic upgrade is incompatible with image digest hashes. If you set a `digestHash` in any image spec (`redisEnterpriseImageSpec`, `redisEnterpriseServicesRiggerImageSpec`, `bootstrapperImageSpec`, or `usageMeter.callHomeClient.imageSpec`) while `autoUpgradeRedisEnterprise` is `true`, the operator invalidates the REC spec and publishes a Kubernetes warning event. Remove all `digestHash` values before you enable automatic upgrade, or upgrade the cluster [manually](#edit-redisenterpriseimagespec-in-the-rec-spec) instead.\n\nBefore beginning the upgrade of the Redis Enterprise cluster, check the [Redis Enterprise for Kubernetes release notes](https://redis.io/docs/latest/operate/kubernetes/release-notes) to find the Redis Enterprise image tag for your target version.\n\nAfter the operator upgrade is complete, you can upgrade Redis Enterprise cluster (REC).\n\n> [!NOTE]\n> For Active-Active databases, we recommend upgrading all participating clusters to the same operator version."},{"id":"edit-redisenterpriseimagespec-in-the-rec-spec","title":"Edit `redisEnterpriseImageSpec` in the REC spec","role":"content","text":"1. Edit the REC custom resource YAML file.\n\n    [code example]\n\n1. Replace the `versionTag:` declaration under `redisEnterpriseImageSpec` with the new version tag.\n\n    [code example]\n\n1. Define any user-defined modules used by databases in the cluster.\n\n    [code example]\n\n  The `name` field must match the `display_name` or `module_name` that appears in the module manifest (for example, \"redisgears\"). This enables the operator to run validation on the user-defined module. If these names don't match, the operator can't run validation on the user-defined module and preventable errors may occur.\n\n  > [!NOTE]\n  > Adding or modifying the `userDefinedModules` list triggers a rolling restart of the Redis Enterprise cluster pods in addition to the rolling upgrade for the version change.\n  >\n\n1. Save the changes to apply."},{"id":"reapply-roles-and-role-bindings","title":"Reapply roles and role bindings","role":"content","text":"If your operator is monitoring multiple namespaces, you'll need to [reapply your role and role bindings](https://redis.io/docs/latest/operate/kubernetes/re-clusters/multi-namespace#create-role-and-role-binding-for-managed-namespaces) for each managed namespace. See [Manage databases in multiple namespaces](https://redis.io/docs/latest/operate/kubernetes/re-clusters/multi-namespace) for more details."},{"id":"monitor-the-upgrade","title":"Monitor the upgrade","role":"content","text":"You can view the state of the REC with `kubectl get rec`.\n\n  During the upgrade, the state should be `Upgrade`.\n  When the upgrade is complete and the cluster is ready to use, the state will change to `Running`.\n  If the state is `InvalidUpgrade`, there is an error (usually relating to configuration) in the upgrade.\n\n[code example]\n\nTo see the status of the current rolling upgrade, run:\n\n[code example]"},{"id":"upgrade-databases","title":"Upgrade databases","role":"content","text":"After the cluster is upgraded, you can upgrade your databases."},{"id":"upgrade-redb","title":"Upgrade REDB","role":"content","text":"To upgrade your REDB, specify your new database version in the `spec.redisVersion` field in the REDB or REAADB custom resources. Supported database versions for operator versions include \"7.2\", \"7.4\", \"8.0\", and \"8.2\" (note this value is a string)."},{"id":"upgrade-reaadb","title":"Upgrade REAADB","role":"content","text":"For Active-Active databases, the `redis.Version` change only needs to be applied on one participating cluster and will automatically propagate to all other participating clusters. All participating clusters must be running operator version 8.0.2-2 or later.\n\nIf your REAADB uses supported modules, keep the existing `moduleList` version numbers unchanged when upgrading `redisVersion`. The database will automatically use the module versions that are bundled with the new Redis version, regardless of what versions are specified in `moduleList`. After the upgrade is complete, you can optionally change the old version numbers from `moduleList`, but this change has no functional impact."},{"id":"upgrade-with-user-defined-modules","title":"Upgrade with user-defined modules","role":"content","text":"If a user-defined module is used by any database in the cluster, the module must be defined in the REC custom resource before upgrading the database. See [Add user-defined modules to the REC](https://redis.io/docs/latest/operate/kubernetes/re-databases/modules#add-user-defined-modules-to-the-rec) for detailed instructions."},{"id":"upgrade-policy","title":"Upgrade policy","role":"content","text":"Note that if your cluster [`redisUpgradePolicy`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_cluster_api#spec) or your database [`redisVersion`](https://redis.io/docs/latest/operate/kubernetes/reference/api/redis_enterprise_database_api#spec) are set to `major`, you won't be able to upgrade those databases to minor versions. See [Redis upgrade policy](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading#redis-upgrade-policy) for more details."},{"id":"troubleshooting","title":"Troubleshooting","role":"errors","text":"If you start an upgrade without meeting the [prerequisites](#prerequisites), the operator will freeze the upgrade. Check the operator logs for the source of the error. The REDB reconsilliation doesn't work during an upgrade, so you need to apply a manual fix with the Redis Software API (examples below). The updates will also need to be added to the REDB custom resource."},{"id":"user-defined-modules-1","title":"User-defined modules","role":"content","text":"If your databases use user-defined modules and you encounter upgrade issues:\n\n1. **Verify `autoUpgradeRedisEnterprise` is set to `false`**: Check the REC spec to ensure automatic upgrades are disabled.\n\n    [code example]\n\n1. **Verify modules are defined in the REC**: Ensure all user-defined modules are listed in the REC `userDefinedModules` section before upgrading.\n\n    [code example]\n\n1. **Check module validation errors**: Review the REC status for module validation errors.\n\n    [code example]\n\nFor more information about user-defined modules, see [User-defined modules](https://redis.io/docs/latest/operate/kubernetes/re-databases/modules#user-defined-modules)."},{"id":"invalid-module-version","title":"Invalid module version","role":"content","text":"If the operator logs show an event related to an unsupported module, download the updated module locally, and install it using the `v2/modules` API endpoint.\n\n[code example]\n\nAfter updating the database with the Redis Software API, update the REDB custom resource to reflect the change."},{"id":"invalid-database-version","title":"Invalid database version","role":"content","text":"If the operator logs show an event related to an incompatible database version, upgrade the database using the Redis Software API.\n\n[code example]\n\nAfter updating the modules with the Redis Software API, update the REDB custom resource to reflect the change."},{"id":"auto-upgrade-blocked-by-image-digest-hashes","title":"Auto-upgrade blocked by image digest hashes","role":"content","text":"If `autoUpgradeRedisEnterprise` is `true` and any image spec sets a `digestHash`, the operator invalidates the REC spec and blocks the upgrade until you remove the digest hashes. The REC enters the `InvalidUpgrade` state and the operator publishes a Kubernetes warning event describing the problem.\n\n1. **Check the REC events**: Review the event published on the REC.\n\n    [code example]\n\n1. **Remove the digest hashes**: Delete the `digestHash` field from every image spec that sets it (`redisEnterpriseImageSpec`, `redisEnterpriseServicesRiggerImageSpec`, `bootstrapperImageSpec`, and `usageMeter.callHomeClient.imageSpec`). Once the spec no longer uses digest hashes, the operator revalidates it and the upgrade proceeds.\n\nAlternatively, set `spec.upgradeSpec.autoUpgradeRedisEnterprise: false` and [upgrade the cluster manually](#edit-redisenterpriseimagespec-in-the-rec-spec), which supports digest hashes."}],"examples":[{"id":"upgrade-with-helm-charts-ex0","language":"sh","code":"helm upgrade <release-name> redis/redis-enterprise-operator --version <chart-version>","section_id":"upgrade-with-helm-charts"},{"id":"upgrade-with-helm-charts-ex1","language":"sh","code":"helm upgrade my-redis-enterprise redis/redis-enterprise-operator --version <chart-version>","section_id":"upgrade-with-helm-charts"},{"id":"upgrade-with-helm-charts-ex2","language":"sh","code":"helm upgrade <release-name> redis/redis-enterprise-operator --version <chart-version> \\\n    --set openshift.mode=true","section_id":"upgrade-with-helm-charts"},{"id":"download-the-bundle-ex0","language":"sh","code":"VERSION=`curl --silent https://api.github.com/repos/RedisLabs/redis-enterprise-k8s-docs/releases/latest | grep tag_name | awk -F'\"' '{print $4}'`\ncurl --silent -O https://raw.githubusercontent.com/RedisLabs/redis-enterprise-k8s-docs/$VERSION/bundle.yaml","section_id":"download-the-bundle"},{"id":"apply-the-bundle-ex0","language":"sh","code":"kubectl apply -f bundle.yaml","section_id":"apply-the-bundle"},{"id":"apply-the-bundle-ex1","language":"sh","code":"role.rbac.authorization.k8s.io/redis-enterprise-operator configured\nserviceaccount/redis-enterprise-operator configured\nrolebinding.rbac.authorization.k8s.io/redis-enterprise-operator configured\ncustomresourcedefinition.apiextensions.k8s.io/redisenterpriseclusters.app.redislabs.com configured\ncustomresourcedefinition.apiextensions.k8s.io/redisenterprisedatabases.app.redislabs.com configured\ndeployment.apps/redis-enterprise-operator configured","section_id":"apply-the-bundle"},{"id":"reapply-webhook-ex0","language":"sh","code":"kubectl delete ValidatingWebhookConfiguration redb-admission","section_id":"reapply-webhook"},{"id":"reapply-webhook-ex1","language":"sh","code":"kubectl apply -f deploy/admission/webhook.yaml","section_id":"reapply-webhook"},{"id":"reapply-webhook-ex2","language":"sh","code":"kubectl get secret admission-tls","section_id":"reapply-webhook"},{"id":"reapply-webhook-ex3","language":"sh","code":"NAME            TYPE     DATA   AGE\n    admission-tls   Opaque   2      2m43s","section_id":"reapply-webhook"},{"id":"reapply-webhook-ex4","language":"sh","code":"CERT=`kubectl get secret admission-tls -o jsonpath='{.data.cert}'`","section_id":"reapply-webhook"},{"id":"reapply-webhook-ex5","language":"sh","code":"sed 's/OPERATOR_NAMESPACE/<namespace>/g' webhook.yaml | kubectl create -f -","section_id":"reapply-webhook"},{"id":"reapply-webhook-ex6","language":"sh","code":"cat > modified-webhook.yaml <<EOF\n    webhooks:\n    - name: redisenterprise.admission.redislabs\n      clientConfig:\n       caBundle: $CERT\n    EOF","section_id":"reapply-webhook"},{"id":"reapply-webhook-ex7","language":"sh","code":"kubectl patch ValidatingWebhookConfiguration \\\n        redis-enterprise-admission --patch \"$(cat modified-webhook.yaml)\"","section_id":"reapply-webhook"},{"id":"verify-the-operator-is-running-ex0","language":"sh","code":"kubectl get deployment/redis-enterprise-operator","section_id":"verify-the-operator-is-running"},{"id":"verify-the-operator-is-running-ex1","language":"sh","code":"NAME                        READY   UP-TO-DATE   AVAILABLE   AGE\nredis-enterprise-operator   1/1     1            1           0m36s","section_id":"verify-the-operator-is-running"},{"id":"edit-redisenterpriseimagespec-in-the-rec-spec-ex0","language":"sh","code":"kubectl edit rec <your-rec.yaml>","section_id":"edit-redisenterpriseimagespec-in-the-rec-spec"},{"id":"edit-redisenterpriseimagespec-in-the-rec-spec-ex1","language":"YAML","code":"spec:\n      redisEnterpriseImageSpec:\n        imagePullPolicy:  IfNotPresent\n        repository:       redislabs/redis\n        versionTag:       <new-version-tag>","section_id":"edit-redisenterpriseimagespec-in-the-rec-spec"},{"id":"edit-redisenterpriseimagespec-in-the-rec-spec-ex2","language":"YAML","code":"spec:\n      userDefinedModules:\n        - name: \"custom-module\"\n          source:\n            https:\n              url: \"https://modules.company.com/search-v2.1.zip\"\n              credentialsSecret: \"module-repo-creds\"","section_id":"edit-redisenterpriseimagespec-in-the-rec-spec"},{"id":"monitor-the-upgrade-ex0","language":"sh","code":"$ kubectl get rec\nNAME   NODES   VERSION      STATE     SPEC STATUS   LICENSE STATE   SHARDS LIMIT   LICENSE EXPIRATION DATE   AGE\nrec    3       8.0.2-17   Upgrade   Valid         Valid           4              2022-07-16T13:59:00Z      92m","section_id":"monitor-the-upgrade"},{"id":"monitor-the-upgrade-ex1","language":"sh","code":"kubectl rollout status sts <REC_name>","section_id":"monitor-the-upgrade"},{"id":"user-defined-modules-1-ex0","language":"sh","code":"kubectl get rec <cluster-name> -o jsonpath='{.spec.upgradeSpec.autoUpgradeRedisEnterprise}'","section_id":"user-defined-modules-1"},{"id":"user-defined-modules-1-ex1","language":"sh","code":"kubectl get rec <cluster-name> -o jsonpath='{.spec.userDefinedModules}' | jq","section_id":"user-defined-modules-1"},{"id":"user-defined-modules-1-ex2","language":"sh","code":"kubectl describe rec <cluster-name>","section_id":"user-defined-modules-1"},{"id":"invalid-module-version-ex0","language":"sh","code":"curl -sfk -u <rec_username>:<rec_password> -X POST -F 'module=@<full path to your module>' https://localhost:9443/v2/modules","section_id":"invalid-module-version"},{"id":"invalid-database-version-ex0","language":"sh","code":"curl -sfk -u <rec_username>:<rec_password> -X POST -H \"Content-Type: application/json\" -d '{\"redis_version\": <target redis version>}' https://localhost:9443/v1/bdbs/<BDB UID>/upgrade","section_id":"invalid-database-version"},{"id":"auto-upgrade-blocked-by-image-digest-hashes-ex0","language":"sh","code":"kubectl describe rec <cluster-name>","section_id":"auto-upgrade-blocked-by-image-digest-hashes"}]}
{"schema_version":2,"id":"operate/oss_and_stack","title":"Redis Open Source","url":"https://redis.io/docs/latest/operate/oss_and_stack/","summary":"Operate Redis Open Source. Redis 8 in Redis Open Source replaces Redis Stack.","aliases":["/stack/"],"tags":["docs","operate","stack","oss"],"last_updated":"2026-09-28T17:31:37-07:00","children":[{"id":"operate/oss_and_stack/install","summary":"How to install Redis Open Source on your target platform","title":"Install Redis Open Source","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/"},{"id":"operate/oss_and_stack/management","summary":"An administrator's guide to Redis","title":"Manage Redis","url":"https://redis.io/docs/latest/operate/oss_and_stack/management/"},{"id":"operate/oss_and_stack/stack-with-enterprise","summary":"","title":"Redis Open Source and Redis Software","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/"},{"id":"operate/oss_and_stack/reference","summary":"Redis Open Source reference documentation","title":"Reference","url":"https://redis.io/docs/latest/operate/oss_and_stack/reference/"}],"page_type":"content","content_hash":"582243b213d4762ad4dc556012ac16d93e965c8910d2816098be2043a86aa18a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Since 2009, the Redis open source project has inspired an enthusiastic and active community of users and contributors. We continue to be committed to fostering an open, welcoming, diverse, inclusive, and healthy community."},{"id":"original-developer-and-core-team","title":"Original developer and core team","role":"content","text":"[Salvatore Sanfilippo](https://github.com/antirez) built, led, and maintained the Redis open source project. In 2020, Salvatore Sanfilippo named two successors to take over and lead the Redis project: Yossi Gottlieb [yossigo](https://github.com/yossigo) and Oran Agra [oranagra](https://github.com/oranagra). Salvatore [returned to Redis in 2024](https://antirez.com/news/144)."},{"id":"code-of-conduct","title":"Code of Conduct","role":"content","text":"Redis has adopted the [Contributor Covenant Code of Conduct](https://github.com/redis/redis/blob/unstable/CODE_OF_CONDUCT.md)."},{"id":"getting-help","title":"Getting help","role":"content","text":"On the [Redis Discord server](https://discord.gg/redis), you can chat with members of the Redis community in real time. You'll meet Redis users, contributors, and developer advocates. This is a great place to stop in for quick questions or to share your latest Redis discoveries."},{"id":"stack-overflow","title":"Stack Overflow","role":"content","text":"Have a question about Redis? Search the [Stack Overflow Redis tag](https://stackoverflow.com/questions/tagged/redis) for answers, or post a question of your own."},{"id":"redis-news","title":"Redis news","role":"content","text":"For occasional updates on the new Redis releases, you can [check out the What’s New section](https://redis.io/docs/latest/develop/whats-new/).\n\nTo keep up with the latest from Redis Inc., including news on Redis Cloud and Redis Stack, consider [following the Redis Twitter feed](https://twitter.com/redisinc)."},{"id":"conferences-and-meetups","title":"Conferences and meetups","role":"content","text":"Redis regularly sponsors conferences, webinars, and meetups. Check out the [Redis events](https://redis.io/events/) page for details."},{"id":"contributing-to-redis","title":"Contributing to Redis","role":"content","text":"The Redis core source repository is hosted under [https://github.com/redis/redis](https://github.com/redis/redis) along with many of the client libraries. Commits to the Redis source repository will require code review, approval of at least one reviewer who is not the author of the commit, and no objections.\n\nThere are many ways to contribute to Redis, starting with documentation all the way to changes to the open source Redis server. Here are a few ways you can get involved."},{"id":"contributing-to-docs","title":"Contributing to docs","role":"content","text":"The [Redis docs](https://github.com/redis/docs) are open source, and we'd love to incorporate your contributions. For small changes and typos, we recommend working in a fork and creating a [pull request](https://github.com/redis/docs/pulls). Your first PR will prompt you to sign the contributor license agreement. Approved PRs will be published with the next batch of changes to the docs site."},{"id":"reporting-bugs","title":"Reporting bugs","role":"content","text":"To report a bug in Redis, create a [Redis Github issue](https://github.com/redis/redis/issues).\n\nFor larger changes, we ask that you first create an issue describing your proposed changes. This is a good way to get feedback in advance to increase the likelihood that your changes will be accepted."},{"id":"client-libraries","title":"Client libraries","role":"content","text":"The Redis [client libraries](https://redis.io/docs/latest/develop/clients) are nearly always open source and accepting of contributions. Consult the contribution guidelines for the library you're interested in."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/install/archive","title":"Install Redis Community Edition or Redis Stack","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/archive/","summary":"How to install your preferred Redis flavor on your target platform","tags":["docs","operate","stack","oss"],"last_updated":"2026-09-21T15:59:08+01:00","children":[{"id":"operate/oss_and_stack/install/archive/install-redis","summary":"Install Redis on Linux, macOS, and Windows","title":"Install Redis","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-redis/"},{"id":"operate/oss_and_stack/install/archive/install-stack","summary":"Install Redis Stack on Linux, macOS, and Windows","title":"Install Redis Stack","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-stack/"}],"page_type":"content","content_hash":"d5315365003081b1337207d628ae98e89aaae605ee800b54e4d08d80da055a3e","sections":[{"id":"content","title":"Content","role":"content","text":"You can install [Redis](https://redis.io/about/) or [Redis Stack](https://redis.io/about/about-stack/) locally on your machine. Redis and Redis Stack are available on Linux, macOS, and Windows.\n\nHere are the installation instructions:\n\n* [Install Redis](https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-redis)\n* [Install Redis Stack](https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-stack)\n\nWhile you can install Redis or Redis Stack locally, you might also consider using Redis Cloud by creating a [free account](https://redis.com/try-free/?utm_source=redisio&utm_medium=referral&utm_campaign=2023-09-try_free&utm_content=cu-redis_cloud_users)."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/install/archive/install-redis","title":"Install Redis","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-redis/","summary":"Install Redis on Linux, macOS, and Windows","aliases":["/operate/oss_and_stack/install/install-redis/","/install/install-redis/"],"tags":["docs","operate","stack","oss"],"last_updated":"2026-09-30T13:16:11-07:00","children":[{"id":"operate/oss_and_stack/install/archive/install-redis/install-redis-on-linux","summary":"How to install Redis on Linux","title":"Install Redis on Linux","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-redis/install-redis-on-linux/"},{"id":"operate/oss_and_stack/install/archive/install-redis/install-redis-on-mac-os","summary":"Use Homebrew to install and start Redis on macOS","title":"Install Redis on macOS","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-redis/install-redis-on-mac-os/"},{"id":"operate/oss_and_stack/install/archive/install-redis/install-redis-on-windows","summary":"Use Redis on Windows through Memurai, official Redis partner for Windows compatibility","title":"Install Redis on Windows","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-redis/install-redis-on-windows/"},{"id":"operate/oss_and_stack/install/archive/install-redis/install-redis-from-source","summary":"Compile and install Redis from source","title":"Install Redis from Source","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-redis/install-redis-from-source/"}],"page_type":"content","content_hash":"d043128df6cd2b2536a1cf44b41c97ff2dba3acba1b52e47bb1d7e36dbea12ed","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a an installation guide. You'll learn how to install, run, and experiment with the Redis server process.\n\nWhile you can install Redis on any of the platforms listed below, you might also consider using Redis Cloud by creating a [free account](https://redis.com/try-free?utm_source=redisio&utm_medium=referral&utm_campaign=2023-09-try_free&utm_content=cu-redis_cloud_users). You can also download [Redis Insight](https://redis.io/docs/latest/operate/redisinsight/install), a free developer tool that works with Redis."},{"id":"install-redis-open-source","title":"Install Redis Open Source","role":"setup","text":"How you install Redis depends on your operating system. See the guide below that best fits your needs:\n\n* [Install Redis on Linux](https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-redis/install-redis-on-linux)\n* [Install Redis on macOS](https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-redis/install-redis-on-mac-os)\n* [Install Redis on Windows](https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-redis/install-redis-on-windows)\n\nRefer to [Redis Administration](https://redis.io/docs/latest/operate/oss_and_stack/management/admin) for detailed setup tips."},{"id":"test-if-you-can-connect-using-the-cli","title":"Test if you can connect using the CLI","role":"content","text":"After you have Redis up and running, you can connect using `redis-cli`.\n\nExternal programs talk to Redis using a TCP socket and a Redis specific protocol. This protocol is implemented in the Redis client libraries for the different programming languages. However, to make hacking with Redis simpler, Redis provides a command line utility that can be used to send commands to Redis. This program is called **redis-cli**.\n\nThe first thing to do to check if Redis is working properly is sending a **PING** command using redis-cli:\n\n[code example]\n\nRunning **redis-cli** followed by a command name and its arguments will send this command to the Redis instance running on localhost at port 6379. You can change the host and port used by `redis-cli` - just try the `--help` option to check the usage information.\n\nAnother interesting way to run `redis-cli` is without arguments: the program will start in interactive mode. You can type different commands and see their replies.\n\n[code example]"},{"id":"securing-redis","title":"Securing Redis","role":"content","text":"By default Redis binds to **all the interfaces** and has no authentication at all. If you use Redis in a very controlled environment, separated from the external internet and in general from attackers, that's fine. However, if an unhardened Redis is exposed to the internet, it is a big security concern. If you are not 100% sure your environment is secured properly, please check the following steps in order to make Redis more secure:\n\n1. Make sure the port Redis uses to listen for connections (by default 6379 and additionally 16379 if you run Redis in cluster mode, plus 26379 for Sentinel) is firewalled, so that it is not possible to contact Redis from the outside world.\n2. Use a configuration file where the `bind` directive is set in order to guarantee that Redis listens on only the network interfaces you are using. For example, only the loopback interface (127.0.0.1) if you are accessing Redis locally from the same computer.\n3. Use the `requirepass` option to add an additional layer of security so that clients will be required to authenticate using the [`AUTH`](https://redis.io/docs/latest/commands/auth) command.\n4. Use [spiped](http://www.tarsnap.com/spiped.html) or another SSL tunneling software to encrypt traffic between Redis servers and Redis clients if your environment requires encryption.\n\nNote that a Redis instance exposed to the internet without any security [is very simple to exploit](http://antirez.com/news/96), so make sure you understand the above and apply **at least** a firewall layer. After the firewall is in place, try to connect with `redis-cli` from an external host to confirm that the instance is not reachable."},{"id":"use-redis-from-your-application","title":"Use Redis from your application","role":"content","text":"Of course using Redis just from the command line interface is not enough as the goal is to use it from your application. To do so, you need to download and install a Redis client library for your programming language.\n\nYou'll find a [full list of supported clients for different languages in this page](https://redis.io/docs/latest/develop/clients)."},{"id":"redis-persistence","title":"Redis persistence","role":"content","text":"You can learn [how Redis persistence works on this page](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence). It is important to understand that, if you start Redis with the default configuration, Redis will spontaneously save the dataset only from time to time. For example, after at least five minutes if you have at least 100 changes in your data. If you want your database to persist and be reloaded after a restart make sure to call the **SAVE** command manually every time you want to force a data set snapshot. Alternatively, you can save the data on disk before quitting by using the **SHUTDOWN** command:\n\n[code example]\n\nThis way, Redis will save the data on disk before quitting. Reading the [persistence page](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence) is strongly suggested to better understand how Redis persistence works."},{"id":"install-redis-properly","title":"Install Redis properly","role":"setup","text":"Running Redis from the command line is fine just to hack a bit or for development. However, at some point you'll have some actual application to run on a real server. For this kind of usage you have two different choices:\n\n* Run Redis using screen.\n* Install Redis in your Linux box in a proper way using an init script, so that after a restart everything will start again properly.\n\nA proper install using an init script is strongly recommended. \n\n> [!NOTE]\n> The available packages for supported Linux distributions already include the capability of starting the Redis server from `/etc/init`.\n\n> [!NOTE]\n> The remainder of this section assumes you've [installed Redis from its source code](https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-redis/install-redis-from-source).\n\nThe following instructions can be used to perform a proper installation using the init script shipped with the Redis source code, `/path/to/redis-stable/utils/redis_init_script`.\n\nIf you have not yet run `make install` after building the Redis source, you will need to do so before continuing. By default, `make install` will copy the `redis-server` and `redis-cli` binaries to `/usr/local/bin`.\n\n* Create a directory in which to store your Redis config files and your data:\n\n    [code example]\n\n* Copy the init script that you'll find in the Redis distribution under the **utils** directory into `/etc/init.d`. We suggest calling it with the name of the port where you are running this instance of Redis. Make sure the resulting file has `0755` permissions.\n    \n    [code example]\n\n* Edit the init script.\n\n    [code example]\n\nMake sure to set the **REDISPORT** variable to the port you are using.\nBoth the pid file path and the configuration file name depend on the port number.\n\n* Copy the template configuration file you'll find in the root directory of the Redis distribution into `/etc/redis/` using the port number as the name, for instance:\n\n    [code example]\n\n* Create a directory inside `/var/redis` that will work as both data and working directory for this Redis instance:\n\n    [code example]\n\n* Edit the configuration file, making sure to perform the following changes:\n    * Set **daemonize** to yes (by default it is set to no).\n    * Set the **pidfile** to `/var/run/redis_6379.pid`, modifying the port as necessary.\n    * Change the **port** accordingly. In our example it is not needed as the default port is already `6379`.\n    * Set your preferred **loglevel**.\n    * Set the **logfile** to `/var/log/redis_6379.log`.\n    * Set the **dir** to `/var/redis/6379` (very important step!).\n* Finally, add the new Redis init script to all the default runlevels using the following command:\n\n    [code example]\n\nYou are done! Now you can try running your instance with:\n\n[code example]\n\nMake sure that everything is working as expected:\n\n1. Try pinging your instance within a `redis-cli` session using the [`PING`](https://redis.io/docs/latest/commands/ping) command.\n2. Do a test save with `redis-cli save` and check that a dump file is correctly saved to `/var/redis/6379/dump.rdb`.\n3. Check that your Redis instance is logging to the `/var/log/redis_6379.log` file.\n4. If it's a new machine where you can try it without problems, make sure that after a reboot everything is still working.\n\n> [!NOTE]\n> The above instructions don't include all of the Redis configuration parameters that you could change. For example, to use AOF persistence instead of RDB persistence, or to set up replication, and so forth. Further details can be found in the [configuration article on this site](https://redis.io/docs/latest/operate/oss_and_stack/management/config).\n\n<hr>"}],"examples":[{"id":"test-if-you-can-connect-using-the-cli-ex0","language":"plaintext","code":"$ redis-cli ping\nPONG","section_id":"test-if-you-can-connect-using-the-cli"},{"id":"test-if-you-can-connect-using-the-cli-ex1","language":"plaintext","code":"$ redis-cli\nredis 127.0.0.1:6379> ping\nPONG","section_id":"test-if-you-can-connect-using-the-cli"},{"id":"redis-persistence-ex0","language":"plaintext","code":"$ redis-cli shutdown","section_id":"redis-persistence"},{"id":"install-redis-properly-ex0","language":"plaintext","code":"sudo mkdir /etc/redis\n    sudo mkdir /var/redis","section_id":"install-redis-properly"},{"id":"install-redis-properly-ex1","language":"plaintext","code":"sudo cp utils/redis_init_script /etc/init.d/redis_6379","section_id":"install-redis-properly"},{"id":"install-redis-properly-ex2","language":"plaintext","code":"sudo vi /etc/init.d/redis_6379","section_id":"install-redis-properly"},{"id":"install-redis-properly-ex3","language":"plaintext","code":"sudo cp redis.conf /etc/redis/6379.conf","section_id":"install-redis-properly"},{"id":"install-redis-properly-ex4","language":"plaintext","code":"sudo mkdir /var/redis/6379","section_id":"install-redis-properly"},{"id":"install-redis-properly-ex5","language":"plaintext","code":"sudo update-rc.d redis_6379 defaults","section_id":"install-redis-properly"},{"id":"install-redis-properly-ex6","language":"plaintext","code":"sudo /etc/init.d/redis_6379 start","section_id":"install-redis-properly"}]}
{"schema_version":2,"id":"operate/oss_and_stack/install/archive/install-redis/install-redis-from-source","title":"Install Redis from Source","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-redis/install-redis-from-source/","summary":"Compile and install Redis from source","aliases":["/operate/oss_and_stack/install/install-redis/install-redis-from-source"],"tags":["docs","operate","stack","oss"],"last_updated":"2026-09-21T15:59:08+01:00","page_type":"content","content_hash":"87ecd819b037fc19a5b29e384db80523f29da1a1635ef8e050a9d5e2f8e853e4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can compile and install Redis from source on variety of platforms and operating systems including Linux and macOS. Redis has no dependencies other than a C  compiler and `libc`."},{"id":"downloading-the-source-files","title":"Downloading the source files","role":"content","text":"The Redis source files are available from the [Download](https://redis.io/downloads) page. You can verify the integrity of these downloads by checking them against the digests in the [redis-hashes git repository](https://github.com/redis/redis-hashes).\n\nTo obtain the source files for the latest stable version of Redis from the Redis downloads site, run:\n\n\nwget https://download.redis.io/redis-stable.tar.gz"},{"id":"compiling-redis","title":"Compiling Redis","role":"content","text":"To compile Redis, first extract the tarball, change to the root directory, and then run `make`:\n\n\ntar -xzvf redis-stable.tar.gz\ncd redis-stable\nmake\n\n\nTo build with TLS support, you'll need to install OpenSSL development libraries (e.g., libssl-dev on Debian/Ubuntu) and then run:\n\n\nmake BUILD_TLS=yes\n\n\nIf the compile succeeds, you'll find several Redis binaries in the `src` directory, including:\n\n* **redis-server**: the Redis Server itself\n* **redis-cli** is the command line interface utility to talk with Redis.\n\nTo install these binaries in `/usr/local/bin`, run:\n\n\nsudo make install"},{"id":"starting-and-stopping-redis-in-the-foreground","title":"Starting and stopping Redis in the foreground","role":"content","text":"Once installed, you can start Redis by running\n\n\nredis-server\n\n\nIf successful, you'll see the startup logs for Redis, and Redis will be running in the foreground.\n\nTo stop Redis, enter `Ctrl-C`.\n\nFor a more complete installation, continue with [these instructions](https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-redis#install-redis-properly)."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/install/archive/install-redis/install-redis-on-linux","title":"Install Redis on Linux","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-redis/install-redis-on-linux/","summary":"How to install Redis on Linux","aliases":["/operate/oss_and_stack/install/install-redis/install-redis-on-linux","/getting-started/installation/install-redis-on-linux/"],"tags":["docs","operate","stack","oss"],"last_updated":"2026-09-30T13:16:11-07:00","page_type":"content","content_hash":"4f69449c92f6bfc81e6e965c85ea5a44f2d74830dfcbc416a2e2a343ef5a4e26","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Most major Linux distributions provide packages for Redis."},{"id":"install-on-ubuntudebian","title":"Install on Ubuntu/Debian","role":"setup","text":"Add the repository to the APT index, update it, and install Redis:\n\n\nsudo apt-get install lsb-release curl gpg\ncurl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg\nsudo chmod 644 /usr/share/keyrings/redis-archive-keyring.gpg\necho \"deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main\" | sudo tee /etc/apt/sources.list.d/redis.list\nsudo apt-get update\nsudo apt-get install redis\n\n\nRedis will start automatically, and it should restart at boot time. If Redis doesn't start across reboots, you may need to manually enable it:\n\n\nsudo systemctl enable redis-server\nsudo systemctl start redis-server"},{"id":"install-on-red-hatrocky","title":"Install on Red Hat/Rocky","role":"setup","text":"sudo yum install redis\nsudo systemctl enable redis\nsudo systemctl start redis\n\n\nRedis will restart at boot time."},{"id":"install-on-ubuntu-using-snap","title":"Install on Ubuntu using Snap","role":"setup","text":"To install via Snap, run:\n\n\nsudo apt update\nsudo apt install redis-tools # for redis-cli\nsudo snap install redis\n\n\nRedis will start automatically, but it won't restart at boot time. To do this, run:\n\n\nsudo snap set redis service.start=true\n\n\nYou can use these additional snap-related commands to start, stop, restart, and check the status of Redis:\n\n* `sudo snap start redis`\n* `sudo snap stop redis`\n* `sudo snap restart redis`\n* `sudo snap services redis`\n\nIf your Linux distribution does not currently have Snap installed, you can install it using the instructions described  [here](https://snapcraft.io/docs/installing-snapd). Then, consult the [Snapcraft store](https://snapcraft.io/redis) for instructions on installing Redis using Snap for your distribution."},{"id":"starting-and-stopping-redis-in-the-background","title":"Starting and stopping Redis in the background","role":"content","text":"You can start the Redis server as a background process using the `systemctl` command. This only applies to Ubuntu/Debian when installed using `apt`, and Red Hat/Rocky when installed using `yum`.\n\n\nsudo systemctl start <redis-service-name> # redis or redis-server depending on platform\n\n\nTo stop the server, use:\n\n\nsudo systemctl stop <redis-service-name> # redis or redis-server depending on platform"},{"id":"connect-to-redis","title":"Connect to Redis","role":"content","text":"Once Redis is running, you can test it by running `redis-cli`:\n\n\nredis-cli\n\n\nTest the connection with the `ping` command:\n\n\n127.0.0.1:6379> ping\nPONG\n\n\nYou can also test that your Redis server is running using\n[Redis Insight](https://redis.io/docs/latest/develop/tools/insight)."},{"id":"next-steps","title":"Next steps","role":"content","text":"Once you have a running Redis instance, you may want to:\n\n* Try the [Redis CLI tutorial](https://redis.io/docs/latest/develop/tools/cli)\n* Connect using one of the [Redis clients](https://redis.io/docs/latest/develop/clients)\n* [Install Redis \"properly\"](https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-redis#install-redis-properly)\n  for production use."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/install/archive/install-redis/install-redis-on-mac-os","title":"Install Redis on macOS","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-redis/install-redis-on-mac-os/","summary":"Use Homebrew to install and start Redis on macOS","aliases":["/operate/oss_and_stack/install/install-redis/install-redis-on-mac-os","/getting-started/installation/install-redis-on-mac-os/"],"tags":["docs","operate","stack","oss"],"last_updated":"2026-09-30T13:16:11-07:00","page_type":"content","content_hash":"456630d6bd5c3f22b7f9606caea57897f6ca173b50ef859adcfefecdc1129b9f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This guide shows you how to install Redis on macOS using Homebrew. Homebrew is the easiest way to install Redis on macOS. If you'd prefer to build Redis from the source files on macOS, see [Installing Redis from Source](https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-redis/install-redis-from-source).\n\n> [!NOTE]\n> The Homebrew distribution of Redis Open Source is only supported on macOS."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"First, make sure you have Homebrew installed. From the terminal, run:\n\n\nbrew --version\n\n\nIf this command fails, you'll need to [follow the Homebrew installation instructions](https://brew.sh/)."},{"id":"installation","title":"Installation","role":"setup","text":"From the terminal, run:\n\n\nbrew install redis\n\n\nThis will install Redis on your system."},{"id":"starting-and-stopping-redis-in-the-foreground","title":"Starting and stopping Redis in the foreground","role":"content","text":"To test your Redis installation, you can run the `redis-server` executable from the command line:\n\n\nredis-server\n\n\nIf successful, you'll see the startup logs for Redis, and Redis will be running in the foreground.\n\nTo stop Redis, enter `Ctrl-C`."},{"id":"starting-and-stopping-redis-using-launchd","title":"Starting and stopping Redis using launchd","role":"content","text":"As an alternative to running Redis in the foreground, you can also use `launchd` to start the process in the background:\n\n\nbrew services start redis\n\n\nThis launches Redis and restarts it at login. You can check the status of a `launchd` managed Redis by running the following:\n\n\nbrew services info redis\n\n\nIf the service is running, you'll see output like the following:\n\n\nredis (homebrew.mxcl.redis)\nRunning: ✔\nLoaded: ✔\nUser: miranda\nPID: 67975\n\n\nTo stop the service, run:\n\n\nbrew services stop redis"},{"id":"connect-to-redis","title":"Connect to Redis","role":"content","text":"Once Redis is running, you can test it by running `redis-cli`:\n\n\nredis-cli\n\n\nTest the connection with the `ping` command:\n\n\n127.0.0.1:6379> ping\nPONG\n\n\nYou can also test that your Redis server is running using\n[Redis Insight](https://redis.io/docs/latest/develop/tools/insight)."},{"id":"next-steps","title":"Next steps","role":"content","text":"Once you have a running Redis instance, you may want to:\n\n* Try the [Redis CLI tutorial](https://redis.io/docs/latest/develop/tools/cli)\n* Connect using one of the [Redis clients](https://redis.io/docs/latest/develop/clients)\n* [Install Redis \"properly\"](https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-redis#install-redis-properly)\n  for production use."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/install/archive/install-redis/install-redis-on-windows","title":"Install Redis on Windows","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-redis/install-redis-on-windows/","summary":"Use Redis on Windows through Memurai, official Redis partner for Windows compatibility","aliases":["/operate/oss_and_stack/install/install-redis/install-redis-on-windows"],"tags":["docs","operate","stack","oss"],"last_updated":"2026-09-21T15:59:08+01:00","page_type":"content","content_hash":"058d330907c8fcb389de708088035c0af1e0fc4cf7cefa47c1888648ee31a5de","sections":[{"id":"run-redis-on-windows-using-memurai","title":"Run Redis on Windows using Memurai","role":"overview","text":"Redis is now natively supported on Windows through [Memurai](https://www.memurai.com/), the official Redis partner for Windows compatibility."},{"id":"run-redis-on-windows-using-wsl-windows-subsystem-for-linux","title":"Run Redis on Windows using WSL (Windows Subsystem for Linux)","role":"content","text":"To install Redis on Windows using WSL, you'll first need to enable [WSL2](https://docs.microsoft.com/en-us/windows/wsl/install) (Windows Subsystem for Linux). WSL2 lets you run Linux binaries natively on Windows. For this method to work, you'll need to be running Windows 10 version 2004 and higher or Windows 11."},{"id":"install-or-enable-wsl2","title":"Install or enable WSL2","role":"setup","text":"Microsoft provides [detailed instructions for installing WSL](https://docs.microsoft.com/en-us/windows/wsl/install). Follow these instructions, and take note of the default Linux distribution it installs. This guide assumes Ubuntu."},{"id":"install-redis","title":"Install Redis","role":"setup","text":"Once you're running Ubuntu on Windows, you can follow the steps detailed at [Install on Ubuntu/Debian](https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-redis/install-redis-on-linux#install-on-ubuntu-debian) to install recent stable versions of Redis from the official `packages.redis.io` APT repository.\nAdd the repository to the <code>apt</code> index, update it, and then install:\n\n\ncurl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg\n\necho \"deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main\" | sudo tee /etc/apt/sources.list.d/redis.list\n\nsudo apt-get update\nsudo apt-get install redis\n\n\nLastly, start the Redis server like so:\n\n\nredis-server --daemonize yes"},{"id":"connect-to-redis","title":"Connect to Redis","role":"content","text":"Once Redis is running, you can test it by running `redis-cli`:\n\n\nredis-cli\n\n\nTest the connection with the `ping` command:\n\n\n127.0.0.1:6379> ping\nPONG\n\n\nYou can also test that your Redis server is running using\n[Redis Insight](https://redis.io/docs/latest/develop/tools/insight)."},{"id":"next-steps","title":"Next steps","role":"content","text":"Once you have a running Redis instance, you may want to:\n\n* Try the [Redis CLI tutorial](https://redis.io/docs/latest/develop/tools/cli)\n* Connect using one of the [Redis clients](https://redis.io/docs/latest/develop/clients)\n* [Install Redis \"properly\"](https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-redis#install-redis-properly)\n  for production use."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/install/archive/install-stack/binaries","title":"Install Redis Stack from binaries","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-stack/binaries/","summary":"How to install Redis Stack using tarballs","tags":["docs","operate","stack","oss"],"last_updated":"2025-04-07T11:41:07-07:00","page_type":"content","content_hash":"25047b1d77e1e391e8c63a28d62e8bb0fe951b02abd1d8a91cd52f3caf31e5de","sections":[{"id":"start-redis-stack-server","title":"Start Redis Stack Server","role":"overview","text":"Install the openssl libraries for your platform. For example, on a Debian or Ubuntu instance run:\n\n\nsudo apt install libssl-dev\n\n\nAfter untarring or unzipping your redis-stack-server download, you can start Redis Stack Server as follows:\n\n\n/path/to/redis-stack-server/bin/redis-stack-server"},{"id":"add-the-binaries-to-your-path","title":"Add the binaries to your PATH","role":"content","text":"You can add the redis-stack-server binaries to your `$PATH` as follows:\n\nOpen the file `~/.bashrc` or `~/zshrc` (depending on your shell), and add the following lines.\n\n\nexport PATH=/path/to/redis-stack-server/bin:$PATH\n\n\nIf you have an existing Redis installation on your system, then you can choose override those override those PATH variables as before, or you can choose to only add redis-stack-server binary as follows:\n\n\nexport PATH=/path/to/redis-stack-server/bin/redis-stack-server:$PATH\n\n\nIf you're running redis-stack-server on a mac, please ensure you have openssl installed, via [homebrew](https://brew.sh/).\n\nNow you can start Redis Stack Server as follows:\n\n\nredis-stack-server"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/install/archive/install-stack/docker","title":"Run Redis Stack on Docker","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-stack/docker/","summary":"How to install Redis Stack using Docker","tags":["docs","operate","stack","oss"],"last_updated":"2026-09-21T15:59:08+01:00","page_type":"content","content_hash":"987fb76f1af5e89ce3034a7560a47a4e145d37e4807ad9a2611f6a90b9173e0c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"To get started with Redis Stack using Docker, you first need to select a Docker image:\n\n* `redis/redis-stack` contains both Redis Stack server and Redis Insight. This container is best for local development because you can use the embedded Redis Insight to visualize your data.\n\n* `redis/redis-stack-server` provides Redis Stack server only. This container is best for production deployment."},{"id":"getting-started","title":"Getting started","role":"setup","text":""},{"id":"redisredis-stack-server","title":"redis/redis-stack-server","role":"content","text":"To start Redis Stack server using the `redis-stack-server` image, run the following command in your terminal:\n\n\ndocker run -d --name redis-stack-server -p 6379:6379 redis/redis-stack-server:latest"},{"id":"redisredis-stack","title":"redis/redis-stack","role":"content","text":"To start a Redis Stack container using the `redis-stack` image, run the following command in your terminal:\n\n\ndocker run -d --name redis-stack -p 6379:6379 -p 8001:8001 redis/redis-stack:latest\n\n\nThe `docker run` command above also exposes Redis Insight on port 8001. You can use Redis Insight by pointing your browser to `localhost:8001`."},{"id":"connect-with-redis-cli","title":"Connect with redis-cli","role":"content","text":"You can then connect to the server using `redis-cli`, just as you connect to any Redis instance.\n\nIf you don’t have `redis-cli` installed locally, you can run it from the Docker container:\n\n\n$ docker exec -it redis-stack redis-cli"},{"id":"configuration","title":"Configuration","role":"configuration","text":""},{"id":"persistence-in-docker","title":"Persistence in Docker","role":"content","text":"To mount directories or files to your Docker container, specify `-v` to configure a local volume. This command stores all data in the local directory `local-data`:\n\n$ docker run -v /local-data/:/data redis/redis-stack:latest"},{"id":"ports","title":"Ports","role":"content","text":"If you want to expose Redis Stack server or Redis Insight on a different port, update the left hand of portion of the `-p` argument. This command exposes Redis Stack server on port `10001` and Redis Insight on port `13333`:\n\n$ docker run -p 10001:6379 -p 13333:8001 redis/redis-stack:latest"},{"id":"config-files","title":"Config files","role":"content","text":"By default, the Redis Stack Docker containers use internal configuration files for Redis. To start Redis with local configuration file, you can use the `-v` volume options:\n\n\n$ docker run -v `pwd`/local-redis-stack.conf:/redis-stack.conf -p 6379:6379 -p 8001:8001 redis/redis-stack:latest"},{"id":"environment-variables","title":"Environment variables","role":"content","text":"To pass in arbitrary configuration changes, you can set any of these environment variables:\n\n* `REDIS_ARGS`: extra arguments for Redis\n\n* `REDISEARCH_ARGS`: arguments for the search and query features (RediSearch)\n\n* `REDISJSON_ARGS`: arguments for JSON (RedisJSON)\n\n* `REDISTIMESERIES_ARGS`: arguments for time series (RedisTimeSeries)\n\n* `REDISBLOOM_ARGS`: arguments for  the probabilistic data structures (RedisBloom)\n\n\nFor example, here's how to use the `REDIS_ARGS` environment variable to pass the `requirepass` directive to Redis:\n\n\ndocker run -e REDIS_ARGS=\"--requirepass redis-stack\" redis/redis-stack:latest\n\n\nAn example of setting [Redis persistence](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence):\n\ndocker run -e REDIS_ARGS=\"--save 60 1000 --appendonly yes\" redis/redis-stack:latest\n\n\nHere's how to set a retention policy for time series:\n\ndocker run -e REDISTIMESERIES_ARGS=\"RETENTION_POLICY=20\" redis/redis-stack:latest"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/install/archive/install-stack","title":"Install Redis Stack","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-stack/","summary":"Install Redis Stack on Linux, macOS, and Windows","tags":["docs","operate","stack","oss"],"last_updated":"2025-04-07T11:41:07-07:00","children":[{"id":"operate/oss_and_stack/install/archive/install-stack/linux","summary":"How to install Redis Stack on Linux","title":"Install Redis Stack on Linux","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-stack/linux/"},{"id":"operate/oss_and_stack/install/archive/install-stack/mac-os","summary":"How to install Redis Stack on macOS","title":"Install Redis Stack on macOS","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-stack/mac-os/"},{"id":"operate/oss_and_stack/install/archive/install-stack/windows","summary":"How to install Redis Stack on Windows","title":"Install Redis Stack on Windows","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-stack/windows/"},{"id":"operate/oss_and_stack/install/archive/install-stack/docker","summary":"How to install Redis Stack using Docker","title":"Run Redis Stack on Docker","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-stack/docker/"},{"id":"operate/oss_and_stack/install/archive/install-stack/binaries","summary":"How to install Redis Stack using tarballs","title":"Install Redis Stack from binaries","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-stack/binaries/"}],"page_type":"content","content_hash":"e335d166c54e95970e7bfaf58619288a7b39d6aaa84bbbe312e2fe42060bb7f2","sections":[{"id":"content","title":"Content","role":"content","text":"While you can install Redis Stack on any of the platforms listed below, you might also consider using Redis Cloud by creating a [free account](https://redis.com/try-free/?utm_source=redisio&utm_medium=referral&utm_campaign=2023-09-try_free&utm_content=cu-redis_cloud_users).\n\n<hr/>"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/install/archive/install-stack/linux","title":"Install Redis Stack on Linux","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-stack/linux/","summary":"How to install Redis Stack on Linux","tags":["docs","operate","stack","oss"],"last_updated":"2026-09-21T15:59:08+01:00","page_type":"content","content_hash":"241bab09e485bfc6b3887fa243d3fc86413f71cf205a29a83a084e7b91ec0c6c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Learn how to install Redis Stack on Linux from the official APT repository or RPM feed, or with Snap or AppImage."},{"id":"from-the-official-ubuntudebian-apt-repository","title":"From the official Ubuntu/Debian APT Repository","role":"content","text":"See [this page](https://redis.io/downloads/#redis-stack-downloads) for a complete list of supported Ubuntu/Debian platforms.\nAdd the repository to the APT index, update it, and install Redis Stack:\n\n\nsudo apt-get install lsb-release curl gpg\ncurl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg\nsudo chmod 644 /usr/share/keyrings/redis-archive-keyring.gpg\necho \"deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main\" | sudo tee /etc/apt/sources.list.d/redis.list\nsudo apt-get update\nsudo apt-get install redis-stack-server\n\n\nRedis will not start automatically, nor will it start at boot time. To do this, run the following commands.\n\n\nsudo systemctl enable redis-stack-server\nsudo systemctl start redis-stack-server"},{"id":"from-the-official-red-hatrocky-rpm-feeds","title":"From the official Red Hat/Rocky RPM Feeds","role":"content","text":"See [this page](https://redis.io/downloads/#redis-stack-downloads) for a complete list of supported Red Hat/Rocky platforms.\nFollow these steps to install Redis Stack.\n\n1. Create the file `/etc/yum.repos.d/redis.repo` with the following contents.\n\n    \n    [Redis]\n    name=Redis\n    baseurl=http://packages.redis.io/rpm/rhel9 # replace rhel9 with the appropriate value for your platform\n    enabled=1\n    gpgcheck=1\n    \n\n1. Run the following commands:\n\n    \n    curl -fsSL https://packages.redis.io/gpg > /tmp/redis.key\n    sudo rpm --import /tmp/redis.key\n    sudo yum install epel-release\n    sudo yum install redis-stack-server\n    \n\nRedis will not start automatically, nor will it start at boot time. To do this, run the following commands.\n\n\nsudo systemctl enable redis-stack-server\nsudo systemctl start redis-stack-server"},{"id":"on-ubuntu-with-snap","title":"On Ubuntu with Snap","role":"content","text":"First, download the latest Redis Stack snap package from [this page](https://redis.io/downloads/).\n\nTo install, run:\n\n\nsudo apt update\nsudo apt install redis-tools\nsudo snap install --dangerous --classic <snapname.snap>\n\n\nRedis will not start automatically, nor will it start at boot time. To start `redis-stack-server` in the foreground, run the command:\n\n\nsudo snap run redis-stack-server\n\n\nTo stop Redis, enter `Ctrl-C`.\n\nFollow these steps to integrate Redis Stack with `systemd` so you can start/stop in/from the background:\n\n1. Edit the `/etc/systemd/system/redis-stack-server.service` file and enter the following information:\n\n    \n    [Unit]\n    Description=Redis Stack Server\n    After=network.target\n\n    [Service]\n    ExecStart=/usr/bin/snap run redis-stack-server\n    Restart=always\n    User=root\n    Group=root\n\n    [Install]\n    WantedBy=multi-user.target\n    \n\n1. Run the following commands.\n\n    \n    sudo systemctl daemon-reload\n    sudo systemctl start redis-stack-server\n    sudo systemctl enable redis-stack-server\n    \n\nIf your Linux distribution does not currently have Snap installed, you can install it using the instructions described  [here](https://snapcraft.io/docs/installing-snapd). Then, download the appropriate from the [downloads page](https://redis.io/downloads/)."},{"id":"on-ubuntu-with-appimage","title":"On Ubuntu with AppImage","role":"content","text":"Fuse needs to be installed before proceeding. Install it as follows.\n\n\nsudo apt update\nsudo apt install fuse\n\n\nNext, download the latest Redis Stack AppImage package from [this page](https://redis.io/downloads/).\n\nTo run the image, execute these commands:\n\n\nsudo apt update\nsudo apt install redis-tools\nchmod a+x <AppImageFile> # replace AppImageFile with the name of your downloaded file\n./<AppImageFile>\n\n\nThis will start Redis in the foreground. To stop Redis, enter `Ctrl-C`.\n\nFollow these steps to integrate Redis Stack with `systemd` so you can start/stop in/from the background:\n\n1. Edit the `/etc/systemd/system/redis-appimage.service` file and enter the following information:\n\n    \n    [Unit]\n    Description=Redis Server (AppImage)\n    After=network.target\n\n    [Service]\n    ExecStart=/path/to/your/<AppImageFile> --daemonize no\n    Restart=always\n    User=redis-user   # replace with an existing user or create a new one\n    Group=redis-group # replace with an existing group or create a new one\n\n    [Install]\n    WantedBy=multi-user.target\n    \n1. Run the following commands.\n\n    \n    sudo systemctl daemon-reload\n    sudo systemctl start redis-appimage\n    sudo systemctl enable redis-appimage"},{"id":"starting-and-stopping-redis-stack-in-the-background","title":"Starting and stopping Redis Stack in the background","role":"content","text":"You can start the Redis server as a background process using the `systemctl` command. This only applies to Ubuntu/Debian when installed using `apt`, and Red Hat/Rocky when installed using `yum`.\n\n\nsudo systemctl start redis-stack-server\n\n\nTo stop the service, use:\n\n\nsudo systemctl stop redis-stack-server"},{"id":"connect-to-redis","title":"Connect to Redis","role":"content","text":"Once Redis is running, you can test it by running `redis-cli`:\n\n\nredis-cli\n\n\nTest the connection with the `ping` command:\n\n\n127.0.0.1:6379> ping\nPONG\n\n\nYou can also test that your Redis server is running using\n[Redis Insight](https://redis.io/docs/latest/develop/tools/insight)."},{"id":"next-steps","title":"Next steps","role":"content","text":"Once you have a running Redis instance, you may want to:\n\n* Try the [Redis CLI tutorial](https://redis.io/docs/latest/develop/tools/cli)\n* Connect using one of the [Redis clients](https://redis.io/docs/latest/develop/clients)\n* [Install Redis properly](https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-redis#install-redis-properly)\n  for production use."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/install/archive/install-stack/mac-os","title":"Install Redis Stack on macOS","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-stack/mac-os/","summary":"How to install Redis Stack on macOS","aliases":["/operate/oss_and_stack/install/install-stack/mac-os/"],"tags":["docs","operate","stack","oss"],"last_updated":"2026-09-21T15:59:08+01:00","page_type":"content","content_hash":"41f9cb4c2dac01a02671807cc4bd748450c462308e50ce4e5cdbc3eed523c836","sections":[{"id":"overview","title":"Overview","role":"overview","text":"To install Redis Stack on macOS, use [Homebrew](https://brew.sh/). Make sure that you have [Homebrew installed](https://docs.brew.sh/Installation) before starting on the installation instructions below.\n\nThere are three brew casks available.\n\n* `redis-stack` contains both `redis-stack-server` and `redis-stack-redisinsight` casks. \n* `redis-stack-server` provides Redis Stack server only.\n* `redis-stack-redisinsight` contains Redis Insight."},{"id":"install-using-homebrew","title":"Install using Homebrew","role":"setup","text":"First, tap the Redis Stack Homebrew tap:\n\n\nbrew tap redis-stack/redis-stack\n\n\nNext, run `brew install`:\n\n\nbrew install redis-stack\n\n\nThe `redis-stack-server` cask will install all Redis and Redis Stack binaries. How you run these binaries depends on whether you already have Redis installed on your system."},{"id":"first-time-redis-installation","title":"First-time Redis installation","role":"content","text":"If this is the first time you've installed Redis on your system, you need to be sure that your `PATH` variable includes the Redis Stack installation location. This location is either `/opt/homebrew/bin` for Apple silicon Macs or `/usr/local/bin` for Intel-based Macs.\n\nTo check this, run:\n\n\necho $PATH\n\n\nThen, confirm that the output contains `/opt/homebrew/bin` (Apple silicon Macs) or `/usr/local/bin` (Intel Mac). If these directories are not in the output, see the \"Existing Redis installation\" instructions below.\n\n> [!NOTE]\n> Because Redis Stack is installed using a brew cask via the `brew tap` command, it will not be integrated with the `brew services` command."},{"id":"existing-redis-installation","title":"Existing Redis installation","role":"content","text":"If you have an existing Redis installation on your system, then might want to modify your `$PATH` to ensure that you're using the latest Redis Stack binaries.\n\nOpen the file `~/.bashrc` or `~/zshrc` (depending on your shell), and add the following lines.\n\nFor Intel-based Macs:\n\n\nexport PATH=/usr/local/Caskroom/redis-stack-server/<VERSION>/bin:$PATH\n\n\nFor Apple silicon Macs:\n\n\nexport PATH=/opt/homebrew/Caskroom/redis-stack-server/<VERSION>/bin:$PATH\n\n\nIn both cases, replace `<VERSION>` with your version of Redis Stack. For example, with version 6.2.0, path is as follows:\n\n\nexport PATH=/opt/homebrew/Caskroom/redis-stack-server/6.2.0/bin:$PATH"},{"id":"start-redis-stack-server","title":"Start Redis Stack Server","role":"content","text":"You can now start Redis Stack Server as follows:\n\n\nredis-stack-server"},{"id":"installing-redis-after-installing-redis-stack","title":"Installing Redis after installing Redis Stack","role":"setup","text":"If you've already installed Redis Stack with Homebrew and then try to install Redis with `brew install redis`, you may encounter errors like the following:\n\n\nError: The brew link step did not complete successfully\nThe formula built, but is not symlinked into /usr/local\nCould not symlink bin/redis-benchmark\nTarget /usr/local/bin/redis-benchmark\nalready exists. You may want to remove it:\nrm '/usr/local/bin/redis-benchmark'\n\nTo force the link and overwrite all conflicting files:\nbrew link --overwrite redis\n\nTo list all files that would be deleted:\nbrew link --overwrite --dry-run redis\n\n\nIn this case, you can overwrite the Redis binaries installed by Redis Stack by running:\n\n\nbrew link --overwrite redis\n\n\nHowever, Redis Stack Server will still be installed. To uninstall Redis Stack Server, see below."},{"id":"uninstall-redis-stack","title":"Uninstall Redis Stack","role":"content","text":"To uninstall Redis Stack, run:\n\n\nbrew uninstall redis-stack-redisinsight redis-stack-server redis-stack\nbrew untap redis-stack/redis-stack"},{"id":"connect-to-redis","title":"Connect to Redis","role":"content","text":"Once Redis is running, you can test it by running `redis-cli`:\n\n\nredis-cli\n\n\nTest the connection with the `ping` command:\n\n\n127.0.0.1:6379> ping\nPONG\n\n\nYou can also test that your Redis server is running using\n[Redis Insight](https://redis.io/docs/latest/develop/tools/insight)."},{"id":"next-steps","title":"Next steps","role":"content","text":"Once you have a running Redis instance, you may want to:\n\n* Try the [Redis CLI tutorial](https://redis.io/docs/latest/develop/tools/cli)\n* Connect using one of the [Redis clients](https://redis.io/docs/latest/develop/clients)\n* [Install Redis \"properly\"](https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-redis#install-redis-properly)\n  for production use."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/install/archive/install-stack/windows","title":"Install Redis Stack on Windows","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-stack/windows/","summary":"How to install Redis Stack on Windows","tags":["docs","operate","stack","oss"],"last_updated":"2026-09-21T15:59:08+01:00","page_type":"content","content_hash":"dc52840e0f060872e03fc50113efbf83715db0d7d57070a2fde752754949c445","sections":[{"id":"run-redis-on-windows-using-memurai","title":"Run Redis on Windows using Memurai","role":"overview","text":"Redis is now natively supported on Windows through [Memurai](https://www.memurai.com/), the official Redis partner for Windows compatibility."},{"id":"run-redis-on-windows-using-docker","title":"Run Redis on Windows using Docker","role":"content","text":"To install Redis Stack on Windows, you will need to have Docker installed. When Docker is up and running, open Windows PowerShell and follow the instructions described in [Run Redis Stack on Docker](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/docker). Then, use Docker to connect with `redis-cli` as explained in that topic.\n\n> [!NOTE] About using WSL and Ubuntu for Windows \n> If you attempt to use Windows Subsystem for Linux (WSL) or Ubuntu for Windows to follow [Linux instructions](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/apt), you will get a `systemd` error telling you `System has not been booted with systemd as init system (PID 1). Can't operate.` Do not fret. Just use Docker. \n>\n> _`systemd` is a suite of basic building blocks for a Linux system._ For more information about its function, see [System and Service Manager](https://systemd.io/). This becomes an issue due to the lack of support for Linux workflows on WSL. But, you can test the instructions listed in [Systemd support is now available in WSL!](https://devblogs.microsoft.com/commandline/systemd-support-is-now-available-in-wsl/). Let us know how that worked for you."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/install/build-stack/almalinux-rocky-10","title":"Build and run Redis Open Source on AlmaLinux/Rocky Linux 10.1+","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/build-stack/almalinux-rocky-10/","summary":"","tags":["docs","operate","stack","oss"],"last_updated":"2026-09-21T15:59:08+01:00","page_type":"content","content_hash":"d00b617f113a7d484cb04dccc484bdf72b8ef306ce2092330f761ffd90ff2095","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Follow the steps below to build and run Redis Open Source with all data structures from its source code on a system running AlmaLinux 10.1 or later or Rocky Linux 10.1 or later.\n\n> [!NOTE]\n> Docker images used to produce these build notes:\n> - AlmaLinux:\n>     - almalinux:10.1\n>     - almalinux:10.1-minimal\n> - Rocky Linux:\n>     - rockylinux/rockylinux:10.1\n>     - rockylinux/rockylinux:10.1-minimal"},{"id":"1-prepare-the-system","title":"1. Prepare the system","role":"content","text":"> [!NOTE]\n> For 10-minimal, you'll need to install `dnf` as follows:\n>\n> [code example]\n\nEnable the required repositories (`epel-release` and CRB provide some of the `-devel` packages):\n\n[code example]"},{"id":"2-install-required-dependencies","title":"2. Install required dependencies","role":"content","text":"Install the necessary development tools and libraries. AlmaLinux/Rocky 10 ship GCC 14 and CMake 3.30 in the default repositories, which are supported by the Redis build, so no separate compiler or CMake toolset is required:\n\n[code example]\n\nOn AlmaLinux/Rocky 10.1 the `clang`, `lld`, and `llvm` packages above are LLVM 21, which matches the LLVM version of the Rust toolchain that `INSTALL_RUST_TOOLCHAIN=yes` installs. RediSearch's cross-language (C/Rust) LTO needs this match, and `llvm` provides the `llvm-ar`/`llvm-ranlib` archiver the LTO build uses, so no separate LLVM toolchain is required."},{"id":"3-download-and-extract-the-redis-source","title":"3. Download and extract the Redis source","role":"content","text":"The Redis source code is available from [the Redis GitHub site](https://github.com/redis/redis/releases). Select the release you want to build and then select the .tar.gz file from the **Assets** drop down menu. You can verify the integrity of these downloads by checking them against the digests in the [redis-hashes GitHub repository](https://github.com/redis/redis-hashes).\n\nCopy the tar(1) file to `/usr/src`.\n\nAlternatively, you can download the file directly using the `wget` command, as shown below.\n\n[code example]\n\nReplace `<version>` with the three-digit Redis release number, for example `8.0.0`.\n\nExtract the source:\n\n[code example]"},{"id":"4-build-redis","title":"4. Build Redis","role":"content","text":"Set the necessary environment variables and build Redis with TLS and module support. RediSearch builds with cross-language LTO (the default) because the `clang`/`lld` 21 installed in step 2 match the Rust toolchain's LLVM. On AlmaLinux, `IGNORE_MISSING_DEPS=1` bypasses the `v8.7.91` dep-checker that does not yet recognize `almalinux` (fixed in `redisearch` v8.8.0; harmless on, and not required for, Rocky Linux 10):\n\n[code example]"},{"id":"5-optional-verify-the-build","title":"5. (Optional) Verify the build","role":"content","text":"Check the built Redis server and CLI versions:\n\n[code example]"},{"id":"6-start-redis","title":"6. Start Redis","role":"content","text":"To start Redis, use the following command:\n\n[code example]\n\n> [!NOTE]\n> Before Redis 8.10, specify `redis-full.conf` instead of `redis.conf`.\n\nTo validate that the available modules have been installed, run the [`INFO`](https://redis.io/docs/latest/commands/info) command and look for lines similar to the following:\n\n[code example]"},{"id":"7-optional-install-redis-to-its-default-location","title":"7. (Optional) Install Redis to its default location","role":"content","text":"[code example]"}],"examples":[{"id":"1-prepare-the-system-ex0","language":"bash","code":"> microdnf install dnf -y\n>","section_id":"1-prepare-the-system"},{"id":"1-prepare-the-system-ex1","language":"bash","code":"sudo dnf install -y epel-release\nsudo dnf config-manager --set-enabled crb","section_id":"1-prepare-the-system"},{"id":"2-install-required-dependencies-ex0","language":"bash","code":"sudo dnf groupinstall \"Development Tools\" -y\nsudo dnf install -y \\\n    pkg-config \\\n    xz \\\n    wget \\\n    which \\\n    gcc \\\n    gcc-c++ \\\n    cmake \\\n    git \\\n    make \\\n    openssl \\\n    openssl-devel \\\n    python3 \\\n    python3-pip \\\n    python3-devel \\\n    unzip \\\n    rsync \\\n    clang \\\n    lld \\\n    llvm \\\n    libtool \\\n    automake \\\n    autoconf \\\n    jq \\\n    systemd-devel","section_id":"2-install-required-dependencies"},{"id":"3-download-and-extract-the-redis-source-ex0","language":"bash","code":"cd /usr/src\nwget -O redis-<version>.tar.gz https://github.com/redis/redis/archive/refs/tags/<version>.tar.gz","section_id":"3-download-and-extract-the-redis-source"},{"id":"3-download-and-extract-the-redis-source-ex1","language":"bash","code":"cd /usr/src\ntar xvf redis-<version>.tar.gz\nrm redis-<version>.tar.gz","section_id":"3-download-and-extract-the-redis-source"},{"id":"4-build-redis-ex0","language":"bash","code":"cd /usr/src/redis-<version>\nexport BUILD_TLS=yes\nexport BUILD_WITH_MODULES=yes\nexport INSTALL_RUST_TOOLCHAIN=yes\nexport IGNORE_MISSING_DEPS=1\nmake -j \"$(nproc)\" all","section_id":"4-build-redis"},{"id":"5-optional-verify-the-build-ex0","language":"bash","code":"cd /usr/src/redis-<version>\n./src/redis-server --version\n./src/redis-cli --version","section_id":"5-optional-verify-the-build"},{"id":"6-start-redis-ex0","language":"bash","code":"cd /usr/src/redis-<version>\n./src/redis-server redis.conf","section_id":"6-start-redis"},{"id":"6-start-redis-ex1","language":"bash","code":"cd /usr/src/redis-<version>\n./src/redis-cli INFO\n...\n# Modules\nmodule:name=ReJSON,ver=20803,api=1,filters=0,usedby=[search],using=[],options=[handle-io-errors]\nmodule:name=search,ver=21005,api=1,filters=0,usedby=[],using=[ReJSON],options=[handle-io-errors]\nmodule:name=bf,ver=20802,api=1,filters=0,usedby=[],using=[],options=[]\nmodule:name=timeseries,ver=11202,api=1,filters=0,usedby=[],using=[],options=[handle-io-errors]\nmodule:name=RedisCompat,ver=1,api=1,filters=0,usedby=[],using=[],options=[]\nmodule:name=vectorset,ver=1,api=1,filters=0,usedby=[],using=[],options=[]\n...","section_id":"6-start-redis"},{"id":"7-optional-install-redis-to-its-default-location-ex0","language":"bash","code":"cd /usr/src/redis-<version>\nsudo make install","section_id":"7-optional-install-redis-to-its-default-location"}]}
{"schema_version":2,"id":"operate/oss_and_stack/install/build-stack/almalinux-rocky-8","title":"Build and run Redis Open Source on AlmaLinux/Rocky Linux 8.10","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/build-stack/almalinux-rocky-8/","summary":"","tags":["docs","operate","stack","oss"],"last_updated":"2026-09-21T15:59:08+01:00","page_type":"content","content_hash":"f8d6e9d63509413f05b184baabe318883bc77a84efd56bc6382b08c53ae37d31","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Follow the steps below to build and run Redis Open Source with all data structures from its source code on a system running AlmaLinux 8.10 or Rocky Linux 8.10.\n\n> [!NOTE]\n> Docker images used to produce these build notes:\n> - AlmaLinux:\n>     - almalinux:8.10\n>     - almalinux:8.10-minimal\n> - Rocky Linux:\n>     - rockylinux/rockylinux:8.10\n>     - rockylinux/rockylinux:8.10-minimal"},{"id":"1-prepare-the-system","title":"1. Prepare the system","role":"content","text":"> [!NOTE]\n> For 8.10-minimal, you'll need to install `sudo` and `dnf` as follows:\n>\n> [code example]\n>\n> For 8.10 (regular), you'll need to install `sudo` as follows:\n>\n> [code example]\n\nEnable the required repositories and install the base development tools:\n\n[code example]"},{"id":"2-install-required-dependencies","title":"2. Install required dependencies","role":"content","text":"Update your package lists and install the necessary development tools and libraries:\n\n[code example]\n\nEnable the GCC toolset:\n\n[code example]"},{"id":"3-install-cmake","title":"3. Install CMake","role":"content","text":"Install CMake 3.25.1 manually:\n\n[code example]"},{"id":"4-download-and-extract-the-redis-source","title":"4. Download and extract the Redis source","role":"content","text":"The Redis source code is available from [the Redis GitHub site](https://github.com/redis/redis/releases). Select the release you want to build and then select the .tar.gz file from the **Assets** drop down menu. You can verify the integrity of these downloads by checking them against the digests in the [redis-hashes GitHub repository](https://github.com/redis/redis-hashes).\n\nCopy the tar(1) file to `/usr/src`.\n\nAlternatively, you can download the file directly using the `wget` command, as shown below.\n\n[code example]\n\nReplace `<version>` with the three-digit Redis release number, for example `8.0.0`.\n\nExtract the source:\n\n[code example]"},{"id":"5-build-redis","title":"5. Build Redis","role":"content","text":"Enable the GCC toolset, set the necessary environment variables, and build Redis with TLS and module support:\n\n[code example]"},{"id":"6-optional-verify-the-build","title":"6. (Optional) Verify the build","role":"content","text":"Check the built Redis server and CLI versions:\n\n[code example]"},{"id":"7-start-redis","title":"7. Start Redis","role":"content","text":"To start Redis, use the following command:\n\n[code example]\n\n> [!NOTE]\n> Before Redis 8.10, specify `redis-full.conf` instead of `redis.conf`.\n\nTo validate that the available modules have been installed, run the [`INFO`](https://redis.io/docs/latest/commands/info) command and look for lines similar to the following:\n\n[code example]"},{"id":"8-optional-install-redis-to-its-default-location","title":"8. (Optional) Install Redis to its default location","role":"content","text":"[code example]"}],"examples":[{"id":"1-prepare-the-system-ex0","language":"bash","code":"> microdnf install dnf sudo -y\n>","section_id":"1-prepare-the-system"},{"id":"1-prepare-the-system-ex1","language":"bash","code":"> dnf install sudo -y\n>","section_id":"1-prepare-the-system"},{"id":"1-prepare-the-system-ex2","language":"bash","code":"sudo dnf groupinstall \"Development Tools\" -y\nsudo dnf config-manager --set-enabled powertools\nsudo dnf install -y epel-release","section_id":"1-prepare-the-system"},{"id":"2-install-required-dependencies-ex0","language":"bash","code":"sudo dnf install -y \\\n    pkg-config \\\n    wget \\\n    gcc-toolset-13-gcc \\\n    gcc-toolset-13-gcc-c++ \\\n    git \\\n    make \\\n    openssl \\\n    openssl-devel \\\n    python3.11 \\\n    python3.11-pip \\\n    python3.11-devel \\\n    unzip \\\n    rsync \\\n    clang \\\n    libtool \\\n    automake \\\n    autoconf \\\n    jq \\\n    systemd-devel","section_id":"2-install-required-dependencies"},{"id":"2-install-required-dependencies-ex1","language":"bash","code":"sudo cp /opt/rh/gcc-toolset-13/enable /etc/profile.d/gcc-toolset-13.sh\necho \"source /etc/profile.d/gcc-toolset-13.sh\" | sudo tee -a /etc/bashrc","section_id":"2-install-required-dependencies"},{"id":"3-install-cmake-ex0","language":"bash","code":"CMAKE_VERSION=3.25.1\nARCH=$(uname -m)\nif [ \"$ARCH\" = \"x86_64\" ]; then\n  CMAKE_FILE=cmake-${CMAKE_VERSION}-linux-x86_64.sh\nelse\n  CMAKE_FILE=cmake-${CMAKE_VERSION}-linux-aarch64.sh\nfi\nwget https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/${CMAKE_FILE}\nchmod +x ${CMAKE_FILE}\n./${CMAKE_FILE} --skip-license --prefix=/usr/local --exclude-subdir\nrm ${CMAKE_FILE}\ncmake --version","section_id":"3-install-cmake"},{"id":"4-download-and-extract-the-redis-source-ex0","language":"bash","code":"cd /usr/src\nwget -O redis-<version>.tar.gz https://github.com/redis/redis/archive/refs/tags/<version>.tar.gz","section_id":"4-download-and-extract-the-redis-source"},{"id":"4-download-and-extract-the-redis-source-ex1","language":"bash","code":"cd /usr/src\ntar xvf redis-<version>.tar.gz\nrm redis-<version>.tar.gz","section_id":"4-download-and-extract-the-redis-source"},{"id":"5-build-redis-ex0","language":"bash","code":"source /etc/profile.d/gcc-toolset-13.sh\ncd /usr/src/redis-<version>\nexport BUILD_TLS=yes\nexport BUILD_WITH_MODULES=yes\nexport INSTALL_RUST_TOOLCHAIN=yes\nmake -j \"$(nproc)\" all","section_id":"5-build-redis"},{"id":"6-optional-verify-the-build-ex0","language":"bash","code":"cd /usr/src/redis-<version>\n./src/redis-server --version\n./src/redis-cli --version","section_id":"6-optional-verify-the-build"},{"id":"7-start-redis-ex0","language":"bash","code":"cd /usr/src/redis-<version>\n./src/redis-server redis.conf","section_id":"7-start-redis"},{"id":"7-start-redis-ex1","language":"bash","code":"cd /usr/src/redis-<version>\n./src/redis-cli INFO\n...\n# Modules\nmodule:name=ReJSON,ver=20803,api=1,filters=0,usedby=[search],using=[],options=[handle-io-errors]\nmodule:name=search,ver=21005,api=1,filters=0,usedby=[],using=[ReJSON],options=[handle-io-errors]\nmodule:name=bf,ver=20802,api=1,filters=0,usedby=[],using=[],options=[]\nmodule:name=timeseries,ver=11202,api=1,filters=0,usedby=[],using=[],options=[handle-io-errors]\nmodule:name=RedisCompat,ver=1,api=1,filters=0,usedby=[],using=[],options=[]\nmodule:name=vectorset,ver=1,api=1,filters=0,usedby=[],using=[],options=[]\n...","section_id":"7-start-redis"},{"id":"8-optional-install-redis-to-its-default-location-ex0","language":"bash","code":"cd /usr/src/redis-<version>\nsudo make install","section_id":"8-optional-install-redis-to-its-default-location"}]}
{"schema_version":2,"id":"operate/oss_and_stack/install/build-stack/almalinux-rocky-9","title":"Build and run Redis Open Source on AlmaLinux/Rocky Linux 9.7+","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/build-stack/almalinux-rocky-9/","summary":"","tags":["docs","operate","stack","oss"],"last_updated":"2026-09-21T15:59:08+01:00","page_type":"content","content_hash":"a203fcd15689816ed2fe500c99fd41ac78c40a0599a28a4b98499bdf41d782f3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Follow the steps below to build and run Redis Open Source with all data structures from its source code on a system running AlmaLinux 9.7 or later or Rocky Linux 9.7 or later.\n\n> [!NOTE]\n> Docker images used to produce these build notes:\n> - AlmaLinux:\n>     - almalinux:9.7\n>     - almalinux:9.7-minimal\n> - Rocky Linux:\n>     - rockylinux/rockylinux:9.7\n>     - rockylinux/rockylinux:9.7-minimal"},{"id":"1-prepare-the-system","title":"1. Prepare the system","role":"content","text":"> [!NOTE]\n> For 9-minimal, you'll need to install `sudo` and `dnf` as follows:\n>\n> [code example]\n>\n> For 9 (regular), you'll need to install `sudo` as follows:\n>\n> [code example]\n\nEnable the required repositories (`epel-release` and CRB provide some of the `-devel` packages):\n\n[code example]"},{"id":"2-install-required-dependencies","title":"2. Install required dependencies","role":"content","text":"Update your package lists and install the necessary development tools and libraries:\n\n[code example]\n\nEnable the GCC toolset:\n\n[code example]"},{"id":"3-install-cmake","title":"3. Install CMake","role":"content","text":"Install CMake 3.25.1 manually:\n\n[code example]"},{"id":"4-download-and-extract-the-redis-source","title":"4. Download and extract the Redis source","role":"content","text":"The Redis source code is available from [the Redis GitHub site](https://github.com/redis/redis/releases). Select the release you want to build and then select the .tar.gz file from the **Assets** drop down menu. You can verify the integrity of these downloads by checking them against the digests in the [redis-hashes GitHub repository](https://github.com/redis/redis-hashes).\n\nCopy the tar(1) file to `/usr/src`.\n\nAlternatively, you can download the file directly using the `wget` command, as shown below.\n\n[code example]\n\nReplace `<version>` with the three-digit Redis release number, for example `8.0.0`.\n\nExtract the source:\n\n[code example]"},{"id":"5-build-redis","title":"5. Build Redis","role":"content","text":"Enable the GCC toolset, set the necessary environment variables, and build Redis with TLS and module support:\n\n[code example]"},{"id":"6-optional-verify-the-build","title":"6. (Optional) Verify the build","role":"content","text":"Check the built Redis server and CLI versions:\n\n[code example]"},{"id":"7-start-redis","title":"7. Start Redis","role":"content","text":"To start Redis, use the following command:\n\n[code example]\n\n> [!NOTE]\n> Before Redis 8.10, specify `redis-full.conf` instead of `redis.conf`.\n\nTo validate that the available modules have been installed, run the [`INFO`](https://redis.io/docs/latest/commands/info) command and look for lines similar to the following:\n\n[code example]"},{"id":"8-optional-install-redis-to-its-default-location","title":"8. (Optional) Install Redis to its default location","role":"content","text":"[code example]"}],"examples":[{"id":"1-prepare-the-system-ex0","language":"bash","code":"> microdnf install dnf sudo -y\n>","section_id":"1-prepare-the-system"},{"id":"1-prepare-the-system-ex1","language":"bash","code":"> dnf install sudo -y\n>","section_id":"1-prepare-the-system"},{"id":"1-prepare-the-system-ex2","language":"bash","code":"sudo dnf install -y epel-release\nsudo dnf config-manager --set-enabled crb","section_id":"1-prepare-the-system"},{"id":"2-install-required-dependencies-ex0","language":"bash","code":"sudo dnf install -y \\\n    pkg-config \\\n    xz \\\n    wget \\\n    which \\\n    gcc-toolset-13-gcc \\\n    gcc-toolset-13-gcc-c++ \\\n    git \\\n    make \\\n    openssl \\\n    openssl-devel \\\n    python3 \\\n    python3-pip \\\n    python3-devel \\\n    unzip \\\n    rsync \\\n    clang \\\n    libtool \\\n    automake \\\n    autoconf \\\n    jq \\\n    systemd-devel","section_id":"2-install-required-dependencies"},{"id":"2-install-required-dependencies-ex1","language":"bash","code":"sudo cp /opt/rh/gcc-toolset-13/enable /etc/profile.d/gcc-toolset-13.sh\necho \"source /etc/profile.d/gcc-toolset-13.sh\" | sudo tee -a /etc/bashrc","section_id":"2-install-required-dependencies"},{"id":"3-install-cmake-ex0","language":"bash","code":"CMAKE_VERSION=3.25.1\nARCH=$(uname -m)\nif [ \"$ARCH\" = \"x86_64\" ]; then\n  CMAKE_FILE=cmake-${CMAKE_VERSION}-linux-x86_64.sh\nelse\n  CMAKE_FILE=cmake-${CMAKE_VERSION}-linux-aarch64.sh\nfi\nwget https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/${CMAKE_FILE}\nchmod +x ${CMAKE_FILE}\n./${CMAKE_FILE} --skip-license --prefix=/usr/local --exclude-subdir\nrm ${CMAKE_FILE}\ncmake --version","section_id":"3-install-cmake"},{"id":"4-download-and-extract-the-redis-source-ex0","language":"bash","code":"cd /usr/src\nwget -O redis-<version>.tar.gz https://github.com/redis/redis/archive/refs/tags/<version>.tar.gz","section_id":"4-download-and-extract-the-redis-source"},{"id":"4-download-and-extract-the-redis-source-ex1","language":"bash","code":"cd /usr/src\ntar xvf redis-<version>.tar.gz\nrm redis-<version>.tar.gz","section_id":"4-download-and-extract-the-redis-source"},{"id":"5-build-redis-ex0","language":"bash","code":"source /etc/profile.d/gcc-toolset-13.sh\ncd /usr/src/redis-<version>\nexport BUILD_TLS=yes\nexport BUILD_WITH_MODULES=yes\nexport INSTALL_RUST_TOOLCHAIN=yes\nmake -j \"$(nproc)\" all","section_id":"5-build-redis"},{"id":"6-optional-verify-the-build-ex0","language":"bash","code":"cd /usr/src/redis-<version>\n./src/redis-server --version\n./src/redis-cli --version","section_id":"6-optional-verify-the-build"},{"id":"7-start-redis-ex0","language":"bash","code":"cd /usr/src/redis-<version>\n./src/redis-server redis.conf","section_id":"7-start-redis"},{"id":"7-start-redis-ex1","language":"bash","code":"cd /usr/src/redis-<version>\n./src/redis-cli INFO\n...\n# Modules\nmodule:name=ReJSON,ver=20803,api=1,filters=0,usedby=[search],using=[],options=[handle-io-errors]\nmodule:name=search,ver=21005,api=1,filters=0,usedby=[],using=[ReJSON],options=[handle-io-errors]\nmodule:name=bf,ver=20802,api=1,filters=0,usedby=[],using=[],options=[]\nmodule:name=timeseries,ver=11202,api=1,filters=0,usedby=[],using=[],options=[handle-io-errors]\nmodule:name=RedisCompat,ver=1,api=1,filters=0,usedby=[],using=[],options=[]\nmodule:name=vectorset,ver=1,api=1,filters=0,usedby=[],using=[],options=[]\n...","section_id":"7-start-redis"},{"id":"8-optional-install-redis-to-its-default-location-ex0","language":"bash","code":"cd /usr/src/redis-<version>\nsudo make install","section_id":"8-optional-install-redis-to-its-default-location"}]}
{"schema_version":2,"id":"operate/oss_and_stack/install/build-stack/alpine","title":"Build and run Redis Open Source on Alpine 3.23+","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/build-stack/alpine/","summary":"","tags":["docs","operate","stack","oss"],"last_updated":"2026-09-21T15:59:08+01:00","page_type":"content","content_hash":"9dafc498085b8f364f665ef972a34f5cf8493ae6f4de2b18088f27f1c4d12449","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Follow the steps below to build and run Redis Open Source with all data structures from its source code on a system running Alpine 3.23 or later.\n\n> [!NOTE]\n> Docker image used to produce these build notes:\n> - alpine:3.23\n>\n> The steps below assume you are running as `root`, as in the tested container image."},{"id":"1-install-required-dependencies","title":"1. Install required dependencies","role":"content","text":"Update your package lists and install the necessary development tools and libraries:\n\n[code example]\n\nInstall the Python packages required by the `RedisJSON` module build:\n\n[code example]"},{"id":"2-download-and-extract-the-redis-source","title":"2. Download and extract the Redis source","role":"content","text":"The Redis source code is available from [the Redis GitHub site](https://github.com/redis/redis/releases). Select the release you want to build and then select the .tar.gz file from the **Assets** drop down menu. You can verify the integrity of these downloads by checking them against the digests in the [redis-hashes GitHub repository](https://github.com/redis/redis-hashes).\n\nCopy the tar(1) file to `/usr/src`.\n\nAlternatively, you can download the file directly using the `wget` command, as shown below.\n\n[code example]\n\nReplace `<version>` with the three-digit Redis release number, for example `8.0.0`.\n\nExtract the source:\n\n[code example]"},{"id":"3-build-redis","title":"3. Build Redis","role":"content","text":"Set the necessary environment variables, apply the `RedisJSON` Rust-flags patch, and build Redis with TLS and module support:\n\n[code example]"},{"id":"4-optional-verify-the-build","title":"4. (Optional) Verify the build","role":"content","text":"Check the built Redis server and CLI versions:\n\n[code example]"},{"id":"5-start-redis","title":"5. Start Redis","role":"content","text":"To start Redis, use the following command:\n\n[code example]\n\n> [!NOTE]\n> Before Redis 8.10, specify `redis-full.conf` instead of `redis.conf`.\n\nTo validate that the available modules have been installed, run the [`INFO`](https://redis.io/docs/latest/commands/info) command and look for lines similar to the following:\n\n[code example]"},{"id":"6-optional-install-redis-to-its-default-location","title":"6. (Optional) Install Redis to its default location","role":"content","text":"[code example]"}],"examples":[{"id":"1-install-required-dependencies-ex0","language":"bash","code":"apk update\napk add --no-cache \\\n    build-base coreutils linux-headers bsd-compat-headers \\\n    openssl openssl-dev cmake bash git wget curl xz unzip tar rsync which \\\n    libtool automake autoconf libffi-dev libgcc ncurses-dev xsimd \\\n    cargo clang21 clang21-static clang21-libclang llvm21-dev lld21 \\\n    python3 py3-pip python3-dev","section_id":"1-install-required-dependencies"},{"id":"1-install-required-dependencies-ex1","language":"bash","code":"export PIP_BREAK_SYSTEM_PACKAGES=1\npip install --upgrade setuptools pip\npip install addict toml jinja2 ramp-packer","section_id":"1-install-required-dependencies"},{"id":"2-download-and-extract-the-redis-source-ex0","language":"bash","code":"mkdir -p /usr/src\ncd /usr/src\nwget -O redis-<version>.tar.gz https://github.com/redis/redis/archive/refs/tags/<version>.tar.gz","section_id":"2-download-and-extract-the-redis-source"},{"id":"2-download-and-extract-the-redis-source-ex1","language":"bash","code":"cd /usr/src\ntar xvf redis-<version>.tar.gz\nrm redis-<version>.tar.gz","section_id":"2-download-and-extract-the-redis-source"},{"id":"3-build-redis-ex0","language":"bash","code":"cd /usr/src/redis-<version>\n\nexport BUILD_TLS=yes\nexport BUILD_WITH_MODULES=yes\nexport INSTALL_RUST_TOOLCHAIN=yes\nexport LTO=1\nexport RUST_DYN_CRT=1\nexport PATH=\"/usr/lib/llvm21/bin:$PATH\"\n\n# RedisJSON's bindgen must dlopen libclang.so; drop crt-static from its Rust flags.\nmake -C modules/redisjson get_source\nsed -i 's/^RUST_FLAGS=$/RUST_FLAGS += -C target-feature=-crt-static/' modules/redisjson/src/Makefile\n\nmake -j \"$(nproc)\" all","section_id":"3-build-redis"},{"id":"4-optional-verify-the-build-ex0","language":"bash","code":"cd /usr/src/redis-<version>\n./src/redis-server --version\n./src/redis-cli --version","section_id":"4-optional-verify-the-build"},{"id":"5-start-redis-ex0","language":"bash","code":"cd /usr/src/redis-<version>\n./src/redis-server redis.conf","section_id":"5-start-redis"},{"id":"5-start-redis-ex1","language":"bash","code":"cd /usr/src/redis-<version>\n./src/redis-cli INFO\n...\n# Modules\nmodule:name=ReJSON,ver=20803,api=1,filters=0,usedby=[search],using=[],options=[handle-io-errors]\nmodule:name=search,ver=21005,api=1,filters=0,usedby=[],using=[ReJSON],options=[handle-io-errors]\nmodule:name=bf,ver=20802,api=1,filters=0,usedby=[],using=[],options=[]\nmodule:name=timeseries,ver=11202,api=1,filters=0,usedby=[],using=[],options=[handle-io-errors]\nmodule:name=RedisCompat,ver=1,api=1,filters=0,usedby=[],using=[],options=[]\nmodule:name=vectorset,ver=1,api=1,filters=0,usedby=[],using=[],options=[]\n...","section_id":"5-start-redis"},{"id":"6-optional-install-redis-to-its-default-location-ex0","language":"bash","code":"cd /usr/src/redis-<version>\nmake install","section_id":"6-optional-install-redis-to-its-default-location"}]}
{"schema_version":2,"id":"operate/oss_and_stack/install/build-stack/build-flags","title":"Build flags and general notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/build-stack/build-flags/","summary":"","tags":["docs","operate","stack","oss"],"last_updated":"2026-09-21T15:59:08+01:00","page_type":"content","content_hash":"b202fa0b84214f581b64344657dec4ad49835acc0348a3c243cf3c8c2af14c98","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The pages in this section provide platform-specific instructions for building and running Redis Open Source from source. This page describes the build flags and general notes that apply across all platforms. For the prerequisites and step-by-step instructions for your operating system, see the platform pages that follow."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"Redis can be compiled and used on Linux, OSX, OpenBSD, NetBSD, and FreeBSD. We support big endian and little endian architectures, and both 32 bit and 64 bit systems.\n\nIt may compile on Solaris derived systems (for instance SmartOS) but our support for this platform is _best effort_ and Redis is not guaranteed to work as well as on Linux, OSX, and \\*BSD."},{"id":"build-flags","title":"Build flags","role":"content","text":""},{"id":"build-with-all-data-structures","title":"Build with all data structures","role":"content","text":"To build Redis with all the data structures (including JSON, time series, Bloom filter, cuckoo filter, count-min sketch, top-k, and t-digest) and with the Redis Query Engine, first make sure that all the prerequisites are installed (see the build instructions for your operating system). Then use the following flag in the `make` command:\n\n[code example]\n\n> [!NOTE]\n> `BUILD_WITH_MODULES=yes` is not supported on 32 bit systems."},{"id":"build-with-just-the-core-data-structures","title":"Build with just the core data structures","role":"content","text":"To build Redis with just the core data structures, use:\n\n[code example]"},{"id":"build-with-tls-support","title":"Build with TLS support","role":"content","text":"To build with TLS support, you need OpenSSL development libraries (for example, `libssl-dev` on Debian/Ubuntu) and the following flag in the `make` command:\n\n[code example]"},{"id":"build-with-systemd-support","title":"Build with systemd support","role":"content","text":"To build with systemd support, you need systemd development libraries (such as `libsystemd-dev` on Debian/Ubuntu or `systemd-devel` on CentOS), and the following flag:\n\n[code example]"},{"id":"append-a-suffix-to-redis-program-names","title":"Append a suffix to Redis program names","role":"content","text":"To append a suffix to Redis program names, add the following flag:\n\n[code example]"},{"id":"build-a-32-bit-binary","title":"Build a 32 bit binary","role":"content","text":"You can build a 32 bit Redis binary using:\n\n[code example]"},{"id":"run-the-tests","title":"Run the tests","role":"content","text":"After building Redis, it is a good idea to test it using:\n\n[code example]\n\nIf TLS is built, you can run the tests with TLS enabled (you will need `tcl-tls` installed):\n\n[code example]"},{"id":"fixing-build-problems-with-dependencies-or-cached-build-options","title":"Fixing build problems with dependencies or cached build options","role":"content","text":"Redis has some dependencies which are included in the `deps` directory. `make` does not automatically rebuild dependencies even if something in the source code of dependencies changes.\n\nWhen you update the source code with `git pull` or when code inside the dependencies tree is modified in any other way, make sure to use the following command in order to really clean everything and rebuild from scratch:\n\n[code example]\n\nThis will clean: jemalloc, lua, hiredis, linenoise and other dependencies.\n\nAlso, if you force certain build options like 32 bit target, no C compiler optimizations (for debugging purposes), and other similar build time options, those options are cached indefinitely until you issue a `make distclean` command."},{"id":"fixing-problems-building-32-bit-binaries","title":"Fixing problems building 32 bit binaries","role":"content","text":"If after building Redis with a 32 bit target you need to rebuild it with a 64 bit target, or the other way around, you need to perform a `make distclean` in the root directory of the Redis distribution.\n\nIn case of build errors when trying to build a 32 bit binary of Redis, try the following steps:\n\n- Install the package `libc6-dev-i386` (also try `g++-multilib`).\n- Try using the following command line instead of `make 32bit`:\n\n  [code example]"},{"id":"allocator","title":"Allocator","role":"content","text":"Selecting a non-default memory allocator when building Redis is done by setting the `MALLOC` environment variable. Redis is compiled and linked against libc malloc by default, except for jemalloc being the default on Linux systems. This default was picked because jemalloc has proven to have fewer fragmentation problems than libc malloc.\n\nTo force compiling against libc malloc, use:\n\n[code example]\n\nTo compile against jemalloc on Mac OS X systems, use:\n\n[code example]"},{"id":"monotonic-clock","title":"Monotonic clock","role":"content","text":"By default, Redis will build using the POSIX `clock_gettime` function as the monotonic clock source. On most modern systems, the internal processor clock can be used to improve performance. Cautions can be found here: [http://oliveryang.net/2015/09/pitfalls-of-TSC-usage/](http://oliveryang.net/2015/09/pitfalls-of-TSC-usage/)\n\nOn ARM aarch64 systems, the hardware clock is enabled by default because the ARM Generic Timer is architecturally guaranteed to be available and monotonic on all ARMv8-A processors (see the *\"The Generic Timer in AArch64 state\"* section of the *Arm Architecture Reference Manual for Armv8-A*).\n\nTo build with support for the processor's internal instruction clock on other architectures, use:\n\n[code example]"},{"id":"verbose-build","title":"Verbose build","role":"content","text":"Redis will build with a user-friendly colorized output by default. If you want to see a more verbose output, use the following:\n\n[code example]"},{"id":"running-redis-with-tls","title":"Running Redis with TLS","role":"content","text":"Please consult the [TLS.md](https://github.com/redis/redis/blob/unstable/TLS.md) file in the Redis source distribution for more information on how to use Redis with TLS."},{"id":"running-redis-with-the-redis-search-and-optional-proprietary-intel-svs-vamana-optimizations","title":"Running Redis with the Redis Search and optional proprietary Intel SVS-VAMANA optimizations","role":"content","text":"> [!NOTE]\n> **License disclaimer**\n>\n> If you are using Redis Open Source under AGPLv3 or SSPLv1, you cannot use it together with the Intel optimizations (LeanVec and LVQ binaries). The reason is that the Intel SVS license is not compatible with those licenses.\n>\n> 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 [here](https://github.com/intel/ScalableVectorSearch).\n\nBy default, Redis with Redis Search supports the SVS-VAMANA index with global 8-bit quantization. To compile Redis with the Intel SVS-VAMANA optimizations, LeanVec and LVQ, use the following:\n\n[code example]\n\nAlternatively, you can export the variable before running the build step for your platform:\n\n[code example]"}],"examples":[{"id":"build-with-all-data-structures-ex0","language":"sh","code":"make BUILD_WITH_MODULES=yes","section_id":"build-with-all-data-structures"},{"id":"build-with-just-the-core-data-structures-ex0","language":"sh","code":"make","section_id":"build-with-just-the-core-data-structures"},{"id":"build-with-tls-support-ex0","language":"sh","code":"make BUILD_TLS=yes","section_id":"build-with-tls-support"},{"id":"build-with-systemd-support-ex0","language":"sh","code":"make USE_SYSTEMD=yes","section_id":"build-with-systemd-support"},{"id":"append-a-suffix-to-redis-program-names-ex0","language":"sh","code":"make PROG_SUFFIX=\"-alt\"","section_id":"append-a-suffix-to-redis-program-names"},{"id":"build-a-32-bit-binary-ex0","language":"sh","code":"make 32bit","section_id":"build-a-32-bit-binary"},{"id":"run-the-tests-ex0","language":"sh","code":"make test","section_id":"run-the-tests"},{"id":"run-the-tests-ex1","language":"sh","code":"./utils/gen-test-certs.sh\n./runtest --tls","section_id":"run-the-tests"},{"id":"fixing-build-problems-with-dependencies-or-cached-build-options-ex0","language":"sh","code":"make distclean","section_id":"fixing-build-problems-with-dependencies-or-cached-build-options"},{"id":"fixing-problems-building-32-bit-binaries-ex0","language":"sh","code":"make CFLAGS=\"-m32 -march=native\" LDFLAGS=\"-m32\"","section_id":"fixing-problems-building-32-bit-binaries"},{"id":"allocator-ex0","language":"sh","code":"make MALLOC=libc","section_id":"allocator"},{"id":"allocator-ex1","language":"sh","code":"make MALLOC=jemalloc","section_id":"allocator"},{"id":"monotonic-clock-ex0","language":"sh","code":"make CFLAGS=\"-DUSE_PROCESSOR_CLOCK\"","section_id":"monotonic-clock"},{"id":"verbose-build-ex0","language":"sh","code":"make V=1","section_id":"verbose-build"},{"id":"running-redis-with-the-redis-search-and-optional-proprietary-intel-svs-vamana-optimizations-ex0","language":"sh","code":"make BUILD_INTEL_SVS_OPT=yes","section_id":"running-redis-with-the-redis-search-and-optional-proprietary-intel-svs-vamana-optimizations"},{"id":"running-redis-with-the-redis-search-and-optional-proprietary-intel-svs-vamana-optimizations-ex1","language":"sh","code":"export BUILD_INTEL_SVS_OPT=yes\nmake","section_id":"running-redis-with-the-redis-search-and-optional-proprietary-intel-svs-vamana-optimizations"}]}
{"schema_version":2,"id":"operate/oss_and_stack/install/build-stack/debian-bookworm","title":"Build and run Redis Open Source on Debian 12 (Bookworm) and Debian 13 (Trixie)","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/build-stack/debian-bookworm/","summary":"","tags":["docs","operate","stack","oss"],"last_updated":"2026-09-21T15:59:08+01:00","page_type":"content","content_hash":"f1563184c84152ae615abf9e89fdf5390426ae149ca1e268aca4b197e96260a8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Follow the steps below to build and run Redis Open Source with all data structures from its source code on a system running Debian 12 (Bookworm) or Debian 13 (Trixie).\n\n> [!NOTE]\n> Docker images used to produce these build notes:\n> - debian:bookworm\n> - debian:bookworm-slim\n> - debian:trixie\n> - debian:trixie-slim"},{"id":"1-install-required-dependencies","title":"1. Install required dependencies","role":"content","text":"Update your package lists and install the development tools and libraries:\n\n[code example]"},{"id":"2-download-and-extract-the-redis-source","title":"2. Download and extract the Redis source","role":"content","text":"The Redis source code is available from [the Redis GitHub site](https://github.com/redis/redis/releases). Select the release you want to build and then select the .tar.gz file from the **Assets** drop down menu. You can verify the integrity of these downloads by checking them against the digests in the [redis-hashes GitHub repository](https://github.com/redis/redis-hashes).\n\nCopy the tar(1) file to `/usr/src`.\n\nAlternatively, you can download the file directly using the `wget` command, as shown below.\n\n[code example]\n\nReplace `<version>` with the three-digit Redis release number, for example `8.0.0`.\n\nExtract the source:\n\n[code example]"},{"id":"3-build-redis","title":"3. Build Redis","role":"content","text":"Set the necessary environment variables and build Redis with TLS and module support:\n\n[code example]"},{"id":"4-optional-verify-the-installation","title":"4. (Optional) Verify the installation","role":"content","text":"Check the built Redis server and CLI versions:\n\n[code example]"},{"id":"5-start-redis","title":"5. Start Redis","role":"content","text":"To start Redis, use the following command:\n\n[code example]\n\n> [!NOTE]\n> Before Redis 8.10, specify `redis-full.conf` instead of `redis.conf`.\n\nTo validate that the available modules have been installed, run the [`INFO`](https://redis.io/docs/latest/commands/info) command and look for lines similar to the following:\n\n[code example]"},{"id":"6-optional-install-redis-to-its-default-location","title":"6. (Optional) Install Redis to its default location","role":"content","text":"[code example]"}],"examples":[{"id":"1-install-required-dependencies-ex0","language":"bash","code":"apt-get update\napt-get install -y sudo\nsudo apt-get install -y --no-install-recommends \\\n    ca-certificates \\\n    wget \\\n    dpkg-dev \\\n    gcc \\\n    g++ \\\n    libc6-dev \\\n    libssl-dev \\\n    make \\\n    git \\\n    cmake \\\n    python3 \\\n    python3-pip \\\n    python3-venv \\\n    python3-dev \\\n    unzip \\\n    rsync \\\n    clang \\\n    automake \\\n    autoconf \\\n    libtool","section_id":"1-install-required-dependencies"},{"id":"2-download-and-extract-the-redis-source-ex0","language":"bash","code":"cd /usr/src\nwget -O redis-<version>.tar.gz https://github.com/redis/redis/archive/refs/tags/<version>.tar.gz","section_id":"2-download-and-extract-the-redis-source"},{"id":"2-download-and-extract-the-redis-source-ex1","language":"bash","code":"cd /usr/src\ntar xvf redis-<version>.tar.gz\nrm redis-<version>.tar.gz","section_id":"2-download-and-extract-the-redis-source"},{"id":"3-build-redis-ex0","language":"bash","code":"cd /usr/src/redis-<version>\nexport BUILD_TLS=yes\nexport BUILD_WITH_MODULES=yes\nexport INSTALL_RUST_TOOLCHAIN=yes\nmake -j \"$(nproc)\" all","section_id":"3-build-redis"},{"id":"4-optional-verify-the-installation-ex0","language":"bash","code":"cd /usr/src/redis-<version>\n./src/redis-server --version\n./src/redis-cli --version","section_id":"4-optional-verify-the-installation"},{"id":"5-start-redis-ex0","language":"bash","code":"cd /usr/src/redis-<version>\n./src/redis-server redis.conf","section_id":"5-start-redis"},{"id":"5-start-redis-ex1","language":"bash","code":"cd /usr/src/redis-<version>\n./src/redis-cli INFO\n...\n# Modules\nmodule:name=ReJSON,ver=20803,api=1,filters=0,usedby=[search],using=[],options=[handle-io-errors]\nmodule:name=search,ver=21005,api=1,filters=0,usedby=[],using=[ReJSON],options=[handle-io-errors]\nmodule:name=bf,ver=20802,api=1,filters=0,usedby=[],using=[],options=[]\nmodule:name=timeseries,ver=11202,api=1,filters=0,usedby=[],using=[],options=[handle-io-errors]\nmodule:name=RedisCompat,ver=1,api=1,filters=0,usedby=[],using=[],options=[]\nmodule:name=vectorset,ver=1,api=1,filters=0,usedby=[],using=[],options=[]\n...","section_id":"5-start-redis"},{"id":"6-optional-install-redis-to-its-default-location-ex0","language":"bash","code":"cd /usr/src/redis-<version>\nsudo make install","section_id":"6-optional-install-redis-to-its-default-location"}]}
{"schema_version":2,"id":"operate/oss_and_stack/install/build-stack","title":"Build and run Redis Open Source","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/build-stack/","summary":"Build and run Redis 8 in Redis Open Source on Linux and macOS","aliases":["/install/install-redis/install-redis-from-source/"],"tags":["docs","operate","stack","oss"],"last_updated":"2026-09-21T15:59:08+01:00","children":[{"id":"operate/oss_and_stack/install/build-stack/build-flags","summary":"","title":"Build flags and general notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/build-stack/build-flags/"},{"id":"operate/oss_and_stack/install/build-stack/almalinux-rocky-8","summary":"","title":"Build and run Redis Open Source on AlmaLinux/Rocky Linux 8.10","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/build-stack/almalinux-rocky-8/"},{"id":"operate/oss_and_stack/install/build-stack/almalinux-rocky-9","summary":"","title":"Build and run Redis Open Source on AlmaLinux/Rocky Linux 9.7+","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/build-stack/almalinux-rocky-9/"},{"id":"operate/oss_and_stack/install/build-stack/almalinux-rocky-10","summary":"","title":"Build and run Redis Open Source on AlmaLinux/Rocky Linux 10.1+","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/build-stack/almalinux-rocky-10/"},{"id":"operate/oss_and_stack/install/build-stack/debian-bookworm","summary":"","title":"Build and run Redis Open Source on Debian 12 (Bookworm) and Debian 13 (Trixie)","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/build-stack/debian-bookworm/"},{"id":"operate/oss_and_stack/install/build-stack/ubuntu-jammy","summary":"","title":"Build and run Redis Open Source on Ubuntu 22.04 (Jammy)","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/build-stack/ubuntu-jammy/"},{"id":"operate/oss_and_stack/install/build-stack/ubuntu-noble","summary":"","title":"Build and run Redis Open Source on Ubuntu 24.04 (Noble)","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/build-stack/ubuntu-noble/"},{"id":"operate/oss_and_stack/install/build-stack/ubuntu-resolute","summary":"","title":"Build and run Redis Open Source on Ubuntu 26.04 (Resolute)","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/build-stack/ubuntu-resolute/"},{"id":"operate/oss_and_stack/install/build-stack/alpine","summary":"","title":"Build and run Redis Open Source on Alpine 3.23+","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/build-stack/alpine/"},{"id":"operate/oss_and_stack/install/build-stack/macos-13-14","summary":"","title":"Build and run Redis Open Source on macOS 14 (Sonoma), 15 (Sequoia), and 26 (Tahoe)","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/build-stack/macos-13-14/"}],"page_type":"content","content_hash":"351250c79703d9d6589bcd18835a28c32d33613df637554e1e7d4057bfdcdab6","sections":[{"id":"content","title":"Content","role":"content","text":"See [Build flags and general notes](https://redis.io/docs/latest/operate/oss_and_stack/install/build-stack/build-flags) for the build flags and options that apply across all platforms.\n\nPlatform-specific build instructions are provided for the following operating systems:"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/install/build-stack/macos-13-14","title":"Build and run Redis Open Source on macOS 14 (Sonoma), 15 (Sequoia), and 26 (Tahoe)","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/build-stack/macos-13-14/","summary":"","tags":["docs","operate","stack","oss"],"last_updated":"2026-09-21T15:59:08+01:00","page_type":"content","content_hash":"975f605acbc480b40995b2e8ec29cf884e3f1c77813c61f4bcc126e63e4dc274","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Follow the steps below to build and run Redis Open Source with all data structures from its source code on a system running macOS 14 (Sonoma), macOS 15 (Sequoia), or macOS 26 (Tahoe). These instructions apply to both Intel and Apple Silicon (ARM) Macs.\n\n> [!NOTE]\n> Three RediSearch-specific build constraints apply on macOS and are handled in the steps below:\n>\n> - The cross-language LTO that RediSearch enables by default requires Linux; its build script aborts on macOS with `Error: LTO is only supported on Linux`. Step 5 sets `LTO=0` to disable it.\n> - RediSearch's Rust workspace uses edition 2024 and features stabilized in Rust 1.94, so the Rust toolchain in step 3 is pinned to `1.94.0`. Older Rust fails with `feature edition2024 is required`.\n> - RediSearch's CMake build calls `libtool -static` (BSD libtool syntax). Step 6's `PATH` prepends `$HOMEBREW_PREFIX/opt/libtool/libexec/gnubin`, so Homebrew's GNU `libtool` is used for that step instead of macOS's `/usr/bin/libtool`."},{"id":"1-install-homebrew","title":"1. Install homebrew","role":"content","text":"If Homebrew isn't already installed, follow the installation instructions on the [Homebrew home page](https://brew.sh)."},{"id":"2-install-required-packages","title":"2. Install required packages","role":"content","text":"[code example]"},{"id":"3-install-rust","title":"3. Install Rust","role":"content","text":"Rust is required to build the JSON package.\n\n[code example]"},{"id":"4-download-and-extract-the-redis-source","title":"4. Download and extract the Redis source","role":"content","text":"The Redis source code is available from [the Redis GitHub site](https://github.com/redis/redis/releases). Select the release you want to build and then select the .tar.gz file from the **Assets** drop down menu. You can verify the integrity of these downloads by checking them against the digests in the [redis-hashes GitHub repository](https://github.com/redis/redis-hashes).\n\nCreate a directory for the src, for example `~/src`.\n\n[code example]\n\nCopy the tar(1) file to `~/src`.\n\nAlternatively, you can download the file directly using the `wget` command, as shown below.\n\n[code example]\n\nReplace `<version>` with the three-digit Redis release number, for example `8.0.0`.\n\nExtract the source:\n\n[code example]"},{"id":"5-build-redis","title":"5. Build Redis","role":"content","text":"[code example]"},{"id":"6-optional-verify-the-installation","title":"6. (Optional) Verify the installation","role":"content","text":"Check the installed Redis server and CLI versions:\n\n[code example]"},{"id":"7-start-redis","title":"7. Start Redis","role":"content","text":"To start Redis, use the following command:\n\n[code example]\n\n> [!NOTE]\n> Before Redis 8.10, specify `redis-full.conf` instead of `redis.conf`.\n\nTo validate that the available modules have been installed, run the [`INFO`](https://redis.io/docs/latest/commands/info) command and look for lines similar to the following:\n\n[code example]"}],"examples":[{"id":"2-install-required-packages-ex0","language":"bash","code":"export HOMEBREW_NO_AUTO_UPDATE=1\nbrew update\nbrew install coreutils\nbrew install make\nbrew install openssl\nbrew install llvm@18\nbrew install cmake\nbrew install gnu-sed\nbrew install automake\nbrew install libtool\nbrew install wget","section_id":"2-install-required-packages"},{"id":"3-install-rust-ex0","language":"bash","code":"RUST_INSTALLER=rust-1.94.0-$(if [ \"$(uname -m)\" = \"arm64\" ]; then echo \"aarch64\"; else echo \"x86_64\"; fi)-apple-darwin\nwget --quiet -O ${RUST_INSTALLER}.tar.xz https://static.rust-lang.org/dist/${RUST_INSTALLER}.tar.xz\ntar -xf ${RUST_INSTALLER}.tar.xz\n(cd ${RUST_INSTALLER} && sudo ./install.sh)","section_id":"3-install-rust"},{"id":"4-download-and-extract-the-redis-source-ex0","language":"bash","code":"mkdir ~/src","section_id":"4-download-and-extract-the-redis-source"},{"id":"4-download-and-extract-the-redis-source-ex1","language":"bash","code":"cd ~/src\nwget -O redis-<version>.tar.gz https://github.com/redis/redis/archive/refs/tags/<version>.tar.gz","section_id":"4-download-and-extract-the-redis-source"},{"id":"4-download-and-extract-the-redis-source-ex2","language":"bash","code":"cd ~/src\ntar xvf redis-<version>.tar.gz\nrm redis-<version>.tar.gz","section_id":"4-download-and-extract-the-redis-source"},{"id":"5-build-redis-ex0","language":"bash","code":"cd ~/src/redis-<version>\nexport HOMEBREW_PREFIX=\"$(brew --prefix)\"\nexport BUILD_WITH_MODULES=yes\nexport BUILD_TLS=yes\nexport LTO=0\nPATH=\"$HOMEBREW_PREFIX/opt/libtool/libexec/gnubin:$HOMEBREW_PREFIX/opt/llvm@18/bin:$HOMEBREW_PREFIX/opt/make/libexec/gnubin:$HOMEBREW_PREFIX/opt/gnu-sed/libexec/gnubin:$HOMEBREW_PREFIX/opt/coreutils/libexec/gnubin:$PATH\"\nexport LDFLAGS=\"-L$HOMEBREW_PREFIX/opt/llvm@18/lib\"\nexport CPPFLAGS=\"-I$HOMEBREW_PREFIX/opt/llvm@18/include\"\nmkdir -p build_dir/etc\nmake -C redis-8.0 -j \"$(nproc)\" all OS=macos\nmake -C redis-8.0 install PREFIX=$(pwd)/build_dir OS=macos","section_id":"5-build-redis"},{"id":"6-optional-verify-the-installation-ex0","language":"bash","code":"cd ~/src/redis-<version>\nbuild_dir/bin/redis-server --version\nbuild_dir/bin/redis-cli --version","section_id":"6-optional-verify-the-installation"},{"id":"7-start-redis-ex0","language":"bash","code":"cd ~/src/redis-<version>\nexport LC_ALL=en_US.UTF-8\nexport LANG=en_US.UTF-8\nbuild_dir/bin/redis-server redis.conf","section_id":"7-start-redis"},{"id":"7-start-redis-ex1","language":"bash","code":"build_dir/bin/redis-cli INFO\n...\n# Modules\nmodule:name=ReJSON,ver=20803,api=1,filters=0,usedby=[search],using=[],options=[handle-io-errors]\nmodule:name=search,ver=21005,api=1,filters=0,usedby=[],using=[ReJSON],options=[handle-io-errors]\nmodule:name=bf,ver=20802,api=1,filters=0,usedby=[],using=[],options=[]\nmodule:name=timeseries,ver=11202,api=1,filters=0,usedby=[],using=[],options=[handle-io-errors]\nmodule:name=RedisCompat,ver=1,api=1,filters=0,usedby=[],using=[],options=[]\nmodule:name=vectorset,ver=1,api=1,filters=0,usedby=[],using=[],options=[]\n...","section_id":"7-start-redis"}]}
{"schema_version":2,"id":"operate/oss_and_stack/install/build-stack/ubuntu-jammy","title":"Build and run Redis Open Source on Ubuntu 22.04 (Jammy)","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/build-stack/ubuntu-jammy/","summary":"","tags":["docs","operate","stack","oss"],"last_updated":"2026-09-21T15:59:08+01:00","page_type":"content","content_hash":"fd70fc54ade6a2c77b29a6c083734f513189452f07a913281feb30e09eb0b119","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Follow the steps below to build and run Redis Open Source with all data structures from its source code on a system running Ubuntu 22.04 (Jammy).\n\n> [!NOTE]\n> Docker images used to produce these build notes:\n> - ubuntu:22.04"},{"id":"1-install-required-dependencies","title":"1. Install required dependencies","role":"content","text":"Update your package lists and install the necessary development tools and libraries:\n\n[code example]"},{"id":"2-install-cmake","title":"2. Install CMake","role":"content","text":"Install CMake using `pip3` and link it for system-wide access.\n\n> [!WARNING]\n> CMake version 3.31.6 is the latest supported version. Newer versions cannot be used.\n\n[code example]"},{"id":"3-download-and-extract-the-redis-source","title":"3. Download and extract the Redis source","role":"content","text":"The Redis source code is available from [the Redis GitHub site](https://github.com/redis/redis/releases). Select the release you want to build and then select the .tar.gz file from the **Assets** drop down menu. You can verify the integrity of these downloads by checking them against the digests in the [redis-hashes GitHub repository](https://github.com/redis/redis-hashes).\n\nCopy the tar(1) file to `/usr/src`.\n\nAlternatively, you can download the file directly using the `wget` command, as shown below.\n\n[code example]\n\nReplace `<version>` with the three-digit Redis release number, for example `8.0.0`.\n\nExtract the source:\n\n[code example]"},{"id":"4-build-redis","title":"4. Build Redis","role":"content","text":"Set the necessary environment variables, and build Redis with TLS and module support:\n\n[code example]"},{"id":"5-optional-verify-the-installation","title":"5. (Optional) Verify the installation","role":"content","text":"Check the built Redis server and CLI versions:\n\n[code example]"},{"id":"6-start-redis","title":"6. Start Redis","role":"content","text":"To start Redis, use the following command:\n\n[code example]\n\n> [!NOTE]\n> Before Redis 8.10, specify `redis-full.conf` instead of `redis.conf`.\n\nTo validate that the available modules have been installed, run the [`INFO`](https://redis.io/docs/latest/commands/info) command and look for lines similar to the following:\n\n[code example]"},{"id":"7-optional-install-redis-to-its-default-location","title":"7. (Optional) Install Redis to its default location","role":"content","text":"[code example]"}],"examples":[{"id":"1-install-required-dependencies-ex0","language":"bash","code":"apt-get update\napt-get install -y sudo\nsudo apt-get install -y --no-install-recommends \\\n    ca-certificates \\\n    wget \\\n    dpkg-dev \\\n    gcc \\\n    g++ \\\n    libc6-dev \\\n    libssl-dev \\\n    make \\\n    git \\\n    cmake \\\n    python3 \\\n    python3-pip \\\n    python3-venv \\\n    python3-dev \\\n    unzip \\\n    rsync \\\n    clang \\\n    automake \\\n    autoconf \\\n    libtool","section_id":"1-install-required-dependencies"},{"id":"2-install-cmake-ex0","language":"bash","code":"pip3 install cmake==3.31.6\nsudo ln -sf /usr/local/bin/cmake /usr/bin/cmake\ncmake --version","section_id":"2-install-cmake"},{"id":"3-download-and-extract-the-redis-source-ex0","language":"bash","code":"cd /usr/src\nwget -O redis-<version>.tar.gz https://github.com/redis/redis/archive/refs/tags/<version>.tar.gz","section_id":"3-download-and-extract-the-redis-source"},{"id":"3-download-and-extract-the-redis-source-ex1","language":"bash","code":"cd /usr/src\ntar xvf redis-<version>.tar.gz\nrm redis-<version>.tar.gz","section_id":"3-download-and-extract-the-redis-source"},{"id":"4-build-redis-ex0","language":"bash","code":"cd /usr/src/redis-<version>\nexport BUILD_TLS=yes\nexport BUILD_WITH_MODULES=yes\nexport INSTALL_RUST_TOOLCHAIN=yes\nmake -j \"$(nproc)\" all","section_id":"4-build-redis"},{"id":"5-optional-verify-the-installation-ex0","language":"bash","code":"cd /usr/src/redis-<version>\n./src/redis-server --version\n./src/redis-cli --version","section_id":"5-optional-verify-the-installation"},{"id":"6-start-redis-ex0","language":"bash","code":"cd /usr/src/redis-<version>\n./src/redis-server redis.conf","section_id":"6-start-redis"},{"id":"6-start-redis-ex1","language":"bash","code":"cd /usr/src/redis-<version>\n./src/redis-cli INFO\n...\n# Modules\nmodule:name=ReJSON,ver=20803,api=1,filters=0,usedby=[search],using=[],options=[handle-io-errors]\nmodule:name=search,ver=21005,api=1,filters=0,usedby=[],using=[ReJSON],options=[handle-io-errors]\nmodule:name=bf,ver=20802,api=1,filters=0,usedby=[],using=[],options=[]\nmodule:name=timeseries,ver=11202,api=1,filters=0,usedby=[],using=[],options=[handle-io-errors]\nmodule:name=RedisCompat,ver=1,api=1,filters=0,usedby=[],using=[],options=[]\nmodule:name=vectorset,ver=1,api=1,filters=0,usedby=[],using=[],options=[]\n...","section_id":"6-start-redis"},{"id":"7-optional-install-redis-to-its-default-location-ex0","language":"bash","code":"cd /usr/src/redis-<version>\nsudo make install","section_id":"7-optional-install-redis-to-its-default-location"}]}
{"schema_version":2,"id":"operate/oss_and_stack/install/build-stack/ubuntu-noble","title":"Build and run Redis Open Source on Ubuntu 24.04 (Noble)","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/build-stack/ubuntu-noble/","summary":"","tags":["docs","operate","stack","oss"],"last_updated":"2026-09-21T15:59:08+01:00","page_type":"content","content_hash":"8242582bb7727cdb26206a24871316aafc7310206c921ab2fec7d0d80b4a1539","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Follow the steps below to build and run Redis Open Source with all data structures from its source code on a system running Ubuntu 24.04 (Noble).\n\n> [!NOTE]\n> Docker images used to produce these build notes:\n> - ubuntu:24.04"},{"id":"1-install-required-dependencies","title":"1. Install required dependencies","role":"content","text":"Update your package lists and install the necessary development tools and libraries:\n\n[code example]"},{"id":"2-download-and-extract-the-redis-source","title":"2. Download and extract the Redis source","role":"content","text":"The Redis source code is available from [the Redis GitHub site](https://github.com/redis/redis/releases). Select the release you want to build and then select the .tar.gz file from the **Assets** drop down menu. You can verify the integrity of these downloads by checking them against the digests in the [redis-hashes GitHub repository](https://github.com/redis/redis-hashes).\n\nCopy the tar(1) file to `/usr/src`.\n\nAlternatively, you can download the file directly using the `wget` command, as shown below.\n\n[code example]\n\nReplace `<version>` with the three-digit Redis release number, for example `8.0.0`.\n\nExtract the source:\n\n[code example]"},{"id":"3-build-redis","title":"3. Build Redis","role":"content","text":"Set the necessary environment variables, and build Redis with TLS and module support:\n\n[code example]"},{"id":"4-optional-verify-the-installation","title":"4. (Optional) Verify the installation","role":"content","text":"Check the built Redis server and CLI versions:\n\n[code example]"},{"id":"5-start-redis","title":"5. Start Redis","role":"content","text":"To start Redis, use the following command:\n\n[code example]\n\n> [!NOTE]\n> Before Redis 8.10, specify `redis-full.conf` instead of `redis.conf`.\n\nTo validate that the available modules have been installed, run the [`INFO`](https://redis.io/docs/latest/commands/info) command and look for lines similar to the following:\n\n[code example]"},{"id":"6-optional-install-redis-to-its-default-location","title":"6. (Optional) Install Redis to its default location","role":"content","text":"[code example]"}],"examples":[{"id":"1-install-required-dependencies-ex0","language":"bash","code":"apt-get update\napt-get install -y sudo\nsudo apt-get install -y --no-install-recommends \\\n    ca-certificates \\\n    wget \\\n    dpkg-dev \\\n    gcc \\\n    g++ \\\n    libc6-dev \\\n    libssl-dev \\\n    make \\\n    git \\\n    cmake \\\n    python3 \\\n    python3-pip \\\n    python3-venv \\\n    python3-dev \\\n    unzip \\\n    rsync \\\n    clang \\\n    automake \\\n    autoconf \\\n    libtool","section_id":"1-install-required-dependencies"},{"id":"2-download-and-extract-the-redis-source-ex0","language":"bash","code":"cd /usr/src\nwget -O redis-<version>.tar.gz https://github.com/redis/redis/archive/refs/tags/<version>.tar.gz","section_id":"2-download-and-extract-the-redis-source"},{"id":"2-download-and-extract-the-redis-source-ex1","language":"bash","code":"cd /usr/src\ntar xvf redis-<version>.tar.gz\nrm redis-<version>.tar.gz","section_id":"2-download-and-extract-the-redis-source"},{"id":"3-build-redis-ex0","language":"bash","code":"cd /usr/src/redis-<version>\nexport BUILD_TLS=yes\nexport BUILD_WITH_MODULES=yes\nexport INSTALL_RUST_TOOLCHAIN=yes\nmake -j \"$(nproc)\" all","section_id":"3-build-redis"},{"id":"4-optional-verify-the-installation-ex0","language":"bash","code":"cd /usr/src/redis-<version>\n./src/redis-server --version\n./src/redis-cli --version","section_id":"4-optional-verify-the-installation"},{"id":"5-start-redis-ex0","language":"bash","code":"cd /usr/src/redis-<version>\n./src/redis-server redis.conf","section_id":"5-start-redis"},{"id":"5-start-redis-ex1","language":"bash","code":"cd /usr/src/redis-<version>\n./src/redis-cli INFO\n...\n# Modules\nmodule:name=ReJSON,ver=20803,api=1,filters=0,usedby=[search],using=[],options=[handle-io-errors]\nmodule:name=search,ver=21005,api=1,filters=0,usedby=[],using=[ReJSON],options=[handle-io-errors]\nmodule:name=bf,ver=20802,api=1,filters=0,usedby=[],using=[],options=[]\nmodule:name=timeseries,ver=11202,api=1,filters=0,usedby=[],using=[],options=[handle-io-errors]\nmodule:name=RedisCompat,ver=1,api=1,filters=0,usedby=[],using=[],options=[]\nmodule:name=vectorset,ver=1,api=1,filters=0,usedby=[],using=[],options=[]\n...","section_id":"5-start-redis"},{"id":"6-optional-install-redis-to-its-default-location-ex0","language":"bash","code":"cd /usr/src/redis-<version>\nsudo make install","section_id":"6-optional-install-redis-to-its-default-location"}]}
{"schema_version":2,"id":"operate/oss_and_stack/install/build-stack/ubuntu-resolute","title":"Build and run Redis Open Source on Ubuntu 26.04 (Resolute)","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/build-stack/ubuntu-resolute/","summary":"","tags":["docs","operate","stack","oss"],"last_updated":"2026-09-21T15:59:08+01:00","page_type":"content","content_hash":"075eb697e9a7a2152b10248eb381cd5dbf8884e582bc2d48b4053402dee8ecf3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Follow the steps below to build and run Redis Open Source with all data structures from its source code on a system running Ubuntu 26.04 (Resolute).\n\n> [!NOTE]\n> Docker image used to produce these build notes:\n> - ubuntu:26.04\n>\n> Ubuntu 26.04 ships CMake 4.x and clang/LLVM 21 in the default repositories. The Redis modules build requires CMake 3.31.6 or earlier and explicitly passes `-fuse-ld=lld`, so a supported CMake must be pinned with `pip3`, and `lld`, `llvm`, and `libcrypt-dev` must be installed. (`libcrypt-dev` is needed to link the `redisearch` module against `libcrypt`.)"},{"id":"1-install-required-dependencies","title":"1. Install required dependencies","role":"content","text":"Update your package lists and install the necessary development tools and libraries. `lld` and `llvm` are required because the modules build invokes clang with `-fuse-ld=lld` and uses `llvm-ar`; `libcrypt-dev` is required to link `redisearch.so`:\n\n[code example]"},{"id":"2-install-cmake","title":"2. Install CMake","role":"content","text":"Install a supported version of CMake using `pip3` inside a virtual environment (Ubuntu enforces [PEP 668](https://peps.python.org/pep-0668/)) and link it for system-wide access.\n\n> [!WARNING]\n> CMake version 3.31.6 is the latest supported version. Newer versions cannot be used.\n\n[code example]"},{"id":"3-download-and-extract-the-redis-source","title":"3. Download and extract the Redis source","role":"content","text":"The Redis source code is available from [the Redis GitHub site](https://github.com/redis/redis/releases). Select the release you want to build and then select the .tar.gz file from the **Assets** drop down menu. You can verify the integrity of these downloads by checking them against the digests in the [redis-hashes GitHub repository](https://github.com/redis/redis-hashes).\n\nCopy the tar(1) file to `/usr/src`.\n\nAlternatively, you can download the file directly using the `wget` command, as shown below.\n\n[code example]\n\nReplace `<version>` with the three-digit Redis release number, for example `8.0.0`.\n\nExtract the source:\n\n[code example]"},{"id":"4-build-redis","title":"4. Build Redis","role":"content","text":"Set the necessary environment variables, and build Redis with TLS and module support:\n\n[code example]"},{"id":"5-optional-verify-the-installation","title":"5. (Optional) Verify the installation","role":"content","text":"Check the built Redis server and CLI versions:\n\n[code example]"},{"id":"6-start-redis","title":"6. Start Redis","role":"content","text":"To start Redis, use the following command:\n\n[code example]\n\n> [!NOTE]\n> Before Redis 8.10, specify `redis-full.conf` instead of `redis.conf`.\n\nTo validate that the available modules have been installed, run the [`INFO`](https://redis.io/docs/latest/commands/info) command and look for lines similar to the following:\n\n[code example]"},{"id":"7-optional-install-redis-to-its-default-location","title":"7. (Optional) Install Redis to its default location","role":"content","text":"[code example]"}],"examples":[{"id":"1-install-required-dependencies-ex0","language":"bash","code":"apt-get update\napt-get install -y sudo\nsudo apt-get install -y --no-install-recommends \\\n    ca-certificates \\\n    wget \\\n    dpkg-dev \\\n    gcc \\\n    g++ \\\n    libc6-dev \\\n    libssl-dev \\\n    libcrypt-dev \\\n    make \\\n    git \\\n    python3 \\\n    python3-pip \\\n    python3-venv \\\n    python3-dev \\\n    unzip \\\n    rsync \\\n    clang \\\n    lld \\\n    llvm \\\n    automake \\\n    autoconf \\\n    libtool","section_id":"1-install-required-dependencies"},{"id":"2-install-cmake-ex0","language":"bash","code":"python3 -m venv /opt/cmake-venv\n/opt/cmake-venv/bin/pip install cmake==3.31.6\nsudo ln -sf /opt/cmake-venv/bin/cmake /usr/local/bin/cmake\ncmake --version","section_id":"2-install-cmake"},{"id":"3-download-and-extract-the-redis-source-ex0","language":"bash","code":"cd /usr/src\nwget -O redis-<version>.tar.gz https://github.com/redis/redis/archive/refs/tags/<version>.tar.gz","section_id":"3-download-and-extract-the-redis-source"},{"id":"3-download-and-extract-the-redis-source-ex1","language":"bash","code":"cd /usr/src\ntar xvf redis-<version>.tar.gz\nrm redis-<version>.tar.gz","section_id":"3-download-and-extract-the-redis-source"},{"id":"4-build-redis-ex0","language":"bash","code":"cd /usr/src/redis-<version>\nexport BUILD_TLS=yes\nexport BUILD_WITH_MODULES=yes\nexport INSTALL_RUST_TOOLCHAIN=yes\nmake -j \"$(nproc)\" all","section_id":"4-build-redis"},{"id":"5-optional-verify-the-installation-ex0","language":"bash","code":"cd /usr/src/redis-<version>\n./src/redis-server --version\n./src/redis-cli --version","section_id":"5-optional-verify-the-installation"},{"id":"6-start-redis-ex0","language":"bash","code":"cd /usr/src/redis-<version>\n./src/redis-server redis.conf","section_id":"6-start-redis"},{"id":"6-start-redis-ex1","language":"bash","code":"cd /usr/src/redis-<version>\n./src/redis-cli INFO\n...\n# Modules\nmodule:name=ReJSON,ver=20803,api=1,filters=0,usedby=[search],using=[],options=[handle-io-errors]\nmodule:name=search,ver=21005,api=1,filters=0,usedby=[],using=[ReJSON],options=[handle-io-errors]\nmodule:name=bf,ver=20802,api=1,filters=0,usedby=[],using=[],options=[]\nmodule:name=timeseries,ver=11202,api=1,filters=0,usedby=[],using=[],options=[handle-io-errors]\nmodule:name=RedisCompat,ver=1,api=1,filters=0,usedby=[],using=[],options=[]\nmodule:name=vectorset,ver=1,api=1,filters=0,usedby=[],using=[],options=[]\n...","section_id":"6-start-redis"},{"id":"7-optional-install-redis-to-its-default-location-ex0","language":"bash","code":"cd /usr/src/redis-<version>\nsudo make install","section_id":"7-optional-install-redis-to-its-default-location"}]}
{"schema_version":2,"id":"operate/oss_and_stack/install","title":"Install Redis Open Source","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/","summary":"How to install Redis Open Source on your target platform","aliases":["/install/","/getting-started/installation/"],"tags":["docs","operate","stack","oss"],"last_updated":"2026-09-30T13:16:11-07:00","children":[{"id":"operate/oss_and_stack/install/install-stack","summary":"Install Redis Open Source on Docker, Linux, macOS, and Windows (using Docker only)","title":"Install Redis Open Source","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/"},{"id":"operate/oss_and_stack/install/upgrade","summary":"Upgrade Redis Open Source","title":"Upgrade Redis Open Source","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/upgrade/"},{"id":"operate/oss_and_stack/install/build-stack","summary":"Build and run Redis 8 in Redis Open Source on Linux and macOS","title":"Build and run Redis Open Source","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/build-stack/"},{"id":"operate/oss_and_stack/install/version-mgmt","summary":"Describes Redis Open Source supported database versions.","title":"Redis Open Source version management","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/version-mgmt/"},{"id":"operate/oss_and_stack/install/archive","summary":"How to install your preferred Redis flavor on your target platform","title":"Install Redis Community Edition or Redis Stack","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/archive/"}],"page_type":"content","content_hash":"3799580cf75e3cf6143133e17174ccd8a5179cca9e9506b0cadfe39f592dd794","sections":[{"id":"content","title":"Content","role":"content","text":"You can install [Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack) locally on your machine. Redis Open Source is available on Docker, Linux, macOS, and Windows (using Docker only).\n\nYou can also download [Redis Insight](https://redis.io/docs/latest/operate/redisinsight/install), a free developer companion tool with an intuitive GUI and advanced CLI, which you can use alongside Redis Open Source.\n\nHere are the installation and upgrade instructions:\n\n* [Install Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack)\n* [Upgrade Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack/install/upgrade)\n\nWhile you can install Redis Open Source locally, you might also consider using Redis Cloud by creating a [free account](https://redis.com/try-free/?utm_source=redisio&utm_medium=referral&utm_campaign=2023-09-try_free&utm_content=cu-redis_cloud_users)."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/install/install-stack/apt","title":"Install Redis Open Source on Linux","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/apt/","summary":"How to install Redis Open Source using APT","tags":["docs","operate","stack","oss"],"last_updated":"2026-09-21T15:59:08+01:00","page_type":"content","content_hash":"61d9af5eccdbf238faedc31096fd216749a88255ed50cbd641ab1b949f7a3dc5","sections":[{"id":"install-redis-open-source-on-ubuntu-or-debian-linux-using-apt","title":"Install Redis Open Source on Ubuntu or Debian Linux using APT","role":"setup","text":"> [!NOTE]\n> If you only need the Redis CLI (`redis-cli`) and not the full Redis Open Source distribution, see [Install redis-cli](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/install-redis-cli).\n\nAdd the repository to the APT index, update it, and install Redis Open Source:\n\n\nsudo apt-get install lsb-release curl gpg\ncurl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg\nsudo chmod 644 /usr/share/keyrings/redis-archive-keyring.gpg\necho \"deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main\" | sudo tee /etc/apt/sources.list.d/redis.list\nsudo apt-get update\nsudo apt-get install redis\n\n\nThe most recent version of Redis Open Source will be installed, along with the redis-tools package (redis-cli, etc.).\nIf you need to install an earlier version, run the following command to list the available versions:\n\n\napt policy redis\n\nredis:\n  Installed: (none)\n  Candidate: 6:8.6.1-1rl1~noble1\n  Version table:\n     6:8.6.1-1rl1~noble1 500\n        500 https://packages.redis.io/deb noble/main amd64 Packages\n        500 https://packages.redis.io/deb noble/main all Packages\n     6:8.6.0-1rl1~noble1 500\n        500 https://packages.redis.io/deb noble/main amd64 Packages\n        500 https://packages.redis.io/deb noble/main all Packages\n     ...\n     6:8.0.0-1rl1~noble1 500\n        500 https://packages.redis.io/deb noble/main amd64 Packages\n        500 https://packages.redis.io/deb noble/main all Packages\n     6:7.4.8-1rl1~noble1 500\n        500 https://packages.redis.io/deb noble/main amd64 Packages\n        500 https://packages.redis.io/deb noble/main all Packages\n\n\nFor example, to install Redis Open Source v7.4.8 on Ubuntu LTS 24.04 (Noble Numbat), run the following command:\n\n\nsudo apt-get install \\\nredis=6:7.4.8-1rl1~noble1 \\\nredis-server=6:7.4.8-1rl1~noble1 \\\nredis-sentinel=6:7.4.8-1rl1~noble1 \\\nredis-tools=6:7.4.8-1rl1~noble1\n\n\nAlternatively, you can also set up a preferences file to pin a particular release:\n\n\nPackage: redis redis-server redis-sentinel redis-tools\nPin: version 6:7.4.*\nPin-Priority: 1001\n\n\nSee [this document](https://manpages.debian.org/buster/apt/apt_preferences.5.en.html#How_APT_Interprets_Priorities) for more information on `Pin-Priority`.\n\nWith the example preferences file give above, `6:7.4.8-1rl1~noble1` is the latest version that matches the pinned version and it will be installed when you run this command:\n\n\nsudo apt-get install redis-server redis-sentinel redis-tools\n\n\nRedis should start automatically after the initial installation and also at boot time.\nShould that not be the case on your system, run the following commands:\n\n\nsudo systemctl enable redis-server\nsudo systemctl start redis-server"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/install/install-stack/docker","title":"Run Redis Open Source on Docker","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/docker/","summary":"How to run Redis Open Source using Docker","tags":["docs","operate","stack","oss"],"last_updated":"2026-09-21T15:59:08+01:00","page_type":"content","content_hash":"96e5de686318ad53f642095ef3e23905efc33e59e66cd68652b33849a31c606f","sections":[{"id":"install-docker","title":"Install Docker","role":"setup","text":"Follow the Docker installation instructions for your operating system:\n\n- [Linux](https://docs.docker.com/desktop/setup/install/linux/)\n- [macOS](https://docs.docker.com/docker-for-mac/install/)\n- [Windows](https://store.docker.com/editions/community/docker-ce-desktop-windows)\n\n> [!NOTE]\n> On Windows, make sure Docker is configured to run Linux-based containers."},{"id":"run-redis-open-source-on-docker","title":"Run Redis Open Source on Docker","role":"content","text":"To start the Redis Open Source server using the `redis:<version>` image, run the following command in your terminal:\n\n\ndocker run -d --name redis -p 6379:6379 redis:<version>"},{"id":"connect-with-redis-cli","title":"Connect with redis-cli","role":"content","text":"You can then connect to the server using `redis-cli`, just as you connect to any Redis instance.\n\nIf you don’t have `redis-cli` installed locally, you can run it from the Docker container:\n\n\n$ docker exec -it redis redis-cli\n\n\nIf you want to install `redis-cli` locally, see\n[Install redis-cli](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/install-redis-cli).\n\nIf you already have `redis-cli` installed locally, you can run it from your terminal:\n\n\n$ redis-cli -h 127.0.0.1 -p 6379"},{"id":"use-a-local-configuration-file","title":"Use a local configuration file","role":"content","text":"By default, the Redis Docker containers use internal configuration files for Redis. To start Redis with local configuration file, you can do one of the following:\n\nYou can create your own Dockerfile that adds a `redis.conf` from the context into `/data/`, like so.\n\n[code example]\nAlternatively, you can specify something along the same lines with docker run options.\n\n\n$ docker run -v /myredis/conf:/usr/local/etc/redis --name myredis redis redis-server /usr/local/etc/redis/redis.conf\n\n\nwhere `/myredis/conf/` is a local directory containing your `redis.conf` file. Using this method means that there is no need for you to have a Dockerfile for your redis container.\n\nThe mapped directory should be writable, as depending on the configuration and mode of operation, Redis may need to create additional configuration files or rewrite existing ones."},{"id":"use-local-storage-for-data-persistence","title":"Use local storage for data persistence","role":"content","text":"To mount directories or files to your Docker container, specify -v to configure a local volume. This command stores all data in the local directory local-data:\n\n\n$ docker run -v /local-data/:/data --name redis -p 6379:6379 redis:<version>\n\n\nSee the [official Redis page on Docker Hub](https://hub.docker.com/_/redis) for more options."}],"examples":[{"id":"use-a-local-configuration-file-ex0","language":"plaintext","code":"FROM redis\nCOPY redis.conf /usr/local/etc/redis/redis.conf\nCMD [ \"redis-server\", \"/usr/local/etc/redis/redis.conf\" ]","section_id":"use-a-local-configuration-file"}]}
{"schema_version":2,"id":"operate/oss_and_stack/install/install-stack/homebrew","title":"Install Redis Open Source on macOS","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/homebrew/","summary":"How to install Redis Open Source on macOS using Homebrew","aliases":["/install/install-redis/install-redis-on-mac-os/"],"tags":["docs","operate","stack","oss"],"last_updated":"2026-10-05T10:55:35+01:00","page_type":"content","content_hash":"31ebaf30c42175fb27a8b0c524c92627426662da29cac605d992507251d16b95","sections":[{"id":"install-redis-open-source-on-macos-using-homebrew","title":"Install Redis Open Source on macOS using Homebrew","role":"setup","text":"> [!NOTE]\n> Installation using Homebrew is only supported on macOS.\n\n> [!NOTE]\n> If you only need the Redis CLI (`redis-cli`) and not the full Redis Open Source distribution, see [Install redis-cli](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/install-redis-cli).\n\nTo install Redis Open Source on macOS, use the [Homebrew](https://brew.sh/) formula called `redis`.\nMake sure that you have [Homebrew installed](https://docs.brew.sh/Installation) before you start.\n\nThe formula is the preferred way to install Redis Open Source on macOS. A [Homebrew cask](#alternative-install-using-the-redis-cask) is also available, but it is not integrated with `brew services` and requires a separate tap."},{"id":"install-using-homebrew","title":"Install using Homebrew","role":"setup","text":"Run `brew install`:\n\n\nbrew install redis\n\n\nThis installs the latest Redis Open Source release, including Redis Search and the JSON, time series, probabilistic, and vector set data structures.\n\n> [!NOTE]\n> The formula always installs the latest Redis Open Source release. Support for Redis Search and the additional data structures was added to the formula in Redis Open Source 8.10.1, so earlier versions installed through the formula do not include them. To install an earlier version, use one of the other [installation methods](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack).\n\nIf you already have Redis installed through Homebrew, see [Upgrade an existing installation](#upgrade-an-existing-installation) instead."},{"id":"upgrade-an-existing-installation","title":"Upgrade an existing installation","role":"content","text":""},{"id":"upgrade-from-an-earlier-version-of-the-formula","title":"Upgrade from an earlier version of the formula","role":"content","text":"If you previously installed Redis using the `redis` formula, upgrade it with the following command:\n\n\nbrew upgrade redis\n\n\nHomebrew does not overwrite a configuration file that you might have changed. It leaves your existing `redis.conf` in place and writes the new default configuration to `redis.conf.default`. Because the older configuration file does not contain the `loadmodule` directives, Redis Search and the additional data structures stay disabled until you use the new default.\n\n1. Compare your configuration file with the new default:\n    [code example]\n1. Back up your configuration file:\n    [code example]\n1. Replace your configuration file with the new default:\n    [code example]\n\n    If you had customized any settings, reapply them to the new file.\n\n1. Restart Redis, then check the results as described in [Verify that all modules are loaded correctly](#verify-that-all-modules-are-loaded-correctly)."},{"id":"migrate-from-the-redis-cask","title":"Migrate from the Redis cask","role":"content","text":"If you previously installed Redis Open Source using the Redis Homebrew cask, remove it before you install the formula. Both provide the `redis-server` and `redis-cli` binaries in the same location.\n\n1. Uninstall the cask:\n    [code example]\n1. Remove the tap:\n    [code example]\n1. Check if the `redis.conf` file is still installed:\n    [code example]\n\n    If you get output similar to the following, then it's still there:\n\n    [code example]\n\n    Run this command to remove the file:\n\n    [code example]\n\n    The configuration file installed by the cask loads the modules from a directory that the cask removes when you uninstall it. If you leave the file in place, `redis-server` fails to start, and Homebrew does not replace it.\n\nNext, follow the instructions in [Install using Homebrew](#install-using-homebrew)."},{"id":"run-redis","title":"Run Redis","role":"content","text":"If this is the first time you've installed Redis on your system, you need to be sure that your `PATH` variable includes the Redis installation location. This location is either `/opt/homebrew/bin` for Apple silicon Macs or `/usr/local/bin` for Intel-based Macs.\n\nTo check this, run:\n\n\necho $PATH\n\n\nNext, confirm that the output contains `/opt/homebrew/bin` (Apple silicon Macs) or `/usr/local/bin` (Intel Mac). If neither `/opt/homebrew/bin` nor `/usr/local/bin` are in the output, add them.\n\nOpen the file `~/.bashrc` or `~/.zshrc` (depending on your shell), and add the following line.\n\n\nexport PATH=$(brew --prefix)/bin:$PATH\n\n\nYou can now start Redis in one of two ways.\n\nTo start Redis now and restart it at login, run it as a Homebrew service:\n\n\nbrew services start redis\n\n\nThe service runs `redis-server` with the configuration file at `$(brew --prefix)/etc/redis.conf`.\n\nIf you don't need a background service, start the server directly:\n\n\nredis-server $(brew --prefix)/etc/redis.conf\n\n\nThe server will run in the background."},{"id":"connect-to-redis","title":"Connect to Redis","role":"content","text":"Once Redis is running, you can test it by running `redis-cli`:\n\n\nredis-cli\n\n\nTest the connection with the `ping` command:\n\n\n127.0.0.1:6379> PING\nPONG"},{"id":"verify-that-all-modules-are-loaded-correctly","title":"Verify that all modules are loaded correctly","role":"content","text":"If you upgraded from an earlier Redis installation, for example 7.2.x or 7.4.x, test to see if all the modules are loaded correctly by running the following command. Your output should look similar to the following:\n\n\n\n\n$ redis-cli MODULE LIST\n1) 1) \"name\"\n   2) \"timeseries\"\n   3) \"ver\"\n   4) (integer) 80991\n   5) \"path\"\n   6) \"/usr/local/lib/redis/modules//redistimeseries.so\"\n   7) \"args\"\n   8) (empty array)\n2) 1) \"name\"\n   2) \"search\"\n   3) \"ver\"\n   4) (integer) 80990\n   5) \"path\"\n   6) \"/usr/local/lib/redis/modules//redisearch.so\"\n   7) \"args\"\n   8) (empty array)\n3) 1) \"name\"\n   2) \"bf\"\n   3) \"ver\"\n   4) (integer) 80990\n   5) \"path\"\n   6) \"/usr/local/lib/redis/modules//redisbloom.so\"\n   7) \"args\"\n   8) (empty array)\n4) 1) \"name\"\n   2) \"vectorset\"\n   3) \"ver\"\n   4) (integer) 1\n   5) \"path\"\n   6) \"\"\n   7) \"args\"\n   8) (empty array)\n5) 1) \"name\"\n   2) \"ReJSON\"\n   3) \"ver\"\n   4) (integer) 80990\n   5) \"path\"\n   6) \"/usr/local/lib/redis/modules//rejson.so\"\n   7) \"args\"\n   8) (empty array)\n\n\nIf the list is empty, your configuration file does not load the modules. See [Upgrade from an earlier version of the formula](#upgrade-from-an-earlier-version-of-the-formula)."},{"id":"stop-redis","title":"Stop Redis","role":"content","text":"If you started Redis as a Homebrew service, run:\n\n\nbrew services stop redis\n\n\nOtherwise, run:\n\n\nredis-cli SHUTDOWN"},{"id":"uninstall-redis-open-source","title":"Uninstall Redis Open Source","role":"content","text":"If you started Redis as a Homebrew service, stop the service first:\n\n\nbrew services stop redis\n\n\nTo uninstall Redis, run:\n\n\nbrew uninstall redis"},{"id":"alternative-install-using-the-redis-cask","title":"Alternative: install using the Redis cask","role":"content","text":"> [!NOTE]\n> The Homebrew formula described in [Install using Homebrew](#install-using-homebrew) is the preferred way to install Redis Open Source on macOS.\n\nRedis also provides a Homebrew cask. First, tap the Redis Homebrew cask:\n\n\nbrew tap redis/redis\n\n\nOn Homebrew 6.0 and later, you also need to trust the tap. This step is not required on earlier versions of Homebrew.\n\n\nbrew trust redis/redis\n\n\nNext, run `brew install`:\n\n\nbrew install --cask redis\n\n\nStart the server with the configuration file that the cask installs:\n\n\nredis-server $(brew --prefix)/etc/redis.conf\n\n\n> [!NOTE]\n> Because Redis is installed using a Homebrew cask with the `brew tap` command, it is not integrated with the `brew services` command.\n\nTo uninstall the cask, run:\n\n\nbrew uninstall --cask redis\nbrew untap redis/redis"},{"id":"next-steps","title":"Next steps","role":"content","text":"Once you have a running Redis instance, you may want to:\n\n* Try the [Redis CLI tutorial](https://redis.io/docs/latest/develop/tools/cli)\n* Connect using one of the [Redis clients](https://redis.io/docs/latest/develop/clients)\n* Connect using [Redis Insight](https://redis.io/docs/latest/develop/tools/insight)"}],"examples":[{"id":"upgrade-from-an-earlier-version-of-the-formula-ex0","language":"bash","code":"diff $(brew --prefix)/etc/redis.conf $(brew --prefix)/etc/redis.conf.default","section_id":"upgrade-from-an-earlier-version-of-the-formula"},{"id":"upgrade-from-an-earlier-version-of-the-formula-ex1","language":"bash","code":"cp $(brew --prefix)/etc/redis.conf $(brew --prefix)/etc/redis.conf.backup","section_id":"upgrade-from-an-earlier-version-of-the-formula"},{"id":"upgrade-from-an-earlier-version-of-the-formula-ex2","language":"bash","code":"cp $(brew --prefix)/etc/redis.conf.default $(brew --prefix)/etc/redis.conf","section_id":"upgrade-from-an-earlier-version-of-the-formula"},{"id":"migrate-from-the-redis-cask-ex0","language":"bash","code":"brew uninstall --cask redis","section_id":"migrate-from-the-redis-cask"},{"id":"migrate-from-the-redis-cask-ex1","language":"bash","code":"brew untap redis/redis","section_id":"migrate-from-the-redis-cask"},{"id":"migrate-from-the-redis-cask-ex2","language":"bash","code":"ls -l $(brew --prefix)/etc/redis.conf","section_id":"migrate-from-the-redis-cask"},{"id":"migrate-from-the-redis-cask-ex3","language":"bash","code":"-rw-r--r--@ 1 user  admin  122821  2 Oct 16:07 /opt/homebrew/etc/redis.conf","section_id":"migrate-from-the-redis-cask"},{"id":"migrate-from-the-redis-cask-ex4","language":"bash","code":"rm -iv $(brew --prefix)/etc/redis.conf","section_id":"migrate-from-the-redis-cask"}]}
{"schema_version":2,"id":"operate/oss_and_stack/install/install-stack","title":"Install Redis Open Source","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/","summary":"Install Redis Open Source on Docker, Linux, macOS, and Windows (using Docker only)","aliases":["/install/install-stack/","/install/install-stack/binaries/","/operate/oss_and_stack/install/install-stack/linux/","/install/install-redis/install-redis-on-linux/"],"tags":["docs","operate","stack","oss"],"last_updated":"2026-09-21T15:59:08+01:00","children":[{"id":"operate/oss_and_stack/install/install-stack/docker","summary":"How to run Redis Open Source using Docker","title":"Run Redis Open Source on Docker","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/docker/"},{"id":"operate/oss_and_stack/install/install-stack/apt","summary":"How to install Redis Open Source using APT","title":"Install Redis Open Source on Linux","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/apt/"},{"id":"operate/oss_and_stack/install/install-stack/rpm","summary":"How to install Redis Open Source using RPM","title":"Install Redis Open Source on Linux","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/rpm/"},{"id":"operate/oss_and_stack/install/install-stack/snap","summary":"How to install Redis Open Source using Snap","title":"Install Redis Open Source on Linux","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/snap/"},{"id":"operate/oss_and_stack/install/install-stack/homebrew","summary":"How to install Redis Open Source on macOS using Homebrew","title":"Install Redis Open Source on macOS","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/homebrew/"},{"id":"operate/oss_and_stack/install/install-stack/windows","summary":"How to run Redis Open Source on Windows","title":"Run Redis Open Source on Windows using Docker","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/windows/"},{"id":"operate/oss_and_stack/install/install-stack/install-redis-cli","summary":"How to install just the Redis CLI (redis-cli) without installing all of Redis","title":"Install redis-cli","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/install-redis-cli/"}],"page_type":"content","content_hash":"11ebdfb43812ad5717e24ec3d6f0a59953e208e4848109ceeb001ba57e73d922","sections":[{"id":"quick-start-with-docker","title":"Quick start with Docker","role":"overview","text":"The fastest way to run Redis is with Docker:\n\n\ndocker run -d --name redis -p 6379:6379 redis\n\n\nSee the [Docker page](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/docker) for full details, including data persistence, custom configuration, and connecting with `redis-cli`.\n\n---"},{"id":"install-on-your-platform","title":"Install on your platform","role":"setup","text":"<script>\n(function () {\n  function detectPlatformIndex() {\n    var ua = (navigator.userAgent || '').toLowerCase();\n    var pl = (navigator.platform || '').toLowerCase();\n    if (pl.indexOf('mac') !== -1 || ua.indexOf('mac os') !== -1) return 1;\n    if (pl.indexOf('win') !== -1 || ua.indexOf('windows') !== -1) return 2;\n    return 0;\n  }\n  document.addEventListener('DOMContentLoaded', function () {\n    var idx = detectPlatformIndex();\n    if (idx > 0) {\n      var radio = document.getElementById('install-by-platform-tab-' + idx);\n      if (radio) radio.checked = true;\n    }\n  });\n})();\n</script>\n\n**Linux:**\n\n#### APT — Ubuntu / Debian\n\n[code example]\n\n[Full APT installation guide](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/apt)\n\n---\n\n#### RPM — Rocky Linux / AlmaLinux\n\nCreate `/etc/yum.repos.d/redis.repo` (use `rockylinux9` or `rockylinux8` as appropriate):\n\n[code example]\n\nThen run:\n\n[code example]\n\n[Full RPM installation guide](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/rpm)\n\n---\n\n#### Snap — Ubuntu\n\n[code example]\n\n[Full Snap installation guide](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/snap)\n\n---\n\n#### Docker\n\n[code example]\n\n[Full Docker installation guide](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/docker)\n\n**macOS:**\n\n#### Homebrew\n\n[code example]\n\nStart Redis:\n\n[code example]\n\n[Full Homebrew installation guide](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/homebrew)\n\n---\n\n#### Docker\n\n[code example]\n\n[Full Docker installation guide](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/docker)\n\n**Windows:**\n\n#### Docker (PowerShell)\n\nMake sure Docker Desktop is installed and configured to run Linux containers, then run in PowerShell:\n\n[code example]\n\n[Full Windows / Docker guide](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/windows)\n\n---\n\n#### Memurai (preview)\n\nA preview of Redis Open Source 8.2 RC1 is available on Windows through [Memurai](https://www.memurai.com/get-memurai), the official Redis partner for Windows compatibility.\n\n\n\n---\n\nThe latest version of Redis Open Source has been tested on the following platforms:\n\n- Ubuntu 22.04 (Jammy Jellyfish), 24.04 (Noble Numbat), and 26.04 (Resolute Raccoon).\n- Rocky Linux 8.10, 9.7, and 10.1.\n- AlmaLinux 8.10, 9.7, and 10.1.\n- Debian 12.13 (Bookworm) and Debian 13.4 (Trixie).\n- Alpine 3.23.\n- macOS 14.8.4 (Sonoma), 15.7.4 (Sequoia), and 26.3 (Tahoe) - for both Intel and ARM processors.\n\nWhile you can install Redis Open Source on any of the platforms listed below, you might also consider using Redis Cloud by creating a [free account](https://redis.com/try-free/?utm_source=redisio&utm_medium=referral&utm_campaign=2023-09-try_free&utm_content=cu-redis_cloud_users).\n\nYou can also download [Redis Insight](https://redis.io/docs/latest/operate/redisinsight/install), a free developer companion tool with an intuitive GUI and advanced CLI, which you can use alongside Redis Open Source.\n\nIf you only need the Redis CLI (`redis-cli`) to connect to a remote Redis server, and not the full Redis Open Source distribution, see [Install redis-cli](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/install-redis-cli).\n\n<hr/>"}],"examples":[{"id":"install-on-your-platform-ex0","language":"bash","code":"sudo apt-get install lsb-release curl gpg\ncurl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg\nsudo chmod 644 /usr/share/keyrings/redis-archive-keyring.gpg\necho \"deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main\" | sudo tee /etc/apt/sources.list.d/redis.list\nsudo apt-get update\nsudo apt-get install redis","section_id":"install-on-your-platform"},{"id":"install-on-your-platform-ex1","language":"ini","code":"[Redis]\nname=Redis\nbaseurl=http://packages.redis.io/rpm/rockylinux9\nenabled=1\ngpgcheck=1","section_id":"install-on-your-platform"},{"id":"install-on-your-platform-ex2","language":"bash","code":"curl -fsSL https://packages.redis.io/gpg > /tmp/redis.key\nsudo rpm --import /tmp/redis.key\nsudo yum install redis","section_id":"install-on-your-platform"},{"id":"install-on-your-platform-ex3","language":"bash","code":"sudo apt update\nsudo apt install redis-tools\nsudo snap install redis","section_id":"install-on-your-platform"},{"id":"install-on-your-platform-ex4","language":"bash","code":"docker run -d --name redis -p 6379:6379 redis","section_id":"install-on-your-platform"},{"id":"install-on-your-platform-ex5","language":"bash","code":"brew install redis","section_id":"install-on-your-platform"},{"id":"install-on-your-platform-ex6","language":"bash","code":"brew services start redis","section_id":"install-on-your-platform"},{"id":"install-on-your-platform-ex7","language":"bash","code":"docker run -d --name redis -p 6379:6379 redis","section_id":"install-on-your-platform"},{"id":"install-on-your-platform-ex8","language":"powershell","code":"docker run -d --name redis -p 6379:6379 redis","section_id":"install-on-your-platform"}]}
{"schema_version":2,"id":"operate/oss_and_stack/install/install-stack/install-redis-cli","title":"Install redis-cli","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/install-redis-cli/","summary":"How to install just the Redis CLI (redis-cli) without installing all of Redis","tags":["docs","operate","stack","oss"],"last_updated":"2026-09-21T15:59:08+01:00","page_type":"content","content_hash":"b570e0b845f256d60435dc0a09fa6fa56f1d476f13b2e5a67dd9bc2b47e9b48d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"If you only need the [Redis CLI](https://redis.io/docs/latest/develop/tools/cli) (`redis-cli`) to connect to a remote Redis server, you can install it on its own, without installing the full Redis Open Source distribution or building it from source.\n\nThe installer downloads a single, statically linked `redis-cli` binary, so it works even on minimal images (such as Amazon Linux, openSUSE, or distroless). It requires only `curl` or `wget`.\n\n> [!NOTE]\n> This installation method is supported on **Linux** and **macOS** only, on `x86_64`/`amd64` and `arm64`/`aarch64` processors. It does not run on native Windows. You have two options:\n>\n> 1. You can run it under the [Windows Subsystem for Linux (WSL)](https://learn.microsoft.com/windows/wsl/).\n> 1. You can use it with Docker; see [Run Redis Open Source on Docker](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/docker) for more information."},{"id":"install-redis-cli","title":"Install redis-cli","role":"setup","text":"Run the following command:\n\n\ncurl -fsSL https://packages.redis.io/redis-cli/install.sh | sh\n\n\nThe script detects your operating system and architecture, downloads the matching `redis-cli` binary, verifies its SHA-256 checksum, and installs it to `/usr/local/bin` (using `sudo` if required). If `/usr/local/bin` is not writable, it installs to `~/.local/bin` instead.\n\n> [!NOTE]\n> As with any `curl ... | sh` command, review the [install script](https://packages.redis.io/redis-cli/install.sh) before running it if you want to see exactly what it does."},{"id":"install-a-specific-version","title":"Install a specific version","role":"setup","text":"By default, the script installs the latest stable release. To install a particular version, set the `REDIS_CLI_VERSION` environment variable:\n\n\ncurl -fsSL https://packages.redis.io/redis-cli/install.sh | REDIS_CLI_VERSION=8.8.0 sh"},{"id":"choose-the-installation-directory","title":"Choose the installation directory","role":"content","text":"To install `redis-cli` to a location of your choice, set the `REDIS_CLI_INSTALL_DIR` environment variable:\n\n\ncurl -fsSL https://packages.redis.io/redis-cli/install.sh | REDIS_CLI_INSTALL_DIR=\"$HOME/bin\" sh\n\n\nIf the installation directory is not on your `PATH`, the script prints a note reminding you to add it."},{"id":"connect-to-redis","title":"Connect to Redis","role":"content","text":"Once `redis-cli` is installed, you can use it to connect to any Redis instance. For example, to connect to a server running on `localhost` on the default port:\n\n\nredis-cli\n\n\nTo connect to a remote server, specify the host and port:\n\n\nredis-cli -h <host> -p <port>\n\n\nTest the connection with the `ping` command:\n\n\n127.0.0.1:6379> PING\nPONG"},{"id":"next-steps","title":"Next steps","role":"content","text":"- Try the [Redis CLI tutorial](https://redis.io/docs/latest/develop/tools/cli).\n- If you need a full Redis installation, see the other [installation guides](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack)."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/install/install-stack/rpm","title":"Install Redis Open Source on Linux","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/rpm/","summary":"How to install Redis Open Source using RPM","tags":["docs","operate","stack","oss"],"last_updated":"2026-09-21T15:59:08+01:00","page_type":"content","content_hash":"900a3bab2b04a19faa90b30687e7097e5f2e6b922a40174c94fb335213ccab8e","sections":[{"id":"install-redis-open-source-on-rocky-linux-8-and-9-or-almalinux-8-and-9-using-rpm","title":"Install Redis Open Source on Rocky Linux 8 and 9, or AlmaLinux 8 and 9 using RPM","role":"setup","text":"> [!NOTE]\n> If you only need the Redis CLI (`redis-cli`) and not the full Redis Open Source distribution, see [Install redis-cli](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/install-redis-cli).\n\nFollow these steps to install Redis Open Source.\n\n1. Create the file `/etc/yum.repos.d/redis.repo` with the following contents.\n\n    - For Rocky Linux 9 and AlmaLinux 9\n    \n    [Redis]\n    name=Redis\n    baseurl=http://packages.redis.io/rpm/rockylinux9\n    enabled=1\n    gpgcheck=1\n    \n\n    - For Rocky Linux 8 and AlmaLinux 8\n    \n    [Redis]\n    name=Redis\n    baseurl=http://packages.redis.io/rpm/rockylinux8\n    enabled=1\n    gpgcheck=1\n    \n\n2. Run the following commands:\n\n    \n    curl -fsSL https://packages.redis.io/gpg > /tmp/redis.key\n    sudo rpm --import /tmp/redis.key\n    sudo yum install redis\n    \n\nRedis will not start automatically, nor will it start at boot time. To do this, run the following commands.\n\n\nsudo systemctl enable redis\nsudo systemctl start redis"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/install/install-stack/snap","title":"Install Redis Open Source on Linux","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/snap/","summary":"How to install Redis Open Source using Snap","tags":["docs","operate","stack","oss"],"last_updated":"2026-09-21T15:59:08+01:00","page_type":"content","content_hash":"155835ca42daebc35b830be848cc37e95d54b40ce6132aa558ca9e3e890d5d0e","sections":[{"id":"install-redis-open-source-on-ubuntu-linux-using-snap","title":"Install Redis Open Source on Ubuntu Linux using Snap","role":"setup","text":"> [!NOTE]\n> If you only need the Redis CLI (`redis-cli`) and not the full Redis Open Source distribution, see [Install redis-cli](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/install-redis-cli).\n\nTo install Redis via snap, run the following commands:\n\n\nsudo apt update\nsudo apt install redis-tools # this will install `redis-cli`\nsudo snap install redis\n\n\nRedis will start automatically after installation and also at boot time."},{"id":"connect-to-redis","title":"Connect to Redis","role":"content","text":"Once Redis is running, you can test it by running `redis-cli`:\n\n\nredis-cli\n\n\nTest the connection with the `ping` command:\n\n\n127.0.0.1:6379> PING\nPONG"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/install/install-stack/windows","title":"Run Redis Open Source on Windows using Docker","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/windows/","summary":"How to run Redis Open Source on Windows","aliases":["/getting-started/installation/install-redis-on-windows/","/install/install-stack/windows/","/install/install-redis/install-redis-on-windows/"],"tags":["docs","operate","stack","oss"],"last_updated":"2026-09-21T15:59:08+01:00","page_type":"content","content_hash":"7e1f2dcf7abea5395e8ebc3d7115a8000284f248d2478701ceceb3e89174cf68","sections":[{"id":"run-redis-open-source-on-windows-using-docker","title":"Run Redis Open Source on Windows using Docker","role":"overview","text":"To install Redis Open Source on Windows, you will need to have Docker installed. When Docker is up and running, open Windows PowerShell and follow the instructions described in [Run Redis Open Source on Docker](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/docker). Then, use Docker to connect with `redis-cli` as explained in that topic."},{"id":"run-redis-on-windows-using-memurai","title":"Run Redis on Windows using Memurai","role":"content","text":"A preview of Redis Open Source 8.2 RC1 is available on Windows through [Memurai](https://www.memurai.com/get-memurai), the official Redis partner for Windows compatibility."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/install/upgrade/cluster","title":"Upgrade a Redis cluster to Redis 8","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/upgrade/cluster/","summary":"Upgrade a Redis Open Source cluster to Redis 8","tags":["docs","operate","stack","oss"],"last_updated":"2026-10-05T10:55:35+01:00","page_type":"content","content_hash":"2e1ea442580a14c727eddc6950ced81f711ddeea44800676161e126fbfc2bb28","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> The supported upgrade paths are:\n> - Redis Open Source 7.x (with or without modules) to Redis 8 in Redis Open Source\n> - Redis Stack 7.2 or 7.4 to Redis 8 in Redis Open Source\n\n> [!NOTE]\n> It's essential to practice upgrading Redis in a controlled environment before upgrading it in a production environment.\n> Docker is an excellent tool to use for this purpose.\n\nFollow these instructions to upgrade a Redis cluster. This guide assumes you have installed Redis using one of the supported methods listed [here](https://redis.io/docs/latest/operate/oss_and_stack/install/archive)."},{"id":"save-your-current-data","title":"Save your current data","role":"content","text":"Before upgrading, create a snapshot of your current dataset on each node (identified by its port) using the following command:\n\n[code example]\n\nRepeat for every node in your cluster, both masters and replicas.\n\nThis creates or updates an `RDB` file, for example `dump.rdb`, in the node's Redis data directory. If you use AOF persistence, the files will be named `appendonly.aof.*` and they will be written in the `appendonlydir` directory inside the data directory. The AOF-related directory and file names are the defaults. Use the names defined in your `redis.conf` file if different from the defaults.\n\nUse the following command on each node to identify where your data directories are located:\n\n[code example]\n\nMake a copy of the files contained in each of those directories before proceeding."},{"id":"upgrade-redis-nodes","title":"Upgrade Redis nodes","role":"content","text":"Upgrade each node one at a time, starting with the replicas, using these steps:\n\n1. Stop the current version of Redis Open Source or Redis Stack server.\n1. Follow the installation steps that correspond to your [Redis distribution](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack) to install Redis 8.\n1. Start Redis 8 if it didn't start automatically."},{"id":"restore-from-saved-files-if-necessary","title":"Restore from saved files (if necessary)","role":"content","text":"If necessary, restore the saved files to their original locations on each node. Then restart Redis on each node.\n\n> [!WARNING]\n> Redis Search won't be fully operational until all master shards have completed their upgrades.\n> Any queries sent during the upgrade process will either return errors or a message such as \"Waiting for all nodes to connect\"."},{"id":"verify-the-upgrade","title":"Verify the upgrade","role":"content","text":"[code example]\n\nYou should also verify that your data is accessible and that your clients can connect successfully."}],"examples":[{"id":"save-your-current-data-ex0","language":"bash","code":"redis-cli -p <port> SAVE","section_id":"save-your-current-data"},{"id":"save-your-current-data-ex1","language":"bash","code":"redis-cli -p <port> CONFIG GET dir","section_id":"save-your-current-data"},{"id":"verify-the-upgrade-ex0","language":"bash","code":"redis-cli -p <port> INFO server | grep redis_version\nredis-cli -p <port> cluster info\nredis-cli --cluster check <IP address>:<port>","section_id":"verify-the-upgrade"}]}
{"schema_version":2,"id":"operate/oss_and_stack/install/upgrade","title":"Upgrade Redis Open Source","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/upgrade/","summary":"Upgrade Redis Open Source","tags":["docs","operate","stack","oss"],"last_updated":"2025-05-01T06:50:58-07:00","children":[{"id":"operate/oss_and_stack/install/upgrade/standalone","summary":"Upgrade a standalone instance of Redis Open Source or Redis Stack to Redis 8","title":"Upgrade a standalone Redis instance to Redis 8","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/upgrade/standalone/"},{"id":"operate/oss_and_stack/install/upgrade/cluster","summary":"Upgrade a Redis Open Source cluster to Redis 8","title":"Upgrade a Redis cluster to Redis 8","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/upgrade/cluster/"}],"page_type":"content","content_hash":"0dd26ce39babd12fbcedf101384d2219e79e441fe72341ed12eda3e21ddb3d3d","sections":[{"id":"content","title":"Content","role":"content","text":"Upgrade Redis Open Source to version 8 from version 7.x with or without modules or from Redis Stack 7.2 and 7.4.\n\n<hr/>"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/install/upgrade/standalone","title":"Upgrade a standalone Redis instance to Redis 8","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/upgrade/standalone/","summary":"Upgrade a standalone instance of Redis Open Source or Redis Stack to Redis 8","tags":["docs","operate","stack","oss"],"last_updated":"2026-09-21T15:59:08+01:00","page_type":"content","content_hash":"f45e65dec3a400d344843b5aca5344d5ef31766ba984dd9e45282008014b9ca3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> The supported upgrade paths are:\n> - Redis Open Source 7.x (with or without modules) to Redis 8 in Redis Open Source\n> - Redis Stack 7.2 or 7.4 to Redis 8 in Redis Open Source\n\n> [!NOTE]\n> It's essential to practice upgrading Redis in a controlled environment before upgrading it in a production environment.\n> Docker is an excellent tool to use for this purpose.\n\nFollow these instructions to upgrade a single-node Redis server. This guide assumes you have installed Redis using one of the supported methods listed [here](https://redis.io/docs/latest/operate/oss_and_stack/install/archive)."},{"id":"save-your-current-data","title":"Save your current data","role":"content","text":"Before upgrading, create a snapshot of your current dataset using the following command:\n\n[code example]\n\nThis creates or updates an `RDB` file, for example `dump.rdb`, in your Redis data directory. If you use AOF persistence, the files will be named `appendonly.aof.*` and they will be written in the `appendonlydir` directory inside the data directory. The AOF-related directory and file names are the defaults. Use the names defined in your `redis.conf` file if different from the defaults.\n\nUse the following command to identify where your data directory is located:\n\n[code example]\n\nMake a copy of these files before proceeding:\n\n[code example]"},{"id":"upgrade-redis","title":"Upgrade Redis","role":"content","text":"Follow these steps to upgrade Redis.\n\n1. Stop the current version of Redis Open Source or Redis Stack server.\n1. Follow the installation steps that correspond to your [Redis distribution](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack) to install Redis 8.\n1. Start Redis 8 if it didn't start automatically."},{"id":"restore-from-saved-files-if-necessary","title":"Restore from saved files (if necessary)","role":"content","text":"If Redis fails to start properly or if data is missing after the upgrade, restore from your backup.\n\n1. Stop the Redis server.\n2. Copy the backup file back into the the Redis data directory:\n   [code example]\n3. Start Redis again and verify data is restored:\n   [code example]"},{"id":"verify-the-upgrade","title":"Verify the upgrade","role":"content","text":"Use the following command to confirm you're running Redis 8:\n\n[code example]\n\nYou should also verify that your data is accessible and that your clients can connect successfully."}],"examples":[{"id":"save-your-current-data-ex0","language":"bash","code":"redis-cli SAVE","section_id":"save-your-current-data"},{"id":"save-your-current-data-ex1","language":"bash","code":"redis-cli CONFIG GET dir","section_id":"save-your-current-data"},{"id":"save-your-current-data-ex2","language":"bash","code":"cp -r /path/to/redis-data-dir/ /path/to/backup/","section_id":"save-your-current-data"},{"id":"restore-from-saved-files-if-necessary-ex0","language":"bash","code":"cp -r /path/to/backup/ /path/to/redis-data-dir/","section_id":"restore-from-saved-files-if-necessary"},{"id":"restore-from-saved-files-if-necessary-ex1","language":"bash","code":"redis-cli INFO persistence","section_id":"restore-from-saved-files-if-necessary"},{"id":"verify-the-upgrade-ex0","language":"bash","code":"redis-cli INFO server | grep redis_version","section_id":"verify-the-upgrade"}]}
{"schema_version":2,"id":"operate/oss_and_stack/install/version-mgmt","title":"Redis Open Source version management","url":"https://redis.io/docs/latest/operate/oss_and_stack/install/version-mgmt/","summary":"Describes Redis Open Source supported database versions.","tags":["docs","operate","oss"],"last_updated":"2026-09-21T15:59:08+01:00","page_type":"content","content_hash":"a431260b772fa4527357bb4e287e8f9c575c61323010bf715169b73e61ccde2a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Open Source provides comprehensive version management that prioritizes customer control over major changes."},{"id":"redis-version-structure","title":"Redis version structure","role":"content","text":"Redis uses a **MAJOR.MINOR.PATCH** versioning scheme:\n\n- **Major versions**: Significant changes that may include breaking changes (for example, Redis 7 → Redis 8).\n- **Minor versions**: New features and improvements within a major version (for example, 8.2 → 8.4 → 8.6 → 8.8).\n- **Patch versions**: Bug fixes and security updates (for example, 8.6.1 → 8.6.2)."},{"id":"version-release-model","title":"Version release model","role":"compatibility","text":"Redis uses two release types within a major version:\n\n- **Standard releases** are the first release in a major version series (for example, 8.0) and intermediate minor releases (for example, 8.4, 8.6). These releases receive security updates and critical bug fixes for 6 months after the following minor version is released.\n- **Extended releases** are the second minor release in a major version series (for example, 8.2) and the final minor release in a series. These releases receive security updates and critical bug fixes for 5 years after their release date."},{"id":"supported-versions","title":"Supported versions","role":"compatibility","text":"> [!NOTE]\n> **We strongly recommend using the latest available version** to benefit from the newest features, performance improvements, and security updates.\n\n| Version | Release type | Status | EOL Date |\n|---------|--------------|--------|----------|\n| **Redis 8.10** | Standard | GA | TBD |\n| **Redis 8.8**  | Standard | GA | TBD |\n| **Redis 8.6**  | Standard | GA | TBD |\n| **Redis 8.4**  | Standard | GA | TBD |\n| **Redis 8.2**  | Extended | GA | September 1, 2030 |\n| **Redis 8.0**  | Standard | GA | December 1, 2026 |\n| **Redis 7.4**  | Extended | GA | December 1, 2029 |\n| **Redis 7.2**  | Extended | GA | December 1, 2029 |\n| **Redis 6.2**  | Extended | GA | April 1, 2027 |"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/management/admin","title":"Redis administration","url":"https://redis.io/docs/latest/operate/oss_and_stack/management/admin/","summary":"Advice for configuring and managing Redis in production","tags":["docs","operate","stack","oss"],"last_updated":"2026-09-21T15:59:03+01:00","page_type":"content","content_hash":"06311159f6d71c55683cf7cabe2bb2dcc4037aae51ad51057ae9538ac2def609","sections":[{"id":"redis-setup-tips","title":"Redis setup tips","role":"overview","text":""},{"id":"linux","title":"Linux","role":"content","text":"* Deploy Redis using the Linux operating system. Redis is also tested on OS X, and from time to time on FreeBSD and OpenBSD systems. However, Linux is where most of the stress testing is performed, and where most production deployments are run.\n\n* Set the Linux kernel overcommit memory setting to 1. Add `vm.overcommit_memory = 1` to `/etc/sysctl.conf`. Then, reboot or run the command `sysctl vm.overcommit_memory=1` to activate the setting. See [FAQ: Background saving fails with a fork() error on Linux?](https://redis.io/docs/latest/develop/get-started/faq#background-saving-fails-with-a-fork-error-on-linux) for details. \n\n* To ensure the Linux kernel feature Transparent Huge Pages does not impact Redis memory usage and latency, run the command: `echo never > /sys/kernel/mm/transparent_hugepage/enabled` to disable it. See [Latency Diagnosis - Latency induced by transparent huge pages](https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/latency#latency-induced-by-transparent-huge-pages) for additional context."},{"id":"memory","title":"Memory","role":"content","text":"* Ensured that swap is enabled and that your swap file size is equal to amount of memory on your system. If Linux does not have swap set up, and your Redis instance accidentally consumes too much memory, Redis can crash when it is out of memory, or the Linux kernel OOM killer can kill the Redis process. When swapping is enabled, you can detect latency spikes and act on them.\n\n* Set an explicit `maxmemory` option limit in your instance to make sure that it will report errors instead of failing when the system memory limit is near to be reached. Note that `maxmemory` should be set by calculating the overhead for Redis, other than data, and the fragmentation overhead. So if you think you have 10 GB of free memory, set it to 8 or 9.\n\n* If you are using Redis in a write-heavy application, while saving an RDB file on disk or rewriting the AOF log, Redis can use up to 2 times the memory normally used. The additional memory used is proportional to the number of memory pages modified by writes during the saving process, so it is often proportional to the number of keys (or aggregate types items) touched during this time. Make sure to size your memory accordingly.\n\n* See the [`LATENCY DOCTOR`](https://redis.io/docs/latest/commands/latency-doctor) and [`MEMORY DOCTOR`](https://redis.io/docs/latest/commands/memory-doctor) commands to assist in troubleshooting."},{"id":"imaging","title":"Imaging","role":"content","text":"* When running under daemontools, use `daemonize no`."},{"id":"replication","title":"Replication","role":"content","text":"* Set up a non-trivial replication backlog in proportion to the amount of memory Redis is using. The backlog allows replicas to sync with the primary (master) instance much more easily.\n\n* If you use replication, Redis performs RDB saves even if persistence is disabled. (This does not apply to diskless replication.) If you don't have disk usage on the master, enable diskless replication.\n\n* If you are using replication, ensure that either your master has persistence enabled, or that it does not automatically restart on crashes. Replicas will try to maintain an exact copy of the master, so if a master restarts with an empty data set, replicas will be wiped as well."},{"id":"security","title":"Security","role":"security","text":"* By default, Redis does not require any authentication and listens to all the network interfaces. This is a big security issue if you leave Redis exposed on the internet or other places where attackers can reach it. See for example [this attack](http://antirez.com/news/96) to see how dangerous it can be. Please check our [security page](https://redis.io/docs/latest/operate/oss_and_stack/management/security) for information about how to secure Redis."},{"id":"running-redis-on-ec2","title":"Running Redis on EC2","role":"content","text":"* Use HVM based instances, not PV based instances.\n* Do not use old instance families. For example, use m3.medium with HVM instead of m1.medium with PV.\n* The use of Redis persistence with EC2 EBS volumes needs to be handled with care because sometimes EBS volumes have high latency characteristics.\n* You may want to try the new diskless replication if you have issues when replicas are synchronizing with the master."},{"id":"upgrading-or-restarting-a-redis-instance-without-downtime","title":"Upgrading or restarting a Redis instance without downtime","role":"content","text":"Redis is designed to be a long-running process in your server. You can modify many configuration options without a restart using the [`CONFIG SET`](https://redis.io/docs/latest/commands/config-set) command. You can also switch from AOF to RDB snapshots persistence, or the other way around, without restarting Redis. Check the output of the `CONFIG GET *` command for more information.\n\nFrom time to time, a restart is required, for example, to upgrade the Redis process to a newer version, or when you need to modify a configuration parameter that is currently not supported by the [`CONFIG`](https://redis.io/docs/latest/commands/config) command.\n\nFollow these steps to avoid downtime.\n\n* Set up your new Redis instance as a replica for your current Redis instance. In order to do so, you need a different server, or a server that has enough RAM to keep two instances of Redis running at the same time.\n\n* If you use a single server, ensure that the replica is started on a different port than the master instance, otherwise the replica cannot start.\n\n* Wait for the replication initial synchronization to complete. Check the replica's log file.\n\n* Using [`INFO`](https://redis.io/docs/latest/commands/info), ensure the master and replica have the same number of keys. Use `redis-cli` to check that the replica is working as expected and is replying to your commands.\n\n* Allow writes to the replica using `CONFIG SET slave-read-only no`.\n\n* Configure all your clients to use the new instance (the replica). Note that you may want to use the [`CLIENT PAUSE`](https://redis.io/docs/latest/commands/client-pause) command to ensure that no client can write to the old master during the switch.\n\n* Once you confirm that the master is no longer receiving any queries (you can check this using the [`MONITOR`](https://redis.io/docs/latest/commands/monitor) command), elect the replica to master using the `REPLICAOF NO ONE` command, and then shut down your master.\n\nIf you are using [Redis Sentinel](https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel) or [Redis Cluster](https://redis.io/docs/latest/operate/oss_and_stack/management/scaling), the simplest way to upgrade to newer versions is to upgrade one replica after the other. Then you can perform a manual failover to promote one of the upgraded replicas to master, and finally promote the last replica.\n\n---\n**NOTE** \n\nRedis Cluster 4.0 is not compatible with Redis Cluster 3.2 at cluster bus protocol level, so a mass restart is needed in this case. However, Redis 5 cluster bus is backward compatible with Redis 4.\n\n---"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/management/config","title":"Redis configuration","url":"https://redis.io/docs/latest/operate/oss_and_stack/management/config/","summary":"Overview of redis.conf, the Redis configuration file","aliases":["/operate/oss_and_stack/management/config-file","/management/config/"],"tags":["docs","operate","stack","oss"],"last_updated":"2026-09-21T15:59:03+01:00","page_type":"content","content_hash":"2b98959a554a1f08ab30727305d4080f2c1549525c476cb818ccfa88666b8a5f","sections":[{"id":"content","title":"Content","role":"content","text":"Redis is able to start without a configuration file using a built-in default\nconfiguration, however this setup is only recommended for testing and\ndevelopment purposes.\n\nThe proper way to configure Redis is by providing a Redis configuration file,\nusually called `redis.conf`. For Redis releases 8.0 - 8.8.x in Redis Open Source, there are two configuration files:\n* `redis.conf` - contains the configuration settings for Redis server only.\n* `redis-full.conf` - contains configuration settings for Redis server and all available components: Redis Search, Redis time series, and Redis probabilistic data structures. This file has as its first line `include redis.conf`, which pulls in the Redis server configuration settings at startup. Use `redis-full.conf` when you want to enable all available components. The file contains four `loadmodule` directives, one for each component, and also loads Redis JSON (though JSON has no configuration parameters).\n\nRedis 8.10 and later use only one configuration file: `redis.conf`.\n\nIf you are building Redis from source and choose to build Redis server without the available components, you can use `redis.conf` as your configuration file.\n\nEach configuration file contains a number of directives that have a very simple format:\n\n    keyword argument1 argument2 ... argumentN\n\nThis is an example of a configuration directive:\n\n    replicaof 127.0.0.1 6380\n\nIt is possible to provide strings containing spaces as arguments using\n(double or single) quotes, as in the following example:\n\n    requirepass \"hello world\"\n\nSingle-quoted string can contain characters escaped by backslashes, and\ndouble-quoted strings can additionally include any ASCII symbols encoded using\nbackslashed hexadecimal notation \"\\\\xff\".\n\nThe list of configuration directives, along with comments describing their meaning and intended usage, is available in the self-documented sample files `redis.conf` and, for Redis releases 8.0 - 8.8.x, `redis-full.conf` files shipped with the Redis distributions.\n\n* Configuration files for Redis 8.10: [redis.conf](https://raw.githubusercontent.com/redis/redis/8.10/redis.conf).\n* Configuration files for Redis 8.8: [redis-full.conf](https://raw.githubusercontent.com/redis/redis/8.8/redis-full.conf) and [redis.conf](https://raw.githubusercontent.com/redis/redis/8.8/redis.conf).\n* Configuration files for Redis 8.6: [redis-full.conf](https://raw.githubusercontent.com/redis/redis/8.6/redis-full.conf) and [redis.conf](https://raw.githubusercontent.com/redis/redis/8.6/redis.conf).\n* Configuration files for Redis 8.4: [redis-full.conf](https://raw.githubusercontent.com/redis/redis/8.4/redis-full.conf) and [redis.conf](https://raw.githubusercontent.com/redis/redis/8.4/redis.conf).\n* Configuration files for Redis 8.2: [redis-full.conf](https://raw.githubusercontent.com/redis/redis/8.2/redis-full.conf) and [redis.conf](https://raw.githubusercontent.com/redis/redis/8.2/redis.conf).\n* Configuration files for Redis 8.0: [redis-full.conf](https://raw.githubusercontent.com/redis/redis/8.0/redis-full.conf) and [redis.conf](https://raw.githubusercontent.com/redis/redis/8.0/redis.conf).\n* Configuration file for Redis 7.4: [redis.conf](https://raw.githubusercontent.com/redis/redis/7.4/redis.conf).\n* Configuration file for Redis 7.2: [redis.conf](https://raw.githubusercontent.com/redis/redis/7.2/redis.conf).\n* Configuration file for Redis 7.0: [redis.conf](https://raw.githubusercontent.com/redis/redis/7.0/redis.conf).\n* Configuration file for Redis 6.2: [redis.conf](https://raw.githubusercontent.com/redis/redis/6.2/redis.conf).\n\nPassing arguments using the command line\n---\n\nYou can also pass Redis configuration parameters\nusing the command line directly. This is very useful for testing purposes.\nThe following is an example that starts a new Redis instance using port 6380\nas a replica of the instance running at 127.0.0.1 port 6379.\n\n    ./redis-server --port 6380 --replicaof 127.0.0.1 6379\n\nThe format of the arguments passed using the command line is exactly the same\nas the one used in the redis.conf file, with the exception that the keyword\nis prefixed with `--`.\n\nNote that internally this generates an in-memory temporary config file\n(possibly concatenating the config file passed by the user, if any) where\narguments are translated into the format of redis.conf.\n\nChanging Redis configuration while the server is running\n---\n\nIt is possible to reconfigure Redis on the fly without stopping and restarting\nthe service, or querying the current configuration programmatically using the\nspecial commands [`CONFIG SET`](https://redis.io/docs/latest/commands/config-set) and [`CONFIG GET`](https://redis.io/docs/latest/commands/config-get).\n\nNot all of the configuration directives are supported in this way, but most\nare supported as expected.\nPlease refer to the [`CONFIG SET`](https://redis.io/docs/latest/commands/config-set) and [`CONFIG GET`](https://redis.io/docs/latest/commands/config-get) pages for more information.\n\nNote that modifying the configuration on the fly does not affect the\n`redis.conf` and `redis-full.conf` files, so at the next restart of Redis, the old configuration will\nbe used instead.\n\nMake sure to also modify the configuration files accordingly to the configuration\nyou set using [`CONFIG SET`](https://redis.io/docs/latest/commands/config-set).\nYou can do it manually, or you can use [`CONFIG REWRITE`](https://redis.io/docs/latest/commands/config-rewrite), which will automatically scan your configuration files and update the fields that don't match the current configuration value.\nFields set to the default value are not added.\nComments inside your configuration file are retained.\n\nConfiguring Redis as a cache\n---\n\nIf you plan to use Redis as a cache where every key will have an\nexpire set, you may consider using the following configuration instead\n(assuming a max memory limit of 2 megabytes as an example):\n\n    maxmemory 2mb\n    maxmemory-policy allkeys-lru\n\nIn this configuration there is no need for the application to set a\ntime to live for keys using the [`EXPIRE`](https://redis.io/docs/latest/commands/expire) command (or equivalent) since\nall the keys will be evicted using an approximated LRU algorithm as long\nas we hit the 2 megabyte memory limit.\n\nBasically, in this configuration Redis acts in a similar way to memcached.\nWe have more extensive documentation about using Redis as an LRU cache [here](https://redis.io/docs/latest/develop/reference/eviction)."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/management/debugging","title":"Debugging","url":"https://redis.io/docs/latest/operate/oss_and_stack/management/debugging/","summary":"A guide to debugging Redis server processes","tags":["docs","operate","stack","oss"],"last_updated":"2024-01-30T17:50:06+01:00","page_type":"content","content_hash":"100df3edd9fcf7e0835de9c6f71f694a050c1c8752e2f63e0f4c37efbe029a05","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis is developed with an emphasis on stability. We do our best with\nevery release to make sure you'll experience a stable product with no\ncrashes. However, if you ever need to debug the Redis process itself, read on.\n\nWhen Redis crashes, it produces a detailed report of what happened. However,\nsometimes looking at the crash report is not enough, nor is it possible for\nthe Redis core team to reproduce the issue independently. In this scenario, we\nneed help from the user who can reproduce the issue.\n\nThis guide shows how to use GDB to provide the information the\nRedis developers will need to track the bug more easily."},{"id":"what-is-gdb","title":"What is GDB?","role":"content","text":"GDB is the Gnu Debugger: a program that is able to inspect the internal state\nof another program. Usually tracking and fixing a bug is an exercise in\ngathering more information about the state of the program at the moment the\nbug happens, so GDB is an extremely useful tool.\n\nGDB can be used in two ways:\n\n* It can attach to a running program and inspect the state of it at runtime.\n* It can inspect the state of a program that already terminated using what is called a *core file*, that is, the image of the memory at the time the program was running.\n\nFrom the point of view of investigating Redis bugs we need to use both of these\nGDB modes. The user able to reproduce the bug attaches GDB to their running Redis\ninstance, and when the crash happens, they create the `core` file that in turn\nthe developer will use to inspect the Redis internals at the time of the crash.\n\nThis way the developer can perform all the inspections in his or her computer\nwithout the help of the user, and the user is free to restart Redis in their\nproduction environment."},{"id":"compiling-redis-without-optimizations","title":"Compiling Redis without optimizations","role":"content","text":"By default Redis is compiled with the `-O2` switch, this means that compiler\noptimizations are enabled. This makes the Redis executable faster, but at the\nsame time it makes Redis (like any other program) harder to inspect using GDB.\n\nIt is better to attach GDB to Redis compiled without optimizations using the\n`make noopt` command (instead of just using the plain `make` command). However,\nif you have an already running Redis in production there is no need to recompile\nand restart it if this is going to create problems on your side. GDB still works\nagainst executables compiled with optimizations.\n\nYou should not be overly concerned at the loss of performance from compiling Redis\nwithout optimizations. It is unlikely that this will cause problems in your\nenvironment as Redis is not very CPU-bound."},{"id":"attaching-gdb-to-a-running-process","title":"Attaching GDB to a running process","role":"content","text":"If you have an already running Redis server, you can attach GDB to it, so that\nif Redis crashes it will be possible to both inspect the internals and generate\na `core dump` file.\n\nAfter you attach GDB to the Redis process it will continue running as usual without\nany loss of performance, so this is not a dangerous procedure.\n\nIn order to attach GDB the first thing you need is the *process ID* of the running\nRedis instance (the *pid* of the process). You can easily obtain it using\n`redis-cli`:\n\n    $ redis-cli info | grep process_id\n    process_id:58414\n\nIn the above example the process ID is **58414**.\n\nLogin into your Redis server.\n\n(Optional but recommended) Start **screen** or **tmux** or any other program that will make sure that your GDB session will not be closed if your ssh connection times out. You can learn more about screen in [this article](http://www.linuxjournal.com/article/6340).\n\nAttach GDB to the running Redis server by typing:\n\n    $ gdb <path-to-redis-executable> <pid>\n\nFor example:\n\n    $ gdb /usr/local/bin/redis-server 58414\n\nGDB will start and will attach to the running server printing something like the following:\n\n    Reading symbols for shared libraries + done\n    0x00007fff8d4797e6 in epoll_wait ()\n    (gdb)\n\nAt this point GDB is attached but **your Redis instance is blocked by GDB**. In\norder to let the Redis instance continue the execution just type **continue** at\nthe GDB prompt, and press enter.\n\n    (gdb) continue\n    Continuing.\n\nDone! Now your Redis instance has GDB attached. Now you can wait for the next crash. :)\n\nNow it's time to detach your screen/tmux session, if you are running GDB using it, by\npressing **Ctrl-a a** key combination."},{"id":"after-the-crash","title":"After the crash","role":"content","text":"Redis has a command to simulate a segmentation fault (in other words a bad crash) using\nthe `DEBUG SEGFAULT` command (don't use it against a real production instance of course!\nSo I'll use this command to crash my instance to show what happens in the GDB side:\n\n    (gdb) continue\n    Continuing.\n\n    Program received signal EXC_BAD_ACCESS, Could not access memory.\n    Reason: KERN_INVALID_ADDRESS at address: 0xffffffffffffffff\n    debugCommand (c=0x7ffc32005000) at debug.c:220\n    220         *((char*)-1) = 'x';\n\nAs you can see GDB detected that Redis crashed, and was even able to show me\nthe file name and line number causing the crash. This is already much better\nthan the Redis crash report back trace (containing just function names and\nbinary offsets)."},{"id":"obtaining-the-stack-trace","title":"Obtaining the stack trace","role":"content","text":"The first thing to do is to obtain a full stack trace with GDB. This is as\nsimple as using the **bt** command:\n\n    (gdb) bt\n    #0  debugCommand (c=0x7ffc32005000) at debug.c:220\n    #1  0x000000010d246d63 in call (c=0x7ffc32005000) at redis.c:1163\n    #2  0x000000010d247290 in processCommand (c=0x7ffc32005000) at redis.c:1305\n    #3  0x000000010d251660 in processInputBuffer (c=0x7ffc32005000) at networking.c:959\n    #4  0x000000010d251872 in readQueryFromClient (el=0x0, fd=5, privdata=0x7fff76f1c0b0, mask=220924512) at networking.c:1021\n    #5  0x000000010d243523 in aeProcessEvents (eventLoop=0x7fff6ce408d0, flags=220829559) at ae.c:352\n    #6  0x000000010d24373b in aeMain (eventLoop=0x10d429ef0) at ae.c:397\n    #7  0x000000010d2494ff in main (argc=1, argv=0x10d2b2900) at redis.c:2046\n\nThis shows the backtrace, but we also want to dump the processor registers using the **info registers** command:\n\n    (gdb) info registers\n    rax            0x0  0\n    rbx            0x7ffc32005000   140721147367424\n    rcx            0x10d2b0a60  4515891808\n    rdx            0x7fff76f1c0b0   140735188943024\n    rsi            0x10d299777  4515796855\n    rdi            0x0  0\n    rbp            0x7fff6ce40730   0x7fff6ce40730\n    rsp            0x7fff6ce40650   0x7fff6ce40650\n    r8             0x4f26b3f7   1327936503\n    r9             0x7fff6ce40718   140735020271384\n    r10            0x81 129\n    r11            0x10d430398  4517462936\n    r12            0x4b7c04f8babc0  1327936503000000\n    r13            0x10d3350a0  4516434080\n    r14            0x10d42d9f0  4517452272\n    r15            0x10d430398  4517462936\n    rip            0x10d26cfd4  0x10d26cfd4 <debugCommand+68>\n    eflags         0x10246  66118\n    cs             0x2b 43\n    ss             0x0  0\n    ds             0x0  0\n    es             0x0  0\n    fs             0x0  0\n    gs             0x0  0\n\nPlease **make sure to include** both of these outputs in your bug report."},{"id":"obtaining-the-core-file","title":"Obtaining the core file","role":"content","text":"The next step is to generate the core dump, that is the image of the memory of the running Redis process. This is done using the `gcore` command:\n\n    (gdb) gcore\n    Saved corefile core.58414\n\nNow you have the core dump to send to the Redis developer, but **it is important\nto understand** that this happens to contain all the data that was inside the\nRedis instance at the time of the crash; Redis developers will make sure not to\nshare the content with anyone else, and will delete the file as soon as it is no\nlonger used for debugging purposes, but you are warned that by sending the core\nfile you are sending your data."},{"id":"what-to-send-to-developers","title":"What to send to developers","role":"content","text":"Finally you can send everything to the Redis core team:\n\n* The Redis executable you are using.\n* The stack trace produced by the **bt** command, and the registers dump.\n* The core file you generated with gdb.\n* Information about the operating system and GCC version, and Redis version you are using."},{"id":"thank-you","title":"Thank you","role":"content","text":"Your help is extremely important! Many issues can only be tracked this way. So\nthanks!"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/management","title":"Manage Redis","url":"https://redis.io/docs/latest/operate/oss_and_stack/management/","summary":"An administrator's guide to Redis","aliases":["/management/"],"tags":["docs","operate","stack","oss"],"last_updated":"2026-04-07T16:24:18+01:00","children":[{"id":"operate/oss_and_stack/management/admin","summary":"Advice for configuring and managing Redis in production","title":"Redis administration","url":"https://redis.io/docs/latest/operate/oss_and_stack/management/admin/"},{"id":"operate/oss_and_stack/management/security","summary":"Security model and features in Redis","title":"Redis security","url":"https://redis.io/docs/latest/operate/oss_and_stack/management/security/"},{"id":"operate/oss_and_stack/management/config","summary":"Overview of redis.conf, the Redis configuration file","title":"Redis configuration","url":"https://redis.io/docs/latest/operate/oss_and_stack/management/config/"},{"id":"operate/oss_and_stack/management/sentinel","summary":"High availability for non-clustered Redis","title":"High availability with Redis Sentinel","url":"https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel/"},{"id":"operate/oss_and_stack/management/replication","summary":"How Redis supports high availability and failover with replication","title":"Redis replication","url":"https://redis.io/docs/latest/operate/oss_and_stack/management/replication/"},{"id":"operate/oss_and_stack/management/scaling","summary":"Horizontal scaling with Redis Cluster","title":"Scale with Redis Cluster","url":"https://redis.io/docs/latest/operate/oss_and_stack/management/scaling/"},{"id":"operate/oss_and_stack/management/persistence","summary":"How Redis writes data to disk","title":"Redis persistence","url":"https://redis.io/docs/latest/operate/oss_and_stack/management/persistence/"},{"id":"operate/oss_and_stack/management/optimization","summary":"Benchmarking, profiling, and optimizations for memory and latency","title":"Optimizing Redis","url":"https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/"},{"id":"operate/oss_and_stack/management/troubleshooting","summary":"Problems with Redis? Start here.","title":"Troubleshooting Redis","url":"https://redis.io/docs/latest/operate/oss_and_stack/management/troubleshooting/"},{"id":"operate/oss_and_stack/management/debugging","summary":"A guide to debugging Redis server processes","title":"Debugging","url":"https://redis.io/docs/latest/operate/oss_and_stack/management/debugging/"}],"page_type":"index","sections":[],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/management/optimization/benchmarks/index","title":"Redis benchmark","url":"https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/benchmarks/","summary":"Use the redis-benchmark utility on a Redis server","aliases":["/manual/optimization/benchmarks/","/management/optimization/benchmarks/"],"tags":["docs","operate","stack","oss"],"last_updated":"2026-09-21T15:59:03+01:00","page_type":"content","content_hash":"25431e117ab60f292ba4616e79392d7dfeadc1de6c9206a0d1079c7481971240","sections":[{"id":"how-fast-is-redis","title":"How fast is Redis?","role":"overview","text":"Redis includes the `redis-benchmark` utility that simulates running commands done\nby N clients at the same time sending M total queries (it is similar to the\nApache's `ab` utility). Below you'll find the full output of a benchmark executed\nagainst a Linux box.\n\nThe following options are supported:\n\n[code example]\n\nOptions:\n\n- `-h <hostname>`: Server hostname (default 127.0.0.1)\n- `-p <port>`: Server port (default 6379)\n- `-s <socket>`: Server socket (overrides host and port)\n- `-a <password>`: Password for Redis Auth\n- `--user <username>`: Used to send ACL style 'AUTH username pass'. Needs -a.\n- `-c <clients>`: Number of parallel connections (default 50)\n- `-n <requests>`: Total number of requests (default 100000)\n- `-d <size>`: Data size of SET/GET value in bytes (default 3)\n- `--dbnum <db>`: SELECT the specified db number (default 0)\n- `--threads <num>`: Enable multi-thread mode.\n- `--cluster`: Enable cluster mode.\n- `--enable-tracking`: Send CLIENT TRACKING on before starting benchmark.\n- `-k <boolean>`: 1=keep alive 0=reconnect (default 1)\n- `-r <keyspacelen>`: Use random keys for SET/GET/INCR, random values for SADD, random members and scores for ZADD. Using this option the benchmark will expand the string __rand_int__ inside an argument with a 12 digits number in the specified range from 0 to keyspacelen-1. The substitution changes every time a command is executed. Default tests use this to hit random keys in the specified range.\n- `-P <numreq>`: Pipeline `<numreq>` requests. Default 1 (no pipeline).\n- `-e`: If server replies with errors, show them on stdout. (No more than 1 error per second is displayed.)\n- `-q`: Quiet. Just show query/sec values\n- `--precision`: Number of decimal places to display in latency output (default 0)\n- `--csv`: Output in CSV format\n- `-l`: Loop. Run the tests forever\n- `-t <tests>`: Only run the comma separated list of tests. The test names are the same as the ones produced as output.\n- `-I`: Idle mode. Just open N idle connections and wait.\n- `--help`: Output this help and exit.\n- `--version`: Output version and exit.\n\nYou need to have a running Redis instance before launching the benchmark.\nA typical example would be:\n\n    redis-benchmark -q -n 100000\n\nUsing this tool is quite easy, and you can also write your own benchmark,\nbut as with any benchmarking activity, there are some pitfalls to avoid.\n\nRunning only a subset of the tests\n---\n\nYou don't need to run all the default tests every time you execute redis-benchmark.\nThe simplest thing to select only a subset of tests is to use the `-t` option\nlike in the following example:\n\n    $ redis-benchmark -t set,lpush -n 100000 -q\n    SET: 180180.17 requests per second, p50=0.143 msec                    \n    LPUSH: 188323.91 requests per second, p50=0.135 msec\n\nIn the above example we asked to just run test the SET and LPUSH commands,\nin quiet mode (see the `-q` switch).\n\nIt is also possible to specify the command to benchmark directly like in the\nfollowing example:\n\n    $ redis-benchmark -n 100000 -q script load \"redis.call('set','foo','bar')\"\n    script load redis.call('set','foo','bar'): 69881.20 requests per second\n\nSelecting the size of the key space\n---\n\nBy default the benchmark runs against a single key. In Redis the difference\nbetween such a synthetic benchmark and a real one is not huge since it is an\nin-memory system, however it is possible to stress cache misses and in general\nto simulate a more real-world work load by using a large key space.\n\nThis is obtained by using the `-r` switch. For instance if I want to run\none million SET operations, using a random key for every operation out of\n100k possible keys, I'll use the following command line:\n\n    $ redis-cli flushall\n    OK\n\n    $ redis-benchmark -t set -r 100000 -n 1000000\n    ====== SET ======\n      1000000 requests completed in 13.86 seconds\n      50 parallel clients\n      3 bytes payload\n      keep alive: 1\n\n    99.76% `<=` 1 milliseconds\n    99.98% `<=` 2 milliseconds\n    100.00% `<=` 3 milliseconds\n    100.00% `<=` 3 milliseconds\n    72144.87 requests per second\n\n    $ redis-cli dbsize\n    (integer) 99993\n\nUsing pipelining\n---\n\nBy default every client (the benchmark simulates 50 clients if not otherwise\nspecified with `-c`) sends the next command only when the reply of the previous\ncommand is received, this means that the server will likely need a read call\nin order to read each command from every client. Also RTT is paid as well.\n\nRedis supports [pipelining](https://redis.io/docs/latest/develop/using-commands/pipelining), so it is possible to send\nmultiple commands at once, a feature often exploited by real world applications.\nRedis pipelining is able to dramatically improve the number of operations per\nsecond a server is able to deliver.\n\nThis is an example of running the benchmark in a MacBook Air 11\" using a\npipelining of 16 commands:\n\n    $ redis-benchmark -n 1000000 -t set,get -P 16 -q\n    SET: 1536098.25 requests per second, p50=0.479 msec                     \n    GET: 1811594.25 requests per second, p50=0.391 msec\n\nUsing pipelining results in a significant increase in performance.\n\nPitfalls and misconceptions\n---------------------------\n\nThe first point is obvious: the golden rule of a useful benchmark is to\nonly compare apples and apples. Different versions of Redis can be compared\non the same workload for instance. Or the same version of Redis, but with\ndifferent options. If you plan to compare Redis to something else, then it is\nimportant to evaluate the functional and technical differences, and take them\nin account.\n\n+ Redis is a server, hence, all commands involve network round trips. Embedded data stores do not involve network and protocol management. When comparing such products to Redis, command execution times should be used instead of end-to-end times.\n+ Redis commands return an acknowledgment for all usual commands. Some other data stores do not. Comparing Redis to stores involving one-way queries is only mildly useful.\n+ Naively iterating on synchronous Redis commands does not benchmark Redis itself, but rather measure your network (or IPC) latency and the client library intrinsic latency. To really test Redis, you need multiple connections (like redis-benchmark) and/or to use pipelining to aggregate several commands and/or multiple threads or processes.\n+ Redis is an in-memory data store with some optional persistence options. If you plan to compare it to transactional servers (MySQL, PostgreSQL, etc ...), then you should consider activating AOF and decide on a suitable fsync policy.\n+ Redis is, mostly, a single-threaded server from the POV of commands execution (actually modern versions of Redis use threads for different things). It is not designed to benefit from multiple CPU cores. People are supposed to launch several Redis instances to scale out on several cores if needed. It is not really fair to compare one single Redis instance to a multi-threaded data store.\n\nA common misconception is that redis-benchmark is designed to make Redis\nperformances look stellar, the throughput achieved by redis-benchmark being\nsomewhat artificial, and not achievable by a real application. This is\nactually not true.\n\nThe `redis-benchmark` program is a quick and useful way to get some figures and\nevaluate the performance of a Redis instance on a given hardware. However,\nby default, it does not represent the maximum throughput a Redis instance can\nsustain. Actually, by using pipelining and a fast client (hiredis), it is fairly\neasy to write a program generating more throughput than redis-benchmark. The\ndefault behavior of redis-benchmark is to achieve throughput by exploiting\nconcurrency only (i.e. it creates several connections to the server).\nIt does not use pipelining or any parallelism at all (one pending query per\nconnection at most, and no multi-threading), if not explicitly enabled via\nthe `-P` parameter. So in some way using `redis-benchmark` and, triggering, for\nexample, a `BGSAVE` operation in the background at the same time, will provide\nthe user with numbers more near to the *worst case* than to the best case.\n\nTo run a benchmark using pipelining mode (and achieve higher throughput),\nyou need to explicitly use the -P option. Please note that it is still a\nrealistic behavior since a lot of Redis based applications actively use\npipelining to improve performance. However you should use a pipeline size that\nis more or less the average pipeline length you'll be able to use in your\napplication in order to get realistic numbers.\n\nFinally, the benchmark should apply the same operations, and work in the same way\nwith the multiple data stores you want to compare. It is absolutely pointless to\ncompare the result of redis-benchmark to the result of another benchmark\nprogram and extrapolate.\n\nFor instance, Redis and memcached in single-threaded mode can be compared on\nGET/SET operations. Both are in-memory data stores, working mostly in the same\nway at the protocol level. Provided their respective benchmark application is\naggregating queries in the same way (pipelining) and use a similar number of\nconnections, the comparison is actually meaningful.\n\nThis perfect example is illustrated by the dialog between Redis (antirez) and\nmemcached (dormando) developers.\n\n[antirez 1 - On Redis, Memcached, Speed, Benchmarks and The Toilet](http://antirez.com/post/redis-memcached-benchmark.html)\n\n[dormando - Redis VS Memcached (slightly better bench)](http://dormando.livejournal.com/525147.html)\n\n[antirez 2 - An update on the Memcached/Redis benchmark](http://antirez.com/post/update-on-memcached-redis-benchmark.html)\n\nYou can see that in the end, the difference between the two solutions is not\nso staggering, once all technical aspects are considered. Please note both\nRedis and memcached have been optimized further after these benchmarks.\n\nFinally, when very efficient servers are benchmarked (and stores like Redis\nor memcached definitely fall in this category), it may be difficult to saturate\nthe server. Sometimes, the performance bottleneck is on client side,\nand not server-side. In that case, the client (i.e. the benchmark program itself)\nmust be fixed, or perhaps scaled out, in order to reach the maximum throughput.\n\nFactors impacting Redis performance\n-----------------------------------\n\nThere are multiple factors having direct consequences on Redis performance.\nWe mention them here, since they can alter the result of any benchmarks.\nPlease note however, that a typical Redis instance running on a low end,\nuntuned box usually provides good enough performance for most applications.\n\n+ Network bandwidth and latency usually have a direct impact on the performance.\nIt is a good practice to use the ping program to quickly check the latency\nbetween the client and server hosts is normal before launching the benchmark.\nRegarding the bandwidth, it is generally useful to estimate\nthe throughput in Gbit/s and compare it to the theoretical bandwidth\nof the network. For instance a benchmark setting 4 KB strings\nin Redis at 100000 q/s, would actually consume 3.2 Gbit/s of bandwidth\nand probably fit within a 10 Gbit/s link, but not a 1 Gbit/s one. In many real\nworld scenarios, Redis throughput is limited by the network well before being\nlimited by the CPU. To consolidate several high-throughput Redis instances\non a single server, it worth considering putting a 10 Gbit/s NIC\nor multiple 1 Gbit/s NICs with TCP/IP bonding.\n+ CPU is another very important factor. Being single-threaded, Redis favors\nfast CPUs with large caches and not many cores. At this game, Intel CPUs are\ncurrently the winners. It is not uncommon to get only half the performance on\nan AMD Opteron CPU compared to similar Nehalem EP/Westmere EP/Sandy Bridge\nIntel CPUs with Redis. When client and server run on the same box, the CPU is\nthe limiting factor with redis-benchmark.\n+ Speed of RAM and memory bandwidth seem less critical for global performance\nespecially for small objects. For large objects (>10 KB), it may become\nnoticeable though. Usually, it is not really cost-effective to buy expensive\nfast memory modules to optimize Redis.\n+ Redis runs slower on a VM compared to running without virtualization using\nthe same hardware. If you have the chance to run Redis on a physical machine\nthis is preferred. However this does not mean that Redis is slow in\nvirtualized environments, the delivered performances are still very good\nand most of the serious performance issues you may incur in virtualized\nenvironments are due to over-provisioning, non-local disks with high latency,\nor old hypervisor software that have slow `fork` syscall implementation.\n+ When the server and client benchmark programs run on the same box, both\nthe TCP/IP loopback and unix domain sockets can be used. Depending on the\nplatform, unix domain sockets can achieve around 50% more throughput than\nthe TCP/IP loopback (on Linux for instance). The default behavior of\nredis-benchmark is to use the TCP/IP loopback.\n+ The performance benefit of unix domain sockets compared to TCP/IP loopback\ntends to decrease when pipelining is heavily used (i.e. long pipelines).\n+ When an ethernet network is used to access Redis, aggregating commands using\npipelining is especially efficient when the size of the data is kept under\nthe ethernet packet size (about 1500 bytes). Actually, processing 10 bytes,\n100 bytes, or 1000 bytes queries almost result in the same throughput.\nSee the graph below.\n\n![Data size impact](https://github.com/dspezia/redis-doc/raw/client_command/topics/Data_size.png)\n\n+ On multi CPU sockets servers, Redis performance becomes dependent on the\nNUMA configuration and process location. The most visible effect is that\nredis-benchmark results seem non-deterministic because client and server\nprocesses are distributed randomly on the cores. To get deterministic results,\nit is required to use process placement tools (on Linux: taskset or numactl).\nThe most efficient combination is always to put the client and server on two\ndifferent cores of the same CPU to benefit from the L3 cache.\nHere are some results of 4 KB SET benchmark for 3 server CPUs (AMD Istanbul,\nIntel Nehalem EX, and Intel Westmere) with different relative placements.\nPlease note this benchmark is not meant to compare CPU models between themselves\n(CPUs exact model and frequency are therefore not disclosed).\n\n![NUMA chart](https://github.com/dspezia/redis-doc/raw/6374a07f93e867353e5e946c1e39a573dfc83f6c/topics/NUMA_chart.gif)\n\n+ With high-end configurations, the number of client connections is also an\nimportant factor. Being based on epoll/kqueue, the Redis event loop is quite\nscalable. Redis has already been benchmarked at more than 60000 connections,\nand was still able to sustain 50000 q/s in these conditions. As a rule of thumb,\nan instance with 30000 connections can only process half the throughput\nachievable with 100 connections. Here is an example showing the throughput of\na Redis instance per number of connections:\n\n![connections chart](https://github.com/dspezia/redis-doc/raw/system_info/topics/Connections_chart.png)\n\n+ With high-end configurations, it is possible to achieve higher throughput by\ntuning the NIC(s) configuration and associated interruptions. Best throughput\nis achieved by setting an affinity between Rx/Tx NIC queues and CPU cores,\nand activating RPS (Receive Packet Steering) support. More information in this\n[thread](https://groups.google.com/forum/#!msg/redis-db/gUhc19gnYgc/BruTPCOroiMJ).\nJumbo frames may also provide a performance boost when large objects are used.\n+ Depending on the platform, Redis can be compiled against different memory\nallocators (libc malloc, jemalloc, tcmalloc), which may have different behaviors\nin term of raw speed, internal and external fragmentation.\nIf you did not compile Redis yourself, you can use the INFO command to check\nthe `mem_allocator` field. Please note most benchmarks do not run long enough to\ngenerate significant external fragmentation (contrary to production Redis\ninstances).\n\nOther things to consider\n------------------------\n\nOne important goal of any benchmark is to get reproducible results, so they\ncan be compared to the results of other tests.\n\n+ A good practice is to try to run tests on isolated hardware as much as possible.\nIf it is not possible, then the system must be monitored to check the benchmark\nis not impacted by some external activity.\n+ Some configurations (desktops and laptops for sure, some servers as well)\nhave a variable CPU core frequency mechanism. The policy controlling this\nmechanism can be set at the OS level. Some CPU models are more aggressive than\nothers at adapting the frequency of the CPU cores to the workload. To get\nreproducible results, it is better to set the highest possible fixed frequency\nfor all the CPU cores involved in the benchmark.\n+ An important point is to size the system accordingly to the benchmark.\nThe system must have enough RAM and must not swap. On Linux, do not forget\nto set the `overcommit_memory` parameter correctly. Please note 32 and 64 bit\nRedis instances do not have the same memory footprint.\n+ If you plan to use RDB or AOF for your benchmark, please check there is no other\nI/O activity in the system. Avoid putting RDB or AOF files on NAS or NFS shares,\nor on any other devices impacting your network bandwidth and/or latency\n(for instance, EBS on Amazon EC2).\n+ Set Redis logging level (loglevel parameter) to warning or notice. Avoid putting\nthe generated log file on a remote filesystem.\n+ Avoid using monitoring tools which can alter the result of the benchmark. For\ninstance using INFO at regular interval to gather statistics is probably fine,\nbut MONITOR will impact the measured performance significantly."},{"id":"benchmark-results-on-bare-metal-servers-across-different-redis-versions","title":"Benchmark results on bare-metal servers across different Redis versions","role":"performance","text":"It is critically important that Redis performance is retained or improved seamlessly on every released version. \n\nTo assess it, we've conducted benchmarks on the released versions of Redis (starting on v2.6.0) using `redis-benchmark` on a series of command types over a standalone redis-server, repeating the same benchmark multiple times, ensuring its statistical significance, and measuring the run-to-run variance.\n\nThe used hardware platform was a stable bare-metal HPE ProLiant DL380 Gen10 Server, with one Intel(R) Xeon(R) Gold 6230 CPU @ 2.10GHz, disabling Intel HT Technology, disabling CPU Frequency scaling, with all configurable BIOS and CPU system settings set to performance. \n\nThe box was running Ubuntu 18.04 Linux release 4.15.0-123, and Redis was compiled with gcc 7.5.0.\nThe used benchmark client (`redis-benchmark`) was kept stable across all tests, with version `redis-benchmark 6.2.0 (git:445aa844)`.\nBoth the redis-server and redis-benchmark processes were pinned to specific physical cores.\n\nThe following benchmark options were used across tests:\n\n\n* The tests were done with 50 simultaneous clients performing 5 million requests.\n* Tests were executed using the loopback interface.\n* Tests were executed without pipelining.\n* The used payload size was 256 Bytes.\n* For each redis-benchmark process, 2 threads were used to ensure that the benchmark client was not the bottleneck.\n* Strings, Hashes, Sets, and Sorted Sets data types were benchmarked.\n\nBelow we present the obtained results, broken by data type.\n\n\n![Strings performance over versions](./performance-strings.png)\n\n![Hashes performance over versions](./performance-hashes.png)\n\n![Sets performance over versions](./performance-sets.png)\n\n![Sorted sets performance over versions](./performance-sorted-sets.png)"},{"id":"other-redis-benchmarking-tools","title":"Other Redis benchmarking tools","role":"content","text":"There are several third-party tools that can be used for benchmarking Redis. Refer to each tool's\ndocumentation for more information about its goals and capabilities.\n\n* [memtier_benchmark](https://github.com/redislabs/memtier_benchmark) from [Redis Labs](https://twitter.com/RedisLabs) is a NoSQL Redis and Memcache traffic generation and benchmarking tool.\n* [rpc-perf](https://github.com/twitter/rpc-perf) from [Twitter](https://twitter.com/twitter) is a tool for benchmarking RPC services that supports Redis and Memcache.\n* [YCSB](https://github.com/brianfrankcooper/YCSB) from [Yahoo @Yahoo](https://twitter.com/Yahoo) is a benchmarking framework with clients to many databases, including Redis."}],"examples":[{"id":"how-fast-is-redis-ex0","language":"text","code":"Usage: redis-benchmark [-h <host>] [-p <port>] [-c <clients>] [-n <requests]> [-k <boolean>]","section_id":"how-fast-is-redis"}]}
{"schema_version":2,"id":"operate/oss_and_stack/management/optimization/cpu-profiling","title":"Redis CPU profiling","url":"https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/cpu-profiling/","summary":"Performance engineering guide for on-CPU profiling and tracing","aliases":["/manual/optimization/cpu-profiling/","/management/optimization/cpu-profiling/"],"tags":["docs","operate","stack","oss"],"last_updated":"2026-04-21T16:48:23+01:00","page_type":"content","content_hash":"02be9afc3eb0aa3c424a7019ffcf03c4535e4cd13d6c1db627ae72531e2fdbd2","sections":[{"id":"filling-the-performance-checklist","title":"Filling the performance checklist","role":"overview","text":"Redis is developed with a great emphasis on performance. We do our best with\nevery release to make sure you'll experience a very stable and fast product. \n\nNevertheless, if you're finding room to improve the efficiency of Redis or\nare pursuing a performance regression investigation you will need a concise\nmethodical way of monitoring and analyzing Redis performance. \n\nTo do so you can rely on different methodologies (some more suited than other \ndepending on the class of issues/analysis we intend to make). A curated list\nof methodologies and their steps are enumerated by Brendan Greg at the\n[following link](http://www.brendangregg.com/methodology.html). \n\nWe recommend the Utilization Saturation and Errors (USE) Method for answering\nthe question of what is your bottleneck. Check the following mapping between\nsystem resource, metric, and tools for a practical deep dive:\n[USE method](http://www.brendangregg.com/USEmethod/use-rosetta.html)."},{"id":"ensuring-the-cpu-is-your-bottleneck","title":"Ensuring the CPU is your bottleneck","role":"content","text":"This guide assumes you've followed one of the above methodologies to perform a \ncomplete check of system health, and identified the bottleneck being the CPU. \n**If you have identified that most of the time is spent blocked on I/O, locks,\ntimers, paging/swapping, etc., this guide is not for you**."},{"id":"build-prerequisites","title":"Build Prerequisites","role":"content","text":"For a proper On-CPU analysis, Redis (and any dynamically loaded library like\nRedis Modules) requires stack traces to be available to tracers, which you may\nneed to fix first. \n\nBy default, Redis is compiled with the `-O2` switch (which we intent to keep\nduring profiling). This means that compiler optimizations are enabled. Many\ncompilers omit the frame pointer as a runtime optimization (saving a register),\nthus breaking frame pointer-based stack walking. This makes the Redis\nexecutable faster, but at the same time it makes Redis (like any other program)\nharder to trace, potentially wrongfully pinpointing on-CPU time to the last\navailable frame pointer of a call stack that can get a lot deeper (but\nimpossible to trace).\n\nIt's important that you ensure that:\n- debug information is present: compile option `-g`\n- frame pointer register is present: `-fno-omit-frame-pointer`\n- we still run with optimizations to get an accurate representation of production run times, meaning we will keep: `-O2`\n\nYou can do it as follows within redis main repo:\n\n    $ make REDIS_CFLAGS=\"-g -fno-omit-frame-pointer\""},{"id":"a-set-of-instruments-to-identify-performance-regressions-andor-potential-on-cpu-performance-improvements","title":"A set of instruments to identify performance regressions and/or potential **on-CPU performance** improvements","role":"content","text":"This document focuses specifically on **on-CPU** resource bottlenecks analysis,\nmeaning we're interested in understanding where threads are spending CPU cycles\nwhile running on-CPU and, as importantly, whether those cycles are effectively\nbeing used for computation or stalled waiting (not blocked!) for memory I/O,\nand cache misses, etc.\n\nFor that we will rely on toolkits (perf, bcc tools), and hardware specific PMCs\n(Performance Monitoring Counters), to proceed with:\n\n- Hotspot analysis (perf or bcc tools): to profile code execution and determine which functions are consuming the most time and thus are targets for optimization. We'll present two options to collect, report, and visualize hotspots either with perf or bcc/BPF tracing tools.\n\n- Call counts analysis: to count events including function calls, enabling us to correlate several calls/components at once, relying on bcc/BPF tracing tools.\n\n- Hardware event sampling: crucial for understanding CPU behavior, including memory I/O, stall cycles, and cache misses."},{"id":"tool-prerequisites","title":"Tool prerequisites","role":"content","text":"The following steps rely on Linux perf_events (aka [\"perf\"](https://man7.org/linux/man-pages/man1/perf.1.html)), [bcc/BPF tracing tools](https://github.com/iovisor/bcc), and Brendan Greg’s [FlameGraph repo](https://github.com/brendangregg/FlameGraph).\n\nWe assume beforehand you have:\n\n- Installed the perf tool on your system. Most Linux distributions will likely package this as a package related to the kernel. More information about the perf tool can be found at perf [wiki](https://perf.wiki.kernel.org/).\n- Followed the install [bcc/BPF](https://github.com/iovisor/bcc/blob/master/INSTALL.md#installing-bcc) instructions to install bcc toolkit on your machine.\n- Cloned Brendan Greg’s [FlameGraph repo](https://github.com/brendangregg/FlameGraph) and made accessible the `difffolded.pl` and `flamegraph.pl` files, to generated the collapsed stack traces and Flame Graphs."},{"id":"hotspot-analysis-with-perf-or-ebpf-stack-traces-sampling","title":"Hotspot analysis with perf or eBPF (stack traces sampling)","role":"content","text":"Profiling CPU usage by sampling stack traces at a timed interval is a fast and\neasy way to identify performance-critical code sections (hotspots)."},{"id":"sampling-stack-traces-using-perf","title":"Sampling stack traces using perf","role":"content","text":"To profile both user- and kernel-level stacks of redis-server for a specific\nlength of time, for example 60 seconds, at a sampling frequency of 999 samples\nper second:\n\n    $ perf record -g --pid $(pgrep redis-server) -F 999 -- sleep 60\n\n#### Displaying the recorded profile information using perf report\n\nBy default perf record will generate a perf.data file in the current working\ndirectory. \n\nYou can then report with a call-graph output (call chain, stack backtrace),\nwith a minimum call graph inclusion threshold of 0.5%, with:\n\n    $ perf report -g \"graph,0.5,caller\"\n\nSee the [perf report](https://man7.org/linux/man-pages/man1/perf-report.1.html)\ndocumentation for advanced filtering, sorting and aggregation capabilities.\n\n#### Visualizing the recorded profile information using Flame Graphs\n\n[Flame graphs](http://www.brendangregg.com/flamegraphs.html) allow for a quick\nand accurate visualization of frequent code-paths. They can be generated using\nBrendan Greg's open source programs on [github](https://github.com/brendangregg/FlameGraph),\nwhich create interactive SVGs from folded stack files.\n\nSpecifically, for perf we need to convert the generated perf.data into the\ncaptured stacks, and fold each of them into single lines. You can then render\nthe on-CPU flame graph with:\n\n    $ perf script > redis.perf.stacks\n    $ stackcollapse-perf.pl redis.perf.stacks > redis.folded.stacks\n    $ flamegraph.pl redis.folded.stacks > redis.svg\n\nBy default, perf script will generate a perf.data file in the current working\ndirectory. See the [perf script](https://linux.die.net/man/1/perf-script)\ndocumentation for advanced usage.\n\nSee [FlameGraph usage options](https://github.com/brendangregg/FlameGraph#options)\nfor more advanced stack trace visualizations (like the differential one).\n\n#### Archiving and sharing recorded profile information\n\nSo that analysis of the perf.data contents can be possible on a machine other\nthan the one on which collection happened, you need to export along with the\nperf.data file all object files with build-ids found in the record data file.\nThis can be easily done with the help of \n[perf-archive.sh](https://github.com/torvalds/linux/blob/master/tools/perf/perf-archive.sh)\nscript:\n\n    $ perf-archive.sh perf.data\n\nNow please run:\n\n    $ tar xvf perf.data.tar.bz2 -C ~/.debug\n\non the machine where you need to run `perf report`."},{"id":"sampling-stack-traces-using-bccbpfs-profile","title":"Sampling stack traces using bcc/BPF's profile","role":"content","text":"Similarly to perf, as of Linux kernel 4.9, BPF-optimized profiling is now fully\navailable with the promise of lower overhead on CPU (as stack traces are\nfrequency counted in kernel context) and disk I/O resources during profiling. \n\nApart from that, and relying solely on bcc/BPF's profile tool, we have also\nremoved the perf.data and intermediate steps if stack traces analysis is our\nmain goal. You can use bcc's profile tool to output folded format directly, for\nflame graph generation:\n\n    $ /usr/share/bcc/tools/profile -F 999 -f --pid $(pgrep redis-server) --duration 60 > redis.folded.stacks\n\nIn that manner, we've remove any preprocessing and can render the on-CPU flame\ngraph with a single command:\n\n    $ flamegraph.pl redis.folded.stacks > redis.svg"},{"id":"visualizing-the-recorded-profile-information-using-flame-graphs","title":"Visualizing the recorded profile information using Flame Graphs","role":"content","text":""},{"id":"call-counts-analysis-with-bccbpf","title":"Call counts analysis with bcc/BPF","role":"content","text":"A function may consume significant CPU cycles either because its code is slow\nor because it's frequently called. To answer at what rate functions are being\ncalled, you can rely upon call counts analysis using BCC's `funccount` tool:\n\n    $ /usr/share/bcc/tools/funccount 'redis-server:(call*|*Read*|*Write*)' --pid $(pgrep redis-server) --duration 60\n    Tracing 64 functions for \"redis-server:(call*|*Read*|*Write*)\"... Hit Ctrl-C to end.\n\n    FUNC                                    COUNT\n    call                                      334\n    handleClientsWithPendingWrites            388\n    clientInstallWriteHandler                 388\n    postponeClientRead                        514\n    handleClientsWithPendingReadsUsingThreads      735\n    handleClientsWithPendingWritesUsingThreads      735\n    prepareClientToWrite                     1442\n    Detaching...\n\n\nThe above output shows that, while tracing, the Redis's call() function was\ncalled 334 times, handleClientsWithPendingWrites() 388 times, etc."},{"id":"hardware-event-counting-with-performance-monitoring-counters-pmcs","title":"Hardware event counting with Performance Monitoring Counters (PMCs)","role":"content","text":"Many modern processors contain a performance monitoring unit (PMU) exposing\nPerformance Monitoring Counters (PMCs). PMCs are crucial for understanding CPU\nbehavior, including memory I/O, stall cycles, and cache misses, and provide\nlow-level CPU performance statistics that aren't available anywhere else.\n\nThe design and functionality of a PMU is CPU-specific and you should assess\nyour CPU supported counters and features by using `perf list`. \n\nTo calculate the number of instructions per cycle, the number of micro ops\nexecuted, the number of cycles during which no micro ops were dispatched, the\nnumber stalled cycles on memory, including a per memory type stalls, for the\nduration of 60s, specifically for redis process: \n\n    $ perf stat -e \"cpu-clock,cpu-cycles,instructions,uops_executed.core,uops_executed.stall_cycles,cache-references,cache-misses,cycle_activity.stalls_total,cycle_activity.stalls_mem_any,cycle_activity.stalls_l3_miss,cycle_activity.stalls_l2_miss,cycle_activity.stalls_l1d_miss\" --pid $(pgrep redis-server) -- sleep 60\n\n    Performance counter stats for process id '3038':\n\n      60046.411437      cpu-clock (msec)          #    1.001 CPUs utilized          \n      168991975443      cpu-cycles                #    2.814 GHz                      (36.40%)\n      388248178431      instructions              #    2.30  insn per cycle           (45.50%)\n      443134227322      uops_executed.core        # 7379.862 M/sec                    (45.51%)\n       30317116399      uops_executed.stall_cycles #  504.895 M/sec                    (45.51%)\n         670821512      cache-references          #   11.172 M/sec                    (45.52%)\n          23727619      cache-misses              #    3.537 % of all cache refs      (45.43%)\n       30278479141      cycle_activity.stalls_total #  504.251 M/sec                    (36.33%)\n       19981138777      cycle_activity.stalls_mem_any #  332.762 M/sec                    (36.33%)\n         725708324      cycle_activity.stalls_l3_miss #   12.086 M/sec                    (36.33%)\n        8487905659      cycle_activity.stalls_l2_miss #  141.356 M/sec                    (36.32%)\n       10011909368      cycle_activity.stalls_l1d_miss #  166.736 M/sec                    (36.31%)\n\n      60.002765665 seconds time elapsed\n\nIt's important to know that there are two very different ways in which PMCs can\nbe used (counting and sampling), and we've focused solely on PMCs counting for\nthe sake of this analysis. Brendan Greg clearly explains it on the following\n[link](http://www.brendangregg.com/blog/2017-05-04/the-pmcs-of-ec2.html)."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/management/optimization","title":"Optimizing Redis","url":"https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/","summary":"Benchmarking, profiling, and optimizations for memory and latency","aliases":["/manual/optimization/","/management/optimization/"],"tags":["docs","operate","stack","oss"],"last_updated":"2026-04-21T16:48:23+01:00","children":[{"id":"operate/oss_and_stack/management/optimization/benchmarks/index","summary":"Use the redis-benchmark utility on a Redis server","title":"Redis benchmark","url":"https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/benchmarks/"},{"id":"operate/oss_and_stack/management/optimization/cpu-profiling","summary":"Performance engineering guide for on-CPU profiling and tracing","title":"Redis CPU profiling","url":"https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/cpu-profiling/"},{"id":"operate/oss_and_stack/management/optimization/latency","summary":"Finding the causes of slow responses","title":"Diagnosing latency issues","url":"https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/latency/"},{"id":"operate/oss_and_stack/management/optimization/latency-monitor","summary":"Discovering slow server events in Redis","title":"Redis latency monitoring","url":"https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/latency-monitor/"},{"id":"operate/oss_and_stack/management/optimization/memory-optimization","summary":"Strategies for optimizing memory usage in Redis","title":"Memory optimization","url":"https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/memory-optimization/"}],"page_type":"index","sections":[],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/management/optimization/latency","title":"Diagnosing latency issues","url":"https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/latency/","summary":"Finding the causes of slow responses","aliases":["/manual/optimization/latency/","/management/optimization/latency/"],"tags":["docs","operate","stack","oss"],"last_updated":"2026-09-21T15:59:03+01:00","page_type":"content","content_hash":"e1139aec0cac462d485087f2f52226cfcd9e7a3947f62fc4f19c5ac561ba0a6c","sections":[{"id":"content","title":"Content","role":"content","text":"This document will help you understand what the problem could be if you\nare experiencing latency problems with Redis.\n\nIn this context *latency* is the maximum delay between the time a client\nissues a command and the time the reply to the command is received by the\nclient. Usually Redis processing time is extremely low, in the sub microsecond\nrange, but there are certain conditions leading to higher latency figures.\n\nI've little time, give me the checklist\n---\n\nThe following documentation is very important in order to run Redis in\na low latency fashion. However I understand that we are busy people, so\nlet's start with a quick checklist. If you fail following these steps, please\nreturn here to read the full documentation.\n\n1. Make sure you are not running slow commands that are blocking the server. Use the Redis [Slow Log feature](https://redis.io/docs/latest/commands/slowlog) to check this.\n2. For EC2 users, make sure you use HVM based modern EC2 instances, like m3.medium. Otherwise fork() is too slow.\n3. Transparent huge pages must be disabled from your kernel. Use `echo never > /sys/kernel/mm/transparent_hugepage/enabled` to disable them, and restart your Redis process.\n4. If you are using a virtual machine, it is possible that you have an intrinsic latency that has nothing to do with Redis. Check the minimum latency you can expect from your runtime environment using `./redis-cli --intrinsic-latency 100`. Note: you need to run this command in *the server* not in the client.\n5. Enable and use the [Latency monitor](https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/latency-monitor) feature of Redis in order to get a human readable description of the latency events and causes in your Redis instance.\n\nIn general, use the following table for durability VS latency/performance tradeoffs, ordered from stronger safety to better latency.\n\n1. AOF + fsync always: this is very slow, you should use it only if you know what you are doing.\n2. AOF + fsync every second: this is a good compromise.\n3. AOF + fsync every second + no-appendfsync-on-rewrite option set to yes: this is as the above, but avoids to fsync during rewrites to lower the disk pressure.\n4. AOF + fsync never. Fsyncing is up to the kernel in this setup, even less disk pressure and risk of latency spikes.\n5. RDB. Here you have a vast spectrum of tradeoffs depending on the save triggers you configure.\n\nAnd now for people with 15 minutes to spend, the details...\n\nMeasuring latency\n-----------------\n\nIf you are experiencing latency problems, you probably know how to measure\nit in the context of your application, or maybe your latency problem is very\nevident even macroscopically. However redis-cli can be used to measure the\nlatency of a Redis server in milliseconds, just try:\n\n    redis-cli --latency -h `host` -p `port`\n\nUsing the internal Redis latency monitoring subsystem\n---\n\nSince Redis 2.8.13, Redis provides latency monitoring capabilities that\nare able to sample different execution paths to understand where the\nserver is blocking. This makes debugging of the problems illustrated in\nthis documentation much simpler, so we suggest enabling latency monitoring\nASAP. Please refer to the [Latency monitor documentation](https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/latency-monitor).\n\nWhile the latency monitoring sampling and reporting capabilities will make\nit simpler to understand the source of latency in your Redis system, it is still\nadvised that you read this documentation extensively to better understand\nthe topic of Redis and latency spikes.\n\nLatency baseline\n----------------\n\nThere is a kind of latency that is inherently part of the environment where\nyou run Redis, that is the latency provided by your operating system kernel\nand, if you are using virtualization, by the hypervisor you are using.\n\nWhile this latency can't be removed it is important to study it because\nit is the baseline, or in other words, you won't be able to achieve a Redis\nlatency that is better than the latency that every process running in your\nenvironment will experience because of the kernel or hypervisor implementation\nor setup.\n\nWe call this kind of latency **intrinsic latency**, and `redis-cli` starting\nfrom Redis version 2.8.7 is able to measure it. This is an example run\nunder Linux 3.11.0 running on an entry level server.\n\nNote: the argument `100` is the number of seconds the test will be executed.\nThe more time we run the test, the more likely we'll be able to spot\nlatency spikes. 100 seconds is usually appropriate, however you may want\nto perform a few runs at different times. Please note that the test is CPU\nintensive and will likely saturate a single core in your system.\n\n    $ ./redis-cli --intrinsic-latency 100\n    Max latency so far: 1 microseconds.\n    Max latency so far: 16 microseconds.\n    Max latency so far: 50 microseconds.\n    Max latency so far: 53 microseconds.\n    Max latency so far: 83 microseconds.\n    Max latency so far: 115 microseconds.\n\nNote: redis-cli in this special case needs to **run in the server** where you run or plan to run Redis, not in the client. In this special mode redis-cli does not connect to a Redis server at all: it will just try to measure the largest time the kernel does not provide CPU time to run to the redis-cli process itself.\n\nIn the above example, the intrinsic latency of the system is just 0.115\nmilliseconds (or 115 microseconds), which is a good news, however keep in mind\nthat the intrinsic latency may change over time depending on the load of the\nsystem.\n\nVirtualized environments will not show so good numbers, especially with high\nload or if there are noisy neighbors. The following is a run on a Linode 4096\ninstance running Redis and Apache:\n\n    $ ./redis-cli --intrinsic-latency 100\n    Max latency so far: 573 microseconds.\n    Max latency so far: 695 microseconds.\n    Max latency so far: 919 microseconds.\n    Max latency so far: 1606 microseconds.\n    Max latency so far: 3191 microseconds.\n    Max latency so far: 9243 microseconds.\n    Max latency so far: 9671 microseconds.\n\nHere we have an intrinsic latency of 9.7 milliseconds: this means that we can't ask better than that to Redis. However other runs at different times in different virtualization environments with higher load or with noisy neighbors can easily show even worse values. We were able to measure up to 40 milliseconds in\nsystems otherwise apparently running normally.\n\nLatency induced by network and communication\n--------------------------------------------\n\nClients connect to Redis using a TCP/IP connection or a Unix domain connection.\nThe typical latency of a 1 Gbit/s network is about 200 us, while the latency\nwith a Unix domain socket can be as low as 30 us. It actually depends on your\nnetwork and system hardware. On top of the communication itself, the system\nadds some more latency (due to thread scheduling, CPU caches, NUMA placement,\netc ...). System induced latencies are significantly higher on a virtualized\nenvironment than on a physical machine.\n\nThe consequence is even if Redis processes most commands in sub microsecond\nrange, a client performing many roundtrips to the server will have to pay\nfor these network and system related latencies.\n\nAn efficient client will therefore try to limit the number of roundtrips by\npipelining several commands together. This is fully supported by the servers\nand most clients. Aggregated commands like MSET/MGET can be also used for\nthat purpose. Starting with Redis 2.4, a number of commands also support\nvariadic parameters for all data types.\n\nHere are some guidelines:\n\n+ If you can afford it, prefer a physical machine over a VM to host the server.\n+ Do not systematically connect/disconnect to the server (especially true\n  for web based applications). Keep your connections as long lived as possible.\n+ If your client is on the same host than the server, use Unix domain sockets.\n+ Prefer to use aggregated commands (MSET/MGET), or commands with variadic\n  parameters (if possible) over pipelining.\n+ Prefer to use pipelining (if possible) over sequence of roundtrips.\n+ Redis supports Lua server-side scripting to cover cases that are not suitable\n  for raw pipelining (for instance when the result of a command is an input for\n  the following commands).\n\nOn Linux, some people can achieve better latencies by playing with process\nplacement (taskset), cgroups, real-time priorities (chrt), NUMA\nconfiguration (numactl), or by using a low-latency kernel. Please note\nvanilla Redis is not really suitable to be bound on a **single** CPU core.\nRedis can fork background tasks that can be extremely CPU consuming\nlike [`BGSAVE`](https://redis.io/docs/latest/commands/bgsave) or [`BGREWRITEAOF`](https://redis.io/docs/latest/commands/bgrewriteaof). These tasks must **never** run on the same core\nas the main event loop.\n\nIn most situations, these kind of system level optimizations are not needed.\nOnly do them if you require them, and if you are familiar with them.\n\nSingle threaded nature of Redis\n-------------------------------\n\nRedis uses a *mostly* single threaded design. This means that a single process\nserves all the client requests, using a technique called **multiplexing**.\nThis means that Redis can serve a single request in every given moment, so\nall the requests are served sequentially. This is very similar to how Node.js\nworks as well. However, both products are not often perceived as being slow.\nThis is caused in part by the small amount of time to complete a single request,\nbut primarily because these products are designed to not block on system calls,\nsuch as reading data from or writing data to a socket.\n\nI said that Redis is *mostly* single threaded since actually from Redis 2.4\nwe use threads in Redis in order to perform some slow I/O operations in the\nbackground, mainly related to disk I/O, but this does not change the fact\nthat Redis serves all the requests using a single thread.\n\nLatency generated by slow commands\n----------------------------------\n\nA consequence of being single thread is that when a request is slow to serve\nall the other clients will wait for this request to be served. When executing\nnormal commands, like [`GET`](https://redis.io/docs/latest/commands/get) or [`SET`](https://redis.io/docs/latest/commands/set) or [`LPUSH`](https://redis.io/docs/latest/commands/lpush) this is not a problem\nat all since these commands are executed in constant (and very small) time.\nHowever there are commands operating on many elements, like [`SORT`](https://redis.io/docs/latest/commands/sort), [`LREM`](https://redis.io/docs/latest/commands/lrem),\n[`SUNION`](https://redis.io/docs/latest/commands/sunion) and others. For instance taking the intersection of two big sets\ncan take a considerable amount of time.\n\nThe algorithmic complexity of all commands is documented. A good practice\nis to systematically check it when using commands you are not familiar with.\n\nIf you have latency concerns you should either not use slow commands against\nvalues composed of many elements, or you should run a replica using Redis\nreplication where you run all your slow queries.\n\nIt is possible to monitor slow commands using the Redis\n[Slow Log feature](https://redis.io/docs/latest/commands/slowlog).\n\nAdditionally, you can use your favorite per-process monitoring program\n(top, htop, prstat, etc ...) to quickly check the CPU consumption of the\nmain Redis process. If it is high while the traffic is not, it is usually\na sign that slow commands are used.\n\n**IMPORTANT NOTE**: a VERY common source of latency generated by the execution\nof slow commands is the use of the [`KEYS`](https://redis.io/docs/latest/commands/keys) command in production environments.\n[`KEYS`](https://redis.io/docs/latest/commands/keys), as documented in the Redis documentation, should only be used for\ndebugging purposes. Since Redis 2.8 a new commands were introduced in order to\niterate the key space and other large collections incrementally, please check\nthe [`SCAN`](https://redis.io/docs/latest/commands/scan), [`SSCAN`](https://redis.io/docs/latest/commands/sscan), [`HSCAN`](https://redis.io/docs/latest/commands/hscan) and [`ZSCAN`](https://redis.io/docs/latest/commands/zscan) commands for more information.\n\nLatency generated by fork\n-------------------------\n\nIn order to generate the RDB file in background, or to rewrite the Append Only File if AOF persistence is enabled, Redis has to fork background processes.\nThe fork operation (running in the main thread) can induce latency by itself.\n\nForking is an expensive operation on most Unix-like systems, since it involves\ncopying a good number of objects linked to the process. This is especially\ntrue for the page table associated to the virtual memory mechanism.\n\nFor instance on a Linux/AMD64 system, the memory is divided in 4 kB pages.\nTo convert virtual addresses to physical addresses, each process stores\na page table (actually represented as a tree) containing at least a pointer\nper page of the address space of the process. So a large 24 GB Redis instance\nrequires a page table of 24 GB / 4 kB * 8 = 48 MB.\n\nWhen a background save is performed, this instance will have to be forked,\nwhich will involve allocating and copying 48 MB of memory. It takes time\nand CPU, especially on virtual machines where allocation and initialization\nof a large memory chunk can be expensive.\n\nFork time in different systems\n------------------------------\n\nModern hardware is pretty fast at copying the page table, but Xen is not.\nThe problem with Xen is not virtualization-specific, but Xen-specific. For instance using VMware or Virtual Box does not result into slow fork time.\nThe following is a table that compares fork time for different Redis instance\nsize. Data is obtained performing a BGSAVE and looking at the `latest_fork_usec` filed in the [`INFO`](https://redis.io/docs/latest/commands/info) command output.\n\nHowever the good news is that **new types of EC2 HVM based instances are much\nbetter with fork times**, almost on par with physical servers, so for example\nusing m3.medium (or better) instances will provide good results.\n\n* **Linux beefy VM on VMware** 6.0GB RSS forked in 77 milliseconds (12.8 milliseconds per GB).\n* **Linux running on physical machine (Unknown HW)** 6.1GB RSS forked in 80 milliseconds (13.1 milliseconds per GB)\n* **Linux running on physical machine (Xeon @ 2.27Ghz)** 6.9GB RSS forked into 62 milliseconds (9 milliseconds per GB).\n* **Linux VM on 6sync (KVM)** 360 MB RSS forked in 8.2 milliseconds (23.3 milliseconds per GB).\n* **Linux VM on EC2, old instance types (Xen)** 6.1GB RSS forked in 1460 milliseconds (239.3 milliseconds per GB).\n* **Linux VM on EC2, new instance types (Xen)** 1GB RSS forked in 10 milliseconds (10 milliseconds per GB).\n* **Linux VM on Linode (Xen)** 0.9GBRSS forked into 382 milliseconds (424 milliseconds per GB).\n\nAs you can see certain VMs running on Xen have a performance hit that is between one order to two orders of magnitude. For EC2 users the suggestion is simple: use modern HVM based instances.\n\nLatency induced by transparent huge pages\n-----------------------------------------\n\nUnfortunately when a Linux kernel has transparent huge pages enabled, Redis\nincurs to a big latency penalty after the `fork` call is used in order to\npersist on disk. Huge pages are the cause of the following issue:\n\n1. Fork is called, two processes with shared huge pages are created.\n2. In a busy instance, a few event loops runs will cause commands to target a few thousand of pages, causing the copy on write of almost the whole process memory.\n3. This will result in big latency and big memory usage.\n\nMake sure to **disable transparent huge pages** using the following command:\n\n    echo never > /sys/kernel/mm/transparent_hugepage/enabled\n\nLatency induced by swapping (operating system paging)\n-----------------------------------------------------\n\nLinux (and many other modern operating systems) is able to relocate memory\npages from the memory to the disk, and vice versa, in order to use the\nsystem memory efficiently.\n\nIf a Redis page is moved by the kernel from the memory to the swap file, when\nthe data stored in this memory page is used by Redis (for example accessing\na key stored into this memory page) the kernel will stop the Redis process\nin order to move the page back into the main memory. This is a slow operation\ninvolving random I/Os (compared to accessing a page that is already in memory)\nand will result into anomalous latency experienced by Redis clients.\n\nThe kernel relocates Redis memory pages on disk mainly because of three reasons:\n\n* The system is under memory pressure since the running processes are demanding\nmore physical memory than the amount that is available. The simplest instance of\nthis problem is simply Redis using more memory than is available.\n* The Redis instance data set, or part of the data set, is mostly completely idle\n(never accessed by clients), so the kernel could swap idle memory pages on disk.\nThis problem is very rare since even a moderately slow instance will touch all\nthe memory pages often, forcing the kernel to retain all the pages in memory.\n* Some processes are generating massive read or write I/Os on the system. Because\nfiles are generally cached, it tends to put pressure on the kernel to increase\nthe filesystem cache, and therefore generate swapping activity. Please note it\nincludes Redis RDB and/or AOF background threads which can produce large files.\n\nFortunately Linux offers good tools to investigate the problem, so the simplest\nthing to do is when latency due to swapping is suspected is just to check if\nthis is the case.\n\nThe first thing to do is to checking the amount of Redis memory that is swapped\non disk. In order to do so you need to obtain the Redis instance pid:\n\n    $ redis-cli info | grep process_id\n    process_id:5454\n\nNow enter the /proc file system directory for this process:\n\n    $ cd /proc/5454\n\nHere you'll find a file called **smaps** that describes the memory layout of\nthe Redis process (assuming you are using Linux 2.6.16 or newer).\nThis file contains very detailed information about our process memory maps,\nand one field called **Swap** is exactly what we are looking for. However\nthere is not just a single swap field since the smaps file contains the\ndifferent memory maps of our Redis process (The memory layout of a process\nis more complex than a simple linear array of pages).\n\nSince we are interested in all the memory swapped by our process the first thing\nto do is to grep for the Swap field across all the file:\n\n    $ cat smaps | grep 'Swap:'\n    Swap:                  0 kB\n    Swap:                  0 kB\n    Swap:                  0 kB\n    Swap:                  0 kB\n    Swap:                  0 kB\n    Swap:                 12 kB\n    Swap:                156 kB\n    Swap:                  8 kB\n    Swap:                  0 kB\n    Swap:                  0 kB\n    Swap:                  0 kB\n    Swap:                  0 kB\n    Swap:                  0 kB\n    Swap:                  0 kB\n    Swap:                  0 kB\n    Swap:                  0 kB\n    Swap:                  0 kB\n    Swap:                  4 kB\n    Swap:                  0 kB\n    Swap:                  0 kB\n    Swap:                  4 kB\n    Swap:                  0 kB\n    Swap:                  0 kB\n    Swap:                  4 kB\n    Swap:                  4 kB\n    Swap:                  0 kB\n    Swap:                  0 kB\n    Swap:                  0 kB\n    Swap:                  0 kB\n    Swap:                  0 kB\n\nIf everything is 0 kB, or if there are sporadic 4k entries, everything is\nperfectly normal. Actually in our example instance (the one of a real web\nsite running Redis and serving hundreds of users every second) there are a\nfew entries that show more swapped pages. To investigate if this is a serious\nproblem or not we change our command in order to also print the size of the\nmemory map:\n\n    $ cat smaps | egrep '^(Swap|Size)'\n    Size:                316 kB\n    Swap:                  0 kB\n    Size:                  4 kB\n    Swap:                  0 kB\n    Size:                  8 kB\n    Swap:                  0 kB\n    Size:                 40 kB\n    Swap:                  0 kB\n    Size:                132 kB\n    Swap:                  0 kB\n    Size:             720896 kB\n    Swap:                 12 kB\n    Size:               4096 kB\n    Swap:                156 kB\n    Size:               4096 kB\n    Swap:                  8 kB\n    Size:               4096 kB\n    Swap:                  0 kB\n    Size:                  4 kB\n    Swap:                  0 kB\n    Size:               1272 kB\n    Swap:                  0 kB\n    Size:                  8 kB\n    Swap:                  0 kB\n    Size:                  4 kB\n    Swap:                  0 kB\n    Size:                 16 kB\n    Swap:                  0 kB\n    Size:                 84 kB\n    Swap:                  0 kB\n    Size:                  4 kB\n    Swap:                  0 kB\n    Size:                  4 kB\n    Swap:                  0 kB\n    Size:                  8 kB\n    Swap:                  4 kB\n    Size:                  8 kB\n    Swap:                  0 kB\n    Size:                  4 kB\n    Swap:                  0 kB\n    Size:                  4 kB\n    Swap:                  4 kB\n    Size:                144 kB\n    Swap:                  0 kB\n    Size:                  4 kB\n    Swap:                  0 kB\n    Size:                  4 kB\n    Swap:                  4 kB\n    Size:                 12 kB\n    Swap:                  4 kB\n    Size:                108 kB\n    Swap:                  0 kB\n    Size:                  4 kB\n    Swap:                  0 kB\n    Size:                  4 kB\n    Swap:                  0 kB\n    Size:                272 kB\n    Swap:                  0 kB\n    Size:                  4 kB\n    Swap:                  0 kB\n\nAs you can see from the output, there is a map of 720896 kB\n(with just 12 kB swapped) and 156 kB more swapped in another map:\nbasically a very small amount of our memory is swapped so this is not\ngoing to create any problem at all.\n\nIf instead a non trivial amount of the process memory is swapped on disk your\nlatency problems are likely related to swapping. If this is the case with your\nRedis instance you can further verify it using the **vmstat** command:\n\n    $ vmstat 1\n    procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----\n     r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa\n     0  0   3980 697932 147180 1406456    0    0     2     2    2    0  4  4 91  0\n     0  0   3980 697428 147180 1406580    0    0     0     0 19088 16104  9  6 84  0\n     0  0   3980 697296 147180 1406616    0    0     0    28 18936 16193  7  6 87  0\n     0  0   3980 697048 147180 1406640    0    0     0     0 18613 15987  6  6 88  0\n     2  0   3980 696924 147180 1406656    0    0     0     0 18744 16299  6  5 88  0\n     0  0   3980 697048 147180 1406688    0    0     0     4 18520 15974  6  6 88  0\n    ^C\n\nThe interesting part of the output for our needs are the two columns **si**\nand **so**, that counts the amount of memory swapped from/to the swap file. If\nyou see non zero counts in those two columns then there is swapping activity\nin your system.\n\nFinally, the **iostat** command can be used to check the global I/O activity of\nthe system.\n\n    $ iostat -xk 1\n    avg-cpu:  %user   %nice %system %iowait  %steal   %idle\n              13.55    0.04    2.92    0.53    0.00   82.95\n\n    Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await  svctm  %util\n    sda               0.77     0.00    0.01    0.00     0.40     0.00    73.65     0.00    3.62   2.58   0.00\n    sdb               1.27     4.75    0.82    3.54    38.00    32.32    32.19     0.11   24.80   4.24   1.85\n\nIf your latency problem is due to Redis memory being swapped on disk you need\nto lower the memory pressure in your system, either adding more RAM if Redis\nis using more memory than the available, or avoiding running other memory\nhungry processes in the same system.\n\nLatency due to AOF and disk I/O\n-------------------------------\n\nAnother source of latency is due to the Append Only File support on Redis.\nThe AOF basically uses two system calls to accomplish its work. One is\nwrite(2) that is used in order to write data to the append only file, and\nthe other one is fdatasync(2) that is used in order to flush the kernel\nfile buffer on disk in order to ensure the durability level specified by\nthe user.\n\nBoth the write(2) and fdatasync(2) calls can be source of latency.\nFor instance write(2) can block both when there is a system wide sync\nin progress, or when the output buffers are full and the kernel requires\nto flush on disk in order to accept new writes.\n\nThe fdatasync(2) call is a worse source of latency as with many combinations\nof kernels and file systems used it can take from a few milliseconds to\na few seconds to complete, especially in the case of some other process\ndoing I/O. For this reason when possible Redis does the fdatasync(2) call\nin a different thread since Redis 2.4.\n\nWe'll see how configuration can affect the amount and source of latency\nwhen using the AOF file.\n\nThe AOF can be configured to perform a fsync on disk in three different\nways using the **appendfsync** configuration option (this setting can be\nmodified at runtime using the **CONFIG SET** command).\n\n* When appendfsync is set to the value of **no** Redis performs no fsync.\nIn this configuration the only source of latency can be write(2).\nWhen this happens usually there is no solution since simply the disk can't\ncope with the speed at which Redis is receiving data, however this is\nuncommon if the disk is not seriously slowed down by other processes doing\nI/O.\n\n* When appendfsync is set to the value of **everysec** Redis performs a\nfsync every second. It uses a different thread, and if the fsync is still\nin progress Redis uses a buffer to delay the write(2) call up to two seconds\n(since write would block on Linux if a fsync is in progress against the\nsame file). However if the fsync is taking too long Redis will eventually\nperform the write(2) call even if the fsync is still in progress, and this\ncan be a source of latency.\n\n* When appendfsync is set to the value of **always** a fsync is performed\nat every write operation before replying back to the client with an OK code\n(actually Redis will try to cluster many commands executed at the same time\ninto a single fsync). In this mode performances are very low in general and\nit is strongly recommended to use a fast disk and a file system implementation\nthat can perform the fsync in short time.\n\nMost Redis users will use either the **no** or **everysec** setting for the\nappendfsync configuration directive. The suggestion for minimum latency is\nto avoid other processes doing I/O in the same system.\nUsing an SSD disk can help as well, but usually even non SSD disks perform\nwell with the append only file if the disk is spare as Redis writes\nto the append only file without performing any seek.\n\nIf you want to investigate your latency issues related to the append only\nfile you can use the strace command under Linux:\n\n    sudo strace -p $(pidof redis-server) -T -e trace=fdatasync\n\nThe above command will show all the fdatasync(2) system calls performed by\nRedis in the main thread. With the above command you'll not see the\nfdatasync system calls performed by the background thread when the\nappendfsync config option is set to **everysec**. In order to do so\njust add the -f switch to strace.\n\nIf you wish you can also see both fdatasync and write system calls with the\nfollowing command:\n\n    sudo strace -p $(pidof redis-server) -T -e trace=fdatasync,write\n\nHowever since write(2) is also used in order to write data to the client\nsockets this will likely show too many things unrelated to disk I/O.\nApparently there is no way to tell strace to just show slow system calls so\nI use the following command:\n\n    sudo strace -f -p $(pidof redis-server) -T -e trace=fdatasync,write 2>&1 | grep -v '0.0' | grep -v unfinished\n\nLatency generated by expires\n----------------------------\n\nRedis evict expired keys in two ways:\n\n+ One *lazy* way expires a key when it is requested by a command, but it is found to be already expired.\n+ One *active* way expires a few keys every 100 milliseconds.\n\nThe active expiring is designed to be adaptive. An expire cycle is started every 100 milliseconds (10 times per second), and will do the following:\n\n+ Sample `ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP` keys, evicting all the keys already expired.\n+ If the more than 25% of the keys were found expired, repeat.\n\nGiven that `ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP` is set to 20 by default, and the process is performed ten times per second, usually just 200 keys per second are actively expired. This is enough to clean the DB fast enough even when already expired keys are not accessed for a long time, so that the *lazy* algorithm does not help. At the same time expiring just 200 keys per second has no effects in the latency a Redis instance.\n\nHowever the algorithm is adaptive and will loop if it finds more than 25% of keys already expired in the set of sampled keys. But given that we run the algorithm ten times per second, this means that the unlucky event of more than 25% of the keys in our random sample are expiring at least *in the same second*.\n\nBasically this means that **if the database has many, many keys expiring in the same second, and these make up at least 25% of the current population of keys with an expire set**, Redis can block in order to get the percentage of keys already expired below 25%.\n\nThis approach is needed in order to avoid using too much memory for keys that are already expired, and usually is absolutely harmless since it's strange that a big number of keys are going to expire in the same exact second, but it is not impossible that the user used [`EXPIREAT`](https://redis.io/docs/latest/commands/expireat) extensively with the same Unix time.\n\nIn short: be aware that many keys expiring at the same moment can be a source of latency.\n\nRedis software watchdog\n---\n\nRedis 2.6 introduces the *Redis Software Watchdog* that is a debugging tool\ndesigned to track those latency problems that for one reason or the other\nescaped an analysis using normal tools.\n\nThe software watchdog is an experimental feature. While it is designed to\nbe used in production environments care should be taken to backup the database\nbefore proceeding as it could possibly have unexpected interactions with the\nnormal execution of the Redis server.\n\nIt is important to use it only as *last resort* when there is no way to track the issue by other means.\n\nThis is how this feature works:\n\n* The user enables the software watchdog using the [`CONFIG SET`](https://redis.io/docs/latest/commands/config-set) command.\n* Redis starts monitoring itself constantly.\n* If Redis detects that the server is blocked into some operation that is not returning fast enough, and that may be the source of the latency issue, a low level report about where the server is blocked is dumped on the log file.\n* The user contacts the developers writing a message in the Redis Google Group, including the watchdog report in the message.\n\nNote that this feature cannot be enabled using the redis.conf file, because it is designed to be enabled only in already running instances and only for debugging purposes.\n\nTo enable the feature just use the following:\n\n    CONFIG SET watchdog-period 500\n\nThe period is specified in milliseconds. In the above example I specified to log latency issues only if the server detects a delay of 500 milliseconds or greater. The minimum configurable period is 200 milliseconds.\n\nWhen you are done with the software watchdog you can turn it off setting the `watchdog-period` parameter to 0. **Important:** remember to do this because keeping the instance with the watchdog turned on for a longer time than needed is generally not a good idea.\n\nThe following is an example of what you'll see printed in the log file once the software watchdog detects a delay longer than the configured one:\n\n    [8547 | signal handler] (1333114359)\n    --- WATCHDOG TIMER EXPIRED ---\n    /lib/libc.so.6(nanosleep+0x2d) [0x7f16b5c2d39d]\n    /lib/libpthread.so.0(+0xf8f0) [0x7f16b5f158f0]\n    /lib/libc.so.6(nanosleep+0x2d) [0x7f16b5c2d39d]\n    /lib/libc.so.6(usleep+0x34) [0x7f16b5c62844]\n    ./redis-server(debugCommand+0x3e1) [0x43ab41]\n    ./redis-server(call+0x5d) [0x415a9d]\n    ./redis-server(processCommand+0x375) [0x415fc5]\n    ./redis-server(processInputBuffer+0x4f) [0x4203cf]\n    ./redis-server(readQueryFromClient+0xa0) [0x4204e0]\n    ./redis-server(aeProcessEvents+0x128) [0x411b48]\n    ./redis-server(aeMain+0x2b) [0x411dbb]\n    ./redis-server(main+0x2b6) [0x418556]\n    /lib/libc.so.6(__libc_start_main+0xfd) [0x7f16b5ba1c4d]\n    ./redis-server() [0x411099]\n    ------\n\nNote: in the example the **DEBUG SLEEP** command was used in order to block the server. The stack trace is different if the server blocks in a different context.\n\nIf you happen to collect multiple watchdog stack traces you are encouraged to send everything to the Redis Google Group: the more traces we obtain, the simpler it will be to understand what the problem with your instance is."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/management/optimization/latency-monitor","title":"Redis latency monitoring","url":"https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/latency-monitor/","summary":"Discovering slow server events in Redis","aliases":["/manual/optimization/latency-monitor/","/management/optimization/latency-monitor/"],"tags":["docs","operate","stack","oss"],"last_updated":"2026-09-21T15:59:03+01:00","page_type":"content","content_hash":"8e7acb7218ffafb1f5a42b7dd5652a1f91a85d4cb636e20ac121ab480886871a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis is often used for demanding use cases, where it\nserves a large number of queries per second per instance, but also has strict latency requirements for the average response\ntime and the worst-case latency.\n\nWhile Redis is an in-memory system, it deals with the operating system in\ndifferent ways, for example, in the context of persisting to disk.\nMoreover Redis implements a rich set of commands. Certain commands\nare fast and run in constant or logarithmic time. Other commands are slower\nO(N) commands that can cause latency spikes.\n\nFinally, Redis is single threaded. This is usually an advantage\nfrom the point of view of the amount of work it can perform per core, and in\nthe latency figures it is able to provide. However, it poses\na challenge for latency, since the single\nthread must be able to perform certain tasks incrementally, for\nexample key expiration, in a way that does not impact the other clients\nthat are served.\n\nFor all these reasons, Redis 2.8.13 introduced a new feature called\n**Latency Monitoring**, that helps the user to check and troubleshoot possible\nlatency problems. Latency monitoring is composed of the following conceptual\nparts:\n\n* Latency hooks that sample different latency-sensitive code paths.\n* Time series recording of latency spikes, split by different events.\n* Reporting engine to fetch raw data from the time series.\n* Analysis engine to provide human-readable reports and hints according to the measurements.\n\nThe rest of this document covers the latency monitoring subsystem\ndetails. For more information about the general topic of Redis\nand latency, see [Redis latency problems troubleshooting](https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/latency)."},{"id":"events-and-time-series","title":"Events and time series","role":"content","text":"Different monitored code paths have different names and are called *events*.\nFor example, `command` is an event that measures latency spikes of possibly slow\ncommand executions, while `fast-command` is the event name for the monitoring\nof the O(1) and O(log N) commands. Other events are less generic and monitor\nspecific operations performed by Redis. For example, the `fork` event\nonly monitors the time taken by Redis to execute the `fork(2)` system call.\n\nA latency spike is an event that takes more time to run than the configured latency\nthreshold. There is a separate time series associated with every monitored\nevent. This is how the time series work:\n\n* Every time a latency spike happens, it is logged in the appropriate time series.\n* Every time series is composed of 160 elements.\n* Each element is a pair made of a Unix timestamp of the time the latency spike was measured and the number of milliseconds the event took to execute.\n* Latency spikes for the same event that occur in the same second are merged by taking the maximum latency. Even if continuous latency spikes are measured for a given event, which could happen with a low threshold, at least 160 seconds of history are available.\n* Records the all-time maximum latency for every element.\n\nThe framework monitors and logs latency spikes in the execution time of these events:\n\n* `command`: regular commands.\n* `fast-command`: O(1) and O(log N) commands.\n* `fork`: the `fork(2)` system call.\n* `rdb-unlink-temp-file`: the `unlink(2)` system call.\n* `aof-fsync-always`: the `fsync(2)` system call when invoked by the `appendfsync allways` policy.\n* `aof-write`: writing to the AOF - a catchall event for `write(2)` system calls.\n* `aof-write-pending-fsync`: the `write(2)` system call when there is a pending fsync.\n* `aof-write-active-child`: the `write(2)` system call when there are active child processes.\n* `aof-write-alone`: the `write(2)` system call when no pending fsync and no active child process.\n* `aof-fstat`: the `fstat(2)` system call.\n* `aof-rename`: the `rename(2)` system call for renaming the temporary file after completing [`BGREWRITEAOF`](https://redis.io/docs/latest/commands/bgrewriteaof).\n* `aof-rewrite-diff-write`: writing the differences accumulated while performing [`BGREWRITEAOF`](https://redis.io/docs/latest/commands/bgrewriteaof).\n* `active-defrag-cycle`: the active defragmentation cycle.\n* `expire-cycle`: the expiration cycle.\n* `eviction-cycle`: the eviction cycle.\n* `eviction-del`: deletes during the eviction cycle."},{"id":"how-to-enable-latency-monitoring","title":"How to enable latency monitoring","role":"content","text":"What is high latency for one use case may not be considered high latency for another. Some applications may require that all queries be served in less than 1 millisecond. For other applications, it may be acceptable for a small amount of clients to experience a 2 second latency on occasion.\n\nThe first step to enable the latency monitor is to set a **latency threshold** in milliseconds. Only events that take longer than the specified threshold will be logged as latency spikes. The user should set the threshold according to their needs. For example, if the application requires a maximum acceptable latency of 100 milliseconds, the threshold should be set to log all the events blocking the server for a time equal or greater to 100 milliseconds.\n\nEnable the latency monitor at runtime in a production server\nwith the following command:\n\n    CONFIG SET latency-monitor-threshold 100\n\nMonitoring is turned off by default (threshold set to 0), even if the actual cost of latency monitoring is near zero. While the memory requirements of latency monitoring are very small, there is no good reason to raise the baseline memory usage of a Redis instance that is working well."},{"id":"report-information-with-the-latency-command","title":"Report information with the LATENCY command","role":"content","text":"The user interface to the latency monitoring subsystem is the [`LATENCY`](https://redis.io/docs/latest/commands/latency) command.\nLike many other Redis commands, [`LATENCY`](https://redis.io/docs/latest/commands/latency) accepts subcommands that modify its behavior. These subcommands are:\n\n* [`LATENCY LATEST`](https://redis.io/docs/latest/commands/latency-latest) - returns the latest latency samples for all events.\n* [`LATENCY HISTORY`](https://redis.io/docs/latest/commands/latency-history) - returns latency time series for a given event.\n* [`LATENCY RESET`](https://redis.io/docs/latest/commands/latency-reset) - resets latency time series data for one or more events.\n* [`LATENCY GRAPH`](https://redis.io/docs/latest/commands/latency-graph) - renders an ASCII-art graph of an event's latency samples.\n* [`LATENCY DOCTOR`](https://redis.io/docs/latest/commands/latency-doctor) - replies with a human-readable latency analysis report.\n\nRefer to each subcommand's documentation page for further information."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/management/optimization/memory-optimization","title":"Memory optimization","url":"https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/memory-optimization/","summary":"Strategies for optimizing memory usage in Redis","aliases":["/manual/optimization/memory-optimization/","/management/optimization/memory-optimization/"],"tags":["docs","operate","stack","oss"],"last_updated":"2026-09-21T15:59:03+01:00","page_type":"content","content_hash":"12df8219c72ae670fdae12119c9dba744e8c497e25c4929c441e7ccaeb4313cd","sections":[{"id":"special-encoding-of-small-aggregate-data-types","title":"Special encoding of small aggregate data types","role":"overview","text":"Since Redis 2.2 many data types are optimized to use less space up to a certain size.\nHashes, Lists, Sets composed of just integers, and Sorted Sets, when smaller than a given number of elements, and up to a maximum element size, are encoded in a very memory-efficient way that uses *up to 10 times less memory* (with 5 times less memory used being the average saving).\n\nThis is completely transparent from the point of view of the user and API.\nSince this is a CPU / memory tradeoff it is possible to tune the maximum \nnumber of elements and maximum element size for special encoded types \nusing the following redis.conf directives (defaults are shown):"},{"id":"redis--62","title":"Redis <= 6.2","role":"content","text":"[code example]"},{"id":"redis--70","title":"Redis >= 7.0","role":"content","text":"[code example]"},{"id":"redis--72","title":"Redis >= 7.2","role":"content","text":"The following directives are also available:\n\n[code example]\n\nIf a specially encoded value overflows the configured max size,\nRedis will automatically convert it into normal encoding.\nThis operation is very fast for small values,\nbut if you change the setting in order to use specially encoded values\nfor much larger aggregate types the suggestion is to run some \nbenchmarks and tests to check the conversion time."},{"id":"using-32-bit-instances","title":"Using 32-bit instances","role":"content","text":"When Redis is compiled as a 32-bit target, it uses a lot less memory per key, since pointers are small,\nbut such an instance will be limited to 4 GB of maximum memory usage.\nTo compile Redis as 32-bit binary use *make 32bit*.\nRDB and AOF files are compatible between 32-bit and 64-bit instances\n(and between little and big endian of course) so you can switch from 32 to 64-bit, or the contrary, without problems."},{"id":"bit-and-byte-level-operations","title":"Bit and byte level operations","role":"content","text":"Redis 2.2 introduced new bit and byte level operations: [`GETRANGE`](https://redis.io/docs/latest/commands/getrange), [`SETRANGE`](https://redis.io/docs/latest/commands/setrange), [`GETBIT`](https://redis.io/docs/latest/commands/getbit) and [`SETBIT`](https://redis.io/docs/latest/commands/setbit).\nUsing these commands you can treat the Redis string type as a random access array.\nFor instance, if you have an application where users are identified by a unique progressive integer number,\nyou can use a bitmap to save information about the subscription of users in a mailing list,\nsetting the bit for subscribed and clearing it for unsubscribed, or the other way around.\nWith 100 million users this data will take just 12 megabytes of RAM in a Redis instance.\nYou can do the same using [`GETRANGE`](https://redis.io/docs/latest/commands/getrange) and [`SETRANGE`](https://redis.io/docs/latest/commands/setrange) to store one byte of information for each user.\nThis is just an example but it is possible to model several problems in very little space with these new primitives."},{"id":"use-hashes-when-possible","title":"Use hashes when possible","role":"content","text":"Small hashes are encoded in a very small space, so you should try representing your data using hashes whenever possible.\nFor instance, if you have objects representing users in a web application, \ninstead of using different keys for name, surname, email, password, use a single hash with all the required fields.\n\nIf you want to know more about this, read the next section."},{"id":"using-hashes-to-abstract-a-very-memory-efficient-plain-key-value-store-on-top-of-redis","title":"Using hashes to abstract a very memory-efficient plain key-value store on top of Redis","role":"content","text":"I understand the title of this section is a bit scary, but I'm going to explain in detail what this is about.\n\nBasically it is possible to model a plain key-value store using Redis\nwhere values can just be just strings, which is not just more memory efficient\nthan Redis plain keys but also much more memory efficient than memcached.\n\nLet's start with some facts: a few keys use a lot more memory than a single key\ncontaining a hash with a few fields. How is this possible? We use a trick.\nIn theory to guarantee that we perform lookups in constant time\n(also known as O(1) in big O notation) there is the need to use a data structure\nwith a constant time complexity in the average case, like a hash table.\n\nBut many times hashes contain just a few fields. When hashes are small we can\ninstead just encode them in an O(N) data structure, like a linear\narray with length-prefixed key-value pairs. Since we do this only when N\nis small, the amortized time for [`HGET`](https://redis.io/docs/latest/commands/hget) and [`HSET`](https://redis.io/docs/latest/commands/hset) commands is still O(1): the\nhash will be converted into a real hash table as soon as the number of elements\nit contains grows too large (you can configure the limit in redis.conf).\n\nThis does not only work well from the point of view of time complexity, but\nalso from the point of view of constant times since a linear array of key-value pairs happens to play very well with the CPU cache (it has a better\ncache locality than a hash table).\n\nHowever since hash fields and values are not (always) represented as full-featured Redis objects, hash fields can't have an associated time to live\n(expire) like a real key, and can only contain a string. But we are okay with\nthis, this was the intention anyway when the hash data type API was\ndesigned (we trust simplicity more than features, so nested data structures\nare not allowed, as expires of single fields are not allowed).\n\nSo hashes are memory efficient. This is useful when using hashes\nto represent objects or to model other problems when there are group of\nrelated fields. But what about if we have a plain key value business?\n\nImagine we want to use Redis as a cache for many small objects, which can be JSON encoded objects, small HTML fragments, simple key -> boolean values\nand so forth. Basically, anything is a string -> string map with small keys\nand values.\n\nNow let's assume the objects we want to cache are numbered, like:\n\n * object:102393\n * object:1234\n * object:5\n\nThis is what we can do. Every time we perform a\nSET operation to set a new value, we actually split the key into two parts,\none part used as a key, and the other part used as the field name for the hash. For instance, the\nobject named \"object:1234\" is actually split into:\n\n* a Key named object:12\n* a Field named 34\n\nSo we use all the characters but the last two for the key, and the final\ntwo characters for the hash field name. To set our key we use the following\ncommand:\n\n[code example]\n\nAs you can see every hash will end up containing 100 fields, which is an optimal compromise between CPU and memory saved.\n\nThere is another important thing to note, with this schema\nevery hash will have more or\nless 100 fields regardless of the number of objects we cached. This is because our objects will always end with a number and not a random string. In some way, the final number can be considered as a form of implicit pre-sharding.\n\nWhat about small numbers? Like object:2? We handle this case using just\n\"object:\" as a key name, and the whole number as the hash field name.\nSo object:2 and object:10 will both end inside the key \"object:\", but one\nas field name \"2\" and one as \"10\".\n\nHow much memory do we save this way?\n\nI used the following Ruby program to test how this works:\n\n[code example]\n\nThis is the result against a 64 bit instance of Redis 2.2:\n\n * USE_OPTIMIZATION set to true: 1.7 MB of used memory\n * USE_OPTIMIZATION set to false; 11 MB of used memory\n\nThis is an order of magnitude, I think this makes Redis more or less the most\nmemory efficient plain key value store out there.\n\n*WARNING*: for this to work, make sure that in your redis.conf you have\nsomething like this:\n\n[code example]\n\nAlso remember to set the following field accordingly to the maximum size\nof your keys and values:\n\n[code example]\n\nEvery time a hash exceeds the number of elements or element size specified\nit will be converted into a real hash table, and the memory saving will be lost.\n\nYou may ask, why don't you do this implicitly in the normal key space so that\nI don't have to care? There are two reasons: one is that we tend to make\ntradeoffs explicit, and this is a clear tradeoff between many things: CPU,\nmemory, and max element size. The second is that the top-level key space must\nsupport a lot of interesting things like expires, LRU data, and so\nforth so it is not practical to do this in a general way.\n\nBut the Redis Way is that the user must understand how things work so that he can pick the best compromise and to understand how the system will\nbehave exactly."},{"id":"memory-allocation","title":"Memory allocation","role":"content","text":"To store user keys, Redis allocates at most as much memory as the `maxmemory`\nsetting enables (however there are small extra allocations possible).\n\nThe exact value can be set in the configuration file or set later via\n[`CONFIG SET`](https://redis.io/docs/latest/commands/config-set) (for more info, see [Using memory as an LRU cache](https://redis.io/docs/latest/develop/reference/eviction)).\nThere are a few things that should be noted about how Redis manages memory:\n\n* Redis will not always free up (return) memory to the OS when keys are removed.\nThis is not something special about Redis, but it is how most malloc() implementations work.\nFor example, if you fill an instance with 5GB worth of data, and then\nremove the equivalent of 2GB of data, the Resident Set Size (also known as\nthe RSS, which is the number of memory pages consumed by the process)\nwill probably still be around 5GB, even if Redis will claim that the user\nmemory is around 3GB.  This happens because the underlying allocator can't easily release the memory.\nFor example, often most of the removed keys were allocated on the same pages as the other keys that still exist.\n* The previous point means that you need to provision memory based on your\n**peak memory usage**. If your workload from time to time requires 10GB, even if\nmost of the time 5GB could do, you need to provision for 10GB.\n* However allocators are smart and are able to reuse free chunks of memory,\nso after you free 2GB of your 5GB data set, when you start adding more keys\nagain, you'll see the RSS (Resident Set Size) stay steady and not grow\nmore, as you add up to 2GB of additional keys. The allocator is basically\ntrying to reuse the 2GB of memory previously (logically) freed.\n* Because of all this, the fragmentation ratio is not reliable when you\nhad a memory usage that at the peak is much larger than the currently used memory.\nThe fragmentation is calculated as the physical memory actually used (the RSS\nvalue) divided by the amount of memory currently in use (as the sum of all\nthe allocations performed by Redis). Because the RSS reflects the peak memory,\nwhen the (virtually) used memory is low since a lot of keys/values were freed, but the RSS is high, the ratio `RSS / mem_used` will be very high.\n\nIf `maxmemory` is not set Redis will keep allocating memory as it sees\nfit and thus it can (gradually) eat up all your free memory.\nTherefore it is generally advisable to configure some limits. You may also\nwant to set `maxmemory-policy` to `noeviction` (which is *not* the default\nvalue in some older versions of Redis).\n\nIt makes Redis return an out-of-memory error for write commands if and when it reaches the \nlimit - which in turn may result in errors in the application but will not render the \nwhole machine dead because of memory starvation."}],"examples":[{"id":"redis--62-ex0","language":"plaintext","code":"hash-max-ziplist-entries 512\nhash-max-ziplist-value 64\nzset-max-ziplist-entries 128 \nzset-max-ziplist-value 64\nset-max-intset-entries 512","section_id":"redis--62"},{"id":"redis--70-ex0","language":"plaintext","code":"hash-max-listpack-entries 512\nhash-max-listpack-value 64\nzset-max-listpack-entries 128\nzset-max-listpack-value 64\nset-max-intset-entries 512","section_id":"redis--70"},{"id":"redis--72-ex0","language":"plaintext","code":"set-max-listpack-entries 128\nset-max-listpack-value 64","section_id":"redis--72"},{"id":"using-hashes-to-abstract-a-very-memory-efficient-plain-key-value-store-on-top-of-redis-ex0","language":"plaintext","code":"HSET object:12 34 somevalue","section_id":"using-hashes-to-abstract-a-very-memory-efficient-plain-key-value-store-on-top-of-redis"},{"id":"using-hashes-to-abstract-a-very-memory-efficient-plain-key-value-store-on-top-of-redis-ex1","language":"ruby","code":"require 'rubygems'\nrequire 'redis'\n\nUSE_OPTIMIZATION = true\n\ndef hash_get_key_field(key)\n  s = key.split(':')\n  if s[1].length > 2\n    { key: s[0] + ':' + s[1][0..-3], field: s[1][-2..-1] }\n  else\n    { key: s[0] + ':', field: s[1] }\n  end\nend\n\ndef hash_set(r, key, value)\n  kf = hash_get_key_field(key)\n  r.hset(kf[:key], kf[:field], value)\nend\n\ndef hash_get(r, key, value)\n  kf = hash_get_key_field(key)\n  r.hget(kf[:key], kf[:field], value)\nend\n\nr = Redis.new\n(0..100_000).each do |id|\n  key = \"object:#{id}\"\n  if USE_OPTIMIZATION\n    hash_set(r, key, 'val')\n  else\n    r.set(key, 'val')\n  end\nend","section_id":"using-hashes-to-abstract-a-very-memory-efficient-plain-key-value-store-on-top-of-redis"},{"id":"using-hashes-to-abstract-a-very-memory-efficient-plain-key-value-store-on-top-of-redis-ex2","language":"plaintext","code":"hash-max-zipmap-entries 256","section_id":"using-hashes-to-abstract-a-very-memory-efficient-plain-key-value-store-on-top-of-redis"},{"id":"using-hashes-to-abstract-a-very-memory-efficient-plain-key-value-store-on-top-of-redis-ex3","language":"plaintext","code":"hash-max-zipmap-value 1024","section_id":"using-hashes-to-abstract-a-very-memory-efficient-plain-key-value-store-on-top-of-redis"}]}
{"schema_version":2,"id":"operate/oss_and_stack/management/persistence","title":"Redis persistence","url":"https://redis.io/docs/latest/operate/oss_and_stack/management/persistence/","summary":"How Redis writes data to disk","aliases":["/management/persistence/","/manual/persistence/"],"tags":["docs","operate","stack","oss"],"last_updated":"2026-09-30T13:16:11-07:00","page_type":"content","content_hash":"91b7955d45c6f9c9355b6938bfb7452918513c15170ad407db1b65e21cf58e3d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Persistence refers to the writing of data to durable storage, such as a solid-state disk (SSD). Redis provides a range of persistence options. These include:\n\n* **RDB** (Redis Database): RDB persistence performs point-in-time snapshots of your dataset at specified intervals.\n* **AOF** (Append Only File): AOF persistence logs every write operation received by the server. These operations can then be replayed again at server startup, reconstructing the original dataset. Commands are logged using the same format as the Redis protocol itself.\n* **No persistence**: You can disable persistence completely. This is sometimes used when caching.\n* **RDB + AOF**: You can also combine both AOF and RDB in the same instance.\n\nIf you'd rather not think about the tradeoffs between these different persistence strategies, you may want to consider [Redis Software's persistence options](https://redis.io/docs/latest/operate/rs/databases/configure/database-persistence), which can be pre-configured using a UI.\n\nTo learn more about how to evaluate your Redis persistence strategy, read on."},{"id":"rdb-advantages","title":"RDB advantages","role":"content","text":"* RDB is a very compact single-file point-in-time representation of your Redis data. RDB files are perfect for backups. For instance you may want to archive your RDB files every hour for the latest 24 hours, and to save an RDB snapshot every day for 30 days. This allows you to easily restore different versions of the data set in case of disasters.\n* RDB is very good for disaster recovery, being a single compact file that can be transferred to far data centers, or onto Amazon S3 (possibly encrypted).\n* RDB maximizes Redis performances since the only work the Redis parent process needs to do in order to persist is forking a child that will do all the rest. The parent process will never perform disk I/O or alike.\n* RDB allows faster restarts with big datasets compared to AOF.\n* On replicas, RDB supports [partial resynchronizations after restarts and failovers](https://redis.io/docs/latest/operate/oss_and_stack/management/replication#partial-resynchronizations-after-restarts-and-failovers)."},{"id":"rdb-disadvantages","title":"RDB disadvantages","role":"content","text":"* RDB is NOT good if you need to minimize the chance of data loss in case Redis stops working (for example after a power outage). You can configure different *save points* where an RDB is produced (for instance after at least five minutes and 100 writes against the data set, you can have multiple save points). However you'll usually create an RDB snapshot every five minutes or more, so in case of Redis stopping working without a correct shutdown for any reason you should be prepared to lose the latest minutes of data.\n* RDB needs to fork() often in order to persist on disk using a child process. fork() can be time consuming if the dataset is big, and may result in Redis stopping serving clients for some milliseconds or even for one second if the dataset is very big and the CPU performance is not great. AOF also needs to fork() but less frequently and you can tune how often you want to rewrite your logs without any trade-off on durability."},{"id":"aof-advantages","title":"AOF advantages","role":"content","text":"* Using AOF Redis is much more durable: you can have different fsync policies: no fsync at all, fsync every second, fsync at every query. With the default policy of fsync every second, write performance is still great. fsync is performed using a background thread and the main thread will try hard to perform writes when no fsync is in progress, so you can only lose one second worth of writes.\n* The AOF log is an append-only log, so there are no seeks, nor corruption problems if there is a power outage. Even if the log ends with a half-written command for some reason (disk full or other reasons) the redis-check-aof tool is able to fix it easily.\n* Redis is able to automatically rewrite the AOF in background when it gets too big. The rewrite is completely safe as while Redis continues appending to the old file, a completely new one is produced with the minimal set of operations needed to create the current data set, and once this second file is ready Redis switches the two and starts appending to the new one.\n* AOF contains a log of all the operations one after the other in an easy to understand and parse format. You can even easily export an AOF file. For instance even if you've accidentally flushed everything using the [`FLUSHALL`](https://redis.io/docs/latest/commands/flushall) command, as long as no rewrite of the log was performed in the meantime, you can still save your data set just by stopping the server, removing the latest command, and restarting Redis again."},{"id":"aof-disadvantages","title":"AOF disadvantages","role":"content","text":"* AOF files are usually bigger than the equivalent RDB files for the same dataset.\n* AOF can be slower than RDB depending on the exact fsync policy. In general with fsync set to *every second* performance is still very high, and with fsync disabled it should be exactly as fast as RDB even under high load. Still RDB is able to provide more guarantees about the maximum latency even in the case of a huge write load.\n\n**Redis < 7.0**\n\n* AOF can use a lot of memory if there are writes to the database during a rewrite (these are buffered in memory and written to the new AOF at the end).\n* All write commands that arrive during rewrite are written to disk twice.\n* Redis could freeze writing and fsyncing these write commands to the new AOF file at the end of the rewrite.\n  \nOk, so what should I use?\n---\n\nThe general indication you should use both persistence methods is if\nyou want a degree of data safety comparable to what PostgreSQL can provide you.\n\nIf you care a lot about your data, but still can live with a few minutes of\ndata loss in case of disasters, you can simply use RDB alone.\n\nThere are many users using AOF alone, but we discourage it since to have an\nRDB snapshot from time to time is a great idea for doing database backups,\nfor faster restarts, and in the event of bugs in the AOF engine.\n\nThe following sections will illustrate a few more details about the two persistence models."},{"id":"snapshotting","title":"Snapshotting","role":"content","text":"By default Redis saves snapshots of the dataset on disk, in a binary\nfile called `dump.rdb`. You can configure Redis to have it save the\ndataset every N seconds if there are at least M changes in the dataset,\nor you can manually call the [`SAVE`](https://redis.io/docs/latest/commands/save) or [`BGSAVE`](https://redis.io/docs/latest/commands/bgsave) commands.\n\nFor example, this configuration will make Redis automatically dump the\ndataset to disk every 60 seconds if at least 1000 keys changed:\n\n    save 60 1000\n\nThis strategy is known as _snapshotting_."},{"id":"how-it-works","title":"How it works","role":"content","text":"Whenever Redis needs to dump the dataset to disk, this is what happens:\n\n* Redis [forks](http://linux.die.net/man/2/fork). We now have a child\nand a parent process.\n\n* The child starts to write the dataset to a temporary RDB file.\n\n* When the child is done writing the new RDB file, it replaces the old\none.\n\nThis method allows Redis to benefit from copy-on-write semantics."},{"id":"append-only-file","title":"Append-only file","role":"content","text":"Snapshotting is not very durable. If your computer running Redis stops,\nyour power line fails, or you accidentally `kill -9` your instance, the\nlatest data written to Redis will be lost.  While this may not be a big\ndeal for some applications, there are use cases for full durability, and\nin these cases Redis snapshotting alone is not a viable option.\n\nThe _append-only file_ is an alternative, fully-durable strategy for\nRedis.  It became available in version 1.1.\n\nYou can turn on the AOF in your configuration file:\n\n    appendonly yes\n\nFrom now on, every time Redis receives a command that changes the\ndataset (e.g. [`SET`](https://redis.io/docs/latest/commands/set)) it will append it to the AOF.  When you restart\nRedis it will re-play the AOF to rebuild the state.\n\nSince Redis 7.0.0, Redis uses a multi part AOF mechanism.\nThat is, the original single AOF file is split into base file (at most one) and incremental files (there may be more than one).\nThe base file represents an initial (RDB or AOF format) snapshot of the data present when the AOF is [rewritten](#log-rewriting).\nThe incremental files contains incremental changes since the last base AOF file was created. All these files are put in a separate directory and are tracked by a manifest file."},{"id":"log-rewriting","title":"Log rewriting","role":"content","text":"The AOF gets bigger and bigger as write operations are\nperformed.  For example, if you are incrementing a counter 100 times,\nyou'll end up with a single key in your dataset containing the final\nvalue, but 100 entries in your AOF. 99 of those entries are not needed\nto rebuild the current state.\n\nThe rewrite is completely safe.\nWhile Redis continues appending to the old file,\na completely new one is produced with the minimal set of operations needed to create the current data set,\nand once this second file is ready Redis switches the two and starts appending to the new one.\n\nSo Redis supports an interesting feature: it is able to rebuild the AOF\nin the background without interrupting service to clients. Whenever\nyou issue a [`BGREWRITEAOF`](https://redis.io/docs/latest/commands/bgrewriteaof), Redis will write the shortest sequence of\ncommands needed to rebuild the current dataset in memory.  If you're\nusing the AOF with Redis 2.2 you'll need to run [`BGREWRITEAOF`](https://redis.io/docs/latest/commands/bgrewriteaof) from time to\ntime. Since Redis 2.4 is able to trigger log rewriting automatically (see the\nexample configuration file for more information).\n\nSince Redis 7.0.0, when an AOF rewrite is scheduled, the Redis parent process opens a new incremental AOF file to continue writing.\nThe child process executes the rewrite logic and generates a new base AOF.\nRedis will use a temporary manifest file to track the newly generated base file and incremental file.\nWhen they are ready, Redis will perform an atomic replacement operation to make this temporary manifest file take effect.\nIn order to avoid the problem of creating many incremental files in case of repeated failures and retries of an AOF rewrite,\nRedis introduces an AOF rewrite limiting mechanism to ensure that failed AOF rewrites are retried at a slower and slower rate."},{"id":"how-durable-is-the-append-only-file","title":"How durable is the append only file?","role":"content","text":"You can configure how many times Redis will\n[`fsync`](http://linux.die.net/man/2/fsync) data on disk. There are\nthree options:\n\n* `appendfsync always`: `fsync` every time new commands are appended to the AOF. Very very slow, very safe. Note that the commands are appended to the AOF after a batch of commands from multiple clients or a pipeline are executed, so it means a single write and a single fsync (before sending the replies).\n* `appendfsync everysec`: `fsync` every second. Fast enough (since version 2.4 likely to be as fast as snapshotting), and you may lose 1 second of data if there is a disaster.\n* `appendfsync no`: Never `fsync`, just put your data in the hands of the Operating System. The faster and less safe method. Normally Linux will flush data every 30 seconds with this configuration, but it's up to the kernel's exact tuning.\n\nThe suggested (and default) policy is to `fsync` every second. It is\nboth fast and relatively safe. The `always` policy is very slow in\npractice, but it supports group commit, so if there are multiple parallel\nwrites Redis will try to perform a single `fsync` operation."},{"id":"what-should-i-do-if-my-aof-gets-truncated","title":"What should I do if my AOF gets truncated?","role":"content","text":"It is possible the server crashed while writing the AOF file, or the\nvolume where the AOF file is stored was full at the time of writing. When this happens the\nAOF still contains consistent data representing a given point-in-time version\nof the dataset (that may be old up to one second with the default AOF fsync\npolicy), but the last command in the AOF could be truncated.\nThe latest major versions of Redis will be able to load the AOF anyway, just\ndiscarding the last non well formed command in the file. In this case the\nserver will emit a log like the following:\n\n[code example]\n\nYou can change the default configuration to force Redis to stop in such\ncases if you want, but the default configuration is to continue regardless of\nthe fact the last command in the file is not well-formed, in order to guarantee\navailability after a restart.\n\nOlder versions of Redis may not recover, and may require the following steps:\n\n* Make a backup copy of your AOF file.\n* Fix the original file using the `redis-check-aof` tool that ships with Redis:\n\n      $ redis-check-aof --fix <filename>\n\n* Optionally use `diff -u` to check what is the difference between two files.\n* Restart the server with the fixed file."},{"id":"what-should-i-do-if-my-aof-gets-corrupted","title":"What should I do if my AOF gets corrupted?","role":"content","text":"If the AOF file is not just truncated, but corrupted with invalid byte\nsequences in the middle, things are more complex. Redis will complain\nat startup and will abort:\n\n[code example]\n\nThe best thing to do is to run the `redis-check-aof` utility, initially without\nthe `--fix` option, then understand the problem, jump to the given\noffset in the file, and see if it is possible to manually repair the file:\nThe AOF uses the same format of the Redis protocol and is quite simple to fix\nmanually. Otherwise it is possible to let the utility fix the file for us, but\nin that case all the AOF portion from the invalid part to the end of the\nfile may be discarded, leading to a massive amount of data loss if the\ncorruption happened to be in the initial part of the file."},{"id":"how-it-works-1","title":"How it works","role":"content","text":"Log rewriting uses the same copy-on-write trick already in use for\nsnapshotting.  This is how it works:\n\n**Redis >= 7.0**\n\n* Redis [forks](http://linux.die.net/man/2/fork), so now we have a child\nand a parent process.\n\n* The child starts writing the new base AOF in a temporary file.\n\n* The parent opens a new increments AOF file to continue writing updates.\n  If the rewriting fails, the old base and increment files (if there are any) plus this newly opened increment file represent the complete updated dataset,\n  so we are safe.\n  \n* When the child is done rewriting the base file, the parent gets a signal,\nand uses the newly opened increment file and child generated base file to build a temp manifest,\nand persist it.\n\n* Profit! Now Redis does an atomic exchange of the manifest files so that the result of this AOF rewrite takes effect. Redis also cleans up the old base file and any unused increment files.\n\n**Redis < 7.0**\n\n* Redis [forks](http://linux.die.net/man/2/fork), so now we have a child\nand a parent process.\n\n* The child starts writing the new AOF in a temporary file.\n\n* The parent accumulates all the new changes in an in-memory buffer (but\nat the same time it writes the new changes in the old append-only file,\nso if the rewriting fails, we are safe).\n\n* When the child is done rewriting the file, the parent gets a signal,\nand appends the in-memory buffer at the end of the file generated by the\nchild.\n\n* Now Redis atomically renames the new file into the old one,\nand starts appending new data into the new file."},{"id":"how-i-can-switch-to-aof-if-im-currently-using-dumprdb-snapshots","title":"How I can switch to AOF, if I'm currently using dump.rdb snapshots?","role":"content","text":"If you want to enable AOF in a server that is currently using RDB snapshots, you need to convert the data by enabling AOF via CONFIG command on the live server first.\n\n**IMPORTANT:** not following this procedure (e.g. just changing the config and restarting the server) can result in data loss!\n\n**Redis >= 2.2**\n\nPreparations:\n\n* Make a backup of your latest dump.rdb file.\n* Transfer this backup to a safe place.\n\nSwitch to AOF on live database:\n\n* Enable AOF: `redis-cli config set appendonly yes`\n* Optionally disable RDB: `redis-cli config set save \"\"`\n* Make sure writes are appended to the append only file correctly.\n* **IMPORTANT:** Update your `redis.conf` (potentially through [`CONFIG REWRITE`](https://redis.io/docs/latest/commands/config-rewrite)) and ensure that it matches the configuration above.\n  If you forget this step, when you restart the server, the configuration changes will be lost and the server will start again with the old configuration, resulting in a loss of your data.\n\nNext time you restart the server:\n\n* Before restarting the server, wait for AOF rewrite to finish persisting the data.\n  You can do that by watching `INFO persistence`, waiting for `aof_rewrite_in_progress` and `aof_rewrite_scheduled` to be `0`, and validating that `aof_last_bgrewrite_status` is `ok`.\n* After restarting the server, check that your database contains the same number of keys it contained previously.\n\n**Redis 2.0**\n\n* Make a backup of your latest dump.rdb file.\n* Transfer this backup into a safe place.\n* Stop all the writes against the database!\n* Issue a `redis-cli BGREWRITEAOF`. This will create the append only file.\n* Stop the server when Redis finished generating the AOF dump.\n* Edit redis.conf end enable append only file persistence.\n* Restart the server.\n* Make sure that your database contains the same number of keys it contained before the switch.\n* Make sure that writes are appended to the append only file correctly."},{"id":"interactions-between-aof-and-rdb-persistence","title":"Interactions between AOF and RDB persistence","role":"content","text":"Redis >= 2.4 makes sure to avoid triggering an AOF rewrite when an RDB\nsnapshotting operation is already in progress, or allowing a [`BGSAVE`](https://redis.io/docs/latest/commands/bgsave) while the\nAOF rewrite is in progress. This prevents two Redis background processes\nfrom doing heavy disk I/O at the same time.\n\nWhen snapshotting is in progress and the user explicitly requests a log\nrewrite operation using [`BGREWRITEAOF`](https://redis.io/docs/latest/commands/bgrewriteaof) the server will reply with an OK\nstatus code telling the user the operation is scheduled, and the rewrite\nwill start once the snapshotting is completed.\n\nIn the case both AOF and RDB persistence are enabled and Redis restarts the\nAOF file will be used to reconstruct the original dataset since it is\nguaranteed to be the most complete."},{"id":"backing-up-redis-data","title":"Backing up Redis data","role":"content","text":"Before starting this section, make sure to read the following sentence: **Make Sure to Backup Your Database**. Disks break, instances in the cloud disappear, and so forth: no backups means huge risk of data disappearing into /dev/null.\n\nRedis is very data backup friendly since you can copy RDB files while the\ndatabase is running: the RDB is never modified once produced, and while it\ngets produced it uses a temporary name and is renamed into its final destination\natomically using rename(2) only when the new snapshot is complete.\n\nThis means that copying the RDB file is completely safe while the server is\nrunning. This is what we suggest:\n\n* Create a cron job in your server creating hourly snapshots of the RDB file in one directory, and daily snapshots in a different directory.\n* Every time the cron script runs, make sure to call the `find` command to make sure too old snapshots are deleted: for instance you can take hourly snapshots for the latest 48 hours, and daily snapshots for one or two months. Make sure to name the snapshots with date and time information.\n* At least one time every day make sure to transfer an RDB snapshot *outside your data center* or at least *outside the physical machine* running your Redis instance."},{"id":"backing-up-aof-persistence","title":"Backing up AOF persistence","role":"content","text":"If you run a Redis instance with only AOF persistence enabled, you can still perform backups.\nSince Redis 7.0.0, AOF files are split into multiple files which reside in a single directory determined by the `appenddirname` configuration.\nDuring normal operation all you need to do is copy/tar the files in this directory to achieve a backup. However, if this is done during a [rewrite](#log-rewriting), you might end up with an invalid backup.\nTo work around this you must disable AOF rewrites during the backup:\n\n1. Turn off automatic rewrites with<br/>\n   [`CONFIG SET`](https://redis.io/docs/latest/commands/config-set) `auto-aof-rewrite-percentage 0`<br/>\n   Make sure you don't manually start a rewrite (using [`BGREWRITEAOF`](https://redis.io/docs/latest/commands/bgrewriteaof)) during this time.\n2. Check there's no current rewrite in progress using<br/>\n   [`INFO`](https://redis.io/docs/latest/commands/info) `persistence`<br/>\n   and verifying `aof_rewrite_in_progress` is 0. If it's 1, then you'll need to wait for the rewrite to complete.\n3. Now you can safely copy the files in the `appenddirname` directory.\n4. Re-enable rewrites when done:<br/>\n   [`CONFIG SET`](https://redis.io/docs/latest/commands/config-set) `auto-aof-rewrite-percentage <prev-value>`\n\n**Note:** If you want to minimize the time AOF rewrites are disabled you may create hard links to the files in `appenddirname` (in step 3 above) and then re-enable rewrites (step 4) after the hard links are created.\nNow you can copy/tar the hardlinks and delete them when done. This works because Redis guarantees that it\nonly appends to files in this directory, or completely replaces them if necessary, so the content should be\nconsistent at any given point in time.\n\n\n**Note:** If you want to handle the case of the server being restarted during the backup and make sure no rewrite will automatically start after the restart you can change step 1 above to also persist the updated configuration via [`CONFIG REWRITE`](https://redis.io/docs/latest/commands/config-rewrite).\nJust make sure to re-enable automatic rewrites when done (step 4) and persist it with another [`CONFIG REWRITE`](https://redis.io/docs/latest/commands/config-rewrite).\n\nPrior to version 7.0.0 backing up the AOF file can be done simply by copying the aof file (like backing up the RDB snapshot). The file may lack the final part\nbut Redis will still be able to load it (see the previous sections about [truncated AOF files](#what-should-i-do-if-my-aof-gets-truncated))."},{"id":"online-backups-with-the-backup-command-family","title":"Online backups with the BACKUP command family","role":"content","text":"Since Redis 8.10.0, the [`BACKUP`](https://redis.io/docs/latest/commands/backup) command family produces a self-contained, restorable backup without stopping writes or manually managing AOF rewrites. A backup reuses the [multi-part AOF](#append-only-file) format, so it is an MP-AOF–compatible artifact set consisting of three files:\n\n* **BASE** — a point-in-time snapshot (`appendonly.aof.N.base.rdb`).\n* **INCR** — the incremental writes appended after the snapshot (`appendonly.aof.N.incr.aof`).\n* **Manifest** — a standalone manifest describing the file set (`appendonly.aof.manifest`).\n\nBecause backup creation is separated from backup finalization, a control plane can stagger [`BACKUP START`](https://redis.io/docs/latest/commands/backup-start) across the nodes of a cluster so that they do not all `fork` at the same time. Each node produces its BASE independently, keeps appending to its INCR, and is later frozen with [`BACKUP SEAL`](https://redis.io/docs/latest/commands/backup-seal). A restore loads `BASE + INCR`, so the restored dataset reflects the state at the seal boundary, not merely the earlier BASE snapshot.\n\nThe typical workflow on a node is:\n\n1. [`BACKUP START`](https://redis.io/docs/latest/commands/backup-start) — open a backup window and produce a fresh BASE snapshot. This works whether or not AOF persistence is enabled.\n2. [`BACKUP LIST`](https://redis.io/docs/latest/commands/backup-list) — get the absolute paths of the immutable files pinned so far. The data plane can begin copying the BASE file while Redis keeps accumulating INCR data.\n3. [`BACKUP SEAL`](https://redis.io/docs/latest/commands/backup-seal) — freeze the backup, hard-linking the INCR and writing the manifest. After sealing, `BACKUP LIST` also includes the INCR and manifest files.\n4. Copy the sealed files reported by `BACKUP LIST` to your backup storage.\n5. [`BACKUP CLEANUP`](https://redis.io/docs/latest/commands/backup-cleanup) — remove the sealed files and release the pinned artifacts after the copy is complete.\n\nUse [`BACKUP STATUS`](https://redis.io/docs/latest/commands/backup-status) at any point to inspect the current backup state, and [`BACKUP ABORT`](https://redis.io/docs/latest/commands/backup-abort) to cancel a backup that has not been sealed yet.\n\nThe backup files are written to the directory named by the `backupdirname` configuration setting (default `backupdir`), resolved under the Redis working directory `dir`. `backupdirname` is a startup-only setting. The `backup-sealed-ttl` setting controls how long, in seconds, Redis keeps a sealed backup before cleaning it up automatically; a value of `0` (the default) disables automatic cleanup so the files remain pinned until `BACKUP CLEANUP` is called.\n\n#### Restoring a backup\n\nTo restore a backup, use the startup-only `preload-file` configuration setting, which loads a specific file or manifest during server startup. It is specified as `<type>:<path>`, where `<type>` is either `aof` or `rdb`. To restore a sealed backup, point it at the manifest:\n\n[code example]\n\nYou can also preload a single RDB file:\n\n[code example]\n\nWhen `preload-file` is set, Redis loads only the specified file or manifest and its associated files. It skips the normal `appenddirname` and `dump.rdb` loading, even if AOF or RDB persistence is enabled. If the preload target is missing or cannot be loaded, startup aborts. After the preload finishes, Redis continues with its normally configured persistence mode."},{"id":"disaster-recovery","title":"Disaster recovery","role":"content","text":"Disaster recovery in the context of Redis is basically the same story as\nbackups, plus the ability to transfer those backups in many different external\ndata centers. This way data is secured even in the case of some catastrophic\nevent affecting the main data center where Redis is running and producing its\nsnapshots.\n\nWe'll review the most interesting disaster recovery techniques\nthat don't have too high costs.\n\n* Amazon S3 and other similar services are a good way for implementing your disaster recovery system. Simply transfer your daily or hourly RDB snapshot to S3 in an encrypted form. You can encrypt your data using `gpg -c` (in symmetric encryption mode). Make sure to store your password in many different safe places (for instance give a copy to the most important people of your organization). It is recommended to use multiple storage services for improved data safety.\n* Transfer your snapshots using SCP (part of SSH) to far servers. This is a fairly simple and safe route: get a small VPS in a place that is very far from you, install ssh there, and generate a ssh client key without passphrase, then add it in the `authorized_keys` file of your small VPS. You are ready to transfer backups in an automated fashion. Get at least two VPS in two different providers\nfor best results.\n\nIt is important to understand that this system can easily fail if not\nimplemented in the right way. At least, make absolutely sure that after the\ntransfer is completed you are able to verify the file size (that should match\nthe one of the file you copied) and possibly the SHA1 digest, if you are using\na VPS.\n\nYou also need some kind of independent alert system if the transfer of fresh\nbackups is not working for some reason."}],"examples":[{"id":"what-should-i-do-if-my-aof-gets-truncated-ex0","language":"plaintext","code":"* Reading RDB preamble from AOF file...\n* Reading the remaining AOF tail...\n# !!! Warning: short read while loading the AOF file !!!\n# !!! Truncating the AOF at offset 439 !!!\n# AOF loaded anyway because aof-load-truncated is enabled","section_id":"what-should-i-do-if-my-aof-gets-truncated"},{"id":"what-should-i-do-if-my-aof-gets-corrupted-ex0","language":"plaintext","code":"* Reading the remaining AOF tail...\n# Bad file format reading the append only file: make a backup of your AOF file, then use ./redis-check-aof --fix <filename>","section_id":"what-should-i-do-if-my-aof-gets-corrupted"},{"id":"online-backups-with-the-backup-command-family-ex0","language":"conf","code":"preload-file aof:/tmp/restore-backup/appendonly.aof.manifest","section_id":"online-backups-with-the-backup-command-family"},{"id":"online-backups-with-the-backup-command-family-ex1","language":"conf","code":"preload-file rdb:/tmp/backup-to-recover.rdb","section_id":"online-backups-with-the-backup-command-family"}]}
{"schema_version":2,"id":"operate/oss_and_stack/management/replication","title":"Redis replication","url":"https://redis.io/docs/latest/operate/oss_and_stack/management/replication/","summary":"How Redis supports high availability and failover with replication","aliases":["/manual/replication/","/management/replication/","/operate/replication/"],"tags":["docs","operate","stack","oss"],"last_updated":"2026-09-21T15:59:03+01:00","page_type":"content","content_hash":"cd4755cd72311f09647f1ced97d1437f5b5561c241cd93d2575203480f8a8b7e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"At the base of Redis replication (excluding the high availability features provided as an additional layer by Redis Cluster or Redis Sentinel) there is a *leader follower* (master-replica) replication that is simple to use and configure. It allows replica Redis instances to be exact copies of master instances. The replica will automatically reconnect to the master every time the link breaks, and will attempt to be an exact copy of it *regardless* of what happens to the master.\n\nThis system works using three main mechanisms:\n\n1. When a master and a replica instance are well-connected, the master keeps the replica updated by sending a stream of commands to the replica to replicate the effects on the dataset happening on the master side due to: client writes, keys expired or evicted, any other action changing the master dataset.\n2. When the link between the master and the replica breaks, for network issues or because a timeout is sensed in the master or the replica, the replica reconnects and attempts to proceed with a partial resynchronization: it means that it will try to just obtain the part of the stream of commands it missed during the disconnection.\n3. When a partial resynchronization is not possible, the replica will ask for a full resynchronization. This will involve a more complex process in which the master needs to create a snapshot of all its data, send it to the replica, and then continue sending the stream of commands as the dataset changes.\n\nRedis uses by default asynchronous replication, which being low latency and\nhigh performance, is the natural replication mode for the vast majority of Redis\nuse cases. However, Redis replicas asynchronously acknowledge the amount of data\nthey receive periodically with the master. So the master does not wait every time\nfor a command to be processed by the replicas, however it knows, if needed, what\nreplica already processed what command. This allows having optional synchronous replication.\n\nSynchronous replication of certain data can be requested by the clients using\nthe [`WAIT`](https://redis.io/docs/latest/commands/wait) command. However [`WAIT`](https://redis.io/docs/latest/commands/wait) is only able to ensure there are the\nspecified number of acknowledged copies in the other Redis instances, it does not\nturn a set of Redis instances into a CP system with strong consistency: acknowledged\nwrites can still be lost during a failover, depending on the exact configuration\nof the Redis persistence. However, [WAIT](https://redis.io/docs/latest/commands/wait) dramatically reduces the probability of losing a write after a failure event to specific hard-to-trigger failure modes.\n\nYou can check the Redis Sentinel or Redis Cluster documentation for more information\nabout high availability and failover. The rest of this document mainly describes the basic characteristics of Redis basic replication."},{"id":"important-facts-about-redis-replication","title":"Important facts about Redis replication","role":"content","text":"* Redis uses asynchronous replication, with asynchronous replica-to-master acknowledges of the amount of data processed.\n* A master can have multiple replicas.\n* Replicas are able to accept connections from other replicas. Aside from connecting a number of replicas to the same master, replicas can also be connected to other replicas in a cascading-like structure. Since Redis 4.0, all the sub-replicas will receive exactly the same replication stream from the master.\n* Redis replication is non-blocking on the master side. This means that the master will continue to handle queries when one or more replicas perform the initial synchronization or a partial resynchronization.\n* Replication is also largely non-blocking on the replica side. While the replica is performing the initial synchronization, it can handle queries using the old version of the dataset, assuming you configured Redis to do so in redis.conf.  Otherwise, you can configure Redis replicas to return an error to clients if the replication stream is down. However, after the initial sync, the old dataset must be deleted and the new one must be loaded. The replica will block incoming connections during this brief window (that can be as long as many seconds for very large datasets). Since Redis 4.0 you can configure Redis so that the deletion of the old data set happens in a different thread, however loading the new initial dataset will still happen in the main thread and block the replica.\n* Replication can be used both for scalability, to have multiple replicas for read-only queries (for example, slow O(N) operations can be offloaded to replicas), or simply for improving data safety and high availability.\n* You can use replication to avoid the cost of having the master writing the full dataset to disk: a typical technique involves configuring your master `redis.conf` to avoid persisting to disk at all, then connect a replica configured to save from time to time, or with AOF enabled. However, this setup must be handled with care, since a restarting master will start with an empty dataset: if the replica tries to sync with it, the replica will be emptied as well."},{"id":"safety-of-replication-when-master-has-persistence-turned-off","title":"Safety of replication when master has persistence turned off","role":"content","text":"In setups where Redis replication is used, it is strongly advised to have\npersistence turned on in the master and in the replicas. When this is not possible,\nfor example because of latency concerns due to very slow disks, instances should\nbe configured to **avoid restarting automatically** after a reboot.\n\nTo better understand why masters with persistence turned off configured to\nauto restart are dangerous, check the following failure mode where data\nis wiped from the master and all its replicas:\n\n1. We have a setup with node A acting as master, with persistence turned down, and nodes B and C replicating from node A.\n2. Node A crashes, however it has some auto-restart system, that restarts the process. However since persistence is turned off, the node restarts with an empty data set.\n3. Nodes B and C will replicate from node A, which is empty, so they'll effectively destroy their copy of the data.\n\nWhen Redis Sentinel is used for high availability, also turning off persistence\non the master, together with auto restart of the process, is dangerous. For example the master can restart fast enough for Sentinel to not detect a failure, so that the failure mode described above happens.\n\nEvery time data safety is important, and replication is used with master configured without persistence, auto restart of instances should be disabled."},{"id":"how-redis-replication-works","title":"How Redis replication works","role":"content","text":"Every Redis master has a replication ID: it is a large pseudo random string\nthat marks a given history of the dataset. Each master also takes an offset that\nincrements for every byte of replication stream that it is produced to be\nsent to replicas, to update the state of the replicas with the new changes\nmodifying the dataset. The replication offset is incremented even if no replica\nis actually connected, so basically every given pair of:\n\n    Replication ID, offset\n\nIdentifies an exact version of the dataset of a master.\n\nWhen replicas connect to masters, they use the [`PSYNC`](https://redis.io/docs/latest/commands/psync) command to send\ntheir old master replication ID and the offsets they processed so far. This way\nthe master can send just the incremental part needed. However if there is not\nenough *backlog* in the master buffers, or if the replica is referring to an\nhistory (replication ID) which is no longer known, then a full resynchronization\nhappens: in this case the replica will get a full copy of the dataset, from scratch.\n\nThis is how a full synchronization works in more details:\n\nThe master starts a background saving process to produce an RDB file. At the same time it starts to buffer all new write commands received from the clients. When the background saving is complete, the master transfers the database file to the replica, which saves it on disk, and then loads it into memory. The master will then send all buffered commands to the replica. This is done as a stream of commands and is in the same format of the Redis protocol itself.\n\nYou can try it yourself via telnet. Connect to the Redis port while the\nserver is doing some work and issue the [`SYNC`](https://redis.io/docs/latest/commands/sync) command. You'll see a bulk\ntransfer and then every command received by the master will be re-issued\nin the telnet session. Actually [`SYNC`](https://redis.io/docs/latest/commands/sync) is an old protocol no longer used by\nnewer Redis instances, but is still there for backward compatibility: it does\nnot allow partial resynchronizations, so now [`PSYNC`](https://redis.io/docs/latest/commands/psync) is used instead.\n\nAs already said, replicas are able to automatically reconnect when the master-replica link goes down for some reason. If the master receives multiple concurrent replica synchronization requests, it performs a single background save in to serve all of them."},{"id":"replication-id-explained","title":"Replication ID explained","role":"content","text":"In the previous section we said that if two instances have the same replication\nID and replication offset, they have exactly the same data. However it is useful\nto understand what exactly is the replication ID, and why instances have actually\ntwo replication IDs: the main ID and the secondary ID.\n\nA replication ID basically marks a given *history* of the data set. Every time\nan instance restarts from scratch as a master, or a replica is promoted to master,\na new replication ID is generated for this instance. The replicas connected to\na master will inherit its replication ID after the handshake. So two instances\nwith the same ID are related by the fact that they hold the same data, but\npotentially at a different time. It is the offset that works as a logical time\nto understand, for a given history (replication ID), who holds the most updated\ndata set.\n\nFor instance, if two instances A and B have the same replication ID, but one\nwith offset 1000 and one with offset 1023, it means that the first lacks certain\ncommands applied to the data set. It also means that A, by applying just a few\ncommands, may reach exactly the same state of B.\n\nThe reason why Redis instances have two replication IDs is because of replicas\nthat are promoted to masters. After a failover, the promoted replica requires\nto still remember what was its past replication ID, because such replication ID\nwas the one of the former master. In this way, when other replicas will sync\nwith the new master, they will try to perform a partial resynchronization using the\nold master replication ID. This will work as expected, because when the replica\nis promoted to master it sets its secondary ID to its main ID, remembering what\nwas the offset when this ID switch happened. Later it will select a new random\nreplication ID, because a new history begins. When handling the new replicas\nconnecting, the master will match their IDs and offsets both with the current\nID and the secondary ID (up to a given offset, for safety). In short this means\nthat after a failover, replicas connecting to the newly promoted master don't have\nto perform a full sync.\n\nIn case you wonder why a replica promoted to master needs to change its\nreplication ID after a failover: it is possible that the old master is still\nworking as a master because of some network partition: retaining the same\nreplication ID would violate the fact that the same ID and same offset of any\ntwo random instances mean they have the same data set."},{"id":"diskless-replication","title":"Diskless replication","role":"content","text":"Normally a full resynchronization requires creating an RDB file on disk,\nthen reloading the same RDB from disk to feed the replicas with the data.\n\nWith slow disks this can be a very stressing operation for the master.\nRedis version 2.8.18 is the first version to have support for diskless\nreplication. In this setup the child process directly sends the\nRDB over the wire to replicas, without using the disk as intermediate storage."},{"id":"configuration","title":"Configuration","role":"configuration","text":"To configure basic Redis replication is trivial: just add the following line to the replica configuration file:\n\n    replicaof 192.168.1.1 6379\n\nOf course you need to replace 192.168.1.1 6379 with your master IP address (or\nhostname) and port. Alternatively, you can call the [`REPLICAOF`](https://redis.io/docs/latest/commands/replicaof) command and the\nmaster host will start a sync with the replica.\n\nThere are also a few parameters for tuning the replication backlog taken\nin memory by the master to perform the partial resynchronization. See the example\n`redis.conf` shipped with the Redis distribution for more information.\n\nDiskless replication can be enabled using the `repl-diskless-sync` configuration\nparameter. The delay to start the transfer to wait for more replicas to\narrive after the first one is controlled by the `repl-diskless-sync-delay`\nparameter. Please refer to the example `redis.conf` file in the Redis distribution\nfor more details."},{"id":"read-only-replica","title":"Read-only replica","role":"content","text":"Since Redis 2.6, replicas support a read-only mode that is enabled by default.\nThis behavior is controlled by the `replica-read-only` option in the redis.conf file, and can be enabled and disabled at runtime using [`CONFIG SET`](https://redis.io/docs/latest/commands/config-set).\n\nRead-only replicas will reject all write commands, so that it is not possible to write to a replica because of a mistake. This does not mean that the feature is intended to expose a replica instance to the internet or more generally to a network where untrusted clients exist, because administrative commands like [`DEBUG`](https://redis.io/docs/latest/commands/debug) or [`CONFIG`](https://redis.io/docs/latest/commands/config) are still enabled. The [Security](https://redis.io/docs/latest/operate/oss_and_stack/management/security) page describes how to secure a Redis instance.\n\nYou may wonder why it is possible to revert the read-only setting\nand have replica instances that can be targeted by write operations.\nThe answer is that writable replicas exist only for historical reasons.\nUsing writable replicas can result in inconsistency between the master and the replica, so it is not recommended to use writable replicas.\nTo understand in which situations this can be a problem, we need to understand how replication works.\nChanges on the master is replicated by propagating regular Redis commands to the replica.\nWhen a key expires on the master, this is propagated as a DEL command.\nIf a key which exists on the master but is deleted, expired or has a different type on the replica compared to the master will react differently to commands like DEL, INCR or RPOP propagated from the master than intended.\nThe propagated command may fail on the replica or result in a different outcome.\nTo minimize the risks (if you insist on using writable replicas) we suggest you follow these recommendations:\n\n* Don't write to keys in a writable replica that are also used on the master.\n  (This can be hard to guarantee if you don't have control over all the clients that write to the master.)\n\n* Don't configure an instance as a writable replica as an intermediary step when upgrading a set of instances in a running system.\n  In general, don't configure an instance as a writable replica if it can ever be promoted to a master if you want to guarantee data consistency.\n\nHistorically, there were some use cases that were considered legitimate for writable replicas.\nAs of version 7.0, these use cases are now all obsolete and the same can be achieved by other means.\nFor example:\n\n* Computing slow Set or Sorted set operations and storing the result in temporary local keys using commands like [`SUNIONSTORE`](https://redis.io/docs/latest/commands/sunionstore) and [`ZINTERSTORE`](https://redis.io/docs/latest/commands/zinterstore).\n  Instead, use commands that return the result without storing it, such as [`SUNION`](https://redis.io/docs/latest/commands/sunion) and [`ZINTER`](https://redis.io/docs/latest/commands/zinter).\n\n* Using the [`SORT`](https://redis.io/docs/latest/commands/sort) command (which is not considered a read-only command because of the optional STORE option and therefore cannot be used on a read-only replica).\n  Instead, use [`SORT_RO`](https://redis.io/docs/latest/commands/sort_ro), which is a read-only command.\n\n* Using [`EVAL`](https://redis.io/docs/latest/commands/eval) and [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) are also not considered read-only commands, because the Lua script may call write commands.\n  Instead, use [`EVAL_RO`](https://redis.io/docs/latest/commands/eval_ro) and [`EVALSHA_RO`](https://redis.io/docs/latest/commands/evalsha_ro) where the Lua script can only call read-only commands.\n\nWhile writes to a replica will be discarded if the replica and the master resync or if the replica is restarted, there is no guarantee that they will sync automatically.\n\nBefore version 4.0, writable replicas were incapable of expiring keys with a time to live set.\nThis means that if you use [`EXPIRE`](https://redis.io/docs/latest/commands/expire) or other commands that set a maximum TTL for a key, the key will leak, and while you may no longer see it while accessing it with read commands, you will see it in the count of keys and it will still use memory.\nRedis 4.0 RC3 and greater versions are able to evict keys with TTL as masters do, with the exceptions of keys written in DB numbers greater than 63 (but by default Redis instances only have 16 databases).\nNote though that even in versions greater than 4.0, using [`EXPIRE`](https://redis.io/docs/latest/commands/expire) on a key that could ever exists on the master can cause inconsistency between the replica and the master.\n\nAlso note that since Redis 4.0 replica writes are only local, and are not propagated to sub-replicas attached to the instance. Sub-replicas instead will always receive the replication stream identical to the one sent by the top-level master to the intermediate replicas. So for example in the following setup:\n\n    A ---> B ---> C\n\nEven if `B` is writable, C will not see `B` writes and will instead have identical dataset as the master instance `A`."},{"id":"setting-a-replica-to-authenticate-to-a-master","title":"Setting a replica to authenticate to a master","role":"configuration","text":"If your master has a password via `requirepass`, it's trivial to configure the\nreplica to use that password in all sync operations.\n\nTo do it on a running instance, use `redis-cli` and type:\n\n    config set masterauth <password>\n\nTo set it permanently, add this to your config file:\n\n    masterauth <password>"},{"id":"allow-writes-only-with-n-attached-replicas","title":"Allow writes only with N attached replicas","role":"content","text":"Starting with Redis 2.8, you can configure a Redis master to\naccept write queries only if at least N replicas are currently connected to the\nmaster.\n\nHowever, because Redis uses asynchronous replication it is not possible to ensure\nthe replica actually received a given write, so there is always a window for data\nloss.\n\nThis is how the feature works:\n\n* Redis replicas ping the master every second, acknowledging the amount of replication stream processed.\n* Redis masters will remember the last time it received a ping from every replica.\n* The user can configure a minimum number of replicas that have a lag not greater than a maximum number of seconds.\n\nIf there are at least N replicas, with a lag less than M seconds, then the write will be accepted.\n\nYou may think of it as a best effort data safety mechanism, where consistency is not ensured for a given write, but at least the time window for data loss is restricted to a given number of seconds. In general bound data loss is better than unbound one.\n\nIf the conditions are not met, the master will instead reply with an error and the write will not be accepted.\n\nThere are two configuration parameters for this feature:\n\n* min-replicas-to-write `<number of replicas>`\n* min-replicas-max-lag `<number of seconds>`\n\nFor more information, please check the example `redis.conf` file shipped with the\nRedis source distribution."},{"id":"how-redis-replication-deals-with-expires-on-keys","title":"How Redis replication deals with expires on keys","role":"content","text":"Redis expires allow keys to have a limited time to live (TTL). Such a feature depends\non the ability of an instance to count the time, however Redis replicas correctly\nreplicate keys with expires, even when such keys are altered using Lua\nscripts.\n\nTo implement such a feature Redis cannot rely on the ability of the master and\nreplica to have synced clocks, since this is a problem that cannot be solved\nand would result in race conditions and diverging data sets, so Redis\nuses three main techniques to make the replication of expired keys\nable to work:\n\n1. Replicas don't expire keys, instead they wait for masters to expire the keys. When a master expires a key (or evict it because of LRU), it synthesizes a [`DEL`](https://redis.io/docs/latest/commands/del) command which is transmitted to all the replicas.\n2. However because of master-driven expire, sometimes replicas may still have in memory keys that are already logically expired, since the master was not able to provide the [`DEL`](https://redis.io/docs/latest/commands/del) command in time. To deal with that the replica uses its logical clock to report that a key does not exist **only for read operations** that don't violate the consistency of the data set (as new commands from the master will arrive). In this way replicas avoid reporting logically expired keys that are still existing. In practical terms, an HTML fragments cache that uses replicas to scale will avoid returning items that are already older than the desired time to live.\n3. During Lua scripts executions no key expiries are performed. As a Lua script runs, conceptually the time in the master is frozen, so that a given key will either exist or not for all the time the script runs. This prevents keys expiring in the middle of a script, and is needed to send the same script to the replica in a way that is guaranteed to have the same effects in the data set.\n\nOnce a replica is promoted to a master it will start to expire keys independently, and will not require any help from its old master."},{"id":"configuring-replication-in-docker-and-nat","title":"Configuring replication in Docker and NAT","role":"configuration","text":"When Docker, or other types of containers using port forwarding, or Network Address Translation is used, Redis replication needs some extra care, especially when using Redis Sentinel or other systems where the master [`INFO`](https://redis.io/docs/latest/commands/info) or [`ROLE`](https://redis.io/docs/latest/commands/role) commands output is scanned to discover replicas' addresses.\n\nThe problem is that the [`ROLE`](https://redis.io/docs/latest/commands/role) command, and the replication section of\nthe [`INFO`](https://redis.io/docs/latest/commands/info) output, when issued into a master instance, will show replicas\nas having the IP address they use to connect to the master, which, in\nenvironments using NAT may be different compared to the logical address of the\nreplica instance (the one that clients should use to connect to replicas).\n\nSimilarly the replicas will be listed with the listening port configured\ninto `redis.conf`, that may be different from the forwarded port in case\nthe port is remapped.\n\nTo fix both issues, it is possible, since Redis 3.2.2, to force\na replica to announce an arbitrary pair of IP and port to the master.\nThe two configurations directives to use are:\n\n    replica-announce-ip 5.5.5.5\n    replica-announce-port 1234\n\nAnd are documented in the example `redis.conf` of recent Redis distributions."},{"id":"the-info-and-role-command","title":"The INFO and ROLE command","role":"content","text":"There are two Redis commands that provide a lot of information on the current\nreplication parameters of master and replica instances. One is [`INFO`](https://redis.io/docs/latest/commands/info). If the\ncommand is called with the `replication` argument as `INFO replication` only\ninformation relevant to the replication are displayed. Another more\ncomputer-friendly command is [`ROLE`](https://redis.io/docs/latest/commands/role), that provides the replication status of\nmasters and replicas together with their replication offsets, list of connected\nreplicas and so forth."},{"id":"partial-sync-after-restarts-and-failovers","title":"Partial sync after restarts and failovers","role":"content","text":"Since Redis 4.0, when an instance is promoted to master after a failover,\nit will still be able to perform a partial resynchronization with the replicas\nof the old master. To do so, the replica remembers the old replication ID and\noffset of its former master, so can provide part of the backlog to the connecting\nreplicas even if they ask for the old replication ID.\n\nHowever the new replication ID of the promoted replica will be different, since it\nconstitutes a different history of the data set. For example, the master can\nreturn available and can continue accepting writes for some time, so using the\nsame replication ID in the promoted replica would violate the rule that a\nreplication ID and offset pair identifies only a single data set.\n\nMoreover, replicas - when powered off gently and restarted - are able to store\nin the `RDB` file the information needed to resync with their\nmaster. This is useful in case of upgrades. When this is needed, it is better to\nuse the [`SHUTDOWN`](https://redis.io/docs/latest/commands/shutdown) command in order to perform a `save & quit` operation on the\nreplica.\n\nIt is not possible to partially sync a replica that restarted via the\nAOF file. However the instance may be turned to RDB persistence before shutting\ndown it, than can be restarted, and finally AOF can be enabled again."},{"id":"maxmemory-on-replicas","title":"`Maxmemory` on replicas","role":"content","text":"By default, a replica will ignore `maxmemory` (unless it is promoted to master after a failover or manually).\nIt means that the eviction of keys will be handled by the master, sending the DEL commands to the replica as keys evict in the master side.\n\nThis behavior ensures that masters and replicas stay consistent, which is usually what you want.\nHowever, if your replica is writable, or you want the replica to have a different memory setting, and you are sure all the writes performed to the replica are idempotent, then you may change this default (but be sure to understand what you are doing).\n\nNote that since the replica by default does not evict, it may end up using more memory than what is set via `maxmemory` (since there are certain buffers that may be larger on the replica, or data structures may sometimes take more memory and so forth).\nMake sure you monitor your replicas, and make sure they have enough memory to never hit a real out-of-memory condition before the master hits the configured `maxmemory` setting.\n\nTo change this behavior, you can allow a replica to not ignore the `maxmemory`. The configuration directives to use is:\n\n    replica-ignore-maxmemory no"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/management/scaling","title":"Scale with Redis Cluster","url":"https://redis.io/docs/latest/operate/oss_and_stack/management/scaling/","summary":"Horizontal scaling with Redis Cluster","aliases":["/management/scaling/","/manual/scaling/"],"tags":["docs","operate","stack","oss"],"last_updated":"2026-09-30T13:16:11-07:00","page_type":"content","content_hash":"5eaddaace366404b47e1c5f15817b49f7cc72637db7135389528518fb3c9c502","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis scales horizontally with a deployment topology called Redis Cluster. \nThis topic will teach you how to set up, test, and operate Redis Cluster in production.\nYou will learn about the availability and consistency characteristics of Redis Cluster from the end user's point of view.\n\nIf you plan to run a production Redis Cluster deployment or want to understand better how Redis Cluster works internally, consult the [Redis Cluster specification](https://redis.io/docs/latest/operate/oss_and_stack/reference/cluster-spec). To learn how Redis Enterprise handles scaling, see [Linear Scaling with Redis Enterprise](https://redis.com/redis-enterprise/technology/linear-scaling-redis-enterprise/)."},{"id":"redis-cluster-101","title":"Redis Cluster 101","role":"content","text":"Redis Cluster provides a way to run a Redis installation where data is automatically sharded across multiple Redis nodes. \nRedis Cluster also provides some degree of availability during partitions&mdash;in practical terms, the ability to continue operations when some nodes fail or are unable to communicate. \nHowever, the cluster will become unavailable in the event of larger failures (for example, when the majority of masters are unavailable).\n\nSo, with Redis Cluster, you get the ability to:\n\n* Automatically split your dataset among multiple nodes.\n* Continue operations when a subset of the nodes are experiencing failures or are unable to communicate with the rest of the cluster.\n\n#### Redis Cluster TCP ports\n\nEvery Redis Cluster node requires two open TCP connections: a Redis TCP port used to serve clients, e.g., 6379, and second port known as the _cluster bus port_. \nBy default, the cluster bus port is set by adding 10000 to the data port (e.g., 16379); however, you can override this in the `cluster-port` configuration.\n\nCluster bus is a node-to-node communication channel that uses a binary protocol, which is more suited to exchanging information between nodes due to\nlittle bandwidth and processing time. \nNodes use the cluster bus for failure detection, configuration updates, failover authorization, and so forth. \nClients should never try to communicate with the cluster bus port, but rather use the Redis command port. \nHowever, make sure you open both ports in your firewall, otherwise Redis cluster nodes won't be able to communicate.\n\nFor a Redis Cluster to work properly you need, for each node:\n\n1. The client communication port (usually 6379) used to communicate with clients and be open to all the clients that need to reach the cluster, plus all the other cluster nodes that use the client port for key migrations.\n2. The cluster bus port must be reachable from all the other cluster nodes.\n\nIf you don't open both TCP ports, your cluster will not work as expected.\n\n#### Redis Cluster and Docker\n\nCurrently, Redis Cluster does not support NATted environments and in general\nenvironments where IP addresses or TCP ports are remapped.\n\nDocker uses a technique called _port mapping_: programs running inside Docker containers may be exposed with a different port compared to the one the program believes to be using. \nThis is useful for running multiple containers using the same ports, at the same time, in the same server.\n\nTo make Docker compatible with Redis Cluster, you need to use Docker's _host networking mode_. \nPlease see the `--net=host` option in the [Docker documentation](https://docs.docker.com/engine/userguide/networking/dockernetworks/) for more information.\n\n#### Redis Cluster data sharding\n\nRedis Cluster does not use consistent hashing, but a different form of sharding\nwhere every key is conceptually part of what we call a **hash slot**.\n\nThere are 16384 hash slots in Redis Cluster, and to compute the hash\nslot for a given key, we simply take the CRC16 of the key modulo\n16384.\n\nEvery node in a Redis Cluster is responsible for a subset of the hash slots,\nso, for example, you may have a cluster with 3 nodes, where:\n\n* Node A contains hash slots from 0 to 5500.\n* Node B contains hash slots from 5501 to 11000.\n* Node C contains hash slots from 11001 to 16383.\n\nThis makes it easy to add and remove cluster nodes. For example, if\nI want to add a new node D, I need to move some hash slots from nodes A, B, C\nto D. Similarly, if I want to remove node A from the cluster, I can just\nmove the hash slots served by A to B and C. Once node A is empty,\nI can remove it from the cluster completely.\n\nMoving hash slots from a node to another does not require stopping\nany operations; therefore, adding and removing nodes, or changing the percentage of hash slots held by a node, requires no downtime.\n\nRedis Cluster supports multiple key operations as long as all of the keys involved in a single command execution (or whole transaction, or Lua script\nexecution) belong to the same hash slot. The user can force multiple keys\nto be part of the same hash slot by using a feature called *hash tags*.\n\nHash tags are documented in the Redis Cluster specification, but the gist is\nthat if there is a substring between {} brackets in a key, only what is\ninside the string is hashed. For example, the keys `user:{123}:profile` and `user:{123}:account` are guaranteed to be in the same hash slot because they share the same hash tag. As a result, you can operate on these two keys in the same multi-key operation.\n\n#### Redis Cluster master-replica model\n\nTo remain available when a subset of master nodes are failing or are\nnot able to communicate with the majority of nodes, Redis Cluster uses a\nmaster-replica model where every hash slot has from 1 (the master itself) to N\nreplicas (N-1 additional replica nodes).\n\nIn our example cluster with nodes A, B, C, if node B fails the cluster is not\nable to continue, since we no longer have a way to serve hash slots in the\nrange 5501-11000.\n\nHowever, when the cluster is created (or at a later time), we add a replica\nnode to every master, so that the final cluster is composed of A, B, C\nthat are master nodes, and A1, B1, C1 that are replica nodes.\nThis way, the system can continue if node B fails.\n\nNode B1 replicates B, and B fails, the cluster will promote node B1 as the new\nmaster and will continue to operate correctly.\n\nHowever, note that if nodes B and B1 fail at the same time, Redis Cluster will not be able to continue to operate.\n\n#### Redis Cluster consistency guarantees\n\nRedis Cluster does not guarantee **strong consistency**. In practical\nterms this means that under certain conditions it is possible that Redis\nCluster will lose writes that were acknowledged by the system to the client.\n\nThe first reason why Redis Cluster can lose writes is because it uses\nasynchronous replication. This means that during writes the following\nhappens:\n\n* Your client writes to the master B.\n* The master B replies OK to your client.\n* The master B propagates the write to its replicas B1, B2 and B3.\n\nAs you can see, B does not wait for an acknowledgement from B1, B2, B3 before\nreplying to the client, since this would be a prohibitive latency penalty\nfor Redis, so if your client writes something, B acknowledges the write,\nbut crashes before being able to send the write to its replicas, one of the\nreplicas (that did not receive the write) can be promoted to master, losing\nthe write forever.\n\nThis is very similar to what happens with most databases that are\nconfigured to flush data to disk every second, so it is a scenario you\nare already able to reason about because of past experiences with traditional\ndatabase systems not involving distributed systems. Similarly you can\nimprove consistency by forcing the database to flush data to disk before\nreplying to the client, but this usually results in prohibitively low\nperformance. That would be the equivalent of synchronous replication in\nthe case of Redis Cluster.\n\nBasically, there is a trade-off to be made between performance and consistency.\n\nRedis Cluster has support for synchronous writes when absolutely needed,\nimplemented via the [`WAIT`](https://redis.io/docs/latest/commands/wait) command. This makes losing writes a lot less\nlikely. However, note that Redis Cluster does not implement strong consistency\neven when synchronous replication is used: it is always possible, under more\ncomplex failure scenarios, that a replica that was not able to receive the write\nwill be elected as master.\n\nThere is another notable scenario where Redis Cluster will lose writes, that\nhappens during a network partition where a client is isolated with a minority\nof instances including at least a master.\n\nTake as an example our 6 nodes cluster composed of A, B, C, A1, B1, C1,\nwith 3 masters and 3 replicas. There is also a client, that we will call Z1.\n\nAfter a partition occurs, it is possible that in one side of the\npartition we have A, C, A1, B1, C1, and in the other side we have B and Z1.\n\nZ1 is still able to write to B, which will accept its writes. If the\npartition heals in a very short time, the cluster will continue normally.\nHowever, if the partition lasts enough time for B1 to be promoted to master\non the majority side of the partition, the writes that Z1 has sent to B\nin the meantime will be lost.\n\n> [!NOTE]\n> There is a **maximum window** to the amount of writes Z1 will be able\n> to send to B: if enough time has elapsed for the majority side of the\n> partition to elect a replica as master, every master node in the minority\n> side will have stopped accepting writes.\n\nThis amount of time is a very important configuration directive of Redis\nCluster, and is called the **node timeout**.\n\nAfter node timeout has elapsed, a master node is considered to be failing,\nand can be replaced by one of its replicas.\nSimilarly, after node timeout has elapsed without a master node to be able\nto sense the majority of the other master nodes, it enters an error state\nand stops accepting writes."},{"id":"redis-cluster-configuration-parameters","title":"Redis Cluster configuration parameters","role":"content","text":"We are about to create an example cluster deployment. \nBefore we continue, let's introduce the configuration parameters that Redis Cluster introduces\nin the `redis.conf` file.\n\n* **cluster-enabled `<yes/no>`**: If yes, enables Redis Cluster support in a specific Redis instance. Otherwise the instance starts as a standalone instance as usual.\n* **cluster-config-file `<filename>`**: Note that despite the name of this option, this is not a user editable configuration file, but the file where a Redis Cluster node automatically persists the cluster configuration (the state, basically) every time there is a change, in order to be able to re-read it at startup. The file lists things like the other nodes in the cluster, their state, persistent variables, and so forth. Often this file is rewritten and flushed on disk as a result of some message reception.\n* **cluster-node-timeout `<milliseconds>`**: The maximum amount of time a Redis Cluster node can be unavailable, without it being considered as failing. If a master node is not reachable for more than the specified amount of time, it will be failed over by its replicas. This parameter controls other important things in Redis Cluster. Notably, every node that can't reach the majority of master nodes for the specified amount of time, will stop accepting queries.\n* **cluster-slave-validity-factor `<factor>`**: If set to zero, a replica will always consider itself valid, and will therefore always try to failover a master, regardless of the amount of time the link between the master and the replica remained disconnected. If the value is positive, a maximum disconnection time is calculated as the *node timeout* value multiplied by the factor provided with this option, and if the node is a replica, it will not try to start a failover if the master link was disconnected for more than the specified amount of time. For example, if the node timeout is set to 5 seconds and the validity factor is set to 10, a replica disconnected from the master for more than 50 seconds will not try to failover its master. Note that any value different than zero may result in Redis Cluster being unavailable after a master failure if there is no replica that is able to failover it. In that case the cluster will return to being available only when the original master rejoins the cluster.\n* **cluster-migration-barrier `<count>`**: Minimum number of replicas a master will remain connected with, for another replica to migrate to a master which is no longer covered by any replica. See the appropriate section about replica migration in this tutorial for more information.\n* **cluster-require-full-coverage `<yes/no>`**: If this is set to yes, as it is by default, the cluster stops accepting writes if some percentage of the key space is not covered by any node. If the option is set to no, the cluster will still serve queries even if only requests about a subset of keys can be processed.\n* **cluster-allow-reads-when-down `<yes/no>`**: If this is set to no, as it is by default, a node in a Redis Cluster will stop serving all traffic when the cluster is marked as failed, either when a node can't reach a quorum of masters or when full coverage is not met. This prevents reading potentially inconsistent data from a node that is unaware of changes in the cluster. This option can be set to yes to allow reads from a node during the fail state, which is useful for applications that want to prioritize read availability but still want to prevent inconsistent writes. It can also be used for when using Redis Cluster with only one or two shards, as it allows the nodes to continue serving writes when a master fails but automatic failover is impossible."},{"id":"create-and-use-a-redis-cluster","title":"Create and use a Redis Cluster","role":"content","text":"To create and use a Redis Cluster, follow these steps:\n\n* [Create a Redis Cluster](#create-a-redis-cluster)\n* [Interact with the cluster](#interact-with-the-cluster)\n* [Write an example app with redis-rb-cluster](#write-an-example-app-with-redis-rb-cluster)\n* [Reshard the cluster](#reshard-the-cluster)\n* [A more interesting example application](#a-more-interesting-example-application)\n* [Test the failover](#test-the-failover)\n* [Manual failover](#manual-failover)\n* [Add a new node](#add-a-new-node)\n* [Remove a node](#remove-a-node)\n* [Replica migration](#replica-migration)\n* [Upgrade nodes in a Redis Cluster](#upgrade-nodes-in-a-redis-cluster)\n* [Migrate to Redis Cluster](#migrate-to-redis-cluster)\n\nBut, first, familiarize yourself with the requirements for creating a cluster.\n\n#### Requirements to create a Redis Cluster\n\nTo create a cluster, the first thing you need is to have a few empty Redis instances running in _cluster mode_. \n\nAt minimum, set the following directives in the `redis.conf` file:\n\n[code example]\n\nTo enable cluster mode, set the `cluster-enabled` directive to `yes`.\nEvery instance also contains the path of a file where the\nconfiguration for this node is stored, which by default is `nodes.conf`.\nThis file is never touched by humans; it is simply generated at startup\nby the Redis Cluster instances, and updated every time it is needed.\n\nNote that the **minimal cluster** that works as expected must contain\nat least three master nodes. For deployment, we strongly recommend\na six-node cluster, with three masters and three replicas.\n\nYou can test this locally by creating the following directories named\nafter the port number of the instance you'll run inside any given directory.\n\nFor example:\n\n[code example]\n\nCreate a `redis.conf` file inside each of the directories, from 7000 to 7005.\nAs a template for your configuration file just use the small example above,\nbut make sure to replace the port number `7000` with the right port number\naccording to the directory name.\n\n\nYou can start each instance as follows, each running in a separate terminal tab:\n\n[code example]\nYou'll see from the logs that every node assigns itself a new ID:\n\n    [82462] 26 Nov 11:56:55.329 * No cluster configuration found, I'm 97a3a64667477371c4479320d683e4c8db5858b1\n\nThis ID will be used forever by this specific instance in order for the instance\nto have a unique name in the context of the cluster. Every node\nremembers every other node using this IDs, and not by IP or port.\nIP addresses and ports may change, but the unique node identifier will never\nchange for all the life of the node. We call this identifier simply **Node ID**.\n\n#### Create a Redis Cluster\n\nNow that we have a number of instances running, you need to create your cluster by writing some meaningful configuration to the nodes.\n\nYou can configure and execute individual instances manually or use the create-cluster script.\nLet's go over how you do it manually.\n\nTo create the cluster, run:\n\n    redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 \\\n    127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 \\\n    --cluster-replicas 1\n\nThe command used here is **create**, since we want to create a new cluster.\nThe option `--cluster-replicas 1` means that we want a replica for every master created.\n\nThe other arguments are the list of addresses of the instances I want to use\nto create the new cluster.\n\n`redis-cli` will propose a configuration. Accept the proposed configuration by typing **yes**.\nThe cluster will be configured and *joined*, which means that instances will be\nbootstrapped into talking with each other. Finally, if everything has gone well, you'll see a message like this:\n\n    [OK] All 16384 slots covered\n\nThis means that there is at least one master instance serving each of the\n16384 available slots.\n\nIf you don't want to create a Redis Cluster by configuring and executing\nindividual instances manually as explained above, there is a much simpler\nsystem (but you'll not learn the same amount of operational details).\n\nFind the `utils/create-cluster` directory in the Redis distribution.\nThere is a script called `create-cluster` inside (same name as the directory\nit is contained into), it's a simple bash script. In order to start\na 6 nodes cluster with 3 masters and 3 replicas just type the following\ncommands:\n\n1. `create-cluster start`\n2. `create-cluster create`\n\nReply to `yes` in step 2 when the `redis-cli` utility wants you to accept\nthe cluster layout.\n\nYou can now interact with the cluster, the first node will start at port 30001\nby default. When you are done, stop the cluster with:\n\n3. `create-cluster stop`\n\nPlease read the `README` inside this directory for more information on how\nto run the script.\n\n#### Interact with the cluster\n\nTo connect to Redis Cluster, you'll need a cluster-aware Redis client. \nSee the [documentation](https://redis.io/docs/latest/develop/clients) for your client of choice to determine its cluster support.\n\nYou can also test your Redis Cluster using the `redis-cli` command line utility:\n\n[code example]\n\n> [!NOTE]\n>  \n> If you created the cluster using the script, your nodes may listen\n> on different ports, starting from 30001 by default.\n\nThe `redis-cli` cluster support is very basic, so it always uses the fact that\nRedis Cluster nodes are able to redirect a client to the right node.\nA serious client is able to do better than that, and cache the map between\nhash slots and nodes addresses, to directly use the right connection to the\nright node. The map is refreshed only when something changed in the cluster\nconfiguration, for example after a failover or after the system administrator\nchanged the cluster layout by adding or removing nodes.\n\n#### Write an example app with redis-rb-cluster\n\nBefore going forward showing how to operate the Redis Cluster, doing things\nlike a failover, or a resharding, we need to create some example application\nor at least to be able to understand the semantics of a simple Redis Cluster\nclient interaction.\n\nIn this way we can run an example and at the same time try to make nodes\nfailing, or start a resharding, to see how Redis Cluster behaves under real\nworld conditions. It is not very helpful to see what happens while nobody\nis writing to the cluster.\n\nThis section explains some basic usage of\n[redis-rb-cluster](https://github.com/antirez/redis-rb-cluster) showing two\nexamples. \nThe first is the following, and is the\n[`example.rb`](https://github.com/antirez/redis-rb-cluster/blob/master/example.rb)\nfile inside the redis-rb-cluster distribution:\n\n[code example]\n\nThe application does a very simple thing, it sets keys in the form `foo<number>` to `number`, one after the other. So if you run the program the result is the\nfollowing stream of commands:\n\n* SET foo0 0\n* SET foo1 1\n* SET foo2 2\n* And so forth...\n\nThe program looks more complex than it should usually as it is designed to\nshow errors on the screen instead of exiting with an exception, so every\noperation performed with the cluster is wrapped by `begin` `rescue` blocks.\n\nThe **line 14** is the first interesting line in the program. It creates the\nRedis Cluster object, using as argument a list of *startup nodes*, the maximum\nnumber of connections this object is allowed to take against different nodes,\nand finally the timeout after a given operation is considered to be failed.\n\nThe startup nodes don't need to be all the nodes of the cluster. The important\nthing is that at least one node is reachable. Also note that redis-rb-cluster\nupdates this list of startup nodes as soon as it is able to connect with the\nfirst node. You should expect such a behavior with any other serious client.\n\nNow that we have the Redis Cluster object instance stored in the **rc** variable,\nwe are ready to use the object like if it was a normal Redis object instance.\n\nThis is exactly what happens in **line 18 to 26**: when we restart the example\nwe don't want to start again with `foo0`, so we store the counter inside\nRedis itself. The code above is designed to read this counter, or if the\ncounter does not exist, to assign it the value of zero.\n\nHowever note how it is a while loop, as we want to try again and again even\nif the cluster is down and is returning errors. Normal applications don't need\nto be so careful.\n\n**Lines between 28 and 37** start the main loop where the keys are set or\nan error is displayed.\n\nNote the `sleep` call at the end of the loop. In your tests you can remove\nthe sleep if you want to write to the cluster as fast as possible (relatively\nto the fact that this is a busy loop without real parallelism of course, so\nyou'll get the usually 10k ops/second in the best of the conditions).\n\nNormally writes are slowed down in order for the example application to be\neasier to follow by humans.\n\nStarting the application produces the following output:\n\n[code example]\n\nThis is not a very interesting program and we'll use a better one in a moment\nbut we can already see what happens during a resharding when the program\nis running.\n\n#### Reshard the cluster\n\nNow we are ready to try a cluster resharding. To do this, please\nkeep the example.rb program running, so that you can see if there is some\nimpact on the program running. Also, you may want to comment the `sleep`\ncall to have some more serious write load during resharding.\n\nResharding basically means to move hash slots from a set of nodes to another\nset of nodes. \nLike cluster creation, it is accomplished using the redis-cli utility.\n\nTo start a resharding, just type:\n\n    redis-cli --cluster reshard 127.0.0.1:7000\n\nYou only need to specify a single node, redis-cli will find the other nodes\nautomatically.\n\nCurrently redis-cli is only able to reshard with the administrator support,\nyou can't just say move 5% of slots from this node to the other one (but\nthis is pretty trivial to implement). So it starts with questions. The first\nis how much of a resharding do you want to do:\n\n    How many slots do you want to move (from 1 to 16384)?\n\nWe can try to reshard 1000 hash slots, that should already contain a non\ntrivial amount of keys if the example is still running without the sleep\ncall.\n\nThen redis-cli needs to know what is the target of the resharding, that is,\nthe node that will receive the hash slots.\nI'll use the first master node, that is, 127.0.0.1:7000, but I need\nto specify the Node ID of the instance. This was already printed in a\nlist by redis-cli, but I can always find the ID of a node with the following\ncommand if I need:\n\n[code example]\n\nOk so my target node is 97a3a64667477371c4479320d683e4c8db5858b1.\n\nNow you'll get asked from what nodes you want to take those keys.\nI'll just type `all` in order to take a bit of hash slots from all the\nother master nodes.\n\nAfter the final confirmation you'll see a message for every slot that\nredis-cli is going to move from a node to another, and a dot will be printed\nfor every actual key moved from one side to the other.\n\nWhile the resharding is in progress you should be able to see your\nexample program running unaffected. You can stop and restart it multiple times\nduring the resharding if you want.\n\nAt the end of the resharding, you can test the health of the cluster with\nthe following command:\n\n    redis-cli --cluster check 127.0.0.1:7000\n\nAll the slots will be covered as usual, but this time the master at\n127.0.0.1:7000 will have more hash slots, something around 6461.\n\nResharding can be performed automatically without the need to manually\nenter the parameters in an interactive way. This is possible using a command\nline like the following:\n\n    redis-cli --cluster reshard <host>:<port> --cluster-from <node-id> --cluster-to <node-id> --cluster-slots <number of slots> --cluster-yes\n\nThis allows to build some automatism if you are likely to reshard often,\nhowever currently there is no way for `redis-cli` to automatically\nrebalance the cluster checking the distribution of keys across the cluster\nnodes and intelligently moving slots as needed. This feature will be added\nin the future.\n\nThe `--cluster-yes` option instructs the cluster manager to automatically answer\n\"yes\" to the command's prompts, allowing it to run in a non-interactive mode.\nNote that this option can also be activated by setting the\n`REDISCLI_CLUSTER_YES` environment variable.\n\n#### A more interesting example application\n\nThe example application we wrote early is not very good.\nIt writes to the cluster in a simple way without even checking if what was\nwritten is the right thing.\n\nFrom our point of view the cluster receiving the writes could just always\nwrite the key `foo` to `42` to every operation, and we would not notice at\nall.\n\nSo in the `redis-rb-cluster` repository, there is a more interesting application\nthat is called `consistency-test.rb`. It uses a set of counters, by default 1000, and sends [`INCR`](https://redis.io/docs/latest/commands/incr) commands in order to increment the counters.\n\nHowever instead of just writing, the application does two additional things:\n\n* When a counter is updated using [`INCR`](https://redis.io/docs/latest/commands/incr), the application remembers the write.\n* It also reads a random counter before every write, and check if the value is what we expected it to be, comparing it with the value it has in memory.\n\nWhat this means is that this application is a simple **consistency checker**,\nand is able to tell you if the cluster lost some write, or if it accepted\na write that we did not receive acknowledgment for. In the first case we'll\nsee a counter having a value that is smaller than the one we remember, while\nin the second case the value will be greater.\n\nRunning the consistency-test application produces a line of output every\nsecond:\n\n[code example]\n\nThe line shows the number of **R**eads and **W**rites performed, and the\nnumber of errors (query not accepted because of errors since the system was\nnot available).\n\nIf some inconsistency is found, new lines are added to the output.\nThis is what happens, for example, if I reset a counter manually while\nthe program is running:\n\n[code example]\n\nWhen I set the counter to 0 the real value was 114, so the program reports\n114 lost writes ([`INCR`](https://redis.io/docs/latest/commands/incr) commands that are not remembered by the cluster).\n\nThis program is much more interesting as a test case, so we'll use it\nto test the Redis Cluster failover.\n\n#### Test the failover\n\nTo trigger the failover, the simplest thing we can do (that is also\nthe semantically simplest failure that can occur in a distributed system)\nis to crash a single process, in our case a single master.\n\n> [!NOTE]\n>  \n> During this test, you should take a tab open with the consistency test\n> application running. \n\nWe can identify a master and crash it with the following command:\n\n[code example]\n\nOk, so 7000, 7001, and 7002 are masters. Let's crash node 7002 with the\n**DEBUG SEGFAULT** command:\n\n[code example]\n\nNow we can look at the output of the consistency test to see what it reported.\n\n[code example]\n\nAs you can see during the failover the system was not able to accept 578 reads and 577 writes, however no inconsistency was created in the database. This may\nsound unexpected as in the first part of this tutorial we stated that Redis\nCluster can lose writes during the failover because it uses asynchronous\nreplication. What we did not say is that this is not very likely to happen\nbecause Redis sends the reply to the client, and the commands to replicate\nto the replicas, about at the same time, so there is a very small window to\nlose data. However the fact that it is hard to trigger does not mean that it\nis impossible, so this does not change the consistency guarantees provided\nby Redis cluster.\n\nWe can now check what is the cluster setup after the failover (note that\nin the meantime I restarted the crashed instance so that it rejoins the\ncluster as a replica):\n\n[code example]\n\nNow the masters are running on ports 7000, 7001 and 7005. What was previously\na master, that is the Redis instance running on port 7002, is now a replica of\n7005.\n\nThe output of the [`CLUSTER NODES`](https://redis.io/docs/latest/commands/cluster-nodes) command may look intimidating, but it is actually pretty simple, and is composed of the following tokens:\n\n* Node ID\n* ip:port\n* flags: master, replica, myself, fail, ...\n* if it is a replica, the Node ID of the master\n* Time of the last pending PING still waiting for a reply.\n* Time of the last PONG received.\n* Configuration epoch for this node (see the Cluster specification).\n* Status of the link to this node.\n* Slots served...\n\n#### Manual failover\n\nSometimes it is useful to force a failover without actually causing any problem\non a master. For example, to upgrade the Redis process of one of the\nmaster nodes it is a good idea to failover it to turn it into a replica\nwith minimal impact on availability.\n\nManual failovers are supported by Redis Cluster using the [`CLUSTER FAILOVER`](https://redis.io/docs/latest/commands/cluster-failover)\ncommand, that must be executed in one of the replicas of the master you want\nto failover.\n\nManual failovers are special and are safer compared to failovers resulting from\nactual master failures. They occur in a way that avoids data loss in the\nprocess, by switching clients from the original master to the new master only\nwhen the system is sure that the new master processed all the replication stream\nfrom the old one.\n\nThis is what you see in the replica log when you perform a manual failover:\n\n    # Manual failover user request accepted.\n    # Received replication offset for paused master manual failover: 347540\n    # All master replication stream processed, manual failover can start.\n    # Start of election delayed for 0 milliseconds (rank #0, offset 347540).\n    # Starting a failover election for epoch 7545.\n    # Failover election won: I'm the new master.\n\nBasically clients connected to the master we are failing over are stopped.\nAt the same time the master sends its replication offset to the replica, that\nwaits to reach the offset on its side. When the replication offset is reached,\nthe failover starts, and the old master is informed about the configuration\nswitch. When the clients are unblocked on the old master, they are redirected\nto the new master.\n\n> [!NOTE]\n>  \n> To promote a replica to master, it must first be known as a replica by a majority of the masters in the cluster.\n>   Otherwise, it cannot win the failover election.\n>   If the replica has just been added to the cluster (see [Add a new node as a replica](#add-a-new-node-as-a-replica)), you may need to wait a while before sending the [`CLUSTER FAILOVER`](https://redis.io/docs/latest/commands/cluster-failover) command, to make sure the masters in cluster are aware of the new replica. \n\n#### Add a new node\n\nAdding a new node is basically the process of adding an empty node and then\nmoving some data into it, in case it is a new master, or telling it to\nsetup as a replica of a known node, in case it is a replica.\n\nWe'll show both, starting with the addition of a new master instance.\n\nIn both cases the first step to perform is **adding an empty node**.\n\nThis is as simple as to start a new node in port 7006 (we already used\nfrom 7000 to 7005 for our existing 6 nodes) with the same configuration\nused for the other nodes, except for the port number, so what you should\ndo in order to conform with the setup we used for the previous nodes:\n\n* Create a new tab in your terminal application.\n* Enter the `cluster-test` directory.\n* Create a directory named `7006`.\n* Create a redis.conf file inside, similar to the one used for the other nodes but using 7006 as port number.\n* Finally start the server with `../redis-server ./redis.conf`\n\nAt this point the server should be running.\n\nNow we can use **redis-cli** as usual in order to add the node to\nthe existing cluster.\n\n    redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000\n\nAs you can see I used the **add-node** command specifying the address of the\nnew node as first argument, and the address of a random existing node in the\ncluster as second argument.\n\nIn practical terms redis-cli here did very little to help us, it just\nsent a [`CLUSTER MEET`](https://redis.io/docs/latest/commands/cluster-meet) message to the node, something that is also possible\nto accomplish manually. However redis-cli also checks the state of the\ncluster before to operate, so it is a good idea to perform cluster operations\nalways via redis-cli even when you know how the internals work.\n\nNow we can connect to the new node to see if it really joined the cluster:\n\n[code example]\n\nNote that since this node is already connected to the cluster it is already\nable to redirect client queries correctly and is generally speaking part of\nthe cluster. However it has two peculiarities compared to the other masters:\n\n* It holds no data as it has no assigned hash slots.\n* Because it is a master without assigned slots, it does not participate in the election process when a replica wants to become a master.\n\nNow it is possible to assign hash slots to this node using the resharding\nfeature of `redis-cli`. \nIt is basically useless to show this as we already\ndid in a previous section, there is no difference, it is just a resharding\nhaving as a target the empty node.\n\n##### Add a new node as a replica\n\nAdding a new replica can be performed in two ways. The obvious one is to\nuse redis-cli again, but with the --cluster-slave option, like this:\n\n    redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000 --cluster-slave\n\nNote that the command line here is exactly like the one we used to add\na new master, so we are not specifying to which master we want to add\nthe replica. In this case, what happens is that redis-cli will add the new\nnode as replica of a random master among the masters with fewer replicas.\n\nHowever you can specify exactly what master you want to target with your\nnew replica with the following command line:\n\n    redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000 --cluster-slave --cluster-master-id 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e\n\nThis way we assign the new replica to a specific master.\n\nA more manual way to add a replica to a specific master is to add the new\nnode as an empty master, and then turn it into a replica using the\n[`CLUSTER REPLICATE`](https://redis.io/docs/latest/commands/cluster-replicate) command. This also works if the node was added as a replica\nbut you want to move it as a replica of a different master.\n\nFor example in order to add a replica for the node 127.0.0.1:7005 that is\ncurrently serving hash slots in the range 11423-16383, that has a Node ID\n3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e, all I need to do is to connect\nwith the new node (already added as empty master) and send the command:\n\n    redis 127.0.0.1:7006> cluster replicate 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e\n\nThat's it. Now we have a new replica for this set of hash slots, and all\nthe other nodes in the cluster already know (after a few seconds needed to\nupdate their config). We can verify with the following command:\n\n[code example]\n\nThe node 3c3a0c... now has two replicas, running on ports 7002 (the existing one) and 7006 (the new one).\n\n#### Remove a node\n\nTo remove a replica node just use the `del-node` command of redis-cli:\n\n    redis-cli --cluster del-node 127.0.0.1:7000 `<node-id>`\n\nThe first argument is just a random node in the cluster, the second argument\nis the ID of the node you want to remove.\n\nYou can remove a master node in the same way as well, **however in order to\nremove a master node it must be empty**. If the master is not empty you need\nto reshard data away from it to all the other master nodes before.\n\nAn alternative to remove a master node is to perform a manual failover of it\nover one of its replicas and remove the node after it turned into a replica of the\nnew master. Obviously this does not help when you want to reduce the actual\nnumber of masters in your cluster, in that case, a resharding is needed.\n\nThere is a special scenario where you want to remove a failed node.\nYou should not use the `del-node` command because it tries to connect to all nodes and you will encounter a \"connection refused\" error.\nInstead, you can use the `call` command:\n\n    redis-cli --cluster call 127.0.0.1:7000 cluster forget `<node-id>`\n\nThis command will execute [`CLUSTER FORGET`](https://redis.io/docs/latest/commands/cluster-forget) command on every node. \n\n#### Replica migration\n\nIn Redis Cluster, you can reconfigure a replica to replicate with a\ndifferent master at any time just using this command:\n\n    CLUSTER REPLICATE <master-node-id>\n\nHowever there is a special scenario where you want replicas to move from one\nmaster to another one automatically, without the help of the system administrator.\nThe automatic reconfiguration of replicas is called *replicas migration* and is\nable to improve the reliability of a Redis Cluster.\n\n> [!NOTE]\n>  \n> You can read the details of replicas migration in the [Redis Cluster Specification](https://redis.io/docs/latest/operate/oss_and_stack/reference/cluster-spec), here we'll only provide some information about the\n> general idea and what you should do in order to benefit from it. \n\nThe reason why you may want to let your cluster replicas to move from one master\nto another under certain condition, is that usually the Redis Cluster is as\nresistant to failures as the number of replicas attached to a given master.\n\nFor example a cluster where every master has a single replica can't continue\noperations if the master and its replica fail at the same time, simply because\nthere is no other instance to have a copy of the hash slots the master was\nserving. However while net-splits are likely to isolate a number of nodes\nat the same time, many other kind of failures, like hardware or software failures\nlocal to a single node, are a very notable class of failures that are unlikely\nto happen at the same time, so it is possible that in your cluster where\nevery master has a replica, the replica is killed at 4am, and the master is killed\nat 6am. This still will result in a cluster that can no longer operate.\n\nTo improve reliability of the system we have the option to add additional\nreplicas to every master, but this is expensive. Replica migration allows to\nadd more replicas to just a few masters. So you have 10 masters with 1 replica\neach, for a total of 20 instances. However you add, for example, 3 instances\nmore as replicas of some of your masters, so certain masters will have more\nthan a single replica.\n\nWith replicas migration what happens is that if a master is left without\nreplicas, a replica from a master that has multiple replicas will migrate to\nthe *orphaned* master. So after your replica goes down at 4am as in the example\nwe made above, another replica will take its place, and when the master\nwill fail as well at 5am, there is still a replica that can be elected so that\nthe cluster can continue to operate.\n\nSo what you should know about replicas migration in short?\n\n* The cluster will try to migrate a replica from the master that has the greatest number of replicas in a given moment.\n* To benefit from replica migration you have just to add a few more replicas to a single master in your cluster, it does not matter what master.\n* There is a configuration parameter that controls the replica migration feature that is called `cluster-migration-barrier`: you can read more about it in the example `redis.conf` file provided with Redis Cluster.\n\n#### Upgrade nodes in a Redis Cluster\n\nUpgrading replica nodes is easy since you just need to stop the node and restart\nit with an updated version of Redis. If there are clients scaling reads using\nreplica nodes, they should be able to reconnect to a different replica if a given\none is not available.\n\nUpgrading masters is a bit more complex, and the suggested procedure is:\n\n1. Use [`CLUSTER FAILOVER`](https://redis.io/docs/latest/commands/cluster-failover) to trigger a manual failover of the master to one of its replicas.\n   (See the [Manual failover](#manual-failover) in this topic.)\n2. Wait for the master to turn into a replica.\n3. Finally upgrade the node as you do for replicas.\n4. If you want the master to be the node you just upgraded, trigger a new manual failover in order to turn back the upgraded node into a master.\n\nFollowing this procedure you should upgrade one node after the other until\nall the nodes are upgraded.\n\n#### Migrate to Redis Cluster\n\nUsers willing to migrate to Redis Cluster may have just a single master, or\nmay already using a preexisting sharding setup, where keys\nare split among N nodes, using some in-house algorithm or a sharding algorithm\nimplemented by their client library or Redis proxy.\n\nIn both cases it is possible to migrate to Redis Cluster easily, however\nwhat is the most important detail is if multiple-keys operations are used\nby the application, and how. There are three different cases:\n\n1. Multiple keys operations, or transactions, or Lua scripts involving multiple keys, are not used. Keys are accessed independently (even if accessed via transactions or Lua scripts grouping multiple commands, about the same key, together).\n2. Multiple keys operations, or transactions, or Lua scripts involving multiple keys are used but only with keys having the same **hash tag**, which means that the keys used together all have a `{...}` sub-string that happens to be identical. For example the following multiple keys operation is defined in the context of the same hash tag: `SUNION {user:1000}.foo {user:1000}.bar`.\n3. Multiple keys operations, or transactions, or Lua scripts involving multiple keys are used with key names not having an explicit, or the same, hash tag.\n\nThe third case is not handled by Redis Cluster: the application requires to\nbe modified in order to not use multi keys operations or only use them in\nthe context of the same hash tag.\n\nCase 1 and 2 are covered, so we'll focus on those two cases, that are handled\nin the same way, so no distinction will be made in the documentation.\n\nAssuming you have your preexisting data set split into N masters, where\nN=1 if you have no preexisting sharding, the following steps are needed\nin order to migrate your data set to Redis Cluster:\n\n1. Stop your clients. No automatic live-migration to Redis Cluster is currently possible. You may be able to do it orchestrating a live migration in the context of your application / environment.\n2. Generate an append only file for all of your N masters using the [`BGREWRITEAOF`](https://redis.io/docs/latest/commands/bgrewriteaof) command, and waiting for the AOF file to be completely generated.\n3. Save your AOF files from aof-1 to aof-N somewhere. At this point you can stop your old instances if you wish (this is useful since in non-virtualized deployments you often need to reuse the same computers).\n4. Create a Redis Cluster composed of N masters and zero replicas. You'll add replicas later. Make sure all your nodes are using the append only file for persistence.\n5. Stop all the cluster nodes, substitute their append only file with your pre-existing append only files, aof-1 for the first node, aof-2 for the second node, up to aof-N.\n6. Restart your Redis Cluster nodes with the new AOF files. They'll complain that there are keys that should not be there according to their configuration.\n7. Use `redis-cli --cluster fix` command in order to fix the cluster so that keys will be migrated according to the hash slots each node is authoritative or not.\n8. Use `redis-cli --cluster check` at the end to make sure your cluster is ok.\n9. Restart your clients modified to use a Redis Cluster aware client library.\n\nThere is an alternative way to import data from external instances to a Redis\nCluster, which is to use the `redis-cli --cluster import` command.\n\nThe command moves all the keys of a running instance (deleting the keys from\nthe source instance) to the specified pre-existing Redis Cluster. However\nnote that if you use a Redis 2.8 instance as source instance the operation\nmay be slow since 2.8 does not implement migrate connection caching, so you\nmay want to restart your source instance with a Redis 3.x version before\nto perform such operation.\n\n> [!NOTE]\n>  \n> Starting with Redis 5, if not for backward compatibility, the Redis project no longer uses the word slave. Unfortunately in this command the word slave is part of the protocol, so we'll be able to remove such occurrences only when this API will be naturally deprecated."},{"id":"learn-more","title":"Learn more","role":"related","text":"* [Redis Cluster specification](https://redis.io/docs/latest/operate/oss_and_stack/reference/cluster-spec)\n* [Linear Scaling with Redis Enterprise](https://redis.com/redis-enterprise/technology/linear-scaling-redis-enterprise/)\n* [Docker documentation](https://docs.docker.com/engine/userguide/networking/dockernetworks/)"}],"examples":[{"id":"create-and-use-a-redis-cluster-ex0","language":"plaintext","code":"port 7000\ncluster-enabled yes\ncluster-config-file nodes.conf\ncluster-node-timeout 5000\nappendonly yes","section_id":"create-and-use-a-redis-cluster"},{"id":"create-and-use-a-redis-cluster-ex1","language":"plaintext","code":"mkdir cluster-test\ncd cluster-test\nmkdir 7000 7001 7002 7003 7004 7005","section_id":"create-and-use-a-redis-cluster"},{"id":"create-and-use-a-redis-cluster-ex2","language":"plaintext","code":"cd 7000\nredis-server ./redis.conf","section_id":"create-and-use-a-redis-cluster"},{"id":"create-and-use-a-redis-cluster-ex3","language":"plaintext","code":"$ redis-cli -c -p 7000\nredis 127.0.0.1:7000> set foo bar\n-> Redirected to slot [12182] located at 127.0.0.1:7002\nOK\nredis 127.0.0.1:7002> set hello world\n-> Redirected to slot [866] located at 127.0.0.1:7000\nOK\nredis 127.0.0.1:7000> get foo\n-> Redirected to slot [12182] located at 127.0.0.1:7002\n\"bar\"\nredis 127.0.0.1:7002> get hello\n-> Redirected to slot [866] located at 127.0.0.1:7000\n\"world\"","section_id":"create-and-use-a-redis-cluster"},{"id":"create-and-use-a-redis-cluster-ex4","language":"plaintext","code":"1  require './cluster'\n   2\n   3  if ARGV.length != 2\n   4      startup_nodes = [\n   5          {:host => \"127.0.0.1\", :port => 7000},\n   6          {:host => \"127.0.0.1\", :port => 7001}\n   7      ]\n   8  else\n   9      startup_nodes = [\n  10          {:host => ARGV[0], :port => ARGV[1].to_i}\n  11      ]\n  12  end\n  13\n  14  rc = RedisCluster.new(startup_nodes,32,:timeout => 0.1)\n  15\n  16  last = false\n  17\n  18  while not last\n  19      begin\n  20          last = rc.get(\"__last__\")\n  21          last = 0 if !last\n  22      rescue => e\n  23          puts \"error #{e.to_s}\"\n  24          sleep 1\n  25      end\n  26  end\n  27\n  28  ((last.to_i+1)..1000000000).each{|x|\n  29      begin\n  30          rc.set(\"foo#{x}\",x)\n  31          puts rc.get(\"foo#{x}\")\n  32          rc.set(\"__last__\",x)\n  33      rescue => e\n  34          puts \"error #{e.to_s}\"\n  35      end\n  36      sleep 0.1\n  37  }","section_id":"create-and-use-a-redis-cluster"},{"id":"create-and-use-a-redis-cluster-ex5","language":"plaintext","code":"ruby ./example.rb\n1\n2\n3\n4\n5\n6\n7\n8\n9\n^C (I stopped the program here)","section_id":"create-and-use-a-redis-cluster"},{"id":"create-and-use-a-redis-cluster-ex6","language":"plaintext","code":"$ redis-cli -p 7000 cluster nodes | grep myself\n97a3a64667477371c4479320d683e4c8db5858b1 :0 myself,master - 0 0 0 connected 0-5460","section_id":"create-and-use-a-redis-cluster"},{"id":"create-and-use-a-redis-cluster-ex7","language":"plaintext","code":"$ ruby consistency-test.rb\n925 R (0 err) | 925 W (0 err) |\n5030 R (0 err) | 5030 W (0 err) |\n9261 R (0 err) | 9261 W (0 err) |\n13517 R (0 err) | 13517 W (0 err) |\n17780 R (0 err) | 17780 W (0 err) |\n22025 R (0 err) | 22025 W (0 err) |\n25818 R (0 err) | 25818 W (0 err) |","section_id":"create-and-use-a-redis-cluster"},{"id":"create-and-use-a-redis-cluster-ex8","language":"plaintext","code":"$ redis-cli -h 127.0.0.1 -p 7000 set key_217 0\nOK\n\n(in the other tab I see...)\n\n94774 R (0 err) | 94774 W (0 err) |\n98821 R (0 err) | 98821 W (0 err) |\n102886 R (0 err) | 102886 W (0 err) | 114 lost |\n107046 R (0 err) | 107046 W (0 err) | 114 lost |","section_id":"create-and-use-a-redis-cluster"},{"id":"create-and-use-a-redis-cluster-ex9","language":"plaintext","code":"$ redis-cli -p 7000 cluster nodes | grep master\n3e3a6cb0d9a9a87168e266b0a0b24026c0aae3f0 127.0.0.1:7001 master - 0 1385482984082 0 connected 5960-10921\n2938205e12de373867bf38f1ca29d31d0ddb3e46 127.0.0.1:7002 master - 0 1385482983582 0 connected 11423-16383\n97a3a64667477371c4479320d683e4c8db5858b1 :0 myself,master - 0 0 0 connected 0-5959 10922-11422","section_id":"create-and-use-a-redis-cluster"},{"id":"create-and-use-a-redis-cluster-ex10","language":"plaintext","code":"$ redis-cli -p 7002 debug segfault\nError: Server closed the connection","section_id":"create-and-use-a-redis-cluster"},{"id":"create-and-use-a-redis-cluster-ex11","language":"plaintext","code":"18849 R (0 err) | 18849 W (0 err) |\n23151 R (0 err) | 23151 W (0 err) |\n27302 R (0 err) | 27302 W (0 err) |\n\n... many error warnings here ...\n\n29659 R (578 err) | 29660 W (577 err) |\n33749 R (578 err) | 33750 W (577 err) |\n37918 R (578 err) | 37919 W (577 err) |\n42077 R (578 err) | 42078 W (577 err) |","section_id":"create-and-use-a-redis-cluster"},{"id":"create-and-use-a-redis-cluster-ex12","language":"plaintext","code":"$ redis-cli -p 7000 cluster nodes\n3fc783611028b1707fd65345e763befb36454d73 127.0.0.1:7004 slave 3e3a6cb0d9a9a87168e266b0a0b24026c0aae3f0 0 1385503418521 0 connected\na211e242fc6b22a9427fed61285e85892fa04e08 127.0.0.1:7003 slave 97a3a64667477371c4479320d683e4c8db5858b1 0 1385503419023 0 connected\n97a3a64667477371c4479320d683e4c8db5858b1 :0 myself,master - 0 0 0 connected 0-5959 10922-11422\n3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 127.0.0.1:7005 master - 0 1385503419023 3 connected 11423-16383\n3e3a6cb0d9a9a87168e266b0a0b24026c0aae3f0 127.0.0.1:7001 master - 0 1385503417005 0 connected 5960-10921\n2938205e12de373867bf38f1ca29d31d0ddb3e46 127.0.0.1:7002 slave 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 0 1385503418016 3 connected","section_id":"create-and-use-a-redis-cluster"},{"id":"create-and-use-a-redis-cluster-ex13","language":"plaintext","code":"redis 127.0.0.1:7006> cluster nodes\n3e3a6cb0d9a9a87168e266b0a0b24026c0aae3f0 127.0.0.1:7001 master - 0 1385543178575 0 connected 5960-10921\n3fc783611028b1707fd65345e763befb36454d73 127.0.0.1:7004 slave 3e3a6cb0d9a9a87168e266b0a0b24026c0aae3f0 0 1385543179583 0 connected\nf093c80dde814da99c5cf72a7dd01590792b783b :0 myself,master - 0 0 0 connected\n2938205e12de373867bf38f1ca29d31d0ddb3e46 127.0.0.1:7002 slave 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 0 1385543178072 3 connected\na211e242fc6b22a9427fed61285e85892fa04e08 127.0.0.1:7003 slave 97a3a64667477371c4479320d683e4c8db5858b1 0 1385543178575 0 connected\n97a3a64667477371c4479320d683e4c8db5858b1 127.0.0.1:7000 master - 0 1385543179080 0 connected 0-5959 10922-11422\n3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 127.0.0.1:7005 master - 0 1385543177568 3 connected 11423-16383","section_id":"create-and-use-a-redis-cluster"},{"id":"create-and-use-a-redis-cluster-ex14","language":"plaintext","code":"$ redis-cli -p 7000 cluster nodes | grep slave | grep 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e\nf093c80dde814da99c5cf72a7dd01590792b783b 127.0.0.1:7006 slave 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 0 1385543617702 3 connected\n2938205e12de373867bf38f1ca29d31d0ddb3e46 127.0.0.1:7002 slave 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e 0 1385543617198 3 connected","section_id":"create-and-use-a-redis-cluster"}]}
{"schema_version":2,"id":"operate/oss_and_stack/management/security/acl","title":"ACL","url":"https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl/","summary":"Redis Access Control List","aliases":["/manual/security/acl/","/management/security/acl/"],"tags":["docs","operate","stack","oss"],"last_updated":"2026-10-13T16:38:58+03:00","page_type":"content","content_hash":"1ee611511bbbaeb6d8eef46776ad1ad000217b5e198db0e7ad25fc0c72d8d62d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The Redis ACL, short for Access Control List, is the feature that allows certain\nconnections to be limited in terms of the commands that can be executed and the\nkeys that can be accessed. The way it works is that, after connecting, a client\nis required to provide a username and a valid password to authenticate. If authentication succeeded, the connection is associated with a given\nuser and the limits the user has. Redis can be configured so that new\nconnections are already authenticated with a \"default\" user (this is the\ndefault configuration). Configuring the default user has, as a side effect,\nthe ability to provide only a specific subset of functionalities to connections\nthat are not explicitly authenticated.\n\nIn the default configuration, Redis 6 (the first version to have ACLs) works\nexactly like older versions of Redis. Every new connection is\ncapable of calling every possible command and accessing every key, so the\nACL feature is backward compatible with old clients and applications. Also\nthe old way to configure a password, using the **requirepass** configuration\ndirective, still works as expected. However, it now\nsets a password for the default user.\n\nThe Redis [`AUTH`](https://redis.io/docs/latest/commands/auth) command was extended in Redis 6, so now it is possible to\nuse it in the two-arguments form:\n\n    AUTH <username> <password>\n\nHere's an example of the old form:\n\n    AUTH <password>\n\nWhat happens is that the username used to authenticate is \"default\", so\njust specifying the password implies that we want to authenticate against\nthe default user. This provides backward compatibility."},{"id":"when-acls-are-useful","title":"When ACLs are useful","role":"content","text":"Before using ACLs, you may want to ask yourself what's the goal you want to\naccomplish by implementing this layer of protection. Normally there are\ntwo main goals that are well served by ACLs:\n\n1. You want to improve security by restricting the access to commands and keys, so that untrusted clients have no access and trusted clients have just the minimum access level to the database in order to perform the work needed. For instance, certain clients may just be able to execute read only commands.\n2. You want to improve operational safety, so that processes or humans accessing Redis are not allowed to damage the data or the configuration because of software errors or manual mistakes. For example, a worker that fetches delayed jobs from Redis does not need permission to call [`FLUSHALL`](https://redis.io/docs/latest/commands/flushall), [`FLUSHDB`](https://redis.io/docs/latest/commands/flushdb), or [`SWAPDB`](https://redis.io/docs/latest/commands/swapdb).\n\nAnother typical usage of ACLs is related to managed Redis instances. Redis is\noften provided as a managed service both by internal company teams that handle\nthe Redis infrastructure for the other internal customers they have, or is\nprovided in a software-as-a-service setup by cloud providers. In both \nsetups, we want to be sure that configuration commands are excluded for the\ncustomers."},{"id":"configure-acls-with-the-acl-command","title":"Configure ACLs with the ACL command","role":"configuration","text":"ACLs are defined using a DSL (domain specific language) that describes what\na given user is allowed to do. Such rules are always implemented from the\nfirst to the last, left-to-right, because sometimes the order of the rules is\nimportant to understand what the user is really able to do.\n\nBy default there is a single user defined, called *default*. We\ncan use the [`ACL LIST`](https://redis.io/docs/latest/commands/acl-list) command in order to check the currently active ACLs\nand verify what the configuration of a freshly started, defaults-configured\nRedis instance is:\n\n    > ACL LIST\n    1) \"user default on nopass ~* &* +@all\"\n\nThe command above reports the list of users in the same format that is\nused in the Redis configuration files, by translating the current ACLs set\nfor the users back into their description.\n\nThe first two words in each line are \"user\" followed by the username. The\nnext words are ACL rules that describe different things. We'll show how the rules work in detail, but for now it is enough to say that the default\nuser is configured to be active (on), to require no password (nopass), to\naccess every possible key (`~*`) and Pub/Sub channel (`&*`), and be able to\ncall every possible command (`+@all`).\n\nAlso, in the special case of the default user, having the *nopass* rule means\nthat new connections are automatically authenticated with the default user\nwithout any explicit [`AUTH`](https://redis.io/docs/latest/commands/auth) call needed."},{"id":"acl-rules","title":"ACL rules","role":"security","text":"The following is the list of valid ACL rules. Certain rules are just\nsingle words that are used in order to activate or remove a flag, or to\nperform a given change to the user ACL. Other rules are char prefixes that\nare concatenated with command or category names, key patterns, and\nso forth.\n\nEnable and disallow users:\n\n* `on`: Enable the user: it is possible to authenticate as this user.\n* `off`: Disallow the user: it's no longer possible to authenticate with this user; however, previously authenticated connections will still work. Note that if the default user is flagged as *off*, new connections will start as not authenticated and will require the user to send [`AUTH`](https://redis.io/docs/latest/commands/auth) or [`HELLO`](https://redis.io/docs/latest/commands/hello) with the AUTH option in order to authenticate in some way, regardless of the default user configuration.\n\nAllow and disallow commands:\n\n* `+<command>`: Add the command to the list of commands the user can call. Can be used with `|` for allowing subcommands (e.g \"+config|get\").\n* `-<command>`: Remove the command to the list of commands the user can call. Starting Redis 7.0, it can be used with `|` for blocking subcommands (e.g \"-config|set\").\n* `+@<category>`: Add all the commands in such category to be called by the user, with valid categories being like @admin, @set, @sortedset, ... and so forth, see the full list by calling the [`ACL CAT`](https://redis.io/docs/latest/commands/acl-cat) command. The special category @all means all the commands, both the ones currently present in the server, and the ones that will be loaded in the future via modules.\n* `-@<category>`: Like `+@<category>` but removes the commands from the list of commands the client can call.\n* `+<command>|first-arg`: Allow a specific first argument of an otherwise disabled command. It is only supported on commands with no sub-commands, and is not allowed as negative form like -SELECT|1, only additive starting with \"+\". This feature is deprecated and may be removed in the future.\n* `allcommands`: Alias for +@all. Note that it implies the ability to execute all the future commands loaded via the modules system.\n* `nocommands`: Alias for -@all.\n\nAllow and disallow certain keys and key permissions:\n\n* `~<pattern>`: Add a pattern of keys that can be mentioned as part of commands. For instance `~*` allows all the keys. The pattern is a glob-style pattern like the one of [`KEYS`](https://redis.io/docs/latest/commands/keys). It is possible to specify multiple patterns.\n* `%R~<pattern>`: (Available in Redis 7.0 and later) Add the specified read key pattern. This behaves similar to the regular key pattern but only grants permission to read from keys that match the given pattern. See [key permissions](#key-permissions) for more information.\n* `%W~<pattern>`: (Available in Redis 7.0 and later) Add the specified write key pattern. This behaves similar to the regular key pattern but only grants permission to write to keys that match the given pattern. See [key permissions](#key-permissions) for more information.\n* `%RW~<pattern>`: (Available in Redis 7.0 and later) Alias for `~<pattern>`. \n* `allkeys`: Alias for `~*`.\n* `resetkeys`: Flush the list of allowed keys patterns. For instance the ACL `~foo:* ~bar:* resetkeys ~objects:*`, will only allow the client to access keys that match the pattern `objects:*`.\n\nAllow and disallow Pub/Sub channels:\n\n* `&<pattern>`: (Available in Redis 6.2 and later) Add a glob style pattern of Pub/Sub channels that can be accessed by the user. It is possible to specify multiple channel patterns. Note that pattern matching is done only for channels mentioned by [`PUBLISH`](https://redis.io/docs/latest/commands/publish) and [`SUBSCRIBE`](https://redis.io/docs/latest/commands/subscribe), whereas [`PSUBSCRIBE`](https://redis.io/docs/latest/commands/psubscribe) requires a literal match between its channel patterns and those allowed for user.\n* `allchannels`: Alias for `&*` that allows the user to access all Pub/Sub channels.\n* `resetchannels`: Flush the list of allowed channel patterns and disconnect the user's Pub/Sub clients if these are no longer able to access their respective channels and/or channel patterns.\n\nConfigure valid passwords for the user:\n\n* `><password>`: Add this password to the list of valid passwords for the user. For example `>mypass` will add \"mypass\" to the list of valid passwords.  This directive clears the *nopass* flag (see later). Every user can have any number of passwords.\n* `<<password>`: Remove this password from the list of valid passwords. Emits an error in case the password you are trying to remove is actually not set.\n* `#<hash>`: Add this SHA-256 hash value to the list of valid passwords for the user. This hash value will be compared to the hash of a password entered for an ACL user. This allows users to store hashes in the `acl.conf` file rather than storing cleartext passwords. Only SHA-256 hash values are accepted as the password hash must be 64 characters and only contain lowercase hexadecimal characters.\n* `!<hash>`: Remove this hash value from the list of valid passwords. This is useful when you do not know the password specified by the hash value but would like to remove the password from the user.\n* `nopass`: All the set passwords of the user are removed, and the user is flagged as requiring no password: it means that every password will work against this user. If this directive is used for the default user, every new connection will be immediately authenticated with the default user without any explicit AUTH command required. Note that the *resetpass* directive will clear this condition.\n* `resetpass`: Flushes the list of allowed passwords and removes the *nopass* status. After *resetpass*, the user has no associated passwords and there is no way to authenticate without adding some password (or setting it as *nopass* later).\n\n*Note: if a user is not flagged with nopass and has no list of valid passwords, that user is effectively impossible to use because there will be no way to log in as that user.*\n\nConfigure selectors for the user:\n\n* `(<rule list>)`: (Available in Redis 7.0 and later) Create a new selector to match rules against. Selectors are evaluated after the user permissions, and are evaluated according to the order they are defined. If a command matches either the user permissions or any selector, it is allowed. See [selectors](#selectors) for more information.\n* `clearselectors`: (Available in Redis 7.0 and later) Delete all of the selectors attached to the user.\n\nReset the user:\n\n* `reset` Performs the following actions: resetpass, resetkeys, resetchannels, allchannels (if acl-pubsub-default is set), off, clearselectors, -@all. The user returns to the same state it had immediately after its creation."},{"id":"create-and-edit-user-acls-with-the-acl-setuser-command","title":"Create and edit user ACLs with the ACL SETUSER command","role":"content","text":"Users can be created and modified in two main ways:\n\n1. Using the ACL command and its [`ACL SETUSER`](https://redis.io/docs/latest/commands/acl-setuser) subcommand.\n2. Modifying the server configuration, where users can be defined, and restarting the server. With an *external ACL file*, just call [`ACL LOAD`](https://redis.io/docs/latest/commands/acl-load).\n\nIn this section we'll learn how to define users using the [`ACL`](https://redis.io/docs/latest/commands/acl) command.\nWith such knowledge, it will be trivial to do the same things via the\nconfiguration files. Defining users in the configuration deserves its own\nsection and will be discussed later separately.\n\nTo start, try the simplest [`ACL SETUSER`](https://redis.io/docs/latest/commands/acl-setuser) command call:\n\n    > ACL SETUSER alice\n    OK\n\nThe [`ACL SETUSER`](https://redis.io/docs/latest/commands/acl-setuser) command takes the username and a list of ACL rules to apply\nto the user. However the above example did not specify any rule at all.\nThis will just create the user if it did not exist, using the defaults for new\nusers. If the user already exists, the command above will do nothing at all.\n\nCheck the default user status:\n\n    > ACL LIST\n    1) \"user alice off resetchannels -@all\"\n    2) \"user default on nopass ~* &* +@all\"\n\nThe new user \"alice\" is:\n\n* In the off status, so [`AUTH`](https://redis.io/docs/latest/commands/auth) will not work for the user \"alice\".\n* The user also has no passwords set.\n* Cannot access any command. Note that the user is created by default without the ability to access any command, so the `-@all` in the output above could be omitted; however, [`ACL LIST`](https://redis.io/docs/latest/commands/acl-list) attempts to be explicit rather than implicit.\n* There are no key patterns that the user can access.\n* There are no Pub/Sub channels that the user can access.\n\nNew users are created with restrictive permissions by default. Starting with Redis 6.2, ACL provides Pub/Sub channels access management as well. To ensure backward compatibility with version 6.0 when upgrading to Redis 6.2, new users are granted the 'allchannels' permission by default. The default can be set to `resetchannels` via the `acl-pubsub-default` configuration directive.\n\nFrom 7.0, The `acl-pubsub-default` value is set to `resetchannels` to restrict the channels access by default to provide better security.\nThe default can be set to `allchannels` via the `acl-pubsub-default` configuration directive to be compatible with previous versions.\n\nSuch user is completely useless. Let's try to define the user so that\nit is active, has a password, and can access with only the [`GET`](https://redis.io/docs/latest/commands/get) command\nto key names starting with the string \"cached:\".\n\n    > ACL SETUSER alice on >p1pp0 ~cached:* +get\n    OK\n\nNow the user can do something, but will refuse to do other things:\n\n    > AUTH alice p1pp0\n    OK\n    > GET foo\n    (error) NOPERM this user has no permissions to access one of the keys used as arguments\n    > GET cached:1234\n    (nil)\n    > SET cached:1234 zap\n    (error) NOPERM this user has no permissions to run the 'set' command\n\nThings are working as expected. In order to inspect the configuration of the\nuser alice (remember that user names are case sensitive), it is possible to\nuse an alternative to [`ACL LIST`](https://redis.io/docs/latest/commands/acl-list) which is designed to be more suitable for\ncomputers to read, while [`ACL GETUSER`](https://redis.io/docs/latest/commands/acl-getuser) is more human readable.\n\n    > ACL GETUSER alice\n    1) \"flags\"\n    2) 1) \"on\"\n    3) \"passwords\"\n    4) 1) \"2d9c75...\"\n    5) \"commands\"\n    6) \"-@all +get\"\n    7) \"keys\"\n    8) \"~cached:*\"\n    9) \"channels\"\n    10) \"\"\n    11) \"selectors\"\n    12) (empty array)\n\nThe [`ACL GETUSER`](https://redis.io/docs/latest/commands/acl-getuser) returns a field-value array that describes the user in more parsable terms. The output includes the set of flags, a list of key patterns, passwords, and so forth. The output is probably more readable if we use RESP3, so that it is returned as a map reply:\n\n    > ACL GETUSER alice\n    1# \"flags\" => 1~ \"on\"\n    2# \"passwords\" => 1) \"2d9c75273d72b32df726fb545c8a4edc719f0a95a6fd993950b10c474ad9c927\"\n    3# \"commands\" => \"-@all +get\"\n    4# \"keys\" => \"~cached:*\"\n    5# \"channels\" => \"\"\n    6# \"selectors\" => (empty array)\n\n*Note: from now on, we'll continue using the Redis default protocol, version 2*\n\nUsing another [`ACL SETUSER`](https://redis.io/docs/latest/commands/acl-setuser) command (from a different user, because alice cannot run the [`ACL`](https://redis.io/docs/latest/commands/acl) command), we can add multiple patterns to the user:\n\n    > ACL SETUSER alice ~objects:* ~items:* ~public:*\n    OK\n    > ACL LIST\n    1) \"user alice on #2d9c75... ~cached:* ~objects:* ~items:* ~public:* resetchannels -@all +get\"\n    2) \"user default on nopass ~* &* +@all\"\n\nThe user representation in memory is now as we expect it to be."},{"id":"multiple-calls-to-acl-setuser","title":"Multiple calls to ACL SETUSER","role":"content","text":"It is very important to understand what happens when [`ACL SETUSER`](https://redis.io/docs/latest/commands/acl-setuser) is called\nmultiple times. What is critical to know is that every [`ACL SETUSER`](https://redis.io/docs/latest/commands/acl-setuser) call will\nNOT reset the user, but will just apply the ACL rules to the existing user.\nThe user is reset only if it was not known before. In that case, a brand new\nuser is created with zeroed-ACLs. The user cannot do anything, is\ndisallowed, has no passwords, and so forth. This is the best default for safety.\n\nHowever later calls will just modify the user incrementally. For instance,\nthe following sequence:\n\n    > ACL SETUSER myuser +set\n    OK\n    > ACL SETUSER myuser +get\n    OK\n\nWill result in myuser being able to call both [`GET`](https://redis.io/docs/latest/commands/get) and [`SET`](https://redis.io/docs/latest/commands/set):\n\n    > ACL LIST\n    1) \"user default on nopass ~* &* +@all\"\n    2) \"user myuser off resetchannels -@all +get +set\""},{"id":"command-categories","title":"Command categories","role":"syntax","text":"Setting user ACLs by specifying all the commands one after the other is\nreally annoying, so instead we do things like this:\n\n    > ACL SETUSER antirez on +@all -@dangerous >42a979... ~*\n\nThe above command includes all commands (`+@all`) and then removes all commands tagged as dangerous (`-@dangerous`) inside the Redis command table.\nNote that command categories, with the exception of `+@all`, never include module commands.\n\nIf you use `+@all` for a particular user, all commands are available to that user, including commands loaded via the modules system. However, if you use `+@read` or any other, module commands are excluded. This concept is fundamental because you should only trust the Redis\ninternal command table. Modules may expose dangerous things, and in\nthe case of an ACL that is just additive, that is, in the form of `+@all -...`,\nyou should be absolutely sure that you won't include what you did not mean to.\n\nThe following is a list of command categories and their meanings:\n\n* <a id=\"admin\"></a>**admin** - Administrative commands. Normal applications will never need to use\n  these. Includes [`REPLICAOF`](https://redis.io/docs/latest/commands/replicaof), [`CONFIG`](https://redis.io/docs/latest/commands/config), [`DEBUG`](https://redis.io/docs/latest/commands/debug), [`SAVE`](https://redis.io/docs/latest/commands/save), [`MONITOR`](https://redis.io/docs/latest/commands/monitor), [`ACL`](https://redis.io/docs/latest/commands/acl), [`SHUTDOWN`](https://redis.io/docs/latest/commands/shutdown), etc.\n* <a id=\"array\"></a>**array** - Data type: all array related commands.\n* <a id=\"bitmap\"></a>**bitmap** - Data type: all bitmap related commands.\n* <a id=\"blocking\"></a>**blocking** - Potentially blocking the connection until released by another\n  command.\n* <a id=\"bloom\"></a>**bloom** - Data type: all Bloom filter related commands. <sup>[1](#cmd-note-1)</sup>\n* <a id=\"cms\"></a>**cms** - Data type: count-min sketch related commands. <sup>[1](#cmd-note-1)</sup>\n* <a id=\"connection\"></a>**connection** - Commands affecting the connection or other connections.\n  This includes [`AUTH`](https://redis.io/docs/latest/commands/auth), [`SELECT`](https://redis.io/docs/latest/commands/select), [`COMMAND`](https://redis.io/docs/latest/commands/command), [`CLIENT`](https://redis.io/docs/latest/commands/client), [`ECHO`](https://redis.io/docs/latest/commands/echo), [`PING`](https://redis.io/docs/latest/commands/ping), etc.\n* <a id=\"cuckoo\"></a>**cuckoo** - Data type: all Cuckoo filter related commands. <sup>[1](#cmd-note-1)</sup>\n* <a id=\"dangerous\"></a>**dangerous** - Potentially dangerous commands (each should be considered with care for\n  various reasons). This includes [`FLUSHALL`](https://redis.io/docs/latest/commands/flushall), [`MIGRATE`](https://redis.io/docs/latest/commands/migrate), [`RESTORE`](https://redis.io/docs/latest/commands/restore), [`SORT`](https://redis.io/docs/latest/commands/sort), [`KEYS`](https://redis.io/docs/latest/commands/keys),\n  [`CLIENT`](https://redis.io/docs/latest/commands/client), [`DEBUG`](https://redis.io/docs/latest/commands/debug), [`INFO`](https://redis.io/docs/latest/commands/info), [`CONFIG`](https://redis.io/docs/latest/commands/config), [`SAVE`](https://redis.io/docs/latest/commands/save), [`REPLICAOF`](https://redis.io/docs/latest/commands/replicaof), etc.\n* <a id=\"fast\"></a>**fast** - Fast O(1) commands. May loop on the number of arguments, but not the number of elements in the key.\n* <a id=\"geo\"></a>**geo** - Data type: all geospatial index related commands.\n* <a id=\"hash\"></a>**hash** - Data type: all hash related commands.\n* <a id=\"hyperloglog\"></a>**hyperloglog** - Data type: all hyperloglog related commands.\n* <a id=\"json\"></a>**json** - Data type: all JSON related commands. <sup>[1](#cmd-note-1)</sup>\n* <a id=\"keyspace\"></a>**keyspace** - Writing or reading from keys, databases, or their metadata\n  in a type agnostic way. Includes [`DEL`](https://redis.io/docs/latest/commands/del), [`RESTORE`](https://redis.io/docs/latest/commands/restore), [`DUMP`](https://redis.io/docs/latest/commands/dump), [`RENAME`](https://redis.io/docs/latest/commands/rename), [`EXISTS`](https://redis.io/docs/latest/commands/exists), [`DBSIZE`](https://redis.io/docs/latest/commands/dbsize),\n  [`KEYS`](https://redis.io/docs/latest/commands/keys), [`SCAN`](https://redis.io/docs/latest/commands/scan), [`EXPIRE`](https://redis.io/docs/latest/commands/expire), [`TTL`](https://redis.io/docs/latest/commands/ttl), [`FLUSHALL`](https://redis.io/docs/latest/commands/flushall), etc. Commands that may modify the keyspace,\n  key, or metadata will also have the `write` category. Commands that only read\n  the keyspace, key, or metadata will have the `read` category.\n* <a id=\"list\"></a>**list** - Data type: all list related commands.\n* <a id=\"pubsub\"></a>**pubsub** - all pubsub related commands.\n* <a id=\"read\"></a>**read** - Reading from keys (values or metadata). Note that commands that don't interact with keys, will not have either `read` or `write`.\n* <a id=\"scripting\"></a>**scripting** - Scripting related.\n* <a id=\"search\"></a>**search** - All search related commands. Only ACL users with access to a superset of the key prefixes defined during index creation can create, modify, or read the index. For example, a user with the key ACL pattern `h:*` can create an index with keys prefixed by `h:*` or `h:p*`, but not keys prefixed by `h*`, `k:*`, or `k*`, because these prefixes may involve keys to which the user does not have access. <sup>[1](#cmd-note-1)</sup>\n* <a id=\"set\"></a>**set** - Data type: all set related commands.\n* <a id=\"sortedset\"></a>**sortedset** - Data type: all sorted set related commands.\n* <a id=\"slow\"></a>**slow** - All commands that are not `fast`.\n* <a id=\"stream\"></a>**stream** - Data type: all stream related commands.\n* <a id=\"string\"></a>**string** - Data type: all string related commands.\n* <a id=\"tdigest\"></a>**tdigest** - Data type: all t-digest related commands. <sup>[1](#cmd-note-1)</sup>\n* <a id=\"timeseries\"></a>**timeseries** - Data type: all time series related commands. <sup>[1](#cmd-note-1)</sup>\n* <a id=\"topk\"></a>**topk** - Data type: all top-k related commands. <sup>[1](#cmd-note-1)</sup>\n* <a id=\"transaction\"></a>**transaction** - [`WATCH`](https://redis.io/docs/latest/commands/watch) / [`MULTI`](https://redis.io/docs/latest/commands/multi) / [`EXEC`](https://redis.io/docs/latest/commands/exec) related commands.\n* <a id=\"write\"></a>**write** - Writing to keys (values or metadata). Note that commands that don't interact with keys, will not have either `read` or `write`.\n\n1. <a name=\"cmd-note-1\"></a> See the [Redis 8 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.0-release-notes#potentially-breaking-changes-to-acls) for more information about these command categories, which may introduce breaking changes to your Redis deployments.\n\nNOTE: Redis can also show you a list of all categories and the exact commands each category includes using the Redis [`ACL CAT`](https://redis.io/docs/latest/commands/acl-cat) command. \nIt can be used in two forms:\n\n    ACL CAT -- Will just list all the categories available\n    ACL CAT <category-name> -- Will list all the commands inside the category\n\nExamples:\n\n[code example]\n\nAs you can see, so far there are 29 distinct categories. Now let's check what\ncommand is part of the *geo* category:\n\n[code example]\n\nNote that commands may be part of multiple categories. For example, an\nACL rule like `+@geo -@read` will result in certain geo commands to be\nexcluded because they are read-only commands."},{"id":"allowblock-subcommands","title":"Allow/block subcommands","role":"content","text":"Starting from Redis 7.0, subcommands can be allowed/blocked just like other\ncommands (by using the separator `|` between the command and subcommand, for\nexample: `+config|get` or `-config|set`)\n\nThat is true for all commands except DEBUG. In order to allow/block specific\nDEBUG subcommands, see the next section."},{"id":"allow-the-first-arg-of-a-blocked-command","title":"Allow the first-arg of a blocked command","role":"content","text":"**Note: This feature is deprecated since Redis 7.0 and may be removed in the future.**\n\nSometimes the ability to exclude or include a command or a subcommand as a whole is not enough.\nMany deployments may not be happy providing the ability to execute a [`SELECT`](https://redis.io/docs/latest/commands/select) for any DB, but may\nstill want to be able to run `SELECT 0`.\n\nIn such case we could alter the ACL of a user in the following way:\n\n    ACL SETUSER myuser -select +select|0\n\nFirst, remove the [`SELECT`](https://redis.io/docs/latest/commands/select) command and then add the allowed\nfirst-arg. Note that **it is not possible to do the reverse** since first-args\ncan be only added, not excluded. It is safer to specify all the first-args\nthat are valid for some user since it is possible that\nnew first-args may be added in the future.\n\nAnother example:\n\n    ACL SETUSER myuser -debug +debug|digest\n\nNote that first-arg matching may add some performance penalty; however, it is hard to measure even with synthetic benchmarks. The\nadditional CPU cost is only paid when such commands are called, and not when\nother commands are called.\n\nIt is possible to use this mechanism in order to allow subcommands in Redis\nversions prior to 7.0 (see above section)."},{"id":"all-vs--all","title":"+@all VS -@all","role":"content","text":"In the previous section, it was observed how it is possible to define command\nACLs based on adding/removing single commands."},{"id":"selectors","title":"Selectors","role":"content","text":"Starting with Redis 7.0, Redis supports adding multiple sets of rules that are evaluated independently of each other.\nThese secondary sets of permissions are called selectors and added by wrapping a set of rules within parentheses.\nIn order to execute a command, either the root permissions (rules defined outside of parenthesis) or any of the selectors (rules defined inside parenthesis) must match the given command.\nInternally, the root permissions are checked first followed by selectors in the order they were added.\n\nFor example, consider a user with the ACL rules `+GET ~key1 (+SET ~key2)`.\nThis user is able to execute `GET key1` and `SET key2 hello`, but not `GET key2` or `SET key1 world`.\n\nUnlike the user's root permissions, selectors cannot be modified after they are added.\nInstead, selectors can be removed with the `clearselectors` keyword, which removes all of the added selectors.\nNote that `clearselectors` does not remove the root permissions."},{"id":"key-permissions","title":"Key permissions","role":"content","text":"Starting with Redis 7.0, key patterns can also be used to define how a command is able to touch a key.\nThis is achieved through rules that define key permissions.\nThe key permission rules take the form of `%(<permission>)~<pattern>`.\nPermissions are defined as individual characters that map to the following key permissions:\n\n* W (Write): The data stored within the key may be updated or deleted. \n* R (Read): User supplied data from the key is processed, copied or returned. Note that this does not include metadata such as size information (example [`STRLEN`](https://redis.io/docs/latest/commands/strlen)), type information (example [`TYPE`](https://redis.io/docs/latest/commands/type)) or information about whether a value exists within a collection (example [`SISMEMBER`](https://redis.io/docs/latest/commands/sismember)). \n\nPermissions can be composed together by specifying multiple characters. \nSpecifying the permission as 'RW' is considered full access and is analogous to just passing in `~<pattern>`.\n\nFor a concrete example, consider a user with ACL rules `+@all ~app1:* (+@read ~app2:*)`.\nThis user has full access on `app1:*` and readonly access on `app2:*`.\nHowever, some commands support reading data from one key, doing some transformation, and storing it into another key.\nOne such command is the [`COPY`](https://redis.io/docs/latest/commands/copy) command, which copies the data from the source key into the destination key.\nThe example set of ACL rules is unable to handle a request copying data from `app2:user` into `app1:user`, since neither the root permission nor the selector fully matches the command.\nHowever, using key selectors you can define a set of ACL rules that can handle this request `+@all ~app1:* %R~app2:*`.\nThe first pattern is able to match `app1:user` and the second pattern is able to match `app2:user`.\n\nWhich type of permission is required for a command is documented through [key specifications](https://redis.io/docs/latest/develop/reference/key-specs#logical-operation-flags).\nThe type of permission is based off the keys logical operation flags. \nThe insert, update, and delete flags map to the write key permission. \nThe access flag maps to the read key permission.\nIf the key has no logical operation flags, such as [`EXISTS`](https://redis.io/docs/latest/commands/exists), the user still needs either key read or key write permissions to execute the command. \n\nNote: Side channels to accessing user data are ignored when it comes to evaluating whether read permissions are required to execute a command.\nThis means that some write commands that return metadata about the modified key only require write permission on the key to execute.\nFor example, consider the following two commands:\n\n* `LPUSH key1 data`: modifies \"key1\" but only returns metadata about it, the size of the list after the push, so the command only requires write permission on \"key1\" to execute.\n* `LPOP key2`: modifies \"key2\" but also returns data from it, the left most item in the list, so the command requires both read and write permission on \"key2\" to execute.\n\nIf an application needs to make sure no data is accessed from a key, including side channels, it's recommended to not provide any access to the key.\n\n> [!NOTE]\n> Key patterns only restrict commands that operate on specific keys named in the command's arguments. Commands that operate on an entire database or the whole keyspace, and therefore take no key arguments, are not limited by key patterns. Such commands are governed only by command and [category](#command-categories) rules.\n\nThis includes commands that can destroy or replace data beyond a user's key patterns, such as [`FLUSHALL`](https://redis.io/docs/latest/commands/flushall), [`FLUSHDB`](https://redis.io/docs/latest/commands/flushdb), and [`SWAPDB`](https://redis.io/docs/latest/commands/swapdb).\n\nFor example, a user defined with `~tenant1:* +@all` can still run `FLUSHALL` to delete every key in the database, even though its key pattern only matches `tenant1:*`. To prevent this, remove those commands explicitly:\n\n    > ACL SETUSER tenant1 on >strong-password ~tenant1:* +@all -flushall -flushdb -swapdb"},{"id":"how-passwords-are-stored-internally","title":"How passwords are stored internally","role":"content","text":"Redis internally stores passwords hashed with SHA256. If you set a password\nand check the output of [`ACL LIST`](https://redis.io/docs/latest/commands/acl-list) or [`ACL GETUSER`](https://redis.io/docs/latest/commands/acl-getuser), you'll see a long hex\nstring that looks pseudo random. Here is an example, because in the previous\nexamples, for the sake of brevity, the long hex string was trimmed:\n\n[code example]\n\nUsing SHA256 provides the ability to avoid storing the password in clear text\nwhile still allowing for a very fast [`AUTH`](https://redis.io/docs/latest/commands/auth) command, which is a very important\nfeature of Redis and is coherent with what clients expect from Redis.\n\nHowever ACL *passwords* are not really passwords. They are shared secrets\nbetween the server and the client, because the password is\nnot an authentication token used by a human being. For instance:\n\n* There are no length limits, the password will just be memorized in some client software. There is no human that needs to recall a password in this context.\n* The ACL password does not protect any other thing. For example, it will never be the password for some email account.\n* Often when you are able to access the hashed password itself, by having full access to the Redis commands of a given server, or corrupting the system itself, you already have access to what the password is protecting: the Redis instance stability and the data it contains.\n\nFor this reason, slowing down the password authentication, in order to use an\nalgorithm that uses time and space to make password cracking hard,\nis a very poor choice. What we suggest instead is to generate strong\npasswords, so that nobody will be able to crack it using a\ndictionary or a brute force attack even if they have the hash. To do so, there is a special ACL\ncommand [`ACL GENPASS`](https://redis.io/docs/latest/commands/acl-genpass) that generates passwords using the system cryptographic pseudorandom\ngenerator:\n\n    > ACL GENPASS\n    \"dd721260bfe1b3d9601e7fbab36de6d04e2e67b0ef1c53de59d45950db0dd3cc\"\n\nThe command outputs a 32-byte (256-bit) pseudorandom string converted to a\n64-byte alphanumerical string. This is long enough to avoid attacks and short\nenough to be easy to manage, cut & paste, store, and so forth. This is what\nyou should use in order to generate Redis passwords."},{"id":"use-an-external-acl-file","title":"Use an external ACL file","role":"content","text":"There are two ways to store users inside the Redis configuration:\n\n1. Users can be specified directly inside the `redis.conf` file.\n2. It is possible to specify an external ACL file.\n\nThe two methods are *mutually incompatible*, so Redis will ask you to use one\nor the other. Specifying users inside `redis.conf` is\ngood for simple use cases. When there are multiple users to define, in a\ncomplex environment, we recommend you use the ACL file instead.\n\nThe format used inside `redis.conf` and in the external ACL file is exactly\nthe same, so it is trivial to switch from one to the other, and is\nthe following:\n\n    user <username> ... acl rules ...\n\nFor instance:\n\n    user worker +@list +@connection ~jobs:* on >ffa9203c493aa99\n\nWhen you want to use an external ACL file, you are required to specify\nthe configuration directive called `aclfile`, like this:\n\n    aclfile /etc/redis/users.acl\n\nWhen you are just specifying a few users directly inside the `redis.conf`\nfile, you can use [`CONFIG REWRITE`](https://redis.io/docs/latest/commands/config-rewrite) in order to store the new user configuration\ninside the file by rewriting it.\n\nThe external ACL file however is more powerful. You can do the following:\n\n* Use [`ACL LOAD`](https://redis.io/docs/latest/commands/acl-load) if you modified the ACL file manually and you want Redis to reload the new configuration. Note that this command is able to load the file *only if all the users are correctly specified*. Otherwise, an error is reported to the user, and the old configuration will remain valid.\n* Use [`ACL SAVE`](https://redis.io/docs/latest/commands/acl-save) to save the current ACL configuration to the ACL file.\n\nNote that [`CONFIG REWRITE`](https://redis.io/docs/latest/commands/config-rewrite) does not also trigger [`ACL SAVE`](https://redis.io/docs/latest/commands/acl-save). When you use\nan ACL file, the configuration and the ACLs are handled separately.\n\nStarting with Redis 8.8, the `ACL LOAD` command will allow comment lines starting with the `#` character. Any such comment lines are stripped when the ACL file is loaded and they are not persisted across `ACL LOAD`/`ACL SAVE` commands."},{"id":"acl-rules-for-sentinel-and-replicas","title":"ACL rules for Sentinel and Replicas","role":"security","text":"In case you don't want to provide Redis replicas and Redis Sentinel instances\nfull access to your Redis instances, the following is the set of commands\nthat must be allowed in order for everything to work correctly.\n\nFor Sentinel, allow the user to access the following commands both in the master and replica instances:\n\n* AUTH, CLIENT, SUBSCRIBE, SCRIPT, PUBLISH, PING, INFO, MULTI, SLAVEOF, CONFIG, CLIENT, EXEC.\n\nSentinel does not need to access any key in the database but does use Pub/Sub, so the ACL rule would be the following (note: [`AUTH`](https://redis.io/docs/latest/commands/auth) is not needed since it is always allowed):\n\n    ACL SETUSER sentinel-user on >somepassword resetchannels &__sentinel__:hello +multi +slaveof +ping +exec +subscribe +config|rewrite +role +publish +info +client|setname +client|kill +script|kill\n\nRedis replicas require the following commands to be allowed on the master instance:\n\n* PSYNC, REPLCONF, PING\n\nNo keys need to be accessed, so this translates to the following rules:\n\n    ACL setuser replica-user on >somepassword +psync +replconf +ping\n\nNote that you don't need to configure the replicas to allow the master to be able to execute any set of commands. The master is always authenticated as the root user from the point of view of replicas."}],"examples":[{"id":"command-categories-ex0","language":"plaintext","code":"> ACL CAT\n 1) \"keyspace\"\n 2) \"read\"\n 3) \"write\"\n 4) \"set\"\n 5) \"sortedset\"\n 6) \"list\"\n 7) \"hash\"\n 8) \"string\"\n 9) \"bitmap\"\n10) \"hyperloglog\"\n11) \"geo\"\n12) \"stream\"\n13) \"pubsub\"\n14) \"admin\"\n15) \"fast\"\n16) \"slow\"\n17) \"blocking\"\n18) \"dangerous\"\n19) \"connection\"\n20) \"transaction\"\n21) \"scripting\"\n22) \"json\"\n23) \"search\"\n24) \"tdigest\"\n25) \"cms\"\n26) \"bloom\"\n27) \"cuckoo\"\n28) \"topk\"\n29) \"timeseries\"","section_id":"command-categories"},{"id":"command-categories-ex1","language":"plaintext","code":"> ACL CAT geo\n1) \"geohash\"\n2) \"georadius_ro\"\n3) \"georadiusbymember\"\n4) \"geopos\"\n5) \"geoadd\"\n6) \"georadiusbymember_ro\"\n7) \"geodist\"\n8) \"georadius\"\n9) \"geosearch\"\n10) \"geosearchstore\"","section_id":"command-categories"},{"id":"how-passwords-are-stored-internally-ex0","language":"plaintext","code":"> ACL GETUSER default\n1) \"flags\"\n2) 1) \"on\"\n3) \"passwords\"\n4) 1) \"2d9c75273d72b32df726fb545c8a4edc719f0a95a6fd993950b10c474ad9c927\"\n5) \"commands\"\n6) \"+@all\"\n7) \"keys\"\n8) \"~*\"\n9) \"channels\"\n10) \"&*\"\n11) \"selectors\"\n12) (empty array)","section_id":"how-passwords-are-stored-internally"}]}
{"schema_version":2,"id":"operate/oss_and_stack/management/security/encryption","title":"TLS","url":"https://redis.io/docs/latest/operate/oss_and_stack/management/security/encryption/","summary":"Redis TLS support","aliases":["/management/security/encryption/"],"tags":["docs","operate","stack","oss"],"last_updated":"2026-04-07T16:24:18+01:00","page_type":"content","content_hash":"0f8bf7522a31d04921eed102d44ad08f56f5a3290231962122e3c4871541a9a8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"SSL/TLS is supported by Redis starting with version 6 as an optional feature\nthat needs to be enabled at compile time."},{"id":"getting-started","title":"Getting Started","role":"setup","text":""},{"id":"building","title":"Building","role":"content","text":"To build with TLS support you'll need OpenSSL development libraries (e.g.\n`libssl-dev` on Debian/Ubuntu).\n\nBuild Redis with the following command:\n\n[code example]"},{"id":"tests","title":"Tests","role":"content","text":"To run Redis test suite with TLS, you'll need TLS support for TCL (i.e.\n`tcl-tls` package on Debian/Ubuntu).\n\n1. Run `./utils/gen-test-certs.sh` to generate a root CA and a server\n   certificate.\n\n2. Run `./runtest --tls` or `./runtest-cluster --tls` to run Redis and Redis\n   Cluster tests in TLS mode."},{"id":"running-manually","title":"Running manually","role":"content","text":"To manually run a Redis server with TLS mode (assuming `gen-test-certs.sh` was\ninvoked so sample certificates/keys are available):\n\n    ./src/redis-server --tls-port 6379 --port 0 \\\n        --tls-cert-file ./tests/tls/redis.crt \\\n        --tls-key-file ./tests/tls/redis.key \\\n        --tls-ca-cert-file ./tests/tls/ca.crt\n\nTo connect to this Redis server with `redis-cli`:\n\n    ./src/redis-cli --tls \\\n        --cert ./tests/tls/redis.crt \\\n        --key ./tests/tls/redis.key \\\n        --cacert ./tests/tls/ca.crt"},{"id":"certificate-configuration","title":"Certificate configuration","role":"content","text":"In order to support TLS, Redis must be configured with a X.509 certificate and a\nprivate key. In addition, it is necessary to specify a CA certificate bundle\nfile or path to be used as a trusted root when validating certificates. To\nsupport DH based ciphers, a DH params file can also be configured. For example:\n\n[code example]"},{"id":"tls-listening-port","title":"TLS listening port","role":"content","text":"The `tls-port` configuration directive enables accepting SSL/TLS connections on\nthe specified port. This is **in addition** to listening on `port` for TCP\nconnections, so it is possible to access Redis on different ports using TLS and\nnon-TLS connections simultaneously.\n\nYou may specify `port 0` to disable the non-TLS port completely. To enable only\nTLS on the default Redis port, use:\n\n[code example]"},{"id":"client-certificate-authentication","title":"Client certificate authentication","role":"content","text":"By default, Redis uses mutual TLS and requires clients to authenticate with a\nvalid certificate (authenticated against trusted root CAs specified by\n`ca-cert-file` or `ca-cert-dir`).\n\nYou may use `tls-auth-clients no` to disable client authentication."},{"id":"replication","title":"Replication","role":"content","text":"A Redis master server handles connecting clients and replica servers in the same\nway, so the above `tls-port` and `tls-auth-clients` directives apply to\nreplication links as well.\n\nOn the replica server side, it is necessary to specify `tls-replication yes` to\nuse TLS for outgoing connections to the master."},{"id":"cluster","title":"Cluster","role":"content","text":"When Redis Cluster is used, use `tls-cluster yes` in order to enable TLS for the\ncluster bus and cross-node connections."},{"id":"sentinel","title":"Sentinel","role":"content","text":"Sentinel inherits its networking configuration from the common Redis\nconfiguration, so all of the above applies to Sentinel as well.\n\nWhen connecting to master servers, Sentinel will use the `tls-replication`\ndirective to determine if a TLS or non-TLS connection is required.\n\nIn addition, the very same `tls-replication` directive will determine whether Sentinel's\nport, that accepts connections from other Sentinels, will support TLS as well. That is,\nSentinel will be configured with `tls-port` if and only if `tls-replication` is enabled."},{"id":"additional-configuration","title":"Additional configuration","role":"content","text":"Additional TLS configuration is available to control the choice of TLS protocol\nversions, ciphers and cipher suites, etc. Please consult the self documented\n`redis.conf` for more information."},{"id":"performance-considerations","title":"Performance considerations","role":"performance","text":"TLS adds a layer to the communication stack with overheads due to writing/reading to/from an SSL connection, encryption/decryption and integrity checks. Consequently, using TLS results in a decrease of the achievable throughput per Redis instance (for more information refer to this [discussion](https://github.com/redis/redis/issues/7595))."},{"id":"limitations","title":"Limitations","role":"limits","text":"Starting with version 8.0, Redis supports I/O threading with TLS."}],"examples":[{"id":"building-ex0","language":"sh","code":"make BUILD_TLS=yes","section_id":"building"},{"id":"certificate-configuration-ex0","language":"plaintext","code":"tls-cert-file /path/to/redis.crt\ntls-key-file /path/to/redis.key\ntls-ca-cert-file /path/to/ca.crt\ntls-dh-params-file /path/to/redis.dh","section_id":"certificate-configuration"},{"id":"tls-listening-port-ex0","language":"plaintext","code":"port 0\ntls-port 6379","section_id":"tls-listening-port"}]}
{"schema_version":2,"id":"operate/oss_and_stack/management/security","title":"Redis security","url":"https://redis.io/docs/latest/operate/oss_and_stack/management/security/","summary":"Security model and features in Redis","aliases":["/management/security/","/manual/security/"],"tags":["docs","operate","stack","oss"],"last_updated":"2026-09-30T13:16:11-07:00","children":[{"id":"operate/oss_and_stack/management/security/acl","summary":"Redis Access Control List","title":"ACL","url":"https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl/"},{"id":"operate/oss_and_stack/management/security/encryption","summary":"Redis TLS support","title":"TLS","url":"https://redis.io/docs/latest/operate/oss_and_stack/management/security/encryption/"}],"page_type":"content","content_hash":"0c72c85d71fb6f623b1782b30dc8c0e5cbbcb033a41bf9ea261cfab157350e5e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This document provides an introduction to the topic of security from the point of\nview of Redis. It covers the access control provided by Redis, code security concerns,\nattacks that can be triggered from the outside by selecting malicious inputs, and\nother similar topics. \nYou can learn more about access control, data protection and encryption, secure Redis architectures, and secure deployment techniques by taking the [Redis University security course](https://university.redis.com/courses/ru330/).\n\nFor security-related contacts, open an issue on GitHub, or when you feel it\nis really important to preserve the security of the communication, use this\n[downloadable GPG key](gpgkey.txt)."},{"id":"security-model","title":"Security model","role":"security","text":"Redis is designed to be accessed by trusted clients inside trusted environments.\nThis means that usually it is not a good idea to expose the Redis instance\ndirectly to the internet or, in general, to an environment where untrusted\nclients can directly access the Redis TCP port or UNIX socket.\n\nFor instance, in the common context of a web application implemented using Redis\nas a database, cache, or messaging system, the clients inside the front-end\n(web side) of the application will query Redis to generate pages or\nto perform operations requested or triggered by the web application user.\n\nIn this case, the web application mediates access between Redis and\nuntrusted clients (the user browsers accessing the web application).\n\nIn general, untrusted access to Redis should\nalways be mediated by a layer implementing ACLs, validating user input,\nand deciding what operations to perform against the Redis instance."},{"id":"network-security","title":"Network security","role":"content","text":"Access to the Redis port should be denied to everybody but trusted clients\nin the network, so the servers running Redis should be directly accessible\nonly by the computers implementing the application using Redis.\n\nIn the common case of a single computer directly exposed to the internet, such\nas a virtualized Linux instance (Linode, EC2, ...), the Redis port should be\nfirewalled to prevent access from the outside. Clients will still be able to\naccess Redis using the loopback interface.\n\nNote that it is possible to bind Redis to a single interface by adding a line\nlike the following to the **redis.conf** file:\n\n    bind 127.0.0.1\n\nFailing to protect the Redis port from the outside can have a big security\nimpact because of the nature of Redis. For instance, a single [`FLUSHALL`](https://redis.io/docs/latest/commands/flushall) command can be used by an external attacker to delete the whole data set."},{"id":"protected-mode","title":"Protected mode","role":"content","text":"Unfortunately, many users fail to protect Redis instances from being accessed\nfrom external networks. Many instances are simply left exposed on the\ninternet with public IPs. Since version 3.2.0, Redis enters a special mode called **protected mode** when it is\nexecuted with the default configuration (binding all the interfaces) and\nwithout any password in order to access it. In this mode, Redis only replies to queries from the\nloopback interfaces, and replies to clients connecting from other\naddresses with an error that explains the problem and how to configure\nRedis properly.\n\nWe expect protected mode to seriously decrease the security issues caused\nby unprotected Redis instances executed without proper administration. However,\nthe system administrator can still ignore the error given by Redis and\ndisable protected mode or manually bind all the interfaces."},{"id":"authentication","title":"Authentication","role":"security","text":"Redis provides two ways to authenticate clients.\nThe recommended authentication method, introduced in Redis 6, is via Access Control Lists, allowing named users to be created and assigned fine-grained permissions.\nRead more about Access Control Lists [here](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl).\n\nThe legacy authentication method is enabled by editing the **redis.conf** file, and providing a database password using the `requirepass` setting.\nThis password is then used by all clients.\n\nWhen the `requirepass` setting is enabled, Redis will refuse any query by\nunauthenticated clients. A client can authenticate itself by sending the\n**AUTH** command followed by the password.\n\nThe password is set by the system administrator in clear text inside the\nredis.conf file. It should be long enough to prevent brute force attacks\nfor two reasons:\n\n* Redis is very fast at serving queries. Many passwords per second can be tested by an external client.\n* The Redis password is stored in the **redis.conf** file and inside the client configuration. Since the system administrator does not need to remember it, the password can be very long.\n\nThe goal of the authentication layer is to optionally provide a layer of\nredundancy. If firewalling or any other system implemented to protect Redis\nfrom external attackers fails, an external client will still not be able to\naccess the Redis instance without knowledge of the authentication password.\n\nSince the [`AUTH`](https://redis.io/docs/latest/commands/auth) command, like every other Redis command, is sent unencrypted, it\ndoes not protect against an attacker who has enough access to the network to\nperform eavesdropping."},{"id":"tls-support","title":"TLS support","role":"content","text":"Redis has optional support for TLS on all communication channels, including\nclient connections, replication links, and the Redis Cluster bus protocol."},{"id":"disallowing-specific-commands","title":"Disallowing specific commands","role":"content","text":"> [!WARNING]\n> The method is deprecated and may be removed in future versions.\n> Instead, use [ACL rules](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl) to disallow specific commands.\n\nIt is possible to disallow commands in Redis or to rename them as an unguessable\nname, so that normal clients are limited to a specified set of commands.\n\nFor instance, a virtualized server provider may offer a managed Redis instance\nservice. In this context, normal users should probably not be able to\ncall the Redis **CONFIG** command to alter the configuration of the instance,\nbut the systems that provide and remove instances should be able to do so.\n\nIn this case, it is possible to either rename or completely shadow commands from\nthe command table. This feature is available as a statement that can be used\ninside the redis.conf configuration file. For example:\n\n    rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52\n\nIn the above example, the **CONFIG** command was renamed into an unguessable name.  It is also possible to completely disallow it (or any other command) by renaming it to the empty string, like in the following example:\n\n    rename-command CONFIG \"\""},{"id":"attacks-triggered-by-malicious-inputs-from-external-clients","title":"Attacks triggered by malicious inputs from external clients","role":"content","text":"There is a class of attacks that an attacker can trigger from the outside even\nwithout external access to the instance. For example, an attacker might insert data into Redis that triggers pathological (worst case)\nalgorithm complexity on data structures implemented inside Redis internals.\n\nAn attacker could supply, via a web form, a set of strings that\nare known to hash to the same bucket in a hash table in order to turn the\nO(1) expected time (the average time) to the O(N) worst case. This can consume more\nCPU than expected and ultimately causes a Denial of Service.\n\nTo prevent this specific attack, Redis uses a per-execution, pseudo-random\nseed to the hash function.\n\nRedis implements the SORT command using the qsort algorithm. Currently,\nthe algorithm is not randomized, so it is possible to trigger a quadratic\nworst-case behavior by carefully selecting the right set of inputs."},{"id":"string-escaping-and-nosql-injection","title":"String escaping and NoSQL injection","role":"content","text":"The Redis protocol has no concept of string escaping, so injection\nis impossible under normal circumstances using a normal client library.\nThe protocol uses prefixed-length strings and is completely binary safe.\n\nSince Lua scripts executed by the [`EVAL`](https://redis.io/docs/latest/commands/eval) and [`EVALSHA`](https://redis.io/docs/latest/commands/evalsha) commands follow the\nsame rules, those commands are also safe.\n\nWhile it would be a strange use case, the application should avoid composing the body of the Lua script from strings obtained from untrusted sources."},{"id":"code-security","title":"Code security","role":"content","text":"In a classical Redis setup, clients are allowed full access to the command set,\nbut accessing the instance should never result in the ability to control the\nsystem where Redis is running.\n\nInternally, Redis uses all the well-known practices for writing secure code to\nprevent buffer overflows, format bugs, and other memory corruption issues.\nHowever, the ability to control the server configuration using the **CONFIG**\ncommand allows the client to change the working directory of the program and\nthe name of the dump file. This allows clients to write RDB Redis files\nto random paths. This is [a security issue](http://antirez.com/news/96) that may lead to the ability to compromise the system and/or run untrusted code as the same user as Redis is running.\n\nRedis does not require root privileges to run. It is recommended to\nrun it as an unprivileged *redis* user that is only used for this purpose."},{"id":"other-security-topics","title":"Other security topics","role":"content","text":"The other pages in this section cover security topics in more detail."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/management/sentinel","title":"High availability with Redis Sentinel","url":"https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel/","summary":"High availability for non-clustered Redis","aliases":["/management/sentinel/","/manual/sentinel/"],"tags":["docs","operate","stack","oss"],"last_updated":"2026-10-13T16:38:58+03:00","page_type":"content","content_hash":"4648fec915414c1774ca81f807b9db6559968a71167e3c9f2a3f5c332c0d0326","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Sentinel provides high availability for Redis when not using [Redis Cluster](https://redis.io/docs/latest/operate/oss_and_stack/management/scaling). \n\nRedis Sentinel also provides other collateral tasks such as monitoring,\nnotifications and acts as a configuration provider for clients.\n\nThis is the full list of Sentinel capabilities at a macroscopic level (i.e. the *big picture*):\n\n* **Monitoring**. Sentinel constantly checks if your master and replica instances are working as expected.\n* **Notification**. Sentinel can notify the system administrator, or other computer programs, via an API, that something is wrong with one of the monitored Redis instances.\n* **Automatic failover**. If a master is not working as expected, Sentinel can start a failover process where a replica is promoted to master, the other additional replicas are reconfigured to use the new master, and the applications using the Redis server are informed about the new address to use when connecting.\n* **Configuration provider**. Sentinel acts as a source of authority for clients service discovery: clients connect to Sentinels in order to ask for the address of the current Redis master responsible for a given service. If a failover occurs, Sentinels will report the new address."},{"id":"sentinel-as-a-distributed-system","title":"Sentinel as a distributed system","role":"content","text":"Redis Sentinel is a distributed system:\n\nSentinel itself is designed to run in a configuration where there are multiple Sentinel processes cooperating together. The advantage of having multiple Sentinel processes cooperating are the following:\n\n1. Failure detection is performed when multiple Sentinels agree about the fact a given master is no longer available. This lowers the probability of false positives.\n2. Sentinel works even if not all the Sentinel processes are working, making the system robust against failures. There is no fun in having a failover system which is itself a single point of failure, after all.\n\nThe sum of Sentinels, Redis instances (masters and replicas) and clients\nconnecting to Sentinel and Redis, are also a larger distributed system with\nspecific properties. In this document concepts will be introduced gradually\nstarting from basic information needed in order to understand the basic\nproperties of Sentinel, to more complex information (that are optional) in\norder to understand how exactly Sentinel works."},{"id":"sentinel-quick-start","title":"Sentinel quick start","role":"content","text":""},{"id":"obtaining-sentinel","title":"Obtaining Sentinel","role":"content","text":"The current version of Sentinel is called **Sentinel 2**. It is a rewrite of\nthe initial Sentinel implementation using stronger and simpler-to-predict\nalgorithms (that are explained in this documentation).\n\nA stable release of Redis Sentinel is shipped since Redis 2.8.\n\nNew developments are performed in the *unstable* branch, and new features\nsometimes are back ported into the latest stable branch as soon as they are\nconsidered to be stable.\n\nRedis Sentinel version 1, shipped with Redis 2.6, is deprecated and should not be used."},{"id":"running-sentinel","title":"Running Sentinel","role":"content","text":"If you are using the `redis-sentinel` executable (or if you have a symbolic\nlink with that name to the `redis-server` executable) you can run Sentinel\nwith the following command line:\n\n    redis-sentinel /path/to/sentinel.conf\n\nOtherwise you can use directly the `redis-server` executable starting it in\nSentinel mode:\n\n    redis-server /path/to/sentinel.conf --sentinel\n\nBoth ways work the same.\n\nHowever **it is mandatory** to use a configuration file when running Sentinel, as this file will be used by the system in order to save the current state that will be reloaded in case of restarts. Sentinel will simply refuse to start if no configuration file is given or if the configuration file path is not writable.\n\nSentinels by default run **listening for connections to TCP port 26379**, so\nfor Sentinels to work, port 26379 of your servers **must be open** to receive\nconnections from the IP addresses of the other Sentinel instances.\nOtherwise Sentinels can't talk and can't agree about what to do, so failover\nwill never be performed."},{"id":"fundamental-things-to-know-about-sentinel-before-deploying","title":"Fundamental things to know about Sentinel before deploying","role":"content","text":"1. You need at least three Sentinel instances for a robust deployment.\n2. The three Sentinel instances should be placed into computers or virtual machines that are believed to fail in an independent way. So for example different physical servers or Virtual Machines executed on different availability zones.\n3. Sentinel + Redis distributed system does not guarantee that acknowledged writes are retained during failures, since Redis uses asynchronous replication. However there are ways to deploy Sentinel that make the window to lose writes limited to certain moments, while there are other less secure ways to deploy it.\n4. You need Sentinel support in your clients. Popular client libraries have Sentinel support, but not all.\n5. There is no HA setup which is safe if you don't test from time to time in development environments, or even better if you can, in production environments, if they work. You may have a misconfiguration that will become apparent only when it's too late (at 3am when your master stops working).\n6. **Sentinel, Docker, or other forms of Network Address Translation or Port Mapping should be mixed with care**: Docker performs port remapping, breaking Sentinel auto discovery of other Sentinel processes and the list of replicas for a master. Check the [section about _Sentinel and Docker_](#sentinel-docker-nat-and-possible-issues) later in this document for more information."},{"id":"configuring-sentinel","title":"Configuring Sentinel","role":"configuration","text":"The Redis source distribution contains a file called `sentinel.conf`\nthat is a self-documented example configuration file you can use to\nconfigure Sentinel, however a typical minimal configuration file looks like the\nfollowing:\n\n    sentinel monitor mymaster 127.0.0.1 6379 2\n    sentinel down-after-milliseconds mymaster 60000\n    sentinel failover-timeout mymaster 180000\n    sentinel parallel-syncs mymaster 1\n\n    sentinel monitor resque 192.168.1.3 6380 4\n    sentinel down-after-milliseconds resque 10000\n    sentinel failover-timeout resque 180000\n    sentinel parallel-syncs resque 5\n\nYou only need to specify the masters to monitor, giving to each separated\nmaster (that may have any number of replicas) a different name. There is no\nneed to specify replicas, which are auto-discovered. Sentinel will update the\nconfiguration automatically with additional information about replicas (in\norder to retain the information in case of restart). The configuration is\nalso rewritten every time a replica is promoted to master during a failover\nand every time a new Sentinel is discovered.\n\nThe example configuration above basically monitors two sets of Redis\ninstances, each composed of a master and an undefined number of replicas.\nOne set of instances is called `mymaster`, and the other `resque`.\n\nThe meaning of the arguments of `sentinel monitor` statements is the following:\n\n    sentinel monitor <master-name> <ip> <port> <quorum>\n\nFor the sake of clarity, let's check line by line what the configuration\noptions mean:\n\nThe first line is used to tell Redis to monitor a master called *mymaster*,\nthat is at address 127.0.0.1 and port 6379, with a quorum of 2. Everything\nis pretty obvious but the **quorum** argument:\n\n* The **quorum** is the number of Sentinels that need to agree about the fact the master is not reachable, in order to really mark the master as failing, and eventually start a failover procedure if possible.\n* However **the quorum is only used to detect the failure**. In order to actually perform a failover, one of the Sentinels need to be elected leader for the failover and be authorized to proceed. This only happens with the vote of the **majority of the Sentinel processes**.\n\nSo for example if you have 5 Sentinel processes, and the quorum for a given\nmaster set to the value of 2, this is what happens:\n\n* If two Sentinels agree at the same time about the master being unreachable, one of the two will try to start a failover.\n* If there are at least a total of three Sentinels reachable, the failover will be authorized and will actually start.\n\nIn practical terms this means during failures **Sentinel never starts a failover if the majority of Sentinel processes are unable to talk** (aka no failover in the minority partition)."},{"id":"other-sentinel-options","title":"Other Sentinel options","role":"content","text":"The other options are almost always in the form:\n\n    sentinel <option_name> <master_name> <option_value>\n\nAnd are used for the following purposes:\n\n* `down-after-milliseconds` is the time in milliseconds an instance should not\nbe reachable (either does not reply to our PINGs or it is replying with an\nerror) for a Sentinel starting to think it is down.\n* `parallel-syncs` sets the number of replicas that can be reconfigured to use\nthe new master after a failover at the same time. The lower the number, the\nmore time it will take for the failover process to complete, however if the\nreplicas are configured to serve old data, you may not want all the replicas to\nre-synchronize with the master at the same time. While the replication\nprocess is mostly non blocking for a replica, there is a moment when it stops to\nload the bulk data from the master. You may want to make sure only one replica\nat a time is not reachable by setting this option to the value of 1.\n\nAdditional options are described in the rest of this document and\ndocumented in the example `sentinel.conf` file shipped with the Redis\ndistribution.\n\nConfiguration parameters can be modified at runtime:\n\n* Master-specific configuration parameters are modified using `SENTINEL SET`.\n* Global configuration parameters are modified using `SENTINEL CONFIG SET`.\n\nSee the [_Reconfiguring Sentinel at runtime_ section](#reconfiguring-sentinel-at-runtime) for more information."},{"id":"example-sentinel-deployments","title":"Example Sentinel deployments","role":"example","text":"Now that you know the basic information about Sentinel, you may wonder where\nyou should place your Sentinel processes, how many Sentinel processes you need\nand so forth. This section shows a few example deployments.\n\nWe use ASCII art in order to show you configuration examples in a *graphical*\nformat, this is what the different symbols means:\n\n    +--------------------+\n    | This is a computer |\n    | or VM that fails   |\n    | independently. We  |\n    | call it a \"box\"    |\n    +--------------------+\n\nWe write inside the boxes what they are running:\n\n    +-------------------+\n    | Redis master M1   |\n    | Redis Sentinel S1 |\n    +-------------------+\n\nDifferent boxes are connected by lines, to show that they are able to talk:\n\n    +-------------+               +-------------+\n    | Sentinel S1 |---------------| Sentinel S2 |\n    +-------------+               +-------------+\n\nNetwork partitions are shown as interrupted lines using slashes:\n\n    +-------------+                +-------------+\n    | Sentinel S1 |------ // ------| Sentinel S2 |\n    +-------------+                +-------------+\n\nAlso note that:\n\n* Masters are called M1, M2, M3, ..., Mn.\n* Replicas are called R1, R2, R3, ..., Rn (R stands for *replica*).\n* Sentinels are called S1, S2, S3, ..., Sn.\n* Clients are called C1, C2, C3, ..., Cn.\n* When an instance changes role because of Sentinel actions, we put it inside square brackets, so [M1] means an instance that is now a master because of Sentinel intervention.\n\nNote that we will never show **setups where just two Sentinels are used**, since\nSentinels always need **to talk with the majority** in order to start a\nfailover.\n\n#### Example 1: just two Sentinels, DON'T DO THIS\n\n    +----+         +----+\n    | M1 |---------| R1 |\n    | S1 |         | S2 |\n    +----+         +----+\n\n    Configuration: quorum = 1\n\n* In this setup, if the master M1 fails, R1 will be promoted since the two Sentinels can reach agreement about the failure (obviously with quorum set to 1) and can also authorize a failover because the majority is two. So apparently it could superficially work, however check the next points to see why this setup is broken.\n* If the box where M1 is running stops working, also S1 stops working. The Sentinel running in the other box S2 will not be able to authorize a failover, so the system will become not available.\n\nNote that a majority is needed in order to order different failovers, and later propagate the latest configuration to all the Sentinels. Also note that the ability to failover in a single side of the above setup, without any agreement, would be very dangerous:\n\n    +----+           +------+\n    | M1 |----//-----| [M1] |\n    | S1 |           | S2   |\n    +----+           +------+\n\nIn the above configuration we created two masters (assuming S2 could failover\nwithout authorization) in a perfectly symmetrical way. Clients may write\nindefinitely to both sides, and there is no way to understand when the\npartition heals what configuration is the right one, in order to prevent\na *permanent split brain condition*.\n\nSo please **deploy at least three Sentinels in three different boxes** always.\n\n#### Example 2: basic setup with three boxes\n\nThis is a very simple setup, that has the advantage to be simple to tune\nfor additional safety. It is based on three boxes, each box running both\na Redis process and a Sentinel process.\n\n\n           +----+\n           | M1 |\n           | S1 |\n           +----+\n              |\n    +----+    |    +----+\n    | R2 |----+----| R3 |\n    | S2 |         | S3 |\n    +----+         +----+\n\n    Configuration: quorum = 2\n\nIf the master M1 fails, S2 and S3 will agree about the failure and will\nbe able to authorize a failover, making clients able to continue.\n\nIn every Sentinel setup, as Redis uses asynchronous replication, there is\nalways the risk of losing some writes because a given acknowledged write\nmay not be able to reach the replica which is promoted to master. However in\nthe above setup there is a higher risk due to clients being partitioned away\nwith an old master, like in the following picture:\n\n             +----+\n             | M1 |\n             | S1 | <- C1 (writes will be lost)\n             +----+\n                |\n                /\n                /\n    +------+    |    +----+\n    | [M2] |----+----| R3 |\n    | S2   |         | S3 |\n    +------+         +----+\n\nIn this case a network partition isolated the old master M1, so the\nreplica R2 is promoted to master. However clients, like C1, that are\nin the same partition as the old master, may continue to write data\nto the old master. This data will be lost forever since when the partition\nwill heal, the master will be reconfigured as a replica of the new master,\ndiscarding its data set.\n\nThis problem can be mitigated using the following Redis replication\nfeature, that allows to stop accepting writes if a master detects that\nit is no longer able to transfer its writes to the specified number of replicas.\n\n    min-replicas-to-write 1\n    min-replicas-max-lag 10\n\nWith the above configuration (please see the self-commented `redis.conf` example in the Redis distribution for more information) a Redis instance, when acting as a master, will stop accepting writes if it can't write to at least 1 replica. Since replication is asynchronous *not being able to write* actually means that the replica is either disconnected, or is not sending us asynchronous acknowledges for more than the specified `max-lag` number of seconds.\n\nUsing this configuration, the old Redis master M1 in the above example, will become unavailable after 10 seconds. When the partition heals, the Sentinel configuration will converge to the new one, the client C1 will be able to fetch a valid configuration and will continue with the new master.\n\nHowever there is no free lunch. With this refinement, if the two replicas are\ndown, the master will stop accepting writes. It's a trade off.\n\n#### Example 3: Sentinel in the client boxes\n\nSometimes we have only two Redis boxes available, one for the master and\none for the replica. The configuration in the example 2 is not viable in\nthat case, so we can resort to the following, where Sentinels are placed\nwhere clients are:\n\n                +----+         +----+\n                | M1 |----+----| R1 |\n                |    |    |    |    |\n                +----+    |    +----+\n                          |\n             +------------+------------+\n             |            |            |\n             |            |            |\n          +----+        +----+      +----+\n          | C1 |        | C2 |      | C3 |\n          | S1 |        | S2 |      | S3 |\n          +----+        +----+      +----+\n\n          Configuration: quorum = 2\n\nIn this setup, the point of view Sentinels is the same as the clients: if\na master is reachable by the majority of the clients, it is fine.\nC1, C2, C3 here are generic clients, it does not mean that C1 identifies\na single client connected to Redis. It is more likely something like\nan application server, a Rails app, or something like that.\n\nIf the box where M1 and S1 are running fails, the failover will happen\nwithout issues, however it is easy to see that different network partitions\nwill result in different behaviors. For example Sentinel will not be able\nto setup if the network between the clients and the Redis servers is\ndisconnected, since the Redis master and replica will both be unavailable.\n\nNote that if C3 gets partitioned with M1 (hardly possible with\nthe network described above, but more likely possible with different\nlayouts, or because of failures at the software layer), we have a similar\nissue as described in Example 2, with the difference that here we have\nno way to break the symmetry, since there is just a replica and master, so\nthe master can't stop accepting queries when it is disconnected from its replica,\notherwise the master would never be available during replica failures.\n\nSo this is a valid setup but the setup in the Example 2 has advantages\nsuch as the HA system of Redis running in the same boxes as Redis itself\nwhich may be simpler to manage, and the ability to put a bound on the amount\nof time a master in the minority partition can receive writes.\n\n#### Example 4: Sentinel client side with less than three clients\n\nThe setup described in the Example 3 cannot be used if there are less than\nthree boxes in the client side (for example three web servers). In this\ncase we need to resort to a mixed setup like the following:\n\n                +----+         +----+\n                | M1 |----+----| R1 |\n                | S1 |    |    | S2 |\n                +----+    |    +----+\n                          |\n                   +------+-----+\n                   |            |\n                   |            |\n                +----+        +----+\n                | C1 |        | C2 |\n                | S3 |        | S4 |\n                +----+        +----+\n\n          Configuration: quorum = 3\n\nThis is similar to the setup in Example 3, but here we run four Sentinels\nin the four boxes we have available. If the master M1 becomes unavailable\nthe other three Sentinels will perform the failover.\n\nIn theory this setup works removing the box where C2 and S4 are running, and\nsetting the quorum to 2. However it is unlikely that we want HA in the\nRedis side without having high availability in our application layer."},{"id":"sentinel-docker-nat-and-possible-issues","title":"Sentinel, Docker, NAT, and possible issues","role":"content","text":"Docker uses a technique called port mapping: programs running inside Docker\ncontainers may be exposed with a different port compared to the one the\nprogram believes to be using. This is useful in order to run multiple\ncontainers using the same ports, at the same time, in the same server.\n\nDocker is not the only software system where this happens, there are other\nNetwork Address Translation setups where ports may be remapped, and sometimes\nnot ports but also IP addresses.\n\nRemapping ports and addresses creates issues with Sentinel in two ways:\n\n1. Sentinel auto-discovery of other Sentinels no longer works, since it is based on *hello* messages where each Sentinel announce at which port and IP address they are listening for connection. However Sentinels have no way to understand that an address or port is remapped, so it is announcing an information that is not correct for other Sentinels to connect.\n2. Replicas are listed in the [`INFO`](https://redis.io/docs/latest/commands/info) output of a Redis master in a similar way: the address is detected by the master checking the remote peer of the TCP connection, while the port is advertised by the replica itself during the handshake, however the port may be wrong for the same reason as exposed in point 1.\n\nSince Sentinels auto detect replicas using masters [`INFO`](https://redis.io/docs/latest/commands/info) output information,\nthe detected replicas will not be reachable, and Sentinel will never be able to\nfailover the master, since there are no good replicas from the point of view of\nthe system, so there is currently no way to monitor with Sentinel a set of\nmaster and replica instances deployed with Docker, **unless you instruct Docker\nto map the port 1:1**.\n\nFor the first problem, in case you want to run a set of Sentinel\ninstances using Docker with forwarded ports (or any other NAT setup where ports\nare remapped), you can use the following two Sentinel configuration directives\nin order to force Sentinel to announce a specific set of IP and port:\n\n    sentinel announce-ip <ip>\n    sentinel announce-port <port>\n\nNote that Docker has the ability to run in *host networking mode* (check the `--net=host` option for more information). This should create no issues since ports are not remapped in this setup."},{"id":"ip-addresses-and-dns-names","title":"IP Addresses and DNS names","role":"content","text":"Older versions of Sentinel did not support host names and required IP addresses to be specified everywhere.\nStarting with version 6.2, Sentinel has *optional* support for host names.\n\n**This capability is disabled by default. If you're going to enable DNS/hostnames support, please note:**\n\n1. The name resolution configuration on your Redis and Sentinel nodes must be reliable and be able to resolve addresses quickly. Unexpected delays in address resolution may have a negative impact on Sentinel.\n2. You should use hostnames everywhere and avoid mixing hostnames and IP addresses. To do that, use `replica-announce-ip <hostname>` and `sentinel announce-ip <hostname>` for all Redis and Sentinel instances, respectively.\n\nEnabling the `resolve-hostnames` global configuration allows Sentinel to accept host names:\n\n* As part of a `sentinel monitor` command\n* As a replica address, if the replica uses a host name value for `replica-announce-ip`\n\nSentinel will accept host names as valid inputs and resolve them, but will still refer to IP addresses when announcing an instance, updating configuration files, etc.\n\nEnabling the `announce-hostnames` global configuration makes Sentinel use host names instead. This affects replies to clients, values written in configuration files, the [`REPLICAOF`](https://redis.io/docs/latest/commands/replicaof) command issued to replicas, etc.\n\nThis behavior may not be compatible with all Sentinel clients, that may explicitly expect an IP address.\n\nUsing host names may be useful when clients use TLS to connect to instances and require a name rather than an IP address in order to perform certificate ASN matching."},{"id":"a-quick-tutorial","title":"A quick tutorial","role":"content","text":"In the next sections of this document, all the details about [_Sentinel API_](#sentinel-api),\nconfiguration and semantics will be covered incrementally. However for people\nthat want to play with the system ASAP, this section is a tutorial that shows\nhow to configure and interact with 3 Sentinel instances.\n\nHere we assume that the instances are executed at port 5000, 5001, 5002.\nWe also assume that you have a running Redis master at port 6379 with a\nreplica running at port 6380. We will use the IPv4 loopback address 127.0.0.1\neverywhere during the tutorial, assuming you are running the simulation\non your personal computer.\n\nThe three Sentinel configuration files should look like the following:\n\n    port 5000\n    sentinel monitor mymaster 127.0.0.1 6379 2\n    sentinel down-after-milliseconds mymaster 5000\n    sentinel failover-timeout mymaster 60000\n    sentinel parallel-syncs mymaster 1\n\nThe other two configuration files will be identical but using 5001 and 5002\nas port numbers.\n\nA few things to note about the above configuration:\n\n* The master set is called `mymaster`. It identifies the master and its replicas. Since each *master set* has a different name, Sentinel can monitor different sets of masters and replicas at the same time.\n* The quorum was set to the value of 2 (last argument of `sentinel monitor` configuration directive).\n* The `down-after-milliseconds` value is 5000 milliseconds, that is 5 seconds, so masters will be detected as failing as soon as we don't receive any reply from our pings within this amount of time.\n\nOnce you start the three Sentinels, you'll see a few messages they log, like:\n\n    +monitor master mymaster 127.0.0.1 6379 quorum 2\n\nThis is a Sentinel event, and you can receive this kind of events via Pub/Sub\nif you [`SUBSCRIBE`](https://redis.io/docs/latest/commands/subscribe) to the event name as specified later in [_Pub/Sub Messages_ section](#pubsub-messages).\n\nSentinel generates and logs different events during failure detection and\nfailover.\n\nAsking Sentinel about the state of a master\n---\n\nThe most obvious thing to do with Sentinel to get started, is check if the\nmaster it is monitoring is doing well:\n\n    $ redis-cli -p 5000\n    127.0.0.1:5000> sentinel master mymaster\n     1) \"name\"\n     2) \"mymaster\"\n     3) \"ip\"\n     4) \"127.0.0.1\"\n     5) \"port\"\n     6) \"6379\"\n     7) \"runid\"\n     8) \"953ae6a589449c13ddefaee3538d356d287f509b\"\n     9) \"flags\"\n    10) \"master\"\n    11) \"link-pending-commands\"\n    12) \"0\"\n    13) \"link-refcount\"\n    14) \"1\"\n    15) \"last-ping-sent\"\n    16) \"0\"\n    17) \"last-ok-ping-reply\"\n    18) \"735\"\n    19) \"last-ping-reply\"\n    20) \"735\"\n    21) \"down-after-milliseconds\"\n    22) \"5000\"\n    23) \"info-refresh\"\n    24) \"126\"\n    25) \"role-reported\"\n    26) \"master\"\n    27) \"role-reported-time\"\n    28) \"532439\"\n    29) \"config-epoch\"\n    30) \"1\"\n    31) \"num-slaves\"\n    32) \"1\"\n    33) \"num-other-sentinels\"\n    34) \"2\"\n    35) \"quorum\"\n    36) \"2\"\n    37) \"failover-timeout\"\n    38) \"60000\"\n    39) \"parallel-syncs\"\n    40) \"1\"\n\nAs you can see, it prints a number of information about the master. There are\na few that are of particular interest for us:\n\n1. `num-other-sentinels` is 2, so we know the Sentinel already detected two more Sentinels for this master. If you check the logs you'll see the `+sentinel` events generated.\n2. `flags` is just `master`. If the master was down we could expect to see `s_down` or `o_down` flag as well here.\n3. `num-slaves` is correctly set to 1, so Sentinel also detected that there is an attached replica to our master.\n\nIn order to explore more about this instance, you may want to try the following\ntwo commands:\n\n    SENTINEL replicas mymaster\n    SENTINEL sentinels mymaster\n\nThe first will provide similar information about the replicas connected to the\nmaster, and the second about the other Sentinels.\n\nObtaining the address of the current master\n---\n\nAs we already specified, Sentinel also acts as a configuration provider for\nclients that want to connect to a set of master and replicas. Because of\npossible failovers or reconfigurations, clients have no idea about who is\nthe currently active master for a given set of instances, so Sentinel exports\nan API to ask this question:\n\n    127.0.0.1:5000> SENTINEL get-master-addr-by-name mymaster\n    1) \"127.0.0.1\"\n    2) \"6379\""},{"id":"testing-the-failover","title":"Testing the failover","role":"content","text":"At this point our toy Sentinel deployment is ready to be tested. We can\njust kill our master and check if the configuration changes. To do so\nwe can just do:\n\n    redis-cli -p 6379 DEBUG sleep 30\n\nThis command will make our master no longer reachable, sleeping for 30 seconds.\nIt basically simulates a master hanging for some reason.\n\nIf you check the Sentinel logs, you should be able to see a lot of action:\n\n1. Each Sentinel detects the master is down with an `+sdown` event.\n2. This event is later escalated to `+odown`, which means that multiple Sentinels agree about the fact the master is not reachable.\n3. Sentinels vote a Sentinel that will start the first failover attempt.\n4. The failover happens.\n\nIf you ask again what is the current master address for `mymaster`, eventually\nwe should get a different reply this time:\n\n    127.0.0.1:5000> SENTINEL get-master-addr-by-name mymaster\n    1) \"127.0.0.1\"\n    2) \"6380\"\n\nSo far so good... At this point you may jump to create your Sentinel deployment\nor can read more to understand all the Sentinel commands and internals."},{"id":"sentinel-api","title":"Sentinel API","role":"content","text":"Sentinel provides an API in order to inspect its state, check the health\nof monitored masters and replicas, subscribe in order to receive specific\nnotifications, and change the Sentinel configuration at run time.\n\nBy default Sentinel runs using TCP port 26379 (note that 6379 is the normal\nRedis port). Sentinels accept commands using the Redis protocol, so you can\nuse `redis-cli` or any other unmodified Redis client in order to talk with\nSentinel.\n\nIt is possible to directly query a Sentinel to check what is the state of\nthe monitored Redis instances from its point of view, to see what other\nSentinels it knows, and so forth. Alternatively, using Pub/Sub, it is possible\nto receive *push style* notifications from Sentinels, every time some event\nhappens, like a failover, or an instance entering an error condition, and\nso forth."},{"id":"sentinel-commands","title":"Sentinel commands","role":"content","text":"The `SENTINEL` command is the main API for Sentinel. The following is the list of its subcommands (minimal version is noted for where applicable):\n\n* **SENTINEL CONFIG GET `<name>`** (`>= 6.2`) Get the current value of a global Sentinel configuration parameter. The specified name may be a wildcard, similar to the Redis [`CONFIG GET`](https://redis.io/docs/latest/commands/config-get) command.\n* **SENTINEL CONFIG SET `<name>` `<value>`** (`>= 6.2`) Set the value of a global Sentinel configuration parameter.\n* **SENTINEL CKQUORUM `<master name>`** Check if the current Sentinel configuration is able to reach the quorum needed to failover a master, and the majority needed to authorize the failover. This command should be used in monitoring systems to check if a Sentinel deployment is ok.\n* **SENTINEL FLUSHCONFIG** Force Sentinel to rewrite its configuration on disk, including the current Sentinel state. Normally Sentinel rewrites the configuration every time something changes in its state (in the context of the subset of the state which is persisted on disk across restart). However sometimes it is possible that the configuration file is lost because of operation errors, disk failures, package upgrade scripts or configuration managers. In those cases a way to force Sentinel to rewrite the configuration file is handy. This command works even if the previous configuration file is completely missing.\n* **SENTINEL FAILOVER `<master name>`** Force a failover as if the master was not reachable, and without asking for agreement to other Sentinels (however a new version of the configuration will be published so that the other Sentinels will update their configurations).\n* **SENTINEL GET-MASTER-ADDR-BY-NAME `<master name>`** Return the ip and port number of the master with that name. If a failover is in progress or terminated successfully for this master it returns the address and port of the promoted replica.\n* **SENTINEL INFO-CACHE** (`>= 3.2`) Return cached [`INFO`](https://redis.io/docs/latest/commands/info) output from masters and replicas.\n* **SENTINEL IS-MASTER-DOWN-BY-ADDR <ip> <port> <current-epoch> <runid>** Check if the master specified by ip:port is down from current Sentinel's point of view. This command is mostly for internal use.\n* **SENTINEL MASTER `<master name>`** Show the state and info of the specified master.\n* **SENTINEL MASTERS** Show a list of monitored masters and their state.\n* **SENTINEL MONITOR** Start Sentinel's monitoring. Refer to the [_Reconfiguring Sentinel at Runtime_ section](#reconfiguring-sentinel-at-runtime) for more information.\n* **SENTINEL MYID** (`>= 6.2`) Return the ID of the Sentinel instance.\n* **SENTINEL PENDING-SCRIPTS** This command returns information about pending scripts.\n* **SENTINEL REMOVE** Stop Sentinel's monitoring. Refer to the [_Reconfiguring Sentinel at Runtime_ section](#reconfiguring-sentinel-at-runtime) for more information.\n* **SENTINEL REPLICAS `<master name>`** (`>= 5.0`) Show a list of replicas for this master, and their state.\n* **SENTINEL SENTINELS `<master name>`** Show a list of sentinel instances for this master, and their state.\n* **SENTINEL SET** Set Sentinel's monitoring configuration. Refer to the [_Reconfiguring Sentinel at Runtime_ section](#reconfiguring-sentinel-at-runtime) for more information.\n* **SENTINEL SIMULATE-FAILURE (crash-after-election|crash-after-promotion|help)** (`>= 3.2`) This command simulates different Sentinel crash scenarios.\n* **SENTINEL RESET `<pattern>`** This command will reset all the masters with matching name. The pattern argument is a glob-style pattern. The reset process clears any previous state in a master (including a failover in progress), and removes every replica and sentinel already discovered and associated with the master.\n\nFor connection management and administration purposes, Sentinel supports the following subset of Redis' commands:\n\n* **ACL** (`>= 6.2`) This command manages the Sentinel Access Control List. For more information refer to the [ACL](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl) documentation page and the [_Sentinel Access Control List authentication_](#sentinel-access-control-list-authentication).\n* **AUTH** (`>= 5.0.1`) Authenticate a client connection. For more information refer to the [`AUTH`](https://redis.io/docs/latest/commands/auth) command and the [_Configuring Sentinel instances with authentication_ section](#configuring-sentinel-instances-with-authentication).\n* **CLIENT** This command manages client connections. For more information refer to its subcommands' pages.\n* **COMMAND** (`>= 6.2`) This command returns information about commands. For more information refer to the [`COMMAND`](https://redis.io/docs/latest/commands/command) command and its various subcommands.\n* **HELLO** (`>= 6.0`) Switch the connection's protocol. For more information refer to the [`HELLO`](https://redis.io/docs/latest/commands/hello) command.\n* **INFO** Return information and statistics about the Sentinel server. For more information see the [`INFO`](https://redis.io/docs/latest/commands/info) command.\n* **PING** This command simply returns PONG.\n* **ROLE** This command returns the string \"sentinel\" and a list of monitored masters. For more information refer to the [`ROLE`](https://redis.io/docs/latest/commands/role) command.\n* **SHUTDOWN** Shut down the Sentinel instance.\n\nLastly, Sentinel also supports the [`SUBSCRIBE`](https://redis.io/docs/latest/commands/subscribe), [`UNSUBSCRIBE`](https://redis.io/docs/latest/commands/unsubscribe), [`PSUBSCRIBE`](https://redis.io/docs/latest/commands/psubscribe) and [`PUNSUBSCRIBE`](https://redis.io/docs/latest/commands/punsubscribe) commands. Refer to the [_Pub/Sub Messages_ section](#pubsub-messages) for more details."},{"id":"reconfiguring-sentinel-at-runtime","title":"Reconfiguring Sentinel at Runtime","role":"content","text":"Starting with Redis version 2.8.4, Sentinel provides an API in order to add, remove, or change the configuration of a given master. Note that if you have multiple sentinels you should apply the changes to all to your instances for Redis Sentinel to work properly. This means that changing the configuration of a single Sentinel does not automatically propagate the changes to the other Sentinels in the network.\n\nThe following is a list of `SENTINEL` subcommands used in order to update the configuration of a Sentinel instance.\n\n* **SENTINEL MONITOR `<name>` `<ip>` `<port>` `<quorum>`** This command tells the Sentinel to start monitoring a new master with the specified name, ip, port, and quorum. It is identical to the `sentinel monitor` configuration directive in `sentinel.conf` configuration file, with the difference that you can't use a hostname in as `ip`, but you need to provide an IPv4 or IPv6 address.\n* **SENTINEL REMOVE `<name>`** is used in order to remove the specified master: the master will no longer be monitored, and will totally be removed from the internal state of the Sentinel, so it will no longer listed by `SENTINEL masters` and so forth.\n* **SENTINEL SET `<name>` [`<option>` `<value>` ...]** The SET command is very similar to the [`CONFIG SET`](https://redis.io/docs/latest/commands/config-set) command of Redis, and is used in order to change configuration parameters of a specific master. Multiple option / value pairs can be specified (or none at all). All the configuration parameters that can be configured via `sentinel.conf` are also configurable using the SET command.\n\nThe following is an example of `SENTINEL SET` command in order to modify the `down-after-milliseconds` configuration of a master called `objects-cache`:\n\n    SENTINEL SET objects-cache-master down-after-milliseconds 1000\n\nAs already stated, `SENTINEL SET` can be used to set all the configuration parameters that are settable in the startup configuration file. Moreover it is possible to change just the master quorum configuration without removing and re-adding the master with `SENTINEL REMOVE` followed by `SENTINEL MONITOR`, but simply using:\n\n    SENTINEL SET objects-cache-master quorum 5\n\nNote that there is no equivalent GET command since `SENTINEL MASTER` provides all the configuration parameters in a simple to parse format (as a field/value pairs array).\n\nStarting with Redis version 6.2, Sentinel also allows getting and setting global configuration parameters which were only supported in the configuration file prior to that.\n\n* **SENTINEL CONFIG GET `<name>`** Get the current value of a global Sentinel configuration parameter. The specified name may be a wildcard, similar to the Redis [`CONFIG GET`](https://redis.io/docs/latest/commands/config-get) command.\n* **SENTINEL CONFIG SET `<name>` `<value>`** Set the value of a global Sentinel configuration parameter.\n\nGlobal parameters that can be manipulated include:\n\n* `resolve-hostnames`, `announce-hostnames`. See [_IP addresses and DNS names_](#ip-addresses-and-dns-names).\n* `announce-ip`, `announce-port`. See [_Sentinel, Docker, NAT, and possible issues_](#sentinel-docker-nat-and-possible-issues).\n* `sentinel-user`, `sentinel-pass`. See [_Configuring Sentinel instances with authentication_](#configuring-sentinel-instances-with-authentication)."},{"id":"adding-or-removing-sentinels","title":"Adding or removing Sentinels","role":"content","text":"Adding a new Sentinel to your deployment is a simple process because of the\nauto-discover mechanism implemented by Sentinel. All you need to do is to\nstart the new Sentinel configured to monitor the currently active master.\nWithin 10 seconds the Sentinel will acquire the list of other Sentinels and\nthe set of replicas attached to the master.\n\nIf you need to add multiple Sentinels at once, it is suggested to add it\none after the other, waiting for all the other Sentinels to already know\nabout the first one before adding the next. This is useful in order to still\nguarantee that majority can be achieved only in one side of a partition,\nin the chance failures should happen in the process of adding new Sentinels.\n\nThis can be easily achieved by adding every new Sentinel with a 30 seconds delay, and during absence of network partitions.\n\nAt the end of the process it is possible to use the command\n`SENTINEL MASTER mastername` in order to check if all the Sentinels agree about\nthe total number of Sentinels monitoring the master.\n\nRemoving a Sentinel is a bit more complex: **Sentinels never forget already seen\nSentinels**, even if they are not reachable for a long time, since we don't\nwant to dynamically change the majority needed to authorize a failover and\nthe creation of a new configuration number. So in order to remove a Sentinel\nthe following steps should be performed in absence of network partitions:\n\n1. Stop the Sentinel process of the Sentinel you want to remove.\n2. Send a `SENTINEL RESET *` command to all the other Sentinel instances (instead of `*` you can use the exact master name if you want to reset just a single master). One after the other, waiting at least 30 seconds between instances.\n3. Check that all the Sentinels agree about the number of Sentinels currently active, by inspecting the output of `SENTINEL MASTER mastername` of every Sentinel."},{"id":"removing-the-old-master-or-unreachable-replicas","title":"Removing the old master or unreachable replicas","role":"content","text":"Sentinels never forget about replicas of a given master, even when they are\nunreachable for a long time. This is useful, because Sentinels should be able\nto correctly reconfigure a returning replica after a network partition or a\nfailure event.\n\nMoreover, after a failover, the failed over master is virtually added as a\nreplica of the new master, this way it will be reconfigured to replicate with\nthe new master as soon as it will be available again.\n\nHowever sometimes you want to remove a replica (that may be the old master)\nforever from the list of replicas monitored by Sentinels.\n\nIn order to do this, you need to send a `SENTINEL RESET mastername` command\nto all the Sentinels: they'll refresh the list of replicas within the next\n10 seconds, only adding the ones listed as correctly replicating from the\ncurrent master [`INFO`](https://redis.io/docs/latest/commands/info) output."},{"id":"pubsub-messages","title":"Pub/Sub messages","role":"content","text":"A client can use a Sentinel as a Redis-compatible Pub/Sub server\n(but you can't use [`PUBLISH`](https://redis.io/docs/latest/commands/publish)) in order to [`SUBSCRIBE`](https://redis.io/docs/latest/commands/subscribe) or [`PSUBSCRIBE`](https://redis.io/docs/latest/commands/psubscribe) to\nchannels and get notified about specific events.\n\nThe channel name is the same as the name of the event. For instance the\nchannel named `+sdown` will receive all the notifications related to instances\nentering an `SDOWN` (SDOWN means the instance is no longer reachable from\nthe point of view of the Sentinel you are querying) condition.\n\nTo get all the messages simply subscribe using `PSUBSCRIBE *`.\n\nThe following is a list of channels and message formats you can receive using\nthis API. The first word is the channel / event name, the rest is the format of the data.\n\nNote: where *instance details* is specified it means that the following arguments are provided to identify the target instance:\n\n    <instance-type> <name> <ip> <port> @ <master-name> <master-ip> <master-port>\n\nThe part identifying the master (from the @ argument to the end) is optional\nand is only specified if the instance is not a master itself.\n\n* **+reset-master** `<instance details>` -- The master was reset.\n* **+slave** `<instance details>` -- A new replica was detected and attached.\n* **+failover-state-reconf-slaves** `<instance details>` -- Failover state changed to `reconf-slaves` state.\n* **+failover-detected** `<instance details>` -- A failover started by another Sentinel or any other external entity was detected (An attached replica turned into a master).\n* **+slave-reconf-sent** `<instance details>` -- The leader sentinel sent the [`REPLICAOF`](https://redis.io/docs/latest/commands/replicaof) command to this instance in order to reconfigure it for the new replica.\n* **+slave-reconf-inprog** `<instance details>` -- The replica being reconfigured showed to be a replica of the new master ip:port pair, but the synchronization process is not yet complete.\n* **+slave-reconf-done** `<instance details>` -- The replica is now synchronized with the new master.\n* **-dup-sentinel** `<instance details>` -- One or more sentinels for the specified master were removed as duplicated (this happens for instance when a Sentinel instance is restarted).\n* **+sentinel** `<instance details>` -- A new sentinel for this master was detected and attached.\n* **+sdown** `<instance details>` -- The specified instance is now in Subjectively Down state.\n* **-sdown** `<instance details>` -- The specified instance is no longer in Subjectively Down state.\n* **+odown** `<instance details>` -- The specified instance is now in Objectively Down state.\n* **-odown** `<instance details>` -- The specified instance is no longer in Objectively Down state.\n* **+new-epoch** `<instance details>` -- The current epoch was updated.\n* **+try-failover** `<instance details>` -- New failover in progress, waiting to be elected by the majority.\n* **+elected-leader** `<instance details>` -- Won the election for the specified epoch, can do the failover.\n* **+failover-state-select-slave** `<instance details>` -- New failover state is `select-slave`: we are trying to find a suitable replica for promotion.\n* **no-good-slave** `<instance details>` -- There is no good replica to promote. Currently we'll try after some time, but probably this will change and the state machine will abort the failover at all in this case.\n* **selected-slave** `<instance details>` -- We found the specified good replica to promote.\n* **failover-state-send-slaveof-noone** `<instance details>` -- We are trying to reconfigure the promoted replica as master, waiting for it to switch.\n* **failover-end-for-timeout** `<instance details>` -- The failover terminated for timeout, replicas will eventually be configured to replicate with the new master anyway.\n* **failover-end** `<instance details>` -- The failover terminated with success. All the replicas appears to be reconfigured to replicate with the new master.\n* **switch-master** `<master name> <oldip> <oldport> <newip> <newport>` -- The master new IP and address is the specified one after a configuration change. This is **the message most external users are interested in**.\n* **+tilt** -- Tilt mode entered.\n* **-tilt** -- Tilt mode exited."},{"id":"handling-of--busy-state","title":"Handling of -BUSY state","role":"content","text":"The -BUSY error is returned by a Redis instance when a Lua script is running for\nmore time than the configured Lua script time limit. When this happens before\ntriggering a fail over Redis Sentinel will try to send a [`SCRIPT KILL`](https://redis.io/docs/latest/commands/script-kill)\ncommand, that will only succeed if the script was read-only.\n\nIf the instance is still in an error condition after this try, it will\neventually be failed over.\n\nReplicas priority\n---\n\nRedis instances have a configuration parameter called `replica-priority`.\nThis information is exposed by Redis replica instances in their [`INFO`](https://redis.io/docs/latest/commands/info) output,\nand Sentinel uses it in order to pick a replica among the ones that can be\nused in order to failover a master:\n\n1. If the replica priority is set to 0, the replica is never promoted to master.\n2. Replicas with a *lower* priority number are preferred by Sentinel.\n\nFor example if there is a replica S1 in the same data center of the current\nmaster, and another replica S2 in another data center, it is possible to set\nS1 with a priority of 10 and S2 with a priority of 100, so that if the master\nfails and both S1 and S2 are available, S1 will be preferred.\n\nFor more information about the way replicas are selected, please check the [_Replica selection and priority_ section](#replica-selection-and-priority) of this documentation."},{"id":"sentinel-and-redis-authentication","title":"Sentinel and Redis authentication","role":"content","text":"When the master is configured to require authentication from clients,\nas a security measure, replicas need to also be aware of the credentials in\norder to authenticate with the master and create the master-replica connection\nused for the asynchronous replication protocol."},{"id":"redis-access-control-list-authentication","title":"Redis Access Control List authentication","role":"content","text":"Starting with Redis 6, user authentication and permission is managed with the [Access Control List (ACL)](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl).\n\nIn order for Sentinels to connect to Redis server instances when they are\nconfigured with ACL, the Sentinel configuration must include the\nfollowing directives:\n\n    sentinel auth-user <master-name> <username>\n    sentinel auth-pass <master-name> <password>\n\nWhere `<username>` and `<password>` are the username and password for accessing the group's instances. These credentials should be provisioned on all of the group's Redis instances with the minimal control permissions. For example:\n\n    127.0.0.1:6379> ACL SETUSER sentinel-user ON >somepassword resetchannels &__sentinel__:hello +multi +slaveof +ping +exec +subscribe +config|rewrite +role +publish +info +client|setname +client|kill +script|kill\n\nThe only Pub/Sub channel that Sentinel uses on a monitored node is `__sentinel__:hello`, so\nthis user is granted access to that channel only (`resetchannels &__sentinel__:hello`) rather\nthan to all channels (`allchannels`, equivalently `&*`).\n\n> [!WARNING]\n> Sentinel discovery relies on the reserved `__sentinel__:hello` Pub/Sub channel on each\n> monitored master and replica. Sentinels periodically publish their presence there and\n> subscribe to it to discover other Sentinels and learn the current topology. These messages\n> carry no authentication and are trusted by receiving Sentinels, so any client that can\n> publish to this channel on a monitored node can send forged HELLO messages that redirect a Sentinel to an attacker-controlled endpoint,\n> potentially exposing the configured master credentials sent over command and Pub/Sub connections, and\n> trigger spurious failovers or a denial of service.\n>\n> Because of this, do not grant broad Pub/Sub access on Sentinel-monitored Redis nodes:\n>\n> * No user needs access to all channels (`allchannels` / `&*`). The Sentinel `auth-user`\n>   needs access only to the `__sentinel__:hello` channel (`&__sentinel__:hello`), and no\n>   other user should be able to publish to or subscribe to it. Since Redis 7.0, the default\n>   value of `acl-pubsub-default` is `resetchannels`, so newly created ACL users have no\n>   channel access unless you grant it explicitly. Be especially careful with the legacy\n>   `default` user, which may still have broad access. This documentation change does not alter existing ACL configurations. Review ACL users on monitored masters and replicas,\n>   including the legacy `default` user, and confirm that no non-Sentinel user can publish to or subscribe to\n>   `__sentinel__:hello`.\n> * Treat the `__sentinel__:` channel prefix as reserved for Sentinel's internal use."},{"id":"redis-password-only-authentication","title":"Redis password-only authentication","role":"content","text":"Until Redis 6, authentication is achieved using the following configuration directives:\n\n* `requirepass` in the master, in order to set the authentication password, and to make sure the instance will not process requests for non authenticated clients.\n* `masterauth` in the replicas in order for the replicas to authenticate with the master in order to correctly replicate data from it.\n\nWhen Sentinel is used, there is not a single master, since after a failover\nreplicas may play the role of masters, and old masters can be reconfigured in\norder to act as replicas, so what you want to do is to set the above directives\nin all your instances, both masters and replicas.\n\nThis is also usually a sane setup since you don't want to protect\ndata only in the master, having the same data accessible in the replicas.\n\nHowever, in the uncommon case where you need a replica that is accessible\nwithout authentication, you can still do it by setting up **a replica priority\nof zero**, to prevent this replica from being promoted to master, and\nconfiguring in this replica only the `masterauth` directive, without\nusing the `requirepass` directive, so that data will be readable by\nunauthenticated clients.\n\nIn order for Sentinels to connect to Redis server instances when they are\nconfigured with `requirepass`, the Sentinel configuration must include the\n`sentinel auth-pass` directive, in the format:\n\n    sentinel auth-pass <master-name> <password>\n\nConfiguring Sentinel instances with authentication\n---\n\nSentinel instances themselves can be secured by requiring clients to authenticate via the [`AUTH`](https://redis.io/docs/latest/commands/auth) command. Starting with Redis 6.2, the [Access Control List (ACL)](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl) is available, whereas previous versions (starting with Redis 5.0.1) support password-only authentication. \n\nNote that Sentinel's authentication configuration should be **applied to each of the instances** in your deployment, and **all instances should use the same configuration**. Furthermore, ACL and password-only authentication should not be used together."},{"id":"sentinel-access-control-list-authentication","title":"Sentinel Access Control List authentication","role":"content","text":"The first step in securing a Sentinel instance with ACL is preventing any unauthorized access to it. To do that, you'll need to disable the default superuser (or at the very least set it up with a strong password) and create a new one and allow it access to Pub/Sub channels:\n\n    127.0.0.1:5000> ACL SETUSER admin ON >admin-password allchannels +@all\n    OK\n    127.0.0.1:5000> ACL SETUSER default off\n    OK\n\nThe default user is used by Sentinel to connect to other instances. You can provide the credentials of another superuser with the following configuration directives:\n\n    sentinel sentinel-user <username>\n    sentinel sentinel-pass <password>\n\nWhere `<username>` and `<password>` are the Sentinel's superuser and password, respectively (e.g. `admin` and `admin-password` in the example above).\n\nLastly, for authenticating incoming client connections, you can create a Sentinel restricted user profile such as the following:\n\n    127.0.0.1:5000> ACL SETUSER sentinel-user ON >user-password -@all +auth +client|getname +client|id +client|setname +command +hello +ping +role +sentinel|get-master-addr-by-name +sentinel|master +sentinel|myid +sentinel|replicas +sentinel|sentinels +sentinel|masters\n\nRefer to the documentation of your Sentinel client of choice for further information."},{"id":"sentinel-password-only-authentication","title":"Sentinel password-only authentication","role":"content","text":"To use Sentinel with password-only authentication, add the `requirepass` configuration directive to **all** your Sentinel instances as follows:\n\n    requirepass \"your_password_here\"\n\nWhen configured this way, Sentinels will do two things:\n\n1. A password will be required from clients in order to send commands to Sentinels. This is obvious since this is how such configuration directive works in Redis in general.\n2. Moreover the same password configured to access the local Sentinel, will be used by this Sentinel instance in order to authenticate to all the other Sentinel instances it connects to.\n\nThis means that **you will have to configure the same `requirepass` password in all the Sentinel instances**. This way every Sentinel can talk with every other Sentinel without any need to configure for each Sentinel the password to access all the other Sentinels, that would be very impractical.\n\nBefore using this configuration, make sure your client library can send the [`AUTH`](https://redis.io/docs/latest/commands/auth) command to Sentinel instances."},{"id":"sentinel-clients-implementation","title":"Sentinel clients implementation","role":"content","text":"---\n\nSentinel requires explicit client support, unless the system is configured to execute a script that performs a transparent redirection of all the requests to the new master instance (virtual IP or other similar systems). The topic of client libraries implementation is covered in the document [Sentinel clients guidelines](https://redis.io/docs/latest/develop/reference/sentinel-clients)."},{"id":"more-advanced-concepts","title":"More advanced concepts","role":"content","text":"In the following sections we'll cover a few details about how Sentinel works,\nwithout resorting to implementation details and algorithms that will be\ncovered in the final part of this document."},{"id":"sdown-and-odown-failure-state","title":"SDOWN and ODOWN failure state","role":"content","text":"Redis Sentinel has two different concepts of *being down*, one is called\na *Subjectively Down* condition (SDOWN) and is a down condition that is\nlocal to a given Sentinel instance. Another is called *Objectively Down*\ncondition (ODOWN) and is reached when enough Sentinels (at least the\nnumber configured as the `quorum` parameter of the monitored master) have\nan SDOWN condition, and get feedback from other Sentinels using\nthe `SENTINEL is-master-down-by-addr` command.\n\nFrom the point of view of a Sentinel an SDOWN condition is reached when it\ndoes not receive a valid reply to PING requests for the number of seconds\nspecified in the configuration as `is-master-down-after-milliseconds`\nparameter.\n\nAn acceptable reply to PING is one of the following:\n\n* PING replied with +PONG.\n* PING replied with -LOADING error.\n* PING replied with -MASTERDOWN error.\n\nAny other reply (or no reply at all) is considered non valid.\nHowever note that **a logical master that advertises itself as a replica in\nthe INFO output is considered to be down**.\n\nNote that SDOWN requires that no acceptable reply is received for the whole\ninterval configured, so for instance if the interval is 30000 milliseconds\n(30 seconds) and we receive an acceptable ping reply every 29 seconds, the\ninstance is considered to be working.\n\nSDOWN is not enough to trigger a failover: it only means a single Sentinel\nbelieves a Redis instance is not available. To trigger a failover, the\nODOWN state must be reached.\n\nTo switch from SDOWN to ODOWN no strong consensus algorithm is used, but\njust a form of gossip: if a given Sentinel gets reports that a master\nis not working from enough Sentinels **in a given time range**, the SDOWN is\npromoted to ODOWN. If this acknowledge is later missing, the flag is cleared.\n\nA more strict authorization that uses an actual majority is required in\norder to really start the failover, but no failover can be triggered without\nreaching the ODOWN state.\n\nThe ODOWN condition **only applies to masters**. For other kind of instances\nSentinel doesn't require to act, so the ODOWN state is never reached for replicas\nand other sentinels, but only SDOWN is.\n\nHowever SDOWN has also semantic implications. For example a replica in SDOWN\nstate is not selected to be promoted by a Sentinel performing a failover.\n\nSentinels and replicas auto discovery\n---\n\nSentinels stay connected with other Sentinels in order to reciprocally\ncheck the availability of each other, and to exchange messages. However you\ndon't need to configure a list of other Sentinel addresses in every Sentinel\ninstance you run, as Sentinel uses the Redis instances Pub/Sub capabilities\nin order to discover the other Sentinels that are monitoring the same masters\nand replicas.\n\nThis feature is implemented by sending *hello messages* into the channel named\n`__sentinel__:hello`.\n\nSimilarly you don't need to configure what is the list of the replicas attached\nto a master, as Sentinel will auto discover this list querying Redis.\n\n* Every Sentinel publishes a message to every monitored master and replica Pub/Sub channel `__sentinel__:hello`, every two seconds, announcing its presence with ip, port, runid.\n* Every Sentinel is subscribed to the Pub/Sub channel `__sentinel__:hello` of every master and replica, looking for unknown sentinels. When new sentinels are detected, they are added as sentinels of this master.\n* Hello messages also include the full current configuration of the master. If the receiving Sentinel has a configuration for a given master which is older than the one received, it updates to the new configuration immediately.\n* Before adding a new sentinel to a master a Sentinel always checks if there is already a sentinel with the same runid or the same address (ip and port pair). In that case all the matching sentinels are removed, and the new added.\n\nSentinel reconfiguration of instances outside the failover procedure\n---\n\nEven when no failover is in progress, Sentinels will always try to set the\ncurrent configuration on monitored instances. Specifically:\n\n* Replicas (according to the current configuration) that claim to be masters, will be configured as replicas to replicate with the current master.\n* Replicas connected to a wrong master, will be reconfigured to replicate with the right master.\n\nFor Sentinels to reconfigure replicas, the wrong configuration must be observed for some time, that is greater than the period used to broadcast new configurations.\n\nThis prevents Sentinels with a stale configuration (for example because they just rejoined from a partition) will try to change the replicas configuration before receiving an update.\n\nAlso note how the semantics of always trying to impose the current configuration makes the failover more resistant to partitions:\n\n* Masters failed over are reconfigured as replicas when they return available.\n* Replicas partitioned away during a partition are reconfigured once reachable.\n\nThe important lesson to remember about this section is: **Sentinel is a system where each process will always try to impose the last logical configuration to the set of monitored instances**."},{"id":"replica-selection-and-priority","title":"Replica selection and priority","role":"content","text":"When a Sentinel instance is ready to perform a failover, since the master\nis in `ODOWN` state and the Sentinel received the authorization to failover\nfrom the majority of the Sentinel instances known, a suitable replica needs\nto be selected.\n\nThe replica selection process evaluates the following information about replicas:\n\n1. Disconnection time from the master.\n2. Replica priority.\n3. Replication offset processed.\n4. Run ID.\n\nA replica that is found to be disconnected from the master for more than ten\ntimes the configured master timeout (down-after-milliseconds option), plus\nthe time the master is also not available from the point of view of the\nSentinel doing the failover, is considered to be not suitable for the failover\nand is skipped.\n\nIn more rigorous terms, a replica whose the [`INFO`](https://redis.io/docs/latest/commands/info) output suggests it has been\ndisconnected from the master for more than:\n\n    (down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state\n\nIs considered to be unreliable and is disregarded entirely.\n\nThe replica selection only considers the replicas that passed the above test,\nand sorts it based on the above criteria, in the following order.\n\n1. The replicas are sorted by `replica-priority` as configured in the `redis.conf` file of the Redis instance. A lower priority will be preferred.\n2. If the priority is the same, the replication offset processed by the replica is checked, and the replica that received more data from the master is selected.\n3. If multiple replicas have the same priority and processed the same data from the master, a further check is performed, selecting the replica with the lexicographically smaller run ID. Having a lower run ID is not a real advantage for a replica, but is useful in order to make the process of replica selection more deterministic, instead of resorting to select a random replica.\n\nIn most cases, `replica-priority` does not need to be set explicitly so all\ninstances will use the same default value. If there is a particular fail-over\npreference, `replica-priority` must be set on all instances, including masters,\nas a master may become a replica at some future point in time - and it will then\nneed the proper `replica-priority` settings.\n\nA Redis instance can be configured with a special `replica-priority` of zero\nin order to be **never selected** by Sentinels as the new master.\nHowever a replica configured in this way will still be reconfigured by\nSentinels in order to replicate with the new master after a failover, the\nonly difference is that it will never become a master itself."},{"id":"algorithms-and-internals","title":"Algorithms and internals","role":"content","text":"In the following sections we will explore the details of Sentinel behavior.\nIt is not strictly needed for users to be aware of all the details, but a\ndeep understanding of Sentinel may help to deploy and operate Sentinel in\na more effective way."},{"id":"quorum","title":"Quorum","role":"content","text":"The previous sections showed that every master monitored by Sentinel is associated to a configured **quorum**. It specifies the number of Sentinel processes\nthat need to agree about the unreachability or error condition of the master in\norder to trigger a failover.\n\nHowever, after the failover is triggered, in order for the failover to actually be performed, **at least a majority of Sentinels must authorize the Sentinel to\nfailover**. Sentinel never performs a failover in the partition where a\nminority of Sentinels exist.\n\nLet's try to make things a bit more clear:\n\n* Quorum: the number of Sentinel processes that need to detect an error condition in order for a master to be flagged as **ODOWN**.\n* The failover is triggered by the **ODOWN** state.\n* Once the failover is triggered, the Sentinel trying to failover is required to ask for authorization to a majority of Sentinels (or more than the majority if the quorum is set to a number greater than the majority).\n\nThe difference may seem subtle but is actually quite simple to understand and use.  For example if you have 5 Sentinel instances, and the quorum is set to 2, a failover will be triggered as soon as 2 Sentinels believe that the master is not reachable, however one of the two Sentinels will be able to failover only if it gets authorization at least from 3 Sentinels.\n\nIf instead the quorum is configured to 5, all the Sentinels must agree about the master error condition, and the authorization from all Sentinels is required in order to failover.\n\nThis means that the quorum can be used to tune Sentinel in two ways:\n\n1. If a quorum is set to a value smaller than the majority of Sentinels we deploy, we are basically making Sentinel more sensitive to master failures, triggering a failover as soon as even just a minority of Sentinels is no longer able to talk with the master.\n2. If a quorum is set to a value greater than the majority of Sentinels, we are making Sentinel able to failover only when there are a very large number (larger than majority) of well connected Sentinels which agree about the master being down."},{"id":"configuration-epochs","title":"Configuration epochs","role":"configuration","text":"Sentinels require to get authorizations from a majority in order to start a\nfailover for a few important reasons:\n\nWhen a Sentinel is authorized, it gets a unique **configuration epoch** for the master it is failing over. This is a number that will be used to version the new configuration after the failover is completed. Because a majority agreed that a given version was assigned to a given Sentinel, no other Sentinel will be able to use it. This means that every configuration of every failover is versioned with a unique version. We'll see why this is so important.\n\nMoreover Sentinels have a rule: if a Sentinel voted another Sentinel for the failover of a given master, it will wait some time to try to failover the same master again. This delay is the `2 * failover-timeout` you can configure in `sentinel.conf`. This means that Sentinels will not try to failover the same master at the same time, the first to ask to be authorized will try, if it fails another will try after some time, and so forth.\n\nRedis Sentinel guarantees the *liveness* property that if a majority of Sentinels are able to talk, eventually one will be authorized to failover if the master is down.\n\nRedis Sentinel also guarantees the *safety* property that every Sentinel will failover the same master using a different *configuration epoch*."},{"id":"configuration-propagation","title":"Configuration propagation","role":"configuration","text":"Once a Sentinel is able to failover a master successfully, it will start to broadcast the new configuration so that the other Sentinels will update their information about a given master.\n\nFor a failover to be considered successful, it requires that the Sentinel was able to send the `REPLICAOF NO ONE` command to the selected replica, and that the switch to master was later observed in the [`INFO`](https://redis.io/docs/latest/commands/info) output of the master.\n\nAt this point, even if the reconfiguration of the replicas is in progress, the failover is considered to be successful, and all the Sentinels are required to start reporting the new configuration.\n\nThe way a new configuration is propagated is the reason why we need that every\nSentinel failover is authorized with a different version number (configuration epoch).\n\nEvery Sentinel continuously broadcast its version of the configuration of a master using Redis Pub/Sub messages, both in the master and all the replicas.  At the same time all the Sentinels wait for messages to see what is the configuration\nadvertised by the other Sentinels.\n\nConfigurations are broadcast in the `__sentinel__:hello` Pub/Sub channel.\n\nBecause every configuration has a different version number, the greater version\nalways wins over smaller versions.\n\nSo for example the configuration for the master `mymaster` start with all the\nSentinels believing the master is at 192.168.1.50:6379. This configuration\nhas version 1. After some time a Sentinel is authorized to failover with version 2. If the failover is successful, it will start to broadcast a new configuration, let's say 192.168.1.50:9000, with version 2. All the other instances will see this configuration and will update their configuration accordingly, since the new configuration has a greater version.\n\nThis means that Sentinel guarantees a second liveness property: a set of\nSentinels that are able to communicate will all converge to the same configuration with the higher version number.\n\nBasically if the net is partitioned, every partition will converge to the higher\nlocal configuration. In the special case of no partitions, there is a single\npartition and every Sentinel will agree about the configuration."},{"id":"consistency-under-partitions","title":"Consistency under partitions","role":"content","text":"Redis Sentinel configurations are eventually consistent, so every partition will\nconverge to the higher configuration available.\nHowever in a real-world system using Sentinel there are three different players:\n\n* Redis instances.\n* Sentinel instances.\n* Clients.\n\nIn order to define the behavior of the system we have to consider all three.\n\nThe following is a simple network where there are 3 nodes, each running\na Redis instance, and a Sentinel instance:\n\n                +-------------+\n                | Sentinel 1  |----- Client A\n                | Redis 1 (M) |\n                +-------------+\n                        |\n                        |\n    +-------------+     |          +------------+\n    | Sentinel 2  |-----+-- // ----| Sentinel 3 |----- Client B\n    | Redis 2 (S) |                | Redis 3 (M)|\n    +-------------+                +------------+\n\nIn this system the original state was that Redis 3 was the master, while\nRedis 1 and 2 were replicas. A partition occurred isolating the old master.\nSentinels 1 and 2 started a failover promoting Sentinel 1 as the new master.\n\nThe Sentinel properties guarantee that Sentinel 1 and 2 now have the new\nconfiguration for the master. However Sentinel 3 has still the old configuration\nsince it lives in a different partition.\n\nWe know that Sentinel 3 will get its configuration updated when the network\npartition will heal, however what happens during the partition if there\nare clients partitioned with the old master?\n\nClients will be still able to write to Redis 3, the old master. When the\npartition will rejoin, Redis 3 will be turned into a replica of Redis 1, and\nall the data written during the partition will be lost.\n\nDepending on your configuration you may want or not that this scenario happens:\n\n* If you are using Redis as a cache, it could be handy that Client B is still able to write to the old master, even if its data will be lost.\n* If you are using Redis as a store, this is not good and you need to configure the system in order to partially prevent this problem.\n\nSince Redis is asynchronously replicated, there is no way to totally prevent data loss in this scenario, however you can bound the divergence between Redis 3 and Redis 1\nusing the following Redis configuration option:\n\n    min-replicas-to-write 1\n    min-replicas-max-lag 10\n\nWith the above configuration (please see the self-commented `redis.conf` example in the Redis distribution for more information) a Redis instance, when acting as a master, will stop accepting writes if it can't write to at least 1 replica. Since replication is asynchronous *not being able to write* actually means that the replica is either disconnected, or is not sending us asynchronous acknowledges for more than the specified `max-lag` number of seconds.\n\nUsing this configuration the Redis 3 in the above example will become unavailable after 10 seconds. When the partition heals, the Sentinel 3 configuration will converge to\nthe new one, and Client B will be able to fetch a valid configuration and continue.\n\nIn general Redis + Sentinel as a whole are an **eventually consistent system** where the merge function is **last failover wins**, and the data from old masters are discarded to replicate the data of the current master, so there is always a window for losing acknowledged writes. This is due to Redis asynchronous\nreplication and the discarding nature of the \"virtual\" merge function of the system. Note that this is not a limitation of Sentinel itself, and if you orchestrate the failover with a strongly consistent replicated state machine, the same properties will still apply. There are only two ways to avoid losing acknowledged writes:\n\n1. Use synchronous replication (and a proper consensus algorithm to run a replicated state machine).\n2. Use an eventually consistent system where different versions of the same object can be merged.\n\nRedis currently is not able to use any of the above systems, and is currently outside the development goals. However there are proxies implementing solution \"2\" on top of Redis stores such as SoundCloud [Roshi](https://github.com/soundcloud/roshi), or Netflix [Dynomite](https://github.com/Netflix/dynomite).\n\nSentinel persistent state\n---\n\nSentinel state is persisted in the sentinel configuration file. For example\nevery time a new configuration is received, or created (leader Sentinels), for\na master, the configuration is persisted on disk together with the configuration\nepoch. This means that it is safe to stop and restart Sentinel processes."},{"id":"tilt-mode","title":"TILT mode","role":"content","text":"Redis Sentinel is heavily dependent on the computer time: for instance in\norder to understand if an instance is available it remembers the time of the\nlatest successful reply to the PING command, and compares it with the current\ntime to understand how old it is.\n\nHowever if the computer time changes in an unexpected way, or if the computer\nis very busy, or the process blocked for some reason, Sentinel may start to\nbehave in an unexpected way.\n\nThe TILT mode is a special \"protection\" mode that a Sentinel can enter when\nsomething odd is detected that can lower the reliability of the system.\nThe Sentinel timer interrupt is normally called 10 times per second, so we\nexpect that more or less 100 milliseconds will elapse between two calls\nto the timer interrupt.\n\nWhat a Sentinel does is to register the previous time the timer interrupt\nwas called, and compare it with the current call: if the time difference\nis negative or unexpectedly big (2 seconds or more) the TILT mode is entered\n(or if it was already entered the exit from the TILT mode postponed).\n\nWhen in TILT mode the Sentinel will continue to monitor everything, but:\n\n* It stops acting at all.\n* It starts to reply negatively to `SENTINEL is-master-down-by-addr` requests as the ability to detect a failure is no longer trusted.\n\nIf everything appears to be normal for 30 second, the TILT mode is exited.\n \nIn the Sentinel TILT mode, if we send the INFO command, we could get the following response:\n\n    $ redis-cli -p 26379\n    127.0.0.1:26379> info\n    (Other information from Sentinel server skipped.)\n\n    # Sentinel\n    sentinel_masters:1\n    sentinel_tilt:0\n    sentinel_tilt_since_seconds:-1\n    sentinel_running_scripts:0\n    sentinel_scripts_queue_length:0\n    sentinel_simulate_failure_flags:0\n    master0:name=mymaster,status=ok,address=127.0.0.1:6379,slaves=0,sentinels=1\n\nThe field \"sentinel_tilt_since_seconds\" indicates how many seconds the Sentinel already is in the TILT mode.\nIf it is not in TILT mode, the value will be -1.\n\nNote that in some ways TILT mode could be replaced using the monotonic clock\nAPI that many kernels offer. However it is not still clear if this is a good\nsolution since the current system avoids issues in case the process is just\nsuspended or not executed by the scheduler for a long time.\n\n**A note about the word slave used in this man page**: Starting with Redis 5, if not for backward compatibility, the Redis project no longer uses the word slave. Unfortunately in this command the word slave is part of the protocol, so we'll be able to remove such occurrences only when this API will be naturally deprecated."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/management/troubleshooting","title":"Troubleshooting Redis","url":"https://redis.io/docs/latest/operate/oss_and_stack/management/troubleshooting/","summary":"Problems with Redis? Start here.","tags":["docs","operate","stack","oss"],"last_updated":"2026-09-21T15:59:03+01:00","page_type":"content","content_hash":"bbd9094e1903cd45b827579fde386fffa0f8758d09f356f6cf35c70315059ff4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This page tries to help you with what to do if you have issues with Redis. Part of the Redis project is helping people that are experiencing problems because we don't like to leave people alone with their issues.\n\n* If you have **latency problems** with Redis, that in some way appears to be idle for some time, read our [Redis latency troubleshooting guide](https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/latency).\n* Redis stable releases are usually very reliable, however in the rare event you are **experiencing crashes** the developers can help a lot more if you provide debugging information. Please read our [Debugging Redis guide](https://redis.io/docs/latest/operate/oss_and_stack/management/debugging).\n* We have a long history of users experiencing crashes with Redis that actually turned out to be servers with **broken RAM**. Please test your RAM using **redis-server --test-memory** in case Redis is not stable in your system. Redis built-in memory test is fast and reasonably reliable, but if you can you should reboot your server and use [memtest86](http://memtest86.com).\n\nFor every other problem please drop a message to the [Redis Google Group](http://groups.google.com/group/redis-db). We will be glad to help.\n\nYou can also find assistance on the [Redis Discord server](https://discord.gg/redis)."},{"id":"list-of-known-critical-bugs-in-redis-30x-28x-and-26x","title":"List of known critical bugs in Redis 3.0.x, 2.8.x and 2.6.x","role":"content","text":"To find a list of critical bugs please refer to the changelogs:\n\n* [Redis 3.0 Changelog](https://raw.githubusercontent.com/redis/redis/3.0/00-RELEASENOTES).\n* [Redis 2.8 Changelog](https://raw.githubusercontent.com/redis/redis/2.8/00-RELEASENOTES).\n* [Redis 2.6 Changelog](https://raw.githubusercontent.com/redis/redis/2.6/00-RELEASENOTES).\n\nCheck the *upgrade urgency* level in each patch release to more easily spot\nreleases that included important fixes."},{"id":"list-of-known-linux-related-bugs-affecting-redis","title":"List of known Linux related bugs affecting Redis.","role":"content","text":"* Ubuntu 10.04 and 10.10 contain [bugs](https://bugs.launchpad.net/ubuntu/+source/linux/+bug/666211) that can cause performance issues. The default kernels shipped with these distributions are not recommended. Bugs were reported as having affected EC2 instances, but some users also cited server impact.\n* Certain versions of the Xen hypervisor report poor fork() performance. See [the latency page](https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/latency) for more information."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/reference/arm","title":"ARM support","url":"https://redis.io/docs/latest/operate/oss_and_stack/reference/arm/","summary":"Exploring Redis on the ARM CPU Architecture","tags":["docs","operate","stack","oss"],"last_updated":"2026-09-21T15:59:03+01:00","page_type":"content","content_hash":"23de5bf6db0baed5fd6de7602594fbbf6f6be446ace5c21d8ed72a68ebd5afa6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis versions 4.0 and above support the ARM processor in general, and\nthe Raspberry Pi specifically, as a main platform. Every new release of Redis is tested on the Pi\nenvironment, and we update this documentation page with information about supported devices and other useful information. While Redis does run on Android, in the future we look forward to extend our testing efforts to Android\nto also make it an officially supported platform.\n\nWe believe that Redis is ideal for IoT and embedded devices for several\nreasons:\n\n* Redis has a very small memory footprint and CPU requirements. It can run in small devices like the Raspberry Pi Zero without impacting the overall performance, using a small amount of memory while delivering good performance for many use cases.\n* The data structures of Redis are often an ideal way to model IoT/embedded use cases. Some examples include accumulating time series data, receiving or queuing commands to execute or respond to send back to the remote servers, and so forth.\n* Modeling data inside Redis can be very useful in order to make in-device decisions for appliances that must respond very quickly or when the remote servers are offline.\n* Redis can be used as a communication system between the processes running in the device.\n* The append-only file storage of Redis is well suited for SSD cards.\n* The stream data structure included in Redis versions 5.0 and higher was specifically designed for time series applications and has a very low memory overhead."},{"id":"redis-proccpualignment-requirements","title":"Redis /proc/cpu/alignment requirements","role":"content","text":"Linux on ARM allows to trap unaligned accesses and fix them inside the kernel\nin order to continue the execution of the offending program instead of\ngenerating a `SIGBUS`. Redis 4.0 and greater are fixed in order to avoid any kind\nof unaligned access, so there is no need to have a specific value for this\nkernel configuration. Even when kernel alignment fixing set as disabled Redis should\nrun as expected."},{"id":"building-redis-in-the-pi","title":"Building Redis in the Pi","role":"content","text":"* Download Redis version 4.0 or higher.\n* Use `make` as usual to create the executable.\n\nThere is nothing special in the process. The only difference is that by\ndefault, Redis uses the `libc` allocator instead of defaulting to `jemalloc`\nas it does in other Linux based environments. This is because we believe\nthat for the small use cases inside embedded devices, memory fragmentation\nis unlikely to be a problem. Moreover `jemalloc` on ARM may not be as tested\nas the `libc` allocator."},{"id":"performance","title":"Performance","role":"performance","text":"Performance testing of Redis was performed on the Raspberry Pi 3 and Pi 1 model B. The difference between the two Pis in terms of delivered performance is quite big. The benchmarks were performed via the\nloopback interface, since most use cases will probably use Redis from within\nthe device and not via the network. The following numbers were obtained using\nRedis 4.0.\n\nRaspberry Pi 3:\n\n* Test 1 : 5 millions writes with 1 million keys (even distribution among keys).  No persistence, no pipelining. 28,000 ops/sec.\n* Test 2: Like test 1 but with pipelining using groups of 8 operations: 80,000 ops/sec.\n* Test 3: Like test 1 but with AOF enabled, fsync 1 sec: 23,000 ops/sec\n* Test 4: Like test 3, but with an AOF rewrite in progress: 21,000 ops/sec\n\nRaspberry Pi 1 model B:\n\n* Test 1 : 5 millions writes with 1 million keys (even distribution among keys).  No persistence, no pipelining.  2,200 ops/sec.\n* Test 2: Like test 1 but with pipelining using groups of 8 operations: 8,500 ops/sec.\n* Test 3: Like test 1 but with AOF enabled, fsync 1 sec: 1,820 ops/sec\n* Test 4: Like test 3, but with an AOF rewrite in progress: 1,000 ops/sec\n\nThe benchmarks above are referring to simple [`SET`](https://redis.io/docs/latest/commands/set)/[`GET`](https://redis.io/docs/latest/commands/get) operations. The performance is similar for all the Redis fast operations (not running in linear time). However sorted sets may show slightly slower numbers."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/reference/cluster-spec","title":"Redis cluster specification","url":"https://redis.io/docs/latest/operate/oss_and_stack/reference/cluster-spec/","summary":"Detailed specification for Redis cluster","aliases":["/develop/reference/cluster-spec/","/reference/cluster-spec/"],"tags":["docs","operate","stack","oss"],"last_updated":"2026-09-21T15:59:03+01:00","page_type":"content","content_hash":"c3307daabe0dfd95ddf2867e1da1e835c30c35ce563099c13cefa32a09995d6b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Welcome to the **Redis Cluster Specification**. Here you'll find information\nabout the algorithms and design rationales of Redis Cluster. This document is a work\nin progress as it is continuously synchronized with the actual implementation\nof Redis."},{"id":"main-properties-and-rationales-of-the-design","title":"Main properties and rationales of the design","role":"content","text":""},{"id":"redis-cluster-goals","title":"Redis Cluster goals","role":"content","text":"Redis Cluster is a distributed implementation of Redis with the following goals in order of importance in the design:\n\n* High performance and linear scalability up to 1000 nodes. There are no proxies, asynchronous replication is used, and no merge operations are performed on values.\n* Acceptable degree of write safety: the system tries (in a best-effort way) to retain all the writes originating from clients connected with the majority of the master nodes. Usually there are small windows where acknowledged writes can be lost. Windows to lose acknowledged writes are larger when clients are in a minority partition.\n* Availability: Redis Cluster is able to survive partitions where the majority of the master nodes are reachable and there is at least one reachable replica for every master node that is no longer reachable. Moreover using *replicas migration*, masters no longer replicated by any replica will receive one from a master which is covered by multiple replicas.\n\nWhat is described in this document is implemented in Redis 3.0 or greater."},{"id":"implemented-subset","title":"Implemented subset","role":"content","text":"Redis Cluster implements all the single key commands available in the\nnon-distributed version of Redis. Commands performing complex multi-key\noperations like set unions and intersections are implemented for cases where\nall of the keys involved in the operation hash to the same slot.\n\nRedis Cluster implements a concept called **hash tags** that can be used\nto force certain keys to be stored in the same hash slot. However, during\nmanual resharding, multi-key operations may become unavailable for some time\nwhile single-key operations are always available.\n\nRedis Cluster does not support multiple databases like the standalone version\nof Redis. We only support database `0`; the [`SELECT`](https://redis.io/docs/latest/commands/select) command is not allowed."},{"id":"client-and-server-roles-in-the-redis-cluster-protocol","title":"Client and Server roles in the Redis cluster protocol","role":"content","text":"In Redis Cluster, nodes are responsible for holding the data,\nand taking the state of the cluster, including mapping keys to the right nodes.\nCluster nodes are also able to auto-discover other nodes, detect non-working\nnodes, and promote replica nodes to master when needed in order\nto continue to operate when a failure occurs.\n\nTo perform their tasks all the cluster nodes are connected using a\nTCP bus and a binary protocol, called the **Redis Cluster Bus**.\nEvery node is connected to every other node in the cluster using the cluster\nbus. Nodes use a gossip protocol to propagate information about the cluster\nin order to discover new nodes, to send ping packets to make sure all the\nother nodes are working properly, and to send cluster messages needed to\nsignal specific conditions. The cluster bus is also used in order to\npropagate Pub/Sub messages across the cluster and to orchestrate manual\nfailovers when requested by users (manual failovers are failovers which\nare not initiated by the Redis Cluster failure detector, but by the\nsystem administrator directly).\n\nSince cluster nodes are not able to proxy requests, clients may be redirected\nto other nodes using redirection errors `-MOVED` and `-ASK`.\nThe client is in theory free to send requests to all the nodes in the cluster,\ngetting redirected if needed, so the client is not required to hold the\nstate of the cluster. However clients that are able to cache the map between\nkeys and nodes can improve the performance in a sensible way."},{"id":"write-safety","title":"Write safety","role":"content","text":"Redis Cluster uses asynchronous replication between nodes, and **last failover wins** implicit merge function. This means that the last elected master dataset eventually replaces all the other replicas. There is always a window of time when it is possible to lose writes during partitions. However these windows are very different in the case of a client that is connected to the majority of masters, and a client that is connected to the minority of masters.\n\nRedis Cluster tries harder to retain writes that are performed by clients connected to the majority of masters, compared to writes performed in the minority side.\nThe following are examples of scenarios that lead to loss of acknowledged\nwrites received in the majority partitions during failures:\n\n1. A write may reach a master, but while the master may be able to reply to the client, the write may not be propagated to replicas via the asynchronous replication used between master and replica nodes. If the master dies without the write reaching the replicas, the write is lost forever if the master is unreachable for a long enough period that one of its replicas is promoted. This is usually hard to observe in the case of a total, sudden failure of a master node since masters try to reply to clients (with the acknowledge of the write) and replicas (propagating the write) at about the same time. However it is a real world failure mode.\n\n2. Another theoretically possible failure mode where writes are lost is the following:\n\n* A master is unreachable because of a partition.\n* It gets failed over by one of its replicas.\n* After some time it may be reachable again.\n* A client with an out-of-date routing table may write to the old master before it is converted into a replica (of the new master) by the cluster.\n\nThe second failure mode is unlikely to happen because master nodes unable to communicate with the majority of the other masters for enough time to be failed over will no longer accept writes, and when the partition is fixed writes are still refused for a small amount of time to allow other nodes to inform about configuration changes. This failure mode also requires that the client's routing table has not yet been updated.\n\nWrites targeting the minority side of a partition have a larger window in which to get lost. For example, Redis Cluster loses a non-trivial number of writes on partitions where there is a minority of masters and at least one or more clients, since all the writes sent to the masters may potentially get lost if the masters are failed over in the majority side.\n\nSpecifically, for a master to be failed over it must be unreachable by the majority of masters for at least `NODE_TIMEOUT`, so if the partition is fixed before that time, no writes are lost. When the partition lasts for more than `NODE_TIMEOUT`, all the writes performed in the minority side up to that point may be lost. However the minority side of a Redis Cluster will start refusing writes as soon as `NODE_TIMEOUT` time has elapsed without contact with the majority, so there is a maximum window after which the minority becomes no longer available. Hence, no writes are accepted or lost after that time."},{"id":"availability","title":"Availability","role":"content","text":"Redis Cluster is not available in the minority side of the partition. In the majority side of the partition assuming that there are at least the majority of masters and a replica for every unreachable master, the cluster becomes available again after `NODE_TIMEOUT` time plus a few more seconds required for a replica to get elected and failover its master (failovers are usually executed in a matter of 1 or 2 seconds).\n\nThis means that Redis Cluster is designed to survive failures of a few nodes in the cluster, but it is not a suitable solution for applications that require availability in the event of large net splits.\n\nIn the example of a cluster composed of N master nodes where every node has a single replica, the majority side of the cluster will remain available as long as a single node is partitioned away, and will remain available with a probability of `1-(1/(N*2-1))` when two nodes are partitioned away (after the first node fails we are left with `N*2-1` nodes in total, and the probability of the only master without a replica to fail is `1/(N*2-1))`.\n\nFor example, in a cluster with 5 nodes and a single replica per node, there is a `1/(5*2-1) = 11.11%` probability that after two nodes are partitioned away from the majority, the cluster will no longer be available.\n\nThanks to a Redis Cluster feature called **replicas migration** the Cluster\navailability is improved in many real world scenarios by the fact that\nreplicas migrate to orphaned masters (masters no longer having replicas).\nSo at every successful failure event, the cluster may reconfigure the replicas\nlayout in order to better resist the next failure."},{"id":"performance","title":"Performance","role":"performance","text":"In Redis Cluster nodes don't proxy commands to the right node in charge for a given key, but instead they redirect clients to the right nodes serving a given portion of the key space.\n\nEventually clients obtain an up-to-date representation of the cluster and which node serves which subset of keys, so during normal operations clients directly contact the right nodes in order to send a given command.\n\nBecause of the use of asynchronous replication, nodes do not wait for other nodes' acknowledgment of writes (if not explicitly requested using the [`WAIT`](https://redis.io/docs/latest/commands/wait) command).\n\nAlso, because multi-key commands are only limited to *near* keys, data is never moved between nodes except when resharding.\n\nNormal operations are handled exactly as in the case of a single Redis instance. This means that in a Redis Cluster with N master nodes you can expect the same performance as a single Redis instance multiplied by N as the design scales linearly. At the same time the query is usually performed in a single round trip, since clients usually retain persistent connections with the nodes, so latency figures are also the same as the single standalone Redis node case.\n\nVery high performance and scalability while preserving weak but\nreasonable forms of data safety and availability is the main goal of\nRedis Cluster."},{"id":"why-merge-operations-are-avoided","title":"Why merge operations are avoided","role":"content","text":"The Redis Cluster design avoids conflicting versions of the same key-value pair in multiple nodes as in the case of the Redis data model this is not always desirable. Values in Redis are often very large; it is common to see lists or sorted sets with millions of elements. Also data types are semantically complex. Transferring and merging these kind of values can be a major bottleneck and/or may require the non-trivial involvement of application-side logic, additional memory to store meta-data, and so forth.\n\nThere are no strict technological limits here. CRDTs or synchronously replicated\nstate machines can model complex data types similar to Redis. However, the\nactual run time behavior of such systems would not be similar to Redis Cluster.\nRedis Cluster was designed in order to cover the exact use cases of the\nnon-clustered Redis version."},{"id":"overview-of-redis-cluster-main-components","title":"Overview of Redis Cluster main components","role":"overview","text":""},{"id":"key-distribution-model","title":"Key distribution model","role":"content","text":"The cluster's key space is split into 16384 slots, effectively setting an upper limit\nfor the cluster size of 16384 master nodes (however, the suggested max size of\nnodes is on the order of ~ 1000 nodes).\n\nEach master node in a cluster handles a subset of the 16384 hash slots.\nThe cluster is **stable** when there is no cluster reconfiguration in\nprogress (i.e. where hash slots are being moved from one node to another).\nWhen the cluster is stable, a single hash slot will be served by a single node\n(however the serving node can have one or more replicas that will replace it in the case of net splits or failures,\nand that can be used in order to scale read operations where reading stale data is acceptable).\n\nThe base algorithm used to map keys to hash slots is the following\n(read the next paragraph for the hash tag exception to this rule):\n\n    HASH_SLOT = CRC16(key) mod 16384\n\nThe CRC16 is specified as follows:\n\n* Name: XMODEM (also known as ZMODEM or CRC-16/ACORN)\n* Width: 16 bit\n* Poly: 1021 (That is actually x^16 + x^12 + x^5 + 1)\n* Initialization: 0000\n* Reflect Input byte: False\n* Reflect Output CRC: False\n* Xor constant to output CRC: 0000\n* Output for \"123456789\": 31C3\n\n14 out of 16 CRC16 output bits are used (this is why there is\na modulo 16384 operation in the formula above).\n\nIn our tests CRC16 behaved remarkably well in distributing different kinds of\nkeys evenly across the 16384 slots.\n\n**Note**: A reference implementation of the CRC16 algorithm used is available in the Appendix A of this document."},{"id":"hash-tags","title":"Hash tags","role":"content","text":"There is an exception for the computation of the hash slot that is used in order\nto implement **hash tags**. Hash tags are a way to ensure that multiple keys\nare allocated in the same hash slot. This is used in order to implement\nmulti-key operations in Redis Cluster.\n\nTo implement hash tags, the hash slot for a key is computed in a\nslightly different way in certain conditions.\nIf the key contains a \"{...}\" pattern only the substring between\n`{` and `}` is hashed in order to obtain the hash slot. However since it is\npossible that there are multiple occurrences of `{` or `}` the algorithm is\nwell specified by the following rules:\n\n* IF the key contains a `{` character.\n* AND IF there is a `}` character to the right of `{`.\n* AND IF there are one or more characters between the first occurrence of `{` and the first occurrence of `}`.\n\nThen instead of hashing the key, only what is between the first occurrence of `{` and the following first occurrence of `}` is hashed.\n\nExamples:\n\n* The two keys `{user1000}.following` and `{user1000}.followers` will hash to the same hash slot since only the substring `user1000` will be hashed in order to compute the hash slot.\n* For the key `foo{}{bar}` the whole key will be hashed as usual since the first occurrence of `{` is followed by `}` on the right without characters in the middle.\n* For the key `foo{{bar}}zap` the substring `{bar` will be hashed, because it is the substring between the first occurrence of `{` and the first occurrence of `}` on its right.\n* For the key `foo{bar}{zap}` the substring `bar` will be hashed, since the algorithm stops at the first valid or invalid (without bytes inside) match of `{` and `}`.\n* What follows from the algorithm is that if the key starts with `{}`, it is guaranteed to be hashed as a whole. This is useful when using binary data as key names.\n\n#### Glob-style patterns\n\nCommands accepting a glob-style pattern, including [`KEYS`](https://redis.io/docs/latest/commands/keys), [`SCAN`](https://redis.io/docs/latest/commands/scan) and [`SORT`](https://redis.io/docs/latest/commands/sort), are optimized for patterns that imply a single slot.\nThis means that if all keys that can match a pattern must belong to a specific slot, only this slot is searched for keys matching the pattern.\nThe pattern slot optimization is introduced in Redis 8.0.\n\nThe optimization kicks in when the pattern meets the following conditions:\n\n* the pattern contains a hashtag,\n* there are no wildcards or escape characters before the hashtag, and\n* the hashtag within curly braces doesn't contain any wildcards or escape characters.\n\nFor example, `SCAN 0 MATCH {abc}*` can successfully recognize the hashtag and scans only the slot corresponding to `abc`.\nHowever, the patterns `*{abc}`, `{a*c}`, or `{a\\*bc}` cannot recognize the hashtag, so all slots need to be scanned.\n\n#### Hash slot example code\n\nAdding the hash tags exception, the following is an implementation of the `HASH_SLOT` function in Ruby and C language.\n\nRuby example code:\n\n    def HASH_SLOT(key)\n        s = key.index \"{\"\n        if s\n            e = key.index \"}\",s+1\n            if e && e != s+1\n                key = key[s+1..e-1]\n            end\n        end\n        crc16(key) % 16384\n    end\n\nC example code:\n\n    unsigned int HASH_SLOT(char *key, int keylen) {\n        int s, e; /* start-end indexes of { and } */\n\n        /* Search the first occurrence of '{'. */\n        for (s = 0; s < keylen; s++)\n            if (key[s] == '{') break;\n\n        /* No '{' ? Hash the whole key. This is the base case. */\n        if (s == keylen) return crc16(key,keylen) & 16383;\n\n        /* '{' found? Check if we have the corresponding '}'. */\n        for (e = s+1; e < keylen; e++)\n            if (key[e] == '}') break;\n\n        /* No '}' or nothing between {} ? Hash the whole key. */\n        if (e == keylen || e == s+1) return crc16(key,keylen) & 16383;\n\n        /* If we are here there is both a { and a } on its right. Hash\n         * what is in the middle between { and }. */\n        return crc16(key+s+1,e-s-1) & 16383;\n    }"},{"id":"cluster-node-attributes","title":"Cluster node attributes","role":"content","text":"Every node has a unique name in the cluster. The node name is the\nhex representation of a 160 bit random number, obtained the first time a\nnode is started (usually using /dev/urandom).\nThe node will save its ID in the node configuration file, and will use the\nsame ID forever, or at least as long as the node configuration file is not\ndeleted by the system administrator, or a *hard reset* is requested\nvia the [`CLUSTER RESET`](https://redis.io/docs/latest/commands/cluster-reset) command.\n\nThe node ID is used to identify every node across the whole cluster.\nIt is possible for a given node to change its IP address without any need\nto also change the node ID. The cluster is also able to detect the change\nin IP/port and reconfigure using the gossip protocol running over the cluster\nbus.\n\nThe node ID is not the only information associated with each node, but is\nthe only one that is always globally consistent. Every node has also the\nfollowing set of information associated. Some information is about the\ncluster configuration detail of this specific node, and is eventually\nconsistent across the cluster. Some other information, like the last time\na node was pinged, is instead local to each node.\n\nEvery node maintains the following information about other nodes that it is\naware of in the cluster: The node ID, IP and port of the node, a set of\nflags, what is the master of the node if it is flagged as `replica`, last time\nthe node was pinged and the last time the pong was received, the current\n*configuration epoch* of the node (explained later in this specification),\nthe link state and finally the set of hash slots served.\n\nA detailed [explanation of all the node fields](https://redis.io/docs/latest/commands/cluster-nodes) is described in the [`CLUSTER NODES`](https://redis.io/docs/latest/commands/cluster-nodes) documentation.\n\nThe [`CLUSTER NODES`](https://redis.io/docs/latest/commands/cluster-nodes) command can be sent to any node in the cluster and provides the state of the cluster and the information for each node according to the local view the queried node has of the cluster.\n\nThe following is sample output of the [`CLUSTER NODES`](https://redis.io/docs/latest/commands/cluster-nodes) command sent to a master\nnode in a small cluster of three nodes.\n\n    $ redis-cli cluster nodes\n    d1861060fe6a534d42d8a19aeb36600e18785e04 127.0.0.1:6379 myself - 0 1318428930 1 connected 0-1364\n    3886e65cc906bfd9b1f7e7bde468726a052d1dae 127.0.0.1:6380 master - 1318428930 1318428931 2 connected 1365-2729\n    d289c575dcbc4bdd2931585fd4339089e461a27d 127.0.0.1:6381 master - 1318428931 1318428931 3 connected 2730-4095\n\nIn the above listing the different fields are in order: node id, address:port, flags, last ping sent, last pong received, configuration epoch, link state, slots. Details about the above fields will be covered as soon as we talk of specific parts of Redis Cluster."},{"id":"the-cluster-bus","title":"The cluster bus","role":"content","text":"Every Redis Cluster node has an additional TCP port for receiving\nincoming connections from other Redis Cluster nodes. This port will be derived by adding 10000 to the data port or it can be specified with the cluster-port config. \n\nExample 1:\n\nIf a Redis node is listening for client connections on port 6379, \nand you do not add cluster-port parameter in redis.conf,\nthe Cluster bus port 16379 will be opened.\n\nExample 2:\n\nIf a Redis node is listening for client connections on port 6379, \nand you set cluster-port 20000 in redis.conf,\nthe Cluster bus port 20000 will be opened.\n\nNode-to-node communication happens exclusively using the Cluster bus and\nthe Cluster bus protocol: a binary protocol composed of frames\nof different types and sizes. The Cluster bus binary protocol is not\npublicly documented since it is not intended for external software devices\nto talk with Redis Cluster nodes using this protocol. However you can\nobtain more details about the Cluster bus protocol by reading the\n`cluster.h` and `cluster.c` files in the Redis Cluster source code."},{"id":"cluster-topology","title":"Cluster topology","role":"content","text":"Redis Cluster is a full mesh where every node is connected with every other node using a TCP connection.\n\nIn a cluster of N nodes, every node has N-1 outgoing TCP connections, and N-1 incoming connections.\n\nThese TCP connections are kept alive all the time and are not created on demand.\nWhen a node expects a pong reply in response to a ping in the cluster bus, before waiting long enough to mark the node as unreachable, it will try to\nrefresh the connection with the node by reconnecting from scratch.\n\nWhile Redis Cluster nodes form a full mesh, **nodes use a gossip protocol and\na configuration update mechanism in order to avoid exchanging too many\nmessages between nodes during normal conditions**, so the number of messages\nexchanged is not exponential."},{"id":"node-handshake","title":"Node handshake","role":"content","text":"Nodes always accept connections on the cluster bus port, and even reply to\npings when received, even if the pinging node is not trusted.\nHowever, all other packets will be discarded by the receiving node if the\nsending node is not considered part of the cluster.\n\nA node will accept another node as part of the cluster only in two ways:\n\n* If a node presents itself with a `MEET` message ([`CLUSTER MEET`](https://redis.io/docs/latest/commands/cluster-meet) command). A meet message is exactly\nlike a [`PING`](https://redis.io/docs/latest/commands/ping) message, but forces the receiver to accept the node as part of\nthe cluster. Nodes will send `MEET` messages to other nodes **only if** the system administrator requests this via the following command:\n\n    CLUSTER MEET ip port\n\n* A node will also register another node as part of the cluster if a node that is already trusted will gossip about this other node. So if A knows B, and B knows C, eventually B will send gossip messages to A about C. When this happens, A will register C as part of the network, and will try to connect with C.\n\nThis means that as long as we join nodes in any connected graph, they'll eventually form a fully connected graph automatically. This means that the cluster is able to auto-discover other nodes, but only if there is a trusted relationship that was forced by the system administrator.\n\nThis mechanism makes the cluster more robust but prevents different Redis clusters from accidentally mixing after change of IP addresses or other network related events."},{"id":"redirection-and-resharding","title":"Redirection and resharding","role":"content","text":""},{"id":"moved-redirection","title":"MOVED Redirection","role":"content","text":"A Redis client is free to send queries to every node in the cluster, including\nreplica nodes. The node will analyze the query, and if it is acceptable\n(that is, only a single key is mentioned in the query, or the multiple keys\nmentioned are all to the same hash slot) it will lookup what\nnode is responsible for the hash slot where the key or keys belong.\n\nIf the hash slot is served by the node, the query is simply processed, otherwise\nthe node will check its internal hash slot to node map, and will reply\nto the client with a MOVED error, like in the following example:\n\n    GET x\n    -MOVED 3999 127.0.0.1:6381\n\nThe error includes the hash slot of the key (3999) and the endpoint:port of the instance that can serve the query.\nThe client needs to reissue the query to the specified node's endpoint address and port. \nThe endpoint can be either an IP address, a hostname, or it can be empty (e.g. `-MOVED 3999 :6380`). \nAn empty endpoint indicates that the server node has an unknown endpoint, and the client should send the next request to the same endpoint as the current request but with the provided port. \n\nNote that even if the client waits a long time before reissuing the query,\nand in the meantime the cluster configuration changed, the destination node\nwill reply again with a MOVED error if the hash slot 3999 is now served by\nanother node. The same happens if the contacted node had no updated information.\n\nSo while from the point of view of the cluster nodes are identified by\nIDs we try to simplify our interface with the client just exposing a map\nbetween hash slots and Redis nodes identified by endpoint:port pairs.\n\nThe client is not required to, but should try to memorize that hash slot\n3999 is served by 127.0.0.1:6381. This way once a new command needs to\nbe issued it can compute the hash slot of the target key and have a\ngreater chance of choosing the right node.\n\nAn alternative is to just refresh the whole client-side cluster layout\nusing the [`CLUSTER SHARDS`](https://redis.io/docs/latest/commands/cluster-shards), or the deprecated [`CLUSTER SLOTS`](https://redis.io/docs/latest/commands/cluster-slots), command\nwhen a MOVED redirection is received. When a redirection is encountered, it\nis likely multiple slots were reconfigured rather than just one, so updating\nthe client configuration as soon as possible is often the best strategy.\n\nNote that when the Cluster is stable (no ongoing changes in the configuration),\neventually all the clients will obtain a map of hash slots -> nodes, making\nthe cluster efficient, with clients directly addressing the right nodes\nwithout redirections, proxies or other single point of failure entities.\n\nA client **must be also able to handle -ASK redirections** that are described\nlater in this document, otherwise it is not a complete Redis Cluster client."},{"id":"live-reconfiguration","title":"Live reconfiguration","role":"content","text":"Redis Cluster supports the ability to add and remove nodes while the cluster\nis running. Adding or removing a node is abstracted into the same\noperation: moving a hash slot from one node to another. This means\nthat the same basic mechanism can be used in order to rebalance the cluster, add\nor remove nodes, and so forth.\n\n* To add a new node to the cluster an empty node is added to the cluster and some set of hash slots are moved from existing nodes to the new node.\n* To remove a node from the cluster the hash slots assigned to that node are moved to other existing nodes.\n* To rebalance the cluster a given set of hash slots are moved between nodes.\n\nThe core of the implementation is the ability to move hash slots around.\nFrom a practical point of view a hash slot is just a set of keys, so\nwhat Redis Cluster really does during *resharding* is to move keys from\nan instance to another instance. Moving a hash slot means moving all the keys\nthat happen to hash into this hash slot.\n\nTo understand how this works we need to show the [`CLUSTER`](https://redis.io/docs/latest/commands/cluster) subcommands\nthat are used to manipulate the slots translation table in a Redis Cluster node.\n\nThe following subcommands are available (among others not useful in this case):\n\n* [`CLUSTER ADDSLOTS`](https://redis.io/docs/latest/commands/cluster-addslots) slot1 [slot2] ... [slotN]\n* [`CLUSTER DELSLOTS`](https://redis.io/docs/latest/commands/cluster-delslots) slot1 [slot2] ... [slotN]\n* [`CLUSTER ADDSLOTSRANGE`](https://redis.io/docs/latest/commands/cluster-addslotsrange) start-slot1 end-slot1 [start-slot2 end-slot2] ... [start-slotN end-slotN]\n* [`CLUSTER DELSLOTSRANGE`](https://redis.io/docs/latest/commands/cluster-delslotsrange) start-slot1 end-slot1 [start-slot2 end-slot2] ... [start-slotN end-slotN]\n* [`CLUSTER SETSLOT`](https://redis.io/docs/latest/commands/cluster-setslot) slot NODE node\n* [`CLUSTER SETSLOT`](https://redis.io/docs/latest/commands/cluster-setslot) slot MIGRATING node\n* [`CLUSTER SETSLOT`](https://redis.io/docs/latest/commands/cluster-setslot) slot IMPORTING node\n\nThe first four commands, `ADDSLOTS`, `DELSLOTS`, `ADDSLOTSRANGE` and `DELSLOTSRANGE`, are simply used to assign\n(or remove) slots to a Redis node. Assigning a slot means to tell a given\nmaster node that it will be in charge of storing and serving content for\nthe specified hash slot.\n\nAfter the hash slots are assigned they will propagate across the cluster\nusing the gossip protocol, as specified later in the\n*configuration propagation* section.\n\nThe `ADDSLOTS` and `ADDSLOTSRANGE` commands are usually used when a new cluster is created\nfrom scratch to assign each master node a subset of all the 16384 hash\nslots available.\n\nThe `DELSLOTS`  and `DELSLOTSRANGE` are mainly used for manual modification of a cluster configuration\nor for debugging tasks: in practice it is rarely used.\n\nThe `SETSLOT` subcommand is used to assign a slot to a specific node ID if\nthe `SETSLOT <slot> NODE` form is used. Otherwise the slot can be set in the\ntwo special states `MIGRATING` and `IMPORTING`. Those two special states\nare used in order to migrate a hash slot from one node to another.\n\n* When a slot is set as MIGRATING, the node will accept all queries that\nare about this hash slot, but only if the key in question\nexists, otherwise the query is forwarded using a `-ASK` redirection to the\nnode that is target of the migration.\n* When a slot is set as IMPORTING, the node will accept all queries that\nare about this hash slot, but only if the request is\npreceded by an [`ASKING`](https://redis.io/docs/latest/commands/asking) command. If the [`ASKING`](https://redis.io/docs/latest/commands/asking) command was not given\nby the client, the query is redirected to the real hash slot owner via\na `-MOVED` redirection error, as would happen normally.\n\nLet's make this clearer with an example of hash slot migration.\nAssume that we have two Redis master nodes, called A and B.\nWe want to move hash slot 8 from A to B, so we issue commands like this:\n\n* We send B: CLUSTER SETSLOT 8 IMPORTING A\n* We send A: CLUSTER SETSLOT 8 MIGRATING B\n\nAll the other nodes will continue to point clients to node \"A\" every time\nthey are queried with a key that belongs to hash slot 8, so what happens\nis that:\n\n* All queries about existing keys are processed by \"A\".\n* All queries about non-existing keys in A are processed by \"B\", because \"A\" will redirect clients to \"B\".\n\nThis way we no longer create new keys in \"A\".\nIn the meantime, `redis-cli` used during reshardings\nand Redis Cluster configuration will migrate existing keys in\nhash slot 8 from A to B.\nThis is performed using the following command:\n\n    CLUSTER GETKEYSINSLOT slot count\n\nThe above command will return `count` keys in the specified hash slot.\nFor keys returned, `redis-cli` sends node \"A\" a [`MIGRATE`](https://redis.io/docs/latest/commands/migrate) command, that\nwill migrate the specified keys from A to B in an atomic way (both instances\nare locked for the time (usually very small time) needed to migrate keys so\nthere are no race conditions). This is how [`MIGRATE`](https://redis.io/docs/latest/commands/migrate) works:\n\n    MIGRATE target_host target_port \"\" target_database id timeout KEYS key1 key2 ...\n\n[`MIGRATE`](https://redis.io/docs/latest/commands/migrate) will connect to the target instance, send a serialized version of\nthe key, and once an OK code is received, the old key from its own dataset\nwill be deleted. From the point of view of an external client a key exists\neither in A or B at any given time.\n\nIn Redis Cluster there is no need to specify a database other than 0, but\n[`MIGRATE`](https://redis.io/docs/latest/commands/migrate) is a general command that can be used for other tasks not\ninvolving Redis Cluster.\n[`MIGRATE`](https://redis.io/docs/latest/commands/migrate) is optimized to be as fast as possible even when moving complex\nkeys such as long lists, but in Redis Cluster reconfiguring the\ncluster where big keys are present is not considered a wise procedure if\nthere are latency constraints in the application using the database.\n\nWhen the migration process is finally finished, the `SETSLOT <slot> NODE <node-id>` command is sent to the two nodes involved in the migration in order to\nset the slots to their normal state again. The same command is usually\nsent to all other nodes to avoid waiting for the natural\npropagation of the new configuration across the cluster."},{"id":"ask-redirection","title":"ASK redirection","role":"content","text":"In the previous section, we briefly talked about ASK redirection. Why can't\nwe simply use MOVED redirection? Because while MOVED means that\nwe think the hash slot is permanently served by a different node and the\nnext queries should be tried against the specified node. ASK means to\nsend only the next query to the specified node.\n\nThis is needed because the next query about hash slot 8 can be about a\nkey that is still in A, so we always want the client to try A and\nthen B if needed. Since this happens only for one hash slot out of 16384\navailable, the performance hit on the cluster is acceptable.\n\nWe need to force that client behavior, so to make sure\nthat clients will only try node B after A was tried, node B will only\naccept queries of a slot that is set as IMPORTING if the client sends the\nASKING command before sending the query.\n\nBasically the ASKING command sets a one-time flag on the client that forces\na node to serve a query about an IMPORTING slot.\n\nThe full semantics of ASK redirection from the point of view of the client is as follows:\n\n* If ASK redirection is received, send only the query that was redirected to the specified node but continue sending subsequent queries to the old node.\n* Start the redirected query with the ASKING command.\n* Don't yet update local client tables to map hash slot 8 to B.\n\nOnce hash slot 8 migration is completed, A will send a MOVED message and\nthe client may permanently map hash slot 8 to the new endpoint and port pair.\nNote that if a buggy client performs the map earlier this is not\na problem since it will not send the ASKING command before issuing the query,\nso B will redirect the client to A using a MOVED redirection error.\n\nSlots migration is explained in similar terms but with different wording\n(for the sake of redundancy in the documentation) in the [`CLUSTER SETSLOT`](https://redis.io/docs/latest/commands/cluster-setslot)\ncommand documentation."},{"id":"client-connections-and-redirection-handling","title":"Client connections and redirection handling","role":"content","text":"To be efficient, Redis Cluster clients maintain a map of the current slot\nconfiguration. However, this configuration is not *required* to be up to date.\nWhen contacting the wrong node results in a redirection, the client\ncan update its internal slot map accordingly.\n\nClients usually need to fetch a complete list of slots and mapped node\naddresses in two different situations:\n\n* At startup, to populate the initial slots configuration\n* When the client receives a `MOVED` redirection\n\nNote that a client may handle the `MOVED` redirection by updating just the\nmoved slot in its table; however this is usually not efficient because often\nthe configuration of multiple slots will be modified at once. For example, if a\nreplica is promoted to master, all of the slots served by the old master will\nbe remapped). It is much simpler to react to a `MOVED` redirection by\nfetching the full map of slots to nodes from scratch.\n\nClient can issue a [`CLUSTER SLOTS`](https://redis.io/docs/latest/commands/cluster-slots) command to retrieve an array of slot\nranges and the associated master and replica nodes serving the specified ranges.\n\nThe following is an example of output of [`CLUSTER SLOTS`](https://redis.io/docs/latest/commands/cluster-slots):\n\n[code example]\n\nThe first two sub-elements of every element of the returned array are the\nstart and end slots of the range. The additional elements represent address-port\npairs. The first address-port pair is the master serving the slot, and the\nadditional address-port pairs are the replicas serving the same slot. Replicas\nwill be listed only when not in an error condition (i.e., when their FAIL flag is not set).\n\nThe first element in the output above says that slots from 5461 to 10922\n(start and end included) are served by 127.0.0.1:7001, and it is possible\nto scale read-only load contacting the replica at 127.0.0.1:7004.\n\n[`CLUSTER SLOTS`](https://redis.io/docs/latest/commands/cluster-slots) is not guaranteed to return ranges that cover the full\n16384 slots if the cluster is misconfigured, so clients should initialize the\nslots configuration map filling the target nodes with NULL objects, and\nreport an error if the user tries to execute commands about keys\nthat belong to unassigned slots.\n\nBefore returning an error to the caller when a slot is found to\nbe unassigned, the client should try to fetch the slots configuration\nagain to check if the cluster is now configured properly."},{"id":"multi-keys-operations","title":"Multi-keys operations","role":"content","text":"Using hash tags, clients are free to use multi-key operations.\nFor example the following operation is valid:\n\n    MSET {user:1000}.name Angela {user:1000}.surname White\n\nMulti-key operations may become unavailable when a resharding of the\nhash slot the keys belong to is in progress.\n\nMore specifically, even during a resharding the multi-key operations targeting\nkeys that all exist and all still hash to the same slot (either the source or\ndestination node) are still available.\n\nOperations on keys that don't exist or are - during the resharding - split\nbetween the source and destination nodes, will generate a `-TRYAGAIN` error.\nThe client can try the operation after some time, or report back the error.\n\nAs soon as migration of the specified hash slot has terminated, all\nmulti-key operations are available again for that hash slot."},{"id":"scaling-reads-using-replica-nodes","title":"Scaling reads using replica nodes","role":"content","text":"Normally replica nodes will redirect clients to the authoritative master for\nthe hash slot involved in a given command, however clients can use replicas\nin order to scale reads using the [`READONLY`](https://redis.io/docs/latest/commands/readonly) command.\n\n[`READONLY`](https://redis.io/docs/latest/commands/readonly) tells a Redis Cluster replica node that the client is ok reading\npossibly stale data and is not interested in running write queries.\n\nWhen the connection is in readonly mode, the cluster will send a redirection\nto the client only if the operation involves keys not served\nby the replica's master node. This may happen because:\n\n1. The client sent a command about hash slots never served by the master of this replica.\n2. The cluster was reconfigured (for example resharded) and the replica is no longer able to serve commands for a given hash slot.\n\nWhen this happens the client should update its hash slot map as explained in\nthe previous sections.\n\nThe readonly state of the connection can be cleared using the [`READWRITE`](https://redis.io/docs/latest/commands/readwrite) command."},{"id":"fault-tolerance","title":"Fault Tolerance","role":"content","text":""},{"id":"heartbeat-and-gossip-messages","title":"Heartbeat and gossip messages","role":"content","text":"Redis Cluster nodes continuously exchange ping and pong packets. Those two kinds of packets have the same structure, and both carry important configuration information. The only actual difference is the message type field. We'll refer to the sum of ping and pong packets as *heartbeat packets*.\n\nUsually nodes send ping packets that will trigger the receivers to reply with pong packets. However this is not necessarily true. It is possible for nodes to just send pong packets to send information to other nodes about their configuration, without triggering a reply. This is useful, for example, in order to broadcast a new configuration as soon as possible.\n\nUsually a node will ping a few random nodes every second so that the total number of ping packets sent (and pong packets received) by each node is a constant amount regardless of the number of nodes in the cluster.\n\nHowever every node makes sure to ping every other node that hasn't sent a ping or received a pong for longer than half the `NODE_TIMEOUT` time. Before `NODE_TIMEOUT` has elapsed, nodes also try to reconnect the TCP link with another node to make sure nodes are not believed to be unreachable only because there is a problem in the current TCP connection.\n\nThe number of messages globally exchanged can be sizable if `NODE_TIMEOUT` is set to a small figure and the number of nodes (N) is very large, since every node will try to ping every other node for which they don't have fresh information every half the `NODE_TIMEOUT` time.\n\nFor example in a 100 node cluster with a node timeout set to 60 seconds, every node will try to send 99 pings every 30 seconds, with a total amount of pings of 3.3 per second. Multiplied by 100 nodes, this is 330 pings per second in the total cluster.\n\nThere are ways to lower the number of messages, however there have been no\nreported issues with the bandwidth currently used by Redis Cluster failure\ndetection, so for now the obvious and direct design is used. Note that even\nin the above example, the 330 packets per second exchanged are evenly\ndivided among 100 different nodes, so the traffic each node receives\nis acceptable."},{"id":"heartbeat-packet-content","title":"Heartbeat packet content","role":"content","text":"Ping and pong packets contain a header that is common to all types of packets (for instance packets to request a failover vote), and a special gossip section that is specific to Ping and Pong packets.\n\nThe common header has the following information:\n\n* Node ID, a 160 bit pseudorandom string that is assigned the first time a node is created and remains the same for all the life of a Redis Cluster node.\n* The `currentEpoch` and `configEpoch` fields of the sending node that are used to mount the distributed algorithms used by Redis Cluster (this is explained in detail in the next sections). If the node is a replica the `configEpoch` is the last known `configEpoch` of its master.\n* The node flags, indicating if the node is a replica, a master, and other single-bit node information.\n* A bitmap of the hash slots served by the sending node, or if the node is a replica, a bitmap of the slots served by its master.\n* The sender TCP base port that is the port used by Redis to accept client commands.\n* The cluster port that is the port used by Redis for node-to-node communication.\n* The state of the cluster from the point of view of the sender (down or ok).\n* The master node ID of the sending node, if it is a replica.\n\nPing and pong packets also contain a gossip section. This section offers to the receiver a view of what the sender node thinks about other nodes in the cluster. The gossip section only contains information about a few random nodes among the set of nodes known to the sender. The number of nodes mentioned in a gossip section is proportional to the cluster size.\n\nFor every node added in the gossip section the following fields are reported:\n\n* Node ID.\n* IP and port of the node.\n* Node flags.\n\nGossip sections allow receiving nodes to get information about the state of other nodes from the point of view of the sender. This is useful both for failure detection and to discover other nodes in the cluster."},{"id":"failure-detection","title":"Failure detection","role":"content","text":"Redis Cluster failure detection is used to recognize when a master or replica node is no longer reachable by the majority of nodes and then respond by promoting a replica to the role of master. When replica promotion is not possible the cluster is put in an error state to stop receiving queries from clients.\n\nAs already mentioned, every node takes a list of flags associated with other known nodes. There are two flags that are used for failure detection that are called `PFAIL` and `FAIL`. `PFAIL` means *Possible failure*, and is a non-acknowledged failure type. `FAIL` means that a node is failing and that this condition was confirmed by a majority of masters within a fixed amount of time.\n\n**PFAIL flag:**\n\nA node flags another node with the `PFAIL` flag when the node is not reachable for more than `NODE_TIMEOUT` time. Both master and replica nodes can flag another node as `PFAIL`, regardless of its type.\n\nThe concept of non-reachability for a Redis Cluster node is that we have an **active ping** (a ping that we sent for which we have yet to get a reply) pending for longer than `NODE_TIMEOUT`. For this mechanism to work the `NODE_TIMEOUT` must be large compared to the network round trip time. In order to add reliability during normal operations, nodes will try to reconnect with other nodes in the cluster as soon as half of the `NODE_TIMEOUT` has elapsed without a reply to a ping. This mechanism ensures that connections are kept alive so broken connections usually won't result in false failure reports between nodes.\n\n**FAIL flag:**\n\nThe `PFAIL` flag alone is just local information every node has about other nodes, but it is not sufficient to trigger a replica promotion. For a node to be considered down the `PFAIL` condition needs to be escalated to a `FAIL` condition.\n\nAs outlined in the node heartbeats section of this document, every node sends gossip messages to every other node including the state of a few random known nodes. Every node eventually receives a set of node flags for every other node. This way every node has a mechanism to signal other nodes about failure conditions they have detected.\n\nA `PFAIL` condition is escalated to a `FAIL` condition when the following set of conditions are met:\n\n* Some node, that we'll call A, has another node B flagged as `PFAIL`.\n* Node A collected, via gossip sections, information about the state of B from the point of view of the majority of masters in the cluster.\n* The majority of masters signaled the `PFAIL` or `FAIL` condition within `NODE_TIMEOUT * FAIL_REPORT_VALIDITY_MULT` time. (The validity factor is set to 2 in the current implementation, so this is just two times the `NODE_TIMEOUT` time).\n\nIf all the above conditions are true, Node A will:\n\n* Mark the node as `FAIL`.\n* Send a `FAIL` message (as opposed to a `FAIL` condition within a heartbeat message) to all the reachable nodes.\n\nThe `FAIL` message will force every receiving node to mark the node in `FAIL` state, whether or not it already flagged the node in `PFAIL` state.\n\nNote that *the FAIL flag is mostly one way*. That is, a node can go from `PFAIL` to `FAIL`, but a `FAIL` flag can only be cleared in the following situations:\n\n* The node is already reachable and is a replica. In this case the `FAIL` flag can be cleared as replicas are not failed over.\n* The node is already reachable and is a master not serving any slot. In this case the `FAIL` flag can be cleared as masters without slots do not really participate in the cluster and are waiting to be configured in order to join the cluster.\n* The node is already reachable and is a master, but a long time (N times the `NODE_TIMEOUT`) has elapsed without any detectable replica promotion. It's better for it to rejoin the cluster and continue in this case.\n\nIt is useful to note that while the `PFAIL` -> `FAIL` transition uses a form of agreement, the agreement used is weak:\n\n1. Nodes collect views of other nodes over some time period, so even if the majority of master nodes need to \"agree\", actually this is just state that we collected from different nodes at different times and we are not sure, nor we require, that at a given moment the majority of masters agreed. However we discard failure reports which are old, so the failure was signaled by the majority of masters within a window of time.\n2. While every node detecting the `FAIL` condition will force that condition on other nodes in the cluster using the `FAIL` message, there is no way to ensure the message will reach all the nodes. For instance a node may detect the `FAIL` condition and because of a partition will not be able to reach any other node.\n\nHowever the Redis Cluster failure detection has a liveness requirement: eventually all the nodes should agree about the state of a given node. There are two cases that can originate from split brain conditions. Either some minority of nodes believe the node is in `FAIL` state, or a minority of nodes believe the node is not in `FAIL` state. In both the cases eventually the cluster will have a single view of the state of a given node:\n\n**Case 1**: If a majority of masters have flagged a node as `FAIL`, because of failure detection and the *chain effect* it generates, every other node will eventually flag the master as `FAIL`, since in the specified window of time enough failures will be reported.\n\n**Case 2**: When only a minority of masters have flagged a node as `FAIL`, the replica promotion will not happen (as it uses a more formal algorithm that makes sure everybody knows about the promotion eventually) and every node will clear the `FAIL` state as per the `FAIL` state clearing rules above (i.e. no promotion after N times the `NODE_TIMEOUT` has elapsed).\n\n**The `FAIL` flag is only used as a trigger to run the safe part of the algorithm** for the replica promotion. In theory a replica may act independently and start a replica promotion when its master is not reachable, and wait for the masters to refuse to provide the acknowledgment if the master is actually reachable by the majority. However the added complexity of the `PFAIL -> FAIL` state, the weak agreement, and the `FAIL` message forcing the propagation of the state in the shortest amount of time in the reachable part of the cluster, have practical advantages. Because of these mechanisms, usually all the nodes will stop accepting writes at about the same time if the cluster is in an error state. This is a desirable feature from the point of view of applications using Redis Cluster. Also erroneous election attempts initiated by replicas that can't reach its master due to local problems (the master is otherwise reachable by the majority of other master nodes) are avoided."},{"id":"configuration-handling-propagation-and-failovers","title":"Configuration handling, propagation, and failovers","role":"configuration","text":""},{"id":"cluster-current-epoch","title":"Cluster current epoch","role":"content","text":"Redis Cluster uses a concept similar to the Raft algorithm \"term\". In Redis Cluster the term is called epoch instead, and it is used in order to give incremental versioning to events. When multiple nodes provide conflicting information, it becomes possible for another node to understand which state is the most up to date.\n\nThe `currentEpoch` is a 64 bit unsigned number.\n\nAt node creation every Redis Cluster node, both replicas and master nodes, set the `currentEpoch` to 0.\n\nEvery time a packet is received from another node, if the epoch of the sender (part of the cluster bus messages header) is greater than the local node epoch, the `currentEpoch` is updated to the sender epoch.\n\nBecause of these semantics, eventually all the nodes will agree to the greatest `currentEpoch` in the cluster.\n\nThis information is used when the state of the cluster is changed and a node seeks agreement in order to perform some action.\n\nCurrently this happens only during replica promotion, as described in the next section. Basically the epoch is a logical clock for the cluster and dictates that given information wins over one with a smaller epoch."},{"id":"configuration-epoch","title":"Configuration epoch","role":"configuration","text":"Every master always advertises its `configEpoch` in ping and pong packets along with a bitmap advertising the set of slots it serves.\n\nThe `configEpoch` is set to zero in masters when a new node is created.\n\nA new `configEpoch` is created during replica election. replicas trying to replace\nfailing masters increment their epoch and try to get authorization from\na majority of masters. When a replica is authorized, a new unique `configEpoch`\nis created and the replica turns into a master using the new `configEpoch`.\n\nAs explained in the next sections the `configEpoch` helps to resolve conflicts when different nodes claim divergent configurations (a condition that may happen because of network partitions and node failures).\n\nreplica nodes also advertise the `configEpoch` field in ping and pong packets, but in the case of replicas the field represents the `configEpoch` of its master as of the last time they exchanged packets. This allows other instances to detect when a replica has an old configuration that needs to be updated (master nodes will not grant votes to replicas with an old configuration).\n\nEvery time the `configEpoch` changes for some known node, it is permanently stored in the nodes.conf file by all the nodes that receive this information. The same also happens for the `currentEpoch` value. These two variables are guaranteed to be saved and `fsync-ed` to disk when updated before a node continues its operations.\n\nThe `configEpoch` values generated using a simple algorithm during failovers\nare guaranteed to be new, incremental, and unique."},{"id":"replica-election-and-promotion","title":"Replica election and promotion","role":"content","text":"Replica election and promotion is handled by replica nodes, with the help of master nodes that vote for the replica to promote.\nA replica election happens when a master is in `FAIL` state from the point of view of at least one of its replicas that has the prerequisites in order to become a master.\n\nIn order for a replica to promote itself to master, it needs to start an election and win it. All the replicas for a given master can start an election if the master is in `FAIL` state, however only one replica will win the election and promote itself to master.\n\nA replica starts an election when the following conditions are met:\n\n* The replica's master is in `FAIL` state.\n* The master was serving a non-zero number of slots.\n* The replica replication link was disconnected from the master for no longer than a given amount of time, in order to ensure the promoted replica's data is reasonably fresh. This time is user configurable.\n\nIn order to be elected, the first step for a replica is to increment its `currentEpoch` counter, and request votes from master instances.\n\nVotes are requested by the replica by broadcasting a `FAILOVER_AUTH_REQUEST` packet to every master node of the cluster. Then it waits for a maximum time of two times the `NODE_TIMEOUT` for replies to arrive (but always for at least 2 seconds).\n\nOnce a master has voted for a given replica, replying positively with a `FAILOVER_AUTH_ACK`, it can no longer vote for another replica of the same master for a period of `NODE_TIMEOUT * 2`. In this period it will not be able to reply to other authorization requests for the same master. This is not needed to guarantee safety, but useful for preventing multiple replicas from getting elected (even if with a different `configEpoch`) at around the same time, which is usually not wanted.\n\nA replica discards any `AUTH_ACK` replies with an epoch that is less than the `currentEpoch` at the time the vote request was sent. This ensures it doesn't count votes intended for a previous election.\n\nOnce the replica receives ACKs from the majority of masters, it wins the election.\nOtherwise if the majority is not reached within the period of two times `NODE_TIMEOUT` (but always at least 2 seconds), the election is aborted and a new one will be tried again after `NODE_TIMEOUT * 4` (and always at least 4 seconds)."},{"id":"replica-rank","title":"Replica rank","role":"content","text":"As soon as a master is in `FAIL` state, a replica waits a short period of time before trying to get elected. That delay is computed as follows:\n\n    DELAY = 500 milliseconds + random delay between 0 and 500 milliseconds +\n            REPLICA_RANK * 1000 milliseconds.\n\nThe fixed delay ensures that we wait for the `FAIL` state to propagate across the cluster, otherwise the replica may try to get elected while the masters are still unaware of the `FAIL` state, refusing to grant their vote.\n\nThe random delay is used to desynchronize replicas so they're unlikely to start an election at the same time.\n\nThe `REPLICA_RANK` is the rank of this replica regarding the amount of replication data it has processed from the master.\nReplicas exchange messages when the master is failing in order to establish a (best effort) rank:\nthe replica with the most updated replication offset is at rank 0, the second most updated at rank 1, and so forth.\nIn this way the most updated replicas try to get elected before others.\n\nRank order is not strictly enforced; if a replica of higher rank fails to be\nelected, the others will try shortly.\n\nOnce a replica wins the election, it obtains a new unique and incremental `configEpoch` which is higher than that of any other existing master. It starts advertising itself as master in ping and pong packets, providing the set of served slots with a `configEpoch` that will win over the past ones.\n\nIn order to speedup the reconfiguration of other nodes, a pong packet is broadcast to all the nodes of the cluster. Currently unreachable nodes will eventually be reconfigured when they receive a ping or pong packet from another node or will receive an `UPDATE` packet from another node if the information it publishes via heartbeat packets are detected to be out of date.\n\nThe other nodes will detect that there is a new master serving the same slots served by the old master but with a greater `configEpoch`, and will upgrade their configuration. Replicas of the old master (or the failed over master if it rejoins the cluster) will not just upgrade the configuration but will also reconfigure to replicate from the new master. How nodes rejoining the cluster are configured is explained in the next sections."},{"id":"masters-reply-to-replica-vote-request","title":"Masters reply to replica vote request","role":"content","text":"In the previous section, we discussed how replicas try to get elected. This section explains what happens from the point of view of a master that is requested to vote for a given replica.\n\nMasters receive requests for votes in form of `FAILOVER_AUTH_REQUEST` requests from replicas.\n\nFor a vote to be granted the following conditions need to be met:\n\n1. A master only votes a single time for a given epoch, and refuses to vote for older epochs: every master has a lastVoteEpoch field and will refuse to vote again as long as the `currentEpoch` in the auth request packet is not greater than the lastVoteEpoch. When a master replies positively to a vote request, the lastVoteEpoch is updated accordingly, and safely stored on disk.\n2. A master votes for a replica only if the replica's master is flagged as `FAIL`.\n3. Auth requests with a `currentEpoch` that is less than the master `currentEpoch` are ignored. Because of this the master reply will always have the same `currentEpoch` as the auth request. If the same replica asks again to be voted, incrementing the `currentEpoch`, it is guaranteed that an old delayed reply from the master can not be accepted for the new vote.\n\nExample of the issue caused by not using rule number 3:\n\nMaster `currentEpoch` is 5, lastVoteEpoch is 1 (this may happen after a few failed elections)\n\n* Replica `currentEpoch` is 3.\n* Replica tries to be elected with epoch 4 (3+1), master replies with an ok with `currentEpoch` 5, however the reply is delayed.\n* Replica will try to be elected again, at a later time, with epoch 5 (4+1), the delayed reply reaches the replica with `currentEpoch` 5, and is accepted as valid.\n\n4. Masters don't vote for a replica of the same master before `NODE_TIMEOUT * 2` has elapsed if a replica of that master was already voted for. This is not strictly required as it is not possible for two replicas to win the election in the same epoch. However, in practical terms it ensures that when a replica is elected it has plenty of time to inform the other replicas and avoid the possibility that another replica will win a new election, performing an unnecessary second failover.\n5. Masters make no effort to select the best replica in any way. If the replica's master is in `FAIL` state and the master did not vote in the current term, a positive vote is granted. The best replica is the most likely to start an election and win it before the other replicas, since it will usually be able to start the voting process earlier because of its *higher rank* as explained in the previous section.\n6. When a master refuses to vote for a given replica there is no negative response, the request is simply ignored.\n7. Masters don't vote for replicas sending a `configEpoch` that is less than any `configEpoch` in the master table for the slots claimed by the replica. Remember that the replica sends the `configEpoch` of its master, and the bitmap of the slots served by its master. This means that the replica requesting the vote must have a configuration for the slots it wants to failover that is newer or equal the one of the master granting the vote."},{"id":"practical-example-of-configuration-epoch-usefulness-during-partitions","title":"Practical example of configuration epoch usefulness during partitions","role":"content","text":"This section illustrates how the epoch concept is used to make the replica promotion process more resistant to partitions.\n\n* A master is no longer reachable indefinitely. The master has three replicas A, B, C.\n* Replica A wins the election and is promoted to master.\n* A network partition makes A not available for the majority of the cluster.\n* Replica B wins the election and is promoted as master.\n* A partition makes B not available for the majority of the cluster.\n* The previous partition is fixed, and A is available again.\n\nAt this point B is down and A is available again with a role of master (actually `UPDATE` messages would reconfigure it promptly, but here we assume all `UPDATE` messages were lost). At the same time, replica C will try to get elected in order to fail over B. This is what happens:\n\n1. C will try to get elected and will succeed, since for the majority of masters its master is actually down. It will obtain a new incremental `configEpoch`.\n2. A will not be able to claim to be the master for its hash slots, because the other nodes already have the same hash slots associated with a higher configuration epoch (the one of B) compared to the one published by A.\n3. So, all the nodes will upgrade their table to assign the hash slots to C, and the cluster will continue its operations.\n\nAs you'll see in the next sections, a stale node rejoining a cluster\nwill usually get notified as soon as possible about the configuration change\nbecause as soon as it pings any other node, the receiver will detect it\nhas stale information and will send an `UPDATE` message."},{"id":"hash-slots-configuration-propagation","title":"Hash slots configuration propagation","role":"content","text":"An important part of Redis Cluster is the mechanism used to propagate the information about which cluster node is serving a given set of hash slots. This is vital to both the startup of a fresh cluster and the ability to upgrade the configuration after a replica was promoted to serve the slots of its failing master.\n\nThe same mechanism allows nodes partitioned away for an indefinite amount of\ntime to rejoin the cluster in a sensible way.\n\nThere are two ways hash slot configurations are propagated:\n\n1. Heartbeat messages. The sender of a ping or pong packet always adds information about the set of hash slots it (or its master, if it is a replica) serves.\n2. `UPDATE` messages. Since in every heartbeat packet there is information about the sender `configEpoch` and set of hash slots served, if a receiver of a heartbeat packet finds the sender information is stale, it will send a packet with new information, forcing the stale node to update its info.\n\nThe receiver of a heartbeat or `UPDATE` message uses certain simple rules in\norder to update its table mapping hash slots to nodes. When a new Redis Cluster node is created, its local hash slot table is simply initialized to `NULL` entries so that each hash slot is not bound or linked to any node. This looks similar to the following:\n\n[code example]\n\nThe first rule followed by a node in order to update its hash slot table is the following:\n\n**Rule 1**: If a hash slot is unassigned (set to `NULL`), and a known node claims it, I'll modify my hash slot table and associate the claimed hash slots to it.\n\nSo if we receive a heartbeat from node A claiming to serve hash slots 1 and 2 with a configuration epoch value of 3, the table will be modified to:\n\n[code example]\n\nWhen a new cluster is created, a system administrator needs to manually assign (using the [`CLUSTER ADDSLOTS`](https://redis.io/docs/latest/commands/cluster-addslots) command, via the redis-cli command line tool, or by any other means) the slots served by each master node only to the node itself, and the information will rapidly propagate across the cluster.\n\nHowever this rule is not enough. We know that hash slot mapping can change\nduring two events:\n\n1. A replica replaces its master during a failover.\n2. A slot is resharded from a node to a different one.\n\nFor now let's focus on failovers. When a replica fails over its master, it obtains\na configuration epoch which is guaranteed to be greater than the one of its\nmaster (and more generally greater than any other configuration epoch\ngenerated previously). For example node B, which is a replica of A, may failover\nA with configuration epoch of 4. It will start to send heartbeat packets\n(the first time mass-broadcasting cluster-wide) and because of the following\nsecond rule, receivers will update their hash slot tables:\n\n**Rule 2**: If a hash slot is already assigned, and a known node is advertising it using a `configEpoch` that is greater than the `configEpoch` of the master currently associated with the slot, I'll rebind the hash slot to the new node.\n\nSo after receiving messages from B that claim to serve hash slots 1 and 2 with configuration epoch of 4, the receivers will update their table in the following way:\n\n[code example]\n\nLiveness property: because of the second rule, eventually all nodes in the cluster will agree that the owner of a slot is the one with the greatest `configEpoch` among the nodes advertising it.\n\nThis mechanism in Redis Cluster is called **last failover wins**.\n\nThe same happens during resharding. When a node importing a hash slot completes\nthe import operation, its configuration epoch is incremented to make sure the\nchange will be propagated throughout the cluster."},{"id":"update-messages-a-closer-look","title":"UPDATE messages, a closer look","role":"content","text":"With the previous section in mind, it is easier to see how update messages\nwork. Node A may rejoin the cluster after some time. It will send heartbeat\npackets where it claims it serves hash slots 1 and 2 with configuration epoch\nof 3. All the receivers with updated information will instead see that\nthe same hash slots are associated with node B having a higher configuration\nepoch. Because of this they'll send an `UPDATE` message to A with the new\nconfiguration for the slots. A will update its configuration because of the\n**rule 2** above."},{"id":"how-nodes-rejoin-the-cluster","title":"How nodes rejoin the cluster","role":"content","text":"The same basic mechanism is used when a node rejoins a cluster.\nContinuing with the example above, node A will be notified\nthat hash slots 1 and 2 are now served by B. Assuming that these two were\nthe only hash slots served by A, the count of hash slots served by A will\ndrop to 0! So A will **reconfigure to be a replica of the new master**.\n\nThe actual rule followed is a bit more complex than this. In general it may\nhappen that A rejoins after a lot of time, in the meantime it may happen that\nhash slots originally served by A are served by multiple nodes, for example\nhash slot 1 may be served by B, and hash slot 2 by C.\n\nSo the actual *Redis Cluster node role switch rule* is: **A master node will change its configuration to replicate (be a replica of) the node that stole its last hash slot**.\n\nDuring reconfiguration, eventually the number of served hash slots will drop to zero, and the node will reconfigure accordingly. Note that in the base case this just means that the old master will be a replica of the replica that replaced it after a failover. However in the general form the rule covers all possible cases.\n\nReplicas do exactly the same: they reconfigure to replicate the node that\nstole the last hash slot of its former master."},{"id":"replica-migration","title":"Replica migration","role":"content","text":"Redis Cluster implements a concept called *replica migration* in order to\nimprove the availability of the system. The idea is that in a cluster with\na master-replica setup, if the map between replicas and masters is fixed\navailability is limited over time if multiple independent failures of single\nnodes happen.\n\nFor example in a cluster where every master has a single replica, the cluster\ncan continue operations as long as either the master or the replica fail, but not\nif both fail the same time. However there is a class of failures that are\nthe independent failures of single nodes caused by hardware or software issues\nthat can accumulate over time. For example:\n\n* Master A has a single replica A1.\n* Master A fails. A1 is promoted as new master.\n* Three hours later A1 fails in an independent manner (unrelated to the failure of A). No other replica is available for promotion since node A is still down. The cluster cannot continue normal operations.\n\nIf the map between masters and replicas is fixed, the only way to make the cluster\nmore resistant to the above scenario is to add replicas to every master, however\nthis is costly as it requires more instances of Redis to be executed, more\nmemory, and so forth.\n\nAn alternative is to create an asymmetry in the cluster, and let the cluster\nlayout automatically change over time. For example the cluster may have three\nmasters A, B, C. A and B have a single replica each, A1 and B1. However the master\nC is different and has two replicas: C1 and C2.\n\nReplica migration is the process of automatic reconfiguration of a replica\nin order to *migrate* to a master that has no longer coverage (no working\nreplicas). With replica migration the scenario mentioned above turns into the\nfollowing:\n\n* Master A fails. A1 is promoted.\n* C2 migrates as replica of A1, that is otherwise not backed by any replica.\n* Three hours later A1 fails as well.\n* C2 is promoted as new master to replace A1.\n* The cluster can continue the operations."},{"id":"replica-migration-algorithm","title":"Replica migration algorithm","role":"content","text":"The migration algorithm does not use any form of agreement since the replica\nlayout in a Redis Cluster is not part of the cluster configuration that needs\nto be consistent and/or versioned with config epochs. Instead it uses an\nalgorithm to avoid mass-migration of replicas when a master is not backed.\nThe algorithm guarantees that eventually (once the cluster configuration is\nstable) every master will be backed by at least one replica.\n\nThis is how the algorithm works. To start we need to define what is a\n*good replica* in this context: a good replica is a replica not in `FAIL` state\nfrom the point of view of a given node.\n\nThe execution of the algorithm is triggered in every replica that detects that\nthere is at least a single master without good replicas. However among all the\nreplicas detecting this condition, only a subset should act. This subset is\nactually often a single replica unless different replicas have in a given moment\na slightly different view of the failure state of other nodes.\n\nThe *acting replica* is the replica among the masters with the maximum number\nof attached replicas, that is not in FAIL state and has the smallest node ID.\n\nSo for example if there are 10 masters with 1 replica each, and 2 masters with\n5 replicas each, the replica that will try to migrate is - among the 2 masters\nhaving 5 replicas - the one with the lowest node ID. Given that no agreement\nis used, it is possible that when the cluster configuration is not stable,\na race condition occurs where multiple replicas believe themselves to be\nthe non-failing replica with the lower node ID (it is unlikely for this to happen\nin practice). If this happens, the result is multiple replicas migrating to the\nsame master, which is harmless. If the race happens in a way that will leave\nthe ceding master without replicas, as soon as the cluster is stable again\nthe algorithm will be re-executed again and will migrate a replica back to\nthe original master.\n\nEventually every master will be backed by at least one replica. However,\nthe normal behavior is that a single replica migrates from a master with\nmultiple replicas to an orphaned master.\n\nThe algorithm is controlled by a user-configurable parameter called\n`cluster-migration-barrier`: the number of good replicas a master\nmust be left with before a replica can migrate away. For example, if this\nparameter is set to 2, a replica can try to migrate only if its master remains\nwith two working replicas."},{"id":"configepoch-conflicts-resolution-algorithm","title":"configEpoch conflicts resolution algorithm","role":"content","text":"When new `configEpoch` values are created via replica promotion during\nfailovers, they are guaranteed to be unique.\n\nHowever there are two distinct events where new configEpoch values are\ncreated in an unsafe way, just incrementing the local `currentEpoch` of\nthe local node and hoping there are no conflicts at the same time.\nBoth the events are system-administrator triggered:\n\n1. [`CLUSTER FAILOVER`](https://redis.io/docs/latest/commands/cluster-failover) command with `TAKEOVER` option is able to manually promote a replica node into a master *without the majority of masters being available*. This is useful, for example, in multi data center setups.\n2. Migration of slots for cluster rebalancing also generates new configuration epochs inside the local node without agreement for performance reasons.\n\nSpecifically, during manual resharding, when a hash slot is migrated from\na node A to a node B, the resharding program will force B to upgrade\nits configuration to an epoch which is the greatest found in the cluster,\nplus 1 (unless the node is already the one with the greatest configuration\nepoch), without requiring agreement from other nodes.\nUsually a real world resharding involves moving several hundred hash slots\n(especially in small clusters). Requiring an agreement to generate new\nconfiguration epochs during resharding, for each hash slot moved, is\ninefficient. Moreover it requires a fsync in each of the cluster nodes\nevery time in order to store the new configuration. Because of the way it is\nperformed instead, we only need a new config epoch when the first hash slot is moved,\nmaking it much more efficient in production environments.\n\nHowever because of the two cases above, it is possible (though unlikely) to end\nwith multiple nodes having the same configuration epoch. A resharding operation\nperformed by the system administrator, and a failover happening at the same\ntime (plus a lot of bad luck) could cause `currentEpoch` collisions if\nthey are not propagated fast enough.\n\nMoreover, software bugs and filesystem corruptions can also contribute\nto multiple nodes having the same configuration epoch.\n\nWhen masters serving different hash slots have the same `configEpoch`, there\nare no issues. It is more important that replicas failing over a master have\nunique configuration epochs.\n\nThat said, manual interventions or resharding may change the cluster\nconfiguration in different ways. The Redis Cluster main liveness property\nrequires that slot configurations always converge, so under every circumstance\nwe really want all the master nodes to have a different `configEpoch`.\n\nIn order to enforce this, **a conflict resolution algorithm** is used in the\nevent that two nodes end up with the same `configEpoch`.\n\n* IF a master node detects another master node is advertising itself with\nthe same `configEpoch`.\n* AND IF the node has a lexicographically smaller Node ID compared to the other node claiming the same `configEpoch`.\n* THEN it increments its `currentEpoch` by 1, and uses it as the new `configEpoch`.\n\nIf there are any set of nodes with the same `configEpoch`, all the nodes but the one with the greatest Node ID will move forward, guaranteeing that, eventually, every node will pick a unique configEpoch regardless of what happened.\n\nThis mechanism also guarantees that after a fresh cluster is created, all\nnodes start with a different `configEpoch` (even if this is not actually\nused) since `redis-cli` makes sure to use [`CLUSTER SET-CONFIG-EPOCH`](https://redis.io/docs/latest/commands/cluster-set-config-epoch) at startup.\nHowever if for some reason a node is left misconfigured, it will update\nits configuration to a different configuration epoch automatically."},{"id":"node-resets","title":"Node resets","role":"content","text":"Nodes can be software reset (without restarting them) in order to be reused\nin a different role or in a different cluster. This is useful in normal\noperations, in testing, and in cloud environments where a given node can\nbe reprovisioned to join a different set of nodes to enlarge or create a new\ncluster.\n\nIn Redis Cluster nodes are reset using the [`CLUSTER RESET`](https://redis.io/docs/latest/commands/cluster-reset) command. The\ncommand is provided in two variants:\n\n* `CLUSTER RESET SOFT`\n* `CLUSTER RESET HARD`\n\nThe command must be sent directly to the node to reset. If no reset type is\nprovided, a soft reset is performed.\n\nThe following is a list of operations performed by a reset:\n\n1. Soft and hard reset: If the node is a replica, it is turned into a master, and its dataset is discarded. If the node is a master and contains keys the reset operation is aborted.\n2. Soft and hard reset: All the slots are released, and the manual failover state is reset.\n3. Soft and hard reset: All the other nodes in the nodes table are removed, so the node no longer knows any other node.\n4. Hard reset only: `currentEpoch`, `configEpoch`, and `lastVoteEpoch` are set to 0.\n5. Hard reset only: the Node ID is changed to a new random ID.\n\nMaster nodes with non-empty data sets can't be reset (since normally you want to reshard data to the other nodes). However, under special conditions when this is appropriate (e.g. when a cluster is totally destroyed with the intent of creating a new one), [`FLUSHALL`](https://redis.io/docs/latest/commands/flushall) must be executed before proceeding with the reset."},{"id":"removing-nodes-from-a-cluster","title":"Removing nodes from a cluster","role":"content","text":"It is possible to practically remove a node from an existing cluster by\nresharding all its data to other nodes (if it is a master node) and\nshutting it down. However, the other nodes will still remember its node\nID and address, and will attempt to connect with it.\n\nFor this reason, when a node is removed we want to also remove its entry\nfrom all the other nodes tables. This is accomplished by using the\n`CLUSTER FORGET <node-id>` command.\n\nThe command does two things:\n\n1. It removes the node with the specified node ID from the nodes table.\n2. It sets a 60 second ban which prevents a node with the same node ID from being re-added.\n\nThe second operation is needed because Redis Cluster uses gossip in order to auto-discover nodes, so removing the node X from node A, could result in node B gossiping about node X to A again. Because of the 60 second ban, the Redis Cluster administration tools have 60 seconds in order to remove the node from all the nodes, preventing the re-addition of the node due to auto discovery.\n\nFurther information is available in the [`CLUSTER FORGET`](https://redis.io/docs/latest/commands/cluster-forget) documentation."},{"id":"publishsubscribe","title":"Publish/Subscribe","role":"content","text":"In a Redis Cluster, clients can subscribe to every node, and can also\npublish to every other node. The cluster will make sure that published\nmessages are forwarded as needed.\n\nThe clients can send SUBSCRIBE to any node and can also send PUBLISH to any node. \nIt will simply broadcast each published message to all other nodes.\n\nRedis 7.0 and later features sharded pub/sub, in which shard channels are assigned to slots by the same algorithm used to assign keys to slots. \nA shard message must be sent to a node that owns the slot the shard channel is hashed to. \nThe cluster makes sure the published shard messages are forwarded to all nodes in the shard, so clients can subscribe to a shard channel by connecting to either the master responsible for the slot, or to any of its replicas."},{"id":"appendix","title":"Appendix","role":"content","text":""},{"id":"appendix-a-crc16-reference-implementation-in-ansi-c","title":"Appendix A: CRC16 reference implementation in ANSI C","role":"content","text":"/*\n     * Copyright 2001-2010 Georges Menie (www.menie.org)\n     * Copyright 2010 Salvatore Sanfilippo (adapted to Redis coding style)\n     * All rights reserved.\n     * Redistribution and use in source and binary forms, with or without\n     * modification, are permitted provided that the following conditions are met:\n     *\n     *     * Redistributions of source code must retain the above copyright\n     *       notice, this list of conditions and the following disclaimer.\n     *     * Redistributions in binary form must reproduce the above copyright\n     *       notice, this list of conditions and the following disclaimer in the\n     *       documentation and/or other materials provided with the distribution.\n     *     * Neither the name of the University of California, Berkeley nor the\n     *       names of its contributors may be used to endorse or promote products\n     *       derived from this software without specific prior written permission.\n     *\n     * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY\n     * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n     * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n     * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY\n     * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n     * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n     * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n     * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n     * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n     * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n     */\n\n    /* CRC16 implementation according to CCITT standards.\n     *\n     * Note by @antirez: this is actually the XMODEM CRC 16 algorithm, using the\n     * following parameters:\n     *\n     * Name                       : \"XMODEM\", also known as \"ZMODEM\", \"CRC-16/ACORN\"\n     * Width                      : 16 bit\n     * Poly                       : 1021 (That is actually x^16 + x^12 + x^5 + 1)\n     * Initialization             : 0000\n     * Reflect Input byte         : False\n     * Reflect Output CRC         : False\n     * Xor constant to output CRC : 0000\n     * Output for \"123456789\"     : 31C3\n     */\n\n    static const uint16_t crc16tab[256]= {\n        0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7,\n        0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef,\n        0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6,\n        0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de,\n        0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485,\n        0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d,\n        0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4,\n        0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc,\n        0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823,\n        0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b,\n        0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12,\n        0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a,\n        0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41,\n        0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49,\n        0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70,\n        0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78,\n        0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f,\n        0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067,\n        0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e,\n        0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256,\n        0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d,\n        0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405,\n        0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c,\n        0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634,\n        0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab,\n        0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3,\n        0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a,\n        0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92,\n        0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9,\n        0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1,\n        0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8,\n        0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0\n    };\n\n    uint16_t crc16(const char *buf, int len) {\n        int counter;\n        uint16_t crc = 0;\n        for (counter = 0; counter < len; counter++)\n                crc = (crc<<8) ^ crc16tab[((crc>>8) ^ *buf++)&0x00FF];\n        return crc;\n    }"}],"examples":[{"id":"client-connections-and-redirection-handling-ex0","language":"plaintext","code":"127.0.0.1:7000> cluster slots\n1) 1) (integer) 5461\n   2) (integer) 10922\n   3) 1) \"127.0.0.1\"\n      2) (integer) 7001\n   4) 1) \"127.0.0.1\"\n      2) (integer) 7004\n2) 1) (integer) 0\n   2) (integer) 5460\n   3) 1) \"127.0.0.1\"\n      2) (integer) 7000\n   4) 1) \"127.0.0.1\"\n      2) (integer) 7003\n3) 1) (integer) 10923\n   2) (integer) 16383\n   3) 1) \"127.0.0.1\"\n      2) (integer) 7002\n   4) 1) \"127.0.0.1\"\n      2) (integer) 7005","section_id":"client-connections-and-redirection-handling"},{"id":"hash-slots-configuration-propagation-ex0","language":"plaintext","code":"0 -> NULL\n1 -> NULL\n2 -> NULL\n...\n16383 -> NULL","section_id":"hash-slots-configuration-propagation"},{"id":"hash-slots-configuration-propagation-ex1","language":"plaintext","code":"0 -> NULL\n1 -> A [3]\n2 -> A [3]\n...\n16383 -> NULL","section_id":"hash-slots-configuration-propagation"},{"id":"hash-slots-configuration-propagation-ex2","language":"plaintext","code":"0 -> NULL\n1 -> B [4]\n2 -> B [4]\n...\n16383 -> NULL","section_id":"hash-slots-configuration-propagation"}]}
{"schema_version":2,"id":"operate/oss_and_stack/reference","title":"Reference","url":"https://redis.io/docs/latest/operate/oss_and_stack/reference/","summary":"Redis Open Source reference documentation","tags":[],"last_updated":"2025-05-01T06:50:58-07:00","children":[{"id":"operate/oss_and_stack/reference/signals","summary":"How Redis handles common Unix signals","title":"Redis signal handling","url":"https://redis.io/docs/latest/operate/oss_and_stack/reference/signals/"},{"id":"operate/oss_and_stack/reference/cluster-spec","summary":"Detailed specification for Redis cluster","title":"Redis cluster specification","url":"https://redis.io/docs/latest/operate/oss_and_stack/reference/cluster-spec/"},{"id":"operate/oss_and_stack/reference/arm","summary":"Exploring Redis on the ARM CPU Architecture","title":"ARM support","url":"https://redis.io/docs/latest/operate/oss_and_stack/reference/arm/"},{"id":"operate/oss_and_stack/reference/internals","summary":"Documents describing internals in early Redis implementations","title":"Redis internals","url":"https://redis.io/docs/latest/operate/oss_and_stack/reference/internals/"}],"page_type":"index","sections":[],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/reference/internals","title":"Redis internals","url":"https://redis.io/docs/latest/operate/oss_and_stack/reference/internals/","summary":"Documents describing internals in early Redis implementations","tags":["docs","operate","stack","oss"],"last_updated":"2024-01-30T17:50:06+01:00","children":[{"id":"operate/oss_and_stack/reference/internals/internals-rediseventlib","summary":"What's an event library, and how was the original Redis event library implemented?","title":"Event library","url":"https://redis.io/docs/latest/operate/oss_and_stack/reference/internals/internals-rediseventlib/"},{"id":"operate/oss_and_stack/reference/internals/internals-sds","summary":"Guide to the original implementation of Redis strings","title":"String internals","url":"https://redis.io/docs/latest/operate/oss_and_stack/reference/internals/internals-sds/"},{"id":"operate/oss_and_stack/reference/internals/internals-vm","summary":"A description of the Redis virtual memory system that was deprecated in 2.4 and removed in 2.6. This document exists for historical interest.","title":"Virtual memory (deprecated)","url":"https://redis.io/docs/latest/operate/oss_and_stack/reference/internals/internals-vm/"},{"id":"operate/oss_and_stack/reference/internals/rdd","summary":"A design for the RDB format written in the early days of Redis","title":"Redis design draft #2 (historical)","url":"https://redis.io/docs/latest/operate/oss_and_stack/reference/internals/rdd/"}],"page_type":"content","content_hash":"ec24a992d97ed9e84b3e4e427056f397605ecbd863c94c7bff50d3863b7c8180","sections":[{"id":"content","title":"Content","role":"content","text":"**The following Redis documents were written by the creator of Redis, Salvatore Sanfilippo, early in the development of Redis (c. 2010), and do not necessarily reflect the latest Redis implementation.**"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/reference/internals/internals-rediseventlib","title":"Event library","url":"https://redis.io/docs/latest/operate/oss_and_stack/reference/internals/internals-rediseventlib/","summary":"What's an event library, and how was the original Redis event library implemented?","tags":["docs","operate","stack","oss"],"last_updated":"2024-01-30T17:50:06+01:00","page_type":"content","content_hash":"9712a60b53542f743339b5b643817b4bab274b39d959c68d406d5745a56cc77b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"**Note: this document was written by the creator of Redis, Salvatore Sanfilippo, early in the development of Redis (c. 2010), and does not necessarily reflect the latest Redis implementation.**"},{"id":"why-is-an-event-library-needed-at-all","title":"Why is an Event Library needed at all?","role":"content","text":"Let us figure it out through a series of Q&As.\n\nQ: What do you expect a network server to be doing all the time? <br/>\nA: Watch for inbound connections on the port its listening and accept them.\n\nQ: Calling [accept](http://man.cx/accept%282%29) yields a descriptor. What do I do with it?<br/>\nA: Save the descriptor and do a non-blocking read/write operation on it.\n\nQ: Why does the read/write have to be non-blocking?<br/>\nA: If the file operation ( even a socket in Unix is a file ) is blocking how could the server for example accept other connection requests when its blocked in a file I/O operation.\n\nQ: I guess I have to do many such non-blocking operations on the socket to see when it's ready. Am I right?<br/>\nA: Yes. That is what an event library does for you. Now you get it.\n\nQ: How do Event Libraries do what they do?<br/>\nA: They use the operating system's polling facility along with timers.\n\nQ: So are there any open source event libraries that do what you just described? <br/>\nA: Yes. `libevent` and `libev` are two such event libraries that I can recall off the top of my head.\n\nQ: Does Redis use such open source event libraries for handling socket I/O?<br/>\nA: No. For various [reasons](http://groups.google.com/group/redis-db/browse_thread/thread/b52814e9ef15b8d0/) Redis uses its own event library."},{"id":"the-redis-event-library","title":"The Redis event library","role":"content","text":"Redis implements its own event library. The event library is implemented in `ae.c`.\n\nThe best way to understand how the Redis event library works is to understand how Redis uses it.\n\nEvent Loop Initialization\n---\n\n`initServer` function defined in `redis.c` initializes the numerous fields of the `redisServer` structure variable. One such field is the Redis event loop `el`:\n\n    aeEventLoop *el\n\n`initServer` initializes `server.el` field by calling `aeCreateEventLoop` defined in `ae.c`. The definition of `aeEventLoop` is below:\n\n    typedef struct aeEventLoop\n    {\n        int maxfd;\n        long long timeEventNextId;\n        aeFileEvent events[AE_SETSIZE]; /* Registered events */\n        aeFiredEvent fired[AE_SETSIZE]; /* Fired events */\n        aeTimeEvent *timeEventHead;\n        int stop;\n        void *apidata; /* This is used for polling API specific data */\n        aeBeforeSleepProc *beforesleep;\n    } aeEventLoop;\n\n`aeCreateEventLoop`\n---\n\n`aeCreateEventLoop` first `malloc`s `aeEventLoop` structure then calls `ae_epoll.c:aeApiCreate`.\n\n`aeApiCreate` `malloc`s `aeApiState` that has two fields - `epfd` that holds the `epoll` file descriptor returned by a call from [`epoll_create`](http://man.cx/epoll_create%282%29) and `events` that is of type `struct epoll_event` define by the Linux `epoll` library. The use of the `events` field will be  described later.\n\nNext is `ae.c:aeCreateTimeEvent`. But before that `initServer` call `anet.c:anetTcpServer` that creates and returns a _listening descriptor_. The descriptor listens on *port 6379* by default. The returned  _listening descriptor_ is stored in `server.fd` field.\n\n`aeCreateTimeEvent`\n---\n\n`aeCreateTimeEvent` accepts the following as parameters:\n\n  * `eventLoop`: This is `server.el` in `redis.c`\n  * milliseconds: The number of milliseconds from the current time after which the timer expires.\n  * `proc`: Function pointer. Stores the address of the function that has to be called after the timer expires.\n  * `clientData`: Mostly `NULL`.\n  * `finalizerProc`: Pointer to the function that has to be called before the timed event is removed from the list of timed events.\n\n`initServer` calls `aeCreateTimeEvent` to add a timed event to `timeEventHead` field of `server.el`. `timeEventHead` is a pointer to a list of such timed events. The call to `aeCreateTimeEvent` from `redis.c:initServer` function is given below:\n\n    aeCreateTimeEvent(server.el /*eventLoop*/, 1 /*milliseconds*/, serverCron /*proc*/, NULL /*clientData*/, NULL /*finalizerProc*/);\n\n`redis.c:serverCron` performs many operations that helps keep Redis running properly.\n\n`aeCreateFileEvent`\n---\n\nThe essence of `aeCreateFileEvent` function is to execute [`epoll_ctl`](http://man.cx/epoll_ctl) system call which adds a watch for `EPOLLIN` event on the _listening descriptor_ create by `anetTcpServer` and associate it with the `epoll` descriptor created by a call to `aeCreateEventLoop`.\n\nFollowing is an explanation of what precisely `aeCreateFileEvent` does when called from `redis.c:initServer`.\n\n`initServer` passes the following arguments to `aeCreateFileEvent`:\n\n  * `server.el`: The event loop created by `aeCreateEventLoop`. The `epoll` descriptor is got from `server.el`.\n  * `server.fd`: The _listening descriptor_ that also serves as an index to access the relevant file event structure from the `eventLoop->events` table and store extra information like the callback function.\n  * `AE_READABLE`: Signifies that `server.fd` has to be watched for `EPOLLIN` event.\n  * `acceptHandler`: The function that has to be executed when the event being watched for is ready. This function pointer is stored in `eventLoop->events[server.fd]->rfileProc`.\n\nThis completes the initialization of Redis event loop.\n\nEvent Loop Processing\n---\n\n`ae.c:aeMain` called from `redis.c:main` does the job of processing the event loop that is initialized in the previous phase.\n\n`ae.c:aeMain` calls `ae.c:aeProcessEvents` in a while loop that processes pending time and file events.\n\n`aeProcessEvents`\n---\n\n`ae.c:aeProcessEvents` looks for the time event that will be pending in the smallest amount of time by calling `ae.c:aeSearchNearestTimer` on the event loop. In our case there is only one timer event in the event loop that was created by `ae.c:aeCreateTimeEvent`.\n\nRemember, that the timer event created by `aeCreateTimeEvent` has probably elapsed by now because it had an expiry time of one millisecond. Since the timer has already expired, the seconds and microseconds fields of the `tvp` `timeval` structure variable is initialized to zero.\n\nThe `tvp` structure variable along with the event loop variable is passed to `ae_epoll.c:aeApiPoll`.\n\n`aeApiPoll` functions does an [`epoll_wait`](http://man.cx/epoll_wait) on the `epoll` descriptor and populates the `eventLoop->fired` table with the details:\n\n  * `fd`: The descriptor that is now ready to do a read/write operation depending on the mask value.\n  * `mask`: The read/write event that can now be performed on the corresponding descriptor.\n\n`aeApiPoll` returns the number of such file events ready for operation. Now to put things in context, if any client has requested for a connection then `aeApiPoll` would have noticed it and populated the `eventLoop->fired` table with an entry of the descriptor being the _listening descriptor_ and mask being `AE_READABLE`.\n\nNow, `aeProcessEvents` calls the `redis.c:acceptHandler` registered as the callback. `acceptHandler` executes [accept](http://man.cx/accept) on the _listening descriptor_ returning a _connected descriptor_ with the client. `redis.c:createClient` adds a file event on the _connected descriptor_ through a call to `ae.c:aeCreateFileEvent` like below:\n\n    if (aeCreateFileEvent(server.el, c->fd, AE_READABLE,\n        readQueryFromClient, c) == AE_ERR) {\n        freeClient(c);\n        return NULL;\n    }\n\n`c` is the `redisClient` structure variable and `c->fd` is the connected descriptor.\n\nNext the `ae.c:aeProcessEvent` calls `ae.c:processTimeEvents`\n\n`processTimeEvents`\n---\n\n`ae.processTimeEvents` iterates over list of time events starting at `eventLoop->timeEventHead`.\n\nFor every timed event that has elapsed `processTimeEvents` calls the registered callback. In this case it calls the only timed event callback registered, that is, `redis.c:serverCron`. The callback returns the time in milliseconds after which the callback must be called again. This change is recorded via a call to `ae.c:aeAddMilliSeconds` and will be handled on the next iteration of `ae.c:aeMain` while loop.\n\nThat's all."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/reference/internals/internals-sds","title":"String internals","url":"https://redis.io/docs/latest/operate/oss_and_stack/reference/internals/internals-sds/","summary":"Guide to the original implementation of Redis strings","tags":["docs","operate","stack","oss"],"last_updated":"2024-01-30T17:50:06+01:00","page_type":"content","content_hash":"cfbe18f367f46ea63b0cae2a71013ba912a3549d7fc5e76ab9dc59350e80c89e","sections":[{"id":"content","title":"Content","role":"content","text":"**Note: this document was written by the creator of Redis, Salvatore Sanfilippo, early in the development of Redis (c. 2010). Virtual Memory has been deprecated since Redis 2.6, so this documentation\nis here only for historical interest.**\n\nThe implementation of Redis strings is contained in `sds.c` (`sds` stands for\nSimple Dynamic Strings). The implementation is available as a standalone library\nat [https://github.com/antirez/sds](https://github.com/antirez/sds).\n\nThe C structure `sdshdr` declared in `sds.h` represents a Redis string:\n\n    struct sdshdr {\n        long len;\n        long free;\n        char buf[];\n    };\n\nThe `buf` character array stores the actual string.\n\nThe `len` field stores the length of `buf`. This makes obtaining the length\nof a Redis string an O(1) operation.\n\nThe `free` field stores the number of additional bytes available for use.\n\nTogether the `len` and `free` field can be thought of as holding the metadata of the `buf` character array.\n\nCreating Redis Strings\n---\n\nA new data type named `sds` is defined in `sds.h` to be a synonym for a character pointer:\n\n    typedef char *sds;\n\n`sdsnewlen` function defined in `sds.c` creates a new Redis String:\n\n    sds sdsnewlen(const void *init, size_t initlen) {\n        struct sdshdr *sh;\n\n        sh = zmalloc(sizeof(struct sdshdr)+initlen+1);\n    #ifdef SDS_ABORT_ON_OOM\n        if (sh == NULL) sdsOomAbort();\n    #else\n        if (sh == NULL) return NULL;\n    #endif\n        sh->len = initlen;\n        sh->free = 0;\n        if (initlen) {\n            if (init) memcpy(sh->buf, init, initlen);\n            else memset(sh->buf,0,initlen);\n        }\n        sh->buf[initlen] = '\\0';\n        return (char*)sh->buf;\n    }\n\nRemember a Redis string is a variable of type `struct sdshdr`. But `sdsnewlen` returns a character pointer!!\n\nThat's a trick and needs some explanation.\n\nSuppose I create a Redis string using `sdsnewlen` like below:\n\n    sdsnewlen(\"redis\", 5);\n\nThis creates a new variable of type `struct sdshdr` allocating memory for `len` and `free`\nfields as well as for the `buf` character array.\n\n    sh = zmalloc(sizeof(struct sdshdr)+initlen+1); // initlen is length of init argument.\n\nAfter `sdsnewlen` successfully creates a Redis string the result is something like:\n\n    -----------\n    |5|0|redis|\n    -----------\n    ^   ^\n    sh  sh->buf\n\n`sdsnewlen` returns `sh->buf` to the caller.\n\nWhat do you do if you need to free the Redis string pointed by `sh`?\n\nYou want the pointer `sh` but you only have the pointer `sh->buf`.\n\nCan you get the pointer `sh` from `sh->buf`?\n\nYes. Pointer arithmetic. Notice from the above ASCII art that if you subtract\nthe size of two longs from `sh->buf` you get the pointer `sh`.\n\nThe `sizeof` two longs happens to be the size of `struct sdshdr`.\n\nLook at `sdslen` function and see this trick at work:\n\n    size_t sdslen(const sds s) {\n        struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));\n        return sh->len;\n    }\n\nKnowing this trick you could easily go through the rest of the functions in `sds.c`.\n\nThe Redis string implementation is hidden behind an interface that accepts only character pointers. The users of Redis strings need not care about how it's implemented and can treat Redis strings as a character pointer."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/reference/internals/internals-vm","title":"Virtual memory (deprecated)","url":"https://redis.io/docs/latest/operate/oss_and_stack/reference/internals/internals-vm/","summary":"A description of the Redis virtual memory system that was deprecated in 2.4 and removed in 2.6. This document exists for historical interest.","tags":["docs","operate","stack","oss"],"last_updated":"2026-09-21T15:59:03+01:00","page_type":"content","content_hash":"def64d4e56a011a933c878fda1ccfa3d8b46fad337de616cab648b5dafd2fb09","sections":[{"id":"content","title":"Content","role":"content","text":"**Note: This document was written by the creator of Redis, Salvatore Sanfilippo, early in the development of Redis (c. 2010). Virtual Memory was deprecated in Redis 2.4 and removed in Redis 2.6, so this documentation exists only for historical interest.**\n\nThis document details the internals of the Redis Virtual Memory subsystem prior to Redis 2.6. The intended audience is not the final user but programmers willing to understand or modify the Virtual Memory implementation.\n\nKeys vs Values: what is swapped out?\n---\n\nThe goal of the VM subsystem is to free memory transferring Redis Objects from memory to disk. This is a very generic command, but specifically, Redis transfers only objects associated with _values_. In order to understand better this concept we'll show, using the DEBUG command, how a key holding a value looks from the point of view of the Redis internals:\n\n    redis> set foo bar\n    OK\n    redis> debug object foo\n    Key at:0x100101d00 refcount:1, value at:0x100101ce0 refcount:1 encoding:raw serializedlength:4\n\nAs you can see from the above output, the Redis top level hash table maps Redis Objects (keys) to other Redis Objects (values). The Virtual Memory is only able to swap _values_ on disk, the objects associated to _keys_ are always taken in memory: this trade off guarantees very good lookup performances, as one of the main design goals of the Redis VM is to have performances similar to Redis with VM disabled when the part of the dataset frequently used fits in RAM.\n\nHow does a swapped value looks like internally\n---\n\nWhen an object is swapped out, this is what happens in the hash table entry:\n\n * The key continues to hold a Redis Object representing the key.\n * The value is set to NULL\n\nSo you may wonder where we store the information that a given value (associated to a given key) was swapped out. Just in the key object!\n\nThis is how the Redis Object structure _robj_ looks like:\n\n    /* The actual Redis Object */\n    typedef struct redisObject {\n        void *ptr;\n        unsigned char type;\n        unsigned char encoding;\n        unsigned char storage;  /* If this object is a key, where is the value?\n                                 * REDIS_VM_MEMORY, REDIS_VM_SWAPPED, ... */\n        unsigned char vtype; /* If this object is a key, and value is swapped out,\n                              * this is the type of the swapped out object. */\n        int refcount;\n        /* VM fields, this are only allocated if VM is active, otherwise the\n         * object allocation function will just allocate\n         * sizeof(redisObject) minus sizeof(redisObjectVM), so using\n         * Redis without VM active will not have any overhead. */\n        struct redisObjectVM vm;\n    } robj;\n\nAs you can see there are a few fields about VM. The most important one is _storage_, that can be one of this values:\n\n * `REDIS_VM_MEMORY`: the associated value is in memory.\n * `REDIS_VM_SWAPPED`: the associated values is swapped, and the value entry of the hash table is just set to NULL.\n * `REDIS_VM_LOADING`: the value is swapped on disk, the entry is NULL, but there is a job to load the object from the swap to the memory (this field is only used when threaded VM is active).\n * `REDIS_VM_SWAPPING`: the value is in memory, the entry is a pointer to the actual Redis Object, but there is an I/O job in order to transfer this value to the swap file.\n\nIf an object is swapped on disk (`REDIS_VM_SWAPPED` or `REDIS_VM_LOADING`), how do we know where it is stored, what type it is, and so forth? That's simple: the _vtype_ field is set to the original type of the Redis object swapped, while the _vm_ field (that is a _redisObjectVM_ structure) holds information about the location of the object. This is the definition of this additional structure:\n\n    /* The VM object structure */\n    struct redisObjectVM {\n        off_t page;         /* the page at which the object is stored on disk */\n        off_t usedpages;    /* number of pages used on disk */\n        time_t atime;       /* Last access time */\n    } vm;\n\nAs you can see the structure contains the page at which the object is located in the swap file, the number of pages used, and the last access time of the object (this is very useful for the algorithm that select what object is a good candidate for swapping, as we want to transfer on disk objects that are rarely accessed).\n\nAs you can see, while all the other fields are using unused bytes in the old Redis Object structure (we had some free bit due to natural memory alignment concerns), the _vm_ field is new, and indeed uses additional memory. Should we pay such a memory cost even when VM is disabled? No! This is the code to create a new Redis Object:\n\n    ... some code ...\n            if (server.vm_enabled) {\n                pthread_mutex_unlock(&server.obj_freelist_mutex);\n                o = zmalloc(sizeof(*o));\n            } else {\n                o = zmalloc(sizeof(*o)-sizeof(struct redisObjectVM));\n            }\n    ... some code ...\n\nAs you can see if the VM system is not enabled we allocate just `sizeof(*o)-sizeof(struct redisObjectVM)` of memory. Given that the _vm_ field is the last in the object structure, and that this fields are never accessed if VM is disabled, we are safe and Redis without VM does not pay the memory overhead.\n\nThe Swap File\n---\n\nThe next step in order to understand how the VM subsystem works is understanding how objects are stored inside the swap file. The good news is that's not some kind of special format, we just use the same format used to store the objects in .rdb files, that are the usual dump files produced by Redis using the [`SAVE`](https://redis.io/docs/latest/commands/save) command.\n\nThe swap file is composed of a given number of pages, where every page size is a given number of bytes. This parameters can be changed in redis.conf, since different Redis instances may work better with different values: it depends on the actual data you store inside it. The following are the default values:\n\n    vm-page-size 32\n    vm-pages 134217728\n\nRedis takes a \"bitmap\" (a contiguous array of bits set to zero or one) in memory, every bit represent a page of the swap file on disk: if a given bit is set to 1, it represents a page that is already used (there is some Redis Object stored there), while if the corresponding bit is zero, the page is free.\n\nTaking this bitmap (that will call the page table) in memory is a huge win in terms of performances, and the memory used is small: we just need 1 bit for every page on disk. For instance in the example below 134217728 pages of 32 bytes each (4GB swap file) is using just 16 MB of RAM for the page table.\n\nTransferring objects from memory to swap\n---\n\nIn order to transfer an object from memory to disk we need to perform the following steps (assuming non threaded VM, just a simple blocking approach):\n\n * Find how many pages are needed in order to store this object on the swap file. This is trivially accomplished just calling the function `rdbSavedObjectPages` that returns the number of pages used by an object on disk. Note that this function does not duplicate the .rdb saving code just to understand what will be the length *after* an object will be saved on disk, we use the trick of opening /dev/null and writing the object there, finally calling `ftello` in order check the amount of bytes required. What we do basically is to save the object on a virtual very fast file, that is, /dev/null.\n * Now that we know how many pages are required in the swap file, we need to find this number of contiguous free pages inside the swap file. This task is accomplished by the `vmFindContiguousPages` function. As you can guess this function may fail if the swap is full, or so fragmented that we can't easily find the required number of contiguous free pages. When this happens we just abort the swapping of the object, that will continue to live in memory.\n * Finally we can write the object on disk, at the specified position, just calling the function `vmWriteObjectOnSwap`.\n\nAs you can guess once the object was correctly written in the swap file, it is freed from memory, the storage field in the associated key is set to `REDIS_VM_SWAPPED`, and the used pages are marked as used in the page table.\n\nLoading objects back in memory\n---\n\nLoading an object from swap to memory is simpler, as we already know where the object is located and how many pages it is using. We also know the type of the object (the loading functions are required to know this information, as there is no header or any other information about the object type on disk), but this is stored in the _vtype_ field of the associated key as already seen above.\n\nCalling the function `vmLoadObject` passing the key object associated to the value object we want to load back is enough. The function will also take care of fixing the storage type of the key (that will be `REDIS_VM_MEMORY`), marking the pages as freed in the page table, and so forth.\n\nThe return value of the function is the loaded Redis Object itself, that we'll have to set again as value in the main hash table (instead of the NULL value we put in place of the object pointer when the value was originally swapped out).\n\nHow blocking VM works\n---\n\nNow we have all the building blocks in order to describe how the blocking VM works. First of all, an important detail about configuration. In order to enable blocking VM in Redis `server.vm_max_threads` must be set to zero.\nWe'll see later how this max number of threads info is used in the threaded VM, for now all it's needed to now is that Redis reverts to fully blocking VM when this is set to zero.\n\nWe also need to introduce another important VM parameter, that is, `server.vm_max_memory`. This parameter is very important as it is used in order to trigger swapping: Redis will try to swap objects only if it is using more memory than the max memory setting, otherwise there is no need to swap as we are matching the user requested memory usage.\n\nBlocking VM swapping\n---\n\nSwapping of object from memory to disk happens in the cron function. This function used to be called every second, while in the recent Redis versions on git it is called every 100 milliseconds (that is, 10 times per second).\nIf this function detects we are out of memory, that is, the memory used is greater than the vm-max-memory setting, it starts transferring objects from memory to disk in a loop calling the function `vmSwapOneObect`. This function takes just one argument, if 0 it will swap objects in a blocking way, otherwise if it is 1, I/O threads are used. In the blocking scenario we just call it with zero as argument.\n\nvmSwapOneObject acts performing the following steps:\n\n * The key space in inspected in order to find a good candidate for swapping (we'll see later what a good candidate for swapping is).\n * The associated value is transferred to disk, in a blocking way.\n * The key storage field is set to `REDIS_VM_SWAPPED`, while the _vm_ fields of the object are set to the right values (the page index where the object was swapped, and the number of pages used to swap it).\n * Finally the value object is freed and the value entry of the hash table is set to NULL.\n\nThe function is called again and again until one of the following happens: there is no way to swap more objects because either the swap file is full or nearly all the objects are already transferred on disk, or simply the memory usage is already under the vm-max-memory parameter.\n\nWhat values to swap when we are out of memory?\n---\n\nUnderstanding what's a good candidate for swapping is not too hard. A few objects at random are sampled, and for each their _swappability_ is commuted as:\n\n    swappability = age*log(size_in_memory)\n\nThe age is the number of seconds the key was not requested, while size_in_memory is a fast estimation of the amount of memory (in bytes) used by the object in memory. So we try to swap out objects that are rarely accessed, and we try to swap bigger objects over smaller one, but the latter is a less important factor (because of the logarithmic function used). This is because we don't want bigger objects to be swapped out and in too often as the bigger the object the more I/O and CPU is required in order to transfer it.\n\nBlocking VM loading\n---\n\nWhat happens if an operation against a key associated with a swapped out object is requested? For instance Redis may just happen to process the following command:\n\n    GET foo\n\nIf the value object of the `foo` key is swapped we need to load it back in memory before processing the operation. In Redis the key lookup process is centralized in the `lookupKeyRead` and `lookupKeyWrite` functions, this two functions are used in the implementation of all the Redis commands accessing the keyspace, so we have a single point in the code where to handle the loading of the key from the swap file to memory.\n\nSo this is what happens:\n\n * The user calls some command having as argument a swapped key\n * The command implementation calls the lookup function\n * The lookup function search for the key in the top level hash table. If the value associated with the requested key is swapped (we can see that checking the _storage_ field of the key object), we load it back in memory in a blocking way before to return to the user.\n\nThis is pretty straightforward, but things will get more _interesting_ with the threads. From the point of view of the blocking VM the only real problem is the saving of the dataset using another process, that is, handling [`BGSAVE`](https://redis.io/docs/latest/commands/bgsave) and [`BGREWRITEAOF`](https://redis.io/docs/latest/commands/bgrewriteaof) commands.\n\nBackground saving when VM is active\n---\n\nThe default Redis way to persist on disk is to create .rdb files using a child process. Redis calls the fork() system call in order to create a child, that has the exact copy of the in memory dataset, since fork duplicates the whole program memory space (actually thanks to a technique called Copy on Write memory pages are shared between the parent and child process, so the fork() call will not require too much memory).\n\nIn the child process we have a copy of the dataset in a given point in the time. Other commands issued by clients will just be served by the parent process and will not modify the child data.\n\nThe child process will just store the whole dataset into the dump.rdb file and finally will exit. But what happens when the VM is active? Values can be swapped out so we don't have all the data in memory, and we need to access the swap file in order to retrieve the swapped values. While child process is saving the swap file is shared between the parent and child process, since:\n\n* The parent process needs to access the swap file in order to load values back into memory if an operation against swapped out values are performed.\n* The child process needs to access the swap file in order to retrieve the full dataset while saving the data set on disk.\n\nIn order to avoid problems while both the processes are accessing the same swap file we do a simple thing, that is, not allowing values to be swapped out in the parent process while a background saving is in progress. This way both the processes will access the swap file in read only. This approach has the problem that while the child process is saving no new values can be transferred on the swap file even if Redis is using more memory than the max memory parameters dictates. This is usually not a problem as the background saving will terminate in a short amount of time and if still needed a percentage of values will be swapped on disk ASAP.\n\nAn alternative to this scenario is to enable the Append Only File that will have this problem only when a log rewrite is performed using the [`BGREWRITEAOF`](https://redis.io/docs/latest/commands/bgrewriteaof) command.\n\nThe problem with the blocking VM\n---\n\nThe problem of blocking VM is that... it's blocking :)\nThis is not a problem when Redis is used in batch processing activities, but for real-time usage one of the good points of Redis is the low latency. The blocking VM will have bad latency behaviors as when a client is accessing a swapped out value, or when Redis needs to swap out values, no other clients will be served in the meantime.\n\nSwapping out keys should happen in background. Similarly when a client is accessing a swapped out value other clients accessing in memory values should be served mostly as fast as when VM is disabled. Only the clients dealing with swapped out keys should be delayed.\n\nAll this limitations called for a non-blocking VM implementation.\n\nThreaded VM\n---\n\nThere are basically three main ways to turn the blocking VM into a non blocking one.\n* 1: One way is obvious, and in my opinion, not a good idea at all, that is, turning Redis itself into a threaded server: if every request is served by a different thread automatically other clients don't need to wait for blocked ones. Redis is fast, exports atomic operations, has no locks, and is just 10k lines of code, *because* it is single threaded, so this was not an option for me.\n* 2: Using non-blocking I/O against the swap file. After all you can think Redis already event-loop based, why don't just handle disk I/O in a non-blocking fashion? I also discarded this possibility because of two main reasons. One is that non blocking file operations, unlike sockets, are an incompatibility nightmare. It's not just like calling select, you need to use OS-specific things. The other problem is that the I/O is just one part of the time consumed to handle VM, another big part is the CPU used in order to encode/decode data to/from the swap file. This is I picked option three, that is...\n* 3: Using I/O threads, that is, a pool of threads handling the swap I/O operations. This is what the Redis VM is using, so let's detail how this works.\n\nI/O Threads\n---\n\nThe threaded VM design goals where the following, in order of importance:\n\n * Simple implementation, little room for race conditions, simple locking, VM system more or less completely decoupled from the rest of Redis code.\n * Good performances, no locks for clients accessing values in memory.\n * Ability to decode/encode objects in the I/O threads.\n\nThe above goals resulted in an implementation where the Redis main thread (the one serving actual clients) and the I/O threads communicate using a queue of jobs, with a single mutex.\nBasically when main thread requires some work done in the background by some I/O thread, it pushes an I/O job structure in the `server.io_newjobs` queue (that is, just a linked list). If there are no active I/O threads, one is started. At this point some I/O thread will process the I/O job, and the result of the processing is pushed in the `server.io_processed` queue. The I/O thread will send a byte using an UNIX pipe to the main thread in order to signal that a new job was processed and the result is ready to be processed.\n\nThis is how the `iojob` structure looks like:\n\n    typedef struct iojob {\n        int type;   /* Request type, REDIS_IOJOB_* */\n        redisDb *db;/* Redis database */\n        robj *key;  /* This I/O request is about swapping this key */\n        robj *val;  /* the value to swap for REDIS_IOREQ_*_SWAP, otherwise this\n                     * field is populated by the I/O thread for REDIS_IOREQ_LOAD. */\n        off_t page; /* Swap page where to read/write the object */\n        off_t pages; /* Swap pages needed to save object. PREPARE_SWAP return val */\n        int canceled; /* True if this command was canceled by blocking side of VM */\n        pthread_t thread; /* ID of the thread processing this entry */\n    } iojob;\n\nThere are just three type of jobs that an I/O thread can perform (the type is specified by the `type` field of the structure):\n\n* `REDIS_IOJOB_LOAD`: load the value associated to a given key from swap to memory. The object offset inside the swap file is `page`, the object type is `key->vtype`. The result of this operation will populate the `val` field of the structure.\n* `REDIS_IOJOB_PREPARE_SWAP`: compute the number of pages needed in order to save the object pointed by `val` into the swap. The result of this operation will populate the `pages` field.\n* `REDIS_IOJOB_DO_SWAP`: Transfer the object pointed by `val` to the swap file, at page offset `page`.\n\nThe main thread delegates just the above three tasks. All the rest is handled by the I/O thread itself, for instance finding a suitable range of free pages in the swap file page table (that is a fast operation), deciding what object to swap, altering the storage field of a Redis object to reflect the current state of a value.\n\nNon blocking VM as probabilistic enhancement of blocking VM\n---\n\nSo now we have a way to request background jobs dealing with slow VM operations. How to add this to the mix of the rest of the work done by the main thread? While blocking VM was aware that an object was swapped out just when the object was looked up, this is too late for us: in C it is not trivial to start a background job in the middle of the command, leave the function, and re-enter in the same point the computation when the I/O thread finished what we requested (that is, no co-routines or continuations or alike).\n\nFortunately there was a much, much simpler way to do this. And we love simple things: basically consider the VM implementation a blocking one, but add an optimization (using non the no blocking VM operations we are able to perform) to make the blocking *very* unlikely.\n\nThis is what we do:\n\n * Every time a client sends us a command, *before* the command is executed, we examine the argument vector of the command in search for swapped keys. After all we know for every command what arguments are keys, as the Redis command format is pretty simple.\n * If we detect that at least a key in the requested command is swapped on disk, we block the client instead of really issuing the command. For every swapped value associated to a requested key, an I/O job is created, in order to bring the values back in memory. The main thread continues the execution of the event loop, without caring about the blocked client.\n * In the meanwhile, I/O threads are loading values in memory. Every time an I/O thread finished loading a value, it sends a byte to the main thread using an UNIX pipe. The pipe file descriptor has a readable event associated in the main thread event loop, that is the function `vmThreadedIOCompletedJob`. If this function detects that all the values needed for a blocked client were loaded, the client is restarted and the original command called.\n\nSo you can think of this as a blocked VM that almost always happen to have the right keys in memory, since we pause clients that are going to issue commands about swapped out values until this values are loaded.\n\nIf the function checking what argument is a key fails in some way, there is no problem: the lookup function will see that a given key is associated to a swapped out value and will block loading it. So our non blocking VM reverts to a blocking one when it is not possible to anticipate what keys are touched.\n\nFor instance in the case of the [`SORT`](https://redis.io/docs/latest/commands/sort) command used together with the [`GET`](https://redis.io/docs/latest/commands/get) or `BY` options, it is not trivial to know beforehand what keys will be requested, so at least in the first implementation, `SORT BY/GET` resorts to the blocking VM implementation.\n\nBlocking clients on swapped keys\n---\n\nHow to block clients? To suspend a client in an event-loop based server is pretty trivial. All we do is canceling its read handler. Sometimes we do something different (for instance for BLPOP) that is just marking the client as blocked, but not processing new data (just accumulating the new data into input buffers).\n\nAborting I/O jobs\n---\n\nThere is something hard to solve about the interactions between our blocking and non blocking VM, that is, what happens if a blocking operation starts about a key that is also \"interested\" by a non blocking operation at the same time?\n\nFor instance while SORT BY is executed, a few keys are being loaded in a blocking manner by the sort command. At the same time, another client may request the same keys with a simple _GET key_ command, that will trigger the creation of an I/O job to load the key in background.\n\nThe only simple way to deal with this problem is to be able to kill I/O jobs in the main thread, so that if a key that we want to load or swap in a blocking way is in the `REDIS_VM_LOADING` or `REDIS_VM_SWAPPING` state (that is, there is an I/O job about this key), we can just kill the I/O job about this key, and go ahead with the blocking operation we want to perform.\n\nThis is not as trivial as it is. In a given moment an I/O job can be in one of the following three queues:\n\n * server.io_newjobs: the job was already queued but no thread is handling it.\n * server.io_processing: the job is being processed by an I/O thread.\n * server.io_processed: the job was already processed.\nThe function able to kill an I/O job is `vmCancelThreadedIOJob`, and this is what it does:\n * If the job is in the newjobs queue, that's simple, removing the iojob structure from the queue is enough as no thread is still executing any operation.\n * If the job is in the processing queue, a thread is messing with our job (and possibly with the associated object!). The only thing we can do is waiting for the item to move to the next queue in a *blocking way*. Fortunately this condition happens very rarely so it's not a performance problem.\n * If the job is in the processed queue, we just mark it as _canceled_ marking setting the `canceled` field to 1 in the iojob structure. The function processing completed jobs will just ignored and free the job instead of really processing it.\n\nQuestions?\n---\n\nThis document is in no way complete, the only way to get the whole picture is reading the source code, but it should be a good introduction in order to make the code review / understanding a lot simpler.\n\nSomething is not clear about this page? Please leave a comment and I'll try to address the issue possibly integrating the answer in this document."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/reference/internals/rdd","title":"Redis design draft #2 (historical)","url":"https://redis.io/docs/latest/operate/oss_and_stack/reference/internals/rdd/","summary":"A design for the RDB format written in the early days of Redis","tags":["docs","operate","stack","oss"],"last_updated":"2024-07-16T11:38:21-04:00","page_type":"content","content_hash":"4b2db9c3cb21aedc0149cd0b0e692c1851bfb78f6db4d306e294b6f037ee6418","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"**Note: this document was written by the creator of Redis, Salvatore Sanfilippo, early in the development of Redis (c. 2013), as part of a series of design drafts. This is preserved for historical interest.**"},{"id":"redis-design-draft-2----rdb-version-7-info-fields","title":"Redis Design Draft 2 -- RDB version 7 info fields","role":"content","text":"* Author: Salvatore Sanfilippo `antirez@gmail.com`\n* GitHub issue [#1048](https://github.com/redis/redis/issues/1048)"},{"id":"history-of-revisions","title":"History of revisions","role":"history","text":"1.0, 10 April 2013 - Initial draft."},{"id":"overview","title":"Overview","role":"overview","text":"The Redis RDB format lacks a simple way to add info fields to an RDB file\nwithout causing a backward compatibility issue even if the added meta data\nis not required in order to load data from the RDB file.\n\nFor example thanks to the info fields specified in this document it will\nbe possible to add to RDB information like file creation time, Redis version\ngenerating the file, and any other useful information, in a way that not\nevery field is required for an RDB version 7 file to be correctly processed.\n\nAlso with minimal changes it will be possible to add RDB version 7 support to\nRedis 2.6 without actually supporting the additional fields but just skipping\nthem when loading an RDB file.\n\nRDB info fields may have semantic meaning if needed, so that the presence\nof the field may add information about the data set specified in the RDB\nfile format, however when an info field is required to be correctly decoded\nin order to understand and load the data set content of the RDB file, the\nRDB file format must be increased so that previous versions of Redis will not\nattempt to load it.\n\nHowever currently the info fields are designed to only hold additional\ninformation that are not useful to load the dataset, but can better specify\nhow the RDB file was created."},{"id":"info-fields-representation","title":"Info fields representation","role":"content","text":"The RDB format 6 has the following layout:\n\n* A 9 bytes magic \"REDIS0006\"\n* key-value pairs\n* An EOF opcode\n* CRC64 checksum\n\nThe proposal for RDB format 7 is to add the optional fields immediately\nafter the first 9 bytes magic, so that the new format will be:\n\n* A 9 bytes magic \"REDIS0007\"\n* Info field 1\n* Info field 2\n* ...\n* Info field N\n* Info field end-of-fields\n* key-value pairs\n* An EOF opcode\n* CRC64 checksum\n\nEvery single info field has the following structure:\n\n* A 16 bit identifier\n* A 64 bit data length\n* A data section of the exact length as specified\n\nBoth the identifier and the data length are stored in little endian byte\nordering.\n\nThe special identifier 0 means that there are no other info fields, and that\nthe remaining of the RDB file contains the key-value pairs."},{"id":"handling-of-info-fields","title":"Handling of info fields","role":"content","text":"A program can simply skip every info field it does not understand, as long\nas the RDB version matches the one that it is capable to load."},{"id":"specification-of-info-fields-ids-and-content","title":"Specification of info fields IDs and content.","role":"content","text":"#### Info field 0 -- End of info fields\n\nThis just means there are no longer info fields to process.\n\n#### Info field 1 -- Creation date\n\nThis field represents the unix time at which the RDB file was created.\nThe format of the unix time is a 64 bit little endian integer representing\nseconds since 1th January 1970.\n\n#### Info field 2 -- Redis version\n\nThis field represents a null-terminated string containing the Redis version\nthat generated the file, as displayed in the Redis version INFO field."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/reference/signals","title":"Redis signal handling","url":"https://redis.io/docs/latest/operate/oss_and_stack/reference/signals/","summary":"How Redis handles common Unix signals","tags":["docs","operate","stack","oss"],"last_updated":"2026-09-21T15:59:03+01:00","page_type":"content","content_hash":"90528d6b85bca35bad004efffa157553f99487a46604afc297704ae19258efbc","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This document provides information about how Redis reacts to different POSIX signals such as `SIGTERM` and `SIGSEGV`.\n\nThe information in this document **only applies to Redis version 2.6 or greater**."},{"id":"sigterm-and-sigint","title":"SIGTERM and SIGINT","role":"content","text":"The `SIGTERM` and `SIGINT` signals tell Redis to shut down gracefully. When the server receives this signal,\nit does not immediately exit. Instead, it schedules\na shutdown similar to the one performed by the [`SHUTDOWN`](https://redis.io/docs/latest/commands/shutdown) command. The scheduled shutdown starts as soon as possible, specifically as long as the\ncurrent command in execution terminates (if any), with a possible additional\ndelay of 0.1 seconds or less.\n\nIf the server is blocked by a long-running Lua script,\nkill the script with [`SCRIPT KILL`](https://redis.io/docs/latest/commands/script-kill) if possible. The scheduled shutdown will\nrun just after the script is killed or terminates spontaneously.\n\nThis shutdown process includes the following actions:\n\n* If there are any replicas lagging behind in replication:\n  * Pause clients attempting to write with [`CLIENT PAUSE`](https://redis.io/docs/latest/commands/client-pause) and the `WRITE` option.\n  * Wait up to the configured `shutdown-timeout` (default 10 seconds) for replicas to catch up with the master's replication offset.\n* If a background child is saving the RDB file or performing an AOF rewrite, the child process is killed.\n* If the AOF is active, Redis calls the `fsync` system call on the AOF file descriptor to flush the buffers on disk.\n* If Redis is configured to persist on disk using RDB files, a synchronous (blocking) save is performed. Since the save is synchronous, it doesn't use any additional memory.\n* If the server is daemonized, the PID file is removed.\n* If the Unix domain socket is enabled, it gets removed.\n* The server exits with an exit code of zero.\n\nIF the RDB file can't be saved, the shutdown fails, and the server continues to run in order to ensure no data loss.\nLikewise, if the user just turned on AOF, and the server triggered the first AOF rewrite in order to create the initial AOF file but this file can't be saved, the shutdown fails and the server continues to run.\nSince Redis 2.6.11, no further attempt to shut down will be made unless a new `SIGTERM` is received or the [`SHUTDOWN`](https://redis.io/docs/latest/commands/shutdown) command is issued.\n\nSince Redis 7.0, the server waits for lagging replicas up to a configurable `shutdown-timeout`, 10 seconds by default, before shutting down.\nThis provides a best effort to minimize the risk of data loss in a situation where no save points are configured and AOF is deactivated.\nBefore version 7.0, shutting down a heavily loaded master node in a diskless setup was more likely to result in data loss.\nTo minimize the risk of data loss in such setups, trigger a manual [`FAILOVER`](https://redis.io/docs/latest/commands/failover) (or [`CLUSTER FAILOVER`](https://redis.io/docs/latest/commands/cluster-failover)) to demote the master to a replica and promote one of the replicas to a new master before shutting down a master node."},{"id":"sigsegv-sigbus-sigfpe-and-sigill","title":"SIGSEGV, SIGBUS, SIGFPE and SIGILL","role":"content","text":"The following signals are handled as a Redis crash:\n\n* SIGSEGV\n* SIGBUS\n* SIGFPE\n* SIGILL\n\nOnce one of these signals is trapped, Redis stops any current operation and performs the following actions:\n\n* Adds a bug report to the log file. This includes a stack trace, dump of registers, and information about the state of clients.\n* Since Redis 2.8, a fast memory test is performed as a first check of the reliability of the crashing system.\n* If the server was daemonized, the PID file is removed.\n* Finally the server unregisters its own signal handler for the received signal and resends the same signal to itself to make sure that the default action is performed, such as dumping the core on the file system."},{"id":"what-happens-when-a-child-process-gets-killed","title":"What happens when a child process gets killed","role":"content","text":"When the child performing the Append Only File rewrite gets killed by a signal,\nRedis handles this as an error and discards the (probably partial or corrupted)\nAOF file. It will attempt the rewrite again later.\n\nWhen the child performing an RDB save is killed, Redis handles the\ncondition as a more severe error. While the failure of an\nAOF file rewrite can cause AOF file enlargement, failed RDB file\ncreation reduces durability.\n\nAs a result of the child producing the RDB file being killed by a signal,\nor when the child exits with an error (non zero exit code), Redis enters\na special error condition where no further write command is accepted.\n\n* Redis will continue to reply to read commands.\n* Redis will reply to all write commands with a `MISCONFIG` error.\n\nThis error condition will persist until it becomes possible to create an RDB file successfully."},{"id":"kill-the-rdb-file-without-errors","title":"Kill the RDB file without errors","role":"content","text":"Sometimes the user may want to kill the RDB-saving child process without\ngenerating an error. Since Redis version 2.6.10, this can be done using the signal `SIGUSR1`. This signal is handled in a special way:\nit kills the child process like any other signal, but the parent process will\nnot detect this as a critical error and will continue to serve write\nrequests."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/bloom/commands","title":"Probabilistic data structure commands","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/bloom/commands/","summary":"Lists probabilistic data structure commands and provides links to the command reference pages.","tags":["docs","operate","stack"],"last_updated":"2026-09-22T10:15:10+01:00","page_type":"content","content_hash":"93bc9bcbff68561acc5599fed212334f1b80a21d685244d708fe074d36de91f6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The following tables list probabilistic data structure commands. See the command links for more information about each command's syntax, arguments, and examples."},{"id":"bloom-filter-commands","title":"Bloom filter commands","role":"content","text":"| Command | Redis<br />Software | Redis<br />Cloud | Description |\n|:--------|:----------------------|:-----------------|:------|\n| [BF.ADD](https://redis.io/docs/latest/commands/bf.add) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\"><nobr>&#x2705; Flexible & Annual</nobr></span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Adds an item to the filter. |\n| [BF.EXISTS](https://redis.io/docs/latest/commands/bf.exists) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Checks if an item exists in the filter. |\n| [BF.INFO](https://redis.io/docs/latest/commands/bf.info) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Returns information about a Bloom filter. |\n| [BF.INSERT](https://redis.io/docs/latest/commands/bf.insert) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Adds multiple items to a filter. If the key does not exist, it creates a new filter. |\n| [BF.LOADCHUNK](https://redis.io/docs/latest/commands/bf.loadchunk) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Restores a Bloom filter previously saved with [BF.SCANDUMP](https://redis.io/docs/latest/commands/bf.scandump). |\n| [BF.MADD](https://redis.io/docs/latest/commands/bf.madd) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Adds multiple items to the filter. |\n| [BF.MEXISTS](https://redis.io/docs/latest/commands/bf.mexists) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | For multiple items, checks if each item exists in the filter. |\n| [BF.RESERVE](https://redis.io/docs/latest/commands/bf.reserve) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Creates a Bloom filter. Sets the false positive rate and capacity. |\n| [BF.SCANDUMP](https://redis.io/docs/latest/commands/bf.scandump) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Starts an incremental save of a Bloom filter. |"},{"id":"cuckoo-filter-commands","title":"Cuckoo filter commands","role":"content","text":"| Command | Redis<br />Software | Redis<br />Cloud | Description |\n|:--------|:----------------------|:-----------------|:------|\n| [CF.ADD](https://redis.io/docs/latest/commands/cf.add) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\"><nobr>&#x2705; Flexible & Annual</nobr></span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Adds an item to a filter. |\n| [CF.ADDNX](https://redis.io/docs/latest/commands/cf.addnx) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Adds an item to a filter only if the item does not already exist. |\n| [CF.COUNT](https://redis.io/docs/latest/commands/cf.count) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Returns the probable number of times an item occurs in the filter. |\n| [CF.DEL](https://redis.io/docs/latest/commands/cf.del) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Removes one instance of an item from the filter. |\n| [CF.EXISTS](https://redis.io/docs/latest/commands/cf.exists) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Checks if an item exists in the filter. |\n| [CF.INFO](https://redis.io/docs/latest/commands/cf.info) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Returns information about a cuckoo filter. |\n| [CF.INSERT](https://redis.io/docs/latest/commands/cf.insert) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Adds multiple items to a filter. Optionally sets the capacity if the filter does not already exist. |\n| [CF.INSERTNX](https://redis.io/docs/latest/commands/cf.insertnx) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Adds multiple items to a filter if they do not already exist. Optionally sets the capacity if the filter does not already exist. |\n| [CF.LOADCHUNK](https://redis.io/docs/latest/commands/cf.loadchunk) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Restores a cuckoo filter previously saved with [CF.SCANDUMP](https://redis.io/docs/latest/commands/cf.scandump). |\n| [CF.MEXISTS](https://redis.io/docs/latest/commands/cf.mexists) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | For multiple items, checks if each item exists in the filter. |\n| [CF.RESERVE](https://redis.io/docs/latest/commands/cf.reserve) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Creates a cuckoo filter and sets its capacity. |\n| [CF.SCANDUMP](https://redis.io/docs/latest/commands/cf.scandump) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Starts an incremental save of a cuckoo filter. |"},{"id":"count-min-sketch-commands","title":"Count-min sketch commands","role":"content","text":"| Command | Redis<br />Software | Redis<br />Cloud | Description |\n|:--------|:----------------------|:-----------------|:------|\n| [CMS.INCRBY](https://redis.io/docs/latest/commands/cms.incrby) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\"><nobr>&#x2705; Flexible & Annual</nobr></span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Increases item counts. |\n| [CMS.INFO](https://redis.io/docs/latest/commands/cms.info) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Returns width, depth, and total count of the sketch. |\n| [CMS.INITBYDIM](https://redis.io/docs/latest/commands/cms.initbydim) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Initializes a count-min sketch to the specified dimensions (width and depth). |\n| [CMS.INITBYPROB](https://redis.io/docs/latest/commands/cms.initbyprob) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Initializes a count-min sketch to allow the specified overestimation percent for the item count and the probability of overestimation. |\n| [CMS.MERGE](https://redis.io/docs/latest/commands/cms.merge) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Merges several sketches into one sketch. |\n| [CMS.QUERY](https://redis.io/docs/latest/commands/cms.query) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Returns the count for one or more items in a sketch. |"},{"id":"top-k-commands","title":"Top-k commands","role":"content","text":"| Command | Redis<br />Software | Redis<br />Cloud | Description |\n|:--------|:----------------------|:-----------------|:------|\n| [TOPK.ADD](https://redis.io/docs/latest/commands/topk.add) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\"><nobr>&#x2705; Flexible & Annual</nobr></span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Adds an item to the data structure. |\n| [TOPK.COUNT](https://redis.io/docs/latest/commands/topk.count) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Returns probable item counts. |\n| [TOPK.INCRBY](https://redis.io/docs/latest/commands/topk.incrby) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Increases the score of an item by the specified number. |\n| [TOPK.INFO](https://redis.io/docs/latest/commands/topk.info) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Returns the number of required items (k), width, depth, and decay values. |\n| [TOPK.LIST](https://redis.io/docs/latest/commands/topk.list) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Returns the keys of items in the top-k list. Optionally returns their item counts. |\n| [TOPK.QUERY](https://redis.io/docs/latest/commands/topk.query) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Checks whether an item is one of top-k items. |\n| [TOPK.RESERVE](https://redis.io/docs/latest/commands/topk.reserve) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Initializes a top-k with the specified number of top occurring items to keep, width, depth, and decay. |"},{"id":"t-digest-sketch-commands","title":"T-digest sketch commands","role":"content","text":"| Command | Redis<br />Software | Redis<br />Cloud | Description |\n|:--------|:----------------------|:-----------------|:------|\n| [TDIGEST.ADD](https://redis.io/docs/latest/commands/tdigest.add) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\"><nobr>&#x2705; Flexible & Annual</nobr></span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Adds one or more samples to a t-digest sketch. |\n| [TDIGEST.CDF](https://redis.io/docs/latest/commands/tdigest.cdf) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Estimates the fraction of all observations which are less than or equal to the specified value. |\n| [TDIGEST.CREATE](https://redis.io/docs/latest/commands/tdigest.create) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Allocates memory and initializes a t-digest sketch. |\n| [TDIGEST.INFO](https://redis.io/docs/latest/commands/tdigest.info) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Returns information about the t-digest sketch. |\n| [TDIGEST.MAX](https://redis.io/docs/latest/commands/tdigest.max) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Returns the maximum value from the sketch. |\n| [TDIGEST.MERGE](https://redis.io/docs/latest/commands/tdigest.merge) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Copies values from one sketch to another. |\n| [TDIGEST.MIN](https://redis.io/docs/latest/commands/tdigest.min) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Returns the minimum value from the sketch. |\n| [TDIGEST.QUANTILE](https://redis.io/docs/latest/commands/tdigest.quantile) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Estimates one or more cutoffs. |\n| [TDIGEST.RESET](https://redis.io/docs/latest/commands/tdigest.reset) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Resets the sketch and reinitializes it. |\n| [TDIGEST.TRIMMED_MEAN](https://redis.io/docs/latest/commands/tdigest.trimmed_mean) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Estimates the mean value from the sketch, excluding values outside the specified range. |"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/bloom/config","title":"Probabilistic data structure configuration compatibility with Redis Software","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/bloom/config/","summary":"Probabilistic data structure configuration settings supported by Redis Software and Redis Cloud.","tags":["docs","operate","stack"],"last_updated":"2026-09-22T10:15:10+01:00","page_type":"content","content_hash":"120b1a7f9270ae02c4eefba51490625061a9dfd45728a063918129180b30d0d9","sections":[{"id":"configure-probabilistic-data-structures-in-redis-software","title":"Configure probabilistic data structures in Redis Software","role":"configuration","text":"[Redis Software](https://redis.io/docs/latest/operate/rs) lets you manually change any [RedisBloom configuration setting](https://redis.io/docs/latest/develop/data-types/probabilistic/configuration#redisbloom-configuration-parameters).\n\nTo change the RedisBloom configuration using the Redis Software Cluster Manager UI:\n\n  1. From the **Databases** list, select the database, then click **Configuration**.\n\n  1. Select the **Edit** button.\n\n  1. In the **Capabilities** section, click **Parameters**.\n\n  1. After you finish editing the module's configuration parameters, click **Done** to close the parameter editor.\n\n  1. Click **Save**."},{"id":"configure-probabilistic-data-structures-in-redis-cloud","title":"Configure probabilistic data structures in Redis Cloud","role":"configuration","text":"[Redis Cloud](https://redis.io/docs/latest/operate/rc) does not let you configure RedisBloom manually. However, if you have a Flexible or Annual [subscription](https://redis.io/docs/latest/operate/rc/subscriptions), you can contact [support](https://redis.com/company/support/) to request a configuration change. You cannot change RedisBloom configuration for Free or Fixed subscriptions."},{"id":"configuration-settings","title":"Configuration settings","role":"configuration","text":"See [configuration parameters](https://redis.io/docs/latest/develop/data-types/probabilistic/configuration) in the Develop section for parameter details and compatibility with Redis Software and Redis Cloud."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/bloom","title":"Probabilistic data structures","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/bloom/","summary":"Probabilistic data structures","aliases":["/stack/bloom"],"tags":["docs","operate","stack"],"last_updated":"2026-10-02T14:53:57+01:00","children":[{"id":"operate/oss_and_stack/stack-with-enterprise/bloom/commands","summary":"Lists probabilistic data structure commands and provides links to the command reference pages.","title":"Probabilistic data structure commands","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/bloom/commands/"},{"id":"operate/oss_and_stack/stack-with-enterprise/bloom/config","summary":"Probabilistic data structure configuration settings supported by Redis Software and Redis Cloud.","title":"Probabilistic data structure configuration compatibility with Redis Software","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/bloom/config/"}],"page_type":"content","content_hash":"3c252a61b94d07839f59bfc31cfef26f771e3c1fd5f2ddf24145cebf1d3c3427","sections":[{"id":"overview","title":"Overview","role":"overview","text":"A Bloom filter is a probabilistic data structure which provides an\nefficient way to verify that an entry is certainly *not* in a set. This\nmakes it especially ideal when trying to search for items on\nexpensive-to-access resources (such as over a network or disk): If I\nhave a large on-disk database and I want to know if the key *foo* exists\nin it, I can query the Bloom filter first, which tells me with a\ncertainty whether it potentially exists (and then the disk lookup can\ncontinue) or whether it does *not* exist, and in this case I can forego\nthe expensive disk lookup and simply send a negative reply up the stack.\n\nWhile it's possible to use other data structures (such as a hash table)\nto perform this, Bloom filters are also especially useful in that they\noccupy very little space per element, typically counted in the number of\n*bits* (not bytes!). There exists a percentage of false positives\n(which is controllable), but for an initial test of whether a key exists\nin a set, they provide excellent speed and most importantly excellent\nspace efficiency.\n\nBloom filters are used in a wide variety of applications such as ad\nserving - making sure a user doesn't see an ad too often; likewise in\ncontent recommendation systems - ensuring recommendations don't appear\ntoo often, in databases - quickly checking if an entry exists in a\ntable before accessing it on disk, and so on."},{"id":"how-bloom-filters-work","title":"How Bloom filters work","role":"content","text":"Most of the literature on Bloom filter uses highly symbolic and/or\nmathematical descriptions to describe it. If you're mathematically\nchallenged like yours truly, you might find my explanation more useful.\n\nA Bloom filter is an array of many bits. When an element is 'added' to a\nbloom filter, the element is hashed. Then *bit\\[hashval % nbits\\]* is\nset to 1. This looks fairly similar to how buckets in a hash table are\nmapped. To check if an item is present or not, the hash is computed and\nthe filter sees if the corresponding bit is set or not.\n\n![](https://redis.io/docs/latest/images/rs/rebloom-hash1.png)\n\nOf course, this is subject to collisions. If a collision happens, the\nfilter returns a false positive - indicating that the entry is\nindeed found (note that a bloom filter never returns a false\nnegative, that is, claim that something does not exist when it fact it\nis present).\n\nIn order to reduce the risk of collisions, an entry may use more than\none bit: the entry is hashed *bits_per_element (bpe)* times with a\ndifferent seed for each iteration resulting in a different hash value,\nand for each hash value, the corresponding *hash % nbits* bit is set. To\ncheck if an entry exists, the candidate key is also hashed *bpe* times,\nand if any corresponding bit is *unset*, then it can be determined with\ncertainty that the item does *not* exist.\n\nThe actual value of *bpe* is determined at the time the filter is\ncreated. Generally the more bits per element, the lower the likelihood\nof false positives.\n\n![](https://redis.io/docs/latest/images/rs/rebloom-hash3.png)\n\nIn the example above, all three bits would need to be set in order for\nthe filter to return a positive result.\n\nAnother value affecting the accuracy of a Bloom filter is its *fill\nratio*, or how many bits in the filter are actually set. If a filter has\na vast majority of bits set, the likelihood of any specific lookup\nreturning false is decreased, and thus the possibility of the filter\nreturning false positives is increased."},{"id":"scalable-bloom-filters","title":"Scalable Bloom filters","role":"content","text":"Typically Bloom filters must be created with a foreknowledge of how many\nentries they contain. The *bpe* number needs to be fixed, and\nlikewise, the width of the bit array is also fixed.\nUnlike hash tables, Bloom filters cannot be \"rebalanced\" because there\nis no way to know *which* entries are part of the filter (the filter can\nonly determine whether a given entry is *not* present, but does not\nactually store the entries which *are* present).\n\nIn order to allow Bloom filters to 'scale' and be able to accommodate\nmore elements than they've been designed to, they may be stacked. Once a\nsingle Bloom filter reaches capacity, a new one is created atop it.\nTypically the new filter has greater capacity than the previous\none in order to reduce the likelihood of needing to stack yet another\nfilter.\n\nIn a stackable (scalable) Bloom filter, checking for membership now\ninvolves inspecting each layer for presence. Adding new items now\ninvolves checking that it does not exist beforehand, and adding it to\nthe current filter. Hashes still only need to be computed once, however.\n\nWhen creating a Bloom filter - even a scalable one, it's important to\nhave a good idea of how many items it is expected to contain. A filter\nwhose initial layer can only contain a small number of elements will\ndegrade performance significantly because it will take more layers to\nreach a larger capacity."},{"id":"more-info","title":"More info","role":"content","text":"- [Probabilistic data structures quick start](https://redis.io/docs/latest/develop/data-types/probabilistic/bloom-filter)\n- [Probabilistic data structure commands](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/bloom/commands)\n- [Probabilistic data structure configuration](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/bloom/config)\n- [RedisBloom source](https://github.com/RedisBloom/RedisBloom/)"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1","title":"Gears","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/","summary":"RedisGears supports batch and event-driven processing for Redis data.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/"],"tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:38+01:00","children":[{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/installing-redisgears","summary":"","title":"Install RedisGears","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/installing-redisgears/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/python","summary":"The RedisGears Python plugin allows you to run RedisGears functions with Python.","title":"RedisGears Python plugin","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/python/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm","summary":"The RedisGears JVM plugin allows you to run RedisGears functions in the Java virtual machine.","title":"RedisGears JVM plugin","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/register-events","summary":"Register RedisGears functions to run when certain events occur in a Redis database.","title":"Register events","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/register-events/"}],"page_type":"content","content_hash":"f22b1ffec3646fcb6001d8cd7bbb6befa2b58b936aae5eb1db8da3fb355f2ffc","sections":[{"id":"what-is-redisgears","title":"What is RedisGears?","role":"overview","text":"RedisGears is an engine for data processing in Redis. RedisGears supports batch and event-driven processing for Redis data. To use RedisGears, you write functions that describe how your data should be processed. You then submit this code to your Redis deployment for remote execution."},{"id":"supported-languages","title":"Supported languages","role":"compatibility","text":"As of RedisGears v1.2, you can enable a plugin to select which programming language to use. It currently supports code written in either [Python](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/python) or [Java](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm).\n\nPrior to v1.2, RedisGears only supported Python. However, an internal C API exists and can be used by other Redis modules. Support for other languages is being planned."},{"id":"getting-started-with-redisgears","title":"Getting started with RedisGears","role":"setup","text":"RedisGears is implemented by a Redis module. To use RedisGears, you'll need to make sure that your Redis deployment has the module installed. [Redis Software](https://redis.io/docs/latest/operate/rs/) supports the module natively.\n\nIf you're running Redis Open Source, you'll also need to [install the RedisGears module](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/installing-redisgears) before using it.\n\nTo get started with RedisGears, see the quick start tutorial for [Python](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/python/quickstart) or [Java](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/quickstart)."},{"id":"write-behind-caching-patterns","title":"Write-behind caching patterns","role":"content","text":"Redis users typically implement caching by using the look-aside pattern. However, with RedisGears, you can implement write-behind caching strategies as well.\n\nRedis publishes RedisGears recipes to support write-behind. You can learn how to use these recipes in our write-behind caching guides for [Python](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/python/recipes/write-behind) and [Java](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/recipes/write-behind)."},{"id":"more-info","title":"More info","role":"content","text":"- [RedisGears source](https://github.com/RedisGears/RedisGears)"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/installing-redisgears","title":"Install RedisGears","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/installing-redisgears/","summary":"","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/installing-redisgears/"],"tags":["docs","operate","stack"],"last_updated":"2026-10-06T08:53:14-07:00","page_type":"content","content_hash":"4d69d22cf983719ed6a15ff1f096e7a994486af9438e9fbe1636f136d9b4dfa5","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Before you can use RedisGears, you have to install the RedisGears module on your Redis Software cluster."},{"id":"minimum-requirements","title":"Minimum requirements","role":"content","text":"- Redis Software 6.0.12 or later\n- The [cluster is setup](https://redis.io/docs/latest/operate/rs/clusters/new-cluster-setup) and all of the nodes are joined to the cluster"},{"id":"install-redisgears","title":"Install RedisGears","role":"setup","text":"If your cluster uses Redis Software v6.0.12 or later and has internet access, you only need to download the RedisGears package. It automatically fetches dependencies like the Python and JVM plugins during online installation.\n\nOffline installation requires you to manually upload dependencies to the primary node."},{"id":"install-redisgears-and-dependencies","title":"Install RedisGears and dependencies","role":"setup","text":"1. Download the **RedisGears** package from the Redis Software [download center](https://cloud.redis.io/#/rlec-downloads).\n\n    > [!NOTE]\n    > For offline installation of RedisGears v1.2 and later, you also need to download the **RedisGears Dependencies** packages for both Python and Java.\n    > <br/>\n    > For RedisGears v1.0, you only need the Python dependency package.\n    >     \n\n1. Upload the RedisGears package to a node in the cluster.\n\n1. For offline installation only, copy the dependencies to the primary node.\n\n    > [!NOTE]\n    > Skip this step unless your cluster does not have internet access.\n    >     \n\n    1. For versions 7.2.4 and later, copy the dependencies to `$modulesdatadir/rg/<version-integer>/<OS_name>/<architecture>/deps/`:\n\n        [code example]\n\n    1. For versions 6.4.2 and earlier, copy the dependencies to `$modulesdatadir/rg/<version-integer>/deps/`:\n    \n        [code example]\n\n    Replace these fields with your own values:\n\n    - `<OS>`: the operating system running Redis Software\n    - `<version>`: the RedisGears version `(x.y.z)`\n    - `<version-integer>`: the RedisGears version as an integer, calculated as <nobr>`(x*10000 + y*100 + z)`</nobr>\n\n        For example, the `<version-integer>` for RedisGears version 1.2.5 is 10205.\n\n    - `<OS_name>`: the operating system's name\n    - `<architecture>`: the node's architecture\n\n1. Add RedisGears to the cluster with a `POST` request to the primary node's [`/v2/modules`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/modules#post-module-v2) REST API endpoint:\n\n    [code example]\n\nHere, the *module* parameter specifies the full path of the module package and must be submitted as form-data. In addition, the package must be available and accessible to the server processing the request.\n\nAfter the install is complete, RedisGears will appear in the list of available modules on the **settings** and **create database** pages of the Redis Software admin console."},{"id":"enable-redisgears-for-a-database","title":"Enable RedisGears for a database","role":"content","text":"After installation, create a new database and enable RedisGears:\n\n- [With Python](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/python/install)\n\n- [With the JVM](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/install)"},{"id":"upgrade-redisgears-for-existing-databases","title":"Upgrade RedisGears for existing databases","role":"content","text":"To upgrade RedisGears for an existing database after installing a new version, use [`rladmin upgrade db`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/upgrade#upgrade-db):\n\n[code example]\n\nThe following example shows how to upgrade a database named `shopping-cart` to RedisGears version 1.2.9 without changing its configuration:\n\n[code example]\n\n> [!NOTE]\n> These command examples also upgrade the database to the latest Redis version on the cluster. For more module upgrade information and examples, see [Upgrade modules](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/upgrade-module)."},{"id":"uninstall-redisgears","title":"Uninstall RedisGears","role":"content","text":"To uninstall RedisGears, make a [`DELETE` request to the `/v2/modules` REST API endpoint](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/modules#delete-module-v2)."}],"examples":[{"id":"install-redisgears-and-dependencies-ex0","language":"sh","code":"cp redisgears-jvm.<OS>.<version>.tgz $modulesdatadir/rg/<version-integer>/<OS_name>/<architecture>/deps/","section_id":"install-redisgears-and-dependencies"},{"id":"install-redisgears-and-dependencies-ex1","language":"sh","code":"cp redisgears-jvm.<OS>.<version>.tgz $modulesdatadir/rg/<version-integer>/deps/","section_id":"install-redisgears-and-dependencies"},{"id":"install-redisgears-and-dependencies-ex2","language":"sh","code":"POST https://[host][:port]/v2/modules\n    {\"module=@/tmp/redisgears.<OS>.<version>.zip\"}","section_id":"install-redisgears-and-dependencies"},{"id":"upgrade-redisgears-for-existing-databases-ex0","language":"sh","code":"rladmin upgrade db <database-name-or-ID> and module module_name rg version <new_version_integer> module_args \"<module arguments>\"","section_id":"upgrade-redisgears-for-existing-databases"},{"id":"upgrade-redisgears-for-existing-databases-ex1","language":"sh","code":"rladmin upgrade db shopping-cart and module module_name rg version 10209 module_args keep_args","section_id":"upgrade-redisgears-for-existing-databases"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/accumulate","title":"Accumulate","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/accumulate/","summary":"Reduces many records in the pipe to a single record.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/classes/gearsbuilder/accumulate/"],"tags":["docs","operate","stack"],"last_updated":"2026-04-21T10:45:15+01:00","page_type":"content","content_hash":"bf1adc0257ef45533e1f1811bb9cd7e9e06d36e067a113c49eaa115c7879b6fc","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[code example]\n\nAccumulate is a many-to-one function that iterates through the records in the pipe and reduces them to a single record.\n\nYou can provide a parameter to set the initial accumulator value. Otherwise, the initial accumulator object is null."},{"id":"parameters","title":"Parameters","role":"parameters","text":"Type parameters:\n\n| Name | Description |\n|------|-------------|\n| I | The template type of the returned builder |\n\nFunction parameters:\n\n| Name | Type | Description |\n|------|------|-------------|\n| accumulator | <nobr>AccumulateOperation<T,​I></nobr> | A function with logic to update the accumulator value with each record |\n| initialValue | template type I | The initial value of the accumulated object |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns a GearsBuilder object with a new template type."},{"id":"examples","title":"Examples","role":"example","text":"Both of the following examples count the number of records in the pipeline.\n\nWithout the `initialValue` parameter:\n\n[code example]\n\nWith the `initialValue` parameter set to 0:\n\n[code example]"}],"examples":[{"id":"overview-ex0","language":"java","code":"public <I extends java.io.Serializable> GearsBuilder<I> accumulate​(\n    gears.operations.AccumulateOperation<T,​I> accumulator)\n\npublic <I extends java.io.Serializable> GearsBuilder<I> accumulate​(\n    I initialValue, \n    gears.operations.AccumulateOperation<T,​I> accumulator)","section_id":"overview"},{"id":"examples-ex0","language":"java","code":"GearsBuilder.CreateGearsBuilder(reader).accumulate((a, r)->{\n    Integer ret = null;\n    if (a == null) {\n\t    ret = 1;\n    } else {\n\t    ret = (Integer)a;\n    }\n    return ret + 1;\n});","section_id":"examples"},{"id":"examples-ex1","language":"java","code":"GearsBuilder.CreateGearsBuilder(reader).accumulate(0, (a, r)->{\n   \treturn a + 1;\n});","section_id":"examples"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/accumulateby","title":"AccumulateBy","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/accumulateby/","summary":"Groups records and reduces each group to a single record per group.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/classes/gearsbuilder/accumulateby/"],"tags":["docs","operate","stack"],"last_updated":"2026-04-21T10:45:15+01:00","page_type":"content","content_hash":"57b43a574c3fc4d51fb89d30401835846ed853fcfd17857ed2d9b4b7e31aa001","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[code example]\n\nIterates through the records in the pipe, groups them based on the provided extractor, and then reduces each group to a single record per group with the accumulator function.\n\nThe initial value of the accumulator is null unless you provide a value initializer operation as a parameter."},{"id":"parameters","title":"Parameters","role":"parameters","text":"Type parameters:\n\n| Name | Description |\n|------|-------------|\n| I | The template type of the returned builder |\n\nFunction parameters:\n\n| Name | Type | Description |\n|------|------|-------------|\n| accumulator | <nobr>AccumulateByOperation<T,​I></nobr> | A function with logic to update the accumulator value with each record |\n| extractor | ExtractorOperation<T> | Extracts a specific value from each record |\n| valueInitializer | ValueInitializerOperation<I> | Whenever the accumulated value is null, use this function to initialize it |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns a GearsBuilder object with a new template type."},{"id":"examples","title":"Examples","role":"example","text":"Both of the following examples count the number of unique values.\n\nWithout the `valueInitializer` parameter:\n\n[code example]\n\nWith the `valueInitializer` parameter:\n\n[code example]"}],"examples":[{"id":"overview-ex0","language":"java","code":"public <I extends java.io.Serializable> GearsBuilder<I> accumulateBy​(\n\tgears.operations.ExtractorOperation<T> extractor, \n\tgears.operations.AccumulateByOperation<T,​I> accumulator)\n\npublic <I extends java.io.Serializable> GearsBuilder<I> accumulateBy​(\n\tgears.operations.ValueInitializerOperation<I> valueInitializer, \n\tgears.operations.ExtractorOperation<T> extractor, \n\tgears.operations.AccumulateByOperation<T,​I> accumulator)","section_id":"overview"},{"id":"examples-ex0","language":"java","code":"GearsBuilder.CreateGearsBuilder(reader).\n   \taccumulateBy(r->{\n   \t\treturn r.getStringVal();\n   \t},(k, a, r)->{\n   \t\tInteger ret = null;\n   \t\tif(a == null) {\n   \t\t\tret = 0;\n   \t\t}else {\n   \t\t\tret = (Integer)a;\n   \t\t}\n   \t\treturn ret + 1;\n});","section_id":"examples"},{"id":"examples-ex1","language":"java","code":"GearsBuilder.CreateGearsBuilder(reader).\n    \taccumulateBy(()->{\n   \t\treturn 0;\n   \t},r->{\n   \t\treturn r.getStringVal();\n   \t},(k, a, r)->{\n   \t\treturn a + 1;\n   \t});","section_id":"examples"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/asyncfilter","title":"AsyncFilter","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/asyncfilter/","summary":"Asynchronously filters out records in the pipe based on a given condition.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/classes/gearsbuilder/asyncfilter/"],"tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:38+01:00","page_type":"content","content_hash":"3806a746b30aaa24908a0cf2e0c86e73c429e9dd1a417ee104ac0f79491710d7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[code example]\n\nThe `asyncFilter` function allows you to use a [`GearsFuture`](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsfuture) object to asynchronously filter out records in the pipe based on a given condition.\n\nThe filter operation should contain a conditional statement and return a boolean for each record:\n- If `true`, the record will continue through the pipe. \n- If `false`, it filters out the record."},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Name | Type | Description |\n|------|------|-------------|\n| filter | AsyncFilterOperation<T> | A function that checks a condition for each record in the pipe. Returns a boolean. |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns a GearsBuilder object with the same template type as the input builder."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"java","code":"public GearsBuilder<T> asyncFilter​(\n    gears.operations.AsyncFilterOperation<T> filter)","section_id":"overview"},{"id":"example-ex0","language":"java","code":"GearsBuilder.CreateGearsBuilder(reader).map(r->r.getKey()).\n\tasyncFilter(r->{\n\t\tGearsFuture<Boolean> f = new GearsFuture<Boolean>();\n\t\tnew Thread(new Runnable() {\n\t\t\t\t\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\ttry {\n\t\t\t\t\tThread.sleep(1);\n\t\t\t\t\t\t\n\t\t\t\t\tf.setResult(r.equals(\"x\"));\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\t\t\t\t\t\n\t\t\t}\n\t\t}).start();\n\t\treturn f;\n});","section_id":"example"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/asyncforeach","title":"AsyncForeach","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/asyncforeach/","summary":"For each record in the pipe, asynchronously runs some operations.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/classes/gearsbuilder/asyncforeach/"],"tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:38+01:00","page_type":"content","content_hash":"cc4b04f8dac52da22aa3efae1161107613619fe3a610a8bb521ce71b712f4da3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[code example]\n\nThe `asyncForeach` function allows you to use a [`GearsFuture`](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsfuture) object to define a set of operations and run them asynchronously for each record in the pipe."},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Name | Type | Description |\n|------|------|-------------|\n| foreach | AsyncForeachOperation<T> | The set of operations to run for each record |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns a GearsBuilder object with a new template type."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"java","code":"public GearsBuilder<T> asyncForeach​(\n    gears.operations.AsyncForeachOperation<T> foreach)","section_id":"overview"},{"id":"example-ex0","language":"java","code":"GearsBuilder.CreateGearsBuilder(reader).map(r->r.getKey()).\n\tasyncForeach(r->{\n\t\tGearsFuture<Serializable> f = new GearsFuture<Serializable>();\n\t\tnew Thread(new Runnable() {\n\t\t\t\t\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\ttry {\n\t\t\t\t\tThread.sleep(1);\n\t\t\t\t\t\t\n\t\t\t\t\tf.setResult(r);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\t\t\t\t\t\n\t\t\t}\n\t\t}).start();\n\t\treturn f;\n});","section_id":"example"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/asyncmap","title":"AsyncMap","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/asyncmap/","summary":"Asynchronously maps records one-to-one.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/classes/gearsbuilder/asyncmap/"],"tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:38+01:00","page_type":"content","content_hash":"c50c40a347252212b7a6cc6f5cb9719cb9490f47e4e3d09d615a99a3a11b0af9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[code example]\n\nThe `asyncMap` function allows you to use a [`GearsFuture`](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsfuture) object to asynchronously map each input record in the pipe to an output record, one-to-one."},{"id":"parameters","title":"Parameters","role":"parameters","text":"Type parameters:\n\n| Name | Description |\n|------|-------------|\n| I | The template type of the returned builder |\n\nFunction parameters:\n\n| Name | Type | Description |\n|------|------|-------------|\n| mapper | <nobr>AsyncMapOperation<T,​I></nobr> | For each input record, returns a new output record |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns a GearsBuilder object with a new template type."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"java","code":"public <I extends java.io.Serializable> GearsBuilder<I> asyncMap​(\n\tgears.operations.AsyncMapOperation<T,​I> mapper)","section_id":"overview"},{"id":"example-ex0","language":"java","code":"GearsBuilder.CreateGearsBuilder(reader).\n\tasyncMap(r->{\n\t\tGearsFuture<String> f = new GearsFuture<String>();\n\t\tnew Thread(new Runnable() {\n\t\t\t\t\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\ttry {\n\t\t\t\t\tThread.sleep(1);\n\t\t\t\t\t\n\t\t\t\t\tf.setResult(\"done\");\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\t\t\t\t\t\n\t\t\t}\n\t\t}).start();\n\t\treturn f;\n});","section_id":"example"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/callnext","title":"CallNext","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/callnext/","summary":"Calls the next execution that overrides the command or the original command itself. A more flexible version of callNextArray.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/classes/gearsbuilder/callnext/"],"tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:38+01:00","page_type":"content","content_hash":"7c23e64368074b30ddcd3cffa2e8fcc7963e1c75a5ba6f60d0e8dffe3a137360","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[code example]\n\nWhen you override a Redis command with the [`CommandOverrider`](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/commandoverrider), use `callNext` to run the next execution that overrides the command or the original command itself.\n\nIt is a more flexible version of [`callNextArray`](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/callnextarray) since the list of string arguments does not have to be an explicit `String[]` object. This allows function calls like: <nobr>`callNext(\"key\", \"value\")`.</nobr>"},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Name | Type | Description |\n|------|------|-------------|\n| args | string | Redis command arguments |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns the command result. It could be a string or an array of strings, depending on the command."},{"id":"examples","title":"Examples","role":"example","text":"Without `String[]`:\n\n[code example]\n\nWith `String[]`:\n\n[code example]"}],"examples":[{"id":"overview-ex0","language":"java","code":"public static java.lang.Object callNext(java.lang.String... args)","section_id":"overview"},{"id":"examples-ex0","language":"java","code":"GearsBuilder.callNext(\"restaurant:1\", \"reviews\", \"50\");","section_id":"examples"},{"id":"examples-ex1","language":"java","code":"GearsBuilder.callNext(new String[]{\"restaurant:1\", \"reviews\", \"50\"});","section_id":"examples"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/callnextarray","title":"CallNextArray","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/callnextarray/","summary":"Calls the next execution that overrides the command or the original command itself.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/classes/gearsbuilder/callnextarray/"],"tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:38+01:00","page_type":"content","content_hash":"30490a358d003f9d26b91c45813ad1b44899c50c13c409719a678413a67953aa","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[code example]\n\nWhen you override a Redis command with the [`CommandOverrider`](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/commandoverrider), use `callNextArray` to run the next execution that overrides the command or the original command itself.\n\nIt accepts an array of strings, which represents the command arguments."},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Name | Type | Description |\n|------|------|-------------|\n| args | array of strings | Redis command arguments |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns the command result. It could be a string or an array of strings, depending on the command."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"java","code":"public static native java.lang.Object callNextArray(\n    java.lang.String[] command)","section_id":"overview"},{"id":"example-ex0","language":"java","code":"GearsBuilder.callNextArray(new String[]{\"restaurant:1\", \"reviews\", \"50\"});","section_id":"example"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/collect","title":"Collect","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/collect/","summary":"Collects all records to the origin shard.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/classes/gearsbuilder/collect/"],"tags":["docs","operate","stack"],"last_updated":"2026-04-21T10:45:15+01:00","page_type":"content","content_hash":"33daa573544e8ed1adbe23d4e9c8113537be14ba21d8f217b7773c8b6632cf9a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[code example]\n\nCollects all of the records to the shard where the RedisGears job started."},{"id":"parameters","title":"Parameters","role":"parameters","text":"None"},{"id":"returns","title":"Returns","role":"returns","text":"Returns a GearsBuilder object with the same template type as the input builder."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"java","code":"public GearsBuilder<T> collect()","section_id":"overview"},{"id":"example-ex0","language":"java","code":"GearsBuilder.CreateGearsBuilder(reader).collect();","section_id":"example"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/config-get","title":"ConfigGet","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/config-get/","summary":"Gets the value of a RedisGears configuration setting.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/classes/gearsbuilder/config-get/"],"tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:38+01:00","page_type":"content","content_hash":"3e4593f9d87f065688c7bbc5861e66c1e32dd394833e6651240ff142b4999ec6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[code example]\n\nGets the value of a RedisGears [configuration setting](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/Configuration).\n\n> [!NOTE]\n> You can set configuration values when you load the module or use the `RG.CONFIGSET` command."},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Name | Type | Description |\n|------|------|-------------|\n| key | string | The configuration setting to get |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns the configuration value of a RedisGears configuration setting."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"java","code":"public static java.lang.String configGet​(java.lang.String key)","section_id":"overview"},{"id":"example-ex0","language":"java","code":"GearsBuilder.configGet(\"ExecutionMaxIdleTime\");","section_id":"example"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/count","title":"Count","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/count/","summary":"Counts the number of records in the pipe.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/classes/gearsbuilder/count/"],"tags":["docs","operate","stack"],"last_updated":"2026-04-21T10:45:15+01:00","page_type":"content","content_hash":"2d5687fe17e1d361766366ecfad13edaffd4b2fc5836e50fec01227109f76f74","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[code example]\n\nCounts the number of records in the pipe and returns the total as a single record."},{"id":"parameters","title":"Parameters","role":"parameters","text":"None"},{"id":"returns","title":"Returns","role":"returns","text":"Returns a GearsBuilder object with a new template type of `Integer`."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"java","code":"public GearsBuilder<java.lang.Integer> count()","section_id":"overview"},{"id":"example-ex0","language":"java","code":"GearsBuilder.CreateGearsBuilder(reader).count();","section_id":"example"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/creategearsbuilder","title":"CreateGearsBuilder","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/creategearsbuilder/","summary":"Creates a new GearsBuilder object.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/classes/gearsbuilder/creategearsbuilder/"],"tags":["docs","operate","stack"],"last_updated":"2026-04-21T10:45:15+01:00","page_type":"content","content_hash":"ed7b09c2f59e7f481bc279c17c9881ad53c9e1506d7827c4deae53f7795980c5","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[code example]\n\nCreates a new `GearsBuilder` object. Use this function instead of a `GearsBuilder` constructor to avoid type warnings."},{"id":"parameters","title":"Parameters","role":"parameters","text":"Type Parameters:\n\n| Name | Description |\n|------|-------------|\n| I | The template type of the returned builder. The reader determines the type. |\n\nParameters:\n\n| Name | Type | Description |\n|------|------|-------------|\n| desc | string | The description |\n| reader | BaseReader<I> | The pipe reader |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns a new GearsBuilder object."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"java","code":"public static <I extends java.io.Serializable> GearsBuilder<I> CreateGearsBuilder​(\n    gears.readers.BaseReader<I> reader)\n\npublic static <I extends java.io.Serializable> GearsBuilder<I> CreateGearsBuilder​(\n    gears.readers.BaseReader<I> reader, \n    java.lang.String desc)","section_id":"overview"},{"id":"example-ex0","language":"java","code":"GearsBuilder.CreateGearsBuilder(reader);","section_id":"example"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/execute","title":"Execute","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/execute/","summary":"Runs a Redis command. A more flexible version of executeArray.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/classes/gearsbuilder/execute/"],"tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:38+01:00","page_type":"content","content_hash":"cb437aba1ba6caecd4af111b81320970343bd8e9e7443f086c7c65923adae1a4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[code example]\n\nRuns a Redis command, similar to [`executeArray`](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/executearray). However, the `execute` function is more flexible. Unlike `executeArray`, the list of string arguments does not have to be an explicit `String[]` object. It allows function calls like this: <nobr>`execute(\"SET\", \"key\", \"value\")`.</nobr>"},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Name | Type | Description |\n|------|------|-------------|\n| command | string | A Redis command |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns the command result. It could be a string or an array of strings, depending on the command."},{"id":"examples","title":"Examples","role":"example","text":"Without `String[]`:\n\n[code example]\n\nWith `String[]`:\n\n[code example]"}],"examples":[{"id":"overview-ex0","language":"java","code":"public static java.lang.Object execute​(java.lang.String... command)","section_id":"overview"},{"id":"examples-ex0","language":"java","code":"GearsBuilder.execute(\"SET\", \"age:maximum\", \"100\");","section_id":"examples"},{"id":"examples-ex1","language":"java","code":"GearsBuilder.execute(new String[]{\"SET\", \"age:maximum\", \"100\"});","section_id":"examples"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/executearray","title":"ExecuteArray","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/executearray/","summary":"Runs a Redis command.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/classes/gearsbuilder/executearray/"],"tags":["docs","operate","stack"],"last_updated":"2026-04-21T10:45:15+01:00","page_type":"content","content_hash":"958a8bf455b058debe3b157bac124103bde0f78bc27905b9084c2e69b43cf37e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[code example]\n\nRuns a Redis command. It accepts an array of strings, which represents the command to execute."},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Name | Type | Description |\n|------|------|-------------|\n| command | array of strings | A Redis command |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns the command result. It could be a string or an array of strings, depending on the command."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"java","code":"public static native java.lang.Object executeArray(\n    java.lang.String[] command)","section_id":"overview"},{"id":"example-ex0","language":"java","code":"GearsBuilder.executeArray(new String[]{\"SET\", \"age:maximum\", \"100\"});","section_id":"example"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/filter","title":"Filter","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/filter/","summary":"Filters out records in the pipe based on a given condition.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/classes/gearsbuilder/filter/"],"tags":["docs","operate","stack"],"last_updated":"2026-04-21T10:45:15+01:00","page_type":"content","content_hash":"7e361f4257664f25e081daa5e443ccc142e9246a88bc11b1c865db4529c8a8b1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[code example]\n\nFilters out records in the pipe based on a given condition.\n\nThe filter operation should contain a conditional statement and return a boolean for each record:\n- If `true`, the record will continue through the pipe. \n- If `false`, it filters out the record."},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Name | Type | Description |\n|------|------|-------------|\n| filter | FilterOperation<T> | A function that checks a condition for each record in the pipe. Returns a boolean. |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns a GearsBuilder object with the same template type as the input builder."},{"id":"example","title":"Example","role":"example","text":"Get all records that contain the substring \"person:\":\n\n[code example]"}],"examples":[{"id":"overview-ex0","language":"java","code":"public GearsBuilder<T> filter​(\n    gears.operations.FilterOperation<T> filter)","section_id":"overview"},{"id":"example-ex0","language":"java","code":"GearsBuilder.CreateGearsBuilder(reader).\n    filter(r->{\n        return r.getKey().contains(\"person:\");\n});","section_id":"example"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/flatmap","title":"FlatMap","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/flatmap/","summary":"Maps a single input record to one or more output records.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/classes/gearsbuilder/flatmap/"],"tags":["docs","operate","stack"],"last_updated":"2026-04-21T10:45:15+01:00","page_type":"content","content_hash":"660cc5c50794fff73b0678926fddb6913a423bd32b1141ddd6cc3574d6eac98a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[code example]\n\nMaps a single input record to one or more output records.\n\nThe FlatMap operation must return an [`Iterable`](https://docs.oracle.com/javase/8/docs/api/java/lang/Iterable.html). RedisGears \nsplits the elements from the `Iterable` object and processes them as individual records."},{"id":"parameters","title":"Parameters","role":"parameters","text":"Type parameters:\n\n| Name | Description |\n|------|-------------|\n| I | The template type of the returned builder object |\n\nFunction parameters:\n\n| Name | Type | Description |\n|------|------|-------------|\n| flatmapper | <nobr>FlatMapOperation<T,​I></nobr> | For each input record, returns one or more output records |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns a GearsBuilder object with a new template type."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"java","code":"public <I extends java.io.Serializable> GearsBuilder<I> flatMap​(\n\tgears.operations.FlatMapOperation<T,​I> flatmapper)","section_id":"overview"},{"id":"example-ex0","language":"java","code":"GearsBuilder.CreateGearsBuilder(reader).flatMap(r->{\n   \treturn r.getListVal();\n});","section_id":"example"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/foreach","title":"Foreach","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/foreach/","summary":"For each record in the pipe, runs some operations.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/classes/gearsbuilder/foreach/"],"tags":["docs","operate","stack"],"last_updated":"2026-04-21T10:45:15+01:00","page_type":"content","content_hash":"6cb034a07255093d6d5025d0fb4660e686dea3e9aefa8c9ce4583b0a95e0335c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[code example]\n\nDefines a set of operations to run for each record in the pipe."},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Name | Type | Description |\n|------|------|-------------|\n| foreach | ForeachOperation<T> | The set of operations to run for each record |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns a GearsBuilder object with a new template type."},{"id":"example","title":"Example","role":"example","text":"For each person hash, add a new full_name field that combines their first and last names:\n\n[code example]"}],"examples":[{"id":"overview-ex0","language":"java","code":"public GearsBuilder<T> foreach​(\n    gears.operations.ForeachOperation<T> foreach)","section_id":"overview"},{"id":"example-ex0","language":"java","code":"GearsBuilder.CreateGearsBuilder(reader).foreach(r->{\n    String firstName = r.getHashVal().get(\"first_name\");\n    String lastName = r.getHashVal().get(\"last_name\");\n   \tr.getHashVal().put(\"full_name\", firstName + lastName);\n});","section_id":"example"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/hashtag","title":"Hashtag","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/hashtag/","summary":"Returns a string that maps to the current shard.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/classes/gearsbuilder/hashtag/"],"tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:38+01:00","page_type":"content","content_hash":"7373d84f3ee82bf8dd7ae40870b2e75890ee31bc0f3753cf50f503fa353477b8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[code example]\n\nReturns a string that maps to the current shard according to the cluster slot mapping.\n\n> [!NOTE]\n> You can use the `hashtag` function when you need to create a key that resides on the current shard."},{"id":"parameters","title":"Parameters","role":"parameters","text":"None"},{"id":"returns","title":"Returns","role":"returns","text":"Returns a string that maps to the current shard."},{"id":"example","title":"Example","role":"example","text":"The following example uses the `hashtag` function to calculate the hslot. The string maps to the current shard.\n\n[code example]"}],"examples":[{"id":"overview-ex0","language":"java","code":"public static java.lang.String hashtag()","section_id":"overview"},{"id":"example-ex0","language":"java","code":"GearsBuilder.execute(\n    \"SET\", \n    String.format(\"key{%s}\", GearsBuilder.hashtag()), \n    \"1\"\n);","section_id":"example"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder","title":"GearsBuilder","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/","summary":"Creates a RedisGears pipeline of operations to transform data.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/classes/gearsbuilder/"],"tags":["docs","operate","stack"],"last_updated":"2026-04-21T10:45:15+01:00","children":[{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/accumulate","summary":"Reduces many records in the pipe to a single record.","title":"Accumulate","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/accumulate/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/accumulateby","summary":"Groups records and reduces each group to a single record per group.","title":"AccumulateBy","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/accumulateby/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/asyncfilter","summary":"Asynchronously filters out records in the pipe based on a given condition.","title":"AsyncFilter","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/asyncfilter/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/asyncforeach","summary":"For each record in the pipe, asynchronously runs some operations.","title":"AsyncForeach","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/asyncforeach/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/asyncmap","summary":"Asynchronously maps records one-to-one.","title":"AsyncMap","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/asyncmap/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/callnext","summary":"Calls the next execution that overrides the command or the original command itself. A more flexible version of callNextArray.","title":"CallNext","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/callnext/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/callnextarray","summary":"Calls the next execution that overrides the command or the original command itself.","title":"CallNextArray","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/callnextarray/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/collect","summary":"Collects all records to the origin shard.","title":"Collect","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/collect/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/config-get","summary":"Gets the value of a RedisGears configuration setting.","title":"ConfigGet","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/config-get/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/count","summary":"Counts the number of records in the pipe.","title":"Count","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/count/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/creategearsbuilder","summary":"Creates a new GearsBuilder object.","title":"CreateGearsBuilder","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/creategearsbuilder/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/execute","summary":"Runs a Redis command. A more flexible version of executeArray.","title":"Execute","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/execute/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/executearray","summary":"Runs a Redis command.","title":"ExecuteArray","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/executearray/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/filter","summary":"Filters out records in the pipe based on a given condition.","title":"Filter","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/filter/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/flatmap","summary":"Maps a single input record to one or more output records.","title":"FlatMap","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/flatmap/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/foreach","summary":"For each record in the pipe, runs some operations.","title":"Foreach","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/foreach/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/hashtag","summary":"Returns a string that maps to the current shard.","title":"Hashtag","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/hashtag/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/localaccumulateby","summary":"Groups records and reduces each group to a single record per group locally on each shard.","title":"LocalAccumulateBy","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/localaccumulateby/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/log","summary":"Writes a log message to the Redis log file.","title":"Log","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/log/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/map","summary":"Maps records one-to-one.","title":"Map","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/map/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/register","summary":"Registers the pipeline of functions to run when certain events occur.","title":"Register","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/register/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/repartition","summary":"Moves records between shards according to the extracted data.","title":"Repartition","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/repartition/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/run","summary":"Runs the pipeline of functions immediately.","title":"Run","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/run/"}],"page_type":"content","content_hash":"773c8681656f49bb126c672e1aae327e02b87a8207f624cacb204040b335bdf5","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `GearsBuilder` class allows you to create a pipeline of RedisGears functions that transform data.\n\nIt requires a reader to supply data to the pipe.\n\nTo create a `GearsBuilder` object, follow this example code:\n\n[code example]"},{"id":"functions","title":"Functions","role":"content","text":"| Function | Description |\n| --- | --- |\n| [accumulate](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/accumulate/) | Reduces many records in the pipe to a single record. |\n| [accumulateBy](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/accumulateby/) | Groups records and reduces each group to a single record per group. |\n| [asyncFilter](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/asyncfilter/) | Asynchronously filters out records in the pipe based on a given condition. |\n| [asyncForeach](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/asyncforeach/) | For each record in the pipe, asynchronously runs some operations. |\n| [asyncMap](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/asyncmap/) | Asynchronously maps records one-to-one. |\n| [callNext](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/callnext/) | Calls the next execution that overrides the command or the original command itself. A more flexible version of callNextArray. |\n| [callNextArray](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/callnextarray/) | Calls the next execution that overrides the command or the original command itself. |\n| [collect](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/collect/) | Collects all records to the origin shard. |\n| [configGet](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/config-get/) | Gets the value of a RedisGears configuration setting. |\n| [count](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/count/) | Counts the number of records in the pipe. |\n| [CreateGearsBuilder](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/creategearsbuilder/) | Creates a new GearsBuilder object. |\n| [execute](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/execute/) | Runs a Redis command. A more flexible version of executeArray. |\n| [executeArray](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/executearray/) | Runs a Redis command. |\n| [filter](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/filter/) | Filters out records in the pipe based on a given condition. |\n| [flatMap](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/flatmap/) | Maps a single input record to one or more output records. |\n| [foreach](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/foreach/) | For each record in the pipe, runs some operations. |\n| [hashtag](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/hashtag/) | Returns a string that maps to the current shard. |\n| [localAccumulateBy](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/localaccumulateby/) | Groups records and reduces each group to a single record per group locally on each shard. |\n| [log](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/log/) | Writes a log message to the Redis log file. |\n| [map](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/map/) | Maps records one-to-one. |\n| [register](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/register/) | Registers the pipeline of functions to run when certain events occur. |\n| [repartition](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/repartition/) | Moves records between shards according to the extracted data. |\n| [run](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/run/) | Runs the pipeline of functions immediately. |"}],"examples":[{"id":"overview-ex0","language":"java","code":"BaseReader reader = ...; // Initialize the reader\nbuilder = GearsBuilder.CreateGearsBuilder(reader);","section_id":"overview"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/localaccumulateby","title":"LocalAccumulateBy","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/localaccumulateby/","summary":"Groups records and reduces each group to a single record per group locally on each shard.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/classes/gearsbuilder/localaccumulateby/"],"tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:38+01:00","page_type":"content","content_hash":"f9c50059846abc24dbc0f2f388db3c2b751a8ffed89a8b416b1f7336a027efa4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[code example]\n\nThe `localAccumulateBy` function is similar to [`accumulateBy`](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/accumulateby), except it performs the operation locally on each shard without moving data between shards.\n\nOn each shard, it iterates through the records in the pipe, groups them based on the provided extractor, and then reduces each group to a single record per group with the accumulator function.\n\nThe initial value of the accumulator is null."},{"id":"parameters","title":"Parameters","role":"parameters","text":"Type parameters:\n\n| Name | Description |\n|------|-------------|\n| I | The template type of the returned builder |\n\nFunction parameters:\n\n| Name | Type | Description |\n|------|------|-------------|\n| accumulator | <nobr>AccumulateByOperation<T,​I></nobr> | A function with logic to update the accumulator value with each record |\n| extractor | ExtractorOperation<T> | Extracts a specific value from each record |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns a GearsBuilder object with a new template type."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"java","code":"public <I extends java.io.Serializable> GearsBuilder<I> localAccumulateBy​(\n\tgears.operations.ExtractorOperation<T> extractor, \n\tgears.operations.AccumulateByOperation<T,​I> accumulator)","section_id":"overview"},{"id":"example-ex0","language":"java","code":"GearsBuilder.CreateGearsBuilder(reader).\n   \tlocalAccumulateBy(r->{\n   \t\treturn r.getStringVal();\n   \t},(k, a, r)->{\n   \t\tInteger ret = null;\n   \t\tif(a == null) {\n   \t\t\tret = 0;\n   \t\t}else {\n   \t\t\tret = (Integer)a;\n   \t\t}\n   \t\treturn ret + 1;\n});","section_id":"example"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/log","title":"Log","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/log/","summary":"Writes a log message to the Redis log file.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/classes/gearsbuilder/log/"],"tags":["docs","operate","stack"],"last_updated":"2026-04-21T10:45:15+01:00","page_type":"content","content_hash":"2ca5a9fb93ab89a11fd1401151c3ea6d98d555b17a21c75aad0b54d75784602b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[code example]\n\nWrites a log message to the Redis log file. If you do not specify a `LogLevel`, it will default to `NOTICE`."},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Name | Type | Description |\n|------|------|-------------|\n| msg | string | The message to write to the log |\n| level | LogLevel | The log level (DEBUG, NOTICE, VERBOSE, WARNING) |"},{"id":"returns","title":"Returns","role":"returns","text":"None"},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"java","code":"public static void log​(java.lang.String msg)\n\npublic static void log​(java.lang.String msg, LogLevel level)","section_id":"overview"},{"id":"example-ex0","language":"java","code":"GearsBuilder.log(\n    \"Setting keys to expire after 1 month\", \n    LogLevel.WARNING\n);","section_id":"example"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/map","title":"Map","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/map/","summary":"Maps records one-to-one.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/classes/gearsbuilder/map/"],"tags":["docs","operate","stack"],"last_updated":"2026-04-21T10:45:15+01:00","page_type":"content","content_hash":"b434062f1f4a03a6baba121f1917826b10152d3ec008bca9b8acfb9d3e2c4a17","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[code example]\n\nMaps each input record in the pipe to an output record, one-to-one."},{"id":"parameters","title":"Parameters","role":"parameters","text":"Type parameters:\n\n| Name | Description |\n|------|-------------|\n| I | The template type of the returned builder |\n\nFunction parameters:\n\n| Name | Type | Description |\n|------|------|-------------|\n| mapper | <nobr>MapOperation<T,​I></nobr> | For each input record, returns a new output record |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns a GearsBuilder object with a new template type."},{"id":"example","title":"Example","role":"example","text":"Map each record to its string value:\n\n[code example]"}],"examples":[{"id":"overview-ex0","language":"java","code":"public <I extends java.io.Serializable> GearsBuilder<I> map​(\n\tgears.operations.MapOperation<T,​I> mapper)","section_id":"overview"},{"id":"example-ex0","language":"java","code":"GearsBuilder.CreateGearsBuilder(reader).\n \t\tmap(r->{\n    \t\treturn r.getStringVal();\n});","section_id":"example"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/register","title":"Register","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/register/","summary":"Registers the pipeline of functions to run when certain events occur.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/classes/gearsbuilder/register/"],"tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:38+01:00","page_type":"content","content_hash":"48123aeca6282d503d15a7930ce30c36087b9505c62479312ca120120a674064","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[code example]\n\nRegisters the pipeline of functions to run when certain [events](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/register-events) occur. The registered functions will run each time the event occurs.\n\nExecution modes:\n\n| Name | Description |\n|------|-------------|\n| ASYNC | Runs asynchronously on all of the shards. |\n| ASYNC_LOCAL | Runs asynchronously but only on the current shard that generated the event. |\n| SYNC | Runs synchronously only on the same shard that generated the event. |\n\n> [!NOTE]\n> If you call `register()` without specifying an execution mode, it will default to `ASYNC`."},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Name | Type | Description |\n|------|------|-------------|\n| mode | ExecutionMode | The execution mode to use (ASYNC/ASYNC_LOCAL/SYNC) |\n| onRegister | OnRegisteredOperation | Register callback that will be called on each shard upon register |\n| onUnregistered | OnUnregisteredOperation | Unregister callback that will be called on each shard upon unregister |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns a registration ID."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"java","code":"public java.lang.String register()\n\npublic java.lang.String register​(ExecutionMode mode)\n\npublic java.lang.String register​(\n    ExecutionMode mode, \n    gears.operations.OnRegisteredOperation onRegister, \n    gears.operations.OnUnregisteredOperation onUnregistered)","section_id":"overview"},{"id":"example-ex0","language":"java","code":"GearsBuilder.CreateGearsBuilder(reader).register();","section_id":"example"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/repartition","title":"Repartition","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/repartition/","summary":"Moves records between shards according to the extracted data.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/classes/gearsbuilder/repartition/"],"tags":["docs","operate","stack"],"last_updated":"2026-04-21T10:45:15+01:00","page_type":"content","content_hash":"5ca7cc005f4096b97fdf12938ee7b14c16338efa95f035436605fd5fa15742d4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[code example]\n\nMoves records between the shards. The extracted data determines the new shard location for each record."},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Name | Type | Description |\n|------|------|-------------|\n| extractor | <nobr>ExtractorOperation<T></nobr> | Extracts a specific value from each record |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns a GearsBuilder object with a new template type."},{"id":"example","title":"Example","role":"example","text":"Repartition by value:\n\n[code example]"}],"examples":[{"id":"overview-ex0","language":"java","code":"public GearsBuilder<T> repartition​(\n\tgears.operations.ExtractorOperation<T> extractor)","section_id":"overview"},{"id":"example-ex0","language":"java","code":"GearsBuilder.CreateGearsBuilder(reader).\n   \trepartition(r->{\n   \t\treturn r.getStringVal();\n});","section_id":"example"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/run","title":"Run","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/run/","summary":"Runs the pipeline of functions immediately.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/classes/gearsbuilder/run/"],"tags":["docs","operate","stack"],"last_updated":"2026-04-21T10:45:15+01:00","page_type":"content","content_hash":"791c741003eeef9a0704e8640ef36aee5f03804e222e8bb56fe4eaaebb141781","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[code example]\n\nRuns the pipeline of functions immediately upon execution. It will only run once."},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Name | Type | Description |\n|------|------|-------------|\n| collect | boolean | Whether or not to collect the results from the entire cluster before returning them |\n| jsonSerialize | boolean | Whether or not to serialize the results to JSON before returning them |"},{"id":"returns","title":"Returns","role":"returns","text":"None"},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"java","code":"public void run()\n\npublic void run​(boolean jsonSerialize, boolean collect)","section_id":"overview"},{"id":"example-ex0","language":"java","code":"GearsBuilder.CreateGearsBuilder(reader).run();","section_id":"example"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsfuture","title":"GearsFuture","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsfuture/","summary":"Allows asynchronous processing of records.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/classes/gearsfuture/"],"tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:38+01:00","children":[{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsfuture/seterror","summary":"Sets an error message.","title":"SetError","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsfuture/seterror/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsfuture/setresult","summary":"Sets a computation to run asynchronously.","title":"SetResult","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsfuture/setresult/"}],"page_type":"content","content_hash":"f944035c3a84a8aad8e0e17b978f376063e3d09e6bdebdd058f4c02c1c69ec7e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `GearsFuture` class allows asynchronous processing of records in another thread.\n\nYou can use a `GearsFuture` object with the following [`GearsBuilder`](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder) functions:\n\n- [`asyncFilter`](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/asyncfilter)\n- [`asyncForeach`](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/asyncforeach)\n- [`asyncMap`](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/asyncmap)"},{"id":"functions","title":"Functions","role":"content","text":"| Function | Description |\n| --- | --- |\n| [setError](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsfuture/seterror/) | Sets an error message. |\n| [setResult](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsfuture/setresult/) | Sets a computation to run asynchronously. |"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsfuture/seterror","title":"SetError","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsfuture/seterror/","summary":"Sets an error message.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/classes/gearsfuture/seterror/"],"tags":["docs","operate","stack"],"last_updated":"2026-04-21T10:45:15+01:00","page_type":"content","content_hash":"ab11e090f841b239778abbd0e57271314403051ed8aa0a3d8f056b194bff173e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[code example]\n\nSets an error message for an asynchronous computation."},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Name | Type | Description |\n|------|------|-------------|\n| error | string | An error message |"},{"id":"returns","title":"Returns","role":"returns","text":"None"},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"java","code":"public void setError​(java.lang.String error) \n\tthrows java.lang.Exception","section_id":"overview"},{"id":"example-ex0","language":"java","code":"GearsFuture<Boolean> f = new GearsFuture<Boolean>();\ntry {\n\tf.setError(\"An error has occurred during asyncForeach\");\n} catch (Exception e) {\n\te.printStackTrace();\n}","section_id":"example"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsfuture/setresult","title":"SetResult","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsfuture/setresult/","summary":"Sets a computation to run asynchronously.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/classes/gearsfuture/setresult/"],"tags":["docs","operate","stack"],"last_updated":"2026-04-21T10:45:15+01:00","page_type":"content","content_hash":"0b33d1f063cef66d018ab94963c30381aa3d96e021055b73a113f4a251cb6d65","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[code example]\n\nSets a computation to run asynchronously."},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Name | Type | Description |\n|------|------|-------------|\n| result | template type I | The result of a computation |"},{"id":"returns","title":"Returns","role":"returns","text":"None"},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"java","code":"public void setResult​(I result) \n\tthrows java.lang.Exception","section_id":"overview"},{"id":"example-ex0","language":"java","code":"GearsBuilder.CreateGearsBuilder(reader).map(r->r.getKey()).\n\tasyncFilter(r->{\n\t\tGearsFuture<Boolean> f = new GearsFuture<Boolean>();\n\t\ttry {\n\t\t\tf.setResult(r.equals(\"x\"));\t\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\t\t\t\n\t\treturn f;\n});","section_id":"example"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes","title":"RedisGears Java classes and functions","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/","summary":"","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/classes/"],"tags":["docs","operate","stack"],"last_updated":"2026-04-21T10:45:15+01:00","children":[{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder","summary":"Creates a RedisGears pipeline of operations to transform data.","title":"GearsBuilder","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsfuture","summary":"Allows asynchronous processing of records.","title":"GearsFuture","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsfuture/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers","summary":"Extracts data from the database and creates records to pass through a RedisGears pipeline.","title":"RedisGears readers","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/"}],"page_type":"content","content_hash":"7e3647e33153b085c3846e0ce5a3e58829cf3f0e77e84967c2c07bfb47ba3e11","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The RedisGears JVM plugin provides a set of classes and functions for you to use in your Java code."},{"id":"classes","title":"Classes","role":"content","text":"| Class | Description |\n| --- | --- |\n| [GearsBuilder](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/) | Creates a RedisGears pipeline of operations to transform data. |\n| [GearsFuture](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsfuture/) | Allows asynchronous processing of records. |\n| [Readers](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/) | Extracts data from the database and creates records to pass through a RedisGears pipeline. |"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/commandoverrider","title":"CommandOverrider","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/commandoverrider/","summary":"Override a Redis command.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/classes/readers/commandoverrider/"],"tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:38+01:00","page_type":"content","content_hash":"19b46c0c23870324dfcafbed0e23d0c84d3d943333cce0726e5c0cf7f2f31f8b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `CommandOverrider` allows you to override and customize Redis commands.\n\n1. Pass the `CommandOverrider` to the [`GearsBuilder.CreateGearsBuilder()`](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/creategearsbuilder) function in your Java code.\n1. Call the [`register()`](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/register) function.\n1. Run `RG.JEXECUTE` to register your code.\n\n> [!NOTE]\n> If you register code that uses `CommandOverrider`, its `reader` value is `\"CommandReader\"` when you run the `RG.DUMPREGISTRATIONS` command, not `\"CommandOverrider\"`."},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Name | Type | Description |\n|------|------|-------------|\n| command | string | The command to override |\n| prefix | string | Only override the command for keys that start with this string |"},{"id":"output-records","title":"Output records","role":"returns","text":"Outputs a record with the command's name and arguments."},{"id":"example","title":"Example","role":"example","text":"The following example shows how to override the `HSET` command so that it also adds a `last_modified` timestamp for \"user:\" hashes.\n\nAdd some user data as a hash:\n\n[code example]\n\nExample code:\n\n[code example]\n\nAfter you register the previous code with the `RG.JEXECUTE` command, try to update the user's data with `HSET` to test it:\n\n[code example]\n\nNow the user's profile should have the updated value for `posts` and a `last_modified` timestamp:\n\n[code example]"}],"examples":[{"id":"example-ex0","language":"sh","code":"redis> HSET user:1 name \"morgan\" posts 201\n(integer) 2","section_id":"example"},{"id":"example-ex1","language":"java","code":"// Create the reader that will pass data to the pipe\nCommandOverrider overrider = new CommandOverrider();\n// Override the HSET command\noverrider.setCommand(\"HSET\");\n// Only override HSET for keys that start with \"user:\"\noverrider.setPrefix(\"user:\");\n\n// Create the data pipe builder\nGearsBuilder.CreateGearsBuilder(overrider).map(r-> {\n    // Get the operation arguments\n    ArrayList<String> operationArguments = new ArrayList<String>();\n    for (int i = 1; i < r.length; i++) {\n        operationArguments.add(new String((byte[]) r[i], StandardCharsets.UTF_8));\n    }\n\n    // Add a last-modified field and a corresponding timestamp to the operation arguments\n    operationArguments.add(\"last-modified\");\n    operationArguments.add(Long.toString(System.currentTimeMillis()));\n\n    // Run the enriched HSET operation\n    Object reply = GearsBuilder.callNext(operationArguments.toArray(new String[0]));\n    \n    return reply.toString();\n}).register(ExecutionMode.SYNC);","section_id":"example"},{"id":"example-ex2","language":"sh","code":"redis> HSET user:1 posts 234\n\"OK\"","section_id":"example"},{"id":"example-ex3","language":"sh","code":"redis> HGETALL user:1\n1) \"name\"\n2) \"morgan\"\n3) \"posts\"\n4) \"234\"\n5) \"last_modified\"\n6) \"1643237927663\"","section_id":"example"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/commandreader","title":"CommandReader","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/commandreader/","summary":"Run RedisGears functions on command.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/classes/readers/commandreader/"],"tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:38+01:00","page_type":"content","content_hash":"02f0c1a8f72d451f32b3ce29a16a1c21dc7227dd294104bf31312d284d515c8f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `CommandReader` allows you to run RedisGears functions on command when you:\n\n1. Pass the `CommandReader` to the [`GearsBuilder.CreateGearsBuilder()`](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/creategearsbuilder) function in your Java code.\n1. Call the [`register()`](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/register) function.\n1. Run `RG.JEXECUTE` to register your code.\n1. Use `RG.TRIGGER` to run your code on command:\n\n    [code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Name | Type | Description |\n|------|------|-------------|\n| trigger | string | The command name that triggers the registered RedisGears functions to run |"},{"id":"output-records","title":"Output records","role":"returns","text":"Outputs a record with the command trigger's name and arguments."},{"id":"example","title":"Example","role":"example","text":"The following example shows how to create a custom command to update an item's stock. It also adds a timestamp to track when the last restock occurred.\n\nAdd a hash to the database that represents an inventory item:\n\n[code example]\n\nExample code:\n\n[code example]\n\nAfter you register the previous code with the `RG.JEXECUTE` command, run `RG.TRIGGER` to test it:\n\n[code example]\n\nThe item now has the updated value for `stock` and a `last_restocked` timestamp:\n\n[code example]"}],"examples":[{"id":"overview-ex0","language":"sh","code":"RG.TRIGGER <Trigger name> [arg1 arg2 ...]","section_id":"overview"},{"id":"example-ex0","language":"sh","code":"redis> HSET inventory:headphones:1 color \"blue\" stock 5 price 30.00\n(integer) 3","section_id":"example"},{"id":"example-ex1","language":"java","code":"// Create the reader that will pass data to the pipe\nCommandReader reader = new CommandReader();\n// Set the name of the custom command\nreader.setTrigger(\"Restock\");\n        \n// Create the data pipe builder\nGearsBuilder.CreateGearsBuilder(reader).map(r-> {\n    // Parse the command arguments to get the key name and new stock value\n    String itemKey = new String((byte[]) r[1], StandardCharsets.UTF_8);\n    String newStock = new String((byte[]) r[2], StandardCharsets.UTF_8);\n        \t\n    // Update the item's stock and add a timestamp\n    GearsBuilder.execute(\"HSET\", itemKey , \"stock\", newStock,\n        \t\t\t\"last_restocked\", Long.toString(System.currentTimeMillis()));\n        \t\n    return \"OK restocked \" + itemKey;\n}).register();","section_id":"example"},{"id":"example-ex2","language":"sh","code":"redis> RG.TRIGGER Restock inventory:headphones:1 20\n1) \"OK restocked inventory:headphones:1\"","section_id":"example"},{"id":"example-ex3","language":"sh","code":"redis> HGETALL inventory:headphones:1\n1) \"color\"\n2) \"blue\"\n3) \"stock\"\n4) \"20\"\n5) \"price\"\n6) \"30.00\"\n7) \"last_restocked\"\n8) \"1643232394078\"","section_id":"example"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers","title":"RedisGears readers","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/","summary":"Extracts data from the database and creates records to pass through a RedisGears pipeline.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/classes/readers/"],"tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:38+01:00","children":[{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/commandoverrider","summary":"Override a Redis command.","title":"CommandOverrider","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/commandoverrider/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/commandreader","summary":"Run RedisGears functions on command.","title":"CommandReader","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/commandreader/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/javareader","summary":"A template for creating custom readers.","title":"JavaReader","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/javareader/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/keysonlyreader","summary":"Gets key names from a database.","title":"KeysOnlyReader","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/keysonlyreader/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/keysreader","summary":"Gets keys and their values from a database.","title":"KeysReader","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/keysreader/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/shardsidreader","summary":"Gets the shard ID for each shard in a database.","title":"ShardsIDReader","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/shardsidreader/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/streamreader","summary":"Reads Redis stream data.","title":"StreamReader","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/streamreader/"}],"page_type":"content","content_hash":"73c522e5648a3f44fa066b9eeb84f882f8a0b3ffbc8e28bfaec12463f95be935","sections":[{"id":"overview","title":"Overview","role":"overview","text":"A reader extracts data from the database and creates records.\n\nThe [`GearsBuilder.CreateGearsBuilder(reader)`](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/creategearsbuilder) function takes a reader as a parameter and passes the generated records through a pipeline of RedisGears functions."},{"id":"classes","title":"Classes","role":"content","text":"| Class | Description |\n| --- | --- |\n| [CommandOverrider](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/commandoverrider/) | Override a Redis command. |\n| [CommandReader](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/commandreader/) | Run RedisGears functions on command. |\n| [JavaReader](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/javareader/) | A template for creating custom readers. |\n| [KeysOnlyReader](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/keysonlyreader/) | Gets key names from a database. |\n| [KeysReader](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/keysreader/) | Gets keys and their values from a database. |\n| [ShardsIDReader](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/shardsidreader/) | Gets the shard ID for each shard in a database. |\n| [StreamReader](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/streamreader/) | Reads Redis stream data. |"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/javareader","title":"JavaReader","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/javareader/","summary":"A template for creating custom readers.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/classes/readers/javareader/"],"tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:38+01:00","page_type":"content","content_hash":"7339257d06d346b73aeee10507ef36f575b003462a85b4e488acdcb9d28052e8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `JavaReader` is an abstract class that allows you to create a custom reader in Java."},{"id":"create-a-custom-reader","title":"Create a custom reader","role":"content","text":"To create a custom reader:\n\n- Extend the `JavaReader` class\n- Override the `iterator()` function"},{"id":"custom-reader-example","title":"Custom reader example","role":"content","text":"The implementation of the [`KeysOnlyReader`](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/keysonlyreader) class shows how to create a custom reader with `JavaReader`:\n\n[code example]"}],"examples":[{"id":"custom-reader-example-ex0","language":"java","code":"import java.util.Iterator;\n\nimport gears.GearsBuilder;\n\n/**\n * A reader that only reads key names from the key space\n *\n */\npublic class KeysOnlyReader extends JavaReader<String> {\n\n\t/**\n\t * \n\t */\n\tprivate static final long serialVersionUID = 1L;\n\t\n\tprivate String scanSize;\n\tprivate String pattern;\n\t\n\t/**\n\t * Create a new KeysOnlyReader reader\n\t * @param scanSize - the size to use with the scan command\n\t * @param pattern - the pattern of the keys to read\n\t */\n\tpublic KeysOnlyReader(int scanSize, String pattern) {\n\t\tthis.scanSize = Integer.toString(scanSize);\n\t\tthis.pattern = pattern;\n\t}\n\t\n\t/**\n\t * Create a new KeysOnlyReader reader with default pattern (*) and default\n\t * scan size (10000)\n\t */\n\tpublic KeysOnlyReader() {\n\t\tthis(10000, \"*\");\n\t}\n\n\t@Override\n\tpublic Iterator<String> iterator() {\n\t\treturn new Iterator<String>() {\n\n\t\t\tString cursor = \"0\";\n\t\t\tint currIndex = 0;\n\t\t\tObject[] keys = null;\n\t\t\tboolean isDone = false;\n\t\t\tString nextKey = null;\n\t\t\t\n\t\t\tprivate String innerNext() {\n\t\t\t\twhile(!isDone) {\n\t\t\t\t\tif(keys == null) {\n\t\t\t\t\t\tObject[] res = (Object[]) GearsBuilder.execute(\"scan\", \n\t\t\t\t\t\t\t\t\t\tcursor == null ? \"0\" : cursor,\n\t\t\t\t\t\t\t\t\t\t\"MATCH\", pattern, \"COUNT\", scanSize);\n\t\t\t\t\t\tkeys = (Object[])res[1];\n\t\t\t\t\t\tcursor = (String)res[0];\n\t\t\t\t\t\tcurrIndex = 0;\n\t\t\t\t\t}\n\t\t\t\t\tif(currIndex < keys.length) {\n\t\t\t\t\t\treturn (String) keys[currIndex++];\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tkeys = null;\n\t\t\t\t\tif(cursor.charAt(0) == '0') {\n\t\t\t\t\t\tisDone = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\t\n\t\t\t@Override\n\t\t\tpublic boolean hasNext() {\n\t\t\t\tif(nextKey == null) {\n\t\t\t\t\tnextKey = innerNext();\n\t\t\t\t}\n\t\t\t\treturn !isDone;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic String next() {\n\t\t\t\tString temp = nextKey != null ? nextKey : innerNext();\n\t\t\t\tnextKey = innerNext();\n\t\t\t\treturn temp;\n\t\t\t}\n\t\t\t\n\t\t};\n\t}\n\n}","section_id":"custom-reader-example"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/keysonlyreader","title":"KeysOnlyReader","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/keysonlyreader/","summary":"Gets key names from a database.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/classes/readers/keysonlyreader/"],"tags":["docs","operate","stack"],"last_updated":"2026-04-21T10:45:15+01:00","page_type":"content","content_hash":"aa58d456c02174f4b321155cfda255368abedd23716e277b47d9f770c2c2104e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `KeysOnlyReader` only extracts the key names from a database."},{"id":"constructors","title":"Constructors","role":"content","text":"You can use one of these constructors to create a new `KeysOnlyReader` object:\n\n[code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Name | Type | Default value | Description |\n|------|------|---------------|-------------|\n| pattern | string | \"\\*\" (match all keys) | Get all keys that match this pattern |\n| scanSize | integer | 10000 | The scan command's size limit |"},{"id":"output-records","title":"Output records","role":"returns","text":"Each output record is a string that represents the key's name."},{"id":"examples","title":"Examples","role":"example","text":"Get all keys in the database:\n\n[code example]\n\nOnly get keys that start with \"user:\":\n\n[code example]"}],"examples":[{"id":"constructors-ex0","language":"java","code":"public KeysOnlyReader()\n\npublic KeysOnlyReader(int scanSize, String pattern)","section_id":"constructors"},{"id":"examples-ex0","language":"java","code":"KeysOnlyReader reader = new KeysOnlyReader();","section_id":"examples"},{"id":"examples-ex1","language":"java","code":"KeysOnlyReader reader = new KeysOnlyReader(1000, \"user:*\");","section_id":"examples"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/keysreader","title":"KeysReader","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/keysreader/","summary":"Gets keys and their values from a database.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/classes/readers/keysreader/"],"tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:38+01:00","page_type":"content","content_hash":"2f24a130bdd42cdfa17f928cc1cf71a2388276b7b2e05ba18060800e85b50f69","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Creates records from the keys and values stored in a Redis database.\n\n> [!NOTE]\n>  Currently only supports string and hash data types. For other data types, it will only extract the key name."},{"id":"constructors","title":"Constructors","role":"content","text":"You can use one of these constructors to create a new `KeysReader` object:\n\n[code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Name | Type | Default value | Description |\n|------|------|---------------|-------------|\n| commands | array of strings | null | The commands that this reader is registered on |\n| eventTypes | array of strings | null | The [event types](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/register-events#event-types) to register on (usually the command name) |\n| keyTypes | array of strings | null | The key types to register on |\n| noScan | boolean | false | Whether or not to scan the key space or just read the pattern as is |\n| pattern/prefix | string | \"\\*\" (match all keys) | The reader will get all keys that match this pattern |\n| readValues | boolean | true | Whether or not to read the keys' values |"},{"id":"output-records","title":"Output records","role":"returns","text":"Creates a `KeysReaderRecord` for each matching key in the database.\n\n| Name | Type | Description |\n|------|------|-------------|\n| key | string | The name of the key |\n| type | long | The core Redis type: 'string', 'hash', 'list', 'set', 'zset', or 'stream' |\n| event | string | The event that triggered the execution (null if using the run function) |\n| stringVal | string | The key's value for string data types |\n| hashVal | Map<String,String> | The key's value for hash data types |\n| listVal | List<String> | The key's value for list data types |\n| setVal | Set<String> | The key's value for set data types |"},{"id":"examples","title":"Examples","role":"example","text":"Here's a basic example of a `KeysReader` that creates records for all keys in the database:\n\n[code example]\n\nIn the following example, the `KeysReader` creates records for all keys in the database that start with \"person:\". When registered, it only runs for hashes after `HSET` and `DEL` events occur.\n\n[code example]"}],"examples":[{"id":"constructors-ex0","language":"java","code":"public KeysReader()\n\npublic KeysReader(String pattern)\n\npublic KeysReader(String prefix, boolean readValues)\n\npublic KeysReader(String pattern, boolean noScan, boolean readValues)\n\npublic KeysReader(String prefix, \n                  boolean readValues, \n                  String[] eventTypes, \n                  String[] keyTypes)\n\npublic KeysReader(String pattern, \n                  boolean noScan, \n                  boolean readValues, \n                  String[] eventTypes, \n                  String[] keyTypes)","section_id":"constructors"},{"id":"examples-ex0","language":"java","code":"KeysReader reader = new KeysReader();","section_id":"examples"},{"id":"examples-ex1","language":"java","code":"String[] eventTypes = {\"HSET\", \"DEL\"};\nString[] keyTypes = {\"HASH\"};\nKeysReader reader = new KeysReader(\"person:*\", false, true, eventTypes, keyTypes);","section_id":"examples"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/shardsidreader","title":"ShardsIDReader","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/shardsidreader/","summary":"Gets the shard ID for each shard in a database.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/classes/readers/shardsidreader/"],"tags":["docs","operate","stack"],"last_updated":"2026-04-21T10:45:15+01:00","page_type":"content","content_hash":"501e30a16238da80be58f3cbc9e7ac11cc3f8981b39f33b949a7248de42d1634","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `ShardsIDReader` creates a single record on each shard that represents the current shard's ID.\n \nUse this reader when you want to run an operation on each shard in the database."},{"id":"parameters","title":"Parameters","role":"parameters","text":"None"},{"id":"output-records","title":"Output records","role":"returns","text":"Creates a record for each shard with the shard's cluster identifier."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"example-ex0","language":"java","code":"ShardsIDReader reader = new ShardsIDReader();","section_id":"example"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/streamreader","title":"StreamReader","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/streamreader/","summary":"Reads Redis stream data.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/classes/readers/streamreader/"],"tags":["docs","operate","stack"],"last_updated":"2026-04-21T10:45:15+01:00","page_type":"content","content_hash":"4635a8cd10aab0280177e0dc6f47120c70ad1b5862c0f7945c1267771bc31a58","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Reads Redis stream data."},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Name | Type | Default value | Description |\n|------|------|---------------|-------------|\n| batchSize | integer | 1 | The number of new messages that will cause the functions to run |\n| duration | integer | 0 | How many seconds to wait before execution, regardless of batch size |\n| failurePolicy | FailurePolicy | FailurePolicy.CONTINUE | How to handle execution failure (CONTINUE/ABORT/RETRY) |\n| failureRetryInterval | integer | 5000 | The number of seconds to wait before retrying |\n| pattern | string | \"\\*\" (match all keys) | The pattern of keys that store streams |\n| startId | string | \"0-0\" | Start reading from this stream ID |\n| trimStream | boolean | true | Whether or not to trim the stream |"},{"id":"output-records","title":"Output records","role":"returns","text":"Creates a record for each message in the input stream.\n\nEach record is a `HashMap<String, Object>` with the following fields:\n\n| Name | Type | Description |\n|------|------|-------------|\n| id | string | The message's ID |\n| key | string | The stream key name |\n| value | HashMap<String, byte[]> | The message's data |"},{"id":"examples","title":"Examples","role":"example","text":"The following example creates a `StreamReader` with default values:\n\n[code example]\n\nTo change the parameter values for a `StreamReader`, use their setter methods:\n\n[code example]"}],"examples":[{"id":"examples-ex0","language":"java","code":"StreamReader reader = new StreamReader();","section_id":"examples"},{"id":"examples-ex1","language":"java","code":"StreamReader reader = new StreamReader();\n// Get streams for keys that match \"weather\"\nreader.setPattern(\"weather\");\n// Run RedisGears functions after every 10 messages\nreader.setBatchSize(10);","section_id":"examples"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/commands","title":"RedisGears JVM commands","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/commands/","summary":"","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/commands/"],"tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:38+01:00","children":[{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/commands/rg-jdumpsessions","summary":"Outputs information about existing Java sessions.","title":"RG.JDUMPSESSIONS","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/commands/rg-jdumpsessions/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/commands/rg-jexecute","summary":"Executes a Java function.","title":"RG.JEXECUTE","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/commands/rg-jexecute/"}],"page_type":"content","content_hash":"902edca2a3f6c792dfef43998f9953047268d0515f25a6417c7c88cf679d319f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Use a Redis client like `redis-cli` to send commands to the RedisGears JVM plugin."},{"id":"jvm-plugin-commands","title":"JVM plugin commands","role":"content","text":"| Command | Description |\n| --- | --- |\n| [RG.JDUMPSESSIONS](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/commands/rg-jdumpsessions/) | Outputs information about existing Java sessions. |\n| [RG.JEXECUTE](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/commands/rg-jexecute/) | Executes a Java function. |\n\n\n> [!NOTE]\n> Ignore any commands that start with `RG.PY` while using the JVM plugin. The `RG.PY` commands are for the Python plugin."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/commands/rg-jdumpsessions","title":"RG.JDUMPSESSIONS","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/commands/rg-jdumpsessions/","summary":"Outputs information about existing Java sessions.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/commands/rg-jdumpsessions/"],"tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:38+01:00","page_type":"content","content_hash":"8f1ba1fbdd7eac98d85ba6f9c095e4b4f1e8c8970d2cffbe3fd4d66a66968147","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[code example]\n\nOutputs information about existing Java sessions.\n\n> [!NOTE]\n> When you run the `RG.JEXECUTE` command, it creates a Java session."},{"id":"arguments","title":"Arguments","role":"parameters","text":"| Name | Description |\n|------|-------------|\n| VERBOSE | Output more details about registrations. |\n| SESSIONS | Only output information about sessions that appears in the given list. Can only be the last argument. |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns information about existing Java sessions."},{"id":"examples","title":"Examples","role":"example","text":"Get information for all sessions:\n\n[code example]\n\nGet more detailed information about a specific session:\n\n[code example]"}],"examples":[{"id":"overview-ex0","language":"sh","code":"RG.JDUMPSESSIONS [VERBOSE] [SESSIONS s1 s2 ...]","section_id":"overview"},{"id":"examples-ex0","language":"sh","code":"> redis-cli RG.JDUMPSESSIONS\n1)  1) \"mainClass\"\n    2) \"gears_experiments.test\"\n    3) \"version\"\n    4) (integer) 1\n    5) \"description\"\n    6) \"foo\"\n    7) \"upgradeData\"\n    8) (nil)\n    9) \"jar\"\n   10) \"/home/user/work/RedisGears/plugins/jvmplugin/-jars/6876b8b78ccfc2ad764edc7ede590f573bd7260b.jar\"\n   11) \"refCount\"\n   12) (integer) 2\n   13) \"linked\"\n   14) \"true\"\n   15) \"ts\"\n   16) \"false\"\n   17) \"registrations\"\n   18) 1) \"0000000000000000000000000000000000000000-1\"","section_id":"examples"},{"id":"examples-ex1","language":"sh","code":"> redis-cli RG.JDUMPSESSIONS VERBOSE SESSIONS gears_experiments.test\n1)  1) \"mainClass\"\n    2) \"gears_experiments.test\"\n    3) \"version\"\n    4) (integer) 1\n    5) \"description\"\n    6) \"foo\"\n    7) \"upgradeData\"\n    8) (nil)\n    9) \"jar\"\n   10) \"/home/user/work/RedisGears/plugins/jvmplugin/-jars/6876b8b78ccfc2ad764edc7ede590f573bd7260b.jar\"\n   11) \"refCount\"\n   12) (integer) 2\n   13) \"linked\"\n   14) \"true\"\n   15) \"ts\"\n   16) \"false\"\n   17) \"registrations\"\n   18) 1)  1) \"id\"\n           2) \"0000000000000000000000000000000000000000-1\"\n           3) \"reader\"\n           4) \"CommandReader\"\n           5) \"desc\"\n           6) (nil)\n           7) \"RegistrationData\"\n           8)  1) \"mode\"\n               2) \"async\"\n               3) \"numTriggered\"\n               4) (integer) 0\n               5) \"numSuccess\"\n               6) (integer) 0\n               7) \"numFailures\"\n               8) (integer) 0\n               9) \"numAborted\"\n              10) (integer) 0\n              11) \"lastRunDurationMS\"\n              12) (integer) 0\n              13) \"totalRunDurationMS\"\n              14) (integer) 0\n              15) \"avgRunDurationMS\"\n              16) \"-nan\"\n              17) \"lastError\"\n              18) (nil)\n              19) \"args\"\n              20) 1) \"trigger\"\n                  2) \"test\"\n                  3) \"inorder\"\n                  4) (integer) 0\n           9) \"ExecutionThreadPool\"\n          10) \"JVMPool\"","section_id":"examples"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/commands/rg-jexecute","title":"RG.JEXECUTE","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/commands/rg-jexecute/","summary":"Executes a Java function.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/commands/rg-jexecute/"],"tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:38+01:00","page_type":"content","content_hash":"0c50bbacaa2dcdfce012a942f46e2d159b56ba62a74adb2d0d38ab43a4538d0b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[code example]\n\nExecutes a Java function.\n\nThe code runs immediately if it uses [`GearsBuilder.run()`](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/run). Code that uses [`GearsBuilder.register()`](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/register) will run later, every time certain events occur in the database."},{"id":"arguments","title":"Arguments","role":"parameters","text":"| Name | Description |\n|------|-------------|\n| path.to.main.class | The path to the main class in the JAR |\n| JAR file | A JAR file that contains the RedisGears code to run or register |\n| UPGRADE | Upgrades registered code to a new version |"},{"id":"returns","title":"Returns","role":"returns","text":"If the executed code calls [`GearsBuilder.run()`](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/run), it returns the output of the executed code.\n\nFor registered code, it returns the string \"`OK`\" instead."},{"id":"examples","title":"Examples","role":"example","text":"The executed code in this example [runs](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/run) immediately:\n\n[code example]\n\nThis example [registers](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/register) the RedisGears code to run every time certain database events occur:\n\n[code example]\n\nHere's an example of how to upgrade registered code to a new version:\n\n[code example]"}],"examples":[{"id":"overview-ex0","language":"sh","code":"RG.JEXECUTE <path.to.main.class> [UPGRADE] <JAR file>","section_id":"overview"},{"id":"examples-ex0","language":"sh","code":"$ redis-cli -x RG.JEXECUTE com.domain.packagename.Reviews < /tmp/rgjvmtest-0.0.1-SNAPSHOT.jar\n1) 1) \"3.6666666666666665\"\n2) (empty array)","section_id":"examples"},{"id":"examples-ex1","language":"sh","code":"$ redis-cli -x RG.JEXECUTE com.domain.packagename.App < /tmp/rgjvmtest-0.0.1-SNAPSHOT.jar\nOK","section_id":"examples"},{"id":"examples-ex2","language":"sh","code":"$ redis-cli -x RG.JEXECUTE com.domain.packagename.App UPGRADE < /tmp/rgjvmtest-0.0.2-SNAPSHOT.jar\nOK","section_id":"examples"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm","title":"RedisGears JVM plugin","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/","summary":"The RedisGears JVM plugin allows you to run RedisGears functions in the Java virtual machine.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/"],"tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:38+01:00","children":[{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/quickstart","summary":"A quick start to learn how to use RedisGears with Java.","title":"RedisGears JVM quick start","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/quickstart/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/install","summary":"","title":"Install RedisGears and the JVM plugin","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/install/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/commands","summary":"","title":"RedisGears JVM commands","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/commands/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes","summary":"","title":"RedisGears Java classes and functions","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/recipes","summary":"","title":"Java-based RedisGears recipes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/recipes/"}],"page_type":"content","content_hash":"c3777d968fa759e99ee713c59baf0ecab358479d78eb963bee55368e41541344","sections":[{"id":"overview","title":"Overview","role":"overview","text":"With the [RedisGears JVM plugin](https://github.com/RedisGears/JVMPlugin), you can write RedisGears functions in [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) and run them on a [Redis Software](https://redis.io/docs/latest/operate/rs) cluster. It currently supports [JVM](https://en.wikipedia.org/wiki/Java_virtual_machine) version 11.\n\nSimilar to the Python plugin, the JVM plugin allows both batch processing and event-driven processing.\n\nBefore you can run RedisGears with Java, you will need to [install the RedisGears module and the JVM plugin](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/installing-redisgears#install-redisgears) on your Redis Software cluster and [enable them for your database](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/install).\n\nOnce you have written your code, compile and package it into a [JAR](https://en.wikipedia.org/wiki/JAR_(file_format)) file and upload it to a node on your Redis Software cluster. Use the `RG.JEXECUTE` command with the `redis-cli` command-line tool to run your code."},{"id":"more-info","title":"More info","role":"content","text":"- [RedisGears JVM quick start](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/quickstart)\n- [RedisGears Java classes and functions](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes)\n- [RedisGears recipes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/recipes)"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/install","title":"Install RedisGears and the JVM plugin","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/install/","summary":"","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/install/"],"tags":["docs","operate","stack"],"last_updated":"2026-10-02T14:53:57+01:00","page_type":"content","content_hash":"252c86fa26d948d70b6d54100af75b2e87251eadafe7cd5c3ca3c6b3e236ebe3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Before you can use RedisGears with the JVM, you need to install the RedisGears module and JVM plugin on your Redis Software cluster and enable them for a database."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"1. Redis Software v6.0.12 or later\n\n1. [Created a Redis Software cluster](https://redis.io/docs/latest/operate/rs/clusters/new-cluster-setup)\n\n1. [Added nodes to the cluster](https://redis.io/docs/latest/operate/rs/clusters/add-node)\n\n1. [Installed RedisGears and the JVM plugin](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/installing-redisgears#install-redisgears)"},{"id":"enable-redisgears-for-a-database","title":"Enable RedisGears for a database","role":"content","text":"1. From the Redis Software admin console's **databases** page, select the **Add** button to create a new database:\n\n    ![The Add icon](https://redis.io/docs/latest/images/rs/icon_add.png)\n\n1. Confirm that you want to create a new Redis database with the **Next** button.\n\n1. On the **create database** page, give your database a name.\n\n1. For **Redis Modules**, select the **Add** button and choose RedisGears from the **Module** dropdown list.\n\n1. Select **Add Configuration**, enter `Plugin gears_jvm` in the box, then select the **OK** button:\n\n    ![The Save icon](https://redis.io/docs/latest/images/rs/icon_save.png)\n\n    > [!NOTE]\n    > You can configure additional JVM options in this box. For example:<br></br>\n    > `Plugin gears_jvm JvmOptions `<nobr>`'-Dproperty1=value1`</nobr> <nobr>`-Dproperty2=value2'`</nobr>\n    >     \n\n1. Select the **Activate** button."},{"id":"verify-the-install","title":"Verify the install","role":"content","text":"Run the `RG.JSTATS` command from a database shard to view statistics and verify that you set up RedisGears and the JVM plugin correctly:\n\n[code example]"}],"examples":[{"id":"verify-the-install-ex0","language":"sh","code":"$ shard-cli 3\n172.16.0.1:12345> RG.JSTATS","section_id":"verify-the-install"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/quickstart","title":"RedisGears JVM quick start","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/quickstart/","summary":"A quick start to learn how to use RedisGears with Java.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/quickstart/"],"tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:38+01:00","page_type":"content","content_hash":"f11ed80872bac39c8aa3abd52bb760b0e07b5b017f7def314f7f280e1029a1a5","sections":[{"id":"prerequisites","title":"Prerequisites","role":"overview","text":"For this quick start, you need:\n\n- A Redis Software cluster with the [RedisGears module and JVM plugin installed](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/installing-redisgears#install-redisgears) and [enabled on a database](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/install)\n- `redis-cli` with connectivity to a Redis database"},{"id":"tutorial","title":"Tutorial","role":"content","text":""},{"id":"create-a-maven-project","title":"Create a Maven project","role":"content","text":"1. Create a new [Maven project](https://maven.apache.org/guides/getting-started/index.html#).\n\n1. Add the following sections to the [pom.xml](https://maven.apache.org/guides/introduction/introduction-to-the-pom.html) file:\n\n    [code example]\n\n1. Add example code for either [batch processing](#batch-processing) or [event processing](#event-processing) to your project's `main` function."},{"id":"build-a-jar","title":"Build a JAR","role":"content","text":"Use the Maven command-line tool or an IDE plugin to compile and package your code into a JAR file:\n\n[code example]"},{"id":"upload-the-jar","title":"Upload the JAR","role":"content","text":"Upload your JAR file to a node in the Redis Software cluster. You will need to use the destination filepath when you run your code."},{"id":"run-redisgears-java-code","title":"Run RedisGears Java code","role":"content","text":"Use the `RG.JEXECUTE` command to run your code:\n\n[code example]\n\n> [!NOTE]\n> When you use [`GearsBuilder.run()`](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/run), `RG.JEXECUTE` runs your code immediately.\n> <br></br>\n> However, if you use [`GearsBuilder.register()`](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/register), `RG.JEXECUTE` only outputs an `OK` message if it registers successfully. Your registered code will run whenever certain database events occur."},{"id":"example-code","title":"Example code","role":"example","text":"You can use these code examples with your own Maven project to try out batch processing or event processing with the RedisGears JVM plugin."},{"id":"batch-processing","title":"Batch processing","role":"content","text":"If you use the [`GearsBuilder.run()`](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/run) function within your code, then the functions you add to the pipeline will run exactly once when you use `RG.JEXECUTE` with your JAR file.\n\nThe following example calculates the average rating of all restaurant reviews stored in your database.\n\n#### Add data to the database\n\n1. Connect to your database with `redis-cli`:\n\n    [code example]\n\n1. Add a few review hashes to the database with the `HSET` command:\n\n    [code example]\n\n#### Example code\n\n[code example]\n\n#### Example output\n\n[code example]"},{"id":"event-processing","title":"Event processing","role":"content","text":"If you use the [`GearsBuilder.register()`](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/register) function in your code, then the functions you add to the pipeline will run every time a certain database event occurs.\n\nThe following example registers a pipeline of functions to automatically update the maximum age every time you add a new person hash to your database.\n\n#### Example code\n\n[code example]\n\n#### Example event processing\n\nAfter you register your code with the `RG.JEXECUTE` command, add some data to the database and check the value of `age:maximum` to verify that it runs correctly.\n\n1. Connect to your database with `redis-cli`:\n\n    [code example]\n\n1. Add a hash that represents a person with `HSET`:\n\n    [code example]\n\n1. The current value of `age:maximum` should match Alex's age:\n\n    [code example]\n\n1. Add another person with a higher age and then check that `age:maximum` updated automatically:\n\n    [code example]\n\n1. Add a person with a lower age and verify that `age:maximum` did not change:\n\n    [code example]"}],"examples":[{"id":"create-a-maven-project-ex0","language":"xml","code":"<repositories>\n        <repository>\n            <id>snapshots-repo</id>\n            <url>https://oss.sonatype.org/content/repositories/snapshots</url>\n        </repository>\n    </repositories>\n\n    <dependencies>\n        <dependency>\n            <groupId>com.redislabs</groupId>\n            <artifactId>gear_runtime</artifactId>\n            <version>0.0.3-SNAPSHOT</version>\n        </dependency>\n    </dependencies>","section_id":"create-a-maven-project"},{"id":"build-a-jar-ex0","language":"sh","code":"$ mvn package","section_id":"build-a-jar"},{"id":"run-redisgears-java-code-ex0","language":"sh","code":"$ redis-cli -x -h {host} -p {port} RG.JEXECUTE {package.MainClass} < {filepath}/{JAR name}.jar","section_id":"run-redisgears-java-code"},{"id":"batch-processing-ex0","language":"sh","code":"$ redis-cli -h <host> -p <port>","section_id":"batch-processing"},{"id":"batch-processing-ex1","language":"sh","code":"127.0.0.1:12000> HSET review:1 user \"Alex L\" message \"My new favorite restaurant!\" rating 5\n    (integer) 3\n    127.0.0.1:12000> HSET review:2 user \"Anonymous user\" message \"Kind of overpriced\" rating 2\n    (integer) 3\n    127.0.0.1:12000> HSET review:3 user \"Francis J\" message \"They have a really unique menu.\" rating 4\n    (integer) 3\n    127.0.0.1:12000> exit","section_id":"batch-processing"},{"id":"batch-processing-ex2","language":"java","code":"import java.io.Serializable;\nimport gears.GearsBuilder;\nimport gears.readers.KeysReader;\nimport gears.records.KeysReaderRecord;\n\npublic class Reviews implements Serializable\n{\n\n\tprivate static final long serialVersionUID = 1L;\n\tint count; // Total number of reviews\n\tint ratingsSum; // Sum of all review ratings\n\t\n    // Reviews constructor\n\tpublic Reviews(int count, int ratingsSum) {\n\t\tthis.count = count;\n\t\tthis.ratingsSum = ratingsSum;\n\t}\n\n    public static void main(String args[]) \n    {  \n        // Create the reader that will pass data to the pipe\n        KeysReader reader = new KeysReader();\n        \n        // Create the data pipe builder\n        GearsBuilder<KeysReaderRecord> gb = GearsBuilder.CreateGearsBuilder(reader);\n        \n\t\tgb.filter(r->{\n\t\t\t// Filter out any keys that are not reviews\n\t\t\treturn r.getKey().startsWith(\"review:\");\n\t\t}).map(r->{\n\t\t\t// Extract the rating field\n\t\t\treturn r.getHashVal().get(\"rating\");\n\t\t})\n\t\t.accumulate(new Reviews(0, 0), (accumulator, record)-> {\n\t\t\t// Count the reviews and add up all of their ratings\n\t\t\taccumulator.count++;\n\t\t\taccumulator.ratingsSum += Integer.parseInt(record);\n\t\t\treturn accumulator;\n\t\t}).map(r->{\n\t\t\t// Calculate the average rating\n\t\t\treturn Double.valueOf(((double) r.ratingsSum) / r.count);\n\t\t});\n             \n        // Run the data through the pipeline immediately\n        gb.run();\n    }\n}","section_id":"batch-processing"},{"id":"batch-processing-ex3","language":"sh","code":"$ redis-cli -x -h {host} -p {port} \\\n    RG.JEXECUTE com.domain.packagename.Reviews < /tmp/rgjvmtest-0.0.1-SNAPSHOT.jar\n1) 1) \"3.6666666666666665\"\n2) (empty array)","section_id":"batch-processing"},{"id":"event-processing-ex0","language":"java","code":"import gears.GearsBuilder;\nimport gears.readers.KeysReader;\nimport gears.records.KeysReaderRecord;\n\npublic class App \n{\n    public static void main(String args[]) \n    {  \n        // Create the reader that will pass data to the pipe\n        KeysReader reader = new KeysReader();\n        \n        // Create the data pipe builder\n        GearsBuilder<KeysReaderRecord> gb = GearsBuilder.CreateGearsBuilder(reader);\n\n        // Only process keys that start with \"person:\"\n        gb.filter(r->{\n        \treturn r.getKey().startsWith(\"person:\");\n       \t});\n\n        // Compare each person's age to the current maximum age\n        gb.foreach(r->{\n        \tString newAgeStr = r.getHashVal().get(\"age\");\n        \tint newAge = Integer.parseInt(newAgeStr);\n        \t\n        \t// Get the current maximum age\n        \tString maxAgeKey = \"age:maximum\";\n        \tString maxAgeStr = (String) GearsBuilder.execute(\"GET\", maxAgeKey);\n        \t\n        \tint maxAge = 0; // Initialize to 0\n        \tif (maxAgeStr != null) {\n                // Convert the maximum age to an integer\n        \t\tmaxAge = Integer.parseInt(maxAgeStr);\n        \t}\n\n        \t// Update the maximum age if a new age is higher\n        \tif (newAge > maxAge) {               \n        \t\tGearsBuilder.execute(\"SET\", maxAgeKey, newAgeStr); \n        \t}\n        });\n        \n        // Store this pipeline of functions and \n        // run them when a new person key is added\n        gb.register(ExecutionMode.SYNC);\n        // Note: ExecutionMode defaults to ASYNC \n        // if you call register() without any arguments\n    }\n}","section_id":"event-processing"},{"id":"event-processing-ex1","language":"sh","code":"$ redis-cli -h <host> -p <port>","section_id":"event-processing"},{"id":"event-processing-ex2","language":"sh","code":"127.0.0.1:12000> HSET person:1 name \"Alex\" age 24\n    (integer) 2","section_id":"event-processing"},{"id":"event-processing-ex3","language":"sh","code":"127.0.0.1:12000> GET age:maximum\n    \"24\"","section_id":"event-processing"},{"id":"event-processing-ex4","language":"sh","code":"127.0.0.1:12000> HSET person:2 name \"Morgan\" age 45\n    (integer) 2\n    127.0.0.1:12000> GET age:maximum\n    \"45\"","section_id":"event-processing"},{"id":"event-processing-ex5","language":"sh","code":"127.0.0.1:12000> HSET person:3 name \"Lee\" age 31\n    (integer) 2\n    127.0.0.1:12000> GET age:maximum\n    \"45\"","section_id":"event-processing"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/recipes","title":"Java-based RedisGears recipes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/recipes/","summary":"","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/recipes/"],"tags":["docs","operate","stack"],"last_updated":"2026-04-21T10:45:15+01:00","children":[{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/recipes/write-behind","summary":"Write-behind, write-through, and read-through caching between Redis and other databases (SQL or NoSQL).","title":"Write-behind, write-through, and read-through caching","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/recipes/write-behind/"}],"page_type":"content","content_hash":"528b261720cb4842eba418c8b360949156cb9ff379e0d80b7547332d915a520e","sections":[{"id":"content","title":"Content","role":"content","text":"A RedisGears recipe is a collection of RedisGears functions, often designed to perform a specific, complex task.\n\n\n| Recipe | Description |\n| --- | --- |\n| [Caching strategies](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/recipes/write-behind/) | Write-behind, write-through, and read-through caching between Redis and other databases (SQL or NoSQL). |"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/recipes/write-behind","title":"Write-behind, write-through, and read-through caching","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/recipes/write-behind/","summary":"Write-behind, write-through, and read-through caching between Redis and other databases (SQL or NoSQL).","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/jvm/recipes/write-behind/"],"tags":["docs","operate","stack"],"last_updated":"2026-06-24T14:18:07+01:00","page_type":"content","content_hash":"cfaa1e3bcdab49800d290db5510f63c47867ee6bda15e7bcdf2565adefeeecc5","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The [rghibernate](https://github.com/RedisGears/rghibernate) recipe uses RedisGears functions and the [Hibernate](https://hibernate.org/) framework to implement write-behind, write-through, and read-through caching.\n\nThese caching strategies allow applications to simply connect to a Redis cache layer instead of an underlying database. Whenever the application updates data in the cache, Redis also syncs the data in the backend database.\n\nThe underlying database could be an SQL database like MySQL, so you will need to provide an XML file that tells rghibernate how to map data between Redis and the other database."},{"id":"differences-from-rgsync","title":"Differences from RGSync","role":"content","text":"[RGSync](https://github.com/RedisGears/rgsync):\n- Python-based recipe\n- Programmable\n\n[rghibernate](https://github.com/RedisGears/rghibernate):\n- Java-based recipe\n- Uses the Hibernate framework\n- Configurable rather than programmable"},{"id":"set-up-write-behind-and-read-through","title":"Set up write-behind and read-through","role":"content","text":"To set up write-behind caching, first build an rghibernate JAR and register it with RedisGears.\n\nThen, register the following configuration files:\n\n- Connector XML: Tells Redis how to connect to the underlying database.\n\n- Mapping XML: Shows how to map data between the two databases, such as mapping Redis hashes to MySQL tables."},{"id":"register-rghibernate-jar","title":"Register rghibernate JAR","role":"content","text":"1. Download the rghibernate JAR from the [rghibernate repository](https://github.com/RedisGears/rghibernate).\n\n1. Upload the JAR to a Redis node.\n\n1. Register rghibernate with RedisGears:\n\n    [code example]"},{"id":"configure-database-connection","title":"Configure database connection","role":"configuration","text":"1. Create a [connector XML file](#connector-xml) with the configuration to connect Redis to an underlying database.\n\n1. Upload the file to a Redis node.\n\n1. Register the connector configuration:\n\n    [code example]"},{"id":"configure-data-mapping","title":"Configure data mapping","role":"configuration","text":"1. Create a [mapping XML file](#mapping-xml) that defines how Redis maps data to an underlying database.\n\n1. Upload the file to a Redis node.\n\n1. Register the mapping configuration for write-behind:\n\n    [code example]`\n\n1. Register the same mapping configuration for read-through:\n\n    [code example]`"},{"id":"example-configuration","title":"Example configuration","role":"example","text":"Here are some example configuration files for connecting to databases and mapping data."},{"id":"connector-xml","title":"Connector XML","role":"content","text":"This configuration file contains connection details for an underlying MySQL database:\n\n[code example]"},{"id":"mapping-xml","title":"Mapping XML","role":"content","text":"The following XML maps Redis hashes, which represent students, to a MySQL table:\n\n[code example]"},{"id":"commands","title":"Commands","role":"syntax","text":"Run rghibernate commands with `RG.TRIGGER`:\n\n[code example]\n\nTo pass a file to a command like `SYNC.REGISTERCONNECTOR` or `SYNC.REGISTERSOURCE`, use the <nobr>`redis-cli -x`</nobr> option:\n\n[code example]"},{"id":"command-list","title":"Command list","role":"syntax","text":"| Command | Description |\n|---------|-------------|\n| [SYNC.REGISTERCONNECTOR](#syncregisterconnector) | Register a new connector |\n| SYNC.UNREGISTERCONNECTOR | Unregister a connector (cannot have sources attached) |\n| [SYNC.REGISTERSOURCE](#syncregistersource) | Extra configuration based on policy |\n| SYNC.UNREGISTERSOURCE | Unregister a source |\n| SYNC.INFO SOURCES | Dump all sources |\n| SYNC.INFO CONNECTORS | Dump all connectors |"},{"id":"syncregisterconnector","title":"SYNC.REGISTERCONNECTOR","role":"content","text":"[code example]\n\n| Name | Description |\n|------|-------------|\n| connector name | Name to give to your connector |\n| batch size | The size of the data sent to the backend in batches |\n| timeout | After this timeout, sends data to the backend even if the batch size was not reached |\n| retry interval | Retry interval on error |\n| connector xml | Hibernate XML definition of the connector |"},{"id":"syncregistersource","title":"SYNC.REGISTERSOURCE","role":"content","text":"[code example]\n\n| Name | Description |\n|------|-------------|\n| source name | Name to give to your source |\n| connector name | Connector to send the data to |\n| policy | WriteBehind/WriteThrough/ReadThrough: <br></br>• On WriteThrough, an extra argument is WriteTimeout <br></br>• On ReadThrough, an extra argument is expire (0 for no expire) |\n| mapping xml | Hibernate XML definition of the mapping |"}],"examples":[{"id":"register-rghibernate-jar-ex0","language":"sh","code":"$ redis-cli -x RG.JEXECUTE com.redislabs.WriteBehind < {filepath}/rghibernate-0.1.1-jar-with-dependencies.jar","section_id":"register-rghibernate-jar"},{"id":"configure-database-connection-ex0","language":"sh","code":"> redis-cli -x RG.TRIGGER SYNC.REGISTERCONNECTOR mysql 1000 10 5 < src/test/resources/mysql_hibernate.cfg.xml \n    1) \"OK\"","section_id":"configure-database-connection"},{"id":"configure-data-mapping-ex0","language":"sh","code":"> redis-cli -x RG.TRIGGER SYNC.REGISTERSOURCE StudentWrite mysql WriteBehind < src/test/resources/Student.hbm.xml \n    1) \"OK\"","section_id":"configure-data-mapping"},{"id":"configure-data-mapping-ex1","language":"sh","code":"> redis-cli -x RG.TRIGGER SYNC.REGISTERSOURCE StudentRead mysql ReadThrough 0 < src/test/resources/Student.hbm.xml \n    1) \"OK\"","section_id":"configure-data-mapping"},{"id":"connector-xml-ex0","language":"xml","code":"<!DOCTYPE hibernate-configuration PUBLIC\n        \"-//Hibernate/Hibernate Configuration DTD 3.0//EN\"\n  \"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd\">\n        \n<hibernate-configuration>\n  <session-factory>\n    <!-- JDBC Database connection settings -->\n    <property name=\"connection.driver_class\">org.mariadb.jdbc.Driver</property>\n    <property name=\"connection.url\">jdbc:mysql://localhost:3306/test?allowPublicKeyRetrieval=true&useSSL=false</property>\n    <property name=\"connection.username\">user</property>\n    <property name=\"connection.password\">pass</property>\n    <!-- JDBC connection pool settings ... using built-in test pool -->\n    <property name=\"connection.pool_size\">1</property>\n    <!-- Echo the SQL to stdout -->\n    <property name=\"show_sql\">false</property>\n    <!-- Set the current session context -->\n    <property name=\"current_session_context_class\">thread</property>\n    <!-- Drop and re-create the database schema on startup -->\n    <property name=\"hbm2ddl.auto\">update</property>\n    <!-- dbcp connection pool configuration -->\n    <property name=\"hibernate.dbcp.initialSize\">5</property>\n    <property name=\"hibernate.dbcp.maxTotal\">20</property>\n    <property name=\"hibernate.dbcp.maxIdle\">10</property>\n    <property name=\"hibernate.dbcp.minIdle\">5</property>\n    <property name=\"hibernate.dbcp.maxWaitMillis\">-1</property>\n  </session-factory>\n</hibernate-configuration>","section_id":"connector-xml"},{"id":"mapping-xml-ex0","language":"xml","code":"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<hibernate-mapping xmlns=\"http://www.hibernate.org/xsd/hibernate-mapping\"\n  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n  xsi:schemaLocation=\"http://www.hibernate.org/xsd/hibernate-mapping\n    http://www.hibernate.org/xsd/hibernate-mapping/hibernate-mapping-4.0.xsd\">\n\n  <class entity-name=\"Student\" table=\"student\">\n          <tuplizer entity-mode=\"dynamic-map\" class=\"org.hibernate.tuple.entity.DynamicMapEntityTuplizer\"/>\n          <id name=\"id\" type=\"integer\" length=\"50\" column=\"id\"/>\n          <property name=\"firstName\" column=\"first_name\" type=\"string\"/>\n          <property name=\"lastName\" column=\"last_name\" type=\"string\"/>\n          <property name=\"email\" column=\"email\" type=\"string\" not-null=\"true\"/>\n          <property name=\"age\" column=\"age\" type=\"integer\"/>\n  </class>\n\n</hibernate-mapping>","section_id":"mapping-xml"},{"id":"commands-ex0","language":"sh","code":"redis-cli RG.TRIGGER SYNC.<COMMAND>","section_id":"commands"},{"id":"commands-ex1","language":"sh","code":"redis-cli -x RG.TRIGGER SYNC.{COMMAND} {arg1 arg2 ...} < {file}","section_id":"commands"},{"id":"syncregisterconnector-ex0","language":"sh","code":"$ redis-cli -x RG.TRIGGER SYNC.REGISTERCONNECTOR \\\n    {connector name} {batch size} {timeout} {retry interval} \\\n    < {connector xml}","section_id":"syncregisterconnector"},{"id":"syncregistersource-ex0","language":"sh","code":"redis-cli -x RG.TRIGGER SYNC.REGISTERSOURCE \\\n    {source name} {connector name} {policy} \\\n    < {mapping xml}","section_id":"syncregistersource"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/python","title":"RedisGears Python plugin","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/python/","summary":"The RedisGears Python plugin allows you to run RedisGears functions with Python.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/python/"],"tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:38+01:00","children":[{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/python/quickstart","summary":"A quick start to learn how to use RedisGears with Python.","title":"RedisGears Python quick start","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/python/quickstart/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/python/install","summary":"","title":"Install RedisGears and the Python plugin","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/python/install/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/python/recipes","summary":"","title":"Python-based RedisGears recipes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/python/recipes/"}],"page_type":"content","content_hash":"54bfc52515f24bf922b9fae625dbe92f25e3910a046f7210930e417d9a5852c5","sections":[{"id":"overview","title":"Overview","role":"overview","text":"With the RedisGears Python plugin, you can write RedisGears functions in [Python](https://www.python.org/) and run them on a [Redis Software](https://redis.io/docs/latest/operate/rs) cluster.\n\nThe Python plugin allows both batch processing and event-driven processing.\n\nBefore you can run RedisGears with Python, you will need to [install the RedisGears module and the Python plugin](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/installing-redisgears#install-redisgears) on your Redis Software cluster and [enable them for your database](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/python/install).\n\nOnce you have written your code, upload it to a node on your Redis Software cluster. Use the `RG.PYEXECUTE` command with the `redis-cli` command-line tool to run your code."},{"id":"more-info","title":"More info","role":"content","text":"- [RedisGears Python quick start](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/python/quickstart)\n- [RedisGears recipes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/python/recipes)"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/python/install","title":"Install RedisGears and the Python plugin","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/python/install/","summary":"","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/python/install/"],"tags":["docs","operate","stack"],"last_updated":"2026-10-02T14:53:57+01:00","page_type":"content","content_hash":"d403ee00af78c7ee64ae932cf82d014985916882d503ad8b8f6f38c60d114bf0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Before you can use RedisGears with Python, you need to install the RedisGears module and Python plugin on your Redis Software cluster and enable them for a database."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"1. Redis Software v6.0.12 or later\n\n1. [Created a Redis Software cluster](https://redis.io/docs/latest/operate/rs/clusters/new-cluster-setup)\n\n1. [Added nodes to the cluster](https://redis.io/docs/latest/operate/rs/clusters/add-node)\n\n1. [Installed RedisGears and the Python plugin](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/installing-redisgears#install-redisgears)"},{"id":"enable-redisgears-for-a-database","title":"Enable RedisGears for a database","role":"content","text":"1. From the Redis Software admin console's **databases** page, select the **Add** button to create a new database:\n\n    ![The Add icon](https://redis.io/docs/latest/images/rs/icon_add.png)\n\n1. Confirm that you want to create a new Redis database with the **Next** button.\n\n1. On the **create database** page, give your database a name.\n\n1. For **Redis Modules**, select the **Add** button and choose RedisGears from the **Module** dropdown list.\n\n1. Select **Add Configuration**, enter <nobr>`Plugin gears_python CreateVenv 1`</nobr> in the box, then select the **OK** button:\n\n    ![The Save icon](https://redis.io/docs/latest/images/rs/icon_save.png)\n\n    > [!NOTE]\n    > Only RedisGears v1.2 and later require this configuration.\n    >     \n\n1. Select the **Activate** button."},{"id":"verify-the-install","title":"Verify the install","role":"content","text":"Run the `RG.PYSTATS` command to view statistics and verify that you set up RedisGears and the Python plugin correctly:\n\n[code example]"}],"examples":[{"id":"verify-the-install-ex0","language":"sh","code":"redis> RG.PYSTATS","section_id":"verify-the-install"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/python/quickstart","title":"RedisGears Python quick start","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/python/quickstart/","summary":"A quick start to learn how to use RedisGears with Python.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/python/quickstart/"],"tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:38+01:00","page_type":"content","content_hash":"182dbda6e1584dbadc33f358b9af74eb98276b7c6f024d74e9ae053e24e851b1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"For this tutorial, you need:\n\n- Either:\n    - A Redis Software cluster with the [RedisGears module and Python plugin installed](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/installing-redisgears#install-redisgears) and [enabled on a database](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/python/install)\n    - A Redis Open Source database with the RedisGears module\n- `redis-cli` with connectivity to a Redis database"},{"id":"redisgears-basics","title":"RedisGears basics","role":"content","text":"In this quick start guide, we'll see how to use RedisGears to perform batch processing and event processing.\n\nWith RedisGears, **batch processing** means processing the data already stored in a Redis database. **Event processing** means processing changes to the Redis key space.\n\nThe examples below assume an empty Redis database."},{"id":"batch-processing","title":"Batch processing","role":"content","text":"Let's start with the simplest example. From the `redis-cli`, run the following command:\n\n[code example]\n\nThis command doesn't do much; it simply iterates over the keyspace. Let's add a key and run it again:\n\n[code example]\n\nWe've added a single string, and you can see that this gears function processes it, even though it does nothing with the data. Let's actually do something with the data. So first, we'll add a few more strings:\n\n[code example]\n\nWe now have three strings in our database. Suppose we want to perform a unique word count on these strings. We can write a RedisGears function to do this just that. So open a file called `wordcount.py`, and add the following code:\n\n[code example]\n\nThere are two ways to load files into RedisGears. For production deployments, we recommend using the special [`gears-cli`](https://github.com/gears-project/gears-cli). However, for the purpose of this demonstration, the easiest way is to pass the filename through the `redis-cli` command, like so:\n\n[code example]\n\nThe results here show the number of occurrences of each word in all of our strings. So, we've effectively processed the data in our Redis database all at once, in a batch."},{"id":"event-processing","title":"Event processing","role":"content","text":"You may have noticed that all of the RedisGears functions above end with a call to `run()`. This indicates that the function should be run immediately on the data in the Redis database. But what if you want to process data as it arrives in Redis? In that case, your functions will end with a call to `register()`, which will store the function and apply it as events occur in Redis.\n\nLet's see how to register a function. First, suppose we're writing hashes to our database that represent users. They take the following form:\n\n[code example]\n\nEach hash has two fields, one containing a name and the other an age. Now, suppose we want to keep a record of the maximum age of all users. We can register a RedisGears function to do this. Open up a file called `maxage.py`, and add the following code:\n\n[code example]\n\nYou can see here that we define two methods: `age()` and `compare_and_swap()`. Even if you're not familiar with Python, you should be able to see what the methods do.\n\nBelow the method definitions is the RedisGears data flow that we're defining. Notice that at the end we call `register()` to register the function to listen for events.\n\nTo load this function into RedisGears, run the following:\n\n[code example]\n\nNow start the `redis-cli`, and create a couple of hashes:\n\n[code example]\n\nTo see if the RedisGears function is working, check the value of `age:maximum`:\n\n[code example]"},{"id":"next-steps","title":"Next steps","role":"content","text":"You should now have a basic idea of how to run RedisGears functions for batch and event processing. If you're interested in write-behind caching, see our [write-behind caching](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/python/recipes/write-behind) overview."}],"examples":[{"id":"batch-processing-ex0","language":"sh","code":"redis.cloud:6379> RG.PYEXECUTE \"GearsBuilder().run()\"\n1) (empty array)\n2) (empty array)","section_id":"batch-processing"},{"id":"batch-processing-ex1","language":"sh","code":"redis.cloud:6379> SET message \"hello world\"\nOK\nredis.cloud:6379> RG.PYEXECUTE \"GearsBuilder().run()\"\n1) 1) \"{'event': None, 'key': 'message', 'type': 'string', 'value': 'hello world'}\"\n2) (empty array)","section_id":"batch-processing"},{"id":"batch-processing-ex2","language":"sh","code":"redis.cloud::6379> SET message:2 \"hello galaxy\"\nOK\nredis.cloud:6379> SET message:3 \"hello universe\"\nOK","section_id":"batch-processing"},{"id":"batch-processing-ex3","language":"py","code":"gb = GearsBuilder()\ngb.map(lambda x: x['value'])     # map each key object to its string value\ngb.flatmap(lambda x: x.split())  # split each string into a list of words\ngb.countby()                     # run a count-unique on these words\ngb.run()","section_id":"batch-processing"},{"id":"batch-processing-ex4","language":"sh","code":"$ redis-cli rg.pyexecute \"`cat wordcount.py`\"\n1) 1) \"{'key': 'world', 'value': 1}\"\n   2) \"{'key': 'galaxy', 'value': 1}\"\n   3) \"{'key': 'hello', 'value': 3}\"\n   4) \"{'key': 'universe', 'value': 1}\"\n2) (empty array)","section_id":"batch-processing"},{"id":"event-processing-ex0","language":"sh","code":"redis.cloud:6379> HSET person:3 name \"Summer Smith\" age 17\n(integer) 2\nredis.cloud:6379> HSET person:4 name \"James Jameson\" age 21\n(integer) 2","section_id":"event-processing"},{"id":"event-processing-ex1","language":"py","code":"def age(x):\n  ''' Extracts the age from a person's record '''\n  return int(x['value']['age'])\n\ndef compare_and_swap(x):\n  ''' Checks and sets the current maximum '''\n  k = 'age:maximum'\n  v = execute('GET', k)   # read key's current value\n  v = int(v) if v else 0  # initialize to 0 if None\n  if x > v:               # if a new maximum found\n    execute('SET', k, x)  # set key to new value\n\n# Event handling function registration\ngb = GearsBuilder()\ngb.map(age) # Extract the 'age' field from each hash\ngb.foreach(compare_and_swap) # Compare the max age to the value stored at age:maximum\ngb.register(prefix='person:*') # Only process keys matching the pattern 'person:*'","section_id":"event-processing"},{"id":"event-processing-ex2","language":"sh","code":"$ redis-cli RG.PYEXECUTE \"`cat maxage.py`\"","section_id":"event-processing"},{"id":"event-processing-ex3","language":"sh","code":"redis.cloud:6379> HSET person:5 name \"Marek Michalski\" age 17\n(integer) 2\nredis.cloud:6379> HSET person:6 name \"Noya Beit\" age 21\n(integer) 2","section_id":"event-processing"},{"id":"event-processing-ex4","language":"sh","code":"redis.cloud:6379> GET age:maximum\n\"21\"","section_id":"event-processing"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/python/recipes","title":"Python-based RedisGears recipes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/python/recipes/","summary":"","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/python/recipes/"],"tags":["docs","operate","stack"],"last_updated":"2026-04-21T10:45:15+01:00","children":[{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/python/recipes/write-behind","summary":"Write-behind and write-through caching between Redis and other databases (SQL or NoSQL).","title":"Write-behind caching","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/python/recipes/write-behind/"}],"page_type":"content","content_hash":"babf47ace47d64587da9548ec1be83bf42dbe433e905b69f821e309521fe0ada","sections":[{"id":"content","title":"Content","role":"content","text":"A RedisGears recipe is a collection of RedisGears functions, often designed to perform a specific, complex task.\n\n\n| Recipe | Description |\n| --- | --- |\n| [Write-behind caching](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/python/recipes/write-behind/) | Write-behind and write-through caching between Redis and other databases (SQL or NoSQL). |"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/python/recipes/write-behind","title":"Write-behind caching","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/python/recipes/write-behind/","summary":"Write-behind and write-through caching between Redis and other databases (SQL or NoSQL).","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/python/recipes/write-behind/"],"tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:38+01:00","page_type":"content","content_hash":"e48ae13c572cb3ee8218b0925e19136c1cc0077abacaac8ad11c58436980c2dc","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Write-behind is a caching strategy in which the cache layer itself connects to the backing database. This means that your applications need only ever connect to your cache layer, and the cache then reads from or updates the backing database as needed. Redis currently supports write-behind caching in [Redis Software](https://redis.io/docs/latest/operate/rs).\n\nHere's how these caching patterns work:\n\n1. Your application uses the cache for reads and writes.\n1. The cache syncs any changed data to the backing database asynchronously."},{"id":"install-the-write-behind-recipe","title":"Install the write-behind recipe","role":"setup","text":"The write-behind recipe comes with two types of dependencies:\n\n- Drivers to connect to the backend database\n- Python libraries for the RedisGears functions\n\nIn most cases all of these can be provisioned to RedisGears before the functions are uploaded.\nHowever, root access for the driver on the host is required in some cases, for example with Oracle drivers."},{"id":"install-oracle-driver-optional","title":"Install Oracle driver (optional)","role":"setup","text":"If you want to do write-behind with an Oracle database:\n\n1. [Download the Oracle driver](https://www.oracle.com/database/technologies/instant-client/linux-x86-64-downloads.html).\n1. On each RS node in the cluster, follow the installation instructions on the download page for the Oracle driver."},{"id":"import-requirements","title":"Import requirements","role":"content","text":"1. Install [Python 3](https://www.python.org/downloads/).\n1. To install the [gears-cli](https://pypi.org/project/gears-cli/), run: \n    [code example]\n\n1. Download [rgsync package](https://github.com/RedisGears/rgsync).\n1. Import the requirements:\n\n    [code example]\n\n    > [!NOTE]\n    > You can be more efficient and import only the requirements you need, but rgsync is always required and can be combined with one or more of these packages according to your backend database:\n    >\n    > - redisgears-requirement-v1-snowflake-sqlalchemy-linux-\\<os>-x64.zip\n    > - redisgears-requirement-v1-PyMySQL-linux-\\<os>-x64.zip\n    > - redisgears-requirement-v1-cx-Oracle-linux-\\<os>-x64.zip\n    > - redisgears-requirement-v1-cassandra-driver-linux-\\<os>-x64.zip\n    >\n    > This list can be different or more extensive in newer versions.\n    > The `module.json` file in the module package lists the dependencies for the module.\n    >\n    >     \n\n1. From the CLI of a node or of a client that is connected to the database, check that the requirements were imported successfully with: \n\n    [code example]\n\n    To connect to the database from a client, run:\n\n    [code example]\n\n    This command returns a list of all available requirements."},{"id":"register-the-functions","title":"Register the functions","role":"content","text":"The following is a RedisGears recipe that shows how to use the write-behind pattern to map data from Redis hashes to MySQL tables.\n\nThe recipe maps all Redis hashes with the prefix <nobr>`person:<id>`</nobr> to the MySQL table `persons`, with `<id>` being the primary key and mapped to the `person_id` column.\nSimilarly, it maps all hashes with the prefix <nobr>`car:<id>`</nobr> to the `cars` table.\n\n[code example]\n\nGo to the [rgsync website](https://pypi.org/project/rgsync/) to get the replication options and the configuration options for the database and mapping.\n\n1. Create a Python file with the configuration mapping according to your specific needs.\n1. Run `gears-cli` with your custom file:\n\n    [code example]"},{"id":"write-behind-caching-with-redisgears","title":"Write-behind caching with RedisGears","role":"content","text":"[RGSync](https://github.com/RedisGears/rgsync) is a RedisGears recipe that uses the write-behind pattern to synchronize Redis data structures to a backing data store (Oracle, MySQL, Cassandra, and Snowflake are currently supported).\n\nHere's how you can create a write-behind function using the `RGSync` recipe against MySQL. This function will synchronize a set of fields in a Redis hash to the columns in a MySQL row. The key name for the Redis hash must have the form `{name}:{id}`, where `id` is the primary key for the row we want to keep in sync with. So, for example, suppose our hashes look like this:\n\n[code example]\n\nThe data in this hash would correspond to a row with ID of 5. But we need to define the mapping from our hash field to our columns. In our function, we do this using a Python dictionary:\n\n[code example]\n\nThis is saying that the `last_name` field in our hash should be synchronized with the 'last' field in our row. So, now we're ready to write our RedisGears function.\n\nFirst, load the necessary libraries and connect to MySQL:\n\n[code example]\n\nNext, create a connector object, specifying the table name and the name of the column storing the primary key:\n\n[code example]\n\nAfter that, define your field-column mappings:\n\n[code example]\n\nFinally, use these initialized objects to create an `RGWRiteBehind` object:\n\n[code example]\n\nNotice here that you can version this function registration with the `version` argument, as newer versions with the same `name` will replace the old one.\n\nIf you've placed all of this code into a file (`example.py`), then you can load it into your Redis deployment using the [`gears-cli`](https://github.com/RedisGears/gears-cli) tool:\n\n[code example]"},{"id":"secret-management","title":"Secret management","role":"content","text":"You may not want to store database credentials in your RedisGears functions. To avoid this, you can pass these credentials as module parameters and then reference them from your functions.\n\nThe code below shows how to reference these credentials when creating a backing database connection.\n\n[code example]\n\nNotice that for each credential, we define a Python function that returns the specified module parameter. We then provide each function reference when we instantiate `MySqlConnection`.\n\nThis code references three parameters: `MySqlUser`, `MySqlPassword`, and `MySqlDB`. In Redis Software, you can set these parameters using the `rladmin` tool. The command to set these parameters takes the following form:\n\n[code example]\n\nTo set the `MySqlPassword` parameter to \"Password123!\" on a database named \"user-api\", you would run the this `rladmin` command:\n\n[code example]\n\nOnce a connection is successfully established, RedisGears will not attempt to reconnect until a disconnect occurs.\nThis means that updates to the parameters that store the secrets will not take effect immediately,\nbut they will be used for all subsequent connection attempts."}],"examples":[{"id":"import-requirements-ex0","language":"sh","code":"pip install gears-cli","section_id":"import-requirements"},{"id":"import-requirements-ex1","language":"sh","code":"# gears-cli import-requirements \\\n        --host HOST [ --port PORT ] --password PASSWORD \\\n        --requirements-path rgsync-99.99.99.linux-bionic-x64.zip","section_id":"import-requirements"},{"id":"import-requirements-ex2","language":"sh","code":"redis-cli RG.PYDUMPREQS","section_id":"import-requirements"},{"id":"import-requirements-ex3","language":"sh","code":"redis-cli -h <FQDN_of_node> -p <host> [-a <password>]","section_id":"import-requirements"},{"id":"register-the-functions-ex0","language":"sh","code":"from rgsync import RGWriteBehind\nfrom rgsync.Connectors import MySqlConnector, MySqlConnection\n\n'''\nCreate MySQL connection object\n'''\nconnection = MySqlConnection('demouser', 'Password123!', 'localhost:3306/test')\n\n'''\nCreate MySQL persons connector\npersons - MySQL table to put the data\nperson_id - primary key\n'''\npersonConnector = MySqlConnector(connection, 'persons', 'person_id')\n\npersonsMappings = {\n\t'first_name':'first',\n\t'last_name':'last',\n\t'age':'age'\n}\n\nRGWriteBehind(GB, keysPrefix='person', mappings=personsMappings, connector=personConnector, name='PersonsWriteBehind', version='99.99.99')\n\n'''\nCreate MySQL car connector\ncars - MySQL table to put the data\ncar_id - primary key\n'''\ncarConnector = MySqlConnector(connection, 'cars', 'car_id')\n\ncarsMappings = {\n\t'id':'id',\n\t'color':'color'\n}\n\nRGWriteBehind(GB, keysPrefix='cars', mappings=carsMappings, connector=carConnector, name='CarsWriteBehind', version='99.99.99')","section_id":"register-the-functions"},{"id":"register-the-functions-ex1","language":"sh","code":"gears-cli run --host <host> --port <post> --password <password> <yourfile>.py","section_id":"register-the-functions"},{"id":"write-behind-caching-with-redisgears-ex0","language":"sh","code":"redis.cloud:6379> HSET person:5 first_name Marek last_name Michalski age 27\n(integer) 2","section_id":"write-behind-caching-with-redisgears"},{"id":"write-behind-caching-with-redisgears-ex1","language":"py","code":"personsMappings = {\n\t'first_name':'first',\n\t'last_name':'last',\n\t'age':'age'\n}","section_id":"write-behind-caching-with-redisgears"},{"id":"write-behind-caching-with-redisgears-ex2","language":"py","code":"from rgsync import RGWriteBehind\nfrom rgsync.Connectors import MySqlConnector, MySqlConnection\n\nconnection = MySqlConnection('myapplication', 'Password123!', 'localhost:3306/test')","section_id":"write-behind-caching-with-redisgears"},{"id":"write-behind-caching-with-redisgears-ex3","language":"py","code":"personConnector = MySqlConnector(mySqlConnection, 'persons', 'id')","section_id":"write-behind-caching-with-redisgears"},{"id":"write-behind-caching-with-redisgears-ex4","language":"py","code":"personsMappings = {\n\t'first_name':'first',\n\t'last_name':'last',\n\t'age':'age'\n}","section_id":"write-behind-caching-with-redisgears"},{"id":"write-behind-caching-with-redisgears-ex5","language":"py","code":"RGWriteBehind(GB,\n              name='PersonsWriteBehind',\n              version='1',\n              connector=personsConnector,\n              keysPrefix='person',\n              mappings=personsMappings)","section_id":"write-behind-caching-with-redisgears"},{"id":"write-behind-caching-with-redisgears-ex6","language":"sh","code":"gears-cli --host <host>\n          --port <post>\n          --password <password>\n          example.py\n          REQUIREMENTS rgsync","section_id":"write-behind-caching-with-redisgears"},{"id":"secret-management-ex0","language":"py","code":"def User():\n\treturn configGet('MySqlUser')\ndef Password():\n\treturn configGet('MySqlPassword')\ndef DB():\n\treturn configGet('MySqlDB')\n\nconnection = MySqlConnection(User, Password, DB)","section_id":"secret-management"},{"id":"secret-management-ex1","language":"sh","code":"rladmin tune db [DB-NAME] module_name rg module_config_params \"[PARAM-NAME] [PARAM-VALUE]\"","section_id":"secret-management"},{"id":"secret-management-ex2","language":"sh","code":"rladmin tune db user-api module_name rg module_config_params \"MySqlPassword Password123!\"","section_id":"secret-management"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/register-events","title":"Register events","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/register-events/","summary":"Register RedisGears functions to run when certain events occur in a Redis database.","aliases":["/operate/oss_and_stack/stack-with-enterprise/gears-v1/register-events/"],"tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:38+01:00","page_type":"content","content_hash":"0aa6bb7bc022ba49aacea760c2d9b175c026e41dd1c98f3c1975d83c3960663a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can register RedisGears functions to run when certain events occur in a Redis database."},{"id":"register-on-events","title":"Register on events","role":"content","text":"To register RedisGears functions to run on an event, your code needs to:\n\n1. Pass `KeysReader` to a `GearsBuilder` object.\n\n1. Call the `GearsBuilder.register()` function.\n\n1. Pass the `eventTypes` parameter to either:\n\n    - The `register` function for Python.\n    \n    - The `KeysReader` object for Java.\n\nFor more information and examples of event registration, see:\n\n- Java references:\n\n    - [`KeysReader`](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/readers/keysreader)\n\n    - [`GearsBuilder.register()`](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm/classes/gearsbuilder/register)"},{"id":"event-types","title":"Event types","role":"content","text":"For the list of event types you can register on, see the [Redis keyspace notification documentation](https://redis.io/docs/latest/develop/pubsub/keyspace-notifications#events-generated-by-different-commands)."},{"id":"active-active-event-types","title":"Active-Active event types","role":"content","text":"In addition to standard Redis [events](https://redis.io/docs/latest/develop/pubsub/keyspace-notifications#events-generated-by-different-commands), [Redis Software Active-Active databases](https://redis.io/docs/latest/operate/rs/databases/active-active) also support the registration of RedisGears functions for the following event types:\n\n- `change`: This event occurs when a key changes on another replica of the Active-Active database."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/graph/commands","title":"Graph commands","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/graph/commands/","summary":"Lists graph commands and provides links to the command reference pages.","tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:38+01:00","page_type":"content","content_hash":"aee99e2265187808e8a17813740a6dde4796e5425c6d0dfb90c37ecbf0050326","sections":[{"id":"content","title":"Content","role":"content","text":"The following table lists graph commands. See the command links for more information about each command's syntax, arguments, and examples.\n\n| Command | Redis Software | Redis Cloud<br />Flexible & Annual | Redis Cloud<br />Free & Fixed | Description |\n|:--------|:----------------------|:-----------------|:-----------------|:------|\n| [GRAPH.CONFIG GET](https://github.com/RedisGraph/RedisGraph/blob/master/docs/commands/graph.config-get.md) | <span title=\"Not supported\"><nobr>&#x274c; Not supported</span> | <span title=\"Not supported\"><nobr>&#x26A0;&#xFE0F; Not supported</span><sup>[2](#table-note-2)</sup> | <span title=\"Not supported\"><nobr>&#x274c; Not supported</nobr></span> | Returns the current value of a [RedisGraph configuration parameter](https://github.com/RedisGraph/RedisGraph/blob/master/docs/docs/configuration.md#redisgraph-configuration-parameters). |\n| [GRAPH.CONFIG SET](https://github.com/RedisGraph/RedisGraph/blob/master/docs/commands/graph.config-set.md) | <span title=\"Not supported\"><nobr>&#x26A0;&#xFE0F; Not supported</span><sup>[1](#table-note-1)</sup> | <span title=\"Not supported\"><nobr>&#x26A0;&#xFE0F; Not supported</span><sup>[2](#table-note-2)</sup> | <span title=\"Not supported\"><nobr>&#x274c; Not supported</nobr></span> | Changes the value of a [RedisGraph configuration parameter](https://github.com/RedisGraph/RedisGraph/blob/master/docs/docs/configuration.md#redisgraph-configuration-parameters). |\n| [GRAPH.DELETE](https://github.com/RedisGraph/RedisGraph/blob/master/docs/commands/graph.delete.md) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> | Removes the graph and its entities. |\n| [GRAPH.EXPLAIN](https://github.com/RedisGraph/RedisGraph/blob/master/docs/commands/graph.explain.md) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> | Displays the query execution plan but does not run the query. |\n| [GRAPH.LIST](https://github.com/RedisGraph/RedisGraph/blob/master/docs/commands/graph.list.md) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> | Lists all graph keys. |\n| [GRAPH.PROFILE](https://github.com/RedisGraph/RedisGraph/blob/master/docs/commands/graph.profile.md) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> | Runs a query and displays the execution plan with metrics for each operation. |\n| [GRAPH.QUERY](https://github.com/RedisGraph/RedisGraph/blob/master/docs/commands/graph.query.md) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> | Runs a query against a graph. Supports a variety of [clauses](https://github.com/RedisGraph/RedisGraph/blob/master/docs/commands/graph.query.md#query-structure) and [functions](https://github.com/RedisGraph/RedisGraph/blob/master/docs/commands/graph.query.md#functions). |\n| [GRAPH.RO_QUERY](https://github.com/RedisGraph/RedisGraph/blob/master/docs/commands/graph.ro_query.md) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> | Runs a read-only query against a graph. |\n| [GRAPH.SLOWLOG](https://github.com/RedisGraph/RedisGraph/blob/master/docs/commands/graph.slowlog.md) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> | Returns the slowest 10 queries run against a specific graph. |\n\n1. <a name=\"table-note-1\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>Use [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin) or the [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api) to change RedisGraph configuration for Redis Software. See [RedisGraph configuration compatibility with Redis Software](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/graph/config) for more information and examples.\n\n2. <a name=\"table-note-2\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>[Contact support](https://redis.com/company/support/) to view the current RedisGraph configuration values or request configuration changes for Flexible or Annual Redis Cloud subscriptions."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/graph/config","title":"Graph configuration compatibility with Redis Software","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/graph/config/","summary":"Graph configuration settings supported by Redis Software.","tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:38+01:00","page_type":"content","content_hash":"67f7f302b22b82c25a2106d37d73623afb05e1a1f697f2b8ab01409817d62e90","sections":[{"id":"content","title":"Content","role":"content","text":"You cannot use `GRAPH.CONFIG SET` to configure RedisGraph in [Redis Software](https://redis.io/docs/latest/operate/rs) or [Redis Cloud](https://redis.io/docs/latest/operate/rc). Instead, use one of the following methods.\n\nFor Redis Cloud:\n\n- _Flexible or Annual [subscriptions](https://redis.io/docs/latest/operate/rc/subscriptions)_: contact [support](https://redis.com/company/support/) to request a configuration change.\n    \n- _Free or Fixed subscriptions_: you cannot change RedisGraph configuration.\n\nFor Redis Software, use either:\n\n- [`rladmin tune db`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-db):\n\n    [code example]\n\n- [Configure module](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/modules/config) REST API request:\n\n    [code example]\n\n| Setting | Redis<br />Software | Redis<br />Cloud | Notes |\n|:--------|:----------------------|:-----------------|:------|\n| [CACHE_SIZE](https://github.com/RedisGraph/RedisGraph/blob/master/docs/docs/configuration.md#cache_size) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> | Default: 25 |\n| [MAX_QUEUED_QUERIES](https://github.com/RedisGraph/RedisGraph/blob/master/docs/docs/configuration.md#max_queued_queries) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> | Default: 25 |\n| [NODE_CREATION_BUFFER](https://github.com/RedisGraph/RedisGraph/blob/master/docs/docs/configuration.md#node_creation_buffer) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> | Default: 16384 |\n| [OMP_THREAD_COUNT](https://github.com/RedisGraph/RedisGraph/blob/master/docs/docs/configuration.md#omp_thread_count) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual\\*</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> | \\* Updates automatically when you change your plan.<br /><br />Redis Software default: Set by plan<br /><br />Redis Cloud defaults:<br />• Flexible & Annual: Set by plan<br />• Free & Fixed: 1<br /> |\n| [QUERY_MEM_CAPACITY](https://github.com/RedisGraph/RedisGraph/blob/master/docs/docs/configuration.md#query_mem_capacity) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> | Default: 100000000 |\n| [RESULTSET_SIZE](https://github.com/RedisGraph/RedisGraph/blob/master/docs/docs/configuration.md#resultset_size) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> | Default: 10000 |\n| [THREAD_COUNT](https://github.com/RedisGraph/RedisGraph/blob/master/docs/docs/configuration.md#thread_count) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual\\*</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> | \\* Updates automatically when you change your plan.<br /><br />Redis Software default: Set by plan<br /><br />Redis Cloud defaults:<br />• Flexible & Annual: Set by plan<br />• Free & Fixed: 1<br /> |\n| [TIMEOUT](https://github.com/RedisGraph/RedisGraph/blob/master/docs/docs/configuration.md#timeout) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> | Default: 100 |\n| [VKEY_MAX_ENTITY_COUNT](https://github.com/RedisGraph/RedisGraph/blob/master/docs/docs/configuration.md#vkey_max_entity_count) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Not supported\"><nobr>&#x274c; Free & Fixed</nobr></span> | Default: 100000 |"}],"examples":[{"id":"content-ex0","language":"sh","code":"$ rladmin tune db db:<ID|name> module_name graph \\\n        module_config_params \"setting-name setting-value\"","section_id":"content"},{"id":"content-ex1","language":"sh","code":"POST /v1/modules/config/bdb/<ID>\n    {\n      \"modules\": [\n        {\n          \"module_name\": \"graph\",\n          \"module_args\": \"setting-name setting-value\"\n        }\n      ]\n    }","section_id":"content"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/graph/graph-quickstart","title":"Graph quick start","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/graph/graph-quickstart/","summary":"Graph quick start","tags":["docs","operate","stack"],"last_updated":"2026-10-06T08:53:14-07:00","page_type":"content","content_hash":"1e8c54f15b57f318ca1fd190fa2860c1b8b174aee764ed2bb07e6fb2e63413b8","sections":[{"id":"prerequisites","title":"Prerequisites","role":"overview","text":"For this quick start tutorial, you need:\n\n- A Redis database with the RedisGraph module enabled. You can use either:\n\n    - A [Redis Cloud](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/stack-quickstart) database\n\n    - A [Redis Software](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/add-module-to-database) database\n\n- [`redis-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/redis-cli) command-line tool\n\n- [`redis-py`](https://github.com/redis/redis-py) client library v4.1.0 or later"},{"id":"graph-with-redis-cli","title":"Graph with `redis-cli`","role":"content","text":"To begin, [connect to your database](https://redis.io/docs/latest/operate/rs/references/cli-utilities/redis-cli#connect-to-a-database) with `redis-cli`."},{"id":"create-a-graph","title":"Create a graph","role":"content","text":"When you create a graph, you can define nodes and the relationships between them with this format:\n\n`(:<node 1>)-[:<relationship>]->(:<node 2>)`\n\nTo define multiple nodes and relationships in a single creation query, separate the entries with commas.\n\nFor example, use the [`CREATE`](https://github.com/RedisGraph/RedisGraph/blob/master/docs/commands/graph.query.md#create) query to create a new graph of motorcycle riders and teams participating in the MotoGP league:\n\n[code example]"},{"id":"add-nodes","title":"Add nodes","role":"content","text":"You can add new nodes to a previously created graph:\n\n[code example]"},{"id":"add-relationships","title":"Add relationships","role":"content","text":"To create new relationships between nodes of a graph:\n\n[code example]"},{"id":"query-the-graph","title":"Query the graph","role":"content","text":"After you create a graph, you can use the [`GRAPH.QUERY`](https://github.com/RedisGraph/RedisGraph/blob/master/docs/commands/graph.query.md) command to query the graph's data.\n\nThe following example returns which motorcycle riders compete for team Yamaha:\n\n[code example]\n\nYou can also use [functions](https://github.com/RedisGraph/RedisGraph/blob/master/docs/commands/graph.query.md#functions) to create more complex queries.\n\nFor example, you can use the `count` function to check how many riders represent team Honda:\n\n[code example]"},{"id":"delete-nodes","title":"Delete nodes","role":"content","text":"You can use the [`DELETE`](https://github.com/RedisGraph/RedisGraph/blob/master/docs/commands/graph.query.md#delete) query to remove a specific node and its relationships from the graph:\n\n[code example]"},{"id":"delete-relationships","title":"Delete relationships","role":"content","text":"You can also use the [`DELETE`](https://github.com/RedisGraph/RedisGraph/blob/master/docs/commands/graph.query.md#delete) query to delete a node's relationships without removing any nodes:\n\n[code example]"},{"id":"delete-a-graph","title":"Delete a graph","role":"content","text":"To delete an entire graph, including all nodes and relationships, run the [`GRAPH.DELETE`](https://github.com/RedisGraph/RedisGraph/blob/master/docs/commands/graph.delete.md) command:\n\n[code example]"},{"id":"graph-with-python","title":"Graph with Python","role":"content","text":"If you want to use graphs within an application, you can use one of these [client libraries](https://github.com/RedisGraph/RedisGraph/blob/master/docs/docs/clients.md).\n\nThe following example uses the Redis Python client library [redis-py](https://github.com/redis/redis-py), which supports graph commands as of v4.1.0.\n\nThis Python code creates a graph that represents friendships between users on a social media website. It also shows how to run queries and change relationships between users.\n\n[code example]\n\nExample output:\n\n[code example]"},{"id":"visualize-graphs-with-redis-insight","title":"Visualize graphs with Redis Insight","role":"content","text":"You can use the [Redis Insight](https://redis.io/docs/latest/develop/tools/insight) workbench to visualize the relationships between the nodes of your graph.\n\n1. Connect to your database with Redis Insight. You can [connect manually](https://redis.io/docs/latest/develop/tools/insight#add-a-standalone-redis-database) or use the [auto-discovery](https://redis.io/docs/latest/develop/tools/insight#auto-discovery-for-redis-cloud-databases) feature.\n\n1. Select the **Workbench** button.\n\n1. Enter a graph query in the text editor.\n\n   For example, this query returns all nodes and relationships in the graph:\n\n   [code example]\n\n1. Select **Run** to run the query.\n\nAfter you run a query, the output log displays a visual representation of your graph's nodes and relationships:\n\n![Visualize a graph with Redis Insight workbench.](https://redis.io/docs/latest/images/modules/visualize-graph.png)"},{"id":"more-info","title":"More info","role":"content","text":"- [Graph commands](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/graph/commands)\n- [RedisGraph client libraries](https://github.com/RedisGraph/RedisGraph/blob/master/docs/docs/clients.md)\n- [RedisGraph documentation on GitHub](https://github.com/RedisGraph/RedisGraph/tree/master/docs/docs)"}],"examples":[{"id":"create-a-graph-ex0","language":"sh","code":"127.0.0.1:12543> GRAPH.QUERY MotoGP \"CREATE (:Rider {name:'Valentino Rossi'})-[:rides]->(:Team {name:'Yamaha'}), (:Rider {name:'Dani Pedrosa'})-[:rides]->(:Team {name:'Honda'}), (:Rider {name:'Andrea Dovizioso'})-[:rides]->(:Team {name:'Ducati'})\"\n1) 1) \"Labels added: 2\"\n   2) \"Nodes created: 6\"\n   3) \"Properties set: 6\"\n   4) \"Relationships created: 3\"\n   5) \"Cached execution: 0\"\n   6) \"Query internal execution time: 0.385472 milliseconds\"","section_id":"create-a-graph"},{"id":"add-nodes-ex0","language":"sh","code":"127.0.0.1:12543> GRAPH.QUERY MotoGP \"CREATE (:Rider {name:'Jorge Lorenzo'})\"\n1) 1) \"Nodes created: 1\"\n   2) \"Properties set: 1\"\n   3) \"Cached execution: 0\"\n   4) \"Query internal execution time: 0.185841 milliseconds\"","section_id":"add-nodes"},{"id":"add-relationships-ex0","language":"sh","code":"127.0.0.1:12543> GRAPH.QUERY MotoGP \"MATCH (r:Rider), (t:Team) WHERE r.name = 'Jorge Lorenzo' and t.name = 'Honda' CREATE (r)-[:rides]->(t)\"\n1) 1) \"Relationships created: 1\"\n   2) \"Cached execution: 0\"\n   3) \"Query internal execution time: 0.356578 milliseconds\"","section_id":"add-relationships"},{"id":"query-the-graph-ex0","language":"sh","code":"127.0.0.1:12543> GRAPH.QUERY MotoGP \"MATCH (r:Rider)-[:rides]->(t:Team) WHERE t.name = 'Yamaha' RETURN r,t\"\n1) 1) \"r\"\n   2) \"t\"\n2) 1) 1) 1) 1) \"id\"\n            2) \"0\"\n         2) 1) \"labels\"\n            2) 1) \"Rider\"\n         3) 1) \"properties\"\n            2) 1) 1) \"name\"\n                  2) \"Valentino Rossi\"\n      2) 1) 1) \"id\"\n            2) \"1\"\n         2) 1) \"labels\"\n            2) 1) \"Team\"\n         3) 1) \"properties\"\n            2) 1) 1) \"name\"\n                  2) \"Yamaha\"\n3) 1) \"Cached execution: 0\"\n   2) \"Query internal execution time: 0.500535 milliseconds\"","section_id":"query-the-graph"},{"id":"query-the-graph-ex1","language":"sh","code":"127.0.0.1:12543> GRAPH.QUERY MotoGP \"MATCH (r:Rider)-[:rides]->(t:Team {name:'Honda'}) RETURN count(r)\"\n1) 1) \"count(r)\"\n2) 1) 1) \"2\"\n3) 1) \"Cached execution: 0\"\n   2) \"Query internal execution time: 0.445760 milliseconds\"","section_id":"query-the-graph"},{"id":"delete-nodes-ex0","language":"sh","code":"127.0.0.1:12543> GRAPH.QUERY MotoGP \"MATCH (r:Rider {name: 'Dani Pedrosa'}) DELETE r\"\n1) 1) \"Nodes deleted: 1\"\n   2) \"Relationships deleted: 1\"\n   3) \"Cached execution: 0\"\n   4) \"Query internal execution time: 0.276815 milliseconds\"","section_id":"delete-nodes"},{"id":"delete-relationships-ex0","language":"sh","code":"127.0.0.1:12543> GRAPH.QUERY MotoGP \"MATCH (:Rider {name: 'Valentino Rossi'})-[r:rides]->() DELETE r\"\n1) 1) \"Relationships deleted: 1\"\n   2) \"Cached execution: 0\"\n   3) \"Query internal execution time: 0.348346 milliseconds\"","section_id":"delete-relationships"},{"id":"delete-a-graph-ex0","language":"sh","code":"127.0.0.1:12543> GRAPH.DELETE MotoGP\n\"Graph removed, internal execution time: 0.013138 milliseconds\"","section_id":"delete-a-graph"},{"id":"graph-with-python-ex0","language":"python","code":"import redis\nfrom redis.commands.graph.edge import Edge\nfrom redis.commands.graph.node import Node\n\n# Connect to a database\nr = redis.Redis(host=\"<endpoint>\", port=\"<port>\", \n                password=\"<password>\")\n\n# Create nodes that represent users\nusers = { \"Alex\": Node(label=\"Person\", properties={\"name\": \"Alex\", \"age\": 35}),\n          \"Jun\": Node(label=\"Person\", properties={\"name\": \"Jun\", \"age\": 33}),\n          \"Taylor\": Node(label=\"Person\", properties={\"name\": \"Taylor\", \"age\": 28}),\n          \"Noor\": Node(label=\"Person\", properties={\"name\": \"Noor\", \"age\": 41}) }\n\n# Define a graph called SocialMedia\nsocial_graph = r.graph(\"SocialMedia\")\n\n# Add users to the graph as nodes\nfor key in users.keys():\n    social_graph.add_node(users[key])\n\n# Add relationships between user nodes\nsocial_graph.add_edge( Edge(users[\"Alex\"], \"friends\", users[\"Jun\"]) )\n# Make the relationship bidirectional\nsocial_graph.add_edge( Edge(users[\"Jun\"], \"friends\", users[\"Alex\"]) )\n\nsocial_graph.add_edge( Edge(users[\"Jun\"], \"friends\", users[\"Taylor\"]) )\nsocial_graph.add_edge( Edge(users[\"Taylor\"], \"friends\", users[\"Jun\"]) )\n\nsocial_graph.add_edge( Edge(users[\"Jun\"], \"friends\", users[\"Noor\"]) )\nsocial_graph.add_edge( Edge(users[\"Noor\"], \"friends\", users[\"Jun\"]) )\n\nsocial_graph.add_edge( Edge(users[\"Alex\"], \"friends\", users[\"Noor\"]) )\nsocial_graph.add_edge( Edge(users[\"Noor\"], \"friends\", users[\"Alex\"]) )\n\n# Create the graph in the database\nsocial_graph.commit()\n\n# Query the graph to find out how many friends Alex has\nresult1 = social_graph.query(\"MATCH (p1:Person {name: 'Alex'})-[:friends]->(p2:Person) RETURN count(p2)\")\nprint(\"Alex's original friend count:\", result1.result_set)\n\n# Delete a relationship without deleting any user nodes\nsocial_graph.query(\"MATCH (:Person {name: 'Alex'})<-[f:friends]->(:Person {name: 'Jun'}) DELETE f\")\n\n# Query the graph again to see Alex's updated friend count\nresult2 = social_graph.query(\"MATCH (p1:Person {name: 'Alex'})-[:friends]->(p2:Person) RETURN count(p2)\")\nprint(\"Alex's updated friend count:\", result2.result_set)\n\n# Delete the entire graph\nsocial_graph.delete()","section_id":"graph-with-python"},{"id":"graph-with-python-ex1","language":"text","code":"$ ./quick_start.py\nAlex's original friend count: [[2]]\nAlex's updated friend count: [[1]]","section_id":"graph-with-python"},{"id":"visualize-graphs-with-redis-insight-ex0","language":"sh","code":"GRAPH.QUERY MotoGP \"MATCH (n) RETURN n\"","section_id":"visualize-graphs-with-redis-insight"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/graph","title":"Graph","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/graph/","summary":"RedisGraph is a queryable graph database built on Redis.","tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:38+01:00","children":[{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/graph/graph-quickstart","summary":"Graph quick start","title":"Graph quick start","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/graph/graph-quickstart/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/graph/commands","summary":"Lists graph commands and provides links to the command reference pages.","title":"Graph commands","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/graph/commands/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/graph/config","summary":"Graph configuration settings supported by Redis Software.","title":"Graph configuration compatibility with Redis Software","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/graph/config/"}],"page_type":"content","content_hash":"e64b87a76ae4f97682c380fb20701943bceb4c9b3babad54c86c999a1897e518","sections":[{"id":"overview","title":"Overview","role":"overview","text":"RedisGraph is the first queryable [Property Graph](https://github.com/opencypher/openCypher/blob/master/docs/property-graph-model.adoc) database to use [sparse matrices](https://en.wikipedia.org/wiki/Sparse_matrix) to represent the [adjacency matrix](https://en.wikipedia.org/wiki/Adjacency_matrix) in graphs and [linear algebra](http://faculty.cse.tamu.edu/davis/GraphBLAS.html) to query the graph."},{"id":"primary-features","title":"Primary features","role":"content","text":"- Based on the [Property Graph Model](https://github.com/opencypher/openCypher/blob/master/docs/property-graph-model.adoc)\n    - Nodes (vertices) and Relationships (edges) that may have attributes\n    - Nodes that can be labeled\n    - Relationships have a relationship type\n- Graphs represented as sparse adjacency matrices\n- [Cypher](http://www.opencypher.org/) as query language\n    - Cypher queries translated into linear algebra expressions"},{"id":"commands","title":"Commands","role":"syntax","text":"See [Graph commands](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/graph/commands) for a list of graph commands that are supported in Redis Software."},{"id":"clients","title":"Clients","role":"content","text":"For a list of available RedisGraph clients, see [RedisGraph client libraries](https://github.com/RedisGraph/RedisGraph/blob/master/docs/docs/clients.md)."},{"id":"configuration","title":"Configuration","role":"configuration","text":"See [Configuration parameters](https://github.com/RedisGraph/RedisGraph/blob/master/docs/docs/configuration.md) for the complete list of RedisGraph configuration parameters.\n\nTo learn which configuration parameters are supported in Redis Software, see [Graph configuration compatibility with Redis Software](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/graph/config)."},{"id":"design","title":"Design","role":"content","text":"For an overview of RedisGraph's design, see [RedisGraph: A High Performance In-Memory Graph Database](https://github.com/RedisGraph/RedisGraph/blob/master/docs/docs/design/_index.md)."},{"id":"client-specification","title":"Client specification","role":"content","text":"RedisGraph client libraries should follow the [Technical specification for writing RedisGraph client libraries](https://github.com/RedisGraph/RedisGraph/blob/master/docs/docs/design/client_spec.md)."},{"id":"result-set-structure","title":"Result set structure","role":"returns","text":"[RedisGraph result set structure](https://github.com/RedisGraph/RedisGraph/blob/master/docs/docs/design/result_structure.md) describes the format RedisGraph uses to print data when using `redis-cli`."},{"id":"graphbulk-endpoint","title":"GRAPH.BULK endpoint","role":"content","text":"You can import new graphs from CSV files using `redisgraph-bulk-loader`.\n\nSee the [`GRAPH.BULK` implementation details](https://github.com/RedisGraph/RedisGraph/blob/master/docs/docs/design/bulk_spec.md) for more information."},{"id":"data-types","title":"Data types","role":"content","text":"RedisGraph supports a number of distinct data types, including:\n\n- Graph types:\n    - Nodes\n    - Relationships\n    - Paths\n\n- Scalar types:\n    - Strings\n    - Booleans\n    - Integers\n    - Floating-point values\n    - Geospatial points\n    - Null\n  \n- Collection types:\n    - Arrays\n    - Maps\n\nSee [RedisGraph data types](https://github.com/RedisGraph/RedisGraph/blob/master/docs/docs/datatypes.md) for details."},{"id":"cypher-coverage","title":"Cypher coverage","role":"content","text":"RedisGraph implements a subset of the Cypher language. See [Cypher coverage](https://github.com/RedisGraph/RedisGraph/blob/master/docs/docs/cypher_support.md) for more information."},{"id":"path-algorithms","title":"Path algorithms","role":"content","text":"To learn how to find single-pair and single-source paths using `algo.SPpaths` and `algo.SSpaths`, see [Path algorithms](https://github.com/RedisGraph/RedisGraph/blob/master/docs/docs/path_algorithm.md)."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"RedisGraph has several [known limitations](https://github.com/RedisGraph/RedisGraph/blob/master/docs/docs/known_limitations.md)."},{"id":"more-info","title":"More info","role":"content","text":"- [Graph quick start](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/graph/graph-quickstart)\n\n- [RedisGraph documentation on GitHub](https://github.com/RedisGraph/RedisGraph/tree/master/docs/docs)\n\n- [References](https://github.com/RedisGraph/RedisGraph/blob/master/docs/docs/References.md)\n\n- [RedisGraph source](https://github.com/RedisGraph/RedisGraph/)"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features","title":"Deprecated Redis Open Source features and modules","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/","summary":"Features (modules) that have been deprecated or replaced with a new version.","tags":["docs","operate","stack"],"last_updated":"2025-05-01T06:50:58-07:00","children":[{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions","summary":"Trigger and execute JavaScript functions in the Redis process","title":"Triggers and functions","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/graph","summary":"RedisGraph is a queryable graph database built on Redis.","title":"Graph","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/graph/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1","summary":"RedisGears supports batch and event-driven processing for Redis data.","title":"Gears","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/"}],"page_type":"content","content_hash":"c3e354e4e8f6a1f6076b8acfa9fbb482eb0dcc075591ca5a418cfd37fe5a10c8","sections":[{"id":"content","title":"Content","role":"content","text":"The following features (modules) have been deprecated or replaced with a new version:\n\n\n| Feature | Description |\n| --- | --- |\n| [Triggers and functions](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/) | Trigger and execute JavaScript functions in the Redis process |\n| [Graph](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/graph/) | RedisGraph is a queryable graph database built on Redis. |\n| [Gears](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/) | RedisGears supports batch and event-driven processing for Redis data. |"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/Binary_Data","title":"Binary data","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/binary_data/","summary":"Working with binary data","aliases":["/develop/interact/programmability/triggers-and-functions/concepts/binary_data/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-21T16:36:38+01:00","page_type":"content","content_hash":"2950cf5037ff406c529144fc683fac987c063810e2089b542859aa9f7251dd8e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"By default, triggers and functions will decode all data as a string and will raise error on failures. Though useful for most users, sometimes there is a need to work with binary data. In order to do so, the library developer has to consider the following:\n\n1. Binary function arguments\n2. Binary command results\n3. Binary key names on [keyspace triggers](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/triggers/KeySpace_Triggers)\n4. Binary data on [stream triggers](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/triggers/Stream_Triggers)"},{"id":"binary-function-arguments","title":"Binary function arguments","role":"content","text":"It is possible to instruct triggers and functions not to decode function arguments as `JS` `Strings` using the [redis.functionFlags.RAW_ARGUMENTS](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/Function_Flags) function flag. In this case, the function arguments will be given as `JS` `ArrayBuffer`. Example:\n\n[code example]\n\nThe above example will allow us to set `key` and `val` even if those are binary data. Run example:\n\n[code example]\n\nNotice that the `call` function also accepts `JS` `ArrayBuffer` arguments."},{"id":"binary-command-results","title":"Binary command results","role":"content","text":"Getting function arguments as binary data is not enough. We might want to read binary data from a Redis key. In order to do this we can use the `callRaw` function, which will not decode the result as a `JS` `String` and instead will return the result as a `JS` `ArrayBuffer`. Example:\n\n[code example]\n\nThe above example will be able to fetch binary data and return it to the user. For example:\n\n[code example]\n\nNotice that a `JS` `ArrayBuffer` can be returned by a function, it will be returned to the client as `bulk string`."},{"id":"binary-keys-names-on-database-triggers","title":"Binary keys names on database triggers","role":"content","text":"On [keyspace triggers](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/triggers/KeySpace_Triggers), if the key name that triggered the event is binary, the `data.key` field will be NULL. The `data.key_raw` field is always provided as a `JS` `ArrayBuffer` and can be used as in the following example:\n\n[code example]\n\nRun example:\n\n[code example]\n\nFor more information see [keyspace triggers](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/triggers/KeySpace_Triggers)."},{"id":"binary-data-on-stream-consumers","title":"Binary data on stream consumers","role":"content","text":"On [stream triggers](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/triggers/Stream_Triggers), if the key name is binary. The `data.stream_name` field will be NULL. The `data.stream_name_raw` field is always provided as a `JS` `ArrayBuffer` and can be used in this case. In addition, if the content of the steam is binary, it will also appear as `null` under `data.record`. In this case, it is possible to use `data.record` (which always exists) and contains the data as a `JS` `ArrayBuffer`. Example:\n\n[code example]\n\nRun Example:\n\n[code example]"}],"examples":[{"id":"binary-function-arguments-ex0","language":"js","code":"#!js api_version=1.0 name=lib\nredis.registerFunction(\"my_set\",\n    (c, key, val) => {\n        return c.call(\"set\", key, val);\n    },\n    {\n        flags: [redis.functionFlags.RAW_ARGUMENTS]\n    }\n);","section_id":"binary-function-arguments"},{"id":"binary-function-arguments-ex1","language":"bash","code":"127.0.0.1:6379> TFCALL lib.my_set 1 \"\\xaa\" \"\\xaa\"\n\"OK\"\n127.0.0.1:6379> get \"\\xaa\"\n\"\\xaa\"","section_id":"binary-function-arguments"},{"id":"binary-command-results-ex0","language":"js","code":"#!js api_version=1.0 name=lib\nredis.registerFunction(\"my_get\", \n    (c, key) => {\n        return c.callRaw(\"get\", key);\n    },\n    {\n        flags: [redis.functionFalgs.RAW_ARGUMENTS]\n    }\n);","section_id":"binary-command-results"},{"id":"binary-command-results-ex1","language":"bash","code":"27.0.0.1:6379> set \"\\xaa\" \"\\xaa\"\nOK\n127.0.0.1:6379> TFCALL lib.my_get 1 \"\\xaa\"\n\"\\xaa\"","section_id":"binary-command-results"},{"id":"binary-keys-names-on-database-triggers-ex0","language":"js","code":"#!js api_version=1.0 name=lib\n/* The following is just an example, in general it is discourage to use globals. */\nvar n_notifications = 0;\nvar last_key = null;\nvar last_key_raw = null;\nredis.registerKeySpaceTrigger(\"consumer\", \"\", function(client, data) {\n    if (data.event == \"set\") {\n        n_notifications += 1;\n        last_data = data.key;\n        last_key_raw = data.key_raw;\n    }\n});\n\nredis.registerFunction(\"notifications_stats\", async function(){\n    return [\n        n_notifications,\n        last_key,\n        last_key_raw\n    ];\n});","section_id":"binary-keys-names-on-database-triggers"},{"id":"binary-keys-names-on-database-triggers-ex1","language":"bash","code":"127.0.0.1:6379> set \"\\xaa\" \"\\xaa\"\nOK\n127.0.0.1:6379> TFCALL lib.notifications_stats 0\n1) (integer) 1\n2) (nil)\n3) \"\\xaa\"","section_id":"binary-keys-names-on-database-triggers"},{"id":"binary-data-on-stream-consumers-ex0","language":"js","code":"#!js api_version=1.0 name=lib\n/* The following is just an example, in general it is discourage to use globals. */\nvar last_key = null;\nvar last_key_raw = null;\nvar last_data = null;\nvar last_data_raw = null;\nredis.registerFunction(\"stats\", function(){\n    return [\n        last_key,\n        last_key_raw,\n        last_data,\n        last_data_raw\n    ];\n})\nredis.registerStreamTrigger(\"consumer\", new Uint8Array([255]).buffer, function(c, data){\n    last_key = data.stream_name;\n    last_key_raw = data.stream_name_raw;\n    last_data = data.record;\n    last_data_raw = data.record_raw;\n})","section_id":"binary-data-on-stream-consumers"},{"id":"binary-data-on-stream-consumers-ex1","language":"bash","code":"127.0.0.1:6379> xadd \"\\xff\\xff\" * \"\\xaa\" \"\\xaa\"\n\"1659515146671-0\"\n127.0.0.1:6379> TFCALL foo.stats 0\n1) (nil)\n2) \"\\xff\\xff\"\n3) 1) 1) (nil)\n      2) (nil)\n4) 1) 1) \"\\xaa\"\n      2) \"\\xaa\"","section_id":"binary-data-on-stream-consumers"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/Cluster_Support","title":"Cluster support","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/cluster_support/","summary":"Cluster support for triggers and functions","aliases":["/develop/interact/programmability/triggers-and-functions/concepts/cluster_support/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-21T16:36:38+01:00","page_type":"content","content_hash":"083c6a3d8335de5acff65e6bd5d023b4ac10b549fdf812d51c6c862b8bad7a88","sections":[{"id":"overview","title":"Overview","role":"overview","text":"**Notice**: On a Redis Open Source cluster, before executing any gears function, you must send a `REDISGEARS_2.REFRESHCLUSTER` command to all the shards so that they will be aware of the cluster topology. Without this step, each shard will act as a single Redis Open Source instance.\n\nTriggers and functions support cross-shard operation on Redis clusters. This means that it is possible to call a function that will be invoked on another shard. We call such a function a remote function.\n\nJust like local functions, remote function must be declared on library load time using `redis.registerClusterFunction` API. The following example declares a remote function that returns the number of keys on the shard:\n\n[code example]\n\n`redis.registerClusterFunction` is passed the remote function name, which will be used later to call the remote function, and the remote function code. The remote function must be a Coroutine (async function) and it is executed in the background on the remote shard. For more information about async function, please refer to [sync and async](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/Sync_Async) page.\n\nWe have couple of options for calling a remote function. These options are exposed through the async client that is given to a Coroutine:\n\n* `async_client.runOnShards` - run the remote function on all the shards (including the current shard). Returns a promise that, once resolved, will give two nested arrays, the first contains another array with the results from all the shards and the other contains an array of errors (`[[res1, res2, ...],[err1, err2, ..]]`).\n* `async_client.runOnKey` - run the remote function on the shard responsible for a given key. Returns a promise that, once resolved, will give the result from the remote function execution or raise an exception in the case of an error.\n\nThe following example registers a function that will return the total number of keys on the cluster. The function will use the remote function defined above:\n\n[code example]\n\nFirst, the function executes a remote function on all shards that returns the number of keys on each shard, then the function adds all the results and returns total number of keys for all shards.\n\nHere's the complete example:\n\n[code example]"},{"id":"arguments-and-results-serialization","title":"Arguments and results serialization","role":"parameters","text":"It is possible to pass arguments to the remote function. The arguments will be given to the remote function after the `async_client`. The following example shows how to get a value of a key from any shard in the cluster:\n\n[code example]\n\nThe function `test` will return the value of `x` regardless of which shard the function was executed on.\n\nThe remote function arguments and results are serialized in the following way:\n\n1. If the argument is of type `ArrayBuffer`, the data will be sent as is.\n2. Otherwise, RedisGears will try to serialize the give arguments (or the return value) as JSON using `JSON.stringify`. A serialization failure will cause an error to be raised."},{"id":"execution-timeout","title":"Execution timeout","role":"content","text":"Remote functions will not be permitted to run forever and will timeout. The timeout period can be configured using [remote-task-default-timeout](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/Configuration#remote-task-default-timeout).\nWhen using `async_client.runOnShards` API, the timeout will be added as error to the error array. When using `async_client.runOnKey`, a timeout will cause an exception to be raised."},{"id":"remote-function-limitations","title":"Remote function limitations","role":"content","text":"All the limitations listed on [coroutines](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/Sync_Async) also apply to remote functions. Remote function also come with some extra limitations:\n\n* Remote functions can only perform read operations. An attempt to perform a write operation will result in an error.\n* Remote function are not guaranteed to succeed (if the shard crashed for example). In such cases a timeout error will be given."}],"examples":[{"id":"overview-ex0","language":"js","code":"redis.registerClusterFunction(\"dbsize\", async(async_client) => {\n    return client.block((async_client) => {\n        return client.call(\"dbsize\").toString();\n    });\n});","section_id":"overview"},{"id":"overview-ex1","language":"js","code":"redis.registerAsyncFunction(\"my_dbsize\", async(async_client) => {\n    let res = await async_client.runOnShards(\"dbsize\");\n    let results = res[0];\n    let errors = res[1];\n    if (errors.length > 0) {\n        return errors;\n    }\n    let sum = BigInt(0);\n    results.forEach((element) => sum+=BigInt(element));\n    return sum;\n});","section_id":"overview"},{"id":"overview-ex2","language":"js","code":"#!js name=lib api_version=1.0\n\nredis.registerClusterFunction(\"dbsize\", async(client) => {\n    return client.block((client) => {\n        return client.call(\"dbsize\").toString();\n    });\n});\n\nredis.registerAsyncFunction(\"test\", async(async_client) => {\n    let res = await async_client.runOnShards(\"dbsize\");\n    let results = res[0];\n    let errors = res[1];\n    if (errors.length > 0) {\n        return errors;\n    }\n    let sum = BigInt(0);\n    results.forEach((element) => sum+=BigInt(element));\n    return sum;\n});","section_id":"overview"},{"id":"arguments-and-results-serialization-ex0","language":"js","code":"#!js name=lib api_version=1.0\nconst remote_get = \"remote_get\";\n\nredis.registerClusterFunction(remote_get, async(client, key) => {\n    let res = client.block((client) => {\n        return client.call(\"get\", key);\n    });\n    return res;\n});\n\nredis.registerAsyncFunction(\"test\", async (async_client) => {\n    return await async_client.runOnKey(\"x\", remote_get, \"x\");\n});","section_id":"arguments-and-results-serialization"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/Function_Flags","title":"Function flags","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/function_flags/","summary":"Function flags for JavaScript functions","aliases":["/develop/interact/programmability/triggers-and-functions/concepts/function_flags/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2024-06-06T06:55:24-07:00","page_type":"content","content_hash":"a1f299fb4472da80683e36b73a9ac087d0850af31fa566fbd464664626af5c90","sections":[{"id":"content","title":"Content","role":"content","text":"When registering a function, it is possible to include additional information about its behavior. This information is known as function flags. Function flags are an optional argument that can be specified after the function implementation. The following flags are supported:\n1. `redis.functionFlags.NO_WRITES`: This flag indicates that the function does not perform any write commands. Enabling this flag allows a function to be executed on read-only replicas or in out-of-memory (OOM) situations. Redis enforces this flag's behavior, meaning that any attempt to call a write command within a function that has this flag set will result in an exception.\n2. `redis.functionFlags.ALLOW_OOM`: By default, Redis prevents any function from running in an OOM scenario. However, this flag allows overriding this behavior and running a function even when there is a memory shortage. Enabling this flag is considered unsafe and may cause Redis to exceed the `maxmemory` limit. **Users should only enable this flag if they are certain that their function does not consume additional memory.** For example, it is safe to run a function that only deletes data during an OOM situation.\n3. `redis.functionFlags.RAW_ARGUMENTS`: By default, Redis attempts to decode all function arguments as `JS` `String`s. If the decoding fails, an error is returned to the client. However, when this flag is set, Redis avoids string decoding and passes the argument as a `JS` `ArrayBuffer` instead.\n\nThe following example shows how to set the `redis.functionFlags.NO_WRITES` flag:\n\n[code example]\n\nRun example:\n\n[code example]"}],"examples":[{"id":"content-ex0","language":"js","code":"#!js api_version=1.0 name=lib\n\nredis.registerFunction('my_ping',\n    function(client){\n        return client.call('ping');\n    },\n    {\n        flags: [redis.functionFlags.NO_WRITES]\n    }\n);","section_id":"content"},{"id":"content-ex1","language":"bash","code":"127.0.0.1:6379> TFCALL lib.my_ping 0\n\"PONG\"\n127.0.0.1:6379> config set maxmemory 1\nOK\n127.0.0.1:6379> TFCALL lib.my_ping 0\n\"PONG\"","section_id":"content"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts","title":"Concepts","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/","summary":"Details about triggers, the JavaScript API, and advanced concepts","aliases":["/develop/interact/programmability/triggers-and-functions/concepts/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2024-06-06T06:55:24-07:00","children":[{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/triggers","summary":"Different ways of executing JavaScript functions","title":"Triggers","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/triggers/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/JavaScript_API","summary":"Overview of the JavaScript API","title":"JavaScript API","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/javascript_api/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/Sync_Async","summary":"Sync and async functions","title":"Sync and async","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/sync_async/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/Cluster_Support","summary":"Cluster support for triggers and functions","title":"Cluster support","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/cluster_support/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/Function_Flags","summary":"Function flags for JavaScript functions","title":"Function flags","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/function_flags/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/Library_Configuration","summary":"How to use configuration in JavaScript functions","title":"Library configuration","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/library_configuration/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/RESP_JS_Conversion","summary":"Converting RESP to and from JavaScript","title":"RESP & JavaScript","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/resp_js_conversion/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/Binary_Data","summary":"Working with binary data","title":"Binary data","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/binary_data/"}],"page_type":"index","sections":[],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/JavaScript_API","title":"JavaScript API","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/javascript_api/","summary":"Overview of the JavaScript API","aliases":["/develop/interact/programmability/triggers-and-functions/concepts/javascript_api/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2024-06-06T06:55:24-07:00","page_type":"content","content_hash":"a19518b06c75f5fcc1b51baad9d25f475fdef0340bf930bc9dcf6db294cba140","sections":[{"id":"redis-object","title":"Redis object","role":"overview","text":"* Since version: 2.0.0\n\nThe triggers and functions JavaScript API provides a singleton instance of an object named *redis*. The *redis* instance enables registered functions to interact with the Redis server on which they are running. Following is the API provided by the *redis* instance."},{"id":"redisregisterfunction","title":"`redis.registerFunction`","role":"content","text":"* Since version: 2.0.0\n\nRegister a new function that can later be invoke using `TFCALL` command.\n\n[code example]"},{"id":"redisregisterasyncfunction","title":"`redis.registerAsyncFunction`","role":"content","text":"* Since version: 2.0.0\n\nRegister a new async function that can later be invoke using `TFCALLASYNC` command.\n\n[code example]"},{"id":"redisregisterkeyspacetrigger","title":"`redis.registerKeySpaceTrigger`","role":"content","text":"* Since version: 2.0.0\n\nRegister a key space notification trigger that will run whenever a key space notification fired.\n\n[code example]"},{"id":"redisregisterstreamtrigger","title":"`redis.registerStreamTrigger`","role":"content","text":"* Since version: 2.0.0\n\nRegister a stream trigger that will be invoke whenever a data is added to a stream.\n\n[code example]"},{"id":"redisregisterclusterfunction","title":"`redis.registerClusterFunction`","role":"content","text":"* Since version: 2.0.0\n\nRegister a cluster function that can later be called using `async_client.runOnKey()` or `async_client.runOnShards()`\n\n[code example]"},{"id":"redisconfig","title":"`redis.config`","role":"content","text":"* Since version: 2.0.0\n\n[code example]\n\n[code example]"},{"id":"client-object","title":"Client object","role":"content","text":"Client object that is used to perform operations on Redis."},{"id":"clientcall","title":"`client.call`","role":"content","text":"* Since version: 2.0.0\n\nRun a command on Redis. The command is executed on the current Redis instrance.\n\n[code example]"},{"id":"clientcallraw","title":"`client.callRaw`","role":"content","text":"* Since version: 2.0.0\n\nRun a command on Redis but does not perform UTF8 decoding on the result. The command is executed on the current Redis instrance.\n\n[code example]"},{"id":"clientcallasync","title":"`client.callAsync`","role":"content","text":"* Since version: 2.0.0\n\nCall a command on Redis. Allow Redis to block the execution if needed (like `blpop` command) and return the result asynchronously. Returns a promise object that will be resolved when the command invocation finished.\n\n[code example]"},{"id":"clientcallasyncraw","title":"`client.callAsyncRaw`","role":"content","text":"* Since version: 2.0.0\n\nCall a command on Redis. Allow Redis to block the execution if needed (like `blpop` command) and return the result asynchronously. Returns a promise object that will be resolved when the command invocation finished. The command is executed on the current Redis instrance.\n\n[code example]"},{"id":"clientisblockallowed","title":"`client.isBlockAllowed`","role":"content","text":"* Since version: 2.0.0\n\nReturn true if it is allow to return promise from the function callback. In case it is allowed and a promise is return, Redis will wait for the promise to be fulfilled and only then will return the function result.\n\n[code example]"},{"id":"clientexecuteasync","title":"`client.executeAsync`","role":"content","text":"* Since version: 2.0.0\n\nExecute the given function asynchroniusly. Return a promise that will be fulfilled when the promise will be resolved/rejected.\n\n\n[code example]"},{"id":"async-client-object","title":"Async client object","role":"content","text":"Background client object that is used to perform background operation on Redis.\nThis client is given to any background task that runs as a JS coroutine."},{"id":"async_clientblock","title":"`async_client.block`","role":"content","text":"* Since version: 2.0.0\n\nBlocks Redis for command invocation. All the command that are executed inside the given function is considered atomic and will be wrapped with `Multi/Exec` and send to the replica/AOF.\n\n[code example]"},{"id":"async_clientrunonkey","title":"`async_client.runOnKey`","role":"content","text":"* Since version: 2.0.0\n\nRuns a remote function on a given key. If the key located on the current shard on which we currently runs on, the remote function will run write away. Otherwise the remote function will run on the remote shard. Returns a promise which will be fulfilled when the invocation finishes. Notice that the remote function must return a json serializable result so we can serialize the result back to the original shard. \n\nNotice that remote function can only perform read operations, not writes are allowed.\n\n\n[code example]"},{"id":"async_clientrunonshards","title":"`async_client.runOnShards`","role":"content","text":"* Since version: 2.0.0\n\nRuns a remote function on all the shards. Returns a promise which will be fulfilled when the invocation finishes on all the shards.\n \nThe result is array of 2 elements, the first is another array of all the results. The second is an array of all the errors happened durring the invocation. Notice that the remote function must return a json serializable result so we can serialize the result back to the original shard.\n\nNotice that remote function can only perform read operations, not writes are allowed.\n\n[code example]"}],"examples":[{"id":"redisregisterfunction-ex0","language":"JavaScript","code":"//name and callback mandatory\n//object with optional arguments\nredis.registerFunction(\n  'foo', //name\n  function(client, args) {}, //callback\n  {\n    description: 'The description',\n    flags: [redis.functionFlags.NO_WRITES, redis.functionFlags.ALLOW_OOM]\n  } // optional arguments\n);","section_id":"redisregisterfunction"},{"id":"redisregisterasyncfunction-ex0","language":"JavaScript","code":"redis.registerAsyncFunction(\n  'foo', //Function name\n  function(client, args){}, //callback\n  {\n    description: 'description',\n    flags: [redis.functionFlags.NO_WRITES, redis.functionFlags.ALLOW_OOM]\n  } //optional arguments\n);","section_id":"redisregisterasyncfunction"},{"id":"redisregisterkeyspacetrigger-ex0","language":"JavaScript","code":"redis.registerKeySpaceTrigger(\n  'foo', // trigger name\n  'keys:*', //key prefix\n  function(client, data) {}, //callback\n  {\n    description: 'description'\n    onTriggerFired: function(client, data){}\n  } //optional arguments\n)","section_id":"redisregisterkeyspacetrigger"},{"id":"redisregisterstreamtrigger-ex0","language":"JavaScript","code":"redis.registerStreamTrigger(\n  'foo', //trigger name\n  'stream:*', //prefix\n  function(client, data){},//callback\n  {\n    description: 'Description'\n    window: 1,\n    isStreamTrimmed: false \n  } //optional arguments\n)","section_id":"redisregisterstreamtrigger"},{"id":"redisregisterclusterfunction-ex0","language":"JavaScript","code":"redis.registerClusterFunction(\n  'foo', //name\n  async function(client, ...args){} //callback\n)","section_id":"redisregisterclusterfunction"},{"id":"redisconfig-ex0","language":"Shell","code":"redis-cli -x RG.FUNCTION LOAD UPGRADE CONFIG '{\"example_config\": \"example_config_value\"}' < main.js","section_id":"redisconfig"},{"id":"redisconfig-ex1","language":"JavaScript","code":"var configExample = redis.config.example_config;","section_id":"redisconfig"},{"id":"clientcall-ex0","language":"JavaScript","code":"client.call(\n  '',\n  ...args\n)","section_id":"clientcall"},{"id":"clientcallraw-ex0","language":"JavaScript","code":"client.callRaw(\n  '',\n  ...args\n)","section_id":"clientcallraw"},{"id":"clientcallasync-ex0","language":"JavaScript","code":"client.callAsync(\n  '',\n  ...args\n)","section_id":"clientcallasync"},{"id":"clientcallasyncraw-ex0","language":"JavaScript","code":"client.callRaw(\n  '',\n  ...args\n)","section_id":"clientcallasyncraw"},{"id":"clientisblockallowed-ex0","language":"JavaScript","code":"client.isBlockAllowed(); // True / False","section_id":"clientisblockallowed"},{"id":"clientexecuteasync-ex0","language":"JavaScript","code":"client.executeAsync(\n  async function(client){}\n)","section_id":"clientexecuteasync"},{"id":"async_clientblock-ex0","language":"JavaScript","code":"async_client.block(\n  function(client){}\n)","section_id":"async_clientblock"},{"id":"async_clientrunonkey-ex0","language":"JavaScript","code":"async_client.runOnKey(\n  'key1', // key\n  'foo', // function name\n  ...args\n)","section_id":"async_clientrunonkey"},{"id":"async_clientrunonshards-ex0","language":"JavaScript","code":"async_client.runOnShards(\n  'foo', //name\n  ...args //arguments\n)","section_id":"async_clientrunonshards"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/Library_Configuration","title":"Library configuration","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/library_configuration/","summary":"How to use configuration in JavaScript functions","aliases":["/develop/interact/programmability/triggers-and-functions/concepts/library_configuration/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-21T16:36:38+01:00","page_type":"content","content_hash":"cde6f262a09b44ed6d6938944e5f42a33359065172d987750a9b1eb177d5a547","sections":[{"id":"content","title":"Content","role":"content","text":"When writing a library, you may want to provide a loading configuration so that different users can use the same library with slightly different behaviour, without changing the base code. For example, assume you write a library that adds a `__last_updated__` field to a hash (you can see how it can also be done with [keyspace triggers](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/triggers/KeySpace_Triggers)), the code will look like this:\n\n[code example]\n\nRun example:\n\n[code example]\n\nThe problem with the above code is that the `__last_update__` field is hard coded. What if we want to allow the user to configure it at runtime? Triggers and functions provide for specifying a library configuration at load time using a [`CONFIG`](https://redis.io/docs/latest/commands/config) argument that is passed to the `TFUNCTION LOAD` command. The configuration argument accepts a string representation of a JSON object. The JSON will be provided to the library as a JS object under the `redis.config` variable. We can change the above example to accept the `__last_update__` field name as a library configuration. The code will look like this:\n\n[code example]\n\nNotice that in the above example we first set `last_update_field_name` to `__last_update__`, the default value in cases where a value is not provided by the configuration. Then we check if we have `last_update_field_name` in our configuration and if we do we use it. We can now load our function with a [`CONFIG`](https://redis.io/docs/latest/commands/config) argument:\n\n[code example]\n\nWe can see that the last update field name is `last_update`:\n\n[code example]\n\nNotice, triggers and functions only provides the library with the JSON configuration. **It's the library's responsibility to verify the correctness of the given configuration**."}],"examples":[{"id":"content-ex0","language":"js","code":"#!js api_version=1.0 name=lib\n\nredis.registerFunction(\"hset\", function(client, key, field, val){\n    // get the current time in ms\n    var curr_time = client.call(\"time\")[0];\n    return client.call('hset', key, field, val, \"__last_update__\", curr_time);\n});","section_id":"content"},{"id":"content-ex1","language":"bash","code":"127.0.0.1:6379> TFCALL lib.hset k a b 0\n(integer) 2\n127.0.0.1:6379> hgetall k\n1) \"foo\"\n2) \"bar\"\n3) \"__last_update__\"\n4) \"1658653125\"","section_id":"content"},{"id":"content-ex2","language":"js","code":"#!js api_version=1.0 name=lib\n\nvar last_update_field_name = \"__last_update__\"\n\nif (redis.config.last_update_field_name !== undefined) {\n    if (typeof redis.config.last_update_field_name != 'string') {\n        throw \"last_update_field_name must be a string\";\n    }\n    last_update_field_name = redis.config.last_update_field_name\n}\n\nredis.registerFunction(\"hset\", function(client, key, field, val){\n    // get the current time in ms\n    var curr_time = client.call(\"time\")[0];\n    return client.call('hset', key, field, val, last_update_field_name, curr_time);\n});","section_id":"content"},{"id":"content-ex3","language":"bash","code":"> redis-cli -x TFUNCTION LOAD REPLACE CONFIG '{\"last_update_field_name\":\"last_update\"}' < <path to code file>\nOK","section_id":"content"},{"id":"content-ex4","language":"bash","code":"127.0.0.1:6379> TFCALL lib.hset h a b 0\n(integer) 2\n127.0.0.1:6379> hgetall h\n1) \"a\"\n2) \"b\"\n3) \"last_update\"\n4) \"1658654047\"","section_id":"content"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/RESP_JS_Conversion","title":"RESP & JavaScript","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/resp_js_conversion/","summary":"Converting RESP to and from JavaScript","aliases":["/develop/interact/programmability/triggers-and-functions/concepts/resp_js_conversion/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2024-06-06T06:55:24-07:00","page_type":"content","content_hash":"4e4f7f57b78ec686574970c82b08ed29af1f8972bca6cfb0377a1a1e7650252d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"When running Redis commands from within a function using the `client.call` API, the reply is parsed as a resp3 reply and converted to a JS object using the following rules:\n\n| resp 3            | JS object type                                                                                                                                 |\n|-------------------|------------------------------------------------------------------------------------------------------------------------------------------------|\n| `status`          | `StringObject` with a field called `__reply_type` and value `status` (or error if failed to convert to utf8)                                   |\n| `bulk string`     | JS `String` (or error if failed to convert to utf8)                                                                                            |\n| `Error`           | Raise JS exception                                                                                                                             |\n| `long`            | JS big integer                                                                                                                                 |\n| `double`          | JS number                                                                                                                                      |\n| `array`           | JS array                                                                                                                                       |\n| `map`             | JS object                                                                                                                                      |\n| `set`             | JS set                                                                                                                                         |\n| `bool`            | JS boolean                                                                                                                                     |\n| `big number`      | `StringObject` with a field called `__reply_type` and value `big_number`                                                                       |\n| `verbatim string` | `StringObject` with 2 additional fields: 1. `__reply_type` and value `verbatim` 2. `__format` with the value of the ext in the verbatim string (or error if failed to convert to utf8) |\n| `null`            | JS null                                                                                                                                        |\n|                   |                                                                                                                                                |\n\nWhen running Redis commands from within a function using the `client.callRaw` API, the reply is parsed as a resp3 reply and converted to a JS object using the following rules:\n\n| resp 3            | JS object type                                                                                                                                 |\n|-------------------|------------------------------------------------------------------------------------------------------------------------------------------------|\n| `status`          | JS `ArrayBuffer` with a field called `__reply_type` and value `status`                                                                        |\n| `bulk string`     | JS `ArrayBuffer`                                                                                                                              |\n| `Error`           | Raise JS exception                                                                                                                             |\n| `long`            | JS big integer                                                                                                                                 |\n| `double`          | JS number                                                                                                                                      |\n| `array`           | JS array                                                                                                                                       |\n| `map`             | JS object                                                                                                                                      |\n| `set`             | JS set                                                                                                                                         |\n| `bool`            | JS boolean                                                                                                                                     |\n| `big number`      | `StringObject` with a field called `__reply_type` and value `big_number`                                                                       |\n| `verbatim string` | JS `ArrayBuffer` with 2 additional fields: 1. `__reply_type` and value `verbatim` 2. `__format` with the value of the ext in the verbatim string |\n| `null`            | JS null                                                                                                                                        |\n|                   |                                                                                                                                                |"},{"id":"javascript-to-resp-conversion","title":"JavaScript to RESP conversion","role":"content","text":"| JS type                                                          | RESP2         | RESP3                                  |\n|------------------------------------------------------------------|---------------|----------------------------------------|\n| `string`                                                         | `bulk string` | `bulk string`                          |\n| `string` object with field `__reply_type=status`                 | `status`      | `status`                               |\n| Exception                                                        | `error`       | `error`                                |\n| `big integer`                                                    | `long`        | `long`                                 |\n| `number`                                                         | `bulk string` | `double`                               |\n| `array`                                                          | `array`       | `array`                                |\n| `map`                                                            | `array`       | `map`                                  |\n| `set`                                                            | `array`       | `set`                                  |\n| `bool`                                                           | `long`        | `bool`                                 |\n| `string` object with field`__reply_type=varbatim` and `__format=txt` | `bulk string` | `verbatim string` with format as `txt` |\n| `null`                                                           | resp2 `null`  | resp3 `null`                           |"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/Sync_Async","title":"Sync and async","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/sync_async/","summary":"Sync and async functions","aliases":["/develop/interact/programmability/triggers-and-functions/concepts/sync_async/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-21T16:36:38+01:00","page_type":"content","content_hash":"28e6a54232d4d5b9c66e83671c43a49ae8ecc6c1505e9ae07f37928b52879d49","sections":[{"id":"content","title":"Content","role":"content","text":"By default, each time a function is invoked, it is executed synchronously. This ensures the atomicity property, meaning that no other commands will be executed on Redis while the function is running. The atomicity property offers several advantages:\n\n* Multiple keys can be updated simultaneously, guaranteeing that other clients see the complete update rather than partial updates.\n* The data in Redis remains unchanged while it is being processed.\n\nHowever, the major disadvantage of the atomicity property is that Redis is blocked throughout the entire invocation, preventing it from serving other clients.\n\nRedis Stack's triggers and functions feature aims to provide greater flexibility to function writers by enabling the invocation of functions in the background. When a function is invoked in the background, it cannot directly access the Redis key space. To interact with the Redis key space from the background, the function must block Redis and enter an atomic section where the atomicity property is once again guaranteed.\n\nTo run triggers and functions in the background, functions can be implemented as JS coroutines using the `registerAsyncFunction` API. The coroutine is invoked on a background thread and does not block the Redis processes. Here's an example:\n\n[code example]\n\nThe simple function shown above will return the value 'test' and will execute on a background thread without blocking Redis. This allows Redis to continue accepting commands from other clients while the function is running.\n\nThe coroutine also accepts an optional client argument, which differs from the client used in synchronous functions. This client argument does not allow direct invocation of Redis commands. Instead, it provides the capability to block Redis and enter an atomic section where the atomicity property is once again guaranteed. Here's an example that demonstrates invoking a ping command from within an async coroutine:\n\n[code example]\n\nRunning this function will return a `pong` reply:\n\n[code example]\n\nNotice that this time, in order to invoke the function, we used `TFCALLASYNC`. **We can only invoke async functions using `TFCALLASYNC`**.\n\nNow let's look at a more complex example. Assume we want to write a function that counts the number of hashes in Redis that have a `name` property with some value. As a first attempt, we'll write a synchronous function that uses the [`SCAN`](https://redis.io/docs/latest/commands/scan) command to scan the key space:\n\n[code example]\n\nWhile this function works, it has the potential to block Redis for a long time. So let's modify this function to run in the background as a coroutine:\n\n[code example]\n\nBoth implementations return the same result, but the second function runs in the background and blocks Redis just to analyze the next batch of keys that are returned from the [`SCAN`](https://redis.io/docs/latest/commands/scan) command. Other commands will be processed in between [`SCAN`](https://redis.io/docs/latest/commands/scan) batches. Notice that the coroutine approach allows the key space to be changed while the scanning it. The function writer will need to decide if this is acceptable.\n\n# Start sync and move to async\n\nThe previous example, although functional, has a drawback in terms of performance. Even though Redis is not blocked, it still takes time to return the reply to the user. However, if we modify the requirement slightly and agree to obtain an approximate value, we can achieve much better performance in most cases. This can be done by implementing result caching using a key named `<name>_count` and setting an expiration time on that key, which triggers recalculation of the value periodically. Here's the updated code:\n\n[code example]\n\nThe above code works as expected. It first checks the cache and if the cache exists it's returned. Otherwise it will perform the calculation and update the cache. But the above example is not optimal. The callback is a coroutine, which means that it will always be calculated on a background thread. Intrinsically, moving to a background thread is costly. The best approach would be to check the cache synchronously and, only if its not there, move to the background. Triggers and functions provides for starting synchronously and then moving asynchronous execution using `executeAsync` function as required. The new code:\n\n[code example]\n\n`executeAsync` will return a `Promise` object. When triggers and functions sees that the function returns a Promise, it waits for the promise to be resolved and returns its result to the client. The above implementation will be much faster in the case of cache hit.\n\n**Notice** that even though we registered a synchronous function (not a coroutine) we still used `registerAsyncFunction`. This is because our function has the potential of blocking the client, taking the execution to the background. If we had used `registerFunction`, Triggers and functions would not have allowed the function to block the client and it would have ignored the returned promise object.\n\n**Also notice** it is not always possible to wait for a promise to be resolved. If the command is called inside a `multi/exec` it is not possible to block it and wait for the promise. In such cases the client will get an error. It is possible to check if blocking the client is allowed using the `client.isBlockAllowed()` function, which will return `true` if it is OK to wait for a promise to be resolved and `false` if it is not possible.\n\n# Call blocking commands\n\nRedis has a few commands that blocks the client and executed asynchronously when some condition holds (commands like [blpop](https://redis.io/docs/latest/commands/blpop)). In general, such commands are not suppose to be called inside a script and calling them will result in running their none blocking logic. For example, [blpop](https://redis.io/docs/latest/commands/blpop) will basically runs lpop and return empty result if the list it empty.\n\nRedisGears allows running blocking commands using `client.callAsync` API. `client.callAsync` will execute the blocking command and return a promise object which will be resolved when the command invocation finished (notice that `client.callAsync` allow calling any command and not just blocking but it will always return a promise object that will be resolve later, so **using it for regular commands is less efficient**). \n\nExample:\n\n[code example]\n\nThe following function will continue popping elements from the requested list up until it will encounter the requested value. In case the list is empty it will wait until elements will be added to the list.\n\nRedisGears also provided `client.callAsyncRaw` API, which is the same as `client.callAsync` but will not decode the replies as utf8.\n\n**Notice**: There is no guarantee when the promise returned from `client.callAsyn` will be resolved. So the **function writer should not make any assumption about atomicity guarantees.**\n\n# Block Redis failure\n\nBlocking Redis might fail for a few reasons:\n\n* Redis reached OOM state and the `redis.functionFlags.NO_WRITES` or `redis.functionFlags.ALLOW_OOM` flags are not set (see [functions flags](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/Function_Flags) for more information)\n* `redis.functionFlags.NO_WRITES` flag is not set and the Redis instance changed roles and is now a replica.\n* The ACL user that invoked the function was deleted.\n\nThe failure will result in an exception that the function writer can choose to handle or throw it to be caught by triggers and functions.\n\n# Block Redis timeout\n\nBlocking Redis for a long time is discouraged and is considered an unsafe operation. The triggers and functions feature attempts to protect the function writer and will time out the blocking function if it continues for too long. The timeout can be set as a [module configuration](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/Configuration) along side the fatal failure policy that indicates how to handle the timeout. Policies can be one of the following:\n\n* Abort - Stop the function invocation even at the cost of losing the atomicity property.\n* Kill - Keep the atomicity property and do not stop the function invocation. In this case there is a risk of an external process killing the Redis server, thinking that the shard is not responding."}],"examples":[{"id":"content-ex0","language":"js","code":"#!js api_version=1.0 name=lib\n\nredis.registerAsyncFunction('test', async function(){\n    return 'test';\n});","section_id":"content"},{"id":"content-ex1","language":"js","code":"#!js api_version=1.0 name=lib\n\nredis.registerFunction('test', async function(client){\n    return client.block(function(redis_client){\n        return redis_client.call('ping');\n    });\n});","section_id":"content"},{"id":"content-ex2","language":"bash","code":"127.0.0.1:6379> TFCALLASYNC lib.test 0\n\"PONG\"","section_id":"content"},{"id":"content-ex3","language":"js","code":"#!js api_version=1.0 name=lib\n\nredis.registerFunction('test', function(client, expected_name){\n    var count = 0;\n    var cursor = '0';\n    do{\n        var res = client.call('scan', cursor);\n        cursor = res[0];\n        var keys = res[1];\n        keys.forEach((key) => {\n            if (client.call('hget', key, 'name') == expected_name) {\n                count += 1;\n            }\n        });\n    } while(cursor != '0');\n    return count;\n});","section_id":"content"},{"id":"content-ex4","language":"js","code":"#!js api_version=1.0 name=lib\n\nredis.registerAsyncFunction('test', async function(async_client, expected_name){\n    var count = 0;\n    var cursor = '0';\n    do{\n        async_client.block((client)=>{\n            var res = client.call('scan', cursor);\n            cursor = res[0];\n            var keys = res[1];\n            keys.forEach((key) => {\n                if (client.call('hget', key, 'name') == expected_name) {\n                    count += 1;\n                }\n            });\n        });\n    } while(cursor != '0');\n    return count;\n});","section_id":"content"},{"id":"content-ex5","language":"js","code":"#!js api_version=1.0 name=lib\n\nredis.registerAsyncFunction('test', async function(async_client, expected_name){\n    // check the cache first\n    var cached_value = async_client.block((client)=>{\n        return client.call('get', expected_name + '_count');\n    });\n\n    if (cached_value != null) {\n        return cached_value;\n    }\n\n\n    var count = 0;\n    var cursor = '0';\n    do{\n        async_client.block((client)=>{\n            var res = client.call('scan', cursor);\n            cursor = res[0];\n            var keys = res[1];\n            keys.forEach((key) => {\n                if (client.call('hget', key, 'name') == expected_name) {\n                    count += 1;\n                }\n            });\n        });\n    } while(cursor != '0');\n\n    // set count to the cache wil 5 seconds expiration\n    async_client.block((client)=>{\n        client.call('set', expected_name + '_count', count);\n        client.call('expire', expected_name + '_count', 5);\n    });\n\n    return count;\n});","section_id":"content"},{"id":"content-ex6","language":"js","code":"#!js api_version=1.0 name=lib\n\nredis.registerAsyncFunction('test', function(client, expected_name){\n    // check the cache first\n    var cached_value = client.call('get', expected_name + '_count');\n    if (cached_value != null) {\n        return cached_value;\n    }\n\n    // cache is not set, move to background\n    return client.executeAsync(async function(async_client) {\n        var count = 0;\n        var cursor = '0';\n        do{\n            async_client.block((client)=>{\n                var res = client.call('scan', cursor);\n                cursor = res[0];\n                var keys = res[1];\n                keys.forEach((key) => {\n                    if (client.call('hget', key, 'name') == expected_name) {\n                        count += 1;\n                    }\n                });\n            });\n        } while(cursor != '0');\n\n        // set count to the cache wil 5 seconds expiration\n        async_client.block((client)=>{\n            client.call('set', expected_name + '_count', count);\n            client.call('expire', expected_name + '_count', 5);\n        });\n\n        return count;\n    });\n});","section_id":"content"},{"id":"content-ex7","language":"js","code":"#!js api_version=1.0 name=lib\n\nredis.registerAsyncFunction('my_blpop', async function(client, key, expected_val) {\n    var res = null\n    do {\n        res = await client.block((c) => {\n            return c.callAsync('blpop', key, '0');\n        })\n    } while (res[1] != expected_val);\n    return res;\n});","section_id":"content"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/triggers","title":"Triggers","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/triggers/","summary":"Different ways of executing JavaScript functions","aliases":["/develop/interact/programmability/triggers-and-functions/concepts/triggers/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2024-06-06T06:55:24-07:00","children":[{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/triggers/User_Functions","summary":"Execute JavaScript functions via `TFCALL` or `TFCALLASYNC`","title":"User functions","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/triggers/user_functions/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/triggers/KeySpace_Triggers","summary":"Execute a JavaScript function based on a keyspace notification","title":"Keyspace triggers","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/triggers/keyspace_triggers/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/triggers/Stream_Triggers","summary":"Execute a JavaScript function when an item is added to a stream","title":"Stream triggers","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/triggers/stream_triggers/"}],"page_type":"index","sections":[],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/triggers/KeySpace_Triggers","title":"Keyspace triggers","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/triggers/keyspace_triggers/","summary":"Execute a JavaScript function based on a keyspace notification","aliases":["/develop/interact/programmability/triggers-and-functions/concepts/triggers/keyspace_triggers/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-21T16:36:38+01:00","page_type":"content","content_hash":"7cc21935c78cc400c8429737f44831a47b3f8a4f3a8db3537f71c3d0e9052b79","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Keyspace triggers allow you to register a function that will be executed whenever an event occurs in the database. Most events are triggered by command invocations, but there are two special events that can occur independently of a command:\n\n1. Expired: This event is fired when a key expires from the database.\n2. Evicted: This event is fired when a key is evicted from the database.\n\nFor a complete list of supported events, please refer to the [Redis keyspace notifications page](https://redis.io/docs/latest/develop/pubsub/keyspace-notifications#events-generated-by-different-commands).\n\nTo register a keyspace trigger, you need to use the `redis.registerKeySpaceTrigger` API when loading your library. The following example demonstrates how to register a database trigger that adds a \"last updated\" field whenever a hash key is modified:\n\n[code example]\n\nArgument Description:\n\n* `consumer`: The consumer name.\n* `prefix `: The key prefix on which the trigger should be fired.\n* `callback`: The callback function to invoke, following the same rules of [Sync and Async invocation](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/Sync_Async). The callback will only be invoked on the primary shard.\n\nRun the example:\n\n[code example]\n\nThe `data` argument, which is passed to the consumer callback, are in the following format:\n\n[code example]\n\nNotice that the `key` field is given only if the key can be decoded as a `JS` `String`, otherwise the value will be `null`.\n\nWe can display trigger information using `TFUNCTION LIST` command:\n\n[code example]"},{"id":"trigger-guarantees","title":"Trigger guarantees","role":"content","text":"If the callback function passed to the trigger is a `JS` function (not a Coroutine), it is guaranteed that the callback will be invoked atomically along side the operation that caused the trigger; meaning all clients will see the data only after the callback has completed. In addition, it is guaranteed that the effect of the callback will be replicated to the replica and the AOF in a `multi/exec` block together with the command that fired the trigger.\n\nIf the callback is a Coroutine, it will be executed in the background and there is no guarantee on where or if it will be executed. The guarantees are the same as described on [sync and async invocation](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/Sync_Async)."},{"id":"upgrades","title":"Upgrades","role":"content","text":"When upgrading existing trigger code using the `REPLACE` option of `TFUNCTION LOAD` command, all trigger parameters can be modified."},{"id":"advanced-usage","title":"Advanced usage","role":"content","text":"For most use cases, the `registerKeySpaceTrigger` API is sufficient. But there are some use cases where you might need a better guarantee on when a trigger will be fired. Let's look at the following example:\n\n[code example]\n\nWhenever a hash key is changed, the example above will read the field `name` from the hash, for example `tom`, and increase the value of the key `name_tom`. (**Notice: This function will not work properly on cluster. We need to use `{}` on the key's name to make sure we are writing to a key located on the current shard. For the sake of simplicity we will ignore the cluster issues for the time being**). Running the function will give the following results:\n\n[code example]\n\nWe can see that the key `name_tom` was incremented once, and the key `name_jerry` was incremented once. Will we get the same results if we wrap the `hset` call with a `multi`/`exec`?\n\n[code example]\n\nWhat just happened? `name_jerry` was incremented twice while `name_tom` was not incremented at all. This happened because, in case of a `multi`/`exec` or Lua function, the notifications are fired at the end of the transaction, so all the clients will receive notifications of the last value written, which is `jerry`.\n\nTo fix the code and still get the expected results even on `multi`/`exec`. Triggers and functions allows you to specify an optional callback that will run exactly when the notification happened (and not at the end of the transaction). The constraint on this callback is that it can only **read** data without performing any writes. The new code will be as follow:\n\n[code example]\n\nThe above code gives an optional function argument, `onTriggerFired`, to our trigger. The function will be fired right after a key change and will allow us to read the content of the key. We are adding the content into the `data` argument, which will be given to the actual trigger function that can write the data. The above code works as expected:\n\n[code example]"}],"examples":[{"id":"overview-ex0","language":"js","code":"#!js api_version=1.0 name=myFirstLibrary\n\nredis.registerKeySpaceTrigger(\"consumer\", \"\", function(client, data){\n    if (client.call(\"type\", data.key) != \"hash\") {\n        // key is not a has, do not touch it.\n        return;\n    }\n    // get the current time in ms\n    var curr_time = client.call(\"time\")[0];\n    // set '__last_updated__' with the current time value\n    client.call('hset', data.key, '__last_updated__', curr_time);\n});","section_id":"overview"},{"id":"overview-ex1","language":"bash","code":"127.0.0.1:6379> hset h x 1\n(integer) 1\n127.0.0.1:6379> hgetall h\n1) \"x\"\n2) \"1\"\n3) \"__last_updated__\"\n4) \"1658390831\"\n127.0.0.1:6379> hincrby h x 1\n(integer) 2\n127.0.0.1:6379> hgetall h\n1) \"x\"\n2) \"2\"\n3) \"__last_updated__\"\n4) \"1658390910\"","section_id":"overview"},{"id":"overview-ex2","language":"json","code":"{\n    \"event\": \"<the event name that fired the trigger>\",\n    \"key\": \"<key name that the event was fired on as String>\",\n    \"key_raw\": \"<key name that the event was fired on as ArrayBuffer>\"\n}","section_id":"overview"},{"id":"overview-ex3","language":"bash","code":"127.0.0.1:6379> TFUNCTION list vvv\n1)  1) \"engine\"\n    2) \"js\"\n    3) \"api_version\"\n    4) \"1.0\"\n    5) \"name\"\n    6) \"foo\"\n    7) \"pending_jobs\"\n    8) (integer) 0\n    9) \"user\"\n    10) \"default\"\n    11) \"functions\"\n   12) (empty array)\n   13) \"keyspace_triggers\"\n   14) (empty array)\n   15) \"stream_triggers\"\n   16) 1)  1) \"name\"\n           2) \"consumer\"\n           3) \"num_triggered\"\n           4) (integer) 2\n           5) \"num_finished\"\n           6) (integer) 2\n           7) \"num_success\"\n           8) (integer) 1\n           9) \"num_failed\"\n          10) (integer) 1\n          11) \"last_error\"\n          12) \"TypeError: redis.call is not a function\"\n          13) \"last_exection_time\"\n          14) (integer) 0\n          15) \"total_exection_time\"\n          16) (integer) 0\n          17) \"avg_exection_time\"\n          18) \"0\"","section_id":"overview"},{"id":"advanced-usage-ex0","language":"js","code":"#!js api_version=1.0 name=myFirstLibrary\n\nredis.registerKeySpaceTrigger(\"consumer\", \"\", function(client, data){\n    if (client.call(\"type\", data.key) != \"hash\") {\n        // key is not a has, do not touch it.\n        return;\n    }\n    var name = client.call('hget', data.key, 'name');\n    client.call('incr', `name_${name}`);\n});","section_id":"advanced-usage"},{"id":"advanced-usage-ex1","language":"bash","code":"127.0.0.1:6379> hset x name tom\n(integer) 1\n127.0.0.1:6379> hset x name jerry\n(integer) 0\n127.0.0.1:6379> get name_tom\n\"1\"\n127.0.0.1:6379> get name_jerry\n\"1\"","section_id":"advanced-usage"},{"id":"advanced-usage-ex2","language":"bash","code":"127.0.0.1:6379> multi\nOK\n127.0.0.1:6379(TX)> hset x name tom\nQUEUED\n127.0.0.1:6379(TX)> hset x name jerry\nQUEUED\n127.0.0.1:6379(TX)> exec\n1) (integer) 1\n2) (integer) 0\n127.0.0.1:6379> get name_tom\n(nil)\n127.0.0.1:6379> get name_jerry\n\"2\"","section_id":"advanced-usage"},{"id":"advanced-usage-ex3","language":"js","code":"#!js api_version=1.0 name=lib\n\nredis.registerKeySpaceTrigger(\"consumer\", \"\", function(client, data){\n    if (data.name !== undefined) {\n        client.call('incr', `name_${data.name}`);\n    }\n},{\n    onTriggerFired: (client, data) => {\n        if (client.call(\"type\", data.key) != \"hash\") {\n            // key is not a has, do not touch it.\n            return;\n        }\n        data.name = client.call('hget', data.key, 'name');\n    }\n});","section_id":"advanced-usage"},{"id":"advanced-usage-ex4","language":"bash","code":"127.0.0.1:6379> multi\nOK\n127.0.0.1:6379(TX)> hset x name tom\nQUEUED\n127.0.0.1:6379(TX)> hset x name jerry\nQUEUED\n127.0.0.1:6379(TX)> exec\n1) (integer) 1\n2) (integer) 0\n127.0.0.1:6379> get name_tom\n\"1\"\n127.0.0.1:6379> get name_jerry\n\"1\"","section_id":"advanced-usage"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/triggers/Stream_Triggers","title":"Stream triggers","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/triggers/stream_triggers/","summary":"Execute a JavaScript function when an item is added to a stream","aliases":["/develop/interact/programmability/triggers-and-functions/concepts/triggers/stream_triggers/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-21T16:36:38+01:00","page_type":"content","content_hash":"201fd35ebc3be7326770e1691ae26a8ac9d8c89c6217baefd2b1868e057b7c4e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Stack's triggers and functions feature comes with a full stream API to process data from [Redis streams](https://redis.io/docs/latest/develop/data-types/streams). Unlike RedisGears v1 that provided a micro batching API, the new triggers and functions feature provides a **real streaming** API, which means that the data will be processed as soon as it enters the stream."},{"id":"register-a-stream-consumer","title":"Register a stream consumer","role":"content","text":"Triggers and functions provide an API that allows to register a stream trigger. Do not get confused with [Redis streams consumer groups](https://redis.io/docs/latest/develop/data-types/streams#consumer-groups), triggers and functions uses the Redis Module API to efficiently read the stream and manage its consumers. This approach gives a much better performance as there is no need to invoke any Redis commands in order to read from the stream. Lets see a simple example:\n\n[code example]\n\nArgument Description:\n\n* consumer - the consumer name.\n* stream - streams name prefix on which to trigger the callback.\n* callback - the callback to invoke on each element in the stream. Following the same rules of [sync and async invocation](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/Sync_Async). The callback will be invoke only on primary shard.\n\nIf we register this library (see the [quick start](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/Quick_Start_CLI) section to learn how to Register a RedisGears function) and run the following command on our Redis:\n\n[code example]\n\nWe will see the following line on the Redis log file:\n\n[code example]\n\nThe `data` argument which pass to the stream consumer callback are in the following format:\n\n[code example]\n\nThe reason why the record is a list of touples and not an object is because the Redis Stream specifications allows duplicate keys.\n\nNotice that `stream_name` and `record` fields might contains `null`'s if the data can not be decoded as string. the `*_raw` fields will always be provided and will contains the data as `JS` `ArrayBuffer`.\n\nWe can observe the streams which are tracked by our registered consumer using `TFUNCTION LIST` command:\n\n[code example]"},{"id":"enable-trimming-and-set-window","title":"Enable trimming and set window","role":"content","text":"We can enable stream trimming by adding `isStreamTrimmed` optional argument after the trigger callback, we can also set the `window` argument that controls how many elements can be processed simultaneously. example:\n\n[code example]\n\nThe default values are:\n* `isStreamTrimmed` - `false`\n* `window` - 1\n\nIt is enough that a single consumer will enable trimming so that the stream will be trimmed. The stream will be trim according to the slowest consumer that consume the stream at a given time (even if this is not the consumer that enabled the trimming). Raising exception during the callback invocation will **not prevent the trimming**. The callback should decide how to handle failures by invoke a retry or write some error log. The error will be added to the `last_error` field on `TFUNCTION LIST` command."},{"id":"data-processing-guarantees","title":"Data processing guarantees","role":"content","text":"As long as the primary shard is up and running we guarantee exactly once property (the callback will be triggered exactly one time on each element in the stream). In case of failure such as shard crashing, we guarantee at least once property (the callback will be triggered at least one time on each element in the stream)"},{"id":"upgrades","title":"Upgrades","role":"content","text":"When upgrading the consumer code (using the `REPLACE` option of `TFUNCTION LOAD` command) the following consumer parameters can be updated:\n\n* Window\n* Trimming\n\nAny attempt to update any other parameter will result in an error when loading the library."}],"examples":[{"id":"register-a-stream-consumer-ex0","language":"js","code":"#!js api_version=1.0 name=myFirstLibrary\n\nredis.registerStreamTrigger(\n    \"consumer\", // consumer name\n    \"stream\", // streams prefix\n    function(c, data) {\n        // callback to run on each element added to the stream\n        redis.log(JSON.stringify(data, (key, value) =>\n            typeof value === 'bigint'\n                ? value.toString()\n                : value // return everything else unchanged\n        ));\n    }\n);","section_id":"register-a-stream-consumer"},{"id":"register-a-stream-consumer-ex1","language":"plaintext","code":"XADD stream:1 * foo1 bar1\nXADD stream:1 * foo2 bar2\nXADD stream:2 * foo1 bar1\nXADD stream:2 * foo2 bar2","section_id":"register-a-stream-consumer"},{"id":"register-a-stream-consumer-ex2","language":"plaintext","code":"2630021:M 05 Jul 2022 17:13:22.506 * <redisgears_2> {\"id\":[\"1657030402506\",\"0\"],\"stream_name\":\"stream:1\",\"record\":[[\"foo1\",\"bar1\"]]}\n2630021:M 05 Jul 2022 17:13:25.323 * <redisgears_2> {\"id\":[\"1657030405323\",\"0\"],\"stream_name\":\"stream:1\",\"record\":[[\"foo2\",\"bar2\"]]}\n2630021:M 05 Jul 2022 17:13:29.475 * <redisgears_2> {\"id\":[\"1657030409475\",\"0\"],\"stream_name\":\"stream:2\",\"record\":[[\"foo1\",\"bar1\"]]}\n2630021:M 05 Jul 2022 17:13:32.715 * <redisgears_2> {\"id\":[\"1657030412715\",\"0\"],\"stream_name\":\"stream:2\",\"record\":[[\"foo2\",\"bar2\"]]}","section_id":"register-a-stream-consumer"},{"id":"register-a-stream-consumer-ex3","language":"json","code":"{\n    \"id\": [\"<ms>\", \"<seq>\"],\n    \"stream_name\": \"<stream name>\",\n    \"stream_name_raw\": \"<stream name as ArrayBuffer>\",\n    \"record\":[\n        [\"<key>\", \"<value>\"],\n        .\n        .\n        [\"<key>\", \"<value>\"]\n    ],\n    \"record_raw\":[\n        [\"<key_raw>\", \"<value_raw>\"],\n        .\n        .\n        [\"<key_raw>\", \"<value_raw>\"]\n    ],\n}","section_id":"register-a-stream-consumer"},{"id":"register-a-stream-consumer-ex4","language":"plaintext","code":"127.0.0.1:6379> TFUNCTION LIST LIBRARY lib vvv\n1)  1) \"engine\"\n    1) \"js\"\n    2) \"api_version\"\n    3) \"1.0\"\n    4) \"name\"\n    5) \"lib\"\n    6) \"pending_jobs\"\n    7) (integer) 0\n    8) \"user\"\n    9)  \"default\"\n    10) \"functions\"\n   1)  (empty array)\n   2)  \"stream_triggers\"\n   3)  1)  1) \"name\"\n           1) \"consumer\"\n           2) \"prefix\"\n           3) \"stream\"\n           4) \"window\"\n           5) (integer) 1\n           6) \"trim\"\n           7) \"disabled\"\n           8) \"num_streams\"\n          1)  (integer) 2\n          2)  \"streams\"\n          3)  1)  1) \"name\"\n                  1) \"stream:2\"\n                  2) \"last_processed_time\"\n                  3) (integer) 0\n                  4) \"avg_processed_time\"\n                  5) \"0\"\n                  6) \"last_lag\"\n                  7) (integer) 0\n                  8) \"avg_lag\"\n                 1)  \"0\"\n                 2)  \"total_record_processed\"\n                 3)  (integer) 2\n                 4)  \"id_to_read_from\"\n                 5)  \"1657030412715-0\"\n                 6)  \"last_error\"\n                 7)  \"None\"\n                 17) \"pending_ids\"\n                 18) (empty array)\n              1)  1) \"name\"\n                  1) \"stream:1\"\n                  2) \"last_processed_time\"\n                  3) (integer) 1\n                  4) \"avg_processed_time\"\n                  5) \"0.5\"\n                  6) \"last_lag\"\n                  7) (integer) 1\n                  8) \"avg_lag\"\n                 1)  \"0.5\"\n                 2)  \"total_record_processed\"\n                 3)  (integer) 2\n                 4)  \"id_to_read_from\"\n                 5)  \"1657030405323-0\"\n                 6)  \"last_error\"\n                 7)  \"None\"\n                 8)  \"pending_ids\"\n                 9)  (empty array)\n   4)  \"keyspace_triggers\"\n   5)  (empty array)","section_id":"register-a-stream-consumer"},{"id":"enable-trimming-and-set-window-ex0","language":"js","code":"#!js api_version=1.0 name=myFirstLibrary\n\nredis.registerStreamTrigger(\n    \"consumer\", // consumer name\n    \"stream\", // streams prefix\n    function(c, data) {\n        // callback to run on each element added to the stream\n        redis.log(JSON.stringify(data, (key, value) =>\n            typeof value === 'bigint'\n                ? value.toString()\n                : value // return everything else unchanged\n        ));\n    }, \n    {\n        isStreamTrimmed: true,\n        window: 3   \n    }\n);","section_id":"enable-trimming-and-set-window"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/triggers/User_Functions","title":"User functions","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/triggers/user_functions/","summary":"Execute JavaScript functions via `TFCALL` or `TFCALLASYNC`","aliases":["/develop/interact/programmability/triggers-and-functions/concepts/triggers/user_functions/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2024-06-06T06:55:24-07:00","page_type":"content","content_hash":"99935aca0f69441ece2ed8145e0019b15e0a335232ce925e7313d7c191567c8d","sections":[{"id":"content","title":"Content","role":"content","text":"All `TFCALL` command arguments that follow the function name are passed to the function callback. The following example shows how to implement a simple function that returns the value of a key of type string or hash:\n\n[code example]\n\nExample of running the preceding function:\n\n[code example]\n\nIt is also possible to get all the function arguments as a `JS` array. This is how we can extend the above example to accept multiple keys and return their values:\n\n[code example]\n\nRun example:\n\n[code example]"}],"examples":[{"id":"content-ex0","language":"js","code":"#!js api_version=1.0 name=lib\n\nredis.registerFunction('my_get', function(client, key_name){\n    if (client.call('type', key_name) == 'string') {\n        return client.call('get', key_name);\n    }\n    if (client.call('type', key_name) == 'hash') {\n        return client.call('hgetall', key_name);\n    }\n    throw \"Unsupported type\";\n});","section_id":"content"},{"id":"content-ex1","language":"bash","code":"127.0.0.1:6379> set x 1\nOK\n127.0.0.1:6379> TFCALL lib.my_get 1 x\n\"1\"\n127.0.0.1:6379> hset h a b x y\n(integer) 2\n127.0.0.1:6379> TFCALL lib.my_get 1 h\n1) \"a\"\n2) \"b\"\n3) \"x\"\n4) \"y\"","section_id":"content"},{"id":"content-ex2","language":"js","code":"#!js api_version=1.0 name=lib\n\nredis.registerFunction('my_get', function(client, ...keys){\n    var results = [];\n    keys.forEach((key_name)=> {\n            if (client.call('type', key_name) == 'string') {\n                results.push(client.call('get', key_name));\n                return;\n            }\n            if (client.call('type', key_name) == 'hash') {\n                results.push(client.call('hgetall', key_name));\n                return;\n            }\n            results.push(\"Unsupported type\");\n        }\n    );\n    return results;\n\n});","section_id":"content"},{"id":"content-ex3","language":"bash","code":"127.0.0.1:6379> TFCALL foo.my_get 2 x h\n1) \"1\"\n2) 1) \"a\"\n   2) \"b\"\n   3) \"x\"\n   4) \"y\"","section_id":"content"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/Configuration","title":"Configuration","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/configuration/","summary":"Configure the operation parameters","aliases":["/develop/interact/programmability/triggers-and-functions/configuration/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-21T16:36:38+01:00","page_type":"content","content_hash":"c743be81deb5663a61c354db3544f8cf2eef008912452d8ee83d93cb0d119626","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Stack's triggers and functions feature provides configuration options to control its operation. These options can be set when the module is bootstrapped and, in some cases, at runtime.\n\nThe following sections describe the configuration options and how to set them."},{"id":"bootstrap-configuration","title":"Bootstrap configuration","role":"content","text":"You can set your configuration options when the module is loaded.\nWhen the module is loaded at start time, the module configuration can be defined in the Redis configuration file. When loading the module at runtime the configuration can be given to the [`MODULE LOADEX`](https://redis.io/docs/latest/commands/module-loadex) command. Each configuration must be prefixed with the module name, `redisgears_2.<configuration name>`."},{"id":"runtime-configuration","title":"Runtime configuration","role":"content","text":"You may set certain configuration options at runtime. Setting a configuration at runtime is done using [`CONFIG SET`](https://redis.io/docs/latest/commands/config-set) command. Here each configuration must be prefixed with the module name, `redisgears_2.<configuration name>`.\n\nExample:\n\n[code example]"},{"id":"configurations","title":"Configurations","role":"configuration","text":""},{"id":"execution-threads","title":"execution-threads","role":"content","text":"The `execution-threads` configuration option controls the number of background threads that run JS code. **Note that libraries are considered single threaded**. This configuration allows Redis to parallelize the invocation of multiple libraries.\n\n_Expected Value_\n\nInteger\n\n_Default_\n\n1\n\n_Minimum Value_\n\n1\n\n_Maximum Value_\n\n32\n\n_Runtime Configurability_\n\nNo"},{"id":"library-fatal-failure-policy","title":"library-fatal-failure-policy","role":"content","text":"The `library-fatal-failure-policy` configuration option controls how to handle a fatal error. A fatal error is consider one of the following:\n\n* Block timeout - The function blocks the Redis processes for too long (configurable using the [lock-redis-timeout](#lock-redis-timeout) configuration value)\n* OOM - The function consumes too much memory (configurable using the [v8-maxmemory](#v8-maxmemory) configuration value).\n\nThis configuration basically allows choosing between two options:\n\n* Do not break atomicity property, even at the cost of killing the Redis processes.\n* Keep my Redis processes alive, even at the cost of losing atomicity.\n\n_Expected Value_\n\n* kill - Save the atomicity property. Risk of killing the Redis processes.\n* abort - Abort the invocation of the function and keep the Redis processes alive. Risk of losing the atomicity property.\n\n_Default_\n\nabort\n\n_Runtime Configurability_\n\nYes"},{"id":"v8-maxmemory","title":"v8-maxmemory","role":"content","text":"The `v8-maxmemory` configuration option controls the maximum amount of memory used by all V8 libraries. Exceeding this limit is considered a fatal error and will be handled base on the [library-fatal-failure-policy](#library-fatal-failure-policy) configuration value.\n\n_Expected Value_\n\nInteger\n\n_Default_\n\n200M\n\n_Minimum Value_\n\n50M\n\n_Maximum Value_\n\n1G\n\n_Runtime Configurability_\n\nNo"},{"id":"v8-library-initial-memory-usage","title":"v8-library-initial-memory-usage","role":"content","text":"The `v8-library-initial-memory-usage` configuration option controls the initial memory given to a single V8 library. This value can not be greater then [`v8-library-initial-memory-limit`](#v8-library-initial-memory-limit) or [v8-maxmemory](#v8-maxmemory).\n\n_Expected Value_\n\nInteger\n\n_Default_\n\n2M\n\n_Minimum Value_\n\n1M\n\n_Maximum Value_\n\n10M\n\n_Runtime Configurability_\n\nNo"},{"id":"v8-library-initial-memory-limit","title":"v8-library-initial-memory-limit","role":"content","text":"The `v8-library-initial-memory-limit` configuration option controls the initial memory limit on a single V8 library. This value can not be greater then [v8-maxmemory](#v8-maxmemory).\n\n_Expected Value_\n\nInteger\n\n_Default_\n\n3M\n\n_Minimum Value_\n\n2M\n\n_Maximum Value_\n\n20M\n\n_Runtime Configurability_\n\nNo"},{"id":"v8-library-memory-usage-delta","title":"v8-library-memory-usage-delta","role":"content","text":"The `v8-library-memory-usage-delta` configuration option controls the delta by which we will increase the V8 library memory limit once the limit reached. This value can not be greater then [v8-maxmemory](#v8-maxmemory).\n\n_Expected Value_\n\nInteger\n\n_Default_\n\n1M\n\n_Minimum Value_\n\n1M\n\n_Maximum Value_\n\n10M\n\n_Runtime Configurability_\n\nNo"},{"id":"lock-redis-timeout","title":"lock-redis-timeout","role":"content","text":"The `lock-redis-timeout` configuration option controls the maximum amount of time (in MS) a library can lock Redis. Exceeding this limit is considered a fatal error and will be handled based on the [library-fatal-failure-policy](#library-fatal-failure-policy) configuration value. This\nconfiguration only affects library loading at runtime with `TFUNCTION LOAD`.\nThe timeout for loading a library from RDB is set separately via\n[db-loading-lock-redis-timeout](#db-loading-lock-redis-timeout).\n\n\n_Expected Value_\n\nInteger\n\n_Default_\n\n500 MS\n\n_Minimum Value_\n\n100 MS\n\n_Maximum Value_\n\nUnlimited\n\n_Runtime Configurability_\n\nYes\n\n#### Side effects\n\nWhen setting `lock-redis-timeout`, if the new value is higher than the\n`db-loading-lock-redis-timeout`, the `db-loading-lock-redis-timeout` is also updated to\nthis value."},{"id":"db-loading-lock-redis-timeout","title":"db-loading-lock-redis-timeout","role":"content","text":"This timeout configuration is used for setting the upper time limit\n(in milliseconds) for the library loading from RDB.\n\n\n_Expected Value_\n\nInteger\n\n_Default_\n\n30000 MS\n\n_Minimum Value_\n\n100 MS\n\n_Maximum Value_\n\nUnlimited\n\n_Runtime Configurability_\n\nYes\n\n#### Notes\n\nThe value cannot be lower than the value of `lock-redis-timeout`."},{"id":"remote-task-default-timeout","title":"remote-task-default-timeout","role":"content","text":"The `remote-task-default-timeout` configuration option controls the timeout when waiting for a remote task to finish. If the timeout is reached an error will result.\n\n_Expected Value_\n\nInteger\n\n_Default_\n\n500 MS\n\n_Minimum Value_\n\n1 MS\n\n_Maximum Value_\n\nUnlimited\n\n_Runtime Configurability_\n\nYes"},{"id":"error-verbosity","title":"error-verbosity","role":"errors","text":"The `error-verbosity` configuration option controls the verbosity of error messages that will be provided by triggers and functions. The higher the value the more verbose the error messages will be (for example, including stack traces and extra information for better analysis and debugging).\n\n_Expected Value_\n\nInteger\n\n_Default_\n\n1\n\n_Minimum Value_\n\n1\n\n_Maximum Value_\n\n2\n\n_Runtime Configurability_\n\nYes"}],"examples":[{"id":"runtime-configuration-ex0","language":"bash","code":"> config set redisgears_2.lock-redis-timeout 1000\nOK","section_id":"runtime-configuration"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/Debugging","title":"Debugging","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/debugging/","summary":"Methods for debugging your Redis Stack functions","aliases":["/develop/interact/programmability/triggers-and-functions/debugging/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-21T16:36:38+01:00","page_type":"content","content_hash":"3b138282e2855c4fd97d9b5428beb3476ca29f328c797037d023459a3f46bf0f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"There are two methods you can use to debug your Redis Stack functions:\n\n1. Make judicious use of the `redis.log` function, which writes to the Redis log file.\n1. Use Redis [pub/sub](https://redis.io/docs/latest/develop/pubsub)."},{"id":"use-redislog","title":"Use `redis.log`","role":"content","text":"If you have access to the Redis log files, `redis.log` is a good method to use when debugging. However, there is a drawback. Redis Cloud users do not have access to the Redis log files, and it's pretty common that only system administrators have access to them on self-hosted installations. Fortunately, you can also use Redis pub/sub, which will be discussed in the next section.\n\nYou don't have to do anything special to use `redis.log`, as it's always available. Here is an example:\n\n[code example]\n\nAfter loading the library and executing the function with `TFCALL`, you'll see something like the following in your Redis log file:\n\n[code example]"},{"id":"use-redis-pubsub","title":"Use Redis pub/sub","role":"content","text":"If you don't have access to your Redis database log files, you can use pub/sub. The following example demonstrates how to use the [client.call](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/JavaScript_API#clientcall) API to publish to a pub/sub channel.\n\n[code example]\nIn a CLI session, subscribe to the `tfLogChannel` channel and watch for messages.\n\n[code example]\n\nIn another CLI session, load the library, replacing what was already there, and then call the new function:\n\n[code example]\n\nYou'll see the following in the previous CLI session:\n\n[code example]\n\nThere is a downside to using pub/sub. Redis pub/sub provides at-most-once message delivery semantics, which means that once a message is sent, it won't be sent again. So, if a message isn't consumed, it's gone forever. This is likely okay for debugging, but for the longer term, `redis.log` is the better solution for log persistence."}],"examples":[{"id":"use-redislog-ex0","language":"javascript","code":"#!js api_version=1.0 name=lib\n\nredis.registerFunction('hello', ()=> {\n  redis.log('Hello log')\n  return 'Hello from an external file'\n})","section_id":"use-redislog"},{"id":"use-redislog-ex1","language":"plaintext","code":"45718:M 01 Nov 2023 07:02:40.593 * <redisgears_2> Hello log","section_id":"use-redislog"},{"id":"use-redis-pubsub-ex0","language":"javascript","code":"#!js api_version=1.0 name=lib\n\nconst logChannel = 'tfLogChannel'\n\nfunction publog(client, message) {\n  client.call('publish', logChannel, message)\n}\n\nredis.registerFunction('tflog', (client) => {\n  publog(client, 'sample pub/sub log message')\n  return 'sample'\n})","section_id":"use-redis-pubsub"},{"id":"use-redis-pubsub-ex1","language":"redis","code":"$ redis-cli\n127.0.0.1:6379> subscribe tfLogChannel\n1) \"subscribe\"\n2) \"tfLogChannel\"\n3) (integer) 1\nReading messages... (press Ctrl-C to quit or any key to type command)","section_id":"use-redis-pubsub"},{"id":"use-redis-pubsub-ex2","language":"redis","code":"127.0.0.1:6379> TFCALL lib.tflog 0\n\"sample\"","section_id":"use-redis-pubsub"},{"id":"use-redis-pubsub-ex3","language":"redis","code":"1) \"message\"\n2) \"tfLogChannel\"\n3) \"sample pub/sub log message\"","section_id":"use-redis-pubsub"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/Development","title":"Development","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/development/","summary":"How to develop for triggers and functions","aliases":["/develop/interact/programmability/triggers-and-functions/development/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-21T16:36:38+01:00","page_type":"content","content_hash":"e17d4e2ceb0ffe5b293d53d4b4e7aea4aecb733e8e37f4a28e458dcda94144ae","sections":[{"id":"overview","title":"Overview","role":"overview","text":"To aid in the development of new libraries of triggers and functions, you can use the type declaration files for the [triggers and functions API](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/JavaScript_API), which allows your preferred development environment to provide autocompletion and type checking. You can install this information using the following command:\n\n[code example]\n\nOr you can manually add it as a devDependency to your `package.json` file:\n\n[code example]"},{"id":"example-project-setup","title":"Example project setup","role":"example","text":"Create an empty directory for your new triggers and functions project, `my_first_project`. Navigate to the folder and run the following command:\n\n[code example]\n\nUpdate the `package.json` to add the gears API to the `devDependencies` object.\n\n[code example]\n\nInstall the dependencies using `npm install`.\n\nCreate a new file, `index.js` and import the gears-api module. While this step is not strictly necessary, doing so will enable code intelligent code completion when adding the `registerFunction`:\n\n[code example]\n\nTo automate the deployment, update the `scripts` section in `package.json`:\n\n[code example]\n\nNow you can deploy your code to Redis Stack using the following command:\n\n[code example]\n\nThe provided URL should follow the following format:\n\n`<redis[s]://[[username][:password]@][host][:port][/db-number]>`\n\nYou can now run your Redis Stack function:\n\n[code example]"},{"id":"example-function-using-a-third-party-npm-package","title":"Example function using a third-party `npm` package","role":"example","text":"If you want to use a third-party `npm` package in your Redis Stack function, the overall process is nearly the same as already discussed, with just a couple of differences.\n\nFirst, you'll need to install your package using `npm install`. For this example, the `pi` symbol from the `math.js` library will be used in a simple function that calculates the area of a circle.\n\n[code example]\n\nNote: your `package.json` file will also be updated with the new dependency. For example:\n\n[code example]\n\nNext, you'll import the required symbol(s) into your function.\n\n[code example]\n\nLoad and run your new function as described in the previous section."}],"examples":[{"id":"overview-ex0","language":"bash","code":"npm install https://gitpkg.now.sh/RedisGears/RedisGears/js_api --save-dev","section_id":"overview"},{"id":"overview-ex1","language":"json","code":"\"devDependencies\": {\n  \"@redis/gears-api\": \"https://gitpkg.now.sh/RedisGears/RedisGears/js_api\"\n}","section_id":"overview"},{"id":"example-project-setup-ex0","language":"bash","code":"$ npm init -y -f\nnpm WARN using --force Recommended protections disabled.\nWrote to /home/work/my_first_project/package.json:\n\n{\n  \"name\": \"my_first_project\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"keywords\": [],\n  \"author\": \"\",\n  \"license\": \"ISC\"\n}","section_id":"example-project-setup"},{"id":"example-project-setup-ex1","language":"js","code":"{\n  \"name\": \"my_first_project\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"devDependencies\": {\n    \"@redis/gears-api\": \"https://gitpkg.now.sh/RedisGears/RedisGears/js_api\"\n  },\n  \"keywords\": [],\n  \"author\": \"\",\n  \"license\": \"ISC\"\n}","section_id":"example-project-setup"},{"id":"example-project-setup-ex2","language":"JavaScript","code":"#!js name=lib api_version=1.0\n\nimport { redis } from '@redis/gears-api';\n\nredis.registerFunction(\"test\", ()=>{\n    return \"test\";\n});","section_id":"example-project-setup"},{"id":"example-project-setup-ex3","language":"json","code":"\"scripts\": {\n  \"deploy\": \"gears-api index.js\"\n}","section_id":"example-project-setup"},{"id":"example-project-setup-ex4","language":"bash","code":"> npm run deploy -- -r redis://localhost:6379\n\n> deploy\n> gears-api index.js\n\nDeployed! :)","section_id":"example-project-setup"},{"id":"example-project-setup-ex5","language":"bash","code":"127.0.0.1:6379> TFCALL lib.test 0\n\"test\"","section_id":"example-project-setup"},{"id":"example-function-using-a-third-party-npm-package-ex0","language":"bash","code":"npm install mathjs","section_id":"example-function-using-a-third-party-npm-package"},{"id":"example-function-using-a-third-party-npm-package-ex1","language":"json","code":"\"dependencies\": {\n    \"mathjs\": \"^12.0.0\"\n}","section_id":"example-function-using-a-third-party-npm-package"},{"id":"example-function-using-a-third-party-npm-package-ex2","language":"javascript","code":"#!js api_version=1.0 name=lib\n\nimport { redis } from '@redis/gears-api';\nimport { pi } from 'mathjs';\n\nfunction calculateCircleArea(client, radius) {\n  return pi * radius * radius;\n}\n\nredis.registerFunction('calculateArea', calculateCircleArea);","section_id":"example-function-using-a-third-party-npm-package"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/Examples","title":"Triggers and functions examples","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/examples/","summary":"How triggers and functions can be used","aliases":["/develop/interact/programmability/triggers-and-functions/examples/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2024-06-06T06:55:24-07:00","page_type":"content","content_hash":"c87d0ff9c081445a1cdfb8915557255f3b54f0cbcaf2dfd1c39daa280e050b13","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Stack's triggers and functions feature enables the detection of changes to data as they happen and guarantee seamless execution of business logic at the data source. This ensures that new options to manipulate the data are possible and can be delivered for all the clients at the same time, while deployment and maintenance are simplified. Let's explore some industry-specific use cases where these capabilities shine:\n\n- **Retail**: In the retail sector, a function can be developed to update the inventory immediately upon order receipt. This empowers businesses to accurately predict stock requirements for specific warehouses on any given day.\n- **Travel**: For the travel industry, a trigger can be utilized to detect new flight bookings and efficiently load the relevant information into a queue for different consumers. Services can then leverage this data to provide recommendations for hotels, restaurants, car rental services, and more. Leveraging Redis geometries, powerful recommendation systems can offer localized and personalized suggestions.\n- **Subscription services**: In the realm of subscription services, employing a keyspace trigger can automatically identify users whose subscriptions have been renewed, seamlessly changing their status to active. Further operations can be performed on these users, such as adding them to a queue for the delivery of notifications or executing additional actions.\n\nThese examples highlight the practical application of triggers and functions in different industries, showcasing their value in streamlining processes and delivering efficient solutions. \n\nUsing keyspace and Stream triggers to capture events and execute the desired JavaScript functions requires few lines of code to enrich applications with new behaviors. The following examples show how typical problems are solved with JavaScript."},{"id":"auditing-and-logging","title":"Auditing and logging","role":"content","text":"Redis Triggers can be used to detect changes to specific data structures, and log an auditing trail in the Redis log. The timestamp of the latest change can be updated in the same function.\n\n[code example]"},{"id":"enrich-and-transform-data","title":"Enrich and transform data","role":"content","text":"Data can be extracted, enriched or transformed, and loaded again. As an example, upon insertion of a document in a Hash data structure, a Trigger can launch the execution of a Function that computes the number of words in the text (in the example, a simple tokenization is presented but the logic can be as complex as required). The counter is finally stored in the same Hash together with the original document. \n\n[code example]"},{"id":"batch-operations","title":"Batch operations","role":"content","text":"JavaScript functions can be executed when required, for example as part of scheduled or periodic maintenance routines. An example could be deleting data identified by the desired pattern. \n\n\n[code example]\n\nThe function `del_keys` performs an asynchronous batch scan of all the keys in the database matching the desired pattern, and enters a blocking section where it gets partial results. The asynchronous design of this function permits non-blocking execution, so other clients will not starve while the iteration is performed on a keyspace of arbitrary size. The function can be invoked as follows.\n\n[code example]"},{"id":"automatic-expiration","title":"Automatic expiration","role":"content","text":"Sometimes it is useful to enforce expiration time for data that by nature is short-lived. An example could be session data, or authentication tokens. A trigger fits the use case and can execute a function that sets the desired TTL.\n\n\n[code example]"}],"examples":[{"id":"auditing-and-logging-ex0","language":"javascript","code":"function alertUserChanged(client, data) {\n    // detect the event and log an audit trail\n    if (data.event == 'hset'){\n        redis.log('User data changed: ' + data.key);\n    }\n    else if (data.event == 'del'){\n        redis.log('User deleted: ' + data.key);\n    }\n\n    var curr_time = client.call(\"time\")[0];\n\n    // add the current timestamp to the Hash\n    client.call('hset', data.key, 'last', curr_time);\n}\n\nredis.registerKeySpaceTrigger('alert_user_changed', 'user:', \n    alertUserChanged, {description: 'Report user data change.'});","section_id":"auditing-and-logging"},{"id":"enrich-and-transform-data-ex0","language":"javascript","code":"function wordsCounter(client, data){\n    text = client.call('hget', data.key, 'content');\n    words = text.split(' ').length;\n    client.call('hset', data.key, 'cnt', words.toString());\n    redis.log('Number of words: ' + words.toString()); //This log is for demo purposes, be aware of spamming the log file in production\n}\n\nredis.registerKeySpaceTrigger('words_counter', 'doc:', \n    wordsCounter, {description: 'Count words in a document.'});","section_id":"enrich-and-transform-data"},{"id":"batch-operations-ex0","language":"javascript","code":"#!js api_version=1.0 name=utils\n\n\nredis.registerAsyncFunction('del_keys', async function(async_client, pattern){\n    var count = 0;\n    var cursor = '0';\n    do {\n        async_client.block((client)=>{\n            var res = client.call('scan', cursor, 'match', pattern);\n            cursor = res[0];\n            var keys = res[1];\n            keys.forEach((key) => {\n                client.call('del', key);\n            });\n        });\n    } while(cursor != '0');\n    return count;\n});","section_id":"batch-operations"},{"id":"batch-operations-ex1","language":"text","code":"TFCALLASYNC utils.del_keys 0 \"user:*\"","section_id":"batch-operations"},{"id":"automatic-expiration-ex0","language":"javascript","code":"function automaticSessionExpiry(client, data){\n    client.call('expire', data.key, '3600');\n    redis.log('Session ' + data.key + ' has been automatically expired'); //This log is for demo purposes, be aware of spamming the log file in production\n}\n\nredis.registerKeySpaceTrigger('automaticSessionExpiry', 'session:', \n    automaticSessionExpiry, {description: 'Set the session TTL to one hour.'});","section_id":"automatic-expiration"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions","title":"Triggers and functions","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/","summary":"Trigger and execute JavaScript functions in the Redis process","aliases":["/develop/interact/programmability/triggers-and-functions/","/commands/tfcall/","/commands/tfcallasync/","/commands/tfunction-delete/","/commands/tfunction-list/","/commands/tfunction-load/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2024-06-06T06:55:24-07:00","children":[{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/Quick_Start_RI","summary":"Get started with triggers and functions using Redis Insight","title":"Quick start using Redis Insight","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/quick_start_ri/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/Quick_Start_CLI","summary":"Get started with triggers and functions using redis-cli","title":"Quick start using redis-cli","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/quick_start_cli/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/Configuration","summary":"Configure the operation parameters","title":"Configuration","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/configuration/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/Development","summary":"How to develop for triggers and functions","title":"Development","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/development/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/Debugging","summary":"Methods for debugging your Redis Stack functions","title":"Debugging","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/debugging/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts","summary":"Details about triggers, the JavaScript API, and advanced concepts","title":"Concepts","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/concepts/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/Examples","summary":"How triggers and functions can be used","title":"Triggers and functions examples","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/examples/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/Known_Limitations","summary":"Overview of the known limitations","title":"Known limitations","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/known_limitations/"}],"page_type":"content","content_hash":"613a8abdf82986e12c0dc3cf28c020b56ab0eba706dc20687a943bfe3c5915db","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"[![discord](https://img.shields.io/discord/697882427875393627?style=flat-square)](https://discord.gg/xTbqgTB)\n[![Github](https://img.shields.io/static/v1?label=&message=repository&color=5961FF&logo=github)](https://github.com/RedisGears/RedisGears/)\n\nThe triggers and functions feature of Redis Stack allows running JavaScript functions inside Redis. These functions can be executed on-demand, by an event-driven trigger, or by a stream processing trigger."},{"id":"quick-links","title":"Quick links","role":"content","text":"* [Command documentation](https://github.com/RedisGears/RedisGears/tree/master/docs/commands)\n* [Source code](https://github.com/RedisGears/RedisGears)\n* [Latest release](https://github.com/RedisGears/RedisGears/releases)\n* [Docker image](https://hub.docker.com/r/redis/redis-stack-server/)"},{"id":"primary-features","title":"Primary features","role":"content","text":"* JavaScript engine for functions\n* On-demand functions\n* Keyspace triggers\n* Stream triggers\n* Async handling of functions\n* Read data from across the cluster"},{"id":"cluster-support","title":"Cluster support","role":"content","text":"Triggers and functions support deployment and execution of functions across a cluster. Functions are executed on the correct shard based on the key that is changed or read functions can be executed on all to return a correct view of the data."},{"id":"references","title":"References","role":"related","text":""},{"id":"blog-posts","title":"Blog posts","role":"content","text":"- [Expanding the Database Trigger Features in Redis](https://redis.com/blog/database-trigger-features/)"},{"id":"overview","title":"Overview","role":"overview","text":""}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/Known_Limitations","title":"Known limitations","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/known_limitations/","summary":"Overview of the known limitations","aliases":["/develop/interact/programmability/triggers-and-functions/known_limitations/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2024-06-06T06:55:24-07:00","page_type":"content","content_hash":"85e21a9db924d35a9859b0f47ad664988881b4a8737aa23b41b3b67b21803213","sections":[{"id":"limited-write-options","title":"Limited write options","role":"limits","text":"JavaScript remote functions are limited to **read operations** only. Any attempt to perform a write operation of the following functions on a shard different than the one executing the function will result in an error.\n\n- `async_client.runOnShards` runs the remote function on all the shards\n- `async_client.runOnKey` runs the remote function on the shard responsible for a given key\n\nIn addition, keyspace modification performed by JavaScript functions that are registered using any of the methods available should perform write operations locally:\n\n- If the function is registered with `registerFunction` or `registerAsyncFunction`, it can insert, modify or delete keys that are in the same shard where the function is executed.\n- If the function is registered with `registerKeySpaceTrigger` or `registerStreamTrigger`, keyspace modification must be local to the shard that originated the events.\n\nIt is also recommended to co-locate the keys to be modified in the same hash slot as the key or Stream that originated the event. As an example, if the user profile stored in the Hash `myserv:user:1234` is subject to changes and we'd like to count them in an external counter, we would name the counter using hash tags: `{myserv:user:1234}:cnt`."},{"id":"exclusive-access-to-the-keyspace","title":"Exclusive access to the keyspace","role":"content","text":"By design, asynchronous functions guarantee exclusive single-threaded access to the keyspace, the distinctive feature of Redis. In asynchronous programming with JavaScript functions, access to the keyspace in read or write mode must be blocking, while if not accessing the keyspace, the execution may be non-blocking. This implementation maintains the same level of data consistency as Redis standard commands or Lua scripts and functions but takes advantage of asynchronous execution, a feature of the JavaScript engine."},{"id":"javascript-variables","title":"JavaScript variables","role":"content","text":"Not all the JavaScript global variables are made available by the JavaScript engine loaded by Redis (e.g. `console`, `document`). The `redis` global variable can be used to manage functions registration, logging etc."},{"id":"sandboxed","title":"Sandboxed","role":"content","text":"This feature is sandboxed, meaning, from within a function, it’s not possible to make calls to external services, including other Redis databases, or APIs."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/Quick_Start_CLI","title":"Quick start using redis-cli","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/quick_start_cli/","summary":"Get started with triggers and functions using redis-cli","aliases":["/develop/interact/programmability/triggers-and-functions/quick_start_ri/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-09-21T16:36:38+01:00","page_type":"content","content_hash":"bb4819cc0387c6588cdce2d568a84bed2141914e26bd27247709970a18df85e9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Make sure that you have [Redis Stack installed](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack) and running. Alternatively, you can create a [free Redis Cloud account](https://redis.com/try-free/?utm_source=redisio&utm_medium=referral&utm_campaign=2023-09-try_free&utm_content=cu-redis_cloud_users). The triggers and functions preview is available in the fixed subscription plan for the Google Cloud Asia Pacific (Tokyo) and AWS Asia Pacific (Singapore) regions."},{"id":"connect-to-redis-stack","title":"Connect to Redis Stack","role":"content","text":"[code example]"},{"id":"load-a-library","title":"Load a library","role":"content","text":"Use the `TFUNCION LOAD` command to create a new library in your Redis instance.\n\n[code example]\n\nWhen the library is created successfully, an `OK` response is returned. Run the `TFUNCTION LIST` command to confirm your library was added to Redis.\n\n[code example]\n\nThe `TFCALL` command is used to execute the JavaScript Function. If the command fails, an error will be returned.\n\n[code example]\n\nTo update the library run the `TFUNCTION LOAD` command with the additional parameter `REPLACE`.\n\n[code example]"},{"id":"uploading-an-external-file","title":"Uploading an external file","role":"content","text":"Use the `redis-cli` command to upload JavaScript from an external file. The file needs to contain the header, which contains the engine identifier, the API version, and the library name: `#!js api_version=1.0 name=myFirstLibrary`.\n\n[code example]\n\nUse the `redis-cli -x` option to send the file with the command and use the `TFUNCTION LOAD REPLACE` to replace the inline library with the one from the `main.js` file.\n\n[code example]"},{"id":"creating-triggers","title":"Creating triggers","role":"content","text":"Functions within Redis can respond to events using keyspace triggers. While the majority of these events are initiated by command invocations, they also include events that occur when a key expires or is removed from the database.\n\nFor the full list of supported events, please refer to the [Redis keyspace notifications page](https://redis.io/docs/latest/develop/pubsub/keyspace-notifications#events-generated-by-different-commands/?utm_source=redis\\&utm_medium=app\\&utm_campaign=redisinsight_triggers_and_functions_guide).\n\nThe following code creates a new keyspace trigger that adds a new field to a new or updated hash with the latest update time. \n\nLoad the code in your database:\n\n[code example]\n\nAdd a new hash with the required prefix to trigger our function.\n\n[code example]\n\nCheck if the last updated time is added to the example.\n\n[code example]"}],"examples":[{"id":"connect-to-redis-stack-ex0","language":"Shell","code":"> redis-cli -h 127.0.0.1 -p 6379","section_id":"connect-to-redis-stack"},{"id":"load-a-library-ex0","language":"Shell","code":"127.0.0.1:6379> TFUNCTION LOAD \"#!js api_version=1.0 name=myFirstLibrary\\n redis.registerFunction('hello', ()=>{ return 'Hello World'})\"\nOK","section_id":"load-a-library"},{"id":"load-a-library-ex1","language":"shell","code":"> TFUNCTION LIST\n1) 1) \"api_version\"\n   2) \"1.0\"\n   3) \"cluster_functions\"\n   4) (empty list or set)\n   5) \"configuration\"\n   6) \"null\"\n   7) \"engine\"\n   8) \"js\"\n   9) \"functions\"\n   10) 1) \"hello\"\n   11) \"keyspace_triggers\"\n   12) (empty list or set)\n   13) \"name\"\n   14) \"myFirstLibrary\"\n   15) \"pending_async_calls\"\n   16) (empty list or set)\n   17) \"pending_jobs\"\n   18) \"0\"\n   19) \"stream_triggers\"\n   20) (empty list or set)\n   21) \"user\"\n   22) \"default\"","section_id":"load-a-library"},{"id":"load-a-library-ex2","language":"Shell","code":"127.0.0.1:6379> TFCALL myFirstLibrary.hello 0\n\"Hello World\"","section_id":"load-a-library"},{"id":"load-a-library-ex3","language":"Shell","code":"127.0.0.1:6379> TFUNCTION LOAD REPLACE \"#!js api_version=1.0 name=myFirstLibrary\\n redis.registerFunction('hello', ()=>{ return 'Hello World updated'})\"\nOK","section_id":"load-a-library"},{"id":"uploading-an-external-file-ex0","language":"JavaScript","code":"#!js api_version=1.0 name=lib\n\nredis.registerFunction('hello', ()=> {\n  return 'Hello from an external file'\n})","section_id":"uploading-an-external-file"},{"id":"uploading-an-external-file-ex1","language":"Shell","code":"redis-cli -x TFUNCTION LOAD REPLACE < ./main.js","section_id":"uploading-an-external-file"},{"id":"creating-triggers-ex0","language":"redis Load keyspace example","code":"TFUNCTION LOAD REPLACE \"#!js name=myFirstLibrary api_version=1.0\\n \n    function addLastUpdatedField(client, data) {\n        if(data.event == 'hset') {\n            var currentDateTime = Date.now();\n            client.call('hset', data.key, 'last_updated', currentDateTime.toString());\n        }\n    } \n    redis.registerKeySpaceTrigger('addLastUpdated', 'fellowship:', addLastUpdatedField);\" // Register the KeySpaceTrigger 'AddLastUpdated' for keys with the prefix 'fellowship' with a callback to the function 'addLastUpdatedField'","section_id":"creating-triggers"},{"id":"creating-triggers-ex1","language":"Shell","code":"127.0.0.1:6379> HSET fellowship:1 name \"Frodo Baggins\" title \"The One Ring Bearer\"","section_id":"creating-triggers"},{"id":"creating-triggers-ex2","language":"Shell","code":"127.0.0.1:6379> HGETALL fellowship:1\n1) \"name\"\n2) \"Frodo Baggins\"\n3) \"title\"\n4) \"The One Ring Bearer\"\n5) \"last_updated\"\n6) \"1693238681822\"","section_id":"creating-triggers"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/Quick_Start_RI","title":"Quick start using Redis Insight","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/quick_start_ri/","summary":"Get started with triggers and functions using Redis Insight","aliases":["/develop/interact/programmability/triggers-and-functions/quick_start_cli/"],"tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-10-02T14:53:57+01:00","page_type":"content","content_hash":"00fb1d287818964ee1d01ead3df018ffe6a8996e2730f365035514688d01066c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Make sure that you have [Redis Stack installed](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack) and running. Alternatively, you can create a [free Redis Cloud account](https://redis.com/try-free/?utm_source=redisio&utm_medium=referral&utm_campaign=2023-09-try_free&utm_content=cu-redis_cloud_users).\n\nIf you haven't already installed Redis Insight, you can download the latest version [here](https://redis.com/redis-enterprise/redis-insight/?_ga=2.232184223.127667221.1704724457-86137583.1685485233&_gl=1*1gygred*_ga*ODYxMzc1ODMuMTY4NTQ4NTIzMw..*_ga_8BKGRQKRPV*MTcwNDkyMzExMC40MDEuMS4xNzA0OTI3MjQ2LjUyLjAuMA..*_gcl_au*MTQzODY1OTU4OS4xNzAxMTg0MzY0). If this is your first time using Redis Insight, you may wish to read through the [Redis Insight guide](https://redis.io/docs/latest/develop/tools/insight) before continuing with this guide."},{"id":"connect-to-redis-stack","title":"Connect to Redis Stack","role":"content","text":"Open the Redis Insight application, and connect to your database by clicking on its database alias.\n\n![](https://redis.io/docs/latest/images/triggers-and-functions/tf-rdi-0.png)"},{"id":"load-a-library","title":"Load a library","role":"content","text":"Click on the triggers and functions icon and then on **+ Library** as shown below.\n\n![](https://redis.io/docs/latest/images/triggers-and-functions/tf-rdi-1.png)\n\nAdd your code to the **Library Code** section of the right-hand panel and then click **Add Library**.\n\n![](https://redis.io/docs/latest/images/triggers-and-functions/tf-rdi-2.png)\n\nYou'll see the following when the library was added:\n\n![](https://redis.io/docs/latest/images/triggers-and-functions/tf-rdi-3.png)\n\nThe `TFCALL` command is used to execute the JavaScript Function. If the command fails, an error will be returned. Click on the **>_ CLI**  button in the lower left-hand corner to open a console window and then run the command shown below.\n\n![](https://redis.io/docs/latest/images/triggers-and-functions/tf-rdi-3a.png)\n\nTo update a library you can edit the library code directly in the interface by clicking on the edit (pencil) icon. When you save your changes, the libary will be reloaded.\n\n![](https://redis.io/docs/latest/images/triggers-and-functions/tf-rdi-4.png)"},{"id":"uploading-an-external-file","title":"Uploading an external file","role":"content","text":"Click on the **+ Add Library** button as before and, instead of adding the code directly into the editor, click on the **Upload** button, select the file from your file browser, and then click on **Add Library**. The file needs to contain the header, which contains the engine identifier, the API version, and the library name: `#!js api_version=1.0 name=myFirstLibrary`.\n\n![](https://redis.io/docs/latest/images/triggers-and-functions/tf-rdi-5.png)"},{"id":"creating-triggers","title":"Creating triggers","role":"content","text":"Functions within Redis can respond to events using keyspace triggers. While the majority of these events are initiated by command invocations, they also include events that occur when a key expires or is removed from the database.\n\nFor the full list of supported events, please refer to the [Redis keyspace notifications page](https://redis.io/docs/latest/develop/pubsub/keyspace-notifications#events-generated-by-different-commands/?utm_source=redis\\&utm_medium=app\\&utm_campaign=redisinsight_triggers_and_functions_guide).\n\nThe following code creates a new keyspace trigger that adds a new field to a new or updated hash with the latest update time.\n\n[code example]\n\nUpdate the existing library as before and then, using the Redis Insight console, add a new hash with the required prefix to trigger the function.\n\n[code example]\n\nRun the [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) command to check if the last updated time is added to the example.\n\n[code example]"}],"examples":[{"id":"creating-triggers-ex0","language":"javascript","code":"#!js name=myFirstLibrary api_version=1.0\n\nfunction addLastUpdatedField(client, data) {\n    if(data.event == 'hset') {\n        var currentDateTime = Date.now();\n        client.call('hset', data.key, 'last_updated', currentDateTime.toString());\n    }\n} \n\n// Register the KeySpaceTrigger 'AddLastUpdated' for keys with the prefix 'fellowship'\n// with the callback function 'addLastUpdatedField'.\nredis.registerKeySpaceTrigger('addLastUpdated', 'fellowship:', addLastUpdatedField);\"","section_id":"creating-triggers"},{"id":"creating-triggers-ex1","language":"Shell","code":"> HSET fellowship:1 name \"Frodo Baggins\" title \"The One Ring Bearer\"","section_id":"creating-triggers"},{"id":"creating-triggers-ex2","language":"Shell","code":"> HGETALL fellowship:1\n1) \"name\"\n2) \"Frodo Baggins\"\n3) \"title\"\n4) \"The One Ring Bearer\"\n5) \"last_updated\"\n6) \"1693238681822\"","section_id":"creating-triggers"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/enterprise-capabilities","title":"Redis Software and Redis Open Source feature compatibility","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/enterprise-capabilities/","summary":"Describes the Redis Software features supported by each Redis Open Source feature.","tags":["docs","operate","stack"],"last_updated":"2026-09-22T10:15:10+01:00","page_type":"content","content_hash":"0493ced69c019c8cb3d74888d13173c5fa25e7df15bbd38bc3529667b73839eb","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This article describes compatibility between Redis Software features and Redis Open Source features. Version numbers indicate the minimum module version required for feature support."},{"id":"supported-redis-open-source-features","title":"Supported Redis Open Source features","role":"compatibility","text":"The following table shows which Redis Open Source features are supported by Redis Software and Redis Cloud.\n\n| Feature | Redis<br/>Software | Redis<br/>Cloud |\n|:-------|:-------------------------|:-----------------------|\n| [Redis Search](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search) | &#x2705; Supported | &#x2705; Supported |\n| [JSON](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json)   | &#x2705; Supported | &#x2705; Supported |\n| [Time series](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/timeseries) | &#x2705; Supported | &#x2705; Supported |\n| [Probabilistic](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/bloom) | &#x2705; Supported | &#x2705; Supported |\n| [Gears](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1) | &#x2705; Supported | &#x274c; Not supported |\n| [Triggers and functions](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions) | &#x26A0;&#xFE0F; Deprecated | &#x26A0;&#xFE0F; Deprecated |\n| [Graph](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/graph) | &#x26A0;&#xFE0F; Deprecated | &#x26A0;&#xFE0F; Deprecated |"},{"id":"feature-compatibility","title":"Feature compatibility","role":"content","text":"The following tables show Redis Software feature support for each Redis Open Source feature. \n\nVersion numbers indicate when the feature was first supported.  If you're using an earlier version than what's shown in the table, the feature is not supported.\n\nFor details about individual features, see the corresponding documentation.\n\n| Feature name/capability   | [Redis Search](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search) | [JSON](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json)    | \n|---------------------------|:--------------:|:------------:|\n| Active-Active (CRDB)[^5]  | Yes (v2.0)     | Yes (v2.2)   |\n| Backup/Restore            | Yes (v1.4)     | Yes (v1.0)   |\n| Clustering                | Yes (v1.6)[^3] | Yes (v1.0)   |\n| Custom hashing policy     | Yes (v2.0)     | Yes (v1.0)   |\n| Eviction expiration       | Yes (v2.0)     | Yes (v1.0)   |\n| Failover/migration        | Yes (v1.4)     | Yes (v1.0)   |\n| Internode encryption      | Yes (v2.0.11)  | Yes (v1.0.8) |\n| Module data types         | Yes            | Yes          |\n| Persistence (AOF)         | Yes (v1.4)     | Yes (v1.0)   |\n| Persistence (snapshot)    | Yes (v1.6)     | Yes (v1.0)   |\n| Auto Tiering [^4]         | Yes (v2.0)     | Yes (v1.0)   |\n| Redis Flex                | No             | Yes (v8.0)   |\n| Replica Of                | Yes (v1.6)[^2] | Yes (v1.0)   |\n| Reshard/rebalance         | Yes (v2.0)     | Yes (v1.0)   |\n\n[^1]: Graphs are compatible with clustering; however, individual graphs contained in a key reside in a single shard, which can affect pricing.  To learn more, [contact support](https://redis.com/company/support/).\n\n[^2]: RediSearch version 1.6 supported Replica Of only between databases with the same number of shards.  This limitation was fixed in v2.0. \n\n[^3]: You cannot use Redis Search with the [OSS Cluster API](https://redis.io/docs/latest/operate/rs/databases/configure/oss-cluster-api). This limitation was fixed in Redis Software version 8.0.\n\n[^4]: You currently cannot combine Auto Tiering with Redis Open Source features in Redis Cloud. \n\n[^5]: With the exception of JSON, you currently cannot combine Active-Active with Redis Open Source features in Redis Cloud.\n\n[^6]: Although time series are compatible with Auto Tiering, the entire series either lives in RAM or on flash.\n\n| Feature name/capability | [Time series](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/timeseries) | [Probabilistic](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/bloom) | [Gears](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1) |\n|--------------------------|:--------------:|:------------:|:----------:|\n| Active-Active (CRDB)[^5] | No             | No           | Yes (v1.0) |\n| Backup/Restore           | Yes (v1.2)     | Yes (v2.0)   | Yes (v1.0) |\n| Clustering               | Yes (v1.2)     | Yes (v2.0)   | Yes (v1.0) |\n| Custom hashing policy    | Yes (v1.2)     | Yes (v2.0)   | Yes (v1.0) |\n| Eviction expiration      | Yes (v1.2)     | Yes (v2.0)   | Yes (v1.0) |\n| Failover/migration       | Yes (v1.2)     | Yes (v2.0)   | Yes (v1.0) |\n| Internode encryption     | Yes (v1.4.9)   | Yes (v2.2.6) | Yes (v1.2) |\n| Module data types        | Yes            | Yes          | Yes        |\n| Persistence (AOF)        | Yes (v1.2)     | Yes (v2.0)   | Yes (v1.0) |\n| Persistence (snapshot)   | Yes (v1.2)     | Yes (v2.0)   | Yes (v1.0) |\n| Auto Tiering [^4]        | Yes (v1.6)[^6] | Yes (vTBD)   | Yes (vTBD) |\n| Redis Flex               | No               | Yes (v8.0)   | No |\n| Replica Of               | Yes (v1.2)     | Yes (v2.0)   | No         |\n| Reshard/rebalance        | Yes (v1.2)     | Yes (v2.0)   | Yes (v1.0) |"},{"id":"feature-descriptions","title":"Feature descriptions","role":"content","text":"The following table briefly describes each feature shown in the earlier tables.\n\n| Feature name/capability | Description |\n|-------------------------|-------------|\n| Active-Active (CRDB)    | Compatible with Active-Active (CRDB) databases  |\n| Backup/Restore          | Supports import and export features |\n| Clustering              | Compatible with sharded databases and shard migration |\n| Custom hashing policy   | Compatible with databases using custom hashing policies |\n| Eviction expiration     | Allows data to be evicted when the database reaches memory limits |\n| Failover/migration      | Compatible with primary/replica failover and the migration of shards between nodes within the cluster |\n| Internode encryption    | Compatible with encryption on the data plane |\n| Persistence (AOF)       | Compatible with databases using AoF persistence |\n| Persistence (snapshot)  | Compatible with databases using snapshot persistence | \n| Auto Tiering    | Compatible with Auto Tiering |\n| Redis Flex      | Compatible with Redis Flex |\n| Replica Of              | Compatible with Active-Passive replication | \n| Reshard/rebalance       | Compatible with database scaling for clustered databases, which redistributes data between the new shards. |"},{"id":"footnotes","title":"Footnotes","role":"content","text":""}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise","title":"Redis Open Source and Redis Software","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/","summary":"","aliases":["/operate/modules"],"tags":["docs","operate","stack"],"last_updated":"2026-10-06T08:53:14-07:00","children":[{"id":"operate/oss_and_stack/stack-with-enterprise/stack-quickstart","summary":"","title":"Redis Open Source quick start","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/stack-quickstart/"},{"id":"operate/oss_and_stack/stack-with-enterprise/enterprise-capabilities","summary":"Describes the Redis Software features supported by each Redis Open Source feature.","title":"Redis Software and Redis Open Source feature compatibility","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/enterprise-capabilities/"},{"id":"operate/oss_and_stack/stack-with-enterprise/search","summary":"","title":"Redis Search","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/"},{"id":"operate/oss_and_stack/stack-with-enterprise/json","summary":"Redis Open Source adds support for JSON to Redis databases.","title":"JSON","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json/"},{"id":"operate/oss_and_stack/stack-with-enterprise/timeseries","summary":"","title":"Time series","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/timeseries/"},{"id":"operate/oss_and_stack/stack-with-enterprise/bloom","summary":"Probabilistic data structures","title":"Probabilistic data structures","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/bloom/"},{"id":"operate/oss_and_stack/stack-with-enterprise/deprecated-features","summary":"Features (modules) that have been deprecated or replaced with a new version.","title":"Deprecated Redis Open Source features and modules","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes","summary":"Redis Open Source and modules release notes","title":"Redis Open Source and modules release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/"}],"page_type":"content","content_hash":"f53b0a5de8f1ac46ae8ded515804886daa785f2c400146b441e0f097dd12137c","sections":[{"id":"content","title":"Content","role":"content","text":"[Redis Software](https://redis.io/docs/latest/operate/rs) and [Redis Cloud](https://redis.io/docs/latest/operate/rc) support all [Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/enterprise-capabilities) features. Some of the capabilities Redis Open Source provides include querying, indexing, and full-text search and support for JSON, time series, and probabilistic data structures.\n\nTo install, enable, or upgrade modules in Redis Software, see [Install and upgrade modules](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules)."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/json/active-active","title":"Store JSON in Active-Active databases","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json/active-active/","summary":"JSON support and conflict resolution rules for Active-Active databases.","tags":["docs","operate","stack"],"last_updated":"2026-10-02T14:53:57+01:00","page_type":"content","content_hash":"6c4123473061707c2506a20dc363ce197552502fafabc214fa4abde9ec4502a1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"RedisJSON v2.2 adds support for JSON in [Active-Active Redis Software databases](https://redis.io/docs/latest/operate/rs/databases/active-active).\n\nThe design is based on [A Conflict-Free Replicated JSON Datatype](https://arxiv.org/abs/1608.03960) by Kleppmann and Beresford, but the implementation includes some changes. Several [conflict resolution rule](#conflict-resolution-rules) examples were adapted from this paper as well."},{"id":"create-an-active-active-json-database","title":"Create an Active-Active JSON database","role":"content","text":"To use JSON in an Active-Active database, you must enable JSON during database creation.\n\nActive-Active Redis Cloud databases add JSON by default. See [Create an Active-Active database](https://redis.io/docs/latest/operate/rc/databases/active-active/create-active-active-database#select-capabilities) in the Redis Cloud documentation for details.\n\nIn Redis Software, Active-Active databases created with or upgraded to Redis version 8 or later automatically enable JSON. For earlier Redis versions, you can enable JSON during database creation:\n\n1. See [Create an Active-Active geo-replicated database](https://redis.io/docs/latest/operate/rs/databases/active-active/create) in the Redis Software documentation for prerequisites and detailed steps.\n\n1. In the **Capabilities** section of the **Create Active-Active database** screen, select **JSON**:\n\n    ![Select JSON from the Capabilities section.](https://redis.io/docs/latest/images/rs/screenshots/databases/active-active-databases/create-a-a-db-json-search.png)\n\n    > [!NOTE]\n    > When you select **JSON**, **Search and Query** is also selected by default to allow you to index and query JSON documents. If you do not want to use these additional features, you can clear the **Search and Query** check box.\n    >     \n\n1. Configure additional database settings.\n\n1. Select **Create**."},{"id":"command-differences","title":"Command differences","role":"syntax","text":"Some JSON commands work differently for Active-Active databases."},{"id":"jsonclear","title":"`JSON.CLEAR`","role":"content","text":"[`JSON.CLEAR`](https://redis.io/docs/latest/commands/json.clear) resets JSON arrays and objects. It supports concurrent updates to JSON documents from different instances in an Active-Active database and allows the results to be merged."},{"id":"conflict-resolution-rules","title":"Conflict resolution rules","role":"content","text":"With Active-Active databases, it's possible for two different instances to try to run write operations on the same data at the same time. If this happens, conflicts can arise when the replicas attempt to sync these changes with each other. Conflict resolution rules determine how the database handles conflicting operations.\n\nThere are two types of conflict resolution:\n\n1. Merge:\n\n    - The operations are associative.\n\n    - Merges the results of both operations.\n\n1. Win over:\n\n    - The operations are not associative.\n\n    - One operation wins the conflict and sets the value.\n\n    - Ignores the losing operation.\n\nThe following conflict resolution rules show how Active-Active databases resolve conflicts for various JSON commands."},{"id":"assign-different-types-to-a-key","title":"Assign different types to a key","role":"content","text":"**Conflict**\n\nTwo instances concurrently assign values of different types to the same key within a JSON document.\n\nFor example:\n\nInstance 1 assigns an object to a key within a JSON document.\n\nInstance 2 assigns an array to the same key.\n\n**Resolution type**\n\nWin over\n\n**Resolution rule**\n\nThe instance with the smaller ID wins, so the key becomes an object in the given example.\n\n**Example**\n\n| Time  | Description | Instance 1 | Instance 2 |\n| :---: | :--- | :--- | :--- |\n| t1 | Set the same key to an object or an array | JSON.SET doc $.a '{}' | JSON.SET doc $.a '[]' |\n| t2 | Add data to the object and array | <nobr>JSON.SET doc  $.a.x '\"y\"'</nobr> <br /><br /> Result: <br /> {\"a\": {\"x\": \"y\"}} | <nobr>JSON.SET doc $.a '[\"z\"]'</nobr> <br /><br /> Result: <br /> {\"a\": [\"z\"]} |\n| t3 | Active-Active synchronization | – Sync – | – Sync – |\n| t4 | Instance 1 wins | JSON.GET doc $ <br /><br /> Result: <br /> {\"a\": {\"x\": \"y\"}} | JSON.GET doc $ <br /><br /> Result: <br /> {\"a\": {\"x\": \"y\"}} |"},{"id":"create-versus-create","title":"Create versus create","role":"content","text":"**Conflict**\n\nTwo instances concurrently use `JSON.SET` to assign a new JSON document to the same key.\n\n**Resolution type**\n\nWin over\n\n**Resolution rule**\n\nThe instance with the smaller ID wins.\n\n**Example**\n\n| Time  | Description | Instance 1 | Instance 2 |\n| :---: | :--- | :--- | :--- |\n| t1 | Create a new JSON document | <nobr>JSON.SET doc $ '{\"field\": \"a\"}'</nobr> | <nobr>JSON.SET doc $ '{\"field\": \"b\"}'</nobr> |\n| t2 | Active-Active synchronization | – Sync – | – Sync – |\n| t3 | Instance 1 wins | JSON.GET doc $ <br /><br /> Result: <br /> {\"field\": \"a\"} | JSON.GET doc $ <br /><br /> Result: <br /> {\"field\": \"a\"} |"},{"id":"create-versus-update","title":"Create versus update","role":"content","text":"**Conflict**\n\nInstance 1 creates a new document and assigns it to an existing key with `JSON.SET`.\n\nInstance 2 updates the existing content of the same key with `JSON.SET`.\n\n**Resolution type**\n\nWin over\n\n**Resolution rule**\n\nThe operation that creates a new document wins.\n\n**Example**\n\n| Time  | Description | Instance 1 | Instance 2 |\n| :---: | :--- | :--- | :--- |\n| t1 | The document exists on both instances | JSON.GET doc $ <br /><br /> Result: <br />  {\"field1\": \"value1\"} | JSON.GET doc $ <br /><br /> Result: <br />  {\"field1\": \"value1\"} |\n| t2 | Instance 1 creates a new document; instance 2 updates the existing document | <nobr>JSON.SET doc $ '{\"field2\": \"value2\"}'</nobr> | <nobr>JSON.SET doc $.field1 '[1, 2, 3]'</nobr> |\n| t3 | Active-Active synchronization | – Sync – | – Sync – |\n| t4 | Instance 1 wins | JSON.GET doc . <br /><br /> Result: <br /> {\"field2\": \"value2\"} | JSON.GET doc . <br /><br /> Result: <br /> {\"field2\": \"value2\"} |"},{"id":"delete-versus-create","title":"Delete versus create","role":"content","text":"**Conflict**\n\nInstance 1 deletes a JSON document with `JSON.DEL`.\n\nInstance 2 uses `JSON.SET` to create a new JSON document and assign it to the key deleted by instance 1.\n\n**Resolution type**\n\nWin over\n\n**Resolution rule**\n\nDocument creation wins over deletion.\n\n**Example**\n\n| Time  | Description | Instance 1 | Instance 2 |\n| :---: | :--- | :--- | :--- |\n| t1 | The document exists on both instances | JSON.GET doc $ <br /><br /> Result: <br /> {\"field1\": \"value1\"} | JSON.GET doc $ <br /><br /> Result: <br /> {\"field1\": \"value1\"} |\n| t2 | Instance 1 deletes the document; instance 2 creates a new document | JSON.DEL doc | <nobr>JSON.SET doc $ '{\"field1\": \"value2\"}'</nobr> |\n| t3 | Active-Active synchronization | – Sync – | – Sync – |\n| t4 | Instance 2 wins | JSON.GET doc $ <br /><br /> Result: <br /> <nobr>{\"field1\": \"value2\"}</nobr> | JSON.GET doc $ <br /><br /> Result: <br /> {\"field1\": \"value2\"} |"},{"id":"delete-versus-update","title":"Delete versus update","role":"content","text":"**Conflict**\n\nInstance 1 deletes a JSON document with `JSON.DEL`.\n\nInstance 2 updates the content of the same document with `JSON.SET`.\n\n**Resolution type**\n\nWin over\n\n**Resolution rule**\n\nDocument deletion wins over updates.\n\n**Example**\n\n| Time  | Description | Instance 1 | Instance 2 |\n| :---: | :--- | :--- | :--- |\n| t1 | The document exists on both instances | JSON.GET doc $ <br /><br /> Result: <br /> <nobr>{\"field1\": \"value1\"}</nobr> | JSON.GET doc $ <br /><br /> Result: <br /> {\"field1\": \"value1\"} |\n| t2 | Instance 1 deletes the document; instance 2 updates it | JSON.DEL doc | <nobr>JSON.SET doc $.field1 '[1, 2, 3]'</nobr> |\n| t3 | Active-Active synchronization | – Sync – | – Sync – |\n| t4 | Instance 1 wins | JSON.GET doc $ <br /><br /> Result: <br /> (nil) | JSON.GET doc $ <br /><br /> Result: <br /> (nil) |"},{"id":"update-versus-update","title":"Update versus update","role":"content","text":"**Conflict**\n\nInstance 1 updates a field inside a JSON document with `JSON.SET`.\n\nInstance 2 updates the same field with a different value.\n\n**Resolution type**\n\nWin over\n\n**Resolution rule**\n\nThe instance with the smallest ID wins.\n\n**Example**\n\n| Time  | Description | Instance 1 | Instance 2 |\n| :---: | :--- | :--- | :--- |\n| t1 | The document exists on both instances | JSON.GET doc $ <br /><br /> Result: <br /> {\"field\": \"a\"} | JSON.GET doc $ <br /><br /> Result: <br /> {\"field\": \"a\"} |\n| t2 | Update the same field with different data | <nobr>JSON.SET doc $.field \"b\"</nobr> | <nobr>JSON.SET doc $.field \"c\"</nobr> |\n| t3 | Active-Active synchronization | – Sync – | – Sync – |\n| t4 | Instance 1 wins | JSON.GET doc $ <br /><br /> Result: <br /> {\"field\": \"b\"} | JSON.GET doc $ <br /><br /> Result: <br /> {\"field\": \"b\"} |"},{"id":"update-versus-clear","title":"Update versus clear","role":"content","text":"The version of RedisJSON prior to v2.2 has two different ways to reset the content of a JSON object:\n\n- Assign a new empty JSON object:\n    \n    [code example]\n\n    If you use this method, it cannot be merged with a concurrent update.\n\n- For each key, remove it with `JSON.DEL`:\n\n    [code example]\n\n    With this method, it can merge the reset with concurrent updates.\n\nAs of RedisJSON v2.2, you can use the `JSON.CLEAR` command to reset the JSON document without removing each key manually. This method also lets concurrent updates be merged.\n\n#### Assign an empty object\n\n**Conflict**\n\nInstance 1 adds \"red\" to the existing \"colors\" object with `JSON.SET`.\n\nInstance 2 assigns a new empty object for \"colors\".\n\n**Resolution type**\n\nWin over\n\n**Resolution rule**\n\nDocument creation wins over the update, so the result will be an empty object.\n\n**Example**\n\n| Time  | Description | Instance 1 | Instance 2 |\n| :---: | :--- | :--- | :--- |\n| t1 | The document exists on both instances | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {\"blue\": \"#0000ff\"}} | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {\"blue\": \"#0000ff\"}} |\n| t2 | Instance 1 adds a new color; instance 2 resets colors to an empty object | <nobr>JSON.SET doc $.colors.red ‘#ff0000’</nobr> | JSON.SET doc $.colors ‘{}’ |\n| t3 | Instance 2 adds a new color |  | <nobr>JSON.SET doc $.colors.green ‘#00ff00’</nobr> |\n| t4 |  | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {\"blue\": \"#0000ff\", \"red\": \"#ff0000\"}} | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {\"green\": \"#00ff00\"}} |\n| t5 | Active-Active synchronization | – Sync – | – Sync – |\n| t6 | Instance 2 wins | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {\"green\": \"#00ff00\"}} | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {\"green\": \"#00ff00\"}} |\n\n#### Use `JSON.CLEAR`\n\n**Conflict**\n\nInstance 1 adds \"red\" to the existing \"colors\" object with `JSON.SET`.\n\nInstance 2 clears the \"colors\" object with `JSON.CLEAR` and adds \"green\" to \"colors\".\n\n**Resolution type**\n\nMerge\n\n**Resolution rule**\n\nMerges the results of all operations.\n\n**Example**\n\n| Time  | Description | Instance 1 | Instance 2 |\n| :---: | :--- | :--- | :--- |\n| t1 | The document exists on both instances | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {\"blue\": \"#0000ff\"}} | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {\"blue\": \"#0000ff\"}} |\n| t2 | Instance 1 adds a new color; instance 2 resets the colors | <nobr>JSON.SET doc $.colors.red ‘#ff0000’</nobr> | JSON.CLEAR doc $.colors |\n| t3 |  | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {\"blue\": \"#0000ff\", \"red\": \"#ff0000\"}} | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {}} |\n| t4 | Instance 2 adds a new color |  | <nobr>JSON.SET doc $.colors.green ‘#00ff00’</nobr> |\n| t5 |  |  | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {\"green\": \"#00ff00\"}} |\n| t6 | Active-Active synchronization | – Sync – | – Sync – |\n| t7 | Merges the results of both instances | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {\"red\": \"#ff0000\", \"green\": \"#00ff00\"}} | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {\"red\": \"#ff0000\", \"green\": \"#00ff00\"}} |"},{"id":"update-versus-update-array","title":"Update versus update array","role":"content","text":"**Conflict**\n\nTwo instances update the same existing array with different content.\n\n**Resolution type**\n\nMerge\n\n**Resolution rule**\n\nMerges the results of all operations on the array. Preserves the original element order from each instance.\n\n**Example**\n\n| Time  | Description | Instance 1 | Instance 2 |\n| :---: | :--- | :--- | :--- |\n| t1 | The document exists on both instances | JSON.GET doc $ <br /><br /> Result: <br /> '[\"a\", \"b\", \"c\"]' | JSON.GET doc $ <br /><br /> Result: <br /> '[\"a\", \"b\", \"c\"]' |\n| t2 | Instance 1 removes an array element; instance 2 adds one | JSON.ARRPOP doc $ 1 <br /><br /> Result: <br /> [\"a\", \"c\"] | <nobr>JSON.ARRINSERT doc $ 0 ‘\"y\"’</nobr> <br /><br /> Result: <br /> [\"y\", \"a\", \"b\", \"c\"] |\n| t3 | Both instances add another element to the array | <nobr>JSON.ARRINSERT doc $ 1 ‘\"x\"’</nobr> <br /><br /> Result: <br /> [\"a\", \"x\", \"c\"] | <nobr>JSON.ARRINSERT doc $ 2 ‘\"z\"’</nobr> <br /><br /> Result: <br /> [\"y\", \"a\", \"z\", \"b\", \"c\"] |\n| t4 | Active-Active synchronization | – Sync – | – Sync – |\n| t5 | Merge results from both instances | JSON.GET doc $ <br /><br /> Result: <br /> [\"y\", \"a\", \"x\", \"z\", \"c\"] | JSON.GET doc $ <br /><br /> Result: <br /> [\"y\", \"a\", \"x\", \"z\", \"c\"] |"},{"id":"update-versus-delete-array-element","title":"Update versus delete array element","role":"content","text":"**Conflict**\n\nInstance 1 removes an element from a JSON array with `JSON.ARRPOP`.\n\nInstance 2 updates the same element that instance 1 removes.\n\n**Resolution type**\n\nWin over\n\n**Resolution rule**\n\nDeletion wins over updates.\n\n**Example**\n\n| Time  | Description | Instance 1 | Instance 2 |\n| :---: | :--- | :--- | :--- |\n| t1 | The document exists on both instances | JSON.GET doc $ <br /><br /> Result: <br /> {\"todo\": [{\"title\": \"buy milk\", \"done\": false}]} | JSON.GET doc $ <br /><br /> Result: <br /> {\"todo\": [{\"title\": \"buy milk\", \"done\": false}]} |\n| t2 | Instance 1 removes an array element; instance 2 updates the same element | <nobr>JSON.ARRPOP doc $.todo 0</nobr> | <nobr>JSON.SET doc '$.todo[0][\"done\"]' 'true'’</nobr> |\n| t3 |  | JSON.GET doc $ <br /><br /> Result: <br /> {\"todo\": []} | JSON.GET doc $ <br /><br /> Result: <br /> [{\"title\": \"buy milk\", \"done\": true}]} |\n| t4 | Active-Active synchronization | – Sync – | – Sync – |\n| t5 | Instance 1 wins | JSON.GET doc $ <br /><br /> Result: <br /> doc = {\"todo\": []} | JSON.GET doc $ <br /><br /> Result: <br /> doc = {\"todo\": []} |"},{"id":"update-versus-update-object","title":"Update versus update object","role":"content","text":"**Conflict**\n\nBoth instances update the same existing object with different content.\n\n**Resolution type**\n\nMerge\n\n**Resolution rule**\n\nMerges the results of all operations on the object.\n\n**Example**\n\n| Time  | Description | Instance 1 | Instance 2 |\n| :---: | :--- | :--- | :--- |\n| t1 | The document exists on both instances | JSON.GET doc $ <br /><br /> Result: <br /> '{\"grocery\": []}' | JSON.GET doc $ <br /><br /> Result: <br /> '{\"grocery\": []}' |\n| t2 | Add new elements to the array | <nobr>JSON.ARRAPPEND doc $.grocery ‘\"eggs\"’</nobr> | JSON.ARRAPPEND doc $.grocery ‘\"milk\"’ |\n| t3 | Add new elements to the array | JSON.ARRAPPEND doc $.grocery ‘\"ham\"’ | <nobr>JSON.ARRAPPEND doc $.grocery ‘\"flour\"’</nobr> |\n| t4 |  | JSON.GET doc $ <br /><br /> Result: <br /> {\"grocery\":[\"eggs\", \"ham\"]} | JSON.GET doc $ <br /><br /> Result: <br /> {\"grocery\":[\"milk\", \"flour\"]} |\n| t5 | Active-Active synchronization | – Sync – | – Sync – |\n| t6 | Merges the results from both instances | JSON.GET doc . <br /><br /> Result: <br /> {\"grocery\":[\"eggs\",\"ham\",\"milk\", \"flour\"]} | JSON.GET doc . <br /><br /> Result: <br /> {\"grocery\":[\"eggs\",\"ham\",\"milk\", \"flour\" ]} |"}],"examples":[{"id":"update-versus-clear-ex0","language":"sh","code":"JSON.SET doc $.colors '{}'","section_id":"update-versus-clear"},{"id":"update-versus-clear-ex1","language":"sh","code":"JSON.DEL doc $.colors.blue","section_id":"update-versus-clear"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/json/commands","title":"JSON commands","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json/commands/","summary":"Lists JSON commands and provides links to the command reference pages.","tags":["docs","operate","stack"],"last_updated":"2026-09-22T10:15:10+01:00","page_type":"content","content_hash":"0ca3d5f3748ef53cf7fffcd4a0055b18c16c18f8d1324856297f89aa31768c8d","sections":[{"id":"content","title":"Content","role":"content","text":"The following table lists JSON commands. See the command links for more information about each command's syntax, arguments, and examples.\n\n| Command | Redis<br />Software | Redis<br />Cloud | Description |\n|:--------|:----------------------|:-----------------|:------|\n| [JSON.ARRAPPEND](https://redis.io/docs/latest/commands/json.arrappend) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\"><nobr>&#x2705; Flexible & Annual</nobr></span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Appends an element to a JSON array. |\n| [JSON.ARRINDEX](https://redis.io/docs/latest/commands/json.arrindex) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Returns the index of a value's first occurrence in a JSON array. |\n| [JSON.ARRINSERT](https://redis.io/docs/latest/commands/json.arrinsert) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Inserts JSON values into a JSON array before the given index. |\n| [JSON.ARRLEN](https://redis.io/docs/latest/commands/json.arrlen) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Returns the length of a JSON array. |\n| [JSON.ARRPOP](https://redis.io/docs/latest/commands/json.arrpop) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Removes and returns an element located at the index in the JSON array. |\n| [JSON.ARRTRIM](https://redis.io/docs/latest/commands/json.arrtrim) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Trims a JSON array so that it contains only the specified inclusive range of elements. |\n| [JSON.CLEAR](https://redis.io/docs/latest/commands/json.clear) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Clears container values (arrays/objects) and sets numeric values to 0. |\n| [JSON.DEBUG](https://redis.io/docs/latest/commands/json.debug) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Debugging container command. |\n| [JSON.DEBUG HELP](https://redis.io/docs/latest/commands/json.debug-help) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Returns helpful information about the [JSON.DEBUG](https://redis.io/docs/latest/commands/json.debug) command. |\n| [JSON.DEBUG MEMORY](https://redis.io/docs/latest/commands/json.debug-memory) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Reports a JSON element's memory usage in bytes. |\n| [JSON.DEL](https://redis.io/docs/latest/commands/json.del) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Removes a JSON element. |\n| [JSON.FORGET](https://redis.io/docs/latest/commands/json.forget) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Removes a JSON element, the same as [JSON.DEL](https://redis.io/docs/latest/commands/json.del). |\n| [JSON.GET](https://redis.io/docs/latest/commands/json.get) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Returns the value of an element in JSON-serialized form. |\n| [JSON.MGET](https://redis.io/docs/latest/commands/json.mget) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Returns the values of multiple elements. |\n| [JSON.MERGE](https://redis.io/docs/latest/commands/json.merge) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Merge a given JSON value into matching paths. |\n| [JSON.NUMINCRBY](https://redis.io/docs/latest/commands/json.numincrby) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Increments the number stored at path by the specified number. |\n| [JSON.NUMMULTBY](https://redis.io/docs/latest/commands/json.nummultby) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Multiplies the number stored at path by the specified number. (deprecated as of RedisJSON v2.0) |\n| [JSON.OBJKEYS](https://redis.io/docs/latest/commands/json.objkeys) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Returns the keys contained in the specified JSON object. |\n| [JSON.OBJLEN](https://redis.io/docs/latest/commands/json.objlen) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Returns the number of keys in the specified JSON object. |\n| [JSON.RESP](https://redis.io/docs/latest/commands/json.resp) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Returns a JSON element in [Redis Serialization Protocol (RESP)](https://redis.io/docs/latest/develop/reference/protocol-spec) format. |\n| [JSON.SET](https://redis.io/docs/latest/commands/json.set) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Sets the value of a JSON element. |\n| [JSON.STRAPPEND](https://redis.io/docs/latest/commands/json.strappend) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Appends the given string to the specified key's existing strings. |\n| [JSON.STRLEN](https://redis.io/docs/latest/commands/json.strlen) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Returns the length of a string. |\n| [JSON.TOGGLE](https://redis.io/docs/latest/commands/json.toggle) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | If the boolean is `true`, changes it to `false`. If the boolean is `false`, changes it to `true`. |\n| [JSON.TYPE](https://redis.io/docs/latest/commands/json.type) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Returns a JSON element's type. |"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/json","title":"JSON","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json/","summary":"Redis Open Source adds support for JSON to Redis databases.","tags":["docs","operate","stack"],"last_updated":"2026-09-22T10:15:10+01:00","children":[{"id":"operate/oss_and_stack/stack-with-enterprise/json/commands","summary":"Lists JSON commands and provides links to the command reference pages.","title":"JSON commands","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json/commands/"},{"id":"operate/oss_and_stack/stack-with-enterprise/json/active-active","summary":"JSON support and conflict resolution rules for Active-Active databases.","title":"Store JSON in Active-Active databases","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json/active-active/"}],"page_type":"content","content_hash":"d965a51bdfc2a1dde086bb3d35f31a937aefbf1c67cd5885d1a521464651c10d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Open Source adds support for the [JSON data structure](http://www.json.org/) to Redis databases.\n\nApplications developed with the [source available version of RedisJSON](https://github.com/RedisJSON/RedisJSON) are 100%\ncompatible with Redis Software databases with JSON enabled."},{"id":"json-paths","title":"JSON paths","role":"content","text":"[Paths](https://redis.io/docs/latest/develop/data-types/json/path) let you traverse the structure of a JSON document, starting from the root, and interact only with the data you want. You can also use paths to perform operations on specific JSON elements.\n\nSince there is no standard for JSON path syntax, Redis Open Source implements its own."},{"id":"jsonpath-syntax","title":"JSONPath syntax","role":"content","text":"RedisJSON v2.0 and later support the [JSONPath syntax](https://redis.io/docs/latest/develop/data-types/json/path), which resembles [Goessner's design](https://goessner.net/articles/JsonPath/):\n  \n  - Paths start with a dollar sign (`$`), which represents the root of the JSON document.\n\n  - See the [JSONPath syntax table](https://redis.io/docs/latest/develop/data-types/json/path) to learn how to access various elements within a JSON document.\n\nThe following path refers to `headphones`, which is a child of `inventory` under the root:\n\n`$.inventory.headphones`\n  \nSee [JSONPath examples](https://redis.io/docs/latest/develop/data-types/json/path) for examples with more complex syntax."},{"id":"legacy-path-syntax","title":"Legacy path syntax","role":"content","text":"The [legacy path syntax](https://redis.io/docs/latest/develop/data-types/json/path#legacy-path-syntax) refers to the path implementation in RedisJSON v1. RedisJSON v2 still supports this legacy path syntax in addition to JSONPath syntax.\n\nThe legacy path syntax works as follows:\n\n  - A period character represents the root.\n  \n  - For paths to the root's children, it is optional to prefix the path with a period.\n\n  - Supports both dot notation and bracket notation for JSON object key access.\n  \nThe following paths refer to `headphones`, which is a child of `inventory` under the root:\n\n`.inventory.headphones`\n \n`inventory[\"headphones\"]`\n\n`['inventory'][\"headphones\"]`"},{"id":"key-name-rules","title":"Key name rules","role":"content","text":"You can only use the legacy path syntax to access JSON keys if they follow these name syntax rules:\n\n- Key names must begin with a letter, a dollar sign (`$`), or an underscore (`_`).\n- Key names can contain letters, digits, dollar signs, and underscores.\n- Key names are case-sensitive."},{"id":"index-and-search-json-documents","title":"Index and search JSON documents","role":"content","text":"You can index, search, and query stored JSON documents.\n\nFor more information about how to search and query JSON documents, see the [quick start](https://redis.io/docs/latest/develop/get-started/search-tutorial)."},{"id":"json-in-active-active-databases","title":"JSON in Active-Active databases","role":"content","text":"RedisJSON v2.2 and later support the JSON data structure as a conflict-free replicated data type [(CRDT)](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type) in [Active-Active Redis Software databases](https://redis.io/docs/latest/operate/rs/databases/active-active).\n\nFor details about how Redis Software resolves JSON operation conflicts that can arise when replicas attempt to sync, see the JSON [conflict resolution rules](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json/active-active#conflict-resolution-rules)."},{"id":"more-info","title":"More info","role":"content","text":"- [JSON quick start](https://redis.io/docs/latest/develop/data-types/json#use-redisjson)\n- [JSON commands](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json/commands)\n- [RedisJSON source](https://github.com/RedisJSON/RedisJSON)"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes","title":"Redis Open Source and modules release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/","summary":"Redis Open Source and modules release notes","tags":["docs","operate","stack"],"last_updated":"2026-09-17T09:01:36-07:00","children":[{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisce","summary":"Redis Open Source release notes for version 7.4 and later","title":"Redis Open Source release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisstack","summary":"Redis Stack release notes for version 7.2 and later","title":"Redis Stack release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisstack/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch","summary":"RediSearch release notes for version 2.10 and earlier","title":"RediSearch release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson","summary":"RedisJSON release notes for version 2.8 and earlier","title":"RedisJSON release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom","summary":"RedisBloom release notes for version 2.8 and earlier","title":"RedisBloom release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries","summary":"RedisTimeSeries release notes for version 1.12 and earlier","title":"RedisTimeSeries release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph","summary":"RedisGraph release notes for version 2.10 and earlier","title":"RedisGraph release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisgears","summary":"RedisGears release notes for version 1.2 and earlier","title":"RedisGears release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgears/"}],"page_type":"content","content_hash":"94c5e39c57f9936e8d19d307de2930b221190999310b9181fed6bfbce2e30851","sections":[{"id":"content","title":"Content","role":"content","text":"Find release notes for current and future product releases in their respective GitHub source code repositories. They are no longer maintained on this site:\n\n- [Redis Open Source](https://github.com/redis/redis/releases)\n- [RedisBloom](https://github.com/RedisBloom/RedisBloom/releases)\n- [RedisJSON](https://github.com/RedisJSON/RedisJSON/releases)\n- [RediSearch](https://github.com/RediSearch/RediSearch/releases)\n- [RedisTimeSeries](https://github.com/RedisTimeSeries/RedisTimeSeries/releases)\n\nSee the following release notes for older features, enhancements, and other changes to Redis Open Source capabilities.\n\n\n| Release notes | Description |\n| --- | --- |\n| [Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/) | Redis Open Source release notes for version 7.4 and later |\n| [Redis Stack](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisstack/) | Redis Stack release notes for version 7.2 and later |\n| [RediSearch](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/) | RediSearch release notes for version 2.10 and earlier |\n| [RedisJSON](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/) | RedisJSON release notes for version 2.8 and earlier |\n| [RedisBloom](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/) | RedisBloom release notes for version 2.8 and earlier |\n| [RedisTimeSeries](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/) | RedisTimeSeries release notes for version 1.12 and earlier |\n| [RedisGraph](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/) | RedisGraph release notes for version 2.10 and earlier |\n| [RedisGears](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgears/) | RedisGears release notes for version 1.2 and earlier |"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom","title":"RedisBloom release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/","summary":"RedisBloom release notes for version 2.8 and earlier","tags":["docs","operate","stack"],"last_updated":"2024-08-08T19:21:33+03:00","children":[{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes","summary":"RedisBloom 2.8 introduces improvements and bug fixes.","title":"RedisBloom 2.8 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes","summary":"RESP3 support. Bug fixes.","title":"RedisBloom 2.6 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes","summary":"Added t-digest - a probabilistic data structure for estimating quantiles based on a data stream or a large dataset of floating-point values.","title":"RedisBloom 2.4 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.2-release-notes","summary":"BF.INFO returns bloom filter details. CF.INFO returns cuckoo filter details. Scalable bloom and cuckoo filters. Configurable bucket size for cuckoo filters. CMS.INCRBY returns count.","title":"RedisBloom 2.2 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.2-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.0-release-notes","summary":"Added more probabilistic data structures, including top-K and count-min sketch.","title":"RedisBloom 2.0 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.0-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-1.1-release-notes","summary":"Updated version.","title":"RedisBloom 1.1 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-1.1-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-1.0-release-notes","summary":"First GA release of RedisBloom.","title":"RedisBloom 1.0 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-1.0-release-notes/"}],"page_type":"content","content_hash":"4513045d40bc9841646a15c7c2fe95d9f9384718710b50caa17916affa57e1cb","sections":[{"id":"content","title":"Content","role":"content","text":"| Version (Release date) | Major changes | Min Redis version | Min cluster version |\n| --- | --- | --- | --- |\n| [v2.8 (July 2024)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes/) | RedisBloom 2.8 introduces improvements and bug fixes. | 7.4 | 7.8 |\n| [v2.6 (July 2023)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes/) | RESP3 support. Bug fixes. | 7.2 | 7.2.4 |\n| [v2.4 (November 2022)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes/) | Added t-digest - a probabilistic data structure for estimating quantiles based on a data stream or a large dataset of floating-point values. | 6.0.16 | 6.2.8 |\n| [v2.2 (December 2019)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.2-release-notes/) | BF.INFO returns bloom filter details. CF.INFO returns cuckoo filter details. Scalable bloom and cuckoo filters. Configurable bucket size for cuckoo filters. CMS.INCRBY returns count. | 4.0.0 | 5.0.0 |\n| [v2.0 (June 2019)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.0-release-notes/) | Added more probabilistic data structures, including top-K and count-min sketch. | 4.0.0 | 5.0.0 |\n| [v1.1 (February 2019)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-1.1-release-notes/) | Updated version. | 4.0.0 | 5.0.0 |\n| [v1.0 (September 2017)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-1.0-release-notes/) | First GA release of RedisBloom. | 4.0.0 | 5.0.0 |"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-1.0-release-notes","title":"RedisBloom 1.0 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-1.0-release-notes/","summary":"First GA release of RedisBloom.","tags":["docs","operate","stack"],"last_updated":"2023-12-14T15:27:49+01:00","page_type":"content","content_hash":"7eb7b7e57ea679842e5e18f59e8de025d52a34fc937bc4bc83ebffbfd921fc7b","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RedisBloom v1.0.3 requires:\n\n- Minimum Redis compatibility version (database): 4.0\n- Minimum Redis Enterprise Software version (cluster): 5.0"},{"id":"v103-december-2017","title":"v1.0.3 (December 2017)","role":"content","text":"This contains a single fix, issue #[19](https://github.com/RedisBloom/RedisBloom/issues/19).\n\nFrom this version onwards, `EXISTS`/`MEXISTS` returns 0 if the (Redis) key does not exist in the database.  Earlier versions returned an error."},{"id":"v102-november-2017","title":"v1.0.2 (November 2017)","role":"content","text":"This fixes a build issue (fixed s3 config in circle yaml)."},{"id":"v100-september-2017","title":"v1.0.0 (September 2017)","role":"content","text":"This is the first GA release of ReBloom."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-1.1-release-notes","title":"RedisBloom 1.1 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-1.1-release-notes/","summary":"Updated version.","tags":["docs","operate","stack"],"last_updated":"2023-12-14T15:27:49+01:00","page_type":"content","content_hash":"e5699f881e2c489e9edd4c6e2de72fe12033686417ab92b454754031ef0ccf06","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RedisBloom v1.1.1 requires:\n\n- Minimum Redis compatibility version (database): 4.0.0\n- Minimum Redis Enterprise Software version (cluster): 5.0.0"},{"id":"redisbloom-111-21-february-2019","title":"RedisBloom 1.1.1 (21 February 2019)","role":"content","text":"Update version to 1.1.1"},{"id":"redisbloom-110-12-february-2019","title":"RedisBloom 1.1.0 (12 February 2019)","role":"content","text":"Release of 1.1.0"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.0-release-notes","title":"RedisBloom 2.0 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.0-release-notes/","summary":"Added more probabilistic data structures, including top-K and count-min sketch.","tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:33+01:00","page_type":"content","content_hash":"ba1d001e0e2d98e90c63158aed1d207b0a1ef6e778c8a75a10aa0ceea63c7afd","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RedisBloom v2.0.3 requires:\n\n- Minimum Redis compatibility version (database): 4.0.0\n- Minimum Redis Enterprise Software version (cluster): 5.0.0"},{"id":"v203-july-2019","title":"v2.0.3 (July 2019)","role":"content","text":"- Performance improvements:\n    - #[95](https://github.com/RedisBloom/RedisBloom/issues/95) Top-K - Reduce checks on heap, now only checks if item count is larger than minimum in heap.\n    - #[95](https://github.com/RedisBloom/RedisBloom/issues/95) Top-K - The power of decay was calculated every time. Changed to use a lookup table.\n- Major bug fix:\n    - #[88](https://github.com/RedisBloom/RedisBloom/issues/88) Replication available for newly added Top-K and Count-min sketch\n- Minor bug fixes:\n    - #[89](https://github.com/RedisBloom/RedisBloom/issues/89) Module update broke rdb files\n    - #[98](https://github.com/RedisBloom/RedisBloom/issues/98) Compilation for macOS"},{"id":"v200-june-2019","title":"v2.0.0 (June 2019)","role":"content","text":"We are proud to announce that we doubled the number of probabilistic data structures that are generally available in RedisBloom.  Full documentation is available on [redisbloom.io](https://redisbloom.io)\n\n- #[70](https://github.com/RedisBloom/RedisBloom/issues/70) Top-K\n    - [Commands](https://redis.io/docs/latest/develop/data-types/probabilistic/top-k)\n    - [Algorithm](https://www.usenix.org/conference/atc18/presentation/gong)\n\n- #[65](https://github.com/RedisBloom/RedisBloom/issues/65) Count-min Sketch\n    - [Commands](https://redis.io/docs/latest/develop/data-types/probabilistic/count-min-sketch)\n    - [Algorithm](https://en.wikipedia.org/wiki/Count%E2%80%93min_sketch)"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.2-release-notes","title":"RedisBloom 2.2 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.2-release-notes/","summary":"BF.INFO returns bloom filter details. CF.INFO returns cuckoo filter details. Scalable bloom and cuckoo filters. Configurable bucket size for cuckoo filters. CMS.INCRBY returns count.","tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:33+01:00","page_type":"content","content_hash":"2384ba4a6ddf99b54b0a11cf21884fb46c455438ced99d0ee7475c6f63192932","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RedisBloom v2.2.18 requires:\n\n- Minimum Redis compatibility version (database): 4.0.0\n- Minimum Redis Enterprise Software version (cluster): 5.0.0"},{"id":"v2218-july-2022","title":"v2.2.18 (July 2022)","role":"content","text":"This is a maintenance release for RedisBloom 2.2.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n    - [#481](https://github.com/RedisBloom/RedisBloom/issues/481) CF crashes on expansion 0\n    - [#478](https://github.com/RedisBloom/RedisBloom/pull/478) [`BF.INFO`](https://redis.io/docs/latest/commands/bf.info) reports an inaccurate result about the memory footprint"},{"id":"v2217-june-2022","title":"v2.2.17 (June 2022)","role":"content","text":"This is a maintenance release for RedisBloom 2.2.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails:\n\n- Bug fixes:\n\n    - [#451](https://github.com/RedisBloom/RedisBloom/pull/451) [`TOPK.INCRBY`](https://redis.io/docs/latest/commands/topk.incrby): fix calculation when old fingerprint count is reduced to 0\n    - [#462](https://github.com/RedisBloom/RedisBloom/pull/462) [`CF.RESERVE`](https://redis.io/docs/latest/commands/cf.reserve): fix potential crash on incorrect number of parameters\n    - [#434](https://github.com/RedisBloom/RedisBloom/pull/434) [`CF.INSERT`](https://redis.io/docs/latest/commands/cf.insert): fix crash when capacity is negative\n    - [#450](https://github.com/RedisBloom/RedisBloom/pull/450) [`TOPK.INCRBY`](https://redis.io/docs/latest/commands/topk.incrby): limit increment to 100,000 to prevent potential long freezes"},{"id":"v2215-may-2022","title":"v2.2.15 (May 2022)","role":"content","text":"This is a maintenance release for RedisBloom 2.2.\n\nUpdate urgency: None.\n\nDetails:\n\n- Enhancements:\n\n    - Added support for RedisBloom for AArch64 Linux"},{"id":"v2214-march-2022","title":"v2.2.14 (March 2022)","role":"content","text":"This is a maintenance release for RedisBloom 2.2.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n    - [#419](https://github.com/RedisBloom/RedisBloom/pull/419) `CMS.INCRBY` now returns an error on overflow\n    - [#412](https://github.com/RedisBloom/RedisBloom/pull/412) Fixed macOS build"},{"id":"v2212-february-2022","title":"v2.2.12 (February 2022)","role":"content","text":"This is a maintenance release for RedisBloom 2.2.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n    - [#392](https://github.com/RedisBloom/RedisBloom/pull/392) Fixed a potential crash on Bloom filter expansion when loading from [AOF](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence)\n    - [#404](https://github.com/RedisBloom/RedisBloom/pull/404) Fixed a potential crash on Cuckoo filter when calling `CF.LOADCHUNK` on a filter with `EXPANSION` greater than 1"},{"id":"v229-november-2021","title":"v2.2.9 (November 2021)","role":"content","text":"This is a maintenance release for RedisBloom 2.2.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n    - [#281](https://github.com/RedisBloom/RedisBloom/pull/281) Memory leak when loading TOPK keys from RDB.\n\n    - [#303](https://github.com/RedisBloom/RedisBloom/pull/303) `CMS.INCRBY` for negative or non-number\n\n    - [#354](https://github.com/RedisBloom/RedisBloom/pull/354) TOPK List Duplicate entries\n\n    - [#369](https://github.com/RedisBloom/RedisBloom/pull/369) Free only latest filter on `CF.COMPACT`\n\n    - [#371](https://github.com/RedisBloom/RedisBloom/pull/371) `CF.SCANDUMP` for cuckoo filter\n \n    - [#374](https://github.com/RedisBloom/RedisBloom/pull/374) ReplicateVerbatim for `CF.LOADCHUNK` (similar to [#309](https://github.com/RedisBloom/RedisBloom/pull/309))\n\n- Improvements:\n\n    - [#331](https://github.com/RedisBloom/RedisBloom/pull/331) Add `WITHCOUNT` flag to `TOPK.LIST`"},{"id":"v226-august-2021","title":"v2.2.6 (August 2021)","role":"content","text":"This is a maintenance release for version 1.0.\n\nUpdate urgency: `LOW` -  No need to upgrade unless there are new features you want to use.\n\nDetails:\n- Enhancements:\n    - #[333](https://github.com/redisbloom/redisbloom/issues/333) Support inter shards TLS capability"},{"id":"v224-july-2020","title":"v2.2.4 (July 2020)","role":"content","text":"Headlines:\n\n- This release improves overall stability and provides fixes for found issues.\n\nDetails:\n\n- Bug fixes:\n    - #[215](https://github.com/redisbloom/redisbloom/issues/215) Count-Min-Sketch CMS.INCRBY command to reply with correct min result.\n    - #[219](https://github.com/redisbloom/redisbloom/issues/219) Cuckoo Filter CF.DEBUG correct response formatting."},{"id":"v223-july-2020","title":"v2.2.3 (July 2020)","role":"content","text":"Headlines:\n\n- This release improves overall stability and provides fixes for found issues.\n\nDetails:\n\n- Bug fixes:\n    - [#217](https://github.com/RedisBloom/RedisBloom/pull/217) Client hung on `BF.INSERT` with multiple new items when a non-scaling filter is full."},{"id":"v222-march-2020","title":"v2.2.2 (March 2020)","role":"content","text":"Headlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n\nDetails:\n\n- Minor enhancements:\n    - Bloom\n        - #[180](https://github.com/redisbloom/redisbloom/issues/180) Removed the upper limit on Bloom Filter capacity."},{"id":"v221-january-2020","title":"v2.2.1 (January 2020)","role":"content","text":"Headlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n\nDetails:\n\n- Minor Enhancements:\n    - Bloom\n        - #[179](https://github.com/RedisBloom/RedisBloom/issues/179) Allow storing filters with size over 4294967295 (UINT32_MAX). Now 64 bits.\n        - #[177](https://github.com/RedisBloom/RedisBloom/issues/177) Prevent passing both `EXPANSION` *and* `NONSCALING` parameters to `BF.RESERVE`."},{"id":"v220-december-2019","title":"v2.2.0 (December 2019)","role":"content","text":"- Added functionality\n    - Bloom\n        - #[149](https://github.com/RedisBloom/RedisBloom/issues/149) `BF.INFO` returns details about a specific bloom filter\n        - Scalable\n            - #[153](https://github.com/RedisBloom/RedisBloom/issues/153) Ability to change the `EXPANSION` rate. This means each subsequent sub-filter will be `expansion` times larger as the previous one.\n            - #[160](https://github.com/RedisBloom/RedisBloom/issues/160) Optimise the scaling up of filter according to the [Scalable Bloom Filter](https://core.ac.uk/download/pdf/55607643.pdf) paper\n            - #[161](https://github.com/RedisBloom/RedisBloom/issues/161) Optional `NONSCALING` argument to disable scaling.  (This saves space since less hash functions are used)\n        - #[155](https://github.com/RedisBloom/RedisBloom/issues/155) Disabling rounding up functionality\n    - Cuckoo\n        - #[149](https://github.com/RedisBloom/RedisBloom/issues/149) `CF.INFO` returns details about a specific cuckoo filter\n        - Scalable\n            - #[138](https://github.com/RedisBloom/RedisBloom/issues/138) Configurable `EXPANSION`. When an additional filter is created, its size will be the size of the current filter multiplied by the `expansion`.  Higher expansion rates will result in lower error rates.\n            - #[142](https://github.com/RedisBloom/RedisBloom/issues/142) The maximum number of expansions limited to 32.\n            - #[131](https://github.com/RedisBloom/RedisBloom/issues/131) Configurable `MAXITERATIONS`. Number of attempts to swap buckets before declaring filter as full and creating an additional filter.\n        - #[135](https://github.com/RedisBloom/RedisBloom/issues/135) Configurable  `BUCKETSIZE`. Number of items in each bucket. Higher bucket size value improves the fill rate but result in a higher error rate and slightly slower operation speed.\n        - #[142](https://github.com/RedisBloom/RedisBloom/issues/142) use of 64bit hash function\n        - #[136](https://github.com/RedisBloom/RedisBloom/issues/136) expose compaction of filters in the API\n    - CMS\n        - #[97](https://github.com/RedisBloom/RedisBloom/issues/97) `CMS.INCRBY` returns count instead of ‘OK’\n\n- Minor bug fixes\n    - Bloom\n        - #[154](https://github.com/RedisBloom/RedisBloom/issues/154) Check error rate is 1< (cannot be equal)\n    - Cuckoo\n        - #[134](https://github.com/RedisBloom/RedisBloom/issues/134) Added `CuckooInsert_MemAllocFailed` exception\n        - #[130](https://github.com/RedisBloom/RedisBloom/issues/130) Number of deletes wasn’t saved to RDB\n    - General\n        - #[117](https://github.com/RedisBloom/RedisBloom/issues/117) Using `RMUtil_RegisterWriteDenyOOMCmd`\n        - #[121](https://github.com/RedisBloom/RedisBloom/issues/121) Moved `ReplicaVerbatim` to end of functions"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes","title":"RedisBloom 2.4 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes/","summary":"Added t-digest - a probabilistic data structure for estimating quantiles based on a data stream or a large dataset of floating-point values.","tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:33+01:00","page_type":"content","content_hash":"8d17912d6da4d5a4d4d5ab1008e55bdc251c95ea30218ae8247883dd0fdc2372","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RedisBloom v2.4.15 requires:\n\n- Minimum Redis compatibility version (database): 6.0.16\n- Minimum Redis Enterprise Software version (cluster): 6.2.8"},{"id":"v2415-april-2025","title":"v2.4.15 (April 2025)","role":"content","text":"This is a maintenance release for RedisBloom 2.4.\n\nUpdate urgency: `LOW`: No need to upgrade unless there are new features you want to use.\n\nDetails:\n\nImprovements:\n- [#865](https://github.com/redisbloom/redisbloom/pull/865) Added support for Azure Linux 3 (MOD-9171)"},{"id":"v2414-february-2025","title":"v2.4.14 (February 2025)","role":"content","text":"This is a maintenance release for RedisBloom 2.4.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n  - [#828](https://github.com/redisbloom/redisbloom/pull/828) `CMS.INCRBY` does not notify `WATCH`ers or client-side caching (MOD-8193)\n  - [#827](https://github.com/redisbloom/redisbloom/pull/827) Top-K - suboptimal results after RDB load due to missing initialization (MOD-8194)"},{"id":"v2413-january-2025","title":"v2.4.13 (January 2025)","role":"content","text":"This is a maintenance release for RedisBloom 2.4.\n\nUpdate urgency: `SECURITY`: There are security fixes in the release.\n\nDetails:\n\n- **Security and privacy:**\n  - [#845](https://github.com/redisbloom/redisbloom/pull/845) (CVE-2024-53993) CMS: potential out-of-bounds write (MOD-6970)\n\n- Bug fixes:\n  - [#845](https://github.com/redisbloom/redisbloom/pull/845) `CMS.MERGE` crashes or hangs on negative number of keys (MOD-6964)\n\n- Improvements:\n  - [#831](https://github.com/redisbloom/redisbloom/pull/831) Added support for Ubuntu 22 and macOS 13 and 14"},{"id":"v249-march-2024","title":"v2.4.9 (March 2024)","role":"content","text":"This is a maintenance release for RedisBloom 2.4.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n  - [#753](https://github.com/RedisBloom/RedisBloom/issues/753) Potential crash on `CMS.MERGE` when using invalid arguments"},{"id":"v248-january-2024","title":"v2.4.8 (January 2024)","role":"content","text":"This is a maintenance release for RedisBloom 2.4.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails:\n\n- Bug fixes:\n\n  - [#727](https://github.com/RedisBloom/RedisBloom/pull/727) Additional fixes for potential crash on `CF.LOADCHUNK` (MOD-6344)"},{"id":"v247-january-2024","title":"v2.4.7 (January 2024)","role":"content","text":"This is a maintenance release for RedisBloom 2.4.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails:\n\n- Bug fixes:\n\n  - [#735](https://github.com/RedisBloom/RedisBloom/pull/735) Potential crash on `CF.RESERVE` (MOD-6343)\n  - [#727](https://github.com/RedisBloom/RedisBloom/pull/727) Potential crash on `CF.LOADCHUNK` (MOD-6344)"},{"id":"v246-december-2023","title":"v2.4.6 (December 2023)","role":"content","text":"This is a maintenance release for RedisBloom 2.4.\n\nUpdate urgency: `LOW`: No need to upgrade unless there are new features you want to use.\n\nDetails:\n\n- Bug fixes:\n\n  - [#707](https://github.com/RedisBloom/RedisBloom/pull/707) Top-K: `TOPK.ADD` and `TOPK.QUERY` crash when an item name is an empty string (RED-114676)\n\n- Improvements:\n\n  - [#706](https://github.com/RedisBloom/RedisBloom/pull/706) Added support for CBL-Mariner 2 (MOD-6200)"},{"id":"v245-april-2023","title":"v2.4.5 (April 2023)","role":"content","text":"This is a maintenance release for RedisBloom 2.4.\n\nUpdate urgency: `LOW`: No need to upgrade unless there are new features you want to use.\n\nDetails:\n\n- Improvements:\n\n  - Internal changes for supporting future Redis Enterprise releases"},{"id":"v244-february-2023","title":"v2.4.4 (February 2023)","role":"content","text":"This is a maintenance release for RedisBloom 2.4.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n  - [#609](https://github.com/RedisBloom/RedisBloom/issues/609) [CF.INFO](https://redis.io/docs/latest/commands/cf.info) - incorrect information for large filters\n\n- Improvements:\n\n  - [#389](https://github.com/RedisBloom/RedisBloom/issues/389) Introduce [BF.CARD](https://redis.io/docs/latest/commands/bf.card) to retrieve the cardinality of a Bloom filter or 0 when such key does not exist"},{"id":"v24-ga-v243-november-2022","title":"v2.4 GA (v2.4.3) (November 2022)","role":"content","text":"This is the General Availability release of RedisBloom 2.4."},{"id":"highlights","title":"Highlights","role":"content","text":"RedisBloom 2.4 introduces a new sketch data structure: **t-digest**."},{"id":"whats-new-in-24","title":"What's new in 2.4","role":"content","text":"[t-digest](https://www.sciencedirect.com/science/article/pii/S2665963820300403) is a probabilistic data structure for estimating quantiles based on a data stream or a large dataset of floating-point values. It can be used to answer the following questions:\n\n- What fraction of the values in the data stream are smaller than a given value?\n- How many values in the data stream are smaller than a given value?\n- Which value is smaller than _p_ percent of the values in the data stream? (what is the _p_-percentile value)?\n- What is the mean value between the _p1_-percentile value and the _p2_-percentile value?\n- What is the value of the _n_-th smallest / largest value in the data stream? (what is the value with [reverse] rank _n_)?\n\nAs for any other probabilistic data structures, t-digest requires sublinear space and has controllable space-accuracy tradeoffs.\n\nUsing t-digest is simple and straightforward:\n\n* **Creating a sketch and adding observations**\n\n  `TDIGEST.CREATE key [COMPRESSION compression]` initializes a new t-digest sketch (and errors if the key already exists). The `COMPRESSION` argument is used to specify the tradeoff between accuracy and memory consumption. The default is 100. Higher values mean more accuracy.\n\n  `TDIGEST.ADD key value...` adds new observations (floating-point values) to the sketch. You can repeat calling [TDIGEST.ADD](https://redis.io/docs/latest/commands/tdigest.add) whenever new observations are available.\n\n* **Estimating fractions or ranks by values**\n\n  Use `TDIGEST.CDF key value...` to retrieve, for each input **value**, an estimation of the **fraction** of (observations **smaller** than the given value + half the observations equal to the given value).\n\n  `TDIGEST.RANK key value...` is similar to [TDIGEST.CDF](https://redis.io/docs/latest/commands/tdigest.cdf), but used for estimating the number of observations instead of the fraction of observations. More accurately it returns, for each input **value**, an estimation of the **number** of (observations **smaller** than a given value + half the observations equal to the given value).\n\n  And lastly, `TDIGEST.REVRANK key value...` is similar to [TDIGEST.RANK](https://redis.io/docs/latest/commands/tdigest.rank), but returns, for each input **value**, an estimation of the **number** of (observations **larger** than a given value + half the observations equal to the given value).\n\n* **Estimating values by fractions or ranks**\n\n  `TDIGEST.QUANTILE key fraction...` returns, for each input **fraction**, an estimation of the **value** (floating point) that is **smaller** than the given fraction of observations.\n\n  `TDIGEST.BYRANK key rank...` returns, for each input **rank**, an estimation of the **value** (floating point) with that rank.\n\n  `TDIGEST.BYREVRANK key rank...` returns, for each input **reverse rank**, an estimation of the **value** (floating point) with that reverse rank.\n\n* **Estimating trimmed mean**\n\n  Use `TDIGEST.TRIMMED_MEAN key lowFraction highFraction` to retrieve an estimation of the mean value between the specified fractions.\n\n  This is especially useful for calculating the average value ignoring outliers. For example, calculating the average value between the 20th percentile and the 80th percentile.\n\n* **Merging sketches**\n\n  Sometimes it is useful to merge sketches. For example, suppose we measure latencies for 3 servers, and we want to calculate the 90%, 95%, and 99% latencies for all the servers combined.\n\n  `TDIGEST.MERGE destKey numKeys sourceKey... [COMPRESSION compression] [OVERRIDE]` merges multiple sketches into a single sketch.\n\n  If `destKey` does not exist, a new sketch is created.\n\n  If `destKey` is an existing sketch, its values are merged with the values of the source keys. To override the destination key contents, use `OVERRIDE`.\n\n* **Retrieving sketch information**\n\n  Use `TDIGEST.MIN` key and `TDIGEST.MAX key` to retrieve the minimal and maximal values in the sketch, respectively.\n\n  Both return NaN (Not a Number) when the sketch is empty.\n\n  Both commands return accurate results and are equivalent to `TDIGEST.BYRANK key 0` and `TDIGEST.BYREVRANK key 0` respectively.\n\n  Use `TDIGEST.INFO key` to retrieve some additional information about the sketch.\n\n* **Resetting a sketch**\n\n  `TDIGEST.RESET key` empties the sketch and reinitializes it."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes","title":"RedisBloom 2.6 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes/","summary":"RESP3 support. Bug fixes.","tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:33+01:00","page_type":"content","content_hash":"6c441209f03859e92ac21f67337c0c60070d4aa5e9e7af09e6d80689d581cf56","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RedisBloom v2.6.18 requires:\n\n- Minimum Redis compatibility version (database): 7.2\n- Minimum Redis Enterprise Software version (cluster): 7.2.4"},{"id":"v2618-april-2025","title":"v2.6.18 (April 2025)","role":"content","text":"This is a maintenance release for RedisBloom 2.6.\n\nUpdate urgency: `LOW`: No need to upgrade unless there are new features you want to use.\n\nDetails:\n\nImprovements:\n- [#865](https://github.com/redisbloom/redisbloom/pull/865) Added support for Azure Linux 3 (MOD-9171)"},{"id":"v2617-february-2025","title":"v2.6.17 (February 2025)","role":"content","text":"This is a maintenance release for RedisBloom 2.6.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n  - [#828](https://github.com/redisbloom/redisbloom/pull/828) `CMS.INCRBY` does not notify `WATCH`ers or client-side caching (MOD-8193)\n  - [#827](https://github.com/redisbloom/redisbloom/pull/827) Top-K - suboptimal results after RDB load due to missing initialization (MOD-8194)"},{"id":"v2616-january-2025","title":"v2.6.16 (January 2025)","role":"content","text":"This is a maintenance release for RedisBloom 2.6.\n\nUpdate urgency: `SECURITY`: There are security fixes in the release.\n\nDetails:\n\n- **Security and privacy:**\n  - [#844](https://github.com/redisbloom/redisbloom/pull/844) (CVE-2024-53993) CMS: potential out-of-bounds write (MOD-6970)\n\n- Bug fixes:\n  - [#844](https://github.com/redisbloom/redisbloom/pull/844) `CMS.MERGE` crashes or hangs on negative number of keys (MOD-6964)\n\n- Improvements:\n  - [#830](https://github.com/redisbloom/redisbloom/pull/830) Added support for Ubuntu 22 and macOS 13 and 14"},{"id":"v2613-july-2024","title":"v2.6.13 (July 2024)","role":"content","text":"This is a maintenance release for RedisBloom 2.6\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n  - [#720](https://github.com/RedisBloom/RedisBloom/pull/720) `BF` crashes on high error rate (MOD-6268)\n  - [#773](https://github.com/RedisBloom/RedisBloom/pull/773) `CMS.MERGE`: reply with an error on overflow and underflow (MOD-6962)"},{"id":"v2612-march-2024","title":"v2.6.12 (March 2024)","role":"content","text":"This is a maintenance release for RedisBloom 2.6.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n  - [#753](https://github.com/RedisBloom/RedisBloom/issues/753) Potential crash on `CMS.MERGE` when using invalid arguments"},{"id":"v2611-january-2024","title":"v2.6.11 (January 2024)","role":"content","text":"This is a maintenance release for RedisBloom 2.6.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails:\n\n- Bug fixes:\n\n  - [#727](https://github.com/RedisBloom/RedisBloom/pull/727) Additional fixes for potential crash on `CF.LOADCHUNK` (MOD-6344)"},{"id":"v2610-january-2024","title":"v2.6.10 (January 2024)","role":"content","text":"This is a maintenance release for RedisBloom 2.6.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails:\n\n- Bug fixes:\n\n  - [#735](https://github.com/RedisBloom/RedisBloom/pull/735) Potential crash on `CF.RESERVE` (MOD-6343)\n  - [#727](https://github.com/RedisBloom/RedisBloom/pull/727) Potential crash on `CF.LOADCHUNK` (MOD-6344)"},{"id":"v269-december-2023","title":"v2.6.9 (December 2023)","role":"content","text":"This is a maintenance release for RedisBloom 2.6.\n\nUpdate urgency: `LOW`: No need to upgrade unless there are new features you want to use.\n\nDetails:\n\n- Bug fixes:\n\n  - [#707](https://github.com/RedisBloom/RedisBloom/pull/707) Top-K: `TOPK.ADD` and `TOPK.QUERY` crash when an item name is an empty string (RED-114676)"},{"id":"v268-october-2023","title":"v2.6.8 (October 2023)","role":"content","text":"This is a maintenance release for RedisBloom 2.6.\n\nUpdate urgency: `LOW`: No need to upgrade unless there are new features you want to use.\n\nDetails:\n\n- Improvements:\n\n  - [#684](https://github.com/RedisBloom/RedisBloom/pull/684), [#685](https://github.com/RedisBloom/RedisBloom/pull/685) Added support for CBL-Mariner 2\n  - [#677](https://github.com/RedisBloom/RedisBloom/pull/677), [#678](https://github.com/RedisBloom/RedisBloom/pull/678) Added support for Rocky Linux 9 and RHEL9"},{"id":"v26-ga-v263-july-2023","title":"v2.6 GA (v2.6.3) (July 2023)","role":"content","text":"This is the General Availability release of RedisBloom 2.6."},{"id":"headlines","title":"Headlines","role":"content","text":"RedisBloom 2.6 introduces support for RESP3 and bug fixes."},{"id":"details","title":"Details","role":"content","text":"Improvements: \n\n- [#664](https://github.com/RedisBloom/RedisBloom/pull/664) `TOPK.ADD`, `TOPK.INCRBY`, and `TOPK.LIST` reply with blob string instead of simple string.\n\n> [!NOTE]\n> - The version inside Redis will be 2.6.3 in semantic versioning. Since the version of a module in Redis is numeric, we could not add a GA flag.\n>\n> - Minimal Redis version: 7.2"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes","title":"RedisBloom 2.8 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes/","summary":"RedisBloom 2.8 introduces improvements and bug fixes.","tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:33+01:00","page_type":"content","content_hash":"027ae9a82cf140ca4bd97512bc65092e0d7f1dc22fd308608154798b663b0c2e","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RedisBloom v2.8.10 requires:\n\n- Minimum Redis compatibility version (database): 7.4\n- Minimum Redis Enterprise Software version (cluster): 7.8"},{"id":"v2810-september-2025","title":"v2.8.10 (September 2025)","role":"content","text":"This is a maintenance release for RedisBloom 2.8.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\nBug fixes:\n- [#886](https://github.com/redisbloom/redisbloom/pull/886) - `TDIGEST.CREATE` crashes (OOM) on huge initialization values (MOD-10840)."},{"id":"v287-april-2025","title":"v2.8.7 (April 2025)","role":"content","text":"This is a maintenance release for RedisBloom 2.8\n\nUpdate urgency: `LOW`: No need to upgrade unless there are new features you want to use.\n\nDetails:\n\nImprovements:\n- [#865](https://github.com/redisbloom/redisbloom/pull/865) Added support for Azure Linux 3 (MOD-9171)"},{"id":"v285-january-2025","title":"v2.8.5 (January 2025)","role":"content","text":"This is a maintenance release for RedisBloom 2.8.\n\nUpdate urgency: `SECURITY`: There are security fixes in the release.\n\nDetails:\n\n- **Security and privacy:**\n  - [#843](https://github.com/redisbloom/redisbloom/pull/843) (CVE-2024-53993) CMS: potential out-of-bounds write (MOD-6970)\n\n- Bug fixes:\n  - [#843](https://github.com/redisbloom/redisbloom/pull/843) `CMS.MERGE` crashes or hangs on negative number of keys (MOD-6964)\n  - [#699](https://github.com/redisbloom/redisbloom/pull/699) `BF.RESERVE` crashes (OOM) on huge initialization values (MOD-7057)\n  - [#843](https://github.com/redisbloom/redisbloom/pull/843) `CF.RESERVE` crashes (OOM) on huge initialization values (MOD-7058)\n  - [#843](https://github.com/redisbloom/redisbloom/pull/843) `TOPK.RESERVE` crashes (OOM) on huge initialization values (MOD-7059)\n  - [#843](https://github.com/redisbloom/redisbloom/pull/843) `CMS.INITBYDIM` and `CMS.INITBYPROB` crash (OOM) on huge initialization values (MOD-7060)\n  - [#828](https://github.com/redisbloom/redisbloom/pull/828) `CMS.INCRBY` does not notify `WATCH`ers or client-side caching (MOD-8193)\n  - [#827](https://github.com/redisbloom/redisbloom/pull/827) Top-K - suboptimal results after RDB load due to missing initialization (MOD-8194)\n\n- Improvements:\n  - [#801](https://github.com/redisbloom/redisbloom/pull/801) Support active memory defragmentation (MOD-7890)\n  - [#829](https://github.com/redisbloom/redisbloom/pull/829) Added support for Ubuntu 22 and macOS 13 and 14"},{"id":"v28-ga-v282-july-2024","title":"v2.8 GA (v2.8.2) (July 2024)","role":"content","text":"This is the General Availability release of RedisBloom 2.8"},{"id":"headlines","title":"Headlines:","role":"content","text":"RedisBloom 2.8 introduces improvements and bug fixes."},{"id":"details","title":"Details","role":"content","text":"- Bug fixes\n\n  - [#773](https://github.com/RedisBloom/RedisBloom/pull/773) `CMS.MERGE`: reply with an error on overflow and underflow (MOD-6962)\n  - [#720](https://github.com/RedisBloom/RedisBloom/pull/720) `BF` crashes on high error rate (MOD-6268)\n\n> [!NOTE]\n> - The version inside Redis will be 2.8.2 in semantic versioning. Since the version of a module in Redis is numeric, we could not add a GA flag.\n> - Minimal Redis version: 7.4"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisce","title":"Redis Open Source release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/","summary":"Redis Open Source release notes for version 7.4 and later","tags":["docs","operate","stack"],"last_updated":"2025-05-01T06:50:58-07:00","children":[{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.12-release-notes","summary":"Redis Open Source 8.12 release notes.","title":"Redis Open Source 8.12 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.12-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.10-release-notes","summary":"Redis Open Source 8.10 release notes.","title":"Redis Open Source 8.10 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.10-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.8-release-notes","summary":"Redis Open Source 8.8 release notes.","title":"Redis Open Source 8.8 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.8-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.6-release-notes","summary":"Redis Open Source 8.6 release notes.","title":"Redis Open Source 8.6 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.6-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.4-release-notes","summary":"Redis Open Source 8.4 release notes.","title":"Redis Open Source 8.4 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.4-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.2-release-notes","summary":"Redis Open Source 8.2 release notes.","title":"Redis Open Source 8.2 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.2-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.0-release-notes","summary":"Redis Open Source 8.0 release notes.","title":"Redis Open Source 8.0 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.0-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisce-7.4-release-notes","summary":"Redis Community Edition 7.4 release notes.","title":"Redis Community Edition 7.4 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisce-7.4-release-notes/"}],"page_type":"content","content_hash":"69402bbaa77c17256f9c1e0643f20f1451a9240a60e29c0ef77fb7cf44a607ed","sections":[{"id":"content","title":"Content","role":"content","text":"| Version (Release date) | Major changes |\n| --- | --- |\n| [v8.12-RC1 (October 2026)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.12-release-notes/) | Redis Open Source 8.12 release notes. |\n| [v8.10.0 (July 2026)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.10-release-notes/) | Redis Open Source 8.10 release notes. |\n| [v8.8.0 (May 2026)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.8-release-notes/) | Redis Open Source 8.8 release notes. |\n| [v8.6.0 (February 2026)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.6-release-notes/) | Redis Open Source 8.6 release notes. |\n| [v8.4.0 (November 2025)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.4-release-notes/) | Redis Open Source 8.4 release notes. |\n| [v8.2.0 (August 2025)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.2-release-notes/) | Redis Open Source 8.2 release notes. |\n| [v8.0.0 (May 2025)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.0-release-notes/) | Redis Open Source 8.0 release notes. |\n| [v7.4.0 (July 2024)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisce-7.4-release-notes/) | Redis Community Edition 7.4 release notes. |"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisce-7.4-release-notes","title":"Redis Community Edition 7.4 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisce-7.4-release-notes/","summary":"Redis Community Edition 7.4 release notes.","tags":["docs","operate","stack"],"last_updated":"2026-08-17T13:21:31-07:00","page_type":"content","content_hash":"4f545b61c9bad0f1dc499ab691d7113fea9a6444a093f633d236b8c4a7062dad","sections":[{"id":"redis-community-edition-7411-august-2026","title":"Redis Community Edition 7.4.11 (August 2026)","role":"overview","text":"Update urgency: `SECURITY`: There are security fixes in the release."},{"id":"security-fixes","title":"Security fixes","role":"security","text":"- Use-after-free in the TLS pending-data list when a command closes another pending connection.\n- [#15478](https://github.com/redis/redis/pull/15478) ACL key permission bypass in `SORT`, `GEORADIUS`/`GEORADIUSBYMEMBER` and `XREAD`/`XREADGROUP`: the keys validated by ACL could differ from the keys the command actually accesses.\n- [#14847](https://github.com/redis/redis/pull/14847) Out-of-bounds `argv` access during key extraction when checking ACL permissions of a `KEYNUM` keyspec command (for example, `EVAL`) with wrong arity.\n- A malicious RDB payload with an out-of-range `SLOT_INFO slot id` causes memory corruption during RDB loading, which may lead to Remote Code Execution.\n- [#15594](https://github.com/redis/redis/pull/15594) Use-after-free in the blocked-client list when reprocessing a command evicts another client blocked on the same key."},{"id":"redis-community-edition-7410-july-2026","title":"Redis Community Edition 7.4.10 (July 2026)","role":"content","text":"Update urgency: `SECURITY`: There is a security fix in the release."},{"id":"security-fixes-1","title":"Security fixes","role":"security","text":"- A crafted stream `RESTORE` payload can make two consumers share the same NACK, leading to a use-after-free that may result in Remote Code Execution."},{"id":"redis-community-edition-749-may-2026","title":"Redis Community Edition 7.4.9 (May 2026):","role":"content","text":"Update urgency: `SECURITY`: There are security fixes in the release."},{"id":"security-fixes-2","title":"Security fixes","role":"security","text":"- (CVE-2026-23479) Use-After-Free in unblock client flow may lead to Remote Code Execution.\n- (CVE-2026-25243) Invalid memory access in `RESTORE` may lead to Remote Code Execution.\n- (CVE-2026-23631) Lua Use-After-Free may lead to remote code execution."},{"id":"bug-fixes","title":"Bug fixes","role":"content","text":"- `SUBSCRIBE`, `PSUBSCRIBE`, `SSUBSCRIBE`: crash on OOM (RED-167788).\n- `CONFIG SET`: some settings allow invalid characters (RED-167787).\n- `SCRIPT DEBUG`: potential crash on scripts (RED-175507)."},{"id":"redis-community-edition-748-february-2026","title":"Redis Community Edition 7.4.8 (February 2026)","role":"content","text":"Update urgency: `SECURITY`: There are security fixes in the release."},{"id":"security-fixes-3","title":"Security fixes","role":"security","text":"- A user can manipulate data read by a connection by injecting `\\r\\n` sequences into a Redis error reply."},{"id":"redis-community-edition-747-november-2025","title":"Redis Community Edition 7.4.7 (November 2025)","role":"content","text":"Update urgency: `HIGH`: There is a critical bug that may affect a subset of users."},{"id":"bug-fixes-1","title":"Bug fixes","role":"content","text":"- Potential crash on HyperLogLog with 2GB+ entries"},{"id":"redis-community-edition-746-october-2025","title":"Redis Community Edition 7.4.6 (October 2025)","role":"content","text":"Update urgency: `SECURITY`: There are security fixes in the release."},{"id":"security-fixes-4","title":"Security fixes","role":"security","text":"- (CVE-2025-49844) A Lua script may lead to remote code execution\n- (CVE-2025-46817) A Lua script may lead to integer overflow and potential RCE\n- (CVE-2025-46818) A Lua script can be executed in the context of another user\n- (CVE-2025-46819) LUA out-of-bound read"},{"id":"bug-fixes-2","title":"Bug fixes","role":"content","text":"- [#14330](https://github.com/redis/redis/pull/14330) Potential use-after-free after pubsub and Lua defrag\n- [#14319](https://github.com/redis/redis/pull/14319) Potential crash on Lua script defrag\n- [#14164](https://github.com/redis/redis/pull/14164) Prevent `CLIENT UNBLOCK` from unblocking `CLIENT PAUSE`\n- [#14165](https://github.com/redis/redis/pull/14165) Endless client blocking for blocking commands\n- [#14163](https://github.com/redis/redis/pull/14163) `EVAL` crash when error table is empty\n- [#14227](https://github.com/redis/redis/pull/14227) `HINCRBYFLOAT` removes field expiration on replica"},{"id":"redis-community-edition-745-july-2025","title":"Redis Community Edition 7.4.5 (July 2025)","role":"content","text":"Update urgency: `SECURITY`: There are security fixes in the release."},{"id":"security-fixes-5","title":"Security fixes","role":"security","text":"* (CVE-2025-27151) redis-check-aof may lead to stack overflow and potential RCE"},{"id":"bug-fixes-3","title":"Bug fixes","role":"content","text":"- [#13966](https://github.com/redis/redis/pull/13966), [#13932](https://github.com/redis/redis/pull/13932) `CLUSTER SLOTS` - TLS port update not reflected in CLUSTER SLOTS\n- [#13958](https://github.com/redis/redis/pull/13958) `XTRIM`, `XADD` - incorrect lag due to trimming stream"},{"id":"redis-community-edition-744-may-2025","title":"Redis Community Edition 7.4.4 (May 2025):","role":"content","text":"Update urgency: `SECURITY`: There are security fixes in the release."},{"id":"security-fixes-6","title":"Security fixes","role":"security","text":"* (CVE-2025-27151) redis-check-aof may lead to stack overflow and potential RCE."},{"id":"bug-fixes-4","title":"Bug fixes","role":"content","text":"- [#13966](https://github.com/redis/redis/pull/13966), [#13932](https://github.com/redis/redis/pull/13932) `CLUSTER SLOTS` - TLS port update not reflected in CLUSTER SLOTS.\n- [#13958](https://github.com/redis/redis/pull/13958) `XTRIM`, `XADD` - incorrect lag due to trimming stream."},{"id":"redis-community-edition-743-april-2025","title":"Redis Community Edition 7.4.3 (April 2025)","role":"content","text":"Update urgency: `SECURITY`: There are security fixes in the release."},{"id":"security-fixes-7","title":"Security fixes","role":"security","text":"* (CVE-2025-21605) An unauthenticated client can cause an unlimited growth of output buffers."},{"id":"bug-fixes-5","title":"Bug fixes","role":"content","text":"* [#13661](https://github.com/redis/redis/pull/13661) `FUNCTION FLUSH` - memory leak when using jemalloc.\n* [#13793](https://github.com/redis/redis/pull/13793) `WAITAOF` returns prematurely.\n* [#13853](https://github.com/redis/redis/pull/13853) `SLAVEOF` - crash when clients are blocked on lazy free.\n* [#13863](https://github.com/redis/redis/pull/13863) `RANDOMKEY` - infinite loop during client pause.\n* [#13877](https://github.com/redis/redis/pull/13877) ShardID inconsistency when both primary and replica support it."},{"id":"redis-community-edition-742-jan-2025","title":"Redis Community Edition 7.4.2 (Jan 2025)","role":"content","text":"Upgrade urgency SECURITY: See security fixes below."},{"id":"security-fixes-8","title":"Security fixes","role":"security","text":"- (CVE-2024-46981) Lua script commands may lead to remote code execution\n- (CVE-2024-51741) Denial-of-service due to malformed ACL selectors"},{"id":"bug-fixes-6","title":"Bug fixes","role":"content","text":"- [#13627](https://github.com/redis/redis/pull/13627) Crash on module memory defragmentation\n- [#13338](https://github.com/redis/redis/pull/13338) Streams: `XINFO` lag field is wrong when tombstone is after the `last_id` of the consume group\n- [#13473](https://github.com/redis/redis/pull/13473) Streams: `XTRIM` does not update the maximal tombstone, leading to an incorrect lag\n- [#13470](https://github.com/redis/redis/pull/13470) `INFO` after `HDEL` show wrong number of hash keys with expiration\n- [#13476](https://github.com/redis/redis/pull/13476) Fix a race condition in the `cache_memory` of `functionsLibCtx`\n- [#13626](https://github.com/redis/redis/pull/13626) Memory leak on failed RDB loading\n- [#13539](https://github.com/redis/redis/pull/13539) Hash: fix key ref for a hash that no longer has fields with expiration on `RENAME`/`MOVE`/`SWAPDB`/`RESTORE`\n- [#13443](https://github.com/redis/redis/pull/13443) Cluster: crash when loading cluster config\n- [#13422](https://github.com/redis/redis/pull/13422) Cluster: `CLUSTER SHARDS` returns empty array\n- [#13465](https://github.com/redis/redis/pull/13465) Cluster: incompatibility with older node versions\n- [#13608](https://github.com/redis/redis/pull/13608) Cluster: `SORT ... GET #`: incorrect error message"},{"id":"redis-community-edition-741-october-2024","title":"Redis Community Edition 7.4.1 (October 2024)","role":"content","text":"Upgrade urgency SECURITY: See security fixes below."},{"id":"security-fixes-9","title":"Security fixes","role":"security","text":"* (CVE-2024-31449) Lua library commands may lead to stack overflow and potential RCE.\n* (CVE-2024-31227) Potential Denial-of-service due to malformed ACL selectors.\n* (CVE-2024-31228) Potential Denial-of-service due to unbounded pattern matching.\n\nFor more information, see the [Redis blog post](https://redis.io/blog/security-advisory-cve-2024-31449-cve-2024-31227-cve-2024-31228/) about these vulnerabilities."},{"id":"redis-community-edition-74-july-2024","title":"Redis Community Edition 7.4 (July 2024)","role":"content","text":"This is the General Availability release of Redis Community Edition 7.4.\n\n**Changes to new 7.4 features (compared to 7.4 RC2)**\n* [#13391](https://github.com/redis/redis/pull/13391),[#13438](https://github.com/redis/redis/pull/13438) Hash - expiration of individual fields: RDB file format changes\n* [#13372](https://github.com/redis/redis/pull/13372) Hash - expiration of individual fields: rename and fix counting of `expired_subkeys` metric\n* [#13372](https://github.com/redis/redis/pull/13372) Hash - expiration of individual fields: rename `INFO` keyspace field to `subexpiry`\n\n**Configuration parameters**\n* [#13400](https://github.com/redis/redis/pull/13400) Add hide-user-data-from-log - allows hiding user data from the log file\n\n**Bug fixes**\n* [#13407](https://github.com/redis/redis/pull/13407) Trigger Lua GC after `SCRIPT LOAD`\n* [#13380](https://github.com/redis/redis/pull/13380) Fix possible crash due to OOM panic on invalid command\n* [#13383](https://github.com/redis/redis/pull/13383) `FUNCTION FLUSH` - improve Lua GC behavior and fix thread race in ASYNC mode\n* [#13408](https://github.com/redis/redis/pull/13408) `HEXPIRE`-like commands should emit `HDEL` keyspace notification if expire time is in the past"},{"id":"redis-community-edition-74-rc2-june-2024","title":"Redis Community Edition 7.4-rc2 (June 2024)","role":"content","text":"Upgrade urgency LOW: This is the second Release Candidate for Redis Community Edition 7.4.\n\n**Performance and resource utilization improvements**\n* [#13296](https://github.com/redis/redis/pull/13296) Optimize CPU cache efficiency\n\n**Changes to new 7.4 new features (compared to 7.4 RC1)**\n* [#13343](https://github.com/redis/redis/pull/13343) Hash - expiration of individual fields: when key does not exist - reply with an array (nonexisting code for each field)\n* [#13329](https://github.com/redis/redis/pull/13329) Hash - expiration of individual fields: new keyspace event: `hexpired`\n\n**Modules API - Potentially breaking changes to new 7.4 features (compared to 7.4 RC1)**\n* [#13326](https://github.com/redis/redis/pull/13326) Hash - expiration of individual fields: avoid lazy expire when called from a Modules API function"},{"id":"redis-community-edition-74-rc1-june-2024","title":"Redis Community Edition 7.4-rc1 (June 2024)","role":"content","text":"Upgrade urgency LOW: This is the first Release Candidate for Redis Community Edition 7.4.\n\nHere is a comprehensive list of changes in this release compared to 7.2.5.\n\n**New Features**\n* [#13303](https://github.com/redis/redis/pull/13303) Hash - expiration of individual fields. 9 commands were introduced:\n  - `HEXPIRE` and `HPEXPIRE` set the remaining time to live for specific fields\n  - `HEXPIREAT` and `HPEXPIREAT` set the expiration time to a UNIX timestamp for specific fields\n  - `HPERSIST` removes the expiration for specific fields\n  - `HEXPIRETIME` and `HPEXPIRETIME` get the expiration time for specific fields\n  - `HTTL` and `HPTTL` get the remaining time to live for specific fields\n* [#13117](https://github.com/redis/redis/pull/13117) `XREAD`: new id value `+` to start reading from the last message\n* [#12765](https://github.com/redis/redis/pull/12765) `HSCAN`: new `NOVALUES` flag to report only field names\n* [#12728](https://github.com/redis/redis/pull/12728) `SORT`, `SORT_RO`: allow `BY` and `GET` options in cluster mode when the pattern maps to the same slot as the key\n* [#12299](https://github.com/redis/redis/pull/12299) `CLIENT KILL`: new optional filter: `MAXAGE maxage` - kill connections older than `maxage` seconds\n* [#12971](https://github.com/redis/redis/pull/12971) Lua: expose `os.clock()` API for getting the elapsed time of Lua code execution\n* [#13276](https://github.com/redis/redis/pull/13276) Allow `SPUBLISH` command within `MULTI ... EXEC` transactions on replica\n\n**Bug fixes**\n* [#12898](https://github.com/redis/redis/pull/12898) `XREADGROUP`: fix entries-read inconsistency between master and replicas\n* [#13042](https://github.com/redis/redis/pull/13042) `SORT ... STORE`: fix created lists to respect list compression and packing configs\n* [#12817](https://github.com/redis/redis/pull/12817), [#12905](https://github.com/redis/redis/pull/12905) Fix race condition issues between the main thread and module threads\n* [#12577](https://github.com/redis/redis/pull/12577) Unsubscribe all clients from replica for shard channel if the master ownership changes\n* [#12622](https://github.com/redis/redis/pull/12622) `WAITAOF` could timeout or hang if used after a module command that propagated effects only to replicas and not to AOF\n* [#11734](https://github.com/redis/redis/pull/11734) `BITCOUNT` and `BITPOS` with nonexistent key and illegal arguments return an error, not 0\n* [#12394](https://github.com/redis/redis/pull/12394) `BITCOUNT`: check for wrong argument before checking if key exists\n* [#12961](https://github.com/redis/redis/pull/12961) Allow execution of read-only transactions when out of memory\n* [#13274](https://github.com/redis/redis/pull/13274) Fix crash when a client performs ACL change that disconnects itself\n* [#13311](https://github.com/redis/redis/pull/13311) Cluster: Fix crash due to unblocking client during slot migration\n\n**Security improvements**\n* [#13108](https://github.com/redis/redis/pull/13108) Lua: LRU eviction for scripts generated with `EVAL` *** BEHAVIOR CHANGE ***\n* [#12961](https://github.com/redis/redis/pull/12961) Restrict the total request size of `MULTI ... EXEC` transactions\n* [#12860](https://github.com/redis/redis/pull/12860) Redact ACL username information and mark `*-key-file-pass configs` as sensitive\n\n\n**Performance and resource utilization improvements**\n* [#12838](https://github.com/redis/redis/pull/12838) Improve performance when many clients call `PUNSUBSCRIBE` / `SUNSUBSCRIBE` simultaneously\n* [#12627](https://github.com/redis/redis/pull/12627) Reduce lag when waking `WAITAOF` clients and there is not much traffic\n* [#12754](https://github.com/redis/redis/pull/12754) Optimize `KEYS` when pattern includes hashtag and implies a single slot\n* [#11695](https://github.com/redis/redis/pull/11695) Reduce memory and improve performance by replacing cluster metadata with slot specific dictionaries\n* [#13087](https://github.com/redis/redis/pull/13087) `SCRIPT FLUSH ASYNC` now does not block the main thread\n* [#12996](https://github.com/redis/redis/pull/12996) Active memory defragmentation efficiency improvements\n* [#12899](https://github.com/redis/redis/pull/12899) Improve performance of read/update operation during rehashing\n* [#12536](https://github.com/redis/redis/pull/12536) `SCAN ... MATCH`: Improve performance when the pattern implies cluster slot\n* [#12450](https://github.com/redis/redis/pull/12450) `ZRANGE ... LIMIT`: improved performance\n\n\n**Other general improvements**\n* [#13133](https://github.com/redis/redis/pull/13133) Lua: allocate VM code with jemalloc instead of libc and count it as used memory *** BEHAVIOR CHANGE ***\n* [#12171](https://github.com/redis/redis/pull/12171) `ACL LOAD`: do not disconnect all clients *** BEHAVIOR CHANGE ***\n* [#13020](https://github.com/redis/redis/pull/13020) Allow adjusting defrag configurations while active defragmentation is running\n* [#12949](https://github.com/redis/redis/pull/12949) Increase the accuracy of avg_ttl (the average keyspace keys TTL)\n* [#12977](https://github.com/redis/redis/pull/12977) Allow running `WAITAOF` in scripts\n* [#12782](https://github.com/redis/redis/pull/12782) Implement TCP Keep-Alives across most Unix-like systems\n* [#12707](https://github.com/redis/redis/pull/12707) Improved error codes when rejecting scripts in cluster mode\n* [#12596](https://github.com/redis/redis/pull/12596) Support `XREAD ... BLOCK` in scripts; rejected only if it ends up blocking\n\n**New metrics**\n* [#12849](https://github.com/redis/redis/pull/12849) `INFO`: `pubsub_clients` - number of clients in Pub/Sub mode\n* [#12966](https://github.com/redis/redis/pull/12966) `INFO`: `watching_clients` - number of clients that are watching keys\n* [#12966](https://github.com/redis/redis/pull/12966) `INFO`: `total_watched_keys` - number of watched keys\n* [#12476](https://github.com/redis/redis/pull/12476) `INFO`: `client_query_buffer_limit_disconnections` - count client input buffer OOM events\n* [#12476](https://github.com/redis/redis/pull/12476) `INFO`: `client_output_buffer_limit_disconnections` - count client output buffer OOM events\n* [#12996](https://github.com/redis/redis/pull/12996) `INFO`: `allocator_muzzy` - memory returned to the OS but still shows as RSS until the OS reclaims it\n* [#13108](https://github.com/redis/redis/pull/13108) `INFO`: `evicted_scripts` - number of evicted eval scripts. Users can check it to see if they are abusing EVAL\n* [#12996](https://github.com/redis/redis/pull/12996) `MEMORY STATS`: `allocator.muzzy` - memory returned to the OS but still shows as RSS until the OS reclaims it\n* [#12913](https://github.com/redis/redis/pull/12913) `INFO MEMORY` `mem_overhead_db_hashtable_rehashing` - memory resharding overhead (only the memory that will be released soon)\n* [#12913](https://github.com/redis/redis/pull/12913) `MEMORY STATS`: `overhead.db.hashtable.lut` - total overhead of dictionary buckets in databases\n* [#12913](https://github.com/redis/redis/pull/12913) `MEMORY STATS`: `overhead.db.hashtable.rehashing` - temporary memory overhead of database dictionaries currently being rehashed \n* [#12913](https://github.com/redis/redis/pull/12913) `MEMORY STATS`: `db.dict.rehashing.count` - number of top level dictionaries currently being rehashed\n* [#12966](https://github.com/redis/redis/pull/12966) `CLIENT LIST`: `watch` - number of keys each client is currently watching\n\n**Modules API**\n* [#12985](https://github.com/redis/redis/pull/12985) New API calls: `RM_TryCalloc` and `RM_TryRealloc` - allow modules to handle memory allocation failures gracefully\n* [#13069](https://github.com/redis/redis/pull/13069) New API call: `RM_ClusterKeySlot` - which slot a key will hash to\n* [#13069](https://github.com/redis/redis/pull/13069) New API call: `RM_ClusterCanonicalKeyNameInSlot` - get a consistent key that will map to a slot \n* [#12486](https://github.com/redis/redis/pull/12486) New API call: `RM_AddACLCategory` - allow modules to declare new ACL categories\n\n\n**Configuration parameters**\n* [#12178](https://github.com/redis/redis/pull/12178) New configuration parameters: `max-new-connections-per-cycle` and `max-new-tls-connections-per-cycle` to limit the number of new client connections per event-loop cycle\n* [#7351](https://github.com/redis/redis/pull/7351) Rename some CPU configuration parameters for style alignment. Added alias to the old names to avoid breaking change\n\n**CLI tools**\n* [#10609](https://github.com/redis/redis/pull/10609) redis-cli: new `-t <timeout>` argument: specify server connection timeout in seconds\n* [#11315](https://github.com/redis/redis/pull/11315) redis-cli: new `-4` and `-6` flags to prefer IPV4 or IPV6 on DNS lookup\n* [#12862](https://github.com/redis/redis/pull/12862) redis-cli: allows pressing up arrow to return any command (including sensitive commands which are still not persisted)\n* [#12543](https://github.com/redis/redis/pull/12543) redis-cli: add reverse history search (like Ctrl+R in terminals)\n* [#12826](https://github.com/redis/redis/pull/12826) redis-cli: add `--keystats` and `--keystats-samples` to combines `--memkeys` and `--bigkeys` with additional distribution data\n* [#12735](https://github.com/redis/redis/pull/12735) redis-cli: fix: `--bigkeys` and `--memkeys` now work on cluster replicas\n* [#9411](https://github.com/redis/redis/pull/9411) redis-benchmark: add support for binary strings\n* [#12986](https://github.com/redis/redis/pull/12986) redis-benchmark: fix: pick random slot for a node to distribute operation across slots"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.0-release-notes","title":"Redis Open Source 8.0 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.0-release-notes/","summary":"Redis Open Source 8.0 release notes.","aliases":["/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.0.0-release-notes/"],"tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:33+01:00","page_type":"content","content_hash":"3d7d9e11490727fd7829151b38363f3648f6a511af47afda51ef57843039e0aa","sections":[{"id":"redis-open-source-806-february-2026","title":"Redis Open Source 8.0.6 (February 2026)","role":"overview","text":"Update urgency: `SECURITY`: There are security fixes in the release."},{"id":"security-fixes","title":"Security fixes","role":"security","text":"- A user can manipulate data read by a connection by injecting `\\r\\n` sequences into a Redis error reply."},{"id":"redis-open-source-805-november-2025","title":"Redis Open Source 8.0.5 (November 2025)","role":"content","text":"Update urgency: `HIGH`: There are critical bugs that may affect a subset of users."},{"id":"bug-fixes","title":"Bug fixes","role":"content","text":"- `HGETEX` - potential crash when `FIELDS` is used  and `numfields` is missing\n- Potential crash on HyperLogLog with 2GB+ entries\n- Cuckoo filter - Division by zero in Cuckoo filter insertion\n- Cuckoo filter - Counter overflow\n- Bloom filter - Arbitrary memory read/write with invalid filter\n- Bloom filter - Out-of-bounds access with empty chain\n- Bloom filter - Restore invalid filter\n- Top-k - Out-of-bounds access"},{"id":"redis-open-source-804-october-2025","title":"Redis Open Source 8.0.4 (October 2025)","role":"content","text":"Update urgency: `SECURITY`: There are security fixes in the release."},{"id":"security-fixes-1","title":"Security fixes","role":"security","text":"- (CVE-2025-49844) A Lua script may lead to remote code execution\n- (CVE-2025-46817) A Lua script may lead to integer overflow and potential RCE\n- (CVE-2025-46818) A Lua script can be executed in the context of another user\n- (CVE-2025-46819) LUA out-of-bound read"},{"id":"new-features","title":"New Features","role":"content","text":"- [#14223](https://github.com/redis/redis/pull/14223) `VSIM`: new `EPSILON` argument to specify maximum distance"},{"id":"bug-fixes-1","title":"Bug fixes","role":"content","text":"- [#14330](https://github.com/redis/redis/pull/14330) Potential use-after-free after pubsub and Lua defrag\n- [#14319](https://github.com/redis/redis/pull/14319) Potential crash on Lua script defrag\n- [#14224](https://github.com/redis/redis/pull/14224) `HINCRBYFLOAT` removes field expiration on replica\n- [#14164](https://github.com/redis/redis/pull/14164) Prevent `CLIENT UNBLOCK` from unblocking `CLIENT PAUSE`\n- [#14165](https://github.com/redis/redis/pull/14165) Endless client blocking for blocking commands\n- [#14144](https://github.com/redis/redis/pull/14144) Vector sets - RDB format is not compatible with big endian machines\n- [#14163](https://github.com/redis/redis/pull/14163) `EVAL` crash when error table is empty\n- [#14143](https://github.com/redis/redis/pull/14143) Gracefully handle short read errors for hashes with TTL during full sync"},{"id":"redis-open-source-803-july-2025","title":"Redis Open Source 8.0.3 (July 2025)","role":"content","text":"Update urgency: `SECURITY`: There are security fixes in the release."},{"id":"security-fixes-2","title":"Security fixes","role":"security","text":"* (CVE-2025-32023) Fix out-of-bounds write in `HyperLogLog` commands\n* (CVE-2025-48367) Retry accepting other connections even if the accepted connection reports an error"},{"id":"new-features-1","title":"New Features","role":"content","text":"- [#14065](https://github.com/redis/redis/pull/14065) `VSIM`: Add new `WITHATTRIBS` option to return the JSON attribute associated with an element"},{"id":"bug-fixes-2","title":"Bug fixes","role":"content","text":"- [#14085](https://github.com/redis/redis/pull/14085) A short read may lead to an `exit()` on a replica\n- [#14092](https://github.com/redis/redis/pull/14092) `db->expires` is not defragmented"},{"id":"redis-open-source-802-may-2025","title":"Redis Open Source 8.0.2 (May 2025)","role":"content","text":"Update urgency: `SECURITY`: There are security fixes in the release."},{"id":"security-fixes-3","title":"Security fixes","role":"security","text":"- (CVE-2025-27151) redis-check-aof may lead to stack overflow and potential RCE"},{"id":"bug-fixes-3","title":"Bug fixes","role":"content","text":"- [#14081](https://github.com/redis/redis/pull/14081) Cron-based timers run twice as fast when active defrag is enabled."},{"id":"other-general-improvements","title":"Other general improvements","role":"content","text":"- [#14048](https://github.com/redis/redis/pull/14048) `LOLWUT` improvements for Redis 8."},{"id":"redis-open-source-801-may-2025","title":"Redis Open Source 8.0.1 (May 2025)","role":"content","text":"Update urgency: `MODERATE`: Plan an upgrade of the server, but it's not urgent."},{"id":"performance-and-resource-utilization-improvements","title":"Performance and resource utilization improvements","role":"performance","text":"- [#13959](https://github.com/redis/redis/pull/13959) Vector sets - faster `VSIM` `FILTER` parsing."},{"id":"bug-fixes-4","title":"Bug fixes","role":"content","text":"- [#QE6083](https://github.com/RediSearch/RediSearch/pull/6083) Query Engine - revert default policy `search-on-timeout` to `RETURN`.\n- [#QE6050](https://github.com/RediSearch/RediSearch/pull/6050) Query Engine - `@__key` on `FT.AGGREGATE` used as reserved field name preventing access to Redis keyspace.\n- [#QE6077](https://github.com/RediSearch/RediSearch/pull/6077) Query Engine - crash when calling `FT.CURSOR DEL` while reading from the CURSOR."},{"id":"notes","title":"Notes","role":"content","text":"- Fixed incorrect text in the license files."},{"id":"redis-open-source-800-may-2025","title":"Redis Open Source 8.0.0 (May 2025)","role":"content","text":"This is the General Availability release of Redis Open Source 8.0.\n\nRedis 8.0 deprecates previous Redis and Redis Stack versions.\n\nStand alone RediSearch, RedisJSON, RedisTimeSeries, and RedisBloom modules are no longer needed as they are now part of Redis."},{"id":"major-changes-compared-to-742","title":"Major changes compared to 7.4.2","role":"content","text":"- Name change: Redis Community Edition is now Redis Open Source\n- License change: licensed under your choice of \n  - (a) the Redis Source Available License 2.0 (RSALv2); or\n  - (b) the Server Side Public License v1 (SSPLv1); or\n  - (c) the GNU Affero General Public License (AGPLv3)\n- Redis Search and 8 new data structures are now an integral part of Redis 8.\n  - (1) Redis Search, which now supports both horizontal and vertical scaling for search, query, and vector workloads.\n  - (2) JSON, a queryable JSON document data type.\n  - (3) Time series.\n  - (4-8) Five probabilistic data structures: Bloom filter, Cuckoo filter, Count-min sketch, Top-k, and t-digest.\n  - (9) Vector set [preview], a data structure designed for vector similarity search, inspired by sorted set.\n  - These nine components are included in all binary distributions.\n\n  - See the [build instructions](https://redis.io/docs/latest/operate/oss_and_stack/install/build-stack) for information about building from source code.\n  - New configuration file: `redis-full.conf`, loads Redis with all components, \n    and contains new configuration parameters for Redis Search and the new data structures.\n  - New ACL categories: `@search`, `@json`, `@timeseries`, `@bloom`, `@cuckoo`, `@cms`, `@topk`, and `@tdigest` commands are also included in the existing ACL categories such as `@read` and `@write`. See [below](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.0-release-notes#potentially-breaking-changes-to-acls) for information about potentially breaking changes.\n- More than 30 performance and resource utilization improvements.\n- A new I/O threading implementation, which enables throughput increase on multi-core environments\n  (set with the `io-threads` configuration parameter).\n- An improved replication mechanism that is more performant and robust.\n- New hash commands: [HGETDEL](https://redis.io/docs/latest/commands/hgetdel), [HGETEX](https://redis.io/docs/latest/commands/hgetex), and [HSETEX](https://redis.io/docs/latest/commands/hsetex)\n\nFor more details, see the release notes for the [8.0-M01](https://github.com/redis/redis/releases/tag/8.0-m01), [8.0-M02](https://github.com/redis/redis/releases/tag/8.0-m02), [8.0-M03](https://github.com/redis/redis/releases/tag/8.0-m03), [8.0-M04](https://github.com/redis/redis/releases/tag/8.0-m04), and [8.0-RC1](https://github.com/redis/redis/releases/tag/8.0-rc1) releases of Redis Open Source."},{"id":"binary-distributions","title":"Binary distributions","role":"content","text":"- [Alpine and Debian Docker images](https://hub.docker.com/_/redis)\n- [Install using snap](https://github.com/redis/redis-snap)\n- [Install using brew](https://github.com/redis/homebrew-redis)\n- [Install using RPM](https://github.com/redis/redis-rpm)\n- [Install using Debian APT](https://github.com/redis/redis-debian)"},{"id":"redis-800-was-tested-on-the-following-operating-systems","title":"Redis 8.0.0 was tested on the following operating systems","role":"content","text":"- Ubuntu 20.04 (Focal Fossa), 22.04 (Jammy Jellyfish), and 24.04 (Noble Numbat).\n- Rocky Linux 8.10 and 9.5.\n- AlmaLinux 8.10 and 9.5.\n- Debian 11 (Bullseye) and 12 (Bookworm).\n- macOS 13 (Ventura), 14 (Sonoma), and 15 (Sequoia)."},{"id":"supported-upgrade-paths-by-replication-or-persistence","title":"Supported upgrade paths (by replication or persistence)","role":"compatibility","text":"- From previous Redis versions without modules.\n- From previous Redis versions with modules (RediSearch, RedisJSON, RedisTimeSeries, RedisBloom).\n- From Redis Stack 7.2 or 7.4."},{"id":"security-fixes-compared-to-80-rc1","title":"Security fixes (compared to 8.0-RC1)","role":"security","text":"* (CVE-2025-21605) An unauthenticated client can cause an unlimited growth of output buffers"},{"id":"bug-fixes-compared-to-80-rc1","title":"Bug fixes (compared to 8.0-RC1)","role":"content","text":"- [#13966](https://github.com/redis/redis/pull/13966), [#13932](https://github.com/redis/redis/pull/13932) `CLUSTER SLOTS` - TLS port update not reflected.\n- [#13958](https://github.com/redis/redis/pull/13958) `XTRIM`, `XADD` - incorrect lag due to trimming stream.\n- [#13931](https://github.com/redis/redis/pull/13931) `HGETEX` - wrong order of keyspace notifications."},{"id":"potentially-breaking-changes-to-acls","title":"Potentially breaking changes to ACLs","role":"content","text":"> [!NOTE]\n> The following content is relevant to all Redis distributions (RS, RC, and ROS).\n\nRedis 8 includes Redis Search, as well as JSON, time series, Bloom filter, cuckoo filter, top-k, count-min sketch, and t-digest data types.\nThe integration of these features into Redis also comes with improvements to Redis [ACL](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl) rules.\n\n> [!WARNING]\n> These ACL changes may introduce breaking changes for some users, which must be analyzed carefully.\n\n#### Extension to the existing ACL categories\n\nBefore Redis 8, the existing ACL categories @read, @write, @dangerous, @admin, @slow, and @fast did not include commands for Redis Search and the JSON, time series, and probabilistic data structures.\n\nStarting with Redis 8, Redis includes all Redis Search, JSON, time series, Bloom filter, cuckoo filter, top-k, count-min sketch, and t-digest commands in these existing ACL categories.\n\nAs a result:\n\n- Existing ACL rules such as `+@read +@write` will allow access to more commands than in previous versions of Redis. Here are some examples:\n  - A user with `+@read` access will be able to execute `FT.SEARCH`.\n  - A user with `+@write` access will be able to execute `JSON.SET`. \n\n- ACL rules such as `+@all -@write`  will allow access to fewer commands than previous versions of Redis. For example:\n  - A user with `+@all -@write` will not be able to execute `JSON.SET`.\n\nNote that the `@all` category did not change, as it always included all the commands.\n\nAdditionally, ACL rules such as `+@read +JSON.GET` can now be simplified as `+@read` because `JSON.GET` is included in the `@read` category.\n\n#### Who is affected by this change?\n\nUsers who currently use Redis Search and/or the JSON, time series, or probabilistic data structures, and use custom ACL rules.\n\nYou should reanalyze your ACL rules to make sure they are aligned with your security and access control requirements."},{"id":"redis-8-introduces-the-following-data-structure-and-processing-engine-acl-categories","title":"Redis 8 introduces the following data structure and processing engine ACL categories.","role":"content","text":"| New ACL commands category names | Included commands               |\n| :----                           | :----                           |\n| `search`                        | All Redis Search commands |\n| `json`                          | All JSON commands               |\n| `timeseries`                    | All time series commands        |\n| `bloom`                         | All Bloom filter commands       |\n| `cuckoo`                        | All cuckoo filter commands      |\n| `topk`                          | All top-k commands              |\n| `cms`                           | All count-min sketch commands   |\n| `tdigest`                       | All t-digest commands           |\n\nYou can use these new categories in your ACL rules."},{"id":"time-series-notes","title":"Time series notes","role":"content","text":"The following time series commands retrieve data from all keys that match a given filter expression: `TS.MGET`, `TS.MRANGE`, and `TS.MREVRANGE`. \n\nThere can be a case where a user may have to only some of the matching keys. In such cases, the command’s result is an error message: \"*current user doesn't have read permission to one or more keys that match the specified filter*\".\n\nOn the other hand, `TS.QUERYINDEX` does not require `@read` access to the keys that match the specified filter, as it accesses only time series metadata (name and labels) and not content (measurements)."},{"id":"redis-search-notes","title":"Redis Search notes","role":"content","text":"The following Redis Search commands may retrieve data from all keys that match the prefixes defined in the index (that is, all indexed documents, per-index):\n\n- `FT.SEARCH`  \n- `FT.AGGREGATE` (may be followed by `FT.CURSOR`)  \n- `FT.PROFILE`\n\nOnly ACL users with access to a superset of the key prefixes defined during index creation can create, modify, or read the index. For example, a user with the key ACL pattern `h:*` can create an index with keys prefixed by `h:*` or `h:p*`, but not keys prefixed by `h*`, `k:*`, or `k*`, because these prefixes may involve keys to which the user does not have access."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.10-release-notes","title":"Redis Open Source 8.10 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.10-release-notes/","summary":"Redis Open Source 8.10 release notes.","tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:33+01:00","page_type":"content","content_hash":"6c911fe16a82a1a6032845941541925428d8ed342a11992a216641a69a8423e0","sections":[{"id":"redis-open-source-8101-august-2026","title":"Redis Open Source 8.10.1 (August 2026)","role":"overview","text":"Update urgency: `SECURITY`: There are security fixes in the release."},{"id":"security-fixes","title":"Security fixes","role":"security","text":"- (CVE-2026-62356) Miscalculated buffer size in `CMSketch` RDB loading may lead to heap OOB write.\n- Out-of-bounds access in TopK heap cleanup path (MOD-15410).\n- Use-after-free in the TLS pending-data list when a command closes another pending connection.\n- A malicious RDB payload with an out-of-range `SLOT_INFO slot id` causes memory corruption during RDB loading, which may lead to Remote Code Execution.\n- Vector Sets: missing node level validation when loading a vector set from RDB may lead to out-of-bounds access.\n- Vector Sets: use-after-free when `VREM` mutates the HNSW graph while background `VSIM` threads are still running.\n- Vector Sets: a negative `hnsw_search()` return was treated as a huge unsigned count, reading past the end of the result arrays.\n- TLS client certificate authentication bypass: a Common Name containing an embedded NUL byte was truncated, allowing a client to authenticate as another (possibly privileged) ACL user.\n- [#15594](https://github.com/redis/redis/pull/15594) Use-after-free in the blocked-client list when reprocessing a command evicts another client blocked on the same key."},{"id":"redis-open-source-8100-july-2026","title":"Redis Open Source 8.10.0 (July 2026)","role":"content","text":"This is the General Availability release of Redis 8.10 in Redis Open Source."},{"id":"major-changes-compared-to-88","title":"Major changes compared to 8.8","role":"content","text":"- Compact hashes - a new hash encoding that reduces memory usage by storing hash field names just once for keys that share a schema\n- New command: `HIMPORT` - high-throughput compact hash bulk insertion\n- TLS peer certificate-based server-to-server authentication\n- New commands: `LMOVEM`, `BLMOVEM` - move multiple elements between lists\n- New command: `SUNIONCARD` - get the cardinality of the union of multiple sets\n- New command: `SDIFFCARD` - get the cardinality of the difference between sets\n- New command: `BACKUP` - node-side implementation for backup and restore based on multi-part AOF (MP-AOF)\n- `XREAD`, `XREADGROUP` - new `MAXCOUNT` and `MAXSIZE` arguments to cap the cumulative reply entries and size\n- New command: `FT.ALIASLIST` - get all aliases for the index \n- Stemmer support for Malay and Tagalog languages \n- JSONPath extensions\n- New commands: `TS.NRANGE`, `TS.NREVRANGE` - Query a range across multiple time series; group results by timestamp \n- New command: `TS.READ` - optionally blocking read\n- New command: `TS.QUERYLABELS` - Get a list of labels and label-values \n- New command: `TS.MRANGE`, `TS.MREVRANGE` - new `EXCLUDEEMPTY` argument to exclude series with no reported samples \n- Performance improvements"},{"id":"binary-distributions","title":"Binary distributions","role":"content","text":"- Alpine and Debian Docker images - https://hub.docker.com/_/redis\n- Install using snap - see https://github.com/redis/redis-snap\n- Install using brew - see https://github.com/redis/homebrew-redis\n- Install using RPM - see https://github.com/redis/redis-rpm\n- Install using Debian APT - see https://github.com/redis/redis-debian"},{"id":"operating-systems-we-test-redis-810-on","title":"Operating systems we test Redis 8.10 on","role":"content","text":"- Ubuntu 22.04 (Jammy Jellyfish), 24.04 (Noble Numbat), 26.04 (Resolute Raccoon)\n- Rocky Linux 8.10, 9.7, 10.1\n- AlmaLinux 8.10, 9.7, 10.1\n- Debian 12.13 (Bookworm), Debian 13.4 (Trixie)\n- Alpine 3.23\n- macOS 14.8.4 (Sonoma), 15.7.4 (Sequoia), 26.3 (Tahoe) - for both Intel and ARM"},{"id":"bug-fixes-compared-to-810-rc2","title":"Bug fixes (compared to 8.10-RC2)","role":"content","text":"- [#15513](https://github.com/redis/redis/pull/15513) Prevented a division-by-zero error when active defragmentation thresholds are configured with equal or reversed value.\n- [#15453](https://github.com/redis/redis/pull/15453) Clients blocked on `BLPOP`, `BLMOVE`, or `BLMOVEM` could remain blocked after `SORT` with `STORE` replaced the target key with a list.\n- [#15499](https://github.com/redis/redis/pull/15499) Memory usage reported for compressed replication clients could be lower than the actual memory consumed.\n- [#15518](https://github.com/redis/redis/pull/15518) notify modules when node's own ip/port changes.\n- [#15539](https://github.com/redis/redis/pull/15539) Loading an AOF file with an RDB preamble could fail when active defragmentation was enabled.\n- RediSearch/RediSearch[#10528](https://github.com/redisearch/redisearch/pull/10528) Crash in the numeric range tree when background garbage collection encounters an empty leaf during active indexing on a numeric field. (MOD-16877)\n- RediSearch/RediSearch[#10605](https://github.com/redisearch/redisearch/pull/10605) FT.CREATE and FT.ALTER now reject an empty string as a field name, preventing a crash loop in the fork-GC on indexes with an empty TAG field. (MOD-17034)\n- RediSearch/RediSearch[#10488](https://github.com/redisearch/redisearch/pull/10488) FT.INFO reports a negative num_records after garbage collection on indexes that include an INDEXMISSING field. (MOD-16940)\n- RediSearch/RediSearch[#10568](https://github.com/redisearch/redisearch/pull/10568) Cluster FT.AGGREGATE fails with SEARCH_FIELD_DUP when a GROUPBY contains multiple REDUCE COLLECT calls over the same field differing only in option-keyword casing. (MOD-16365)\n- RediSearch/RediSearch[#10605](https://github.com/redisearch/redisearch/pull/10605) FT.CREATE and FT.ALTER now return an error when a field name is an empty string; previously such schemas were accepted silently. (MOD-17034)\n- RediSearch/RediSearch[#10522](https://github.com/redisearch/redisearch/pull/10522) coord_total_query_warnings_timeout double-counts timeout warnings for FT.SEARCH queries that use a hybrid (vector + filter) execution path. (MOD-15973)"},{"id":"redis-open-source-810-rc2-july-2026","title":"Redis Open Source 8.10-RC2 (July 2026)","role":"content","text":"This is the second Release Candidate of Redis 8.10 in Redis Open Source.\n\nRelease Candidates are feature-complete pre-releases. Pre-releases are not suitable for production use."},{"id":"bug-fixes-compared-to-810-rc1","title":"Bug fixes (compared to 8.10-RC1)","role":"content","text":"- A user can manipulate data read by a connection by injecting `\\r\\n` sequences into a Redis error reply\n- A typo in `release.h` that could cause build failures"},{"id":"redis-open-source-810-rc1-july-2026","title":"Redis Open Source 8.10-RC1 (July 2026)","role":"content","text":"This is the first Release Candidate of Redis 8.10 in Redis Open Source.\n\nRelease Candidates are feature-complete pre-releases. Pre-releases are not suitable for production use."},{"id":"headlines","title":"Headlines:","role":"content","text":"Redis 8.10 introduces new features and performance improvements."},{"id":"operating-systems-we-test-redis-810-on-1","title":"Operating systems we test Redis 8.10 on","role":"content","text":"- Ubuntu 22.04 (Jammy Jellyfish), 24.04 (Noble Numbat), 26.04 (Resolute Raccoon)\n- Rocky Linux 8.10, 9.7, 10.1\n- AlmaLinux 8.10, 9.7, 10.1\n- Debian 12.13 (Bookworm), Debian 13.4 (Trixie)\n- Alpine 3.23\n- macOS 14.8.4 (Sonoma), 15.7.4 (Sequoia), 26.3 (Tahoe) - for both Intel and ARM"},{"id":"new-features-compared-to-88","title":"New Features (compared to 8.8)","role":"content","text":"- [#15364](https://github.com/redis/redis/pull/15364) Compact hashes - a new hash encoding that reduces memory usage by storing hash field names just once for keys that share a schema\n- [#15364](https://github.com/redis/redis/pull/15364) New command: `HIMPORT` - high-throughput compact hash bulk insertion\n- [#15405](https://github.com/redis/redis/pull/15405) New commands: `LMOVEM`, `BLMOVEM` - move multiple elements between lists\n- [#14893](https://github.com/redis/redis/pull/14893) New command: `SUNIONCARD` - get the cardinality of the union of multiple sets\n- [#15278](https://github.com/redis/redis/pull/15278) New command: `SDIFFCARD` - get the cardinality of the difference between sets\n- [#15441](https://github.com/redis/redis/pull/15441) New command: `BACKUP` - node-side implementation for backup and restore based on multi-part AOF (MP-AOF)\n- [#15282](https://github.com/redis/redis/pull/15282) `XREAD`, `XREADGROUP` - new `MAXCOUNT` and `MAXSIZE` arguments to cap the cumulative reply entries and size\n- [#15337](https://github.com/redis/redis/pull/15337) New `SCRIPT_RUNNER` command flag: flag commands that execute scripts or functions\n- [#15347](https://github.com/redis/redis/pull/15347) `SLOWLOG GET` - new reply argument: total argument count\n- [#Q9626](https://github.com/RediSearch/RediSearch/pull/9626) New command: `FT.ALIASLIST` - get all aliases for the index (RED-197340)\n- [#Q9052](https://github.com/RediSearch/RediSearch/pull/9052) Stemmer support for Malay and Tagalog languages (RED-132425)\n- [#Q9291](https://github.com/RediSearch/RediSearch/pull/9291) `FT.AGGREGATE` - new `COLLECT` reducer: separate remote and local reducer invocations (RED-177887)\n- [#Q8169](https://github.com/RediSearch/RediSearch/pull/8169), [#Q8236](https://github.com/RediSearch/RediSearch/pull/8236), [#Q9234](https://github.com/RediSearch/RediSearch/pull/9234), [#Q9443](https://github.com/RediSearch/RediSearch/pull/9443), [#Q9861](https://github.com/RediSearch/RediSearch/pull/9861) `FT.SEARCH`, `FT.AGGREGATE`, `FT.HYBRID`: enforce query `TIMEOUT` more strictly\n  - The `search-on-timeout` policy now offers three options:\n    - `FAIL` — reject timed-out queries. With `search-workers > 0` (the default), the timeout is enforced preemptively\n    - `RETURN` (default) — return best-effort partial results, without enforcing strictness during post-processing\n    - `RETURN_STRICT` (new) — return best-effort partial results while enforcing the timeout through the post-processing (result) pipeline. Available when `search-workers > 0`\n  - Queries executed on the main thread (i.e., when `search-workers` is 0) are capped by [`search-_max-foreground-timeout-limit`](https://redis.io/docs/latest/develop/ai/search-and-query/administration/configuration#search-_max-foreground-timeout-limit) (default `60000` ms).\n- [#J1602](https://github.com/RedisJSON/RedisJSON/pull/1602), [#J1603](https://github.com/RedisJSON/RedisJSON/pull/1603), [#J1604](https://github.com/RedisJSON/RedisJSON/pull/1604), [#J1607](https://github.com/RedisJSON/RedisJSON/pull/1607), [#J1618](https://github.com/RedisJSON/RedisJSON/pull/1618) JSONPath extensions (MOD-16274, MOD-16275):\n  - Projection expressions at the top level of a JSONPath query\n  - `==` and `!=` can now compare any literal, including array and object literals\n  - Filter negation operator: `!`\n  - `size`/`sizeof` and `empty` operators on string, array, object, and nodelist\n  - `in` and `nin` operators: membership test on an array and nodelist\n  - Operators on numbers: binary `-`, `+`, `*`, `/`, `%`, and unary `-` and `+`\n  - Operator on object: `~`\n  - `length()` function on array, object, and string\n  - Functions on number: `abs()`, `ceiling()`, `floor()`\n  - Functions on string: `match()`, `search()`\n  - Strings concatenation with `concat()`\n  - Functions on array: `first()`, `last()`, `index()`, `append()`\n  - Aggregation functions on array: `min()`, `max()`, `avg()`, `sum()`, `stddev()`\n  - Function on object: `keys()`\n  - Function on nodelist: `count()`\n  - Function on nodelist with exactly one node: `value()`\n  - Relations functions on array and nodelist: `subsetof()`, `anyof()`, `noneof()`\n- [#T2052](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/2052) New commands: `TS.NRANGE`, `TS.NREVRANGE` - Query a range across multiple time series; group results by timestamp (RED-149232)\n- [#T2054](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/2054) New command: `TS.READ` - optionally blocking read (RED-132421)\n- [#T2090](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/2090) New command: `TS.QUERYLABELS` - Get a list of labels and label-values (RED-132355)\n- [#T2072](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/2072) New command: `TS.MRANGE`, `TS.MREVRANGE` - new `EXCLUDEEMPTY` argument to exclude series with no reported samples (RED-132536)"},{"id":"bug-fixes-compared-to-880","title":"Bug fixes (compared to 8.8.0)","role":"content","text":"- [#15478](https://github.com/redis/redis/pull/15478) `SORT`, `GEORADIUS`, `GEORADIUSBYMEMBER`, `XREAD`, `XREADGROUP` - ACL permission bypass\n- [#15329](https://github.com/redis/redis/pull/15329), [#15467](https://github.com/redis/redis/pull/15467) I/O thread busy looping for replica clients\n- [#15466](https://github.com/redis/redis/pull/15466) Duplicate KeyMeta restoration in `RESTORE`-based AOF rewrites\n- [#15462](https://github.com/redis/redis/pull/15462) `MEMORY USAGE` over-reports memory consumption\n- [#15447](https://github.com/redis/redis/pull/15447) Full sync under heavy write load\n- [#15412](https://github.com/redis/redis/pull/15412) Unit mismatch disables the FAST expire cycle stale trigger\n- [#15392](https://github.com/redis/redis/pull/15392) Crash on `VRANDMEMBER` with `LLONG_MIN` count\n- [#15409](https://github.com/redis/redis/pull/15409) `CONFIG SET` - crash on TLS options in non-TLS builds\n- [#15371](https://github.com/redis/redis/pull/15371) ACL key-name leak in BCAST client-side caching invalidations\n- [#15433](https://github.com/redis/redis/pull/15433) Signed overflow in `BITFIELD` offset parsing\n- [#15446](https://github.com/redis/redis/pull/15446) The select-based event loop backend enqueues registered file descriptors that `select()` did not mark ready\n- [#15309](https://github.com/redis/redis/pull/15309) `mem_clients_normal` drift when a replica drops its cached master after a failed partial resync\n- [#15357](https://github.com/redis/redis/pull/15357) Crash on missing module numeric config\n- [#15377](https://github.com/redis/redis/pull/15377) In-progress atomic slot migration tasks keep running on `RM_ResetDataset` and `RM_RdbLoad`\n- [#15391](https://github.com/redis/redis/pull/15391), [#15356](https://github.com/redis/redis/pull/15356), [#15436](https://github.com/redis/redis/pull/15436) NULL dereference\n- [#15390](https://github.com/redis/redis/pull/15390) Overflow on memory unit conversions\n- [#15291](https://github.com/redis/redis/pull/15291) `SET` does not enforce mutually exclusive `NX`/`XX` and `IF*` options\n- [#15322](https://github.com/redis/redis/pull/15322) `CONFIG SET` does not reject duplicate arguments when using both primary name and alias\n- [#15407](https://github.com/redis/redis/pull/15407) `LSET` out-of-range 64-bit index truncation\n- [#15308](https://github.com/redis/redis/pull/15308) Improve RDB load robustness (streams)\n- [#15263](https://github.com/redis/redis/pull/15263) Tighter cluster bus parsing for `PING`, `PONG`, and `MEET` packets\n- [#15247](https://github.com/redis/redis/pull/15247) Partial crash log on LoongArch architecture\n- [#15270](https://github.com/redis/redis/pull/15270) `VADD ... CAS SETATTR` - wrong attributes count\n- [#Q10375](https://github.com/RediSearch/RediSearch/pull/10375) `FT.CURSOR READ`: crash after the underlying index was dropped (MOD-16703)\n- [#Q10408](https://github.com/RediSearch/RediSearch/pull/10408) `FT.SEARCH` with `LIMIT` returns too many results in cluster mode over RESP3 (MOD-16767)\n- [#Q10420](https://github.com/RediSearch/RediSearch/pull/10420) `FT.INFO` could report incorrect values for multi-value TAG fields after document updates (MOD-16745)\n- [#Q10488](https://github.com/RediSearch/RediSearch/pull/10488) `FT.INFO` reports a negative `num_records` after garbage collection on indexes with an `INDEXMISSING` field (MOD-16940)\n- [#Q10392](https://github.com/RediSearch/RediSearch/pull/10392) Vector search: crash (SIGSEGV) in FP32/L2 distance computation on x86 without AVX support, affecting certain vector dimensions (MOD-16730)\n- [#Q9963](https://github.com/RediSearch/RediSearch/pull/9963) Range query returns incomplete results when the lower bound is an excluded empty string (MOD-15897)\n- [#Q10066](https://github.com/RediSearch/RediSearch/pull/10066) `FT.AGGREGATE .. WITHCOUNT` coordinator leaks resources on timeout (MOD-16210)\n- [#Q10247](https://github.com/RediSearch/RediSearch/pull/10247) Local `FT.HYBRID` ignores `ON_TIMEOUT RETURN_STRICT` and continues execution past the deadline (MOD-16492)\n- [#Q10255](https://github.com/RediSearch/RediSearch/pull/10255) Blocked search clients trigger unnecessary query dumps, adding CPU and log overhead (MOD-16518)\n- [#Q10151](https://github.com/RediSearch/RediSearch/pull/10151) `FT.HYBRID` supports `EXPLAINSCORE`, exposing the fusion method (`RRF` or `LINEAR`) (MOD-10044)\n- [#J1542](https://github.com/RedisJSON/RedisJSON/pull/1542), [#J1543](https://github.com/RedisJSON/RedisJSON/pull/1543) Wrong results when evaluating paths with recursive descent (MOD-6722, MOD-14664)\n- [#J1554](https://github.com/RedisJSON/RedisJSON/pull/1554) Any literal that starts with true/false/null is interpreted as true/false/null (MOD-7266)\n- [#J1600](https://github.com/RedisJSON/RedisJSON/pull/1600) Improve RDB load robustness\n- [#T2067](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/2067) `TS.INFO` - inaccurate memory usage calculation (MOD-6409)\n- [#T2036](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/2036), [#T2053](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/2053), [#T2056](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/2056), [#T2074](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/2074) Aggregation fixes (MOD-8187, MOD-16224, MOD-15565)\n- [#T2004](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/2004), [#T2051](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/2051) Improve RDB load robustness\n- [#T2104](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/2104) Issues after cluster topology changes\n- [#T2122](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/2122) Error on the first cross-shard command (TLS)\n- [#T2109](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/2109) `TS.INCRBY` with `TIMESTAMP *` replication timestamp drift\n- [#P1014](https://github.com/RedisBloom/RedisBloom/pull/1014) `CF.LOADCHUNK` partial replication (MOD-16050)\n- [#P1026](https://github.com/RedisBloom/RedisBloom/pull/1026), [#P1027](https://github.com/RedisBloom/RedisBloom/pull/1027) Improve RDB load robustness"},{"id":"performance-and-resource-utilization-improvements-compared-to-880","title":"Performance and resource utilization improvements (compared to 8.8.0)","role":"performance","text":"- [#15376](https://github.com/redis/redis/pull/15376) Optimize `lpSeek()` by validating entries only when necessary\n- [#15345](https://github.com/redis/redis/pull/15345) Optimize wide `HSET`/`HMSET` on a fresh hash with a single batched listpack append\n- [#15330](https://github.com/redis/redis/pull/15330) Avoid recomputing allocator fragmentation twice per cron tick (RED-200844)\n- [#15259](https://github.com/redis/redis/pull/15259) Trim excess SDS allocation in inline command parsing\n- [#15256](https://github.com/redis/redis/pull/15256) rax memory reduction: leaf-inlining for fixed-length-key trees - improves `XREADGROUP` performance\n- [#15397](https://github.com/redis/redis/pull/15397) Improve `RESTORE REPLACE` performance for new keys\n- [#14704](https://github.com/redis/redis/pull/14704) Optimizes an internal memory accounting\n- [#Q10246](https://github.com/RediSearch/RediSearch/pull/10246) Write operations block on a read lock held throughout vector range query result collection (MOD-16437)\n- [#Q10392](https://github.com/RediSearch/RediSearch/pull/10392) Vector search: reduce HNSW index memory usage with one-byte per-node locks (MOD-16696)\n- [#J1617](https://github.com/RedisJSON/RedisJSON/pull/1617) JSON - memory object footprint improvements (MOD-16608)"},{"id":"modules-api","title":"Modules API","role":"content","text":"- [#15350](https://github.com/redis/redis/pull/15350) `RedisModuleEvent_ClusterTopologyChange` - cluster topology change\n- [#15327](https://github.com/redis/redis/pull/15327) `REDISMODULE_SUBEVENT_FORK_CHILD_*` - allow multi-threaded modules can quiesce background work before `fork()`\n- [#15373](https://github.com/redis/redis/pull/15373) `REDISMODULE_SUBEVENT_CLUSTER_SLOT_MIGRATION_MIGRATE_MODULE_PROPAGATE_END` - inject commands after ASM replication stream\n- [#15242](https://github.com/redis/redis/pull/15242) `RedisModule_AddPostNotificationJobForKey` - binds deferred work to a specific key from a keyspace-notification handler"},{"id":"configuration-parameters","title":"Configuration parameters","role":"configuration","text":"- [#15364](https://github.com/redis/redis/pull/15364) Compact hashes:\n  - `hash-rdb-load-min-template-entries`: minimum field count to convert a plain hash to a template during load\n  - `hash-rdb-load-max-template-entries`: maximum field count for load-time conversion\n  - `hash-rdb-load-template-disassembly-threshold`: minimum number of keys a converted template must end up with to be kept"},{"id":"metrics","title":"Metrics","role":"content","text":"- [#15364](https://github.com/redis/redis/pull/15364) Compact hashes:\n  - `INFO STATS` - `hash_templates`: number of distinct compact hash templates\n  - `INFO STATS` - `hash_template_keys`: total number of keys backed by a compact hash template\n  - `INFO MEMORY` - `used_memory_hash_templates`: total memory used by all compact hash templates\n  - `MEMORY STATS` - `hash.templates`: total memory used by all compact hash templates\n  - `MEMORY USAGE <key>` reports the key's own memory plus its share of a compact hash template's cost"},{"id":"cli-tools","title":"CLI tools","role":"content","text":"- [#15352](https://github.com/redis/redis/pull/15352) Adds `--latency-percentiles <p1,p2,...>` to `--latency` / `--latency-history`: reporting user-chosen percentiles\n- [#15262](https://github.com/redis/redis/pull/15262) `redis-cli --cluster rebalance` - CROSSSLOT error when using `-user` without `-a`\n- [#15338](https://github.com/redis/redis/pull/15338) `redis-cli --cluster reshard` and `rebalance` now move slots with server-side atomic slot migration"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.12-release-notes","title":"Redis Open Source 8.12 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.12-release-notes/","summary":"Redis Open Source 8.12 release notes.","tags":["docs","operate","stack"],"last_updated":"2026-10-21T09:46:20-07:00","page_type":"content","content_hash":"1fa2f7f894dd7a06764629798fbf28d25e316e66b328ad6e1bfa62c573319dbd","sections":[{"id":"redis-open-source-812-rc1-october-2026","title":"Redis Open Source 8.12-RC1 (October 2026)","role":"overview","text":"This is the first Release Candidate of Redis 8.12 in Redis Open Source.\n\nRelease Candidates are feature-complete pre-releases. Pre-releases are not suitable for production use."},{"id":"headlines","title":"Headlines:","role":"content","text":"Redis 8.12 introduces new features and performance improvements."},{"id":"operating-systems-we-test-redis-812-on","title":"Operating systems we test Redis 8.12 on","role":"content","text":"- Ubuntu 22.04 (Jammy Jellyfish), 24.04 (Noble Numbat), 26.04 (Resolute Raccoon)\n- Rocky Linux 8.10, 9.7, 10.1\n- AlmaLinux 8.10, 9.7, 10.1\n- Debian 12.13 (Bookworm), Debian 13.4 (Trixie)\n- Alpine 3.23\n- macOS 14.8.4 (Sonoma), 15.7.4 (Sequoia), 26.3 (Tahoe) - for both Intel and ARM"},{"id":"security-fixes-compared-to-8100","title":"Security fixes (compared to 8.10.0)","role":"security","text":"- [#Q10073](https://github.com/RediSearch/RediSearch/pull/10073) Indexed value hashing is collision-resistant, preventing index degradation from hash-based denial-of-service (HashDoS) attacks. (MOD-14459)\n- [#Q11202](https://github.com/RediSearch/RediSearch/pull/11202) `unicode_tolower` no longer reads out of bounds on a dangling multi-byte lead byte in indexed TEXT fields encoded with the 8-bit Unicode Transformation Format (UTF-8). (MOD-18088)"},{"id":"new-features-compared-to-810","title":"New Features (compared to 8.10)","role":"content","text":"- [#15331](https://github.com/redis/redis/pull/15331) Compressed bitmaps - redis-roaring integration reduces memory usage and speeds up bitmap operations. Thanks to Antonio, its creator, for [contributing this capability](https://redisroaring.com/redis-merge/#/1).\n- [#15986](https://github.com/redis/redis/pull/15986) New command: `BITRANGEINTARRAY` - returns set bit offsets within a range, equivalent to redis-roaring's `R.RANGEINTARRAY`.\n- [#15938](https://github.com/redis/redis/pull/15938) Set expiration on individual set members:\n  - New commands: `SEXPIRE`, `SPEXPIRE`, `SEXPIREAT`, `SPEXPIREAT` - set expiration.\n  - New commands: `STTL`, `SPTTL`, `SEXPIRETIME`, `SPEXPIRETIME` - retrieve remaining time-to-live (TTL) values or expiration timestamps.\n- [#15828](https://github.com/redis/redis/pull/15828) Bless keys: avoid eviction of specified keys; new command: `BLESS`.\n- [#15829](https://github.com/redis/redis/pull/15829) `CLUSTER MIGRATION STATUS` reports progress for both the snapshot and incremental migration phases.\n- [#15556](https://github.com/redis/redis/pull/15556) Configure Transport Layer Security (TLS) named groups with `tls-groups`.\n- [#P1063](https://github.com/RedisBloom/RedisBloom/pull/1063) Count-min sketches support optional `CELL_SIZE` in `CMS.INITBYDIM` and `CMS.INITBYPROB` for lower memory usage. `CMS.INCRBY` accepts negative increments to decrement counts. `CMS.INFO` reports cell sizes.\n- [#J1640](https://github.com/RedisJSON/RedisJSON/pull/1640) Automatically create deep paths in JavaScript Object Notation (JSON).\n- [#Q11330](https://github.com/RediSearch/RediSearch/pull/11330) 8-bit scalar quantization (SQ8) compression is available for hierarchical navigable small world (HNSW) vector fields. (MOD-14958)\n- [#Q10063](https://github.com/RediSearch/RediSearch/pull/10063) `COLLECT LIMIT` honors the configurable `search-max-aggregate-results`."},{"id":"bug-fixes-compared-to-8100","title":"Bug fixes (compared to 8.10.0)","role":"content","text":"- [#15569](https://github.com/redis/redis/pull/15569) Changes made by `XDELEX DELREF` for already-deleted stream entries could fail to propagate to replicas or the append-only file (AOF).\n- [#15508](https://github.com/redis/redis/pull/15508) Operations using random radix-tree traversal could behave incorrectly when starting from certain leaf nodes.\n- [#15604](https://github.com/redis/redis/pull/15604) Memory usage could be calculated incorrectly for hashes using shared templates.\n- [#15489](https://github.com/redis/redis/pull/15489) Redis database (RDB) files could fail to load after `XSETID` reduced a stream's `entries_added` counter.\n- [#15626](https://github.com/redis/redis/pull/15626) `SET` could calculate an incorrect expiration time when given an excessively large relative TTL.\n- [#15628](https://github.com/redis/redis/pull/15628) Active defragmentation could incorrectly handle the shared-template registry used by compact hashes.\n- [#15530](https://github.com/redis/redis/pull/15530) After failover, a replica could remain connected to another replica instead of the new primary.\n- [#15733](https://github.com/redis/redis/pull/15733) Invalid cluster slot ranges could be accepted after being merged into a valid range.\n- [#15756](https://github.com/redis/redis/pull/15756) `RESTORE` could calculate an incorrect expiration time when given an excessively large relative TTL.\n- [#15699](https://github.com/redis/redis/pull/15699) Memory growth could be accounted for incorrectly when `SETBIT` or `BITFIELD` expanded a string.\n- [#15815](https://github.com/redis/redis/pull/15815) `XTRIM` could leave a stream in a state that prevented an RDB file containing it from loading.\n- [#15795](https://github.com/redis/redis/pull/15795) Clients could fail to receive a protocol error when a malformed command arrived across multiple network reads.\n- [#15875](https://github.com/redis/redis/pull/15875) `ARLASTITEMS` could omit elements appearing after a gap in an array.\n- [#15916](https://github.com/redis/redis/pull/15916) Data could be lost when a legacy AOF file was upgraded before loading completed.\n- [#15917](https://github.com/redis/redis/pull/15917) Hiredis clients on Linux could mishandle errors when establishing Transmission Control Protocol (TCP) connections.\n- [#15869](https://github.com/redis/redis/pull/15869) Redis could crash when a module thread paused input/output (I/O) threads.\n- [#15468](https://github.com/redis/redis/pull/15468) Publish/subscribe (Pub/Sub) subscriptions could remain active after the subscribing user's channel permissions were revoked or the user was deleted, if the client had re-authenticated as another user.\n- [#15623](https://github.com/redis/redis/pull/15623) Module actions scheduled by hash-field expiration notifications could be delayed after fields expired in the background.\n- [#15680](https://github.com/redis/redis/pull/15680) Data could be lost after a replica migrated to a different shard.\n- [#15587](https://github.com/redis/redis/pull/15587) `LMOVEM` could emit keyspace notifications inconsistent with those produced by `LMOVE`.\n- [#15695](https://github.com/redis/redis/pull/15695) Command side effects could be propagated to replicas or the AOF despite settings that should have excluded them.\n- [#Q10872](https://github.com/RediSearch/RediSearch/pull/10872) The shard no longer crashes with a segmentation fault (`SIGSEGV`) when `RESTORE` of a legacy `ft_index0` payload is issued outside an RDB load. (MOD-15685)\n- [#Q10728](https://github.com/RediSearch/RediSearch/pull/10728) Arming the garbage collection (GC) timer off the main thread on ARM no longer causes random shard crashes after extended uptime. (MOD-17309)\n- [#Q10767](https://github.com/RediSearch/RediSearch/pull/10767) The server no longer crashes on writes when an index `FILTER` references no schema fields. (MOD-15592)\n- [#Q11303](https://github.com/RediSearch/RediSearch/pull/11303), [#Q11304](https://github.com/RediSearch/RediSearch/pull/11304), [#Q11308](https://github.com/RediSearch/RediSearch/pull/11308), [#Q11309](https://github.com/RediSearch/RediSearch/pull/11309), [#Q11310](https://github.com/RediSearch/RediSearch/pull/11310), [#Q11311](https://github.com/RediSearch/RediSearch/pull/11311) `FT.ALTER SCHEMA ADD` running concurrently with queries under `WORKERS>0` no longer crashes query execution across VECTOR, TAG, NUMERIC, GEO, ISMISSING, and GEOSHAPE nodes. (MOD-18356, MOD-18361, MOD-18366, MOD-18367, MOD-18368)\n- [#Q11474](https://github.com/RediSearch/RediSearch/pull/11474) `FT.AGGREGATE` with the numeric `SORTBY` optimizer no longer crashes or returns wrong results. (MOD-6967)\n- [#Q10209](https://github.com/RediSearch/RediSearch/pull/10209) The coordinator no longer crashes with Redis serialization protocol version 2 (RESP2) when `FT.PROFILE HYBRID` hits a return-strict timeout. (MOD-16396)\n- [#Q10145](https://github.com/RediSearch/RediSearch/pull/10145) TEXT `PHONETIC` matches now behave correctly when non-TEXT fields precede TEXT fields in the schema. (MOD-16304)\n- [#Q11262](https://github.com/RediSearch/RediSearch/pull/11262) An `FT.AGGREGATE` cursor with `FILTER` reports the correct result count. (MOD-18172)\n- [#Q11282](https://github.com/RediSearch/RediSearch/pull/11282) `FT.EXPLAIN` now renders a multi-word tag value as a single tag rather than an `INTERSECT`. (MOD-8666)\n- [#Q10768](https://github.com/RediSearch/RediSearch/pull/10768) NOT queries no longer leak excluded documents or drop results after a concurrent index change. (MOD-17394)\n- [#Q10637](https://github.com/RediSearch/RediSearch/pull/10637) Numeric index cardinality and bounds are now correct under float compression. (MOD-16877)\n- [#Q10704](https://github.com/RediSearch/RediSearch/pull/10704) Empty wildcard patterns no longer cause out-of-bounds writes or spurious empty-value matches. (MOD-17451)\n- [#Q10752](https://github.com/RediSearch/RediSearch/pull/10752) Wildcard `?` on `WITHSUFFIXTRIE` TEXT fields now matches per codepoint rather than per byte. (MOD-17476)\n- [#Q10112](https://github.com/RediSearch/RediSearch/pull/10112) Suffix-trie wildcard queries on TEXT fields are now case-insensitive. (MOD-16259)\n- [#Q11450](https://github.com/RediSearch/RediSearch/pull/11450) `Wildcard_MatchRune` no longer hangs on a literal `*` in the indexed string.\n- [#Q11267](https://github.com/RediSearch/RediSearch/pull/11267) Duplicate vector results are no longer returned during tiered indexing. (MOD-17795)\n- [#Q10731](https://github.com/RediSearch/RediSearch/pull/10731) `FT.SEARCH` on a cluster returns an error for invalid `LIMIT` arguments, matching standalone behavior. (MOD-12465)\n- [#Q10251](https://github.com/RediSearch/RediSearch/pull/10251) `FT.SEARCH` under `WORKERS>0` no longer returns a nil field-array for documents re-indexed mid-query. (MOD-16507)\n- [#Q10921](https://github.com/RediSearch/RediSearch/pull/10921) Sort keys with embedded NUL bytes are no longer truncated in replies.\n- [#Q10085](https://github.com/RediSearch/RediSearch/pull/10085) The `RETURN_STRICT` safe-loader no longer deadlocks the Redis global lock (GIL) on client timeout. (MOD-16267)\n- [#Q11449](https://github.com/RediSearch/RediSearch/pull/11449) An index reference leak after queued search cancellation no longer occurs. (MOD-18682)\n- [#Q11369](https://github.com/RediSearch/RediSearch/pull/11369) Memory leaks after coordinator SEARCH cancellation no longer occur. (MOD-18548)\n- [#Q10881](https://github.com/RediSearch/RediSearch/pull/10881) SST replication no longer hangs and the numeric snapshot is no longer corrupted when disk GC is mid-cycle. (MOD-17197) <!-- TODO: Expand SST. -->\n- [#Q10110](https://github.com/RediSearch/RediSearch/pull/10110) `FT.HYBRID` cursor setup now has a bounded channel timeout. (MOD-16145)\n- [#Q10211](https://github.com/RediSearch/RediSearch/pull/10211), [#Q10745](https://github.com/RediSearch/RediSearch/pull/10745) `RETURN_STRICT` cursors are depleted on timeout; strict-timed-out hybrid cursor cycles drop their cursors at cycle end. (MOD-16091, MOD-16991) <!-- TODO: Clarify strict timeout behavior. -->\n- [#Q10947](https://github.com/RediSearch/RediSearch/pull/10947) Coordinator cursor memory is now capped via `INDEX_CURSOR_LIMIT`. (MOD-17479)\n- [#Q10766](https://github.com/RediSearch/RediSearch/pull/10766), [#Q10763](https://github.com/RediSearch/RediSearch/pull/10763) Cursor reads no longer time out against an earlier read's deadline and correctly report a timeout that expires while iterators are revalidated. (MOD-17489, MOD-17488)\n- [#Q10993](https://github.com/RediSearch/RediSearch/pull/10993) RDB and replication payloads no longer leak a stray heap byte. (MOD-17800)\n- [#Q10074](https://github.com/RediSearch/RediSearch/pull/10074) `FT.HYBRID` returns a stable reciprocal rank fusion (RRF) result order across cluster shards. (MOD-12438)\n- [#Q11027](https://github.com/RediSearch/RediSearch/pull/11027) Cluster TLS connections now use Redis node metadata, aligning with topology changes. (MOD-15869)\n- [#Q10380](https://github.com/RediSearch/RediSearch/pull/10380), [#Q10430](https://github.com/RediSearch/RediSearch/pull/10430) Cluster topology is now refreshed on topology-change events rather than by polling. (MOD-15868, MOD-15870)\n- [#Q10835](https://github.com/RediSearch/RediSearch/pull/10835) Coordinator fanout now preserves binary argument lengths.\n- [#Q11095](https://github.com/RediSearch/RediSearch/pull/11095) Replica stalls during `FT.DROPINDEX DD` are reduced.\n- [#Q10083](https://github.com/RediSearch/RediSearch/pull/10083) Internal query arguments missing during a rolling upgrade are tolerated. (MOD-16047)\n- [#Q10816](https://github.com/RediSearch/RediSearch/pull/10816) Forced GC now waits for a concurrent child process instead of skipping its cycle.\n- [#Q10958](https://github.com/RediSearch/RediSearch/pull/10958) The asynchronous disk read pool no longer spins when saturated; it blocks instead.\n- [#Q10646](https://github.com/RediSearch/RediSearch/pull/10646) The VectorSimilarity dependency addresses unaligned metadata access on ARM/Scalable Vector Extension (SVE) paths. (MOD-15303)\n- [#Q10352](https://github.com/RediSearch/RediSearch/pull/10352) `FT.PROFILE ON_TIMEOUT FAIL` now reports a pre-execution timeout as a warning rather than an error. (MOD-16660)\n- [#Q10870](https://github.com/RediSearch/RediSearch/pull/10870) Crash reports now include in-flight coordinator aggregate, hybrid, and cursor-read queries. (MOD-16992)\n- [#Q11089](https://github.com/RediSearch/RediSearch/pull/11089) Debug aggregate request synchronization no longer crashes. (MOD-17616)\n- [#J1636](https://github.com/RedisJSON/RedisJSON/pull/1636) Fixed array-index normalization overflow for extreme negative indexes, including `i64::MIN`, affecting commands such as `JSON.ARRTRIM`.\n- [#J1648](https://github.com/RedisJSON/RedisJSON/pull/1648) Corrected `JSON.MGET` key-spec metadata to identify all key arguments and exclude the trailing JSONPath argument.\n- [#J1670](https://github.com/RedisJSON/RedisJSON/pull/1670) Fixed downstream compilation when using `redis_json_module_create!` with the `as-library` feature and the new RDB depth configuration.\n- [#P1035](https://github.com/RedisBloom/RedisBloom/pull/1035) `TDIGEST.QUANTILE`, `TDIGEST.CDF`, and `TDIGEST.TRIMMED_MEAN` could return a not-a-number (NaN) value for a sketch with exactly one observation.\n- [#P1053](https://github.com/RedisBloom/RedisBloom/pull/1053) RDB loading and `RESTORE` inconsistently rejected invalid numeric values, allocation sizes, serialized counts, or inconsistent metadata in Bloom filters, Count-Min Sketches, Top-K sketches, and t-digests. Bloom filter growth lacked overflow checks.\n- [#P1067](https://github.com/RedisBloom/RedisBloom/pull/1067) Bloom filters with excessive hash-function counts could be accepted during loading and restoration, including through `BF.LOADCHUNK`.\n- [#P1053](https://github.com/RedisBloom/RedisBloom/pull/1053) Top-K sketches containing nonempty items with zero counts or empty-string items with nonzero counts could be restored incorrectly.\n- [#P1062](https://github.com/RedisBloom/RedisBloom/pull/1062) `CMS.MERGE` key metadata did not correctly identify the destination and all source keys, affecting cluster slot validation.\n- [#P1071](https://github.com/RedisBloom/RedisBloom/pull/1071) `CF.COMPACT` command metadata was missing its argument count and key specification."},{"id":"performance-and-resource-utilization-improvements-compared-to-8100","title":"Performance and resource utilization improvements (compared to 8.10.0)","role":"performance","text":"- [#15618](https://github.com/redis/redis/pull/15618) Batch updates to hashes using shared templates run more efficiently.\n- [#15018](https://github.com/redis/redis/pull/15018) Supported x86 systems use the processor-based monotonic clock by default, with runtime calibration to reduce timing overhead.\n- [#15782](https://github.com/redis/redis/pull/15782) RDB and AOF loading provides sequential-read hints to the operating system to improve loading efficiency.\n- [#Q10169](https://github.com/RediSearch/RediSearch/pull/10169) Searches on indexes using field-level expiration (`HEXPIRE`) are faster. (MOD-16350)\n- [#Q10981](https://github.com/RediSearch/RediSearch/pull/10981), [#Q11065](https://github.com/RediSearch/RediSearch/pull/11065) Coordinator field lookup and row-reply serialization overhead is reduced. (MOD-17924)\n- [#Q11055](https://github.com/RediSearch/RediSearch/pull/11055), [#Q11056](https://github.com/RediSearch/RediSearch/pull/11056), [#Q11057](https://github.com/RediSearch/RediSearch/pull/11057) Cluster reply conversion, result destruction, and `GROUPBY` cleanup have reduced overhead. (MOD-17908, MOD-17909, MOD-17910)\n- [#Q11507](https://github.com/RediSearch/RediSearch/pull/11507), [#Q11506](https://github.com/RediSearch/RediSearch/pull/11506) Query replies now format integral numeric values without `snprintf` and declare reply collection lengths up front. (MOD-18800, MOD-18799)\n- [#Q11509](https://github.com/RediSearch/RediSearch/pull/11509) Hash field loading for `RETURN` and `LOAD` no longer requires per-field allocations. (MOD-18811)\n- [#Q11278](https://github.com/RediSearch/RediSearch/pull/11278) `FT.ALTER` avoids unnecessary document reindexing during schema additions. (MOD-18171)\n- [#Q11384](https://github.com/RediSearch/RediSearch/pull/11384) Score/payload-only hash updates avoid reindexing. (MOD-18182)\n- [#Q11230](https://github.com/RediSearch/RediSearch/pull/11230) Hash documents are no longer re-indexed when no indexed field is touched. (MOD-17688)\n- [#Q10515](https://github.com/RediSearch/RediSearch/pull/10515) Coordinator requests are now allocated on the main thread, removing `CoordRequestCtx`. (MOD-16990)\n- [#Q11018](https://github.com/RediSearch/RediSearch/pull/11018) Query usage is now separated from administrative index operations. (MOD-17536)\n- [#J1645](https://github.com/RedisJSON/RedisJSON/pull/1645) Memory usage reporting divides a shared string's allocation, including header and padding, proportionally among its references. Previously, each reference was charged the full allocation.\n- [#J1660](https://github.com/RedisJSON/RedisJSON/pull/1660) JSON parsing uses compact object allocation to reduce memory usage.\n- [#J1661](https://github.com/RedisJSON/RedisJSON/pull/1661) JSONPath parsing and evaluation reduce backtracking, temporary allocations, and repeated traversal. Optimizations cache root-path operands across filter candidates, stop existence checks at the first match, and reduce regex matching and string comparison overhead.\n- [#J1631](https://github.com/RedisJSON/RedisJSON/pull/1631) Improved typed-array element access through direct indexing, avoiding repeated iteration and quadratic work during full indexed scans.\n- [#P1049](https://github.com/RedisBloom/RedisBloom/pull/1049) Improved t-digest compression performance for inputs containing many duplicate values. Sorting is bounded to O(n log n), avoiding quadratic behavior on pathological inputs."},{"id":"configuration-parameters","title":"Configuration parameters","role":"configuration","text":"- [#15331](https://github.com/redis/redis/pull/15331) redis-roaring: `bitmap-default-roaring` - enables the new roaring bitmaps type; disabled by default.\n- [#J1657](https://github.com/RedisJSON/RedisJSON/pull/1657) `json-legacy-rdb-depth-limit` enforces a maximum nesting depth of 128 when loading JSON from RDB or through `RESTORE`. It is disabled by default.\n- [#J1640](https://github.com/RedisJSON/RedisJSON/pull/1640) `json-auto-create-deep-paths` is disabled by default and configurable only at load time. Use the same value across shard nodes and AOF replays to prevent data divergence."},{"id":"metrics","title":"Metrics","role":"content","text":"- [#15374](https://github.com/redis/redis/pull/15374) `INFO keysizes` histograms include stream key sizes.\n- [#15682](https://github.com/redis/redis/pull/15682) `INFO STREAMS` provides per-database histograms of consumer groups per stream, pending entries per group, and consumers per group. Enable with `stream-stats yes` (disabled by default).\n- [#Q10097](https://github.com/RediSearch/RediSearch/pull/10097) `FT.INFO` now exposes the default `EF_RUNTIME`. (MOD-16147)\n- [#Q10107](https://github.com/RediSearch/RediSearch/pull/10107) Disk GC `bytes_collected` and cycle statistics are reported in `FT.INFO`. (MOD-14621)\n- [#Q10464](https://github.com/RediSearch/RediSearch/pull/10464) `FT.INFO` on the coordinator aggregates per-field disk metrics via the reducer. (MOD-14859)\n- [#Q10204](https://github.com/RediSearch/RediSearch/pull/10204) Redis `INFO` now breaks down query timeout metrics by pipeline stage. (MOD-13192)\n- [#Q11521](https://github.com/RediSearch/RediSearch/pull/11521) Index metrics are no longer collected for unrelated `INFO` sections. (MOD-18033)\n- [#Q10536](https://github.com/RediSearch/RediSearch/pull/10536) `FT.PROFILE` loader-field timing resolution is improved. (MOD-17132)"},{"id":"behavior-changes","title":"Behavior changes","role":"content","text":"- [#Q10109](https://github.com/RediSearch/RediSearch/pull/10109) `FT.CREATE` rejects `RERANK` on HNSW vector fields in random access memory (RAM) mode. (MOD-16015)\n- [#Q10200](https://github.com/RediSearch/RediSearch/pull/10200) Disk indexes reject `DIALECT 4` and `WITHOUTCOUNT`. (MOD-15997)\n- [#Q10082](https://github.com/RediSearch/RediSearch/pull/10082) `COLLECT` requires an `@` prefix for field names. (MOD-16233)\n- [#Q11106](https://github.com/RediSearch/RediSearch/pull/11106) JSON `HIGHLIGHT`/`SUMMARIZE` fields with multi-value JSONPath are now rejected. (MOD-16530)\n- [#Q10208](https://github.com/RediSearch/RediSearch/pull/10208) Non-cacheable commands are now marked with `dont_cache`. (MOD-16175)\n- [#Q10699](https://github.com/RediSearch/RediSearch/pull/10699) Numeric command arguments are now strictly parsed. (MOD-17409)\n- [#Q11335](https://github.com/RediSearch/RediSearch/pull/11335) The Search module requires Redis 8.9.241 or newer to load. (MOD-15868)\n- [#P1063](https://github.com/RedisBloom/RedisBloom/pull/1063) Rejected `CMS.INCRBY` item updates now leave counters and the total count unchanged instead of saturating counters on overflow. Multi-item commands can still partially succeed.\n- [#P1063](https://github.com/RedisBloom/RedisBloom/pull/1063) `CMS.MERGE` now requires matching cell sizes across the destination and all sources."},{"id":"modules-api","title":"Modules API","role":"content","text":"- [#15517](https://github.com/redis/redis/pull/15517) The modules application programming interface (API) adds the `REDISMODULE_NODE_PORT_TLS` flag to `RM_GetClusterNodeInfo()` to retrieve a cluster node's TLS port.\n- [#J1623](https://github.com/RedisJSON/RedisJSON/pull/1623) Added `getWithPath` in shared API V9 to evaluate a precompiled JSONPath, allowing callers to compile a path once and reuse it across documents.\n- [#J1666](https://github.com/RedisJSON/RedisJSON/pull/1666) Added `pathParseNoErrorMsg` in shared API V10 to compile JSONPaths without a Redis context or an allocated error message. Returns a compiled handle on success or `NULL` on failure.\n- [#J1653](https://github.com/RedisJSON/RedisJSON/pull/1653) Made pre-command hooks optional and allowed hook errors to abort JSON commands before execution. Hooks now return `RedisResult<()>`; shared C API calls continue to ignore hook errors.\n- [#J1640](https://github.com/RedisJSON/RedisJSON/pull/1640) Extended the Rust `Manager` interface with `clone_value` and `create_object` to support construction of missing paths. Custom implementations must implement these methods and satisfy the new `Borrow<Self::V>` requirement for owned values."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.2-release-notes","title":"Redis Open Source 8.2 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.2-release-notes/","summary":"Redis Open Source 8.2 release notes.","tags":["docs","operate","stack"],"last_updated":"2026-08-17T13:21:31-07:00","page_type":"content","content_hash":"875e4a68fc5fdcd776ba57ea0729f440b381a64fbcdf71a7e26baf28e2d07ce4","sections":[{"id":"redis-open-source-829-august-2026","title":"Redis Open Source 8.2.9 (August 2026)","role":"overview","text":"Update urgency: `SECURITY`: There are security fixes in the release."},{"id":"security-fixes","title":"Security fixes","role":"security","text":"- (CVE-2026-62356) Miscalculated buffer size in `CMSketch` RDB loading may lead to heap OOB write.\n- Out-of-bounds access in TopK heap cleanup path (MOD-15410).\n- Use-after-free in the TLS pending-data list when a command closes another pending connection.\n- [#15478](https://github.com/redis/redis/pull/15478) ACL key permission bypass in `SORT`, `GEORADIUS`/`GEORADIUSBYMEMBER` and `XREAD`/`XREADGROUP`: the keys validated by ACL could differ from the keys the command actually accesses.\n- [#14847](https://github.com/redis/redis/pull/14847) Out-of-bounds `argv` access during key extraction when checking ACL permissions of a `KEYNUM keyspec` command (for example, `EVAL`) with wrong arity.\n- A malicious RDB payload with an out-of-range `SLOT_INFO slot id` causes memory corruption during RDB loading, which may lead to Remote Code Execution.\n- Vector Sets: missing node level validation when loading a vector set from RDB may lead to out-of-bounds access.\n- Vector Sets: use-after-free when `VREM` mutates the HNSW graph while background `VSIM` threads are still running.\n- Vector Sets: a negative `hnsw_search()` return was treated as a huge unsigned count, reading past the end of the result arrays.\n- [#15594](https://github.com/redis/redis/pull/15594) Use-after-free in the blocked-client list when reprocessing a command evicts another client blocked on the same key."},{"id":"redis-open-source-828-july-2026","title":"Redis Open Source 8.2.8 (July 2026)","role":"content","text":"SECURITY: There are security fixes in the release."},{"id":"security-fixes-1","title":"Security fixes","role":"security","text":"- Use-after-free when loading a stream consumer group via `RESTORE` may lead to Remote Code Execution.\n- RedisBloom/RedisBloom[#1041](https://github.com/redisbloom/redisbloom/pull/1041) Crafted RESTORE payloads in RedisBloom and TDigest may trigger out-of-bounds writes, potentially leading to remote code execution."},{"id":"bug-fixes","title":"Bug fixes","role":"content","text":"- RedisBloom/RedisBloom[#1019](https://github.com/redisbloom/redisbloom/pull/1019) Replicate `CF.LOADCHUNK` data chunks to prevent silent Cuckoo Filter data loss on failover."},{"id":"redis-open-source-827-june-2026","title":"Redis Open Source 8.2.7 (June 2026)","role":"content","text":"Update urgency: `HIGH`: There are critical bugs that may affect a subset of users."},{"id":"bug-fixes-1","title":"Bug fixes","role":"content","text":"- [#15175](https://github.com/redis/redis/pull/15175), RediSearch/RediSearch[#9262](https://github.com/redisearch/redisearch/pull/9262) Redis fails to start on AArch64.\n- [#14537](https://github.com/redis/redis/pull/14537) `SCAN`: restore original filter order (revert change introduced in 8.2).\n- [#14816](https://github.com/redis/redis/pull/14816) setModuleEnumConfig() passing prefixed name to module callbacks.\n- [#14623](https://github.com/redis/redis/pull/14623) Streams: `XTRIM`/`XADD` with approx mode (`~`) don’t delete entries for `DELREF`/`ACKED` strategies.\n- [#14552](https://github.com/redis/redis/pull/14552) Streams: Incorrect behavior when using `XDELEX...`ACKED` after `XGROUP DESTROY`.\n- [#14420](https://github.com/redis/redis/pull/14420) Shutdown blocked client not being properly reset after shutdown cancellation.\n- [#14415](https://github.com/redis/redis/pull/14415) Potential crash in `lookupKey()` when `executing_client` is NULL.\n- [#14417](https://github.com/redis/redis/pull/14417) `CLUSTER FORGET` - heap-buffer-overflow.\n- [#15188](https://github.com/redis/redis/pull/15188) `cluster-announce-ip` rejecting hostnames (regression).\n- [#14667](https://github.com/redis/redis/pull/14667), [#14886](https://github.com/redis/redis/pull/14886) Potential TCP stalls/deadlocks.\n- RediSearch/RediSearch[#9484](https://github.com/redisearch/redisearch/pull/9484) Shard crash during background index scan of JSON documents with vector fields on Active-Active (CRDT) databases (MOD-15542).\n- RediSearch/RediSearch[#9635](https://github.com/redisearch/redisearch/pull/9635) Severe latency spikes and shard unresponsiveness when `EXPIRE` or `EXPIREAT` operations run concurrently with queries on large indexes (MOD-14930)."},{"id":"redis-open-source-826-may-2026","title":"Redis Open Source 8.2.6 (May 2026)","role":"content","text":"Update urgency: `SECURITY`: There are security fixes in the release."},{"id":"security-fixes-2","title":"Security fixes","role":"security","text":"- (CVE-2026-23479) Use-After-Free in unblock client flow may lead to Remote Code Execution.\n- (CVE-2026-25243) Invalid memory access in `RESTORE` may lead to Remote Code Execution.\n- (CVE-2026-23631) Lua Use-After-Free may lead to remote code execution.\n- (CVE-2026-25588) Invalid memory access in `RESTORE` may lead to Remote Code Execution (Time Series).\n- (CVE-2026-25589) Invalid memory access in `RESTORE` may lead to Remote Code Execution (Probabilistic)."},{"id":"bug-fixes-2","title":"Bug fixes","role":"content","text":"- `SUBSCRIBE`, `PSUBSCRIBE`, `SSUBSCRIBE`: crash on OOM (RED-167788).\n- `CONFIG SET`: some settings allow invalid characters (RED-167787).\n- `SCRIPT DEBUG`: potential crash on scripts (RED-175507).\n- `VADD`: crash or buffer overflow on large `REDUCE` value (RED-170921).\n- `VSET`: crash on huge allocations (MOD-12678).\n- Potential crash on disconnections and TLS failures (Time Series) (MOD-14850).\n- RediSearch/RediSearch[#8743](https://github.com/redisearch/redisearch/pull/8743) Crash when many keys receive expirations under heavy TTL activity (MOD-14500).\n- RediSearch/RediSearch[#8850](https://github.com/redisearch/redisearch/pull/8850) HNSW vector index memory growth under high-churn workloads until shard restart (MOD-13761).\n- RediSearch/RediSearch[#9178](https://github.com/redisearch/redisearch/pull/9178) Coordinator deadlock under mixed `FT.SEARCH` and `FT.AGGREGATE` load (MOD-14268).\n- RediSearch/RediSearch[#9049](https://github.com/redisearch/redisearch/pull/9049) `FT.PROFILE` output is inconsistent when a profiled value is missing (MOD-10560).\n- RediSearch/RediSearch[#8793](https://github.com/redisearch/redisearch/pull/8793) `FT.EXPLAIN` does not lock, causing a race with concurrent index changes (MOD-14461).\n- RediSearch/RediSearch[#8600](https://github.com/redisearch/redisearch/pull/8600) `FILTER` returns inconsistent results with multiple indexes sharing field aliases (MOD-14063).\n- RediSearch/RediSearch[#8662](https://github.com/redisearch/redisearch/pull/8662) `FILTER` behavior depends on property order in the expression (MOD-14342).\n- RediSearch/RediSearch[#8602](https://github.com/redisearch/redisearch/pull/8602) Filter expressions are evaluated for indexes that do not match the document type (MOD-14064).\n- RediSearch/RediSearch[#8601](https://github.com/redisearch/redisearch/pull/8601) Documents are inconsistently included or excluded depending on the indexing path taken (MOD-13948).\n- RediSearch/RediSearch[#8599](https://github.com/redisearch/redisearch/pull/8599) `RENAME` notification handler loads the wrong key, causing stale index entries after a rename (MOD-14062).\n- RediSearch/RediSearch[#9019](https://github.com/redisearch/redisearch/pull/9019) `PERSIST` and `HPERSIST` notifications are not reflected in index expiration tracking (MOD-14800).\n- RediSearch/RediSearch[#9081](https://github.com/redisearch/redisearch/pull/9081) `FT.SPELLCHECK` treats `PARAMS` placeholders as literal terms instead of resolving them (MOD-10596).\n- RediSearch/RediSearch[#8464](https://github.com/redisearch/redisearch/pull/8464) GC out-of-memory on replica shards leaves the replica in an inconsistent state (MOD-14066).\n- RediSearch/RediSearch[#8888](https://github.com/redisearch/redisearch/pull/8888) `FT.CURSOR` enters an infinite loop when the ACL user lacks specific permissions (MOD-14479).\n- RediSearch/RediSearch[#9166](https://github.com/redisearch/redisearch/pull/9166) Crash on `FT.SEARCH` when topology validation fails (for example, some nodes unreachable) (MOD-14475).\n- RediSearch/RediSearch[#8453](https://github.com/redisearch/redisearch/pull/8453) `FT.INFO`-style output no longer reports zero-index summary data when no indices exist (MOD-14081).\n- RediSearch/RediSearch[#9076](https://github.com/redisearch/redisearch/pull/9076) `FT.CREATE` now rejects schema definitions with invalid option combinations at creation time (MOD-14655)."},{"id":"metrics","title":"Metrics","role":"content","text":"- RediSearch/RediSearch[#8235](https://github.com/redisearch/redisearch/pull/8235) `FT.PROFILE`: added queue time tracking (MOD-13602)."},{"id":"redis-open-source-825-february-2026","title":"Redis Open Source 8.2.5 (February 2026)","role":"content","text":"Update urgency: `SECURITY`: There are security fixes in the release."},{"id":"security-fixes-3","title":"Security fixes","role":"security","text":"- A user can manipulate data read by a connection by injecting `\\r\\n` sequences into a Redis error reply."},{"id":"redis-open-source-824-february-2026","title":"Redis Open Source 8.2.4 (February 2026)","role":"content","text":"Update urgency: `SECURITY`: There are security fixes in the release."},{"id":"security-fixes-4","title":"Security fixes","role":"security","text":"- RedisTimeSeries/RedisTimeSeries[#1837](https://github.com/redistimeseries/redistimeseries/pull/1837), RedisJSON/RedisJSON[#1474](https://github.com/redisjson/redisjson/pull/1474) Hide Personally Identifiable Information from server log."},{"id":"bug-fixes-3","title":"Bug fixes","role":"content","text":"- RedisJSON/RedisJSON[#1430](https://github.com/redisjson/redisjson/pull/1430) Malformed panic log messages (MOD-9365).\n- RedisBloom/RedisBloom[#945](https://github.com/redisbloom/redisbloom/pull/945) Bloom filter: crash on RDB load on large number of filters (MOD-11590).\n- RediSearch/RediSearch[#6973](https://github.com/redisearch/redisearch/pull/6973) Correct empty string token counting in byteOffset calculations to ensure accurate text position tracking (MOD-11233).\n- RediSearch/RediSearch[#6995](https://github.com/redisearch/redisearch/pull/6995) Prevent `FT.INFO` command fanout to replicas to reduce unnecessary cluster traffic.\n- RediSearch/RediSearch[#7034](https://github.com/redisearch/redisearch/pull/7034) Correct goto statement handling on RDB load.\n- RediSearch/RediSearch[#7154](https://github.com/redisearch/redisearch/pull/7154) Display Background Indexing OOM warning in `FT.AGGREGATE` when memory limits are approached (MOD-11817).\n- RediSearch/RediSearch[#7219](https://github.com/redisearch/redisearch/pull/7219) Resolve concurrency issue in Reducer that caused intermittent errors (MOD-12243).\n- RediSearch/RediSearch[#7255](https://github.com/redisearch/redisearch/pull/7255) Correct `BM25STD` underflow wraparound to prevent incorrect scoring (MOD-12223).\n- RediSearch/RediSearch[#7264](https://github.com/redisearch/redisearch/pull/7264) Ensure accurate `totalDocsLen` updates to maintain correct document statistics (MOD-12234).\n- RediSearch/RediSearch[#7275](https://github.com/redisearch/redisearch/pull/7275) Report used memory as unsigned long to prevent overflow (RED-169833).\n- RediSearch/RediSearch[#7350](https://github.com/redisearch/redisearch/pull/7350) Allow `FT.CREATE` with LeanVec parameters on non-Intel architectures (RED-176382).\n- RediSearch/RediSearch[#7384](https://github.com/redisearch/redisearch/pull/7384) Reduce index load from RDB temporary memory overhead (MOD-12212).\n- RediSearch/RediSearch[#7435](https://github.com/redisearch/redisearch/pull/7435) Ensure full profile output on timeout with `RETURN` policy in `FT.PROFILE` (MOD-12320).\n- RediSearch/RediSearch[#7446](https://github.com/redisearch/redisearch/pull/7446) Remove outdated validation from debug aggregate in cluster mode (MOD-12435).\n- RediSearch/RediSearch[#7458](https://github.com/redisearch/redisearch/pull/7458) Correct GC regression that caused stability issues (MOD-12538).\n- RediSearch/RediSearch[#7459](https://github.com/redisearch/redisearch/pull/7459) Prevent potential double-free in Fork GC error path (MOD-12521).\n- RediSearch/RediSearch[#7470](https://github.com/redisearch/redisearch/pull/7470) Remove draining from Flush callback to avoid blocking.\n- RediSearch/RediSearch[#7499](https://github.com/redisearch/redisearch/pull/7499) Propagate `HGETALL` command in HDT mode (MOD-12662).\n- RediSearch/RediSearch[#7534](https://github.com/redisearch/redisearch/pull/7534) Reduce number of worker threads asynchronously to prevent performance degradation (MOD-12252, MOD-11658).\n- RediSearch/RediSearch[#7554](https://github.com/redisearch/redisearch/pull/7554) Handle Coordinator case when `SCORE` is sent alone without extra fields (MOD-12647).\n- RediSearch/RediSearch[#7561](https://github.com/redisearch/redisearch/pull/7561) Prevent memory corruption when freeing searchRequestCtx on error (MOD-12699).\n- RediSearch/RediSearch[#7685](https://github.com/redisearch/redisearch/pull/7685) Resolve cursor logical leak that could lead to resource exhaustion (MOD-12807).\n- RediSearch/RediSearch[#7710](https://github.com/redisearch/redisearch/pull/7710) Add support for `WITHCOUNT` in `FT.AGGREGATE` (MOD-11751).\n- RediSearch/RediSearch[#7794](https://github.com/redisearch/redisearch/pull/7794) Correctly handle binary data with embedded NULLs to prevent crashes (MOD-13010).\n- RediSearch/RediSearch[#7812](https://github.com/redisearch/redisearch/pull/7812) Correct SVS GC for no-workers case (MOD-12983).\n- RediSearch/RediSearch[#7873](https://github.com/redisearch/redisearch/pull/7873) Handle warnings in empty `FT.AGGREGATE` replies in cluster mode (MOD-12640).\n- RediSearch/RediSearch[#7886](https://github.com/redisearch/redisearch/pull/7886) Remove non-TEXT fields from spec's keys dictionary to prevent incorrect field handling (MOD-13150).\n- RediSearch/RediSearch[#7901](https://github.com/redisearch/redisearch/pull/7901) Support multiple warnings in reply to prevent warning loss (MOD-13252).\n- RediSearch/RediSearch[#8083](https://github.com/redisearch/redisearch/pull/8083) Correct `FULLTEXT` field metric count accuracy (MOD-13432).\n- RediSearch/RediSearch[#8153](https://github.com/redisearch/redisearch/pull/8153) Resolve config registration issue (RED-171841).\n- RediSearch/RediSearch[#7371](https://github.com/redisearch/redisearch/pull/7371) Validate `search-min-operation-workers` min value correctly (MOD-12383).\n- RediSearch/RediSearch[#8151](https://github.com/redisearch/redisearch/pull/8151) Correct `FT.PROFILE` shard total profile time calculation (MOD-13735, MOD-13181).\n- RediSearch/RediSearch[#7165](https://github.com/redisearch/redisearch/pull/7165) (Redis Enterprise only) `FT.DROPINDEX` as touches-arbitrary-keys for proper cluster handling causing crash on A-A (MOD-11090).\n- RediSearch/RediSearch[#7023](https://github.com/redisearch/redisearch/pull/7023) (Redis Enterprise only) Ensure all `FT.SUG*` commands are hashslot-aware to prevent cluster routing errors (MOD-11756)."},{"id":"performance-and-resource-utilization-improvements","title":"Performance and resource utilization improvements","role":"performance","text":"- RediSearch/RediSearch[#7496](https://github.com/redisearch/redisearch/pull/7496) Vector search performance improvements (MOD-12011, MOD-12063, MOD-12629, MOD-12346).\n- RediSearch/RediSearch[#7694](https://github.com/redisearch/redisearch/pull/7694) Use asynchronous jobs in GC for SVS to reduce blocking (MOD-12668)."},{"id":"metrics-1","title":"Metrics","role":"content","text":"- RediSearch/RediSearch[#7614](https://github.com/redisearch/redisearch/pull/7614) Track timeout errors and warnings in info (MOD-12419).\n- RediSearch/RediSearch[#7646](https://github.com/redisearch/redisearch/pull/7646) Track `maxprefixexpansions` errors and warnings in info (MOD-12417).\n- RediSearch/RediSearch[#7957](https://github.com/redisearch/redisearch/pull/7957) Persist query warnings across cursor reads (MOD-12984).\n- RediSearch/RediSearch[#7341](https://github.com/redisearch/redisearch/pull/7341) Rename `FT.PROFILE` counter fields for clarity (MOD-6056).\n- RediSearch/RediSearch[#7436](https://github.com/redisearch/redisearch/pull/7436) Enhance `FT.PROFILE` with vector search execution details (MOD-12263).\n- RediSearch/RediSearch[#7737](https://github.com/redisearch/redisearch/pull/7737) Add `Internal cursor reads` metric to cluster `FT.PROFILE` output (MOD-12414).\n- RediSearch/RediSearch[#7692](https://github.com/redisearch/redisearch/pull/7692) Declare query error struct on `_FT.CURSOR PROFILE` (MOD-12955).\n- RediSearch/RediSearch[#7552](https://github.com/redisearch/redisearch/pull/7552) Add `active_io_threads` metric (MOD-12069, MOD-12695).\n- RediSearch/RediSearch[#7564](https://github.com/redisearch/redisearch/pull/7564) Add `active_worker_threads` metric (MOD-12694, MOD-12069).\n- RediSearch/RediSearch[#7623](https://github.com/redisearch/redisearch/pull/7623) Add `active_coord_threads` metric (MOD-12694, MOD-12069).\n- RediSearch/RediSearch[#7626](https://github.com/redisearch/redisearch/pull/7626) Add `*_pending_jobs` metrics for job queues (MOD-12069).\n- RediSearch/RediSearch[#7672](https://github.com/redisearch/redisearch/pull/7672) Add pending workers admin jobs metric (MOD-12069, MOD-12791).\n- RediSearch/RediSearch[#7732](https://github.com/redisearch/redisearch/pull/7732) Introduce `active_topology_update_threads` metric (MOD-12069, MOD-12790).\n- RediSearch/RediSearch[#7759](https://github.com/redisearch/redisearch/pull/7759) Extend indexing metrics for better observability (MOD-12070)."},{"id":"configuration-parameters","title":"Configuration parameters","role":"configuration","text":"- RediSearch/RediSearch[#7083](https://github.com/redisearch/redisearch/pull/7083) Add default scorer configuration option (MOD-10037)"},{"id":"redis-open-source-823-november-2025","title":"Redis Open Source 8.2.3 (November 2025)","role":"content","text":"Update urgency: `SECURITY`: There is a security fix in the release."},{"id":"security-fixes-5","title":"Security fixes","role":"security","text":"- (CVE-2025-62507) `XACKDEL` - potential stack overflow and RCE"},{"id":"bug-fixes-4","title":"Bug fixes","role":"content","text":"- `HGETEX` - potential crash when `FIELDS` is used  and `numfields` is missing\n- Potential crash on HyperLogLog with 2GB+ entries\n- Cuckoo filter - Division by zero in Cuckoo filter insertion\n- Cuckoo filter - Counter overflow\n- Bloom filter - Arbitrary memory read/write with invalid filter\n- Bloom filter - Out-of-bounds access with empty chain\n- Bloom filter - Restore invalid filter\n- Top-k - Out-of-bounds access"},{"id":"redis-open-source-822-october-2025","title":"Redis Open Source 8.2.2 (October 2025)","role":"content","text":"Update urgency: `SECURITY`: There are security fixes in the release."},{"id":"security-fixes-6","title":"Security fixes","role":"security","text":"- (CVE-2025-49844) A Lua script may lead to remote code execution\n- (CVE-2025-46817) A Lua script may lead to integer overflow and potential RCE\n- (CVE-2025-46818) A Lua script can be executed in the context of another user\n- (CVE-2025-46819) LUA out-of-bound read"},{"id":"new-features","title":"New Features","role":"content","text":"- [#14223](https://github.com/redis/redis/pull/14223) `VSIM`: new `EPSILON` argument to specify maximum distance\n- [#Q6867](https://github.com/RediSearch/RediSearch/pull/6867), [#Q6845](https://github.com/RediSearch/RediSearch/pull/6845) `SVS-VAMANA`: allow use of `BUILD_INTEL_SVS_OPT` flag for Intel optimisations (MOD-10920)"},{"id":"bug-fixes-5","title":"Bug fixes","role":"content","text":"- [#14319](https://github.com/redis/redis/pull/14319) Potential crash on Lua script defrag\n- [#14323](https://github.com/redis/redis/pull/14323) Potential crash on streams and HFE defrag\n- [#14330](https://github.com/redis/redis/pull/14330) Potential use-after-free after pubsub and Lua defrag\n- [#14288](https://github.com/redis/redis/pull/14288) `MEMORY USAGE`: fix reported value\n- [#14259](https://github.com/redis/redis/pull/14259) `XGROUP CREATE`, `XGROUP SETID`: limit `ENTRIESREAD` value to the number of entries added to the stream\n- [#J1374](https://github.com/RedisJSON/RedisJSON/issues/1374) `JSON.DEL` doesn’t delete all matching object members / array elements (MOD-11032, MOD-11067)\n- [#P886](https://github.com/RedisBloom/RedisBloom/pull/886) `TDIGEST.CREATE` crashes (OOM) on huge initialization values (MOD-10840)\n- [#Q6787](https://github.com/RediSearch/RediSearch/pull/6787) Potential shard restart while reindexing vectors on RDB loading (MOD-11011)\n- [#Q6676](https://github.com/RediSearch/RediSearch/pull/6676) Potential crash when using small `CONSTRUCTION_WINDOW_SIZE` on `SVS-VAMANA` (MOD-10771)\n- [#Q6701](https://github.com/RediSearch/RediSearch/pull/6701) Potential crash (OOM) in heavy updates due to a file descriptor leak (MOD-10975)\n- [#Q6723](https://github.com/RediSearch/RediSearch/pull/6723) Potential crash when using ACL rules (MOD-10748)\n- [#Q6641](https://github.com/RediSearch/RediSearch/pull/6641) `INFO SEARCH`: `search_used_memory_indexes` vector index memory value incorrect\n- [#Q6665](https://github.com/RediSearch/RediSearch/pull/6665) `FT.PROFILE`: more accurate execution duration measurements (MOD-10622)"},{"id":"performance-and-resource-utilization","title":"Performance and resource utilization","role":"performance","text":"- [#Q6648](https://github.com/RediSearch/RediSearch/pull/6648) Improve RESP3 serialization performance (MOD-9687)"},{"id":"metrics-2","title":"Metrics","role":"content","text":"- [#Q6671](https://github.com/RediSearch/RediSearch/pull/6671) `INFO SEARCH`: new `SVS-VAMANA` metrics"},{"id":"redis-open-source-821-august-2025","title":"Redis Open Source 8.2.1 (August 2025)","role":"content","text":"Update urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent."},{"id":"bug-fixes-6","title":"Bug fixes","role":"content","text":"- [#14240](https://github.com/redis/redis/pull/14240) `INFO KEYSIZES` - potential incorrect histogram updates on cluster mode with modules.\n- [#14274](https://github.com/redis/redis/pull/14274) Disable Active Defrag during flushing replica.\n- [#14276](https://github.com/redis/redis/pull/14276) `XADD` or `XTRIM` can crash the server after loading RDB.\n- [#Q6601](https://github.com/RediSearch/RediSearch/pull/6601) Potential crash when running `FLUSHDB` (MOD-10681)."},{"id":"performance-and-resource-utilization-1","title":"Performance and resource utilization","role":"performance","text":"- Query Engine - LeanVec and LVQ proprietary Intel optimizations were removed from Redis Open Source.\n- [#Q6621](https://github.com/RediSearch/RediSearch/pull/6621) Fix regression in `INFO` (MOD-10779)."},{"id":"redis-open-source-82-august-2025","title":"Redis Open Source 8.2 (August 2025)","role":"content","text":"This is the General Availability release of Redis 8.2 in Redis Open Source."},{"id":"headlines","title":"Headlines","role":"content","text":"Redis 8.2 introduces major performance and memory footprint improvements, new commands, and command extensions."},{"id":"major-changes-compared-to-80","title":"Major changes compared to 8.0","role":"content","text":"- Streams - new commands: `XDELEX` and `XACKDEL`; extension to `XADD` and `XTRIM`.\n- Bitmap - `BITOP`: new operators: `DIFF`, `DIFF1`, `ANDOR`, and `ONE`.\n- Query Engine - new SVS-VAMANA vector index type which supports vector compression.\n- More than 15 performance and resource utilization improvements.\n- New metrics: per-slot usage metrics, key size distributions for basic data types, and more."},{"id":"binary-distributions","title":"Binary distributions","role":"content","text":"- Alpine and Debian Docker images - https://hub.docker.com/_/redis\n- Install using snap - see https://github.com/redis/redis-snap\n- Install using brew - see https://github.com/redis/homebrew-redis\n- Install using RPM - see https://github.com/redis/redis-rpm\n- Install using Debian APT - see https://github.com/redis/redis-debian"},{"id":"redis-800-was-tested-on-the-following-operating-systems","title":"Redis 8.0.0 was tested on the following operating systems","role":"content","text":"- Ubuntu 22.04 (Jammy Jellyfish), 24.04 (Noble Numbat)\n- Rocky Linux 8.10, 9.5\n- AlmaLinux 8.10, 9.5\n- Debian 12 (Bookworm)\n- macOS 13 (Ventura), 14 (Sonoma), 15 (Sequoia)"},{"id":"security-fixes-compared-to-82-rc1","title":"Security fixes (compared to 8.2-RC1)","role":"security","text":"- (CVE-2025-32023) Fix out-of-bounds write in `HyperLogLog` commands.\n- (CVE-2025-48367) Retry accepting other connections even if the accepted connection reports an error."},{"id":"new-features-compared-to-82-rc1","title":"New Features (compared to 8.2-RC1)","role":"content","text":"- [#14141](https://github.com/redis/redis/pull/14141) Keyspace notifications - new event types:\n  - `OVERWRITTEN` - the value of a key is completely overwritten\n  - `TYPE_CHANGED` - key type change"},{"id":"bug-fixes-compared-to-82-rc1","title":"Bug fixes (compared to 8.2-RC1)","role":"content","text":"- [#14162](https://github.com/redis/redis/pull/14162) Crash when using evport with I/O threads.\n- [#14163](https://github.com/redis/redis/pull/14163) `EVAL` crash when error table is empty.\n- [#14144](https://github.com/redis/redis/pull/14144) Vector sets - RDB format is not compatible with big endian machines.\n- [#14165](https://github.com/redis/redis/pull/14165) Endless client blocking for blocking commands.\n- [#14164](https://github.com/redis/redis/pull/14164) Prevent `CLIENT UNBLOCK` from unblocking `CLIENT PAUSE`.\n- [#14216](https://github.com/redis/redis/pull/14216) TTL was not removed by the `SET` command.\n- [#14224](https://github.com/redis/redis/pull/14224) `HINCRBYFLOAT` removes field expiration on replica."},{"id":"performance-and-resource-utilization-improvements-compared-to-82-rc1","title":"Performance and resource utilization improvements (compared to 8.2-RC1)","role":"performance","text":"- [#Q6430](https://github.com/RediSearch/RediSearch/pull/6430) More compression variants for the SVS-VAMANA vector index.\n- [#Q6535](https://github.com/RediSearch/RediSearch/pull/6535) `SHARD_K_RATIO` parameter - favor network latency over accuracy for KNN vector query in a Redis cluster (unstable feature) (MOD-10359).\n- [#14144](https://github.com/redis/redis/pull/14144) Vector set - improve RDB loading / RESTORE speed by storing the worst link info ."},{"id":"modules-api","title":"Modules API","role":"content","text":"- [#14051](https://github.com/redis/redis/pull/14051) `RedisModule_Get*`, `RedisModule_Set*` - allow modules to access Redis configurations.\n- [#14114](https://github.com/redis/redis/pull/14114) `RM_UnsubscribeFromKeyspaceEvents` - unregister a module from specific keyspace notifications."},{"id":"redis-open-source-82-rc1-july-2025","title":"Redis Open Source 8.2-RC1 (July 2025)","role":"content","text":"This is the first Release Candidate of Redis Open Source 8.2.\n\nRelease Candidates are feature-complete pre-releases. Pre-releases are not suitable for production use."},{"id":"headlines-1","title":"Headlines","role":"content","text":"Redis 8.2 introduces major performance and memory footprint improvements, new commands, and command extensions.\n\nRedis 8.2-RC1 is available as a Docker image and can be downloaded from [Docker Hub](https://hub.docker.com/_/redis). Additional distributions will be introduced in upcoming pre-releases."},{"id":"security-fixes-compared-to-82-m01","title":"Security fixes (compared to 8.2-M01)","role":"security","text":"- (CVE-2025-27151) redis-check-aof may lead to stack overflow and potential RCE"},{"id":"new-features-compared-to-82-m01","title":"New Features (compared to 8.2-M01)","role":"content","text":"- [#14130](https://github.com/redis/redis/pull/14130) Streams - new commands: `XDELEX` and `XACKDEL`; extension to `XADD` and `XTRIM`.\n- [#14039](https://github.com/redis/redis/pull/14039) New command: `CLUSTER SLOT-STATS` - get per-slot usage metrics such as key count, CPU time, and network I/O.\n- [#14122](https://github.com/redis/redis/pull/14122) `VSIM` - new `IN` operator for filtering expressions.\n- [#Q6329](https://github.com/RediSearch/RediSearch/pull/6329), [#Q6394](https://github.com/RediSearch/RediSearch/pull/6394) - Query Engine - new SVS-VAMANA vector index type which supports vector compression (optimized for Intel machines)."},{"id":"bug-fixes-compared-to-82-m01","title":"Bug fixes (compared to 8.2-M01)","role":"content","text":"- [#14143](https://github.com/redis/redis/pull/14143) Gracefully handle short read errors for hashes with TTL during full sync."},{"id":"performance-and-resource-utilization-improvements-compared-to-82-m01","title":"Performance and resource utilization improvements (compared to 8.2-M01)","role":"performance","text":"- [#14103](https://github.com/redis/redis/pull/14103) Optimize `BITCOUNT` by introducing prefetching.\n- [#14121](https://github.com/redis/redis/pull/14121) Optimize `SCAN` by performing expiration checks only on DBs with volatile keys.\n- [#14140](https://github.com/redis/redis/pull/14140) Optimize expiry check in `scanCallback`.\n- [#14131](https://github.com/redis/redis/pull/14131) Optimize `LREM`, `LPOS`, `LINSERT`, `ZRANK`, and more by caching `string2ll` results in `quicklistCompare`.\n- [#14088](https://github.com/redis/redis/pull/14088) Optimize `COPY`, `RENAME`, and `RESTORE` when TTL is used.\n- [#14074](https://github.com/redis/redis/pull/14074) Reduce the overhead associated with tracking `malloc`’s usable memory.\n- [#13900](https://github.com/redis/redis/pull/13900) Optimize the client’s cron to avoid blocking the main thread.\n- [#J1350](https://github.com/RedisJSON/RedisJSON/pull/1350) JSON - memory footprint improvement by inlining numbers (MOD-9511)."},{"id":"metrics-3","title":"Metrics","role":"content","text":"- [#14067](https://github.com/redis/redis/pull/14067) `INFO`: `used_memory_peak_time` - time when `used_memory_peak` was hit.\n- [#13990](https://github.com/redis/redis/pull/13990) `INFO`:\n  - `master_current_sync_attempts` - number of times the replica attempted to sync to a master since last disconnection.\n  - `master_total_sync_attempts` - number of times the replica attempted to sync to a master.\n  - `master_link_up_since_seconds` - number of seconds since the link has been up.\n  - `total_disconnect_time_sec` - total cumulative time the replica has been disconnected."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.4-release-notes","title":"Redis Open Source 8.4 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.4-release-notes/","summary":"Redis Open Source 8.4 release notes.","tags":["docs","operate","stack"],"last_updated":"2026-08-17T13:21:31-07:00","page_type":"content","content_hash":"74ab8bb8737f0f05a7e70d5313033fce5e59a1985b80f4ca64ba9f79189e5251","sections":[{"id":"redis-open-source-846-august-2026","title":"Redis Open Source 8.4.6 (August 2026)","role":"overview","text":"Update urgency: `SECURITY`: There are security fixes in the release."},{"id":"security-fixes","title":"Security fixes","role":"security","text":"- (CVE-2026-62356) Miscalculated buffer size in `CMSketch` RDB loading may lead to heap OOB write.\n- Out-of-bounds access in TopK heap cleanup path (MOD-15410).\n- Use-after-free in the TLS pending-data list when a command closes another pending connection.\n- [#15478](https://github.com/redis/redis/pull/15478) ACL key permission bypass in `SORT`, `GEORADIUS`/`GEORADIUSBYMEMBER` and `XREAD`/`XREADGROUP`: the keys validated by ACL could differ from the keys the command actually accesses.\n- [#14847](https://github.com/redis/redis/pull/14847) Out-of-bounds `argv` access during key extraction when checking ACL permissions of a `KEYNUM keyspec` command (for example, `EVAL`) with wrong arity.\n- A malicious RDB payload with an out-of-range `SLOT_INFO slot id` causes memory corruption during RDB loading, which may lead to Remote Code Execution.\n- Vector Sets: missing node level validation when loading a vector set from RDB may lead to out-of-bounds access.\n- Vector Sets: use-after-free when `VREM` mutates the HNSW graph while background `VSIM` threads are still running.\n- Vector Sets: a negative `hnsw_search()` return was treated as a huge unsigned count, reading past the end of the result arrays.\n- [#15594](https://github.com/redis/redis/pull/15594) Use-after-free in the blocked-client list when reprocessing a command evicts another client blocked on the same key."},{"id":"redis-open-source-845-july-2026","title":"Redis Open Source 8.4.5 (July 2026)","role":"content","text":"SECURITY: There are security fixes in the release."},{"id":"security-fixes-1","title":"Security fixes","role":"security","text":"- Use-after-free when loading a stream consumer group via `RESTORE` may lead to Remote Code Execution.\n- RedisBloom/RedisBloom[#1039](https://github.com/redisbloom/redisbloom/pull/1039) Crafted RESTORE payloads in RedisBloom and TDigest may trigger out-of-bounds writes, potentially leading to remote code execution."},{"id":"bug-fixes","title":"Bug fixes","role":"content","text":"- RedisBloom/RedisBloom[#1020](https://github.com/redisbloom/redisbloom/pull/1020) Replicate `CF.LOADCHUNK` data chunks to prevent silent Cuckoo Filter data loss on failover."},{"id":"redis-open-source-844-june-2026","title":"Redis Open Source 8.4.4 (June 2026)","role":"content","text":"Update urgency: `HIGH`: There are critical bugs that may affect a subset of users."},{"id":"bug-fixes-1","title":"Bug fixes","role":"content","text":"- [#15175](https://github.com/redis/redis/pull/15175), RediSearch/RediSearch[#9262](https://github.com/redisearch/redisearch/pull/9262) Redis fails to start on AArch64.\n- [#15163](https://github.com/redis/redis/pull/15163) `MULTI` queue memory incorrect memory accounting.\n- [#14581](https://github.com/redis/redis/pull/14581) Rare server hang at shutdown.\n- [#14545](https://github.com/redis/redis/pull/14545) ACL: AOF loading fails if ACL rules are changed and don't allow some commands in `MULTI`-`EXEC`.\n- [#14537](https://github.com/redis/redis/pull/14537) `SCAN`: restore original filter order (revert change introduced in 8.2).\n- [#14816](https://github.com/redis/redis/pull/14816) setModuleEnumConfig() passing prefixed name to module callbacks.\n- [#14659](https://github.com/redis/redis/pull/14659) ACL: Key-pattern bypass in `MSETEX`.\n- [#14623](https://github.com/redis/redis/pull/14623) Streams: `XTRIM`/`XADD` with approx mode (`~`) don’t delete entries for `DELREF`/`ACKED` strategies.\n- [#14552](https://github.com/redis/redis/pull/14552) Streams: Incorrect behavior when using `XDELEX...`ACKED` after `XGROUP DESTROY`.\n- [#14848](https://github.com/redis/redis/pull/14848) Crash during command processing on replicas performing full synchronization.\n- [#15188](https://github.com/redis/redis/pull/15188) `cluster-announce-ip` rejecting hostnames (regression).\n- [#14667](https://github.com/redis/redis/pull/14667), [#14886](https://github.com/redis/redis/pull/14886) Potential TCP stalls/deadlocks.\n- RediSearch/RediSearch[#9484](https://github.com/redisearch/redisearch/pull/9484) Shard crash during background index scan of JSON documents with vector fields on Active-Active (CRDT) databases (MOD-15542).\n- RediSearch/RediSearch[#9635](https://github.com/redisearch/redisearch/pull/9635) Severe latency spikes and shard unresponsiveness when `EXPIRE` or `EXPIREAT` operations run concurrently with queries on large indexes (MOD-14930)."},{"id":"redis-open-source-843-may-2026","title":"Redis Open Source 8.4.3 (May 2026)","role":"content","text":"Update urgency: `SECURITY`: There are security fixes in the release."},{"id":"security-fixes-2","title":"Security fixes","role":"security","text":"- (CVE-2026-23479) Use-After-Free in unblock client flow may lead to Remote Code Execution.\n- (CVE-2026-25243) Invalid memory access in `RESTORE` may lead to Remote Code Execution.\n- (CVE-2026-23631) Lua Use-After-Free may lead to remote code execution.\n- (CVE-2026-25588) Invalid memory access in `RESTORE` may lead to Remote Code Execution (Time Series).\n- (CVE-2026-25589) Invalid memory access in `RESTORE` may lead to Remote Code Execution (Probabilistic)."},{"id":"bug-fixes-2","title":"Bug fixes","role":"content","text":"- `SUBSCRIBE`, `PSUBSCRIBE`, `SSUBSCRIBE`: crash on OOM (RED-167788).\n- `CONFIG SET`: some settings allow invalid characters (RED-167787).\n- `SCRIPT DEBUG`: potential crash on scripts (RED-175507).\n- `VADD`: crash or buffer overflow on large `REDUCE` value (RED-170921).\n- `VSET`: crash on huge allocations (MOD-12678).\n- Potential crash on disconnections and TLS failures (Time Series) (MOD-14850).\n- RediSearch/RediSearch[#8744](https://github.com/redisearch/redisearch/pull/8744) Crash when many keys receive expirations under heavy TTL activity (MOD-14500).\n- RediSearch/RediSearch[#8849](https://github.com/redisearch/redisearch/pull/8849) HNSW vector index memory growth under high-churn workloads until shard restart (MOD-13761).\n- RediSearch/RediSearch[#8258](https://github.com/redisearch/redisearch/pull/8258) `FT.HYBRID` `VSIM RANGE` + `FILTER` incorrectly returns zero results (MOD-13885).\n- RediSearch/RediSearch[#9183](https://github.com/redisearch/redisearch/pull/9183) `FT.PROFILE HYBRID` returns an empty reply (MOD-14778).\n- RediSearch/RediSearch[#9048](https://github.com/redisearch/redisearch/pull/9048) `FT.PROFILE` output is inconsistent when a profiled value is missing (MOD-10560).\n- RediSearch/RediSearch[#8792](https://github.com/redisearch/redisearch/pull/8792) `FT.EXPLAIN` does not lock, causing a race with concurrent index changes (MOD-14461).\n- RediSearch/RediSearch[#8384](https://github.com/redisearch/redisearch/pull/8384) Crash when indexing negative zero (-0.0) (MOD-13904).\n- RediSearch/RediSearch[#8596](https://github.com/redisearch/redisearch/pull/8596) `FILTER` returns inconsistent results with multiple indexes sharing field aliases (MOD-14063).\n- RediSearch/RediSearch[#8661](https://github.com/redisearch/redisearch/pull/8661) `FILTER` behavior depends on property order in the expression (MOD-14065).\n- RediSearch/RediSearch[#8598](https://github.com/redisearch/redisearch/pull/8598) Filter expressions are evaluated for indexes that do not match the document type (MOD-14064).\n- RediSearch/RediSearch[#8597](https://github.com/redisearch/redisearch/pull/8597) Documents are inconsistently included or excluded depending on the indexing path taken (MOD-13948).\n- RediSearch/RediSearch[#8595](https://github.com/redisearch/redisearch/pull/8595) `RENAME` notification handler loads the wrong key, causing stale index entries after a rename (MOD-14062).\n- RediSearch/RediSearch[#9011](https://github.com/redisearch/redisearch/pull/9011) `PERSIST` and `HPERSIST` notifications are not reflected in index expiration tracking (MOD-14800).\n- RediSearch/RediSearch[#9080](https://github.com/redisearch/redisearch/pull/9080) `FT.SPELLCHECK` treats `PARAMS` placeholders as literal terms instead of resolving them (MOD-10596).\n- RediSearch/RediSearch[#8461](https://github.com/redisearch/redisearch/pull/8461) GC out-of-memory on replica shards leaves the replica in an inconsistent state (MOD-14066).\n- RediSearch/RediSearch[#9091](https://github.com/redisearch/redisearch/pull/9091) Race condition in `FT.HYBRID` causes intermittent failures under concurrent hybrid query load (MOD-14732).\n- RediSearch/RediSearch[#9161](https://github.com/redisearch/redisearch/pull/9161) Coordinator deadlock under mixed `FT.SEARCH` and `FT.AGGREGATE` load (MOD-14268).\n- RediSearch/RediSearch[#9165](https://github.com/redisearch/redisearch/pull/9165) Crash on `FT.SEARCH` when topology validation fails (for example, some nodes unreachable) (MOD-14475).\n- RediSearch/RediSearch[#8394](https://github.com/redisearch/redisearch/pull/8394) `FT.SEARCH` fails with \"Query requires unavailable slots\" after shard restart or failover (MOD-13828).\n- RediSearch/RediSearch[#8452](https://github.com/redisearch/redisearch/pull/8452) `FT.INFO`-style output no longer reports zero-index summary data when no indices exist (MOD-14080).\n- RediSearch/RediSearch[#9077](https://github.com/redisearch/redisearch/pull/9077) `FT.CREATE` now rejects schema definitions with invalid option combinations at creation time (MOD-14655)."},{"id":"metrics","title":"Metrics","role":"content","text":"- RediSearch/RediSearch[#8210](https://github.com/redisearch/redisearch/pull/8210), RediSearch/RediSearch[#8231](https://github.com/redis/redis/pull/8231) `FT.PROFILE`: added queue time tracking (MOD-13602)."},{"id":"redis-open-source-842-february-2026","title":"Redis Open Source 8.4.2 (February 2026)","role":"content","text":"Update urgency: `SECURITY`: There are security fixes in the release."},{"id":"security-fixes-3","title":"Security fixes","role":"security","text":"- A user can manipulate data read by a connection by injecting `\\r\\n` sequences into a Redis error reply."},{"id":"redis-open-source-841-february-2026","title":"Redis Open Source 8.4.1 (February 2026)","role":"content","text":"Update urgency: `SECURITY`: There are security fixes in the release."},{"id":"security-fixes-4","title":"Security fixes","role":"security","text":"- RedisTimeSeries/RedisTimeSeries[#1837](https://github.com/redistimeseries/redistimeseries/pull/1837), RedisJSON/RedisJSON[#1474](https://github.com/redisjson/redisjson/pull/1474) Hide personally identifiable information from server log.\n- RedisBloom/RedisBloom[#936](https://github.com/redisbloom/redisbloom/pull/936) Cuckoo filter: crash on RDB load on 0 buckets (MOD-11593).\n- RedisBloom/RedisBloom[#945](https://github.com/redisbloom/redisbloom/pull/945) Bloom filter: crash on RDB load on large number of filters (MOD-11590)."},{"id":"bug-fixes-3","title":"Bug fixes","role":"content","text":"- [#14637](https://github.com/redis/redis/pull/14637) Atomic slot migration: wrong adjacent slot range behavior.\n- [#14567](https://github.com/redis/redis/pull/14567) Atomic slot migration: support delay trimming slots after finishing migrating slots.\n- [#14746](https://github.com/redis/redis/pull/14746) `CLUSTER SLOT-STATS`: Fix a few memory tracking bugs.\n- RedisTimeSeries/RedisTimeSeries[#1864](https://github.com/redistimeseries/redistimeseries/pull/1864) Atomic slot migration: time series limitations (MOD-13611).\n- RediSearch/RediSearch[#6973](https://github.com/redisearch/redisearch/pull/6973) Correct empty string token counting in `byteOffset` calculations to ensure accurate text position tracking (MOD-11233).\n- RediSearch/RediSearch[#6995](https://github.com/redisearch/redisearch/pull/6995) Prevent `FT.INFO` command fanout to replicas to reduce unnecessary cluster traffic.\n- RediSearch/RediSearch[#7154](https://github.com/redisearch/redisearch/pull/7154) Display Background Indexing OOM warning in `FT.AGGREGATE` when memory limits are approached (MOD-11817).\n- RediSearch/RediSearch[#7219](https://github.com/redisearch/redisearch/pull/7219) Resolve concurrency issue in `FT.AGGREGATE `reducer that caused intermittent errors (MOD-12243).\n- RediSearch/RediSearch[#7255](https://github.com/redisearch/redisearch/pull/7255) Correct `BM25STD` underflow wraparound to prevent incorrect scoring (MOD-12223).\n- RediSearch/RediSearch[#7264](https://github.com/redisearch/redisearch/pull/7264) Ensure accurate `totalDocsLen` updates to maintain correct document statistics (MOD-12234).\n- RediSearch/RediSearch[#7275](https://github.com/redisearch/redisearch/pull/7275) Report used memory as `unsigned long` to prevent overflow (RED-169833).\n- RediSearch/RediSearch[#7350](https://github.com/redisearch/redisearch/pull/7350) `FT.CREATE` with LeanVec parameters on non-Intel architectures (RED-176382).\n- RediSearch/RediSearch[#7371](https://github.com/redisearch/redisearch/pull/7371) Validate `search-min-operation-workers` min value correctly (MOD-12383).\n- RediSearch/RediSearch[#7430](https://github.com/redisearch/redisearch/pull/7430) Prevent coordinator deadlock in `FT.HYBRID` queries by avoiding index read lock (MOD-12489).\n- RediSearch/RediSearch[#7435](https://github.com/redisearch/redisearch/pull/7435) Ensure full profile output on timeout with `RETURN` policy in `FT.PROFILE` (MOD-12320).\n- RediSearch/RediSearch[#7446](https://github.com/redisearch/redisearch/pull/7446) Remove outdated validation from debug aggregate in cluster mode (MOD-12435).\n- RediSearch/RediSearch[#7455](https://github.com/redisearch/redisearch/pull/7455) Ensure internal cursors are deleted immediately in cluster mode (MOD-12493).\n- RediSearch/RediSearch[#7458](https://github.com/redisearch/redisearch/pull/7458) Correct GC regression that caused stability issues (MOD-12538).\n- RediSearch/RediSearch[#7460](https://github.com/redisearch/redisearch/pull/7460) Prevent potential double-free on error path in Fork GC (MOD-12521).\n- RediSearch/RediSearch[#7499](https://github.com/redisearch/redisearch/pull/7499) Propagate `HGETALL` command in HDT mode (MOD-12662).\n- RediSearch/RediSearch[#7534](https://github.com/redisearch/redisearch/pull/7534) Reduce number of worker threads asynchronously to prevent performance degradation (MOD-12252, MOD-11658).\n- RediSearch/RediSearch[#7553](https://github.com/redisearch/redisearch/pull/7553) Handle `WITHSCORES` correctly when `SCORE` is sent alone without extra fields in coordinator (MOD-12647).\n- RediSearch/RediSearch[#7560](https://github.com/redisearch/redisearch/pull/7560) Properly handle connection closing in IO thread at shutdown and fix `searchRequestCtx` freeing on error (MOD-12699).\n- RediSearch/RediSearch[#7685](https://github.com/redisearch/redisearch/pull/7685) Resolve cursor logical leak that could lead to resource exhaustion (MOD-12807).\n- RediSearch/RediSearch[#7710](https://github.com/redisearch/redisearch/pull/7710) Support for `WITHCOUNT` in `FT.AGGREGATE` (MOD-11751).\n- RediSearch/RediSearch[#7794](https://github.com/redisearch/redisearch/pull/7794) Correctly handle binary data with embedded nulls to prevent crashes (MOD-13010).\n- RediSearch/RediSearch[#7812](https://github.com/redisearch/redisearch/pull/7812) Correct SVS GC for no-workers case (MOD-12983).\n- RediSearch/RediSearch[#7815](https://github.com/redisearch/redisearch/pull/7815) Fix command routing in cluster mode by not relying on shard index (MOD-13049).\n- RediSearch/RediSearch[#7823](https://github.com/redisearch/redisearch/pull/7823) Support vector blob only through parameter in `FT.HYBRID`(MOD-13123).\n- RediSearch/RediSearch[#7873](https://github.com/redisearch/redisearch/pull/7873) Handle warnings in empty `FT.AGGREGATE` replies in cluster mode (MOD-12640).\n- RediSearch/RediSearch[#7897](https://github.com/redisearch/redisearch/pull/7897) Remove asserts from `DownloadFile` to prevent crash (MOD-13096).\n- RediSearch/RediSearch[#7901](https://github.com/redisearch/redisearch/pull/7901) Support multiple warnings in reply to prevent warning loss (MOD-13252).\n- RediSearch/RediSearch[#7903](https://github.com/redisearch/redisearch/pull/7903) Eliminate memory leak in `FT.HYBRID` queries with Active-Active enabled (MOD-13143).\n- RediSearch/RediSearch[#7886](https://github.com/redisearch/redisearch/pull/7886) Remove non-TEXT fields from spec's keys dictionary to prevent incorrect field handling (MOD-13150).\n- RediSearch/RediSearch[#7905](https://github.com/redisearch/redisearch/pull/7905) Remove non-TEXT fields from spec's keys dictionary and refactor keys dict (MOD-13150, MOD-13151).\n- RediSearch/RediSearch[#7978](https://github.com/redisearch/redisearch/pull/7978) Avoid using negative key position values during command registration (MOD-13332).\n- RediSearch/RediSearch[#8052](https://github.com/redisearch/redisearch/pull/8052) Resolve incorrect results when using `LOAD *` with `FT.HYBRID` (MOD-12736, MOD-13556).\n- RediSearch/RediSearch[#8083](https://github.com/redisearch/redisearch/pull/8083) Correct `FULLTEXT` field metric count accuracy (MOD-13432).\n- RediSearch/RediSearch[#8089](https://github.com/redisearch/redisearch/pull/8089) Handle edge case in clusterset (MOD-13562).\n- RediSearch/RediSearch[#8151](https://github.com/redisearch/redisearch/pull/8151) Correct `FT.PROFILE` shard total profile time calculation (MOD-13735, MOD-13181).\n- RediSearch/RediSearch[#8153](https://github.com/redisearch/redisearch/pull/8153) Resolve config registration issue (RED-171841).\n- RediSearch/RediSearch[#7449](https://github.com/redisearch/redisearch/pull/7449) Ensure `FT.HYBRID` respects timeout settings (MOD-11004).\n- RediSearch/RediSearch[#7238](https://github.com/redisearch/redisearch/pull/7238) Initialize `GIL_TIME` properly for `FT.PROFILE` (MOD-12553).\n- RediSearch/RediSearch[#7453](https://github.com/redisearch/redisearch/pull/7453) Error behavior on early bailout and split OOM warning for shard and coordinator (MOD-12449).\n- RediSearch/RediSearch[#7615](https://github.com/redisearch/redisearch/pull/7615) Parameter `numDocs` from non-optimized wildcard iterator (MOD-12392).\n- RediSearch/RediSearch[#7165](https://github.com/redisearch/redisearch/pull/7165) (Redis Enterprise only) `FT.DROPINDEX` as touches-arbitrary-keys for proper cluster handling causing crash on A-A (MOD-11090).\n- RediSearch/RediSearch[#7023](https://github.com/redisearch/redisearch/pull/7023) (Redis Enterprise only) Ensure all `FT.SUG*` commands are hashslot-aware to prevent cluster routing errors (MOD-11756)."},{"id":"performance-and-resource-utilization-improvements","title":"Performance and resource utilization improvements","role":"performance","text":"- RediSearch/RediSearch[#7496](https://github.com/redisearch/redisearch/pull/7496) Vector search performance improvements (MOD-12011, MOD-12063, MOD-12629, MOD-12346).\n- RediSearch/RediSearch[#7519](https://github.com/redisearch/redisearch/pull/7519) Reduce number of worker threads asynchronously to improve resource utilization (MOD-12252, MOD-11658).\n- RediSearch/RediSearch[#7694](https://github.com/redisearch/redisearch/pull/7694) Use asynchronous jobs in GC for SVS to reduce blocking (MOD-12668).\n- RediSearch/RediSearch[#7730](https://github.com/redisearch/redisearch/pull/7730) Support `filter_policy` and `batch_size` parameters for vector similarity search tuning (MOD-13007, MOD-12371).\n- RediSearch/RediSearch[#7782](https://github.com/redisearch/redisearch/pull/7782) Resolve SVS GC failures when worker threads are disabled (MOD-12983).\n- RediSearch/RediSearch[#7572](https://github.com/redisearch/redisearch/pull/7572) Implement ASM state machine on notifications (MOD-12170).\n- RediSearch/RediSearch[#7829](https://github.com/redisearch/redisearch/pull/7829) ASM-aware search flow for Active-Active deployments (MOD-12171, MOD-12169).\n- RediSearch/RediSearch[#7589](https://github.com/redisearch/redisearch/pull/7589) Support multiple slot ranges in `search.CLUSTERSET` for flexible cluster topology updates (MOD-11657).\n- RediSearch/RediSearch[#7862](https://github.com/redisearch/redisearch/pull/7862) Support subquery count in `FT.HYBRID` (MOD-11858, MOD-13146).\n- RediSearch/RediSearch[#7893](https://github.com/redisearch/redisearch/pull/7893) Request policy support for cursor operations (MOD-13146, MOD-9573, MOD-8104).\n- RediSearch/RediSearch[#8087](https://github.com/redisearch/redisearch/pull/8087) Warning when cursor may give inaccurate results due to Active-Active replication (MOD-12899).\n- RediSearch/RediSearch[#7445](https://github.com/redisearch/redisearch/pull/7445) Remove outdated validation from Debug Aggregate in cluster mode (MOD-12435).\n- RediSearch/RediSearch[#7384](https://github.com/redisearch/redisearch/pull/7384) Reduce index load from RDB temporary memory overhead (MOD-12212)."},{"id":"metrics-1","title":"Metrics","role":"content","text":"- RediSearch/RediSearch[#7960](https://github.com/redisearch/redisearch/pull/7960) Persist query warnings across cursor reads (MOD-12984).\n- RediSearch/RediSearch[#7612](https://github.com/redisearch/redisearch/pull/7612) Track `maxprefixexpansions` errors and warnings in info (MOD-12417).\n- RediSearch/RediSearch[#7872](https://github.com/redisearch/redisearch/pull/7872) Handle warnings in empty `FT.AGGREGATE` replies in cluster mode (MOD-12640).\n- RediSearch/RediSearch[#7900](https://github.com/redisearch/redisearch/pull/7900) Support multiple warnings in reply (MOD-13252).\n- RediSearch/RediSearch[#7576](https://github.com/redisearch/redisearch/pull/7576) Track OOM errors and warnings in info (MOD-12418).\n- RediSearch/RediSearch[#7507](https://github.com/redisearch/redisearch/pull/7507) Track timeout errors and warnings in info (MOD-12419).\n- RediSearch/RediSearch[#7341](https://github.com/redisearch/redisearch/pull/7341) Rename `FT.PROFILE` counter fields for clarity .(MOD-6056)\n- RediSearch/RediSearch[#7436](https://github.com/redisearch/redisearch/pull/7436), RediSearch/RediSearch[#7427](https://github.com/redisearch/redisearch/pull/7427) Enhance `FT.PROFILE` with vector search execution details (MOD-12263).\n- RediSearch/RediSearch[#7573](https://github.com/redisearch/redisearch/pull/7573) Debug support for `FT.PROFILE` command (MOD-12627).\n- RediSearch/RediSearch[#7736](https://github.com/redisearch/redisearch/pull/7736) Add `Internal cursor reads` metric to cluster `FT.PROFILE` output (MOD-12414).\n- RediSearch/RediSearch[#7692](https://github.com/redisearch/redisearch/pull/7692) Declare query error struct on `_FT.CURSOR PROFILE` (MOD-12955).\n- RediSearch/RediSearch[#7848](https://github.com/redisearch/redisearch/pull/7848) Store and display shard ID in profile output (MOD-12321).\n- RediSearch/RediSearch[#7422](https://github.com/redisearch/redisearch/pull/7422) Track syntax and argument errors in query error metrics (MOD-12416).\n- RediSearch/RediSearch[#7552](https://github.com/redisearch/redisearch/pull/7552) Add `active_io_threads` metric (MOD-12069, MOD-12695).\n- RediSearch/RediSearch[#7622](https://github.com/redisearch/redisearch/pull/7622) Add `active_coord_threads` metric (MOD-12694, MOD-12069).\n- RediSearch/RediSearch[#7564](https://github.com/redisearch/redisearch/pull/7564) Add `active_worker_threads` metric (MOD-12694, MOD-12069).\n- RediSearch/RediSearch[#7626](https://github.com/redisearch/redisearch/pull/7626) Add `*_pending_jobs` metrics for job queues (MOD-12069).\n- RediSearch/RediSearch[#7658](https://github.com/redisearch/redisearch/pull/7658) Add pending workers admin jobs metric (MOD-12069, MOD-12791).\n- RediSearch/RediSearch[#7731](https://github.com/redisearch/redisearch/pull/7731) Add `active_topology_update_threads` metric (MOD-12069, MOD-12790).\n- RediSearch/RediSearch[#7760](https://github.com/redisearch/redisearch/pull/7760) Extend indexing metrics for more detailed performance data (MOD-12070)."},{"id":"configuration-parameters","title":"Configuration parameters","role":"configuration","text":"- RediSearch/RediSearch[#7083](https://github.com/redisearch/redisearch/pull/7083) Add default scorer configuration option (MOD-10037)."},{"id":"redis-open-source-840-november-2025","title":"Redis Open Source 8.4.0 (November 2025)","role":"content","text":"This is the General Availability (GA) release of Redis 8.4 in Redis Open Source."},{"id":"major-changes-compared-to-redis-82","title":"Major changes compared to Redis 8.2","role":"content","text":"- `DIGEST`, `DELEX`; `SET` extensions - atomic compare-and-set and compare-and-delete for string keys\n- `MSETEX` - atomically set multiple string keys and update their expiration\n- `XREADGROUP` - new `CLAIM` option for reading both idle pending and incoming stream entries\n- `CLUSTER MIGRATION` - atomic slot migration\n- `CLUSTER SLOT-STATS` - per-slot usage metrics: key count, CPU time, and network I/O\n- Redis query engine: `FT.HYBRID` - hybrid search and fused scoring\n- Redis query engine: I/O threading with performance boost for search and query commands (`FT.*`) \n- I/O threading: substantial throughput increase (e.g. >30% for caching use cases (10% `SET`, 90% `GET`), 4 cores)\n- JSON: substantial memory reduction for homogenous arrays (up to 91%)"},{"id":"binary-distributions","title":"Binary distributions","role":"content","text":"- Alpine and Debian Docker images - https://hub.docker.com/_/redis\n- Install using snap - see https://github.com/redis/redis-snap\n- Install using brew - see https://github.com/redis/homebrew-redis\n- Install using RPM - see https://github.com/redis/redis-rpm\n- Install using Debian APT - see https://github.com/redis/redis-debian"},{"id":"redis-84-was-tested-on-the-following-operating-systems","title":"Redis 8.4 was tested on the following operating systems","role":"content","text":"- Ubuntu 22.04 (Jammy Jellyfish), 24.04 (Noble Numbat)\n- Rocky Linux 8.10, 9.5\n- AlmaLinux 8.10, 9.5\n- Debian 12 (Bookworm), Debian 13 (Trixie)\n- macOS 13 (Ventura), 14 (Sonoma), 15 (Sequoia)"},{"id":"bug-fixes-compared-to-84-rc1","title":"Bug fixes (compared to 8.4-RC1)","role":"content","text":"- [#14524](https://github.com/redis/redis/pull/14524) `XREADGROUP CLAIM` returns strings instead of integers\n- [#14529](https://github.com/redis/redis/pull/14529) Add variable key-spec flags to `SET IF*` and `DELEX`\n- #P928 Potential memory leak (MOD-11484)\n- #T1801, #T1805 macOS build failures (MOD-12293)\n- #J1438 `JSON.NUMINCRBY` - wrong result on integer array with non-integer increment (MOD-12282)\n- #J1437 Thread safety issue related to ASM and shared strings (MOD-12013)"},{"id":"performance-and-resource-utilization-improvements-compared-to-84-rc1","title":"Performance and resource utilization improvements (compared to 8.4-RC1)","role":"performance","text":"- [#14480](https://github.com/redis/redis/pull/14480), [#14516](https://github.com/redis/redis/pull/14516) Optimize `XREADGROUP`"},{"id":"known-bugs-and-limitations","title":"Known bugs and limitations","role":"content","text":"- 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.\n- `FT.PROFILE`, `FT.EXPLAIN` and `FT.EXPLACINCLI` do not contain the `FT.HYBRID` option.\n- Metrics from `FT.HYBRID` command aren’t displayed on `FT.INFO` and `INFO`.\n- Option `EXPLAINSCORE`, `SHARD_K_RATIO`, `YIELD_DISTANCE_AS` and `WITHCURSOR` with `FT.HYBRID` are not available.\n- Post-filtering (after `COMBINE` step) using FILTER is not available.\n- Currently the default response format considers only `key_id` and `score`, this may change for delivering entire document content."},{"id":"redis-open-source-84-rc1-november-2025","title":"Redis Open Source 8.4-RC1 (November 2025)","role":"content","text":"This is the first Release Candidate of Redis 8.4 in Redis Open Source.\n\nRelease Candidates are feature-complete pre-releases. Pre-releases are not suitable for production use."},{"id":"binary-distributions-1","title":"Binary distributions","role":"content","text":"- Alpine and Debian Docker images - https://hub.docker.com/_/redis\n- Install using snap - see https://github.com/redis/redis-snap\n- Install using brew - see https://github.com/redis/homebrew-redis\n- Install using RPM - see https://github.com/redis/redis-rpm\n- Install using Debian APT - see https://github.com/redis/redis-debian"},{"id":"redis-84-was-tested-on-the-following-operating-systems-1","title":"Redis 8.4 was tested on the following operating systems","role":"content","text":"- Ubuntu 22.04 (Jammy Jellyfish), 24.04 (Noble Numbat)\n- Rocky Linux 8.10, 9.5\n- AlmaLinux 8.10, 9.5\n- Debian 12 (Bookworm), Debian 13 (Trixie)\n- macOS 13 (Ventura), 14 (Sonoma), 15 (Sequoia)"},{"id":"new-features-compared-to-823","title":"New Features (compared to 8.2.3)","role":"content","text":"- [#14414](https://github.com/redis/redis/pull/14414) New command: `CLUSTER MIGRATION` - atomic slot migration.\n- [#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.\n- [#14434](https://github.com/redis/redis/pull/14434) New command: `MSETEX` - set multiple keys and update their expiration.\n- [#14402](https://github.com/redis/redis/pull/14402) `XREADGROUP` - add `CLAIM min-idle-time` to consume both idle pending entries and incoming entries.\n- [#14058](https://github.com/redis/redis/pull/14058) Add auto-repair options for broken AOF tail on startup.\n- [#14296](https://github.com/redis/redis/pull/14296) Support decoding JSON empty array as a Lua array.\n- RedisTimeSeries/RedisTimeSeries[#1773](https://github.com/redistimeseries/redistimeseries/pull/1773) `HELP` and `COMMAND DOCS` now support time series commands (MOD-8133).\n- RedisBloom/RedisBloom[#892](https://github.com/redisbloom/redisbloom/pull/892) `HELP` and `COMMAND DOCS` now support probabilistic commands (MOD-8133).\n- 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.\n- RediSearch/RediSearch[#7022](https://github.com/redisearch/redisearch/pull/7022) Support index updates when atomic slot migrations occur.\n- RediSearch/RediSearch[#6313](https://github.com/redisearch/redisearch/pull/6313) Support multiple I/O threads for Redis Search cluster manager - Coordinator (MOD-10562)."},{"id":"bug-fixes-compared-to-823","title":"Bug fixes (compared to 8.2.3)","role":"content","text":"- [#14423](https://github.com/redis/redis/pull/14423) Potential infinite loop when a stream is corrupted.\n- [#14420](https://github.com/redis/redis/pull/14420) Shutdown blocked client not being properly reset after shutdown cancellation.\n- [#14417](https://github.com/redis/redis/pull/14417) `CLUSTER FORGET` - heap-buffer-overflow.\n- [#14415](https://github.com/redis/redis/pull/14415) Potential crash in `lookupKey()` when `executing_client` is NULL.\n- 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)."},{"id":"performance-and-resource-utilization-improvements-compared-to-823","title":"Performance and resource utilization improvements (compared to 8.2.3)","role":"performance","text":"- [#14440](https://github.com/redis/redis/pull/14440) Lookahead prefetching - parse multiple commands in advance through a lookahead pipeline.\n- [#14309](https://github.com/redis/redis/pull/14309) Optimize `BITCOUNT` with AVX2 and AVX512 popcount implementations.\n- [#14227](https://github.com/redis/redis/pull/14227) Optimize `BITCOUNT` with Arm Neon SIMD vectorization.\n- [#14428](https://github.com/redis/redis/pull/14428) Optimize HyperLogLog with branchless comparisons and Arm Neon SIMD vectorization.\n- [#14222](https://github.com/redis/redis/pull/14222) Optimize Vector set `VADD` and `VSIM` with AVX2 and AVX512 dot product implementations.\n- RedisJSON/ijson[#9](https://github.com/redisjson/redisjson/pull/9) JSON - memory footprint improvement by using homogeneous arrays (MOD-9511).\n- RedisJSON/ijson[#7](https://github.com/redisjson/redisjson/pull/7) JSON - memory footprint improvement by inlining short strings (MOD-9511)."},{"id":"configuration-parameters-1","title":"Configuration parameters","role":"configuration","text":"- [#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.\n- [#14296](https://github.com/redis/redis/pull/14296) `decode_array_with_array_mt` - Lua: control how empty JSON arrays are handled.\n- [#14440](https://github.com/redis/redis/pull/14440) `lookahead` - runtime-configurable lookahead depth (default: 16).\n- RediSearch/RediSearch[#7065](https://github.com/redisearch/redisearch/pull/7065) `search-default-scorer` - default text and tag scorer (new default is BM25STD).\n- 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:\n  - `IGNORE` - queries run despite OOM, not recommended for heavy result sets (current behaviour).\n  - `FAIL` - query execution fails if any node is in OOM state at start.\n  - `RETURN` - returns partial results if OOM is detected in only some cluster nodes (default).\n- 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)."},{"id":"known-bugs-and-limitations-1","title":"Known bugs and limitations","role":"content","text":"- 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.\n- `FT.PROFILE`, `FT.EXPLAIN` and `FT.EXPLACINCLI` don’t contain the `FT.HYBRID` option.\n- Metrics from `FT.HYBRID` command aren’t displayed on `FT.INFO` and `INFO`.\n- Option `EXPLAINSCORE`, `SHARD_K_RATIO`, `YIELD_DISTANCE_AS`, and `WITHCURSOR` with `FT.HYBRID` are not available\n- Post-filtering (after `COMBINE` step) using FILTER is not available.\n- Currently, the default response format considers only `key_id` and `score`, this may change for delivering the entire document content."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.6-release-notes","title":"Redis Open Source 8.6 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.6-release-notes/","summary":"Redis Open Source 8.6 release notes.","tags":["docs","operate","stack"],"last_updated":"2026-08-17T13:21:31-07:00","page_type":"content","content_hash":"4100f26455a013e4a2ad67663e8192a424345608b8aeab9b3ef410da3632e2b8","sections":[{"id":"redis-open-source-866-august-2026","title":"Redis Open Source 8.6.6 (August 2026)","role":"overview","text":"Update urgency: `SECURITY`: There are security fixes in the release."},{"id":"security-fixes","title":"Security fixes","role":"security","text":"- (CVE-2026-62356) Miscalculated buffer size in `CMSketch` RDB loading may lead to heap OOB write.\n- Out-of-bounds access in TopK heap cleanup path (MOD-15410).\n- Use-after-free in the TLS pending-data list when a command closes another pending connection.\n- [#15478](https://github.com/redis/redis/pull/15478) ACL key permission bypass in `SORT`, `GEORADIUS`/`GEORADIUSBYMEMBER` and `XREAD`/`XREADGROUP`: the keys validated by ACL could differ from the keys the command actually accesses\n- A malicious RDB payload with an out-of-range `SLOT_INFO slot id` causes memory corruption during RDB loading, which may lead to Remote Code Execution.\n- Vector Sets: missing node level validation when loading a vector set from RDB may lead to out-of-bounds access.\n- Vector Sets: use-after-free when `VREM` mutates the HNSW graph while background `VSIM` threads are still running.\n- Vector Sets: a negative `hnsw_search()` return was treated as a huge unsigned count, reading past the end of the result arrays.\n- TLS client certificate authentication bypass: a Common Name containing an embedded `NUL` byte was truncated, allowing a client to authenticate as another (possibly privileged) ACL user.\n- [#15594](https://github.com/redis/redis/pull/15594) Use-after-free in the blocked-client list when reprocessing a command evicts another client blocked on the same key."},{"id":"redis-open-source-865-july-2026","title":"Redis Open Source 8.6.5 (July 2026)","role":"content","text":"SECURITY: There are security fixes in the release."},{"id":"security-fixes-1","title":"Security fixes","role":"security","text":"- A crafted stream RESTORE payload can make two consumers share the same NACK, leading to a use-after-free that may result in Remote Code Execution.\n- RedisBloom/RedisBloom[#1046](https://github.com/redisbloom/redisbloom/pull/1046) Crafted RESTORE payloads in RedisBloom and TDigest may trigger out-of-bounds writes, potentially leading to remote code execution."},{"id":"bug-fixes","title":"Bug fixes","role":"content","text":"- RedisBloom/RedisBloom[#1021](https://github.com/redisbloom/redisbloom/pull/1021) Replicate `CF.LOADCHUNK` data chunks to prevent silent Cuckoo Filter data loss on failover."},{"id":"redis-open-source-864-june-2026","title":"Redis Open Source 8.6.4 (June 2026)","role":"content","text":"Update urgency: `HIGH`: There are critical bugs that may affect a subset of users."},{"id":"bug-fixes-1","title":"Bug fixes","role":"content","text":"- [#15175](https://github.com/redis/redis/pull/15175), RediSearch/RediSearch[#9262](https://github.com/redisearch/redisearch/pull/9262) Redis fails to start on AArch64.\n- [#15163](https://github.com/redis/redis/pull/15163) `MULTI` queue memory incorrect memory accounting.\n- [#15115](https://github.com/redis/redis/pull/15115) Under-copy in the Lua debugger.\n- [#15094](https://github.com/redis/redis/pull/15094) Cluster crash when `CLIENT KILL` unsubscribes `SSUBSCRIBE` client inside `EXEC`.\n- [#14963](https://github.com/redis/redis/pull/14963) `XREADGROUP`: consumer replication inconsistency.\n- [#14934](https://github.com/redis/redis/pull/14934) Client output buffer memory tracking not accounting for copy-avoided bulk string references.\n- [#14970](https://github.com/redis/redis/pull/14970) Sentinel config injection via `SENTINEL SET`.\n- [#14982](https://github.com/redis/redis/pull/14982) `SCAN` commands: integer overflow in `COUNT` parameter.\n- [#15073](https://github.com/redis/redis/pull/15073) `CLIENT TRACKING`: self-overlap returning non-zero loop index.\n- [#15059](https://github.com/redis/redis/pull/15059) Use-after-free.\n- [#15037](https://github.com/redis/redis/pull/15037) `XINFO STREAM`: wrong value in the per-slot memory tracking.\n- [#15034](https://github.com/redis/redis/pull/15034), [#15081](https://github.com/redis/redis/pull/15081) Issues processing corrupt RDB data.\n- [#15021](https://github.com/redis/redis/pull/15021) `HEXPIRE`: overflow on fields count.\n- [#14942](https://github.com/redis/redis/pull/14942) Fix `COMMAND GETKEYS for PFMERGE` with no source keys.\n- [#15188](https://github.com/redis/redis/pull/15188) `cluster-announce-ip` rejecting hostnames (regression).\n- [#14667](https://github.com/redis/redis/pull/14667), [#14886](https://github.com/redis/redis/pull/14886) Potential TCP stalls/deadlocks.\n- RediSearch/RediSearch[#9484](https://github.com/redisearch/redisearch/pull/9484) Shard crash during background index scan of JSON documents with vector fields on Active-Active (CRDT) databases (MOD-15542).\n- RediSearch/RediSearch[#9635](https://github.com/redisearch/redisearch/pull/9635) Severe latency spikes and shard unresponsiveness when `EXPIRE` or `EXPIREAT` operations run concurrently with queries on large indexes (MOD-14930)."},{"id":"redis-open-source-863-may-2026","title":"Redis Open Source 8.6.3 (May 2026)","role":"content","text":"Update urgency: `SECURITY`: There are security fixes in the release."},{"id":"security-fixes-2","title":"Security fixes","role":"security","text":"- (CVE-2026-23479) Use-After-Free in unblock client flow may lead to Remote Code Execution.\n- (CVE-2026-25243) Invalid memory access in `RESTORE` may lead to Remote Code Execution.\n- (CVE-2026-23631) Lua Use-After-Free may lead to remote code execution.\n- (CVE-2026-25588) Invalid memory access in `RESTORE` may lead to Remote Code Execution (Time Series).\n- (CVE-2026-25589) Invalid memory access in `RESTORE` may lead to Remote Code Execution (Probabilistic)."},{"id":"bug-fixes-2","title":"Bug fixes","role":"content","text":"- `SUBSCRIBE`, `PSUBSCRIBE`, `SSUBSCRIBE`: crash on OOM (RED-167788).\n- `CONFIG SET`: some settings allow invalid characters (RED-167787).\n- `SCRIPT DEBUG`: potential crash on scripts (RED-175507).\n- `VADD`: crash or buffer overflow on large `REDUCE` value (RED-170921).\n- `VSET`: crash on huge allocations (MOD-12678).\n- Potential crash on disconnections and TLS failures (Time Series) (MOD-14850).\n- RediSearch/RediSearch[#8745](https://github.com/redisearch/redisearch/pull/8745) Crash when many keys receive expirations under heavy TTL activity (MOD-14500).\n- RediSearch/RediSearch[#8848](https://github.com/redisearch/redisearch/pull/8848) HNSW vector index memory growth under high-churn workloads until shard restart (MOD-13761).\n- RediSearch/RediSearch[#8205](https://github.com/redisearch/redisearch/pull/8205), RediSearch/RediSearch[#8259](https://github.com/redisearch/redisearch/pull/8259) `FT.HYBRID` `VSIM RANGE` + `FILTER` incorrectly returns zero results (MOD-12370, MOD-13884).\n- RediSearch/RediSearch[#9182](https://github.com/redisearch/redisearch/pull/9182) `FT.PROFILE HYBRID` returns an empty reply (MOD-14778).\n- RediSearch/RediSearch[#8129](https://github.com/redisearch/redisearch/pull/8129), RediSearch/RediSearch[#8140](https://github.com/redisearch/redisearch/pull/8140) `FT.PROFILE` reports an incorrect shard total profile time (MOD-13735, MOD-13181).\n- RediSearch/RediSearch[#9047](https://github.com/redisearch/redisearch/pull/9047) `FT.PROFILE` output is inconsistent when a profiled value is missing (MOD-10560).\n- RediSearch/RediSearch[#8791](https://github.com/redisearch/redisearch/pull/8791) `FT.EXPLAIN` does not lock, causing a race with concurrent index changes (MOD-14461).\n- RediSearch/RediSearch[#8382](https://github.com/redisearch/redisearch/pull/8382) Crash when indexing negative zero (-0.0) (MOD-13904).\n- RediSearch/RediSearch[#8590](https://github.com/redisearch/redisearch/pull/8590) `FILTER` returns inconsistent results with multiple indexes sharing field aliases (MOD-14063).\n- RediSearch/RediSearch[#8660](https://github.com/redisearch/redisearch/pull/8660) `FILTER` behavior depends on property order in the expression (MOD-14065).\n- RediSearch/RediSearch[#8593](https://github.com/redisearch/redisearch/pull/8593) Filter expressions are evaluated for indexes that do not match the document type (MOD-14064).\n- RediSearch/RediSearch[#8591](https://github.com/redisearch/redisearch/pull/8591) Documents are inconsistently included or excluded depending on the indexing path taken (MOD-13948).\n- RediSearch/RediSearch[#8589](https://github.com/redisearch/redisearch/pull/8589) `RENAME` notification handler loads the wrong key, causing stale index entries after a rename (MOD-14328).\n- RediSearch/RediSearch[#9012](https://github.com/redisearch/redisearch/pull/9012) `PERSIST` and `HPERSIST` notifications are not reflected in index expiration tracking (MOD-14800).\n- RediSearch/RediSearch[#9079](https://github.com/redisearch/redisearch/pull/9079) `FT.SPELLCHECK` treats `PARAMS` placeholders as literal terms instead of resolving them (MOD-10596).\n- RediSearch/RediSearch[#8462](https://github.com/redisearch/redisearch/pull/8462) GC out-of-memory on replica shards leaves the replica in an inconsistent state (MOD-14066).\n- RediSearch/RediSearch[#9066](https://github.com/redisearch/redisearch/pull/9066) Race condition in `FT.HYBRID` causes intermittent failures under concurrent hybrid query load (MOD-14732).\n- RediSearch/RediSearch[#8109](https://github.com/redisearch/redisearch/pull/8109), RediSearch/RediSearch[#8149](https://github.com/redisearch/redisearch/pull/8149) Configuration registration omits module parameters, causing them to be unexposed or misapplied (RED-171841).\n- RediSearch/RediSearch[#9163](https://github.com/redisearch/redisearch/pull/9163) Crash on `FT.SEARCH` when topology validation fails (for example, some nodes unreachable) (MOD-14475).\n- RediSearch/RediSearch[#8395](https://github.com/redisearch/redisearch/pull/8395) `FT.SEARCH` fails with \"Query requires unavailable slots\" after shard restart or failover (MOD-13828).\n- RediSearch/RediSearch[#8451](https://github.com/redisearch/redisearch/pull/8451) `FT.INFO`-style output no longer reports zero-index summary data when no indices exist (MOD-14079).\n- RediSearch/RediSearch[#9078](https://github.com/redisearch/redisearch/pull/9078) `FT.CREATE` now rejects schema definitions with invalid option combinations at creation time (MOD-14655).\n- RediSearch/RediSearch[#8051](https://github.com/redisearch/redisearch/pull/8051), RediSearch/RediSearch[#8114](https://github.com/redisearch/redisearch/pull/8114) Crash diagnostics now include the `IndexSpec` of the index the failing thread was working on (MOD-7574)."},{"id":"metrics","title":"Metrics","role":"content","text":"- RediSearch/RediSearch[#8210](https://github.com/redisearch/redisearch/pull/8210), RediSearch/RediSearch[#8231](https://github.com/redisearch/redisearch/pull/8231) `FT.PROFILE`: added queue time tracking (MOD-13602)."},{"id":"redis-open-source-862-march-2026","title":"Redis Open Source 8.6.2 (March 2026)","role":"content","text":"Update urgency: `SECURITY`: There are security fixes in the release."},{"id":"security-fixes-3","title":"Security fixes","role":"security","text":"- [#14824](https://github.com/redis/redis/pull/14824) Potential UAF: don't use reply copy avoidance for module strings."},{"id":"bug-fixes-3","title":"Bug fixes","role":"content","text":"- [#14848](https://github.com/redis/redis/pull/14848) Crash during command processing on replicas performing full synchronization.\n- [#14794](https://github.com/redis/redis/pull/14794) New `XIDMPRECORD` internal command and AOFRW emission to restore stream IDMP state.\n- [#14816](https://github.com/redis/redis/pull/14816) `setModuleEnumConfig()` passing prefixed name to module callbacks.\n- [#14858](https://github.com/redis/redis/pull/14858) Streams: Ensures `XADD` with `IDMP`/`IDMPAUTO` that hits an existing IID records the metadata change.\n- [#14855](https://github.com/redis/redis/pull/14855), [#14831](https://github.com/redis/redis/pull/14831), [#14817](https://github.com/redis/redis/pull/14817) Potential Memory leaks.\n- [#14869](https://github.com/redis/redis/pull/14869) Streams: IDMP cron expiration not working after RDB load.\n- [#14847](https://github.com/redis/redis/pull/14847) Potential crash during ACL checks on wrong-arity commands.\n- [#14883](https://github.com/redis/redis/pull/14883) `HSETEX` and `HGETEX` do not validate that `FIELDS` is specified only once.\n- [#14897](https://github.com/redis/redis/pull/14897) Streams: IDMP-related bugs."},{"id":"redis-open-source-861-february-2026","title":"Redis Open Source 8.6.1 (February 2026)","role":"content","text":"Update urgency: `SECURITY`: There are security fixes in the release."},{"id":"security-fixes-4","title":"Security fixes","role":"security","text":"- A user can manipulate data read by a connection by injecting `\\r\\n` sequences into a Redis error reply."},{"id":"bug-fixes-4","title":"Bug fixes","role":"content","text":"- [#14785](https://github.com/redis/redis/pull/14785) `HOTKEYS`: The `INFO` command may display module information, and the missing `HOTKEYS HELP` subcommand has been added.\n- [#14789](https://github.com/redis/redis/pull/14789) Bug in RDB loading prevented hash table expansion, increasing load time."},{"id":"redis-open-source-860-february-2026","title":"Redis Open Source 8.6.0 (February 2026)","role":"content","text":"This is the General Availability release of Redis 8.6 in Redis Open Source."},{"id":"major-changes-compared-to-84","title":"Major changes compared to 8.4","role":"content","text":"- Substantial performance improvements.\n- Substantial memory reduction for hashes (hashtable-encoded) and sorted sets (skiplist-encoded).\n- Streams: `XADD` idempotency (at-most-once guarantee) with new `IDMPAUTO` and `IDMP` arguments.\n- New eviction policies - least recently modified: `volatile-lrm` and `allkeys-lrm`.\n- Hot keys detection and reporting; new command: `HOTKEYS`.\n- TLS certificate-based automatic client authentication.\n- Time series: support NaN values; new aggregators: `COUNTNAN` and `COUNTALL`."},{"id":"binary-distributions","title":"Binary distributions","role":"content","text":"- Alpine and Debian Docker images - https://hub.docker.com/_/redis\n- Install using snap - see https://github.com/redis/redis-snap\n- Install using brew - see https://github.com/redis/homebrew-redis\n- Install using RPM - see https://github.com/redis/redis-rpm\n- Install using Debian APT - see https://github.com/redis/redis-debian"},{"id":"operating-systems-we-test-redis-86-on","title":"Operating systems we test Redis 8.6 on","role":"content","text":"- Ubuntu 22.04 (Jammy Jellyfish), 24.04 (Noble Numbat)\n- Rocky Linux 8.10, 9.5\n- AlmaLinux 8.10, 9.5, 10.1\n- Debian 12 (Bookworm), Debian 13 (Trixie)\n- macOS 14 (Sonoma), 15 (Sequoia)"},{"id":"new-features-compared-to-86-rc1","title":"New Features (compared to 8.6-RC1)","role":"content","text":"- [#14695](https://github.com/redis/redis/pull/14695) Keys memory size histograms."},{"id":"performance-and-resource-utilization-improvements-compared-to-86-rc1","title":"Performance and resource utilization improvements (compared to 8.6-RC1)","role":"performance","text":"- [#14714](https://github.com/redis/redis/pull/14714) Optimize user ACL permission verification.\n- [#14692](https://github.com/redis/redis/pull/14692) Optimize peak memory metric collection.\n- [#14739](https://github.com/redis/redis/pull/14739) Avoid allocating and releasing list node in reply copy avoidance.\n- [#14713](https://github.com/redis/redis/pull/14713) Reduce per command syscalls by reusing cached time when hardware monotonic clock is available.\n- [#14726](https://github.com/redis/redis/pull/14726) Optimize `XREADGROUP CLAIM`.\n- [#13962](https://github.com/redis/redis/pull/13962) Vector set: replace manual popcount with __builtin_popcountll for binary vector distance (Intel, AMD, ARM).\n- [#14474](https://github.com/redis/redis/pull/14474) Vector set: vectorized the quantized 8-bit vector distance calculation (Intel, AMD).\n- [#14492](https://github.com/redis/redis/pull/14492) Vector set: vectorize binary quantization path for vectorsets distance calculation (Intel, AMD)."},{"id":"configuration-parameters","title":"Configuration parameters","role":"configuration","text":"- [#14719](https://github.com/redis/redis/pull/14719) `cluster-slot-stats-enabled` - per-slot resource consumptions statistics to collect.\n- [#14695](https://github.com/redis/redis/pull/14695) `key-memory-histograms` collect memory consumption histograms per data type."},{"id":"metrics-1","title":"Metrics","role":"content","text":"- [#14695](https://github.com/redis/redis/pull/14695) `db0_distrib_lists_sizes`, `db0_distrib_sets_sizes`, `db0_distrib_hashes_sizes`, `db0_distrib_zsets_sizes`."},{"id":"known-bugs-and-limitations","title":"Known bugs and limitations","role":"content","text":"- Streams: avoid using `XADD` with the new `IDMP` or `IDMPAUTO` options when using `appendonly yes` with `aof-use-rdb-preamble no` (non default).\n  This limitation will be removed in the next patch."},{"id":"redis-open-source-86-rc1-january-2026","title":"Redis Open Source 8.6-RC1 (January 2026)","role":"content","text":"This is the first Release Candidate of Redis 8.6 in Redis Open Source.\n\nRelease Candidates are feature-complete pre-releases. Pre-releases are not suitable for production use."},{"id":"binary-distributions-1","title":"Binary distributions","role":"content","text":"- Alpine and Debian Docker images - https://hub.docker.com/_/redis\n- Install using snap - see https://github.com/redis/redis-snap\n- Install using brew - see https://github.com/redis/homebrew-redis\n- Install using RPM - see https://github.com/redis/redis-rpm\n- Install using Debian APT - see https://github.com/redis/redis-debian"},{"id":"operating-systems-we-test-redis-86-on-1","title":"Operating systems we test Redis 8.6 on","role":"content","text":"- Ubuntu 22.04 (Jammy Jellyfish), 24.04 (Noble Numbat)\n- Rocky Linux 8.10, 9.5, 10.1\n- AlmaLinux 8.10, 9.5, 10.1\n- Debian 12 (Bookworm), Debian 13 (Trixie)\n- 14 (Sonoma), 15 (Sequoia)"},{"id":"new-features-compared-to-840","title":"New Features (compared to 8.4.0)","role":"content","text":"- [#14615](https://github.com/redis/redis/pull/14615) Streams: `XADD` idempotency (at-most-once guarantee) with new `IDMPAUTO` and `IDMP` arguments.\n- [#14624](https://github.com/redis/redis/pull/14624) New eviction policies - least recently modified: `volatile-lrm` and `allkeys-lrm`.\n- [#14680](https://github.com/redis/redis/pull/14680) Hot keys detection and reporting; new command: `HOTKEYS`.\n- [#14610](https://github.com/redis/redis/pull/14610) TLS certificate-based automatic client authentication.\n- RedisTimeSeries/RedisTimeSeries[#1853](https://github.com/redistimeseries/redistimeseries/pull/1853) Time series: support NaN values; new aggregators: `COUNTNAN` and `COUNTALL`."},{"id":"security-and-privacy-fixes","title":"Security and privacy fixes","role":"security","text":"- [#14645](https://github.com/redis/redis/pull/14645) Hide personally identifiable information from ACL log.\n- [#14659](https://github.com/redis/redis/pull/14659) ACL: Key-pattern bypass in `MSETEX`.\n- RedisTimeSeries/RedisTimeSeries[#1837](https://github.com/redistimeseries/redistimeseries/pull/1837), RedisJSON/RedisJSON[#1474](https://github.com/redisjson/redisjson/pull/1474) Hide personally identifiable information from server log.\n- RedisBloom/RedisBloom[#950](https://github.com/redisbloom/redisbloom/pull/950) Out-of-bounds read when loading an invalid RDB file (MOD-12802)."},{"id":"bug-fixes-compared-to-840","title":"Bug fixes (compared to 8.4.0)","role":"content","text":"- [#14545](https://github.com/redis/redis/pull/14545) ACL: AOF loading fails if ACL rules are changed and don't allow some commands in `MULTI`-`EXEC`.\n- [#14637](https://github.com/redis/redis/pull/14637) Atomic slot migration: wrong adjacent slot range behavior.\n- [#14567](https://github.com/redis/redis/pull/14567) Atomic slot migration: support delay trimming slots after finishing migrating slots.\n- [#14623](https://github.com/redis/redis/pull/14623) Streams: `XTRIM`/`XADD` with approx mode (`~`) don’t delete entries for `DELREF`/`ACKED` strategies.\n- [#14552](https://github.com/redis/redis/pull/14552) Streams: Incorrect behavior when using `XDELEX...ACKED` after `XGROUP DESTROY`.\n- [#14537](https://github.com/redis/redis/pull/14537) `SCAN`: restore original filter order (revert change introduced in 8.2).\n- [#14581](https://github.com/redis/redis/pull/14581) Rare server hang at shutdown.\n- [#14597](https://github.com/redis/redis/pull/14597) Panic when cluster node is uninitialized.\n- [#14583](https://github.com/redis/redis/pull/14583) `FLUSHALL ASYNC` on a writable replica may block the main thread for an extended period.\n- [#14504](https://github.com/redis/redis/pull/14504) Cluster: fix race condition in broadcast configuration.\n- [#14416](https://github.com/redis/redis/pull/14416) Fixed argument position handling in Redis APIs.\n- RedisTimeSeries/RedisTimeSeries[#1784](https://github.com/redistimeseries/redistimeseries/pull/1784), RedisTimeSeries/RedisTimeSeries[#1839](https://github.com/redistimeseries/redistimeseries/pull/1839), RedisBloom/RedisBloom[#952](https://github.com/redisbloom/redisbloom/pull/952), RedisJSON/RedisJSON[#1477](https://github.com/redisjson/redisjson/pull/1477) Atomic slot migration support.\n- RedisBloom/RedisBloom[#946](https://github.com/redisbloom/redisbloom/pull/946) `MEMORY USAGE`: fix reported value (MOD-12799).\n- RedisJSON/RedisJSON[#1473](https://github.com/redisjson/redisjson/pull/1473) Adding escapes to already-escaped characters (MOD-8137).\n- RedisJSON/RedisJSON[#1475](https://github.com/redisjson/redisjson/pull/1475) `JSON.CLEAR` does not error if more than one path is specified (MOD-13109)."},{"id":"performance-and-resource-utilization-improvements-compared-to-840","title":"Performance and resource utilization improvements (compared to 8.4.0)","role":"performance","text":"- [#14608](https://github.com/redis/redis/pull/14608) Reply copy-avoidance path to reduce memory copies for bulk string replies.\n- [#14595](https://github.com/redis/redis/pull/14595) Hash: unify field name and value into a single struct.\n- [#14701](https://github.com/redis/redis/pull/14701) Sorted set: unify score and value into a single struct.\n- [#14662](https://github.com/redis/redis/pull/14662) Optimize listpack iterator on hash fields.\n- [#14699](https://github.com/redis/redis/pull/14699) Optimize set commands with expiration.\n- [#14700](https://github.com/redis/redis/pull/14700) Optimize prefetching.\n- [#14715](https://github.com/redis/redis/pull/14715) Optimize prefetch sizing logic.\n- [#14636](https://github.com/redis/redis/pull/14636) Optimize `ZRANK`.\n- [#14676](https://github.com/redis/redis/pull/14676) Utilize hardware clock by default on ARM AArch64.\n- [#14575](https://github.com/redis/redis/pull/14575) Disable RDB compression when diskless replication is used."},{"id":"modules-api","title":"Modules API","role":"content","text":"- [#14445](https://github.com/redis/redis/pull/14445)\n  - `RM_CreateKeyMetaClass` - define a new key-metadata class.\n  - `RM_ReleaseKeyMetaClass` - release a key-metadata class.\n  - `RM_SetKeyMeta` - attach or update a metadata value for a key under a specific metadata-key class.\n  - `RM_GetKeyMeta` - get a metadata value for a key under a specific metadata-key class."},{"id":"configuration-parameters-1","title":"Configuration parameters","role":"configuration","text":"- [#14624](https://github.com/redis/redis/pull/14624) `maxmemory-policy`: new eviction policies: `volatile-lrm`, `allkeys-lrm`.\n- [#14615](https://github.com/redis/redis/pull/14615) `stream-idmp-duration`, `stream-idmp-maxsize` - defaults for streams idempotent production.\n- [#14610](https://github.com/redis/redis/pull/14610) `tls-auth-clients-user` TLS certificate-based automatic client authentication.\n- [#14596](https://github.com/redis/redis/pull/14596) `flushdb` option for `repl-diskless-load`: always flush the entire dataset before diskless load."},{"id":"metrics-2","title":"Metrics","role":"content","text":"- [#14610](https://github.com/redis/redis/pull/14610) `acl_access_denied_tls_cert` - failed TLS certificate–based authentication attempts."},{"id":"known-bugs-and-limitations-1","title":"Known bugs and limitations","role":"content","text":"- Redis Search: In case of load rebalancing operations (such as Atomic Slot Migration) taking place during the lifetime of a cursor, there is a chance that some results may be missing."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.8-release-notes","title":"Redis Open Source 8.8 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.8-release-notes/","summary":"Redis Open Source 8.8 release notes.","tags":["docs","operate","stack"],"last_updated":"2026-08-17T13:21:31-07:00","page_type":"content","content_hash":"7d9a45663674fd9b2122e1bcf0fbd7fd44d4e32094d0f7ba653d5cb798eaa80b","sections":[{"id":"redis-open-source-882-august-2026","title":"Redis Open Source 8.8.2 (August 2026)","role":"overview","text":"Update urgency: `SECURITY`: There are security fixes in the release."},{"id":"security-fixes","title":"Security fixes","role":"security","text":"- (CVE-2026-62356) Miscalculated buffer size in `CMSketch` RDB loading may lead to heap OOB write.\n- Out-of-bounds access in TopK heap cleanup path (MOD-15410).\n- Use-after-free in the TLS pending-data list when a command closes another pending connection.\n- [#15478](https://github.com/redis/redis/pull/15478) ACL key permission bypass in `SORT`, `GEORADIUS`/`GEORADIUSBYMEMBER` and `XREAD`/`XREADGROUP`: the keys validated by ACL could differ from the keys the command actually accesses\n- A malicious RDB payload with an out-of-range `SLOT_INFO slot id` causes memory corruption during RDB loading, which may lead to Remote Code Execution.\n- Vector Sets: missing node level validation when loading a vector set from RDB may lead to out-of-bounds access.\n- Vector Sets: use-after-free when `VREM` mutates the HNSW graph while background `VSIM` threads are still running.\n- Vector Sets: a negative `hnsw_search()` return was treated as a huge unsigned count, reading past the end of the result arrays.\n- TLS client certificate authentication bypass: a Common Name containing an embedded `NUL` byte was truncated, allowing a client to authenticate as another (possibly privileged) ACL user.\n- [#15594](https://github.com/redis/redis/pull/15594) Use-after-free in the blocked-client list when reprocessing a command evicts another client blocked on the same key."},{"id":"redis-open-source-881-july-2026","title":"Redis Open Source 8.8.1 (July 2026)","role":"content","text":"SECURITY: There is a security fix in the release."},{"id":"security-fixes-1","title":"Security fixes","role":"security","text":"- RedisBloom/RedisBloom[#1044](https://github.com/redisbloom/redisbloom/pull/1044) Crafted RESTORE payloads in RedisBloom and TDigest may trigger out-of-bounds writes, potentially leading to remote code execution."},{"id":"redis-open-source-880-may-2026","title":"Redis Open Source 8.8.0 (May 2026)","role":"content","text":"This is the General Availability release of Redis 8.8 in Redis Open Source."},{"id":"major-changes-compared-to-86","title":"Major changes compared to 8.6","role":"content","text":"- New data structure: Array (@antirez)\n- Subkey notification for hash fields - field-level notifications\n- `INCREX`: a window counter rate limiter combining `INCR`, `INCRBY`, `INCRBYFLOAT`, bounds, and expiration (@raffertyyu + Redis team)\n- `XNACK`: a new streams command - allow consumers to explicitly release pending messages\n- `ZUNION`, `ZINTER`, `ZUNIONSTORE`, `ZINTERSTORE`: new `COUNT` aggregator\n- `JSON.SET`: new `FPHA` argument to specify the FP type for homogeneous FP arrays\n- `TS.RANGE`, `TS.REVRANGE`, `TS.MRANGE`, `TS.MREVRANGE`: multiple aggregators in a single command\n- `FT.HYBRID` `KNN` clause: new argument to request fewer candidates per shard\n- `FT.PROFILE` `HYBRID`: profiling support for `FT.HYBRID`\n- Performance improvements"},{"id":"binary-distributions","title":"Binary distributions","role":"content","text":"- Alpine and Debian Docker images - https://hub.docker.com/_/redis\n- Install using snap - see https://github.com/redis/redis-snap\n- Install using brew - see https://github.com/redis/homebrew-redis\n- Install using RPM - see https://github.com/redis/redis-rpm\n- Install using Debian APT - see https://github.com/redis/redis-debian"},{"id":"operating-systems-we-test-redis-88-on","title":"Operating systems we test Redis 8.8 on","role":"content","text":"- Ubuntu 22.04 (Jammy Jellyfish), 24.04 (Noble Numbat), 26.04 (Resolute Raccoon)\n- Rocky Linux 8.10, 9.7, 10.1\n- AlmaLinux 8.10, 9.7, 10.1\n- Debian 12.13 (Bookworm), Debian 13.4 (Trixie)\n- Alpine 3.23\n- macOS 14.8.4 (Sonoma), 15.7.4 (Sequoia), 26.3 (Tahoe) - for both Intel and ARM"},{"id":"bug-fixes-compared-to-88-rc1","title":"Bug fixes (compared to 8.8-RC1)","role":"content","text":"- [#15237](https://github.com/redis/redis/pull/15237) `INCREX` syntax update.\n- [#15005](https://github.com/redis/redis/pull/15005) Memory tracking can be enabled at runtime in non-clustered mode .\n- RedisTimeSeries/RedisTimeSeries[#1930](https://github.com/redistimeseries/redistimeseries/pull/1930) Cluster topology changes during a multi-shard command are not handled (MOD-14439).\n- RedisBloom/RedisBloom[#1007](https://github.com/redisbloom/redisbloom/pull/1007) Memory leak on RDB load (MOD-15418)."},{"id":"redis-open-source-88-rc1-may-2026","title":"Redis Open Source 8.8-RC1 (May 2026)","role":"content","text":"This is the first Release Candidate of Redis 8.8 in Redis Open Source.\n\nRelease Candidates are feature-complete pre-releases. Pre-releases are not suitable for production use."},{"id":"headlines","title":"Headlines:","role":"content","text":"Redis 8.8 introduces new features and performance improvements."},{"id":"operating-systems-we-test-redis-88-on-1","title":"Operating systems we test Redis 8.8 on","role":"content","text":"- Ubuntu 22.04 (Jammy Jellyfish), 24.04 (Noble Numbat), 26.04 (Resolute Raccoon)\n- Rocky Linux 8.10, 9.7, 10.1\n- AlmaLinux 8.10, 9.7, 10.1\n- Debian 12.13 (Bookworm), Debian 13.4 (Trixie)\n- Alpine 3.23\n- macOS 14.8.4 (Sonoma), 15.7.4 (Sequoia), 26.3 (Tahoe) - for both Intel and ARM"},{"id":"security-fixes-compared-to-88-m03","title":"Security fixes (compared to 8.8-M03)","role":"security","text":"- (CVE-2026-23479) Use-After-Free in unblock client flow may lead to Remote Code Execution.\n- (CVE-2026-25243) Invalid memory access in `RESTORE` may lead to Remote Code Execution.\n- (CVE-2026-23631) Lua Use-After-Free may lead to remote code execution.\n- (CVE-2026-25588) Invalid memory access in `RESTORE` may lead to Remote Code Execution (Time Series).\n- (CVE-2026-25589) Invalid memory access in `RESTORE` may lead to Remote Code Execution (Probabilistic)."},{"id":"new-features-compared-to-88-m03","title":"New Features (compared to 8.8-M03)","role":"content","text":"- [#15162](https://github.com/redis/redis/pull/15162) New data structure: Array (@antirez)\n- [#15045](https://github.com/redis/redis/pull/15045) `INCREX`: a window counter rate limiter combining `INCR`,`INCRBY`,`INCRBYFLOAT`, bounds, and expiration (@raffertyyu + Redis team)\n- In group sorting new reducer, allowing unwind grouped documents (after `GROUPBY`) and sort them"},{"id":"removed-features-compared-to-88-m03","title":"Removed Features (compared to 8.8-M03)","role":"content","text":"- [#15191](https://github.com/redis/redis/pull/15191) Remove GCRA rate limiter"},{"id":"bug-fixes-compared-to-88-m03","title":"Bug fixes (compared to 8.8-M03)","role":"content","text":"- `SUBSCRIBE`, `PSUBSCRIBE`, `SSUBSCRIBE`: crash on OOM (RED-167788)\n- `CONFIG SET`: some settings allow invalid characters (RED-167787)\n- `SCRIPT DEBUG`: potential crash on scripts (RED-175507)\n- `VADD`: crash or buffer overflow on large `REDUCE` value (RED-170921)\n- `VSET`: crash on huge allocations (MOD-12678)\n- [#15188](https://github.com/redis/redis/pull/15188) `cluster-announce-ip` rejecting hostnames (regression)\n- [#15095](https://github.com/redis/redis/pull/15095) Double free when loading streams with duplicate consumer PEL entries\n- [#15124](https://github.com/redis/redis/pull/15124) Issues processing corrupt Streams RDB data\n- [#15111](https://github.com/redis/redis/pull/15111) `fast_float_strtod` rounding mismatch\n- [#15190](https://github.com/redis/redis/pull/15190) `vecClear` reset the logical size without releasing element ownership\n- [#15163](https://github.com/redis/redis/pull/15163) `MULTI` queue memory incorrect memory accounting \n- [#15094](https://github.com/redis/redis/pull/15094) Cluster crash when `CLIENT KILL` unsubscribes `SSUBSCRIBE` client inside `EXEC`\n- [#15151](https://github.com/redis/redis/pull/15151) Listpack backlength encoding thresholds off-by-one\n- [#15115](https://github.com/redis/redis/pull/15115) Under-copy in the Lua debugger\n- [#14970](https://github.com/redis/redis/pull/14970) Sentinel config injection via `SENTINEL SET`\n- [#14934](https://github.com/redis/redis/pull/14934) Client output buffer memory tracking not accounting for copy-avoided bulk string references\n- RediSearch/RediSearch[#9182](https://github.com/redisearch/redisearch/pull/9182) `FT.PROFILE HYBRID` returns an empty reply (MOD-14778)\n- RediSearch/RediSearch[#9079](https://github.com/redisearch/redisearch/pull/9079) `FT.SPELLCHECK` treats `PARAMS` placeholders as literal terms instead of resolving them (MOD-10596)\n- RediSearch/RediSearch[#9047](https://github.com/redisearch/redisearch/pull/9047) `FT.PROFILE` output is inconsistent when a profiled value is missing (MOD-10560)\n- RediSearch/RediSearch[#9078](https://github.com/redisearch/redisearch/pull/9078) `FT.CREATE` now rejects schema definitions with invalid option combinations at creation time (MOD-14655)\n- RediSearch/RediSearch[#9012](https://github.com/redisearch/redisearch/pull/9012) `PERSIST` and `HPERSIST` notifications are not reflected in index expiration tracking (MOD-14800)\n- RediSearch/RediSearch[#9066](https://github.com/redisearch/redisearch/pull/9066) Race condition in `FT.HYBRID` causes intermittent failures under concurrent hybrid query load (MOD-14732)\n- RediSearch/RediSearch[#9163](https://github.com/redisearch/redisearch/pull/9163) Crash on `FT.SEARCH` when topology validation fails (for example, some nodes unreachable) (MOD-14475)\n- RediSearch/RediSearch[#9031](https://github.com/redisearch/redisearch/pull/9031), RediSearch/RediSearch[#9473](https://github.com/redisearch/redisearch/pull/9473) Coordinator deadlock under mixed `FT.SEARCH` and `FT.AGGREGATE` load (MOD-14268)\n- RediSearch/RediSearch[#9028](https://github.com/redisearch/redisearch/pull/9028) Memory leak when `FT.DROPINDEX` runs concurrently with in-flight hybrid queries (MOD-14135)\n- RediSearch/RediSearch[#9310](https://github.com/redisearch/redisearch/pull/9310), RediSearch/RediSearch[#9350](https://github.com/redisearch/redisearch/pull/9350) `FT.CURSOR READ` timeout and `ON_TIMEOUT FAIL` not enforced on coordinator and shard (MOD-14284, MOD-14998)\n- RediSearch/RediSearch[#9425](https://github.com/redisearch/redisearch/pull/9425) Cursors not cleaned up after `MAXIDLE`, causing resource exhaustion (MOD-6430)\n- RediSearch/RediSearch[#9234](https://github.com/redisearch/redisearch/pull/9234), RediSearch/RediSearch[#9404](https://github.com/redisearch/redisearch/pull/9404) Coordinator `RETURN_STRICT` returns wrong data on partial results, including `SORTBY` pipeline (MOD-13617)\n- RediSearch/RediSearch[#9382](https://github.com/redisearch/redisearch/pull/9382) `MAXPREFIXEXPANSION` warnings not propagated to clients in cluster mode (MOD-13804)\n- RediSearch/RediSearch[#9218](https://github.com/redisearch/redisearch/pull/9218) Search commands fail when no worker thread is available instead of falling back to main thread (MOD-14921)\n- RediSearch/RediSearch[#9448](https://github.com/redisearch/redisearch/pull/9448) RDB load missing validation of `FT.CREATE` arguments, allowing corrupt index state on load (MOD-13118)\n- RediSearch/RediSearch[#9377](https://github.com/redisearch/redisearch/pull/9377) Use-after-move in `Indexer_Process` causes crash during indexing (MOD-14980)\n- RediSearch/RediSearch[#9408](https://github.com/redisearch/redisearch/pull/9408) Deadlock between background query and main-thread writer (MOD-15364)\n- RediSearch/RediSearch[#9114](https://github.com/redisearch/redisearch/pull/9114) `FT.PROFILE` prints output using wrong iterator type (MOD-14678)\n- RediSearch/RediSearch[#9421](https://github.com/redisearch/redisearch/pull/9421) Confusing error returned when `DEBUG_PARAMS_COUNT` is zero (MOD-15118)\n- RediSearch/RediSearch[#9045](https://github.com/redisearch/redisearch/pull/9045) Stack-smashing error in coordinator code path (MOD-14649)\n- RedisJSON/RedisJSON[#1554](https://github.com/redisjson/redisjson/pull/1554) Trailing chars are ignored (MOD-7266); Fixes RedisJSON/RedisJSON[#976](https://github.com/redisjson/redisjson/pull/976)\n- RedisJSON/RedisJSON[#1543](https://github.com/redisjson/redisjson/pull/1543) Wrong mutation ordering for array commands with recursive paths (MOD-6722)\n- RedisJSON/RedisJSON[#1542](https://github.com/redisjson/redisjson/pull/1542) JSONPath evaluation issues (MOD-14664); Fixes RedisJSON/RedisJSON[#968](https://github.com/redisjson/redisjson/pull/968) (MOD-7264), RedisJSON/RedisJSON[#962](https://github.com/redisjson/redisjson/pull/962) (MOD-7272), RedisJSON/RedisJSON[#963](https://github.com/redisjson/redisjson/pull/963) (MOD-7270), RedisJSON/RedisJSON[#1089](https://github.com/redisjson/redisjson/pull/1089) (MOD-7268)\n- RedisTimeSeries/RedisTimeSeries[#2003](https://github.com/redistimeseries/redistimeseries/pull/2003) Potential crash on disconnections and TLS failures (MOD-14850)\n- RedisTimeSeries/RedisTimeSeries[#2013](https://github.com/redistimeseries/redistimeseries/pull/2013) `count`, `countNaN`, `countAll` reducers return NaN when all values are NaN (MOD-14420)"},{"id":"performance-and-resource-utilization-improvements-compared-to-88-m03","title":"Performance and resource utilization improvements (compared to 8.8-M03)","role":"performance","text":"- [#15049](https://github.com/redis/redis/pull/15049) Hyperloglog: 4 independent accumulators that are merged at the end\n- [#15133](https://github.com/redis/redis/pull/15133) Batched prefetch for `MGET` and `MSET`\n- [#14988](https://github.com/redis/redis/pull/14988) Batched prefetch for `HGETALL` on hashtable-encoded hashes\n- [#15071](https://github.com/redis/redis/pull/15071) Pass size hint to jemalloc for faster deallocation\n- [#15096](https://github.com/redis/redis/pull/15096) Reduces allocator and accounting overhead by adding compile-time jemalloc tuning\n- RediSearch/RediSearch[#9197](https://github.com/redisearch/redisearch/pull/9197) Vector index hot path (HNSW and brute-force) devirtualized, reducing per-query latency (MOD-14916)\n- RediSearch/RediSearch[#9262](https://github.com/redisearch/redisearch/pull/9262), RediSearch/RediSearch[#9476](https://github.com/redisearch/redisearch/pull/9476) Inline LSE atomics enabled on AArch64, improving atomic operation throughput on ARM64 (MOD-14916, MOD-15419)\n- RediSearch/RediSearch[#9293](https://github.com/redisearch/redisearch/pull/9293) Expiration handling overhead reduced when many keys expire simultaneously (MOD-14916)\n- RediSearch/RediSearch[#9017](https://github.com/redisearch/redisearch/pull/9017) LTO (link-time optimization) enabled for x86_64 release builds (MOD-14700)\n- RediSearch/RediSearch[#8765](https://github.com/redisearch/redisearch/pull/8765) Shard-level timeout adjusted to coordinator dispatch time for more accurate accounting (MOD-13189)\n- RediSearch/RediSearch[#8790](https://github.com/redisearch/redisearch/pull/8790), RediSearch/RediSearch[#8900](https://github.com/redisearch/redisearch/pull/8900), RediSearch/RediSearch[#8827](https://github.com/redisearch/redisearch/pull/8827), RediSearch/RediSearch[#8971](https://github.com/redisearch/redisearch/pull/8971), RediSearch/RediSearch[#8966](https://github.com/redisearch/redisearch/pull/8966), RediSearch/RediSearch[#8762](https://github.com/redisearch/redisearch/pull/8762), RediSearch/RediSearch[#8678](https://github.com/redisearch/redisearch/pull/8678), RediSearch/RediSearch[#8915](https://github.com/redisearch/redisearch/pull/8915), RediSearch/RediSearch[#8653](https://github.com/redisearch/redisearch/pull/8653), RediSearch/RediSearch[#9085](https://github.com/redisearch/redisearch/pull/9085), RediSearch/RediSearch[#8751](https://github.com/redisearch/redisearch/pull/8751), RediSearch/RediSearch[#8692](https://github.com/redisearch/redisearch/pull/8692), RediSearch/RediSearch[#9224](https://github.com/redisearch/redisearch/pull/9224) Iterators ported to Rust, reducing FFI overhead\n- RediSearch/RediSearch[#9500](https://github.com/redisearch/redisearch/pull/9500) `numRecords` no longer updated for vector fields, removing unnecessary write overhead on ingest (MOD-15487)\n- VecSim SVS thread pool integrated with the worker pool for better thread utilization (MOD-9881)"},{"id":"configuration-parameters","title":"Configuration parameters","role":"configuration","text":"- [#15182](https://github.com/redis/redis/pull/15182) Slowlog entry truncation limits:\n  - `slowlog-entry-max-argc`: maximum number of command arguments kept in a slowlog entry\n  - `slowlog-entry-max-string-len`: maximum length of a command argument in a slowlog entry\n- RediSearch/RediSearch[#8876](https://github.com/redisearch/redisearch/pull/8876), RediSearch/RediSearch[#8960](https://github.com/redisearch/redisearch/pull/8960) Default maximum worker threads value updated; `MAX_WORKER_THREADS` is now a string config (MOD-14486, MOD-14763)"},{"id":"metrics-compared-to-88-m03","title":"Metrics (compared to 8.8-M03)","role":"content","text":"- RediSearch/RediSearch[#8210](https://github.com/redisearch/redisearch/pull/8210), RediSearch/RediSearch[#8231](https://github.com/redisearch/redisearch/pull/8231) `FT.PROFILE`: added queue time tracking (MOD-13602)"},{"id":"cli-tools","title":"CLI tools","role":"content","text":"- [#15150](https://github.com/redis/redis/pull/15150) Memory leak on malformed legacy help entry in redis-cli"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch","title":"RediSearch release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/","summary":"RediSearch release notes for version 2.10 and earlier","tags":["docs","operate","stack"],"last_updated":"2024-08-08T19:21:33+03:00","children":[{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes","summary":"New vector data types. Enhanced indexing capabilities. Simplified query syntax. Expanded geospatial search.","title":"RediSearch 2.10 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes","summary":"RESP3 support. Geo Polygon Search. Performance improvements.","title":"RediSearch 2.8 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes","summary":"Search using wildcard queries for TEXT and TAG fields, multi-value indexing and querying of attributes for any attribute type, and indexing double-precision floating-point vectors and range queries from a given vector.","title":"RediSearch 2.6 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.4-release-notes","summary":"Vector Similarity Search (VSS). New query syntax Dialect version 2. Choose between Dialect 1 and Dialect 2 for query parser behavior. Hybrid queries.","title":"RediSearch 2.4 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.4-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.2-release-notes","summary":"Search and index JSON documents. Profiling queries. Field aliasing.","title":"RediSearch 2.2 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.2-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.0-release-notes","summary":"Automatically indexes data based on a key pattern. Scale a single index over multiple Redis shards. Improved query performance.","title":"RediSearch 2.0 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.0-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-1.6-release-notes","summary":"Improved performance of full-text search and aggregation queries. Support for aliasing of indices. Added a C API to embed RediSearch in other modules. Forked process garbage collection.","title":"RediSearch 1.6 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-1.6-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-1.4-release-notes","summary":"Conditional updates. Schema modification. Query spelling correction. Phonetic matching. More fuzziness in search. Retrieve and change runtime configuration. Unlimited autocomplete results.","title":"RediSearch 1.4 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-1.4-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-1.2-release-notes","summary":"Aggregation filters. Query attributes. Fuzzy matching. Conditional updates. Backslash escaping. Synonyms support.","title":"RediSearch 1.2 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-1.2-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-1.1-release-notes","summary":"Aggregations engine.","title":"RediSearch 1.1 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-1.1-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-1.0-release-notes","summary":"Hamming distance scoring. Wildcard queries. Optional deletion of documents in FT.DEL. Optionally keep document hashes in FT.DROP. Delete geo-sets when dropping an index. Tag prefix completion support.","title":"RediSearch 1.0 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-1.0-release-notes/"}],"page_type":"content","content_hash":"c02d2f0b28f2a42f22996bfeba403e59cd6738579e782fb9936e0e549f5d3b17","sections":[{"id":"content","title":"Content","role":"content","text":"| Version (Release date) | Major changes | Min Redis version | Min cluster version |\n| --- | --- | --- | --- |\n| [v2.10 (July 2024)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes/) | New vector data types. Enhanced indexing capabilities. Simplified query syntax. Expanded geospatial search. | 7.4 | 7.8 |\n| [v2.8 (July 2023)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes/) | RESP3 support. Geo Polygon Search. Performance improvements. | 7.2 | 7.2.4 |\n| [v2.6 (November 2022)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes/) | Search using wildcard queries for TEXT and TAG fields, multi-value indexing and querying of attributes for any attribute type, and indexing double-precision floating-point vectors and range queries from a given vector. | 6.0.16 | 6.2.8 |\n| [v2.4 (March 2022)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.4-release-notes/) | Vector Similarity Search (VSS). New query syntax Dialect version 2. Choose between Dialect 1 and Dialect 2 for query parser behavior. Hybrid queries. | 6.0.0 | 6.0.0 |\n| [v2.2 (November 2021)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.2-release-notes/) | Search and index JSON documents. Profiling queries. Field aliasing. | 6.0.0 | 6.0.0 |\n| [v2.0 (September 2020)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.0-release-notes/) | Automatically indexes data based on a key pattern. Scale a single index over multiple Redis shards. Improved query performance. | 6.0.0 | 6.0.0 |\n| [v1.6 (January 2020)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-1.6-release-notes/) | Improved performance of full-text search and aggregation queries. Support for aliasing of indices. Added a C API to embed RediSearch in other modules. Forked process garbage collection. | 4.0.0 | 5.0.0 |\n| [v1.4 (August 2018)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-1.4-release-notes/) | Conditional updates. Schema modification. Query spelling correction. Phonetic matching. More fuzziness in search. Retrieve and change runtime configuration. Unlimited autocomplete results. | 4.0.0 | 5.0.0 |\n| [v1.2 (June 2018)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-1.2-release-notes/) | Aggregation filters. Query attributes. Fuzzy matching. Conditional updates. Backslash escaping. Synonyms support. | 4.0.0 | 5.0.0 |\n| [v1.1 (April 2018)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-1.1-release-notes/) | Aggregations engine. | 4.0.0 | 5.0.0 |\n| [v1.0 (April 2018)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-1.0-release-notes/) | Hamming distance scoring. Wildcard queries. Optional deletion of documents in FT.DEL. Optionally keep document hashes in FT.DROP. Delete geo-sets when dropping an index. Tag prefix completion support. | 4.0.0 | 5.0.0 |"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-1.0-release-notes","title":"RediSearch 1.0 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-1.0-release-notes/","summary":"Hamming distance scoring. Wildcard queries. Optional deletion of documents in FT.DEL. Optionally keep document hashes in FT.DROP. Delete geo-sets when dropping an index. Tag prefix completion support.","tags":["docs","operate","stack"],"last_updated":"2023-12-14T15:27:49+01:00","page_type":"content","content_hash":"0621cef961412022df88bf191064b986efb6809c415a4da35042937e724fe69f","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RediSearch v1.0.10 requires:\n\n- Minimum Redis compatibility version (database): 4.0.0\n- Minimum Redis Enterprise Software version (cluster): 5.0.0"},{"id":"redisearch-1010-april-2018","title":"RediSearch 1.0.10 (April 2018)","role":"content","text":"This is a bug-fix release with some stability fixes, a few processing bug fixes, and a few small additions:"},{"id":"fixes","title":"Fixes","role":"content","text":"- Fixed false positives in NOT iterator (5fe948e)\n\n- Prevent crashing with uninitialized sorting vectors (96f2473)\n\n- Fixed overflow bug in triemap, and limited tag length to 4KB (09e682f)\n\n- Fixed false positives in intersects and negative intersects (4998772)\n\n- Fixed bug in union of id lists (b8e74ef3)"},{"id":"new-features","title":"New features","role":"content","text":"- Added support for tag prefix completions (137b346)"},{"id":"redisearch-109-march-2018","title":"RediSearch 1.0.9 (March 2018)","role":"content","text":"This is a maintenance release from the 1.0 branch, that does not yet contain the aggregation engine that's already in master. It includes a few small fixes and API additions that were backported from master."},{"id":"changes","title":"Changes","role":"content","text":"#### 1. Hamming distance scoring\n\nRediSearch can now sort the search result by the inverse Hamming Distance between document payloads and the query payloads (provided they are both the same length). This can be used as a nearest neighbor search ranking when a feature vector is encoded as a bitmap and the distance metric is Hamming distance.\n\n#### 2. Wildcard queries\n\nIt's now possible to search for just `*`, scanning all the documents in the index. This is useful in conjunction with the Hamming Distance scorer.\n\n#### 3. Optional deletion of documents in FT.DEL\n\nUp until now, FT.DEL did not delete the Hash key containing the actual document. As of this version, you can call it with an optional DD (Delete Document) argument, and it will also delete the document.\n\nFor example: `FT.DEL myIndex myDoc DD`.\n\n#### 4. Optionally keep document hashes in FT.DROP\n\nUp until now, FT.DROP always deleted the document Hash keys. As of this version, you can specify the `KEEPDOCS` argument to FT.DROP and it will not touch them.\n\n#### 5. Delete geo-sets when dropping an index\n\nUntil now we did not delete the geo-set keys where documents were geographically indexed. This version fixes this (#[295](https://github.com/RediSearch/RediSearch/issues/295))"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-1.1-release-notes","title":"RediSearch 1.1 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-1.1-release-notes/","summary":"Aggregations engine.","tags":["docs","operate","stack"],"last_updated":"2023-12-14T15:27:49+01:00","page_type":"content","content_hash":"3ce1126134137c65ed39ff89e0bd029ad348a4f61bb7ee80c9d8899ab424cdd3","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RediSearch v1.1.0 requires:\n\n- Minimum Redis compatibility version (database): 4.0.0\n- Minimum Redis Enterprise Software version (cluster): 5.0.0"},{"id":"redisearch-110-april-2018","title":"RediSearch 1.1.0 (April 2018)","role":"content","text":"This is a major version (we almost named it 2.0), which includes months of work, mostly on the brand new aggregations engine."},{"id":"aggregations","title":"Aggregations","role":"content","text":"Aggregations are a way to process the results of a search query, group, sort and transform them - and extract analytic insights from them. Much like aggregation queries in other databases and search engines, they can be used to create analytics report, or to perform Faceted Search style queries.\n\n#### Example aggregation request\n\nFor example, indexing a web-server's logs, we can create report for unique users by hour. Suppose our schema includes the **SORTABLE** fields `timestamp` (Unix-timestamp) and `userId`:\n\n[code example]\n\nSee [the full documentation on aggregations for more details](http://redisearch.io/Aggregations/)"},{"id":"bug-fixes-over-1010","title":"Bug fixes over 1.0.10","role":"content","text":"- Fixed #[313](https://github.com/RediSearch/RediSearch/issues/313) - removed -mpopcnt compile flag.\n\n- Fixed #[312](https://github.com/RediSearch/RediSearch/issues/312) - crash on highlighting"}],"examples":[{"id":"aggregations-ex0","language":"sh","code":"FT.AGGREGATE idx \"*\"\n   APPLY hour(@timestamp) AS hour\n   GROUPBY 1 @hour\n      REDUCE COUNT_DISTINCT 1 @userId AS unique_users\n   SORTBY 2 @hour ASC","section_id":"aggregations"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-1.2-release-notes","title":"RediSearch 1.2 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-1.2-release-notes/","summary":"Aggregation filters. Query attributes. Fuzzy matching. Conditional updates. Backslash escaping. Synonyms support.","tags":["docs","operate","stack"],"last_updated":"2023-12-14T15:27:49+01:00","page_type":"content","content_hash":"fe4a8e2b09df64c0255e79a1db130b774025ed1052abeec9cce3823b30433e53","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RediSearch v1.2.0 requires:\n\n- Minimum Redis compatibility version (database): 4.0.0\n- Minimum Redis Enterprise Software version (cluster): 5.0.0"},{"id":"redisearch-120-june-2018","title":"RediSearch 1.2.0 (June 2018)","role":"content","text":"This version is the last version that I (@dvirsky) will be releasing, and it includes a lot of new cool features, and actually NO bug-fixes over 1.1.0! So long and thanks for all the fish!"},{"id":"new-features","title":"New features","role":"content","text":"#### Aggregation filters\n\nAs an addition to the aggregation API in `FT.AGGREGATE`, it is possible to do post-index filtering of the pipeline, using the `FILTER` keyword. e.g.:\n\n[code example]\n\nSee [http://redisearch.io/Aggregations/](http://redisearch.io/Aggregations/) for more details.\n\n#### Query attributes\n\nIt is now possible to apply specific query modifying attributes to specific clauses of the query (see #[212](https://github.com/RediSearch/RediSearch/issues/212)).\n\nThe syntax is `(foo bar) => { $attribute: value; $attribute:value; ...}`, e.g:\n\n[code example]\n\nThe supported attributes are:\n\n1. $weight: determines the weight of the sub-query or token in the overall ranking on the result.\n1. $slop: determines the maximum allowed \"slop\" (space between terms) in the query clause.\n1. $inorder: whether or not the terms in a query clause must appear in the same order as in the query.\n\n#### Fuzzy matching\n\nWrapping a search term with `%` will cause the index to expand the query to terms that are within an Edit Distance of 1 from the original term. For example, querying for `%redis%` will expand it to query for `redis, jedis, credis, predis`, etc (provided the terms appear in documents in the index).\n\nNotice that each query term needs to be wrapped independently, and that we limit the maximum amount of expansions to 200 per term, as this hurts performance significantly.\n\n#### Conditional updates\n\nIt is now possible to update documents (`FT.ADD ... REPLACE [PARTIAL]`) only if a certain condition is met regarding the document's state **before** the updates. So for example, if our document has a timestamp field, and we would like to update its title only if the timestamp is below a certain value, we can do the following:\n\n[code example]\n\n#### Backslash escaping\n\nFollowing several user requests, it is now possible to escape separator characters in documents (it is already done in the query itself), and avoid tokenization when needed.\n\nFor example, indexing the text `hello\\-world hello world` will create the tokenization `[\"hello-world\", \"hello\", \"world\"]`. Notice that in most languages and in redis-cli, you will need to escape the backslash itself, so instead of `hello\\-world` you will need to send the string `hello\\\\-world`.\n\nThe same goes to the query string: If the document contains the token `hello-world`, it can be found by running in redis-cli: `FT.SEARCH idx \"hello\\\\-world\", which will cause the query not to be separated.\n\n#### Synonyms support\n\nIt is now possible to provide the index with synonym groups (.e.g boy, child, kid), and have it automatically index synonymous terms so that searching for one will return documents containing another.\n\nSee [http://redisearch.io/Synonyms/](http://redisearch.io/Synonyms/) for more details and examples."}],"examples":[{"id":"new-features-ex0","language":"sh","code":"FT.AGGREGATE idx \"*\"\n   GROUPBY 1 @foo\n     REDUCE count 0 AS num\n   FILTER \"@num < 100\"","section_id":"new-features"},{"id":"new-features-ex1","language":"sh","code":"(foo bar) => { $weight: 2.0; $slop: 1 }\n~(bar baz) => { $weight: 0.5; }","section_id":"new-features"},{"id":"new-features-ex2","language":"sh","code":"FT.ADD myIndex myDoc 1.0\n   REPLACE PARTIAL\n   IF \"@timestamp < 12313134523\"\n   FIELDS\n       title \"new title\"","section_id":"new-features"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-1.4-release-notes","title":"RediSearch 1.4 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-1.4-release-notes/","summary":"Conditional updates. Schema modification. Query spelling correction. Phonetic matching. More fuzziness in search. Retrieve and change runtime configuration. Unlimited autocomplete results.","tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:33+01:00","page_type":"content","content_hash":"3e276810465dc6e0083f840bf9ca593d4f844a1e0c6affad21f90e0c71f5b843","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RediSearch v1.4.28 requires:\n\n- Minimum Redis compatibility version (database): 4.0.0\n- Minimum Redis Enterprise Software version (cluster): 5.0.0"},{"id":"v1428-may-2020","title":"v1.4.28 (May 2020)","role":"content","text":"This is a maintenance release for version 1.4.\n\nHeadlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n\nDetails:\n\n- Bugfixes:\n    - #[1218](https://github.com/RediSearch/RediSearch/pull/1218) Potential crash when running without concurrency and using the cursor API."},{"id":"v1427-april-2020","title":"v1.4.27 (April 2020)","role":"content","text":"Headlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n\nDetails:\n\n- Features:\n    - #[1172](https://github.com/redisearch/redisearch/issues/1172) Added `exists` function that can be used on conditional updates `REPLACE PARTIAL` to check if a field exists in the document.\n- Minor Enhancements:\n    - #[1172](https://github.com/redisearch/redisearch/issues/1172) Lazy evaluation of the right side of 'or'/'and' clauses in IF condition.\n- Bugfixes:\n    - #[1110](https://github.com/redisearch/redisearch/issues/1110) Rare GC failure when accessing uninitialized variable.\n    - #[1131](https://github.com/redisearch/redisearch/issues/1131) Crash on highlighting a search query where the document no longer exists."},{"id":"v1426-march-2020","title":"v1.4.26 (March 2020)","role":"content","text":"Headlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n\nDetails:\n\n- Fix rare `FORK GC` crash which caused by accessing uninitialized variable."},{"id":"v1425-march-2020","title":"v1.4.25 (March 2020)","role":"content","text":"Headlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n\nDetails:\n\n- Features:\n    - #[1051](https://github.com/RediSearch/RediSearch/issues/1051) Added support for updating tag fields on document updates with `NOINDEX` fields.\n- Bugfixes:\n    - #[1051](https://github.com/RediSearch/RediSearch/issues/1051) `FORK GC` was not updating the unique sum of the numeric index."},{"id":"v1424-january-2020","title":"v1.4.24 (January 2020)","role":"content","text":"Headlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n\nDetails:\n\n- Bugfixes:\n    - #[1038](https://github.com/RediSearch/RediSearch/issues/1038) Memory leak on cursor.\n    - #[1049](https://github.com/RediSearch/RediSearch/issues/1049) Crash on conversion error when freeing other indexed fields."},{"id":"v1423","title":"v1.4.23","role":"content","text":"Headlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n\nDetails:\n\n- Bugfixes:\n    - Memory leak when cursor timed out and cursor wasn't consumed."},{"id":"v1422","title":"v1.4.22","role":"content","text":"Headlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n\nDetails:\n\n- Bugfixes:\n    - FILTER option was not working correctly with coordinator.\n    - Memory leak when cursor was combined with sorted fields."},{"id":"v1421","title":"v1.4.21","role":"content","text":"Headlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n\nDetails:\n\n- Bugfixes:\n    - #[1031](https://github.com/RediSearch/RediSearch/issues/1031) Highlighting crashed when used with `NOINDEX` fields."},{"id":"v1420-january-2020","title":"v1.4.20 (January 2020)","role":"content","text":"Update urgency: Medium\nThis is a maintenance release for version 1.4.\n\nThis release improves overall stability and provides fixes for issues found after the previous release.\n\n- Improvements\n    - #[1020](https://github.com/RediSearch/RediSearch/issues/1020) Performance improvement for reading fields that are not sortable.\n- Bugfixes\n    - #[1022](https://github.com/RediSearch/RediSearch/issues/1022) Illegal memory access by queries during `GC` run.\n    - #[1022](https://github.com/RediSearch/RediSearch/issues/1022) Recreating the index with the same name (delete+create) removed the index from cursor list.\n    - #[1022](https://github.com/RediSearch/RediSearch/issues/1022) Memory leak when performing `FT.AGGREGATE` on non-existing index.\n    - #[1022](https://github.com/RediSearch/RediSearch/issues/1022) Potential data corruption during `GC` run.\n    - #[1025](https://github.com/RediSearch/RediSearch/issues/1025) Aliasing not working properly with `FT.AGGREGATE`."},{"id":"v1419-december-2019","title":"v1.4.19 (December 2019)","role":"content","text":"Update urgency: Medium\nThis is a maintenance release for version 1.4.\n\nThis release improves overall stability and provides fixes for issues found after the previous release.\n\n- Bugfixes\n    - #[1000](https://github.com/RediSearch/RediSearch/issues/1000) - `FT.DEL` was not replicated to replica correctly\n    - #[1004](https://github.com/RediSearch/RediSearch/issues/1004) -  Memory leak on `TAG` array on certain situations\n    - #[1006](https://github.com/RediSearch/RediSearch/issues/1006) - Unexposed error on conditional update `IF` that caused the error message to leak"},{"id":"v1418-november-2019","title":"v1.4.18 (November 2019)","role":"content","text":"Update urgency: Medium\nThis is a maintenance release for version 1.4.\n\nThis release improves overall stability and provides fixes for issues found after the previous release.\n\n- Bug fixes\n    - [#947](https://github.com/RediSearch/RediSearch/issues/947) Fix short read on FORK GC pipe that could result in a crash and potential data corruption"},{"id":"v1417-october-2019","title":"v1.4.17 (October 2019)","role":"content","text":"Update urgency: Medium\nThis is a maintenance release for version 1.4.\n\nThis release improves overall stability and provides fixes for issues found after the previous release.\n\n- Features:\n    - #[779](https://github.com/RediSearch/RediSearch/issues/779) Added `to_number()` and `to_str()` functions for ambiguity reasons\n- Improvements\n    - #[891](https://github.com/RediSearch/RediSearch/issues/891) All memory allocations will now use the redis memory allocator.  This means that all the memory will be exposed correctly in the redis `INFO MEMORY` command."},{"id":"v1416-september-2019","title":"v1.4.16 (September 2019)","role":"content","text":"Update urgency: Medium\nThis is a maintenance release for version 1.4.\n\nThis release improves overall stability and provides fixes for issues found after the previous release.\n\nMain features:\n\n- #[883](https://github.com/RediSearch/RediSearch/issues/883) Forkgc optimizations - introduce new config parameter `FORK_GC_CLEAN_THRESHOLD`. RediSearch will only start to clean when the number of not cleaned documents is exceeding this threshold.\n\nMain Fixes:\n\n- #[848](https://github.com/RediSearch/RediSearch/issues/848) RediSearch will not crash when sorting on fields that don't exists in all documents.\n- #[884](https://github.com/RediSearch/RediSearch/issues/884) Fix wrong results on intersect iterator."},{"id":"v1415-28-august-2019","title":"v1.4.15 (28 August 2019)","role":"content","text":"Update urgency: Medium\nThis is a maintenance release for version 1.4.\n\nThis release improves overall stability and provides fixes for issues found after the previous release.\n\nMain Fixes:\n\n- #[866](https://github.com/RediSearch/RediSearch/issues/866) - Fix RDB corruption caused by deleting none-existing terms from the suggestion trie.\n- When Redis exits, forked processes by FORK Garbage Collection will now be closed accordingly.\n- For indices that are not temporary and interleaved: When an index is dropped, the indexer thread is now closed."},{"id":"v1414-20-august-2019","title":"v1.4.14 (20 August 2019)","role":"content","text":"Update urgency: Medium\nThis is a maintenance release for version 1.4.\n\nThis release improves overall stability and provides fixes for issues found after the previous release.\n\nMain Fixes:\n\n- #[851](https://github.com/RediSearch/RediSearch/issues/851) In interleaved mode (non safemode), documents deleted by concurrent updates, will be ignored."},{"id":"v1413-8-august-2019","title":"v1.4.13 (8 August 2019)","role":"content","text":"Update urgency: Medium\nThis is a maintenance release for version 1.4.\n\nThis release improves overall stability and provides fixes for issues found after the previous release .\n\nMain Fixes:\n\n- #[798](https://github.com/RediSearch/RediSearch/issues/798) fix issue where phonetic queries return wrong results\n- #[820](https://github.com/RediSearch/RediSearch/issues/820) fix crash on getting a none existing doc from a doc table\n- Fix issue with invalid memory read when using tags with ' ' (space) separator"},{"id":"v1412-5-august-2019","title":"v1.4.12 (5 August 2019)","role":"content","text":"Update urgency: Medium\nThis is a maintenance release for version 1.4.\n\nThis release improves overall stability and provides fixes for issues found after the previous release .\n\nMain features:\n\n- #[741](https://github.com/RediSearch/RediSearch/issues/741) Allow Chinese tokenizing to recognise \\-escape for punctuations\n\nMain Fixes:\n\n- #[739](https://github.com/RediSearch/RediSearch/issues/739) Fix crash on search error\n- #[346](https://github.com/RediSearch/RediSearch/issues/346) Fix issue where fuzzy can not be used with numbers\n- #[769](https://github.com/RediSearch/RediSearch/issues/769) Fix rare crash on rdb loading\n- #[749](https://github.com/RediSearch/RediSearch/issues/749) On prefix searches, do not expand prefixes to terms which have no documents"},{"id":"v1411-june-2019","title":"v1.4.11 (June 2019)","role":"content","text":"Update urgency: Medium\n\nThis release only add support for aliasing to the previews 1.4.10 release.\n\n- Added functionality\n    - #[731](https://github.com/RediSearch/RediSearch/issues/731) Add index aliasing. This allows users to provide (or remove) ‘links’ to indexes. The commands are FT.ALIASADD, FT.ALIASDEL, and FT.ALIASUPDATE."},{"id":"v1410-28-may-2019","title":"v1.4.10 (28 May 2019)","role":"content","text":"Update urgency: Medium\n\nThis is a maintenance release for version 1.4.\n\nThis release improves overall stability and provides fixes for issues found after the previous release .\n\nMain Fixes:\n\n- Fix memory leak when combining SORT with APPLY on FT.AGGREGATE"},{"id":"v149-18-may-2019","title":"v1.4.9 (18 May 2019)","role":"content","text":"Update urgency: Medium\n\nThis is a maintenance release for version 1.4.\n\nThis release improves overall stability and provides fixes for issues found after the previous release .\n\nMain Fixes:\n\n- Fork GC will now squash the empty blocks of the inverted indexes.\n- Fix invalid memory access when using aggregate with GROUPBY.\n- Fix issue where using limit with SORTBY might return duplicate results.\n\nKnown Issue:\n\n- Memory leak when combining SORT and APPLY in FT.AGGREGATE. This issue is fixed on 1.4.10. It's recommended to skip directly to 1.4.10."},{"id":"v148-29-april-2019","title":"v1.4.8 (29 April 2019)","role":"content","text":"Update urgency: Low\n\nTechnical release, no changes nor fixes."},{"id":"v147-29-april-2019","title":"v1.4.7 (29 April 2019)","role":"content","text":"Update urgency: Medium\n\nThis is a maintenance release for version 1.4.\n\nThis release improves overall stability and provides fixes for issues found after the previous release .\n\nMain Fixes:\n\n- Fix issue where Dropping and recreating the same index while querying might cause crashes."},{"id":"v146-8-april-2019","title":"v1.4.6 (8 April 2019)","role":"content","text":"Update urgency: Medium\n\nThis is a maintenance release for version 1.4.\n\nThis release improves overall stability and provides fixes for issues found after the previous release .\n\nMain Fixes:\n\n- Loading a potentially corrupted RDB files generated by versions lower than 1.4\n- Fix issue where `REPLACE PARTIAL` might not work properly (#[621](https://github.com/RediSearch/RediSearch/issues/621))"},{"id":"v145-march-2019","title":"v1.4.5 (March 2019)","role":"content","text":"Update urgency: Low\n\nThis is a maintenance release for version 1.4.\n\nThis release improves overall stability and provides fixes for issues found after the previous release .\n\nMain Fixes:\n\n- Fixed issue where FORK GC causing redis rdb fork to fail"},{"id":"v144-21-february-2019","title":"v1.4.4 (21 February 2019)","role":"content","text":"Update urgency: Low\n\nThis is a maintenance release for version 1.4.\n\nThis release improves overall stability and provides fixes for issues found after the previous release .\n\nMain Fixes:\n\n- Fixed memory leak on Fork GC\n- Fixed key close after releasing GIL on Fork GC (might cause crashed on rare situations)"},{"id":"v143-4-february-2019","title":"v1.4.3 (4 February 2019)","role":"content","text":"Update urgency: Low\n\nThis is a maintenance release for version 1.4.\n\nThis release improves overall stability and provides fixes for issues found after the previous release .\n\nMain Fixes:\n\n1. Fixed memory leak on mempool destroy\n1. Fixed process crash when running with fork GC (preview)\n1. Fixed fork GC  (preview)  deadlock when using Tags\n1. Fixed memory leaks on index creation and deletion"},{"id":"v142-27-november-2018","title":"v1.4.2 (27 November 2018)","role":"content","text":"Update urgency: Low\n\nThis is a maintenance release for version 1.4.\n\nThis release improves overall stability and provides fixes to issues found."},{"id":"v141-12-november-2018","title":"v1.4.1 (12 November 2018)","role":"content","text":"Update urgency: Medium\n\nThis is a maintenance release for version 1.4, with the next version planned for release being 2.0.\n\nThis release improves overall stability and focuses on performance improvements of the garbage collector. It also includes:\n\n- New: Runtime Configuration\n- Change: Unlimited Autocomplete Results"},{"id":"garbage-collector","title":"Garbage collector","role":"content","text":"RediSearch employs a garbage collector that removes deleted documents from the internal data structures.\n\nIn this release the garbage collection mechanism was improved in terms of efficiency, i.e. the amount of memory it reclaims, as well as in terms of performance (i.e. it is faster). The improved mechanism uses forked threads. Additional details can be found in this post: [How We Increased Garbage Collection Performance with RediSearch 1.4.1](https://redislabs.com/blog/increased-garbage-collection-performance-redisearch-1-4-1/).\n\nThe improved collection mechanism is currently **experimental*- and is not enabled by default. Enabling the new garbage collection mechanism requires setting the `GC_POLICY` configuration option to `FORK` at load time, for example:\n\n[code example]"},{"id":"runtime-configuration","title":"Runtime configuration","role":"content","text":"RediSearch's [configuration](https://redis.io/docs/latest/develop/ai/search-and-query/administration/configuration) is applied via arguments passed to the module at load time. This release introduces the new `FT.CONFIG` command that allows to retrieve the current configuration as well as change it during runtime."},{"id":"unlimited-autocomplete-results","title":"Unlimited autocomplete results","role":"content","text":"This version removes the limit of 10 results from `FT.SUGGET` - you can set the `MAX num` as high as needed."},{"id":"v140-august-2018","title":"v1.4.0 (August 2018)","role":"content","text":"**Update urgency:*- Medium - mainly due to numerous fixes\n\nThis version improves overall stability and performance of RediSearch. It also delivers better support for use cases in which documents are continuously updated. New features:\n\n- Conditional updates\n- Schema modification\n- Query spelling correction\n- Phonetic matching\n- Enhancement: More fuzziness in search"},{"id":"continuous-updates","title":"Continuous updates","role":"content","text":"Like most search engines, RediSearch was designed for maintaining append-mostly indices. To update an existing document, the document is actually replaced - that is deleted and added to the index.\n\nBecause RediSearch provides realtime indexing and searching, it is sometimes used to search near-realtime data (or the results of its processing). In such cases, the number of indexed documents stays mostly static, but their contents are continuously updated.\n\nTo support this use case, RediSearch has been reworked internally to use 64-bit internal document IDs so that an index can sustain high update throughputs without overflowing. Furthermore and as a result, significant effort has been put into improving memory management and garbage collection."},{"id":"conditional-updates","title":"Conditional updates","role":"content","text":"The `IF` subcommand has been add to `FT.ADD`. When used with the existing `REPLACE [PARTIAL]` subcommand, the document will be updated only if the condition provided evaluates to a truth value, otherwise a special `NOADD` reply is returned."},{"id":"schema-modification","title":"Schema modification","role":"content","text":"The `FT.ALTER` command has been introduced, and provides the ability to add new fields to the definition of an existing index. The contents of such newly-added fields are indexed only for new or updated documents."},{"id":"query-spelling-correction","title":"Query spelling correction","role":"content","text":"Query spelling correction, a.k.a \"did you mean\", is now provided via the `FT.SPELLCHECK` command. It enables generating suggestions for search terms that could be misspelled. For more details see [Query Spelling Correction](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/spellcheck)."},{"id":"phonetic-matching","title":"Phonetic matching","role":"content","text":"Phonetic matching, a.k.a \"Jon or John?\", is now supported via the `PHONETIC` text field attribute. The terms in such fields are also indexed by their phonetic equivalents, and search results include these by default. For more details see [Phonetic Matching](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/phonetic_matching)."},{"id":"more-fuzziness-in-search","title":"More fuzziness in search","role":"content","text":"The fuzzy match operator, '%', can now be repeated up to three times to specify the Levenshtein distance. That means that the queries `%hello%`, `%%hello%%` and `%%%hello%%%` will perform fuzzy matching on 'hello' for all terms with LD of 1, 2 and 3, respectively."}],"examples":[{"id":"garbage-collector-ex0","language":"sh","code":"redis-server --loadmodule ./redisearch.so GC_POLICY FORK","section_id":"garbage-collector"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-1.6-release-notes","title":"RediSearch 1.6 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-1.6-release-notes/","summary":"Improved performance of full-text search and aggregation queries. Support for aliasing of indices. Added a C API to embed RediSearch in other modules. Forked process garbage collection.","tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:33+01:00","page_type":"content","content_hash":"55fdd083f1cbe54e4fe37dca9d89313564b2340831fdcd0ea9f80a3021cbe64d","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RediSearch v1.6.16 requires:\n\n- Minimum Redis compatibility version (database): 4.0.0\n- Minimum Redis Enterprise Software version (cluster): 5.0.0"},{"id":"v1616-june-2021","title":"v1.6.16 (June 2021)","role":"content","text":"This is a maintenance release for version 1.6.\n\nUpdate urgency: MODERATE - Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fix:\n\n    - #[2018](https://github.com/RediSearch/RediSearch/pull/2018): FT.ADD REPLACE leaves fields from the previous document that are not included in the new document #[647](https://github.com/RediSearch/RediSearch/pull/647) #[1193](https://github.com/RediSearch/RediSearch/pull/1193)"},{"id":"v1615-february-2021","title":"v1.6.15 (February 2021)","role":"content","text":"This is a maintenance release for version 1.6.\n\nUpdate urgency: Low\n\nDetails:\n\n- Minor enhancements:\n    - #[1225](https://github.com/RediSearch/RediSearch/pull/1225) Allow scientific representation of numbers for numeric fields.\n    - #[1574](https://github.com/RediSearch/RediSearch/pull/1574) Allow SORTBY for non-sortable fields.\n\n- Minor bugfixes:\n    - #[1683](https://github.com/RediSearch/RediSearch/pull/1683) Add a module parameter _NUMERIC_COMPRESS which prevent <nobr>double -> float</nobr> compression. It prevents an issue where an exact match on some floating-point numbers is not found.\n    - #[1757](https://github.com/RediSearch/RediSearch/pull/1757) Remove assertion on the limitation of the number of sortable fields, instead return an error.\n    - #[1668](https://github.com/RediSearch/RediSearch/pull/1668) Query words from stopword list on tag fields.\n    - #[1745](https://github.com/RediSearch/RediSearch/pull/1745) Enforce 0 value for count reducer.\n    - #[1774](https://github.com/RediSearch/RediSearch/pull/1774) MINPREFIX & MAXEXPANSION can be changed in runtime.\n    - #[1861](https://github.com/RediSearch/RediSearch/pull/1761) Fix issue for FT.SCOREEXPLAIN where reply array depth can exceed 7.\n    - #[1689](https://github.com/RediSearch/RediSearch/pull/1689) FT.SUGGET results from RSCoordinator are more consistent.\n    - Various small tweaks under the hood."},{"id":"v1614-september-2020","title":"v1.6.14 (September 2020)","role":"content","text":"- This is a maintenance release for version 1.6.\n\nDetails:\n\n- Minor features:\n\n    - #[1420](https://github.com/RediSearch/RediSearch/pull/1420) The hard limit of the number of results produced by FT.SEARCH is now configurable with MAXSEARCHRESULTS.\n\n- Bug fixes:\n\n    - #[1313](https://github.com/RediSearch/RediSearch/pull/1313) Wrong error of unsupported phonetic field.\n    - #[1286](https://github.com/RediSearch/RediSearch/pull/1286) Possible crash on optional search.\n    - #[1449](https://github.com/RediSearch/RediSearch/pull/1449) Rare file descriptor leak on FORK GC.\n    - #[1469](https://github.com/RediSearch/RediSearch/pull/1469) Endless loop when reaching internal docid above uint32_max."},{"id":"v1613-may-2020","title":"v1.6.13 (May 2020)","role":"content","text":"This is a maintenance release for version 1.6.\n\nHeadlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n\nDetails:\n\n- Bugfixes:\n    - #[1186](https://github.com/RediSearch/RediSearch/pull/1186) #[1188](https://github.com/RediSearch/RediSearch/pull/1188) incorrect values for `inverted_sz_mb` and `num_records` in `FT.INFO` command."},{"id":"v1612-april-2020","title":"v1.6.12 (April 2020)","role":"content","text":"Headlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n\nDetails:\n\n- Features:\n    - #[1172](https://github.com/redisearch/redisearch/issues/1172) Added `exists` function that can be used on conditional updates (`REPLACE PARTIAL`) to check if a field exists in the document.\n- Minor Enhancements:\n    - #[1172](https://github.com/redisearch/redisearch/issues/1172) Lazy evaluation of the right side of 'or'/'and' clauses in IF condition.\n    - #[1134](https://github.com/redisearch/redisearch/issues/1134) Remove hard limit on LIMIT when using FT.SEARCH.\n- Bugfixes:\n    - #[1124](https://github.com/redisearch/redisearch/issues/1124) NOINDEX tag fields could not be updated on UPDATE PARTIAL with no indexed fields.\n    - #[1120](https://github.com/redisearch/redisearch/issues/1120) Release loop in II_GetCriteriaTester which released the same criteria tester multiple times.\n    - #[1161](https://github.com/redisearch/redisearch/issues/1161) Case where setting `MAXDOCTABLESIZE` had no effect.\n    - #[1169](https://github.com/redisearch/redisearch/issues/1169) `FIRST_VALUE` reducer crashed when value did not exist.\n    - #[1159](https://github.com/redisearch/redisearch/issues/1159) Infinite loop on `NOT` criteria tester.%"},{"id":"v1611-march-2020","title":"v1.6.11 (March 2020)","role":"content","text":"Headlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n\nDetails:\n\n- Bugfixes:\n    - #[1126](https://github.com/redisearch/redisearch/issues/1126) Memory leak introduced by queries for tag fields that have no results."},{"id":"v1610-march-2020","title":"v1.6.10 (March 2020)","role":"content","text":"Headlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n- This release fixes certain backwards compatibility issues compared to 1.4.  Although they are rare cases, it is recommended when upgrading to 1.6 to use this version or newer.\n\nDetails:\n\n- Minor Enhancements:\n    - #[1062](https://github.com/redisearch/redisearch/issues/1062) Added Custom stopwords list in `FT.INFO`\n- Fixed backwards incompatible issues:\n    - #[1075](https://github.com/redisearch/redisearch/issues/1075) Fields should always be returned to the user as a string.\n    - #[1074](https://github.com/redisearch/redisearch/issues/1074) Don't truncate possible integral values when printing.\n    - #[1065](https://github.com/redisearch/redisearch/issues/1065) Revert \"Change how generated reducer aliases are made\".\n- Bugfixes:\n    - #[1085](https://github.com/redisearch/redisearch/issues/1085) Min and max value on non leaf nodes in the numeric tree should not be updated.\n    - #[1106](https://github.com/redisearch/redisearch/issues/1106)  Pipe leak on `FORK GC` caused by closing the fork without holding the lock.\n    - #[1114](https://github.com/redisearch/redisearch/issues/1114)  PR #[986](https://github.com/redisearch/redisearch/issues/986) reverted the work from #[985](https://github.com/redisearch/redisearch/issues/985), #[989](https://github.com/redisearch/redisearch/issues/989). This PR reintroduces these features."},{"id":"v169-february-2020","title":"v1.6.9 (February 2020)","role":"content","text":"Headlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n\nDetails:\n\n- Bugfixes:\n    - #[1052](https://github.com/redisearch/redisearch/issues/1052) Remove wrong optimization on Quantile.\n    - #[1057](https://github.com/redisearch/redisearch/issues/1057) Memory pool did not release memory when certain limit was reached."},{"id":"v168-february-2020","title":"v1.6.8 (February 2020)","role":"content","text":"Headlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n\nDetails:\n\n- Bugfixes:\n    - #[1052](https://github.com/redisearch/redisearch/issues/1052) Remove wrong optimization on Quantile.\n    - #[1057](https://github.com/redisearch/redisearch/issues/1057) Memory pool did not release memory when certain limit was reached."},{"id":"v16-ga-january-2020","title":"v1.6 GA (January 2020)","role":"content","text":"This is the General Availability Release of RediSearch 1.6 (v1.6.7).\n\nHeadlines:\n\n- Several performance improvements increasing full-text search queries up to 60% and aggregation queries up to 73%.\n- Support for aliasing of indices.\n- Low-level API in C (and [Rust bindings](https://github.com/RediSearch/redisearch-api-rs)) to make RediSearch embeddable in other Redis modules. [RedisGraph](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/graph) is the first GA consumer.\n- Forked process Garbage Collection (FORK GC) allows for stable read latencies.\n\nFull details:\n\n- Added functionality\n    - #[658](https://github.com/RediSearch/RediSearch/issues/658) `FT.ADD … REPLACE … NOCREATE` will not add the document if the document does not exist.\n    - #[575](https://github.com/RediSearch/RediSearch/issues/575) Add index aliasing. This allows users to provide (or remove) ‘links’ to indexes. The commands are `FT.ALIASADD`, `FT.ALIASDEL`, and `FT.ALIASUPDATE`.\n    - New C API to make RediSearch embeddable in other Redis modules. This API allows other Redis modules to use functionality of RedisSearch without actually having the \"module\" functionality active. Note that this must still be used on Redis proper. Modules that are already incorporating this API\n    - [RedisGraph](https://github.com/RedisGraph/RedisGraph/releases/tag/v2.0.1) GA\n    - [RedisTimeSeries](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/186) (WIP)\n    - [RedisJSON](https://github.com/RedisJSON/RedisJSON2) (WIP)\n\n- Performance improvements\n    - Improve performance when using many union (|) iterators\n    - Improve performance when using many intersect iterators\n    - Improve overall index reading performance\n    - #[598](https://github.com/RediSearch/RediSearch/issues/598) Do not return `foo: NULL` if `foo` is not present in the document. This conserves network bandwidth\n\n- Bugfixes - Semantics\n    - #[688](https://github.com/RediSearch/RediSearch/issues/688) #[623](https://github.com/RediSearch/RediSearch/issues/623) Fix various issues with optional (~) search operator:\n        - Fixes omitted results when using union operators in addition to optional iterators.\n        - Allow optional iterator to be used in isolation in promotion-only mode (without a filter query)\n        - Fix issue where weight attribute was being ignored\n    - #[653](https://github.com/RediSearch/RediSearch/issues/653) FT.GET will no longer return a document as existing if it was not added by FT.ADD, even if the document exists in the server as a plain redis hash\n    - FT.AGGREGATE is now more stringent with its semantics, avoiding nonsensical queries or referencing fields which do not exist in the schema or LOADed.\n    - #[779](https://github.com/RediSearch/RediSearch/issues/779) Added `to_number()` and `to_str()` functions for ambiguity reasons\n    - #[906](https://github.com/RediSearch/RediSearch/issues/906) A description of how scores were calculated can be added by adding 'EXPLAINSCORE'\n    - #[897](https://github.com/RediSearch/RediSearch/issues/897) `FORK GC` has now lowest priority over indexing and read queries\n    - Added automated tests to ensure macOs build works\n\n- Bugfixes - Crash/Stability\n    - Improved overall architectural stability\n    - #[666](https://github.com/RediSearch/RediSearch/issues/666) Fix crash when conflict between internal key name and user key name is encountered; e.g. creating a new document with `ft:two/two`\n    - #[697](https://github.com/RediSearch/RediSearch/issues/697) #[588](https://github.com/RediSearch/RediSearch/issues/588) Fix memory leaks\n    - #[691](https://github.com/RediSearch/RediSearch/issues/691) Fix crash on `FT.EXPLAIN`\n    - Proper module-level and index-level cleanup functionality\n    - Simplified concurrency model\n    - #[898](https://github.com/RediSearch/RediSearch/issues/898) Fix rare issue where `FORK GC` doesn't exists on termination of Redis\n    - #[865](https://github.com/RediSearch/RediSearch/issues/865) When using `FT.SEARCH` with `SORTBY`, it will only be possible to sort by one field\n    - #[917](https://github.com/RediSearch/RediSearch/issues/917) Fix wrong results introduced in a skip optimisation\n    - #[888](https://github.com/RediSearch/RediSearch/issues/888) `NULL` terms cause `FORK` GC to crash\n    - #[887](https://github.com/RediSearch/RediSearch/issues/887) Chinese searches not being converted to simplified Chinese.\n    - Fix `FORK GC` issue where read from pipe did not returned all the data"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.0-release-notes","title":"RediSearch 2.0 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.0-release-notes/","summary":"Automatically indexes data based on a key pattern. Scale a single index over multiple Redis shards. Improved query performance.","tags":["docs","operate","stack"],"last_updated":"2026-10-02T14:53:57+01:00","page_type":"content","content_hash":"4c8a2747894879c49505e80ebc478ff0bb6f6dab57e8e489549e8a9d97566cd5","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RediSearch v2.0.13 requires:\n\n- Minimum Redis compatibility version (database): 6.0.0\n- Minimum Redis Enterprise Software version (cluster): 6.0.0"},{"id":"v2015-december-2021","title":"v2.0.15 (December 2021)","role":"content","text":"This is a maintenance release for RediSearch 2.0.\n\nUpdate urgency: `MODERATE` - Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n    - [#2388](https://github.com/RediSearch/RediSearch/pull/2388) Garbage Collection (GC) for empty ranges in numeric index\n    - [#2409](https://github.com/RediSearch/RediSearch/pull/2409) Introduction of `FORK_GC_CLEAN_NUMERIC_EMPTY_NODES true` module argument to enable [#2388](https://github.com/RediSearch/RediSearch/pull/2388) (off by default)\n    - [#325](https://github.com/RediSearch/RediSearch/pull/325) Used Redis allocator in hiredis (RSCoordinator)\n    - [#2362](https://github.com/RediSearch/RediSearch/pull/2362) Crash on empty field name"},{"id":"v2013-november-2021","title":"v2.0.13 (November 2021)","role":"content","text":"This is a maintenance release for version 2.0.\n\nDetails:\n\n- Enhancements:\n  - #[2243](https://github.com/redisearch/redisearch/issues/2243) Add `LOAD *` for FT.AGGREGATE which will load all fields\n\n- Bug fixes:\n  - #[2269](https://github.com/redisearch/redisearch/issues/2269) #[2291](https://github.com/redisearch/redisearch/issues/2291) Remove TAG values from trie with no entries on Garbage Collection.\n  - #[2287](https://github.com/redisearch/redisearch/issues/2287) Uninitialized read on `FT.ADD`\n  - #[2342](https://github.com/redisearch/redisearch/issues/2342) Check for NULL result on intersect iterator\n  - #[2350](https://github.com/redisearch/redisearch/issues/2350) Crash on `FT.AGGREGATE` with `LIMIT 0 0`"},{"id":"v2012-september-2021","title":"v2.0.12 (September 2021)","role":"content","text":"This is a maintenance release for version 2.0.\n\nDetails:\n\n- Enhancements:\n  - #[2184](https://github.com/redisearch/redisearch/issues/2184) API: getter functions for score, language and stop words list\n  - #[2188](https://github.com/redisearch/redisearch/issues/2188) Introduced the UNF parameter to SORTABLE to disable normalisation on TAG/TEXT fields\n  - #[2218](https://github.com/redisearch/redisearch/issues/2218) API: added RediSearch_CreateDocument2\n\n- Bug fix:\n  - #[2153](https://github.com/redisearch/redisearch/issues/2153) Restore FT.INFO complexity to O(1)\n  - #[2203](https://github.com/redisearch/redisearch/issues/2203) FT.AGGREGATE returns inaccurate results when TAG field is not set in hash"},{"id":"v2011-august-2021","title":"v2.0.11 (August 2021)","role":"content","text":"This is a maintenance release for version 2.0.\n\nDetails:\n\n- Enhancements:\n  - #[2156](https://github.com/redisearch/redisearch/issues/2156) TAG fields can now be case sensitive using the `CASESENSITIVE` parameter\n  - #[2113](https://github.com/redisearch/redisearch/issues/2113) An already existing document that can't be updated is removed from the index (JIRA MOD-1266)\n  - #[267](https://github.com/RediSearch/RSCoordinator/pull/267) #[287](https://github.com/RediSearch/RSCoordinator/pull/287) Updated Hiredis to support Intershard TLS\n\n- Bug Fixes:\n  - #[2117](https://github.com/redisearch/redisearch/issues/2117) #[2115](https://github.com/redisearch/redisearch/issues/2115) Fix crash on coordinator on first value reducer"},{"id":"v2010-july-2021","title":"v2.0.10 (July 2021)","role":"content","text":"This is a maintenance release for version 2.0.\n\nDetails:\n\n- Enhancements:\n    - #[2025](https://github.com/redisearch/redisearch/issues/2025) Improve performances on numeric range search\n    - #[1958](https://github.com/redisearch/redisearch/issues/1958) #[2033](https://github.com/redisearch/redisearch/issues/2033) Support of sortable on the `GEO` type\n    - #[2079](https://github.com/redisearch/redisearch/issues/2079) Update to Snowball 2.1.0, adds Armenian, Serbian and Yiddish stemming support\n    - #[2002](https://github.com/redisearch/redisearch/issues/2002) Add stopwords list support in the API\n\n- Bug fix:\n    - #[2045](https://github.com/redisearch/redisearch/issues/2045) Possible crash when loading an RDB file (silently ignore double load of alias)\n    - #[2099](https://github.com/redisearch/redisearch/issues/2099) #[2101](https://github.com/redisearch/redisearch/issues/2101) Fixes possible crash with CRDT on `FT.DROPINDEX`\n    - #[1994](https://github.com/redisearch/redisearch/issues/1994) Skip intersect iterator qsort if `INORDER` flag is used \n    - #[257](https://github.com/redisearch/rscoordinator/issues/257) Switch coordinator to send _FT.CURSOR instead FT.CURSOR to prevent data access without holding the lock"},{"id":"v209-may-2021","title":"v2.0.9 (May 2021)","role":"content","text":"This is a maintenance release for version 2.0.\n\nDetails:\n\n- Bug fix in RSCoordinator:\n    - #[259](https://github.com/RediSearch/RSCoordinator/pull/259): Fix deadlock on cursor read by performing cursor command on background thread"},{"id":"v208-may-2021","title":"v2.0.8 (May 2021)","role":"content","text":"This is a maintenance release for version 2.0.\n\nThis release fixes an important regression introduced by the 2.0 release. The payload is supposed to be returned only when the WITHPAYLOADS parameter is set.\n\nDetails:\n\n- Bug fixes:\n    - #[1959](https://github.com/redisearch/redisearch/issues/1959) Renamed parse_time() to parsetime()\n    - #[1932](https://github.com/redisearch/redisearch/issues/1932) Fixed crash resulted from LIMIT arguments\n    - #[1919](https://github.com/redisearch/redisearch/issues/1919) Prevent GC fork from crashing\n- Minor enhancements:\n    - #[1880](https://github.com/redisearch/redisearch/issues/1880) Optimisation of intersect iterator\n    - #[1914](https://github.com/redisearch/redisearch/issues/1914) Do not return payload as a field"},{"id":"v207-may-2021","title":"v2.0.7 (May 2021)","role":"content","text":"This is a maintenance release for version 2.0.\n\nDetails:\n\n- Major enhancements:\n    - #[1864](https://github.com/redisearch/redisearch/issues/1864) Improve query time by predetermining reply array length.\n    - #[1879](https://github.com/redisearch/redisearch/issues/1879) Improve loading time by calling RM_ScanKey instead of RM_Call\n- Major bug fix:\n    - #[1866](https://github.com/redisearch/redisearch/issues/1866) Fix a linking issue causing incompatibility with Redis 6.2.0.\n    - #[1842](https://github.com/redisearch/redisearch/issues/1842) #1852 Fix macOS build.\n- Minor bug fixes:\n    - #[1850](https://github.com/redisearch/redisearch/issues/1850) Fix a race condition on drop temporary index.\n    - #[1855](https://github.com/redisearch/redisearch/issues/1855) Fix a binary payload corruption.\n    - #[1876](https://github.com/redisearch/redisearch/issues/1876) Fix crash if the depth of the reply array is larger than 7.\n    - #[1843](https://github.com/redisearch/redisearch/issues/1843) #[1860](https://github.com/redisearch/redisearch/issues/1860) Fix low-level API issues."},{"id":"v206-february-2021","title":"v2.0.6 (February 2021)","role":"content","text":"This is a maintenance release for version 2.0.\n\nDetails:\n\n- Minor additions:\n    - #[1696](https://github.com/redisearch/redisearch/issues/1696) The maximum number of results produced by `FT.AGGREGATE` is now configurable: `MAXAGGREGATERESULTS`.\n    - #[1708](https://github.com/redisearch/redisearch/issues/1708) [Stemming](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/stemming) updated with support of new languages: Basque, Catalan, Greek, Indonesian, Irish, Lithuanian, Nepali.\n- Minor bugfixes:\n    - #[1668](https://github.com/redisearch/redisearch/issues/1668) Fixes support of stop words in [tag fields](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/tags). Solves also the following related issues: #[166](https://github.com/redisearch/redisearch/issues/166),  #[984](https://github.com/redisearch/redisearch/issues/984), #[1237](https://github.com/redisearch/redisearch/issues/1237), #[1294](https://github.com/redisearch/redisearch/issues/1294).\n    - #[1689](https://github.com/redisearch/redisearch/issues/1689) Consistency fix and performance improvement when using `FT.SUGGET` with [RSCoordinator](https://github.com/RediSearch/RSCoordinator).\n    - #[1774](https://github.com/redisearch/redisearch/issues/1774) `MINPREFIX` and `MAXFILTEREXPANSION` configuration options can be changed at runtime.\n    - #[1745](https://github.com/redisearch/redisearch/issues/1745) Enforce 0 value for `REDUCER COUNT`.\n    - #[1757](https://github.com/redisearch/redisearch/issues/1757) Return an error when reaching the maximum number of sortable fields, instead of crashing.\n    - #[1762](https://github.com/redisearch/redisearch/issues/1762) Align the maximum number of sortable fields with the maximum number of fields (1024)"},{"id":"v205-december-2020","title":"v2.0.5 (December 2020)","role":"content","text":"This is a maintenance release for version 2.0.\n\nDetails:\n\n- Minor features:\n    - [#1696](https://github.com/RediSearch/RediSearch/pull/1696) Add `MAXAGGREGATERESULTS` module configuration for `FT.AGGREGATE`. Similar to `MAXSEARCHRESULTS` for `FT.SEARCH`, it limits the maximum number of results returned."},{"id":"v204-december-2020","title":"v2.0.4 (December 2020)","role":"content","text":"This is a maintenance release for version 2.0.\n\nDetails:\n\n- Bugfixes in RediSearch:\n    - [#1668](https://github.com/RediSearch/RediSearch/pull/1668) Stopwords are not filtered out on tag fields.\n- Bugfixes in RSCoordinator:\n    - [#206](https://github.com/RediSearch/RediSearch/pull/206) `FT.AGGREGATE` with `LIMIT` and `offset` greater than `0` returned fewer results than requested."},{"id":"v203-november-2020","title":"v2.0.3 (November 2020)","role":"content","text":"This is a maintenance release for version 2.0.\n\nMinor bugfixes:\n\n- Added [`OSS_GLOBAL_PASSWORD`](https://github.com/RediSearch/RSCoordinator#running-rscoordinator) config argument to allow specify shards password on OSS cluster.\n- Update `min_redis_pack_version` to 6.0.8"},{"id":"v202-november-2020","title":"v2.0.2 (November 2020)","role":"content","text":"This is a maintenance release for version 2.0.\n\n- Minor enhancements:\n\n    - [#1625](https://github.com/RediSearch/RediSearch/pull/1625) MAXPREFIXEXPANSIONS configuration which replaces the now deprecated config MAXEXPANSIONS. Same behaviour, more declarative naming.\n    - [#1614](https://github.com/RediSearch/RediSearch/pull/1614) Prevent multiple sortby steps on FT.AGGREGATE\n\n- Minor bug fixes:\n\n    - [#1605](https://github.com/RediSearch/RediSearch/pull/1605) Rare bug where identical results would get a lower score"},{"id":"v201-october-2020","title":"v2.0.1 (October 2020)","role":"content","text":"This is a maintenance release for version 2.0.\n\n- Minor additions:\n\n    - [#1432](https://github.com/RediSearch/RediSearch/pull/1432) FT.AGGREGATE allows filtering by key and returning the key by using LOAD \"@__key\" .\n\n- Minor bug fixes:\n\n    - [#1571](https://github.com/RediSearch/RediSearch/pull/1571) Using FILTER in FT.CREATE might cause index to not be in sync with data.\n    - [#1572](https://github.com/RediSearch/RediSearch/pull/1572) Crash when using WITHSORTKEYS without SORTBY.\n    - [#1540](https://github.com/RediSearch/RediSearch/pull/1540) SORTBY should raise an error if the field is not defined as SORTABLE."},{"id":"v200-september-2020","title":"v2.0.0 (September 2020)","role":"content","text":"RediSearch 2.0 is a public preview release meeting GA standards. This release includes several improvements in performance and usability over RediSearch 1.0. These improvements necessitate a few backward-breaking changes to the API."},{"id":"highlights","title":"Highlights","role":"content","text":"For this release, we changed the way in which the search indexes are kept in sync with your data. In RediSearch 1.x, you had to manually add data to your indexes using the `FT.ADD` command. In RediSearch 2.x, your data is indexed automatically based on a key pattern.\n\nThese changes are designed to enhance developer productivity, and to ensure that\nyour search indexes are always kept in sync with your data. To support this, we've\nmade a few changes to the API.\n\nIn addition to simplifying indexing, RediSearch 2.0 allows you to scale a single index over multiple Redis shards using the Redis cluster API.\n\nFinally, RediSearch 2.x keeps its indexes outside of the main Redis key space. Improvements to the indexing code have increased query performance 2.4x.\n\nYou can read more details in [the RediSearch 2.0 announcement blog post](https://redislabs.com/blog/introducing-redisearch-2-0/), and you can get started by checking out this [quick start blog post](https://redislabs.com/blog/getting-started-with-redisearch-2-0/).\n\n![Compares the architecture of RediSearch 2.0 to architecture of earlier versions.](https://redis.io/docs/latest/images/modules/redisearch-2-0-architecture.png)"},{"id":"details","title":"Details","role":"content","text":"- When you create an index, you must specify a prefix condition and/or a filter. This determines which hashes RediSearch will index.\n- Several RediSearch commands now map to their Redis equivalents: `FT.ADD` -> `HSET`, `FT.DEL` -> `DEL` (equivalent to `FT.DEL` with the DD flag in RediSearch 1.x), `FT.GET` -> `HGETALL`, `FT.MGET` -> `HGETALL`.\n- RediSearch indexes no longer reside within the key space, and the indexes are no longer saved to the RDB.\n- You can upgrade from RediSearch 1.x to RediSearch 2.x."},{"id":"noteworthy-changes","title":"Noteworthy changes","role":"content","text":"- [#1246](https://github.com/RediSearch/RediSearch/pull/1246): `geodistance` function for `FT.AGGREGATE` APPLY operation.\n- [#1394](https://github.com/RediSearch/RediSearch/pull/1394): Expired documents (TTL) will be removed from the index.\n- [#1394](https://github.com/RediSearch/RediSearch/pull/1394): Optimization to avoid reindexing documents when non-indexed fields are updated.\n- After index creation, an initial scan starts for existing documents.  You can check the status of this scan by calling `FT.INFO` and looking at the `indexing` and `percent_indexed` values.  While `indexing` is true, queries return partial results.\n- [#1435](https://github.com/RediSearch/RediSearch/pull/1435): `NOINITIALINDEX` flag on `FT.CREATE` to skip the initial scan of documents on index creation.\n- [#1401](https://github.com/RediSearch/RediSearch/pull/1401): Support upgrade from v1.x and for reading RDB's created by RediSearch 1.x.\n- [#1445](https://github.com/RediSearch/RediSearch/pull/1445): Support for load event. This event indexes documents when they are loaded from RDB, ensuring that indexes are fully available when RDB loading is complete (available from Redis 6.0.7 and above).\n- [#1384](https://github.com/RediSearch/RediSearch/pull/1384): `FT.DROPINDEX`, which by default does not delete documents underlying the index (see deprecated `FT.DROP`).\n- [#1385](https://github.com/RediSearch/RediSearch/pull/1385): Add index definition to `FT.INFO` response.\n- [#1097](https://github.com/RediSearch/RediSearch/pull/1097): Add Hindi snowball stemmer.\n- The `FT._LIST` command returns a list of all available indices.  Note that this is a temporary command, as indicated by the `_` in the name, so it's not documented. We're working on a [`SCAN`](/commands/scan)-like command for databases with many indexes.\n- The RediSearch version will appear in Redis as `20000`, which is equivalent to 2.0.0 in semantic versioning. Since the version of a module in Redis is numeric, we cannot explicitly add an GA flag.\n- RediSearch 2.x requires Redis 6.0 or later."},{"id":"behavior-changes","title":"Behavior changes","role":"content","text":"Make sure you review these changes before upgrading to RediSearch 2.0:\n\n- [#1381](https://github.com/RediSearch/RediSearch/pull/1381): `FT.SYNADD` is removed; use `FT.SYNUPDATE` instead. `FT.SYNUPDATE` requires both\nand index name and a synonym group ID. This ID can be any ASCII string.\n- [#1437](https://github.com/RediSearch/RediSearch/pull/1437): Documents that expire during query execution time will not appear in the results (but might have been counted in the number of produced documents).\n- [#1221](https://github.com/RediSearch/RediSearch/pull/1221): Synonyms support for lower case. This can result in a different result set on FT.SEARCH when using synonyms.\n- RediSearch will not index hashes whose fields do not match an existing index schema. You can see the number of hashes not indexed using `FT.INFO` - `hash_indexing_failures `.  The requirement for adding support for partially indexing and blocking is captured here: [#1455](https://github.com/RediSearch/RediSearch/pull/1455).\n- Removed support for `NOSAVE`.\n- RDB loading will take longer due to the index not being persisted.\n- Field names in the [query syntax](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/query_syntax) are now case-sensitive.\n- Deprecated commands:\n    - `FT.DROP` (replaced by `FT.DROPINDEX`, which by default keeps the documents)\n    - `FT.ADD` (mapped to `HSET` for backward compatibility)\n    - `FT.DEL` (mapped to `DEL` for backward compatibility)\n    - `FT.GET` (mapped to `HGETALL` for backward compatibility)\n    - `FT.MGET` (mapped to `HGETALL` for backward compatibility)\n- Removed commands:\n    - `FT.ADDHASH` (no longer makes sense)\n    - `FT.SYNADD` (see [#1381](https://github.com/RediSearch/RediSearch/pull/1381))\n    - `FT.OPTIMIZE`"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes","title":"RediSearch 2.10 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes/","summary":"New vector data types. Enhanced indexing capabilities. Simplified query syntax. Expanded geospatial search.","tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:33+01:00","page_type":"content","content_hash":"7087b16c9f3c3962faf3e75bc7d6c17c6811588398b413b0725bce11d95083ea","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RediSearch v2.10.31 requires:\n\n- Minimum Redis compatibility version (database): 7.4\n- Minimum Redis Enterprise Software version (cluster): 7.8"},{"id":"v21031-june-2026","title":"v2.10.31 (June 2026)","role":"content","text":"This is a maintenance release for Redis Search 2.10.\nUpdate urgency: `LOW`: No need to upgrade unless there are new features you want to use.\n\n**Bug Fixes**\n\n- [#9938](https://github.com/redisearch/redisearch/pull/9938) Server can crash in fork GC when the last document with an empty TAG value is deleted from an INDEXEMPTY WITHSUFFIXTRIE field. (MOD-15996) \n- [#10104](https://github.com/redisearch/redisearch/pull/10104) FT.SEARCH and FT.AGGREGATE return Unknown argument errors on 2.10 shards during rolling upgrades when a newer coordinator injects internal query arguments. (MOD-16047) \n- [#9578](https://github.com/redisearch/redisearch/pull/9578) Server can crash when FT.CREATE is called with an extremely large number of arguments or fields. (MOD-6411) \n- [#9810](https://github.com/redisearch/redisearch/pull/9810) FT.SYNUPDATE leaves the synonym map partially mutated when an update batch exceeds synonym or group-ID limits. (MOD-15402) \n- [#9533](https://github.com/redisearch/redisearch/pull/9533) FT.INFO num_records grows without bound for indexes with vector fields because vector records were counted on insert but never decremented. (MOD-15487)"},{"id":"v21030-may-2026","title":"v2.10.30 (May 2026)","role":"content","text":"This is a maintenance release for Redis Search 2.10. \n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nBug fixes:\n- [#Q8948](https://github.com/redisearch/redisearch/pull/8948) `FT.CURSOR READ` enters an infinite loop when the caller lacks the required ACL permissions. (MOD-14479)\n- [#Q8794](https://github.com/redisearch/redisearch/pull/8794) `FT.EXPLAIN` crashes or produces corrupt output when a concurrent schema change occurs. (MOD-14461)\n- [#Q9176](https://github.com/redisearch/redisearch/pull/9176), [#Q9314](https://github.com/redisearch/redisearch/pull/9314) Coordinator deadlock under mixed `FT.SEARCH` and `FT.AGGREGATE` load. (MOD-14268)\n- [#Q9273](https://github.com/redisearch/redisearch/pull/9273), [#Q9302](https://github.com/redisearch/redisearch/pull/9302) Async shard connect hangs indefinitely when a peer endpoint is unreachable; the connect is now bounded by search-connect-timeout. (MOD-12739)\n- [#Q9167](https://github.com/redisearch/redisearch/pull/9167) Crash on `FT.SEARCH` when topology validation fails (for example, some nodes unreachable). (MOD-14475)\n- [#Q9082](https://github.com/redisearch/redisearch/pull/9082) `FT.SPELLCHECK` ignores $param placeholders even when `PARAMS` is supplied. (MOD-10596)\n- [#Q9050](https://github.com/redisearch/redisearch/pull/9050) `FT.PROFILE` produces malformed output for documents with missing field values. (MOD-10560)\n- [#Q9075](https://github.com/redisearch/redisearch/pull/9075) Invalid schema option combinations are silently accepted at `FT.CREATE` time. (MOD-14655)\n- [#Q9436](https://github.com/redisearch/redisearch/pull/9436) Malformed index definitions in an RDB file crash the shard or load an invalid index. (MOD-13118)\n- [#Q7686](https://github.com/redisearch/redisearch/pull/7686) Cursors are not released on certain error paths, leaking cursor slots over time. (MOD-12807)\n- [#Q8465](https://github.com/redisearch/redisearch/pull/8465) Garbage collector mishandles out-of-memory responses on replicas, leaving the index inconsistent. (MOD-14066)\n- [#Q7771](https://github.com/redisearch/redisearch/pull/7771) String comparisons truncate at embedded `NULL` bytes, returning wrong results for TAG and binary fields. (MOD-13010)\n- [#Q7851](https://github.com/redisearch/redisearch/pull/7851) `FT.SUG*` commands are not registered on Redis Enterprise builds. (MOD-13152)\n- [#Q8154](https://github.com/redisearch/redisearch/pull/8154) Per-shard total time in cluster `FT.PROFILE` output is reported incorrectly. (MOD-13735, MOD-13181)\n- [#Q8084](https://github.com/redisearch/redisearch/pull/8084) `FULLTEXT` field count in `FT.INFO` statistics is inaccurate. (MOD-13432)\n\nMetrics:\n- [#Q7558](https://github.com/redisearch/redisearch/pull/7558), [#Q7688](https://github.com/redisearch/redisearch/pull/7688), [#Q7723](https://github.com/redisearch/redisearch/pull/7723), [#Q7791](https://github.com/redisearch/redisearch/pull/7791), [#Q7749](https://github.com/redisearch/redisearch/pull/7749), [#Q7738](https://github.com/redisearch/redisearch/pull/7738), [#Q7744](https://github.com/redisearch/redisearch/pull/7744) New `FT.INFO` metrics expose live thread-pool utilization and pending-job depth for worker, coordinator, IO, and topology-update pools (`search_active_io_threads`, `search_active_worker_threads`, `search_active_coord_threads`, `search_active_topology_update_threads`, `search_workers_low_priority_pending_jobs`, `search_workers_high_priority_pending_jobs`, `search_workers_admin_priority_pending_jobs`, `search_coord_high_priority_pending_jobs`). (MOD-12069, MOD-12695, MOD-12694, MOD-12790, MOD-12791)\n- [#Q7793](https://github.com/redisearch/redisearch/pull/7793), [#Q7816](https://github.com/redisearch/redisearch/pull/7816) New `FT.INFO` counters track per-shard and coordinator query timeouts and `MAXPREFIXEXPANSIONS` truncations (`search_shard_total_query_errors_timeout`, `search_shard_total_query_warnings_timeout`, `search_shard_total_query_warnings_max_prefix_expansions`, and equivalent `search_coord_total_* counters`). (MOD-12419, MOD-12417)\n- [#Q7766](https://github.com/redisearch/redisearch/pull/7766) New `FT.INFO` per-field-type indexing-operation counters added for `TEXT`, `TAG`, `NUMERIC`, `GEO`, `GEOSHAPE`, and `VECTOR fields`. (MOD-12070)\n- [#Q7746](https://github.com/redisearch/redisearch/pull/7746) New Internal cursor reads counter in cluster `FT.PROFILE` output surfaces internal cursor-hop round-trips per query. (MOD-12414)\n- [#Q7593](https://github.com/redisearch/redisearch/pull/7593) FT.PROFILE now reports GIL time, giving an accurate breakdown of where query time is spent. (MOD-11987, MOD-12816)"},{"id":"v21025-december-2025","title":"v2.10.25 (December 2025)","role":"content","text":"This is a maintenance release for RediSearch 2.10.\n\nUpdate urgency: `LOW`: No need to upgrade unless there are new features you want to use.\n\nImprovements:\n\n- [#7157](https://github.com/RediSearch/RediSearch/pull/7157) Show background Indexing OOM warning in FT.AGGREGATE reply in RESP3.\n- [#7086](https://github.com/RediSearch/RediSearch/pull/7086) Add the default text scorer as a new configuration.\n- [#7380](https://github.com/RediSearch/RediSearch/pull/7380) Rename FT.PROFILE counter fields.\n- [#7366](https://github.com/RediSearch/RediSearch/pull/7366) Reduce temporary memory overhead upon index load from RDB.\n- [#7393](https://github.com/RediSearch/RediSearch/pull/7393) Improve timeout check for FT.AGGREGATE in cluster mode.\n- [#7451](https://github.com/RediSearch/RediSearch/pull/7451) Enhance FT.PROFILE with vector search execution details.\n- [#7480](https://github.com/RediSearch/RediSearch/pull/7480) Ensure full profile output on timeout with RETURN policy.\n\nBug Fixes:\n\n- [#7216](https://github.com/RediSearch/RediSearch/pull/7216) Fix a concurrency issue on Reducer in FT.AGGREGATE.\n- [#7259](https://github.com/RediSearch/RediSearch/pull/7259) Fix underflow in BM25STD.\n- [#7278](https://github.com/RediSearch/RediSearch/pull/7278) Report used memory as unsigned long long to avoid underflows.\n- [#7340](https://github.com/RediSearch/RediSearch/pull/7340) Fix a rare leak in GC.\n- [#7462](https://github.com/RediSearch/RediSearch/pull/7462) Fix Fork GC potential double-free on error path.\n- [#7525](https://github.com/RediSearch/RediSearch/pull/7525) Avoid draining workers thread pool from FLUSHDB callback to avoid potential deadlocks.\n\n\nFull Changelog: https://github.com/RediSearch/RediSearch/compare/v2.10.24...v2.10.25"},{"id":"v21024-october-2025","title":"v2.10.24 (October 2025)","role":"content","text":"This is a maintenance release for RediSearch 2.10.\n\nUpdate urgency: `LOW`: No need to upgrade unless there are new features you want to use.\n\nBug Fixes\n- [#6960](https://github.com/redisearch/redisearch/pull/6960) - FT.INFO returns the wrong number of documents in OSS Cluster with replicas.\n- [#6938](https://github.com/redisearch/redisearch/pull/6938) - Fix for the HIGHLIGHT feature, where if some fields have empty strings, wrong tokens might be highlighted.\n- [#7047](https://github.com/redisearch/redisearch/pull/7047) - Avoid crashing in the FT.AGGREGATE command in clusters where different shards have different ON_TIMEOUT policies configured (fail vs return).\n\nFull Changelog: https://github.com/RediSearch/RediSearch/compare/v2.10.23...v2.10.24."},{"id":"v21023-september-2025","title":"v2.10.23 (September 2025)","role":"content","text":"This is a maintenance release for RediSearch 2.10.\n\nUpdate urgency: `HIGH` : There is a critical bug that may affect a subset of users. Upgrade!\n\nBug fixes:\n- [#6672](https://github.com/RediSearch/RediSearch/pull/6672) Fix potential file descriptor leak when OOM.\n- [#6763](https://github.com/RediSearch/RediSearch/pull/6763) Fix potential deadlock during RDB loading in cases where the `INFO` command is sent to the server.\n\nFull Changelog: https://github.com/RediSearch/RediSearch/compare/v2.10.22...v2.10.23"},{"id":"v21022-august-2025","title":"v2.10.22 (August 2025)","role":"content","text":"This is a maintenance release for RediSearch 2.10.\n\nUpdate urgency: `HIGH` : There is a critical bug that may affect a subset of users. Upgrade!\n\nBug fixes:\n- [#6600](https://github.com/redisearch/redisearch/pull/6600) `FLUSHDB` while active queries are still running could lead to a crash due to premature release of the CURSOR (MOD-10681).\n\nImprovements:\n- [#6664](https://github.com/redisearch/redisearch/pull/6664) Time measurement on `FT.PROFILE` using thread-independent clock mechanism (MOD-10622).\n- [#6647](https://github.com/redisearch/redisearch/pull/6647) Response on RESP2/3 validation was inefficiently consuming excessive CPU cycles (MOD-9687)."},{"id":"v21021-july-2025","title":"v2.10.21 (July 2025)","role":"content","text":"This is a maintenance release for RediSearch 2.10.\n\nUpdate urgency: `LOW`: No need to upgrade unless there are new features you want to use.\n\nBug fixes:\n- [#6405](https://github.com/redisearch/redisearch/pull/6405) Validate compatibility against RedisJSON version upon open key.\n\nImprovements:\n- [#6340](https://github.com/redisearch/redisearch/pull/6340) Handle excessive logging when processing JSON.DEL errors."},{"id":"v21020-june-2025","title":"v2.10.20 (June 2025)","role":"content","text":"This is a maintenance release for RediSearch 2.10.\n\nUpdate urgency: `LOW` No need to upgrade unless there are new features you want to use.\n\nImprovements:\n- [#6279](https://github.com/redisearch/redisearch/pull/6279) Added a locking mechanism for collecting `FT.INFO` statistics when concurrently running the index sanitiser (MOD-10007, MOD-9761)."},{"id":"v21019-june-2025","title":"v2.10.19 (June 2025)","role":"content","text":"This is a maintenance release for RediSearch 2.10.\n\nUpdate urgency: `LOW` No need to upgrade unless there are new features you want to use.\n\nBug fixes:\n- [#6211](https://github.com/redisearch/redisearch/pull/6211) Some languages (like Russian) could have multiple lower and upper case matches, causing index misbehaviour (MOD-9835).\n\nImprovements:\n- [#5637](https://github.com/redisearch/redisearch/pull/5637) Memory allocation when converting special UTF-8 symbols requires more memory (MOD-8799)."},{"id":"v21018-may-2025","title":"v2.10.18 (May 2025)","role":"content","text":"This is a maintenance release for RediSearch 2.10.\n\nUpdate urgency: `HIGH` : There is a critical bug that may affect a subset of users. Upgrade!\n\nBug fixes:\n- [#6184](https://github.com/redisearch/redisearch/pull/6184) Reindexing from RDB with multiple vector indices could lead to a crash due to cluster health check - NodeWD (MOD-9220,MOD-8809)\n- [#6028](https://github.com/redisearch/redisearch/pull/6028) `FT.CURSOR...DEL` while another thread is reading it could lead to a crash (MOD-9408,MOD-9432,MOD-9433,MOD-9434,MOD-9435)\n- [#5967](https://github.com/redisearch/redisearch/pull/5967) Indexing documents using `TEXT` without the text in the documents leads to an `inf` or `nan` score (MOD-9423)\n- [#6056](https://github.com/redisearch/redisearch/pull/6056) Avoid lazy expiration in background indexing for Active-Active setup preventing keys from expiring incorrectly (MOD-9486)\n- [#6108](https://github.com/redisearch/redisearch/pull/6108) A timeout failure is returned when the `ON_TIMEOUT RETURN` policy is set to stop the collection of partial results - best effort (MOD-9612)\n\nImprovements:\n- [#6007](https://github.com/redisearch/redisearch/pull/6007) Parser for intersections on parentheses and sub-queries order won't affect full-text scores (MOD-9278)\n- [#6020](https://github.com/redisearch/redisearch/pull/6020) Prevent access to the Redis key space when `LOAD...@__key` is used (MOD-9419)"},{"id":"v21017-april-2025","title":"v2.10.17 (April 2025)","role":"content","text":"This is a maintenance release for RediSearch 2.10.\n\nUpdate urgency: `HIGH` : There is a critical bug that may affect a subset of users. Upgrade!\n\nBug fixes:\n- [#5859](https://github.com/redisearch/redisearch/pull/5859) Last query result using `FT.AGGREGATE` with `ON_TIMEOUT RETURN` and using multi-threading could be missing (MOD-9222)\n- [#5858](https://github.com/redisearch/redisearch/pull/5858) Collecting empty results from shards during `FT.AGGREGATE` with RESP3 could cause a crash (MOD-9174)\n\nImprovements:\n- [#5938](https://github.com/redisearch/redisearch/pull/5938) Improved performance (reduced CPU time) of collecting vector index statistics(MOD-9354)\n- [#5800](https://github.com/redisearch/redisearch/pull/5800) Improved accuracy of index memory reporting by correcting a bug that caused negative memory counts (MOD-5904)"},{"id":"v21015-march-2025","title":"v2.10.15 (March 2025)","role":"content","text":"This is a maintenance release for RediSearch 2.10.\n\nUpdate urgency: `HIGH` : There is a critical bug that may affect a subset of users. Upgrade!\n\nBug fixes:\n- [#5788](https://github.com/redisearch/redisearch/pull/5788) Cursor with `SORTBY` is never depleted, blocking queries if the cursor limit is reached (MOD-8483)\n- [#5788](https://github.com/redisearch/redisearch/pull/5788) Empty results with RESP3 due to the `TIMEOUT`, even if `ON_TIMEOUT` is set to `RETURN`(MOD-8482)\n- [#5788](https://github.com/redisearch/redisearch/pull/5788) Cursor with RESP3 on `FT.AGGREGATE` is never depleted, blocking queries if cursor the limit is achieved (MOD-8515)\n- [#5788](https://github.com/redisearch/redisearch/pull/5788) Using `FT.CURSOR READ` on queries that timed out led to fewer results than expected (MOD-8606)\n- [#5810](https://github.com/redisearch/redisearch/pull/5810) The `total_results` field of the `FT.AGGREGATE` command is not correct in RESP3 (MOD-9054)\n\nImprovements:\n- [#5788](https://github.com/redisearch/redisearch/pull/5788) Corrected a coordinator race condition that prevented premature release, avoiding errors and inconsistencies during query executions (MOD-8794)"},{"id":"v21014-march-2025","title":"v2.10.14 (March 2025)","role":"content","text":"This is a maintenance release for RediSearch 2.10.\n\nUpdate urgency: `LOW` No need to upgrade unless there are new features you want to use.\n\nBug fixes:\n- [#5704](https://github.com/redisearch/redisearch/pull/5704) Weights in the query string are ignored if using `SCORER BM25` (MOD-7896)."},{"id":"v21013-february-2025","title":"v2.10.13 (February 2025)","role":"content","text":"This is a maintenance release for RediSearch 2.10.\n\nUpdate urgency: `LOW` No need to upgrade unless there are new features you want to use.\n\nBug fixes:\n- [#5646](https://github.com/redisearch/redisearch/pull/5646) `FT.SEARCH` using Cyrillic characters and wildcards delivering no results (MOD-7944)"},{"id":"v21012-february-2025","title":"v2.10.12 (February 2025)","role":"content","text":"This is a maintenance release for RediSearch 2.10.\n\nUpdate urgency: `HIGH` : There is a critical bug that may affect a subset of users. Upgrade!\n\nBug fixes:\n- [#5596](https://github.com/redisearch/redisearch/pull/5596) Changes on the memory block reading logic could cause crash on `FT.SEARCH` with error \"_Redis 7.4.2 crashed by signal: 11, si_code: 128\"_\n\nKnown limitations:\n- Only the first 128 characters of string fields are normalized to lowercase during ingestion (for example, on `HSET`).\n    Example:\n\n    [code example]\n\n    The `FT.SEARCH` command will return no documents."},{"id":"v21011-january-2025","title":"v2.10.11 (January 2025)","role":"content","text":"This is a maintenance release for RediSearch 2.10.\n\nUpdate urgency: `HIGH` : There is a critical bug that may affect a subset of users. Upgrade!\n\nBug fixes:\n- [#5536](https://github.com/redisearch/redisearch/pull/5536) Querying for the latest document added to the index may result in a crash if the last block is not read (MOD-8561)."},{"id":"v21010-january-2025","title":"v2.10.10 (January 2025)","role":"content","text":"This is a maintenance release for RediSearch 2.10\n\nUpdate urgency: `SECURITY`: There are security fixes in the release.\n\n**Security and privacy**:\n- [#5459](https://github.com/redisearch/redisearch/pull/5459) (CVE-2024-51737) Query: potential out-of-bounds write (MOD-8486)\n\nBug fixes:\n- [#5392](https://github.com/redisearch/redisearch/pull/5392) `NOSTEM` option does not work on query, just on tokenisation (index creation) (MOD-7634)\n- [#5300](https://github.com/redisearch/redisearch/pull/5300) Prefix/Infix/Suffix queries longer than 1024 chars could cause a crash (MOD-7882)\n- [#5294](https://github.com/redisearch/redisearch/pull/5294) Expired keys while background indexing could cause cross slot error when using `replicaof` (MOD-7949)\n- [#5282](https://github.com/redisearch/redisearch/pull/5282) `FT.CURSOR READ` retrieving deleted `TAG` fields cause a crash (MOD-8011)\n- [#5424](https://github.com/redisearch/redisearch/pull/5424) `FT.AGGREGATE` on numeric fields lead to `failed_calls` count increase on clustered DBs (MOD-8058)\n- [#5241](https://github.com/redisearch/redisearch/pull/5241) Memory count on `bytes_collected` by the index sanitiser with missing values (MOD-8097, MOD-8114)\n\nImprovements:\n- [#5257](https://github.com/redisearch/redisearch/pull/5257) Optimizing index consumed memory with the creation only upon write operations (MOD-8125)"},{"id":"v2107-septermber-2024","title":"v2.10.7 (Septermber 2024)","role":"content","text":"This is a maintenance release for RediSearch 2.10\n\nUpdate urgency: `HIGH` : There is a critical bug that may affect a subset of users. Upgrade!\n\nBug Fixes:\n- https://github.com/RediSearch/RediSearch/pull/4941 Adjusting the module configuration to avoid routing overload on the first shard in a clustered database (MOD-7505)\n- https://github.com/RediSearch/RediSearch/pull/4950 `FT.PROFILE` on `AGGREGATE` numeric queries could cause a crash due to reuse of an internal CURSOR in a large range of numeric values (MOD-7454)"},{"id":"v2106-august-2024","title":"v2.10.6 (August 2024):","role":"content","text":"This is a maintenance release for RediSearch 2.10.\n\nUpdate urgency: `HIGH` - There is a critical bug that may affect a subset of users. Upgrade!\n\nBug fixes:\n- [#4916](https://github.com/redisearch/redisearch/pull/4916) - Union query, similar to `\"is|the\"`, starting with 2 [storwords](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/stopwords/) could cause a crash (MOD-7495)\n- [#4895](https://github.com/redisearch/redisearch/pull/4895) - `FT.AGGREGATE` with `VERBATIM` option is not handled by the shards in cluster mode (MOD-7463) \n- [#4922](https://github.com/redisearch/redisearch/pull/4922) - Counting twice the field statistics at `#search` section of `INFO` response (MOD-7339)"},{"id":"v210-ga-v2105-july-2024","title":"v2.10 GA (v2.10.5) (July 2024)","role":"content","text":"This is the General Availability release of RediSearch 2.10"},{"id":"headlines","title":"Headlines:","role":"content","text":"This latest RediSearch introduces memory-efficient vector data types, enhanced indexing capabilities with support for empty and missing fields, simplified query syntax, and expanded geospatial search features."},{"id":"whats-new-in-2105","title":"What's new in 2.10.5","role":"content","text":"This new major version introduces new `BFLOAT16` and `FLOAT16` vector data types, reducing memory consumed by vectors while preserving accuracy. This update also includes highly-requested support for indexing empty and missing values and enhances the developer experience for queries with exact matching capabilities. Developers can now match `TAG` fields without needing to escape special characters, making the onboarding process and use of the query syntax easier. Lastly, Geospatial search capabilities have been expanded with new `INTERSECT` and `DISJOINT` operators, and ergonomics have been improved by providing better reporting of the memory consumed by the index and exposing the full-text scoring in an aggregation pipeline.\n\nFeatures:\n\n- Enhancing exact matching queries with `TAG` avoiding escaping special meaning characters using the [simpler syntax](https://redis.io/docs/latest/develop/ai/search-and-query/query/exact-match/#tag-field) `'@tag:{\"my-query%term\"}'` and `NUMERIC` queries:\n  - [#4802](https://github.com/RediSearch/RediSearch/pull/4802) - Using double quotes to wrap exact matching query terms such as `@email:{\"test@redis.com\"}` in `DIALECT 2` ( MOD-7299)\n  - [#4676](https://github.com/RediSearch/RediSearch/pull/4676), [#4433](https://github.com/RediSearch/RediSearch/pull/4433) - Enhancing query parser to avoid unnecessary escaping (MOD-5756)\n  - [#4527](https://github.com/RediSearch/RediSearch/pull/4527) - Enhancing exact matching queries for `NUMERIC` using single value `FT.SEARCH idx @numeric:[3456]` (MOD-6623)\n  - [#4802](https://github.com/RediSearch/RediSearch/pull/4802) - Enabling support for single operators in `NUMERIC` queries such as equivalence `==`, difference `!=`, greater than `>` and `>=` and less than `<` and `<=` as in `FT.SEARCH idx '@numeric==3456'` (MOD-6749)\n\n- Adding new keywords to support indexing empty values using `INDEXEMPTY` and missing values using `INDEXMISSING` per field in the `SCHEMA` while defining the [index](https://redis.io/docs/latest/commands/ft.create/) with `FT.CREATE`\n  - [#4663](https://github.com/RediSearch/RediSearch/pull/4663), [#4721](https://github.com/RediSearch/RediSearch/pull/4721) - Indexing empty strings values `\"\"` for `TAG` and `TEXT` fields (MOD-6540, MOD-7200)\n  - [#4721](https://github.com/RediSearch/RediSearch/pull/4721) - Updating the query parser to support empty value queries for `TEXT` as `FT.SEARCH idx '@text_field:\"\"'`or `FT.SEARCH idx '\"\"'` and for `TAG` as in `FT.SEARCH idx '@tag_field:{\"\"}'` (MOD-7200)\n  - [#4720](https://github.com/RediSearch/RediSearch/pull/4720), [#4635](https://github.com/RediSearch/RediSearch/pull/4635) - Indexing missing values for all field types introducing the query syntax function `ismissing(@field)` enabling query for missing fields as in `FT.SEARCH idx 'ismissing(@text)'` (MOD-6532)\n\n\n- Enabling new vector data types reducing memory consumed by vectors with the new `BFLOAT16` and `FLOAT16`\n  - [#4674](https://github.com/RediSearch/RediSearch/pull/4674) - Adding support `BFLOAT16` and `FLOAT16` in the [vector index definition](https://redis.io/docs/latest/develop/ai/search-and-query/vectors/#creation-attributes-per-algorithm) (MOD-6765, MOD-6776)\n\n- Exposing the full-text score values during the aggregation pipeline using `ADDSCORE`. When calling the scores you can use `@__score` in the pipeline as in `FT.AGGREGATE idx 'hello' ADDSCORES SORTBY 2 @__score DESC`\n  - [#4859](https://github.com/RediSearch/RediSearch/pull/4859) - Expose scores to `FT.AGGREGATE` pipeline (MOD-7190)\n\n- [#4227](https://github.com/RediSearch/RediSearch/pull/4227) - Adding support for new operators `INTERSECT` and `DISJOINT` when querying for `GEOSHAPE` [polygons](https://redis.io/docs/latest/develop/ai/search-and-query/query/geo-spatial/) (MOD-6178)\n\n\nBug fixes (since 2.10.4):\n- [#4854](https://github.com/RediSearch/RediSearch/pull/4854) - Avoid expansion on stemming from numeric values (MOD-7025)\n\nImprovements (since 2.10.4):\n- [#4865](https://github.com/RediSearch/RediSearch/pull/4865) - Add coverage for cleaning garbage entries when indexing missing fields (MOD-7415)\n\n> [!NOTE]\n> - The version inside Redis will be 2.10.5 in semantic versioning. Since the version of a module in Redis is numeric, we could not add a Release Candidate flag.\n> - Minimal Redis version: 7.4\n> - If indexing and querying RedisJSON data structures, this version is best combined with RedisJSON 2.8 (v2.8.2 onwards)\n> - If one or more fields of a hash key expire after a query begins (using FT.SEARCH or FT.AGGREGATE), Redis does not account for these lazily expired fields. As a result, keys with expired fields may still be included in the query results, leading to potentially incorrect or inconsistent results."}],"examples":[{"id":"v21012-february-2025-ex0","language":"plaintext","code":"HSET doc __score 1.0 name \"idx1S...S\" mynum 1          # Assume \"S...S\" is a string of 252 capital S's\n    FT.CREATE \"idx\" SCHEMA \"name\" \"TEXT\" \"mynum\" \"NUMERIC\"\n    FT.SEARCH \"idx\" \"@name:idx1S...S\"                      # Assume \"S...S\" is a string of 252 capital S's","section_id":"v21012-february-2025"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.2-release-notes","title":"RediSearch 2.2 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.2-release-notes/","summary":"Search and index JSON documents. Profiling queries. Field aliasing.","tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:33+01:00","page_type":"content","content_hash":"5f2e3708abf8914cfe1615916cc079f79430841600d762abda8423fd087cb861","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RediSearch v2.2.10 requires:\n\n- Minimum Redis compatibility version (database): 6.0.0\n- Minimum Redis Enterprise Software version (cluster): 6.0.0"},{"id":"v2210-march-2022","title":"v2.2.10 (March 2022)","role":"content","text":"This is a maintenance release for RediSearch 2.2.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Improvements:\n\n  - Mac M1 support\n  - [#2645](https://github.com/RediSearch/RediSearch/pull/2645) Ignore `NULL` values in JSON documents on indexing (prior behaviour would ignore the entire document)\n  - [#2623](https://github.com/RediSearch/RediSearch/pull/2623) Improved Multi Sortby error message for `FT.SEARCH`\n\n- Bug fixes:\n\n  - [#2641](https://github.com/RediSearch/RediSearch/pull/2641) Memory leak in Coordinator\n  - [#2651](https://github.com/RediSearch/RediSearch/pull/2651) Client freeze on docs expire during query\n  - [#2670](https://github.com/RediSearch/RediSearch/pull/2670) Memory leak in RediSearch found in Active-Active (MOD-2518)"},{"id":"v229-march-2022","title":"v2.2.9 (March 2022)","role":"content","text":"This is a maintenance release for RediSearch 2.2.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Improvements:\n\n  - [#2605](https://github.com/RediSearch/RediSearch/pull/2605) Added support for `tls-key-file-pass` capability (MOD-2086)\n  - [#2583](https://github.com/RediSearch/RediSearch/pull/2583) Release index-specific information off the main thread (performance enhancement)\n\n- Bug fixes:\n\n  - [#2436](https://github.com/RediSearch/RediSearch/pull/2436) When indexing JSON documents, filters cause no documents to be indexed (MOD-2214)\n  - [#2507](https://github.com/RediSearch/RediSearch/pull/2507) `QUANTILE` aggregation function outputting wrong values (MOD-2432)\n  - [#2521](https://github.com/RediSearch/RediSearch/pull/2521) `contains()` with an empty string argument leaves Redis hanging at CPU 100% indefinitely (MOD-2428)\n  - [#2560](https://github.com/RediSearch/RediSearch/pull/2560) Free prefix and cursor efficiently for cases with many indices (MOD-2080)\n  - [#2541](https://github.com/RediSearch/RediSearch/pull/2541) Numeric types for `FT.INFO` on coordinator\n  - [#2553](https://github.com/RediSearch/RediSearch/pull/2553) Fix union high iterator\n  - [#2404](https://github.com/RediSearch/RediSearch/pull/2404) Update coordination strategy of `FlatSearchCommandHandler`"},{"id":"v227-february-2022","title":"v2.2.7 (February 2022)","role":"content","text":"This is a maintenance release for RediSearch 2.2.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n  - [#2466](https://github.com/RediSearch/RediSearch/pull/2466) `FT.PROFILE` for `FT.AGGREGATE` on clustered databases\n  - [#2473](https://github.com/RediSearch/RediSearch/pull/2473) `FT.PROFILE` with no result processor present\n  - [#2490](https://github.com/RediSearch/RediSearch/pull/2490) Case sensitivity issue in searches of TAG field on JSON\n\n- Improvements:\n  - [#2469](https://github.com/RediSearch/RediSearch/pull/2469) Add API for TAG children query nodes"},{"id":"v226-january-2022","title":"v2.2.6 (January 2022)","role":"content","text":"This is a maintenance release for RediSearch 2.2.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n    - [#2362](https://github.com/RediSearch/RediSearch/pull/2362) Crash on empty field name\n    - [#2407](https://github.com/RediSearch/RediSearch/pull/2407) Inconsistency with `FT.CREATE` and `ON`: return error for spec without fields parameters\n    - [#2392](https://github.com/RediSearch/RediSearch/pull/2392) Remove limit on Geo field precision\n    - [#2440](https://github.com/RediSearch/RediSearch/pull/2440) Fix in NOT(-) iterator\n    - [#2414](https://github.com/RediSearch/RediSearch/pull/2414) Prevent server freeze when `FT.SEARCH` timeout while sorting\n    - [#2386](https://github.com/RediSearch/RediSearch/pull/2386) Memory leak in numeric field index\n\n- Improvements:\n\n    - [#2424](https://github.com/RediSearch/RediSearch/pull/2424), [#2425](https://github.com/RediSearch/RediSearch/pull/2425) Performance improvements for numeric indices\n    - [#2408](https://github.com/RediSearch/RediSearch/pull/2408) API: Added `RediSearch_IndexInfo`\n    - [#2448](https://github.com/RediSearch/RediSearch/pull/2448) API: Added `setLang` and `setScore`"},{"id":"v225-november-2021","title":"v2.2.5 (November 2021)","role":"content","text":"This is the General Availability release of RediSearch 2.2."},{"id":"headlines","title":"Headlines","role":"content","text":"#### Searching and indexing JSON documents\n\nThis release introduces the ability to [index, query, and full-text search JSON documents](https://redis.io/docs/latest/develop/ai/search-and-query/indexing) using JSONPath queries.\n\nOn the schema creation `FT.CREATE`, it is now possible to map a JSONPath query with a field. When a JSON document is indexed, the value extracted by the JSONPath query is indexed in the given field.\n\n _This features require the module [RedisJSON 2.0](https://www.redisjson.io) to be installed._ \n\n#### Profiling queries\n\nWith the new `FT.PROFILE` command, it is now possible to profile in detail the execution time of several internal steps involved in the execution of `FT.SEARCH` and `FT.AGGREGATE`.\nThat way, it is possible to understand which part of the query is taking most of the resources.\n\n#### Field aliasing\n\nWith the support of JSON document indexing, it is now possible to map a JSONPath query to an alias. Therefore, it is possible to index the same value in different indexing attributes with different indexing strategies.\n\n> [!NOTE]\n> As of RediSearch v2.2, index `fields` are now known as `attributes` in order to avoid confusion with hash fields. This change impacts the `FT.INFO` command's response, which might break clients.  It is advised to upgrade your application to the latest supported Redis clients prior to upgrading to RediSearch 2.2."},{"id":"details","title":"Details","role":"content","text":"- Enhancements:\n  - #[2337](https://github.com/redisearch/redisearch/issues/2337) Add support for Redis COPY command \n  - #[2243](https://github.com/redisearch/redisearch/issues/2243) Add `LOAD *` for `FT.AGGREGATE`\n  - #[2207](https://github.com/redisearch/redisearch/issues/2207) Add multi value recursive decent tag\n  - #[2188](https://github.com/redisearch/redisearch/issues/2188) Add `UNF` flag for `SORTABLE` fields\n  - #[2184](https://github.com/redisearch/redisearch/issues/2184) LLAPI getter functions for score, language, and stopwords list\n  - #[2133](https://github.com/redisearch/redisearch/issues/2133) JSON array can be stored in a TAG field\n  - #[2153](https://github.com/redisearch/redisearch/issues/2153) Improve FT.INFO complexity to O(1) \n  - #[2138](https://github.com/redisearch/redisearch/issues/2138) Add `CASESENSITIVE` to `TAG` fields\n  - #[2137](https://github.com/redisearch/redisearch/issues/2137) `FT.INFO` has identifier and attribute for fields\n\n- Bug fixes:\n  - #[2341](https://github.com/redisearch/redisearch/issues/2341) Fix score field for JSON\n  - #[2325](https://github.com/redisearch/redisearch/issues/2325) Fix escaping for tags\n  - #[2269](https://github.com/redisearch/redisearch/issues/2269) Remove empty tag values\n  - #[2223](https://github.com/redisearch/redisearch/issues/2223) Replace NULL with empty iterator for child of negative iterator\n  - #[2215](https://github.com/redisearch/redisearch/issues/2215) Update field limit on tags\n  - #[2143](https://github.com/redisearch/redisearch/issues/2143) Partial JSON documents are not indexed\n  - #[2109](https://github.com/redisearch/redisearch/issues/2109) Field loaded with 'AS' can't be used by functions\n\nNotes:\nThis is the first GA version of 2.2. The version inside Redis will be 2.2.5 in semantic versioning.\n_Since the version of a module in Redis is numeric, we could not add a GA flag._"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.4-release-notes","title":"RediSearch 2.4 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.4-release-notes/","summary":"Vector Similarity Search (VSS). New query syntax Dialect version 2. Choose between Dialect 1 and Dialect 2 for query parser behavior. Hybrid queries.","tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:33+01:00","page_type":"content","content_hash":"c9e260599cd432e43ecce811745fe222dc95b627ee5cd194d0d147f545d1e935","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RediSearch v2.4.16 requires:\n\n- Minimum Redis compatibility version (database): 6.0.0\n- Minimum Redis Enterprise Software version (cluster): 6.0.0"},{"id":"v2416-november-2022","title":"v2.4.16 (November 2022)","role":"content","text":"This is a maintenance release for RediSearch 2.4\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails:\n\n- Bug fixes:\n\n  - [#2962](https://github.com/RediSearch/RediSearch/pull/2962) Crash upon AOF preload - Use local GC context in periodic callback (MOD-3951)\n  - [#2863](https://github.com/RediSearch/RediSearch/pull/2863) High CPU Utilization - Change MAX_LEV_DISTANCE to 4 (MOD-3563)\n  - [#3041](https://github.com/RediSearch/RediSearch/pull/3041), [#3063](https://github.com/RediSearch/RediSearch/pull/3063), [#3051](https://github.com/RediSearch/RediSearch/pull/3051), [#3143](https://github.com/RediSearch/RediSearch/pull/3143) Several memory leaks (MOD-4121, MOD-4252)\n  - [#3119](https://github.com/RediSearch/RediSearch/pull/3119) Crash on intersect iterator GetCriteriaTester (MOD-4200)\n  - [#3128](https://github.com/RediSearch/RediSearch/pull/3128) Filter rules must be reevaluated per index per document (MOD-4207)\n  - [#3127](https://github.com/RediSearch/RediSearch/pull/3127) Fix assertion failure on wrong result counting, which leads to a crash (MOD-4214)\n  - [#3171](https://github.com/RediSearch/RediSearch/pull/3171) Missing implementation of NumericRangeIterator_OnReopen which lead to crash (MOD-4255)\n  - [#3191](https://github.com/RediSearch/RediSearch/pull/3191) Wrong query iterator casting which resulted in 100% CPU utilization (MOD-4290)\n  - [#3197](https://github.com/RediSearch/RediSearch/pull/3197) Release failed to create temporary indices on the main thread (MOD-4388)\n  - [#2981](https://github.com/RediSearch/RediSearch/pull/2981) Double freeing in iterators of hybrid queries resulting in crash (MOD-4411)\n  - [#3161](https://github.com/RediSearch/RediSearch/pull/3161) Latency used to increase over time when combining INKEYS and wildcard query (MOD-4343)"},{"id":"v2415-october-2022","title":"v2.4.15 (October 2022)","role":"content","text":"This is a maintenance release for RediSearch 2.4.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n  - [#3095](https://github.com/RediSearch/RediSearch/pull/3095) Replace order of parsing the parameters and query in the coordinator (MOD-4205)\n  - [#3012](https://github.com/RediSearch/RediSearch/pull/3012) Improved efficiency of LLAPI `findInfo`, which could reduce stability during upgrade in Redis Enterprise (MOD-4197, MOD-4052)\n  - [#3040](https://github.com/RediSearch/RediSearch/pull/3040), [#3049](https://github.com/RediSearch/RediSearch/pull/3049) Fix for `SORTBY` numeric field for non-SORTABLE fields on the coordinator (MOD-4115)\n  - [#3050](https://github.com/RediSearch/RediSearch/pull/3050) Results from fields from a missing value should come last when combined with `SORTBY` (MOD-4120)\n\n- Improvements:\n\n  - [#3047](https://github.com/RediSearch/RediSearch/pull/3047) Add `strlen` string function to `FT.AGGREGATE` (MOD-4141)\n  - [#3038](https://github.com/RediSearch/RediSearch/pull/3038) Add `number_of_uses` to `FT.INFO` for the number of times the index was queried"},{"id":"v2414-august-2022","title":"v2.4.14 (August 2022)","role":"content","text":"This is a maintenance release for RediSearch 2.4.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Improvements:\n\n  - [Vector similarity v0.3.2](https://github.com/RedisAI/VectorSimilarity/releases/tag/v0.3.2)\n  - [#2955](https://github.com/RediSearch/RediSearch/pull/2955) Add timeout during prefix query (MOD-3949)\n  - [#2957](https://github.com/RediSearch/RediSearch/pull/2957) Efficient removal from prefix list for cases with many indices\n\n- Bug fixes:\n\n  - [#2937](https://github.com/RediSearch/RediSearch/pull/2937) Returning `NULL` response after encountering an expired document (MOD-3515)\n  - [#2962](https://github.com/RediSearch/RediSearch/pull/2962) Crash upon AOF preload (MOD-3951)\n  - [#2986](https://github.com/RediSearch/RediSearch/pull/2986) Memory leak related to schema prefixes"},{"id":"v2411-july-2022","title":"v2.4.11 (July 2022)","role":"content","text":"This is a maintenance release for RediSearch 2.4.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n  - [#2892](https://github.com/RediSearch/RediSearch/pull/2892) Combining `SORTBY` with `MAX` on `FT.SEARCH` (which is not supported) caused an inconsistent response and out-of-memory error (MOD-3540, MOD-3644)\n  - [VecSim v0.3.1](https://github.com/RedisAI/VectorSimilarity/releases/tag/v0.3.1)\n    - HNSW indices: reclaim memory upon deletion - HNSW index's data structures now reclaim memory and shrink upon deletion\n\n- Improvements:\n\n  - [VecSim v0.3.1](https://github.com/RedisAI/VectorSimilarity/releases/tag/v0.3.1)\n    - HNSW indices: delete procedure is up to 40% faster\n    - More accurate memory consumption reporting for HNSW indices"},{"id":"v2410-july-2022","title":"v2.4.10 (July 2022)","role":"content","text":"This is a maintenance release for RediSearch 2.4.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n  - [#2863](https://github.com/RediSearch/RediSearch/pull/2863) Crash due to too high (Levenshtein) `DISTANCE` in `FT.SPELLCHECK`. This fix limits the `DISTANCE` to 4. (MOD-3563)\n  - [#2875](https://github.com/RediSearch/RediSearch/pull/2875) Not all documents with vector fields were indexed with Auto Tiering (MOD-3584)\n  - [#2846](https://github.com/RediSearch/RediSearch/pull/2846) Enforce Redis Enterprise memory limit for vector indices"},{"id":"v249-june-2022","title":"v2.4.9 (June 2022)","role":"content","text":"This is a maintenance release for RediSearch 2.4.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n  - [#2837](https://github.com/RediSearch/RediSearch/pull/2837), [#2836](https://github.com/RediSearch/RediSearch/issues/2836) Crash on `FT.AGGREGATE` \"... APPLY '-INF % -1'...\"\n  - [#2814](https://github.com/RediSearch/RediSearch/pull/2814) `FT.EXPLAIN` without parameters causes a crash\n  - [#2790](https://github.com/RediSearch/RediSearch/pull/2790) Incorrect `num_terms` value in `FT.INFO` after a term is deleted from all the docs (garbage collection)\n  - [#2804](https://github.com/RediSearch/RediSearch/pull/2804) Freeze when `OFFSET`+`LIMIT` was greater than `maxSearchResults` (config)\n  - [#2791](https://github.com/RediSearch/RediSearch/pull/2791) Add `BlockedClientMeasureTime` to coordinator for more accurate performance stats\n  - [#2802](https://github.com/RediSearch/RediSearch/pull/2802) Tagged parts of keys (curly brackets `{}`) are now returned by `FT.SEARCH`\n\n- Improvements:\n\n  - [#2806](https://github.com/RediSearch/RediSearch/pull/2806) Do not load the JSON API when RediSearch is initialized as a library\n\n- Minor breaking change:\n\n  - As pointed out above, [#2802](https://github.com/RediSearch/RediSearch/pull/2802) is a bug fix. However, if your application relies on RediSearch incorrectly trimming the tagged part of a key (using `{}`), this could break your application. This only applies to users who are using RediSearch in clustered databases."},{"id":"v248-may-2022","title":"v2.4.8 (May 2022)","role":"content","text":"This is a maintenance release for RediSearch 2.4.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\nHowever, if you're using Vector Similarity (introduced in RediSearch 2.4), there are some critical bugs that may affect a subset of users. In this case, you should upgrade.\n\nDetails:\n\n- Bug fixes:\n\n  - [#2739](https://github.com/RediSearch/RediSearch/pull/2739) Memory leak in coordinator related to Vector Similarity (MOD-3023)\n  - [#2736](https://github.com/RediSearch/RediSearch/pull/2736), [#2782](https://github.com/RediSearch/RediSearch/pull/2782) Memory allocation restrictions for Vector Similarity indices (causing OOM) (MOD-3195)\n  - [#2755](https://github.com/RediSearch/RediSearch/pull/2755) Compare the entire vector field name instead of a prefix when creating a new vector index\n  - [#2780](https://github.com/RediSearch/RediSearch/pull/2780) Initialize all variables in `EvalContext` (which might have led to crashes in clustered databases)\n\n- Improvements:\n\n  - [#2740](https://github.com/RediSearch/RediSearch/pull/2740) Performance optimization for hybrid vector queries"},{"id":"v246-may-2022","title":"v2.4.6 (May 2022)","role":"content","text":"This is a maintenance release for RediSearch 2.4.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n  - [#2716](https://github.com/RediSearch/RediSearch/pull/2716) Removed assert statement that could cause crashes with replica of (MOD-3008, MOD-3012)\n  - [#2734](https://github.com/RediSearch/RediSearch/pull/2734) `ON_TIMEOUT RETURN` policy fix: return results obtained until timeout rather than discarding them\n  - [#2714](https://github.com/RediSearch/RediSearch/pull/2714) Memory leak on non-TLS setup in coordinator"},{"id":"v245-april-2022","title":"v2.4.5 (April 2022)","role":"content","text":"This is a maintenance release for RediSearch 2.4.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n  - [#2702](https://github.com/RediSearch/RediSearch/pull/2702) `INKEYS` combined with Vector Similarity caused server unresponsiveness (MOD-2952)\n  - [#2705](https://github.com/RediSearch/RediSearch/pull/2705) Incorrect results when deleting a document that was skipped at index time\n  - [#2698](https://github.com/RediSearch/RediSearch/issues/2698) Synonyms in Chinese\n\n- Improvements:\n\n  - [#2694](https://github.com/RediSearch/RediSearch/pull/2694) Performance: In a `TEXT` field, skip term iterator if term does not appear in requested field"},{"id":"v243-march-2022","title":"v2.4.3 (March 2022)","role":"content","text":"This is the General Availability release of RediSearch 2.4."},{"id":"headlines","title":"Headlines","role":"content","text":"RediSearch 2.4 introduces a new capability, Vector Similarity Search (VSS), which allows indexing and querying vector data stored (as BLOBs) in Redis hashes.\n\nIt also introduces a new query syntax to address query parser inconsistencies found in previous versions of RediSearch. Users can now choose between Dialect version 1 (to keep existing query parser behavior) or Dialect version 2 (to switch to the updated behavior).\n\nAll VSS queries or any query using the `PARAMS` option must use Dialect version 2."},{"id":"whats-new-in-24","title":"What's new in 2.4","role":"content","text":"- `FT.CREATE` is extended to support the creation of 2 popular types of vector indexes:\n\n  - **FLAT Index**\n\n    This type of index is used when the recall is more important than the speed of query execution. A query vector will be compared to all vectors in a flat index. The search results will return the exact top k nearest neighbors to the query vector.\n\n  - **Hierarchical Navigable Small World (HNSW) Index**\n\n    This index is a modified implementation of the library written by the author of this influential [academic paper](https://arxiv.org/abs/1603.09320). An HNSW vector index is used when the speed of query execution is preferred over recall. The results returned are approximate nearest neighbors (ANNs).\n\n    You can try out different HNSW index parameters (`M`, `EFCONSTRUCTION`, `EFRUNTIME`) to improve the \"recall versus speed\" balance.\n\n- Use `FT.SEARCH` to retrieve the top K hashes with the most similar vectors to a given query vector.\n\n- Hybrid queries in `FT.SEARCH`:\n\n  Use hybrid queries to retrieve Redis hashes that match a combination of vector and non-vector search criteria. Non-vector search criteria can include expressions combining `NUMERIC`, `TEXT`, `TAG`, and `GEO` fields.\n\n  Hybrid queries are often used in modern ecommerce search applications featuring \"visual\" similarity plus metadata similarity.\n  For example, you can use a single hybrid query to find products that are visually similar to a given image within a price range and/or geo-location.\n\n- Use [`FT.CONFIG SET`](https://redis.io/docs/latest/commands/ft.config-set) to set `DEFAULT_DIALECT` at the module level. By default, `DEFAULT_DIALECT` is set to 1.\n\n- Override `DIALECT`:\n\n  It is possible to override the module-level dialect for a specific command at runtime. You can specify the dialect when executing any of the following commands:\n\n    - [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search)\n\n    - [`FT.AGGREGATE`](https://redis.io/docs/latest/commands/ft.aggregate)\n\n    - [`FT.EXPLAIN`](https://redis.io/docs/latest/commands/ft.explain)\n\n    - [`FT.EXPLAINCLI`](https://redis.io/docs/latest/commands/ft.explaincli)\n\n    - [`FT.SPELLCHECK`](https://redis.io/docs/latest/commands/ft.spellcheck)\n\n  If you do not specify dialect when running any of these commands, they will use the default module-level dialect value."},{"id":"details","title":"Details","role":"content","text":"- Features:\n\n  - [#2671](https://github.com/RediSearch/RediSearch/pull/2671) Add Dialect support\n\n- Performance enhancements (since 2.4-RC1):\n\n  - [#2647](https://github.com/RediSearch/RediSearch/pull/2647) Normalize vector once for ad-hoc flat search\n  - [#2638](https://github.com/RediSearch/RediSearch/pull/2638) Optimized hybrid query when no scores are required\n  - [#2653](https://github.com/RediSearch/RediSearch/pull/2653) Updating specific field load optimization rule\n  - [#2670](https://github.com/RediSearch/RediSearch/pull/2670) Use `REDISMODULE_EVENT_SHUTDOWN` to clear resources\n\n- Security and privacy (since 2.4-RC1):\n\n  - [#2584](https://github.com/RediSearch/RediSearch/pull/2584) Fix MOD-2086, added support for TLS passphrase\n\n- Bug fixes (since 2.4-RC1):\n\n  - [#2651](https://github.com/RediSearch/RediSearch/pull/2651) Fix client freeze on docs expire during query\n  - [#2641](https://github.com/RediSearch/RediSearch/pull/2641) Memory leak in the coordinator\n  - [#2645](https://github.com/RediSearch/RediSearch/pull/2645) Ignore NULL value on ingest\n  - [#2654](https://github.com/RediSearch/RediSearch/pull/2654) VecSim hybrid query - return empty iterator for invalid intersection child iterator"},{"id":"introducing-dialect","title":"Introducing DIALECT","role":"content","text":"RediSearch 2.4.3 introduces a new query syntax to address query parser inconsistencies found in previous versions of RediSearch. Users can now choose between:\n\n- Dialect version 1 (to keep the query dialect as in RediSearch 2.2)\n\n- Dialect version 2 (to use the updated dialect)\n\nExisting RediSearch 2.2 users will not have to modify their queries since the default dialect is 1.\nHowever, all RediSearch users should gradually update their queries to use dialect version 2.\n\n#### Background\n\nUnder certain conditions, some query parsing rules did not behave as originally intended.\nQueries containing the following operators could return unexpected results:\n\n- `AND`\n- quotes, ~, -, and % (exact, optional, negation, fuzzy)\n- `OR`\n\nTo minimize the impact on existing, unaffected RediSearch users, a DIALECT setting was introduced to allow:\n\n- Existing queries to run without any modification (DIALECT 1)\n\n- New queries to benefit from the updated query-parsing behavior (DIALECT 2)\n\n\n#### Examples of impacted queries\n\nYour existing queries may behave differently under different DIALECT versions, if they fall into any of the following categories:\n\n- Your query has a field modifier followed by multiple words.\n\n  Consider this simple query <nobr>`@name:James Brown`</nobr>.\n\n  The field modifier `@name` is followed by 2 words: `James` and `Brown`.\n\n  With DIALECT 1, the parser interprets this query as \"find `James Brown` in the `@name` field.\"\n\n  With DIALECT 2, the parser interprets it as \"find `James` in the `@name` field AND `Brown` in ANY text field.\" In other words, the query parser interprets it as <nobr>`(@name:James) Brown`</nobr>.\n\n  With DIALECT 2, you could achieve the default behavior from DIALECT 1 by updating your query to <nobr>`@name:(James Brown)`</nobr>.\n\n- Your query uses quotes, ~, -, % (exact, optional, negation, fuzzy).\n\n  Consider a simple query with negation <nobr>`-hello world`</nobr>.\n\n  With DIALECT 1, the parser interprets this query as \"find values in any field that does not contain `hello` AND does not contain `world`.\" This is the equivalent of <nobr>`-(hello world)`</nobr> or <nobr>`-hello -world`</nobr>.\n\n  With DIALECT 2, the parser interprets it as `-hello` AND `world`, so only `hello` is negated.\n\n  With DIALECT 2, you could achieve the default behavior from dialect 1 by updating your query to <nobr>`-(hello world)`</nobr>.\n\nAnother example that illustrates the differences in parser behavior is\n`hello world | \"goodbye\" moon`:\n\n- With DIALECT 1, the parser interprets this query as searching for `(hello world | \"goodbye\") moon`\n\n- With DIALECT 2, the parser interprets it as searching for either `hello world` OR `\"goodbye\" moon`.\n\n> [!NOTE]\n> This is the first GA version of 2.4. The version inside Redis will be 2.4.3 in semantic versioning. Since the version of a module in Redis is numeric, we could not add a GA flag."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes","title":"RediSearch 2.6 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes/","summary":"Search using wildcard queries for TEXT and TAG fields, multi-value indexing and querying of attributes for any attribute type, and indexing double-precision floating-point vectors and range queries from a given vector.","tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:33+01:00","page_type":"content","content_hash":"eb1ff3f45a25c4352b321a1bb59af71d9fc0432103ea9839667dec7fe8f8421f","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RediSearch v2.6.37 requires:\n\n- Minimum Redis compatibility version (database): 6.0.16\n- Minimum Redis Enterprise Software version (cluster): 6.2.8"},{"id":"v2637-june-2026","title":"v2.6.37 (June 2026)","role":"content","text":"This is a maintenance release for Redis Search 2.6.\nUpdate urgency: `LOW`: No need to upgrade unless there are new features you want to use.\n\n**Bug Fixes**\n\n* [#10105](https://github.com/redisearch/redisearch/pull/10105) FT.SEARCH and FT.AGGREGATE return Unknown argument errors on 2.8 shards during rolling upgrades when a newer coordinator injects internal query arguments. (MOD-16047) \n* [#9579](https://github.com/redisearch/redisearch/pull/9579) Server becomes unresponsive when FT.CREATE is called with an extremely large number of fields. (MOD-6411) \n* [#9825](https://github.com/redisearch/redisearch/pull/9825) FT.SYNUPDATE leaves the synonym map partially mutated when an update batch exceeds synonym or group-ID limits. (MOD-15402) \n* [#9534](https://github.com/redisearch/redisearch/pull/9534) FT.INFO num_records is skewed for indexes with vector fields because vector entries are never decremented on deletion. (MOD-15487)"},{"id":"v2636-may-2026","title":"v2.6.36 (May 2026)","role":"content","text":"This is a maintenance release for Redis Search 2.6.\n\nUpdate urgency: `HIGH` : There is a critical bug that may affect a subset of users. Upgrade!\n\nBug fixes:\n\n- [#Q8950](https://github.com/redisearch/redisearch/pull/8950) `FT.CURSOR` enters an infinite loop when the invoking user's ACL restricts access to the cursor's index, causing CPU exhaustion and shard restarts. (MOD-14479)\n- [#Q8468](https://github.com/redisearch/redisearch/pull/8468) Garbage collector mishandles out-of-memory conditions on replicas, causing instability under memory pressure during indexing. (MOD-14066)\n- [#Q6792](https://github.com/redisearch/redisearch/pull/6792) Counter result-processor cleanup uses an incorrect type cast, risking memory corruption when freeing query pipeline resources. (MOD-11216)\n- [#Q6998](https://github.com/redisearch/redisearch/pull/6998) `FT.INFO` is fanned out to replicas in clustered deployments, producing inconsistent responses and unnecessary inter-node traffic.\n- [#Q6871](https://github.com/redisearch/redisearch/pull/6871) Coupled grow/shrink logic in Flat and HNSW vector index containers causes excess memory churn for large or fluctuating vector datasets. (MOD-10559)"},{"id":"v2632-august-2025","title":"v2.6.32 (August 2025)","role":"content","text":"This is a maintenance release for RediSearch 2.6.\n\nUpdate urgency: `HIGH` : There is a critical bug that may affect a subset of users. Upgrade!\n\nBug fixes:\n- [#6405](https://github.com/redisearch/redisearch/pull/6405) Validate compatibility against RedisJSON version upon open key (MOD-10298).\n- [#6419](https://github.com/redisearch/redisearch/pull/6419) Errors when loading schema from RDB get incorrectly cleared (MOD-10307).\n- [#6604](https://github.com/redisearch/redisearch/pull/6604) `FLUSHDB` while active queries are still running could lead to a crash due to premature release of the CURSOR (MOD-10681).\n\nImprovements:\n- [#6467](https://github.com/redisearch/redisearch/pull/6467) Handle excessive error logs when handling JSON.DEL errors (MOD-10266).\n- [#6659](https://github.com/redisearch/redisearch/pull/6659) Time measurement on `FT.PROFILE` using thread-independent clock mechanism (MOD-10622)."},{"id":"v2631-june-2025","title":"v2.6.31 (June 2025)","role":"content","text":"This is a maintenance release for RediSearch 2.6.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nBug fixes:\n- [#6349](https://github.com/redisearch/redisearch/pull/6349) Search on terms larger than 128 characters could lead to missing matches (MOD-6786).\n- [#6305](https://github.com/redisearch/redisearch/pull/6305) While iterating over a large index, frequent document updates could hit the `TIMEOUT`, causing a crash (MOD-9856).\n- [#6191](https://github.com/redisearch/redisearch/pull/6191) Reindexing from an RDB with multiple vector indexes could lead to a crash because of the cluster health check - NodeWD (MOD-9220)."},{"id":"v2630-may-2025","title":"v2.6.30 (May 2025)","role":"content","text":"This is a maintenance release for RediSearch 2.6.\n\nUpdate urgency: `HIGH` : There is a critical bug that may affect a subset of users. Upgrade!\n\nBug fixes:\n- [#6032](https://github.com/redisearch/redisearch/pull/6032) `FT.CURSOR...DEL` while another thread is reading it could lead to a crash (MOD-9408,MOD-9432,MOD-9433,MOD-9434,MOD-9435)\n- [#5965](https://github.com/redisearch/redisearch/pull/5965) Indexing documents using `TEXT` without the text in the documents leads to an `inf` or `nan` score (MOD-9423)\n- [#6058](https://github.com/redisearch/redisearch/pull/6058) Avoid lazy expiration in background indexing for Active-Active setup, preventing keys from expiring incorrectly (MOD-9486)\n- [#5962](https://github.com/redisearch/redisearch/pull/5962) Empty results with RESP3 due to the `TIMEOUT` even if setting to deliver partial results using the `ON_TIMEOUT` policy (MOD-8482)\n- [#5962](https://github.com/redisearch/redisearch/pull/5962) Cursor with RESP3 on `FT.AGGREGATE` is never depleted, blocking queries if the cursor limit is achieved (MOD-8515)\n- [#5962](https://github.com/redisearch/redisearch/pull/5962) Using `FT.CURSOR READ` on queries that timed out led to fewer results than expected (MOD-8606)\n\nImprovements:\n- [#6009](https://github.com/redisearch/redisearch/pull/6009) Parser for intersections on parentheses and sub-queries order won't affect full-text scores (MOD-9278)\n- [#5962](https://github.com/redisearch/redisearch/pull/5962) Fixed a coordinator race condition preventing the premature release and avoiding errors and inconsistencies during query executions (MOD-8794)"},{"id":"v2629-april-2025","title":"v2.6.29 (April 2025)","role":"content","text":"This is a maintenance release for RediSearch 2.6.\n\nUpdate urgency: `LOW` No need to upgrade unless there are new features you want to use.\n\nImprovements:\n- [#5940](https://github.com/redisearch/redisearch/pull/5940) Improved performance (reduced CPU time) of collecting vector index statistics (MOD-9354)\n- [#5816](https://github.com/redisearch/redisearch/pull/5816) Improved accuracy of index memory reporting by correcting a bug that caused negative memory counts (MOD-5904)"},{"id":"v2628-march-2025","title":"v2.6.28 (March 2025)","role":"content","text":"This is a maintenance release for RediSearch 2.6.\n\nUpdate urgency: `LOW` No need to upgrade unless there are new features you want to use.\n\nBug fixes:\n- [#5712](https://github.com/redisearch/redisearch/pull/5712) Weights in the query string are ignored if using `SCORER BM25` (MOD-7896)"},{"id":"v2627-february-2025","title":"v2.6.27 (February 2025)","role":"content","text":"This is a maintenance release for RediSearch 2.6.\n\nUpdate urgency: `LOW` No need to upgrade unless there are new features you want to use.\n\nBug fixes:\n\n- [#5648](https://github.com/redisearch/redisearch/pull/5648) `FT.SEARCH` using Cyrillic characters and wildcards delivering no results (MOD-7944)"},{"id":"v2626-february-2025","title":"v2.6.26 (February 2025)","role":"content","text":"This is a maintenance release for RediSearch 2.6.\n\nUpdate urgency: `HIGH` : There is a critical bug that may affect a subset of users. Upgrade!\n\nBug fixes:\n- [#5606](https://github.com/redisearch/redisearch/pull/5606) Changes on the memory block reading logic could cause crash on `FT.SEARCH` with error \"_Redis 7.4.2 crashed by signal: 11, si_code: 128_\"\n\nKnown limitations:\n- Only the first 128 characters of string fields are normalized to lowercase during ingestion (for example, on `HSET`).\n    Example:\n\n    [code example]\n\n    The `FT.SEARCH` command will return no documents."},{"id":"v2625-january-2025","title":"v2.6.25 (January 2025)","role":"content","text":"This is a maintenance release for RediSearch 2.6.\n\nUpdate urgency: `HIGH` : There is a critical bug that may affect a subset of users. Upgrade!\n\nBug fixes:\n- [#5484](https://github.com/redisearch/redisearch/pull/5484) NOSTEM option does not work on query, just tokenising (MOD-7634)\n- [#5543](https://github.com/redisearch/redisearch/pull/5543) Querying for the latest document added to the index may result in a crash if the last block is not read (MOD-8561)."},{"id":"v2624-january-2025","title":"v2.6.24 (January 2025)","role":"content","text":"This is a maintenance release for RediSearch 2.6.\n\nUpdate urgency: `SECURITY`: There are security fixes in the release.\n\n- **Security and privacy:**\n  - [#5458](https://github.com/redisearch/redisearch/pull/5458) (CVE-2024-51737) Query: potential out-of-bounds write (MOD-8486)\n\n- Bug fixes:\n  - [#5302](https://github.com/redisearch/redisearch/pull/5302) Prefix/Infix/Suffix queries longer than 1024 chars could cause a crash (MOD-7882)\n  - [#5281](https://github.com/redisearch/redisearch/pull/5281) `FT.CURSOR READ` retrieving deleted `TAG` fields cause a crash (MOD-8011)\n  - [#5168](https://github.com/redisearch/redisearch/pull/5168) Cursors from queries that timed out weren't depleted causing exhaustion of number of cursors available(MOD-8009)"},{"id":"v2621-august-2024","title":"v2.6.21 (August 2024)","role":"content","text":"This is a maintenance release for RediSearch 2.6.\n\nUpdate urgency: `HIGH` : There is a critical bug that may affect a subset of users. Upgrade!\n\n- Bug fixes:\n  - [#4944](https://github.com/redisearch/redisearch/pull/4944) Adjusting the module configuration to avoid routing overload on the first shard in a clustered database (MOD-7505)\n  - [#4897](https://github.com/redisearch/redisearch/pull/4897) - `FT.AGGREGATE` with `VERBATIM` option is not handled by the shards in cluster mode (MOD-7463)\n  - [#4918](https://github.com/redisearch/redisearch/pull/4918) - Union query, similar to `\"is|the\"`, starting with 2 [stopwords](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/stopwords/) could cause a crash (MOD-7495)\n  - [#4919](https://github.com/redisearch/redisearch/pull/4919) - Counting twice the field statistics at the `#search` section of an `INFO` response (MOD-7339)\n  - [#4923](https://github.com/redisearch/redisearch/pull/4923) - Loop when using the wildcard `w'term'` and prefix/infix/suffix pattern `'ter*'`, causing the shard to restart (MOD-7453)\n  - [#4954](https://github.com/redisearch/redisearch/pull/4954) `FT.PROFILE` on `AGGREGATE` numeric queries could cause a crash due to reusing the internal `CURSOR` in a large range of numeric values (MOD-7454)"},{"id":"v2620-july-2024","title":"v2.6.20 (July 2024)","role":"content","text":"This is a maintenance release for RediSearch 2.6.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\n- Bug fixes:\n  - [#4755](https://github.com/RediSearch/RediSearch/pull/4755) - Correct return the maximum value for negative values when using `MAX` reducer (MOD-7252)\n  - [#4733](https://github.com/RediSearch/RediSearch/pull/4733) - Separators ignored when escaping backslash `\\` after the escaped character such as in `hello\\\\,world` ignoring `,` (MOD-7240)\n  - [#4717](https://github.com/RediSearch/RediSearch/pull/4717) - Sorting by multiple fields as in `SORTBY 2 @field1 @field2` was ignoring the subsequent field (MOD-7206)\n\n- Improvements:\n  - [#4793](https://github.com/RediSearch/RediSearch/pull/4793) - Add character validations to simple string replies and escape it when required(MOD-7258)\n  - [#4769](https://github.com/RediSearch/RediSearch/pull/4769) - Indicate which value is missing on the error message at the aggregation pipeline (MOD-7201)\n  - [#4746](https://github.com/RediSearch/RediSearch/pull/4746) - `GROUPBY` recursion cleanup (MOD-7245)"},{"id":"v2619-june-2024","title":"v2.6.19 (June 2024)","role":"content","text":"This is a maintenance release for RediSearch 2.6.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\n- Bug fixes:\n\n  - [#4616](https://github.com/RediSearch/RediSearch/pull/4616) Shards become unresponsive when using FT.AGGREGATE with APPLY 'split(...)' (MOD-6759)\n  - [#4557](https://github.com/RediSearch/RediSearch/pull/4557) FT.EXPLAIN returns additional } when querying using wildcards (MOD-6768)\n  - [#4647](https://github.com/RediSearch/RediSearch/pull/4647) FT.DROPINDEX with DD flag deleted keys in one AA cluster but not the others (MOD-1855)\n\n- Improvements:\n\n  - [#4599](https://github.com/RediSearch/RediSearch/pull/4599) Report additional memory consumed by the TAG and TEXT tries (MOD-5902)\n  - [#4688](https://github.com/RediSearch/RediSearch/pull/4688) Add missing FT.INFO fields when used within a cluster (MOD-6920)"},{"id":"v2618-april-2024","title":"v2.6.18 (April 2024)","role":"content","text":"This is a maintenance release for RediSearch 2.6.\n\nUpdate urgency: `LOW`: No need to upgrade unless there are new features you want to use.\n\nDetails:\n\n- Bug fixes:\n\n  - [#4557](https://github.com/RediSearch/RediSearch/pull/4557) Additional \"`}`\" on wildcards replies for `FT.EXPLAIN` (MOD-6768)"},{"id":"v2617-april-2024","title":"v2.6.17 (April 2024)","role":"content","text":"This is a maintenance release for RediSearch 2.6.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails:\n\n- Bug fixes:\n\n  - [#4524](https://github.com/RediSearch/RediSearch/pull/4524) `FT.CURSOR READ` in a numeric query causing a crash (MOD-6597)\n  - [#4543](https://github.com/RediSearch/RediSearch/pull/4543) `FT.SEARCH` accessing an inexistent memory address causes a crash if using deprecated `FT.ADD` command (MOD-6599)\n  - [#4535](https://github.com/RediSearch/RediSearch/pull/4535) `FT.PROFILE` with incorrect arguments could cause a crash on cluster setup (MOD-6791)\n  - [#4540](https://github.com/RediSearch/RediSearch/pull/4540) Unfree memory from an existing RDB while re-indexing loading a new RDB could cause a crash (MOD-6831, 6810)\n  - [#4485](https://github.com/RediSearch/RediSearch/pull/4485) Some parameter settings using just prefixes instead of full values were working (MOD-6709)\n\n- Improvements:\n\n  - [#4502](https://github.com/RediSearch/RediSearch/pull/4502) Handle error properly when trying to execute Search commands on cluster setup as part of `MULTI ... EXEC` or LUA script (MOD-6541)"},{"id":"v2616-march-2024","title":"v2.6.16 (March 2024)","role":"content","text":"This is a maintenance release for RediSearch 2.6.\n\nUpdate urgency: `MODERATE` : Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n  - [#4477](https://github.com/RediSearch/RediSearch/pull/4477) Split `INFIX` and `SUFFIX` report on `FT.EXPLAIN` and `FT.EXPLAINCLI` (MOD-6186)\n  - [#4468](https://github.com/RediSearch/RediSearch/pull/4468) Memory leak upon suffix query for a `TAG` indexed with `WITHSUFFIXTRIE` (MOD-6644)\n  - [#4407](https://github.com/RediSearch/RediSearch/pull/4407) Clustered `FT.SEARCH` hangs forever without replying when an invalid topology is found (MOD-6557)\n  - [#4359](https://github.com/RediSearch/RediSearch/pull/4359) Searching for a synonym will iterate in the same group multiple times, causing a performance hit (MOD-6490)\n  - [#4310](https://github.com/RediSearch/RediSearch/pull/4310) Memory tracking on cluster setups causing high memory usage and potentially Out-of-Memory (MOD-6123, MOD-5639)"},{"id":"v2615-december-2023","title":"v2.6.15 (December 2023)","role":"content","text":"This is a maintenance release for RediSearch 2.6.\n\nUpdate urgency: `HIGH` : There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails:\n\n- Bug fixes:\n\n  - [#4244](https://github.com/RediSearch/RediSearch/pull/4244), [#4255](https://github.com/RediSearch/RediSearch/pull/4255) Profiling `FT.AGGREGATE` using the `WITHCURSOR` flag causes a crash due to timeout (MOD-5512)\n  - [#4238](https://github.com/RediSearch/RediSearch/pull/4238) Memory excessively growing on databases caused by unbalanced nodes on inverted index trie (MOD-5880, MOD-5952, MOD-6003) \n  - [#3995](https://github.com/RediSearch/RediSearch/pull/3995) `FT.CURSOR READ` with geo queries causing a crash when data is updated between the cursor reads (MOD-5646) \n  - [#4155](https://github.com/RediSearch/RediSearch/pull/4155) `FT.SEARCH` not responding when using TLS encryption on Amazon Linux 2 (MOD-6012)\n\n- Improvements:\n\n  - [#4176](https://github.com/RediSearch/RediSearch/pull/4176) Initialization of the maximum numeric value range leading to a better balance of the index leaf splitting (MOD-6232) \n  - [#4123](https://github.com/RediSearch/RediSearch/pull/4123) Possibly problematic index name alias check-in command multiplexing (MOD-5945)\n  - [#4195](https://github.com/RediSearch/RediSearch/pull/4195) Query optimization when predicate contains multiple `INTERSECTION` (AND) of `UNION` (OR) (MOD-5910)"},{"id":"v2614-november-2023","title":"v2.6.14 (November 2023)","role":"content","text":"This is a maintenance release for RediSearch 2.6.\n\nUpdate urgency: `SECURITY`: There are security fixes in the release.\n\nDetails:\n\n- Bug fixes:\n\n  - [#3783](https://github.com/RediSearch/RediSearch/pull/3783) Broken lower and upper `APPLY` functions in `FT.AGGREGATE` on `DIALECT 3` (MOD-5041)\n  - [#3823](https://github.com/RediSearch/RediSearch/pull/3823) `APPLY` or `FILTER` expression causing a leak (MOD-5751)\n  - [#3899](https://github.com/RediSearch/RediSearch/pull/3899) Connection using TLS fail on Redis (MOD-5768)\n  - [#3910](https://github.com/RediSearch/RediSearch/pull/3910) Heavy document updates causing memory growth if memory blocks weren't properly released (MOD-5181)(MOD-5757)\n  - [#3928](https://github.com/RediSearch/RediSearch/pull/3928) Queries with `WITHCURSOR` making memory growth since `CURSOR` wasn't invalidated in the shards (MOD-5580)\n  - [#3946](https://github.com/RediSearch/RediSearch/pull/3946) Vector range query could cause Out-of-Memory due to memory corruption (MOD-5791)\n  - [#3972](https://github.com/RediSearch/RediSearch/pull/3972) Adding new nodes to OSS cluster can cause a crash (MOD-5778)\n  - [#3957](https://github.com/RediSearch/RediSearch/pull/3957) After cleaning the index, the GC could corrupt unique values (MOD-5815)\n  - [#4002](https://github.com/RediSearch/RediSearch/pull/4002) Setting a low `MAXIDLE` parameter value in `FT.AGGREGATE` causes a crash (MOD-5608)\n\n- Security and privacy:\n\n  - [#3844](https://github.com/RediSearch/RediSearch/pull/3844) Limits maximum phonetic length to avoid vulnerability (MOD 5767)"},{"id":"v2612-july-2023","title":"v2.6.12 (July 2023)","role":"content","text":"This is a maintenance release for RediSearch 2.6.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails:\n\n- Bug fixes:\n\n  - [#3557](https://github.com/RediSearch/RediSearch/pull/3557) `TIMEOUT` configuration on `FT.AGGREGATE` query being ignored (MOD-5208)\n  - [#3552](https://github.com/RediSearch/RediSearch/pull/3552) `FT.CURSOR READ` on `JSON` numeric queries not returning results (MOD-4830)\n  - [#3606](https://github.com/RediSearch/RediSearch/pull/3606) Update numeric inverted index `numEntries` avoiding excessive memory consumption (MOD-5181)\n  - [#3597](https://github.com/RediSearch/RediSearch/pull/3597) Duplicating alias as output name on `FT.AGGREGATE` reducer (`REDUCE` argument) doesn't return results (MOD-5268)\n  - [#3654](https://github.com/RediSearch/RediSearch/pull/3654) Added check for `@` prefix on `GROUPBY` fields returning an error instead of wrong results\n\n- Improvements:\n\n  - [#3628](https://github.com/RediSearch/RediSearch/pull/3628) Background indexing scanning performance (MOD-5259)\n  - [#3259](https://github.com/RediSearch/RediSearch/pull/3259) Allow alias name beginning with `as`\n  - [#3641](https://github.com/RediSearch/RediSearch/pull/3641) Indexing sanitizing trigger in heavy data updates scenario"},{"id":"v269-april-2023","title":"v2.6.9 (April 2023)","role":"content","text":"This is a maintenance release for RediSearch 2.6.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails:\n\n- Bug fixes:\n\n  - [#3468](https://github.com/RediSearch/RediSearch/pull/3468) KNN searching for 0 vectors with a filter resulted in crash (MOD-5006)\n  - [#3499](https://github.com/RediSearch/RediSearch/pull/3499) `MAXSEARCHRESULTS` set to `0` causing `FT.SEARCH` to crash (MOD-5062)\n  - [#3494](https://github.com/RediSearch/RediSearch/pull/3494) Removing `MAXSEARCHRESULTS` limit causes crash on `FT.AGGREGATE` (MOD-4974)\n  - [#3504](https://github.com/RediSearch/RediSearch/pull/3504) Uninitialised vector similarity query parameter bug (MOD-5063)\n\n- Improvements:\n\n  - [#3430](https://github.com/RediSearch/RediSearch/pull/3430) Improve min-max heap structure for better readability and performance\n  - [#3450](https://github.com/RediSearch/RediSearch/pull/3450) Display `NOHL` option in `FT.INFO` command\n  - [#3534](https://github.com/RediSearch/RediSearch/pull/3534) Vector Similarity 0.6.1: Improve multi-value index deletion logic ([#346](https://github.com/RedisAI/VectorSimilarity/pull/346))"},{"id":"v266-march-2023","title":"v2.6.6 (March 2023)","role":"content","text":"This is a maintenance release for RediSearch 2.6.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n  - [#3403](https://github.com/RediSearch/RediSearch/pull/3403) Fix suffix and prefix matching when using `CASESENSITIVE` flag (MOD-4872)\n\n- Improvements:\n\n  - [#3397](https://github.com/RediSearch/RediSearch/pull/3397) Improve the Vecsim initial capacity default value"},{"id":"v265-february-2023","title":"v2.6.5 (February 2023)","role":"content","text":"This is a maintenance release for RediSearch 2.6.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails:\n\n- Bug fixes:\n\n  - [#3354](https://github.com/RediSearch/RediSearch/pull/3354) Library update preventing a crash during cluster failover (MOD-4560)\n  - [#3357](https://github.com/RediSearch/RediSearch/pull/3357) Handling division by zero in expressions preventing nodes to restart (MOD-4296)\n  - [#3332](https://github.com/RediSearch/RediSearch/pull/3332) Fix wildcards `*` queries on `DIALECT 2` and `DIALECT 3`\n\n- Improvements:\n  \n  - [#3361](https://github.com/RediSearch/RediSearch/pull/3361) Enable the use of IPv6 for all cluster and module communication"},{"id":"v264-december-2022","title":"v2.6.4 (December 2022)","role":"content","text":"This is a maintenance release for RediSearch 2.6.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n  - [#3289](https://github.com/RediSearch/RediSearch/pull/3289) Potential crash when querying multiple fields (MOD-4639)\n  - [#3279](https://github.com/RediSearch/RediSearch/pull/3279) Potential crash when querying using wildcard `*` on TAG field (MOD-4653)\n\n- Improvements:\n  \n  - [#3256](https://github.com/RediSearch/RediSearch/pull/3256) Support IPv6 on cluster set command\n  - [#3194](https://github.com/RediSearch/RediSearch/pull/3194) Add the query dialects that are in use to `FT.INFO` and `INFO MODULE` commands (MOD-4232)\n  - [#3258](https://github.com/RediSearch/RediSearch/pull/3258) Add the module version and Redis version to `INFO MODULE`"},{"id":"v26-ga-v263-november-2022","title":"v2.6 GA (v2.6.3) (November 2022)","role":"content","text":"This is the General Availability release of RediSearch 2.6."},{"id":"highlights","title":"Highlights","role":"content","text":"This new major version introduces the ability to search using **wildcard queries** for TEXT and TAG fields. This enables the frequently requested feature **suffix search** (`*vatore` and `ant?rez` are now supported).\nIn addition, the 2.6 release is all about **multi-value indexing and querying of attributes** for any attribute type ( [Text](https://redis.io/docs/latest/develop/ai/search-and-query/indexing#index-json-arrays-as-text), [Tag](https://redis.io/docs/latest/develop/ai/search-and-query/indexing#index-json-arrays-as-tag), [Numeric](https://redis.io/docs/latest/develop/ai/search-and-query/indexing#index-json-arrays-as-numeric), [Geo](https://redis.io/docs/latest/develop/ai/search-and-query/indexing#index-json-arrays-as-geo) and [Vector](https://redis.io/docs/latest/develop/ai/search-and-query/indexing#index-json-arrays-as-vector)) defined by a [JSONPath](https://redis.io/docs/latest/develop/data-types/json/path) leading to an array or to multiple scalar values.\nLastly, this version adds support for indexing double-precision floating-point vectors and range queries from a given vector."},{"id":"whats-new-in-26","title":"What's new in 2.6","role":"content","text":""},{"id":"details","title":"Details","role":"content","text":"- Improvements:\n\n  - [#2886](https://github.com/RediSearch/RediSearch/pull/2886) Support for [wildcard queries](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/query_syntax#wildcard-matching) for TEXT and TAG fields, where\n    - `?` matches any single character\n    - `*` matches zero or more characters\n    - use `'` and `\\` for escaping, other special characters are ignored\n    - [#2932](https://github.com/RediSearch/RediSearch/pull/2932) Optimized wildcard query support (i.e., suffix trie)\n  - Multi-value indexing and querying\n    - [#2819](https://github.com/RediSearch/RediSearch/pull/2819), [#2947](https://github.com/RediSearch/RediSearch/pull/2947) Multi-value text search - perform full-text search on an [array of strings or on a JSONPath](https://redis.io/docs/latest/develop/ai/search-and-query/indexing#index-json-arrays-as-tag) leading to multiple strings\n    - [#3131](https://github.com/RediSearch/RediSearch/pull/3131) Geo [#3118](https://github.com/RediSearch/RediSearch/pull/3118) Vector [#2985](https://github.com/RediSearch/RediSearch/pull/2985) Numeric [#3180](https://github.com/RediSearch/RediSearch/pull/3180) Tag\n    - [#3060](https://github.com/RediSearch/RediSearch/pull/3060) Return JSON rather than scalars from multi-value attributes.  This is enabled via Dialect 3 in order not to break existing applications.\n    - Support indexing and querying of multi-value JSONPath attributes and/or arrays (requires JSON >2.4.1)\n    - [#3182](https://github.com/RediSearch/RediSearch/pull/3182) Support for `SORTABLE` fields on JSON in an implicit un-normalized form (UNF)\n  - [#3156](https://github.com/RediSearch/RediSearch/pull/3156) Vector similarity 0.5.1:\n    - Better space optimization selection ([#175](https://github.com/RedisAI/VectorSimilarity/pull/175)) \n    - Aligning index capacity with block size ([#177](https://github.com/RedisAI/VectorSimilarity/pull/177)) \n    - [#3129](https://github.com/RediSearch/RediSearch/pull/3129) Support FLOAT64 as vector data type \n    - [#3176](https://github.com/RediSearch/RediSearch/pull/3176) Range query support\n    - [#3105](https://github.com/RediSearch/RediSearch/pull/3105) Support query attributes for vector queries\n\n- Bugs (since 2.6-RC1 / v2.6.1):\n\n  - [#3197](https://github.com/RediSearch/RediSearch/pull/3197) Failure to create temporary indices\n  - [#3098](https://github.com/RediSearch/RediSearch/pull/3098) Wrong return value in Geo query\n  - [#3230](https://github.com/RediSearch/RediSearch/issues/3230) Use the correct total number of matching records\n\n> [!NOTE]\n> With this release, we stop supporting direct upgrades from RediSearch v1.4 and v1.6 that are End-of-Life. Such RDB files can still be upgraded to RediSearch 2.0 first.\n\n> [!NOTE]\n> If indexing and querying RedisJSON data structures, this version is best combined with RedisJSON 2.4 GA (v2.4.1 onwards)."}],"examples":[{"id":"v2626-february-2025-ex0","language":"plaintext","code":"HSET doc __score 1.0 name \"idx1S...S\" mynum 1          # Assume \"S...S\" is a string of 252 capital S's\n    FT.CREATE \"idx\" SCHEMA \"name\" \"TEXT\" \"mynum\" \"NUMERIC\"\n    FT.SEARCH \"idx\" \"@name:idx1S...S\"                      # Assume \"S...S\" is a string of 252 capital S's","section_id":"v2626-february-2025"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes","title":"RediSearch 2.8 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes/","summary":"RESP3 support. Geo Polygon Search. Performance improvements.","tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:33+01:00","page_type":"content","content_hash":"a60aac5b71f5dd2d554fcac845486da63b848aa0b86e364b477207ab951a06d8","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RediSearch v2.8.38 requires:\n\n- Minimum Redis compatibility version (database): 7.2\n- Minimum Redis Enterprise Software version (cluster): 7.2.4"},{"id":"v2838-june-2026","title":"v2.8.38 (June 2026)","role":"content","text":"This is a maintenance release for Redis Search 2.8.\nUpdate urgency: `LOW`: No need to upgrade unless there are new features you want to use.\n\n**Bug Fixes**\n\n* [#10105](https://github.com/redisearch/redisearch/pull/10105) FT.SEARCH and FT.AGGREGATE return Unknown argument errors on 2.8 shards during rolling upgrades when a newer coordinator injects internal query arguments. (MOD-16047)\n* [#9579](https://github.com/redisearch/redisearch/pull/9579) Server becomes unresponsive when FT.CREATE is called with an extremely large number of fields. (MOD-6411)\n* [#9825](https://github.com/redisearch/redisearch/pull/9825) FT.SYNUPDATE leaves the synonym map partially mutated when an update batch exceeds synonym or group-ID limits. (MOD-15402)\n* [#9534](https://github.com/redisearch/redisearch/pull/9534) FT.INFO num_records grows without bound for indexes with vector fields due to missing decrement on deletion. (MOD-15487)"},{"id":"v2837-may-2026","title":"v2.8.37 (May 2026)","role":"content","text":"This is a maintenance release for Redis Search 2.8.\n\nUpdate urgency: `HIGH` : There is a critical bug that may affect a subset of users. Upgrade!\n\nBug fixes:\n\n- [#Q7860](https://github.com/redisearch/redisearch/pull/7860): `FT.SUGADD` and `FT.SUGDEL`return inconsistent results in sharded pre-Redis-8 deployments due to random shard routing (MOD-13152)\n- [#Q8949](https://github.com/redisearch/redisearch/pull/8949): `FT.CURSOR` enters an infinite loop when the caller's ACL restricts key access (MOD-14479)\n- [#Q8795](https://github.com/redisearch/redisearch/pull/8795):  `FT.EXPLAIN` crashes or reads stale state under concurrent schema changes (MOD-14461)\n- [#Q7697](https://github.com/redisearch/redisearch/pull/7697): Stack-use-after-scope in `_FT.CURSOR PROFILE` produces undefined behavior (MOD-12955)\n- [#Q7687](https://github.com/redisearch/redisearch/pull/7687): Cursor resources leak when the associated index is dropped while the cursor is open (MOD-12807)\n- [#Q8466](https://github.com/redisearch/redisearch/pull/8466): Garbage collector mishandles Out-of-Memory conditions on replicas (MOD-14066)\n- [#Q8163](https://github.com/redisearch/redisearch/pull/8163): `FT.PROFILE` reports incorrect shard total profile time in cluster mode (MOD-13735, MOD-13181)\n- [#Q8085](https://github.com/redisearch/redisearch/pull/8085): `FT.PROFILE` reports incorrect `FULLTEXT` field metric count (MOD-13432)\n- [#Q7721](https://github.com/redisearch/redisearch/pull/7721): `FT.PROFILE` always reports GILTime 0 due to missing initialization and accumulation (MOD-11987, MOD-12816)\n- [#Q9074](https://github.com/redisearch/redisearch/pull/9074): `FT.CREATE` accepts invalid schema option combinations that fail silently at query time (MOD-14655)\n- [#Q9083](https://github.com/redisearch/redisearch/pull/9083): `FT.SPELLCHECK` ignores $param placeholders from the `PARAMS` clause (MOD-10596)\n- [#Q9435](https://github.com/redisearch/redisearch/pull/9435): RDB loader accepts out-of-range or malformed values, causing instability on restore (MOD-13118)\n- [#Q7703](https://github.com/redisearch/redisearch/pull/7703), #Q7727: Pending coordinator and worker jobs are not correctly tracked in the queue\n- [#Q7629](https://github.com/redisearch/redisearch/pull/7629), #Q7653, #Q7662: `FT.PROFILE` gains a debug mode to simulate timeouts, crashes, and mid-execution pauses (MOD-8127, MOD-11155, MOD-12627)\n- [#Q7966](https://github.com/redisearch/redisearch/pull/7966): Module load no longer fails when find_module is undefined (MOD-13330)\n\nMetrics:\n\n- [#Q7597](https://github.com/redisearch/redisearch/pull/7597), [#Q7645](https://github.com/redisearch/redisearch/pull/7645), [#Q7665](https://github.com/redisearch/redisearch/pull/7665): New `active_io_threads`, `active_worker_threads`, and `active_coord_threads` metrics expose live thread activity (MOD-12069, MOD-12694, MOD-12695)\n- [#Q7778](https://github.com/redisearch/redisearch/pull/7778): New counters track total documents indexed and fields indexed per field type across all indexes (MOD-12070)\n- [#Q7788](https://github.com/redisearch/redisearch/pull/7788), [#Q7819](https://github.com/redisearch/redisearch/pull/7819), [#Q7820](https://github.com/redisearch/redisearch/pull/7820): New `FT.INFO` counters track query syntax errors, timeout errors/warnings, and MAXPREFIXEXPANSIONS warnings (MOD-12416, MOD-12417, MOD-12419)\n- [#Q7751](https://github.com/redisearch/redisearch/pull/7751): Cluster-mode `FT.PROFILE` now reports Internal cursor reads per shard (MOD-12414)\n- [#Q8066](https://github.com/redisearch/redisearch/pull/8066): Index commands now emit informational log entries for operational visibility (MOD-13431)"},{"id":"v2832-november-2025","title":"v2.8.32 (November 2025)","role":"content","text":"This is a maintenance release for RediSearch 2.8.\n\nUpdate urgency: `LOW`: No need to upgrade unless there are new features you want to use.\n\nImprovements:\n- [#7157](https://github.com/RediSearch/RediSearch/pull/7157) Show background indexing OOM warning in `FT.AGGREGATE` reply in RESP3.\n- [#7380](https://github.com/RediSearch/RediSearch/pull/7380) Rename `FT.PROFILE` counter fields.\n- [#7366](https://github.com/RediSearch/RediSearch/pull/7366) Reduce temporary memory overhead upon index load from RDB.\n- [#7393](https://github.com/RediSearch/RediSearch/pull/7393) Enhance `FT.PROFILE` with vector search execution details.\n- [#7480](https://github.com/RediSearch/RediSearch/pull/7480) Ensure full profile output on timeout with RETURN policy.\n\nBug Fixes:\n- [#7216](https://github.com/RediSearch/RediSearch/pull/7216) Fix a concurrency issue on Reducer in `FT.AGGREGATE`.\n- [#7259](https://github.com/RediSearch/RediSearch/pull/7259) Fix underflow in BM25STD.\n- [#7278](https://github.com/RediSearch/RediSearch/pull/7278) Report used memory as unsigned long long to avoid underflows.\n- [#7340](https://github.com/RediSearch/RediSearch/pull/7340) Fix a rare leak in GC.\n- [#7462](https://github.com/RediSearch/RediSearch/pull/7462) Fix Fork GC potential double-free on error path.\n- [#7525](https://github.com/RediSearch/RediSearch/pull/7525) Avoid draining workers thread pool from `FLUSHDB` callback to avoid potential deadlocks.\n\nFull Changelog: https://github.com/RediSearch/RediSearch/compare/v2.8.31...v2.8.32"},{"id":"v2831-october-2025","title":"v2.8.31 (October 2025)","role":"content","text":"This is a maintenance release for RediSearch 2.8.\n\nUpdate urgency: `LOW`: No need to upgrade unless there are new features you want to use.\n\nBug Fixes:\n- [#6960](https://github.com/RediSearch/RediSearch/pull/6960) - FT.INFO returns the wrong number of documents in OSS Cluster with replicas.\n- [#6938](https://github.com/RediSearch/RediSearch/pull/6938) - Fix for the HIGHLIGHT feature, where if some fields have empty strings, wrong tokens might be highlighted.\n- [#7049](https://github.com/RediSearch/RediSearch/pull/7049) - Avoid crashing in the FT.AGGREGATE command in clusters where different shards have different ON_TIMEOUT policies configured (fail vs return)."},{"id":"v2830-september-2025","title":"v2.8.30 (September 2025)","role":"content","text":"This is a maintenance release for RediSearch 2.8.\n\nUpdate urgency: `HIGH` : There is a critical bug that may affect a subset of users. Upgrade!\n\nBug fixes:\n- [#6672](https://github.com/RediSearch/RediSearch/pull/6672) Fix potential file descriptor leak when OOM.\n- [#6763](https://github.com/RediSearch/RediSearch/pull/6763) Fix potential deadlock during RDB loading in cases where the `INFO` command is sent to the server.\n\nFull Changelog: https://github.com/RediSearch/RediSearch/compare/v2.8.29...v2.8.30."},{"id":"v2829-august-2025","title":"v2.8.29 (August 2025)","role":"content","text":"This is a maintenance release for RediSearch 2.8.\n\nUpdate urgency: `HIGH` : There is a critical bug that may affect a subset of users. Upgrade!\n\nBug fixes:\n- [#6599](https://github.com/redisearch/redisearch/pull/6599) `FLUSHDB` while active queries are still running could lead to a crash due to premature release of the CURSOR (MOD-10681).\n- [#6418](https://github.com/redisearch/redisearch/pull/6418) Errors when loading schema from RDB get incorrectly cleared (MOD-10307).\n- [#6405](https://github.com/redisearch/redisearch/pull/6405) Validate compatibility against RedisJSON version upon open key (MOD-10298).\n\nImprovements:\n- [#6466](https://github.com/redisearch/redisearch/pull/6466) Handle excessive error logs when handling JSON.DEL errors (MOD-10266).\n- [#6663](https://github.com/redisearch/redisearch/pull/6663) Time measurement on `FT.PROFILE` using thread-independent clock mechanism (MOD-10622).\n- [#6646](https://github.com/redisearch/redisearch/pull/6646) Response on RESP2/3 validation was inefficiently consuming excessive CPU cycles (MOD-9687)."},{"id":"v2828-june-2025","title":"v2.8.28 (June 2025)","role":"content","text":"This is a maintenance release for RediSearch 2.8.\n\nUpdate urgency: `HIGH` : There is a critical bug that may affect a subset of users. Upgrade!\n\nBug fixes:\n- [#6207](https://github.com/redisearch/redisearch/pull/6207) Continuous increasing of index error counts on `FT.INFO` could lead to an overflow and memory leak (MOD-9396).\n- [#6349](https://github.com/redisearch/redisearch/pull/6349) Search on terms larger than 128 characters could lead to missing matches (MOD-6786).\n- [#6305](https://github.com/redisearch/redisearch/pull/6305) While iterating over a large index, frequent document updates could hit the `TIMEOUT`, causing a crash (MOD-9856).\n\nImprovements:\n- [#6340](https://github.com/redisearch/redisearch/pull/6340) Added a locking mechanism for collecting `FT.INFO` statistics when concurrently running the index sanitiser (MOD-10007, MOD-9761)."},{"id":"v2827-may-2025","title":"v2.8.27 (May 2025)","role":"content","text":"This is a maintenance release for RediSearch 2.8.\n\nUpdate urgency: `HIGH` : There is a critical bug that may affect a subset of users. Upgrade!\n\nBug fixes:\n- [#6191](https://github.com/redisearch/redisearch/pull/6191) Reindexing from RDB with multiple vector indices could lead to a crash due to cluster health check - NodeWD (MOD-9220,MOD-8809)\n- [#6031](https://github.com/redisearch/redisearch/pull/6031) `FT.CURSOR...DEL` while another thread is reading it could lead to a crash (MOD-9408,MOD-9432,MOD-9433,MOD-9434,MOD-9435)\n- [#5966](https://github.com/redisearch/redisearch/pull/5966) Indexing documents using `TEXT` without the text in the documents leads to an `inf` or `nan` score (MOD-9423)\n- [#6057](https://github.com/redisearch/redisearch/pull/6057) Avoid lazy expiration in background indexing for Active-Active setup, preventing keys from expiring incorrectly (MOD-9486)\n- [#6113](https://github.com/redisearch/redisearch/pull/6113) A timeout failure is returned when the `ON_TIMEOUT RETURN` policy is set to stop the collection of partial results - best effort (MOD-9612)\n\nImprovements:\n- [#6008](https://github.com/redisearch/redisearch/pull/6008) Parser for intersections on parentheses and sub-queries order won't affect full-text scores (MOD-9278)"},{"id":"v2826-april-2025","title":"v2.8.26 (April 2025)","role":"content","text":"This is a maintenance release for RediSearch 2.8.\n\nUpdate urgency: `HIGH` : There is a critical bug that may affect a subset of users. Upgrade!\n\nBug fixes:\n- [#5864](https://github.com/redisearch/redisearch/pull/5864) Last query result using `FT.AGGREGATE` with `ON_TIMEOUT RETURN` and using multi-threading could be missing (MOD-9222)\n- [#5863](https://github.com/redisearch/redisearch/pull/5863) Collecting empty results from shards during `FT.AGGREGATE` with RESP3 could cause a crash (MOD-9174)\n\nImprovements:\n- [#5938](https://github.com/redisearch/redisearch/pull/5938) Improved performance (reduced CPU time) of collecting vector index statistics(MOD-9354)\n- [#5800](https://github.com/redisearch/redisearch/pull/5800) Improved accuracy of index memory reporting by correcting a bug that caused negative memory counts (MOD-5904)\n\n\nCreated on: 2025-04-21T14:51:40Z"},{"id":"v2825-march-2025","title":"v2.8.25 (March 2025)","role":"content","text":"This is a maintenance release for RediSearch 2.8.\n\nUpdate urgency: `HIGH` : There is a critical bug that may affect a subset of users. Upgrade!\n\nBug fixes:\n- [#5709](https://github.com/redisearch/redisearch/pull/5709) Weights in the query string are ignored if using `SCORER BM25` (MOD-7896)\n- [#5788](https://github.com/redisearch/redisearch/pull/5788) Cursor with `SORTBY` is never depleted, blocking queries if the cursor limit is reached (MOD-8483)\n- [#5788](https://github.com/redisearch/redisearch/pull/5788) Empty results with RESP3 due to the `TIMEOUT`, even if `ON_TIMEOUT` is set to `RETURN`(MOD-8482)\n- [#5788](https://github.com/redisearch/redisearch/pull/5788) Cursor with RESP3 on `FT.AGGREGATE` is never depleted, blocking queries if cursor the limit is achieved (MOD-8515)\n- [#5788](https://github.com/redisearch/redisearch/pull/5788) Using `FT.CURSOR READ` on queries that timed out led to fewer results than expected (MOD-8606)\n- [#5810](https://github.com/redisearch/redisearch/pull/5810) The `total_results` field of the `FT.AGGREGATE` command is not correct in RESP3 (MOD-9054)\n\nImprovements:\n- [#5788](https://github.com/redisearch/redisearch/pull/5788) Corrected a coordinator race condition that prevented premature release, avoiding errors and inconsistencies during query executions (MOD-8794)"},{"id":"v2824-february-2025","title":"v2.8.24 (February 2025)","role":"content","text":"This is a maintenance release for RediSearch 2.8.\n\nUpdate urgency: `LOW` No need to upgrade unless there are new features you want to use.\n\nBug fixes:\n- [#5647](https://github.com/redisearch/redisearch/pull/5647) `FT.SEARCH` using Cyrillic characters and wildcards delivering no results (MOD-7944)"},{"id":"v2823-february-2025","title":"v2.8.23 (February 2025)","role":"content","text":"This is a maintenance release for RediSearch 2.8.\n\nUpdate urgency: `HIGH` : There is a critical bug that may affect a subset of users. Upgrade!\n\nBug fixes:\n- [#5605](https://github.com/redisearch/redisearch/pull/5605) Changes on the memory block reading logic could cause crash on `FT.SEARCH` with error \"_Redis 7.4.2 crashed by signal: 11, si_code: 128_\"\n\nKnown limitations:\n- Only the first 128 characters of string fields are normalized to lowercase during ingestion (for example, on `HSET`).\n    Example:\n\n    [code example]\n\n    The `FT.SEARCH` command will return no documents."},{"id":"v2822-january-2025","title":"v2.8.22 (January 2025)","role":"content","text":"This is a maintenance release for RediSearch 2.8.\n\nUpdate urgency: `HIGH` : There is a critical bug that may affect a subset of users. Upgrade!\n\nBug fixes:\n- [#5475](https://github.com/redisearch/redisearch/pull/5475) NOSTEM option does not work on query, just tokenising (MOD-7634)\n- [#5542](https://github.com/redisearch/redisearch/pull/5542) Querying for the latest document added to the index may result in a crash if the last block is not read (MOD-8561)."},{"id":"v2821-january-2025","title":"v2.8.21 (January 2025)","role":"content","text":"This is a maintenance release for RediSearch 2.8.\n\nUpdate urgency: `SECURITY`: There are security fixes in the release.\n\n**Security and privacy:**\n- [#5457](https://github.com/redisearch/redisearch/pull/5457) (CVE-2024-51737) Query: potential out-of-bounds write (MOD-8486)\n\nBug fixes:\n- [#5299](https://github.com/redisearch/redisearch/pull/5299) Prefix/Infix/Suffix queries longer than 1024 chars could cause a crash (MOD-7882)\n- [#5303](https://github.com/redisearch/redisearch/pull/5303) Expired keys while background indexing could cause cross slot error when using `replicaof` (MOD-7949)\n- [#5280](https://github.com/redisearch/redisearch/pull/5280) `FT.CURSOR READ` retrieving deleted `TAG` fields cause a crash (MOD-8011)\n- [#5427](https://github.com/redisearch/redisearch/pull/5427) `FT.AGGREGATE` on numeric fields lead to `failed_calls` count increase on clustered DBs (MOD-8058)\n- [#5242](https://github.com/redisearch/redisearch/pull/5242) Memory count on `bytes_collected` by the index sanitiser with missing values (MOD-8097, MOD-8114)\n- [#5167](https://github.com/redisearch/redisearch/pull/5167) Cursors from queries that timed out weren't depleted causing exhaustion of number of cursors available(MOD-8009)\n\nImprovements:\n- [#5260](https://github.com/redisearch/redisearch/pull/5260) Optimizing index consumed memory with the creation only upon write operations (MOD-8125)"},{"id":"v2817-august-2024","title":"v2.8.17 (August 2024)","role":"content","text":"This is a maintenance release for RediSearch 2.8.\n\nUpdate urgency: `HIGH` : There is a critical bug that may affect a subset of users. Upgrade!\n\n- Bug fixes:\n\n  - [#4941](https://github.com/redisearch/redisearch/pull/4941) Adjusting the module configuration to avoid routing overload on the first shard in a clustered database (MOD-7505)\n  - [#4950](https://github.com/redisearch/redisearch/pull/4950) `FT.PROFILE` on `AGGREGATE` numeric queries could cause a crash due to reusing internal `CURSOR` in large range of numeric values (MOD-7454)"},{"id":"v2816-august-2024","title":"v2.8.16 (August 2024)","role":"content","text":"This is a maintenance release for RediSearch 2.8.\n\nUpdate urgency: `HIGH` : There is a critical bug that may affect a subset of users. Upgrade!\n\n- Bug fixes:\n\n  - [#4896](https://github.com/redisearch/redisearch/pull/4896) - `FT.AGGREGATE` with `VERBATIM` option is not handled by the shards in cluster mode (MOD-7463)\n  - [#4917](https://github.com/redisearch/redisearch/pull/4917) - Union query, similar to `\"is|the\"`, starting with 2 [stopwords](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/stopwords/) could cause a crash (MOD-7495)\n  - [#4921](https://github.com/redisearch/redisearch/pull/4921) - Counting twice the field statistics at the `#search` section of an `INFO` response (MOD-7339)\n  - [#4939](https://github.com/redisearch/redisearch/pull/4939) - Query warning when using RESP3 response for reaching `MAXPREFIXEXPANSION` (MOD-7588)\n  - [#4930](https://github.com/redisearch/redisearch/pull/4930) - Loop when using the wildcard `w'term'` and prefix/infix/suffix pattern `'ter*'` leading shard to restart (MOD-7453)\n  - [#4912](https://github.com/redisearch/redisearch/pull/4912) - Avoid stemming expansion when querying for numeric values (MOD-7025)"},{"id":"v2815-july-2024","title":"v2.8.15 (July 2024)","role":"content","text":"Update urgency: `HIGH` : There is a critical bug that may affect a subset of users. Upgrade!\n\n- Bug fixes:\n  - [#4754](https://github.com/RediSearch/RediSearch/pull/4754) - Correct return the maximum value for negative values when using `MAX` reducer (MOD-7252)\n  - [#4737](https://github.com/RediSearch/RediSearch/pull/4737) - Separators ignored when escaping backslash `\\` after the escaped character such as in `hello\\\\,world` ignoring `,` (MOD-7240)\n  - [#4717](https://github.com/RediSearch/RediSearch/pull/4717) - Sorting by multiple fields `SORTBY 2 @field1 @field2` was ignoring the subsequent field(MOD-7206)\n  - [#4803](https://github.com/RediSearch/RediSearch/pull/4803) - Keys expiring during query returning empty array (MOD-7010)\n  - [#4794](https://github.com/RediSearch/RediSearch/pull/4794) - Index sanitiser (GC) trying to clean deleted numeric index could cause a crash (MOD-7303)\n\n- Improvements:\n  - [#4792](https://github.com/RediSearch/RediSearch/pull/4792) - Add character validations to simple string replies and escape it when required(MOD-7258)\n  - [#4768](https://github.com/RediSearch/RediSearch/pull/4768) - Indicate which value is missing on the error message at the aggregation pipeline (MOD-7201)\n  - [#4745](https://github.com/RediSearch/RediSearch/pull/4745) - `GROUPBY` recursion cleanup (MOD-7245)\n  - [#4823](https://github.com/RediSearch/RediSearch/pull/4823) - Mechanism of keys expiration during the query execution clearing intermediate results"},{"id":"v2814-june-2024","title":"v2.8.14 (June 2024)","role":"content","text":"This is a maintenance release for RediSearch 2.8.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\n- Bug fixes:\n\n  - [#4614](https://github.com/RediSearch/RediSearch/pull/4614) Shards become unresponsive when using `FT.AGGREGATE` with `APPLY 'split(...)'`(MOD-6759)\n  - [#4556](https://github.com/RediSearch/RediSearch/pull/4556) `FT.EXPLAIN` returns additional `}` when querying using wildcards (MOD-6768)\n  - [#4646](https://github.com/RediSearch/RediSearch/pull/4646) `FT.DROPINDEX` with `DD` flag deleted keys in one AA cluster but not the others (MOD-1855)\n\n- Improvements:\n  - [#4595](https://github.com/RediSearch/RediSearch/pull/4595) Report memory of the `TAG` and `TEXT` tries (MOD-5902)\n  - [#4669](https://github.com/RediSearch/RediSearch/pull/4669) Inverted index memory counting (MOD-5977,MOD-5866)\n  - [#4687](https://github.com/RediSearch/RediSearch/pull/4687) Add missing `FT.INFO` fields when used within a cluster (MOD-6920)"},{"id":"v2813-march-2024","title":"v2.8.13 (March 2024)","role":"content","text":"This is a maintenance release for RediSearch 2.8.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails:\n\n- Bug fixes:\n\n  - [#4481](https://github.com/RediSearch/RediSearch/pull/4481) Query syntax on `GEOSHAPE` accepting just prefix instead of complete predicate (MOD-6663)\n  - [#4513](https://github.com/RediSearch/RediSearch/pull/4513) `FT.CURSOR READ` in a numeric query causing a crash (MOD-6597)\n  - [#4534](https://github.com/RediSearch/RediSearch/pull/4534) `FT.PROFILE` with incorrect arguments could cause a crash on cluster setup (MOD-6791)\n  - [#4530](https://github.com/RediSearch/RediSearch/pull/4530) Some parameter settings using just prefixes instead of full values were working (MOD-6709)\n  - [#4539](https://github.com/RediSearch/RediSearch/pull/4539) Unfree memory while re-indexing a new RDB as it's loading could cause a crash (MOD-6831, 6810)\n  - [#4498](https://github.com/RediSearch/RediSearch/pull/4498) Vector pre-filtered query (hybrid query) that times out causing a crash due to deadlock when trying to write a new document (MOD-6510, MOD-6244)\n  - [#4495](https://github.com/RediSearch/RediSearch/pull/4495) `FT.SEARCH` accessing an inexistent memory address causes a crash if using the deprecated `FT.ADD` command (MOD-6599)\n\n- Improvements:\n\n  - [#4502](https://github.com/RediSearch/RediSearch/pull/4502) Handle error properly when trying to execute Search commands on cluster setup as part of `MULTI ... EXEC` or LUA script (MOD-6541)\n  - [#4526](https://github.com/RediSearch/RediSearch/pull/4526) Adding detailed geometry info on error messages (MOD-6701)"},{"id":"v2812-march-2024","title":"v2.8.12 (March 2024)","role":"content","text":"This is a maintenance release for RediSearch 2.8.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n  - [#4476](https://github.com/RediSearch/RediSearch/pull/4476) Split `INFIX` and `SUFFIX` report on `FT.EXPLAIN` and `FT.EXPLAINCLI` (MOD-6186)\n  - [#4467](https://github.com/RediSearch/RediSearch/pull/4467) Memory leak upon suffix query for a `TAG` indexed with `WITHSUFFIXTRIE` (MOD-6644)\n  - [#4403](https://github.com/RediSearch/RediSearch/pull/4403) Clustered `FT.SEARCH` hangs forever without replying when an invalid topology is found (MOD-6557)\n  - [#4355](https://github.com/RediSearch/RediSearch/pull/4355) Searching for a synonym will iterate in the same group multiple times, causing a performance hit (MOD-6490)\n\n- Improvements:\n\n  - [#4313](https://github.com/RediSearch/RediSearch/pull/4313) Memory allocation patterns on the memory used to query `GEOSHAPE` types (MOD-6431)"},{"id":"v2811-january-2024","title":"v2.8.11 (January 2024)","role":"content","text":"This is a maintenance release for RediSearch 2.8.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n  - [#4324](https://github.com/RediSearch/RediSearch/pull/4324) Internal cluster mechanism not waiting until all replies from shards causing a crash (MOD-6287)\n  - [#4297](https://github.com/RediSearch/RediSearch/pull/4297) Execution loader when using `FT.AGGREGATE` with `LOAD` stage failing to buffer the right results potentially causing a crash (MOD-6385)\n\n- Improvements:\n\n  - [#4264](https://github.com/RediSearch/RediSearch/pull/4264) Granularity of the time reporting counters on `FT.PROFILE` (MOD-6002)"},{"id":"v2810-january-2024","title":"v2.8.10 (January 2024)","role":"content","text":"This is a maintenance release for RediSearch 2.8.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails:\n\n- Bug fixes:\n\n  - [#4287](https://github.com/RediSearch/RediSearch/pull/4287) Re-index process while syncing from the replica causes a crash due to internal index variable initialization (MOD-6337, MOD-6336)\n  - [#4249](https://github.com/RediSearch/RediSearch/pull/4249) Memory tracking on cluster setups causing high memory usage and potentially Out-of-Memory (MOD-6123, MOD-5639)\n  - [#4244](https://github.com/RediSearch/RediSearch/pull/4244) Profiling `FT.AGGREGATE` using the `WITHCURSOR` flag with a `-` clause causes a crash due to timeout (MOD-5512)\n  - [#3916](https://github.com/RediSearch/RediSearch/pull/3916) Expiring `JSON` documents while querying it causing a crash due to deadlock (MOD-5769, MOD-5895, MOD-6189, MOD-5895)\n  - [#4235](https://github.com/RediSearch/RediSearch/pull/4235) Memory excessively growing on databases caused by unbalanced nodes on inverted index trie (MOD-5880, MOD-5952, MOD-6003) \n  - [#4190](https://github.com/RediSearch/RediSearch/pull/4190) Profiling `FT.AGGREGATE` causes a crash on RESP3 replies (MOD-6250, MOD-6295)\n  - [#4148](https://github.com/RediSearch/RediSearch/pull/4148), [#4038](https://github.com/RediSearch/RediSearch/pull/4038) `ON_TIMEOUT FAIL\\RETURN` policies in the cluster setup not being respected (MOD-6035, MOD-5948, MOD-6090)\n  - [#4110](https://github.com/RediSearch/RediSearch/pull/4110) Format of error response contains inconsistencies when timing out (MOD-6011, MOD-5965)\n  - [#4104](https://github.com/RediSearch/RediSearch/pull/4104) `FT.SEARCH` not responding when using TLS encryption on Amazon Linux 2 (MOD-6012)\n  - [#4009](https://github.com/RediSearch/RediSearch/pull/4009) In cluster setup does not return a timeout error for `FT.SEARCH` (MOD-5911)\n  - [#3920](https://github.com/RediSearch/RediSearch/pull/3920) In cluster setup does not return a timeout error for `FT.AGGREGATE` (MOD-5209)\n  - [#3914](https://github.com/RediSearch/RediSearch/pull/3914) `FT.CURSOR READ` with geo queries causing a crash when data is updated between the cursor reads (MOD-5646) \n  - [#4220](https://github.com/RediSearch/RediSearch/pull/4220) Server crash when attempting to run the ForkGC (Garbage Collection routine) after dropping the index (MOD-6276)\n\n- Improvements:\n\n  - [#3682](https://github.com/RediSearch/RediSearch/pull/3682) Report last key error and field type indexing failures on `FT.INFO` (MOD-5364)\n  - [#4236](https://github.com/RediSearch/RediSearch/pull/4236) Adding Vector index parameters at the `FT.INFO` report (MOD-6198)\n  - [#4196](https://github.com/RediSearch/RediSearch/pull/4196) Check for timeout after results processing in `FT.SEARCH` on cluster setup (MOD-6278)\n  - [#4164](https://github.com/RediSearch/RediSearch/pull/4164) Report `TIMEOUT`, `MAXPREFIXEXPANSION` warnings in RESP3 replies (MOD-6234)\n  - [#4165](https://github.com/RediSearch/RediSearch/pull/4165) Indicate timeout on `FT.PROFILE` report (MOD-6184)\n  - [#4149](https://github.com/RediSearch/RediSearch/pull/4149) Indicate timeout from Cursor on `FAIL` timeout policy (MOD-5990)\n  - [#4147](https://github.com/RediSearch/RediSearch/pull/4147) Initialization of the maximum numeric value range leading to a better balance of the index leaf splitting (MOD-6232)\n  - [#3940](https://github.com/RediSearch/RediSearch/pull/3940) Query optimization when predicate contains multiple `INTERSECTION` (AND) of `UNION` (OR) (MOD-5910)\n  - [#4059](https://github.com/RediSearch/RediSearch/pull/4059) Return cursor id when experiencing a timeout, when the policy is `ON_TIMEOUT RETURN` (MOD-5966)\n  - [#4006](https://github.com/RediSearch/RediSearch/pull/4006) Possibly problematic index name alias validation (MOD-5945)"},{"id":"v289-october-2023","title":"v2.8.9 (October 2023)","role":"content","text":"This is a maintenance release for RediSearch 2.8.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails:\n\n- Bug fixes:\n\n  - [#3874](https://github.com/RediSearch/RediSearch/pull/3874) Heavy document updates causing memory growth once memory blocks weren't properly released (MOD-5181)\n  - [#3967](https://github.com/RediSearch/RediSearch/pull/3967) Resharding optimizations causing the process to get stuck (MOD-5874, MOD-5864)\n  - [#3892](https://github.com/RediSearch/RediSearch/pull/3892) After cleaning the index the GC could cause corruption on unique values (MOD-5815)\n  - [#3853](https://github.com/RediSearch/RediSearch/pull/3853) Queries with `WITHCURSOR` making memory growth since `CURSOR` wasn't invalidated in the shards (MOD-5580)\n\n- Improvements:\n\n  - [#3938](https://github.com/RediSearch/RediSearch/pull/3938) Propagating error messages in multiple shards database, instead of failing silently (MOD-5211)\n  - [#3903](https://github.com/RediSearch/RediSearch/pull/3903) Added support for Rocky Linux 9 and RHEL9 (MOD-5759)"},{"id":"v288-september-2023","title":"v2.8.8 (September 2023)","role":"content","text":"This is a maintenance release for RediSearch 2.8.\n\nUpdate urgency: `SECURITY`: There are security fixes in the release.\n\nDetails:\n\n- Security and privacy:\n\n  - [#3788](https://github.com/RediSearch/RediSearch/pull/3788) Don’t expose internal cluster commands (MOD-5706)\n  - [#3844](https://github.com/RediSearch/RediSearch/pull/3844) Limits maximum phonetic length avoiding to be exploit (MOD 5767)\n\n- Bug fixes:\n\n  - [#3771](https://github.com/RediSearch/RediSearch/pull/3771) Broken `lower()` and `upper()` functions on `APPLY` stage in `FT.AGGREGATE` in `DIALECT 3` (MOD-5041)\n  - [#3752](https://github.com/RediSearch/RediSearch/pull/3752) Setting low `MAXIDLE` parameter value in `FT.AGGREGATE` cause a crash (MOD-5608)\n  - [#3780](https://github.com/RediSearch/RediSearch/pull/3780) Wrong document length calculation causing incorrect score values (MOD-5622)\n  - [#3808](https://github.com/RediSearch/RediSearch/pull/3808) `LOAD` step after a `FILTER` step could cause a crash on `FT.AGGREGATE` (MOD-5267)\n  - [#3823](https://github.com/RediSearch/RediSearch/pull/3823) `APPLY` or `FILTER` parser leak (MOD-5751)\n  - [#3837](https://github.com/RediSearch/RediSearch/pull/3837) Connection using TLS fail on Redis 7.2 (MOD-5768)\n  - [#3856](https://github.com/RediSearch/RediSearch/pull/3856) Adding new nodes to OSS cluster causing a crash (MOD-5778)\n  - [#3854](https://github.com/RediSearch/RediSearch/pull/3854) Vector range query could cause Out-of-Memory due to a memory corruption (MOD-5791)\n\n- Improvements:\n\n  - [#3534](https://github.com/RediSearch/RediSearch/pull/3534) Vector Similarity 0.7.1 (MOD-5624)"},{"id":"v28-ga-v284-july-2023","title":"v2.8 GA (v2.8.4) (July 2023)","role":"content","text":"This is the General Availability release of RediSearch 2.8."},{"id":"headlines","title":"Headlines","role":"content","text":"RediSearch 2.8 introduces support for RESP3, new features, performance improvements, and bug fixes."},{"id":"whats-new-in-284","title":"What's new in 2.8.4","role":"content","text":"This new major version introduces new and frequently asked for Geo Polygon Search, adding the `GEOSHAPE` field type that supports polygon shapes using [WKT notation](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry). Besides the current `GEO` (alias for `GEOPOINT`) used in geo range queries, we add support for `POLYGON` and `POINT` as new geo shape formats (new `GEOSHAPE`). In addition, 2.8 brings performance improvements for `SORT BY` operations using `FT.SEARCH` and `FT.AGGREGATE`, and new `FORMAT` for enhanced responses on `FT.SEARCH` and `FT.AGGREGATE` in RESP3 only.\n\nFeatures:\n\n- Introduce support for geo polygon shapes and queries:\n\n  - Adding `GEOSHAPE` [field type](https://redis.io/docs/latest/commands/ft.create) to map polygons in the `SCHEMA` on `FT.CREATE` (MOD-4798)\n\n  - Support for polygons `POLYGON` and `POINT` using [WKT notation](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry), for example `POLYGON((lon1 lat1, lon2 lat2, ...))`\n\n  - Adjust the [query syntax](https://redis.io/docs/latest/commands/ft.search#examples) on `FT.SEARCH` for polygons using the predicate `@geom:[OPERATOR $poly]` and defining polygon in WKT format as `PARAMS 2 poly \"POLYGON((10 20, ...))\"` using `DIALECT 3`\n\n  - Initially `WITHIN` and `CONTAINS` operators with `GEOSHAPES` for now\n\n  - Support multiple coordinate systems: cartesian (X,Y) with the flag `FLAT` for flat earth and geographic (lon, lat) using the flag `SPHERICAL` (MOD-5303). Geographic coordinate system using spherical indexing as default (`SPHERICAL`)\n\n  - Add memory usage per Geometry Index in the `FT.INFO` response report (MOD-5278)\n\n- Introduce performance optimization for sorting operations on `FT.SEARCH` and `FT.AGGREGATE` as default on `DIALECT 4`. It will improve performance in 4 different scenarios, listed below:\n\n  - Skip Sorter - applied when there is no sort of any kind. The query can return once it reaches the `LIMIT` requested results.\n\n  - Partial Range - applied when there is a `SORTBY` a numeric field, with no filter or filter by the same numeric field, the query iterates on a range large enough to satisfy the `LIMIT` requested results.\n\n  - Hybrid - applied when there is a `SORTBY` a numeric field in addition to another non-numeric filter. Some results will get filtered, and the initial range may not be large enough. The iterator then is rewinded with the following ranges, and an additional iteration takes place to collect `LIMIT` requested results.\n\n  - No optimization - If there is a sort by score or by non-numeric field, there is no other option but to retrieve all results and compare their values.\n\n- Add `WITHCOUNT` argument that allow return accurate counts for the query results with sorting. This operation processes all results in order to get accurate count, being less performant than the optimized option (default behavior on `DIALECT 4`) (MOD-5311)\n\n- New `FORMAT` argument in `FT.SEARCH` and `FT.AGGREGATE` to retrieve the results as JSON strings or RESP3 hierarchical structures (RESP3 only) (MOD-5390)\n\nImprovements (since 2.8.3):\n\n- [#3717](https://github.com/RediSearch/RediSearch/pull/3717) - Polygon shapes validation and orientation correction when clockwise (MOD-5575)\n\n- [#3534](https://github.com/RediSearch/RediSearch/pull/3534) - Vector Similarity \\[[0.7.0](https://github.com/RedisAI/VectorSimilarity/releases/tag/v0.7.0)\\]\n\n- [#3657](https://github.com/RediSearch/RediSearch/pull/3657) - Allow GC calls for all tiered indexes in the schema\n\n- [#3701](https://github.com/RediSearch/RediSearch/pull/3701) - HNSW is now using data blocks to store vectors and metadata instead of array\n\nChanged behavior:\n\n- [#3355](https://github.com/RediSearch/RediSearch/pull/3355), [#3635](https://github.com/RediSearch/RediSearch/pull/3635) Expired keys deleted from replica's index, returning an empty array instead of `nil` (MOD-4739)\n\n> [!NOTE]\n> - The version inside Redis will be 2.8.4 in semantic versioning. Since the version of a module in Redis is numeric, we could not add a GA flag.\n>\n> - Minimal Redis version: 7.2\n>\n> - If indexing and querying RedisJSON data structures, this version is best combined with RedisJSON 2.6 (v2.6.0 onwards)."}],"examples":[{"id":"v2823-february-2025-ex0","language":"plaintext","code":"HSET doc __score 1.0 name \"idx1S...S\" mynum 1          # Assume \"S...S\" is a string of 252 capital S's\n    FT.CREATE \"idx\" SCHEMA \"name\" \"TEXT\" \"mynum\" \"NUMERIC\"\n    FT.SEARCH \"idx\" \"@name:idx1S...S\"                      # Assume \"S...S\" is a string of 252 capital S's","section_id":"v2823-february-2025"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisgears","title":"RedisGears release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgears/","summary":"RedisGears release notes for version 1.2 and earlier","tags":["docs","operate","stack"],"last_updated":"2024-02-26T13:11:00+01:00","children":[{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisgears/redisgears-1.2-release-notes","summary":"Plugins and JVM support. Python async await. Override commands API. Register functions on key miss events. Tracks new statistics. Python profiler support. Extended RedisAI integration.","title":"RedisGears 1.2 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgears/redisgears-1.2-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisgears/redisgears-1.0-release-notes","summary":"First GA release of RedisGears. Built-in C-API and Python interpreter. Run a serverless engine in memory next to your Redis data.","title":"RedisGears 1.0 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgears/redisgears-1.0-release-notes/"}],"page_type":"content","content_hash":"4ec7cce87b40b12387d574a1a766ac97889a84f92d47c29907435b4a56c8e8f9","sections":[{"id":"content","title":"Content","role":"content","text":"| Version (Release date) | Major changes | Min Redis version | Min cluster version |\n| --- | --- | --- | --- |\n| [v1.2 (February 2022)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgears/redisgears-1.2-release-notes/) | Plugins and JVM support. Python async await. Override commands API. Register functions on key miss events. Tracks new statistics. Python profiler support. Extended RedisAI integration. | 6.0.0 | 6.0.12 |\n| [v1.0 (May 2020)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgears/redisgears-1.0-release-notes/) | First GA release of RedisGears. Built-in C-API and Python interpreter. Run a serverless engine in memory next to your Redis data. | 6.0.0 | 6.0.0 |"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisgears/redisgears-1.0-release-notes","title":"RedisGears 1.0 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgears/redisgears-1.0-release-notes/","summary":"First GA release of RedisGears. Built-in C-API and Python interpreter. Run a serverless engine in memory next to your Redis data.","tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:33+01:00","page_type":"content","content_hash":"f335ca5fb3d529d8c396f858916867399c6c8f02b23303394bbf3ac57cc9e2a1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Here's what's changed recently in RedisGears. \n\nTo learn more, see the linked pull requests."},{"id":"requirements","title":"Requirements","role":"content","text":"RedisGears v1.0.9 requires:\n\n- Minimum Redis compatibility version (database): 6.0.0\n- Minimum Redis Enterprise Software version (cluster): 6.0.0"},{"id":"v109-january-2022","title":"v1.0.9 (January 2022)","role":"content","text":"This is a maintenance release for RedisGears 1.0\n\nUpdate urgency: `LOW` : No need to upgrade unless there are new features you want to use.\n\nDetails:\n\n- Improvements:\n\n    - [#652](https://github.com/RedisGears/RedisGears/pull/652) Support for keys space notifications coming from other modules."},{"id":"v108-november-2021","title":"v1.0.8 (November 2021)","role":"content","text":"This is a maintenance release for RedisGears 1.0.\n\nUpdate urgency: `MODERATE` - Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Minor features:\n\n    - [#624](https://github.com/RedisGears/RedisGears/pull/624), [#626](https://github.com/RedisGears/RedisGears/pull/626) Update Python interpreter version to 3.7.12\n\n- Bug fixes:\n\n    - [#610](https://github.com/RedisGears/RedisGears/pull/610) Fix symbol collision with RediSearch and RedisGraph that causes deadlock\n\n    - [#609](https://github.com/RedisGears/RedisGears/pull/609), [#611](https://github.com/RedisGears/RedisGears/pull/611) Crash on stream reader where stream is deleted during read\n\n    - [#612](https://github.com/RedisGears/RedisGears/pull/612) Return error when GearsBuilder is used after `registered` or `run`\n\n    - [#613](https://github.com/RedisGears/RedisGears/pull/613) Rare issue where stream reader might trigger executions on replica\n\n    - [#629](https://github.com/RedisGears/RedisGears/pull/629) Rare deadlock on stream reader"},{"id":"v107-august-2021","title":"v1.0.7 (August 2021)","role":"content","text":"This is a maintenance release for version 1.0.\n\nUpdate urgency: `LOW` - No need to upgrade unless there are new features you want to use.\n\nDetails:\n\n- Minor Features:\n\n    - [#594](https://github.com/RedisGears/RedisGears/pull/594) Added RedisGears info section to Redis info command\n\n    - [#587](https://github.com/RedisGears/RedisGears/pull/587) `inorder` option for the command reader\n    \n    - [#592](https://github.com/RedisGears/RedisGears/pull/592), [#586](https://github.com/RedisGears/RedisGears/pull/586) Background executions will now appear on Redis slowlog, available in Redis 6.2 and above."},{"id":"v106-march-2021","title":"v1.0.6 (March 2021)","role":"content","text":"This is a maintenance release for version 1.0.\n\n- Update urgency: Medium\n\n- Highlights:\n\n    This release improves overall stability and provides fixes for issues found after the previous release.\n\n- Bug fixes:\n\n    - #[496](https://github.com/RedisGears/RedisGears/pull/496) Allow parallel execution on Command Reader.\n\n    - #[505](https://github.com/RedisGears/RedisGears/pull/505) Crash on a client that sends cluster set and disconnects."},{"id":"v105-18-january-2021","title":"v1.0.5 (18 January 2021)","role":"content","text":"This is a maintenance release for version 1.0.\n\n- Update urgency: High\n\n- Highlights:\n\n    This release improves overall stability and provides fixes for issues found after the previous release.\n\n- Bug fixes:\n\n    - #[480](https://github.com/RedisGears/RedisGears/pull/480) Crash when map/accumulate/accumulateby raises an error which causes the same pyobject to be freed twice.\n\n    - #[480](https://github.com/RedisGears/RedisGears/pull/480) 'Cluster set' happened before resending hello request, which caused a crash.\n\n    - #[480](https://github.com/RedisGears/RedisGears/pull/480) Memory leaks on 'cluster set'."},{"id":"v104-17-january-2021","title":"v1.0.4 (17 January 2021)","role":"content","text":"This is a maintenance release for version 1.0.\n\n- Highights:\n\n    This release improves overall stability and provides fixes for issues found after the previous release.\n\n- Bug fixes:\n\n    - #[458](https://github.com/RedisGears/RedisGears/pull/458) Crash on StreamReader when the stream is dropped during processing of the function.\n\n    - #[477](https://github.com/RedisGears/RedisGears/pull/477) Rare crash on accumulate step."},{"id":"v103-november-2020","title":"v1.0.3 (November 2020)","role":"content","text":"This is a maintenance release for version 1.0.\n\n- Highlights:\n\n    This release improves overall stability and provides fixes for issues found after the previous release.\n\n- Bug fixes:\n\n    - #[427](https://github.com/RedisGears/RedisGears/pull/427) Rare issue where messages might get lost and cause executions timeouts.\n\n    - #[434](https://github.com/RedisGears/RedisGears/pull/434) Triggering an execution on uninitialized cluster state should raise a cluster uninitialized error."},{"id":"v102-october-2020","title":"v1.0.2 (October 2020)","role":"content","text":"This is a maintenance release for version 1.0.\n\n- Highlights:\n\n    This release improves overall stability and provides fixes for issues found after the previous release.\n\n- Details:\n\n    - Minor Features:\n        - #[368](https://github.com/RedisGears/RedisGears/pull/368) Support for buffer input on `createTensorFromBlob` when integrating with [RedisAI](https://github.com/RedisAI/RedisAI) API.\n\n        - #[373](https://github.com/RedisGears/RedisGears/pull/373) Registered execution plans will not re-send their metadata when they are triggered (this feature reduces network overhead and improve performance of distributed registrations).\n\n        - #[371](https://github.com/RedisGears/RedisGears/pull/371) Full details on import requirement failure.\n\n    - Bug fixes:\n\n        - #[374](https://github.com/RedisGears/RedisGears/pull/374) Fix RedisAI `toFlatList` function to handle long long encoding.\n\n        - #[375](https://github.com/RedisGears/RedisGears/pull/375) Call `OnRegister` on RDBLoad.\n        \n        - #[388](https://github.com/RedisGears/RedisGears/pull/388) Fix crash on hello request failure (internal protocol between shards), a retry will be triggered after 1 second.\n\n        - #[395](https://github.com/RedisGears/RedisGears/pull/395) Fix rare wrong results on `aggregate` and `aggregateby`. Use deep copy on the zero value to avoid those incorrect results."},{"id":"v101-july-2020","title":"v1.0.1 (July 2020)","role":"content","text":"This is a maintenance release for version 1.0.\n\n- Highlights:\n\n    - *Import and Export requirements* - With this capability, you can export the python requirements present in RedisGears and import them into another instance using [gears-cli](https://github.com/RedisGears/gears-cli). \n    \n        This enables, amongst others, to import requirements to environments without internet access. Notice that the requirement should be exported from and imported into machines with the same OS and version. This is the recommended tool to use for productionising RedisGears functions (for example to be used in your CI).\n\n    - *Optimised requirements installation mechanism* - requirements will now be distributed once to each shard and not each time an execution is triggered.\n\n    - *RedisAI API fixes and additions* - fixes/additions related to the integration with RedisAI.\n\n- Details:\n\n    - Features:\n\n        - #[330](https://github.com/RedisGears/RedisGears/pull/330) `SendMsgRetries` configuration parameter indicating how many times RedisGears should try sending messages between shards.\n\n        - #[330](https://github.com/RedisGears/RedisGears/pull/330) `PythonInstallReqMaxIdleTime` configuration parameter for the maximum amount of time to wait for requirements to be installed.\n\n        - #[330](https://github.com/RedisGears/RedisGears/pull/330) `RG.PYDUMPREQ` command that output all the currently available python requirements.\n\n        - Added the following API functionality to the RedisAI integration:\n\n            - #[346](https://github.com/RedisGears/RedisGears/pull/346) Script run will now return a list of tensors.\n\n            - #[347](https://github.com/RedisGears/RedisGears/pull/347) Allow tensor creation from bytes.\n\n            - #[350](https://github.com/RedisGears/RedisGears/pull/350) Added mget and mset to get/set multiple tensors from/to the keyspace.\n\n            - #[345](https://github.com/RedisGears/RedisGears/pull/345) Free python GIL before calling redisAI model/script run.\n\n    - Bug fixes:\n\n        - #[325](https://github.com/RedisGears/RedisGears/pull/325) Circular reference that caused a logical memory leak.\n\n        - #[330](https://github.com/RedisGears/RedisGears/pull/330) Crash on wrong reply status.\n\n        - #[345](https://github.com/RedisGears/RedisGears/pull/345) Crash on python session (private data on rg.dumpregistration) 'tostr' function when there is no requirements.\n\n        - #[334](https://github.com/RedisGears/RedisGears/pull/334) Type check to RedisAI API to prevent potential crashes."},{"id":"v100-may-2020","title":"v1.0.0 (May 2020)","role":"content","text":"This is the first general availability (GA) release of RedisGears (v1.0.0).\n\n- Highlights:\n\n    - Program everything you want in Redis - With a built-in C-API and Python interpreter, RedisGears lets you use full-fledged Python scripts and libraries to process data inside Redis.\n\n    - Write once, deploy anywhere - Write your functions against a standalone Redis database and deploy them to production clusters—with no changes required.\n\n    - Run your serverless engine where your data lives - RedisGears lets you process events and streams faster by running in memory next to your data in Redis.\n\n    Bug fixes (compared to RC1):\n\n    - #[288](https://github.com/RedisGears/RedisGears/issues/288) dependencies with version will not crash the server.\n\n    - #[309](https://github.com/RedisGears/RedisGears/issues/309), #[313](https://github.com/RedisGears/RedisGears/issues/313) Prevent crashes on RedisAI intergration and update the low level C api of RedisAI.\n\n    - #[311](https://github.com/RedisGears/RedisGears/issues/311) Fix out-of-order reply in certain situations.\n\n    - #[321](https://github.com/RedisGears/RedisGears/issues/321) Return error when unknown argument is given.\n\n> [!NOTE]\n> - The version inside Redis will be 10000 or 1.0.0 in semantic versioning.\n> - This version requires the Redis version to be 6.0 and above."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisgears/redisgears-1.2-release-notes","title":"RedisGears 1.2 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgears/redisgears-1.2-release-notes/","summary":"Plugins and JVM support. Python async await. Override commands API. Register functions on key miss events. Tracks new statistics. Python profiler support. Extended RedisAI integration.","tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:33+01:00","page_type":"content","content_hash":"dc50e43de5207f342ffea2ca3287bef67cefd8ddde34fa35bc8e0cf48989aa46","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RedisGears v1.2.13 requires:\n\n- Minimum Redis compatibility version (database): 6.0.0\n- Minimum Redis Enterprise Software version (cluster): 6.0.12"},{"id":"1213-august-2025","title":"1.2.13 (August 2025)","role":"content","text":"This is a maintenance release for RedisGears 1.2.\n\nUpdate urgency: `LOW`: No need to upgrade unless there are new features or fixes.\n\nDetails\n\n- Added support for Ubuntu 22.\n- Ubuntu 16.04 and Ubuntu 18.04 are no longer supported.\n- RedisGears dependencies (Python) now include redis-py 5.0."},{"id":"1212-december-2024","title":"1.2.12 (December 2024)","role":"content","text":"This is a maintenance release for RedisGears 1.2.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails\n\n- Bug fixes:\n  - [#1125](https://github.com/redisgears/redisgears/pull/1125) The same registration ID might be generated twice. This may lead to an RDB corruption.\n  - [#1121](https://github.com/redisgears/redisgears/pull/1121) Recreate the virtual environment on startup. Fixes an issue where the virtual environment might point to the old version of the Python interpreter.\n  - [#1122](https://github.com/redisgears/redisgears/pull/1122) Clear the last error after a successful run to avoid confusion if the error is relevant.\n  - [#1122](https://github.com/redisgears/redisgears/pull/1122) Clear not-yet-started executions after unregister, pause, or error on registration occcurs. (MOD-8184)"},{"id":"1210-october-2024","title":"1.2.10 (October 2024)","role":"content","text":"This is a maintenance release for RedisGears 1.2.\n\nUpdate urgency: `LOW`: No need to upgrade unless there are new features or fixes.\n\nDetails\n\n- Bug fixes:\n  - [#1114](https://github.com/redisgears/redisgears/pull/1114) (REPLICAOF and Redis Enterprise A-A only) Cross slot violation. Avoid using `RM_Call` + `SCAN` command which might replicate multiple keys deletion inside a `MULTI EXEC` block when a lazy expire takes place\n\n- Notes:\n  - RHEL7 is no longer supported"},{"id":"129-march-2024","title":"1.2.9 (March 2024)","role":"content","text":"This is a maintenance release for RedisGears 1.2.\n\nUpdate urgency: `LOW`: No need to upgrade unless there are new features or fixes.\n\nDetails\n\n- Features:\n\n  - [#1087](https://github.com/RedisGears/RedisGears/pull/1087) Support for RHEL9\n  - [#986](https://github.com/RedisGears/RedisGears/pull/986) Added execution mode parameter to `run` function: allows choosing the execution mode: `sync`/`async`/`async_local` - same as when used with register\n\n- Bug fixes:\n\n  - [#1097](https://github.com/RedisGears/RedisGears/pull/1097) Fix stream reader missing notification due to wrong key name comparison (comparing using case sensitive)"},{"id":"127-july-2023","title":"1.2.7 (July 2023)","role":"content","text":"This is a maintenance release for RedisGears 1.2.\n\nUpdate urgency: `LOW`: No need to upgrade unless there are new features or fixes.\n\nDetails\n\n- Features:\n\n  - [#972](https://github.com/RedisGears/RedisGears/pull/972) Upgrade JVM to 17.0.7"},{"id":"126-march-2023","title":"1.2.6 (March 2023)","role":"content","text":"This is a maintenance release for RedisGears 1.2.\n\nUpdate urgency: `LOW` : No need to upgrade unless there are new features or fixes.\n\nDetails\n\n- Features:\n\n  - [#832](https://github.com/RedisGears/RedisGears/pull/832), [#844](https://github.com/RedisGears/RedisGears/pull/844) Added IPV6 support\n  - [#841](https://github.com/RedisGears/RedisGears/pull/841) Generate artifacts with dependencies included\n\n- Bug fixes:\n\n  - [#810](https://github.com/RedisGears/RedisGears/pull/810) Fix invalid memory access when checking if a trigger is already registered"},{"id":"v125-july-2022","title":"v1.2.5 (July 2022)","role":"content","text":"This is a maintenance release for RedisGears 1.2.\n\nUpdate urgency: `MODERATE` : Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n    - [#792](https://github.com/RedisGears/RedisGears/issues/792), [#798](https://github.com/RedisGears/RedisGears/pull/798) Execution was triggered infinitely when trimming is turned off.\n    - [#791](https://github.com/RedisGears/RedisGears/issues/791), [#796](https://github.com/RedisGears/RedisGears/pull/796) Stop triggering executions during pause even on failure.\n    - [#794](https://github.com/RedisGears/RedisGears/pull/794), [#797](https://github.com/RedisGears/RedisGears/pull/797) Use `PythonInstallationDir` configuration to find the virtual environment location on Redis Enterprise. (MOD-1734)"},{"id":"v124-may-2022","title":"v1.2.4 (May 2022)","role":"content","text":"This is a maintenance release for RedisGears 1.2.\n\nUpdate urgency: `LOW` : No need to upgrade unless there are new features you want to use.\n\nDetails:\n\n- Improvements:\n\n    - [#772](https://github.com/RedisGears/RedisGears/pull/772) Added the ability to upgrade a dependency at runtime with `FORCE_REQUIREMENTS_REINSTALLATION` on `RG.PYEXECUTE`.\n    - [#765](https://github.com/RedisGears/RedisGears/pull/765) Allow deactivating override Python allocators for performance improvements.\n\n- Bug fixes:\n\n    - [#761](https://github.com/RedisGears/RedisGears/issues/761), [#760](https://github.com/RedisGears/RedisGears/issues/760), [#778](https://github.com/RedisGears/RedisGears/pull/778) `StreamReader` fixes to pause and unregister stream processing."},{"id":"v123-april-2022","title":"v1.2.3 (April 2022)","role":"content","text":"This is a maintenance release for RedisGears 1.2.\n\nUpdate urgency: `LOW` : No need to upgrade unless there are new features you want to use.\n\nDetails:\n\n- Improvements:\n\n    - [#739](https://github.com/RedisGears/RedisGears/pull/739) Added TLS support\n    - [#734](https://github.com/RedisGears/RedisGears/pull/734) Pause/Unpause registrations\n    - [#741](https://github.com/RedisGears/RedisGears/pull/741) Added build for Python-only artifacts\n\n- Bug fixes:\n\n    - [#740](https://github.com/RedisGears/RedisGears/pull/740) Fix -nan value on registration stats"},{"id":"v122-february-2022","title":"v1.2.2 (February 2022)","role":"content","text":"This is the General Availability release of RedisGears 1.2."},{"id":"headlines","title":"Headlines","role":"content","text":"#### Plugins and JVM support\n\nRedisGears 1.2 comes with a new plugin mechanism that allows you to decide which languages you want to load into RedisGears. Currently, we support two languages: Python and Java (JVM languages). You can decide which language you want to use using the new `Plugin` configuration.\n\nFull documentation for JVM support can be found on the [Redis documentation website](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/jvm).\n\n#### Python async await support\n\nRedisGears provides support for Python coroutines. Each step of your gears function can now be a Python coroutine that will take the execution to the background or will wait for some event to happen. Refer to the following links for more information:\n\n- Async Await Support\n- Async Await Advanced Topics\n\n#### Override commands API\n\nYou can override Redis vanilla commands with a function. For more information, refer to the RedisGears command hooks documentation.\n\n#### Key miss event for read-through pattern\n\nRequested by many users, RedisGears 1.2 allows you to register functions on key miss event. One use case for this is to implement a read-through caching pattern. For more information about this topic, refer to the following links:\n\n- Key Miss Event in the RedisGears documentation.\n- [rghibernate](https://github.com/RedisGears/rghibernate) recipe that leverages the key miss event to implement read-through from external databases.\n\n#### Better visibility and analyzing tools\n\nWe improved the experience during the development phase by enabling better debugging and troubleshooting. There is still room for improvement but RedisGears 1.2 makes the first steps toward a simpler API that is easier to use. This new version allows you to name your code and upgrade it with a single Redis command. For more information, refer to the upgrade section of the RedisGears introduction documentation.\n\nRedisGears now tracks the following new statistics to better analyze your registrations:\n\n- `lastRunDurationMS` - duration in milliseconds of the last execution\n- `totalRunDurationMS` - total runtime of all executions in milliseconds\n- `avgRunDurationMS` - average execution runtime in milliseconds\n\nFor streams, RedisGears also tracks the following data:\n\n- `lastEstimatedLagMS` - gives the last batch lag (the time difference between the first batch entry in the stream and the time the entire batch finished processing)\n- `avgEstimatedLagMS` - average of the `lastEstimatedLagMS` field.\n\nThe `RG.DUMPREGISTRATIONS` command exposes these new statistics.\n\nRedisGears 1.2 also adds support for a Python profiler, specifically [`cProfile`](https://docs.python.org/3.7/library/profile.html#module-cProfile). For more information, refer to the documentation for the following commands:\n\n- `RG.PYPROFILE STATS`\n- `RG.PYPROFILE RESET`\n\n#### RedisAI integration\n\nAlthough RedisAI integration was already supported in v1.0, RedisGears 1.2 adds official support for all capabilities in RedisAI v1.2. The API was extended to support RedisAI DAG and was combined with the new async await API to achieve the best performance possible."},{"id":"details","title":"Details","role":"content","text":"Bug fixes (since 1.0.9):\n\n- [#557](https://github.com/RedisGears/RedisGears/pull/557), [#554](https://github.com/RedisGears/RedisGears/issues/554) `RG.CONFIGGET` returns user-defined configuration\n- [#572](https://github.com/RedisGears/RedisGears/pull/572) Lock Redis GIL when creating RedisAI DAG\n- [#661](https://github.com/RedisGears/RedisGears/pull/661), [#536](https://github.com/RedisGears/RedisGears/issues/536) Added `RG.TRIGGERONKEY`\n- [#650](https://github.com/RedisGears/RedisGears/issues/650) Do not propagate `MULTI EXEC` on Redis 7\n- [#671](https://github.com/RedisGears/RedisGears/pull/671), [#558](https://github.com/RedisGears/RedisGears/issues/558) Wait for cluster to be initialized when reading stream data\n- [#656](https://github.com/RedisGears/RedisGears/pull/656) Stream reader creates more than one execution on a stream\n- [#676](https://github.com/RedisGears/RedisGears/pull/676) Globals dictionary not set correctly after deserialization\n- [#665](https://github.com/RedisGears/RedisGears/issues/665), [#679](https://github.com/RedisGears/RedisGears/pull/679) Allow case-insensitive event type on command reader\n- [#697](https://github.com/RedisGears/RedisGears/pull/697) `hashtag()` function for Redis Enterprise\n- [#688](https://github.com/RedisGears/RedisGears/pull/688), [#545](https://github.com/RedisGears/RedisGears/issues/545) Check `REDISMODULE_CTX_FLAGS_DENY_BLOCKING` flag before blocking the client\n\n> [!NOTE]\n> - This is the first GA version of 1.2. The version inside Redis is 1.2.2 in semantic versioning. Since the version of a module in Redis is numeric, we could not add a GA flag.\n>\n> - Minimum Redis version: 6.0.0"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph","title":"RedisGraph release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/","summary":"RedisGraph release notes for version 2.10 and earlier","tags":["docs","operate","stack"],"last_updated":"2024-02-26T13:11:00+01:00","children":[{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes","summary":"Introduces new path algorithms, additional expressivity (constructs and functions), performance improvements, and bug fixes.","title":"RedisGraph 2.10 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.8-release-notes","summary":"Introduces multi-labeled nodes, indexes over relationship properties, and additional expressivity (Cypher construct, functions, and operators). Major performance enhancements. Many bug fixes.","title":"RedisGraph 2.8 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.8-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.4-release-notes","summary":"Added Map and Geospatial Point data types.","title":"RedisGraph 2.4 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.4-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.2-release-notes","summary":"Support for scaling reads, OPTIONAL MATCH, query caching, and GRAPH.SLOWLOG.","title":"RedisGraph 2.2 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.2-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.0-release-notes","summary":"Enabled graph-aided search and graph visualisation. Cypher coverage. Performance improvements.","title":"RedisGraph 2.0 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.0-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-1.2-release-notes","summary":"Support multiple relationships of the same type R connecting two nodes. Lexer elides escape characters in string creation. Performance improvements.","title":"RedisGraph 1.2 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-1.2-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-1.0-release-notes","summary":"Fixed memory leaks. Support ‘*’ within RETURN clause. Added TYPE function. Initial support for UNWIND clause.","title":"RedisGraph 1.0 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-1.0-release-notes/"}],"page_type":"content","content_hash":"1c8f79b27a3704de94c675a1fecf5ba5f2812db92d99af9506de3ffe0659a9da","sections":[{"id":"content","title":"Content","role":"content","text":"Redis, Inc. has announced the end of life for RedisGraph. We will carry out the process gradually and in accordance with our commitment to our customers. See [details](https://redis.com/blog/redisgraph-eol/).\n\n\n\n| Version (Release date) | Major changes | Min Redis version | Min cluster version |\n| --- | --- | --- | --- |\n| [v2.10 (November 2022)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes/) | Introduces new path algorithms, additional expressivity (constructs and functions), performance improvements, and bug fixes. | 6.2.0 | 6.2.8 |\n| [v2.8 (February 2022)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.8-release-notes/) | Introduces multi-labeled nodes, indexes over relationship properties, and additional expressivity (Cypher construct, functions, and operators). Major performance enhancements. Many bug fixes. | 6.2.0 | 6.2.8 |\n| [v2.4 (March 2021)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.4-release-notes/) | Added Map and Geospatial Point data types. | 6.0.0 | 6.0.8 |\n| [v2.2 (November 2020)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.2-release-notes/) | Support for scaling reads, OPTIONAL MATCH, query caching, and GRAPH.SLOWLOG. | 5.0.7 | 6.0.8 |\n| [v2.0 (January 2020)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.0-release-notes/) | Enabled graph-aided search and graph visualisation. Cypher coverage. Performance improvements. | 5.0.7 | 5.4.11 |\n| [v1.2 (April 2019)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-1.2-release-notes/) | Support multiple relationships of the same type R connecting two nodes. Lexer elides escape characters in string creation. Performance improvements. | 4.0.0 | 5.0.0 |\n| [v1.0 (November 2018)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-1.0-release-notes/) | Fixed memory leaks. Support ‘*’ within RETURN clause. Added TYPE function. Initial support for UNWIND clause. | 4.0.0 | 5.0.0 |"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-1.0-release-notes","title":"RedisGraph 1.0 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-1.0-release-notes/","summary":"Fixed memory leaks. Support ‘*’ within RETURN clause. Added TYPE function. Initial support for UNWIND clause.","aliases":["/modules/redisgraph/release-notes/redisgraph-1.0-release-notes/"],"tags":["Modules"],"last_updated":"2024-01-30T17:50:06+01:00","page_type":"content","content_hash":"84d8eecf7f269f507da176a116977b54365787649b330ad6d7c16e286395f77c","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RedisGraph v1.0.15 requires:\n\n- Minimum Redis compatibility version (database): 4.0.0\n- Minimum Redis Enterprise Software version (cluster): 5.0.0"},{"id":"v1015-march-2019","title":"v1.0.15 (March 2019)","role":"content","text":"- GraphBLAS 2.3.0 [release notes](https://github.com/RedisLabsModules/RedisGraph/pull/390#issuecomment-470620353)\n- WITH clause: Allows query parts to be chained together, piping the results from one to be used as starting points or criteria of the next. [ref p.78](https://s3.amazonaws.com/artifacts.opencypher.org/openCypher9.pdf)\n- Enhancements\n    - #[393](https://github.com/RedisGraph/RedisGraph/issues/393) Discard distinct when performing aggregation\n- Minor bugfixes\n    - #[359](https://github.com/RedisGraph/RedisGraph/issues/359) Adding ORDER BY changes the number of returned hits when used in combination with LIMIT\n    - #[363](https://github.com/RedisGraph/RedisGraph/issues/363) Remove graph entity property when it is set to null\n    - #[386](https://github.com/RedisGraph/RedisGraph/issues/386) Return updated values on queries that modify data"},{"id":"v1014-21-february-2019","title":"v1.0.14 (21 February 2019)","role":"content","text":"License update, REDIS SOURCE AVAILABLE LICENSE AGREEMENT."},{"id":"v1013-6-february-2019","title":"v1.0.13 (6 February 2019)","role":"content","text":"Reuse attribute name to avoid duplication"},{"id":"v1012-28-january-2019","title":"v1.0.12 (28 January 2019)","role":"content","text":"Traverse direction optimization to reduce number of matrix transpose"},{"id":"v1011-17-january-2019","title":"v1.0.11 (17 January 2019)","role":"content","text":"- Removed OpenMP requirement\n- Traverse from multiple nodes concurrently"},{"id":"v1010-9-january-2019","title":"v1.0.10 (9 January 2019)","role":"content","text":"Update indices when MERGE creates new entities"},{"id":"v109-6-january-2019","title":"v1.0.9 (6 January 2019)","role":"content","text":"- Support for mixing MERGE and SET clauses\n- Granular writer locking\n- Fix graph serialization error in duplicate edge handling"},{"id":"v108-24-december-2018","title":"v1.0.8 (24 December 2018)","role":"content","text":"- Index utilization when performing cartesian product\n- Increase usage of rm_malloc functions in module\n- Allow serialization of NULL-valued properties\n- Support for multiple relationship types"},{"id":"v107-18-december-2018","title":"v1.0.7 (18 December 2018)","role":"content","text":"- Bulk-insert support unicode\n- Bulk-insert better progress reporting\n- Multiple relationship types"},{"id":"v105-15-december-2018","title":"v1.0.5 (15 December 2018)","role":"content","text":"Bulk insert supports ID specifying + relationships attributes"},{"id":"v104-9-december-2018","title":"v1.0.4 (9 December 2018)","role":"content","text":"- Compact GraphBLAS, using structural semiring"},{"id":"v103-2-december-2018","title":"v1.0.3 (2 December 2018)","role":"content","text":"- GraphBLAS 2.2\n- Multiple CREATE clauses\n- Multiple MATCH clauses\n- Bug fixes:\n    - Replace operations appropriately when rewriting execution plan\n    - Entity returned from datablock should have its internals cleared\n    - Loaded triemap strings are not guaranteed space for a null terminator"},{"id":"v102-25-november-2018","title":"v1.0.2 (25 November 2018)","role":"content","text":"- Bug fix #[249](https://github.com/RedisGraph/RedisGraph/issues/249) reset operation within execution plan should propagate upwards"},{"id":"v101-22-november-2018","title":"v1.0.1 (22 November 2018)","role":"content","text":"- Resolved a number of memory leaks\n- Support '*' within RETURN clause\n- Add TYPE function\n- Initial support for UNWIND clause"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-1.2-release-notes","title":"RedisGraph 1.2 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-1.2-release-notes/","summary":"Support multiple relationships of the same type R connecting two nodes. Lexer elides escape characters in string creation. Performance improvements.","tags":["docs","operate","stack"],"last_updated":"2024-08-08T16:28:23-05:00","page_type":"content","content_hash":"060c6e794d4fbfb2300995e7ac556c5a7fe6e42eda4ac65b0fbc6e3c04d5c054","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RedisGraph v1.2.2 requires:\n\n- Minimum Redis compatibility version (database): 4.0.0\n- Minimum Redis Enterprise Software version (cluster): 5.0.0"},{"id":"v122-may-2019","title":"v1.2.2 (May 2019)","role":"content","text":"Update urgency: Medium\nThis is a maintenance release for version 1.2.\n\nThis release improves overall stability and provides fixes for issues found after the previous release."},{"id":"v120-april-2019","title":"v1.2.0 (April 2019)","role":"content","text":"This is a major release for RedisGraph.\nCompared to previous release 1.0.15:\n\n- Added functionality\n    - #[452](https://github.com/RedisGraph/RedisGraph/issues/452) Support multiple relationships of the same type R connecting two nodes\n    - #[392](https://github.com/RedisGraph/RedisGraph/issues/392) Lexer elides escape characters in string creation: support for property values with `'` or `\"`\n- Performance improvements\n    - #[442](https://github.com/RedisGraph/RedisGraph/issues/442) Seek graph entity by id: `MATCH (n) WHERE ID(n) = 5 RETURN n`\n    - #[422](https://github.com/RedisGraph/RedisGraph/issues/422) `MATCH (n) RETURN COUNT(n)` is now O(1)\n    - #[421](https://github.com/RedisGraph/RedisGraph/issues/421) and #[459](https://github.com/RedisGraph/RedisGraph/issues/459) Lazy loading of matrices on subsequent writes of relationships and nodes\n    - #[399](https://github.com/RedisGraph/RedisGraph/issues/399) Single transpose of matrices\n- Minor Bugfixes:\n    - #[456](https://github.com/RedisGraph/RedisGraph/issues/456) Memory leaks\n    - #[439](https://github.com/RedisGraph/RedisGraph/issues/439) When label doesn't exists, the node count should return 0 #[439](https://github.com/RedisGraph/RedisGraph/issues/439)\n    - #[435](https://github.com/RedisGraph/RedisGraph/issues/435) Referencing non existing properties #[435](https://github.com/RedisGraph/RedisGraph/issues/435)\n\nCompared to 1.0:\n\n- Added functionality\n    - #[390](https://github.com/RedisGraph/RedisGraph/issues/390) GraphBLAS 2.3.0 [release notes](https://github.com/RedisLabsModules/RedisGraph/pull/390#issuecomment-470620353)\n    - #[452](https://github.com/RedisGraph/RedisGraph/issues/452) Support multiple relationships of the same type R connecting two nodes\n    - #[392](https://github.com/RedisGraph/RedisGraph/issues/392) Lexer elides escape characters in string creation: support for property values with ' or \"\n    - Enhanced cypher\n        - Initial support for WITH clause: Allows query parts to be chained together, piping the results from one to be used as starting points or criteria of the next. [ref p.78](https://s3.amazonaws.com/artifacts.opencypher.org/openCypher9.pdf)\n        - #[247](https://github.com/RedisGraph/RedisGraph/issues/247) Initial support for UNWIND\n        - #[244](https://github.com/RedisGraph/RedisGraph/issues/244) RETURN * (give me everything bro)\n        - #[236](https://github.com/RedisGraph/RedisGraph/issues/236) TYPE function (returns the type of a relationship)\n        - #[288](https://github.com/RedisGraph/RedisGraph/issues/288) Querying patterns where a relationship between two nodes can be of different types. ()-[:A|:B]->()\n        - #[252](https://github.com/RedisGraph/RedisGraph/issues/252) Multiple MATCH clauses\n        - #[327](https://github.com/RedisGraph/RedisGraph/issues/327) Multiple CREATE clauses\n        - #[305](https://github.com/RedisGraph/RedisGraph/issues/305) MERGE + SET\n        - #[348](https://github.com/RedisGraph/RedisGraph/issues/348) Smaller memory footprint - encode properties\n    - [Faster and better bulk loading](https://github.com/RedisGraph/redisgraph-bulk-loader)\n- Performance improvements\n    - #[442](https://github.com/RedisGraph/RedisGraph/issues/442) Seek graph entity by id: MATCH (n) WHERE ID(n) = 5 RETURN n\n    - #[422](https://github.com/RedisGraph/RedisGraph/issues/422) MATCH (n) RETURN COUNT(n) is now O(1)\n    - #[421](https://github.com/RedisGraph/RedisGraph/issues/421) and #[459](https://github.com/RedisGraph/RedisGraph/issues/459) Lazy loading of matrices on subsequent writes of relationships and nodes\n    - #[399](https://github.com/RedisGraph/RedisGraph/issues/399) Single transpose of matrices\n    - #[393](https://github.com/RedisGraph/RedisGraph/issues/393) Discard distinct when performing aggregation\n    - #[289](https://github.com/RedisGraph/RedisGraph/issues/289) Index utilization when performing Cartesian product\n    - #[308](https://github.com/RedisGraph/RedisGraph/issues/308) Granular writer locking\n- Most noticeable bugfixes\n    - #[456](https://github.com/RedisGraph/RedisGraph/issues/456) Memory leaks\n    - #[439](https://github.com/RedisGraph/RedisGraph/issues/439) When label doesn't exists, the node count should return 0\n    - #[435](https://github.com/RedisGraph/RedisGraph/issues/435) Referencing non existing properties\n    - #[359](https://github.com/RedisGraph/RedisGraph/issues/359) Adding ORDER BY changes the number of returned hits when used in combination with LIMIT\n    - #[363](https://github.com/RedisGraph/RedisGraph/issues/363) Remove graph entity property when it is set to null\n    - #[386](https://github.com/RedisGraph/RedisGraph/issues/386) Return updated values on queries that modify data\n    - #[249](https://github.com/RedisGraph/RedisGraph/issues/249) reset operation within execution plan should propagate upwards\n    - #[259](https://github.com/RedisGraph/RedisGraph/issues/259) Replace operations appropriately when rewriting execution plan\n    - #[262](https://github.com/RedisGraph/RedisGraph/issues/262) Entity returned from datablock should have its internals cleared\n    - #[264](https://github.com/RedisGraph/RedisGraph/issues/264) Loaded triemap strings are not guaranteed space for a null terminator"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.0-release-notes","title":"RedisGraph 2.0 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.0-release-notes/","summary":"Enabled graph-aided search and graph visualisation. Cypher coverage. Performance improvements.","tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:33+01:00","page_type":"content","content_hash":"6dd257c69c28eeea42dc512e2fae3fe16202495693d6f010f9ca5ebb833e8121","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RedisGraph v2.0.21 requires:\n\n- Minimum Redis compatibility version (database): 5.0.7\n- Minimum Redis Enterprise Software version (cluster): 5.4.11"},{"id":"v2021-october-2020","title":"v2.0.21 (October 2020)","role":"content","text":"This is a maintenance release for version 2.0.\n\nHeadlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n\nDetails:\n\n- Minor enhancements:\n\n    - #[1326](https://github.com/RedisGraph/RedisGraph/pull/1326), #[1330](https://github.com/RedisGraph/RedisGraph/pull/1330) Enable comments in Cypher queries.\n\n- Bug fixes:\n\n- #[1338](https://github.com/RedisGraph/RedisGraph/pull/1338) Update parser to support negative values.\n- #[1319](https://github.com/RedisGraph/RedisGraph/pull/1319) Replace assertion with runtime error - execution plan building process is unable locate an operation suitable for a filter.\n- #[1184](https://github.com/RedisGraph/RedisGraph/pull/1184) EXPLAIN and PROFILE are invalid clauses. They have counterpart commands `GRAPH.EXPLAIN` and `GRAPH.PROFILE`\n- #[1212](https://github.com/RedisGraph/RedisGraph/pull/1212) Emit error on the creation of undirected edges."},{"id":"v2020-september-2020","title":"v2.0.20 (September 2020)","role":"content","text":"This is a maintenance release for version 2.0.\n\nHeadlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n\nDetails:\n\n- Minor updates:\n\n    - #[1315](https://github.com/RedisGraph/RedisGraph/pull/1315) RediSearch 1.8.3\n    - #[1276](https://github.com/RedisGraph/RedisGraph/pull/1276) Graph info is added to Redis crash report.\n    - #[1265](https://github.com/RedisGraph/RedisGraph/pull/1265) Debug assertion functionality."},{"id":"v2019-august-2020","title":"v2.0.19 (August 2020)","role":"content","text":"This is a maintenance release for version 2.0.\n\nHeadlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n\nDetails:\n\n- Minor updates:\n    - #[1229](https://github.com/RedisGraph/RedisGraph/pull/1229) Upgrade RediSearch dependency to 1.8.2\n- Performance enhancements:\n    - #[1218](https://github.com/RedisGraph/RedisGraph/pull/1218) Enable parameters support for ID-based entity retrieval\n    - #[1242](https://github.com/RedisGraph/RedisGraph/pull/1242) Improve update logic, update RediSearch indices only once per update\n    - #[1226](https://github.com/RedisGraph/RedisGraph/pull/1226) Let RediSearch perform document deletion when replacing a document\n- Bug Fixes:\n    - #[1223](https://github.com/RedisGraph/RedisGraph/pull/1223) Resolve race condition in accessing/updating attribute maps.\n    - #[1196](https://github.com/RedisGraph/RedisGraph/pull/1196) Enable RediSearch [Garbage Collection](https://redis.io/docs/latest/develop/ai/search-and-query/administration/gc)"},{"id":"v2015-25-june-2020","title":"v2.0.15 (25 June 2020)","role":"content","text":"This is a maintenance release for version 2.0.\n\nHeadlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n\nDetails:\n\n- Bug Fixes:\n    - #[1159](https://github.com/RedisGraph/RedisGraph/pull/1159) `IN` operator didn't result in an index array lookup with parameterised array (`IN $param`)."},{"id":"v2014-22-june-2020","title":"v2.0.14 (22 June 2020)","role":"content","text":"This is a maintenance release for version 2.0.\n\nHeadlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n\nDetails:\n\n- Bug Fixes:\n    - #[1157](https://github.com/RedisGraph/RedisGraph/pull/1157) Index search not using query parameters."},{"id":"v2013-15-june-2020","title":"v2.0.13 (15 June 2020)","role":"content","text":"This is a maintenance release for version 2.0.\n\nHeadlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n\nDetails:\n\n- Enhancements:\n    - #[1118](https://github.com/RedisGraph/RedisGraph/pull/1118) Added module configuration parameters for the number of threads in OpenMP and concurrent queries.\n    - #[1121](https://github.com/RedisGraph/RedisGraph/pull/1121) [RediSearch](https://github.com/RediSearch/RediSearch/) v1.8.1"},{"id":"v2012-may-2020","title":"v2.0.12 (May 2020)","role":"content","text":"This is a maintenance release for version 2.0.\n\nHeadlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n\nDetails:\n\n- Enhancements:\n    - #[1103](https://github.com/RedisGraph/RedisGraph/pull/1103) RediSearch 1.8. This upgrade will reduce the resources required for Garbage Collection of indices inside RedisGraph."},{"id":"v2011-april-2020","title":"v2.0.11 (April 2020)","role":"content","text":"This is a maintenance release for version 2.0.\n\nHeadlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n\nDetails:\n\n- Minor Enhancements:\n    - #[1056](https://github.com/RedisGraph/RedisGraph/pull/1056) Added `DISTINCT` support for paths and arrays.\n- Minor Bugfixes:\n    - #[1056](https://github.com/RedisGraph/RedisGraph/pull/1056) Streamlined logic for uniquing entities.\n    - #[1056](https://github.com/RedisGraph/RedisGraph/pull/1056) Enabled comparison routine for paths."},{"id":"v2010-29-march-2020","title":"v2.0.10 (29 March 2020)","role":"content","text":"Headlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n\nDetails:\n\n- Minor Enhancements:\n    - #[1035](https://github.com/redisgraph/redisgraph/issues/1035) - RediSearch version [1.6.11](https://github.com/RediSearch/RediSearch/releases/tag/v1.6.11).\n- Bugfixes:\n    - #[1017](https://github.com/redisgraph/redisgraph/issues/1017), #[1019](https://github.com/redisgraph/redisgraph/issues/1019) - Algebraic expressions correctness.\n    - #[1020](https://github.com/redisgraph/redisgraph/issues/1020) - Support parameterised SKIP and LIMIT."},{"id":"v209-19-march-2020","title":"v2.0.9 (19 March 2020)","role":"content","text":"Headlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n\nDetails:\n\n- Bugfixes:\n    - #[1028](https://github.com/redisgraph/redisgraph/issues/1028) Ensure proper placement of Index Scans when partially replacing Filter ops."},{"id":"v208-18-march-2020","title":"v2.0.8 (18 March 2020)","role":"content","text":"Headlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n\nDetails:\n\n- Bugfixes:\n    - #[1023](https://github.com/redisgraph/redisgraph/issues/1023) Fix regression in checking argument counts to GRAPH endpoints."},{"id":"v206-15-march-2020","title":"v2.0.6 (15 March 2020)","role":"content","text":"Headlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n\nDetails:\n\n- #[897](https://github.com/redisgraph/redisgraph/issues/897) GRAPH.SLOWLOG\n- #[1004](https://github.com/redisgraph/redisgraph/issues/1004) Re-enable GRAPH.PROFILE\n- #[917](https://github.com/redisgraph/redisgraph/issues/917), #[991](https://github.com/redisgraph/redisgraph/issues/991), #[940](https://github.com/redisgraph/redisgraph/issues/940), #[984](https://github.com/redisgraph/redisgraph/issues/984) Memory leak fixes\n- #[925](https://github.com/redisgraph/redisgraph/issues/925) Bug fix within RediSearch\n- #[1001](https://github.com/redisgraph/redisgraph/issues/1001) Bug fix label scan invalid range"},{"id":"v205-23-february-2020","title":"v2.0.5 (23 February 2020)","role":"content","text":"Headlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n\nDetails:\n\n- Enhancement:\n    - #[955](https://github.com/redisgraph/redisgraph/issues/955)  Switch OR,AND boolean semiring to ANY,PAIR."},{"id":"v204-23-february-2020","title":"v2.0.4 (23 February 2020)","role":"content","text":"Details:\n\n- Enhancement:\n    - Reduce GraphBLAS size"},{"id":"v202-23-february-2020","title":"v2.0.2 (23 February 2020)","role":"content","text":"Headlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n\nDetails:\n\n- Improved error reporting:\n    - #[925](https://github.com/redisgraph/redisgraph/issues/925) RediSearch query error reporting\n    - #[919](https://github.com/redisgraph/redisgraph/issues/919) Added non existing entity runtime error\n- Enhancements:\n    - #[942](https://github.com/redisgraph/redisgraph/issues/942) Update GraphBLAS version (3.2.0)\n    - #[922](https://github.com/redisgraph/redisgraph/issues/922) Filter tree compaction\n    - #[906](https://github.com/redisgraph/redisgraph/issues/906) Optimize cartesian product\n    - #[898](https://github.com/redisgraph/redisgraph/issues/898) Granular matrix locking\n- Bug fixes:\n    - #[917](https://github.com/redisgraph/redisgraph/issues/917) #[940](https://github.com/redisgraph/redisgraph/issues/940) Resolve memory leaks\n    - #[938](https://github.com/RedisGraph/RedisGraph/pull/938) Label matrix should be fetch right before eval"},{"id":"v20-ga-201---january-2020","title":"v2.0 GA (2.0.1 - January 2020)","role":"content","text":"This is the General Availability (GA) release of RedisGraph 2.0 (2.0.1)!\n\nHeadlines:\n\n- Full Text Search (FTS) enabling Graph-aided Search.\n- Full graph response enabling visualisation.\n- Substantial amount of Cypher coverage.\n- Performance improvements of up to 4x compared to RedisGraph 1.2.\n\n(We will blog about this release soon including performance improvements results and the link here)\n\nFull details:\n\n- Major features\n    - #[339](https://github.com/RedisGraph/RedisGraph/issues/339) Full Graph Response.  RedisGraph now allows to return Graph entities such as Nodes and Relationships.  This feature also enables graph visualisation.\n    - #[558](https://github.com/RedisGraph/RedisGraph/issues/558) Indexing functionality replaced by [RediSearch](https://redisearch.io). This results in support for\n        - compound indices\n        - full text search\n        - graph-aided search\n    - #[691](https://github.com/RedisGraph/RedisGraph/issues/691) RediSearch index is used with `IN` operator on indexed properties.\n    - #[488](https://github.com/RedisGraph/RedisGraph/issues/488) Replace flex/lemon parser with [libcypher-parser](https://github.com/cleishm/libcypher-parser).\n    - #[574](https://github.com/RedisGraph/RedisGraph/issues/574) Introduction of the array data type. This introduces significant support of Cypher as well as properties can be arrays on both nodes and relationships.\n    - Cypher\n        - #[714](https://github.com/RedisGraph/RedisGraph/issues/714) `MERGE` can be combined with any other clause. `MERGE` will take into account bounded entities.\n        - #[786](https://github.com/RedisGraph/RedisGraph/issues/786) Support for passing all supported graph data types (Node, Relationship, Array,...) as arguments to procedure calls, rather than requiring all arguments to be Strings.\n        - #[708](https://github.com/RedisGraph/RedisGraph/issues/708) Named path support.\n        - #[717](https://github.com/RedisGraph/RedisGraph/issues/717) `count(*)`\n        - #[730](https://github.com/RedisGraph/RedisGraph/issues/730) `UNION`\n        - #[757](https://github.com/RedisGraph/RedisGraph/issues/757) `coalesce`\n        - #[658](https://github.com/RedisGraph/RedisGraph/issues/658) Support for `COUNT DISTINCT`\n        - #[574](https://github.com/RedisGraph/RedisGraph/issues/574) with the array data support comes `IN`, `collect`, `head`,`range`, `reverse`, `size`, `tail`\n        - #[624](https://github.com/RedisGraph/RedisGraph/issues/624) `randomUUID()`\n        - #[632](https://github.com/RedisGraph/RedisGraph/issues/632) `IS NULL` and `IS NOT NULL`.\n        - #[594](https://github.com/RedisGraph/RedisGraph/issues/594) Support dynamic inline properties for `CREATE` and `MERGE` patterns.\n        - #[583](https://github.com/RedisGraph/RedisGraph/issues/583) `NOT` Operator.\n        - #[569](https://github.com/RedisGraph/RedisGraph/issues/569) `timestamp()` function.\n        - #[586](https://github.com/RedisGraph/RedisGraph/issues/586) `CASE WHEN` (simple form).\n        - #[582](https://github.com/RedisGraph/RedisGraph/issues/582) `contains` function.\n        - #[596](https://github.com/RedisGraph/RedisGraph/issues/596) `indegree` and `outdegree` functions for nodes.\n        - #[587](https://github.com/RedisGraph/RedisGraph/issues/587) improved boolean logic.\n        - #[539](https://github.com/RedisGraph/RedisGraph/issues/539) Reusable entities in pattern matching.\n    - #[668](https://github.com/RedisGraph/RedisGraph/issues/668) PageRank support.\n    - #[713](https://github.com/RedisGraph/RedisGraph/issues/713) Parameterised queries support. Most client drivers are updated. This enables future performance enhancements for query caching.\n    - #[662](https://github.com/RedisGraph/RedisGraph/issues/662) Support for AOF.\n- Enhancements\n    - #[752](https://github.com/RedisGraph/RedisGraph/issues/752) Use GraphBLAS 3.1.1.\n    - #[674](https://github.com/RedisGraph/RedisGraph/issues/674) `GRAPH.QUERY` will not fail even if graph doesn't exist.\n    - #[613](https://github.com/RedisGraph/RedisGraph/issues/613) Runtime arithmetic error handling.\n- Performance Improvements\n    - #[773](https://github.com/RedisGraph/RedisGraph/issues/773) Bulk deletion of relationships. Deletion of relationships within the same query will be efficiently handled by batch operations in GraphBLAS.\n    - #[783](https://github.com/RedisGraph/RedisGraph/issues/783) Better replication support. Only write queries that mutate the graph or create indices will be replicated to AOF and slaves.\n    - #[783](https://github.com/RedisGraph/RedisGraph/issues/783) Better commit flow. The Redis Global Lock and RedisGraphs's R/W lock are released once writes are done. Fixed duplicate replications in queries with multiple write segments.\n    - #[640](https://github.com/RedisGraph/RedisGraph/issues/640) GraphBlas to support OpenMP.\n    - #[532](https://github.com/RedisGraph/RedisGraph/issues/532) #[535](https://github.com/RedisGraph/RedisGraph/issues/535) Counting edges of given type by reducing matrices.\n    - #[534](https://github.com/RedisGraph/RedisGraph/issues/534) #[571](https://github.com/RedisGraph/RedisGraph/issues/571) Query parsing and `GRAPH.EXPLAIN` in dedicated thread.\n    - #[550](https://github.com/RedisGraph/RedisGraph/issues/550) #[555](https://github.com/RedisGraph/RedisGraph/issues/555) Optimize cartesian product by reducing to join.\n    - #[641](https://github.com/RedisGraph/RedisGraph/issues/641) Cartesian products with multiple incoming streams >2 can now be optimized with \"HashJoin\".\n- Bugfixes\n    - #[783](https://github.com/RedisGraph/RedisGraph/issues/783) Master-replica replication - slaves were dropping index mutations caused by procedure calls.\n    - #[785](https://github.com/RedisGraph/RedisGraph/issues/785) Solved several major memory leaks.\n    - #[795](https://github.com/RedisGraph/RedisGraph/issues/795) The underlying graph object's attributes were not updated on `RENAME`.\n    - #[720](https://github.com/RedisGraph/RedisGraph/issues/720) Validation of function calls in `WITH` and `CREATE` clauses e.g. `CREATE (a {v: fake()})`.\n    - #[732](https://github.com/RedisGraph/RedisGraph/issues/732), #[736](https://github.com/RedisGraph/RedisGraph/issues/736), #[747](https://github.com/RedisGraph/RedisGraph/issues/747) Detect cycles in graph and generate algebraic expression.\n    - #[734](https://github.com/RedisGraph/RedisGraph/issues/734) Fix access of uninitialised GrB_Info.\n    - #[735](https://github.com/RedisGraph/RedisGraph/issues/735) Index scans with `IN` filters did not compare with strings properly,\n    - #[758](https://github.com/RedisGraph/RedisGraph/issues/758) Validate that during `UNION` all return clauses are annotated.\n    - #[412](https://github.com/RedisGraph/RedisGraph/issues/412) Better handling for `GRAPH.DELETE`.\n    - #[537](https://github.com/RedisGraph/RedisGraph/issues/537) Wrong row index within expand-into, enabled few TCK tests.\n    - #[591](https://github.com/RedisGraph/RedisGraph/issues/591) Emit validation error when an alias refers to both nodes and edges.\n    - #[606](https://github.com/RedisGraph/RedisGraph/issues/606) Fix Record ID of edges held in ExpandInto op.\n    - #[607](https://github.com/RedisGraph/RedisGraph/issues/607) All ops NULL-set variables in their free routines (memory).\n    - #[893](https://github.com/RedisGraph/RedisGraph/issues/893) Preserve the children array order when replacing operations.\n\n> [!NOTE]\n> The version inside Redis will be 20001 or 2.0.1 in semantic versioning."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes","title":"RedisGraph 2.10 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes/","summary":"Introduces new path algorithms, additional expressivity (constructs and functions), performance improvements, and bug fixes.","tags":["docs","operate","stack"],"last_updated":"2023-12-14T15:27:49+01:00","page_type":"content","content_hash":"fc4f5a7b4436244d7f8fce24185df67bdb404d024248c357f4bd44981e733799","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RedisGraph v2.10.15 requires:\n\n- Minimum Redis compatibility version (database): 6.2.0\n- Minimum Redis Enterprise Software version (cluster): 6.2.8"},{"id":"v21015-september-2023","title":"v2.10.15 (September 2023)","role":"content","text":"This is a maintenance release for RedisGraph 2.10.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n  - [#3172](https://github.com/RedisGraph/RedisGraph/pull/3172) Crash on certain write queries (MOD-5645)\n  - [#3179](https://github.com/RedisGraph/RedisGraph/pull/3179) High CPU on idle\n\n- Improvements:\n\n  - [#3184](https://github.com/RedisGraph/RedisGraph/pull/3184) Revert breaking change introduced in 2.10: `toString` supports list argument again\n  - [#3185](https://github.com/RedisGraph/RedisGraph/pull/3185) Don’t write AUX field in the RDB file when there are no graphs in the keyspace"},{"id":"v21012-july-2023","title":"v2.10.12 (July 2023)","role":"content","text":"This is a maintenance release for RedisGraph 2.10.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n  - [#3129](https://github.com/RedisGraph/RedisGraph/pull/3129) Crash on certain queries (`INDEX SCAN` followed by `DEL` followed by `SET`) (MOD-5430)"},{"id":"v21011-june-2023","title":"v2.10.11 (June 2023)","role":"content","text":"This is a maintenance release for RedisGraph 2.10.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n  - [#3096](https://github.com/RedisGraph/RedisGraph/pull/3096) Potential crash on query timeout (MOD-5202)\n  - [#3042](https://github.com/RedisGraph/RedisGraph/issues/3042), [#3052](https://github.com/RedisGraph/RedisGraph/issues/3052) Potential crashes due to false assertions"},{"id":"v21010-april-2023","title":"v2.10.10 (April 2023)","role":"content","text":"This is a maintenance release for RedisGraph 2.10.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n  - [#3038](https://github.com/RedisGraph/RedisGraph/issues/3038) Potential crash when a query with a `UNION` clause sets or modifies an indexed property\n  - [#2631](https://github.com/RedisGraph/RedisGraph/issues/2631), [#2968](https://github.com/RedisGraph/RedisGraph/issues/2968) Potential crash on certain `MATCH` clauses where label filters are used (MOD-5093)\n  - [#2957](https://github.com/RedisGraph/RedisGraph/issues/2957) Label filters in expressions such as `WITH n MATCH (n:X)` are ignored\n  - [#2931](https://github.com/RedisGraph/RedisGraph/issues/2931), [#3027](https://github.com/RedisGraph/RedisGraph/issues/3027) Wrong overflow error message"},{"id":"v2109-march-2023","title":"v2.10.9 (March 2023)","role":"content","text":"This is a maintenance release for RedisGraph 2.10.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails:\n\n- Bug fixes:\n\n  - [#2880](https://github.com/RedisGraph/RedisGraph/issues/2880) Potential crash when using `WITH *` expressions\n  - [#2917](https://github.com/RedisGraph/RedisGraph/issues/2917) Potential crash when using `CASE` expressions (MOD-4873)\n  - [#2836](https://github.com/RedisGraph/RedisGraph/issues/2836) Potential crash on `*0` variable-length path (MOD-4817)\n  - [#2916](https://github.com/RedisGraph/RedisGraph/issues/2916) Potential crash when executing concurrent queries that utilize full-text indices (MOD-4818)"},{"id":"v2108-february-2023","title":"v2.10.8 (February 2023)","role":"content","text":"This is a maintenance release for RedisGraph 2.10.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails:\n\n- Bug fixes:\n\n  - [#2777](https://github.com/RedisGraph/RedisGraph/issues/2777), [#2841](https://github.com/RedisGraph/RedisGraph/issues/2841) Potential crash when sending queries from multiple connections and timeout is not 0\n  - [#2844](https://github.com/RedisGraph/RedisGraph/issues/2844) Potential partial results when same parametrized query is running from multiple connections\n  - [#2739](https://github.com/RedisGraph/RedisGraph/issues/2739), [#2774](https://github.com/RedisGraph/RedisGraph/issues/2774) Paths with exact variable length >1 are not matched\n  - [#2794](https://github.com/RedisGraph/RedisGraph/issues/2794) `toInteger` and `toIntegerOrNull` don't convert Booleans\n  - [#2798](https://github.com/RedisGraph/RedisGraph/issues/2798) `right` and `left` should reply with an error when `length` is null\n  - [#2809](https://github.com/RedisGraph/RedisGraph/issues/2809) `TIMEOUT_MAX` configuration parameter is not enforced when `TIMEOUT_DEFAULT` is 0\n  - [#2780](https://github.com/RedisGraph/RedisGraph/issues/2780) `indegree` and `outdegree` - relationships are counted more than once when same relationship type is supplied more than once\n\n- Improvements:\n\n  - [#2790](https://github.com/RedisGraph/RedisGraph/pull/2790) Improved performance by disabling SuiteSparse:GraphBLAS' global free pool\n  - [#2758](https://github.com/RedisGraph/RedisGraph/pull/2758) Improved edge deletion performance\n  - [#2781](https://github.com/RedisGraph/RedisGraph/issues/2781) `indegree` and `outdegree` now also accept an argument which is a list of labels"},{"id":"v2105-december-2022","title":"v2.10.5 (December 2022)","role":"content","text":"This is a maintenance release for RedisGraph 2.10.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails:\n\n- Bug fixes:\n\n  - [#2754](https://github.com/RedisGraph/RedisGraph/pull/2754) Partial sync may hang (MOD-4594)\n\n- Improvements:\n\n  - [#2757](https://github.com/RedisGraph/RedisGraph/pull/2757) Improved performance of `indegree` and `outdegree`\n  - [#2681](https://github.com/RedisGraph/RedisGraph/issues/2681) Fixed some error messages\n  - [#2740](https://github.com/RedisGraph/RedisGraph/issues/2740) Don’t show partial results for timed-out `GRAPH.PROFILE`"},{"id":"v210-ga-v2104-november-2022","title":"v2.10 GA (v2.10.4) (November 2022)","role":"content","text":"This is the General Availability release of RedisGraph 2.10."},{"id":"highlights","title":"Highlights","role":"content","text":"RedisGraph 2.10 introduces new path algorithms, additional expressivity (constructs and functions), performance improvements, and bug fixes.\n\n#### What's new in 2.10\n\n- New pathfinding algorithms: \n  - The `algo.SPpaths` procedure yields one, _n_, or all minimal-weight, optionally bounded-cost, optionally bounded-length paths between a given pair of nodes.\n  - The `algo.SSpaths` procedure yields one, _n_, or all minimal-weight, optionally bounded-cost, optionally bounded-length paths from a given node.\n- Introduce `SET` for adding node labels and `REMOVE` for removing node labels, node properties, and edge properties.\n- Support deleting paths with `DELETE`.\n- Introduce 29 functions: `toBoolean`, `toBooleanOrNull`, `toFloatOrNull`, `toIntegerOrNull`, `toStringOrNull`, `toBooleanList`, `toFloatList`, `toIntegerList`, `toStringList`, `properties`, `split`, `last`, `isEmpty`, `e`, `exp`, `log`, `log10`, `sin`, `cos`, `tan`, `cot`, `asin`, `acos`, `atan`, `atan2`, `degrees`, `radians`, `pi`, and `haversin`.\n- Graph slow log can be reset with `GRAPH.SLOWLOG g RESET`.\n- Queries are now atomic (_Atomicity_ is the guarantee that each query either succeeds or fails with no side effects). Whenever a failure occurs, the query effect is rolled back from an undo log."},{"id":"details","title":"Details","role":"content","text":"- Bug fixes (since 2.10-RC1):\n\n  - [#2695](https://github.com/RedisGraph/RedisGraph/pull/2695) Potential crash on certain write queries (MOD-4286, MOD-4545)\n  - [#2724](https://github.com/RedisGraph/RedisGraph/issues/2724) Potential crash when setting property values based on nonexistent properties\n  - [#2460](https://github.com/RedisGraph/RedisGraph/issues/2460), [#2637](https://github.com/RedisGraph/RedisGraph/issues/2637), [#2680](https://github.com/RedisGraph/RedisGraph/issues/2680) Crash on invalid queries\n  - [#2672](https://github.com/RedisGraph/RedisGraph/issues/2672) Wrong matching result on multiple labels\n  - [#2643](https://github.com/RedisGraph/RedisGraph/issues/2643) Duplicate reports when matching relationship type `:R|R`\n  - [#2687](https://github.com/RedisGraph/RedisGraph/issues/2687), [#2414](https://github.com/RedisGraph/RedisGraph/issues/2414) Error when `UNWIND`ing relationships\n  - [#2636](https://github.com/RedisGraph/RedisGraph/issues/2636) `MERGE` ... `ON` ... - cannot remove property by setting it to null\n  - [#2710](https://github.com/RedisGraph/RedisGraph/pull/2710) Undo log fix\n  - [#2435](https://github.com/RedisGraph/RedisGraph/issues/2435) Incorrect result when comparing a value to NaN\n  - [#2497](https://github.com/RedisGraph/RedisGraph/issues/2497) Incorrect result when comparing a value to null\n  - [#2676](https://github.com/RedisGraph/RedisGraph/issues/2676) `sqrt`, `log`, `log10` - incorrect result for negative values\n  - [#2213](https://github.com/RedisGraph/RedisGraph/issues/2213) Division and Modulo by zero - wrong behavior"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.2-release-notes","title":"RedisGraph 2.2 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.2-release-notes/","summary":"Support for scaling reads, OPTIONAL MATCH, query caching, and GRAPH.SLOWLOG.","tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:33+01:00","page_type":"content","content_hash":"a9f14ace6667676ea1327d8ee62e7caf9f82a66f5648313e28d60ed293b4e94c","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RedisGraph v2.2.16 requires:\n\n- Minimum Redis compatibility version (database): 5.0.7\n- Minimum Redis Enterprise Software version (cluster): 6.0.8"},{"id":"v2216-april-2021","title":"v2.2.16 (April 2021)","role":"content","text":"This is a maintenance release for version 2.2.\n\nUpdate urgency: Medium\n\nHeadlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n\nDetails:\n\n- Enhancements:\n\n    - #[1610](https://github.com/RedisGraph/RedisGraph/pull/1610) Add module-level configuration for query timeouts\n    - #[1572](https://github.com/RedisGraph/RedisGraph/pull/1572) Remove query size limitation from parser\n    - #[1590](https://github.com/RedisGraph/RedisGraph/pull/1590) Improve reporting of runtime errors\n    - #[1596](https://github.com/RedisGraph/RedisGraph/pull/1596) Run bulk loader commands on a non-blocking thread\n\n- Performance Improvements:\n\n    - #[1569](https://github.com/RedisGraph/RedisGraph/pull/1569) Avoid network overhead of emitting arrays with postponed length values\n\n- Bugfixes:\n\n    - #[1573](https://github.com/RedisGraph/RedisGraph/pull/1573) Lock graph on building/deleting full-text indexes, fixing a bug in full-text index replication\n    - #[1618](https://github.com/RedisGraph/RedisGraph/pull/1618) Fix error in traversals of 3 or more possible relationship types\n\n- Infrastructure:\n\n    - #[1557](https://github.com/RedisGraph/RedisGraph/pull/1557) Add automated performance benchmarks and regression testing\n    - #[1587](https://github.com/RedisGraph/RedisGraph/pull/1587), #[1598](https://github.com/RedisGraph/RedisGraph/pull/1598) Create build artifacts for multiple environments"},{"id":"v2214-16-february-2021","title":"v2.2.14 (16 February 2021)","role":"content","text":"This is a maintenance release for version 2.2.\n\nUpdate urgency: Medium\n\nHeadlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n\nDetails:\n\n- Performance improvements:\n\n    - #[1565](https://github.com/RedisGraph/RedisGraph/pull/1565) Reserve a single thread for performing all write queries.\n    - #[1548](https://github.com/RedisGraph/RedisGraph/pull/1548), #[1553](https://github.com/RedisGraph/RedisGraph/pull/1553), #[1554](https://github.com/RedisGraph/RedisGraph/pull/1554) only perform SET property updates when property value is changed.\n    - #[1543](https://github.com/RedisGraph/RedisGraph/pull/1543) Enable O3 compiler optimizations on libcypher-parser.\n\n- Bugfixes:\n\n    - #[1556](https://github.com/RedisGraph/RedisGraph/pull/1556) Avoid premature freeing of Records in create and update contexts.\n    - #[1541](https://github.com/RedisGraph/RedisGraph/pull/1541) Fix excessive memory consumption in OpApply."},{"id":"v2213-16-february-2021","title":"v2.2.13 (16 February 2021)","role":"content","text":"This is a maintenance release for version 2.2.\n\nUpdate urgency: Medium\n\nHeadlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n\nDetails:\n\n- Minor enhancements:\n\n    - #[1490](https://github.com/RedisGraph/RedisGraph/pull/1490) Add support for startNode() and endNode() functions.\n    - #[1519](https://github.com/RedisGraph/RedisGraph/pull/1519) Add support for db.indexes() procedure.\n\n- Performance improvements:\n\n    - #[1393](https://github.com/RedisGraph/RedisGraph/pull/1393) Use one unified cache for all RedisGraph worker threads.\n    - #[1498](https://github.com/RedisGraph/RedisGraph/pull/1498) Consider both filters and labels in selecting a traversal's starting point.\n    - #[1468](https://github.com/RedisGraph/RedisGraph/pull/1468) Unify logic and error handling for aggregate and scalar functions.\n\n- Bugfixes:\n\n    - #[1534](https://github.com/RedisGraph/RedisGraph/pull/1534) Outdated label information used when performing index scans.\n    - #[1532](https://github.com/RedisGraph/RedisGraph/pull/1532) Invalid optimization of IN \\[array] lookups when values are indexed.\n    - #[1524](https://github.com/RedisGraph/RedisGraph/pull/1524) Memory error in accessing complex values after CREATE operations.\n    - #[1523](https://github.com/RedisGraph/RedisGraph/pull/1523) Erroneous results when traversing over some transposed matrix sequences.\n    - #[1499](https://github.com/RedisGraph/RedisGraph/pull/1499) Erroneous results on variable-length traversals on repeatedly-transposed arrays.\n    - #[1504](https://github.com/RedisGraph/RedisGraph/pull/1504) Crash on certain constructions of 0-hop traversals.\n    - #[1495](https://github.com/RedisGraph/RedisGraph/pull/1495) Disallow access of properties that have not yet been created.\n    - #[1503](https://github.com/RedisGraph/RedisGraph/pull/1503) Only allow literal values as parameters.\n    - #[1496](https://github.com/RedisGraph/RedisGraph/pull/1496) Disallow assignment of complex data types to property values in MERGE contexts.\n    - #[1494](https://github.com/RedisGraph/RedisGraph/pull/1494) Disallow assignment of values to properties on deleted entities.\n\n- Build:\n\n    - #[1524](https://github.com/RedisGraph/RedisGraph/pull/1524) Update test suite to run under Python 3."},{"id":"v2211-20-december-2020","title":"v2.2.11 (20 December 2020)","role":"content","text":"This is a maintenance release for version 2.2.\n\nHeadlines:\n\nThis release introduces support for runtime configuration of the maximum number of results to be returned.\n\nDetails:\n\n- Features\n    - #[1480](https://github.com/RedisGraph/RedisGraph/pull/1480) Introduce GRAPH.CONFIG SET/GET to allow result-set size to be configured at runtime."},{"id":"v2210-20-december-2020","title":"v2.2.10 (20 December 2020)","role":"content","text":"This is a maintenance release for version 2.2.\n\nHeadlines:\n\nThis release improves overall stability and fixes an issue with backwards compatibility logic for indexing.\n\nDetails:\n\n- Bugfixes:\n    - #[1475](https://github.com/RedisGraph/RedisGraph/pull/1475) Fix erroneous property update when using `SET` with an unindexed property followed by an indexed one.\n    - #[1473](https://github.com/RedisGraph/RedisGraph/pull/1473) Fix backwards compatibility issue in rebuilding and dropping exact-match and full-text indexes."},{"id":"v229-4-december-2020","title":"v2.2.9 (4 December 2020)","role":"content","text":"This is a maintenance release for version 2.2.\n\nHeadlines:\n\nThis release improves overall stability and provides fixes for issues found after the previous release.\n\nDetails:\n\n- Performance improvements:\n    - #[1426](https://github.com/RedisGraph/RedisGraph/pull/1426) Improve load times of adjacency matrices that always have different source and destination nodes.\n- Minor enhancements:\n    - #[1463](https://github.com/RedisGraph/RedisGraph/pull/1463) Remove all assertions from production builds.\n    - #[1442](https://github.com/RedisGraph/RedisGraph/pull/1442) NULL property values should be ignored in `CREATE` clauses and trigger errors in MERGE clauses.\n    - #[1429](https://github.com/RedisGraph/RedisGraph/pull/1429) Improve error handling of a number of invalid query constructions.\n    - #[1431](https://github.com/RedisGraph/RedisGraph/pull/1431) Traversal patterns are only allowed in `MATCH`, `MERGE`, `CREATE`, and `WHERE` contexts.\n- Bugfixes:\n    - #[1429](https://github.com/RedisGraph/RedisGraph/pull/1429) Fix potential misapplications of `WHERE` filters.\n    - #[1460](https://github.com/RedisGraph/RedisGraph/pull/1460) `RETURN` clauses can only be followed by `UNION` clauses.\n    - #[1467](https://github.com/RedisGraph/RedisGraph/pull/1467) Disallow assignment of complex data types to property values.\n    - #[1437](https://github.com/RedisGraph/RedisGraph/pull/1437) Failed property updates on `MERGE` should be handled gracefully.\n    - #[1446](https://github.com/RedisGraph/RedisGraph/pull/1446) Fix errors in Cartesian Product filter placement."},{"id":"v22-ga-november-2020","title":"v2.2 GA (November 2020)","role":"content","text":"This is the General Availability Release of RedisGraph 2.2 (v2.2.8)!\n\nHeadlines:\n\n- Enhanced support for scaling reads\n- `OPTIONAL MATCH` (Left outer join)\n- Query cache: Improve performance by caching the query execution plan\n- Tooling to increase developer experience\n\nDetails:\n\n- Support for scaling reads\n    - #[1054](https://github.com/RedisGraph/RedisGraph/pull/1054)\n Drastic reduction of memory consumption during replication (and [Active-Passive](https://redis.io/docs/latest/operate/rs/databases/import-export/replica-of/create)). Break down a large graph into several portions, each accommodating a virtual key and distribute those for reconstruction at the replica end, by doing so we reduce memory consumption on the replica's end.\n\n- `OPTIONAL MATCH` support\n    Unlike MATCH, which requires for a pattern to exist, `OPTIONAL MATCH` continues processing when the optional pattern doesn't exist. We can easily compare `OPTIONAL MATCH` in Cypher to an outer join in SQL. It works just like a regular `MATCH` with the difference that if no matches are found, `OPTIONAL MATCH` will use a null for missing parts of the pattern.\n\n    - Query cache: Improve performance by caching the query execution plan\n\n        When executing parameterised queries, RedisGraph will cache the execution plan for increased performance. By caching query's execution-plan, RedisGraph skips the parsing and execution-plan construction phase.\n        Cache size can be configured at module load time. The default value is 25.\n\n- Tooling to increase developer experience\n\n    Introduction of `GRAPH.SLOWLOG` command which returns the longest running queries.\n    In addition #[1274](https://github.com/RedisGraph/RedisGraph/pull/1274)\n introduces query timeouts with an optional query flag.\n\n- Smaller features and enhancements:\n    - #[1225](https://github.com/RedisGraph/RedisGraph/pull/1225)\n Only update index on change of relevant Node properties.\n    - #[1229](https://github.com/RedisGraph/RedisGraph/pull/1229)\n RediSearch 1.8.2. (Dependency update)\n    - #[1266](https://github.com/RedisGraph/RedisGraph/pull/1266)\n, #[1277](https://github.com/RedisGraph/RedisGraph/pull/1277)\n Add support for `any()` and `all()` functions in list comprehension.\n    - #[877](https://github.com/RedisGraph/RedisGraph/pull/877)\n Transpose matrices: maintain transpose relationship matrices such that we won't have to compute the transpose of a matrix at run-time. This feature is on by default but can be configured to reduce memory consumption.\n\n- Small updates compared to RC7\n    - Minor enhancements:\n        - #[1365](https://github.com/RedisGraph/RedisGraph/pull/1365)\n Support array properties in bulk loader.\n        - #[1377](https://github.com/RedisGraph/RedisGraph/pull/1377)\n Adding a new procedure `dbms.procedures` that returns all the procedures in the system.\n        - #[1389](https://github.com/RedisGraph/RedisGraph/pull/1389)\n Allow property accesses on non-identifier entity references.\n    - Bug fixes:\n        - #[1406](https://github.com/RedisGraph/RedisGraph/pull/1406)\n Query validation should not check if procedure outputs have been defined.\n        - #[1382](https://github.com/RedisGraph/RedisGraph/pull/1382)\n Add graph version to graph context object, response with an error when client graph version mismatch.\n        - #[1361](https://github.com/RedisGraph/RedisGraph/pull/1361)\n Don't migrate `WITH` filters into Merge and Apply operation scope.\n\nNotes:\nThis is the GA version of 2.2. The version inside Redis will be 20208 or 2.2.8 in semantic versioning. Since the version of a module in Redis is numeric, we could not add a GA flag."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.4-release-notes","title":"RedisGraph 2.4 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.4-release-notes/","summary":"Added Map and Geospatial Point data types.","tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:33+01:00","page_type":"content","content_hash":"485e7ff02d528277455b58af3ebba8ba0401338d89a4c950a4964c62c5612673","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RedisGraph v2.4.14 requires:\n\n- Minimum Redis compatibility version (database): 6.0.0\n- Minimum Redis Enterprise Software version (cluster): 6.0.8"},{"id":"v2414-may-2022","title":"v2.4.14 (May 2022)","role":"content","text":"This is a maintenance release for RedisGraph 2.4.\n\nUpdate urgency: `LOW`: No need to upgrade unless there are new features you want to use.\n\nDetails:\n\n- Bug fixes:\n\n    - [#2072](https://github.com/RedisGraph/RedisGraph/issues/2072), [#2081](https://github.com/RedisGraph/RedisGraph/pull/2081) CRLF sequences embedded in strings no longer trigger a protocol error when being emitted\n\n- Improvements:\n\n    - [#2102](https://github.com/RedisGraph/RedisGraph/pull/2102) New load-time configuration option `NODE_CREATION_BUFFER` - see [documentation](https://github.com/RedisGraph/RedisGraph/blob/master/docs/docs/configuration.md#node_creation_buffer) (MOD-2348)\n\n> [!NOTE]\n> For Redis Enterprise users who want to upgrade to this patch, this version requires being on version 6.2.8 or later."},{"id":"v2413-december-2021","title":"v2.4.13 (December 2021)","role":"content","text":"This is a maintenance release for RedisGraph 2.4.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails:\n\n- Performance improvements\n    - [#2040](https://github.com/RedisGraph/RedisGraph/pull/2040) Use optimal order of traversals\n\n- Bug fixes:\n    - [#2046](https://github.com/RedisGraph/RedisGraph/pull/2046) Avoid serialization of duplicate graph keys (high urgency)\n    - [#2033](https://github.com/RedisGraph/RedisGraph/pull/2033) Update RediSearch to 2.0.13"},{"id":"v2412-october-2021","title":"v2.4.12 (October 2021)","role":"content","text":"This is a maintenance release for RedisGraph 2.4.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails:\n\n- Bug fixes:\n    - [#1981](https://github.com/RedisGraph/RedisGraph/pull/1981) Crash in index scan"},{"id":"v2411-october-2021","title":"v2.4.11 (October 2021)","role":"content","text":"This is a maintenance release for RedisGraph 2.4.\n\nUpdate urgency: `MODERATE` : Program an upgrade of the server, but it's not urgent.\n\nDetails:\n- Bug fixes:\n    - #[1931](https://github.com/RedisGraph/RedisGraph/issues/1931) Fix race condition on calling [`BGSAVE`](https://redis.io/docs/latest/commands/bgsave) while flushing matrices\n    - #[1898](https://github.com/RedisGraph/RedisGraph/issues/1898) Error when setting a property to an array containing an invalid type\n    - #[1897](https://github.com/RedisGraph/RedisGraph/issues/1897) Aliases in `WITH...ORDER BY` must be valid references%"},{"id":"v2410-september-2021","title":"v2.4.10 (September 2021)","role":"content","text":"This is a maintenance release for RedisGraph 2.4\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails:\n- Critical bug fixes:\n    - #[1911](https://github.com/RedisGraph/RedisGraph/issues/1911) When a query timeout is set and the graph contained indexes, a crash could occur while releasing indexes\n\n- Bug fixes:\n    - #[1913](https://github.com/RedisGraph/RedisGraph/issues/1913) Update thread-local AST for every cloned operation\n    - #[1915](https://github.com/RedisGraph/RedisGraph/issues/1915) Validate function references in parameters%"},{"id":"v247-july-2021","title":"v2.4.7 (July 2021)","role":"content","text":"This is a maintenance release for RedisGraph 2.4\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails:\n- Bug fixes:\n    - #[1746](https://github.com/redisgraph/redisgraph/issues/1746) Avoid invalid memory access after reallocating labels array\n    - #[1748](https://github.com/redisgraph/redisgraph/issues/1748) Only allocate space for deleted nodes and edges\n    - #[1749](https://github.com/redisgraph/redisgraph/issues/1749), #[1730](https://github.com/redisgraph/redisgraph/issues/1730) Support updating properties with map referenced by parameter or variable\n    - #[1754](https://github.com/redisgraph/redisgraph/issues/1754) Map should return volatile data\n    - #[1773](https://github.com/redisgraph/redisgraph/issues/1773) Matrix resize doesn't set both rows and columns atomically\n    - #[1799](https://github.com/redisgraph/redisgraph/issues/1799) Always instantiate new persistent matrices as sparse%"},{"id":"v246-june-2021","title":"v2.4.6 (June 2021)","role":"content","text":"This is a maintenance release for RedisGraph 2.4\n\nUpdate urgency: Medium\n\nDetails:\n\n- Performance improvements:\n    - #[1702](https://github.com/redisgraph/redisgraph/issues/1702) Optimize matrix synchronization in GRAPH.BULK commands\n    - #[1716](https://github.com/redisgraph/redisgraph/issues/1716) Reduce lock calls in retrieving matrices for reading\n    - #[1741](https://github.com/redisgraph/redisgraph/issues/1741) Improve edge extraction\n    - #[1388](https://github.com/redisgraph/redisgraph/issues/1388) Node creation buffer\n    - #[1724](https://github.com/redisgraph/redisgraph/issues/1724) Update RediSearch 2.0\n    - #[1731](https://github.com/redisgraph/redisgraph/issues/1731) Release lock after MergeCreate builds entities\n    - #[1734](https://github.com/redisgraph/redisgraph/issues/1734) Added multi edge flag\n\n- Bug fixes:\n    - #[1726](https://github.com/redisgraph/redisgraph/issues/1726) JsonEncoder: invalid cast\n    - #[1709](https://github.com/redisgraph/redisgraph/issues/1709) #1712 Fix potential deadlock in BGSAVE\n    - #[1703](https://github.com/redisgraph/redisgraph/issues/1703) Uniquing logic in UNION subqueries\n    - #[1720](https://github.com/redisgraph/redisgraph/issues/1720) Propagate configuration updates to replicas\n    - #[1714](https://github.com/redisgraph/redisgraph/issues/1714) Don't build scan operations for bound variables\n    - #[1708](https://github.com/redisgraph/redisgraph/issues/1708) Regression in GRAPH.CONFIG GET *\n    - #[1715](https://github.com/redisgraph/redisgraph/issues/1715) Validate function private data before freeing it\n\n- Miscellaneous:\n\n    - #[1691](https://github.com/redisgraph/redisgraph/issues/1691) Reword error message on setting property value to invalid type"},{"id":"v244-15-may-2021","title":"v2.4.4 (15 May 2021)","role":"content","text":"This is a maintenance release for RedisGraph 2.4.\n\nUpdate urgency: Medium\n\nDetails:\n\n- Minor Enhancements:\n    - #[1666](https://github.com/redisgraph/redisgraph/issues/1666) Add configuration parameter to limit the memory usage of queries\n    - #[1639](https://github.com/redisgraph/redisgraph/issues/1639) Add functionality to SET all properties on an entity to a map\n\n- Bug fixes:\n    - #[1678](https://github.com/redisgraph/redisgraph/issues/1678) Crash on multiple procedure calls in a single query"},{"id":"v243-8-may-2021","title":"v2.4.3 (8 May 2021)","role":"content","text":"This is a maintenance release for RedisGraph 2.4.\n\nUpdate urgency: Medium\n\nDetails:\n\n- Features:\n    - #[1657](https://github.com/redisgraph/redisgraph/issues/1657) Support filtering on variable-length edges in traversals\n    - #[1514](https://github.com/redisgraph/redisgraph/issues/1514) Add support for shortestPath in MATCH clauses\n    - #[1664](https://github.com/redisgraph/redisgraph/issues/1664) Add support for limiting max queued queries\n    - #[1653](https://github.com/redisgraph/redisgraph/issues/1653), #[1655](https://github.com/redisgraph/redisgraph/issues/1655) Add support for square root (sqrt) function\n\n- Performance improvements:\n    - #[1641](https://github.com/redisgraph/redisgraph/issues/1641) Use indexes when filters can only be resolved at runtime\n    - #[1668](https://github.com/redisgraph/redisgraph/issues/1668) Apply path filters after all other filters\n\n- Bug fixes:\n    - #[1650](https://github.com/redisgraph/redisgraph/issues/1650) Fix memory leak on allocated keys in OpAggregate\n    - #[1671](https://github.com/redisgraph/redisgraph/issues/1671) Disallow nested aggregates in map values, circumventing a bug that introduced invalid group keys\n    - #[1644](https://github.com/redisgraph/redisgraph/issues/1644) Fix bug in which values for unknown schema types are cached\n    - #[1656](https://github.com/redisgraph/redisgraph/issues/1656) Fix potential crash when releasing thread pools on server shutdown"},{"id":"v24-ga-march-2021","title":"v2.4 GA (March 2021)","role":"content","text":"Headlines:\n\n- This release introduces the Map and Geospatial Point data types.\n\nDetails:\n\n- Features:\n  - #[1514](https://github.com/redisgraph/redisgraph/issues/1514) Add support for Map data type.\n  - #[1516](https://github.com/redisgraph/redisgraph/issues/1516) Add support for Geospatial Point data type.\n  - #[1562](https://github.com/redisgraph/redisgraph/issues/1562) Add `toJSON` function.\n  - #[1607](https://github.com/redisgraph/redisgraph/issues/1607) Querying full-text indexes can yield the `score` of each result.\n  - #[1610](https://github.com/redisgraph/redisgraph/issues/1610) Expose runtime configuration for read query timeouts.\n\n- Performance improvements:\n  - #[1596](https://github.com/redisgraph/redisgraph/issues/1596) [Redisgraph-bulk-loader](https://github.com/RedisGraph/redisgraph-bulk-loader) no longer blocks the server.\n\n- Minor Enhancements\n  - #[1590](https://github.com/redisgraph/redisgraph/issues/1590) Improve handling of runtime errors.\n  - #[1580](https://github.com/redisgraph/redisgraph/issues/1580) Enable assertions in debug mode.\n\n- Bugfixes (compared to RC1)\n  - #[1618](https://github.com/redisgraph/redisgraph/issues/1618) Fix relationship types being omitted in traversals of 3 or more types.\n\n> [!NOTE]\n> This is the first GA version of 2.4. The version inside Redis will be 2.4.2 in semantic versioning. Since the version of a module in Redis is numeric, we could not add an GA flag."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.8-release-notes","title":"RedisGraph 2.8 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.8-release-notes/","summary":"Introduces multi-labeled nodes, indexes over relationship properties, and additional expressivity (Cypher construct, functions, and operators). Major performance enhancements. Many bug fixes.","tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:33+01:00","page_type":"content","content_hash":"cf61f3417851a7ba3cd0d434f3c3f1eaed83409fcffda0d36fd2bbde730b030b","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RedisGraph v2.8.26 requires:\n\n- Minimum Redis compatibility version (database): 6.2.0\n- Minimum Redis Enterprise Software version (cluster): 6.2.8"},{"id":"v2826-march-2023","title":"v2.8.26 (March 2023)","role":"content","text":"This is a maintenance release for RedisGraph 2.8.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails:\n\n- Bug fixes:\n\n  - [#2880](https://github.com/RedisGraph/RedisGraph/issues/2880) Potential crash when using WITH * expressions\n  - [#2917](https://github.com/RedisGraph/RedisGraph/issues/2917) Potential crash when using CASE expressions (MOD-4873)\n  - [#2836](https://github.com/RedisGraph/RedisGraph/issues/2836) Potential crash on *0 variable-length path (MOD-4817)\n  - [#2916](https://github.com/RedisGraph/RedisGraph/issues/2916) Potential crash when executing concurrent queries that utilize full-text indices (MOD-4818)"},{"id":"v2825-february-2023","title":"v2.8.25 (February 2023)","role":"content","text":"This is a maintenance release for RedisGraph 2.8.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n  - [#2890](https://github.com/RedisGraph/RedisGraph/issues/2890) Potential wrong results on variable-length path"},{"id":"v2824-february-2023","title":"v2.8.24 (February 2023)","role":"content","text":"This is a maintenance release for RedisGraph 2.8.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails:\n\n- Bug fixes:\n\n  - [#2777](https://github.com/RedisGraph/RedisGraph/issues/2777), [#2841](https://github.com/RedisGraph/RedisGraph/issues/2841) Potential crash when sending queries from multiple connections and timeout is not 0\n  - [#2844](https://github.com/RedisGraph/RedisGraph/issues/2844) Potential partial results when same parametrized query is running from multiple connections\n  - [#2739](https://github.com/RedisGraph/RedisGraph/issues/2739), [#2774](https://github.com/RedisGraph/RedisGraph/issues/2774) Paths with exact variable length >1 are not matched\n\n- Improvements:\n\n  - [#2758](https://github.com/RedisGraph/RedisGraph/pull/2758) Improved edge deletion performance"},{"id":"v2821-january-2023","title":"v2.8.21 (January 2023)","role":"content","text":"This is a maintenance release for RedisGraph 2.8.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails:\n\n- Bug fixes:\n\n    - [#2754](https://github.com/RedisGraph/RedisGraph/pull/2754) Partial sync may hang (MOD-4594)\n    - [#2695](https://github.com/RedisGraph/RedisGraph/pull/2695) Potential crash on certain write queries (MOD-4286, MOD-4545)\n    - [#2637](https://github.com/RedisGraph/RedisGraph/issues/2637), [#2460](https://github.com/RedisGraph/RedisGraph/issues/2460), [#2680](https://github.com/RedisGraph/RedisGraph/issues/2680) Crash on invalid queries\n    - [#2484](https://github.com/RedisGraph/RedisGraph/issues/2484) Indexes can be created on invalid property names\n    - [#2672](https://github.com/RedisGraph/RedisGraph/issues/2672) Wrong matching result on multiple labels\n    - [#2643](https://github.com/RedisGraph/RedisGraph/issues/2643) Duplicate reports when matching relationship type `:R|R`\n    - [#2687](https://github.com/RedisGraph/RedisGraph/issues/2687), [#2414](https://github.com/RedisGraph/RedisGraph/issues/2414) Error when `UNWIND`ing relationships\n    - [#2635](https://github.com/RedisGraph/RedisGraph/issues/2635) Cannot `UNWIND` an expression that is not a list\n    - [#2636](https://github.com/RedisGraph/RedisGraph/issues/2636) `MERGE` ... `ON` ... - cannot remove a property by setting it to null\n\n- Improvements:\n\n    - [#2790](https://github.com/RedisGraph/RedisGraph/pull/2790) Improved performance by disabling SuiteSparse:GraphBLAS' global free pool\n    - [#2757](https://github.com/RedisGraph/RedisGraph/pull/2757) Improved performance of `indegree` and `outdegree`\n    - [#2740](https://github.com/RedisGraph/RedisGraph/issues/2740) Don’t show partial results for timed out `GRAPH.PROFILE`"},{"id":"v2820-september-2022","title":"v2.8.20 (September 2022)","role":"content","text":"This is a maintenance release for RedisGraph 2.8.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n    - [#2591](https://github.com/RedisGraph/RedisGraph/issues/2591) Potential crash trying to utilize a nonexistent index\n    - [#2558](https://github.com/RedisGraph/RedisGraph/issues/2558) Multi-relationship properties created before index creation are not indexed\n    - [#2571](https://github.com/RedisGraph/RedisGraph/issues/2571) `min` and `max` return wrong results when the argument is an array\n    - [#2587](https://github.com/RedisGraph/RedisGraph/issues/2587) Some queries generate a \"forced unlocking commit flow\" warning\n\n- Improvements:\n\n    - [#2533](https://github.com/RedisGraph/RedisGraph/issues/2533) Graph slow log can be reset with `GRAPH.SLOWLOG g RESET`"},{"id":"v2819-august-2022","title":"v2.8.19 (August 2022)","role":"content","text":"This is a maintenance release for RedisGraph 2.8.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n    - [#2517](https://github.com/RedisGraph/RedisGraph/issues/2517) Crash on invalid `REDUCE` queries\n    - [#2525](https://github.com/RedisGraph/RedisGraph/issues/2525) `toJSON` - node labels are not separated with a comma (`,`)\n    - [#2467](https://github.com/RedisGraph/RedisGraph/issues/2467) Possibly wrong results when using a variable named `anon_N`\n    - [#2522](https://github.com/RedisGraph/RedisGraph/pull/2522) Deleting an edge index leaves traces\n    - [#2477](https://github.com/RedisGraph/RedisGraph/issues/2477) Cannot extract the latitude or the longitude of a point\n\n- Improvements:\n\n    - [#2519](https://github.com/RedisGraph/RedisGraph/pull/2519) When a query is cached and then a relevant index is created, recalculate the execution plan"},{"id":"v2817-july-2022","title":"v2.8.17 (July 2022)","role":"content","text":"This is a maintenance release for RedisGraph 2.8.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails:\n\n- Bug fixes:\n\n    - [#2499](https://github.com/RedisGraph/RedisGraph/pull/2499) Potential crash with concurrent connections due to missing lock - additional fixes\n    - [#2424](https://github.com/RedisGraph/RedisGraph/issues/2424) Potential crash when using `ORDER BY`\n    - [#2491](https://github.com/RedisGraph/RedisGraph/issues/2491) Whitespaces between `MATCH` terms can render the query invalid"},{"id":"v2816-july-2022","title":"v2.8.16 (July 2022)","role":"content","text":"This is a maintenance release for RedisGraph 2.8.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails:\n\n- Bug fixes:\n\n    - [#2478](https://github.com/RedisGraph/RedisGraph/pull/2478) Potential crash with concurrent connections due to missing lock\n    - [#2370](https://github.com/RedisGraph/RedisGraph/issues/2370) Potential crash / wrong results / warning messages when using edge indexes\n    - [#2473](https://github.com/RedisGraph/RedisGraph/issues/2473) Crash on invalid `distance()` query with index"},{"id":"v2815-june-2022","title":"v2.8.15 (June 2022)","role":"content","text":"This is a maintenance release for RedisGraph 2.8.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails:\n\n- Bug fixes:\n\n    - [#2241](https://github.com/RedisGraph/RedisGraph/issues/2241) Possible crash on queries with `MERGE` operation in a Cartesian product (MOD-3500)\n    - [#2394](https://github.com/RedisGraph/RedisGraph/issues/2394) Possible crash when freeing an index immediately after its creation"},{"id":"v2814-june-2022","title":"v2.8.14 (June 2022)","role":"content","text":"This is a maintenance release for RedisGraph 2.8.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails:\n\n- Features:\n\n    - [#2403](https://github.com/RedisGraph/RedisGraph/pull/2403) Introduce `toFloat` function\n    - [#2365](https://github.com/RedisGraph/RedisGraph/issues/2365) Commands that don't execute on the main thread now also appear in [SLOWLOG](https://redis.io/docs/latest/commands/slowlog)\n\n- Bug fixes:\n\n    - [#2381](https://github.com/RedisGraph/RedisGraph/pull/2381) Rare data corruption on data encoding\n    - [#2393](https://github.com/RedisGraph/RedisGraph/issues/2393), [#2395](https://github.com/RedisGraph/RedisGraph/issues/2395) Crash when searching an index for a runtime-determined value\n    - [#2377](https://github.com/RedisGraph/RedisGraph/issues/2377) Crash on `INT_MIN % -1`\n    - [#2390](https://github.com/RedisGraph/RedisGraph/issues/2390) Crash on distance filter\n    - [#2407](https://github.com/RedisGraph/RedisGraph/issues/2407) Crash on double to string\n    - [#2422](https://github.com/RedisGraph/RedisGraph/pull/2422) `toJSON` returned floating points cropped to 6 decimals\n\n> [!NOTE]\n> New RDB version (v12). RDB files created with v2.8.14 or later are not backward compatible."},{"id":"v2813-may-2022","title":"v2.8.13 (May 2022)","role":"content","text":"This is a maintenance release for RedisGraph 2.8.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails:\n\n- Bug fixes:\n\n    - [#2351](https://github.com/RedisGraph/RedisGraph/pull/2351) Potential memory leak on query timeout\n    - [#2348](https://github.com/RedisGraph/RedisGraph/issues/2348) Crash when converting certain Cypher queries to RediSearch queries\n    - [#2331](https://github.com/RedisGraph/RedisGraph/pull/2331) Two memory leaks (one on failed RDB loading, one on certain invalid queries)\n    - [#2328](https://github.com/RedisGraph/RedisGraph/pull/2328), [#2306](https://github.com/RedisGraph/RedisGraph/issues/2306), [#2307](https://github.com/RedisGraph/RedisGraph/issues/2307), [#2326](https://github.com/RedisGraph/RedisGraph/issues/2326) Disallow redeclaration of variables; fixed false redeclaration errors\n    - [#2363](https://github.com/RedisGraph/RedisGraph/pull/2363) Nodes were sometimes created with more labels than those specified"},{"id":"v2812-may-2022","title":"v2.8.12 (May 2022)","role":"content","text":"This is a maintenance release for RedisGraph 2.8.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails:\n\n- Bug fixes:\n\n    - [#2278](https://github.com/RedisGraph/RedisGraph/issues/2278), [#2301](https://github.com/RedisGraph/RedisGraph/pull/2301) Potential crash on bulk update\n    - [#2262](https://github.com/RedisGraph/RedisGraph/issues/2262), [#2271](https://github.com/RedisGraph/RedisGraph/issues/2271), [#2272](https://github.com/RedisGraph/RedisGraph/issues/2272), [#2273](https://github.com/RedisGraph/RedisGraph/issues/2273), [#2282](https://github.com/RedisGraph/RedisGraph/issues/2282), [#2288](https://github.com/RedisGraph/RedisGraph/issues/2288), [#2299](https://github.com/RedisGraph/RedisGraph/issues/2299), [#2312](https://github.com/RedisGraph/RedisGraph/issues/2312), [#2321](https://github.com/RedisGraph/RedisGraph/issues/2321), [#2325](https://github.com/RedisGraph/RedisGraph/issues/2325), [#2323](https://github.com/RedisGraph/RedisGraph/issues/2323) Potential crash on certain queries\n    - [#1441](https://github.com/RedisGraph/RedisGraph/issues/1441) Query returns wrong result: projection before Cartesian product causes only one result to be returned\n    - [#2298](https://github.com/RedisGraph/RedisGraph/issues/2298) Query returns wrong result: wrong value when fetching a string property from a map\n    - [#2318](https://github.com/RedisGraph/RedisGraph/pull/2318) Memory leak\n\n> [!NOTE]\n> Duplicate column names in a query are no longer valid. For example, the query `GRAPH.QUERY g \"MATCH (e) RETURN e, e\"` is not valid. If you need to return the same column twice, you can rewrite the query as `GRAPH.QUERY g \"MATCH (e) RETURN e, e as e2\"`."},{"id":"v2811-march-2022","title":"v2.8.11 (March 2022)","role":"content","text":"This is a maintenance release for RedisGraph 2.8.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails:\n\n- Bug fixes:\n\n    - [#2259](https://github.com/RedisGraph/RedisGraph/issues/2259), [#2258](https://github.com/RedisGraph/RedisGraph/pull/2258) Fix memory leak and potential crash on [RDB](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence) saving"},{"id":"v2810-march-2022","title":"v2.8.10 (March 2022)","role":"content","text":"This is a maintenance release for RedisGraph 2.8.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails: \n\n- Features:\n\n    - [#2245](https://github.com/RedisGraph/RedisGraph/pull/2245) Support graphs [eviction](https://redis.io/docs/latest/develop/reference/eviction)\n\n- Bug fixes:\n\n    - [#1493](https://github.com/RedisGraph/RedisGraph/issues/1493), [#2240](https://github.com/RedisGraph/RedisGraph/pull/2240) Fixed crash on certain queries\n    - [#2229](https://github.com/RedisGraph/RedisGraph/issues/2229), [#2222](https://github.com/RedisGraph/RedisGraph/pull/2222) Fixed crash on certain queries\n    - [#2209](https://github.com/RedisGraph/RedisGraph/issues/2209), [#2228](https://github.com/RedisGraph/RedisGraph/pull/2228) Fixed crash on certain invalid `DELETE` queries\n    - [#2237](https://github.com/RedisGraph/RedisGraph/issues/2237), [#2242](https://github.com/RedisGraph/RedisGraph/pull/2242) Fixed crash on certain `PROFILE` queries\n    - [#2230](https://github.com/RedisGraph/RedisGraph/issues/2230), [#2232](https://github.com/RedisGraph/RedisGraph/pull/2232) Fixed wrong number of reported deleted relationships on certain queries\n    - [#2233](https://github.com/RedisGraph/RedisGraph/pull/2233) Certain valid queries were reported invalid\n    - [#2246](https://github.com/RedisGraph/RedisGraph/issues/2246) Fixed memory leaks\n\n- Improvements:\n\n    - [#2235](https://github.com/RedisGraph/RedisGraph/pull/2235) Improved [RDB](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence) loading performance"},{"id":"v289-march-2022","title":"v2.8.9 (March 2022)","role":"content","text":"This is a maintenance release for RedisGraph 2.8.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails: \n\n- Features:\n\n    - [#2181](https://github.com/RedisGraph/RedisGraph/pull/2181), [#2182](https://github.com/RedisGraph/RedisGraph/pull/2182) Full support for ARM builds\n\n- Bug fixes:\n\n    - [#2167](https://github.com/RedisGraph/RedisGraph/pull/2167) Fixed a potential crash: filter placement in `OPTIONAL` subtrees\n    - [#2176](https://github.com/RedisGraph/RedisGraph/pull/2176) Fixed a potential crash: invalid memory access in nested `DISTINCT` functions\n    - [#2217](https://github.com/RedisGraph/RedisGraph/pull/2217) Fixed a potential crash: memory access after free on `FLUSHALL`\n    - [#2207](https://github.com/RedisGraph/RedisGraph/pull/2207) Fixed memory leak when `MAX_QUEUED_QUERIES` is used\n    - [#2220](https://github.com/RedisGraph/RedisGraph/pull/2220) `WITH * WHERE` - the `WHERE` filters were ignored\n    - [#2151](https://github.com/RedisGraph/RedisGraph/pull/2151) Return correct results for aggregations with no inputs\n    - [#2163](https://github.com/RedisGraph/RedisGraph/pull/2163) Emit error correctly on multi-query inputs\n\n- Improvements:\n\n    - [#2173](https://github.com/RedisGraph/RedisGraph/pull/2173) Improve performance of breadth-first search"},{"id":"v28-ga-v288-february-2022","title":"v2.8 GA (v2.8.8) (February 2022)","role":"content","text":"This is the General Availability release of RedisGraph 2.8."},{"id":"highlights","title":"Highlights","role":"content","text":"RedisGraph 2.8 introduces multi-labeled nodes, indexes over relationship properties, additional expressivity (construct, functions, and operators), major performance enhancements, and many bug fixes.\n\n#### What's new in 2.8\n\n- Multi-labeled nodes\n\n    > [!NOTE]\n    > The `labels` function's signature has changed. The function now returns a list of labels instead of a single label.\n    > <br /><br />\n    > If you are using this function and upgrading to RedisGraph 2.8, a simple fix is to replace any call to `labels(x)` with `labels(x)[0]`. This returns the first label associated with node `x`. For uni-labeled nodes, the result in RedisGraph 2.8 is similar to the results of `labels(x)` in RedisGraph 2.4.\n  \n- Indexes over relationship properties\n- Enhanced full-text search\n- Delta matrices: node and relationships additions and deletions are much faster, as they are first updated in small delta matrices. The main matrices are then bulk-updated.\n- Additional Cypher construct, functions, and operators\n- RediSearch 2.2.7\n- [SuiteSparse](https://github.com/DrTimothyAldenDavis/GraphBLAS) (GraphBLAS) 6.0.0"},{"id":"details","title":"Details","role":"content","text":"- Features (since 2.8-M02):\n\n    - [#2109](https://github.com/RedisGraph/RedisGraph/pull/2109) Introduce `allShortestPaths` BFS function\n    - [#2099](https://github.com/RedisGraph/RedisGraph/pull/2099) Introduce `keys` function\n    - [#2047](https://github.com/RedisGraph/RedisGraph/pull/2047) Introduce `reduce` function\n    - [#2076](https://github.com/RedisGraph/RedisGraph/pull/2076) Introduce XOR operation in filter trees\n    - [#2088](https://github.com/RedisGraph/RedisGraph/pull/2088) Introduce pattern comprehensions\n    - [#2051](https://github.com/RedisGraph/RedisGraph/pull/2051) Allow copying of entity attribute sets in `SET` clauses\n    - [#2067](https://github.com/RedisGraph/RedisGraph/pull/2067) Allow modification of virtual key entity count (`VKEY_MAX_ENTITY_COUNT`) at runtime\n    - [#2102](https://github.com/RedisGraph/RedisGraph/pull/2102) New load time configuration option `NODE_CREATION_BUFFER` - see [documentation](https://github.com/RedisGraph/RedisGraph/blob/master/docs/docs/configuration.md#node_creation_buffer) (MOD-2348)\n    - [#2049](https://github.com/RedisGraph/RedisGraph/pull/2049) RediSearch supports field definitions\n\n- Performance improvements (since 2.8-M02):\n\n    - [#2097](https://github.com/RedisGraph/RedisGraph/pull/2097) Locks favor writers to prevent write exhaustion\n    - [#1945](https://github.com/RedisGraph/RedisGraph/pull/1945) Track node count per label in graph statistics\n    - [#1872](https://github.com/RedisGraph/RedisGraph/pull/1872) Delta matrices are always [hypersparse](https://fossies.org/linux/SuiteSparse/GraphBLAS/docs/graphblas_demo.html#40)\n    - [#1871](https://github.com/RedisGraph/RedisGraph/pull/1871) Matrix sync policies reduce the number of syncs required\n    - [#1869](https://github.com/RedisGraph/RedisGraph/pull/1869) Transposed matrices are always boolean\n    - [#2101](https://github.com/RedisGraph/RedisGraph/pull/2101) Entity annotation has been replaced with an AST `toString` function\n    - [#1878](https://github.com/RedisGraph/RedisGraph/pull/1878) Slowlog queries no longer create graphs\n    - [#2067](https://github.com/RedisGraph/RedisGraph/pull/2067) Index graph entities incrementally on restore\n    - Faster AOF recovery (PM-1252)\n\n- Bug fixes (since 2.8-M02):\n\n    - [#2016](https://github.com/RedisGraph/RedisGraph/pull/2016) Implement new BFS algorithm\n    - [#2105](https://github.com/RedisGraph/RedisGraph/pull/2105) Creating a node with multiple properties using the same key only accepts the last value\n    - [#2055](https://github.com/RedisGraph/RedisGraph/pull/2055) Avoid arithmetic overflow in `avg` function\n    - [#2048](https://github.com/RedisGraph/RedisGraph/pull/2048) Modulo by zero emits division by zero error\n    - [#2020](https://github.com/RedisGraph/RedisGraph/pull/2020) Fix evaluation of variable-length edges in expression ordering\n    - [#2028](https://github.com/RedisGraph/RedisGraph/pull/2028) Fix utilization of record offset in procedure calls, refactor outputs\n    - [#2014](https://github.com/RedisGraph/RedisGraph/pull/2014) Update label for every node in the AST\n    - [#2002](https://github.com/RedisGraph/RedisGraph/pull/2002) Fix crash in index utilization using wrong query_graph\n    - [#1976](https://github.com/RedisGraph/RedisGraph/pull/1976) Use operand matrix when available\n    - [#1973](https://github.com/RedisGraph/RedisGraph/pull/1973) Emit error on a query that only contains parameters\n    - [#1950](https://github.com/RedisGraph/RedisGraph/pull/1950) Print ExecutionPlan in `GRAPH.EXPLAIN` only if no errors are encountered in the construction\n    - [#1933](https://github.com/RedisGraph/RedisGraph/pull/1933) Free thread-local data on graph deletion\n    - [#1942](https://github.com/RedisGraph/RedisGraph/pull/1942) Fix dimensions of transposed delta matrices\n    - [#1940](https://github.com/RedisGraph/RedisGraph/pull/1940) Do not use block client if deny blocking is specified\n    - [#1898](https://github.com/RedisGraph/RedisGraph/pull/1898) Error when setting a property to an array containing an invalid type\n    - [#1931](https://github.com/RedisGraph/RedisGraph/pull/1931) Sync matrices on parent process before serialization fork\n    - [#1897](https://github.com/RedisGraph/RedisGraph/pull/1897) Aliases in `WITH` ... `ORDER BY` must be valid references\n    - [#1913](https://github.com/RedisGraph/RedisGraph/pull/1913) Update thread-local AST for every cloned operation\n    - [#1915](https://github.com/RedisGraph/RedisGraph/pull/1915) Validate function references in parameters\n    - [#1911](https://github.com/RedisGraph/RedisGraph/pull/1911) Refactor `cron` task for managing query timeouts\n    - [#1902](https://github.com/RedisGraph/RedisGraph/pull/1902) Fix incorrect behavior on `NULL` values in `CASE`...`WHEN` expressions\n    - [#1904](https://github.com/RedisGraph/RedisGraph/pull/1904) Allow reconfiguring query timeout to 0\n    - [#1888](https://github.com/RedisGraph/RedisGraph/pull/1888) Synchronize matrices on creation in RDB load\n    - [#1892](https://github.com/RedisGraph/RedisGraph/pull/1892) Validate values specified in `SET` clauses\n    - [#1889](https://github.com/RedisGraph/RedisGraph/pull/1889) Tuple iterator now updates properly when changing matrix to serialize\n    - [#1870](https://github.com/RedisGraph/RedisGraph/pull/1870) Fix crash in `range` function\n    - [#2125](https://github.com/RedisGraph/RedisGraph/issues/2125) Fix crash on `UNION` ... `RETURN` * queries (MOD-2524)\n    - [#2043](https://github.com/RedisGraph/RedisGraph/pull/2043) Avoid serialization of duplicate graph keys\n    - [#2067](https://github.com/RedisGraph/RedisGraph/pull/2067) Numeric indices no longer lose precision on very large values\n    - [#2072](https://github.com/RedisGraph/RedisGraph/issues/2072), [#2081](https://github.com/RedisGraph/RedisGraph/pull/2081) CRLF sequences embedded in strings no longer trigger a protocol error when being emitted\n    - [#2139](https://github.com/RedisGraph/RedisGraph/pull/2139) Fix crash when trying to retrieve an out-of-bounds item\n    - [#2149](https://github.com/RedisGraph/RedisGraph/pull/2149) Fix crash when matching a node engages an index to search for a value that is a RediSearch stop word"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson","title":"RedisJSON release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/","summary":"RedisJSON release notes for version 2.8 and earlier","tags":["docs","operate","stack"],"last_updated":"2024-08-08T19:21:33+03:00","children":[{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes","summary":"RedisJSON 2.8 introduces bug fixes.","title":"RedisJSON 2.8 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes","summary":"RESP3 support. New commands JSON.MERGE and JSON.MSET.","title":"RedisJSON 2.6 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes","summary":"Low-level API changes in order to support the multi-value indexing and querying support that comes with RediSearch 2.6. RediSearch 2.6 comes with multi-value indexing and querying of attributes for any attribute type (Text, Tag, Numeric, Geo and Vector) defined by a JSONPath leading to an array or to multiple scalar values.","title":"RedisJSON 2.4 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.2-release-notes","summary":"Preview of Active-Active support for JSON.","title":"RedisJSON 2.2 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.2-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.0-release-notes","summary":"Index JSON documents. JSONPath support. Commands operate on multiple paths.","title":"RedisJSON 2.0 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.0-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-1.0-release-notes","summary":"Serialization cache for JSON.GET.","title":"RedisJSON 1.0 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-1.0-release-notes/"}],"page_type":"content","content_hash":"ff1e7b5a6bc55c780c62224fdfc068a4a885c2db09b52233de40adf2e3535ccb","sections":[{"id":"content","title":"Content","role":"content","text":"| Version (Release date) | Major changes | Min Redis version | Min cluster version |\n| --- | --- | --- | --- |\n| [v2.8 (July 2024)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes/) | RedisJSON 2.8 introduces bug fixes. | 7.4 | 7.8 |\n| [v2.6 (July 2023)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes/) | RESP3 support. New commands JSON.MERGE and JSON.MSET. | 7.2 | 7.2.4 |\n| [v2.4 (November 2022)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes/) | Low-level API changes in order to support the multi-value indexing and querying support that comes with RediSearch 2.6. RediSearch 2.6 comes with multi-value indexing and querying of attributes for any attribute type (Text, Tag, Numeric, Geo and Vector) defined by a JSONPath leading to an array or to multiple scalar values. | 6.0.16 | 6.2.18 |\n| [v2.2 (July 2022)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.2-release-notes/) | Preview of Active-Active support for JSON. | 6.0.0 | 6.2.12 |\n| [v2.0 (November 2021)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.0-release-notes/) | Index JSON documents. JSONPath support. Commands operate on multiple paths. | 6.0.0 | 6.0.0 |\n| [v1.0 (December 2017)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-1.0-release-notes/) | Serialization cache for JSON.GET. | 4.0.0 | 5.0.0 |"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-1.0-release-notes","title":"RedisJSON 1.0 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-1.0-release-notes/","summary":"Serialization cache for JSON.GET.","tags":["docs","operate","stack"],"last_updated":"2024-08-08T16:28:23-05:00","page_type":"content","content_hash":"0c3ee0bd733c8c7d9da6e9eb82b636988a2e810cf5165f42493ef5378a8adf4c","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RedisJSON v1.0.8 requires:\n\n- Minimum Redis compatibility version (database): 4.0.0\n- Minimum Redis Enterprise Software version (cluster): 5.0.0"},{"id":"v108-august-2021","title":"v1.0.8 (August 2021)","role":"content","text":"This is a maintenance release for version 1.0.\n\nUpdate urgency: `LOW` -  No need to upgrade unless there are new features you want to use.\n\nDetails:\n\n- Enhancements:\n    - #[257](https://github.com/redisjson/redisjson/issues/257) Support inter shards TLS capability\n\n- Bug fixes:\n   - #[282](https://github.com/redisjson/redisjson/issues/282) Change `JSON.DEBUG` first key index to 2"},{"id":"v107-december-2020","title":"v1.0.7 (December 2020)","role":"content","text":"This is a maintenance release for version 1.0.\n\nHeadlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n\nDetails:\n\n- [#222](https://github.com/RedisJSON/RedisJSON/pull/222) Validate path is not empty.\n- [#253](https://github.com/RedisJSON/RedisJSON/pull/253) `NULL` de-reference after error."},{"id":"v105-september-2020","title":"v1.0.5 (September 2020)","role":"content","text":"This is a maintenance release for version 1.0.\n\nHeadlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n\nDetails:\n\n- Enhancements:\n    - Republish docker image based on Redis 6\n- Bugfixes:\n    - #[200](https://github.com/RedisJSON/RedisJSON/issues/200) Following a call to JSON.ARRPOP and JSON.STRAPPEND, the LRU cache should be invalidated."},{"id":"v104-february-2019","title":"v1.0.4 (February 2019)","role":"content","text":"This release updates the version to 1.0.4."},{"id":"v103-12-september-2018","title":"v1.0.3 (12 September 2018)","role":"content","text":"This release disables the cache by default, and allows an explicit `CACHE` `<ON|OFF>` module argument to enable it."},{"id":"v102-10-september-2018","title":"v1.0.2 (10 September 2018)","role":"content","text":"This release contains some bug fixes over 1.0.2. It also includes some ci changes as well."},{"id":"v101-december-2017","title":"v1.0.1 (December 2017)","role":"content","text":"This version provides a serialization cache for JSON.GET (when used with the\ndefault parameters). The cache policy is LRU (last recently used), and extremely\nsmall items are not cached.\n\nIn the future, options may be added to configure the cache, but for the time being\nthe cache is intended to be small and transparent."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.0-release-notes","title":"RedisJSON 2.0 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.0-release-notes/","summary":"Index JSON documents. JSONPath support. Commands operate on multiple paths.","tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:33+01:00","page_type":"content","content_hash":"7675de1f4eb591825e8348f798357ed120c1163781af51efaae65598ff23a530","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RedisJSON v2.0.11 requires:\n\n- Minimum Redis compatibility version (database): 6.0.0\n- Minimum Redis Enterprise Software version (cluster): 6.0.0"},{"id":"v2011-july-2022","title":"v2.0.11 (July 2022)","role":"content","text":"This is a maintenance release for RedisJSON 2.0.\nUpdate urgency: `LOW` - No need to upgrade unless there are new features you want to use.\n\nDetails:\n\n- Improvements:\n\n  - Minor documentation changes and internal build improvements"},{"id":"v209-june-2022","title":"v2.0.9 (June 2022)","role":"content","text":"This is a maintenance release for RedisJSON 2.0.\n\nUpdate urgency: `LOW`: No need to upgrade unless there are new features you want to use.\n\nDetails:\n\n- Bug fixes:\n\n  - [#721](https://github.com/RedisJSON/RedisJSON/pull/721) Skip String and Boolean scalars in `JSON.CLEAR` (MOD-3136)\n\n- Improvements:\n\n  - [#709](https://github.com/RedisJSON/RedisJSON/pull/709) Allow internal JSON API's `getdouble` to succeed with integer values"},{"id":"v208-april-2022","title":"v2.0.8 (April 2022)","role":"content","text":"This is a maintenance release for RedisJSON 2.0.\n\nUpdate urgency: `LOW` - No need to upgrade unless there are new features you want to use.\n\nDetails:\n\n- Bug fixes:\n\n  - [#691](https://github.com/RedisJSON/RedisJSON/pull/691), [#667](https://github.com/RedisJSON/RedisJSON/issues/667) Duplicate results in JSONPath query"},{"id":"v207-march-2022","title":"v2.0.7 (March 2022)","role":"content","text":"This is a maintenance release for RedisJSON 2.0.\n\nUpdate urgency: `LOW` - No need to upgrade unless there are new features you want to use.\n\nDetails:\n\n- Improvements:\n\n  - [#632](https://github.com/RedisJSON/RedisJSON/pull/632), [#605](https://github.com/RedisJSON/RedisJSON/pull/605) Support `JSON.CLEAR` for string, bool, and numeric scalars (MOD-2394)\n  - [#637](https://github.com/RedisJSON/RedisJSON/pull/637) Add `intershard_tls_pass` support (MOD-2522)\n  - [#594](https://github.com/RedisJSON/RedisJSON/pull/594) Support for `MEMORY USAGE` and memory info in `JSON.DEBUG` (MOD- 2079)\n\n- Bug fixes:\n\n  - [#646](https://github.com/RedisJSON/RedisJSON/pull/646), [#644](https://github.com/RedisJSON/RedisJSON/pull/644) Do not fail `JSON.MGET` on wrong/unregistered key type (MOD-2511)\n  - [#643](https://github.com/RedisJSON/RedisJSON/pull/643) Null-terminate JSON string in `rdb_save`\n  - [#591](https://github.com/RedisJSON/RedisJSON/pull/591) Avoid crash on overflow in `JSON.NUMINCRBY` or `JSON.NUMMULTBY` (MOD-2513)\n  - [#593](https://github.com/RedisJSON/RedisJSON/pull/593) Return no updates when performing `JSON.SET` with `NX` to an existing array element (MOD-2512)"},{"id":"v206-december-2021","title":"v2.0.6 (December 2021)","role":"content","text":"This is a maintenance release for RedisJSON 2.0.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nThis patch neutralizes the increased memory consumption from v1 to v2.\n\nDetails:\n\n- Improvements:\n\n  - [#563](https://github.com/RedisJSON/RedisJSON/pull/563) Introduction of [ijson](https://libraries.io/cargo/ijson)."},{"id":"v205-december-2021","title":"v2.0.5 (December 2021)","role":"content","text":"This is a maintenance release for RedisJSON 2.0.\n\nDetails:\n\n- Bug fixes:\n  - #[553](https://github.com/RedisJSON/RedisJSON/pull/553) Return an empty array on a nonexistent path\n  - #[548](https://github.com/RedisJSON/RedisJSON/pull/548) Align error handling behavior\n  - #[546](https://github.com/RedisJSON/RedisJSON/pull/546) #[545](https://github.com/RedisJSON/RedisJSON/pull/545) Fix key location in `JSON.DEBUG MEMORY`"},{"id":"v204-november-2021","title":"v2.0.4 (November 2021)","role":"content","text":"This is the General Availability release of RedisJSON 2.0."},{"id":"headlines","title":"Headlines","role":"content","text":"RedisJSON is a [high-performance JSON document store](https://redis.com/blog/redisjson-public-preview-performance-benchmarking/) that allows developers to build modern applications. It stores and processes JSON in-memory, supporting millions of operations per second with sub-millisecond response times. The combination of `RediSearch`, native indexing, querying, and full-text search of JSON documents allows developers to create secondary indexes and query data at lightning speed.\n\n#### Indexing JSON documents\n\nUsing [RediSearch](https://redisearch.io), it is now possible to [index, query, and search JSON documents](https://redis.io/docs/latest/develop/ai/search-and-query/indexing), gaining full-text search capabilities and document retrieving based on their content.\n\nTo do so, you must install both modules, `RedisJSON` and `RediSearch`, on the same database.\n\n#### Support of JSON Path\n\nThe commands [support JSONPath](https://redis.io/docs/latest/develop/data-types/json/path) as specified in the [original specifications](https://goessner.net/articles/JsonPath).\n\nThe legacy path syntax is still supported.\n\n#### Commands operate on multiple paths\n\nA `JSONPath` query may resolve to several paths. Every command supports multiple paths and applies the operation to all the encountered paths.\n\nNotice that the output of the commands evolved to provide multiple results according to the number of paths impacted."},{"id":"details","title":"Details","role":"content","text":"- Enhancements\n  - #[477](https://github.com/RedisJSON/RedisJSON/pull/477) Support of Multipath\n  - #[336](https://github.com/RedisJSON/RedisJSON/pull/336) Added generic JSON path implementation \n  - #[525](https://github.com/RedisJSON/RedisJSON/pull/525) Error messages prefixed with ERR or WRONGTYPE \n  - #[490](https://github.com/RedisJSON/RedisJSON/pull/490) Performance: Discard `to_value` method\n  - #[426](https://github.com/RedisJSON/RedisJSON/pull/426) Move from next_string to next_str \n  - #[464](https://github.com/RedisJSON/RedisJSON/pull/464) Initial RedisJSON commands.json file \n  - #[488](https://github.com/RedisJSON/RedisJSON/pull/488) Docker with RediSearch revisited \n\n- Bug fixes\n  - #[515](https://github.com/RedisJSON/RedisJSON/pull/515) JSON.DEL count deleted null value \n  - #[499](https://github.com/RedisJSON/RedisJSON/pull/499) Avoid crash in ARRTRIM \n  - #[458](https://github.com/RedisJSON/RedisJSON/pull/458) Docker/Debian: moved from Buster to Bullseye \n  - #[397](https://github.com/RedisJSON/RedisJSON/pull/397) Support RDB Short Read in RedisJSON (a.k.a diskless-load) \n  - #[398](https://github.com/RedisJSON/RedisJSON/pull/398) Avoid path clone when not needed \n  - #[416](https://github.com/RedisJSON/RedisJSON/pull/416) Add testGetWithBracketNotation"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.2-release-notes","title":"RedisJSON 2.2 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.2-release-notes/","summary":"Preview of Active-Active support for JSON.","tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:33+01:00","page_type":"content","content_hash":"3144ff3d9de05591a75de3db32709a6a091d9e74be7ad329f1cc13c50b94a397","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RedisJSON v2.2.0 requires:\n\n- Minimum Redis compatibility version (database): 6.0.0\n- Minimum Redis Enterprise Software version (cluster): 6.2.18"},{"id":"v220-july-2022","title":"v2.2.0 (July 2022)","role":"content","text":"A preview of RedisJSON 2.2 is available for Free and Fixed subscription plans in Redis Cloud."},{"id":"headlines","title":"Headlines","role":"content","text":"This release adds support for the JSON data structure as a CRDT (Conflict-free Replicated Data Type) when used with Redis Enterprise [Active-Active databases](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json/active-active).\n\nActive-Active JSON requires Redis Enterprise Software v6.2.12. Contact your account manager or support to access the preview of RedisJSON 2.2."},{"id":"details","title":"Details","role":"content","text":"- Enhancements:\n\n  - [#758](https://github.com/RedisJSON/RedisJSON/pull/758) Add support for [`COPY`](https://redis.io/docs/latest/commands/copy)"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes","title":"RedisJSON 2.4 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes/","summary":"Low-level API changes in order to support the multi-value indexing and querying support that comes with RediSearch 2.6. RediSearch 2.6 comes with multi-value indexing and querying of attributes for any attribute type (Text, Tag, Numeric, Geo and Vector) defined by a JSONPath leading to an array or to multiple scalar values.","tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:33+01:00","page_type":"content","content_hash":"96575473a1c4823a1b1befc71b96f8b81f9c5714371207953bc03b8fa3489279","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RedisJSON v2.4.19 requires:\n\n- Minimum Redis compatibility version (database): 6.0.16\n- Minimum Redis Enterprise Software version (cluster): 6.2.18"},{"id":"v2419-april-2026","title":"v2.4.19 (April 2026):","role":"content","text":"This is a maintenance release for RedisJSON 2.4.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n  - [#1554](https://github.com/redisjson/redisjson/pull/1554) Trailing chars are ignored (MOD-7266); Fixes [#976](https://github.com/redisjson/redisjson/pull/976).\n  - [#1543](https://github.com/redisjson/redisjson/pull/1543) Wrong mutation ordering for array commands with recursive paths (MOD-6722).\n  - [#1542](https://github.com/redisjson/redisjson/pull/1542) JSONPath evaluation issues (MOD-14664); Fixes [#968](https://github.com/redisjson/redisjson/pull/968) (MOD-7264), [#962](https://github.com/redisjson/redisjson/pull/962) (MOD-7272), [#963](https://github.com/redisjson/redisjson/pull/963) (MOD-7270), [#1089](https://github.com/redisjson/redisjson/pull/1089) (MOD-7268)."},{"id":"v2417-september-2025","title":"v2.4.17 (September 2025)","role":"content","text":"This is a maintenance release for RedisJSON 2.4.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n  - [#1374](https://github.com/redisjson/redisjson/pull/1374) - `JSON.DEL` doesn’t delete all matching object members / array elements (MOD-11032, MOD-11067)."},{"id":"v2412-april-2025","title":"v2.4.12 (April 2025)","role":"content","text":"This is a maintenance release for RedisJSON 2.4.\n\nUpdate urgency: `LOW`: No need to upgrade unless there are new features you want to use.\n\nDetails:\n\nImprovements:\n- [#1335](https://github.com/redisjson/redisjson/pull/1335) Added support for Azure Linux 3 (MOD-9172)"},{"id":"v2411-january-2025","title":"v2.4.11 (January 2025)","role":"content","text":"This is a maintenance release for RedisJSON 2.4\n\nUpdate urgency: `LOW`: No need to upgrade unless there are new features you want to use.\n\nDetails\n\n- Bug fixes:\n  - [#1313](https://github.com/redisjson/redisjson/pull/1313) (Redis Enterprise A-A only) Potential crash on `JSON.DEBUG MEMORY` (MOD-8412)\n\n- Improvements:\n  - [#1310](https://github.com/redisjson/redisjson/pull/1310) Added support for Ubuntu 22 and macOS 13 and 14"},{"id":"v249-april-2024","title":"v2.4.9 (April 2024)","role":"content","text":"This is a maintenance release for RedisJSON 2.4.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n  - [#1192](https://github.com/RedisJSON/RedisJSON/pull/1192) Crashes with numeric values greater than i64::MAX (MOD-6501, MOD-4551, MOD-4856, MOD-5714)\n  - HDT#228 (Redis Enterprise A-A only) Incorrect error when processing escaped characters (MOD-6645)"},{"id":"v248-january-2024","title":"v2.4.8 (January 2024)","role":"content","text":"This is a maintenance release for RedisJSON 2.4.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\n> [!NOTE]\n> Ubuntu 16.04 and RHEL 7 are no longer supported as of v2.4.8.\n\nDetails:\n\n- Improvements:\n\n  - [#1149](https://github.com/RedisJSON/RedisJSON/pull/1149) Added support for CBL-Mariner 2\n\n- Bug fixes:\n\n  - [#1025](https://github.com/RedisJSON/RedisJSON/pull/1025) `JSON.TOGGLE` - missing keyspace notification"},{"id":"v247-april-2023","title":"v2.4.7 (April 2023)","role":"content","text":"This is a maintenance release for RedisJSON 2.4.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n  - [#947](https://github.com/RedisJSON/RedisJSON/issues/947) Crash when using array slice operator (`[start:end:step]`) with step `0`"},{"id":"v246-march-2023","title":"v2.4.6 (March 2023)","role":"content","text":"This is a maintenance release for RedisJSON 2.4.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n  - [#912](https://github.com/RedisJSON/RedisJSON/pull/912) Fix actual memory usage calculation (MOD-4787)"},{"id":"v245-february-2023","title":"v2.4.5 (February 2023)","role":"content","text":"This is a maintenance release for RedisJSON 2.4.\n\nUpdate urgency: `LOW`: No need to upgrade unless there are new features you want to use.\n\nDetails:\n\n- Improvements:\n\n  - Adds support to Redis Enterprise on Ubuntu Linux 20.04 LTS (Focal Fossa)"},{"id":"v244-february-2023","title":"v2.4.4 (February 2023)","role":"content","text":"This is a maintenance release for RedisJSON 2.4.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n  - [#919](https://github.com/RedisJSON/RedisJSON/pull/919) Possible failure loading RDB files (MOD-4822)\n  \n- Improvements:\n\n  - [#725](https://github.com/RedisJSON/RedisJSON/issues/725) Improve error messages\n  - [#918](https://github.com/RedisJSON/RedisJSON/pull/918) Add IPv6 to the capabilities list"},{"id":"v243-december-2022","title":"v2.4.3 (December 2022)","role":"content","text":"This is a maintenance release for RedisJSON 2.4.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n  - [#890](https://github.com/RedisJSON/RedisJSON/pull/890) JSONPath ignores any filter condition beyond the second (MOD-4602)\n  \n- Improvements:\n\n  - [#892](https://github.com/RedisJSON/RedisJSON/pull/892) Allow `JSON.ARRINDEX` with nonscalar values"},{"id":"v24-ga-v242-november-2022","title":"v2.4 GA (v2.4.2) (November 2022)","role":"content","text":"This is the General Availability release of RedisJSON 2.4."},{"id":"highlights","title":"Highlights","role":"content","text":"RedisJSON 2.4 contains several low-level API changes in order to support the multi-value indexing and querying support that comes with RediSearch 2.6. RediSearch 2.6 comes with multi-value indexing and querying of attributes for any attribute type (Text, Tag, Numeric, Geo and Vector) defined by a JSONPath leading to an array or to multiple scalar values."},{"id":"whats-new-in-24","title":"What's new in 2.4","role":"content","text":"- Features:\n\n  - [#848](https://github.com/RedisJSON/RedisJSON/pull/848) Add JSONPath filter the regexp match operator (MOD-4432)\n  - [#861](https://github.com/RedisJSON/RedisJSON/pull/861) Support legacy JSONPath with the dollar sign $ (MOD-4436)\n\n- Performance enhancements:\n\n  - [#699](https://github.com/RedisJSON/RedisJSON/pull/699) A new JSONPath library which enhances the performance of parsing any JSONPath expression in RedisJSON.\n\n- Changing behaviour:\n\n  - [#699](https://github.com/RedisJSON/RedisJSON/pull/699) Floating point numbers which become round numbers due to some operation, such as `JSON.NUMINCRBY`, will now return as a floating point with a trailing `.0`, e.g., instead of just `42`, now `42.0` will be returned.\n\n- Bugs fixes (since 2.4-RC1/ v2.4.0):\n\n  - [#850](https://github.com/RedisJSON/RedisJSON/pull/850) Allow repetition of filter relation instead of optional (MOD-4431)"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes","title":"RedisJSON 2.6 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes/","summary":"RESP3 support. New commands JSON.MERGE and JSON.MSET.","tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:33+01:00","page_type":"content","content_hash":"f1ed3a39be2627cbf4cb010591d4459a4dc5250c77d0c25434f75cba3e3b0ed9","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RedisJSON v2.6.24 requires:\n\n- Minimum Redis compatibility version (database): 7.2\n- Minimum Redis Enterprise Software version (cluster): 7.2.4"},{"id":"v2624-april-2026","title":"v2.6.24 (April 2026):","role":"content","text":"This is a maintenance release for RedisJSON 2.6.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n  - [#1554](https://github.com/redisjson/redisjson/pull/1554) Trailing chars are ignored (MOD-7266); Fixes [#976](https://github.com/redisjson/redisjson/pull/976).\n  - [#1543](https://github.com/redisjson/redisjson/pull/1543) Wrong mutation ordering for array commands with recursive paths (MOD-6722).\n  - [#1542](https://github.com/redisjson/redisjson/pull/1542) JSONPath evaluation issues (MOD-14664); Fixes [#968](https://github.com/redisjson/redisjson/pull/968) (MOD-7264), [#962](https://github.com/redisjson/redisjson/pull/962) (MOD-7272), [#963](https://github.com/redisjson/redisjson/pull/963) (MOD-7270), [#1089](https://github.com/redisjson/redisjson/pull/1089) (MOD-7268)."},{"id":"v2622-october-2025","title":"v2.6.22 (October 2025)","role":"content","text":"This is a maintenance release for RedisJSON 2.6.\n\nUpdate urgency: `LOW`: No need to upgrade unless there are new features you want to use.\n\nDetails:\n\n- Improvements:\n  - Added support for Rocky Linux 9 and RHEL9 ARM."},{"id":"v2621-september-2025","title":"v2.6.21 (September 2025)","role":"content","text":"This is a maintenance release for RedisJSON 2.6.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes\n  - [#1374](https://github.com/redisjson/redisjson/pull/1374) - `JSON.DEL` doesn’t delete all matching object members / array elements (MOD-11032, MOD-11067)."},{"id":"v2615-april-2025","title":"v2.6.15 (April 2025)","role":"content","text":"This is a maintenance release for RedisJSON 2.6.\n\nUpdate urgency: `LOW`: No need to upgrade unless there are new features you want to use.\n\nDetails:\n\n- Bug fixes:\n  - [#1329](https://github.com/redisjson/redisjson/pull/1329) Memory usage calculation: some allocations are counted twice (MOD-9169) \n\n- Improvements:\n  - [#1335](https://github.com/redisjson/redisjson/pull/1335) Added support for Azure Linux 3 (MOD-9172)"},{"id":"v2614-january-2025","title":"v2.6.14 (January 2025)","role":"content","text":"This is a maintenance release for RedisJSON 2.6\n\nUpdate urgency: `LOW`: No need to upgrade unless there are new features you want to use.\n\nDetails:\n\n- Bug fixes:\n  - [#1313](https://github.com/redisjson/redisjson/pull/1313) (Redis Enterprise A-A only) Potential crash on `JSON.DEBUG MEMORY` (MOD-8412)\n\n- Improvements:\n  - [#1312](https://github.com/redisjson/redisjson/pull/1312) Added support for Ubuntu 22 and macOS 13 and 14"},{"id":"v2612-september-2024","title":"v2.6.12 (September 2024)","role":"content","text":"This is a maintenance release for RedisJSON 2.6\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n  - [#1225](https://github.com/redisjson/redisjson/pull/1225) Crash on SET commands with recursive overlapping paths (MOD-7279)\n  - HDT#261 (Redis Enterprise A-A only) Crash when a JSON contains an EOF character (MOD-7464)"},{"id":"v2610-april-2024","title":"v2.6.10 (April 2024)","role":"content","text":"This is a maintenance release for RedisJSON 2.6.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n  - [#1192](https://github.com/RedisJSON/RedisJSON/pull/1192) Crashes with numeric values greater than i64::MAX (MOD-6501, MOD-4551, MOD-4856, MOD-5714)\n  - HDT#228 (Redis Enterprise A-A only) Incorrect error when processing escaped characters (MOD-6645)"},{"id":"v269-january-2024","title":"v2.6.9 (January 2024)","role":"content","text":"This is a maintenance release for RedisJSON 2.6.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Improvements:\n\n   - [#1131](https://github.com/RedisJSON/RedisJSON/issues/1131), [#1143](https://github.com/RedisJSON/RedisJSON/pull/1143) **BREAKING** - Revert JSONPath default path value from `$` to `.` under RESP3 (MOD-6156)\n\n- Bug fixes:\n\n  - [#1095](https://github.com/RedisJSON/RedisJSON/pull/1095) Fix for RediSearch deadlock. See RediSearch 2.8.10 release notes (MOD-5895)"},{"id":"v268-november-2023","title":"v2.6.8 (November 2023)","role":"content","text":"This is a maintenance release for RedisJSON 2.6.\n\nUpdate urgency: `LOW`: No need to upgrade unless there are new features you want to use.\n\nDetails:\n\n> [!NOTE]\n> RHEL 7 is no longer supported as of v2.6.8.\n\nThis is a version number alignment with RedisJSON for Redis Enterprise (with Active-Active support)."},{"id":"v267-october-2023","title":"v2.6.7 (October 2023)","role":"content","text":"This is a maintenance release for RedisJSON 2.6.\n\nUpdate urgency: `LOW`: No need to upgrade unless there are new features you want to use.\n\nDetails:\n\n- Improvements:\n\n  - [#1102](https://github.com/RedisJSON/RedisJSON/pull/1102) Added support for CBL-Mariner 2\n  - [#1099](https://github.com/RedisJSON/RedisJSON/pull/1099) Added support for RHEL9 and Rocky Linux 9"},{"id":"v266-august-2023","title":"v2.6.6 (August 2023)","role":"content","text":"This is a maintenance release for RedisJSON 2.6.\n\nUpdate urgency: `LOW`: No need to upgrade unless there are new features you want to use.\n\nDetails:\n\nThis is just a version number alignment with RedisJSON for Redis Enterprise (with Active-Active support)."},{"id":"v26-ga-v264-july-2023","title":"v2.6 GA (v2.6.4) (July 2023)","role":"content","text":"This is the General Availability release of RedisJSON 2.6."},{"id":"headlines","title":"Headlines","role":"content","text":"RedisJSON 2.6 introduces support for RESP3 and new commands."},{"id":"whats-new-in-26","title":"What's new in 2.6","role":"content","text":"- Introduce [`JSON.MERGE`](https://redis.io/docs/latest/commands/json.merge) in compliance with [RFC 7396](https://datatracker.ietf.org/doc/html/rfc7396), supporting:\n\n  - Creating new attributes on an existing JSON document\n\n  - Updating and replacing values in parent and child attributes\n\n  - Deleting existing attributes (by setting the value to `null`)\n\n  - Array update - replacing an entire array with the new value\n\n- Introduce [`JSON.MSET`](https://redis.io/docs/latest/commands/json.mset), supporting atomic multiple sets for keys in the same hash slot\n\n- New `FORMAT` argument in `JSON.ARRPOP` and `JSON.GET` to retrieve the results as JSON strings or RESP3 hierarchical structures (RESP3 only)\n\n- `JSON.RESP` is now regarded as deprecated\n\n- Legacy paths (paths that don't start with either `$.` or `$[` or equal to `$`), except those starting with `.`, are now deprecated\n\n> [!NOTE]\n> - The version inside Redis will be 2.6.4 in semantic versioning. Since the version of a module in Redis is numeric, we could not add a GA flag.\n>\n> - Minimal Redis version: 7.2"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes","title":"RedisJSON 2.8 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes/","summary":"RedisJSON 2.8 introduces bug fixes.","tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:33+01:00","page_type":"content","content_hash":"6d70fa597f10db75d1d2450eab2c569e3f9636b6bccd1f9b31fbd80f7d71afb4","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RedisJSON v2.8.19 requires:\n\n- Minimum Redis compatibility version (database): 7.4\n- Minimum Redis Enterprise Software version (cluster): 7.8"},{"id":"v2819-april-2026","title":"v2.8.19 (April 2026):","role":"content","text":"> [!NOTE]\n> Starting with Redis 8 in Redis Open Source, the JSON data structure is integral to Redis. You don't need to install this module separately.\n>\n> We no longer release standalone versions of RedisJSON.\n>\n> See https://github.com/redis/redis.\n\nThis is a maintenance release for RedisJSON 2.8.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n  - [#1554](https://github.com/redisjson/redisjson/pull/1554) Trailing chars are ignored (MOD-7266); Fixes [#976](https://github.com/redisjson/redisjson/pull/976).\n  - [#1543](https://github.com/redisjson/redisjson/pull/1543) Wrong mutation ordering for array commands with recursive paths (MOD-6722).\n  - [#1542](https://github.com/redisjson/redisjson/pull/1542) JSONPath evaluation issues (MOD-14664); Fixes [#968](https://github.com/redisjson/redisjson/pull/968) (MOD-7264), [#962](https://github.com/redisjson/redisjson/pull/962) (MOD-7272), [#963](https://github.com/redisjson/redisjson/pull/963) (MOD-7270), [#1089](https://github.com/redisjson/redisjson/pull/1089) (MOD-7268)."},{"id":"v2816-november-2025","title":"v2.8.16 (November 2025)","role":"content","text":"This is a maintenance release for RedisJSON 2.8.\n\nUpdate urgency: `LOW`: No need to upgrade unless there are new features you want to use.\n\nDetails:\n\nBug fixes:\n- [#1446](https://github.com/redisjson/redisjson/pull/1446) Rare race condition in async flush (MOD-12014)."},{"id":"v2815-october-2025","title":"v2.8.15 (October 2025)","role":"content","text":"This is a maintenance release for RedisJSON 2.8.\n\nUpdate urgency: `LOW`: No need to upgrade unless there are new features you want to use.\n\nDetails:\n\nImprovements:\n\n- Added support for Rocky Linux 9 and RHEL9 ARM."},{"id":"v2814-september-2025","title":"v2.8.14 (September 2025)","role":"content","text":"This is a maintenance release for RedisJSON 2.8.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\nBug fixes\n- [#1374](https://github.com/redisjson/redisjson/pull/1374) - `JSON.DEL` doesn’t delete all matching object members / array elements (MOD-11032, MOD-11067)."},{"id":"v289-april-2025","title":"v2.8.9 (April 2025)","role":"content","text":"This is a maintenance release for RedisJSON 2.8.\n\nUpdate urgency: `LOW`: No need to upgrade unless there are new features you want to use.\n\nDetails:\n\nBug fixes\n- [#1329](https://github.com/redisjson/redisjson/pull/1329) Memory usage calculation: some allocations are counted twice (MOD-9169) \n\nImprovements:\n- [#1335](https://github.com/redisjson/redisjson/pull/1335) Added support for Azure Linux 3 (MOD-9172)"},{"id":"v288-january-2025","title":"v2.8.8 (January 2025)","role":"content","text":"This is a maintenance release for RedisJSON 2.8\n\nUpdate urgency: `LOW`: No need to upgrade unless there are new features you want to use.\n\nDetails:  \n\n- Bug fixes:\n  - [#1313](https://github.com/redisjson/redisjson/pull/1313) (Redis Enterprise A-A only) Potential crash on `JSON.DEBUG MEMORY` (MOD-8412)\n\n- Improvements:\n  - [#1262](https://github.com/redisjson/redisjson/pull/1262) Support active memory defragmentation (MOD-7888)\n  - [#1308](https://github.com/redisjson/redisjson/pull/1308) Added support for Ubuntu 22 and macOS 13 and 14"},{"id":"v284-september-2024","title":"v2.8.4 (September 2024)","role":"content","text":"This is a maintenance release for RedisJSON 2.8\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n  - [#1225](https://github.com/redisjson/redisjson/pull/1225) Crash on SET commands with recursive overlapping paths (MOD-7279)\n  - HDT#261 (Redis Enterprise A-A only) Crash when a JSON contains an EOF character (MOD-7464)"},{"id":"v28-ga-v283-july-2024","title":"v2.8 GA (v2.8.3) (July 2024)","role":"content","text":"This is the General Availability release of RedisJSON 2.8"},{"id":"headlines","title":"Headlines:","role":"content","text":"RedisJSON 2.8 introduces bug fixes.\n\nDetails\n\n- Bug fixes\n\n  - [#1212](https://github.com/RedisJSON/RedisJSON/pull/1212) `JSON.MSET`- AOF commands are duplicated multiple times (MOD-7293)\n\n> [!NOTE]\n> - The version inside Redis will be 2.8.3 in semantic versioning. Since the version of a module in Redis is numeric, we could not add a GA flag.\n> - Minimal Redis version: 7.4"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisstack","title":"Redis Stack release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisstack/","summary":"Redis Stack release notes for version 7.2 and later","tags":["docs","operate","stack"],"last_updated":"2024-10-30T14:10:10-07:00","children":[{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisstack/redisstack-6.2.6-release-notes","summary":"Redis Stack 6.2.6 release notes.","title":"Redis Stack 6.2.6 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisstack/redisstack-6.2.6-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisstack/redisstack-7.2-release-notes","summary":"Redis Stack 7.2 release notes.","title":"Redis Stack 7.2 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisstack/redisstack-7.2-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisstack/redisstack-7.4-release-notes","summary":"Redis Stack 7.4 release notes.","title":"Redis Stack 7.4 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisstack/redisstack-7.4-release-notes/"}],"page_type":"content","content_hash":"f2419393709647e75b64c8417d6b9f732f068ec4907cca491383d063c1d26db4","sections":[{"id":"content","title":"Content","role":"content","text":"| Version (Release date) | Major changes |\n| --- | --- |\n| [v6.2.6-v0 (December 2022)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisstack/redisstack-6.2.6-release-notes/) | Redis Stack 6.2.6 release notes. |\n| [v7.2.0-v0 (August 2023)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisstack/redisstack-7.2-release-notes/) | Redis Stack 7.2 release notes. |\n| [v7.4.0-v0 (July 2024)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisstack/redisstack-7.4-release-notes/) | Redis Stack 7.4 release notes. |"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisstack/redisstack-6.2.6-release-notes","title":"Redis Stack 6.2.6 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisstack/redisstack-6.2.6-release-notes/","summary":"Redis Stack 6.2.6 release notes.","tags":["docs","operate","stack"],"last_updated":"2024-11-05T16:33:52+01:00","page_type":"content","content_hash":"5794d4a61ee9734c470d5b1824b18b9bdb22ed3144a19af807305f6e5bb0a3c4","sections":[{"id":"redis-stack-server-626-v17-october-2024","title":"Redis Stack Server 6.2.6-v17 (October 2024)","role":"overview","text":"This is a maintenance release for Redis Stack Server 6.2.6\n\nUpdate urgency: `SECURITY`: there are security fixes in the release.\n\n[Docker](https://hub.docker.com/r/redis/redis-stack) | [Download](https://redis.io/downloads/#stack)"},{"id":"headlines","title":"Headlines:","role":"content","text":"This version includes security fixes for the **Redis** server, addressing potential vulnerabilities such as an RCE when using Lua library components, and a denial-of-service (DoS) risk due to unbounded pattern matching.\nAdditionally, this maintenance release includes the latest version of **Redis Insight**."},{"id":"details","title":"Details:","role":"content","text":"**Security and privacy**\n* **Redis**:\n  * (CVE-2024-31449) Lua library commands may lead to stack overflow and potential RCE.\n  * (CVE-2024-31228) Potential Denial-of-service due to unbounded pattern matching.\n\n**Redis version**\n* [Redis 6.2.16](https://github.com/redis/redis/releases/tag/6.2.16)\n\n**Module versions**\t\n* __[RediSearch 2.6.20](https://github.com/RediSearch/RediSearch/releases/tag/v2.6.20)__\n* __[RedisJSON 2.4.9](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.4.9)__\n* __[RedisGraph 2.10.12](https://github.com/RedisGraph/RedisGraph/releases/tag/v2.10.15)__\n* __[RedisTimeSeries 1.8.14](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.8.14)__\n* __[RedisBloom 2.4.9](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.4.9)__\n\n**Recommended Client Libraries**\n* Java\n  * [Jedis 5.2.0 or greater](https://github.com/redis/jedis/releases/tag/v5.2.0)\n  * [redis-om-spring 0.9.5 or greater](https://github.com/redis/redis-om-spring/releases/tag/v0.9.5)\n* Python\n  * [redis-py 5.1.0 or greater](https://github.com/redis/redis-py/releases/tag/v5.1.0)\n  * [redis-om-python 0.3.2 or greater](https://github.com/redis/redis-om-python/releases/tag/v0.3.2)\n* NodeJS\n  * [node-redis 4.7.0 or greater](https://github.com/redis/node-redis/releases/tag/redis%404.7.0)\n  * [redis-om-node 0.2.0 or greater](https://github.com/redis/redis-om-node/releases/tag/v0.2.0)\n* .NET\n  * [redis-om-dotnet 0.7.4 or greater](https://github.com/redis/redis-om-dotnet/releases/tag/v0.7.4)\n  * [NRedisStack 0.13.0 or greater](https://github.com/redis/NRedisStack/releases/tag/v0.13.0)\n* Go\n  * [go-redis 9.6.1 or greater](https://github.com/redis/go-redis/releases/tag/v9.6.1)\n  * [rueidis 1.0.47 or greater](https://github.com/redis/rueidis/releases/tag/v1.0.47)\n\nCompatible with [Redis Insight](https://redis.io/download). The docker image redis/redis-stack for this version is bundled with [Redis Insight 2.58](https://github.com/RedisInsight/RedisInsight/releases/tag/2.58.0).\n\nNote: version numbers follow the pattern:\n\n`x.y.z-b`\n* `x.y` Redis major version\n* `z` increases with even numbers as a module x.y version increases.\n* `b` denotes a patch to Redis or a module (any `z` of Redis or modules). `b` will consist of a `v` + numeric value."},{"id":"redis-stack-server-626-v16-august-2024","title":"Redis Stack Server 6.2.6-v16 (August 2024)","role":"content","text":"This is a maintenance release for Redis Stack Server 6.2.6\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent."},{"id":"headlines-1","title":"Headlines:","role":"content","text":"The updated **search and query**  version introduces several new features and bug fixes. This new release of Redis Stack 6.2.6 also includes updated versions of **JSON** and **time series** data structures, each incorporating several bug fixes.\n\nThis maintenance release also contains the latest version of **RedisInsight**.\n\n ### Details:\n\n **Improvements**\n* **Search and query**:\n  * [#4793](https://github.com/RediSearch/RediSearch/pull/4793) - Add character validations to simple string replies and escape it when required(MOD-7258)\n  * [#4769](https://github.com/RediSearch/RediSearch/pull/4769) - Indicate which value is missing on the error message at the aggregation pipeline (MOD-7201)\n  * [#4746](https://github.com/RediSearch/RediSearch/pull/4746) - `GROUPBY` recursion cleanup (MOD-7245)\n\n**Bug Fixes**\n* **Search and query**:\n  * [#4755](https://github.com/RediSearch/RediSearch/pull/4755) - Correct return the maximum value for negative values when using `MAX` reducer (MOD-7252)\n  * [#4733](https://github.com/RediSearch/RediSearch/pull/4733) - Separators ignored when escaping backslash `\\` after the escaped character such as in `hello\\\\,world` ignoring `,` (MOD-7240)\n  * [#4717](https://github.com/RediSearch/RediSearch/pull/4717) - Sorting by multiple fields as in `SORTBY 2 @field1 @field2` was ignoring the subsequent field (MOD-7206)\n\n* **Time series**:\n  * [#1607](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1607) Potential crash after deleting and recreating a source key of a compaction rule (MOD-7338)\n  * [#1610](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1610) `COUNT` argument accepts non-positive values (MOD-5413)\n\n**Redis version**\n* [Redis 6.2.14](https://github.com/redis/redis/releases/tag/6.2.14)\n\n**Module versions**\t\n* __[RediSearch 2.6.20](https://github.com/RediSearch/RediSearch/releases/tag/v2.6.20)__\n* __[RedisJSON 2.4.9](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.4.9)__\n* __[RedisGraph 2.10.12](https://github.com/RedisGraph/RedisGraph/releases/tag/v2.10.15)__\n* __[RedisTimeSeries 1.8.14](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.8.14)__\n* __[RedisBloom 2.4.9](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.4.9)__\n\n**Recommended Client Libraries**\n* Java\n  * [Jedis 5.1.4 or greater](https://github.com/redis/jedis/releases/tag/v5.1.4)\n  * [redis-om-spring 0.9.4 or greater](https://github.com/redis/redis-om-spring/releases/tag/v0.9.4)\n* Python\n  * [redis-py 5.0.9 or greater](https://github.com/redis/redis-py/releases/tag/v5.0.9)\n  * [redis-om-python 0.3.1 or greater](https://github.com/redis/redis-om-python/releases/tag/v0.3.1)\n* NodeJS\n  * [node-redis 4.7.0 or greater](https://github.com/redis/node-redis/releases/tag/redis%404.7.0)\n  * [redis-om-node 0.2.0 or greater](https://github.com/redis/redis-om-node/releases/tag/v0.2.0)\n* .NET\n  * [redis-om-dotnet 0.7.4 or greater](https://github.com/redis/redis-om-dotnet/releases/tag/v0.7.4)\n  * [NRedisStack 0.12.0 or greater](https://github.com/redis/NRedisStack/releases/tag/v0.12.0)\n* Go\n  * [go-redis 9.6.1 or greater](https://github.com/redis/go-redis/releases/tag/v9.6.1)\n  * [rueidis 1.0.43 or greater](https://github.com/redis/rueidis/releases/tag/v1.0.43)\n\nCompatible with [Redis Insight](https://redis.io/download). The docker image redis/redis-stack for this version is bundled with [Redis Insight 2.54](https://github.com/RedisInsight/RedisInsight/releases/tag/2.54.0).\n\nNote: version numbers follow the pattern:\n\n`x.y.z-b`\n* `x.y` Redis major version\n* `z` increases with even numbers as a module x.y version increases.\n* `b` denotes a patch to Redis or a module (any `z` of Redis or modules). `b` will consist of a `v` + numeric value."},{"id":"redis-stack-server-626-v15-june-2024","title":"Redis Stack Server 6.2.6-v15 (June 2024)","role":"content","text":"This is a maintenance release for Redis Stack Server 6.2.6\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent."},{"id":"headlines-2","title":"Headlines:","role":"content","text":"Redis Stack 6.2.6-v15 introduces an updated **search and query** capability with several enhancements and bug fixes.\nThe updated **search and query** version features improved memory reporting that accounts for additional memory consumed by `TAG` and `TEXT` tries. Also, it includes additional fields in the `FT.INFO` command when used within a cluster.\nThis maintenance release also contains the latest version of **RedisInsight**."},{"id":"details-1","title":"Details:","role":"content","text":"**Improvements**\n* **Search and query**:\n  * [#4599](https://github.com/RediSearch/RediSearch/pull/4599) - Report additional memory consumed by the `TAG` and `TEXT` tries (MOD-5902)\n  * [#4688](https://github.com/RediSearch/RediSearch/pull/4688) - Add missing `FT.INFO` fields when used within a cluster (MOD-6920)\n\n**Bug Fixes**\n* **Search and query**:\n  * [#4616](https://github.com/RediSearch/RediSearch/pull/4616) - Shards become unresponsive when using `FT.AGGREGATE` with `APPLY 'split(...)'`(MOD-6759)\n  * [#4557](https://github.com/RediSearch/RediSearch/pull/4557) - `FT.EXPLAIN` returns additional `}` when querying using wildcards (MOD-6768)\n  * [#4647](https://github.com/RediSearch/RediSearch/pull/4647) - `FT.DROPINDEX` with `DD` flag deleted keys in one AA cluster but not the others (MOD-1855)\n\n**Redis version**\n* [Redis 6.2.14](https://github.com/redis/redis/releases/tag/6.2.14)\n\n**Module versions**\t\n* __[RediSearch 2.6.19](https://github.com/RediSearch/RediSearch/releases/tag/v2.6.19)__\n* __[RedisJSON 2.4.9](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.4.9)__\n* __[RedisGraph 2.10.12](https://github.com/RedisGraph/RedisGraph/releases/tag/v2.10.15)__\n* __[RedisTimeSeries 1.8.13](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.8.13)__\n* __[RedisBloom 2.4.9](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.4.9)__\n\n**Recommended Client Libraries**\n* Java\n  * [Jedis 5.1.3 or later](https://github.com/redis/jedis/releases/tag/v5.1.3)\n  * [redis-om-spring 0.9.1 or later](https://github.com/redis/redis-om-spring/releases/tag/v0.9.1)\n* Python\n  * [redis-py 5.0.5 or later](https://github.com/redis/redis-py/releases/tag/v5.0.5)\n  * [redis-om-python 0.3.1 or later](https://github.com/redis/redis-om-python/releases/tag/v0.3.1)\n* NodeJS\n  * [node-redis 4.6.14 or later](https://github.com/redis/node-redis/releases/tag/redis%404.6.14)\n  * [redis-om-node 0.2.0 or later](https://github.com/redis/redis-om-node/releases/tag/v0.2.0)\n* .NET\n  * [redis-om-dotnet 0.7.1 or later](https://github.com/redis/redis-om-dotnet/releases/tag/v0.7.1)\n  * [NRedisStack 0.12.0 or later](https://github.com/redis/NRedisStack/releases/tag/v0.12.0)\n* Go\n  * [go-redis 9.5.2 or later](https://github.com/redis/go-redis/releases/tag/v9.5.2)\n  * [rueidis 1.0.38 or later](https://github.com/redis/rueidis/releases/tag/v1.0.38)\n\nCompatible with [Redis Insight](https://redis.io/download). The docker image redis/redis-stack for this version is bundled with [Redis Insight 2.50](https://github.com/RedisInsight/RedisInsight/releases/tag/2.50.0).\n\nNote: version numbers follow the pattern:\n\n`x.y.z-b`\n* `x.y` Redis major version\n* `z` increases with even numbers as a module x.y version increases.\n* `b` denotes a patch to Redis or a module (any `z` of Redis or modules). `b` will consist of a `v` + numeric value."},{"id":"downloads","title":"Downloads","role":"content","text":"* macOS: [x86_64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v15.catalina.x86_64.zip), [arm64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v15.monterey.arm64.zip)\n* AppImage: [x86_64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v15-x86_64.AppImage)\n* Ubuntu: [Bionic x86_64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v15.bionic.x86_64.tar.gz), [Bionic arm64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v15.bionic.arm64.tar.gz), [Focal x86_64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v15.focal.x86_64.tar.gz), [Focal arm64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v15.focal.arm64.tar.gz), [Snap x86_64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v15.x86_64.snap), [Snap arm64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v15.arm64.snap), [Jammy x86_64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v15.jammy.x86_64.tar.gz), [Jammy arm64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v15.jammy.arm64.tar.gz)\n* Debian: [Bullseye x86_64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v15.bullseye.x86_64.tar.gz)\n* RHEL 7/CentOS Linux 7: [x86_64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v15.rhel7.x86_64.tar.gz)\n* RHEL 8/CentOS Linux 8: [x86_64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v15.rhel8.x86_64.tar.gz)\n* Redis Stack on [Dockerhub](https://hub.docker.com/u/redis): [x86_64 and arm64](https://hub.docker.com/r/redis/redis-stack)\n* Redis Stack server on [Dockerhub](https://hub.docker.com/u/redis): [x86_64 and arm64](https://hub.docker.com/r/redis/redis-stack-server)"},{"id":"redis-stack-server-626-v14-april-2024","title":"Redis Stack Server 6.2.6-v14 (April 2024)","role":"content","text":"This is a maintenance release for Redis Stack Server 6.2.6\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!"},{"id":"headlines-3","title":"Headlines:","role":"content","text":"This version contains the latest **search and query** capability with several improvements and bug fixes, including a critical bug fix. This release also includes the latest **JSON** data structure with a fix for a potential crash, and the **time series** data structure with more detailed LibMR error messages and a fix for a potential crash. It also contains the latest version of **RedisInsight**."},{"id":"details-2","title":"Details:","role":"content","text":"**Improvements**\n* **Search and query**:\n  * [#4502](https://github.com/RediSearch/RediSearch/pull/4502) Handle error properly when trying to execute Search commands on cluster setup as part of `MULTI`/`EXEC` or LUA script (MOD-6541)\n\n* **Time series**:\n  * [#1593](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1593) More detailed LibMR error messages\n\n**Bug Fixes**\n* **Search and query**:\n  * [#4524](https://github.com/RediSearch/RediSearch/pull/4524) `FT.CURSOR READ` in a numeric query causing a crash (MOD-6597)\n  * [#4543](https://github.com/RediSearch/RediSearch/pull/4543) `FT.SEARCH` accessing an inexistent memory address causes a crash if using deprecated `FT.ADD` command (MOD-6599)\n  * [#4535](https://github.com/RediSearch/RediSearch/pull/4535) `FT.PROFILE` with incorrect arguments could cause a crash on cluster setup (MOD-6791)\n  * [#4540](https://github.com/RediSearch/RediSearch/pull/4540) Unfree memory from an existing RDB while re-indexing loading a new RDB could cause a crash (MOD-6831, 6810)\n  * [#4485](https://github.com/RediSearch/RediSearch/pull/4485) Some parameter settings using just prefixes instead of full values were working (MOD-6709)\n  * [#4557](https://github.com/RediSearch/RediSearch/pull/4557) Additional \"`}`\" on wildcards replies for `FT.EXPLAIN` (MOD-6768)\n\n* **JSON**:\n  * [#1192](https://github.com/RedisJSON/RedisJSON/pull/1192) Crashes with numeric values greater than i64::MAX (MOD-6501, MOD-4551, MOD-4856, MOD-5714)\n\n* **Time series**:\n  * [LibMR#51](https://github.com/RedisGears/LibMR/pull/51) Crash on SSL initialization failure (MOD-5647)\n\n**Redis version**\n* [Redis 6.2.14](https://github.com/redis/redis/releases/tag/6.2.14)\n\n**Module versions**\t\n* __[RediSearch 2.6.18](https://github.com/RediSearch/RediSearch/releases/tag/v2.6.18)__\n* __[RedisJSON 2.4.9](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.4.9)__\n* __[RedisGraph 2.10.12](https://github.com/RedisGraph/RedisGraph/releases/tag/v2.10.15)__\n* __[RedisTimeSeries 1.8.13](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.8.13)__\n* __[RedisBloom 2.4.9](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.4.9)__\n\n**Recommended Client Libraries**\n* Java\n  * [Jedis 5.1.2 or later](https://github.com/redis/jedis/releases/tag/v5.1.2)\n  * [redis-om-spring 0.8.9 or later](https://github.com/redis/redis-om-spring/releases/tag/v0.8.9)\n* Python\n  * [redis-py 5.0.3 or later](https://github.com/redis/redis-py/releases/tag/v5.0.3)\n  * [redis-om-python 0.2.2 or later](https://github.com/redis/redis-om-python/releases/tag/v0.2.2)\n* NodeJS\n  * [node-redis 4.6.13 or later](https://github.com/redis/node-redis/releases/tag/redis%404.6.13)\n  * [redis-om-node 0.2.0 or later](https://github.com/redis/redis-om-node/releases/tag/v0.2.0)\n* .NET\n  * [redis-om-dotnet 0.6.1 or later](https://github.com/redis/redis-om-dotnet/releases/tag/v0.6.1)\n  * [NRedisStack 0.12.0 or later](https://github.com/redis/NRedisStack/releases/tag/v0.12.0)\n* Go\n  * [go-redis 9.5.1 or later](https://github.com/redis/go-redis/releases/tag/v9.5.1)\n  * [rueidis 1.0.33 or later](https://github.com/redis/rueidis/releases/tag/v1.0.33)\n\nCompatible with [RedisInsight](https://redis.io/download). The docker image redis/redis-stack for this version is bundled with [RedisInsight 2.46](https://github.com/RedisInsight/RedisInsight/releases/tag/2.46.0).\n\nNote: version numbers follow the pattern:\n\n`x.y.z-b`\n* `x.y` Redis major version\n* `z` increases with even numbers as a module x.y version increases.\n* `b` denotes a patch to Redis or a module (any `z` of Redis or modules). `b` will consist of a `v` + numeric value."},{"id":"downloads-1","title":"Downloads","role":"content","text":"* macOS: [x86_64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v14.catalina.x86_64.zip), [arm64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v14.monterey.arm64.zip)\n* AppImage: [x86_64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v14-x86_64.AppImage)\n* Ubuntu: [Bionic x86_64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v14.bionic.x86_64.tar.gz), [Bionic arm64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v14.bionic.arm64.tar.gz), [Focal x86_64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v14.focal.x86_64.tar.gz), [Focal arm64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v14.focal.arm64.tar.gz), [Snap x86_64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v14.x86_64.snap), [Snap arm64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v14.arm64.snap), [Jammy x86_64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v14.jammy.x86_64.tar.gz), [Jammy arm64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v14.jammy.arm64.tar.gz)\n* Debian: [Bullseye x86_64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v14.bullseye.x86_64.tar.gz)\n* RHEL 7/CentOS Linux 7: [x86_64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v14.rhel7.x86_64.tar.gz)\n* RHEL 8/CentOS Linux 8: [x86_64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v14.rhel8.x86_64.tar.gz)\n* Redis Stack on [Dockerhub](https://hub.docker.com/u/redis): [x86_64 and arm64](https://hub.docker.com/r/redis/redis-stack)\n* Redis Stack server on [Dockerhub](https://hub.docker.com/u/redis): [x86_64 and arm64](https://hub.docker.com/r/redis/redis-stack-server)"},{"id":"redis-stack-server-626-v13-march-2024","title":"Redis Stack Server 6.2.6-v13 (March 2024)","role":"content","text":"This is a maintenance release for Redis Stack Server 6.2.6\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent."},{"id":"headlines-4","title":"Headlines:","role":"content","text":"This version contains the latest **search and query** capability and **probabilistic** data structures with several bug fixes. It also contains the latest version of **RedisInsight**."},{"id":"details-3","title":"Details:","role":"content","text":"**Bug Fixes**\n* **Search and query**:\n  * [#4477](https://github.com/RediSearch/RediSearch/pull/4477) Split `INFIX` and `SUFFIX` report on `FT.EXPLAIN` and `FT.EXPLAINCLI` (MOD-6186)\n  * [#4468](https://github.com/RediSearch/RediSearch/pull/4468) Memory leak upon suffix query for a `TAG` indexed with `WITHSUFFIXTRIE` (MOD-6644)\n  * [#4407](https://github.com/RediSearch/RediSearch/pull/4407) Clustered `FT.SEARCH` hangs forever without replying when an invalid topology is found (MOD-6557)\n  * [#4359](https://github.com/RediSearch/RediSearch/pull/4359) Searching for a synonym will iterate in the same group multiple times, causing a performance hit (MOD-6490)\n  * [#4310](https://github.com/RediSearch/RediSearch/pull/4310) Memory tracking on cluster setups causing high memory usage and potentially Out-of-Memory (MOD-6123, MOD-5639)\n\n* **Probabilistic data structures**:\n  * [#753](https://github.com/RedisBloom/RedisBloom/issues/753) Potential crash on `CMS.MERGE` when using invalid arguments\n\n**Redis version**\n* [Redis 6.2.14](https://github.com/redis/redis/releases/tag/6.2.14)\n\n**Module versions**\t\n* __[RediSearch 2.6.16](https://github.com/RediSearch/RediSearch/releases/tag/v2.6.16)__\n* __[RedisJSON 2.4.8](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.4.8)__\n* __[RedisGraph 2.10.12](https://github.com/RedisGraph/RedisGraph/releases/tag/v2.10.15)__\n* __[RedisTimeSeries 1.8.12](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.8.12)__\n* __[RedisBloom 2.4.9](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.4.9)__\n\n**Recommended Client Libraries**\n* Java\n  * [Jedis 5.1.1 or greater](https://github.com/redis/jedis/releases/tag/v5.1.1)\n  * [redis-om-spring 0.8.8 or greater](https://github.com/redis/redis-om-spring/releases/tag/v0.8.8)\n* Python\n  * [redis-py 5.0.2 or greater](https://github.com/redis/redis-py/releases/tag/v5.0.2)\n  * [redis-om-python 0.2.1 or greater](https://github.com/redis/redis-om-python/releases/tag/v0.2.1)\n* NodeJS\n  * [node-redis 4.6.13 or greater](https://github.com/redis/node-redis/releases/tag/redis%404.6.13)\n  * [redis-om-node 0.2.0 or greater](https://github.com/redis/redis-om-node/releases/tag/v0.2.0)\n* .NET\n  * [redis-om-dotnet 0.6.1 or greater](https://github.com/redis/redis-om-dotnet/releases/tag/v0.6.1)\n  * [NRedisStack 0.11.0 or greater](https://github.com/redis/NRedisStack/releases/tag/v0.11.0)\n* Go\n  * [go-redis 9.5.1 or greater](https://github.com/redis/go-redis/releases/tag/v9.5.1)\n  * [rueidis 1.0.31 or greater](https://github.com/redis/rueidis/releases/tag/v1.0.31)\n\nCompatible with [RedisInsight](https://redis.io/download). The docker image redis/redis-stack for this version is bundled with [RedisInsight 2.44](https://github.com/RedisInsight/RedisInsight/releases/tag/2.44.0).\n\nNote: version numbers follow the pattern:\n\n`x.y.z-b`\n* `x.y` Redis major version\n* `z` increases with even numbers as a module x.y version increases.\n* `b` denotes a patch to Redis or a module (any `z` of Redis or modules). `b` will consist of a `v` + numeric value."},{"id":"downloads-2","title":"Downloads","role":"content","text":"* macOS: [x86_64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v13.catalina.x86_64.zip), [arm64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v13.monterey.arm64.zip)\n* AppImage: [x86_64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v13-x86_64.AppImage)\n* Ubuntu: [Bionic x86_64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v13.bionic.x86_64.tar.gz), [Bionic arm64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v13.bionic.arm64.tar.gz), [Focal x86_64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v13.focal.x86_64.tar.gz), [Focal arm64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v13.focal.arm64.tar.gz), [Snap x86_64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v13.x86_64.snap), [Snap arm64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v13.arm64.snap), [Jammy x86_64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v13.jammy.x86_64.tar.gz), [Jammy arm64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v13.jammy.arm64.tar.gz)\n* Debian: [Bullseye x86_64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v13.bullseye.x86_64.tar.gz)\n* RHEL 7/CentOS Linux 7: [x86_64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v13.rhel7.x86_64.tar.gz)\n* RHEL 8/CentOS Linux 8: [x86_64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v13.rhel8.x86_64.tar.gz)\n* Redis Stack on [Dockerhub](https://hub.docker.com/u/redis): [x86_64 and arm64](https://hub.docker.com/r/redis/redis-stack)\n* Redis Stack server on [Dockerhub](https://hub.docker.com/u/redis): [x86_64 and arm64](https://hub.docker.com/r/redis/redis-stack-server)"},{"id":"redis-stack-server-626-v12-january-2024","title":"Redis Stack Server 6.2.6-v12 (January 2024)","role":"content","text":"This is a maintenance release for Redis Stack Server 6.2.6\n\nUpdate urgency: `HIGH`: There is a critical bug fixed in the probabilistic data structures that may affect a subset of users. Upgrade!"},{"id":"headlines-5","title":"Headlines:","role":"content","text":"This maintenance release contains the new version of **probabilistic** data structures with a critical bug fix, the new version of  **JSON** data structure with added support for CBL-Mariner 2, and a fix to add keyspace notifications for `JSON.TOGGLE`. It also contains the latest version of **RedisInsight**."},{"id":"details-4","title":"Details:","role":"content","text":"**Improvements**\n* **JSON**:\n  * [#1149](https://github.com/RedisJSON/RedisJSON/pull/1149) Added support for CBL-Mariner 2\n\n**Bug Fixes**\n* **JSON**:\n  * [#1025](https://github.com/RedisJSON/RedisJSON/pull/1025) `JSON.TOGGLE` - missing keyspace notification\n\n* **Probabilistic data structures**:\n  * [#727](https://github.com/RedisBloom/RedisBloom/pull/727) Potential crash on `CF.LOADCHUNK` (MOD-6344) - Additional fixes\n\n**Redis version**\n* [Redis 6.2.14](https://github.com/redis/redis/releases/tag/6.2.14)\n\n**Module versions**\t\n* __[RediSearch 2.6.15](https://github.com/RediSearch/RediSearch/releases/tag/v2.6.15)__\n* __[RedisJSON 2.4.8](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.4.8)__\n* __[RedisGraph 2.10.12](https://github.com/RedisGraph/RedisGraph/releases/tag/v2.10.12)__\n* __[RedisTimeSeries 1.8.12](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.8.12)__\n* __[RedisBloom 2.4.8](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.4.8)__\n\n**Recommended Client Libraries**\n* Java\n  * [Jedis 5.1.0 or greater](https://github.com/redis/jedis/releases/tag/v5.1.0)\n  * [redis-om-spring 0.8.8 or greater](https://github.com/redis/redis-om-spring/releases/tag/v0.8.8)\n* Python\n  * [redis-py 5.0.1 or greater](https://github.com/redis/redis-py/releases/tag/v5.0.1)\n  * [redis-om-python 0.2.1 or greater](https://github.com/redis/redis-om-python/releases/tag/v0.2.1)\n* NodeJS\n  * [node-redis 4.6.12 or greater](https://github.com/redis/node-redis/releases/tag/redis%404.6.12)\n  * [redis-om-node 0.2.0 or greater](https://github.com/redis/redis-om-node/releases/tag/v0.2.0)\n* .NET\n  * [redis-om-dotnet 0.6.1 or greater](https://github.com/redis/redis-om-dotnet/releases/tag/v0.6.1)\n  * [NRedisStack 0.11.0 or greater](https://github.com/redis/NRedisStack/releases/tag/v0.11.0)\n* Go\n  * [go-redis 9.4.0 or greater](https://github.com/redis/go-redis/releases/tag/v9.4.0)\n  * [rueidis 1.0.27 or greater](https://github.com/redis/rueidis/releases/tag/v1.0.27)\n\nCompatible with [RedisInsight](https://redis.io/download). The docker image redis/redis-stack for this version is bundled with [RedisInsight 2.40](https://github.com/RedisInsight/RedisInsight/releases/tag/2.40.0).\n\nNote: version numbers follow the pattern:\n\n`x.y.z-b`\n* `x.y` Redis major version\n* `z` increases with even numbers as a module x.y version increases.\n* `b` denotes a patch to Redis or a module (any `z` of Redis or modules). `b` will consist of a `v` + numeric value."},{"id":"redis-stack-server-626-v11-january-2024","title":"Redis Stack Server 6.2.6-v11 (January 2024)","role":"content","text":"This is a maintenance release for Redis Stack Server 6.2.6\n\nUpdate urgency: `SECURITY`:  there are security fixes in the release."},{"id":"headlines-6","title":"Headlines:","role":"content","text":"This version contains the latest **time series data** structure with a security fix to not expose internal commands, a fix for potential crashes when using an invalid argument value, and support for CBL-Mariner 2. The new Redis Stack version introduces security fixes for **probabilistic data structures** to avoid potential crashes. It also includes the latest **search and query** capability with several bug fixes and improvements. This version contains the latest version of **RedisInsight**."},{"id":"details-5","title":"Details:","role":"content","text":"**Security and privacy**\n* **Time series**:\n  * [#1506](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1506)​​ Don’t expose internal commands (MOD-5643)\n\n* **Probabilistic data structures**:\n  * [#721](https://github.com/RedisBloom/RedisBloom/issues/721) Potential crash on `CF.RESERVE` (MOD-6343)\n  * [#722](https://github.com/RedisBloom/RedisBloom/issues/722) Potential crash on `CF.LOADCHUNK` (MOD-6344)\n\n **Improvements**\n* **Search and query**:\n  * [#4176](https://github.com/RediSearch/RediSearch/pull/4176) Initialization of the maximum numeric value range leading to a better balance of the index leaf splitting (MOD-6232)\n  * [#4123](https://github.com/RediSearch/RediSearch/pull/4123) Possibly problematic index name alias check-in command multiplexing (MOD-5945)\n  * [#4195](https://github.com/RediSearch/RediSearch/pull/4195) Query optimisation when predicate contains multiple `INTERSECTION` (AND) of `UNION` (OR) (MOD-5910)\n\n* **Time series**:\n  * [#1516](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1516) Added support for CBL-Mariner 2\n\n**Bug Fixes**\n* **Search and query**:\n  * [#4244](https://github.com/RediSearch/RediSearch/pull/4244), [#4255](https://github.com/RediSearch/RediSearch/pull/4255) Profiling `FT.AGGREGATE` using the `WITHCURSOR` flag cause a crash due to timeout (MOD-5512)\n  * [#4238](https://github.com/RediSearch/RediSearch/pull/4238) Memory excessively growing on databases caused by unbalanced nodes on inverted index trie (MOD-5880, MOD-5952, MOD-6003)\n  * [#3995](https://github.com/RediSearch/RediSearch/pull/3995) `FT.CURSOR READ` with geo queries causing a crash when data is updated between the cursor reads (MOD-5646)\n  * [#4155](https://github.com/RediSearch/RediSearch/pull/4155) `FT.SEARCH` not responding when using TLS encryption on Amazon Linux 2 (MOD-6012)\n\n* **Time series**:\n  * [#1494](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/1494) Potential crash when using an invalid argument value\n\n**Redis version**\n* [Redis 6.2.14](https://github.com/redis/redis/releases/tag/6.2.14)\n\n**Module versions**\t\n* __[RediSearch 2.6.15](https://github.com/RediSearch/RediSearch/releases/tag/v2.6.15)__\n* __[RedisJSON 2.4.7](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.4.7)__\n* __[RedisGraph 2.10.12](https://github.com/RedisGraph/RedisGraph/releases/tag/v2.10.12)__\n* __[RedisTimeSeries 1.8.12](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.8.12)__\n* __[RedisBloom 2.4.7](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.4.7)__\n\n**Recommended Client Libraries**\n* Java\n  * [Jedis 5.1.0 or greater](https://github.com/redis/jedis/releases/tag/v5.1.0)\n  * [redis-om-spring 0.8.7 or greater](https://github.com/redis/redis-om-spring/releases/tag/v0.8.7)\n* Python\n  * [redis-py 5.0.1 or greater](https://github.com/redis/redis-py/releases/tag/v5.0.1)\n  * [redis-om-python 0.2.1 or greater](https://github.com/redis/redis-om-python/releases/tag/v0.2.1)\n* NodeJS\n  * [node-redis 4.6.12 or greater](https://github.com/redis/node-redis/releases/tag/redis%404.6.12)\n  * [redis-om-node 0.2.0 or greater](https://github.com/redis/redis-om-node/releases/tag/v0.2.0)\n* .NET\n  * [redis-om-dotnet 0.6.1 or greater](https://github.com/redis/redis-om-dotnet/releases/tag/v0.6.1)\n  * [NRedisStack 0.11.0 or greater](https://github.com/redis/NRedisStack/releases/tag/v0.11.0)\n* Go\n  * [go-redis 9.4.0 or greater](https://github.com/redis/go-redis/releases/tag/v9.4.0)\n  * [rueidis 1.0.26 or greater](https://github.com/redis/rueidis/releases/tag/v1.0.26)\n\nCompatible with [RedisInsight](https://redis.io/download). The docker image redis/redis-stack for this version is bundled with [RedisInsight 2.40](https://github.com/RedisInsight/RedisInsight/releases/tag/2.40.0).\n\nNote: version numbers follow the pattern:\n\n`x.y.z-b`\n* `x.y` Redis major version\n* `z` increases with even numbers as a module x.y version increases.\n* `b` denotes a patch to Redis or a module (any `z` of Redis or modules). `b` will consist of a `v` + numeric value."},{"id":"redis-stack-server-626-v10-november-2023","title":"Redis Stack Server 6.2.6-v10 (November 2023)","role":"content","text":"This is a maintenance release for Redis Stack Server 6.2.6\n\nUpdate urgency: `SECURITY`:  there are security fixes in the release."},{"id":"headlines-7","title":"Headlines:","role":"content","text":"This version contains a security fix for the Redis server to avoid bypassing desired Unix socket permissions on startup. It also includes the latest Search and Query capability with a fix to limit the maximum phonetic length and several bug fixes. This version contains the latest version of RedisInsight."},{"id":"details-6","title":"Details:","role":"content","text":"**Security and privacy:**\n* **Redis**:\n  * (CVE-2023-45145) The wrong order of `listen(2)` and `chmod(2)` calls creates a race condition that can be used by another process to bypass desired Unix socket permissions on startup.\n\n* **Search and Query**:\n  * [#3844](https://github.com/RediSearch/RediSearch/pull/3844) Limits maximum phonetic length avoiding to be exploited (MOD 5767)\n\n**Bug Fixes**\n* **Search and Query**:\n  * [#3771](https://github.com/RediSearch/RediSearch/pull/3771) Broken lower and upper `APPLY` functions in `FT.AGGREGATE` on `DIALECT 3` (MOD-5041)\n  * [#3910](https://github.com/RediSearch/RediSearch/pull/3910) Heavy document updates causing memory growth once memory blocks weren't properly released (MOD-5181)\n  * [#3853](https://github.com/RediSearch/RediSearch/pull/3853) Queries with `WITHCURSOR` making memory growth since `CURSOR` wasn't invalidated in the shards (MOD-5580)\n  * [#3752](https://github.com/RediSearch/RediSearch/pull/3752) Setting low `MAXIDLE` parameter value in `FT.AGGREGATE` causes a crash (MOD-5608)\n  * [#3823](https://github.com/RediSearch/RediSearch/pull/3823) `APPLY` or `FILTER` expression causing a leak (MOD-5751)\n  * [#3837](https://github.com/RediSearch/RediSearch/pull/3837) Connection using TLS fails on Redis (MOD-5768)\n  * [#3856](https://github.com/RediSearch/RediSearch/pull/3856) Adding new nodes to OSS cluster causing a crash (MOD-5778)\n  * [#3854](https://github.com/RediSearch/RediSearch/pull/3854) Vector range query could cause Out-of-Memory due a memory corruption (MOD-5791)\n  * [#3892](https://github.com/RediSearch/RediSearch/pull/3892) After cleaning the index the GC could cause corruption on unique values (MOD-5815)\n\n\n\n**Redis version**\n* [Redis 6.2.14](https://github.com/redis/redis/releases/tag/6.2.14)\n\n**Module versions**\t\n* __[RediSearch 2.6.14](https://github.com/RediSearch/RediSearch/releases/tag/v2.6.14)__\n* __[RedisJSON 2.4.7](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.4.7)__\n* __[RedisGraph 2.10.12](https://github.com/RedisGraph/RedisGraph/releases/tag/v2.10.12)__\n* __[RedisTimeSeries 1.8.11](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.8.11)__\n* __[RedisBloom 2.4.5](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.4.5)__\n\n**Recommended Client Libraries**\n* Java\n  * [Jedis 5.0.2 or greater](https://github.com/redis/jedis/releases/tag/v5.0.2)\n  * [redis-om-spring 0.8.7 or greater](https://github.com/redis/redis-om-spring/releases/tag/v0.8.7)\n* Python\n  * [redis-py 5.0.1 or greater](https://github.com/redis/redis-py/releases/tag/v5.0.1)\n  * [redis-om-python 0.2.1 or greater](https://github.com/redis/redis-om-python/releases/tag/v0.2.1)\n* NodeJS\n  * [node-redis 4.6.10 or greater](https://github.com/redis/node-redis/releases/tag/redis%404.6.10)\n  * [redis-om-node 0.2.0 or greater](https://github.com/redis/redis-om-node/releases/tag/v0.2.0)\n* .NET\n  * [redis-om-dotnet 0.5.4 or greater](https://github.com/redis/redis-om-dotnet/releases/tag/v0.5.4)\n  * [NRedisStack 0.10.1 or greater](https://github.com/redis/NRedisStack/releases/tag/v0.10.1)\n* Go\n  * [go-redis 9.3.0 or greater](https://github.com/redis/go-redis/releases/tag/v9.3.0)\n  * [rueidis 1.0.22 or greater](https://github.com/redis/rueidis/releases/tag/v1.0.22)\n\nCompatible with [RedisInsight](https://redis.io/download). The docker image redis/redis-stack for this version is bundled with [RedisInsight 2.36](https://github.com/RedisInsight/RedisInsight/releases/tag/2.36.0).\n\nNote: version numbers follow the pattern:\n\n`x.y.z-b`\n* `x.y` Redis major version\n* `z` increases with even numbers as a module x.y version increases.\n* `b` denotes a patch to Redis or a module (any `z` of Redis or modules). `b` will consist of a `v` + numeric value."},{"id":"downloads-3","title":"Downloads","role":"content","text":"* macOS: [x86_64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v10.catalina.x86_64.zip), [arm64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v10.monterey.arm64.zip)\n* AppImage: [x86_64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v10-x86_64.AppImage)\n* Ubuntu: [Bionic x86_64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v10.bionic.x86_64.tar.gz), [Bionic arm64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v10.bionic.arm64.tar.gz), [Focal x86_64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v10.focal.x86_64.tar.gz), [Focal arm64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v10.focal.arm64.tar.gz), [Snap x86_64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v10.x86_64.snap), [Snap arm64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v10.arm64.snap), [Jammy x86_64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v10.jammy.x86_64.tar.gz), [Jammy arm64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v10.jammy.arm64.tar.gz)\n* Debian: [Bullseye x86_64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v10.bullseye.x86_64.tar.gz)\n* RHEL 7/CentOS Linux 7: [x86_64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v10.rhel7.x86_64.tar.gz)\n* RHEL 8/CentOS Linux 8: [x86_64](https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v10.rhel8.x86_64.tar.gz)\n* Redis Stack on [Dockerhub](https://hub.docker.com/u/redis): [x86_64 and arm64](https://hub.docker.com/r/redis/redis-stack)\n* Redis Stack server on [Dockerhub](https://hub.docker.com/u/redis): [x86_64 and arm64](https://hub.docker.com/r/redis/redis-stack-server)"},{"id":"redis-stack-server-626-v9-july-2023","title":"Redis Stack Server 6.2.6-v9 (July 2023)","role":"content","text":"This is a maintenance release for Redis Stack Server 6.2.6\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!"},{"id":"headlines-8","title":"Headlines:","role":"content","text":"This version contains the latest Search and Query capability v. 2.6.12, time series v. 1.8.11, and graph v. 2.10.12 with fixes and improvements. It also includes the latest version of RedisInsight."},{"id":"details-7","title":"Details:","role":"content","text":"**Improvements**\n* **Search and Query**:\n  * [#3628](https://github.com/RediSearch/RediSearch/pull/3628) Background indexing scanning performance\n  * [#3259](https://github.com/RediSearch/RediSearch/pull/3259) Allow alias name beginning with `as`\n  * [#3641](https://github.com/RediSearch/RediSearch/pull/3641) Indexing sanitizing trigger in heavily data updates scenario\n\n* **Time series**:\n  * [#1476](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/1476) Significant performance improvement when using multiple label filters (`TS.MGET`, `TS.MRANGE`, `TS.MREVRANGE`, and `TS.QUERYINDEX`)\n\n**Bug Fixes**\n* **Search and Query**:\n  * [#3557](https://github.com/RediSearch/RediSearch/pull/3557) `TIMEOUT` configuration on `FT.AGGREGATE` query being ignored\n  * [#3552](https://github.com/RediSearch/RediSearch/pull/3552) `FT.CURSOR READ` on `JSON` numeric queries not returning results\n  * [#3606](https://github.com/RediSearch/RediSearch/pull/3606) Update numeric inverted index `numEntries` avoiding excessive memory consumption\n  * [#3597](https://github.com/RediSearch/RediSearch/pull/3597) Duplicating alias as output name on `FT.AGGREGATE` reducer (`REDUCE` argument) isn't return results\n  * [#3654](https://github.com/RediSearch/RediSearch/pull/3654) Added check for @ prefix on `GROUPBY` fields returnig an error instead of wrong results\n\n* **Time series**:\n  * [#1486](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/1486) When using `LATEST`, results may contain samples earlier than `fromTimestamp` (`TS.RANGE`, `TS.REVRANGE`, `TS.MRANGE`, and `TS.MREVRANGE`)\n  * [#1471](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1471) Potential crash on `TS.MRANGE` when aggregating millions of time series\n  * [#1469](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1469) Potential memory leak in `TS.MRANGE` after eviction\n\n* **Graph**:\n  * [#3129](https://github.com/RedisGraph/RedisGraph/pull/3129) Crash on certain queries (`INDEX SCAN` followed by `DEL` followed by `SET`)\n\n**Redis version**\n* [Redis 6.2.13](https://github.com/redis/redis/blob/6.2.13/00-RELEASENOTES)\n\n**Module versions**\t\n* __[RediSearch 2.6.12](https://github.com/RediSearch/RediSearch/releases/tag/v2.6.12)__\n* __[RedisJSON 2.4.7](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.4.7)__\n* __[RedisGraph 2.10.12](https://github.com/RedisGraph/RedisGraph/releases/tag/v2.10.12)__\n* __[RedisTimeSeries 1.8.11](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.8.11)__\n* __[RedisBloom 2.4.5](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.4.5)__\n\n**Recommended Client Libraries**\n* Java\n  * [Jedis 4.4.3 or greater ](https://github.com/redis/jedis/releases/tag/v4.4.3)\n  * [redis-om-spring 0.8.4 or greater](https://github.com/redis/redis-om-spring/releases/tag/v0.8.4)\n* Python\n  * [redis-py 4.6.0 or greater ](https://github.com/redis/redis-py/releases/tag/v4.6.0)\n  * [redis-om-python 0.1.2 or greater](https://github.com/redis/redis-om-python/releases/tag/v0.1.2)\n* NodeJS\n  * [node-redis 4.6.7  or greater](https://github.com/redis/node-redis/releases/tag/redis%404.6.7)\n  * [redis-om-node 0.2.0 or greater](https://github.com/redis/redis-om-node/releases/tag/v0.2.0)\n* .NET\n  * [redis-om-dotnet 0.5.2 or greater](https://github.com/redis/redis-om-dotnet/releases/tag/v0.5.2)\n  * [NRedisStack 0.8.0 or greater](https://github.com/redis/NRedisStack/releases/tag/v0.8.0)\n\nCompatible with [RedisInsight](https://redis.io/download). The docker image redis/redis-stack for this version is bundled with [RedisInsight 2.28](https://github.com/RedisInsight/RedisInsight/releases/tag/2.28.0).\n\nNote: version numbers follow the pattern:\n\n`x.y.z-b`\n* `x.y` Redis major version\n* `z` increases with even numbers as a module x.y version increases.\n* `b` denotes a patch to Redis or a module (any `z` of Redis or modules). `b` will consist of a `v` + numeric value."},{"id":"redis-stack-server-626-v8-july-2023","title":"Redis Stack Server 6.2.6-v8 (July 2023)","role":"content","text":"This is a maintenance release for Redis Stack Server 6.2.6\n\nUpdate urgency: `SECURITY`:  there are security fixes in the release."},{"id":"headlines-9","title":"Headlines:","role":"content","text":"This version contains security improvements for the Redis server."},{"id":"details-8","title":"Details:","role":"content","text":"**Improvements**\n* **Redis**:\n  * ([CVE-2022-24834](https://github.com/redis/redis/security/advisories/GHSA-p8x2-9v9q-c838)) A specially crafted Lua script executing in Redis can trigger\na heap overflow in the cjson and cmsgpack libraries, and result in heap\ncorruption and potentially remote code execution. The problem exists in all\nversions of Redis with Lua scripting support, starting from 2.6, and affects\nonly authenticated and authorized users.\n  * ([CVE-2023-36824](https://github.com/redis/redis/security/advisories/GHSA-4cfx-h9gq-xpx3)) Extracting key names from a command and a list of arguments\nmay, in some cases, trigger a heap overflow and result in reading random heap\nmemory, heap corruption and potentially remote code execution. Specifically:\nusing COMMAND GETKEYS* and validation of key names in ACL rules.\n\n**Bug Fixes**\n* **Redis**:\n  * [#12276](https://github.com/redis/redis/pull/12276) Re-enable downscale rehashing while there is a fork child\n\n**Redis version**\n* [Redis 6.2.13](https://github.com/redis/redis/blob/6.2.13/00-RELEASENOTES)\n\n**Module versions**\n* __[RediSearch 2.6.9](https://github.com/RediSearch/RediSearch/releases/tag/v2.6.9)__\n* __[RedisJSON 2.4.7](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.4.7)__\n* __[RedisGraph 2.10.10](https://github.com/RedisGraph/RedisGraph/releases/tag/v2.10.10)__\n* __[RedisTimeSeries 1.8.10](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.8.10)__\n* __[RedisBloom 2.4.5](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.4.5)__\n\n**Recommended Client Libraries**\n* Java\n  * [Jedis 4.3.1 or greater ](https://github.com/redis/jedis/releases/tag/v4.3.1)\n  * [redis-om-spring](https://github.com/redis/redis-om-spring)\n* Python\n  * [redis-py 4.5.4 or greater ](https://github.com/redis/redis-py/releases/tag/v4.5.4)\n  * [redis-om-python](https://github.com/redis/redis-om-python)\n* NodeJS\n  * [node-redis 4.6.5  or greater](https://www.npmjs.com/package/redis)\n  * [redis-om-node](https://github.com/redis/redis-om-node)\n* .NET\n  * [redis-om-dotnet](https://github.com/redis/redis-om-dotnet)\n  * [NRedisStack](https://github.com/redis/NRedisStack)\n\nCompatible with [RedisInsight](https://redis.io/download). The docker image redis/redis-stack for this version is bundled with [RedisInsight](https://github.com/RedisInsight/RedisInsight/releases/).\n\nNote: version numbers follow the pattern:\n\n`x.y.z-b`\n* `x.y` Redis major version\n* `z` increases with even numbers as a module x.y version increases.\n* `b` denotes a patch to Redis or a module (any `z` of Redis or modules). `b` will consist of a `v` + numeric value."},{"id":"redis-stack-server-626-v7-april-2023","title":"Redis Stack Server 6.2.6-v7 (April 2023)","role":"content","text":"This is a maintenance release for Redis Stack Server 6.2.6\n\nUpdate urgency: `SECURITY`:  there are security fixes in the release."},{"id":"headlines-10","title":"Headlines:","role":"content","text":"This version contains the latest RediSearch 2.6.9, RedisJSON 2.4.7, RedisGraph 2.10.10, RedisTimeSeries 1.8.10, and RedisBloom 2.4.5, RedisInsight 2.22 and new Redis server 6.2.12 with fixes to security issues."},{"id":"details-9","title":"Details:","role":"content","text":"**Improvements**:\n* Redis [#12057](https://github.com/redis/redis/pull/12057) Authenticated users can use the [HINCRBYFLOAT](https://redis.io/commands/hincrbyfloat/) command to create an invalid hash field that will crash Redis on access\n* RediSearch [#3430](https://github.com/RediSearch/RediSearch/pull/3430) Improve `min-max heap` structure for better readability and performance\n* RediSearch [#3450](https://github.com/RediSearch/RediSearch/pull/3450) Display `NOHL` option in `FT.INFO` command\n* RediSearch [#3534](https://github.com/RediSearch/RediSearch/pull/3534) Vector Similarity 0.6.1: Improve multi-value index deletion logic [(#346)](https://github.com/RedisAI/VectorSimilarity/pull/346)\n\n**Bug Fixes**\n* RediSearch [#3468](https://github.com/RediSearch/RediSearch/pull/3468) KNN searching for 0 vectors with a filter resulted in crash\n* RediSearch [#3499](https://github.com/RediSearch/RediSearch/pull/3499) `MAXSEARCHRESULTS` set to `0` causing `FT.SEARCH` crash\n* RediSearch [#3494](https://github.com/RediSearch/RediSearch/pull/3494) Removing `MAXSEARCHRESULTS`limit causes crash on `FT.AGGREGATE`\n* RediSearch [#3504](https://github.com/RediSearch/RediSearch/pull/3504) Uninitialised vector similarity query parameter bug\n* RedisJSON [#947](https://github.com/RedisJSON/RedisJSON/issues/947) Using array slice operator (`[start:end:step]`) with step `0` causes crash\n* RedisGraph [#3038](https://github.com/RedisGraph/RedisGraph/issues/3038) Potential crash when a query with a `UNION` clause sets or modifies an indexed property\n* RedisGraph [#2631](https://github.com/RedisGraph/RedisGraph/issues/2631), [#2968](https://github.com/RedisGraph/RedisGraph/issues/2968) Potential crash on certain `MATCH` clauses where label filters are used\n* RedisGraph [#2957](https://github.com/RedisGraph/RedisGraph/issues/2957) Label filters in expressions such as `WITH n MATCH (n:X)` are ignored\n* RedisGraph [#2931](https://github.com/RedisGraph/RedisGraph/issues/2931), [#3027](https://github.com/RedisGraph/RedisGraph/issues/3027) Wrong overflow error message\n* RedisTimeSeries [#1455](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/1455) [TS.ADD](https://redis.io/commands/ts.add/) - optional arguments are not replicated\n\n**Redis version**\n* [Redis 6.2.12](https://github.com/redis/redis/blob/6.2.12/00-RELEASENOTES)\n\n**Module versions**\n* __[RediSearch 2.6.9](https://github.com/RediSearch/RediSearch/releases/tag/v2.6.9)__\n* __[RedisJSON 2.4.7](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.4.7)__\n* __[RedisGraph 2.10.10](https://github.com/RedisGraph/RedisGraph/releases/tag/v2.10.10)__\n* __[RedisTimeSeries 1.8.10](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.8.10)__\n* __[RedisBloom 2.4.5](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.4.5)__\n\n**Recommended Client Libraries**\n* Java\n  * [Jedis 4.3.1 or greater ](https://github.com/redis/jedis/releases/tag/v4.3.1)\n  * [redis-om-spring](https://github.com/redis/redis-om-spring)\n* Python\n  * [redis-py 4.5.4 or greater ](https://github.com/redis/redis-py/releases/tag/v4.5.4)\n  * [redis-om-python](https://github.com/redis/redis-om-python)\n* NodeJS\n  * [node-redis 4.6.5  or greater](https://www.npmjs.com/package/redis)\n  * [redis-om-node](https://github.com/redis/redis-om-node)\n* .NET\n  * [redis-om-dotnet](https://github.com/redis/redis-om-dotnet)\n  * [NRedisStack](https://github.com/redis/NRedisStack)\n\nCompatible with the latest [RedisInsight](https://redis.io/download). The docker image redis/redis-stack for this version is bundled with [RedisInsight 2.22](https://github.com/RedisInsight/RedisInsight/releases/tag/2.22.1).\n\nNote: version numbers follow the pattern:\n\n`x.y.z-b`\n* `x.y` Redis major version\n* `z` increases with even numbers as a module x.y version increases.\n* `b` denotes a patch to Redis or a module (any `z` of Redis or modules). `b` will consist of a `v` + numeric value."},{"id":"redis-stack-server-626-v6-march-2023","title":"Redis Stack Server 6.2.6-v6 (March 2023)","role":"content","text":"This is a maintenance release for Redis Stack Server 6.2.6\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent."},{"id":"headlines-11","title":"Headlines:","role":"content","text":"This version contains the latest RedisTimeSeries 1.8.9 with bugs fixed."},{"id":"details-10","title":"Details:","role":"content","text":"**Bug Fixes**\n* RedisTimeSeries [#1421](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1421) Potential crash after deleting from a time series with an `AVG` compaction\n* RedisTimeSeries [#1422](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1422) Incorrectly return an error when deleting from a time series with a compaction and with no expiry\n\n\n**Redis version**\n* [Redis 6.2.11](https://github.com/redis/redis/blob/6.2.11/00-RELEASENOTES)\n\n\n**Module versions**\n* __[RediSearch 2.6.6](https://github.com/RediSearch/RediSearch/releases/tag/v2.6.6)__\n* __[RedisJSON 2.4.6](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.4.6)__\n* __[RedisGraph 2.10.9](https://github.com/RedisGraph/RedisGraph/releases/tag/v2.10.9)__\n* __[RedisTimeSeries 1.8.9](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.8.9)__\n* __[RedisBloom 2.4.5](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.4.5)__\n\n**Recommended Client Libraries**\n* Java\n  * [Jedis 4.3.1 or greater ](https://github.com/redis/jedis/releases/tag/v4.3.1)\n  * [redis-om-spring](https://github.com/redis/redis-om-spring)\n* Python\n  * [redis-py 4.4.0 or greater ](https://github.com/redis/redis-py/releases/tag/v4.4.0)\n  * [redis-om-python](https://github.com/redis/redis-om-python)\n* NodeJS\n  * [node-redis 4.5.0  or greater](https://www.npmjs.com/package/redis)\n  * [redis-om-node](https://github.com/redis/redis-om-node)\n* .NET\n  * [redis-om-dotnet](https://github.com/redis/redis-om-dotnet)\n  * [NRedisStack](https://github.com/redis/NRedisStack)\n\nCompatible with the latest [RedisInsight](https://redis.io/download). The docker image redis/redis-stack for this version is bundled with [RedisInsight 2.20](https://github.com/RedisInsight/RedisInsight/releases/tag/2.20.0).\n\nNote: version numbers follow the pattern:\n\n`x.y.z-b`\n* `x.y` Redis major version\n* `z` increases with even numbers as a module x.y version increases.\n* `b` denotes a patch to Redis or a module (any `z` of Redis or modules). `b` will consist of a `v` + numeric value."},{"id":"redis-stack-server-626-v5-march-2023","title":"Redis Stack Server 6.2.6-v5 (March 2023)","role":"content","text":"This is a maintenance release for Redis Stack Server 6.2.6\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!"},{"id":"headlines-12","title":"Headlines:","role":"content","text":"This version contains the latest RediSearch 2.6.6, RedisJSON 2.4.6, RedisGraph 2.10.9, RedisTimeSeries 1.8.8, RedisBloom 2.4.5, and RedisInsight 2.20 with improvements and bugs fixed."},{"id":"details-11","title":"Details:","role":"content","text":"**Improvements**\n* RediSearch [ #3397](https://github.com/RediSearch/RediSearch/pull/3397) Improve the Vecsim initial capacity default value\n* RediSearch and RedisJSON [#3418](https://github.com/RediSearch/RediSearch/pull/3418) Add support to OS Amazon Linux 2\n* RediSearch and RedisJSON - Add full OS list on RAMP file\n* RedisBloom - Internal changes for supporting future Redis Enterprise releases\n\n**Bug Fixes**\n* RediSearch [#3403](https://github.com/RediSearch/RediSearch/pull/3403) Fix suffix and prefix matching when using `CASESENSITIVE` flag\n* RedisJSON [#912](https://github.com/RedisJSON/RedisJSON/pull/912) Fix actual memory usage calculation\n* RedisGraph [#2880](https://github.com/RedisGraph/RedisGraph/issues/2880) Potential crash when using `WITH *` expressions\n* RedisGraph [#2917](https://github.com/RedisGraph/RedisGraph/issues/2917) Potential crash when using `CASE` expressions\n* RedisGraph [#2836](https://github.com/RedisGraph/RedisGraph/issues/2836) Potential crash on `*0` variable-length path\n* RedisGraph [#2916](https://github.com/RedisGraph/RedisGraph/issues/2916) Potential crash when executing concurrent queries that utilize full-text indices\n* RedisTimeSeries [#1290](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/1290) Potential crash when using `FILTER_BY_TS`\n* RedisTimeSeries [#1397](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1397) Memory leak when trying to create an already existing key\n\n**Redis version**\n* [Redis 6.2.11](https://github.com/redis/redis/blob/6.2.11/00-RELEASENOTES)\n\n**Module versions**\n* __[RediSearch 2.6.6](https://github.com/RediSearch/RediSearch/releases/tag/v2.6.6)__\n* __[RedisJSON 2.4.6](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.4.6)__\n* __[RedisGraph 2.10.9](https://github.com/RedisGraph/RedisGraph/releases/tag/v2.10.9)__\n* __[RedisTimeSeries 1.8.8](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.8.8)__\n* __[RedisBloom 2.4.5](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.4.5)__\n\n**Recommended Client Libraries**\n* Java\n  * [Jedis 4.3.1 or greater ](https://github.com/redis/jedis/releases/tag/v4.3.1)\n  * [redis-om-spring](https://github.com/redis/redis-om-spring)\n* Python\n  * [redis-py 4.4.0 or greater ](https://github.com/redis/redis-py/releases/tag/v4.4.0)\n  * [redis-om-python](https://github.com/redis/redis-om-python)\n* NodeJS\n  * [node-redis 4.5.0  or greater](https://www.npmjs.com/package/redis)\n  * [redis-om-node](https://github.com/redis/redis-om-node)\n* .NET\n  * [redis-om-dotnet](https://github.com/redis/redis-om-dotnet)\n  * [NRedisStack](https://github.com/redis/NRedisStack)\n\nCompatible with the latest [RedisInsight](https://redis.io/download). The docker image redis/redis-stack for this version is bundled with [RedisInsight 2.20](https://github.com/RedisInsight/RedisInsight/releases/tag/2.20.0).\n\nNote: version numbers follow the pattern:\n\n`x.y.z-b`\n* `x.y` Redis major version\n* `z` increases with even numbers as a module x.y version increases.\n* `b` denotes a patch to Redis or a module (any `z` of Redis or modules). `b` will consist of a `v` + numeric value."},{"id":"redis-stack-server-626-v4-february-2023","title":"Redis Stack Server 6.2.6-v4 (February 2023)","role":"content","text":"This is a maintenance release for Redis Stack Server 6.2.6\n\nUpdate urgency: `LOW`: No need to upgrade unless there are new features you want to use."},{"id":"headlines-13","title":"Headlines:","role":"content","text":"This version contains the latest RedisJSON 2.4.5 with support for Ubuntu 20 - Focal Fossa OS.\n\n**Module versions**\n* __[RediSearch 2.6.5](https://github.com/RediSearch/RediSearch/releases/tag/v2.6.5)__\n* __[RedisJSON 2.4.5](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.4.5)__\n* __[RedisGraph 2.10.8](https://github.com/RedisGraph/RedisGraph/releases/tag/v2.10.8)__\n* __[RedisTimeSeries 1.8.5](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.8.5)__\n* __[RedisBloom 2.4.4](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.4.4)__\n\n**Recommended Client Libraries**\n* Java\n  * [Jedis 4.3.1 or greater ](https://github.com/redis/jedis/releases/tag/v4.3.1)\n  * [redis-om-spring](https://github.com/redis/redis-om-spring)\n* Python\n  * [redis-py 4.4.0 or greater ](https://github.com/redis/redis-py/releases/tag/v4.4.0)\n  * [redis-om-python](https://github.com/redis/redis-om-python)\n* NodeJS\n  * [node-redis 4.5.0  or greater](https://www.npmjs.com/package/redis)\n  * [redis-om-node](https://github.com/redis/redis-om-node)\n* .NET\n  * [redis-om-dotnet](https://github.com/redis/redis-om-dotnet)\n  * [NRedisStack](https://github.com/redis/NRedisStack)\n\nCompatible with the latest [RedisInsight](https://redis.io/download). The docker image redis/redis-stack for this version is bundled with [RedisInsight 2.18](https://github.com/RedisInsight/RedisInsight/releases/tag/2.18.0).\n\nNote: version numbers follow the pattern:\n\n`x.y.z-b`\n* `x.y` Redis major version\n* `z` increases with even numbers as a module x.y version increases.\n* `b` denotes a patch to Redis or a module (any `z` of Redis or modules). `b` will consist of a `v` + numeric value."},{"id":"redis-stack-server-626-v3-february-2023","title":"Redis Stack Server 6.2.6-v3 (February 2023)","role":"content","text":"This is a maintenance release for Redis Stack Server 6.2.6\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!"},{"id":"headlines-14","title":"Headlines:","role":"content","text":"This version contains the latest RediSearch 2.6.5, RedisJSON 2.4.4, RedisGraph 2.10.8, RedisBloom 2.4.4, and RedisInsight 2.18 with new features and bugs fixed."},{"id":"details-12","title":"Details:","role":"content","text":"**Improvements**\n* RediSearch [#3361](https://github.com/RediSearch/RediSearch/pull/3361) Enable the use of IPv6 for all cluster and module communication\n* RedisJSON [#813](https://github.com/RedisJSON/RedisJSON/pull/813) - Improve the errors messages in modules [#725](https://github.com/RedisJSON/RedisJSON/issues/725)\n* RedisJSON [#918](https://github.com/RedisJSON/RedisJSON/pull/918) - Add IPv6 on the capability list\n* RedisGraph [#2790](https://github.com/RedisGraph/RedisGraph/pull/2790) Improved performance by disabling SuiteSparse:GraphBLAS' global free pool\n* RedisGraph [#2758](https://github.com/RedisGraph/RedisGraph/pull/2758) Improved edge deletion performance\n* RedisGraph [#2781](https://github.com/RedisGraph/RedisGraph/issues/2781) `indegree` and `outdegree` now also accept an argument which is a list of labels\n* RedisBloom [#389](https://github.com/RedisBloom/RedisBloom/issues/389) Introduce `BF.CARD` to retrieve the cardinality of a Bloom filter or 0 when such key does not exist\n\n**Bug Fixes**\n* RediSearch [#3354](https://github.com/RediSearch/RediSearch/pull/3354) Library update preventing a crash during cluster failover\n* RediSearch [#3357](https://github.com/RediSearch/RediSearch/pull/3357) Handling division by zero in expressions preventing nodes to restart\n* RediSearch [#3332](https://github.com/RediSearch/RediSearch/pull/3332) Fix wildcards * queries on `DIALECT 2` and `DIALECT 3`\n* RedisJSON [#919](https://github.com/RedisJSON/RedisJSON/pull/919) Possible failure loading `.rdb` files\n* RedisGraph [#2777](https://github.com/RedisGraph/RedisGraph/issues/2777), [#2841](https://github.com/RedisGraph/RedisGraph/issues/2841) Potential crash when sending queries from multiple connections and timeout is not 0\n* RedisGraph [#2844](https://github.com/RedisGraph/RedisGraph/issues/2844) Potential partial results when same parametrized query is running from multiple connections\n* RedisGraph [#2739](https://github.com/RedisGraph/RedisGraph/issues/2739), [#2774](https://github.com/RedisGraph/RedisGraph/issues/2774) Paths with exact variable length >1 are not matched\n* RedisGraph [#2794](https://github.com/RedisGraph/RedisGraph/issues/2794) `toInteger` and `toIntegerOrNull` don't convert Booleans\n* RedisGraph [#2798](https://github.com/RedisGraph/RedisGraph/issues/2798) `right` and `left` should reply with an error when `length` is null\n* RedisGraph [#2809](https://github.com/RedisGraph/RedisGraph/issues/2809) `TIMEOUT_MAX` configuration parameter in not enforced when `TIMEOUT_DEFAULT` is 0\n* RedisGraph [#2780](https://github.com/RedisGraph/RedisGraph/issues/2780) `indegree` and `outdegree` - relationships are counted more than once when same relationship type is supplied more than once\n* RedisBloom [#609](https://github.com/RedisBloom/RedisBloom/issues/609) `CF.INFO` - incorrect information for large filters\n\n**Redis version**\n* [Redis 6.2.10](https://github.com/redis/redis/blob/6.2.10/00-RELEASENOTES)\n\n**Module versions**\n* __[RediSearch 2.6.5](https://github.com/RediSearch/RediSearch/releases/tag/v2.6.5)__\n* __[RedisJSON 2.4.4](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.4.4)__\n* __[RedisGraph 2.10.8](https://github.com/RedisGraph/RedisGraph/releases/tag/v2.10.8)__\n* __[RedisTimeSeries 1.8.5](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.8.5)__\n* __[RedisBloom 2.4.4](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.4.4)__\n\n**Recommended Client Libraries**\n* Java\n  * [Jedis 4.3.1 or greater ](https://github.com/redis/jedis/releases/tag/v4.3.1)\n  * [redis-om-spring](https://github.com/redis/redis-om-spring)\n* Python\n  * [redis-py 4.4.0 or greater ](https://github.com/redis/redis-py/releases/tag/v4.4.0)\n  * [redis-om-python](https://github.com/redis/redis-om-python)\n* NodeJS\n  * [node-redis 4.5.0  or greater](https://www.npmjs.com/package/redis)\n  * [redis-om-node](https://github.com/redis/redis-om-node)\n* .NET\n  * [nredisstack 0.5.0  or greater](https://www.nuget.org/packages/nredisstack/)\n  * [redis-om-dotnet](https://github.com/redis/redis-om-dotnet)\n\nCompatible with the latest [RedisInsight](https://redis.io/download). The docker image redis/redis-stack for is bundled with [RedisInsight 2.18](https://github.com/RedisInsight/RedisInsight/releases/tag/2.18.0).\n\nNote: version numbers follow the pattern:\n\n`x.y.z-b`\n* `x.y` Redis major version\n* `z` increases with even numbers as a module x.y version increases.\n* `b` denotes a patch to Redis or a module (any `z` of Redis or modules). `b` will consist of a `v` + numeric value."},{"id":"redis-stack-server-626-v2--january-2023","title":"Redis Stack Server 6.2.6-v2  (January 2023)","role":"content","text":"This is a maintenance release for Redis Stack Server 6.2.6\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!"},{"id":"headlines-15","title":"Headlines:","role":"content","text":"This version contains the latest RedisInsight 2.16 and RedisTimeSeries 1.8.5 with bugs fixed."},{"id":"details-13","title":"Details:","role":"content","text":"**Bug Fixes**\n* RedisTimeSeries [#1388](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1388) Potential crash when upgrading from v1.6 to 1.8 if there are compactions with `min` or `max` aggregation\n \n**Redis version** (no changes)\n* [Redis 6.2.8](https://github.com/redis/redis/blob/6.2.7/00-RELEASENOTES)\n \n**Module versions**\n* __[RediSearch 2.6.4](https://github.com/RediSearch/RediSearch/releases/tag/v2.6.4)__\n* __[RedisJSON 2.4.3](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.4.3)__\n* __[RedisGraph 2.10.5](https://github.com/RedisGraph/RedisGraph/releases/tag/v2.10.5)__\n* __[RedisTimeSeries 1.8.5](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.8.5)__\n* __[RedisBloom 2.4.3](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.4.3)__\n\n**Recommended Client Libraries**\n* Java\n  * [Jedis 4.3.1 or greater ](https://github.com/redis/jedis/releases/tag/v4.3.1)\n  * [redis-om-spring](https://github.com/redis/redis-om-spring)\n* Python\n  * [redis-py 4.4.0 or greater ](https://github.com/redis/redis-py/releases/tag/v4.4.0)\n  * [redis-om-python](https://github.com/redis/redis-om-python)\n* NodeJS\n  * [node-redis 4.5.0  or greater](https://www.npmjs.com/package/redis)\n  * [redis-om-node](https://github.com/redis/redis-om-node)\n* .NET\n  * [redis-om-dotnet](https://github.com/redis/redis-om-dotnet)\n\nCompatible with the latest [RedisInsight](https://redis.io/download). The docker image redis/redis-stack for this version is bundled with [RedisInsight 2.16](https://github.com/RedisInsight/RedisInsight/releases/tag/2.16.0).\n\nNote: version numbers follow the pattern:\n\n`x.y.z-b`\n* `x.y` Redis major version\n* `z` increases with even numbers as a module x.y version increases.\n* `b` denotes a patch to Redis or a module (any `z` of Redis or modules). `b` will consist of a `v` + numeric value."},{"id":"redis-stack-626-v1-january-2023","title":"Redis Stack 6.2.6-v1 (January 2023)","role":"content","text":"This is a maintenance release for Redis Stack Server 6.2.6\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!"},{"id":"headlines-16","title":"Headlines:","role":"content","text":"This version contains the latest RediSearch 2.6.4, RedisJSON 2.4.3, RedisGraph 2.10.5, and RedisTimeSeries 1.8.4 with new features and bugs fixed."},{"id":"details-14","title":"Details:","role":"content","text":"**Improvements**\n* RediSearch [#3256](https://github.com/RediSearch/RediSearch/pull/3256) Support IPv6 on cluster set command\n* RediSearch [#3194](https://github.com/RediSearch/RediSearch/pull/3194) Add the query dialects that are in use to `FT.INFO` and `INFO MODULE` commands\n* RediSearch [#3258](https://github.com/RediSearch/RediSearch/pull/3258) Add the module version and Redis version to `INFO MODULE`\n* RedisJSON [#892](https://github.com/RedisJSON/RedisJSON/pull/892) Allow `JSON.ARRINDEX` with none scalar values\n* RedisGraph [#2757](https://github.com/RedisGraph/RedisGraph/pull/2757) Improved performance of `indegree` and `outdegree`\n* RedisGraph [#2681](https://github.com/RedisGraph/RedisGraph/issues/2681) Fixed some error messages\n* RedisGraph [#2740](https://github.com/RedisGraph/RedisGraph/issues/2740) Don’t show partial results for timed-out [GRAPH.PROFILE](https://redis.io/commands/graph.profile/)\n* RedisTimeSeries [#1215](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/1215) OSS cluster: Support TLS and IPv6; Introduce new configuration parameter: [OSS_GLOBAL_PASSWORD](https://redis.io/docs/stack/timeseries/configuration/#oss_global_password)\n \n**Bug Fixes**\n* RediSearch [#3289](https://github.com/RediSearch/RediSearch/pull/3289) Potential crash when querying multiple fields\n* RediSearch [#3279](https://github.com/RediSearch/RediSearch/pull/3279) Potential crash when querying using wildcard * on TAG field\n* RedisJSON [#890](https://github.com/RedisJSON/RedisJSON/pull/890) JSONPath ignores any filter condition beyond the second\n* RedisGraph [#2754](https://github.com/RedisGraph/RedisGraph/pull/2754) Partial sync may hang\n* RedisTimeSeries [#1360](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1360) Potential crash when upgrading from v1.6 to 1.8 if there are compactions with `min` or `min` aggregation\n* RedisTimeSeries [#1370](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/1370) Potential crash when using [TS.REVRANGE](https://redis.io/commands/ts.revrange/) or [TS.MREVRANGE](https://redis.io/commands/ts.mrevrange/) with aggregation\n* RedisTimeSeries [#1347](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1347) When adding samples with [TS.ADD](https://redis.io/commands/ts.add/) or [TS.MADD](https://redis.io/commands/ts.madd/) using * as timestamp, the timestamp could differ between master and replica shards\n \n**Redis version** (no changes)\n* [Redis 6.2.8](https://github.com/redis/redis/blob/6.2.8/00-RELEASENOTES)\n \n**Module versions**\n* __[RediSearch 2.6.4](https://github.com/RediSearch/RediSearch/releases/tag/v2.6.4)__\n* __[RedisJSON 2.4.3](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.4.3)__\n* __[RedisGraph 2.10.5](https://github.com/RedisGraph/RedisGraph/releases/tag/v2.10.5)__\n* __[RedisTimeSeries 1.8.4](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.8.4)__\n* __[RedisBloom 2.4.3 (v2.4.3)](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.4.3)__\n\n**Recommended Client Libraries**\n* Java\n  * [Jedis 4.3.1 or greater ](https://github.com/redis/jedis/releases/tag/v4.3.1)\n  * [redis-om-spring](https://github.com/redis/redis-om-spring)\n* Python\n  * [redis-py 4.4.0 or greater ](https://github.com/redis/redis-py/releases/tag/v4.4.0)\n  * [redis-om-python](https://github.com/redis/redis-om-python)\n* NodeJS\n  * [node-redis 4.5.0  or greater](https://www.npmjs.com/package/redis)\n  * [redis-om-node](https://github.com/redis/redis-om-node)\n* .NET\n  * [redis-om-dotnet](https://github.com/redis/redis-om-dotnet)\n\nCompatible with [RedisInsight](https://redis.io/download). The docker image redis/redis-stack for this version is bundled with [RedisInsight 2.14](https://github.com/RedisInsight/RedisInsight/releases/tag/2.14.0).\n\nNote: version numbers follow the pattern:\n\n`x.y.z-b`\n* `x.y` Redis major version\n* `z` increases with even numbers as a module x.y version increases.\n* `b` denotes a patch to Redis or a module (any `z` of Redis or modules). `b` will consist of a `v` + numeric value."},{"id":"redis-stack-server-626-v0-december-2022","title":"Redis Stack Server 6.2.6-v0 (December 2022)","role":"content","text":"This is a GA release of Redis Stack version 6.2.6"},{"id":"headlines-17","title":"Headlines:","role":"content","text":"* Query & Search:\n  - multi-value index and query for: text, tag, numeric, geo and vector!\n  - suffix search `*vatore` and wildcard search `ant?rez`\n  - support for FP64 vectors and range queries from a given vector\n* New faster JSONPath parser\n* New probabilistic data structure: t-digest\n* New path-finding algorithms `algo.SPpaths` and `algo.SSpaths` for Graph\n* Support for gap filling for Time series"},{"id":"details-15","title":"Details:","role":"content","text":"**RediSearch**  introduces the following features:\n* Support for [wildcard queries](https://redis.io/docs/stack/search/reference/query_syntax/#wildcard-matching)  for TEXT and TAG fields, where\n  - `?` matches any single character\n  - `*` matches zero or more characters\n  - use `'` and `\\` for escaping, other special characters are ignored\n  - Optimized wildcard query support (i.e., suffix trie)\n* Multi-value indexing and querying\n  - Multi-value text search - perform full-text search on [array of string or on a JSONPath](https://redis.io/docs/stack/search/indexing_json/#index-json-arrays-as-tag) leading to multiple strings\n  - Support for Geo, Vector, Numeric, Tag\n  - Return JSON rather than scalars from multi-value attributes. This is enabled via Dialect 3 in order not to break existing applications.\n  - Support indexing and querying of multi-value JSONPath attributes and/or arrays (requires JSON >2.4.1)\n  - Support for `SORTABLE` fields on JSON in an implicit un-normalized form (UNF)\n* Vector similarity 0.5.1:\n  - Better space optimization selection\n  - Aligning index capacity with block size\n  - Support FLOAT64 as vector data type\n  - Range query support\n  - Support query attributes for vector queries\n\n**RedisJSON**  introduces the following features:\n* Add JSONPath filter the regexp match operator\n* Support legacy JSONpath with dollar `$`\n* A new JSONPath library which enhances the performance of parsing any JSONPath expression in RedisJSON.\n* Floating point numbers which become round numbers due to some operation, for example by `JSON.NUMINCRBY`, will now return as a floating point with a trailing `.0`, e.g., instead of just `42`, now `42.0` will be returned.\n\n**RedisBloom** introduces the following new features:\n* A new sketch data structure: t-digest. t-digest is a sketch data structure for estimating quantiles based on a data stream or a large dataset of values. As for other sketch data structures, t-digest requires sublinear space and has controllable space-accuracy tradeoffs.\n\n**RedisGraph** introduces the following new features:\n* New path-finding algorithms: \n  - The `algo.SPpaths` procedure returns one, _n_, or all minimal-weight, optionally bounded-cost, optionally bounded-length paths between a given pair of nodes.\n  - The `algo.SSpaths` procedure returns one, _n_, or all minimal-weight, optionally bounded-cost, optionally bounded-length paths from a given node.\n* Introduce `SET` for adding node labels and `REMOVE` for removing node labels, node properties, and edge properties\n* Support deleting paths with `DELETE`\n* Introduce `toBoolean`, `toBooleanOrNull`, `toFloatOrNull`, `toIntegerOrNull`, `toStringOrNull`, `toBooleanList`, `toFloatList`, `toIntegerList`, `toStringList`, `properties`, `split`, `last`, `isEmpty`,`e`, `exp`, `log`, `log10`, `sin`, `cos`, `tan`, `cot`, `asin`, `acos`, `atan`, `atan2`, `degrees`, `radians`, `pi`, and `haversin` functions.\n* Graph slow log can be reset with `GRAPH.SLOWLOG g RESET`\n* Queries are now atomic (_Atomicity_ is the guarantee that each query either succeeds or fails with no side effects). Whenever a failure occurs, the query effect is rolled-back from an undo-log.\n \n**RedisTimeSeries** introduces the following new features:\n* Introduction of a new aggregator: `twa` (time-weighted average)\n* Introduction of a new optional `EMPTY` flag to `TS.RANGE`, `TS.REVRANGE`, `TS.MRANGE`, and `TS.MREVRANGE` to retrieve aggregations for empty buckets as well.\n* Gap-filling: Using `EMPTY` when the aggregator is `twa` allows estimating the average of a continuous signal even for empty buckets based on linear interpolation of previous and next samples. Using `EMPTY` when the aggregator is `last` would repeat the value of the previous sample when the bucket is empty.\n* Introduction of a new optional `BUCKETTIMESTAMP` parameter to `TS.RANGE`, `TS.REVRANGE`, `TS.MRANGE`, and `TS.MREVRANGE`. It is now possible to report the start time, the end time, or the mid time for each bucket.\n* Introduction of a new optional `alignTimestamp` parameter to `TS.CREATERULE` and to `COMPACTION_POLICY` configuration parameter.  It is now possible to define alignment for compaction rules, so one can, for example, aggregate daily events from 06:00 to 06:00 the next day.\n* Introduction of additional reducer types in `GROUPBY` (`TS.MRANGE`, and `TS.MREVRANGE`): `avg`, `range`, `count`, `std.p`, `std.s`, `var.p`, and `var.s`\n* Introduction of a new optional `LATEST` flag to `TS.GET`, `TS.MGET`, `TS.RANGE`, `TS.REVRANGE`, `TS.MRANGE`, and `TS.MREVRANGE`. it is possible to retrieve the latest (possibly partial) bucket as well.\n\n**Bug Fixes** (since 6.2.6-RC1):\n* RediSearch [#3098](https://github.com/RediSearch/RediSearch/pull/3098) Wrong return value in Geo query\n* RediSearch [#3230](https://github.com/RediSearch/RediSearch/pull/3230) Precalculated number of replies must be equal to actual number\n* RediSearch [#3171](https://github.com/RediSearch/RediSearch/pull/3171) Shard of DB with RedisSearch 2.4.8/11 got restarted by node_wd\n* RediSearch [#3197](https://github.com/RediSearch/RediSearch/pull/3197) RediSearch 2.4.15 crashed\n* RediSearch [#3197](https://github.com/RediSearch/RediSearch/pull/3197) failure to create temporary indices\n* RedisJSON [#850](https://github.com/RedisJSON/RedisJSON/pull/850) Allow repetition of filter relation instead of optional\n* RedisGraph [#2695](https://github.com/RedisGraph/RedisGraph/pull/2695) Potential crash on certain write queries\n* RedisGraph [#2724](https://github.com/RedisGraph/RedisGraph/pull/2724) Potential crash when setting property values based on nonexistent properties\n* RedisGraph [#2460](https://github.com/RedisGraph/RedisGraph/issues/2460), [#2637](https://github.com/RedisGraph/RedisGraph/issues/2637), [#2680](https://github.com/RedisGraph/RedisGraph/issues/2680) Crash on invalid queries\n* RedisGraph [#2672](https://github.com/RedisGraph/RedisGraph/pull/2672) Wrong matching result on multiple labels\n* RedisGraph [#2643](https://github.com/RedisGraph/RedisGraph/issues/2643) Duplicate reports when matching relationship type `:R|R`\n* RedisGraph [#2687](https://github.com/RedisGraph/RedisGraph/issues/2687), [#2414](https://github.com/RedisGraph/RedisGraph/issues/2414) Error when `UNWIND`ing relationships\n* RedisGraph [#2636](https://github.com/RedisGraph/RedisGraph/issues/2636) `MERGE` …` ON` ... - cannot remove property by setting it to null\n* RedisGraph [#2710](https://github.com/RedisGraph/RedisGraph/pull/2710) Undo-log fix\n* RedisGraph [#2435](https://github.com/RedisGraph/RedisGraph/pull/2435) Incorrect result when comparing a value to NaN\n* RedisGraph [#2497](https://github.com/RedisGraph/RedisGraph/pull/2497) Incorrect result when comparing a value to null\n* RedisGraph [#2676](https://github.com/RedisGraph/RedisGraph/issues/2676) `sqrt`, `log`, `log10` - incorrect result for negative values\n* RedisGraph [#2213](https://github.com/RedisGraph/RedisGraph/issues/2213) Division and Modulo by zero - wrong behavior\n* RedisTimeSeries [#1333](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/1333) Potential crash when aggregating over a compaction with the `avg` aggregator and the `LATEST` flag\n \n**Redis version** (no changes)\n* [Redis 6.2.7](https://github.com/redis/redis/blob/6.2.7/00-RELEASENOTES)\n \n**Module versions**\n* __[RediSearch 2.6.3](https://github.com/RediSearch/RediSearch/releases/tag/v2.6.3)__\n* __[RedisJSON 2.4.2](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.4.2)__\n* __[RedisGraph 2.10.4](https://github.com/RedisGraph/RedisGraph/releases/tag/v2.10.4)__\n* __[RedisTimeSeries 1.8.3](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.8.3)__\n* __[RedisBloom 2.4.3](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.4.3)__\n\n**Recommended Client Libraries  (no changes)**\n* Java\n  * [Jedis 4.2.0 or greater ](https://github.com/redis/jedis/releases/tag/v4.2.0)\n  * [redis-om-spring](https://github.com/redis/redis-om-spring)\n* Python\n  * [redis-py 4.3.1 or greater ](https://github.com/redis/redis-py/releases/tag/v4.3.1)\n  * [redis-om-python](https://github.com/redis/redis-om-python)\n* NodeJS\n  * [node-redis 4.4.0  or greater](https://www.npmjs.com/package/redis)\n  * [redis-om-node](https://github.com/redis/redis-om-node)\n* .NET\n  * [redis-om-dotnet](https://github.com/redis/redis-om-dotnet)\n\nCompatible with the latest [RedisInsight](https://redis.io/download). The docker image redis/redis-stack for this version is bundled with [RedisInsight 2.14](https://github.com/RedisInsight/RedisInsight/releases/tag/2.14.0).\n\nNote: version numbers follow the pattern:\n\n`x.y.z-b`\n* `x.y` Redis major version\n* `z` increases with even numbers as a module x.y version increases.\n* `b` denotes a patch to Redis or a module (any `z` of Redis or modules). `b` will consist of a `v` + numeric value."},{"id":"redis-stack-server-626-rc1-november-2022","title":"Redis Stack Server 6.2.6-RC1 (November 2022)","role":"content","text":"This is a Release Candidate of Redis Stack Server 6.2.6"},{"id":"headlines-18","title":"Headlines:","role":"content","text":"* Query & Search:\n  - multi-value index and query for: text, tag, numeric, geo and vector!\n  - affix search `*oolman` and wildcard search `y?fta*`\n  - support for FP64 vectors\n* New faster JSONPath parser\n* New probabilistic data structure: t-digest\n* New path-finding algorithms `algo.SPpaths` and `algo.SSpaths` for Graph\n* Support for gap filling for Time series"},{"id":"details-16","title":"Details:","role":"content","text":"**RediSearch**  introduces the following features:\n* the ability to search using wildcard queries for TEXT and TAG fields. This enables the frequently asked feature of affix search and includes optimized wildcard query support :\n   - `?` matches any single character\n   - `*` matches zero or more characters\n   - use `’` and `\\` for escaping, other special characters are ignored\n* Multi-value indexing and querying of attributes for any attribute type ([Text](https://redis.io/docs/stack/search/indexing_json/#index-json-arrays-as-text), [Tag](https://redis.io/docs/stack/search/indexing_json/#index-json-arrays-as-tag), [Numeric](https://redis.io/docs/stack/search/indexing_json/#index-json-arrays-as-numeric), [Geo](https://redis.io/docs/stack/search/indexing_json/#index-json-arrays-as-geo) and [Vector](https://redis.io/docs/stack/search/indexing_json/#index-json-arrays-as-vector)) defined by a [JSONPath](https://redis.io/docs/stack/json/path/) leading to an array:\n  - Multi-value text search - perform full-text search on [array of string or on a JSONPath](https://redis.io/docs/stack/search/indexing_json/#index-json-arrays-as-tag) leading to multiple strings\n  - Return JSON rather than scalars from multi-value attributes. This is enabled via Dialect 3 in order not to break existing applications.\n  - Support for `SORTABLE` fields on JSON in an implicit un-normalized form (UNF)\n* Support for indexing double-precision floating-point vectors and range queries from a given vector:\n  - Better space optimization selection\n  - Aligning index capacity with block size\n  - Support FLOAT64 as vector data type\n  - Range query support\n  - Support query attributes for vector queries\n \n**RedisJSON**  introduces the following features:\n* A new JSONPath library which enhances the performance of parsing any JSONPath expression in RedisJSON.\n \n**RedisBloom** introduces the following new features:\n* A new sketch data structure: t-digest. t-digest is a sketch data structure for estimating quantiles based on a data stream or a large dataset of values. As for other sketch data structures, t-digest requires sublinear space and has controllable space-accuracy tradeoffs.\n\n**RedisGraph** introduces the following new features:\n* New path-finding algorithms: \n  - The `algo.SPpaths` procedure returns one, _n_, or all minimal-weight, optionally bounded-cost, optionally bounded-length paths between a given pair of nodes.\n  - The `algo.SSpaths` procedure returns one, _n_, or all minimal-weight, optionally bounded-cost, optionally bounded-length paths from a given node.\n* Introduce `SET` for adding node labels and `REMOVE` for removing node labels, node properties, and edge properties\n* Support deleting paths with `DELETE`\n* Introduce `toBoolean`, `toBooleanOrNull`, `toFloatOrNull`, `toIntegerOrNull`, `toStringOrNull`, `toBooleanList`, `toFloatList`, `toIntegerList`, `toStringList`, `properties`, `split`, `last`, `isEmpty`,`e`, `exp`, `log`, `log10`, `sin`, `cos`, `tan`, `cot`, `asin`, `acos`, `atan`, `atan2`, `degrees`, `radians`, `pi`, and `haversin` functions.\n* Graph slow log can be reset with `GRAPH.SLOWLOG g RESET` (also added in 2.8.20)\n* Queries are now atomic (_Atomicity_ is the guarantee that each query either succeeds or fails with no side effects). Whenever a failure occurs, the query effect is rolled-back from an undo-log.\n \n**RedisTimeSeries** introduces the following new features:\n* Introduction of a new aggregator: `twa` (time-weighted average)\n* Introduction of a new optional `EMPTY` flag to `TS.RANGE`, `TS.REVRANGE`, `TS.MRANGE`, and `TS.MREVRANGE` to retrieve aggregations for empty buckets as well.\n* Gap-filling: Using `EMPTY` when the aggregator is `twa` allows estimating the average of a continuous signal even for empty buckets based on linear interpolation of previous and next samples. Using `EMPTY` when the aggregator is `last` would repeat the value of the previous sample when the bucket is empty.\n* Introduction of a new optional `BUCKETTIMESTAMP` parameter to `TS.RANGE`, `TS.REVRANGE`, `TS.MRANGE`, and `TS.MREVRANGE`. It is now possible to report the start time, the end time, or the mid time for each bucket.\n* Introduction of a new optional `alignTimestamp` parameter to `TS.CREATERULE` and to `COMPACTION_POLICY` configuration parameter.  It is now possible to define alignment for compaction rules, so one can, for example, aggregate daily events from 06:00 to 06:00 the next day.\n* Introduction of additional reducer types in `GROUPBY` (`TS.MRANGE`, and `TS.MREVRANGE`): `avg`, `range`, `count`, `std.p`, `std.s`, `var.p`, and `var.s`\n* Introduction of a new optional `LATEST` flag to `TS.GET`, `TS.MGET`, `TS.RANGE`, `TS.REVRANGE`, `TS.MRANGE`, and `TS.MREVRANGE`. it is possible to retrieve the latest (possibly partial) bucket as well.\n\n \n**Redis version** (no changes)\n* [Redis 6.2.7](https://github.com/redis/redis/blob/6.2.7/00-RELEASENOTES)\n \n\n**Module versions**\n* __[RediSearch 2.6.1](https://github.com/RediSearch/RediSearch/releases/tag/v2.6.1)__\n* __[RedisJSON 2.4.0](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.4.0)__\n* __[RedisGraph 2.10-RC1 (v2.10.2)](https://github.com/RedisGraph/RedisGraph/releases/tag/v2.10.2)__\n* __[RedisTimeSeries 1.8-RC3 (v1.8.2)](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.8.2)__\n* __[RedisBloom 2.4-RC3 (v2.4.2)](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.4.2)__\n\n**Recommended Client Libraries  (no changes)**\n* Java\n  * [Jedis 4.2.0 or greater ](https://github.com/redis/jedis/releases/tag/v4.2.0)\n  * [redis-om-spring](https://github.com/redis/redis-om-spring)\n* Python\n  * [redis-py 4.3.1 or greater ](https://github.com/redis/redis-py/releases/tag/v4.3.1)\n  * [redis-om-python](https://github.com/redis/redis-om-python)\n* NodeJS\n  * [node-redis 4.4.0  or greater](https://www.npmjs.com/package/redis)\n  * [redis-om-node](https://github.com/redis/redis-om-node)\n* .NET\n  * [redis-om-dotnet](https://github.com/redis/redis-om-dotnet)\n\nCompatible with the latest [RedisInsight](https://redis.io/download). The docker image redis/redis-stack for this version is bundled with RedisInsight 2.12.0.\n\nNote: version numbers follow the pattern:\n\n`x.y.z-b`\n* `x.y` Redis major version\n* `z` increases with even numbers as a module x.y version increases.\n* `b` denotes a patch to Redis or a module (any `z` of Redis or modules). `b` will consist of a `v` + numeric value."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisstack/redisstack-7.2-release-notes","title":"Redis Stack 7.2 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisstack/redisstack-7.2-release-notes/","summary":"Redis Stack 7.2 release notes.","tags":["docs","operate","stack"],"last_updated":"2026-08-27T15:53:06+01:00","page_type":"content","content_hash":"ae0d5fa9a5a6017eef6ad1e9c9275a62c71c8d42e868f0ac635a51fc8dd8b490","sections":[{"id":"redis-stack-720-v13-october-2024","title":"Redis Stack 7.2.0-v13 (October 2024)","role":"overview","text":"This is a maintenance release for Redis Stack Server 7.2.0.\n\nUpdate urgency: `SECURITY`: there are security fixes in the release.\n\n[Docker](https://hub.docker.com/r/redis/redis-stack) | [Download](https://redis.io/downloads/#stack)"},{"id":"headlines","title":"Headlines:","role":"content","text":"This version includes security fixes for the **Redis** server, addressing potential vulnerabilities such as an RCE when using Lua library components, and a denial-of-service (DoS) risk due to malformed ACL selectors or unbounded pattern matching.\nAdditionally, this maintenance release contains a bug fix to prevent crashes in cluster mode and includes the latest version of **Redis Insight**."},{"id":"details","title":"Details:","role":"content","text":"**Security and privacy**\n* **Redis**:\n  * (CVE-2024-31449) Lua library commands may lead to stack overflow and potential RCE.\n  * (CVE-2024-31227) Potential Denial-of-service due to malformed ACL selectors.\n  * (CVE-2024-31228) Potential Denial-of-service due to unbounded pattern matching.\n\n**Bug Fixes**\n* **Redis**:\n  *  [#13315](https://github.com/redis/redis/pull/13315) Fixed crashes in cluster mode\n\n* **Redis version**:\n  * __[Redis 7.2.6](https://github.com/redis/redis/releases/tag/7.2.6)__\n\n**Module versions**\t\n* __[RediSearch 2.8.15](https://github.com/RediSearch/RediSearch/releases/tag/v2.8.15)__\n* __[RedisJSON 2.6.11](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.6.11)__\n* __[RedisTimeSeries 1.10.13](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.10.12)__\n* __[RedisBloom 2.6.12](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.6.12)__\n* __[RedisGears 2.0.23](https://github.com/RedisGears/RedisGears/releases)__\n\n**Recommended Client Libraries**\n* Java\n  * [Jedis 5.2.0 or greater](https://github.com/redis/jedis/releases/tag/v5.2.0)\n  * [redis-om-spring 0.9.5 or greater](https://github.com/redis/redis-om-spring/releases/tag/v0.9.5)\n* Python\n  * [redis-py 5.1.0 or greater](https://github.com/redis/redis-py/releases/tag/v5.1.0)\n  * [redis-om-python 0.3.2 or greater](https://github.com/redis/redis-om-python/releases/tag/v0.3.2)\n* NodeJS\n  * [node-redis 4.7.0 or greater](https://github.com/redis/node-redis/releases/tag/redis%404.7.0)\n  * [redis-om-node 0.2.0 or greater](https://github.com/redis/redis-om-node/releases/tag/v0.2.0)\n* .NET\n  * [redis-om-dotnet 0.7.4 or greater](https://github.com/redis/redis-om-dotnet/releases/tag/v0.7.4)\n  * [NRedisStack 0.13.0 or greater](https://github.com/redis/NRedisStack/releases/tag/v0.13.0)\n* Go\n  * [go-redis 9.6.1 or greater](https://github.com/redis/go-redis/releases/tag/v9.6.1)\n  * [rueidis 1.0.47 or greater](https://github.com/redis/rueidis/releases/tag/v1.0.47)\n\nCompatible with [Redis Insight](https://redis.io/download). The docker image redis/redis-stack for this version is bundled with [Redis Insight 2.58](https://github.com/RedisInsight/RedisInsight/releases/tag/2.58.0).\n\nNote: version numbers follow the pattern:\n\n`x.y.z-b`\n* `x.y` Redis major version\n* `z` increases with even numbers as a module x.y version increases.\n* `b` denotes a patch to Redis or a module (any `z` of Redis modules). `b` will consist of a `v` + numeric value."},{"id":"redis-stack-720-v12-august-2024","title":"Redis Stack 7.2.0-v12 (August 2024)","role":"content","text":"This is a maintenance release for Redis Stack Server 7.2.0.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!"},{"id":"headlines-1","title":"Headlines:","role":"content","text":"The updated **search and query**  version introduces several new features and important bug fixes, including a critical one. This new release of Redis Stack 7.2.0 also includes updated versions of **JSON** and **time series** data structures, each incorporating several bug fixes.\n\nThis maintenance release also contains the latest version of **RedisInsight**."},{"id":"details-1","title":"Details:","role":"content","text":"**Improvements**\n* **Redis Search**:\n  * [#4792](https://github.com/RediSearch/RediSearch/pull/4792) - Add character validations to simple string replies and escape it when required (MOD-7258)\n  * [#4768](https://github.com/RediSearch/RediSearch/pull/4768) - Indicate which value is missing on the error message at the aggregation pipeline (MOD-7201)\n  * [#4745](https://github.com/RediSearch/RediSearch/pull/4745) - `GROUPBY` recursion cleanup (MOD-7245)\n  * [#4823](https://github.com/RediSearch/RediSearch/pull/4823) - Mechanism of keys expiration during the query execution clearing intermediate results\n\n**Bug Fixes**\n* **Redis Search**:\n  * [#4754](https://github.com/RediSearch/RediSearch/pull/4754) - Correct return the maximum value for negative values when using `MAX` reducer (MOD-7252)\n  * [#4737](https://github.com/RediSearch/RediSearch/pull/4737) - Separators ignored when escaping backslash `\\` after the escaped character such as in `hello\\\\,world` ignoring `,` (MOD-7240)\n  * [#4717](https://github.com/RediSearch/RediSearch/pull/4717) - Sorting by multiple fields `SORTBY 2 @field1 @field2` was ignoring the subsequent field(MOD-7206)\n  * [#4803](https://github.com/RediSearch/RediSearch/pull/4803) - Keys expiring during query returning empty array (MOD-7010)\n  * [#4794](https://github.com/RediSearch/RediSearch/pull/4794) - Index sanitiser (GC) trying to clean deleted numeric index could cause a crash (MOD-7303)\n\n* **JSON**:\n  * [#1212](https://github.com/RedisJSON/RedisJSON/pull/1212) `JSON.MSET`- AOF commands are duplicated multiple times (MOD-7293)\n\n* **Time series**:\n  * [#1607](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1607) Potential crash after deleting and recreating a source key of a compaction rule (MOD-7338)\n  * [#1610](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1610) `COUNT` argument accepts non-positive values (MOD-5413)\n\n* **Redis version**:\n  * __[Redis 7.2.5](https://github.com/redis/redis/releases/tag/7.2.5)__\n\n**Module versions**\t\n* __[RediSearch 2.8.15](https://github.com/RediSearch/RediSearch/releases/tag/v2.8.15)__\n* __[RedisJSON 2.6.11](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.6.11)__\n* __[RedisTimeSeries 1.10.13](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.10.12)__\n* __[RedisBloom 2.6.12](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.6.12)__\n* __[RedisGears 2.0.23](https://github.com/RedisGears/RedisGears/releases)__\n\n**Recommended Client Libraries**\n* Java\n  * [Jedis 5.1.4 or greater](https://github.com/redis/jedis/releases/tag/v5.1.4)\n  * [redis-om-spring 0.9.4 or greater](https://github.com/redis/redis-om-spring/releases/tag/v0.9.4)\n* Python\n  * [redis-py 5.0.9 or greater](https://github.com/redis/redis-py/releases/tag/v5.0.9)\n  * [redis-om-python 0.3.1 or greater](https://github.com/redis/redis-om-python/releases/tag/v0.3.1)\n* NodeJS\n  * [node-redis 4.7.0 or greater](https://github.com/redis/node-redis/releases/tag/redis%404.7.0)\n  * [redis-om-node 0.2.0 or greater](https://github.com/redis/redis-om-node/releases/tag/v0.2.0)\n* .NET\n  * [redis-om-dotnet 0.7.4 or greater](https://github.com/redis/redis-om-dotnet/releases/tag/v0.7.4)\n  * [NRedisStack 0.12.0 or greater](https://github.com/redis/NRedisStack/releases/tag/v0.12.0)\n* Go\n  * [go-redis 9.6.1 or greater](https://github.com/redis/go-redis/releases/tag/v9.6.1)\n  * [rueidis 1.0.43 or greater](https://github.com/redis/rueidis/releases/tag/v1.0.43)\n\nCompatible with [Redis Insight](https://redis.io/download). The docker image redis/redis-stack for this version is bundled with [Redis Insight 2.54](https://github.com/RedisInsight/RedisInsight/releases/tag/2.54.0).\n\nNote: version numbers follow the pattern:\n\n`x.y.z-b`\n* `x.y` Redis major version\n* `z` increases with even numbers as a module x.y version increases.\n* `b` denotes a patch to Redis or a module (any `z` of Redis or modules). `b` will consist of a `v` + numeric value."},{"id":"redis-stack-720-v11-june-2024","title":"Redis Stack 7.2.0-v11 (June 2024)","role":"content","text":"This is a maintenance release for Redis Stack Server 7.2.0.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\n[Docker](https://hub.docker.com/r/redis/redis-stack) | [Download](https://redis.io/downloads/#stack)"},{"id":"headlines-2","title":"Headlines:","role":"content","text":"Redis Stack 7.2.0-v11 introduces a new version of the **Redis** server with several bug fixes, and an updated **search and query** capability with numerous enhancements and bug fixes.\n\nThe updated **search and query** version features improved memory reporting that accounts for additional memory consumed by `TAG` and `TEXT` tries, as well as enhanced memory counting for inverted indexes.  Also, it includes additional fields in the `FT.INFO` command when used within a cluster.\n\nThis maintenance release also contains the latest version of **RedisInsight**."},{"id":"details-2","title":"Details:","role":"content","text":"**Improvements**\n* **Redis Search**:\n  * [#4595](https://github.com/RediSearch/RediSearch/pull/4595) - Report memory of the `TAG` and `TEXT` tries (MOD-5902)\n  * [#4669](https://github.com/RediSearch/RediSearch/pull/4669) - Inverted index memory counting (MOD-5977,MOD-5866)\n  * [#4687](https://github.com/RediSearch/RediSearch/pull/4687) - Add missing `FT.INFO` fields when used within a cluster (MOD-6920)\t\n\n**Bug Fixes**\n* **Redis**:\n  * [#12824](https://github.com/redis/redis/pull/12824) A single shard cluster leaves failed replicas in CLUSTER SLOTS instead of removing them\n  * [#12955](https://github.com/redis/redis/pull/12955) Crash in LSET command when replacing small items and exceeding 4GB\n  * [#13004](https://github.com/redis/redis/pull/13004) Blocking commands timeout is reset due to re-processing command\n  * [#13115](https://github.com/redis/redis/pull/13115) Conversion of numbers in Lua args to redis args can fail. Bug introduced in 7.2.0\n\n* **CLI tools**:\n  * [#13092](https://github.com/redis/redis/pull/13092) redis-cli: --count (for --scan, --bigkeys, etc) was ignored unless --pattern was also used\n  * [#12958](https://github.com/redis/redis/pull/12958) redis-check-aof: incorrectly considering data in manifest format as MP-AOF\n\n* **Redis Search**:\n  * [#4614](https://github.com/RediSearch/RediSearch/pull/4614) - Shards become unresponsive when using `FT.AGGREGATE` with `APPLY 'split(...)'`(MOD-6759)\n  * [#4556](https://github.com/RediSearch/RediSearch/pull/4556) - `FT.EXPLAIN` returns additional } when querying using wildcards (MOD-6768)\n  * [#4646](https://github.com/RediSearch/RediSearch/pull/4646) - `FT.DROPINDEX` with `DD` flag deleted keys in one AA cluster but not the others (MOD-1855)\n\n* **Redis version**:\n  * __[Redis 7.2.5](https://github.com/redis/redis/releases/tag/7.2.5)__\n\n**Module versions**\t\n* __[RediSearch 2.8.14](https://github.com/RediSearch/RediSearch/releases/tag/v2.8.14)__\n* __[RedisJSON 2.6.10](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.6.10)__\n* __[RedisTimeSeries 1.10.12](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.10.12)__\n* __[RedisBloom 2.6.12](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.6.12)__\n* __[RedisGears 2.0.23](https://github.com/RedisGears/RedisGears/releases)__\n\n**Recommended Client Libraries**\n* Java\n  * [Jedis 5.1.3 or later](https://github.com/redis/jedis/releases/tag/v5.1.3)\n  * [redis-om-spring 0.9.1 or later](https://github.com/redis/redis-om-spring/releases/tag/v0.9.1)\n* Python\n  * [redis-py 5.0.5 or later](https://github.com/redis/redis-py/releases/tag/v5.0.5)\n  * [redis-om-python 0.3.1 or later](https://github.com/redis/redis-om-python/releases/tag/v0.3.1)\n* NodeJS\n  * [node-redis 4.6.14 or later](https://github.com/redis/node-redis/releases/tag/redis%404.6.14)\n  * [redis-om-node 0.2.0 or later](https://github.com/redis/redis-om-node/releases/tag/v0.2.0)\n* .NET\n  * [redis-om-dotnet 0.7.1 or later](https://github.com/redis/redis-om-dotnet/releases/tag/v0.7.1)\n  * [NRedisStack 0.12.0 or later](https://github.com/redis/NRedisStack/releases/tag/v0.12.0)\n* Go\n  * [go-redis 9.5.2 or later](https://github.com/redis/go-redis/releases/tag/v9.5.2)\n  * [rueidis 1.0.38 or later](https://github.com/redis/rueidis/releases/tag/v1.0.38)\n\nCompatible with [Redis Insight](https://redis.io/download). The docker image redis/redis-stack for this version is bundled with [Redis Insight 2.50](https://github.com/RedisInsight/RedisInsight/releases/tag/2.50.0).\n\nNote: version numbers follow the pattern:\n\n`x.y.z-b`\n* `x.y` Redis major version\n* `z` increases with even numbers as a module x.y version increases.\n* `b` denotes a patch to Redis or a module (any `z` of Redis or modules). `b` will consist of a `v` + numeric value."},{"id":"redis-stack-720-v10-april-2024","title":"Redis Stack 7.2.0-v10 (April 2024)","role":"content","text":"This is a maintenance release for Redis Stack Server 7.2.0.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!"},{"id":"headlines-3","title":"Headlines:","role":"content","text":"This version contains the latest **search and query** capability with several improvements and bug fixes, including critical bug fixes. This release also includes the latest **JSON** data structure with a fix for a potential crash, the **time series** data structure with more detailed LibMR error messages, and the latest version of **triggers and functions** with updated v8 version and a bug fix. It also contains the latest version of **RedisInsight**."},{"id":"details-3","title":"Details:","role":"content","text":"**Improvements**\n* **Redis Search**:\n  * [#4502](https://github.com/RediSearch/RediSearch/pull/4502) Handle error properly when trying to execute Search commands on cluster setup as part of `MULTI`/`EXEC` or LUA script (MOD-6541)\n  * [#4526](https://github.com/RediSearch/RediSearch/pull/4526) Adding detailed geometry info on error messages (MOD-6701)\n\n* **Time series**:\n  * [#1593](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1593) More detailed LibMR error messages\n\n* **Triggers and Functions**:\n  * [#1100](https://github.com/RedisGears/RedisGears/pull/1100) Update v8 version to 12.3.219.15\n\n**Bug Fixes**\n* **Redis Search**:\n  * [#4481](https://github.com/RediSearch/RediSearch/pull/4481) Query syntax on `GEOSHAPE` accepting just prefix instead of complete predicate (MOD-6663)\n  * [#4513](https://github.com/RediSearch/RediSearch/pull/4513) `FT.CURSOR READ` in a numeric query causing a crash (MOD-6597) [**critical bug**]\n  * [#4534](https://github.com/RediSearch/RediSearch/pull/4534) `FT.PROFILE` with incorrect arguments could cause a crash on cluster setup (MOD-6791) [**critical bug**]\n  * [#4530](https://github.com/RediSearch/RediSearch/pull/4530) Some parameter settings using just prefixes instead of full values were working (MOD-6709)\n  * [#4539](https://github.com/RediSearch/RediSearch/pull/4539) Unfree memory while re-indexing loading a new RDB could cause a crash (MOD-6831, 6810) [**critical bug**]\n  * [#4498](https://github.com/RediSearch/RediSearch/pull/4498) Vector pre-filtered query (hybrid query) that timeout causing a crash due to deadlock when trying to write a new document(MOD-6510, MOD-6244) [**critical bug**]\n  * [#4495](https://github.com/RediSearch/RediSearch/pull/4495) `FT.SEARCH` accessing an inexistent memory address causes a crash if using the deprecated `FT.ADD` command (MOD-6599) [**critical bug**]\n\n* **JSON**:\n  * [#1192](https://github.com/RedisJSON/RedisJSON/pull/1192) Crashes with numeric values greater than i64::MAX (MOD-6501, MOD-4551, MOD-4856, MOD-5714)\n\n* **Triggers and Functions**:\n  * [#1093](https://github.com/RedisGears/RedisGears/pull/1093) Crash when logging NULL characters\n\n**Redis version**:\n  * __[Redis 7.2.4](https://github.com/redis/redis/releases/tag/7.2.4)__\n\n**Module versions**\t\n* __[RediSearch 2.8.13](https://github.com/RediSearch/RediSearch/releases/tag/v2.8.13)__\n* __[RedisJSON 2.6.10](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.6.10)__\n* __[RedisTimeSeries 1.10.12](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.10.12)__\n* __[RedisBloom 2.6.12](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.6.12)__\n* __[RedisGears 2.0.20](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.20-m21)__\n\n**Recommended Client Libraries**\n* Java\n  * [Jedis 5.1.2 or later](https://github.com/redis/jedis/releases/tag/v5.1.2)\n  * [redis-om-spring 0.8.9 or later](https://github.com/redis/redis-om-spring/releases/tag/v0.8.9)\n* Python\n  * [redis-py 5.0.3 or later](https://github.com/redis/redis-py/releases/tag/v5.0.3)\n  * [redis-om-python 0.2.2 or later](https://github.com/redis/redis-om-python/releases/tag/v0.2.2)\n* NodeJS\n  * [node-redis 4.6.13 or later](https://github.com/redis/node-redis/releases/tag/redis%404.6.13)\n  * [redis-om-node 0.2.0 or later](https://github.com/redis/redis-om-node/releases/tag/v0.2.0)\n* .NET\n  * [redis-om-dotnet 0.6.1 or later](https://github.com/redis/redis-om-dotnet/releases/tag/v0.6.1)\n  * [NRedisStack 0.12.0 or later](https://github.com/redis/NRedisStack/releases/tag/v0.12.0)\n* Go\n  * [go-redis 9.5.1 or later](https://github.com/redis/go-redis/releases/tag/v9.5.1)\n  * [rueidis 1.0.33 or later](https://github.com/redis/rueidis/releases/tag/v1.0.33)\n\nCompatible with [RedisInsight](https://redis.io/download). The docker image redis/redis-stack for this version is bundled with [RedisInsight 2.46](https://github.com/RedisInsight/RedisInsight/releases/tag/2.46.0).\n\nNote: version numbers follow the pattern:\n\n`x.y.z-b`\n* `x.y` Redis major version\n* `z` increases with even numbers as a module x.y version increases.\n* `b` denotes a patch to Redis or a module (any `z` of Redis or modules). `b` will consist of a `v` + numeric value."},{"id":"downloads","title":"Downloads","role":"content","text":"* macOS: [x86_64](https://packages.redis.io/redis-stack/redis-stack-server-7.2.0-v10.catalina.x86_64.zip), [arm64](https://packages.redis.io/redis-stack/redis-stack-server-7.2.0-v10.monterey.arm64.zip)\n* AppImage: [x86_64](https://packages.redis.io/redis-stack/redis-stack-server-7.2.0-v10-x86_64.AppImage)\n* Ubuntu: [Bionic x86_64](https://packages.redis.io/redis-stack/redis-stack-server-7.2.0-v10.bionic.x86_64.tar.gz), [Bionic arm64](https://packages.redis.io/redis-stack/redis-stack-server-7.2.0-v10.bionic.arm64.tar.gz), [Focal x86_64](https://packages.redis.io/redis-stack/redis-stack-server-7.2.0-v10.focal.x86_64.tar.gz), [Focal arm64](https://packages.redis.io/redis-stack/redis-stack-server-7.2.0-v10.focal.arm64.tar.gz), [Snap x86_64](https://packages.redis.io/redis-stack/redis-stack-server-7.2.0-v10.x86_64.snap), [Snap arm64](https://packages.redis.io/redis-stack/redis-stack-server-7.2.0-v10.arm64.snap), [Jammy x86_64](https://packages.redis.io/redis-stack/redis-stack-server-7.2.0-v10.jammy.x86_64.tar.gz), [Jammy arm64](https://packages.redis.io/redis-stack/redis-stack-server-7.2.0-v10.jammy.arm64.tar.gz)\n* Debian: [Bullseye x86_64](https://packages.redis.io/redis-stack/redis-stack-server-7.2.0-v10.bullseye.x86_64.tar.gz)\n* RHEL 7/CentOS Linux 7: [x86_64](https://packages.redis.io/redis-stack/redis-stack-server-7.2.0-v10.rhel7.x86_64.tar.gz)\n* RHEL 8/CentOS Linux 8: [x86_64](https://packages.redis.io/redis-stack/redis-stack-server-7.2.0-v10.rhel8.x86_64.tar.gz)\n* RHEL 9/Rocky Linux 9/CentOS Linux 9: [x86_64](https://packages.redis.io/redis-stack/redis-stack-server-7.2.0-v10.rhel9.x86_64.tar.gz)\n* Redis Stack on [Dockerhub](https://hub.docker.com/u/redis): [x86_64 and arm64](https://hub.docker.com/r/redis/redis-stack)\n* Redis Stack server on [Dockerhub](https://hub.docker.com/u/redis): [x86_64 and arm64](https://hub.docker.com/r/redis/redis-stack-server)"},{"id":"redis-stack-720-v9-march-2024","title":"Redis Stack 7.2.0-v9 (March 2024)","role":"content","text":"This is a maintenance release for Redis Stack Server 7.2.0.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent."},{"id":"headlines-4","title":"Headlines:","role":"content","text":"This version contains the latest **search and query** capability with an improvement for memory allocation patterns on the memory used to query `GEOSHAPE` types, and several bug fixes. This release also includes the latest **probabilistic** data structures with a fix for a potential crash, and **triggers and functions** capability with an updated v8 version. It also contains the latest version of **RedisInsight**."},{"id":"details-4","title":"Details:","role":"content","text":"**Improvements**\n* **Redis Search**:\n  * [#4313](https://github.com/RediSearch/RediSearch/pull/4313) Memory allocation patterns on the memory used to query `GEOSHAPE` types (MOD 6431)\n\n**Bug Fixes**\n* **Redis Search**:\n  * [#4476](https://github.com/RediSearch/RediSearch/pull/4476) Split `INFIX` and `SUFFIX` report on `FT.EXPLAIN` and `FT.EXPLAINCLI` (MOD-6186)\n  * [#4467](https://github.com/RediSearch/RediSearch/pull/4467) Memory leak upon suffix query for a `TAG` indexed with `WITHSUFFIXTRIE` (MOD-6644)\n  * [#4403](https://github.com/RediSearch/RediSearch/pull/4403) Clustered `FT.SEARCH` hangs forever without replying when an invalid topology is found (MOD-6557)\n  * [#4355](https://github.com/RediSearch/RediSearch/pull/4355) Searching for a synonym will iterate in the same group multiple times, causing a performance hit (MOD-6490)\n\n* **Probabilistic data structures**:\n  * [#753](https://github.com/RedisBloom/RedisBloom/issues/753) Potential crash on `CMS.MERGE` when using invalid arguments\n\n* **Triggers and Functions**:\n  * [#1089](https://github.com/RedisGears/RedisGears/pull/1089) Update v8 version to 12.2.281.21\n\n* **Redis version**:\n  * __[Redis 7.2.4](https://github.com/redis/redis/releases/tag/7.2.4)__\n\n**Module versions**\t\n* __[RediSearch 2.8.12](https://github.com/RediSearch/RediSearch/releases/tag/v2.8.12)__\n* __[RedisJSON 2.6.9](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.6.9)__\n* __[RedisTimeSeries 1.10.11](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.10.11)__\n* __[RedisBloom 2.6.12](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.6.12)__\n* __[RedisGears 2.0.19](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.19-m20)__\n\n**Recommended Client Libraries**\n* Java\n  * [Jedis 5.1.1 or greater](https://github.com/redis/jedis/releases/tag/v5.1.1)\n  * [redis-om-spring 0.8.8 or greater](https://github.com/redis/redis-om-spring/releases/tag/v0.8.8)\n* Python\n  * [redis-py 5.0.2 or greater](https://github.com/redis/redis-py/releases/tag/v5.0.2)\n  * [redis-om-python 0.2.1 or greater](https://github.com/redis/redis-om-python/releases/tag/v0.2.1)\n* NodeJS\n  * [node-redis 4.6.13 or greater](https://github.com/redis/node-redis/releases/tag/redis%404.6.13)\n  * [redis-om-node 0.2.0 or greater](https://github.com/redis/redis-om-node/releases/tag/v0.2.0)\n* .NET\n  * [redis-om-dotnet 0.6.1 or greater](https://github.com/redis/redis-om-dotnet/releases/tag/v0.6.1)\n  * [NRedisStack 0.11.0 or greater](https://github.com/redis/NRedisStack/releases/tag/v0.11.0)\n* Go\n  * [go-redis 9.5.1 or greater](https://github.com/redis/go-redis/releases/tag/v9.5.1)\n  * [rueidis 1.0.31 or greater](https://github.com/redis/rueidis/releases/tag/v1.0.31)\n\nCompatible with [RedisInsight](https://redis.io/download). The docker image redis/redis-stack for this version is bundled with [RedisInsight 2.44](https://github.com/RedisInsight/RedisInsight/releases/tag/2.44.0).\n\nNote: version numbers follow the pattern:\n\n`x.y.z-b`\n* `x.y` Redis major version\n* `z` increases with even numbers as a module x.y version increases.\n* `b` denotes a patch to Redis or a module (any `z` of Redis or modules). `b` will consist of a `v` + numeric value."},{"id":"redis-stack-720-v8-january-2024","title":"Redis Stack 7.2.0-v8 (January 2024)","role":"content","text":"This is a maintenance release for Redis Stack Server 7.2.0.\n\nUpdate urgency: `HIGH`: There is a critical bug fixed in the probabilistic data structures that may affect a subset of users. Upgrade!"},{"id":"headlines-5","title":"Headlines:","role":"content","text":"This version contains the latest **probabilistic** data structures with a critical bug fix, as well as the latest **JSON** data structure with a breaking change to revert JSONPath default path value from `$` to `.` under RESP3, and a fix for RediSearch deadlock. This version contains the latest version of **RedisInsight**."},{"id":"details-5","title":"Details:","role":"content","text":"**Improvements**\n* **JSON**:\n  * [#1131](https://github.com/RedisJSON/RedisJSON/issues/1131), [#1143](https://github.com/RedisJSON/RedisJSON/pull/1143) BREAKING - Revert JSONPath default path value from `$` to `.` under RESP3 (MOD-6156)\n\n**Bug Fixes**\n* **JSON**:\n  * [#1095](https://github.com/RedisJSON/RedisJSON/pull/1095) Fix for RediSearch deadlock. See RediSearch 2.8.10 release notes (MOD-5895)\n\n* **Probabilistic data structures**:\n  * [#727](https://github.com/RedisBloom/RedisBloom/pull/727) Potential crash on CF.LOADCHUNK (MOD-6344) - Additional fixes\n\n* **Redis version**:\n  * __[Redis 7.2.4](https://github.com/redis/redis/releases/tag/7.2.4)__\n\n**Module versions**\t\n* __[RediSearch 2.8.11](https://github.com/RediSearch/RediSearch/releases/tag/v2.8.11)__\n* __[RedisJSON 2.6.9](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.6.9)__\n* __[RedisTimeSeries 1.10.11](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.10.11)__\n* __[RedisBloom 2.6.11](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.6.11)__\n* __[RedisGears 2.0.16](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.16-m17)__\n\n**Recommended Client Libraries**\n* Java\n  * [Jedis 5.1.0 or greater](https://github.com/redis/jedis/releases/tag/v5.1.0)\n  * [redis-om-spring 0.8.8 or greater](https://github.com/redis/redis-om-spring/releases/tag/v0.8.8)\n* Python\n  * [redis-py 5.0.1 or greater](https://github.com/redis/redis-py/releases/tag/v5.0.1)\n  * [redis-om-python 0.2.1 or greater](https://github.com/redis/redis-om-python/releases/tag/v0.2.1)\n* NodeJS\n  * [node-redis 4.6.12 or greater](https://github.com/redis/node-redis/releases/tag/redis%404.6.12)\n  * [redis-om-node 0.2.0 or greater](https://github.com/redis/redis-om-node/releases/tag/v0.2.0)\n* .NET\n  * [redis-om-dotnet 0.6.1 or greater](https://github.com/redis/redis-om-dotnet/releases/tag/v0.6.1)\n  * [NRedisStack 0.11.0 or greater](https://github.com/redis/NRedisStack/releases/tag/v0.11.0)\n* Go\n  * [go-redis 9.4.0 or greater](https://github.com/redis/go-redis/releases/tag/v9.4.0)\n  * [rueidis 1.0.27 or greater](https://github.com/redis/rueidis/releases/tag/v1.0.27)\n\nCompatible with [RedisInsight](https://redis.io/download). The docker image redis/redis-stack for this version is bundled with [RedisInsight 2.40](https://github.com/RedisInsight/RedisInsight/releases/tag/2.40.0).\n\nNote: version numbers follow the pattern:\n\n`x.y.z-b`\n* `x.y` Redis major version\n* `z` increases with even numbers as a module x.y version increases.\n* `b` denotes a patch to Redis or a module (any `z` of Redis or modules). `b` will consist of a `v` + numeric value."},{"id":"redis-stack-720-v7-january-2024","title":"Redis Stack 7.2.0-v7 (January 2024)","role":"content","text":"This is a maintenance release for Redis Stack Server 7.2.0.\n\nUpdate urgency: `SECURITY`:  there are security fixes in the release."},{"id":"headlines-6","title":"Headlines:","role":"content","text":"This version contains security fixes for the **Redis** server to properly handle the resizing of memory buffers, as well as security fixes for **probabilistic data structures** to avoid potential crashes. The new Redis Stack version also contains several improvements and bug fixes for the **Redis Search** capability, including fixes of critical bugs. It also offers the latest **time series data structure**, the latest **Triggers and Functions** capability with various improvements and bug fixes, and it contains the latest version of **RedisInsight**."},{"id":"details-6","title":"Details:","role":"content","text":"**Security and privacy**\n* **Redis**:\n  * (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution\n\n* **Probabilistic data structures**:\n  * [#721](https://github.com/RedisBloom/RedisBloom/issues/721) Potential crash on `CF.RESERVE` (MOD-6343)\n  * [#722](https://github.com/RedisBloom/RedisBloom/issues/722) Potential crash on `CF.LOADCHUNK` (MOD-6344)\n\n **Improvements**\n* **Redis Search**:\n  * [#3682](https://github.com/RediSearch/RediSearch/pull/3682) Report last key error and field type indexing failures on `FT.INFO` (MOD-5364)\n  * [#4236](https://github.com/RediSearch/RediSearch/pull/4236) Adding Vector index parameters at the `FT.INFO` report (MOD-6198)\n  * [#4196](https://github.com/RediSearch/RediSearch/pull/4196) Check for timeout after results processing in `FT.SEARCH` on cluster setup (MOD-6278)\n  * [#4164](https://github.com/RediSearch/RediSearch/pull/4164) Report `TIMEOUT`, `MAXPREFIXEXPANSION` warnings in RESP3 replies (MOD-6234)\n  * [#4165](https://github.com/RediSearch/RediSearch/pull/4165) Indicate timeout on `FT.PROFILE` report (MOD-6184)\n  * [#4149](https://github.com/RediSearch/RediSearch/pull/4149) Indicate timeout from Cursor on `FAIL` timeout policy (MOD-5990)\n  * [#4147](https://github.com/RediSearch/RediSearch/pull/4147) Initialization of the maximum numeric value range leading to a better balance of the index leaf splitting (MOD-6232)\n  * [#3940](https://github.com/RediSearch/RediSearch/pull/3940) Query optimisation when predicate contains multiple `INTERSECTION` (AND) of `UNION` (OR) (MOD-5910)\n  * [#4059](https://github.com/RediSearch/RediSearch/pull/4059) Return cursor id when experiencing a timeout, when the policy is `ON_TIMEOUT RETURN` (MOD-5966)\n  * [#4006](https://github.com/RediSearch/RediSearch/pull/4006) Possibly problematic index name alias validation (MOD-5945)\n  * [#4264](https://github.com/RediSearch/RediSearch/pull/4264) Granularity of the time reporting counters on `FT.PROFILE` (MOD-6002)\n\n* **Triggers and Functions**:\n  * Update v8 version to 12.0/267.13\n\n**Bug Fixes**\n* **Redis**:\n  * [#12805](https://github.com/redis/redis/pull/12805), [#12832](https://github.com/redis/redis/pull/12832) Fix crashes of cluster commands clusters with mixed versions of 7.0 and 7.2\n  * [#12564](https://github.com/redis/redis/pull/12564) Fix slot ownership not being properly handled when deleting a slot from a node\n  * [#12733](https://github.com/redis/redis/pull/12733) Fix atomicity issues with the RedisModuleEvent_Key module API event\n  * [#4324](https://github.com/RediSearch/RediSearch/pull/4324) Internal cluster mechanism not waiting until all replies from shards causing a crash (MOD-6287)\n  * [#4297](https://github.com/RediSearch/RediSearch/pull/4297) Execution loader when using `FT.AGGREGATE` with `LOAD` stage failing to buffer the right results potentially causing a crash (MOD-6385)\n\n* **Redis Search**:\n  * [#4287](https://github.com/RediSearch/RediSearch/pull/4287) Re-index process while syncing from the replica causes a crash due to internal index variable initialisation\n(MOD-6337, MOD-6336)\n  * [#4249](https://github.com/RediSearch/RediSearch/pull/4249) Memory tracking on cluster setups causing high memory usage and potentially Out-of-Memory (MOD-6123, MOD-5639)\n  * [#4244](https://github.com/RediSearch/RediSearch/pull/4244) Profiling `FT.AGGREGATE` using the `WITHCURSOR` flag with - clause causes a crash due to timeout (MOD-5512)\n  * [#3916](https://github.com/RediSearch/RediSearch/pull/3916) Expiring `JSON` documents while querying it causing a crash due to deadlock (MOD-5769, MOD-5895, MOD-6189, MOD-5895)\n  * [#4235](https://github.com/RediSearch/RediSearch/pull/4235) Memory excessively growing on databases caused by unbalanced nodes on inverted index trie (MOD-5880, MOD-5952, MOD-6003)\n  * [#4190](https://github.com/RediSearch/RediSearch/pull/4190) Profiling `FT.AGGREGATE` causes a crash on RESP3 replies (MOD-6250, MOD-6295)\n  * [#4148](https://github.com/RediSearch/RediSearch/pull/4148), [#4038](https://github.com/RediSearch/RediSearch/pull/4038) `ON_TIMEOUT FAIL\\RETURN` policies in the cluster setup not being respected (MOD-6035, MOD-5948, MOD-6090)\n  * [#4110](https://github.com/RediSearch/RediSearch/pull/4110) Format of error response contains inconsistencies when timing out(MOD-6011, MOD-5965)\n  * [#4104](https://github.com/RediSearch/RediSearch/pull/4104) `FT.SEARCH` not responding when using TLS encryption on Amazon Linux 2 (MOD-6012)\n  * [#4009](https://github.com/RediSearch/RediSearch/pull/4009) In cluster setup does not return a timeout error for `FT.SEARCH` (MOD-5911)\n  * [#3920](https://github.com/RediSearch/RediSearch/pull/3920) In cluster setup does not return a timeout error for `FT.AGGREGATE` (MOD-5209)\n  * [#3914](https://github.com/RediSearch/RediSearch/pull/3914) `FT.CURSOR READ` with geo queries causing a crash when data is updated between the cursor reads (MOD-5646)\n  * [#4220](https://github.com/RediSearch/RediSearch/pull/4220) Server crash when attempting to run the ForkGC (Garbage Collection routine) after dropping the index (MOD-6276)\n\n* **Time series**:\n  * [LibMR#51](https://github.com/RedisGears/LibMR/pull/51) Crash on SSL initialization failure (MOD-5647)\n  * [#1538](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1538) Amazon Linux 2: crash on SSL initialization. We now use openssl11-devel instead of openssl-devel (MOD-6015)\n\n* **Probabilistic data structures**:\n  * [#707](https://github.com/RedisBloom/RedisBloom/pull/707) Top-K: `TOPK.ADD` and `TOPK.QUERY` crash when an item name is an empty string (RED-114676)\n\n* **Triggers and Functions**:\n  * [#1069](https://github.com/RedisGears/RedisGears/pull/1069) Fixed an issue where a keyspace notification could be lost\n\n* **Redis version**:\n  * __[Redis 7.2.4](https://github.com/redis/redis/releases/tag/7.2.4)__\n\n**Module versions**\t\n* __[RediSearch 2.8.11](https://github.com/RediSearch/RediSearch/releases/tag/v2.8.11)__\n* __[RedisJSON 2.6.7](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.6.7)__\n* __[RedisTimeSeries 1.10.11](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.10.11)__\n* __[RedisBloom 2.6.10](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.6.10)__\n* __[RedisGears 2.0.16](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.16-m17)__\n\n**Recommended Client Libraries**\n* Java\n  * [Jedis 5.1.0 or greater](https://github.com/redis/jedis/releases/tag/v5.1.0)\n  * [redis-om-spring 0.8.7 or greater](https://github.com/redis/redis-om-spring/releases/tag/v0.8.7)\n* Python\n  * [redis-py 5.0.1 or greater](https://github.com/redis/redis-py/releases/tag/v5.0.1)\n  * [redis-om-python 0.2.1 or greater](https://github.com/redis/redis-om-python/releases/tag/v0.2.1)\n* NodeJS\n  * [node-redis 4.6.12 or greater](https://github.com/redis/node-redis/releases/tag/redis%404.6.12)\n  * [redis-om-node 0.2.0 or greater](https://github.com/redis/redis-om-node/releases/tag/v0.2.0)\n* .NET\n  * [redis-om-dotnet 0.6.1 or greater](https://github.com/redis/redis-om-dotnet/releases/tag/v0.6.1)\n  * [NRedisStack 0.11.0 or greater](https://github.com/redis/NRedisStack/releases/tag/v0.11.0)\n* Go\n  * [go-redis 9.4.0 or greater](https://github.com/redis/go-redis/releases/tag/v9.4.0)\n  * [rueidis 1.0.26 or greater](https://github.com/redis/rueidis/releases/tag/v1.0.26)\n\nCompatible with [RedisInsight](https://redis.io/download). The docker image redis/redis-stack for this version is bundled with [RedisInsight 2.40](https://github.com/RedisInsight/RedisInsight/releases/tag/2.40.0).\n\nNote: version numbers follow the pattern:\n\n`x.y.z-b`\n* `x.y` Redis major version\n* `z` increases with even numbers as a module x.y version increases.\n* `b` denotes a patch to Redis or a module (any `z` of Redis or modules). `b` will consist of a `v` + numeric value."},{"id":"redis-stack-720-v5-october-2023","title":"Redis Stack 7.2.0-v5 (October 2023)","role":"content","text":"This is a maintenance release for Redis Stack Server 7.2.0.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!"},{"id":"headlines-7","title":"Headlines:","role":"content","text":"This version contains the latest Search and Query with various improvements and fixes for critical bugs, triggers and functions with an updated v8 version, as well as new JSON, time series, and probabilistic data structures with several improvements. The new version introduces support for RHEL 9 and Rocky Linux 9. It also includes the latest version of RedisInsight."},{"id":"details-7","title":"Details:","role":"content","text":"**Improvements**\n* **Search and Query**:\n  * [#3938](https://github.com/RediSearch/RediSearch/pull/3938) Propagating error messages in a multi-shard database, instead of failing silently (MOD-5211)\n\n* **JSON**:\n  * [#1102](https://github.com/RedisJSON/RedisJSON/pull/1102) Added support for CBL-Mariner 2\n\n* **Time series**:\n  * [#1516](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1516) Added support for CBL-Mariner 2\n\n* **Probabilistic data structures**:\n  * [#684](https://github.com/RedisBloom/RedisBloom/pull/684), [#685](https://github.com/RedisBloom/RedisBloom/pull/685) Added support for CBL-Mariner 2\n\n**Bug Fixes**\n* **Redis**:\n  * Fix for the \"The RDB file contains AUX module data I can't load: no matching module 'graphdata'\" [error message](https://github.com/redis/redis/issues/12490) when upgrading Redis Stack\n\n* **Search and Query**:\n  * [#3874](https://github.com/RediSearch/RediSearch/pull/3874) Heavy document updates cause memory growth once memory blocks aren't properly released (MOD-5181)\n  * [#3967](https://github.com/RediSearch/RediSearch/pull/3967) Resharding optimizations cause the process to get stuck (MOD-5874, MOD-5864)\n  * [#3892](https://github.com/RediSearch/RediSearch/pull/3892) After cleaning the index the GC could cause corruption on unique values (MOD-5815)\n  * [#3853](https://github.com/RediSearch/RediSearch/pull/3853) Queries with `WITHCURSOR` making memory growth since `CURSOR` wasn't invalidated in the shards (MOD-5580)\n\n* **Triggers and Functions**:\n  * Update v8 version to 11.8.172.15\n\n**Redis version**\n* __[Redis 7.2.2](https://github.com/redis/redis/releases/tag/7.2.2)__\n\n**Module versions**\t\n* __[RediSearch 2.8.9](https://github.com/RediSearch/RediSearch/releases/tag/v2.8.9)__\n* __[RedisJSON 2.6.7](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.6.7)__\n* __[RedisTimeSeries 1.10.9](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.10.9)__\n* __[RedisBloom 2.6.8](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.6.8)__\n* __[RedisGears 2.0.14](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.14-m15)__\n\n**Recommended Client Libraries**\n* Java\n  * [Jedis 5.0.2 or greater](https://github.com/redis/jedis/releases/tag/v5.0.2)\n  * [redis-om-spring 0.8.7 or greater](https://github.com/redis/redis-om-spring/releases/tag/v0.8.7)\n* Python\n  * [redis-py 5.0.1 or greater ](https://github.com/redis/redis-py/releases/tag/v5.0.1)\n  * [redis-om-python 0.2.1 or greater](https://github.com/redis/redis-om-python/releases/tag/v0.2.1)\n* NodeJS\n  * [node-redis 4.6.10 or greater](https://github.com/redis/node-redis/releases/tag/redis%404.6.10)\n  * [redis-om-node 0.2.0 or greater](https://github.com/redis/redis-om-node/releases/tag/v0.2.0)\n* .NET\n  * [redis-om-dotnet 0.5.3 or greater](https://github.com/redis/redis-om-dotnet/releases/tag/v0.5.3)\n  * [NRedisStack 0.10.1 or greater](https://github.com/redis/NRedisStack/releases/tag/v0.10.1)\n* Go\n  * [go-redis 9.2.1 or greater](https://github.com/redis/go-redis/releases/tag/v9.2.1)\n  * [rueidis 1.0.20 or greater](https://github.com/redis/rueidis/releases/tag/v1.0.20)\n\nCompatible with [RedisInsight](https://redis.io/download). The docker image redis/redis-stack for this version is bundled with [RedisInsight 2.36](https://github.com/RedisInsight/RedisInsight/releases/tag/2.36.0).\n\nNote: version numbers follow the pattern:\n\n`x.y.z-b`\n* `x.y` Redis major version\n* `z` increases with even numbers as a module x.y version increases.\n* `b` denotes a patch to Redis or a module (any `z` of Redis or modules). `b` will consist of a `v` + numeric value."},{"id":"redis-stack-720-v4-october-2023","title":"Redis Stack 7.2.0-v4 (October 2023)","role":"content","text":"This is a maintenance release for Redis Stack Server 7.2.0.\n\nUpdate urgency: `SECURITY`: There are security fixes in the release."},{"id":"headlines-8","title":"Headlines:","role":"content","text":"This version contains a security fix for the Redis server to avoid bypassing desired Unix socket permissions on startup, as well as several improvements and bug fixes."},{"id":"details-8","title":"Details:","role":"content","text":"**Security and privacy**\n* **Redis**:\n  * (CVE-2023-45145) The wrong order of `listen(2)` and `chmod(2)` calls creates a\nrace condition that can be used by another process to bypass desired Unix\nsocket permissions on startup.\n\n **Improvements**\n* **Redis**:\n  * [#12611](https://github.com/redis/redis/pull/12611) Fix compilation error on MacOS 13\n  * [#12604](https://github.com/redis/redis/pull/12604) Fix crash when running rebalance command in a mixed cluster of 7.0 and 7.2\nNodes\n  * [#12561](https://github.com/redis/redis/pull/12561) Fix the return type of the slot number in cluster shards to integer, which\nmakes it consistent with past behavior\n  * [#12569](https://github.com/redis/redis/pull/12569) Fix CLUSTER commands are called from modules or scripts to return TLS info\nappropriately\n * [#12571](https://github.com/redis/redis/pull/12571) redis-cli, fix crash on reconnect when in SUBSCRIBE mode\n * [#12474](https://github.com/redis/redis/pull/12474) Fix overflow calculation for next timer event\n\n**Bug Fixes**\n* **Redis**:\n * [#12620](https://github.com/redis/redis/pull/12620) WAITAOF could timeout in the absence of write traffic in case a new AOF is\ncreated and an AOF rewrite can't immediately start\n\n**Redis version**\n* __[Redis 7.2.2](https://github.com/redis/redis/releases/tag/7.2.2)__\n\n**Module versions**\t\n* __[RediSearch 2.8.8](https://github.com/RediSearch/RediSearch/releases/tag/v2.8.8)__\n* __[RedisJSON 2.6.6](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.6.6)__\n* __[RedisTimeSeries 1.10.6](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.10.6)__\n* __[RedisBloom 2.6.3](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.6.3)__\n* __[RedisGears 2.0.13](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.13-m14)__\n\n**Recommended Client Libraries**\n* Java\n  * [Jedis 5.0.1 or greater](https://github.com/redis/jedis/releases/tag/v5.0.1)\n  * [redis-om-spring 0.8.7 or greater](https://github.com/redis/redis-om-spring/releases/tag/v0.8.7)\n* Python\n  * [redis-py 5.0.1 or greater ](https://github.com/redis/redis-py/releases/tag/v5.0.1)\n  * [redis-om-python 0.2.1 or greater](https://github.com/redis/redis-om-python/releases/tag/v0.2.1)\n* NodeJS\n  * [node-redis 4.6.10 or greater](https://github.com/redis/node-redis/releases/tag/redis%404.6.10)\n  * [redis-om-node 0.2.0 or greater](https://github.com/redis/redis-om-node/releases/tag/v0.2.0)\n* .NET\n  * [redis-om-dotnet 0.5.3 or greater](https://github.com/redis/redis-om-dotnet/releases/tag/v0.5.3)\n  * [NRedisStack 0.9.0 or greater](https://github.com/redis/NRedisStack/releases/tag/v0.9.0)\n* Go\n  * [go-redis 9.2.1 or greater](https://github.com/redis/go-redis/releases/tag/v9.2.1)\n  * [rueidis 1.0.20 or greater](https://github.com/redis/rueidis/releases/tag/v1.0.20)\n\nCompatible with [RedisInsight](https://redis.io/download). The docker image redis/redis-stack for this version is bundled with [RedisInsight 2.34](https://github.com/RedisInsight/RedisInsight/releases/tag/2.34.0).\n\nNote: version numbers follow the pattern:\n\n`x.y.z-b`\n* `x.y` Redis major version\n* `z` increases with even numbers as a module x.y version increases.\n* `b` denotes a patch to Redis or a module (any `z` of Redis or modules). `b` will consist of a `v` + numeric value."},{"id":"redis-stack-720-v3-october-2023","title":"Redis Stack 7.2.0-v3 (October 2023)","role":"content","text":"This is a maintenance release for Redis Stack Server 7.2.0.\n\nUpdate urgency: `SECURITY`: There are security fixes in the release."},{"id":"headlines-9","title":"Headlines:","role":"content","text":"This version contains the latest Search and Query capability v. 2.8.8, and triggers and functions v. 2.0.13 with various improvements, security fixes for Search and Query, and several bug fixes. It also includes the latest version of RedisInsight."},{"id":"details-9","title":"Details:","role":"content","text":"**Security and privacy**\n* **Search and Query**:\n  * [#3788](https://github.com/RediSearch/RediSearch/pull/3788) Don’t expose internal cluster commands\n  * [#3844](https://github.com/RediSearch/RediSearch/pull/3844) Limits maximum phonetic length avoiding to be exploited\n\n **Improvements**\n* **Search and Query**:\n  * [#3534](https://github.com/RediSearch/RediSearch/pull/3534) - Vector Similarity [0.7.1](https://github.com/RedisAI/VectorSimilarity/releases/tag/v0.7.1)\n\n**Bug Fixes**\n* **Search and Query**:\n  * [#3771](https://github.com/RediSearch/RediSearch/pull/3771)  Broken `lower()` and `upper()` functions on `APPLY` stage in `FT.AGGREGATE` in `DIALECT 3`\n  * [#3752](https://github.com/RediSearch/RediSearch/pull/3752) Setting a low `MAXIDLE` parameter value in `FT.AGGREGATE` causes a crash\n  * [#3780](https://github.com/RediSearch/RediSearch/pull/3780) Wrong document length calculation causing incorrect score values\n  * [#3808](https://github.com/RediSearch/RediSearch/pull/3808) `LOAD` step after a `FILTER` step could cause a crash on `FT.AGGREGATE`\n  * [#3823](https://github.com/RediSearch/RediSearch/pull/3823) `APPLY` or `FILTER` parser leak\n  * [#3837](https://github.com/RediSearch/RediSearch/pull/3837) Connection using TLS fails on Redis 7.2\n  * [#3856](https://github.com/RediSearch/RediSearch/pull/3856) Adding new nodes to OSS cluster causing a crash\n  * [#3854](https://github.com/RediSearch/RediSearch/pull/3854) Vector range query could cause Out-of-Memory due a memory corruption\n\n* **Triggers and Functions**:\n  * Update v8 version to 11.7.439.17.\n\n**Redis version**\n* __[Redis 7.2.1](https://github.com/redis/redis/releases/tag/7.2.1)__\n\n**Module versions**\t\n* __[RediSearch 2.8.8](https://github.com/RediSearch/RediSearch/releases/tag/v2.8.8)__\n* __[RedisJSON 2.6.6](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.6.6)__\n* __[RedisTimeSeries 1.10.6](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.10.6)__\n* __[RedisBloom 2.6.3](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.6.3)__\n* __[RedisGears 2.0.13](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.13-m14)__\n\n**Recommended Client Libraries**\n* Java\n  * [Jedis 5.0.1 or greater](https://github.com/redis/jedis/releases/tag/v5.0.1)\n  * [redis-om-spring 0.8.7 or greater](https://github.com/redis/redis-om-spring/releases/tag/v0.8.7)\n* Python\n  * [redis-py 5.0.1 or greater ](https://github.com/redis/redis-py/releases/tag/v5.0.1)\n  * [redis-om-python 0.2.1 or greater](https://github.com/redis/redis-om-python/releases/tag/v0.2.1)\n* NodeJS\n  * [node-redis 4.6.10 or greater](https://github.com/redis/node-redis/releases/tag/redis%404.6.10)\n  * [redis-om-node 0.2.0 or greater](https://github.com/redis/redis-om-node/releases/tag/v0.2.0)\n* .NET\n  * [redis-om-dotnet 0.5.3 or greater](https://github.com/redis/redis-om-dotnet/releases/tag/v0.5.3)\n  * [NRedisStack 0.9.0 or greater](https://github.com/redis/NRedisStack/releases/tag/v0.9.0)\n* Go\n  * [go-redis 9.2.1 or greater](https://github.com/redis/go-redis/releases/tag/v9.2.1)\n  * [rueidis 1.0.19 or greater](https://github.com/redis/rueidis/releases/tag/v1.0.19)\n\nCompatible with [RedisInsight](https://redis.io/download). The docker image redis/redis-stack for this version is bundled with [RedisInsight 2.34](https://github.com/RedisInsight/RedisInsight/releases/tag/2.34.0).\n\nNote: version numbers follow the pattern:\n\n`x.y.z-b`\n* `x.y` Redis major version\n* `z` increases with even numbers as a module x.y version increases.\n* `b` denotes a patch to Redis or a module (any `z` of Redis or modules). `b` will consist of a `v` + numeric value."},{"id":"redis-stack-720-v2-september-2023","title":"Redis Stack 7.2.0-v2 (September 2023)","role":"content","text":"This is a maintenance release for Redis Stack Server 7.2.0.\n\nUpdate urgency: `SECURITY`: There are security fixes in the release."},{"id":"headlines-10","title":"Headlines:","role":"content","text":"This version contains security improvements for time series to not expose internal commands and several bug fixes for triggers and functions."},{"id":"details-10","title":"Details:","role":"content","text":"**Security and privacy**\n\n* **Time series**:\n  * [#1506](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1506) Don’t expose internal commands\n\n**Bug Fixes**\n\n* **Triggers and Functions**:\n  * [#1023](https://github.com/RedisGears/RedisGears/pull/1023) Add TCONFIG_GET and redisgears_2.FORCESHARDSCONNECTION to exclude commands.\n  * [#1008](https://github.com/RedisGears/RedisGears/pull/1008) and [#1020](https://github.com/RedisGears/RedisGears/pull/1020) Fix when the data is read when a StreamTrigger is fired.\n  * [#1010](https://github.com/RedisGears/RedisGears/pull/1010) Apply loading timeout on AOF and replication stream.\n  * Minor patch update for the V8 JavaScript engine.\n\n**Redis version**\n* __[Redis 7.2.1](https://github.com/redis/redis/releases/tag/7.2.1)__\n\n**Module versions**\t\n* __[RediSearch 2.8.4](https://github.com/RediSearch/RediSearch/releases/tag/v2.8.4)__\n* __[RedisJSON 2.6.6](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.6.6)__\n* __[RedisTimeSeries 1.10.6](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.10.6)__\n* __[RedisBloom 2.6.3](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.6.3)__\n* __[RedisGears 2.0.12](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.12-m13)__\n\n**Recommended Client Libraries**\n* Java\n  * [Jedis 5.0.0 or greater](https://github.com/redis/jedis/releases/tag/v5.0.0)\n  * [redis-om-spring 0.8.7 or greater](https://github.com/redis/redis-om-spring/releases/tag/v0.8.7)\n* Python\n  * [redis-py 5.0.0 or greater ](https://github.com/redis/redis-py/releases/tag/v5.0.0)\n  * [redis-om-python 0.2.1 or greater](https://github.com/redis/redis-om-python/releases/tag/v0.2.1)\n* NodeJS\n  * [node-redis 4.6.8 or greater](https://github.com/redis/node-redis/releases/tag/redis%404.6.8)\n  * [redis-om-node 0.2.0 or greater](https://github.com/redis/redis-om-node/releases/tag/v0.2.0)\n* .NET\n  * [redis-om-dotnet 0.5.3 or greater](https://github.com/redis/redis-om-dotnet/releases/tag/v0.5.3)\n  * [NRedisStack 0.9.0 or greater](https://github.com/redis/NRedisStack/releases/tag/v0.9.0)\n* Go\n  * [go-redis 9.1.0 or greater](https://github.com/redis/go-redis/releases/tag/v9.1.0)\n  * [rueidis 1.0.17 or greater](https://github.com/redis/rueidis/releases/tag/v1.0.17)\n\nCompatible with [RedisInsight](https://redis.io/download). The docker image redis/redis-stack for this version is bundled with [RedisInsight 2.32](https://github.com/RedisInsight/RedisInsight/releases/tag/2.32).\n\nNote: version numbers follow the pattern:\n\n`x.y.z-b`\n* `x.y` Redis major version\n* `z` increases with even numbers as a module x.y version increases.\n* `b` denotes a patch to Redis or a module (any `z` of Redis or modules). `b` will consist of a `v` + numeric value."},{"id":"redis-stack-720-v1-september-2023","title":"Redis Stack 7.2.0-v1 (September 2023)","role":"content","text":"This is a maintenance release for Redis Stack Server 7.2.0\n\nUpdate urgency: `SECURITY`: There are security fixes in the release."},{"id":"headlines-11","title":"Headlines:","role":"content","text":"This version contains security fixes for the Redis server for cases when users may access keys that are not explicitly authorized by the ACL configuration using the [SORT_RO](https://redis.io/commands/sort_ro/) command."},{"id":"details-11","title":"Details:","role":"content","text":"**Security Fixes**\n* **Redis**:\n  * (CVE-2023-41053) Redis does not correctly identify keys accessed by SORT_RO and,\nas a result, may grant users executing this command access to keys that are not\nexplicitly authorized by the ACL configuration.\n\n**Bug Fixes**\n* **Redis**:\n  * [#12538](https://github.com/redis/redis/pull/12538) Fix crashes when joining a node to an existing 7.0 Redis Cluster\n  * [#12545](https://github.com/redis/redis/pull/12545), [#12530](https://github.com/redis/redis/pull/12530) Correct request_policy and response_policy command tips on for some admin /\nconfiguration commands\n\n**Redis version**\n* __[Redis 7.2.1](https://github.com/redis/redis/releases/tag/7.2.1)__\n\n**Module versions**\t\n* __[RediSearch 2.8.4](https://github.com/RediSearch/RediSearch/releases/tag/v2.8.4)__\n* __[RedisJSON 2.6.6](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.6.6)__\n* __[RedisTimeSeries 1.10.4](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.10.4)__\n* __[RedisBloom 2.6.3](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.6.3)__\n* __[RedisGears 2.0.11](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.11-m12)__\n\n**Recommended Client Libraries**\n* Java\n  * [Jedis 5.0.0 or greater](https://github.com/redis/jedis/releases/tag/v5.0.0)\n  * [redis-om-spring 0.8.7 or greater](https://github.com/redis/redis-om-spring/releases/tag/v0.8.7)\n* Python\n  * [redis-py 5.0.0 or greater ](https://github.com/redis/redis-py/releases/tag/v5.0.0)\n  * [redis-om-python 0.2.1 or greater](https://github.com/redis/redis-om-python/releases/tag/v0.2.1)\n* NodeJS\n  * [node-redis 4.6.8 or greater](https://github.com/redis/node-redis/releases/tag/redis%404.6.8)\n  * [redis-om-node 0.2.0 or greater](https://github.com/redis/redis-om-node/releases/tag/v0.2.0)\n* .NET\n  * [redis-om-dotnet 0.5.3 or greater](https://github.com/redis/redis-om-dotnet/releases/tag/v0.5.3)\n  * [NRedisStack 0.9.0 or greater](https://github.com/redis/NRedisStack/releases/tag/v0.9.0)\n* Go\n  * [go-redis 9.1.0 or greater](https://github.com/redis/go-redis/releases/tag/v9.1.0)\n  * [rueidis 1.0.17 or greater](https://github.com/redis/rueidis/releases/tag/v1.0.17)\n\nCompatible with [RedisInsight](https://redis.io/download). The docker image redis/redis-stack for this version is bundled with [RedisInsight 2.32](https://github.com/RedisInsight/RedisInsight/releases/tag/2.32).\n\nNote: version numbers follow the following pattern:\n\n`x.y.z-b`\n* `x.y` Redis major version\n* `z` increases with even numbers as a module x.y version increases.\n* `b` denotes a patch to Redis or a module (any `z` of Redis or modules). `b` will consist of a `v` + numeric value."},{"id":"redis-stack-720-v0-august-2023","title":"Redis Stack 7.2.0-v0 (August 2023)","role":"content","text":"This is a GA release of Redis Stack version 7.2."},{"id":"headlines-12","title":"Headlines:","role":"content","text":"Redis server 7.2 introduces significant memory and speed optimizations for lists, sets, and sorted sets, several improvements, including encoding improvements for sets and lists, stream consumer group improvements, and improved durability.\nSearch and Query brings the frequently asked Geo polygons queries for basic shapes and improved query performance on sorting in different scenarios. \nJSON introduces two new commands: [JSON.MERGE](https://redis.io/commands/json.merge/) and [JSON.MSET](https://redis.io/commands/json.mset/) for more efficient data manipulation.\nPreview of triggers and functions that allows developers to run JavaScript functions inside the Redis process.\nLastly, 7.2 adds the option to use the new protocol [RESP3](https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md) improving the response format for all commands.\n\n**Redis server**:\nRedis server 7.2 brings performance and resource utilization improvements, including significant memory and speed optimizations for lists, sets, and sorted sets. This new version adds a new `WITHSCORE` command argument for [ZRANK](https://redis.io/commands/zrank/) and [ZREVRANK](https://redis.io/commands/zrevrank/), new commands, such as [CLIENT NO-TOUCH](https://redis.io/commands/client-no-touch/) for clients to run commands without affecting LRU/LFU of keys, and more. Redis 7.2 brings behavior changes by introducing a new format (version 11) for RDB files, which is incompatible with older versions, changing module API, and other changes.\n\n**Search and Query**:\nThis new major version introduces the frequently asked [Geo Polygon](https://redis.io/commands/ft.search/#examples) Search. Adding the [GEOSHAPE](https://redis.io/commands/ft.create/) field type that supports polygon shapes using the [WKT notation](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry). In addition to the existing `GEO` for geo range queries, now an alias for `GEOPOINT`, we add `GEOSHAPE` with the support for `POLYGON` and `POINT` as new shapes formats and polygons operations.\n\nIn addition, 7.2 brings improvements in performance for `SORT BY` operations using [`FT.SEARCH`](https://redis.io/commands/ft.search/#optional-arguments) and [`FT.AGGREGATE`](https://redis.io/commands/ft.aggregate/#optional-arguments), and the new `FORMAT` for better readability and future support for better error handling responses on `FT.SEARCH` and `FT.AGGREGATE` in RESP3 only.\n\n**JSON**:\nJSON introduces two new commands:\n - [JSON.MERGE](https://redis.io/commands/json.merge/) merges a given JSON value into matching paths so that the JSON values at the matching paths are updated, deleted, or expanded.\n - [JSON.MSET](https://redis.io/commands/json.mset/) sets or updates one or more JSON values according to specified key-path-value triplets.\n\n**Graph**:\n\nGraph capabilities are no longer included in Redis Stack. See the [RedisGraph End-of-Life Announcement](https://redis.com/blog/redisgraph-eol/).\n\n> [!WARNING]  \n> If you are using graph capabilities with an older version of Redis Stack - please don't upgrade.\n\n**Triggers and functions preview**:\nTriggers and functions is part of Redis Stack 7.2 as public preview, any feedback is highly appreciated.\n\nTriggers and functions provides support for running JavaScript functions inside the Redis process. These functions can be executed on-demand, by an event-driven trigger, or by a stream processing trigger. Triggers and functions empowers developers to build and maintain real-time applications by moving logic closer to the data, ensuring a lower latency whilst delivering the best developer experience.\n\nTry it out with the [triggers and functions quick start](https://redis.io/docs/interact/programmability/triggers-and-functions/quick_start/)."},{"id":"details-12","title":"Details:","role":"content","text":"Find more details about features and optimizations introduced with Redis Stack 7.2 here:\n* [Redis server](https://github.com/redis/redis/blob/7.2/00-RELEASENOTES)\n* [Search and Query](https://github.com/RediSearch/RediSearch/releases/)\n* [JSON](https://github.com/RedisJSON/RedisJSON/releases)\n* [Triggers and functions](https://github.com/RedisGears/RedisGears/releases)\n\n**Redis version**\n* __[Redis 7.2](https://github.com/redis/redis/blob/7.2/00-RELEASENOTES)__\n\n**Module versions**\t\n* __[RediSearch 2.8.4](https://github.com/RediSearch/RediSearch/releases/tag/v2.8.4)__\n* __[RedisJSON 2.6.6](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.6.6)__\n* __[RedisTimeSeries 1.10.4](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.10.4)__\n* __[RedisBloom 2.6.3](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.6.3)__\n* __[RedisGears 2.0.11](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.11-m12)__\n\n**Recommended Client Libraries**\n* Java\n  * [Jedis 4.4.3 or greater](https://github.com/redis/jedis/releases/tag/v4.4.3)\n  * [redis-om-spring 0.8.6 or greater](https://github.com/redis/redis-om-spring/releases/tag/v0.8.6)\n* Python\n  * [redis-py 5.0.0 or greater ](https://github.com/redis/redis-py/releases/tag/v5.0.0)\n  * [redis-om-python 0.2.1 or greater](https://github.com/redis/redis-om-python/releases/tag/v0.2.1)\n* NodeJS\n  * [node-redis 4.6.7 or greater](https://github.com/redis/node-redis/releases/tag/redis%404.6.7)\n  * [redis-om-node 0.2.0 or greater](https://github.com/redis/redis-om-node/releases/tag/v0.2.0)\n* .NET\n  * [redis-om-dotnet 0.5.2 or greater](https://github.com/redis/redis-om-dotnet/releases/tag/v0.5.2)\n  * [NRedisStack 0.8.0 or greater](https://github.com/redis/NRedisStack/releases/tag/v0.8.0)\n* Go\n  * [go-redis 9.0.5 or greater](https://github.com/redis/go-redis/releases/tag/v9.0.5)\n  * [rueidis 1.0.15 or greater](https://github.com/redis/rueidis/releases/tag/v1.0.15)\n\nCompatible with the latest [RedisInsight](https://redis.io/download). The docker image redis/redis-stack for this version is bundled with [RedisInsight 2.30](https://github.com/RedisInsight/RedisInsight/releases/tag/2.30.0).\n\nNote: version numbers follow the pattern:\n\n`x.y.z-b`\n* `x.y` Redis major version\n* `z` increases with even numbers as a module x.y version increases.\n* `b` denotes a patch to Redis or a module (any `z` of Redis or modules). `b` will consist of a `v` + numeric value."},{"id":"redis-stack-720-rc2-july-2023","title":"Redis Stack 7.2.0-rc2 (July 2023)","role":"content","text":"This is the third release of Redis Stack version 7.2.0.\n\nUpdate urgency: `SECURITY`:  there are security fixes in the release."},{"id":"headlines-13","title":"Headlines:","role":"content","text":"This version contains security improvements for the Redis server."},{"id":"details-13","title":"Details:","role":"content","text":"**Features**\n* **Redis**:\n  * [#10362](https://github.com/redis/redis/pull/10362) Make SENTINEL CONFIG [SET|GET] variadic\n  * [#12133](https://github.com/redis/redis/pull/12133) Add a new loglevel \"nothing\" to disable logging\n  * [#9564](https://github.com/redis/redis/pull/9564) Add cluster-announce-human-nodename - a unique identifier for a node that is\nbe used in logs for debugging\n\n **Improvements**\n* **Redis**:\n  * ([CVE-2022-24834](https://github.com/redis/redis/security/advisories/GHSA-p8x2-9v9q-c838)) A specially crafted Lua script executing in Redis can trigger\na heap overflow in the cjson and cmsgpack libraries, and result in heap\ncorruption and potentially remote code execution. The problem exists in all\nversions of Redis with Lua scripting support, starting from 2.6, and affects\nonly authenticated and authorized users.\n  * ([CVE-2023-36824](https://github.com/redis/redis/security/advisories/GHSA-4cfx-h9gq-xpx3)) Extracting key names from a command and a list of arguments\nmay, in some cases, trigger a heap overflow and result in reading random heap\nmemory, heap corruption and potentially remote code execution. Specifically:\nusing COMMAND GETKEYS* and validation of key names in ACL rules.\n  * [#12269](https://github.com/redis/redis/pull/12269) Allow CLUSTER SLOTS / SHARDS commands during loading\n  * [#12233](https://github.com/redis/redis/pull/12233) Support TLS service when \"tls-cluster\" is not enabled and persist both plain\nand TLS port in nodes.conf\n  * [#12320](https://github.com/redis/redis/pull/12320) Update SPOP and RESTORE commands to replicate unlink commands to replicas\nwhen the server is configured to use async server deletes\n  * [#12229](https://github.com/redis/redis/pull/12229) Try lazyfree the temporary zset in ZUNION / ZINTER / ZDIFF\n  * [#12298](https://github.com/redis/redis/pull/12298) Optimize PSUBSCRIBE and PUNSUBSCRIBE from O(N*M) to O(N)\n  * [#12209](https://github.com/redis/redis/pull/12209) Optimize SCAN, SSCAN, HSCAN, ZSCAN commands\n  * [#12315](https://github.com/redis/redis/pull/12315) Set Jemalloc --disable-cache-oblivious to reduce memory overhead\n  * [#12229](https://github.com/redis/redis/pull/12229) Optimize ZINTERCARD to avoid create a temporary zset\n  * [#12205](https://github.com/redis/redis/pull/12205) Optimize HRANDFIELD and ZRANDMEMBER listpack encoded ()\n  * [#12155](https://github.com/redis/redis/pull/12155), [#12082](https://github.com/redis/redis/pull/12082), [#11626](https://github.com/redis/redis/pull/11626), [#11944](https://github.com/redis/redis/pull/11944), [#12316](https://github.com/redis/redis/pull/12316), [#12250](https://github.com/redis/redis/pull/12250),\n[#12177](https://github.com/redis/redis/pull/12177), [#12185](https://github.com/redis/redis/pull/12185) Numerous other optimizations\n  * [#12254](https://github.com/redis/redis/pull/12254) redis-cli: Handle RESP3 double responses that contain a NaN\n  * [#11834](https://github.com/redis/redis/pull/11834) redis-cli: Support URIs with IPv6\n\n**Changed Behavior**\n* **Redis**:\n  * [#10536](https://github.com/redis/redis/pull/10536), [#12166](https://github.com/redis/redis/pull/12166) Cluster SHARD IDs are no longer visible in the cluster nodes output\n  * [#12326](https://github.com/redis/redis/pull/12326) When calling PUBLISH with a RESP3 client that's also subscribed to the same channel, the order is changed and the reply is sent before the published message\n  * [#12321](https://github.com/redis/redis/pull/12321) Align semantics of the new (v7.2 RC2) RM_ReplyWithErrorFormat with RM_ReplyWithError.\nThis is a breaking change that affects the generated error code\n  * [#12304](https://github.com/redis/redis/pull/12304) Forbid RM_AddPostNotificationJob on loading and on read-only replicas\n  * [#12219](https://github.com/redis/redis/pull/12219) Add ability for module command filter to know which client is being handled\n\n**Bug Fixes**\n* **Redis**:\n  * [#12326](https://github.com/redis/redis/pull/12326) Fix broken protocol when PUBLISH is used inside MULTI when the RESP3\npublishing client is also subscribed for the channel\n  * [#12220](https://github.com/redis/redis/pull/12220) Fix WAIT to be effective after a blocked module command being unblocked\n  * [#12276](https://github.com/redis/redis/pull/12276) Re-enable downscale rehashing while there is a fork child\n  * [#12276](https://github.com/redis/redis/pull/12276) Fix possible hang in HRANDFIELD, SRANDMEMBER, ZRANDMEMBER when used with `<count>`\n  * [#12276](https://github.com/redis/redis/pull/12276) Improve fairness issue in RANDOMKEY, HRANDFIELD, SRANDMEMBER, ZRANDMEMBER, SPOP, and eviction\n  * [#12344](https://github.com/redis/redis/pull/12344) Cluster: fix a race condition where a slot migration may revert on a subsequent failover or node joining\n  * [#12301](https://github.com/redis/redis/pull/12301)Fix XREADGROUP BLOCK with \">\" from hanging\n  * [#12247](https://github.com/redis/redis/pull/12247)Fix assertion when a blocked command is rejected when re-processed\n  * [#12342](https://github.com/redis/redis/pull/12342)Fix use after free on a blocking RM_Call\n\n**Redis version**\n* __[Redis 7.2 RC3](https://github.com/redis/redis/blob/7.2/00-RELEASENOTES)__\n\n\n**Module versions**\t\n* __[RediSearch 2.8.3](https://github.com/RediSearch/RediSearch/releases/tag/v2.8.3)__\n* __[RedisJSON 2.6.2](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.6.2)__\n* __[RedisTimeSeries 1.10.1](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.10.1)__\n* __[RedisBloom 2.6.1](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.6.1)__\n* __[RedisGears 2.0.6](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.6-m07)__\n\n**Recommended Client Libraries**\n* Java\n  * [Jedis 4.4.3 or greater](https://github.com/redis/jedis/releases/tag/v4.4.3)\n  * [redis-om-spring 0.8.1 or greater](https://github.com/redis/redis-om-spring/releases/tag/v0.8.1)\n* Python\n  * [redis-py 4.5.5 or greater ](https://github.com/redis/redis-py/releases/tag/v4.5.5)\n  * [redis-om-python 0.1.2 or greater](https://github.com/redis/redis-om-python/releases/tag/v0.1.2)\n* NodeJS\n  * [node-redis 4.6.7 or greater](https://github.com/redis/node-redis/releases/tag/redis%404.6.7)\n  * [redis-om-node 0.2.0 or greater](https://github.com/redis/redis-om-node/releases/tag/v0.2.0)\n* .NET\n  * [redis-om-dotnet 0.5.1 or greater](https://github.com/redis/redis-om-dotnet/releases/tag/v0.5.1)\n  * [NRedisStack 0.7.0 or greater](https://github.com/redis/NRedisStack/releases/tag/v0.7.0)\n* Go\n  * [go-redis 9.0.4 or greater](https://github.com/redis/go-redis/releases/tag/v9.0.4)\n  * [rueidis 1.0.3 or greater](https://github.com/redis/rueidis/releases/tag/v1.0.3)\n\nCompatible with [RedisInsight](https://redis.io/download). The docker image redis/redis-stack for this version is bundled with [RedisInsight 2.26](https://github.com/RedisInsight/RedisInsight/releases/tag/2.26.0).\n\nNote: version numbers follow the pattern:\n\n`x.y.z-b`\n* `x.y` Redis major version\n* `z` increases with even numbers as a module x.y version increases.\n* `b` denotes a patch to Redis or a module (any `z` of Redis or modules). `b` will consist of a `v` + numeric value."},{"id":"redis-stack-720-rc2-june-2023","title":"Redis Stack 7.2.0-rc2 (June 2023)","role":"content","text":"This is the second release of Redis Stack version 7.2.0."},{"id":"headlines-14","title":"Headlines:","role":"content","text":"This version contains a new capability - **Triggers and Functions**, improvements to the **Search and Query** capability, and improvements to the **JSON**, **time series** and **probabilistic data structures**. It also includes the latest version of **RedisInsight**.\n\nThe new **Search and Query** version introduces new and frequently asked **Geo Polygon Search**. Adding the `GEOSHAPE` field type that supports polygon shapes using [WKT notation](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry). Besides the current `GEO` (alias for `GEOPOINT`) used already geo range queries, we add the support for `POLYGON` as new shapes. In addition, it brings improvements in performance for `SORT BY` operations using `FT.SEARCH` and `FT.AGGREGATE`.\n\nThis release includes a new capability - [Triggers and Functions](https://github.com/RedisGears/RedisGears/tree/master) that brings the ability to execute and trigger business logic inside Redis using JavaScript (JS). Detailed documentation and examples can be found in this [GitHub repository](https://github.com/RedisGears/RedisGears#redisgears-20). The Triggers and Functions commands are aligned with the Redis Functions command. The API is not yet final and might change based on feedback. **Any [feedback](https://github.com/RedisGears/RedisGears) is highly appreciated.**"},{"id":"details-14","title":"Details:","role":"content","text":"**Features**\n* **Search and Query**:\n  * [#3553](https://github.com/RediSearch/RediSearch/pull/3553) Introduce support for Geo-polygon shapes and queries\n    * [#3476](https://github.com/RediSearch/RediSearch/pull/3476), [#3660](https://github.com/RediSearch/RediSearch/pull/3660) Adding `GEOSHAPE` [field type](https://redis.io/commands/ft.create/) to map polygons in the `SCHEMA` on `FT.CREATE`.\n    * Support for polygons `POLYGON` using [WKT notation](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry), for example `POLYGON((x1 y1, x2 y2, ...))`.\n    * [#3556](https://github.com/RediSearch/RediSearch/pull/3556) Adjust the [query syntax](https://redis.io/commands/ft.search/#examples) on `FT.SEARCH` for Polygons using the predicate `@geom:[OPERATOR $poly]` and defining polygon in WKT format as `PARAMS 2 poly \"POLYGON((10 20, ...))\"` using `DIALECT 3`.\n    * Initially `WITHIN` and `CONTAINS` operators with `GEOSHAPES` for now.\n    * [#3645](https://github.com/RediSearch/RediSearch/pull/3645) Support multiple coordinate systems cartesian (X,Y) and geographic (lon, lat). Geographic coordinate system using spherical indexing as default (`SPHERICAL`).\n  * [#3046](https://github.com/RediSearch/RediSearch/pull/3046) Introduce performance optimization for sorting operations on `FT.SEARCH` and `FT.AGGREGATE` as default on `DIALECT 4`. It will improve performance in 4 different scenarios, listed below:\n    * Skip Sorter - applied when there is no sort of any kind. The query can return once it reaches the `LIMIT` requested results.\n    * Partial Range - applied when there is a `SORTBY` a numeric field, with no filter or filter by the same numeric field, the query iterate on a range large enough to satisfy the `LIMIT` requested results.\n    * Hybrid - applied when there is a `SORTBY` a numeric field in addition to another non-numeric filter. Some results will get filtered, and the initial range may not be large enough. The iterator then is rewinded with the following ranges, and an additional iteration takes place to collect `LIMIT` requested results.\n    * No optimization - If there is a sort by score or by non-numeric field, there is no other option but to retrieve all results and compare their values.\n    * [#3651](https://github.com/RediSearch/RediSearch/pull/3651) Add `WITHCOUNT` argument that allow return accurate counts for the query results with sorting. This operation processes all results in order to get accurate count, being less performant than the optimised option (default behaviour on `DIALECT 4`).\n\n\n* **JSON**:\n  * [#916](https://github.com/RedisJSON/RedisJSON/pull/916) Introduce the new `JSON.MERGE` in compliance with [RFC 7396](https://datatracker.ietf.org/doc/html/rfc7396), supporting:\n    * Creating new attributes on an existing JSON document\n    * Updating and replacing values in parent and child attributes\n    * Deleting existing attributes (giving `null` as value)\n    * Array update - replacing an entire array with the new value.\n  * [#944](https://github.com/RedisJSON/RedisJSON/pull/944) Introduce `JSON.MSET`, supporting atomic multiple sets for keys in the same hash slot.\n\n* **Triggers and Functions**:\n  * [#875](https://github.com/RedisGears/RedisGears/pull/875) The api_version was added to the library prologue indicating the minimum required API version. Indicating the library works on the specified API version and later minor versions.\n[#896](https://github.com/RedisGears/RedisGears/pull/896) Additional optional callback on a KeySpace trigger that can perform read operations exactly when the notification happens.\n  * [#910](https://github.com/RedisGears/RedisGears/pull/910) The JavaScript API is updated to be inline with JavaScript best practices. Registrations of functions and triggers is done with mandatory arguments and a single optional object.\n  * [#935](https://github.com/RedisGears/RedisGears/pull/935) Ability to load an RDB from a Redis Server with the RedisGears module loaded but NOT used into a Redis Server without the RedisGears module.\n  * Updated commands:\n    * [#910](https://github.com/RedisGears/RedisGears/pull/910) `TFUNCTION` and `TFCALL` is used instead of `RG.FUNCTION` and `RG.FCALL`.\n    * [#939](https://github.com/RedisGears/RedisGears/pull/939) The commands are updated to match the Redis Functions commands. `TFCALL` combines the library and function in a single argument with '.' separated. Example: `TFCALL <library_name>.<function_name>`.\n    * [#900](https://github.com/RedisGears/RedisGears/pull/900) rated the ability to run functions as a coroutine in `TFCALLASYNC`. Clients rely on `TFCALL` to never block shared connections so other commands are not delayed on such connections.\n\n\n **Improvements**\n* **Search and Query**:\n  * [#3641](https://github.com/RediSearch/RediSearch/pull/3641) Indexing sanitizing trigger in heavily data updates scenario.\n  * [#3614](https://github.com/RediSearch/RediSearch/pull/3614) Several improvements in the aggregations execution pipeline.\n\n* **Triggers and Functions**:\n  * [#906](https://github.com/RedisGears/RedisGears/pull/906) Limit the total memory usage of all combined libraries, the values can be configured during module load.\n  * [#940](https://github.com/RedisGears/RedisGears/pull/940) Validate the current memory usage when uploading a new library to not exceed the max memory limit.\n\n\n\n**Changed Behavior**\n* **Search and Query**:\n  * [#3355](https://github.com/RediSearch/RediSearch/pull/3355), [#3635](https://github.com/RediSearch/RediSearch/pull/3635) Expired keys deleted from slave's index, returning an empty array instead of `nil`.\n\n**Bug Fixes**\n* **Search and Query**:\n  * [#3597](https://github.com/RediSearch/RediSearch/pull/3597) Duplicating alias as output name on `FT.AGGREGATE` reducer (`REDUCE` argument) isn't return results.\n  * [#3654](https://github.com/RediSearch/RediSearch/pull/3654) Added check for `@` prefix on `GROUPBY` fields returnig an error instead of wrong results.\n  * [#3501](https://github.com/RediSearch/RediSearch/pull/3501) Sorting by 2 or more fields follow the order not ignoring the second argument.\n  * [#3582](https://github.com/RediSearch/RediSearch/pull/3582) Sorter will set lower rank in documents expired during query time, preventing clients to freeze.\n\n* **JSON**:\n  * [#1025](https://github.com/RedisJSON/RedisJSON/pull/1025) `JSON.TOGGLE` key space notification fix.\n\n* **Time series**:\n  * [#1471](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1471) Potential crash on `MRANGE` when aggregating millions of time series.\n  * [#1469](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1469) Potential memory leak in `MRANGE` after eviction.\n\n* **Triggers and Functions**:\n  * [#913](https://github.com/RedisGears/RedisGears/pull/913) Do not trigger notifications when Redis is loading from a persistence file.\n\n**Redis version**\n* __[Redis 7.2 RC2](https://github.com/redis/redis/blob/7.2/00-RELEASENOTES)__\n\n**Module versions**\t\n* __[RediSearch 2.8.3](https://github.com/RediSearch/RediSearch/releases/tag/v2.8.3)__\n* __[RedisJSON 2.6.2](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.6.2)__\n* __[RedisTimeSeries 1.10.1](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.10.1)__\n* __[RedisBloom 2.6.1](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.6.1)__\n* __[RedisGears 2.0.6](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.6-m07)__\n\n**Recommended Client Libraries**\n* Java\n  * [Jedis 4.4.3 or greater](https://github.com/redis/jedis/releases/tag/v4.4.3)\n  * [redis-om-spring 0.8.1 or greater](https://github.com/redis/redis-om-spring/releases/tag/v0.8.1)\n* Python\n  * [redis-py 4.5.5 or greater ](https://github.com/redis/redis-py/releases/tag/v4.5.5)\n  * [redis-om-python 0.1.2 or greater](https://github.com/redis/redis-om-python/releases/tag/v0.1.2)\n* NodeJS\n  * [node-redis 4.6.7 or greater](https://github.com/redis/node-redis/releases/tag/redis%404.6.7)\n  * [redis-om-node 0.2.0 or greater](https://github.com/redis/redis-om-node/releases/tag/v0.2.0)\n* .NET\n  * [redis-om-dotnet 0.5.1 or greater](https://github.com/redis/redis-om-dotnet/releases/tag/v0.5.1)\n  * [NRedisStack 0.7.0 or greater](https://github.com/redis/NRedisStack/releases/tag/v0.7.0)\n* Go\n  * [go-redis 9.0.4 or greater](https://github.com/redis/go-redis/releases/tag/v9.0.4)\n  * [rueidis 1.0.3 or greater](https://github.com/redis/rueidis/releases/tag/v1.0.3)\n\nCompatible with the latest [RedisInsight](https://redis.io/download). The docker image redis/redis-stack for this version is bundled with [RedisInsight 2.26](https://github.com/RedisInsight/RedisInsight/releases/tag/2.26.0).\n\nNote: version numbers follow the pattern:\n\n`x.y.z-b`\n* `x.y` Redis major version\n* `z` increases with even numbers as a module x.y version increases.\n* `b` denotes a patch to Redis or a module (any `z` of Redis or modules). `b` will consist of a `v` + numeric value."},{"id":"redis-stack-720-rc1-june-2023","title":"Redis Stack 7.2.0-rc1 (June 2023)","role":"content","text":"This is the first release of Redis Stack version 7.2.0."},{"id":"headlines-15","title":"Headlines:","role":"content","text":"This version contains the latest **Search and Query** capability, **JSON** capability, and a new version of RedisInsight. \nThe latest **Search and Query** capability introduces new and frequently asked Geo Polygon Search. Adding the `GEOMETRY` field type that supports polygon shapes using [WKT](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry) notation. In addition, it brings improvements in performance for `SORT BY` operations using `FT.SEARCH` and `FT.AGGREGATE`.\nThe latest **JSON** capability introduces the new `JSON.MERGE` in compliance with [RFC 7396](https://datatracker.ietf.org/doc/html/rfc7396), and `JSON.MSET`, which supports atomic multiple sets for keys in the same hash slot."},{"id":"details-15","title":"Details:","role":"content","text":"**Features**\n* **Search and Query**:\n  * [#3553](https://github.com/RediSearch/RediSearch/pull/3553) Introduce support for Geo-polygon shapes and queries\n    * [#3476](https://github.com/RediSearch/RediSearch/pull/3476) Adding `GEOMETRY` [field type](https://redis.io/commands/ft.create/) to map polygons in the `SCHEMA` on `FT.CREATE`\n    * Adding polygons using [WKT notation](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry), for example `POLYGON((x1 y1, x2 y2, ...))`\n    * [#3556](https://github.com/RediSearch/RediSearch/pull/3556) Adjust the [query syntax](https://redis.io/commands/ft.search/#examples) on `FT.SEARCH` for Polygons using the predicate `@geom:[OPERATOR $poly]` and defining polygon in WKT format as `PARAMS 2 poly \"POLYGON((10 20, ...))\"` using `DIALECT 3`\n    * Initially `WITHIN` and `CONTAINS` operators with Geometries only\n  * [#3046](https://github.com/RediSearch/RediSearch/pull/3046) Introduce the `OPTIMIZE` keyword to `SORTBY` queries using `FT.SEARCH` and `FT.AGGREGATE` that improve performance in 4 different scenarios:\n    * **Skip Sorter** - applied when there is no sort of any kind. The query can return once it reaches the `LIMIT` requested results.\n    * **Partial Range** - applied when there is a `SORTBY` numeric field, with no filter or filter by the same numeric field, the query iterate on a range large enough to satisfy the `LIMIT` requested results.\n    * **Hybrid** - applied when there is a `SORTBY` numeric field in addition to another non-numeric filter. Some results will get filtered, and the initial range may not be large enough. The iterator then is rewinded with the following ranges, and an additional iteration takes place to collect the `LIMIT` requested results.\n    * **No optimization** - If there is a sort by score or non-numeric field, there is no other option but to retrieve all results and compare their values.\n\n* **JSON**:\n  * [#916](https://github.com/RedisJSON/RedisJSON/pull/916) Introduce the new `JSON.MERGE` in compliance with [RFC 7396](https://datatracker.ietf.org/doc/html/rfc7396), supporting:\n    * Creating new attributes on an existing JSON document\n    * Updating and replacing values in parent and child attributes\n    * Deleting existing attributes (giving `null` as value)\n    * Array update - replacing an entire array with the new value\n  * [#944](https://github.com/RedisJSON/RedisJSON/pull/944) Introduce `JSON.MSET`, supporting atomic multiple sets for keys in the same hash slot\n\n **Improvements**\n* **Search and Query**:\n  * [#3628](https://github.com/RediSearch/RediSearch/pull/3628) Background indexing scanning performance\n\n**Changed Behavior**\n* **Search and Query**:\n  * [#3355](https://github.com/RediSearch/RediSearch/pull/3355) Expired keys deleted from the slave's index, returning an empty array instead of `nil`\n\n**Bug Fixes**\n* **Search and Query**:\n  * [#3562](https://github.com/RediSearch/RediSearch/pull/3562) Index definition may load several times when using `REPLICAOF` causing a failure\n  * [#3557](https://github.com/RediSearch/RediSearch/pull/3557) `TIMEOUT` configuration on `FT.AGGREGATE` query being ignored\n  * [#3606](https://github.com/RediSearch/RediSearch/pull/3606) Update numeric inverted index `numEntries` avoiding excessive memory consumption\n  * [#3552](https://github.com/RediSearch/RediSearch/pull/3552) `FT.CURSOR READ` on `JSON` numeric queries not returning results\n\n**Redis version**\n* __[Redis 7.2 RC2](https://github.com/redis/redis/blob/7.2/00-RELEASENOTES)__\n\n**Module versions**\t\n* __[RediSearch 2.8.2](https://github.com/RediSearch/RediSearch/releases/tag/v2.8.2)__\n* __[RedisJSON 2.6.1](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.6.1)__\n* __[RedisTimeSeries 1.10.0](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.10.0)__\n* __[RedisBloom 2.6.0](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.6.0)__\n\n**Recommended Client Libraries**\n* Java\n  * [Jedis 4.4.0 or greater](https://github.com/redis/jedis/releases/)\n  * [redis-om-spring 0.8.1 or greater](https://github.com/redis/redis-om-spring/releases)\n* Python\n  * [redis-py 4.5.5 or greater ](https://github.com/redis/redis-py/releases)\n  * [redis-om-python 0.1.2 or greater](https://github.com/redis/redis-om-python/releases)\n* NodeJS\n  * [node-redis 4.6.6 or greater](https://github.com/redis/node-redis/releases)\n  * [redis-om-node 0.2.0 or greater](https://github.com/redis/redis-om-node/tags)\n* .NET\n  * [redis-om-dotnet 0.5.1 or greater](https://github.com/redis/redis-om-dotnet/releases)\n  * [NRedisStack 0.6.1 or greater](https://github.com/redis/NRedisStack/releases)\n* Go\n  * [go-redis 9.0.4 or greater](https://github.com/redis/go-redis/releases)\n  * [rueidis 1.0.3 or greater](https://github.com/redis/rueidis/releases)\n\nCompatible with the latest [RedisInsight](https://redis.io/download). The docker image redis/redis-stack for this version is bundled with [RedisInsight 2.26](https://github.com/RedisInsight/RedisInsight/releases/tag/2.26.0).\n\nNote: version numbers follow the pattern:\n\n`x.y.z-b`\n* `x.y` Redis major version\n* `z` increases with even numbers as a module x.y version increases.\n* `b` denotes a patch to Redis or a module (any `z` of Redis or modules). `b` will consist of a `v` + numeric value."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redisstack/redisstack-7.4-release-notes","title":"Redis Stack 7.4 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisstack/redisstack-7.4-release-notes/","summary":"Redis Stack 7.4 release notes.","tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:33+01:00","page_type":"content","content_hash":"cfa7de1b02ddb2044ecaae995fd223da35c0174a5938d3d22e3d3d910b23c947","sections":[{"id":"redis-stack-740-v1-october-2024","title":"Redis Stack 7.4.0-v1 (October 2024)","role":"overview","text":"This is a maintenance release for Redis Stack Server 7.4.0.\n\nUpdate urgency: `SECURITY`: there are security fixes in the release.\n\n[Docker](https://hub.docker.com/r/redis/redis-stack) | [Download](https://redis.io/downloads/#stack)"},{"id":"headlines","title":"Headlines","role":"content","text":"This version includes security fixes for the **Redis** server, addressing potential vulnerabilities such as an RCE when using Lua library components, and a denial-of-service (DoS) risk due to malformed ACL selectors or unbounded pattern matching.\nAdditionally, this maintenance release includes the latest version of **Redis Insight**."},{"id":"details","title":"Details","role":"content","text":"**Security and privacy**\n* **Redis**:\n  * (CVE-2024-31449) Lua library commands may lead to stack overflow and potential RCE.\n  * (CVE-2024-31227) Potential Denial-of-service due to malformed ACL selectors.\n  * (CVE-2024-31228) Potential Denial-of-service due to unbounded pattern matching.\n\t\n**Redis Community Edition version**\n  * [Redis 7.4.1](https://github.com/redis/redis/releases/tag/7.4.1)\n\n**Module versions**\t\n* [RediSearch 2.10.5](https://github.com/RediSearch/RediSearch/releases/tag/v2.10.5)\n* [RedisJSON 2.8.3](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.8.3)\n* [RedisTimeSeries 1.12.2](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.12.2)\n* [RedisBloom 2.8.2](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.8.2)\n\n**Recommended client libraries**\n* Java\n  * [Jedis 5.2.0 or later](https://github.com/redis/jedis/releases/tag/v5.2.0)\n  * [Lettuce 6.4.0 or later](https://github.com/redis/lettuce/releases/tag/6.4.0.RELEASE)\n* Python\n  * [redis-py 5.1.0 or later](https://github.com/redis/redis-py/releases/tag/v5.1.0)\n* NodeJS\n  * [node-redis 4.7.0 or later](https://github.com/redis/node-redis/releases/tag/redis%404.7.0)\n* Go\n  * [go-redis 9.6.1 or later](https://github.com/redis/go-redis/releases/tag/v9.6.1)\n\nCompatible with [Redis Insight](https://redis.io/download). The docker image redis/redis-stack for this version is bundled with [Redis Insight 2.58](https://github.com/RedisInsight/RedisInsight/releases/tag/2.58.0).\n\nNote: version numbers follow the pattern:\n\n`x.y.z-b`\n* `x.y` Redis Major version\n* `z` increases with even numbers as a module x.y version increases.\n* `b` denotes a patch to Redis or a module (any `z` of Redis or modules). `b` will consist of a `v` + numeric value."},{"id":"redis-community-edition-740-v0-july-2024","title":"Redis Community Edition 7.4.0-v0 (July 2024)","role":"content","text":"This is a GA release of Redis Stack version 7.4.\n\n[Docker](https://hub.docker.com/r/redis/redis-stack) | [Download](https://redis.io/downloads/#stack)"},{"id":"headlines-1","title":"Headlines","role":"content","text":"**Data Structures**\n\n`Hash`: Redis now supports expiration of individual hash fields. Redis already supports key expiration. For each key, users can specify a time when it should expire, or specify the remaining time-to-live (TTL) after which it would expire. One very frequent request was to allow specifying expiration time or TTL also for individual hash fields, which is now supported using nine new Redis commands:\n- `HEXPIRE`, `HPEXPIRE`, `HEXPIREAT`, `HPEXPIREAT` - set the time when specific hash fields should expire, or the remaining time-to-live for specific fields. \n- `HTTL`, `HPTTL`, `HEXPIRETIME`, `HPEXPIRETIME` - retrieve the time when specific hash fields should expire, or the remaining time-to-live for specific fields\n- `HPERSIST` - remove the expiration of specific hash fields.\n\n`Streams`: It is now possible to start reading from the last stream message using `XREAD` with the new id value `+`.\nThere are many additional improvements, including new command arguments, security, performance, and resource utilization enhancements, several new metrics and configuration parameters were introduced, and multiple bugs were fixed.\n\n`Time series`: The latest time series data structure adds a highly requested feature: insertion-filter for close samples. Many sensors report data periodically. Often, the difference between the measured value and the previously measured value is negligible and related to random noise or to measurement accuracy limitations. When both the time difference and the value difference between the current and the previous sample are small, it may be preferable to ignore (not to add) the new measurement.\n\n`JSON`: Introduces a fix to avoid duplicating AOF commands multiple times in `JSON.MSET`.\n\n`Probabilistic`: Now, an error is returned if `CMS.MERGE` results in an overflow or underflow.\n\n**Redis Search**\n\n- New `BFLOAT16` and `FLOAT16` vector data types reduce memory consumed by vectors while preserving accuracy.\n- Support for indexing empty and missing values and enhanced developer experience for queries with exact matching capabilities.\n- Developers can now match `TAG` fields without needing to escape special characters, making the onboarding process and use of the query syntax simpler.\n- Geospatial search capabilities have been expanded with new `INTERSECT` and `DISJOINT` operators, and ergonomics have been improved by providing better reporting of the memory consumed by the index and exposing the Full-text scoring in the aggregation pipeline.\n\n> [!WARNING]\n> If one or more fields of a hash key expire after a query begins (using FT.SEARCH or FT.AGGREGATE), Redis does not account for these lazily expired fields. As a result, keys with expired fields may still be included in the query results, leading to potentially incorrect or inconsistent results.\n\n**Removal of triggers and functions**\n\nRedis Stack 7.4 will no longer include triggers and functions. To ensure a seamless upgrade, remove any T&F functions created before loading an RDB file into the new Redis Stack."},{"id":"details-1","title":"Details","role":"content","text":"Find more details about features and optimizations introduced with Redis Stack 7.4 here:\n  * [Redis server](https://github.com/redis/redis/blob/7.4.0/00-RELEASENOTES)\n  * [Redis Search capability](https://github.com/RediSearch/RediSearch/releases/tag/v2.10.5)\n  * [JSON data structure](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.8.3)\n  * [Time series data structure](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.12.2)\n  * [Probabilistic data structures](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.8.2)\n\n**Redis version**\n  * [Redis 7.4.0](https://github.com/redis/redis/releases/tag/7.4.0)\n\n**Module versions**\t\n* [RediSearch 2.10.5](https://github.com/RediSearch/RediSearch/releases/tag/v2.10.5)\n* [RedisJSON 2.8.3](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.8.3)\n* [RedisTimeSeries 1.12.2](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.12.2)\n* [RedisBloom 2.8.2](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.8.2)\n\n**Recommended client libraries**\n* Java\n  * [Jedis 5.1.4 or later](https://github.com/redis/jedis/releases/tag/v5.1.4)\n  * [Lettuce 6.4.0 or later](https://github.com/redis/lettuce/releases/tag/6.4.0.RELEASE)\n* Python\n  * [redis-py 5.0.8 or later](https://github.com/redis/redis-py/releases/tag/v5.0.8)\n* NodeJS\n  * [node-redis 4.7.0 or later](https://github.com/redis/node-redis/releases/tag/redis%404.7.0)\n* Go\n  * [go-redis 9.6.1 or later](https://github.com/redis/go-redis/releases/tag/v9.6.1)\n\nCompatible with [Redis Insight](https://redis.io/download). The docker image redis/redis-stack for this version is bundled with [Redis Insight 2.52](https://github.com/RedisInsight/RedisInsight/releases/tag/2.52.0).\n\nNote: version numbers follow the pattern:\n\n`x.y.z-b`\n* `x.y` Redis Major version\n* `z` increases with even numbers as a module x.y version increases.\n* `b` denotes a patch to Redis or a module (any `z` of Redis or modules). `b` will consist of a `v` + numeric value."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries","title":"RedisTimeSeries release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/","summary":"RedisTimeSeries release notes for version 1.12 and earlier","tags":["docs","operate","stack"],"last_updated":"2024-08-08T19:21:33+03:00","children":[{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes","summary":"RedisTimeSeries 1.12 adds a highly requested feature - insertion-filter for close samples.","title":"RedisTimeSeries 1.12 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes","summary":"RESP3 support. Performance improvements.","title":"RedisTimeSeries 1.10 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes","summary":"Added a time-weighted average aggregator, gap filling, ability to control bucket timestamps, ability to control alignment for compaction rules, new reducer types, and ability to include the latest (possibly partial) raw bucket samples when retrieving compactions","title":"RedisTimeSeries 1.8 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.6-release-notes","summary":"Added support for aggregating across multiple time series (multi-key). Can compute queries such as \"the maximum observed value of a set of time series\" server-side instead of client-side.","title":"RedisTimeSeries 1.6 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.6-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.4-release-notes","summary":"Added ability to backfill time series.","title":"RedisTimeSeries 1.4 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.4-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.2-release-notes","summary":"Added compression. Stable ingestion time independent of the number of the data points on a time series. API performance improvements. Extended client support.","title":"RedisTimeSeries 1.2 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.2-release-notes/"},{"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.0-release-notes","summary":"Downsampling/compaction. Secondary indexing. Aggregation at read time. Integration with Prometheus, Grafana, and Telegraph.","title":"RedisTimeSeries 1.0 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.0-release-notes/"}],"page_type":"content","content_hash":"d02c2087ffc1609dd5499f73f350da1b465e4abf6a13aa5b629102c999e38e1d","sections":[{"id":"content","title":"Content","role":"content","text":"| Version (Release date) | Major changes | Min Redis version | Min cluster version |\n| --- | --- | --- | --- |\n| [v1.12 (July 2024)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes/) | RedisTimeSeries 1.12 adds a highly requested feature - insertion-filter for close samples. | 7.4 | 7.8 |\n| [v1.10 (July 2023)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes/) | RESP3 support. Performance improvements. | 7.2 | 7.2.4 |\n| [v1.8 (November 2022)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes/) | Added a time-weighted average aggregator, gap filling, ability to control bucket timestamps, ability to control alignment for compaction rules, new reducer types, and ability to include the latest (possibly partial) raw bucket samples when retrieving compactions | 6.0.16 | 6.2.8 |\n| [v1.6 (January 2022)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.6-release-notes/) | Added support for aggregating across multiple time series (multi-key). Can compute queries such as \"the maximum observed value of a set of time series\" server-side instead of client-side. | 6.0.16 | 6.2.8 |\n| [v1.4 (September 2020)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.4-release-notes/) | Added ability to backfill time series. | 5.0.0 | 6.0.12 |\n| [v1.2 (January 2020)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.2-release-notes/) | Added compression. Stable ingestion time independent of the number of the data points on a time series. API performance improvements. Extended client support. | 5.0.0 | 6.0.12 |\n| [v1.0 (June 2019)](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.0-release-notes/) | Downsampling/compaction. Secondary indexing. Aggregation at read time. Integration with Prometheus, Grafana, and Telegraph. | 5.0.0 | 5.4.0 |"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.0-release-notes","title":"RedisTimeSeries 1.0 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.0-release-notes/","summary":"Downsampling/compaction. Secondary indexing. Aggregation at read time. Integration with Prometheus, Grafana, and Telegraph.","tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:33+01:00","page_type":"content","content_hash":"25992d48db9257a398d1c6222064cd1e7858346271cde8ae2e86141597f89069","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RedisTimeSeries v1.0.3 requires:\n\n- Minimum Redis compatibility version (database): 5.0.0\n- Minimum Redis Enterprise Software version (cluster): 5.4.0"},{"id":"v103-september-2019","title":"v1.0.3 (September 2019)","role":"content","text":"Update urgency: Medium\n\nThis is a maintenance release for version 1.0.\n\nThis release improves overall stability and provides fixes for issues found after the previous release.\n\nMain features:\n\n- #[143](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/143) Standard Deviation for Aggregations\n- #[163](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/163) `TS.RANGE` and `TS.MRANGE` can limit results via optional `COUNT` flag\n- #[161](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/161) Support for ARM architectures\n- #[160](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/160) Optional `TIMESTAMP` in `TS.INCRBY` and `TS.DECRBY`\n\nMain Fixes:\n\n- #[199](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/199) `RETENTION` is now 64bit\n- #[211](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/211) write commands to return OOM error when redis reaches max memory\n\nMain Performance improvements:\n\n- #[3651](https://github.com/RedisTimeSeries/RedisTimeSeries/commit/3651ef8eb65b390e333053b91a64617fc2382f6e) Do not use `_union` if there's only 1 leaf in the index\n- #[0a68](https://github.com/RedisTimeSeries/RedisTimeSeries/commit/0a68d4eca95108595ac7dfbae68d3f0371e41470) Make _difference faster by iterating over the left dict (which is always smaller)"},{"id":"v101-july-2019","title":"v1.0.1 (July 2019)","role":"content","text":"Update urgency: Minor\n\nThis is a maintenance release for version 1.0.\n\nSecondary index should work faster when a filter consists of a list of k=v predicates."},{"id":"v100-june-2019","title":"v1.0.0 (June 2019)","role":"content","text":"This is the General Availability release of RedisTimeSeries!  Read the [full story here](https://redislabs.com/blog/redistimeseries-ga-making-4th-dimension-truly-immersive)"},{"id":"features","title":"Features","role":"content","text":"In RedisTimeSeries, we are introducing a new data type that uses chunks of memory of fixed size for time series samples, indexed by the same [Radix Tree implementation](https://github.com/antirez/rax) as Redis Streams. With Streams, you can create a [capped stream](/commands/xadd), effectively limiting the number of messages by count. In RedisTimeSeries, you can apply a retention policy in milliseconds. This is better for time series use cases, because they are typically interested in the data during a given time window, rather than a fixed number of samples.\n\n#### Downsampling/compaction\n\nIf you want to keep all of your raw data points indefinitely, your data set will grow linearly over time. However, if your use case allows you to have less fine-grained data further back in time, downsampling can be applied. This allows you to keep fewer historical data points by aggregating raw data for a given time window using a given aggregation function. RedisTimeSeries supports downsampling with the following aggregations: avg, sum, min, max, range, count, first and last.  \n\n#### Secondary indexing\n\nWhen using Redis’ core data structures, you can only retrieve a time series by knowing the exact key holding the time series. Unfortunately, for many time series use cases (such as root cause analysis or monitoring), your application won’t know the exact key it’s looking for. These use cases typically want to query a set of time series that relate to each other in a couple of dimensions to extract the insight you need. You could create your own secondary index with core Redis data structures to help with this, but it would come with a high development cost and require you to manage edge cases to make sure the index is correct.\n\nRedisTimeSeries does this indexing for you based on `field value` pairs (a.k.a labels) you can add to each time series, and use to filter at query time (a full list of these filters is available in our documentation). Here’s an example of creating a time series with two labels (sensor_id and area_id are the fields with values 2 and 32 respectively) and a retention window of 60,000 milliseconds:\n\n[code example]\n\n#### Aggregation at read time\n\nWhen you need to query a time series, it’s cumbersome to stream all raw data points if you’re only interested in, say, an average over a given time interval. RedisTimeSeries follows the Redis philosophy to only transfer the minimum required data to ensure lowest latency. Below is an example of aggregation query over time buckets of 5,000 milliseconds with an aggregation function:  \n\n[code example]\n\n#### Integrations\n\nRedisTimeSeries comes with several integrations into existing time series tools. One such integration is our [RedisTimeSeries adapter for Prometheus](https://github.com/RedisTimeSeries/prometheus-redistimeseries-adapter), which keeps all your monitoring metrics inside RedisTimeSeries while leveraging the entire Prometheus ecosystem.\nFurthermore, we also created direct integration for [Grafana](https://github.com/RedisTimeSeries/grafana-redistimeseries). [This repository](https://github.com/RedisTimeSeries/prometheus-demos) contains a docker-compose setup of RedisTimeSeries, its remote write adaptor, Prometheus and Grafana. It also comes with a set of data generators and pre-built Grafana dashboards."}],"examples":[{"id":"features-ex0","language":"sh","code":"TS.CREATE temperature RETENTION 60000 LABELS sensor_id 2 area_id 32","section_id":"features"},{"id":"features-ex1","language":"sh","code":"127.0.0.1:6379> TS.RANGE temperature:3:32 1548149180000 1548149210000 AGGREGATION avg 5000\n1) 1) (integer) 1548149180000\n   2) \"26.199999999999999\"\n2) 1) (integer) 1548149185000\n   2) \"27.399999999999999\"\n3) 1) (integer) 1548149190000\n   2) \"24.800000000000001\"\n4) 1) (integer) 1548149195000\n   2) \"23.199999999999999\"\n5) 1) (integer) 1548149200000\n   2) \"25.199999999999999\"\n6) 1) (integer) 1548149205000\n   2) \"28\"\n7) 1) (integer) 1548149210000\n   2) \"20\"","section_id":"features"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes","title":"RedisTimeSeries 1.10 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes/","summary":"RESP3 support. Performance improvements.","tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:33+01:00","page_type":"content","content_hash":"901a8cf06b67cde40457adcbbafb1e3c9c761102948fa2f9c0a4b44faa917fd5","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RedisTimeSeries v1.10.27 requires:\n\n- Minimum Redis compatibility version (database): 7.2\n- Minimum Redis Enterprise Software version (cluster): 7.2.4"},{"id":"v11027-sep-2026","title":"v1.10.27 (Sep 2026)","role":"content","text":"This is a maintenance release for RedisTimeSeries 1.10.\n\nUpdate urgency: `SECURITY`: There are security fixes in the release.\n\nDetails:\n\n- **Security**\n  - Prevent heap buffer overflows and invalid memory access when restoring malformed time-series data.\n\n- Bug fixes:\n  - Fix `TS.INCRBY` replication to preserve the primary’s timestamp when using server-generated timestamps."},{"id":"v11024-may-2026","title":"v1.10.24 (May 2026)","role":"content","text":"This is a maintenance release for RedisTimeSeries 1.10.\n\nUpdate urgency: `SECURITY`: There are security fixes in the release.\n\nDetails:\n\n- **Security:**\n\n  - (CVE-2026-25588) Invalid memory access in `RESTORE` may lead to Remote Code Execution."},{"id":"v11020-october-2025","title":"v1.10.20 (October 2025)","role":"content","text":"This is a maintenance release for RedisTimeSeries 1.10.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\nBug fixes:\n\n- [#1776](https://github.com/redistimeseries/redistimeseries/pull/1776) Potential crash on `TS.RANGE` with `ALIGN +`, `AGGREGATION twa` and `EMPTY` (MOD-11620, MOD-10484).\n\nImprovements:\n\n- Added support for Rocky Linux 9 and RHEL9 ARM."},{"id":"v11017-april-2025","title":"v1.10.17 (April 2025)","role":"content","text":"This is a maintenance release for RedisTimeSeries 1.10.\n\nUpdate urgency: `MODERATE`: Plan an upgrade of the server, but it's not urgent.\n\nDetails:\n\nBug fixes:\n- [#1725](https://github.com/redistimeseries/redistimeseries/pull/1725) `TS.DEL` crashes on keys with compactions if the deletion removes the last compaction bucket (MOD-8936)\n- [LibMR#58](https://github.com/RedisGears/LibMR/pull/58) Crash when a cluster contains both 1.8 and newer nodes (MOD-8976, MOD-9192)\n\nImprovements:\n- [#1712](https://github.com/redistimeseries/redistimeseries/pull/1712) Added support for Azure Linux 3 (MOD-9170)\n- [#1736](https://github.com/redistimeseries/redistimeseries/pull/1736) (Redis Enterprise only) Cross-key commands are handled by a random shard rather than the first shard (MOD-9262, MOD-9314)"},{"id":"v11016-january-2025","title":"v1.10.16 (January 2025)","role":"content","text":"This is a maintenance release for RedisTimeSeries 1.10.\n\nUpdate urgency: `SECURITY`: There are security fixes in the release.\n\nDetails:\n\n- **Security and privacy:**\n  - [#1673](https://github.com/redistimeseries/redistimeseries/pull/1673) (CVE-2024-51480) `TS.QUERYINDEX`, `TS.MGET`, `TS.MRANGE`, `TS.MREVRANGE` - potential integer overflow leading to an out-of-bounds write (MOD-7548)\n\n- Improvements:\n  - [#1663](https://github.com/redistimeseries/redistimeseries/pull/1663) Added support for Ubuntu 22 and macOS 13 and 14"},{"id":"v11012-march-2024","title":"v1.10.12 (March 2024)","role":"content","text":"This is a maintenance release for RedisTimeSeries 1.10.\n\nUpdate urgency: `LOW`: No need to upgrade unless there are new features you want to use.\n\nDetails:\n\n- Improvements:\n\n  - [#1593](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1593) More detailed LibMR error messages"},{"id":"v11011-october-2023","title":"v1.10.11 (October 2023)","role":"content","text":"This is a maintenance release for RedisTimeSeries 1.10.\n\nUpdate urgency: `LOW`: No need to upgrade unless there are new features you want to use.\n\nDetails:\n\n- Bug fixes:\n\n  - [LibMR#51](https://github.com/RedisGears/LibMR/pull/51) Crash on SSL initialization failure (MOD-5647)\n  - [#1538](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1538) Amazon Linux 2: crash on SSL initialization. We now use openssl11-devel instead of openssl-devel (MOD-6015)"},{"id":"v1109-october-2023","title":"v1.10.9 (October 2023)","role":"content","text":"This is a maintenance release for RedisTimeSeries 1.10.\n\nUpdate urgency: `LOW`: No need to upgrade unless there are new features you want to use.\n\nDetails:\n\n- Improvements:\n\n  - [#1516](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1516) Added support for CBL-Mariner 2\n  - [#1514](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1514) Added support for Rocky Linux 9 and RHEL9"},{"id":"v1106-september-2023","title":"v1.10.6 (September 2023)","role":"content","text":"This is a maintenance release for RedisTimeSeries 1.10.\n\nUpdate urgency: `SECURITY`: There are security fixes in the release.\n\nDetails:\n\n- Security and privacy:\n\n  - [#1506](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1506) Don’t expose internal commands (MOD-5643)"},{"id":"v110-ga-v1104-july-2023","title":"v1.10 GA (v1.10.4) (July 2023)","role":"content","text":"This is the General Availability release of RedisTimeSeries 1.10."},{"id":"headlines","title":"Headlines","role":"content","text":"RedisTimeSeries 1.10 introduces support for RESP3, performance improvements, and bug fixes."},{"id":"details","title":"Details","role":"content","text":"Bug fixes (since 1.10-RC3):\n\n- [#1494](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/1494) Potential crash when using an invalid argument value\n\n> [!NOTE]\n> - The version inside Redis will be 1.10.4 in semantic versioning. Since the version of a module in Redis is numeric, we could not add a GA flag.\n>\n> - Minimal Redis version: 7.2"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes","title":"RedisTimeSeries 1.12 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes/","summary":"RedisTimeSeries 1.12 adds a highly requested feature - insertion-filter for close samples.","tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:33+01:00","page_type":"content","content_hash":"9b1381601296b42fffc3b0acae0686317d59d448665426ea1d52f3411bbd8789","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RedisTimeSeries v1.12.17 requires:\n\n- Minimum Redis compatibility version (database): 7.4\n- Minimum Redis Enterprise Software version (cluster): 7.8"},{"id":"v11217-sep-2026","title":"v1.12.17 (Sep 2026)","role":"content","text":"This is a maintenance release for RedisTimeSeries 1.12.\n\nUpdate urgency: `SECURITY`: There are security fixes in the release.\n\nDetails:\n\n- **Security**\n\n  - Prevent heap buffer overflows and invalid memory access when restoring malformed time-series data.\n\n- Bug fixes:\n\n  - Fix `TS.INCRBY` replication to preserve the primary’s timestamp when using server-generated timestamps."},{"id":"v11214-may-2026","title":"v1.12.14 (May 2026)","role":"content","text":"> [!NOTE]\n> Starting with Redis 8, the time series data structure is integral to Redis. You don't need to install this module separately.\n> This is not the latest release.\n> We no longer release new standalone versions of RedisTimeSeries; we only patch old versions.\n> See https://github.com/redis/redis\n\nThis is a maintenance release for RedisTimeSeries 1.12.\n\nUpdate urgency: `SECURITY`: There are security fixes in the release.\n\nDetails:\n\n- **Security:**\n\n  - (CVE-2026-25588) Invalid memory access in `RESTORE` may lead to Remote Code Execution."},{"id":"v1129-october-2025","title":"v1.12.9 (October 2025)","role":"content","text":"This is a maintenance release for RedisTimeSeries 1.12.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\nBug fixes:\n\n- [#1776](https://github.com/redistimeseries/redistimeseries/pull/1776) Potential crash on `TS.RANGE` with `ALIGN +`, `AGGREGATION twa` and `EMPTY` (MOD-11620, MOD-10484).\n\nImprovements:\n\n- Added support for Rocky Linux 9 and RHEL9 ARM."},{"id":"v1126-april-2025","title":"v1.12.6 (April 2025)","role":"content","text":"This is a maintenance release for RedisTimeSeries 1.12.\n\nUpdate urgency: `MODERATE`: Plan an upgrade of the server, but it's not urgent.\n\nDetails:\n\nBug fixes:\n- [#1725](https://github.com/redistimeseries/redistimeseries/pull/1725) `TS.DEL` crashes on keys with compactions if the deletion removes the last compaction bucket (MOD-8936)\n- [LibMR#58](https://github.com/RedisGears/LibMR/pull/58) Crash when a cluster contains both 1.8 and newer nodes (MOD-8976, MOD-9192)\n\nImprovements:\n- [#1712](https://github.com/redistimeseries/redistimeseries/pull/1712) Added support for Azure Linux 3 (MOD-9170)\n- [#1736](https://github.com/redistimeseries/redistimeseries/pull/1736) (Redis Enterprise only) Cross-key commands are handled by a random shard rather than the first shard (MOD-9262, MOD-9314)"},{"id":"v1125-january-2025","title":"v1.12.5 (January 2025)","role":"content","text":"This is a maintenance release for RedisTimeSeries 1.12.\n\nUpdate urgency: `SECURITY`: There are security fixes in the release.\n\nDetails:\n\n- **Security and privacy:**\n  - [#1674](https://github.com/redistimeseries/redistimeseries/pull/1674) (CVE-2024-51480) `TS.QUERYINDEX`, `TS.MGET`, `TS.MRANGE`, `TS.MREVRANGE` - potential integer overflow leading to an out-of-bounds write (MOD-7548)\n\n- Improvements:\n  - [#1662](https://github.com/redistimeseries/redistimeseries/pull/1662) Added support for Ubuntu 22 and macOS 13 and 14"},{"id":"v112-ga-v1122-july-2024","title":"v1.12 GA (v1.12.2) (July 2024)","role":"content","text":"This is the General Availability release of RedisTimeSeries 1.12"},{"id":"headlines","title":"Headlines","role":"content","text":"RedisTimeSeries 1.12 adds a highly requested feature: insertion-filter for close samples. Many sensors report data periodically. Often, the difference between the measured value and the previous measured value is negligible and related to random noise or to measurement accuracy limitations. When both the time difference and the value difference between the current and the previous sample are small, it may be preferable to ignore (not to add) the new measurement."},{"id":"whats-new-in-112","title":"What's new in 1.12","role":"content","text":"- [#1543](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1543) insertion-filter for close samples:\n\n  - Two new [module configuration parameters](https://redis.io/docs/data-types/timeseries/configuration/) are introduced: `IGNORE_MAX_TIME_DIFF` and `IGNORE_MAX_VAL_DIFF`.\n  - Two new similar per-key parameters are introduced: `ignoreMaxTimeDiff` and `ignoreMaxValDiff`.\n  - `TS.ADD`, `TS.INCRBY`, and `TS.DECRBY` now have a new optional argument: `[IGNORE ignoreMaxTimeDiff ignoreMaxValDiff]`.\n    When creating a new time series, these two values are used to set the per-key parameters and override the two module configuration parameters. These values are ignored when specified with an existing time series.\n  - `[IGNORE ignoreMaxTimeDiff ignoreMaxValDiff]` is also supported by `TS.ALTER`.\n  - For each call to `TS.ADD`, if the following conditions are met:\n      - series is not a compaction\n      - the series' `DUPLICATE_POLICY` is `LAST`\n      - `timestamp` ≥ `max_timestamp`\n      - `timestamp` - `max_timestamp` ≤ `ignoreMaxTimeDiff`\n      - abs(`value` - `value_at_max_timestamp`) ≤ `ignoreMaxValDiff`))\n    then this sample is ignored (not added) to the time series. `max_timestamp` is the maximal timestamp in the time series.\n  - The same logic also applies for each call to `TS.MADD` based on the values of the per-key parameters.\n  - When a sample is ignored, the returned value for `TS.ADD` and for the applicable array element in `TS.MADD` is `max_timestamp`."},{"id":"details","title":"Details","role":"content","text":"- Bug fixes (since 1.12-RC1)\n\n  - [#1607](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1607) Potential crash after deleting and recreating a source key of a compaction rule (MOD-7338)\n  - [#1610](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1610) `COUNT` argument accepts non-positive values (MOD-5413)\n\n> [!NOTE]\n> - The version inside Redis will be 1.12.2 in semantic versioning. Since the version of a module in Redis is numeric, we could not add a GA flag.\n> - Minimal Redis version: 7.4"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.2-release-notes","title":"RedisTimeSeries 1.2 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.2-release-notes/","summary":"Added compression. Stable ingestion time independent of the number of the data points on a time series. API performance improvements. Extended client support.","tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:33+01:00","page_type":"content","content_hash":"082a5bfb6c9b2c1f6dcda4ee90b9dddf5ba87b4383fec85d5f68ca78ca8124ac","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RedisTimeSeries v1.2.7 requires:\n\n- Minimum Redis compatibility version (database): 5.0.0\n- Minimum Redis Enterprise Software version (cluster): 6.0.12"},{"id":"v127-june-2020","title":"v1.2.7 (June 2020)","role":"content","text":"This is a maintenance release for version 1.2.\n\nDetails:\n\n- Bugfixes:\n\n    - #[414](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/414) Crash when a query had an empty label `(foo,)`"},{"id":"v126-may-2020","title":"v1.2.6 (May 2020)","role":"content","text":"This is a maintenance release for version 1.2.\n\nDetails:\n\n- Minor enhancements:\n\n    - #[403](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/403) Support for multi-value filtering in `TS.MGET` and `TS.MRANGE`.\n\n- Bugfixes:\n\n    - #[378](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/378) Using `snprintf` to ensure the same precision of floating-point value replies.\n    - #[374](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/374) `TS.RANGE` crashed when COUNT argument was missing.\n    - #[395](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/395) Check minimum compatible Redis version at module load time."},{"id":"v125-march-2020","title":"v1.2.5 (March 2020)","role":"content","text":"Headlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n\nDetails:\n\n- Bugfixes:\n    - #[358](https://github.com/redistimeseries/redistimeseries/issues/358) [Wrong behaviour](https://groups.google.com/forum/?utm_medium=email&utm_source=footer#!topic/redistimeseries/wH0R69e8lOs) in `TS.RANGE` due to shifting left.\n    - #[353](https://github.com/redistimeseries/redistimeseries/issues/353) Crash where the name of a time-series was already taken due to auto-compaction."},{"id":"v123-february-2020","title":"v1.2.3 (February 2020)","role":"content","text":"Headlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n\nDetails:\n\n- Bugfixes:\n    - #[338](https://github.com/redistimeseries/redistimeseries/issues/338) reverting #[319](https://github.com/redistimeseries/redistimeseries/issues/319).  Aggregation should happen on deterministic time buckets."},{"id":"v12-ga-122---january-2020","title":"v1.2 GA (1.2.2 - January 2020)","role":"content","text":"This is the general availability (GA) release of RedisTimeSeries 1.2 (1.2.2).\n\nHeadlines:\n\n- Compression added which can reduce memory up to 98% and improve read performance up to 50%.\n- Stable ingestion time independent of the number of the data points on a time-series.\n- Reviewed API with performance improvements and removed ambiguity.\n- Extended [client support](https://redis.io/docs/latest/develop/data-types/timeseries)\n\n(we will blog about this release soon including performance improvements results and the link here)\n\nFull details:\n\n- Added functionality\n    - #[261](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/261) Samples are compressed using `Double Delta compression` which results in cost savings and faster query times.\n    - Based on the [Gorilla paper](https://www.vldb.org/pvldb/vol8/p1816-teller.pdf).\n        - In theory, this can save space up to 98%. (2 bits per sample in stead of 128).\n        - In practice, a memory reduction of 5-8x is common but depends on the use case.\n    - Initial benchmarks show 94% memory savings and performance improvements in reads up to XX%.\n    - `UNCOMPRESSED` option in `TS.CREATE`.\n\n- API changes / Enhancements\n    - #[241](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/241) Overwriting the last sample with the same timestamp is not allowed.\n    - #[242](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/242) revised `TS.INCRBY/DECRBY`\n    - Returns a timestamp.  The behaviour is now aligned with `TS.ADD`.\n    - The `RESET` functionality was removed. `RESET` contradicted the rewriting of the last sample (#[241](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/241)).\n    Alternatively, you can reconstruct similar behaviour by\n        - `TS.ADD ts * 1` + `sum` aggregation\n        - `TS.INCRBY ts 1` + `range` aggregation\n    - #[317](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/317) Aligning response on empty series of `TS.GET` with `TS.RANGE`.\n    - #[285](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/285) #[318](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/318) Changed default behaviour of `TS.MRANGE` and `TS.MGET` to no longer returns the labels of each time-series in order reduce network traffic. Optional `WITHLABELS` argument added.\n    - #[319](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/319) `TS.RANGE` and `TS.MRANGE` aggregation starting from requested timestamp.\n\n- Performance improvements\n    - #[237](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/237) Downsampling after time window is closed vs. downsampling with each sample.\n    - #[285](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/285) #[318](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/318) Optional `WITHLABELS` argument added.  This feature improves read performance drastically.\n\n- Minor Enhancements\n    - #[230](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/230) `TS.INFO` now includes `total samples`, `memory usage`,`first time stamp`, ...\n    - #[230](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/230) `MEMORY` calculates series memory footprint.\n\n- Bugfixes since 1.0.3\n    - #[204](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/204) Module initialization params changed to 64 bits.\n    - #[266](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/266) Memory leak in the aggregator context.\n    - #[260](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/260) Better error messages.\n    - #[259](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/259) #[257](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/257) #[219](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/219) Miscellaneous.\n    - #[320](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/320) Delete the existing key prior to restoring it.\n    - #[323](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/323) Empty first sample on aggregation.\n\n> [!NOTE]\n> The version inside Redis will be 10202 or 1.2.2 in semantic versioning."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.4-release-notes","title":"RedisTimeSeries 1.4 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.4-release-notes/","summary":"Added ability to backfill time series.","tags":["docs","operate","stack"],"last_updated":"2026-09-21T16:36:33+01:00","page_type":"content","content_hash":"9607c1ae6808fab244d171c1bc11c13a164fef61449a38f2602f6db1d8299da6","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RedisTimeSeries v1.4.14 requires:\n\n- Minimum Redis compatibility version (database): 5.0.0\n- Minimum Redis Enterprise Software version (cluster): 6.0.12"},{"id":"v1414-february-2022","title":"v1.4.14 (February 2022)","role":"content","text":"This is a maintenance release for RedisTimeSeries 1.4.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nBug fixes:\n\n- [#891](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/891), [#892](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/892) Fixed memory leak in parseCreateArgs when parsing error occurs (MOD-1958)"},{"id":"v1413-november-2021","title":"v1.4.13 (November 2021)","role":"content","text":"This is a maintenance release for RedisTimeSeries 1.4.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nBug fixes:\n\n- [#881](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/881) Replicate only successful insertion of `TS.MADD`"},{"id":"v1411-november-2021","title":"v1.4.11 (November 2021)","role":"content","text":"This is a maintenance release for RedisTimeSeries 1.4.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nBug fixes:\n\n- [#862](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/862) Index dictionary should be freed on removing the last element"},{"id":"v1410-july-2021","title":"v1.4.10 (July 2021)","role":"content","text":"This is a maintenance release for RedisTimeSeries 1.4.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nBug fixes:\n\n- [#760](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/760) Avoid closing the same key twice, causing server crash on [`RENAME`](https://redis.io/docs/latest/commands/rename) of other keys"},{"id":"v149-may-2021","title":"v1.4.9 (May 2021)","role":"content","text":"This is a maintenance release for version 1.4.\n\nUpdate urgency: Medium\n\nHeadlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n\nBug fixes:\n\n- #[#712](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/712) Missing keytype check on TS.INCRBY/DECRBY causes shards to crash\n- #[#719](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/719) Support for renaming time series keys"},{"id":"v148-march-2021","title":"v1.4.8 (March 2021)","role":"content","text":"This is a maintenance release for version 1.4.\n\nHeadlines:\n\n- This release improves overall stability and provides fixes for issues found after the previous release.\n\nBug fixes:\n\n- [#612](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/612) Crash on `MGET`/`MRANGE`\n- [#606](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/606) Memory leak when key loaded from RDB\n- [#624](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/624) Uninitialised memory access on log"},{"id":"v147-december-2020","title":"v1.4.7 (December 2020)","role":"content","text":"This is a maintenance release for version 1.4.\n\nHeadlines:\n\n- This release improves overall stability, and provides fixes for issues found after the previous release.\n\nBug fixes:\n\n- [#581](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/581) Misaligned allocators usage might crash Redis.\n- [#588](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/588) `ON_DUPLICATE` min/max rules not working for negative value."},{"id":"v146-november-2020","title":"v1.4.6 (November 2020)","role":"content","text":"This is a maintenance release for version 1.4.\n\nHeadlines:\n\nThis release improves overall stability and provides fixes for issues found after the previous release.\n\nMinor enhancements:\n\n- [#565](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/565)\n    duplicate policy: add `SUM` option: If a previous sample exists, add the new sample to it so that the updated value is equal to (previous + new).\n    If no previous sample exists, set the updated value equal to the new value. (PR [#565](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/565)\n- [#559](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/559)\n    Compressed chunk will be be the default global option\n- [#559](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/559)\n    Added `chunkType` to `TS.INFO`\n\nBug fixes:\n\n- [#528](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/528)\n    Out of order insert might crash Redis if there's an update to an empty downsampled key\n- [#561](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/561)\n    `TS.MRANGE` command might crash if there's an expired key that was deleted in the result set"},{"id":"v14-ga-september-2020","title":"v1.4 GA (September 2020)","role":"content","text":"This is the General Availability release for RedisTimeSeries 1.4.\n\nHighlights:\n\n**Ability to backfill time series!** You can now add samples to a time series where the time of the sample is older than the newest sample in the series. This enables:\n\n- Adding out of order of samples to time series.\n- Batch loading of historical samples into an existing series.\n- Updating existing samples (for example for compliance reasons).\n\nThis has been the most requested feature for RedisTimeSeries. We look forward to your feedback so we can move to a general availability release soon.\n\nDetails:\n\n- Added functionality:\n    - [#254](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/254) TS.REVRANGE and TS.MREVRANGE commands allow for querying in descending order of Timestamps.\n    - [#503](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/503) - RDB saves the whole chunk instead of individual samples giving a speed and space improvement when saving or loading an RDB file\n    - [#502](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/502) - The ability to set, at creation time, the data section size of each chunk using flag `CHUNK_SIZE`. TS.INFO uses `chunkSize` instead of `maxSamplesPerChunk`.\n    - [#437](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/437) Allow backfilling of samples and updating of existing samples\n        - Works with [compressed](https://redislabs.com/blog/redistimeseries-version-1-2-is-here/) and uncompressed series.\n        - This comes with a performance hit when a sample is written out-of-order. We will publish numbers once we are generally available, but are still considering optimisations.\n    - [#521](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/521) `DUPLICATE_POLICY` allows to configure on module, series and sample level how to handle duplicate samples. A duplicate sample is a sample for which the series holds already a sample on the same timestamp. Note that the default behaviour is equal to v1.2: `BLOCK`\n\nNotes:\nThe version inside Redis will be 10405 or 1.4.5 in semantic versioning. Since the version of a module in Redis is numeric, we could not add an GA flag."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.6-release-notes","title":"RedisTimeSeries 1.6 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.6-release-notes/","summary":"Added support for aggregating across multiple time series (multi-key). Can compute queries such as \"the maximum observed value of a set of time series\" server-side instead of client-side.","aliases":["/modules/redistimeseries/release-notes/redistimeseries-1.6-release-notes/"],"tags":["Modules"],"last_updated":"2026-09-21T16:36:33+01:00","page_type":"content","content_hash":"1e8d94176d4a287ad13c2c6844b54d845ae02f3b7457e3b3f35905da5a8a22e9","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RedisTimeSeries v1.6.19 requires:\n\n- Minimum Redis compatibility version (database): 6.0.16\n- Minimum Redis Enterprise Software version (cluster): 6.2.8"},{"id":"v1619-february-2023","title":"v1.6.19 (February 2023)","role":"content","text":"This is a maintenance release for RedisTimeSeries 1.6.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n    - [#1397](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/1397) Memory leak when trying to create an already existing key (MOD-4724, RED-93418)"},{"id":"v1617-july-2022","title":"v1.6.17 (July 2022)","role":"content","text":"This is a maintenance release for RedisTimeSeries 1.6.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails:\n\n- Bug fixes:\n\n    - [#1240](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1240) Compaction rules are not saved to [RoF](https://docs.redis.com/latest/rs/databases/redis-on-flash) (Redis Enterprise)"},{"id":"v1616-june-2022","title":"v1.6.16 (June 2022)","role":"content","text":"This is a maintenance release for RedisTimeSeries 1.6.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails:\n\n- Features:\n\n    - [#1193](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1193) Commands that don’t execute on the main thread now appear in [SLOWLOG](https://redis.io/docs/latest/commands/slowlog)\n\n- Bug fixes:\n\n    - [#1203](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1203) Compaction rules are not replicated (Replica Of) on Redis Enterprise\n    - [#1204](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/1204) When the last sample is deleted with [`TS.DEL`](https://redis.io/docs/latest/commands/ts.del), it may still be accessible with [`TS.GET`](https://redis.io/docs/latest/commands/ts.get)\n    - [#1226](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1226) [`TS.MRANGE`](https://redis.io/docs/latest/commands/ts.mrange), [`TS.MREVRANGE`](https://redis.io/docs/latest/commands/ts.mrevrange): on a multi-shard environment, some chunks may be skipped\n\n> [!NOTE]\n> New RDB version (v5). RDB files created with 1.6.16 are not backward compatible."},{"id":"v1613-june-2022","title":"v1.6.13 (June 2022)","role":"content","text":"This is a maintenance release for RedisTimeSeries 1.6.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails:\n\n- Bug fixes:\n\n    - [#1176](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/1176), [#1187](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1187) When executing [`DEL`](https://redis.io/docs/latest/commands/ts.del), chunk index could be set to a wrong value and cause some data to be inaccessible\n    - [#1180](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1180) When executing [`MADD`](https://redis.io/docs/latest/commands/ts.madd), make sure that only successful insertions are replicated"},{"id":"v1611-may-2022","title":"v1.6.11 (May 2022)","role":"content","text":"This is a maintenance release for RedisTimeSeries 1.6.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails:\n\n- Bug fixes:\n\n    - [#1166](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1166) Stop forwarding multi-shard commands during cluster resharding and upgrade (MOD-3154)\n    - [#1165](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1165) Stop forwarding multi-shard commands during transactions (`MULTI EXEC`) (MOD-3182)\n    - [LibMR](https://github.com/RedisGears/LibMR): Fixed crash on multi-shard commands in some rare scenarios (MOD-3182)"},{"id":"v1610-may-2022","title":"v1.6.10 (May 2022)","role":"content","text":"This is a maintenance release for RedisTimeSeries 1.6.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n    - [#1074](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1074) [`RANGE`](https://redis.io/docs/latest/commands/ts.range), [`REVRANGE`](https://redis.io/docs/latest/commands/ts.revrange), [`MRANGE`](https://redis.io/docs/latest/commands/ts.mrange), and [`MREVRANGE`](https://redis.io/docs/latest/commands/ts.mrevrange): Possibly incorrect result when using `ALIGN` and aggregating a bucket with a timestamp close to 0\n    - [#1094](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1094) [LibMR](https://github.com/RedisGears/LibMR): Potential memory leak; memory release delay\n    - [#1127](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1127) Memory leak on [`RANGE`](https://redis.io/docs/latest/commands/ts.range) and [`REVRANGE`](https://redis.io/docs/latest/commands/ts.revrange) when argument parsing fails\n    - [#1096](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1096) [`RANGE`](https://redis.io/docs/latest/commands/ts.range), [`REVRANGE`](https://redis.io/docs/latest/commands/ts.revrange), [`MRANGE`](https://redis.io/docs/latest/commands/ts.mrange), and [`MREVRANGE`](https://redis.io/docs/latest/commands/ts.mrevrange): Using `FILTER_BY_TS` without specifying timestamps now returns an error as expected"},{"id":"v169-february-2022","title":"v1.6.9 (February 2022)","role":"content","text":"This is a maintenance release for RedisTimeSeries 1.6.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Security and privacy:\n\n    - [#1061](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1061) Internode communications encryption: support passphrases for PEM files\n\n- Bug fixes:\n\n    - [#1056](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1056) Return an error when a shard is down (in v1.6.8, returned an empty result)"},{"id":"v16-ga-v168-january-2022","title":"v1.6 GA (v1.6.8) (January 2022)","role":"content","text":"This is the General Availability release of RedisTimeSeries 1.6."},{"id":"highlights","title":"Highlights","role":"content","text":"RedisTimeSeries 1.6 adds support for aggregating across multiple time series (multi-key). Before this version, queries such as \"the maximum observed value of a set of time series\" needed to be calculated client-side. Such queries can now be computed server-side, leveraging the heart of RedisGears ([LibMR](https://github.com/RedisGears/LibMR)) for clustered databases."},{"id":"whats-new-in-16","title":"What's new in 1.6","role":"content","text":"- Introduction of `GROUPBY` and `REDUCE` in `TS.MRANGE` and `TS.MREVRANGE` to add support for \"multi-key aggregation\" and support for such aggregations spanning multiple shards, leveraging [LibMR](https://github.com/RedisGears/LibMR). Currently, we support `min`, `max`, and `sum` as reducers and grouping by a label.\n\n- [#722](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/722), [#275](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/275) Filter results using `FILTER_BY_TS` by providing a list of timestamps and `FILTER_BY_VALUE` by providing a `min` and a `max` value (`TS.RANGE`, `TS.REVRANGE`, `TS.MRANGE`, and `TS.MREVRANGE`).\n\n- [#603](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/603), [#611](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/611), [#841](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/841) Introduction of TS.DEL which allows deleting samples in a time series within two timestamps (inclusive).\n\n- [#762](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/762) Limit the number of returned labels in the response of read commands (`TS.MRANGE`, `TS.MREVRANGE`, and `TS.MGET`) using `SELECTED_LABELS`. This can be a significant performance improvement when returning a large number of series.\n\n- [#655](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/655), [#801](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/801) Ability to align the aggregation buckets with the requested start, end, or specific timestamp on aggregation queries using `ALIGN` (`TS.RANGE`, `TS.REVRANGE`, `TS.MRANGE`, and `TS.MREVRANGE`).\n\n- [#675](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/675) Add keyspace notifications for all CRUD commands. Check out [this test](https://github.com/RedisTimeSeries/RedisTimeSeries/blob/master/tests/flow/test_ts_keyspace.py) for the details.\n\n- [#882](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/882) [Auto Tiering ](https://docs.redis.com/latest/rs/databases/redis-on-flash//#:~:text=Redis%20on%20Flash%20%20offers,dedicated%20flash%20memory%20(SSD).) support."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes","title":"RedisTimeSeries 1.8 release notes","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes/","summary":"Added a time-weighted average aggregator, gap filling, ability to control bucket timestamps, ability to control alignment for compaction rules, new reducer types, and ability to include the latest (possibly partial) raw bucket samples when retrieving compactions","tags":["docs","operate","stack"],"last_updated":"2026-10-02T14:53:57+01:00","page_type":"content","content_hash":"415d288e68360de2a7d5f37d245e224a57f611c4a0651406fcbbb528ba711877","sections":[{"id":"requirements","title":"Requirements","role":"overview","text":"RedisTimeSeries v1.8.26 requires:\n\n- Minimum Redis compatibility version (database): 6.0.16\n- Minimum Redis Enterprise Software version (cluster): 6.2.8"},{"id":"v1826-sep-2026","title":"v1.8.26 (Sep 2026)","role":"content","text":"This is a maintenance release for RedisTimeSeries 1.8.\n\nUpdate urgency: `SECURITY`: There are security fixes in the release.\n\nDetails:\n\n- **Security:**\n  - Prevent heap buffer overflows and invalid memory access when restoring malformed time-series data.\n\n- Bug fixes:\n  - Fix `TS.INCRBY` replication to preserve the primary’s timestamp when using server-generated timestamps."},{"id":"v1823-may-2026","title":"v1.8.23 (May 2026)","role":"content","text":"This is a maintenance release for RedisTimeSeries 1.8.\n\nUpdate urgency: `SECURITY`: There are security fixes in the release.\n\nDetails:\n\n- **Security:**\n\n  - (CVE-2026-25588) Invalid memory access in `RESTORE` may lead to Remote Code Execution."},{"id":"v1819-october-2025","title":"v1.8.19 (October 2025)","role":"content","text":"This is a maintenance release for RedisTimeSeries 1.8.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\nBug fixes:\n\n- [#1776](https://github.com/redistimeseries/redistimeseries/pull/1776) Potential crash on `TS.RANGE` with `ALIGN +`, `AGGREGATION twa` and `EMPTY` (MOD-11620, MOD-10484).\n\nImprovements:\n\n- Added support for Rocky Linux 9 and RHEL9 ARM."},{"id":"v1817-april-2025","title":"v1.8.17 (April 2025)","role":"content","text":"This is a maintenance release for RedisTimeSeries 1.8.\n\nUpdate urgency: `MODERATE`: Plan an upgrade of the server, but it's not urgent.\n\nDetails:\n\nBug fixes:\n- [#1725](https://github.com/redistimeseries/redistimeseries/pull/1725) `TS.DEL` crashes on keys with compactions if the deletion removes the last compaction bucket (MOD-8936)\n- [LibMR#58](https://github.com/RedisGears/LibMR/pull/58) Crash when a cluster contains both 1.8 and newer nodes (MOD-8976, MOD-9192)\n\nImprovements:\n- [#1712](https://github.com/redistimeseries/redistimeseries/pull/1712) Added support for Azure Linux 3 (MOD-9170)\n- [#1736](https://github.com/redistimeseries/redistimeseries/pull/1736) (Redis Enterprise only) Cross-key commands are handled by a random shard rather than the first shard (MOD-9262, MOD-9314)"},{"id":"v1816-january-2025","title":"v1.8.16 (January 2025)","role":"content","text":"This is a maintenance release for RedisTimeSeries 1.8.\n\nUpdate urgency: `SECURITY`: There are security fixes in the release.\n\nDetails\n\n- **Security and privacy:**\n  - [#1672](https://github.com/redistimeseries/redistimeseries/pull/1672) (CVE-2024-51480) `TS.QUERYINDEX`, `TS.MGET`, `TS.MRANGE`, `TS.MREVRANGE` - potential integer overflow leading to an out-of-bounds write (MOD-7548)\n\n- Improvements:\n  - [#1664](https://github.com/redistimeseries/redistimeseries/pull/1664) Added support for Ubuntu 22 and macOS 13 and 14"},{"id":"v1813-march-2024","title":"v1.8.13 (March 2024)","role":"content","text":"This is a maintenance release for RedisTimeSeries 1.8.\n\nUpdate urgency: `LOW`: No need to upgrade unless there are new features you want to use."},{"id":"details","title":"Details","role":"content","text":"- Bug fixes:\n\n  - [LibMR#51](https://github.com/RedisGears/LibMR/pull/51) Crash on SSL initialization failure (MOD-5647)\n\n- Improvements:\n\n  - [#1593](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1593) More detailed LibMR error messages"},{"id":"v1812-december-2023","title":"v1.8.12 (December 2023)","role":"content","text":"This is a maintenance release for RedisTimeSeries 1.8.\n\nUpdate urgency: `SECURITY`: There are security fixes in the release.\n\nDetails:\n\n- Security and privacy:\n\n  - [#1506](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1506) Don’t expose internal commands (MOD-5643)\n\n- Bug fixes:\n\n  - [#1494](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/1494) Potential crash when using an invalid argument value\n\n- Improvements:\n\n  - [#1516](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1516) Added support for CBL-Mariner 2"},{"id":"v1811-july-2023","title":"v1.8.11 (July 2023)","role":"content","text":"This is a maintenance release for RedisTimeSeries 1.8.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n    - [#1486](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/1486) When using `LATEST`, results may contain samples earlier than `fromTimestamp` (`TS.RANGE`, `TS.REVRANGE`, `TS.MRANGE`, and `TS.MREVRANGE`)\n    - [#1471](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1471) Potential crash on `TS.MRANGE` when aggregating millions of time series\n    - [#1469](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1469) Potential memory leak in `TS.MRANGE` after eviction\n\n- Performance enhancements:\n\n    - [#1476](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/1476) Significant performance improvement when using multiple label filters (`TS.MGET`, `TS.MRANGE`, `TS.MREVRANGE`, and `TS.QUERYINDEX`) (MOD-5338)"},{"id":"v1810-april-2023","title":"v1.8.10 (April 2023)","role":"content","text":"This is a maintenance release for RedisTimeSeries 1.8.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n    - [#1455](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/1455) `TS.ADD` - optional arguments are not replicated (MOD-5110)"},{"id":"v189-march-2023","title":"v1.8.9 (March 2023)","role":"content","text":"This is a maintenance release for RedisTimeSeries 1.8.\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n    - [#1421](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/1421) Potential crash after deleting from a time series with an `AVG` compaction (MOD-4972)\n    - [#1422](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/1422) Incorrectly return an error when deleting from a time series with a compaction and with no expiry"},{"id":"v188-march-2023","title":"v1.8.8 (March 2023)","role":"content","text":"This is a maintenance release for RedisTimeSeries 1.8\n\nUpdate urgency: `MODERATE`: Program an upgrade of the server, but it's not urgent.\n\nDetails:\n\n- Bug fixes:\n\n    - [#1290](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/1290) Potential crash when using `FILTER_BY_TS`\n    - [#1397](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/1397) Memory leak when trying to create an already existing key (MOD-4724, RED-93418)"},{"id":"v185-january-2023","title":"v1.8.5 (January 2023)","role":"content","text":"This is a maintenance release for RedisTimeSeries 1.8.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails:\n\n- Bug fixes:\n\n    - [#1388](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1388) Potential crash when upgrading from v1.6 to 1.8 if there are compactions with `min` or `max` aggregation (MOD-4695)"},{"id":"v184-december-2022","title":"v1.8.4 (December 2022)","role":"content","text":"This is a maintenance release for RedisTimeSeries 1.8.\n\nUpdate urgency: `HIGH`: There is a critical bug that may affect a subset of users. Upgrade!\n\nDetails:\n\n- Bug fixes:\n\n    - [#1360](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1360) Potential crash when upgrading from v1.6 to 1.8 if there are compactions with `min` or `max` aggregation (MOD-4559)\n    - [#1370](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/1370) Potential crash when using [TS.REVRANGE](https://redis.io/docs/latest/commands/ts.revrange) or [TS.MREVRANGE](https://redis.io/docs/latest/commands/ts.mrevrange) with aggregation\n    - [#1347](https://github.com/RedisTimeSeries/RedisTimeSeries/pull/1347) When adding samples with [TS.ADD](https://redis.io/docs/latest/commands/ts.add) or [TS.MADD](https://redis.io/docs/latest/commands/ts.madd) using  `*` as timestamp, the timestamp could differ between master and replica shards\n\n- Improvements:\n    - [#1215](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/1215) OSS cluster: Support TLS and IPv6; introduce new configuration parameter: [OSS_GLOBAL_PASSWORD](https://redis.io/docs/latest/develop/data-types/timeseries/configuration#oss_global_password)"},{"id":"v18-ga-v183-november-2022","title":"v1.8 GA (v1.8.3) (November 2022)","role":"content","text":"This is the General Availability release of RedisTimeSeries 1.8."},{"id":"highlights","title":"Highlights","role":"content","text":"RedisTimeSeries 1.8 introduces seven highly requested features, performance improvements, and bug fixes."},{"id":"whats-new-in-18","title":"What's new in 1.8","role":"content","text":"* **Optionally retrieving aggregation results for the latest (still open) bucket for compactions**\n\n  Till version 1.8, when a time series is a compaction, [TS.GET](https://redis.io/docs/latest/commands/ts.get), [TS.MGET](https://redis.io/docs/latest/commands/ts.mget), [TS.RANGE](https://redis.io/docs/latest/commands/ts.range), [TS.REVRANGE](https://redis.io/docs/latest/commands/ts.revrange), [TS.MRANGE](https://redis.io/docs/latest/commands/ts.mrange), [TS.MREVRANGE](https://redis.io/docs/latest/commands/ts.mrevrange) did not report the compacted value of the latest bucket. The reason is that the data in the latest bucket of a compaction is still partial. A bucket is ‘closed’ and compacted only upon the arrival of data that ‘opens’ a ‘new latest’ bucket.\n\n  There are use cases, however, where the compaction of the latest bucket should be retrieved as well. For example, a user may want to receive the count of events since the start of the decade, and the retention period for raw data is only one month. Till version 1.8, the user would have to run two queries - one on a compaction and one on the latest raw data, and then sum the results. Since version 1.8, by specifying `LATEST`, it is possible to retrieve the latest (possibly partial) bucket as well.\n\n  To report aggregations for the latest bucket, use the new optional `LATEST` flag to [TS.GET](https://redis.io/docs/latest/commands/ts.get), [TS.MGET](https://redis.io/docs/latest/commands/ts.mget), [TS.RANGE](https://redis.io/docs/latest/commands/ts.range), [TS.REVRANGE](https://redis.io/docs/latest/commands/ts.revrange), [TS.MRANGE](https://redis.io/docs/latest/commands/ts.mrange), and [TS.REVRANGE](https://redis.io/docs/latest/commands/ts.mrevrange).\n\n* **Optionally retrieving aggregation results for empty buckets**\n\n  The commands [TS.RANGE](https://redis.io/docs/latest/commands/ts.range), [TS.REVRANGE](https://redis.io/docs/latest/commands/ts.revrange), [TS.MRANGE](https://redis.io/docs/latest/commands/ts.mrange), and [TS.MREVRANGE](https://redis.io/docs/latest/commands/ts.mrevrange) have an optional `[AGGREGATION aggregator bucketDuration]` parameter. When this parameter is specified, raw reports are aggregated per bucket.\n\n  Till version 1.8, results were not reported for empty buckets. With `EMPTY`, it is now possible to report aggregations for empty buckets as well.\n\n  The two primary reasons for wanting to retrieve values for empty buckets:\n\n  - It is easier to align results from similar queries over multiple time series\n\n  - It is easier to use the retrieved results with some external tools (e.g., charting tools)\n\n  For the `sum` and `count` aggregators, the value 0 is reported for empty buckets.\n\n  For the `min`, `max`, `range`, `avg`, `first`, `std.p`, and `std.s` aggregators, the value NaN (not a number) is reported.\n\n  For the `last` aggregator and the new `twa` aggregator, the `EMPTY` flag is used for gap filling (see below).\n\n  To report aggregations for empty buckets, use the new optional `EMPTY` flag in [TS.RANGE](https://redis.io/docs/latest/commands/ts.range), [TS.REVRANGE](https://redis.io/docs/latest/commands/ts.revrange), [TS.MRANGE](https://redis.io/docs/latest/commands/ts.mrange), and [TS.MREVRANGE](https://redis.io/docs/latest/commands/ts.mrevrange).\n\n  Regardless of the values of `fromTimestamp` and `toTimestamp`, no data is reported for empty buckets that end before the earliest sample or begin after the latest sample in the time series.\n\n* **A new aggregator: time-weighted average**\n\n  When a time series holds discrete samples of a continuous signal (e.g., temperature), using `avg` to estimate the average value over a given timeframe would produce a good estimate only when the signal is sampled at constant intervals. If, however, samples are available at non-constant intervals (e.g., when some samples are missing), the `twa` aggregator produces a more accurate estimate by averaging samples over time instead of simply averaging the samples.\n\n  ![A graph showing the difference between average and time-weighted average.](https://redis.io/docs/latest/images/modules/twa.png)\n\n  This is an extreme example: the signal in the diagram above has 4 samples in its ‘high’ value and 13 samples in its ‘low’ value. However, the period in each of those states is about the same. It is easy to see that the simple average (avg) of all the 17 samples does not represent the signal’s average over time.\n\n  Time-weighted average (`twa`) adds weight to each sample. The weight is proportional to the time interval that the sample represents. In the diagram, the time-weighted average over the whole period assigns appropriate weight to each sample, so the result represents the signal’s average value over the whole period much more accurately.\n\n* **Gap filling: optionally interpolate or repeat last value for empty buckets**\n\n  Gap filling is performed when using `EMPTY` together with either `last` or `twa` aggregator.\n\n  Using `EMPTY` with the `twa` aggregator allows us to estimate the average of a continuous signal even for buckets where no samples were collected (gap-filling).\n\n  ![A graph that illustrates gap-filling.](https://redis.io/docs/latest/images/modules/GapFilling.png)\n\n  Consider we want to use [TS.RANGE](https://redis.io/docs/latest/commands/ts.range) to calculate the average value of each bucket (p1, p2, p3 in the diagram above). Using avg, the value reported for bucket p2 would be NaN, as this bucket contains no samples. If we use `EMPTY` with `twa`, on the other hand, the average value for bucket p2 would be calculated based on the linear interpolation of the value left of p2 and the value right of p2.\n\n  When sampling a continuous signal, we can use this ‘gap-filling’ capability to calculate the average value of the signal over equal-width buckets without concern about bucket alignment or missing samples.\n\n  Using `EMPTY` with the last aggregator allows filling empty buckets by repeating the value of the previous sample. This is useful, for example, when values in the time series represent stock prices and the price has not been changed during a bucket’s timeframe.\n\n* **Ability to control how bucket timestamps are reported**\n\n  Till version 1.8, [TS.RANGE](https://redis.io/docs/latest/commands/ts.range), [TS.REVRANGE](https://redis.io/docs/latest/commands/ts.revrange), [TS.MRANGE](https://redis.io/docs/latest/commands/ts.mrange), and [TS.MREVRANGE](https://redis.io/docs/latest/commands/ts.mrevrange) returned the start time of each reported bucket as its timestamp.\n\n  Changing this behavior and reporting each bucket’s start time, end time, or mid-time is now possible. This is required in many use cases. For example, when drawing bars in trading applications, annotating each bar with the end timestamp of the bucket it represents is very common.\n\n  The way bucket timestamps are reported can be specified with the new optional `BUCKETTIMESTAMP` parameter of [TS.RANGE](https://redis.io/docs/latest/commands/ts.range), [TS.REVRANGE](https://redis.io/docs/latest/commands/ts.revrange), [TS.MRANGE](https://redis.io/docs/latest/commands/ts.mrange), and [TS.MREVRANGE](https://redis.io/docs/latest/commands/ts.mrevrange).\n\n  `-` or `low`: the bucket's start time (default)\n\n  `+` or `high`: the bucket's end time\n\n  `~` or `mid`: the bucket's mid-time (rounded down if not an integer)\n\n* **Ability to control alignment for compaction rules**\n\n  Till version 1.8, compaction rules could not be aligned. One could specify a compaction rule with 24-hour buckets, and as a result, each bucket would aggregate events from midnight till the next midnight. The first bucket always started at the epoch and all other buckets were aligned accordingly.\n\n  But what if we want to aggregate daily events from 06:00 to 06:00 the next day? We can now specify alignment for compaction rules.\n\n  Alignment can be specified with the new optional `alignTimestamp` parameter of [TS.CREATERULE](https://redis.io/docs/latest/commands/ts.createrule) and the [COMPACTION_POLICY](https://redis.io/docs/latest/develop/data-types/timeseries/configuration) configuration parameter. Specifying `alignTimestamp` ensures that there is a bucket that starts exactly at `alignTimestamp` and all other buckets are aligned accordingly. `alignTimestamp` is expressed in milliseconds. The default value is 0 (aligned with the epoch).\n\n* **New reducers**\n\n  Till version 1.8, only the `sum`, `min`, and `max` could be used as reducer types.\n\n  It is now possible, for example, to calculate the maximal temperature per timeframe for each sensor and then report the average temperature (avg reducer) over groups of sensors (grouped by a given label's value).\n\n  This can be specified with the new reducer types ([TS.MRANGE](https://redis.io/docs/latest/commands/ts.mrange) and [TS.MREVRANGE](https://redis.io/docs/latest/commands/ts.mrevrange): `avg`, `range`, `count`, `std.p`, `std.s`, `var.p`, and `var.s`.\n\n> [!NOTE]\n> New RDB version (v7). RDB files created with v1.8.3 are not backward compatible."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/search/commands","title":"Redis Search commands","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/commands/","summary":"Lists Redis Search commands and provides links to the command reference pages.","tags":["docs","operate","stack"],"last_updated":"2026-09-22T10:15:10+01:00","page_type":"content","content_hash":"fa58b20eb86110c74130b5bb782da9ba1b6f931e475df7c3fa4450b1679cfea4","sections":[{"id":"content","title":"Content","role":"content","text":"The following table lists Redis Search commands. See the command links for more information about each command's syntax, arguments, and examples.\n\n| Command | Redis Software | Redis Cloud<br />Flexible & Annual | Redis Cloud<br />Free & Fixed | Description |\n|:--------|:----------------------|:-----------------|:-----------------|:------|\n| [FT.AGGREGATE](https://redis.io/docs/latest/commands/ft.aggregate) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> | Runs a search query on an index and groups, sorts, transforms, limits, and/or filters the results. |\n| [FT.ALIASADD](https://redis.io/docs/latest/commands/ft.aliasadd) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> | Adds an alias to an index.  |\n| [FT.ALIASDEL](https://redis.io/docs/latest/commands/ft.aliasdel) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> | Removes an alias from an index. |\n| [FT.ALIASLIST](https://redis.io/docs/latest/commands/ft.aliaslist) | <span title=\"Not supported\">&#x274c; Not supported</span> | <span title=\"Not supported\">&#x274c; Not supported</span> | <span title=\"Not supported\">&#x274c; Not supported</nobr></span> | List aliases for an index. |\n| [FT.ALIASUPDATE](https://redis.io/docs/latest/commands/ft.aliasupdate) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> | Adds an alias to an index. If the alias already exists for a different index, it updates the alias to point to the specified index instead. |\n| [FT.ALTER](https://redis.io/docs/latest/commands/ft.alter) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> | Adds a new field to an index. |\n| [FT.CONFIG GET](https://redis.io/docs/latest/commands/ft.config-get) |  <span title=\"Not supported\"><nobr>&#x274c; Not supported</span> | <span title=\"Not supported\"><nobr>&#x26A0;&#xFE0F; Not supported</span><sup>[2](#table-note-2)</sup> | <span title=\"Not supported\"><nobr>&#x274c; Not supported</nobr></span> | Displays configuration options. |\n| [FT.CONFIG HELP](https://redis.io/docs/latest/commands/ft.config-help) |   <span title=\"Not supported\"><nobr>&#x274c; Not supported</span> | <span title=\"Not supported\"><nobr>&#x274c; Not supported</span> | <span title=\"Not supported\"><nobr>&#x274c; Not supported</nobr></span> | Describes configuration options. |\n| [FT.CONFIG SET](https://redis.io/docs/latest/commands/ft.config-set) | <span title=\"Not supported\"><nobr>&#x26A0;&#xFE0F; Not supported</span><sup>[1](#table-note-1)</sup> | <span title=\"Not supported\"><nobr>&#x26A0;&#xFE0F; Not supported</span><sup>[2](#table-note-2)</sup> | <span title=\"Not supported\"><nobr>&#x274c; Not supported</nobr></span> | Sets configuration options. |\n| [FT.CREATE](https://redis.io/docs/latest/commands/ft.create) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> | Creates an index. |\n| [FT.CURSOR DEL](https://redis.io/docs/latest/commands/ft.cursor-del) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> | Deletes a cursor. |\n| [FT.CURSOR&nbsp;READ](https://redis.io/docs/latest/commands/ft.cursor-read) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> | Reads the next results from an existing cursor. |\n| [FT.DICTADD](https://redis.io/docs/latest/commands/ft.dictadd) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> | Adds terms to a dictionary. |\n| [FT.DICTDEL](https://redis.io/docs/latest/commands/ft.dictdel) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> | Deletes terms from a dictionary. |\n| [FT.DICTDUMP](https://redis.io/docs/latest/commands/ft.dictdump) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> | Returns all terms in the specified dictionary. |\n| [FT.DROPINDEX](https://redis.io/docs/latest/commands/ft.dropindex) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> | Deletes an index. |\n| [FT.EXPLAIN](https://redis.io/docs/latest/commands/ft.explain) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> | Returns the execution plan for a complex query as a string. |\n| [FT.EXPLAINCLI](https://redis.io/docs/latest/commands/ft.explaincli) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> | Returns the execution plan for a complex query as an [array](https://redis.io/docs/latest/develop/reference/protocol-spec#arrays). |\n| [FT.HYBRID](https://redis.io/docs/latest/commands/ft.hybrid) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> | Performs hybrid search combining text search and vector similarity with configurable fusion methods. |\n| [FT.INFO](https://redis.io/docs/latest/commands/ft.info) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> | Returns index information and statistics.  |\n| [FT._LIST](https://redis.io/docs/latest/commands/ft._list) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> | Lists all indexes. |\n| [FT.PROFILE](https://redis.io/docs/latest/commands/ft.profile) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> | Runs [FT.SEARCH](https://redis.io/docs/latest/commands/ft.search) or [FT.AGGREGATE](https://redis.io/docs/latest/commands/ft.aggregate) and reports performance information. |\n| [FT.SEARCH](https://redis.io/docs/latest/commands/ft.search) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> | Searches an index for a text query and returns matching documents or document IDs. |\n| [FT.SPELLCHECK](https://redis.io/docs/latest/commands/ft.spellcheck) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> | Suggests spelling corrections for misspelled terms in a query. |\n| [FT.SYNDUMP](https://redis.io/docs/latest/commands/ft.syndump) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> | Returns a list of synonym terms and their synonym group IDs. |\n| [FT.SYNUPDATE](https://redis.io/docs/latest/commands/ft.synupdate) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> | Creates or updates a synonym group with additional terms. |\n| [FT.TAGVALS](https://redis.io/docs/latest/commands/ft.tagvals) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</nobr></span> | Returns all distinct values indexed in a tag field. |\n\n1. <a name=\"table-note-1\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>Use [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin) or the [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api) to change Redis Search configuration for Redis Software. See [Redis Search configuration compatibility with Redis Software](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/config) for more information and examples.\n\n2. <a name=\"table-note-2\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>[Contact support](https://redis.com/company/support/) to view the current configuration values or request configuration changes for Flexible or Annual Redis Cloud subscriptions."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/search/config","title":"Redis Search configuration compatibility with Redis Software","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/config/","summary":"Redis Search configuration settings supported by Redis Software and Redis Cloud.","tags":["docs","operate","stack"],"last_updated":"2026-10-02T14:53:57+01:00","page_type":"content","content_hash":"00a585b7a12237c6534fdfc3654e44fc0c1bfdf1d5c4a22d94d5fca0a7bf67e7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"To configure Redis Search in [Redis Software](https://redis.io/docs/latest/operate/rs) or [Redis Cloud](https://redis.io/docs/latest/operate/rc), use one of the following methods instead of [`FT.CONFIG SET`](https://redis.io/docs/latest/commands/ft.config-set)."},{"id":"configure-search-in-redis-cloud","title":"Configure search in Redis Cloud","role":"configuration","text":"For Redis Cloud:\n\n- _Flexible or Annual [subscriptions](https://redis.io/docs/latest/operate/rc/subscriptions)_: contact [support](https://redis.com/company/support/) to request a configuration change.\n    \n- _Free or Fixed subscriptions_: you cannot change Redis Search configuration."},{"id":"configure-search-in-redis-software","title":"Configure search in Redis Software","role":"configuration","text":"For Redis Software, use one of the following methods:\n\n- Cluster Manager UI:\n\n  1. From the **Databases** list, select the database, then click **Configuration**.\n\n  1. Select the **Edit** button.\n\n  1. In the **Capabilities** section, click **Parameters**.\n\n  1. Enter the setting name and setting value in the **RediSearch** box.\n  \n      In the **Query Performance Factor** section, you can configure settings to improve query performance. See [Configure the query performance factor for Redis Search in Redis Software](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/query-performance-factor) for more information.\n\n      ![The Parameters dialog includes sections to edit RediSearch settings and the Query Performance Factor settings.](https://redis.io/docs/latest/images/rs/screenshots/databases/rs-config-search-params.png)\n\n  1. After you finish editing the module's configuration parameters, click **Done** to close the parameter editor.\n\n  1. Click **Save**.\n\n- [`rladmin tune db`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-db):\n\n    [code example]\n\n- [Configure module](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/modules/config) REST API request:\n\n    [code example]"},{"id":"configuration-settings","title":"Configuration settings","role":"configuration","text":"See [configuration parameters](https://redis.io/docs/latest/develop/ai/search-and-query/administration/configuration) in the Develop section for parameter details and compatibility with Redis Software and Redis Cloud."}],"examples":[{"id":"configure-search-in-redis-software-ex0","language":"sh","code":"$ rladmin tune db db:<ID|name> module_name search \\\n        module_config_params \"setting-name setting-value\"","section_id":"configure-search-in-redis-software"},{"id":"configure-search-in-redis-software-ex1","language":"sh","code":"POST /v1/modules/config/bdb/<ID>\n    {\n      \"modules\": [\n        {\n          \"module_name\": \"search\",\n          \"module_args\": \"setting-name setting-value\"\n        }\n      ]\n    }","section_id":"configure-search-in-redis-software"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/search","title":"Redis Search","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/","summary":"","aliases":["/stack/search/"],"tags":["docs","operate","stack"],"last_updated":"2026-09-22T10:15:10+01:00","children":[{"id":"operate/oss_and_stack/stack-with-enterprise/search/commands","summary":"Lists Redis Search commands and provides links to the command reference pages.","title":"Redis Search commands","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/commands/"},{"id":"operate/oss_and_stack/stack-with-enterprise/search/config","summary":"Redis Search configuration settings supported by Redis Software and Redis Cloud.","title":"Redis Search configuration compatibility with Redis Software","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/config/"},{"id":"operate/oss_and_stack/stack-with-enterprise/search/query-performance-factor","summary":"Configure the query performance factor for Redis Search in Redis Software to increase the performance of queries.","title":"Configure the query performance factor for Redis Search in Redis Software","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/query-performance-factor/"},{"id":"operate/oss_and_stack/stack-with-enterprise/search/scalable-query-best-practices","summary":"Best practices for scalable Redis Search in Redis Software and Redis Cloud.","title":"Best practices for scalable Redis Search","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/scalable-query-best-practices/"},{"id":"operate/oss_and_stack/stack-with-enterprise/search/search-active-active","summary":"Search and query Active-Active databases.","title":"Search and query Active-Active databases","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/search-active-active/"},{"id":"operate/oss_and_stack/stack-with-enterprise/search/redisearch-2-upgrade","summary":"Upgrade a database from Search 1.x to Search 2.x.","title":"Upgrade a database to Search 2.x","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/redisearch-2-upgrade/"}],"page_type":"content","content_hash":"3e8fc76d211ba7faff4afbd16db1f4511bb1badd32d33477eb057d83dc243114","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The [RediSearch 2.x module](https://redis.com/blog/introducing-redisearch-2-0/) is a source-available project that lets you build powerful search queries for open source Redis databases.\nWhen combined with Redis Software, you can use the same protocols and [commands](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/commands)\nto run geo-replicated queries and full-text searches over efficient in-memory indexes."},{"id":"index-documents","title":"Index documents","role":"content","text":"Redis Search indexes documents, which are objects that represent data as field-value pairs. You can index more than one field per document, and these fields can represent text, numeric, or geospatial data types.\n\nAs the documents in your database change, the index automatically processes these changes to keep the search results up to date.\n\nWith indexes, you can do:\n- Language-aware [fuzzy matching](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/query_syntax#fuzzy-matching)\n- Fast [auto-complete](https://redis.io/docs/latest/develop/ai/search-and-query/administration/overview#auto-complete)\n- [Exact phrase matching](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/query_syntax)\n- [Numeric filtering](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/query_syntax#numeric-filters-in-query)\n- [Geo-radius queries](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/query_syntax#geo-filters)"},{"id":"supported-document-types","title":"Supported document types","role":"compatibility","text":"You can store documents as Redis [hashes](https://redis.io/docs/latest/develop/data-types/hashes) or [JSON](https://redis.io/docs/latest/develop/data-types/json). To search and query JSON documents, you also need to enable [JSON](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json) in your database."},{"id":"hash-documents","title":"Hash documents","role":"content","text":"With Redis [hashes](https://redis.io/docs/latest/develop/data-types/hashes), each document is assigned to a single key and uses field-value pairs to represent the document's contents.\n\nYou can run [`HGETALL`](https://redis.io/docs/latest/commands/hgetall) to retrieve the entire hash document."},{"id":"json-documents","title":"JSON documents","role":"content","text":"You can index, search, and query JSON documents stored in your database.\n\nFor more information about how to search and query JSON documents, see the [quick start](https://redis.io/docs/latest/develop/get-started/search-tutorial)."},{"id":"search-features","title":"Search features","role":"content","text":"For full-text searches, you can customize the field queries and ranking of the search results.\nWhen querying, you can use multiple predicates that query text, numeric, and geospatial fields in one query.\nYou can also sort by a specific field and limit the results with an offset to produce customized results pages.\n\nRedis Open Source supports [over 15 natural languages](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/stemming) for stemming and includes auto-complete engines with specific commands that can provide real-time [interactive search suggestions](https://redis.io/docs/latest/commands/ft.sugadd)."},{"id":"redis-search-in-active-active-databases","title":"Redis Search in Active-Active databases","role":"content","text":"As a result of the new RediSearch architecture and methodology, [RediSearch 2.x supports Active-Active databases](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/search-active-active).\nYou can now serve your index information from geo-distributed database instances."},{"id":"resharding-indexed-data","title":"Resharding indexed data","role":"content","text":"By moving the index out of the keyspace and structuring the data as hashes, RediSearch 2.x makes it possible to reshard the database.\nWhen half of the data moves to the new shard, the index related to that data is created synchronously and Redis removes the keys from the index when it detects that the keys were deleted.\nBecause the index on the new shard is created synchronously though, it's expected that the resharding process will take longer than resharding of a database without Redis Search enabled."},{"id":"limitations","title":"Limitations","role":"limits","text":"- You cannot use Redis Search capabilities with the [OSS Cluster API](https://redis.io/docs/latest/operate/rs/databases/configure/oss-cluster-api). This limitation was fixed in Redis Software version 8.0."},{"id":"more-info","title":"More info","role":"content","text":"- [Getting Started with RediSearch 2.0](https://redis.com/blog/getting-started-with-redisearch-2-0/)\n- [Redis Search quick start](https://redis.io/docs/latest/develop/get-started/search-tutorial)\n- [Redis Search configuration](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/config)\n- [Redis Search commands](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/commands)\n- [Redis Search references](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts)\n- [RediSearch source](https://github.com/RediSearch/RediSearch)"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/search/query-performance-factor","title":"Configure the query performance factor for Redis Search in Redis Software","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/query-performance-factor/","summary":"Configure the query performance factor for Redis Search in Redis Software to increase the performance of queries.","aliases":["/operate/oss_and_stack/stack-with-enterprise/search/scalable-search/","/operate/oss_and_stack/stack-with-enterprise/search/query-performance-factor/"],"tags":["docs","operate","stack"],"last_updated":"2026-10-02T14:53:57+01:00","page_type":"content","content_hash":"be059fd3c8dbab5c09ffaee5ef855cf1b74d5302c856764e23da7026bad6e6b9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Query performance factors are intended to increase the performance of queries, including [vector search](https://redis.io/docs/latest/develop/ai/search-and-query/query/vector-search). When enabled, it allows you to increase a database's compute capacity and query throughput by allocating more virtual CPUs per shard. This is in addition to horizontal scaling with more shards which enables a higher throughput of key value operations. This document describes how to configure the query performance factor.\n\n> [!NOTE]\n> Some use cases might not scale effectively. Redis experts can help determine if vertical scaling with Redis Search will boost performance for your use case and guide you on whether to use vertical scaling, horizontal scaling, or both."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Redis Search requires a cluster running Redis Software version 7.4.2-54 or later. For the simplified configuration experience (no shard restart required, new UI, and new REST API), Redis Software version 8.x or later is required.\n\nIf you do not have a cluster that supports Redis Search, [install Redis Software](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/install-on-linux) version 7.4.2-54 or later on a new cluster, or [upgrade an existing cluster](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-cluster)."},{"id":"sizing","title":"Sizing","role":"content","text":"1. Calculate the hardware requirements for your Redis database:\n\n    1. Use the [hardware requirements documentation](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/hardware-requirements) to derive the overall cluster architecture.\n\n    1. Calculate the RAM requirements using the [Index Size Calculator](https://redis.io/redisearch-sizing-calculator/). The total RAM required is the sum of the dataset and index sizes.\n\n1. [Determine the query performance factor](#calculate-query-performance-factor) you want and the required number of CPUs. Unused CPUs, above the 20% necessary for Redis, can be used for the scalable Redis Search.\n\n1. Create a new Redis database with the number of CPUs configured for the query performance factor."},{"id":"calculate-query-performance-factor","title":"Calculate query performance factor","role":"content","text":""},{"id":"cpus-for-query-performance-factor","title":"CPUs for query performance factor","role":"content","text":"Vertical scaling of Redis Search is achieved by provisioning additional CPUs for the RediSearch module. At least 20% of the available CPUs must be reserved for Redis internal processing. Use the following formula to define the maximum number of CPUs that can be allocated to search.\n\n| Variable | Value |\n|----------|-------|\n| CPUs per node | x |\n| Redis internals | 20% |\n| Available CPUs for Redis Search | floor(0.8 * x) |"},{"id":"query-performance-factor-versus-cpus","title":"Query performance factor versus CPUs","role":"content","text":"The following table shows the number of CPUs required for each performance factor. This calculation is sensitive to how the search index and queries are defined. Certain scenarios might yield less throughput than the ratios in the following table.\n\n| Scale factor | Minimum CPUs required for Redis Search <WORKERS> |\n|----------------|-----------------------------------------|\n| None (default) | 1 |\n| 2 | 3 |\n| 4 | 6 |\n| 6 | 9 |\n| 8 | 12 |\n| 10 | 15 |\n| 12 | 18 |\n| 14 | 21 |\n| 16 | 24 |"},{"id":"example-performance-factor-calculation","title":"Example performance factor calculation","role":"example","text":"| Variable | Value |\n|----------|-------|\n| CPUs per node | 8 |\n| Available CPUs | floor(0.8 * 8)=6 |\n| Scale factor | 4x |\n| Minimum CPUs required for scale factor - WORKERS | 6 |"},{"id":"configure-query-performance-factor-in-the-cluster-manager-ui","title":"Configure query performance factor in the Cluster Manager UI","role":"configuration","text":""},{"id":"config-db-ui-v8","title":"Redis Software 8.x and later","role":"content","text":"Starting with Redis Software 8.x, configuring the query performance factor is simplified. You can select the query performance factor directly from the Cluster Manager UI without manually setting connection routing, connection limits, or module parameters. Changes to the query performance factor for existing databases take effect immediately without restarting shards.\n\nTo configure the query performance factor:\n\n1. In the Cluster Manager UI, [create a new database](https://redis.io/docs/latest/operate/rs/databases/create) or [edit an existing database](https://redis.io/docs/latest/operate/rs/databases/configure#edit-database-settings).\n\n1. In the **Capabilities** section, click **Add parameters**.\n\n    ![The Create database screen with the Add parameters button in the Capabilities section.](https://redis.io/docs/latest/images/rs/screenshots/databases/rs-qpf-add-parameters-v8.png)\n\n1. In the **Parameters** dialog, use the **Query performance factor** dropdown to select the performance factor value (Standard, 2x, 4x, 6x, 8x, 10x, 12x, 14x, or 16x).\n\n    ![The Parameters dialog with the Query performance factor dropdown showing available scaling options.](https://redis.io/docs/latest/images/rs/screenshots/databases/rs-qpf-select-factor-v8.png)\n\n1. Click **Done** to close the parameter editor.\n\n1. Click **Create** or **Save**.\n\nThe new configuration applies automatically. No shard restart is required, even for existing databases."},{"id":"config-db-ui-prior","title":"Prior to Redis Software 8","role":"content","text":"To manually configure the query performance factor in Redis Software versions prior to 8.x:\n\n1. [Configure query performance factor parameters](#config-db-ui-params) when you create a new database or edit an existing database's configuration in the Cluster Manager UI.\n\n1. If you configure the query performance factor for an existing database, you also need to [restart shards](#restart-shards). Newly created databases can skip this step.\n\n#### Configure query performance factor parameters {#config-db-ui-params}\n\nYou can use the Cluster Manager UI to configure the query performance factor when you [create a new database](https://redis.io/docs/latest/operate/rs/databases/create) or [edit an existing database](https://redis.io/docs/latest/operate/rs/databases/configure#edit-database-settings) with search enabled.\n\n1. In the **Capabilities** section of the database configuration screen, click **Parameters**.\n\n1. If you are creating a new database, select **Search and query**.\n\n1. Adjust the **RediSearch** parameters to include: \n\n    `WORKERS <NUMBER_OF_THREADS>`\n\n    See [Calculate query performance factor](#calculate-query-performance-factor) to determine the minimum CPUs required to use for `<NUMBER_OF_THREADS>`.\n    \n1. Expand the **Query Performance Factor** section and enter the following values:\n\n    - `mnp` for **Connections routing**\n\n    - `32` for **Connections limit**\n\n    ![Configure search parameters and query performance factor.](https://redis.io/docs/latest/images/rs/screenshots/databases/rs-config-query-performance-factor.png)\n    \n1. Click **Done** to close the parameter editor.\n\n1. Click **Create** or **Save**.\n\n#### Restart shards {#restart-shards}\n\nAfter you update the query performance factor for an existing database, restart all shards to apply the new settings. You can migrate shards to restart them. Newly created databases can skip this step.\n\n1. Use [`rladmin status shards db <db-name>`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status#status-shards) to list all shards for your database:\n\n    [code example]\n\n    Example output:\n\n    [code example]\n\n    Note the following fields for the next steps: \n    - `ID`: the Redis shard's ID.\n    - `NODE`: the node on which the shard currently resides.\n    - `ROLE`: `master` is a primary shard; `slave` is a replica shard.\n\n1. For each replica shard, use [`rladmin migrate shard`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/migrate) to move it to a different node and restart it:\n\n    [code example]\n\n1. After you migrate the replica shards, migrate the original primary shards.\n\n1. Rerun `rladmin status shards db <db-name>` to verify the shards migrated to different nodes:\n\n    [code example]\n\n    Example output:\n\n    [code example]"},{"id":"configure-query-performance-factor-with-the-rest-api","title":"Configure query performance factor with the REST API","role":"configuration","text":""},{"id":"rest-api-v8","title":"Redis Software 8.x and later","role":"content","text":"Starting with Redis Software 8.x, you can configure the query performance factor using the [`query_performance_factor`](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/query_performance_factor) object in the [BDB object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb). You no longer need to manually set `sched_policy`, `conns`, or module `WORKERS` parameters. Changes take effect immediately without restarting shards.\n\n#### Create a new database {#create-db-rest-api-v8}\n\nTo create a database with the query performance factor enabled, use the [create database REST API endpoint](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#post-bdbs-v1) with the `query_performance_factor` field:\n\n[code example]\n\nThe following [cURL](https://curl.se/docs/) request creates a new database from the JSON example:\n\n[code example]\n\n#### Update an existing database {#update-db-rest-api-v8}\n\nTo configure the query performance factor for an existing database, use the [update database configuration](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#put-bdbs) endpoint with the `query_performance_factor` field:\n\n[code example]\n\nNo shard restart is required. The new configuration applies automatically.\n\n#### Query performance factor object fields\n\n| Field | Type/Value | Description |\n|-------|------------|-------------|\n| active | boolean (default:&nbsp;false) | If true, enables query performance factor for the database |\n| scaling_factor | integer (range:&nbsp;0-16) (default:&nbsp;0) | Scales the magnitude of the query performance factor |\n\nSee the [`query_performance_factor` object reference](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/query_performance_factor) for more details."},{"id":"rest-api-prior","title":"Prior to Redis Software 8","role":"content","text":"You can configure the query performance factor when you [create a new database](#create-db-rest-api) or [update an existing database](#update-db-rest-api) using the Redis Software [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api).\n\n#### Create new database with the REST API {#create-db-rest-api}\n\nTo create a database and configure the query performance factor, use the [create database REST API endpoint](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#post-bdbs-v1) with a [BDB object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb) that includes the following parameters:\n\n[code example]\n\nSee [Calculate performance factor](#calculate-query-performance-factor) to determine the value to use for `<NUMBER_OF_CPUS>`.\n\n##### Example REST API request for a new database\n\nThe following JSON is an example request body used to create a new database with a 4x query performance factor configured:\n\n[code example]\n\nThe following [cURL](https://curl.se/docs/) request creates a new database from the JSON example:\n\n[code example]\n\n#### Update existing database with the REST API {#update-db-rest-api}\n\nTo configure the query performance factor for an existing database, use the following REST API requests:\n\n- [Update database configuration](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#put-bdbs) to modify the DMC proxy.\n\n- [Upgrade module](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/modules/upgrade#post-bdb-modules-upgrade) to set the search module’s query performance factor.\n\n> [!NOTE]\n> - Because this procedure also restarts the database shards, you should perform it during a maintenance period.\n> - This procedure overwrites any existing module configuration parameters.\n\nThe following example script uses both endpoints to configure a 4x query performance factor:\n\n[code example]"},{"id":"monitoring-redis-search","title":"Monitoring Redis Search","role":"content","text":"To monitor a database with a query performance factor configured:\n\n1. Integrate your Redis Software deployment with Prometheus. See [Prometheus and Grafana with Redis Software](https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise) for instructions.\n\n1. Monitor the `redis_process_cpu_usage_percent` shard metric.\n\n    The following Prometheus UI screenshot shows `redis_process_cpu_usage_percent` spikes for a database with two shards:\n\n    - 1st 100% spike: [`memtier_benchmark`](https://github.com/RedisLabs/memtier_benchmark) search test at the default (no additional CPUs for search).\n\n    - 2nd 100% spike: reconfiguration and shard restart for a 4x query performance factor.\n\n    - 3rd 600% spike: `memtier_benchmark` search test with threading at a 4x query performance factor (6 CPUs per shard).\n\n    ![The Prometheus graph shows three spikes for redis_process_cpu_usage_percent: 100%, another 100%, then 600%.](https://redis.io/docs/latest/images/rs/screenshots/monitor-rs-scalable-search-cpu-usage.png)"}],"examples":[{"id":"config-db-ui-prior-ex0","language":"sh","code":"rladmin status shards db db-name","section_id":"config-db-ui-prior"},{"id":"config-db-ui-prior-ex1","language":"sh","code":"SHARDS:\n    DB:ID   NAME                ID      NODE   ROLE   SLOTS   USED_MEMORY STATUS\n    db:2    db-name             redis:1 node:1 master 0-16383 1.95MB      OK    \n    db:2    db-name             redis:2 node:2 slave  0-16383 1.95MB      OK","section_id":"config-db-ui-prior"},{"id":"config-db-ui-prior-ex2","language":"sh","code":"rladmin migrate shard <shard_id> target_node <node_id>","section_id":"config-db-ui-prior"},{"id":"config-db-ui-prior-ex3","language":"sh","code":"rladmin status shards db db-name","section_id":"config-db-ui-prior"},{"id":"config-db-ui-prior-ex4","language":"sh","code":"SHARDS:\n    DB:ID   NAME                ID      NODE   ROLE   SLOTS   USED_MEMORY STATUS\n    db:2    db-name             redis:1 node:2 master 0-16383 1.95MB      OK    \n    db:2    db-name             redis:2 node:1 slave  0-16383 1.95MB      OK","section_id":"config-db-ui-prior"},{"id":"rest-api-v8-ex0","language":"json","code":"{\n    \"name\": \"scalable-search-db\",\n    \"type\": \"redis\",\n    \"memory_size\": 10000000,\n    \"port\": 13000,\n    \"authentication_redis_pass\": \"<your default db pwd>\",\n    \"proxy_policy\": \"all-master-shards\",\n    \"sharding\": true,\n    \"shards_count\": 3,\n    \"shards_placement\": \"sparse\",\n    \"shard_key_regex\": [{\"regex\": \".*\\\\{(?<tag>.*)\\\\}.*\"}, {\"regex\": \"(?<tag>.*)\"}],\n    \"replication\": false,\n    \"module_list\": [{\n        \"module_name\": \"search\",\n        \"module_args\": \"\"\n    }],\n    \"query_performance_factor\": {\n        \"active\": true,\n        \"scaling_factor\": 4\n    }\n}","section_id":"rest-api-v8"},{"id":"rest-api-v8-ex1","language":"sh","code":"curl -k -u \"<user>:<password>\" https://<host>:9443/v1/bdbs -H \"Content-Type:application/json\" -d @scalable-search-db.json","section_id":"rest-api-v8"},{"id":"rest-api-v8-ex2","language":"sh","code":"curl -k -u \"<user>:<password>\" -X PUT https://<host>:9443/v1/bdbs/<DB_ID> -H \"Content-Type:application/json\" -d '{\n    \"query_performance_factor\": {\n        \"active\": true,\n        \"scaling_factor\": 4\n    }\n}'","section_id":"rest-api-v8"},{"id":"rest-api-prior-ex0","language":"json","code":"{\n    \"sched_policy\": \"mnp\",\n    \"conns\": 32,\n    \"module_list\": [{\n        \"module_name\": \"search\",\n        \"module_args\": \"WORKERS <NUMBER_OF_THREADS>\"\n    }]\n}","section_id":"rest-api-prior"},{"id":"rest-api-prior-ex1","language":"json","code":"{\n    \"name\": \"scalable-search-db\",\n    \"type\": \"redis\",\n    \"memory_size\": 10000000,\n    \"port\": 13000,\n    \"authentication_redis_pass\": \"<your default db pwd>\",\n    \"proxy_policy\": \"all-master-shards\",\n    \"sched_policy\": \"mnp\",\n    \"conns\": 32,\n    \"sharding\": true,\n    \"shards_count\": 3,\n    \"shards_placement\": \"sparse\",\n    \"shard_key_regex\": [{\"regex\": \".*\\\\{(?<tag>.*)\\\\}.*\"}, {\"regex\": \"(?<tag>.*)\"}],\n    \"replication\": false,\n    \"module_list\": [{\n        \"module_name\": \"search\",\n        \"module_args\": \"WORKERS 6\"\n    }]\n}","section_id":"rest-api-prior"},{"id":"rest-api-prior-ex2","language":"sh","code":"curl -k -u \"<user>:<password>\" https://<host>:9443/v1/bdbs -H \"Content-Type:application/json\" -d @scalable-search-db.json","section_id":"rest-api-prior"},{"id":"rest-api-prior-ex3","language":"sh","code":"#!/bin/bash\nexport DB_ID=1\nexport CPU=6\nexport MODULE_ID=`curl -s -k -u \"<user>:<password>\" https://<host>:9443/v1/bdbs/$DB_ID | jq '.module_list[] | select(.module_name==\"search\").module_id' | tr -d '\"'`\n\ncurl -o /dev/null -s -k -u \"<user>:<password>\" -X PUT https://<host>:9443/v1/bdbs/$DB_ID -H \"Content-Type:application/json\" -d '{\n    \"sched_policy\": \"mnp\",\n    \"conns\": 32\n}'\n\nsleep 1\n\ncurl -o /dev/null -s -k -u \"<user>:<password>\" https://<host>:9443/v1/bdbs/$DB_ID/modules/upgrade -H \"Content-Type:application/json\" -d '{\n    \"modules\": [\n      {\n        \"module_name\": \"search\",\n        \"new_module_args\": \"WORKERS '$CPU'\",\n        \"current_module\": \"'$MODULE_ID'\",\n        \"new_module\": \"'$MODULE_ID'\"\n      }\n    ]\n}'","section_id":"rest-api-prior"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/search/redisearch-2-upgrade","title":"Upgrade a database to Search 2.x","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/redisearch-2-upgrade/","summary":"Upgrade a database from Search 1.x to Search 2.x.","tags":["docs","operate","stack"],"last_updated":"2026-10-02T14:53:57+01:00","page_type":"content","content_hash":"a3073606dbc73fdb6ab69a53c929d345069a69dbebbdc7f4bbfe50f74b7f9e39","sections":[{"id":"overview","title":"Overview","role":"overview","text":"RediSearch 2.x includes some significant architectural changes that improve functionality.\nThe main change is that RediSearch 2.x stores indexes outside of the Redis database that contains the data.\nThis makes commands more efficient and improves replication between clusters because the index changes are managed by the participating clusters rather than being synchronized with the data.\n\nThis change allows databases with RediSearch to support:\n\n- [Active-Active databases](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/search-active-active)\n- Database cluster re-sharding\n- Replica Of to a sharded destination database\n- [EXPIRE](https://redis.io/docs/latest/commands/ttl) of documents reflected in the index\n\nIn addition, RediSearch 2.x indexes data that already existed in the database at the time that the index was created.\n\nTo upgrade a Redis Software database with RediSearch 1.x to RediSearch 2.x, you have to set up a new database with RediSearch 2.x and use the `RediSearch_Syncer.py` script to replicate the data from the old database into the new database.\n\n> [!NOTE]\n> After you create the database or after you replicate the data, [create an index](https://redis.io/docs/latest/commands/ft.create) with a prefix or filter that defines the keys that you want to index."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Install Python 3 on the host where you plan to run the synchronization script:\n\n[code example]"},{"id":"limitations","title":"Limitations","role":"limits","text":"- Suggestions (`FT.SUG` APIs) and spell check dictionaries are not replicated from the source database. You need to add them manually.\n- If there are multiple indexes on the source and the documents do not have prefixes that identify them with an index, RediSearch 2.x can't index the documents in their respective indexes.\n- The `NOSAVE` option is no longer supported. Indexes created with the `NOSAVE` option can't be upgraded.\n- Databases that contain temporary indexes can't be upgraded.\n- Any attempt to add, delete, or modify an index during the upgrade will cause the replication process to fail. During the upgrade, the source database can only receive `FT.ADD` and `FT.DEL` commands."},{"id":"replicate-data-from-redisearch-1x-to-redisearch-2x","title":"Replicate data from RediSearch 1.x to RediSearch 2.x","role":"content","text":"To replicate a RediSearch 1.x database to a RediSearch 2.x database:\n\n1. Sign in to the admin console of the Redis Software cluster where you want to host the new RediSearch 2.x database.\n\n1. [Add the RediSearch 2.x module](#add-new-module) to the cluster.\n\n1. [Create a new database](#create-db) with RediSearch 2.x.\n\n1. [Migrate data](#migrate-data) to the RediSearch 2.x database."},{"id":"add-new-module","title":"Add RediSearch 2.x to the cluster","role":"content","text":"1. Download the RediSearch 2.x module package from the [Redis Download Center](https://redis.io/downloads).\n\n1. In the Redis Software admin console, select **settings**.\n\n1. In **redis modules**, select the **Add module** button.\n\n    ![](https://redis.io/docs/latest/images/rs/upgrade_module.png)\n\n1. Use the file browser to select the module package and upload it to the cluster."},{"id":"create-db","title":"Create a RediSearch 2.x database","role":"content","text":"1. [Create a database](https://redis.io/docs/latest/operate/rs/databases/create) and configure its settings.\n\n1. In the **Redis Modules** section:\n\n    1. Select the **Add** button:\n    \n        ![The Add icon](https://redis.io/docs/latest/images/rs/icon_add.png)\n    \n    1. Select **RediSearch 2** from the dropdown list.\n\n    1. Select the **OK** button to confirm:\n\n        ![The Save icon](https://redis.io/docs/latest/images/rs/icon_save.png)\n\n1. Select the **Activate** button to create the database."},{"id":"migrate-data","title":"Migrate data","role":"content","text":"1. Download the RediSearch_Syncer package for your operating system:\n\n    - [RedHat Enterprise Linux 7](http://redismodules.s3.amazonaws.com/redisearch/RediSearchSyncer/RediSearchSyncer-rhel7.zip)\n    - [Ubuntu 18.04](http://redismodules.s3.amazonaws.com/redisearch/RediSearchSyncer/RediSearchSyncer-bionic.zip)\n    - [Ubuntu 16.04](http://redismodules.s3.amazonaws.com/redisearch/RediSearchSyncer/RediSearchSyncer-xenial.zip)\n    - [Ubuntu 14.04](http://redismodules.s3.amazonaws.com/redisearch/RediSearchSyncer/RediSearchSyncer-trusty.zip)\n\n1. Extract the package: \n\n    [code example]\n\n1. In the extracted directory, run the RediSearch_Syncer.py script:\n\n    [code example]\n\n    Replace the following variables with your own values:\n\n    - `destination url` - The replication URL of the RediSearch 2.x database. To find this value, go to the database configuration screen in the admin console and select **Get Replica of source URL**.\n\n    - `source url` - The replication URL of the RediSearch 1.x database. To find this value, go to the database configuration screen in the admin console and select **Get Replica of source URL**.\n\n    - `--add-prefix <prefix>` (optional) - Adds a prefix to all of the hashes that are replicated to the new database.\n\n        > [!NOTE]\n        > Only use the `add-prefix` option if you want to index all of the hashes in the same index in the source database.\n        >         \n\n    The script shows a table with the progress of the replication process.\n    Press **F5** to see the updated status.\n\n    For example:\n\n    [code example]\n\n1. Stop the processes that are sending requests to the source database so all of the data gets synchronized to the destination database.\n\n1. Run `FT.INFO` on both source and destination databases and compare the number of indexed documents. The replication process is complete when the number of indexed documents is the same in both databases.\n\n1. When the status field is `st_in_sync`, you can press **Ctrl-C** to cancel the synchronization process.\n\n1. Press **Q** to quit the `RediSearch_Syncer.py`.\n\nYou can now redirect your database connections to the RediSearch 2.x database."}],"examples":[{"id":"prerequisites-ex0","language":"sh","code":"sudo apt install python3","section_id":"prerequisites"},{"id":"migrate-data-ex0","language":"sh","code":"unzip <package_name>","section_id":"migrate-data"},{"id":"migrate-data-ex1","language":"sh","code":"python3 RediSearch_Syncer.py -d <destination_url> -s <source_url> [--add-prefix <prefix>]","section_id":"migrate-data"},{"id":"migrate-data-ex2","language":"sh","code":"python RediSearch_Syncer.py -d redis://admin:IBrS0xaL6rShfB1wKTtUkcQG1g3UWAlTd53AotPdTcQvGIVP@redis-19472.cluster1.local:19472 -s redis://admin:1GjFuUbBqTSPDbRfaxEPLSoXpFmRdFxmBKMD0BuxwMJ2DEaO@redis-19636.cluster1.local:19636","section_id":"migrate-data"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/search/scalable-query-best-practices","title":"Best practices for scalable Redis Search","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/scalable-query-best-practices/","summary":"Best practices for scalable Redis Search in Redis Software and Redis Cloud.","tags":["docs","operate","stack"],"last_updated":"2026-09-22T10:15:10+01:00","page_type":"content","content_hash":"52e6dd40801c457127e63ff776faa032a40c0591a11e461a3328e55c74f01f56","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Vertical scaling of Redis Search](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/query-performance-factor) requires configuring query performance factors. With careful crafting of search indexes and queries, query performance factors allow throughput scaling up to 16X. The following recommendations can help optimize your indexes and queries to maximize the performance benefits from additional CPUs allocated by query performance factors."},{"id":"best-candidates-for-query-performance-factor-improvements","title":"Best candidates for query performance factor improvements","role":"content","text":"- Query types:\n\n    - [Full-text](https://redis.io/docs/latest/develop/ai/search-and-query/query/full-text)\n\n    - [Tag](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/tags)\n\n    - [Vector](https://redis.io/docs/latest/develop/ai/search-and-query/query/vector-search)\n\n    - [Numeric](https://redis.io/docs/latest/develop/ai/search-and-query/query/range)\n\n    - [Geo](https://redis.io/docs/latest/develop/ai/search-and-query/query/geo-spatial)\n\n- Result set types:\n\n    - Small result sets\n\n    - Document subsets that are indexed in their [non-normalized](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/sorting#normalization-unf-option) form"},{"id":"best-practices","title":"Best practices","role":"content","text":"If query performance factors have not boosted the performance of your queries as much as expected:\n\n1. Verify your index includes all queried and returned fields.\n\n1. Identify and avoid query [anti-patterns](https://redis.io/docs/latest/develop/ai/search-and-query/best-practices/scalable-query-best-practices#anti-patterns) that limit scalability.\n\n1. Follow best practices to [improve indexing](#improve-indexing).\n\n1. Follow best practices to [improve queries](#improve-queries)."},{"id":"improve-indexing","title":"Improve indexing","role":"content","text":"Follow these best practices for [indexing](https://redis.io/docs/latest/develop/ai/search-and-query/indexing):\n\n- Include fields in the index definition that are used in the query or the required result sets (projections).\n\n- Use `SORTABLE` for all fields returned in result sets.\n\n- Use the `UNF` option for `TAG` and `GEO` fields.\n\n- Use the `NOSTEM` option for `TEXT` fields."},{"id":"improve-queries","title":"Improve queries","role":"content","text":"Follow these best practices to optimize [queries](https://redis.io/docs/latest/develop/ai/search-and-query/query):\n\n- Specify the result set fields in the `RETURN` or `LOAD` clauses and include them in the index definition. Don’t just return the default result set from [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search)  or `LOAD *` from [`FT.AGGREGATE`](https://redis.io/docs/latest/commands/ft.aggregate).\n\n- Use `LIMIT` to reduce the result set size.\n\n- Use [`DIALECT 3`](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/dialects#dialect-3) or higher for any queries against JSON."},{"id":"index-and-query-examples","title":"Index and query examples","role":"content","text":"The following examples depict an anti-pattern index schema and query, followed by a corrected schema and query, which allows for scalability with Redis Search."},{"id":"anti-pattern-index-schema","title":"Anti-pattern index schema","role":"content","text":"The following index schema is not optimized for vertical scaling:\n\n[code example]"},{"id":"anti-pattern-query","title":"Anti-pattern query","role":"content","text":"The following query is not optimized for vertical scaling:\n\n[code example]"},{"id":"improved-index-schema","title":"Improved index schema","role":"content","text":"Here's an improved index schema that follows best practices for vertical scaling:\n\n[code example]"},{"id":"improved-query","title":"Improved query","role":"content","text":"Here's an improved query that follows best practices for vertical scaling:\n\n[code example]"},{"id":"performance-results","title":"Performance results","role":"performance","text":"The following benchmarks show the performance improvements for different query types achieved with query performance factors. Vector, tag, and text queries strongly benefit, while numeric and geographic queries show more limited improvements."},{"id":"vector-schema-type","title":"Vector schema type","role":"content","text":"#### Vector ingest\n\n| Shards | Threads per shard | CPUs | Speedup factor |\n|--------|-------------------|------|----------------|\n| 1      | 0                 | 1    | 0              |\n| 6      | 0                 | 6    | 6.6            |\n| 1      | 6                 | 6    | 2.5            |\n| 2      | 6                 | 12   | 6.1            |\n| 4      | 6                 | 24   | 24.3           |\n\n#### Vector query\n\n| Shards | Threads per shard | CPUs | Speedup factor |\n|--------|-------------------|------|----------------|\n| 1      | 0                 | 1    | 0              |\n| 6      | 0                 | 6    | 0.8            |\n| 1      | 6                 | 6    | 4.7            |\n| 2      | 6                 | 12   | 5.1            |\n| 4      | 6                 | 24   | 5.6            |"},{"id":"tag-schema-type","title":"Tag schema type","role":"content","text":"| Worker threads | % change |\n|----------------|----------|\n| 0              | 0        |\n| 6              | 135.88   |"},{"id":"text-schema-type","title":"Text schema type","role":"content","text":"#### Two-word union queries\n\n| Worker threads | Queries per second | % change |\n|----------------|--------------------|----------|\n| 0              | 188                | 0        |\n| 6              | 1,072              | 470      |\n| 12             | 1,995              | 961      |\n| 18             | 2,834              | 1,407    |\n\n#### Two-word intersection queries\n\n| Worker threads | Queries per second | % change |\n|----------------|--------------------|----------|\n| 0              | 2,373              | 0        |\n| 6              | 12,396             | 422      |\n| 12             | 17,506             | 638      |\n| 18             | 19,764             | 733      |\n\n#### Simple one-word match\n\n| Worker threads | Queries per second | % change |\n|----------------|--------------------|----------|\n| 0              | 476                | 0        |\n| 6              | 2,837              | 496      |\n| 12             | 5,292              | 1,012    |\n| 18             | 7,512              | 1,478    |"},{"id":"numeric-schema-type","title":"Numeric schema type","role":"content","text":"| Worker threads | Queries per second | % change |\n|----------------|--------------------|----------|\n| 0              | 33,584             | 0        |\n| 1              | 36,993             | 10.15    |\n| 3              | 36,504             | 8.69     |\n| 6              | 36,897             | 9.86     |"},{"id":"geo-schema-type","title":"Geo schema type","role":"content","text":"#### Geo queries without UNF\n\n| Worker threads | Queries per second | % change |\n|----------------|--------------------|----------|\n| 0              | 48                 | 0        |\n| 6              | 96                 | 100      |\n| 12             | 96                 | 100      |\n| 18             | 98                 | 104      |\n\n#### Geo queries with UNF\n\n| Worker threads | Queries per second | % change |\n|----------------|--------------------|----------|\n| 0              | 61                 | 0        |\n| 6              | 227                | 272      |\n| 12             | 217                | 256      |\n| 18             | 217                | 256      |"}],"examples":[{"id":"anti-pattern-index-schema-ex0","language":"sh","code":"FT.CREATE jsonidx:profiles ON JSON PREFIX 1 profiles: \n          SCHEMA $.tags.* as t NUMERIC SORTABLE \n                 $.firstName as name TEXT \n                 $.location as loc GEO","section_id":"anti-pattern-index-schema"},{"id":"anti-pattern-query-ex0","language":"sh","code":"FT.AGGREGATE jsonidx:profiles '@t:[1299 1299]' LOAD * LIMIT 0 10","section_id":"anti-pattern-query"},{"id":"improved-index-schema-ex0","language":"sh","code":"FT.CREATE jsonidx:profiles ON JSON PREFIX 1 profiles: \n          SCHEMA $.tags.* as t NUMERIC SORTABLE \n                 $.firstName as name TEXT NOSTEM SORTABLE \n                 $.lastName as lastname TEXT NOSTEM SORTABLE \n                 $.location as loc GEO SORTABLE \n                 $.id as id TAG SORTABLE UNF \n                 $.ver as ver TAG SORTABLE UNF","section_id":"improved-index-schema"},{"id":"improved-query-ex0","language":"sh","code":"FT.AGGREGATE jsonidx:profiles '@t:[1299 1299]' \n                LOAD 6 id t name lastname loc ver\n                LIMIT 0 10\n                DIALECT 3","section_id":"improved-query"}]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/search/search-active-active","title":"Search and query Active-Active databases","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/search-active-active/","summary":"Search and query Active-Active databases.","tags":["docs","operate","stack"],"last_updated":"2026-10-06T08:53:14-07:00","page_type":"content","content_hash":"63cc5848e855a6730b8cf93190812bd1acb42d4412ef4c0a1278d309cda66908","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Starting with RediSearch 2.x, supported in Redis Software (RS) 6.0 and later, you can [enable Redis Search](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/add-module-to-database) for [Active-Active databases](https://redis.io/docs/latest/operate/rs/databases/active-active) at the time of creation.\n\nYou can run search operations on any instance of an Active-Active database."},{"id":"how-it-works","title":"How it works","role":"content","text":"1. Create an Active-Active database with RediSearch 2.x enabled. Active-Active databases created with or upgraded to Redis version 8 or later automatically enable Redis Search.\n1. [Create the index](https://redis.io/docs/latest/commands/ft.create) on each instance of the database.\n1. If you are using [synonyms](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/synonyms), you need to add them to each replica.\n1. The index is maintained by each instance outside of the database keyspace, so only updates to the hashes in the databases are synchronized."},{"id":"command-compatibility","title":"Command compatibility","role":"syntax","text":"Active-Active databases do not support the following Redis Search commands: \n\n- [`FT.SUGADD`](https://redis.io/docs/latest/commands/ft.sugadd)\n- [`FT.SUGGET`](https://redis.io/docs/latest/commands/ft.sugget)\n- [`FT.SUGDEL`](https://redis.io/docs/latest/commands/ft.sugdel)\n- [`FT.SUGLEN`](https://redis.io/docs/latest/commands/ft.suglen)"},{"id":"example","title":"Example","role":"example","text":"Here's an example to help visualize Active-Active Redis Search:\n\n| Time  | Description | CRDB Instance1 | Redis Search Instance 1 | CRDB Instance 2 | Redis Search Instance 2 |\n| :---: | :--- | :--- | :--- | :--- | :--- |\n|  t0 | Create the index on each instance |  | FT.CREATE idx .... |  | FT.CREATE idx .... |\n|  t1 | Add doc1 as a hash on instance 1; Redis Search indexes doc1 on instance 1 | HSET doc1 field1 \"a\" | (Index doc1 field1 \"a\") |  |  |\n|  t2 | Add doc2 as a hash on instance 2; Redis Search indexes doc2 on instance 2 |  |  | HSET doc1 field2 \"b\" | (Index doc1 field2 \"b\") |\n|  t3 | Searching for \"a\" in each instance only finds the result in instance 1 |  | FT.Search idx \"a\"<br/>1) 1<br/>2) doc1 |  | FT.Search idx \"a\"<br/>1) 0 |\n|  t4 | Active-Active synchronization | - Sync - |  | - Sync - |  |\n|  t5 | Both hashes are found in each instance | HGETALL doc1<br/>1) \"field2\"<br/>2) \"b\"<br/>3) \"field1\"<br/>4) \"a\" |  | HGETALL doc1<br/>1) \"field2\"<br/>2) \"b\"<br/>3) \"field1\"<br/>4) \"a\" |  |\n|  t6 | Searching for \"a\" in each instance finds both documents |  | FT.Search idx \"a\"<br/>1) 1<br/>2) doc1 |  | FT.Search idx \"a\"<br/>1) 1<br/>2) doc1 |\n\nThe practical result is that you have a geo-distributed database with a high level of consistency that can also run search operations on any instance."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/stack-quickstart","title":"Redis Open Source quick start","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/stack-quickstart/","summary":"","tags":["docs","operate","stack"],"last_updated":"2026-10-02T14:53:57+01:00","page_type":"content","content_hash":"fe2ccb3c862baa517fe75d01ec6d3232e22f81d76d03a4951d53fc10036f2e9e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"To quickly set up a database with Redis Stack (Redis Open Source) features,\nyou can sign up for a free [Redis Cloud](https://cloud.redis.io/#/sign-up) subscription and create a Redis Stack database.\n\nAlternatively, you can use one of these methods:\n\n- [Redis Software](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/redis-enterprise-software-quickstart)\n- Redis Software in a [Docker container](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart)\n- [Other platforms](https://redis.io/docs/latest/operate/kubernetes) for Redis Software"},{"id":"set-up-a-redis-cloud-database","title":"Set up a Redis Cloud database","role":"content","text":"To set up a Redis Cloud database with Redis Stack features, follow these steps:\n\n1. [Create a new Redis Cloud subscription](#create-a-subscription).\n\n1. [Create a Redis Stack database](#create-a-redis-stack-database).\n\n1. [Connect to the database](#connect-to-the-database).\n\nFor more details, see the Redis Cloud [quick start](https://redis.io/docs/latest/operate/rc/rc-quickstart)."},{"id":"create-a-subscription","title":"Create a subscription","role":"content","text":"To create a new subscription:\n\n1. Sign in to the Redis Cloud [admin console](http://cloud.redis.io) or create a new account.\n\n1. Select the **New subscription** button:\n\n    ![The New subscriptions button in the admin console menu.](https://redis.io/docs/latest/images/rc/button-subscription-new.png)\n\n1. Configure your subscription:\n\n    1. Select **Fixed plans**.\n    1. For the cloud vendor, select **Amazon Web Services** (AWS), **Google Cloud** (GCP), or **Microsoft Azure**.\n    1. Select a region to deploy the subscription to.\n    1. From the dataset size list, select the Free tier (30MB).\n    1. Enter a name for the subscription.\n\n1. Select the **Create subscription** button:\n\n    ![The Create Subscription button.](https://redis.io/docs/latest/images/rc/button-subscription-create.png)"},{"id":"create-a-redis-stack-database","title":"Create a Redis Stack database","role":"content","text":"After you create a subscription, follow these steps to create a Redis Stack database:\n\n1. Select the **New database** button:\n\n    ![The New Database button creates a new database for your subscription.](https://redis.io/docs/latest/images/rc/button-database-new.png)\n\n1. In **General** settings, enter a **Database name**.\n\n1. For database **Type**, select **Redis Stack**.\n\n1. Select the **Activate database** button:\n\n    ![Use the Activate database button to create and activate your database.](https://redis.io/docs/latest/images/rc/button-database-activate.png)"},{"id":"connect-to-the-database","title":"Connect to the database","role":"content","text":"After creating the database, you can view its **Configuration** settings. You will need the following information to connect to your new database:\n\n- **Public endpoint**: The host address of the database\n- **Redis password**/**Default user password**: The password used to authenticate with the database\n\nWith this information, you can connect to your database with the [`redis-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/redis-cli) command-line tool, an application, or [Redis Insight](https://redislabs.com/redisinsight/)."},{"id":"try-redis-open-source-features","title":"Try Redis Open Source features","role":"content","text":"To try out Redis Open Source features, follow the examples provided by the corresponding guides:\n\n- [Redis Search quick start](https://redis.io/docs/latest/develop/get-started/search-tutorial)\n- [JSON quick start](https://redis.io/docs/latest/develop/data-types/json#use-redisjson)\n- [Time series quick start](https://redis.io/docs/latest/develop/data-types/timeseries)\n- [Probabilistic data structures quick start](https://redis.io/docs/latest/develop/data-types/probabilistic)"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/timeseries/commands","title":"Time series commands","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/timeseries/commands/","summary":"Lists time series commands and provides links to the command reference pages.","tags":["docs","operate","stack"],"last_updated":"2026-09-22T10:15:10+01:00","page_type":"content","content_hash":"25d26686dac3465584fc89815b7f6d8ff8f6e647f1348bbe6c579059e3f82af1","sections":[{"id":"content","title":"Content","role":"content","text":"The following table lists time series commands. See the command links for more information about each command's syntax, arguments, and examples.\n\n| Command | Redis<br />Software | Redis<br />Cloud | Description |\n|:--------|:----------------------|:-----------------|:------|\n| [TS.ADD](https://redis.io/docs/latest/commands/ts.add) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\"><nobr>&#x2705; Flexible & Annual</nobr></span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Appends a sample to a time series. |\n| [TS.ALTER](https://redis.io/docs/latest/commands/ts.alter) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Updates the retention, chunk size, duplicate policy, or labels for an existing time series. |\n| [TS.CREATE](https://redis.io/docs/latest/commands/ts.create) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Creates a new time series. |\n| [TS.CREATERULE](https://redis.io/docs/latest/commands/ts.createrule) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Creates a compaction rule for [downsampling](https://redis.io/docs/latest/develop/data-types/timeseries#aggregation). |\n| [TS.DECRBY](https://redis.io/docs/latest/commands/ts.decrby) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Decreases the value of the latest sample in a time series by the specified number. Either modifies the existing sample or adds the decreased value as a new sample, depending on the timestamp option. |\n| [TS.DEL](https://redis.io/docs/latest/commands/ts.del) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Removes all samples between two timestamps for a given time series. |\n| [TS.DELETERULE](https://redis.io/docs/latest/commands/ts.deleterule) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Removes a compaction rule. |\n| [TS.GET](https://redis.io/docs/latest/commands/ts.get) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Returns the last sample in a time series. |\n| [TS.INCRBY](https://redis.io/docs/latest/commands/ts.incrby) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Increases the value of the latest sample in a time series by the specified number. Either modifies the existing sample or adds the increased value as a new sample, depending on the timestamp option. |\n| [TS.INFO](https://redis.io/docs/latest/commands/ts.info) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Returns time series information and statistics. |\n| [TS.MADD](https://redis.io/docs/latest/commands/ts.madd) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Appends multiple samples to one or more time series. |\n| [TS.MGET](https://redis.io/docs/latest/commands/ts.mget) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Returns multiple samples with [labels](https://redis.io/docs/latest/develop/data-types/timeseries#create-a-time-series) that match the filter. |\n| [TS.MRANGE](https://redis.io/docs/latest/commands/ts.mrange) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | For multiple time series, runs a query against samples within a range of timestamps, from earliest to latest. Supports [filtering](https://redis.io/docs/latest/develop/data-types/timeseries#query-data-points) and [aggregation](https://redis.io/docs/latest/develop/data-types/timeseries#aggregation). |\n| [TS.MREVRANGE](https://redis.io/docs/latest/commands/ts.mrevrange) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | For multiple time series, runs a query against samples within a range of timestamps in reverse order, from latest to earliest. Supports [filtering](https://redis.io/docs/latest/develop/data-types/timeseries#query-data-points) and [aggregation](https://redis.io/docs/latest/develop/data-types/timeseries#aggregation). |\n| [TS.NRANGE](https://redis.io/docs/latest/commands/ts.nrange) | <span title=\"Not supported\">&#x274c; Not supported</span><br /><span><br /></span> | <span title=\"Not supported\">&#x274c; Flexible & Annual</span><br /><span title=\"Not supported\">&#x274c; Free & Fixed</nobr></span> | Query a range across multiple time series in forward direction, returning the results grouped by timestamp. |\n| [TS.NREVRANGE](https://redis.io/docs/latest/commands/ts.nrevrange) | <span title=\"Not supported\">&#x274c; Not supported</span><br /><span><br /></span> | <span title=\"Not supported\">&#x274c; Flexible & Annual</span><br /><span title=\"Not supported\">&#x274c; Free & Fixed</nobr></span> | Query a range across multiple time series in reverse direction, returning the results grouped by timestamp. |\n| [TS.QUERYINDEX](https://redis.io/docs/latest/commands/ts.queryindex) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | Returns the keys of all time series with [labels](https://redis.io/docs/latest/develop/data-types/timeseries#create-a-time-series) that match the given filters. |\n| [TS.QUERYLABELS](https://redis.io/docs/latest/commands/ts.querylabels) | <span title=\"Not supported\">&#x274c; Not supported</span><br /><span><br /></span> | <span title=\"Not supported\">&#x274c; Flexible & Annual</span><br /><span title=\"Not supported\">&#x274c; Free & Fixed</nobr></span> | Returns all label names, or all values of a given label, for time series matching a filter list, or all series. |\n| [TS.READ](https://redis.io/docs/latest/commands/ts.read) | <span title=\"Not supported\">&#x274c; Not supported</span><br /><span><br /></span> | <span title=\"Not supported\">&#x274c; Flexible & Annual</span><br /><span title=\"Not supported\">&#x274c; Free & Fixed</nobr></span> | Read a batch of time series samples at or after a given timestamp, returning up to `max_count` of the oldest qualifying samples ordered by increasing timestamp. By default `TS.READ` returns immediately with whatever is available; add the optional `BLOCK` keyword to wait until at least `min_count` samples exist or `milliseconds` elapse, whichever occurs first. |\n| [TS.RANGE](https://redis.io/docs/latest/commands/ts.range) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | From the start of a single time series, runs a query against samples within a range of timestamps. Supports [filtering](https://redis.io/docs/latest/develop/data-types/timeseries#query-data-points) and [aggregation](https://redis.io/docs/latest/develop/data-types/timeseries#aggregation). |\n| [TS.REVRANGE](https://redis.io/docs/latest/commands/ts.revrange) | <span title=\"Supported\">&#x2705; Supported</span><br /><span><br /></span> | <span title=\"Supported\">&#x2705; Flexible & Annual</span><br /><span title=\"Supported\">&#x2705; Free & Fixed</nobr></span> | From the end of a single time series, runs a query against samples within a range of timestamps in reverse order. Supports [filtering](https://redis.io/docs/latest/develop/data-types/timeseries#query-data-points) and [aggregation](https://redis.io/docs/latest/develop/data-types/timeseries#aggregation). |"}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/timeseries/config","title":"Time series configuration compatibility with Redis Software","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/timeseries/config/","summary":"Time series configuration settings supported by Redis Software and Redis Cloud.","tags":["docs","operate","stack"],"last_updated":"2026-09-22T10:15:10+01:00","page_type":"content","content_hash":"61a0e1e2826ba9d5063f60c3f44e2f3ab66aebf17211720c4e861980cc6ab9da","sections":[{"id":"configure-time-series-in-redis-software","title":"Configure time series in Redis Software","role":"configuration","text":"[Redis Software](https://redis.io/docs/latest/operate/rs) lets you manually change any [RedisTimeSeries configuration setting](https://redis.io/docs/latest/develop/data-types/timeseries/configuration#redistimeseries-configuration-parameters).\n\nTo change RedisTimeSeries configuration using the Redis Software Cluster Manager UI:\n\n  1. From the **Databases** list, select the database, then click **Configuration**.\n\n  1. Select the **Edit** button.\n\n  1. In the **Capabilities** section, click **Parameters**.\n\n  1. After you finish editing the module's configuration parameters, click **Done** to close the parameter editor.\n\n  1. Click **Save**."},{"id":"configure-time-series-in-redis-cloud","title":"Configure time series in Redis Cloud","role":"configuration","text":"[Redis Cloud](https://redis.io/docs/latest/operate/rc) does not let you configure RedisTimeSeries manually. However, if you have a Flexible or Annual [subscription](https://redis.io/docs/latest/operate/rc/subscriptions), you can contact [support](https://redis.com/company/support/) to request a configuration change. You cannot change RedisTimeSeries configuration for Free or Fixed subscriptions."},{"id":"configuration-settings","title":"Configuration settings","role":"configuration","text":"See [configuration parameters](https://redis.io/docs/latest/develop/data-types/timeseries/configuration) in the Develop section for parameter details and compatibility with Redis Software and Redis Cloud."}],"examples":[]}
{"schema_version":2,"id":"operate/oss_and_stack/stack-with-enterprise/timeseries","title":"Time series","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/timeseries/","summary":"","tags":["docs","operate","stack"],"last_updated":"2026-10-05T10:59:52+01:00","children":[{"id":"operate/oss_and_stack/stack-with-enterprise/timeseries/commands","summary":"Lists time series commands and provides links to the command reference pages.","title":"Time series commands","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/timeseries/commands/"},{"id":"operate/oss_and_stack/stack-with-enterprise/timeseries/config","summary":"Time series configuration settings supported by Redis Software and Redis Cloud.","title":"Time series configuration compatibility with Redis Software","url":"https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/timeseries/config/"}],"page_type":"content","content_hash":"b36fbf1f9ff7e0390fd64b45a6b1e103a07aad209f147bb3679f3bce7fc02c7d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can manage time series data in Redis Software with Redis Open Source."},{"id":"features","title":"Features","role":"content","text":"- Query by start time and end-time\n- Query by labels sets\n- Aggregated queries (Min, Max, Avg, Sum, Range, Count, First, Last) for any time bucket\n- Configurable max retention period\n- Compaction/Roll-ups - automatically updated aggregated time series\n- labels index - each key has labels which allows query by labels"},{"id":"memory-model","title":"Memory model","role":"content","text":"A time series is a linked list of memory chunks.\nEach chunk has a predefined size of samples.\nEach sample is a tuple of the time and the value of 128 bits,\n64 bits for the timestamp and 64 bits for the value."},{"id":"time-series-capabilities","title":"Time series capabilities","role":"content","text":"Redis Open Source provides a new data type that uses chunks of memory of fixed size for time series samples, indexed by the same Radix Tree implementation as Redis streams. With streams, you can create [a capped stream](https://redis.io/docs/latest/develop/data-types/streams#capped-streams), effectively limiting the number of messages by count. For time series, you can apply a retention policy in milliseconds. This is better for time series use cases, because they are typically interested in the data during a given time window, rather than a fixed number of samples."},{"id":"downsamplingcompaction","title":"Downsampling/compaction","role":"content","text":"| Before Downsampling | After Downsampling |\n| --- | --- |\n| ![](https://redis.io/docs/latest/images/rs/TimeSeries-downsampling1.png) | ![](https://redis.io/docs/latest/images/rs/TimeSeries-downsampling2.png) |\n\nIf you want to keep all of your raw data points indefinitely, your data set grows linearly over time. However, if your use case allows you to have less fine-grained data further back in time, downsampling can be applied. This allows you to keep fewer historical data points by aggregating raw data for a given time window using a given aggregation function. Time series support [downsampling](https://redis.io/docs/latest/develop/data-types/timeseries#aggregation) with the following aggregations: avg, sum, min, max, range, count, first, and last."},{"id":"secondary-indexing","title":"Secondary indexing","role":"content","text":"When using Redis’ core data structures, you can only retrieve a time series by knowing the exact key holding the time series. Unfortunately, for many time series use cases (such as root cause analysis or monitoring), your application won’t know the exact key it’s looking for. These use cases typically want to query a set of time series that relate to each other in a couple of dimensions to extract the insight you need. You could create your own secondary index with core Redis data structures to help with this, but it would come with a high development cost and require you to manage edge cases to make sure the index is correct.\n\nRedis does this indexing for you based on `field value` pairs called [labels](https://redis.io/docs/latest/develop/data-types/timeseries#create-a-time-series). You can add labels to each time series and use them to [filter](https://redis.io/docs/latest/develop/data-types/timeseries#query-data-points) at query time.\n\nHere’s an example of creating a time series with two labels (sensor_id and area_id are the fields with values 2 and 32 respectively) and a retention window of 60,000 milliseconds:\n\n[code example]"},{"id":"aggregation-at-read-time","title":"Aggregation at read time","role":"content","text":"When you need to query a time series, it’s cumbersome to stream all raw data points if you’re only interested in, say, an average over a given time interval. Time series only transfer the minimum required data to ensure lowest latency.\n\nHere's an example of [aggregation](https://redis.io/docs/latest/develop/data-types/timeseries#aggregation) over time buckets of 5,000 milliseconds:  \n\n[code example]"},{"id":"integrations","title":"Integrations","role":"content","text":"Redis Open Source comes with several integrations into existing time series tools. One such integration is our [RedisTimeSeries adapter](https://github.com/RedisTimeSeries/prometheus-redistimeseries-adapter) for [Prometheus](https://prometheus.io/), which keeps all your monitoring metrics inside time series while leveraging the entire [Prometheus ecosystem](https://prometheus.io/docs/prometheus/latest/storage/#remote-storage-integrations).\n\n![](https://redis.io/docs/latest/images/rs/TimeSeries-integrations.png)\n\nFurthermore, we also created direct integrations for [Grafana](https://github.com/RedisTimeSeries/grafana-redistimeseries). [This repository](https://github.com/RedisTimeSeries/prometheus-demos) contains a docker-compose setup of RedisTimeSeries, its remote write adaptor, Prometheus and [Grafana](https://grafana.com/). It also comes with a set of data generators and pre-built Grafana dashboards."},{"id":"time-series-modeling-approaches-with-redis","title":"Time series modeling approaches with Redis","role":"content","text":""},{"id":"data-modeling-approaches","title":"Data modeling approaches","role":"content","text":"Redis streams allow you to add several field value pairs in a message for a given timestamp. For each device, we collected 10 metrics that were modeled as 10 separate fields in a single stream message.\n\n![](https://redis.io/docs/latest/images/rs/TimeSeries-modeling1.png)\n\nFor sorted sets, we modeled the data in two different ways. For \"Sorted set per device\", we concatenated the metrics and separated them out by colons, e.g. `\"<timestamp>:<metric1>:<metric2>: … :<metric10>\"`.\n\n![](https://redis.io/docs/latest/images/rs/TimeSeries-modeling2.png)\n\nOf course, this consumes less memory but needs more CPU cycles to get the correct metric at read time. It also implies that changing the number of metrics per device isn’t straightforward, which is why we also benchmarked a second sorted set approach. In \"Sorted set per metric,\" we kept each metric in its own sorted set and had 10 sorted sets per device. We logged values in the format `\"<timestamp>:<metric>\"`.\n\n![](https://redis.io/docs/latest/images/rs/TimeSeries-modeling3.png)\n\nAnother alternative approach would be to normalize the data by creating a hash with a unique key to track all measurements for a given device for a given timestamp. This key would then be the value in the sorted set. However, having to access many hashes to read a time series would come at a huge cost during read time, so we abandoned this path.\n\nEach time series holds a single metric. We chose this design to maintain the Redis principle that a larger number of small keys is better than a fewer number of large keys.\n\n![](https://redis.io/docs/latest/images/rs/TimeSeries-modeling4.png)\n\nOur benchmark did not utilize the out-of-the-box secondary indexing capabilities of time series. Redis keeps a partial secondary index in each shard, and since the index inherits the same hash-slot of the key it indexes, it is always hosted on the same shard. This approach would make the setup for native data structures even more complex to model, so for the sake of simplicity, we decided not to include it in our benchmarks. Additionally, while Redis Software can use the [proxy](https://redis.com/redis-enterprise/technology/redis-enterprise-cluster-architecture/) to fan out requests for commands like [TS.MGET](https://redis.io/docs/latest/commands/ts.mget) and [TS.MRANGE](https://redis.io/docs/latest/commands/ts.mrange) to all the shards and aggregate the results, we chose not to exploit this advantage in the benchmark either."},{"id":"data-ingestion","title":"Data ingestion","role":"content","text":"For the data ingestion part of our benchmark, we compared the four approaches by measuring how many devices’ data we could ingest per second. Our client side had 8 worker threads with 50 connections each, and a pipeline of 50 commands per request.\n\nIngestion details of each approach:\n\n|                         | **Redis streams** | **Time series** | **Sorted set<br/>per device** | **Sorted set<br/>per metric** |\n| ----------------------- | ----------------- | ------------------- | ----------------------------- | ----------------------------- |\n| **Command**             | XADD              | TS.MADD             | ZADD                          | ZADD                          |\n| **Pipeline**            | 50                | 50                  | 50                            | 50                            |\n| **Metrics per request** | 5000              | 5000                | 5000                          | 500                           |\n| **# keys**              | 4000              | 40000               | 4000                          | 40000                         |\n\n![](https://redis.io/docs/latest/images/rs/TimeSeries-DataIngestion.png)\n\nAll our ingestion operations were executed at sub-millisecond latency. Although both used the same Rax data structure, the time series approach has slightly higher throughput than Redis streams.\n\nEach approach yields different results, which shows the value of prototyping against specific use cases. As we see on query performance, the sorted set per device comes with improved write throughput but at the expense of query performance. It’s a trade off between ingestion, query performance, and flexibility (remember the earlier data modeling remark)."},{"id":"read-performance","title":"Read performance","role":"content","text":"The read query we used in this benchmark queried a single time series and aggregated it in one-hour time buckets by keeping the maximum observed CPU percentage in each bucket. The time range we considered in the query was exactly one hour, so a single maximum value was returned. For time series, this is out of the box functionality.  \n\n[code example]\n\nFor the Redis streams and sorted sets approaches, we created [the following LUA scripts](https://gist.github.com/itamarhaber/0107020b91c71cb52e57e9a9c890c24e). The client once again had 8 threads and 50 connections each. Since we executed the same query, only a single shard was hit, and in all four cases this shard maxed out at 100% CPU.\n\n![](https://redis.io/docs/latest/images/rs/TimeSeries-ReadQueries.png)\n\nThis is where you can see the real power of having dedicated data structure for a given use case with a toolbox that runs alongside it. Using time series exceeds all other approaches and is the only one to achieve sub-millisecond response times."},{"id":"memory-utilization","title":"Memory utilization","role":"content","text":"For both the Redis streams and sorted set approaches, the samples were stored as strings, while time series stored them as doubles. In this specific data set, we chose a CPU measurement with rounded integer values between 0-100, which thus consumes two bytes of memory as a string. With time series, each metric had 64-bit precision.\n\nTime series can dramatically reduce the memory consumption when compared against both sorted set approaches. Given the unbounded nature of time series data, this is typically a critical criteria to evaluate - the overall data set size that needs to be retained in memory. Redis streams reduces the memory consumption further but would be equal or higher than time series when more digits for a higher precision would be required.\n\n![](https://redis.io/docs/latest/images/rs/TimeSeries-UsedMemory.png)"},{"id":"more-info","title":"More info","role":"content","text":"- [Time series quick start](https://redis.io/docs/latest/develop/data-types/timeseries)\n- [Time series commands](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/timeseries/commands)\n- [Time series configuration](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/timeseries/config)\n- [RedisTimeSeries source](https://github.com/RedisTimeSeries/RedisTimeSeries)"}],"examples":[{"id":"secondary-indexing-ex0","language":"sh","code":"TS.CREATE temperature RETENTION 60000 LABELS sensor_id 2 area_id 32","section_id":"secondary-indexing"},{"id":"aggregation-at-read-time-ex0","language":"sh","code":"127.0.0.1:12543> TS.RANGE temperature:3:32 1548149180000 1548149210000 AGGREGATION avg 5000\n    1) 1) (integer) 1548149180000\n       2) \"26.199999999999999\"\n    2) 1) (integer) 1548149185000\n       2) \"27.399999999999999\"\n    3) 1) (integer) 1548149190000\n       2) \"24.800000000000001\"\n    4) 1) (integer) 1548149195000\n       2) \"23.199999999999999\"\n    5) 1) (integer) 1548149200000\n       2) \"25.199999999999999\"\n    6) 1) (integer) 1548149205000\n       2) \"28\"\n    7) 1) (integer) 1548149210000\n       2) \"20\"","section_id":"aggregation-at-read-time"},{"id":"read-performance-ex0","language":"sh","code":"127.0.0.1:12543> TS.RANGE cpu_usage_user{1340993056} 1451606390000 1451609990000 AGGREGATION max 3600000","section_id":"read-performance"}]}
{"schema_version":2,"id":"operate/radar/agent","title":"Connect clusters with an agent","url":"https://redis.io/docs/latest/operate/radar/agent/","summary":"Install an agent to connect Redis Software and Redis Open Source clusters that Radar cannot reach directly.","tags":["docs","operate","radar"],"last_updated":"2026-10-07T13:34:00-05:00","page_type":"content","content_hash":"9a3f4a1234edbeeec9a7bc05e186fc3bd5cc64729c35640f963ce55d84ffebb6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The Radar agent collects data from Redis deployments that Radar can't reach directly. You install it on a host inside your network, and it opens an outbound connection to Radar. Radar never needs inbound access to your network."},{"id":"when-to-connect-clusters-with-an-agent","title":"When to connect clusters with an agent","role":"content","text":"Use an agent for Redis Software and Redis Open Source deployments behind a firewall, in a private subnet, or on a segmented network.\n\nThe agent always connects to Radar over Transport Layer Security (TLS), using gRPC, and makes separate local connections to each Redis endpoint you configure.\n\nRadar accepts those connections on its agent gRPC endpoint, which isn't enabled on every Radar deployment. Confirm the endpoint is available before you install an agent, and get its host and port from whoever administers your Radar deployment.\n\nThe agent runs in one of two modes. They differ in where your source credentials live.\n\n| Mode | Radar holds source credentials | You maintain | Choose it when |\n|---|---|---|---|\n| Managed | Yes | Nothing on the host | You want to add and change sources from the Radar UI. |\n| Static | No | A YAML file on the agent host | Source credentials must never leave the agent host. |"},{"id":"trust-a-private-certificate-authority","title":"Trust a private certificate authority","role":"content","text":"If Radar presents a certificate from a public certificate authority (CA), certificate verification doesn't require any configuration. The agent verifies it against the host's system roots.\n\nIf Radar uses a private CA, point the agent at its certificate bundle instead: `tls.ca_file` in a static agent's configuration file, or `--ca-file` on `radar-agent activate` for a managed agent. The bundle you supply replaces the host's system roots rather than adding to them, so it must contain every certificate the agent needs to verify Radar."},{"id":"install-the-agent","title":"Install the agent","role":"setup","text":"Each Radar release publishes standalone agent tarballs, so a collector host runs the agent without running Radar itself.\n\nInstall the agent on a host that can reach both Radar and the Redis endpoints you want to collect from.\n\n1. Get the tarball for your platform from the [Redis Download Center](https://redis.io/downloads/#Modules_Tools_and_Integration).\n\n   Take the `radar-agent-fips-` build only if you require Federal Information Processing Standards (FIPS) 140-3. It refuses to start unless FIPS 140-3 mode is active in its runtime.\n\n   <br>\n\n2. Extract the archive and confirm the version.\n\n   [code example]\n\n   The agent must report version `1.0.0` or newer. Radar rejects anything older.\n\n   <br>\n\n3. Create the `mcm` service identity the unit runs as.\n\n   Skip this step if the host already runs Radar from the RPM, which creates `mcm` for you.\n\n   [code example]\n\n   <br>\n\n4. Install the binary, the unit, and the environment file, then create the state directory.\n\n   [code example]\n\n   `/var/lib/radar-agent` holds runtime state. Create it now, because a managed agent writes to it during activation, before the service first starts. If you install the binary elsewhere, update the paths in the unit.\n\nRun every agent command as the service identity, as `sudo -u mcm /usr/libexec/mcm/radar-agent <command>`."},{"id":"set-up-a-managed-agent","title":"Set up a managed agent","role":"content","text":"1. In Radar, go to **Settings > Agents** and select **Activate managed agent**.\n\n   Radar shows an activation command and a daemon command.\n\n   ![The Activate managed agent dialog, showing the activation command and the daemon command](https://redis.io/docs/latest/images/radar/activate-managed-agent.png)\n\n   <br>\n\n2. Copy the whole activation command from the dialog, then run it on the agent host as the service identity.\n\n   Keep every argument the dialog generated. On Redis Cloud, the dialog adds `--tenant-activation-id`, a short-lived handle bound to your tenant, and the agent gRPC service rejects an activation that omits it. A self-managed install doesn't generate one.\n\n   Replace the endpoint and the agent name. On Redis Cloud, the endpoint is `agent-radar.redis.io:443`. On a self-managed install, it's your deployment's public agent gRPC host and port, and the public port isn't necessarily `9443`.\n\n   [code example]\n\n   When the dialog shows a **Start before** time, run the command before it. The command prints an activation code and waits for approval. You use that code to identify this host in Radar.\n\n   <br>\n\n3. In Radar, find the request under **Pending activations**, confirm the activation code matches, and select **Approve**.\n\n   ![The Agents tab, showing pending activations with Approve and Deny actions above the registered agents list](https://redis.io/docs/latest/images/radar/settings-agents.png)\n\n   The activation expires 15 minutes after the command prints the code, so approve it while the command is still waiting.\n\n   <br>\n\n4. Enter the Redis Software or Redis Open Source connection details in the approval form, then select **Confirm approval**.\n\n   Radar stores these credentials and sends them to the agent. Entering the details doesn't submit the approval; **Confirm approval** does.\n\n   Wait for the agent to print `Activation approved; managed credential stored.` before you continue. The daemon needs that credential to start.\n\n   <br>\n\n5. Set the daemon arguments and start the service.\n\n   Edit `/etc/radar-agent/radar-agent.env` and replace the default `RADAR_AGENT_DAEMON_ARGS` line with:\n\n   [code example]\n\n   `--metrics-addr` is optional. It exposes the agent's health and metrics endpoints on the address you give it.\n\n   [code example]\n\nTo change an agent's sources later, go to **Settings > Agents**, find the agent under **Registered agents**, and select **Edit connections**.\n\n> [!NOTE]\n> Managed mode stores the credential it was issued in `/var/lib/radar-agent/agent-key.json`. Keep that file readable only by its owner. The configuration cache Radar writes alongside it holds no secrets."},{"id":"set-up-a-static-agent","title":"Set up a static agent","role":"content","text":"1. In Radar, go to **Settings > Agents** and select **Provision static agent**.\n\n   Radar returns an agent ID and a one-time access token in the form `<key_id>.<secret>`. Copy both now, because Radar shows the token only once.\n\n   Manage these tokens later under **Settings > Access keys**.\n\n   ![The Access keys tab, listing credential keys with their creation date, last use, and Revoke action](https://redis.io/docs/latest/images/radar/settings-access-keys.png)\n\n   <br>\n\n2. Generate one universally unique identifier (UUID) for each Redis source you plan to collect from.\n\n   [code example]\n\n   <br>\n\n3. Copy the example configuration from the archive, fill it in, and install it.\n\n   [code example]\n\n   Set the agent ID, the token, and one UUID per source. See [Configuration reference](#configuration-reference) for every setting. Then install it with the ownership the service expects.\n\n   [code example]\n\n   <br>\n\n4. Validate the configuration.\n\n   [code example]\n\n   <br>\n\n5. Create the redaction salt.\n\n   The example configuration turns redaction on and reads the salt from `/var/lib/radar-agent/redaction-salt`. The agent reads that file and doesn't create it, so generate it once before the first collection.\n\n   [code example]\n\n   Keep the same salt across restarts and upgrades. A new salt changes what existing values redact to, so don't overwrite it if you repeat these steps.\n\n   <br>\n\n6. Collect from every source and print a redacted summary without submitting to Radar.\n\n   [code example]\n\n   <br>\n\n7. Submit one collection to confirm the connection to Radar works.\n\n   [code example]\n\n   Radar creates each source the first time it receives data from that source, and uses your `name` value as the display label when that value is valid.\n\n   <br>\n\n8. Start the service.\n\n   The default `RADAR_AGENT_DAEMON_ARGS` in `/etc/radar-agent/radar-agent.env` already points at `/etc/radar-agent/config.yaml`. To expose health and metrics, add `--metrics-addr 127.0.0.1:9090` to that line.\n\n   [code example]\n\n> [!NOTE]\n> Don't also add a static agent's sources as regular Radar connections. In static mode the endpoint and credential values belong only in the agent's YAML file."},{"id":"configuration-reference","title":"Configuration reference","role":"configuration","text":"Static mode uses a single YAML file. Managed mode doesn't.\n\n| Setting | Description |\n|---|---|\n| `agent.id` | The agent ID Radar returned when you provisioned the agent. It must match the identity bound to the token, or Radar denies every submission. |\n| `agent.version` | Optional. Defaults to the version of the installed binary. |\n| `radar.endpoint` | The Radar agent endpoint, as `host:port`. |\n| `radar.agent_key` | The one-time token, as `<key_id>.<secret>`. |\n| `radar.tls.ca_file` | Optional. A PEM bundle used to verify Radar's certificate, replacing the host's system roots. |\n| `radar.tls.server_name` | Optional. Overrides the certificate name the agent verifies. |\n| `redaction.enabled` | Whether the agent redacts values in the payloads it submits. Enabled by default. |\n| `redaction.salt_file` | The salt file the agent uses so a given value redacts to the same result on every collection. |\n| `collection.interval` | How often the agent collects when running as a daemon. |\n| `collection.timeout` | How long a single collection can run. |\n| `sources[].id` | The UUID you generated for this source. |\n| `sources[].name` | A name for the source. Radar uses it as the display label. |\n| `sources[].type` | Either `redis_enterprise` for Redis Software or `redis_oss` for Redis Open Source. |\n\nA `redis_enterprise` source takes `base_url`, `username`, and `password`. To collect from a cluster running an older Redis Software version, set `old_version_compatibility.enabled` to `true`.\n\nA `redis_oss` source takes `host`, `port`, and `password`. Set `username` only if you connect as an access control list (ACL) user.\n\nEach source type takes a different TLS setting: `tls.skip_verify` for a `redis_enterprise` source, and `tls.enabled` for a `redis_oss` source. Neither one affects the agent's connection to Radar, which `radar.tls` controls.\n\nTo keep secrets out of the file, reference environment variables instead of literal values."},{"id":"collect-from-an-isolated-network","title":"Collect from an isolated network","role":"content","text":"When the agent host can't reach Radar at all, collect and submit in two steps from different hosts. Install the agent on both hosts, and give each one only the configuration it needs. `export` requires your source credentials and no Radar token, and `submit-export` requires the Radar endpoint and token and no sources. Use the same `agent.id` in both files.\n\nThis keeps your source credentials on the collector, which is the custody benefit static mode exists for.\n\nOn the collector, the sources and the redaction salt:\n\n[code example]\n\nOn the submission host, the Radar endpoint and token:\n\n[code example]\n\n1. On the host that can reach your Redis sources, collect to a file.\n\n   [code example]\n\n   <br>\n\n2. Move the file to a host that can reach Radar, then submit it.\n\n   [code example]\n\nThe exported file holds sanitized telemetry only. It never contains your Radar token or your source credentials. Each configuration file does hold secrets for its own half, so protect both hosts."},{"id":"monitor-and-secure-the-agent","title":"Monitor and secure the agent","role":"content","text":"Every agent appears in Radar under **Settings > Agents** and in the **Connected agents** list on the Connections page. Radar shows each agent's mode, platform, version, and last heartbeat, along with the combined health of the sources it collects from.\n\n![The Connections page, with the Connected agents list showing each agent's mode, platform, version, and status](https://redis.io/docs/latest/images/radar/connections-with-agents.png)\n\nTo expose health and metrics endpoints on the agent host, pass `--metrics-addr` when you start the daemon:\n\n- `GET /healthz` returns a health snapshot: `200` when the agent is healthy, `503` when a source is failing.\n- `GET /metrics` returns per-source collection, submission, and failure counters in Prometheus format.\n\n> [!WARNING]\n> These endpoints are unauthenticated. Bind them to loopback, as in `--metrics-addr 127.0.0.1:9090`, or put a firewall in front of them.\n\nThe daemon writes a local health snapshot only when you start it with `--health-file`, and the `health` command needs that same path. Add `--health-file /var/lib/radar-agent/health.json` to `RADAR_AGENT_DAEMON_ARGS` in `/etc/radar-agent/radar-agent.env`, restart the service, then read it:\n\n[code example]\n\n`--metrics-addr` serves health over HTTP but doesn't write this file. Use `journalctl` for the agent's logs and `systemctl` to restart the service.\n\nBoth `/etc/radar-agent/config.yaml` and `/var/lib/radar-agent/agent-key.json` hold secrets, so restrict them to their owner. Never set `tls.insecure_skip_verify` outside local development, because it turns off certificate verification. To block an agent, revoke its key under **Settings > Access keys**. Revoking takes effect centrally, and the agent can no longer connect.\n\nSource passwords and the agent's token are redacted from logs, diagnostics, health output, error messages, and export bundles."},{"id":"upgrade-or-remove-the-agent","title":"Upgrade or remove the agent","role":"content","text":"To upgrade, get the new tarball, verify it, and replace the binary in place. Your configuration and environment file are untouched. If you run a static agent whose configuration file holds both source and Radar credentials, revalidate it before restarting:\n\n[code example]\n\nKeep `/etc/radar-agent` and `/var/lib/radar-agent` in place, because the agent needs its state to stay registered.\n\nRadar enforces a version policy on every request. An agent older than the minimum supported version is rejected until you upgrade it. An agent newer than the Radar deployment supports is also rejected, and Radar asks for a server upgrade instead.\n\nIf Radar's database is reset, activate the managed agent again. Stop the service before you clear the managed registration state. Recreate the state directory if you removed it, because `/var/lib` is owned by root and `mcm` can't create the directory during activation.\n\n[code example]\n\nThen repeat [Set up a managed agent](#set-up-a-managed-agent) and start the service again. On Redis Cloud, get a fresh tenant activation handle from the dialog first.\n\nTo stop collecting from this host, turn off the service and delete its configuration and state.\n\n[code example]\n\nThen delete `/usr/libexec/mcm/radar-agent` and `/usr/lib/systemd/system/radar-agent.service`, and revoke the agent's key in Radar under **Settings > Access keys**."},{"id":"next-steps","title":"Next steps","role":"content","text":"After the agent reports its first collection, its sources appear alongside your other clusters. See [Monitor your fleet](https://redis.io/docs/latest/operate/radar/monitor)."}],"examples":[{"id":"install-the-agent-ex0","language":"bash","code":"tar -xzf radar-agent-<tag>-linux-amd64.tar.gz\n   cd radar-agent-<tag>-linux-amd64\n   ./radar-agent version","section_id":"install-the-agent"},{"id":"install-the-agent-ex1","language":"bash","code":"sudo groupadd --system mcm\n   sudo useradd --system --gid mcm --home-dir / --no-create-home \\\n     --shell /sbin/nologin --comment \"Radar service identity\" mcm","section_id":"install-the-agent"},{"id":"install-the-agent-ex2","language":"bash","code":"sudo install -d -m 0755 /usr/libexec/mcm\n   sudo install -o root -g root -m 0755 radar-agent /usr/libexec/mcm/radar-agent\n   sudo install -d -o root -g mcm -m 0750 /etc/radar-agent\n   sudo install -o root -g mcm -m 0640 systemd/radar-agent.env /etc/radar-agent/radar-agent.env\n   sudo install -o root -g root -m 0644 systemd/radar-agent.service /usr/lib/systemd/system/\n   sudo install -d -o mcm -g mcm -m 0700 /var/lib/radar-agent\n   sudo systemctl daemon-reload","section_id":"install-the-agent"},{"id":"set-up-a-managed-agent-ex0","language":"bash","code":"sudo -u mcm /usr/libexec/mcm/radar-agent activate \\\n     --endpoint <radar-agent-grpc-host>:<port> \\\n     --state-dir /var/lib/radar-agent \\\n     --display-name <agent-name> \\\n     --tenant-activation-id <tenant-activation-id>","section_id":"set-up-a-managed-agent"},{"id":"set-up-a-managed-agent-ex1","language":"bash","code":"RADAR_AGENT_DAEMON_ARGS=--managed --state-dir /var/lib/radar-agent --metrics-addr 127.0.0.1:9090","section_id":"set-up-a-managed-agent"},{"id":"set-up-a-managed-agent-ex2","language":"bash","code":"sudo systemctl enable --now radar-agent.service","section_id":"set-up-a-managed-agent"},{"id":"set-up-a-static-agent-ex0","language":"bash","code":"uuidgen","section_id":"set-up-a-static-agent"},{"id":"set-up-a-static-agent-ex1","language":"bash","code":"cp examples/static-agent.yaml config.yaml","section_id":"set-up-a-static-agent"},{"id":"set-up-a-static-agent-ex2","language":"bash","code":"sudo install -o root -g mcm -m 0640 config.yaml /etc/radar-agent/config.yaml","section_id":"set-up-a-static-agent"},{"id":"set-up-a-static-agent-ex3","language":"bash","code":"sudo -u mcm /usr/libexec/mcm/radar-agent validate --config /etc/radar-agent/config.yaml","section_id":"set-up-a-static-agent"},{"id":"set-up-a-static-agent-ex4","language":"bash","code":"sudo -u mcm sh -c 'umask 077 && openssl rand -hex 32 > /var/lib/radar-agent/redaction-salt'","section_id":"set-up-a-static-agent"},{"id":"set-up-a-static-agent-ex5","language":"bash","code":"sudo -u mcm /usr/libexec/mcm/radar-agent dry-run --config /etc/radar-agent/config.yaml","section_id":"set-up-a-static-agent"},{"id":"set-up-a-static-agent-ex6","language":"bash","code":"sudo -u mcm /usr/libexec/mcm/radar-agent once --config /etc/radar-agent/config.yaml","section_id":"set-up-a-static-agent"},{"id":"set-up-a-static-agent-ex7","language":"bash","code":"sudo systemctl enable --now radar-agent.service","section_id":"set-up-a-static-agent"},{"id":"collect-from-an-isolated-network-ex0","language":"yaml","code":"agent:\n  id: \"<agent-id>\"\n\nredaction:\n  enabled: true\n  salt_file: \"/var/lib/radar-agent/redaction-salt\"\n\nsources:\n  - id: \"<source-uuid>\"\n    name: \"<source-name>\"\n    type: redis_enterprise\n    base_url: \"https://<cluster-host>:9443\"\n    username: \"<username>\"\n    password: \"<password>\"","section_id":"collect-from-an-isolated-network"},{"id":"collect-from-an-isolated-network-ex1","language":"yaml","code":"agent:\n  id: \"<agent-id>\"\n\nradar:\n  endpoint: \"<radar-agent-grpc-host>:<port>\"\n  agent_key: \"<key-id>.<secret>\"","section_id":"collect-from-an-isolated-network"},{"id":"collect-from-an-isolated-network-ex2","language":"bash","code":"sudo -u mcm /usr/libexec/mcm/radar-agent export --config /etc/radar-agent/config.yaml --output telemetry.json","section_id":"collect-from-an-isolated-network"},{"id":"collect-from-an-isolated-network-ex3","language":"bash","code":"sudo -u mcm /usr/libexec/mcm/radar-agent submit-export --config /etc/radar-agent/config.yaml --input telemetry.json","section_id":"collect-from-an-isolated-network"},{"id":"monitor-and-secure-the-agent-ex0","language":"bash","code":"sudo -u mcm /usr/libexec/mcm/radar-agent health --health-file /var/lib/radar-agent/health.json","section_id":"monitor-and-secure-the-agent"},{"id":"upgrade-or-remove-the-agent-ex0","language":"bash","code":"sudo -u mcm /usr/libexec/mcm/radar-agent validate --config /etc/radar-agent/config.yaml\nsudo systemctl restart radar-agent.service","section_id":"upgrade-or-remove-the-agent"},{"id":"upgrade-or-remove-the-agent-ex1","language":"bash","code":"sudo systemctl stop radar-agent.service\nsudo rm -rf /var/lib/radar-agent\nsudo install -d -o mcm -g mcm -m 0700 /var/lib/radar-agent","section_id":"upgrade-or-remove-the-agent"},{"id":"upgrade-or-remove-the-agent-ex2","language":"bash","code":"sudo systemctl disable --now radar-agent.service\nsudo rm -rf /etc/radar-agent /var/lib/radar-agent","section_id":"upgrade-or-remove-the-agent"}]}
{"schema_version":2,"id":"operate/radar/connect","title":"Connect clusters","url":"https://redis.io/docs/latest/operate/radar/connect/","summary":"Add Redis Software, Redis Cloud, Redis Open Source, Amazon ElastiCache, and Google Memorystore sources to Radar.","tags":["docs","operate","radar"],"last_updated":"2026-10-06T08:54:47-05:00","page_type":"content","content_hash":"be90488ca93fa5cfe14d14b980b5b12c7f86014f91e99305d902c34e93a7d473","sections":[{"id":"overview","title":"Overview","role":"overview","text":"After installing Redis Radar, it's time to connect your clusters. Add each cluster manually — Radar doesn't discover clusters on its own. After you add a cluster, Radar tests the connection and then keeps collecting that source's state automatically going forward. While Radar lets you view your cluster's status, it doesn't change the any of the cluster settings or configuration."},{"id":"what-you-can-connect","title":"What you can connect","role":"content","text":"| Type | How Radar reaches it | What you supply |\n|---|---|---|\n| [Redis Software](#connect-a-redis-software-cluster) | The cluster REST API, over HTTPS on port 9443 | A hostname or IP address, plus an account on the cluster |\n| [Redis Open Source](#connect-a-redis-open-source-instance) | Direct access to the Redis instance | A hostname and port, plus credentials if the instance requires them |\n| [Redis Cloud](#connect-a-redis-cloud-account) | The Redis Cloud API | An API account key and an API user key |\n| [Amazon ElastiCache](#connect-amazon-elasticache) | The AWS control-plane APIs | Read-only AWS credentials and the regions to scan |\n| [Google Memorystore](#connect-google-memorystore) | The Google Cloud APIs | A read-only service account, plus the project and regions to scan |\n\nRadar encrypts every credential before storing it. Credentials are never returned through the API and never written to logs.\n\n![The Add connection dialog, with the connection type selector open](https://redis.io/docs/latest/images/radar/add-connection.png)"},{"id":"self-managed-connections","title":"Self-managed connections","role":"content","text":"Connect Redis Software clusters and Redis Open Source instances that you run yourself. Radar reaches each one directly, so you supply a host and an account on the cluster or instance. Redis Software and Redis Open Source connections are per cluster or per instance."},{"id":"before-you-connect","title":"Before you connect","role":"content","text":"For every connection you need:\n\n- **Credentials for the cluster or instance.** Radar uses the same management interface you do, so it needs an account there. Read access is enough.\n- **Network access from Radar to the source.** Radar connects outbound. Nothing needs to connect back to Radar, so no inbound rule is required on the Radar host. If Radar can't reach a cluster directly, install an agent inside that network instead. See [Connect clusters with an agent](https://redis.io/docs/latest/operate/radar/agent)."},{"id":"connect-a-redis-software-cluster","title":"Connect a Redis Software cluster","role":"content","text":"Radar reads Redis Software clusters through the cluster REST API.\n\n1. Select **Add connection**, from either the top bar or the **Connections** page.\n2. Set the **connection type** to **Redis Software**.\n3. Enter a **display name**. Use something you can recognize in a fleet-wide list, such as `Production East 1`.\n4. Enter the cluster's **hostname or IP address**, for example `cluster.redis.internal` or `10.0.0.1`.\n5. Enter the **port**. The default is `9443`, the cluster REST API port.\n6. Enter the **username** and **password** of an account on the cluster.\n7. Select **Add connection**.\n\nRadar tests the connection before it saves anything. If the test fails, Radar shows the error and does not create the connection, so you can correct the details and select **Retry**.\n\nRadar always reaches the REST API over HTTPS.\n\n![The Add connection dialog set to connection type Redis Software](https://redis.io/docs/latest/images/radar/add-connection-redis-software.png)\n\n> [!NOTE]\n> Point Radar at the cluster's fully qualified domain name rather than one node's address. A node address works until that node is unavailable, at which point Radar reports the whole cluster as unreachable."},{"id":"connect-a-redis-open-source-instance","title":"Connect a Redis Open Source instance","role":"content","text":"Radar connects straight to the instance, so it needs network access to that endpoint.\n\n1. Select **Add connection**.\n2. Set the **connection type** to **Redis Open Source**.\n3. Enter a **display name**.\n4. Enter the **hostname or IP address** and **port**. The default port is `6379`.\n5. Enter a **username** and **password** if the instance requires authentication. Leave both empty if it does not.\n6. Select **Use TLS (rediss://)** if the instance requires an encrypted connection.\n7. Select **Add connection**.\n\n![The Add connection dialog set to connection type Redis Open Source](https://redis.io/docs/latest/images/radar/add-connection-redis-open-source.png)"},{"id":"cloud-connections","title":"Cloud connections","role":"content","text":"Connect a Redis Cloud account, Amazon ElastiCache, or Google Memorystore. These connections use each provider's own API rather than talking to a database directly, so what you supply is API credentials rather than an account on a cluster. One Redis Cloud connection covers every subscription and database in that account, and one ElastiCache or Memorystore connection covers every resource in the regions you select. ElastiCache and Memorystore resources appear on the **Databases** view rather than **Clusters**."},{"id":"before-you-connect-1","title":"Before you connect","role":"content","text":"For every connection you need:\n\n- **API credentials for the provider.** A Redis Cloud API account key and API user key, read-only AWS credentials, or a read-only Google Cloud service account. Radar never writes to your Redis Cloud subscription or provider account.\n- **The regions to scan, for Amazon ElastiCache and Google Memorystore.** Radar only scans the regions you list.\n- **Network access from Radar to the provider's API.** Radar connects outbound only."},{"id":"connect-a-redis-cloud-account","title":"Connect a Redis Cloud account","role":"content","text":"A Redis Cloud connection uses the Redis Cloud API, so it covers every subscription and database in the account at once.\n\n1. Create a Redis Cloud API key. You need both the **API account key** and an **API user key**. See [Redis Cloud API](https://redis.io/docs/latest/operate/rc/api).\n2. In Radar, select **Add connection**.\n3. Set the **connection type** to **Redis Cloud**.\n4. Enter a **display name**.\n5. Enter the **API account key** and the **API user key**.\n6. Select **Add connection**.\n\n![The Add connection dialog set to connection type Redis Cloud](https://redis.io/docs/latest/images/radar/add-connection-redis-cloud.png)"},{"id":"connect-amazon-elasticache","title":"Connect Amazon ElastiCache","role":"content","text":"One ElastiCache connection covers every ElastiCache resource in the regions you select.\n\n**Amazon ElastiCache** appears in the connection type list only if an administrator enabled the ElastiCache connector, which is off by default.\n\nBefore you connect, create an AWS identity with read-only ElastiCache access. The policy needs no write permissions and no cache data-plane permissions:\n\n- `elasticache:DescribeReplicationGroups`\n- `elasticache:DescribeCacheClusters`\n- `elasticache:DescribeServerlessCaches`\n- `cloudwatch:GetMetricData`\n- `tag:GetResources`\n- `ec2:DescribeRegions`\n\nHow Radar authenticates depends on where it runs:\n\n**Radar on Redis Cloud:**\n\nOn Redis Cloud, Radar assumes a read-only role in your AWS account, so you supply a role ARN rather than keys:\n\n1. Select **Add connection**.\n2. Set the **connection type** to **Amazon ElastiCache**.\n3. Enter your 12-digit **AWS account ID**, then select **Continue**. Radar generates an external ID for this connection and shows a trust policy that uses it.\n4. In AWS, create a read-only IAM role with those permissions, and give it the trust policy Radar shows.\n5. Enter a **display name**.\n6. Enter the **role ARN**, and the **AWS regions** to scan, separated by commas, for example `us-east-1, us-west-2`.\n7. Select **Add connection**.\n\nRadar generates the external ID and keeps it. Don't supply one of your own, and don't reuse the external ID from another connection.\n\n**Self-managed Radar:**\n\nOn a self-managed install, Radar authenticates with a long-lived IAM access key pair:\n\n1. Select **Add connection**.\n2. Set the **connection type** to **Amazon ElastiCache**.\n3. Enter a **display name**.\n4. Enter the **AWS access key ID** and **AWS secret access key**.\n5. Enter the **AWS regions** to scan, separated by commas, for example `us-east-1, us-west-2`.\n6. Select **Add connection**.\n\nRadar derives the AWS account ID itself, so you do not enter it.\n\n\n\nBoth the API server and the worker need outbound HTTPS on port 443 to the AWS control-plane endpoints in every region you configure: `sts`, `elasticache`, `monitoring`, `tagging`, and `ec2`. Radar never opens a connection to a cache endpoint.\n\n![The Add connection dialog set to connection type Amazon ElastiCache](https://redis.io/docs/latest/images/radar/add-connection-amazon-elasticache.png)\n\n> [!NOTE]\n> Blocking the CloudWatch or tagging endpoints degrades what Radar can report and produces a capability warning. Blocking the identity or ElastiCache inventory endpoints stops the connection test and collection outright."},{"id":"connect-google-memorystore","title":"Connect Google Memorystore","role":"content","text":"One Memorystore connection covers every Memorystore resource in the regions you select, across the Redis, Valkey, and Memcached engines.\n\n**Google Memorystore** appears in the connection type list only if an administrator enabled the Memorystore connector, which is off by default.\n\nBefore you connect, create a service account in the target project and grant it the read roles for the engines you run:\n\n| Role | Covers |\n|---|---|\n| `roles/redis.viewer` | Memorystore for Redis and Redis Cluster |\n| `roles/memorystore.viewer` | Memorystore for Valkey only |\n| `roles/memcache.viewer` | Memorystore for Memcached |\n| `roles/monitoring.viewer` | Cloud Monitoring metrics for every engine |\n\nA single custom role with the same read permissions works too, as long as it keeps `resourcemanager.projects.get`.\n\nHow Radar authenticates depends on where it runs:\n\n**Radar on Redis Cloud:**\n\nOn Redis Cloud, Radar impersonates the service account rather than holding a key for it, so there is no key to create or paste. Prepare the project first:\n\n- Label the project `redis-radar-tenant=<your-tenant-id>`. Your tenant ID is in the Radar user menu. Radar checks the label before it creates the connection and before every collection, so a missing label fails the connection test.\n- Grant Radar's service account, `radar-memorystore-delegate@rcp-prod.iam.gserviceaccount.com`, the **Service Account Token Creator** role (`roles/iam.serviceAccountTokenCreator`) on the service account you created.\n\n1. Select **Add connection**.\n2. Set the **connection type** to **Google Memorystore**.\n3. Enter a **display name**.\n4. Enter the **GCP project ID**, for example `my-gcp-project`.\n5. Enter the **GCP regions** to scan, separated by commas, for example `us-central1, us-east1`.\n6. Enter the **service account email**, for example `radar-reader@my-gcp-project.iam.gserviceaccount.com`.\n7. Select **Add connection**.\n\n**Self-managed Radar:**\n\nOn a self-managed install, Radar authenticates with a service account key:\n\n1. Select **Add connection**.\n2. Set the **connection type** to **Google Memorystore**.\n3. Enter a **display name**.\n4. Enter the **GCP project ID**, for example `my-gcp-project`.\n5. Enter the **GCP regions** to scan, separated by commas, for example `us-central1, us-east1`.\n6. Paste the **service account key JSON**. It must be 16 KiB or less.\n7. Select **Add connection**.\n\n\n\nBoth the API server and the worker need outbound HTTPS on port 443 to `oauth2.googleapis.com`, `redis.googleapis.com`, `memorystore.googleapis.com`, `memcache.googleapis.com`, and `monitoring.googleapis.com`. Allowing `*.googleapis.com` covers the whole path.\n\n![The Add connection dialog set to connection type Google Memorystore](https://redis.io/docs/latest/images/radar/add-connection-google-memorystore.png)"},{"id":"secure-cluster-connections","title":"Secure cluster connections","role":"content","text":"Radar holds credentials for every cluster in your fleet, so treat the connection path as sensitive.\n\n- **Encrypt the connection.** Redis Software connections always use HTTPS. For Redis Open Source, select **Use TLS** unless the instance is unencrypted and you accept that.\n- **Give Radar its own account on each cluster.** A dedicated account keeps Radar's access auditable and separate from any user's, and lets you limit what Radar can do.\n- **Protect the encryption key.** On a self-managed install, every credential Radar stores is encrypted with the key you supplied at install. Back that key up alongside the database and store the backup separately. See [Install Radar](https://redis.io/docs/latest/operate/radar/install#the-credential-encryption-key)."},{"id":"remove-or-reconnect-a-cluster","title":"Remove or reconnect a cluster","role":"content","text":"Only administrators can remove a connection. See [Manage access](https://redis.io/docs/latest/operate/radar/manage-access).\n\nRadar has no way to edit a saved connection's credentials, host, port, or scanned regions. If any of those change — for example, a rotated password or access key, or a cluster that moved to a new address — remove the connection and add it again with the new details.\n\nTo remove a connection:\n\n1. Go to the **Connections** page.\n2. Select **More options** next to the connection.\n3. Select **Remove connection**.\n4. Confirm the removal.\n\nRemoving a connection permanently deletes the health and usage data Radar collected through it. It does not affect the underlying Redis deployment or cloud account, and it cannot be undone.\n\n> [!WARNING]\n> For a Redis Cloud account connection, removing it removes every subscription discovered through that account, not just one database."},{"id":"next-steps","title":"Next steps","role":"content","text":"Radar starts collecting information as soon as a connection is saved. The first collection populates the fleet view, and Radar refreshes each source after that. See [Monitor clusters and databases](https://redis.io/docs/latest/operate/radar/monitor)."}],"examples":[]}
{"schema_version":2,"id":"operate/radar","title":"Redis Radar","url":"https://redis.io/docs/latest/operate/radar/","summary":"Monitor the status of every Redis cluster you run from one place.","tags":["docs","operate","radar"],"last_updated":"2026-10-02T17:56:08-07:00","children":[{"id":"operate/radar/install","summary":"Install Radar on RHEL or Kubernetes, then make it reachable from your network.","title":"Install self-managed Radar","url":"https://redis.io/docs/latest/operate/radar/install/"},{"id":"operate/radar/connect","summary":"Add Redis Software, Redis Cloud, Redis Open Source, Amazon ElastiCache, and Google Memorystore sources to Radar.","title":"Connect clusters","url":"https://redis.io/docs/latest/operate/radar/connect/"},{"id":"operate/radar/agent","summary":"Install an agent to connect Redis Software and Redis Open Source clusters that Radar cannot reach directly.","title":"Connect clusters with an agent","url":"https://redis.io/docs/latest/operate/radar/agent/"},{"id":"operate/radar/monitor","summary":"Read fleet health, capacity, usage, and alerts across every cluster Radar collects from.","title":"Monitor clusters and databases","url":"https://redis.io/docs/latest/operate/radar/monitor/"},{"id":"operate/radar/manage-access","summary":"Create Radar users, assign roles, and connect LDAP or SAML SSO.","title":"Manage access","url":"https://redis.io/docs/latest/operate/radar/manage-access/"},{"id":"operate/radar/licenses-and-certificates","summary":"Track license and certificate expiry across your fleet.","title":"Licenses and certificates","url":"https://redis.io/docs/latest/operate/radar/licenses-and-certificates/"},{"id":"operate/radar/release-notes","summary":"Get Radar release artifacts and see what changed in each release.","title":"Release notes","url":"https://redis.io/docs/latest/operate/radar/release-notes/"}],"page_type":"content","content_hash":"0d1365d6bc01dec788a281abe9b0d4cd291e17c379dedcf1f31ead6564268ab6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Radar give you one place to view the status of every Redis cluster in your fleet. Instead of checking the status of your clusters individually, get a complete view of them all with Redis Radar.\n\n![The Radar Overview page, showing fleet status, inventory, database health, alerts, certificates, and version distribution](https://redis.io/docs/latest/images/radar/radar-overview.png)\n\nRadar connects to each cluster, collects its state, and presents one fleet-wide view across Redis Software, Redis Cloud, Redis Open Source, Amazon ElastiCache, and Google Memorystore. Radar is primarily a visibility tool. Connecting a cluster to Radar does not change how that cluster runs on its own."},{"id":"how-you-run-radar","title":"How you run Radar","role":"content","text":"Radar runs two ways:\n\n- **[Redis Cloud](https://redis.io/docs/latest/operate/rc/radar).** Sign in with your existing Redis Cloud credentials. Redis Cloud hosts and manages the Radar deployment for you.\n- **[Self-managed](https://redis.io/docs/latest/operate/radar/install).** Install Radar on your own infrastructure with a Helm chart or an RPM."},{"id":"what-radar-shows-you","title":"What Radar shows you","role":"content","text":"- **Overview.** Fleet-wide health, version distribution, host inventory, and licenses and certificates nearing expiry — one screen for \"is anything wrong anywhere.\"\n- **Clusters and databases.** Every cluster and database Radar knows about, with deployment type, version, status, and when Radar last reached it. Drill into one for detail.\n- **Usage.** Memory, ops/sec, and shard consumption for Redis Software clusters, against your licensed limits.\n- **Alerts.** The alerts your clusters are already raising, aggregated fleet-wide by severity."},{"id":"how-radar-collects-data","title":"How Radar collects data","role":"content","text":"Radar reads each cluster's management interface using credentials you supply, then normalizes and stores it. A **connector** handles each source type, which is how one fleet view spans products with very different APIs.\n\nThis approach has two significant consequences:\n\n- **Radar needs credentials for every cluster you want to see.** Getting that access in place is most of the setup work. See [Connect clusters](https://redis.io/docs/latest/operate/radar/connect).\n- **Radar must be able to reach each cluster.** Where it can't, such as a cluster behind a firewall or on an isolated network, an agent you install inside that network collects and submits on Radar's behalf. See [Connect clusters with an agent](https://redis.io/docs/latest/operate/radar/agent).\n- **Radar shows the last successful state collection rather than live state.** Radar collects the state of every connected cluster — Redis Software, Redis Open Source, Redis Cloud, Amazon ElastiCache, and Google Memorystore — on a fixed interval, so a value is only as fresh as the last time Radar reached that cluster. Radar shows that age alongside the data."},{"id":"next-steps","title":"Next steps","role":"content","text":"Sign in on [Redis Cloud](https://redis.io/docs/latest/operate/rc/radar), or [install self-managed Radar](https://redis.io/docs/latest/operate/radar/install) yourself."}],"examples":[]}
{"schema_version":2,"id":"operate/radar/install","title":"Install self-managed Radar","url":"https://redis.io/docs/latest/operate/radar/install/","summary":"Install Radar on RHEL or Kubernetes, then make it reachable from your network.","tags":["docs","operate","radar"],"last_updated":"2026-10-07T13:34:00-05:00","page_type":"content","content_hash":"d6e49efa9d88df76ce68636e715516771ec549ea1953a0bcb2d51d77e977544f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This page covers self-managed Radar. If you're using Redis Cloud's hosted Radar, see [Redis Radar on Redis Cloud](https://redis.io/docs/latest/operate/rc/radar) instead.\n\nRadar runs as two services backed by a PostgreSQL database that you provide:\n\n- API server: serves the REST API and the web UI.\n- Worker: connects to each cluster on a schedule, collects its state, and stores it in the database.\n\nBoth services read the same database and the same encryption key. You supply both, no matter which install method you choose.\n\nPlan for remote access before you start. Set up:\n\n- TLS certificates\n- A proxy or load balancer\n- Firewall rules\n- DNS\n- Network access to your PostgreSQL database\n\nWhat you have to do differs by method, so each install method below ends with its own remote-access step."},{"id":"choose-an-install-method","title":"Choose an install method","role":"content","text":"| Method | Use it when | Requires | Containers |\n|---|---|---|---|\n| [RPM on RHEL](#install-on-rhel-with-the-rpm) | You run RHEL 9 and cannot or do not want to run containers. | RHEL 9 on x86_64 | No |\n| [Kubernetes with Helm](#install-on-kubernetes-with-helm) | You already run Kubernetes or OpenShift. | Kubernetes 1.23 or later, and Helm 3.x. Validated on OpenShift 4.x | Yes |\n\nBoth are supported and built from the same release. You can install either one on a host with no internet access. See [Install on an air-gapped host](#install-on-an-air-gapped-host).\n\nGet the RPM from the [Redis Download Center](https://redis.io/downloads/#Modules_Tools_and_Integration). Get the container images from Docker Hub, and the Helm chart from the Redis Helm repository at `https://helm.redis.io/radar`."},{"id":"before-you-start","title":"Before you start","role":"content","text":"Before you install:\n\n- Set up an external, managed PostgreSQL database for production (evaluation can use a bundled container instead). See [PostgreSQL](#postgresql).\n- Generate a 32-byte credential encryption key. See [The credential encryption key](#the-credential-encryption-key).\n- Decide if you need FIPS, since it's a separate build, not a setting you can change later. See [FIPS mode](#fips-mode)."},{"id":"postgresql","title":"PostgreSQL","role":"content","text":"Radar requires PostgreSQL 16 or later. Redis tests Radar against PostgreSQL 16 and 18. For production, set up your own external, managed [PostgreSQL](https://www.postgresql.org/docs/) database before you install Radar. You need to provision, back up, and tune it yourself, since Radar only connects to it and creates the roles and schema it needs on startup.\n\nFor evaluation or testing, you can skip that step: the Helm chart can start a PostgreSQL container for you, though it isn't hardened for production use.\n\nThe connection string needs privileges for both normal runtime work and schema migration, including `CREATEROLE`. The login doesn't need `SUPERUSER` or `BYPASSRLS`. On startup, the API server creates the roles it needs before it begins serving traffic.\n\nAn external database needs one more setting that `CREATEROLE` doesn't cover. A database owner or administrator has to apply the role membership default to the database named by `DATABASE_URL`:\n\n[code example]\n\nReconnect the migration login after you change this setting, so its new session inherits the database default. Radar doesn't apply this setting for you on an external database, and schema migration fails without it.\n\nUse `sslmode=require` or stricter to encrypt the connection. Radar passes your connection string through unchanged."},{"id":"the-credential-encryption-key","title":"The credential encryption key","role":"content","text":"Radar encrypts the cluster credentials you supply. Each tenant gets its own data key, and all of those keys are wrapped by one key-encryption key (KEK) that you supply. The key must be **32 raw bytes**, not base64 or hex.\n\n> [!WARNING]\n> Back up the KEK alongside the database and store the two backups separately. Neither is usable without the other. Radar cannot decrypt stored credentials if the API server and the worker read different keys, or if a restored database is paired with the wrong key. It fails closed rather than losing them silently."},{"id":"fips-mode","title":"FIPS mode","role":"content","text":"[FIPS 140-3](https://csrc.nist.gov/pubs/fips/140-3/final) validated cryptography comes as a separate build of Radar, not a setting you turn on later, so decide before you install. Contact your Redis account team for the FIPS variant.\n\nSet the `MCM_REQUIRE_FIPS=true` environment variable to make FIPS mandatory. Radar then refuses to start unless FIPS is active, and it checks before it touches the database or opens a port. A misconfigured deployment fails immediately rather than running with cryptography you did not approve.\n\nEach service logs its FIPS state once at startup, so you can confirm what is running. In production, Radar writes this log line as structured JSON rather than flat text, and includes additional fields such as the Go runtime's FIPS build mode. The core fields look like this:\n\n[code example]\n\nIn that line, both `enabled` and `required` should read `true`. Search your logs by field name rather than this literal line, since the exact format depends on your deployment method."},{"id":"package-and-service-names","title":"Package and service names","role":"content","text":"Radar's services and paths use an `mcm` prefix. The RPM is named `radar`, its services are `mcm-api` and `mcm-worker`, and its configuration lives in `/etc/mcm/`. The Helm chart pulls one Docker Hub repository, `redislabs/radar`, and selects each component by tag: `app-v<version>`, `worker-v<version>`, and `migrate-v<version>`."},{"id":"install-on-rhel-with-the-rpm","title":"Install on RHEL with the RPM","role":"setup","text":"The RPM installs native binaries and needs no container runtime. It also installs the `radar` diagnostics command.\n\n> [!NOTE]\n> The RPM listens only on loopback by default. A successful RPM install is not yet reachable from any other machine until you put a proxy in front of it.\n\n1. Install the package.\n\n   [code example]\n\n   The package requires `postgresql-server` and `postgresql-contrib` version 16 or later, from the `postgresql:16` module stream. Enable that stream before you install, since `dnf` resolves the dependency only from a stream you have already enabled. Installing the package never creates, starts, or tunes a database.\n\n   <br>\n\n2. Confirm the services are inactive.\n\n   The package deliberately installs them without starting or enabling them.\n\n   [code example]\n\n   <br>\n\n3. Configure the package. Edit `/etc/mcm/mcm.env` and set the two required values.\n\n   [code example]\n\n   | Setting | Description |\n   |---|---|\n   | `DATABASE_URL` | Connection string for your PostgreSQL database, with runtime and migration privileges. |\n   | `CREDENTIAL_ENCRYPTION_KEY` | The credential encryption key. |\n\n   Radar refuses to start while the placeholder values are still in place. The file is owned by `root:mcm`, redacted from logs and diagnostics, and kept across upgrades and removal. Include it in your backup plan.\n\n   You may want to change these defaults.\n\n   | Setting | Default |\n   |---|---|\n   | `HTTP_ADDR` | `127.0.0.1:8080` |\n   | `CREDENTIAL_KEK_PATH` | `/var/lib/mcm/kek` |\n   | `SESSION_COOKIE_SECURE` | `true` |\n\n   Restart `mcm-api.service` after changing API, UI, CORS, LDAP, session, or HTTP settings. Restart `mcm-worker.service` after changing worker, database, encryption, or collection settings. Restart both after changing `DATABASE_URL` or `CREDENTIAL_ENCRYPTION_KEY`.\n\n   <br>\n\n4. Check the configuration.\n\n   [code example]\n\n   Before the first start, `radar doctor` reports that schema migration has not run yet. That is expected; the configuration and database connectivity checks should still pass.\n\n   <br>\n\n5. Start the services.\n\n   [code example]\n\n   The API server applies the database migrations as it starts.\n\n   <br>\n\n6. Verify the services are running.\n\n   [code example]\n\n   <br>\n\n7. Enable the services only after the health checks pass, so a reboot cannot start a half-configured deployment.\n\n   [code example]\n\n   <br>\n\n8. Create the first administrator.\n\n   Radar does not ship a default account or a default password. Open the UI once the API server is healthy and complete the one-time first-administrator flow. It is available only while the database has no users; after that, it closes and normal sign-in applies.\n\n   <br>\n\n9. Provide remote access.\n\n   At this point Radar is reachable only from the host itself.\n\n   Run a reverse proxy that terminates TLS and forwards to the loopback address. Keep `HTTP_ADDR=127.0.0.1:8080` when the proxy runs on the same host. That is the safest arrangement, because nothing but the proxy can reach the API.\n\n   If the proxy runs on a different host, set `HTTP_ADDR` to the private interface it should reach, then restrict access with your own firewall rules. Restart the API server.\n\n   [code example]\n\n   `radar doctor` checks runtime health through the configured address. If it reports a runtime-health failure after you change the listen address, confirm the service bound to the interface you expected and that the proxy forwards to the same address.\n\n   > [!WARNING]\n   > Do not expose Radar directly on a public interface. Terminate TLS and apply access controls at the edge.\n\n   <br>"},{"id":"install-on-kubernetes-with-helm","title":"Install on Kubernetes with Helm","role":"setup","text":"A production install has four parts you supply: the PostgreSQL connection, the credential encryption key, image pull access, and an external access path.\n\nAdd the Redis Helm repository first. The commands below install from it.\n\n[code example]\n\nThe chart version matches the Radar version. Replace `<version>` in the commands with the release you install, such as `2026.9.5`, and use that release's container images. Each release's [release notes](https://redis.io/docs/latest/operate/radar/release-notes) list its chart version and images under **Downloads**.\n\nTo install from a chart package file instead, such as on a cluster with no internet access, substitute the `radar-<version>.tgz` file for `radar/radar` in the `helm install` commands. See [Install on an air-gapped host](#install-on-an-air-gapped-host).\n\n1. Create the namespace.\n\n   [code example]\n\n   <br>\n\n2. Create the database secret. Store the database connection string in a secret.\n\n   [code example]\n\n   The chart also needs the database hostname as `database.host`, separate from the connection string. It uses the hostname to wait for the database before it starts the API server, the worker, and the migration job. If your database listens on a port other than 5432, also set `database.port`.\n\n   <br>\n\n3. Create the credentials secret. Generate the credential encryption key as a file and load it with `--from-file`.\n\n   [code example]\n\n   > [!NOTE]\n   > Write the key to a file rather than using `--from-literal=\"$(head -c 32 /dev/urandom)\"`. If the random key contains a zero byte, command substitution truncates it there, so the key would be shorter than 32 bytes.\n\n   The secret must contain a key named `CREDENTIAL_KEK`. Without it, the pods stay in `ContainerCreating` rather than starting with no encryption key.\n\n   <br>\n\n4. Install the chart.\n\n   [code example]\n\n   With an external database, as configured here, the chart runs schema migration as a Kubernetes job before the API server and worker start. If you use the chart's bundled PostgreSQL container instead, migration instead runs after the API and worker pods start, so expect them to restart briefly until the migration job completes. Migrations apply forward only; there is no automated rollback.\n\n   **For a private or air-gapped registry**, mirror the images listed in the release notes for your version, keeping each image's repository path, then point the chart at your registry. Save these values to a file, such as `registry-values.yaml`, and add `-f registry-values.yaml` to the `helm install` command.\n\n   [code example]\n\n   `global.imageRegistry` applies to every image the chart pulls, including `busybox` and the bundled PostgreSQL container. `global.imagePullSecrets` applies to every pod, including the `helm test` pod. To use a different registry for some images, set `image.registry` for the API server, worker, and migration images together, `dbWaitInitContainer.image.registry` for `busybox`, or `postgresql.image.registry` for PostgreSQL.\n\n   In chart 2026.9.5 and earlier, `global.imageRegistry` does not apply to the Radar images, and the `busybox` image has no registry setting. Use these values instead, and if you use the bundled PostgreSQL container, also set `postgresql.image.registry`:\n\n   [code example]\n\n   In those versions, the `helm test` pod also doesn't receive `global.imagePullSecrets`. If your registry requires authentication, attach the pull secret to the namespace's `default` service account, which the test pod runs as, so `helm test` can pull `busybox`. On OpenShift:\n\n   [code example]\n\n   On Kubernetes:\n\n   [code example]\n\n   **For OpenShift**, use the OpenShift values file instead, which lets OpenShift assign namespace-scoped user IDs and switches the external access path from an ingress to a route. The file ships inside the chart, so extract it first.\n\n   [code example]\n\n   The chart does not need an `anyuid` policy, privileged security context, host paths, or `cluster-admin` permissions.\n\n   <br>\n\n5. Verify the install.\n\n   [code example]\n\n   Expect a running API pod and a running worker pod. With an external database, Helm deletes the migration job once it succeeds, so the job is listed only while it runs or if it fails. With the bundled PostgreSQL container, the completed job stays listed for seven days by default. To check health without an external access path, use the following commands.\n\n   [code example]\n\n   <br>\n\n6. Provide remote access. \n   \n   The API server and UI are served on port 80 of an in-cluster service. Expose it with an ingress, an OpenShift route, or a `LoadBalancer` service, and terminate TLS there.\n\n   [code example]\n\n   Radar marks the browser session cookie as secure by default, so serve Radar over HTTPS. Over plain HTTP the browser rejects the cookie and sign-in fails.\n\n   <br>"},{"id":"install-on-an-air-gapped-host","title":"Install on an air-gapped host","role":"setup","text":"Air-gapped installation uses the same two methods.\n\nTransfer the release artifacts to the target host or to an offline repository it can reach. If they include a `radar-v<version>.SHA256SUMS` file, verify them:\n\n[code example]\n\n| Method | What to transfer | How it installs |\n|---|---|---|\n| RPM | The `.rpm` and the dependency closure, including `postgresql-server` if the host has no offline PostgreSQL | `dnf install` from the local file |\n| Helm | The chart package, `radar-<version>.tgz`, and the container images listed in the release notes for your version | Copy the images into a registry the cluster can pull from, then install the chart from the package. See [Helm on an air-gapped cluster](#helm-on-an-air-gapped-cluster). |\n\nYour PostgreSQL database and the clusters you plan to monitor still need to be reachable from the Radar host over the network."},{"id":"helm-on-an-air-gapped-cluster","title":"Helm on an air-gapped cluster","role":"content","text":"1. On a machine with internet access, download the chart package.\n\n   [code example]\n\n   The command saves `radar-<version>.tgz`, which includes `values-openshift.yaml`.\n\n   <br>\n\n2. Copy each image listed under **Downloads** in the [release notes](https://redis.io/docs/latest/operate/radar/release-notes) for your version into your registry, keeping its repository path. For example, with `skopeo`:\n\n   [code example]\n\n   The `--all` option copies every platform of a multi-platform image, such as `busybox`, rather than only the platform of the machine that runs the copy.\n\n   <br>\n\n3. Install from the package. Follow [Install on Kubernetes with Helm](#install-on-kubernetes-with-helm) without adding the Helm repository, substitute `./radar-<version>.tgz` for `radar/radar`, and add the registry values file. For OpenShift, extract the values file from the package instead of running `helm pull`:\n\n   [code example]\n\n   <br>"},{"id":"next-steps","title":"Next steps","role":"content","text":"Radar is installed but has nothing to show yet. Continue to [Connect clusters](https://redis.io/docs/latest/operate/radar/connect) to add your first cluster."}],"examples":[{"id":"postgresql-ex0","language":"sql","code":"ALTER DATABASE <database_name> SET createrole_self_grant = 'set, inherit';","section_id":"postgresql"},{"id":"fips-mode-ex0","language":"text","code":"fips state service=mcm-api category=startup enabled=true required=true","section_id":"fips-mode"},{"id":"install-on-rhel-with-the-rpm-ex0","language":"bash","code":"sudo dnf install -y ./radar-<version>-<release>.x86_64.rpm","section_id":"install-on-rhel-with-the-rpm"},{"id":"install-on-rhel-with-the-rpm-ex1","language":"bash","code":"systemctl is-active mcm-api.service || true\n   systemctl is-active mcm-worker.service || true","section_id":"install-on-rhel-with-the-rpm"},{"id":"install-on-rhel-with-the-rpm-ex2","language":"bash","code":"sudoedit /etc/mcm/mcm.env","section_id":"install-on-rhel-with-the-rpm"},{"id":"install-on-rhel-with-the-rpm-ex3","language":"bash","code":"sudo radar doctor","section_id":"install-on-rhel-with-the-rpm"},{"id":"install-on-rhel-with-the-rpm-ex4","language":"bash","code":"sudo systemctl start mcm-api.service\n   sudo systemctl start mcm-worker.service","section_id":"install-on-rhel-with-the-rpm"},{"id":"install-on-rhel-with-the-rpm-ex5","language":"bash","code":"curl -fsS http://127.0.0.1:8080/healthz/ready\n   sudo radar doctor","section_id":"install-on-rhel-with-the-rpm"},{"id":"install-on-rhel-with-the-rpm-ex6","language":"bash","code":"sudo systemctl enable mcm-api.service\n   sudo systemctl enable mcm-worker.service","section_id":"install-on-rhel-with-the-rpm"},{"id":"install-on-rhel-with-the-rpm-ex7","language":"bash","code":"sudo systemctl restart mcm-api.service\n   sudo radar doctor","section_id":"install-on-rhel-with-the-rpm"},{"id":"install-on-kubernetes-with-helm-ex0","language":"bash","code":"helm repo add radar https://helm.redis.io/radar\nhelm repo update radar","section_id":"install-on-kubernetes-with-helm"},{"id":"install-on-kubernetes-with-helm-ex1","language":"bash","code":"kubectl create namespace radar","section_id":"install-on-kubernetes-with-helm"},{"id":"install-on-kubernetes-with-helm-ex2","language":"bash","code":"kubectl create secret generic radar-db \\\n     --namespace radar \\\n     --from-literal=DATABASE_URL='postgres://radar:secret@postgres.example.com:5432/radar?sslmode=require'","section_id":"install-on-kubernetes-with-helm"},{"id":"install-on-kubernetes-with-helm-ex3","language":"bash","code":"head -c 32 /dev/urandom > kek.bin\n\n   kubectl create secret generic radar-credentials \\\n     --namespace radar \\\n     --from-file=CREDENTIAL_KEK=./kek.bin\n\n   shred -u kek.bin","section_id":"install-on-kubernetes-with-helm"},{"id":"install-on-kubernetes-with-helm-ex4","language":"bash","code":"helm install radar radar/radar \\\n     --version <version> \\\n     --namespace radar \\\n     --set database.host=postgres.example.com \\\n     --set database.existingSecret=radar-db \\\n     --set credentials.existingSecret=radar-credentials \\\n     --set ingress.enabled=true \\\n     --set ingress.className=nginx \\\n     --set 'ingress.hosts[0].host=radar.example.com' \\\n     --set 'ingress.hosts[0].paths[0].path=/' \\\n     --set 'ingress.hosts[0].paths[0].pathType=Prefix'","section_id":"install-on-kubernetes-with-helm"},{"id":"install-on-kubernetes-with-helm-ex5","language":"yaml","code":"global:\n     imageRegistry: registry.example.com\n     imagePullSecrets:\n       - name: registry-creds","section_id":"install-on-kubernetes-with-helm"},{"id":"install-on-kubernetes-with-helm-ex6","language":"yaml","code":"image:\n     registry: registry.example.com\n   dbWaitInitContainer:\n     image:\n       repository: registry.example.com/library/busybox\n   global:\n     imagePullSecrets:\n       - name: registry-creds","section_id":"install-on-kubernetes-with-helm"},{"id":"install-on-kubernetes-with-helm-ex7","language":"bash","code":"oc secrets link default registry-creds --for=pull -n radar","section_id":"install-on-kubernetes-with-helm"},{"id":"install-on-kubernetes-with-helm-ex8","language":"bash","code":"kubectl patch serviceaccount default -n radar \\\n     -p '{\"imagePullSecrets\": [{\"name\": \"registry-creds\"}]}'","section_id":"install-on-kubernetes-with-helm"},{"id":"install-on-kubernetes-with-helm-ex9","language":"bash","code":"helm pull radar/radar --version <version> --untar --untardir .\n\n   helm install radar radar/radar \\\n     --version <version> \\\n     --namespace radar \\\n     -f ./radar/values-openshift.yaml \\\n     --set database.host=postgres.example.com \\\n     --set database.existingSecret=radar-db \\\n     --set credentials.existingSecret=radar-credentials \\\n     --set route.host=radar.apps.example.com","section_id":"install-on-kubernetes-with-helm"},{"id":"install-on-kubernetes-with-helm-ex10","language":"bash","code":"kubectl get pods -n radar\n   kubectl get jobs -n radar -l app.kubernetes.io/component=migrate\n   helm test radar --namespace radar","section_id":"install-on-kubernetes-with-helm"},{"id":"install-on-kubernetes-with-helm-ex11","language":"bash","code":"kubectl port-forward -n radar svc/radar 8080:80\n   curl http://localhost:8080/healthz/ready","section_id":"install-on-kubernetes-with-helm"},{"id":"install-on-kubernetes-with-helm-ex12","language":"yaml","code":"ingress:\n     enabled: true\n     className: nginx\n     annotations:\n       cert-manager.io/cluster-issuer: letsencrypt-prod\n     hosts:\n       - host: radar.example.com\n         paths:\n           - path: /\n             pathType: Prefix\n     tls:\n       - secretName: radar-tls\n         hosts:\n           - radar.example.com","section_id":"install-on-kubernetes-with-helm"},{"id":"install-on-an-air-gapped-host-ex0","language":"bash","code":"sha256sum -c radar-v<version>.SHA256SUMS","section_id":"install-on-an-air-gapped-host"},{"id":"helm-on-an-air-gapped-cluster-ex0","language":"bash","code":"helm repo add radar https://helm.redis.io/radar\n   helm pull radar/radar --version <version>","section_id":"helm-on-an-air-gapped-cluster"},{"id":"helm-on-an-air-gapped-cluster-ex1","language":"bash","code":"skopeo copy --all \\\n     docker://docker.io/redislabs/radar:app-v<version> \\\n     docker://registry.example.com/redislabs/radar:app-v<version>","section_id":"helm-on-an-air-gapped-cluster"},{"id":"helm-on-an-air-gapped-cluster-ex2","language":"bash","code":"tar -xzf radar-<version>.tgz radar/values-openshift.yaml","section_id":"helm-on-an-air-gapped-cluster"}]}
{"schema_version":2,"id":"operate/radar/licenses-and-certificates","title":"Licenses and certificates","url":"https://redis.io/docs/latest/operate/radar/licenses-and-certificates/","summary":"Track license and certificate expiry across your fleet.","tags":["docs","operate","radar"],"last_updated":"2026-10-02T14:53:52+01:00","page_type":"content","content_hash":"c8ff95e8c860a8ca6ee34314d07844e002ed7ed529aa7ddcf7bea4b48007365a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Certificates expire per cluster, so the more clusters you run, the more likely one of them is close to expiring without anyone noticing. Licenses usually share a single expiry date across the fleet, so for licenses the number that changes is consumption, not the date. Radar collects both across the whole fleet and lists them by expiry date, so you find out before a customer does.\n\nRadar presents licenses and certificates as two lists that work the same way. Every row carries a status:\n\n| Status | Meaning |\n|---|---|\n| Valid | Not close to expiry. No action required. |\n| Expiring | Expires within 30 days. |\n| Expired | Past its expiration date. Renew immediately. |\n\nYou can filter either list by status to see only what needs attention."},{"id":"licenses","title":"Licenses","role":"content","text":"The **Licenses** view lists every cluster's license with its expiration date, how many days remain, and how much of the license you are consuming.\n\nRadar reports shard usage against the licensed limit, broken out by type:\n\n- Total shards in use, against the licensed shard limit.\n- RAM shards in use, against their limit.\n- Flex shards in use, against their limit.\n\nRadar also shows a fleet-wide total, so you can see aggregate headroom without adding up rows, and you can export the list as CSV. Usage matters as much as the date: a license that does not expire for a year can still stop you from adding a database next week.\n\nA license changed on the cluster shows up in Radar at its next collection.\n\n![The Licenses page, listing clusters by license status, expiration date, and shard usage](https://redis.io/docs/latest/images/radar/licenses.png)"},{"id":"certificates","title":"Certificates","role":"content","text":"The **Certificates** view lists each cluster's certificates by type, with the expiration date and days remaining. One cluster can have several, such as `Api_cert`, `Cm_cert`, `Metrics_exporter_cert`, `Proxy_cert`, and `Syncer_cert`.\n\nA certificate renewed on the cluster shows up in Radar with its new expiration date at the next collection.\n\n![The Certificates page, listing certificates by type, cluster, and expiration date](https://redis.io/docs/latest/images/radar/certificates.png)"},{"id":"next-steps","title":"Next steps","role":"content","text":"- [Manage access](https://redis.io/docs/latest/operate/radar/manage-access)\n- [Connect clusters](https://redis.io/docs/latest/operate/radar/connect)"}],"examples":[]}
{"schema_version":2,"id":"operate/radar/manage-access","title":"Manage access","url":"https://redis.io/docs/latest/operate/radar/manage-access/","summary":"Create Radar users, assign roles, and connect LDAP or SAML SSO.","tags":["docs","operate","radar"],"last_updated":"2026-10-05T16:11:44+01:00","page_type":"content","content_hash":"8b65bad72f665b19e20a8d9d743f0014605567bfa5aad23cb51e5cfba3f2c4f7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This page applies to self-managed Radar installations. Redis Cloud manages Radar users and roles for you, so the local accounts, roles, LDAP, and SAML SSO described here do not apply there.\n\nRadar has two roles, administrator and viewer, one of which is assigned to every user. Users sign in with a local Radar account, through your directory over LDAP, or through your identity provider with SAML SSO.\n\nCreate local accounts under **Users**. Choose and configure the sign-in method under **Settings > Authentication**, where that choice applies to all of Radar.\n\n![The Settings > Authentication page, showing the Application, SAML, and LDAP method selector](https://redis.io/docs/latest/images/radar/settings-authentication.png)"},{"id":"roles","title":"Roles","role":"content","text":"| Role | Can do |\n|---|---|\n| Admin | Everything a viewer can, plus add and remove connections, manage users, and change settings. |\n| Viewer | Read the fleet: clusters, databases, usage, and alerts. |\n\nA role applies to the entire fleet. You cannot scope a user to a subset of clusters.\n\nNew users are viewers unless you choose otherwise. Only admins can see or change users; the **Users** area is hidden from viewers."},{"id":"create-local-users","title":"Create local users","role":"content","text":"1. Go to **Users** and add a user.\n2. Enter a **username** and **email address**.\n3. Set a **password**. It must be 6 to 128 characters and contain at least one uppercase letter.\n4. Choose a **role**.\n\nThe **Users** list shows each account's role, where it authenticates, when it was created, and its last sign-in, so you can tell a local account from a directory account at a glance.\n\n![The Users page, listing a local account with its role, status, and last login](https://redis.io/docs/latest/images/radar/users.png)\n\nIf an account locks, admins unlock it from the same list.\n\nRadar creates no default account and ships no default password. The one-time bootstrap flow creates the first administrator while the database has no users. See [Install Radar](https://redis.io/docs/latest/operate/radar/install#install-on-rhel-with-the-rpm)."},{"id":"connect-a-directory-with-ldap","title":"Connect a directory with LDAP","role":"content","text":"Radar authenticates against your directory and takes each user's role from their group membership, so you grant and revoke Radar access by changing groups.\n\nConfigure LDAP under **Settings > Authentication**, in the **Active Directory / LDAP** section."},{"id":"connect-to-the-directory","title":"Connect to the directory","role":"content","text":"Enter the network address of your directory server and the bind account Radar uses to search it:\n\n| Setting | Description |\n|---|---|\n| Server host | The hostname or IP address of the directory server. |\n| Server port | Port the directory listens on. |\n| Bind DN | Distinguished name of the account Radar uses to search the directory. |\n| Bind password | Password for the bind account. |\n| Base DN | Where in the directory tree Radar looks for users. |"},{"id":"find-the-user","title":"Find the user","role":"content","text":"Radar resolves a username one of two ways. Set the one that matches your directory:\n\n| Setting | Description |\n|---|---|\n| User filter | An LDAP search filter. Radar substitutes `{username}` with the name entered at sign-in. Use this when users are spread across the tree. |\n| User DN pattern | A distinguished name template containing `{username}`, used to bind directly. Use this when every user's DN follows one predictable shape. |\n\nIf both are set, the search filter takes precedence."},{"id":"map-groups-to-roles","title":"Map groups to roles","role":"content","text":"Map your directory's groups to Radar's two roles:\n\n| Setting | Description |\n|---|---|\n| Group base DN | Where in the tree Radar looks for groups. |\n| Group filter | A search filter for a user's groups. Radar substitutes `{userDn}` with the user's distinguished name. |\n| Admin group DN | Members of this group sign in as admins. |\n| Viewer group DN | Members of this group sign in as viewers. |"},{"id":"secure-the-connection","title":"Secure the connection","role":"content","text":"Configure how Radar's connection to the directory is encrypted:\n\n| Setting | Description |\n|---|---|\n| Use SSL | Connect over LDAPS. |\n| Start TLS | Upgrade a plain connection to TLS after connecting. |\n| Validate certificate | Verify the directory's certificate. Leave this on. |\n| CA certificate | The certificate authority chain in PEM format, if your directory uses a private CA. |\n\n> [!WARNING]\n> Turning off certificate validation means Radar sends the bind password over a connection it has not verified. Supply the CA certificate instead."},{"id":"set-up-saml-sso","title":"Set up SAML SSO","role":"content","text":"Radar acts as the service provider and your identity provider asserts who the user is and what role they get.\n\nConfigure SAML under **Settings > Authentication**, in the **Single Sign-On** section."},{"id":"1-give-radar-its-address","title":"1. Give Radar its address","role":"content","text":"Enter the **service address**: the URL where users reach Radar. Radar derives its service provider entity ID and its assertion consumer service (ACS) URL from it, then displays both so you can register them with your identity provider.\n\nGet the address right the first time. It is the value your identity provider validates against, so a mismatch fails every sign-in."},{"id":"2-enter-your-identity-providers-details","title":"2. Enter your identity provider's details","role":"content","text":"| Setting | Description |\n|---|---|\n| IdP entity ID | The identity provider's unique identifier. |\n| IdP SSO URL | Where Radar sends users to sign in. |\n| IdP certificate | The identity provider's signing certificate, in PEM format. Radar verifies assertions against it. |"},{"id":"3-supply-the-service-provider-certificate","title":"3. Supply the service provider certificate","role":"content","text":"Provide Radar's own certificate and private key in PEM format so it can sign requests. Radar encrypts the private key at rest and never returns it through the API. Leaving these fields empty keeps whatever is already stored, so you do not have to re-paste them to change another setting."},{"id":"4-map-assertion-values-to-roles","title":"4. Map assertion values to roles","role":"content","text":"Tell Radar which assertion attribute carries the role, and which values grant each one:\n\n| Setting | Description |\n|---|---|\n| Role attribute | The assertion attribute Radar reads the role from. |\n| Admin role values | Values in that attribute that grant the administrator role. |\n| Viewer role values | Values that grant the viewer role. |"},{"id":"5-restrict-and-enforce","title":"5. Restrict and enforce","role":"content","text":"Narrow who can sign in through SSO and decide whether local passwords still work alongside it:\n\n| Setting | Description |\n|---|---|\n| Allowed email domains | Only accept assertions for users in these domains. |\n| Require link confirmation | Ask a user to confirm before their identity provider account is linked to an existing Radar account. |\n| Enforce SSO | Require SSO and stop accepting local passwords. |\n\n> [!WARNING]\n> Confirm that at least one administrator can sign in through your identity provider before you turn on Enforce SSO. Once local passwords stop being accepted, a broken SAML configuration locks everyone out, including you."},{"id":"next-steps","title":"Next steps","role":"content","text":"- [Licenses and certificates](https://redis.io/docs/latest/operate/radar/licenses-and-certificates)\n- [Connect clusters](https://redis.io/docs/latest/operate/radar/connect)"}],"examples":[]}
{"schema_version":2,"id":"operate/radar/monitor","title":"Monitor clusters and databases","url":"https://redis.io/docs/latest/operate/radar/monitor/","summary":"Read fleet health, capacity, usage, and alerts across every cluster Radar collects from.","tags":["docs","operate","radar"],"last_updated":"2026-10-06T08:57:27-05:00","page_type":"content","content_hash":"ad9ebb46261771714c32d7105f1b16a4300bf7bdbba23997ab45914a1107a2a6","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"Radar has five views:\n\n| View | Description |\n|---|---|\n| [Overview](#overview) | Fleet-wide health at a glance. |\n| [Clusters](#clusters) | Every cluster in your fleet, with type, status, and capacity. |\n| [Databases](#databases) | Every database across your fleet, independent of which cluster it's on. |\n| [Usage](#usage) | Consumption against your licensed limits. |\n| [Alerts](#alerts) | Alerts raised by your clusters, ordered by severity. |\n\nAll the views are read-only. You must access the cluster itself to take action or make changes."},{"id":"how-current-the-data-is","title":"How current the data is","role":"content","text":"Radar shows the result of the last successful collection — one pull of a cluster or database's state — not live state. The Clusters, Databases, and Alerts lists carry a **Last Seen** column detailing the age of the information you are seeing.\n\nDifferent data refreshes at different rates. Radar aims to collect the Usage report about once an hour, and the rest of a cluster's state every 5 to 15 minutes. Collection runs through a queue, so these are targets rather than guarantees, and some cluster versions collect faster.\n\nIf a cluster's Last Seen value stops advancing, Radar has stopped successfully collecting that cluster's state. Do not read it as a healthy cluster. The cause can be anywhere between Radar and the cluster, including the cluster being down. Radar keeps showing the last state it successfully collected, so check the cluster itself.\n\nOn a Redis Cloud database's detail view, **Force refresh** collects that database's state again instead of waiting for the next scheduled collection. Use it after changing something on the cluster, to confirm Radar has caught up."},{"id":"when-a-value-reads-na","title":"When a value reads N/A","role":"content","text":"**N/A** means Radar has no value for that metric, not that the value is zero. Radar also hides the utilization meter, because a `0` would read as an idle fleet.\n\nThis affects memory and ops/sec. Older Redis Software versions do not expose the per-database statistics behind those figures, so a fleet of Redis Software 7.2 or 7.4 clusters can show N/A where a newer fleet shows a number.\n\nShard counts always show a number, because zero shards is a real answer rather than a missing one."},{"id":"overview","title":"Overview","role":"overview","text":"The **Overview** answers one question for a fleet too large to check by hand: is anything wrong anywhere?\n\n- **Fleet health.** Cluster and database health side by side, so a problem that affects one database in one cluster is still visible at the top level.\n- **Redis deployments and version distribution.** What you are running and how many versions you are spread across. Version sprawl is usually invisible until you look at a fleet-wide count.\n- **Hosts.** Host inventory behind the fleet, so you can see what's backing your clusters without opening each one.\n- **Licenses and certificates.** Anything expiring, summarized. See [Licenses and certificates](https://redis.io/docs/latest/operate/radar/licenses-and-certificates).\n- **Alerts.** The most severe alerts currently raised. See [Alerts](#alerts).\n- **Running actions.** Operations in progress on your clusters.\n\n![The Radar Overview page, showing fleet status, inventory, database health, and alerts](https://redis.io/docs/latest/images/radar/overview.png)"},{"id":"clusters","title":"Clusters","role":"content","text":"The **Clusters** list is one row per cluster, whichever deployment type it came from:\n\n| Column | Description |\n|---|---|\n| Name | Your display name for the cluster. |\n| Type | Deployment type: Redis Software, Redis Cloud, or Redis Open Source. |\n| Account ID | The account the cluster belongs to, for cloud connections. |\n| Region | The cloud region it runs in, for cloud connections. |\n| Status | Current health: Healthy, Warning, or Error. |\n| License | Shards used against the license's shard limit, as a count and percentage. Reads N/A if the cluster has no license or limit. |\n| Plan | The subscription plan, for cloud connections. |\n| Memory | Memory in use. |\n| Hosts | Number of hosts. |\n| Databases | Number of databases. |\n| Version | Redis database version. |\n| Last Seen | When Radar last collected this cluster's state. |\n\nSort by **Name** or **Memory**; the other columns are display-only.\n\n![The Radar Clusters page, listing clusters with their type, status, memory, and database counts](https://redis.io/docs/latest/images/radar/clusters.png)\n\nSelect any cluster to open its detail view, which carries that cluster's nodes, databases, and configuration.\n\nFor Redis Software clusters, select a row's **More options** menu and choose **Open cluster UI** to jump straight to that cluster's own management console in a new tab.\n\n![A cluster detail view for a Redis Software cluster, showing its overview, nodes, databases, and alerts](https://redis.io/docs/latest/images/radar/cluster-detail-enterprise.png)"},{"id":"databases","title":"Databases","role":"content","text":"The **Databases** list crosses cluster boundaries, so you can find a database without knowing which cluster it lives on:\n\n| Column | Description |\n|---|---|\n| Name | The name reported by the source: an administrator-set name for Redis Software, Redis Cloud, and Redis Open Source, or the AWS or Google Cloud resource identifier for Amazon ElastiCache and Google Memorystore. |\n| Cluster / Resource | The Redis cluster it belongs to, or the Amazon ElastiCache or Google Memorystore resource it came from. |\n| Type | Deployment type: Redis Software, Redis Cloud, Redis Open Source, Amazon ElastiCache, or Google Memorystore. |\n| Status | Current health: Active, Warning, or Down. |\n| HA Status | High-availability state, including Active-Active replication health. |\n| Version | Redis database version. |\n| Memory | Memory in use. |\n| Last Seen | When Radar last collected this database's state. |\n\n![The Radar Databases page, listing databases across clusters with their type, status, and version](https://redis.io/docs/latest/images/radar/databases.png)\n\nSelect a database to open its detail view.\n\n![A database detail view for a Redis Software database](https://redis.io/docs/latest/images/radar/database-detail-enterprise.png)"},{"id":"active-active-databases","title":"Active-Active databases","role":"content","text":"Radar identifies Active-Active databases and reports their replication health as the cluster reports it, so a replication problem between regions shows up in the same list as everything else. Radar passes through the health values the cluster supplies rather than reinterpreting them."},{"id":"usage","title":"Usage","role":"syntax","text":"The **Usage** view is about consumption against your licensed limits, rather than health. It covers Redis Software clusters only; databases from other Redis deployments do not appear here.\n\nUsage is collected about once an hour, so a number here can be up to an hour behind the health figures on the Overview.\n\nSelect a row to open that cluster's usage report, which breaks memory, ops/sec, and shard usage down by database.\n\n| Column | Description |\n|---|---|\n| Cluster | The Redis Software cluster this usage report covers. |\n| Memory (Used / Provisioned) | Memory consumed against memory provisioned. Can read N/A — see [When a value reads N/A](#when-a-value-reads-na). |\n| Ops/Sec | Throughput. Can read N/A — see [When a value reads N/A](#when-a-value-reads-na). |\n| Shards (Used / Limit) | Shards in use against the licensed shard limit. |"},{"id":"alerts","title":"Alerts","role":"content","text":"The **Alerts** view collects the alerts your clusters are already raising and puts them in one list, ordered by severity: critical, warning, then informational. It covers Redis Software clusters and nodes only; alerts from other Redis deployments do not appear here. \n\nRadar reads alerts from each cluster's own alert endpoints, at the cluster and node level. **The alert rules live on the cluster, not in Radar.** To change what raises an alert, change it on the cluster; Radar reflects the change the next time it collects that cluster's state.\n\nFilter by severity or search by alert, cluster, or FQDN. Select the cluster name in an alert to open that cluster's detail view.\n\n![The Radar Alerts page, listing alerts by severity, category, and affected resource](https://redis.io/docs/latest/images/radar/alerts.png)"},{"id":"download-a-support-package","title":"Download a support package","role":"content","text":"Download a diagnostics package from a Redis Software cluster to attach to a Redis Support ticket. Only administrators can do this. See [Manage access](https://redis.io/docs/latest/operate/radar/manage-access) to view the administrators for your Radar deployment.\n\nDownload it from the cluster's detail view, or from the **Support** page if you'd rather pick the cluster from a list.\n\nThe download runs in your browser, so it might take several minutes to prepare the package for a large cluster. The download will continue while you navigate elsewhere in Radar, but will cancel if the tab is closed or reloaded.\n\nTo send Redis Support information about Radar itself rather than a cluster, see [Download Radar diagnostic logs](#download-radar-diagnostic-logs)."},{"id":"download-radar-diagnostic-logs","title":"Download Radar diagnostic logs","role":"content","text":"On a self-managed install, you can download Radar's own diagnostic data to attach to a Redis Support ticket. Only administrators can do this. This option is not available in Radar on Redis Cloud.\n\nTo download the diagnostic logs:\n\n1. Open **Settings**.\n2. Select **Download logs** in the page header.\n\n**Download logs** is also on the **Data & Monitoring** tab, under **Diagnostic Export**.\n\nRadar downloads a ZIP file named `mcm-diagnostic-logs-<timestamp>.zip`. It holds CSV files covering the last 30 days of connections, collection requests and runs, connection actions, audit events, and errors.\n\nIt does not include stored credentials or a Redis Software support package. It does include usernames, IP addresses, connection names, and warning and error messages from Radar's services. To get diagnostics from a cluster, [download a support package](#download-a-support-package) instead."},{"id":"next-steps","title":"Next steps","role":"content","text":"- [Licenses and certificates](https://redis.io/docs/latest/operate/radar/licenses-and-certificates)\n- [Connect clusters](https://redis.io/docs/latest/operate/radar/connect)\n- [Manage access](https://redis.io/docs/latest/operate/radar/manage-access)"}],"examples":[]}
{"schema_version":2,"id":"operate/radar/release-notes/2026-10-1","title":"Redis Radar release notes 2026.10.1","url":"https://redis.io/docs/latest/operate/radar/release-notes/2026-10-1/","summary":"Alert descriptions, complete usage exports, and simpler private-registry and air-gapped OpenShift installs.","tags":["docs","operate","radar"],"last_updated":"2026-10-06T11:21:43-05:00","page_type":"content","content_hash":"0082f17d611f9f30df0aec5168564970ab8c1a152175055bcd1927247a34df61","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Radar 2026.10.1 adds readable alert descriptions and a complete usage export. It also makes installing from a private registry or on an air-gapped OpenShift cluster simpler, and fixes stale indicators and gaps in Redis Cloud discovery.\n\nThis is a Short-Term Support (STS) release. See [Self-managed releases](https://redis.io/docs/latest/operate/radar/release-notes#self-managed-releases) for more details."},{"id":"enhancements","title":"Enhancements","role":"content","text":"- **Alert descriptions.** Redis Software alerts now include a readable description and, where the cluster reports one, the measured value.\n- **Complete usage export.** The **Usage** CSV export now includes the retained raw usage fields and nested data. The export redacts sensitive data and protects against spreadsheet formulas.\n- **One registry setting for every image.** `global.imageRegistry` now applies to the API server, worker, migration job, and `busybox` images, as it already did to the bundled PostgreSQL container, so one setting covers every image the chart pulls. The `busybox` image also gets its own override, `dbWaitInitContainer.image.registry`, and the `helm test` pod now receives `global.imagePullSecrets`.\n- **Air-gapped values file.** The chart now includes `values-airgap.yaml`, which lists the images to mirror and the values to set for your registry. On OpenShift, pass it after `values-openshift.yaml`.\n- **Route certificates from a Secret.** Set `route.tls.externalCertificate.name` to a `kubernetes.io/tls` Secret in the release namespace, so the private key stays out of your Helm values. The chart grants the OpenShift router read access to that Secret unless you set `route.grantRouterSecretAccess: false`. This requires OpenShift 4.16 or later. It's generally available on 4.19 and later, and a Technology Preview on 4.16 through 4.18.\n- **Managed-agent audit records.** Radar now records audit events for managed-agent activation decisions, source changes, assignment updates, and failed administrative requests.\n- **Polling interval for Redis Software 7.2 and 7.4.** `app.pollerIntervals.compat` sets how often Radar collects from Redis Software 7.2 and 7.4 clusters. The default is `5m`. A shorter interval keeps data fresher but adds load to each cluster's management plane.\n- **Database queries.** Radar skips health reads that aren't needed by Active-Active.\n- **Packaged documentation.** The documentation packaged with the Helm chart and the RPM has improved image-mirroring and external PostgreSQL setup guidance.\n- **Dependencies.** Updated backend, frontend, and build dependencies."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- **Freshness reporting.** Cluster and Overview freshness now follows each source's collection interval. Amazon ElastiCache freshness metrics stay consistent across worker replicas.\n- **Redis Cloud discovery.** Fixed a pagination issue that could make a Redis Cloud Pro subscription with databases appear empty.\n- **Redis Cloud API retries.** Radar retries a Redis Cloud API read once when it fails with a transient error.\n- **Health collection.** A slow Redis Software alert request no longer interrupts health collection.\n- **Redis Cloud connection fields.** The Redis Cloud connection fields are now labeled **API account key** and **API user key**.\n- **Telemetry settings.** The nonfunctional Telemetry settings are removed.\n- **OpenShift validation.** Helm now rejects Route TLS configurations that can't reach Radar. See [Breaking changes](#breaking-changes).\n- **PostgreSQL diagnostics.** When schema migration fails because the database login can't `SET ROLE`, the migration log now includes a hint naming the setting to apply."},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"On OpenShift, the chart now accepts only `edge` termination for the Route. If your values set `route.tls.termination` to `reencrypt` or `passthrough`, or set `route.tls.destinationCACertificate`, update them before you upgrade. Helm rejects those values."},{"id":"downloads","title":"Downloads","role":"content","text":"Radar packages and images are built for `linux/amd64` (x86_64) only."},{"id":"helm-chart","title":"Helm chart","role":"content","text":"- **Chart name**: `radar`\n- **Chart version**: `2026.10.1`\n- **Repository**: `https://helm.redis.io/radar`"},{"id":"container-images","title":"Container images","role":"content","text":"- **API server**: `redislabs/radar:app-v2026.10.1`\n- **Worker**: `redislabs/radar:worker-v2026.10.1`\n- **Migration job**: `redislabs/radar:migrate-v2026.10.1`\n- **Database wait and Helm test**: `library/busybox:1.37`\n- **Bundled PostgreSQL**, only if you enable it: `library/postgres:18.4-bookworm`"},{"id":"rpm","title":"RPM","role":"content","text":"- **Package**: `radar-2026.10.1-1.x86_64.rpm`\n- **Download**: [Redis Download Center](https://redis.io/downloads/#Modules_Tools_and_Integration)"},{"id":"agent","title":"Agent","role":"content","text":"- **Package**: `radar-agent-v2026.10.1-linux-amd64.tar.gz`\n- **Download**: [Redis Download Center](https://redis.io/downloads/#Modules_Tools_and_Integration)"}],"examples":[]}
{"schema_version":2,"id":"operate/radar/release-notes/2026-9-2","title":"Redis Radar release notes 2026.9.2","url":"https://redis.io/docs/latest/operate/radar/release-notes/2026-9-2/","summary":"Initial release.","tags":["docs","operate","radar"],"last_updated":"2026-10-06T11:21:43-05:00","page_type":"content","content_hash":"798895a2c7d833073f56f813eb1b2a7a242b099e07a8055da122bc8a1c75e17f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Radar's first generally available release. Radar gives you one fleet-wide view of every Redis cluster you run.\n\nThis is a Short-Term Support (STS) release. See [Self-managed releases](https://redis.io/docs/latest/operate/radar/release-notes#self-managed-releases) for more details."},{"id":"features","title":"Features","role":"content","text":"- **Fleet visibility.** Connect and monitor **Redis Software**, **Redis Cloud**, **Redis Open Source**, **Amazon ElastiCache**, and **Google Memorystore** clusters from a single view. ElastiCache and Memorystore connections are off by default; an administrator turns them on. See [Connect clusters](https://redis.io/docs/latest/operate/radar/connect).\n- **Views.** Five dedicated views: **Overview**, **Clusters**, **Databases**, **Usage**, and **Alerts**. See [Monitor clusters and databases](https://redis.io/docs/latest/operate/radar/monitor).\n- **Cross-cluster database list.** A cross-cluster **Databases** list, so you can find a database without knowing which cluster it lives on, including Active-Active replication health.\n- **Usage tracking.** Memory, ops/sec, and shard consumption against licensed limits for Redis Software.\n- **Alerts.** Aggregated from every connected Redis Software cluster and node, ordered by severity, with rules that stay owned by the cluster.\n- **License tracking.** Fleet-wide **license** tracking: expiration date, days remaining, and shard usage broken out by total, RAM, and Flex shards, with CSV export.\n- **Certificate tracking.** Fleet-wide **certificate** tracking by type, with expiration dates and a shared Valid/Expiring/Expired status model. See [Licenses and certificates](https://redis.io/docs/latest/operate/radar/licenses-and-certificates).\n- **Local accounts.** Local accounts with two roles, administrator and viewer, plus account lockout and unlock.\n- **LDAP.** Integration with group-to-role mapping.\n- **SAML SSO.** Includes enforced SSO and email-domain restriction options.\n- **FIPS 140-3.** Validated cryptography available as a separate build.\n- **Credential encryption key.** A credential encryption key you control, wrapping every stored connection credential. See [Manage access](https://redis.io/docs/latest/operate/radar/manage-access).\n- **Install methods.** Two install methods — **RPM** (RHEL 9) and **Helm** (Kubernetes and OpenShift) — from the same release artifacts.\n- **Air-gapped installation.** Air-gapped installation supported for both methods. See [Install Radar](https://redis.io/docs/latest/operate/radar/install)."},{"id":"downloads","title":"Downloads","role":"content","text":"Radar packages and images are built for `linux/amd64` (x86_64) only."},{"id":"helm-chart","title":"Helm chart","role":"content","text":"- **Chart name**: `radar`\n- **Chart version**: `2026.9.2`\n- **Repository**: `https://helm.redis.io/radar`"},{"id":"container-images","title":"Container images","role":"content","text":"- **API server**: `redislabs/radar:app-v2026.9.2`\n- **Worker**: `redislabs/radar:worker-v2026.9.2`\n- **Migration job**: `redislabs/radar:migrate-v2026.9.2`\n- **Database wait and Helm test**: `library/busybox:1.37`\n- **Bundled PostgreSQL**, only if you enable it: `library/postgres:18.4-bookworm`"}],"examples":[]}
{"schema_version":2,"id":"operate/radar/release-notes/2026-9-5","title":"Redis Radar release notes 2026.9.5","url":"https://redis.io/docs/latest/operate/radar/release-notes/2026-9-5/","summary":"Connect clusters that Radar can't reach directly by installing an agent.","tags":["docs","operate","radar"],"last_updated":"2026-10-06T11:21:43-05:00","page_type":"content","content_hash":"d95e2d353c3d92cb27aebacb7efe02d3e3bd2838b536baa2789ef52ba1ee24e0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Radar can now collect from clusters it can't reach directly. You install an agent on a host inside the network that holds them, and the agent opens an outbound connection to Radar.\n\nThis is a Short-Term Support (STS) release. See [Self-managed releases](https://redis.io/docs/latest/operate/radar/release-notes#self-managed-releases) for more details."},{"id":"enhancements","title":"Enhancements","role":"content","text":"- **Connect clusters with an agent.** Collect from **Redis Software** and **Redis Open Source** clusters behind a firewall, in a private subnet, or on a segmented network. Radar needs no inbound access to your network. Radar accepts agent connections on its agent gRPC endpoint, which isn't enabled on every Radar deployment. See [Connect clusters with an agent](https://redis.io/docs/latest/operate/radar/agent).\n- **Managed and static agents.** Choose between two modes. **Managed** agents receive source credentials from Radar, so you add and change sources from the Radar UI. **Static** agents read their sources from a file on the agent host, so those credentials never leave it.\n- **Isolated networks.** Collect from a fully isolated network by exporting to a file on one host and submitting it from another.\n- **Agent packages.** Install from standalone agent packages for Linux on amd64, including Federal Information Processing Standards (FIPS) 140-3 builds.\n- **Agent tracking.** Track every agent under **Settings > Agents** and in the **Connected agents** list on the Connections page, with its mode, platform, version, and last heartbeat."},{"id":"downloads","title":"Downloads","role":"content","text":"Radar packages and images are built for `linux/amd64` (x86_64) only."},{"id":"helm-chart","title":"Helm chart","role":"content","text":"- **Chart name**: `radar`\n- **Chart version**: `2026.9.5`\n- **Repository**: `https://helm.redis.io/radar`"},{"id":"container-images","title":"Container images","role":"content","text":"- **API server**: `redislabs/radar:app-v2026.9.5`\n- **Worker**: `redislabs/radar:worker-v2026.9.5`\n- **Migration job**: `redislabs/radar:migrate-v2026.9.5`\n- **Database wait and Helm test**: `library/busybox:1.37`\n- **Bundled PostgreSQL**, only if you enable it: `library/postgres:18.4-bookworm`"}],"examples":[]}
{"schema_version":2,"id":"operate/radar/release-notes/cloud-changelog","title":"Redis Radar on Redis Cloud","url":"https://redis.io/docs/latest/operate/radar/release-notes/cloud-changelog/","summary":"What changed in each Redis Radar release on Redis Cloud.","tags":["docs","operate","radar"],"last_updated":"2026-10-06T11:21:43-05:00","page_type":"content","content_hash":"235bf131e2d953c75c41598238311891cd61ddaf3c57163b65b9aba144e93ad7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This page lists what changed in each Redis Radar release on Redis Cloud. Redis Cloud rolls out these releases automatically, so there's nothing for you to install or upgrade. If you run Radar on your own infrastructure instead, see the [Self-managed releases](https://redis.io/docs/latest/operate/radar/release-notes#self-managed-releases), which you install and upgrade yourself.\n\nTo get started with Redis Cloud's hosted Radar, see [Redis Radar on Redis Cloud](https://redis.io/docs/latest/operate/rc/radar)."},{"id":"2026101","title":"2026.10.1","role":"content","text":"Radar adds readable alert descriptions and a complete usage export, and fixes stale indicators and gaps in Redis Cloud discovery."},{"id":"enhancements","title":"Enhancements","role":"content","text":"- **Alert descriptions.** Redis Software alerts now include a readable description and, where the cluster reports one, the measured value.\n- **Complete usage export.** The **Usage** CSV export now includes the retained raw usage fields and nested data. The export redacts sensitive data and protects against spreadsheet formulas.\n- **Managed-agent audit records.** Radar now records audit events for managed-agent activation decisions, source changes, assignment updates, and failed administrative requests.\n- **Database queries.** Radar skips health reads that aren't needed by Active-Active.\n- **Dependencies.** Updated backend, frontend, and build dependencies."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- **Freshness reporting.** Cluster and Overview freshness now follows each source's collection interval. Amazon ElastiCache freshness metrics stay consistent across worker replicas.\n- **Redis Cloud discovery.** Fixed a pagination issue that could make a Redis Cloud Pro subscription with databases appear empty.\n- **Redis Cloud API retries.** Radar retries a Redis Cloud API read once when it fails with a transient error.\n- **Health collection.** A slow Redis Software alert request no longer interrupts health collection.\n- **Redis Cloud connection fields.** The Redis Cloud connection fields are now labeled **API account key** and **API user key**.\n- **Telemetry settings.** The nonfunctional Telemetry settings are removed.\n- **Navigation.** Existing `/overview` launch URLs now redirect to the dashboard."},{"id":"202692","title":"2026.9.2","role":"content","text":"Redis Radar becomes available in Redis Cloud: sign in with your existing Redis Cloud credentials, with nothing to install, and get one fleet-wide view of every Redis cluster you run."},{"id":"features","title":"Features","role":"content","text":"- **Fleet visibility.** Connect and monitor **Redis Software**, **Redis Cloud**, **Redis Open Source**, **Amazon ElastiCache**, and **Google Memorystore** clusters from a single view. ElastiCache and Memorystore connections are off by default; an administrator turns them on. Amazon ElastiCache connections authenticate through an IAM role, and Google Memorystore connections through impersonation, rather than long-lived credentials. See [Connect clusters](https://redis.io/docs/latest/operate/radar/connect).\n- **Views.** Five dedicated views: **Overview**, **Clusters**, **Databases**, **Usage**, and **Alerts**. See [Monitor clusters and databases](https://redis.io/docs/latest/operate/radar/monitor).\n- **Cross-cluster database list.** A cross-cluster **Databases** list, so you can find a database without knowing which cluster it lives on, including Active-Active replication health.\n- **Usage tracking.** Memory, ops/sec, and shard consumption against licensed limits for Redis Software.\n- **Alerts.** Aggregated from every connected Redis Software cluster and node, ordered by severity, with rules that stay owned by the cluster.\n- **License tracking.** Fleet-wide **license** tracking: expiration date, days remaining, and shard usage broken out by total, RAM, and Flex shards, with CSV export.\n- **Certificate tracking.** Fleet-wide **certificate** tracking by type, with expiration dates and a shared Valid/Expiring/Expired status model. See [Licenses and certificates](https://redis.io/docs/latest/operate/radar/licenses-and-certificates)."}],"examples":[]}
{"schema_version":2,"id":"operate/radar/release-notes","title":"Release notes","url":"https://redis.io/docs/latest/operate/radar/release-notes/","summary":"Get Radar release artifacts and see what changed in each release.","tags":["docs","operate","radar"],"last_updated":"2026-10-07T13:34:00-05:00","children":[{"id":"operate/radar/release-notes/2026-10-1","summary":"Alert descriptions, complete usage exports, and simpler private-registry and air-gapped OpenShift installs.","title":"Redis Radar release notes 2026.10.1","url":"https://redis.io/docs/latest/operate/radar/release-notes/2026-10-1/"},{"id":"operate/radar/release-notes/2026-9-5","summary":"Connect clusters that Radar can't reach directly by installing an agent.","title":"Redis Radar release notes 2026.9.5","url":"https://redis.io/docs/latest/operate/radar/release-notes/2026-9-5/"},{"id":"operate/radar/release-notes/2026-9-2","summary":"Initial release.","title":"Redis Radar release notes 2026.9.2","url":"https://redis.io/docs/latest/operate/radar/release-notes/2026-9-2/"},{"id":"operate/radar/release-notes/cloud-changelog","summary":"What changed in each Redis Radar release on Redis Cloud.","title":"Redis Radar on Redis Cloud","url":"https://redis.io/docs/latest/operate/radar/release-notes/cloud-changelog/"}],"page_type":"content","content_hash":"9e03740e165781471f19585e0d84d9be19933bf0d51c17ca63e89db6c47bc78f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This page lists Redis Radar release artifacts and what changed in each release.\n\nRadar versions releases using calendar versioning (CalVer), so a release's version number reflects when it shipped rather than a sequential count."},{"id":"cloud-releases","title":"Cloud releases","role":"content","text":"Redis Cloud's hosted Radar needs no download and no installation. Sign in with your existing Redis Cloud credentials.\n\nCloud releases roll out automatically on a regular basis, so there's nothing to schedule or install. See the [changelog](https://redis.io/docs/latest/operate/radar/release-notes/cloud-changelog) for what changed in each release."},{"id":"self-managed-releases","title":"Self-managed releases","role":"content","text":"Self-managed Radar ships as a Helm chart and an RPM, as described in [Install Radar](https://redis.io/docs/latest/operate/radar/install). Get the RPM from the [Redis Download Center](https://redis.io/downloads/#Modules_Tools_and_Integration), the container images from Docker Hub, and the Helm chart from the Redis Helm repository at `https://helm.redis.io/radar`. Each release's notes list the exact chart version, container images, and packages under **Downloads**.\n\nRadar ships on two support tracks:\n\n- **Long-Term Support (LTS)** releases prioritize stability and receive support for an extended period. Choose LTS if you want predictable upgrade cycles.\n- **Short-Term Support (STS)** releases give you earlier access to new features and improvements, but have a shorter support lifecycle and need more frequent upgrades.\n\nSTS releases receive security and bug fixes for 6 months after release. LTS releases receive support for 2 years.\n\nInstall a new release when you're ready to upgrade. See [Install Radar](https://redis.io/docs/latest/operate/radar/install).\n\n| Release                                                            | Support track | What changed                         |\n|--------------------------------------------------------------------|---------------|--------------------------------------|\n| [2026.10.1](https://redis.io/docs/latest/operate/radar/release-notes/2026-10-1) | STS           | Alert descriptions, complete usage exports, and air-gapped OpenShift installs. |\n| [2026.9.5](https://redis.io/docs/latest/operate/radar/release-notes/2026-9-5) | STS           | Connect clusters with an agent.      |\n| [2026.9.2](https://redis.io/docs/latest/operate/radar/release-notes/2026-9-2) | STS           | Initial release.                     |"}],"examples":[]}
{"schema_version":2,"id":"operate/rc/accounts/account-settings","title":"Manage account settings","url":"https://redis.io/docs/latest/operate/rc/accounts/account-settings/","summary":"Describes the settings for a Redis Cloud account.","tags":["docs","operate","rc"],"last_updated":"2026-10-01T15:21:38+01:00","page_type":"content","content_hash":"ed5f6571521d42df27ae76d4cdcba77dee4b9eb294656d9b24dbbd3386a2b927","sections":[{"id":"overview","title":"Overview","role":"overview","text":"To review or manage the settings associated with your Redis Cloud account, sign in to the [Redis Cloud console](https://cloud.redis.io/) and then select **Account Settings** from the menu.\n\nThis displays the **Account Settings** screen:\n\n![Use the Account tab of the Account Settings screen to review and update settings associated with your Redis Cloud account.](https://redis.io/docs/latest/images/rc/account-settings-account-tab.png)\n\nThe available tabs depend on your account, and may include:\n\n- The **Account** tab displays basic information associated with your account, including general info, address details, time zone setting, security settings, and provider integration details.\n\n- The **Cloud Account** tab is displayed for accounts with Redis Cloud Bring your own Cloud enabled. To learn more, see [Bring your own Cloud](https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud).\n\n- The **Integrations** tab lets you manage certain integrations.\n\nYou can change some settings by selecting **Edit**. For help changing other settings, [contact Support](https://redis.io/support/).\n\n![The Edit button changes account settings.](https://redis.io/docs/latest/images/rc/button-database-edit.png)"},{"id":"redis-account-info-settings","title":"Redis account info settings","role":"content","text":"The **Redis Account Info** section provides basic details about your account, including:\n\n| Setting          | Description |\n|------------------|-------------|\n| _Redis account name_   | Name associated with the Redis Cloud account | \n| _Redis account number_ | Internal ID of the Redis account |\n| _Date created_   | Date the user's Redis Cloud account was created, which may differ from the organization account creation date |\n| _Last updated_   | Date of the last administrative change to the owner's account, typically reflects access changes or other administrative updates | \n\nSelect **Edit** to change the Redis Account name.\n\n![The Edit button changes account settings.](https://redis.io/docs/latest/images/rc/button-database-edit.png)\n\nAfter changing the account name, use the **Save changes** button to save changes or **Discard changes** to revert them.\n\n![Use the Discard Changes and the Save Changes buttons to manage changes to account settings.](https://redis.io/docs/latest/images/rc/account-settings-buttons-save-discard.png)\n\nYou cannot change the email address associated with a Redis Cloud account.  Instead, create a new account with the updated email address, assign it as an administrator to the organization account, and then use the new account to delete the account with the invalid email address."},{"id":"contacts--business-information","title":"Contacts & Business information","role":"content","text":"The **Contacts & Business information** section shows the company name and business address associated with the current Redis Cloud account. Redis uses the company name and business address for invoice and tax purposes.\n\nIn addition, this section may include fields unique to your location.  For example, certain regions require tax IDs or other regulatory details.\n\nSelect **Edit** to change the account's company name and business address.\n\n   ![The Edit button changes account settings.](https://redis.io/docs/latest/images/rc/button-database-edit.png)\n\n   ![The Contacts & Business information section, with details changed.](https://redis.io/docs/latest/images/rc/account-settings-change-business-info.png)\n\nWhen you change your business address, you must approve the use of the information in this section for communication and billing purposes before you can save. After approving, select **Save changes** to save your changes or **Discard changes** to revert them. \n\n![Use the Discard Changes and the Save Changes buttons to manage changes to account settings.](https://redis.io/docs/latest/images/rc/account-settings-buttons-save-discard.png)"},{"id":"security-settings","title":"Security settings","role":"security","text":"The **Security** section lets you:\n\n- Manage [multi-factor authentication](https://redis.io/docs/latest/operate/rc/security/access-control/multi-factor-authentication) (MFA) for your Redis Cloud account.\n\n- Download the [Redis Cloud certificate authority (CA) bundle](https://redis.io/docs/latest/operate/rc/security/database-security/tls-ssl#download-certificates) as a [PEM](https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail) file, which contains the certificates associated with your Redis Cloud account."},{"id":"time-zone-settings","title":"Time zone settings","role":"content","text":"To change the time zone settings, select **Edit** and then select the desired time zone from the **Time zone** drop-down.\n\nSelect **Save changes** to save your changes or **Discard changes** to revert them. \n\n![Use the Discard Changes and the Save Changes buttons to manage changes to account settings.](https://redis.io/docs/latest/images/rc/account-settings-buttons-save-discard.png)"},{"id":"integration-settings","title":"Integration settings","role":"content","text":"The **Integrations** tab includes settings that help you manage integrations of your Redis Cloud account with other services.\n\nFor more details, see:\n- [Use the Redis Sink Confluent Connector](https://redis.io/docs/latest/integrate/confluent-with-redis-cloud)\n- [Prometheus and Grafana with Redis Cloud](https://redis.io/docs/latest/integrate/prometheus-with-redis-cloud)"}],"examples":[]}
{"schema_version":2,"id":"operate/rc/accounts","title":"Manage accounts and settings","url":"https://redis.io/docs/latest/operate/rc/accounts/","summary":"Describes how to work with Redis Cloud accounts and manage their settings.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T14:59:36+01:00","children":[{"id":"operate/rc/accounts/account-settings","summary":"Describes the settings for a Redis Cloud account.","title":"Manage account settings","url":"https://redis.io/docs/latest/operate/rc/accounts/account-settings/"},{"id":"operate/rc/accounts/user-profile","summary":"Describes the how to manage your user account profile and how to switch between Redis Cloud accounts.","title":"Manage user account and profile","url":"https://redis.io/docs/latest/operate/rc/accounts/user-profile/"}],"page_type":"content","content_hash":"2d002783395ffab4fe826de9771d418961d5e1c5b809c46846027a61fcce909a","sections":[{"id":"content","title":"Content","role":"content","text":"Here, you learn how to manage Redis Cloud accounts and their settings:\n\n- [Manage account settings](https://redis.io/docs/latest/operate/rc/accounts/account-settings)\n\n- [Manage user account and profile](https://redis.io/docs/latest/operate/rc/accounts/user-profile)\n\n- [Switch between Redis Cloud accounts](https://redis.io/docs/latest/operate/rc/accounts/user-profile#switch-redis-cloud-accounts)"}],"examples":[]}
{"schema_version":2,"id":"operate/rc/accounts/user-profile","title":"Manage user account and profile","url":"https://redis.io/docs/latest/operate/rc/accounts/user-profile/","summary":"Describes the how to manage your user account profile and how to switch between Redis Cloud accounts.","tags":["docs","operate","rc"],"last_updated":"2026-10-01T15:21:38+01:00","page_type":"content","content_hash":"f3ff21bf9aee350e4a856f5e513ee72062fb57641de2e29d60da63e4fb3ae8e6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"When you sign in to the [Redis Cloud console](https://cloud.redis.io/), you use a profile associated with one or more Redis Cloud accounts.  \n\nThis account has a profile with settings that you can manage using the Profile control located near the top, right corner of the Redis Cloud console:\n\n![Use the Profile control to manage your user account profile and to switch between Redis Cloud accounts.](https://redis.io/docs/latest/images/rc/account-selector-single-account.png)\n\nWhen you open the Profile control, you can:\n\n- Review and manage your user account profile.\n\n- Sign out from the Redis Cloud console.\n\n- Switch between Redis Cloud subscriptions administered by your user account."},{"id":"manage-user-profile","title":"Manage user profile","role":"content","text":"To review your user profile settings, select **User profile** from the Profile control.  This displays the **User Profile** screen:\n\n![The User Profile screen lets you manage selected settings associated with your user account.](https://redis.io/docs/latest/images/rc/user-profile-settings.png)\n\nThis screen contains up to three sections, including:\n\n- The *User details* section includes basic information about your account, including _First name_, _Last name_, _Job title_, _Email_, and the date the account was created.  The names and job title can be edited; other settings are read-only.\n\n- The *Password* section lets you change the password for accounts created and managed by Redis Cloud. \n\n    If you're using single sign-on authentication, you cannot change the password using the *User Profile* screen.  Such accounts are managed by an identity provider (IdP).  For help changing (or recovering) the passwords for these accounts, consult your identity provider docs.\n\n- The **Multi-factor authentication (MFA)** section lets you [manage MFA settings](https://redis.io/docs/latest/operate/rc/security/access-control/multi-factor-authentication) for the current user account.  \n\n    When you activate a mobile device, you can use SMS MFA as a second authentication factor.\n\n    To use an authentication app as the factor, you need to activate a mobile device and then use that device to enable the app."},{"id":"sign-out","title":"Sign out","role":"content","text":"To sign out from the Redis Cloud console, select **Logout** from the profile control."},{"id":"switch-redis-cloud-accounts","title":"Switch Redis cloud accounts","role":"content","text":"When your user account is authorized to manage multiple Redis Cloud accounts, each account is displayed in the Profile control.\n\n![To switch between Redis Cloud accounts, select the desired account from the list shown on the Profile control.](https://redis.io/docs/latest/images/rc/account-selector-switch-account.png)\n\nTo switch accounts, select the desired account from the list shown in the Profile control.\n\n> [!NOTE]\n> To create another Redis Cloud account associated with the same email address, you need to [sign up for Redis Cloud](https://redis.io/docs/latest/operate/rc/rc-quickstart) again with a plus address of the email address. For many email providers, you can do this by adding `+` and any string to the end of your username. For example, if your address is `example@example.com`, enter `example+account2@example.com`."},{"id":"save-or-discard-changes","title":"Save or discard changes","role":"content","text":"Use the **Discard changes** button to cancel user profile setting changes or the **Save changes** button to save changes."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/api/api-reference","title":"Redis Cloud API","url":"https://redis.io/docs/latest/operate/rc/api/api-reference/","summary":"","tags":[],"last_updated":"2025-06-18T12:21:08-04:00","page_type":"index","sections":[],"examples":[]}
{"schema_version":2,"id":"operate/rc/api/examples/audit-system-logs","title":"Audit using System Log","url":"https://redis.io/docs/latest/operate/rc/api/examples/audit-system-logs/","summary":"Use the system log to track and audit actions performed in the account.","tags":["docs","operate","rc"],"last_updated":"2026-10-01T15:21:38+01:00","page_type":"content","content_hash":"d230f15261197b1de2c859e075d4b25f36e1f2f5c9b52aa985894bade0cf41c0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"System logs collect and report actions performed on various entities in your Redis Cloud subscription.  These entities include the account itself, users, API Keys, subscriptions, databases, accounts, payment methods, and more. For each entity, various lifecycle events are logged in the system log.\n\nTo view the log, sign in to the [Redis Cloud console](https://cloud.redis.io/) and then select **Logs** from the main menu.\n\n![Choose the Logs command from the Redis Cloud console menu to view your subscription system log.](https://redis.io/docs/latest/images/rc/system-logs.png)\n\nTo learn more, see [System logs](https://redis.io/docs/latest/operate/rc/logs-reports/system-logs)."},{"id":"get-system-logs-via-rest-api","title":"Get system logs via REST API","role":"content","text":"The REST API operation for querying the system log is [`GET /logs`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Account/operation/getAccountSystemLogs).  Your account must have the **Owner**, **Viewer**, or **Logs viewer** role to query this endpoint.\n\nThe `/logs` API operation accepts the following parameters:\n\n- `offset` - The starting point for the results.  The default value of `0` starts with the latest log entry. A value of `11` skips the first 10 entries and retrieves entries starting with the 11 and older.\n- `limit` - The maximum number of entries to return per request. The default value is `100`.\n\nThe system log reports activity for the entire account. It reports log entries for all types of entities, including subscriptions, databases, and so on."},{"id":"request-results","title":"Request results","role":"content","text":"An API system log request results in data that includes an `entries` array. The entries are sorted by system log entry ID in descending order and include the following properties:\n\n- `id` - A unique identifier for each system log entry.\n\n- `time` - The system log entry timestamp, defined in ISO-8601 date format and in the UTC timezone (for example: `2019-03-15T14:26:02Z`).\n\n- `originator` - The name of the user who performed the action described by the system log entry.\n\n- `apiKeyName` - The name of the API key used to perform the action described by the system log entry.\n    This field only appears if the action was performed through the API.\n    If the operation was performed through the Redis Cloud console, this property is omitted.\n\n- `resource` - The name of the entity associated with the logged action (for example, database name).\n    This property is omitted if it is not applicable to the specific log entry.\n\n- `type` - The category associated with the action log entry.\n\n- `description` - The detailed description of the action in the log entry."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/api/examples/back-up-and-import-data","title":"Database backup and import","url":"https://redis.io/docs/latest/operate/rc/api/examples/back-up-and-import-data/","summary":"When you create or update a database, you can specify the backup path. The import API operation lets you import data from various source types and specified locations.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T14:59:52+01:00","page_type":"content","content_hash":"3b2cdc3be7631e222166dd397847c38b75f038ad4ae755cf078ecb7e79913da3","sections":[{"id":"back-up-a-database","title":"Back up a database","role":"overview","text":"When you create or update a database, you can specify the (optional) `periodicBackupPath` parameter\nwith a [backup path](https://redis.io/docs/latest/operate/rc/databases/back-up-data).\nThis parameter enables periodic and on-demand backup operations for the specified database.\n\n> [!NOTE]\n> The number of database backups that can run simultaneously on a cluster is limited to 4 by default.\n\nFor Redis Cloud Pro databases, back up a database with [`POST /subscriptions/{subscriptionId}/databases/{databaseId}/backup`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Databases-Pro/operation/backupDatabase). For Redis Cloud Essentials databases, use [`POST /fixed/subscriptions/{subscriptionId}/databases/{databaseId}/backup`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Databases-Essentials/operation/backupDatabase_1).\nOn-demand database backup is an [asynchronous operation](https://redis.io/docs/latest/operate/rc/api/get-started/process-lifecycle).\n\nThe backup database API does not require a body. Instead, the `periodicBackupPath` must be set to a valid path with available storage capacity to store the backup files for the specific database. You can set an `adhocBackupPath` in the body to specify a different backup location for this backup.\n\nSee [Set up backup storage locations](https://redis.io/docs/latest/operate/rc/databases/back-up-data#set-up-backup-storage-locations) to learn how to configure your backup storage locations."},{"id":"import-data-to-a-database","title":"Import data to a database","role":"content","text":"You can import data into an existing database from multiple storage sources, including AWS S3, Redis, and FTP.\nDatabase import is an [asynchronous operation](https://redis.io/docs/latest/operate/rc/api/get-started/process-lifecycle).\n\nUse [`POST /v1/subscriptions/{subscriptionId}/databases/{databaseId}/import`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Databases-Pro/operation/importDatabase) to import data to an existing Redis Cloud Pro database. For Redis Cloud Essentials databases, use [`POST /fixed/subscriptions/{subscriptionId}/databases/{databaseId}/backup`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Databases-Essentials/operation/importDatabase_1)\n\nThe requirements for data import are:\n\n- The URI of the data\n    - The source URI must be accessible to the importing database\n    - The data format must be a Redis backup file or a Redis database\n- The subscription ID and database ID of the destination database\n\nThe duration of the import process depends on the amount of data imported and the network bandwidth between the data source and the importing database.\n\n> [!WARNING]\n> Data imported into an existing database overwrites any existing data.\n\nYou can specify the backup location with the `sourceType` and `importFromUri` values for these sources:\n\n|Data location|`sourceType`|`importFromUri`|\n|---|---|---|\n|Amazon AWS S3|aws-s3|s3://bucketname/[path/]filename.rdb[.gz]|\n|FTP|ftp|ftp://[username][:password]@[:port]/[path/]filename.rdb[.gz]|\n|Google Blob Storage|google-blob-storage|gs://bucketname/[path/]filename.rdb[.gz]|\n|Microsoft Azure Blob Storage|azure-blob-storage|abs://:storage_account_access_key@storage_account_name/[container/]filename.rdb[.gz]|\n|Redis server|redis|redis://[db_password]@[host]:[port]|\n|Web server|HTTP|HTTP://[username][:password]@[:port]/[path/]filename.rdb[.gz]|\n\nSee [Import data](https://redis.io/docs/latest/operate/rc/databases/import-data) to learn how to set up your storage locations for data import."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/api/examples/create-database","title":"Create and manage databases","url":"https://redis.io/docs/latest/operate/rc/api/examples/create-database/","summary":"This article describes how to create and manage a database using the Redis Cloud API.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T14:59:52+01:00","page_type":"content","content_hash":"4193e06521a48ae189810357836089fd1972bfa8dd6ebfd1def5f01eee140e57","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can use the Redis Cloud REST API to create and manage databases."},{"id":"redis-cloud-essentials","title":"Redis Cloud Essentials","role":"content","text":""},{"id":"create-an-essentials-database","title":"Create an Essentials database","role":"content","text":"To create a Redis Cloud Essentials database, use [`POST /fixed/subscriptions/{subscriptionId}/databases`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Databases-Essentials/operation/createFixedDatabase).\n\nThis call creates a database in the specified subscription. Use [`GET /fixed/subscriptions`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Subscriptions-Essentials/operation/getAllSubscriptions_1) to get a list of Essentials subscriptions and their IDs. \n\n[code example]\n\nThis example JSON body contains only the most basic, required parameters to create a database:\n\n- `name`: The database name. A unique name per subscription that can contain only alphanumeric characters and hyphens\n\nThere are other additional parameters and settings that can be defined on database creation. Review the database parameters and options in the [full API reference](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Databases-Essentials/operation/createFixedDatabase). \n\nSome options may not be compatible with your selected plan. Use [`GET /fixed/subscriptions/{subscriptionId}`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Subscriptions-Essentials/operation/getFixedSubscriptionsPlansBySubscriptionId) to view the plan you have selected and what options it supports.\n\nThe response body contains the `taskId` for the task that creates the database. You can use [`GET /v1/tasks/{taskId}`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Tasks/operation/getTaskById) to track the task's status."},{"id":"update-an-essentials-database","title":"Update an Essentials database","role":"content","text":"To update a Redis Cloud Essentials database, use [`PUT /fixed/subscriptions/{subscriptionId}/databases/{databaseId}`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Databases-Essentials/operation/deleteFixedDatabaseByID). \n\nThe primary component of a database update request is the JSON request body that contains the details of the requested database changes. You can see the full set of changes you can make in the [full API reference](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Databases-Essentials/operation/deleteFixedDatabaseByID).\n\nSome options may not be compatible with your selected plan. Use [`GET /fixed/subscriptions/{subscriptionId}`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Subscriptions-Essentials/operation/getFixedSubscriptionsPlansBySubscriptionId) to view the plan you have selected and what options it supports.\n\nThe response body contains the `taskId` for the task that updates the database. You can use [`GET /v1/tasks/{taskId}`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Tasks/operation/getTaskById) to track the task's status."},{"id":"redis-cloud-pro","title":"Redis Cloud Pro","role":"content","text":""},{"id":"create-a-pro-database","title":"Create a Pro database","role":"content","text":"If you want to create a Pro database in a new subscription, see [Create a Pro subscription](https://redis.io/docs/latest/operate/rc/api/examples/manage-subscriptions#create-a-pro-subscription).\n\nTo create a Redis Cloud Pro database in an existing subscription, use [`POST /subscriptions/{subscriptionId}/databases`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Databases-Pro/operation/createDatabase).\n\nThis call creates a database in the specified subscription. Use [`GET /subscriptions`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Subscriptions-Pro/operation/getAllSubscriptions) to get a list of subscriptions and their IDs. \n\n[code example]\n\nThis example JSON body contains only the most basic, required parameters to create a database:\n\n- `name`: The database name. A unique name per subscription that can contain only alphanumeric characters and hyphens\n- `datasetSizeInGb`: Maximum dataset size in GB\n\nThere are many additional parameters and settings that can be defined on database creation. Review the database parameters and options in the [full API reference](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Databases-Pro/operation/createDatabase).\n\nThe response body contains the `taskId` for the task that creates the database. You can use [`GET /v1/tasks/{taskId}`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Tasks/operation/getTaskById) to track the task's status."},{"id":"update-a-redis-cloud-pro-database","title":"Update a Redis Cloud Pro database","role":"content","text":"To update a Redis Cloud Pro database, use [`PUT /subscriptions/{subscriptionId}/databases/{databaseId}`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Databases-Pro/operation/updateDatabase). \n\nThe primary component of a database update request is the JSON request body that contains the details of the requested database changes. You can see the full set of changes you can make in the [full API reference](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Databases-Pro/operation/updateDatabase).\n\nThe response body contains the `taskId` for the task that updates the database. You can use [`GET /v1/tasks/{taskId}`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Tasks/operation/getTaskById) to track the task's status."}],"examples":[{"id":"create-an-essentials-database-ex0","language":"shell","code":"POST \"https://[host]/v1/fixed/subscriptions/{subscriptionId}/databases\"\n{\n  \"name\": \"Basic-essentials-database-example\"\n}","section_id":"create-an-essentials-database"},{"id":"create-a-pro-database-ex0","language":"shell","code":"POST \"https://[host]/v1/subscriptions/{subscriptionId}/databases\"\n{\n  \"name\": \"Basic-database-example\",\n  \"datasetSizeInGb\": 1\n}","section_id":"create-a-pro-database"}]}
{"schema_version":2,"id":"operate/rc/api/examples/dryrun-cost-estimates","title":"Estimate cost","url":"https://redis.io/docs/latest/operate/rc/api/examples/dryrun-cost-estimates/","summary":"How to evaluate the cost of a specific subscription or database without changing existing resources.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T14:59:52+01:00","page_type":"content","content_hash":"919caf29aff394037b3d473c066c68df1e6de6ecba9086752afe7ae2037a8b78","sections":[{"id":"overview","title":"Overview","role":"overview","text":"When you change your subscriptions and databases, you also change the cost of your deployment.\nWith a **dry-run request**, you can evaluate the impact and obtain a new cost estimate before you deploy these changes:\n\n- Create subscription\n- Create a database\n- Update a database"},{"id":"defining-a-dry-run-request","title":"Defining a dry-run request","role":"content","text":"API operations that support dry-run requests accept the `dryRun` boolean parameter in the JSON request body.\n\nFor example, the JSON body of a create subscription request body can include `\"dryRun\": true`:\n\n\n[code example]"},{"id":"executing-a-dry-run-request","title":"Executing a dry-run request","role":"content","text":"Dry-run requests behave like regular requests except that no changes are made to existing resources.\nA dry-run request produces a cost evaluation report for the subscription.\n\n| API Operation | `\"dryRun\": false` (default) | `\"dryRun\": true` |\n|---|---|---|\n| **Create subscription** | Create a subscription | Returns a cost evaluation report of the planned subscription |\n| **Create database** | Creates a new database in the subscription | Returns a cost evaluation report for the relevant subscription |\n| **Update database** | Changes the specified database | Returns a cost evaluation report and evaluates whether the relevant subscription requires additional resources based on the database modification |"},{"id":"example-of-a-dry-run-request--response","title":"Example of a dry-run request & response","role":"example","text":"This section demonstrates a complete example of a create subscription dry-run request and response.\n\n#### Dry-run request body\n\nHere is an example of the pricing request body:\n\n[code example]\n\n1. The create subscription request contains the `\"dryRun\": true` parameter\n2. The databases array contains the definitions of multiple database templates, named `database-a`,  `database-b`, `database-c`, etc.\n3. Note that the databases differ in structure (size, throughput measurement, replication, quantity, etc.)\n\n#### Dry-run response\n\nHere is an example of the pricing response section for the above create subscription dry-run request:\n\n[code example]\n\n> [!NOTE]\n> Some of the response content was omitted for brevity.\n\n1. The `pricing` array contains an element for each database, containing the database name and cost evaluation related to that database\n2. The database cost is measured in type and number of shards required for the specific database, as defined by the database template in the request. See [Cloud Pricing](https://redislabs.com/redis-enterprise-cloud/pricing)\n3. The cost evaluation for each database is measured in quantity of the specific shard type required by the requested database. The cost per shard is defined by the fields `pricePerUnit` (where `unit` is a shard of the specific type), `priceCurrency`, and `pricePeriod`\n4. For example, to calculate the total hourly cost of a database, use the following formula:  `pricePerUnit` * `quantity`\n5. The structure of the pricing response depends on the cloud account used by the request. There are two types of cloud accounts: a cloud account owned, named, and managed by the customer (AWS only) and one maintained by \"Redis resources\" cloud account (All cloud providers)\n6. For a customer-provided cloud account - The cost evaluation response lists the AWS resources required (storage and compute instances) without pricing data (which depends on the specific details of the customer's AWS account)\n7. For a Redis internal cloud account (defined a `cloudAccountId = 1` in the create subscription request) - The cost evaluation response includes a `MinimumPrice` element. This indicates the minimal hourly cost of the entire subscription. This minimum price will be charged if the sum of all shards for all the subscription's databases is less than the specified minimum price"},{"id":"viewing-actual-subscription-cost","title":"Viewing actual subscription cost","role":"content","text":"The `Get subscriptions` and `Get subscription by id` JSON response contains an element named `subscriptionPricing` that details the latest calculated cost of a subscription, grouped by shard type for all the databases in the subscription."}],"examples":[{"id":"defining-a-dry-run-request-ex0","language":"json","code":"{\n  \"name\": \"DryRun Cost evaluation subscription\",\n  \"dryRun\": true,\n  \"paymentMethodId\": 8240,\n  \"cloudProviders\": [\n    {\n      \"cloudAccountId\": 16424,\n      \"regions\": [\n        {\n          \"region\": \"us-east-1\",\n          \"networking\": {\n            \"deploymentCIDR\": \"10.0.0.0/24\"\n          }\n        }\n      ]\n    }\n  ],\n  \"databases\": [\n    {\n      \"name\": \"database-a\",\n      \"memoryLimitInGb\": 0.1\n    }\n  ]\n}","section_id":"defining-a-dry-run-request"},{"id":"example-of-a-dry-run-request--response-ex0","language":"json","code":"{\n  \"name\": \"DryRun Cost evaluation subscription\",\n  \"dryRun\": true,\n  \"paymentMethodId\": 8240,\n  \"cloudProviders\": [\n    {\n      \"cloudAccountId\": 16424,\n      \"regions\": [\n        {\n          \"region\": \"us-east-1\",\n          \"networking\": {\n            \"deploymentCIDR\": \"10.0.0.0/24\"\n          }\n        }\n      ]\n    }\n  ],\n  \"databases\": [\n    {\n      \"name\": \"database-a\",\n      \"throughputMeasurement\": {\n        \"by\": \"operations-per-second\",\n        \"value\": 1000\n      },\n      \"memoryLimitInGb\": 0.1\n      , \"quantity\": 3\n    },\n    {\n      \"name\": \"database-b\",\n      \"throughputMeasurement\": {\n        \"by\": \"operations-per-second\",\n        \"value\": 1000\n      },\n      \"replication\": false\n      ,\"memoryLimitInGb\": 0.1\n    },\n    {\n      \"name\": \"database-c\",\n      \"throughputMeasurement\": {\n        \"by\": \"operations-per-second\",\n        \"value\": 1000\n      },\n      \"replication\": true\n      ,\"memoryLimitInGb\": 5\n    },\n    {\n      \"name\": \"database-d\",\n      \"throughputMeasurement\": {\n        \"by\": \"operations-per-second\",\n        \"value\": 10000\n      },\n      \"replication\": false\n      ,\"memoryLimitInGb\": 12\n    },\n    {\n      \"name\": \"database-e\",\n      \"throughputMeasurement\": {\n        \"by\": \"operations-per-second\",\n        \"value\": 25000\n      },\n      \"replication\": true\n      ,\"memoryLimitInGb\": 25\n    }   ]\n}","section_id":"example-of-a-dry-run-request--response"},{"id":"example-of-a-dry-run-request--response-ex1","language":"shell","code":"{\n  \"response\": {\n    \"resource\": {\n      \"pricing\": [\n        {\n          \"databaseName\": \"database-a\",\n          \"type\": \"Shards\",\n          \"typeDetails\": \"micro\",\n          \"quantity\": 6,\n          \"quantityMeasurement\": \"shards\",\n          \"pricePerUnit\": 0.027,\n          \"priceCurrency\": \"USD\",\n          \"pricePeriod\": \"hour\"\n        },\n        {\n          \"databaseName\": \"database-b\",\n          \"type\": \"Shards\",\n          \"typeDetails\": \"micro\",\n          \"quantity\": 1,\n          \"quantityMeasurement\": \"shards\",\n          \"pricePerUnit\": 0.027,\n          \"priceCurrency\": \"USD\",\n          \"pricePeriod\": \"hour\"\n        },\n        {\n          \"databaseName\": \"database-c\",\n          \"type\": \"Shards\",\n          \"typeDetails\": \"high-throughput\",\n          \"quantity\": 2,\n          \"quantityMeasurement\": \"shards\",\n          \"pricePerUnit\": 0.124,\n          \"priceCurrency\": \"USD\",\n          \"pricePeriod\": \"hour\"\n        },\n        {\n          \"databaseName\": \"database-d\",\n          \"type\": \"Shards\",\n          \"typeDetails\": \"small\",\n          \"quantity\": 1,\n          \"quantityMeasurement\": \"shards\",\n          \"pricePerUnit\": 0.156,\n          \"priceCurrency\": \"USD\",\n          \"pricePeriod\": \"hour\"\n        },\n        {\n          \"databaseName\": \"database-e\",\n          \"type\": \"Shards\",\n          \"typeDetails\": \"large\",\n          \"quantity\": 2,\n          \"quantityMeasurement\": \"shards\",\n          \"pricePerUnit\": 0.293,\n          \"priceCurrency\": \"USD\",\n          \"pricePeriod\": \"hour\"\n        },\n        {\n          \"type\": \"EBS Volume\",\n          \"quantity\": 345,\n          \"quantityMeasurement\": \"GB\"\n        },\n        {\n          \"type\": \"r5.xlarge\",\n          \"quantity\": 3,\n          \"quantityMeasurement\": \"instances\"\n        }\n      ]\n    }\n  }  \n}","section_id":"example-of-a-dry-run-request--response"}]}
{"schema_version":2,"id":"operate/rc/api/examples/generate-cost-report","title":"Generate FOCUS-compliant cost report","url":"https://redis.io/docs/latest/operate/rc/api/examples/generate-cost-report/","summary":"Shows how to generate and download a cost report in FOCUS format using the Redis Cloud REST API.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T14:59:52+01:00","page_type":"content","content_hash":"491f50ba02db9d845dbfe254e6a6926e5e7d72dc6c0f728404dbdbe0925f42ca","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can use the Redis Cloud REST API to generate and download a cost report in a [FinOps Open Cost and Usage Specification (FOCUS)](https://focus.finops.org/) compatible format. The report includes detailed information about your Redis Cloud subscription usage and associated charges.\n\nTo get the cost report using the REST API:\n\n1. Use [`POST /cost-report`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Account/operation/createCostReport) to generate a cost report, with the request body containing the `startDate` and `endDate` for the report as well as any optional filters. Your account must have the **Owner**, **Viewer**, or **Billing admin** role to generate a cost report through this endpoint.\n\n    The response includes a `taskId` that you can use to track the status of the report generation.\n\n1. Use [`GET /tasks/{taskId}`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Tasks/operation/getTaskById) to check report generation status. The report is ready when the `status` is `processing-completed` and the `response` field contains a `costReportId`.\n1. Use [`GET /cost-report/{costReportId}`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Account/operation/getCostReport) to download the generated cost report.\n\nThe following sections provide examples for each step."},{"id":"generate-a-cost-report","title":"Generate a cost report","role":"content","text":"To generate a cost report, use [`POST /cost-report`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Account/operation/createCostReport). Your account must have the **Owner**, **Billing admin**, or **Viewer** role to generate a cost report through this endpoint.\n\nInclude `startDate` and `endDate` in your request body using `YYYY-MM-DD` format. You can specify a date range up to 40 days.\n\n[code example]\n\nMore options and filters can be added to the request body to filter the report data.\n\n| Option name | Type | Description |\n|-------------|--------|-------------|\n| `format` | Enum: `csv`, `json` | The format for the report file. Default is `csv`. |\n| `subscriptionIds` | Array of integers | Filters the report to only include the specified subscriptions. |\n| `databaseIds` | Array of integers | Filters the report to only include the specified databases. |\n| `subscriptionType` | Enum: `essentials`, `pro` | Filters the report to only include subscriptions of the specified type. |\n| `regions` | Array of strings | Filters the report to only include subscriptions in the specified regions. |\n| `tags` | Array of key-value pairs | Filters the report to only include databases with the specified [tags](https://redis.io/docs/latest/operate/rc/databases/tag-database). Both `key` and `value` are required for each tag. |\n\nFor example, the following request body generates a CSV report for all databases in the `us-east-1` region that have the `team:marketing` tag in January 2025:\n\n[code example]\n\nThe response body is a [task object](https://redis.io/docs/latest/operate/rc/api/get-started/manage-tasks#task-information) that contains the `taskId` for the task that generates the cost report:\n\n[code example]"},{"id":"get-cost-report-status","title":"Get cost report status","role":"content","text":"To get the status of the cost report generation, use [`GET /tasks/{taskId}`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Tasks/operation/getTaskById) with the `taskId` from the previous step.\n\nWhen the report is ready, the `status` is `processing-completed` and the `response` field contains a `costReportId`:\n\n[code example]"},{"id":"download-cost-report","title":"Download cost report","role":"content","text":"To get the cost report, use [`GET /cost-report/{costReportId}`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Account/operation/getCostReport) with the `costReportId` from the previous step.\n\nYou can use this cost report with any FOCUS-compatible cost reporting tool to analyze and visualize your costs."},{"id":"cost-report-fields","title":"Cost report fields","role":"content","text":"The cost report returned from the Redis Cloud REST API contains fields from the [FOCUS column library](https://focus.finops.org/focus-columns/). The Redis Cloud-specific implementation is described in the following sections.\n\n#### Billing account and publisher fields\n\n| Field | Type | Description |\n|---|---|---|\n| `BillingAccountId` | String | Redis Cloud account ID. |\n| `BillingAccountName` | String | Display name of the Redis Cloud account. |\n| `BillingAccountType` | String | Type of billing account structure. Always set to `Redis Cloud`. |\n| `PublisherName` | String | Publisher/vendor of the service, which is Redis. Always `redis.io`. |\n\n#### Pricing fields\n\n| Field | Type | Description |\n|---|---|---|\n| `PricingCategory` | String | Pricing model category. Always `Standard`. |\n| `PricingCurrency` | String | The currency used for pricing. Always `USD`. |\n| `PricingQuantity` | BigDecimal | Quantity of units being priced. For hourly services like Redis Cloud Pro, this is the number of hours. For monthly services like Redis Cloud Essentials and network costs, this is typically one month. |\n| `PricingUnit` | String | The unit of measure for pricing. Can be `Hours` (Pro), `Months` (Essentials), or `Network`. |\n\n#### Period fields\n\n| Field | Type | Description |\n|---|---|---|\n| `BillingPeriodStart` | Instant (ISO 8601 DateTime) | Start of the billing period (inclusive). |\n| `BillingPeriodEnd` | Instant (ISO 8601 DateTime) | End of the billing period (exclusive). |\n| `ChargePeriodStart` | Instant (ISO 8601 DateTime) | Start of the specific charge period (inclusive). May differ from billing period if the resource was created mid-period. |\n| `ChargePeriodEnd` | Instant (ISO 8601 DateTime) | End of the specific charge period (exclusive). May differ from billing period if the resource was deleted mid-period. |\n\n#### Cost fields\n\n| Field | Type | Description |\n|---|---|---|\n| `BilledCost` | BigDecimal | Cost that Redis will invoice you for. |\n| `BillingCurrency` | String (ISO 4217) | Currency code for all billing amounts. Always `USD`. |\n| `ConsumedQuantity` | BigDecimal | Actual amount of resources used. May be null for fixed-price plans. |\n| `ConsumedUnit` | String | Unit for `ConsumedQuantity`. Can be `Hours` or `Network`. |\n| `ContractedCost` | BigDecimal | Cost after applying contractual discounts but before credits. |\n| `ContractedUnitPrice` | BigDecimal | Per-unit price after applying your negotiated discounts. |\n| `EffectiveCost` | BigDecimal | True cost after all discounts, credits, and adjustments. |\n| `ListCost` | BigDecimal | Cost at Redis's published list prices before any discounts. |\n| `ListUnitPrice` | BigDecimal | Redis's published price per unit at list rates. |\n\n#### Location, Resource, and SKU fields\n\n| Field | Type | Description |\n|---|---|---|\n| `RegionId` | String | Cloud vendor region identifier. |\n| `RegionName` | String | Display name of the region. |\n| `ResourceId` | String | Identifier of the resource being charged. |\n| `ResourceName` | String | Display name you gave to the resource in the Redis Cloud console. |\n| `ResourceType` | String | Whether the charge is for a specific database or a subscription-level service. |\n| `Tags` | JSON Map<String, String> | User-defined tags on resources. |\n| `SkuPriceDetails` | JSON Map<String, Object> | Redis-specific technical details about the database configuration, such as RBUs, memory limit, and throughput. |\n\n#### Charge fields\n\n| Field | Type | Description |\n|---|---|---|\n| `ChargeCategory` | String | Category of the charge: `Usage`, `Purchase`, `Tax`, or `Adjustment`. |\n| `ChargeDescription` | String | A clear description of what you're being charged for, combining service type and resource details. |\n| `ChargeFrequency` | String | How often the charge occurs - always `Recurring`. |\n\n#### Service fields\n\n| Field | Type | Description |\n|---|---|---|\n| `ServiceCategory` | String | The high-level category of service - always `Databases`. |\n| `ServiceName` | String | The Redis Cloud service tier - `Redis Cloud Pro` or `Redis Cloud Essentials`. |\n| `ServiceSubcategory` | String | The specific type of database service - always `Caching`. |"}],"examples":[{"id":"generate-a-cost-report-ex0","language":"json","code":"{\n  \"startDate\": \"2025-01-01\",\n  \"endDate\": \"2025-01-31\"\n}","section_id":"generate-a-cost-report"},{"id":"generate-a-cost-report-ex1","language":"json","code":"{\n  \"startDate\": \"2025-01-01\",\n  \"endDate\": \"2025-01-31\",\n  \"format\": \"csv\",\n  \"regions\": [\"us-east-1\"],\n  \"tags\": [\n    {\n      \"key\": \"team\",\n      \"value\": \"marketing\"\n    }\n  ]\n}","section_id":"generate-a-cost-report"},{"id":"generate-a-cost-report-ex2","language":"json","code":"{\n  \"taskId\": \"7ba51acc-cd1d-44c7-8453-281730d214ce\",\n  \"commandType\": \"costReportCreateRequest\",\n  \"status\": \"received\",\n  \"description\": \"Task request received and is being queued for processing.\",\n  \"timestamp\": \"2025-11-07T15:44:29.811142433Z\",\n  \"links\": [\n    {\n      \"href\": \"https://api.redislabs.com/v1/tasks/7ba51acc-cd1d-44c7-8453-281730d214ce\",\n      \"type\": \"GET\",\n      \"rel\": \"task\"\n    }\n  ]\n}","section_id":"generate-a-cost-report"},{"id":"get-cost-report-status-ex0","language":"json","code":"{\n  \"taskId\": \"7ba51acc-cd1d-44c7-8453-281730d214ce\",\n  \"commandType\": \"costReportCreateRequest\",\n  \"status\": \"processing-completed\",\n  \"description\": \"Request processing completed successfully and its resources are now being provisioned / de-provisioned.\",\n  \"timestamp\": \"2025-11-07T15:44:31.168900133Z\",\n  \"response\": {\n    \"resource\": {\n      \"costReportId\": \"a07524cf-6d4d-47ec-a1b7-810d1cbafcf7.json\"\n    }\n  },\n  \"links\": [\n    {\n      \"href\": \"https://api.redislabs.com/v1/tasks/7ba51acc-cd1d-44c7-8453-281730d214ce\",\n      \"type\": \"GET\",\n      \"rel\": \"self\"\n    }\n  ]\n}","section_id":"get-cost-report-status"}]}
{"schema_version":2,"id":"operate/rc/api/examples","title":"REST API Examples","url":"https://redis.io/docs/latest/operate/rc/api/examples/","summary":"These examples show how to use the API and the results to expect.","tags":["docs","operate","rc"],"last_updated":"2024-02-26T13:11:00+01:00","children":[{"id":"operate/rc/api/examples/manage-subscriptions","summary":"Describes how to create and manage a subscription using the Redis Cloud API.","title":"Create and manage subscriptions","url":"https://redis.io/docs/latest/operate/rc/api/examples/manage-subscriptions/"},{"id":"operate/rc/api/examples/create-database","summary":"This article describes how to create and manage a database using the Redis Cloud API.","title":"Create and manage databases","url":"https://redis.io/docs/latest/operate/rc/api/examples/create-database/"},{"id":"operate/rc/api/examples/back-up-and-import-data","summary":"When you create or update a database, you can specify the backup path. The import API operation lets you import data from various source types and specified locations.","title":"Database backup and import","url":"https://redis.io/docs/latest/operate/rc/api/examples/back-up-and-import-data/"},{"id":"operate/rc/api/examples/dryrun-cost-estimates","summary":"How to evaluate the cost of a specific subscription or database without changing existing resources.","title":"Estimate cost","url":"https://redis.io/docs/latest/operate/rc/api/examples/dryrun-cost-estimates/"},{"id":"operate/rc/api/examples/audit-system-logs","summary":"Use the system log to track and audit actions performed in the account.","title":"Audit using System Log","url":"https://redis.io/docs/latest/operate/rc/api/examples/audit-system-logs/"},{"id":"operate/rc/api/examples/generate-cost-report","summary":"Shows how to generate and download a cost report in FOCUS format using the Redis Cloud REST API.","title":"Generate FOCUS-compliant cost report","url":"https://redis.io/docs/latest/operate/rc/api/examples/generate-cost-report/"}],"page_type":"index","sections":[],"examples":[]}
{"schema_version":2,"id":"operate/rc/api/examples/manage-subscriptions","title":"Create and manage subscriptions","url":"https://redis.io/docs/latest/operate/rc/api/examples/manage-subscriptions/","summary":"Describes how to create and manage a subscription using the Redis Cloud API.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T14:59:52+01:00","page_type":"content","content_hash":"ca5c772836a9e5cadf5eea9f083d23c5dd7f0185352b5286fad3e0711ff6f6c2","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The Redis Cloud REST API lets you create and manage all kinds of subscriptions."},{"id":"redis-cloud-essentials","title":"Redis Cloud Essentials","role":"content","text":""},{"id":"create-an-essentials-subscription","title":"Create an Essentials subscription","role":"content","text":"Use [`POST /v1/fixed/subscriptions`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Subscriptions-Essentials/operation/createSubscription_1) to create an Essentials subscription.\n\n[code example]\n\nModify the following parameters in the sample JSON document to create a subscription on your own account:\n\n- **`paymentMethodId`** - Specify a payment method connected to your account.\n\n    Use [`GET /v1/payment-methods`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Account/operation/getAccountPaymentMethods) to find a payment method ID.\n\n    You don't need to pass this field in your API request if you subscribed to Redis Cloud through a marketplace integration.\n\n- **`planId`** - Specify an essentials plan to create.\n\n    Use [`GET /v1/fixed/plans`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Subscriptions-Essentials/operation/getAllFixedSubscriptionsPlans) to get a list of plan IDs.\n\nAfter you create an Essentials subscription, you must use the [`POST /v1/fixed/subscriptions/{subscriptionId}/databases`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Databases-Essentials/operation/createFixedDatabase) endpoint to [create the database](https://redis.io/docs/latest/operate/rc/api/examples/create-database#redis-cloud-essentials).\n\nYou can include the contents of the JSON document in the `POST /v1/fixed/subscriptions` operation in the [Swagger UI](https://api.redislabs.com/v1/swagger-ui.html). See [Swagger user interface](https://redis.io/docs/latest/operate/rc/api/get-started/use-rest-api#swagger-user-interface) for more details.\n\n> [!NOTE]\n> The Swagger UI generates default JSON examples for `POST` and `PUT` operations. You can reference these examples and modify them to fit your specific needs and account settings. The examples will fail if used as-is.\n\nThe response body contains the `taskId` for the task that creates the subscription. You can use [`GET /v1/tasks/{taskId}`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Tasks/operation/getTaskById) to track the task's status."},{"id":"update-an-essentials-subscription","title":"Update an Essentials subscription","role":"content","text":"Use [`PUT /v1/fixed/subscriptions/{subscriptionId}`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Subscriptions-Essentials/operation/updateSubscription_1) to update an Essentials subscription.\n\n[code example]\n\nYou can only change the following settings with this endpoint:\n- **`name`** - Specify a new name for your subscription.\n\n- **`planId`** - Specify a new Essentials plan to change to.\n\n    You can only change to a plan that:\n        \n    - Is in the same cloud provider and region of your current plan.\n        \n    - Has a compatible [High-availability option](https://redis.io/docs/latest/operate/rc/databases/configuration/high-availability) to your current plan. For example, if your current plan has single-zone replication, you cannot switch to a plan with multi-zone replication, but you can switch to a plan with no replication. If your current plan has multi-zone replication, you must switch to a plan with multi-zone replication.\n\n    Use [`GET /v1/fixed/plans/subscriptions/{subscriptionID}`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Subscriptions-Essentials/operation/getFixedSubscriptionsPlanById) to get a list of plan IDs that are compatible with your subscription.\n\n- **`paymentMethodId`** - Specify a different payment method connected to your account.\n\n    Use [`GET /v1/payment-methods`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Account/operation/getAccountPaymentMethods) to find a payment method ID.\n\nThe response body contains the `taskId` for the task that updates the subscription. You can use [`GET /v1/tasks/{taskId}`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Tasks/operation/getTaskById) to track the task's status."},{"id":"delete-an-essentials-subscription","title":"Delete an Essentials subscription","role":"content","text":"Use [`DELETE /v1/fixed/subscriptions/{subscriptionId}`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Subscriptions-Essentials/operation/deleteSubscriptionById_1) to delete a subscription.\n\n[code example]\nThe response body contains the `taskId` for the task that deletes the subscription. You can use [`GET /v1/tasks/{taskId}`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Tasks/operation/getTaskById) to track the task's status."},{"id":"redis-cloud-pro","title":"Redis Cloud Pro","role":"content","text":""},{"id":"create-a-pro-subscription","title":"Create a Pro subscription","role":"content","text":"Use [`POST /v1/subscriptions`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Subscriptions-Pro/operation/createSubscription) to create a Pro subscription.\n\n[code example]\n\nModify the following parameters in the sample JSON document to create a subscription on your own account:\n\n- **`paymentMethodId`** - Specify a payment method connected to your account.\n\n    Use [`GET /v1/payment-methods`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Account/operation/getAccountPaymentMethods) to find a payment method ID.\n\n    You don't need to pass this field in your API request if you subscribed to Redis Cloud through a marketplace integration.\n\nThe request JSON body contains two primary segments: subscription specification and databases specification. When you create a subscription, you must specify one or more databases in the \"`databases`\" array. \n\nThere are many additional parameters and settings that can be defined on subscription and database creation. Review the subscription parameters and options in the [Full API documentation](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Subscriptions-Pro/operation/createSubscription).\n\nThe response body contains the `taskId` for the task that creates the subscription. You can use [`GET /v1/tasks/{taskId}`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Tasks/operation/getTaskById) to track the task's status."},{"id":"update-a-pro-subscription","title":"Update a Pro subscription","role":"content","text":"Use [`PUT /v1/subscriptions/{subscriptionId}`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Subscriptions-Pro/operation/updateSubscription) to update a Pro subscription.\n\n[code example]\n\nYou can only change the following settings with this endpoint:\n- **`name`** - Specify a new name for your subscription.\n\n- **`paymentMethodId`** - Specify a different payment method connected to your account.\n\n    Use [`GET /v1/payment-methods`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Account/operation/getAccountPaymentMethods) to find a payment method ID.\n\nThe response body contains the `taskId` for the task that updates the subscription. You can use [`GET /v1/tasks/{taskId}`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Tasks/operation/getTaskById) to track the task's status."},{"id":"delete-a-pro-subscription","title":"Delete a Pro subscription","role":"content","text":"Use [`DELETE /v1/subscriptions/{subscriptionId}`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Subscriptions-Pro/operation/deleteSubscriptionById) to delete a subscription.\n\n[code example]\nThe response body contains the `taskId` for the task that deletes the subscription. You can use [`GET /v1/tasks/{taskId}`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Tasks/operation/getTaskById) to track the task's status."}],"examples":[{"id":"create-an-essentials-subscription-ex0","language":"sh","code":"POST \"https://[host]/v1/fixed/subscriptions\"\n{\n    \"name\": \"My new subscription\",\n    \"planId\": <plan_id>,\n    \"paymentMethodId\": <payment_id>\n}","section_id":"create-an-essentials-subscription"},{"id":"update-an-essentials-subscription-ex0","language":"sh","code":"PUT \"https://[host]/v1/fixed/subscriptions/{subscriptionId}\"\n{\n    \"name\": \"new-subscription-name\",\n    \"planId\": <plan_id>,\n    \"paymentMethodId\": <payment_id>\n}","section_id":"update-an-essentials-subscription"},{"id":"delete-an-essentials-subscription-ex0","language":"sh","code":"DELETE \"https://[host]/v1/fixed/subscriptions/{subscriptionId}\"","section_id":"delete-an-essentials-subscription"},{"id":"create-a-pro-subscription-ex0","language":"sh","code":"POST \"https://[host]/v1/subscriptions\"\n{\n    \"name\": \"Basic Subscription Example\",\n    \"paymentMethodId\": <payment_id>,\n    \"cloudProviders\": [\n        {\n            \"provider\": \"AWS\",\n            \"regions\": [\n                {\n                    \"region\": \"us-east-1\",\n                    \"networking\": {\n                        \"deploymentCIDR\": \"10.0.1.0/24\"\n                    }\n                }\n            ]\n        }\n    ],\n    \"databases\": [\n        {\n            \"name\": \"Redis-database-example\",\n            \"protocol\": \"redis\",\n            \"datasetSizeInGb\": 1\n        }\n    ]\n}","section_id":"create-a-pro-subscription"},{"id":"update-a-pro-subscription-ex0","language":"sh","code":"PUT \"https://[host]/v1/subscriptions/{subscriptionId}\"\n{\n    \"name\": \"new-subscription-name\",\n    \"paymentMethodId\": <payment_id>\n}","section_id":"update-a-pro-subscription"},{"id":"delete-a-pro-subscription-ex0","language":"sh","code":"DELETE \"https://[host]/v1/subscriptions/{subscriptionId}\"","section_id":"delete-a-pro-subscription"}]}
{"schema_version":2,"id":"operate/rc/api/get-started/enable-the-api","title":"Enable the API","url":"https://redis.io/docs/latest/operate/rc/api/get-started/enable-the-api/","summary":"Use the Redis Cloud dashboard to enable the REST API.","tags":["docs","operate","rc"],"last_updated":"2026-10-01T15:21:38+01:00","page_type":"content","content_hash":"6f00e559aa83ed476013f24760e8ae1ce70e0d2f0be8c0205a6c54cb06ffbe81","sections":[{"id":"content","title":"Content","role":"content","text":"If you have a Redis Cloud account, you can use a REST API to manage your subscriptions and databases programmatically.\n\nFor security reasons, the Redis Cloud API is inactive by default.\n\nTo enable the API:\n\n1. Sign in to your [Redis Cloud account](https://cloud.redis.io) as an account owner.\n1. From the menu, choose **Access Management**.\n1. When the **Access Management** screen appears, select the **API Keys** tab.\n\n    ![Use the **API Keys** tab of the **Access Management** screen to manage your REST API keys.](https://redis.io/docs/latest/images/rc/access-management-api-keys-tab.png)\n\n1. If a **Copy** button appears to the right of the API account key, the API is enabled.  This button copies the account key to the Clipboard.\n\n    ![Use the **Copy** button to copy the access key to the Clipboard.](https://redis.io/docs/latest/images/rc/button-copy.png)\n\n    If you see an **Enable API** button, select it to enable the API and generate your API account key.\n\n    ![Use the **Enable API** button to enable the REST API for your account.](https://redis.io/docs/latest/images/rc/button-access-management-enable-api.png)\n\nTo authenticate REST API calls, you need to use both the API account key and an [API user key](https://redis.io/docs/latest/operate/rc/api/get-started/manage-api-keys#api-user-keys) to make API calls.\n\nOnly account owners can see the access key in the account settings and give API access to other users.\n\n> [!WARNING]\n> Make sure that you keep your access keys secret. Anyone who sends an API request with a valid access key can make changes to your account.\n\nTo manage your API keys or to limit IP addresses for user keys, see [Manage API keys](https://redis.io/docs/latest/operate/rc/api/get-started/manage-api-keys)."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/api/get-started","title":"Get started with the REST API","url":"https://redis.io/docs/latest/operate/rc/api/get-started/","summary":"Describes how Redis Cloud REST API uses keys to authenticate and authorize access.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T14:59:52+01:00","children":[{"id":"operate/rc/api/get-started/enable-the-api","summary":"Use the Redis Cloud dashboard to enable the REST API.","title":"Enable the API","url":"https://redis.io/docs/latest/operate/rc/api/get-started/enable-the-api/"},{"id":"operate/rc/api/get-started/manage-api-keys","summary":"How to use the Redis Cloud console to create and manage API user keys for your account's team owners","title":"Manage API keys","url":"https://redis.io/docs/latest/operate/rc/api/get-started/manage-api-keys/"},{"id":"operate/rc/api/get-started/use-rest-api","summary":"How to use the API with various tools (especially `cURL`)","title":"Use the REST API","url":"https://redis.io/docs/latest/operate/rc/api/get-started/use-rest-api/"},{"id":"operate/rc/api/get-started/manage-tasks","summary":"A task is an API operation that is performed asynchronously because it exceeds the time allowed for the synchronous request/response model.","title":"Manage API tasks","url":"https://redis.io/docs/latest/operate/rc/api/get-started/manage-tasks/"},{"id":"operate/rc/api/get-started/process-lifecycle","summary":"API requests follow specific lifecycle phases and states, based on the complexity and length of execution of the operation.","title":"The API request lifecycle","url":"https://redis.io/docs/latest/operate/rc/api/get-started/process-lifecycle/"}],"page_type":"content","content_hash":"54b37463af35b5ae3b643c25c3eb985965b5a482b2edbb4d6c0cfd41a3a9d821","sections":[{"id":"overview","title":"Overview","role":"overview","text":"To use the Redis Cloud REST API, you need to:\n\n- Enable the API\n- Create an account key\n- Create a user key\n- Collect endpoint details\n\nTo use the keys to authenticate and authorize your request, include the keys with the request headers:\n\n| Key name         | HTTP&nbsp;header&nbsp;name   |Description                                            |\n| -----------      | -------------------| ----------------------------------------------------- |\n| Account&nbsp;key | `x-api-key`        | Account-level key assigned to all users of an account |\n| User key       | <nobr>`x-api-secret-key`</nobr> | Personal key associated with a specific user and possibly limited to certain IP ranges                      |"},{"id":"enable-the-api","title":"Enable the API","role":"content","text":"The API is disabled on all accounts by default. You must [enable the API](https://redis.io/docs/latest/operate/rc/api/get-started/enable-the-api) before you can use it."},{"id":"account-key","title":"Account key","role":"content","text":"The account key identifies your specific account when you perform an API request.  This is the account responsible for your subscription.\n\n> [!NOTE]\n> An account key is an account-level secret. Do not share this key with anyone not authorized to use the account.\n\nYou create the account key once when enabling API access.\n\nIf you need to change or delete your account key, please [contact support](https://redislabs.com/company/support/)."},{"id":"user-key","title":"User key","role":"content","text":"The user key is a personal key that belongs to a specific user having the **Owner**, **Viewer**, **Billing admin**, or **Logs viewer** role.  User keys are assigned to users when they're created.  Keys can belong to only one user; however, a user may have multiple keys.\n\nYou can view keys or copy their values _only_ during the [creation process](https://redis.io/docs/latest/operate/rc/api/get-started/manage-api-keys).\n\n> [!NOTE]\n> User keys are personal secrets. Do not share them.\n\nIndividual owners can [generate multiple user keys](https://redis.io/docs/latest/operate/rc/api/get-started/manage-api-keys)\nfor themselves, for separate apps, or for other owners, viewers, or log viewers within the same account.\n\nUse key names to uniquely associate specific API requests to individual users or apps.\n\nDoing so lets you [audit API requests](https://redis.io/docs/latest/operate/rc/api/examples/audit-system-logs) using the system log, which tracks the key used to authenticate each request."},{"id":"authentication-using-api-keys","title":"Authentication using API keys","role":"security","text":"Every API request must use the **account key** and a **user key** to authenticate.\n\nThe keys are provided as [HTTP request headers](https://redis.io/docs/latest/operate/rc/api/get-started/use-rest-api#use-the-curl-http-client), shown earlier."},{"id":"authenticate-a-request","title":"Authenticate a request","role":"security","text":"An API request successfully authenticates when:\n\n1. The account and user keys are valid and properly defined in the HTTP request headers.\n1. The user key is associated with the same account as the account key.\n1. The request originates from a valid source IP address, as defined in a [CIDR](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing) allow list associated with the user key.\n\n    This requirement applies when you've [defined a CIDR allow list](https://redis.io/docs/latest/operate/rc/api/get-started/manage-api-keys#manage-cidr-allow-list) for the secret key."},{"id":"more-info","title":"More info","role":"content","text":"To learn more, see:\n\n- [Manage API keys](https://redis.io/docs/latest/operate/rc/api/get-started/manage-api-keys)\n- [Use the API](https://redis.io/docs/latest/operate/rc/api/get-started/use-rest-api)\n- [Full API reference](https://redis.io/docs/latest/operate/rc/api/api-reference)"}],"examples":[]}
{"schema_version":2,"id":"operate/rc/api/get-started/manage-api-keys","title":"Manage API keys","url":"https://redis.io/docs/latest/operate/rc/api/get-started/manage-api-keys/","summary":"How to use the Redis Cloud console to create and manage API user keys for your account's team owners","tags":["docs","operate","rc"],"last_updated":"2026-10-05T10:59:52+01:00","page_type":"content","content_hash":"90180899102b860912f7dc7f67ab4867551834ace53db7a1dae859fff1586c93","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Every REST API request must include the following API keys:\n\n1. The **Account key** identifies the account associated with the Redis Cloud subscription.\n\n2. The **User key** identifies the user and (optionally) the context of a request.  Generated by account owners.\n\nUse the **API Keys** tab of the **Access Management** screen to manage your keys:\n\n1. Sign in to your [Redis Cloud account](https://cloud.redis.io) as an account owner.\n\n1. From the menu, choose **Access Management** and then select the **API Keys** tab.\n\n    ![Use the **API Keys** tab of the **Access Management** screen to manage your REST API keys.](https://redis.io/docs/latest/images/rc/access-management-api-keys-tab.png)\n\nIf an **Enable API** button appears, select it to [enable the REST API](https://redis.io/docs/latest/operate/rc/api/get-started/enable-the-api) for your account.\n\n![Use the **Enable API** button to enable the REST API for your account.](https://redis.io/docs/latest/images/rc/button-access-management-enable-api.png)"},{"id":"api-account-key","title":"API account key","role":"content","text":"The **API account key** is used as the value of the `x-api-key` HTTP header in order to authenticate a REST API request.\n\nBy default, the **API account key** is masked; that is, it is obscured for security reasons.  You can use the **Show** button to display the key and the **Hide** button to mask it.\n\n![The **Show** button displays the account key.](https://redis.io/docs/latest/images/rc/button-access-management-show-key.png#no-click) &nbsp; ![The **Hide** button masks the account key.](https://redis.io/docs/latest/images/rc/button-access-management-hide-key.png#no-click)\n\nThe **Copy** button copies the account key to the Clipboard.\n\n![The **Copy** button copies the account key to the Clipboard.](https://redis.io/docs/latest/images/rc/button-copy.png)"},{"id":"api-user-keys","title":"API user keys","role":"content","text":"**API user keys** (also known as _secret keys_) are used as the value of the `x-api-secret-key` HTTP header used to authenticate a REST API request.\n\nIn this context, _user_ refers to the account used to sign in to the Redis Cloud console.  Users must have an owner (read-write), billing admin (read-write for billing tasks), viewer (read-only), or logs viewer (read-only for logs).\n\nUsers can have more than one user key; however, users should not share user keys."},{"id":"secret","title":"Create a new user key","role":"content","text":"Use the **Add** button to create a new user key.\n\n![Use the **Add** button to begin creating a new user key.](https://redis.io/docs/latest/images/rc/icon-add.png)\n\nWhen you do this, you're prompted for the **Key name** and the associated **User name**.\n\n![When you add a user key, you're prompted to specify the name of the key and the asscoiated user.](https://redis.io/docs/latest/images/rc/access-management-user-key-add.png)\n\nThe key name:\n\n- Must be between 10 and 50 characters long\n- Can contain alphanumeric characters, hyphens, and underscores. Spaces are not allowed.\n\nThe selected user must have an owner, viewer, billing admin, or logs viewer role.\n\nSelect **Create** to create the new key.  \n\n![Use the **Create** button to create the new user key.](https://redis.io/docs/latest/images/rc/button-access-management-user-key-create.png)\n\nWhen you do this, the **API user key** dialog appears.\n\n![The **API user key** dialog lets you copy the value of the new key to the Clipboard.](https://redis.io/docs/latest/images/rc/access-management-create-user-key.png)\n\n> [!WARNING]\n> This is the only time the value of the user key is available.  Save it to a secure location before closing the dialog box.  <br/><br/>\n> If you lose the user key value, it cannot be retrieved.  If this happens, create a new key to replace the lost one.\n\nThe **Finish** button is inactive until you copy the key to the clipboard. After you save the user key, you can also select **Copy account key** to save the account key."},{"id":"delete-a-user-key","title":"Delete a user key","role":"content","text":"To delete a user key:\n\n1.  Use the **API Keys** tab of the **Access Management** screen to locate the target key. Hover over the key to display the **Delete** button.\n\n    ![The **Delete** button appears to the right of the selected user key.](https://redis.io/docs/latest/images/rc/access-management-api-user-key-delete.png)\n\n2.  Select the **Delete** button.\n\n    ![Select the **Delete** button to begin deleting the selected user key.](https://redis.io/docs/latest/images/rc/icon-delete-teal.png)\n\n3.  This displays the **Delete API secret key** dialog box.  \n\n    ![The **Delete** button appears to the right of the selected user key.](https://redis.io/docs/latest/images/rc/access-management-delete-api-secret-key.png)\n\n    Select the **Delete** button to confirm."},{"id":"manage-cidr-allow-list","title":"Manage CIDR allow list","role":"content","text":"By default, REST API requests are allowed from all IP addresses.  To limit access to specific addresses, define a CIDR allow list for the user key.\n\nTo manage the CIDR allow list:\n\n1.  Use the **API Keys** tab of the **Access Management** screen to locate the target key. Hover over the key to display the **Manage** link.\n\n    ![The **Manage** link appears to the right of the user name for the selected user key.](https://redis.io/docs/latest/images/rc/access-management-api-user-key-delete.png)\n\n2.  Select the **Manage** link in the **CIDR allow list** column; this displays the **Manage CIDR allow list** dialog box.\n\n    ![Select the **Manage** link to define the **CIDR allow list** dialog.](https://redis.io/docs/latest/images/rc/access-management-user-key-manage-cidr.png)\n\n3.  Enter each allowed IP address in [CIDR format](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing#CIDR_notation) (example: `127.1.0.0/24`) and then select the **Save** button.\n\n    ![Use the **Save** button to save a CIDR allow list rule.](https://redis.io/docs/latest/images/rc/button-access-management-cidr-rule-save.png)\n\n    Use the **Add CIDR rule** button to add additional addresses to the list.\n\n    ![Use the **Add Rule** button to add a new address to the CIDR allow list.](https://redis.io/docs/latest/images/rc/button-access-management-cidr-rule-add.png)\n\n    Use the **Edit** button to change the address for a rule or the **Delete button** to remove a rule.\n\n    ![Use the **Edit** button to change the address for a CIDR allow list rule.](https://redis.io/docs/latest/images/rc/icon-edit.png#no-click) &nbsp; ![Use the **Delete** button to remove an address from the CIDR allow list.](https://redis.io/docs/latest/images/rc/icon-delete-teal.png#no-click)"}],"examples":[]}
{"schema_version":2,"id":"operate/rc/api/get-started/manage-tasks","title":"Manage API tasks","url":"https://redis.io/docs/latest/operate/rc/api/get-started/manage-tasks/","summary":"A task is an API operation that is performed asynchronously because it exceeds the time allowed for the synchronous request/response model.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T14:59:52+01:00","page_type":"content","content_hash":"14d35840d8454694510b4922adcb4d2520f9d7e6d62bfc34bc6b6a247a982eb0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Examples of API operations that use tasks are:\n\n- create subscription\n- create database\n- update database\n- delete database\n\nAll create, update, and delete API operations (`POST`, `PUT`, and `DELETE`) and some query operations (`GET`) use tasks.\n\nAfter you request an asynchronous operation, the operation returns a `taskId` that identities the specific task, and contains contextual and status data on the API operation performed by the task.\n\nTasks are part of the API [processing and provisioning lifecycle](https://redis.io/docs/latest/operate/rc/api/get-started/process-lifecycle)."},{"id":"task-information","title":"Task information","role":"content","text":"When you request an asynchronous operation, the response to the request includes the task status and additional information about the task:\n\n[code example]\n\nWhere:\n\n- `taskId` - The unique identifier (UUID) of the specific task\n- `commandType` - The request (command) type\n- `status` - The [status](https://redis.io/docs/latest/operate/rc/api/get-started/process-lifecycle#task-process-states) of the task\n- `description` - A description of the status\n- `timestamp` - The time of the response in ISO-8601 date format and in the UTC timezone\n- `_links` - URI links to resources related to the task including:\n    - A link to itself\n    - Additional links based on the context of the response"},{"id":"task-status-updates","title":"Task status updates","role":"content","text":"With the task ID, you can query the task status for updates and progress information.\nThe response in the above example shows a URL with the title `getTaskStatusUpdates`.\nThe URL in the `href` property returns updates for the specified task.\n\nUse [`GET /v1/tasks/{taskId}`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Tasks/operation/getTaskById) to check the task status.\n\nThis endpoint returns information about the queried task.\n\n[code example]\n\nThis response example shows:\n\n- The `status` value is `\"processing-completed\"`.\n- The `response` field contains the resource identifier of the subscription resource changed by this task.\n- The `links` array contains another `getSubscriptionInformation` URL that links to the newly created subscription.\n    This link queries the subscription status during [provisioning](https://redis.io/docs/latest/operate/rc/api/get-started/process-lifecycle))"},{"id":"tasks-list","title":"Tasks list","role":"content","text":"You can use the API operation [`GET /tasks`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Tasks/operation/getAllTasks) to list the recently submitted and completed tasks for the current account.\n\nThis API operation returns a list of tasks for the current account, sorted by most recent status update.\n\nThe result returns all the tasks submitted during the past 10 days."}],"examples":[{"id":"task-information-ex0","language":"json","code":"{\n  \"taskId\": \"f3ec0e7b-0548-46e3-82f3-1977012ec738\",\n  \"commandType\": \"subscriptionCreateRequest\",\n  \"status\": \"received\",\n  \"description\": \"Task request received and is being queued for processing.\",\n  \"timestamp\": \"2019-08-08T09:07:39.826Z\",\n  \"_links\": {\n    \"task\": {\n      \"href\": \"https://api.redislabs.com/v1/tasks/f3ec0e7b-0548-46e3-82f3-1977012ec738\",\n      \"title\": \"getTaskStatusUpdates\",\n      \"type\": \"GET\"\n    }\n  }\n}","section_id":"task-information"},{"id":"task-status-updates-ex0","language":"json","code":"{\n  \"taskId\": \"36d4b04d-72d4-4404-8600-a223120a553e\",\n  \"commandType\": \"subscriptionCreateRequest\",\n  \"status\": \"processing-completed\",\n  \"description\": \"Request processing completed successfully and its resources are now being provisioned / de-provisioned.\",\n  \"timestamp\": \"2019-08-08T06:49:15.929Z\",\n  \"response\": {\n    \"resourceId\": 77899\n  },\n  \"_links\": {\n    \"resource\": {\n      \"href\": \"https://api.redislabs.com/v1/subscriptions/77899\",\n      \"title\": \"getSubscriptionInformation\",\n      \"type\": \"GET\"\n    },\n    \"self\": {\n      \"href\": \"https://api.redislabs.com/v1/tasks/36d4b04d-72d4-4404-8600-a223120a553e\",\n      \"type\": \"GET\"\n    }\n  }\n}","section_id":"task-status-updates"}]}
{"schema_version":2,"id":"operate/rc/api/get-started/process-lifecycle","title":"The API request lifecycle","url":"https://redis.io/docs/latest/operate/rc/api/get-started/process-lifecycle/","summary":"API requests follow specific lifecycle phases and states, based on the complexity and length of execution of the operation.","tags":["docs","operate","rc"],"last_updated":"2026-10-01T15:21:38+01:00","page_type":"content","content_hash":"6278e65da296f3aee5ed1b811d455bde2b48ad88cc5b5b1966537d165c23fdf3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Cloud owners and viewers can leverage a REST API that permits operations against a variety of resources, including subscriptions, databases, and related infrastructure.\n\n[Once it's enabled](https://redis.io/docs/latest/operate/rc/api/get-started/enable-the-api), you can use the REST API to create, update, and delete subscriptions, databases, and other entities.\n\nAll create, update, and delete API operations (`POST`, `PUT`, and `DELETE`) and some query operations (`GET`) run asynchronously, which means that provisioning or processing occurs in the background.  When you submit a request, a background process, known as a **Task**, starts working on it.  The response object includes an ID that lets you determine the status of the background process as it works.\n\nFor operations that do not create or modify resources or do not require additional processing (such as most GET operations), the API is synchronous; that is, the response object reports the results of the request.\n\nAsynchronous operations have two main phases: processing and provisioning.  A resource is not available until both phases are complete.\n\n![](https://redis.io/docs/latest/images/rv/api/processing-and-provisioning.png)"},{"id":"task-processing","title":"Task processing","role":"content","text":"During this phase, the request is received, evaluated, planned, and executed."},{"id":"use-tasks-to-track-requests","title":"Use tasks to track requests","role":"content","text":"The response objects for all asynchronous operations provide a `taskId` identifier that lets you track the progress of the underlying operation.\n\nYou can query the `taskId` to track the state of a specific task using [`GET /v1/tasks/{taskId}`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Tasks/operation/getTaskById).\n\nYou can also query the state of all active tasks or recently completed tasks in your account using [`GET /tasks`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Tasks/operation/getAllTasks)."},{"id":"task-process-states","title":"Task process states","role":"content","text":"During the processing of a request, the task moves through these states:\n\n- `received` - Request is received and awaits processing.\n- `processing-in-progress` - A dedicated worker is processing the request.\n- `processing-completed` - Request processing succeeded and the request is being provisioned (or de-provisioned, depending on the specific request).\n    A `response` segment is included with the task status JSON response.\n    The response includes a `resourceId` for each resource that the request creates, such as Subscription or Database ID.\n- `processing-error` - Request processing failed.\n    A detailed cause or reason is included in the task status JSON response.\n\n> [!NOTE]\n> A task that reaches the `received` state cannot be cancelled and it will await completion (i.e. processing and provisioning). If you wish to undo an operation that was performed by a task, perform a compensating action (for example: delete a subscription that was created unintentionally)"},{"id":"task-provisioning-phase","title":"Task provisioning phase","role":"content","text":"When the processing phase succeeds and the task is in the `processing-completed` state, the provisioning phase starts.\nDuring the provisioning phase, the API orchestrates all of the infrastructure, resources, and dependencies required by the request.\n\n> [!NOTE]\n> The term \"provisioning\" refers to all infrastructure changes required in order to apply the request. This includes provisioning new or additional infrastructure.\n\nThe provisioning phase may require several minutes to complete. You can query the resource identifier to track the progress of the provisioning phase.\n\nFor example, when you provision a new subscription, use [`GET /v1/subscriptions/{subscriptionId}`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Subscriptions-Pro/operation/getSubscriptionById), where the `{subscriptionId}` is the resource ID that you receive when the task is in the `processing-completed` state."},{"id":"provisioning-state-values","title":"Provisioning state values","role":"content","text":"During the provisioning of a resource (such as a subscription, database, or cloud account) the resource transitions through these states:\n\n- `pending` - Provisioning is in progress.\n- `active` - Provisioning completed successfully.\n- `deleting` - De-provisioning and deletion is in progress.\n- `error` - An error occurred during the provisioning phase, including the details of the error."},{"id":"process-limitations","title":"Process limitations","role":"content","text":"The following limitations apply to asynchronous operations:\n\n- For each account, only one operation is **processed** concurrently. When multiple tasks are sent for the same account, they will be received and processed one after the other.\n- The provisioning phase can be performed in parallel except:\n    - Subscription creation, update, and deletion: You cannot change (make non-active) more than three subscriptions at the same time.\n    - Database creation in an existing subscription: This can cause the subscription state to change from `active` to `pending`\n    during  database provisioning in cases such as database sizing that requires cluster resizing or updating cluster metadata.\n\n- For example:\n    - Concurrently sending multiple \"create database\" tasks will cause each task to be in the `received` state, awaiting processing.\n    - When the first task starts processing it will be moved to the `processing-in-progress` state.\n    - When that first task is completed (either `processing-completed` or `processing-error`), the second task will start processing, and so on.\n    - Typically, the processing phase is much faster than the provisioning phase, and multiple tasks will be in provisioned concurrently.\n    - If the creation of the database requires an update to the subscription, the subscription state is set to `pending`.\n    When you create multiple databases one after the other, we recommend that you check the subscription state after the processing phase of each database create request.\n    If the subscription is in `pending` state you must wait for the subscription changes to complete and the subscription state to return to `active`."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/api/get-started/use-rest-api","title":"Use the REST API","url":"https://redis.io/docs/latest/operate/rc/api/get-started/use-rest-api/","summary":"How to use the API with various tools (especially `cURL`)","tags":["docs","operate","rc"],"last_updated":"2026-10-01T15:21:38+01:00","page_type":"content","content_hash":"1e8b4160fceb26b9f1b7b8e9dcb655fc754df3cb223a0acffbfbe1db5f0d1d05","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can access and use the API endpoint URI ([`https://api.redislabs.com/v1`](https://api.redislabs.com/v1/)) with any of the following tools:\n- The Swagger user interface\n- The cURL HTTP client\n- An HTTP client in any programming language\n\n> [!NOTE]\n> For the Redis Cloud API, there is a request limit of 400 requests per minute per Account API key. If you go past this limit, your API requests will fail."},{"id":"swagger-user-interface","title":"Swagger user interface","role":"content","text":"The [Swagger UI](https://api.redislabs.com/v1/swagger-ui.html) is useful for initial introduction and for learning about API operations, models, and simulated usage."},{"id":"authenticate-to-swagger","title":"Authenticate to Swagger","role":"security","text":"To authenticate to the Swagger UI:\n\n1. Open the [Swagger UI](https://api.redislabs.com/v1/swagger-ui.html) page in a browser.\n\n    ![](https://redis.io/docs/latest/images/rv/api/swagger-authorize-and-try-now.png)\n\n1. Select `Authorize`.\n\n    The **Available Authorizations** box is shown with the headers and values that are used for authentication in all API interactions with Swagger.\n\n    ![](https://redis.io/docs/latest/images/rv/api/swagger-authorizations.png)\n\n1. Insert the API Key values:\n\n    1. Enter the [Account Key](https://redis.io/docs/latest/operate/rc/api/get-started/enable-the-api) as the `x-api-key` value and then choose **Authorize**.\n    1. Enter the [Secret Key](https://redis.io/docs/latest/operate/rc/api/get-started/manage-api-keys) as the `x-api-secret-key` value and then choose **Authorize**.\n    1. Select **Close**.\n\n> [!NOTE]\n> The key values are not saved when you refresh the page.\n\nWhen authorization is successful, the lock icon displays a closed lock.\n\n![](https://redis.io/docs/latest/images/rv/api/swagger-closed-lock.png)"},{"id":"make-api-requests","title":"Make API requests","role":"content","text":"After you complete the authorization in the Swagger UI, you can make an API request:\n\n1. Open an action category and select an API operation.\n\n    For example, in the `Account` category select the `GET /payment-methods` operation.\n\n    ![](https://redis.io/docs/latest/images/rv/api/swagger-payment-methods-try-it-now.png)\n\n1. Select **Try it out** and then select **Execute**.\n\n    The API response is shown in the **Responses** section of the API operation.\n    The results include an example of how to execute the same operation in a standard command-line utility using `cURL`.\n\n    ![](https://redis.io/docs/latest/images/rv/api/swagger-query-results.png)\n\n#### Inputs for operations in Swagger\n\nSome API operations require input, such as:\n\n- **Parameters** - When an API operation requires URI parameters, such as \"get subscription by subscription id\", you can enter the values for the parameters.\n\n    ![](https://redis.io/docs/latest/images/rv/api/swagger-parameters.png)\n\n- **JSON Request Body** - For API operations that require a JSON request body, you can either:\n\n    - Use the **model display** to write the request based on the expected JSON structure and parameters.\n\n        ![](https://redis.io/docs/latest/images/rv/api/swagger-post-body-model.png)\n\n    - Use the **Try it now** sample JSON created by Swagger as a base template that you can edit and execute.\n\n        ![](https://redis.io/docs/latest/images/rv/api/swagger-post-edit-body.png)\n\n> [!WARNING]\n> The Swagger UI generates default JSON examples for `POST` and `PUT` operations. You should modify these examples to suit your specific needs and account settings. The examples will fail if used as-is. <br/><br/>For more examples showing how to use specific endpoints, see [REST API examples](https://redis.io/docs/latest/operate/rc/api/examples)."},{"id":"use-the-curl-http-client","title":"Use the `cURL` HTTP client","role":"content","text":"`cURL` is a popular command line tool used to perform HTTP requests,\neither as individual commands or within shell scripts (such as bash and zsh).\nFor an introduction, see [How to start using cURL and why: a hands-on introduction](https://www.freecodecamp.org/news/how-to-start-using-curl-and-why-a-hands-on-introduction-ea1c913caaaa/).\n\n> [!INFO]\n> Our examples use `cURL` and Linux shell scripts to demonstrate the API; you can use any standard REST client or library.<br/><br/>\n> Our examples also use `jq`, [a JSON parser](https://stedolan.github.io/jq/).  Use your package manager to install it  (Example: `sudo apt install jq`)\n\nFor example, a standard API call to get System Log information looks like this in `cURL`:\n\n[code example]\n\n- The example expects several variables to be set in the Linux shell:\n\n    - **$HOST** - The URI of the REST API host (`api.redislabs.com/v1`)\n    - **$ACCOUNT_KEY** - The [account key value](https://redis.io/docs/latest/operate/rc/api/get-started/enable-the-api)\n    - **$SECRET_KEY** - The personal [secret key value](https://redis.io/docs/latest/operate/rc/api/get-started/manage-api-keys)\n\n- The line \"`| jq -r .`\" means that the HTTP response will be piped (forwarded) to the `jq` JSON parser, and it will display only the raw output (\"`-r`\") of the root element (\"`.`\")\n- You can set the variables using shell commands like the following:\n\n"}],"examples":[{"id":"use-the-curl-http-client-ex0","language":"bash","code":"curl -s -X GET \"https://$HOST/logs\" \\\n    -H \"accept: application/json\" \\\n    -H \"x-api-key: $ACCOUNT_KEY\" \\\n    -H \"x-api-secret-key: $SECRET_KEY\" \\\n    | jq -r .","section_id":"use-the-curl-http-client"}]}
{"schema_version":2,"id":"operate/rc/api","title":"Redis Cloud REST API","url":"https://redis.io/docs/latest/operate/rc/api/","summary":"Describes the Redis Cloud REST API and links to additional info","tags":["docs","operate","rc"],"last_updated":"2026-09-25T14:59:52+01:00","children":[{"id":"operate/rc/api/get-started","summary":"Describes how Redis Cloud REST API uses keys to authenticate and authorize access.","title":"Get started with the REST API","url":"https://redis.io/docs/latest/operate/rc/api/get-started/"},{"id":"operate/rc/api/examples","summary":"These examples show how to use the API and the results to expect.","title":"REST API Examples","url":"https://redis.io/docs/latest/operate/rc/api/examples/"},{"id":"operate/rc/api/api-reference","summary":"","title":"Redis Cloud API","url":"https://redis.io/docs/latest/operate/rc/api/api-reference/"}],"page_type":"content","content_hash":"b93b922c807b362f2bc59601eea64f19f6cecf6bc4f670bed8cad52e7f3c58d6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The [Redis Cloud REST API](https://redis.io/docs/latest/operate/rc/api/api-reference) helps you manage your Redis Cloud databases programmatically.\n\nYou can use the API to:\n \n- Create or manage databases  \n- Define or change hosting credentials    \n- Audit access via logs  \n- Backup or import databases"},{"id":"get-started","title":"Get started","role":"content","text":"1. [Enable the API](https://redis.io/docs/latest/operate/rc/api/get-started/enable-the-api)\n1. [Authenticate and authorize](https://redis.io/docs/latest/operate/rc/api/get-started)\n1. [Create API keys](https://redis.io/docs/latest/operate/rc/api/get-started/manage-api-keys)\n1. [Use the API](https://redis.io/docs/latest/operate/rc/api/get-started/use-rest-api)\n1. [Learn the API lifecycle](https://redis.io/docs/latest/operate/rc/api/get-started/process-lifecycle)\n1. [Create and manage subscriptions](https://redis.io/docs/latest/operate/rc/api/examples/manage-subscriptions)"},{"id":"examples","title":"Examples","role":"example","text":"1. [Manage subscriptions](https://redis.io/docs/latest/operate/rc/api/examples/manage-subscriptions)\n1. Database examples\n    - [Create and manage databases](https://redis.io/docs/latest/operate/rc/api/examples/create-database)\n    - [Back up and import data](https://redis.io/docs/latest/operate/rc/api/examples/back-up-and-import-data)\n1. [Estimate costs](https://redis.io/docs/latest/operate/rc/api/examples/dryrun-cost-estimates)"},{"id":"more-info","title":"More info","role":"content","text":"- Use the [Redis Cloud API](https://redis.io/docs/latest/operate/rc/api/get-started/use-rest-api)\n- [Full API Reference](https://redis.io/docs/latest/operate/rc/api/api-reference)\n- Secure [authentication and authorization](https://redis.io/docs/latest/operate/rc/api/get-started)"}],"examples":[]}
{"schema_version":2,"id":"operate/rc/billing-and-payments/cost-report","title":"Download and analyze cost report","url":"https://redis.io/docs/latest/operate/rc/billing-and-payments/cost-report/","summary":"Learn how to download and use the cost report for your Redis Cloud account.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T14:59:36+01:00","page_type":"content","content_hash":"a24f37ea51071f887f19b969968b856e58fadc5e8e65576525ffeee5d710a2db","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The Redis Cloud cost report gives you a detailed breakdown of your Redis Cloud subscription usage and associated charges. You can use it to track, audit, and optimize your Redis Cloud spending across Essentials and Pro subscription plans.\n\nYou can download the cost report from the [**Billing and payments**](https://redis.io/docs/latest/operate/rc/billing-and-payments) and [**Usage reports**](https://redis.io/docs/latest/operate/rc/logs-reports/usage-reports) pages. You can also use the [Redis Cloud REST API](#rest-api) to get a cost report in [FOCUS](https://focus.finops.org/) format.\n\nSelect the **Cost report** button to download a report of all shard costs in CSV format.\n\n![The cost report download button.](https://redis.io/docs/latest/images/rc/button-cost-report-download.png)\n\nThe report gives a breakdown of all your Redis billing unit costs, per subscription and database, by month. Subscriptions under the minimum subscription price are broken down into actual billing unit prices and the difference from minimum price.\n\nThe cost report helps you with:\n\n-   Internal cost visibility\n-   Usage trend analysis\n-   Budget forecasting\n-   Troubleshooting unexpected charges\n\n> [!NOTE]\n> Consider the following limitations when you view the cost report:\n>\n> -   All pricing is based on list price. Account-specific discounts don't appear in the cost report.\n> -   Network costs for Pro subscriptions aren't available in the cost report before November 2025.\n> -   Network costs for Pro subscriptions appear for each subscription. Network costs for individual databases aren't available.\n> -   Monthly network costs for Pro subscriptions take up to 72 hours to appear in the cost report."},{"id":"structure","title":"Structure","role":"content","text":"The cost report CSV file has two sections: one for Redis Cloud Essentials subscriptions and one for Redis Cloud Pro subscriptions.\n\nFor more information about how to use the cost report effectively, see [How to download and visualize the cost report](https://support.redislabs.com/hc/en-us/articles/30042563097874-How-to-Download-and-Visualize-Redis-Cloud-Cost-Report) in the Redis knowledge base."},{"id":"essentials-subscriptions","title":"Essentials subscriptions","role":"content","text":"In the Essentials subscription section, each row represents one month of usage for a subscription.\n\n| Column | Description |\n|:-------|:------------|\n| **Start date** | The start date of the usage period |\n| **End date** | The end date of the usage period |\n| **Subscription ID** | The subscription's unique ID |\n| **Subscription name** | The subscription's name |\n| **Plan size** | The size of the [subscription plan](https://redis.io/docs/latest/operate/rc/subscriptions/view-essentials-subscription/essentials-plan-details), in MB |\n| **Plan cost** | The cost of the subscription plan for the usage period |\n\nAll columns after the **Plan cost** column show the key values for any [tags](https://redis.io/docs/latest/operate/rc/databases/tag-database) you added to databases in all subscriptions. If a database has a tag with that key, the cell shows the tag value. You can use this information to filter and analyze your costs by tag.\n\n> [!NOTE]\n> The cost report doesn't show tags for Essentials subscriptions created before January 2024 with multiple databases."},{"id":"pro-subscriptions","title":"Pro subscriptions","role":"content","text":"In the Pro subscription section, each subscription has multiple rows per usage period:\n\n-   Each row represents each database's usage within the subscription over the usage period.\n    -   If you changed a database's configuration, the cost report shows additional rows for usage before and after the change.\n    -   For Active-Active databases, there are multiple rows for each region of the database.\n-   Another row shows the monthly network costs for the subscription after November 2025.\n-   If the Pro subscription doesn't meet the minimum price threshold, the cost report shows the actual cost of the subscription and another row shows the difference from the minimum price.\n\n| Column | Description |\n|:-------|:------------|\n| **Start date** | The start date of the usage period |\n| **End date** | The end date of the usage period |\n| **Subscription ID** | The subscription's unique ID |\n| **Subscription name** | The subscription's name |\n| **Database ID** | The database's unique ID |\n| **Database name** | The database's name |\n| **Region** | The database's region. For Active-Active databases, the cost report shows a row for each region of the database. |\n| **High availability** | Whether the subscription is [highly available](https://redis.io/docs/latest/operate/rc/databases/configuration/high-availability) |\n| **Memory limit** | The database's memory limit, in MB |\n| **Throughput** | The database's throughput, in ops/sec |\n| **RBU type** | The [Redis Billing Unit (RBU)](#billing-unit-types) type of the database. This column shows \"difference from min subscription price\" for the row that shows the difference from the minimum subscription price, and \"Network\" for the row that shows the monthly network costs for the subscription. |\n| **RBU Count** | The number of Redis Billing Units (RBUs) used by the database. |\n| **RBU Price/hr** | The price per hour for the Redis Billing Unit (RBU) type of the database. |\n| **Hours** | The number of hours the database was active during the usage period. |\n| **Total cost** | The total cost of the database or network costs for the usage period |\n\nAll columns after the **Total cost** column show the key values for any [tags](https://redis.io/docs/latest/operate/rc/databases/tag-database) you added to databases in all subscriptions. If a database has a tag with that key, the column shows the value for that tag. You can use this information to filter and analyze your costs by tag.\n\n#### Billing unit types\n\nThe Redis Billing Unit types associated with your Pro subscription depend on your database memory size and throughput requirements.\n\n| Billing unit type | Capacity (Memory/Throughput) |\n|:------------|:----------|\n| Pico | 100MB / 100 ops/sec |\n| Nano | 500MB / 500 ops/sec |\n| Micro | 1GB / 1K ops/sec |\n| High-throughput | 2.5GB / 25K ops/sec |\n| Small | 12.5GB / 12.5K ops/sec |\n| Large | 25GB  / 25K ops/sec |\n| Very large<sup>[1](#table-note-1)</sup> | 50GB / 5K ops/sec |\n| XLarge<sup>[2](#table-note-2)</sup> | 50GB / 10K ops/sec |\n| Flex-10<sup>[3](#table-note-3)</sup> | 50GB / 5K ops/sec |\n| Flex-20<sup>[3](#table-note-3)</sup> | 50GB / 10K ops/sec |\n| Flex-30<sup>[3](#table-note-3)</sup> | 50GB / 15K ops/sec |\n| Flex-40<sup>[3](#table-note-3)</sup> | 50GB / 20K ops/sec |\n| Flex-50<sup>[3](#table-note-3)</sup> | 50GB / 25K ops/sec |\n\n1. <a name=\"table-note-1\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>Used for databases with Auto Tiering before Redis 7.2.\n\n2. <a name=\"table-note-2\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>Used for hosted databases with Auto Tiering between Redis 7.2 and Redis 8.2. \n\n3. <a name=\"table-note-3\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>Used for Redis Flex databases for Redis 8.2 or later. Available Flex billing units depend on the RAM percentage you set for your database.\n\nPrices vary according to the cloud provider and region.  Minimum prices apply."},{"id":"get-focus-format-using-rest-api","title":"Get FOCUS format using REST API","role":"content","text":"You can use the [Redis Cloud REST API](https://redis.io/docs/latest/operate/rc/api) to get a cost report in [FinOps Open Cost and Usage Specification (FOCUS)](https://focus.finops.org/) compatible format.\n\nTo get the cost report using the REST API:\n\n1. Use [`POST /cost-report`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Account/operation/createCostReport) to generate a cost report, with the request body containing the `startDate` and `endDate` for the report as well as any optional filters. Your account must have the **Owner**, **Viewer**, or **Billing admin** role to generate a cost report through this endpoint.\n\n    The response includes a `taskId` that you can use to track the status of the report generation.\n\n1. Use [`GET /tasks/{taskId}`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Tasks/operation/getTaskById) to check report generation status. The report is ready when the `status` is `processing-completed` and the `response` field contains a `costReportId`.\n1. Use [`GET /cost-report/{costReportId}`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Account/operation/getCostReport) to download the generated cost report.\n\nThe cost report returned from the Redis Cloud REST API contains fields from the [FOCUS column library](https://focus.finops.org/focus-columns/). See [Generate FOCUS-compliant cost report with REST API](https://redis.io/docs/latest/operate/rc/api/examples/generate-cost-report) for more details."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/billing-and-payments","title":"Billing & payments","url":"https://redis.io/docs/latest/operate/rc/billing-and-payments/","summary":"Describes how to view billing transactions and manage payment methods for Redis Cloud subscriptions.","tags":["docs","operate","rc"],"last_updated":"2026-10-01T15:21:38+01:00","children":[{"id":"operate/rc/billing-and-payments/cost-report","summary":"Learn how to download and use the cost report for your Redis Cloud account.","title":"Download and analyze cost report","url":"https://redis.io/docs/latest/operate/rc/billing-and-payments/cost-report/"}],"page_type":"content","content_hash":"e224c7663f894f0bb653b8304ab9eb5d0bf979dd7ea92f9becb9dc672acb341d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The **Billing & Payments** screen:\n\n- Shows recent transactions for your account\n- Helps you manage your payment methods\n- Applies coupon credits to your account\n\n![The Billing & Payments screen shows billing transactions and manage payment methods.](https://redis.io/docs/latest/images/rc/billing-billing-history-tab.png)\n\nThe following tabs are available:\n\n- The **Billing History** tab displays recent charges and payments.  Each transaction includes the following details:\n\n    | Detail | Description |\n    |:-------|:------------|\n    | Date   | Date the transaction was recorded |\n    | Description | Description of the transaction |\n    | Reference | Reference number |\n    | Amount    | Transaction amount |\n\n    Billing details may vary between regions.\n\n    You can [download invoices](#download-invoice) on this tab.\n\n    A **Pay Now** button appears in selected regions.\n\n    ![Use the Pay Now button to Pay your invoice in selected regions.](https://redis.io/docs/latest/images/rc/billing-payments-pay-now.png)\n    \n    Select this button to pay your invoice.\n\n- The **Payment Methods** tab lists your current payment methods. Your account uses a single default credit card for all active subscriptions. A **Default** badge identifies that card, but only when your account has more than one stored card; if you have only one card, it's implicitly the default.\n\n    ![The Payments Methods tab helps you manage payments for your subscriptions.](https://redis.io/docs/latest/images/rc/billing-payment-method-tab.png)\n\n    Select **Add credit card** to enter new credit card details, or **Add marketplace account** to add a [Google Cloud Marketplace](https://redis.io/docs/latest/operate/rc/cloud-integrations/gcp-marketplace) or [AWS Marketplace](https://redis.io/docs/latest/operate/rc/cloud-integrations/aws-marketplace) account. See [Set the default credit card](#set-the-default-credit-card) and [Remove a credit card](#remove-a-credit-card).\n\n    > [!NOTE]\n    > If your account has more than one stored credit card, Redis Cloud shows a banner on login and on the Payment Methods page asking you to remove any cards you're not using: \"Please remove any unused cards from your account. You can manage your credit cards anytime from the Billing & Payments page.\" You can dismiss this banner and continue using the console.\n    >\n\n- The **Credits** tab shows coupon credits that have been applied to your account, if any.\n\n    ![The Credits tab lets you apply coupons to your account and shows credits that have already been applied.](https://redis.io/docs/latest/images/rc/billing-payments-credits-tab.png)\n\n    | Detail | Description |\n    |:-------|:------------|\n    | Code   | Coupon code |\n    | Coupon Amount | Amount credited to your account |\n    | Current Balance | Amount left |\n    | Date added | Date applied to your account |\n    | Expiration Date | Date the amount expires |\n\n    You can [apply a coupon](#apply-coupon) on this tab."},{"id":"download-invoice","title":"Download invoice","role":"content","text":"To download an invoice:\n\n1. In the [Redis Cloud console](https://cloud.redis.io/), select **Billing & Payments**.\n\n2.  From the **Billing History** tab, locate and select the invoice.\n\n3.  Select the **Download invoice** icon displayed to the right of the invoice amount.\n\n    ![Use the download icon to download a PDF for the selected invoice.](https://redis.io/docs/latest/images/rc/icon-billing-download.png)\n\nThe invoice is downloaded as an Acrobat PDF file.  Use your browser's download features to manage the file."},{"id":"add-credit-card","title":"Add credit card","role":"content","text":"To add a new credit card:\n\n1. In the [Redis Cloud console](https://cloud.redis.io/), select **Billing & Payments > Payment Methods**.\n\n2.  Select **Add credit card** and add the credit card details.\n\n    If you have not already added a business address to your account, you must enter one. Redis uses your business address for communication, invoicing, and tax purposes.\n\n![The Add credit card screen before a business address has already been added.](https://redis.io/docs/latest/images/rc/billing-add-first-card.png)\n\nIf you already have a business address, you don't need to enter one here. See [Account settings](https://redis.io/docs/latest/operate/rc/accounts/account-settings) to learn how to update your business address.\n\n![The Add credit card screen after a business address has already been added.](https://redis.io/docs/latest/images/rc/billing-add-credit-card.png)\n\nIf the card's billing address is the same as your account's business address, select **Same as business address** to fill the billing address with your business address details. \n\n3.  Select the **Add Credit Card** button to save your changes.\n\n    ![Use the Save Card button to save new payment details.](https://redis.io/docs/latest/images/rc/button-billing-save-card.png)\n\n    > [!NOTE]\n    > If this is the only credit card on your account, Redis Cloud automatically sets it as your default card. While your account has only one stored card, **Add credit card** is unavailable and you can't remove that card; use **Update Card** to change its details instead.\n    >"},{"id":"set-the-default-credit-card","title":"Set the default credit card","role":"content","text":"Your account's default credit card applies to all active subscriptions. To change it:\n\n1. In the [Redis Cloud console](https://cloud.redis.io/), select **Billing & Payments > Payment Methods**.\n\n2.  Locate the card you want to use, then select **Set as Default**.\n\n3.  In the **Change default credit card** dialog, select **Confirm**.\n\n    Changing your default credit card applies that card to all active subscriptions in your account that use a credit card as their payment method.\n\nIf your account has more than one credit card actively used across different subscriptions, an Account Admin or Billing Admin must choose a single default card. Starting October 1, 2026, that admin sees a dialog on login listing each stored card by its last 4 digits, expiration date, and name on card. You must select a card and select **Confirm Default Card** to continue to the console; you can't close or dismiss this dialog without selecting a card.\n\nAfter you confirm a default card, Redis Cloud applies it to all active subscriptions in your account."},{"id":"remove-a-credit-card","title":"Remove a credit card","role":"content","text":"To remove a credit card you no longer use:\n\n1. In the [Redis Cloud console](https://cloud.redis.io/), select **Billing & Payments > Payment Methods**.\n\n2.  Locate the card, then select **Remove**.\n\nYou can't remove your default credit card. If you try, Redis Cloud shows this message: \"You cannot delete your default credit card. Please set another card as default first.\" [Set a different card as default](#set-the-default-credit-card) first, then remove the one you no longer need.\n\nIf your account has only one stored credit card, **Remove** is unavailable, because your account always needs a default credit card."},{"id":"add-marketplace-account","title":"Add marketplace account","role":"content","text":"If you have a [Google Cloud Marketplace](https://redis.io/docs/latest/operate/rc/cloud-integrations/gcp-marketplace) or [AWS Marketplace](https://redis.io/docs/latest/operate/rc/cloud-integrations/aws-marketplace) account already associated with another Redis Account that you own, you can add it to your current account. \n\nTo do this:\n\n1. In the [Redis Cloud console](https://cloud.redis.io/), sign in to the account you would like to associate with your marketplace account. \n\n1. Select **Billing & Payments > Payment Methods**.\n\n1. Select **Add Marketplace account**.\n\n    ![The Add marketplace account button.](https://redis.io/docs/latest/images/rc/add-marketplace-account.png)\n\n1. Select the marketplace account you want to connect and confirm that it will pay for your Redis Cloud resources going forward.\n\n1. Select **Connect account**.\n\n    ![The Connect account button.](https://redis.io/docs/latest/images/rc/button-connect-account.png)\n\nYour marketplace account is now connected to your Redis Cloud account. If you would like to disconnect your marketplace account from your Redis Cloud account, select **Disconnect**."},{"id":"apply-coupon","title":"Apply coupon","role":"content","text":"Coupons apply credits to your Redis Cloud account.  To redeem a coupon:\n\n1. In the [Redis Cloud console](https://cloud.redis.io/), select **Billing & Payments** and then select the **Credits** tab.\n\n2.  Enter the coupon code and then select the **Apply** button.\n\n    ![Use the Apply button to redeem a coupon.](https://redis.io/docs/latest/images/rc/button-billing-payments-apply.png)\n\n    The value of the coupon is applied to your account when accepted.  \n\nFor help, contact [Support](https://redis.io/support/).\n\n> [!NOTE]\n> Generally, charges are non-refundable.\n>\n> For any special circumstances that may warrant a refund, please contact [Support](https://redis.io/support/) and be sure to provide detail about the reasons for the refund request."},{"id":"download-cost-report","title":"Download cost report","role":"content","text":"Select the **Cost report** button to download a report of all shard costs in CSV format.\n\n![The cost report download button.](https://redis.io/docs/latest/images/rc/button-cost-report-download.png)\n\nThe report gives a breakdown of all your Redis billing unit costs, per subscription and database, by month. Subscriptions under the minimum subscription price are broken down into actual billing unit prices and the difference from minimum price.\n\nSee [Cost report](https://redis.io/docs/latest/operate/rc/billing-and-payments/cost-report) and [How to download and visualize the cost report](https://support.redislabs.com/hc/en-us/articles/30042563097874-How-to-Download-and-Visualize-Redis-Cloud-Cost-Report) for more information."},{"id":"troubleshoot","title":"Troubleshoot","role":"errors","text":"See [Billing](https://support.redislabs.com/hc/en-us/categories/26174537325842-Billing) on the Redis knowledge base for help troubleshooting billing issues:\n- [Resolving payment failures in Redis Cloud](https://support.redislabs.com/hc/en-us/articles/26266042933522-Resolving-Payment-Failures-in-Redis-Cloud)\n- [Credit card retry behavior and troubleshooting](https://support.redislabs.com/hc/en-us/articles/27697187187986-Credit-Card-Retry-Behavior-and-Troubleshooting)\n- [Access invoices and payment links](https://support.redislabs.com/hc/en-us/articles/26265945230226-Access-Invoices-and-Payment-Links-in-the-Portal)\n- [How to find payment information](https://support.redislabs.com/hc/en-us/articles/26266104144914-How-to-Find-Payment-Information)"}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/2023/april-2023","title":"Redis Cloud changelog (April 2023)","url":"https://redis.io/docs/latest/operate/rc/changelog/2023/april-2023/","summary":"New features, enhancements, and other changes added to Redis Cloud during April 2023.","aliases":["/operate/rc/changelog/april-2023"],"tags":["docs","operate","rc"],"last_updated":"2025-06-10T14:12:58-04:00","page_type":"content","content_hash":"fe4e02415ea7e411d10f5174621b5fc75fb90185fb052d867fa767b7a3c4890a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This changelog lists new features, enhancements, and other changes added to Redis Cloud during April 2023."},{"id":"new-features-and-enhancements","title":"New features and enhancements","role":"content","text":"- Exposed advanced capabilities when creating a new database\n\n- Bug fixes and enhancements"}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/2023/august-2023","title":"Redis Cloud changelog (August 2023)","url":"https://redis.io/docs/latest/operate/rc/changelog/2023/august-2023/","summary":"New features, enhancements, and other changes added to Redis Cloud during August 2023.","aliases":["/operate/rc/changelog/august-2023"],"tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:13+01:00","page_type":"content","content_hash":"fc7788c6963950309d7c89b154e5db7abc8a7b2a549af42452bebaf2b5921e8c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This changelog lists new features, enhancements, and other changes added to Redis Cloud during August 2023."},{"id":"new-features","title":"New features","role":"content","text":""},{"id":"redis-72-flexible-opt-in","title":"Redis 7.2 flexible opt-in","role":"content","text":"You can opt in to Redis 7.2 on [Flexible subscriptions](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new). Redis 7.2 introduces several changes to existing Redis commands; see the [list of breaking changes](https://redis.io/docs/latest/operate/rc/changelog/2023/june-2023#redis-72-breaking-changes) published in June's changelog for more details."},{"id":"triggers-and-functions-preview","title":"Triggers and functions preview","role":"content","text":"A preview of [triggers and functions](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions) (previously known as RedisGears) is now available in the following regions:\n\n- AWS Asia Pacific - Singapore (`ap-southeast-1`)\n- GCP Asia Pacific - Tokyo (`asia-northeast1`)\n\nTo use it, [create a fixed subscription](https://redis.io/docs/latest/operate/rc/databases/create-database/create-essentials-database) in one of these regions. Then, [create your database](https://redis.io/docs/latest/operate/rc/databases/create-database) and set the database Type to Redis and select Triggers and Functions in the drop-down. Or, set the database Type to Redis Stack to get all of our advanced capabilities.\n\nIf you'd like to use triggers and functions with a [Flexible subscription](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new), contact [support](https://redis.com/company/support/).\n\nFor more information about triggers and functions, see the [triggers and functions documentation](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions).\n\n> [!NOTE]\n> Triggers and functions is discontinued as of [May 2024](https://redis.io/docs/latest/operate/rc/changelog/2024/may-2024)."},{"id":"maintenance-windows","title":"Maintenance windows","role":"content","text":"You can now [set manual maintenance windows](https://redis.io/docs/latest/operate/rc/subscriptions/maintenance/set-maintenance-windows) if you want to control when Redis can perform [maintenance](https://redis.io/docs/latest/operate/rc/subscriptions/maintenance) for a Flexible subscription."},{"id":"known-issues","title":"Known issues","role":"content","text":"#### Invalid ACL rule causes failed state machine\n\nApplying an invalid ACL rule to a database may cause a failed state machine. If this happens, use the [`PUT /v1/acl/redisRules/{aclRedisRuleId}`](https://api.redislabs.com/v1/swagger-ui/index.html#/Access%20Control%20List/updateRedisRule) API call to update the rule, and then delete it if necessary."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/2023/december-2023","title":"Redis Cloud changelog (December 2023)","url":"https://redis.io/docs/latest/operate/rc/changelog/2023/december-2023/","summary":"New features, enhancements, and other changes added to Redis Cloud during December 2023.","aliases":["/operate/rc/changelog/december-2023"],"tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:13+01:00","page_type":"content","content_hash":"e727432e1ac81ae2c90352536136a702b2c43dadf6ad10b644ebe32d4d63c4bb","sections":[{"id":"new-features","title":"New features","role":"overview","text":""},{"id":"active-active-json-support","title":"Active-Active JSON support","role":"content","text":"[Active-Active databases](https://redis.io/docs/latest/operate/rc/databases/active-active) on Redis Cloud now support the [JSON](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json) data type.\n\nSee [Create an Active-Active subscription](https://redis.io/docs/latest/operate/rc/databases/active-active/create-active-active-database) to learn how to create an Active-Active subscription."},{"id":"mutual-tls-enhancements","title":"Mutual TLS enhancements","role":"content","text":"Databases that support [Transport layer security (TLS)](https://redis.io/docs/latest/operate/rc/security/database-security/tls-ssl) now support multiple client certificates for use with mutual TLS. This makes it easier to rotate client certificates outside of a maintenance window. In addition, you can now provide a client Certificate Authority chain to trust any leaf certificate it signed for more flexibility.\n\nSee [Transport layer security (TLS)](https://redis.io/docs/latest/operate/rc/security/database-security/tls-ssl) to learn how to enable TLS."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/2023/february-2023","title":"Redis Cloud changelog (February 2023)","url":"https://redis.io/docs/latest/operate/rc/changelog/2023/february-2023/","summary":"New features, enhancements, and other changes added to Redis Cloud during February 2023.","aliases":["/operate/rc/changelog/february-2023"],"tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:13+01:00","page_type":"content","content_hash":"cf962fd830f32957fca5a71294a746190865717c9c2b3eb6d799b794f8a5c4ad","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This changelog lists new features, enhancements, and other changes added to Redis Cloud during February 2023."},{"id":"new-features-and-enhancements","title":"New features and enhancements","role":"content","text":"- [Private Service Connect](https://redis.io/docs/latest/operate/rc/security/private-service-connect)\n\n- Additional enhancements"}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/2023","title":"Redis Cloud changelog (2023)","url":"https://redis.io/docs/latest/operate/rc/changelog/2023/","summary":"All Redis Cloud changelogs from 2023.","tags":["docs","operate","rc"],"last_updated":"2025-06-10T14:12:58-04:00","children":[{"id":"operate/rc/changelog/2023/december-2023","summary":"New features, enhancements, and other changes added to Redis Cloud during December 2023.","title":"Redis Cloud changelog (December 2023)","url":"https://redis.io/docs/latest/operate/rc/changelog/2023/december-2023/"},{"id":"operate/rc/changelog/2023/november-2023","summary":"New features, enhancements, and other changes added to Redis Cloud during November 2023.","title":"Redis Cloud changelog (November 2023)","url":"https://redis.io/docs/latest/operate/rc/changelog/2023/november-2023/"},{"id":"operate/rc/changelog/2023/october-2023","summary":"New features, enhancements, and other changes added to Redis Cloud during October 2023.","title":"Redis Cloud changelog (October 2023)","url":"https://redis.io/docs/latest/operate/rc/changelog/2023/october-2023/"},{"id":"operate/rc/changelog/2023/september-2023","summary":"New features, enhancements, and other changes added to Redis Cloud during September 2023.","title":"Redis Cloud changelog (September 2023)","url":"https://redis.io/docs/latest/operate/rc/changelog/2023/september-2023/"},{"id":"operate/rc/changelog/2023/august-2023","summary":"New features, enhancements, and other changes added to Redis Cloud during August 2023.","title":"Redis Cloud changelog (August 2023)","url":"https://redis.io/docs/latest/operate/rc/changelog/2023/august-2023/"},{"id":"operate/rc/changelog/2023/july-2023","summary":"New features, enhancements, and other changes added to Redis Cloud during July 2023.","title":"Redis Cloud changelog (July 2023)","url":"https://redis.io/docs/latest/operate/rc/changelog/2023/july-2023/"},{"id":"operate/rc/changelog/2023/june-2023","summary":"New features, enhancements, and other changes added to Redis Cloud during June 2023.","title":"Redis Cloud changelog (June 2023)","url":"https://redis.io/docs/latest/operate/rc/changelog/2023/june-2023/"},{"id":"operate/rc/changelog/2023/may-2023","summary":"New features, enhancements, and other changes added to Redis Cloud during May 2023.","title":"Redis Cloud changelog (May 2023)","url":"https://redis.io/docs/latest/operate/rc/changelog/2023/may-2023/"},{"id":"operate/rc/changelog/2023/april-2023","summary":"New features, enhancements, and other changes added to Redis Cloud during April 2023.","title":"Redis Cloud changelog (April 2023)","url":"https://redis.io/docs/latest/operate/rc/changelog/2023/april-2023/"},{"id":"operate/rc/changelog/2023/march-2023","summary":"New features, enhancements, and other changes added to Redis Cloud during March 2023.","title":"Redis Cloud changelog (March 2023)","url":"https://redis.io/docs/latest/operate/rc/changelog/2023/march-2023/"},{"id":"operate/rc/changelog/2023/february-2023","summary":"New features, enhancements, and other changes added to Redis Cloud during February 2023.","title":"Redis Cloud changelog (February 2023)","url":"https://redis.io/docs/latest/operate/rc/changelog/2023/february-2023/"},{"id":"operate/rc/changelog/2023/january-2023","summary":"New features, enhancements, and other changes added to Redis Cloud during January 2023.","title":"Redis Cloud changelog (January 2023)","url":"https://redis.io/docs/latest/operate/rc/changelog/2023/january-2023/"}],"page_type":"content","content_hash":"1455adde92e1d9a76dbac2437ab4ec6bd3039661d6bc637a72ebb4f518499d02","sections":[{"id":"content","title":"Content","role":"content","text":"Select a month from the following table to see a more detailed changelog for that month:\n\n\n| Month | Major changes |\n| --- | --- |\n| [December 2023](https://redis.io/docs/latest/operate/rc/changelog/2023/december-2023/) | Active-Active JSON support, mTLS enhancements |\n| [November 2023](https://redis.io/docs/latest/operate/rc/changelog/2023/november-2023/) | New Fixed plans, Redis 7.2 Fixed region support |\n| [October 2023](https://redis.io/docs/latest/operate/rc/changelog/2023/october-2023/) | Cost report CSV download, SAML Account linking tokens |\n| [September 2023](https://redis.io/docs/latest/operate/rc/changelog/2023/september-2023/) | RESP protocol selection |\n| [August 2023](https://redis.io/docs/latest/operate/rc/changelog/2023/august-2023/) | Redis 7.2 Opt-in for Flexible subscriptions, Triggers and functions preview |\n| [July 2023](https://redis.io/docs/latest/operate/rc/changelog/2023/july-2023/) | New ACL capabilities |\n| [June 2023](https://redis.io/docs/latest/operate/rc/changelog/2023/june-2023/) | Redis 7.2 opt-in for Fixed subscriptions |\n| [May 2023](https://redis.io/docs/latest/operate/rc/changelog/2023/may-2023/) | Cloud API Fixed plans support, Active-Active ACL roles |\n| [April 2023](https://redis.io/docs/latest/operate/rc/changelog/2023/april-2023/) | Exposed advanced capabilities when creating a new database |\n| [March 2023](https://redis.io/docs/latest/operate/rc/changelog/2023/march-2023/) | Redis 7.0 preview |\n| [February 2023](https://redis.io/docs/latest/operate/rc/changelog/2023/february-2023/) | Private Service Connect |\n| [January 2023](https://redis.io/docs/latest/operate/rc/changelog/2023/january-2023/) | Pub/sub ACLs, multiple availability zones, add users with first and last names |"}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/2023/january-2023","title":"Redis Cloud changelog (January 2023)","url":"https://redis.io/docs/latest/operate/rc/changelog/2023/january-2023/","summary":"New features, enhancements, and other changes added to Redis Cloud during January 2023.","aliases":["/operate/rc/changelog/january-2023"],"tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:13+01:00","page_type":"content","content_hash":"29b22d97fba41ba3ffd26ed2b4b115ab7962a2423db02bba1ec7a2325e87826f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This changelog lists new features, enhancements, and other changes added to Redis Cloud during January 2023."},{"id":"new-features-and-enhancements","title":"New features and enhancements","role":"content","text":"- [Pub/sub ACLs](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/configure-acls#pubsub-acl-rules)\n\n- Add users with first name and last name\n\n- Select multiple [availability zones](https://redis.io/docs/latest/operate/rc/databases/configuration/high-availability#availability-zones)\n\n- Additional enhancements"}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/2023/july-2023","title":"Redis Cloud changelog (July 2023)","url":"https://redis.io/docs/latest/operate/rc/changelog/2023/july-2023/","summary":"New features, enhancements, and other changes added to Redis Cloud during July 2023.","aliases":["/operate/rc/changelog/july-2023"],"tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:13+01:00","page_type":"content","content_hash":"8c143a1f7841c2031881c0c6c9db2a34170247681ff96bf912230162f8d9c33d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This changelog lists new features, enhancements, and other changes added to Redis Cloud during July 2023."},{"id":"new-features","title":"New features","role":"content","text":""},{"id":"new-acl-capabilities","title":"New ACL capabilities","role":"content","text":"You can now use [Selectors](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl#selectors) and [Key Permissions](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl#selectors) in your [ACL rules](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/configure-acls) for all Redis 7.2 databases. \n\nIn addition, the admin console now returns an error if you try to enter an invalid ACL. It will also associate the ACL rule with the supported database version when you [add it to a role](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/create-roles)."},{"id":"deprecations","title":"Deprecations","role":"content","text":"- RedisGraph is no longer available when you select Redis Stack on Fixed. For more information, see the [RedisGraph end of life announcement](https://redis.com/blog/redisgraph-eol/).\n- You are no longer able to [create an ACL rule](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/configure-acls) using the rule builder. Instead, use [ACL syntax](https://redis.io/docs/management/security/acl/) to define your ACL rules."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/2023/june-2023","title":"Redis Cloud changelog (June 2023)","url":"https://redis.io/docs/latest/operate/rc/changelog/2023/june-2023/","summary":"New features, enhancements, and other changes added to Redis Cloud during June 2023.","aliases":["/operate/rc/changelog/june-2023"],"tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:13+01:00","page_type":"content","content_hash":"75ca8b7adf610b5c442e659b0a11906f280e978aaaba64804a5b15632e714b16","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This changelog lists new features, enhancements, and other changes added to Redis Cloud during June 2023."},{"id":"new-features","title":"New features","role":"content","text":""},{"id":"redis-72-fixed-opt-in","title":"Redis 7.2 fixed opt-in","role":"content","text":"You can opt in to Redis 7.2 on [Fixed subscriptions](https://redis.io/docs/latest/operate/rc/databases/create-database/create-essentials-database) in selected regions in AWS and GCP. Redis 7.2 introduces several changes to existing Redis commands; see the [list of breaking changes](#redis-72-breaking-changes) for more details."},{"id":"export-database-list-to-csv-includes-active-active","title":"Export database list to CSV includes Active-Active","role":"content","text":"Your Active-Active databases will now appear in the database list when you export it to a CSV file. To support this, we added a \"Region\" column to differentiate between  Active-Active databases with the same name.\n\nTo export your database list to a CSV file, select **Export all** on the **Databases** screen."},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":""},{"id":"redis-72-breaking-changes","title":"Redis 7.2 breaking changes","role":"content","text":"When new major versions of open source Redis change existing commands, upgrading your database to a new version can potentially break some functionality. Before you upgrade, make sure to read the provided list of breaking changes that affect Redis Enterprise and update any applications that connect to your database to handle these changes.\n\nConfirm your Redis database version (`redis_version`) using the admin console or run the following [`INFO`](https://redis.io/docs/latest/commands/info) command via [`redis-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/redis-cli):\n\n[code example]\n\n#### Breaking changes from version 7.0\n\nUpgrading to Redis version 7.2 from version 7.0 introduces the following potentially breaking changes to Redis Enterprise:\n\n##### Programmability\n\n- Time sampling is now frozen during command execution and scripts ([#10300](https://github.com/redis/redis/pull/10300)). While a command or script is running, the keys used by the command or script will not expire. This breaks any script that uses a loop to wait for a key to expire. \n\n- Blocked commands in scripts now work the same way as when they are used in [transactions](https://redis.io/docs/latest/develop/using-commands/transactions) ([#11568](https://github.com/redis/redis/pull/11568)). \n\n##### Error handling\n\n- A blocked stream command that is released when a key no longer exists returns a different error code ([#11012](https://github.com/redis/redis/pull/11012)).\n    - For newly unblocked streams, lists, and zsets, the old implementation returned `UNBLOCKED` when the stream key was deleted or overwritten with a different type. Now, errors will be the same as if the command was processed after the effect.\n\n- ACL errors have been unified across Redis. ([#11160](https://github.com/redis/redis/pull/11160))\n    - When using [RedisModule_Call](https://redis.io/docs/latest/develop/reference/modules/modules-api-ref#redismodule_call) module API function, ACL errors return `-NOPERM` instead of `-ERR`\n\n- [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) and [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) create a consumer regardless of whether it was able to perform reading or claiming ([#11012](https://github.com/redis/redis/pull/11012)).\n\n- Any float that is Not a Number will return `nan` ([#11597](https://github.com/redis/redis/pull/11597)).\n\n##### ACLs\n\n- When a blocked client is being unblocked, checks for ACLs and OOM condition checks are now re-evaluated ([#11012](https://github.com/redis/redis/pull/11012)).\n    - If the ACL rules have changed since the command was executed, the command might fail after the client is unblocked.\n\n##### Command introspection, stats, and configuration\n\n- When running [`XINFO CONSUMERS`](https://redis.io/docs/latest/commands/xinfo-consumers), the `idle` time now shows the number of milliseconds that have passed since the last attempted interaction, and the `inactive` time shows the number of milliseconds since the last successful interaction ([#11099](https://github.com/redis/redis/pull/11099))\n    - Previously, `idle` time showed the number of milliseconds that passed since the last successful interaction and there was no `inactive` time.\n\n- Command stats are only updated when the command executes ([#11012](https://github.com/redis/redis/pull/11012)).\n    - Previously, the command stats were updated even if a command was blocked. The command stats are now updated only if and when the command is executed.\n\n#### Breaking changes from version 6.2\n\nUpgrading to open source Redis version 7.2 from version 6.2 introduces the following potentially breaking changes to Redis Enterprise.\n\nAll [breaking changes from version 7.0](#breaking-changes-from-version-70) also apply to Redis version 6.2.\n\n##### Programmability\n\n-  Lua scripts no longer have access to the `print()` function ([#10651](https://github.com/redis/redis/pull/10651)) - The `print`  function was removed from Lua because it can potentially cause the Redis processes to get stuck (if no one reads from stdout). Users should use redis.log. An alternative is to override the  `print`  implementation and print the message to the log file.  \n\n- Block [`PFCOUNT`](https://redis.io/docs/latest/commands/pfcount) and [`PUBLISH`](https://redis.io/docs/latest/commands/publish) in read-only scripts (*_RO commands,  `no-writes`) ([#10744](https://github.com/redis/redis/pull/10744)) - Consider `PFCOUNT` and `PUBLISH` as write commands in scripts, in addition to `EVAL`; meaning:\n  - They can never be used in scripts with shebang (`#!`) and no `no-writes` flag\n  - They are blocked in `EVAL_RO` and `_RO` variants, (even in scripts without shebang (`#!`) flags)\n  - Allow `no-write` scripts in EVAL (not just in EVAL_RO), even during `CLIENT PAUSE WRITE` \n\n- Hide the `may_replicate` flag from the [`COMMAND`](https://redis.io/docs/latest/commands/command) command response  ([#10744](https://github.com/redis/redis/pull/10744)) - As part of the change to treat `may_replicate` commands `PFCOUNT` and `PUBLISH` as write commands in scripts, in addition to `EVAL`, the `may_replicate` flag has been removed from the `COMMAND` response.\n\n##### Error handling\n\n- Rephrased some error responses about invalid commands or arguments ([#10612](https://github.com/redis/redis/pull/10612)) - \n  - Error response for unknown command introduced a case change (`Unknown` to `unknown`)\n  - Errors for module commands extended to cover subcommands, updated syntax to match Redis Server syntax\n  - Arity errors for module commands introduce a case change (`Wrong` to `wrong`); will consider full command name\n\n- Corrected error codes returned from [`EVAL`](https://redis.io/docs/latest/commands/eval) scripts ([#10218](https://github.com/redis/redis/pull/10218), [#10329](https://github.com/redis/redis/pull/10329)). \n\n  These examples show changes in behavior:\n\n  [code example]\n\n- [`ZPOPMIN`](https://redis.io/docs/latest/commands/zpopmin)/[`ZPOPMAX`](https://redis.io/docs/latest/commands/zpopmax) used to produce wrong replies when count is 0 with non-zset  [#9711](https://github.com/redis/redis/pull/9711)):\n  -  `ZPOPMIN`/`ZPOPMAX` used to produce an `(empty array)` when `key` was not a sorted set and the optional `count` argument was set to `0` and now produces a `WRONGTYPE` error response instead.\n  -  The optional `count` argument must be positive. A negative value produces a `value is out of range` error.\n\n  These examples show changes in behavior:\n\n  [code example]\n\n- [`LPOP`](https://redis.io/docs/latest/commands/lpop)/[`RPOP`](https://redis.io/docs/latest/commands/rpop) with count against a nonexistent list returns a null array instead of `(nil)`([#10095](https://github.com/redis/redis/pull/10095)). This change was backported to 6.2.\n\n- [`LPOP`](https://redis.io/docs/latest/commands/lpop)/[`RPOP`](https://redis.io/docs/latest/commands/rpop) used to produce `(nil)` when count is 0, now produces a null array ([#9692](https://github.com/redis/redis/pull/9692)). This change was backported to 6.2.\n\n- [`XCLAIM`](https://redis.io/docs/latest/commands/xclaim)/[`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) skips deleted entries instead of replying with `nil` and deletes them from the pending entry list ([#10227](https://github.com/redis/redis/pull/10227)) - `XCLAIM`/`XAUTOCLAIM` now behaves in the following way:\n  - If you try to claim a deleted entry, it is deleted from the pending entry list (PEL) where it is found (as well as the group PEL). Therefore, such an entry is not claimed, just cleared from PEL (because it doesn't exist in the stream anyway).\n  - Because deleted entries are not claimed, `X[AUTO]CLAIM` does not return \"nil\" instead of an entry.\n  - Added an array of all the deleted stream IDs to `XAUTOCLAIM` response.\n\n#####  ACLs\n\n- [`ACL GETUSER`](https://redis.io/docs/latest/commands/acl-getuser) reply now uses ACL syntax for `keys` and `channels` ([#9974](https://github.com/redis/redis/pull/9974)). `ACL GETUSER` now uses the ACL DSL (Domain Specific Language) for keys and channels. \n\n  These examples show changes in behavior:\n  [code example]\n\n- [`SORT`](https://redis.io/docs/latest/commands/sort)/[`SORT_RO`](https://redis.io/docs/latest/commands/sort_ro) commands reject key access patterns in `GET` and `BY` if the ACL doesn't grant the command full keyspace access ([#10340](https://github.com/redis/redis/pull/10340)) - The `sort` and `sort_ro` commands can access external keys via `GET` and `BY`. In order to make sure the user cannot violate the authorization ACL rules, Redis 7 will reject external keys access patterns unless ACL allows `SORT` full access to all keys.\nFor backwards compatibility, `SORT` with `GET`/`BY` keeps working, but if ACL has restrictions to certain keys, the use of these features will result in a permission denied error.\n\n  These examples show changes in behavior:\n\n  [code example]\n\n  [code example]\n\n  [code example]\n\n  [code example]\n\n- Fix ACL category for [`SELECT`](https://redis.io/docs/latest/commands/select), [`WAIT`](https://redis.io/docs/latest/commands/wait, [`ROLE`](https://redis.io/docs/latest/commands/role, [`LASTSAVE`](https://redis.io/docs/latest/commands/lastsave, [`READONLY`](https://redis.io/docs/latest/commands/readonly, [`READWRITE`](https://redis.io/docs/latest/commands/readwrite, [`ASKING`](https://redis.io/docs/latest/commands/asking) ([#9208](https://github.com/redis/redis/pull/9208)):\n  - `SELECT` and `WAIT` have been recategorized from `@keyspace` to `@connection`  \n  - `ROLE`, `LASTSAVE` have been categorized as  `@admin`  and  `@dangerous`\n  -  `ASKING`, `READONLY`, `READWRITE` have also been assigned the  `@connection`  category and  removed from `@keyspace`\n  - Command categories are explained in [ACL documentation](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl#command-categories)\n\n##### Command introspection, stats, and configuration\n\n- [`COMMAND`](https://redis.io/docs/latest/commands/command) reply drops `random` and `sort-for-scripts` flags, which are now part of [command tips](https://redis.io/docs/latest/develop/reference/command-tips) ([#10104](https://github.com/redis/redis/pull/10104)) - The `random` flag was replaced with the `nondeterministic_output` tip; the `sort-for-scripts` flag was replaced by the ` nondeterministic_output_order` tip\n\n- [`INFO`](https://redis.io/docs/latest/commands/info) `commandstats` now shows the stats per sub-command ([#9504](https://github.com/redis/redis/pull/9504))\nFor example, while previous versions would provide a single entry for all command usage, in Redis 7, each sub command is reported separately:\n\n  - Redis 6.2:\n  \n    [code example]\n\n  - Redis 7:\n  \n    [code example]\n\n- [`CONFIG REWRITE`](https://redis.io/docs/latest/commands/config-rewrite), [`CONFIG RESETSTAT`](https://redis.io/docs/latest/commands/config-resetstat, and most [`CONFIG SET`](https://redis.io/docs/latest/commands/config-setcommands are now allowed during loading ([#9878](https://github.com/redis/redis/pull/9878))"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"There are two known limitations for the version of Redis 7.2 that is packaged with Redis Enterprise.\n\n- Using [`XREADGROUP BLOCK`](https://redis.io/docs/latest/commands/xreadgroup) with `>` to return all new streams will cause the Redis database to freeze until the shard is restarted ([#12031](https://github.com/redis/redis/pull/12301))\n\n- Because a rejected command does not record the duration for command stats, an error will appear after it is reprocessed that will cause the Redis database to freeze until the shard is restarted ([#12247](https://github.com/redis/redis/pull/12247))"},{"id":"migration-guides-for-certain-clients","title":"Migration guides for certain clients","role":"content","text":"If you are using [Go-Redis](https://redis.uptrace.dev/) v9 or [Lettuce](https://redis.github.io/lettuce/) v6+, which by default switch to the RESP3 protocol, and you are running Redis Stack commands, please make sure the protocol version is pinned to RESP2 prior to upgrading to Redis version 7.2.\n\n#### Go-Redis\n\nFor applications using Go-Redis v9 and later, ensure that you have Go-Redis v9.0.5 or newer and pin the protocol version as shown below:\n\n[code example]\n\nNo actions are required for applications using Go-Redis v8 or earlier.\n\n#### Lettuce\n\nTo pin the protocol version, use the snippet below:\n\n[code example]\n\nIf you are using [LettuceMod](https://github.com/redis-developer/lettucemod/), you need to upgrade to [v3.6.0](https://github.com/redis-developer/lettucemod/releases/tag/v3.6.0)."},{"id":"deprecations","title":"Deprecations","role":"content","text":"- [`CLUSTER SLOTS`](https://redis.io/docs/latest/commands/cluster-slots) is deprecated as of Redis 7.0\n- [`QUIT`](https://redis.io/docs/latest/commands/quit) is deprecated as of Redis 7.2"}],"examples":[{"id":"redis-72-breaking-changes-ex0","language":"sh","code":"$ redis-cli -p <port> INFO\n\"# Server\nredis_version:7.0.8\n...\"","section_id":"redis-72-breaking-changes"},{"id":"redis-72-breaking-changes-ex1","language":"diff","code":"1: config set maxmemory 1\n    2: +OK\n    3: eval \"return redis.call('set','x','y')\" 0\n  - 4: -ERR Error running script (call to 71e6319f97b0fe8bdfa1c5df3ce4489946dda479): @user_script:1: @user_script: 1: -OOM command not allowed when used memory > 'maxmemory'.\n  + 4: -ERR Error running script (call to 71e6319f97b0fe8bdfa1c5df3ce4489946dda479): @user_script:1: OOM command not allowed when used memory > 'maxmemory'.\n    5: eval \"return redis.pcall('set','x','y')\" 0\n  - 6: -@user_script: 1: -OOM command not allowed when used memory > 'maxmemory'.\n  + 6: -OOM command not allowed when used memory > 'maxmemory'.\n    7: eval \"return redis.call('select',99)\" 0\n    8: -ERR Error running script (call to 4ad5abfc50bbccb484223905f9a16f09cd043ba8): @user_script:1: ERR DB index is out of range\n    9: eval \"return redis.pcall('select',99)\" 0\n   10: -ERR DB index is out of range\n   11: eval_ro \"return redis.call('set','x','y')\" 0\n  -12: -ERR Error running script (call to 71e6319f97b0fe8bdfa1c5df3ce4489946dda479): @user_script:1: @user_script: 1: Write commands are not allowed from read-only scripts.\n  +12: -ERR Error running script (call to 71e6319f97b0fe8bdfa1c5df3ce4489946dda479): @user_script:1: ERR Write commands are not allowed from read-only scripts.\n   13: eval_ro \"return redis.pcall('set','x','y')\" 0\n  -14: -@user_script: 1: Write commands are not allowed from read-only scripts.\n  +14: -ERR Write commands are not allowed from read-only scripts.","section_id":"redis-72-breaking-changes"},{"id":"redis-72-breaking-changes-ex2","language":"diff","code":"1: zadd myzset 1 \"one\"\n    2: (integer) 1\n    3: zadd myzset 2 \"two\"\n    4: (integer) 1\n    5: zadd myzset 3 \"three\"\n    6: (integer) 1\n    7: zpopmin myzset -1\n  - 8: (empty array)\n  + 8: (error) ERR value is out of range, must be positive\n    9: 127.0.0.1:6379> set foo bar\n   10: OK\n   11: zpopmin foo 0\n  -12: (empty array)\n  +12: (error) WRONGTYPE Operation against a key holding the wrong kind of value","section_id":"redis-72-breaking-changes"},{"id":"redis-72-breaking-changes-ex3","language":"diff","code":"1: acl setuser foo off resetchannels &channel1 -@all +get\n    2: OK\n    3: acl getuser foo\n    4: 1) \"flags\"\n    5: 2) 1) \"off\"\n    6: 3) \"passwords\"\n    7: 4) (empty array)\n    8: 5) \"commands\"\n    9: 6) \"-@all +get\"\n   10: 7) \"keys\"\n  -11: 8) (empty array)\n  +11: 8) \"\"\n   12: 9)\"channels\"\n  -13 10) 1) \"channel1\"\n  +13 10) \"&channel1\"","section_id":"redis-72-breaking-changes"},{"id":"redis-72-breaking-changes-ex4","language":"plaintext","code":"USER FOO (+sort ~* ~mylist) \n  #FOO> sort mylist by w* get v*  - is O.K since ~* provides full key access","section_id":"redis-72-breaking-changes"},{"id":"redis-72-breaking-changes-ex5","language":"plaintext","code":"USER FOO (+sort %R~* ~mylist) \n  #FOO> sort mylist by w* get v*  - is O.K since %R~* provides full key READ access**","section_id":"redis-72-breaking-changes"},{"id":"redis-72-breaking-changes-ex6","language":"plaintext","code":"USER FOO (+sort %W~* ~mylist)\n  #FOO> sort mylist by w* get v*  - will now fail since $W~* only provides full key WRITE access","section_id":"redis-72-breaking-changes"},{"id":"redis-72-breaking-changes-ex7","language":"plaintext","code":"USER FOO (+sort ~v* ~mylist)\n  #FOO> sort mylist by w* get v*  - will now fail since ~v* only provides partial key access","section_id":"redis-72-breaking-changes"},{"id":"redis-72-breaking-changes-ex8","language":"plaintext","code":"cmdstat_acl:calls=4,usec=279,usec_per_call=69.75,rejected_calls=0,failed_calls=2","section_id":"redis-72-breaking-changes"},{"id":"redis-72-breaking-changes-ex9","language":"  ","code":"cmdstat_acl|list:calls=1,usec=4994,usec_per_call=4994.00,rejected_calls=0,failed_calls=0\n    cmdstat_acl|setuser:calls=2,usec=16409,usec_per_call=8204.50,rejected_calls=0,failed_calls=0\n    cmdstat_acl|deluser:calls=1,usec=774,usec_per_call=774.00,rejected_calls=0,failed_calls=0\n    cmdstat_acl|getuser:calls=1,usec=6044,usec_per_call=6044.00,rejected_calls=0,failed_calls=0","section_id":"redis-72-breaking-changes"},{"id":"migration-guides-for-certain-clients-ex0","language":"go","code":"client := redis.NewClient(&redis.Options{\n    Addr:     \"<database_endpoint>\",\n    Protocol: 2, // Pin the protocol version\n})","section_id":"migration-guides-for-certain-clients"},{"id":"migration-guides-for-certain-clients-ex1","language":"java","code":"import io.lettuce.core.*;\nimport io.lettuce.core.api.*;\nimport io.lettuce.core.protocol.ProtocolVersion;\n\n// ...\nRedisClient client = RedisClient.create(\"<database_endpoint>\");\nclient.setOptions(ClientOptions.builder()\n        .protocolVersion(ProtocolVersion.RESP2) // Pin the protocol version \t\n        .build());\n// ...","section_id":"migration-guides-for-certain-clients"}]}
{"schema_version":2,"id":"operate/rc/changelog/2023/march-2023","title":"Redis Cloud changelog (March 2023)","url":"https://redis.io/docs/latest/operate/rc/changelog/2023/march-2023/","summary":"New features, enhancements, and other changes added to Redis Cloud during March 2023.","aliases":["/operate/rc/changelog/march-2023"],"tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:13+01:00","page_type":"content","content_hash":"8df3cc90b33631b8d90e7d3517faf419198cc2fdc23df5dd3996bfbb5cc59ddd","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This changelog lists new features, enhancements, and other changes added to Redis Cloud during March 2023."},{"id":"new-features-and-enhancements","title":"New features and enhancements","role":"content","text":""},{"id":"redis-70-preview","title":"Redis 7.0 preview","role":"content","text":"A preview of Redis 7.0 is available for [Fixed subscriptions](https://redis.io/docs/latest/operate/rc/databases/create-database/create-essentials-database) in selected regions in AWS and GCP. However, some Redis 7.0 functionality might not be fully available during preview. Redis 7.0 also introduces several changes to existing Redis commands; see the [list of breaking changes](#redis-70-breaking-changes) for more details.\n\nThe following tables show which new open source Redis 7.0 commands are supported in Redis 7.0 subscriptions.\n\n#### [Cluster management commands](/content/commands?group=cluster)\n\n| <span style=\"min-width: 10em; display: table-cell\">Command</span> | Supported |\n|:--------|:----------|\n| [CLUSTER ADDSLOTSRANGE](https://redis.io/docs/latest/commands/cluster-addslotsrange) | <span title=\"Not supported\">&#x274c; Not supported</span> |\n| [CLUSTER DELSLOTSRANGE](https://redis.io/docs/latest/commands/cluster-delslotsrange) | <span title=\"Not supported\">&#x274c; Not supported</span> |\n| [CLUSTER LINKS](https://redis.io/docs/latest/commands/cluster-links) | <span title=\"Not supported\">&#x274c; Not supported</span> |\n| [CLUSTER SHARDS](https://redis.io/docs/latest/commands/cluster-shards) | <span title=\"Not supported\">&#x274c; Not supported</span> |\n\n#### [Connection management commands](/content/commands?group=connection)\n\n| <span style=\"min-width: 10em; display: table-cell\">Command</span> | Supported |\n|:--------|:----------|\n| [CLIENT NO-EVICT](https://redis.io/docs/latest/commands/client-no-evict) | <span title=\"Not supported\">&#x274c; Not supported</span> |\n\n#### Data type commands\n\n| Data type | Command | Supported |\n|:----------|:--------|:----------|\n| [List](/content/commands?group=list) | [BLMPOP](https://redis.io/docs/latest/commands/blmpop) | <span title=\"Supported\">&#x2705; Supported</span>|\n| [List](/content/commands?group=list) | [LMPOP](https://redis.io/docs/latest/commands/lmpop) | <span title=\"Supported\">&#x2705; Supported</span>|\n| [Set](/content/commands?group=set) | [SINTERCARD](https://redis.io/docs/latest/commands/sintercard) | <span title=\"Supported\">&#x2705; Supported</span>|\n| [Sorted set](/content/commands?group=sorted-set) | [BZMPOP](https://redis.io/docs/latest/commands/bzmpop) | <span title=\"Supported\">&#x2705; Supported</span>|\n| [Sorted set](/content/commands?group=sorted-set) | [ZINTERCARD](https://redis.io/docs/latest/commands/zintercard) | <span title=\"Supported\">&#x2705; Supported</span>|\n| [Sorted set](/content/commands?group=sorted-set) | [BZMPOP](https://redis.io/docs/latest/commands/bzmpop) | <span title=\"Supported\">&#x2705; Supported</span>|\n\n#### [Keys (generic) commands](/content/commands?group=generic)\n\n| <span style=\"min-width: 10em; display: table-cell\">Command</span> | Supported |\n|:--------|:----------|\n| [EXPIRETIME](https://redis.io/docs/latest/commands/expiretime) | <span title=\"Supported\">&#x2705; Supported</span>|\n| [PEXPIRETIME](https://redis.io/docs/latest/commands/pexpiretime) | <span title=\"Supported\">&#x2705; Supported</span>|\n| [SORT_RO](https://redis.io/docs/latest/commands/sort_ro) | <span title=\"Supported\">&#x2705; Supported</span>|\n\n#### [Pub/sub commands](/content/commands?group=pubsub)\n\n| <span style=\"min-width: 10em; display: table-cell\">Command</span> | Supported |\n|:--------|:----------|\n| [PUBSUB SHARDCHANNELS](https://redis.io/docs/latest/commands/pubsub-shardchannels) | <span title=\"Supported\">&#x2705; Supported</span>|\n| [PUBSUB SHARDNUMSUB](https://redis.io/docs/latest/commands/pubsub-shardnumsub) | <span title=\"Supported\">&#x2705; Supported</span>|\n| [SPUBLISH](https://redis.io/docs/latest/commands/spublish) | <span title=\"Not supported\">&#x274c; Not supported</span> |\n| [SSUBSCRIBE](https://redis.io/docs/latest/commands/ssubscribe) | <span title=\"Not supported\">&#x274c; Not supported</span> |\n| [SUNSUBSCRIBE](https://redis.io/docs/latest/commands/sunsubscribe) | <span title=\"Not supported\">&#x274c; Not supported</span> |\n\n#### [Scripting and function commands](/content/commands?group=scripting)\n\n| <span style=\"min-width: 10em; display: table-cell\">Command</span> | Supported |\n|:--------|:----------|\n| [EVAL_RO](https://redis.io/docs/latest/commands/eval_ro) | <span title=\"Not supported\">&#x274c; Not supported</span> |\n| [EVALSHA_RO](https://redis.io/docs/latest/commands/evalsha_ro) | <span title=\"Not supported\">&#x274c; Not supported</span> |\n| [FUNCTION DELETE](https://redis.io/docs/latest/commands/function-delete) | <span title=\"Supported\">&#x2705; Supported</span>|\n| [FUNCTION DUMP](https://redis.io/docs/latest/commands/function-dump) | <span title=\"Supported\">&#x2705; Supported</span>|\n| [FUNCTION FLUSH](https://redis.io/docs/latest/commands/function-flush) | <span title=\"Supported\">&#x2705; Supported</span>|\n| [FUNCTION HELP](https://redis.io/docs/latest/commands/function-help) | <span title=\"Supported\">&#x2705; Supported</span>|\n| [FUNCTION KILL](https://redis.io/docs/latest/commands/function-kill) | <span title=\"Supported\">&#x2705; Supported</span>|\n| [FUNCTION LIST](https://redis.io/docs/latest/commands/function-list) | <span title=\"Supported\">&#x2705; Supported</span>|\n| [FUNCTION LOAD](https://redis.io/docs/latest/commands/function-load) | <span title=\"Supported\">&#x2705; Supported</span>|\n| [FUNCTION RESTORE](https://redis.io/docs/latest/commands/function-restore) | <span title=\"Supported\">&#x2705; Supported</span>|\n| [FUNCTION STATS](https://redis.io/docs/latest/commands/function-stats) | <span title=\"Not supported\">&#x274c; Not supported</span> |\n\n#### [Server management commands](/content/commands?group=server)\n\n| <span style=\"min-width: 10em; display: table-cell\">Command</span> | Supported |\n|:--------|:----------|\n| [ACL DRYRUN](https://redis.io/docs/latest/commands/acl-dryrun) | <span title=\"Not supported\">&#x274c; Not supported</span> |\n| [COMMAND DOCS](https://redis.io/docs/latest/commands/command-docs) | <span title=\"Supported\">&#x2705; Supported</span>|\n| [COMMAND GETKEYSANDFLAGS](https://redis.io/docs/latest/commands/command-getkeysandflags) | <span title=\"Supported\">&#x2705; Supported</span>|\n| [COMMAND LIST](https://redis.io/docs/latest/commands/command-list) | <span title=\"Supported\">&#x2705; Supported</span>|\n| [MODULE LOADEX](https://redis.io/docs/latest/commands/module-loadex) | <span title=\"Not supported\">&#x274c; Not supported</span> |\n| [LATENCY HISTOGRAM](https://redis.io/docs/latest/commands/latency-histogram) | <span title=\"Not supported\">&#x274c; Not supported</span> |"},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":""},{"id":"redis-70-breaking-changes","title":"Redis 7.0 breaking changes","role":"content","text":"When new major versions of open source Redis change existing commands, upgrading your database to a new version can potentially break some functionality. Before you upgrade, make sure to read the provided list of breaking changes that affect Redis Enterprise and update any applications that connect to your database to handle these changes.\n\nTo check your Redis database version (`redis_version`), you can use the admin console or run the [`INFO`](https://redis.io/docs/latest/commands/info) command with [`redis-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/redis-cli):\n\n[code example]\n\nOpen source Redis version 7.0 introduces the following potentially breaking changes to Redis Enterprise:\n\n#### Programmability\n\n-  Lua scripts no longer have access to the `print()` function ([#10651](https://github.com/redis/redis/pull/10651)) - The `print`  function was removed from Lua because it can potentially cause the Redis processes to get stuck (if no one reads from stdout). Users should use redis.log. An alternative is to override the  `print`  implementation and print the message to the log file.  \n\n- Block [`PFCOUNT`](https://redis.io/docs/latest/commands/pfcount) and [`PUBLISH`](https://redis.io/docs/latest/commands/publish) in read-only scripts (*_RO commands,  `no-writes`) ([#10744](https://github.com/redis/redis/pull/10744)) - Consider `PFCOUNT` and `PUBLISH` as write commands in scripts, in addition to `EVAL`; meaning:\n  - They can never be used in scripts with shebang (`#!`) and no `no-writes` flag\n  - They are blocked in `EVAL_RO` and `_RO` variants, (even in scripts without shebang (`#!`) flags)\n  - Allow `no-write` scripts in EVAL (not just in EVAL_RO), even during `CLIENT PAUSE WRITE` \n\n- Hide the `may_replicate` flag from the [`COMMAND`](https://redis.io/docs/latest/commands/command) command response  ([#10744](https://github.com/redis/redis/pull/10744)) - As part of the change to treat `may_replicate` commands `PFCOUNT` and `PUBLISH` as write commands in scripts, in addition to `EVAL`, the `may_replicate` flag has been removed from the `COMMAND` response.\n\n#### Error handling\n\n- Rephrased some error responses about invalid commands or arguments ([#10612](https://github.com/redis/redis/pull/10612)) - \n  - Error response for unknown command introduced a case change (`Unknown` to `unknown`)\n  - Errors for module commands extended to cover subcommands, updated syntax to match Redis Server syntax\n  - Arity errors for module commands introduce a case change (`Wrong` to `wrong`); will consider full command name\n\n- Corrected error codes returned from [`EVAL`](https://redis.io/docs/latest/commands/eval) scripts ([#10218](https://github.com/redis/redis/pull/10218), [#10329](https://github.com/redis/redis/pull/10329)). \n\n  These examples show changes in behavior:\n\n  [code example]\n\n- [`ZPOPMIN`](https://redis.io/docs/latest/commands/zpopmin)/[`ZPOPMAX`](https://redis.io/docs/latest/commands/zpopmax) used to produce wrong replies when count is 0 with non-zset  [#9711](https://github.com/redis/redis/pull/9711)):\n  -  `ZPOPMIN`/`ZPOPMAX` used to produce an `(empty array)` when `key` was not a sorted set and the optional `count` argument was set to `0` and now produces a `WRONGTYPE` error response instead.\n  -  The optional `count` argument must be positive. A negative value produces a `value is out of range` error.\n\n  These examples show changes in behavior:\n\n  [code example]\n\n- [`LPOP`](https://redis.io/docs/latest/commands/lpop)/[`RPOP`](https://redis.io/docs/latest/commands/rpop) with count against a nonexistent list returns a null array instead of `(nil)`([#10095](https://github.com/redis/redis/pull/10095)). This change was backported to 6.2.\n\n- [`LPOP`](https://redis.io/docs/latest/commands/lpop)/[`RPOP`](https://redis.io/docs/latest/commands/rpop) used to produce `(nil)` when count is 0, now produces a null array ([#9692](https://github.com/redis/redis/pull/9692)). This change was backported to 6.2.\n\n- [`XCLAIM`](https://redis.io/docs/latest/commands/xclaim)/[`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) skips deleted entries instead of replying with `nil` and deletes them from the pending entry list ([#10227](https://github.com/redis/redis/pull/10227)) - `XCLAIM`/`XAUTOCLAIM` now behaves in the following way:\n  - If you try to claim a deleted entry, it is deleted from the pending entry list (PEL) where it is found (as well as the group PEL). Therefore, such an entry is not claimed, just cleared from PEL (because it doesn't exist in the stream anyway).\n  - Because deleted entries are not claimed, `X[AUTO]CLAIM` does not return \"nil\" instead of an entry.\n  - Added an array of all the deleted stream IDs to `XAUTOCLAIM` response.\n\n####  ACLs\n\n- [`ACL GETUSER`](https://redis.io/docs/latest/commands/acl-getuser) reply now uses ACL syntax for `keys` and `channels` ([#9974](https://github.com/redis/redis/pull/9974)). `ACL GETUSER` now uses the ACL DSL (Domain Specific Language) for keys and channels. \n\n  These examples show changes in behavior:\n  [code example]\n\n- [`SORT`](https://redis.io/docs/latest/commands/sort)/[`SORT_RO`](https://redis.io/docs/latest/commands/sort_ro) commands reject key access patterns in `GET` and `BY` if the ACL doesn't grant the command full keyspace access ([#10340](https://github.com/redis/redis/pull/10340)) - The `sort` and `sort_ro` commands can access external keys via `GET` and `BY`. In order to make sure the user cannot violate the authorization ACL rules, Redis 7 will reject external keys access patterns unless ACL allows `SORT` full access to all keys.\nFor backwards compatibility, `SORT` with `GET`/`BY` keeps working, but if ACL has restrictions to certain keys, the use of these features will result in a permission denied error.\n\n  These examples show changes in behavior:\n\n  [code example]\n\n  [code example]\n\n  [code example]\n\n  [code example]\n\n- Fix ACL category for [`SELECT`](https://redis.io/docs/latest/commands/select), [`WAIT`](https://redis.io/docs/latest/commands/wait, [`ROLE`](https://redis.io/docs/latest/commands/role) [`LASTSAVE`](https://redis.io/docs/latest/commands/lastsave, [`READONLY`](https://redis.io/docs/latest/commands/readonly, [`READWRITE`](https://redis.io/docs/latest/commands/readwrite, [`ASKING`](https://redis.io/docs/latest/commands/asking)  ([#9208](https://github.com/redis/redis/pull/9208)):\n  - `SELECT` and `WAIT` have been recategorized from `@keyspace` to `@connection`  \n  - `ROLE`, `LASTSAVE` have been categorized as  `@admin`  and  `@dangerous`\n  -  `ASKING`, `READONLY`, `READWRITE` have also been assigned the  `@connection`  category and  removed from `@keyspace`\n  - Command categories are explained in [ACL documentation](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl#command-categories)\n\n#### Command introspection, stats, and configuration\n\n- [`COMMAND`](https://redis.io/docs/latest/commands/command) reply drops `random` and `sort-for-scripts` flags, which are now part of [command tips](https://redis.io/docs/latest/develop/reference/command-tips) ([#10104](https://github.com/redis/redis/pull/10104)) - The `random` flag was replaced with the `nondeterministic_output` tip; the `sort-for-scripts` flag was replaced by the ` nondeterministic_output_order` tip\n\n- [`INFO`](https://redis.io/docs/latest/commands/info) `commandstats` now shows the stats per sub-command ([#9504](https://github.com/redis/redis/pull/9504))\nFor example, while previous versions would provide a single entry for all command usage, in Redis 7, each sub command is reported separately:\n\n  - Redis 6.2:\n  \n    [code example]\n\n  - Redis 7:\n  \n    [code example]\n\n- [`CONFIG REWRITE`](https://redis.io/docs/latest/commands/config-rewrite), [`CONFIG RESETSTAT`](https://redis.io/docs/latest/commands/config-resetstat, and most [`CONFIG SET`](https://redis.io/docs/latest/commands/config-setcommands are now allowed during loading ([#9878](https://github.com/redis/redis/pull/9878))"},{"id":"deprecations","title":"Deprecations","role":"content","text":"- [`CLUSTER SLOTS`](https://redis.io/docs/latest/commands/cluster-slots) is deprecated as of Redis 7.0"}],"examples":[{"id":"redis-70-breaking-changes-ex0","language":"sh","code":"$ redis-cli -p <port> INFO\n\"# Server\nredis_version:7.0.8\n...\"","section_id":"redis-70-breaking-changes"},{"id":"redis-70-breaking-changes-ex1","language":"diff","code":"1: config set maxmemory 1\n    2: +OK\n    3: eval \"return redis.call('set','x','y')\" 0\n  - 4: -ERR Error running script (call to 71e6319f97b0fe8bdfa1c5df3ce4489946dda479): @user_script:1: @user_script: 1: -OOM command not allowed when used memory > 'maxmemory'.\n  + 4: -ERR Error running script (call to 71e6319f97b0fe8bdfa1c5df3ce4489946dda479): @user_script:1: OOM command not allowed when used memory > 'maxmemory'.\n    5: eval \"return redis.pcall('set','x','y')\" 0\n  - 6: -@user_script: 1: -OOM command not allowed when used memory > 'maxmemory'.\n  + 6: -OOM command not allowed when used memory > 'maxmemory'.\n    7: eval \"return redis.call('select',99)\" 0\n    8: -ERR Error running script (call to 4ad5abfc50bbccb484223905f9a16f09cd043ba8): @user_script:1: ERR DB index is out of range\n    9: eval \"return redis.pcall('select',99)\" 0\n   10: -ERR DB index is out of range\n   11: eval_ro \"return redis.call('set','x','y')\" 0\n  -12: -ERR Error running script (call to 71e6319f97b0fe8bdfa1c5df3ce4489946dda479): @user_script:1: @user_script: 1: Write commands are not allowed from read-only scripts.\n  +12: -ERR Error running script (call to 71e6319f97b0fe8bdfa1c5df3ce4489946dda479): @user_script:1: ERR Write commands are not allowed from read-only scripts.\n   13: eval_ro \"return redis.pcall('set','x','y')\" 0\n  -14: -@user_script: 1: Write commands are not allowed from read-only scripts.\n  +14: -ERR Write commands are not allowed from read-only scripts.","section_id":"redis-70-breaking-changes"},{"id":"redis-70-breaking-changes-ex2","language":"diff","code":"1: zadd myzset 1 \"one\"\n    2: (integer) 1\n    3: zadd myzset 2 \"two\"\n    4: (integer) 1\n    5: zadd myzset 3 \"three\"\n    6: (integer) 1\n    7: zpopmin myzset -1\n  - 8: (empty array)\n  + 8: (error) ERR value is out of range, must be positive\n    9: 127.0.0.1:6379> set foo bar\n   10: OK\n   11: zpopmin foo 0\n  -12: (empty array)\n  +12: (error) WRONGTYPE Operation against a key holding the wrong kind of value","section_id":"redis-70-breaking-changes"},{"id":"redis-70-breaking-changes-ex3","language":"diff","code":"1: acl setuser foo off resetchannels &channel1 -@all +get\n    2: OK\n    3: acl getuser foo\n    4: 1) \"flags\"\n    5: 2) 1) \"off\"\n    6: 3) \"passwords\"\n    7: 4) (empty array)\n    8: 5) \"commands\"\n    9: 6) \"-@all +get\"\n   10: 7) \"keys\"\n  -11: 8) (empty array)\n  +11: 8) \"\"\n   12: 9)\"channels\"\n  -13 10) 1) \"channel1\"\n  +13 10) \"&channel1\"","section_id":"redis-70-breaking-changes"},{"id":"redis-70-breaking-changes-ex4","language":"plaintext","code":"USER FOO (+sort ~* ~mylist) \n  #FOO> sort mylist by w* get v*  - is O.K since ~* provides full key access","section_id":"redis-70-breaking-changes"},{"id":"redis-70-breaking-changes-ex5","language":"plaintext","code":"USER FOO (+sort %R~* ~mylist) \n  #FOO> sort mylist by w* get v*  - is O.K since %R~* provides full key READ access**","section_id":"redis-70-breaking-changes"},{"id":"redis-70-breaking-changes-ex6","language":"plaintext","code":"USER FOO (+sort %W~* ~mylist)\n  #FOO> sort mylist by w* get v*  - will now fail since $W~* only provides full key WRITE access","section_id":"redis-70-breaking-changes"},{"id":"redis-70-breaking-changes-ex7","language":"plaintext","code":"USER FOO (+sort ~v* ~mylist)\n  #FOO> sort mylist by w* get v*  - will now fail since ~v* only provides partial key access","section_id":"redis-70-breaking-changes"},{"id":"redis-70-breaking-changes-ex8","language":"plaintext","code":"cmdstat_acl:calls=4,usec=279,usec_per_call=69.75,rejected_calls=0,failed_calls=2","section_id":"redis-70-breaking-changes"},{"id":"redis-70-breaking-changes-ex9","language":"  ","code":"cmdstat_acl|list:calls=1,usec=4994,usec_per_call=4994.00,rejected_calls=0,failed_calls=0\n    cmdstat_acl|setuser:calls=2,usec=16409,usec_per_call=8204.50,rejected_calls=0,failed_calls=0\n    cmdstat_acl|deluser:calls=1,usec=774,usec_per_call=774.00,rejected_calls=0,failed_calls=0\n    cmdstat_acl|getuser:calls=1,usec=6044,usec_per_call=6044.00,rejected_calls=0,failed_calls=0","section_id":"redis-70-breaking-changes"}]}
{"schema_version":2,"id":"operate/rc/changelog/2023/may-2023","title":"Redis Cloud changelog (May 2023)","url":"https://redis.io/docs/latest/operate/rc/changelog/2023/may-2023/","summary":"New features, enhancements, and other changes added to Redis Cloud during May 2023.","aliases":["/operate/rc/changelog/may-2023"],"tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:13+01:00","page_type":"content","content_hash":"98ed903a9867bdb70985d95b98c12b775bdff2a3aa9038592d8fb16e2f3cafd0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This changelog lists new features, enhancements, and other changes added to Redis Cloud during May 2023."},{"id":"new-features-and-enhancements","title":"New features and enhancements","role":"content","text":""},{"id":"cloud-api---fixed-plans-support","title":"Cloud API - Fixed plans support","role":"content","text":"The [Redis Cloud API](https://redis.io/docs/latest/operate/rc/api) supports new endpoints to manage fixed subscriptions and databases. \n\nThe new fixed endpoints can be viewed in the [Redis Cloud Swagger UI](https://api.redislabs.com/v1/swagger-ui/index.html#/Subscriptions%20-%20Fixed). For more information about using the Redis Cloud API, see [Use the REST API](https://redis.io/docs/latest/operate/rc/api/get-started/use-rest-api)."},{"id":"active-active-access-roles","title":"Active-Active access roles","role":"content","text":"For [Active-Active databases](https://redis.io/docs/latest/operate/rc/databases/active-active), you can now define roles with different levels of access for different regions.\n\nFor more information, see [Active-Active roles](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/active-active-roles)."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/2023/november-2023","title":"Redis Cloud changelog (November 2023)","url":"https://redis.io/docs/latest/operate/rc/changelog/2023/november-2023/","summary":"New features, enhancements, and other changes added to Redis Cloud during November 2023.","aliases":["/operate/rc/changelog/november-2023/"],"tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:13+01:00","page_type":"content","content_hash":"82498c1090837a5a55f1f78b3c4e468c0ac67f47db37b4ef25db6bbf830ba706","sections":[{"id":"new-features","title":"New features","role":"overview","text":""},{"id":"new-fixed-plans","title":"New Fixed plans","role":"content","text":"Redis is updating our [Fixed subscriptions](https://redis.io/docs/latest/operate/rc/databases/create-database/create-essentials-database) on Redis Cloud. These plans offer increased capacity at lower prices compared to legacy plans.\n\nSee [Fixed plan subscription tiers](https://redis.io/docs/latest/operate/rc/databases/create-database/create-essentials-database#fixed-plans) for an updated list of all Fixed plans."},{"id":"redis-72-fixed-region-support","title":"Redis 7.2 Fixed region support","role":"content","text":"You can now use Redis 7.2 on [Fixed subscriptions](https://redis.io/docs/latest/operate/rc/databases/create-database/create-essentials-database) in `us-east-1` on Amazon Web Services. New Fixed subscriptions created in `us-east-1` will use Redis 7.2, and existing subscriptions will continue to use Redis 6.2.\n\nRedis 7.2 introduces several changes to existing Redis commands; see the [list of breaking changes](https://redis.io/docs/latest/operate/rc/changelog/2023/june-2023#redis-72-breaking-changes) published in June's changelog for more details."},{"id":"deprecations","title":"Deprecations","role":"content","text":""},{"id":"heroku-add-on-free-database-limit","title":"Heroku add-on free database limit","role":"content","text":"Heroku add-ons for [Redis Cloud](https://elements.heroku.com/addons/rediscloud) and [Memcached Cloud](https://elements.heroku.com/addons/memcachedcloud) are limited to one free database per Heroku account. As of November 1st, users will not be able to add a new free database if they already have one or more free databases. There will be no impact to existing free and paid databases.\n\nAdditionally, Heroku Review and CI Apps will provision a 100 MB database for the [Redis Cloud](https://elements.heroku.com/addons/rediscloud) and [Memcached Cloud](https://elements.heroku.com/addons/memcachedcloud) add-on selections."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/2023/october-2023","title":"Redis Cloud changelog (October 2023)","url":"https://redis.io/docs/latest/operate/rc/changelog/2023/october-2023/","summary":"New features, enhancements, and other changes added to Redis Cloud during October 2023.","aliases":["/operate/rc/changelog/october-2023"],"tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:13+01:00","page_type":"content","content_hash":"0ff5eb7475393128767eb8e2baf00f29aecad388c0ac662b2fbad323f1201884","sections":[{"id":"new-features","title":"New features","role":"overview","text":""},{"id":"cost-report-csv-download","title":"Cost report CSV download","role":"content","text":"You can now download shard cost reports in CSV format from the [**Billing and Payments**](https://redis.io/docs/latest/operate/rc/billing-and-payments) and [**Usage Reports**](https://redis.io/docs/latest/operate/rc/logs-reports/usage-reports) pages.\n\nSelect the **Cost report** button to download a report of all shard costs in CSV format.\n\n![The cost report download button.](https://redis.io/docs/latest/images/rc/button-cost-report-download.png)\n\nThe report gives a breakdown of all your Redis billing unit costs, per subscription and database, by month. Subscriptions under the minimum subscription price are broken down into actual billing unit prices and the difference from minimum price."},{"id":"saml-account-linking-tokens","title":"SAML account linking tokens","role":"content","text":"The process for [linking new Redis accounts](https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso#link-other-accounts) to your [SAML single sign-on](https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso) configuration has changed to enhance security. Now, both accounts must use a token to ensure that the connection is legitimate."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/2023/september-2023","title":"Redis Cloud changelog (September 2023)","url":"https://redis.io/docs/latest/operate/rc/changelog/2023/september-2023/","summary":"New features, enhancements, and other changes added to Redis Cloud during September 2023.","aliases":["/operate/rc/changelog/september-2023"],"tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:13+01:00","page_type":"content","content_hash":"687e5718bfca4598076471bfe36a9897cdbe535a43a0e4a04acfba4869c5386f","sections":[{"id":"new-features","title":"New features","role":"overview","text":""},{"id":"resp-protocol-selection","title":"RESP protocol selection","role":"content","text":"For all databases using Redis 7.2, you can now choose between the RESP2 and RESP3 protocols when you [create a database](https://redis.io/docs/latest/operate/rc/databases/create-database). For more information about the different RESP versions, see the [Redis serialization protocol specification](https://redis.io/docs/latest/develop/reference/protocol-spec#resp-versions)."},{"id":"opt-in-to-redis-72","title":"Opt-in to Redis 7.2","role":"content","text":"Select **Opt-in to 7.2** to request to upgrade your subscription and databases to Redis 7.2 (*Redis 6.2 and earlier only*).\n\n![Opt-in to 7.2 button.](https://redis.io/docs/latest/images/rc/button-opt-in-to-72.png)\n\nThe upgrade will start one week from your request, according to [maintenance windows](https://redis.io/docs/latest/operate/rc/subscriptions/maintenance/set-maintenance-windows). \n\nRedis 7.2 introduces several changes to existing Redis commands; see the list of [breaking changes](https://redis.io/docs/latest/operate/rc/changelog/2023/june-2023#redis-72-breaking-changes) for more details."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/2024/april-2024","title":"Redis Cloud changelog (April 2024)","url":"https://redis.io/docs/latest/operate/rc/changelog/2024/april-2024/","summary":"New features, enhancements, and other changes added to Redis Cloud during April 2024.","aliases":["/operate/rc/changelog/april-2024/"],"tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:13+01:00","page_type":"content","content_hash":"2ffb0bada3d81b93ded61742742a8e62ae03aaaacf0b09a4779cd3ddb8100695","sections":[{"id":"new-features","title":"New features","role":"overview","text":""},{"id":"nano-billing-unit","title":"Nano billing unit","role":"content","text":"We added a Nano billing unit for Pro databases with a maximum size of 500 MB and a maximum throughput of 500 ops/sec. Use it to create smaller databases for a lower cost."},{"id":"essentials-subscriptions-through-aws-marketplace","title":"Essentials subscriptions through AWS Marketplace","role":"content","text":"You can now use your [AWS Marketplace](https://redis.io/docs/latest/operate/rc/cloud-integrations/gcp-marketplace) account to pay for your Essentials subscriptions as well as your Pro subscriptions."},{"id":"enhancements","title":"Enhancements","role":"content","text":""},{"id":"search-and-query-throughput-in-opssec","title":"Search and query throughput in ops/sec","role":"content","text":"You can now set the throughput for databases with Search and query in operations per second (ops/sec), like all other Redis databases. This will let you seamlessly scale your query workload in and out as needed."},{"id":"deprecations","title":"Deprecations","role":"content","text":"- Setting throughput by `number-of-shards` is now deprecated for the `POST /v1/subscriptions/{subscriptionId}/databases` REST API endpoint and will be removed soon. We recommend changing the throughput measurement to `operations-per-second` when you create databases using the Redis Cloud API."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/2024/december-2024","title":"Redis Cloud changelog (December 2024)","url":"https://redis.io/docs/latest/operate/rc/changelog/2024/december-2024/","summary":"New features, enhancements, and other changes added to Redis Cloud during December 2024.","aliases":["/operate/rc/changelog/december-2024/"],"tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:13+01:00","page_type":"content","content_hash":"424d30ae6a74bbb373301f784c680ec8f7436208cbbf516a92a402dfccfbee5b","sections":[{"id":"new-features","title":"New features","role":"overview","text":""},{"id":"logs-viewer-role","title":"Logs Viewer role","role":"content","text":"You can now add a user with the **Logs Viewer** role in the [Access Management](https://redis.io/docs/latest/operate/rc/security/access-control/access-management) screen. Logs Viewers can only use the [Redis Cloud API](https://redis.io/docs/latest/operate/rc/api) [`GET logs/`](https://api.redislabs.com/v1/swagger-ui/index.html#/Account/getAccountSystemLogs) endpoint. \n\nSee [Team Management roles](https://redis.io/docs/latest/operate/rc/security/access-control/access-management#team-management-roles) to see an overview of user roles and their permissions."},{"id":"redis-flex-preview-on-redis-cloud-essentials","title":"Redis Flex preview on Redis Cloud Essentials","role":"content","text":"Redis Flex is now available in Preview on Redis Cloud Essentials.\n\nRedis Flex databases have a tiered solid state drive (SSD) and RAM architecture. Using SSDs instead of RAM significantly reduces infrastructure costs, which means developers can build applications that require large datasets using the same Redis API.\n\nSee [Create a Redis Flex database](https://redis.io/docs/latest/operate/rc/databases/create-database/create-flex-database) for more info."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/2024/february-2024","title":"Redis Cloud changelog (February 2024)","url":"https://redis.io/docs/latest/operate/rc/changelog/2024/february-2024/","summary":"New features, enhancements, and other changes added to Redis Cloud during February 2024.","aliases":["/operate/rc/changelog/february-2024/"],"tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:13+01:00","page_type":"content","content_hash":"33b661163b36b43cd9cb7518c56b739b0fd3ab9ba07c5fcf2ee28d2dcef9247a","sections":[{"id":"enhancements","title":"Enhancements","role":"overview","text":""},{"id":"dataset-size-alert-set-by-default","title":"Dataset size alert set by default","role":"content","text":"The **Dataset size has reached** [alert](https://redis.io/docs/latest/operate/rc/databases/create-database#alerts-section) is now set by default when you create your first database in a Flexible subscription."},{"id":"separated-capability-selection","title":"Separated capability selection","role":"content","text":"When you select [advanced capabilities](https://redis.io/docs/latest/operate/rc/databases/create-database#modules) during database creation, selecting **Search and query** will no longer automatically select **JSON**. You can still combine the **Search and query** and **JSON** advanced capabilities."},{"id":"new-backup-location-errors","title":"New backup location errors","role":"content","text":"The Redis Cloud console will now notify you through email and on the application if the backup location you specify does not exist or has the wrong permissions. See [Back up databases](https://redis.io/docs/latest/operate/rc/databases/back-up-data) to learn how to set the correct permissions for your backup locations."},{"id":"cidr-overlap-detection","title":"CIDR overlap detection","role":"content","text":"When you [create a new Flexible subscription](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new), the Redis Cloud console will now detect if the Classless Inter-Domain Routing (CIDR) subnet range you specify is in use by another Flexible subscription on the same account. This will allow you to address and resolve CIDR conflicts in your Redis deployment."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/2024","title":"Redis Cloud changelog (2024)","url":"https://redis.io/docs/latest/operate/rc/changelog/2024/","summary":"All Redis Cloud changelogs from 2024.","tags":["docs","operate","rc"],"last_updated":"2025-07-10T15:53:24-04:00","children":[{"id":"operate/rc/changelog/2024/december-2024","summary":"New features, enhancements, and other changes added to Redis Cloud during December 2024.","title":"Redis Cloud changelog (December 2024)","url":"https://redis.io/docs/latest/operate/rc/changelog/2024/december-2024/"},{"id":"operate/rc/changelog/2024/november-2024","summary":"New features, enhancements, and other changes added to Redis Cloud during November 2024.","title":"Redis Cloud changelog (November 2024)","url":"https://redis.io/docs/latest/operate/rc/changelog/2024/november-2024/"},{"id":"operate/rc/changelog/2024/october-2024","summary":"New features, enhancements, and other changes added to Redis Cloud during October 2024.","title":"Redis Cloud changelog (October 2024)","url":"https://redis.io/docs/latest/operate/rc/changelog/2024/october-2024/"},{"id":"operate/rc/changelog/2024/july-2024","summary":"New features, enhancements, and other changes added to Redis Cloud during July 2024.","title":"Redis Cloud changelog (July 2024)","url":"https://redis.io/docs/latest/operate/rc/changelog/2024/july-2024/"},{"id":"operate/rc/changelog/2024/june-2024","summary":"New features, enhancements, and other changes added to Redis Cloud during June 2024.","title":"Redis Cloud changelog (June 2024)","url":"https://redis.io/docs/latest/operate/rc/changelog/2024/june-2024/"},{"id":"operate/rc/changelog/2024/may-2024","summary":"New features, enhancements, and other changes added to Redis Cloud during May 2024.","title":"Redis Cloud changelog (May 2024)","url":"https://redis.io/docs/latest/operate/rc/changelog/2024/may-2024/"},{"id":"operate/rc/changelog/2024/april-2024","summary":"New features, enhancements, and other changes added to Redis Cloud during April 2024.","title":"Redis Cloud changelog (April 2024)","url":"https://redis.io/docs/latest/operate/rc/changelog/2024/april-2024/"},{"id":"operate/rc/changelog/2024/march-2024","summary":"New features, enhancements, and other changes added to Redis Cloud during March 2024.","title":"Redis Cloud changelog (March 2024)","url":"https://redis.io/docs/latest/operate/rc/changelog/2024/march-2024/"},{"id":"operate/rc/changelog/2024/february-2024","summary":"New features, enhancements, and other changes added to Redis Cloud during February 2024.","title":"Redis Cloud changelog (February 2024)","url":"https://redis.io/docs/latest/operate/rc/changelog/2024/february-2024/"},{"id":"operate/rc/changelog/2024/january-2024","summary":"New features, enhancements, and other changes added to Redis Cloud during January 2024.","title":"Redis Cloud changelog (January 2024)","url":"https://redis.io/docs/latest/operate/rc/changelog/2024/january-2024/"}],"page_type":"content","content_hash":"3e12b44a9b3cba260cbb09d90adb84454eac8a23ed637424b0fa51424b167298","sections":[{"id":"content","title":"Content","role":"content","text":"Select a month from the following table to see a more detailed changelog for that month:\n\n\n| Month | Major changes |\n| --- | --- |\n| [December 2024](https://redis.io/docs/latest/operate/rc/changelog/2024/december-2024/) | Logs Viewer API role |\n| [November 2024](https://redis.io/docs/latest/operate/rc/changelog/2024/november-2024/) | Pro subscription version upgrade |\n| [October 2024](https://redis.io/docs/latest/operate/rc/changelog/2024/october-2024/) | Redis 7.4 Preview on Redis Cloud Pro, Security fixes |\n| [July 2024](https://redis.io/docs/latest/operate/rc/changelog/2024/july-2024/) | Redis 7.4 Preview |\n| [June 2024](https://redis.io/docs/latest/operate/rc/changelog/2024/june-2024/) | Redis Cloud Essentials support for Terraform |\n| [May 2024](https://redis.io/docs/latest/operate/rc/changelog/2024/may-2024/) | Multi-domain SAML setup, SAML domain verification, Active-Active search support |\n| [April 2024](https://redis.io/docs/latest/operate/rc/changelog/2024/april-2024/) | Nano billing unit, Essentials subscriptions through AWS Marketplace |\n| [March 2024](https://redis.io/docs/latest/operate/rc/changelog/2024/march-2024/) | Fixed plans through Google Cloud Marketplace |\n| [February 2024](https://redis.io/docs/latest/operate/rc/changelog/2024/february-2024/) | CIDR overlap detection |\n| [January 2024](https://redis.io/docs/latest/operate/rc/changelog/2024/january-2024/) | Transit Gateway, Database tags |"}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/2024/january-2024","title":"Redis Cloud changelog (January 2024)","url":"https://redis.io/docs/latest/operate/rc/changelog/2024/january-2024/","summary":"New features, enhancements, and other changes added to Redis Cloud during January 2024.","aliases":["/operate/rc/changelog/january-2024/"],"tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:13+01:00","page_type":"content","content_hash":"3e0371c6d0a5261008adfbcad0dea2665c3c2e03355a8a4dc71bfbc4691693da","sections":[{"id":"new-features","title":"New features","role":"overview","text":""},{"id":"transit-gateway","title":"Transit Gateway","role":"content","text":"You can now connect your Flexible subscriptions hosted on Amazon Web Services (AWS) to [AWS Transit Gateway](https://docs.aws.amazon.com/vpc/latest/tgw/how-transit-gateways-work.html). See [Connect to Transit Gateway](https://redis.io/docs/latest/operate/rc/security/aws-transit-gateway) to learn how to connect your Flexible subscription to Transit Gateway."},{"id":"billing-admin-role","title":"Billing Admin role","role":"content","text":"You can now add a user with the Billing Admin role in the [Access Management](https://redis.io/docs/latest/operate/rc/security/access-control/access-management) screen. Billing Admins can view and edit settings related to billing and payments. They can add and remove payment methods and change the payment method for a subscription, but they cannot change any other subscription or database settings.\n\nSee [Team Management roles](https://redis.io/docs/latest/operate/rc/security/access-control/access-management#team-management-roles) to see an overview of user roles and their permissions."},{"id":"database-tags","title":"Database tags","role":"content","text":"You can now add [tags](https://redis.io/docs/latest/operate/rc/databases/tag-database) to your database.\n\nTags are key-value pairs that let you categorize your databases. You can create tags and add them to your databases to associate them with each other. Once you've added tags, you can filter your databases in the [database list](https://redis.io/docs/latest/operate/rc/databases/view-edit-database#manage-the-database-list) by tag key or value. \n\nSee [Manage tags](https://redis.io/docs/latest/operate/rc/databases/tag-database) to learn how to add tags to your database."},{"id":"network-data-cost-breakdowns","title":"Network data cost breakdowns","role":"content","text":"Invoices for customers with Flexible and Annual subscriptions now include a breakdown of network data costs for the subscription. To download an invoice, go to the [Billing and Payments](https://redis.io/docs/latest/operate/rc/billing-and-payments#download-invoice) page on the [Redis Cloud console](https://cloud.redis.io/).\n\nCustomers with Annual subscriptions can also find a breakdown of network data costs in the consumption report."},{"id":"deprecations","title":"Deprecations","role":"content","text":"- As of January 2024, new and upgraded Redis Cloud Essentials subscriptions are limited to one database per subscription. If you have a Redis Cloud Essentials subscription created before January 2024, you can add databases up to the previous plan limit. If you choose to upgrade your plan, you can keep your databases, but you will need to create a new subscription to add another database."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/2024/july-2024","title":"Redis Cloud changelog (July 2024)","url":"https://redis.io/docs/latest/operate/rc/changelog/2024/july-2024/","summary":"New features, enhancements, and other changes added to Redis Cloud during July 2024.","aliases":["/operate/rc/changelog/july-2024/"],"tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:13+01:00","page_type":"content","content_hash":"0e70b7e5021a0346daabef1a85b2017afc4936e9cc7129d8bfdb83d87dc18e44","sections":[{"id":"new-features","title":"New Features","role":"overview","text":""},{"id":"query-performance-factor-preview","title":"Query performance factor preview","role":"content","text":"When you create a Pro database with [Search and query](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search) capabilities, you can now choose to boost your query capabilities with the Query performance factor. The query performance factor adds dedicated power specifically for Search and query. For more information, see [Search and query sizing](https://redis.io/docs/latest/operate/rc/databases/configuration/advanced-capabilities#search-and-query-sizing).\n\nYou can take advantage of extra query speed for free during the preview period.\n\nThe query performance factor is available for Redis Cloud Pro databases on Redis 7.2 and later."},{"id":"redis-74-preview","title":"Redis 7.4 Preview","role":"content","text":"A preview of Redis 7.4 is now available on [Redis Cloud Essentials databases](https://redis.io/docs/latest/operate/rc/databases/create-database/create-essentials-database) in select regions on Amazon Web Services and Google Cloud.\n\nRedis 7.4 offers hash field expiration and other feature set improvements. For more information on the changes in Redis 7.4, see the [Redis 7.4 release blog post](https://redis.io/blog/announcing-redis-community-edition-and-redis-stack-74).\n\n#### Redis database version 7.4 breaking changes {#redis-74-breaking-changes}\n\nWhen new versions of Redis Community Edition change existing commands, upgrading your database to a new version can potentially break some functionality. Before you upgrade, read the provided list of breaking changes that affect Redis Software and update any applications that connect to your database to handle these changes.\n\nConfirm your Redis database version (`redis_version`) using the Cluster Manager UI or run the following [`INFO`](https://redis.io/docs/latest/commands/info) command with [`redis-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/redis-cli):\n\n[code example]\n\n##### Security behavior changes\n\n- [#13108](https://github.com/redis/redis/pull/13108) Lua: LRU eviction for scripts generated with `EVAL`. \n\n##### Other general behavior changes\n\n- [#13133](https://github.com/redis/redis/pull/13133) Lua: allocate VM code with jemalloc instead of libc and count it as used memory.\n\n- [#12171](https://github.com/redis/redis/pull/12171) `ACL LOAD`: do not disconnect all clients."}],"examples":[{"id":"redis-74-preview-ex0","language":"sh","code":"$ redis-cli -p <port> INFO\n\"# Server\nredis_version:7.0.8\n...\"","section_id":"redis-74-preview"}]}
{"schema_version":2,"id":"operate/rc/changelog/2024/june-2024","title":"Redis Cloud changelog (June 2024)","url":"https://redis.io/docs/latest/operate/rc/changelog/2024/june-2024/","summary":"New features, enhancements, and other changes added to Redis Cloud during June 2024.","aliases":["/operate/rc/changelog/june-2024/"],"tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:13+01:00","page_type":"content","content_hash":"2e81465731090fc9cfabae0bf1b9eccd0d27a4ff9adbeb771542d3debd423565","sections":[{"id":"enhancements","title":"Enhancements","role":"overview","text":""},{"id":"redis-cloud-essentials-support-for-terraform","title":"Redis Cloud Essentials support for Terraform","role":"content","text":"The [Redis Cloud Terraform provider](https://redis.io/docs/latest/integrate/terraform-provider-for-redis-cloud) now supports provisioning Redis Cloud Essentials databases. It also supports Active-Active databases with advanced capabilities. \n\nSee the [Redis Cloud Terraform provider changelog](https://github.com/RedisLabs/terraform-provider-rediscloud/blob/main/CHANGELOG.md) for full version content.\n\nSee [Get started with Terraform](https://redis.io/docs/latest/integrate/terraform-provider-for-redis-cloud/get-started) to learn how to install the Redis Cloud provider and create a subscription and database."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/2024/march-2024","title":"Redis Cloud changelog (March 2024)","url":"https://redis.io/docs/latest/operate/rc/changelog/2024/march-2024/","summary":"New features, enhancements, and other changes added to Redis Cloud during March 2024.","aliases":["/operate/rc/changelog/march-2024/"],"tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:13+01:00","page_type":"content","content_hash":"07c4f8f07685a58c6a4d51fe3057b851ebdd5a9a221eea0defc9184960405f36","sections":[{"id":"new-features","title":"New Features","role":"overview","text":""},{"id":"fixed-subscriptions-through-google-cloud-marketplace","title":"Fixed subscriptions through Google Cloud Marketplace","role":"content","text":"You can now use your [Google Cloud Marketplace](https://redis.io/docs/latest/operate/rc/cloud-integrations/gcp-marketplace) account to pay for your Fixed subscriptions as well as your Flexible subscriptions."},{"id":"active-passive-with-tls","title":"Active-Passive with TLS","role":"content","text":"[Active-Passive](https://redis.io/docs/latest/operate/rc/databases/migrate-databases#sync-using-active-passive) now supports source databases that require connections over TLS. See [Migrate data](https://redis.io/docs/latest/operate/rc/databases/migrate-databases) for more information."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/2024/may-2024","title":"Redis Cloud changelog (May 2024)","url":"https://redis.io/docs/latest/operate/rc/changelog/2024/may-2024/","summary":"New features, enhancements, and other changes added to Redis Cloud during May 2024.","aliases":["/operate/rc/changelog/may-2024/"],"tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:13+01:00","page_type":"content","content_hash":"84ae10c83db8b72f61123ce25eeb788a92f8e1b24f9a30d3fe942988655485c2","sections":[{"id":"new-features","title":"New features","role":"overview","text":""},{"id":"multi-domain-saml-setup","title":"Multi-domain SAML setup","role":"content","text":"[SAML single sign-on](https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso) now supports multiple username domains. Select **Manage domains** on the **Single sign-on** screen to associate additional domains to your SAML setup."},{"id":"saml-domain-verification","title":"SAML domain verification","role":"content","text":"We added domain verification to [SAML single sign-on](https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso) to enhance security. Before you set up SAML in Redis Cloud, you must first [verify domain ownership](https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso#verify-domain) for any domains associated with your SAML setup.\n\nIf you have an active SAML configuration, you can still sign in with SAML SSO, but your domains will be set to **Unverified**. You should [verify domain ownership](https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso#verify-domain) as soon as possible."},{"id":"active-active-search-and-query-support","title":"Active-Active Search and query support","role":"content","text":"[Active-Active databases](https://redis.io/docs/latest/operate/rc/databases/active-active) on Redis Cloud now support [Search and query](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search).\n\nSee [Create an Active-Active database](https://redis.io/docs/latest/operate/rc/databases/active-active/create-active-active-database) to learn how to create an Active-Active database.\n\nSee [Search and query Active-Active databases](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/search-active-active) to learn how to use Search and query on Active-Active databases."},{"id":"enhancements","title":"Enhancements","role":"content","text":""},{"id":"dataset-size","title":"Dataset size","role":"content","text":"We now distinguish between dataset size and memory limit to reduce confusion.\n\nFor Redis Cloud Essentials, the plan size refers to the full memory limit, not the dataset size. If you select high availability for an Essentials database, your dataset size will be half of the plan size. Both the total memory limit and dataset size are listed under **Database details** when you create an Essentials database.\n\nFor Redis Cloud Pro, you define your dataset size when you create the database, and we calculate your total memory limit automatically based on the features you choose.\n\nSee [Dataset size](https://redis.io/docs/latest/operate/rc/databases/configuration/sizing#dataset-size) for more information."},{"id":"deprecations","title":"Deprecations","role":"content","text":""},{"id":"end-of-triggers-and-functions-preview","title":"End of Triggers and Functions preview","role":"content","text":"Redis has discontinued the \"Triggers and Functions\" preview feature. You are no longer able to create a database with Triggers and Functions.  \n\n- Commands such as TFCALL, TFCALLASYNC, and TFUNCTION are now deprecated and will return error messages upon execution.\n- Any JavaScript functions stored in Redis will be automatically removed, and JavaScript-based triggers will be disabled.\n- Lua functions and scripts will remain unaffected by this change."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/2024/november-2024","title":"Redis Cloud changelog (November 2024)","url":"https://redis.io/docs/latest/operate/rc/changelog/2024/november-2024/","summary":"New features, enhancements, and other changes added to Redis Cloud during November 2024.","aliases":["/operate/rc/changelog/november-2024/"],"tags":["docs","operate","rc"],"last_updated":"2026-10-01T15:21:44+01:00","page_type":"content","content_hash":"95e2deeb78376c0d8950912eb993dfe73a0e97183daf77e9e84ba67deed5d2a0","sections":[{"id":"new-features","title":"New Features","role":"overview","text":""},{"id":"pro-subscription-version-upgrade","title":"Pro subscription version upgrade","role":"content","text":"You can now upgrade all databases in your Pro subscription from your [subscription page](https://redis.io/docs/latest/operate/rc/subscriptions/view-pro-subscription). \n\nSelect **Version upgrade** to request to upgrade your subscription and databases if there is a later version available.\n\n![Version upgrade button.](https://redis.io/docs/latest/images/rc/button-version-upgrade.png)\n\nSelect the version to upgrade your databases from the list and select **Upgrade** to submit the upgrade request.\n\n![Version upgrade request list with version 7.4 selected.](https://redis.io/docs/latest/images/rc/version-upgrade-request.png)\n\nThe upgrade will start one week from your request, according to your subscription's [maintenance windows](https://redis.io/docs/latest/operate/rc/subscriptions/maintenance/set-maintenance-windows). \n\nReview the [7.2 breaking changes](https://redis.io/docs/latest/operate/rc/changelog/2023/june-2023#redis-72-breaking-changes) or [7.4 breaking changes](https://redis.io/docs/latest/operate/rc/changelog/2024/july-2024#redis-74-breaking-changes) before you request to upgrade."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/2024/october-2024","title":"Redis Cloud changelog (October 2024)","url":"https://redis.io/docs/latest/operate/rc/changelog/2024/october-2024/","summary":"New features, enhancements, and other changes added to Redis Cloud during October 2024.","aliases":["/operate/rc/changelog/october-2024/"],"tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:13+01:00","page_type":"content","content_hash":"49d4aebdc36712c90df8c599ba28c9443b05962c67195a0274c7c4cff10906c2","sections":[{"id":"new-features","title":"New Features","role":"overview","text":""},{"id":"redis-74-preview-on-redis-cloud-pro","title":"Redis 7.4 Preview on Redis Cloud Pro","role":"content","text":"A preview of Redis 7.4 is now available on [Redis Cloud Pro databases](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new).\n\nRedis 7.4 offers hash field expiration and other feature set improvements. For more information on the changes in Redis 7.4, see the [Redis 7.4 release blog post](https://redis.io/blog/announcing-redis-community-edition-and-redis-stack-74)."},{"id":"security-fixes","title":"Security fixes","role":"security","text":"Redis Cloud has already been updated with patches for CVE-2024-31449, CVE-2024-31227, and CVE-2024-31228. No further action is required at this time. For more information, see the [Redis blog post](https://redis.io/blog/security-advisory-cve-2024-31449-cve-2024-31227-cve-2024-31228/) about these vulnerabilities."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/april-2025","title":"Redis Cloud changelog (April 2025)","url":"https://redis.io/docs/latest/operate/rc/changelog/april-2025/","summary":"New features, enhancements, and other changes added to Redis Cloud during April 2025.","tags":["docs","operate","rc"],"last_updated":"2026-10-02T16:06:07+01:00","page_type":"content","content_hash":"223e281eb81d841bbd7c7385fde9aa3658fd7ebdc0d1c10975ea4d5dda13cbcb","sections":[{"id":"new-features","title":"New features","role":"overview","text":""},{"id":"new-ui-and-dark-mode","title":"New UI and dark mode","role":"content","text":"The Redis Cloud console has a refreshed user interface! You can now choose between light mode and dark mode when using the Redis Cloud console.\n\n![Mode selection toggle with light mode selected.](https://redis.io/docs/latest/images/rc/mode-select-light.png#no-click)\n\n![Mode selection toggle with dark mode selected.](https://redis.io/docs/latest/images/rc/mode-select-dark.png#no-click)\n\nThe new user interface and dark mode are currently available for selected accounts and will be gradually rolled out to new accounts over time."},{"id":"map-multiple-redis-cloud-accounts-to-marketplace-account","title":"Map multiple Redis Cloud accounts to marketplace account","role":"content","text":"You can now map multiple Redis Cloud accounts to a single [Google Cloud Marketplace](https://redis.io/docs/latest/operate/rc/cloud-integrations/gcp-marketplace) or [AWS Marketplace](https://redis.io/docs/latest/operate/rc/cloud-integrations/aws-marketplace) account."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/august-2025","title":"Redis Cloud changelog (August 2025)","url":"https://redis.io/docs/latest/operate/rc/changelog/august-2025/","summary":"New features, enhancements, and other changes added to Redis Cloud during August 2025.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:13+01:00","page_type":"content","content_hash":"06096bbceeb38a73433195844d419ea55c274c1710144d3330f7150f70626e63","sections":[{"id":"new-features","title":"New features","role":"overview","text":""},{"id":"active-active-on-redis-cloud-byoc","title":"Active-Active on Redis Cloud BYOC","role":"content","text":"You can now deploy [Active-Active databases](https://redis.io/docs/latest/operate/rc/databases/active-active/create-active-active-database) to [Redis Cloud Bring your own Cloud](https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud)."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/december-2025","title":"Redis Cloud changelog (December 2025)","url":"https://redis.io/docs/latest/operate/rc/changelog/december-2025/","summary":"New features, enhancements, and other changes added to Redis Cloud during December 2025.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:13+01:00","page_type":"content","content_hash":"00fa911d49831c622d4e9c0c21406085f07b3fa0d257e2b93df0428a02ce2018","sections":[{"id":"new-features","title":"New features","role":"overview","text":""},{"id":"focus-compliant-cost-reports-with-rest-api","title":"FOCUS-compliant cost reports with REST API","role":"content","text":"Select users can now generate and download [cost reports](https://redis.io/docs/latest/operate/rc/billing-and-payments/cost-report) in a [FinOps Open Cost and Usage Specification (FOCUS)](https://focus.finops.org/) compliant format using the Redis Cloud REST API. You can use this cost report with any FOCUS-compatible cost reporting tool to analyze and visualize your costs.\n\n[Contact support](https://redis.io/support/) to request access to the cost report endpoint.\n\nSee [Generate FOCUS-compliant cost report with REST API](https://redis.io/docs/latest/operate/rc/api/examples/generate-cost-report) for examples."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/february-2025","title":"Redis Cloud changelog (February 2025)","url":"https://redis.io/docs/latest/operate/rc/changelog/february-2025/","summary":"New features, enhancements, and other changes added to Redis Cloud during February 2025.","tags":["docs","operate","rc"],"last_updated":"2025-07-10T15:53:24-04:00","page_type":"content","content_hash":"0973d9bf5e600596db7df164ddd3b614c544421766515a941eb80c2d4b63635f","sections":[{"id":"new-features","title":"New features","role":"overview","text":""},{"id":"pico-billing-unit","title":"Pico billing unit","role":"content","text":"We added a Pico billing unit for Pro databases with a maximum size of 100 MB and a maximum throughput of 100 ops/sec. Use it to create smaller databases for a lower cost."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog","title":"Redis Cloud changelog","url":"https://redis.io/docs/latest/operate/rc/changelog/","summary":"Lists new features, enhancements, and other changes in Redis Cloud.","tags":["docs","operate","rc"],"last_updated":"2025-06-10T14:40:49-04:00","children":[{"id":"operate/rc/changelog/version-release-notes","summary":"Lists release notes and breaking changes for available Redis database versions on Redis Cloud.","title":"Redis version release notes and breaking changes","url":"https://redis.io/docs/latest/operate/rc/changelog/version-release-notes/"},{"id":"operate/rc/changelog/september-2026","summary":"New features, enhancements, and other changes added to Redis Cloud during September 2026.","title":"Redis Cloud changelog (September 2026)","url":"https://redis.io/docs/latest/operate/rc/changelog/september-2026/"},{"id":"operate/rc/changelog/july-2026","summary":"New features, enhancements, and other changes added to Redis Cloud during July 2026.","title":"Redis Cloud changelog (July 2026)","url":"https://redis.io/docs/latest/operate/rc/changelog/july-2026/"},{"id":"operate/rc/changelog/june-2026","summary":"New features, enhancements, and other changes added to Redis Cloud during June 2026.","title":"Redis Cloud changelog (June 2026)","url":"https://redis.io/docs/latest/operate/rc/changelog/june-2026/"},{"id":"operate/rc/changelog/may-2026","summary":"New features, enhancements, and other changes added to Redis Cloud during May 2026.","title":"Redis Cloud changelog (May 2026)","url":"https://redis.io/docs/latest/operate/rc/changelog/may-2026/"},{"id":"operate/rc/changelog/march-2026","summary":"New features, enhancements, and other changes added to Redis Cloud during March 2026.","title":"Redis Cloud changelog (March 2026)","url":"https://redis.io/docs/latest/operate/rc/changelog/march-2026/"},{"id":"operate/rc/changelog/january-2026","summary":"New features, enhancements, and other changes added to Redis Cloud during January 2026.","title":"Redis Cloud changelog (January 2026)","url":"https://redis.io/docs/latest/operate/rc/changelog/january-2026/"},{"id":"operate/rc/changelog/december-2025","summary":"New features, enhancements, and other changes added to Redis Cloud during December 2025.","title":"Redis Cloud changelog (December 2025)","url":"https://redis.io/docs/latest/operate/rc/changelog/december-2025/"},{"id":"operate/rc/changelog/november-2025","summary":"New features, enhancements, and other changes added to Redis Cloud during November 2025.","title":"Redis Cloud changelog (November 2025)","url":"https://redis.io/docs/latest/operate/rc/changelog/november-2025/"},{"id":"operate/rc/changelog/october-2025","summary":"New features, enhancements, and other changes added to Redis Cloud during October 2025.","title":"Redis Cloud changelog (October 2025)","url":"https://redis.io/docs/latest/operate/rc/changelog/october-2025/"},{"id":"operate/rc/changelog/august-2025","summary":"New features, enhancements, and other changes added to Redis Cloud during August 2025.","title":"Redis Cloud changelog (August 2025)","url":"https://redis.io/docs/latest/operate/rc/changelog/august-2025/"},{"id":"operate/rc/changelog/july-2025","summary":"New features, enhancements, and other changes added to Redis Cloud during July 2025.","title":"Redis Cloud changelog (July 2025)","url":"https://redis.io/docs/latest/operate/rc/changelog/july-2025/"},{"id":"operate/rc/changelog/june-2025","summary":"New features, enhancements, and other changes added to Redis Cloud during June 2025.","title":"Redis Cloud changelog (June 2025)","url":"https://redis.io/docs/latest/operate/rc/changelog/june-2025/"},{"id":"operate/rc/changelog/may-2025","summary":"New features, enhancements, and other changes added to Redis Cloud during May 2025.","title":"Redis Cloud changelog (May 2025)","url":"https://redis.io/docs/latest/operate/rc/changelog/may-2025/"},{"id":"operate/rc/changelog/april-2025","summary":"New features, enhancements, and other changes added to Redis Cloud during April 2025.","title":"Redis Cloud changelog (April 2025)","url":"https://redis.io/docs/latest/operate/rc/changelog/april-2025/"},{"id":"operate/rc/changelog/march-2025","summary":"New features, enhancements, and other changes added to Redis Cloud during March 2025.","title":"Redis Cloud changelog (March 2025)","url":"https://redis.io/docs/latest/operate/rc/changelog/march-2025/"},{"id":"operate/rc/changelog/february-2025","summary":"New features, enhancements, and other changes added to Redis Cloud during February 2025.","title":"Redis Cloud changelog (February 2025)","url":"https://redis.io/docs/latest/operate/rc/changelog/february-2025/"},{"id":"operate/rc/changelog/2024","summary":"All Redis Cloud changelogs from 2024.","title":"Redis Cloud changelog (2024)","url":"https://redis.io/docs/latest/operate/rc/changelog/2024/"},{"id":"operate/rc/changelog/2023","summary":"All Redis Cloud changelogs from 2023.","title":"Redis Cloud changelog (2023)","url":"https://redis.io/docs/latest/operate/rc/changelog/2023/"}],"page_type":"content","content_hash":"281d9ffbe12072aed0687493e3eccd05a8897164739516874f6a3f5344d4c050","sections":[{"id":"redis-version-release-notes-and-breaking-changes","title":"Redis version release notes and breaking changes","role":"overview","text":"When new versions of Redis Open Source change existing commands, upgrading your Redis Cloud database to a new version can potentially break some functionality. Before you upgrade, read the provided list of breaking changes that affect Redis Cloud and update any applications that connect to your database to handle these changes.\n\n\n| Version | Description |\n| --- | --- |\n| [Redis 8.6](https://redis.io/docs/latest/operate/rc/changelog/version-release-notes/8-6/) | Release notes and breaking changes for Redis 8.6 on Redis Cloud. |\n| [Redis 8.4](https://redis.io/docs/latest/operate/rc/changelog/version-release-notes/8-4/) | Release notes and breaking changes for Redis 8.4 on Redis Cloud. |\n| [Redis 8.2](https://redis.io/docs/latest/operate/rc/changelog/version-release-notes/8-2/) | Release notes and breaking changes for Redis 8.2 on Redis Cloud. |\n| [Redis 8.0](https://redis.io/docs/latest/operate/rc/changelog/version-release-notes/8-0/) | Release notes and breaking changes for Redis 8.0 on Redis Cloud. |\n| [Redis 7.4](https://redis.io/docs/latest/operate/rc/changelog/version-release-notes/7-4/) | Release notes and breaking changes for Redis 7.4 on Redis Cloud. |\n| [Redis 7.2](https://redis.io/docs/latest/operate/rc/changelog/version-release-notes/7-2/) | Release notes and breaking changes for Redis 7.2 on Redis Cloud. |"},{"id":"redis-cloud-changelogs","title":"Redis Cloud changelogs","role":"content","text":"Changelogs list new features, enhancements, and other changes in Redis Cloud.\n\nSelect a month from the following table to see a more detailed changelog for that month:\n\n\n| Month | Major changes |\n| --- | --- |\n| [September 2026](https://redis.io/docs/latest/operate/rc/changelog/september-2026/) | Redis Search on Flex (beta), multiple sources in Data Integration pipelines, Redis Radar |\n| [July 2026](https://redis.io/docs/latest/operate/rc/changelog/july-2026/) | Redis 8.6 on Redis Cloud Pro |\n| [June 2026](https://redis.io/docs/latest/operate/rc/changelog/june-2026/) | Billing Admin API access, Redis Data Integration GA |\n| [May 2026](https://redis.io/docs/latest/operate/rc/changelog/may-2026/) | Redis Iris, Redis 8.6 on Redis Cloud Essentials, Dynamic endpoints, Redirect dynamic endpoints |\n| [March 2026](https://redis.io/docs/latest/operate/rc/changelog/march-2026/) | Passwordless authentication for Redis Cloud Pro, Redis 8.4 on Redis Cloud Pro, Automatic database upgrades |\n| [January 2026](https://redis.io/docs/latest/operate/rc/changelog/january-2026/) | Redis 8.4 on Redis Cloud Essentials |\n| [December 2025](https://redis.io/docs/latest/operate/rc/changelog/december-2025/) | FOCUS-compliant cost reports with REST API |\n| [November 2025](https://redis.io/docs/latest/operate/rc/changelog/november-2025/) | Redis 8.2 on Redis Cloud Pro |\n| [October 2025](https://redis.io/docs/latest/operate/rc/changelog/october-2025/) | Security fixes |\n| [August 2025](https://redis.io/docs/latest/operate/rc/changelog/august-2025/) | Active-Active on Redis Cloud BYOC |\n| [July 2025](https://redis.io/docs/latest/operate/rc/changelog/july-2025/) | LangCache public preview, Redis 8.0 preview |\n| [June 2025](https://redis.io/docs/latest/operate/rc/changelog/june-2025/) | Block public endpoints, Free database selection, Faster scaling with Redis hashing policy |\n| [May 2025](https://redis.io/docs/latest/operate/rc/changelog/may-2025/) | Upgrade database version for a single Pro database, Business address and billing address separation, Redis Cloud Bring your own Cloud |\n| [April 2025](https://redis.io/docs/latest/operate/rc/changelog/april-2025/) | New UI and dark mode, Map multiple Redis Cloud accounts to marketplace account |\n| [March 2025](https://redis.io/docs/latest/operate/rc/changelog/march-2025/) | Redis Insight on Redis Cloud, Redis Hashing policy |\n| [February 2025](https://redis.io/docs/latest/operate/rc/changelog/february-2025/) | Pico billing unit |\n| [2024](https://redis.io/docs/latest/operate/rc/changelog/2024/) | All Redis Cloud changelogs from 2024. |\n| [2023](https://redis.io/docs/latest/operate/rc/changelog/2023/) | All Redis Cloud changelogs from 2023. |"}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/january-2026","title":"Redis Cloud changelog (January 2026)","url":"https://redis.io/docs/latest/operate/rc/changelog/january-2026/","summary":"New features, enhancements, and other changes added to Redis Cloud during January 2026.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:13+01:00","page_type":"content","content_hash":"42db359fef4b8dec447a2dc184ec8a88b835a1a65aca082e07acd046764e5830","sections":[{"id":"new-features","title":"New features","role":"overview","text":""},{"id":"redis-84-on-redis-cloud-essentials","title":"Redis 8.4 on Redis Cloud Essentials","role":"content","text":"Redis 8.4 is now available for [Redis Cloud Essentials databases](https://redis.io/docs/latest/operate/rc/databases/create-database/create-essentials-database) in select regions.\n\nRedis 8.4 builds on the foundation of Redis 8.2 with significant enhancements to cluster operations, string manipulation, and stream processing capabilities. For more information on the changes in Redis 8.4, see [What's new in Redis 8.4](https://redis.io/docs/latest/develop/whats-new/8-4) and review the Redis Open Source [8.4 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.4-release-notes)."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/july-2025","title":"Redis Cloud changelog (July 2025)","url":"https://redis.io/docs/latest/operate/rc/changelog/july-2025/","summary":"New features, enhancements, and other changes added to Redis Cloud during July 2025.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:13+01:00","page_type":"content","content_hash":"e4e887f030b22fb23d82edac0af1de3487782c7e714a2d8219406dc2a6dbaf6a","sections":[{"id":"new-features","title":"New features","role":"overview","text":""},{"id":"langcache-public-preview","title":"LangCache public preview","role":"content","text":"[LangCache](https://redis.io/docs/latest/operate/iris/langcache) is now available in public preview on Redis Cloud.\n\nLangCache is a semantic caching service available as a REST API that stores LLM responses for fast and cheaper retrieval, built on the Redis vector database. By using semantic caching, you can significantly reduce API costs and lower the average latency of your generative AI applications.\n\nFor more information about how LangCache works, see the [LangCache overview](https://redis.io/docs/latest/develop/ai/context-engine/langcache).\n\nTo set up LangCache on Redis Cloud:\n\n1. [Create a database](https://redis.io/docs/latest/operate/rc/databases/create-database) on Redis Cloud.\n2. [Create a LangCache service](https://redis.io/docs/latest/operate/iris/langcache/create-service) for your database on Redis Cloud.\n3. [Use the LangCache API](https://redis.io/docs/latest/operate/iris/langcache/use-langcache) from your client app.\n\nAfter you set up LangCache, you can [view and edit the cache](https://redis.io/docs/latest/operate/iris/langcache/view-edit-cache) and [monitor the cache's performance](https://redis.io/docs/latest/operate/iris/langcache/monitor-cache).\n\nSee also our [Redis LangCache setup](https://www.youtube.com/watch?v=UOGhMZlZLko)\ntutorial video for advice on how to get started."},{"id":"redis-80-preview","title":"Redis 8.0 preview","role":"content","text":"A preview of Redis 8.0 is now available for [Redis Cloud Essentials databases](https://redis.io/docs/latest/operate/rc/databases/create-database/create-essentials-database) in select regions.\n\nRedis 8.0 introduces powerful new capabilities, including the beta release of the Vector Set data structure, designed for AI use cases such as semantic search and recommendation systems. For more information on the changes in Redis 8.0, see [What's new in Redis 8.0](https://redis.io/docs/latest/develop/whats-new/8-0) and review the [breaking changes](https://redis.io/docs/latest/operate/rc/changelog/version-release-notes/8-0)."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/july-2026","title":"Redis Cloud changelog (July 2026)","url":"https://redis.io/docs/latest/operate/rc/changelog/july-2026/","summary":"New features, enhancements, and other changes added to Redis Cloud during July 2026.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:13+01:00","page_type":"content","content_hash":"93c0d40736c5280df06a43d7d37cc513ec63521f5c8a0c3c33c114fcb4c620b0","sections":[{"id":"new-features","title":"New features","role":"overview","text":""},{"id":"redis-86-on-redis-cloud-pro","title":"Redis 8.6 on Redis Cloud Pro","role":"content","text":"Redis 8.6 is now generally available for [Redis Cloud Pro databases](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new). See the [Redis 8.6 release notes](https://redis.io/docs/latest/operate/rc/changelog/version-release-notes/8-6) and [What's new in Redis 8.6](https://redis.io/docs/latest/develop/whats-new/8-6) for more information."},{"id":"active-active-support-in-regions-with-fewer-than-three-availability-zones","title":"Active-Active support in regions with fewer than three availability zones","role":"content","text":"You can now create [Active-Active databases](https://redis.io/docs/latest/operate/rc/databases/active-active) in regions with fewer than three availability zones. These regions use single-zone replication and provide 99.99% (four-nines) availability instead of 99.999%. When an Active-Active database spans regions with different availability zone counts, the availability of the entire deployment is determined by the least resilient region.\n\nThese regions are not available in the Redis Cloud console. To create an Active-Active database that includes one of these regions, use the REST API to [create a Pro subscription](https://redis.io/docs/latest/operate/rc/api/examples/manage-subscriptions#create-a-pro-subscription). You'll be able to manage these databases using the Redis Cloud console after they're created."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/june-2025","title":"Redis Cloud changelog (June 2025)","url":"https://redis.io/docs/latest/operate/rc/changelog/june-2025/","summary":"New features, enhancements, and other changes added to Redis Cloud during June 2025.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:13+01:00","page_type":"content","content_hash":"5b6fb885793c9454771ba4d988546d68d7adda96a2ca4e9932160f9883fe239c","sections":[{"id":"new-features","title":"New features","role":"overview","text":""},{"id":"block-public-endpoints","title":"Block public endpoints","role":"content","text":"Users with Redis Cloud Pro databases can now choose to block public endpoints for all databases in their subscription. See [Block public endpoints](https://redis.io/docs/latest/operate/rc/security/database-security/block-public-endpoints) for more information."},{"id":"free-database-selection","title":"Free database selection","role":"content","text":"You can now create a free database from the [Create a database](https://redis.io/docs/latest/operate/rc/databases/create-database/create-free-database) page. Free databases are perfect for learning and exploring Redis. You get 30 MB of space for you to learn Redis concepts and develop application prototypes.\n\nYou can only have one free database per account. If you already have a free database, you can [delete it](https://redis.io/docs/latest/operate/rc/databases/delete-database) or [upgrade it to a paid Essentials plan](https://redis.io/docs/latest/operate/rc/subscriptions/view-essentials-subscription#upgrade-plan) before creating a new one."},{"id":"enhancements","title":"Enhancements","role":"content","text":""},{"id":"faster-scaling-with-redis-hashing-policy","title":"Faster scaling with Redis hashing policy","role":"content","text":"Starting June 24, 2025, accounts with the [Redis hashing policy](https://redis.io/docs/latest/operate/rc/databases/configuration/clustering#redis-hashing-policy) can create databases that scale faster. This makes resharding operations up to 40% faster than before.\n\nFaster scaling requires a new database created after June 24, 2025, running Redis 7.4 or later, with the [Redis hashing policy](https://redis.io/docs/latest/operate/rc/databases/configuration/clustering#redis-hashing-policy) selected.\n\nFor these databases, this enhancement is enabled automatically, ensuring that all future resharding operations complete faster than ever."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/june-2026","title":"Redis Cloud changelog (June 2026)","url":"https://redis.io/docs/latest/operate/rc/changelog/june-2026/","summary":"New features, enhancements, and other changes added to Redis Cloud during June 2026.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:13+01:00","page_type":"content","content_hash":"6d846638bc28065c0a519b3a85430427fb29deb7a90419acb310da39035b58ec","sections":[{"id":"new-features","title":"New features","role":"overview","text":""},{"id":"billing-admin-api-access","title":"Billing Admin API access","role":"content","text":"Users with the [Billing Admin role](https://redis.io/docs/latest/operate/rc/security/access-control/access-management#team-management-roles) can now use the [Redis Cloud REST API](https://redis.io/docs/latest/operate/rc/api) to perform billing operations. See [Team management roles](https://redis.io/docs/latest/operate/rc/security/access-control/access-management#team-management-roles) for the full list of permissions for each role."},{"id":"redis-data-integration","title":"Redis Data Integration","role":"content","text":"[Redis Data Integration (RDI)](https://redis.io/docs/latest/operate/rc/rdi) is now generally available on Redis Cloud. RDI lets you create data pipelines that ingest data from a supported primary database into Redis, so your cache stays in sync with live data and is always ready for queries.\n\nSee [Data Integration](https://redis.io/docs/latest/operate/rc/rdi) to learn how to set up data pipelines with Redis Cloud."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/march-2025","title":"Redis Cloud changelog (March 2025)","url":"https://redis.io/docs/latest/operate/rc/changelog/march-2025/","summary":"New features, enhancements, and other changes added to Redis Cloud during March 2025.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:13+01:00","page_type":"content","content_hash":"c099f935fa6970c2dfd095867551ad48cf824f5d18a0e262eaf20db1cde453f1","sections":[{"id":"new-features","title":"New features","role":"overview","text":""},{"id":"redis-insight-on-redis-cloud","title":"Redis Insight on Redis Cloud","role":"content","text":"Users with select Redis Cloud Essentials databases can now open a browser-based version of [Redis Insight](https://redis.io/docs/latest/operate/rc/databases/connect/insight-cloud) directly from Redis Cloud. See [Connect to your database](https://redis.io/docs/latest/operate/rc/databases/connect#ri-browser) to learn how to open Redis Insight from Redis Cloud.\n\nThis browser-based version of Redis Insight has a subset of the features of Redis Insight on desktop. For more information, see [Open with Redis Insight on Redis Cloud](https://redis.io/docs/latest/operate/rc/databases/connect/insight-cloud)."},{"id":"redis-hashing-policy","title":"Redis hashing policy","role":"content","text":"Accounts created after March 31, 2025, can select the new [Redis hashing policy](https://redis.io/docs/latest/operate/rc/databases/configuration/clustering#redis-hashing-policy) for their databases when creating a new database. \n\nThe Redis hashing policy is identical to the [hashing policy used by Redis Community Edition](https://redis.io/docs/latest/operate/oss_and_stack/reference/cluster-spec#hash-tags). This policy is recommended for most users. Select it if any of the following conditions apply:\n- This is your first Redis Cloud account, and you are starting fresh.\n- You are migrating data from Redis Community Edition or other Redis-managed platforms.\n- Your application does not use hashtags in database key names.\n- Your application uses binary data as key names.\n\nSee [Clustering](https://redis.io/docs/latest/operate/rc/databases/configuration/clustering#manage-the-hashing-policy) for more information."},{"id":"deprecations","title":"Deprecations","role":"content","text":"- The [Custom hashing policy](https://redis.io/docs/latest/operate/rc/databases/configuration/clustering#custom-hashing-policy) is no longer available for accounts created after March 31, 2025. For all other accounts, this policy is not recommended and will be deprecated in the future. Only select a custom hashing policy if you are already using a custom hashing policy with your existing Redis Cloud databases."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/march-2026","title":"Redis Cloud changelog (March 2026)","url":"https://redis.io/docs/latest/operate/rc/changelog/march-2026/","summary":"New features, enhancements, and other changes added to Redis Cloud during March 2026.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:13+01:00","page_type":"content","content_hash":"7ceb8c103bcd4403557097e72cb2bb687551f5817b0fbbd7a33547edc25c2f8c","sections":[{"id":"new-features","title":"New features","role":"overview","text":""},{"id":"passwordless-authentication-for-redis-cloud-pro","title":"Passwordless authentication for Redis Cloud Pro","role":"content","text":"Passwordless authentication is now available for Redis Cloud Pro databases on subscriptions that have [blocked the public endpoint](https://redis.io/docs/latest/operate/rc/security/database-security/block-public-endpoints). For more information, see [Turn on passwordless authentication for the default user](https://redis.io/docs/latest/operate/rc/security/database-security/block-public-endpoints#turn-on-passwordless-authentication-for-the-default-user)."},{"id":"redis-84-on-redis-cloud-pro","title":"Redis 8.4 on Redis Cloud Pro","role":"content","text":"Redis 8.4 is now available for [Redis Cloud Pro databases](https://redis.io/docs/latest/operate/rc/databases/create-database/create-essentials-database) in select regions.\n\nRedis 8.4 builds on the foundation of Redis 8.2 with significant enhancements to cluster operations, string manipulation, and stream processing capabilities. For more information on the changes in Redis 8.4, see [What's new in Redis 8.4](https://redis.io/docs/latest/develop/whats-new/8-4) and review the Redis Open Source [8.4 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.4-release-notes)."},{"id":"automatic-database-upgrades","title":"Automatic database upgrades","role":"content","text":"All Redis Cloud databases running Redis 8.4 and later will be automatically upgraded to the next minor version upon release. For example, all Redis 8.4 databases will be upgraded to Redis 8.6 when it is available on Redis Cloud.\n\nRedis Cloud Pro users can opt out of minor version auto-upgrades. See [Version management](https://redis.io/docs/latest/operate/rc/databases/version-management) for more details."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/may-2025","title":"Redis Cloud changelog (May 2025)","url":"https://redis.io/docs/latest/operate/rc/changelog/may-2025/","summary":"New features, enhancements, and other changes added to Redis Cloud during May 2025.","tags":["docs","operate","rc"],"last_updated":"2026-10-01T15:21:44+01:00","page_type":"content","content_hash":"4b1e63e8b99c218479d405e238c3a00880b6db5ac6bb99cb2f8bf6d26bbfa09e","sections":[{"id":"new-features","title":"New features","role":"overview","text":""},{"id":"upgrade-database-version-for-a-single-pro-database","title":"Upgrade database version for a single Pro database","role":"content","text":"Selected accounts can now upgrade a single Pro database to a later version. From the database page or database list, select **More actions > Version upgrade** to upgrade your database.\n\n![The More Actions menu on the Database page.](https://redis.io/docs/latest/images/rc/databases-more-actions-menu.png)\n\nSee [Upgrade database version](https://redis.io/docs/latest/operate/rc/databases/version-management/upgrade-version) for more information."},{"id":"business-address-and-billing-address-separation","title":"Business address and billing address separation","role":"content","text":"You can now have multiple credit cards with different billing addresses. See [Billing and payments](https://redis.io/docs/latest/operate/rc/billing-and-payments) to learn how to add a new credit card.\n\nYour account now also has a **Business address**, which you can find in [Account settings](https://redis.io/docs/latest/operate/rc/accounts/account-settings)."},{"id":"enhancements","title":"Enhancements","role":"content","text":""},{"id":"redis-cloud-bring-your-own-cloud","title":"Redis Cloud Bring your own Cloud","role":"content","text":"AWS Cloud Accounts are now known as Redis Cloud Bring your own Cloud (BYOC). See [Redis Cloud Bring your own Cloud](https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud) to learn how to use your own AWS cloud infrastructure to deploy Redis Cloud."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/may-2026","title":"Redis Cloud changelog (May 2026)","url":"https://redis.io/docs/latest/operate/rc/changelog/may-2026/","summary":"New features, enhancements, and other changes added to Redis Cloud during May 2026.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:13+01:00","page_type":"content","content_hash":"879a4ca3f840ee0376872a2674e3970719826ceef498d69b1ac4ae4ee97fdf7b","sections":[{"id":"new-features","title":"New features","role":"overview","text":""},{"id":"redis-iris","title":"Redis Iris","role":"content","text":"Redis Iris is a suite of fully-managed services that give AI agents the context engine they need to reliably act on business data. Both [Redis Agent Memory](https://redis.io/docs/latest/operate/iris/agent-memory) and [Redis Context Retriever](https://redis.io/docs/latest/operate/iris/context-retriever) are now available on Redis Cloud as a public preview."},{"id":"bring-your-own-cloud-resource-tags","title":"Bring your own Cloud resource tags","role":"content","text":"Accounts with [Redis Cloud Bring your own Cloud (BYOC)](https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud) enabled can now add Cloud Provider resource tags to their BYOC deployments. See [Resource tags](https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud/resource-tags) for more information."},{"id":"redis-86-on-redis-cloud-essentials","title":"Redis 8.6 on Redis Cloud Essentials","role":"content","text":"Redis 8.6 is now available for [Redis Cloud Essentials databases](https://redis.io/docs/latest/operate/rc/databases/create-database/create-essentials-database) in select regions."},{"id":"dynamic-endpoints","title":"Dynamic endpoints","role":"content","text":"Redis Cloud is gradually rolling out dynamic endpoints. During the rollout, some databases will display both a dynamic endpoint and a legacy static endpoint, while some newly created databases will display only a dynamic endpoint. Previously assigned static endpoints will continue to work for now but may be deprecated in the future.\n\nStatic endpoints on Redis Cloud start with `redis-<port>.c<number>` (or `redis-<port>.internal.c<number>` for the private endpoint). For example, a static endpoint might look like this:\n\n[code example]\n\nDynamic endpoints on Redis Cloud always contain three words and a random number, and end in `db.redis.io`. For example, a dynamic endpoint might look like this:\n\n[code example]\n\nYou can see the Dynamic endpoints for databases with both static and dynamic endpoints by expanding the **Dynamic endpoints** section in the **General** section of the **Configuration** tab. \n\n![The Static and dynamic endpoints for a database with both kinds of endpoints.](https://redis.io/docs/latest/images/rc/databases-configuration-general-endpoints-legacy.png)\n\nWe recommend slowly migrating connections to the dynamic endpoints. Moving connections from the static endpoints to the dynamic endpoints does not cause any downtime. See [Applications that use legacy static endpoints](https://redis.io/docs/latest/operate/rc/databases/redirect-endpoints#applications-that-use-legacy-static-endpoints) for more information."},{"id":"redirect-dynamic-endpoints","title":"Redirect dynamic endpoints","role":"content","text":"> [!NOTE]\n> Dynamic endpoint redirection is currently in public preview. Features and behavior are subject to change.\n\nYou can redirect your dynamic endpoints to any Redis Cloud Pro database in the same account. Redirecting your dynamic endpoints lets you switch connections to your new database seamlessly through Redis Cloud without any code changes. See [Redirect database endpoints](https://redis.io/docs/latest/operate/rc/databases/redirect-endpoints) for more information."},{"id":"enhancements","title":"Enhancements","role":"content","text":""},{"id":"metrics-resolution-updates","title":"Metrics resolution updates","role":"content","text":"> [!NOTE]\n> This change only affects Redis on RAM databases. Flex and Auto Tiering databases continue to use the previous metrics time ranges.\n\nThe [metrics tab of a Redis Cloud database](https://redis.io/docs/latest/operate/rc/databases/monitor-performance#view-database-metrics) now displays database metrics at different levels of granularity depending on the selected time range. Short time ranges now show finer granularity for detailed investigation, and longer time ranges use aggregated intervals for clearer trend analysis. See [Metric intervals](https://redis.io/docs/latest/operate/rc/databases/monitor-performance#metric-intervals) for a list of available intervals and their corresponding resolutions.\n\nTo simplify the monitoring experience and ensure each time range provides meaningful insight, the 1 minute and 1 year ranges have been removed. The 1 year range has been replaced with a 3 month range."}],"examples":[{"id":"dynamic-endpoints-ex0","language":"text","code":"redis-12345.c12345.us-east-1-mz.ec2.cloud.rlrcp.com","section_id":"dynamic-endpoints"},{"id":"dynamic-endpoints-ex1","language":"text","code":"horse-battery-staple-12345.db.redis.io","section_id":"dynamic-endpoints"}]}
{"schema_version":2,"id":"operate/rc/changelog/november-2025","title":"Redis Cloud changelog (November 2025)","url":"https://redis.io/docs/latest/operate/rc/changelog/november-2025/","summary":"New features, enhancements, and other changes added to Redis Cloud during November 2025.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:13+01:00","page_type":"content","content_hash":"241163c92f3c9033394c74669ade7bd6d54aa47bf8ad52757c046f48ab5f749c","sections":[{"id":"new-features","title":"New features","role":"overview","text":""},{"id":"redis-82-on-redis-cloud-pro","title":"Redis 8.2 on Redis Cloud Pro","role":"content","text":"Redis 8.2 is now available on Redis Cloud Pro. You can now upgrade your existing Pro database to Redis 8.2. \n\nSee [upgrade database version](https://redis.io/docs/latest/operate/rc/databases/version-management/upgrade-version) to learn how to upgrade your existing Pro database to the latest version. If you are upgrading to Redis 8.2 from Redis 7.4 or earlier, review the [Redis 8.0 breaking changes](https://redis.io/docs/latest/operate/rc/changelog/version-release-notes/8-0#breaking-changes) before upgrading."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/october-2025","title":"Redis Cloud changelog (October 2025)","url":"https://redis.io/docs/latest/operate/rc/changelog/october-2025/","summary":"New features, enhancements, and other changes added to Redis Cloud during October 2025.","aliases":["/operate/rc/changelog/september-2025/"],"tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:13+01:00","page_type":"content","content_hash":"eaac4206825b7e26c505cbbb41435f37022b643e65971fb67c99489d60ed87ed","sections":[{"id":"new-features","title":"New features","role":"overview","text":""},{"id":"data-integration","title":"Data Integration","role":"content","text":"Redis Cloud now supports [Redis Data Integration (RDI)](https://redis.io/docs/latest/operate/rc/rdi) to create data pipelines that ingest data from a supported primary database to Redis. \n\nUsing a data pipeline lets you have a cache that is always ready for queries. RDI Data pipelines ensure that any changes made to your primary database are captured in your Redis cache within a few seconds, preventing cache misses and stale data within the cache. \n\nSee [Data Integration](https://redis.io/docs/latest/operate/rc/rdi) to learn how to set up data pipelines with Redis Cloud."},{"id":"security-fixes","title":"Security fixes","role":"security","text":"Redis Cloud has already been updated with a patches for the following vulnerabilities: \n\n- (CVE-2025-49844) A Lua script may lead to remote code execution\n- (CVE-2025-46817) A Lua script may lead to integer overflow and potential RCE\n- (CVE-2025-46818) A Lua script can be executed in the context of another user\n- (CVE-2025-46819) LUA out-of-bound read\n\nNo further action is required at this time. \n\nFor more information, see the [Redis blog](https://redis.io/blog/security-advisory-cve-2025-49844/) and the [Redis release notes](https://github.com/redis/redis/releases)."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/september-2026","title":"Redis Cloud changelog (September 2026)","url":"https://redis.io/docs/latest/operate/rc/changelog/september-2026/","summary":"New features, enhancements, and other changes added to Redis Cloud during September 2026.","tags":["docs","operate","rc"],"last_updated":"2026-10-05T16:05:04-07:00","page_type":"content","content_hash":"590b0715281c78bc2b7c9a4f0669b81063d9ab47d917df5e115ebe9a290ff9fd","sections":[{"id":"new-features","title":"New features","role":"overview","text":""},{"id":"redis-search-on-flex-beta","title":"Redis Search on Flex (beta)","role":"content","text":"[Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) is now available as a beta feature for [Flex databases](https://redis.io/docs/latest/operate/rc/databases/create-database/create-flex-database) on Redis Cloud Pro. To use it, enable the beta flag for Redis Search on Flex for your Redis Cloud Pro subscription in the Redis Cloud console.\n\nThe beta supports HASH documents with `TEXT`, `TAG`, and `VECTOR` fields. JSON documents, `NUMERIC` and `GEO` fields, `FT.AGGREGATE`, `FT.HYBRID`, and background indexing aren't available yet. See [Redis Search on Flex](https://redis.io/docs/latest/operate/rc/databases/create-database/create-flex-database#redis-search-on-flex-beta) for the full list of supported features."},{"id":"multiple-sources-in-data-integration-pipelines","title":"Multiple sources in Data Integration pipelines","role":"content","text":"[Redis Data Integration (RDI)](https://redis.io/docs/latest/operate/rc/rdi) on Redis Cloud now uses RDI 2.0.0. A Data Integration pipeline can now connect one or more source databases to one target Redis database. Sources can use the same or different database types. Each source has its own connectivity, credentials, and selection of tables and columns, and you can monitor and manage each source from the pipeline dashboard.\n\nAll Data Integration pipelines now use the Flink processor. See the [RDI Cloud FAQ](https://redis.io/docs/latest/operate/rc/rdi/faq#multiple-sources) for how resetting, flushing, and deleting work with multiple sources."},{"id":"redis-radar","title":"Redis Radar","role":"content","text":"[Redis Radar](https://redis.io/docs/latest/operate/rc/radar) gives you one view of the status of every Redis cluster you run, across Redis Software, Redis Cloud, Redis Open Source, Amazon ElastiCache, and Google Memorystore. Redis Cloud's hosted Radar needs no setup. Go to [`radar.redis.io`](http://radar.redis.io) and sign in with your Redis Cloud credentials."},{"id":"enhancements","title":"Enhancements","role":"content","text":""},{"id":"connect-to-aws-privatelink-from-on-premises","title":"Connect to AWS PrivateLink from on-premises","role":"content","text":"You can now connect to a database over [AWS PrivateLink](https://redis.io/docs/latest/operate/rc/security/aws-privatelink) from an on-premises network connected to your consumer virtual private cloud (VPC) over AWS Direct Connect or a virtual private network (VPN). See [Connect from on-premises](https://redis.io/docs/latest/operate/rc/security/aws-privatelink#connect-from-on-premises) for the Domain Name System (DNS) options, including which ones work with Transport Layer Security (TLS)."},{"id":"single-default-credit-card","title":"Single default credit card","role":"content","text":"Your Redis Cloud account now uses a single default credit card for all active subscriptions. You can [set the default credit card](https://redis.io/docs/latest/operate/rc/billing-and-payments#set-the-default-credit-card) from **Billing & Payments > Payment Methods**.\n\nIf your account has more than one credit card in use across different subscriptions, an Account Admin or Billing Admin must choose a single default card. Starting October 1, 2026, that admin sees a dialog on login and must confirm a default card before continuing to the console."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/version-release-notes/7-2","title":"Redis 7.2 release notes and breaking changes","url":"https://redis.io/docs/latest/operate/rc/changelog/version-release-notes/7-2/","summary":"Release notes and breaking changes for Redis 7.2 on Redis Cloud.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:13+01:00","page_type":"content","content_hash":"0e34900b1b626b6b2f1c89517376970d94bbd8ba1b1108d47fc3b661132849de","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis 7.2 introduces several changes to existing Redis commands. For more information on the changes in Redis 7.2, see [What's new in Redis 7.2](https://redis.io/docs/latest/develop/whats-new/7-2)."},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":""},{"id":"redis-72-breaking-changes","title":"Redis 7.2 breaking changes","role":"content","text":"When new major versions of open source Redis change existing commands, upgrading your database to a new version can potentially break some functionality. Before you upgrade, make sure to read the provided list of breaking changes that affect Redis Enterprise and update any applications that connect to your database to handle these changes.\n\nConfirm your Redis database version (`redis_version`) using the admin console or run the following [`INFO`](https://redis.io/docs/latest/commands/info) command via [`redis-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/redis-cli):\n\n[code example]\n\n#### Breaking changes from version 7.0\n\nUpgrading to Redis version 7.2 from version 7.0 introduces the following potentially breaking changes to Redis Enterprise:\n\n##### Programmability\n\n- Time sampling is now frozen during command execution and scripts ([#10300](https://github.com/redis/redis/pull/10300)). While a command or script is running, the keys used by the command or script will not expire. This breaks any script that uses a loop to wait for a key to expire. \n\n- Blocked commands in scripts now work the same way as when they are used in [transactions](https://redis.io/docs/latest/develop/using-commands/transactions) ([#11568](https://github.com/redis/redis/pull/11568)). \n\n##### Error handling\n\n- A blocked stream command that is released when a key no longer exists returns a different error code ([#11012](https://github.com/redis/redis/pull/11012)).\n    - For newly unblocked streams, lists, and zsets, the old implementation returned `UNBLOCKED` when the stream key was deleted or overwritten with a different type. Now, errors will be the same as if the command was processed after the effect.\n\n- ACL errors have been unified across Redis. ([#11160](https://github.com/redis/redis/pull/11160))\n    - When using [RedisModule_Call](https://redis.io/docs/latest/develop/reference/modules/modules-api-ref#redismodule_call) module API function, ACL errors return `-NOPERM` instead of `-ERR`\n\n- [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) and [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) create a consumer regardless of whether it was able to perform reading or claiming ([#11012](https://github.com/redis/redis/pull/11012)).\n\n- Any float that is Not a Number will return `nan` ([#11597](https://github.com/redis/redis/pull/11597)).\n\n##### ACLs\n\n- When a blocked client is being unblocked, checks for ACLs and OOM condition checks are now re-evaluated ([#11012](https://github.com/redis/redis/pull/11012)).\n    - If the ACL rules have changed since the command was executed, the command might fail after the client is unblocked.\n\n##### Command introspection, stats, and configuration\n\n- When running [`XINFO CONSUMERS`](https://redis.io/docs/latest/commands/xinfo-consumers), the `idle` time now shows the number of milliseconds that have passed since the last attempted interaction, and the `inactive` time shows the number of milliseconds since the last successful interaction ([#11099](https://github.com/redis/redis/pull/11099))\n    - Previously, `idle` time showed the number of milliseconds that passed since the last successful interaction and there was no `inactive` time.\n\n- Command stats are only updated when the command executes ([#11012](https://github.com/redis/redis/pull/11012)).\n    - Previously, the command stats were updated even if a command was blocked. The command stats are now updated only if and when the command is executed.\n\n#### Breaking changes from version 6.2\n\nUpgrading to open source Redis version 7.2 from version 6.2 introduces the following potentially breaking changes to Redis Enterprise.\n\nAll [breaking changes from version 7.0](#breaking-changes-from-version-70) also apply to Redis version 6.2.\n\n##### Programmability\n\n-  Lua scripts no longer have access to the `print()` function ([#10651](https://github.com/redis/redis/pull/10651)) - The `print`  function was removed from Lua because it can potentially cause the Redis processes to get stuck (if no one reads from stdout). Users should use redis.log. An alternative is to override the  `print`  implementation and print the message to the log file.  \n\n- Block [`PFCOUNT`](https://redis.io/docs/latest/commands/pfcount) and [`PUBLISH`](https://redis.io/docs/latest/commands/publish) in read-only scripts (*_RO commands,  `no-writes`) ([#10744](https://github.com/redis/redis/pull/10744)) - Consider `PFCOUNT` and `PUBLISH` as write commands in scripts, in addition to `EVAL`; meaning:\n  - They can never be used in scripts with shebang (`#!`) and no `no-writes` flag\n  - They are blocked in `EVAL_RO` and `_RO` variants, (even in scripts without shebang (`#!`) flags)\n  - Allow `no-write` scripts in EVAL (not just in EVAL_RO), even during `CLIENT PAUSE WRITE` \n\n- Hide the `may_replicate` flag from the [`COMMAND`](https://redis.io/docs/latest/commands/command) command response  ([#10744](https://github.com/redis/redis/pull/10744)) - As part of the change to treat `may_replicate` commands `PFCOUNT` and `PUBLISH` as write commands in scripts, in addition to `EVAL`, the `may_replicate` flag has been removed from the `COMMAND` response.\n\n##### Error handling\n\n- Rephrased some error responses about invalid commands or arguments ([#10612](https://github.com/redis/redis/pull/10612)) - \n  - Error response for unknown command introduced a case change (`Unknown` to `unknown`)\n  - Errors for module commands extended to cover subcommands, updated syntax to match Redis Server syntax\n  - Arity errors for module commands introduce a case change (`Wrong` to `wrong`); will consider full command name\n\n- Corrected error codes returned from [`EVAL`](https://redis.io/docs/latest/commands/eval) scripts ([#10218](https://github.com/redis/redis/pull/10218), [#10329](https://github.com/redis/redis/pull/10329)). \n\n  These examples show changes in behavior:\n\n  [code example]\n\n- [`ZPOPMIN`](https://redis.io/docs/latest/commands/zpopmin)/[`ZPOPMAX`](https://redis.io/docs/latest/commands/zpopmax) used to produce wrong replies when count is 0 with non-zset  [#9711](https://github.com/redis/redis/pull/9711)):\n  -  `ZPOPMIN`/`ZPOPMAX` used to produce an `(empty array)` when `key` was not a sorted set and the optional `count` argument was set to `0` and now produces a `WRONGTYPE` error response instead.\n  -  The optional `count` argument must be positive. A negative value produces a `value is out of range` error.\n\n  These examples show changes in behavior:\n\n  [code example]\n\n- [`LPOP`](https://redis.io/docs/latest/commands/lpop)/[`RPOP`](https://redis.io/docs/latest/commands/rpop) with count against a nonexistent list returns a null array instead of `(nil)`([#10095](https://github.com/redis/redis/pull/10095)). This change was backported to 6.2.\n\n- [`LPOP`](https://redis.io/docs/latest/commands/lpop)/[`RPOP`](https://redis.io/docs/latest/commands/rpop) used to produce `(nil)` when count is 0, now produces a null array ([#9692](https://github.com/redis/redis/pull/9692)). This change was backported to 6.2.\n\n- [`XCLAIM`](https://redis.io/docs/latest/commands/xclaim)/[`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) skips deleted entries instead of replying with `nil` and deletes them from the pending entry list ([#10227](https://github.com/redis/redis/pull/10227)) - `XCLAIM`/`XAUTOCLAIM` now behaves in the following way:\n  - If you try to claim a deleted entry, it is deleted from the pending entry list (PEL) where it is found (as well as the group PEL). Therefore, such an entry is not claimed, just cleared from PEL (because it doesn't exist in the stream anyway).\n  - Because deleted entries are not claimed, `X[AUTO]CLAIM` does not return \"nil\" instead of an entry.\n  - Added an array of all the deleted stream IDs to `XAUTOCLAIM` response.\n\n#####  ACLs\n\n- [`ACL GETUSER`](https://redis.io/docs/latest/commands/acl-getuser) reply now uses ACL syntax for `keys` and `channels` ([#9974](https://github.com/redis/redis/pull/9974)). `ACL GETUSER` now uses the ACL DSL (Domain Specific Language) for keys and channels. \n\n  These examples show changes in behavior:\n  [code example]\n\n- [`SORT`](https://redis.io/docs/latest/commands/sort)/[`SORT_RO`](https://redis.io/docs/latest/commands/sort_ro) commands reject key access patterns in `GET` and `BY` if the ACL doesn't grant the command full keyspace access ([#10340](https://github.com/redis/redis/pull/10340)) - The `sort` and `sort_ro` commands can access external keys via `GET` and `BY`. In order to make sure the user cannot violate the authorization ACL rules, Redis 7 will reject external keys access patterns unless ACL allows `SORT` full access to all keys.\nFor backwards compatibility, `SORT` with `GET`/`BY` keeps working, but if ACL has restrictions to certain keys, the use of these features will result in a permission denied error.\n\n  These examples show changes in behavior:\n\n  [code example]\n\n  [code example]\n\n  [code example]\n\n  [code example]\n\n- Fix ACL category for [`SELECT`](https://redis.io/docs/latest/commands/select), [`WAIT`](https://redis.io/docs/latest/commands/wait, [`ROLE`](https://redis.io/docs/latest/commands/role, [`LASTSAVE`](https://redis.io/docs/latest/commands/lastsave, [`READONLY`](https://redis.io/docs/latest/commands/readonly, [`READWRITE`](https://redis.io/docs/latest/commands/readwrite, [`ASKING`](https://redis.io/docs/latest/commands/asking) ([#9208](https://github.com/redis/redis/pull/9208)):\n  - `SELECT` and `WAIT` have been recategorized from `@keyspace` to `@connection`  \n  - `ROLE`, `LASTSAVE` have been categorized as  `@admin`  and  `@dangerous`\n  -  `ASKING`, `READONLY`, `READWRITE` have also been assigned the  `@connection`  category and  removed from `@keyspace`\n  - Command categories are explained in [ACL documentation](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl#command-categories)\n\n##### Command introspection, stats, and configuration\n\n- [`COMMAND`](https://redis.io/docs/latest/commands/command) reply drops `random` and `sort-for-scripts` flags, which are now part of [command tips](https://redis.io/docs/latest/develop/reference/command-tips) ([#10104](https://github.com/redis/redis/pull/10104)) - The `random` flag was replaced with the `nondeterministic_output` tip; the `sort-for-scripts` flag was replaced by the ` nondeterministic_output_order` tip\n\n- [`INFO`](https://redis.io/docs/latest/commands/info) `commandstats` now shows the stats per sub-command ([#9504](https://github.com/redis/redis/pull/9504))\nFor example, while previous versions would provide a single entry for all command usage, in Redis 7, each sub command is reported separately:\n\n  - Redis 6.2:\n  \n    [code example]\n\n  - Redis 7:\n  \n    [code example]\n\n- [`CONFIG REWRITE`](https://redis.io/docs/latest/commands/config-rewrite), [`CONFIG RESETSTAT`](https://redis.io/docs/latest/commands/config-resetstat, and most [`CONFIG SET`](https://redis.io/docs/latest/commands/config-setcommands are now allowed during loading ([#9878](https://github.com/redis/redis/pull/9878))"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"There are two known limitations for the version of Redis 7.2 that is packaged with Redis Enterprise.\n\n- Using [`XREADGROUP BLOCK`](https://redis.io/docs/latest/commands/xreadgroup) with `>` to return all new streams will cause the Redis database to freeze until the shard is restarted ([#12031](https://github.com/redis/redis/pull/12301))\n\n- Because a rejected command does not record the duration for command stats, an error will appear after it is reprocessed that will cause the Redis database to freeze until the shard is restarted ([#12247](https://github.com/redis/redis/pull/12247))"},{"id":"migration-guides-for-certain-clients","title":"Migration guides for certain clients","role":"content","text":"If you are using [Go-Redis](https://redis.uptrace.dev/) v9 or [Lettuce](https://redis.github.io/lettuce/) v6+, which by default switch to the RESP3 protocol, and you are running Redis Stack commands, please make sure the protocol version is pinned to RESP2 prior to upgrading to Redis version 7.2.\n\n#### Go-Redis\n\nFor applications using Go-Redis v9 and later, ensure that you have Go-Redis v9.0.5 or newer and pin the protocol version as shown below:\n\n[code example]\n\nNo actions are required for applications using Go-Redis v8 or earlier.\n\n#### Lettuce\n\nTo pin the protocol version, use the snippet below:\n\n[code example]\n\nIf you are using [LettuceMod](https://github.com/redis-developer/lettucemod/), you need to upgrade to [v3.6.0](https://github.com/redis-developer/lettucemod/releases/tag/v3.6.0)."}],"examples":[{"id":"redis-72-breaking-changes-ex0","language":"sh","code":"$ redis-cli -p <port> INFO\n\"# Server\nredis_version:7.0.8\n...\"","section_id":"redis-72-breaking-changes"},{"id":"redis-72-breaking-changes-ex1","language":"diff","code":"1: config set maxmemory 1\n    2: +OK\n    3: eval \"return redis.call('set','x','y')\" 0\n  - 4: -ERR Error running script (call to 71e6319f97b0fe8bdfa1c5df3ce4489946dda479): @user_script:1: @user_script: 1: -OOM command not allowed when used memory > 'maxmemory'.\n  + 4: -ERR Error running script (call to 71e6319f97b0fe8bdfa1c5df3ce4489946dda479): @user_script:1: OOM command not allowed when used memory > 'maxmemory'.\n    5: eval \"return redis.pcall('set','x','y')\" 0\n  - 6: -@user_script: 1: -OOM command not allowed when used memory > 'maxmemory'.\n  + 6: -OOM command not allowed when used memory > 'maxmemory'.\n    7: eval \"return redis.call('select',99)\" 0\n    8: -ERR Error running script (call to 4ad5abfc50bbccb484223905f9a16f09cd043ba8): @user_script:1: ERR DB index is out of range\n    9: eval \"return redis.pcall('select',99)\" 0\n   10: -ERR DB index is out of range\n   11: eval_ro \"return redis.call('set','x','y')\" 0\n  -12: -ERR Error running script (call to 71e6319f97b0fe8bdfa1c5df3ce4489946dda479): @user_script:1: @user_script: 1: Write commands are not allowed from read-only scripts.\n  +12: -ERR Error running script (call to 71e6319f97b0fe8bdfa1c5df3ce4489946dda479): @user_script:1: ERR Write commands are not allowed from read-only scripts.\n   13: eval_ro \"return redis.pcall('set','x','y')\" 0\n  -14: -@user_script: 1: Write commands are not allowed from read-only scripts.\n  +14: -ERR Write commands are not allowed from read-only scripts.","section_id":"redis-72-breaking-changes"},{"id":"redis-72-breaking-changes-ex2","language":"diff","code":"1: zadd myzset 1 \"one\"\n    2: (integer) 1\n    3: zadd myzset 2 \"two\"\n    4: (integer) 1\n    5: zadd myzset 3 \"three\"\n    6: (integer) 1\n    7: zpopmin myzset -1\n  - 8: (empty array)\n  + 8: (error) ERR value is out of range, must be positive\n    9: 127.0.0.1:6379> set foo bar\n   10: OK\n   11: zpopmin foo 0\n  -12: (empty array)\n  +12: (error) WRONGTYPE Operation against a key holding the wrong kind of value","section_id":"redis-72-breaking-changes"},{"id":"redis-72-breaking-changes-ex3","language":"diff","code":"1: acl setuser foo off resetchannels &channel1 -@all +get\n    2: OK\n    3: acl getuser foo\n    4: 1) \"flags\"\n    5: 2) 1) \"off\"\n    6: 3) \"passwords\"\n    7: 4) (empty array)\n    8: 5) \"commands\"\n    9: 6) \"-@all +get\"\n   10: 7) \"keys\"\n  -11: 8) (empty array)\n  +11: 8) \"\"\n   12: 9)\"channels\"\n  -13 10) 1) \"channel1\"\n  +13 10) \"&channel1\"","section_id":"redis-72-breaking-changes"},{"id":"redis-72-breaking-changes-ex4","language":"plaintext","code":"USER FOO (+sort ~* ~mylist) \n  #FOO> sort mylist by w* get v*  - is O.K since ~* provides full key access","section_id":"redis-72-breaking-changes"},{"id":"redis-72-breaking-changes-ex5","language":"plaintext","code":"USER FOO (+sort %R~* ~mylist) \n  #FOO> sort mylist by w* get v*  - is O.K since %R~* provides full key READ access**","section_id":"redis-72-breaking-changes"},{"id":"redis-72-breaking-changes-ex6","language":"plaintext","code":"USER FOO (+sort %W~* ~mylist)\n  #FOO> sort mylist by w* get v*  - will now fail since $W~* only provides full key WRITE access","section_id":"redis-72-breaking-changes"},{"id":"redis-72-breaking-changes-ex7","language":"plaintext","code":"USER FOO (+sort ~v* ~mylist)\n  #FOO> sort mylist by w* get v*  - will now fail since ~v* only provides partial key access","section_id":"redis-72-breaking-changes"},{"id":"redis-72-breaking-changes-ex8","language":"plaintext","code":"cmdstat_acl:calls=4,usec=279,usec_per_call=69.75,rejected_calls=0,failed_calls=2","section_id":"redis-72-breaking-changes"},{"id":"redis-72-breaking-changes-ex9","language":"  ","code":"cmdstat_acl|list:calls=1,usec=4994,usec_per_call=4994.00,rejected_calls=0,failed_calls=0\n    cmdstat_acl|setuser:calls=2,usec=16409,usec_per_call=8204.50,rejected_calls=0,failed_calls=0\n    cmdstat_acl|deluser:calls=1,usec=774,usec_per_call=774.00,rejected_calls=0,failed_calls=0\n    cmdstat_acl|getuser:calls=1,usec=6044,usec_per_call=6044.00,rejected_calls=0,failed_calls=0","section_id":"redis-72-breaking-changes"},{"id":"migration-guides-for-certain-clients-ex0","language":"go","code":"client := redis.NewClient(&redis.Options{\n    Addr:     \"<database_endpoint>\",\n    Protocol: 2, // Pin the protocol version\n})","section_id":"migration-guides-for-certain-clients"},{"id":"migration-guides-for-certain-clients-ex1","language":"java","code":"import io.lettuce.core.*;\nimport io.lettuce.core.api.*;\nimport io.lettuce.core.protocol.ProtocolVersion;\n\n// ...\nRedisClient client = RedisClient.create(\"<database_endpoint>\");\nclient.setOptions(ClientOptions.builder()\n        .protocolVersion(ProtocolVersion.RESP2) // Pin the protocol version \t\n        .build());\n// ...","section_id":"migration-guides-for-certain-clients"}]}
{"schema_version":2,"id":"operate/rc/changelog/version-release-notes/7-4","title":"Redis 7.4 release notes and breaking changes","url":"https://redis.io/docs/latest/operate/rc/changelog/version-release-notes/7-4/","summary":"Release notes and breaking changes for Redis 7.4 on Redis Cloud.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:13+01:00","page_type":"content","content_hash":"26804aa77a91ba257fe30d31fa82ab86db26d5fac08916e4c19bb72070939f1e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis 7.4 offers hash field expiration and other feature set improvements. For more information on the changes in Redis 7.4, see [What's new in Redis 7.4](https://redis.io/docs/latest/develop/whats-new/7-4)."},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"When new versions of Redis Community Edition change existing commands, upgrading your database to a new version can potentially break some functionality. Before you upgrade, read the provided list of breaking changes that affect Redis Cloud and update any applications that connect to your database to handle these changes.\n\nIf you're upgrading from Redis 6.4, also review the [Redis 7.2 breaking changes](https://redis.io/docs/latest/operate/rc/changelog/version-release-notes/7-2#breaking-changes) before upgrading."},{"id":"security-behavior-changes","title":"Security behavior changes","role":"security","text":"- [#13108](https://github.com/redis/redis/pull/13108) Lua: LRU eviction for scripts generated with `EVAL`."},{"id":"other-general-behavior-changes","title":"Other general behavior changes","role":"content","text":"- [#13133](https://github.com/redis/redis/pull/13133) Lua: allocate VM code with jemalloc instead of libc and count it as used memory.\n\n- [#12171](https://github.com/redis/redis/pull/12171) `ACL LOAD`: do not disconnect all clients."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/version-release-notes/8-0","title":"Redis 8.0 release notes and breaking changes","url":"https://redis.io/docs/latest/operate/rc/changelog/version-release-notes/8-0/","summary":"Release notes and breaking changes for Redis 8.0 on Redis Cloud.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:13+01:00","page_type":"content","content_hash":"9c8582aa66960d06b11b5957f052741697a0580a017b121e963ab52d8e87b043","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis 8.0 introduces powerful new capabilities, including the beta release of the Vector Set data structure, designed for AI use cases such as semantic search and recommendation systems. Redis 8 also merges Redis Stack and Redis Community Edition into a single unified distribution: Redis Open Source. For more information on the changes in Redis 8.0, see [What's new in Redis 8.0](https://redis.io/docs/latest/develop/whats-new/8-0)."},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"When new versions of Redis Open Source change existing commands, upgrading your database to a new version can potentially break some functionality. Before you upgrade, read the provided list of breaking changes that affect Redis Cloud and update any applications that connect to your database to handle these changes.\n\nMake sure to review all breaking changes between your current version of Redis and the version you are upgrading to."},{"id":"potentially-breaking-changes-to-acls","title":"Potentially breaking changes to ACLs","role":"content","text":"> [!NOTE]\n> The following content is relevant to all Redis distributions (RS, RC, and ROS).\n\nRedis 8 includes Redis Search, as well as JSON, time series, Bloom filter, cuckoo filter, top-k, count-min sketch, and t-digest data types.\nThe integration of these features into Redis also comes with improvements to Redis [ACL](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl) rules.\n\n> [!WARNING]\n> These ACL changes may introduce breaking changes for some users, which must be analyzed carefully.\n\n#### Extension to the existing ACL categories\n\nBefore Redis 8, the existing ACL categories @read, @write, @dangerous, @admin, @slow, and @fast did not include commands for Redis Search and the JSON, time series, and probabilistic data structures.\n\nStarting with Redis 8, Redis includes all Redis Search, JSON, time series, Bloom filter, cuckoo filter, top-k, count-min sketch, and t-digest commands in these existing ACL categories.\n\nAs a result:\n\n- Existing ACL rules such as `+@read +@write` will allow access to more commands than in previous versions of Redis. Here are some examples:\n  - A user with `+@read` access will be able to execute `FT.SEARCH`.\n  - A user with `+@write` access will be able to execute `JSON.SET`. \n\n- ACL rules such as `+@all -@write`  will allow access to fewer commands than previous versions of Redis. For example:\n  - A user with `+@all -@write` will not be able to execute `JSON.SET`.\n\nNote that the `@all` category did not change, as it always included all the commands.\n\nAdditionally, ACL rules such as `+@read +JSON.GET` can now be simplified as `+@read` because `JSON.GET` is included in the `@read` category.\n\n#### Who is affected by this change?\n\nUsers who currently use Redis Search and/or the JSON, time series, or probabilistic data structures, and use custom ACL rules.\n\nYou should reanalyze your ACL rules to make sure they are aligned with your security and access control requirements."},{"id":"redis-search","title":"Redis Search","role":"content","text":"The following changes affect behavior and validation in Redis Search:\n- Enforces validation for `LIMIT` arguments (offset must be 0 if limit is 0).\n- Enforces parsing rules for `FT.CURSOR READ` and `FT.ALIASADD`.\n- Parentheses are now required for exponentiation precedence in `APPLY` expressions.\n- Invalid input now returns errors instead of empty results.\n- Default values revisited for reducers like `AVG`, `COUNT`, `SUM`, `STDDEV`, `QUANTILE`, and others.\n- Updates to scoring (`BM25` is now the default instead of `TF-IDF`).\n- Improved handling of expired records, memory constraints, and malformed fields.\n\nFor a full list of Redis Search-related changes, see the [release notes](https://github.com/redis/redis/releases)."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/version-release-notes/8-2","title":"Redis 8.2 release notes and breaking changes","url":"https://redis.io/docs/latest/operate/rc/changelog/version-release-notes/8-2/","summary":"Release notes and breaking changes for Redis 8.2 on Redis Cloud.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:13+01:00","page_type":"content","content_hash":"8b8c77fc9cf31dc4b5ba98d0985c99a7d022c8017bc833deef1b5ba197c2688c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis 8.2 builds on the foundation of Redis 8.0 with significant performance and memory optimizations, enhanced streaming capabilities, and improved cluster management tools. For more information on the changes in Redis 8.2, see [What's new in Redis 8.2](https://redis.io/docs/latest/develop/whats-new/8-2) and review the Redis Open Source [8.2 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.2-release-notes)."},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"When new versions of Redis Open Source change existing commands, upgrading your database to a new version can potentially break some functionality. Before you upgrade, read the provided list of breaking changes that affect Redis Cloud and update any applications that connect to your database to handle these changes.\n\nIf you are upgrading from Redis 7.4, review the [Redis 8.0 breaking changes](https://redis.io/docs/latest/operate/rc/changelog/version-release-notes/8-0#breaking-changes) before upgrading."},{"id":"terraform-compatibility-limitation","title":"Terraform compatibility limitation","role":"content","text":"If you use the [Redis Cloud Terraform provider](https://redis.io/docs/latest/integrate/terraform-provider-for-redis-cloud) versions 2.1.2 through 2.4.5 with Redis 8.2 databases, database creation will fail. This issue only affects the Terraform provider and does not impact existing databases or data.\n\nTo resolve this issue, upgrade to the [latest supported version](https://registry.terraform.io/providers/RedisLabs/rediscloud/latest) and run `terraform init -upgrade`."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/version-release-notes/8-4","title":"Redis 8.4 release notes and breaking changes","url":"https://redis.io/docs/latest/operate/rc/changelog/version-release-notes/8-4/","summary":"Release notes and breaking changes for Redis 8.4 on Redis Cloud.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:13+01:00","page_type":"content","content_hash":"413f1ec0b34ab47503b5e3ae29c474d2b7cfa1dc849e342bdefb1cab89399833","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis 8.4 builds on the foundation of Redis 8.2 with significant enhancements to cluster operations, string manipulation, and stream processing capabilities.. For more information on the changes in Redis 8.4, see [What's new in Redis 8.4](https://redis.io/docs/latest/develop/whats-new/8-4) and review the Redis Open Source [8.4 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.4-release-notes)."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"When using Redis 8.4, be aware of these current limitations:\n\n- 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.\n- `FT.PROFILE`, `FT.EXPLAIN`, and `FT.EXPLAINCLI` don't include `FT.HYBRID` options.\n- `FT.HYBRID` metrics aren't displayed in `FT.INFO` and `INFO` commands.\n- Several `FT.HYBRID` options (`EXPLAINSCORE`, `SHARD_K_RATIO`, `YIELD_DISTANCE_AS`, `WITHCURSOR`) are not yet available.\n- Post-filtering after the `COMBINE` step using `FILTER` is not currently supported."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/version-release-notes/8-6","title":"Redis 8.6 release notes and breaking changes","url":"https://redis.io/docs/latest/operate/rc/changelog/version-release-notes/8-6/","summary":"Release notes and breaking changes for Redis 8.6 on Redis Cloud.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:13+01:00","page_type":"content","content_hash":"44862786f3e1c06623a8419e40f9aa3e2f184fd9897fa10110ea12b115e349db","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis 8.6 builds on the foundation of Redis 8.4 with significant enhancements to stream reliability, memory management, and security features. For more information on the changes in Redis 8.6, see [What's new in Redis 8.6](https://redis.io/docs/latest/develop/whats-new/8-6) and review the Redis Open Source [8.6 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.6-release-notes)."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"When using Redis 8.6, be aware of these current limitations:\n\n- **Redis Search**: During load rebalancing operations (such as atomic slot migration), cursors may miss some results due to data movement between nodes.\n- **Stream idempotency**: Avoid using `XADD` with the new `IDMP` or `IDMPAUTO` options when using `appendonly yes` with `aof-use-rdb-preamble no` (a non-default configuration). This limitation will be removed in the next patch release."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/changelog/version-release-notes","title":"Redis version release notes and breaking changes","url":"https://redis.io/docs/latest/operate/rc/changelog/version-release-notes/","summary":"Lists release notes and breaking changes for available Redis database versions on Redis Cloud.","tags":["docs","operate","rc"],"last_updated":"2025-07-10T15:53:24-04:00","children":[{"id":"operate/rc/changelog/version-release-notes/8-6","summary":"Release notes and breaking changes for Redis 8.6 on Redis Cloud.","title":"Redis 8.6 release notes and breaking changes","url":"https://redis.io/docs/latest/operate/rc/changelog/version-release-notes/8-6/"},{"id":"operate/rc/changelog/version-release-notes/8-4","summary":"Release notes and breaking changes for Redis 8.4 on Redis Cloud.","title":"Redis 8.4 release notes and breaking changes","url":"https://redis.io/docs/latest/operate/rc/changelog/version-release-notes/8-4/"},{"id":"operate/rc/changelog/version-release-notes/8-2","summary":"Release notes and breaking changes for Redis 8.2 on Redis Cloud.","title":"Redis 8.2 release notes and breaking changes","url":"https://redis.io/docs/latest/operate/rc/changelog/version-release-notes/8-2/"},{"id":"operate/rc/changelog/version-release-notes/8-0","summary":"Release notes and breaking changes for Redis 8.0 on Redis Cloud.","title":"Redis 8.0 release notes and breaking changes","url":"https://redis.io/docs/latest/operate/rc/changelog/version-release-notes/8-0/"},{"id":"operate/rc/changelog/version-release-notes/7-4","summary":"Release notes and breaking changes for Redis 7.4 on Redis Cloud.","title":"Redis 7.4 release notes and breaking changes","url":"https://redis.io/docs/latest/operate/rc/changelog/version-release-notes/7-4/"},{"id":"operate/rc/changelog/version-release-notes/7-2","summary":"Release notes and breaking changes for Redis 7.2 on Redis Cloud.","title":"Redis 7.2 release notes and breaking changes","url":"https://redis.io/docs/latest/operate/rc/changelog/version-release-notes/7-2/"}],"page_type":"content","content_hash":"073239b674530ffea961f3d29a964854de4746151bc934e49860f31d56547b38","sections":[{"id":"content","title":"Content","role":"content","text":"When new versions of Redis Open Source change existing commands, upgrading your Redis Cloud database to a new version can potentially break some functionality. Before you upgrade, read the provided list of changes that affect Redis Cloud and update any applications that connect to your database to handle these changes.\n\n\n| Version (Release date) | Major changes |\n| --- | --- |\n| [Redis 8.6](https://redis.io/docs/latest/operate/rc/changelog/version-release-notes/8-6/) | Release notes and breaking changes for Redis 8.6 on Redis Cloud. |\n| [Redis 8.4](https://redis.io/docs/latest/operate/rc/changelog/version-release-notes/8-4/) | Release notes and breaking changes for Redis 8.4 on Redis Cloud. |\n| [Redis 8.2](https://redis.io/docs/latest/operate/rc/changelog/version-release-notes/8-2/) | Release notes and breaking changes for Redis 8.2 on Redis Cloud. |\n| [Redis 8.0](https://redis.io/docs/latest/operate/rc/changelog/version-release-notes/8-0/) | Release notes and breaking changes for Redis 8.0 on Redis Cloud. |\n| [Redis 7.4](https://redis.io/docs/latest/operate/rc/changelog/version-release-notes/7-4/) | Release notes and breaking changes for Redis 7.4 on Redis Cloud. |\n| [Redis 7.2](https://redis.io/docs/latest/operate/rc/changelog/version-release-notes/7-2/) | Release notes and breaking changes for Redis 7.2 on Redis Cloud. |"}],"examples":[]}
{"schema_version":2,"id":"operate/rc/cloud-archive","title":"Archive","url":"https://redis.io/docs/latest/operate/rc/cloud-archive/","summary":"Describes where to view the archive of Redis Cloud documentation.","tags":["docs","operate","rc"],"last_updated":"2026-03-04T10:31:00-05:00","page_type":"content","content_hash":"adb904ce180c4fd50a9bc54751b04623492f1bc4548c5bfcb018def2b5d9c763","sections":[{"id":"content","title":"Content","role":"content","text":"Previous versions of Redis Cloud documentation are available on the [Redis Cloud documentation archive](https://docs.redis.com/2024-04-07/rc/)."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/cloud-integrations/aws-marketplace","title":"Sign up for Redis Cloud with AWS Marketplace","url":"https://redis.io/docs/latest/operate/rc/cloud-integrations/aws-marketplace/","summary":"Shows how to subscribe to Redis Cloud using AWS Marketplace","tags":["docs","operate","rc"],"last_updated":"2026-10-01T15:21:44+01:00","children":[],"page_type":"content","content_hash":"a7101045a79a9bfce1f356fdd1341a4fbc60bd8da2491509b85381c3cdee7a5d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can use AWS Marketplace to subscribe to Redis Cloud through AWS Marketplace.  This lets you provision according to your needs and pay using your AWS account."},{"id":"subscribe-to-redis-cloud-with-aws-marketplace","title":"Subscribe to Redis Cloud with AWS Marketplace","role":"content","text":"Here's how to subscribe to Redis Cloud with AWS Marketplace:\n\n1.  Sign in to the [AWS console](https://console.aws.amazon.com/).\n\n1.  Search AWS Marketplace for [Redis Cloud: Real-Time Data Layer for AI Apps & Agents](https://aws.amazon.com/marketplace/pp/prodview-mwscixe4ujhkq) and subscribe to the listing.\n\n    ![The Redis Cloud listing on AWS Marketplace](https://redis.io/docs/latest/images/rc/aws-marketplace-rc-listing.png)\n\n    Or, click on the URL provided by your Redis seller for a private offer for [Redis Cloud - Annual Commits](https://aws.amazon.com/marketplace/pp/prodview-e6y7ork67pjwg?sr=0-3&ref_=beagle&applicationId=AWSMPContessa) and accept the private offer.\n\n1.  Locate the **Set Up Your Account button**, and then select it to begin mapping your Redis Cloud account with your AWS Marketplace account.\n\n    ![Use the Set Up Your Account button after subscribing to Redis Cloud with your AWS Marketplace account.](https://redis.io/docs/latest/images/rc/aws-marketplace-account-setup-button.png)\n\n1.  Sign in to the [Redis Cloud console](https://cloud.redis.io). If you do not yet have an account, you will need to create one. You must have the **Account Owner** role.\n\n1.  Select the Redis Cloud account(s) to be mapped to your AWS Marketplace account and confirm that your Marketplace account will pay for your Redis Cloud resources going forward.\n\n    ![Use the AWS Marketplace dialog to map your Redis Cloud account to your AWS Marketplace account.](https://redis.io/docs/latest/images/rc/aws-marketplace-map-account-dialog.png)\n\n1.  Use the **Connect account** button to confirm your choice.\n\n    > [!NOTE]\n    > You must complete this step to bill your Redis Cloud resources to your AWS Marketplace account.\n\n1.  Once your Redis account is mapped to your AWS Marketplace account, your AWS Marketplace account will be charged for all your Redis Cloud resources going forward.\n\nAt this point, you can create a new database using the [standard workflow](https://redis.io/docs/latest/operate/rc/databases/create-database), with one important change. You don't need to enter a payment method, as it's automatically assigned to your AWS Marketplace account.\n\nTo confirm this, review the payment method associated with your subscription.\n\nIf your AWS Marketplace account is deactivated or otherwise unavailable, you won't be able to use your databases until the billing method is updated.  For help, [contact support](https://redis.io/support/)."},{"id":"add-additional-redis-accounts-to-aws-marketplace","title":"Add additional Redis accounts to AWS Marketplace","role":"content","text":"If you are the owner of more than one Redis Cloud account and have already associated your AWS marketplace account with one or more of your Redis accounts, you can add your AWS marketplace account to your other Redis Cloud accounts.\n\nTo do this:\n\n1. In the [Redis Cloud console](https://cloud.redis.io/), sign in to the account you would like to associate with your marketplace account. \n\n1. Select **Billing & Payments > Payment Methods**.\n\n1. Select **Add Marketplace account**.\n\n    ![The Add marketplace account button.](https://redis.io/docs/latest/images/rc/add-marketplace-account.png)\n\n1. Select the marketplace account you want to connect and confirm that it will pay for your Redis Cloud resources going forward.\n\n1. Select **Connect account**.\n\n    ![The Connect account button.](https://redis.io/docs/latest/images/rc/button-connect-account.png)\n\nYour marketplace account is now connected to your Redis Cloud account. If you would like to disconnect your marketplace account from your Redis Cloud account, select **Disconnect**."},{"id":"switch-from-annual-to-pay-as-you-go","title":"Switch from Annual to Pay as You Go","role":"content","text":"If you have an Annual private offer and want to switch to a Pay as You Go subscription, you can do so by subscribing to the [Redis Cloud: Real-Time Data Layer for AI Apps & Agents](https://aws.amazon.com/marketplace/pp/prodview-mwscixe4ujhkq) listing. Both existing and future resources will continue to be billed under the Annual offer until it expires, after which they will be billed under the Pay as You Go offer."},{"id":"disconnect-aws-marketplace-account","title":"Disconnect AWS Marketplace account","role":"content","text":"See [Disconnect AWS Marketplace account](https://support.redislabs.com/hc/en-us/articles/29215779949074-Disconnect-Redis-Cloud-from-AWS-Marketplace) on the Redis knowledge base for help disconnecting your AWS Marketplace account from your Redis Cloud account."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/cloud-integrations/gcp-marketplace","title":"Sign up for Redis Cloud with Google Cloud Marketplace","url":"https://redis.io/docs/latest/operate/rc/cloud-integrations/gcp-marketplace/","summary":"Shows how to subscribe to Redis Cloud using Google Cloud marketplace","tags":["docs","operate","rc"],"last_updated":"2026-10-01T15:21:44+01:00","children":[{"id":"operate/rc/cloud-integrations/gcp-marketplace/team-management","summary":"Shows how to add users in a legacy version of the Google Cloud Marketplace subscription.","title":"Google Cloud Marketplace team management","url":"https://redis.io/docs/latest/operate/rc/cloud-integrations/gcp-marketplace/team-management/"}],"page_type":"content","content_hash":"3539259aa8c7aea682eef1bb7a35dbb69e09532e48f22196e078c509c4fbd6a9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can use Google Cloud marketplace to subscribe to Redis Cloud. This lets you provision according to your needs and pay using your Google Cloud account.\n\n\nSee [Connecting Google Cloud Marketplace to Redis Cloud](https://www.youtube.com/watch?v=jo3e5VNye0E) for a short video tutorial."},{"id":"subscribe-to-redis-cloud-using-google-cloud-marketplace","title":"Subscribe to Redis Cloud using Google Cloud marketplace","role":"content","text":"Here's how to subscribe to Redis Cloud with Google Cloud marketplace:\n\n1.  Sign in to the [Google Cloud console](https://console.cloud.google.com/).\n\n1. Identify the Google Cloud Billing account that will subscribe to Redis Cloud. This account must have one of the following sets of Identity and Access Management (IAM) Roles:\n\n    - [Billing Administrator](https://cloud.google.com/billing/docs/how-to/billing-access#overview-of-cloud-billing-roles-in-cloud-iam) (`roles/billing.admin`), OR\n    - [Billing User](https://cloud.google.com/billing/docs/how-to/billing-access#overview-of-cloud-billing-roles-in-cloud-iam) (`roles/billing.user`) AND [Consumer Procurement Order Administrator](https://cloud.google.com/marketplace/docs/access-control#roles-permissions)\n\n    If you don't have the correct roles, request access from your Google Cloud Billing Administrator or Organization Administrator. For more information about granting roles, see [Access control for Google Cloud Marketplace](https://cloud.google.com/marketplace/docs/access-control).\n\n1.  Search Google Cloud Marketplace for [Redis® Cloud Cache and Vector Database](https://console.cloud.google.com/marketplace/product/redis-marketplace-isaas/redis-enterprise-cloud-flexible-plan).\n\n    ![The Redis Cloud - Pay as You Go plan listing on Google Cloud Marketplace](https://redis.io/docs/latest/images/rc/gcp-marketplace-rc-payg-plan.png)\n\n    Alternatively, in the navigation panel, select **View all products** and select **Redis Cloud** under **Databases**. You can pin Redis Cloud for easy access.\n\n    Or, click on the URL provided by your Redis seller for a private offer for **Redis Cloud - Annual** and accept the private offer.\n\n1.  Select the **Subscribe** button. This redirects you to the subscription details page.\n\n1. Under **Purchase Details**, select the Google Cloud Billing account that will subscribe to Redis Cloud. Review the subscription details, accept the terms, and select **Subscribe**.\n\n1. When you subscribe for the first time, select **Sign Up with Redis**. This will redirect you to the [Redis Cloud console](https://cloud.redis.io).\n\n1. Create a Redis Cloud account or sign in to an existing account. You must have the **Account Owner** role.\n\n1.  Select the Redis account to be mapped to your GCP Marketplace account and confirm that your Marketplace account will pay for your Redis Cloud resources going forward.\n\n    ![Use the GCP Marketplace dialog to map your Redis Cloud account to your Google Cloud Marketplace account.](https://redis.io/docs/latest/images/rc/gcp-marketplace-map-account-dialog.png)\n\n1.  Select **Connect account** to confirm your choice.\n\n    > [!NOTE]\n    > You must complete this step to bill your Redis Cloud resources to your Google Cloud Marketplace account.\n\n1.  After you connect your Redis account to your Google Cloud Marketplace account, your Google Cloud Marketplace account will be charged for all your Redis Cloud resources going forward.\n\n1. On the Google Cloud Marketplace listing, select **Manage on provider** to go to the [Redis Cloud console](https://cloud.redis.io).\n\n    ![The Manage on Provider button](https://redis.io/docs/latest/images/rc/gcp-marketplace-manage-on-provider.png)\n\nAt this point, you can create a new database using the [standard workflow](https://redis.io/docs/latest/operate/rc/databases/create-database), with one important change. You don't need to enter a payment method, as it automatically uses your Google Cloud Marketplace account.\n\nTo confirm this, review the payment method associated with your subscription.\n\nAdditional users can be added on the Redis Cloud console using the [Access Management](https://redis.io/docs/latest/operate/rc/security/access-control/access-management) page.\n\nIf your Google Cloud Marketplace account is deactivated or otherwise unavailable, you can't use your subscription until you update the billing method.  For help, [contact support](https://redis.io/support/)."},{"id":"add-additional-redis-accounts-to-google-cloud-marketplace","title":"Add additional Redis accounts to Google Cloud marketplace","role":"content","text":"If you are the owner of more than one Redis Cloud account and have already associated your Google Cloud marketplace account with one or more of your Redis accounts, you can add your Google Cloud marketplace account to your other Redis Cloud accounts.\n\nTo do this:\n\n1. In the [Redis Cloud console](https://cloud.redis.io/), sign in to the account you would like to associate with your marketplace account. \n\n1. Select **Billing & Payments > Payment Methods**.\n\n1. Select **Add Marketplace account**.\n\n    ![The Add marketplace account button.](https://redis.io/docs/latest/images/rc/add-marketplace-account.png)\n\n1. Select the marketplace account you want to connect and confirm that it will pay for your Redis Cloud resources going forward.\n\n1. Select **Connect account**.\n\n    ![The Connect account button.](https://redis.io/docs/latest/images/rc/button-connect-account.png)\n\nYour marketplace account is now connected to your Redis Cloud account. If you would like to disconnect your marketplace account from your Redis Cloud account, select **Disconnect**."},{"id":"switch-from-annual-to-pay-as-you-go","title":"Switch from Annual to Pay as You Go","role":"content","text":"If you have an Annual private offer and want to switch to a Pay as You Go subscription, you can do so by subscribing to the [Redis® Cloud Cache and Vector Database](https://console.cloud.google.com/marketplace/product/redis-marketplace-isaas/redis-enterprise-cloud-flexible-plan) listing. Both existing and future resources will continue to be billed under the Annual offer until it expires, after which they will be billed under the Pay as You Go offer."},{"id":"disconnect-google-cloud-marketplace-account","title":"Disconnect Google Cloud marketplace account","role":"content","text":"See [Disconnect Google Cloud Marketplace account](https://support.redislabs.com/hc/en-us/articles/29209085344018-Disconnect-Redis-Cloud-from-Google-Cloud-Marketplace) on the Redis knowledge base for help disconnecting your Google Cloud Marketplace account from your Redis Cloud account."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/cloud-integrations/gcp-marketplace/team-management","title":"Google Cloud Marketplace team management","url":"https://redis.io/docs/latest/operate/rc/cloud-integrations/gcp-marketplace/team-management/","summary":"Shows how to add users in a legacy version of the Google Cloud Marketplace subscription.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T14:59:52+01:00","page_type":"content","content_hash":"8db0105ac908a9219435664e8e41c191dfd4bea77fbc149d18f61fe47c865ec9","sections":[{"id":"content","title":"Content","role":"content","text":"This article applies to an earlier Google Cloud marketplace listing. For the current [Redis Cloud - Pay as You Go](https://console.cloud.google.com/marketplace/product/redis-marketplace-isaas/redis-enterprise-cloud-flexible-plan) listing, manage your team through the [Access Management](https://redis.io/docs/latest/operate/rc/security/access-control/access-management) screen. See [Sign up for Redis Cloud with Google Cloud Marketplace](https://redis.io/docs/latest/operate/rc/cloud-integrations/gcp-marketplace) for more information.\n\n\nIf you subscribed to Redis Cloud through Google Cloud Marketplace using the **Redis Cloud** listing, use the IAM section of the Google Cloud console to manage your team.\n\nTo grant Redis Cloud access to a Google Cloud user, select **Add** to add a member, insert the email address, and then assign the following roles to the user:\n- To designate a viewer, assign `editor`, `serviceusage.serviceUsageViewer`, and `redisenterprisecloud.viewer`\n- To designate an owner, assign `editor`, `serviceusage.serviceUsageViewer`, and `redisenterprisecloud.admin`\n\n\nIf these roles are not available, you can add them to your project.\n\n1. Select **Manage Roles**.\n2. Use the filter table field to locate the role. (Search for \"service usage viewer\" or \"Redis Cloud admin\".)\n3. Select the role.\n4. Select **Create role from selection** and then select **Create**.\n5. Use IAM to add a member and assign the desired role.\n\nUsers must sign in to Redis Cloud using their single sign-on credentials before they appear in the team member list."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/cloud-integrations/heroku","title":"Deploy Redis Cloud as a Heroku add-on","url":"https://redis.io/docs/latest/operate/rc/cloud-integrations/heroku/","summary":"Add Redis Cloud to your Heroku applications for fast data storage and caching.","tags":["docs","operate","rc"],"last_updated":"2025-09-26T14:14:00-04:00","page_type":"content","content_hash":"b27891796ffa0770ec92b1ac88f1181bb9485f7e96df67b333c0a3ea7bb1f289","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"Deploy Redis Cloud as a Heroku add-on to enhance your applications with high-performance data storage and caching capabilities."},{"id":"overview","title":"Overview","role":"overview","text":"The [Redis Cloud Heroku add-on](https://elements.heroku.com/addons/rediscloud) provides a fully managed Redis service as a Heroku add-on, making it easy to quickly add high-performance data storage to your Heroku applications. Redis Cloud on Heroku is perfect for web applications requiring fast caching, session storage, and real-time features."},{"id":"key-features","title":"Key Features","role":"content","text":"- **One-Click Installation**: Easy installation through Heroku add-ons marketplace\n- **Automatic Provisioning**: Instant Redis database setup with connection credentials\n- **Heroku Integration**: Seamless integration with Heroku's platform and tooling\n- **Scaling**: Easy scaling through Heroku dashboard or CLI\n- **Monitoring**: Built-in monitoring and alerting for your Redis databases\n- **Backup & Recovery**: Automated backups and point-in-time recovery\n- **Multi-language Support**: Works with all programming languages supported by Heroku"},{"id":"get-started","title":"Get started","role":"content","text":"Learn how to [add Redis Cloud to Heroku applications](https://devcenter.heroku.com/articles/rediscloud)."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/cloud-integrations","title":"Manage marketplace integrations","url":"https://redis.io/docs/latest/operate/rc/cloud-integrations/","summary":"Describes how to integrate Redis Cloud subscriptions into existing cloud provider services, whether existing subscriptions or through vendor marketplaces.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T14:59:52+01:00","children":[{"id":"operate/rc/cloud-integrations/aws-marketplace","summary":"Shows how to subscribe to Redis Cloud using AWS Marketplace","title":"Sign up for Redis Cloud with AWS Marketplace","url":"https://redis.io/docs/latest/operate/rc/cloud-integrations/aws-marketplace/"},{"id":"operate/rc/cloud-integrations/gcp-marketplace","summary":"Shows how to subscribe to Redis Cloud using Google Cloud marketplace","title":"Sign up for Redis Cloud with Google Cloud Marketplace","url":"https://redis.io/docs/latest/operate/rc/cloud-integrations/gcp-marketplace/"},{"id":"operate/rc/cloud-integrations/vercel","summary":"Shows how to create a Redis Cloud database with Vercel integration.","title":"Create a Redis Cloud database with the Vercel integration","url":"https://redis.io/docs/latest/operate/rc/cloud-integrations/vercel/"},{"id":"operate/rc/cloud-integrations/heroku","summary":"Add Redis Cloud to your Heroku applications for fast data storage and caching.","title":"Deploy Redis Cloud as a Heroku add-on","url":"https://redis.io/docs/latest/operate/rc/cloud-integrations/heroku/"}],"page_type":"content","content_hash":"7824ce0b90dfda28ec42310269829b6b015a9a613caa79b3a4e62e2891f2e5af","sections":[{"id":"overview","title":"Overview","role":"overview","text":"By default, Redis Cloud subscriptions are hosted in cloud vendor accounts owned and managed by Redis.\n\nTo integrate Redis Cloud into an existing cloud vendor account, you can:\n\n- Subscribe to Redis Cloud through [AWS Marketplace](https://redis.io/docs/latest/operate/rc/cloud-integrations/aws-marketplace).\n\n- Subscribe to Redis Cloud through [Google Cloud Marketplace](https://redis.io/docs/latest/operate/rc/cloud-integrations/gcp-marketplace).\n\nWhen you subscribe to Redis Cloud through a cloud vendor marketplace, billing is handled through the marketplace.\n\nRedis also offers monthly and annual commitments through cloud vendor marketplaces. [Contact sales](https://redis.io/meeting/) if you're interested in a monthly or annual offer."},{"id":"marketplace-billing-considerations","title":"Marketplace billing considerations","role":"content","text":"Cloud vendor marketplaces provide a convenient way to handle multiple subscription fees.  However, this also means that billing issues impact multiple subscriptions, including Redis Cloud.\n\nWhen billing details change, you should verify that each service is operating normally and reflects the updated billing details.  Otherwise, you might experience unexpected consequences, such as data loss or subscription removal.\n\nFor best results, we recommend:\n\n- [Backing up all data](https://redis.io/docs/latest/operate/rc/databases/back-up-data) _before_ updating billing details.\n\n- Contacting [support](https://redis.io/support/) or your account team for assistance."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/cloud-integrations/vercel","title":"Create a Redis Cloud database with the Vercel integration","url":"https://redis.io/docs/latest/operate/rc/cloud-integrations/vercel/","summary":"Shows how to create a Redis Cloud database with Vercel integration.","tags":["docs","operate","rc"],"last_updated":"2026-10-05T10:59:47+01:00","page_type":"content","content_hash":"13b308979f51645c8011067d7aa8896f9be27f55f92507b71ebdcb2cdcfc8f64","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The [Redis Cloud Vercel integration](https://vercel.com/marketplace/redis-cloud) lets you create a new Redis database from your Vercel account and connect it to your Vercel project(s)."},{"id":"create-database","title":"Create database","role":"content","text":"1. Log in to your Vercel account (or create a new one).\n\n1. Navigate to the **Storage** tab. Under **Marketplace Database Providers**, find **Redis** and select **Create**.\n\n    ![The Redis database provider in the Vercel Storage tab.](https://redis.io/docs/latest/images/rc/vercel-storage-create-database-button.png)\n\n1. In the **Install integration** dialog under **Configuration and plan**, select your region and [high availability](https://redis.io/docs/latest/operate/rc/databases/configuration/high-availability) settings.\n\n    ![Vercel Region and high availability settings](https://redis.io/docs/latest/images/rc/vercel-create-db-select-plan.png)\n\n1. Choose a plan and select **Continue**.\n\n1. Enter your database name or use the automatically generated name.\n\n1. Select **Create**."},{"id":"connect-to-your-database","title":"Connect to your database","role":"content","text":"After creation, you will see your database details. After provisioning is complete, the status will change from `Initializing` to `Available` (you may need to refresh your browser).\n\n![Vercel database details](https://redis.io/docs/latest/images/rc/vercel-status-available.png)\n\nYou can use the connection string shown under **Quickstart** to [connect to your database](https://redis.io/docs/latest/operate/rc/databases/connect)."},{"id":"link-database-to-your-project","title":"Link database to your project","role":"content","text":"1. Navigate to the **Storage** tab.\n1. Find your new database in the list of your team's databases.\n1. Select **Connect Project**.\n\n    ![Connect Project button](https://redis.io/docs/latest/images/rc/vercel-connect-project-button.png)\n\n1. Choose your project and environments and select **Connect**.\n\n    ![Connect project](https://redis.io/docs/latest/images/rc/vercel-connect-project.png)"},{"id":"manage-your-database","title":"Manage your database","role":"content","text":"From the database details page, you can make edits to your database under **Settings**.\n\nMore configuration options are coming soon, including plan changes, multi-zone high availability, and region selection."},{"id":"configure-from-redis-cloud","title":"Configure from Redis Cloud","role":"configuration","text":"You can also edit some configuration options on the Redis Cloud console.\n\nFrom the database detail page, select **Open in Redis**.\n\n![Open in Redis](https://redis.io/docs/latest/images/rc/vercel-open-in-redis-cloud.png)\n\nYour Redis Cloud account is linked to your Vercel account. All your team's Redis databases will be listed under **Databases** in Redis Cloud.\n\n> [!WARNING]\n> Anybody on your team that has a [Vercel access role](https://vercel.com/docs/rbac/access-roles) other than **Pro Viewer**, **Enterprise Viewer**, or **Billing**, can make changes to your database configuration on the Redis Cloud console as if they were an **Owner** on Redis Cloud. See [Team management roles](https://redis.io/docs/latest/operate/rc/security/access-control/access-management#team-management-roles) for more information.\n\nSelect your new database to make configuration changes such as passwords or the eviction policy.\n\n> [!NOTE]\n>  The [eviction policy](https://redis.io/docs/latest/operate/rc/databases/configuration/data-eviction-policies) defaults to `no eviction` for new databases.  You can change this by [editing the database details](https://redis.io/docs/latest/operate/rc/databases/view-edit-database)."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/compatibility","title":"Redis Cloud compatibility with Redis Open Source","url":"https://redis.io/docs/latest/operate/rc/compatibility/","summary":"Redis Cloud compatibility with Redis Open Source.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T14:59:36+01:00","page_type":"content","content_hash":"ac9830e8e4d525b7259fa113df4fab526f68ea6bd2701c13e7c8133d3fb85204","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Both [Redis Software](https://redis.io/docs/latest/operate/rs) and Redis Cloud are compatible with Redis Open Source.\n\nRedis contributes extensively to the Redis project and uses it inside of Redis Software and Redis Cloud. As a rule, we adhere to the project's specifications and update both products with the latest version of Redis."},{"id":"redis-commands","title":"Redis commands","role":"content","text":"See [Compatibility with Redis commands](https://redis.io/docs/latest/operate/rs/references/compatibility/commands) to learn which Redis commands are compatible with Redis Software and Redis Cloud."},{"id":"configuration-settings","title":"Configuration settings","role":"configuration","text":"[Compatibility with Redis configuration settings](https://redis.io/docs/latest/operate/rs/references/compatibility/config-settings) lists the Redis configuration settings supported by Redis Software and Redis Cloud."},{"id":"redis-clients","title":"Redis clients","role":"content","text":"You can use any standard [Redis client](https://redis.io/docs/latest/develop/clients) with Redis Software and Redis Cloud."},{"id":"resp-compatibility","title":"RESP compatibility","role":"content","text":"Redis Software and Redis Cloud support RESP2 and RESP3. In Redis Cloud, you can choose between RESP2 and RESP3 when you [create a database](https://redis.io/docs/latest/operate/rc/databases/create-database) and you can change it when you [edit a database](https://redis.io/docs/latest/operate/rc/databases/view-edit-database). For more information about the different RESP versions, see the [Redis serialization protocol specification](https://redis.io/docs/latest/develop/reference/protocol-spec#resp-versions)."},{"id":"client-side-caching-compatibility","title":"Client-side caching compatibility","role":"content","text":"Redis Software and Redis Cloud support [client-side caching](https://redis.io/docs/latest/develop/clients/client-side-caching) for databases with Redis versions 7.4 or later. See [Client-side caching compatibility with Redis Software and Redis Cloud](https://redis.io/docs/latest/operate/rs/references/compatibility/client-side-caching) for more information about compatibility."},{"id":"compatibility-with-redis-cluster-api","title":"Compatibility with Redis Cluster API","role":"compatibility","text":"Redis Cloud supports [Redis Cluster API](https://redis.io/docs/latest/operate/rc/databases/configuration/clustering#oss-cluster-api) on Redis Cloud Pro if it is enabled for a database. Review [Redis Cluster API architecture](https://redis.io/docs/latest/operate/rs/clusters/optimize/oss-cluster-api) to determine if you should enable this feature for your database."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/context-engine/agent-memory","title":"Redis Agent Memory on Redis Cloud","url":"https://redis.io/docs/latest/operate/rc/context-engine/agent-memory/","summary":"Use Agent Memory as a managed service on Redis Cloud.","aliases":["/operate/rc/agent-memory/"],"tags":["docs","operate","rc"],"last_updated":"2026-09-25T14:59:52+01:00","children":[],"page_type":"content","content_hash":"60501de8ad92ba8bb3ea5fa16e1f8eb72c3ca181bc65bc38ecf845c6597069f1","sections":[{"id":"content","title":"Content","role":"content","text":"Redis Agent Memory is available as a managed service on Redis Cloud.\n\nUse the Redis Iris operational documentation to [create an Agent Memory service](https://redis.io/docs/latest/operate/iris/agent-memory/create-service), [configure its AI models](https://redis.io/docs/latest/operate/iris/agent-memory/model-configuration), or [view and manage an existing service](https://redis.io/docs/latest/operate/iris/agent-memory/view-service).\n\nFor deployment options, see the [Agent Memory operational overview](https://redis.io/docs/latest/operate/iris/agent-memory)."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/context-engine/context-retriever","title":"Redis Context Retriever on Redis Cloud","url":"https://redis.io/docs/latest/operate/rc/context-engine/context-retriever/","summary":"Expose schema first retrieval tools from your Redis Cloud data to AI agents.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T14:59:52+01:00","children":[],"page_type":"content","content_hash":"eac5304737b66cfbb1b7805ae286108122185b2adc45ee1bebeb0a6267481138","sections":[{"id":"content","title":"Content","role":"content","text":"Redis Context Retriever is available as a managed service on Redis Cloud.\n\nUse the Redis Iris operational documentation to [create and manage a Context Retriever service](https://redis.io/docs/latest/operate/iris/context-retriever)."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/context-engine","title":"Redis Iris context engine on Redis Cloud","url":"https://redis.io/docs/latest/operate/rc/context-engine/","summary":"Deploy AI agents with Redis Cloud.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T14:59:52+01:00","children":[{"id":"operate/rc/context-engine/agent-memory","summary":"Use Agent Memory as a managed service on Redis Cloud.","title":"Redis Agent Memory on Redis Cloud","url":"https://redis.io/docs/latest/operate/rc/context-engine/agent-memory/"},{"id":"operate/rc/context-engine/langcache","summary":"Store LLM responses for AI applications in Redis Cloud.","title":"Semantic caching with LangCache on Redis Cloud","url":"https://redis.io/docs/latest/operate/rc/context-engine/langcache/"},{"id":"operate/rc/context-engine/context-retriever","summary":"Expose schema first retrieval tools from your Redis Cloud data to AI agents.","title":"Redis Context Retriever on Redis Cloud","url":"https://redis.io/docs/latest/operate/rc/context-engine/context-retriever/"}],"page_type":"content","content_hash":"d7ce96ef6935e5609c7a48386373ca9585eae4037ada261e5015e91c949edbd4","sections":[{"id":"redis-cloud","title":"Redis Cloud","role":"overview","text":"* [Redis Agent Memory on Redis Cloud](https://redis.io/docs/latest/operate/iris/agent-memory)\n  Store agent memory for AI applications in Redis Cloud.\n\n* [Semantic caching with LangCache on Redis Cloud](https://redis.io/docs/latest/operate/iris/langcache)\n  Store LLM responses for AI applications in Redis Cloud.\n\n* [Redis Context Retriever on Redis Cloud](https://redis.io/docs/latest/operate/iris/context-retriever)\n  Expose schema first retrieval tools from your Redis Cloud data to AI agents."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/context-engine/langcache","title":"Semantic caching with LangCache on Redis Cloud","url":"https://redis.io/docs/latest/operate/rc/context-engine/langcache/","summary":"Store LLM responses for AI applications in Redis Cloud.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T14:59:52+01:00","children":[],"page_type":"content","content_hash":"c4eacd739858d54e756f16e3022dfd949605cf233c5500194c390e941300e381","sections":[{"id":"content","title":"Content","role":"content","text":"LangCache is available as a managed service on Redis Cloud.\n\nUse the Redis Iris operational documentation to [create and manage a LangCache service](https://redis.io/docs/latest/operate/iris/langcache)."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/databases/active-active/create-active-active-database","title":"Create an Active-Active database","url":"https://redis.io/docs/latest/operate/rc/databases/active-active/create-active-active-database/","summary":"Shows how to create an Active-Active database","aliases":["/operate/rc/subscriptions/create-active-active-subscription","/operate/rc/databases/create-database/create-active-active-database"],"tags":["docs","operate","rc"],"last_updated":"2026-10-02T16:06:07+01:00","page_type":"content","content_hash":"c481cbe7695471d5808396d565931ef5086be8335a090615c7b79cf0b3166799","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Active-Active databases store data across multiple regions and availability zones.  This improves scalability, performance, and availability, especially when compared to standalone databases. See [Active-Active Redis](https://redis.io/docs/latest/operate/rc/databases/active-active) for more information.\n\nTo deploy Active-Active databases in Redis Cloud, you need a Redis Cloud Pro plan that enables Active-Active Redis and defines the regions for each copy of your databases.\n\nActive-Active databases consist of multiple copies (also called _instances_) deployed to different regions throughout the world.\n\nThis reduces latency for local users and improves availability should a region fail.\n\nRedis Cloud maintains consistency among instances in the background; that is, each copy eventually includes updates from every region.  As a result, memory limit and throughput increase."},{"id":"create-an-active-active-database","title":"Create an Active-Active database","role":"content","text":"Before creating a Redis Cloud database, you need to [create an account](https://redis.io/docs/latest/operate/rc/rc-quickstart).\n\nTo create a database in your Redis Cloud account:\n\n1. Sign in to the [Redis Cloud console](https://cloud.redis.io).\n\n2. Select the **New database** button.\n\n    ![The New Database button creates a new database.](https://redis.io/docs/latest/images/rc/button-database-new.png)\n\n    This displays the **Create database** screen.\n\n3. Select the type of [subscription](https://redis.io/docs/latest/operate/rc/subscriptions) you need. For this guide, select **Pro**. \n\n    ![The Subscription selection panel with Pro selected.](https://redis.io/docs/latest/images/rc/create-database-subscription-pro-new.png)\n\n    > [!NOTE]\n    > This guide shows how to create an Active-Active database with a new Pro subscription. If you already have an Active-Active subscription and want to add a database to it, see [Create a Pro database in an existing subscription](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-existing). Active-Active subscriptions can host a maximum of 10 databases.\n    >\n    \n\nAfter you select **Pro**, the **Database settings** section will appear.\n\n![The database settings section, with custom settings selected.](https://redis.io/docs/latest/images/rc/create-pro-db-settings-custom.png)\n\nFor this guide, select **Custom settings**. For an Active-Active database, you will need to: \n\n1. Set up the deployment options, including cloud vendor and region details for each instance.\n\n2. Define the database size requirements.\n\n3. Review your choices, provide payment details, and then create your databases.\n\nThe following sections provide more information."},{"id":"set-up-deployment-details","title":"Set up deployment details","role":"content","text":"The **Setup** tab specifies general settings for your Redis deployment.\n\n![The Setup tab of the new Pro Database process.](https://redis.io/docs/latest/images/rc/subscription-new-flexible-tabs-setup.png)\n\nThere are two sections on this tab:\n\n- [General settings](#general-settings) include the cloud provider details and specific configuration options.\n- [Advanced options](#advanced-options) define settings for high availability and security. Configurable settings vary according to cloud provider.\n\n#### General settings {#general-settings}\n\nSelect **Active-Active (Multi-region)** to turn on Active-Active. \n\n![The general settings of the setup tab with Active-Active selected.](https://redis.io/docs/latest/images/rc/create-flexible-sub-active-active-on.png)\n\nWhen you enable Active-Active Redis, two regions are selected by default. Select the drop-down arrow to display a list of provider regions that support Active-Active databases.\n\n![Use the Region drop-down to select the regions for your Active-Active database.](https://redis.io/docs/latest/images/rc/create-sub-active-active-regions.png)\n\n> [!NOTE]\n> Active-Active subscriptions on Redis Cloud are limited to a maximum of 10 regions.\n\nUse the checkboxes in the list to select or remove regions.  The Search box lets you locate specific regions.\n\nYou can use a region's Remove button to remove it from the list.\n\n![Select the Delete button to remove a region from the list.](https://redis.io/docs/latest/images/rc/icon-region-delete.png)\n\n#### Advanced options {#advanced-options}\n\n![Each region needs a unique CIDR address block to communicate securely with other instances.](https://redis.io/docs/latest/images/rc/create-sub-active-active-cidr.png)\n\nIn the **Advanced options** section, you can:\n\n- Choose to deploy your Active-Active database to an existing Cloud Account, if [Redis Cloud Bring your own Cloud](https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud) is enabled.\n    \n- Define CIDR addresses for each region in the **VPC configuration** section.\n\n    Every CIDR should be unique to properly route network traffic between each Active-Active database instance and your consumer VPCs. The CIDR block regions should _not_ overlap between the Redis server and your app consumer VPCs. In addition, CIDR blocks should not overlap between cluster instances. \n\nWhen all **Deployment CIDR** regions display a green checkmark, you're ready to continue.  \n\n![Green checkmarks indicate valid CIDR address values.](https://redis.io/docs/latest/images/rc/icon-cidr-address-ok.png)\n\nRed exclamation marks indicate error conditions; the tooltip provides additional details.\n\n![Red exclamation points indicate CIDR address problems.](https://redis.io/docs/latest/images/rc/icon-cidr-address-error.png)\n    \n    If you chose to deploy your Active-Active database to an existing [Bring your own Cloud](https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud) account, you can also define the VPC ID for each region. Select **In existing VPC** and the set the VPC ID for each selected region.\n    \n- Set your [maintenance](https://redis.io/docs/latest/operate/rc/subscriptions/maintenance) settings in the **Maintenance windows** section. Select **Manual** if you want to set [manual maintenance windows](https://redis.io/docs/latest/operate/rc/subscriptions/maintenance/set-maintenance-windows).\n\n> [!NOTE]\n> Multi-AZ replication is required for all Active-Active databases.\n>\n> Regions with fewer than three availability zones are not available in the Redis Cloud console. To create an Active-Active database that includes one of these regions, use the REST API to [create a Pro subscription](https://redis.io/docs/latest/operate/rc/api/examples/manage-subscriptions#create-a-pro-subscription). You'll be able to manage these subscriptions and databases using the Redis Cloud console after they're created. Active-Active databases in these regions use single-zone replication and provide 99.99% (four-nines) availability instead of 99.999%. For more information, see [Active-Active Redis](https://redis.io/docs/latest/operate/rc/databases/active-active#multi-zone).\n\nWhen finished, choose **Continue** to determine your size requirements.\n\n![Select the Continue button to continue to the next step.](https://redis.io/docs/latest/images/rc/button-subscription-continue.png)"},{"id":"sizing-tab","title":"Sizing tab","role":"content","text":"The **Sizing** tab helps you specify the database, memory, and throughput requirements for your subscription.\n\n![The Sizing tab when creating a new Pro subscription.](https://redis.io/docs/latest/images/rc/subscription-new-flexible-sizing-tab.png)\n\nWhen you first visit the **Sizing** tab, there are no databases defined.  Select the **Add** button to create one.\n\n![Use the Add button to define a new database for your subscription.](https://redis.io/docs/latest/images/rc/icon-add.png)\n\nThis opens the **Database configurations** dialog, which lets you define the requirements for your new database.\n\n![New database dialog for Active-Active database.](https://redis.io/docs/latest/images/rc/create-database-active-active.png)\n\nBy default, you're shown basic settings, which include:\n\n- **Name**: A custom name for your database.\n- **Version**: The Redis version for your database. We recommend you choose the latest available version. \n- **Advanced Capabilities**: Advanced data types or features used by the database. Active-Active databases support the [JSON](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json) data type and [Search and query](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search) features.\n\n    ![When you create an Active-Active database, you can select the JSON and Search and query advanced capabilities.](https://redis.io/docs/latest/images/rc/active-active-json-detail.png)\n\n    For Redis versions prior to 8.0, we select both capabilities for you automatically. You can remove a capability by clicking on it while selected. Selected capabilities will be available in all regions, including those added in the future.\n\n    Starting with Redis 8.0, JSON and Search and query are included by default.\n\n    See [Search and query Active-Active databases](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/search-active-active) to learn how to use Search and query on Active-Active databases.\n\n- **Dataset size**: The amount of data needed for your dataset in GB. \n\n    For Search and query databases, use the [Sizing calculator](https://redis.io/redisearch-sizing-calculator/) to estimate your index size and throughput requirements. When you're entering the dataset size for your database, add the estimated index size from the Sizing calculator to your expected dataset size.\n\n- **Hashing policy**: Determines how data is distributed across multiple Redis processes of a database. Available options depend on your account creation date. See [Clustering](https://redis.io/docs/latest/operate/rc/databases/configuration/clustering#manage-the-hashing-policy) for more information.\n\n- **Throughput**: When you create an Active-Active database, you define the throughput for each instance. The total operations per second combines the total read ops/sec and applies the write ops/sec for each region across every region. \n\n    ![When you create an Active-Active database, you define throughput for each region.](https://redis.io/docs/latest/images/rc/active-active-throughput-detail.png)\n\n    The total ops/sec for each region is calculated as follows:\n\n    [code example]\n\n    The total ops/sec for the database is the sum of the ops/sec for each region.\n    \n    Because each instance needs the ability to write to every other instance, write operations significantly affect the total number of ops/sec. \n\n    Select a tab to see examples of throughput calculations for different Active-Active configurations.\n\n    **Two regions, balanced between regions:**\n\nFor this database, we have two regions where read and write operations are balanced between the regions, as described in the table below:\n\n| Region | Local read ops/sec | Local write ops/sec | \n|:------:|:------------------:|:-------------------:|\n| Region 1 | 2000 | 1000 |\n| Region 2 | 2000 | 1000 |\n\nThe total ops/sec for this database is calculated as follows:\n\n[code example]\n\n**Two regions, read/write heavy in one region:**\n\nFor this database, we have two regions where one region has more read and write operations than the other region, as described in the table below:\n\n| Region | Local read ops/sec | Local write ops/sec | \n|:------:|:------------------:|:-------------------:|\n| Region 1 | 2000 | 1000 |\n| Region 2 | 4000 | 2000 |\n\nThe total ops/sec for this database is calculated as follows:\n\n[code example]\n\n**Three regions, balanced between regions:**\n\nFor this database, we have three regions where read and write operations are balanced between the regions, as described in the table below:\n\n| Region | Local read ops/sec | Local write ops/sec | \n|:------:|:------------------:|:-------------------:|\n| Region 1 | 2000 | 1000 |\n| Region 2 | 2000 | 1000 |\n| Region 3 | 2000 | 1000 |\n\nThe total ops/sec for this database is calculated as follows:\n\n[code example]\n\n**Three regions, different read/write in each region:**\n\nFor this database, we have three regions where read and write operations are different between the regions, as described in the table below:\n\n| Region | Local read ops/sec | Local write ops/sec | \n|:------:|:------------------:|:-------------------:|\n| Region 1 | 3000 | 1000 |\n| Region 2 | 4000 | 3000 |\n| Region 3 | 1000 | 2000 |\n\nThe total ops/sec for this database is calculated as follows:\n\n[code example]\n\n\n\n    For Search and query databases, the estimated throughput from the [Sizing calculator](https://redis.io/redisearch-sizing-calculator/) is the total amount of throughput you need. When setting throughput for your Active-Active database, use the total amount for each region and divide it depending on your read (query) and write (update) needs for each region. For example, if the total amount of throughput needed is 50000 ops/sec, you could set each region to have 20000 ops/sec for reads (queries) and 30000 ops/sec for writes (updates).\n\n- **Data Persistence**: Defines the data persistence policy, if any. See [Database persistence](https://redis.io/docs/latest/operate/rs/databases/configure/database-persistence).\n- **Supported Protocol(s)**: Choose between RESP2 and RESP3 _(Redis 7.2 or later)_. See [Redis serialization protocol](https://redis.io/docs/latest/develop/reference/protocol-spec#resp-versions) for details.\n- **Quantity**: Number of databases to create with these settings. \n\nWhen finished, select **Save configuration** to save your database configuration.\n\n![Select the Save configuration button to define your new database.](https://redis.io/docs/latest/images/rc/button-configuration-save.png)\n\nUse the **Add database** button to define additional databases or select the **Continue button** to display the **Review and create** tab.\n\nHover over a database to see the **Edit** and **Delete** icons. You can use the **Edit** icon to change a database or the **Delete** icon to remove a database from the list.\n\n![Use the Edit button to change database settings.](https://redis.io/docs/latest/images/rc/icon-edit.png#no-click)&nbsp;![Use the Delete button to remove a database.](https://redis.io/docs/latest/images/rc/icon-delete-lb.png#no-click)"},{"id":"review-and-create-tab","title":"Review and Create tab","role":"content","text":"The **Review and Create** tab provides a cost estimate for your Redis Cloud Pro plan:\n\n![The Review & Create tab of the New Active-Active subscription screen.](https://redis.io/docs/latest/images/rc/create-pro-aa-review.png)\n\nRedis breaks down your databases to Redis Billing Units (RBUs), each with their own size and throughput requirements. For more info, see [Billing unit types](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new#billing-unit-types).\n\nThe **Payment methods** section of this tab shows which payment method you're using for this database. Select the arrow on the top right of this section to view all available payment methods.\n\n![The payment method list.](https://redis.io/docs/latest/images/rc/subscription-new-flexible-cardlist.png)\n\nIf you have not added a payment method or want to add a new payment method, select **Add credit card** to add a new credit card.\n\nIf you have not already added a business address to your account, you must enter one. Redis uses your business address for communication, invoicing, and tax purposes.\n\n![The Add credit card screen before a business address has already been added.](https://redis.io/docs/latest/images/rc/billing-add-first-card.png)\n\nIf you already have a business address, you don't need to enter one here. See [Account settings](https://redis.io/docs/latest/operate/rc/accounts/account-settings) to learn how to update your business address.\n\n![The Add credit card screen after a business address has already been added.](https://redis.io/docs/latest/images/rc/billing-add-credit-card.png)\n\nIf the card's billing address is the same as your account's business address, select **Same as business address** to fill the billing address with your business address details.\n\nSelect **Back to Sizing** to make changes or **Confirm & Pay** to create your databases.\n\n![Select Confirm & pay to create your database.](https://redis.io/docs/latest/images/rc/button-create-db-confirm-pay.png)\n\nNote that databases are created in the background.  While they are provisioning, you aren't allowed to make changes. This process generally takes 10-15 minutes.\n\nUse the **Database list** to check the status of your databases."},{"id":"more-info","title":"More info","role":"content","text":"- [Create a Pro database with a new subscription](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new)\n- [Active-Active Redis](https://redis.io/docs/latest/operate/rc/databases/active-active)\n- [Develop applications with Active-Active databases](https://redis.io/docs/latest/operate/rc/databases/active-active/develop)\n- Database [memory limit](https://redis.io/docs/latest/operate/rc/databases/configuration/sizing#dataset-size)\n- Redis Cloud [subscription plans](https://redis.io/docs/latest/operate/rc/subscriptions)\n- [Redis Cloud pricing](https://redis.io/pricing/#monthly)"}],"examples":[{"id":"sizing-tab-ex0","language":"sh","code":"Region ops/sec = Local read ops/sec + \n                    Sum of write ops/sec from all regions","section_id":"sizing-tab"},{"id":"sizing-tab-ex1","language":"text","code":"Region 1 ops/sec = 2000 (local read) + \n                   1000 (local write) + \n                   1000 (write from Region 2) = 4000 ops/sec\n\nRegion 2 ops/sec = 2000 (local read) + \n                   1000 (local write) + \n                   1000 (write from Region 1) = 4000 ops/sec\n\nTotal ops/sec = 4000 (Region 1) + 4000 (Region 2) \n              = 8000 ops/sec","section_id":"sizing-tab"},{"id":"sizing-tab-ex2","language":"text","code":"Region 1 ops/sec = 2000 (local read) + \n                   1000 (local write) + \n                   2000 (write from Region 2) = 5000 ops/sec\n\nRegion 2 ops/sec = 4000 (local read) + \n                   2000 (local write) + \n                   1000 (write from Region 1) = 7000 ops/sec\n\nTotal ops/sec = 5000 (Region 1) + 7000 (Region 2) \n              = 12000 ops/sec","section_id":"sizing-tab"},{"id":"sizing-tab-ex3","language":"text","code":"Region 1 ops/sec = 2000 (local read) + \n                   1000 (local write) + \n                   1000 (write from Region 2) +\n                   1000 (write from Region 3) = 5000 ops/sec\n\nRegion 2 ops/sec = 2000 (local read) + \n                   1000 (local write) + \n                   1000 (write from Region 1) +\n                   1000 (write from Region 3) = 5000 ops/sec\n\nRegion 3 ops/sec = 2000 (local read) + \n                   1000 (local write) + \n                   1000 (write from Region 1) +\n                   1000 (write from Region 2) = 5000 ops/sec\n\nTotal ops/sec = 5000 (Region 1) + 5000 (Region 2) + 5000 (Region 3) \n              = 15000 ops/sec","section_id":"sizing-tab"},{"id":"sizing-tab-ex4","language":"text","code":"Region 1 ops/sec = 3000 (local read) + \n                   1000 (local write) + \n                   3000 (write from Region 2) +\n                   2000 (write from Region 3) = 9000 ops/sec\n\nRegion 2 ops/sec = 4000 (local read) + \n                   3000 (local write) + \n                   1000 (write from Region 1) +\n                   2000 (write from Region 3) = 10000 ops/sec\n\nRegion 3 ops/sec = 1000 (local read) + \n                   2000 (local write) + \n                   1000 (write from Region 1) +\n                   3000 (write from Region 2) = 7000 ops/sec\n\nTotal ops/sec = 9000 (Region 1) + 10000 (Region 2) + 7000 (Region 3) \n              = 26000 ops/sec","section_id":"sizing-tab"}]}
{"schema_version":2,"id":"operate/rc/databases/active-active/develop/app-failover-active-active","title":"Application failover with Active-Active databases","url":"https://redis.io/docs/latest/operate/rc/databases/active-active/develop/app-failover-active-active/","summary":"How to failover your application to connect to a remote replica.","tags":["docs","operate","rs","rc"],"last_updated":"2026-09-24T09:16:24+01:00","page_type":"content","content_hash":"2610c4fa39dfcbb0ec1641ca456e3d9ae2c03315216b241c637df765b9128712","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Active-Active Redis deployments don't have a built-in failover or failback mechanism for application connections.\nAn application deployed with an Active-Active database connects to a replica of the database that is geographically nearby.\nIf that replica is not available, the application can failover to a remote replica, and failback again if necessary.\nIn this article we explain how this process works.\n\n> [!NOTE]\n> For other disaster recovery strategies including network-based, proxy-based, and client library approaches, see [Active-Active disaster recovery strategies](https://redis.io/docs/latest/operate/rs/databases/active-active/disaster-recovery).\n\nActive-Active connection failover can improve data availability, but can negatively impact data consistency.\nActive-Active replication, like Redis replication, is asynchronous.\nAn application that fails over to another replica can miss write operations.\nIf the failed replica saved the write operations in persistent storage,\nthen the write operations are processed when the failed replica recovers."},{"id":"detecting-failure","title":"Detecting Failure","role":"content","text":"Your application can detect two types of failure:\n\n1. **Local failures** - The local replica is down or otherwise unavailable\n1. **Replication failures** - The local replica is available but fails to replicate to or from remote replicas\n\nYou can also use [database availability API requests](https://redis.io/docs/latest/operate/rs/monitoring/db-availability) to determine if a database replica is available to handle read and write operations. The lag-aware database availability requests considers CRDT replication lag as a health check criterion to prevent reading stale data during failback scenarios."},{"id":"local-failures","title":"Local Failures","role":"content","text":"Local failure is detected when the application is unable to connect to the database endpoint for any reason. Reasons for a local failure can include: multiple node failures, configuration errors, connection refused, connection timed out, unexpected protocol level errors."},{"id":"replication-failures","title":"Replication Failures","role":"content","text":"Replication failures are more difficult to detect reliably without causing false positives. Replication failures can include: network split, replication configuration issues, remote replica failures.\n\nThe most reliable method for health-checking replication is by using the Redis publish/subscribe (pub/sub) mechanism.\n\n> [!NOTE]\n> Note that this document does not suggest that Redis pub/sub is reliable in the common sense. Messages can get lost in certain conditions, but that is acceptable in this case because typically the application determines that replication is down only after not being able to deliver a number of messages over a period of time.\n\nWhen you use the pub/sub data type to detect failures, the application:\n\n1. Connects to all replicas and subscribes to a dedicated channel for each replica.\n1. Connects to all replicas and periodically publishes a uniquely identifiable message.\n1. Monitors received messages and ensures that it is able to receive its own messages within a predetermined window of time.\n\nYou can also use known dataset changes to monitor the reliability of the replication stream,\nbut pub/sub is preferred method because:\n\n1. It does not involve dataset changes.\n1. It does not make any assumptions about the dataset.\n1. Pub/sub messages are delivered as replicated effects and are a more reliable indicator of a live replication link. In certain cases, dataset keys may appear to be modified even if the replication link fails. This happens because keys may receive updates through full-state replication (re-sync) or through online replication of effects."},{"id":"impact-of-sharding-on-failure-detection","title":"Impact of sharding on failure detection","role":"content","text":"If your sharding configuration is symmetric, make sure to use at least one key (PUB/SUB channels or real dataset key) per shard. Shards are replicated individually and are vulnerable to failure. Symmetric sharding configurations have the same number of shards and hash slots for all replicas.\nWe do not recommend an asymmetric sharding configuration, which requires at least one key per hash slot that intersects with a pair of shards.\n\nTo make sure that there is at least one key per shard, the application should:\n\n1. Use the Cluster API to retrieve the database sharding configuration.\n1. Compute a number of key names, such that there is one key per shard.\n1. Use those key names as channel names for the pub/sub mechanism."},{"id":"failing-over","title":"Failing over","role":"content","text":"When the application needs to failover to another replica, it should simply re-establish its connections with the endpoint on the remote replica. Because Active/Active and Redis replication are asynchronous, the remote endpoint may not have all of the locally performed and acknowledged writes.\n\nIt's best if your application doesn't read its own recent writes. Those writes can be either:\n\n1. Lost forever, if the local replica has an event such as a double failure or loss of persistent files.\n1. Temporarily unavailable, but will be available at a later time if the local replica's failure is temporary."},{"id":"failback-decision","title":"Failback decision","role":"content","text":"Your application can use the same checks described above to continue monitoring the state of the failed replica after failover.\n\nTo monitor the state of a replica during the failback process, you must make sure the replica is available, re-synced with the remote replicas, and not in stale mode. The PUB/SUB mechanism is an effective way to monitor this.\n\nDataset-based mechanisms are potentially less reliable for several reasons:\n1. In order to determine that a local replica is not stale, it is not enough to simply read keys from it. You must also attempt to write to it.\n1. As stated above, remote writes for some keys appear in the local replica before the replication link is back up and while the replica is still in stale mode.\n1. A replica that was never written to never becomes stale, so on startup it is immediately ready but serves stale data for a longer period of time."},{"id":"replica-configuration-changes","title":"Replica Configuration Changes","role":"content","text":"All failover and failback operations should be done strictly on the application side, and should not involve changes to the Active-Active configuration.\nThe only valid case for re-configuring the Active-Active deployment and removing a replica is when memory consumption becomes too high because garbage collection cannot be performed.\nOnce a replica is removed, it can only be re-joined as a new replica and it loses any writes that were not converged."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/databases/active-active/develop/data-types/hashes","title":"Hashes in an Active-Active databases","url":"https://redis.io/docs/latest/operate/rc/databases/active-active/develop/data-types/hashes/","summary":"Information about using hashes with an Active-Active database.","tags":["docs","operate","rs","rc"],"last_updated":"2025-09-03T12:06:34-05:00","page_type":"content","content_hash":"3ccbfb437f1545ab6352d00fed8335433fa501302178cc63b26c23d37409f438","sections":[{"id":"content","title":"Content","role":"content","text":"Hashes are great for structured data that contain a map of fields and\nvalues. They are used for managing distributed user or app session\nstate, user preferences, form data and so on. Hash fields contain string\ntype and string types operate just like the standard Redis string types\nwhen it comes to CRDTs. Fields in hashes can be initialized as a string\nusing HSET or HMSET or can be used to initialize counter types that are\nnumeric integers using HINCRBY or floats using HINCRBYFLOAT.\n\nHashes in Active-Active databases behave the same and maintain additional metadata to\nachieve an \"OR-Set\" behavior to handle concurrent conflicting writes.\nWith the OR-Set behavior, writes to add new fields across multiple Active-Active database\ninstances are typically unioned except in cases of conflicts.\nConflicting instance writes can happen when an Active-Active database instance deletes a\nfield while the other adds the same field. In this case and observed\nremove rule is followed. That is, remove can only remove fields it has\nalready seen and in all other cases element add/update wins.\n\nField values behave just like CRDT strings. String values can be types\nstring, counter integer based on the command used for initialization of\nthe field value. See \"String Data Type in Active-Active databases\" and \"String Data Type\nwith Counter Value in Active-Active databases\" for more details.\n\nHere is an example of an \"add wins\" case:\n\n|  **Time** | **CRDB Instance1** | **CRDB Instance2** |\n|  ------: | :------: | :------: |\n|  t1 | HSET key1 field1 \"a\" |  |\n|  t2 |  | HSET key1 field2 \"b\" |\n|  t4 | - Sync - | - Sync - |\n|  t5 | HGETALL key1<br/>1) \"field2\"<br/>2) \"b\"<br/>3) \"field1\"<br/>4) \"a\" | HGETALL key1<br/>1) \"field2\"<br/>2) \"b\"<br/>3) \"field1\"<br/>4) \"a\" |"}],"examples":[]}
{"schema_version":2,"id":"operate/rc/databases/active-active/develop/data-types/hyperloglog","title":"HyperLogLog in Active-Active databases","url":"https://redis.io/docs/latest/operate/rc/databases/active-active/develop/data-types/hyperloglog/","summary":"Information about using hyperloglog with an Active-Active database.","tags":["docs","operate","rs","rc"],"last_updated":"2026-09-24T09:16:24+01:00","page_type":"content","content_hash":"14c70ac1ccca2d825fa889da201502374cc526b70dec8fa1b7936263ac3d95e4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"**HyperLogLog** is an algorithm that addresses the [count-distinct problem](https://en.wikipedia.org/wiki/Count-distinct_problem).\nTo do this it approximates the numbers of items in a [set](https://en.wikipedia.org/wiki/Multiset).\nDetermining the _exact_ cardinality of a set requires memory according to the cardinality of the set.\nBecause it estimates the cardinality by probability, the HyperLogLog algorithm can run with more reasonable memory requirements."},{"id":"hyperloglog-in-redis","title":"HyperLogLog in Redis","role":"content","text":"Redis Open source implements [HyperLogLog](https://redis.io/docs/latest/develop/data-types/probabilistic/hyperloglogs) (HLL) as a native data structure.\nIt supports adding elements ([PFADD](https://redis.io/docs/latest/commands/pfadd) to an HLL, counting elements ([PFCOUNT](https://redis.io/docs/latest/commands/pfcount) of HLLs, and merging of ([PFMERGE](https://redis.io/docs/latest/commands/pfmerge) HLLs.\n\nHere is an example of a simple write case:\n\n| Time | Replica 1         | Replica 2         |\n| ---- | ----------------- | ----------------- |\n| t1   | PFADD hll x       |                   |\n| t2   | --- sync ---      |                   |\n| t3   |                   | PFADD hll y       |\n| t4   | --- sync ---      |                   |\n| t5   | PFCOUNT hll --> 2 | PFCOUNT hll --> 2 |\n\nHere is an example of a concurrent add case:\n\n| Time | Replica 1         | Replica 2         |\n| ---- | ----------------- | ----------------- |\n| t1   | PFADD hll x       | PFADD hll y       |\n| t2   | PFCOUNT hll --> 1 | PFCOUNT hll --> 1 |\n| t3   | --- sync ---      |                   |\n| t4   | PFCOUNT hll --> 2 | PFCOUNT hll --> 2 |"},{"id":"the-del-wins-approach","title":"The DEL-wins approach","role":"content","text":"Other collections in the Redis-CRDT implementation use the observed remove method to resolve conflicts.\nThe CRDT-HLL uses the DEL-wins method.\nIf a DEL request is received at the same time as any other request (ADD/MERGE/EXPIRE) on the HLL-key\nthe replicas consistently converge to delete key.\nIn the observed remove method used by other collections (sets, lists, sorted-sets and hashes),\nonly the replica that received the DEL request removes the elements, but elements added concurrently in other replicas exist in the consistently converged collection.\nWe chose to use the DEL-wins method for the CRDT-HLL to maintain the original time and space complexity of the HLL in Redis Open source.\n\nHere is an example of a DEL-wins case:\n\n| HLL  |                 |                 | \\|  | Set  |                     |                     |\n| ---- | --------------- | --------------- | --- | ---- | ------------------- | ------------------- |\n|      |                 |                 | \\|  |      |                     |                     |\n| Time | Replica 1       | Replica 2       | \\|  | Time | Replica 1           | Replica 2           |\n|      |                 |                 | \\|  |      |                     |                     |\n| t1   | PFADD h e1      |                 | \\|  | t1   | SADD s e1           |                     |\n| t2   | --- sync ---    |                 | \\|  | t2   | --- sync ---        |                     |\n| t3   | PFCOUNT h --> 1 | PFCOUNT h --> 1 | \\|  | t3   | SCARD s --> 1       | SCARD s --> 1       |\n| t4   | PFADD h e2      | Del h           | \\|  | t4   | SADD s e2           | Del S               |\n| t5   | PFCOUNT h --> 2 | PFCOUNT h --> 0 | \\|  | t5   | SCARD s --> 2       | SCARD s --> 0       |\n| t6   | --- sync ---    |                 | \\|  | t6   | --- sync ---        |                     |\n| t7   | PFCOUNT h --> 0 | PFCOUNT h --> 0 | \\|  | t7   | SCARD s --> 1       | SCARD s --> 1       |\n| t8   | Exists h --> 0  | Exists h --> 0  | \\|  | t8   | Exists s --> 1      | Exists s --> 1      |\n|      |                 |                 | \\|  | t9   | SMEMBERS s --> {e2} | SMEMBERS s --> {e2} |"},{"id":"hll-in-active-active-databases-versus-hll-in-redis-open-source","title":"HLL in Active-Active databases versus HLL in Redis Open source","role":"content","text":"In Active-Active databases, we implemented HLL within the CRDT on the basis of the Redis implementation with a few exceptions:\n\n- Redis keeps the HLL data structure as an encoded string object\n    such that you can potentially run any string request can on a key that contains an HLL. In CRDT, only get and set are supported for HLL.\n- In CRDT, if you do SET on a key that contains a value encoded as an HLL, then the value will remain an HLL. If the value is not encoded as HLL, then it will be a register."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/databases/active-active/develop/data-types","title":"Data types for Active-Active databases","url":"https://redis.io/docs/latest/operate/rc/databases/active-active/develop/data-types/","summary":"Introduction to differences in data types between standalone and Active-Active Redis databases.","tags":["docs","operate","rs","rc"],"last_updated":"2025-06-30T08:23:21-05:00","children":[{"id":"operate/rc/databases/active-active/develop/data-types/hashes","summary":"Information about using hashes with an Active-Active database.","title":"Hashes in an Active-Active databases","url":"https://redis.io/docs/latest/operate/rc/databases/active-active/develop/data-types/hashes/"},{"id":"operate/rc/databases/active-active/develop/data-types/hyperloglog","summary":"Information about using hyperloglog with an Active-Active database.","title":"HyperLogLog in Active-Active databases","url":"https://redis.io/docs/latest/operate/rc/databases/active-active/develop/data-types/hyperloglog/"},{"id":"operate/rc/databases/active-active/develop/data-types/json","summary":"Information about using JSON with an Active-Active database.","title":"JSON in Active-Active databases","url":"https://redis.io/docs/latest/operate/rc/databases/active-active/develop/data-types/json/"},{"id":"operate/rc/databases/active-active/develop/data-types/lists","summary":"Information about using list with an Active-Active database.","title":"Lists in Active-Active databases","url":"https://redis.io/docs/latest/operate/rc/databases/active-active/develop/data-types/lists/"},{"id":"operate/rc/databases/active-active/develop/data-types/sets","summary":"Information about using sets with an Active-Active database.","title":"Sets in Active-Active databases","url":"https://redis.io/docs/latest/operate/rc/databases/active-active/develop/data-types/sets/"},{"id":"operate/rc/databases/active-active/develop/data-types/sorted-sets","summary":"Information about using sorted sets with an Active-Active database.","title":"Sorted sets in Active-Active databases","url":"https://redis.io/docs/latest/operate/rc/databases/active-active/develop/data-types/sorted-sets/"},{"id":"operate/rc/databases/active-active/develop/data-types/streams","summary":"Information about using streams with an Active-Active database.","title":"Streams in Active-Active databases","url":"https://redis.io/docs/latest/operate/rc/databases/active-active/develop/data-types/streams/"},{"id":"operate/rc/databases/active-active/develop/data-types/strings","summary":"Information about using strings and bitfields with an Active-Active database.","title":"Strings and bitfields in Active-Active databases","url":"https://redis.io/docs/latest/operate/rc/databases/active-active/develop/data-types/strings/"}],"page_type":"content","content_hash":"52929c0f556302030cd20000a7d657bc41fe355d9d8a171f96cd461ecedb2390","sections":[{"id":"content","title":"Content","role":"content","text":"Active-Active databases use conflict-free replicated data types (CRDTs). From a developer perspective, most supported data types work the same for Active-Active and standard Redis databases. However, a few methods also come with specific requirements in Active-Active databases.\n\nEven though they look identical to standard Redis data types, there are specific rules that govern the handling of\nconflicting concurrent writes for each data type.\n\nAs conflict handling rules differ between data types, some commands have slightly different requirements in Active-Active databases versus standard Redis databases.\n\nSee the following articles for more information."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/databases/active-active/develop/data-types/json","title":"JSON in Active-Active databases","url":"https://redis.io/docs/latest/operate/rc/databases/active-active/develop/data-types/json/","summary":"Information about using JSON with an Active-Active database.","tags":["docs","operate","rs","rc"],"last_updated":"2026-09-24T09:16:24+01:00","page_type":"content","content_hash":"38a340da935a84abf52721470f99d2aba5338b3f1c2ba5bccb53c64503da0145","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Active-Active databases support JSON data structures.\n\nThe design is based on [A Conflict-Free Replicated JSON Datatype](https://arxiv.org/abs/1608.03960) by Kleppmann and Beresford, but the implementation includes some changes. Several [conflict resolution rule](#conflict-resolution-rules) examples were adapted from this paper as well."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"To use JSON in an Active-Active database, you must enable JSON during database creation.\n\nActive-Active Redis Cloud databases add JSON by default. See [Create an Active-Active subscription](https://redis.io/docs/latest/operate/rc/databases/active-active/create-active-active-database#select-capabilities) in the Redis Cloud documentation for details.\n\nIn Redis Software, Active-Active databases created with or upgraded to Redis version 8 or later automatically enable JSON. For earlier Redis versions, see [Create an Active-Active JSON database](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json/active-active#create-an-active-active-json-database) in the Redis Stack and Redis Software documentation for instructions."},{"id":"command-differences","title":"Command differences","role":"syntax","text":"Some JSON commands work differently for Active-Active databases."},{"id":"jsonclear","title":"`JSON.CLEAR`","role":"content","text":"[`JSON.CLEAR`](https://redis.io/docs/latest/commands/json.clear) resets JSON arrays and objects. It supports concurrent updates to JSON documents from different instances in an Active-Active database and allows the results to be merged."},{"id":"conflict-resolution-rules","title":"Conflict resolution rules","role":"content","text":"With Active-Active databases, it's possible for two different instances to try to run write operations on the same data at the same time. If this happens, conflicts can arise when the replicas attempt to sync these changes with each other. Conflict resolution rules determine how the database handles conflicting operations.\n\nThere are two types of conflict resolution:\n\n1. Merge:\n\n    - The operations are associative.\n\n    - Merges the results of both operations.\n\n1. Win over:\n\n    - The operations are not associative.\n\n    - One operation wins the conflict and sets the value.\n\n    - Ignores the losing operation.\n\nThe following conflict resolution rules show how Active-Active databases resolve conflicts for various JSON commands."},{"id":"assign-different-types-to-a-key","title":"Assign different types to a key","role":"content","text":"**Conflict**\n\nTwo instances concurrently assign values of different types to the same key within a JSON document.\n\nFor example:\n\nInstance 1 assigns an object to a key within a JSON document.\n\nInstance 2 assigns an array to the same key.\n\n**Resolution type**\n\nWin over\n\n**Resolution rule**\n\nThe instance with the smaller ID wins, so the key becomes an object in the given example.\n\n**Example**\n\n| Time  | Description | Instance 1 | Instance 2 |\n| :---: | :--- | :--- | :--- |\n| t1 | Set the same key to an object or an array | JSON.SET doc $.a '{}' | JSON.SET doc $.a '[]' |\n| t2 | Add data to the object and array | <nobr>JSON.SET doc  $.a.x '\"y\"'</nobr> <br /><br /> Result: <br /> {\"a\": {\"x\": \"y\"}} | <nobr>JSON.SET doc $.a '[\"z\"]'</nobr> <br /><br /> Result: <br /> {\"a\": [\"z\"]} |\n| t3 | Active-Active synchronization | – Sync – | – Sync – |\n| t4 | Instance 1 wins | JSON.GET doc $ <br /><br /> Result: <br /> {\"a\": {\"x\": \"y\"}} | JSON.GET doc $ <br /><br /> Result: <br /> {\"a\": {\"x\": \"y\"}} |"},{"id":"create-versus-create","title":"Create versus create","role":"content","text":"**Conflict**\n\nTwo instances concurrently use `JSON.SET` to assign a new JSON document to the same key.\n\n**Resolution type**\n\nWin over\n\n**Resolution rule**\n\nThe instance with the smaller ID wins.\n\n**Example**\n\n| Time  | Description | Instance 1 | Instance 2 |\n| :---: | :--- | :--- | :--- |\n| t1 | Create a new JSON document | <nobr>JSON.SET doc $ '{\"field\": \"a\"}'</nobr> | <nobr>JSON.SET doc $ '{\"field\": \"b\"}'</nobr> |\n| t2 | Active-Active synchronization | – Sync – | – Sync – |\n| t3 | Instance 1 wins | JSON.GET doc $ <br /><br /> Result: <br /> {\"field\": \"a\"} | JSON.GET doc $ <br /><br /> Result: <br /> {\"field\": \"a\"} |"},{"id":"create-versus-update","title":"Create versus update","role":"content","text":"**Conflict**\n\nInstance 1 creates a new document and assigns it to an existing key with `JSON.SET`.\n\nInstance 2 updates the existing content of the same key with `JSON.SET`.\n\n**Resolution type**\n\nWin over\n\n**Resolution rule**\n\nThe operation that creates a new document wins.\n\n**Example**\n\n| Time  | Description | Instance 1 | Instance 2 |\n| :---: | :--- | :--- | :--- |\n| t1 | The document exists on both instances | JSON.GET doc $ <br /><br /> Result: <br />  {\"field1\": \"value1\"} | JSON.GET doc $ <br /><br /> Result: <br />  {\"field1\": \"value1\"} |\n| t2 | Instance 1 creates a new document; instance 2 updates the existing document | <nobr>JSON.SET doc $ '{\"field2\": \"value2\"}'</nobr> | <nobr>JSON.SET doc $.field1 '[1, 2, 3]'</nobr> |\n| t3 | Active-Active synchronization | – Sync – | – Sync – |\n| t4 | Instance 1 wins | JSON.GET doc . <br /><br /> Result: <br /> {\"field2\": \"value2\"} | JSON.GET doc . <br /><br /> Result: <br /> {\"field2\": \"value2\"} |"},{"id":"delete-versus-create","title":"Delete versus create","role":"content","text":"**Conflict**\n\nInstance 1 deletes a JSON document with `JSON.DEL`.\n\nInstance 2 uses `JSON.SET` to create a new JSON document and assign it to the key deleted by instance 1.\n\n**Resolution type**\n\nWin over\n\n**Resolution rule**\n\nDocument creation wins over deletion.\n\n**Example**\n\n| Time  | Description | Instance 1 | Instance 2 |\n| :---: | :--- | :--- | :--- |\n| t1 | The document exists on both instances | JSON.GET doc $ <br /><br /> Result: <br /> {\"field1\": \"value1\"} | JSON.GET doc $ <br /><br /> Result: <br /> {\"field1\": \"value1\"} |\n| t2 | Instance 1 deletes the document; instance 2 creates a new document | JSON.DEL doc | <nobr>JSON.SET doc $ '{\"field1\": \"value2\"}'</nobr> |\n| t3 | Active-Active synchronization | – Sync – | – Sync – |\n| t4 | Instance 2 wins | JSON.GET doc $ <br /><br /> Result: <br /> <nobr>{\"field1\": \"value2\"}</nobr> | JSON.GET doc $ <br /><br /> Result: <br /> {\"field1\": \"value2\"} |"},{"id":"delete-versus-update","title":"Delete versus update","role":"content","text":"**Conflict**\n\nInstance 1 deletes a JSON document with `JSON.DEL`.\n\nInstance 2 updates the content of the same document with `JSON.SET`.\n\n**Resolution type**\n\nWin over\n\n**Resolution rule**\n\nDocument deletion wins over updates.\n\n**Example**\n\n| Time  | Description | Instance 1 | Instance 2 |\n| :---: | :--- | :--- | :--- |\n| t1 | The document exists on both instances | JSON.GET doc $ <br /><br /> Result: <br /> <nobr>{\"field1\": \"value1\"}</nobr> | JSON.GET doc $ <br /><br /> Result: <br /> {\"field1\": \"value1\"} |\n| t2 | Instance 1 deletes the document; instance 2 updates it | JSON.DEL doc | <nobr>JSON.SET doc $.field1 '[1, 2, 3]'</nobr> |\n| t3 | Active-Active synchronization | – Sync – | – Sync – |\n| t4 | Instance 1 wins | JSON.GET doc $ <br /><br /> Result: <br /> (nil) | JSON.GET doc $ <br /><br /> Result: <br /> (nil) |"},{"id":"update-versus-update","title":"Update versus update","role":"content","text":"**Conflict**\n\nInstance 1 updates a field inside a JSON document with `JSON.SET`.\n\nInstance 2 updates the same field with a different value.\n\n**Resolution type**\n\nWin over\n\n**Resolution rule**\n\nThe instance with the smallest ID wins.\n\n**Example**\n\n| Time  | Description | Instance 1 | Instance 2 |\n| :---: | :--- | :--- | :--- |\n| t1 | The document exists on both instances | JSON.GET doc $ <br /><br /> Result: <br /> {\"field\": \"a\"} | JSON.GET doc $ <br /><br /> Result: <br /> {\"field\": \"a\"} |\n| t2 | Update the same field with different data | <nobr>JSON.SET doc $.field \"b\"</nobr> | <nobr>JSON.SET doc $.field \"c\"</nobr> |\n| t3 | Active-Active synchronization | – Sync – | – Sync – |\n| t4 | Instance 1 wins | JSON.GET doc $ <br /><br /> Result: <br /> {\"field\": \"b\"} | JSON.GET doc $ <br /><br /> Result: <br /> {\"field\": \"b\"} |"},{"id":"update-versus-clear","title":"Update versus clear","role":"content","text":"The version of RedisJSON prior to v2.2 has two different ways to reset the content of a JSON object:\n\n- Assign a new empty JSON object:\n    \n    [code example]\n\n    If you use this method, it cannot be merged with a concurrent update.\n\n- For each key, remove it with `JSON.DEL`:\n\n    [code example]\n\n    With this method, it can merge the reset with concurrent updates.\n\nAs of RedisJSON v2.2, you can use the `JSON.CLEAR` command to reset the JSON document without removing each key manually. This method also lets concurrent updates be merged.\n\n#### Assign an empty object\n\n**Conflict**\n\nInstance 1 adds \"red\" to the existing \"colors\" object with `JSON.SET`.\n\nInstance 2 assigns a new empty object for \"colors\".\n\n**Resolution type**\n\nWin over\n\n**Resolution rule**\n\nDocument creation wins over the update, so the result will be an empty object.\n\n**Example**\n\n| Time  | Description | Instance 1 | Instance 2 |\n| :---: | :--- | :--- | :--- |\n| t1 | The document exists on both instances | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {\"blue\": \"#0000ff\"}} | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {\"blue\": \"#0000ff\"}} |\n| t2 | Instance 1 adds a new color; instance 2 resets colors to an empty object | <nobr>JSON.SET doc $.colors.red ‘#ff0000’</nobr> | JSON.SET doc $.colors ‘{}’ |\n| t3 | Instance 2 adds a new color |  | <nobr>JSON.SET doc $.colors.green ‘#00ff00’</nobr> |\n| t4 |  | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {\"blue\": \"#0000ff\", \"red\": \"#ff0000\"}} | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {\"green\": \"#00ff00\"}} |\n| t5 | Active-Active synchronization | – Sync – | – Sync – |\n| t6 | Instance 2 wins | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {\"green\": \"#00ff00\"}} | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {\"green\": \"#00ff00\"}} |\n\n#### Use `JSON.CLEAR`\n\n**Conflict**\n\nInstance 1 adds \"red\" to the existing \"colors\" object with `JSON.SET`.\n\nInstance 2 clears the \"colors\" object with `JSON.CLEAR` and adds \"green\" to \"colors\".\n\n**Resolution type**\n\nMerge\n\n**Resolution rule**\n\nMerges the results of all operations.\n\n**Example**\n\n| Time  | Description | Instance 1 | Instance 2 |\n| :---: | :--- | :--- | :--- |\n| t1 | The document exists on both instances | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {\"blue\": \"#0000ff\"}} | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {\"blue\": \"#0000ff\"}} |\n| t2 | Instance 1 adds a new color; instance 2 resets the colors | <nobr>JSON.SET doc $.colors.red ‘#ff0000’</nobr> | JSON.CLEAR doc $.colors |\n| t3 |  | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {\"blue\": \"#0000ff\", \"red\": \"#ff0000\"}} | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {}} |\n| t4 | Instance 2 adds a new color |  | <nobr>JSON.SET doc $.colors.green ‘#00ff00’</nobr> |\n| t5 |  |  | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {\"green\": \"#00ff00\"}} |\n| t6 | Active-Active synchronization | – Sync – | – Sync – |\n| t7 | Merges the results of both instances | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {\"red\": \"#ff0000\", \"green\": \"#00ff00\"}} | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {\"red\": \"#ff0000\", \"green\": \"#00ff00\"}} |"},{"id":"update-versus-update-array","title":"Update versus update array","role":"content","text":"**Conflict**\n\nTwo instances update the same existing array with different content.\n\n**Resolution type**\n\nMerge\n\n**Resolution rule**\n\nMerges the results of all operations on the array. Preserves the original element order from each instance.\n\n**Example**\n\n| Time  | Description | Instance 1 | Instance 2 |\n| :---: | :--- | :--- | :--- |\n| t1 | The document exists on both instances | JSON.GET doc $ <br /><br /> Result: <br /> '[\"a\", \"b\", \"c\"]' | JSON.GET doc $ <br /><br /> Result: <br /> '[\"a\", \"b\", \"c\"]' |\n| t2 | Instance 1 removes an array element; instance 2 adds one | JSON.ARRPOP doc $ 1 <br /><br /> Result: <br /> [\"a\", \"c\"] | <nobr>JSON.ARRINSERT doc $ 0 ‘\"y\"’</nobr> <br /><br /> Result: <br /> [\"y\", \"a\", \"b\", \"c\"] |\n| t3 | Both instances add another element to the array | <nobr>JSON.ARRINSERT doc $ 1 ‘\"x\"’</nobr> <br /><br /> Result: <br /> [\"a\", \"x\", \"c\"] | <nobr>JSON.ARRINSERT doc $ 2 ‘\"z\"’</nobr> <br /><br /> Result: <br /> [\"y\", \"a\", \"z\", \"b\", \"c\"] |\n| t4 | Active-Active synchronization | – Sync – | – Sync – |\n| t5 | Merge results from both instances | JSON.GET doc $ <br /><br /> Result: <br /> [\"y\", \"a\", \"x\", \"z\", \"c\"] | JSON.GET doc $ <br /><br /> Result: <br /> [\"y\", \"a\", \"x\", \"z\", \"c\"] |"},{"id":"update-versus-delete-array-element","title":"Update versus delete array element","role":"content","text":"**Conflict**\n\nInstance 1 removes an element from a JSON array with `JSON.ARRPOP`.\n\nInstance 2 updates the same element that instance 1 removes.\n\n**Resolution type**\n\nWin over\n\n**Resolution rule**\n\nDeletion wins over updates.\n\n**Example**\n\n| Time  | Description | Instance 1 | Instance 2 |\n| :---: | :--- | :--- | :--- |\n| t1 | The document exists on both instances | JSON.GET doc $ <br /><br /> Result: <br /> {\"todo\": [{\"title\": \"buy milk\", \"done\": false}]} | JSON.GET doc $ <br /><br /> Result: <br /> {\"todo\": [{\"title\": \"buy milk\", \"done\": false}]} |\n| t2 | Instance 1 removes an array element; instance 2 updates the same element | <nobr>JSON.ARRPOP doc $.todo 0</nobr> | <nobr>JSON.SET doc '$.todo[0][\"done\"]' 'true'’</nobr> |\n| t3 |  | JSON.GET doc $ <br /><br /> Result: <br /> {\"todo\": []} | JSON.GET doc $ <br /><br /> Result: <br /> [{\"title\": \"buy milk\", \"done\": true}]} |\n| t4 | Active-Active synchronization | – Sync – | – Sync – |\n| t5 | Instance 1 wins | JSON.GET doc $ <br /><br /> Result: <br /> doc = {\"todo\": []} | JSON.GET doc $ <br /><br /> Result: <br /> doc = {\"todo\": []} |"},{"id":"update-versus-update-object","title":"Update versus update object","role":"content","text":"**Conflict**\n\nBoth instances update the same existing object with different content.\n\n**Resolution type**\n\nMerge\n\n**Resolution rule**\n\nMerges the results of all operations on the object.\n\n**Example**\n\n| Time  | Description | Instance 1 | Instance 2 |\n| :---: | :--- | :--- | :--- |\n| t1 | The document exists on both instances | JSON.GET doc $ <br /><br /> Result: <br /> '{\"grocery\": []}' | JSON.GET doc $ <br /><br /> Result: <br /> '{\"grocery\": []}' |\n| t2 | Add new elements to the array | <nobr>JSON.ARRAPPEND doc $.grocery ‘\"eggs\"’</nobr> | JSON.ARRAPPEND doc $.grocery ‘\"milk\"’ |\n| t3 | Add new elements to the array | JSON.ARRAPPEND doc $.grocery ‘\"ham\"’ | <nobr>JSON.ARRAPPEND doc $.grocery ‘\"flour\"’</nobr> |\n| t4 |  | JSON.GET doc $ <br /><br /> Result: <br /> {\"grocery\":[\"eggs\", \"ham\"]} | JSON.GET doc $ <br /><br /> Result: <br /> {\"grocery\":[\"milk\", \"flour\"]} |\n| t5 | Active-Active synchronization | – Sync – | – Sync – |\n| t6 | Merges the results from both instances | JSON.GET doc . <br /><br /> Result: <br /> {\"grocery\":[\"eggs\",\"ham\",\"milk\", \"flour\"]} | JSON.GET doc . <br /><br /> Result: <br /> {\"grocery\":[\"eggs\",\"ham\",\"milk\", \"flour\" ]} |"}],"examples":[{"id":"update-versus-clear-ex0","language":"sh","code":"JSON.SET doc $.colors '{}'","section_id":"update-versus-clear"},{"id":"update-versus-clear-ex1","language":"sh","code":"JSON.DEL doc $.colors.blue","section_id":"update-versus-clear"}]}
{"schema_version":2,"id":"operate/rc/databases/active-active/develop/data-types/lists","title":"Lists in Active-Active databases","url":"https://redis.io/docs/latest/operate/rc/databases/active-active/develop/data-types/lists/","summary":"Information about using list with an Active-Active database.","tags":["docs","operate","rs","rc"],"last_updated":"2025-09-03T12:06:34-05:00","page_type":"content","content_hash":"a5bc3695c5bd200e6e9bf1efa8896a4ce97de5f953fcc189fb46bd859df52bc7","sections":[{"id":"content","title":"Content","role":"content","text":"Redis lists are simply lists of strings, sorted by insertion order. It\nis possible to add elements to a Redis List that push new elements to\nthe head (on the left) or to the tail (on the right) of the list. Redis\nlists can be used to easily implement queues (using LPUSH and RPOP, for\nexample) and stacks (using LPUSH and LPOP, for\nexample).\n\nLists in Active-Active databases are just the same as regular Redis Lists. See the\nfollowing examples to get familiar with Lists' behavior in an\nActive-Active database.\n\nSimple Lists\nexample:\n\n|  **Time** | **CRDB Instance 1** | **CRDB Instance 2** |\n|  ------: | :------: | :------: |\n|  t1 | LPUSH mylist \"hello\" |  |\n|  t2 | — Sync — | — Sync — |\n|  t3 |  | LPUSH mylist \"world\" |\n|  t4 | — Sync — | — Sync — |\n|  t5 | LRANGE mylist 0 -1 =>\"world\" \"hello\" | LRANGE mylist 0 -1 => \"world\" \"hello\" |\n\n**Explanation**:\nThe final list contains both the \"world\" and \"hello\" elements, in that\norder (Instance 2 observed \"hello\" when it added\n\"world\").\n\nExample of Lists with Concurrent\nInsertions:\n\n|  **Time** | **CRDB Instance 1** | **CRDB Instance 2** |\n|  ------: | :------: | :------: |\n|  t1 | LPUSH L x |  |\n|  t2 | — Sync — | — Sync — |\n|  t3 | LINSERT L AFTER x y1 |  |\n|  t4 |  | LINSERT L AFTER x y2 |\n|  t5 | LRANGE L 0 -1 => x y1 | LRANGE L 0 -1 => x y2 |\n|  t6 | — Sync — | — Sync — |\n|  t7 | LRANGE L 0 -1 => x y1 y2 | LRANGE L 0 -1 => x y1 y2 |\n\n**Explanation**:\nInstance 1 added an element y1 after x, and then Instance 2 added element y2 after x.\nThe final List contains all three elements: x is the first element, after it y1 and then y2.\nThe Active-Active database resolves the conflict arbitrarily but applies the resolution consistently across all Active-Active database instances.\n\nExample of Deleting a List while Pushing a New\nElement:\n\n|  **Time** | **CRDB Instance 1** | **CRDB Instance 2** |\n|  ------: | :------: | :------: |\n|  t1 | LPUSH L x |  |\n|  t2 | — Sync — | — Sync — |\n|  t3 | LRANGE L 0 -1 => x | LRANGE L 0 -1 => x |\n|  t4 | LPUSH L y | DEL L |\n|  t5 | — Sync — | — Sync — |\n|  t6 | LRANGE L 0 -1 => y | LRANGE L 0 -1 => y |\n\n**Explanation**\nAt t4 - t6, DEL deletes only observed elements. This is why L still\ncontains y.\n\nExample of Popping Elements from a\nList:\n\n|  **Time** | **CRDB Instance 1** | **CRDB Instance 2** |\n|  ------: | :------: | :------: |\n|  t1 | LPUSH L x y z |  |\n|  t2 | — Sync — | — Sync — |\n|  t3 |  | RPOP L => x |\n|  t4 | — Sync — | — Sync — |\n|  t5 | RPOP L => y |  |\n|  t6 | — Sync — | — Sync — |\n|  t7 | RPOP L => z | RPOP L => z |\n\n**Explanation**:\nAt t1, the operation pushes elements x, y, z to List L. At t3, the\nsequential pops behave as expected from a queue. At t7, the concurrent\npop in both instances might show the same result. The instance was not\nable to sync regarding the z removal so, from the point of view of each\ninstance, z is located in the List and can be popped. After syncing,\nboth lists are empty.\n\nBe aware of the behavior of Lists in Active-Active databases when using List as a stack\nor queue. As seen in the above example, two parallel RPOP operations\nperformed by two different Active-Active database instances can get the same element in\nthe case of a concurrent operation. Lists in Active-Active databases guarantee that each\nelement is POP-ed at least once, but cannot guarantee that each\nelement is POP-ed only once. Such behavior should be taken into\naccount when, for example, using Lists in Active-Active databases as building blocks for\ninter-process communication systems.\n\nIn that case, if the same element cannot be handled twice by the\napplications, it's recommended that the POP operations be performed by\none Active-Active database instance, whereas the PUSH operations can be performed by\nmultiple instances."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/databases/active-active/develop/data-types/sets","title":"Sets in Active-Active databases","url":"https://redis.io/docs/latest/operate/rc/databases/active-active/develop/data-types/sets/","summary":"Information about using sets with an Active-Active database.","tags":["docs","operate","rs","rc"],"last_updated":"2025-09-03T12:06:34-05:00","page_type":"content","content_hash":"ed52a8e6dd4890a60934975d9a50904b42be46367af3f1f960383a55f92b08ce","sections":[{"id":"content","title":"Content","role":"content","text":"A Redis set is an unordered collection of strings. It is possible to\nadd, remove, and test for the existence of members with Redis commands.\nA Redis set maintains a unique collection of elements. Sets can be great\nfor maintaining a list of events (click streams), users (in a group\nconversation), products (in recommendation lists), engagements (likes,\nshares) and so on.\n\nSets in Active-Active databases behave the same and maintain additional metadata to\nachieve an \"OR-Set\" behavior to handle concurrent conflicting\nwrites. With the OR-Set behavior, writes across multiple Active-Active database instances\nare typically unioned except in cases of conflicts. Conflicting instance\nwrites can happen when a Active-Active database instance deletes an element while the\nother adds the same element. In this case and observed remove rule is\nfollowed. That is, remove can only remove instances it has already seen\nand in all other cases element add wins.\n\nHere is an example of an \"add wins\" case:\n\n|  **Time** | **CRDB Instance1** | **CRDB Instance2** |\n|  ------: | :------: | :------: |\n|  t1 | SADD key1 \"a\" |  |\n|  t2 |  | SADD key1 \"b\" |\n|  t3 | SMEMBERS key1 \"a\" | SMEMBERS key1 \"b\" |\n|  t4 | — Sync — | — Sync — |\n|  t3 | SMEMBERS key1 \"a\" \"b\" | SMEMBERS key1 \"a\" \"b\" |\n\nHere is an example of an \"observed remove\" case.\n\n|  **Time** | **CRDB Instance1** | **CRDB Instance2** |\n|  ------: | :------: | :------: |\n|  t1 | SMEMBERS key1 \"a\" \"b\" | SMEMBERS key1 \"a\" \"b\" |\n|  t2 | SREM key1 \"a\" | SADD key1 \"c\" |\n|  t3 | SREM key1 \"c\" |  |\n|  t4 | — Sync — | — Sync — |\n|  t3 | SMEMBERS key1 \"c\" \"b\" | SMEMBERS key1 \"c\" \"b\" |"}],"examples":[]}
{"schema_version":2,"id":"operate/rc/databases/active-active/develop/data-types/sorted-sets","title":"Sorted sets in Active-Active databases","url":"https://redis.io/docs/latest/operate/rc/databases/active-active/develop/data-types/sorted-sets/","summary":"Information about using sorted sets with an Active-Active database.","tags":["docs","operate","rs","rc"],"last_updated":"2026-09-24T09:16:24+01:00","page_type":"content","content_hash":"e2fb1c479e7984eb62a959b36948c1b819ac43b006eac92932aeaedcfd546940","sections":[{"id":"content","title":"Content","role":"content","text":"> [!NOTE]\n> [Redis Geospatial (Geo)](https://redis.io/docs/latest/commands/geoadd) is based on Sorted Sets, so the same Active-Active database development instructions apply to Geo.\n\nSimilar to Redis Sets, Redis Sorted Sets are non-repeating collections\nof Strings. The difference between the two is that every member of a\nSorted Set is associated with a score used to order the Sorted Set from\nlowest to highest. While members are unique, they may have the same\nscore.\n\nWith Sorted Sets, you can quickly add, remove or update elements as\nwell as get ranges by score or by rank (position). Sorted Sets in Active-Active databases\nbehave the same and maintain additional metadata to handle concurrent\nconflicting writes. Conflict resolution is done in two\nphases:\n\n1. First, the database resolves conflict at the set level using \"OR\n    Set\" (Observed-Remove Set). With OR-Set behavior, writes across\n    multiple Active-Active database instances are typically unioned except in cases of\n    conflicts. Conflicting writes can happen when an Active-Active database instance\n    deletes an element while the other adds or updates the same element.\n    In this case, an observed Remove rule is followed, and only\n    instances it has already seen are removed. In all other cases, the\n    Add / Update element wins.\n1. Second, the database resolves conflict at the score level. In this\n    case, the score is treated as a counter and applies the same\n    conflict resolution as regular counters.\n\nSee the following examples to get familiar with Sorted Sets'\nbehavior in Active-Active database:\n\nExample of Simple Sorted Set with No\nConflict:\n\n|  **Time** | **CRDB Instance 1** | **CRDB Instance 2** |\n|  ------: | :------: | :------: |\n|  t1 | ZADD Z 1.1 x |  |\n|  t2 | — Sync — | — Sync — |\n|  t3 |  | ZADD Z 1.2 y |\n|  t4 | — Sync — | — Sync — |\n|  t5 | ZRANGE Z 0 -1 => x y | ZRANGE Z 0 -1 => x y |\n\n**Explanation**:\nWhen adding two different elements to a Sorted Set from different\nreplicas (in this example, x with score 1.1 was added by Instance 1 to\nSorted Set Z, and y with score 1.2 was added by Instance 2 to Sorted Set\nZ) in a non-concurrent manner (i.e. each operation happened separately\nand after both instances were in sync), the end result is a Sorted\nSet including both elements in each Active-Active database instance.\nExample of Sorted Set and Concurrent\nAdd:\n\n|  **Time** | **CRDB Instance 1** | **CRDB Instance 2** |\n|  ------: | :------: | :------: |\n|  t1 | ZADD Z 1.1 x |  |\n|  t2 |  | ZADD Z 2.1 x |\n|  t3 | ZSCORE Z x => 1.1 | ZSCORE Z x => 2.1 |\n|  t4 | — Sync — | — Sync — |\n|  t5 | ZSCORE Z x => 2.1 | ZSCORE Z x => 2.1 |\n\n**Explanation**:\nWhen concurrently adding an element x to a Sorted Set Z by two different\nActive-Active database instances (Instance 1 added score 1.1 and Instance 2 added score\n2.1), the Active-Active database implements Last Write Win (LWW) to determine the score of\nx. In this scenario, Instance 2 performed the ZADD operation at time\nt2\\>t1 and therefore the Active-Active database sets the score 2.1 to\nx.\n\nExample of Sorted Set with Concurrent Add Happening at the Exact Same\nTime:\n\n|  **Time** | **CRDB Instance 1** | **CRDB Instance 2** |\n|  ------: | :------: | :------: |\n|  t1 | ZADD Z 1.1 x | ZADD Z 2.1 x |\n|  t2 | ZSCORE Z x => 1.1 | ZSCORE Z x => 2.1 |\n|  t3 | — Sync — | — Sync — |\n|  t4 | ZSCORE Z x => 1.1 | ZSCORE Z x => 1.1 |\n\n**Explanation**:\nThe example above shows a relatively rare situation, in which two Active-Active database\ninstances concurrently added the same element x to a Sorted Set at the\nsame exact time but with a different score, i.e. Instance 1 added x with\na 1.1 score and Instance 2 added x with a 2.1 score. After syncing, the\nActive-Active database realized that both operations happened at the same time and\nresolved the conflict by arbitrarily (but consistently across all Active-Active database\ninstances) giving precedence to Instance 1.\nExample of Sorted Set with Concurrent Counter\nIncrement:\n\n|  **Time** | **CRDB Instance 1** | **CRDB Instance 2** |\n|  ------: | :------: | :------: |\n|  t1 | ZADD Z 1.1 x |  |\n|  t2 | — Sync — | — Sync — |\n|  t3 | ZINCRBY Z 1.0 x | ZINCRBY Z 1.0 x |\n|  t4 | — Sync — | — Sync — |\n|  t5 | ZSCORE Z x => 3.1 | ZSCORE Z x => 3.1 |\n\n**Explanation**:\nThe result is the sum of all\nZINCRBY\noperations performed by all Active-Active database instances.\n\nExample of Removing an Element from a Sorted\nSet:\n\n|  **Time** | **CRDB Instance 1** | **CRDB Instance 2** |\n|  ------: | :------: | :------: |\n|  t1 | ZADD Z 4.1 x |  |\n|  t2 | — Sync — | — Sync — |\n|  t3 | ZSCORE Z x => 4.1 | ZSCORE Z x => 4.1 |\n|  t4 | ZREM Z x | ZINCRBY Z 2.0 x |\n|  t5 | ZSCORE Z x => nill | ZSCORE Z x => 6.1 |\n|  t6 | — Sync — | — Sync — |\n|  t7 | ZSCORE Z x => 2.0 | ZSCORE Z x => 2.0 |\n\n**Explanation**:\nAt t4 - t5, concurrent ZREM and ZINCRBY operations ran on Instance 1\nand Instance 2 respectively. Before the instances were in sync, the ZREM\noperation could only delete what had been seen by Instance 1, so\nInstance 2 was not affected. Therefore, the ZSCORE operation shows the\nlocal effect on x. At t7, after both instances were in-sync, the Active-Active database\nresolved the conflict by subtracting 4.1 (the value of element x in\nInstance 1) from 6.1 (the value of element x in Instance 2)."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/databases/active-active/develop/data-types/streams","title":"Streams in Active-Active databases","url":"https://redis.io/docs/latest/operate/rc/databases/active-active/develop/data-types/streams/","summary":"Information about using streams with an Active-Active database.","tags":["docs","operate","rs","rc"],"last_updated":"2026-09-24T09:16:24+01:00","page_type":"content","content_hash":"a359e3e6e9266d2372251e4cc8c21fbf11d17ba205ee153a79e56c9392864855","sections":[{"id":"overview","title":"Overview","role":"overview","text":"A [Redis Stream](https://redis.io/docs/latest/develop/data-types/streams) is a data structure that acts like an append-only log.\nEach stream entry consists of:\n\n- A unique, monotonically increasing ID\n- A payload consisting of a series key-value pairs\n\nYou add entries to a stream with the XADD command. You access stream entries using the XRANGE, XREADGROUP, and XREAD commands (however, see the caveat about XREAD below)."},{"id":"streams-and-active-active","title":"Streams and Active-Active","role":"content","text":"Active-Active databases allow you to write to the same logical stream from more than one region.\nStreams are synchronized across the regions of an Active-Active database.\n\nIn the example below, we write to a stream concurrently from two regions. Notice that after syncing, both regions have identical streams:\n\n<table style='width: auto;'>\n<thead>\n<tr>\n<th>Time</th>\n<th>Region 1</th>\n<th>Region 2</th>\n</tr>\n</thead>\n\n<tbody>\n<tr>\n<td><em>t1</em></td>\n<td><code>XADD messages * text hello</code></td>\n<td><code>XADD messages * text goodbye</code></td>\n</tr>\n\n<tr>\n<td><em>t2</em></td>\n<td><code>XRANGE messages - +</code> <br/><strong>→ [1589929244828-1]</strong></td>\n<td><code>XRANGE messages - +</code> <br/><strong>→ [1589929246795-2]</strong></td>\n</tr>\n\n<tr>\n<td><em>t3</em></td>\n<td><em>— Sync —</em></td>\n<td><em>— Sync —</em></td>\n</tr>\n\n<tr>\n<td><em>t4</em></td>\n<td><code>XRANGE messages - +</code> <br/><strong>→ [1589929244828-1, 1589929246795-2]</strong></td>\n<td><code>XRANGE messages - +</code> <br/><strong>→ [1589929244828-1, 1589929246795-2]</strong></td>\n</tr>\n</tbody>\n</table>\n\nNotice also that the synchronized streams contain no duplicate IDs. As long as you allow the database to generate your stream IDs, you'll never have more than one stream entry with the same ID.\n\n> [!NOTE]\n> Redis Open Source uses one radix tree (referred to as `rax` in the code base) to implement each stream. However, Active-Active databases implement a single logical stream using one `rax` per region.\n> Each region adds entries only to its associated `rax` (but can remove entries from all `rax` trees).\n> This means that XREAD and XREADGROUP iterate simultaneously over all `rax` trees and return the appropriate entry by comparing the entry IDs from each `rax`."},{"id":"conflict-resolution","title":"Conflict resolution","role":"content","text":"Active-Active databases use an \"observed-remove\" approach to automatically resolve potential conflicts.\n\nWith this approach, a delete only affects the locally observable data.\n\nIn the example below, a stream, `x`, is created at _t1_. At _t3_, the stream exists in two regions.\n\n<table style='width: 100%;'>\n<thead>\n<tr>\n<th>Time</th>\n<th>Region 1</th>\n<th>Region 2</th>\n</tr>\n</thead>\n\n<tbody>\n<tr>\n<td><em>t1</em></td>\n<td><code>XADD messages * text hello</code></td>\n<td></td>\n</tr>\n\n<tr>\n<td><em>t2</em></td>\n<td><em>— Sync —</em></td>\n<td><em>— Sync —</em></td>\n</tr>\n\n<tr>\n<td><em>t3</em></td>\n<td><code>XRANGE messages - +</code> <br/><strong>→ [1589929244828-1]</strong></code></td>\n<td><code>XRANGE messages - +</code> <br/><strong>→ [1589929244828-1]</strong></code></td>\n</tr>\n\n\n<tr>\n<td><em>t4</em></td>\n<td><code>DEL messages</code></strong></td>\n<td><code>XADD messages * text goodbye</code></td>\n</tr>\n\n<tr>\n<td><em>t5</em></td>\n<td><em>— Sync —</em></td>\n<td><em>— Sync —</em></td>\n</tr>\n\n\n<tr>\n<td><em>t6</em></td>\n<td><code>XRANGE messages - +</code> <br/><strong>→ [1589929246795-2]</strong></code></td>\n<td><code>XRANGE messages - +</code> <br/><strong>→ [1589929246795-2]</strong></code></td>\n</tr>\n\n\n</tbody>\n</table>\n\nAt _t4_, the stream is deleted from Region 1. At the same time, an entry with ID ending in `3700` is added to the same stream at Region 2. After the sync, at _t6_, the entry with ID ending in `3700` exists in both regions. This is because that entry was not visible when the local stream was deleted at _t4_."},{"id":"id-generation-modes","title":"ID generation modes","role":"content","text":"Usually, you should allow Redis streams generate its own stream entry IDs. You do this by specifying `*` as the ID in calls to XADD. However, you _can_ provide your own custom ID when adding entries to a stream.\n\nBecause Active-Active databases replicate asynchronously, providing your own IDs can create streams with duplicate IDs. This can occur when you write to the same stream from multiple regions.\n\n| Time | Region 1                      | Region 2                      |\n| ---- | ------------------------------- | ------------------------------- |\n| _t1_   | `XADD x 100-1 f1 v1`              | `XADD x 100-1 f1 v1`              |\n| _t2_   | _— Sync —_                    | _— Sync —_                    |\n| _t3_   | `XRANGE x - +`<br/> **→ [100-1, 100-1]** | `XRANGE x - +`<br/> **→ [100-1, 100-1]** |\n\nIn this scenario, two entries with the ID `100-1` are added at _t1_. After syncing, the stream `x` contains two entries with the same ID.\n\n> [!NOTE]\n> Stream IDs in Redis Open Source consist of two integers separated by a dash ('-'). When the server generates the ID, the first integer is the current time in milliseconds, and the second integer is a sequence number. So, the format for stream IDs is MS-SEQ.\n\nTo prevent duplicate IDs and to comply with the original Redis streams design, Active-Active databases provide three ID modes for XADD:\n\n1. **Strict**: In _strict_ mode, XADD allows server-generated IDs (using the '`*`' ID specifier) or IDs consisting only of the millisecond (MS) portion. When the millisecond portion of the ID is provided, the ID's sequence number is calculated using the database's region ID. This prevents duplicate IDs in the stream. Strict mode rejects full IDs (that is, IDs containing both milliseconds and a sequence number).\n1. **Semi-strict**: _Semi-strict_ mode is just like _strict_ mode except that it allows full IDs (MS-SEQ). Because it allows full IDs, duplicate IDs are possible in this mode.\n1. **Liberal**: XADD allows any monotonically ascending ID. When given the millisecond portion of the ID, the sequence number will be set to `0`. This mode may also lead to duplicate IDs.\n\nThe default and recommended mode is _strict_, which prevents duplicate IDs.\n\n> [!WARNING]\n> Why do you want to prevent duplicate IDs? First, XDEL, XCLAIM, and other commands can affect more than one entry when duplicate IDs are present in a stream. Second, duplicate entries may be removed if a database is exported or renamed.\n\nTo change XADD's ID generation mode, use the `rladmin` command-line utility:\n\nSet _strict_ mode:\n[code example]\n\nSet _semi-strict_ mode:\n[code example]\n\nSet _liberal_ mode:\n[code example]"},{"id":"iterating-a-stream-with-xread","title":"Iterating a stream with XREAD","role":"content","text":"In Redis Open Source and in non-Active-Active databases, you can use XREAD to iterate over the entries in a Redis Stream. However, with an Active-Active database, XREAD may skip entries. This can happen when multiple regions write to the same stream.\n\nIn the example below, XREAD skips entry `115-2`.\n\n| Time | Region 1                                         | Region 2                                         |\n| ---- | -------------------------------------------------- | -------------------------------------------------- |\n| _t1_   | `XADD x 110 f1 v1`                                   | `XADD x 115 f1 v1`                                   |\n| _t2_   | `XADD x 120 f1 v1`                                   |                                                    |\n| _t3_   | `XADD x 130 f1 v1`                                   |                                                    |\n| _t4_   | `XREAD COUNT 2 STREAMS x 0` <br/>**→ [110-1, 120-1]**       |                                                    |\n| _t5_   | _— Sync —_                                       | _— Sync —_                                       |\n| _t6_   | `XREAD COUNT 2 STREAMS x 120-1` <br/>**→ [130-1]**            |                                                    |\n| _t7_   | `XREAD STREAMS x 0` <br/>**→[110-1, 115-2, 120-1, 130-1]** | `XREAD STREAMS x 0` <br/>**→[110-1, 115-2, 120-1, 130-1]** |\n\n\nYou can use XREAD to reliably consume a stream only if all writes to the stream originate from a single region. Otherwise, you should use XREADGROUP, which always guarantees reliable stream consumption."},{"id":"consumer-groups","title":"Consumer groups","role":"content","text":"Active-Active databases fully support consumer groups with Redis Streams. Here is an example of creating two consumer groups concurrently:\n\n| Time | Region 1                  | Region 2                  |\n| ---- | --------------------------- | --------------------------- |\n| _t1_   | `XGROUP CREATE x group1 0`        | `XGROUP CREATE x group2 0`        |\n| _t2_   | `XINFO GROUPS x` <br/>**→ [group1]**     | `XINFO GROUPS x` <br/>**→ [group2]**     |\n| _t3_   | _— Sync —_                | — Sync —                |\n| _t4_   | `XINFO GROUPS x` <br/>**→ [group1, group2]** | `XINFO GROUPS x` <br/>**→ [group1, group2]** |\n\n\n> [!NOTE]\n> Redis Open Source uses one radix tree (`rax`) to hold the global pending entries list and another `rax` for each consumer's PEL.\n> The global PEL is a unification of all consumer PELs, which are disjoint.\n>\n> An Active-Active database stream maintains a global PEL and a per-consumer PEL for each region.\n>\n> When given an ID different from the special \">\" ID, XREADGROUP iterates simultaneously over all of the PELs for all consumers.\n> It returns the next entry by comparing entry IDs from the different PELs."},{"id":"conflict-resolution-1","title":"Conflict resolution","role":"content","text":"The \"delete wins\" approach is a way to automatically resolve conflicts with consumer groups.\nIn case of concurrent consumer group operations, a delete will \"win\" over other concurrent operations on the same group.\n\nIn this example, the DEL at _t4_ deletes both the observed `group1` and the non-observed `group2`:\n\n| Time | Region 1              | Region 2              |\n| ---- | ----------------------- | ----------------------- |\n| _t1_   | `XGROUP CREATE x group1 0`    |                         |\n| _t2_   | _— Sync —_            | _— Sync —_            |\n| _t3_   | `XINFO GROUPS x` <br/>**→ [group1]** | `XINFO GROUPS x` <br/>**→ [group1]** |\n| _t4_   | `DEL x`                   | `XGROUP CREATE x group2 0`    |\n| _t5_   | _— Sync —_            | _— Sync —_            |\n| _t6_   | `EXISTS x` <br/>**→ 0** | `EXISTS x` <br/>**→  0**          |\n\nIn this example, the XGROUP DESTROY at _t4_ affects both the observed `group1` created in Region 1 and the non-observed `group1` created in Region 3:\n\n| time | Region 1              | Region 2              | Region 3            |\n| ---- | ----------------------- | ----------------------- | --------------------- |\n| _t1_   | `XGROUP CREATE x group1 0`    |                         |                       |\n| _t2_   | _— Sync —_            | _— Sync —_            |                       |\n| _t3_   | `XINFO GROUPS x` <br/>**→ [group1]** | `XINFO GROUPS x` <br/>**→  [group1]** | `XINFO GROUPS x` <br/>**→  []** |\n| _t4_   |                         | `XGROUP DESTROY x group1`     | `XGROUP CREATE x group1 0` |\n| _t5_   | _— Sync —_            | _— Sync —            | — Sync —          |\n| _t6_   | `EXISTS x` <br/>**→  0**          | `EXISTS x` <br/>**→ 0**          | `EXISTS x` <br/>**→  0**    |"},{"id":"group-replication","title":"Group replication","role":"content","text":"Calls to XREADGROUP and XACK change the state of a consumer group or consumer. However, it's not efficient to replicate every change to a consumer or consumer group.\n\nTo maintain consumer groups in Active-Active databases with optimal performance:\n\n1. Group existence (CREATE/DESTROY) is replicated.\n1. Most XACK operations are replicated.\n1. Other operations, such as XGROUP, SETID, DELCONSUMER, are not replicated.\n\nFor example:\n\n| Time | Region 1                                        | Region 2               |\n| ---- | ------------------------------------------------- | ------------------------ |\n| _t1_   | `XADD messages 110 text hello`                                |                          |\n| _t2_   | `XGROUP CREATE messages group1 0`                              |                          |\n| _t3_   | `XREADGROUP GROUP group1 Alice STREAMS messages >` <br/>**→ [110-1]** |                          |\n| _t4_   | _— Sync —_                                      | _— Sync —_             |\n| _t5_   | `XRANGE messages - +` <br/>**→ [110-1]**                          | XRANGE messages - + <br/>**→ [110-1]** |\n| _t6_   | `XINFO GROUPS messages` <br/>**→ [group1]**                            | XINFO GROUPS messages <br/>**→ [group1]**   |\n| _t7_   | `XINFO CONSUMERS messages group1` <br/>**→ [Alice]**                      | XINFO CONSUMERS messages group1 <br/>**→ []**  |\n| _t8_   | `XPENDING messages group1 - + 1` <br/>**→ [110-1]**                     | XPENDING messages group1 - + 1<br/>**→ []** |\n\nUsing XREADGROUP across regions can result in regions reading the same entries.\nThis is due to the fact that Active-Active Streams is designed for at-least-once reads or a single consumer.\nAs shown in the previous example, Region 2 is not aware of any consumer group activity, so redirecting the XREADGROUP traffic from Region 1 to Region 2 results in reading entries that have already been read."},{"id":"replication-performance-optimizations","title":"Replication performance optimizations","role":"content","text":"Consumers acknowledge messages using the XACK command. Each ack effectively records the last consumed message. This can result in a lot of cross-region traffic. To reduce this traffic, we replicate XACK messages only when all of the read entries are acknowledged.\n\n| Time | Region 1                                                      | Region 2   | Explanation                                                                                                     |\n| ---- | --------------------------------------------------------------- | ------------ | --------------------------------------------------------------------------------------------------------------- |\n| _t1_   | `XADD x 110-0 f1 v1`                                              |              |                                                                                                                 |\n| _t2_   | `XADD x 120-0 f1 v1`                                              |              |                                                                                                                 |\n| _t3_   | `XADD x 130-0 f1 v1`                                              |              |                                                                                                                 |\n| _t4_   | `XGROUP CREATE x group1 0`                                            |              |                                                                                                                 |\n| _t5_   | `XREADGROUP GROUP group1 Alice STREAMS x >` <br/>**→ [110-0, 120-0, 130-0]** |              |                                                                                                                 |\n| _t6_   | `XACK x group1 110-0`                                                   |              |                                                                                                                 |\n| _t7_   | _— Sync —_                                                    | _— Sync —_ | 110-0 and its preceding entries (none) were acknowledged. We replicate an XACK effect for 110-0.                |\n| _t8_   | `XACK x group1 130-0`                                                   |              |                                                                                                                 |\n| _t9_   | _— Sync —_                                                    | _— Sync —_ | 130-0 was acknowledged, but not its preceding entries (120-0). We DO NOT replicate an XACK effect for 130-0     |\n| _t10_  | `XACK x group1 120-0`                                                   |              |                                                                                                                 |\n| _t11_  | _— Sync —_                                                    | _— Sync —_ | 120-0 and its preceding entries (110-0 through 130-0) were acknowledged. We replicate an XACK effect for 130-0. |\n\nIn this scenario, if we redirect the XREADGROUP traffic from Region 1 to Region 2 we do not re-read entries 110-0, 120-0 and 130-0.\nThis means that the XREADGROUP does not return already-acknowledged entries."},{"id":"guarantees","title":"Guarantees","role":"content","text":"Unlike XREAD, XREADGOUP will never skip stream entries.\n\nIn traffic redirection, XREADGROUP may return entries that have been read but not acknowledged. It may also even return entries that have already been acknowledged.\n\nWhen entries are added from multiple regions, Redis guarantees ordered entries in the reply of a single read command (`XREAD`, `XREADGROUP`, or `XRANGE`). However, ordered IDs are not guaranteed for a sequence of read commands.\nFor example, two subsequent `XREADGROUP GROUP group consumer COUNT 1 STREAMS key >` commands may reply with decreasing IDs."},{"id":"summary","title":"Summary","role":"content","text":"With Active-Active streams, you can write to the same logical stream from multiple regions. As a result, the behavior of Active-Active streams differs somewhat from the behavior you get with Redis Open Source. This is summarized below:"},{"id":"stream-commands","title":"Stream commands","role":"content","text":"1. When using the _strict_ ID generation mode, XADD does not permit full stream entry IDs (that is, an ID containing both MS and SEQ).\n1. XREAD may skip entries when iterating a stream that is concurrently written to from more than one region. For reliable stream iteration, use XREADGROUP instead.\n1. XSETID fails when the new ID is less than current ID."},{"id":"consumer-group-notes","title":"Consumer group notes","role":"content","text":"The following consumer group operations are replicated:\n\n1. Consecutive XACK operations\n1. Consumer group creation and deletion (that is, XGROUP CREATE and XGROUP DESTROY)\n\nAll other consumer group metadata is not replicated.\n\nA few other notes:\n\n1. XGROUP SETID and DELCONSUMER are not replicated.\n1. Consumers exist locally (XREADGROUP creates a consumer implicitly).\n1. Renaming a stream (using RENAME) deletes all consumer group information."}],"examples":[{"id":"id-generation-modes-ex0","language":"sh","code":"rladmin tune db crdb crdt_xadd_id_uniqueness_mode strict","section_id":"id-generation-modes"},{"id":"id-generation-modes-ex1","language":"sh","code":"rladmin tune db crdb crdt_xadd_id_uniqueness_mode semi-strict","section_id":"id-generation-modes"},{"id":"id-generation-modes-ex2","language":"sh","code":"rladmin tune db crdb crdt_xadd_id_uniqueness_mode liberal","section_id":"id-generation-modes"}]}
{"schema_version":2,"id":"operate/rc/databases/active-active/develop/data-types/strings","title":"Strings and bitfields in Active-Active databases","url":"https://redis.io/docs/latest/operate/rc/databases/active-active/develop/data-types/strings/","summary":"Information about using strings and bitfields with an Active-Active database.","tags":["docs","operate","rs","rc"],"last_updated":"2026-09-24T09:16:24+01:00","page_type":"content","content_hash":"71703cb17032839265ea8a5dbab9ce487b58a043d4e7a28ef124e6a6d42641a7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Active-Active databases support both strings and bitfields.\n\n> [!NOTE]\n> Active-Active **bitfield** support was added in RS version 6.0.20.\n\nChanges to both of these data structures will be replicated across Active-Active member databases."},{"id":"replication-semantics","title":"Replication semantics","role":"content","text":"Except in the case of [string counters](#string-counter-support) (see below), both strings and bitfields are replicated using a \"last write wins\" approach. The reason for this is that strings and bitfields are effectively binary objects. So, unlike with lists, sets, and hashes, the conflict resolution semantics of a given operation on a string or bitfield are undefined."},{"id":"how-last-write-wins-works","title":"How \"last write wins\" works","role":"content","text":"A wall-clock timestamp (OS time) is stored in the metadata of every string\nand bitfield operation. If the replication syncer cannot determine the order of operations,\nthe value with the latest timestamp wins. This is the only case with Active-Active databases where OS time is used to resolve a conflict.\n\nHere's an example where an update happening to the same key at a later\ntime (t2) wins over the update at t1.\n\n|  **Time** | **Region 1** | **Region 2** |\n|  :------: | :------: | :------: |\n|  t1 | SET text \"a\" |  |\n|  t2 |  | SET text \"b\" |\n|  t3 | — Sync — | — Sync — |\n|  t4 | SET text \"c\" |  |\n|  t5 | — Sync — | — Sync — |\n|  t6 |  | SET text \"d\" |"},{"id":"string-counter-support","title":"String counter support","role":"content","text":"When you're using a string as counter (for instance, with the [INCR](https://redis.io/docs/latest/commands/incr) or [INCRBY](https://redis.io/docs/latest/commands/incrby) commands),\nthen conflicts will be resolved semantically.\n\nOn conflicting writes, counters accumulate the total counter operations\nacross all member Active-Active databases in each sync.\n\nHere's an example of how counter\nvalues works when synced between two member Active-Active databases. With\neach sync, the counter value accumulates the private increment and\ndecrements of each site and maintain an accurate counter across\nconcurrent writes.\n\n|  **Time** | **Region 1** | **Region 2** |\n|  :------: | :------: | :------: |\n|  t1 | INCRBY counter 7 |  |\n|  t2 |  | INCRBY counter 3 |\n|  t3 | GET counter<br/>7 | GET counter<br/>3 |\n|  t4 | — Sync — | — Sync — |\n|  t5 | GET counter<br/>10 | GET counter<br/>10 |\n|  t6 | DECRBY counter 3 |  |\n|  t7 |  | INCRBY counter 6 |\n|  t8 | — Sync — | — Sync — |\n|  t9 | GET counter<br/>13 | GET counter<br/>13 |\n\n> [!NOTE]\n> Active-Active databases support 59-bit counters.\n> This limitation is to protect from overflowing a counter in a concurrent operation."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/databases/active-active/develop/develop-for-aa","title":"Develop applications with Active-Active databases","url":"https://redis.io/docs/latest/operate/rc/databases/active-active/develop/develop-for-aa/","summary":"Overview of how developing applications differs for Active-Active databases from standalone Redis databases.","tags":["docs","operate","rs","rc"],"last_updated":"2026-09-24T09:16:24+01:00","page_type":"content","content_hash":"ee9a8032a3c0e0bd833e5b8d9166be24069f6b077c63d36bb4f9137894ef8a2e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Developing geo-distributed, multi-master applications can be difficult.\nApplication developers may have to understand a large number of race\nconditions between updates to various sites, network, and cluster\nfailures that could reorder the events and change the outcome of the\nupdates performed across geo-distributed writes.\n\nActive-Active databases (formerly known as CRDB) are geo-distributed databases that span multiple Redis Software clusters.\nActive-Active databases depend on [multi-master replication](https://en.wikipedia.org/wiki/Multi-master_replication) and [conflict-free\nreplicated data types (CRDTs)](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type) to power a simple development experience\nfor geo-distributed applications. Active-Active databases allow developers to use existing\nRedis data types and commands, but automatically handle conflicting concurrent writes to the same key\nacross multiple geographies. For example, developers can simply use the\n`INCR` or `INCRBY` method in Redis in all instances of the geo-distributed\napplication, and Active-Active databases handle the additive nature of `INCR` to reflect the\ncorrect final value.\n\nThe following example displays a sequence of events\nover time: t1 to t9. This Active-Active database has two member Active-Active databases: member CRDB1 and\nmember CRDB2. The local operations running in each member Active-Active database are\nlisted under the member Active-Active database name. The sync events represent the moment\nwhen synchronization catches up to distribute all local member Active-Active database\nupdates to other participating clusters and other member Active-Active databases.\n\n|  **Time** | **Member CRDB1** | **Member CRDB2** |\n|  :------: | :------: | :------: |\n|  t1 | INCRBY key1 7 |  |\n|  t2 |  | INCRBY key1 3 |\n|  t3 | GET key1<br/>7 | GET key1<br/>3 |\n|  t4 | — Sync — | — Sync — |\n|  t5 | GET key1<br/>10 | GET key1<br/>10 |\n|  t6 | DECRBY key1 3 |  |\n|  t7 |  | INCRBY key1 6 |\n|  t8 | — Sync — | — Sync — |\n|  t9 | GET key1<br/>13 | GET key1<br/>13 |\n\nDatabases provide various approaches to address some of these concerns:\n\n- **Active-Passive geo-distributed deployments**: With active-passive\n    distributions, all writes go to an active cluster. Redis Sofware\n    provides a [Replica Of](https://redis.io/docs/latest/operate/rs/databases/import-export/replica-of) capability that provides a similar approach.\n    This can be employed when the workload is heavily balanced toward\n    reads and few writes. However, WAN performance and availability\n    can be unreliable, and traveling large distances for writes takes away\n    from application performance and availability.\n- **Two-phase commit (2PC)**: This approach is designed around a protocol\n    that commits a transaction across multiple transaction managers.\n    Two-phase commit provides a consistent transactional write across\n    regions but fails transactions unless all participating transaction\n    managers are available at the time of the transaction. The number\n    of messages exchanged and its cross-regional availability\n    requirement make two-phase commit unsuitable for even moderate\n    throughputs and cross-geo writes that go over WANs.\n- **Sync update with quorum-based writes**: This approach synchronously\n    coordinates a write across the majority of replicas across\n    clusters spanning multiple regions. However, just like two-phase\n    commit, the number of messages exchanged and its cross-regional\n    availability requirement make geo-distributed quorum writes\n    unsuitable for moderate throughputs and cross-geo writes that go\n    over WANs.\n- **Last-Writer-Wins (LWW) conflict resolution**: Some systems provide\n    simplistic conflict resolution for all types of writes where\n    system clocks are used to determine the winner across conflicting\n    writes. LWW is lightweight and can be suitable for simpler data.\n    However, LWW can be destructive to updates that are not necessarily\n    conflicting. For example, adding a new element to a set across two\n    geographies concurrently would result in only one of these new\n    elements appearing in the final result with LWW.\n- **MVCC (multi-version concurrency control)**: MVCC systems maintain\n    multiple versions of data and may expose ways for applications to\n    resolve conflicts. Even though an MVCC system can provide a flexible\n    way to resolve conflicting writes, it comes at the cost of great\n    complexity in the development of a solution.\n\nEven though types and commands in Active-Active databases look identical to standard Redis\ntypes and commands, the underlying types in Redis Software are enhanced to maintain\nmore metadata to create the conflict-free data type experience. This\nsection explains what you need to know about developing with Active-Active databases on\nRedis Software."},{"id":"lua-scripts","title":"Lua scripts","role":"content","text":"Active-Active databases support Lua scripts, but unlike standard Redis, Lua scripts always\nexecute in effects replication mode. There is currently no way to\nexecute them in script-replication mode."},{"id":"eviction","title":"Eviction","role":"content","text":"The default policy for Active-Active databases is _noeviction_ mode. Redis Software version 6.0.20 and later support all eviction policies for Active-Active databases, unless [Redis Flex or Auto Tiering](https://redis.io/docs/latest/operate/rs/databases/flash) (previously known as Redis on Flash) is enabled.\n\nFor details, see [eviction for Active-Active databases (Redis Software)](https://redis.io/docs/latest/operate/rs/databases/memory-performance/eviction-policy#active-active-database-eviction) or [eviction for Active-Active databases (Redis Cloud)](https://redis.io/docs/latest/operate/rc/databases/configuration/data-eviction-policies#active-active-replication-considerations)."},{"id":"expiration","title":"Expiration","role":"content","text":"Expiration is supported with special multi-master semantics.\n\nIf a key's expiration time is changed at the same time on different\nmembers of the Active-Active database, the longer extended time set via TTL on a key is\npreserved.\n\nIf this command was performed on key1 on cluster #1:\n\n[code example]\n\nIf this command was performed on key1 on cluster #2:\n\n[code example]\n\nThe `EXPIRE` command setting the key to 50 would win.\n\nAnd if this command was performed on key1 on cluster #3:\n\n[code example]\n\nIt would win out of the three clusters hosting the Active-Active database as it sets the\nTTL on key1 to an infinite time.\n\nThe replica responsible for the winning expire value is also\nresponsible for expiring the key and propagating a DEL effect when this\nhappens. From this point on, a losing replica is not responsible\nfor expiring the key, unless another `EXPIRE` command resets the TTL.\nFurthermore, a replica that is not the owner of the expired value:\n\n- Silently ignores the key if a user attempts to access it in READ\n    mode, for example, treating it as if it was expired but not propagating a\n    DEL.\n- Expires it (sending a DEL) before making any modifications if a user\n    attempts to access it in WRITE mode.\n    \n    > [!NOTE]\n    > Expiration values are in the range of [0,&nbsp;2^49] for Active-Active databases and [0,&nbsp;2^64] for regular databases.\n    >"},{"id":"tombstones","title":"Tombstones","role":"content","text":"For conflict resolution purposes, Active-Active databases cannot immediately release a deleted key. Instead, the key is logically deleted but remains in memory as a tombstone until the garbage collector can safely remove it.\n\nWhen a deleted key becomes a tombstone, it frees some memory previously consumed by the key. The size of each tombstone varies depending on the data type and the key's history.\n\nThe garbage collector automatically removes a tombstone when all instances in the Active-Active database have observed the deletion operation.\n\nTo monitor tombstones, you can use shard-level metrics exposed by [`INFO crdt`](#info) and Grafana."},{"id":"outofmemory-oom","title":"Out-of-Memory (OOM)","role":"content","text":"If a member Active-Active database is out of memory, that member is marked as\n\"inconsistent\", the member stops responding to user traffic, and\nthe syncer initiates full reconciliation with other peers in the Active-Active database."},{"id":"active-active-database-key-counts","title":"Active-Active database key counts","role":"content","text":"Keys are counted differently for Active-Active databases:\n\n- DBSIZE (in `shard-cli dbsize`) reports key header instances\n    that represent multiple potential values of a key before a replication conflict is resolved.\n- expired_keys (in `bdb-cli info`) can be more than the keys count in DBSIZE (in `shard-cli dbsize`) \n    because expires are not always removed when a key becomes a tombstone.\n- The Expires average TTL (in `bdb-cli info`) is computed for local expires only."},{"id":"info","title":"INFO","role":"content","text":"The `INFO` command has an additional CRDT section that provides advanced troubleshooting information:\n\n|  **Section** | **Field** | **Description** |\n|  ------ | ------ | ------ |\n|  **CRDT Context** | crdt_config_version | Current Active-Active database configuration version. |\n|   | crdt_slots | Hash slots assigned and reported by this shard. |\n|   | crdt_replid | Unique replica/shard IDs. |\n|   | crdt_clock | Clock value of local vector clock. |\n|   | crdt_ovc | Locally observed Active-Active database vector clock. |\n|  **Peers** | | A list of currently connected peer replication peers. This is similar to the replicas list reported by Redis. |\n|  **Backlogs** | | A list of peer replication backlogs currently maintained. Typically in a full mesh topology, only a single backlog is used for all peers, as the requested IDs are identical. |\n|  **CRDT Stats** | crdt_sync_full | Number of inbound full synchronization processes performed. |\n|   | crdt_sync_partial_ok | Number of partial (backlog based) re-synchronization processes performed. |\n|   | crdt_sync_partial-err | Number of partial re-synchronization processes failed due to exhausted backlog. |\n|   | crdt_merge_reqs | Number of inbound merge requests processed. |\n|   | crdt_effect_reqs | Number of inbound effect requests processed. |\n|   | crdt_ovc_filtered_effect_reqs | Number of inbound effect requests filtered due to old vector clock. |\n|   | crdt_gc_pending | Number of elements pending garbage collection. |\n|   | crdt_gc_attempted | Number of attempts to garbage collect tombstones. |\n|   | crdt_gc_collected | Number of tombstones garbage collected successfully. |\n|   | crdt_gc_gvc_min | The minimal globally observed vector clock, as computed locally from all received observed clocks. |\n|   | crdt_stale_released_with_merge | Indicates the last stale flag transition was a result of a complete full sync. |\n|  **CRDT Replicas** | | A list of crdt_replica \\<uid> entries, each describes the known state of a remote instance with the following fields: |\n|   | config_version | Last configuration version reported. |\n|   | shards | Number of shards. |\n|   | slots | Total number of hash slots. |\n|   | slot_coverage | A flag indicating remote shards provide full coverage (all shards are alive). |\n|   | max_ops_lag | Number of local operations not yet observed by the least updated remote shard. |\n|   | min_ops_lag | Number of local operations not yet observed by the most updated remote shard. |"}],"examples":[{"id":"expiration-ex0","language":"sh","code":"127.0.0.1:6379> EXPIRE key1 10","section_id":"expiration"},{"id":"expiration-ex1","language":"sh","code":"127.0.0.1:6379> EXPIRE key1 50","section_id":"expiration"},{"id":"expiration-ex2","language":"sh","code":"127.0.0.1:6379> PERSIST key1","section_id":"expiration"}]}
{"schema_version":2,"id":"operate/rc/databases/active-active/develop","title":"Active-Active Redis applications","url":"https://redis.io/docs/latest/operate/rc/databases/active-active/develop/","summary":"General information to keep in mind while developing applications for an Active-Active database.","tags":["docs","operate","rc"],"last_updated":"2026-10-01T15:21:38+01:00","children":[{"id":"operate/rc/databases/active-active/develop/develop-for-aa","summary":"Overview of how developing applications differs for Active-Active databases from standalone Redis databases.","title":"Develop applications with Active-Active databases","url":"https://redis.io/docs/latest/operate/rc/databases/active-active/develop/develop-for-aa/"},{"id":"operate/rc/databases/active-active/develop/data-types","summary":"Introduction to differences in data types between standalone and Active-Active Redis databases.","title":"Data types for Active-Active databases","url":"https://redis.io/docs/latest/operate/rc/databases/active-active/develop/data-types/"},{"id":"operate/rc/databases/active-active/develop/app-failover-active-active","summary":"How to failover your application to connect to a remote replica.","title":"Application failover with Active-Active databases","url":"https://redis.io/docs/latest/operate/rc/databases/active-active/develop/app-failover-active-active/"}],"page_type":"content","content_hash":"52f9204dcbfc6bfcb56c9f88c13eb26a42e348577bd014a58aef0719096471ea","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Developing globally distributed applications can be challenging, as\ndevelopers have to think about race conditions and complex combinations\nof events under geo-failovers and cross-region write conflicts. In Redis Cloud, Active-Active databases\nsimplify developing such applications by directly using built-in smarts\nfor handling conflicting writes based on the data type in use. Instead\nof depending on just simplistic \"last-writer-wins\" type conflict\nresolution, geo-distributed Active-Active databases combine techniques defined in CRDT\n(conflict-free replicated data types) research with Redis types to\nprovide smart and automatic conflict resolution based on the data type's\nintent.\n\nAn Active-Active database is a globally distributed database that spans multiple cloud provider regions. Each Active-Active database can have many Active-Active database instances\nthat come with added smarts for handling globally distributed writes\nusing the proven\n[CRDT](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type)\napproach.\n[CRDT](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type)\nresearch describes a set of techniques for creating systems that can\nhandle conflicting writes. CRDBs powered by Multi-Master Replication\n(MMR) provide a straightforward and effective way to replicate your\ndata between regions and simplify development of complex applications\nthat can maintain correctness under geo-failovers and concurrent\ncross-region writes to the same data.\n\n![Geo-replication world map](https://redis.io/docs/latest/images/rs/crdbs.png)\n\nActive-Active databases replicate data between multiple Redis Cloud provider regions. Common uses for Active-Active databases include disaster recovery,\ngeographically redundant applications, and keeping data closer to your\nusers' locations. MMR is always multi-directional amongst the regions\nconfigured in the Active-Active database. For unidirectional replication, see [Active-Passive replication](https://redis.io/docs/latest/operate/rc/databases/migrate-databases#sync-using-active-passive)."},{"id":"example-of-synchronization","title":"Example of synchronization","role":"example","text":"In the example below, database writes are concurrent at the points in\ntime t1 and t2 and happen before a sync can communicate the changes.\nHowever, writes at times t4 and t6 are not concurrent as a sync happened\nin between.\n\n|  **Time** | **CRDB Instance1** | **CRDB Instance2** |\n|  ------: | :------: | :------: |\n|  t1 | SET key1 \"a\" |  |\n|  t2 |  | SET key1 \"b\" |\n|  t3 | — Sync — | — Sync — |\n|  t4 | SET key1 \"c\" |  |\n|  t5 | — Sync — | — Sync — |\n|  t6 |  | SET key1 \"d\" |\n\n[Learn more about\nsynchronization for\neach supported data type](/operate/rc/databases/active-active/develop/data-types/) and [how to develop applications](/operate/rc/databases/active-active/develop/develop-for-aa) with them on Redis Cloud."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/databases/active-active","title":"Active-Active Redis","url":"https://redis.io/docs/latest/operate/rc/databases/active-active/","summary":"Overview of the Active-Active feature for Redis Cloud.","aliases":["/operate/rc/databases/configuration/active-active-redis/"],"tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:09+01:00","children":[{"id":"operate/rc/databases/active-active/create-active-active-database","summary":"Shows how to create an Active-Active database","title":"Create an Active-Active database","url":"https://redis.io/docs/latest/operate/rc/databases/active-active/create-active-active-database/"},{"id":"operate/rc/databases/active-active/manage-regions","summary":"Learn how to add and edit regions for an Active-Active deployment.","title":"Manage regions in Active-Active databases","url":"https://redis.io/docs/latest/operate/rc/databases/active-active/manage-regions/"},{"id":"operate/rc/databases/active-active/develop","summary":"General information to keep in mind while developing applications for an Active-Active database.","title":"Active-Active Redis applications","url":"https://redis.io/docs/latest/operate/rc/databases/active-active/develop/"}],"page_type":"content","content_hash":"e65ac8bf3bd17efd194cfe930f66e3617dc740a2781ca29018366ce8b27837a5","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Active-Active databases store data across multiple regions and availability zones.  This improves scalability, performance, and availability, especially when compared to standalone databases.\n\nTo create Active-Active databases, you need a Redis Cloud Pro subscription that enables Active-Active Redis and defines the regions for each copy of your databases. See [Create an Active-Active database](https://redis.io/docs/latest/operate/rc/databases/active-active/create-active-active-database) for instructions.\n\nActive-Active databases are distributed across multiple regions (geo-distribution).  This improves performance by reducing latency for nearby users and improves availability by protecting against data loss in case of network or resource failure.\n\nActive-Active databases allow read and write operations in each copy.  Each copy eventually reflects changes made in other copies ([eventual consistency](https://redis.io/docs/latest/glossary#eventual-consistency)).  Conflict-free replicated data types (CRDTs) synchronize read and write operations between copies.  CRDTs ensure consistency and resolve conflicts.\n\nActive-Active databases use TLS to synchronize data between regions.  You can also use TLS to encrypt client connections.  See [Transport Layer Security (TLS)](https://redis.io/docs/latest/operate/rc/security/database-security/tls-ssl) for more information.\n\nWhen developing for Active-Active databases, you need to consider some important differences. See [Develop applications with Active-Active databases](https://redis.io/docs/latest/operate/rc/databases/active-active/develop) for related information.\n\n> [!NOTE]\n> Active-Active subscriptions on Redis Cloud are limited to a maximum of 10 regions and 10 databases."},{"id":"active-active-geo-distributed-replication-highlights","title":"Active-Active geo-distributed replication highlights","role":"content","text":""},{"id":"multi-zone","title":"Multi-zone","role":"content","text":"Geo-distributed replication maintains copies of both primary and replica shards in multiple clusters. These clusters can be spread across multiple availability zones. Active-Active Redis uses zone awareness to spread your primary and replica shards across zones, which helps protect against data loss from regional outages.\n\nThe availability of each region depends on the number of availability zones it provides:\n\n- Regions with three or more availability zones use multi-zone replication and provide **99.999%** (five-nines) availability.\n- Regions with fewer than three availability zones use single-zone replication and provide **99.99%** (four-nines) availability.\n\nAn Active-Active database can combine regions with different availability zone counts. When it does, the availability of the entire deployment is determined by the least resilient region. For example, an Active-Active database that spans a region with three availability zones and a region with fewer than three availability zones provides 99.99% (four-nines) availability overall.\n\n> [!NOTE]\n> Regions with fewer than three availability zones are not available when you create an Active-Active database in the Redis Cloud console. To create an Active-Active database that includes one of these regions, use the [Redis Cloud REST API](https://redis.io/docs/latest/operate/rc/api). You'll be able to manage these databases using the Redis Cloud console after they're created."},{"id":"local-latency-with-unique-endpoints","title":"Local latency with unique endpoints","role":"content","text":"Applications can connect to a specific copy of an Active-Active database using its unique endpoint. For local latency, configure your application to use a database endpoint in the closest region."},{"id":"conflict-resolution","title":"Conflict resolution","role":"content","text":"Active-Active databases use special data types called conflict-free replicated data types (CRDT). These automatically resolve conflicts that occur when writes are made to different clusters at the same time."},{"id":"failover-handling","title":"Failover handling","role":"content","text":"After a failure at the process, node, or zone level, Active-Active databases automatically promote replica shards to replace failed primaries, copy data to new replica shards, and migrate shards to new nodes as needed. This reduces downtime and makes the most of your computing resources, even in the event of a failure.  \n\nHowever, Active-Active databases do not have a built-in [failover](https://en.wikipedia.org/wiki/Failover) or failback mechanism for application connections. To handle cluster-level failures, you should implement one of the following disaster recovery strategies to redirect traffic between regions:\n\n- [Network-based](https://redis.io/docs/latest/operate/rs/databases/active-active/disaster-recovery/network-based): Global traffic managers and load balancers for routing.\n\n- [Proxy-based](https://redis.io/docs/latest/operate/rs/databases/active-active/disaster-recovery/proxy-based): Software proxies handle detection and routing logic.\n\n- [Client library-based](https://redis.io/docs/latest/operate/rs/databases/active-active/disaster-recovery/client-library-based): Database client libraries with built-in failover logic.\n\n- [Application-based](/operate/rc/databases/active-active/develop/app-failover-active-active): Custom application-level monitoring and connectivity management.\n\nFor more information and guidance on which disaster recovery strategy to implement, see [Disaster recovery strategies for Active-Active databases](https://redis.io/docs/latest/operate/rs/databases/active-active/disaster-recovery).\n\nData automatically syncs to a recovered cluster when it returns to a healthy state."},{"id":"sizing-and-memory","title":"Sizing and memory","role":"content","text":"Active-Active databases consume more memory than standalone databases. Because Active-Active requires replication, and Active-Active replication doubles memory consumption on top of that, the memory limit impact can be as large as four times (4x) the original data size.\n\nActive-Active databases also begin evicting keys earlier than standalone databases, at 80% of an instance's memory limit, and reserve additional memory for replication backlogs. Account for these factors when you size your database.\n\nFor more information, see [Memory limits and sizing](https://redis.io/docs/latest/operate/rc/databases/configuration/sizing#dataset-size)."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/databases/active-active/manage-regions","title":"Manage regions in Active-Active databases","url":"https://redis.io/docs/latest/operate/rc/databases/active-active/manage-regions/","summary":"Learn how to add and edit regions for an Active-Active deployment.","tags":["docs","operate","rc"],"last_updated":"2026-10-01T15:21:38+01:00","page_type":"content","content_hash":"20f39a03f198780d1d4def1c64856ca52d075f1468b41e15d367144612403f93","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can add and edit regions for an Active-Active deployment after it has been created by going to the **Regions** tab of the [subscription page](https://redis.io/docs/latest/operate/rc/subscriptions/view-pro-subscription). \n\n![The Regions tab lets you manage the regions in your Active-Active subscription.](https://redis.io/docs/latest/images/rc/subscription-details-regions-tab.png)"},{"id":"add-a-new-region","title":"Add a new region","role":"content","text":"To add a new region to an Active-Active deployment:\n\n1. In the **Regions** tab of the subscription page, select **Add region**.\n\n    ![Select Add region to add a new region to your Active-Active subscription.](https://redis.io/docs/latest/images/rc/subscription-details-regions-tab-add.png)\n\n1. In the **Region setup** step:\n\n    ![The Region setup step.](https://redis.io/docs/latest/images/rc/subscription-add-region-setup.png)\n\n    1. Select the desired region from the **Region** list.\n\n    1. If you chose to deploy your Active-Active database to an existing [Bring your own Cloud](https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud) account, you can define the VPC ID for the new region. Select **In existing VPC** and the set the VPC ID for the region.\n\n    1. Enter a unique CIDR address for the region.  \n    \n        Every CIDR should be unique to properly route network traffic between each Active-Active database instance and your consumer VPCs. The CIDR block regions should _not_ overlap between the Redis server and your app consumer VPCs. In addition, CIDR blocks should not overlap between cluster instances. \n\nWhen all **Deployment CIDR** regions display a green checkmark, you're ready to continue.  \n\n![Green checkmarks indicate valid CIDR address values.](https://redis.io/docs/latest/images/rc/icon-cidr-address-ok.png)\n\nRed exclamation marks indicate error conditions; the tooltip provides additional details.\n\n![Red exclamation points indicate CIDR address problems.](https://redis.io/docs/latest/images/rc/icon-cidr-address-error.png)\n\n    Select **Continue**.\n\n1. In the **Throughput** step, enter the desired read and write throughput for the new region.\n\n    You can also select **Set throughput as existing region** to duplicate the throughput settings from an existing region.\n\n    ![The Throughput step.](https://redis.io/docs/latest/images/rc/subscription-add-region-throughput.png)\n\n1. Review the required resources and updated price for the database in the **Required resources** step. \n\n    ![The Required resources step.](https://redis.io/docs/latest/images/rc/subscription-add-region-required-resources.png)\n\n1. Select **Continue** to add the region to your Active-Active deployment.\n\nRedis Cloud will provision the new region in the background and add it to all databases in the subscription. You aren't allowed to make changes to the database settings while this process is running. \n\nYour database will still be available, but you may notice some increased latency. This process will take some time depending on the size of your database."},{"id":"edit-regions","title":"Edit regions","role":"content","text":"To edit the local throughput settings for a database instance or remove a region from the deployment, select **Edit regions** in the **Regions** tab of the subscription page.\n\n![Select Edit regions to update regions for your Active-Active subscription.](https://redis.io/docs/latest/images/rc/subscription-details-regions-tab-edit.png)\n\nFrom here, you can:\n\n- [Edit local throughput](#edit-local-throughput) for a database instance\n- [Remove a region](#remove-region) from the subscription\n\nAfter making the desired changes, [save and confirm the changes](#save-and-confirm-changes)."},{"id":"edit-local-throughput","title":"Edit local throughput","role":"content","text":"To edit the local throughput settings for a database instance:\n\n1. Select the region you want to edit. \n\n1. Add or reduce the local read and write throughput for each database in that region in the **Local reads ops/sec** and **Local writes ops/sec** fields.\n\n    ![The Edit regions page, with throughput settings changed.](https://redis.io/docs/latest/images/rc/subscription-edit-region-throughput.png)"},{"id":"remove-region","title":"Remove region","role":"content","text":"To remove a region from an Active-Active deployment, select the **Delete** button for the region you want to remove.\n\n![Select the Delete button to remove a region from your Active-Active subscription.](https://redis.io/docs/latest/images/rc/subscription-edit-region-delete.png)"},{"id":"save-and-confirm-changes","title":"Save and confirm changes","role":"content","text":"After making your changes, select **Save changes** to apply them. You'll need to review and confirm the changes and any updated costs before they are applied. Select **Confirm** to apply the changes. \n\nRedis Cloud will make the desired changes in the background. You aren't allowed to make changes to the database settings while this process is running. \n\nYour databases will still be available, but you may notice some increased latency. This process will take some time depending on the size of your database."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/databases/back-up-data","title":"Back up and export a database","url":"https://redis.io/docs/latest/operate/rc/databases/back-up-data/","summary":"","aliases":["/operate/rc/administration/configuration/backups"],"tags":["docs","operate","rc"],"last_updated":"2026-10-01T15:21:31+01:00","page_type":"content","content_hash":"1d72251a09415171d168c4aa5ea9c75a55d4c6dc6a79a025be52fb62cdc9ea82","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The backup options for Redis Cloud databases depend on your plan:\n\n- Redis Cloud Pro subscriptions can back up or export a database on-demand and schedule daily backups that occur during a set hour.\n\n- Paid Redis Cloud Essentials plans can back up or export a database on-demand and schedule backups that occur every 24 hours.  \n\n- Free plans cannot back up or export a database through the Redis Cloud console.\n\n> [!NOTE]\n> The number of database backups that can run simultaneously on a subscription is limited to 4 by default.\n\nBackups are saved to predefined storage locations available to your subscription. Backup locations need to be available before you turn on database backups.  To learn more, see [Set up backup storage locations](#set-up-backup-storage-locations).\n\nBackups are saved in RDB format. If the database is comprised of multiple shards, an RDB file will be created for each shard of the database. For more information on restoring data from a backup, see [Restore from an RDB file](https://redis.io/docs/latest/operate/rc/databases/import-data#restore-from-an-rdb-file).\n\nHere, you'll learn how to store backups using different cloud providers."},{"id":"turn-on-backups","title":"Turn on backups","role":"content","text":"To turn on database backups:\n\n1. Sign in to the [Redis Cloud console](https://cloud.redis.io/). \n\n1.  Select the database to open the **Database** page and then select **Edit**.\n\n    ![The Edit button lets you change database settings.](https://redis.io/docs/latest/images/rc/button-database-edit.png)\n\n1.  In the **Configuration** tab, locate the **Remote backup** setting: in the **Performance & availability > Remote backup** section for Essentials databases, or the **Durability** section for Pro databases.\n\nWhen you enable **Remote backup**, additional options appear.  The options vary according to your subscription.\n\n![Backup settings appear when you enable the Remote backup settings.](https://redis.io/docs/latest/images/rc/database-details-configuration-durability-backup.png)\n\n|Setting name|Description|\n|:-----------|:----------|\n| **Interval** | Defines the frequency of automatic backups.  Paid Redis Cloud Essentials databases are backed up every 24 hours.  Redis Cloud Pro databases can be set to 24, 12, 6, 4, 2, or 1 hour backup intervals. |\n| **Set backup time** | When checked, this lets you set the hour of the **Backup time**. (_Redis Cloud Pro only_) |\n| **Backup time** | Defines the hour automatic backups are made.  Note that actual backup times will vary up in order to minimize customer access disruptions.  (_Redis Cloud Pro only_)<br/> Times are expressed in [Coordinated Universal Time](https://en.wikipedia.org/wiki/Coordinated_Universal_Time) (UTC).|\n| **Storage type** | Defines the provider of the storage location, which can be: `AWS S3`, `Google Cloud Storage`, `Azure Blob Storage`, `FTP` (FTPS), or `Redis-managed repo`. `Redis-managed repo` is only available for databases created through the [Heroku](https://redis.io/docs/latest/operate/rc/cloud-integrations/heroku) or [Vercel](https://redis.io/docs/latest/operate/rc/cloud-integrations/vercel) integrations. |\n| **Backup destination** | Defines a URI representing the backup storage location. |"},{"id":"back-up-and-export-data-on-demand","title":"Back up and export data on demand","role":"content","text":"After backups are turned on, you can back up your data at any time.  Use the **Backup now** button, located in the **Performance & availability > Remote backup** section for Essentials databases, or the **Durability** section for Pro databases.\n\n![Use the Backup Now button to make backups on demand.](https://redis.io/docs/latest/images/rc/button-database-backup-now.png)\n\nYou can only use the **Backup now** button after you turn on backups."},{"id":"set-up-backup-storage-locations","title":"Set up backup storage locations","role":"content","text":"Database backups can be stored to a cloud provider service or saved to a URI using FTP/FTPS.\n\nYour subscription needs the ability to view permissions and update objects in the storage location.  Specific details vary according to the provider.  To learn more, consult the provider's documentation.\n\nBe aware that provider features change frequently.  For best results, use your provider's documentation for the latest info.\n\nSelect the tab for your storage location type.\n\n**AWS S3:**\n\nTo store backups in an Amazon Web Services (AWS) Simple Storage Service (S3) [bucket](https://docs.aws.amazon.com/AmazonS3/latest/userguide/creating-buckets-s3.html):\n\n1. In the [AWS Management Console](https://console.aws.amazon.com/), use the **Services** menu to locate and select **Storage** > **S3**.  This takes you to the Amazon S3 admin panel.\n\n1.  Use the **Buckets** list to locate and select your bucket.  When the settings appear, select the **Permissions** tab, locate the **Bucket policy** section, and then click **Edit**.\n\n    -  If there is no existing bucket policy, add the following JSON bucket policy. Replace `UNIQUE-BUCKET-NAME` with the name of your bucket.\n\n        [code example]\n\n    - If a bucket policy already exists, add the following JSON policy statement to the list of statements. Replace `UNIQUE-BUCKET-NAME` with the name of your bucket.\n\n        [code example]\n\n\n1. Save your changes.\n\n1. If the bucket is encrypted using [SSE-KMS](https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingKMSEncryption.html), add the following statement to your [key policy](https://docs.aws.amazon.com/kms/latest/developerguide/key-policy-modifying.html). If you do not have a key policy, see [Creating a key policy](https://docs.aws.amazon.com/kms/latest/developerguide/key-policy-overview.html). Replace `UNIQUE-BUCKET-NAME` with the name of your bucket and `CUSTOM-KEY-ARN` with your key's [Amazon Resource Name (ARN)](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference-arns.html).\n\n    [code example]\n\nOnce the bucket is available and the permissions are set, use the name of your bucket as the **Backup destination** for your database's Remote backup settings. For example, suppose your bucket is named *backups-bucket*.  In that case, set **Backup destination** to `s3://backups-bucket`.\n\nTo learn more, see [Using bucket policies](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucket-policies.html) on the AWS docs.\n\n> [!NOTE]\n> An AWS S3 bucket can be used by only one Redis Cloud account. If you have more than one Redis Cloud account, repeat the setup steps for multiple buckets.\n\n**Google Cloud Storage:**\n\nTo store backups in a Google Cloud Storage [bucket](https://cloud.google.com/storage/docs/creating-buckets):\n\n1. Sign in to the Google Cloud console.\n\n1. In the console menu, locate the _Storage_ section, then select **Cloud Storage&nbsp;>&nbsp;Buckets**.\n\n1. Create or select a bucket.\n\n1. Select the **Permissions** tab.\n\n1. Select the **Grant Access** button and then add:\n\n    [code example]\n\n1. Set **Role** to **Storage Legacy Bucket Writer**.\n\n1. Save your changes.\n\nUse the bucket details **Configuration** tab to locate the **gsutil URI**.  This is the value you'll assign to your resource's backup path.\n\nTo learn more, see [Use IAM permissions](https://cloud.google.com/storage/docs/access-control/using-iam-permissions#bucket-iam).\n\n**Azure Blob Storage:**\n\nTo store your backup in Microsoft Azure Blob Storage, sign in to the Azure portal and then:\n\n1. [Create an Azure Storage account](https://docs.microsoft.com/en-us/azure/storage/common/storage-account-create) if you do not already have one.\n\n1. [Create a container](https://docs.microsoft.com/en-us/azure/storage/blobs/storage-quickstart-blobs-portal#create-a-container) if you do not already have one.\n\n1. Find your storage account's primary access key. See [Manage storage account access keys](https://docs.microsoft.com/en-us/azure/storage/common/storage-account-keys-manage) for instructions.\n\nSet your resource's **Backup Path** to the path of your storage account.\n\nThe syntax for creating the backup varies according to your authorization mechanism.  For example:\n\n[code example]\n\nWhere:\n\n- *storage_account_access_key:* the primary access key to the\n    storage account\n- *storage_account_name:* the storage account name\n- *container_name:* the name of the container, if needed.\n- *path*: the backups path, if needed.\n\nTo learn more, see [Authorizing access to data in Azure Storage](https://docs.microsoft.com/en-us/azure/storage/common/storage-auth).\n\n**FTP/FTPS Server:**\n\nTo store your backups on an FTP server, set its **Backup Path** using the following syntax:\n\n[code example]\n\n\nWhere:\n\n- *protocol*: the server's protocol, can be either `ftp` or `ftps`.\n- *username*: your username, if needed.\n- *password*: your password, if needed.\n- *hostname*: the hostname or IP address of the server.\n- *port*: the port number of the server, if needed.\n- *path*: the backup path, if needed.\n\n    > [!NOTE]\n    > If your FTP username or password contains special characters such as `@`, `\\`, or `:`, you must URL encode (also known as Percent encode) these special characters. If you don't, your database may become stuck.\n    >\n\nThe user account needs permission to write files to the server."}],"examples":[{"id":"set-up-backup-storage-locations-ex0","language":"json","code":"{\n            \"Version\": \"2012-10-17\",\n            \"Id\": \"MyBucketPolicy\",\n            \"Statement\": [\n                {\n                    \"Sid\": \"RedisCloudBackupsAccess\",\n                    \"Effect\": \"Allow\",\n                    \"Principal\": {\n                        \"AWS\": \"arn:aws:iam::168085023892:root\"\n                    },\n                    \"Action\": [\n                        \"s3:PutObject\",\n                        \"s3:getObject\",\n                        \"s3:DeleteObject\"\n                    ],\n                    \"Resource\": \"arn:aws:s3:::UNIQUE-BUCKET-NAME/*\"\n                }\n            ]\n        }","section_id":"set-up-backup-storage-locations"},{"id":"set-up-backup-storage-locations-ex1","language":"json","code":"{\n            \"Sid\": \"RedisCloudBackupsAccess\",\n            \"Effect\": \"Allow\",\n            \"Principal\": {\n                \"AWS\": \"arn:aws:iam::168085023892:root\"\n            },\n            \"Action\": [\n                \"s3:PutObject\",\n                \"s3:getObject\",\n                \"s3:DeleteObject\"\n            ],\n            \"Resource\": \"arn:aws:s3:::UNIQUE-BUCKET-NAME/*\"\n        }","section_id":"set-up-backup-storage-locations"},{"id":"set-up-backup-storage-locations-ex2","language":"json","code":"{\n        \"Sid\": \"Allow use of the key\",\n        \"Effect\": \"Allow\",\n        \"Principal\": {\n            \"AWS\": \"arn:aws:iam::168085023892:root\"\n        },\n        \"Action\": [\n            \"kms:Encrypt\",\n            \"kms:Decrypt\",\n            \"kms:ReEncrypt*\",\n            \"kms:GenerateDataKey*\",\n            \"kms:DescribeKey\"\n        ],\n        \"Resource\": [\n            \"arn:aws:s3:::UNIQUE-BUCKET-NAME/*\",\n            \"CUSTOM-KEY-ARN\"\n        ]\n    }","section_id":"set-up-backup-storage-locations"},{"id":"set-up-backup-storage-locations-ex3","language":"sh","code":"service@redislabs-prod-clusters.iam.gserviceaccount.com","section_id":"set-up-backup-storage-locations"},{"id":"set-up-backup-storage-locations-ex4","language":"plaintext","code":"abs://storage_account_access_key@storage_account_name/container_name/[path/]","section_id":"set-up-backup-storage-locations"},{"id":"set-up-backup-storage-locations-ex5","language":"sh","code":"<protocol>://[username]:[password]@[hostname]:[port]/[path]/","section_id":"set-up-backup-storage-locations"}]}
{"schema_version":2,"id":"operate/rc/databases/configuration/advanced-capabilities","title":"Advanced capabilities","url":"https://redis.io/docs/latest/operate/rc/databases/configuration/advanced-capabilities/","summary":"Describes Redis Cloud Advanced capability options.","tags":["docs","operate","rc"],"last_updated":"2026-10-01T15:21:38+01:00","page_type":"content","content_hash":"18086f5f04f36524fa2d48b54f7738fee19cc98e88b4899f322e8279ce85a5c2","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Advanced capabilities extend Redis database functionality by adding new features and data types.  \n\nAvailable options depend on your database plan and **Type**."},{"id":"essentials","title":"Redis Cloud Essentials","role":"content","text":"All Redis Cloud Essentials databases with versions prior to Redis 8.0 support [Redis Stack](https://redis.io/docs/latest/operate/oss_and_stack), which enables the most frequently used capabilities.\n\n![For Essentials, the Type setting in the General section includes an option for Redis.](https://redis.io/docs/latest/images/rc/new-database-general-type-free-stack.png)\n\nWhen the database **Type** is set to _Redis_, the Advanced capabilities section of the database details page displays the advanced capabilities included with the database and their versions.\n\n![For Essentials, the Database details page lists the capabilities and versions added by Redis Stack.](https://redis.io/docs/latest/images/rc/database-details-modules-stack-free.png)\n\nStarting with Redis 8.0, all advanced capabilities provided by Redis Stack are included in Redis by default.\n\nRedis Cloud is updated on a regular basis, which includes the advanced capabilities supported by the service. Versions displayed by the Redis Cloud console may vary from those shown above.  For the latest details of any capability, see [Redis Stack and Redis Software](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise). For more information about Redis Cloud version management, see [Redis version management](https://redis.io/docs/latest/operate/rc/databases/version-management)."},{"id":"pro","title":"Redis Cloud Pro","role":"content","text":"By default, Redis Cloud Pro databases load all supported advanced capabilities. For versions prior to Redis 8.0, you can choose to load specific capabilities when you create your database. To choose which capabilities to load for your Pro database, [create it with custom settings](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new) and select **More options** when adding your database in the **Sizing tab** to view advanced capability settings.\n\n![For Pro databases, you can select the capabilities included in your database.](https://redis.io/docs/latest/images/rc/database-details-redis-module-select-flexible.png)\n\nYou can select more than one advanced capability for a database, though there are limits:\n\n- The following advanced capabilities can be combined in Pro databases:\n\n    - Search and query\n    - JSON\n    - Time series\n    - Probabilistic\n\n- [Active-Active databases](https://redis.io/docs/latest/operate/rc/databases/active-active/create-active-active-database) only support JSON and Search and query.\n\nTo remove a selected capability, clear its checkbox.\n\nStarting with Redis 8.0, all advanced capabilities provided by Redis Stack are included in Redis by default. \n\nTo learn more, see [Redis Stack](https://redis.io/docs/latest/develop/get-started) and [Redis Stack and Redis Software](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise). For more information about Redis Cloud version management, see [Redis version management](https://redis.io/docs/latest/operate/rc/databases/version-management)."},{"id":"search-and-query-sizing","title":"Search and query sizing","role":"content","text":"When you create a Pro database with [Search and Query](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search), you must consider the sizing and throughput requirements for search and query when you define the dataset size and throughput for your database. \n\nUse the [Search and query sizing calculator](https://redis.io/redisearch-sizing-calculator/) to estimate your index size and throughput requirements.\n\n- **Dataset Size**: Add the estimated index size from the Sizing calculator to your expected dataset size.\n- **Throughput**: Enter the total estimated throughput from the Sizing calculator when you enter your throughput.\n- **Query performance factor**: The query performance factor adds extra power specifically for search and query. Select a factor to increase your queries per second by that amount.\n\n#### Query performance factor\n\nThe query performance factor adds extra compute power specifically for search and query. When you create a Pro database with search and query, you can increase your search queries per second by the selected factor.\n\n![For Pro databases, you can select the query performance factor for your database.](https://redis.io/docs/latest/images/rc/database-details-query-performance-factor-pro.png)\n\nWe recommend testing your application with a test database to see your baseline queries per second and determine how much you want to boost your query performance. After you have determined your queries per second and your desired performance factor, [create a new database](https://redis.io/docs/latest/operate/rc/databases/create-database) with the desired performance factor and [migrate data](https://redis.io/docs/latest/operate/rc/databases/migrate-databases) from the test database to your new database.\n\nFor more info on how to use scalable search, see [Best practices for scalable Redis Search](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/scalable-query-best-practices).\n\nThe query performance factor is available for Redis Cloud Pro databases on Redis 7.2 and later."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/databases/configuration/clustering","title":"Clustering Redis Databases","url":"https://redis.io/docs/latest/operate/rc/databases/configuration/clustering/","summary":"Redis Cloud uses clustering to manage very large databases (25 GB and larger) or high-throughput databases (25,000 ops/sec and higher). Here, you'll learn how to manage clustering and how to use hashing policies to control how data is managed.","aliases":["/operate/rc/concepts/clustering"],"tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:09+01:00","page_type":"content","content_hash":"45a0e9dcc4aee87f88459db160f166ccb66988b717f288324191a3c07b32b9b9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"For very large databases, Redis Cloud distributes database data to different cloud instances.  For example:\n\n- When data grows beyond the RAM resources of a single server.\n\n    Multiple shards should be used when data grows to 25 GB (50 GB for Auto Tiering) to create multiple shards.\n\n- The operations performed against the database are CPU intensive enough to degrade performance.\n\n    Multiple shards should be used when throughput grows to 25,000 ops/sec. Clustering distributes operational load, whether to instances on the same server or across multiple servers.\n\nThis distribution is called _clustering_ because it manages the way data is distributed throughout the cluster of nodes that support the database."},{"id":"how-data-is-distributed","title":"How data is distributed","role":"content","text":"A Redis Cloud cluster is a set of managed Redis processes and cloud instances,\nwith each process managing a subset of the database keyspace.\nClustering uses multiple cores and resources of multiple instances to overcome scaling challenges.\n\nIn a Redis Cloud cluster, the keyspace is partitioned into hash\nslots. At any given time a hash slot resides on and is managed by a single\nRedis server. \n\nAn instance that belongs to a cluster can manage multiple hash\nslots. This division of the key space, known as _sharding_, is achieved by\nhashing the key names, or parts of these (key hash tags), in order to\nobtain the hash slot in which a key should reside. Redis Cloud supports several [hashing policies](#manage-the-hashing-policy).\n\nEven when using multiple Redis processes, the use of a Redis\nEnterprise Cloud cluster is nearly transparent to the application that\nuses it. The cluster is accessible via a single endpoint that\nautomatically routes all operations to the relevant shards, without the\ncomplexity of a cluster-aware Redis client. This allows applications to\nbenefit from using the cluster without performing any code changes, even\nif they were not designed to use it beforehand.\n\nWhen creating or editing a Redis database on Redis Cloud, the\nsystem automatically calculates the number of shards needed based on\nthe database memory limit and required throughput."},{"id":"multikey-operations","title":"Multi-key operations","role":"content","text":"Operations on multiple keys in a sharded Redis Cloud cluster\nare supported, with the following limitations:\n\n1. **Multi-key commands:** Redis offers several commands that accept\n    multiple keys as arguments. In a sharded setup, you can run multi-key commands\n    only if all the affected keys reside in the same slot (thus on the same shard).\n    This restriction applies to all mulit-key commands, including BITOP, BLPOP, BRPOP, BRPOPLPUSH, MSETNX,\n    RPOPLPUSH, SDIFF, SDIFFSTORE, SINTER, SINTERSTORE, SMOVE, SORT,\n    SUNION, XREAD, XREADGROUP, ZINTER, ZINTERSTORE, ZUNION, ZUNIONSTORE, ZDIFF, ZDIFFSTORE\n1. **Geo commands:** In GEORADIUS/GEORADIUSBYMEMBER/GEOSEARCHSTORE commands, the\n    STORE and STOREDIST options can only be used when all affected keys\n    reside in the same slot.\n1. **Transactions:** All operations within a WATCH/MULTI/EXEC block\n    should be performed on keys that are in the same slot.\n1. **Lua scripts:** All keys that are used by the script must reside in\n    the same slot and need to be provided as arguments to the\n    EVAL/EVALSHA commands (as per the Redis specification).\n1. **Renaming/Copy keys:** The use of the RENAME/RENAMENX/COPY commands is\n    allowed only when both the key's original name and its new name are\n    mapped to the same hash slot.\n1. **Variadic commands**: The use of (MGET, MSET, HMGET, HMSET, etc..)\n    and pipelining are supported with Redis Cloud cluster\n    as if it were a non-cluster DB.\n\n    [Active-Active databases](https://redis.io/docs/latest/operate/rc/databases/active-active) have stricter rules: multi-key write commands (DEL, MSET, UNLINK) can only run on keys in the same slot. Only MGET, EXISTS, and TOUCH are allowed across slots. See [Multi-key operations on Active-Active databases](https://redis.io/docs/latest/develop/using-commands/multi-key-operations#active-active-databases) for details."},{"id":"manage-the-hashing-policy","title":"Hashing policies and hash tags","role":"content","text":"The hashing policy determines how data is distributed across multiple Redis processes of a database. It uses a hashing function to map keys to hash slots in these processes, ensuring an even distribution of data for optimal performance and scalability.\n\nThe hashing function uses the entire key name to calculate the hash slot, unless the key name contains a **hash tag**, represented by a `{...}` pattern.\n\nIf the key contains a `{...}` pattern, only the substring between `{` and `}` is hashed in order to obtain the hash slot.\n\nYou can use the `{...}` pattern to direct related keys to the same hash slot, so that multi-key operations are supported on them. On the other hand, not using a hashtag in the key's name results in a (statistically) even distribution of keys across the keyspace's shards, which improves resource utilization. If your application does not perform multi-key operations, you don't need to construct key names with hashtags.\n\nRedis Cloud offers 3 hashing policies, which differ in how hash tags are processed. These hashing policies are not always available.\n\nRedis Cloud defaults to the [Redis hashing policy](#redis-hashing-policy) **when creating a new database**. You can select a different hashing policy during database creation, but you cannot change the hashing policy to or from the Redis hashing policy after the database is created."},{"id":"redis-hashing-policy","title":"Redis hashing policy","role":"content","text":"The Redis hashing policy is identical to the [hashing policy used by Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack/reference/cluster-spec#hash-tags). This policy is recommended for most users and you should select it if any of the following conditions apply:\n- This is your first Redis Cloud account, and you are starting fresh.\n- You are migrating data from Redis Open Source or other Redis-managed platforms.\n- Your application does not use hashtags in database key names.\n- Your application uses binary data as key names.\n\nThe Redis hashing policy allows for [Smooth Scaling](#smooth-scaling) where available."},{"id":"standard-hashing-policy","title":"Standard hashing policy","role":"content","text":"The Standard hashing policy is mostly consistent with the Redis hashing policy, and will generate the same hash-slot calculation in the following cases:\n1. Keys with a single hashtag: a key's hashtag is any substring between '{' and '}' in the key's name. That means that when a key's name includes the pattern '{...}', the hashtag is used as input for the hashing function. For example, the following key names have the same hashtag and are mapped to the same slot: foo{bar}, {bar}baz & foo{bar}baz.\n1. Keys without a hashtag: when a key doesn't contain the '{...}' pattern, the entire key's name is used for hashing\n\nIn some cases, the Standard hashing policy behaves differently from the Redis hashing policy:\n1. Using empty hashtags (\"{}\"): the Standard hashing policy does not ignore empty hashtags, so two keys that start with empty hashtags will be hashed to the same hashslot (while the Redis hashing policy would ignore them). \n    For example: given 2 keys {}foo and {}bar, hashing would be:\n    - Standard hashing policy: to the same hash slot\n    - Redis hashing policy: to different hash slots\n2. Using multiple curly brackets: when a key’s name contains multiple curly brackets, the Standard hashing calculation might be different than the Redis hashing policy. \n    For example: given 2 keys {foo}bar} and {foo}qux}:\n    - Standard hashing policy: substrings \"foo}bar\" and \"foo}qux\" will be used for the 1st and 2nd key respectively, hashed each key to a different hash-slot.\n    - Redis hashing policy: the substring \"foo\" will be used for both keys, hashing them to the same slot.\n\n> [!NOTE]\n> To allow seamless transition between hashing policies, the following techniques are not recommended:\n> - Using empty hashtags to hash different keys to the same hashslot\n> - Using multiple curly brackets within a key’s name"},{"id":"custom-hashing-policy","title":"Custom hashing policy","role":"content","text":"> [!NOTE]\n> The custom hashing policy is not available for accounts created after March 31, 2025.\n>\n> For all other accounts, this policy is not recommended and will be deprecated in the future. Select this option only if you are already using a custom hashing policy with your existing Redis Cloud databases.\n\nA Redis Cloud  cluster can be configured to use a custom hashing\npolicy. A custom hashing policy is required when different keys need to\nbe kept together on the same shard to allow multi-key operations. Redis\nCloud's custom hashing policy is provided via a set of Perl\nCompatible Regular Expressions (PCRE) rules that describe the dataset's\nkey name patterns.\n\nTo configure a custom hashing policy, enter regular expression (RegEx)\nrules that identify the substring in the key's name - hashtag - on\nwhich hashing will be done. The hashing tag is denoted in the RegEx by\nthe use of the \\`tag\\` named subpattern. Different keys that have the\nsame hashtag will be stored and managed in the same slot.\n\nOnce you enable the custom hashing policy, the Redis Cloud's\ndefault RegEx rules that implement the standard hashing policy are:\n\n| RegEx Rule | Description |\n|-----------|-----------------|\n| .\\*{(?\\<tag\\>.\\*)}.\\* | Hashing is done on the substring between the curly braces. |\n| (?\\<tag\\>.\\*) | The entire key's name is used for hashing. |\n\nYou can modify existing rules, add new ones, delete rules, or change\ntheir order to suit your application's requirements.\n\n> [!WARNING]\n> If the Custom hashing policy is available, you can change the hashing policy between Standard and Custom after you create your database. However, hashing policy changes delete existing data \n> (using [`FLUSHDB`](https://redis.io/docs/latest/commands/flushdb)) before they're applied. \n>\n> These changes include:\n>\n> 1. Changing the hashing policy, either from standard to custom or vice versa.\n> 1. Changing the order of custom hashing policy rules.\n> 1. Adding rules before existing ones in the custom hashing policy.\n> 1. Deleting rules from the custom hashing policy.\n> 1. Disabling clustering for the database."},{"id":"custom-hashing-policy-notes-and-limitations","title":"Custom hashing policy notes and limitations","role":"content","text":"1. You can define up to 32 RegEx rules, each up to 256 characters.\n1. RegEx rules are evaluated by their order.\n1. The first rule matched is used; strive to place common key name\n    patterns at the beginning of the rule list.\n1. Key names that do not match any of the RegEx rules trigger an\n    error.\n1. The '.\\*(?\\<tag\\>)' RegEx rule forces keys into a single slot as the\n    hash key is always empty. When used, this should be the last\n    catch-all rule.\n1. The following flag is enabled in our regular expression parser:\n   - **PCRE_ANCHORED:** the pattern is constrained to match only at\n        the start of the string which is being searched."},{"id":"oss-cluster-api","title":"OSS Cluster API","role":"content","text":"OSS Cluster API reduces access times and latency with near-linear scalability.\nThe OSS Cluster API provides a simple mechanism for Redis clients to know the cluster topology.\n\nClients must first connect to the master node to get the cluster topology,\nand then they connect directly to the Redis proxy on each node that hosts a master shard.\n\n> [!NOTE]\n> You must use a client that supports the cluster API to connect to a database\n> that has the cluster API enabled.\n\n\nThe OSS Cluster API is only supported on Redis Cloud Pro databases. You can enable it in the **Performance** section of the configuration screen.\n\nAfter you select OSS Cluster API, you can select **Use external endpoint** if you want to use the external endpoint for the database. Selecting **Use external endpoint** will block the private endpoint for this database.\n\nThe OSS Cluster API is supported when a database uses the [standard hashing policy](#standard-hashing-policy) or the [Redis hashing policy](#redis-hashing-policy).\n\nReview [OSS Cluster API architecture](https://redis.io/docs/latest/operate/rs/clusters/optimize/oss-cluster-api) to determine if you should enable this feature for your database."},{"id":"smooth-scaling","title":"Smooth scaling","role":"content","text":"Smooth scaling is an improved resharding method for Redis Cloud Pro databases. Compared to traditional resharding, it is significantly faster and reduces latency spikes and disconnects during scaling.\n\n> [!NOTE]\n> Smooth scaling is available for databases that meet the following prerequisites. Other databases continue to use traditional scaling."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Smooth scaling is used automatically when a database meets all of the following conditions:\n\n| Requirement | Detail |\n|---|---|\n| Hashing policy | Must use the [Redis hashing policy](#redis-hashing-policy). Databases using the Standard or Custom hashing policy use traditional scaling instead. |\n| Database version | Redis 8.4 or later. |"},{"id":"not-supported","title":"Not supported","role":"content","text":"Smooth scaling is not available for:\n\n- Active-Active databases\n- Flex (Auto Tiering) databases\n- Existing databases that use the Standard or Custom hashing policy"},{"id":"backward-compatibility","title":"Backward compatibility","role":"content","text":"You do not need to make any changes to your application code. The changes related to smooth scaling are implemented internally and do not affect RESP commands or how clients connect to and communicate with the database."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/databases/configuration/data-eviction-policies","title":"Data eviction","url":"https://redis.io/docs/latest/operate/rc/databases/configuration/data-eviction-policies/","summary":"Data eviction policies control what happens when new data exceeds the memory limits of a database. Here, you'll learn the available policies and how to change which one is used for a database.","aliases":["/operate/rc/concepts/data-eviction-policies"],"tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:09+01:00","page_type":"content","content_hash":"2eb51fe7a0974119efcf06c469c33f5fcbf8a84730e0f0773127a57702d485cb","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The data eviction policy of a database controls what happens when new data exceeds the memory size of a database.  Typically, such situations require _evicting_ (or deleting) data previously added to the database.  \n\nYou can [edit database details](https://redis.io/docs/latest/operate/rc/databases/view-edit-database) to change the **Data eviction policy** setting at the database level."},{"id":"available-policies","title":"Available policies","role":"content","text":"For each database, you can choose from these data eviction policies:\n\n| **Available&nbsp;policies** | **Description** |\n|:------------|:-----------------|\n| allkeys-lru | Keeps most recently used keys; removes least recently used (LRU) keys |\n| allkeys-lfu | Keeps frequently used keys; removes least frequently used (LFU) keys |\n| allkeys-random | Randomly removes keys |\n| volatile-lru | Removes least recently used keys with `expire` field set to true (*Default*) |\n| volatile-lfu | Removes least frequently used keys with `expire` field set to true |\n| volatile-random | Randomly removes keys with `expire` field set to true |\n| volatile-ttl | Removes keys with expire field set to true and the shortest remaining time-to-live (TTL) value |\n| no eviction | New values aren't saved when memory limit is reached<br/><br/>When a database uses replication, this applies to the primary database |"},{"id":"prevent-data-eviction","title":"Prevent data eviction","role":"content","text":"To avoid data eviction, make sure your database is large enough to hold required values. \n\nRedis Cloud supports [Auto Tiering](https://redis.io/docs/latest/operate/rs/databases/flash) on Redis Cloud Pro and [Redis Flex](https://redis.io/docs/latest/operate/rc/databases/create-database/create-flex-database) on Redis Cloud Essentials\nto prevent data eviction but maintain high performance.\n\nAuto Tiering and Redis Flex can extend your database across RAM and Flash Memory and intelligently manage \"hot\" (active) data in RAM and \"cold\" (less active) data in Flash memory (SSD)."},{"id":"active-passive-replication-considerations","title":"Active-Passive replication considerations","role":"content","text":"When using [Active-Passive replication](https://redis.io/docs/latest/operate/rc/databases/migrate-databases#sync-using-active-passive), eviction and expiration only operate on the source (active) database. The target database does not evict or expire data while Active-Passive is enabled. \n\nDo not write to the target database while Active-Passive is enabled. Doing so can cause the following issues:\n\n- The target database cannot rely on eviction or expiration to manage local writes, requiring sufficient memory to handle both replicated data and local writes.\n- Local writes create differences between the source and target databases, causing replicated commands to behave differently on each database.\n- Inconsistent data can cause replicated commands to fail with errors, which will cause the synchronization process to exit and break replication."},{"id":"active-active-replication-considerations","title":"Active-Active replication considerations","role":"content","text":"The eviction policy mechanism for [Active-Active databases](https://redis.io/docs/latest/operate/rc/databases/active-active) kicks in earlier than for standalone databases because it requires propagation to all regions. The eviction policy starts to evict keys when one of the Active-Active instances reaches 80% of its memory limit. If memory usage continues to rise while the keys are being evicted, the rate of eviction will increase to prevent reaching the Out-of-Memory state."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/databases/configuration/data-persistence","title":"Data persistence","url":"https://redis.io/docs/latest/operate/rc/databases/configuration/data-persistence/","summary":"Data persistence enables recovery in the event of memory loss or other catastrophic failure. Here, you learn data persistence options, when they're available, and how to apply specific settings to individual databases.","aliases":["/operate/rc/concepts/data-persistence"],"tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:09+01:00","page_type":"content","content_hash":"9d97c33edd9285c357b588de9cc3816fbcab79b16d194a6c5a0094d46d44a0ab","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Cloud can persist data to enable recovery in the event of memory loss or other catastrophic failure.  When you enable data persistence, in-memory data is copied to persistent storage attached to the underlying cloud instance."},{"id":"persistence-options","title":"Persistence options","role":"content","text":"Data can be persisted in one of two ways:\n\n- An _Append-Only File_ (AOF) maintains a record (sometimes called a _redo log_ or _journal_) of write operations.  This allows the data to be restored by using the record to reconstruct the database up to the point of failure.\n\n    The AOF file records write operations made to the database; it can be updated every second or on every write (_Redis Cloud Pro plans only_).\n\n- Snapshots are copies of the in-memory database, taken at periodic intervals (one, six, or twelve hours). You can restore data to the snapshot's point in time.\n\n\nAOF files provide greater protection (durability) than snapshots at the cost of resources and recovery time. \nAlthough snapshot recovery is faster, the risk of data loss is higher, depending on the time between failure and the most recent snapshot.\n\n> [!WARNING]\n> If you turn off data persistence, data is lost when the database goes down."},{"id":"configure-data-persistence","title":"Configure data persistence","role":"configuration","text":"In Redis Cloud, data persistence is a database configuration setting that can be changed by [editing your database](https://redis.io/docs/latest/operate/rc/databases/view-edit-database) settings.\n\nThe availability of the setting depends on your plan:\n\n- Free Redis Cloud Essentials plans do not support data persistence; the setting is disabled entirely.\n\n- Paid Redis Cloud Essentials plans support AOF every second and all snapshot options.\n\n- Redis Cloud Pro enables data persistence settings for every database.\n\nWhen enabled, you can change the **Data persistence** setting to one of the following values:\n\n| **Options** | **Description** |\n|------------|-----------------|\n|  None | Data is not persisted to disk at all. |\n|  Append Only File (AoF) every write | _(Redis Cloud Pro only)_ Every write is recorded (synchronized to disk using `fsync`) |\n|  Append Only File (AoF) every 1 second | Record is updated every second (synchronized to disk using `fsync`)|\n|  Snapshot every 1 hour | A snapshot of the database is created every hour |\n|  Snapshot every 6 hours | A snapshot of the database is created every 6 hours |\n|  Snapshot every 12 hours | A snapshot of the database is created every 12 hours |\n\nWhen you save changes to data persistence settings, the updates are applied in the background.  This means there is a brief delay while the new settings are applied.\n\nWhen replication is enabled for a database, persistence is performed against replicas (copies) to reduce performance impact on the primary (_master_) database."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/databases/configuration/high-availability","title":"High availability and replication","url":"https://redis.io/docs/latest/operate/rc/databases/configuration/high-availability/","summary":"Describes database replication and high availability as it affects Redis Cloud.","aliases":["/operate/rc/databases/high-availability"],"tags":["docs","operate","rc"],"last_updated":"2026-10-01T15:21:38+01:00","page_type":"content","content_hash":"bc34e968aa9079ef858b02277c5c30c81cba73305a0daa4371fe939cd8015a68","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Database replication helps ensure high availability. \n\nWhen replication is enabled, your dataset is duplicated to create a replica that is synchronized with the primary dataset.  \n\nReplication allows for automatic failover and greater fault tolerance.  It can prevent data loss in the event of a hardware or zone failure."},{"id":"options-and-plan-support","title":"Options and plan support","role":"parameters","text":"Redis Cloud supports three levels of replication:\n\n- _No replication_ means that you will have a single copy of your database.\n\n- _Single-zone replication_ means that your database will have a primary and a replica located in the same cloud zone. If anything happens to the primary, the replica takes over and becomes the new primary. Regions with fewer than three availability zones can only use single-zone replication.\n\n- _Multi-zone replication_ means that the primary and its replicas are stored in different zones. This means that your database can remain online even if an entire zone becomes unavailable. Multi-zone replication requires a region with three or more availability zones.\n\nYour replication options depend on your [subscription plan](https://redis.io/docs/latest/operate/rc/subscriptions):\n\n- Free Redis Cloud Essentials plans do not support replication.\n- Paid Redis Cloud Essentials plans and Redis Cloud Pro plans let you choose between multi-zone or single-zone replication when creating a subscription. You can also turn off replication.\n\nWhether or not you can change your replication settings after database creation depends on your plan type and what type of replication you originally selected.\n\n- For Redis Cloud Essentials, you select the kind of replication when you create your database. If you selected Multi-zone replication when you created your database, you can't change it to Single-zone or turn it off after database creation. However, if you selected No Replication or Single-zone replication, you can switch between the two at any time by [editing your Essentials subscription](https://redis.io/docs/latest/operate/rc/subscriptions/view-essentials-subscription#change-high-availability-and-persistence). \n- For Redis Cloud Pro, you select whether you want a multi-zone deployment during subscription creation. You can't change this setting or any [Zone settings](https://redis.io/docs/latest/operate/rc/databases/configuration/high-availability#zone-setting-maintenance) after subscription creation. However, you can activate or deactivate replication for each database within the subscription at any time by [editing your database](https://redis.io/docs/latest/operate/rc/databases/view-edit-database)."},{"id":"performance-impact","title":"Performance impact","role":"performance","text":"Replication can affect performance as traffic increases to synchronize all copies."},{"id":"dataset-size","title":"Dataset size","role":"content","text":"For both Redis Cloud Essentials and Redis Cloud Pro, replication requires a memory limit that is double the dataset size of your database.\n\nFor Redis Cloud Essentials, the size of the plan you choose includes replication. Therefore, if you choose replication, the dataset size you can use is half of the stated plan size. For example, if you choose a 1 GB plan, Redis allocates 512 MB for the dataset size, and the other 512 MB for replication.\n\nFor Redis Cloud Pro, you select your dataset size when you create your database, and we calculate your memory limit based on the replication settings you choose."},{"id":"zone-setting-maintenance","title":"Zone setting maintenance","role":"content","text":"Zone settings can only be defined when a subscription is created.  You cannot change these settings once the subscription becomes active.\n\nThis means you can't convert a multi-zone subscription to a single zone (or vice-versa).  \n\nTo use different zone settings, create a new subscription with the preferred settings and then [migrate data](https://redis.io/docs/latest/operate/rc/databases/migrate-databases) from the original subscription."},{"id":"availability-zones","title":"Availability zones","role":"content","text":"You can reduce network transfer costs and network latency by ensuring your Redis Cloud Pro cluster and your application are located in the same availability zone. \n\nTo specify the availability zone(s) for your cluster, [create your Pro database with custom settings](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new), and select *Manual Selection* under **Allowed Availability Zones** in **Advanced options**.\n\nFor Google Cloud clusters and [Redis Cloud BYOC](https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud), select an availability zone from the **Zone name** list.\n\n![Select one availability zone when Multi-AZ is turned off.](https://redis.io/docs/latest/images/rc/availability-zones-no-multi-az.png)\n\nFor all other AWS clusters, select an availability zone ID from the **Zone IDs** list. For more information on how to find an availability zone ID, see the [AWS docs](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html#concepts-availability-zones).\n\n![For hosted AWS clusters, select availability zone IDs from the Zone IDs list.](https://redis.io/docs/latest/images/rc/availability-zones-aws-hosted-no-multi-az.png)\n\n> [!NOTE]\n> Multi-AZ requires a region with at least three availability zones. Regions with fewer than three availability zones don't support Multi-AZ deployments.\n\nIf **Multi-AZ** is enabled, you must select three availability zones from the list.\n\n![Select Manual selection to select three availability zones when Multi-AZ is enabled.](https://redis.io/docs/latest/images/rc/availability-zones-multi-az.png)\n\nFor more information on availability zones, see the [Google Cloud docs](https://cloud.google.com/compute/docs/regions-zones/#available) or the [AWS docs](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html#concepts-availability-zones)."},{"id":"more-info","title":"More info","role":"content","text":"To learn more about high availability and replication, see:\n- [Highly Available Redis](https://redis.io/docs/latest/operate/rs/databases/durability-ha)\n- [Database replication](https://redis.io/docs/latest/operate/rs/databases/durability-ha/replication)"}],"examples":[]}
{"schema_version":2,"id":"operate/rc/databases/configuration","title":"Configure databases","url":"https://redis.io/docs/latest/operate/rc/databases/configuration/","summary":"Provides background information to help you configure Redis Cloud databases to best fit your needs.","tags":["docs","operate","rc"],"last_updated":"2024-02-26T13:11:00+01:00","children":[{"id":"operate/rc/databases/configuration/advanced-capabilities","summary":"Describes Redis Cloud Advanced capability options.","title":"Advanced capabilities","url":"https://redis.io/docs/latest/operate/rc/databases/configuration/advanced-capabilities/"},{"id":"operate/rc/databases/configuration/clustering","summary":"Redis Cloud uses clustering to manage very large databases (25 GB and larger) or high-throughput databases (25,000 ops/sec and higher). Here, you'll learn how to manage clustering and how to use hashing policies to control how data is managed.","title":"Clustering Redis Databases","url":"https://redis.io/docs/latest/operate/rc/databases/configuration/clustering/"},{"id":"operate/rc/databases/configuration/data-eviction-policies","summary":"Data eviction policies control what happens when new data exceeds the memory limits of a database. Here, you'll learn the available policies and how to change which one is used for a database.","title":"Data eviction","url":"https://redis.io/docs/latest/operate/rc/databases/configuration/data-eviction-policies/"},{"id":"operate/rc/databases/configuration/data-persistence","summary":"Data persistence enables recovery in the event of memory loss or other catastrophic failure. Here, you learn data persistence options, when they're available, and how to apply specific settings to individual databases.","title":"Data persistence","url":"https://redis.io/docs/latest/operate/rc/databases/configuration/data-persistence/"},{"id":"operate/rc/databases/configuration/high-availability","summary":"Describes database replication and high availability as it affects Redis Cloud.","title":"High availability and replication","url":"https://redis.io/docs/latest/operate/rc/databases/configuration/high-availability/"},{"id":"operate/rc/databases/configuration/sizing","summary":"Describes sizing considerations for your Redis Cloud database, including throughput and dataset size.","title":"Size a Redis Cloud database","url":"https://redis.io/docs/latest/operate/rc/databases/configuration/sizing/"}],"page_type":"content","content_hash":"34fa687baab861de9d44aa7077677b18c6c01b61ba3130ac64e38c9d42a56c71","sections":[{"id":"content","title":"Content","role":"content","text":"Here, you'll find background info and conceptual details to help you customize your Redis Cloud databases to meet your needs."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/databases/configuration/sizing","title":"Size a Redis Cloud database","url":"https://redis.io/docs/latest/operate/rc/databases/configuration/sizing/","summary":"Describes sizing considerations for your Redis Cloud database, including throughput and dataset size.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:09+01:00","page_type":"content","content_hash":"41c892b9a66ef7a4fc6677c77bd9a51cc8ba2aeb34a01d04f0fc31e14a4ec84f","sections":[{"id":"dataset-size","title":"Dataset size","role":"overview","text":"The dataset size of a database is a part of the full memory limit for the database. The memory limit represents the maximum amount of memory for the database, which includes data values, keys, module data, and overhead for specific features.  High availability features, such as replication and Active-Active,  increase memory consumption, so your dataset size and memory limit will be different.\n\nFor Redis Cloud Essentials, the plan size refers to the full memory limit, not the dataset size. Both the total memory limit and dataset size are listed under **Database details** when you create an Essentials database.\n\nFor Redis Cloud Pro, you define your dataset size when you create the database, and we calculate your total memory limit based on the features you choose. \n\nHere are some general guidelines:\n\n- Memory limit represents an upper limit.  You cannot store more data than the memory limit.  Depending on your other selections, available memory for data may be less than expected.\n\n- [Replication](https://redis.io/docs/latest/operate/rc/databases/configuration/high-availability) doubles memory consumption; that is, 512 MB of data requires at least 1 GB of memory limit when replication is enabled. This affects both Redis Cloud Pro and Redis Cloud Essentials. For example, if you subscribe to a 1 GB Essentials plan, Redis will allocate 512 MB for your dataset and the other 512 MB for replication.\n\n- [Active-Active](https://redis.io/docs/latest/operate/rc/databases/active-active) also doubles memory consumption and the effect is cumulative with the impact of replication. Since Active-Active requires replication to be turned on, the memory limit impact can be as large as four times (4x) the original data size.\n\n    Active-Active databases also have additional sizing considerations:\n\n    - Active-Active databases begin evicting keys when any one instance reaches 80% of its memory limit, rather than 100%, because eviction must propagate to all participating instances. Size your database so that expected usage stays below this threshold.\n\n    - Each Active-Active instance reserves a replication backlog for shard synchronization and an Active-Active replication backlog for synchronization between instances. By default, each backlog is set to 1% of the database size.\n\n- [Advanced capabilities](https://redis.io/docs/latest/operate/rc/databases/configuration/advanced-capabilities) also consume memory. For search databases, consider index size when you size your database. See [Search and query sizing](https://redis.io/docs/latest/operate/rc/databases/configuration/advanced-capabilities#search-and-query-sizing) for more info.\n\nMemory limits in Redis Cloud are subject to the same considerations as Redis Software; to learn more, see [Database memory limits](https://redis.io/docs/latest/operate/rs/databases/memory-performance/memory-limit)."},{"id":"throughput","title":"Throughput","role":"content","text":"Throughput is the number of operations a database can handle over a certain period of time. For Redis Cloud databases, throughput is defined in operations per second (ops/sec).\n\nFor a Redis Cloud Pro subscription, you define throughput for a database when you create it. For a Redis Cloud Essentials subscription, your maximum throughput depends on your plan. \n\nWe use this setting to guide the allocation of compute power and network bandwidth, ensuring your database can handle the expected workload. However, the throughput specified is not guaranteed - actual throughput may be higher or lower depending on your workload and database configuration."},{"id":"throughput-factors-and-variability","title":"Throughput factors and variability","role":"content","text":"Some factors that can affect throughput include:\n- **Request size**: Smaller requests (under 3KB) consume less network bandwidth and may result in more operations per second than requested, while larger requests may result in fewer operations per second.\n- **Command complexity**: Simple commands, like `GET` and `SET`, are faster and require fewer resources, whereas more complex commands involve more processing time and can reduce throughput. See the [Command list](/content/commands) to see which commands are more complex than others.\n- **Replication**: Using [multi-zone replication](https://redis.io/docs/latest/operate/rc/databases/configuration/high-availability) affects throughput as each write operation is executed asynchronously in each zone.\n- **Security**: Some security options, such as [transport layer security](https://redis.io/docs/latest/operate/rc/security/database-security/tls-ssl), may affect throughput.\n- **Number of client connections**: The number of client connections affects throughput. Increasing or decreasing the number of client connections can result in higher or lower throughput."},{"id":"optimize-throughput","title":"Optimize throughput","role":"content","text":"Here are some things to keep in mind for optimizing throughput:\n- Optimize capacity planning and sizing of your Redis Cloud databases to meet your app performance requirements.\n- Benchmark your app to understand what latency expectations are required, and adjust throughput accordingly.\n- Test and monitor your app's performance and adjust the set ops/sec based on how if performs in real-world conditions.\n- If your average request size is larger than 3KB, consider setting your throughput higher than expected. \n- Track the slow logs using the [`SLOWLOG` command](https://redis.io/docs/latest/commands/slowlog) or the **Slowlog** tab on the [database screen](https://redis.io/docs/latest/operate/rc/databases/view-edit-database).\n- Use [pipelining](https://redis.io/docs/latest/develop/using-commands/pipelining) and [concurrent connections](https://redis.io/docs/latest/develop/reference/clients) effectively to optimize throughput and latency.\n- Search databases have their own throughput requirements. See [Search and query sizing](https://redis.io/docs/latest/operate/rc/databases/configuration/advanced-capabilities#search-and-query-sizing) for more info."},{"id":"frequently-asked-questions","title":"Frequently asked questions","role":"content","text":"**Can my workload exceed the configured throughput?**\n\nYes, many workloads perform better than expected, especially with optimized configurations and ideal conditions.\n\n**Why is my application getting less throughput than what I set?**\n\nFactors like high connection counts, complex commands, large payloads, and network limitations can affect throughput. Test and adjust based on your application’s needs. \n\n**How do I know the optimal throughput for my application?**\n\nStart with the expected ops/sec. Most of the time, it’s more than enough. For an average request size of less than 3KB, we suggest specifying lower ops/sec than expected to reduce costs. For request sizes higher than 3 KB, we suggest specifying higher ops/sec than your target. Benchmarking your application under production-like conditions will help you find the best configuration.\n\n**How does network bandwidth impact throughput?**\n\nInsufficient bandwidth can bottleneck performance, especially with large request sizes.\n\n**What is the expected latency for my application?**\n\nLatency expectations vary by use case. Some applications tolerate milliseconds, while others require sub-millisecond performance. Benchmark your application to understand its latency profile."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/databases/connect","title":"Connect to a Redis Cloud database","url":"https://redis.io/docs/latest/operate/rc/databases/connect/","summary":"","tags":["docs","operate","rc"],"last_updated":"2026-10-05T10:59:52+01:00","children":[{"id":"operate/rc/databases/connect/insight-cloud","summary":"Shows how to open your database in a browser-based version of Redis Insight and lists the features that are available.","title":"Use Redis Insight on Redis Cloud","url":"https://redis.io/docs/latest/operate/rc/databases/connect/insight-cloud/"}],"page_type":"content","content_hash":"4b1745f642d6ce2052fc08c6cd1671e0f37596a98b8c617a5281e6420e04c884","sections":[{"id":"overview","title":"Overview","role":"overview","text":"After you [create your database](https://redis.io/docs/latest/operate/rc/databases/create-database), you can connect to it using the public or private endpoint."},{"id":"get-connection-information","title":"Get connection information","role":"content","text":"To connect to the database, you need the following information:\n- The database endpoint\n- Your database username and password"},{"id":"database-endpoints","title":"Database endpoints","role":"content","text":"The database endpoints are listed in the **Configuration** tab for your database: in the **Access** section for Essentials databases, or the **General** section for Pro databases.\n\n![The General section of the Configuration tab of the Pro database details page.](https://redis.io/docs/latest/images/rc/databases-configuration-general-endpoints.png)\n\n![The Access section for an Essentials database.](https://redis.io/docs/latest/images/rc/database-details-configuration-tab-access-essentials.png)\n\nRedis Cloud Pro and Redis Cloud Essentials databases have a public endpoint, which you can access from the public internet. Redis Cloud Pro databases also have a private endpoint. You can connect to the private endpoint from a private network. Before you can connect to the private endpoint, you must set up a private connectivity method, such as:\n- [VPC peering](https://redis.io/docs/latest/operate/rc/security/vpc-peering)\n- [Google Cloud Private Service Connect](https://redis.io/docs/latest/operate/rc/security/private-service-connect) (Google Cloud only)\n- [AWS Transit Gateway](https://redis.io/docs/latest/operate/rc/security/aws-transit-gateway) or [AWS PrivateLink](https://redis.io/docs/latest/operate/rc/security/aws-privatelink) (AWS only)\n\nRedis Cloud Pro users can block the public endpoint for their databases. For more information, see [Block public endpoints](https://redis.io/docs/latest/operate/rc/security/database-security/block-public-endpoints).\n\n#### Static and dynamic endpoints\n\nStatic endpoints on Redis Cloud start with `redis-<port>.c<number>` (or `redis-<port>.internal.c<number>` for the private endpoint). For example, a static endpoint might look like this:\n\n[code example]\n\nDynamic endpoints on Redis Cloud always contain three words and a random number, and end in `db.redis.io`. For example, a dynamic endpoint might look like this:\n\n[code example]\n\nYou can see the Dynamic endpoints for databases with both static and dynamic endpoints by expanding the **Dynamic endpoints** section in the **General** section of the **Configuration** tab. \n\n![The Static and dynamic endpoints for a database with both kinds of endpoints.](https://redis.io/docs/latest/images/rc/databases-configuration-general-endpoints-legacy.png)\n\nYou can redirect the dynamic endpoints to a different database at any time, but you cannot redirect the static endpoints. We recommend using the dynamic endpoints for your application so that you can migrate your database endpoints to a different database in the future without any code changes. See [Redirect dynamic endpoints](https://redis.io/docs/latest/operate/rc/databases/redirect-endpoints) for more information."},{"id":"database-username-and-password","title":"Database username and password","role":"content","text":"By default, your database is protected by a [**Default user**](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/default-user) with the username `default` and a masked **Default user password**. For Essentials databases, select **Default user > Configure** and then select the eye icon to view your password. \n\nFor Pro databases, you can see the default user password in the **Security** section of the **Configuration** details for your database. Select the eye icon to show or hide the password.    \n\nIf you've turned on [Role-based access control](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/role-based-access-control) for your database and [turned off the default User](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/default-user#turn-off-default-user), use the username and password for your data access role."},{"id":"connect-to-your-database-with-connection-wizard","title":"Connect to your database with connection wizard","role":"content","text":"Select **Connect** to open the connection wizard.\n\n![Connect button.](https://redis.io/docs/latest/images/rc/button-connect.png#no-click)\n\n> [!NOTE]\n> For [Active-Active databases](https://redis.io/docs/latest/operate/rc/databases/active-active), you connect to one of the database instances. Choose the region you want to connect to from the region selection to access the connection information for that instance.\n\nThe connection wizard provides the following database connection methods:\n\n- [Redis Insight](#using-redisinsight)\n\n- [`redis-cli`](#using-rediscli) utility\n\n- [Redis client](#using-redis-client) for your preferred programming language\n\n![The connection wizard.](https://redis.io/docs/latest/images/rc/connection-wizard.png)"},{"id":"using-redisinsight","title":"Redis Insight","role":"content","text":"[Redis Insight](https://redis.io/docs/latest/develop/tools/insight) is a free Redis GUI that lets you visualize your Redis data and learn more about Redis.\n\nYou can connect to your database with Redis Insight in two ways:\n\n1. [Open your database in Redis Insight in your browser](#ri-browser).\n\n1. [Download and Install Redis Insight](#ri-app) on Windows, macOS, and Linux.\n\n#### Open in your browser {#ri-browser}\n\n> [!NOTE]\n> Opening your database with Redis Insight in your browser is only available for Essentials databases. For all other databases, [Download and install Redis Insight](#ri-app) on your computer.\n\nIf Redis Insight on Redis Cloud is available for your database, select **Launch Redis Insight web** from the connection wizard to open it.\n\n![Launch Redis Insight web from the Connection Wizard.](https://redis.io/docs/latest/images/rc/rc-ri-wizard-launch.png)\n\nYou can also select **Launch** from the database page under **View and manage data with Redis Insight** to open Redis Insight in your browser.\n\n![Launch Redis Insight web from the database page.](https://redis.io/docs/latest/images/rc/rc-ri-open.png)\n\nRedis Insight will open in a new tab. \n\nThis browser-based version of Redis Insight has a subset of the features of Redis Insight. For more information, see [Open with Redis Insight on Redis Cloud](https://redis.io/docs/latest/operate/rc/databases/connect/insight-cloud).\n\n#### Install and open on your computer {#ri-app}\n\n1. If you haven't downloaded Redis Insight, select **Download** under **Redis Insight** in the Connection wizard to download it. \n\n1. [Install Redis Insight](https://redis.io/docs/latest/develop/tools/insight).\n\n1. Once installed, select **Open with Redis Insight**.\n\n1. A pop-up asks if you wish to open the link with Redis Insight. Select **Open Redis Insight** to connect to your database with Redis Insight.\n\nIf you get an error when connecting with Redis Insight, [manually connect to your database](https://redis.io/docs/latest/develop/tools/insight) from Redis Insight.\n\nYou can use Redis Insight to view your data, run Redis commands, and analyze database performance. See the [Redis Insight docs](https://redis.io/docs/latest/develop/tools/insight) for more info."},{"id":"using-redis-client","title":"Redis client","role":"content","text":"A Redis client is a software library or tool that enables applications to interact with a Redis server. Each client has its own syntax and installation process. For help with a specific client, see the client's documentation.\n\nThe connection wizard provides code snippets to connect to your database with the following programming languages:\n\n- .NET using [StackExchange.Redis/NRedisStack](https://redis.io/docs/latest/develop/clients/dotnet)\n- node.js using [node-redis](https://redis.io/docs/latest/develop/clients/nodejs)\n- Python using [redis-py](https://redis.io/docs/latest/develop/clients/redis-py)\n- Java using [Jedis](https://redis.io/docs/latest/develop/clients/jedis) and [Lettuce](https://redis.io/docs/latest/develop/clients/lettuce)\n- Go using [go-redis](https://redis.io/docs/latest/develop/clients/go)\n- PHP using [Predis](https://redis.io/docs/latest/develop/clients/php)\n\n![The connection wizard clients.](https://redis.io/docs/latest/images/rc/connection-wizard-clients.png)\n\nIf the username and password are not already filled in, replace `<username>` and `<password>` with your username and password.\n\nSee [Clients](https://redis.io/docs/latest/develop/clients) to learn how to connect with the official Redis clients.\n\n> [!NOTE]\n> We recommend using the [dynamic endpoints](#static-and-dynamic-endpoints) for your application so that you can migrate your database endpoints to a different database in the future without any code changes.  See [Redirect dynamic endpoints](https://redis.io/docs/latest/operate/rc/databases/redirect-endpoints) for more information.\n\n#### redis-cli {#using-rediscli}\n\nThe [`redis-cli`](https://redis.io/docs/latest/develop/tools/cli) utility is installed when you install Redis.  It provides a command-line interface that lets you work with your database using core [Redis commands](/content/commands).\n\nTo run `redis-cli`, [install Redis](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack) on your machine. After it's installed, copy the `redis-cli` command under **Redis CLI** in the connection wizard and enter it into your terminal. If the username and password are not already filled in, replace `<username>` and `<password>` with your username and password.\n\nIf you only need the Redis CLI (`redis-cli`) and not the full Redis Open Source distribution, you can [install the standalone `redis-cli` binary](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/install-redis-cli) on Linux or macOS.\n\nSee [Redis CLI](https://redis.io/docs/latest/develop/tools/cli) to learn how to use `redis-cli`."},{"id":"more-info","title":"More info","role":"content","text":"- [Connect your application](https://redis.io/docs/latest/develop/clients)\n- [Connect with TLS](https://redis.io/docs/latest/operate/rc/security/database-security/tls-ssl#connect-over-tls)\n- [Default user](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/default-user)\n- [Role-based access control](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/role-based-access-control)"}],"examples":[{"id":"database-endpoints-ex0","language":"text","code":"redis-12345.c12345.us-east-1-mz.ec2.cloud.rlrcp.com","section_id":"database-endpoints"},{"id":"database-endpoints-ex1","language":"text","code":"horse-battery-staple-12345.db.redis.io","section_id":"database-endpoints"}]}
{"schema_version":2,"id":"operate/rc/databases/connect/insight-cloud","title":"Use Redis Insight on Redis Cloud","url":"https://redis.io/docs/latest/operate/rc/databases/connect/insight-cloud/","summary":"Shows how to open your database in a browser-based version of Redis Insight and lists the features that are available.","tags":["docs","operate","rc","redisinsight"],"last_updated":"2026-10-05T10:59:47+01:00","page_type":"content","content_hash":"46af795dfd2c1f2e52b6ccba3284af59157bd16cf5731931e5c20b813e1c8390","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Redis Insight](https://redis.io/docs/latest/develop/tools/insight) is a free Redis GUI that lets you visualize your Redis data and learn more about Redis.\n\nYou can either [install Redis Insight](https://redis.io/docs/latest/develop/tools/insight) on your computer, or you can open your database in Redis Insight directly on Redis Cloud.\n\n> [!NOTE]\n> Opening your database with Redis Insight in your browser is only available for Essentials databases. For all other databases, [install Redis Insight](https://redis.io/docs/latest/develop/tools/insight) on your computer and [open Redis Insight](https://redis.io/docs/latest/operate/rc/databases/connect#ri-app) from the database page.\n\nTo open your database with Redis Insight on Redis Cloud, select **Open with Redis Insight** on the [database screen](https://redis.io/docs/latest/operate/rc/databases/view-edit-database).\n\n![Open with Redis Insight](https://redis.io/docs/latest/images/rc/rc-ri-open.png)\n\nRedis Insight will open in a new tab. \n\nThis browser-based version of Redis Insight has a subset of the features of Redis Insight. For other Redis Insight features, [install Redis Insight](https://redis.io/docs/latest/develop/tools/insight) on your computer and [open Redis Insight](https://redis.io/docs/latest/operate/rc/databases/connect#ri-app) from the database page."},{"id":"browse","title":"Browse","role":"content","text":"The **Browse** tab lets you browse, filter, and visualize your Redis data structures.\n\n- Create, read, update, and delete lists, hashes, strings, sets, sorted sets, streams, and [JSON](https://redis.io/docs/latest/develop/data-types/json)\n- Filter keys by key name or pattern, and by key type\n- Group keys according to their namespaces\n\n    ![Keys in a database grouped by namespace.](https://redis.io/docs/latest/images/rc/rc-ri-browser-group.png)\n\n- View, validate, and manage your key values in a human-readable format using formatters that prettify and highlight data in different formats (for example, Unicode, JSON, MessagePack, HEX, and ASCII)\n\n    ![Human-readable view of a hash key.](https://redis.io/docs/latest/images/rc/rc-ri-browser-view.png)\n\n- Search by key values using your [search indexes](https://redis.io/docs/latest/develop/ai/search-and-query)\n\n    ![Search for keys using a search index.](https://redis.io/docs/latest/images/rc/rc-ri-browser-search.png)\n\nIf you don't have any Redis data yet, you can select **Load sample data** to add sample data into your database.\n\n![Load Sample Data button](https://redis.io/docs/latest/images/rc/rc-ri-load-data.png)"},{"id":"cli-and-command-helper","title":"CLI and Command Helper","role":"content","text":"The **CLI** lets you run Redis commands directly. \n\n![The CLI and command helper](https://redis.io/docs/latest/images/rc/rc-ri-cli.png)\n\nThe CLI integrates with a **Command Helper** that lets you search and read information about Redis commands."},{"id":"workbench","title":"Workbench","role":"content","text":"The **Workbench** is an advanced command line interface with intelligent command auto-complete and complex data visualization support.\n\n![The Workbench](https://redis.io/docs/latest/images/rc/rc-ri-workbench.png)"},{"id":"insights","title":"Insights","role":"content","text":"The **Insights** panel provides interactive tutorials and recommendations to help you optimize your database performance and memory usage.\n\n![The Insights panel](https://redis.io/docs/latest/images/rc/rc-ri-insights.png)\n\nFrom the **Workbench** of an empty database, select **Explore** to view the **Insights** panel.\n\n![The Explore button](https://redis.io/docs/latest/images/rc/rc-ri-explore-button.png)\n\nYou can also select the **Insights** button in the top-right corner to view the **Insights** panel.\n\n![The Insights button](https://redis.io/docs/latest/images/rc/rc-ri-insights-button.png)"}],"examples":[]}
{"schema_version":2,"id":"operate/rc/databases/create-database/create-essentials-database","title":"Create an Essentials database","url":"https://redis.io/docs/latest/operate/rc/databases/create-database/create-essentials-database/","summary":"Shows how to create an Essentials database.","aliases":["/operate/rc/subscriptions/create-fixed-subscription"],"tags":["docs","operate","rc"],"last_updated":"2026-10-01T15:21:31+01:00","page_type":"content","content_hash":"6fd264dae0e237d3cd60d13a12d669837e27be22b8f94004b1890ba6698ad06e","sections":[{"id":"content","title":"Content","role":"content","text":"Redis Cloud Essentials is cost-efficient and designed for low-throughput scenarios. You can quickly scale up your Essentials database as your application grows.\n\nBefore creating a Redis Cloud database, you need to [create an account](https://redis.io/docs/latest/operate/rc/rc-quickstart).\n\nTo create a database in your Redis Cloud account:\n\n1. Sign in to the [Redis Cloud console](https://cloud.redis.io).\n\n2. Select the **New database** button.\n\n    ![The New Database button creates a new database.](https://redis.io/docs/latest/images/rc/button-database-new.png)\n\n    This displays the **Create database** screen. \n\n3. Select the type of [subscription](https://redis.io/docs/latest/operate/rc/subscriptions) you need. For this guide, select **Essentials**.\n\n    ![The Subscription selection panel with Essentials selected.](https://redis.io/docs/latest/images/rc/create-database-subscription-essentials.png)\n\n    > [!NOTE]\n    > This guide shows how to create a paid Essentials database.\n    > - If you want to create a free Essentials database, see [Create a free database](https://redis.io/docs/latest/operate/rc/rc-quickstart). You can only have one free database per account.\n    > - If you'd rather create a Pro database, see [Create a Pro database with a new subscription](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new).\n    > - If you already have a Pro subscription and want to add a database to it, see [Create a Pro database in an existing subscription](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-existing).\n    >\n    \n    After you select **Essentials**, the rest of the database details will appear.\n\n    ![The database name, cloud vendor, version, region, type, and durability settings.](https://redis.io/docs/latest/images/rc/create-database-essentials-cloud-vendor.png)\n\n1. Redis will generate a database name for you. If you want to change it, you can do so in the **Name** field.  \n\n1. Choose a **Cloud vendor** for your database from the list. You can choose between **Amazon Web Services (AWS)**, **Google Cloud**, and **Microsoft Azure** for the Cloud Vendor. \n\n    ![The list of available cloud vendors.](https://redis.io/docs/latest/images/rc/create-database-essentials-cloud-vendor-list.png)\n\n1. Choose a **Region** from the list. See [Supported regions](https://redis.io/docs/latest/operate/rc/supported-regions) for a list of supported regions by cloud vendor.\n\n1. Select the **Database version** you want to use.\n\n1. The **Type** of database controls the protocol and advanced capabilities. Leave this as **Redis** unless you have a legacy application that uses **Memcached**.\n\n1. Select your desired plan.\n\n    ![Available Essentials plans.](https://redis.io/docs/latest/images/rc/subscription-new-fixed-tiers.png)\n\n    The plan determines the size of your database and other limits. For a comparison of available plans, see [Redis Cloud Essentials plans](https://redis.io/docs/latest/operate/rc/subscriptions/view-essentials-subscription/essentials-plan-details).\n\n1. Select **RAM** or **RAM + SSD (Flex)** to choose between a database that uses only RAM or one that uses both RAM and Flash memory. \n\n    Choose Flex for larger datasets and cost-efficient scale when you can tolerate single-digit millisecond latency for warm data. Choose RAM when you need the absolute lowest latency for all data. To learn more about Flex, see [Create a Flex database](https://redis.io/docs/latest/operate/rc/databases/create-database/create-flex-database).\n\n1. Choose your **High availability (replication)** settings.\n\n    Redis Cloud supports the following high availability settings:\n\n    - **None**: You will have a single copy of your database without replication.\n    - **Single-Zone**: Your database will have a primary and a replica located in the same cloud zone. If anything happens to the primary, the replica takes over and becomes the new primary.\n    - **Multi-Zone**: The primary and its replicas are stored in different zones. This means that your database can remain online even if an entire zone becomes unavailable.\n\n    See [High availability](https://redis.io/docs/latest/operate/rc/databases/configuration/high-availability) for more information about these settings.\n\n1. Choose your **Data persistence** settings from the list.\n\n    Redis Cloud supports the following data persistence options:\n\n    - An **Append-Only File** maintains a record (sometimes called a _redo log_ or _journal_) of write operations.  This allows the data to be restored by using the record to reconstruct the database up to the point of failure. For Essentials databases, Redis updates the Append-Only file every second.\n\n    - A **Snapshot** is a copy of the in-memory database, taken at periodic intervals (one, six, or twelve hours). You can restore data to the snapshot's point in time. \n    \n    See [Data persistence](https://redis.io/docs/latest/operate/rc/databases/configuration/data-persistence) for more information about these settings.\n\n1.  Enter your payment details.\n\n    If you haven't previously entered a payment method, use the **Add Credit Card** button to add one.\n\n    ![The Add credit card icon.](https://redis.io/docs/latest/images/rc/icon-add.png)\n\n    If you have not already added a business address to your account, you must enter one. Redis uses your business address for communication, invoicing, and tax purposes.\n\n![The Add credit card screen before a business address has already been added.](https://redis.io/docs/latest/images/rc/billing-add-first-card.png)\n\nIf you already have a business address, you don't need to enter one here. See [Account settings](https://redis.io/docs/latest/operate/rc/accounts/account-settings) to learn how to update your business address.\n\n![The Add credit card screen after a business address has already been added.](https://redis.io/docs/latest/images/rc/billing-add-credit-card.png)\n\nIf the card's billing address is the same as your account's business address, select **Same as business address** to fill the billing address with your business address details.\n\n1. Select **Confirm & pay** to create your database.\n\n![Select Confirm & Pay to create your new database.](https://redis.io/docs/latest/images/rc/button-create-db-confirm-pay.png)\n\nWhen you create your database, there's a brief pause while your request is processed and then the **Database details** page appears.\n\nYou can now [connect to your database](https://redis.io/docs/latest/operate/rc/databases/connect) and start working with Redis."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/databases/create-database/create-flex-database","title":"Create a Flex database","url":"https://redis.io/docs/latest/operate/rc/databases/create-database/create-flex-database/","summary":"Shows how to create a Flex database on Redis Cloud and describes the best use cases for Flex.","tags":["docs","operate","rc"],"last_updated":"2026-10-05T09:52:50-05:00","page_type":"content","content_hash":"6845361eb258639f40535d722ca95a5ac4115c578c2415f6fa68e0473c19c79a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Flex allows your dataset to span both high-performance RAM and cost-efficient dedicated Flash memory. Flex automatically manages data placement between the two tiers, keeping frequently accessed (“hot”) data in RAM for sub-millisecond latency, while moving less active (“warm”) data to Flash to optimize capacity and cost. This dual memory architecture delivers predictable performance at scale, enabling larger datasets without compromising speed or operational simplicity.\n\nFlex databases are currently compatible with most existing Redis applications, except for applications that use Time Series. [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) is available on Flex databases on Redis Cloud Pro as a beta feature; see [Redis Search on Flex](#redis-search-on-flex-beta).\n\nFlex is available on both Redis Cloud Essentials and Redis Cloud Pro."},{"id":"when-to-use-flex","title":"When to use Flex","role":"content","text":"Flex is ideal for workloads that demand large-scale, low-latency data access with the flexibility to optimize cost and performance.\n\nConsider Flex when you need to:\n\n- Run Redis at terabyte scale while maintaining high throughput and sub-10 ms latency.\n- Power real-time feature stores for machine learning applications such as fraud detection, recommendation systems, and personalization engines.\n- Operate large distributed caches that require elastic scaling and consistent performance under heavy load.\n- Optimize infrastructure cost by combining high-speed RAM with cost-efficient Flash storage through Flex's automatic data tiering.\n\nFlex is **not** a durable data store. It is designed for performance, elasticity, and scalability, not for long-term data persistence. While Flex can temporarily retain data in memory or Flash, it should not be used as a primary system of record or persistent storage layer.\n\nFor workloads that require durability and recovery across restarts or failures, use Redis Cloud's [Data persistence](https://redis.io/docs/latest/operate/rc/databases/configuration/data-persistence) features."},{"id":"best-practices-and-limitations","title":"Best practices and limitations","role":"content","text":""},{"id":"redis-search-on-flex-beta","title":"Redis Search on Flex (Beta)","role":"content","text":"[Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) on Flex databases is available as a beta feature on Redis Cloud Pro. It isn't available on Redis Cloud Essentials.\n\nTo use it, enable the beta flag for Redis Search on Flex in the Redis Cloud console for your Redis Cloud Pro subscription, then create a Flex database with a Redis Search index on tiered storage.\n\nThe beta supports:\n\n- HASH documents\n- `TEXT` fields, including prefix, infix, suffix, wildcard, and fuzzy matching\n- `TAG` fields\n- `VECTOR` fields with `HNSW` and `FLAT` indexes\n- Loading fields from the keyspace with `SORTBY` and `RETURN`\n- High availability, persistence, backup, and upgrades\n\nJSON documents, `NUMERIC` and `GEO` fields, `FT.AGGREGATE`, `FT.HYBRID`, and background indexing aren't yet available for Redis Search on Flex, even though they're available for Redis Search on Redis Software today. As a beta feature, the supported feature set will continue to expand ahead of general availability.\n\nTerraform support for Redis Search on Flex is available at Preview level."},{"id":"ram-percentage","title":"RAM percentage","role":"content","text":"On Redis Cloud Essentials, the RAM percentage for Flex plans is fixed at 10% and can't be changed. On Redis Cloud Pro, you can select a RAM percentage between 10% and 50% when you create your database. See [Choosing the right RAM ratio](https://support.redislabs.com/hc/en-us/articles/36437338181522-Redis-Flex-V2-Choosing-the-Right-RAM-Ratio-and-Troubleshooting-Performance) for guidance on selecting a RAM percentage."},{"id":"key-and-value-sizes","title":"Key and value sizes","role":"content","text":"Similar to best practices for in-memory Redis, we recommend storing relatively small keys and values in Flex. If possible, avoid large objects (>10 KB).\n\nDepending on your application's traffic pattern, using large objects might decrease performance, since the entire value is moved from RAM to Flash storage and vice versa.\n\nFlex databases cannot store keys or values larger than 4 GB in Flash storage. Keys or values larger than 4 GB are stored in RAM only, and will generate warnings like the following in the Redis logs:\n\n[code example]"},{"id":"create-a-flex-database","title":"Create a Flex database","role":"content","text":""},{"id":"redis-cloud-essentials","title":"Redis Cloud Essentials","role":"content","text":"Before creating a Redis Cloud database, you need to [create an account](https://redis.io/docs/latest/operate/rc/rc-quickstart).\n\nTo create a database in your Redis Cloud account:\n\n1. Sign in to the [Redis Cloud console](https://cloud.redis.io).\n\n2. Select the **New database** button.\n\n    ![The New Database button creates a new database.](https://redis.io/docs/latest/images/rc/button-database-new.png)\n\n    This displays the **Create database** screen. \n\n3. Select the type of [subscription](https://redis.io/docs/latest/operate/rc/subscriptions) you need. For this guide, select **Essentials**.\n\n    ![The Subscription selection panel with Essentials selected.](https://redis.io/docs/latest/images/rc/create-database-subscription-essentials.png)\n\n    After you select **Essentials**, the rest of the database details will appear. Select **Flex (RAM + SSD)** to use Flex.\n\n    ![The database name, cloud vendor, version, region, type, and durability settings.](https://redis.io/docs/latest/images/rc/create-database-essentials-cloud-vendor.png)\n\n1. Redis will generate a database name for you. If you want to change it, you can do so in the **Database name** field.  \n\n1. Choose a **Region** on Amazon Web Services for your database. See [Supported regions](https://redis.io/docs/latest/operate/rc/supported-regions) for a list of supported regions by cloud vendor.\n\n1. Select the **Database version** you want to use.\n\n1. Select your desired memory limit. \n\n    ![Available Flex plans.](https://redis.io/docs/latest/images/rc/subscription-new-flex-tiers.png)\n\n    For a comparison of available plans, see [Redis Cloud Essentials plans](https://redis.io/docs/latest/operate/rc/subscriptions/view-essentials-subscription/essentials-plan-details).\n\n    All Flex plans on Redis Cloud Essentials have a default RAM percentage of 10%. \n\n1. Choose your **High availability (replication)** settings from the list. \n\n    Redis Cloud supports the following high availability settings with Flex:\n\n    - **None**: You will have a single copy of your database without replication.\n    - **Single-Zone**: Your database will have a primary and a replica located in the same cloud zone. If anything happens to the primary, the replica takes over and becomes the new primary.\n\n    See [High availability](https://redis.io/docs/latest/operate/rc/databases/configuration/high-availability) for more information about these settings.\n\n1. Choose your **Data persistence** settings from the list.\n\n    Redis Cloud supports the following data persistence options:\n\n    - An **Append-Only File** maintains a record (sometimes called a _redo log_ or _journal_) of write operations.  This allows the data to be restored by using the record to reconstruct the database up to the point of failure. For Essentials databases, Redis updates the Append-Only file every second.\n\n    - A **Snapshot** is a copy of the in-memory database, taken at periodic intervals (one, six, or twelve hours). You can restore data to the snapshot's point in time. \n    \n    See [Data persistence](https://redis.io/docs/latest/operate/rc/databases/configuration/data-persistence) for more information about these settings.\n\n1. Select the **Database version** you want to use.\n\n1.  Enter your payment details.\n\n    If you haven't previously entered a payment method, use the **Add Credit Card** button to add one.\n\n    ![The Add credit card icon.](https://redis.io/docs/latest/images/rc/icon-add.png)\n\n    If you have not already added a business address to your account, you must enter one. Redis uses your business address for communication, invoicing, and tax purposes.\n\n![The Add credit card screen before a business address has already been added.](https://redis.io/docs/latest/images/rc/billing-add-first-card.png)\n\nIf you already have a business address, you don't need to enter one here. See [Account settings](https://redis.io/docs/latest/operate/rc/accounts/account-settings) to learn how to update your business address.\n\n![The Add credit card screen after a business address has already been added.](https://redis.io/docs/latest/images/rc/billing-add-credit-card.png)\n\nIf the card's billing address is the same as your account's business address, select **Same as business address** to fill the billing address with your business address details.\n\n1. Select **Confirm & pay** to create your database.\n\n![Select Confirm & Pay to create your new database.](https://redis.io/docs/latest/images/rc/button-create-db-confirm-pay.png)\n\nWhen you create your database, there's a brief pause while your request is processed and then the **Database details** page appears."},{"id":"redis-cloud-pro","title":"Redis Cloud Pro","role":"content","text":"To create a Flex database on Redis Cloud Pro, [create a new Pro database with custom settings](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new#custom-settings). \n\nIn the **Advanced options** of the **Setup** tab, select **Redis Flex**.\n\n![The Flex setting selected.](https://redis.io/docs/latest/images/rc/pro-flex-on.png)\n\nDuring the **Sizing** step, when you are provisioning your databases, you can select the RAM percentage for your database. The default is 20%, but you can select a percentage between 10% and 50%. Lower RAM percentages reduce cost but may increase latency, while higher RAM percentages improve throughput and latency at higher cost. See [Choosing the right RAM ratio](https://support.redislabs.com/hc/en-us/articles/36437338181522-Redis-Flex-V2-Choosing-the-Right-RAM-Ratio-and-Troubleshooting-Performance) to choose the right RAM percentage for your database.\n\n![The RAM percentage setting.](https://redis.io/docs/latest/images/rc/pro-flex-ram-percentage.png)\n\nContinue with the instructions to [create your database](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new#custom-settings)."},{"id":"troubleshooting","title":"Troubleshooting","role":"errors","text":"See [Troubleshooting Redis Flex performance questions](https://support.redislabs.com/hc/en-us/articles/36437338181522-Redis-Flex-V2-Choosing-the-Right-RAM-Ratio-and-Troubleshooting-Performance#h_01KV6F382KNM8N0W00DFEHPY3S) on the Redis Support knowledge base to learn how to troubleshoot Redis Flex databases."}],"examples":[{"id":"key-and-value-sizes-ex0","language":"plaintext","code":"# WARNING: key too big for disk driver, size: 4703717276, key: subactinfo:htable","section_id":"key-and-value-sizes"}]}
{"schema_version":2,"id":"operate/rc/databases/create-database/create-free-database","title":"Create a free database","url":"https://redis.io/docs/latest/operate/rc/databases/create-database/create-free-database/","summary":"Shows how to create a free database.","tags":["docs","operate","rc"],"last_updated":"2026-10-01T15:21:31+01:00","page_type":"content","content_hash":"ac183fe8a62db1ee7f12fe3a3f103034ba62b83a786af50841d649297fcfa106","sections":[{"id":"content","title":"Content","role":"content","text":"Free databases are perfect for learning and exploring Redis. You get 30 MB of space for you to learn Redis concepts and develop application prototypes.\n\n> [!NOTE]\n> You can only have one free database per account. If you already have a free database, you can [delete it](https://redis.io/docs/latest/operate/rc/databases/delete-database) or [upgrade it to a paid Essentials plan](https://redis.io/docs/latest/operate/rc/subscriptions/view-essentials-subscription#upgrade-plan) before creating a new one.\n\nBefore creating a Redis Cloud database, you need to [create an account](https://redis.io/docs/latest/operate/rc/rc-quickstart).\n\nTo create a database in your Redis Cloud account:\n\n1. Sign in to the [Redis Cloud console](https://cloud.redis.io).\n\n2. Select the **New database** button.\n\n    ![The New Database button creates a new database.](https://redis.io/docs/latest/images/rc/button-database-new.png)\n\n    This displays the **Create database** screen. \n\n3. Select the type of [subscription](https://redis.io/docs/latest/operate/rc/subscriptions) you need. For this guide, select **Try 30 MB for free**.\n\n    ![The Subscription selection panel with Free selected.](https://redis.io/docs/latest/images/rc/create-database-subscription-free.png)\n\n    The rest of the database settings will appear.\n\n    ![The database name, cloud vendor, and region settings.](https://redis.io/docs/latest/images/rc/create-database-free-settings.png)\n\n4. Redis will generate a database name for you. If you want to change it, you can do so in the **Database name** field. \n\n1. Select the **Database version** you want to use.\n\n1. Choose your **Cloud vendor** and **Region**. You can choose between **Amazon Web Services (AWS)**, **Google Cloud**, and **Microsoft Azure** for the Cloud Vendor. \n\n    ![The list of available cloud vendors.](https://redis.io/docs/latest/images/rc/create-database-essentials-cloud-vendor-list.png)\n\n    See [Supported regions](https://redis.io/docs/latest/operate/rc/supported-regions) for a list of supported regions by cloud vendor.\n\n6. Select **Create database**.\n\n    ![Select the Create database button to create your new database.](https://redis.io/docs/latest/images/rc/button-create-db.png)\n\n    When you create your database, there's a brief pause while your request is processed and then the **Database details** page appears.\n\nYou can now [connect to your database](https://redis.io/docs/latest/operate/rc/databases/connect) and start working with Redis. Once your app is ready to scale up, you can [upgrade to a paid Essentials plan](https://redis.io/docs/latest/operate/rc/subscriptions/view-essentials-subscription#upgrade-plan) at any time."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/databases/create-database/create-pro-database-existing","title":"Create a Pro database in an existing subscription","url":"https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-existing/","summary":"Shows how to create a Pro database in an existing subscription","tags":["docs","operate","rc"],"last_updated":"2026-10-01T15:21:31+01:00","page_type":"content","content_hash":"da554b2a6d11e4d8a949df0569b888b7ecf3ce3f71600d2843fc9f979c31d34e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> This guide shows how to create a Pro database in an existing subscription.\n> - If you don't yet have a Pro subscription, see [Create a Pro database with a new subscription](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new).\n> - If you'd rather create an Essentials database, see [Create an Essentials database](https://redis.io/docs/latest/operate/rc/databases/create-database/create-essentials-database).\n\nTo create a database in an already existing Pro subscription: \n\n1. Sign in to the [Redis Cloud console](https://cloud.redis.io).\n\n2. Select the **New database** button.\n\n    ![The New Database button creates a new database.](https://redis.io/docs/latest/images/rc/button-database-new.png)\n\n    This displays the **Create database** screen.\n\n1. Select the type of [subscription](https://redis.io/docs/latest/operate/rc/subscriptions) you need. For this guide, select **Existing subscription**, and then select your existing Pro subscription from the list.\n\n    ![The Subscription selection panel with Pro selected and an existing subscription selected.](https://redis.io/docs/latest/images/rc/create-database-subscription-pro-existing.png)\n\nAfter you select your existing subscription from the list, select **Continue** to go to the **New database** page.\n\nYou can also select your subscription from the [subscription list](https://redis.io/docs/latest/operate/rc/subscriptions/view-pro-subscription) and select **Create database in this subscription**.\n\n![The Create database in this subscription button.](https://redis.io/docs/latest/images/rc/button-add-new-to-pro.png)\n\nThe **New database** page is divided into sections, each dedicated to a specific category of settings. The following sections provide more details.\n\nWhen you've configured your new database, click the **Activate database** button to create and activate it.\n\n![Use the Activate database button to create and activate your database.](https://redis.io/docs/latest/images/rc/button-database-activate.png)"},{"id":"general-section","title":"General section","role":"content","text":"The **General** section defines basic properties about your database.\n\n![The general section of the New Database screen.](https://redis.io/docs/latest/images/rc/database-new-flexible.png)\n\nThe available settings vary according to your subscription plan:\n\n| Setting name              | Description                                                                                                                                                                                                                                                                                                       |\n|:--------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| **Subscription**          | Read-only description of your Pro subscription, including cloud provider and region                                                                                                                                                                                                                              |\n| **Database name**         | A name for your database (_required_)                                                                                                                                                                                                                                                                             |\n| **Database port**         | Automatically or manually assigns a database port (range: 10000-19999).  You cannot assign a port that is reserved or already in use.                                                                                                                                                                                      |\n| **Database version**         | The Redis version of your database. We recommend you choose the latest available version.                                                                                                                                                                                      |\n| **Type**                  | Controls advanced database capabilities and protocol.  Supported values include _Redis_ and _Memcached_                                                                       |\n| **Advanced capabilities** | [Advanced features and data types](https://redis.io/docs/latest/operate/rc/databases/configuration/advanced-capabilities) used by the database. Starting with Redis 8.0, all advanced capabilities are included in Redis by default. |\n| **Query performance factor** | *(Search and query databases on Redis 7.2 or later only)* Adds additional compute power to process your query and vector search workloads and boost your queries per second. See [Search and query sizing](https://redis.io/docs/latest/operate/rc/databases/configuration/advanced-capabilities#search-and-query-sizing) for more information. |\n| **Supported Protocol(s)** | Choose between RESP2 and RESP3 _(Redis 7.2 only)_. See [Redis serialization protocol](https://redis.io/docs/latest/develop/reference/protocol-spec#resp-versions) for details                                                                                                                                                   |"},{"id":"performance-section","title":"Performance section","role":"performance","text":"The **Performance** section lets you manage the maximum size, throughput, and hashing policy for a database.\n\n![Use the Performance section to control the size, throughput, and hashing policy for a database.](https://redis.io/docs/latest/images/rc/database-new-flexible-scalability.png)\n\n| Setting name        | Description                                                                                                                                                                                                                                                                                                                                   |\n|:--------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| **Dataset size**    | Maximum size (in GB) for your dataset. See [Dataset size](https://redis.io/docs/latest/operate/rc/databases/configuration/sizing#dataset-size) for sizing considerations. <br/> Databases with Search and query have specific size requirements, see [Search and query sizing](https://redis.io/docs/latest/operate/rc/databases/configuration/advanced-capabilities#search-and-query-sizing) for more information. |\n| **Throughput**      | Defines throughput in terms of maximum operations per second for the database. See [Throughput](https://redis.io/docs/latest/operate/rc/databases/configuration/sizing#throughput) for more info. <br/> Databases with Search and query have specific throughput requirements, see [Search and query sizing](https://redis.io/docs/latest/operate/rc/databases/configuration/advanced-capabilities#search-and-query-sizing) for more information. |\n| **High availability**    | Indicates whether a replica copy of the database is maintained in case the primary database becomes unavailable. See [High availability](https://redis.io/docs/latest/operate/rc/databases/configuration/high-availability) for more info|\n| **Query performance factor** | *(Search and query databases on Redis 7.2 or later)* Adds additional compute power to process your query and vector search workloads and boost your queries per second. See [Search and query sizing](https://redis.io/docs/latest/operate/rc/databases/configuration/advanced-capabilities#search-and-query-sizing) for more information. |\n| **Hashing policy**  | Defines the [hashing policy](https://redis.io/docs/latest/operate/rc/databases/configuration/clustering#manage-the-hashing-policy).  |\n| **OSS Cluster API** | Activates the [OSS Cluster API](https://redis.io/docs/latest/operate/rc/databases/configuration/clustering#oss-cluster-api) for a database.<br/> After you select OSS Cluster API, you can select **Use external endpoint** if you want to use the external endpoint for the database. Selecting **Use external endpoint** will block the private endpoint for this database. |"},{"id":"durability-section","title":"Durability section","role":"content","text":"The **Durability** section helps you keep your database (and your data) available when problems occur.\n\n![Use the Durability settings to keep your database (and data) available when problems occur.](https://redis.io/docs/latest/images/rc/database-new-flexible-durability.png)\n\n\n| Setting name             | Description                                                                                                                                                                |\n|:-------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| **Data persistence**     | Defines the data persistence policy, if any. See [Data persistence](https://redis.io/docs/latest/operate/rc/databases/configuration/data-persistence).            |\n| **Data eviction policy** | Configures which [policy](https://redis.io/docs/latest/operate/rc/databases/configuration/data-eviction-policies) is applied when your database reaches its memory limit.              |\n| **Remote backup**        | When enabled, identifies a location and interval for [data backups](https://redis.io/docs/latest/operate/rc/databases/back-up-data). |\n| **Active-Passive Redis** | When enabled, identifies a path to the linked database. See [Migrate data](https://redis.io/docs/latest/operate/rc/databases/migrate-databases) for more information.           |"},{"id":"tags-section","title":"Tags section","role":"content","text":"The **Tags** section lets you add [tags](https://redis.io/docs/latest/operate/rc/databases/tag-database) to the database.\n\n![Use the Tag settings to add tags to the database.](https://redis.io/docs/latest/images/rc/database-new-tags.png)\n\nSelect **Add tag** to add a tag.\n\n![The Add tag button.](https://redis.io/docs/latest/images/rc/tags-button-add-tag.png)\n\nYou can select the tag's **Key** and **Value** from the suggestions, or you can enter your own.\n\n![The New tag fields.](https://redis.io/docs/latest/images/rc/tags-new-tag.png)\n\nA valid tag must follow these guidelines:\n- Tag keys must be between 1 and 64 characters long, and values must be at least 1 character long.\n- Tags can only have lowercase letters, numbers, spaces, and these special characters: `-`, `_`, `.`, `+`, `@`, and `:`.\n\nAfter you add your first tag, you can:\n\n- Select the **Key** or **Value** field of an existing tag and enter new text to edit it.\n\n- Select **Delete** next to a tag to delete it.\n\n    ![Delete button.](https://redis.io/docs/latest/images/rc/icon-delete-lb.png)\n\n- Select **Add additional tag** to add another tag.\n\n    ![The Add additional tag button.](https://redis.io/docs/latest/images/rc/tags-button-add-additional-tag.png)"},{"id":"security-section","title":"Security section","role":"security","text":"The **Security** section helps you control access to your database.\n\n![Use the Security settings to control access to your database.](https://redis.io/docs/latest/images/rc/database-new-flexible-security.png)\n\n\n| Setting name                       | Description                                                                                                                                                                           |\n|:-----------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| **Default user**                   | When enabled, permits access using the `default` username and a simple password (see [Default User](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/default-user)). Turn on [Role-based access control](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/role-based-access-control) to turn off this setting.              |\n| **Default user password**                 | Password assigned to the database when created.                                                                                                                                        |  \n| **CIDR allow list**                | [Allow list](https://redis.io/docs/latest/operate/rc/security/cidr-whitelist) of IP addresses/security groups permitted to access the database |\n| **Transport layer security (TLS)** | Enables [transport layer security](https://redis.io/docs/latest/operate/rc/security/database-security/tls-ssl) (TLS) encryption for database access.          |"},{"id":"alerts-section","title":"Alerts section","role":"content","text":"The **Alerts** section defines notification emails sent to your account and the conditions that trigger them.\n\n![The Alerts section defines the notification emails and their triggering conditions.](https://redis.io/docs/latest/images/rc/database-new-flexible-alerts.png)\n\nThe available alerts vary according to the plan type. See [Configure alerts](https://redis.io/docs/latest/operate/rc/databases/monitor-performance#configure-metric-alerts) for more information."},{"id":"billing-unit-types","title":"Billing unit types","role":"content","text":"The Redis Billing Unit types associated with your Pro subscription depend on your database memory size and throughput requirements.\n\n| Billing unit type | Capacity (Memory/Throughput) |\n|:------------|:----------|\n| Pico | 100MB / 100 ops/sec |\n| Nano | 500MB / 500 ops/sec |\n| Micro | 1GB / 1K ops/sec |\n| High-throughput | 2.5GB / 25K ops/sec |\n| Small | 12.5GB / 12.5K ops/sec |\n| Large | 25GB  / 25K ops/sec |\n| Very large<sup>[1](#table-note-1)</sup> | 50GB / 5K ops/sec |\n| XLarge<sup>[2](#table-note-2)</sup> | 50GB / 10K ops/sec |\n| Flex-10<sup>[3](#table-note-3)</sup> | 50GB / 5K ops/sec |\n| Flex-20<sup>[3](#table-note-3)</sup> | 50GB / 10K ops/sec |\n| Flex-30<sup>[3](#table-note-3)</sup> | 50GB / 15K ops/sec |\n| Flex-40<sup>[3](#table-note-3)</sup> | 50GB / 20K ops/sec |\n| Flex-50<sup>[3](#table-note-3)</sup> | 50GB / 25K ops/sec |\n\n1. <a name=\"table-note-1\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>Used for databases with Auto Tiering before Redis 7.2.\n\n2. <a name=\"table-note-2\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>Used for hosted databases with Auto Tiering between Redis 7.2 and Redis 8.2. \n\n3. <a name=\"table-note-3\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>Used for Redis Flex databases for Redis 8.2 or later. Available Flex billing units depend on the RAM percentage you set for your database.\n\nPrices vary according to the cloud provider and region.  Minimum prices apply."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/databases/create-database/create-pro-database-new","title":"Create a Pro database with a new subscription","url":"https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new/","summary":"Shows how to create a Pro database with a new subscription","tags":["docs","operate","rc"],"last_updated":"2026-10-13T13:09:15-07:00","page_type":"content","content_hash":"cd089ad385679d2c09fd361413ca6407e589dd887a6748ed844be579830014fc","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Cloud Pro supports more databases, larger databases, greater throughput, and unlimited connections compared to Redis Cloud Essentials. Redis Cloud Pro databases are perfect for teams building mission-critical systems in the cloud.\n\nBefore creating a Redis Cloud database, you need to [create an account](https://redis.io/docs/latest/operate/rc/rc-quickstart).\n\nTo create a database in your Redis Cloud account:\n\n1. Sign in to the [Redis Cloud console](https://cloud.redis.io).\n\n2. Select the **New database** button.\n\n    ![The New Database button creates a new database.](https://redis.io/docs/latest/images/rc/button-database-new.png)\n\n    This displays the **Create database** screen.\n\n3. Select the type of [subscription](https://redis.io/docs/latest/operate/rc/subscriptions) you need. For this guide, select **Pro**. \n\n    ![The Subscription selection panel with Pro selected.](https://redis.io/docs/latest/images/rc/create-database-subscription-pro-new.png)\n\n    > [!NOTE]\n    > This guide shows how to create a Pro database with a new subscription.\n    > - If you already have a Pro subscription and want to add a database to it, see [Create a Pro database in an existing subscription](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-existing).\n    > - If you'd rather create an Essentials database, see [Create an Essentials database](https://redis.io/docs/latest/operate/rc/databases/create-database/create-essentials-database).\n    >\n    \n\nAfter you select **Pro**, the **Database settings** section will appear.\n\n![The database settings section.](https://redis.io/docs/latest/images/rc/create-pro-db-settings.png)\n\nYou can choose to create your database in one of two ways:\n\n- [**Easy create**](#easy-create) selects the optimal settings for you to get started faster.\n- [**Custom settings**](#custom-settings) lets you select all of the configuration options for your new database."},{"id":"easy-create","title":"Create database with Easy create","role":"content","text":"If you choose to create your database with Easy create:\n\n![The database name, cloud vendor and region settings.](https://redis.io/docs/latest/images/rc/pro-easy-create-vendor.png)\n\n1. Choose a **Cloud Provider** and a **Region**.\n\n1. Enter the following settings for your database:\n\n    ![The Dataset size, throughput, and High availability settings.](https://redis.io/docs/latest/images/rc/pro-easy-create-size-throughput.png)\n\n    | Database&nbsp;setting | Description |\n    |:---------|:-----------|\n    | **Name** | The name for your database. Redis will generate a database name for you, but you can change it at any time.  |\n    | **Dataset size (GB)** | The amount of data for your dataset. See [Dataset size](https://redis.io/docs/latest/operate/rc/databases/configuration/sizing#dataset-size) for sizing considerations. <br/> Databases with Search and query have specific sizing requirements, see [Search and query sizing](https://redis.io/docs/latest/operate/rc/databases/configuration/advanced-capabilities#search-and-query-sizing) for more information. |\n    | **Throughput** | Identifies maximum throughput for the database, which is specified in terms of operations per second (**Ops/sec**). See [Throughput](https://redis.io/docs/latest/operate/rc/databases/configuration/sizing#throughput) for more information. <br/> Databases with Search and query have specific throughput requirements, see [Search and query sizing](https://redis.io/docs/latest/operate/rc/databases/configuration/advanced-capabilities#search-and-query-sizing) for more information. |\n    | **High Availability** | Indicates whether a replica copy of the database is maintained in case the primary database becomes unavailable.  (Warning: doubles memory consumption). See [High Availability](https://redis.io/docs/latest/operate/rc/databases/configuration/high-availability).  |\n\n1. Select **View all settings** to review the database settings that we selected for you.\n\n    ![The optimal database settings.](https://redis.io/docs/latest/images/rc/pro-easy-create-optimal-settings.png)\n\n    If you want to change these settings, select [**Switch to custom settings**](#custom-settings).\n\n1. Enter your payment details.\n\n    If you haven't previously entered a payment method, use the **Add Credit Card** button to add one.\n\n    ![The Add credit card icon.](https://redis.io/docs/latest/images/rc/icon-add.png)\n\n    If you have not already added a business address to your account, you must enter one. Redis uses your business address for communication, invoicing, and tax purposes.\n\n![The Add credit card screen before a business address has already been added.](https://redis.io/docs/latest/images/rc/billing-add-first-card.png)\n\nIf you already have a business address, you don't need to enter one here. See [Account settings](https://redis.io/docs/latest/operate/rc/accounts/account-settings) to learn how to update your business address.\n\n![The Add credit card screen after a business address has already been added.](https://redis.io/docs/latest/images/rc/billing-add-credit-card.png)\n\nIf the card's billing address is the same as your account's business address, select **Same as business address** to fill the billing address with your business address details.\n\n Select **Confirm & pay** to create your database.\n\n![Select Confirm & Pay to create your new database.](https://redis.io/docs/latest/images/rc/button-create-db-confirm-pay.png)\n\nNote that databases are created in the background.  While they are provisioning, you aren't allowed to make changes. This process generally takes 10-15 minutes.\n\nUse the **Database list** to check the status of your databases."},{"id":"custom-settings","title":"Create database with Custom settings","role":"content","text":"![The database settings section, with custom settings selected.](https://redis.io/docs/latest/images/rc/create-pro-db-settings-custom.png)\n\nIf you choose to create your database with custom settings, you need to:\n\n1. Set up the deployment options, including cloud vendor details, high availability settings, and advanced options.\n\n2. Define the database size requirements.\n\n3. Review your choices, provide payment details, and then create your databases.\n\nThe following sections provide more information."},{"id":"set-up-deployment-details","title":"Set up deployment details","role":"content","text":"The **Setup** tab specifies general settings for your Redis deployment.\n\n![The Setup tab of the new Pro Database process.](https://redis.io/docs/latest/images/rc/subscription-new-flexible-tabs-setup.png)\n\nThere are two sections on this tab:\n\n- [General settings](#general-settings) include the cloud provider details and specific configuration options.\n- [Advanced options](#advanced-options) define settings for high availability and security. Configurable settings vary according to cloud provider.\n\n#### General settings {#general-settings}\n\n![The General settings of the Setup tab.](https://redis.io/docs/latest/images/rc/subscription-new-flexible-setup-general.png)\n\nIn the General settings of the **Setup** tab, you need to:\n\n1. Select the public **Cloud vendor** to deploy your databases.\n\n1. Select the **Region** where you want to deploy your database. \n\n    > [!NOTE]\n    > This guide is for single region database deployment. If you want to create a multi-region Active-Active database, see [Create an Active-Active database](https://redis.io/docs/latest/operate/rc/databases/active-active/create-active-active-database) for specific steps and configuration options exclusive to Active-Active.\n    >\n\n#### Advanced options {#advanced-options}\n\n![The Advanced settings of the Setup tab.](https://redis.io/docs/latest/images/rc/subscription-new-flexible-setup-advanced.png)\n\nThe following settings are defined in the **Advanced options** of the **Setup** tab:\n\n| Advanced option | Description |\n|---|---|\n| **Redis Flex** | Determines if your databases are stored only in memory (RAM) or are split between memory and Flash storage (RAM+Flash).  See [Redis Flex](https://redis.io/docs/latest/operate/rc/databases/create-database/create-flex-database) for more information. |\n| **BYOC account** | To deploy these databases to an existing cloud account, select it here.  Use the **Add** button to add a new cloud account.<br/><br/>(Available only if [Redis Cloud Bring your own Cloud](https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud) is enabled) |\n| **AWS Resource tags** | Applies resource tags to the infrastructure deployed to your BYOC account. See [Resource tags](https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud/resource-tags) for more information.<br/><br/>(Available only if [Redis Cloud Bring your own Cloud](https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud) is enabled) |\n| **Multi-AZ** | Determines if replication spans multiple Availability Zones, which provides automatic failover when problems occur. See [High Availability](https://redis.io/docs/latest/operate/rc/databases/configuration/high-availability). |\n| **VPC configuration** | Select **In a new VPC** to deploy to a new [virtual private cloud](https://en.wikipedia.org/wiki/Virtual_private_cloud) (VPC).<br/><br/>To deploy these databases to an existing virtual private cloud, select **In existing VPC** and then set VPC ID to the appropriate ID value.<br/><br/>(Available only if [Redis Cloud Bring your own Cloud](https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud) is enabled and you have selected an existing BYOC account) |\n| **Allowed Availability Zones** | The availability zones for your selected region.<br/><br/>If you choose **Manual selection**, you must select at least one zone ID from the **Zone IDs** list.  For more information, see [Availability zones](https://redis.io/docs/latest/operate/rc/databases/configuration/high-availability#availability-zones). |\n| **Deployment CIDR** | The [CIDR](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing) range of IP addresses for your deployment. Redis creates a new [subnet](https://en.wikipedia.org/wiki/Subnetwork) for the **Deployment CIDR** in your [virtual private cloud](https://en.wikipedia.org/wiki/Virtual_private_cloud) (VPC). It cannot overlap with the CIDR ranges of other subnets used by your account.<br/><br/>For deployments in an existing VPC, the **Deployment CIDR** must be within your VPC's **primary** CIDR range (secondary CIDRs are not supported). |\n| **Public endpoint access** | Select whether or not to [block public endpoints](https://redis.io/docs/latest/operate/rc/security/database-security/block-public-endpoints) for all databases in the subscription.  |\n| **Persistent storage encryption** | Select whether to encrypt persistent storage with a Cloud-provider managed key or a [self-managed encryption key](https://redis.io/docs/latest/operate/rc/security/manage-encryption-keys). If you select **Customer managed key**, you'll get clear instructions to provide access to your self-managed key after you set up your database(s). See [Grant key permissions](https://redis.io/docs/latest/operate/rc/security/manage-encryption-keys#grant-key-permissions) for more information. |\n| **Maintenance windows** | Determines when Redis can perform [maintenance](https://redis.io/docs/latest/operate/rc/subscriptions/maintenance) on your databases. Select **Manual** if you want to set [manual maintenance windows](https://redis.io/docs/latest/operate/rc/subscriptions/maintenance/set-maintenance-windows). |\n\nWhen finished, choose **Continue** to determine your size requirements.\n\n![Select the Continue button to continue to the next step.](https://redis.io/docs/latest/images/rc/button-subscription-continue.png)"},{"id":"sizing-tab","title":"Sizing tab","role":"content","text":"The **Sizing** tab helps you specify the database, memory, and throughput requirements for your subscription.\n\n![The Sizing tab when creating a new Pro subscription.](https://redis.io/docs/latest/images/rc/subscription-new-flexible-sizing-tab.png)\n\nWhen you first visit the **Sizing** tab, there are no databases defined.  Select the **Add** button to create one.\n\n![Use the Add button to define a new database for your subscription.](https://redis.io/docs/latest/images/rc/icon-add.png)\n\nThis opens the **Database configurations** dialog, which lets you define the requirements for your new database.\n\n![The Database configurations dialog with basic settings.](https://redis.io/docs/latest/images/rc/flexible-add-database-basic.png)\n\nBy default, you're shown basic settings, which include:\n\n| Database&nbsp;setting | Description |\n|:---------|:-----------|\n| **Name** | Redis Cloud generates a database name for you, but you can change it at any time. |\n| **Version** | The Redis version for your database. We recommend you choose the latest available version. See [Redis version management](https://redis.io/docs/latest/operate/rc/databases/version-management) for more information. |\n| **Dataset size (GB)** | The amount of data for your dataset. Specify small sizes as decimals of 1.0&nbsp;GB; example: `0.1` GB (minimum). We calculate the total memory limit for you based on the other settings you choose for your database. <br/> Databases with Search and query have specific sizing requirements, see [Search and query sizing](https://redis.io/docs/latest/operate/rc/databases/configuration/advanced-capabilities#search-and-query-sizing) for more information. |\n| **High Availability** | Indicates whether a replica copy of the database is maintained in case the primary database becomes unavailable.  (Warning: Doubles memory consumption). See [High Availability](https://redis.io/docs/latest/operate/rc/databases/configuration/high-availability).  |\n| **Throughput** | Identifies maximum throughput for the database, which is specified in terms of operations per second (**Ops/sec**). See [Throughput](https://redis.io/docs/latest/operate/rc/databases/configuration/sizing#throughput) for more information. <br/> Databases with Search and query have specific throughput requirements. See [Search and query sizing](https://redis.io/docs/latest/operate/rc/databases/configuration/advanced-capabilities#search-and-query-sizing) for more information. |\n| **Hashing policy** | Determines how data is distributed across multiple Redis processes of a database. Available options depend on your account creation date. See [Clustering](https://redis.io/docs/latest/operate/rc/databases/configuration/clustering#manage-the-hashing-policy) for more information.  |\n| **Query performance factor** | *(Search and query databases on Redis 7.2 or later only)* Adds additional compute power to process your query and vector search workloads and boost your queries per second. See [Search and query sizing](https://redis.io/docs/latest/operate/rc/databases/configuration/advanced-capabilities#search-and-query-sizing) for more information. |\n| **Data Persistence** | Defines the data persistence policy, if any. See [Data persistence](https://redis.io/docs/latest/operate/rc/databases/configuration/data-persistence). |\n\nSelect **More options** to specify values for the following settings.\n\n![The Database configurations dialog with advanced settings.](https://redis.io/docs/latest/images/rc/flexible-add-database-advanced.png)\n\n| Database&nbsp;option | Description                                                                                                                                                     |\n|:---------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| **Advanced Capabilities** | [Advanced features and data types](https://redis.io/docs/latest/operate/rc/databases/configuration/advanced-capabilities) used by the database. Starting with Redis 8.0, all advanced capabilities are included in Redis by default. |\n| **OSS Cluster API** | Enable to use the [OSS Cluster API](https://redis.io/docs/latest/operate/rc/databases/configuration/clustering#oss-cluster-api).                                                                                                                |\n| **Type** | Set to **Redis**, otherwise **Memcached** database for legacy database support.                                                                                     |\n| **Supported Protocol(s)** | Choose between RESP2 and RESP3 _(Redis 7.2 and later only)_. See [Redis serialization protocol](https://redis.io/docs/latest/develop/reference/protocol-spec#resp-versions) for details |\n| **Quantity** | Number of databases to create with these settings.                                                                                                              |\n\nWhen finished, select **Save configuration** to save your database configuration.\n\n![Select the Save configuration button to define your new database.](https://redis.io/docs/latest/images/rc/button-configuration-save.png)\n\nUse the **Add database** button to define additional databases or select the **Continue button** to display the **Review and create** tab.\n\nHover over a database to see the **Edit** and **Delete** icons. You can use the **Edit** icon to change a database or the **Delete** icon to remove a database from the list.\n\n![Use the Edit button to change database settings.](https://redis.io/docs/latest/images/rc/icon-edit.png#no-click)&nbsp;![Use the Delete button to remove a database.](https://redis.io/docs/latest/images/rc/icon-delete-lb.png#no-click)"},{"id":"review-and-create-tab","title":"Review and Create tab","role":"content","text":"The **Review & Create** tab provides a cost estimate for your Redis Cloud Pro plan:\n\n![The Review & Create tab of the New Flexible subscription screen.](https://redis.io/docs/latest/images/rc/subscription-new-flexible-review.png)\n\nRedis breaks down your databases to Redis Billing Units (RBUs), each with their own size and throughput requirements. For more info, see [Billing unit types](#billing-unit-types).\n\nThe **Payment methods** section of this tab shows which payment method you're using for this database. Select the arrow on the top right of this section to view all available payment methods.\n\n![The payment method list.](https://redis.io/docs/latest/images/rc/subscription-new-flexible-cardlist.png)\n\nIf you have not added a payment method or want to add a new payment method, select **Add credit card** to add a new credit card.\n\nIf you have not already added a business address to your account, you must enter one. Redis uses your business address for communication, invoicing, and tax purposes.\n\n![The Add credit card screen before a business address has already been added.](https://redis.io/docs/latest/images/rc/billing-add-first-card.png)\n\nIf you already have a business address, you don't need to enter one here. See [Account settings](https://redis.io/docs/latest/operate/rc/accounts/account-settings) to learn how to update your business address.\n\n![The Add credit card screen after a business address has already been added.](https://redis.io/docs/latest/images/rc/billing-add-credit-card.png)\n\nIf the card's billing address is the same as your account's business address, select **Same as business address** to fill the billing address with your business address details.\n\nSelect **Back to Sizing** to make changes or **Confirm & Pay** to create your databases.\n\n![Select Confirm & pay to create your database.](https://redis.io/docs/latest/images/rc/button-create-db-confirm-pay.png)\n\nNote that databases are created in the background.  While they are provisioning, you aren't allowed to make changes. This process generally takes 10-15 minutes.\n\nUse the **Database list** to check the status of your databases."},{"id":"billing-unit-types","title":"Billing unit types","role":"content","text":"The Redis Billing Unit types associated with your Pro subscription depend on your database memory size and throughput requirements.\n\n| Billing unit type | Capacity (Memory/Throughput) |\n|:------------|:----------|\n| Pico | 100MB / 100 ops/sec |\n| Nano | 500MB / 500 ops/sec |\n| Micro | 1GB / 1K ops/sec |\n| High-throughput | 2.5GB / 25K ops/sec |\n| Small | 12.5GB / 12.5K ops/sec |\n| Large | 25GB  / 25K ops/sec |\n| Very large<sup>[1](#table-note-1)</sup> | 50GB / 5K ops/sec |\n| XLarge<sup>[2](#table-note-2)</sup> | 50GB / 10K ops/sec |\n| Flex-10<sup>[3](#table-note-3)</sup> | 50GB / 5K ops/sec |\n| Flex-20<sup>[3](#table-note-3)</sup> | 50GB / 10K ops/sec |\n| Flex-30<sup>[3](#table-note-3)</sup> | 50GB / 15K ops/sec |\n| Flex-40<sup>[3](#table-note-3)</sup> | 50GB / 20K ops/sec |\n| Flex-50<sup>[3](#table-note-3)</sup> | 50GB / 25K ops/sec |\n\n1. <a name=\"table-note-1\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>Used for databases with Auto Tiering before Redis 7.2.\n\n2. <a name=\"table-note-2\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>Used for hosted databases with Auto Tiering between Redis 7.2 and Redis 8.2. \n\n3. <a name=\"table-note-3\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>Used for Redis Flex databases for Redis 8.2 or later. Available Flex billing units depend on the RAM percentage you set for your database.\n\nPrices vary according to the cloud provider and region.  Minimum prices apply."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/databases/create-database","title":"Create a database","url":"https://redis.io/docs/latest/operate/rc/databases/create-database/","summary":"","tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:09+01:00","children":[{"id":"operate/rc/databases/create-database/create-free-database","summary":"Shows how to create a free database.","title":"Create a free database","url":"https://redis.io/docs/latest/operate/rc/databases/create-database/create-free-database/"},{"id":"operate/rc/databases/create-database/create-essentials-database","summary":"Shows how to create an Essentials database.","title":"Create an Essentials database","url":"https://redis.io/docs/latest/operate/rc/databases/create-database/create-essentials-database/"},{"id":"operate/rc/databases/create-database/create-flex-database","summary":"Shows how to create a Flex database on Redis Cloud and describes the best use cases for Flex.","title":"Create a Flex database","url":"https://redis.io/docs/latest/operate/rc/databases/create-database/create-flex-database/"},{"id":"operate/rc/databases/create-database/create-pro-database-new","summary":"Shows how to create a Pro database with a new subscription","title":"Create a Pro database with a new subscription","url":"https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new/"},{"id":"operate/rc/databases/create-database/create-pro-database-existing","summary":"Shows how to create a Pro database in an existing subscription","title":"Create a Pro database in an existing subscription","url":"https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-existing/"}],"page_type":"content","content_hash":"acfe0bc7831351f87285e6152f10a821dd8951881fdd32907d0fddbe0804c050","sections":[{"id":"content","title":"Content","role":"content","text":"A database is the heart of any Redis Cloud deployment. \n\nThe process for Creating a database depends on the type of [subscription plan](https://redis.io/docs/latest/operate/rc/subscriptions) you need.\n\nAn **Essentials** plan is a fixed monthly price for a single database. It is cost-efficient and designed for low-throughput scenarios. It supports a range of availability, persistence, and backup options. Pricing supports low throughput workloads.\n- [Create an Essentials database](https://redis.io/docs/latest/operate/rc/databases/create-database/create-essentials-database)\n- [Create a Redis Flex database](https://redis.io/docs/latest/operate/rc/databases/create-database/create-flex-database)\n\nA **Free** plan is a type of Essentials plan designed for learning and building test projects. It gives you enough space to learn Redis concepts and develop application prototypes. You can upgrade to a paid Essentials plan at any time.\n- [Create a free database](https://redis.io/docs/latest/operate/rc/databases/create-database/create-free-database)\n\nA **Pro** plan is an hourly price based on capacity. It supports more databases, larger databases, greater throughput, and unlimited connections. \n- [Create a Pro database with a new subscription](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new)\n- [Create a Pro database in an existing subscription](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-existing)\n- [Create an Active-Active database](https://redis.io/docs/latest/operate/rc/databases/active-active/create-active-active-database)"}],"examples":[]}
{"schema_version":2,"id":"operate/rc/databases/delete-database","title":"Delete database","url":"https://redis.io/docs/latest/operate/rc/databases/delete-database/","summary":"","tags":["docs","operate","rc"],"last_updated":"2026-10-01T15:21:31+01:00","page_type":"content","content_hash":"2c624cf171a94632c4be6637505b64336f08be9887c96f59ffd0671fcb92fefc","sections":[{"id":"content","title":"Content","role":"content","text":"Deleting a database requires the Account Owner role.\n\nDeleted databases cannot be recovered.  We recommend [making a backup](https://redis.io/docs/latest/operate/rc/databases/back-up-data), just in case.\n\nSign in to the [Redis Cloud console](https://cloud.redis.io/), then follow the steps for your plan to open the **Delete database** confirmation dialog.\n\n**Essentials:**\n\n1. Select the database from the list to open the **Database** screen.\n\n1. Select **More actions** > **Delete database**.\n\n    <img src=\"../../../../images/rc/database-more-actions-delete-essentials.png\" alt=\"The More actions menu on the database screen, with Delete database selected.\" width=40%>\n\n**Pro:**\n\n1. Select the database from the list. The **Configuration** tab is selected by default.\n\n1. Scroll to the **Danger zone**.\n\n    <img src=\"../../../../images/rc/database-details-configuration-tab-danger-flexible.png\" width=\"75%\" alt=\"The Danger zone of the database details screen.\">\n\n1. Select the **Delete** button.\n\n    <img src=\"../../../../images/rc/button-danger-zone-delete.png\" alt=\"The Delete button is located in the Danger zone section of the database Configuration tab.\" width=\"100px\">\n\n\n\nThe **Delete database** confirmation dialog appears. If this database is the only one in the subscription, you can also delete the subscription at this time.\n\n- Select **Delete my subscription and stop my payment** to delete both the database and the subscription.\n\n- Clear **Delete my subscription and stop my payment** to delete the database but keep the subscription.\n\n> [!NOTE]\n> You will continue to be charged for your subscription until you delete it, even if there are no databases in your subscription.\n\n![A delete database confirmation dialog asks you to consider deleting the subscription as well.](https://redis.io/docs/latest/images/rc/database-delete-last-dialog.png)\n\nSelect **Delete database** to confirm your choice.\n\n![The Delete database button.](https://redis.io/docs/latest/images/rc/button-database-delete.png)\n\nWhen the operation completes, the database and its data are deleted."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/databases/import-data","title":"Import data into a database","url":"https://redis.io/docs/latest/operate/rc/databases/import-data/","summary":"","tags":["docs","operate","rc"],"last_updated":"2026-10-05T10:59:47+01:00","page_type":"content","content_hash":"03a86a95127bf1a14a72a611e7067b9947a8835d1572159dc0ed6c3cafaece3b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can import an existing dataset into your Redis Cloud instance from an existing Redis server or an RDB file.\n\n> [!WARNING]\n> Data imported into an existing database overwrites existing data. \n> - If you import a dataset into the target database of an [Active-Passive setup](https://redis.io/docs/latest/operate/rc/databases/migrate-databases#sync-using-active-passive), the dataset will be overwritten by any changes to the source database.\n> - If you import a dataset into one region in an [Active-Active database](https://redis.io/docs/latest/operate/rc/databases/active-active), the dataset will also be replicated across regions.\n\n> [!NOTE]\n> Expired keys are not imported.\n> As a result, the number of keys in the source and destination databases can be different after the import is complete."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Make sure the Redis version of the source database is compatible with the database where the data will be imported."},{"id":"redis-server","title":"Import from a Redis server","role":"content","text":"To import a dataset from any publicly available Redis Open Source server:\n\n1. Select **Databases** from the Redis Cloud console menu and then select the target database from the database list.\n1. Select **Import**.\n\n    ![The Import dataset section and Import button.](https://redis.io/docs/latest/images/rc/database-configuration-import.png)\n\n1. Enter the source database details:\n    - Source type - Select **Redis**.\n    - Redis Hostname/IP Address - Enter the hostname or the public IP address of the source Redis server. Do not include the port number in the hostname.\n    - Redis port - Enter the port of the source Redis server if it is not the default value of `6379`.\n    - Password - Enter the password, if required by the source Redis database.\n1. Select **Import**."},{"id":"restore-from-an-rdb-file","title":"Restore from an RDB file","role":"content","text":"If you have an RDB or a compressed RDB file from a previous backup, you can restore data from that file into your Redis Cloud database.\n\nSelect the tab for your storage location type.\n\n**FTP or HTTP server:**\n\n1. Select **Databases** from the Redis Cloud console menu and then select your database from the list.\n2. Select **Import**.\n3. Enter the details for the RDB file:\n    - Source type - Select **FTP** or **HTTP**.\n    - Source path - Enter the URL for the RDB file: \n    \n    [code example]\n\n    Where:\n\n    - `protocol` - Server protocol: ftp, ftps, http, https\n    - `username` - Your username, if necessary\n    - `password` - Your password, if necessary\n    - `hostname` - Hostname or IP address of the server\n    - `port` - Port number of the server, if not `6379`\n    - `path` - Path to the file, if necessary\n    - `filename` - Filename of the RDB file, including the `.gz` suffix if the file is compressed\n\n    > [!NOTE]\n    > If your FTP username or password contains special characters such as `@`, `\\`, or `:`, you must URL encode (also known as Percent encode) these special characters. If you don't, your database may become stuck.\n    >\n\n4. For sharded databases with multiple RDB files, select **Add source** to add another RDB file.\n    > [!WARNING]\n    > For sharded databases with multiple RDB files, make sure to add every file before proceeding.\n    >\n\n5. Select **Import**.\n\n**AWS S3:**\n\nTo use the Redis Cloud console to import your data, you must first share the file from the Amazon Web Services (AWS) management console.\n\nTo share and import an RDB file that is stored in an AWS Simple Storage Service (S3) bucket:\n\n1. In the [AWS Management Console](https://console.aws.amazon.com/), configure the bucket's [bucket policy](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucket-policies.html) to give access to Redis Cloud:\n    1. Use the **Services** menu to locate and select **Storage** > **S3**.  This takes you to the Amazon S3 admin panel.\n\n    1.  Use the **Buckets** list to locate and select your bucket.  When the settings appear, select the **Permissions** tab, locate the **Bucket policy** section, and then click **Edit**.\n\n        -  If there is no existing bucket policy, add the following JSON bucket policy. Replace `UNIQUE-BUCKET-NAME` with the name of your bucket.\n\n            [code example]\n\n        - If a bucket policy already exists, add the following JSON policy statement to the list of statements. Replace `UNIQUE-BUCKET-NAME` with the name of your bucket.\n\n            [code example]\n\n    1. Save your changes.\n\n    1. If the bucket is encrypted using [SSE-KMS](https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingKMSEncryption.html), add the following statement to your [key policy](https://docs.aws.amazon.com/kms/latest/developerguide/key-policy-modifying.html). If you do not have a key policy, see [Creating a key policy](https://docs.aws.amazon.com/kms/latest/developerguide/key-policy-overview.html). Replace `UNIQUE-BUCKET-NAME` with the name of your bucket and `CUSTOM-KEY-ARN` with your key's [Amazon Resource Name (ARN)](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference-arns.html).\n\n        [code example]\n\n\n\n1. In the [Redis Cloud console](https://cloud.redis.io/), select the target database from the database list.\n1. Select **Import**.\n1. Enter the details for the RDB file:\n    - Source type - Select **AWS S3**.\n    - Source path - Enter the URL for the RDB file: \n    \n    [code example]\n\n    Where:\n\n    - `bucketname` - Name of the S3 bucket\n    - `path` - Path to the file, if necessary\n    - `filename` - Filename of the RDB file, including the .gz suffix if the file is compressed\n\n1. For sharded databases with multiple RDB files, select **Add source** to add another RDB file.\n\n1. Select **Import**.\n\n**Google Cloud Storage:**\n\nTo use the Redis Cloud console to import your data, you must first share the file from the Google Cloud console.\n\nTo share and import an RDB file that is stored in a Google Cloud Storage bucket:\n\n1. In the Google Cloud Storage bucket, edit the file's Access Control List to give read access to Redis Cloud:\n    1. Select **Edit access** in the RDB file menu.\n    1. Select **Add item**.\n    1. Enter the user details and access:\n       - In the Entity field of the new item, select **User**.\n       - In the Name field of the new item, enter: `service@redislabs-prod-clusters.iam.gserviceaccount.com`\n       - In the Access field of the new item, select **Reader**.\n    1. Select **Save**.\n\n    For more info, see [Set ACLs](https://cloud.google.com/storage/docs/access-control/create-manage-lists#set-an-acl).\n\n1. In the [Redis Cloud console](https://cloud.redis.io/), select the target database from the database list.\n1. Select **Import**.\n1. Enter the details for the RDB file:\n    - Source type - Select **Google Cloud Storage**.\n    - Source path - Enter the URL for the RDB file: `gs://bucketname/[path/]filename.rdb[.gz]`\n\n        Where:\n        - `bucketname` - Name of the GCS bucket\n        - `path` - Path to the file\n        - `filename` - Filename of the RDB file, including the .gz suffix if the file is compressed\n\n1. For sharded databases with multiple RDB files, select **Add source** to add another RDB file.\n\n1. Select **Import**.\n\n**Azure Blob Storage:**\n\nTo import an RDB file stored in a Microsoft Azure Blog storage container:\n\n1. In the Redis Cloud console, select the target database from the database list.\n1. Select **Import**.\n1. Enter the details for the RDB file:\n    - Source type - Select **Azure Blob Storage**.\n    - Source path - Enter the URL for the RDB file:\n        [code example]\n\n        Where:\n        - `storage_account_access_key` - Primary access key to the storage account\n        - `storage_account_name` - Name of the storage account\n        - `url` - URL of the storage account\n        - `container` - Name of the container, if necessary\n        - `filename` - Filename of the RDB file, including the .gz suffix if the file is compressed\n\n1. For sharded databases with multiple RDB files, select **Add source** to add another RDB file.\n\n1. Select **Import**."}],"examples":[{"id":"restore-from-an-rdb-file-ex0","language":"plaintext","code":"<protocol>://[username][:password]@hostname[:port]/[path/]filename.rdb[.gz]","section_id":"restore-from-an-rdb-file"},{"id":"restore-from-an-rdb-file-ex1","language":"json","code":"{\n                \"Version\": \"2012-10-17\",\n                \"Id\": \"MyBucketPolicy\",\n                \"Statement\": [\n                    {\n                        \"Sid\": \"RedisCloudBackupsAccess\",\n                        \"Effect\": \"Allow\",\n                        \"Principal\": {\n                            \"AWS\": \"arn:aws:iam::168085023892:root\"\n                        },\n                        \"Action\": [\n                            \"s3:PutObject\",\n                            \"s3:getObject\",\n                            \"s3:DeleteObject\"\n                        ],\n                        \"Resource\": \"arn:aws:s3:::UNIQUE-BUCKET-NAME/*\"\n                    }\n                ]\n            }","section_id":"restore-from-an-rdb-file"},{"id":"restore-from-an-rdb-file-ex2","language":"json","code":"{\n                \"Sid\": \"RedisCloudBackupsAccess\",\n                \"Effect\": \"Allow\",\n                \"Principal\": {\n                    \"AWS\": \"arn:aws:iam::168085023892:root\"\n                },\n                \"Action\": [\n                    \"s3:PutObject\",\n                    \"s3:getObject\",\n                    \"s3:DeleteObject\"\n                ],\n                \"Resource\": \"arn:aws:s3:::UNIQUE-BUCKET-NAME/*\"\n            }","section_id":"restore-from-an-rdb-file"},{"id":"restore-from-an-rdb-file-ex3","language":"json","code":"{\n            \"Sid\": \"Allow use of the key\",\n            \"Effect\": \"Allow\",\n            \"Principal\": {\n                \"AWS\": \"arn:aws:iam::168085023892:root\"\n            },\n            \"Action\": [\n                \"kms:Encrypt\",\n                \"kms:Decrypt\",\n                \"kms:ReEncrypt*\",\n                \"kms:GenerateDataKey*\",\n                \"kms:DescribeKey\"\n            ],\n            \"Resource\": [\n                \"arn:aws:s3:::UNIQUE-BUCKET-NAME/*\",\n                \"CUSTOM-KEY-ARN\"\n            ]\n        }","section_id":"restore-from-an-rdb-file"},{"id":"restore-from-an-rdb-file-ex4","language":"text","code":"s3://bucketname/[path/]filename.rdb[.gz]","section_id":"restore-from-an-rdb-file"},{"id":"restore-from-an-rdb-file-ex5","language":"plaintext","code":"abs://:storage_account_access_key@storage_account_name/[container/]filename.rdb[.gz]","section_id":"restore-from-an-rdb-file"}]}
{"schema_version":2,"id":"operate/rc/databases","title":"Manage databases","url":"https://redis.io/docs/latest/operate/rc/databases/","summary":"","tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:09+01:00","children":[{"id":"operate/rc/databases/create-database","summary":"","title":"Create a database","url":"https://redis.io/docs/latest/operate/rc/databases/create-database/"},{"id":"operate/rc/databases/connect","summary":"","title":"Connect to a Redis Cloud database","url":"https://redis.io/docs/latest/operate/rc/databases/connect/"},{"id":"operate/rc/databases/view-edit-database","summary":"","title":"View and edit databases","url":"https://redis.io/docs/latest/operate/rc/databases/view-edit-database/"},{"id":"operate/rc/databases/import-data","summary":"","title":"Import data into a database","url":"https://redis.io/docs/latest/operate/rc/databases/import-data/"},{"id":"operate/rc/databases/back-up-data","summary":"","title":"Back up and export a database","url":"https://redis.io/docs/latest/operate/rc/databases/back-up-data/"},{"id":"operate/rc/databases/migrate-databases","summary":"Shows two ways to migrate data from one Redis Cloud database to another.","title":"Migrate data between Redis Cloud databases","url":"https://redis.io/docs/latest/operate/rc/databases/migrate-databases/"},{"id":"operate/rc/databases/redirect-endpoints","summary":"Shows how to redirect dynamic endpoints to a different database.","title":"Redirect dynamic endpoints","url":"https://redis.io/docs/latest/operate/rc/databases/redirect-endpoints/"},{"id":"operate/rc/databases/tag-database","summary":"","title":"Manage database tags","url":"https://redis.io/docs/latest/operate/rc/databases/tag-database/"},{"id":"operate/rc/databases/monitor-performance","summary":"","title":"Monitor database performance","url":"https://redis.io/docs/latest/operate/rc/databases/monitor-performance/"},{"id":"operate/rc/databases/version-management","summary":"Describes Redis Cloud supported database versions and upgrade options.","title":"Redis version management","url":"https://redis.io/docs/latest/operate/rc/databases/version-management/"},{"id":"operate/rc/databases/active-active","summary":"Overview of the Active-Active feature for Redis Cloud.","title":"Active-Active Redis","url":"https://redis.io/docs/latest/operate/rc/databases/active-active/"},{"id":"operate/rc/databases/delete-database","summary":"","title":"Delete database","url":"https://redis.io/docs/latest/operate/rc/databases/delete-database/"},{"id":"operate/rc/databases/configuration","summary":"Provides background information to help you configure Redis Cloud databases to best fit your needs.","title":"Configure databases","url":"https://redis.io/docs/latest/operate/rc/databases/configuration/"}],"page_type":"content","content_hash":"ec613c6d8c09a9361e69931215dce44254b2e58d5d43bf7bfbf85c47f93b0f94","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Databases are the heart of any Redis Cloud deployment.  \n\nHere's how to perform a variety of tasks:"},{"id":"common-database-tasks","title":"Common database tasks","role":"content","text":"- [Create a database](https://redis.io/docs/latest/operate/rc/databases/create-database)\n\n    - [Create an Essentials database](https://redis.io/docs/latest/operate/rc/databases/create-database/create-essentials-database)\n    - [Create a Pro database with a new subscription](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new)\n    - [Create a Pro database in an existing subscription](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-existing)\n    - [Create an Active-Active database](https://redis.io/docs/latest/operate/rc/databases/active-active/create-active-active-database)\n\n- [View and edit databases](https://redis.io/docs/latest/operate/rc/databases/view-edit-database)\n\n- [Delete database](https://redis.io/docs/latest/operate/rc/databases/delete-database)\n\nIf you're new to Redis Cloud, see the [Quick Start](https://redis.io/docs/latest/operate/rc/rc-quickstart)."},{"id":"additional-tasks","title":"Additional tasks","role":"content","text":"- [Monitor database performance](https://redis.io/docs/latest/operate/rc/databases/monitor-performance) \n\n- [Import data into databases](https://redis.io/docs/latest/operate/rc/databases/import-data) \n\n- [Back up databases](https://redis.io/docs/latest/operate/rc/databases/back-up-data)\n\n- [Secure database access](https://redis.io/docs/latest/operate/rc/security/database-security)"},{"id":"configuration-details","title":"Configuration details","role":"configuration","text":"These topics provide background details that can help you tailor your databases to better fit your needs.\n\n- [Clustering Redis databases](https://redis.io/docs/latest/operate/rc/databases/configuration/clustering): Redis Cloud uses clustering to manage very large databases (25 GB and larger). Learn how to manage clustering and how to use hashing policies to manage the process.\n\n- [Data eviction policies](https://redis.io/docs/latest/operate/rc/databases/configuration/data-eviction-policies): Data eviction policies control what happens when new data exceeds the memory limits of a database. Learn the available policies and how to control them.\n\n- [Data persistence](https://redis.io/docs/latest/operate/rc/databases/configuration/data-persistence): Data persistence enables recovery in the event of memory loss or other catastrophic failure. Learn which options are available and how to apply specific settings to individual databases.\n\n- [High availability and replication](https://redis.io/docs/latest/operate/rc/databases/configuration/high-availability): Replication allows for automatic failover and greater fault tolerance. It can prevent data loss in the event of a hardware or zone failure.  Learn which options are available for Redis Cloud subscriptions.\n\n- [Advanced Capabilities](https://redis.io/docs/latest/operate/rc/databases/configuration/advanced-capabilities): Advanced capabilities extend Redis database functionality by adding new data types and options. Learn about the advanced capability options that are available for your database."},{"id":"compatibility","title":"Compatibility","role":"compatibility","text":"Redis Cloud does not support certain commands. Instead of using these commands, Redis Cloud automatically handles features like replication and lets you [manage your database](https://redis.io/docs/latest/operate/rc/databases) from the [Redis Cloud console](https://cloud.redis.io/) or [Redis Cloud REST API](https://redis.io/docs/latest/operate/rc/api).\n\nFor more details, see:\n\n- [Redis Cloud compatibility](https://redis.io/docs/latest/operate/rc/compatibility)\n\n- [Command compatibility](https://redis.io/docs/latest/operate/rs/references/compatibility/commands)\n\n- [Configuration compatibility](https://redis.io/docs/latest/operate/rs/references/compatibility/config-settings)"}],"examples":[]}
{"schema_version":2,"id":"operate/rc/databases/migrate-databases","title":"Migrate data between Redis Cloud databases","url":"https://redis.io/docs/latest/operate/rc/databases/migrate-databases/","summary":"Shows two ways to migrate data from one Redis Cloud database to another.","tags":["docs","operate","rc"],"last_updated":"2026-10-05T10:59:52+01:00","page_type":"content","content_hash":"f8a78c6ef40cb5e06de14e8896af3848fccb41ee08d6da763e8cb3345ecdf527","sections":[{"id":"overview","title":"Overview","role":"overview","text":"There are times when you need to migrate data from one Redis Cloud database to another.  \n\nHere are two common ways to do this.\n\nEach approach is suitable for different situations and the steps can vary according to your needs."},{"id":"transfer-via-import","title":"Transfer via import","role":"content","text":"The most common way to transfer data to a new database is to import a copy of the data into it.\n\nHere's how it works:\n\n1.  [Select an export storage destination](https://redis.io/docs/latest/operate/rc/databases/back-up-data#set-up-backup-storage-locations) and verify that it's ready for use and has sufficient space.\n\n1.  [Export](https://redis.io/docs/latest/operate/rc/databases/back-up-data) the data from the original database to the storage location.\n\n1.  [Import](https://redis.io/docs/latest/operate/rc/databases/import-data) the exported data into the target database, the one hosted by the new subscription.\n\nThe migrated data reflects the state of the data at the time it was originally exported.  \n\nIf you have apps or other connections actively using the source database, consider scheduling downtime for the migration to avoid loss.\n\nThis approach also lets you transfer data between databases hosted by different services."},{"id":"sync-using-active-passive","title":"Sync using Active-Passive","role":"content","text":"If your target database is hosted on Redis Cloud Pro, you can use Active-Passive to sync the source database to the target database.  The source database can be hosted on Redis Cloud Pro or Redis Cloud Essentials.\n\nThe source database remains active while the data migrates.\n\nThe following sections describe Active-Passive syncing processes."},{"id":"general-active-passive-syncing-process","title":"General Active-Passive syncing process","role":"content","text":"To migrate data using Active-Passive syncing, specify the target database as an Active-Passive replica of the source database:\n\n1.  Get the source database's public endpoint from the **General** section of the database's **Configuration** tab.\n2.  Activate the target database as an Active-Passive replica for the source.\n3.  Stop all writes to the source database and wait for the data to sync.\n4.  Turn off Active-Passive for the target database.\n5.  Switch apps and other connections to the target database.\n\n> [!NOTE]\n> Before you use Active-Passive, be aware of the following limitations:\n>\n> - As long as Active-Passive is enabled, data in the target database will not expire and will not be evicted regardless of the set [data eviction policy](https://redis.io/docs/latest/operate/rc/databases/configuration/data-eviction-policies). **Do not write to the target database while Active-Passive is enabled.** We recommend that you turn off Active-Passive after the databases are synced. \n>\n> - Turning on Active-Passive will flush the target database. Make sure that your target database has no important data before you turn on Active-Passive."},{"id":"sync-using-active-passive-1","title":"Detailed Active-Passive syncing process","role":"content","text":"Follow these detailed steps to migrate data using Active-Passive syncing:\n\n1.  Select **Databases** from the [Redis Cloud console](https://cloud.redis.io/) menu and select the target database in the list.\n\n    ![Select the target database from the database list.](https://redis.io/docs/latest/images/rc/migrate-database-select-source.png)\n\n1.  From the **Configuration** tab of the target database, select **Edit**.\n\n    ![Use the **Edit Database** button to change the configuration of the target database.](https://redis.io/docs/latest/images/rc/migrate-data-target-edit.png)\n\n1.  In the **Durability** section, enable **Active-Passive Redis** and then select **Add Source**.\n\n    ![Active-Passive settings are located in the **Durability** section of the database **Configuration** tab.](https://redis.io/docs/latest/images/rc/migrate-data-active-passive-enable.png)\n\n    ![Use the **Add Source** button to specify the source of the Active-Passive replica.](https://redis.io/docs/latest/images/rc/button-database-uri-add.png)\n\n1. This will open the **Add Active-Passive Redis** screen. Select where the source database is located.\n\n    ![The Add Active-Passive Redis screen.](https://redis.io/docs/latest/images/rc/migrate-data-add-active-passive.png)\n\n    - **Source database in this Redis Cloud account**: select **Current account**, then select the source database from the **Source database** list. You can type in the database's name to find it.\n\n        ![Select the Source database from the database list.](https://redis.io/docs/latest/images/rc/database-add-account-path-list.png)\n\n    - **Source database not hosted on Redis Cloud** (for example, a self-managed Redis deployment): select **External**.\n\n        > [!NOTE]\n        > Don't select **External** to sync from a database hosted in a *different* Redis Cloud account. Redis Cloud rejects Active-Passive connections between accounts and returns an error, even if you enter that database's public endpoint here. [Contact support](https://redis.io/support/) to migrate a database between Redis Cloud accounts using Active-Passive.\n        >\n\n        1.  In the **Enter the source URI** field, type `redis://` and then paste in the public endpoint details. \n\n            ![The source URI must be specified using the 'redis://' protocol.](https://redis.io/docs/latest/images/rc/migrate-data-specify-source-uri.png)\n\n        1. Select if the source database requires Transport Layer Security (TLS).\n        \n            - If the source database requires TLS, select **TLS** and enter the public server certificate in the **Server Certificate** field.\n\n            ![The Server Certificate field.](https://redis.io/docs/latest/images/rc/migrate-data-tls-server-cert.png)\n\n            - If the source database requires client authentication, select **Mutual TLS**.\n\n                1. Enter the public server certificate in the **Server Certificate** field.\n\n                    ![The Server Certificate field.](https://redis.io/docs/latest/images/rc/migrate-data-tls-server-cert.png)\n\n                1. Select **Download** to download the client certificate. \n\n                    ![The Client Certificate field. Select Download to download the client certificate.](https://redis.io/docs/latest/images/rc/migrate-data-tls-client-cert.png)\n\n                1. Configure the source database to accept the client certificate.\n\n1. Select **Save Active-Passive** to save your Active-Passive settings.\n\n    ![The **Save Active-Passive** button saves the active-passive settings.](https://redis.io/docs/latest/images/rc/icon-database-save-active-passive.png)\n\n    For an external database, we'll verify the endpoint at this step. If the endpoint can't be verified, make sure that you've copied the details directly from the source database and that the value you entered starts with `redis://`.\n\n1.  Select **Save Database** to begin updating the database.\n\n    ![Use the **Save Database** button to save your changes, deploy the database, and to start data migration.](https://redis.io/docs/latest/images/rc/button-database-save.png)\n\n    Initially, the database status is __Pending__, which means the update task is still running.  \n\n    ![When the status is 'Pending', your changes are still being deployed.](https://redis.io/docs/latest/images/rc/icon-database-update-status-pending.png)\n\n    The sync process doesn't begin until the database becomes `Active`.  \n\n    ![When the status becomes 'Active', data begins to sync.](https://redis.io/docs/latest/images/rc/icon-database-status-active.png)\n\n    When data has fully migrated to the target database, database status reports `Synced`.  \n\n    ![When the data is migrated, the target database status displays Synced.](https://redis.io/docs/latest/images/rc/migrate-data-status-synced.png)\n\nActive-Passive sync lets you migrate data while apps and other connections are using the source database.  Once the data is migrated, you should migrate active connections to the target database.\n\n> [!WARNING]\n> Do not write to the target database until turning off Active-Passive. Writing to the target database of an Active-Passive setup can cause data consistency issues and replication failures. See [Active-Passive replication considerations](https://redis.io/docs/latest/operate/rc/databases/configuration/data-eviction-policies#active-passive-replication-considerations) for more information."},{"id":"active-passive-memory-requirements","title":"Active-Passive memory requirements","role":"content","text":"Active-Passive sync requires more memory than data import.  On average, you need an extra 25% memory on top of other requirements, though specific requirements depend on the data types and other factors.  \n\nTo illustrate, suppose you want to migrate a 1&nbsp;GB source database without replication to a target database with replication enabled.  Here, the target database memory limit should be at least 2.5&nbsp;GB to avoid data loss."},{"id":"next-steps","title":"Next steps","role":"content","text":"If you want to redirect your application's connections to the target database, you can [redirect your database endpoints](https://redis.io/docs/latest/operate/rc/databases/redirect-endpoints) to the target database. \n\nBefore you redirect your endpoints, make sure:\n- The import or replication is finished.\n- Basic metrics for both the source and target databases are reporting normally.\n- The application authentication and authorization are set up correctly for the target database.\n- You have tested connection to the target database to confirm connectivity and credentials.\n- If using Active-Passive, turn off Active-Passive for the target database.\n\nDifferent applications have different availability and consistency requirements. Pausing writes before endpoint redirection is a standard best practice to help ensure data consistency. Still, you can choose the timing and behavior that fits your system (for example, whether to allow reads, how long to pause traffic, and what validation to run)."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/databases/monitor-performance","title":"Monitor database performance","url":"https://redis.io/docs/latest/operate/rc/databases/monitor-performance/","summary":"","tags":["docs","operate","rc"],"last_updated":"2026-10-01T15:21:31+01:00","page_type":"content","content_hash":"e08af1e37f224951a98ded2ce3918f880dc995602f51448c087b8fccba50d586","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Cloud provides a variety of metrics to help you monitor database performance.  You can view graphs of performance data at any time and receive emails when performance crosses defined thresholds."},{"id":"view-database-metrics","title":"View database metrics","role":"content","text":"The **Metrics** tab of the **View Database** screen provides a series of graphs showing performance data for your database.\n\n![The Metrics tab of the View Database screen.](https://redis.io/docs/latest/images/rc/database-metrics-tab.png)\n\nPerformance data provides insight into how your database is being used and how well it is performing."},{"id":"metric-intervals","title":"Metric intervals","role":"content","text":"The interval scrollbar controls the time period displayed in the graphs.  \n\n![The Metrics tab of the View Database screen.](https://redis.io/docs/latest/images/rc/database-metrics-interval-scrollbar.png)\n\nThe following intervals are available:\n\n| Interval | Resolution |\n|----------|------------|\n| 5 minutes | 30 seconds |\n| 15 minutes | 30 seconds |\n| Hour | 1 minute |\n| Day | 5 minutes |\n| Week | 30 minutes |\n| Month | 4 hours |\n| 3 months | 12 hours |"},{"id":"promote-metric-graphs","title":"Promote metric graphs","role":"content","text":"The **Metrics** screen shows two primary graphs and a collection of smaller ones.  You can promote any smaller graph to a primary position.\n\nWhen you use the mouse to point to a smaller graph, three things appear:\n\n- A promotion icon pointing left\n- A promotion icon pointing right\n- A summary panel showing the minimum, average, maximum, and most recent values displayed in the graph.\n\n![Promoting graphs to primary positions](https://redis.io/docs/latest/images/rc/metrics-promote-graphs.png)\n\nUse the promotion icons to promote the smaller graph to one of the primary positions. The left icon promotes the smaller graph to the left position and the right icon promotes the smaller graph to the right position."},{"id":"metric-definitions","title":"Metric definitions","role":"content","text":"Several metric graphs are available:\n\n| **Metric** | **Description** |\n|------------|-----------------|\n| [Ops/sec](https://redis.io/docs/latest/operate/rs/references/metrics/database-operations#opssec) | The number of overall operations per sec for all Redis commands |\n| [Reads/sec](https://redis.io/docs/latest/operate/rs/references/metrics/database-operations#readssec) | The number of read operations per second |\n| [Writes/sec](https://redis.io/docs/latest/operate/rs/references/metrics/database-operations#writessec) | The number of write operations per second |\n| [Other cmds/sec](https://redis.io/docs/latest/operate/rs/references/metrics/database-operations#other-commandssec) | The number of other Redis commands per second |\n| [Latency](https://redis.io/docs/latest/operate/rs/references/metrics/database-operations#latency) | Latency per operation, in milliseconds |\n| [Reads latency](https://redis.io/docs/latest/operate/rs/references/metrics/database-operations#reads-latency) | Latency per read operation, in milliseconds |\n| [Writes latency](https://redis.io/docs/latest/operate/rs/references/metrics/database-operations#writes-latency) | Latency per write operation, in milliseconds |\n| [Other latency](https://redis.io/docs/latest/operate/rs/references/metrics/database-operations#other-commands-latency) | Latency of other commands, in milliseconds |\n| [Used memory](https://redis.io/docs/latest/operate/rs/references/metrics/resource-usage#used-memory) | Amount of memory used by the database |\n| [Total keys](https://redis.io/docs/latest/operate/rs/references/metrics/database-operations#total-keys) | Total number of keys in the database |\n| [Connections](https://redis.io/docs/latest/operate/rs/references/metrics/resource-usage#connections) | Total number of connections to the endpoint |\n| [Evicted objects/sec](https://redis.io/docs/latest/operate/rs/references/metrics/database-operations#evicted-objectssec) | Number of objects evicted from the database per second. |\n| [Expired objects/sec](https://redis.io/docs/latest/operate/rs/references/metrics/database-operations#expired-objectssec) | Number of expired objects per second. An expired object is an object with expired TTL that was deleted from the database. |\n| [Hit ratio](https://redis.io/docs/latest/operate/rs/references/metrics/database-operations#hit-ratio) | Percent of operations on existing keys out of the total number database operations |\n| Network Ingress bytes/sec | Amount of traffic in bytes per second entering the database network |\n| Network Egress bytes/sec | Amount of traffic in bytes per second exiting the database network |\n\nFor more detailed analysis, consider using [Redis Insight](https://redis.io/docs/latest/develop/tools/insight) or [Prometheus and Grafana](https://redis.io/docs/latest/integrate/prometheus-with-redis-cloud)."},{"id":"configure-metric-alerts","title":"Configure alerts","role":"configuration","text":"Depending on your subscription plan, you can enable alerts for several metrics for a given database.\n\nTo do so, locate the alerts settings for your database: for Essentials databases, select the **Alerts** tab on the database screen; for Pro databases, go to the **Configuration** tab and locate the **Alerts** section. \n\n| Setting name | Description | Default Value | Accepted Range |\n|---|---|---|---|\n| **Dataset size has reached** | Sends an alert when the dataset size reaches or exceeds the defined percentage of the memory limit. (_Pro only_) | 80% | 1-100% |\n| **Total size of datasets under this plan reached** | Sends an alert when the total size of all datasets in the plan reaches or exceeds the defined percentage of the plan limit. (_Essentials only_) | 80% | 1-100% |\n| **Throughput is higher than** | Sends an alert when throughput is over the defined limit in operations per second (ops/sec). (_Paid Essentials or Pro only_) | 1000 ops/sec | 1-10000000 ops/sec |\n| **Throughput is lower than** | Sends an alert when throughput is under the defined limit in operations per second (ops/sec). (_Paid Essentials or Pro only_) | 10 ops/sec | 1-10000000 ops/sec |\n| **Latency is higher than** | Sends an alert when the latency is over the defined limit in milliseconds (msec). (_Paid Essentials or Pro only_) | 10 msec | 1-10000 msec |\n| **Number of connections** | Sends an alert when the number of connections exceeds the defined percentage of the plan limit.  (_Essentials only_) | 80% | 1-100% |\n| **Replica Of - database unable to sync with source** | Sends an alert when the target database cannot sync with the source database after the defined number of seconds. (_Pro only_) | 1 second | 0-1 seconds |\n| **Replica Of - sync lag is higher than** | Sends an alert if lag between the source and target databases exceeds the defined number of seconds. (_Pro only_) | 600 seconds | 1-86400 seconds |\n\nAlert settings are specific to each database. Make sure you've configured alerts for all desired databases."},{"id":"change-alert-recipients","title":"Change alert recipients","role":"content","text":"Any member of the account team can receive alert emails.\n\nTo update alert settings for one or more team members, select **Access Management** from the Redis Cloud console menu and then select the **Team** tab. For details, see [Access management](https://redis.io/docs/latest/operate/rc/security/access-control/access-management).\n\nIf you subscribe to Redis Cloud through a Platform-as-a-Service (PaaS) provider (such as Heroku), you will need to review your provider's documentation for help managing your team."},{"id":"connect-to-prometheus","title":"Connect to Prometheus","role":"content","text":"Redis Cloud exposes a Prometheus endpoint for Redis Cloud Pro databases. You can use this endpoint to connect Prometheus to your database.\n\nIn the **Metrics** tab of your database, select **Connect to Prometheus > Copy Prometheus endpoint** to save your Prometheus endpoint to the clipboard.\n\n![Use the Connect to Prometheus button to get the Prometheus endpoint.](https://redis.io/docs/latest/images/rc/database-metrics-connect-prometheus.png)\n\nYou can also get the Prometheus endpoint by calling [`GET /subscriptions/{subscriptionId}`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Subscriptions-Pro/operation/getSubscriptionById) and getting the `prometheusEndpoint` from the response.\n\n\nFor more information on how to connect Prometheus to Redis Cloud, see [Prometheus and Grafana with Redis Cloud](https://redis.io/docs/latest/integrate/prometheus-with-redis-cloud)."},{"id":"continue-learning-with-redis-university","title":"Continue learning with Redis University","role":"content","text":""}],"examples":[]}
{"schema_version":2,"id":"operate/rc/databases/redirect-endpoints","title":"Redirect dynamic endpoints","url":"https://redis.io/docs/latest/operate/rc/databases/redirect-endpoints/","summary":"Shows how to redirect dynamic endpoints to a different database.","tags":["docs","operate","rc"],"last_updated":"2026-10-16T10:47:17+01:00","page_type":"content","content_hash":"e233d2a3cbd0bfe47eb06fbac24586e15caabccf7f1c9d312527d55bf37de077","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Dynamic endpoints allow you to redirect application traffic from one database to another in the same Redis Cloud account without updating the endpoints in your application. Redis manages endpoint redirection for you.\n\nYou can redirect any database's dynamic endpoints to any Redis Cloud Pro database in the same account. If you need to replicate the data from the source database, you should [migrate your data](https://redis.io/docs/latest/operate/rc/databases/migrate-databases) to the target database before you redirect your endpoints."},{"id":"when-to-redirect-dynamic-endpoints","title":"When to redirect dynamic endpoints","role":"content","text":"Use endpoint redirection to seamlessly migrate your application traffic to a different database within the same Redis Cloud account. There is no need to update the endpoints in your application, since they'll remain the same. For example, you might want to:\n\n- Upgrade your database's subscription from an [Essentials Plan to a Pro Plan](https://redis.io/docs/latest/operate/rc/subscriptions/upgrade-essentials-pro)\n- Move between Redis Cloud offerings, such as Redis on RAM to Redis Flex\n- Split a subscription or combine databases from multiple subscriptions into one\n- Migrate your database to a different cloud provider, region, or availability zone\n- Redirect the endpoint to another database to restore service during Disaster Recovery"},{"id":"applications-that-use-legacy-static-endpoints","title":"Applications that use legacy static endpoints","role":"content","text":"Existing databases have both legacy static endpoints and dynamic endpoints. \n\nStatic endpoints on Redis Cloud start with `redis-<port>.c<number>` (or `redis-<port>.internal.c<number>` for the private endpoint). For example, a static endpoint might look like this:\n\n[code example]\n\nDynamic endpoints on Redis Cloud always contain three words and a random number, and end in `db.redis.io`. For example, a dynamic endpoint might look like this:\n\n[code example]\n\nYou can see the Dynamic endpoints for databases with both static and dynamic endpoints by expanding the **Dynamic endpoints** section in the **General** section of the **Configuration** tab. \n\n![The Static and dynamic endpoints for a database with both kinds of endpoints.](https://redis.io/docs/latest/images/rc/databases-configuration-general-endpoints-legacy.png)\n\nYou can only migrate the dynamic endpoints to point to a new database. Transitioning from the static to the dynamic endpoint does not cause downtime and allows you to gradually manage client disconnections. To migrate to the dynamic endpoint safely:\n- Move clients one-by-one (or service-by-service) from legacy static endpoints to dynamic endpoints. Note that during the transition period, both static and Dynamic endpoints can be used concurrently.\n- After all clients use the dynamic endpoint, you can then redirect the dynamic endpoints to the target database.\n\nThis phased approach minimizes risk and allows controlled client reconnections throughout the migration process."},{"id":"before-you-start","title":"Before you start","role":"content","text":"Read the following sections to prepare for endpoint redirection."},{"id":"scope-and-impact","title":"Scope and impact","role":"content","text":"This process redirects a source database's dynamic endpoints to a selected target database, including both public and private (if available) endpoints. **Redirecting endpoints does not migrate the data in your database.** You can choose to redirect the endpoints without migrating your data. If you need your data to be available in the target database, you must [migrate your data](https://redis.io/docs/latest/operate/rc/databases/migrate-databases) to the target database **before** you redirect your endpoints.\n\nTo ensure all connections are redirected to the target database, Redis Cloud will block all traffic to the source database for at least 5 minutes. This block period gives DNS caches time to expire and allow clients to reconnect and re-resolve the endpoint. All existing connections to the source database will be terminated and new connections will be refused. To reduce the risk of traffic being split between the source and target database, traffic will remain blocked until you [unblock it](#unblock-database-traffic).   \n\nShort-lived connection disruptions may occur as clients reconnect to the database, depending on client reconnection behavior.\n\nWe recommend redirecting during a low-traffic window.\n\n#### Redirecting endpoints after data migration\n\nIf you [migrated your data](https://redis.io/docs/latest/operate/rc/databases/migrate-databases) to the target database before redirecting your endpoints, make sure that:\n- The import or replication is finished.\n- Basic metrics for both the source and target databases are reporting normally.\n- The application authentication and authorization are set up correctly for the target database.\n- You have tested connection to the target database to confirm connectivity and credentials.\n\nDifferent applications have different availability and consistency requirements. Redis Cloud blocks traffic to the source database for at least 5 minutes to ensure all clients have reconnected to the target database, but you can choose the timing and behavior that fits your system (for example, how long to block traffic and what validation to run)."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Make sure you have met the following prerequisites:\n\n- Your application is using the dynamic endpoint. Endpoint redirection does not redirect [static endpoints](#applications-that-use-legacy-static-endpoints).\n- You have [created a target Redis Cloud Pro database](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new) in the same account that [is compatible with the source database](#redirection-compatibility).\n- If you monitor the source database with Prometheus, add the target database to Prometheus before your redirect the endpoint so that you can monitor the target database after the redirection. See [Connect to Prometheus](https://redis.io/docs/latest/operate/rc/databases/monitor-performance#connect-to-prometheus) for more information.\n- Make sure that your client connections are configured with socket/connection timeouts. If you do not configure them, your client may hang indefinitely and the connection will not refresh until the application is restarted. Refer to the [Client documentation](https://redis.io/docs/latest/develop/clients) for your client for more information.\n\n#### Redirection compatibility\n\nEndpoint redirection is only allowed when the source and target databases are compatible. Redis Cloud will validate compatibility and may prevent redirection if the source and target databases are not compatible.\n\nIf any of the following properties differ, the databases are not compatible and you cannot redirect the endpoints:\n- Port number\n- Connectivity settings, such as:\n    - [TLS settings](https://redis.io/docs/latest/operate/rc/security/database-security/tls-ssl)\n    - [VPC Peering](https://redis.io/docs/latest/operate/rc/security/vpc-peering) or other connectivity method settings\n    - [Default User settings](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/default-user)\n    - [CIDR allow list](https://redis.io/docs/latest/operate/rc/security/cidr-whitelist) settings\n\nSome differences may be intentional but can affect application behavior. In those cases, the console will warn you about the difference but allow you to proceed with redirection. The following differences will cause a warning:\n- Redis version\n- [RESP Database protocol version](https://redis.io/docs/latest/develop/reference/protocol-spec#resp-versions)\n- [OSS Cluster API](https://redis.io/docs/latest/operate/rc/databases/configuration/clustering#oss-cluster-api) configuration"},{"id":"limitations","title":"Limitations","role":"limits","text":"Be aware of the following limitations when redirecting dynamic endpoints:\n- The target database must be a Redis Cloud Pro database in a different subscription from the source database, under the same account.\n- Active-Active databases are currently not supported as either a source or target database (coming soon).\n- Databases using GCP Private Service Connect or AWS PrivateLink are currently not supported for endpoint redirection (coming soon)."},{"id":"redirect-database-endpoints","title":"Redirect database endpoints","role":"content","text":"To redirect your database endpoints:\n\n1. From the Redis Cloud console, select **Databases** from the menu and select the source database in the list.\n\n1. In the **General** section of the **Configuration** tab, select **Redirect endpoints**.\n\n    ![Use the **Redirect endpoints** button to change the target database for the source database endpoints.](https://redis.io/docs/latest/images/rc/databases-configuration-redirect-endpoints.png)\n\n1. Select the target Redis Cloud Pro database from the **Target database** list. You can type in the database's name to find it.\n\n    ![Select the target database from the database list.](https://redis.io/docs/latest/images/rc/migrate-data-redirect-pro-endpoints.png)\n\n1. If you want to assign the same [Role-based Access Control (RBAC) roles](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/role-based-access-control) to the target database that are assigned to the source database, select **Assign the same ACLs to the target database**.\n\n    ![Select **Assign the same ACLs to the target database** to assign the same roles to the target database.](https://redis.io/docs/latest/images/rc/migrate-data-redirect-assign-acls.png)\n\n1. Select **I acknowledge this action will redirect my database endpoints** to confirm that you understand that this action will redirect your database endpoints. Then select **Redirect endpoints**.\n\n    ![The **Redirect endpoints** button redirects the source database endpoints to the target database.](https://redis.io/docs/latest/images/rc/migrate-data-redirect-acknowledge.png)\n\nAfter you redirect your database endpoints, you can go to the **Configuration** tab of the target database to verify that the endpoints now point to the target database. To ensure all connections are redirected to the target database, Redis Cloud will block all traffic to the source database for at least 5 minutes to ensure all clients have reconnected to the target database. After 5 minutes, you can  [unblock traffic to the source database](#unblock-database-traffic). You can also [revert the redirection](#revert-endpoint-redirection) within 24 hours to restore the original endpoints."},{"id":"revert-endpoint-redirection","title":"Revert endpoint redirection","role":"content","text":"You can revert endpoint redirection within 24 hours to restore the original endpoints. From either database's **Configuration** tab, select **Revert** to revert endpoint migration.\n\n![The **Revert** button reverts endpoint migration.](https://redis.io/docs/latest/images/rc/migrate-data-redirect-revert.png)\n\nAfter the 24-hour window, you can no longer revert to the original endpoints. You can redirect them back to the source database if the source database is a Redis Cloud Pro database. However, doing this will create new endpoints for the target database.\n\nAfter you revert endpoint redirection, Redis Cloud will unblock traffic to the source database and block traffic to the target database for 5 minutes to ensure all clients have reconnected to the source database. After 5 minutes, you can [unblock traffic to the target database](#unblock-database-traffic)."},{"id":"unblock-database-traffic","title":"Unblock database traffic","role":"content","text":"After you redirect your database endpoints, Redis Cloud will block all traffic to the source database for at least 5 minutes to ensure all clients have reconnected to the target database. If you revert endpoint redirection, Redis Cloud will unblock traffic to the source database and block traffic to the target database for at least 5 minutes to ensure all clients have reconnected to the source database. If Redis Cloud has blocked traffic to a database, the database will display a **Traffic blocked** badge in the Redis Cloud console.\n\n![The **Traffic blocked** badge indicates that Redis Cloud has blocked traffic to the database.](https://redis.io/docs/latest/images/rc/migrate-data-traffic-blocked.png)\n\nYou will not be able to unblock traffic to a database until 5 minutes after you redirect your database endpoints or revert endpoint redirection. Before you unblock traffic to a database, make sure that all clients have reconnected to the correct database.\n\nTo unblock traffic to a database, from the database's **Configuration** tab, select **Unblock traffic**.\n\n![The **Unblock traffic** button unblocks traffic to the source database.](https://redis.io/docs/latest/images/rc/migrate-data-redirect-unblock.png)"}],"examples":[{"id":"applications-that-use-legacy-static-endpoints-ex0","language":"text","code":"redis-12345.c12345.us-east-1-mz.ec2.cloud.rlrcp.com","section_id":"applications-that-use-legacy-static-endpoints"},{"id":"applications-that-use-legacy-static-endpoints-ex1","language":"text","code":"horse-battery-staple-12345.db.redis.io","section_id":"applications-that-use-legacy-static-endpoints"}]}
{"schema_version":2,"id":"operate/rc/databases/tag-database","title":"Manage database tags","url":"https://redis.io/docs/latest/operate/rc/databases/tag-database/","summary":"","tags":["docs","operate","rc"],"last_updated":"2026-10-02T16:06:07+01:00","page_type":"content","content_hash":"893df771fac00ebe7575f1ea5e366338921da574a7410010ce77d5b570acaa6b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Tags are key-value pairs that let you categorize your databases. You can create tags and add them to your databases to associate them with each other. Once you've added tags, you can filter your databases in the [database list](https://redis.io/docs/latest/operate/rc/databases/view-edit-database#manage-the-database-list) or in the [cost report](https://redis.io/docs/latest/operate/rc/billing-and-payments/cost-report) by tag key or value."},{"id":"manage-tags","title":"Manage tags","role":"content","text":"You can manage tags from the [Redis Cloud console](https://cloud.redis.io/#/) in the [tag manager](#tag-manager). You can find the tag manager in the following places: \n\n- [From your database in the **Configuration** tab](#configuration-tab)\n- [From the database list](#database-list)\n\nAfter you open the [tag manager](#tag-manager), you can use it to add, edit, or delete tags."},{"id":"configuration-tab","title":"Open tag manager from the Configuration tab","role":"content","text":"To learn how to navigate to your database, see [View and edit databases](https://redis.io/docs/latest/operate/rc/databases/view-edit-database). Select the **Configuration** tab to view the tags that are set for your database. For Essentials databases, the **Manage tags** button is at the top of the tab; for Pro databases, go to the **General** section.\n\n![The Configuration tab of the Database details screen.](https://redis.io/docs/latest/images/rc/database-details-configuration-tab-general-flexible.png)\n\nSelect **Manage Tags** to open the [tag manager](#tag-manager).\n\n![The Manage tags button.](https://redis.io/docs/latest/images/rc/tags-button-manage-tags.png)"},{"id":"database-list","title":"Open tag manager from the database list","role":"content","text":"Using the database list allows you to manage tags for multiple databases without having to go into each database's **Tags** tab.\n\nTo get to the database list, select **Databases** from the main menu. \n\n![The database list with databases that are tagged.](https://redis.io/docs/latest/images/rc/tags-database-list.png)\n\nHover over the database and select **Manage tags**, or select **More actions** > **Manage tags** to open the [tag manager](#tag-manager).\n\n![Manage tags button.](https://redis.io/docs/latest/images/rc/tags-icon-manage-tags.png#no-click)\n![More actions button.](https://redis.io/docs/latest/images/rc/tags-icon-more-actions.png#no-click)"},{"id":"tag-manager","title":"Use the tag manager","role":"content","text":"The tag manager shows any tags that are associated with the database and allows you to create, edit, or delete tags.\n\n![The tag manager.](https://redis.io/docs/latest/images/rc/tags-tag-manager.png)\n\nYou can select the tag's **Key** and **Value** from the suggestions, or you can enter your own.\n\n![The New tag fields.](https://redis.io/docs/latest/images/rc/tags-new-tag.png)\n\nA valid tag must follow these guidelines:\n- Tag keys must be between 1 and 64 characters long, and values must be at least 1 character long.\n- Tags can only have lowercase letters, numbers, spaces, and these special characters: `-`, `_`, `.`, `+`, `@`, and `:`.\n\nAfter you add your first tag, you can:\n\n- Select the **Key** or **Value** field of an existing tag and enter new text to edit it.\n\n- Select **Delete** next to a tag to delete it.\n\n    ![Delete button.](https://redis.io/docs/latest/images/rc/icon-delete-lb.png)\n\n- Select **Add additional tag** to add another tag.\n\n    ![The Add additional tag button.](https://redis.io/docs/latest/images/rc/tags-button-add-additional-tag.png)\n\nSelect **Save tags** to save your changes.\n\n![The Save tags button.](https://redis.io/docs/latest/images/rc/tags-button-save-tags.png)"}],"examples":[]}
{"schema_version":2,"id":"operate/rc/databases/version-management","title":"Redis version management","url":"https://redis.io/docs/latest/operate/rc/databases/version-management/","summary":"Describes Redis Cloud supported database versions and upgrade options.","aliases":["/operate/rc/databases/version-management/version-management/"],"tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:09+01:00","children":[{"id":"operate/rc/databases/version-management/upgrade-version","summary":"Describes when you can upgrade your database to the latest available version for each plan type.","title":"Upgrade Redis database version","url":"https://redis.io/docs/latest/operate/rc/databases/version-management/upgrade-version/"}],"page_type":"content","content_hash":"b2226bff9110b8efd8cf0214a68ae877cdd88335912a80f7fdfecae1008c140c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Cloud provides comprehensive database version management that prioritizes customer control over major changes. \n\n> [!NOTE]\n> This page describes database version management for Redis Cloud. Redis Cloud manages the cluster version for you automatically."},{"id":"redis-version-structure","title":"Redis version structure","role":"content","text":"Redis uses a **MAJOR.MINOR.PATCH** versioning scheme:\n\n- **Major versions**: Significant changes that may include breaking changes (e.g., Redis 7 → Redis 8)\n- **Minor versions**: New features and improvements within a major version (e.g., 8.2 → 8.4 → 8.6)\n- **Patch versions**: Bug fixes and security updates (e.g., 8.2.1 → 8.2.2)"},{"id":"version-support-models","title":"Version support models","role":"compatibility","text":"Redis Cloud supports the following version support models:\n\n- **Long-Term Support (LTS)** versions are the second and last minor versions in a major version. LTS versions on Redis Cloud get 5 years of extended support, including security updates and major bug fixes.\n- **Short-Term Support (STS)** versions are all minor releases except the second and last minor versions in a major version. STS versions on Redis Cloud are supported for 6 months after the release of the following version.\n\nWhen a Redis version reaches **End-of-Life (EOL)**, Redis Cloud will automatically upgrade your database to the next minor version during maintenance windows if you do not manually upgrade before EOL."},{"id":"supported-database-versions","title":"Supported database versions","role":"compatibility","text":"> [!NOTE]\n> **We strongly recommend using the latest available database version** to benefit from the newest features, performance improvements, and security updates.\n\n| Version | Support model | Status | EOL Date | Plans |\n|---------|--------|--------|----------|-------|\n| **Redis 8.6** | STS | GA | TBD | Essentials, Pro |\n| **Redis 8.4** | STS | GA | January 1, 2027 | Essentials, Pro | \n| **Redis 8.2** | LTS | GA | September 1, 2030 | Essentials, Pro |\n| **Redis 8.0** | STS | Preview | December 1, 2026 | Essentials<sup>[1](#table-note-1)</sup> |\n| **Redis 7.4** | LTS | GA | December 1, 2029 | Essentials, Pro |\n| **Redis 7.2** | LTS | GA | December 1, 2029 | Essentials, Pro |\n| **Redis 6.2** | LTS | GA | April 1, 2027 | Pro |\n\n1. <a name=\"table-note-1\" style=\"display: block; height: 80px; margin-top: -80px;\"></a> Redis 8.0 is not available for new databases.\n\nWhen a database version reaches End-of-Life (EOL), Redis Cloud will automatically upgrade your database to the following minor version during maintenance windows if you do not manually upgrade before EOL."},{"id":"version-selection","title":"Version selection","role":"compatibility","text":"When creating a database, you select the database version (e.g., Redis 8.2). Redis Cloud automatically provides the latest patch version within that version.\n\nFor existing databases, minor version auto-upgrades apply differently by plan:\n\n- **Essentials**: Minor version auto-upgrades apply for all Redis versions.\n- **Pro**: Minor version auto-upgrades apply for Redis 8.4 and later only (with opt-out available). For Redis 8.2 and earlier, minor upgrades must be done manually.\n\nSee [Upgrade database version](https://redis.io/docs/latest/operate/rc/databases/version-management/upgrade-version) for more details."},{"id":"plan-differences","title":"Plan differences","role":"content","text":"| Feature | Essentials | Pro |\n|---------|------------|-----|\n| Minor version auto-upgrades | All versions, always enabled | Redis 8.4+ only, default enabled (can disable) |\n| Major version upgrades | Customer controlled | Customer controlled |\n| Manual upgrades | Major upgrades only | Yes |\n| [Automatic upgrade time](https://redis.io/docs/latest/operate/rc/subscriptions/maintenance) | Standard - between 12 AM and 6 AM region time | Configurable - [Set maintenance windows](https://redis.io/docs/latest/operate/rc/subscriptions/maintenance/set-maintenance-windows) |"},{"id":"manual-upgrades","title":"Manual upgrades","role":"content","text":"You can update your databases to a later version if by selecting **More actions** > **Version upgrade** from the database list or database page.\n\nBefore upgrading, you should:\n\n- Review the [release notes](https://redis.io/docs/latest/operate/rc/changelog/version-release-notes) for your target version and all versions in between to ensure compatibility with your applications.\n- [Back up your data](https://redis.io/docs/latest/operate/rc/databases/back-up-data), review [breaking changes](https://redis.io/docs/latest/operate/rc/changelog/version-release-notes).\n- Upgrade your staging or QA database before upgrading your production database.\n\nSee [Upgrade database version](https://redis.io/docs/latest/operate/rc/databases/version-management/upgrade-version) for detailed instructions."},{"id":"faq","title":"FAQ","role":"content","text":"**Can I prevent minor version auto-upgrades?**\nPro users can opt out of minor version auto-upgrades for Redis 8.4+. \n\n**What if I don't upgrade before EOL?**\nRedis Cloud will force upgrade after notifications during your next maintenance window to ensure security and support.\n\n**Can I downgrade?**\nAutomatically reverting to a previous Redis version is not supported on Redis Cloud. See [Manually revert upgrade](https://redis.io/docs/latest/operate/rc/databases/version-management/upgrade-version#manually-revert-upgrade) for more details.\n\n**How do I identify LTS vs STS?**\nCheck the [supported versions table](#supported-versions)."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/databases/version-management/upgrade-version","title":"Upgrade Redis database version","url":"https://redis.io/docs/latest/operate/rc/databases/version-management/upgrade-version/","summary":"Describes when you can upgrade your database to the latest available version for each plan type.","aliases":["/rc/databases/upgrade-version","/operate/rc/databases/upgrade-version/"],"tags":["docs","operate","rc"],"last_updated":"2026-10-01T15:21:38+01:00","page_type":"content","content_hash":"e9de100413bb3510a34e5c8e9d0e413ae279d94c1c940037f8151b0f00a8ad69","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can upgrade databases that are not on the latest available version of Redis to a later database version at any time.\n\n> [!NOTE]\n> Please keep in mind the following before upgrading your database version:\n>\n> - We recommend that you [back up your data](https://redis.io/docs/latest/operate/rc/databases/back-up-data) before upgrading to make it easier to [manually revert the upgrade](#manually-revert-upgrade) if needed.\n>\n> - We recommend that you upgrade your database during off-peak hours or during application maintenance to minimize reconnections.\n>\n> - Review the breaking changes for the new database version before upgrading: \n>     - [Redis 7.2](https://redis.io/docs/latest/operate/rc/changelog/version-release-notes/7-2) \n>     - [Redis 7.4](https://redis.io/docs/latest/operate/rc/changelog/version-release-notes/7-4)\n>     - [Redis 8.0](https://redis.io/docs/latest/operate/rc/changelog/version-release-notes/8-0)\n>     - [Redis 8.2](https://redis.io/docs/latest/operate/rc/changelog/version-release-notes/8-2)\n>     - [Redis 8.4](https://redis.io/docs/latest/operate/rc/changelog/version-release-notes/8-4)\n>     - [Redis 8.6](https://redis.io/docs/latest/operate/rc/changelog/version-release-notes/8-6)\n>\n> - You must upgrade the target database in an [Active-Passive](https://redis.io/docs/latest/operate/rc/databases/migrate-databases#sync-using-active-passive) setup before you upgrade the source database to prevent compatibility issues."},{"id":"upgrade-database","title":"Upgrade database","role":"content","text":"To upgrade a single-region Redis Cloud database: \n\n1. Choose your database from the **Databases** list to open your database page. Select **More actions > Version upgrade**.\n\n    ![The More Actions menu on the Database page.](https://redis.io/docs/latest/images/rc/databases-more-actions-menu.png)\n    \n    You can also select **More actions > Version upgrade** from the database list.\n\n1. Select the target version from the **Select version** list.\n\n    ![The Redis version upgrade screen.](https://redis.io/docs/latest/images/rc/database-version-upgrade.png)\n\n    If your database has not been backed up before, we recommend that you back up your database. Select **Go to backup** to go to the [backup settings](https://redis.io/docs/latest/operate/rc/databases/back-up-data).\n\n1. Select **Upgrade Now** to start the upgrade.\n\n    ![The upgrade button.](https://redis.io/docs/latest/images/rc/button-upgrade-now.png)\n\nThe database will start upgrading to the selected version immediately. The upgrade may take a few minutes. \n\nYou can continue to use the Redis Cloud console for other tasks during the upgrade."},{"id":"manually-revert-upgrade","title":"Manually revert upgrade","role":"content","text":"Automatically reverting to a previous Redis database version is not supported on Redis Cloud.\n\nIf you [backed up your database](https://redis.io/docs/latest/operate/rc/databases/back-up-data) before you upgraded your database version, you can:\n\n1. [Delete your database](https://redis.io/docs/latest/operate/rc/databases/delete-database) without deleting your subscription.\n1. [Create a new database](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-existing) in your subscription with the following settings:\n    - **Port number**: Use the same port number as the old database.\n    - **Version**: Select the original version of Redis.\n1. [Import the backup files](https://redis.io/docs/latest/operate/rc/databases/import-data) into the new database.\n\nThis allows you to connect to the database on the previous version without changing your connection details in your application.\n\nIf you did not back up your database before upgrading:\n\n1. [Back up your database](https://redis.io/docs/latest/operate/rc/databases/back-up-data).\n1. [Create a new database](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-existing) in your subscription and select the original version of Redis.\n1. [Import the backup files](https://redis.io/docs/latest/operate/rc/databases/import-data) into the new database.\n1. Change connection details in your application from the old database to the new database."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/databases/view-edit-database","title":"View and edit databases","url":"https://redis.io/docs/latest/operate/rc/databases/view-edit-database/","summary":"","tags":["docs","operate","rc"],"last_updated":"2026-10-02T16:06:07+01:00","page_type":"content","content_hash":"eca1fa8f802347b8d39b966c00a004888ddd391d13574f3d61c94a3bf1e5e57d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Use the **Databases** menu of the Redis Cloud console to manage your databases.\n\nTo view the details of a database:\n\n1. Sign in to the [Redis Cloud console](https://cloud.redis.io/).  (Create an account if you don't already have one.)\n\n2. Select the **Databases** menu to display a [searchable list of all databases](#manage-the-database-list).\n \n3. Locate the database in the list.\n\n4. Select the database name to open the **Database** page.\n\nThe **Database** screen lets you review:\n- Configuration details of a database\n- Graphs showing performance metrics\n- Recent activity via a \"[slowlog](https://redis.io/docs/latest/commands/slowlog),\" which lists queries that exceed a certain execution time.\n\nFor help changing database settings, see [Edit database details](#edit-database-details)."},{"id":"configuration-tab","title":"Configuration tab","role":"configuration","text":"The **Configuration** screen is divided into sections, each dedicated to a specific category.  Note that not every section or setting is available to every [subscription plan](https://redis.io/docs/latest/operate/rc/subscriptions).\n\nThe Configuration tab is organized differently for Essentials and Pro databases. Select your plan type below.\n\n**Essentials:**"},{"id":"performance--availability","title":"Performance & availability","role":"performance","text":"The **Performance & availability** section defines the plan and backup settings of your database.\n\n<img src=\"../../../../images/rc/database-details-configuration-tab-performance-availability-essentials.png\" alt=\"The Performance & availability section for an Essentials database.\">\n\n| Setting name | Editable | Description |\n|---|---|---|\n| **Plan** | <span title=\"Yes\">&#x2705; Yes</span> | The plan for your database. <br><br/>Your Essentials plan determines the size of your database and other limits. For a comparison of available plans, see [Redis Cloud Essentials plans](https://redis.io/docs/latest/operate/rc/subscriptions/view-essentials-subscription/essentials-plan-details). |\n| **High Availability** | <span title=\"Yes\">&#x2705; Yes</span> | Whether and how your data replicates across multiple nodes; see [available options](https://redis.io/docs/latest/operate/rc/databases/configuration/high-availability) |\n| **Data persistence** | <span title=\"Yes\">&#x2705; Yes</span> | Defines whether (and how) data is saved to disk; see [available options](https://redis.io/docs/latest/operate/rc/databases/configuration/data-persistence)  |\n| **Remote backup**  | <span title=\"Yes\">&#x2705; Yes</span> | When enabled, identifies a location and interval for [data backups](https://redis.io/docs/latest/operate/rc/databases/back-up-data). |\n| **Data eviction policy** | <span title=\"Yes\">&#x2705; Yes</span> | Configures which [policy](https://redis.io/docs/latest/operate/rc/databases/configuration/data-eviction-policies) is applied when your database reaches its memory limit |"},{"id":"access","title":"Access","role":"content","text":"The **Access** section shows the connection information for your database.\n\n<img src=\"../../../../images/rc/database-details-configuration-tab-access-essentials.png\" alt=\"The Access section for an Essentials database.\" width=50%>\n\nHere, you can: \n- View the number of active connections to your database.\n- View the Public endpoint of your database.\n- Select **Connect** to [connect to your database](https://redis.io/docs/latest/operate/rc/databases/connect)"},{"id":"data","title":"Data","role":"content","text":"In the **Data** section, you can:\n\n<img src=\"../../../../images/rc/database-details-configuration-tab-data-essentials.png\" alt=\"The Data section for an Essentials database.\" width=40%>\n\n- [Import data](https://redis.io/docs/latest/operate/rc/databases/import-data) into your database\n- Open [Redis Insight](https://redis.io/docs/latest/operate/rc/databases/connect/insight-cloud) to explore your data."},{"id":"security","title":"Security","role":"security","text":"The **Security** section helps you control access to your database.\n\n<img src=\"../../../../images/rc/database-details-configuration-tab-security-essentials.png\" alt=\"The Security section for an Essentials database.\">\n\n| Setting name | Editable | Description |\n|---|---|---|\n| **Default user** | <span title=\"Yes\">&#x2705; Yes</span> | When **On**, permits access using the `default` username with a simple password. Select **Configure** to turn off the default user or change the password. See [Default user](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/default-user) for more information. |\n| **CIDR allow list** | <span title=\"Yes\">&#x2705; Yes</span> | [Allow list](https://redis.io/docs/latest/operate/rc/security/cidr-whitelist) of IP addresses/security groups permitted to access the database. Select **Configure** to edit the CIDR allow list. |\n| **Transport layer security (TLS)** | <span title=\"Yes\">&#x2705; Yes</span> | Select **Configure** to turn on [transport layer security](https://redis.io/docs/latest/operate/rc/security/database-security/tls-ssl) (TLS) encryption for database access. |"},{"id":"database-info","title":"Database info","role":"content","text":"The **Database info** section defines basic properties about your database.\n\n<img src=\"../../../../images/rc/database-details-configuration-tab-database-info-essentials.png\" alt=\"The Database info section for an Essentials database.\">\n\n| Setting name | Editable | Description |\n|---|---|---|\n| **Name** | <span title=\"Yes\">&#x2705; Yes</span> | The name given to your database |\n| **ID** | <span title=\"No\">&#x274c; No</span> | The database's numeric ID |\n| **Creation** | <span title=\"No\">&#x274c; No</span> | Date the database was created |\n| **Last changed** | <span title=\"No\">&#x274c; No</span> | Date of last update |\n| **Advanced Capabilities** | <span title=\"No\">&#x274c; No</span> | This setting appears when an [advanced capability](https://redis.io/docs/latest/operate/rc/databases/configuration/advanced-capabilities) is enabled for a database |\n| **Architecture** | <span title=\"No\">&#x274c; No</span> | Shows whether the database runs in **RAM** or is a [**Flex**](https://redis.io/docs/latest/operate/rc/databases/create-database/create-flex-database) database |\n| **Version** | <span title=\"Yes\">&#x2705; Yes</span> | The Redis version your database uses |\n| **Protocol** | <span title=\"Yes\">&#x2705; Yes</span> | Shows which version of RESP the database uses. See [Redis serialization protocol](https://redis.io/docs/latest/develop/reference/protocol-spec#resp-versions) for details |\n\n**Pro:**"},{"id":"general-settings","title":"General settings","role":"content","text":"The **General** section defines basic properties about your database.\n\n<img src=\"../../../../images/rc/database-details-configuration-tab-general-flexible.png\" alt=\"The Configuration tab of the Database details screen.\">\n\nThe available settings vary according to your cloud provider and design choices. \n\n| Setting name              | Editable | Description |\n|:--------------------------|:---------|:------------|\n| **Database Name**         | <span title=\"Yes\">&#x2705; Yes</span> | The name given to your database                                                                                                                             |\n| **Subscription Name**     | <span title=\"No\">&#x274c; No</span> | The name for the subscription your database is a part of                                                                                                    |\n| **Public endpoint**       | <span title=\"No\">&#x274c; No</span> | Public URI used by any application or client to access the database. You can [block the public endpoint](https://redis.io/docs/latest/operate/rc/security/database-security/block-public-endpoints).                                                  |\n| **Private endpoint**      | <span title=\"No\">&#x274c; No</span> | Private endpoint URI available to approved clients; use CIDR allow list, VPC peering, or other connectivity options to enable access. |\n| **Tags**                  | <span title=\"Yes\">&#x2705; Yes</span> | A list of the [tags](https://redis.io/docs/latest/operate/rc/databases/tag-database) associated with the database. Select [Manage tags](https://redis.io/docs/latest/operate/rc/databases/tag-database#configuration-tab) to manage the database tags. |\n| **Vendor**                | <span title=\"No\">&#x274c; No</span> | The Cloud vendor hosting your database: AWS, Google Cloud, or Azure.                                                   |\n| **Region**                | <span title=\"No\">&#x274c; No</span> | The Cloud vendor region hosting your database                                                  |\n| **Type**                  | <span title=\"No\">&#x274c; No</span> | Displays 'Redis', 'Redis Stack' or 'memcached' based on the value selected when the database was created                                                    |\n| **Redis version**         | <span title=\"No\">&#x274c; No</span> | Redis version of the database                                                                                                                  |\n| **Auto Tiering**          | <span title=\"No\">&#x274c; No</span> | Checked when the subscription supports Auto Tiering                                                               |\n| **Active-Active Redis**   | <span title=\"No\">&#x274c; No</span> | Checked when the database is part of an [Active-Active](https://redis.io/docs/latest/operate/rc/databases/active-active) relationship                                                                                         |\n| **Creation time**         | <span title=\"No\">&#x274c; No</span> | Date and time the database was created                                                                                                                      |\n| **Last changed**          | <span title=\"No\">&#x274c; No</span> | Date and time of last update                                                                                                                                |\n| **Supported Protocol(s)** | <span title=\"Yes\">&#x2705; Yes</span> | Shows which version of RESP the database uses. See [Redis serialization protocol](https://redis.io/docs/latest/develop/reference/protocol-spec#resp-versions) for details |\n| **Advanced Capabilities**  | <span title=\"No\">&#x274c; No</span> | This setting appears when an [advanced capability](https://redis.io/docs/latest/operate/rc/databases/configuration/advanced-capabilities) is enabled for a database                                                  |"},{"id":"performance-section","title":"Performance section","role":"performance","text":"The **Performance** section describes the memory size, throughput, and hashing policy for a database.\n\n<img src=\"../../../../images/rc/database-details-configuration-tab-scalability-flexible.png\" alt=\"Use the Performance section to control the size, throughput, and hashing policy for a database.\">\n\n| Setting name          | Editable | Description |\n|:----------------------|:---------|:------------|\n| **Dataset size** | <span title=\"Yes\">&#x2705; Yes</span> | Maximum size (in GB) for your dataset. See [Dataset size](https://redis.io/docs/latest/operate/rc/databases/configuration/sizing#dataset-size).  |\n| **Throughput**        | <span title=\"Yes\">&#x2705; Yes</span> | Defines [throughput](https://redis.io/docs/latest/operate/rc/databases/configuration/sizing#throughput) in terms of maximum operations per second for the database. |\n| **Memory used**       | <span title=\"No\">&#x274c; No</span> | Memory currently used for your database.  |\n| **High availability**    | <span title=\"Yes\">&#x2705; Yes</span> | Replicates your data across multiple nodes; see [available options](https://redis.io/docs/latest/operate/rc/databases/configuration/high-availability)  |\n| **Hashing policy**    | <span title=\"Yes\">&#x2705; Yes</span> | Defines the [hashing policy](https://redis.io/docs/latest/operate/rc/databases/configuration/clustering#manage-the-hashing-policy).  |\n| **OSS Cluster API**       | <span title=\"Yes\">&#x2705; Yes</span> | Enables the [Cluster API](https://redis.io/docs/latest/operate/rc/databases/configuration/clustering#oss-cluster-api) for a database.<br/><br/>When this option is enabled, you cannot define a custom hashing policy.|\n\nTo learn more about these settings and when to use them, see [Sizing](https://redis.io/docs/latest/operate/rc/databases/configuration/sizing) and [Database clustering](https://redis.io/docs/latest/operate/rc/databases/configuration/clustering)."},{"id":"durability-section","title":"Durability section","role":"content","text":"The Durability section helps protect your data when problems occur.  These settings define replication, persistence, backup, and eviction policies.\n\n<img src=\"../../../../images/rc/database-details-configuration-tab-durability-flexible.png\" alt=\"Use the Durability section to protect your data from unexpected problems.\">\n\n| Setting name             | Editable | Description |\n|:-------------------------|:---------|:------------|\n| **Data persistence**     | <span title=\"Yes\">&#x2705; Yes</span> | Defines whether (and how) data is saved to disk; see [available options](https://redis.io/docs/latest/operate/rc/databases/configuration/data-persistence) |\n| **Data eviction policy** | <span title=\"Yes\">&#x2705; Yes</span> | Configures which [policy](https://redis.io/docs/latest/operate/rc/databases/configuration/data-eviction-policies) is applied when your database reaches its memory limit        |\n| **Remote backup**        | <span title=\"Yes\">&#x2705; Yes</span> | When enabled, identifies a location and interval for [data backups](https://redis.io/docs/latest/operate/rc/databases/back-up-data).                |\n| **Active-Passive Redis** | <span title=\"Yes\">&#x2705; Yes</span> | When enabled, identifies a path to the [linked database](https://redis.io/docs/latest/operate/rc/databases/migrate-databases#sync-using-active-passive).                                                               |"},{"id":"security-section","title":"Security section","role":"security","text":"The **Security** section helps you control access to your database.\n\n<img src=\"../../../../images/rc/database-details-configuration-tab-security-flexible.png\" alt=\"Use the Security settings to control access to your database.\">\n\n|Setting name| Editable | Description |\n|:-----------|:---------|:------------|\n| **Default user** | <span title=\"Yes\">&#x2705; Yes</span> | When enabled, permits access using a simple password                                                                                                                           |\n| **Default user password** | <span title=\"Yes\">&#x2705; Yes</span> | Password for default user. A default password is assigned to the database on creation and may be updated. If you [block the public endpoint](https://redis.io/docs/latest/operate/rc/security/database-security/block-public-endpoints), you can also turn on passwordless authentication for the default user here.  |\n| **CIDR allow list** | <span title=\"Yes\">&#x2705; Yes</span> | [Allow list](https://redis.io/docs/latest/operate/rc/security/cidr-whitelist) of IP addresses/security groups permitted to access the database.                    |\n| **Transport layer security (TLS)** | <span title=\"Yes\">&#x2705; Yes</span> | Enables [transport layer security](https://redis.io/docs/latest/operate/rc/security/database-security/tls-ssl) (TLS) encryption for database access.  |"},{"id":"alerts-section","title":"Alerts section","role":"content","text":"The **Alerts** section defines notification emails sent to your account and the conditions that trigger them.\n\n<img src=\"../../../../images/rc/database-details-configuration-tab-alerts-flexible.png\" alt=\"The Alerts section defines the notification emails and their triggering conditions.\">\n\nThe available alerts vary according to the plan type. See [Configure alerts](https://redis.io/docs/latest/operate/rc/databases/monitor-performance#configure-metric-alerts) for more information."},{"id":"danger-zone","title":"Danger zone","role":"content","text":"Actions in the **Danger Zone** are permanent and should not be taken lightly.\n\n<img src=\"../../../../images/rc/database-details-configuration-tab-danger-flexible.png\" alt=\"The Danger Zone includes activities that impact data, such as deleting a database.  Use with care.\">\n\nHere, you can:\n\n- Delete the database. Databases must be active before they can be deleted.  To learn more, see [Delete a database](https://redis.io/docs/latest/operate/rc/databases/delete-database).\n- Flush the database (_Active-Active databases only_).\n\nFor best results, we recommend [backing up data](https://redis.io/docs/latest/operate/rc/databases/back-up-data) before any danger zone actions."},{"id":"other-actions-and-info","title":"Other actions and info","role":"content","text":"The **View Database** screen also has tabs that let you view:\n\n- **Metrics**: a series of graphs showing database performance over time.  See [Monitor performance](https://redis.io/docs/latest/operate/rc/databases/monitor-performance) for more information.\n\n- **Slowlog**: a log showing recent [slow queries](https://redis.io/docs/latest/commands/slowlog) run against your database.  The log displays when the action started, the duration, the complexity of the operation, and any parameters passed to the operation.\n\n- **Alerts** (_Essentials databases only_): the notification emails sent to your account and the conditions that trigger them.  See [Configure alerts](https://redis.io/docs/latest/operate/rc/databases/monitor-performance#configure-metric-alerts) for more information."},{"id":"edit-database-details","title":"Edit database details","role":"content","text":"Editing your database depends on your plan type. Select your plan type to learn more.\n\n**Essentials:**\n\nYou can make direct changes to your database on the database page. Any changes you make won't be saved until you review and confirm them.\n\nAfter you've made changes to your database, select **Review changes** to review your changes. If your changes result in any cost changes, you'll see the cost change there.\n\n<img src=\"../../../../images/rc/database-essentials-review-changes.png\" alt=\"The Review changes button lets you review your changes and save them.\" width=\"50%\">\n\nFrom there, select **Confirm** or **Confirm & pay** to save your changes.\n\n**Pro:**\n\nUse the **Edit** button to edit database details.\n\n<img src=\"../../../../images/rc/button-database-edit.png\" alt=\"The Edit button lets you change selected database properties.\" width=\"100px\">\n\nChoose **Save database** to save your changes.\n\n<img src=\"../../../../images/rc/button-database-save.png\" alt=\"Use the Save database button to save database changes.\" width=\"150px\">\n\n\n\nBecause databases exist within the context of a deployment, certain fields cannot be updated, especially those that might lead to data loss. Refer to the tables in the previous sections for your plan to learn which settings are editable.\n\nIf you need to change other details, create a new database and then migrate existing data."},{"id":"manage-the-database-list","title":"Manage the database list","role":"content","text":"The **Databases** list summarizes the status of all databases associated with your account.  \n\nYou can:\n\n- Search by typing into the search box located above the database list.\n\n    ![Use the search bar to filter the list.](https://redis.io/docs/latest/images/rc/database-list-search.png)\n\n- Filter by selecting a filter type and then selecting the checkbox next to the options you want to include from the dropdown.  Select the Filter toggle, located on the right of the search bar, if the filter types are hidden.\n\n    ![Use the filter toggle to display filter options.](https://redis.io/docs/latest/images/rc/database-list-filter.png)\n\n    You can filter the list on **Status**, **Subscription**, **Subscription Type**, **Capabilities**, **Options**, **Tags**, and **Version**.  \n\n    A list of selected filters appears below the filter types.\n\n    To remove a filter click the **x** to the right of the name of that filter.  To remove all filters, select **Clear all**.\n\n    ![Use the filter toggle to display filter options.](https://redis.io/docs/latest/images/rc/database-list-filter-selected.png)\n\n- Select **Columns** to change what information is displayed on the list.\n\n    ![Use the columns toggle to display column options.](https://redis.io/docs/latest/images/rc/database-list-columns.png)\n\n- Select the **Export** button to export the current view as a CSV file.\n\n    ![The Export button exports the current list view to CSV.](https://redis.io/docs/latest/images/rc/icon-export-to-csv.png)\n\n- Sort the list in descending or ascending order using the arrow displayed to right of the field name in the header.  Supported fields include **Subscription**, **Database name**, **Memory usage**, and **Version**.\n\n    ![Use the arrows in the list header to sort the list.](https://redis.io/docs/latest/images/rc/icon-list-sort-asc.png#no-click) ![The direction of the arrow corresponds to the direction of the sort.](https://redis.io/docs/latest/images/rc/icon-list-sort-desc.png#no-click)\n\n    Select the arrow icon to change the sort order.  One sort order can be active at any given time.\n\n- Use the controls in the list footer to change the number of items displayed in the list or to navigate.\n\nSort orders and filter expressions are not saved between console sessions."}],"examples":[]}
{"schema_version":2,"id":"operate/rc","title":"Redis Cloud","url":"https://redis.io/docs/latest/operate/rc/","summary":"The fastest way to set up Redis - a fully managed Redis database on major public cloud services.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T14:59:36+01:00","children":[{"id":"operate/rc/rc-quickstart","summary":"","title":"Redis Cloud quick start","url":"https://redis.io/docs/latest/operate/rc/rc-quickstart/"},{"id":"operate/rc/databases","summary":"","title":"Manage databases","url":"https://redis.io/docs/latest/operate/rc/databases/"},{"id":"operate/rc/subscriptions","summary":"","title":"Manage subscriptions","url":"https://redis.io/docs/latest/operate/rc/subscriptions/"},{"id":"operate/rc/context-engine","summary":"Deploy AI agents with Redis Cloud.","title":"Redis Iris context engine on Redis Cloud","url":"https://redis.io/docs/latest/operate/rc/context-engine/"},{"id":"operate/rc/radar","summary":"See the status of every Redis cluster you run, in one place, with Redis Radar.","title":"Redis Radar on Redis Cloud","url":"https://redis.io/docs/latest/operate/rc/radar/"},{"id":"operate/rc/rdi","summary":"Use Redis Data Integration with Redis Cloud.","title":"Data Integration","url":"https://redis.io/docs/latest/operate/rc/rdi/"},{"id":"operate/rc/logs-reports","summary":"","title":"View logs and reports","url":"https://redis.io/docs/latest/operate/rc/logs-reports/"},{"id":"operate/rc/accounts","summary":"Describes how to work with Redis Cloud accounts and manage their settings.","title":"Manage accounts and settings","url":"https://redis.io/docs/latest/operate/rc/accounts/"},{"id":"operate/rc/billing-and-payments","summary":"Describes how to view billing transactions and manage payment methods for Redis Cloud subscriptions.","title":"Billing & payments","url":"https://redis.io/docs/latest/operate/rc/billing-and-payments/"},{"id":"operate/rc/cloud-integrations","summary":"Describes how to integrate Redis Cloud subscriptions into existing cloud provider services, whether existing subscriptions or through vendor marketplaces.","title":"Manage marketplace integrations","url":"https://redis.io/docs/latest/operate/rc/cloud-integrations/"},{"id":"operate/rc/security","summary":"","title":"Security","url":"https://redis.io/docs/latest/operate/rc/security/"},{"id":"operate/rc/api","summary":"Describes the Redis Cloud REST API and links to additional info","title":"Redis Cloud REST API","url":"https://redis.io/docs/latest/operate/rc/api/"},{"id":"operate/rc/resilient-apps","summary":"Set up your Redis Cloud databases to enable stable and highly available apps.","title":"Develop highly available and resilient apps with Redis Cloud","url":"https://redis.io/docs/latest/operate/rc/resilient-apps/"},{"id":"operate/rc/troubleshoot","summary":"Use the Redis Cloud knowledge base to find answers to common Redis Cloud questions.","title":"Troubleshoot Redis Cloud issues with the Redis Cloud knowledge base","url":"https://redis.io/docs/latest/operate/rc/troubleshoot/"},{"id":"operate/rc/compatibility","summary":"Redis Cloud compatibility with Redis Open Source.","title":"Redis Cloud compatibility with Redis Open Source","url":"https://redis.io/docs/latest/operate/rc/compatibility/"},{"id":"operate/rc/supported-regions","summary":"","title":"Supported Cloud providers and regions","url":"https://redis.io/docs/latest/operate/rc/supported-regions/"},{"id":"operate/rc/changelog","summary":"Lists new features, enhancements, and other changes in Redis Cloud.","title":"Redis Cloud changelog","url":"https://redis.io/docs/latest/operate/rc/changelog/"},{"id":"operate/rc/cloud-archive","summary":"Describes where to view the archive of Redis Cloud documentation.","title":"Archive","url":"https://redis.io/docs/latest/operate/rc/cloud-archive/"}],"page_type":"content","content_hash":"0bdf638fbc88a2deb0d62e8b9b19059e7ca50d9f2d98958d29d7780a3c9ea5ef","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Redis Cloud](https://redis.io/cloud/) is a fully managed database-as-a-service that brings the speed and reliability of Redis to the cloud, offering seamless scalability and high availability for modern applications.\n\nWith Redis Cloud, you get all of the features of Redis Software, including:\n- [Redis](https://redis.io/docs/latest/develop) and [Redis Stack](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise) support\n- Linear scalability\n- Instant failover, backups, and recovery\n- Predictable performance\n- 24/7 monitoring and support\n\n[Try Redis Cloud](https://redis.io/try-free/) to set up your free 30MB database."},{"id":"get-started","title":"Get started","role":"content","text":"Use the [Quick start](https://redis.io/docs/latest/operate/rc/rc-quickstart) to learn how to create your free database.\n- [Connect with redis-cli](https://redis.io/docs/latest/operate/rc/rc-quickstart#using-rediscli)\n- [Connect with Redis client](https://redis.io/docs/latest/operate/rc/rc-quickstart#using-redis-client)\n- [Connect with Redis Insight](https://redis.io/docs/latest/operate/rc/rc-quickstart#using-redisinsight)\n\nYou can also use [Vercel's Redis Cloud integration](https://vercel.com/marketplace/redis-cloud) or [Heroku's Redis Cloud add-on](https://elements.heroku.com/addons/rediscloud) to quickly add a Redis database to your project."},{"id":"databases","title":"Databases","role":"content","text":"Create and manage [Redis databases](https://redis.io/docs/latest/operate/rc/databases) in the cloud.\n- [Create database](https://redis.io/docs/latest/operate/rc/databases/create-database)\n- [View and edit databases](https://redis.io/docs/latest/operate/rc/databases/view-edit-database)\n- [Monitor performance](https://redis.io/docs/latest/operate/rc/databases/monitor-performance)\n- [Manage databases](https://redis.io/docs/latest/operate/rc/databases/configuration)\n- [Redis commands](/content/commands) & [compatibility](https://redis.io/docs/latest/operate/rc/compatibility)"},{"id":"subscriptions","title":"Subscriptions","role":"content","text":"Learn about the [types of subscriptions](https://redis.io/docs/latest/operate/rc/subscriptions).\n- [View and upgrade Essentials plan](https://redis.io/docs/latest/operate/rc/subscriptions/view-essentials-subscription)\n- [Essentials plans](https://redis.io/docs/latest/operate/rc/subscriptions/view-essentials-subscription/essentials-plan-details)\n- [View and edit Pro plan](https://redis.io/docs/latest/operate/rc/subscriptions/view-pro-subscription)"},{"id":"accounts--settings","title":"Accounts & settings","role":"content","text":"Manage Redis Cloud [accounts and settings](https://redis.io/docs/latest/operate/rc/accounts).\n- [Billing and payments](https://redis.io/docs/latest/operate/rc/billing-and-payments)\n- [Manage marketplace integrations](https://redis.io/docs/latest/operate/rc/cloud-integrations)"},{"id":"security","title":"Security","role":"security","text":"Manage [secure connections](https://redis.io/docs/latest/operate/rc/security) to cloud databases.\n- [Access management](https://redis.io/docs/latest/operate/rc/security/access-control/access-management) for Redis Cloud console security and account management\n- [Cloud database security](https://redis.io/docs/latest/operate/rc/security/database-security)\n- [Multi-factor authentication](https://redis.io/docs/latest/operate/rc/security/access-control/multi-factor-authentication)\n- [Single sign-on](https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso) and [social login](https://redis.io/docs/latest/operate/rc/security/access-control/social-login)\n- [Data access control](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control)"},{"id":"rest-api","title":"REST API","role":"content","text":"Use the [REST API](https://redis.io/docs/latest/operate/rc/api) to manage Redis Cloud databases and subscriptions.\n- [Get started with the REST API](https://redis.io/docs/latest/operate/rc/api/get-started)\n- REST API [reference](https://redis.io/docs/latest/operate/rc/api/api-reference) & [examples](https://redis.io/docs/latest/operate/rc/api/examples)\n- [`redisctl`](https://github.com/redis/redisctl) — a CLI tool that wraps the Redis Cloud and Redis Software APIs for terminal-based management"},{"id":"migrate-to-redis-cloud","title":"Migrate to Redis Cloud","role":"content","text":"Follow the step-by-step guide for your source environment:\n- [ElastiCache to Redis Cloud](https://redis.io/tutorials/migration/elasticache-to-redis-cloud/) — offline and live migration from AWS ElastiCache\n- [Memorystore to Redis Cloud](https://redis.io/tutorials/migration/memorystore-to-redis-cloud/) — offline and live migration from Google Cloud Memorystore\n- [Open source Redis to Redis Cloud](https://redis.io/tutorials/migration/redis-open-source-to-redis-cloud/) — migrate from a self-hosted Redis instance"},{"id":"migrate-to-azure-managed-redis","title":"Migrate to Azure Managed Redis","role":"content","text":"- [ElastiCache to Azure Managed Redis (AMR)](https://redis.io/tutorials/learn/migration/elasti-cache-to-azure-managed-redis/) — move your workload from AWS to Azure\n- [Memorystore to Azure Managed Redis (AMR)](https://redis.io/tutorials/learn/migration/memorystore-to-azure-managed-redis/) — move your workload from Google Cloud to Azure"},{"id":"related-info","title":"Related info","role":"related","text":"- [Redis Software](https://redis.io/docs/latest/operate/rs)\n- [Develop with Redis](https://redis.io/docs/latest/develop)\n- [Redis Stack](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise)\n- [Glossary](https://redis.io/docs/latest/glossary)"}],"examples":[]}
{"schema_version":2,"id":"operate/rc/logs-reports","title":"View logs and reports","url":"https://redis.io/docs/latest/operate/rc/logs-reports/","summary":"","tags":["docs","operate","rc"],"last_updated":"2026-04-24T11:28:22-04:00","children":[{"id":"operate/rc/logs-reports/system-logs","summary":"You can view and export system logs to track any activity associated with your subscriptions and databases.","title":"System logs","url":"https://redis.io/docs/latest/operate/rc/logs-reports/system-logs/"},{"id":"operate/rc/logs-reports/usage-reports","summary":"You can view usage reports to track the memory usage and shard usage of all your databases.","title":"Usage reports","url":"https://redis.io/docs/latest/operate/rc/logs-reports/usage-reports/"}],"page_type":"content","content_hash":"debbd7b5407ec56b347bc570f947be1c550e92603f753758232113417fd27221","sections":[{"id":"content","title":"Content","role":"content","text":"Here, you learn how to use Redis Cloud logs and reports to track activity and usage statistics."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/logs-reports/system-logs","title":"System logs","url":"https://redis.io/docs/latest/operate/rc/logs-reports/system-logs/","summary":"You can view and export system logs to track any activity associated with your subscriptions and databases.","tags":["docs","operate","rc"],"last_updated":"2026-10-02T16:06:07+01:00","page_type":"content","content_hash":"58b1c583296a99ef42845cb2791e1433a7dbaaf7d6326f3e92726a3cf7bcfbce","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The **Logs** page contains events, alerts, and logs from the activities, databases, and subscriptions associated with your account.\n\n![Choose the Logs page from the Redis Cloud console menu to view your subscription system log.](https://redis.io/docs/latest/images/rc/system-logs.png)\n\nYou can:\n\n* Sort the list by a specific field in descending or ascending order. Supported fields include *Time*, *Originator*, *Database name*, *API key name*, and *Activity*.\n\n    ![Use the arrows in the list header to sort the list.](https://redis.io/docs/latest/images/rc/icon-list-sort-asc.png#no-click)&nbsp;![The direction of the arrow corresponds to the direction of the sort.](https://redis.io/docs/latest/images/rc/icon-list-sort-desc.png#no-click)    \n    \n    Select the arrow icon to change the sort order.  You can only sort by one field at a time.\n\n* Use the **Export all** button to export all logs as a comma-separated values (CSV) file for use in other systems and programs.\n\n    ![Use the export all button in the top right to export all logs to a CSV file](https://redis.io/docs/latest/images/rc/system-logs-export.png)\n\n* Use the refresh button to refresh the system logs.\n\n    ![Use the refresh button in the top right to refresh the system logs](https://redis.io/docs/latest/images/rc/system-logs-refresh.png)\n\n* Use the search bar to search for specific entries. Supported fields include *Originator*, *Database name*, *API key name*, *Activity*, and *Description*."},{"id":"get-system-logs-using-rest-api","title":"Get system logs using REST API","role":"content","text":"Use [`GET /logs`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Account/operation/getAccountSystemLogs) to get the system logs using the REST API. See [Audit system logs](https://redis.io/docs/latest/operate/rc/api/examples/audit-system-logs) for more information."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/logs-reports/usage-reports","title":"Usage reports","url":"https://redis.io/docs/latest/operate/rc/logs-reports/usage-reports/","summary":"You can view usage reports to track the memory usage and shard usage of all your databases.","tags":["docs","operate","rc"],"last_updated":"2026-10-01T15:21:38+01:00","page_type":"content","content_hash":"9e704dd2dfc509851998ea9efb651bf45adbb67bc48ab037f539aadd113a916c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The **Usage Report** shows the daily memory usage and shard usage of all databases from the subscriptions associated with your account.\n\n![](https://redis.io/docs/latest/images/rc/usage-report-memory-usage.png)\n\nTo filter the data, you can:\n* Select a month and year from the **View Statement For** list to view the daily memory usage during a specific month.\n* Select a subscription from the **Subscription** list to view the daily memory usage of a specific subscription associated with your account.\n* Select a database from the **Database** list to view the daily memory usage of a specific database.\n\nYou can also hold the pointer over each bar in the graph to view the precise memory usage on that day."},{"id":"download-cost-report","title":"Download cost report","role":"content","text":"Select the **Cost report** button to download a report of all shard costs in CSV format.\n\n![The cost report download button.](https://redis.io/docs/latest/images/rc/button-cost-report-download.png)\n\nThe report gives a breakdown of all your Redis billing unit costs, per subscription and database, by month. Subscriptions under the minimum subscription price are broken down into actual billing unit prices and the difference from minimum price.\n\nSee [Cost report](https://redis.io/docs/latest/operate/rc/billing-and-payments/cost-report) and [How to download and visualize the cost report](https://support.redislabs.com/hc/en-us/articles/30042563097874-How-to-Download-and-Visualize-Redis-Cloud-Cost-Report) for more information."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/radar","title":"Redis Radar on Redis Cloud","url":"https://redis.io/docs/latest/operate/rc/radar/","summary":"See the status of every Redis cluster you run, in one place, with Redis Radar.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T14:59:36+01:00","page_type":"content","content_hash":"e7a470b5fbcb05ee1aa6a8db9bbc1c674ce5f4503143d95c67dbf3605105ec5d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Radar gives you one place to view the status of every Redis cluster in your fleet. Instead of checking each cluster separately, get one fleet-wide view across Redis Software, Redis Cloud, Redis Open Source, Amazon ElastiCache, and Google Memorystore.\n\nRedis Cloud's hosted Radar needs no setup. There's no subscription toggle to turn on and nothing to request from your account team.\n\n1. Go to [`radar.redis.io`](http://radar.redis.io).\n2. Sign in with your Redis Cloud credentials. If you don't have a Redis Cloud account, create one first.\n\nRadar uses the same sign-in as the rest of Redis Cloud, so any Redis Cloud account can sign in."},{"id":"next-steps","title":"Next steps","role":"content","text":"- [Connect clusters](https://redis.io/docs/latest/operate/radar/connect) to add your first Redis Cloud, Amazon ElastiCache, or Google Memorystore source.\n- [Monitor clusters and databases](https://redis.io/docs/latest/operate/radar/monitor) for what Radar shows you once a connection is collecting.\n- [Licenses and certificates](https://redis.io/docs/latest/operate/radar/licenses-and-certificates) to track certificate expiry across your fleet.\n- [Release notes](https://redis.io/docs/latest/operate/radar/release-notes) for what changed in each Cloud release."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/rc-quickstart","title":"Redis Cloud quick start","url":"https://redis.io/docs/latest/operate/rc/rc-quickstart/","summary":"","tags":["docs","operate","rc"],"last_updated":"2026-10-05T10:59:47+01:00","page_type":"content","content_hash":"70ea56d8a70cf7e225e93725c3b15d377cf6220695892921459d676bb6692c15","sections":[{"id":"overview","title":"Overview","role":"overview","text":"If you're new to Redis Cloud, this quick start helps you get up and running.  \n\nYou'll learn how to:\n\n1.  Create an account and a free database\n\n1.  Connect to your database\n\nIf you already have an account, see [Create an Essentials database](https://redis.io/docs/latest/operate/rc/databases/create-database/create-essentials-database) to create a Free 30 MB Essentials database. Free plans are a type of Essentials plans; this provides an easy upgrade path when you need it.\n\nIf you already have a database, see [Manage databases](https://redis.io/docs/latest/operate/rc/databases)."},{"id":"create-an-account","title":"Create an account","role":"content","text":"To create a new account with a free database:\n\n1. Go to the [Sign up](https://redis.io/try-free/) page.\n\n1. There are two options available to get started with Redis Cloud:\n   * Enter your information in the form and select **Get Started**.\n   * Sign up with **Google** or **Github**.\n\n1. After you enter your information, you should receive an activation email from Redis. Select **Activate account** to go to the **Create your database** page in the [Redis Cloud console](https://cloud.redis.io).\n\n1. You'll go to the [create database](https://redis.io/docs/latest/operate/rc/databases/create-database) page with the **Free** plan selected.\n\n    ![The Subscription selection panel with Free selected.](https://redis.io/docs/latest/images/rc/create-database-subscription-free.png)\n\n    > [!NOTE]\n    > If you want to create a different type of database, see the following guides:\n    > - [Create a Redis Flex database](https://redis.io/docs/latest/operate/rc/databases/create-database/create-flex-database)\n    > - [Create an Essentials database](https://redis.io/docs/latest/operate/rc/databases/create-database/create-essentials-database)\n    > - [Create a Pro database](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new)\n    >\n\n1. Redis will generate a database name for you. If you want to change it, you can do so in the **Database name** field. \n\n    ![The database name, cloud vendor, and region settings.](https://redis.io/docs/latest/images/rc/create-database-free-settings.png)\n\n1. Select the **Database version** you want to use.\n\n1. Choose your **Cloud vendor** and **Region**. You can choose between **Amazon Web Services (AWS)**, **Google Cloud**, and **Microsoft Azure** for the Cloud Vendor. \n\n    See [Supported regions](https://redis.io/docs/latest/operate/rc/supported-regions) for a list of supported regions by cloud vendor.\n\n1. Select **Create database**.\n\n    ![Select the Create database button to create your new database.](https://redis.io/docs/latest/images/rc/button-create-db.png)\n\n    When you create your database, there's a brief pause while your request is processed and then the **Database details** page appears.\n\n1.  In the upper corner, an icon shows the current status of the database.  If the icon shows an orange clock, this means your database is still being created and its status is _pending_.\n\n    ![Pending database status](https://redis.io/docs/latest/images/rc/icon-database-update-status-pending.png#no-click) ![Active database status](https://redis.io/docs/latest/images/rc/icon-database-status-active.png#no-click)</nobr>\n\n    Once the database has been created, it becomes _active_ and the status indicator switches to a green circle containing a checkmark.  \n\n    When your new database becomes active, you're ready to connect to it."},{"id":"connect-to-a-database","title":"Connect to a database","role":"content","text":"At this point, you're viewing the **Configuration** details for your new database. Go to the **Security** section of the page.\n\nTo connect to your database, you need your username and password. Each database is protected by a **Default user** called `default` and a masked **Default user password**. Select **Default user > Configure** and then select the eye icon to view your password.    \n\n![The Security section of the Configuration tab of the database details page.](https://redis.io/docs/latest/images/rc/database-essentials-default-user.png)\n\nOnce you have the username and password, select **Connect** to open the connection wizard.\n\n![Connect button.](https://redis.io/docs/latest/images/rc/button-connect.png#no-click)\n\nThe connection wizard provides the following database connection methods:\n\n- [Redis Insight](#using-redisinsight)\n\n- [`redis-cli`](#using-rediscli) utility\n\n- [Redis client](#using-redis-client) for your preferred programming language\n\n\n![The connection wizard.](https://redis.io/docs/latest/images/rc/connection-wizard.png)"},{"id":"using-redisinsight","title":"Redis Insight","role":"content","text":"[Redis Insight](https://redis.io/docs/latest/develop/tools/insight) is a free Redis GUI that lets you visualize your Redis data and learn more about Redis.\n\nYou can connect to your database with Redis Insight in two ways:\n\n1. [Open your database in Redis Insight in your browser](#ri-browser).\n\n1. [Download and Install Redis Insight](#ri-app) on Windows, macOS, and Linux.\n\n#### Open in your browser {#ri-browser}\n\n> [!NOTE]\n> Opening your database with Redis Insight in your browser is only available for Essentials databases. For all other databases, [Download and install Redis Insight](#ri-app) on your computer.\n\nSelect **Launch Redis Insight web** from the connection wizard to open it.\n\n![Launch Redis Insight web from the Connection Wizard.](https://redis.io/docs/latest/images/rc/rc-ri-wizard-launch.png)\n\nYou can also select **Launch** from the database page under **View and manage data with Redis Insight** to open Redis Insight in your browser.\n\n![Launch Redis Insight web from the database page.](https://redis.io/docs/latest/images/rc/rc-ri-open.png)\n\nRedis Insight will open in a new tab. \n\nFrom there, you can:\n\n- Select **Load sample data** to add sample data into your database.\n\n    ![Load Sample Data button](https://redis.io/docs/latest/images/rc/rc-ri-load-data.png)\n\n- Select **Insights** to learn how to use Redis.\n\n    ![The Insights icon](https://redis.io/docs/latest/images/rc/rc-ri-explore-icon.png)\n\nFor more information on how to use Redis Insight in your browser, see [Open with Redis Insight on Redis Cloud](https://redis.io/docs/latest/operate/rc/databases/connect/insight-cloud).\n\n#### Install and open on your computer {#ri-app}\n\n1. In the connection wizard, under **Redis Insight**, select **Download** to download Redis Insight.\n\n1. [Install Redis Insight](https://redis.io/docs/latest/develop/tools/insight).\n\n1. Once installed, select **Open in desktop**.\n\n1. A pop-up asks if you wish to open the link with Redis Insight. Select **Open Redis Insight** to connect to your database with Redis Insight.\n\n\nSee the [Redis Insight docs](https://redis.io/docs/latest/develop/tools/insight) for more info."},{"id":"using-redis-client","title":"Redis client","role":"content","text":"A Redis client is a software library or tool that enables applications to interact with a Redis server. Each client has its own syntax and installation process. For help with a specific client, see the client's documentation.\n\nThe connection wizard provides code snippets to connect to your database with the following programming languages:\n\n- .NET using [StackExchange.Redis/NRedisStack](https://redis.io/docs/latest/develop/clients/dotnet)\n- node.js using [node-redis](https://redis.io/docs/latest/develop/clients/nodejs)\n- Python using [redis-py](https://redis.io/docs/latest/develop/clients/redis-py)\n- Java using [Jedis](https://redis.io/docs/latest/develop/clients/jedis) and [Lettuce](https://redis.io/docs/latest/develop/clients/lettuce)\n- Go using [go-redis](https://redis.io/docs/latest/develop/clients/go)\n- PHP using [Predis](https://redis.io/docs/latest/develop/clients/php)\n\n![The connection wizard clients.](https://redis.io/docs/latest/images/rc/connection-wizard-clients.png)\n\nSee [Clients](https://redis.io/docs/latest/develop/clients) to learn how to connect with the official Redis clients.\n\nSee the following guides to get started with different Redis use cases:\n- [Data structure store](https://redis.io/docs/latest/develop/get-started/data-store)\n- [Document database](https://redis.io/docs/latest/develop/get-started/search-tutorial)\n- [Vector database](https://redis.io/docs/latest/develop/get-started/search-tutorial/vector-search)\n- [RAG with Redis](https://redis.io/docs/latest/develop/get-started/rag)\n- [Redis for AI](https://redis.io/docs/latest/develop/ai)"},{"id":"using-rediscli","title":"redis-cli","role":"content","text":"The [`redis-cli`](https://redis.io/docs/latest/develop/tools/cli) utility is installed when you install Redis.  It provides a command-line interface that lets you work with your database using core [Redis commands](/content/commands).\n\nTo run `redis-cli`, [install Redis](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack) on your machine.\n\nIf you only need the Redis CLI (`redis-cli`) and not the full Redis Open Source distribution, you can [install the standalone `redis-cli` binary](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/install-redis-cli) on Linux or macOS.\n\nSee [Redis CLI](https://redis.io/docs/latest/develop/tools/cli) to learn how to use `redis-cli`."},{"id":"more-info","title":"More info","role":"content","text":"- [Connect your application](https://redis.io/docs/latest/develop/clients)\n- [Import data](https://redis.io/docs/latest/operate/rc/databases/import-data)\n- [Manage databases](https://redis.io/docs/latest/operate/rc/databases)\n- [Data persistence](https://redis.io/docs/latest/operate/rc/databases/configuration/data-persistence)\n- [Secure your Redis Cloud database](https://redis.io/docs/latest/operate/rc/security)\n- [Back-up databases](https://redis.io/docs/latest/operate/rc/databases/back-up-data)\n- [Monitor Redis Cloud performance](https://redis.io/docs/latest/operate/rc/databases/monitor-performance)"},{"id":"continue-learning-with-redis-university","title":"Continue learning with Redis University","role":"content","text":""}],"examples":[]}
{"schema_version":2,"id":"operate/rc/rdi/create-workspace","title":"Create and manage Data Integration workspace","url":"https://redis.io/docs/latest/operate/rc/rdi/create-workspace/","summary":"Create and manage the infrastructure for your Data Integration pipelines.","aliases":["/operate/rc/databases/rdi/create-workspace","/operate/rc/databases/rdi/create-workspace/"],"tags":["docs","operate","rc"],"last_updated":"2026-10-12T14:51:21+03:00","page_type":"content","content_hash":"6980c52e69528c854ab7adceb6354d1f5ad76218cca3ce008ebe7ddb16c6254c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Before you can create your first Data Integration pipeline for a Redis Cloud subscription, you must first deploy the cloud infrastructure needed to host the pipeline and run the workers associated with the pipeline. In Redis Cloud, this is called a **Workspace**. Each Pro subscription can have one Data integration workspace. You only need to set up the workspace once - any pipelines you create for your subscription will run on the workspace until you delete it. You won't be charged for a workspace until you start running your Data Integration pipeline."},{"id":"create-a-data-integration-workspace","title":"Create a Data Integration workspace","role":"content","text":"For multiple source databases or Redis subscriptions, see [Plan source connectivity](https://redis.io/docs/latest/operate/rc/rdi/networking). It explains which endpoint services you can reuse and which connections you configure for each workspace.\n\nTo create a Data Integration workspace for an existing [Pro subscription](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new):\n\n1. In the Redis Cloud console, select **Data Integration**.\n\n2. Select **Create workspace** if you have no workspaces yet.\n\n    ![The create workspace button.](https://redis.io/docs/latest/images/rc/rdi/rdi-create-workspace-button.png)\n\n    If you already have a workspace, select **New workspace**.\n\n    ![The new workspace button.](https://redis.io/docs/latest/images/rc/rdi/rdi-new-workspace-button.png)\n\n    You can also open **Data Integration** from a subscription or database page.\n    Select **Create workspace** to use that subscription.\n\n    ![The create workspace button.](https://redis.io/docs/latest/images/rc/rdi/rdi-create-workspace-button.png)\n\n3. Select your Pro subscription if it is not already selected.\n\n    ![The select pro subscription drop down.](https://redis.io/docs/latest/images/rc/rdi/rdi-create-workspace-select-subscription.png)\n\n4. Review **Data Integration subnet (CIDR)**. The console suggests a dedicated\n   `/22` Classless Inter-Domain Routing (CIDR) range for the workspace.\n\n    [Plan network capacity for all pipelines](https://redis.io/docs/latest/operate/rc/rdi/scale-pipeline#plan-workspace-network-capacity)\n    before you create the workspace. Choose a `/21` or larger range if you plan\n    more than 5 sources or more than 5 processor replicas in total.\n    These counts are a conservative planning trigger, not a capacity limit.\n    You cannot enlarge the workspace CIDR after creation.\n\n    For Amazon Web Services (AWS), the RDI workspace CIDR must:\n\n    - Be in the same [RFC 1918 private address range](https://datatracker.ietf.org/doc/html/rfc1918#section-3) as the subscription virtual private cloud (VPC)'s primary CIDR: `10.0.0.0/8`, `172.16.0.0/12`, or `192.168.0.0/16`.\n    - Not overlap with existing subscription, peering, transit gateway (TGW), application, database, or RDI workspace CIDR ranges.\n\n    For example, a subscription VPC has the primary CIDR `10.238.252.0/24`.\n    The range `192.168.0.0/22` is invalid because it is in a different\n    RFC 1918 range. An unused range such as `10.239.0.0/22` is valid.\n\n    If the suggestion is missing or unsuitable, select another unused range.\n    It must meet your capacity needs and use the same private address range.\n    See [VPC CIDR block association restrictions](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-cidr-blocks.html#vpc-resize).\n\n    ![The select pro subscription drop down.](https://redis.io/docs/latest/images/rc/rdi/rdi-create-workspace-cidr.png)\n\n5. Select **Create workspace**.\n\n    ![The create workspace button.](https://redis.io/docs/latest/images/rc/rdi/rdi-create-workspace-button.png)\n\nRedis Cloud creates the workspace in the background. Select **Create pipeline**\nto [create your pipeline](https://redis.io/docs/latest/operate/rc/rdi/define) while it provisions.\nSelect **Create pipeline later** to return to the Redis Cloud console.\n\n\n![The Add pipeline control is available while the workspace is being created.](https://redis.io/docs/latest/images/rc/rdi/rdi-workspace-add-pipeline.png)"},{"id":"view-workspace-status-and-details","title":"View workspace status and details","role":"content","text":"You can view your workspace in one of the following ways:\n\n- From the Redis Cloud console, go to the **Data integration** page, or\n- From your subscription, select the **Data Integration** tab\n\n![Workspace pipeline list showing a streaming pipeline and its Sources column.](https://redis.io/docs/latest/images/rc/rdi/rdi-2-workspace-sources.png)\n\nThere, you'll see your workspace and its pipelines. The **Sources** column lists each pipeline's source names and database types. A source with unfinished configuration is marked **Pending setup**. Open its draft to continue setup. To see your workspace details, including the deployment CIDR and region information, select **Workspace actions > Workspace details**.\n\n![Workspace actions menu with Workspace details and Delete workspace.](https://redis.io/docs/latest/images/rc/rdi/rdi-2-workspace-actions.png)"},{"id":"delete-workspace","title":"Delete workspace","role":"content","text":"> [!WARNING]\n> Make sure to [delete your data pipeline](https://redis.io/docs/latest/operate/rc/rdi/view-edit#delete-pipeline) before deleting your workspace.\n\nTo delete your workspace, select **Workspace actions > Delete workspace** from your workspace.\n\n![Workspace actions menu with Workspace details and Delete workspace.](https://redis.io/docs/latest/images/rc/rdi/rdi-2-workspace-actions.png)"}],"examples":[]}
{"schema_version":2,"id":"operate/rc/rdi/define","title":"Create data pipeline","url":"https://redis.io/docs/latest/operate/rc/rdi/define/","summary":"Connect one or more source databases to a Redis Cloud target and define your data pipeline.","aliases":["/operate/rc/databases/rdi/define/","/operate/rc/databases/rdi/define"],"tags":["docs","operate","rc"],"last_updated":"2026-10-18T03:56:52+03:00","page_type":"content","content_hash":"4d26ace8430e0d463d5b43e74d0f6a6b0d808677870d0467d9cbaf99ffb30b76","sections":[{"id":"overview","title":"Overview","role":"overview","text":"After you have [prepared each source database](https://redis.io/docs/latest/operate/rc/rdi/setup) and [created a workspace](https://redis.io/docs/latest/operate/rc/rdi/create-workspace), you can create a pipeline. One pipeline can ingest data from several source databases into one Redis target.\n\nIn the [Redis Cloud console](https://cloud.redis.io/), open your target database's **Data Integration** tab and select **Add pipeline**. You can also open the workspace from the **Data Integration** page or your subscription's **Data Integration** tab. To continue an existing draft, open its actions menu and select **Resume pipeline setup**.\n\n![The Add pipeline control is available while the workspace is being created.](https://redis.io/docs/latest/images/rc/rdi/rdi-workspace-add-pipeline.png)\n\nTo create a pipeline:\n\n1. [**Settings**](#settings): Select the shared target, default data structure, and processor properties.\n1. [**Add sources**](#pipeline-setup): Select source types and give each source a unique name.\n1. [**Configure source**](#source-configuration): Configure and test connectivity and credentials for each source.\n1. [**Select data**](#dataset): Select schemas, tables, and columns for each source.\n1. [**Add transformations**](#transformations): Add jobs and assign them to sources.\n1. [**Review & deploy**](#review-and-deploy): Review all sources and deploy the pipeline."},{"id":"settings","title":"Settings","role":"configuration","text":"1. Select the target from the **Target database** list. All sources write to this database.\n\n    ![The target database list in pipeline Settings.](https://redis.io/docs/latest/images/rc/rdi/rdi-choose-target.png)\n\n1. Select **Hash** or **JSON** as the **Default data structure**. Transformation jobs can override how individual records are written.\n1. If needed, configure **Processor properties**. These apply to the whole pipeline, not to an individual source. See the [processor configuration reference](https://redis.io/docs/latest/integrate/redis-data-integration/reference/config-yaml-reference#processors-data-processing-configuration).\n\n    ![The processor advanced properties editor with key and value fields.](https://redis.io/docs/latest/images/rc/rdi/rdi-processor-advanced-properties.png)\n\n1. Select **Continue**.\n\nRDI Cloud uses the Flink processor."},{"id":"pipeline-setup","title":"Add sources","role":"content","text":"1. Select a source database type: **MySQL**, **MariaDB**, **Oracle**, **SQL Server**, **PostgreSQL**, **MongoDB**, or **Snowflake** (Preview).\n1. Enter a unique **Source name**, for example `inventory-mysql`. Use up to 22 characters: lowercase letters, numbers, and hyphens. Start with a lowercase letter and end with a letter or number. The names `rdi`, `source`, `target`, and `api` cannot be used for sources.\n1. To include another source, select **Add source** in the **Sources** list and repeat these steps. You can combine different database types or add several sources of the same type.\n1. Select **Continue**.\n\nThe source name identifies the source in the pipeline configuration and transformation jobs; it is not just a display label. These naming rules were introduced with RDI 2.0.0. Sources that existed before the upgrade retain their names, even if they do not meet these rules.\n\nSelect a source in the **Sources** list to configure its connection and dataset. Each source has separate progress indicators for its details, configuration, and data selection."},{"id":"source-configuration","title":"Configure source","role":"configuration","text":"Repeat the following configuration for every source. The **Configure source** step has three expandable sections:\n\n1. [Source connectivity](#source-connectivity)\n1. [Secrets](#secrets)\n1. [Source configuration](#source-configuration-section)\n\nComplete the sections and select **Test source**. Correct any reported errors before continuing. Select each other source and test its configuration, then select **Continue** to open **Select data**."},{"id":"source-connectivity","title":"Source connectivity","role":"content","text":"Choose **AWS Private Link** or **Public Endpoint** for the selected source, according to its [connectivity requirements](https://redis.io/docs/latest/operate/rc/rdi#prerequisites).\n\n- For **AWS Private Link**, enter the **Private Link service name** from your [endpoint service](https://redis.io/docs/latest/operate/rc/rdi/setup#set-up-connectivity). Select **Connect to Private Link** and wait for connectivity to complete. If the connection fails, check the service name and its allowed principal.\n\n    ![AWS Private Link connectivity with the service name and Connect to Private Link control.](https://redis.io/docs/latest/images/rc/rdi/rdi-source-configuration-source-connectivity-privatelink.png)\n\n- For **Public Endpoint**, enter the source IP address or hostname. Add the Redis Cloud outbound IP address shown in the console to your source database's allowlist.\n\n    ![Public endpoint connectivity with source hostname and Redis Cloud outbound IP addresses.](https://redis.io/docs/latest/images/rc/rdi/rdi-source-configuration-source-connectivity-public.png)\n\nConfigure connectivity for each source separately. Sources in the same pipeline can use different connectivity methods.\n\nFor shared endpoint services, separate source networks, or multiple subscriptions, see [Plan source connectivity](https://redis.io/docs/latest/operate/rc/rdi/networking)."},{"id":"secrets","title":"Secrets","role":"content","text":"Enter the Amazon Resource Name (ARN) of the selected source's [database credentials secret](https://redis.io/docs/latest/operate/rc/rdi/setup#create-database-credentials-secrets) in **Credentials Secret ARN**.\n\n![The Credentials Secret ARN field, transit security options, and Validate control.](https://redis.io/docs/latest/images/rc/rdi/rdi-source-configuration-secrets.png)\n\nFor Snowflake, select the authentication method matching your secret:\n\n- **Password**: The credentials secret contains `username` and `password`.\n- **Key-pair**: The credentials secret contains `username`; any `password` is ignored. Provide the private key secret ARN. The private key is stored in plain text PEM format.\n\nUnder **Transit security**, select the mode required by your source:\n\n- **TLS** (Transport Layer Security): Provide the CA certificate secret ARN when your source requires it.\n\n    ![TLS transit security with the CA Certificate Secret ARN field.](https://redis.io/docs/latest/images/rc/rdi/rdi-define-tls.png)\n\n- **mTLS** (mutual TLS): Provide the CA certificate, client certificate, and client private key secret ARNs. Also provide the client key passphrase secret ARN if the key is encrypted.\n\n    ![mTLS transit security with certificate, private key, and optional password secret ARN fields.](https://redis.io/docs/latest/images/rc/rdi/rdi-define-mtls.png)\n\nSelect **Validate** to check access to the selected source's secrets. Repeat this for each source. The AWS secret contents and permissions are described in [Share source database credentials](https://redis.io/docs/latest/operate/rc/rdi/setup#share-source-database-credentials)."},{"id":"source-configuration-section","title":"Source configuration","role":"content","text":"Enter the selected source's database settings. The fields depend on the database type and can include:\n\n- **Port**: The source database port.\n- **Database(s)**: The database name, or a comma-separated list for source types that support multiple databases.\n- **Database Server ID**: A replication client ID for MySQL or MariaDB. Use a different ID for each collector connecting to the same database server.\n- **PDB**: The Oracle pluggable database.\n- **Connection string**: The MongoDB connection information.\n\n![Source-specific database, port, and collector properties.](https://redis.io/docs/latest/images/rc/rdi/rdi-2-source-configuration.png)\n\nUse **Collector properties** for additional source and sink settings. These settings apply to the selected source. See the [collector source properties](https://redis.io/docs/latest/integrate/redis-data-integration/reference/config-yaml-reference#sourcesadvancedsource-advanced-source-settings) and [collector sink properties](https://redis.io/docs/latest/integrate/redis-data-integration/reference/config-yaml-reference#sourcesadvancedsink-rdi-collector-stream-writer-configuration).\n\nFor Debezium sources, set `snapshot.mode` in **Collector source properties** to\ncontrol data snapshots. The default mode is `initial`. Set it for each source separately;\navailable modes depend on the source database. See [Choose a snapshot mode](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config#choose-a-snapshot-mode).\nWith `when_needed` mode, an unavailable saved position can trigger a new snapshot, but\n[missed deletes can leave stale target records](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config#missed-deletes).\nThis property does not configure the Snowflake collector.\n\n![The Edit advanced properties control under Collector properties.](https://redis.io/docs/latest/images/rc/rdi/rdi-source-configuration-collector-properties.png)\n\n![The advanced properties dialog with separate collector source and sink properties.](https://redis.io/docs/latest/images/rc/rdi/rdi-advanced-properties.png)\n\nFor PostgreSQL, [configure the publication properties](https://redis.io/docs/latest/operate/rc/rdi/setup#postgresql-publications)\nbefore deployment. Use `disabled` for an administrator-managed table-list publication, or\n`filtered` with the required permissions for RDI to manage it."},{"id":"dataset","title":"Select data","role":"content","text":"Select each source in the **Sources** list and choose the data to ingest from that source.\n\nFor PostgreSQL, this selection does not narrow an existing `FOR ALL TABLES` publication.\nIf your administrator manages the publication, update its table list to match the selection.\nSee [PostgreSQL publications](https://redis.io/docs/latest/operate/rc/rdi/setup#postgresql-publications).\n\n> [!WARNING]\n> Do not write data directly to keys managed by RDI. Changes from another application can cause transformation failures or data inconsistencies, and RDI can overwrite them. A pipeline reset does not flush the target database. **Flush target database** is a separate action that deletes all target data, including data written outside RDI. See [Data recovery](https://redis.io/docs/latest/operate/rc/rdi/faq#data-recovery).\n\n1. Select a schema in **Schemas** to see its tables.\n1. Select the tables to ingest in **Tables**.\n\n    ![Selecting a schema shows its tables for ingestion.](https://redis.io/docs/latest/images/rc/rdi/rdi-dataset-schema-selected.png)\n\n1. Select a table to see its **Columns**, then select the columns to ingest.\n\n    ![Selecting a table shows its columns and the columns selected for ingestion.](https://redis.io/docs/latest/images/rc/rdi/rdi-select-columns.png)\n\n1. If a table has no unique key, use the key control next to the column that identifies each record. Review any missing-key warnings before continuing.\n\n    ![The missing unique key warning with the affected table and its columns.](https://redis.io/docs/latest/images/rc/rdi/rdi-dataset-missing-unique-key.png)\n\n    ![The key control beside the column used to identify a record.](https://redis.io/docs/latest/images/rc/rdi/rdi-unique-key-selected.png)\n\n    For PostgreSQL, choosing a key here does not create a primary key or replica identity\n    in the source database. [Configure replica identity](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/postgresql#check-replica-identity-and-choose-a-publication-mode)\n    separately for tables that publish updates or deletes.\n\n1. Repeat for the other sources, then select **Continue**.\n\nThe available schema, table, and column controls depend on the source type. Each source keeps its own selection, including when two sources contain tables with the same name."},{"id":"transformations","title":"Add transformations","role":"content","text":"Transformation jobs are optional. Without a matching job, RDI writes records using the pipeline's default data structure.\n\n1. Select **Upload jobs** to upload the [transformation job files](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples) needed for your selected tables.\n1. Check the **Source name** assignment for each job. In a multi-source pipeline, `source.server_name` identifies the source the job reads from. Select the source name you chose during setup.\n1. Review each job's validation status and correct errors.\n\n    ![Example transformation jobs with their source assignments and validation status.](https://redis.io/docs/latest/images/rc/rdi/rdi-2-transformation-jobs.png)\n\n1. Select **Continue to review & deploy**.\n\nFor the Flink processor, source matchers can use lists or entries prefixed with `regex:` to select several tables. Jobs must not overlap on the same table. See [Transformation examples](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples)."},{"id":"review-and-deploy","title":"Review and deploy","role":"content","text":"Review every source's connection, selected data, and transformation jobs, together with the shared target and settings. Use the source actions to return to a section that needs changes.\n\nSelect **Deploy pipeline** to start the pipeline. With the default `initial` mode, each Debezium source without a saved position takes a snapshot and then captures ongoing changes. Other [snapshot modes](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config#choose-a-snapshot-mode) can skip existing rows or omit ongoing change capture. Snapshot duration depends on the amount of selected data and source performance.\n\n![The Deploy pipeline button.](https://redis.io/docs/latest/images/rc/rdi/rdi-confirm-deploy.png)\n\nOpen the pipeline's [Dashboard and Metrics tabs](https://redis.io/docs/latest/operate/rc/rdi/view-edit) to follow progress for each source."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/rdi/faq","title":"RDI Cloud FAQ","url":"https://redis.io/docs/latest/operate/rc/rdi/faq/","summary":"Answers to common questions about RDI on Redis Cloud.","tags":["docs","operate","rc"],"last_updated":"2026-10-21T08:57:20+03:00","page_type":"content","content_hash":"cc80f889a395d36f0bd34582e7b8d16f474a63c31d5600a9fe94e9eafdef8496","sections":[{"id":"data-recovery","title":"Data recovery","role":"overview","text":""},{"id":"reset-pipeline","title":"What happens when I reset the pipeline?","role":"content","text":"A pipeline reset clears the internal RDI state for all sources, including their saved positions. When the pipeline runs, each source follows its configured initial data load settings. With the default `initial` mode, Debezium sources take new snapshots, apply the current transformations, and resume streaming. Other [snapshot modes](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config#choose-a-snapshot-mode) can skip existing rows or omit ongoing change capture. If you reset a stopped pipeline, it remains stopped until you start it.\n\nA reset does **not** flush the target Redis database. Records already in the target remain until RDI overwrites or deletes them through normal processing. Keys that are no longer produced by the current dataset or transformations can remain in the target after a reset. For example, changing a transformation's key prefix and resetting creates keys with the new prefix without deleting keys with the old prefix.\n\nSee [Reset data pipeline](https://redis.io/docs/latest/operate/rc/rdi/view-edit#reset-data-pipeline) for the steps."},{"id":"missed-deletes","title":"Why can records remain after a recovery snapshot?","role":"content","text":"A new snapshot can leave stale records in Redis. If a source row was deleted while\ncollection was interrupted and the required log history expired, the snapshot cannot\nreplay its delete event. This applies to Debezium sources with `type: cdc`, including\nautomatic snapshots with `when_needed` and snapshots after a manual reset.\n\nThe sequence is:\n\n1. A source row is copied to Redis.\n1. Collection stops, and the collector saves its source position.\n1. The source deletes the row while Redis still has it.\n1. The saved position becomes unavailable, so the delete cannot be replayed.\n1. `when_needed` mode takes a new snapshot. The deleted row is absent.\n1. Streaming resumes, but Redis still has the stale row because its delete was missed.\n\nAn interruption alone does not cause this gap. The saved position must become\nunavailable. The snapshot does not clean up the target, and a reset does not flush it.\nSee the [detailed missed-delete sequence](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config#missed-deletes)\nand [recovery guidance](https://redis.io/docs/latest/integrate/redis-data-integration/troubleshooting#unavailable-source-history)."},{"id":"what-happens-when-i-flush-the-target-database","title":"What happens when I flush the target database?","role":"content","text":"**Flush target database** permanently deletes all data from the shared target, including data from every source and data written outside RDI. Stop the pipeline before flushing. The source databases are not changed.\n\nFlushing does not clear RDI's saved source positions. Starting a Debezium source follows its [snapshot mode](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config#choose-a-snapshot-mode). With the default `initial` mode and a valid saved position, it resumes streaming without reloading unchanged source rows.\n\nSee [Flush the target database](https://redis.io/docs/latest/operate/rc/rdi/view-edit#flush-the-target-database)."},{"id":"reload-after-flush","title":"How do I reload data after a flush?","role":"content","text":"Before reloading, check each source's initial data load settings. For Debezium sources,\nselect a [snapshot mode](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config#choose-a-snapshot-mode) that loads existing rows, such as\n`initial`. Modes that skip data snapshots cannot refill the target's existing rows.\n\n1. Wait for the flush to finish.\n1. [Reset the pipeline](https://redis.io/docs/latest/operate/rc/rdi/view-edit#reset-data-pipeline) while it is stopped, and wait for the reset to finish.\n1. [Start the pipeline](https://redis.io/docs/latest/operate/rc/rdi/view-edit#stop-and-restart-data-pipeline) to take new snapshots of the selected data from all sources.\n1. Check each source's initial sync progress and record counts on the **Dashboard** and **Metrics** tabs. Wait for initial sync to finish before relying on the target as a complete copy of the selected data.\n\nRDI reloads data available in the source databases using the current dataset and transformation settings. It cannot restore data that existed only in the target."},{"id":"multiple-sources","title":"Multiple sources","role":"content","text":""},{"id":"reset-one-source","title":"What happens when I reset one source?","role":"content","text":"RDI clears the selected source's internal state, including its saved position. With the default `initial` mode, a Debezium source takes a new data snapshot. Other [snapshot modes](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config#choose-a-snapshot-mode) can skip existing rows or omit ongoing change capture. The whole pipeline, including the other sources and the processor, restarts. Other sources keep their saved positions but follow their configured modes on restart; `always` mode takes another snapshot even with a preserved position.\n\nAll records already in the target remain, including records from the reset source. The new snapshot can overwrite that source's records. Resetting a source does not selectively delete its target data.\n\nSee [Reset a source](https://redis.io/docs/latest/operate/rc/rdi/view-edit#reset-source)."},{"id":"flush-one-source","title":"Can I flush data for a single source?","role":"content","text":"Currently, RDI cannot flush target data for a single source. Connect to the target Redis database and selectively delete the records you want to remove. Identify them from your key naming and transformation rules, and check that other sources do not write to the same keys. Do not use **Flush target database** for this purpose: it deletes data for all sources.\n\nStop the affected source before deleting its records and allow its pending records to finish processing. Starting the source resumes ingestion, and later changes can recreate records. To reload all its selected data, select a mode that takes a data snapshot, then [reset that source](https://redis.io/docs/latest/operate/rc/rdi/view-edit#reset-source)."},{"id":"does-deleting-a-source-delete-its-target-data","title":"Does deleting a source delete its target data?","role":"content","text":"No. Deleting a source removes its pipeline configuration and internal RDI state. Records it already wrote to the target remain. You must remove or reassign transformation jobs that refer to the source before deleting it. See [Remove a source](https://redis.io/docs/latest/operate/rc/rdi/view-edit#remove-source)."},{"id":"capacity-and-network-planning","title":"Capacity and network planning","role":"content","text":""},{"id":"what-cidr-range-should-i-use-for-cloud-rdi","title":"What CIDR range should I use for Cloud RDI?","role":"content","text":"Plan the Classless Inter-Domain Routing (CIDR) range for all pipelines in the\nworkspace, including future sources and processor replicas. You cannot enlarge\nit after creation. See [Plan workspace network capacity](https://redis.io/docs/latest/operate/rc/rdi/scale-pipeline#plan-workspace-network-capacity)\nfor the `/22`, `/21`, and `/20` capacity table and its configuration."},{"id":"change-workspace-cidr","title":"How do I change a workspace CIDR that is too small?","role":"content","text":"You cannot change or enlarge the CIDR of an existing workspace in place.\nTo use a larger range, [delete the existing workspace](https://redis.io/docs/latest/operate/rc/rdi/create-workspace#delete-workspace),\n[create a workspace](https://redis.io/docs/latest/operate/rc/rdi/create-workspace#create-a-data-integration-workspace)\nwith the larger CIDR, and redeploy your pipelines.\n\nBefore you delete anything, record your pipeline configuration, source settings,\ntransformation jobs, and secret references. Recreation interrupts ingestion\nand removes the saved pipeline state. Redeployed sources take new initial\nsnapshots. Records already written to the target database remain there.\n\nFor help planning the recreation and connectivity changes, open a\n[Redis support ticket](https://redis.io/support/) before deleting anything. Include\nyour subscription and workspace identifiers, current CIDR, and planned totals for\nsources and processor replicas across all pipelines."},{"id":"upgrades-and-maintenance","title":"Upgrades and maintenance","role":"content","text":""},{"id":"what-happens-during-an-rdi-cloud-upgrade","title":"What happens during an RDI Cloud upgrade?","role":"content","text":"Redis manages RDI upgrades in Redis Cloud. Maintenance follows your Redis Cloud Pro subscription's [maintenance window](https://redis.io/docs/latest/operate/rc/rdi#maintenance-windows).\n\nDuring an upgrade, monitoring may be temporarily unavailable, and ingestion pauses while the pipeline components restart. With the default `initial` mode and valid saved positions, Debezium sources resume streaming and process the accumulated changes. Other [snapshot modes](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config#choose-a-snapshot-mode) or unavailable source history can change this behavior.\n\nA routine upgrade does not flush the target database or require you to reset the pipeline. Existing target records remain available, but they may temporarily lag behind the source data."},{"id":"billing","title":"Billing","role":"content","text":""},{"id":"how-is-rdi-usage-calculated","title":"How is RDI usage calculated?","role":"content","text":"RDI usage is measured in RDI processing units (RPUs). The usage consists of:\n\n| Component | RPUs |\n| :-- | --: |\n| Workspace with a deployed pipeline | 1 |\n| Each running source collector | 2 |\n| Each running processor replica | 2 |\n\nUsage is measured every minute and billed using the peak usage in each hour. Adding sources or processor replicas increases usage. Initial sync and streaming use the same RPU rate. The price per RPU depends on your region and pricing plan. Contact your Redis account team for the applicable rate.\n\nFor example, one workspace with one source and one processor replica uses `1 + 2 + 2 = 5 RPUs`. With two sources and one processor replica, it uses `1 + 4 + 2 = 7 RPUs`. Adding a second processor replica increases that to `1 + 4 + 4 = 9 RPUs`.\n\nThese examples cover RDI usage. Target Redis database and applicable network charges are separate."},{"id":"stopping-and-billing","title":"How does stopping sources or the pipeline affect billing?","role":"content","text":"Stopping a source removes its collector usage, but the processor and workspace remain billable. Stopping every source individually still leaves the processor running. Stopping the whole pipeline also stops the processor, but the workspace charge continues while the pipeline remains deployed.\n\nThe following examples assume one workspace, two sources, and one processor replica. Each state must apply for a full billing hour after the stop has completed:\n\n| State | Workspace RPUs | Collector RPUs | Processor RPUs | Total RPUs |\n| :-- | --: | --: | --: | --: |\n| Both sources running | 1 | 4 | 2 | 7 |\n| One source stopped | 1 | 2 | 2 | 5 |\n| Both sources stopped individually; pipeline still running | 1 | 0 | 2 | 3 |\n| Whole pipeline stopped | 1 | 0 | 0 | 1 |\n\nA brief stop might not reduce the charge for that hour. For example, if usage reaches 7 RPUs before you stop one source, the hourly peak remains 7 RPUs. A later full hour with that source stopped is billed at 5 RPUs, assuming the other resources stay the same."},{"id":"am-i-charged-if-no-source-records-change","title":"Am I charged if no source records change?","role":"content","text":"Yes. RDI usage is based on the running collectors and processor replicas, not the number of records processed. An idle streaming pipeline still uses resources."},{"id":"when-does-rdi-billing-start-and-stop","title":"When does RDI billing start and stop?","role":"content","text":"Creating a workspace or saving a setup draft does not start RDI usage billing. Billing starts when a pipeline is deployed. Stopping the pipeline reduces usage to the workspace charge. Deleting the deployed pipeline ends new RDI usage when no deployed pipelines remain in the workspace; usage already recorded for the hour can still be billed.\n\nDelete an unused pipeline and then [delete its workspace](https://redis.io/docs/latest/operate/rc/rdi/create-workspace#delete-workspace) when you no longer need RDI. This does not delete the target Redis database or stop its separate charges."},{"id":"processor-scaling","title":"Processor scaling","role":"content","text":""},{"id":"does-cloud-rdi-automatically-scale-the-processor","title":"Does Cloud RDI automatically scale the processor?","role":"content","text":"No. Set the desired processor replica count with\n`advanced.resources.taskManager.replicas`. See [Increase processor capacity](https://redis.io/docs/latest/operate/rc/rdi/scale-pipeline#increase-processor-capacity)."},{"id":"how-do-i-increase-processing-capacity-for-a-pipeline","title":"How do I increase processing capacity for a pipeline?","role":"content","text":"First [identify the bottleneck](https://redis.io/docs/latest/operate/rc/rdi/scale-pipeline#identify-the-bottleneck).\nThen tune the collector, [RDI database](https://redis.io/docs/latest/operate/rc/rdi/scale-pipeline#increase-rdi-database-throughput),\nor processor. Check shared\nworkspace network capacity before adding sources or processor replicas."},{"id":"can-i-see-that-the-processor-has-scaled-in-the-console","title":"Can I see that the processor has scaled in the console?","role":"content","text":"Yes. The **Dashboard** shows running and configured processor replicas next to\nthe processor status. See [Confirm the applied capacity](https://redis.io/docs/latest/operate/rc/rdi/scale-pipeline#confirm-the-applied-capacity)\nfor how to read the count and handle a persistent mismatch."},{"id":"can-i-use-billing-to-confirm-a-scaling-change","title":"Can I use billing to confirm a scaling change?","role":"content","text":"No. Billing does not show scaling changes in real time. Use the Dashboard or\nthe [Redis Cloud application programming interface (API)](https://redis.io/docs/latest/operate/rc/rdi/scale-pipeline#confirm-the-applied-capacity)\nto confirm the running count."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/rdi","title":"Data Integration","url":"https://redis.io/docs/latest/operate/rc/rdi/","summary":"Use Redis Data Integration with Redis Cloud.","aliases":["/operate/rc/databases/rdi/","/operate/rc/databases/rdi"],"tags":["docs","operate","rc"],"last_updated":"2026-10-12T16:35:02+03:00","children":[{"id":"operate/rc/rdi/quick-start","summary":"Learn how to create a data pipeline between a PostgreSQL source database created with Terraform and a Redis Cloud target database.","title":"RDI on Redis Cloud quick start","url":"https://redis.io/docs/latest/operate/rc/rdi/quick-start/"},{"id":"operate/rc/rdi/create-workspace","summary":"Create and manage the infrastructure for your Data Integration pipelines.","title":"Create and manage Data Integration workspace","url":"https://redis.io/docs/latest/operate/rc/rdi/create-workspace/"},{"id":"operate/rc/rdi/setup","summary":"Prepare your source database, network setup, and database credentials for Data integration.","title":"Prepare source database","url":"https://redis.io/docs/latest/operate/rc/rdi/setup/"},{"id":"operate/rc/rdi/define","summary":"Connect one or more source databases to a Redis Cloud target and define your data pipeline.","title":"Create data pipeline","url":"https://redis.io/docs/latest/operate/rc/rdi/define/"},{"id":"operate/rc/rdi/networking","summary":"Network reference for connecting a Data Integration pipeline to your source database.","title":"Networking","url":"https://redis.io/docs/latest/operate/rc/rdi/networking/"},{"id":"operate/rc/rdi/view-edit","summary":"Observe and change your data pipeline.","title":"View and edit data pipeline","url":"https://redis.io/docs/latest/operate/rc/rdi/view-edit/"},{"id":"operate/rc/rdi/scale-pipeline","summary":"Increase collector, RDI database, and processor capacity for a Cloud RDI pipeline.","title":"Scale a Cloud RDI pipeline","url":"https://redis.io/docs/latest/operate/rc/rdi/scale-pipeline/"},{"id":"operate/rc/rdi/faq","summary":"Answers to common questions about RDI on Redis Cloud.","title":"RDI Cloud FAQ","url":"https://redis.io/docs/latest/operate/rc/rdi/faq/"},{"id":"operate/rc/rdi/use-cases","summary":"Reference setups for Data Integration pipelines that run in production.","title":"Production use cases","url":"https://redis.io/docs/latest/operate/rc/rdi/use-cases/"},{"id":"operate/rc/rdi/rds-proxy","summary":"Set up RDS Proxy for Redis Data Integration (not recommended).","title":"RDS Proxy setup for RDI","url":"https://redis.io/docs/latest/operate/rc/rdi/rds-proxy/"}],"page_type":"content","content_hash":"26d1480de000724846f38541821163df450b4ca5a69a461e9649f37cf38f518b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Cloud now supports [Redis Data Integration (RDI)](https://redis.io/docs/latest/integrate/redis-data-integration), a fast and simple way to bring your data into Redis from other types of primary databases.\n\n![The Data Integration page in the Redis Cloud console, with the Create workspace button and supported source databases.](https://redis.io/docs/latest/images/rc/rdi-get-started.png)\n\nA relational database usually handles queries much more slowly than a Redis database. If your application uses a relational database and makes many more reads than writes (which is the typical case) then you can improve performance by using Redis as a cache to handle the read queries quickly. Redis Cloud uses [ingest](https://redis.io/docs/latest/integrate/redis-data-integration) to help you offload all read queries from the application database to Redis automatically.\n\nUsing a data pipeline lets you have a cache that is always ready for queries. RDI Data pipelines ensure that any changes made to your primary database are captured in your Redis cache within a few seconds, preventing cache misses and stale data within the cache. \n\nRDI helps Redis customers sync Redis Cloud with live data from their primary databases to:\n- Meet the required speed and scale of read queries and provide an excellent and predictable user experience.\n- Save resources and time when building pipelines and coding data transformations.\n- Reduce the total cost of ownership by saving money on expensive database read replicas.\n\nUsing RDI with Redis Cloud simplifies managing your data integration pipeline. No need to worry about hardware or underlying infrastructure, as Redis Cloud manages that for you. Creating the data flow from source to target is much easier, and there are validations in place to reduce errors."},{"id":"is-rdi-a-good-fit-for-my-architecture","title":"Is RDI a good fit for my architecture?","role":"content","text":"RDI is designed to support apps that must use a disk-based database as the system of record\nbut must also be fast and scalable. This is a common requirement for mobile and web\napps with a rapidly-growing number of users; the performance of the main database is fine at first\nbut it will soon struggle to handle the increasing demand without a cache.\n\nUse the information in the sections below to determine whether RDI is a good fit for your architecture. See also the\n[decision tree for using RDI](https://redis.io/docs/latest/integrate/redis-data-integration/when-to-use#decision-tree-for-using-rdi)\nwhich presents the considerations in a straightforward question-and-answer format.\n\n"},{"id":"when-to-use-rdi","title":"When to use RDI","role":"content","text":"RDI is a good fit when:\n\n- You want your app/micro-services to read from Redis to scale reads at speed.\n- You want to transfer data to Redis from one or more source databases.\n- You must use a slow database as the system of record for the app.\n- The app must always *write* its data to the slow database.\n- Your app can tolerate *eventual* consistency of data in the Redis cache.\n- You want a self-managed solution or AWS based solution.\n- The source data changes frequently in small increments.\n- The source database has no more than 20K changes per second.\n- RDI throughput during [full sync](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines#pipeline-lifecycle)\n  stays below 60K records per second, assuming an average record size of 1KB and a pipeline without transformations.\n- RDI throughput during [CDC](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines#pipeline-lifecycle)\n  stays below 20K records per second, assuming an average record size of 1KB and a pipeline without transformations.\n- The total data size is no larger than 200GB, so a full sync completes in under an hour without exceeding the throughput\n  limits above. RDI can ingest larger datasets, but it will take longer than an hour.\n- You don’t need to perform join operations on the data from several tables\n  into a [nested Redis JSON object](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/data-denormalization#joining-one-to-many-relationships).\n- RDI supports the [data transformations](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples) you need for your app.\n- Your data caching needs are too complex or demanding to implement and maintain yourself.\n- Your database administrator has reviewed RDI's requirements for the source database and\n  confirmed that they are acceptable."},{"id":"when-not-to-use-rdi","title":"When not to use RDI","role":"content","text":"RDI is not a good fit when:\n\n- You are migrating an existing data set into Redis only once.\n- Your app needs *immediate* cache consistency (or a hard limit on latency) rather\n  than *eventual* consistency.\n- You need *transactional* consistency between the source and target databases.\n- The app must *write* data to the Redis cache, which then updates the source database\n  (write-behind/write-through patterns).\n- Your data set will only ever be small.\n- Your data is updated by some batch or ETL process with long and large transactions - RDI will fail\n  processing these changes.\n- You need complex stream processing of data (aggregations, sliding window processing, complex \n  custom logic).\n- You need to write data to multiple targets from the same pipeline (Redis supports other\n  ways to replicate data across Redis databases such as replicaOf and Active-Active).\n- Your database administrator has rejected RDI's requirements for the source database."},{"id":"data-pipeline-architecture","title":"Data pipeline architecture","role":"content","text":"An RDI data pipeline connects one or more source databases to one target Redis database. Sources can use the same or different supported database types. Each source has its own collector, connectivity, credentials, and selection of tables and columns. All sources share the pipeline's processor and target.\n\nEach source first imports its selected data during the *initial sync* phase, then captures changes during the *streaming* phase. The pipeline transforms the captured records and writes them to Redis. You can monitor and manage each source from the pipeline dashboard.\n\nRDI Cloud uses the Flink processor for all pipelines.\n\nFor more info on how RDI works, see [RDI Architecture](https://redis.io/docs/latest/integrate/redis-data-integration/architecture)."},{"id":"pipeline-security","title":"Pipeline security","role":"content","text":"Data pipelines are set up to ensure a high level of data security. Source database credentials and TLS secrets are stored in AWS secret manager and shared using the Kubernetes CSI driver for secrets. See [Share source database credentials](https://redis.io/docs/latest/operate/rc/rdi/setup#share-source-database-credentials) to learn how to share your source database credentials and TLS certificates with Redis Cloud.\n\nConfigure connectivity separately for each source. A source can use a public endpoint or [AWS PrivateLink](https://aws.amazon.com/privatelink/), subject to the source-specific requirements in [Prerequisites](#prerequisites). See [Set up connectivity](https://redis.io/docs/latest/operate/rc/rdi/setup#set-up-connectivity) to learn how to connect your PrivateLink to the Redis Cloud VPC.\n\nRDI encrypts all network connections with TLS. The pipeline will process data from the source database in-memory and write it to the target database using a TLS connection. There are no external connections to your data pipeline except from Redis Cloud management services."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before you can create a data pipeline, you must have:\n\n- A [Redis Cloud Pro database](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new) hosted on Amazon Web Services (AWS). This will be the target database.\n- One or more supported source databases that are publicly accessible or hosted on an AWS EC2 instance, AWS RDS, or AWS Aurora:\n\n| Database | Versions | AWS RDS  Versions |\n|:---|:---|:---|\n| Oracle | 19c, 21c | 19c, 21c |\n| MariaDB | 10.5, 11.4.3 | 10.4 to 10.11, 11.4.3 |\n| MySQL | 5.7, 8.0.x, 8.2 | 8.0.x |\n| PostgreSQL | 10, 11, 12, 13, 14, 15, 16, 17, 18 | 11, 12, 13, 14, 15, 16, 17, 18 |\n| Supabase (uses PostgreSQL) | 10, 11, 12, 13, 14, 15, 16, 17 | - |\n| AWS Aurora PostgreSQL | 15 | 15 |\n| SQL Server | 2017, 2019, 2022 | 2016, 2017, 2019, 2022 |\n| MongoDB | 6.0, 7.0, 8.0 | - |\n| MongoDB Atlas | 6.0, 7.0, 8.0 | - |\n| Snowflake | - | - |\n\n\n> [!NOTE]\n> Please be aware of the following limitations:\n>\n> - The target database must be a Redis Cloud Pro database hosted on Amazon Web Services (AWS). Redis Cloud Essentials databases and databases hosted on Google Cloud do not support Data Integration.\n> - The target database must use [high availability](https://redis.io/docs/latest/operate/rc/databases/configuration/high-availability). It can use either single-zone or multi-zone high availability.\n> - The target database can use TLS, but can not use mutual TLS.\n> - The target database can't use Active-Active topology.\n> - If your source database is not publicly accessible, or if it is a MongoDB Atlas or Snowflake database, it must be hosted on AWS.\n> - You must use a [custom encryption key on AWS](https://docs.aws.amazon.com/kms/latest/developerguide/create-keys.html) to create the instance hosting the database.\n> - Each pipeline has one target database shared by all of its sources.\n> - If the source database is not publicly accessible, you must be able to set up AWS PrivateLink to connect your source database to your target database. RDI only works with AWS PrivateLink and not VPC Peering or other private connectivity options.\n> - Mutual TLS is not supported for AWS RDS and AWS Aurora source databases."},{"id":"get-started","title":"Get started","role":"content","text":"To get started fast with RDI on Redis Cloud, see the [RDI Cloud quick start](https://redis.io/docs/latest/operate/rc/rdi/quick-start) to create a data pipeline between a PostgreSQL source database and a Redis Cloud target database.\n\nTo create a new data pipeline, you need to:\n\n1. [Create a Data Integration workspace](https://redis.io/docs/latest/operate/rc/rdi/create-workspace) for your Pro subscription.\n1. [Prepare each source database](https://redis.io/docs/latest/operate/rc/rdi/setup) and any associated credentials.\n1. [Define the source connection and data pipeline](https://redis.io/docs/latest/operate/rc/rdi/define) by selecting which tables to sync.\n\nOnce your data pipeline is defined, you can [view and edit](https://redis.io/docs/latest/operate/rc/rdi/view-edit) it.\n\nFor complete production setups, including SQL Server failover handling, see [Production use cases](https://redis.io/docs/latest/operate/rc/rdi/use-cases).\n\nTo plan workspace capacity and scale a pipeline, see [Scale a Cloud RDI pipeline](https://redis.io/docs/latest/operate/rc/rdi/scale-pipeline)."},{"id":"billing-and-common-questions","title":"Billing and common questions","role":"content","text":"See the [RDI Cloud FAQ](https://redis.io/docs/latest/operate/rc/rdi/faq) for billing examples, reset and flush behavior, and working with multiple sources."},{"id":"maintenance-windows","title":"Maintenance windows","role":"content","text":"RDI Cloud maintenance follows the same subscription-wide maintenance window as your Redis Cloud Pro subscription. During a maintenance window, your data pipeline may experience brief interruptions as Redis applies updates.\n\nTo control when maintenance occurs, [set a manual maintenance window](https://redis.io/docs/latest/operate/rc/subscriptions/maintenance/set-maintenance-windows) for your Redis Cloud Pro subscription. Any maintenance window you configure applies to both your databases and your RDI data pipeline."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/rdi/networking/aws-privatelink","title":"AWS PrivateLink reference","url":"https://redis.io/docs/latest/operate/rc/rdi/networking/aws-privatelink/","summary":"How traffic flows over AWS PrivateLink between a Data Integration pipeline and your source database, and how to keep the connection available during failover.","tags":["docs","operate","rc"],"last_updated":"2026-10-02T14:53:47+01:00","page_type":"content","content_hash":"15f575a05d85ef9f6c103cd24aadc554bdfa0e877c1da30c075b9de25007eb00","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This page explains how a Data Integration pipeline reaches your source database over AWS PrivateLink, and how to keep the connection available when your database fails over. For the steps to create the PrivateLink connection, see [Set up connectivity](https://redis.io/docs/latest/operate/rc/rdi/setup#set-up-connectivity)."},{"id":"how-traffic-flows","title":"How traffic flows","role":"content","text":"With PrivateLink, the pipeline and your database are in separate address spaces that never mix. Each component only connects to one address, and that address does not change during a failover.\n\n[code example]\n\nThe following table shows which address each component sees:\n\n| Component | Connects to | What it sees |\n|:--|:--|:--|\n| RDI pipeline | The VPC endpoint for your endpoint service | Only the endpoint's addresses, which come from the workspace CIDR. It never sees your database's real IP address. |\n| Network Load Balancer | The registered target (instance or IP address) | Your database's real address. The target group is the only place where that address appears, and the only thing that changes during a failover. |\n| Source database | Nothing. It only receives connections. | Incoming connections from the **NLB's private IP addresses**, and never from Redis Cloud addresses. Allow the NLB subnets in your database's firewall or allow list. |\n\nYour database always receives the connection from the NLB's own private IP address, never from a Redis Cloud address. PrivateLink and the NLB rewrite the source address as the traffic passes through (network address translation), so Redis Cloud addresses are never visible anywhere in your network. The only firewall rule your database needs is to allow connections from the NLB's subnets.\n\nBecause PrivateLink translates addresses instead of routing between the two networks, the workspace CIDR can overlap with your own VPC or on-premises ranges without any conflict. It only needs to be valid on the Redis Cloud side. See [Create a Data Integration workspace](https://redis.io/docs/latest/operate/rc/rdi/create-workspace) for the workspace CIDR requirements."},{"id":"connect-to-a-database-outside-the-vpc","title":"Connect to a database outside the VPC","role":"content","text":"Your source database does not have to run inside the VPC. It can be anywhere your VPC can privately route to, such as an on-premises data center reached over [AWS Direct Connect](https://aws.amazon.com/directconnect/) or a [Site-to-Site VPN](https://docs.aws.amazon.com/vpn/latest/s2svpn/VPC_VPN.html), or another network reachable through your VPC's routing.\n\nThe core rule is the same in every case: the NLB uses an IP address target that is reachable from the VPC, and the database receives the connection from the NLB's private IP address. The database never sees a Redis Cloud address.\n\n[code example]\n\nTo set this up:\n\n- When you [create the network load balancer](https://redis.io/docs/latest/operate/rc/rdi/setup#set-up-connectivity), create a target group with target type **IP addresses** and register the database's IP address.\n- Make sure the VPC can route to that IP address and port, and that the database's firewall or allow list accepts connections from the NLB's subnets."},{"id":"automate-failover","title":"Why failover needs an IP address update","role":"content","text":"The NLB target group points to one database address at a time. If your database moves to another server, its address changes, but the target group still points to the old address, so the pipeline can no longer reach the database. This happens, for example, when a high availability replica is promoted, or when a disaster recovery server is restored from backup with a new IP address.\n\nTo recover, the NLB target group must be updated to point to the new address. This is the only change needed. Nothing changes on the Redis Cloud side: the pipeline keeps connecting to the same VPC endpoint, retries while the path is down, and catches up automatically once the target group points to a healthy server.\n\nHow you update the target group depends on your database:\n\n- **AWS RDS or Aurora**: Use the Lambda function that responds to RDS failover events and updates the target group automatically. To set it up, see [Set up connectivity](https://redis.io/docs/latest/operate/rc/rdi/setup#set-up-connectivity), select the **AWS RDS or Aurora** tab, and follow **Set up Lambda function connectivity**.\n- **Self-managed database (EC2 or on premises)**: Update the target group from your own failover process. You can trigger the update from your database's failover events or from the NLB's health checks."}],"examples":[{"id":"how-traffic-flows-ex0","language":"mermaid {width=\"100%\"}","code":"graph LR\n    subgraph rc[\"Redis Cloud\"]\n        pipeline[\"<b>RDI pipeline</b>\"] --> endpoint[\"<b>VPC endpoint</b>\"]\n    end\n    subgraph vpc[\"Your AWS VPC\"]\n        nlb[\"<b>Network Load Balancer</b><br/>(private IPs)\"] --> db[\"<b>Source database</b>\"]\n    end\n    endpoint -->|PrivateLink| nlb","section_id":"how-traffic-flows"},{"id":"connect-to-a-database-outside-the-vpc-ex0","language":"mermaid {width=\"100%\"}","code":"graph LR\n    subgraph rc[\"Redis Cloud\"]\n        pipeline[\"<b>RDI pipeline</b>\"] --> endpoint[\"<b>VPC endpoint</b>\"]\n    end\n    subgraph vpc[\"Your AWS VPC\"]\n        nlb[\"<b>NLB</b><br/>(IP target)\"]\n    end\n    subgraph net[\"Network reachable from the VPC\"]\n        db[\"<b>Source database</b>\"]\n    end\n    endpoint -->|PrivateLink| nlb\n    nlb -->|Private route| db","section_id":"connect-to-a-database-outside-the-vpc"}]}
{"schema_version":2,"id":"operate/rc/rdi/networking","title":"Networking","url":"https://redis.io/docs/latest/operate/rc/rdi/networking/","summary":"Network reference for connecting a Data Integration pipeline to your source database.","tags":["docs","operate","rc"],"last_updated":"2026-10-12T14:51:21+03:00","children":[{"id":"operate/rc/rdi/networking/aws-privatelink","summary":"How traffic flows over AWS PrivateLink between a Data Integration pipeline and your source database, and how to keep the connection available during failover.","title":"AWS PrivateLink reference","url":"https://redis.io/docs/latest/operate/rc/rdi/networking/aws-privatelink/"}],"page_type":"content","content_hash":"54737521e45887dc646b7154745bf3d6c5a96b20ecc16db6f70ad7f5413d578c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Configure connectivity for each source in your Data Integration pipeline. Sources can share an Amazon Web Services (AWS) PrivateLink endpoint service or use separate services. They can also use public endpoints where the [source requirements](https://redis.io/docs/latest/operate/rc/rdi#prerequisites) permit them."},{"id":"workspaces-sources-and-targets","title":"Workspaces, sources, and targets","role":"content","text":"Create one Data Integration workspace for each Pro subscription that needs a pipeline. All sources in a pipeline write to one target Redis database in that subscription. To write to targets in different subscriptions, set up a workspace and pipeline in each subscription.\n\nRedis Cloud manages the workspace and its virtual private cloud (VPC) endpoints. A public source uses an Internet Protocol (IP) address or hostname. For a customer-managed source, you manage the endpoint service, Network Load Balancer (NLB), and database access rules. The endpoint service must be in the same AWS region as the workspace.\n\nThe example shows two subscriptions connecting to the same customer endpoint service. Subscription A also connects to a separate service and a public hostname. All resources in this example use the same AWS region.\n\n<div style=\"overflow-x: auto;\" role=\"region\" aria-label=\"PrivateLink topology diagram\" tabindex=\"0\">\n\n[code example]\n\n</div>\n\nSource boxes are configured sources in the pipeline, not the source databases. Solid arrows show initiated network connections. Dotted arrows show data passed from each source to its shared processor. The processor writes the data to the pipeline's Redis target. Source 4 connects directly to a public IP address or hostname, without PrivateLink. The listener ports are examples. A listener can forward to a different database port."},{"id":"choose-your-connectivity-setup","title":"Choose your connectivity setup","role":"content","text":"| Your setup | What you configure |\n|:--|:--|\n| Several databases behind one NLB | Use a separate listener port and target group for each database. Enter the same endpoint service name for each source, with its matching listener port. Redis Cloud reuses the workspace's endpoint for that service. |\n| Databases in separate networks | Use separate endpoint services when one NLB cannot privately reach all databases. Configure the matching service for each source. Your source networks do not need to connect to each other. |\n| The same source feeds several subscriptions | You can reuse your endpoint service and NLB. Each workspace needs its own VPC endpoint connection. Allow the principal shown for each workspace and accept each new connection. Configure and test the source in each pipeline. |\n| Private and public sources in one pipeline | Choose connectivity separately for each source. For public sources, allow the outbound IP addresses shown in that workspace's console. |\n\nSharing an endpoint service does not share source configuration. Each source still needs its own connection details, credentials, and data selection. A second subscription does not reuse the first workspace's VPC endpoint."},{"id":"set-up-customer-managed-privatelink","title":"Set up customer-managed PrivateLink","role":"content","text":"1. [Create the NLB and endpoint service](https://redis.io/docs/latest/operate/rc/rdi/setup#set-up-connectivity). For shared connectivity, add a listener and target group for each database.\n1. Allow the Redis Cloud principal shown in the console for each workspace that will connect.\n1. [Configure each source](https://redis.io/docs/latest/operate/rc/rdi/define#source-connectivity) with its endpoint service name. Use the matching listener port in the source configuration.\n1. Accept each new endpoint connection in your AWS endpoint service. Sources sharing an existing workspace endpoint do not need a new acceptance.\n1. Share each source's [credentials](https://redis.io/docs/latest/operate/rc/rdi/setup#share-source-database-credentials).\n1. Select **Test source** for every source.\n\nYou manage routes from the NLB to its database targets, database access rules, and target updates after failover. See the [AWS PrivateLink reference](https://redis.io/docs/latest/operate/rc/rdi/networking/aws-privatelink) for address visibility, databases outside the VPC, and failover."},{"id":"managed-source-providers","title":"Managed source providers","role":"content","text":"MongoDB Atlas manages its endpoint service. Follow the [Atlas setup steps](https://redis.io/docs/latest/operate/rc/rdi/setup#set-up-connectivity): give Redis Cloud the Atlas service ID, then register the returned VPC endpoint ID in Atlas. Repeat the handshake for each workspace connection.\n\nProvider requirements still apply. Snowflake is in Preview and must be hosted on AWS. Supabase AWS PrivateLink connectivity is not supported. Check the [source requirements](https://redis.io/docs/latest/operate/rc/rdi#prerequisites) and [source setup instructions](https://redis.io/docs/latest/operate/rc/rdi/setup) before choosing connectivity."}],"examples":[{"id":"workspaces-sources-and-targets-ex0","language":"mermaid {width=\"100%\"}","code":"---\nconfig:\n  elk:\n    nodePlacementStrategy: NETWORK_SIMPLEX\n  flowchart:\n    minNodeWidth: 60\n    wrappingWidth: 90\n    padding: 6\n    diagramPadding: 4\n---\ngraph TB\n    subgraph redis[\"Redis Cloud — Redis manages\"]\n        subgraph subA[\"Pro subscription A\"]\n            subgraph workspaceA[\"Workspace A / pipeline\"]\n                sourceA1[\"Source 1\"]\n                sourceA2[\"Source 2\"]\n                sourceA3[\"Source 3\"]\n                sourceA4[\"Source 4<br/>Public connection\"]\n                processorA[\"Shared processor A\"]\n                endpointA[\"VPC endpoint A1\"]\n                endpointA2[\"VPC endpoint A2\"]\n                sourceA1 -->|Port 5432| endpointA\n                sourceA2 -->|Port 5433| endpointA\n                sourceA3 -->|Port 3306| endpointA2\n                sourceA1 -.-> processorA\n                sourceA2 -.-> processorA\n                sourceA3 -.-> processorA\n                sourceA4 -.-> processorA\n            end\n            targetA[\"Redis target A\"]\n            processorA -->|Write| targetA\n        end\n        subgraph subB[\"Pro subscription B\"]\n            subgraph workspaceB[\"Workspace B / pipeline\"]\n                sourceB1[\"Source 1\"]\n                sourceB2[\"Source 2\"]\n                processorB[\"Shared processor B\"]\n                endpointB[\"VPC endpoint B1\"]\n                sourceB1 -->|Port 5432| endpointB\n                sourceB2 -->|Port 5433| endpointB\n                sourceB1 -.-> processorB\n                sourceB2 -.-> processorB\n            end\n            targetB[\"Redis target B\"]\n            processorB -->|Write| targetB\n        end\n    end\n    subgraph customer[\"Your AWS networks — you manage\"]\n        shared[\"Endpoint service 1 / NLB<br/>Two listeners\"]\n        separate[\"Endpoint service 2 / NLB\"]\n        db1[\"Source database 1\"]\n        db2[\"Source database 2\"]\n        db3[\"Source database 3\"]\n        shared -->|Listener 5432| db1\n        shared -->|Listener 5433| db2\n        separate -->|Listener 3306| db3\n    end\n    publicDb[\"Source database 4<br/>Public IP or hostname\"]\n    endpointA -->|PrivateLink| shared\n    endpointB -->|PrivateLink| shared\n    endpointA2 -->|PrivateLink| separate\n    sourceA4 -->|Public connection| publicDb","section_id":"workspaces-sources-and-targets"}]}
{"schema_version":2,"id":"operate/rc/rdi/quick-start","title":"RDI on Redis Cloud quick start","url":"https://redis.io/docs/latest/operate/rc/rdi/quick-start/","summary":"Learn how to create a data pipeline between a PostgreSQL source database created with Terraform and a Redis Cloud target database.","aliases":["/operate/rc/databases/rdi/quick-start/","/operate/rc/databases/rdi/quick-start"],"tags":["docs","operate","rc"],"last_updated":"2026-10-18T03:56:52+03:00","page_type":"content","content_hash":"223fbdb0dfea1d35ac1630f431c9290144f936a5f9d78924af2d214e1fb29401","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The [`rdi-cloud-automation` GitHub repository](https://github.com/redis/rdi-cloud-automation) contains a Terraform script that quickly sets up a PostgreSQL source database on an EC2 instance and all required permissions and network setup to connect it to a Redis Cloud target database.\n\n> [!NOTE]\n> This guide is for demonstration purposes only. It is not recommended for production use."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"To follow this guide, you need to:\n\n1. Create a [Redis Cloud Pro database](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new) hosted on Amazon Web Services (AWS).\n\n    Turn on Multi-AZ replication and [manually select the availability zones](https://redis.io/docs/latest/operate/rc/databases/configuration/high-availability#availability-zones) when creating the database.\n\n1. Install the [AWS CLI](https://aws.amazon.com/cli/) and set up [credentials for the CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-sso.html).\n\n1. Install [Terraform](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli)."},{"id":"create-a-data-integration-workspace","title":"Create a data integration workspace","role":"content","text":"Before you can create your first Data Integration pipeline for a Redis Cloud subscription, you must first deploy the cloud infrastructure needed to host the pipeline and run the workers associated with the pipeline. In Redis Cloud, this is called a **Workspace**. See [Create and manage Data Integration workspace](https://redis.io/docs/latest/operate/rc/rdi/create-workspace) for more information.\n\nTo create a Data Integration workspace for an existing [Pro subscription](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new):\n\n1. In the Redis Cloud console, select **Data Integration**.\n\n2. Select **Create workspace** if you have no workspaces yet.\n\n    ![The create workspace button.](https://redis.io/docs/latest/images/rc/rdi/rdi-create-workspace-button.png)\n\n    If you already have a workspace, select **New workspace**.\n\n    ![The new workspace button.](https://redis.io/docs/latest/images/rc/rdi/rdi-new-workspace-button.png)\n\n    You can also open **Data Integration** from a subscription or database page.\n    Select **Create workspace** to use that subscription.\n\n    ![The create workspace button.](https://redis.io/docs/latest/images/rc/rdi/rdi-create-workspace-button.png)\n\n3. Select your Pro subscription if it is not already selected.\n\n    ![The select pro subscription drop down.](https://redis.io/docs/latest/images/rc/rdi/rdi-create-workspace-select-subscription.png)\n\n4. Review **Data Integration subnet (CIDR)**. The console suggests a dedicated\n   `/22` Classless Inter-Domain Routing (CIDR) range for the workspace.\n\n    [Plan network capacity for all pipelines](https://redis.io/docs/latest/operate/rc/rdi/scale-pipeline#plan-workspace-network-capacity)\n    before you create the workspace. Choose a `/21` or larger range if you plan\n    more than 5 sources or more than 5 processor replicas in total.\n    These counts are a conservative planning trigger, not a capacity limit.\n    You cannot enlarge the workspace CIDR after creation.\n\n    For Amazon Web Services (AWS), the RDI workspace CIDR must:\n\n    - Be in the same [RFC 1918 private address range](https://datatracker.ietf.org/doc/html/rfc1918#section-3) as the subscription virtual private cloud (VPC)'s primary CIDR: `10.0.0.0/8`, `172.16.0.0/12`, or `192.168.0.0/16`.\n    - Not overlap with existing subscription, peering, transit gateway (TGW), application, database, or RDI workspace CIDR ranges.\n\n    For example, a subscription VPC has the primary CIDR `10.238.252.0/24`.\n    The range `192.168.0.0/22` is invalid because it is in a different\n    RFC 1918 range. An unused range such as `10.239.0.0/22` is valid.\n\n    If the suggestion is missing or unsuitable, select another unused range.\n    It must meet your capacity needs and use the same private address range.\n    See [VPC CIDR block association restrictions](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-cidr-blocks.html#vpc-resize).\n\n    ![The select pro subscription drop down.](https://redis.io/docs/latest/images/rc/rdi/rdi-create-workspace-cidr.png)\n\n5. Select **Create workspace**.\n\n    ![The create workspace button.](https://redis.io/docs/latest/images/rc/rdi/rdi-create-workspace-button.png)\n\nRedis Cloud creates the workspace in the background. Select **Create pipeline**\nto [create your pipeline](https://redis.io/docs/latest/operate/rc/rdi/define) while it provisions.\nSelect **Create pipeline later** to return to the Redis Cloud console."},{"id":"get-required-arns","title":"Get required ARNs","role":"content","text":"This example creates one PostgreSQL source. You can [add more sources](https://redis.io/docs/latest/operate/rc/rdi/view-edit#add-source) after the pipeline is running.\n\n1. On the [Redis Cloud console](https://cloud.redis.io/), open your target database's **Data Integration** tab and select **Add pipeline**.\n\n    ![The Add pipeline control is available while the workspace is being created.](https://redis.io/docs/latest/images/rc/rdi/rdi-workspace-add-pipeline.png)\n\n1. In **Settings**, select your target database and choose **Hash** or **JSON** as the default data structure, then select **Continue**.\n1. In **Add sources**, select **PostgreSQL**.\n1. Enter a **Source name** of your choice, for example `inventory-postgres`.\n1. Select **Continue** to open **Configure source**.\n1. Under **Source connectivity**, copy the **Role ARN**. Use it as `redis_privatelink_arn` in the Terraform configuration.\n\n    ![The source connectivity Role ARN and availability zones.](https://redis.io/docs/latest/images/rc/rdi/rdi-setup-connectivity-arn.png)\n\n1. Under **Secrets**, copy the **Role ARN**. Use it as `redis_secrets_arn` in the Terraform configuration.\n\n    ![The Role ARN in the Secrets section.](https://redis.io/docs/latest/images/rc/rdi/rdi-credentials-arn.png)\n\n1. Select **Save & exit** while you create the source resources."},{"id":"create-the-source-database-and-network-resources","title":"Create the source database and network resources","role":"content","text":"1. Clone or download the [`rdi-cloud-automation` GitHub repository](https://github.com/redis/rdi-cloud-automation).\n\n1. In a terminal window, go to the `examples/aws-ec2-privatelink` directory.\n\n1. Run `terraform init` to initialize the Terraform working directory.\n\n1. Open the `example.tfvars` file and edit the following variables:\n\n    - `region`: The AWS region where your Redis Cloud database is deployed.\n    - `azs`: The availability zone IDs where your Redis Cloud database is deployed.\n    - `port`: The port number for the new PostgreSQL source database.\n    - `name`: A prefix for all of the created AWS resources.\n    - `redis_secrets_arn`: The role ARN from **Secrets** in the Redis Cloud console.\n    - `redis_privatelink_arn`: The role ARN from **Source connectivity** in the Redis Cloud console.\n\n1. To view the configuration, run:\n\n    [code example]\n\n1. To create the AWS resources, run:\n\n    [code example]\n\n    This example creates the following resources on your AWS account:\n\n    - An AWS KMS key with the required permissions for RDI\n    - A VPC with a public and private subnet and all necessary route tables\n    - An EC2 instance running a PostgreSQL database with a security group that allows access from Redis Cloud\n    - An AWS Secrets Manager secret for the PostgreSQL database credentials\n    - A Network Load Balancer (NLB), a listener, and target group to route traffic to the EC2 instance with AWS PrivateLink\n    - An AWS PrivateLink endpoint service for the PostgreSQL database\n\nCreating the AWS resources will take some time. After the resources are created, you'll be able to view them in the AWS management console.\n\nSave the following outputs:\n\n- `database`: The name of the PostgreSQL database.\n- `port`: The port number for the PostgreSQL database.\n- `secret_arn`: The ARN of the AWS Secrets Manager secret for the PostgreSQL database credentials.\n- `vpc_endpoint_service_name`: The name of the AWS PrivateLink endpoint service for the PostgreSQL database.\n\nIf you lose any outputs, run `terraform output` to view them again."},{"id":"resume-pipeline-setup","title":"Resume pipeline setup","role":"content","text":"1. Return to your database's **Data Integration** tab in the [Redis Cloud console](https://cloud.redis.io/).\n1. Open the draft pipeline's actions menu and select **Resume pipeline setup**.\n1. Open **Configure source** and select your PostgreSQL source in the **Sources** list.\n1. Under **Source connectivity**, enter the Terraform `vpc_endpoint_service_name` output as the **Private Link service name**.\n1. Select **Connect to Private Link** and wait for connectivity to complete.\n\n    ![AWS Private Link connectivity with the service name and Connect to Private Link control.](https://redis.io/docs/latest/images/rc/rdi/rdi-source-configuration-source-connectivity-privatelink.png)\n\n1. Under **Secrets**, enter the `secret_arn` output as **Credentials Secret ARN**.\n1. Select **Validate** to check access to the secret.\n\n    ![The Credentials Secret ARN field, transit security options, and Validate control.](https://redis.io/docs/latest/images/rc/rdi/rdi-source-configuration-secrets.png)\n\n1. Under **Source configuration**, enter the `database` and `port` Terraform outputs in the matching fields.\n1. Under **Collector properties**, set `publication.autocreate.mode` to `filtered` so RDI\n   creates a publication for the selected tables. This example uses the `postgres` user,\n   which has the required permissions. For an administrator-managed publication or an\n   existing all-tables publication, follow [PostgreSQL publications](https://redis.io/docs/latest/operate/rc/rdi/setup#postgresql-publications).\n1. Select **Test source** and correct any validation errors, then select **Continue**.\n1. In **Select data**, select the schemas, tables, and columns to ingest. Review the selected key for each table.\n\n    ![Selecting a schema shows its tables for ingestion.](https://redis.io/docs/latest/images/rc/rdi/rdi-dataset-schema-selected.png)\n\n    ![Selecting a table shows its columns and the columns selected for ingestion.](https://redis.io/docs/latest/images/rc/rdi/rdi-select-columns.png)\n\n1. Select **Continue** to open **Add transformations**. For this example, you can keep the default mapping without adding jobs.\n1. Select **Continue to review & deploy**.\n1. Review the source and target, then select **Deploy pipeline**.\n\n    ![The Deploy pipeline button.](https://redis.io/docs/latest/images/rc/rdi/rdi-confirm-deploy.png)\n\nThe source first imports its selected data, then captures ongoing changes. Open the pipeline's **Dashboard** or **Metrics** tab to follow its progress.\n\nThe following example shows the metrics for one selected source in a pipeline with multiple sources.\n\n![Metrics for one selected source in a pipeline, including snapshot progress and per-table record counts.](https://redis.io/docs/latest/images/rc/rdi/rdi-2-metrics.png)\n\nSee [View and edit data pipeline](https://redis.io/docs/latest/operate/rc/rdi/view-edit) for source actions, dataset changes, and monitoring."},{"id":"delete-sample-resources","title":"Delete sample resources","role":"content","text":"> [!WARNING]\n> Make sure to [delete your data pipeline](https://redis.io/docs/latest/operate/rc/rdi/view-edit#delete-pipeline) before deleting the sample resources.\n\nTo delete the sample resources created by Terraform, run:\n\n[code example]"}],"examples":[{"id":"create-the-source-database-and-network-resources-ex0","language":"sh","code":"terraform plan -var-file=example.tfvars","section_id":"create-the-source-database-and-network-resources"},{"id":"create-the-source-database-and-network-resources-ex1","language":"sh","code":"terraform apply -var-file=example.tfvars","section_id":"create-the-source-database-and-network-resources"},{"id":"delete-sample-resources-ex0","language":"sh","code":"terraform destroy -var-file=example.tfvars","section_id":"delete-sample-resources"}]}
{"schema_version":2,"id":"operate/rc/rdi/rds-proxy","title":"RDS Proxy setup for RDI","url":"https://redis.io/docs/latest/operate/rc/rdi/rds-proxy/","summary":"Set up RDS Proxy for Redis Data Integration (not recommended).","aliases":["/operate/rc/databases/rdi/rds-proxy/","/operate/rc/databases/rdi/rds-proxy"],"tags":["docs","operate","rc"],"last_updated":"2026-10-19T20:49:52+03:00","page_type":"content","content_hash":"04555b4884d1570139bf30da79679bb8cd9bdd503b12737116945f51a0f98db7","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"> [!WARNING]\n> We do not recommend using RDS Proxy for RDI connections. The [Lambda function approach](https://redis.io/docs/latest/operate/rc/rdi/setup#setup-lambda-function) provides better failover handling and is the recommended solution for production environments.\n>\n> Additionally, RDS Proxy does not work with RDS PostgreSQL and Aurora PostgreSQL because it does not support PostgreSQL logical replication.\n>\n> Only use RDS Proxy if you have specific requirements that necessitate it."},{"id":"overview","title":"Overview","role":"overview","text":"RDS Proxy is a fully managed, highly available database proxy for Amazon RDS. While it can be used with RDI, we recommend the Lambda function approach instead for the following reasons:\n\n- **PostgreSQL incompatibility**: RDS Proxy does not support PostgreSQL logical replication, which is required for CDC (Change Data Capture).\n- **Added complexity**: RDS Proxy adds an additional layer between RDI and your database.\n- **Lambda provides better failover**: The Lambda function approach handles failover scenarios more efficiently.\n\nThe diagram shows the RDS Proxy alternative. It does not apply to PostgreSQL sources.\n\n![RDI connects through PrivateLink and a network load balancer to RDS Proxy, which forwards connections to the RDS or Aurora database.](https://redis.io/docs/latest/images/rc/rdi/rdi-setup-diagram-aurora.png)\n\nIf you still need to use RDS Proxy, follow the instructions below."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before setting up RDS Proxy, ensure you have:\n\n- An RDS or Aurora database (MySQL or SQL Server only)\n- AWS Secrets Manager secret containing your database credentials\n- AWS KMS encryption key for the secret\n- Appropriate IAM permissions"},{"id":"create-rds-proxy","title":"Create RDS Proxy","role":"content","text":"Follow the AWS documentation to create an RDS Proxy:\n\n- [Creating an RDS Proxy](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-proxy-setup.html) (AWS documentation)\n- [How RDS Proxy works](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-proxy.howitworks.html) (AWS documentation)\n- [RDS Proxy TLS/SSL](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-proxy.howitworks.html#rds-proxy-security.tls) (AWS documentation)"},{"id":"iam-permissions","title":"IAM permissions","role":"content","text":"The Proxy's IAM role must have the following permissions to access the database using the credentials secret and encryption key:\n\n- `secretsmanager:GetSecretValue`\n- `secretsmanager:DescribeSecret`\n- `kms:Decrypt`\n\nExample IAM policy:\n\n[code example]"},{"id":"get-the-rds-proxy-ip-address","title":"Get the RDS Proxy IP address","role":"content","text":"After creating the RDS Proxy, you need to get its static IP address to use when configuring the Network Load Balancer.\n\nTo get the static IP address of your RDS Proxy, run the following command on an EC2 instance in the same VPC as the Proxy:\n\n[code example]\n\nReplace `<proxy-endpoint>` with the endpoint of your RDS Proxy. Save this IP address for use in the Network Load Balancer configuration."},{"id":"configure-the-network-load-balancer","title":"Configure the Network Load Balancer","role":"configuration","text":"When you [create the Network Load Balancer](https://redis.io/docs/latest/operate/rc/rdi/setup#create-network-load-balancer-rds), use the RDS Proxy IP address instead of the database IP address:\n\n1. In **Register targets**, enter the static IP address of your RDS Proxy (obtained in the previous step).\n2. Enter the port number where your RDS Proxy is exposed.\n3. Select **Include as pending below**.\n4. Complete the remaining Network Load Balancer setup as described in the [main setup guide](https://redis.io/docs/latest/operate/rc/rdi/setup#create-network-load-balancer-rds)."},{"id":"next-steps","title":"Next steps","role":"content","text":"After setting up RDS Proxy and the Network Load Balancer:\n\n1. [Create an endpoint service](https://redis.io/docs/latest/operate/rc/rdi/setup#create-endpoint-service-rds) through AWS PrivateLink.\n2. [Share your source database credentials](https://redis.io/docs/latest/operate/rc/rdi/setup#share-source-database-credentials) with Redis Cloud.\n3. Continue with the [RDI pipeline configuration](https://redis.io/docs/latest/operate/rc/rdi/define).\n\n> [!NOTE]\n> When using RDS Proxy, you do not need to set up the Lambda function for failover handling, as the proxy provides a static endpoint."}],"examples":[{"id":"iam-permissions-ex0","language":"json","code":"{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Effect\": \"Allow\",\n            \"Action\": [\n                \"secretsmanager:GetSecretValue\",\n                \"secretsmanager:DescribeSecret\"\n            ],\n            \"Resource\": \"arn:aws:secretsmanager:region:account-id:secret:secret-name\"\n        },\n        {\n            \"Effect\": \"Allow\",\n            \"Action\": \"kms:Decrypt\",\n            \"Resource\": \"arn:aws:kms:region:account-id:key/key-id\"\n        }\n    ]\n}","section_id":"iam-permissions"},{"id":"get-the-rds-proxy-ip-address-ex0","language":"sh","code":"$ nslookup <proxy-endpoint>","section_id":"get-the-rds-proxy-ip-address"}]}
{"schema_version":2,"id":"operate/rc/rdi/scale-pipeline","title":"Scale a Cloud RDI pipeline","url":"https://redis.io/docs/latest/operate/rc/rdi/scale-pipeline/","summary":"Increase collector, RDI database, and processor capacity for a Cloud RDI pipeline.","aliases":["/operate/rc/rdi/scale-processor/","/operate/rc/rdi/scale-processor","/operate/rc/databases/rdi/scale-processor/","/operate/rc/databases/rdi/scale-processor"],"tags":["docs","operate","rc"],"last_updated":"2026-10-21T08:57:20+03:00","page_type":"content","content_hash":"e6b1c86dbc1e5dc63c42f48b5a2fc0c2962b3bc39a7bb42b9e4f8dd4b04dabd2","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Every Redis Data Integration (RDI) pipeline in Redis Cloud uses the Flink\nprocessor. The collector, RDI database, processor, or target database can\nlimit pipeline capacity. Identify the bottleneck before you change a setting.\n\nCloud RDI does not automatically add or remove processor replicas\n(TaskManagers) based on load, pending records, throughput, or backpressure.\nSet the number of processor replicas when you need more processing capacity."},{"id":"plan-workspace-network-capacity","title":"Plan workspace network capacity","role":"content","text":"Choose the workspace Classless Inter-Domain Routing (CIDR) range for all\npipelines you plan to run. All sources and processor replicas share the\nworkspace's network capacity. Each source uses a collector. Each TaskManager\nis a processor replica. Adding either uses more workspace addresses.\n\nThe console suggests a `/22` range. If you expect more than 5 sources or more\nthan 5 processor replicas in total across the workspace, choose a `/21` or\nlarger range when you create it. For further growth, consider a `/20` or larger\nrange. A `/21` has twice the address space of a `/22`; a `/20` has twice that\nof a `/21`. Doubling the address space does not guarantee twice as many\nsources or processor replicas.\n\nThis is conservative planning guidance, not a guaranteed capacity limit.\nLeave room for planned growth, replacement resources, and maintenance.\nThe range must also meet the requirements of your connectivity method and\nmust not overlap with connected networks."},{"id":"processor-capacity-by-cidr","title":"Processor capacity by CIDR","role":"content","text":"Use this table as a reference for processor address capacity. The reference\nconfiguration uses RDI 2.0.0 on Amazon Web Services (AWS), with three\navailability zones. It has one pipeline, no sources, and two control-plane\nnodes. Each processor requests one central processing unit (CPU) and 5 GiB\nof memory. Temporary workspace scaling overrides allow the configured counts\nin the table. The allocation window is 10 minutes per scaling request.\n\nConfigured replicas are the requested count. Ready replicas are running\nprocessors that pass readiness checks. Both columns count processors only.\nThey exclude source collectors and control-plane nodes. These resources\nalso use workspace addresses.\n\n| Workspace CIDR | Configured replicas | Ready replicas | Capacity constraint |\n| --- | ---: | ---: | --- |\n| `/22` | 15 | 14 | Address blocks exhausted in the assigned subnet. Another zone retained a block. |\n| `/21` | 50 | 32 | Address blocks exhausted in all worker subnets. Allocation requests were also throttled. |\n| `/20` | 50 | 50 | Capacity for at least 50 replicas in this configuration. The upper limit is not established. |\n\nThe `/22` and `/21` figures use gradual scaling. The `/20` figure uses a\ndirect request for 50 replicas.\n\nThe counts of 14 and 32 are not fixed workspace limits. Address distribution\nacross zones and allocation throttling also affect scaling. The table does\nnot define supported replica limits, throughput, or combined source and\nprocessor capacity. Reserve addresses for collectors, other pipelines,\ngrowth, and maintenance.\n\nYou cannot enlarge an existing workspace CIDR. A larger range requires\n[workspace recreation](https://redis.io/docs/latest/operate/rc/rdi/faq#change-workspace-cidr). See\n[Create a workspace](https://redis.io/docs/latest/operate/rc/rdi/create-workspace) for CIDR\nselection and connectivity requirements."},{"id":"identify-the-bottleneck","title":"Identify the bottleneck","role":"content","text":"Use the **Dashboard** to compare **Throughput**, **Pending**, **Processor load**,\nand **RDI database load** before you change capacity:\n\n- If ingestion from a source is slow and the processor has spare capacity,\n  check the source's diagnostics in **Metrics** before tuning collector\n  properties.\n- If the processor stays at capacity while pending records grow, consider\n  increasing processor replicas.\n- If the processor is backpressured, check target database performance and\n  connectivity before adding processor replicas.\n- If the RDI database stays full or reports out-of-memory (OOM) errors, check\n  ingestion and processing rates, database memory, and throughput. OOM alone\n  does not identify which capacity setting to change. See\n  [Increase RDI database throughput](#increase-rdi-database-throughput).\n\nChange one setting at a time and compare the same representative workload.\nConfirm that the change improves throughput or reduces pending records before\nmaking another increase."},{"id":"increase-collector-ingestion-capacity","title":"Increase collector ingestion capacity","role":"content","text":"Tune collector properties when the collector cannot ingest data from the\nsource quickly enough. These properties are specific to each source. In the\nCloud console, select the source in **Configuration**, then select **Edit\ncollector properties**.\n\nThe console exposes these commonly tuned Debezium properties when they apply to\nthe selected source:\n\n| Collector section | Property | Effect |\n| --- | --- | --- |\n| Source | `snapshot.max.threads` | Increases the threads used for the initial snapshot. |\n| Source | `snapshot.fetch.size` | Increases the rows fetched in each snapshot batch. |\n| Source | `max.batch.size` | Increases the records processed in each batch. |\n| Source | `max.queue.size` | Increases the records buffered in collector memory. |\n| Source | `poll.interval.ms` | Sets the wait before the collector polls for change data capture (CDC) changes. |\n| Source | `record.processing.threads` | Increases the threads that process captured records. |\n| Sink | `redis.batch.size` | Increases the records written to the RDI database in each batch. |\n| Sink | `redis.flush.interval.ms` | Reduces the maximum wait before the collector flushes a batch. |\n\nUse only properties shown for the selected source. The available properties can\nvary by source database.\n\nCloud RDI collectors have 2 CPUs and 8 GB of\nrandom access memory (RAM). You cannot select a different collector size.\nTest each change with a representative workload before you use it in production.\n\nLarger batches and queues use more collector memory. More snapshot and\nrecord-processing threads share the same 2 CPUs. A shorter poll interval can\nreduce CDC latency but can increase load on the source database. Change one\nproperty at a time and use the Dashboard to compare throughput, pending\nrecords, and processor load."},{"id":"increase-rdi-database-throughput","title":"Increase RDI database throughput","role":"content","text":"Each pipeline has an RDI database. When the RDI database limits ingestion or\nprocessing, increase its memory limit or throughput in its **Performance**\nsettings."},{"id":"relevant-metrics","title":"Relevant metrics","role":"content","text":"To view the RDI database metrics, open the RDI database in the Cloud console\nand select the **Metrics** tab. Check these metrics:\n\n- **Used memory**: The RDI database holds records until the processor writes\n  them to the target database. Large rows and high data volumes can fill the\n  RDI database faster than the processor drains it. When the RDI database\n  reaches its memory limit, it rejects writes with OOM errors and the\n  collectors wait.\n- **Ops/sec**: If **Ops/sec** stays close to the RDI database's throughput\n  value, or **Latency** keeps rising, the RDI database cannot handle more\n  writes."},{"id":"single-pipeline","title":"Single pipeline","role":"content","text":"For a single pipeline, check **Used memory** and **Ops/sec**:\n\n- If **Used memory** reaches the memory limit, increase **Memory limit**.\n- If **Ops/sec** stays close to the throughput value, increase **Throughput**."},{"id":"multiple-pipelines","title":"Multiple pipelines","role":"content","text":"For multiple pipelines, check **Used memory** and **Ops/sec** the same way as\nfor a single pipeline.\n\nWhen you increase **Throughput**, choose a value with as many shards as you\nhave pipelines. Each pipeline writes to its own hash slot, and all sources in\na pipeline share that slot. RDI spreads the pipelines' hash slots across the\nslot range, so when the number of shards matches the number of pipelines,\neach pipeline's slot is on a different shard.\n\n| Throughput | Nodes | Shards |\n| --- | ---: | ---: |\n| 25,000 ops/sec (default) | 1 | 1 |\n| 50,000 ops/sec | 2 | 2 |\n| 75,000 ops/sec | 3 | 3 |"},{"id":"edit-the-performance-settings","title":"Edit the performance settings","role":"content","text":"1. In the Cloud console, open the RDI database for the pipeline.\n1. Edit the database **Performance** settings.\n1. Increase **Memory limit** or **Throughput** to the next available value.\n1. Check the RDI database **Metrics** tab and the pipeline Dashboard before\n   you make another increase.\n\nStop increasing the values when the pipeline no longer improves. More RDI\ndatabase throughput does not resolve a bottleneck in another component, so\ncheck processor and target database performance too."},{"id":"increase-processor-capacity","title":"Increase processor capacity","role":"content","text":"1. From the Cloud RDI **Pipelines** list, select the pipeline.\n1. Select the **Settings** tab.\n1. Select **Edit**.\n1. Add or update the `advanced.resources.taskManager.replicas` property with\n   the number of processor replicas you need. Use a positive whole number\n   accepted by the console and check [workspace network capacity](#plan-workspace-network-capacity)\n   before you increase it.\n1. Select **Save changes**.\n1. Select **Apply and restart** to apply the change. This temporarily\n   interrupts processing.\n\nThis property sets the desired processor replica count. After the restart,\ncheck the running count and pipeline performance. More replicas do not\nguarantee higher throughput.\n\nFor application programming interface (API) configuration, the same property\nis represented as follows:\n\n[code example]"},{"id":"confirm-the-applied-capacity","title":"Confirm the applied capacity","role":"content","text":"The **Dashboard** shows the processor replica count next to the processor\nstatus. It shows the running count and configured count in the format\n`running / configured replicas`. For example, `3 / 3 replicas` means three\nTaskManagers are running. The configured count is also three. If only one\ncount is available, the Dashboard labels it as running or configured.\n\nThe running count can be lower while a restart is in progress. If it remains\nbelow the configured count after the restart, check pipeline and source status\nfor errors and contact [Redis support](https://redis.io/support/). Network\ncapacity can prevent additional replicas from starting. Increasing the\nconfigured count again does not resolve a network capacity shortage.\n\nThe console metrics show data-stream record counts and pending records. They do\nnot include a TaskManager replica-count metric.\n\nFor programmatic confirmation, use the [Redis Cloud API](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Data-Integration)\nto send a `GET` request to\n`/v1/subscriptions/<subscription-id>/data-integration-workspace/rdi/api/v2/pipelines/<pipeline-name>/status`.\nInspect the `flink-processor` entry in `components`. Its `replicas` value is\nthe number of ready processor replicas. For example:\n\n[code example]\n\nBilling is not a real-time way to confirm that a scaling change has completed.\nUse the Dashboard or the status API instead."}],"examples":[{"id":"increase-processor-capacity-ex0","language":"yaml","code":"processors:\n  advanced:\n    resources:\n      taskManager:\n        replicas: 3","section_id":"increase-processor-capacity"},{"id":"confirm-the-applied-capacity-ex0","language":"json","code":"{\n  \"name\": \"flink-processor\",\n  \"replicas\": 3\n}","section_id":"confirm-the-applied-capacity"}]}
{"schema_version":2,"id":"operate/rc/rdi/setup","title":"Prepare source database","url":"https://redis.io/docs/latest/operate/rc/rdi/setup/","summary":"Prepare your source database, network setup, and database credentials for Data integration.","aliases":["/operate/rc/databases/rdi/setup/","/operate/rc/databases/rdi/setup"],"tags":["docs","operate","rc"],"last_updated":"2026-10-19T20:49:52+03:00","page_type":"content","content_hash":"034b7c5c95dfab11a30202595a4aa2271a17e3cb932191a9dd73d6a099056f53","sections":[{"id":"prepare-source-database","title":"Prepare source database","role":"overview","text":"Create the target Redis Cloud Pro database before starting the pipeline. See the [quickstart prerequisites](https://redis.io/docs/latest/operate/rc/rdi/quick-start#prerequisites).\n\nPrepare every source database before adding it to a pipeline. Each source needs its own change data capture (CDC) configuration, connectivity, and credentials. See [Prerequisites](https://redis.io/docs/latest/operate/rc/rdi#prerequisites) for a list of supported source databases and database versions.\n\nSee [Prepare source databases](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs) to find steps for your database type:\n- [MongoDB Atlas](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/mongodb)\n- [Snowflake](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/snowflake)\n- [Supabase](#supabase)\n- Hosted on an AWS EC2 instance:\n    - [MySQL and mariaDB](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/my-sql-mariadb)\n    - [Oracle](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/oracle)\n    - [SQL Server](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/sql-server)\n    - [PostgreSQL](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/postgresql)\n- Hosted on AWS RDS or AWS Aurora:\n    - [AWS Aurora PostgreSQL and AWS RDS PostgreSQL](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/aws-aurora-rds/aws-aur-pgsql)\n    - [AWS Aurora MySQL and AWS RDS MySQL](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/aws-aurora-rds/aws-aur-mysql)\n    - [AWS RDS SQL Server](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/aws-aurora-rds/aws-rds-sqlserver)\n\nSee the [RDI architecture overview](https://redis.io/docs/latest/integrate/redis-data-integration/architecture#overview) for more information about CDC."},{"id":"postgresql-publications","title":"PostgreSQL publications","role":"content","text":"For PostgreSQL sources, including databases hosted on EC2, Aurora/RDS, and Supabase,\n[check replica identity](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/postgresql#check-replica-identity-and-choose-a-publication-mode)\nbefore deploying. Selecting tables in the console does not narrow an existing\n`FOR ALL TABLES` publication. That publication includes current and future tables, and\nupdates or deletes on any published table without replica identity can fail in your\napplication.\n\nFor a publication managed by your database administrator, create an explicit table list\nas shown in the [PostgreSQL preparation guide](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/postgresql#choose-a-publication-mode).\nIn **Configure source**, under **Collector properties**, open **Edit advanced properties**\nand enter these source properties:\n\n| Property | Value |\n|:--|:--|\n| `publication.name` | The name of the publication created by your administrator |\n| `publication.autocreate.mode` | `disabled` |\n\nKeep the publication's table list aligned with the source's selected tables. With\n`disabled`, RDI does not update the publication when you change the selection.\n\nTo let RDI create and manage a publication for selected tables, use `filtered` instead.\nThis requires [publication creation and table ownership permissions](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/postgresql#set-privileges-for-debezium-to-create-postgresql-publications-with-pgoutput).\nUse a separate publication for each source with different table selections.\n\nIf a source already uses `FOR ALL TABLES`, follow\n[Replace an existing all-tables publication](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/postgresql#replace-an-existing-all-tables-publication).\nChanging the collector property alone does not convert that publication."},{"id":"supabase","title":"Supabase","role":"content","text":"You can use a hosted [Supabase](https://supabase.com/) PostgreSQL database as\nthe source for an RDI pipeline on Redis Cloud. The integration was validated with\nRDI 1.19.0 and hosted Supabase PostgreSQL 17.6.\n\nFirst, follow the steps in [Prepare Supabase for RDI](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/supabase)\nto create the database role, grant table access, account for Row Level\nSecurity, and create a publication. Then complete the Redis Cloud-specific steps below.\n\n> [!WARNING]\n> Supabase AWS PrivateLink connectivity isn't supported. Supabase shares a\n> Resource Configuration through AWS Resource Access Manager and requires a\n> Resource-type VPC endpoint. This differs from the AWS PrivateLink\n> endpoint-service connectivity supported by RDI on Redis Cloud, so you can skip\n> [Set up AWS Private Link connectivity](#set-up-connectivity) and use the Supabase\n> public direct database endpoint instead.\n\n#### Configure public connectivity\n\nSupabase logical replication requires the direct database endpoint. Don't use\na Supavisor transaction or session pooler endpoint.\n\nThe direct endpoint uses IPv6 by default, but RDI on Redis Cloud requires an\nIPv4 endpoint, so you must enable the Supabase\n[dedicated IPv4 add-on](https://supabase.com/docs/guides/platform/ipv4-address)\n(you need a paid Supabase plan to do this).\n\nWhen you create the RDI pipeline:\n\n1. Select **PostgreSQL** as the source type.\n1. Select **Public endpoint**.\n1. Copy every **Redis Cloud outbound IP address** displayed by the setup flow.\n1. In Supabase, open **Database settings** > **Network restrictions**.\n1. Add every Redis Cloud outbound address as a `/32` CIDR.\n\nIf you recreate the RDI workspace, its outbound addresses can change. Add the\nnew addresses to Supabase before starting the replacement pipeline, and remove\nthe old addresses after the new connection succeeds.\n\n#### Configure secrets\n\nFollow the steps in [Share source database credentials](#share-source-database-credentials)\nto create and share:\n\n- A credentials secret containing the dedicated Supabase `username` and\n  `password`.\n- A plaintext CA certificate secret containing the certificate downloaded\n  from Supabase **Database settings** > **SSL configuration**.\n\nEncrypt both secrets with the customer-managed AWS KMS key configured for the\nRDI workspace. Use the AWS region that contains your Redis Cloud subscription.\n\nIn the pipeline's **Secrets** section:\n\n1. Enter the credentials secret ARN.\n1. Select **TLS** under **Transit security**.\n1. Enter the CA certificate secret ARN.\n1. Select **Validate**.\n\nRDI on Redis Cloud uses TLS and validates the Supabase CA certificate.\n\n#### Configure the source\n\nIn the RDI pipeline setup flow, open the source configuration and enter the\nfollowing values:\n\n| Field | Value |\n|:--|:--|\n| Source IP address / Hostname | `db.<project-ref>.supabase.co` |\n| Port | `5432` |\n| Database | `postgres` |\n\nUnder **Collector properties**, set:\n\n| Property | Value |\n|:--|:--|\n| `plugin.name` | `pgoutput` |\n| `publication.name` | `rdi_publication` |\n| `publication.autocreate.mode` | `disabled` |\n| `slot.name` | A unique value, such as `rdi_supabase` |\n\nThe publication name must match the publication you created in Supabase. Use a\nunique replication slot name for each active pipeline connected to the\nproject.\n\nSelect **Test source**. After the test succeeds, select the schemas and tables\nto capture and deploy the pipeline.\n\n#### Monitor the pipeline\n\nAfter deployment:\n\n1. Confirm the initial snapshot reaches zero pending and rejected records.\n1. Insert, update, and delete test records in Supabase.\n1. Confirm the corresponding counters increase in the pipeline metrics.\n\nSupabase logical replication slots retain write-ahead log (WAL) while the\npipeline is stopped. Follow the steps in\n[Monitor replication slots](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/prepare-dbs/supabase#7-monitor-replication-slots)\nto monitor retained WAL and prepare for Supabase PostgreSQL upgrades."},{"id":"get-cluster-account-id","title":"Get cluster account ID","role":"content","text":"Before you can set up your source connectivity and secrets, you need the AWS Account ID for your Redis Cloud cluster so that you can give it access to your connectivity and secrets. \n\n1. On the [Redis Cloud console](https://cloud.redis.io/), open your target database's **Data Integration** tab.\n1. Select **Add pipeline**, or resume an existing draft. To add a source to a running pipeline, select **Add source** on its **Dashboard**.\n\n    ![The Add pipeline control is available while the workspace is being created.](https://redis.io/docs/latest/images/rc/rdi/rdi-workspace-add-pipeline.png)\n\n1. For a new pipeline, complete **Settings**, including the target database, and select **Continue**.\n1. In **Add sources**, select the source type and enter a unique **Source name**. This name identifies the source in the pipeline configuration and transformation jobs. See [Add sources](https://redis.io/docs/latest/operate/rc/rdi/define#pipeline-setup) for naming rules.\n1. Select **Continue** to open **Configure source**.\n1. Under **Source connectivity**, copy the **Role ARN** and extract its AWS account ID.\n\n    ![The source connectivity Role ARN and availability zones.](https://redis.io/docs/latest/images/rc/rdi/rdi-setup-connectivity-arn.png)\n\n    The account ID is the number after `arn:aws:iam::`. For example, `arn:aws:iam::123456789012:role/redis-data-pipeline` contains account ID `123456789012`.\n\n1. For a source using **Public Endpoint**, also copy the Redis Cloud outbound IP address to add to the source database's allowlist.\n1. Select **Save & exit** to return to setup after preparing connectivity and secrets.\n\nRepeat the preparation for each source. Keep track of which endpoint service and secrets belong to each source; configuring one source does not configure the others."},{"id":"set-up-connectivity","title":"Set up AWS Private Link connectivity","role":"content","text":"> [!NOTE]\n> If your source database is accessible via a public endpoint and you want to use public connectivity for your data pipeline, proceed to [Share source database credentials](#share-source-database-credentials). \n\nIf your source database is not accessible via a public endpoint, you need to set up an endpoint service through AWS PrivateLink to be able to connect to it. For how traffic flows over PrivateLink, how to connect to an on-premises database over AWS Direct Connect, and how to keep the connection available during failover, see the [AWS PrivateLink reference](https://redis.io/docs/latest/operate/rc/rdi/networking/aws-privatelink).\n\nChoose the diagram and instructions for your source database:\n\n- Database hosted on an AWS EC2 instance:\n\n    ![The network setup for a database hosted on an AWS EC2 instance.](https://redis.io/docs/latest/images/rc/rdi/rdi-setup-diagram-ec2.png)\n\n- Database hosted on AWS RDS or AWS Aurora:\n\n    ![RDI connects to the RDS or Aurora writer through PrivateLink and an internal network load balancer. Lambda updates the target IP address after an RDS event.](https://redis.io/docs/latest/images/rc/rdi/rdi-setup-diagram-aurora-lambda.svg)\n\n    Solid arrows show database connections. Dashed arrows show how Lambda updates the target group when the database address changes. Lambda does not carry database traffic.\n\nSelect the steps for your database setup.\n\n**EC2 instance:**\n\nTo set up PrivateLink for a database hosted on an EC2 instance:\n\n1. [Create a network load balancer](#create-network-load-balancer-ec2) that will route incoming TCP connections to your database.\n1. [Create an endpoint service](#create-endpoint-service-ec2) through AWS PrivateLink."},{"id":"create-network-load-balancer-ec2","title":"Create network load balancer","role":"content","text":"In the [AWS Management Console](https://console.aws.amazon.com/), use the **Services** menu to locate and select **Compute** > **EC2**. [Create a network load balancer](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/create-network-load-balancer.html#configure-load-balancer) with the following settings:\n\n1. In **Basic configuration**: \n    - **Scheme**: Select **Internal**.\n    - **Load balancer IP address type**: Select **IPv4**.\n1. In **Network mapping**, select the VPC and availability zone associated with your source database.\n1. Create a security group for the network load balancer in the source database VPC:\n    - **Inbound rules**: Leave empty. You will disable inbound rule checks for PrivateLink traffic in a later step.\n    - **Outbound rules**: Allow TCP to the database security group on the database port (`5432` for PostgreSQL unless you configured another port).\n1. In the load balancer settings, select this group under **Security groups**.\n1. In **Listeners and routing**: \n    1. Select **Create target group** to [create a target group](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/create-target-group.html) with the following settings:\n        1. In **Specify group details**:\n            - **Target type**: Select **Instances**.\n            - **Protocol : Port**: Select **TCP**, and then enter the database port (`5432` for PostgreSQL unless you configured another port).\n            - The **IP address type** and **VPC** should be selected already and match the VPC you selected earlier.\n        1. In **Health checks**, select **TCP** and use the traffic port.\n        1. In **Register targets**, select the EC2 instance that runs your source database, enter the port, and select **Include as pending below**. Then, select **Create target group** to create your target group. Return to **Listeners and routing** in the Network Load Balancer setup.\n    1. Set the following **Listener** properties:\n        - **Protocol**: Select **TCP**.\n        - **Port**: Enter your source database's port (`5432` for PostgreSQL unless you configured another port), matching the target group.\n        - **Default action**: Select the target group you created in the previous step.\n1. Review the network load balancer settings, and then select **Create load balancer** to continue.\n1. After the network load balancer is active, select **Security**. \n\n    Select **Edit**, deselect **Enforce inbound rules on PrivateLink traffic**, and select **Save changes**. This allows accepted PrivateLink connections through the load balancer without an inbound CIDR rule. Configure allowed principals and connection acceptance in the endpoint service steps that follow.\n\n1. Open the security group attached to your source database.\n1. Select **Edit inbound rules**, then **Add rule** to add a rule with the following settings:\n    - **Type**: Select **Custom TCP**.\n    - **Port range**: Enter the database port (`5432` for PostgreSQL unless you configured another port).\n    - **Source**: Select the network load balancer security group ID. This rule allows both database connections and the TCP health checks configured on the same port.\n    Select **Save rules** to save your changes.\n1. Return to the network load balancer and select **Actions** > **Edit Load Balancer Attributes**.\n    - Under **Load balancer targets selection policy** select **Enable cross-zone load balancing**.\n    Click the **Save Changes** button."},{"id":"create-endpoint-service-ec2","title":"Create endpoint service","role":"content","text":"In the [AWS Management Console](https://console.aws.amazon.com/), use the **Services** menu to locate and select **Networking & Content Delivery** > **VPC**. There, select **PrivateLink and Lattice** > **Endpoint services**. [Create an endpoint service](https://docs.aws.amazon.com/vpc/latest/privatelink/create-endpoint-service.html) with the following settings:\n\n1. In **Available load balancers**, select the [network load balancer](#create-network-load-balancer-ec2) you created.\n1. In **Additional settings**, choose the following settings:\n    - **Require acceptance for endpoint**: Select **Acceptance required**.\n    - **Supported IP address types**: Select **IPv4**.\n1. Select **Create** to create the endpoint service.\n\nAfter you create the endpoint service, you need to add Redis Cloud as an Allowed Principal on your [endpoint service VPC permissions](https://docs.aws.amazon.com/vpc/latest/privatelink/configure-endpoint-service.html#add-remove-permissions). \n\n1. In the Redis Cloud Console, copy the Amazon Resource Name (ARN) provided in the **Setup connectivity** section.\n1. Return to the endpoint service list on the [Amazon VPC console](https://console.aws.amazon.com/vpc/). Select the endpoint service you just created.\n1. Navigate to **Allow principals** tab.\n1. Add the Redis Cloud ARN you copied and choose **Allow principals**.\n1. Save the service name for later. \n\nFor more details on AWS PrivateLink, see [Share your services through AWS PrivateLink](https://docs.aws.amazon.com/vpc/latest/privatelink/privatelink-share-your-services.html).\n\n-\n\n**AWS RDS or Aurora:**\n\n-\n\nConnect the network load balancer (NLB) directly to your RDS or Aurora database. The database address can change after failover or maintenance. We recommend using the Lambda function to keep the NLB target group up to date.\n\n1. [Create a network load balancer](#create-network-load-balancer-rds) that forwards TCP connections to your database.\n1. [Create an endpoint service](#create-endpoint-service-rds) through AWS PrivateLink.\n1. [Set up the Lambda function](#setup-lambda-function) to update the target group when the database address changes.\n\n> [!NOTE]\n> RDS Proxy is not recommended for RDI and does not support PostgreSQL logical replication. If you need this alternative for a compatible database, see the [RDS Proxy setup guide](https://redis.io/docs/latest/operate/rc/rdi/rds-proxy)."},{"id":"create-network-load-balancer-rds","title":"Create network load balancer","role":"content","text":"In the [AWS Management Console](https://console.aws.amazon.com/), use the **Services** menu to locate and select **Compute** > **EC2**. [Create a network load balancer](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/create-network-load-balancer.html#configure-load-balancer) with the following settings:\n\n1. In **Basic configuration**:\n    - **Scheme**: Select **Internal**.\n    - **Load balancer IP address type**: Select **IPv4**.\n1. In **Network mapping**, select the VPC and availability zone associated with your source database.\n1. Create a security group for the network load balancer in the source database VPC:\n    - **Inbound rules**: Leave empty. You will disable inbound rule checks for PrivateLink traffic in a later step.\n    - **Outbound rules**: Allow TCP to the database security group on the database port (`5432` for PostgreSQL unless you configured another port).\n1. In the load balancer settings, select this group under **Security groups**.\n1. In **Listeners and routing**:\n    1. Select **Create target group** to [create a target group](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/create-target-group.html) with the following settings:\n        1. In **Specify group details**:\n            - **Target type**: Select **IP Addresses**.\n            - **Protocol : Port**: Select **TCP**, and then enter the database port (`5432` for PostgreSQL unless you configured another port).\n            - The **IP address type** and **VPC** should be selected already and match the VPC you selected earlier.\n        1. In **Health checks**, select **TCP** and use the traffic port.\n        1. In **Register targets**, enter the currently resolved private IPv4 address of your database, enter the port, and select **Include as pending below**. Then, select **Create target group** to create your target group. Return to **Listeners and routing** in the Network Load Balancer setup.\n\n            To get the current private IPv4 address of your database, run the following command on an EC2 instance in the same VPC as the database:\n            [code example]\n            Replace `<database-endpoint>` with the writer endpoint of your RDS or Aurora database. The address can change after failover or maintenance. [Configure the existing failover Lambda](#setup-lambda-function) to update the target group when the writer address changes.\n    1. Set the following **Listener** properties:\n        - **Protocol**: Select **TCP**.\n        - **Port**: Enter your source database's port (`5432` for PostgreSQL unless you configured another port), matching the target group.\n        - **Default action**: Select the target group you created in the previous step.\n1. Review the network load balancer settings, and then select **Create load balancer** to continue.\n1. After the network load balancer is active, select **Security**.\n\n    Select **Edit**, deselect **Enforce inbound rules on PrivateLink traffic**, and select **Save changes**. This allows accepted PrivateLink connections through the load balancer without an inbound CIDR rule. Configure allowed principals and connection acceptance in the endpoint service steps that follow.\n\n1. Open the security group attached to your source database.\n\n1. Select **Edit inbound rules**, then **Add rule** to add a rule with the following settings:\n    - **Type**: Select **Custom TCP**.\n    - **Port range**: Enter the database port (`5432` for PostgreSQL unless you configured another port).\n    - **Source**: Select the network load balancer security group ID. This rule allows both database connections and the TCP health checks configured on the same port.\n    Select **Save rules** to save your changes.\n1. Return to the network load balancer and select **Actions** > **Edit Load Balancer Attributes**.\n    - Under **Load balancer targets selection policy** select **Enable cross-zone load balancing**.\n    Click the **Save Changes** button."},{"id":"create-endpoint-service-rds","title":"Create endpoint service","role":"content","text":"In the [AWS Management Console](https://console.aws.amazon.com/), use the **Services** menu to locate and select **Networking & Content Delivery** > **VPC**. There, select **PrivateLink and Lattice** > **Endpoint services**. [Create an endpoint service](https://docs.aws.amazon.com/vpc/latest/privatelink/create-endpoint-service.html) with the following settings:\n\n1. In **Available load balancers**, select the [network load balancer](#create-network-load-balancer-rds) you created.\n1. In **Additional settings**, choose the following settings:\n    - **Require acceptance for endpoint**: Select **Acceptance required**.\n    - **Supported IP address types**: Select **IPv4**.\n1. Select **Create** to create the endpoint service.\n\nAfter you create the endpoint service, you need to add Redis Cloud as an Allowed Principal on your [endpoint service VPC permissions](https://docs.aws.amazon.com/vpc/latest/privatelink/configure-endpoint-service.html#add-remove-permissions). \n\n1. In the Redis Cloud Console, copy the Amazon Resource Name (ARN) provided in the **Setup connectivity** section.\n1. Return to the endpoint service list on the [Amazon VPC console](https://console.aws.amazon.com/vpc/). Select the endpoint service you just created.\n1. Navigate to **Allow principals** tab.\n1. Add the Redis Cloud ARN you copied and choose **Allow principals**.\n1. Save the service name for later. \n\nFor more details on AWS PrivateLink, see [Share your services through AWS PrivateLink](https://docs.aws.amazon.com/vpc/latest/privatelink/privatelink-share-your-services.html)."},{"id":"setup-lambda-function","title":"Set up Lambda to update database addresses","role":"content","text":"> [!NOTE]\n> The Lambda function is optional but recommended for production. Without it, you must update the target group yourself when the database address changes.\n\nRDS sends event notifications through Amazon Simple Notification Service (SNS). SNS invokes the Lambda function. Lambda looks up the current writer address using the Domain Name System (DNS) and updates the NLB target group. RDI can reconnect after the new database target is ready.\n\n#### Option 1: Use the Redis Terraform module\n\nRedis provides a ready-to-use Terraform module that automates the Lambda function deployment. This is the recommended approach.\n\n##### Prerequisites\n\n- [Terraform](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli) >= 1.5.7\n- [AWS CLI](https://aws.amazon.com/cli/) configured with credentials\n- The ARNs from the Network Load Balancer and Endpoint Service you created in the previous steps\n\n##### Required variables\n\nBefore deploying the Lambda module, gather the following information:\n\n| Variable | Description | Where to find it |\n|----------|-------------|------------------|\n| `identifier` | A unique name for the Lambda resources | Choose a descriptive name (e.g., `rdi-failover-handler`) |\n| `db_endpoint` | Your RDS cluster or instance endpoint | AWS Console → RDS → Your database → Connectivity |\n| `db_port` | Your database port | AWS Console → RDS → Your database → Connectivity (default: `5432` for PostgreSQL, `3306` for MySQL, `1433` for SQL Server) |\n| `elb_tg_arn` | The NLB Target Group ARN | AWS Console → EC2 → Target Groups → Your target group |\n| `rds_arn` | The RDS cluster or instance ARN | AWS Console → RDS → Your database → Configuration |\n| `rds_cluster_identifier` | The RDS cluster identifier | AWS Console → RDS → Your cluster name |\n\n##### Deploy the Lambda module\n\n1. Clone the Redis cloud automation repository:\n\n    [code example]\n\n1. Create a `terraform.tfvars` file with your configuration:\n\n    [code example]\n\n1. Initialize and apply Terraform:\n\n    [code example]\n\n##### How the Lambda function works\n\nThe deployed Lambda function:\n\n1. **Monitors RDS events**: Subscribes to RDS failover events via SNS\n1. **Detects failover**: When a failover occurs, RDS triggers an SNS notification\n1. **Resolves new IP**: The Lambda function queries DNS to get the new primary's IP address\n1. **Updates NLB target**: Automatically updates the NLB Target Group with the new IP\n\nThis process typically completes within 30-60 seconds for Aurora, or 60-120 seconds for standard RDS.\n\n##### Verify the deployment\n\nAfter deployment, verify the Lambda function is configured correctly:\n\n1. Check the Lambda function in AWS Console → Lambda → Functions\n1. Verify the environment variables are set correctly:\n    - `Cluster_EndPoint`: Your RDS endpoint\n    - `RDS_Port`: Your database port\n    - `NLB_TG_ARN`: Your NLB Target Group ARN\n1. Check the SNS subscription in AWS Console → SNS → Subscriptions\n\n#### Option 2: Full infrastructure deployment\n\nFor new deployments, Redis provides a complete Terraform example that deploys the entire infrastructure including the RDS database, NLB, PrivateLink, and Lambda function.\n\nSee the [AWS RDS PrivateLink Failover Example](https://github.com/redis/rdi-cloud-automation/tree/main/examples/aws-rds-privatelink-failover) for:\n\n- Multi-engine support (PostgreSQL, MySQL, SQL Server)\n- Automatic CDC user creation\n- Complete VPC and networking setup\n- Lambda-based failover handling\n\n#### Option 3: Manual Lambda setup\n\nFor custom implementations, refer to the AWS documentation:\n[Access Amazon RDS across VPCs using AWS PrivateLink and Network Load Balancer](https://aws.amazon.com/blogs/database/access-amazon-rds-across-vpcs-using-aws-privatelink-and-network-load-balancer/)\n\n-\n\n**MongoDB Atlas:**\n\n-\n\nTo set up Private Link for a MongoDB Atlas source database:\n\nMongoDB Atlas manages its own endpoint service. The flow is a two-way handshake — you get an endpoint service ID from Atlas, give it to Redis Cloud, and then take the VPC Endpoint ID that Redis Cloud returns back to Atlas to complete the connection.\n\n> [!NOTE]\n> Create the Atlas private endpoint in the same AWS region as your Redis Cloud target database."},{"id":"create-a-private-endpoint-in-mongodb-atlas","title":"Create a private endpoint in MongoDB Atlas","role":"content","text":"1. In the [MongoDB Atlas UI](https://cloud.mongodb.com/), go to **Security** > **Network Access**.\n1. Select the **Private Endpoint** tab, then select **Dedicated Cluster**.\n1. Select **Add Private Endpoint**.\n    - Select **AWS** as the cloud provider.\n    - Select the AWS region that matches your Redis Cloud target database.\n    - Select **Next**.\n1. Atlas displays an **Endpoint Service ID** (for example, `vpce-svc-xxxxxxxxxxxxxxxxx`). Copy this value."},{"id":"register-the-endpoint-service-with-redis-cloud","title":"Register the endpoint service with Redis Cloud","role":"content","text":"1. In the Redis Cloud console, in the pipeline creation flow, go to the **Source connectivity** step.\n1. In the **Private Link service name** field, paste the Endpoint Service ID you copied from Atlas.\n1. Redis Cloud creates a VPC endpoint and displays a **VPC Endpoint ID** (for example, `vpce-xxxxxxxxxxxxxxxxx`). Copy this value."},{"id":"complete-the-connection-in-mongodb-atlas","title":"Complete the connection in MongoDB Atlas","role":"content","text":"1. Return to the **Add Private Endpoint** page in the Atlas UI. In the **Your VPC Endpoint ID** field, enter the VPC Endpoint ID you copied from the Redis Cloud console. Select **Create**.\n1. Wait for the endpoint status to show as **Available**. This can take a few minutes.\n1. In the Atlas UI, go to your cluster and select **Connect** > **Private Endpoint**.\n1. Choose the private endpoint you just registered (the `vpce-` ID you entered above).\n1. Choose a connection method, then select **Shell**.\n1. Copy the connection string shown.\n\n    > [!NOTE]\n    > Copy the connection string from the **Private Endpoint** connection method only. The standard connection string does not route traffic through the private endpoint."},{"id":"finish-pipeline-setup","title":"Finish pipeline setup","role":"content","text":"1. Return to the Redis Cloud pipeline creation flow and paste the connection string into the **Source configuration** section."},{"id":"share-source-database-credentials","title":"Share source database credentials","role":"content","text":"Share the credentials and certificates for each source through AWS Secrets Manager. Enter the matching secret ARNs when you configure that source in the console.\n\nTo do this, you need to:\n1. [Create an encryption key](#create-encryption-key) using AWS Key Management Service with the right permissions.\n1. [Create secrets](#create-database-credentials-secrets) containing the source database credentials encrypted using that key."},{"id":"create-encryption-key","title":"Create encryption key","role":"content","text":"In the [AWS Management Console](https://console.aws.amazon.com/), use the **Services** menu to locate and select **Security, Identity, and Compliance** > **Key Management Service**. [Create an encryption key](https://docs.aws.amazon.com/kms/latest/developerguide/create-symmetric-cmk.html) with the following settings:\n\n1. In **Step 1 - Configure key**:\n    - **Key type**: Select **Symmetric**.\n    - **Key usage**: Select **Encrypt and decrypt**.\n    - Under **Advanced options**, set the following:\n        - **Key material origin**: Select **KMS - recommended**.\n        - **Regionality**: Select **Single-Region key**.\n1. In **Step 2 - Add labels**, add an alias and description for the key.\n1. In **Step 3 - Define key administrative permissions**, under **Key deletion**, select **Allow key administrators to delete this key**.\n1. In **Step 4 - Define key usage permissions**, under **Other AWS accounts**, select **Add another AWS account**. Enter the AWS account ID for the Redis Cloud cluster that you saved earlier.\n\nReview the key policy and key settings, and then select **Finish** to create the key."},{"id":"create-database-credentials-secrets","title":"Create database credentials secrets","role":"content","text":"To let Redis Cloud access your source database, you need to create AWS secrets for the source database's credentials and certificates. \n\nThe required secrets depend on your source database's security configuration. The following table shows the required secrets for each configuration:\n\n| Security configuration | Required secrets |\n| :-- | :-- |\n| Username and password only | <ul><li>Credentials secret (username and password for the RDI pipeline user)</li></ul> |\n| Username and key pair only *(Snowflake source databases only)* | <ul><li>Credentials secret (username for the RDI pipeline user)</li><li>Private key secret</li></ul> |\n| TLS connection | <ul><li>Credentials secret (username and password for the RDI pipeline user)</li><li>CA Certificate secret (server certificate)</li></ul> |\n| mTLS connection | <ul><li>Credentials secret (username and password for the RDI pipeline user)</li><li>CA Certificate secret (server certificate)</li><li>Client certificate secret</li><li>Client key secret</li></ul> |\n| mTLS connection with client key passphrase | <ul><li>Credentials secret (username and password for the RDI pipeline user)</li><li>CA Certificate secret (server certificate)</li><li>Client certificate secret</li><li>Client key secret</li><li>Client key passphrase secret</li></ul> |\n\n> [!NOTE]\n> When creating secrets for TLS or mTLS, ensure that all certificates and keys are in `PEM` format. The only exception to this is that for PostgreSQL, the source's private key secret must be in `DER` format. If you have a key in `PEM` format, you must convert it to `DER` before creating the secret using the command:\n\n[code example]\n\nThis command assumes that the private key is not encrypted. See the [`openssl` documentation](https://docs.openssl.org/master/) to learn how to convert an encrypted private key.\n\n\nSelect a tab to learn how to create the required secret.\n\n**Credentials secret:**\n\nIn the [AWS Management Console](https://console.aws.amazon.com/), use the **Services** menu to locate and select **Security, Identity, and Compliance** > **Secrets Manager**. [Create a secret](https://docs.aws.amazon.com/secretsmanager/latest/userguide/create_secret.html) of type **Other type of secret** with the following settings:\n\n- **Key/value pairs**: Enter the following key/value pairs.\n\n    - `username`: Database username for the RDI pipeline user\n    - `password`: Database password for the RDI pipeline user\n\n    > [!NOTE]\n    > Snowflake source databases that use user/key pair authentication should only enter the `username` for the database.\n\n* **Encryption key**: Select the [encryption key](#create-encryption-key) you created earlier.\n\n* **Resource permissions**: Add the following permissions to your secret to allow the Redis data pipeline to access your secret. Replace `<AWS ACCOUNT ID>` with the AWS account ID for the Redis Cloud cluster that you saved earlier.\n\n[code example]\n\nAfter you store this secret, you can view and copy the [Amazon Resource Name (ARN)](https://docs.aws.amazon.com/secretsmanager/latest/userguide/reference_iam-permissions.html#iam-resources) of your secret on the secret details page. Save the secret ARN to use when you [define your source database](https://redis.io/docs/latest/operate/rc/rdi/define).\n\n-\n\n**CA Certificate secret:**\n\n-\n\nIn the [AWS Management Console](https://console.aws.amazon.com/), use the **Services** menu to locate and select **Security, Identity, and Compliance** > **Secrets Manager**. [Create a secret](https://docs.aws.amazon.com/secretsmanager/latest/userguide/create_secret.html) of type **Other type of secret** with the following settings:\n\n- **Key/value pairs**: Select **Plaintext** and enter the server certificate.\n\n* **Encryption key**: Select the [encryption key](#create-encryption-key) you created earlier.\n\n* **Resource permissions**: Add the following permissions to your secret to allow the Redis data pipeline to access your secret. Replace `<AWS ACCOUNT ID>` with the AWS account ID for the Redis Cloud cluster that you saved earlier.\n\n[code example]\n\nAfter you store this secret, you can view and copy the [Amazon Resource Name (ARN)](https://docs.aws.amazon.com/secretsmanager/latest/userguide/reference_iam-permissions.html#iam-resources) of your secret on the secret details page. Save the secret ARN to use when you [define your source database](https://redis.io/docs/latest/operate/rc/rdi/define).\n\n-\n\n**Client certificate secret:**\n\n-\n\nIn the [AWS Management Console](https://console.aws.amazon.com/), use the **Services** menu to locate and select **Security, Identity, and Compliance** > **Secrets Manager**. [Create a secret](https://docs.aws.amazon.com/secretsmanager/latest/userguide/create_secret.html) of type **Other type of secret** with the following settings:\n\n- **Key/value pairs**: Select **Plaintext** and enter the client certificate.\n\n* **Encryption key**: Select the [encryption key](#create-encryption-key) you created earlier.\n\n* **Resource permissions**: Add the following permissions to your secret to allow the Redis data pipeline to access your secret. Replace `<AWS ACCOUNT ID>` with the AWS account ID for the Redis Cloud cluster that you saved earlier.\n\n[code example]\n\nAfter you store this secret, you can view and copy the [Amazon Resource Name (ARN)](https://docs.aws.amazon.com/secretsmanager/latest/userguide/reference_iam-permissions.html#iam-resources) of your secret on the secret details page. Save the secret ARN to use when you [define your source database](https://redis.io/docs/latest/operate/rc/rdi/define).\n\n-\n\n**Client key secret:**\n\n-\n\nUse the [AWS CLI create-secret command](https://docs.aws.amazon.com/cli/latest/reference/secretsmanager/create-secret.html) or the [AWS CreateSecret API endpoint](https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_CreateSecret.html) to create a binary secret containing the client key.\n\nFor example, using the AWS CLI, run the following command:\n\n[code example]\n\nWhere:\n- `<secret-name>` - Name of the secret\n- `<path-to-client-key>` - Path to the client key file\n- `<encryption-key-arn>` - ARN of the [encryption key](#create-encryption-key) you created earlier\n\nAfter you create the secret, you need to add permissions to allow the data pipeline to access it. \n\nIn the [AWS Management Console](https://console.aws.amazon.com/), use the **Services** menu to locate and select **Security, Identity, and Compliance** > **Secrets Manager**. Select the private key secret you just created and then select **Edit permissions**. \n\nAdd the following permissions to your secret. Replace `<AWS ACCOUNT ID>` with the AWS account ID for the Redis Cloud cluster that you saved earlier.\n\n[code example]\n\nAfter you store this secret, you can view and copy the [Amazon Resource Name (ARN)](https://docs.aws.amazon.com/secretsmanager/latest/userguide/reference_iam-permissions.html#iam-resources) of your secret on the secret details page. Save the secret ARN to use when you [define your source database](https://redis.io/docs/latest/operate/rc/rdi/define).\n\n-\n\n**Client key passphrase secret:**\n\n-\n\nIn the [AWS Management Console](https://console.aws.amazon.com/), use the **Services** menu to locate and select **Security, Identity, and Compliance** > **Secrets Manager**. [Create a secret](https://docs.aws.amazon.com/secretsmanager/latest/userguide/create_secret.html) of type **Other type of secret** with the following settings:\n\n- **Key/value pairs**: Select **Plaintext** and enter the client key passphrase.\n\n* **Encryption key**: Select the [encryption key](#create-encryption-key) you created earlier.\n\n* **Resource permissions**: Add the following permissions to your secret to allow the Redis data pipeline to access your secret. Replace `<AWS ACCOUNT ID>` with the AWS account ID for the Redis Cloud cluster that you saved earlier.\n\n[code example]\n\nAfter you store this secret, you can view and copy the [Amazon Resource Name (ARN)](https://docs.aws.amazon.com/secretsmanager/latest/userguide/reference_iam-permissions.html#iam-resources) of your secret on the secret details page. Save the secret ARN to use when you [define your source database](https://redis.io/docs/latest/operate/rc/rdi/define).\n\n-\n\n**Private key secret:**\n\n-\n\nThis secret is required for Snowflake source databases that use key-pair authentication.\n\nIn the [AWS Management Console](https://console.aws.amazon.com/), use the **Services** menu to locate and select **Security, Identity, and Compliance** > **Secrets Manager**. [Create a secret](https://docs.aws.amazon.com/secretsmanager/latest/userguide/create_secret.html) of type **Other type of secret** with the following settings:\n\n- **Key/value pairs**: Select **Plaintext** and enter the private key in plain text PEM format.\n\n* **Encryption key**: Select the [encryption key](#create-encryption-key) you created earlier.\n\n* **Resource permissions**: Add the following permissions to your secret to allow the Redis data pipeline to access your secret. Replace `<AWS ACCOUNT ID>` with the AWS account ID for the Redis Cloud cluster that you saved earlier.\n\n[code example]\n\nAfter you store this secret, you can view and copy the [Amazon Resource Name (ARN)](https://docs.aws.amazon.com/secretsmanager/latest/userguide/reference_iam-permissions.html#iam-resources) of your secret on the secret details page. Save the secret ARN to use when you [define your source database](https://redis.io/docs/latest/operate/rc/rdi/define)."},{"id":"next-steps","title":"Next steps","role":"content","text":"After you have prepared connectivity and credentials for each source, resume your pipeline draft from the workspace and complete **Configure source**. Continue with [Create data pipeline](https://redis.io/docs/latest/operate/rc/rdi/define)."}],"examples":[{"id":"create-network-load-balancer-rds-ex0","language":"sh","code":"$ nslookup <database-endpoint>","section_id":"create-network-load-balancer-rds"},{"id":"setup-lambda-function-ex0","language":"bash","code":"git clone https://github.com/redis/rdi-cloud-automation.git\n    cd rdi-cloud-automation/modules/aws-rds-lambda","section_id":"setup-lambda-function"},{"id":"setup-lambda-function-ex1","language":"hcl","code":"identifier             = \"rdi-failover-handler\"\n    db_endpoint            = \"your-cluster.cluster-xxxxxxxxx.us-east-1.rds.amazonaws.com\"\n    db_port                = 5432\n    elb_tg_arn             = \"arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/your-tg/xxxxxxxxx\"\n    rds_arn                = \"arn:aws:rds:us-east-1:123456789012:cluster:your-cluster\"\n    rds_cluster_identifier = \"your-cluster\"","section_id":"setup-lambda-function"},{"id":"setup-lambda-function-ex2","language":"bash","code":"terraform init\n    terraform apply","section_id":"setup-lambda-function"},{"id":"create-database-credentials-secrets-ex0","language":"bash","code":"openssl pkcs8 -topk8 -inform PEM -outform DER \\\n    -in /path/to/myclient.pem \\\n    -out /path/to/myclient.pk8 -nocrypt","section_id":"create-database-credentials-secrets"},{"id":"create-database-credentials-secrets-ex1","language":"json","code":"{\n    \"Version\" : \"2012-10-17\",\n    \"Statement\" : [ {\n        \"Sid\" : \"RedisDataIntegrationRoleAccess\",\n        \"Effect\" : \"Allow\",\n        \"Principal\" : \"*\",\n        \"Action\" : [ \"secretsmanager:GetSecretValue\", \"secretsmanager:DescribeSecret\" ],\n        \"Resource\" : \"*\",\n        \"Condition\" : {\n            \"StringLike\" : {\n                \"aws:PrincipalArn\" : \"arn:aws:iam::<AWS ACCOUNT ID>:role/redis-data-pipeline-secrets-role\"\n            }\n        }\n    } ]\n}","section_id":"create-database-credentials-secrets"},{"id":"create-database-credentials-secrets-ex2","language":"json","code":"{\n    \"Version\" : \"2012-10-17\",\n    \"Statement\" : [ {\n        \"Sid\" : \"RedisDataIntegrationRoleAccess\",\n        \"Effect\" : \"Allow\",\n        \"Principal\" : \"*\",\n        \"Action\" : [ \"secretsmanager:GetSecretValue\", \"secretsmanager:DescribeSecret\" ],\n        \"Resource\" : \"*\",\n        \"Condition\" : {\n            \"StringLike\" : {\n                \"aws:PrincipalArn\" : \"arn:aws:iam::<AWS ACCOUNT ID>:role/redis-data-pipeline-secrets-role\"\n            }\n        }\n    } ]\n}","section_id":"create-database-credentials-secrets"},{"id":"create-database-credentials-secrets-ex3","language":"json","code":"{\n    \"Version\" : \"2012-10-17\",\n    \"Statement\" : [ {\n        \"Sid\" : \"RedisDataIntegrationRoleAccess\",\n        \"Effect\" : \"Allow\",\n        \"Principal\" : \"*\",\n        \"Action\" : [ \"secretsmanager:GetSecretValue\", \"secretsmanager:DescribeSecret\" ],\n        \"Resource\" : \"*\",\n        \"Condition\" : {\n            \"StringLike\" : {\n                \"aws:PrincipalArn\" : \"arn:aws:iam::<AWS ACCOUNT ID>:role/redis-data-pipeline-secrets-role\"\n            }\n        }\n    } ]\n}","section_id":"create-database-credentials-secrets"},{"id":"create-database-credentials-secrets-ex4","language":"sh","code":"aws secretsmanager create-secret \\\n    --name <secret-name> \\\n    --secret-binary fileb://<path-to-client-key> \\\n    --kms-key-id <encryption-key-arn>","section_id":"create-database-credentials-secrets"},{"id":"create-database-credentials-secrets-ex5","language":"json","code":"{\n    \"Version\" : \"2012-10-17\",\n    \"Statement\" : [ {\n        \"Sid\" : \"RedisDataIntegrationRoleAccess\",\n        \"Effect\" : \"Allow\",\n        \"Principal\" : \"*\",\n        \"Action\" : [ \"secretsmanager:GetSecretValue\", \"secretsmanager:DescribeSecret\" ],\n        \"Resource\" : \"*\",\n        \"Condition\" : {\n            \"StringLike\" : {\n                \"aws:PrincipalArn\" : \"arn:aws:iam::<AWS ACCOUNT ID>:role/redis-data-pipeline-secrets-role\"\n            }\n        }\n    } ]\n}","section_id":"create-database-credentials-secrets"},{"id":"create-database-credentials-secrets-ex6","language":"json","code":"{\n    \"Version\" : \"2012-10-17\",\n    \"Statement\" : [ {\n        \"Sid\" : \"RedisDataIntegrationRoleAccess\",\n        \"Effect\" : \"Allow\",\n        \"Principal\" : \"*\",\n        \"Action\" : [ \"secretsmanager:GetSecretValue\", \"secretsmanager:DescribeSecret\" ],\n        \"Resource\" : \"*\",\n        \"Condition\" : {\n            \"StringLike\" : {\n                \"aws:PrincipalArn\" : \"arn:aws:iam::<AWS ACCOUNT ID>:role/redis-data-pipeline-secrets-role\"\n            }\n        }\n    } ]\n}","section_id":"create-database-credentials-secrets"},{"id":"create-database-credentials-secrets-ex7","language":"json","code":"{\n    \"Version\" : \"2012-10-17\",\n    \"Statement\" : [ {\n        \"Sid\" : \"RedisDataIntegrationRoleAccess\",\n        \"Effect\" : \"Allow\",\n        \"Principal\" : \"*\",\n        \"Action\" : [ \"secretsmanager:GetSecretValue\", \"secretsmanager:DescribeSecret\" ],\n        \"Resource\" : \"*\",\n        \"Condition\" : {\n            \"StringLike\" : {\n                \"aws:PrincipalArn\" : \"arn:aws:iam::<AWS ACCOUNT ID>:role/redis-data-pipeline-secrets-role\"\n            }\n        }\n    } ]\n}","section_id":"create-database-credentials-secrets"}]}
{"schema_version":2,"id":"operate/rc/rdi/use-cases","title":"Production use cases","url":"https://redis.io/docs/latest/operate/rc/rdi/use-cases/","summary":"Reference setups for Data Integration pipelines that run in production.","tags":["docs","operate","rc"],"last_updated":"2026-10-02T14:53:47+01:00","children":[{"id":"operate/rc/rdi/use-cases/sql-server-failover","summary":"Connect a Data Integration pipeline to a self-managed SQL Server Always On availability group and keep it running through failover.","title":"Connect a data pipeline to on-premises SQL Server and handle failover","url":"https://redis.io/docs/latest/operate/rc/rdi/use-cases/sql-server-failover/"}],"page_type":"content","content_hash":"881b0cd85c67e633359ccd91eda0511425c56679f23aade56a59502c6b1daf35","sections":[{"id":"content","title":"Content","role":"content","text":"This section describes complete Data Integration setups that customers run in production. Each guide covers a real topology from end to end, including the networking setup, the source database configuration, and how to handle failure scenarios.\n\nAvailable guides:\n\n- [Connect a data pipeline to on-premises SQL Server and handle failover](https://redis.io/docs/latest/operate/rc/rdi/use-cases/sql-server-failover): Connect a pipeline to a self-managed SQL Server Always On availability group and keep it running through failover and disaster recovery events."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/rdi/use-cases/sql-server-failover","title":"Connect a data pipeline to on-premises SQL Server and handle failover","url":"https://redis.io/docs/latest/operate/rc/rdi/use-cases/sql-server-failover/","summary":"Connect a Data Integration pipeline to a self-managed SQL Server Always On availability group and keep it running through failover.","tags":["docs","operate","rc"],"last_updated":"2026-10-02T14:53:47+01:00","page_type":"content","content_hash":"166c7b4ad1d71ea0d953aab7e35b1481048e7cf47ead01c5531e1247d045f15d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This guide shows how to connect a data pipeline to a self-managed Microsoft SQL Server deployment that uses an [Always On availability group](https://learn.microsoft.com/en-us/sql/database-engine/availability-groups/windows/overview-of-always-on-availability-groups-sql-server), and what to do when the deployment fails over. Self-managed means you run SQL Server yourself, on your own servers on-premises or on EC2 instances, rather than through a managed service such as Amazon RDS."},{"id":"topology","title":"Topology","role":"content","text":"The setup in this guide uses:\n\n- A SQL Server Always On availability group with a primary replica and one or more readable secondary replicas. The replicas can be in more than one site for disaster recovery.\n- A [Network Load Balancer and PrivateLink endpoint service](https://redis.io/docs/latest/operate/rc/rdi/setup#set-up-connectivity) in your AWS VPC. The NLB target group points to the replica that the pipeline should read from, which is usually a readable secondary. If the replicas are on premises, the NLB reaches them over AWS Direct Connect.\n- A Lambda function that updates the NLB target group when the deployment fails over. See [Automate the NLB update](#automate-the-nlb-update).\n\n[code example]"},{"id":"why-an-nlb-and-not-the-availability-group-listener","title":"Why an NLB and not the availability group listener","role":"content","text":"An availability group listener cannot replace the NLB in this setup, for two reasons:\n\n- The pipeline can only reach the one address that PrivateLink exposes. Read-only routing through a listener works by redirecting the client to the secondary replica's own address, and that address is not reachable through PrivateLink.\n- A listener requires a Windows Server Failover Cluster. Availability groups with `CLUSTER_TYPE = NONE` cannot have a listener at all.\n\nThe listener can still be useful inside your own network. For example, the Lambda function can resolve the listener DNS name to find the address of a replica after a failover."},{"id":"connect-to-a-readable-secondary","title":"Connect to a readable secondary","role":"content","text":"To make the pipeline read from a readable secondary instead of the primary, add the following property to the collector in your [pipeline configuration](https://redis.io/docs/latest/operate/rc/rdi/view-edit#edit-collector-properties):\n\n[code example]\n\nThis property changes two things in the collector:\n\n- The initial snapshot uses snapshot isolation instead of table locks, because a read-only replica does not allow locks. Readable secondaries in an availability group support snapshot isolation automatically.\n- The collector refreshes its view of the change tables on every poll, so it always sees new changes.\n\nKeep the following in mind:\n\n- The property is safe during failover. If the readable secondary the pipeline reads from is promoted to primary, the pipeline keeps working: it reconnects and resumes streaming from its saved position without taking a new snapshot, so the snapshot isolation requirement below does not apply to the failover itself. A primary accepts connections with `ApplicationIntent=ReadOnly`.\n- With this property set, the initial snapshot uses snapshot isolation, which requires `ALLOW_SNAPSHOT_ISOLATION` to be enabled on the source database. A readable secondary in an availability group provides this automatically, so no action is needed for the normal setup. Any other database does not have it enabled by default, so if the pipeline ever takes an initial snapshot against a primary or a standalone server (for example after a [restore from backup](#recover-after-a-restore-from-backup)), enable it first with `ALTER DATABASE <database> SET ALLOW_SNAPSHOT_ISOLATION ON`.\n- Leave the availability group's primary role connection setting at its default of `ALL`. If you set the primary role to `READ_WRITE`, the primary refuses connections that request `ApplicationIntent=ReadOnly`, so the pipeline cannot fall back to the primary when no readable secondary is available. See the `ALLOW_CONNECTIONS` options in [CREATE AVAILABILITY GROUP](https://learn.microsoft.com/en-us/sql/t-sql/statements/create-availability-group-transact-sql)."},{"id":"what-happens-during-a-failover","title":"What happens during a failover","role":"content","text":"The pipeline behaves as follows during a failover. In all of these scenarios, it needs no restart, redeploy, or configuration change:\n\n| Scenario | What happens |\n|:--|:--|\n| The secondary that the pipeline reads from is promoted to primary | The connection drops at promotion. After a brief interruption, the collector reconnects automatically, resumes from its saved position, and skips the snapshot. No data is lost. The new primary also needs its CDC jobs created before it produces changes. See [Create the CDC jobs on a promoted replica](#create-the-cdc-jobs-on-a-promoted-replica). |\n| The node is demoted back to a readable secondary | Same behavior. The collector reconnects and resumes from its saved position. |\n| The replica that the pipeline reads from goes down and the NLB is repointed to another replica | The pipeline retries while the path is down, reconnects through the unchanged PrivateLink once the NLB points to a healthy replica, and catches up completely. |\n| A database is restored from a backup older than the pipeline's saved position | The restored database is behind the pipeline, so reset the pipeline afterward. See [Disaster recovery and restoring from backup](#recover-after-a-restore-from-backup). |\n\nThe pipeline delivers events at least once. After a crash recovery, the collector can redeliver a small batch of already delivered events, and the target absorbs them by key."},{"id":"create-the-cdc-jobs-on-a-promoted-replica","title":"Create the CDC jobs on a promoted replica","role":"content","text":"There is one required action on the SQL Server side after every promotion. The CDC change tables and their history live in the user database, so the availability group replicates them to every replica. The CDC capture and cleanup jobs are SQL Agent jobs, which live in the `msdb` system database. The availability group does not replicate `msdb`, so these jobs do not exist on a replica until you create them there.\n\nMicrosoft documents this directly: to resume harvesting changes after a failover, `sys.sp_cdc_add_job` must be run at the new primary. Run both jobs on the promoted replica:\n\n[code example]\n\nUntil the capture job exists on the new primary, no new change events are produced, and the pipeline shows no error. The data flow just stops. If the promoted node previously held the primary role, it may already have these jobs from that time, so verify their state before you add them. For how to manage the CDC jobs across role changes, follow the Microsoft guidance linked below. Ask your database administrator to automate this step as part of the failover procedure.\n\nFor more information, see [Change data capture with Always On availability groups](https://learn.microsoft.com/en-us/sql/database-engine/availability-groups/windows/replicate-track-change-data-capture-always-on-availability#change-data-capture) and [sys.sp_cdc_add_job](https://learn.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sys-sp-cdc-add-job-transact-sql)."},{"id":"automate-the-nlb-update","title":"Automate the NLB update","role":"content","text":"When the replica that the pipeline connects to fails, something must point the NLB target group at another replica that the pipeline can read from. You can automate this with a Lambda function. There are two ways to trigger it:\n\n- **Event-driven**: When the deployment fails over, a script on any SQL Server node publishes to an SNS topic, and the SNS topic invokes the Lambda function. The Lambda function resolves a known address, such as the availability group listener DNS name, to find the replica to register in the target group. This avoids the detection delay of health checks.\n\n    > [!NOTE]\n    > Verify what the listener DNS name resolves to in your deployment before you rely on it. A listener DNS name usually resolves to the listener's virtual IP address, which routes to the primary replica. In that case the pipeline reads from the primary after the failover, which works, but the read load moves off the secondary.\n\n- **Health-check driven**: The NLB's own health checks detect the failure and trigger the Lambda function through a CloudWatch alarm. The Lambda function finds the replacement server, for example by an EC2 tag. This needs no changes on the SQL Server side, but detection takes several minutes.\n\nWith either trigger, add an email subscription to the SNS topic so that a person always knows a failover happened. This matters most for the restore scenario below, which needs a manual follow-up."},{"id":"recover-after-a-restore-from-backup","title":"Disaster recovery and restoring from backup","role":"content","text":"Disaster recovery with an availability group is safe for the pipeline. When a secondary is promoted, or you point the NLB at another in-sync replica, the pipeline reconnects and resumes from its saved position with no reset and no data loss. The availability group is responsible for keeping its replicas synchronized, so a promoted replica holds the committed transactions the pipeline has already read. For more information, see [Availability modes for an availability group](https://learn.microsoft.com/en-us/sql/database-engine/availability-groups/windows/availability-modes-always-on-availability-groups) in the SQL Server documentation.\n\nIf instead you restore the database from an old backup, rather than recovering it through the availability group, you risk a different situation: the restored database can be behind the position the pipeline has already processed, so the pipeline has nowhere valid to resume from. If you restore from a backup, [reset the pipeline](https://redis.io/docs/latest/operate/rc/rdi/view-edit#reset-data-pipeline) afterward to take a fresh snapshot.\n\nIf the restored server is standalone and not yet part of the availability group, enable snapshot isolation before you reset, or the snapshot fails and retries until you do:\n\n[code example]"}],"examples":[{"id":"topology-ex0","language":"mermaid {width=\"100%\"}","code":"graph LR\n    subgraph rc[\"Redis Cloud\"]\n        pipeline[\"<b>RDI pipeline</b>\"] --> endpoint[\"<b>VPC endpoint</b>\"]\n    end\n    subgraph vpc[\"Your AWS VPC\"]\n        nlb[\"<b>NLB</b>\"]\n        lambda[\"<b>Lambda</b>\"]\n    end\n    subgraph dc[\"Your data center\"]\n        subgraph ag[\"SQL Server AG\"]\n            primary[\"Primary\"]\n            secondary[\"Secondary (readable)\"]\n            dr[\"DR secondary\"]\n        end\n    end\n    endpoint -->|PrivateLink| nlb\n    nlb -->|Direct Connect| ag","section_id":"topology"},{"id":"connect-to-a-readable-secondary-ex0","language":"yaml","code":"driver.applicationIntent: ReadOnly","section_id":"connect-to-a-readable-secondary"},{"id":"create-the-cdc-jobs-on-a-promoted-replica-ex0","language":"sql","code":"USE <database>;\nEXEC sys.sp_cdc_add_job @job_type = N'capture';\nEXEC sys.sp_cdc_add_job @job_type = N'cleanup';","section_id":"create-the-cdc-jobs-on-a-promoted-replica"},{"id":"recover-after-a-restore-from-backup-ex0","language":"sql","code":"ALTER DATABASE <database> SET ALLOW_SNAPSHOT_ISOLATION ON;","section_id":"recover-after-a-restore-from-backup"}]}
{"schema_version":2,"id":"operate/rc/rdi/view-edit","title":"View and edit data pipeline","url":"https://redis.io/docs/latest/operate/rc/rdi/view-edit/","summary":"Observe and change your data pipeline.","aliases":["/operate/rc/databases/rdi/view-edit/","/operate/rc/databases/rdi/view-edit"],"tags":["docs","operate","rc"],"last_updated":"2026-10-18T03:32:17+03:00","page_type":"content","content_hash":"77e658c4a86a33ff6a5ef3752c6755f18aca043a3ec360db6befeec9890ef1a2","sections":[{"id":"overview","title":"Overview","role":"overview","text":"To manage a pipeline, select it from your workspace on the **Data Integration** page or from the **Data Integration** tab of your subscription or database.\n\nThe pipeline page has the following tabs:\n\n- [Dashboard](#dashboard)\n- [Metrics](#metrics)\n- [Settings](#settings)\n- [Configuration](#configuration)\n- [Dataset](#dataset)\n- [Transformations](#transformations)\n\nUse the **Sources** list in **Metrics**, **Configuration**, and **Dataset** to select the source you want to inspect. **Settings** applies to the whole pipeline. **Transformations** lists jobs with their source assignments."},{"id":"dashboard","title":"Dashboard","role":"content","text":"The **Dashboard** shows pipeline health, processor and target status, throughput, pending records, record health, and processor load. It also shows the number of sources, selected tables, and transformation jobs.\n\n![Pipeline dashboard with throughput, pending records, record health, and processor load.](https://redis.io/docs/latest/images/rc/rdi/rdi-2-dashboard.png)\n\nEach source has a card showing its database type, name, activity, and status. Expand a card for more details. Open the source's **More actions** menu to view its configuration, edit its dataset or transformations, or manage the source.\n\n![Three source cards: one PostgreSQL source and two MySQL sources, all streaming.](https://redis.io/docs/latest/images/rc/rdi/rdi-2-sources.png)\n\n![Source actions for configuration, dataset, transformations, stop, reset, and deletion.](https://redis.io/docs/latest/images/rc/rdi/rdi-2-source-actions.png)\n\nA pipeline can be streaming, stopped, or in an error or transitional state. If there is a problem, you can use the source cards and **Metrics** tab to identify the affected source before taking remedial action. Pipeline health and individual source status can differ, for example when one source is stopped."},{"id":"add-source","title":"Add a source","role":"content","text":"1. On **Dashboard**, select **Add source**.\n1. In **Add sources**, select **Add source** in the **Sources** list, choose the new database type, and enter a unique source name. Existing sources remain listed with their connection details read-only.\n1. Complete **Configure source**, **Select data**, and **Add transformations** for the new source. See [Create data pipeline](https://redis.io/docs/latest/operate/rc/rdi/define) for the configuration steps.\n1. Review the changes in **Review & deploy** and select **Deploy pipeline**.\n\n![Add-source wizard showing existing sources and the Add source control.](https://redis.io/docs/latest/images/rc/rdi/rdi-2-add-sources.png)\n\nAdding a source keeps the existing pipeline's shared target and settings, so the wizard starts at **Add sources**. A source with an incomplete setup appears with **Pending setup** in the workspace. You can resume an existing draft setup at any time to complete it. If you remove a source, wait for the removal process to finish before adding another source.\n\nWhen extending an older single-source pipeline, preserve the original source name and its existing job assignments. You do not need to recreate the pipeline to add a source."},{"id":"stop-and-start-source","title":"Stop and start a source","role":"content","text":"1. On **Dashboard**, open the source card's **More actions** menu.\n1. Select **Stop source**, then confirm with **Stop source**.\n\nStopping one source pauses data capture from that source without stopping the other sources. When you start a source again, its behavior depends on the source type:\n\n- Debezium sources follow their [snapshot mode](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config#choose-a-snapshot-mode) and the availability of their saved position.\n- Snowflake sources use RIOT-X and Snowflake Streams. They resume from the saved stream offset when the stream retains the required changes. If the retention window expires while the source is stopped, changes can be lost.\n\nTo recover a Snowflake source with an unusable saved position, set `clearOffset` to\n`true` in that source's **Collector source properties**, then apply the changes. In the\npipeline configuration, the property is\n`sources.<source-name>.advanced.riotx.clearOffset`. The Snowflake `snapshot` property\ncontrols whether the collector reloads existing rows:\n\n- `INITIAL` (default): Load current source rows, then capture new changes.\n- `NEVER`: Skip existing rows and capture only new changes.\n\nClearing the offset does not replay missed events or remove stale target records.\nAfter recovery, set `clearOffset` back to `false` so later collector starts preserve\nthe saved position. See [Edit collector properties](#edit-collector-properties).\n\nTo resume a source, open its **More actions** menu and select **Start source**, then confirm with **Start source**. A source processes data only while the pipeline is running. See [How does stopping sources affect billing?](https://redis.io/docs/latest/operate/rc/rdi/faq#stopping-and-billing)."},{"id":"reset-source","title":"Reset a source","role":"content","text":"Resetting one source clears its saved position. With the default `initial` mode, a Debezium source takes a new snapshot and reprocesses its selected data. Other [snapshot modes](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config#choose-a-snapshot-mode) can skip existing rows or omit ongoing change capture.\n\n1. On **Dashboard**, open the source card's **More actions** menu.\n1. Select **Reset source**.\n1. Review the effect and confirm with **Reset source**.\n\n![Reset source confirmation explaining that other sources retain their data and the pipeline temporarily stops.](https://redis.io/docs/latest/images/rc/rdi/rdi-2-reset-source.png)\n\nThe reset clears that source's internal RDI streams, offsets, schema history, rejected records, and processing counters. The whole pipeline and all its sources restart during the reset. Other sources keep their saved positions. Debezium sources restart according to their snapshot modes; for example, `always` mode takes another snapshot even when its saved position is preserved. All records already in the shared target database remain, including those from the reset source. The new snapshot can overwrite records for that source. See [What happens when I reset one source?](https://redis.io/docs/latest/operate/rc/rdi/faq#reset-one-source)."},{"id":"remove-source","title":"Remove a source","role":"content","text":"Before deleting a source, remove or reassign transformation jobs that refer to it and apply the changes. RDI rejects deletion while a transformation job still refers to the source.\n\n1. On **Dashboard**, open the source card's **More actions** menu.\n1. Select **Delete source**.\n1. Review the confirmation and select **Delete source**.\n\n![Delete source confirmation for the selected PostgreSQL source.](https://redis.io/docs/latest/images/rc/rdi/rdi-2-delete-source.png)\n\nDeleting a source removes its data selection and internal RDI state, including streams, offsets, schema history, rejected records, and processing counters. Records already written to the target Redis database remain there. Other sources retain their data. The whole pipeline stops while RDI cleans up the removed source and starts again afterwards.\n\nSource deletion cannot be undone. To remove its records from the target, see [Can I flush data for a single source?](https://redis.io/docs/latest/operate/rc/rdi/faq#flush-one-source)."},{"id":"change-target-database","title":"Change target database","role":"content","text":"You can change the target to another database in the same subscription.\n\n1. On **Dashboard**, open the target card's **More actions** menu and select **Change pipeline target**.\n1. Select the new target database.\n1. Confirm with **Change target**.\n\nAll sources share the new target. Changing the target restarts the pipeline. Whether a Debezium source reloads existing rows depends on its [snapshot mode](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config#choose-a-snapshot-mode). To copy existing source data to the new target, select modes that take data snapshots, then [reset the pipeline](#reset-data-pipeline)."},{"id":"metrics","title":"Metrics","role":"content","text":"Select a source in the **Sources** list to see its connection status, snapshot progress, queue usage, errors, and table-level record counts. Available collector diagnostics depend on the source type. Expand **Diagnostics** to see additional metrics for the selected source.\n\n![Metrics for the selected PostgreSQL source, including collector diagnostics and table-level counts.](https://redis.io/docs/latest/images/rc/rdi/rdi-2-metrics.png)\n\n| Metric | Description |\n|--------|-------------|\n| **Source table** | Name of the data stream. Each stream corresponds to a table from the source database.  |\n| **Total** | Total number of records that arrived from the source table. |\n| **Pending** | Number of records from the source table that are waiting to be processed. |\n| **Inserted** | Number of new records from the source table that have been written to the target database. |\n| **Updated** | Number of updated records from the source table that have been updated in the target database. |\n| **Deleted** | Number of deleted records from the source table that have been deleted in the target database. |\n| **Filtered** | Number of records from the source table that were filtered from being inserted into the target database. |\n| **Rejected** | Number of records from the source table that could not be parsed or inserted into the target database. Select a rejected count to open the [Rejected records](#rejected-records) view for that table. |"},{"id":"view-metrics-endpoints","title":"View metrics endpoints","role":"content","text":"On **Metrics**, select **Connect to Prometheus** to view the available collector and processor endpoints. Add the required endpoints to your Prometheus configuration. Select the relevant source when inspecting per-source metrics.\n\nPrometheus endpoints are exposed on Redis Cloud's internal network. To access this network, enable [VPC peering](https://redis.io/docs/latest/operate/rc/security/vpc-peering) or [AWS Transit Gateway](https://redis.io/docs/latest/operate/rc/security/aws-transit-gateway). See [Prometheus and Grafana with Redis Cloud](https://redis.io/docs/latest/integrate/prometheus-with-redis-cloud) for more information.\n\n> [!NOTE]\n> VPC peering and AWS Transit Gateway are the tested and supported methods for accessing Prometheus endpoints. AWS PrivateLink support for this feature is under evaluation and is not currently supported.\n\nFor more information about available RDI metrics, see [Observability](https://redis.io/docs/latest/integrate/redis-data-integration/observability)."},{"id":"rejected-records","title":"Rejected records","role":"content","text":"The **Rejected records** view shows records that RDI sent to the dead letter queue (DLQ) because processing failed. Open it from the rejected count on the **Dashboard** tab or from a table-level rejected count on the **Metrics** tab.\n\nThe view shows:\n\n- The total number of rejected records.\n- The number of affected tables.\n- The affected tables and their rejected counts.\n- Rejected record IDs and rejection times.\n- Safe troubleshooting metadata, such as the rejection reason, operation, affected table, and transformation job details when available. See [Using the operation code](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples/redis-opcode-example) for the operation labels.\n\nRedis Cloud uses the RDI DLQ API to show a sanitized view of rejected records. It\ndoes not show the original source record payload or every field stored in the\nDLQ stream. To inspect the full DLQ entry, connect to the RDI database and read\nthe corresponding DLQ stream directly.\n\nFor more information about why records are rejected and how RDI stores them, see [Rejected records](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/rejected-records)."},{"id":"settings","title":"Settings","role":"configuration","text":"The **Settings** tab contains the default data structure (**Hash** or **JSON**) and advanced processor properties. These settings apply to all sources in the pipeline.\n\n![Pipeline-wide Settings tab with default data structure and processor properties.](https://redis.io/docs/latest/images/rc/rdi/rdi-2-settings.png)\n\nSelect **Edit** to change these settings. Select **Save changes**, then **Apply and restart**. Every Cloud RDI pipeline uses the Flink processor. Review [processor properties](https://redis.io/docs/latest/integrate/redis-data-integration/reference/config-yaml-reference#processors-data-processing-configuration) before changing them. To change processor capacity, see [Scale a Cloud RDI pipeline](https://redis.io/docs/latest/operate/rc/rdi/scale-pipeline).\n\n![The processor advanced properties editor with key and value fields.](https://redis.io/docs/latest/images/rc/rdi/rdi-processor-advanced-properties.png)"},{"id":"configuration","title":"Configuration","role":"configuration","text":"Select a source in the **Sources** list to view its connectivity, secret references, and collector configuration. The connection details and secrets of a deployed source are read-only in this view."},{"id":"edit-collector-properties","title":"Edit collector properties","role":"content","text":"1. Select the source in **Configuration**.\n1. Select **Edit collector properties**.\n1. Update the [collector source properties](https://redis.io/docs/latest/integrate/redis-data-integration/reference/config-yaml-reference#sourcesadvancedsource-advanced-source-settings) or [collector sink properties](https://redis.io/docs/latest/integrate/redis-data-integration/reference/config-yaml-reference#sourcesadvancedsink-rdi-collector-stream-writer-configuration) for that source.\n\n    ![The advanced properties dialog with separate collector source and sink properties.](https://redis.io/docs/latest/images/rc/rdi/rdi-advanced-properties.png)\n\n1. Save the properties and review the restart confirmation before applying the changes."},{"id":"dataset","title":"Dataset","role":"content","text":"Select a source in the **Sources** list to view the schemas, tables, columns, and keys selected for ingestion from that source.\n\n![Dataset tab with a source selector and selected schema, tables, and columns.](https://redis.io/docs/latest/images/rc/rdi/rdi-2-dataset.png)"},{"id":"edit-dataset","title":"Edit dataset","role":"content","text":"1. Select the source, then select **Edit**.\n\n    ![The Edit button.](https://redis.io/docs/latest/images/rc/rdi/rdi-view-edit-button.png)\n\n1. Select the schemas, tables, and columns to ingest. Review the record key for each selected table and correct any missing-key warnings.\n\n    ![Selecting a schema shows its tables for ingestion.](https://redis.io/docs/latest/images/rc/rdi/rdi-dataset-schema-selected.png)\n\n    ![Selecting a table shows its columns and the columns selected for ingestion.](https://redis.io/docs/latest/images/rc/rdi/rdi-select-columns.png)\n\n    ![The missing unique key warning with the affected table and its columns.](https://redis.io/docs/latest/images/rc/rdi/rdi-dataset-missing-unique-key.png)\n\n    ![The key control beside the column used to identify a record.](https://redis.io/docs/latest/images/rc/rdi/rdi-unique-key-selected.png)\n\n1. Select **Save changes**.\n1. Review the restart warning and select **Apply and restart**.\n\nDataset changes belong to the selected source. Applying changes restarts the pipeline and can temporarily interrupt processing. To reload existing rows, select a snapshot mode that takes a data snapshot, then use [Reset a source](#reset-source)."},{"id":"transformations","title":"Transformations","role":"content","text":"The **Transformations** tab lists the pipeline's jobs, their source assignments, matching databases, schemas and tables, and validation status.\n\n![Transformation jobs assigned to three different sources, with verified status.](https://redis.io/docs/latest/images/rc/rdi/rdi-2-transformation-jobs.png)"},{"id":"edit-transformations","title":"Edit transformations","role":"content","text":"1. Select **Edit** on **Transformations**.\n1. Add, upload, or edit the [transformation jobs](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/transform-examples) needed for your tables.\n1. Select the **Source name** for each job.\n1. Resolve job validation errors and select **Save changes**.\n1. Review the restart warning and select **Apply and restart**.\n\nThe Flink processor accepts lists and `regex:` entries in source selection patterns, but rejects jobs where more than one job can apply to a given table. The default data structure and processor properties are in [Settings](#settings)."},{"id":"reset-data-pipeline","title":"Reset data pipeline","role":"content","text":"Resetting the whole pipeline clears its internal RDI state for all sources. A running pipeline restarts each source according to its configured initial data load settings. With the default `initial` mode, Debezium sources take new snapshots. Other [snapshot modes](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config#choose-a-snapshot-mode) can skip existing rows or omit ongoing change capture. A stopped pipeline remains stopped until you [start it](#stop-and-restart-data-pipeline). When a source takes a data snapshot, RDI reprocesses the selected data using the current transformations. Reset alone does not delete target records, and a new snapshot can [leave stale records after missed deletes](https://redis.io/docs/latest/integrate/redis-data-integration/data-pipelines/pipeline-config#missed-deletes). See [What happens when I reset the pipeline?](https://redis.io/docs/latest/operate/rc/rdi/faq#reset-pipeline).\n\n1. Open **Pipeline actions** and select **Reset pipeline**.\n1. Review the confirmation and select **Reset data pipeline**.\n\nTo clear the saved position of one source while preserving other sources' internal data, see [Reset a source](#reset-source)."},{"id":"stop-and-restart-data-pipeline","title":"Stop and restart data pipeline","role":"content","text":"1. Open **Pipeline actions** and select **Stop pipeline**.\n1. Confirm with **Stop pipeline**.\n\nStopping the pipeline pauses processing for all sources. To resume, open **Pipeline actions**, select **Start pipeline**, and confirm with **Start pipeline**. Use the [source actions](#stop-and-start-source) to control an individual source separately. Stopping all sources individually leaves the processor running. See [Billing](https://redis.io/docs/latest/operate/rc/rdi/faq#billing) for the difference in charges."},{"id":"flush-the-target-database","title":"Flush the target database","role":"content","text":"Flushing permanently deletes **all data** from the target database, including records from every source and data written outside RDI. It does not reset source positions or automatically reload the data.\n\n1. [Stop the pipeline](#stop-and-restart-data-pipeline) and wait until it is stopped.\n1. Open **Pipeline actions** and select **Flush target database**. This action is disabled while the pipeline is running.\n1. Check the target database and confirm with **Flush target database**.\n\nTo refill the target, follow [How do I reload data after a flush?](https://redis.io/docs/latest/operate/rc/rdi/faq#reload-after-flush)."},{"id":"delete-pipeline","title":"Delete pipeline","role":"content","text":"> [!NOTE]\n> You cannot delete a pipeline while the target database's Redis Cloud cluster is\n> in maintenance mode. Wait until maintenance is complete, then delete the\n> pipeline.\n\n1. Return to the workspace list on the **Data Integration** page or your database's **Data Integration** tab.\n1. Open the pipeline's actions menu and select **Delete pipeline**.\n1. Review and confirm the deletion.\n\nDeleted pipelines cannot be recovered. If the pipeline has an associated setup draft, deletion also removes that draft. You can then [delete the workspace](https://redis.io/docs/latest/operate/rc/rdi/create-workspace#delete-workspace) when it is no longer needed."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/resilient-apps","title":"Develop highly available and resilient apps with Redis Cloud","url":"https://redis.io/docs/latest/operate/rc/resilient-apps/","summary":"Set up your Redis Cloud databases to enable stable and highly available apps.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T14:59:36+01:00","page_type":"content","content_hash":"9692f2feaccf52f286e5d69b3767c518067f7c779579f3cfdfaa72d8743903fc","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can set up your Redis Cloud databases and Redis Client libraries to ensure your app re-connects to your database after unexpected failover events or network outages and minimize data losses."},{"id":"set-up-databases","title":"Set up databases","role":"content","text":"These settings can be turned on when you create or edit your database."},{"id":"enable-data-persistence","title":"Enable data persistence","role":"content","text":"Enabling [Data persistence](https://redis.io/docs/latest/operate/rc/databases/configuration/data-persistence) allows Redis to save your data to a durable storage medium, such as a disk, to ensure data availability in case of memory loss or system failure.\n\nRedis Cloud supports the following data persistence options:\n\n- An **Append-Only File** maintains a record of write operations. This lets you restore data by using the record to reconstruct the database up to the point of failure. The Append-only file updates on every write or every second.\n\n- A **Snapshot** is a copy of the in-memory database, taken at periodic intervals (one, six, or twelve hours). You can restore data to the snapshot's point in time. \n\nAppend-only files provide greater protection than snapshots at the cost of resources and recovery time. \nAlthough snapshot recovery is faster, the risk of data loss is higher, depending on the time between failure and the most recent snapshot.\n\nIf you do not enable data persistence, your data may be lost if the database goes down. It is best practice to  enable data persistence for all production databases. You can turn on data persistence when you create or edit your database."},{"id":"enable-replication","title":"Enable replication","role":"content","text":"[Database replication](https://redis.io/docs/latest/operate/rc/databases/configuration/high-availability) allows for automatic failover and greater fault tolerance. Replication can prevent data loss in the event of a hardware or zone failure. \n\nRedis Cloud supports these replication settings:\n\n- **No replication**: You will have a single copy of your database in one zone. If anything happens to your database, your app will not be able to connect to it. \n- **Single-Zone**: Your database will have a primary dataset and a replica dataset located in the same cloud zone. If anything happens to the primary database, the replica takes over and becomes the new primary. Your app will connect to the new primary database automatically with the same endpoint.\n- **Multi-Zone** _(or Multi-AZ)_: The primary and its replicas are stored in different availability zones. This means that your app will connect to the new primary database zone automatically if an entire availability zone becomes unavailable.\n\nYou can enable replication when you create your database. You can switch between no replication and single-zone replication after your create your database. However, you can't switch from no replication or single-zone replication to multi-zone replication.\n\nIt is best practice to enable replication for any databases that need to be highly available."},{"id":"create-active-active-databases-for-geographic-availability","title":"Create Active-Active databases for geographic availability","role":"content","text":"For geographic availability, create [Active-Active databases](https://redis.io/docs/latest/operate/rc/databases/active-active), which provide:\n\n- **Geographic distribution**: Data is replicated across multiple regions and availability zones. Applications can read from and write to the nearest region, reducing latency for users worldwide.\n\n- **99.999% availability**: Higher availability than single-region deployments by protecting against regional outages. Regions with fewer than three availability zones use single-zone replication and provide 99.99% (four-nines) availability instead. When an Active-Active database spans regions with different availability zone counts, the availability of the entire deployment is determined by the least resilient region.\n\n- **Automatic conflict resolution**: Uses conflict-free replicated data types (CRDTs) to handle concurrent writes across regions."},{"id":"set-manual-maintenance-windows","title":"Set manual maintenance windows","role":"content","text":"Redis maintains your Redis Cloud subscriptions and databases as needed to ensure your databases are running the most stable and up-to-date version of Redis. By default, Redis will perform [maintenance](https://redis.io/docs/latest/operate/rc/subscriptions/maintenance) automatically while limiting service disruption as much as possible.\n\nFor stable apps, you may want to control when Redis can perform maintenance on your databases. For Redis Cloud Pro subscriptions, you can [set manual maintenance windows](https://redis.io/docs/latest/operate/rc/subscriptions/maintenance/set-maintenance-windows) to ensure non-urgent maintenance will occur at set times. Configuring or altering the maintenance window will not have any impact on your subscription or databases.\n\nA Redis Cloud Essentials database has a set maintenance window based on the region where it is located. See [Essentials maintenance](https://redis.io/docs/latest/operate/rc/subscriptions/maintenance#redis-cloud-essentials) for more information."},{"id":"set-up-redis-clients","title":"Set up Redis clients","role":"content","text":"When you're developing your apps, it is best to use specific Redis Client features to connect to Redis Cloud if they are available for your preferred client. Use the latest version of your client library to ensure you have access to the latest resilience features and security patches.\n\nSee [Clients](https://redis.io/docs/latest/develop/clients) to learn how to connect with the official Redis clients."},{"id":"use-smart-client-handoffs","title":"Use Smart Client Handoffs","role":"content","text":"Some Redis client libraries support [Smart Client Handoffs (SCH)](https://redis.io/docs/latest/develop/clients/sch).\nSCH allows the Cloud server to notify the client when server maintenance is about to start. The client can then take action to avoid disruptions in service.\n\nSee [Smart client handoffs](https://redis.io/docs/latest/develop/clients/sch) for more information about SCH,\nincluding a list of clients that support it."},{"id":"production-usage-guides","title":"Production usage guides","role":"content","text":"For production-ready configurations and best practices, see the production usage guides for each client:\n\n- [redis-py](https://redis.io/docs/latest/develop/clients/redis-py/produsage)\n- [Node.js](https://redis.io/docs/latest/develop/clients/nodejs/produsage)\n- [Jedis](https://redis.io/docs/latest/develop/clients/jedis/produsage)\n- [Lettuce](https://redis.io/docs/latest/develop/clients/lettuce/produsage)\n- [go-redis](https://redis.io/docs/latest/develop/clients/go/produsage)\n- [StackExchange.Redis](https://redis.io/docs/latest/develop/clients/dotnet/produsage)"},{"id":"re-attempt-connections","title":"Re-attempt connections","role":"content","text":"Some clients allow you to re-try connecting to your database if the connection fails. For these clients, we recommend that you implement connection re-attempts to ensure high availability and connection stability. \n\nView your [client's docs](https://redis.io/docs/latest/develop/clients) to learn more."},{"id":"refresh-dns","title":"Refresh DNS","role":"content","text":"Your application may disconnect from your database either during planned maintenance or for other, unplanned reasons. Most Redis clients are set to refresh their DNS address when they reconnect to the database, and you will not be required to perform any further action. If you encounter connectivity problems for more than a minute during maintenance then you should refresh your DNS entries. \n\nDepending on the client, you may be recommended to turn off the DNS cache entirely. Refer to your [client's docs](https://redis.io/docs/latest/develop/clients) to learn more."},{"id":"use-the-wait-and-waitaof-commands","title":"Use the WAIT and WAITAOF commands","role":"content","text":"The [WAIT](https://redis.io/docs/latest/commands/wait) and [WAITAOF](https://redis.io/docs/latest/commands/waitaof) commands block the current client until all previous write commands are persisted between replicas. With these commands, your application guarantees that acknowledged writes are recorded between replicas. \n\nFor more info, see [Use the WAIT command for strong consistency](https://redis.io/docs/latest/operate/rs/clusters/optimize/wait)."},{"id":"test-failover-behavior","title":"Test failover behavior","role":"content","text":"You can test your app's failover behavior in Redis Cloud by running a failover test. A failover test in Redis Cloud simulates a controlled disruption such as an endpoint migration, node failure, or cluster outage to confirm that your app can reconnect, recover, and continue operating without data loss. These tests are a critical part of validating high-availability and disaster recovery, ensuring that applications meet recovery time and recovery point objectives. \n\nFor more info, see [How to run a Failover Test in Redis Cloud](https://support.redislabs.com/hc/en-us/articles/29001166157074-How-to-Run-a-Failover-Test-in-Redis-Cloud)."},{"id":"more-info","title":"More info","role":"content","text":"- [Redis Clients](https://redis.io/docs/latest/develop/clients)\n- [Active-Active Redis](https://redis.io/docs/latest/operate/rc/databases/active-active)\n- [Active-Active Redis applications](https://redis.io/docs/latest/operate/rs/databases/active-active/develop)"}],"examples":[]}
{"schema_version":2,"id":"operate/rc/security/access-control/access-management","title":"Access management","url":"https://redis.io/docs/latest/operate/rc/security/access-control/access-management/","summary":"Control who can make changes to your databases using the Redis Cloud console.","tags":["docs","operate","rc"],"last_updated":"2026-10-02T16:06:07+01:00","page_type":"content","content_hash":"f860469c5f9030e121afaa89440a6b94b30f65d74aeb7558a251c901aa2b72ba","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The **Access management** screen helps you manage:\n\n- The team of users allowed to access your subscription and its databases.\n- The API keys that authenticate application access to your account.\n- [Single sign-on (SSO) with SAML](https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso).\n\nHere, you learn how to manage your team's users and control their level of access. \n\nFor help managing API keys, see [Manage API keys](https://redis.io/docs/latest/operate/rc/api/get-started/manage-api-keys)."},{"id":"manage-team-access","title":"Manage team access","role":"content","text":"The **Team** tab lets you manage the people allowed to access your account. Each authorized person is assigned to a role that specifies their privileges.\n\n![The Access management tab helps you manage the people allowed to access your subscription.](https://redis.io/docs/latest/images/rc/access-management-team-tab.png)\n\nThe list contains one entry summarizing the team settings for each user in your team. By default, the list includes the account owner.\n\nThe list includes several buttons and icons to help you manage the list:\n\n| Icon | Description |\n|------|-------------|\n| ![Use the Add button to add members to your team.](https://redis.io/docs/latest/images/rc/icon-add.png#no-click) | The **Add** button lets you add members to your team |\n| ![Use the Edit button change details for a team member.](https://redis.io/docs/latest/images/rc/icon-edit.png#no-click) | The **Edit** button lets you edit the settings for the selected team member |\n| ![Use the Delete button to remove a member from your team.](https://redis.io/docs/latest/images/rc/icon-delete-teal.png#no-click) | The **Delete** button lets you remove members from your team\n| <nobr>![The Sort ascending button displays members in ascending order according to the values of the selected field.](https://redis.io/docs/latest/images/rc/icon-list-sort-asc.png#no-click)![The Sort descending button displays members in descending order according to the values of the selected field.](https://redis.io/docs/latest/images/rc/icon-list-sort-desc.png#no-click)</nobr> | The **Sort ascending** and **Sort descending** icons display the list according to the selected order |\n\nYou can also use the list search to find a specific user or filter by **Role**, **User Type**, or **Options**."},{"id":"add-user","title":"Add user","role":"content","text":"When you add a member to your team, the **Add user** dialog appears.  \n\n![Use the Add User dialog to specify the details for your new user.](https://redis.io/docs/latest/images/rc/access-mgmt-add-user-dialog.png)\n\nUse the dialog to specify these values.\n\n| Setting | Description |\n|---------|-------------|\n| **First name** | First name of the user displayed in the Redis Cloud console and in email messages |\n| **Last name** | Last name of the user displayed in the Redis Cloud console and in email messages |\n| **Role** | The role identifies their subscription and account privileges.  For details, see [Team management roles](#team-management-roles). |\n| **Email** | The address used for alerts and other email messages regarding the account | \n| **Alert emails** | Enable to be notified when subscription databases cross certain thresholds, such as exceeding memory limits or latency requirements |\n| **Operational emails** | Notifications about subscription and database changes, such as creating or deleting a database, and [subscription and database maintenance](https://redis.io/docs/latest/operate/rc/subscriptions/maintenance) |\n| **Billing emails** | Notifications about billing, such as when bills are issued and paid |\n| **Multi-factor authentication** | Whether MFA is enabled for the member.  This is deactivated when members have not enabled or confirmed MFA in their user profile settings. |\n\nUse the **Add user** button to save your new team member details. \n\nRedis will send an activation email to the user once their details are saved. After following the activation link, they can sign in."},{"id":"edit-user","title":"Edit user","role":"content","text":"To edit user team details, select the user from the list and then select the **Edit** button.\nThe **Edit user** dialog displays the details you can change.  \n\n![Use the Edit User dialog to change the details for a user](https://redis.io/docs/latest/images/rc/access-mgmt-edit-user-dialog.png)\n\nYou can change any detail except the team member's email address.\n\nSelect **Save user** to save your changes."},{"id":"delete-user","title":"Delete user","role":"content","text":"To remove a member from your team, select them from the list and then select the **Delete** button. A confirmation dialog appears.  \n\n![Confirm that you want to remove a user from your team](https://redis.io/docs/latest/images/rc/access-management-delete-user-dialog.png)\n\nSelect **Delete user** to confirm removal. This is a permanent action that cannot be undone."},{"id":"team-management-roles","title":"Team management roles","role":"content","text":"Each team member is assigned a role that identifies their privileges and limits their activities in the Redis Cloud console.\n\nRoles and responsibilities are:\n\n- **Owner** can view, create, and edit any settings in the account.\n\n    Each subscription must have at least one account owner. Accounts can have multiple owners.\n\n    Owners can also manage subscriptions, databases, and API keys.\n\n- **Billing Admin** can view and edit settings related to billing and payments.\n\n    Billing Admins can add and remove payment methods and change the payment method for a subscription, but they cannot change any other subscription or database settings.\n\n- **Manager** can view, create, and edit any setting in the subscription. \n\n    Managers can change subscription costs and change payment methods associated with a subscription, but they cannot add or remove available payment methods.\n\n- **Member** can view, create, and edit databases in ways that do not impact costs.\n\n    Members cannot create databases or edit databases in ways that impact costs.\n\n- **Viewer** can view all databases and their configurations, including database secrets.\n\n- **Logs viewer** can not access the Redis Cloud console. They are only allowed access to the [Redis Cloud API](https://redis.io/docs/latest/operate/rc/api) [`GET logs/`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Account/operation/getAccountSystemLogs) endpoint. \n\nThis table shows each role's ability to perform common tasks.\n\n| **Task** | **Owner** | **Billing Admin** | **Manager** | **Member** | **Viewer** | **Logs Viewer** |\n|---|---|---|---|---|---|---|\n| Access management | <span title=\"Owners can manage account access\"><nobr>&#x2705; Yes</nobr></span> | <span title=\"Billing Admins may not manage account access\"><nobr>&#x274c; No</nobr></span> | <span title=\"Managers may not manage account access\"><nobr>&#x274c; No</nobr></span> | <span title=\"Members may not manage account access\"><nobr>&#x274c; No</nobr></span> | <span title=\"Viewers may not manage account access\"><nobr>&#x274c; No</nobr></span> | <span title=\"Logs Viewers may not manage account access\"><nobr>&#x274c; No</nobr></span> |\n| Account settings | <span title=\"Owners can change account settings\"><nobr>&#x2705; Yes</nobr></span> | <span title=\"Billing Admins can change the billing address in account settings\"><nobr>&#x2705; Yes<sup>[1](#table-note-1)</sup></nobr></span> | <span title=\"Managers may not manage account settings\"><nobr>&#x274c; No</nobr></span> | <span title=\"Members may not manage account settings\"><nobr>&#x274c; No</nobr></span> | <span title=\"Viewers may not manage account settings\"><nobr>&#x274c; No</nobr></span> | <span title=\"Logs Viewers may not manage account settings\"><nobr>&#x274c; No</nobr></span> |\n| Billing & payments | <span title=\"Owners can add/remove payment methods and view history\"><nobr>&#x2705; Yes</nobr></span> | <span title=\"Billing Admins can add/remove payment methods and view history\"><nobr>&#x2705; Yes</nobr></span> | <span title=\"Managers may not add/remove payment methods or view billing history (Managers may change subscription payment methods between those available to the account)\"><nobr>&#x274c; No<sup>[2](#table-note-2)</sup></nobr></span> | <span title=\"Members may not add/remove payment methods or view billing history\"><nobr>&#x274c; No</nobr></span> | <span title=\"Viewers may not add/remove payment methods or view billing history\"><nobr>&#x274c; No</nobr></span> | <span title=\"Logs viewers may not add/remove payment methods or view billing history\"><nobr>&#x274c; No</nobr></span> |\n| Create subscription | <span title=\"Owners can create new subscriptions\"><nobr>&#x2705; Yes</nobr></span> | <span title=\"Billing Admins may not create subscriptions\"><nobr>&#x274c; No</nobr></span> | <span title=\"Managers can create new subscriptions\"><nobr>&#x2705; Yes</nobr></span> | <span title=\"Members may not create subscriptions\"><nobr>&#x274c; No</nobr></span> | <span title=\"Viewers may not create subscriptions\"><nobr>&#x274c; No</nobr></span> | <span title=\"Logs viewers may not create subscriptions\"><nobr>&#x274c; No</nobr></span> |\n| Edit subscription | <span title=\"Owners can edit subscriptions\"><nobr>&#x2705; Yes</nobr></span> | <span title=\"Billing Admins can edit subscriptions to change the payment method\"><nobr>&#x2705; Yes<sup>[3](#table-note-3)</sup></nobr></span> | <span title=\"Managers can edit subscriptions\"><nobr>&#x2705; Yes</nobr></span> | <span title=\"Members may not edit subscriptions\"><nobr>&#x274c; No</nobr></span> | <span title=\"Viewers may not edit subscriptions\"><nobr>&#x274c; No</nobr></span> | <span title=\"Logs viewers may not edit subscriptions\"><nobr>&#x274c; No</nobr></span> |\n| Create database (affects cost) | <span title=\"Owners can create databases when doing so will impact costs\"><nobr>&#x2705; Yes</nobr></span> | <span title=\"Billing Admins may not create databases when doing so will impact costs\"><nobr>&#x274c; No</nobr></span> | <span title=\"Managers can create databases when doing so will impact costs\"><nobr>&#x2705; Yes</nobr></span> | <span title=\"Members may not create databases when doing so will impact costs\"><nobr>&#x274c; No</nobr></span> | <span title=\"Viewers may not create databases when doing so will impact costs\"><nobr>&#x274c; No</nobr></span> | <span title=\"Logs viewers may not create databases when doing so will impact costs\"><nobr>&#x274c; No</nobr></span> |\n| Edit database (affects cost) | <span title=\"Owners can edit databases in ways that will impact costs\"><nobr>&#x2705; Yes</nobr></span> | <span title=\"Billing Admins may not edit databases in ways that impact costs\"><nobr>&#x274c; No</nobr></span> | <span title=\"Managers can edit databases in ways that impact costs\"><nobr>&#x2705; Yes</nobr></span> | <span title=\"Members may not edit databases in ways that impact costs\"><nobr>&#x274c; No</nobr></span> | <span title=\"Viewers may not edit databases in ways that impact costs\"><nobr>&#x274c; No</nobr></span> | <span title=\"Logs viewers may not edit databases in ways that impact costs\"><nobr>&#x274c; No</nobr></span> |\n| Create database (no cost impact) | <span title=\"Owners can create databases when doing so will not impact costs\"><nobr>&#x2705; Yes</nobr></span> | <span title=\"Billing Admins may not create databases when doing so will not impact costs\"><nobr>&#x274c; No</nobr></span> | <span title=\"Managers can create databases when doing so will not impact costs\"><nobr>&#x2705; Yes</nobr></span> | <span title=\"Members can not create databases when doing so will not impact costs\"><nobr>&#x274c; No</nobr></span> | <span title=\"Viewers may not create databases when doing so will not impact costs\"><nobr>&#x274c; No</nobr></span> | <span title=\"Logs viewers may not create databases when doing so will not impact costs\"><nobr>&#x274c; No</nobr></span> |\n| Edit database (no cost impact) | <span title=\"Owners can edit databases in ways that do not impact costs\"><nobr>&#x2705; Yes</nobr></span> | <span title=\"Billing Admins may not change databases in ways that do not affect costs\"><nobr>&#x274c; No</nobr></span> | <span title=\"Managers can change databases in ways that do not affect costs\"><nobr>&#x2705; Yes</nobr></span> | <span title=\"Members can change databases in ways that do not affect costs\"><nobr>&#x2705; Yes</nobr></span> | <span title=\"Viewers may not change databases in ways that do not affect costs\"><nobr>&#x274c; No</nobr></span> | <span title=\"Logs viewers may not change databases in ways that do not affect costs\"><nobr>&#x274c; No</nobr></span> |\n| View subscription | <span title=\"Owners can view subscription details\"><nobr>&#x2705; Yes</nobr></span> | <span title=\"Billing Admins can view subscription details\"><nobr>&#x2705; Yes</nobr></span> | <span title=\"Managers can view subscription details\"><nobr>&#x2705; Yes</nobr></span> | <span title=\"Members can view subscription details\"><nobr>&#x2705; Yes</nobr></span> | <span title=\"Viewers can view subscription details\"><nobr>&#x2705; Yes</nobr></span> | <span title=\"Logs viewers may not view subscriptions\"><nobr>&#x274c; No</nobr></span> |\n| View database | <span title=\"Owners can view database details\"><nobr>&#x2705; Yes</nobr></span> | <span title=\"Billing Admins can view the list of databases but not database details\"><nobr>&#x2705; Yes<sup>[4](#table-note-4)</sup></nobr></span> | <span title=\"Managers can view database details\"><nobr>&#x2705; Yes</nobr></span> | <span title=\"Members can view database details\"><nobr>&#x2705; Yes</nobr></span> | <span title=\"Viewers can view database details\"><nobr>&#x2705; Yes</nobr></span> | <span title=\"Logs viewers may not view databases\"><nobr>&#x274c; No</nobr></span> |\n| Use the [REST API](https://redis.io/docs/latest/operate/rc/api) | <span title=\"Owners can use the Redis Cloud API\"><nobr>&#x2705; Yes</nobr></span> | <span title=\"Billing Admins can use the Redis Cloud REST API for billing data\"><nobr>&#x2705; Yes<sup>[5](#table-note-5)</sup></nobr></span> | <span title=\"Managers may not use the REST API\"><nobr>&#x274c; No</nobr></span> | <span title=\"Members may not use the REST API\"><nobr>&#x274c; No</nobr></span> | <span title=\"Viewers can use the REST API for GET requests only\"><nobr>&#x2705; Yes<sup>[6](#table-note-6)</sup></nobr></span> | <span title=\"Logs viewers can use the REST API for GET /logs only\"><nobr>&#x2705; Yes<sup>[7](#table-note-7)</sup></nobr></span> |\n\n1. <a name=\"table-note-1\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>Billing Admins can only edit the account billing address in Account Settings.\n\n2. <a name=\"table-note-2\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>Managers are able to view existing payment methods and assign them as payment methods for subscriptions, but they cannot add or remove payment methods.\n\n3. <a name=\"table-note-3\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>Billing Admins can only change the payment method when editing a subscription.\n\n4. <a name=\"table-note-4\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>Billing Admins can see the list of databases, but can not see database details, including connection details.\n\n5. <a name=\"table-note-5\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>Billing admins can use REST API for billing data.\n\n6. <a name=\"table-note-6\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>Viewers can use the REST API for GET requests, but cannot modify subscription or database details.\n\n7. <a name=\"table-note-7\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>Logs viewers can only use the [`GET logs/`](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Account/operation/getAccountSystemLogs) endpoint of the REST API."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/security/access-control/data-access-control/active-active-roles","title":"Create roles for Active-Active databases","url":"https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/active-active-roles/","summary":"","tags":["docs","operate","rc"],"last_updated":"2026-10-01T14:41:35+01:00","page_type":"content","content_hash":"a39a022ae7eabcff52178b683898205c006adc4078e952f9864274101bafd8e9","sections":[{"id":"content","title":"Content","role":"content","text":"For [Active-Active databases](https://redis.io/docs/latest/operate/rc/databases/active-active), you can define roles with different levels of access for different regions. For example, you can define a user role with full database access in one region and read-only access in another. Or, you can prevent a user from running any commands in a specified region.\n\n1. Go to **Data Access Control > Roles** and either select `+` to create a new role or point to an existing role and select the pencil icon to edit it.\n\n    ![Add or edit a role.](https://redis.io/docs/latest/images/rc/data-access-control-roles-add-or-edit.png)\n\n1. In the **Associations** section of the **Edit role** or **Create new role** screen, you can select `+` to create a new association or point to an existing association and select the pencil icon to edit it.\n\n1. Select one or more Active-Active databases from the **Databases** list. Active-Active databases are marked with a globe icon.\n\n    ![List of databases. Active-Active databases are marked with a globe icon.](https://redis.io/docs/latest/images/rc/roles-select-aa-database.png)\n\n1. To set the role's default level of access to the selected databases, choose a **Redis ACL** from the list and select the check mark to confirm the association.\n\n    > [!NOTE]\n    > The default level of access to the selected database only applies to regions that exist when the role is created. If you add a new region to your Active-Active subscription, the new region will default to **No Access** for the role.\n    >\n\n1. Select the ACL name next to a region to change which ACL applies to that region.\n\n    ![Assign different ACL rules for different regions.](https://redis.io/docs/latest/images/rc/roles-assign-rules-active-active.png)\n\n    In addition to the ACL rules that are already configured, you can set a role to have **No-Access** in a region. This is a special rule that prevents a user with this role from running any commands when connecting to the database in that region.\n\n1. Select **Save role**.\n\nWhen you assign a user-defined ACL rule to a role and associate it with one or more databases, Redis will verify that the ACL rule will work with the selected databases. Verify that the [syntax](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/configure-acls#define-permissions-with-acl-syntax) of the ACL rule is correct if you receive an error.\n\nAfter you create a role, you can assign it to a user. Users with this role can access the databases according to the role's associated Redis ACLs. For more information, see [Assign roles to users](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/create-assign-users#assign-roles-to-existing-users).\n\n> [!NOTE]\n> Applying ACLs to the database is an asynchronous operation. You may notice a delay before an ACL is fully applied to the database."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/security/access-control/data-access-control/configure-acls","title":"Configure permissions with Redis ACLs","url":"https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/configure-acls/","summary":"","tags":["docs","operate","rc"],"last_updated":"2026-10-01T14:41:35+01:00","page_type":"content","content_hash":"50b7a59975bcbad7da4974a9fbbd0edb5d2934be35a0b3f1c5cc49cddc3592d6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can define custom Redis ACL rules to assign to a data access role or use predefined Redis ACLs.\n\nRedis provides three predefined ACL rules, which are marked with the Redis logo and can not be changed:\n\n- **Full-Access**: Allows all commands. \n- **Read-Write**: Allows read and write commands and excludes dangerous commands.\n- **Read-Only**: Allows read commands only.\n\nACLs that are not marked with the Redis logo are user-defined ACL rules.\n\nTo configure a Redis ACL that you can assign to a data access role:\n\n1. Go to **Data Access Control** from the [Redis Cloud console](https://cloud.redis.io/#/) menu.\n\n    ![Menu for database access control.](https://redis.io/docs/latest/images/rc/data-access-control-menu.png)\n\n1. Select the **Redis ACLs** tab.\n\n    ![Redis ACLs area.](https://redis.io/docs/latest/images/rc/data-access-control-redis-acls.png)\n\n1. Either select `+` to create a new Redis ACL or point to an existing ACL and select the pencil icon to edit it.\n\n    ![Add or Update Redis ACL.](https://redis.io/docs/latest/images/rc/data-access-control-redis-acls-add-or-update.png)\n\n    > [!NOTE]\n    > The built-in Redis ACLs can't be edited.\n\n1. Provide a descriptive name and create the ACL rule [using ACL syntax](#define-permissions-with-acl-syntax).\n\n    ![Add Redis ACL.](https://redis.io/docs/latest/images/rc/data-access-control-redis-acls-add.png)\n\n1. Select the check mark to save your changes.  Your new Redis ACL should appear in the list.\n\n    ![Saved Redis ACL.](https://redis.io/docs/latest/images/rc/data-access-control-redis-acls-saved.png)\n\nAfter you create a Redis ACL, you can assign it to a role. Redis ACLs are not fully verified until they are assigned to a role. For more information, see [Create roles](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/create-roles) or [Active-Active access roles](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/active-active-roles) for an [Active-Active database](https://redis.io/docs/latest/operate/rc/databases/active-active)."},{"id":"define-permissions-with-acl-syntax","title":"Define permissions with ACL syntax","role":"content","text":"You can define these permissions using the [Redis ACL syntax](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl#acl-rules). This syntax lets you concisely specify which commands, command categories, keys, and pub/sub channels to allow.\n\n- `+` *includes* commands or command categories\n- `-` *excludes* commands or command categories\n- `@` indicates a command category\n- `~` defines a permitted key pattern\n- `&` allows access to a [pub/sub channel](https://redis.io/docs/latest/develop/pubsub)\n\nThe Redis Cloud console will validate your ACL syntax while you are typing."},{"id":"command-acl-rules","title":"Command ACL rules","role":"syntax","text":"A **command** can be any [Redis command](/content/commands).\n\nFor example, this Redis ACL rule indicates that the `SET` command is permitted:\n\n[code example]"},{"id":"command-category-acl-rules","title":"Command category ACL rules","role":"syntax","text":"A [**command category**](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl#command-categories) is a predefined, named set of commands.\n\nFor example, the Redis commands that\nread data are available in the `read` command category. This Redis ACL rule permits access to all read commands:\n\n[code example]\n\nTo find out which commands are included in the\n`read` command category, run the following command with [`redis-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/redis-cli):\n\n[code example]"},{"id":"key-acl-rules","title":"Key ACL rules","role":"content","text":"To specify which **keys** are accessible, use the [key permissions syntax](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl#key-permissions).\n\nThe following ACL rule allows access to all keys:\n\n[code example]\n\nWhereas, this ACL rule only allows access to keys prefixed with `cache:`\n\n[code example]\n\nStarting with Redis 7.0, key patterns can also be used to define how a command is able to read or write a key.\n\nThe following ACL rule allows you to copy information from keys prefixed with `cache:` into keys prefixed with `app:`:\n\n[code example]\n\nFor more information on how this works, see the [key permissions syntax](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl#key-permissions)."},{"id":"pubsub-acl-rules","title":"Pub/sub ACL rules","role":"content","text":"Pub/sub ACL rules determine which pub/sub channels a user can access. For more information see, [Redis pub/sub](https://redis.io/docs/latest/develop/pubsub)\n\nFor versions older than Redis 7.0, pub/sub is permissive and allows access to all channels by default.\n\nRedis 7.0 makes pub/sub restrictive and blocks access to all channels in Redis Software. However, Redis Cloud still defaults to permissive pub/sub even for Redis 7.0 subscriptions.\n\n| Redis<br />version | Redis Open Source<br />pub/sub ACLs | Redis Cloud<br />pub/sub ACLs |\n|:-------------:|-----------|-------------|\n| 6.0 | Not supported | Not supported |\n| 6.2 | Permissive | Permissive |\n| 7.0 | Restrictive | Permissive |\n\n#### Restrict channel access\n\nTo block access to all channels, use the following ACL rule:\n\n[code example]\n\nIf you want to limit access to specific channels, first include `resetchannels`. Then use `&` syntax to allow access to particular channels:\n\n[code example]\n\n#### Allow all channels\n\nTo make pub/sub explicitly permissive and allow users to access all channels, set the following rule:\n\n[code example]"},{"id":"selectors","title":"Selectors","role":"content","text":"Starting with Redis 7.0, Redis supports adding multiple sets of rules that are evaluated independently of each other, called [selectors](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl#selectors). \n\nThe following ACL rule allows a user to execute `GET` on keys prefixed with `cache` and `SET` on keys prefixed with `app`:\n\n[code example]"},{"id":"predefined-permissions","title":"Predefined permissions","role":"content","text":"Redis Cloud includes three predefined permissions:\n\n- Full-Access (`+@all ~*`) - All commands are allowed for all keys.\n\n- Read-Write (`+@all -@dangerous ~*`) - All commands except for the `dangerous` command category are allowed for all keys.\n\n- Read-Only (`+@read ~*`) - Only the `read` command category is allowed for all keys."},{"id":"advanced-capability-command-permissions","title":"Advanced capability command permissions","role":"content","text":"Note that you can define permissions for the Redis commands of any advanced capabilities that are part of your subscription;\nhowever, these permissions can only be used for databases that support those capabilities."}],"examples":[{"id":"command-acl-rules-ex0","language":"sh","code":"+set","section_id":"command-acl-rules"},{"id":"command-category-acl-rules-ex0","language":"sh","code":"+@read","section_id":"command-category-acl-rules"},{"id":"command-category-acl-rules-ex1","language":"sh","code":"ACL CAT read","section_id":"command-category-acl-rules"},{"id":"key-acl-rules-ex0","language":"sh","code":"~*","section_id":"key-acl-rules"},{"id":"key-acl-rules-ex1","language":"plaintext","code":"~cache:*","section_id":"key-acl-rules"},{"id":"key-acl-rules-ex2","language":"text","code":"+@all ~app:* %R~cache:*","section_id":"key-acl-rules"},{"id":"pubsub-acl-rules-ex0","language":"sh","code":"resetchannels","section_id":"pubsub-acl-rules"},{"id":"pubsub-acl-rules-ex1","language":"sh","code":"resetchannels &channel1 &channel2","section_id":"pubsub-acl-rules"},{"id":"pubsub-acl-rules-ex2","language":"sh","code":"allchannels","section_id":"pubsub-acl-rules"},{"id":"selectors-ex0","language":"text","code":"+GET ~cache:* (+SET ~app:*)","section_id":"selectors"}]}
{"schema_version":2,"id":"operate/rc/security/access-control/data-access-control/create-assign-users","title":"Create and edit database users","url":"https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/create-assign-users/","summary":"Create a database user and assign it a role.","tags":["docs","operate","rc"],"last_updated":"2026-10-01T14:41:35+01:00","page_type":"content","content_hash":"43034cab2e98dfec72c9a2c1dcd69384c48482a47fb9d4b7bbc786282798923d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Before you create a database user, you must [create a data access role](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/create-roles) to assign to that user."},{"id":"create-a-user","title":"Create a user","role":"content","text":"To create a user:\n\n1. Go to **Data Access Control** from the [Redis Cloud console](https://cloud.redis.io/#/) menu.\n\n    ![Menu for database access control.](https://redis.io/docs/latest/images/rc/data-access-control-menu.png)\n\n1. Select the **Users** tab.\n\n    ![User configuration area.](https://redis.io/docs/latest/images/rc/data-access-control-users-no-users.png)\n\n2. Select `+` to create a new user.\n\n    ![User add or edit.](https://redis.io/docs/latest/images/rc/data-access-control-users-add-or-edit.png)\n\n3. Enter a username in the **Username** field.\n\n    ![User add username.](https://redis.io/docs/latest/images/rc/data-access-control-users-add.png)\n\n    > [!NOTE]\n    > An error occurs if a user tries to connect to a memcached database with the username `admin`. Do not use `admin` for a username if the user will be connecting to a memcached database.\n    >\n\n1. Select a [**Role**](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/create-roles) from the list.\n\n    ![User select role.](https://redis.io/docs/latest/images/rc/data-access-control-users-add-role.png)\n\n1. Enter and confirm the user's password. ACL user passwords must be between 8 and 128 characters long.\n\n    Then, select the check mark to save the user. \n\n    ![User add password and finish.](https://redis.io/docs/latest/images/rc/data-access-control-users-password-and-finish.png)"},{"id":"assign-roles-to-existing-users","title":"Assign roles to existing users","role":"content","text":"To assign a data access role to an existing user:\n\n1. Go to **Data Access Control** from the [Redis Cloud console](https://cloud.redis.io/#/) menu.\n\n    ![Menu for database access control.](https://redis.io/docs/latest/images/rc/data-access-control-menu.png)\n\n1. Select the **Users** tab.\n\n    ![User configuration area.](https://redis.io/docs/latest/images/rc/data-access-control-users.png)\n\n1. Point to the user and select the **Edit*** icon when it appears.\n\n    ![User add or edit.](https://redis.io/docs/latest/images/rc/data-access-control-users-add-or-edit.png)\n\n1. Select a [**Role**](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/create-roles) from the list.\n\n    ![User select role.](https://redis.io/docs/latest/images/rc/data-access-control-users-add-role.png)\n\n1. Select the check mark to save the user. \n\n    ![User add password and finish.](https://redis.io/docs/latest/images/rc/data-access-control-users-password-and-finish.png)"}],"examples":[]}
{"schema_version":2,"id":"operate/rc/security/access-control/data-access-control/create-roles","title":"Assign permissions to roles","url":"https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/create-roles/","summary":"","tags":["docs","operate","rc"],"last_updated":"2026-10-01T14:41:35+01:00","page_type":"content","content_hash":"b602a9d38d84d71115741a496400f980beb839fd349ad278f2f090f2ba523155","sections":[{"id":"content","title":"Content","role":"content","text":"To assign [Redis ACLs](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/configure-acls) to a data access role:\n\n1. Go to **Data Access Control** from the [Redis Cloud console](https://cloud.redis.io/#/) menu.\n\n    ![Menu for database access control.](https://redis.io/docs/latest/images/rc/data-access-control-menu.png)\n\n1. Select the **Roles** tab.\n\n    ![Role configuration area.](https://redis.io/docs/latest/images/rc/data-access-control-roles.png)\n\n1. Select `+` to create a new role or point to an existing role and select the pencil icon to edit it.\n\n    ![Add or edit a role.](https://redis.io/docs/latest/images/rc/data-access-control-roles-add-or-edit.png)\n\n1. Enter a name for the role.\n\n    ![Role add screen.](https://redis.io/docs/latest/images/rc/data-access-control-roles-add.png)\n\n1. Select an **ACL rule** to assign to the role.\n\n    ![Select an ACL Rule.](https://redis.io/docs/latest/images/rc/data-access-control-roles-select-acl.png)\n\n1. Select one or more databases from the **Databases** list and click the check mark to confirm the association.\n\n    ![Select databases.](https://redis.io/docs/latest/images/rc/data-access-control-roles-select-databases.png)\n\n1. Select **Save role**.\n\nWhen you assign a user-defined ACL rule to a role and associate it with one or more databases, we'll verify that the ACL rule will work with the selected databases. The database may go into an Inactive state for a few seconds while we verify the ACL rule.\n\nAfter you create a role, you can assign it to a user. Users with this role can access the databases according to the role's associated Redis ACLs. For more information, see [Assign roles to users](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/create-assign-users#assign-roles-to-existing-users).\n\nTo assign Redis ACLs to a role for an [Active-Active database](https://redis.io/docs/latest/operate/rc/databases/active-active), see [Active-Active access roles](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/active-active-roles).\n\n> [!NOTE]\n> Applying ACLs to the database is an asynchronous operation. You may notice a delay before an ACL is fully applied to the database."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/security/access-control/data-access-control/default-user","title":"Default user","url":"https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/default-user/","summary":"Learn how to change your default user password or turn off access using the default user password.","tags":["docs","operate","rc"],"last_updated":"2026-10-01T14:41:35+01:00","page_type":"content","content_hash":"56b9d4f87ccec6180d64dd8bd398bc486bf726d0a487524f70f3322e0f99f51b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Password-based authentication is a basic but essential Redis security feature. When you create a Redis Cloud database, your database is given a randomly generated password called the **Default user password**.\n\nThis password appears in the **Security** section of the **Configuration** tab of the database details screen for Pro databases. For Essentials databases, go to **Security > Default user** and select **Configure** to view the default user password.\n\nUse the copy button to copy the password to the clipboard:\n\n![Use the Copy button to copy the default user password.](https://redis.io/docs/latest/images/rc/button-copy.png)\n\nYou'll need to use this password whenever you connect to your database using a Redis client. See [Connect to a database](https://redis.io/docs/latest/operate/rc/databases/connect) for more info.\n\nIf you have [blocked the public endpoint](https://redis.io/docs/latest/operate/rc/security/database-security/block-public-endpoints) for your Redis Cloud Pro subscription, you can also turn on passwordless authentication for the default user. See [Block public endpoints](https://redis.io/docs/latest/operate/rc/security/database-security/block-public-endpoints#turn-on-passwordless-authentication-for-the-default-user) for more info.\n\nSee your [Redis client's documentation](https://redis.io/docs/latest/develop/clients) to learn how to provide your password when connecting."},{"id":"change-password","title":"Change password","role":"content","text":"How you change the default user password depends on your plan.\n\n**Essentials:**\n\n1. In the **Security** section of the **Configuration** tab, select **Configure** for the default user.\n\n1. Enter the new password. Database passwords must be less than 50 characters long.\n\n1. Select **OK** to save your changes.\n\n**Pro:**\n\n1. From the database **Configuration** tab, select **Edit**.\n\n    <img src=\"../../../../../../images/rc/button-database-edit.png\" width=\"100px\" alt=\"The Edit button lets you change the database's default user password.\">\n\n1. Under the **Security** section, enter the new password in the **Default user password** field. Database passwords must be less than 50 characters long.\n\n1. Select **Save database** to update the password.\n\n    <img src=\"../../../../../../images/rc/button-database-save.png\" width=\"150px\" alt=\"Use the Save database button to save the new password.\">"},{"id":"turn-off-default-user","title":"Turn off default user","role":"content","text":"After you set up [role-based access control](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/role-based-access-control) to limit who can access your database, we recommend that you turn off default user access.\n\nHow you turn off the default user depends on your plan.\n\n**Essentials:**\n\n1. In the **Security** section of the **Configuration** tab, select **Configure** for the default user.\n\n1. Turn off the **Default user** switch.\n\n1. Select **OK** to save your changes.\n\n**Pro:**\n\n1. From the database **Configuration** tab, select **Edit**.\n\n    <img src=\"../../../../../../images/rc/button-database-edit.png\" width=\"100px\" alt=\"The Edit database button lets you change the database's default user password.\">\n\n1. Under the **Security** section, select the **Default User** switch to turn it off.\n\n1. Select **Save database**.\n\n    <img src=\"../../../../../../images/rc/button-database-save.png\" width=\"150px\" alt=\"Use the Save database button to save your changes.\">"}],"examples":[]}
{"schema_version":2,"id":"operate/rc/security/access-control/data-access-control","title":"Data access control","url":"https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/","summary":"Control who can access your databases using the default user database password and role-based access control.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T14:59:57+01:00","children":[{"id":"operate/rc/security/access-control/data-access-control/default-user","summary":"Learn how to change your default user password or turn off access using the default user password.","title":"Default user","url":"https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/default-user/"},{"id":"operate/rc/security/access-control/data-access-control/role-based-access-control","summary":"Lets you define multiple users with fine-grained data authorization features.","title":"Enable Role-based access control","url":"https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/role-based-access-control/"},{"id":"operate/rc/security/access-control/data-access-control/configure-acls","summary":"","title":"Configure permissions with Redis ACLs","url":"https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/configure-acls/"},{"id":"operate/rc/security/access-control/data-access-control/create-roles","summary":"","title":"Assign permissions to roles","url":"https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/create-roles/"},{"id":"operate/rc/security/access-control/data-access-control/create-assign-users","summary":"Create a database user and assign it a role.","title":"Create and edit database users","url":"https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/create-assign-users/"},{"id":"operate/rc/security/access-control/data-access-control/active-active-roles","summary":"","title":"Create roles for Active-Active databases","url":"https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/active-active-roles/"}],"page_type":"content","content_hash":"6fecbe1548db4ab270cdab662b66ffab9463353889bd1ac2d2a4296e0af6c526","sections":[{"id":"default-user","title":"Default user","role":"overview","text":"When you create a Redis Cloud database, your database is given a randomly generated password called the [**Default user password**](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/default-user). Learn how to [change the default user password](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/default-user#change-password) or [turn off default user access](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/default-user#turn-off-default-user)."},{"id":"role-based-access-control","title":"Role-based access control","role":"content","text":"With [role-based access control (RBAC)](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/role-based-access-control), you create roles and assign users to those roles to grant different levels of access to the database.\n\n- [Enable RBAC](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/role-based-access-control)\n- [Configure ACLs](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/configure-acls)\n- [Create roles](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/create-roles)\n- [Create and edit database users](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/create-assign-users)\n- [Active-Active roles](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/active-active-roles)"}],"examples":[]}
{"schema_version":2,"id":"operate/rc/security/access-control/data-access-control/role-based-access-control","title":"Enable Role-based access control","url":"https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/role-based-access-control/","summary":"Lets you define multiple users with fine-grained data authorization features.","tags":["docs","operate","rc"],"last_updated":"2026-10-01T14:41:35+01:00","page_type":"content","content_hash":"6d053569c5b2c3ee26720d0d8db8f1e82aabfd9ed5ff777e14e4c0534c75b083","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Role-based access control (RBAC) lets you define *roles* with specific sets of *permissions*. You can then assign *users* to these roles\nto provide appropriate levels of access.\n\nRBAC effectively lets you implement the [principle of least privilege](https://en.wikipedia.org/wiki/Principle_of_least_privilege). For example, you can provide\nread-only access to an application whose only job is to display Redis data. Similarly, you can prevent new developers from running dangerous administrative commands."},{"id":"set-up-rbac","title":"Set up RBAC","role":"content","text":"To set up RBAC, first navigate to the **Data Access Control** screen.\n\nThere are three tabs on this screen: **Users**, **Roles**, and **Redis ACLs**.\n\nIn the **Redis ACLs** tab, you [define named *permissions*](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/configure-acls) for specific Redis commands, keys, and pub/sub channels.\n\n![Data access control screen.](https://redis.io/docs/latest/images/rc/data-access-control-acls.png)\n\nIn the **Roles** tab, you [create roles](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/create-roles). Each role consists of a set of permissions for one or more Redis Cloud databases.\n\n![Data access control screen.](https://redis.io/docs/latest/images/rc/data-access-control-roles.png)\n\nFinally, in the **Users** tab, you [create users](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/create-assign-users) and [assign each user a role](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/create-assign-users#assign-roles-to-existing-users).\n\n![Data access control screen.](https://redis.io/docs/latest/images/rc/data-access-control-users.png)\n\n> [!NOTE]\n> Database access users are different from account access users. To learn more, see [Access management](https://redis.io/docs/latest/operate/rc/security/access-control)."},{"id":"redis-acls-vs-redis-cloud-rbac","title":"Redis ACLs vs. Redis Cloud RBAC","role":"content","text":"In Redis, you can create users and assign ACLs to them using the `ACL` command. However, \nRedis does not support generic roles.\n\nIn Redis Cloud, you configure RBAC using the Redis Cloud console. As a result, certain Redis ACL\nsubcommands are not available in Redis Cloud. The following table shows which ACL commands are supported.\n\n| Command | Supported |\n|:--------|:----------|\n| [ACL CAT](https://redis.io/docs/latest/commands/acl-cat) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> \n| [ACL DELUSER](https://redis.io/docs/latest/commands/acl-deluser) | <span title=\"Not supported\">&#x274c; Not supported</span>| <span title=\"Not supported\">&#x274c; Not supported</span>|  |\n| [ACL DRYRUN](https://redis.io/docs/latest/commands/acl-dryrun) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> \n| [ACL GENPASS](https://redis.io/docs/latest/commands/acl-genpass) | <span title=\"Not supported\">&#x274c; Not supported</span>| <span title=\"Not supported\">&#x274c; Not supported</span>|  |\n| [ACL GETUSER](https://redis.io/docs/latest/commands/acl-getuser) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> \n| [ACL HELP](https://redis.io/docs/latest/commands/acl-help) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> \n| [ACL LIST](https://redis.io/docs/latest/commands/acl-list) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> \n| [ACL LOAD](https://redis.io/docs/latest/commands/acl-load) | <span title=\"Not supported\">&#x274c; Not supported</span>| <span title=\"Not supported\">&#x274c; Not supported</span>|  |\n| [ACL LOG](https://redis.io/docs/latest/commands/acl-log) | <span title=\"Not supported\">&#x274c; Not supported</span>| <span title=\"Not supported\">&#x274c; Not supported</span>|  |\n| [ACL SAVE](https://redis.io/docs/latest/commands/acl-save) | <span title=\"Not supported\">&#x274c; Not supported</span>| <span title=\"Not supported\">&#x274c; Not supported</span>|  |\n| [ACL SETUSER](https://redis.io/docs/latest/commands/acl-setuser) | <span title=\"Not supported\">&#x274c; Not supported</span>| <span title=\"Not supported\">&#x274c; Not supported</span>|  |\n| [ACL USERS](https://redis.io/docs/latest/commands/acl-users) | <span title=\"Supported\">&#x2705; Supported</span> |\n| [ACL WHOAMI](https://redis.io/docs/latest/commands/acl-whoami) | <span title=\"Supported\">&#x2705; Supported</span> |\n\n\nIn Redis, you must explicitly provide access to the `MULTI`, `EXEC`, and `DISCARD` commands.\nIn Redis Cloud, these commands, which are used in transactions, are always permitted. However, the commands\nrun within the transaction block are subject to RBAC permissions.\n\nWhen you run multi-key commands on multi-slot keys, the return value is `failure` but the command runs on the keys that are allowed."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/security/access-control","title":"Access control","url":"https://redis.io/docs/latest/operate/rc/security/access-control/","summary":"Control who can access the Redis Cloud console and databases.","tags":["docs","operate","rc"],"last_updated":"2024-04-03T11:41:33-04:00","children":[{"id":"operate/rc/security/access-control/access-management","summary":"Control who can make changes to your databases using the Redis Cloud console.","title":"Access management","url":"https://redis.io/docs/latest/operate/rc/security/access-control/access-management/"},{"id":"operate/rc/security/access-control/social-login","summary":"Social login with Redis Cloud.","title":"Social login","url":"https://redis.io/docs/latest/operate/rc/security/access-control/social-login/"},{"id":"operate/rc/security/access-control/multi-factor-authentication","summary":"Redis Cloud supports multi-factor authentication to reduce the risk of unauthorized Redis Cloud console access.","title":"Multi-factor authentication","url":"https://redis.io/docs/latest/operate/rc/security/access-control/multi-factor-authentication/"},{"id":"operate/rc/security/access-control/saml-sso","summary":"Redis Cloud supports both IdP-initiated and SP-initiated single sign-on (SSO) with SAML (Security Assertion Markup Language). You can use any identity provider to integrate with Redis Cloud as long as it supports the SAML protocol, or you can refer to integration guides for a few specific providers.","title":"SAML single sign-on","url":"https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso/"},{"id":"operate/rc/security/access-control/data-access-control","summary":"Control who can access your databases using the default user database password and role-based access control.","title":"Data access control","url":"https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/"}],"page_type":"content","content_hash":"9fa6fcbd2d2a76ff333c003cb74abaf2587767f013086f853b89e8f619c17f20","sections":[{"id":"content","title":"Content","role":"content","text":"Redis Cloud offers you a few different ways to control who can access and make changes to your databases."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/security/access-control/multi-factor-authentication","title":"Multi-factor authentication","url":"https://redis.io/docs/latest/operate/rc/security/access-control/multi-factor-authentication/","summary":"Redis Cloud supports multi-factor authentication to reduce the risk of unauthorized Redis Cloud console access.","tags":["docs","operate","rc"],"last_updated":"2026-10-01T14:41:35+01:00","page_type":"content","content_hash":"687b5e340b5fd8765711de4195f98d3835c2fa83375f5bb2986e002cff96ffca","sections":[{"id":"overview","title":"Overview","role":"overview","text":"To reduce the chances of unauthorized access, Redis Cloud allows users to enable multi-factor authentication (MFA).\n\nWhen MFA is enabled, users must enter their username, password, and an authentication code when signing in. MFA requires a mobile device that can receive these authentication codes over text messaging. In addition, you may [use an authenticator app](#app) such as [Google Authenticator](https://en.wikipedia.org/wiki/Google_Authenticator) as one of your factors.\n\nTo further increase the security of the account, the account owner can [require MFA enforcement for all users](#enforcing).\n\n> [!NOTE]\n> Once you enable MFA, it will be required to access every account that belongs to you. Deactivating MFA enforcement on an account does not deactivate MFA enforcement for other users of that account that have defined a phone number. To deactivate MFA enforcement for other users, those users will each need to deactivate MFA enforcement for their own user accounts."},{"id":"enable-mfa","title":"Enable MFA","role":"content","text":"Each user can enable and configure MFA for their account.\nThe default MFA configuration sends an authentication code by text message that users must enter when they sign in.\n\nTo configure MFA for your user account:\n\n1. Sign in to your account.\n2. From the Redis Cloud console menu, select your name and then **User Profile** from the drop down menu.\n\n    ![Use the user drop down menu to get to the User Profile section.](https://redis.io/docs/latest/images/rc/multi-factor-authentication-user-dropdown.png)\n\n3. Under your user profile, locate **Multi-factor authentication**.\n\n    ![Multi-factor authentication is located on the user profile page.](https://redis.io/docs/latest/images/rc/multi-factor-authentication-user-profile.png)\n\n4. Enter your mobile phone number in the **Text message** box and then select **Send code**.\n5. You will receive a confirmation code sent by text message. Enter the code when prompted by the Redis Cloud console and select **Verify**.\n\nYour account is now configured for MFA.\n\n> [!NOTE]\n> We recommend that you also [configure MFA for an authenticator app](#app) as an additional factor.\n\nIf you cannot sign in to your account because of MFA, please [contact support](https://redis.io/support/).\n\nIf your mobile phone is lost or stolen, make sure that you update the MFA configuration to prevent unauthorized sign-ins."},{"id":"change-your-mfa-phone-number","title":"Change your MFA phone number","role":"content","text":"To change the mobile phone number used for MFA:\n\n1. Navigate to the Multi-Factor authentication section in the **User Profile**.\n2. Select **Change number**.\n3. Enter the new mobile phone number, and complete the verification process as described above."},{"id":"app","title":"Configure MFA for an authenticator app","role":"configuration","text":"After you configure MFA for text messages, you can also configure MFA to work with a time-based one-time password (TOTP) app such as [Google Authenticator](https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=en_US&gl=US&pli=1).\n\nWhen you sign in to the Redis Cloud console, you can select  either an authentication code sent by text message or an authentication code shown in your authenticator app.\n\nTo configure MFA for an authenticator app:\n\n1. Install an authenticator app on your mobile phone.\n1. Add Redis Cloud to the app:\n    1. From the **User Profile** view in your Redis Cloud account, locate **Multi-Factor Authentication**.\n    1. Select **Connect** for the authenticator app. A QR code appears, requesting verification.\n    1. Scan the QR code using your phone's authenticator app.\n    1. Enter the code generated by your authenticator app to verify the setup.\n\nYou can now use either a text message code or an authenticator app code as your second factor when signing in.\n\n#### Deactivate MFA\n\nYou can deactivate MFA for your user account. To deactivate MFA, go to your profile, locate **Multi-Factor Authentication**, and select **Deactivate**."},{"id":"enforcing","title":"Enforce MFA for all user accounts","role":"content","text":"Account owner users can enable MFA enforcement for all users in their account.\nAfter MFA is enforced for the account, all users who do not have MFA enabled will be required to configure MFA the next time they sign in to the Redis Cloud console.\n\n> [!NOTE]\n> Redis Cloud does not enforce MFA for [SAML SSO](https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso) users since the identity provider handles MFA management and enforcement.\n\nTo enable MFA enforcement for all user accounts:\n\n1. Sign in as an account owner.\n2. Go to **Account Settings**.\n3. Under **Security**, switch the **MFA environment** toggle to *On*.\n\n    ![Multi-factor authentication toggle for all users on.](https://redis.io/docs/latest/images/rc/multi-factor-authentication-force-all-users-on.png)\n\n- When you enable MFA enforcement, users cannot disable MFA for their account.\n- When you disable MFA enforcement, users can disable MFA for their account.\n\n4. Select **Save changes** to save your changes.\n\n> [!TIP]\n> Notify all of your Redis Cloud console users before enabling MFA enforcement."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/security/access-control/saml-sso","title":"SAML single sign-on","url":"https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso/","summary":"Redis Cloud supports both IdP-initiated and SP-initiated single sign-on (SSO) with SAML (Security Assertion Markup Language). You can use any identity provider to integrate with Redis Cloud as long as it supports the SAML protocol, or you can refer to integration guides for a few specific providers.","tags":["docs","operate","rc"],"last_updated":"2026-10-02T16:06:07+01:00","children":[{"id":"operate/rc/security/access-control/saml-sso/saml-integration-auth0","summary":"This integration guide shows how to configure Auth0 as a SAML single sign on provider for your Redis Cloud account.","title":"Auth0 SAML integration guide","url":"https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso/saml-integration-auth0/"},{"id":"operate/rc/security/access-control/saml-sso/saml-integration-aws-identity-center","summary":"This integration guide shows how to configure AWS IAM Identity Center as a SAML single sign on provider for your Redis Cloud account.","title":"AWS IAM Identity Center SAML integration guide","url":"https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso/saml-integration-aws-identity-center/"},{"id":"operate/rc/security/access-control/saml-sso/saml-integration-google","summary":"This integration guide shows how to configure Google Workspace as a SAML single sign on provider for your Redis Cloud account.","title":"Google Workspace SAML integration guide","url":"https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso/saml-integration-google/"},{"id":"operate/rc/security/access-control/saml-sso/saml-integration-azure-ad","summary":"This integration guide shows how to set up Microsoft Entra as a SAML single sign on provider for your Redis Cloud account.","title":"Microsoft Entra (formerly Azure Active Directory) SAML integration guide","url":"https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso/saml-integration-azure-ad/"},{"id":"operate/rc/security/access-control/saml-sso/saml-integration-okta-generic","summary":"This integration guide shows how to set up Okta as a SAML single sign-on provider for your Redis Cloud account.","title":"Okta SAML integration guide (Generic)","url":"https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso/saml-integration-okta-generic/"},{"id":"operate/rc/security/access-control/saml-sso/saml-integration-okta-org2org","summary":"This integration guide shows how to set up Okta as a SAML single sign on provider for your Redis Cloud account.","title":"Okta SAML integration guide (Org2Org)","url":"https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso/saml-integration-okta-org2org/"},{"id":"operate/rc/security/access-control/saml-sso/saml-integration-ping-identity","summary":"This integration guide shows how to set up PingIndentity as a SAML single sign-on provider for your Redis Cloud account.","title":"PingIdentity SAML integration guide","url":"https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso/saml-integration-ping-identity/"}],"page_type":"content","content_hash":"48f092158404e64b92fbb94cec4a11877eef56ef3b188f5b7b8735e8eadea516","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Cloud supports both [IdP-initiated](#idp-initiated-sso) and [SP-initiated](#sp-initiated-sso) [single sign-on (SSO)](https://en.wikipedia.org/wiki/Single_sign-on) with [SAML (Security Assertion Markup Language)](https://en.wikipedia.org/wiki/Security_Assertion_Markup_Language).\n\nYou cannot use [SCIM (System for Cross-domain Identity Management)](https://en.wikipedia.org/wiki/System_for_Cross-domain_Identity_Management) to provision Redis Cloud users. However, Redis Cloud supports just-in-time (JIT) user provisioning, which means Redis Cloud automatically creates a user account the first time a new user signs in with SAML SSO."},{"id":"saml-sso-overview","title":"SAML SSO overview","role":"content","text":"When SAML SSO is enabled, the [identity provider (IdP)](https://en.wikipedia.org/wiki/Identity_provider) admin handles SAML user management instead of the Redis Cloud account owner.\n\nYou can use any identity provider to integrate with Redis Cloud as long as it supports the SAML protocol. You can also refer to these integration guides for several popular identity providers:\n\n  - [Auth0 SAML integration](https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso/saml-integration-auth0)\n  - [AWS IAM Identity Center SAML integration](https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso/saml-integration-aws-identity-center)\n  - [Azure Active Directory SAML integration](https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso/saml-integration-azure-ad)\n  - [Google Workspace integration](https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso/saml-integration-google)\n  - [Okta SAML integration (Generic)](https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso/saml-integration-okta-generic)\n  - [Okta SAML integration (Org2Org)](https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso/saml-integration-okta-org2org)\n  - [PingIdentity SAML integration](https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso/saml-integration-ping-identity)\n\nAfter you activate SAML SSO for a Redis Cloud account, all existing local users for the account, except for the user that set up SAML SSO, are converted to SAML users and are required to use SAML SSO to sign in. Before they can sign in to Redis Cloud, the identity provider admin needs to set up these users on the IdP side and configure the `redisAccountMapping` attribute to map them to the appropriate Redis Cloud accounts and [roles](https://redis.io/docs/latest/operate/rc/security/access-control/access-management#team-management-roles)."},{"id":"idp-initiated-sso","title":"IdP-initiated SSO","role":"content","text":"With IdP-initiated single sign-on, you can select the Redis Cloud application after you sign in to your [identity provider (IdP)](https://en.wikipedia.org/wiki/Identity_provider). This redirects you to the [Redis Cloud console](https://cloud.redis.io/#/login) and signs you in to your SAML user account."},{"id":"sp-initiated-sso","title":"SP-initiated SSO","role":"content","text":"You can also initiate single sign-on from the [Redis Cloud console](https://cloud.redis.io/#/login). This process is known as [service provider (SP)](https://en.wikipedia.org/wiki/Service_provider)-initiated single sign-on.\n\n1. From the Redis Cloud console's [sign-in screen](https://cloud.redis.io/#/login), select **SSO**.\n\n    ![Sign in with SSO button](https://redis.io/docs/latest/images/rc/button-sign-in-sso.png)\n\n1. Enter the email address associated with your SAML user account.\n\n1. Select the **Login** button.\n\n    - If you already have an active SSO session with your identity provider, this signs you in to your SAML user account.\n\n    - Otherwise, the SSO flow redirects you to your identity provider's sign in screen. Enter your IdP user credentials to sign in. This redirects you back to the Redis Cloud console and automatically signs in to your SAML user account."},{"id":"multi-factor-authentication","title":"Multi-factor authentication","role":"content","text":"The account owner remains a local user and should set up [multi-factor authentication (MFA)](https://redis.io/docs/latest/operate/rc/security/access-control/multi-factor-authentication) to help secure their account. After SAML activation, the account owner can set up additional local bypass users with MFA enabled.\n\nIf MFA enforcement is enabled, note that Redis Cloud does not enforce MFA for SAML users since the identity provider handles MFA management and enforcement."},{"id":"set-up-saml-sso","title":"Set up SAML SSO","role":"content","text":"To set up SAML single sign-on for a Redis Cloud account:\n\n1. [Verify domain ownership in Redis Cloud](#verify-domain).\n\n1. [Set up a SAML app](#set-up-app) to integrate Redis Cloud with your identity provider.\n\n1. [Configure SAML identity provider in Redis Cloud](#configure-idp).\n\n1. [Download service provider metadata](#download-sp) and upload it to your identity provider.\n\n1. [Activate SAML SSO](#activate-saml-sso)."},{"id":"verify-domain","title":"Verify domain ownership in Redis Cloud","role":"content","text":"Before you set up SAML SSO in Redis Cloud, you must verify that you own the domain(s) associated with your SAML setup.\n\n1. Sign in to [Redis Cloud](https://cloud.redis.io/#/login) with the email address associated with the SAML user you set up with your identity provider.\n\n1. Select **Access Management** from the [Redis Cloud console](https://cloud.redis.io) menu.\n\n1. Select **Single Sign-On**.\n\n1. Select the **Setup SAML SSO** button:\n\n    ![Setup SSO button](https://redis.io/docs/latest/images/rc/button-access-management-sso-setup.png)\n\n1. From the **SAML** screen of the [Redis Cloud console](https://cloud.redis.io), you must verify you own the domains associated with your SAML configuration. Select **Add domain** to open the **Manage domain bindings** panel.\n\n    ![Add domain button](https://redis.io/docs/latest/images/rc/saml-button-add-domain.png)\n\n    ![The Manage domain bindings panel](https://redis.io/docs/latest/images/rc/saml-manage-domain-bindings.png)\n\n1. Select **Copy** to copy the provided TXT DNS record. For each domain you want to associate with your SAML setup, add the copied TXT record to its DNS records. \n\n1. Select **Add domain** to add a domain. \n\n    ![Add domain button](https://redis.io/docs/latest/images/rc/saml-button-add-domain.png)\n\n1. Enter the domain name and select ![Confirm domain](https://redis.io/docs/latest/images/rc/saml-button-confirm.png#no-click) to save it, or select ![Cancel](https://redis.io/docs/latest/images/rc/saml-button-cancel.png#no-click) to cancel.\n\n    ![Enter domain name in the Domain field.](https://redis.io/docs/latest/images/rc/saml-enter-domain.png)\n\n1. After you save the domain name, its status is **Pending**. Select **Verify** to verify it. \n\n    ![The Manage domain bindings panel, with a pending domain](https://redis.io/docs/latest/images/rc/saml-domain-pending.png)\n\n    We'll check the domain's DNS records for the provided TXT record. If the TXT record does not exist or we can't resolve your domain, we won't be able to verify the domain and users with that domain won't be able to sign in using SAML SSO. \n    \n    Select ![delete domain](https://redis.io/docs/latest/images/rc/icon-delete-teal.png#no-click) to delete a domain if it was added by mistake.\n\n    If we find the TXT record, the domain's status will change to **Verified**.\n\n    You can select **Add domain** to add another domain.\n\n1. Select **Close** to close the domain binding panel.\n\n    ![Close button](https://redis.io/docs/latest/images/rc/saml-button-close.png)\n\nAfter you verify at least one domain, you can select **Manage domains** to open the **Manage domain bindings** panel again and add or verify more domains."},{"id":"set-up-app","title":"Set up SAML app","role":"content","text":"Set up a SAML app to integrate Redis Cloud with your identity provider:\n\n1. Sign in to your identity provider's admin console.\n\n1. Create or add a SAML integration app for the service provider Redis Cloud.\n\n1. Set up your SAML service provider app so the SAML assertion contains the following attributes:\n\n    | Attribute&nbsp;name<br />(case-sensitive) | Description |\n    |-------------------------------------------|-------------|\n    | FirstName | User's first name |\n    | LastName | User's last name |\n    | Email | User's email address (used as the username in the Redis Cloud console) |\n    | redisAccountMapping | Key-value pair of a lowercase [role name](https://redis.io/docs/latest/operate/rc/security/access-control/access-management#team-management-roles) (owner, member, manager, billing_admin, or viewer) and the user's Redis Cloud **Account number** found in the [account settings](https://redis.io/docs/latest/operate/rc/accounts/account-settings) |\n\n    For `redisAccountMapping`, you can add the same user to multiple SAML-enabled accounts using one of these options:\n\n    - A single string that contains a comma-separated list of account/role pairs\n\n        [code example]\n\n    - Multiple strings, where each represents a single account/role pair\n\n        [code example]\n\n    > [!NOTE]\n    > To confirm the identity provider's SAML assertions contain the required attributes, you can use a SAML-tracer web developer tool to inspect them.\n    >\n\n1. Set up any additional configuration required by your identity provider to ensure you can configure the `redisAccountMapping` attribute for SAML users.\n\n    If your identity provider lets you configure custom attributes with workflows or group rules, you can set up automation to configure the `redisAccountMapping` field automatically instead of manually."},{"id":"configure-idp","title":"Configure SAML in Redis Cloud","role":"configuration","text":"After you set up the SAML integration app and create a SAML user in your identity provider, you need to configure your Redis Cloud account to set up SAML SSO.\n\n1. Sign in to [Redis Cloud](https://cloud.redis.io/#/login) with the email address associated with the SAML user you set up with your identity provider.\n\n1. Select **Access Management** from the [Redis Cloud console](https://cloud.redis.io) menu.\n\n1. Select **Single Sign-On**.\n\n1. [Verify at least one domain](#verify-domain) if you haven't.\n\n1. Configure the **Identity Provider metadata** settings. \n\n    ![SAML Single Sign-On configuration screen.](https://redis.io/docs/latest/images/rc/access-management-saml-config.png)\n\n    To do so, you need the following metadata values from your identity provider:\n\n    | Setting | Description |\n    |---------|-------------|\n    | **Issuer (IdP entity ID)** | The unique entity ID for the identity provider |\n    | **IdP server URL** | The identity provider's HTTPS URL for SAML SSO |\n    | **Single logout URL** | The URL used to sign out of the identity provider and connected apps (optional) |\n    | **Assertion signing certificate** | Public SHA-256 certificate used to validate SAML assertions from the identity provider |\n\n    To find these metadata values, see your identity provider's documentation.\n\n1. Select **Enable**.\n\n    ![Enable button](https://redis.io/docs/latest/images/rc/saml-enable-button.png)\n\n1. From the **SAML activation** dialog box, select **Continue**."},{"id":"download-sp","title":"Download service provider metadata","role":"content","text":"Next, you need to download the service provider metadata for Redis Cloud and use it to finish configuring the SAML integration app for your identity provider:\n\n1. Select the **Download** button to download the service provider [metadata](https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf) in XML format.\n\n1. Sign in to your identity provider's admin console.\n\n1. Configure the Redis Cloud service provider app with the downloaded XML.\n\n    - Some identity providers let you upload the XML file directly. \n    \n    - Others require you to manually configure the service provider app with specific metadata fields, such as:\n    \n        | XML attribute | Value | Description |\n        |---------------|-------|-------------|\n        | EntityDescriptor's **entityID** | https://<nobr>auth.redis.com</nobr>/saml2/<nobr>service-provider</nobr>/\\<ID\\> | Unique URL that identifies the Redis Cloud service provider |\n        | AssertionConsumerService's **Location** | <nobr> https://<nobr>auth.redis.com</nobr>/sso/saml2/\\<ID\\> | The service provider endpoint where the identity provider sends a SAML assertion that authenticates a user  |\n\n    - To use [IdP-initiated SSO](#idp-initiated-sso) with certain identity providers, you also need to set the RelayState parameter to the following URL:\n    \n        [code example]\n\n       > Replace `<ID>` so it matches the `AssertionConsumerService Location` URL's ID.\n        \n    To learn more about how to configure service provider apps, see your identity provider's documentation."},{"id":"activate-saml-sso","title":"Activate SAML SSO","role":"content","text":"After you finish the required SAML SSO configuration between your identity provider and Redis Cloud account, you can test and activate SAML SSO.\n\nAll users associated with the account, excluding the local user you used to set up SAML SSO, are converted to SAML users on successful activation. They can no longer sign in with their previous sign-in method and must use SAML SSO instead. However, you can add local bypass users after SAML SSO activation to allow access to the account in case of identity provider downtime or other issues with SAML SSO.\n\nTo activate SAML SSO:\n\n1. Sign out of any active SSO sessions with your identity provider.\n\n1. For **Activate SAML integration**, select the **Activate** button.\n\n1. From the **Logout notification** dialog, select **Continue**. This redirects you to your configured identity provider's sign-in screen.\n\n1. Sign in with your identity provider.\n\n1. When redirected to the Redis Cloud sign-in screen, you can either:\n\n    - Sign in with your local credentials as usual.\n\n    - Select **SSO** and enter the email address associated with the SAML user configured in your identity provider. Your user converts to a SAML user in Redis Cloud. Don't use this method if you want your user account to remain a local bypass user.\n\n    > [!NOTE]\n    > If you see a **SAML activation failed** notification when redirected to the Redis Cloud sign-in screen, sign in with your local user credentials and review the SAML configuration for issues.\n    >\n\nAfter you activate SAML SSO, [add a few local bypass users](https://redis.io/docs/latest/operate/rc/security/access-control/access-management#manage-team-access) from the **Team** tab. Local bypass users should [set up MFA](https://redis.io/docs/latest/operate/rc/security/access-control/multi-factor-authentication) for additional security."},{"id":"update-config","title":"Update configuration","role":"content","text":"If you change certain metadata or configuration settings after you set up SAML SSO, such as the assertion signing certificate, remember to do the following:\n\n1. [Update the SAML SSO configuration](#configure-idp) with the new values.\n\n1. [Download the updated service provider metadata](#download-sp) and use it to update the Redis Cloud service provider app."},{"id":"link-other-accounts","title":"Link other accounts","role":"content","text":"After you set up SAML SSO for one account, you can link other accounts you own to the existing SAML configuration. This lets you use the same SAML configuration for SSO across multiple accounts.\n\n> [!NOTE]\n> You must have the `Owner` role assigned to your user account for all accounts you want to link. The same user must link each account to the original configuration.\n\nTo link other accounts to an existing SAML SSO configuration:\n\n1. Sign in to the [Redis Cloud console](https://cloud.redis.io/) with the account that has an existing SAML configuration.\n\n1. Go to **Access Management > Single Sign-On**. \n\n1. Select **Get token**.\n\n    ![Get Token popup](https://redis.io/docs/latest/images/rc/saml/popup-saml-get-token.png)\n\n    Select **Copy** to copy the linking token.\n\n1. Sign in to the account that you want to link to the SAML configuration. Go to **Access Management > Single Sign-On** and then enter the copied token into the **Join an existing SAML configuration** text box. Select the arrow to confirm.\n\n    After you do this, the owner of the original account will receive a request to link the new account to the SAML configuration.\n\n1. Sign in with the original account and select **Access Management > Single Sign-On**. You should see the new account in the **Unlinked accounts** list.\n\n1. Select **Link account**.\n\n    ![The Link Account button](https://redis.io/docs/latest/images/rc/saml/button-saml-link-account.png)\n\n1. In the **Convert existing users** dialog, select **Confirm conversion** to finish linking the accounts."},{"id":"deactivate-saml-sso","title":"Deactivate SAML SSO","role":"content","text":"Before you can deactivate SAML SSO for an account, you must sign in to the account as a local (non-SAML) user with the owner role assigned.\n\nDeactivating SAML SSO for an account also removes any existing SAML-type users associated with the account.\n\nTo deactivate SAML SSO for a specific account:\n\n1. In the [Redis Cloud console](https://cloud.redis.io), select your name to display your available accounts.\n\n1. If the relevant account is not already selected, select it from the **Switch account** list.\n\n1. Go to **Access Management > Single Sign-On**.\n\n1. Select **Deactivate SAML**. This only deactivates SAML SSO for the current account. Other linked accounts continue to use this SAML SSO configuration.\n\n1. Select **Deactivate** to confirm deactivation."},{"id":"deprovision-saml-users","title":"Deprovision SAML users","role":"content","text":"When a user is removed from your identity provider, their access to Redis Cloud should also be removed.  \n\nWhen you have revoked a user’s access to Redis Cloud, they cannot access the Redis Cloud console, but their API keys remain active. You can [delete an API key](https://redis.io/docs/latest/operate/rc/api/get-started/manage-api-keys#delete-a-user-key) to remove access.\n\nTo deprovision SAML users upon deletion, the identity provider admin can set up a webhook to automatically make the appropriate Cloud API requests. For more information about managing users with API requests, see [Users](https://redis.io/docs/latest/operate/rc/api/api-reference#tag/Users) in the Redis Cloud API documentation."}],"examples":[{"id":"set-up-app-ex0","language":"xml","code":"<saml2:Attribute Name=\"redisAccountMapping\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified\">\n            <saml2:AttributeValue xsi:type=\"xs:string\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n                12345=owner,54321=manager\n            </saml2:AttributeValue>\n        </saml2:Attribute>","section_id":"set-up-app"},{"id":"set-up-app-ex1","language":"xml","code":"<saml2:Attribute Name=\"redisAccountMapping\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified\">\n            <saml2:AttributeValue xsi:type=\"xs:string\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n                12345=owner\n            </saml2:AttributeValue>\n            <saml2:AttributeValue xsi:type=\"xs:string\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n                54321=manager\n            </saml2:AttributeValue>\n        </saml2:Attribute>","section_id":"set-up-app"},{"id":"download-sp-ex0","language":"sh","code":"https://cloud.redis.io/#/login/?idpId=<ID>","section_id":"download-sp"}]}
{"schema_version":2,"id":"operate/rc/security/access-control/saml-sso/saml-integration-auth0","title":"Auth0 SAML integration guide","url":"https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso/saml-integration-auth0/","summary":"This integration guide shows how to configure Auth0 as a SAML single sign on provider for your Redis Cloud account.","tags":["docs","operate","rc"],"last_updated":"2026-10-01T14:41:30+01:00","page_type":"content","content_hash":"a04bad58e239f27a3b59df4e3efda3d09b7557b2f626b0ce1bff76d5d8912167","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This guide shows how to configure [Auth0](https://auth0.com/docs) as a SAML single sign-on identity provider (IdP) for your Redis Cloud account.\n\nTo learn more about Redis Cloud support for SAML, see [SAML single sign-on](https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso).\n\nBefore completing this guide, you must [verify ownership of any domains](https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso#verify-domain) you want to associate with your SAML setup."},{"id":"step-1-set-up-your-identity-provider-idp","title":"Step 1: Set up your identity provider (IdP)","role":"content","text":""},{"id":"specify-the-saml-owner","title":"Specify the SAML owner","role":"content","text":"1. Sign in to your Auth0 account and navigate to **User Management > Users**.\n   * Select the SAML owner.\n   * Verify the details.\n\n    SAML assertion requires first and last name, which are not available in the default user profile. \n\n    > [!NOTE]\n    > Depending how they are created, users can have different profiles. \n    >\n\n    ![](https://redis.io/docs/latest/images/rc/saml/auth0_saml_1.png)\n\n1. Add `user_metadata` to fulfill the SAML assertion, then select **Save**. \n   \n   ![](https://redis.io/docs/latest/images/rc/saml/auth0_saml_2.png)\n\n   The key-value pair of `redisAccountMapping` consists of a lowercase role name (owner, member, manager, billing_admin, or viewer) and your Redis Cloud Account ID found in the [account settings](https://redis.io/docs/latest/operate/rc/accounts/account-settings).\n\n    [code example]\n\n1. Open **Actions > Triggers** and select **`post-login`**.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/auth0_saml_3.png)\n\n1. Select **Create Action** to create a new custom action.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/auth0_saml_add-action.png)\n\n1. Provide a **name** for the action and select **Create**.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/auth0_saml_create-action.png)\n\n1. Add the following code to the action:\n\n    [code example]\n\n1. Select **Deploy** to save and deploy the action.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/auth0_saml_4.png)\n\n1. Return to the **`post-login`** trigger and drag the action you just created to the trigger.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/auth0_saml_action-trigger.png)\n\n    Select **Apply** to save your changes."},{"id":"create-and-configure-the-saml-application","title":"Create and configure the SAML application","role":"content","text":"1. Open **Applications > Applications** and select **Create Application**.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/auth0_saml_5.png)\n\n1. Provide a **name** for the Application and select **Single Page Web Applications**. Select **Create**.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/auth0_saml_6.png)\n\n1. From the newly created application, go to **Settings > Advanced Settings > Certificates**.\n\n    * Copy and save the **Signing Certificate**. You will need this information to configure SAML in admin console.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/auth0_saml_7.png)\n\n    * We suggest that you update the default logo of the application to the Redis icon for better visibility. \n\n1. From the newly created application, go to **Addons** and enable **SAML 2 WEB APP**.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/auth0_saml_8.png)\n\n1. From the **Usage** tab:\n\n    * Copy and save the **Issuer** value.\n    * Copy and save the **Identity Provider Login URL**.\n    \n    You will need both of these values, along with the certificate value you copied in the previous step, to configure SAML in the Redis Cloud console.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/auth0_saml_9.png)"},{"id":"step-2-configure-saml-support-in-redis-cloud","title":"Step 2: Configure SAML support in Redis Cloud","role":"content","text":"Now that you have you Auth0 IdP server ready, configure support for SAML in Redis Cloud."},{"id":"sign-in-to-redis-cloud","title":"Sign in to Redis Cloud","role":"content","text":"Sign in to your account on the [Redis Cloud console](https://cloud.redis.io/#/login)."},{"id":"activate-saml-in-access-management","title":"Activate SAML in Access Management","role":"content","text":"To activate SAML, you need to have a local user (or social sign-on user) with the **owner** role. If you have the correct permissions, you will see the **Single Sign-On** tab.\n\n1. Fill in the information you saved previously in the **setup** form. This includes:\n\n    * **Issuer (IdP Entity ID)**: Issuer value from Auth0\n    * **IdP server URL**: Identity Provider Login URL from Auth0\n    * **Assertion signing certificate**: Certificate value from Auth0\n\n    ![](https://redis.io/docs/latest/images/rc/saml/sm_saml_1.png)\n\n    Once you click **Enable**, wait a few seconds for the status to change.\n\n1. Download the service provider (SP) metadata. Save the file to your local hard disk.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/sm_saml_3.png)\n\n1. Open the file in any text editor. Save the following text from the metadata:\n\n    * **EntityID**: The unique name of the service provider (SP)\n\n    ![](https://redis.io/docs/latest/images/rc/saml/sm_saml_4.png)\n\n    * **Location** : The location of the assertion consumer service\n\n    ![](https://redis.io/docs/latest/images/rc/saml/sm_saml_5.png)"},{"id":"step-3-finish-saml-configuration-in-auth0","title":"Step 3: Finish SAML configuration in Auth0","role":"content","text":"1. Return to the Auth0 SAML application and select **Addons > SAML 2 Web App > Settings**:\n\n    ![](https://redis.io/docs/latest/images/rc/saml/auth0_saml_10.png)\n\n    * Paste the **Location** link in **Application Callback URL** field.\n\n    * Enter the following code in the **Settings** code area. Modify the `audience` variable with the `EntityID` value, and the `recipient` variable with the `Location` value from the metadata file you downloaded. \n\n    [code example]\n    Scroll down and select **Enable** to apply the configuration."},{"id":"idp-initiated-sso","title":"IdP initiated SSO","role":"content","text":"To use IdP-initiated SSO with certain identity providers, you also need to set the `RelayState` parameter to this URL: \n\n`https://cloud.redis.io/#/login/?idpId=<ID>`\n\n> [!NOTE]\n> Replace `ID` so it matches the `AssertionConsumerService` Location URL ID (the content after the last forward slash \"/\"). To learn more about how to configure service provider apps, see your identity provider’s documentation."},{"id":"step-4-return-to-the-redis-cloud-console","title":"Step 4: Return to the Redis Cloud console","role":"content","text":"1. Return to the Redis Cloud console and select **Activate**.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/sm_saml_8.png)\n\n   A popup appears, explaining that, in order to test the SAML connection, we need to login with credentials of a user defined in Auth0. Select **Continue** to go to the Auth0 login screen.\n\n1. The Auth0 login screen appears. Enter the credentials and select **Sign In**.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/auth0_saml_12.png)\n\nIf everything is configured correctly, you will see the the Redis Cloud console screen. Your local account is now considered a SAML account. \n\nTo log in to the Redis Cloud console from now on, click on **Sign in with SSO**.\n\n![Sign in with SSO button](https://redis.io/docs/latest/images/rc/button-sign-in-sso.png)"}],"examples":[{"id":"specify-the-saml-owner-ex0","language":"json","code":"{\n       \"FirstName\": \"Test\",\n       \"LastName\": \"User\",\n       \"redisAccountMapping\": \"YOUR_REDIS_CLOUD_ID=owner\"\n    }","section_id":"specify-the-saml-owner"},{"id":"specify-the-saml-owner-ex1","language":"js","code":"exports.onExecutePostLogin = async (event, api) => {\n        const um = event.user.user_metadata || {};\n        const am = event.user.app_metadata || {};\n        api.samlResponse.setAttribute('email', event.user.email);\n        api.samlResponse.setAttribute(\n            'firstName',\n            um.FirstName || ''\n        );\n        api.samlResponse.setAttribute(\n            'lastName',\n            um.LastName || ''\n        );\n        const mapping = am.redisAccountMapping || um.redisAccountMapping;\n        if (mapping) {\n            api.samlResponse.setAttribute('redisAccountMapping', mapping);\n        } else {\n            api.access.deny('missing_redis_account_mapping', 'redisAccountMapping not set for user');\n        }\n    };","section_id":"specify-the-saml-owner"},{"id":"step-3-finish-saml-configuration-in-auth0-ex0","language":"json","code":"{\n      \"audience\": \"<EntityID>\",\n      \"recipient\": \"<Location>\",\n      \"passthroughClaimsWithNoMapping\": false,\n      \"nameIdentifierProbes\": [\n        \"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress\"\n      ]\n    }","section_id":"step-3-finish-saml-configuration-in-auth0"}]}
{"schema_version":2,"id":"operate/rc/security/access-control/saml-sso/saml-integration-aws-identity-center","title":"AWS IAM Identity Center SAML integration guide","url":"https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso/saml-integration-aws-identity-center/","summary":"This integration guide shows how to configure AWS IAM Identity Center as a SAML single sign on provider for your Redis Cloud account.","tags":["docs","operate","rc"],"last_updated":"2026-10-01T14:41:30+01:00","page_type":"content","content_hash":"45feacbf008cda99d15a97ecf1e70cbb9026b869e335860169d91435aa380816","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This guide shows how to configure [AWS IAM Identity Center](https://aws.amazon.com/iam/identity-center/) as a SAML single sign-on identity provider (IdP) for your Redis Cloud account.\n\nTo learn more about Redis Cloud support for SAML, see [SAML single sign-on](https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso).\n\nBefore completing this guide, you must [verify ownership of any domains](https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso#verify-domain) you want to associate with your SAML setup."},{"id":"step-1-setup-your-identity-provider-idp","title":"Step 1: Setup your identity provider (IdP)","role":"content","text":""},{"id":"create-the-aws-iam-identity-center-saml-application","title":"Create the AWS IAM Identity Center SAML application","role":"content","text":"1. Sign in to your AWS account.\n\n1. From the main menu, search for **IAM Identity Center (successor to AWS Single Sign-On)**.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/aws_iam_identity_center_saml_1.png)\n\n1. Once in IAM Identity Center, select **Applications**.\n\n   ![](https://redis.io/docs/latest/images/rc/saml/aws_iam_identity_center_saml_2.png)\n\n1. Next, select **Add application**.\n\n   ![](https://redis.io/docs/latest/images/rc/saml/aws_iam_identity_center_saml_3.png)\n\n1. In the next screen, select **Add custom SAML 2.0 application** then **Next**.\n\n   ![](https://redis.io/docs/latest/images/rc/saml/aws_iam_identity_center_saml_4.png)\n\n1. The **Configure Application** screen is where you initially get the information needed to configure SAML in Redis Cloud. To begin, change the **Display name** and **Description** to **Redis Cloud**.\n\n   ![](https://redis.io/docs/latest/images/rc/saml/aws_iam_identity_center_saml_5.png)\n\n1. Next, scroll to the **IAM Identity Center metadata** section. Here, you will find all of the information needed to configure SAML in Redis Cloud:\n\n* IAM Identity Center sign-in URL\n* IAM Identity Center SAML issuer URL\n* IAM Identity Center Certificate\n\nNote down or copy the URLs and select **Download** to download the certification information.\n\n> [!NOTE]\n> Both the IAM Identity Center sign-in URL and the IAM Identity Center SAML issuer URL are the same value. This is expected.\n\n   ![](https://redis.io/docs/latest/images/rc/saml/aws_iam_identity_center_saml_6.png)"},{"id":"step-2-configure-saml-support-in-redis-cloud","title":"Step 2: Configure SAML support in Redis Cloud","role":"content","text":"Now that you have your IAM Identity Center IdP server information, configure support for SAML in Redis Cloud."},{"id":"sign-in-to-redis-cloud","title":"Sign in to Redis Cloud","role":"content","text":"Sign in to your account on the [Redis Cloud console](https://cloud.redis.io/#/login)."},{"id":"activate-saml-in-access-management","title":"Activate SAML in Access Management","role":"content","text":"To activate SAML, you must have a local user (or social sign-on user) with the `owner` role. If you have the correct permissions, you will see the **Single Sign-On** tab.\n\n1. Add the information you saved previously in the **Configuration setup** screen. This includes:\n\n   * **Issuer (IdP Entity ID)**: IAM Identity Center SAML issuer URL.\n   * **IdP server URL**: IAM Identity Center sign-in URL.\n   * **Assertion signing certificate**: Drag and drop the certificate file you downloaded to disk in the form text area.\n\n     ![](https://redis.io/docs/latest/images/rc/saml/sm_saml_1.png)\n\n   Select **Enable** and wait a few seconds for the status to change.\n\n1. Select **Download** to get the service provider (SP) metadata. Save the file to your local hard disk.\n\n   ![](https://redis.io/docs/latest/images/rc/saml/sm_saml_3.png)\n\n1. Open the file in any text editor. Save the following text from the metadata:\n\n   * **EntityID** - The unique name of the service provider (SP).\n\n     ![](https://redis.io/docs/latest/images/rc/saml/sm_saml_4.png)\n\n   * **Location** : The location of the assertion consumer service.\n\n      ![](https://redis.io/docs/latest/images/rc/saml/sm_saml_5.png)"},{"id":"step-3-finish-saml-configuration-in-aws-iam-identity-centers-redis-cloud-application","title":"Step 3: Finish SAML configuration in AWS IAM Identity Center's Redis Cloud Application","role":"content","text":"1. Return to the **Configuration setup** screen in IAM identity Center. Scroll down to the bottom of the page and select **Upload application SAML metadata file**. Select **upload** and choose the file that you downloaded in the SAML configuration screen in Redis Cloud. \n\n   ![](https://redis.io/docs/latest/images/rc/saml/aws_iam_identity_center_saml_10.png)\n\n1. If you would like to also configure an IdP initiated workflow, fill in the **relay state** field in the **Application properties** section. Use this URL: `https://cloud.redis.io/#/login/?idpId=XXXXXX`. Take the ID from the location URL in step 3 (the content after the last forward slash \"/\") and append to the URL.\n\n   ![](https://redis.io/docs/latest/images/rc/saml/aws_iam_identity_center_saml_11.png)\n\n1. Select **Submit** to finish creating the application.\n\n   ![](https://redis.io/docs/latest/images/rc/saml/aws_iam_identity_center_saml_12.png)\n\n1. Configure the **Redis Cloud** application's attribute mappings. Select **Actions > Edit Attribute Mappings**. \n\n   ![](https://redis.io/docs/latest/images/rc/saml/aws_iam_identity_center_saml_13.png)\n\n   In the next screen, add these attributes:\n\n   * **Subject**: `${user:email}`, `unspecified`\n   * **Email**: `${user:email}`, `unspecified`\n   * **FirstName**: `${user:givenName}`, `unspecified`\n   * **LastName**: `${user:familyName}`, `unspecified`\n   * **redisAccountMapping**: `XXXXXXX=owner`, `unspecified`\n\nThe `redisAccountMapping` key-value pair consists of the lowercase role name (owner, member, manager, billing_admin, or viewer) and your Redis Cloud Account ID found in the [account settings](https://redis.io/docs/latest/operate/rc/accounts/account-settings).\n\n![](https://redis.io/docs/latest/images/rc/saml/aws_iam_identity_center_saml_14.png)"},{"id":"step-4-ensure-that-the-cloud-account-user-has-an-iam-identity-center-user-account","title":"Step 4: Ensure that the Cloud account user has an IAM Identity Center user account","role":"content","text":"To complete SAML setup, ensure that the user who began SAML configuration in Redis Cloud console has a user defined in the AWS IAM identity center. This user account is required to complete the SAML setup.\n\nAlso, make sure that the user has been assigned to the **Redis Cloud** Application."},{"id":"step-5-activate-saml-integration","title":"Step 5: Activate SAML integration","role":"content","text":"The final step in our SAML integration with AWS IAM identity Center is to activate the SAML integration. \n\n1. In the Single Sign-On screen, select **Activate**.\n\n   ![](https://redis.io/docs/latest/images/rc/saml/sm_saml_8.png)\n\n  A logout notification screen displays, letting you know that you will be redirected to your identity provider. Select **Continue** to go to the AWS IAM Identity Center's login screen.\n\n1. Enter your AWS IAM Identity Center credentials.\n\n   ![](https://redis.io/docs/latest/images/rc/saml/aws_iam_identity_center_saml_18.png)\n\nIf everything is configured correctly, you will see the the Redis Cloud console screen. Your local account is now considered a SAML account. \n\nTo log in to the Redis Cloud console from now on, click on **Sign in with SSO**.\n\n![Sign in with SSO button](https://redis.io/docs/latest/images/rc/button-sign-in-sso.png)"}],"examples":[]}
{"schema_version":2,"id":"operate/rc/security/access-control/saml-sso/saml-integration-azure-ad","title":"Microsoft Entra (formerly Azure Active Directory) SAML integration guide","url":"https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso/saml-integration-azure-ad/","summary":"This integration guide shows how to set up Microsoft Entra as a SAML single sign on provider for your Redis Cloud account.","tags":["docs","operate","rc"],"last_updated":"2026-10-01T14:41:26+01:00","page_type":"content","content_hash":"3915a55ddae3b1b841116e3e38fc95f2e8cb939e67bf52f148dc35febe2a6d6f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This guide shows how to configure [Microsoft Entra](https://learn.microsoft.com/en-us/azure/active-directory/fundamentals/active-directory-architecture) (formerly Azure Active Directory) as a SAML single sign-on identity provider (IdP) for your Redis Cloud account.\n\nTo learn more about Redis Cloud support for SAML, see [SAML single sign on](https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso).\n\nBefore completing this guide, you must [verify ownership of any domains](https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso#verify-domain) you want to associate with your SAML setup."},{"id":"step-1-set-up-your-identity-provider-idp","title":"Step 1: Set up your identity provider (IdP)","role":"content","text":"To create the Microsoft Entra SAML Toolkit integration application:\n\n1. Sign in to your Microsoft Azure account.\n\n1. From the main menu, select **Microsoft Entra ID > Enterprise Applications**. Select **New application** to add a new application.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/ad_saml_1.png)\n\n1. Select **Microsoft Entra SAML Toolkit** from the list of apps.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/ad_saml_2.png)\n\n1. Name the application **Redis Cloud** and then select **Create**. \n\n    ![](https://redis.io/docs/latest/images/rc/saml/ad_saml_3.png)\n\n1. Select **Properties** and upload the Redis logo. \n\n    ![](https://redis.io/docs/latest/images/rc/saml/ad_saml_17.png)\n\n    Select **Save**.\n\n1. Once you've created the application, go to the **Application Overview** and select **Set up single sign on**.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/ad_saml_4.png)\n\n1. Select **SAML** as the single sign-on method.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/ad_saml_5.png)\n\n1. Go to **Step 1** in the configuration screen and select **Edit**.\n\n    Enter some mock data in the required fields.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/ad_saml_8.png)\n\n    Select **Save** to save your changes.\n\n1. Go to **Step 3** in the configuration screen.\n\n   * Certificate (Base 64) is required to complete SAML configuration in the Redis Cloud console. Select **Download** to download it.\n\n     ![](https://redis.io/docs/latest/images/rc/saml/ad_saml_7.png)\n\n1. Go to **Step 4** in the configuration screen, and note down or copy the following information:\n\n   * **Login URL** is used as the \"IdP server URL\" in the SAML configuration in admin console.\n   * **Microsoft Entra Identifier** is used as the \"Issuer (IdP Entity ID)\" in the SAML configuration in admin console.\n  \n    ![](https://redis.io/docs/latest/images/rc/saml/ad_saml_6.png)"},{"id":"step-2-configure-saml-support-in-redis-cloud","title":"Step 2: Configure SAML support in Redis Cloud","role":"content","text":"Now that you have your Entra IdP server ready, configure support for SAML in Redis Cloud."},{"id":"sign-in-to-redis-cloud","title":"Sign in to Redis Cloud","role":"content","text":"Sign in to your account on the [Redis Cloud console](https://cloud.redis.io/#/login)."},{"id":"activate-saml-in-access-management","title":"Activate SAML in access management","role":"content","text":"To activate SAML, you must have a local user (or social sign-on user) with the **owner** role. If you have the correct permissions, you will see the **Single Sign-On** tab.\n\n1. Fill in the information you saved previously in the **setup** form. This includes :\n\n   * **Issuer (IdP Entity ID)**: Microsoft Entra Identifier\n   * **IdP server URL**: Login URL\n   * **Assertion signing certificate**: Drag-and-drop the certificate file you downloaded to disk in the form text area\n\n     ![](https://redis.io/docs/latest/images/rc/saml/sm_saml_1.png)\n\n   Once you click the **Enable** button, wait a few seconds for the status to change.\n\n1. You will then be able to **download** the service provider (SP) metadata. Save the file to your local hard disk.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/sm_saml_3.png)\n\n1. Open the file in any text editor. Save the following text from the metadata:\n\n   * **EntityID**: The unique name of the service provider (SP)\n\n    ![](https://redis.io/docs/latest/images/rc/saml/sm_saml_4.png)\n\n   * **Location**: The location of the assertion consumer service\n\n    ![](https://redis.io/docs/latest/images/rc/saml/sm_saml_5.png)"},{"id":"step-3-finish-saml-configuration-in-microsoft-entra-id","title":"Step 3: Finish SAML configuration in Microsoft Entra ID","role":"content","text":"1. Go back to Microsoft Entra ID setup and **Edit** the Basic SAML Configuration in **Step 1**. \n   \n   This is where you entered mock data. Let's now enter the correct data for this step.\n\n   > [!NOTE]\n   > For the `EntityID` and `Location` fields below you can directly upload the metadata file using the option at the top of the page. However, you will still need to manually    add the **Sign on URL**.\n   >\n\n    * Paste `EntityID` information in the `Identifier (Entity ID)` field.\n  \n    * Paste `Location` link in `Reply URL (Assertion Consumer Service URL)` field.\n\n    * For the `Sign on URL` field, add URL `https://cloud.redis.io/#/login/?idpId=` where you need to add the ID from the Reply URL ID, for example,    `https://cloud.redis.io/#/login/?idpId=0oa5pwatz2JfpfCb91d7`.\n\n    Select **Save**.\n\n      ![](https://redis.io/docs/latest/images/rc/saml/ad_saml_23.png)\n\n1. Go to step 2, **Attributes & Claims** and select **Edit**.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/ad_saml_24.png)\n\n1. Configure these attributes and claims:\n\n    * Modify Unique User Identifier (Name ID) to **user.mail**\n  \n    * Modify additional claims to match SAML assertion as follows:\n  \n        * **Email**: user.mail\n        * **FirstName**: user.givenname\n        * **LastName**: user.surname\n        * **redisAccountMapping**: `<sm_account_id>=owner`\n\n            The `redisAccountMapping` claim maps Redis Cloud accounts to the role each user receives. Its value is a comma-separated list of `accountId=role` pairs, for example `2613034=owner,2923247=member`.\n\n            * **`accountId`** must be the numeric **Redis Cloud Account ID** found in your [account settings](https://redis.io/docs/latest/operate/rc/accounts/account-settings). Non-numeric values are silently skipped.\n            * **`role`** must be lowercase and one of `owner`, `member`, `manager`, `billing_admin`, or `viewer`. Note the underscore in `billing_admin`.\n            * Redis Cloud reads a **single value** for this claim. If it resolves to multiple values, only one is used — see [Claim conditions and user groups](#claim-conditions-and-user-groups).\n\n          ![](https://redis.io/docs/latest/images/rc/saml/ad_saml_14.png)\n        \n        > [!NOTE]\n        > Make sure the **Namespace** field is empty when modifying these claims.\n        >\n        > ![](https://redis.io/docs/latest/images/rc/saml/ad_saml_namespace_field.png)\n        >\n\n\n1. To add a user to the application, select **User and Groups > Add user/group**.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/ad_saml_15.png)\n\n1. Add the user and select **Assign**.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/ad_saml_16.png)"},{"id":"step-4-return-to-redis-cloud-console","title":"Step 4: Return to Redis Cloud console","role":"content","text":"1. Return to Redis Cloud console and select **Activate**.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/sm_saml_8.png)\n\n1. A popup appears, explaining that you must log in with the credentials of a Microsoft Entra user to test the SAML connection. Select **Continue** to go to the Microsoft login screen.\n\n1. The Microsoft login screen will appear. Enter the credentials and click **Sign In**.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/ad_saml_19.png)\n\nIf everything is configured correctly, you will see the the Redis Cloud console screen. Your local account is now considered a SAML account. \n\nTo log in to the Redis Cloud console from now on, click on **Sign in with SSO**.\n\n![Sign in with SSO button](https://redis.io/docs/latest/images/rc/button-sign-in-sso.png)"},{"id":"claim-conditions-and-user-groups","title":"Claim conditions and user groups","role":"content","text":"The simple `redisAccountMapping` value shown above gives every user assigned to the application the same role. If different users need different roles, or you prefer to manage roles with directory groups, you can build the claim value from each user's group membership instead.\n\nHowever you build it, Redis Cloud reads `redisAccountMapping` as a single string of comma-separated `accountId=role` pairs, so the emitted value must resolve to clean `accountId=role` pairs using the same format described in [Step 3](#step-3-finish-saml-configuration-in-microsoft-entra-id)."},{"id":"map-roles-from-directory-groups-regex-replace","title":"Map roles from directory groups (regex replace)","role":"content","text":"Microsoft Entra can build the claim value from the names of the groups a user belongs to, using **Apply a regex replace to groups claim content**.\n\n1. Create one directory group per role, using a `redis-<role>` naming convention:\n\n    * `redis-owner`\n    * `redis-member`\n    * `redis-manager`\n    * `redis-billing_admin` — note the **underscore**, so the group name matches the `billing_admin` role token exactly\n    * `redis-viewer`\n\n1. On the `redisAccountMapping` claim, set the source to **Attribute**, choose the **groups** attribute, and enable **Apply a regex replace to groups claim content**.\n\n1. Set the regex and replacement patterns:\n\n    * **Regex pattern**: `^redis-(?<role>owner|member|manager|billing_admin|viewer)$`\n    * **Replacement pattern**: `<accountId>={role}` — replace `<accountId>` with your numeric Redis Cloud Account ID. To map more than one account, comma-join the pairs, for example `2613034={role},2923247={role}`.\n\n    > [!WARNING]\n    > The substitution token is `{role}` — **curly braces only**. Do **not** write `${role}`. Entra emits the `$` as a literal character, producing a value like `2613034=$owner`, which Redis Cloud rejects with `saml-config-invalid-account-mapping` because `$owner` is not a valid role token.\n    >\n\n1. Make sure **Emit groups as role claims** is **turned off**. When it is on, the value is emitted under the `http://schemas.microsoft.com/ws/2008/06/identity/claims/role` claim type instead of your custom `redisAccountMapping` claim name, so Redis Cloud never receives the mapping.\n\n> [!NOTE]\n> Set the group claim **Source attribute** to the group **name**, not **Group ID**. If the source is Group ID, the emitted value is a GUID that never matches the `redis-<role>` regex. For groups synced from on-premises Active Directory, use `sAMAccountName`; for cloud-only groups, enable the group-name option.\n\n![](https://redis.io/docs/latest/images/rc/saml/ad_saml_20.png)"},{"id":"users-in-multiple-groups","title":"Users in multiple groups","role":"content","text":"The group-claim regex emits **one value per matching group**, so a user who belongs to more than one `redis-<role>` group produces a multi-valued claim.\n\nThis does not fail login, but because Redis Cloud reads only a single value, the broker forwards one of the values (typically the first). The user then resolves to a single group's role rather than a combination, which makes the effective role unpredictable.\n\n* Assign each user to a **single** `redis-<role>` group per account set, so the resulting role is deterministic.\n* If users need **different roles in different accounts**, do not build the value from groups. Instead, store the full `accountId=role,...` string in a per-user directory or extension attribute and emit that attribute directly, with no regex. This always produces a single value."},{"id":"troubleshooting","title":"Troubleshooting","role":"errors","text":""},{"id":"saml-config-invalid-account-mapping","title":"`saml-config-invalid-account-mapping`","role":"content","text":"This error means the `redisAccountMapping` claim reached Redis Cloud, but no valid `accountId=role` pair could be parsed from its value. Check the emitted value for:\n\n* **Stray characters** — most often a literal `$` from writing `${role}` instead of `{role}` in the regex replacement pattern, which produces `accountId=$owner`.\n* **A wrong role token** — the role must be exactly `owner`, `member`, `manager`, `billing_admin`, or `viewer`, in lowercase. Values like `Owner` or `billing-admin` (hyphen) are rejected.\n* **A non-numeric account ID** — `accountId` must be the numeric Redis Cloud Account ID from [account settings](https://redis.io/docs/latest/operate/rc/accounts/account-settings).\n\nMulti-group membership (a multi-valued claim) does **not** cause this error. Login succeeds, but the user resolves to a single group's role — see [Users in multiple groups](#users-in-multiple-groups)."},{"id":"verify-the-emitted-claim","title":"Verify the emitted claim","role":"content","text":"To confirm the claim value before users sign in:\n\n* Use Entra's **Test this application** on the SAML-based sign-on screen, or\n* Capture the SAML assertion during login — with a tool such as [SAML-tracer](https://addons.mozilla.org/firefox/addon/saml-tracer/), or a HAR capture taken with **Preserve log** enabled from the start of the login flow.\n\nConfirm that `redisAccountMapping` contains clean `accountId=role` pairs, with no `$` or other stray characters and no duplicate account IDs."},{"id":"idp-initiated-sso","title":"IdP initiated SSO","role":"content","text":"If you correctly set the up the **Sign on URL**, the SAML application appears by default on the user's **My Apps** panel.\n\n![](https://redis.io/docs/latest/images/rc/saml/ad_saml_25.png)\n\nWhile assigning the user to the app, a notification will appear:\n\n![](https://redis.io/docs/latest/images/rc/saml/ad_saml_26.png)\n\nTherefore, if you sign into `https://myapplications.microsoft.com/`, the application will be available.\n\nIf the app is not available, make sure that the App is registered. It should be done automatically.\n\n![](https://redis.io/docs/latest/images/rc/saml/ad_saml_27.png)\n\n![](https://redis.io/docs/latest/images/rc/saml/ad_saml_28.png)\n\nYou can also access the app directly by using the **User access Url** from App Properties."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/security/access-control/saml-sso/saml-integration-google","title":"Google Workspace SAML integration guide","url":"https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso/saml-integration-google/","summary":"This integration guide shows how to configure Google Workspace as a SAML single sign on provider for your Redis Cloud account.","tags":["docs","operate","rc"],"last_updated":"2026-10-01T14:41:30+01:00","page_type":"content","content_hash":"e5f6f2419be85a6f887e1d64bf4259af0412c77b742cf9aa6993c1205f1cc3e8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This guide shows how to configure [Google Workspace](https://workspace.google.com/) as a SAML single sign-on identity provider (IdP) for your Redis Cloud account.\n\nTo learn more about Redis Cloud support for SAML, see [SAML single sign-on](https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso).\n\nBefore completing this guide, you must [verify ownership of any domains](https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso#verify-domain) you want to associate with your SAML setup."},{"id":"step-1-set-up-your-identity-provider-idp","title":"Step 1: Set up your identity provider (IdP)","role":"content","text":""},{"id":"create-the-google-workspace-saml-application","title":"Create the Google Workspace SAML application","role":"content","text":"1. Sign in to your [Google Workspace admin account](https://admin.google.com/).\n\n1. From the main menu, select **Apps** then **Web and mobile apps**.\n\n   ![](https://redis.io/docs/latest/images/rc/saml/google_workspace_saml_0.png)\n\n1. Once in Web and mobile apps, select **Add custom SAML app** from the dropdown list.\n\n   ![](https://redis.io/docs/latest/images/rc/saml/google_workspace_saml_1.png)\n\n1. To begin, change the **App name** and **Description** to **Redis Cloud**. You can also choose an **App icon** for the application. We suggest you upload a Redis icon. Once complete, select **Continue**.\n\n   ![](https://redis.io/docs/latest/images/rc/saml/google_workspace_saml_2.png)\n\n1. In the next screen, you will find all of the information needed to configure SAML in Redis Cloud. Select the **copy** button for the following information sections:\n\n   * SSO URL\n   * Entity ID\n   * Certificate\n\n   ![](https://redis.io/docs/latest/images/rc/saml/google_workspace_saml_3.png)\n\nOnce complete, select **Continue**."},{"id":"step-2-configure-saml-support-in-redis-cloud","title":"Step 2: Configure SAML support in Redis Cloud","role":"content","text":"Now that you have your Google Workspace IdP server information, configure support for SAML in Redis Cloud."},{"id":"sign-in-to-redis-cloud","title":"Sign in to Redis Cloud","role":"content","text":"Sign in to your account on the [Redis Cloud console](https://cloud.redis.io/#/login)."},{"id":"activate-saml-in-access-management","title":"Activate SAML in Access Management","role":"content","text":"To activate SAML, you must have a local user (or social sign-on user) with the `owner` role. If you have the correct permissions, you will see the **Single Sign-On** tab.\n\n1. Add the information you saved previously in the **Google identity provider details** screen. This includes:\n\n    * **Issuer (IdP Entity ID)**: `Entity ID`.\n    * **IdP server URL**: `SSO URL`.\n    * **Assertion signing certificate**: `Certificate`.\n\n   ![](https://redis.io/docs/latest/images/rc/saml/sm_saml_1.png)\n\n   Select **Enable** and wait a few seconds for the status to change.\n\n1. Select **Download** to get the service provider (SP) metadata. Save the file to your local hard disk.\n\n   ![](https://redis.io/docs/latest/images/rc/saml/sm_saml_3.png)\n\n1. Open the file in any text editor. Save the following text from the metadata:\n\n    * **EntityID**: The unique name of the service provider (SP).\n\n      ![](https://redis.io/docs/latest/images/rc/saml/sm_saml_4.png)\n\n   * **Location**: The location of the assertion consumer service.\n\n  ![](https://redis.io/docs/latest/images/rc/saml/sm_saml_5.png)"},{"id":"step-3-add-a-custom-attribute-to-google-workspaces-user-profile","title":"Step 3: Add a custom attribute to Google Workspace's user profile","role":"content","text":"1. From the main menu in Google Workspace, select **Directory** then **Users**, and from the **more options** dropdown select **Manage custom attributes**.\n   \n   ![](https://redis.io/docs/latest/images/rc/saml/google_workspace_saml_7.png)\n\n1. From the **Manage user attributes** screen, select **Add Custom Attribute**.\n\n   ![](https://redis.io/docs/latest/images/rc/saml/google_workspace_saml_8.png)\n\n1. Add the following information for the new custom attribute:\n\n   * **Category**: `Redis Cloud`\n   * **Name**: `redisAccountMapping`\n   * **Info type**: `Text`\n   * **Visibility**: `Visible to user and admin`\n   * **No. of values**: `Single`\n\n   ![](https://redis.io/docs/latest/images/rc/saml/google_workspace_saml_9.png)\n\n   Once complete, select **Add**. The summary page now displays the new **redisAccountMapping** custom field.\n\n   ![](https://redis.io/docs/latest/images/rc/saml/google_workspace_saml_10.png)\n\n1. From the main menu in Google Workspace, select **Directory** then **Users**, then select the user you wish to configure. \n\n   ![](https://redis.io/docs/latest/images/rc/saml/google_workspace_saml_11.png)\n\n1. Each user who needs to access Redis Cloud through SAML needs to define the **redisAccountMapping** attribute. The `redisAccountMapping` key-value pair consists of the lowercase role name (owner, member, manager, billing_admin, or viewer) and your Redis Cloud Account ID found in the [account settings](https://redis.io/docs/latest/operate/rc/accounts/account-settings).\n\n   ![](https://redis.io/docs/latest/images/rc/saml/google_workspace_saml_12.png)\n\n   Once complete, select **Save**.\n\n   ![](https://redis.io/docs/latest/images/rc/saml/google_workspace_saml_13.png)\n\n   Repeat this step for each user who needs to define the `redisAccountMapping` attribute."},{"id":"step-4-finish-saml-configuration-in-google-workspaces-redis-cloud-application","title":"Step 4: Finish SAML configuration in Google Workspace's Redis Cloud Application","role":"content","text":"1. Return to the **Service provider details** screen in Google Workspace, and add the following information:\n\n   * **ACS URL**: The Location from the downloaded service provider (SP) metadata\n   * **Entity Id**: The EntityID from the downloaded service provider (SP) metadata\n\n   ![](https://redis.io/docs/latest/images/rc/saml/google_workspace_saml_6.png)\n\n   Leave the **Name ID** default information as it is. Once complete, select **Continue**.\n\n1. Configure the **Redis Cloud** application's attribute mappings. Select **Add Mapping**.\n\n   ![](https://redis.io/docs/latest/images/rc/saml/google_workspace_saml_14.png)\n\n   In the next screen, map these attributes:\n\n   * **Primary Email**: `Email`\n   * **First name**: `FirstName`\n   * **Last name**: `LastName`\n   * **redisAccountMapping**: `redisAccountMapping`\n\n   ![](https://redis.io/docs/latest/images/rc/saml/google_workspace_saml_15.png)\n\n   Once complete, select **Finish**.\n\n1. Next, we need to turn on the Redis Cloud service for all users, select **Web and mobile apps** -> **Redis Cloud** and then **service status**. Select **ON for everyone**. Once complete, select **Save**.\n\n   ![](https://redis.io/docs/latest/images/rc/saml/google_workspace_saml_16.png)"},{"id":"step-5-activate-saml-integration","title":"Step 5: Activate SAML integration","role":"content","text":"The final step in our SAML integration with AWS IAM identity Center is to activate the SAML integration.\n\n1. In the Single Sign-On screen, select **Activate**.\n\n   ![](https://redis.io/docs/latest/images/rc/saml/sm_saml_8.png)\n\nA logout notification screen displays, letting you know that you are redirected to Google's login screen.\n\n1. Select the Google account you wish to login with.\n\n   ![](https://redis.io/docs/latest/images/rc/saml/google_workspace_saml_18.png)\n\nIf everything is configured correctly, you will see the the Redis Cloud console screen. Your local account is now considered a SAML account. \n\nTo log in to the Redis Cloud console from now on, click on **Sign in with SSO**.\n\n![Sign in with SSO button](https://redis.io/docs/latest/images/rc/button-sign-in-sso.png)"}],"examples":[]}
{"schema_version":2,"id":"operate/rc/security/access-control/saml-sso/saml-integration-okta-generic","title":"Okta SAML integration guide (Generic)","url":"https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso/saml-integration-okta-generic/","summary":"This integration guide shows how to set up Okta as a SAML single sign-on provider for your Redis Cloud account.","tags":["docs","operate","rc"],"last_updated":"2026-10-01T14:41:26+01:00","page_type":"content","content_hash":"5e26f2e2f70001016727ed51d007df61b3b2954e32837a2513a3a00e2e7598ab","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This guide shows how to configure [Okta](https://help.okta.com/en-us/Content/Topics/Security/Identity_Providers.htm) as a SAML single sign-on identity provider (IdP) for your Redis Cloud account.\n\nLearn how to use the generic application template. You can also refer to the [Org2Org](https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso/saml-integration-okta-org2org) application template.\n\nTo learn more about Redis Cloud support for SAML, see [SAML single sign-on](https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso).\n\nBefore completing this guide, you must [verify ownership of any domains](https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso#verify-domain) you want to associate with your SAML setup. To do this, you must add a DNS TXT record provided by Redis Cloud to each domain; SAML SSO can't be enabled until the domain is verified."},{"id":"step-1-set-up-your-demo-identity-provider-idp","title":"Step 1: Set up your demo identity provider (IdP)","role":"content","text":"To create the Okta SAML integration application:\n\n1. Log in to the Okta admin console. Select **Applications > Create App Integration**.\n\n   ![](https://redis.io/docs/latest/images/rc/saml/okta_saml_app_int_1.png)\n\n1. Select **SAML 2.0**, then select **Next**.\n\n   ![](https://redis.io/docs/latest/images/rc/saml/okta_saml_app_int_2.png)\n\n1. Complete these fields for the SAML application **General Settings** section:\n\n    * **App name**: Redis Cloud\n\n    * **App logo**: Upload a Redis icon.\n\n    Select **Next**.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/okta_saml_app_int_3.png)\n\n1. In the **Configure SAML** tab, enter this data in the **General** section:\n\n    > [!NOTE]\n    > Setting up SAML SSO is a two-part handshake. You first create the Okta application with temporary mock URLs so that Redis Cloud can generate its own service provider (SP) metadata. Later, in [Step 3](#step-3-configure-saml-support-in-redis-cloud), you return to this Okta application and replace these mock URLs with the real values from that Redis Cloud SP metadata.\n    >\n\n    * **Single sign-on URL**: `http://www.fake.com`. This is a temporary mock URL that you will modify later.\n    * **Audience URI (SP Entity ID)**: `http://www.fake.com`. This is a temporary mock URL that you will modify later.\n    * **Default RelayState**: `https://cloud.redis.io/#/login/?idpId=XXXXXX`. You will need to complete this URL with the `idpId` later. _Complete this field only if you need your SAML flow to be IdP initiated._\n    * **Name ID forma**: `Unspecified`\n    * **Application username**: `Okta username`\n    * **Update application username on**: `Create and update`\n\n    ![](https://redis.io/docs/latest/images/rc/saml/okta_saml_app_int_4.png)\n\n    Next, add __attribute statements__, which are required for the configuration:\n\n    * **Attribute 1**:\n      * **Name**: `redisAccountMapping`\n      * **Name Format**: `Basic`\n      * **Value**: `appuser.redisAccountMapping`\n\n    * **Attribute 2**:\n      * **Name**: `FirstName`\n      * **Name Format**: `Basic`\n      * **Value**: `user.firstName`\n\n    * **Attribute 3**:\n      * **Name**: `LastName`\n      * **Name Format**: `Basic`\n      * **Value**: `user.lastName`\n\n    * **Attribute 4**:\n      * **Name**: `Email`\n      * **Name Format**: `Basic`\n      * **Value**: `user.login`\n\n    ![](https://redis.io/docs/latest/images/rc/saml/okta_saml_app_int_5.png)\n\n    > [!NOTE]\n    > The `redisAccountMapping` attribute is required. If it's missing or malformed, SAML activation and subsequent SAML logins fail.\n    >\n\n    Select **Next**.\n\n1. The last step is an optional feedback step for Okta. Select **I'm an Okta customer adding an internal app** and then select **Finish**.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/okta_saml_app_int_6.png)\n\n1. Next, scroll down the page of your newly created app integration and select **View Setup Instructions**. A new browser window opens, providing the information you need to configure the IdP in Redis Cloud.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/okta_saml_app_int_7.png)\n\n   Scroll down to **Applications > Applications**, and note down the following information:\n   \n    * **Identity Provider Single Sign-On URL**\n    * **Identity Provider Issuer**\n    * **X.509 Certificate**\n\n    ![](https://redis.io/docs/latest/images/rc/saml/okta_saml_app_int_8.png)\n\n   Once you save the information, close the window.\n\nYou must also add `redisAccountMapping` as a custom attribute on the user profile itself, using the Profile Editor. The attribute statement you added above tells Okta which profile field (`appuser.redisAccountMapping`) to pass in the SAML assertion, but that field doesn't exist until you define it here. Without this step, Okta has no `redisAccountMapping` value to send, and Redis Cloud can't determine the user's role or account.\n\nTo modify the application user profile:\n\n1. Go to **Directory > Profile Editor** in the left menu, and select **Redis Cloud User**.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/okta_saml_app_int_9.png)\n\n1. Add the custom attribute to your user profile to specify which Redis Cloud role the user has and on which account. Select **Add Attribute**.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/okta_saml_app_int_10.png)\n\n1. Add this information for the new custom attribute.\n\n    * **Data type**: `string array`\n    * **Display name**: `redisAccountMapping`\n    * **Variable name**: `redisAccountMapping`\n    * **Description**: `redisAccountMapping`\n    * **Attribute required**: `Yes`\n    * **Group priority**: `Combine values across groups`\n\n    ![](https://redis.io/docs/latest/images/rc/saml/okta_saml_app_int_11.png)\n\n1. Once you add the attribute, it appears in the list of attributes for the profile.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/okta_saml_9.png)"},{"id":"step-2-create-a-group-and-assign-the-application","title":"Step 2: Create a group and assign the application","role":"content","text":"Now that your SAML IdP is configured, create an Okta group and assign users to the Redis Cloud application."},{"id":"create-the-group","title":"Create the group","role":"content","text":"1. In the left menu, select **Directory > Groups**, then select **Add group**.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/okta_saml_group_1.png)\n\n1. Complete **Name** and  **Description**, then click **Save**.\n \n    ![](https://redis.io/docs/latest/images/rc/saml/okta_saml_group_2.png)\n\n    ![](https://redis.io/docs/latest/images/rc/saml/okta_saml_group_3.png)"},{"id":"assign-users-to-the-group","title":"Assign users to the group","role":"content","text":"1. Select the group, then select **Assign people**.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/okta_saml_group_4.png)\n\n1. For each user you want to add to the group, highlight the user in the table and select **+**. You can also select **Add all** to add all users. Once you add all the users to your group, select **Save**.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/okta_saml_group_5.png)"},{"id":"assign-the-application-to-the-group","title":"Assign the application to the group","role":"content","text":"Now that your group is populated with its users, you can assign the SAML integration application to your group. From **Applications > Applications > Redis Cloud**:\n\n1. Select **Assign to groups** menu item.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/okta_saml_group_6.png)\n\n1. In the **Redis Cloud User Group**, select **Assign**.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/okta_saml_group_7.png)\n\n1. Define the Redis account mapping string default for this group and select **Save and Go Back**. The key-value pair consists of your **Redis Cloud Account ID** found in the [account settings](https://redis.io/docs/latest/operate/rc/accounts/account-settings) and the lowercase role name (owner, member, manager, billing_admin, or viewer) — for example, `1937217=viewer`. Select **\"Done\"**.\n\n    > [!NOTE]\n    > Role values must be lowercase. A value like `1937217=Viewer` or `1937217=VIEWER` is rejected.\n    >\n\n    ![](https://redis.io/docs/latest/images/rc/saml/okta_saml_group_8.png)\n\n    The mapping field has now been defined as a default for each member of the group.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/okta_saml_group_9.png)"},{"id":"edit-the-mapping-field-for-the-group","title":"Edit the mapping field for the group","role":"content","text":"To modify the Redis mapping field, select the pencil icon of the Redis Cloud group in the **Redis Cloud** application screen.\n\n![](https://redis.io/docs/latest/images/rc/saml/okta_saml_group_10.png)\n\nYou can modify the mapping field for the whole group on the edit screen that appears.\n\n![](https://redis.io/docs/latest/images/rc/saml/okta_saml_group_11.png)"},{"id":"edit-the-mapping-field-for-a-specific-user","title":"Edit the mapping field for a specific user","role":"content","text":"To override the Redis mapping field at an individual user level, select the **People** menu, and then the pencil icon of the person whose field you want to modify.\n\n![](https://redis.io/docs/latest/images/rc/saml/okta_saml_group_15.png)\n\nSet the user's **Assignment master** to **Administrator**, enabling the group's policy override. Select **Save**.\n\n![](https://redis.io/docs/latest/images/rc/saml/okta_saml_group_13.png)\n\nThe user's **Type** is set to `Individual`.\n\n![](https://redis.io/docs/latest/images/rc/saml/okta_saml_group_14.png)\n\nOn the screen that appears, select the pencil icon of the user to modify the Redis mapping field.\n\n![](https://redis.io/docs/latest/images/rc/saml/okta_saml_group_15.png)\n\nThen, edit the user assignment.\n\n![](https://redis.io/docs/latest/images/rc/saml/okta_saml_group_16.png)"},{"id":"step-3-configure-saml-support-in-redis-cloud","title":"Step 3: Configure SAML support in Redis Cloud","role":"content","text":"Now that you have a test IdP server ready as well as your user group, configure support for SAML in Redis Cloud."},{"id":"sign-in-to-redis-cloud","title":"Sign in to Redis Cloud","role":"content","text":"Sign in to your account on the [Redis Cloud console](https://cloud.redis.io/#/login)."},{"id":"activate-saml-in-access-management","title":"Activate SAML in Access Management","role":"content","text":"To activate SAML, you must have a local user (or social sign-on user) with the **owner** role. If you have the correct permissions, you will see the **Single Sign-On** tab.\n\n> [!NOTE]\n> The user who activates SAML must have `ACCOUNT_ID=owner` in their `redisAccountMapping` value, where `ACCOUNT_ID` is the Redis Cloud account being configured. Activation fails if this user's mapping doesn't grant the owner role on that account.\n\n1. Fill in the information you saved in step 6 in the **setup** form, including:\n\n    * **IdP Server URL**: Identity Provider Single Sign-On URL\n    * **Issuer**: Identity Provider Issuer\n    * **Assertion signing certificate**: X.509 Certificate\n\n    > [!NOTE]\n    > Paste the X.509 certificate exactly as Okta provides it. Don't add `-----BEGIN CERTIFICATE-----`/`-----END CERTIFICATE-----` headers unless the Redis Cloud UI specifically asks for them.\n    >\n\n    ![](https://redis.io/docs/latest/images/rc/saml/sm_saml_1.png)\n\n1. Select **Enable** and wait a few seconds for the status to change. You are then able to download the service provider (SP) metadata. Save the file to your local hard disk.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/sm_saml_3.png)\n\n1. Open the file in any text editor. Save the following text from the metadata:\n\n    * **EntityID**: The unique name of the service provider (SP)\n\n    ![](https://redis.io/docs/latest/images/rc/saml/sm_saml_4.png)\n\n    * **Location**: The location of the assertion consumer service\n\n    ![](https://redis.io/docs/latest/images/rc/saml/sm_saml_5.png)\n\n1. Return to Okta, select **Applications > Redis Cloud > General** and select **Edit**.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/okta_saml_app_int_12.png)\n\n1. Then, navigate to **Configure SAML** (step 2) and update the following information in **SAML Settings General**:\n\n   * **Single sign-on URL**: Use the information that you copied for **Location**.\n   * **Audience URI (SP Entity ID)**: Use the information that you copied for **EntityID**.\n   * **Default RelayState**: Only needed if you want to have an IdP initiated flow. Take the ID from the location URL in step 3 (the content after the last forward slash \"/\") and append to the url (for example, `https://cloud.redis.io/#/login/?idpId=YOUR_LOCATION_ID`).\n\n    ![](https://redis.io/docs/latest/images/rc/saml/okta_saml_app_int_13.png)\n\n   Select **Next**, then select **Finish**."},{"id":"return-to-redis-cloud-console","title":"Return to Redis Cloud console","role":"returns","text":"1. Return to Redis Cloud console and select **Activate**.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/sm_saml_8.png)\n\n    A popup appears, stating that to test the SAML connection, you need to log in with Okta credentials of the user defined in the Redis Cloud group. This user is part of the group to which you assigned the Redis Cloud application. Select **Continue** to go to the Okta login screen.\n\n1. The Okta login screen appears. Enter the credentials and select **Sign In**.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/okta_saml_app_int_14.png)\n\nIf everything is configured correctly, you will see the the Redis Cloud console screen. Your local account is now considered a SAML account. \n\nTo log in to the Redis Cloud console from now on, click on **Sign in with SSO**.\n\n![Sign in with SSO button](https://redis.io/docs/latest/images/rc/button-sign-in-sso.png)"}],"examples":[]}
{"schema_version":2,"id":"operate/rc/security/access-control/saml-sso/saml-integration-okta-org2org","title":"Okta SAML integration guide (Org2Org)","url":"https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso/saml-integration-okta-org2org/","summary":"This integration guide shows how to set up Okta as a SAML single sign on provider for your Redis Cloud account.","tags":["docs","operate","rc"],"last_updated":"2026-10-01T14:41:26+01:00","page_type":"content","content_hash":"7b72288a7c448d10214d6259ac3f94ad75e80fcdfda6ae4b75bce334d745d74e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This guide shows how to configure [Okta](https://help.okta.com/en-us/Content/Topics/Security/Identity_Providers.htm) as a SAML single sign-on identity provider (IdP) for your Redis Cloud account.\n\nThis guide shows how to use the Org2Org application template. You can also use the [Generic](https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso/saml-integration-okta-generic) application template.\n\nTo learn more about Redis Cloud support for SAML, see [SAML single sign-on](https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso).\n\nBefore completing this guide, you must [verify ownership of any domains](https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso#verify-domain) you want to associate with your SAML setup."},{"id":"step-1-set-up-your-identity-provider","title":"Step 1: Set up your identity provider","role":"content","text":""},{"id":"create-the-okta-saml-integration-application","title":"Create the Okta SAML integration application","role":"content","text":"Create an Okta \"Org2Org\" SAML integration appliction. \n\n1. Sign in to the Okta admin console.\n\n1. From the left menu, select **Applications**.\n\n1. Select **Browse App Catalog**.\n\n1. Locate and select **Okta Org2Org**.\n\n    ![Use the Okta admin console to locate the Org2Org application template.](https://redis.io/docs/latest/images/rc/saml/okta_saml_1.png)\n\n1. Once you have found the application, click \"Add\".\n\n    ![Data transformaiton Pipeline](https://redis.io/docs/latest/images/rc/saml/okta_saml_2.png)\n\n1. Enter this field for the **Org2Org** application **General Settings** section and select **Next**:\n\n   * **Application label**: `Redis Cloud`\n\n    ![Use the Okta admin console to locate the Org2Org application template.](https://redis.io/docs/latest/images/rc/saml/okta_saml_3.png)\n\n1. Enter the following fields in the **Sign-On Options > Attributes** section:\n\n    * **Name**: `redisAccountMapping`\n    * **Name Format**: `Basic`\n    * **Value**: `appuser.redisAccountMapping`\n\n    > [!WARNING]\n    > To ensure the role mapping will not take effect, don't skip entering `appuser.redisAccountMapping` in the **Value** field.\n    >\n\n\n    ![Use the Okta admin console to locate the Org2Org application template.](https://redis.io/docs/latest/images/rc/saml/okta_saml_4.png)\n\n1. Next, select **View Setup Instructions**. A new browser window opens, providing the information needed to configure the IdP in Redis Cloud.\n\n    ![Use the Okta admin console to locate the Org2Org application template.](https://redis.io/docs/latest/images/rc/saml/okta_saml_5.png)\n\n1. Scroll down to section 6 in the page, and note the following information:\n\n   * **IdP Issuer URI**\n   * **IdP Single Sign-On Url**\n   * **IdP Signature Certificate**: Click the link and download the certificate to your hard drive\n\n    ![Use the Okta admin console to locate the Org2Org application template.](https://redis.io/docs/latest/images/rc/saml/okta_saml_6.png)\n\n    Once you capture the information, close the window, return to the Okta admin console, and select **Done**."},{"id":"modify-the-application-user-profile","title":"Modify the application user profile","role":"content","text":"1. In the left menu, select **Directory > Profile Editor**, then select **Redis Cloud User**.\n\n    ![Use the Okta admin console to locate the Org2Org application template.](https://redis.io/docs/latest/images/rc/saml/okta_saml_7_customer.png)\n\n1. Select **Add Attribute** to add a custom attribute to the user profile and specify the Redis Cloud role.\n\n    ![Use the Okta admin console to locate the Org2Org application template.](https://redis.io/docs/latest/images/rc/saml/okta_saml_7_5_customer.png)\n\n1. Add this information for the new custom attribute:\n\n   * **Data type**: `string array`\n   * **Display name**: `redisAccountMapping`\n   * **Variable nam**: `redisAccountMapping`\n   * **Description**: `redisAccountMapping`\n   * **Attribute required**: `Yes`\n   * **Group priority**: `Combine values across groups`\n\n    ![Use the Okta admin console to locate the Org2Org application template.](https://redis.io/docs/latest/images/rc/saml/okta_saml_app_int_11.png)\n\n1. Once you add the attribute, it appears in the list of profile attributes.\n\n    ![Use the Okta admin console to locate the Org2Org application template.](https://redis.io/docs/latest/images/rc/saml/okta_saml_9.png)\n\n1. Add a Redis Cloud icon to the application because it's easier for users to identify the application. Select the pencil icon on the application logo and upload a Redis image using these steps:\n\n    ![Use the Okta admin console to locate the Org2Org application template.](https://redis.io/docs/latest/images/rc/saml/okta_saml_10_customer.png)\n\n    ![Use the Okta admin console to locate the Org2Org application template.](https://redis.io/docs/latest/images/rc/saml/okta_saml_11_customer.png)"},{"id":"createtestuser","title":"Step 2: Create a group and assign the application","role":"content","text":"Now that our SAML IdP is configured, create an Okta group and assign the Redis Cloud application."},{"id":"create-the-group","title":"Create the group","role":"content","text":"1. In the left menu, select **Directory > Groups**, then select **Add group**.\n\n    ![Use the Okta admin console to locate the Org2Org application template.](https://redis.io/docs/latest/images/rc/saml/okta_saml_group_1.png)\n\n1. Enter **Name** and **Description**.\n\n    ![Use the Okta admin console to locate the Org2Org application template.](https://redis.io/docs/latest/images/rc/saml/okta_saml_group_2.png)\n\n    ![Use the Okta admin console to locate the Org2Org application template.](https://redis.io/docs/latest/images/rc/saml/okta_saml_group_3.png)"},{"id":"assign-users-to-the-group","title":"Assign users to the group","role":"content","text":"1. Select the group, then select **Assign people**.\n\n    ![Use the Okta admin console to locate the Org2Org application template.](https://redis.io/docs/latest/images/rc/saml/okta_saml_group_4.png)\n\n1. For each user you want to add to the group, highlight the user in the table and select **+**. You can also add all users by selecting **Add all**. After you add all the users to your group, select **Save**.\n\n    ![Use the Okta admin console to locate the Org2Org application template.](https://redis.io/docs/latest/images/rc/saml/okta_saml_group_5.png)"},{"id":"assign-application-to-the-group","title":"Assign application to the group","role":"content","text":"Now that your group is populated with its users, assign the SAML integration application to your group. \n\n1. From the menu, select **Applications > Applications > Redis Cloud**. Then, select **Assign to groups**.\n\n    ![Use the Okta admin console to locate the Org2Org application template.](https://redis.io/docs/latest/images/rc/saml/okta_saml_group_6.png)\n\n1. In the **Redis Cloud User Group**, select **Assign**.\n\n    ![Use the Okta admin console to locate the Org2Org application template.](https://redis.io/docs/latest/images/rc/saml/okta_saml_group_7.png)\n\n1. Now, define the Redis account mapping string default for this group and select **Save and go back**. The key-value pair consists of the lowercase role name (owner, member, manager, billing_admin, or viewer) and your **Redis Cloud Account ID** found in the [account settings](https://redis.io/docs/latest/operate/rc/accounts/account-settings). Select **\"Done\"**.\n\n    ![Use the Okta admin console to locate the Org2Org application template.](https://redis.io/docs/latest/images/rc/saml/okta_saml_group_8.png)\n\n    The mapping field is now defined as a default for each member of the group. \n\n    ![Use the Okta admin console to locate the Org2Org application template.](https://redis.io/docs/latest/images/rc/saml/okta_saml_group_9.png)"},{"id":"editing-the-mapping-field-for-the-group","title":"Editing the mapping field for the group","role":"content","text":"To modify the Redis mapping field, select the pencil icon of the Redis Cloud group in the \"Redis Cloud\" application screen.\n\n![Use the Okta admin console to locate the Org2Org application template.](https://redis.io/docs/latest/images/rc/saml/okta_saml_group_10.png)\n\nYou can modify the mapping field for the whole group on the edit screen that appears.\n\n![Use the Okta admin console to locate the Org2Org application template.](https://redis.io/docs/latest/images/rc/saml/okta_saml_group_11.png)"},{"id":"editing-the-mapping-field-for-a-specific-user","title":"Editing the mapping field for a specific user","role":"content","text":"To override the Redis mapping field at an individual user level, select the **People** menu, then select the pencil icon of the person whos field you want to modify.\n\n![Use the Okta admin console to locate the Org2Org application template.](https://redis.io/docs/latest/images/rc/saml/okta_saml_group_12.png)\n\nSet the user's **Assignment master** to `Administrator` to enable group policy overrides. Select **Save**.\n\n![Use the Okta admin console to locate the Org2Org application template.](https://redis.io/docs/latest/images/rc/saml/okta_saml_group_13.png)\n\nThe user's **Type** is set to `Individual`.\n\n![Use the Okta admin console to locate the Org2Org application template.](https://redis.io/docs/latest/images/rc/saml/okta_saml_group_14.png)\n\nOn the screen that appears, select the pencil icon of the user to modify the Redis mapping field.\n\n![Use the Okta admin console to locate the Org2Org application template.](https://redis.io/docs/latest/images/rc/saml/okta_saml_group_15.png)\n\n![Use the Okta admin console to locate the Org2Org application template.](https://redis.io/docs/latest/images/rc/saml/okta_saml_group_16.png)"},{"id":"step-3-configure-saml-support-in-redis-cloud","title":"Step 3: Configure SAML support in Redis Cloud","role":"content","text":"Now that you have a test IdP server and your user group ready, configure support for SAML in Redis Cloud."},{"id":"sign-in-to-redis-cloud","title":"Sign in to Redis Cloud","role":"content","text":"Sign in to your account on the [Redis Cloud console](https://cloud.redis.io/#/login)."},{"id":"activate-saml-in-access-management","title":"Activate SAML in access management","role":"content","text":"To activate SAML, you must have a local user (or social sign-on user) with the **owner** role. If you have the correct permissions, the **Single Sign-On** tab is enabled.\n\n1. Add the information you saved previously in the **setup** form (step 1), including:\n\n   * **Issuer (IdP Entity ID)**: IdP Issuer URI\n   * **IdP server URL**: IdP Single Sign-On Url\n   * **Assertion signing certificate**: Drag and drop the file you downloaded to disk in the form text area.\n\n    ![Use the Okta admin console to locate the Org2Org application template.](https://redis.io/docs/latest/images/rc/saml/sm_saml_1.png)\n\n1. Select **Enable** and wait a few seconds for the status to change. Then, download the service provider (SP) metadata. Save the file to your local hard disk.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/sm_saml_3.png)\n\n1. Open the file in any text editor. Save the following text from the metadata:\n\n   * **EntityID**: Unique name of the service provider (SP)\n\n    ![](https://redis.io/docs/latest/images/rc/saml/sm_saml_4.png)\n\n   * **Location**: Location of the assertion consumer service\n\n    ![](https://redis.io/docs/latest/images/rc/saml/sm_saml_5.png)\n\n\n1. Return to Okta, select **Applications > Redis Cloud > General**, then select **Edit**.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/sm_saml_6.png)\n\n1. Update this information in **Advanced Sign-on Settings**.\n\n   * **Hub ACS URL**: Use the information that you copied for **Location**.\n   * **Audience URI**: Use the information that you copied for **EntityID**.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/sm_saml_7.png)\n\n\nSelect **Save**."},{"id":"idp-initiated-sso","title":"IdP-initiated SSO","role":"content","text":"To use IdP-initiated SSO with identity providers, set the RelayState parameter to URL `https://cloud.redis.io/#/login/?idpId=<ID>`.\n\n> [!NOTE]\n> Replace `<ID>` so it matches the AssertionConsumerService Location URL ID (the content after the last forward slash \"/\"). To learn more about configuring service provider applications, see your identity provider's documentation."},{"id":"return-to-redis-cloud-console","title":"Return to Redis Cloud console","role":"returns","text":"1. Return to Redis Cloud console and select **Activate**.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/sm_saml_8.png)\n\n   A popup appears, explaining that, to test the SAML connection, you need to log in with Okta credentials of the user defined in the Redis Cloud group. This user is part of the group to which you assigned the Redis Cloud application. Select **Continue** to go to the Okta login screen.\n\n1. The Okta login screen appears. Enter the credentials and select **Sign In**.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/sm_saml_10.png)\n\nIf everything is configured correctly, you will see the the Redis Cloud console screen. Your local account is now considered a SAML account. \n\nTo log in to the Redis Cloud console from now on, click on **Sign in with SSO**.\n\n![Sign in with SSO button](https://redis.io/docs/latest/images/rc/button-sign-in-sso.png)"}],"examples":[]}
{"schema_version":2,"id":"operate/rc/security/access-control/saml-sso/saml-integration-ping-identity","title":"PingIdentity SAML integration guide","url":"https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso/saml-integration-ping-identity/","summary":"This integration guide shows how to set up PingIndentity as a SAML single sign-on provider for your Redis Cloud account.","tags":["docs","operate","rc"],"last_updated":"2026-10-01T14:41:30+01:00","page_type":"content","content_hash":"f54dcf3c82cbead33b1e2e5a855c2d2cd66d91f220cfe6a8fd85d0ca78895b93","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This guide shows how to configure [PingIdentity](https://docs.pingidentity.com/) as a SAML single sign-on identity provider (IdP) for your Redis Cloud account.\n\nTo learn more about Redis Cloud support for SAML, see [SAML single sign-on](https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso).\n\nBefore completing this guide, you must [verify ownership of any domains](https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso#verify-domain) you want to associate with your SAML setup."},{"id":"step-1-set-up-your-identity-provider-idp","title":"Step 1: Set up your identity provider (IdP)","role":"content","text":""},{"id":"add-the-redisaccountmapping-attribute","title":"Add the `redisAccountMapping` attribute","role":"content","text":"1. Log in into your Ping Identity account. Open **Administrators > Identities > User Attributes** and select **Add Attribute**.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/ping_identity_saml_1.png)\n\n2. Select the **DECLARED** attribute type.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/ping_identity_saml_2.png)\n\n3. Fill in the fields with the following values:\n\n    * **Name**: `redisAccountMapping`\n    * **Display name**: `redisAccountMapping`\n    * **Description**: `redisAccountMapping`\n\n    ![](https://redis.io/docs/latest/images/rc/saml/ping_identity_saml_3.png)\n\n    Select **Save and Close**. Then, verify that the attribute was created successfully.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/ping_identity_saml_4.png)"},{"id":"add-the-user-who-will-activate-saml-in-service-manager-redis-cloud","title":"Add the user who will activate SAML in Service Manager (Redis Cloud)","role":"content","text":"1. Go to **Administrators > Identities > Users** and select **Add User**.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/ping_identity_saml_5.png)\n\n1. Fill in the following information:\n\n    * **redisAccountMapping**: `{accountID}={role}`\n     \n    **accountID** is the account ID from [account settings](https://redis.io/docs/latest/operate/rc/accounts/account-settings) and **role** represents the role that the user will be assigned in Redis Cloud console (owner, member, manager, billing_admin, or viewer):\n\n    ![](https://redis.io/docs/latest/images/rc/saml/ping_identity_saml_6.png)\n\n    Save and check that the user was added successfully."},{"id":"create-the-ping-identity-saml-application","title":"Create the Ping Identity SAML application","role":"content","text":"1. Go to **Administrators > Connections > Applications** and select **+** to add a new application.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/ping_identity_saml_7.png)\n\n1. Choose a name for the application, select **SAML Application Type** and select **Configure**.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/ping_identity_saml_8.png)\n\n1. In the ACS URLs and Entity ID field add for now some dummy data, like https://example.com\n   \n    * This data will be updated with the correct data in a subsequent step.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/ping_identity_saml_9.png)\n\n    Select **Save**.\n\n1. Go to the **Configuration** tab and save the following information:\n   \n    * Issuer ID\n    * Single Logout Service\n    * Single Signon Service\n\n    This information will be needed once we configure SAML in the Redis Cloud console.\n\n    * Select **Download Metadata**. An XML file will be downloaded. Open it and copy the certificate, which is required for the configuration in Redis Cloud console.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/ping_identity_saml_10.png)\n\n5. Go to the **Attribute Mappings** tab. Add the following attributes:\n\n    * saml_subject\n    * Email\n    * FirstName\n    * LastName\n    * redisAccountMapping\n\n    ![](https://redis.io/docs/latest/images/rc/saml/ping_identity_saml_11.png)"},{"id":"step-2-configure-saml-support-in-redis-cloud","title":"Step 2: Configure SAML support in Redis Cloud","role":"content","text":"Now that we have our Ping Identity IdP server ready, we need to configure support for SAML in Redis Cloud."},{"id":"sign-in-to-redis-cloud","title":"Sign in to Redis Cloud","role":"content","text":"Sign in to your account on the [Redis Cloud console](https://cloud.redis.io/#/login)."},{"id":"activate-saml-in-access-management","title":"Activate SAML in access management","role":"content","text":"To activate SAML, you must have a local user (or social sign-on user) with the **owner** role. If you have the correct permissions, you will see the **Single Sign-On** tab.\n\n1. Fill in the information you copied previously, including:\n\n    * **Issuer (IdP Entity ID)**: `Issuer ID`\n    * **IdP server URL**: `Single Signon Service`\n    * **Single logout URL**: `Single Logout Service`\n    * **Assertion signing certificate**: Certificate info you copied from the Ping Identity XML file\n\n    Also add:\n\n    * **Email domain binding**: The domain used in your company's email addresses\n\n    ![](https://redis.io/docs/latest/images/rc/saml/sm_saml_1.png)\n\n    Select **Enable** and wait a few seconds for the status to change.\n\n1. You will then be able to **Download** the service provider (SP) metadata. Save the file to your local hard disk.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/sm_saml_3.png)\n\n1. Open the file in any text editor. Save the following text from the metadata:\n\n    * **EntityID**: The unique name of the service provider (SP)\n\n    ![](https://redis.io/docs/latest/images/rc/saml/sm_saml_4.png)\n\n    * **Location**: The location of the assertion consumer service\n\n    ![](https://redis.io/docs/latest/images/rc/saml/sm_saml_5.png)"},{"id":"step-3-finish-saml-configuration-in-ping-identity","title":"Step 3: Finish SAML configuration in Ping Identity","role":"content","text":"1. In Ping Identity, go to **Administrators > Connections > Applications** and select your application name. Select the **Configuration** tab and select **Edit**.\n\n    This is where we had entered mock data. We will now enter the correct data for this step:\n\n    * Paste **EntityID** information in the **Entity ID** field.\n\n    * Paste **Location** link in the ACS URLS field.\n\n    * For the **Sign on URL** field, add URL `https://cloud.redis.io/#/login/?idpId=`, where you need to add the ID from the Reply URL ID, for example, `https://cloud.redis.io/#/login/?idpId=0oa5pwatz2JfpfCb91d7`.\n\n    Select **Save**.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/ping_identity_saml_13.png)\n\n1. Select the slider to enable the app.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/ping_identity_saml_14.png)"},{"id":"step-4-return-to-redis-cloud-console","title":"Step 4: Return to Redis Cloud console","role":"content","text":"1. Return to the Redis Cloud console and select **Activate**.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/sm_saml_8.png)\n\n1. A popup appears, explaining that to test the SAML connection, you need to log in with credentials of a user defined in Ping Federate. Select **Continue** to go to the Ping Federate login screen.\n\n1. The Ping Federate login screen will appear. Enter the credentials and select **Sign In**.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/ping_identity_saml_20.png)\n\nIf everything is configured correctly, you will see the the Redis Cloud console screen. Your local account is now considered a SAML account. \n\nTo log in to the Redis Cloud console from now on, click on **Sign in with SSO**.\n\n![Sign in with SSO button](https://redis.io/docs/latest/images/rc/button-sign-in-sso.png)"},{"id":"idp-initiated-sso","title":"IdP-initiated SSO","role":"content","text":"`https://cloud.redis.io/#/login/?idpId=`\n\n1. In Ping Identity, go to **Administrators > Connections > Applications** and select your application name. Select the **Configuration** tab and select **Edit**.\n\n1. Go to **Target Application URL** and enter: **https://{enviroment}/#/login/?idpId={idpId}**, where idpId is the ID found in the Location field, after the last '/'\n\n1. Select **Save**.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/ping_identity_saml_16.png)\n\n1. Go to **https://apps.pingone.com/{environment}/myapps/#**, where environment is the environment ID, found in **Administrators -> Environment** for your app.\n\n    ![](https://redis.io/docs/latest/images/rc/saml/ping_identity_saml_17.png)\n\n   You are redirected to the Redis Cloud console."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/security/access-control/social-login","title":"Social login","url":"https://redis.io/docs/latest/operate/rc/security/access-control/social-login/","summary":"Social login with Redis Cloud.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T14:59:57+01:00","page_type":"content","content_hash":"93af2e312f14b2986944ddcb88683375c4598c664a6d23823f7ac267c3951606","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Cloud supports the following social logins:\n- [GitHub](https://github.com/)\n- [Google](https://accounts.google.com/)\n\n> [!NOTE]\n> If your Google and GitHub accounts share an email address (such as Gmail), you can use either one to sign in to the same Redis Cloud account."},{"id":"create-a-new-account-with-social-login","title":"Create a new account with social login","role":"content","text":"To set up Google login:\n\n1. Select **Google** from the [sign-in](https://cloud.redis.io/) screen.\n1. Sign in with the Google account you would like to use.\n1. Select **Confirm** on the **Sign in with Google** prompt.\n\nTo set up GitHub login:\n\n1. Select **Github** from the [sign-in](https://cloud.redis.io/) screen.\n2. Sign in with the Github account you would like to use.\n3. Select **Authorize** on the **Authorize Redis Okta Auth Github** prompt.\n\n> [!NOTE]\n> If the email address associated with your GitHub account is not public, you will see an error message that your sign in attempt failed. You need to [make your email address public](https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences) on GitHub before you try again."},{"id":"migrate-an-existing-account-to-social-login","title":"Migrate an existing account to social login","role":"content","text":"If you already have a Redis Cloud account that requires an email address and password to sign in, you can migrate your existing account to use a social login associated with that same email address instead.\n\n> [!WARNING]\n> Once you migrate your account to use social login, you cannot revert to your old email/password sign in method.\n\nTo migrate your account to Google social login:\n\n1. Select **Google** on the [sign-in](https://cloud.redis.io/) screen.\n1. Choose your preferred account from the list.\n1. A confirmation prompt will display and warn that you cannot revert to your old sign in method if you proceed with the migration to social login.\n1. Select **Confirm** to continue migration.\n\nTo migrate your account to GitHub social login:\n\n1. Select **Github** on the [sign-in](https://cloud.redis.io/) screen.\n1. Select **Authorize** on the **Authorize Redis Okta Auth Github** prompt.\n1. Enter your GitHub password to **Confirm access**. A confirmation prompt warns that you cannot revert to your old sign-in method if you proceed with the migration to social login.\n1. Select **Confirm** to continue migration."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/security/aws-privatelink","title":"Connect to Amazon Web Services PrivateLink","url":"https://redis.io/docs/latest/operate/rc/security/aws-privatelink/","summary":"","tags":["docs","operate","rc"],"last_updated":"2026-10-01T14:41:35+01:00","page_type":"content","content_hash":"5ab0fd4ca876fb481d0b87d25196b87d189c14bf5e16bfbc3e4d204da870a14b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Amazon Web Services (AWS) PrivateLink](https://docs.aws.amazon.com/vpc/latest/privatelink/privatelink-access-resources.html) allows service providers to securely expose specific services without exposing the entire service provider and consumer VPCs to each other. With AWS PrivateLink, Redis Cloud exposes a VPC endpoint service that you connect to as a consumer from your own VPC. Traffic stays within the AWS network and is isolated from external networks. \n\n> [!NOTE]\n> Connecting to Redis Cloud with an AWS PrivateLink is available only with Redis Cloud Pro.  It is not supported for Redis Cloud Essentials.\n\nYou can use PrivateLink as an alternative to Layer 3 connectivity options like [VPC peering](https://redis.io/docs/latest/operate/rc/security/vpc-peering) and [Transit Gateway](https://redis.io/docs/latest/operate/rc/security/aws-transit-gateway).\n\nAWS PrivateLink provides the following benefits:\n\n- **Improved Security**: PrivateLink exposes the Redis cluster and database(s) as a unidirectional endpoint inside your consumer VPC, thereby avoiding exposing entire VPC subnets to each other and eliminating some possible attack vectors.\n- **Network Flexibility**: PrivateLink enables cross-account and cross-VPC connectivity and can be configured even when the Redis Cloud VPC and your consumer VPC have overlapping CIDR/IP ranges.\n- **Simplified architecture and low latency**: PrivateLink does not require NAT, internet gateways, or VPNs. It provides simplified network routing, without the need for a network load balancer between the application and the Redis database.\n\nSee [Connecting to Redis Cloud with AWS PrivateLink vs. VPC Peering](https://redis.io/blog/connecting-to-redis-cloud-with-aws-privatelink-vs-vpc-peering/) to learn more about the benefits of using AWS PrivateLink to connect to Redis Cloud.\n\n\nSee [Connect to Redis Cloud with AWS PrivateLink](https://www.youtube.com/watch?v=i3aTmcyFihY) for a short video tutorial on how to connect to Redis Cloud with AWS PrivateLink."},{"id":"limitations","title":"Limitations","role":"limits","text":"Be aware of the following limitations when using PrivateLink with Redis Cloud:\n- If you use Layer 3 connectivity options like VPC peering or Transit Gateway together with PrivateLink in the same consumer VPC, the database endpoints exposed through PrivateLink's private DNS will resolve to the VPC endpoint and not to the IPs associated with the Layer 3 connectivity options.\n- Your subnets must have at least 16 available IP addresses for the resource endpoint.\n- Some AWS regions do not support PrivateLink Resource Endpoints. See [AWS VPC Lattice Pricing](https://aws.amazon.com/vpc/lattice/pricing/) for a list of regions that support AWS PrivateLink Resource Endpoints.\n- Redis Cloud's PrivateLink implementation uses PrivateLink Resource Endpoints, which is based on Amazon VPC Lattice, so the [VPC Lattice quotas](https://docs.aws.amazon.com/vpc-lattice/latest/ug/quotas.html) apply. Currently, the following availability zones are not supported with Amazon VPC Lattice: \n    - `use1-az3`\n    - `usw1-az2`\n    - `apne1-az3`\n    - `apne2-az2`\n    - `euw1-az4`\n    - `cac1-az3`\n    - `ilc1-az2`\n\n    We recommend avoiding these availability zones when creating your Redis Cloud database if you plan to use AWS PrivateLink."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before you can connect to Redis Cloud with an AWS PrivateLink VPC resource endpoint, you must have:\n\n- A [Redis Cloud Pro database](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new) and the **Owner** or **Manager** role for your Redis Cloud account.\n- An [AWS VPC](https://docs.aws.amazon.com/vpc/latest/userguide/what-is-amazon-vpc.html) with the following:\n    - A [security group](https://docs.aws.amazon.com/vpc/latest/userguide/creating-security-groups.html) that allows ingress traffic to the following ports: \n        - The database port range (port 10000-19999)\n        - The Redis Cloud metrics port (port 8070), if desired\n    - Subnets in the same region as your Redis Cloud database.\n    - Settings to allow **DNS resolution** and **DNS hostnames**. See [View and update DNS attributes for your VPC](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-dns-updating.html) for more information.\n- Permission to create and manage VPC endpoints or Service networks in AWS.\n- AWS CLI version 2.32 or greater if using the AWS CLI."},{"id":"set-up-privatelink-connection","title":"Set up PrivateLink connection","role":"content","text":"To set up a connection to Redis Cloud with an AWS PrivateLink VPC resource endpoint, you need to:\n\n1. [Associate the Redis Cloud Resource share with one or more AWS accounts](#associate-resource-share).\n1. [Add a connection](#add-connection) from your consumer account using a VPC resource endpoint or a VPC Lattice service network."},{"id":"associate-resource-share","title":"Associate Redis Cloud resource share with an AWS account","role":"content","text":"In this step, you will associate the Redis Cloud resource share with an AWS Account.\n\n1. From the [Redis Cloud console](https://cloud.redis.io/), select the **Subscriptions** menu and then select your subscription from the list.\n\n1. Select **Connectivity > PrivateLink** to view the PrivateLink settings. If you haven't set up AWS PrivateLink before, select **Set up PrivateLink**. \n\n1. In the **Resource Share** section, select **Manage Principals** to open the **Manage Principals** window.\n\n    ![The Resource Share section, with the manage principals button.](https://redis.io/docs/latest/images/rc/privatelink-resource-share.png)\n\n    ![The Manage Principals window lets you add and remove principals from the resource share.](https://redis.io/docs/latest/images/rc/privatelink-manage-principals.png)\n\n1. Select the **Add** button in the **AWS consumer principals** section to add a principal to the resource share.\n\n    ![The Add button adds principals to the resource share.](https://redis.io/docs/latest/images/rc/icon-add.png)\n\n1. Enter the account's ID in the **AWS Account** field. You can also add an optional alias in the **AWS principal alias** field.\n\n    ![The AWS consumer principals section with an AWS account added as a principal.](https://redis.io/docs/latest/images/rc/privatelink-aws-consumer-principals.png)\n\n1. Select **Share** to share the resource share with the principal. The first resource share may take a few minutes.\n\n1. After sharing the resource share with the principal, [accept the resource share in the Resource Access Manager](https://docs.aws.amazon.com/ram/latest/userguide/working-with-shared-invitations.html) or copy the **Accept resource share** command and run it with the AWS CLI.\n\nAfter you accept the resource share, the Redis Cloud console will show the principal as **Accepted**.\n\n![The Consumer Principals section, with the consumer principal shown as accepted.](https://redis.io/docs/latest/images/rc/privatelink-principal-accepted.png)\n\nYou can add additional principals to the resource share at any time."},{"id":"add-connection","title":"Add a connection","role":"content","text":"In this step, you will add a connection from your consumer account using a VPC resource endpoint or a VPC lattice service network. \n\nFrom the **Connectivity > PrivateLink** tab in your Redis Cloud subscription, open the **Add connection** section.\n\n![The Add connection section.](https://redis.io/docs/latest/images/rc/privatelink-add-connection.png)\n\nHere, choose whether you want to connect to Redis using a **Resource endpoint** or a **Service network**. \n\n**Resource endpoint:**\n\nYou can connect with a VPC resource endpoint through the AWS Console or with the AWS CLI.\n\n#### AWS Console\n\nFollow the guide to [create a VPC resource endpoint in the AWS console](https://docs.aws.amazon.com/vpc/latest/privatelink/use-resource-endpoint.html#create-resource-endpoint-aws) with the following settings:\n\n- **Type**: Select **Resources**.\n- **Resource configurations**: Select the configuration with the same Resource Configuration ID as the one shown in the Redis Cloud console.\n- **VPC**: Select your VPC from the list.\n- **Additional settings**: Select **Enable private DNS name** and set **Private DNS Preference** to **Verified domains only** or **Verified domains and specified domains**.\n- **Subnets**: Select the subnets to create endpoint network resources in.\n- **Security groups**: Select any security groups you want to associate with the resource endpoint, including the security group that allows access to the necessary ports, as described in the [prerequisites](#prerequisites)\n\n#### AWS CLI\n\nTo use the AWS CLI to add a VPC resource endpoint, select **Copy** under the **AWS CLI Command** to save the command to your clipboard. Enter the saved command in a terminal shell to create the resource endpoint and replace the following parameters with your own values:\n\n- `<vpc id>`: The ID of your VPC\n- `<subnet ids>`: The IDs of the subnets to create endpoint network resources in\n- `<security group ids>`: The IDs of any security groups you want to associate with the resource endpoint, including the security group that allows access to the necessary ports, as described in the [prerequisites](#prerequisites)\n\n**Service network:**\n\nYou can connect with an existing [VPC lattice service network](https://docs.aws.amazon.com/vpc-lattice/latest/ug/service-networks.html) through the AWS Console or with the AWS CLI.\n\n#### AWS Console\n\nFollow the guide to [Manage resource configuration associations](https://docs.aws.amazon.com/vpc-lattice/latest/ug/service-network-associations.html#service-network-resource-config-association) for your service network. Select the configuration with the same Resource Configuration ID as the one shown in the Redis Cloud console.\n\n#### AWS CLI\n\nTo use the AWS CLI to connect to an already existing service network, select **Copy** under the **AWS CLI Command** to save the command to your clipboard. Enter the saved command in a terminal shell to connect to the service network and replace `<service network id>` with the ID of your service network."},{"id":"connect-to-your-database-and-metrics-endpoint-with-privatelink","title":"Connect to your database and metrics endpoint with PrivateLink","role":"content","text":"After you've connected to Redis Cloud with a VPC resource endpoint or a VPC lattice service network, you can find the endpoints for your databases and cluster metrics in the AWS UI by going to the **Associations** tab for your endpoint or service network and viewing the Private DNS entries. You will have one entry for each database and one entry for the metrics endpoint.\n\n![The Associations tab for a VPC resource endpoint, showing the Private DNS entries for the databases and metrics endpoint.](https://redis.io/docs/latest/images/rc/privatelink-aws-endpoint-associations.png)\n\nTo view them on Redis Cloud, download the **Discovery script** from the Redis Cloud console and run it in your consumer VPC to discover the database endpoints.\n\nThe script returns a list of database endpoints that you can connect to from your consumer VPC.\n\n[code example]\n\nYou can connect to your database by using the database `private-dns-entry` and `port` from your consumer VPC. You can also connect to the metrics endpoint with services like [Prometheus and Grafana](https://redis.io/docs/latest/integrate/prometheus-with-redis-cloud) by using the metrics `private-dns-entry` and `port`.\n\nAfter you've connected to your database, you can view the connection details in the Redis Cloud console in your subscription's **Connectivity > PrivateLink** tab or by going to the [connection wizard](https://redis.io/docs/latest/operate/rc/databases/connect) for your database. The private endpoint will point to the PrivateLink VPC resource endpoint or service network that you created.\n\n> [!NOTE]\n> The connection wizard and other parts of the console show your database's [public endpoint](https://redis.io/docs/latest/operate/rc/databases/connect), which is different from the `private-dns-entry` the discovery script returns. The public endpoint hostname is publicly resolvable and, by default, returns your database's address inside the Redis-managed VPC. That's correct behavior, and it's what [VPC peering](https://redis.io/docs/latest/operate/rc/security/vpc-peering) and [Transit Gateway](https://redis.io/docs/latest/operate/rc/security/aws-transit-gateway) consumers rely on. For PrivateLink, that address is overridden only inside your consumer VPC, by the private hosted zone that AWS creates there. Resolving the public endpoint hostname from anywhere else — including from on-premises over Direct Connect or a VPN — returns the unreachable Redis-managed VPC address, not an error. Use the database's `private-dns-entry` from your consumer VPC, or see [Connect from on-premises](#connect-from-on-premises) if you're connecting from outside it."},{"id":"connect-from-on-premises","title":"Connect from on-premises","role":"content","text":"The private hosted zone that overrides the public endpoint hostname inside your consumer VPC doesn't extend to on-premises networks connected over Direct Connect or a VPN, even though they can reach the consumer VPC. The sections below describe other approaches that you can use instead.\n\nCertificate verification decides which approach you need. Redis Cloud issues server certificates for its own hostnames, not for AWS-owned PrivateLink hostnames. If you use TLS, or expect to, your connection string must use the database's public endpoint hostname so that certificate verification succeeds, which means you need one of the two DNS-based approaches below. If you don't use TLS, you can connect directly with the resource endpoint's default DNS name."},{"id":"use-the-resource-endpoints-default-dns-name","title":"Use the resource endpoint's default DNS name","role":"content","text":"AWS documents this as a supported way to reach a resource endpoint from on-premises, not a workaround:\n\n> [!NOTE]\n> DNS requests from outside the VPC still return the private IP addresses of the resource endpoint's network interfaces. You can use these DNS names to access the resource from on premises, as long as you have access to the VPC that the resource endpoint is in, through VPN or Direct Connect.\n\nFind the resource endpoint's default DNS name in the AWS console, under the endpoint's details, and use it directly in your connection string. This requires no DNS configuration on either side, but it doesn't work with TLS, because the hostname doesn't match your database's certificate."},{"id":"add-a-cname-to-the-public-endpoint-hostname","title":"Add a CNAME to the public endpoint hostname","role":"content","text":"If you want to keep using the database's public endpoint hostname, including with TLS, create a CNAME record in your own DNS that points the hostname to the resource endpoint's default DNS name. This needs one DNS record and no changes on the AWS or Redis Cloud side.\n\nAdd a CNAME record, not an A record to the resource endpoint's network interface addresses. Those addresses aren't guaranteed to stay the same if the resource endpoint is recreated."},{"id":"use-a-route-53-resolver-inbound-endpoint","title":"Use a Route 53 Resolver inbound endpoint","role":"content","text":"For a fully AWS-native setup, create a [Route 53 Resolver inbound endpoint](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/resolver-forwarding-inbound-queries.html) in the consumer VPC and configure conditional forwarding from on-premises for your database's public endpoint domain. This also works with TLS, since it resolves the same public endpoint hostname your certificate expects. See [Integrating AWS Transit Gateway with AWS PrivateLink and Amazon Route 53 Resolver](https://aws.amazon.com/blogs/networking-and-content-delivery/integrating-aws-transit-gateway-with-aws-privatelink-and-amazon-route-53-resolver/) for the reference architecture.\n\nRoute 53 Resolver endpoints are billed per elastic network interface per hour, with a two-interface minimum. See [Amazon Route 53 pricing](https://aws.amazon.com/route53/pricing/). This option costs more than a CNAME record, so it suits consumers of several PrivateLink services rather than a single database."},{"id":"disassociate-connection","title":"Disassociate connection","role":"content","text":"To disassociate a PrivateLink connection:\n\n1. Go to the **Connectivity > PrivateLink** tab in your Redis Cloud subscription. \n\n1. In the **Connections** section, select **Disassociate** button next to the connection you want to disassociate.\n\n    ![The Disassociate button next to a VPC endpoint connection.](https://redis.io/docs/latest/images/rc/privatelink-disassociate-connection.png)\n\n1. Select **Disassociate VPC endpoint** or **Disassociate service network** to confirm.\n\nAfter disassociating the connection, you can delete the VPC resource endpoint or service network in the AWS console."},{"id":"remove-privatelink","title":"Remove PrivateLink","role":"content","text":"To remove PrivateLink as a connectivity option from your database:\n\n1. Go to the **Connectivity > PrivateLink** tab in your Redis Cloud subscription. \n\n1. Select **More Actions > Remove Private Link**.\n\n    ![The Remove Private Link option in the More Actions menu.](https://redis.io/docs/latest/images/rc/privatelink-remove-menu.png)\n\n1. Select the **Remove Private Link** button to confirm.\n\n    ![The Remove Private Link confirmation pop-up.](https://redis.io/docs/latest/images/rc/privatelink-remove-confirm.png)\n\nAll connections that depend on PrivateLink will be immediately rejected as when you remove it from your subscription."}],"examples":[{"id":"connect-to-your-database-and-metrics-endpoint-with-privatelink-ex0","language":"json","code":"[\n  {\n    \"type\": \"metrics\",\n    \"dns-entry\": \"<METRICS DNS ENTRY>\",\n    \"private-dns-entry\": \"<METRIC PRIVATE DNS ENTRY>\",\n    \"port\": 8070\n  },\n  {\n    \"type\": \"database\",\n    \"dns-entry\": \"<DATABASE DNS ENTRY>\",\n    \"private-dns-entry\": \"<PRIVATE DNS ENTRY>\",\n    \"port\": 12345,\n    \"database_id\": 1234567890\n  }\n]","section_id":"connect-to-your-database-and-metrics-endpoint-with-privatelink"}]}
{"schema_version":2,"id":"operate/rc/security/aws-transit-gateway","title":"Connect to Amazon Web Services Transit Gateway","url":"https://redis.io/docs/latest/operate/rc/security/aws-transit-gateway/","summary":"","tags":["docs","operate","rc"],"last_updated":"2026-10-01T14:41:35+01:00","page_type":"content","content_hash":"01fc3a07cd7d6f32304d86a58330c8a84dd5ecf5fb06f16565786004773fe20e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Amazon Web Services (AWS) Transit Gateway](https://docs.aws.amazon.com/vpc/latest/tgw/how-transit-gateways-work.html) acts as a Regional virtual router for traffic flowing between your virtual private cloud(s) (VPCs) and on-premises networks. You can attach different resources to your Transit Gateway which include:\n\n- One or more VPCs\n- One or more virtual private network (VPN) connections\n- One or more AWS Direct Connect gateways\n- One or more Transit Gateway Connect attachments\n- One or more transit gateway peering connections\n\nYou can connect your Redis Cloud Pro subscription to a Transit Gateway which is attached to the VPC of your application. This lets your application connect securely to your Redis Cloud database while optimizing performance.\n\n> [!NOTE]\n> Transit Gateway is available only with Redis Cloud Pro.  It is not supported for Redis Cloud Essentials."},{"id":"considerations","title":"Considerations","role":"content","text":"You can use Transit Gateway as an alternative to [VPC peering](https://redis.io/docs/latest/operate/rc/security/vpc-peering), or you can enable both for your subscription.\n\nCompared to VPC peering, Transit Gateway:\n\n- Supports complex network topologies, such as multiple VPCs or site-to-site VPNs.\n\n- Uses security groups and network ACLs to control traffic between VPCs.\n\n- Has a higher network latency and cost than VPC peering due to Transit Gateway infrastructure costs.\n\nConsider using VPC peering and Transit Gateway in parallel for the following situations:\n\n- When migrating from one connectivity solution to the other.\n\n- If different applications need to connect to the same database but have different latency or security requirements."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before you can set up Transit Gateway, you need to:\n\n1. [Create a database with Redis Cloud Pro](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new) from the [Redis Cloud console](https://cloud.redis.io/#/).\n\n1. [Create a transit gateway](https://docs.aws.amazon.com/vpc/latest/tgw/create-tgw.html) from the [AWS VPC console](https://console.aws.amazon.com/vpc/) in the same region as your database.\n\n> [!NOTE]\n> If you use Redis Cloud Bring your own Cloud, you will need to set its IAM Instance Policy to include Transit Gateway. See [Create IAM resources using the AWS console](https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud/iam-resources/aws-console) for more information."},{"id":"aws-transit-gateway","title":"AWS Transit Gateway","role":"content","text":"To set up Transit Gateway:\n\n1. [Associate your resource share with the Redis AWS account](#associate-resource-share).\n\n1. [Accept the resource share and create an attachment](#accept-resource-share).\n\n1. [Add consumer CIDRs](#add-consumer-cidrs) to the attachment.\n\n1. [Update AWS route tables](#update-route-tables) with the Redis Cloud producer CIDRs."},{"id":"associate-resource-share","title":"Associate resource share with Redis Cloud","role":"content","text":"In this step, you will associate your resource share with your subscription's AWS account. You can do this either in the [AWS console](#aws-console) or with the [AWS CLI](#aws-cli).\n\n#### AWS Console\n\nTo use the AWS console to set up the resource share:\n\n1. From the [Redis Cloud console](https://cloud.redis.io/), select the **Subscriptions** menu and then select your subscription from the list.\n\n1. Select **Connectivity > Transit Gateway** to view the transit gateway settings.\n\n1. In the **Share Transit Gateway** section, select **Copy** under **AWS console** to copy the Redis AWS Account number.\n\n    ![The Share Transit Gateway section.](https://redis.io/docs/latest/images/rc/tgw-share-transit-gateway.png)\n\n1. If you don't have a resource share for your transit gateway, follow the guide to [create a resource share](https://docs.aws.amazon.com/ram/latest/userguide/working-with-sharing-create.html) in the [AWS resource access manager](https://console.aws.amazon.com/ram/home). If you do, follow the guide to [update](https://docs.aws.amazon.com/ram/latest/userguide/working-with-sharing-update.html) the resource share to include the provided AWS account as an Allowed Principal.\n\n    During the **Grant access to principals** step, select **AWS Account** in the **Select principal type** field. Enter the copied AWS account number in the **Enter an AWS Account ID** field. \n\n    ![The AWS Add principal field.](https://redis.io/docs/latest/images/rc/aws-tgw-add-principal.png)\n\n    After the principal is added, it may take some time before it is associated. You can see the status of the principals under **Shared Principals** in the resource share page.\n\n#### AWS CLI\n\nTo use the AWS CLI to set up the resource share:\n\n1. From the [Redis Cloud console](https://cloud.redis.io/), select the **Subscriptions** menu and then select your subscription from the list.\n\n1. Select **Connectivity > Transit Gateway** to view the transit gateway settings.\n\n1. In the **Share Transit Gateway** section, select **Copy** under **AWS CLI Command** to copy the Redis AWS Account number.\n\n    ![The Share Transit Gateway section.](https://redis.io/docs/latest/images/rc/tgw-share-transit-gateway.png)\n\n1. Enter the copied CLI command into a terminal shell. Replace `<TGW ARN>` with the Amazon resource name of your transit gateway."},{"id":"accept-resource-share","title":"Accept resource share and create attachment","role":"content","text":"After you've associated the Redis AWS account with your resource share, you must accept the resource share in the Redis Cloud console.\n\n1. In your Redis Cloud subscription's Transit Gateway settings, you should now see that a **Resource Share** is available. Select **Resource Shares** to view the resource share you initiated.\n\n    ![The Share Transit Gateway section.](https://redis.io/docs/latest/images/rc/tgw-resource-shares-button.png)\n\n1. Select **Accept** to associate the **Resource Share** with your Redis Cloud console account.\n\n    ![The Accept resource shares section.](https://redis.io/docs/latest/images/rc/tgw-accept-resource-shares.png)\n\n1. Select **Close** to close the **Accept resource shares** section.\n\n1. You will now see your transit gateway in the **Transit Gateways** section. After the **TGW status** is **Available**, select **Create Attachment** under **Attachment status**. \n\n    ![The Create attachment button.](https://redis.io/docs/latest/images/rc/tgw-create-attachment-button.png)\n\n    This will request a peering attachment representing Redis's AWS account to the Transit Gateway. \n\n1. If your transit gateway does not automatically accept peering attachment requests, the attachment will be in **Pending acceptance** status. Follow the guide to [Accept a peering attachment request](https://docs.aws.amazon.com/vpc/latest/tgw/tgw-peering.html#tgw-peering-accept-reject) from the [AWS VPC console](https://console.aws.amazon.com/vpc/)."},{"id":"add-consumer-cidrs","title":"Add consumer CIDRs","role":"content","text":"1. In your Redis Cloud subscription's Transit Gateway settings, in the **Transit Gateways** section, select **Add CIDRs** under **Consumer CIDRs**.\n\n    ![The Add CIDRs button.](https://redis.io/docs/latest/images/rc/tgw-add-cidrs-button.png)\n\n1. Enter the IPv4 CIDR of the VPC you want to connect to that is also connected to your transit gateway. To find this, go to the [AWS VPC console](https://console.aws.amazon.com/vpc/) and select **Your VPCs**.\n\n    Select **Add** to add another CIDR if needed.\n\n    ![The Add button for adding additional CIDRs.](https://redis.io/docs/latest/images/rc/tgw-add-additional-cidrs-button.png)\n\n    Select **Save** to save your changes."},{"id":"update-route-tables","title":"Update AWS route tables","role":"content","text":"To finish Transit gateway setup, [update your route tables for the peering connection](https://docs.aws.amazon.com/vpc/latest/peering/vpc-peering-routing.html) with the following details:\n\n1. In the **Destination** field, enter the producer deployment CIDRs. \n\n    You can find the producer deployment CIDRs on the Redis Cloud console in the Transit Gateway settings by selecting **More actions > View Attachment** in the **Transit Gateway** section.\n\n    ![The More actions menu.](https://redis.io/docs/latest/images/rc/tgw-attachment-more-actions-menu.png)\n\n    ![The Producer deployment CIDRs in the Attachment settings. ](https://redis.io/docs/latest/images/rc/tgw-producer-cidr-copy.png)\n\n1. In the **Target** field, select **Transit Gateway** and select the relevant **Transit gateway ID**.\n\nAfter Transit gateway is established, we recommend switching your application connection string to the private endpoint.\n\n> [!NOTE]\n> If you've enabled the database's [CIDR allow list](https://redis.io/docs/latest/operate/rc/security/cidr-whitelist), you must also [add the Transit Gateway's IP address to the CIDR allow list](https://redis.io/docs/latest/operate/rc/security/cidr-whitelist#define-cidr-allow-list) to connect to the database via the private endpoint."},{"id":"continue-learning-with-redis-university","title":"Continue learning with Redis University","role":"content","text":""}],"examples":[]}
{"schema_version":2,"id":"operate/rc/security/cidr-whitelist","title":"Configure CIDR allow list","url":"https://redis.io/docs/latest/operate/rc/security/cidr-whitelist/","summary":"The CIDR allow list permits traffic between a range of IP addresses and the Redis Cloud VPC.","tags":["docs","operate","rc"],"last_updated":"2026-10-01T14:41:35+01:00","page_type":"content","content_hash":"169d10bf9b4a4c80ea5bd0fdb1f0bfd4df6989effb7daac5df191d7c62e09511","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The [CIDR](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing) [allow list](https://en.wikipedia.org/wiki/Whitelist) lets you restrict traffic to your Redis Cloud database. When you configure an allow list, only the [IP addresses](https://en.wikipedia.org/wiki/IP_address) defined in the list can connect to the database. Traffic from all other IP addresses is blocked.\n\n\n> [!NOTE]\n> To use the CIDR allow list, you must be on either paid Redis Cloud Essentials or on Redis Cloud Pro.  This feature is not supported on free Redis Cloud Essentials plans."},{"id":"define-cidr-allow-list","title":"Define CIDR allow list","role":"content","text":"To define the CIDR allow list for a database:\n\n1. Select **Databases** from the [Redis Cloud console](https://cloud.redis.io/) menu and then select your database from the list.\n\n1. From the database's **Configuration** screen, select the **Edit database** button.\n\n1. In the **Security** section, turn on the **CIDR allow list** toggle.\n\n1. Enter the first IP address (in CIDR format) you want to allow in the text box and then select the check mark to add it to the allow list:\n\n    ![Add the first IP address to the CIDR allow list.](https://redis.io/docs/latest/images/rc/database-details-configuration-tab-security-cidr-allowlist-add-first-ip.png)\n   \n1. To allow additional IP addresses:\n\n    1. Select **Add CIDR**.\n\n    1. Enter the new IP address in the text box and then select check to add it to the allow list.\n\n        ![Add a new IP address to the CIDR allow list.](https://redis.io/docs/latest/images/rc/database-details-configuration-tab-security-cidr-allowlist-add-more-ips.png)\n\n    The number of CIDR allow list entries that you can add is based on your Redis Cloud plan. Redis Cloud Essentials plans can have between 4 and 32 entries depending on the plan. Redis Cloud Pro plans can have up to 32 entries.\n\n1. Select **Save database** to apply your changes.\n\n> [!NOTE]\n> The database CIDR allow list applies to both the public endpoint and the private endpoint. If you use connectivity options such as [VPC Peering](https://redis.io/docs/latest/operate/rc/security/vpc-peering) and [Transit Gateway](https://redis.io/docs/latest/operate/rc/security/aws-transit-gateway) to connect to your database via the private endpoint, you must also add those IPs to your database's CIDR allow list.\n\n&nbsp;\n> [!NOTE]\n> If the target database has a CIDR allow list, you must add the Redis Data Integration (RDI) pipeline's CIDR to the target database's CIDR allow list. Otherwise, the RDI pipeline can't access the database. The RDI pipeline's CIDR is displayed in the [**Workspace details**](https://redis.io/docs/latest/operate/rc/rdi/create-workspace) modal."},{"id":"continue-learning-with-redis-university","title":"Continue learning with Redis University","role":"content","text":""}],"examples":[]}
{"schema_version":2,"id":"operate/rc/security/database-security/block-public-endpoints","title":"Block public endpoints","url":"https://redis.io/docs/latest/operate/rc/security/database-security/block-public-endpoints/","summary":"Learn how to block the public endpoints of your databases.","tags":["docs","operate","rc"],"last_updated":"2026-10-05T10:59:47+01:00","page_type":"content","content_hash":"dc50aefbcedbe78d6b3ddfb3f6044f767df62a770ba766daacc291c90bd2e261","sections":[{"id":"overview","title":"Overview","role":"overview","text":"By default, you can connect to Redis Cloud databases through the database's public endpoint, or through the database's private endpoint with a private connectivity method. \n\nPublic endpoints are accessible from the public internet and don't require a private connectivity method. While this makes Redis Cloud databases convenient to use, it also exposes the databases to potential unauthorized access or brute force attacks, even with a database password in place. Some organizations may want to block public access to their databases to comply with security policies or to better meet stringent compliance standards.\n\nUsers with Redis Cloud Pro databases can choose to block public endpoints for all databases in their subscription.\n\n> [!NOTE]\n> After you block your database's public endpoint, any connection from an IP address that is not part of the private address space defined in [RFC 1918](https://datatracker.ietf.org/doc/html/rfc1918#section-3) will be rejected. Ensure that all connections to your database are made through a private connectivity method before blocking the public endpoint."},{"id":"block-public-endpoints","title":"Block public endpoints","role":"content","text":"You can block public endpoints for a [new subscription](#new-subscription) or an [existing subscription](#existing-subscription)."},{"id":"new-subscription","title":"New subscription","role":"content","text":"To block the public endpoints when you [create a new Pro subscription](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new):\n\n1. Follow the instructions to [create a Pro database with custom settings](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new#custom-settings). \n1. On the **Setup** tab, go to **Advanced options > Security** to select persistent storage encryption options. \n1. Select **Block public endpoint** to block the public endpoint for all databases on the subscription. \n1. Select **Continue** to go to the [Sizing tab](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new#sizing-tab). Follow the instructions to provision your database(s).\n\nAfter you block the public endpoints for a new subscription, you will need to set up a [private connectivity method](#private-connectivity-methods) to connect to your databases."},{"id":"existing-subscription","title":"Existing subscription","role":"content","text":"For existing subscriptions, we recommend setting up a [private connectivity method](#private-connectivity-methods) to connect to your databases before blocking the private endpoint and migrating all connections to the private endpoint. \n\nTo block the public endpoints of an existing Pro subscription:\n\n1. From the [Redis Cloud console](https://cloud.redis.io/), select the **Subscriptions** menu and then select your subscription from the list. \n1. Open the **Security** tab to view security settings.\n1. In the **Endpoint** section, select **Edit**.\n1. Select **Block public endpoint**.\n1. Select **Save** to save your changes.\n1. A window will appear asking you to confirm that blocking the public endpoint will reject clients connecting to the public endpoint. Select **I understand** and then **Block** to confirm.\n\nAfter your changes are saved, any incoming connections to the public endpoint of your database will be rejected, and only connections through a private connectivity method will be allowed."},{"id":"turn-on-passwordless-authentication-for-the-default-user","title":"Turn on passwordless authentication for the default user","role":"content","text":"If you have blocked the public endpoint for your Redis Cloud Pro subscription, you can turn on passwordless authentication for the [default user](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/default-user) for any database in that subscription.\n\nTo turn on passwordless authentication:\n\n1. From the database **Configuration** tab, select **Edit**.\n1. In the **Security** section, select **Use passwordless auth** to turn on passwordless authentication.\n\n    ![Turn on passwordless authentication for the default user.](https://redis.io/docs/latest/images/rc/database-configuration-security-passwordless.png)\n\n1. Select **Save database** to save your changes."},{"id":"private-connectivity-methods","title":"Private connectivity methods","role":"content","text":"Redis Cloud supports the following private connectivity options:\n- [VPC peering](https://redis.io/docs/latest/operate/rc/security/vpc-peering)\n- [Google Cloud Private Service Connect](https://redis.io/docs/latest/operate/rc/security/private-service-connect) _(Google Cloud only)_\n- [AWS Transit Gateway](https://redis.io/docs/latest/operate/rc/security/aws-transit-gateway) or [AWS PrivateLink](https://redis.io/docs/latest/operate/rc/security/aws-privatelink) _(AWS only)_"}],"examples":[]}
{"schema_version":2,"id":"operate/rc/security/database-security","title":"Cloud database security","url":"https://redis.io/docs/latest/operate/rc/security/database-security/","summary":"","tags":["docs","operate","rc"],"last_updated":"2026-09-25T14:59:57+01:00","children":[{"id":"operate/rc/security/database-security/tls-ssl","summary":"Enable TLS to encrypt data communications between applications and Redis databases.","title":"Transport Layer Security (TLS)","url":"https://redis.io/docs/latest/operate/rc/security/database-security/tls-ssl/"},{"id":"operate/rc/security/database-security/network-security","summary":"","title":"Network security","url":"https://redis.io/docs/latest/operate/rc/security/database-security/network-security/"},{"id":"operate/rc/security/database-security/block-public-endpoints","summary":"Learn how to block the public endpoints of your databases.","title":"Block public endpoints","url":"https://redis.io/docs/latest/operate/rc/security/database-security/block-public-endpoints/"}],"page_type":"content","content_hash":"0ca424c05a3a35a13f2014c85c38bec3ed119073e0c0656d036b353cd34be57e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Cloud provides several features to help you secure your databases. These include\n[password-based authentication](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/default-user) and [role-based access control](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/role-based-access-control),\n[network security](https://redis.io/docs/latest/operate/rc/security/database-security/network-security), [TLS](https://redis.io/docs/latest/operate/rc/security/database-security/tls-ssl), and [encryption-at-rest](https://redis.io/docs/latest/operate/rc/security/encryption-at-rest)."},{"id":"passwords-users-and-roles","title":"Passwords, users, and roles","role":"content","text":"All Redis Cloud databases [require a password](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/default-user) to connect. However, we recommend enabling [role-based access control](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/role-based-access-control) (RBAC) for additional security. With RBAC, you can define\nall the roles you need, with the appropriate permissions, and assign those roles\nto your users."},{"id":"network-security","title":"Network security","role":"content","text":"Redis Cloud supports two types of network security: [IP Restrictions](https://redis.io/docs/latest/operate/rc/security/database-security/network-security#ip) and [VPCs](https://redis.io/docs/latest/operate/rc/security/database-security/network-security#virtual-private-clouds). We recommend that you employ at least one of these network security options to constrain access to your databases."},{"id":"transport-layer-security-tls","title":"Transport Layer Security (TLS)","role":"content","text":"Redis Cloud supports [Transport Layer Security](https://redis.io/docs/latest/operate/rc/security/database-security/tls-ssl) (TLS) for database access. TLS, often called \"SSL\", ensures the privacy of the TCP connection between your application and database. When client\nauthentication is activated, TLS ensures that only clients with an authorized key can access to your Redis databases.\n\nWe strongly recommend enabling TLS for any application transmitting sensitive data across the wire."},{"id":"disk-encryption","title":"Disk encryption","role":"content","text":"Redis Cloud provides encryption for all data stored on disk in Redis databases. See our [encryption at rest documentation](https://redis.io/docs/latest/operate/rc/security/encryption-at-rest) for specific details."},{"id":"continue-learning-with-redis-university","title":"Continue learning with Redis University","role":"content","text":""}],"examples":[]}
{"schema_version":2,"id":"operate/rc/security/database-security/network-security","title":"Network security","url":"https://redis.io/docs/latest/operate/rc/security/database-security/network-security/","summary":"","tags":["docs","operate","rc"],"last_updated":"2026-09-25T14:59:57+01:00","page_type":"content","content_hash":"7bec36326cae81f758fe36d03233585eb203984d70fe817c3324fc6e7a3b38ff","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Cloud supports two types of network security: database-level CIDR allow lists and [VPC](#virtual-private-clouds)-wide CIDR allow lists.\n\nThese features are available in most Redis Cloud configurations, as shown here:\n\n| Cloud&nbsp;provider | VPC peering | IP restrictions |\n|:-------------------:|-------------|-----------------|\n| AWS | Redis Cloud Pro | Paid Redis Cloud Essentials and Redis Cloud Pro |\n| Google Cloud | Redis Cloud Pro | Paid Redis Cloud Essentials and Redis Cloud Pro |\n| Azure | Redis Cloud Pro | Redis Cloud Pro |"},{"id":"ip","title":"IP and subnet restrictions","role":"content","text":"You can restrict database access to a configurable\nset of source IP addresses and subnets. This is roughly equivalent\nto using [iptables](https://en.wikipedia.org/wiki/Iptables) to limit access to a host.\n\nTo restrict a database to a specific set of source IP addresses or subnets, see [Configure CIDR allow list](https://redis.io/docs/latest/operate/rc/security/cidr-whitelist)."},{"id":"virtual-private-clouds","title":"Virtual private clouds","role":"content","text":"A [Virtual Private Cloud](https://en.wikipedia.org/wiki/Virtual_private_cloud) (VPC) is an isolated set of resources within a [public cloud](https://en.wikipedia.org/wiki/Cloud_computing#Public_cloud), usually having its own subnets and VLAN.\n\nRedis Cloud Pro databases are almost always deployed in a Redis VPC. In most cases, you'll need to create a [VPC peering connection](https://redis.io/docs/latest/operate/rc/security/vpc-peering) to access these databases. A VPC peering connection allows unrestricted network access between two VPCs.\n\nConnection creation and supported features vary by public cloud provider. Learn more about VPC usage for [AWS](#vpcs-with-aws), [Google Cloud](#vpcs-with-google-cloud), and [Azure](#vpcs-with-azure)."},{"id":"vpcs-with-aws","title":"VPCs with AWS","role":"content","text":"Subscriptions that run on AWS support two VPC options. To ensure that that you can securely connect to your database, you need to [create a VPC peering connection](https://redis.io/docs/latest/operate/rc/security/vpc-peering#aws-vpc-peering).\n\nIf you create a VPC peering connection, you can also [configure a CIDR allow list](https://redis.io/docs/latest/operate/rc/security/cidr-whitelist) to allow connections only from specific IP address blocks or security groups."},{"id":"vpcs-with-google-cloud","title":"VPCs with Google Cloud","role":"content","text":"Subscriptions that run on Google Cloud *require* a VPC peering connection. See [Google Cloud VPC peering](https://redis.io/docs/latest/operate/rc/security/vpc-peering#gcp-vpc-peering) to learn how to set up VPC peering for Google Cloud."},{"id":"vpcs-with-azure","title":"VPCs with Azure","role":"content","text":"When you request a Redis Cloud Annual subscription, all databases will be deployed in your own Azure VPC."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/security/database-security/tls-ssl","title":"Transport Layer Security (TLS)","url":"https://redis.io/docs/latest/operate/rc/security/database-security/tls-ssl/","summary":"Enable TLS to encrypt data communications between applications and Redis databases.","tags":["docs","operate","rc"],"last_updated":"2026-10-01T14:41:35+01:00","page_type":"content","content_hash":"942126d8002b11ca461848ba598a592a39ae5203ceca8374c449c7daa76ad8a8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Transport Layer Security (TLS) uses encryption to secure [network communications](https://en.wikipedia.org/wiki/Transport_Layer_Security).  \n\nPaid Redis Cloud Essentials plans and Redis Cloud Pro plans can use TLS to encrypt data communications between applications and Redis databases.\n\n> [!NOTE]\n> TLS is not available for Free Redis Cloud Essentials plans."},{"id":"tls-recommendations","title":"TLS recommendations","role":"content","text":"TLS is not enabled by default.\n\nBecause TLS has an impact on performance, you need to determine whether the security benefits of TLS are worth the performance impact. TLS recommendations depend on the subscription plan and whether clients connect to your database using public or private endpoints.\n\nThis table shows TLS recommendations:\n\n| Subscription | Public&nbsp;endpoint | Private endpoint |\n|--------------|----------------------|------------|\n| Redis Cloud Essentials        | Enable TLS           | N/A |\n| Redis Cloud Pro     | Enable TLS           | Enable TLS if security outweighs performance impact |"},{"id":"client-authentication","title":"Client authentication","role":"content","text":"When you enable TLS, you can optionally require client authentication (also known as \"mutual authentication\"). If enabled, all clients must present a valid client certificate when they connect to the database.\n\nClient authentication is not required by Redis Cloud; however, it is strongly recommended."},{"id":"enable-tls","title":"Enable TLS","role":"content","text":"To enable TLS for a Redis Cloud database:\n\n1. Select **Databases** from the [Redis Cloud console](https://cloud.redis.io/) menu and then select your database from the list.\n\n1. From the database's **Configuration** screen, select **Edit**:\n\n    ![The Edit database button lets you change selected database properties.](https://redis.io/docs/latest/images/rc/button-database-edit.png)\n\n1. In the **Security** section, use the **Transport layer security (TLS)** toggle to enable TLS:\n\n    ![Use the Transport Layer Security toggle to enable TLS.](https://redis.io/docs/latest/images/rc/database-details-configuration-tab-security-tls-toggle.png)\n\n1. Select the **Download server certificate** button to download the Redis Cloud certificate bundle `redis_ca.pem`:\n\n    ![Use the Download server certificate button to download the Redis Cloud CA certificates.](https://redis.io/docs/latest/images/rc/button-database-config-security-server-ca-download.png)\n\n1. Decide whether you want to require client authentication:\n\n    - If you only want clients that present a valid certificate to be able to connect, continue to the next step.\n    \n    - If you do not want to require client authentication, skip to the final step to apply your changes.\n\n1. To require client authentication, select the **Mutual TLS (require client authentication)** checkbox. \n\n1. Select **Add client certificate** to add a certificate.\n\n    ![The Add client certificate button.](https://redis.io/docs/latest/images/rc/mtls-add-client-certificate.png)\n\n1. Either provide an [X.509 client certificate](https://en.wikipedia.org/wiki/X.509) or chain in PEM format for your client or select **Generate** to create one:\n\n    ![Provide or generate a certificate for Mutual TLS.](https://redis.io/docs/latest/images/rc/database-details-configuration-tab-security-tls-client-auth-certificate.png)\n\n    - If you generate your certificate from the Redis Cloud console, a **Download certificate** button will appear after it is generated. Select it to download the certificate. \n\n        ![The Download certificate button.](https://redis.io/docs/latest/images/rc/mtls-download-certificate.png)\n        \n        The download contains:\n\n        - `redis-db-<database_id>.crt` – the certificate's public key.\n\n        - `redis-db-<database_id>.key` – the certificate's private key.\n\n        > [!NOTE]\n        > You must download the certificate using the button at this point.  After your changes have been applied, the full bundle of public and private keys will no longer be available for download.\n        >\n    \n    - If you provide a client certificate or a certificate chain, you will see the certificate details before you save your changes.\n\n        ![The Download certificate button.](https://redis.io/docs/latest/images/rc/mtls-certificate-details.png)\n    \n    Select **Save** to save the client certificate.\n\n1. You can select **Add client certificate** again to add another certificate.\n\n    ![The Add client certificate button.](https://redis.io/docs/latest/images/rc/mtls-add-client-certificate.png)\n\n1. To apply your changes and enable TLS, select the **Save database** button:\n\n    ![Use the Save database button to save database changes.](https://redis.io/docs/latest/images/rc/button-database-save.png)\n\n> [!NOTE]\n> - When you enable or turn off TLS, the change applies to new connections but does not affect existing connections. Clients must close existing connections and reconnect to apply the change.\n>\n> - Once you've enabled TLS, all client connections to your database must use TLS. Unencrypted connections will no longer be permitted."},{"id":"connect-over-tls","title":"Connect over TLS","role":"content","text":"To connect to a Redis Cloud database over TLS, you need:\n\n* A Redis client that supports TLS\n* Redis Cloud CA certificates"},{"id":"download-certificates","title":"Download CA certificates","role":"content","text":"If you don't have the Redis Cloud CA certificates, you can download them from the Redis Cloud console:\n\n1. Either select **Account Settings** from the Redis Cloud console menu or go to the database's **Configuration** screen.\n\n1. Go to the **Security** section.\n\n1. For **Redis Cloud certificate authority**, either:\n\n    - Select the **Download** button to download the certificates from **Redis Cloud certificate authority** in **Account Settings**:\n\n        ![Use the Download button to download the Redis Cloud CA certificates.](https://redis.io/docs/latest/images/rc/button-account-settings-security-ca-download.png)\n\n    - Select the **Download server certificate** button to download the certificates from the database's **Configuration** screen:\n\n        ![Use the Download server certificate button to download the Redis Cloud CA certificates.](https://redis.io/docs/latest/images/rc/button-database-config-security-server-ca-download.png)\n\nThe download contains a file called `redis_ca.pem`, which includes the following certificates:\n   \n- Self-signed Redis Cloud Essentials plan Root CA (deprecated but still in use)\n\n- Self-signed Redis Cloud Pro plan Root CA and intermediate CA (deprecated but still in use)\n\n- Publicly trusted GlobalSign Root CA\n\n> [!NOTE]\n> The downloaded PEM file contains multiple certificates. Make sure to import **all** certificates to your client trust store. If your client code is not implemented properly, it may only import the first certificate. \n\nTo inspect the certificates in `redis_ca.pem`, run the `keytool` command:\n\n[code example]\n\nYou can add `redis_ca.pem` to the trust store or pass it directly to a Redis client.\n\nIf your database requires client authentication, you also need the public (`redis_user.crt`) and private (`redis_user_private.key`) client keys. See\n[Enable TLS](#enable-tls) for details."},{"id":"connect-with-the-redis-cli","title":"Connect with the Redis CLI","role":"content","text":"Here's how to use the [Redis CLI](https://redis.io/docs/latest/operate/rs/references/cli-utilities/redis-cli) to connect to a TLS-enabled Redis Cloud database.\n\nEndpoint and port details are available from the **Databases** list or the database's **Configuration** screen.\n\n#### Without client authentication\n\nIf your database doesn't require client authentication, then provide the Redis Cloud CA certificate bundle (`redis_ca.pem`) when you connect:\n\n[code example]\n\n#### With client authentication\n\nIf your database requires client authentication, then you also need to provide your client's private and public keys:\n\n[code example]"}],"examples":[{"id":"download-certificates-ex0","language":"sh","code":"keytool -printcert -file ./redis_ca.pem | grep \"Owner:\"","section_id":"download-certificates"},{"id":"connect-with-the-redis-cli-ex0","language":"sh","code":"redis-cli -h <endpoint> -p <port> --tls --cacert redis_ca.pem","section_id":"connect-with-the-redis-cli"},{"id":"connect-with-the-redis-cli-ex1","language":"sh","code":"redis-cli -h <endpoint> -p <port> --tls --cacert redis_ca.pem \\\n    --cert redis_user.crt --key redis_user_private.key","section_id":"connect-with-the-redis-cli"}]}
{"schema_version":2,"id":"operate/rc/security/encryption-at-rest","title":"Encryption at rest","url":"https://redis.io/docs/latest/operate/rc/security/encryption-at-rest/","summary":"Describes when data is encrypted at rest.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T14:59:57+01:00","page_type":"content","content_hash":"2525eb19fe9a41b49425809ad09743d11da18f3e8e38cc5f1c22a634c9dca9ed","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Cloud databases write their data to disk whenever [persistence](https://redis.io/docs/latest/operate/rc/databases/configuration/data-persistence) is enabled. \n\nRedis Cloud deployments are always encrypted at rest."},{"id":"aws","title":"Encryption at rest on AWS","role":"content","text":"Persistent data is written to encrypted EBS volumes. For more information, see [Amazon EBS encryption](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSEncryption.html). \n\nWhen Auto Tiering is enabled, the flash memory data is written to encrypted NVMe SSD volumes. For more information, see [SSD instance store volumes](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ssd-instance-store.html)."},{"id":"gcp","title":"Disk encryption on Google Cloud","role":"content","text":"All data written to disk on Google Cloud-based Redis Cloud deployments is encrypted by default. When deploying\na Redis Cloud database on Google Cloud, you don't need to take any actions to enable this encryption.\n\nTo learn more, see the [Google Cloud Default encryption at rest](https://cloud.google.com/security/encryption-at-rest)."},{"id":"azure","title":"Disk encryption on Azure","role":"content","text":"All data written to disk on Azure-based Redis Cloud deployments is encrypted by default. When deploying\na Redis Cloud database on Azure, you don't need to take any actions to enable this encryption.\n\nTo learn more, see the [Azure Data Encryption at rest](https://docs.microsoft.com/en-us/azure/security/fundamentals/encryption-atrest)."},{"id":"continue-learning-with-redis-university","title":"Continue learning with Redis University","role":"content","text":""}],"examples":[]}
{"schema_version":2,"id":"operate/rc/security","title":"Security","url":"https://redis.io/docs/latest/operate/rc/security/","summary":"","tags":["docs","operate","rc"],"last_updated":"2026-09-25T14:59:57+01:00","children":[{"id":"operate/rc/security/access-control","summary":"Control who can access the Redis Cloud console and databases.","title":"Access control","url":"https://redis.io/docs/latest/operate/rc/security/access-control/"},{"id":"operate/rc/security/shared-responsibility-model","summary":"","title":"Redis Cloud shared responsibility model","url":"https://redis.io/docs/latest/operate/rc/security/shared-responsibility-model/"},{"id":"operate/rc/security/database-security","summary":"","title":"Cloud database security","url":"https://redis.io/docs/latest/operate/rc/security/database-security/"},{"id":"operate/rc/security/cidr-whitelist","summary":"The CIDR allow list permits traffic between a range of IP addresses and the Redis Cloud VPC.","title":"Configure CIDR allow list","url":"https://redis.io/docs/latest/operate/rc/security/cidr-whitelist/"},{"id":"operate/rc/security/encryption-at-rest","summary":"Describes when data is encrypted at rest.","title":"Encryption at rest","url":"https://redis.io/docs/latest/operate/rc/security/encryption-at-rest/"},{"id":"operate/rc/security/aws-privatelink","summary":"","title":"Connect to Amazon Web Services PrivateLink","url":"https://redis.io/docs/latest/operate/rc/security/aws-privatelink/"},{"id":"operate/rc/security/aws-transit-gateway","summary":"","title":"Connect to Amazon Web Services Transit Gateway","url":"https://redis.io/docs/latest/operate/rc/security/aws-transit-gateway/"},{"id":"operate/rc/security/private-service-connect","summary":"Private Service Connect creates a private endpoint that allows secure connections to Redis Cloud databases without exposing your application VPC.","title":"Enable Google Cloud Private Service Connect","url":"https://redis.io/docs/latest/operate/rc/security/private-service-connect/"},{"id":"operate/rc/security/vpc-peering","summary":"VPC peering uses private IP addresses to route traffic between a Redis Cloud VPC and an application VPC.","title":"Enable VPC peering","url":"https://redis.io/docs/latest/operate/rc/security/vpc-peering/"},{"id":"operate/rc/security/manage-encryption-keys","summary":"Learn how to use your own keys for persistent storage encryption on Redis Cloud.","title":"Self-managed persistent storage encryption","url":"https://redis.io/docs/latest/operate/rc/security/manage-encryption-keys/"}],"page_type":"content","content_hash":"e4a15bde6f0735e169d0928251bb3f8096f2263c28c284646c200a813ec013f8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Cloud provides a number of ways to secure subscriptions and databases.\n\nAs a Redis Cloud user, consider these security domains:\n\n- The [Redis Cloud console](#admin-console-security)\n- [Your databases](https://redis.io/docs/latest/operate/rc/security/database-security)\n- The [Redis Cloud API](https://redis.io/docs/latest/operate/rc/api/get-started/enable-the-api)\n\nFirst, you might want to review our [shared responsibility model](https://redis.io/docs/latest/operate/rc/security/shared-responsibility-model) for security."},{"id":"admin-console-security","title":"Redis Cloud console security","role":"content","text":"The Redis Cloud console is the web application you use to manage your Redis Cloud deployments. \n\nSecure access to the Redis Cloud console by:\n\n- Assigning appropriate roles to [team members with access](https://redis.io/docs/latest/operate/rc/security/access-control/access-management#team-management-roles).\n\n- Enabling [multi-factor authentication](https://redis.io/docs/latest/operate/rc/security/access-control/multi-factor-authentication).\n\n- Enabling [SAML SSO](https://redis.io/docs/latest/operate/rc/security/access-control/saml-sso).\n\n> [!NOTE]\n> A user session on Redis Cloud expires after 30 minutes. You'll be signed out of Redis Cloud after 30 minutes of inactivity."},{"id":"database-security","title":"Database security","role":"content","text":"You have several options when it comes to securing your Redis Cloud databases. For more information, see [Cloud database security](https://redis.io/docs/latest/operate/rc/security/database-security). Options include:\n\n- [Encryption at rest](https://redis.io/docs/latest/operate/rc/security/encryption-at-rest)\n- [Role-based access control](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/role-based-access-control)\n- [TLS](https://redis.io/docs/latest/operate/rc/security/database-security/tls-ssl)\n- [Network security](https://redis.io/docs/latest/operate/rc/security/database-security/network-security) using\n- [VPC peering](https://redis.io/docs/latest/operate/rc/security/vpc-peering) and [CIDR whitelist](https://redis.io/docs/latest/operate/rc/security/cidr-whitelist)"},{"id":"api-security","title":"API security","role":"content","text":"The Redis Cloud API allows you to programmatically administer your subscriptions and database deployments. This API is disabled by default. When you [enable the API](https://redis.io/docs/latest/operate/rc/api/get-started/enable-the-api), you can then [manage the API keys](https://redis.io/docs/latest/operate/rc/api/get-started/manage-api-keys) for all owners of your Redis Cloud account. For an overview of the security features of the API, see the [API authentication documentation](https://redis.io/docs/latest/operate/rc/api/get-started)."},{"id":"continue-learning-with-redis-university","title":"Continue learning with Redis University","role":"content","text":""}],"examples":[]}
{"schema_version":2,"id":"operate/rc/security/manage-encryption-keys","title":"Self-managed persistent storage encryption","url":"https://redis.io/docs/latest/operate/rc/security/manage-encryption-keys/","summary":"Learn how to use your own keys for persistent storage encryption on Redis Cloud.","tags":["docs","operate","rc"],"last_updated":"2026-10-14T07:28:08-07:00","page_type":"content","content_hash":"ae1e8c756e07917e80da3db028ddfa382cb1db2d0d4984af502a07e1dd879bcd","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Cloud databases write their data to disk whenever [persistence](https://redis.io/docs/latest/operate/rc/databases/configuration/data-persistence.md) is enabled. \n\nAll persistent data on Redis Cloud is [encrypted at rest](https://redis.io/docs/latest/operate/rc/security/encryption-at-rest). By default, disk storage is encrypted by keys managed by the cloud provider.\n\nRedis Cloud Pro users can choose to use self-managed encryption keys for persistent storage for all databases in a subscription."},{"id":"self-managed-encryption-use-cases-and-benefits","title":"Self-managed encryption use cases and benefits","role":"content","text":"Self-managed persistent storage encryption allows you to:\n- **Shield sensitive data**: With Self-managed persistent storage encryption, you can stop sensitive data exposure by revoking key access at any time.\n- **Meet security and compliance requirements**: With self-managed persistent storage encryption, you may be able to easier meet compliance standards for data security and privacy protection.\n\nConsider using self-managed persistent storage encryption if you have specific organizational needs or requirements for data security."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before you set up self-managed encryption, you must have a self-managed encryption key. \n\nThe encryption key must be hosted by the same cloud provider as your database and must be available in your database's cloud provider region.\n\nRefer to the provider's documentation to create a key:\n- [Amazon Web Services - Create a KMS key](https://docs.aws.amazon.com/kms/latest/developerguide/create-keys.html)\n- [Google Cloud - Create a key](https://docs.cloud.google.com/kms/docs/create-key)\n\n\nActive-Active databases span multiple regions. You can use a single multi-region key that covers all regions, or a separate key for each region. Each key must be hosted by the same cloud provider and available in the region where it's used."},{"id":"set-up-self-managed-encryption","title":"Set up self-managed encryption","role":"content","text":"To set up self-managed encryption:\n\n1. [Activate self-managed encryption](#activate-self-managed-encryption) for a new subscription.\n2. [Grant Redis permission to access your encryption key](#grant-key-permissions)."},{"id":"activate-self-managed-encryption","title":"Activate self-managed encryption","role":"content","text":"To activate self-managed encryption when you [create a new Pro subscription](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new):\n\n1. Follow the instructions to [create a Pro database with custom settings](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new#custom-settings). \n1. On the **Setup** tab, go to **Advanced options > Security** to select persistent storage encryption options. \n1. Select **Customer managed key** to activate self-managed encryption. \n1. Select **Continue** to go to the [Sizing tab](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new#sizing-tab). Follow the instructions to provision your database(s).\n\nAfter you set up your subscription and database(s), your subscription will be **Pending** until you [grant Redis access to your encryption key](#grant-key-permissions). You won't be charged for your subscription while it's pending. \n\nIf you don't grant key permissions after 7 days, we'll remove your initial setup."},{"id":"grant-key-permissions","title":"Grant key permissions","role":"content","text":"After you activate self-managed encryption, you must grant Redis access to your encryption key on your cloud provider so we can use it for storage encryption and for recovery. \n\nFor Active-Active databases, you must grant Redis access to a key for each region. After you've granted the required permissions for Redis to access your encryption keys, provide the key resource name for each region. If you have a multi-region key, you can use the same key for each region.\n\n![For an Active-Active subscription, the Provide the resource name of your customer managed key section has a Key resource name field for each region.](https://redis.io/docs/latest/images/rc/cmek-permission.png)\n\nFollow the steps for your cloud provider:\n- [Amazon Web Services](#amazon-web-services)\n- [Google Cloud](#google-cloud)\n\n#### Amazon Web Services \n\nTo grant Redis access to a key on AWS:\n\n1. From your subscription page on the Redis Cloud console, copy the provided objects to add to the key policy.\n\n    ![The Grant Redis your customer-managed key section shows the IAM role that needs access to your key and objects to add to your key policy.](https://redis.io/docs/latest/images/rc/cmek-access-roles-aws.png)\n\n2. Go to the [Key management service](https://console.aws.amazon.com/kms) on the AWS console and locate your key.\n\n3. [Modify the key policy](https://docs.aws.amazon.com/kms/latest/developerguide/key-policy-modifying.html) using the policy view. Add the provided objects in the **Statement** section and then save your changes.\n\n4. Return to the Redis Cloud console. In your subscription page, enter your key's ARN in the **Key resource name** field.\n\n    ![The Provide the name of your customer-managed key section lets you provide your key to Redis.](https://redis.io/docs/latest/images/rc/cmek-provide-resource-name-aws.png)\n\n    At this point, Redis Cloud will check to see if it can access your key. If it can't access your key, make sure that you've added the correct permissions to your key, that the key is available in the database's cloud provider region, and that you have correctly entered your key's resource name.\n\n5. Choose a **Deletion grace period** from the list. You can choose between the following options:\n    - **Alert only (No deletion, limited SLA)**: If Redis Cloud loses access to your key, Redis will notify you but will not delete your database.\n    - **15 minutes**, **30 minutes**, **1 hour**, **4 hours**, **8 hours**, **12 hours**, or **24 hours**: If Redis Cloud loses access to your key, Redis will notify you and delete your database if access isn't restored before the selected period ends.\n    - **Immediate**: If Redis Cloud loses access to your key, Redis will notify you and delete your database immediately.\n\n    \nIf you select **Alert only (No deletion, limited SLA)**, Redis will not be able to make changes to your database if we lose access to your key. This includes database upgrades, failovers to persistent storage, and other operations that require access to your key. Because of this, Redis will not be able to meet its [Service Level Agreement (SLA)](https://redis.io/legal/redis-cloud-service-level-agreement/) if we lose access to your key.\n\nProvide a new key as soon as possible to avoid service disruption.\n    \n\n6. After you finish granting access to your key, you can save your changes. For a new subscription, select **Activate** to activate your subscription and start billing.\n\n    ![The Activate button.](https://redis.io/docs/latest/images/rc/cmek-new-subscription-activate.png)\n\n#### Google Cloud\n\nTo grant Redis access to a key on Google Cloud:\n\n1. From your subscription page on the Redis Cloud console, copy the provided Redis service account name.\n\n    ![The Grant Redis your customer-managed key section shows the service account needed to add as a principal.](https://redis.io/docs/latest/images/rc/cmek-access-roles-google.png)\n\n2. Go to [Key management](https://console.cloud.google.com/security/kms) on the Google Cloud console and locate your key.\n\n3. Add the provided service account as a principal for your key, with one of the following Role options:\n\n    - Add the pre-defined IAM roles [Cloud KMS CryptoKey Encrypter/Decrypter](https://docs.cloud.google.com/kms/docs/reference/permissions-and-roles#cloudkms.cryptoKeyEncrypterDecrypter) and [Cloud KMS Viewer](https://docs.cloud.google.com/kms/docs/reference/permissions-and-roles#cloudkms.viewer), OR\n    - [Create a custom IAM role](https://docs.cloud.google.com/iam/docs/creating-custom-roles#creating) with the following minimal permissions needed to use the key, and then assign that custom role to the principal:\n        - cloudkms.cryptoKeyVersions.useToDecrypt\n        - cloudkms.cryptoKeyVersions.useToEncrypt\n        - cloudkms.cryptoKeys.get\n\n4. Return to the Redis Cloud console. In your subscription page, enter your key's resource name in the **Key resource name** field.\n\n    ![The Provide the name of your customer-managed key section lets you provide your key to Redis.](https://redis.io/docs/latest/images/rc/cmek-provide-resource-name-google.png)\n\nAt this point, Redis Cloud will check to see if it can access your key. If it can't access your key, make sure that you've added the correct permissions to your key, that the key is available in the database's cloud provider region, and that you have correctly entered your key's resource name.\n\n5. Choose a **Deletion grace period** from the list. You can choose between the following options:\n    - **Alert only (No deletion, limited SLA)**: If Redis Cloud loses access to your key, Redis will notify you but will not delete your database.\n    - **15 minutes**, **30 minutes**, **1 hour**, **4 hours**, **8 hours**, **12 hours**, or **24 hours**: If Redis Cloud loses access to your key, Redis will notify you and delete your database if access isn't restored before the selected period ends.\n    - **Immediate**: If Redis Cloud loses access to your key, Redis will notify you and delete your database immediately.\n\n    \nIf you select **Alert only (No deletion, limited SLA)**, Redis will not be able to make changes to your database if we lose access to your key. This includes database upgrades, failovers to persistent storage, and other operations that require access to your key. Because of this, Redis will not be able to meet its [Service Level Agreement (SLA)](https://redis.io/legal/redis-cloud-service-level-agreement/) if we lose access to your key.\n\nProvide a new key as soon as possible to avoid service disruption.\n    \n\n6. After you finish granting access to your key, you can save your changes. For a new subscription, select **Activate** to activate your subscription and start billing.\n\n    ![The Activate button.](https://redis.io/docs/latest/images/rc/cmek-new-subscription-activate.png)"},{"id":"rotate-your-encryption-key","title":"Rotate your encryption key","role":"content","text":"You can rotate your key material or key version on your cloud provider independently of Redis:\n\n- **Amazon Web Services**: The key ARN stays the same, and only the underlying key material is rotated.\n- **Google Cloud**: The key stays the same, and your cloud provider creates a new key version.\n\nYour cloud provider manages the key rotation and encryption process. Any node provisioned after the rotation uses the new key material or version."},{"id":"change-to-a-different-key","title":"Change to a different key","role":"content","text":"You can't replace the configured key with a different key. Changing the key ARN isn't supported. You can only rotate the key material or version, as described in [Rotate your encryption key](#rotate-your-encryption-key)."},{"id":"revoke-key-access","title":"Revoke key access","role":"content","text":"When you have set up self-managed encryption, you can revoke Redis's access to your encryption key at any time through your cloud provider. \n\nIf you selected **Immediate** as the deletion grace period, Redis will immediately delete your database if we lose access to your key. If you selected a timed grace period, such as **1 hour**, Redis will notify you and delete your database if access isn't restored before that period ends. If you selected **Alert only (No deletion, limited SLA)**, Redis will notify you but will not delete your database.\n\n\nIf you selected **Alert only (No deletion, limited SLA)**, Redis will not be able to make changes to your database if we lose access to your key. This includes database upgrades, failovers to persistent storage, and other operations that require access to your key. Because of this, Redis will not be able to meet its [Service Level Agreement (SLA)](https://redis.io/legal/redis-cloud-service-level-agreement/) if we lose access to your key.\n\nProvide a new key as soon as possible to avoid service disruption."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/security/private-service-connect","title":"Enable Google Cloud Private Service Connect","url":"https://redis.io/docs/latest/operate/rc/security/private-service-connect/","summary":"Private Service Connect creates a private endpoint that allows secure connections to Redis Cloud databases without exposing your application VPC.","tags":["docs","operate","rc"],"last_updated":"2026-10-01T14:41:35+01:00","page_type":"content","content_hash":"3987b33831901cbcc66387246a22d32130b62cb4af4504f573931e4d4831de4f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Private Service Connect](https://cloud.google.com/vpc/docs/private-service-connect) (PSC) creates a private endpoint that allows secure connections to Redis Cloud databases without exposing your application's [virtual private cloud](https://en.wikipedia.org/wiki/Virtual_private_cloud) (VPC). \n\nThis feature is only available for Redis Cloud Pro subscriptions hosted on Google Cloud."},{"id":"considerations","title":"Considerations","role":"content","text":"You can use Private Service Connect as an alternative to [VPC peering](https://redis.io/docs/latest/operate/rc/security/vpc-peering), or you can enable both for your subscription.\n\nCompared to VPC peering, Private Service Connect:\n\n- Only exposes the private endpoint instead of the entire application VPC network.\n\n- Allows producer (Redis Cloud VPC) and consumer (application VPC) CIDR ranges to overlap.\n \n- Has slightly higher network latency than VPC peering due to load balancing requirements.\n\n    > [!NOTE]\n    > Larger clusters are more likely to experience increased latency with Private Service Connect versus VPC peering.\n    >\n\nConsider using VPC peering and Private Service Connect in parallel for the following situations:\n\n- When migrating from one connectivity solution to the other.\n\n- If different applications need to connect to the same database but have different latency or security requirements."},{"id":"setup-psc","title":"Set up Private Service Connect","role":"content","text":"To set up Private Service Connect, you need to:\n\n1. [Configure Private Service Connect](#configure-psc) in the Redis Cloud console.\n\n1. [Create Private Service Connect endpoints](#create-endpoints) in the application VPC.\n\n1. From the Redis Cloud console, review and [accept the Private Service Connect endpoint connection](#accept-psc)."},{"id":"configure-psc","title":"Configure PSC","role":"configuration","text":"First, configure Private Service Connect in Redis Cloud:\n\n1. Select **Subscriptions** from the [Redis Cloud console](https://cloud.redis.io/) menu and then select your subscription from the list.\n\n2. Select the **Connectivity** tab and then **Private Service Connect**.\n\n3. Select the **Create connection** button:\n\n    ![Use the Create connection button to configure a new PSC endpoint.](https://redis.io/docs/latest/images/rc/button-subscription-connectivity-psc-create-connection.png)\n\n4. Read the **Latency and cost impact** message and select **Accept and continue**:\n\n    ![Use the Accept and continue button to acknowledge PSC's impact on latency and cost.](https://redis.io/docs/latest/images/rc/button-subscription-connectivity-psc-accept-continue.png)\n\n5. For **Create connection**, enter the following **Endpoint details**:\n\n    | Setting&nbsp;name | Description |\n    |-------------------|-------------|\n    | _Google Cloud project ID_ | Google Cloud project ID |\n    | _VPC name_ | Name of the VPC that hosts your application |\n    | _Subnet name_ | Name of your VPC's subnet of IP address ranges |\n    | _Endpoint name_ | Prefix used to create PSC endpoints in the consumer application VPC, so endpoint names appear in Google Cloud as _endpoint name prefix + endpoint number_ |\n\n6. Continue to the **Add connections** step:\n    \n    ![Use the Continue button to proceed to the Add connections step.](https://redis.io/docs/latest/images/rc/button-subscription-continue.png)\n\n7. Select either **Bash Shell** or **PowerShell** and then download or copy the provided `gcloud` script for later:\n\n    ![Use the Download or Copy buttons to save the gcloud script for later use.](https://redis.io/docs/latest/images/rc/subscription-connectivity-psc-gcloud-script.png)\n\n8. Select **Continue** to save this endpoint configuration:\n\n    ![Use the Continue button to save the PSC endpoint configuration.](https://redis.io/docs/latest/images/rc/button-subscription-continue.png)"},{"id":"create-endpoints","title":"Create endpoints","role":"content","text":"Now that you have a pending Private Service Connect entry, you need to create the endpoints in your application's VPC:\n\n1. If you have not already done so, [enable Cloud DNS](https://cloud.google.com/dns/docs/set-up-dns-records-domain-name) for your Google Cloud project.\n\n    > [!NOTE]\n    > Since it can take some time for the DNS changes to become active, we recommend you wait 10 minutes before running the `gcloud` script in the next steps.\n    >\n\n1. If you already have a copy of the `gcloud` script shown earlier during the **Add connections** step, you can continue to the next step. \n\n    1. Otherwise, return to your Redis Cloud subscription's **Connectivity > Private Service Connect** screen and select **Complete setup** for the pending endpoint:\n\n        ![Use the Complete setup button if you need access to the gcloud script again.](https://redis.io/docs/latest/images/rc/button-subscription-connectivity-psc-complete-setup.png)\n\n    1. Download or copy the script.\n\n1. Use the [`gcloud` CLI](https://cloud.google.com/sdk/gcloud) to run the script.\n\n    > [!WARNING]\n    > To ensure the `gcloud` script configures the endpoints correctly, do not make any changes to it.\n    >\n\nThe `gcloud` script creates a PSC endpoint and a DNS response policy in the consumer application VPC."},{"id":"accept-psc","title":"Accept PSC connection","role":"content","text":"After the `gcloud` script finishes creating the Private Service Connect endpoint, you need to accept the connection in Redis Cloud:\n\n1. In the Redis Cloud console, return to your subscription's **Connectivity > Private Service Connect** screen.\n\n1. Find your pending endpoint connection in the list and select **Accept**:\n\n    ![Use the Accept button to finish PSC endpoint setup.](https://redis.io/docs/latest/images/rc/button-subscription-connectivity-psc-accept.png)"},{"id":"connect-db","title":"Connect to database","role":"content","text":"Once your Private Service Connect endpoint is active, you can connect your application to a database:\n\n1. From your subscription's **Connectivity > Private Service Connect** tab, select the **Connect** button for the active endpoint:\n\n    ![Use the Connect button to retrieve PSC connection details.](https://redis.io/docs/latest/images/rc/button-connect.png)\n\n1. Select a database from the list.\n\n1. Copy the endpoint and use it in your application to connect to your database.\n\n> [!NOTE]\n> Once your Private Service Connect endpoint is active, you must connect to the database from the Private Service Connect endpoint if you want to connect with Private Service connect. If you previously used the public or private endpoint to connect to your database, you must migrate any connections to the Private Service Connect endpoint."},{"id":"deactivate-psc","title":"Deactivate Private Service Connect","role":"content","text":"To deactivate Private Service Connect for your subscription:\n\n1. Select **Subscriptions** from the [Redis Cloud console](https://cloud.redis.io/) menu and then select your subscription from the list.\n\n1. Select the **Connectivity** tab and then **Private Service Connect**.\n\n1. For each endpoint:\n\n    1. Select the **Delete PSC endpoint** button:\n\n        ![Use the Delete PSC endpoint button to remove an endpoint.](https://redis.io/docs/latest/images/rc/icon-delete-teal.png)\n\n    1. Copy the provided `gcloud` script from the **Remove endpoint** dialog.\n\n    1. Run the `gcloud` script with [`gcloud` CLI](https://cloud.google.com/sdk/gcloud) to delete the endpoint and DNS response policy.\n\n1. After you remove all endpoints, select the **Actions** button to see a list of available actions:\n\n    ![Use the Toggle actions button to see a list of actions.](https://redis.io/docs/latest/images/rc/icon-psc-actions.png)\n\n1. Select **Remove service** and then **Confirm**:\n\n    ![Use the Confirm button to deactivate Private Service Connect.](https://redis.io/docs/latest/images/rc/button-subscription-connectivity-psc-remove-service-confirm.png)\n\nOnce you remove all of your Private Service Connect endpoints and deactivate it, you must migrate any connections from a Private Service Connect endpoint to the public or private endpoint of your database."},{"id":"limitations","title":"Limitations","role":"limits","text":"Private Service Connect has the following limitations in Redis Cloud:\n\n- Although Redis Cloud supports using Private Service Connect with Enterprise clustering, you cannot use the [Cluster API](https://redis.io/docs/latest/operate/rc/databases/create-database#oss-cluster-api) with Private Service Connect enabled.\n- The pre-handoff feature of [Smart client handoffs](https://redis.io/docs/latest/develop/clients/sch#redis-cloud) is not currently supported with Private Service Connect, but relaxed timeouts are available and enabled by default."},{"id":"continue-learning-with-redis-university","title":"Continue learning with Redis University","role":"content","text":""}],"examples":[]}
{"schema_version":2,"id":"operate/rc/security/shared-responsibility-model","title":"Redis Cloud shared responsibility model","url":"https://redis.io/docs/latest/operate/rc/security/shared-responsibility-model/","summary":"","tags":["docs","operate","rc"],"last_updated":"2025-02-12T16:03:01+01:00","page_type":"content","content_hash":"8f44a6b80d3f8a90ca55d808a4ef94b2eafc52912590b1b679f1c9de33d6c71c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The security of all Redis Cloud deployments is a shared responsibility. Redis, the public cloud providers (Amazon Web Services [AWS], Google Cloud, and Microsoft Azure), and our customers all help ensure the security of these deployments."},{"id":"redis-responsibility","title":"Redis responsibility","role":"content","text":"Redis Cloud's offerings are managed by Redis and deployed on AWS, Azure, and Google Cloud infrastructure.\n\nRedis is responsible for the software that runs Redis Cloud. This includes the patching and maintenance of\nthe operating systems that Redis is deployed on as well as the patching and maintenance of Redis Cloud."},{"id":"cloud-provider-responsibility","title":"Cloud provider responsibility","role":"content","text":"The public cloud provider hosting your Redis Cloud databases is responsible for the physical security of their data centers and\nthe security of the network, storage, servers, and virtualization that form the core infrastructure of your deployment.\n\nAmazon, Microsoft, and Google public clouds embrace a wide range of security best practices and compliance standards. Compliance information—including audits, attestations, and certifications about resources hosted—can be found on these compliance pages:\n\n* [AWS Compliance](https://aws.amazon.com/compliance/)\n* [Google Cloud Compliance](https://cloud.google.com/security/compliance)\n* [Azure Compliance](https://azure.microsoft.com/en-us/overview/trusted-cloud/compliance/)"},{"id":"customer-responsibility","title":"Customer responsibility","role":"content","text":"Customers are responsible for the security configurations in their Redis databases and the Redis Cloud console. Customers must understand and implement the Redis Cloud security features and best practices.\n\nCustomers are also responsible for the applications built on Redis and the data they store in Redis. Customers determine the cloud provider, region, and availability zone of their deployments.\n\nCustomers understand that Redis Cloud Essentials plans are deployed to a multi-tenant infrastructure. Redis Cloud Pro plans are deployed to a single-tenant infrastructure dedicated to one specific customer."},{"id":"continue-learning-with-redis-university","title":"Continue learning with Redis University","role":"content","text":""}],"examples":[]}
{"schema_version":2,"id":"operate/rc/security/vpc-peering","title":"Enable VPC peering","url":"https://redis.io/docs/latest/operate/rc/security/vpc-peering/","summary":"VPC peering uses private IP addresses to route traffic between a Redis Cloud VPC and an application VPC.","tags":["docs","operate","rc"],"last_updated":"2026-10-01T14:41:35+01:00","page_type":"content","content_hash":"fc4e50f5bdb9d9cd4e250a020e79e4fedc6ea7300766e69c0455ef250ec60516","sections":[{"id":"overview","title":"Overview","role":"overview","text":"VPC peering uses private IP addresses to allow network connections between two [virtual private clouds](https://en.wikipedia.org/wiki/Virtual_private_cloud) (VPCs).\n\nYou can connect your VPC in the Redis Cloud subscription to the VPC of your application. This lets your application connect securely to your Redis Cloud database using VPC peering to optimize the performance of your application.\n\n> [!NOTE]\n> VPC peering is available only with Redis Cloud Pro.  It is not supported for Redis Cloud Essentials.\n\nVPC peering configuration requires you to initiate VPC peering on your Redis Cloud subscription and then accept the VPC peering request for the AWS VPC that you want to peer with."},{"id":"aws-vpc-peering","title":"AWS VPC peering","role":"content","text":"If you want to peer a Redis Cloud VPC with an AWS VPC, you need to:\n\n1. [Configure and initiate VPC peering](#config-aws-vpc-peering) for your Redis Cloud subscription.\n\n1. [Approve the VPC peering request](#approve-aws-vpc-peering).\n\n1. [Update the routes tables](#update-route-tables)."},{"id":"config-aws-vpc-peering","title":"Configure VPC peering","role":"configuration","text":"To set up VPC peering:\n\n1. From the [Redis Cloud console](https://cloud.redis.io/), select the **Subscriptions** menu and then select your subscription from the list.\n\n1. Select **Connectivity > VPC Peering**.\n\n1. Select **Add peering**.\n\n1. Enter **VPC peering** details:\n\n    | Setting | Description |\n    |-------------------|-------------|\n    | _Consumer AWS Account_ | Your AWS account ID (see [Finding your AWS account ID](https://docs.aws.amazon.com/IAM/latest/UserGuide/console_account-alias.html#FindingYourAWSId)) |\n    | _Consumer region_ | AWS VPC region |\n    | _Consumer VPC ID_ | The VPC ID for the application that needs to access your Redis Cloud VPC (see [Finding a VPC ID](https://docs.aws.amazon.com/managedservices/latest/userguide/find-vpc.html)) |\n    | _Consumer VPC CIDRs_ | [CIDR-formatted](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing) IP addresses for the AWS VPC that needs to access your Redis Cloud VPC; must not overlap with the _Redis producer VPC CIDR_ (see [View your VPCs](https://docs.aws.amazon.com/vpc/latest/userguide/working-with-vpcs.html#view-vpc)) |\n\n1. You can provide up to five VPC CIDRs.\n    \n    To add multiple VPC CIDRs:\n\n    1. Select **Add CIDR**.\n    1. Enter additional CIDR-formatted IP address in the box:\n\n        ![Select the Add CIDR button to add another VPC CIDR.](https://redis.io/docs/latest/images/rc/subscription-connectivity-vpc-peering-add-multiple-cidrs.png)\n\n1. Select **Initiate peering**.\n1. Note the **Peering ID** of the VPC peering request.\n\n    ![View VPC peering list.](https://redis.io/docs/latest/images/rc/subscription-connectivity-vpc-peering-aws.png)"},{"id":"approve-aws-vpc-peering","title":"Approve VPC peering request","role":"content","text":"After you set up and intitiate VPC peering, you need to approve the VPC peering request:\n\n1. Follow the AWS guide to [accept the VPC peering connection](https://docs.aws.amazon.com/vpc/latest/peering/accept-vpc-peering-connection.html).\n\n1. After you accept the peering request, select **Modify my route tables now**. This button can be found in the green header once the peering connection is **active**.\n\n![Modify Route Table.](https://redis.io/docs/latest/images/rc/modify_route_tables_aws.png)"},{"id":"update-route-tables","title":"Update route tables","role":"content","text":"To finish VPC peering setup, [update your route tables for the peering connection](https://docs.aws.amazon.com/vpc/latest/peering/vpc-peering-routing.html). There may be many different route tables assigned to different VPCs. It is important to choose the route table assigned to the VPC you specified in the [Configure VPC Peering](#config-aws-vpc-peering) section with the following details:\n\n1. In the **Destination** field, enter the Redis Cloud VPC CIDR shown when you accepted the peering request.\n\n    This is the Redis Cloud VPC CIDR address, to which your application's VPC connects.\n\n1. In the **Target** field, select **Peering Connection** and select the relevant **Peering ID**.\n\nOnce VPC peering is established, we recommend switching your application connection string to the private endpoint.\n\n> [!NOTE]\n> If you've enabled the database's [CIDR allow list](https://redis.io/docs/latest/operate/rc/security/cidr-whitelist), you must also [add the VPC peered IP addresses to the CIDR allow list](https://redis.io/docs/latest/operate/rc/security/cidr-whitelist#define-cidr-allow-list) to connect to the database via the private endpoint."},{"id":"gcp-vpc-peering","title":"Google Cloud VPC peering","role":"content","text":"If you want to peer a Redis Cloud VPC with a Google Cloud VPC, you need to:\n\n1. [Configure and initiate VPC peering](#config-gcp-vpc-peering) for your Redis Cloud subscription.\n\n1. [Approve the VPC peering request](#approve-gcp-vpc-peering)."},{"id":"config-gcp-vpc-peering","title":"Configure VPC peering","role":"configuration","text":"To set up VPC peering:\n\n1. Select **Subscriptions** from the [Redis Cloud console](https://cloud.redis.io/) menu and then select your subscription from the list.\n\n1. Select **Connectivity > VPC Peering**.\n\n1. Select **Add peering**.\n\n1. Enter the VPC peering details:\n\n    | Setting&nbsp;name | Description |\n    |-------------------|-------------|\n    | **Project ID** | Google Cloud project ID (see [Identifying projects](https://cloud.google.com/resource-manager/docs/creating-managing-projects#identifying_projects)) |\n    | **Network name** | Google Cloud VPC network you want to peer with (see [View networks](https://cloud.google.com/vpc/docs/create-modify-vpc-networks#viewing-networks)) |\n\n1. Copy the **Google cloud command** after you enter the other VPC peering settings. You need this command to accept the peering request later:\n\n    ![The Initiate peering button creates a VPC peering request.](https://redis.io/docs/latest/images/rc/subscription-connectivity-vpc-peering-gcloud-command.png)\n\n1. Select **Initiate peering**.\n1. Note the **Cloud peering ID** of the VPC peering request.\n\n    ![View VPC peering list.](https://redis.io/docs/latest/images/rc/subscription-connectivity-vpc-peering-gcp.png)\n\n> [!NOTE]\n> Redis Cloud enables **Import custom routes** by default on its side of the peering connection. If you want Redis Cloud to import custom routes from your application VPC (for example, to reach Redis Cloud from other networks connected to your application VPC), you must also enable **Export custom routes** on your application VPC. Otherwise, custom routes from your application VPC aren't imported and you may experience connectivity issues."},{"id":"approve-gcp-vpc-peering","title":"Approve VPC peering request","role":"content","text":"To approve the VPC peering request between Redis Cloud and Google Cloud, use the [`gcloud` CLI](https://cloud.google.com/sdk/gcloud) to run the **Google cloud command** that you copied before you initiated VPC peering.\n\nOnce VPC peering is established, we recommend switching your application connection string to the private endpoint.\n\n> [!NOTE]\n> If you've enabled the database's [CIDR allow list](https://redis.io/docs/latest/operate/rc/security/cidr-whitelist), you must also [add the VPC peered IP addresses to the CIDR allow list](https://redis.io/docs/latest/operate/rc/security/cidr-whitelist#define-cidr-allow-list) to connect to the database via the private endpoint."},{"id":"continue-learning-with-redis-university","title":"Continue learning with Redis University","role":"content","text":""}],"examples":[]}
{"schema_version":2,"id":"operate/rc/subscriptions/bring-your-own-cloud/cloud-account-settings","title":"Create and edit Cloud accounts","url":"https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud/cloud-account-settings/","summary":"","aliases":["/operate/rc/how-to/view-edit-cloud-account/cloud-account-settings","/operate/rc/cloud-accounts/cloud-account-settings","/operate/rc/cloud-integrations/aws-cloud-accounts/cloud-account-settings","/operate/rc/subscriptions/aws-cloud-accounts/cloud-account-settings/"],"tags":["docs","operate","rc"],"last_updated":"2026-10-01T15:21:44+01:00","page_type":"content","content_hash":"7fdb6dc4952b7b2a6393e88462fed60cea34828e1c6dc8a09b03dddb2df9daa1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Cloud Bring your own Cloud (BYOC) lets you use your own cloud infrastructure to deploy Redis Cloud.\n\nYou can associate your existing AWS account as a _cloud account_ for your subscription.  This requires setting up and entering credentials that enable monitoring, maintenance, and technical support of your subscription.\n\nTo do this, you need:\n\n1. A programmatic user with an access key and a secret access key for that user.\n1. A console role that allows administrative access to the cloud account.\n\nYou need to create these resources before adding the cloud account to your subscription.  To learn more, see [Create IAM resources](https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud/iam-resources).\n\n> [!WARNING]\n> After an AWS account has been configured as a cloud account, you **must not** change the configurations of provisioned resources or stop or terminate provisioned instances. If you do, your databases will be inaccessible and Redis will not be able to ensure database stability. See [Avoid service disruption](https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud#avoid-service-disruption) for more details."},{"id":"view-cloud-account-settings","title":"View cloud account settings","role":"content","text":"To create or edit a cloud account in Redis Cloud:\n\n1. Sign in to the [Redis Cloud console](https://cloud.redis.io/) and then select the target subscription.\n\n1. From the console menu, select **Account Settings** and then select the **BYOC Accounts** tab.\n\n    This displays a list of cloud accounts associated with your Redis Cloud subscription.\n\n    ![Use the Cloud Account tab of the Account Settings screen to define cloud accounts for your Redis Cloud subscription.](https://redis.io/docs/latest/images/rc/account-settings-cloud-account-tab.png)\n\nThe **Cloud account** tab lets you manage cloud accounts associated with your Redis Cloud subscription.\n\nThe **Cloud Account** tab is only available for accounts with Redis Cloud Bring your own Cloud (BYOC) subscriptions."},{"id":"add-a-new-cloud-account","title":"Add a new cloud account","role":"content","text":"To add a new cloud account to your Redis Cloud subscription, select the **Add** button from the **BYOC Accounts** tab of the Account Settings screen.\n\n![Use the Add button to add new cloud accounts to your Redis Cloud subscription.](https://redis.io/docs/latest/images/rc/icon-add.png)\n\nThis displays the **Bring your own Cloud (BYOC)** dialog.\n\n![Use the Bring your own Cloud prompt to enter the details of the cloud account.](https://redis.io/docs/latest/images/rc/account-settings-prompt-add-cloud-account.png)\n\nEach of the following fields are required.\n\n|Setting|Description|\n|-------|-----------|\n| _Account name_ | A descriptive name for your cloud account settings |\n| _AWS&nbsp;access&nbsp;key_ | The AWS access key for the programmatic user created to support your cloud account settings |\n| _AWS&nbsp;secret&nbsp;key_ | The AWS secret key for the programmatic user created to support your cloud account settings |\n| _IAM role name_ | The name of the AWS console role with access to the AWS console |\n\nUse the **Add account** button to save your cloud account details.\n\n![Use the Add account button to save the details of your new cloud account.](https://redis.io/docs/latest/images/rc/button-cloud-account-add.png)\n\nBe sure to create the resources before adding the cloud account to your subscription, as they're used to verify access to the cloud account.  The details can be saved only after access is verified.\n\nWhen problems occur, an information icon appears and the field is highlighted in red.  When this happens, the icon includes a tooltip that explains the issue.\n\nIf the **Add account** button is inactive, verify that:\n\n- You've specified all field values correctly\n- The resources exist in your AWS account\n- Each resource provides the required level of access\n\nFor help, see [Create IAM resources](https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud/iam-resources)."},{"id":"edit-cloud-account-details","title":"Edit cloud account details","role":"content","text":"To update the details of a cloud account associated with your Redis Cloud subscription, select the cloud account from the **Cloud account** tab and then select the **Edit** button.\n\n![Use the Edit button to update cloud account details.](https://redis.io/docs/latest/images/rc/icon-edit.png)\n\nThis displays the **Edit cloud account** dialog:\n\n![Use the Edit cloud account prompt to update the details of the cloud account.](https://redis.io/docs/latest/images/rc/account-settings-prompt-edit-cloud-account.png)\n\n|Setting|Description|\n|-------|-----------|\n| _Account name_ | A descriptive name for your cloud account settings |\n| _AWS access key_ | The AWS access key for the programmatic user created to support your cloud account settings |\n| _AWS secret key_ | The AWS secret key for the programmatic user created to support your cloud account settings |\n| _IAM role name_ | The name of the AWS console role with access to the AWS console |\n\nUse the **Update account** button to save your changes.\n\n![Use the Update account button to save the updated cloud account details.](https://redis.io/docs/latest/images/rc/button-cloud-account-update.png)"},{"id":"delete-cloud-account","title":"Delete cloud account","role":"content","text":"To remove a cloud account from your Redis cloud subscription, select the cloud account from the **Cloud account** tab and then select the **Delete** button.\n\n![Use the Delete button to remove cloud account details.](https://redis.io/docs/latest/images/rc/icon-delete-lb.png)"},{"id":"dedicated-iam-resources","title":"Dedicated IAM resources","role":"content","text":"We strongly recommend using dedicated identity and access management (IAM) resources to manage your AWS cloud accounts.  These resources should not be shared with any other task, account, or process.\n\nTo learn more, see [Create IAM resources for AWS cloud accounts](https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud/iam-resources)."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/subscriptions/bring-your-own-cloud/iam-resources/aws-console","title":"Create IAM resources using AWS console","url":"https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud/iam-resources/aws-console/","summary":"","aliases":["/operate/rc/how-to/view-edit-cloud-account/iam-resources/aws-console","/operate/rc/cloud-accounts/iam-resources/aws-console","/operate/rc/cloud-integrations/aws-cloud-accounts/iam-resources/aws-console","/operate/rc/subscriptions/aws-cloud-accounts/iam-resources/aws-console/"],"tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:18+01:00","page_type":"content","content_hash":"b28f2f15b04b5f61d9b5f9cd05ae8e31153542edb0bbe92c30a73fa276ef759e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Follow these steps to manually create IAM resources using the [AWS console](https://console.aws.amazon.com/).\n\n> [!WARNING]\n> We use the provided credentials to configure your AWS environment and provision required resources.\n>\n> You **must not** change the configurations of provisioned resources or stop or terminate provisioned instances. If you do, your databases will be inaccessible and Redis will not be able to ensure database stability. See [Avoid service disruption](https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud/cloud-account-settings#avoid-service-disruption) for more details."},{"id":"step-1-create-the-iam-instance-policy","title":"Step 1: Create the IAM instance policy","role":"content","text":"The IAM instance policy controls the permissions for the instances that Redis Cloud creates in your AWS account.\n\nFollow the steps to [create an IAM policy using the JSON editor](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_create-console.html#access_policies_create-json-editor) with the following settings:\n\n- In **Specify permissions**, select **JSON**, and then enter the contents of the RedisLabsInstanceRolePolicy.json policy file:\n\n    \n\n    \n\n- In **Review and Create**, enter `RedisLabsInstanceRolePolicy` in the **Policy name** field.\n\nSelect **Create policy** to finish policy creation."},{"id":"step-2-create-the-service-role","title":"Step 2: Create the service role","role":"content","text":"After creating the instance role policy, you must create a role to assign the policy.\n\nFollow the steps to [create a role for an AWS service](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-service.html#roles-creatingrole-service-console) with the following settings:\n\n- In **Select trusted entity**:\n    - **Trusted entity**: Select **AWS service**.\n    - **Service or use case**: Select **EC2**.\n    - **Use case**: Select **EC2**.\n- In **Add permissions**, select the **RedisLabsInstanceRolePolicy** you created.\n- In **Name, review, and create**, enter `redislabs-cluster-node-role` in the **Role name** field.\n\nSelect **Create role** to finish role creation."},{"id":"step-3-create-the-user-policy","title":"Step 3: Create the user policy","role":"content","text":"The user policy controls the permissions for the user that Redis Cloud uses to manage your AWS account.\n\nFollow the steps to [create an IAM policy using the JSON editor](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_create-console.html#access_policies_create-json-editor) with the following settings:\n\n- In **Specify permissions**, select **JSON**, and then enter the contents of the RedisLabsIAMUserRestrictedPolicy.json policy file:\n\n    \n\n    \n\n- In **Review and Create**, enter `RedislabsIAMUserRestrictedPolicy` in the **Policy name** field.\n\nSelect **Create policy** to finish policy creation."},{"id":"step-4-create-the-programmatic-access-user","title":"Step 4: Create the programmatic access user","role":"content","text":"After you create the user policy, you must create a programmatic access user and attach the policy to it.\n\nFollow the steps to [create a user on the AWS console](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html), with the following settings:\n\n- In **Specify user details**, For **User name**, enter `redislabs-user`.\n- In **Set permissions**:\n    - **Permissions options**: Select **Attach existing policies directly**.\n    - **Permissions policies**: Select the **RedislabsIAMUserRestrictedPolicy** you created from the list.\n\nSelect **Create user** to create the user.\n\nAfter you create the user, you need to add an access key for the user.\n\nFollow the steps to [create an access key](https://docs.aws.amazon.com/IAM/latest/UserGuide/access-keys-admin-managed.html#admin-create-access-key) for the user you just created. Save the access key ID and secret access key in a secure location."},{"id":"step-5-create-the-console-access-role","title":"Step 5: Create the console access role","role":"content","text":"The console access role controls the permissions for the user that Redis Cloud uses to access the AWS console.\n\nFollow the steps to [Create a role for an IAM user](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user.html) with the following settings:\n\n- In **Select trusted entity**:\n    - **Trusted entity**: Select **AWS account**.\n    - **An AWS account**: Select **Another AWS account**.\n    - **Account ID**: Enter account number `168085023892` (Redis Cloud's AWS account).\n    - **Options**: Select **Require MFA**.\n    > [!WARNING]\n    > Do not check the **Require external ID** checkbox.\n    >\n- In **Add permissions**, select the **RedislabsIAMUserRestrictedPolicy** you created.\n- In **Name, review, and create**, enter `redislabs-role` in the **Role name** field.\n\nSelect **Create role** to finish role creation. Save the Role name for later."},{"id":"next-steps","title":"Next steps","role":"content","text":"When you've finished creating all of the resources, you can [create a Cloud Account](https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud/cloud-account-settings) in the Redis Cloud console. To do this, you'll need the following information:\n\n- **Access Key ID**: The Access Key ID for the [programmatic user you created](#step-4-create-the-programmatic-access-user).\n- **Secret Access Key**: The Secret Access Key for the [programmatic user you created](#step-4-create-the-programmatic-access-user).\n- **IAM Role Name**: The name of the [console access role you created](#step-5-create-the-console-access-role)."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/subscriptions/bring-your-own-cloud/iam-resources/cloudformation","title":"Create IAM resources using CloudFormation","url":"https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud/iam-resources/cloudformation/","summary":"","aliases":["/operate/rc/how-to/view-edit-cloud-account/iam-resources/cloudformation","/operate/rc/cloud-accounts/iam-resources/cloudformation","/operate/rc/cloud-integrations/aws-cloud-accounts/iam-resources/cloudformation","/operate/rc/subscriptions/aws-cloud-accounts/iam-resources/cloudformation/"],"tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:18+01:00","page_type":"content","content_hash":"85c853b35b6ec142869f28e790f1c46d3544ca39f2bf9e39271cc541125d142f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can use [AWS CloudFormation](https://aws.amazon.com/cloudformation/) to create the IAM resources for Redis Cloud Bring your Own Cloud (BYOC).\n\n> [!WARNING]\n> We use the provided credentials to configure your AWS environment and provision required resources.\n>\n> You **must not** change the configurations of provisioned resources or stop or terminate provisioned instances. If you do, your databases will be inaccessible and Redis will not be able to ensure database stability. See [Avoid service disruption](https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud/cloud-account-settings#avoid-service-disruption) for more details."},{"id":"create-resources-using-cloudformation-on-the-aws-console","title":"Create resources using CloudFormation on the AWS Console","role":"content","text":"The following link uses CloudFormation to create a stack using the AWS console:\n\n<a href=\"https://console.aws.amazon.com/cloudformation/home?#/stacks/new?stackName=RedisCloud&templateURL=https://s3.amazonaws.com/iam-resource-automation-do-not-delete/RedisCloud.yaml\">\n<img alt=\"Launch RedisCloud template\" src=\"https://s3.amazonaws.com/cloudformation-examples/cloudformation-launch-stack.png\"/>\n</a>\n\nWhen the stack finishes, select the stack and then the **Outputs** tab. You need the following information to [create a Cloud Account](https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud/cloud-account-settings) in the Redis Cloud console:\n\n- **Access Key ID**: The `accessKeyId` output.\n- **Secret Access Key**: Follow the link to AWS Secrets Manager in the `accessSecretKey` output and select **Retrieve secret value**. \n- **IAM Role Name**: The `IAMRoleName` output."},{"id":"create-resources-using-cloudformation-through-aws-cli","title":"Create resources using CloudFormation through AWS CLI","role":"content","text":"You can also use the AWS command-line interface (CLI) to create the stack:\n\n[code example]\n\nYou can track the status of the cloud formation with the following command:\n\n[code example]\n\nAfter the stack finishes, you can retrieve the outputs from the `Outputs` section of the response.\n\nYou need the following information to [create a Cloud Account](https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud/cloud-account-settings) in the Redis Cloud console:\n\n- **Access Key ID**: The `accessKeyId` output.\n- **Secret Access Key**: Extract the secret ID from the `accessSecretKey` output. The secret ID is the `name` query parameter in the `accessSecretKey` output. \n\n    For example, if the `accessSecretKey` output is `https://console.aws.amazon.com/secretsmanager/home?region=<region>/secret?name=/redislabsuser/secret_access_key`, then the secret ID is `/redislabsuser/secret_access_key`.\n\n    Use the secret ID to retrieve the secret value using the AWS secretsmanager CLI:\n\n    [code example]\n    Replace `<accessSecretKey-ID>` with the secret ID you extracted from the `accessSecretKey` output.\n- **IAM Role Name**: The `IAMRoleName` output."}],"examples":[{"id":"create-resources-using-cloudformation-through-aws-cli-ex0","language":" shell","code":"aws cloudformation create-stack --stack-name RedisCloud --template-url \\\nhttps://s3.amazonaws.com/iam-resource-automation-do-not-delete/RedisCloud.yaml \\\n--capabilities CAPABILITY_AUTO_EXPAND CAPABILITY_NAMED_IAM CAPABILITY_IAM","section_id":"create-resources-using-cloudformation-through-aws-cli"},{"id":"create-resources-using-cloudformation-through-aws-cli-ex1","language":"shell","code":"aws cloudformation describe-stacks --stack-name RedisCloud","section_id":"create-resources-using-cloudformation-through-aws-cli"},{"id":"create-resources-using-cloudformation-through-aws-cli-ex2","language":" shell","code":"aws secretsmanager get-secret-value --secret-id=<accessSecretKey-ID>","section_id":"create-resources-using-cloudformation-through-aws-cli"}]}
{"schema_version":2,"id":"operate/rc/subscriptions/bring-your-own-cloud/iam-resources","title":"Create IAM resources for AWS cloud accounts","url":"https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud/iam-resources/","summary":"","aliases":["/operate/rc/how-to/view-edit-cloud-account/iam-resources","/operate/rc/cloud-accounts/iam-resources","/operate/rc/cloud-integrations/aws-cloud-accounts/iam-resources","/operate/rc/subscriptions/aws-cloud-accounts/iam-resources/"],"tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:18+01:00","children":[{"id":"operate/rc/subscriptions/bring-your-own-cloud/iam-resources/aws-console","summary":"","title":"Create IAM resources using AWS console","url":"https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud/iam-resources/aws-console/"},{"id":"operate/rc/subscriptions/bring-your-own-cloud/iam-resources/cloudformation","summary":"","title":"Create IAM resources using CloudFormation","url":"https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud/iam-resources/cloudformation/"},{"id":"operate/rc/subscriptions/bring-your-own-cloud/iam-resources/terraform","summary":"","title":"Create IAM resources using Terraform","url":"https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud/iam-resources/terraform/"}],"page_type":"content","content_hash":"bdbc97c6f56861f6808f3fd2b23eb2e2f4ce580ab1b2d7f7b518171e8b377960","sections":[{"id":"content","title":"Content","role":"content","text":"For Redis Cloud Bring your Own Cloud (BYOC) on Amazon Web Services (AWS), we manage the supporting infrastructure for you in dedicated AWS accounts.\n\nYou can manage this infrastructure with your own AWS accounts.  \n\nYou'll want these accounts to be separate from any AWS application accounts \nand you'll need to create dedicated [identity and access management](https://docs.aws.amazon.com/IAM/latest/UserGuide/introduction.html) (IAM) resources to allow us to manage the infrastructure.\n\nIn the new AWS account, you need to create:\n\n- An **instance role**\n- A user with an **access key**\n- A role that grants **AWS console access**\n\nSave the access key in a secure location so that you can enter it when you [register the cloud account](https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud/cloud-account-settings) with your Redis Cloud subscription.\n\n> [!WARNING]\n> We use the provided credentials to configure your AWS environment and provision required resources.\n>\n> You **must not** change the configurations of provisioned resources or stop or terminate provisioned instances. If you do, your databases will be inaccessible and Redis will not be able to ensure database stability. See [Avoid service disruption](https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud/cloud-account-settings#avoid-service-disruption) for more details.\n\nFor help creating an AWS user, see the [AWS IAM documentation](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html).\n\nYou can use one of the following tools to create IAM resources:\n\n- [CloudFormation](https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud/iam-resources/cloudformation)\n- [Terraform](https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud/iam-resources/terraform)\n- The [AWS Console](https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud/iam-resources/aws-console)"}],"examples":[]}
{"schema_version":2,"id":"operate/rc/subscriptions/bring-your-own-cloud/iam-resources/terraform","title":"Create IAM resources using Terraform","url":"https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud/iam-resources/terraform/","summary":"","aliases":["/operate/rc/how-to/view-edit-cloud-account/iam-resources/terraform","/operate/rc/cloud-accounts/iam-resources/terraform","/operate/rc/cloud-integrations/aws-cloud-accounts/iam-resources/terraform","/operate/rc/subscriptions/aws-cloud-accounts/iam-resources/terraform/"],"tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:18+01:00","page_type":"content","content_hash":"bca9d890668f2a1b02ded9e260602da98df907d62d457fa431cdc5ef552cfb96","sections":[{"id":"content","title":"Content","role":"content","text":"You can use [HashiCorp Terraform](https://www.terraform.io/intro/index.html) to create identity and access management (IAM) resources to support AWS cloud account access to Redis Cloud subscriptions.\n\n> [!WARNING]\n> We use the provided credentials to configure your AWS environment and provision required resources.\n>\n> You **must not** change the configurations of provisioned resources or stop or terminate provisioned instances. If you do, your databases will be inaccessible and Redis will not be able to ensure database stability. See [Avoid service disruption](https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud/cloud-account-settings#avoid-service-disruption) for more details.\n\nThe following example uses the `terraform-aws-Redislabs-Cloud-Account-IAM-Resources` module, located in Amazon&nbsp;S3:\n\n\n1. Copy the following code into a file called `main.tf`.\n\n    \n\n    \n\n    Replace the following values in the `main.tf` file:\n\n    - `<profile>`: The AWS CLI profile to use.\n    - `<region>`: The AWS region to use.\n    - `<pgp_key>`: The PGP key to use. For details, see the [Terraform docs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_user_login_profile#pgp_key).\n\n2. Initialize Terraform with the module:\n\n    Note: Terraform requires [AWS credentials be supplied](https://www.terraform.io/docs/language/modules/sources.html#s3-bucket), but the source of the module is a public S3 bucket, so any valid credentials should work. \n\n    [code example]\n\n    Replace `<access_key_id>` and `<secret_key>` with valid AWS keys.\n\n3. Build the resources and display the outputs:\n\n    [code example]\n\n    You need the following information to [create a Cloud Account](https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud/cloud-account-settings) in the Redis Cloud console:\n\n    - **Access Key ID**: The `accessKeyId` output.\n    - **Secret Access Key**: Run the following command to extract the secret key from the `accessSecretKey` output:\n        [code example]\n    - **IAM Role Name**: The `IAMRoleName` output."}],"examples":[{"id":"content-ex0","language":"plaintext","code":"AWS_ACCESS_KEY_ID=<access_key_id> AWS_SECRET_KEY=<secret_key> terraform init","section_id":"content"},{"id":"content-ex1","language":"plaintext","code":"terraform apply","section_id":"content"},{"id":"content-ex2","language":" shell","code":"echo $(terraform output -raw accessSecretKey)","section_id":"content"}]}
{"schema_version":2,"id":"operate/rc/subscriptions/bring-your-own-cloud","title":"Redis Cloud Bring your own Cloud","url":"https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud/","summary":"Describes how to provision your Redis Cloud subscription to use an existing cloud provider account.","aliases":["/operate/rc/how-to/view-edit-cloud-account","/operate/rc/cloud-accounts","/operate/rc/cloud-integrations/aws-cloud-accounts"],"tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:18+01:00","children":[{"id":"operate/rc/subscriptions/bring-your-own-cloud/iam-resources","summary":"","title":"Create IAM resources for AWS cloud accounts","url":"https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud/iam-resources/"},{"id":"operate/rc/subscriptions/bring-your-own-cloud/cloud-account-settings","summary":"","title":"Create and edit Cloud accounts","url":"https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud/cloud-account-settings/"},{"id":"operate/rc/subscriptions/bring-your-own-cloud/resource-tags","summary":"Describes how to apply resource tags to Redis Cloud Bring your own Cloud (BYOC) resources.","title":"Manage Cloud Provider resource tags","url":"https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud/resource-tags/"},{"id":"operate/rc/subscriptions/bring-your-own-cloud/subscription-whitelist","summary":"The CIDR allow list permits traffic between a range of IP addresses and the Redis Cloud VPC.","title":"Configure subscription CIDR allow list","url":"https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud/subscription-whitelist/"}],"page_type":"content","content_hash":"9a8d89d13b23266b7de4784dc42acf5067602c75ae4a6a58410265f65addacee","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Cloud Bring your own Cloud (BYOC) allows organizations to use their own cloud infrastructure to deploy Redis Cloud.\n\nBy default, Redis hosts the infrastructure that runs Redis Cloud. However, if your organization has specific requirements, you may need to have more control over your data residency or security options. Redis Cloud BYOC lets you fulfill these requirements while still having a fully managed service experience of Redis Cloud. Like standard Redis Cloud, Redis manages deployment, scaling, monitoring, and support, so you can focus on building applications while Redis manages the backend.\n\nRedis Cloud BYOC availability requires approval from Redis. If you're interested, [contact sales](https://redis.io/meeting/)."},{"id":"benefits-of-redis-cloud-byoc","title":"Benefits of Redis Cloud BYOC","role":"content","text":"Redis Cloud BYOC has some benefits over standard Redis Cloud:\n\n- **Full Data Control**: All data stays within your environment, giving you complete control over data security and reducing risks associated with data sovereignty.\n- **Simplified Compliance**: BYOC simplifies adherence to regulatory standards like GDPR, HIPAA, and compliance frameworks like SOC 2 and PCI DSS since data remains within your own cloud account.\n- **Maximize Cloud Commitments**: Infrastructure costs are billed directly through your cloud provider account, enabling you to fully utilize existing commitments.\n- **Separation of Costs**: Customers pay Redis only for licensing and management, while infrastructure costs go directly to the cloud provider, offering clear visibility into each cost stream.\n- **Instant Access Control**: You can revoke Redis’ access to your environment at any time, offering complete control over data and infrastructure."},{"id":"redis-cloud-byoc-use-cases","title":"Redis Cloud BYOC Use cases","role":"content","text":"Consider using Redis Cloud BYOC if you have specific organizational needs or requirements, such as:\n\n- **Data Residency and Compliance**: Organizations operating in heavily regulated industries (such as finance, healthcare, and government) can use Redis Cloud BYOC to meet strict data residency and sovereignty requirements, ensuring sensitive data remains within their controlled environments.\n- **Cloud spend optimization**: Companies with significant cloud provider commitments can use Redis Cloud BYOC to optimize their cloud spend. Since infrastructure costs are billed directly through the cloud provider, customers can fully utilize existing cloud provider discounts, improving cost efficiency.\n- **Enhanced Security and Control**: Redis Cloud BYOC is ideal for organizations that require a higher degree of security and control over their data. By keeping data within your own cloud account, you can use existing security policies, tools, and procedures in your cloud’s security infrastructure. In this way, you can minimize external access to your data, and adhere to more strict organizational security guidelines."},{"id":"set-up-redis-cloud-byoc","title":"Set up Redis Cloud BYOC","role":"content","text":"To set up Redis Cloud BYOC on Amazon Web Services (AWS):\n\n1. [Create the Identity and Access Management (IAM) resources](https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud/iam-resources) needed to let Redis create, monitor, and maintain deployment infrastructure.\n\n1. [Set your AWS Account as a BYOC Cloud account](https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud/cloud-account-settings) on Redis Cloud.\n\nAfter you set up your cloud account, you can create a [new Redis Cloud Pro subscription](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new) or an [Active-Active database](https://redis.io/docs/latest/operate/rc/databases/active-active/create-active-active-database) that uses your cloud account."},{"id":"redis-cloud-byoc-pricing","title":"Redis Cloud BYOC pricing","role":"content","text":"With Redis Cloud BYOC, you pay for the Redis Cloud service, including database management, monitoring, and support.  You also pay for the underlying cloud infrastructure, including virtual machines, storage, and networking, directly to your cloud provider. \n\nWhen you create a database, you'll see the costs for the Redis Cloud service, but not the costs for the underlying cloud infrastructure. You can view and manage those costs in your cloud provider account."},{"id":"avoid-service-disruption","title":"Avoid Redis Cloud BYOC service disruption","role":"content","text":"> [!WARNING]\n> Failing to follow the instructions in this section will cause your databases to become inaccessible, and Redis will not be able to ensure cluster and database stability. Please read them carefully.\n\nTo keep your Redis Cloud BYOC service and all databases running without issues:\n- Do not stop, delete, or modify Redis-managed resources through the cloud provider - including VPCs, subnets, virtual machines, and other infrastructure.\n- Do not change roles or access policies.\n\nIf you make any infrastructure changes, whether or not they are listed above, it may violate our SLA commitments. See [Appendix 1 in our Terms of Service](https://redis.io/legal/cloud-tos/#Appendix-1) for more details."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/subscriptions/bring-your-own-cloud/resource-tags","title":"Manage Cloud Provider resource tags","url":"https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud/resource-tags/","summary":"Describes how to apply resource tags to Redis Cloud Bring your own Cloud (BYOC) resources.","tags":["docs","operate","rc"],"last_updated":"2026-10-01T15:21:44+01:00","page_type":"content","content_hash":"b11df70789e63a2b99be3492e22dabf23cf659523e9d519d98c14870f149437d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"For Redis Cloud [Bring Your Own Cloud (BYOC)](https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud) deployments, Redis Cloud provisions infrastructure directly within your Cloud Provider account. Many organizations enforce tagging policies for cost allocation, governance, and compliance. In some environments, resource creation may fail if required tags are not present at creation time. \n\nTo address this, Redis Cloud allows you to define resource tags at the subscription level, ensuring consistent tagging across all infrastructure resources created under that subscription.\n\nYou can define key-value tags for your Redis Cloud BYOC resources [during database creation](#new-subscription) or by [updating an existing subscription](#existing-subscription). These tags are automatically applied to supported Cloud Provider resources and you can manage them centrally at the subscription level."},{"id":"supported-resources","title":"Supported resources","role":"compatibility","text":"BYOC Resource tags are applied to the following AWS resources:\n- EC2 instances\n- EBS volumes\n- EBS snapshots\n- VPCs\n- Subnets\n- Route tables\n- Internet Gateways\n- Security Groups\n- SSH key pairs\n- Elastic Network Interfaces (ENIs)\n- VPC peering connections\n- Transit Gateway attachments\n- Resource Gateways\n- Resource Configurations"},{"id":"new-subscription","title":"Add resource tags to a new subscription","role":"content","text":"To add resource tags when you create a new BYOC subscription:\n\n1. [Create a new Pro subscription](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new) and select **Custom settings**.\n\n1. On the **Setup** step, go to **Advanced options > Deployment account** to see the **AWS Resource tags** section.\n\n1. Select **Add additional tag** to add a tag. \n\n    ![The Add additional tag button.](https://redis.io/docs/latest/images/rc/tags-button-add-additional-tag.png)\n\n1. Enter a **Key** and **Value** for the tag.\n\n    ![The Add additional tag button.](https://redis.io/docs/latest/images/rc/byoc-resource-tags-add-tags-new.png)\n\n    After you add your first tag, you can:\n\n    - Select the **Key** or **Value** field of an existing tag and enter new text to edit it.\n\n    - Select **Delete** next to a tag to delete it.\n\n        ![Delete button.](https://redis.io/docs/latest/images/rc/icon-delete-lb.png)\n\n    - Select **Add additional tag** to add another tag.\n\n        ![The Add additional tag button.](https://redis.io/docs/latest/images/rc/tags-button-add-additional-tag.png)\n\n1. Continue creating your subscription.\n\nRedis Cloud applies your tags automatically to all [supported resources](#supported-resources) when they are created, ensuring that your resources will comply with all tagging policies from the start."},{"id":"existing-subscription","title":"Add resource tags to an existing subscription","role":"content","text":"You can add, edit, or remove resource tags on an existing BYOC subscription at any time.\n\n1. Sign in to the [Redis Cloud console](https://cloud.redis.io/#/) and select your subscription from the **Subscriptions** list.\n\n1. Select the **Overview** tab.\n\n1. In **General > AWS Resource tags**, select **Edit**.\n\n    ![Use the **Edit** button to edit resource tags.](https://redis.io/docs/latest/images/rc/icon-edit-subscription-name.png)\n\n    This opens the **AWS Resource tags** sidebar. \n\n    ![The AWS Resource tags sidebar.](https://redis.io/docs/latest/images/rc/byoc-resource-tags-edit-existing.png)\n\n1. From here, you can:\n\n    - Select the **Key** or **Value** field of an existing tag and enter new text to edit it.\n\n    - Select **Delete** next to a tag to delete it.\n\n        ![Delete button.](https://redis.io/docs/latest/images/rc/icon-delete-lb.png)\n\n    - Select **Add additional tag** to add another tag.\n\n        ![The Add additional tag button.](https://redis.io/docs/latest/images/rc/tags-button-add-additional-tag.png)\n\n1. Select **Save tags** to save your changes.\n\n> [!NOTE]\n> Redis Cloud applies resource tags only to resources created after you add or update the tags. Existing resources keep their previous tags."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/subscriptions/bring-your-own-cloud/subscription-whitelist","title":"Configure subscription CIDR allow list","url":"https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud/subscription-whitelist/","summary":"The CIDR allow list permits traffic between a range of IP addresses and the Redis Cloud VPC.","aliases":["/operate/rc/how-to/view-edit-cloud-account/subscription-whitelist","/operate/rc/cloud-accounts/subscription-whitelist","/operate/rc/cloud-integrations/aws-cloud-accounts/subscription-whitelist","/operate/rc/subscriptions/aws-cloud-accounts/subscription-whitelist/"],"tags":["docs","operate","rc"],"last_updated":"2026-10-02T16:06:07+01:00","page_type":"content","content_hash":"c1a733d39fdeaf8984869ccf439a7a0afcf6e024497ae184f28b4d72070dbdef","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The [CIDR](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing) [allow list](https://en.wikipedia.org/wiki/Whitelist) lets you restrict traffic to your Redis Cloud database. When you configure an allow list, only the [IP addresses](https://en.wikipedia.org/wiki/IP_address) defined in the list can connect to the database. Traffic from all other IP addresses is blocked.\n\nIf you use [Redis Cloud Bring your Own Cloud](https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud) to host your Redis Cloud deployment, you can configure a subscription-wide allow list\nto restrict traffic to all databases associated with the subscription.\n\nThe subscription CIDR allow list defines a range of IP addresses and [AWS security groups](https://docs.aws.amazon.com/managedservices/latest/userguide/about-security-groups.html) that control inbound and outbound traffic to the Redis Cloud [VPC](https://en.wikipedia.org/wiki/Virtual_private_cloud). When you add security groups to the allow list, you can also use the same security groups to manage access to your application.\n\n> [!NOTE]\n> The subscription-level allow list is available for Redis Cloud Bring your Own Cloud. If you don't have Redis Cloud Bring your Own Cloud, you can configure a [CIDR allow list](https://redis.io/docs/latest/operate/rc/security/cidr-whitelist) for each database."},{"id":"allow-ip-address-or-security-group","title":"Allow IP address or security group","role":"content","text":"To add IP addresses or [AWS security groups](https://docs.aws.amazon.com/managedservices/latest/userguide/about-security-groups.html) to a subscription's allow list:\n\n1. From the [Redis Cloud console](https://cloud.redis.io/) menu, select **Subscriptions** and then select your subscription from the list.\n\n1. Select **Connectivity > Allow List**.\n\n1. If the allow list is empty, select **Add allow list**.\n\n    ![Use the Add allow list button to create an allow list.](https://redis.io/docs/latest/images/rc/button-subscription-allow-list-add.png)\n\n1. Select an entry **Type** from the list:\n\n    ![Select the type of entry to add to the allow list from the Type list. You can choose between IP address and AWS security group.](https://redis.io/docs/latest/images/rc/subscription-connectivity-allow-list-type-dropdown.png)\n\n1. In the **Value** box, enter one of these options:\n\n    - An IP address in CIDR format\n\n    - The AWS security group ID\n\n1. Select the check to add the entry to the allow list.\n\n1. To allow additional IP addresses or security groups:\n\n    1. Select **Add** to add a new entry.\n\n    ![The Add button adds new entries to the allow list.](https://redis.io/docs/latest/images/rc/icon-add.png)\n\n    1. Select the new entry's **Type**, enter the **Value**, and select the check to add it to the allow list.\n\n        ![Define the new entry and select the Submit entry button to add it to the allow list.](https://redis.io/docs/latest/images/rc/subscription-connectivity-allow-list-add-entry.png)\n    \n    You can also select the **Edit** button to change an entry or the **Delete** button to remove it.\n\n    ![The Edit button updates an entry in the allow list.](https://redis.io/docs/latest/images/rc/icon-edit.png#no-click)&nbsp;![The Delete button removes an entry from the allow list.](https://redis.io/docs/latest/images/rc/icon-delete-lb.png#no-click)\n    \n1. Select **Apply all changes** to apply the allow list updates.\n\n    ![The Apply all changes button saves your allow list changes.](https://redis.io/docs/latest/images/rc/button-subscription-allow-list-apply-all.png)"}],"examples":[]}
{"schema_version":2,"id":"operate/rc/subscriptions/delete-subscription","title":"Delete a subscription","url":"https://redis.io/docs/latest/operate/rc/subscriptions/delete-subscription/","summary":"","tags":["docs","operate","rc"],"last_updated":"2026-10-01T15:21:44+01:00","page_type":"content","content_hash":"2b1f719484a8055eabb00da3bafae9090fd4064e94d7d17cfe4870a9e88c04d2","sections":[{"id":"content","title":"Content","role":"content","text":"> [!NOTE]\n> Once a subscription is deleted, it cannot be recovered. We recommend [backing up your data](https://redis.io/docs/latest/operate/rc/databases/back-up-data) before removing databases or subscriptions.\n\nTo delete a subscription:\n\n1.  [Delete all databases](https://redis.io/docs/latest/operate/rc/databases/delete-database) from the subscription.\n\n    The number of databases for the subscription is shown in the subscription list. You cannot delete a subscription until there are zero databases in the subscription.\n\n    ![The number of databases is shown in the bottom, left of the subscription in the subscription list.](https://redis.io/docs/latest/images/rc/subscription-list-pro-no-databases.png)\n\n2.  View the subscription details.  \n\n    If you have more than one subscription, select the target subscription from the subscription list.\n\n3.  Select the **Overview** tab.\n\n    ![The Overview tab displays the details of your subscription.](https://redis.io/docs/latest/images/rc/subscription-details-overview-flexible.png)\n\n4.  Select the **Delete subscription** button.\n\n    ![Use the Delete subscription button to delete your subscription plan.](https://redis.io/docs/latest/images/rc/button-subscription-delete.png)\n\n    ![Select the Yes, cancel button to confirm the subscription cancellation.](https://redis.io/docs/latest/images/rc/subscription-delete-confirm-dialog.png)\n\n\n5.  Select the **Delete subscription** button to confirm your choice."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/subscriptions","title":"Manage subscriptions","url":"https://redis.io/docs/latest/operate/rc/subscriptions/","summary":"","tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:18+01:00","children":[{"id":"operate/rc/subscriptions/view-essentials-subscription","summary":"","title":"View and Upgrade Redis Cloud Essentials plan","url":"https://redis.io/docs/latest/operate/rc/subscriptions/view-essentials-subscription/"},{"id":"operate/rc/subscriptions/view-pro-subscription","summary":"","title":"View and edit Redis Cloud Pro plan","url":"https://redis.io/docs/latest/operate/rc/subscriptions/view-pro-subscription/"},{"id":"operate/rc/subscriptions/upgrade-essentials-pro","summary":"Upgrade your Redis Cloud Essentials subscription to a Redis Cloud Pro subscription.","title":"Upgrade database from Redis Cloud Essentials to Redis Cloud Pro","url":"https://redis.io/docs/latest/operate/rc/subscriptions/upgrade-essentials-pro/"},{"id":"operate/rc/subscriptions/delete-subscription","summary":"","title":"Delete a subscription","url":"https://redis.io/docs/latest/operate/rc/subscriptions/delete-subscription/"},{"id":"operate/rc/subscriptions/bring-your-own-cloud","summary":"Describes how to provision your Redis Cloud subscription to use an existing cloud provider account.","title":"Redis Cloud Bring your own Cloud","url":"https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud/"},{"id":"operate/rc/subscriptions/maintenance","summary":"Describes maintenance that Redis performs on a Redis Cloud subscription.","title":"Subscription and database maintenance","url":"https://redis.io/docs/latest/operate/rc/subscriptions/maintenance/"}],"page_type":"content","content_hash":"8fb97210a4dbd3c7eb1ec2ebf098bad00009e522f6a270bc92e8707f4db0dd50","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This page helps you manage your Redis Cloud subscriptions; it briefly compares available plans and shows where to find help with common tasks."},{"id":"subscription-plans","title":"Subscription plans","role":"content","text":"As of April 2024, Redis Cloud supports the following subscription plans:\n\n- [Free Redis Cloud Essentials](#free-rce)\n- [Paid Redis Cloud Essentials](#paid-rce)\n- [Essentials with Redis Flex](#redis-flex)\n- [Redis Cloud Pro](#redis-cloud-pro)\n\nHere's a quick comparison of each plan:\n\n| Feature | Redis Cloud Essentials (free) | Redis Cloud Essentials (paid) | Essentials with Redis Flex | Redis Cloud Pro |\n|:---:|:---:|:---:|:---:|:---:|\n| Memory size | 30 MB | 250 MB-12 GB | 1 GB-100GB | 50 TB |\n| Concurrent connections | 30 | 256-10000 | 1024-10000 | No fixed database-level limit.<br/>Actual capacity depends on infrastructure, database configuration, and workload characteristics.<br/>Additional connections may be limited when infrastructure capacity is reached to preserve service stability. |\n| Security | Role-based auth<br/>Password protection<br/>Encryption in transit | Role-based auth<br/>Password protection<br/>TLS auth<br/>Encryption in transit | Role-based auth<br/>Password protection<br/>TLS auth<br/>Encryption in transit | Role-based auth<br/>Password protection<br/>TLS auth<br/>Encryption in transit<br/>Encryption at rest<br/>Private Connectivity |\n| REST API | No | Yes | Yes | Yes |\n| Selected additional features<br/> <br/> <br/> |  | Replication<br/>Auto-failover<br /> | Replication<br/>Auto-failover<br /> | Dedicated accounts<br>Redis Flex<br/>Active/Active<br/> |\n\nTo learn more, see [Redis Cloud Pricing](https://redis.io/pricing/)."},{"id":"free-rce","title":"Free Redis Cloud Essentials","role":"content","text":"Free plans are a type of Redis Cloud Essentials plans designed for training purposes and prototyping. They can be seamlessly upgraded to other Redis Cloud Essentials plans with no data loss."},{"id":"paid-rce","title":"Paid Redis Cloud Essentials","role":"content","text":"Redis Cloud Essentials is cost-efficient and designed for low-throughput scenarios. It support a range of availability, persistence, and backup options.  Pricing supports low throughput workloads. See [Redis Cloud Essentials plans](https://redis.io/docs/latest/operate/rc/subscriptions/view-essentials-subscription/essentials-plan-details#redis-cloud-essentials-plans) for a list of available plans.\n\n- [View and upgrade Essentials or Flex plan](https://redis.io/docs/latest/operate/rc/subscriptions/view-essentials-subscription)\n\n#### Essentials with Redis Flex {#redis-flex}\n\nYou can select Redis Cloud Essentials plans that support a tiered solid state drive (SSD) and RAM architecture. Using SSDs instead of RAM significantly reduces infrastructure costs, which means developers can build applications that require large datasets using the same Redis API.\n\nRedis Flex is ideal when your:\n\n- working set is significantly smaller than your dataset (high RAM hit rate)\n- average key size is smaller than average value size (all key names are stored in RAM)\n- most recent data is the most frequently used (high RAM hit rate)\n\nSee [Create a Redis Flex database](https://redis.io/docs/latest/operate/rc/databases/create-database/create-flex-database) to learn more about Redis Flex, and see [Redis Flex plans](https://redis.io/docs/latest/operate/rc/subscriptions/view-essentials-subscription/essentials-plan-details#redis-flex-plans) for a list of available plans.\n\n- [View and upgrade Essentials or Flex plan](https://redis.io/docs/latest/operate/rc/subscriptions/view-essentials-subscription)"},{"id":"redis-cloud-pro","title":"Redis Cloud Pro","role":"content","text":"Redis Cloud Pro supports more databases, larger databases, greater throughput, and no fixed database-level connection limit compared to Redis Cloud Essentials. Hosted in dedicated VPCs, they feature high-availability in a single or multi-AZ, Active-Active, clustering, data persistence, and configurable backups.  Pricing is \"pay as you go\" to support any dataset size or throughput. [Redis Flex](https://redis.io/docs/latest/operate/rc/databases/create-database/create-flex-database) is also available on Redis Cloud Pro.\n\nRedis Cloud Pro annual plans support the same features as Redis Cloud Pro but at significant savings. Annual plans also provide Premium support. The underlying commitment applies to all workloads across multiple providers and regions.\n\n- [View and edit Pro subscription](https://redis.io/docs/latest/operate/rc/subscriptions/view-pro-subscription)\n- [Upgrade from Essentials to Pro](https://redis.io/docs/latest/operate/rc/subscriptions/upgrade-essentials-pro)\n- [Subscription maintenance](https://redis.io/docs/latest/operate/rc/subscriptions/maintenance)"}],"examples":[]}
{"schema_version":2,"id":"operate/rc/subscriptions/maintenance","title":"Subscription and database maintenance","url":"https://redis.io/docs/latest/operate/rc/subscriptions/maintenance/","summary":"Describes maintenance that Redis performs on a Redis Cloud subscription.","tags":["docs","operate","rc"],"last_updated":"2026-10-01T15:21:44+01:00","children":[{"id":"operate/rc/subscriptions/maintenance/set-maintenance-windows","summary":"Shows how to set manual maintenance windows and skip maintenance.","title":"Set maintenance windows","url":"https://redis.io/docs/latest/operate/rc/subscriptions/maintenance/set-maintenance-windows/"}],"page_type":"content","content_hash":"aec17b556ee4eacc06492fdb696cb03e9092e8b7a9088407fac1d392e4df0c7e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis will maintain your Redis Cloud subscriptions and databases as needed to ensure your databases are running the most stable and up-to-date version of Redis."},{"id":"maintenance-windows","title":"Maintenance windows","role":"content","text":"Maintenance windows define when Redis can perform maintenance on your subscriptions and databases. Maintenance window options depend on your subscription type."},{"id":"redis-cloud-pro","title":"Redis Cloud Pro","role":"content","text":"For Redis Cloud Pro plans, Redis will perform maintenance automatically while limiting service disruption as much as possible. If you want to control when we can perform maintenance for a Redis Cloud Pro subscription, you can [set manual maintenance windows](https://redis.io/docs/latest/operate/rc/subscriptions/maintenance/set-maintenance-windows#set-manual-maintenance-windows)."},{"id":"redis-cloud-essentials","title":"Redis Cloud Essentials","role":"content","text":"Redis Cloud Essentials databases have a set maintenance window based on the cloud provider region. The maintenance window for Redis Cloud Essentials databases is **daily from 12 AM to 6 AM** in the region's local time zone. We won't use more than one maintenance window per week unless we need to do [urgent maintenance](https://redis.io/docs/latest/operate/rc/subscriptions/maintenance#urgent-maintenance), and we won't perform any maintenance that introduces breaking changes during these maintenance windows."},{"id":"maintenance-activities","title":"Maintenance activities","role":"content","text":"During maintenance, Redis ensures the stability of your subscriptions and databases. \n\nThis includes, but is not limited to:\n\n- Upgrading Redis or an advanced capability to the latest version\n- Cluster optimization\n- Replacing a cluster node\n- Adding more memory to a node\n- Applying security patches\n\nRedis will notify users by email when maintenance starts and ends. For more details, see [Notifications](#notifications).\n\nDuring maintenance, your database will be operational, but you may notice some latency when connecting to your databases.\n\nYour application may also disconnect from your database for a few seconds. Most Redis clients are set to refresh their DNS address when they reconnect to the database, and you will not be required to perform any further action. If you encounter connectivity problems for more than a minute during maintenance, please refresh your DNS entries.\n\n> [!TIP]\n> To make sure your applications are set to reconnect after maintenance activity, see [Develop highly available and resilient apps with Redis Cloud](https://redis.io/docs/latest/operate/rc/resilient-apps)."},{"id":"urgent-maintenance","title":"Urgent maintenance","role":"content","text":"Urgent maintenance refers to any activity that could affect service and cannot wait for scheduling. This includes applying urgent security patches.\n\nRedis can perform urgent maintenance at any time, even if you have set a manual maintenance window or have temporarily [skipped maintenance](https://redis.io/docs/latest/operate/rc/subscriptions/maintenance/set-maintenance-windows#skip-maintenance-temporarily). Redis will notify you by email when urgent maintenance starts and ends."},{"id":"major-upgrades","title":"Major upgrades","role":"content","text":"We won't schedule major upgrades or upgrades that might include breaking changes until you opt-in to those changes. You can control your Redis major version on the [subscription page](https://redis.io/docs/latest/operate/rc/subscriptions/view-pro-subscription)."},{"id":"notifications","title":"Notifications","role":"content","text":"Redis will notify you by email when maintenance starts and ends. If Redis needs an action from you to start maintenance, we will notify you with a reasonable amount of time before planned maintenance.\n\nIf you want to receive advance maintenance notifications, you must [set manual maintenance windows](https://redis.io/docs/latest/operate/rc/subscriptions/maintenance/set-maintenance-windows#set-manual-maintenance-windows) and [contact support](https://redis.io/support/) to request advance maintenance notifications.\n\nTo receive maintenance notifications by email:\n \n1. Go to [Access Management](https://redis.io/docs/latest/operate/rc/security/access-control/access-management) and select your account in the list.\n\n1. Select the Edit button.\n\n    ![Use the Edit button change details for a team member.](https://redis.io/docs/latest/images/rc/icon-edit.png)\n\n1. Select **Operational emails** if it is not already turned on.\n\n    ![Use the Edit User dialog to change the details for a user](https://redis.io/docs/latest/images/rc/access-mgmt-edit-user-dialog.png)\n\n1. Select **Save user** to save your changes."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/subscriptions/maintenance/set-maintenance-windows","title":"Set maintenance windows","url":"https://redis.io/docs/latest/operate/rc/subscriptions/maintenance/set-maintenance-windows/","summary":"Shows how to set manual maintenance windows and skip maintenance.","tags":["docs","operate","rc"],"last_updated":"2026-10-01T15:21:44+01:00","page_type":"content","content_hash":"f3870e5434282c7e3227101f5982e728d6dce64283dffae4fcd312a88f0ec21f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"By default, Redis will maintain your Redis Cloud subscriptions and databases as needed. During maintenance, you may notice some latency when connecting to your databases. For more information about the types of maintenance and how Redis maintains your subscriptions, see [Maintenance](https://redis.io/docs/latest/operate/rc/subscriptions/maintenance).\n\n> [!NOTE]\n> If your subscription includes an [RDI data pipeline](https://redis.io/docs/latest/operate/rc/rdi#data-pipeline-architecture), maintenance windows apply to the pipeline as well. The pipeline may experience brief interruptions during the configured maintenance window.\n\nFor Redis Cloud Pro plans, Redis will perform maintenance automatically while limiting service disruption as much as possible. If you want to control when Redis performs maintenance for a Redis Cloud Pro subscription, you can [set manual maintenance windows](#set-manual-maintenance-windows) to ensure non-urgent maintenance will occur at set times. Configuring or altering the maintenance window will not have any impact on your subscription or databases.\n\n> [!NOTE]\n> You can only set manual maintenance windows for Redis Cloud Pro plans. A Redis Cloud Essentials database has a set maintenance window based on the region where it is located. See [Essentials maintenance](https://redis.io/docs/latest/operate/rc/subscriptions/maintenance#redis-cloud-essentials) for more information."},{"id":"set-manual-maintenance-windows","title":"Set manual maintenance windows","role":"content","text":"To set manual maintenance windows for a single Redis Cloud Pro subscription:\n\n1. From the [Redis Cloud console](https://cloud.redis.io/), select the **Subscriptions** menu and then select your subscription from the list.\n\n1. Select the **Overview** tab.\n\n1. In the **Maintenance Windows** panel, select **Manual**.\n\n1. Click **Activate** to confirm your selection.\n\n1. Enter the maintenance window time frame details.\n\n    ![The set maintenance windows panel](https://redis.io/docs/latest/images/rc/subscriptions-set-maintenance-window.png)\n\n    - Select the days that Redis can perform maintenance in the **Days** drop-down.\n    - Select the time that Redis can start performing maintenance on those days in the **From** drop-down.\n    - Select the amount of time that Redis can perform maintenance in the **Duration** drop-down.\n    - Select **+ Time Frame** to add another time frame for maintenance.\n    - Select **Advance notification** if you want to be notified of maintenance in advance.\n\n    > [!NOTE]\n    > You must [contact support](https://redis.io/support/) to request and be approved for advance maintenance notifications, otherwise skip this step as the this option does not appear by default.\n    >\n\n    Redis recommends allowing maintenance on at least two different days for 8 hours on each day.\n\n1. Click **Save**."},{"id":"skip-maintenance-temporarily","title":"Skip maintenance temporarily","role":"content","text":"To skip maintenance temporarily for a subscription:\n\n1. From the [Redis Cloud console](https://cloud.redis.io/), select the **Subscriptions** menu and then select your subscription from the list.\n\n1. Select the **Overview** tab.\n\n1. In the **Maintenance Windows** panel, select **Skip the next 14 days**.\n\n1. Click **Continue** to confirm your selection.\n\nYou will only be allowed to skip maintenance once per month. \n\nDuring the skipped maintenance period, Redis will not perform any minor or major upgrades. Redis may perform [urgent maintenance](https://redis.io/docs/latest/operate/rc/subscriptions/maintenance#urgent-maintenance) on your subscription, but only if it is absolutely necessary."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/subscriptions/upgrade-essentials-pro","title":"Upgrade database from Redis Cloud Essentials to Redis Cloud Pro","url":"https://redis.io/docs/latest/operate/rc/subscriptions/upgrade-essentials-pro/","summary":"Upgrade your Redis Cloud Essentials subscription to a Redis Cloud Pro subscription.","tags":["docs","operate","rc"],"last_updated":"2026-10-05T10:59:52+01:00","page_type":"content","content_hash":"4450209f4604a23a6f689fb6d5b75cc130abda4984120cebc427bc9a958e444d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Cloud Essentials supports low throughput workflows. It supports a range of availability, persistence, and backup options, and can be great for testing and prototyping. However, if your databases need higher throughput, or you're missing features that are not available with Redis Cloud Essentials, you may want to upgrade Redis Cloud Essentials to Redis Cloud Pro.\n\nFor more information about the different subscription plans, see [Subscription plans](https://redis.io/docs/latest/operate/rc/subscriptions#subscription-plans).\n\nTo upgrade your Essentials plan to another essential plan, see [Upgrade subscription plan](https://redis.io/docs/latest/operate/rc/subscriptions/view-essentials-subscription#upgrade-plan)."},{"id":"upgrade-essentials-subscription-to-pro","title":"Upgrade Essentials subscription to Pro","role":"content","text":"To follow the steps in this guide, you must have a database with [Redis Cloud Essentials](https://redis.io/docs/latest/operate/rc/subscriptions/view-essentials-subscription) that you want to upgrade to Redis Cloud Pro.\n\nTo upgrade your Essentials database to Redis Cloud Pro:\n\n1. [Create a new database in Redis Cloud Pro](#create-rcp) with the right specifications to be able to migrate your database.\n\n1. [Migrate the data in your Essentials database](#migrate-data) to your new Redis Cloud Pro database, if needed.\n\n1. [Redirect the endpoints](#redirect-database-endpoints) to your new Redis Cloud Pro database."},{"id":"create-rcp","title":"Create Redis Cloud Pro database","role":"content","text":"[Create a new database](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new) with the following specifications:\n\n- Select **Redis Cloud Pro** for your subscription type.\n- In the [**Sizing tab**](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new#sizing-tab), create your databases with the following specifications:\n    - Set the memory limit to comply with [Active-Passive memory requirements](https://redis.io/docs/latest/operate/rc/databases/migrate-databases#active-passive-memory-requirements) if you want to migrate your database using [Active-Passive](https://redis.io/docs/latest/operate/rc/databases/migrate-databases#sync-using-active-passive).\n    - In **More options**, set the **Port** to **Manually assign**, and enter the port of your Essentials database. You must set the port number to match the port of your Essentials database if you want to migrate your database endpoints. You can find the port in the endpoint of your database on the **Configuration** tab of your database."},{"id":"migrate-data","title":"Migrate data","role":"content","text":"You can migrate your Redis Cloud Essentials database to your new Redis Cloud Pro subscription using any method in the [Migrate databases](https://redis.io/docs/latest/operate/rc/databases/migrate-databases) guide. This guide uses [Active-Passive](https://redis.io/docs/latest/operate/rc/databases/migrate-databases#sync-using-active-passive) to migrate databases between subscriptions in the same account.\n\n> [!NOTE]\n> Before you follow this guide, be aware of the following limitations:\n>\n> - This guide is for migrating databases between subscriptions in the same Redis Cloud console account. [Contact support](https://redis.io/support/) if you want to migrate a database between accounts using Active-Passive.\n>\n> - As long as Active-Passive is enabled, data in the target database will not expire and will not be evicted regardless of the set [data eviction policy](https://redis.io/docs/latest/operate/rc/databases/configuration/data-eviction-policies). We recommend that you turn off Active-Passive after the databases are synced. \n\n1. Select the database you want to migrate your data to. This will be your target database.\n\n1. From the **Configuration** tab of the target database, select **Edit**.\n\n    ![The Edit database button lets you change selected database properties.](https://redis.io/docs/latest/images/rc/button-database-edit.png)\n\n1. In the **Durability** section, enable **Active-Passive Redis** and then select **Add Source**.\n\n    ![Active-Passive settings are located in the **Durability** section of the database **Configuration** tab.](https://redis.io/docs/latest/images/rc/migrate-data-active-passive-enable.png)\n\n    ![Use the **Add Source** button to specify the source of the Active-Passive replica.](https://redis.io/docs/latest/images/rc/button-database-uri-add.png)\n\n1. This will open the **Add Active-Passive Redis** screen. Select **Current account** to connect a database in your current account.\n\n    ![The Add Active-Passive Redis screen.](https://redis.io/docs/latest/images/rc/migrate-data-add-active-passive.png)\n\n1. Select your Redis Cloud Essentials database from the **Source database** list. This will be your source database. You can type in the database's name to find it.\n\n    ![Select the Source database from the database list.](https://redis.io/docs/latest/images/rc/database-add-account-path-list.png)\n\n1. Select **Save Database** to begin updating the database.\n\n    ![Use the **Save Database** button to save your changes, deploy the database, and to start data migration.](https://redis.io/docs/latest/images/rc/button-database-save.png)\n\n    Initially, the database status is __Pending__, which means the update task is still running.  \n\n    ![When the status is 'Pending', your changes are still being deployed.](https://redis.io/docs/latest/images/rc/icon-database-update-status-pending.png)\n\n    The sync process doesn't begin until the database becomes `Active`.  \n\n    ![When the status becomes 'Active', data begins to sync.](https://redis.io/docs/latest/images/rc/icon-database-status-active.png)\n\n    When data has fully migrated to the target database, database status reports `Synced`.  \n\n    ![When the data is migrated, the target database status displays Synced.](https://redis.io/docs/latest/images/rc/migrate-data-status-synced.png)\n\n    Active-Passive sync lets you migrate data while apps and other connections are using the source database. Once the data is migrated, you should migrate active connections to the target database and turn off Active-Passive before you move on."},{"id":"redirect-database-endpoints","title":"Redirect database endpoints","role":"content","text":"Redirecting your database endpoints after migrating your data lets you direct connections to your new database without any code changes.\n\n> [!NOTE]\n> See [Redirect dynamic endpoints](https://redis.io/docs/latest/operate/rc/databases/redirect-endpoints) for more information on the limitations and requirements for endpoint redirection.\n\nTo redirect your database endpoints:\n\n1. From the Redis Cloud console, select **Databases** from the menu and select the source database in the list.\n\n1. In the **General** section of the **Configuration** tab, select **Redirect endpoints**.\n\n    ![Use the **Redirect endpoints** button to change the target database for the source database endpoints.](https://redis.io/docs/latest/images/rc/databases-configuration-redirect-endpoints.png)\n\n1. Select the target Redis Cloud Pro database from the **Target database** list. You can type in the database's name to find it.\n\n    You can choose whether to map the original endpoint to the **Public** or the **Private** endpoint. \n\n    ![Choose whether to map the original endpoint to the Public or Private endpoint.](https://redis.io/docs/latest/images/rc/migrate-data-redirect-essentials-endpoints.png)\n\n    You must have a private connectivity method set up to be able to select the **Private** endpoint, such as:\n    - [VPC peering](https://redis.io/docs/latest/operate/rc/security/vpc-peering)\n    - [Google Cloud Private Service Connect](https://redis.io/docs/latest/operate/rc/security/private-service-connect) _(Google Cloud only)_\n    - [AWS Transit Gateway](https://redis.io/docs/latest/operate/rc/security/aws-transit-gateway) or [AWS PrivateLink](https://redis.io/docs/latest/operate/rc/security/aws-privatelink) _(AWS only)_\n\n1. If you want to assign the same [Role-based Access Control (RBAC) roles](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/role-based-access-control) to the target database that are assigned to the source database, select **Assign the same ACLs to the target database**.\n\n    ![Select **Assign the same ACLs to the target database** to assign the same roles to the target database.](https://redis.io/docs/latest/images/rc/migrate-data-redirect-assign-acls.png)\n\n1. Select **I acknowledge this action will redirect my database endpoints** to confirm that you understand that this action will redirect your database endpoints. Then select **Redirect endpoints**.\n\n    ![The **Redirect endpoints** button redirects the source database endpoints to the target database.](https://redis.io/docs/latest/images/rc/migrate-data-redirect-acknowledge.png)\n\nAfter you redirect your database endpoints, you can go to the **Configuration** tab of the target database to verify that the endpoints now point to the target database. \n\nYou can revert endpoint migration within 24 hours to restore the original endpoints. Select **Revert** to revert endpoint migration.\n\n![The **Revert** button reverts endpoint migration.](https://redis.io/docs/latest/images/rc/migrate-data-redirect-revert.png)\n\nAfter the 24-hour window, you can no longer revert to the endpoint to the original database."},{"id":"delete-essentials-database","title":"Delete Essentials database","role":"content","text":"After you migrate your data and redirect your endpoints, [delete the source database](https://redis.io/docs/latest/operate/rc/databases/delete-database)."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/subscriptions/view-essentials-subscription/essentials-plan-details","title":"Redis Cloud Essentials plans","url":"https://redis.io/docs/latest/operate/rc/subscriptions/view-essentials-subscription/essentials-plan-details/","summary":"Provides detailed information on Redis Cloud Essentials plans.","tags":["docs","operate","rc"],"last_updated":"2026-09-25T15:00:18+01:00","page_type":"content","content_hash":"5fa48134b5d665e61ffd51f02901417f4e88ac6def05c9fae3445a53f8e3b3c9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Cloud Essentials is cost-efficient and designed for low-throughput scenarios. It supports a range of availability, persistence, and backup options.  \n\n[Essentials plan pricing](https://redis.io/pricing/#monthly) scales according to the memory size of the database defined in the subscription.  Additional limits also apply, as shown in the tables below.\n\nThe 30 MB Essentials plan is free; it's designed for learning and building test projects. It gives you enough space to learn Redis concepts and develop application prototypes.\n\nOur paid plans start at 250 MB and offer extra features, like high availability and backups (both daily and instant). They are great for bigger projects or production environments that require increased storage, reliability, and other features to support your operational needs.\n\nEssentials plans have bandwidth and throughput limits described in the tables below. We will notify you before limiting your database usage if you exceed these limits.\n\nIf you need additional resources, you can [upgrade your subscription](https://redis.io/docs/latest/operate/rc/subscriptions/view-essentials-subscription#upgrade-plan) at any time."},{"id":"current-plans","title":"Current plans","role":"content","text":"These plans are currently offered for all new and upgraded Essentials subscriptions (updated December 2025)."},{"id":"redis-cloud-essentials-plans","title":"Redis Cloud Essentials plans","role":"content","text":"| **DB&nbsp;size**<sup>[1](#table-note-1)</sup> | **30&nbsp;MB&nbsp;(Free)** | **250 MB** | **1 GB** | **2.5 GB** | **5 GB** | **12 GB** |\n|---|---|---|---|---|---|---|\n| **Concurrent<br/>connections<br/>per database** | 30 | 256 | 1024 | 2500 | 5000 | 10000 |\n| **CIDR<br/> allow rules** | 1 | 4 | 4-8 | 4-8 | 4-16 | 4-32 |\n| **Monthly<br/> total network<br/> bandwidth<sup>[2](#table-note-2)</sup>** | 5&nbsp;GB | 100&nbsp;GB | 200&nbsp;GB | 400&nbsp;GB | 800&nbsp;GB | 2000&nbsp;GB |\n| **Maximum<br/> throughput<sup>[3](#table-note-3)</sup>** | 100&nbsp;ops/sec | 1000&nbsp;ops/sec | 2000&nbsp;ops/sec | 4000&nbsp;ops/sec | 8000&nbsp;ops/sec | 16000&nbsp;ops/sec |\n\n1. <a name=\"table-note-1\" style=\"display: block; height: 80px; margin-top: -80px;\"></a> Database size includes replication where applicable. See [High availability cost impact](https://redis.io/docs/latest/operate/rc/databases/configuration/high-availability#dataset-size) for more information.\n\n2. <a name=\"table-note-2\" style=\"display: block; height: 80px; margin-top: -80px;\"></a> The monthly total network bandwidth limit applies to the entire plan. All databases in the plan share the allocated bandwidth limit.\n\n3. <a name=\"table-note-3\" style=\"display: block; height: 80px; margin-top: -80px;\"></a> Assumes request size of 1 KiB. Maximums are capped by actual MB/s reached. To find the MB/s limit, divide the Maximum throughput by 1024."},{"id":"redis-cloud-essentials-plans-with-redis-flex","title":"Redis Cloud Essentials plans with Redis Flex","role":"content","text":"| **DB&nbsp;size**<sup>[1](#table-note-1-flex)</sup> | **1 GB** | **2.5 GB** | **5 GB** | **12 GB** | **25 GB** | **50 GB** | **100 GB** |\n|---|---|---|---|---|---|---|---|\n| **Concurrent<br/>connections<br/>per database** | 1024 | 2500 | 5000 | 10000 | 10000 | 10000 | 10000 |\n| **CIDR<br/> allow rules** | 4-8 | 4-8 | 4-16 | 4-32 | 4-32 | 4-32 | 4-32 |\n| **Monthly<br/> total network<br/> bandwidth** | 50&nbsp;GB | 100&nbsp;GB | 200&nbsp;GB | 500&nbsp;GB | 1000&nbsp;GB | 2000&nbsp;GB | 4000&nbsp;GB |\n| **Maximum<br/> throughput<sup>[2](#table-note-2-flex)</sup>** | 200&nbsp;ops/sec | 500&nbsp;ops/sec | 1000&nbsp;ops/sec | 2400&nbsp;ops/sec | 5000&nbsp;ops/sec | 10000&nbsp;ops/sec | 20000&nbsp;ops/sec |\n\n1. <a name=\"table-note-1\" style=\"display: block; height: 80px; margin-top: -80px;\"></a> Database size includes replication where applicable. See [High availability cost impact](https://redis.io/docs/latest/operate/rc/databases/configuration/high-availability#dataset-size) for more information.\n\n2. <a name=\"table-note-2\" style=\"display: block; height: 80px; margin-top: -80px;\"></a> Assumes request size of 1 KiB. Maximums are capped by actual MB/s reached. To find the MB/s limit, divide the Maximum throughput by 1024."},{"id":"legacy","title":"Previous plans","role":"content","text":"These plans were available before November 2023.\n\n#### Table part 1 of 2, rows 1-3\n\n| **Max&nbsp;DB&nbsp;size&nbsp;**<sup>[1](#table-note-1-legacy)</sup> | **30 MB** | **100 MB** | **250 MB** | **500 MB** | **1 GB** | **2.5 GB** |\n| ----------------------------------------------------------------------------------- | ------------------ | ------------------ | ------------------- | ------------------- | ------------------- | ------------------- |\n| **Concurrent<br/>connections<br/>per database** | 30 | 256 | 256 | 512 | 1024 | 2500 |\n| **Monthly<br/> total network<br/> bandwidth**<sup>[2](#table-note-2-legacy)</sup> | 5&nbsp;GB | 50&nbsp;GB | 100&nbsp;GB | 150&nbsp;GB | 200&nbsp;GB | 400&nbsp;GB |\n| **Maximum<br/> throughput<sup>[3](#table-note-3-legacy)</sup>** | 100&nbsp;ops/sec | 500&nbsp;ops/sec | 1000&nbsp;ops/sec | 1500&nbsp;ops/sec | 2000&nbsp;ops/sec | 4000&nbsp;ops/sec |\n\n#### Table part 2 of 2, rows 1-3\n\n| **Max&nbsp;DB&nbsp;size&nbsp;**<sup>[1](#table-note-1-legacy)</sup> | **5 GB** | **10 GB** |\n| ----------------------------------------------------------------------------------- | ------------------- | -------------------- |\n| **Concurrent<br/>connections<br/>per database** | 5000 | 10000 |\n| **Monthly<br/> total network<br/> bandwidth**<sup>[2](#table-note-2-legacy)</sup> | 800&nbsp;GB | 2000&nbsp;GB |\n| **Maximum<br/> throughput<sup>[3](#table-note-3-legacy)</sup>** | 8000&nbsp;ops/sec | 16000&nbsp;ops/sec |\n\n\n\n1. <a name=\"table-note-1-legacy\" style=\"display: block; height: 80px; margin-top: -80px;\"></a> Database size includes replication. See [High availability cost impact](https://redis.io/docs/latest/operate/rc/databases/configuration/high-availability#dataset-size) for more information.\n\n2. <a name=\"table-note-2-legacy\" style=\"display: block; height: 80px; margin-top: -80px;\"></a> The monthly total network bandwidth limit applies to the entire plan. All databases in the plan share the allocated bandwidth limit.\n\n2. <a name=\"table-note-2-legacy\" style=\"display: block; height: 80px; margin-top: -80px;\"></a> Assumes request size of 1 KiB. Maximums are capped by actual MB/s reached. To find the MB/s limit, divide the Maximum throughput by 1024."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/subscriptions/view-essentials-subscription","title":"View and Upgrade Redis Cloud Essentials plan","url":"https://redis.io/docs/latest/operate/rc/subscriptions/view-essentials-subscription/","summary":"","aliases":["/operate/rc/subscriptions/view-fixed-subscription/"],"tags":["docs","operate","rc"],"last_updated":"2026-10-01T15:21:44+01:00","children":[{"id":"operate/rc/subscriptions/view-essentials-subscription/essentials-plan-details","summary":"Provides detailed information on Redis Cloud Essentials plans.","title":"Redis Cloud Essentials plans","url":"https://redis.io/docs/latest/operate/rc/subscriptions/view-essentials-subscription/essentials-plan-details/"}],"page_type":"content","content_hash":"a5ac94257596ba38be2b59600dc2fac706d9313ebfc38ad7155602a441be29a4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"To view the details of a Redis Cloud Essentials subscription:\n\n1.  Sign in to the [Redis Cloud console](https://cloud.redis.io/) and select the **Subscriptions** list.\n\n1.  Select the target subscription from the subscription list.\n\n    ![The Subscription list shows your current subscriptions.](https://redis.io/docs/latest/images/rc/subscription-list-select.png)\n\n1.  Your subscription details appear, along with a summary of your database details.\n\n    ![The Databases tab of the subscription details page is the default view.](https://redis.io/docs/latest/images/rc/subscription-details-fixed-databases-tab.png)\n\nFrom here, you can:\n\n- Select the **Plan upgrade** button to update your subscription plan, high availability settings, or payment method.\n\n    ![Select the Upgrade plan button to update your subscription settings.](https://redis.io/docs/latest/images/rc/button-subscription-upgrade-plan.png)\n\n- Select the **Overview** tab to view and edit subscription details.\n\nThe following sections provide more details."},{"id":"upgrade-plan","title":"Upgrade plan","role":"content","text":"Use the **Plan upgrade** button to update your Redis Cloud Essentials plan, your high availability settings, or your payment method. Upgrading your database between Redis Cloud Essentials plans does not impact database availability during the update.\n\n![Use the Plan upgrade button to change selected Redis Cloud Essentials subscription detils.](https://redis.io/docs/latest/images/rc/button-subscription-upgrade-plan.png)\n\nFor information on how to upgrade to Redis Cloud Pro, see [upgrade subscription plan from Essentials to Pro](https://redis.io/docs/latest/operate/rc/subscriptions/upgrade-essentials-pro)."},{"id":"change-high-availability-and-persistence","title":"Change high availability and persistence","role":"content","text":"To change your plan's [high availability](https://redis.io/docs/latest/operate/rc/databases/configuration/high-availability) and [data persistence](https://redis.io/docs/latest/operate/rc/databases/configuration/data-persistence) settings, change them in the **Configure your settings** panel.\n\n![Use the Configure your settings panel to set Essentials subscription settings.](https://redis.io/docs/latest/images/rc/subscription-fixed-high-availability-panel.png)\n\nYou can switch between **No replication** and **Single-zone replication** at any time, but you cannot choose **Multi-zone replication** after your subscription is created. You also cannot switch from **Multi-zone replication** to another high availability option."},{"id":"change-subscription-plan","title":"Change subscription plan","role":"content","text":"To change your subscription plan, select the desired plan from the list and select the **Upgrade plan** button:\n\n![Select the desired subscription plan from the ones shown.](https://redis.io/docs/latest/images/rc/subscription-change-fixed-tiers.png)\n\nEach Redis Cloud Essentials plan provides a variety of benefits, including increased memory and number of connections.\nFor a comparison of available plans, see [Redis Cloud Essentials plans](https://redis.io/docs/latest/operate/rc/subscriptions/view-essentials-subscription/essentials-plan-details).\n\nWhen you change your plan, your data and endpoints are not disrupted.  \n\nIf you upgrade a free plan to a paid plan, you need to add a payment method.\n\nIf you change your subscription to a lower plan, make sure your data fits within the limits of the new plan; otherwise, the change attempt will fail.\n\n> [!NOTE]\n> As of January 2024, new and upgraded Redis Cloud Essentials subscriptions are limited to one database per subscription. If you have a Redis Cloud Essentials subscription created before January 2024, you can add databases up to the previous plan limit. If you choose to upgrade your plan, you can keep your databases, but you will need to create a new subscription to add another database."},{"id":"change-payment-method","title":"Change payment method","role":"content","text":"To change your subscription payment method, update the **Payment method** settings.  You can select a known payment method from the drop-down list or use the **Add** button to add a new one.\n\n![Use the Credit card drop-down to set your subscription payment method.](https://redis.io/docs/latest/images/rc/subscription-change-credit-card.png)\n\nPayment method changes require the Owner or Billing Admin roles. To verify your role, select **Access Management** from the admin menu and then locate your credentials in the **Team** tab."},{"id":"save-changes","title":"Save changes","role":"content","text":"Use the **Update Database** button to save changes.\n\n![Use the Upgrade plan button to save your subscription plan changes.](https://redis.io/docs/latest/images/rc/button-subscription-upgrade-plan-blue.png)"},{"id":"subscription-overview","title":"Subscription overview","role":"content","text":"The **Overview** tab summarizes your Redis Cloud Essentials subscription details using a series of panels:\n\n![The Overview tab displays the details of your Fixed subscription.](https://redis.io/docs/latest/images/rc/subscription-details-fixed-overview-tab.png)\n\nThe following details are displayed:\n\n| Detail | Description |\n|:---------|:--------------|\n| **Subscription description** | Brief summary of subscription, including the plan type, cloud provider, region, and data size limit |\n| **Cloud vendor** | Your database's cloud vendor |\n| **Availability** | Describes high availability settings |\n| **Region** | The region your subscription is deployed to |\n| **Plan** | The maximum database size of your Essentials plan. Also displays the cost for paid plans. |\n| **Databases** | Maximum number of databases for your plan |\n| **Connections** | Maximum number of concurrent connections |\n| **CIDR allow rules** | Maximum number of authorization rules |\n| **Data persistence** | Indicates whether persistence is supported for your subscription |\n| **Daily & instant backups** | Indicates whether backups are supported for your subscription |\n| **Replication** | Indicates whether replication is supported for your subscription |\n| **Clustering** | Indicates whether clustering is supported for your subscription |\n\nThe **Delete Database** button lets you [delete your database](https://redis.io/docs/latest/operate/rc/databases/delete-database).\n\n![Use the Delete subscription button to delete your subscription plan.](https://redis.io/docs/latest/images/rc/button-delete-database-essentials-overview.png)"}],"examples":[]}
{"schema_version":2,"id":"operate/rc/subscriptions/view-pro-subscription","title":"View and edit Redis Cloud Pro plan","url":"https://redis.io/docs/latest/operate/rc/subscriptions/view-pro-subscription/","summary":"","aliases":["/operate/rc/subscriptions/view-flexible-subscription/"],"tags":["docs","operate","rc"],"last_updated":"2026-10-02T16:06:07+01:00","page_type":"content","content_hash":"49839280387460d55927ddb8e2fe2a4da0330efe78f0f75f11f09d32cecc30d0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"To view the details of a Redis Cloud Pro subscription:\n\n1.  Sign in to the [Redis Cloud console](https://cloud.redis.io/#) and select **Subscriptions**.\n\n1.  If you have more than one subscription, select the target subscription from the subscription list.\n\n    ![The Subscription list shows your current subscriptions.](https://redis.io/docs/latest/images/rc/subscription-list-select.png)\n\n1.  Your subscription details appear, along with a summary of your database details.\n\n    ![The Databases tab of the subscription details page is the default view.](https://redis.io/docs/latest/images/rc/subscription-flexible-databases-tab-pending.png)\n\nFrom here, you can:\n\n- Select **Create database in this subscription** to [add a database to your subscription](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-existing).\n\n    ![The Create database in this subscription button.](https://redis.io/docs/latest/images/rc/button-add-new-to-pro.png)\n\n- View the Status icon to learn the status of your subscription.  Active subscriptions display a green circle with a check mark. Pending subscriptions display an animated yellow circle.\n\n    ![When a subscription is active, the status icon displays a green circle with a checkmark.](https://redis.io/docs/latest/images/rc/icon-database-status-active.png#no-click) &nbsp; ![When a subscription is pending, the status icon displays a gre, animated circle.](https://redis.io/docs/latest/images/rc/icon-subscription-status-pending.png#no-click)\n\nBecause subscriptions represent active deployments, there aren't many details you can change.  If your needs change, [create a new subscription](https://redis.io/docs/latest/operate/rc/databases/create-database/create-pro-database-new) and then [migrate the existing data](https://redis.io/docs/latest/operate/rc/databases/migrate-databases) to the new databases.\n\nIn addition, you can view and edit the following subscription details:\n\n1.  The **Databases** tab lists the databases in your subscription and summarizes their settings.\n\n2.  The **Overview** tab displays subscription settings for your Redis Cloud Pro subscription.\n\n3. The **Data integration** tab displays your Data integration workspace and pipelines.\n\n4.  The **Connectivity** tab lets you limit access to the subscription by defining a VPC peering or other connectivity options.\n\n5.  The **Security** tab lets you set security settings for the databases in your subscription.\n\n6.  The **Regions** tab lets you manage the regions in your Active-Active subscription (_Active-Active subscriptions only_).\n\nThe following sections provide more info."},{"id":"databases-tab","title":"**Databases** tab","role":"content","text":"The **Databases** tab summarizes the databases in your subscription.  \n\n![The Databases tab of the subscription details page is the default view.](https://redis.io/docs/latest/images/rc/subscription-flexible-databases-tab-pending.png)\n\nThe following details are provided:\n\n| Detail | Description |\n|:---------|:--------------|\n| **Status** | An icon indicating whether the database is active (a green circle) or pending (yellow circle)<br/>![Active status is indicated by a teal circle.](https://redis.io/docs/latest/images/rc/icon-database-detail-status-active.png#no-click)&nbsp;![Pending status is indicated by a yellow circle.](https://redis.io/docs/latest/images/rc/icon-database-detail-status-pending.png#no-click) |\n| **Name** | The database name |\n| **Endpoint** | Use the **Copy** button to copy the endpoint URI to the Clipboard |\n| **Memory** | Memory size of the database, showing the current size and the maximum size |\n| **Throughput** | Maximum operations per second supported for the database |\n| **Capabilities** | Identifies advanced capabilities attached to the database |\n| **Options** | Icons showing options associated with the database |\n\nTo view full details of a database, click its name in the list."},{"id":"overview-tab","title":"**Overview** tab","role":"content","text":"The **Overview** summarizes the options used to create the subscription.\n\n![The Overview tab displays the settings used to create your Redis Cloud Pro subscription.](https://redis.io/docs/latest/images/rc/subscription-details-overview-flexible.png)\n\n- The general settings panel describes the cloud vendor, region, and high-availability settings for your subscription.\n\n    Select **Edit** to change the name of the subscription.\n\n    ![Use the **Edit** button to change the subscription name.](https://redis.io/docs/latest/images/rc/icon-edit-subscription-name.png)\n\n\n    | Setting | Description |\n    |:---------|:--------------|\n    | **Cloud vendor** | Your subscription cloud vendor |\n    | **Plan description** | Brief summary of subscription, including the plan type, cloud provider, and region |\n    | **Auto Tiering** | Checked when Auto Tiering is enabled |\n    | **Multi-AZ** | Checked when multiple availability zones are enabled |\n    | **Active-Active Redis** | Checked when Active-Active Redis is enabled for your subscription |\n    | **Region** | Describes the region your subscription is deployed to |\n    | **Availability Zones** | The availability zones your subscription is deployed in (Visible if you selected availability zones on creation) |\n    | **AWS Resource tags** | The tags applied to your resources in your BYOC account. Select the **Edit** button to manage your resource tags. See [Resource tags](https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud/resource-tags) for more information.<br/><br/>(Available only if [Redis Cloud Bring your own Cloud](https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud) is enabled) |\n\n\n- The **Price** panel shows the monthly cost of your Redis Cloud Pro subscription.\n\n- The **Payment Method** panel shows the current payment details.\n\n    Select the **Edit payment method** button to change the credit card associated with this subscription.\n\n    ![The edit payment method button, selected and showing a credit card.](https://redis.io/docs/latest/images/rc/icon-subscription-detail-change-payment-flexible.png)\n\n    Select **Add credit card** to add a new credit card.\n\n- The **Maintenance Window** panel shows your current [maintenance window settings](https://redis.io/docs/latest/operate/rc/subscriptions/maintenance/set-maintenance-windows).\n\n    See [Maintenance](https://redis.io/docs/latest/operate/rc/subscriptions/maintenance) for more information about subscription maintenance on Redis Cloud.\n\n- The **Provisioned cloud resources** panel shows the storage resources used by your subscription.\n\n  If your subscription is attached to a cloud account, the details appear in the panel header.\n\n- The **Redis price** panel breaks down your subscription price."},{"id":"data-integration-tab","title":"**Data Integration** tab","role":"content","text":"The **Data integration** tab displays your Data integration workspace and pipelines. See [Data integration](https://redis.io/docs/latest/operate/rc/rdi) for more information."},{"id":"connectivity-tab","title":"**Connectivity** tab","role":"content","text":"The **Connectivity** tabs helps secure your subscription.  \n\n![The Connectivity tab helps you secure your subscription.](https://redis.io/docs/latest/images/rc/subscription-details-connectivity-tab-flexible.png)\n\nHere, you can:\n\n- Set up a [VPC peering](https://redis.io/docs/latest/operate/rc/security/vpc-peering) relationship between the virtual private cloud (VPC) hosting your subscription and another VPC.\n\n- Set up a [CIDR allow list](https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud/subscription-whitelist) containing IP addresses or security groups permitted to access your subscription (_[Bring your own Cloud](https://redis.io/docs/latest/operate/rc/subscriptions/bring-your-own-cloud) only_).\n\n- Set up [Private Service Connect](https://redis.io/docs/latest/operate/rc/security/private-service-connect) (*Google Cloud only*), [Transit Gateway](https://redis.io/docs/latest/operate/rc/security/aws-transit-gateway) (*AWS only*), or [AWS PrivateLink](https://redis.io/docs/latest/operate/rc/security/aws-privatelink) (*AWS only*).\n\nSee the individual links to learn more."},{"id":"security-tab","title":"**Security** tab","role":"content","text":"The **Security** tab lets you set security settings for the databases in your subscription.\n\nHere, you can [block public endpoints](https://redis.io/docs/latest/operate/rc/security/database-security/block-public-endpoints) for all databases in the subscription."},{"id":"regions-tab","title":"**Regions** tab","role":"content","text":"The **Regions** tab is only available for Active-Active subscriptions.  It lets you manage the regions in your Active-Active subscription.\n\n![The Regions tab lets you manage the regions in your Active-Active subscription.](https://redis.io/docs/latest/images/rc/subscription-details-regions-tab.png)\n\nSee [Manage regions for an Active-Active database](https://redis.io/docs/latest/operate/rc/databases/active-active/manage-regions) for more information."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/supported-regions","title":"Supported Cloud providers and regions","url":"https://redis.io/docs/latest/operate/rc/supported-regions/","summary":"","tags":["docs","operate","rc"],"last_updated":"2026-09-25T14:59:36+01:00","page_type":"content","content_hash":"d27ac4faf1bb013db24076661bc1c6e3782d2c4c6ce38785cb7a8e38de0c08a8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Your choice of cloud provider and region may affect latency between your application and your database, and may affect what connectivity options are available for your database.\n\nRedis Cloud supports databases on the following cloud providers:\n- [Amazon Web Services (AWS)](#amazon-web-services)\n- [Google Cloud](#google-cloud)\n- [Microsoft Azure](#microsoft-azure)"},{"id":"amazon-web-services","title":"Amazon Web Services","role":"content","text":"Redis Cloud supports databases in the following Amazon Web Services (AWS) regions.\n\nRedis Cloud Pro databases on AWS support [VPC Peering](https://redis.io/docs/latest/operate/rc/security/vpc-peering#aws-vpc-peering), [Transit Gateway](https://redis.io/docs/latest/operate/rc/security/aws-transit-gateway), and [AWS PrivateLink](https://redis.io/docs/latest/operate/rc/security/aws-privatelink)."},{"id":"google-cloud","title":"Google Cloud","role":"content","text":"Redis Cloud supports databases in the following Google Cloud regions.\n\nRedis Cloud Pro databases on Google Cloud support [VPC Peering](https://redis.io/docs/latest/operate/rc/security/vpc-peering#gcp-vpc-peering) and [Private Service Connect](https://redis.io/docs/latest/operate/rc/security/private-service-connect)."},{"id":"microsoft-azure","title":"Microsoft Azure","role":"content","text":"Redis Cloud Essentials is available on the following Microsoft Azure regions:\n\n\n\n> [!NOTE]\n> Redis Cloud Pro is available on Azure through [Azure Managed Redis](https://azure.microsoft.com/en-us/products/managed-redis/). See [Azure Managed Redis pricing](https://azure.microsoft.com/en-us/pricing/details/managed-redis/) to view the list of Azure regions that support Azure Managed Redis."}],"examples":[]}
{"schema_version":2,"id":"operate/rc/troubleshoot","title":"Troubleshoot Redis Cloud issues with the Redis Cloud knowledge base","url":"https://redis.io/docs/latest/operate/rc/troubleshoot/","summary":"Use the Redis Cloud knowledge base to find answers to common Redis Cloud questions.","tags":["docs","operate","rc"],"last_updated":"2025-09-19T16:08:48-04:00","page_type":"content","content_hash":"0474ac61b11719a293bd2eb54777df025332dec8828f1574249fe575a64a5a45","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The [Redis Cloud knowledge base](https://support.redislabs.com/hc/en-us/categories/26174183986450-Redis-Cloud) provides articles to help you troubleshoot common Redis Cloud issues."},{"id":"account-and-user-management","title":"Account and User Management","role":"content","text":"- [Restore access to your Redis Cloud account](https://support.redislabs.com/hc/en-us/articles/27496470929170-Restore-Access-to-Your-Redis-Cloud-Account)\n- [Cancel a Redis Cloud subscription](https://support.redislabs.com/hc/en-us/articles/27215953424786-Cancel-a-Redis-Cloud-Subscription)\n- [Delete a Redis Cloud account](https://support.redislabs.com/hc/en-us/articles/26180817014162-Delete-a-Redis-Cloud-Account)\n- [Submit a Redis Cloud privacy deletion request](https://support.redislabs.com/hc/en-us/articles/27646907553170-Submit-a-Redis-Cloud-Privacy-Deletion-Request)"},{"id":"billing-and-payments","title":"Billing and Payments","role":"content","text":"- [Resolving payment failures in Redis Cloud](https://support.redislabs.com/hc/en-us/articles/26266042933522-Resolving-Payment-Failures-in-Redis-Cloud)\n- [Credit card retry behavior and troubleshooting](https://support.redislabs.com/hc/en-us/articles/27697187187986-Credit-Card-Retry-Behavior-and-Troubleshooting)\n- [Access invoices and payment links](https://support.redislabs.com/hc/en-us/articles/26265945230226-Access-Invoices-and-Payment-Links-in-the-Portal)\n- [How to find payment information](https://support.redislabs.com/hc/en-us/articles/26266104144914-How-to-Find-Payment-Information)"},{"id":"database-management-and-observability","title":"Database management and observability","role":"content","text":"- [Manage network usage and subscription limits in Redis Cloud Essentials](https://support.redislabs.com/hc/en-us/articles/27315881501202-Manage-Network-Usage-and-Subscription-Limits-in-Redis-Cloud-Essentials)\n- [Configuring Access Control and RBAC in Redis Cloud](https://support.redislabs.com/hc/en-us/articles/28595965192338-Configuring-Access-Control-and-RBAC-in-Redis-Cloud)\n- [How to run a Failover Test in Redis Cloud](https://support.redislabs.com/hc/en-us/articles/29001166157074-How-to-Run-a-Failover-Test-in-Redis-Cloud)"}],"examples":[]}
{"schema_version":2,"id":"operate/rdi","title":"Redis Data Integration (RDI)","url":"https://redis.io/docs/latest/operate/rdi/","summary":"Keep Redis in sync with a primary database in near real time.","tags":["docs","operate","rdi"],"last_updated":"2026-10-02T17:56:08-07:00","page_type":"content","content_hash":"3d4fd1aba22363b834f7c1f7a25debbe6ac99c21b8ddbd1a270c53e9eef6e4f1","sections":[{"id":"content","title":"Content","role":"content","text":"Redis Data Integration (RDI) is a [change data capture](https://en.wikipedia.org/wiki/Change_data_capture) (CDC) system that tracks changes to the data in a non-Redis source database and makes corresponding changes to a Redis target database. You can use the target as a cache to improve performance because it will typically handle read queries much faster than the source.\n\n![The Data Integration page in the Redis Cloud console, with the Create workspace button and supported source databases.](https://redis.io/docs/latest/images/rc/rdi-get-started.png)\n\nSee the main [RDI docs section](https://redis.io/docs/latest/integrate/redis-data-integration)\nunder [Libraries and tools](https://redis.io/docs/latest/integrate) to learn how to install and use RDI on your own servers.  See the\n[Redis Cloud RDI guide](https://redis.io/docs/latest/operate/rc/rdi) to learn how to set up RDI for a cloud database."}],"examples":[]}
{"schema_version":2,"id":"operate/redisinsight/configuration","title":"Redis Insight configuration settings","url":"https://redis.io/docs/latest/operate/redisinsight/configuration/","summary":"","aliases":["/operate/redisinsight/install/env-variables/","/operate/oss_and_stack/install/install-redis-insight/env-variables/"],"tags":["docs","operate","redisinsight"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"9f139022ca653dbe7aabe5c05f0d9d5f7986b798524438884e5594dba94cf01d","sections":[{"id":"configuration-environment-variables","title":"Configuration environment variables","role":"configuration","text":"| Variable | Purpose | Default | Additional info |\n| --- | --- | --- | --- |\n| RI_APP_PORT | The port that Redis Insight listens on. | <ul><li> Docker: 5540 <li> desktop: 5530 </ul> | See [Express Documentation](https://expressjs.com/en/api.html#app.listen)|\n| RI_APP_HOST | The host that Redis Insight connects to. | <ul><li> Docker: 0.0.0.0 <li> desktop: 127.0.0.1 </ul> | See [Express Documentation](https://expressjs.com/en/api.html#app.listen)|\n| RI_SERVER_TLS_KEY | Private key for HTTPS. | n/a | Private key in [PEM format](https://www.ssl.com/guide/pem-der-crt-and-cer-x-509-encodings-and-conversions/#ftoc-heading-3). Can be a path to a file or a string in PEM format.|\n| RI_SERVER_TLS_CERT | Certificate for supplied private key. | n/a | Public certificate in [PEM format](https://www.ssl.com/guide/pem-der-crt-and-cer-x-509-encodings-and-conversions/#ftoc-heading-3). Can be a path to a file or a string in PEM format.|\n| RI_ENCRYPTION_KEY | Key to encrypt data with. | n/a | Available only for Docker. <br> Redis insight stores sensitive information (database passwords, Workbench history, etc.) locally (using [sqlite3](https://github.com/TryGhost/node-sqlite3)). This variable allows you to store sensitive information encrypted using the specified encryption key. <br />Note: The same encryption key should be provided for subsequent `docker run` commands with the same volume attached to decrypt the information. |\n| RI_LOG_LEVEL | Configures the log level of the application. | `info` | Supported logging levels are prioritized from highest to lowest: <ul> <li>error<li> warn<li>info<li> http<li> verbose<li> debug<li> silly</ul> |\n| RI_FILES_LOGGER | Logs to file. | `true` | By default, you can find log files in the following folders: <ul> <li> Docker: `/data/logs` <li> desktop: `<user-home-dir>/.redisinsight-app/logs` </ul>|\n| RI_STDOUT_LOGGER | Logs to STDOUT. | `true` | |\n| RI_PROXY_PATH | Configures a subpath for a proxy. | n/a | Available only for Docker. |\n| RI_DATABASE_MANAGEMENT | When set to `false`, this disables the ability to manage database connections (adding, editing, or deleting). | `true` | | \n| RI_ACCEPT_TERMS_AND_CONDITIONS | This environment variable allows you to accept the End User License Agreement (EULA) without displaying it in the UI. By setting this variable, you acknowledge that your use of Redis Insight is governed either by your signed agreement with Redis or, if none exists, by the Redis Enterprise Software Subscription Agreement. If neither applies, your use is subject to the Server Side Public License (SSPL). | `true` | |"},{"id":"preconfigure-database-connections","title":"Preconfigure database connections","role":"content","text":"Redis Insight allows you to preconfigure database connections using environment variables or a JSON file, enabling centralized and efficient configuration.\nThere are two ways to preconfigure database connections in Redis Insight Electron and Docker:\n1. Use environment variables.\n1. Use a JSON file."},{"id":"preconfigure-database-connections-using-environment-variables","title":"Preconfigure database connections using environment variables","role":"content","text":"Redis Insight allows you to preconfigure database connections using environment variables.\n\n**NOTES**:\n- To configure multiple database connections, replace the asterisk (*) in each environment variable with a unique identifier for each database connection. If setting up only one connection, you can omit the asterisk, and Redis Insight will default to using 0 as the ID.\n- If you modify environment variables, the changes will take effect after restarting Redis Insight.\n- If you restart Redis Insight without these environment variables, all previously added database connections will be removed.\n\n| Variable | Purpose | Default | Additional info |\n| --- | --- | --- | --- |\n| RI_REDIS_HOST* | Host of a Redis database. | N/A |  |\n| RI_REDIS_PORT* | Port of a Redis database. | `6379` |  |\n| RI_REDIS_ALIAS* | Alias of a database connection. | `{host}:{port}` |  |\n| RI_REDIS_USERNAME* | Username to connect to a Redis database. | `default` |  |\n| RI_REDIS_PASSWORD* | Password to connect to a Redis database. | No password |  |\n| RI_REDIS_TLS* | Indicates whether TLS certificates should be used to connect. | `false` | Accepts `true` or `false` |\n| RI_REDIS_TLS_CA_BASE64* | CA certificate in base64 format. | N/A | Specify a CA certificate in this environment variable or provide a file path using `RI_REDIS_TLS_CA_PATH*`. |\n| RI_REDIS_TLS_CA_PATH* | Path to the CA certificate file. | N/A |  |\n| RI_REDIS_TLS_CERT_BASE64* | Client certificate in base64 format. | N/A | Specify a client certificate in this environment variable or provide a file path using `RI_REDIS_TLS_CERT_PATH*`. |\n| RI_REDIS_TLS_CERT_PATH* | Path to the Client certificate file. | N/A |  |\n| RI_REDIS_TLS_KEY_BASE64* | Private key for the client certificate in base64 format. | N/A | Indicate a private key in this environment variable or use another variable to get it from a file. |\n| RI_REDIS_TLS_KEY_PATH* | Path to private key file. | N/A |  |\n| RI_REDIS_DB | Database index to connect to. | N/A | |"},{"id":"preconfigure-database-connections-using-a-json-file","title":"Preconfigure database connections using a JSON file","role":"content","text":"Redis Insight also allows you to preconfigure database connections using a JSON file.\n\n**NOTES**\n- The JSON file format should match the one used when exporting database connections from Redis Insight.\n- The `id` field in the JSON file should include unique identifiers to avoid conflicts for database connections.\n- Changes to the JSON file will take effect after restarting Redis Insight.\n- If the JSON file is removed, all database connections added via the file will be removed.\n\n| Variable | Purpose | Default | Additional info |\n| --- | --- | --- | --- |\n| RI_PRE_SETUP_DATABASES_PATH | Path to a JSON file containing the database connections to preconfigure  |  |"},{"id":"use-redis-insight-behind-a-reverse-proxy","title":"Use Redis Insight behind a reverse proxy","role":"content","text":"When you configure Redis Insight to run behind a reverse proxy like [NGINX](https://www.nginx.com/), set the request timeout to over 30 seconds on the reverse proxy because some requests can be long-running.\n\nRedis Insight also allows you to manage its connection timeout on the form to configure the connection details. The default timeout is 30 seconds.\n\nHosting Redis Insight behind a prefix path (path-rewriting) is not supported."}],"examples":[]}
{"schema_version":2,"id":"operate/redisinsight","title":"Redis Insight","url":"https://redis.io/docs/latest/operate/redisinsight/","summary":"Install and manage Redis Insight","tags":["docs","operate","redisinsight"],"last_updated":"2026-09-28T17:31:37-07:00","children":[{"id":"operate/redisinsight/install","summary":"Install Redis Insight on AWS, Docker, Kubernetes, and desktop","title":"Install Redis Insight","url":"https://redis.io/docs/latest/operate/redisinsight/install/"},{"id":"operate/redisinsight/configuration","summary":"","title":"Redis Insight configuration settings","url":"https://redis.io/docs/latest/operate/redisinsight/configuration/"},{"id":"operate/redisinsight/proxy","summary":"","title":"Subpath proxy","url":"https://redis.io/docs/latest/operate/redisinsight/proxy/"}],"page_type":"content","content_hash":"e1a507b02df772f378a6516290561057c634d340a93d576646ebeee86935504e","sections":[{"id":"content","title":"Content","role":"content","text":"<div class=\"grid grid-cols-1 md:grid-cols-2 gap-6 my-8 max-w-2xl\">\n  \n</div>\n\nFor information on using Redis Insight, see [these pages](https://redis.io/docs/latest/develop/tools/insight)."}],"examples":[]}
{"schema_version":2,"id":"operate/redisinsight/install","title":"Install Redis Insight","url":"https://redis.io/docs/latest/operate/redisinsight/install/","summary":"Install Redis Insight on AWS, Docker, Kubernetes, and desktop","aliases":["/operate/oss_and_stack/install/install-redis-insight/"],"tags":["docs","operate","redisinsight"],"last_updated":"2026-09-28T17:31:37-07:00","children":[{"id":"operate/redisinsight/install/install-on-desktop","summary":"How to install Redis Insight on the desktop","title":"Install on desktop","url":"https://redis.io/docs/latest/operate/redisinsight/install/install-on-desktop/"},{"id":"operate/redisinsight/install/install-on-docker","summary":"How to install Redis Insight on Docker","title":"Install on Docker","url":"https://redis.io/docs/latest/operate/redisinsight/install/install-on-docker/"},{"id":"operate/redisinsight/install/install-on-k8s","summary":"How to install Redis Insight on Kubernetes","title":"Install on Kubernetes","url":"https://redis.io/docs/latest/operate/redisinsight/install/install-on-k8s/"},{"id":"operate/redisinsight/install/install-on-aws","summary":"How to install Redis Insight on AWS EC2","title":"Install on AWS EC2","url":"https://redis.io/docs/latest/operate/redisinsight/install/install-on-aws/"}],"page_type":"content","content_hash":"bd7c28950d4ecfb6f390c8fa75e63b1a381ac2ed3570f1396159d2894df7039e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"<div class=\"grid grid-cols-1 md:grid-cols-2 gap-6 my-8 max-w-2xl\">\n  \n</div>"},{"id":"installation-options","title":"Installation options","role":"setup","text":"Learn how to install Redis Insight on your desktop, Amazon Web Services (AWS), Docker, and Kubernetes.\n\n<div class=\"grid grid-cols-1 md:grid-cols-2 gap-6 my-8\">\n  \n  \n  \n  \n</div>"}],"examples":[]}
{"schema_version":2,"id":"operate/redisinsight/install/install-on-aws","title":"Install on AWS EC2","url":"https://redis.io/docs/latest/operate/redisinsight/install/install-on-aws/","summary":"How to install Redis Insight on AWS EC2","aliases":["/operate/oss_and_stack/install/install-redis-insight/install-on-aws/"],"tags":["docs","operate","redisinsight"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"3282c77becae574b590737c859a205d55cc643cbe56084e7a5c165e44475d1e3","sections":[{"id":"content","title":"Content","role":"content","text":"This tutorial shows you how to install Redis Insight on an AWS EC2 instance and manage ElastiCache Redis instances using Redis Insight. To complete this tutorial you must have access to the AWS Console and permissions to launch EC2 instances.\n\nStep 1: Launch EC2 Instance\n--------------\n\nNext, launch an EC2 instance.\n\n1. Navigate to EC2 under AWS Console.\n1. Click Launch Instance.\n1. Choose 64-bit Amazon Linux AMI.\n1. Choose at least a t2.medium instance. The size of the instance depends on the memory used by your ElastiCache instance that you want to analyze.\n1. Under Configure Instance:\n   * Choose the VPC that has your ElastiCache instances.\n   * Choose a subnet that has network access to your ElastiCache instances.\n   * Ensure that your EC2 instance has a public IP Address.\n   * Assign the IAM role that you created in Step 1.\n1. Under the storage section, allocate at least 100 GiB storage.\n1. Under security group, ensure that:\n    * Incoming traffic is allowed on port 5540\n    * Incoming traffic is allowed on port 22 only during installation\n1. Review and launch the ec2 instance.\n\nStep 2: Verify permissions and connectivity\n----------\n\nNext, verify that the EC2 instance has the required IAM permissions and can connect to ElastiCache Redis instances.\n\n1. SSH into the newly launched EC2 instance.\n1. Open a command prompt.\n1. Run the command `aws s3 ls`. This should list all S3 buckets.\n    1. If the `aws` command cannot be found, make sure your EC2 instance is based of Amazon Linux.\n1. Next, find the hostname of the ElastiCache instance you want to analyze and run the command `echo info | nc <redis host> 6379`.\n1. If you see some details about the ElastiCache Redis instance, you can proceed to the next step.\n1. If you cannot connect to redis, you should review your VPC, subnet, and security group settings.\n\nStep 3: Install Docker on EC2\n-------\n\nNext, install Docker on the EC2 instance. Run the following commands:\n\n1. `sudo yum update -y`\n1. `sudo yum install -y docker`\n1. `sudo service docker start`\n1. `sudo usermod -a -G docker ec2-user`\n1. Log out and log back in again to pick up the new docker group permissions.\n1. To verify, run `docker ps`. You should see some output without having to run `sudo`.\n\nStep 4: Run Redis Insight in the Docker container\n-------\n\nFinally, install Redis Insight using one of the options described below.\n\n1. If you do not want to persist your Redis Insight data:\n\n[code example]\n2. If you want to persist your Redis Insight data, first attach the Docker volume to the `/data` path and then run the following command:\n\n[code example]\n\nIf the previous command returns a permission error, ensure that the user with `ID = 1000` has the necessary permission to access the volume provided (`redisinsight` in the command above).\n\nFind the IP Address of your EC2 instances and launch your browser at `http://<EC2 IP Address>:5540`. Accept the EULA and start using Redis Insight.\n\nRedis Insight also provides a health check endpoint at `http://<EC2 IP Address>:5540/api/health/` to monitor the health of the running container.\n\nSummary\n------\n\nIn this guide, we installed Redis Insight on an AWS EC2 instance running Docker. As a next step, you should add an ElastiCache Redis Instance and then run the memory analysis."}],"examples":[{"id":"content-ex0","language":"bash","code":"docker run -d --name redisinsight -p 5540:5540 redis/redisinsight:latest","section_id":"content"},{"id":"content-ex1","language":"bash","code":"docker run -d --name redisinsight -p 5540:5540 redis/redisinsight:latest -v redisinsight:/data","section_id":"content"}]}
{"schema_version":2,"id":"operate/redisinsight/install/install-on-desktop","title":"Install on desktop","url":"https://redis.io/docs/latest/operate/redisinsight/install/install-on-desktop/","summary":"How to install Redis Insight on the desktop","tags":["docs","operate","redisinsight"],"last_updated":"2026-09-28T17:31:37-07:00","page_type":"content","content_hash":"2e73f17ccfbf0a3b49869a235c7cb1d571c84231675e484938e9a83370efccd5","sections":[{"id":"overview","title":"Overview","role":"overview","text":"<div class=\"grid grid-cols-1 md:grid-cols-2 gap-6 my-8 max-w-2xl\">\n  \n</div>"},{"id":"supported-operating-systems","title":"Supported operating systems","role":"compatibility","text":"Redis Insight is supported on multiple operating systems:\n\n| Operating System | Supported Versions [^1] |\n|:---              |:---                     |\n| **Windows**      | Windows 11              |\n|                  | Windows 10              |\n| **macOS**        | macOS 15                |\n|                  | macOS 14                |\n|                  | macOS 13                |\n|                  | macOS 12                |\n|                  | macOS 11                |\n|                  | macOS 10.15             |\n| **Ubuntu Linux** | Ubuntu 24.04            |\n|                  | Ubuntu 23.10            |\n|                  | Ubuntu 22.04            |\n|                  | Ubuntu 20.04            |\n| **Debian Linux** | Debian 12               |\n|                  | Debian 11               |\n\n[^1]: Includes later versions of same major or major.minor release."},{"id":"install","title":"Install","role":"setup","text":"Check out the features of the AI-powered assistant, advanced CLI, and intuitive GUI [here](https://redis.io/insight/?utm_source=redisinsight&utm_medium=website&utm_campaign=install_redisinsight#insight-form).\n\nIt is also available on:\n- Microsoft Store\n- Apple Store\n- Snapcraft\n- Flathub\n- [Docker Hub](https://redis.io/docs/latest/operate/redisinsight/install/install-on-docker).\n\nAfter installation, run the Redis Insight application in the same was as you would run other desktop applications."},{"id":"build","title":"Build","role":"content","text":"Alternatively, you can also build Redis Insight from source. See the [wiki](https://github.com/RedisInsight/RedisInsight#build) for instructions."}],"examples":[]}
{"schema_version":2,"id":"operate/redisinsight/install/install-on-docker","title":"Install on Docker","url":"https://redis.io/docs/latest/operate/redisinsight/install/install-on-docker/","summary":"How to install Redis Insight on Docker","aliases":["/operate/oss_and_stack/install/install-redis-insight/install-on-docker/"],"tags":["docs","operate","redisinsight"],"last_updated":"2026-08-10T10:40:34+01:00","page_type":"content","content_hash":"1460053147c0a4b736f4163f59932eef4518b4358de2818ee742bfdfe6c27625","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This tutorial shows how to install Redis Insight on [Docker](https://www.docker.com/) so you can use Redis Insight in development.\nSee a separate guide for installing [Redis Insight on AWS](https://redis.io/docs/latest/operate/redisinsight/install/install-on-aws)."},{"id":"install-docker","title":"Install Docker","role":"setup","text":"The first step is to [install Docker for your operating system](https://docs.docker.com/install/)."},{"id":"run-redis-insight-docker-image","title":"Run Redis Insight Docker image","role":"content","text":"You can install Redis Insight using one of the options described below.\n\n1. If you do not want to persist your Redis Insight data:\n\n[code example]\n2. If you want to persist your Redis Insight data, first attach the Docker volume to the `/data` path and then run the following command:\n\n[code example]\n\nIf the previous command returns a permission error, ensure that the user with `ID = 1000` has the necessary permissions to access the volume provided (`redisinsight` in the command above).\n\nNext, point your browser to `http://localhost:5540`.\n\nRedis Insight also provides a health check endpoint at `http://localhost:5540/api/health/` to monitor the health of the running container."}],"examples":[{"id":"run-redis-insight-docker-image-ex0","language":"bash","code":"docker run -d --name redisinsight -p 5540:5540 redis/redisinsight:latest","section_id":"run-redis-insight-docker-image"},{"id":"run-redis-insight-docker-image-ex1","language":"bash","code":"docker run -d --name redisinsight -p 5540:5540 -v redisinsight:/data redis/redisinsight:latest","section_id":"run-redis-insight-docker-image"}]}
{"schema_version":2,"id":"operate/redisinsight/install/install-on-k8s","title":"Install on Kubernetes","url":"https://redis.io/docs/latest/operate/redisinsight/install/install-on-k8s/","summary":"How to install Redis Insight on Kubernetes","aliases":["/operate/oss_and_stack/install/install-redis-insight/install-on-k8s/"],"tags":["docs","operate","redisinsight"],"last_updated":"2026-10-05T16:11:44+01:00","page_type":"content","content_hash":"8efde69913eddf7c660bcdc858b4e61e45dfe3ce2477086e19d45738f1307c75","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This tutorial shows how to install Redis Insight on [Kubernetes](https://kubernetes.io/) (K8s).\nThis is an easy way to use Redis Insight with a [Redis Enterprise K8s deployment](https://redis.io/docs/latest/operate/kubernetes/)."},{"id":"create-the-redis-insight-deployment-and-service","title":"Create the Redis Insight deployment and service","role":"content","text":"Below is an annotated YAML file that will create a Redis Insight\ndeployment and a service in a K8s cluster.\n\n1. Create a new file named `redisinsight.yaml` with the content below.\n\n[code example]\n\n2. Create the Redis Insight deployment and service:\n\n[code example]\n\n3. Once the deployment and service are successfully applied and complete, access Redis Insight. This can be accomplished by using the `<external-ip>` of the service we created to reach Redis Insight.\n\n[code example]\n\n4. If you are using minikube, run `minikube list` to list the service and access Redis Insight at `http://<minikube-ip>:<minikube-service-port>`.\n[code example]"},{"id":"create-the-redis-insight-deployment-with-persistant-storage","title":"Create the Redis Insight deployment with persistant storage","role":"content","text":"Below is an annotated YAML file that will create a Redis Insight\ndeployment in a K8s cluster. It will assign a peristent volume created from a volume claim template.\nWrite access to the container is configured in an init container. When using deployments\nwith persistent writeable volumes, it's best to set the strategy to `Recreate`. Otherwise you may find yourself\nwith two pods trying to use the same volume.\n\n1. Create a new file `redisinsight.yaml` with the content below.\n\n[code example]\n\n2. Create the Redis Insight deployment and service.\n\n[code example]"},{"id":"create-the-redis-insight-deployment-without-a-service","title":"Create the Redis Insight deployment without a service.","role":"content","text":"Below is an annotated YAML file that will create a Redis Insight\ndeployment in a K8s cluster.\n\n1. Create a new file redisinsight.yaml with the content below\n\n[code example]\n\n2. Create the Redis Insight deployment\n\n[code example]\n\n> [!NOTE]\n> If the deployment will be exposed by a service whose name is 'redisinsight', set `RI_APP_HOST` and `RI_APP_PORT` environment variables to override the environment variables created by the service."},{"id":"run-redis-insight","title":"Run Redis Insight","role":"content","text":"Once the deployment has been successfully applied and the deployment is complete, access Redis Insight. This can be accomplished by exposing the deployment as a K8s Service or by using port forwarding, as in the example below:\n\n[code example]\n\nOpen your browser and point to <http://localhost:5540>"}],"examples":[{"id":"create-the-redis-insight-deployment-and-service-ex0","language":"yaml","code":"# Redis Insight service with name 'redisinsight-service'\napiVersion: v1\nkind: Service\nmetadata:\n  name: redisinsight-service       # name should not be 'redisinsight'\n                                   # since the service creates\n                                   # environment variables that\n                                   # conflicts with redisinsight\n                                   # application's environment\n                                   # variables `RI_APP_HOST` and\n                                   # `RI_APP_PORT`\nspec:\n  type: LoadBalancer\n  ports:\n    - port: 80\n      targetPort: 5540\n  selector:\n    app: redisinsight\n---\n# Redis Insight deployment with name 'redisinsight'\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: redisinsight #deployment name\n  labels:\n    app: redisinsight #deployment label\nspec:\n  replicas: 1 #a single replica pod\n  selector:\n    matchLabels:\n      app: redisinsight #which pods is the deployment managing, as defined by the pod template\n  template: #pod template\n    metadata:\n      labels:\n        app: redisinsight #label for pod/s\n    spec:\n      containers:\n\n      - name:  redisinsight #Container name (DNS_LABEL, unique)\n        image: redis/redisinsight:latest #repo/image\n        imagePullPolicy: IfNotPresent #Installs the latest Redis Insight version\n        volumeMounts:\n        - name: redisinsight #Pod volumes to mount into the container's filesystem. Cannot be updated.\n          mountPath: /data\n        ports:\n        - containerPort: 5540 #exposed container port and protocol\n          protocol: TCP\n      volumes:\n      - name: redisinsight\n        emptyDir: {} # node-ephemeral volume https://kubernetes.io/docs/concepts/storage/volumes/#emptydir","section_id":"create-the-redis-insight-deployment-and-service"},{"id":"create-the-redis-insight-deployment-and-service-ex1","language":"sh","code":"kubectl apply -f redisinsight.yaml","section_id":"create-the-redis-insight-deployment-and-service"},{"id":"create-the-redis-insight-deployment-and-service-ex2","language":"sh","code":"$ kubectl get svc redisinsight-service\nNAME                   CLUSTER-IP       EXTERNAL-IP      PORT(S)         AGE\nredisinsight-service   <cluster-ip>     <external-ip>    80:32143/TCP    1m","section_id":"create-the-redis-insight-deployment-and-service"},{"id":"create-the-redis-insight-deployment-and-service-ex3","language":"plaintext","code":"$ minikube list\n|-------------|----------------------|--------------|---------------------------------------------|\n|  NAMESPACE  |         NAME         | TARGET PORT  |           URL                               |\n|-------------|----------------------|--------------|---------------------------------------------|\n| default     | kubernetes           | No node port |                                             |\n| default     | redisinsight-service |           80 | http://<minikube-ip>:<minikubeservice-port> |\n| kube-system | kube-dns             | No node port |                                             |\n|-------------|----------------------|--------------|---------------------------------------------|","section_id":"create-the-redis-insight-deployment-and-service"},{"id":"create-the-redis-insight-deployment-with-persistant-storage-ex0","language":"yaml","code":"# Redis Insight service with name 'redisinsight-service'\napiVersion: v1\nkind: Service\nmetadata:\n  name: redisinsight-service       # name should not be 'redisinsight'\n                                   # since the service creates\n                                   # environment variables that\n                                   # conflicts with redisinsight\n                                   # application's environment\n                                   # variables `RI_APP_HOST` and\n                                   # `RI_APP_PORT`\nspec:\n  type: LoadBalancer\n  ports:\n    - port: 80\n      targetPort: 5540\n  selector:\n    app: redisinsight\n---\napiVersion: v1\nkind: PersistentVolumeClaim\nmetadata:\n  name: redisinsight-pv-claim\n  labels:\n    app: redisinsight\nspec:\n  accessModes:\n    - ReadWriteOnce\n  resources:\n    requests:\n      storage: 2Gi\n  storageClassName: default\n---\n# Redis Insight deployment with name 'redisinsight'\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: redisinsight #deployment name\n  labels:\n    app: redisinsight #deployment label\nspec:\n  replicas: 1 #a single replica pod\n  strategy:\n    type: Recreate\n  selector:\n    matchLabels:\n      app: redisinsight #which pods is the deployment managing, as defined by the pod template\n  template: #pod template\n    metadata:\n      labels:\n        app: redisinsight #label for pod/s\n    spec:\n      volumes:\n        - name: redisinsight\n          persistentVolumeClaim:\n            claimName: redisinsight-pv-claim\n      initContainers:\n        - name: init\n          image: busybox\n          command:\n            - /bin/sh\n            - '-c'\n            - |\n              chown -R 1000 /data\n          resources: {}\n          volumeMounts:\n            - name: redisinsight\n              mountPath: /data\n          terminationMessagePath: /dev/termination-log\n          terminationMessagePolicy: File\n      containers:\n        - name:  redisinsight #Container name (DNS_LABEL, unique)\n          image: redis/redisinsight:latest #repo/image\n          imagePullPolicy: IfNotPresent #Always pull image\n          volumeMounts:\n          - name: redisinsight #Pod volumes to mount into the container's filesystem. Cannot be updated.\n            mountPath: /data\n          ports:\n          - containerPort: 5540 #exposed container port and protocol\n            protocol: TCP","section_id":"create-the-redis-insight-deployment-with-persistant-storage"},{"id":"create-the-redis-insight-deployment-with-persistant-storage-ex1","language":"sh","code":"kubectl apply -f redisinsight.yaml","section_id":"create-the-redis-insight-deployment-with-persistant-storage"},{"id":"create-the-redis-insight-deployment-without-a-service-ex0","language":"yaml","code":"apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: redisinsight # deployment name\n  labels:\n    app: redisinsight # deployment label\nspec:\n  replicas: 1 # a single replica pod\n  selector:\n    matchLabels:\n      app: redisinsight # which pods is the deployment managing, as defined by the pod template\n  template: # pod template\n    metadata:\n      labels:\n        app: redisinsight # label for pod/s\n    spec:\n      containers:\n      - name: redisinsight # Container name (DNS_LABEL, unique)\n        image: redis/redisinsight:latest # repo/image\n        imagePullPolicy: IfNotPresent # Always pull image\n        env:\n          # If there's a service named 'redisinsight' that exposes the\n          # deployment, we manually set `RI_APP_HOST` and\n          # `RI_APP_PORT` to override the service environment\n          # variables.\n          - name: RI_APP_HOST\n            value: \"0.0.0.0\"\n          - name: RI_APP_PORT\n            value: \"5540\"\n        volumeMounts:\n        - name: redisinsight # Pod volumes to mount into the container's filesystem. Cannot be updated.\n          mountPath: /data\n        ports:\n        - containerPort: 5540 # exposed container port and protocol\n          protocol: TCP\n        livenessProbe: # Probe to check container health\n          httpGet:\n            path: /api/health/ # exposed RI endpoint for healthcheck\n            port: 5540 # exposed container port\n          initialDelaySeconds: 5 # number of seconds to wait after the container starts to perform liveness probe\n          periodSeconds: 5 # period in seconds after which liveness probe is performed\n          failureThreshold: 1 # number of liveness probe failures after which container restarts\n      volumes:\n      - name: redisinsight\n        emptyDir: {} # node-ephemeral volume https://kubernetes.io/docs/concepts/storage/volumes/#emptydir","section_id":"create-the-redis-insight-deployment-without-a-service"},{"id":"create-the-redis-insight-deployment-without-a-service-ex1","language":"sh","code":"kubectl apply -f redisinsight.yaml","section_id":"create-the-redis-insight-deployment-without-a-service"},{"id":"run-redis-insight-ex0","language":"sh","code":"kubectl port-forward deployment/redisinsight 5540","section_id":"run-redis-insight"}]}
{"schema_version":2,"id":"operate/redisinsight/proxy","title":"Subpath proxy","url":"https://redis.io/docs/latest/operate/redisinsight/proxy/","summary":"","tags":["docs","operate","redisinsight"],"last_updated":"2026-10-05T16:11:44+01:00","page_type":"content","content_hash":"7d4dc83060bf47629a7b7b1b7c598e1395cf2dad0d7d4a59501bd2833b6538f0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can enable the subpath proxy by setting the `RI_PROXY_PATH` environment variable.\n\n\nWhen `RI_PROXY_PATH` is being set with a path, Redis Insight is\naccessible only on that subpath. The default routes are given the\nprovided prefix subpath. There isn’t any way to add another proxy behind\nthis one unless the same subpath is used for the new one.\n\n> [!NOTE]\n> Once you set the static subpath environment variable, Redis Insight is only reachable on the provided subpath. The default endpoint won't work."},{"id":"using-redis-insight-behind-a-reverse-proxy","title":"Using Redis Insight behind a reverse proxy","role":"content","text":"When you configure Redis Insight to run behind a reverse proxy like NGINX, set the request timeout to over 30 seconds on the reverse proxy because some requests can be long-running.\n\nRedis Insight also allows you to manage its connection timeout on the form to configure the connection details. The default timeout is 30 seconds.\n\nHosting Redis Insight behind a prefix path (path-rewriting) is not supported."},{"id":"example","title":"Example","role":"example","text":""},{"id":"docker-compose-file","title":"Docker compose file","role":"content","text":"[code example]"},{"id":"nginx-config","title":"nginx config","role":"content","text":"[code example]"},{"id":"login-page","title":"Login page","role":"content","text":"![RedisInsight login page](https://redis.io/docs/latest/images/ri/ri-reverse-proxy-login.png)"},{"id":"after-login","title":"After login","role":"content","text":"![RedisInsight after login](https://redis.io/docs/latest/images/ri/ri-reverse-proxy-post-login.png)"}],"examples":[{"id":"docker-compose-file-ex0","language":"yaml","code":"version: \"3.7\"\nservices:\n  redis-stack:\n    image: redis/redis-stack-server\n    networks:\n      - redis-network\n\n  redisinsight:\n    image: redis/redisinsight\n    environment:\n      - RIPORT=${RIPORT:-5540}\n      - RITRUSTEDORIGINS=http://localhost:9000\n    depends_on:\n      - redis-stack\n    networks:\n      - redis-network\n\n  nginx-basicauth:\n    image: nginx\n    volumes:\n      - ./nginx-basic-auth.conf.template:/etc/nginx/templates/nginx-basic-auth.conf.template\n    ports:\n      - \"${NGINX_PORT:-9000}:${NGINX_PORT:-9000}\"\n    environment:\n      - FORWARD_HOST=redisinsight\n      - FORWARD_PORT=${RIPORT:-5540}\n      - NGINX_PORT=${NGINX_PORT:-9000}\n      - BASIC_USERNAME=${BASIC_USERNAME:-redis}\n      - BASIC_PASSWORD=${BASIC_PASSWORD:-password}\n    command:\n      - bash\n      - -c\n      - |\n        printf \"$$BASIC_USERNAME:$$(openssl passwd -1 $$BASIC_PASSWORD)\\n\" >> /etc/nginx/.htpasswd\n        /docker-entrypoint.sh nginx -g \"daemon off;\"\n    depends_on:\n      - redisinsight\n    networks:\n      - redis-network","section_id":"docker-compose-file"},{"id":"nginx-config-ex0","language":"plaintext","code":"server {\n listen ${NGINX_PORT} default_server;\n\n location / {\n     auth_basic             \"redisinsight\";\n     auth_basic_user_file   .htpasswd;\n\n     proxy_pass             http://${FORWARD_HOST}:${FORWARD_PORT};\n     proxy_read_timeout     900;\n }\n}","section_id":"nginx-config"}]}
{"schema_version":2,"id":"operate/rs/clusters/add-node","title":"Add a node","url":"https://redis.io/docs/latest/operate/rs/clusters/add-node/","summary":"Add a node to your existing Redis Software cluster.","tags":["docs","operate","rs"],"last_updated":"2026-10-02T16:06:07+01:00","page_type":"content","content_hash":"fbe50105ec678d4f676a4677d6ccb6746c2d5d7718f43e21b9bd7e8ac228ca34","sections":[{"id":"overview","title":"Overview","role":"overview","text":"When you install Redis Software on the first node of a cluster, you create the new cluster.\nAfter you install the first node, you can add more nodes to the cluster."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before you add a node to the cluster:\n\n- The clocks on all nodes must always be [synchronized](https://redis.io/docs/latest/operate/rs/clusters/configure/sync-clocks).\n\n    If the clock in the node you are trying to join to the cluster is not synchronized with the nodes already in the cluster,\n    the action fails and an error message is shown indicating that you must synchronize the clocks first.\n\n- You must [update the DNS records](https://redis.io/docs/latest/operate/rs/networking/cluster-dns)\n    each time a node is added or replaced.\n\n- We recommend that you add nodes one after the other rather than in parallel\n    to avoid errors that occur because the connection to the other nodes in the cluster cannot be verified."},{"id":"add-node","title":"Add node","role":"content","text":"To add a node to an existing cluster:\n\n1. [Install the Redis Software installation package](https://redis.io/docs/latest/operate/rs/installing-upgrading) on a clean installation\n    of a [supported operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/supported-platforms).\n\n1. To connect to the Cluster Manager UI of the new Redis Software installation, go to: <https://URL or IP address:8443>\n\n    For example, if you installed Redis Software on a machine with IP address 10.0.1.34, go to `https://10.0.1.34:8443`.\n\n    > [!TIP]\n    > The management UI uses TLS encryption with a default certificate.\n    > You can also [replace the TLS certificate](https://redis.io/docs/latest/operate/rs/security/certificates/updating-certificates)\n    > with a custom certificate.\n\n1. Select **Join cluster**.\n\n1. For **Cluster identification**, enter the internal IP address or DNS name of a node that is a cluster member.\n\n    If the node only has one IP address, enter that IP address.\n\n1. For **Cluster sign in**, enter the credentials of the cluster administrator.\n\n    The cluster administrator is the user account that you create when you configure the first node in the cluster.\n\n1. Click **Next**.\n\n1. Configure storage and network settings:\n\n    1. Enter a path for [*Ephemeral storage*](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/persistent-ephemeral-storage), or leave the default path.\n\n    1. Enter a path for [*Persistent storage*](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/persistent-ephemeral-storage),\n        or leave the default path.\n\n    1. To enable [*Redis Flex or Auto Tiering*](https://redis.io/docs/latest/operate/rs/databases/flash),\n        select **Enable flash storage** and enter the path to the flash storage.\n\n    1. If the cluster is configured to support [rack-zone awareness](https://redis.io/docs/latest/operate/rs/clusters/configure/rack-zone-awareness), set the **Rack-zone ID** for the new node.\n\n    1. If your machine has multiple IP addresses, assign a single IPv4 type address for **Node-to-node communication (internal traffic)** and multiple IPv4/IPv6 type addresses for **External traffic**.\n\n1. Select **Join cluster**.\n\nThe node is added to the cluster.\nYou can see it in the list of nodes in the cluster.\n\nIf you see an error when you add the node, try adding the node again."},{"id":"verify-node","title":"Verify node","role":"content","text":"We recommend that you verify the node is functioning properly using one of the following methods:\n\n- Cluster Manager UI method:\n\n    1. On the **Nodes** screen, click ![More actions button](https://redis.io/docs/latest/images/rs/buttons/button-toggle-actions-vertical.png#no-click) for the node you want to verify.\n    \n    1. Select **Verify node** from the list.\n\n    ![Click the more actions button for a node to access node actions.](https://redis.io/docs/latest/images/rs/screenshots/nodes/secondary-nodes-more-actions.png)\n\n- Command-line method:\n\n    Run the [`rlcheck`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rlcheck) utility from the node's command line."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/clusters/change-node-role","title":"Change node roles","url":"https://redis.io/docs/latest/operate/rs/clusters/change-node-role/","summary":"Change node roles to demote the primary node to a secondary node or promote a secondary node to become the primary node.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:27:03+01:00","page_type":"content","content_hash":"bc1a34326e6ec433f61a5f1afa2111c455fae69a1c42a5d47af674dc2c4f134a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"A Redis Software cluster contains a primary node, which coordinates cluster-wide management operations, and multiple secondary nodes. Nodes with either role can host database shards."},{"id":"demote-primary-node","title":"Demote primary node","role":"example","text":"**Cluster Manager UI:**\n\nTo demote the primary node to a secondary node using the Cluster Manager UI:\n\n1. On the **Nodes** screen, click the **More actions** button (**&vellip;**) for the primary node you want to demote.\n\n    ![Click the more actions button for a node to access node actions.](https://redis.io/docs/latest/images/rs/screenshots/nodes/primary-node-more-actions.png)\n\n1. Select **Set as a secondary node** from the list.\n\n1. Select one of the options to determine the new primary node:\n\n    - **Automatically**: The cluster decides which node becomes the new primary node.\n\n    - **Choose specific node**: You can manually select which node becomes the new primary node.\n\n    ![The Set as a secondary node dialog has two options to select the new primary node, either automatically or manually.](https://redis.io/docs/latest/images/rs/screenshots/nodes/primary-node-set-as-secondary-dialog.png)\n\n1. Click **Confirm**.\n\n**rladmin:**\n\nTo demote the primary node to a secondary node using `rladmin`:\n\n1. Identify the primary node's ID with [`rladmin cluster master`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/master):\n\n    [code example]\n\n1. Run [`rladmin node enslave`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/node/enslave#node-enslave) with the `demote_node` option:\n\n    [code example]\n\n    Replace `<primary-node-ID>` with the ID returned by `rladmin cluster master`."},{"id":"promote-secondary-node","title":"Promote secondary node","role":"content","text":"**Cluster Manager UI:**\n\nTo promote a secondary node to become the primary node using the Cluster Manager UI:\n\n1. On the **Nodes** screen, click the **More actions** button (**&vellip;**) for the secondary node you want to promote.\n\n    ![Click the more actions button for a node to access node actions.](https://redis.io/docs/latest/images/rs/screenshots/nodes/secondary-nodes-more-actions.png)\n\n1. Select **Set as the primary node** from the list.\n\n1. Click **Confirm**.\n\n**rladmin:**\n\nTo promote a secondary node to become the primary node using `rladmin`:\n\n1. To find the IDs of secondary nodes, run [`rladmin status nodes`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status#status-nodes):\n\n    [code example]\n\n    Nodes with the `slave` role are secondary nodes.\n\n1. Run [`rladmin cluster master set`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/master):\n\n    [code example]\n\n    Replace `<secondary-node-ID>` with the ID of the secondary node you want to promote.\n\n\n\nAfter this node becomes the primary node, all cluster management traffic is directed to it."}],"examples":[{"id":"demote-primary-node-ex0","language":"sh","code":"$ rladmin cluster master\n    Node <primary-node-id> is the cluster master node","section_id":"demote-primary-node"},{"id":"demote-primary-node-ex1","language":"sh","code":"rladmin node <primary-node-ID> enslave demote_node","section_id":"demote-primary-node"},{"id":"promote-secondary-node-ex0","language":"sh","code":"$ rladmin status nodes\n    CLUSTER NODES:\n    NODE:ID     ROLE       ADDRESS          EXTERNAL_ADDRESS          HOSTNAME            SHARDS     CORES          FREE_RAM                 PROVISIONAL_RAM          VERSION        STATUS   \n    node:1      master     198.51.100.2                                 3d99db1fdf4b        4/100      6              14.74GB/19.54GB          10.73GB/16.02GB          7.22.0-250     OK       \n    *node:3     slave      198.51.100.4                                 b87cc06c830f        0/100      6              14.74GB/19.54GB          11.22GB/16.02GB          7.22.0-250     OK       \n    node:2      slave      198.51.100.3                                 fc7a3d332458        0/100      6              14.74GB/19.54GB          11.22GB/16.02GB          7.22.0-250     OK","section_id":"promote-secondary-node"},{"id":"promote-secondary-node-ex1","language":"sh","code":"rladmin cluster master set <secondary-node-ID>","section_id":"promote-secondary-node"}]}
{"schema_version":2,"id":"operate/rs/clusters/cluster-recovery","title":"Recover a failed cluster","url":"https://redis.io/docs/latest/operate/rs/clusters/cluster-recovery/","summary":"How to use the cluster configuration file and database data to recover a failed cluster.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:16+01:00","page_type":"content","content_hash":"93e8bce3a669626ca5eb8f5e0361d2e2903bbf1a93dbfb0982e46bce2b74c81e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"When a Redis Software cluster fails,\nyou must use the cluster configuration file and database data to recover the cluster.\n\n> [!NOTE]\n> For cluster recovery in a Kubernetes deployment, see [Recover a Redis Enterprise cluster on Kubernetes](https://redis.io/docs/latest/operate/kubernetes/re-clusters/cluster-recovery).\n\nCluster failure can be caused by:\n\n- A hardware or software failure that causes the cluster to be unresponsive to client requests or administrative actions.\n- More than half of the cluster nodes lose connection with the cluster, resulting in quorum loss.\n\nTo recover a cluster and re-create it as it was before the failure,\nyou must restore the cluster configuration `ccs-redis.rdb` to the cluster nodes.\nTo recover databases in the new cluster, you must restore the databases from persistence files such as backup files, append-only files (AOF), or RDB snapshots.\nThese files are stored in the [persistent storage location](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/persistent-ephemeral-storage).\n\nThe cluster recovery process includes:\n\n1. Install Redis Software on the nodes of the new cluster.\n1. Mount the persistent storage with the recovery files from the original cluster to the nodes of the new cluster.\n1. Recover the cluster configuration on the first node in the new cluster.\n1. Join the remaining nodes to the new cluster.\n1. [Recover the databases](https://redis.io/docs/latest/operate/rs/databases/recover)."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"- We recommend that you recover the cluster to clean nodes.\n    If you use the original nodes,\n    make sure there are no Redis processes running on any nodes in the new cluster.\n- We recommend that you use clean persistent storage drives for the new cluster.\n    If you use the original storage drives,\n    make sure you back up the files on the original storage drives to a safe location.\n- Identify the cluster configuration file that you want to use as the configuration for the recovered cluster.\n    The cluster configuration file is `/ccs/ccs-redis.rdb` on the persistent storage for each node."},{"id":"recover-the-cluster","title":"Recover the cluster","role":"content","text":"1. (Optional) If you want to recover the cluster to the original cluster nodes, uninstall Redis Software from the nodes.\n\n1. [Install Redis Software](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/install-on-linux) on the new cluster nodes.\n\n    The new servers must have the same basic hardware and software configuration as the original servers, including:\n\n    - The same number of nodes\n    - At least the same amount of memory\n    - The same Redis Software version\n    - The same installation user and paths\n\n    > [!NOTE]\n    > The cluster recovery can fail if these requirements are not met.\n\n1. Mount the persistent storage drives with the recovery files to the new nodes.\n    These drives must contain the cluster configuration backup files and database persistence files.\n\n    > [!NOTE]\n    > Make sure that the user redislabs has permissions to access the storage location\n    > of the configuration and persistence files on each of the nodes.\n\n    If you use local persistent storage, place all of the recovery files on each of the cluster nodes.\n\n1. To recover the original cluster configuration, run [`rladmin cluster recover`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/recover) on the first node in the new cluster:\n\n    [code example]\n\n    For example:\n\n    [code example]\n\n    When the recovery command succeeds,\n    this node is configured as the node from the old cluster that has ID 1.\n\n1. To join the remaining servers to the new cluster, run [`rladmin cluster join`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/join) from each new node:\n\n    [code example]\n\n    For example:\n\n    [code example]\n\n1. Run [`rladmin status`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status) to verify the recovered nodes are now active and the databases are pending recovery:\n\n    [code example]\n\n    > [!NOTE]\n    > Make sure that you update your [DNS records](https://redis.io/docs/latest/operate/rs/networking/cluster-dns)\n    > with the IP addresses of the new nodes.\n\nAfter the cluster is recovered, you must [recover the databases](https://redis.io/docs/latest/operate/rs/databases/recover)."}],"examples":[{"id":"recover-the-cluster-ex0","language":"sh","code":"rladmin cluster recover filename [ <persistent_path> | <ephemeral_path> ]<filename> node_uid <node_uid> rack_id <rack_id>","section_id":"recover-the-cluster"},{"id":"recover-the-cluster-ex1","language":"sh","code":"rladmin cluster recover filename /tmp/persist/ccs/ccs-redis.rdb node_uid 1 rack_id 5","section_id":"recover-the-cluster"},{"id":"recover-the-cluster-ex2","language":"sh","code":"rladmin cluster join nodes <cluster_member_ip_address> username <username> password <password> replace_node <node_id>","section_id":"recover-the-cluster"},{"id":"recover-the-cluster-ex3","language":"sh","code":"rladmin cluster join nodes 10.142.0.4 username admin@example.com password mysecret replace_node 2","section_id":"recover-the-cluster"},{"id":"recover-the-cluster-ex4","language":"sh","code":"rladmin status","section_id":"recover-the-cluster"}]}
{"schema_version":2,"id":"operate/rs/clusters/configure/call-home","title":"Call home client","url":"https://redis.io/docs/latest/operate/rs/clusters/configure/call-home/","summary":"The call home client sends your Redis Software cluster's daily usage statistics to Redis.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:16+01:00","page_type":"content","content_hash":"b37bcff19568e11cf0b71364cf417ad9ab532753788f1e6cc28eeea3c86ae89b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The call home client collects data hourly and sends daily usage statistics to Redis with a POST request to `https://usage.redis.io/callHome`. Reports include memory usage, shard details, enabled features, and other operational metrics. To prevent increased load when multiple clusters are running, the daily report is sent at a random time.\n\nThese reports provide insights into license consumption, which helps Redis to ensure performance metrics align with contractual agreements, optimize service delivery, and offer proactive customer support.\nWe recommend contacting [Redis support](https://redis.io/support/) before making changes to call home behavior."},{"id":"collected-data","title":"Collected data","role":"content","text":"The following example shows the data collected hourly for each database:\n\n[code example]"},{"id":"change-data-collection-schedule","title":"Change data collection schedule","role":"content","text":"The cluster collects usage data hourly by default.\n\nTo change the data collection schedule, [update job scheduler settings](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/job_scheduler#put-job-scheduler) for `bdb_usage_report_job_settings` with a REST API request:\n\n[code example]\n\nReplace `cron_expression`'s value with a [`cron` expression](https://en.wikipedia.org/wiki/Cron#CRON_expression) that defines the new data collection schedule."},{"id":"turn-off-call-home-client","title":"Turn off call home client","role":"content","text":"To stop the call home client from sending daily usage statistics to Redis, [update cluster services configuration](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/services_configuration#put-cluster-services_config) for `call_home_agent` with a REST API request:\n\n[code example]"}],"examples":[{"id":"collected-data-ex0","language":"sh","code":"{\n  \"support_package\": true,\n  \"customer_name\": \"string\",\n  \"license_hash\": \"string\",\n  \"usage_data\": [\n    {\n      \"date\": \"2025-03-25T11:42:13.984Z\",\n      \"cluster_UUID\": \"string\",\n      \"cluster_name\": \"string\",\n      \"api_version\": \"string\",\n      \"software_version\": \"string\",\n      \"bdb_uid\": \"string\",\n      \"type\": \"string\",\n      \"shard_type\": \"string\",\n      \"dominant_shard_criteria\": \"string\",\n      \"provisioned_memory\": 0,\n      \"used_memory\": 0,\n      \"master_shards_count\": 0,\n      \"no_eviction\": true,\n      \"persistence\": true,\n      \"backup\": true,\n      \"using_redis_search\": true,\n      \"ops_sec\": 0,\n      \"replication\": true,\n      \"active_active\": true\n    }\n  ]\n}","section_id":"collected-data"},{"id":"change-data-collection-schedule-ex0","language":"sh","code":"PUT /v1/job_scheduler\n{\n  \"bdb_usage_report_job_settings\": {\n    \"enabled\": true,\n    \"cron_expression\": \"*/60 * * * *\"\n  }\n}","section_id":"change-data-collection-schedule"},{"id":"turn-off-call-home-client-ex0","language":"sh","code":"PUT /v1/cluster/services_configuration\n{\n  \"call_home_agent\": {\n    \"operating_mode\": \"disabled\"\n  }\n}","section_id":"turn-off-call-home-client"}]}
{"schema_version":2,"id":"operate/rs/clusters/configure/cluster-settings","title":"Cluster settings","url":"https://redis.io/docs/latest/operate/rs/clusters/configure/cluster-settings/","summary":"You can view and set various cluster settings such as cluster name, email service, time zone, and license.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:16+01:00","page_type":"content","content_hash":"da6d1df3e08fed3b627074e78b08676bcc01d1c1b62784cd9c25a29fac18c90b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can view and set various cluster settings, such as cluster name, email service, time zone, and license, on the **Cluster > Configuration** page."},{"id":"general-configuration-tab","title":"General configuration tab","role":"content","text":""},{"id":"upload-cluster-license-key","title":"Upload cluster license key","role":"content","text":"After purchasing a cluster license and if your account has the \"Admin\" role,\nyou can upload the cluster license key, either during initial\ncluster creation or at any time afterward. The license key defines various\ncluster settings, such as the maximum number of shards you can have in\nthe cluster. For more detailed information see [Cluster license\nkeys](https://redis.io/docs/latest/operate/rs/clusters/configure/license-keys)."},{"id":"view-max-number-of-allowed-shards","title":"View max number of allowed shards","role":"content","text":"The maximum number of allowed shards, which is determined by the cluster license\nkey, appears in the **Max number of shards** field in the **License** section."},{"id":"view-cluster-name","title":"View cluster name","role":"content","text":"The cluster name appears in the **Cluster name** field in the **License** section. This gives a\ncommon name that your team or Redis support can refer to. It is\nespecially helpful if you have multiple clusters."},{"id":"set-time-zone","title":"Set time zone","role":"content","text":"You can change the **Time zone** field to ensure the date, time fields, and log entries in the Cluster Manager UI are shown in your preferred time zone. This setting doesn't affect other system logs or services."},{"id":"alert-settings-tab","title":"Alert settings tab","role":"content","text":"The **Alert Settings** tab lets you configure alerts that are relevant to the entire cluster, such as alerts for cluster utilization, nodes, node utilization, security, and database utilization.\n\nYou can also configure email server settings and [send alerts by email](https://redis.io/docs/latest/operate/rs/monitoring/v1_monitoring#send-alerts-by-email) to relevant users."},{"id":"configure-email-server-settings","title":"Configure email server settings","role":"configuration","text":"To enable email alerts:\n\n1. Enter your email\nserver details in the **Email server settings** section.\n\n1. Select a connection security method:\n\n    - TLS/SSL \n\n    - STARTTLS\n    \n    - None\n\n1. Send a test email to verify your email server settings."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/clusters/configure","title":"Configure clusters","url":"https://redis.io/docs/latest/operate/rs/clusters/configure/","summary":"Configuration options for your Redis Software cluster.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:16+01:00","children":[{"id":"operate/rs/clusters/configure/cluster-settings","summary":"You can view and set various cluster settings such as cluster name, email service, time zone, and license.","title":"Cluster settings","url":"https://redis.io/docs/latest/operate/rs/clusters/configure/cluster-settings/"},{"id":"operate/rs/clusters/configure/license-keys","summary":"The cluster key (or license) enables features and capacity within Redis Enterprise Software","title":"Cluster license keys","url":"https://redis.io/docs/latest/operate/rs/clusters/configure/license-keys/"},{"id":"operate/rs/clusters/configure/sync-clocks","summary":"Sync node clocks to avoid problems with internal custer communication.","title":"Synchronize cluster node clocks","url":"https://redis.io/docs/latest/operate/rs/clusters/configure/sync-clocks/"},{"id":"operate/rs/clusters/configure/rack-zone-awareness","summary":"Rack-zone awareness ensures high availability in the event of a rack or zone failure.","title":"Rack-zone awareness in Redis Software","url":"https://redis.io/docs/latest/operate/rs/clusters/configure/rack-zone-awareness/"},{"id":"operate/rs/clusters/configure/call-home","summary":"The call home client sends your Redis Software cluster's daily usage statistics to Redis.","title":"Call home client","url":"https://redis.io/docs/latest/operate/rs/clusters/configure/call-home/"},{"id":"operate/rs/clusters/configure/sch","summary":"Enable Smart client handoffs for your Redis Software cluster.","title":"Smart client handoffs","url":"https://redis.io/docs/latest/operate/rs/clusters/configure/sch/"}],"page_type":"content","content_hash":"42a52f1d8e89aa07be74b09d99696d2c018982b60f9e76b857076380989f5ec5","sections":[{"id":"content","title":"Content","role":"content","text":"You can manage your Redis Software clusters with several different tools:\n\n- Cluster Manager UI (the web-based user interface)\n- Command-line tools ([rladmin](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin), [redis-cli](https://redis.io/docs/latest/develop/tools/cli), [crdb-cli](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli))\n- [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/clusters/configure/license-keys","title":"Cluster license keys","url":"https://redis.io/docs/latest/operate/rs/clusters/configure/license-keys/","summary":"The cluster key (or license) enables features and capacity within Redis Enterprise Software","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"570d184c2cda5e5cc4dd7a33337753c15f1245a2c5ee264e4133948fa70868c9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The cluster license key enables Redis Software features and determines shard usage and limits.\nYou can add or update a cluster key at any time.\n\nA single license can only be deployed on a single cluster. Each cluster, including passive or cold standby clusters used for disaster recovery, requires its own separate license, regardless of whether all clusters are active simultaneously."},{"id":"trial-mode","title":"Trial mode","role":"content","text":"Trial mode allows all features to be enabled during the trial period.\n\nTrial mode is limited to 30 days and 4 shards, including master and replica shards. A new Redis Software installation starts its 30-day trial period from the day you set up the cluster on the first node.\n\nTrial mode requires a trial license. If you do not provide a license when you create a cluster using the Cluster Manager UI or a [bootstrapping REST API request](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bootstrap#post-bootstrap), a trial cluster license is generated by default."},{"id":"view-cluster-license-key","title":"View cluster license key","role":"content","text":"To view the cluster license key, use a [`GET /v1/license`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/license#get-license) REST API request. For a list of returned fields, see the [response section](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/license#get-response).\n\n> [!NOTE]\n> As of version 7.2, Redis Software enforces shard limits by shard types, RAM or flash, instead of the total number of shards. The flash shards limit only appears in the UI if Auto Tiering is enabled."},{"id":"update-cluster-license","title":"Update cluster license","role":"content","text":"> [!NOTE]\n> After you add a cluster key, you cannot remove the key to return the cluster to trial mode.\n\nYou can update the cluster license key:\n\n- During cluster setup using the Cluster Manager UI or CLI\n\n- After cluster setup using the Cluster Manager UI:\n\n    1. Go to **Cluster > Configuration > General > License**.\n    \n    1. Select **Change**.\n\n    1. Upload or enter your cluster license key.\n    \n    1. Select **Save**.\n\nYou can update an existing cluster key at any time.\nRedis Software checks its validity for the following:\n- Cluster name\n- Activation and expiration dates\n- Shard usage and limits\n- Features\n\nIf saving a new cluster key fails, the operation returns an error with the failure's cause.\nIn this case, the existing key stays in effect."},{"id":"expired-cluster-license","title":"Expired cluster license","role":"content","text":"When the license is expired:\n\n- You cannot do these actions:\n\n    - Change database settings, including security and configuration options.\n\n    - Add a database.\n\n- You can do these actions:\n\n    - Sign in to the Cluster Manager UI and view settings and metrics at all resolutions for the cluster, nodes, and databases.\n\n    - Change cluster settings, including the license key, security for administrators, and cluster alerts.\n\n    - Fail over when a node fails and explicitly migrate shards between nodes.\n\n    - Upgrade a node to a new version of Redis Software."},{"id":"configure-license-expiration-alert","title":"Configure license expiration alert","role":"configuration","text":"By default, a cluster license alert is scheduled to occur 7 days before the cluster license expiration date.\n\n![An alert appears in the Cluster Manager UI that says 'Your license will expire on day-month-year time. Contact support to renew your license.'](https://redis.io/docs/latest/images/rs/screenshots/cluster/cluster-license-expiration-alert.png)\n\nYou can adjust the threshold value of the cluster license expiration alert based on how far in advance you want to be notified of the license expiration. The value should be within a reasonable range that allows your organization enough time to take action, such as renewing the license, before it expires.\n\nTo change the cluster license alert settings, use one of the following methods:\n\n- Cluster Manager UI:\n\n    1. On the **Cluster > Configuration** screen, select the **Alerts Settings** tab.\n\n    1. Click **Edit**.\n\n    1. In the **Cluster utilization** section, enable the alert setting \"License expiry notifications will be sent \\<value\\> days before the license expires\" and enter a new value in the box.\n\n        ![Cluster utilization alert settings.](https://redis.io/docs/latest/images/rs/screenshots/cluster/cluster-config-alert-settings-utilization.png)\n\n    1. Click **Save**.\n\n\n- [Update cluster](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster#put-cluster) REST API request:\n\n    The following example changes the cluster license alert to occur 30 days before the cluster license expiration date:\n\n    [code example]"},{"id":"monitor-cluster-license","title":"Monitor cluster license","role":"content","text":"As of version 7.2, Redis Software exposes the license quotas and the shards consumption metrics in the Cluster Manager UI or via the [Prometheus integration](https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise).\n\nThe `cluster_shards_limit` metric displays the total shard limit by shard type.\n\nExamples:\n- `cluster_shards_limit{cluster=\"mycluster.local\",shard_type=\"ram\"} 100.0`\n- `cluster_shards_limit{cluster=\"mycluster.local\",shard_type=\"flash\"} 50.0`\n\nThe `bdb_shards_used` metric displays the used shard count by database and shard type.\n\nExamples:\n- `bdb_shards_used{bdb=\"2\",cluster=\"mycluster.local\",shard_type=\"ram\"} 86.0`\n- `bdb_shards_used{bdb=\"3\",cluster=\"mycluster.local\",shard_type=\"flash\"} 23.0`"}],"examples":[{"id":"configure-license-expiration-alert-ex0","language":"sh","code":"PUT /v1/cluster\n    {\n        \"alert_settings\": {\n            \"cluster_license_about_to_expire\": {\n                \"enabled\": true,\n                \"threshold\": \"30\"\n            }\n        }\n    }","section_id":"configure-license-expiration-alert"}]}
{"schema_version":2,"id":"operate/rs/clusters/configure/rack-zone-awareness","title":"Rack-zone awareness in Redis Software","url":"https://redis.io/docs/latest/operate/rs/clusters/configure/rack-zone-awareness/","summary":"Rack-zone awareness ensures high availability in the event of a rack or zone failure.","tags":["docs","operate","rs","kubernetes"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"4a74986d8f19838c0c22ed1aec2290b2d8a3615c2cf34cd81841eddab32078a9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Rack-zone awareness helps ensure high availability in the event of a rack or zone failure.\n\nWhen you enable rack-zone awareness in a Redis Software cluster, you assign\na [rack-zone ID](#rack-zone-id-rules) to each node. This ID is used to map the node to a\nphysical rack or logical zone. The cluster can then ensure that primary shards, corresponding replica shards, and associated endpoints are placed on [nodes in different racks or zones](#node-layout-guidelines).\n\nIn the event of a rack or zone failure, the replicas and endpoints in the remaining racks and zones are promoted. This ensures high availability when a rack or zone fails.\n\nThere is no limitation on the number of racks and zones per cluster. Each\nnode can belong to a different rack or multiple nodes can belong to the\nsame rack.\n\nRack-zone awareness affects various cluster, node, and database actions, such as node rebalancing, node removal, node replacement, shard and endpoint migration, and database failover."},{"id":"rack-zone-id-rules","title":"Rack-zone ID rules","role":"content","text":"The rack-zone ID must comply with the following rules:\n\n- Maximum length of 63 characters.\n- Characters consist of letters, digits, and hyphens ('-'). Underscores ('_') are also accepted as of Redis Software [6.4.2-61](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-61).\n- ID starts with a letter and ends with a letter or a digit.\n\n> [!NOTE]\n> Rack-zone IDs are **case-insensitive** (uppercase and lowercase letter are treated as the same)."},{"id":"node-layout-guidelines","title":"Node layout guidelines","role":"content","text":"Avoid placing the majority of nodes in one availability zone.\n\nIf a Redis Software cluster consists of three nodes (the recommended minimum), follow these guidelines:\n\n- For high availability, the three nodes must be distributed across three *distinct* racks or zones.\n\n- When using availability zones, all three zones should exist within the same *region* to avoid potential latency issues."},{"id":"set-up-rack-zone-awareness","title":"Set up rack-zone awareness","role":"content","text":"To enable rack-zone awareness, you need to configure it for the\ncluster, nodes, and [databases](#enable-database-rack-zone-awareness).\n\n> [!NOTE]\n> - After rack-zone awareness is enabled for a cluster, it cannot be turned off.\n>\n> - After a rack ID is assigned to a node, it cannot be changed."},{"id":"new-cluster","title":"New cluster","role":"content","text":"You can set up rack-zone awareness for the cluster and its nodes during [cluster creation](https://redis.io/docs/latest/operate/rs/clusters/new-cluster-setup):\n\n1. In the **Cluster** screen's **Configuration** section, enable **Rack zone awareness**.\n\n1. Select **Next** to continue to the **Node** configuration screen.\n\n1. Enter a **Rack-zone ID** for the current node.\n\n1. Finish [cluster setup](https://redis.io/docs/latest/operate/rs/clusters/new-cluster-setup).\n\n1. For every [node you add to the cluster](https://redis.io/docs/latest/operate/rs/clusters/add-node), assign a different **Rack-zone ID**."},{"id":"existing-cluster","title":"Existing cluster","role":"content","text":"If you did not configure rack-zone awareness during cluster creation, you can configure rack-zone awareness for existing clusters using the [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api):\n\n1. For each node in the cluster, assign a different rack-zone ID using the REST API to [update the node](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/nodes#put-node):\n\n    [code example]\n\n1. [Update the cluster policy](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/policy#put-cluster-policy) to enable rack-zone awareness:\n\n    [code example]"},{"id":"set-up-two-dimensional-rack-zone-awareness","title":"Set up two-dimensional rack-zone awareness","role":"content","text":"As of Redis Software version 7.22, you can assign a `second_rack_id` to set up two-dimensional rack-zone awareness.\n\nYou can use two-dimensional rack-zone awareness to create logical zones within a zone or rack."},{"id":"new-clusters","title":"New clusters","role":"content","text":"To set up two-dimensional rack-zone awareness during cluster creation, assign a `second_rack_id` to each node in the cluster in addition to the `rack_id` using the [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api) or [rladmin](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin).\n\n#### REST API method\n\nTo create a new cluster with two-dimensional rack-zone awareness, you can use [bootstrap REST API requests](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bootstrap#post-bootstrap):\n\n1. Create the new cluster on the first node, set `rack_aware` to `true`, and assign a `rack_id` and `second_rack_id` to the first node:\n\n    [code example]\n\n1. Join each new node you want to add to the cluster and assign a different `rack_id` and `second_rack_id` to it:\n\n    [code example]\n\n#### Command-line method\n\nTo create a new cluster with two-dimensional rack-zone awareness using the command line:\n\n1. Run [`rladmin cluster create`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/create) to create the initial cluster on one node, enable rack-zone awareness, and assign a `rack_id` and `second_rack_id`:\n\n    [code example]\n\n1. Run [`rladmin cluster join`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/join) for each new node you want to add to the cluster and assign a different `rack_id` and `second_rack_id`:\n\n    [code example]"},{"id":"existing-clusters","title":"Existing clusters","role":"content","text":"You can configure two-dimensional rack-zone awareness for existing clusters using the [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api).\n\nFor each node in the cluster, assign a different `second_rack_id` using the REST API to [update the node](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/nodes#put-node):\n\n[code example]"},{"id":"enable-database-rack-zone-awareness","title":"Enable database rack-zone awareness","role":"content","text":"Before you can enable rack-zone awareness for a database, you must configure rack-zone awareness for the cluster and its nodes. For more information, see [set up rack-zone awareness](#set-up-rack-zone-awareness).\n\n\n\nTo enable rack-zone awareness for a database, use a [REST API request](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#put-bdbs):\n\n[code example]"},{"id":"rearrange-database-shards","title":"Rearrange database shards","role":"content","text":"After you enable rack-zone awareness for an existing database, you should generate an optimized shard placement blueprint using the [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api) and use it to rearrange the shards in different racks or zones.\n\n1. [Generate an optimized shard placement blueprint](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/optimize_shards_placement#get-bdbs-actions-optimize-shards-placement):\n\n    1. Send the following `GET` request:\n\n        [code example]\n\n    1. Copy the `cluster-state-id` from the response headers.\n\n    1. Copy the JSON response body, which represents the new shard placement blueprint.\n\n1. [Rearrange the database shards](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/optimize_shards_placement#put-bdbs-rearrange-shards) according to the new shard placement blueprint:\n\n    1. In the request headers, include the <nobr>`cluster-state-id`</nobr> from the `optimize_shards_placement` response.\n\n    1. Add the following JSON in the request body and replace <nobr>`<shard placement blueprint>`</nobr> with the new blueprint:\n\n        [code example]\n\n    1. Send the following `PUT` request to rearrange the shards:\n\n        [code example]"},{"id":"shard-placement-without-rack-zone-awareness","title":"Shard placement without rack-zone awareness","role":"content","text":"Even if a database has rack-zone awareness turned off, the cluster still ensures that primary and replica shards are placed on distinct nodes."}],"examples":[{"id":"existing-cluster-ex0","language":"sh","code":"PUT /v1/nodes/<node-ID>\n    { \"rack_id\": \"rack-zone-ID\" }","section_id":"existing-cluster"},{"id":"existing-cluster-ex1","language":"sh","code":"PUT /v1/cluster/policy\n    { \"rack_aware\": true }","section_id":"existing-cluster"},{"id":"new-clusters-ex0","language":"sh","code":"POST /v1/bootstrap/create_cluster\n    {\n      \"action\": \"create_cluster\",\n      \"cluster\": {\n        \"nodes\": [],\n        \"name\": \"<cluster.fqdn>\"\n      },\n      \"credentials\": {\n        \"username\": \"<admin-email>\",\n        \"password\": \"<admin-password>\"\n      },\n      \"node\": {\n        \"identity\": {\n            \"rack_id\": \"<availability-zone-ID>\",\n            \"second_rack_id\": \"<rack-ID>\"\n        }\n      },\n      \"policy\": {\n        \"rack_aware\": true\n      }\n    }","section_id":"new-clusters"},{"id":"new-clusters-ex1","language":"sh","code":"POST /v1/bootstrap/join_cluster\n    {\n      \"action\": \"join_cluster\",\n      \"cluster\": {\n         \"nodes\": [],\n         \"name\": \"<cluster.fqdn>\"\n      },\n      \"credentials\": {\n        \"username\": \"<admin-email>\",\n        \"password\": \"<admin-password>\"\n      },\n      \"node\": {\n        \"identity\": {\n            \"rack_id\": \"<availability-zone-ID>\",\n            \"second_rack_id\": \"<rack-ID>\"\n        }\n      }\n    }","section_id":"new-clusters"},{"id":"new-clusters-ex2","language":"sh","code":"$ rladmin cluster create name <cluster-name> \\\n        username <admin-email> \\\n        password <admin-password> \\\n        rack_aware \\\n        rack_id <node-rack-ID> \\\n        second_rack_id <second-node rack-ID>","section_id":"new-clusters"},{"id":"new-clusters-ex3","language":"sh","code":"$ rladmin cluster join nodes <node-IP-address> \\\n        username <admin-email> \\\n        password <admin-password> \\\n        rack_id <node-rack ID> \\\n        second_rack_id <second-node-rack-ID>","section_id":"new-clusters"},{"id":"existing-clusters-ex0","language":"sh","code":"PUT /v1/nodes/<node-ID>\n{ \"second_rack_id\": \"rack-ID\" }","section_id":"existing-clusters"},{"id":"enable-database-rack-zone-awareness-ex0","language":"sh","code":"PUT /v1/bdbs/<database-ID>\n{ \"rack_aware\": true }","section_id":"enable-database-rack-zone-awareness"},{"id":"rearrange-database-shards-ex0","language":"sh","code":"GET /v1/bdbs/<database-ID>/actions/optimize_shards_placement","section_id":"rearrange-database-shards"},{"id":"rearrange-database-shards-ex1","language":"sh","code":"{\n          \"shards_blueprint\": <shard placement blueprint>\n        }","section_id":"rearrange-database-shards"},{"id":"rearrange-database-shards-ex2","language":"sh","code":"PUT /v1/bdbs/<database-ID>","section_id":"rearrange-database-shards"}]}
{"schema_version":2,"id":"operate/rs/clusters/configure/sch","title":"Smart client handoffs","url":"https://redis.io/docs/latest/operate/rs/clusters/configure/sch/","summary":"Enable Smart client handoffs for your Redis Software cluster.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:16+01:00","page_type":"content","content_hash":"288424176a6e5c75f4e1f91a3e6be619bf4be42b1fa823d781fae15b1573ec77","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Smart client handoffs (SCH) is a feature of Redis Cloud and Redis Software servers that lets them actively notify clients about planned server maintenance shortly before it happens. This lets a client reconnect or otherwise respond gracefully without significant interruptions in service.\nSee [Smart client handoffs](https://redis.io/docs/latest/develop/clients/sch) for more information about SCH.\n\nSCH is supported for Redis Software from v8.0.2 on, and OSS Cluster API from v8.0.16 on.\nThe degree of support for SCH depends on the specific upgrade method you use, as detailed in the table below.\n\n| Upgrade method | SCH support | Expected behavior |\n| --- | --- | --- |\n| [Rolling upgrade](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-cluster#rolling-upgrade) | Full | New nodes and old ones removed sequentially. SCH pre-handoffs and relaxed timeouts greatly reduce disruptions during the upgrade. |\n| [In-place upgrade](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-cluster#in-place-upgrade) | Partial | Relaxed timeouts reduce errors but there are no pre-handoffs. Disconnections occur when processes are replaced during the upgrade, so clients should rely on auto-reconnect, which will cause brief lapses in service. |\n| [Maintenance mode](https://redis.io/docs/latest/operate/rs/clusters/maintenance-mode) | Full | SCH is fully supported during hardware or OS patching operations. Pre-handoffs and relaxed timeouts minimize application impact. |\n\n\nTo enable SCH on a Redis Software server, you must use the\n[/v1/cluster](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster#put-cluster)\nREST API request to set the `client_maint_notifications` option to `true`.\nThe example below shows how to do this using the\n[`curl`](https://curl.se/) command line utility:\n\n[code example]"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"During a rolling upgrade with SCH enabled, when some slot's shards have migrated to a different node but are still reachable from their original node, results of the `SCAN` command might be inconsistent: it might show some of the keys, it might show keys that are not reachable from that node, or it might show no keys. When the slot migration is done, this issue will fix itself."}],"examples":[{"id":"overview-ex0","language":"bash","code":"curl -k -X PUT -H \"accept: application/json\" \\\n    -H \"content-type: application/json\" \\\n    -u \"<username>:<password>\" \\\n    -d '{ \"client_maint_notifications\": true }' \\\n    https://<host>:<port>/v1/cluster","section_id":"overview"}]}
{"schema_version":2,"id":"operate/rs/clusters/configure/sync-clocks","title":"Synchronize cluster node clocks","url":"https://redis.io/docs/latest/operate/rs/clusters/configure/sync-clocks/","summary":"Sync node clocks to avoid problems with internal custer communication.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:16+01:00","page_type":"content","content_hash":"9a1660de6f8f4ff0f6b0af07d1ba1c82fcadaced071a736a9682796b9335514b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"To avoid problems with internal cluster communications that can impact your data integrity,\nmake sure that the clocks on all of the cluster nodes are synchronized using Chrony and/or NTP.\n\nWhen you install Redis Software,\nthe install script checks if Chrony or NTP is running.\nIf they are not, the installation process asks for permission to configure a scheduled Cron job.\nThis should make sure that the node's clock is always synchronized.\nIf you did not confirm configuring this job during the installation process,\nyou must use the Network Time Protocol (NTP) regularly to make sure that all server clocks are synchronized.\n\nTo synchronize the server clock, run the command that is appropriate for your operating system."},{"id":"set-up-ntp-synchronization","title":"Set up NTP synchronization","role":"content","text":"To set up NTP synchronization, see the following sections for instructions for specific operating systems."},{"id":"ubuntu-1804-and-ubuntu-2004","title":"Ubuntu 18.04 and Ubuntu 20.04","role":"content","text":"1. Install Chrony, a replacement for NTP:\n   [code example]\n   \n1. Edit the Chrony configuration file:\n   [code example]\n\n1. Add `server pool.ntp.org` to the file, replace `pool.ntp.org` with your own NTP server, then save.\n\n1. Restart the Chrony service: \n   [code example]\n\n1. Check the Chrony service status:\n   [code example]\n   \nFor more details, refer to the official [Ubuntu 20.04 documentation](https://ubuntu.com/server/docs/network-ntp)."},{"id":"rhel-7","title":"RHEL 7","role":"content","text":"1. Install Chrony, a replacement for NTP:\n   [code example]\n\n1. Edit the Chrony configuration file:\n   [code example]\n\n1. Add `server pool.ntp.org` to the file, replace `pool.ntp.org` with your own NTP server, then save.\n\n1. Enable and start the Chrony service:\n   [code example]\n\n1. Check the Chrony service status:\n   [code example]\n\nFor more details, refer to the official [RHEL 7 documentation](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/system_administrators_guide/ch-configuring_ntp_using_the_chrony_suite#sect-Using_chrony)."},{"id":"rhel-8-and-rhel-9","title":"RHEL 8 and RHEL 9","role":"content","text":"1. Install Chrony, a replacement for NTP:\n   [code example]\n\n1. Edit the Chrony configuration file:\n   [code example]\n\n1. Add `server pool.ntp.org` to the file, replace `pool.ntp.org` with your own NTP server, then save.\n\n1. Enable and start the Chrony service:\n   [code example]\n\n1. Check the Chrony service status:\n   [code example]\n\nFor more details, refer to the official [RHEL 8 and 9 documentation](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/configuring_basic_system_settings/configuring-time-synchronization_configuring-basic-system-settings)."},{"id":"amazon-linux-2","title":"Amazon Linux 2","role":"content","text":"1. Install Chrony, a replacement for NTP:\n   [code example]\n\n1. Edit the Chrony configuration file:\n   [code example]\n\n1. Add `server pool.ntp.org` to the file, replace `pool.ntp.org` with your own NTP server, then save.\n\n1. Enable and start the Chrony service:\n   [code example]\n\n1. Check the Chrony service status:\n   [code example]\n\nFor more details, refer to the official [Amazon Linux 2 documentation](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/set-time.html).\n\nIf you are using Active-Active databases, you must use [Network Time Service (ntpd)](https://redis.io/docs/latest/operate/rs/databases/active-active#network-time-service-ntp-or-chrony)\nto synchronize OS clocks consistently across clusters to handle conflict resolution according to the OS time."}],"examples":[{"id":"ubuntu-1804-and-ubuntu-2004-ex0","language":"sh","code":"sudo apt install chrony","section_id":"ubuntu-1804-and-ubuntu-2004"},{"id":"ubuntu-1804-and-ubuntu-2004-ex1","language":"sh","code":"sudo nano /etc/chrony/chrony.conf","section_id":"ubuntu-1804-and-ubuntu-2004"},{"id":"ubuntu-1804-and-ubuntu-2004-ex2","language":"sh","code":"sudo systemctl restart chrony","section_id":"ubuntu-1804-and-ubuntu-2004"},{"id":"ubuntu-1804-and-ubuntu-2004-ex3","language":"sh","code":"sudo systemctl status chrony","section_id":"ubuntu-1804-and-ubuntu-2004"},{"id":"rhel-7-ex0","language":"sh","code":"sudo yum install chrony","section_id":"rhel-7"},{"id":"rhel-7-ex1","language":"sh","code":"sudo nano /etc/chrony.conf","section_id":"rhel-7"},{"id":"rhel-7-ex2","language":"sh ","code":"sudo systemctl enable chronyd && sudo systemctl start chronyd","section_id":"rhel-7"},{"id":"rhel-7-ex3","language":"sh","code":"sudo systemctl status chronyd","section_id":"rhel-7"},{"id":"rhel-8-and-rhel-9-ex0","language":"sh","code":"sudo dnf install chrony","section_id":"rhel-8-and-rhel-9"},{"id":"rhel-8-and-rhel-9-ex1","language":"sh","code":"sudo nano /etc/chrony.conf","section_id":"rhel-8-and-rhel-9"},{"id":"rhel-8-and-rhel-9-ex2","language":"sh","code":"sudo systemctl enable chronyd && sudo systemctl start chronyd","section_id":"rhel-8-and-rhel-9"},{"id":"rhel-8-and-rhel-9-ex3","language":"sh","code":"sudo systemctl status chronyd","section_id":"rhel-8-and-rhel-9"},{"id":"amazon-linux-2-ex0","language":"sh","code":"sudo yum install chrony","section_id":"amazon-linux-2"},{"id":"amazon-linux-2-ex1","language":"sh","code":"sudo nano /etc/chrony.conf","section_id":"amazon-linux-2"},{"id":"amazon-linux-2-ex2","language":"sh","code":"sudo systemctl enable chronyd && sudo systemctl start chronyd","section_id":"amazon-linux-2"},{"id":"amazon-linux-2-ex3","language":"sh","code":"sudo systemctl status chronyd","section_id":"amazon-linux-2"}]}
{"schema_version":2,"id":"operate/rs/clusters","title":"Manage clusters","url":"https://redis.io/docs/latest/operate/rs/clusters/","summary":"Administrative tasks and information related to the Redis Software cluster.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:16+01:00","children":[{"id":"operate/rs/clusters/new-cluster-setup","summary":"How to set up a new cluster using the management UI.","title":"Set up a new cluster","url":"https://redis.io/docs/latest/operate/rs/clusters/new-cluster-setup/"},{"id":"operate/rs/clusters/add-node","summary":"Add a node to your existing Redis Software cluster.","title":"Add a node","url":"https://redis.io/docs/latest/operate/rs/clusters/add-node/"},{"id":"operate/rs/clusters/quorum-node","summary":"To reduce infrastructure costs, you can set up a quorum-only node with minimal resources.","title":"Set up a quorum node","url":"https://redis.io/docs/latest/operate/rs/clusters/quorum-node/"},{"id":"operate/rs/clusters/configure","summary":"Configuration options for your Redis Software cluster.","title":"Configure clusters","url":"https://redis.io/docs/latest/operate/rs/clusters/configure/"},{"id":"operate/rs/clusters/optimize","summary":"Configuration changes and information you can use to optimize your performance and memory usage.","title":"Optimize clusters","url":"https://redis.io/docs/latest/operate/rs/clusters/optimize/"},{"id":"operate/rs/clusters/maintenance-mode","summary":"Prepare a cluster node for maintenance.","title":"Maintenance mode for cluster nodes","url":"https://redis.io/docs/latest/operate/rs/clusters/maintenance-mode/"},{"id":"operate/rs/clusters/change-node-role","summary":"Change node roles to demote the primary node to a secondary node or promote a secondary node to become the primary node.","title":"Change node roles","url":"https://redis.io/docs/latest/operate/rs/clusters/change-node-role/"},{"id":"operate/rs/clusters/cluster-recovery","summary":"How to use the cluster configuration file and database data to recover a failed cluster.","title":"Recover a failed cluster","url":"https://redis.io/docs/latest/operate/rs/clusters/cluster-recovery/"},{"id":"operate/rs/clusters/remove-node","summary":"Remove a node from your Redis Software cluster.","title":"Remove a node","url":"https://redis.io/docs/latest/operate/rs/clusters/remove-node/"},{"id":"operate/rs/clusters/replace-node","summary":"Replace a node in your cluster that is down.","title":"Replace a cluster node","url":"https://redis.io/docs/latest/operate/rs/clusters/replace-node/"},{"id":"operate/rs/clusters/logging","summary":"Management actions performed with Redis Software are logged to make sure system management tasks are appropriately performed or monitored by administrators and for compliance with regulatory standards.","title":"Logging events","url":"https://redis.io/docs/latest/operate/rs/clusters/logging/"}],"page_type":"content","content_hash":"c8a2db0832400efb74b0d70dff201574944479ca2d5f8f7f69cda6461deb507b","sections":[{"id":"content","title":"Content","role":"content","text":"You can manage your Redis Software clusters with several different tools:\n\n- Cluster Manager UI (the web-based user interface)\n- Command-line tools ([rladmin](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin), [redis-cli](https://redis.io/docs/latest/develop/tools/cli), [crdb-cli](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli))\n- [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/clusters/logging/alerts-events","title":"Alerts and events","url":"https://redis.io/docs/latest/operate/rs/clusters/logging/alerts-events/","summary":"Logged alerts and events","tags":["docs","operate","rs"],"last_updated":"2025-11-25T01:29:27+02:00","page_type":"content","content_hash":"8bf68fd5cd8afff64827f95bb9458508d5687d693cd32a76f5aea37ba5b8ff0a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The following alerts and events can appear in `syslog` and the Cluster Manager UI logs."},{"id":"alerts","title":"Alerts","role":"content","text":"| Alert | UI message | Severity | Notes |\n|-------|------------|----------|-------|\n| aof_slow_disk_io | Redis performance is degraded as a result of disk I/O limits | True: error, False: info | Node alert |\n| authentication_err | Error authenticating with the source database | error | BDB event |\n| backup_delayed | Periodic backup has been delayed for longer than `<threshold>` minutes | True: warning, False: info | BDB alert; Has threshold parameter in the data section of the log entry |\n| cpu_utilization | CPU utilization has reached `<threshold>`% | True: warning, False: info | Node alert; Has global_threshold parameter in the key/value section of the log entry |\n| even_node_count | True high availability requires an odd number of nodes | True: warning, False: info | Cluster alert |\n| ephemeral_storage | Ephemeral storage has reached `<threshold>`% of its capacity | True: warning, False: info | Node alert; Has global_threshold parameter in the key/value section of the log entry |\n| free_flash | Flash storage has reached `<threshold>`% of its capacity | True: warning, False: info | Node alert; Has global_threshold parameter in the key/value section of the log entry |\n| high_latency | Latency is higher than `<threshold>` milliseconds | True: warning, False: info | BDB alert; Has threshold parameter in the key/value section of the log entry |\n| high_syncer_lag | Sync lag is higher than `<threshold>` seconds | True: warning, False: info | BDB alert; Has threshold parameter in the key/value section of the log entry |\n| high_throughput | Throughput is higher than `<threshold>` RPS (requests per second) | True: warning, False: info | BDB alert; Has threshold parameter in the key/value section of the log entry |\n| inconsistent_rl_sw | Not all nodes in the cluster are running the same Redis Labs Enterprise Cluster version | True: warning, False: info | Cluster alert |\n| insufficient_disk_aofrw | Node has insufficient disk space for AOF rewrite | True: error, False: info | Node alert |\n| memory | Node memory has reached `<threshold>`% of its capacity | True: warning, False: info | Node alert; Has global_threshold parameter in the key/value section of the log entry |\n| multiple_nodes_down | Multiple cluster nodes are down - this might cause data loss | True: warning, False: info | Cluster alert |\n| net_throughput | Network throughput has reached `<threshold>` MB/s | True: warning, False: info | Node alert; Has global_threshold parameter in the key/value section of the log entry |\n| ocsp_query_failed | Failed querying OCSP server | True: error, False: info | Cluster alert |\n| ocsp_status_revoked | OCSP status revoked | True: error, False: info | Cluster alert |\n| persistent_storage | Persistent storage has reached `<threshold>`% of its capacity | True: warning, False: info | Node alert; Has global_threshold parameter in the key/value section of the log entry |\n| ram_dataset_overhead | RAM Dataset overhead in a shard has reached `<threshold>`% of its RAM limit | True: warning, False: info | BDB alert; Has threshold parameter in the key/value section of the log entry |\n| ram_overcommit | Cluster capacity is less than total memory allocated to its databases | True: error, False: info | Cluster alert |\n| ram_values | Percent of values in a shard's RAM is lower than `<threshold>`% of its key count | True: warning, False: info | BDB alert; Has threshold parameter in the key/value section of the log entry |\n| shard_num_ram_values | Number of values in a shard's RAM is lower than `<threshold>` values | True: warning, False: info | BDB alert; Has threshold parameter in the key/value section of the log entry |\n| size | Dataset size has reached `<threshold>`% of the memory limit | True: warning, False: info | BDB alert; Has threshold parameter in the key/value section of the log entry |\n| syncer_connection_error | Syncer connection error | error | BDB alert |\n| syncer_general_error | Syncer general error | error | BDB alert |\n| too_few_nodes_for_replication | Database replication requires at least two nodes in cluster | True: warning, False: info | Cluster alert |"},{"id":"events","title":"Events","role":"content","text":"| Event | UI message | Severity | Notes |\n|-------|------------|----------|-------|\n| backup_failed | Backup failed | error | BDB event |\n| backup_started | Backup started | info | BDB event |\n| backup_succeeded | Backup succeeded | info | BDB event |\n| bdb_created | Database created | info | BDB event |\n| bdb_deleted | Database deleted | info | BDB event |\n| bdb_updated | Database updated | info | BDB event; Indicates that a BDB configuration has been updated |\n| checks_error | Node checks error | error | Node event; Indicates that one or more node checks have failed |\n| cluster_updated | Cluster settings updated | info | Cluster event; Indicates that cluster settings have been updated |\n| compression_unsup_err | Compression not supported by sync destination | error | BDB event |\n| crossslot_err | Sharded destination does not support operation executed on source | error | BDB event |\n| export_failed | Export failed | error | BDB event |\n| export_started | Export started | info | BDB event |\n| export_succeeded | Export succeeded | info | BDB event |\n| import_failed | Import failed | error | BDB event |\n| import_started | Import started | info | BDB event |\n| import_succeeded | Import succeeded | info | BDB event |\n| license_added | License added | info | Cluster event |\n| license_deleted | License deleted | info | Cluster event |\n| license_updated | License updated | info | Cluster event |\n| node_abort_remove_request | Node abort remove request | info | Node event |\n| node_joined | Node joined | info | Cluster event |\n| node_operation_failed | Node operation failed | error | Cluster event |\n| node_remove_abort_completed | Node remove abort completed | info | Cluster event; Node remove is a process that can fail and can also be aborted. If aborted, the abort can succeed or fail |\n| node_remove_abort_failed | Node remove abort failed | error | Cluster event; Node remove is a process that can fail and can also be aborted. If aborted, the abort can succeed or fail |\n| node_remove_completed | Node removed | info | Cluster event; Node remove is a process that can fail and can also be aborted. If aborted, the abort can succeed or fail |\n| node_remove_failed | Node removed | error | Cluster event; Node remove is a process that can fail and can also be aborted. If aborted, the abort can succeed or fail |\n| node_remove_request | Node remove request | info | Node event |\n| user_created | User created | info | User event |\n| user_deleted | User deleted | info | User event |\n| user_updated | User updated | info | User event; Indicates that a user configuration has been updated |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/clusters/logging/diagnostic-logging","title":"Diagnostic logging","url":"https://redis.io/docs/latest/operate/rs/clusters/logging/diagnostic-logging/","summary":"Diagnostic logs can help troubleshoot Redis Software.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:16+01:00","page_type":"content","content_hash":"28eb0639e1ea02cc1193742c9b08eafa4a3fe9b06455d16a8bfca67e857d0920","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The diagnostic logging service collects detailed system logs, which you can use to troubleshoot Redis Software."},{"id":"view-diagnostic-logs","title":"View diagnostic logs","role":"content","text":"Diagnostic logs are collected at scheduled intervals and saved in the `/var/opt/redislabs/log/diagnostics/` directory. Each diagnostic log file is overwritten with the new data at the scheduled collection interval."},{"id":"view-log-collector-settings","title":"View log collector settings","role":"content","text":"To view the current log collection schedule and parameters for each log collector, use the REST API to [get the diagnostic logging service configuration](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/diagnostics#get-diagnostics).\n\n[code example]\n\nExample response:\n\n[code example]"},{"id":"change-log-collector-settings","title":"Change log collector settings","role":"content","text":"To change how often a log is collected, set the `cron_expression` when you [update the diagnostic logging service configuration](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/diagnostics#put-diagnostics) with the REST API.\n\n[code example]\n\n- Replace `<log_collector>` with the name of the log collector you want to turn off.\n\n- Use standard [cron syntax](https://en.wikipedia.org/wiki/Cron) to set the collection interval in the `cron_expression`.\n\nFor the slowlog collector only, you can also set `max_entries` to change the maximum number of slow log entries to collect:\n\n[code example]"},{"id":"turn-off-log-collectors","title":"Turn off log collectors","role":"content","text":"To turn off a log collector, set its `cron_expression` to an empty string when you [update the diagnostic logging service configuration](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/diagnostics#put-diagnostics) with the REST API.\n\n[code example]\n\nReplace `<log_collector>` with the name of the log collector you want to turn off."},{"id":"log-collectors","title":"Log collectors","role":"content","text":"Each log collector runs independently and writes a separate log file. The following table describes the log collectors.\n\nDefault `cron_expression` values:\n\n- `*/10 * * * *`: Logs are collected every 10 minutes.\n\n- `*/30 * * * *`: Logs are collected every 30 minutes.\n\n| Log collector | Description |\n|---------------|-------------|\n| bdb | Logs database metadata similar to [`GET /bdbs`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs). Each entry is in JSON format.<br />Default settings:<br />\"cron_expression\": \"*/10 * * * *\" |\n| bdb_client_list | Logs database client lists, with a separate file for each database. Each entry is in JSON format.<br />Default settings:<br />\"cron_expression\": \"*/10 * * * *\" |\n| bdb_info | Logs the result of running [`INFO ALL`](https://redis.io/docs/latest/commands/info) on a database, excluding `commandstats`, with a separate file for each database. Each entry is in JSON format.<br />Default settings:<br />\"cron_expression\": \"*/10 * * * *\" |\n| command_stats | Logs [`INFO commandstats`](https://redis.io/docs/latest/commands/info) for each database, with a separate file for each database. Each entry is in JSON format.<br />Default settings:<br />\"cron_expression\": \"*/30 * * * *\" |\n| network_stats | Logs the node's network statistics.<br />Default settings:<br />\"cron_expression\": \"*/30 * * * *\" |\n| persistent_files | Lists persistent files from `/var/opt/redislabs/persist/redis`<br />Default settings:<br />\"cron_expression\": \"*/10 * * * *\" |\n| rladmin_status | Logs data about nodes, databases, endpoints, and shards from [`rladmin status`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status). Each entry is in JSON format.<br />Default settings:<br />\"cron_expression\": \"*/10 * * * *\" |\n| shard | Logs shard status similar to [`GET /shards`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/shards). Each entry is in JSON format.<br />Default settings:<br />\"cron_expression\": \"*/10 * * * *\" |\n| shard_info | Logs the result of running [INFO ALL](https://redis.io/docs/latest/commands/info) on a shard, with a separate file for each shard. Each entry is in JSON format.<br />Default settings:<br />\"cron_expression\": \"*/10 * * * *\" |\n| shard_latency | Logs the result of running [`latency latest`](https://redis.io/docs/latest/commands/latency-latest) on a shard, with a separate file for each shard.<br />Default settings:<br />\"cron_expression\": \"*/10 * * * *\" |\n| <span class=\"break-all\">`shard_latency_histogram`</span> | Logs the result of running [`latency histogram`](https://redis.io/docs/latest/commands/latency-histogram) on a shard, with a separate file for each shard.<br />Default settings:<br />\"cron_expression\": \"*/10 * * * *\" |\n| slowlog | Logs slow commands from the databases using [`SLOWLOG GET`](https://redis.io/docs/latest/commands/slowlog-get), with a separate file for each database. Each entry is in JSON format. The log is sanitized, only the commands are visible.<br />Default settings:<br />\"cron_expression\": \"*/10 * * * *\",<br />\"max_entries\": 100 |\n| socket_files | Lists socket files used by Redis Software.<br />Default settings:<br />\"cron_expression\": \"*/10 * * * *\" |"}],"examples":[{"id":"view-log-collector-settings-ex0","language":"sh","code":"GET /v1/diagnostics","section_id":"view-log-collector-settings"},{"id":"view-log-collector-settings-ex1","language":"json","code":"{\n    \"bdb_client_list_target\": {\n        \"cron_expression\": \"*/10 * * * *\"\n    },\n    \"bdb_info_target\": {\n        \"cron_expression\": \"*/10 * * * *\"\n    },\n    \"bdb_target\": {\n        \"cron_expression\": \"*/10 * * * *\"\n    },\n    \"command_stats_target\": {\n        \"cron_expression\": \"*/30 * * * *\"\n    },\n    \"network_stats_target\": {\n        \"cron_expression\": \"*/30 * * * *\"\n    },\n    \"persistent_files_target\": {\n        \"cron_expression\": \"*/10 * * * *\"\n    },\n    \"rladmin_status_target\": {\n        \"cron_expression\": \"*/10 * * * *\"\n    },\n    \"shard_info_target\": {\n        \"cron_expression\": \"*/10 * * * *\"\n    },\n    \"shard_latency_histogram_target\": {\n        \"cron_expression\": \"*/10 * * * *\"\n    },\n    \"shard_latency_target\": {\n        \"cron_expression\": \"*/10 * * * *\"\n    },\n    \"shard_target\": {\n        \"cron_expression\": \"*/10 * * * *\"\n    },\n    \"slowlog_target\": {\n        \"cron_expression\": \"*/10 * * * *\",\n        \"max_entries\": 100\n    },\n    \"socket_files_target\": {\n        \"cron_expression\": \"*/10 * * * *\"\n    }\n}","section_id":"view-log-collector-settings"},{"id":"change-log-collector-settings-ex0","language":"sh","code":"PUT /v1/diagnostics\n{\n  \"<log_collector>_target\": {\n    \"cron_expression\": \"*/5 * * * *\"\n  }\n}","section_id":"change-log-collector-settings"},{"id":"change-log-collector-settings-ex1","language":"sh","code":"PUT /v1/diagnostics\n{\n  \"slowlog_target\": {\n    \"cron_expression\": \"*/5 * * * *\",\n    \"max_entries\": 200\n  }\n}","section_id":"change-log-collector-settings"},{"id":"turn-off-log-collectors-ex0","language":"sh","code":"PUT /v1/diagnostics\n{\n  \"<log_collector>_target\": {\n    \"cron_expression\": \"\"\n  }\n}","section_id":"turn-off-log-collectors"}]}
{"schema_version":2,"id":"operate/rs/clusters/logging","title":"Logging events","url":"https://redis.io/docs/latest/operate/rs/clusters/logging/","summary":"Management actions performed with Redis Software are logged to make sure system management tasks are appropriately performed or monitored by administrators and for compliance with regulatory standards.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","children":[{"id":"operate/rs/clusters/logging/alerts-events","summary":"Logged alerts and events","title":"Alerts and events","url":"https://redis.io/docs/latest/operate/rs/clusters/logging/alerts-events/"},{"id":"operate/rs/clusters/logging/diagnostic-logging","summary":"Diagnostic logs can help troubleshoot Redis Software.","title":"Diagnostic logging","url":"https://redis.io/docs/latest/operate/rs/clusters/logging/diagnostic-logging/"},{"id":"operate/rs/clusters/logging/log-security","summary":"","title":"Manage logs","url":"https://redis.io/docs/latest/operate/rs/clusters/logging/log-security/"},{"id":"operate/rs/clusters/logging/rsyslog-logging","summary":"This document explains the structure of Redis Software log entries in `rsyslog` and how to use these log entries to identify events.","title":"rsyslog logging","url":"https://redis.io/docs/latest/operate/rs/clusters/logging/rsyslog-logging/"},{"id":"operate/rs/clusters/logging/redis-slow-log","summary":"","title":"View and manage Redis slow log","url":"https://redis.io/docs/latest/operate/rs/clusters/logging/redis-slow-log/"}],"page_type":"content","content_hash":"b2db3317c2e34aa4d258694762efb7e7de21d5deb5428cb4529c0c7cf9ba7be8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Management actions performed with Redis Software are logged to make sure system management tasks are appropriately performed or monitored by administrators and for compliance with regulatory standards.\n\nLog entries contain the\nfollowing information:\n\n1. Who performed the action?\n1. What exactly was the performed action?\n1. When was the action performed?\n1. Did the action succeed or not?\n\nTo get the list of logged events, you can use the REST API or\nthe **Logs** screen in the UI. The **Logs** screen displays the system and user\nevents regarding alerts, notifications, and configuration.\n\n![Logs screen in the new Cluster Manager UI.](https://redis.io/docs/latest/images/rs/screenshots/cluster/cluster-logs.png)\n\nYou can use the **Logs** screen to review what actions a user has performed, such as editing a database's configuration.\n\n- [Redis slow\n    log](https://redis.io/docs/latest/operate/rs/clusters/logging/redis-slow-log)\n- [rsyslog logging](https://redis.io/docs/latest/operate/rs/clusters/logging/rsyslog-logging)"},{"id":"view-logs-in-the-ui","title":"View logs in the UI","role":"content","text":"Redis Software provides log files for auditing cluster management actions and troubleshooting. You can view these logs in the UI and on the host operating system.\n\nTo view event logs in the new Cluster Manager UI, go to **Cluster > Logs**."},{"id":"view-logs-on-the-server","title":"View logs on the server","role":"content","text":"Server logs can be found by default in the directory `/var/opt/redislabs/log/`.\n\nThese log files are used by the Redis support team to troubleshoot issues. The logs you will most frequently interact with is 'event_log.log'. This log file is where logs of configuration actions within Redis are stored and is useful to determine events that occur within Redis Software."},{"id":"configure-log-timestamps","title":"Configure log timestamps","role":"configuration","text":"Redis Software allows you to configure log timestamps. To configure log timestamps in the new Cluster Manager UI:\n\n1. Go to **Cluster > Configuration > General**.\n\n1. Change the **Time zone** for the logs based on your location."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/clusters/logging/log-security","title":"Manage logs","url":"https://redis.io/docs/latest/operate/rs/clusters/logging/log-security/","summary":"","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:16+01:00","page_type":"content","content_hash":"985bb084338120b4b4222f501ca5aa43df58f76d8572c5713fdd28b3ca28cad7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software comes with [a set of logs](https://redis.io/docs/latest/operate/rs/clusters/logging) on the server and available through the user interface to assist users in investigating actions taken on the server and to troubleshoot issues."},{"id":"send-logs-to-a-remote-logging-server","title":"Send logs to a remote logging server","role":"content","text":"Redis Software sends logs to syslog by default. You can send these logs to a remote logging server by configuring syslog.\n\nTo do this, modify the syslog or rsyslog configuration on your operating system to send logs in the `$logdir` directory (`/var/opt/redislabs/log` in default installations) to a remote monitoring server of your choice. See [rsyslog logging](https://redis.io/docs/latest/operate/rs/clusters/logging/rsyslog-logging) for additional details."},{"id":"log-rotation","title":"Log rotation","role":"content","text":"Redis Software's job scheduler runs `logrotate` every five minutes to examine logs stored on the operating system and rotate them based on the log rotation configuration. You can find the log rotation configuration file at `$pkgconfdir/logrotate.conf` as of Redis Software version 7.2 (`pkgconfdir` is `/opt/redislabs/config` by default, but can be changed in a custom installation).\n\nBy default, log rotation occurs when a log exceeds 200 MB. We recommend sending log files to a remote logging server so you can maintain them more effectively.\n\nThe following log rotation policy is enabled by default in Redis Software, but you can modify it as needed.\n\n[code example]\n\n- `${logdir}/*.log`: `logrotate` checks the files under the `$logdir` directory (`/var/opt/redislabs/log/`) and rotates any files that end with the extension `.log`.\n\n- `${logdir}/cluster_wd.log.long_retention`: `cluster_wd.log` is copied to `cluster_wd.log.long_retention` before rotation. This copy is kept longer than usual, which is 30 days by default.\n\n- `maxsize 200M`: Rotate log files that exceed 200 MB.\n\n- `daily`: Rotate logs every day regardless of their size.\n\n- `missingok`: If there are missing log files, do nothing.\n\n- `copytruncate`: Truncate the original log file to zero sizes after creating a copy.\n\n- `rotate 10`: Save a maximum of 10 rotated log files. To keep effectively infinite log files, use `rotate 2000` instead.\n\n- `compress`: gzip log files.\n\n- `maxage 7`: Keep the rotated log files for 7 days.\n\n- `notifempty`: Don't rotate the log file if it is empty.\n\n> [!NOTE]\n> For large scale deployments, you might need to rotate logs at faster intervals than daily. You can also use a cronjob or external vendor solutions."}],"examples":[{"id":"log-rotation-ex0","language":"sh","code":"${logdir}/*.log {\n    su ${osuser} ${osgroup}\n    maxsize 200M\n    daily\n    missingok\n    copytruncate\n    rotate 10\n    maxage 7\n    compress\n    notifempty\n    nodateext\n    nosharedscripts\n    prerotate\n        # copy cluster_wd log to another file that will have longer retention\n        if [ \"\\$1\" = \"${logdir}/cluster_wd.log\" ]; then\n            cp -p ${logdir}/cluster_wd.log ${logdir}/cluster_wd.log.long_retention\n        fi\n    endscript\n}\n${logdir}/cluster_wd.log.long_retention {\n    su ${osuser} ${osgroup}\n    daily\n    missingok\n    copytruncate\n    rotate 30\n    compress\n    notifempty\n    nodateext\n}","section_id":"log-rotation"}]}
{"schema_version":2,"id":"operate/rs/clusters/logging/redis-slow-log","title":"View and manage Redis slow log","url":"https://redis.io/docs/latest/operate/rs/clusters/logging/redis-slow-log/","summary":"","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:16+01:00","page_type":"content","content_hash":"b95240eb9801daad8dfa89c884dc5e2933ebdfa00eabf0ec46a8b6bcf16065b1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Redis slow log](https://redis.io/docs/latest/commands/slowlog) is one of the best\ntools for debugging and tracing your Redis database, especially if you\nexperience high latency and high CPU usage with Redis operations.\nBecause Redis is based on a single threaded architecture, Redis slow log\ncan be much more useful than slow log mechanisms of multi-threaded\ndatabase systems such as MySQL slow query log.\n\nUnlike tools that introduce lock overhead, which complicates the debugging\nprocess, Redis slow log is highly effective at showing the actual processing time of each command."},{"id":"redis-software-slow-log-enhancements","title":"Redis Software slow log enhancements","role":"content","text":"Redis Software includes enhancements to the standard Redis\nslow log capabilities that allow you to analyze the execution time\ncomplexity of each command. This enhancement can help you better analyze\nRedis operations, allowing you to compare the differences between\nexecution times of the same command, observe spikes in CPU usage, and\nmore.\n\nThis is especially useful with complex commands such as\n[ZUNIONSTORE](https://redis.io/docs/latest/commands/zunionstore),\n[ZINTERSTORE](https://redis.io/docs/latest/commands/zinterstore), and\n[ZRANGEBYSCORE](https://redis.io/docs/latest/commands/zrangebyscore).\n\nThe enhanced Redis Software slow log adds the **Complexity info** field to the\noutput data.\n\nView the complexity info data by its respective command in the table\nbelow:\n\n| Command | Value of interest | Complexity |\n|------------|-----------------|-----------------|\n| LINSERT | N - list len | O(N) |\n| LREM | N - list len | O(N) |\n| LTRIM | N - number of removed elements | O(N) |\n| PUBLISH | N - number of channel subscribers</br>M - number of subscribed patterns | O(N+M) |\n| PSUBSCRIBE | N - number of patterns client is subscribed to</br>argc - number of arguments passed to the command | O(argc\\*N) |\n| PUNSUBSCRIBE | N - number of patterns client is subscribed to</br>M - total number of subscribed patterns</br>argc - number of arguments passed to the command | O(argc\\*(N+M)) |\n| SDIFF | N - total number of elements in all sets | O(N) |\n| SDIFFSTORE | N - total number of elements in all sets | O(N) |\n| SINTER                | N - number of elements in smallest set</br>argc - number of arguments passed to the command | O(argc\\*N) |\n| SINTERSTORE           | N - number of elements in smallest set</br>argc - number of arguments passed to the command | O(argc\\*N) |\n| SMEMBERS              | N - number of elements in a set | O(N) |\n| SORT                  | N - number of elements in the when no sorting list/set/zset</br>M - number of elements in result | O(N+M\\*log(M))O(N) |\n| SUNION                | N - number of elements in all sets | O(N) |\n| SUNIONSTORE           | N - number of elements in all sets | O(N) |\n| UNSUBSCRIBE           | N - total number of clients subscribed to all channels | O(N) |\n| ZADD                  | N - number of elements in the zset | O(log(N)) |\n| ZCOUNT                | N - number of elements in the zset</br>M - number of elements between min and max | O(log(N)+M) |\n| ZINCRBY               | N - number of elements in the zset | O(log(N)) |\n| ZINTERSTORE           | N – number of elements in the smallest zset</br>K – number of zsets</br>M – number of elements in the results set | O(N\\*K)+O(M\\*log(M)) |\n| ZRANGE                | N – number of elements in the zset</br>M – number of results | O(log(N)+M) |\n| ZRANGEBYSCORE         | N – number of elements in the zset</br>M – number of results | O(log(N)+M) |\n| ZRANK                 | N – number of elements in the zset | O(log(N)) |\n| ZREM                  | N – number of elements in the zset</br>argc – number of arguments passed to the command | O(argc\\*log(N)) |\n| ZREMRANGEBYRANK       | N – number of elements in the zset</br>argc – number of arguments passed to the command | O(log(N)+M) |\n| ZREMRANGEBYSCORE      | N – number of elements in the zset</br>M – number of elements removed | O(log(N)+M) |\n| ZREVRANGE             | N – number of elements in the zset</br>M – number of results | O(log(N)+M) |\n| ZREVRANK              | N – number of elements in the zset | O(log(N)) |\n| ZUNIONSTORE           | N – sum of element counts of all zsets</br>M – element count of result | O(N)+O(M\\*log(M)) |"},{"id":"view-slow-log","title":"View slow log","role":"content","text":"To view slow log entries for Redis Software databases, use one of the following methods:\n\n- Cluster Manager UI:\n\n    1. To access the slow log in the Cluster Manager UI, your [cluster management role](https://redis.io/docs/latest/operate/rs/security/access-control/create-cluster-roles) must be Admin, Cluster Member, or DB Member.\n    \n    1. Select a database from the **Databases** list.\n\n    1. On the database's **Configuration** screen, select the **Slowlog** tab.\n\n- Command line:\n\n    Use [`redis-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/redis-cli) to run [`SLOWLOG GET`](https://redis.io/docs/latest/commands/slowlog-get):\n\n    [code example]"},{"id":"change-slow-log-threshold","title":"Change slow log threshold","role":"content","text":"The slow log includes all database commands that take longer than ten milliseconds (10,000 microseconds) by default. You can use [`redis-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/redis-cli) to view or change this threshold.\n\nTo check the current threshold, run [`CONFIG GET`](https://redis.io/docs/latest/commands/config-get):\n\n[code example]\n\nTo change the threshold, run [`CONFIG SET`](https://redis.io/docs/latest/commands/config-set):\n\n[code example]"},{"id":"change-maximum-entries","title":"Change maximum entries","role":"content","text":"The slow log retains the last 128 entries by default. You can use [`redis-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/redis-cli) to view or change the maximum number of entries.\n\nTo check the current maximum, run [`CONFIG GET`](https://redis.io/docs/latest/commands/config-get):\n\n[code example]\n\nTo change the maximum, run [`CONFIG SET`](https://redis.io/docs/latest/commands/config-set):\n\n[code example]"}],"examples":[{"id":"view-slow-log-ex0","language":"sh","code":"redis-cli -h <endpoint> -p <port> SLOWLOG GET <count>","section_id":"view-slow-log"},{"id":"change-slow-log-threshold-ex0","language":"sh","code":"redis-cli -h <endpoint> -p <port> CONFIG GET slowlog-log-slower-than","section_id":"change-slow-log-threshold"},{"id":"change-slow-log-threshold-ex1","language":"sh","code":"redis-cli -h <endpoint> -p <port> CONFIG SET slowlog-log-slower-than <value_in_microseconds>","section_id":"change-slow-log-threshold"},{"id":"change-maximum-entries-ex0","language":"sh","code":"redis-cli -h <endpoint> -p <port> CONFIG GET slowlog-max-len","section_id":"change-maximum-entries"},{"id":"change-maximum-entries-ex1","language":"sh","code":"redis-cli -h <endpoint> -p <port> CONFIG SET slowlog-max-len <value>","section_id":"change-maximum-entries"}]}
{"schema_version":2,"id":"operate/rs/clusters/logging/rsyslog-logging","title":"rsyslog logging","url":"https://redis.io/docs/latest/operate/rs/clusters/logging/rsyslog-logging/","summary":"This document explains the structure of Redis Software log entries in `rsyslog` and how to use these log entries to identify events.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:16+01:00","children":[],"page_type":"content","content_hash":"14f6b05b253f71665890c29927a383db091a444eab126db5e05d55b5df0cee72","sections":[{"id":"log-concepts","title":"Log concepts","role":"overview","text":"Redis Software logs information from a variety of components in response to actions and events that occur within the cluster.\n\nIn some cases, a single action, such as removing a node from the cluster, may actually consist of several events. These actions may generate multiple log entries.\n\nAll log entries displayed in the Cluster Manager UI are also written to `syslog`.  You can configure `rsyslog` to monitor `syslog`. Enabled alerts are logged to `syslog` and appear with other log entries.\n\nYou can also [manage your logs](https://redis.io/docs/latest/operate/rs/clusters/logging/log-security) with a remote logging server and log rotation."},{"id":"types-of-log-entries","title":"Types of log entries","role":"content","text":"Log entries are categorized into events and alerts. Both types of entries appear in the logs, but alert log entries also include a boolean `\"state\"` parameter that indicates whether the alert is enabled or disabled.\n\nLog entries include information about the specific event that occurred. See the log entry tables for [alerts and events](https://redis.io/docs/latest/operate/rs/clusters/logging/alerts-events) for more details."},{"id":"severity","title":"Severity","role":"content","text":"You can also configure `rsyslog` to add other information, such as the event severity.\n\nSince `rsyslog` entries do not include severity by default, you can follow these steps to enable it:\n\n1. Add the following line to `/etc/rsyslog.conf`:\n    [code example]\n\n2. Modify `$ActionFileDefaultTemplate` to use your new template `$ActionFileDefaultTemplateTraditionalFormatWithPRI`\n\n3. Save these changes and restart `rsyslog` to apply them\n\nYou can see the log entries for alerts and events in the `/var/log/messages` file.\n\n**Command components:**\n\n- `%pri­text%` ­adds the severity\n- `%timegenerated%` ­adds the timestamp\n- `%HOSTNAME%` ­adds the machine name\n- `%syslogtag%` adds ­the Redis Software message. See the [log entry structure](#log-entry-structure) section for more details.\n- `%msg:::drop­last­lf%n` ­removes duplicated log entries"},{"id":"log-entry-structure","title":"Log entry structure","role":"content","text":"The log entries have the following basic structure:\n\n    event_log[<process id>]:{<list of key-value pairs in any order>}\n\n- **event_log**:­ Plain static text is always shown at the beginning of the entry.\n- **process id­**: The ID of the logging process\n- **list of key-value pairs in any order**:­ A list of key-value pairs that describe the specific event. They can appear in any order. Some key­-value pairs are always shown, and some appear depending on the specific event.\n    - **Key-­value pairs that always appear:**\n        - `\"type\"`: A unique code­ name for the logged event. For the list of codenames, see the [logged alerts and events](https://redis.io/docs/latest/operate/rs/clusters/logging/alerts-events) tables.\n        - `\"object\"`: Defines the object type and ID (if relevant) of the object this event relates to, such as cluster, node with ID, BDB with ID, etc. Has the format of <nobr>`<object type>[:<id>]`</nobr>. \n        - `\"time\"`: Unix epoch time but can be ignored in this context.\n    - **Key-­value pairs that might appear depending on the specific entry:**\n        - `\"state\"`: A boolean where `true` means the alert is enabled, and `false` means the alert is disabled. This is only relevant for alert log entries.\n        - `\"global_threshold\"`: The value of a threshold for alerts related to cluster or node objects.\n        - `\"threshold\"`: The value of a threshold for alerts related to a BDB object"},{"id":"log-entry-samples","title":"Log entry samples","role":"content","text":"This section provides examples of log entries that include the [`rsyslog` configuration](#severity) to add the severity, timestamp, and machine name."},{"id":"ephemeral-storage-passed-threshold","title":"Ephemeral storage passed threshold","role":"content","text":"#### \"Alert on\" log entry sample\n\n[code example]\n\nIn this example, the storage utilization on node 1 reached the value of ~90%, which triggered the alert for \"Ephemeral storage has reached 70% of its capacity.\"\n\n**Log entry components:**\n\n- `daemon.warning` -­ Severity of entry is `warning`\n- `Jun 14 14:49:20` -­ The timestamp of the event\n- `node1`:­ Machine name\n- `event_log` -­ Static text that always appears\n- `[3464]­` - Process ID\n- `\"storage_util\":90.061643120001` - Current ephemeral storage utilization\n- `\"global_threshold\":\"70\"` - The user-configured threshold above which the alert is raised\n- `\"object\":\"node:1\"`­ - The object related to this alert\n- `\"state\":true­` - Current state of the alert\n- `\"time\":1434282560­` - Can be ignored\n- `\"type\":\"ephemeral_storage\"` - The code name of this specific event. See [logged alerts and events](https://redis.io/docs/latest/operate/rs/clusters/logging/alerts-events) for more details.\n\n#### \"Alert off\" log entry sample\n\n[code example]\n\nThis log entry is an example of when the alert for the node with ID 1 \"Ephemeral storage has reached 70% of its capacity\" has been turned off as result of storage utilization reaching the value of ~60%.\n\n**Log entry components**:\n\n- `daemon.info` -­ Severity of entry is `info`\n- `Jun 14 14:51:35` -­ The timestamp of the event\n- `node1` -­ Machine name\n- `event_log` -­ Static text that always appears\n- `[3464]` -­ Process ID\n- `\"storage_util\":60.051723520008­` - Current ephemeral storage utilization\n- `\"global_threshold\":\"70\"` - The user configured threshold above which the alert is raised (70% in this case)\n- `\"object\":\"node:1\"` -­ The object related to this alert\n- `\"state\":false­` - Current state of the alert\n- `\"time\":1434283480­` - Can be ignored\n- `\"type\":\"ephemeral_storage\"` -­ The code name identifier of this specific event. See [logged alerts and events](https://redis.io/docs/latest/operate/rs/clusters/logging/alerts-events) for more details."},{"id":"odd-number-of-nodes-with-a-minimum-of-three-nodes-alert","title":"Odd number of nodes with a minimum of three nodes alert","role":"content","text":"#### \"Alert on\" log entry sample\n\n[code example]\n\nThis log entry is an example of when the alert for \"True high availability requires an odd number of nodes with a minimum of three nodes\" has been turned on as result of the cluster having only one node.\n\n**Log entry components:**\n\n- `daemon.warning­` - Severity of entry is warning\n- `Jun 14 15:25:00` - The timestamp of the event\n- `node1­` - Machine name\n- `event_log` -­ Static text that always appears\n- `[8310]­` - Process ID\n- `\"object\":\"cluster\"­` - The object related to this alert\n- `\"state\":true` -­ Current state of the alert\n- `\"time\":1434284700­` - Can be ignored\n- `\"node_count\":1­` - The number of nodes in the cluster\n- `\"type\":\"even_node_count\"­` - The code name identifier of this specific event. See [logged alerts and events](https://redis.io/docs/latest/operate/rs/clusters/logging/alerts-events) for more details.\n\n#### \"Alert off\" log entry sample\n\n[code example]\n\nThis log entry is an example of when the alert for \"True high availability requires an odd number of nodes with a minimum of three nodes\" has been turned off as result of the cluster having 3 nodes.\n\n**Log entry components:**\n\n- `daemon.warning` - Severity of entry is warning\n- `Jun 14 15:30:40` -­ The timestamp of the event\n- `node1­` - Machine name\n- `event_log­` - Static text that always appears\n- `[8310]` -­ Process ID\n- `\"object\":\"cluster\"` -­ The object related to this alert\n- `\"state\":false­` - Current state of the alert\n- `\"time\":1434285200­` - Can be ignored\n- `\"node_count\":3­` - The number of nodes in the cluster\n- `\"type\":\"even_node_count\"` -­ The code name of this specific event. See [logged alerts and events](https://redis.io/docs/latest/operate/rs/clusters/logging/alerts-events) for more details."},{"id":"node-has-insufficient-disk-space-for-aof-rewrite","title":"Node has insufficient disk space for AOF rewrite","role":"content","text":"#### \"Alert on\" log entry sample\n\n[code example]\n\nThis log entry is an example of when the alert for \"Node has insufficient disk space for AOF rewrite\" has been turned on as result of not having enough persistent storage disk space for AOF rewrite purposes. It is missing 604602126 bytes.\n\n**Log entry components:**\n\n- `daemon.err`­ - Severity of entry is error\n- `Jun 15 13:51:23` - The timestamp of the event\n- `node1­` - Machine name\n- `event_log` -­ Static text that always appears\n- `[34252]` -­ Process ID\n- `\"used\":23457188­` - The amount of disk space in bytes currently used for AOF files\n- `\"missing\":604602126­` - The amount of disk space in bytes that is currently missing for AOF rewrite purposes\n- `\"object\":\"node:1″` -­ The object related to this alert\n- `\"free\":9867264­` - The amount of disk space in bytes that is currently\n    free\n- `\"needed\":637926578­` - The amount of total disk space in bytes that is needed for AOF rewrite purposes\n- `\"state\":true­` - Current state of the alert\n- `\"time\":1434365483` -­ Can be ignored\n- `\"disk\":705667072­` - The total size in bytes of the persistent storage\n- `\"type\":\"insufficient_disk_aofrw\"­` - The code name of this specific event. See [logged alerts and events](https://redis.io/docs/latest/operate/rs/clusters/logging/alerts-events) for more details.\n\n#### \"Alert off\" log entry sample\n\n[code example]\n\n**Log entry components:**\n\n- `daemon.info­` - Severity of entry is info\n- `Jun 15 13:51:11` - The timestamp of the event\n- `node1­` - Machine name\n- `event_log` -­ Static text that always appears\n- `[34252]­` - Process ID\n- `\"used\":0­` - The amount of disk space in bytes currently used for AOF files\n- `\"missing\":‐21614592­` - The amount of disk space in bytes that is currently missing for AOF rewrite purposes. In this case, it is not missing because the number is negative.\n- `\"object\":\"node:1″` -­ The object related to this alert\n- `\"free\":21614592` -­ The amount of disk space in bytes that is currently free\n- `\"needed\":0­` - The amount of total disk space in bytes that is needed for AOF rewrite purposes. In this case, no space is needed.\n- `\"state\":false­` - Current state of the alert\n- `\"time\":1434365471­` - Can be ignored\n- `\"disk\":705667072­` - The total size in bytes of the persistent storage\n- `\"type\":\"insufficient_disk_aofrw\"`­ - The code name of this specific event. See [logged alerts and events](https://redis.io/docs/latest/operate/rs/clusters/logging/alerts-events) for more details."}],"examples":[{"id":"severity-ex0","language":"plaintext","code":"$template TraditionalFormatWithPRI,\"%pri-text%: %timegenerated% %HOSTNAME% %syslogtag%%msg:::drop-last-lf%\\n\"","section_id":"severity"},{"id":"ephemeral-storage-passed-threshold-ex0","language":"plaintext","code":"daemon.warning: Jun 14 14:49:20 node1 event_log[3464]:\n{\n    \"storage_util\": 90.061643120001, \n    \"global_threshold\": \"70\", \n    \"object\": \"node:1\", \n    \"state\": true, \n    \"time\": 1434282560, \n    \"type\": \"ephemeral_storage\"\n}","section_id":"ephemeral-storage-passed-threshold"},{"id":"ephemeral-storage-passed-threshold-ex1","language":"plaintext","code":"daemon.info: Jun 14 14:51:35 node1 event_log[3464]: \n{\n    \"storage_util\":60.051723520008, \n    \"global_threshold\": \"70\", \n    \"object\": \"node:1\", \n    \"state\":false, \n    \"time\": 1434283480, \n    \"type\": \"ephemeral_storage\"\n}","section_id":"ephemeral-storage-passed-threshold"},{"id":"odd-number-of-nodes-with-a-minimum-of-three-nodes-alert-ex0","language":"plaintext","code":"daemon.warning: Jun 14 15:25:00 node1 event_log[8310]: \n{\n    \"object\":\"cluster\", \n    \"state\": true, \n    \"time\": 1434284700, \n    \"node_count\": 1, \n    \"type\":\"even_node_count\"\n}","section_id":"odd-number-of-nodes-with-a-minimum-of-three-nodes-alert"},{"id":"odd-number-of-nodes-with-a-minimum-of-three-nodes-alert-ex1","language":"plaintext","code":"daemon.warning: Jun 14 15:30:40 node1 event_log[8310]: \n{\n    \"object\":\"cluster\", \n    \"state\": false, \n    \"time\": 1434285200, \n    \"node_count\": 3, \n    \"type\":\"even_node_count\"\n}","section_id":"odd-number-of-nodes-with-a-minimum-of-three-nodes-alert"},{"id":"node-has-insufficient-disk-space-for-aof-rewrite-ex0","language":"plaintext","code":"daemon.err: Jun 15 13:51:23 node1 event_log[34252]: \n{\n    \"used\": 23457188, \n    \"missing\": 604602126, \n    \"object\": \"node:1\", \n    \"free\": 9867264, \n    \"needed\":637926578, \n    \"state\": true, \n    \"time\": 1434365483, \n    \"disk\": 705667072, \n    \"type\":\"insufficient_disk_aofrw\"\n}","section_id":"node-has-insufficient-disk-space-for-aof-rewrite"},{"id":"node-has-insufficient-disk-space-for-aof-rewrite-ex1","language":"plaintext","code":"daemon.info: Jun 15 13:51:11 node1 event_log[34252]: \n{\n    \"used\": 0, \"missing\":-21614592, \n    \"object\": \"node:1\", \n    \"free\": 21614592, \n    \"needed\": 0, \n    \"state\":false, \n    \"time\": 1434365471, \n    \"disk\": 705667072, \n    \"type\":\"insufficient_disk_aofrw\"\n}","section_id":"node-has-insufficient-disk-space-for-aof-rewrite"}]}
{"schema_version":2,"id":"operate/rs/clusters/maintenance-mode","title":"Maintenance mode for cluster nodes","url":"https://redis.io/docs/latest/operate/rs/clusters/maintenance-mode/","summary":"Prepare a cluster node for maintenance.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"52020de7adbdbf0623e6fc83ee4d0bb97e9b45afb597ae86e06dcaa59863fa42","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Use maintenance mode to prevent data loss during hardware patching or operating system maintenance on Redis Software servers. When maintenance mode is on, all shards move off of the node under maintenance and migrate to another available node."},{"id":"activate-maintenance-mode","title":"Activate maintenance mode","role":"content","text":"When you activate maintenance mode, Redis Software does the following:\n\n1. Checks whether the cluster has enough active nodes to maintain quorum if one goes down. If taking a node offline would cause quorum loss, maintenance mode will not activate.\n\n    > [!WARNING]\n    > This check does not consider if other nodes are already in maintenance mode. Do not put the majority of nodes into maintenance mode and restart them simultaneously, as this can cause the cluster to lose quorum and lead to data loss.\n\n1. If no maintenance mode snapshots already exist or if you use `overwrite_snapshot` when you activate maintenance mode, Redis Software creates a new node snapshot that records the node's shard and endpoint configuration.\n\n1. Marks the node as a quorum node to prevent shards and endpoints from migrating to it.\n\n    At this point, [`rladmin status`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status) displays the node's shards field in yellow, which indicates that shards cannot migrate to the node.\n\n    ![](https://redis.io/docs/latest/images/rs/maintenance_mode.png)\n\n1. Migrates shards and binds endpoints to other nodes, when space is available.\n\nMaintenance mode does not demote a master node by default. The cluster elects a new master node when the original master node restarts. \n\nAdd the `demote_node` option to the `rladmin` command to [demote a master node](#demote-a-master-node) when you activate maintenance mode.\n\nTo activate maintenance mode for a node, run the following command:\n\n[code example]\n\nYou can start server maintenance if:\n\n- All shards and endpoints have moved to other nodes\n\n- Enough nodes are still online to maintain quorum"},{"id":"prevent-replica-shard-migration","title":"Prevent replica shard migration","role":"content","text":"If you do not have enough resources available to move all of the shards to other nodes, you can turn maintenance mode on without migrating the replica shards.\n\nBefore you prevent replica shard migration during maintenance mode, consider the following effects:\n\n- Replica shards remain on the node during maintenance.\n\n- If the maintenance node fails, the master shards do not have replica shards to maintain data redundancy and high availability.\n\n- Replica shards that remain on the node can still be promoted during failover to preserve availability.\n\nTo activate maintenance mode without replica shard migration, run:\n\n[code example]"},{"id":"demote-a-master-node","title":"Demote a master node","role":"example","text":"If maintenance might affect connectivity to the master node, you can demote the master node when you  activate maintenance mode.  This lets the cluster elect a new master node.\n\nTo demote a master node when activating maintenance mode, run:\n\n[code example]"},{"id":"verify-maintenance-mode-activation","title":"Verify maintenance mode activation","role":"content","text":"To verify maintenance mode for a node, use `rladmin status` and review the node's shards field.  If that value is displayed in yellow (shown earlier), then the node is in maintenance mode.\n\nAvoid activating maintenance mode when it is already active.  Maintenance mode activations stack.  If you activate maintenance mode for a node that is already in maintenance mode, you will have to deactivate maintenance mode twice in order to restore full functionality."},{"id":"deactivate-maintenance-mode","title":"Deactivate maintenance mode","role":"content","text":"When you deactivate maintenance mode, Redis Software:\n\n1. Loads a [specified node snapshot](#specify-a-snapshot) or defaults to the latest maintenance mode snapshot.\n\n1. Unmarks the node as a quorum node to allow shards and endpoints to migrate to the node.\n\n1. Restores the shards and endpoints that were in the node at the time of the snapshot.\n\n1. Deletes the snapshot.\n\nTo deactivate maintenance mode after server maintenance, run:\n\n[code example]\n\nBy default, a snapshot is required to deactivate maintenance mode.  If the snapshot cannot be restored, deactivation is cancelled and the node remains in maintenance mode.  In such events, it may be necessary to [reset node status](#reset_node_status)."},{"id":"specify-a-snapshot","title":"Specify a snapshot","role":"content","text":"When you turn off maintenance mode, you can restore the node configuration from a maintenance mode snapshot or any snapshots previously created by [`rladmin node <node_id> snapshot create`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/node/snapshot#node-snapshot-create). If you do not specify a snapshot, Redis Software uses the latest maintenance mode snapshot by default.\n\nTo get a list of available snapshots, run:\n\n[code example]\n\nTo specify a snapshot when you turn maintenance mode off, run:\n\n[code example]\n\n> [!NOTE]\n> If an error occurs when you turn on maintenance mode, the snapshot is not deleted.\n> When you rerun the command, use the snapshot from the initial attempt since it contains the original state of the node."},{"id":"skip-shard-restoration","title":"Skip shard restoration","role":"content","text":"You can prevent the migrated shards and endpoints from returning to the original node after you turn off maintenance mode.\n\nTo turn maintenance mode off and skip shard restoration, run:\n\n[code example]"},{"id":"reset-node-status","title":"Reset node status","role":"content","text":"In extreme cases, you may need to reset a node's status.  Run the following commands to do so:\n\n[code example]\n\nUse these commands with caution.  For best results, contact Support before running these commands."},{"id":"cluster-status-example","title":"Cluster status example","role":"content","text":"This example shows how the output of [`rladmin status`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status) changes when you turn on maintenance mode for a node.\n\nThe cluster status before turning on maintenance mode:\n\n[code example]\n\nThe cluster status after turning on maintenance mode:\n\n[code example]\n\nAfter turning on maintenance mode for node 2, Redis Software saves a snapshot of its configuration and then moves its shards and endpoints to node 3.\n\nNow node 2 has `0/0` shards because shards cannot migrate to it while it is in maintenance mode."},{"id":"maintenance-mode-rest-api","title":"Maintenance mode REST API","role":"content","text":"You can also turn maintenance mode on or off using [REST API requests](https://redis.io/docs/latest/operate/rs/references/rest-api) to [<nobr>POST `/nodes/{node_uid}/actions/{action}`</nobr>](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/nodes/actions#post-node-action)."},{"id":"activate-maintenance-mode-rest-api","title":"Activate maintenance mode (REST API)","role":"content","text":"Use <nobr>`POST /nodes/{node_uid}/actions/maintenance_on`</nobr> to activate maintenance mode:\n\n[code example]\n\nYou can set `evict_ha_replica` and `evict_active_active_replica` to `false` to [prevent replica shard migration](#prevent-replica-shard-migration).\n\nThe `maintenance_on` request returns a JSON response body:\n\n[code example]"},{"id":"deactivate-maintenance-mode-rest-api","title":"Deactivate maintenance mode (REST API)","role":"content","text":"Use <nobr>`POST /nodes/{node_uid}/actions/maintenance_off`</nobr> deactivate maintenance mode:\n\n[code example]\n\nThe `skip_shards_restore` boolean flag allows the `maintenance_off` action to [skip shard restoration](#skip-shard-restoration) when set to `true`.\n\nThe `maintenance_off` request returns a JSON response body:\n\n[code example]"},{"id":"track-action-status","title":"Track action status","role":"content","text":"You can send a request to [<nobr>GET `/nodes/{node_uid}/actions/{action}`</nobr>](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/nodes/actions#get-node-action) to track the [status](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/action) of the `maintenance_on` and `maintenance_off` actions.\n\nThis request returns the status of the `maintenance_on` action:\n\n[code example]\n\nThe response body:\n\n[code example]"}],"examples":[{"id":"activate-maintenance-mode-ex0","language":"sh","code":"rladmin node <node_id> maintenance_mode on overwrite_snapshot","section_id":"activate-maintenance-mode"},{"id":"prevent-replica-shard-migration-ex0","language":"sh","code":"rladmin node <node_id> maintenance_mode on evict_ha_replica disabled evict_active_active_replica disabled","section_id":"prevent-replica-shard-migration"},{"id":"demote-a-master-node-ex0","language":"sh","code":"rladmin node <node_id> maintenance_mode on demote_node","section_id":"demote-a-master-node"},{"id":"deactivate-maintenance-mode-ex0","language":"sh","code":"rladmin node <node_id> maintenance_mode off","section_id":"deactivate-maintenance-mode"},{"id":"specify-a-snapshot-ex0","language":"sh","code":"rladmin node <node_id> snapshot list","section_id":"specify-a-snapshot"},{"id":"specify-a-snapshot-ex1","language":"sh","code":"rladmin node <node_id> maintenance_mode off snapshot_name <snapshot_name>","section_id":"specify-a-snapshot"},{"id":"skip-shard-restoration-ex0","language":"sh","code":"rladmin node <node_id> maintenance_mode off skip_shards_restore","section_id":"skip-shard-restoration"},{"id":"reset-node-status-ex0","language":"plaintext","code":"$ rladmin tune node <node_id> max_listeners 100\n$ rladmin tune node <node_id> quorum_only disabled","section_id":"reset-node-status"},{"id":"cluster-status-example-ex0","language":"sh","code":"redislabs@rp1_node1:/opt$ rladmin status\nCLUSTER NODES:\nNODE:ID   ROLE     ADDRESS       EXTERNAL_ADDRESS     HOSTNAME    SHARDS\n*node:1   master   172.17.0.2                         rp1_node1   2/100\nnode:2    slave    172.17.0.4                         rp3_node1   2/100\nnode:3    slave    172.17.0.3                         rp2_node1   0/100","section_id":"cluster-status-example"},{"id":"cluster-status-example-ex1","language":"sh","code":"redislabs@rp1_node1:/opt$ rladmin node 2 maintenance_mode on\nPerforming maintenance_on action on node:2: 0%\ncreated snapshot NodeSnapshot<name=maintenance_mode_2019-03-14_09-50-59,time=None,node_uid=2>\n\nnode:2 will not accept any more shards\nPerforming maintenance_on action on node:2: 100%\nOK\nredislabs@rp1_node1:/opt$ rladmin status\nCLUSTER NODES:\nNODE:ID   ROLE     ADDRESS       EXTERNAL_ADDRESS     HOSTNAME    SHARDS\n*node:1   master   172.17.0.2                         rp1_node1   2/100\nnode:2    slave    172.17.0.4                         rp3_node1   0/0\nnode:3    slave    172.17.0.3                         rp2_node1   2/100","section_id":"cluster-status-example"},{"id":"activate-maintenance-mode-rest-api-ex0","language":"sh","code":"POST https://<hostname>:9443/v1/nodes/<node_id>/actions/maintenance_on\n{\n    \"overwrite_snapshot\": true,\n    \"evict_ha_replica\": true,\n    \"evict_active_active_replica\": true\n}","section_id":"activate-maintenance-mode-rest-api"},{"id":"activate-maintenance-mode-rest-api-ex1","language":"json","code":"{\n    \"status\":\"queued\",\n    \"task_id\":\"<task-id-guid>\"\n}","section_id":"activate-maintenance-mode-rest-api"},{"id":"deactivate-maintenance-mode-rest-api-ex0","language":"sh","code":"POST https://<hostname>:9443/v1/nodes/<node_id>/actions/maintenance_off\n{ \"skip_shards_restore\": false }","section_id":"deactivate-maintenance-mode-rest-api"},{"id":"deactivate-maintenance-mode-rest-api-ex1","language":"json","code":"{\n    \"status\":\"queued\",\n    \"task_id\":\"<task-id-guid>\"\n}","section_id":"deactivate-maintenance-mode-rest-api"},{"id":"track-action-status-ex0","language":"sh","code":"GET https://<hostname>:9443/v1/nodes/<node_id>/actions/maintenance_on","section_id":"track-action-status"},{"id":"track-action-status-ex1","language":"json","code":"{\n    \"status\":\"completed\",\n    \"task_id\":\"38c7405b-26a7-4379-b84c-cab4b3db706d\"\n}","section_id":"track-action-status"}]}
{"schema_version":2,"id":"operate/rs/clusters/new-cluster-setup","title":"Set up a new cluster","url":"https://redis.io/docs/latest/operate/rs/clusters/new-cluster-setup/","summary":"How to set up a new cluster using the management UI.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:16+01:00","page_type":"content","content_hash":"087029f5f67a5bcacc9e7d81f9b827f6ca0f24a6a0264a54bb5b5854292b4a0d","sections":[{"id":"content","title":"Content","role":"content","text":"A Redis Software cluster typically consists of several nodes.\nFor production deployments, we recommend an uneven number of nodes, with a minimum of three.\n\n> [!NOTE]\n> In a cluster that consists of only one node, some features and capabilities are not enabled,\n> such as database replication that provides high availability.\n\nTo set up a new cluster, you must first [install the Redis Software package](https://redis.io/docs/latest/operate/rs/installing-upgrading)\nand then set up the cluster as described below.\nAfter the cluster is created you can [add multiple nodes to the cluster](https://redis.io/docs/latest/operate/rs/clusters/add-node).\n\nTo create a cluster:\n\n1. In a browser, go to `https://<name or IP address of the machine with Redis Software installed>:8443`.\n    For example, if you installed Redis Software on a machine with IP address 10.0.1.34, go to <https://10.0.1.34:8443>.\n\n    > [!NOTE]\n    > - The management UI uses a [self-signed certificate for TLS encryption](https://redis.io/docs/latest/operate/rs/security/certificates/updating-certificates).\n    > - If the machine has both an internal IP address and an external IP address, use the external IP address to access the setup UI.\n\n1. Select **Create new cluster**.\n\n1. Enter an email and password for the administrator account, then select **Next** to proceed to cluster setup.\n\n1. Enter your cluster license key if you have one. Otherwise, the cluster uses the trial license by default.\n\n1. In the **Configuration** section:\n\n    1. For **FQDN (Fully Qualified Domain Name)**, enter a unique name for the cluster.\n\n        See the [instructions for DNS setup](https://redis.io/docs/latest/operate/rs/networking/cluster-dns)\n        to make sure your cluster is reachable by name.\n\n    1. Choose whether to [**Enable private & public endpoints support**](https://redis.io/docs/latest/operate/rs/networking/private-public-endpoints).\n\n    1. Choose whether to [**Enable rack-zone awareness**](https://redis.io/docs/latest/operate/rs/clusters/configure/rack-zone-awareness).\n\n1. Click **Next**.\n\n1. Configure storage and network settings:\n\n    1. Enter a path for [*Ephemeral storage*](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/persistent-ephemeral-storage), or leave the default path.\n\n    1. Enter a path for [*Persistent storage*](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/persistent-ephemeral-storage),\n        or leave the default path.\n\n    1. To enable [*Redis Flex or Auto Tiering*](https://redis.io/docs/latest/operate/rs/databases/flash),\n        select **Enable flash storage** and enter the path to the flash storage.\n\n    1. If the cluster is configured to support [rack-zone awareness](https://redis.io/docs/latest/operate/rs/clusters/configure/rack-zone-awareness), set the **Rack-zone ID** for the new node.\n\n    1. If your machine has multiple IP addresses, assign a single IPv4 type address for **Node-to-node communication (internal traffic)** and multiple IPv4/IPv6 type addresses for **External traffic**.\n\n1. Select **Create cluster**.\n\n1. Click **OK** to confirm that you are aware of the replacement of the HTTPS TLS certificate on the node,\n    and proceed through the browser warning.\n\nAfter a short wait, your cluster is created and you can sign in to the Cluster Manager UI.\n\nYou can now access any of the management capabilities, including:\n\n- [Creating a new database](https://redis.io/docs/latest/operate/rs/databases/create)\n- [Joining a new node to a cluster](https://redis.io/docs/latest/operate/rs/clusters/add-node)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/clusters/optimize/disk-sizing-heavy-write-scenarios","title":"Disk sizing for heavy write scenarios","url":"https://redis.io/docs/latest/operate/rs/clusters/optimize/disk-sizing-heavy-write-scenarios/","summary":"Sizing considerations for persistent disk space for heavy throughput databases.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:16+01:00","page_type":"content","content_hash":"85bce486dab65239966defe1737c3d2b594426c87eb60eaa1d2f1875b86ca541","sections":[{"id":"overview","title":"Overview","role":"overview","text":"In extreme write scenarios when append-only files (AOF) are enabled, the AOF rewrite process can require considerably more disk space for [database persistence](https://redis.io/docs/latest/operate/rs/databases/configure/database-persistence).\n\nFor disk size requirements for standard usage, see [Hardware requirements](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/hardware-requirements)."},{"id":"estimate-required-disk-space","title":"Estimate required disk space","role":"content","text":"To estimate the required persistent disk space for AOF rewrite purposes in extreme write scenarios, use the following formula:\n\n**X (1 + 3Y + Y²)**\n\nWhere:\n- **X** = size of each shard in GB\n- **Y** = number of shards"},{"id":"examples","title":"Examples","role":"example","text":"The following examples show how to calculate the persistent disk space required for heavy write scenarios for different database configurations, where:\n\n- **Database size** is the memory limit configured for the database.\n\n- **Number of shards** is the total number of shards (primary shards + replica shards)."},{"id":"example-1","title":"Example 1","role":"example","text":"- Database size = 10 GB\n- Number of shards = 4\n\n1. Calculate the shard size:\n\n    [code example]\n\n1. Use the formula to calculate the required persistent disk space:\n\n    [code example]\n\n1. Round up to 73 GB of required disk space."},{"id":"example-2","title":"Example 2","role":"example","text":"- Database size = 10 GB\n- Number of shards = 16\n\n1. Calculate the shard size:\n\n    [code example]\n\n1. Use the formula to calculate the required persistent disk space:\n\n    [code example]\n\n1. Round up to 191 GB of required disk space."},{"id":"example-3","title":"Example 3","role":"example","text":"- Database size = 40 GB\n- Number of shards = 5\n\n1. Calculate the shard size:\n\n    [code example]\n\n1. Use the formula to calculate the required persistent disk space:\n\n    [code example]\n\n1. Required disk space: 328 GB."},{"id":"example-4","title":"Example 4","role":"example","text":"- Database size = 40 GB\n- Number of shards = 15\n\n1. Calculate the shard size:\n\n    [code example]\n\n1. Use the formula to calculate the required persistent disk space:\n\n    [code example]\n\n1. Round up to 724 GB of required disk space."}],"examples":[{"id":"example-1-ex0","language":"sh","code":"Shard size = database size / number of shards\n               = 10 GB / 4 shards\n               = 2.5 GB per shard","section_id":"example-1"},{"id":"example-1-ex1","language":"sh","code":"Disk space = X (1 + 3Y + Y²)\n               = 2.5 (1 + 3 × 4 + 4²)\n               = 2.5 (1 + 12 + 16)\n               = 2.5 × 29\n               = 72.5 GB","section_id":"example-1"},{"id":"example-2-ex0","language":"sh","code":"Shard size = database size / number of shards\n               = 10 GB / 16 shards\n               = 0.625 GB per shard","section_id":"example-2"},{"id":"example-2-ex1","language":"sh","code":"Disk space = X (1 + 3Y + Y²)\n               = 0.625 (1 + 3 × 16 + 16²)\n               = 0.625 (1 + 48 + 256)\n               = 0.625 × 305\n               = 190.625 GB","section_id":"example-2"},{"id":"example-3-ex0","language":"sh","code":"Shard size = database size / number of shards\n               = 40 GB / 5 shards\n               = 8 GB per shard","section_id":"example-3"},{"id":"example-3-ex1","language":"sh","code":"Disk space = X (1 + 3Y + Y²)\n               = 8 (1 + 3 × 5 + 5²)\n               = 8 (1 + 15 + 25)\n               = 8 × 41\n               = 328 GB","section_id":"example-3"},{"id":"example-4-ex0","language":"sh","code":"Shard size = database size / number of shards\n               = 40 GB / 15 shards\n               = 2.67 GB per shard","section_id":"example-4"},{"id":"example-4-ex1","language":"sh","code":"Disk space = X (1 + 3Y + Y²)\n               = 2.67 (1 + 3 × 15 + 15²)\n               = 2.67 (1 + 45 + 225)\n               = 2.67 × 271\n               = 723.57 GB","section_id":"example-4"}]}
{"schema_version":2,"id":"operate/rs/clusters/optimize","title":"Optimize clusters","url":"https://redis.io/docs/latest/operate/rs/clusters/optimize/","summary":"Configuration changes and information you can use to optimize your performance and memory usage.","tags":["docs","operate","rs"],"last_updated":"2024-04-02T12:43:54-05:00","children":[{"id":"operate/rs/clusters/optimize/memtier-benchmark","summary":"Use the `memtier_benchmark` tool to perform a performance benchmark of Redis Software.","title":"Benchmarking Redis Software","url":"https://redis.io/docs/latest/operate/rs/clusters/optimize/memtier-benchmark/"},{"id":"operate/rs/clusters/optimize/disk-sizing-heavy-write-scenarios","summary":"Sizing considerations for persistent disk space for heavy throughput databases.","title":"Disk sizing for heavy write scenarios","url":"https://redis.io/docs/latest/operate/rs/clusters/optimize/disk-sizing-heavy-write-scenarios/"},{"id":"operate/rs/clusters/optimize/optimization","summary":"Optimize your cluster environments for better performance.","title":"Cluster environment optimization","url":"https://redis.io/docs/latest/operate/rs/clusters/optimize/optimization/"},{"id":"operate/rs/clusters/optimize/turn-off-services","summary":"Turn off services to free memory and improve performance.","title":"Turn off services to free system memory","url":"https://redis.io/docs/latest/operate/rs/clusters/optimize/turn-off-services/"},{"id":"operate/rs/clusters/optimize/oss-cluster-api","summary":"Use the Redis OSS Cluster API to improve performance and keep applications current with cluster topology changes.","title":"OSS Cluster API","url":"https://redis.io/docs/latest/operate/rs/clusters/optimize/oss-cluster-api/"},{"id":"operate/rs/clusters/optimize/wait","summary":"Use the wait command to take full advantage of Redis Software's replication-based durability.","title":"Use the WAIT command to improve data safety and durability","url":"https://redis.io/docs/latest/operate/rs/clusters/optimize/wait/"}],"page_type":"index","sections":[],"examples":[]}
{"schema_version":2,"id":"operate/rs/clusters/optimize/memtier-benchmark","title":"Benchmarking Redis Software","url":"https://redis.io/docs/latest/operate/rs/clusters/optimize/memtier-benchmark/","summary":"Use the `memtier_benchmark` tool to perform a performance benchmark of Redis Software.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"41b007a20616eb86e2e95ec06e1e8bc9dc7795f6110174032666a8c4761a323e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Use the `memtier_benchmark` tool to perform a performance benchmark of Redis Software.\n\nPrerequisites:\n\n- Redis Software installed\n- A cluster configured\n- A database created\n\nFor help with the prerequisites, see the [Redis Software quickstart](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/redis-enterprise-software-quickstart).\n\nIt is recommended to run memtier_benchmark on a separate node that is\nnot part of the cluster being tested. If you run it on a node of the\ncluster, be mindful that it affects the performance of both the\ncluster and memtier_benchmark.\n\n[code example]\n\nThis command instructs memtier_benchmark to connect to your Redis\nEnterprise database and generates a load doing the following:\n\n- A 50/50 Set to Get ratio\n- Each object has random data in the value"},{"id":"populate-a-database-with-testing-data","title":"Populate a database with testing data","role":"content","text":"If you need to populate a database with some test data for a proof of\nconcept, or failover testing, etc. here is an example for you.\n\n[code example]\n\nThis command instructs memtier_benchmark to connect to your Redis\nEnterprise database and generates a load doing the following:\n\n- Write objects only, no reads\n- A 500 byte object\n- Each object has random data in the value\n- Each key has a random pattern, then a colon, followed by a\n    random pattern.\n\nRun this command until it fills up your database to where you want it\nfor testing. The easiest way to check is on the database metrics page.\n\n![](https://redis.io/docs/latest/images/rs/memtier_metrics_page.png)\n\nAnother use for memtier_benchmark is to populate a database with data\nfor failure testing."}],"examples":[{"id":"overview-ex0","language":"sh","code":"/opt/redislabs/bin/memtier_benchmark -s $DB_HOST -p $DB_PORT -a $DB_PASSWORD -t 4 -R --ratio=1:1","section_id":"overview"},{"id":"populate-a-database-with-testing-data-ex0","language":"sh","code":"/opt/redislabs/bin/memtier_benchmark -s $DB_HOST -p $DB_PORT -a $DB_PASSWORD -R -n allkeys -d 500 --key-pattern=P:P --ratio=1:0","section_id":"populate-a-database-with-testing-data"}]}
{"schema_version":2,"id":"operate/rs/clusters/optimize/optimization","title":"Cluster environment optimization","url":"https://redis.io/docs/latest/operate/rs/clusters/optimize/optimization/","summary":"Optimize your cluster environments for better performance.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:16+01:00","page_type":"content","content_hash":"ef3b70a7188f741791e1b7b59ebde7f2a2477488eb80126197d1b04cc8039899","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software uses various algorithms to optimize\nperformance. As part of this process, Redis Software examines usage\nand load to adjust its runtime configuration. Depending\non your specific usage and load, Redis Software might take some\ntime to adjust for optimal performance.\n\nTo ensure optimal performance, you must run your workload several times\nand for a long duration until performance stabilizes."},{"id":"failure-detection-sensitivity-policies","title":"Failure detection sensitivity policies","role":"content","text":"You can optimize your cluster's thresholds and timeouts for different environments using the `failure_detection_sensitivity` cluster policy:\n\n- `high` (previously known as `local-network watchdog_profile`) – high failure detection sensitivity, lower thresholds, and faster failure detection and failover\n\n- `low` (previously known as `cloud watchdog_profile`) – low failure detection sensitivity and higher tolerance for latency variance (also called network jitter)\n\nDepending on which policy you choose, Redis Software uses different\nthresholds to make operation-related decisions.\n\nThe default policy is `high` failure detection sensitivity for `local-network` environments. If you are\nrunning Redis Software in a cloud environment, you should change the\nconfiguration."},{"id":"change-failure-detection-sensitivity","title":"Change failure detection sensitivity","role":"content","text":"To change the cluster's `failure_detection_sensitivity`, run one of the following [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-cluster) commands.\n\n- For Redis Software version 6.4.2-69 and later, run:\n\n    [code example]\n\n- For Redis Software version 6.4.2-61 and earlier, run:\n\n    [code example]\n\nIf Redis Software still\ndoes not meet your performance expectations after following these instructions, [contact support](https://redis.com/company/support/) for help optimizing your environment."}],"examples":[{"id":"change-failure-detection-sensitivity-ex0","language":"sh","code":"rladmin tune cluster failure_detection_sensitivity [ low | high ]","section_id":"change-failure-detection-sensitivity"},{"id":"change-failure-detection-sensitivity-ex1","language":"sh","code":"rladmin tune cluster watchdog_profile [ cloud | local-network ]","section_id":"change-failure-detection-sensitivity"}]}
{"schema_version":2,"id":"operate/rs/clusters/optimize/oss-cluster-api","title":"OSS Cluster API","url":"https://redis.io/docs/latest/operate/rs/clusters/optimize/oss-cluster-api/","summary":"Use the Redis OSS Cluster API to improve performance and keep applications current with cluster topology changes.","tags":["docs","operate","rs","rc","kubernetes"],"last_updated":"2026-09-24T10:14:16+01:00","page_type":"content","content_hash":"f0a089870b8132337b5380b0913a3ddf878858da919070d3a5afedaf8d769628","sections":[{"id":"content","title":"Content","role":"content","text":"OSS Cluster API reduces access times and latency with near-linear scalability.\nThe OSS Cluster API provides a simple mechanism for Redis clients to know the cluster topology.\n\nClients must first connect to the master node to get the cluster topology,\nand then they connect directly to the Redis proxy on each node that hosts a master shard.\n\n> [!NOTE]\n> You must use a client that supports the cluster API to connect to a database\n> that has the cluster API enabled.\n\n\nYou can use the Redis OSS Cluster API along with other Redis Enterprise Software high availability\nto get high performance with low latency\nand let applications stay current with cluster topology changes, including add node, remove node, and node failover.\n\nFor more about working with the OSS Cluster API in Redis Software, see [Enable OSS Cluster API](https://redis.io/docs/latest/operate/rs/databases/configure/oss-cluster-api). \n\nTo learn how to enable OSS Cluster API in Redis Cloud, see [Clustering Redis databases](https://redis.io/docs/latest/operate/rc/databases/configuration/clustering#cluster-api).\n\nTo enable OSS Cluster API in Kubernetes, see [Enable cluster-aware clients (OSS Cluster API)](https://redis.io/docs/latest/operate/kubernetes/networking/cluster-aware-clients)."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/clusters/optimize/turn-off-services","title":"Turn off services to free system memory","url":"https://redis.io/docs/latest/operate/rs/clusters/optimize/turn-off-services/","summary":"Turn off services to free memory and improve performance.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:16+01:00","page_type":"content","content_hash":"36a7347ac3962924e391d4298629d26198b76a506bca538a7dffa1cd9c0537cb","sections":[{"id":"content","title":"Content","role":"content","text":"The Redis Software cluster nodes host a range of services that support the cluster processes.\nIn most deployments, either all of these services are required,\nor there are enough memory resources on the nodes for the database requirements.\n\nIn a deployment with limited memory resources, certain services can be disabled from API endpoint to free system memory or using the `rladmin` command.\nBefore you turn off a service, make sure that your deployment does not depend on that service.\nAfter you turn off a service, you can re-enable in the same way.\n\nThe services that you can turn off are:\n\n- RS Admin Console - `cm_server`\n- Logs in CSV format - `stats_archiver`\n- [LDAP authentication](https://redis.io/docs/latest/operate/rs/security/access-control/ldap) - `saslauthd`\n- [Discovery service](https://redis.io/docs/latest/operate/rs/networking/cluster-dns)- `mdns_server`, `pdns_server`\n- [Active-Active databases](https://redis.io/docs/latest/operate/rs/databases/active-active) - `crdb_coordinator`, `crdb_worker`\n- Alert Manager - `alert_mgr` (For best results, disable only if you have an alternate alert system.)\n\nTo turn off a service with the `rladmin cluster config` command, use the `services` parameter and the name of the service, followed by `disabled`.\n[code example]\n\nTo turn off a service with the API, use the [`PUT /v1/services_configuration`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/services_configuration#put-cluster-services_config) endpoint\nwith the name of the service and the operating mode (enabled/disabled) in JSON format.\n\nFor example:\n- To turn off the Redis Software Cluster Manager UI, use this PUT request:\n\n    [code example]\n\n- To turn off the CRDB services and enable the `stats_archiver` for cluster component statistics, use this PUT request:\n\n    [code example]"}],"examples":[{"id":"content-ex0","language":"text","code":"rladmin cluster config\n        [ services <service_name> <enabled | disabled> ]","section_id":"content"},{"id":"content-ex1","language":"sh","code":"PUT https://[host][:9443]/v1/cluster/services_configuration\n    '{\n        \"cm_server\":{\n            \"operating_mode\":\"disabled\"\n        }\n    }'","section_id":"content"},{"id":"content-ex2","language":"sh","code":"PUT https://[host][:9443]/v1/cluster/services_configuration\n    '{\n        \"crdb_coordinator\":{\n            \"operating_mode\":\"disabled\"\n        },\n        \"crdb_worker\":{\n            \"operating_mode\":\"disabled\"\n        },\n        \"stats_archiver\":{\n            \"operating_mode\":\"enabled\"\n        }\n    }'","section_id":"content"}]}
{"schema_version":2,"id":"operate/rs/clusters/optimize/wait","title":"Use the WAIT command to improve data safety and durability","url":"https://redis.io/docs/latest/operate/rs/clusters/optimize/wait/","summary":"Use the wait command to take full advantage of Redis Software's replication-based durability.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"9cc9d4683fba0e85fef7fd66d55ba76cf1028a8a6c78f82a9daef7aed9b218ee","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software comes with the ability to replicate data\nto another replica for high availability and persist in-memory data on\ndisk permanently for durability. With the [`WAIT`](https://redis.io/docs/latest/commands/wait) command, you can\ncontrol the consistency and durability guarantees for the replicated and\npersisted database."},{"id":"non-blocking-redis-write-operation","title":"Non-blocking Redis write operation","role":"content","text":"Any updates that are issued to the database are typically performed with the following flow:\n\n1. Application issues a write.\n1. Proxy communicates with the correct primary shard in the system that contains the given key.\n1. The acknowledgment is sent to proxy after the write operation completes.\n1. The proxy sends the acknowledgment back to the application.\n\nIndependently, the write is communicated from the primary shard to the replica, and\nreplication acknowledges the write back to the primary shard. These are steps 5 and 6.\n\nIndependently, the write to a replica is also persisted to disk and\nacknowledged within the replica. These are steps 7 and 8.\n\n![](https://redis.io/docs/latest/images/rs/weak-consistency.png)"},{"id":"blocking-write-operation-on-replication","title":"Blocking write operation on replication","role":"content","text":"With the [`WAIT`](https://redis.io/docs/latest/commands/wait) or [`WAITAOF`](https://redis.io/docs/latest/commands/waitaof) commands, applications can ask to wait for\nacknowledgments only after replication or persistence is confirmed on\nthe replica. The flow of a write operation with `WAIT` or `WAITAOF` is:\n\n1. The application issues a write.\n1. The proxy communicates with the correct primary shard in the system that contains the given key.\n1. Replication communicates the update to the replica shard.\n1. If using `WAITAOF` and the AOF every write setting, the replica persists the update to disk before sending the acknowledgment.\n1. The acknowledgment is sent back from the replica all the way to the proxy with steps 5 to 8.\n\nThe application only gets the acknowledgment from the write after durability is achieved with replication to the replica for `WAIT` or `WAITAOF` and to the persistent storage for `WAITAOF` only.\n\n![](https://redis.io/docs/latest/images/rs/strong-consistency.png)\n\nThe `WAIT` command always returns the number of replicas that acknowledged the write commands sent by the current client before the `WAIT` command, both in the case where the specified number of replicas are reached, or when the timeout is reached. In Redis Software, the number of replicas is always 1 for databases with high availability enabled.\n\nSee the [`WAITAOF`](https://redis.io/docs/latest/commands/waitaof) command for details for enhanced data safety and durability capabilities introduced with Redis 7.2."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/clusters/quorum-node","title":"Set up a quorum node","url":"https://redis.io/docs/latest/operate/rs/clusters/quorum-node/","summary":"To reduce infrastructure costs, you can set up a quorum-only node with minimal resources.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:16+01:00","page_type":"content","content_hash":"95626cd3fd75df7778686a8bee15f4e0815e6e2087855e7aee4fa69c643ebd4d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Clusters require an odd number of nodes to maintain [quorum](https://en.wikipedia.org/wiki/Quorum_(distributed_computing)) and avoid [split-brain](https://en.wikipedia.org/wiki/Split-brain_(computing)) scenarios when making decisions. If you need to add another node to a cluster for quorum but want to provision fewer resources and reduce infrastructure costs, you can set up a quorum node instead of a regular node.\n\nQuorum nodes participate in cluster quorum decisions and can act as a tiebreaker. However, they do not host database shards or endpoints, which reduces their resource requirements. A quorum node should have at least 2 cores and 8 GB of RAM. See the [hardware requirements](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/hardware-requirements) for more details and considerations."},{"id":"enable-quorum_only-setting","title":"Enable quorum_only setting","role":"content","text":"1. Run [`rladmin status nodes`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status#status-nodes) and find a node with no shards.\n\n   In the following example, nodes without shards have `0/100` for the `SHARDS` column.\n\n   [code example]\n\n2. Enable `quorum_only` for the node using [`rladmin tune node`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-node):\n   [code example]\n\n3. Rerun `rladmin status nodes` to verify the quorum node shows  `0/0` for `SHARDS` and `0KB/0KB` for `PROVISIONAL_RAM`.\n\n   [code example]"}],"examples":[{"id":"enable-quorum_only-setting-ex0","language":"bash","code":"$ rladmin status nodes\n   CLUSTER NODES:\n   NODE:ID    ROLE      ADDRESS             EXTERNAL_ADDRESS        HOSTNAME                SHARDS    CORES         FREE_RAM            PROVISIONAL_RAM        VERSION           STATUS\n   node:1     master    192.0.2.0                                   3d99db1fdf4b            0/100     2             6.33GB/7.79GB       4.93GB/6.39GB          7.8.6-36          OK\n   node:2     slave     198.51.100.0                                b87cc06c830f            2/100     2             6.44GB/7.79GB       1.04GB/6.39GB          7.8.6-36          OK\n   *node:3    slave     203.0.113.0                                 fc7a3d332458            0/100     2             6.45GB/7.79GB       4.93GB/6.39GB          7.8.6-36          OK","section_id":"enable-quorum_only-setting"},{"id":"enable-quorum_only-setting-ex1","language":"bash","code":"$ rladmin tune node <node-id> quorum_only enabled","section_id":"enable-quorum_only-setting"},{"id":"enable-quorum_only-setting-ex2","language":"sh","code":"$ rladmin status nodes\n   CLUSTER NODES:\n   NODE:ID    ROLE      ADDRESS             EXTERNAL_ADDRESS        HOSTNAME                SHARDS    CORES         FREE_RAM            PROVISIONAL_RAM        VERSION           STATUS\n   node:1     master    192.0.2.0                                   3d99db1fdf4b            0/100     2             6.33GB/7.79GB       4.93GB/6.39GB          7.8.6-36          OK\n   node:2     slave     198.51.100.0                                b87cc06c830f            2/100     2             6.44GB/7.79GB       1.04GB/6.39GB          7.8.6-36          OK\n   *node:3    slave     203.0.113.0                                 fc7a3d332458            0/0       2             6.45GB/7.79GB       0KB/0KB                7.8.6-36          OK","section_id":"enable-quorum_only-setting"}]}
{"schema_version":2,"id":"operate/rs/clusters/remove-node","title":"Remove a node","url":"https://redis.io/docs/latest/operate/rs/clusters/remove-node/","summary":"Remove a node from your Redis Software cluster.","tags":["docs","operate","rs"],"last_updated":"2026-10-02T16:06:07+01:00","page_type":"content","content_hash":"c36544d4c754a6f7eefd6883152a71cc24f3f46ead0eea4a8ef16220f2106053","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You might want to remove a node from a Redis Software cluster for one of the following reasons:\n\n- To [permanently remove a node](#permanently-remove-a-node) if you no longer need the extra capacity.\n- To [replace a faulty node](#replace-a-faulty-node) with a healthy node.\n- To [replace a healthy node](#replace-a-healthy-node) with a different node.\n\nYou can configure [email alerts from the cluster](https://redis.io/docs/latest/operate/rs/monitoring/v1_monitoring#cluster-alerts) to notify you of cluster changes, including when a node is removed.\n\n> [!WARNING]\n> Read through these explanations thoroughly before taking\n> any action."},{"id":"permanently-remove-a-node","title":"Permanently remove a node","role":"content","text":"Permanently removing a node means you are decreasing cluster capacity.\nBefore trying to remove a node, make sure that the cluster has enough\ncapacity for all resources without that node, otherwise you cannot remove the node.\n\nIf there is not enough capacity in the cluster to facilitate removing\nthe node, you can either delete databases or add another node instead of\nthe one you would like to remove.\n\nDuring the removal process, the cluster migrates all resources from the\nnode being removed to other nodes in the cluster. In order to ensure\ndatabase connectivity, and database high availability (when replication\nis enabled), the cluster first creates replacement shards or endpoints\non one of the other nodes in the cluster, initiates failover as needed,\nand only then removes the node.\n\nIf a cluster has only two nodes (which is not recommended for production\ndeployments) and some databases have replication enabled, you cannot remove a node."},{"id":"replace-a-faulty-node","title":"Replace a faulty node","role":"content","text":"If the cluster has a faulty node that you would like to replace, you\nonly need to add a new node to the cluster. The cluster recognizes the\nexistence of a faulty node and automatically replaces the faulty node\nwith the new node.\n\nFor guidelines, refer to [Replacing a faulty\nnode](https://redis.io/docs/latest/operate/rs/clusters/replace-node)."},{"id":"replace-a-healthy-node","title":"Replace a healthy node","role":"content","text":"If you would like to replace a healthy node with a different node, you\nmust first add the new node to the cluster, migrate all the resources\nfrom the node you would like to remove, and only then remove the node.\n\nFor further guidance, refer to [adding a new node to a\ncluster](https://redis.io/docs/latest/operate/rs/clusters/add-node).\n\nYou can migrate resources by using the `rladmin` command-line interface\n(CLI). For guidelines, refer to [`rladmin` command-line interface\n(CLI)](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin).\n\n> [!NOTE]\n> The [DNS records](https://redis.io/docs/latest/operate/rs/networking/cluster-dns) must be updated each time a node is added or replaced."},{"id":"remove-a-node","title":"Remove a node","role":"content","text":"To remove a node using the Cluster Manager UI:\n\n1. On the **Nodes** screen, click ![More actions button](https://redis.io/docs/latest/images/rs/buttons/button-toggle-actions-vertical.png#no-click) for the node you want to remove.\n\n    ![Click the more actions button for a node to access node actions.](https://redis.io/docs/latest/images/rs/screenshots/nodes/secondary-nodes-more-actions.png)\n\n1. Select **Remove node** from the list.\n\n1. If there are shards on the node, select **Confirm node removal**.\n\n    ![The Confirm node removal checkbox is selected, and the Remove button is clickable.](https://redis.io/docs/latest/images/rs/screenshots/nodes/confirm-node-removal-selected.png)\n\n    If the node has no shards, the **Confirm node removal** checkbox does not appear.\n\n1. Click **Remove**.\n\n    Redis Software examines the node and the cluster, then takes the actions required to remove the node, such as migrating shards to other nodes. After the process finishes, the node is no longer shown in the UI.\n\n    > [!NOTE]\n    > At any point, you can click the **Cancel removal** button to stop the process. When cancelled, the current internal action is completed, and then the process stops.\n\nTo remove a node using the REST API, use [`POST /v1/nodes/<node_id>/actions/remove`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/nodes/actions#post-node-action).\n\nBy default, the remove node action completes after all resources migrate off the removed node. Node removal does not wait for migrated shards' persistence files to be created on the new nodes.\n\nTo change node removal to wait for the creation of new persistence files for all migrated shards, set `wait_for_persistence` to `true` in the request body or [update the cluster policy](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/policy#put-cluster-policy) `persistent_node_removal` to `true` to change the cluster's default behavior.\n\nFor example:\n\n[code example]\n\n> [!NOTE]\n> If you need to add a removed node back to the cluster,\n> you must [uninstall](https://redis.io/docs/latest/operate/rs/installing-upgrading/uninstalling)\n> and [reinstall](https://redis.io/docs/latest/operate/rs/installing-upgrading) the software on that node."}],"examples":[{"id":"remove-a-node-ex0","language":"sh","code":"POST https://<hostname>:9443/v1/nodes/<node_id>/actions/remove\n{\n    \"wait_for_persistence\": true\n}","section_id":"remove-a-node"}]}
{"schema_version":2,"id":"operate/rs/clusters/replace-node","title":"Replace a cluster node","url":"https://redis.io/docs/latest/operate/rs/clusters/replace-node/","summary":"Replace a node in your cluster that is down.","tags":["docs","operate","rs"],"last_updated":"2026-10-02T16:06:07+01:00","page_type":"content","content_hash":"1b4df0f7d05cb6ae170b723fa7177ee71a43d4cc1afed71adacbf2252d8944ce","sections":[{"id":"content","title":"Content","role":"content","text":"A failed node will appear as `Down` (![Node down icon](https://redis.io/docs/latest/images/rs/icons/node-down-icon.png#no-click)) in the **Nodes** list.\n\nTo replace a failed node: \n\n1. Prepare a new node identical to the old one.\n\n1.  Install and\n    configure Redis Software on the node. See [Install and setup](https://redis.io/docs/latest/operate/rs/installing-upgrading) for more information.\n\n    > [!NOTE]\n    > If you are using [Redis Flex or Auto Tiering](https://redis.io/docs/latest/operate/rs/databases/flash), make sure the required flash storage is set up on this new node.\n\n1. [Add the node](https://redis.io/docs/latest/operate/rs/clusters/add-node) to the cluster. Make sure the new node has as much available memory as the faulty\n    node.\n\n    If the new node does not have enough memory, you will be prompted to add a node with enough memory.\n\n1. A message will appear informing you that the cluster has a faulty node\n    and that the new node will replace the faulty node.\n\n    > [!NOTE]\n    > - If there is a faulty node in the cluster to which you are adding a node, Redis Software will use the new node to replace the faulty one.\n    > - Any existing [DNS records](https://redis.io/docs/latest/operate/rs/networking/cluster-dns) must be updated\n    > each time a node is added or replaced."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/databases/active-active/causal-consistency","title":"Enable causal consistency","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/causal-consistency/","summary":"Enable causal consistency in an Active-Active database.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"0718a5e832f06fc84792f4d788192fdfd303967776710496e8730d4ab9f2c1e7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"When you enable causal consistency in Active-Active databases,\nthe order of operations on a specific key are maintained across all Active-Active database instances.\n\nFor example, if operations A and B were applied on the same key and the effect of A was observed by the instance that initiated B before B was applied to the key.\nAll instances of an Active-Active database would then observe the effect of A before observing the effect of B.\nThis way, any causal relationship between operations on the same key is also observed and maintained by every replica."},{"id":"enable-causal-consistency","title":"Enable causal consistency","role":"content","text":"When you create an Active-Active database, you can enable causal consistency in the Cluster Manager UI:\n\n1. In the **Participating clusters** section of the **Create Active-Active database** screen, locate **Causal Consistency**:\n\n    ![The Participating clusters section of the Create Active-Active database screen.](https://redis.io/docs/latest/images/rs/screenshots/databases/active-active-databases/create-a-a-db-participating-clusters.png)\n\n1. Click **Change** to open the **Causal Consistency** dialog.\n\n1. Select **Enabled**:\n\n    ![Enabled is selected in the Causal Consistency dialog.](https://redis.io/docs/latest/images/rs/screenshots/databases/active-active-databases/enable-causal-consistency.png)\n\n1. Click **Change** to confirm your selection.\n\nAfter database creation, you can only turn causal consistency on or off using the REST API or `crdb-cli`.\nThe updated setting only affects commands and operations received after the change."},{"id":"causal-consistency-side-effects","title":"Causal consistency side effects","role":"content","text":"When the causal consistency option is enabled, each instance maintains the order of operations it received from another instance\nand relays that information to all other N-2 instances,\nwhere N represents the number of instances used by the Active-Active database.\n\nAs a result, network traffic is increased by a factor of (N-2).\nThe memory consumed by each instance and overall performance are also impacted when causal consistency is activated."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/databases/active-active/connect","title":"Connect to your Active-Active databases","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/connect/","summary":"How to connect to an Active-Active database using redis-cli or a sample Python application.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"ef3c5a9763efc5d9c4febf1b77c77a053420872f4197aeff7b9cc5bde05a1a86","sections":[{"id":"overview","title":"Overview","role":"overview","text":"With the Redis database created, you are ready to connect to your\ndatabase to store data. You can use one of the following ways to test\nconnectivity to your database:\n\n- Connect with redis-cli, the built-in command-line tool\n- Connect with a _Hello World_ application written in Python\n\nRemember we have two member Active-Active databases that are available for connections and\nconcurrent reads and writes. The member Active-Active databases are using bi-directional\nreplication to for the global Active-Active database.\n\n![](https://redis.io/docs/latest/images/rs/crdb-diagram.png)"},{"id":"connecting-using-rediscli","title":"Connecting using redis-cli","role":"content","text":"redis-cli is a simple command-line tool to interact with redis database.\n\n1. To use redis-cli on port 12000 from the node 1 terminal, run:\n\n    [code example]\n\n1. Store and retrieve a key in the database to test the connection with these\n    commands:\n\n    - `set key1 123`\n    - `get key1`\n\n    The output of the command looks like this:\n\n    [code example]\n\n1. Enter the terminal of node 1 in cluster 2, run the redis-cli, and\n   retrieve key1.\n\n    The output of the commands looks like this:\n\n    [code example]"},{"id":"connecting-using-_hello-world_-application-in-python","title":"Connecting using _Hello World_ application in Python","role":"content","text":"A simple python application running on the host machine can also connect\nto the database.\n\n> [!NOTE]\n> Before you continue, you must have python and\n> [redis-py](https://github.com/andymccurdy/redis-py#installation)\n> (python library for connecting to Redis) configured on the host machine\n> running the container.\n\n1. In the command-line terminal, create a new file called \"redis_test.py\"\n\n    [code example]\n\n1. Paste this code into the \"redis_test.py\" file.\n\n    This application stores a value in key1 in cluster 1, gets that value from\n    key1 in cluster 1, and gets the value from key1 in cluster 2.\n\n    [code example]\n\n1. To run the \"redis_test.py\" application, run:\n\n    [code example]\n\n    If the connection is successful, the output of the application looks like:\n\n    [code example]"}],"examples":[{"id":"connecting-using-rediscli-ex0","language":"sh","code":"redis-cli -p 12000","section_id":"connecting-using-rediscli"},{"id":"connecting-using-rediscli-ex1","language":"sh","code":"127.0.0.1:12000> set key1 123\n    OK\n    127.0.0.1:12000> get key1\n    \"123\"","section_id":"connecting-using-rediscli"},{"id":"connecting-using-rediscli-ex2","language":"sh","code":"$ redis-cli -p 12000\n    127.0.0.1:12000> get key1\n    \"123\"","section_id":"connecting-using-rediscli"},{"id":"connecting-using-_hello-world_-application-in-python-ex0","language":"sh","code":"vi redis_test.py","section_id":"connecting-using-_hello-world_-application-in-python"},{"id":"connecting-using-_hello-world_-application-in-python-ex1","language":"py","code":"import redis\n    rp1 = redis.StrictRedis(host='localhost', port=12000, db=0)\n    rp2 = redis.StrictRedis(host='localhost', port=12002, db=0)\n    print (\"set key1 123 in cluster 1\")\n    print (rp1.set('key1', '123'))\n    print (\"get key1 cluster 1\")\n    print (rp1.get('key1'))\n    print (\"get key1 from cluster 2\")\n    print (rp2.get('key1'))","section_id":"connecting-using-_hello-world_-application-in-python"},{"id":"connecting-using-_hello-world_-application-in-python-ex2","language":"sh","code":"python redis_test.py","section_id":"connecting-using-_hello-world_-application-in-python"},{"id":"connecting-using-_hello-world_-application-in-python-ex3","language":"sh","code":"set key1 123 in cluster 1\n    True\n    get key1 cluster 1\n    \"123\"\n    get key1 from cluster 2\n    \"123\"","section_id":"connecting-using-_hello-world_-application-in-python"}]}
{"schema_version":2,"id":"operate/rs/databases/active-active/create","title":"Create an Active-Active geo-replicated database","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/create/","summary":"How to create an Active-Active database and things to consider when setting it up.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"87fde4848fa96f73126ef720836bf1fc2fadeb75e34494d9dc17a28cef5a8a78","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Active-Active geo-replicated databases](https://redis.io/docs/latest/operate/rs/databases/active-active) (formerly known as CRDBs) give applications write access\nto replicas of the dataset in different geographical locations.\n\nThe participating Redis Software clusters that host the instances can be distributed in different geographic locations.\nEvery instance of an Active-Active database can receive write operations, and all operations are [synchronized](https://redis.io/docs/latest/operate/rs/databases/active-active/develop#example-of-synchronization) to all instances without conflict."},{"id":"steps-to-create-an-active-active-database","title":"Steps to create an Active-Active database","role":"content","text":"1. **Create a service account** - On each participating cluster, create a dedicated user account with the Admin role.\n1. **Confirm connectivity** - Confirm network connectivity between the participating clusters.\n1. **Create Active-Active database** - Connect to one of your clusters and create a new Active-Active database.\n1. **Add participating clusters** - Add the participating clusters to the Active-Active database with the user credentials for the service account.\n1. **Verify creation** - Log in to each of the participating clusters and verify your Active-Active database was created on them.\n1. **Confirm Active-Active database synchronization** - Test writing to one cluster and reading from a different cluster."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"- Two or more machines with the same version of Redis Software installed\n- Network connectivity and cluster FQDN name resolution between all participating clusters\n- [Network time service](https://redis.io/docs/latest/operate/rs/databases/active-active#network-time-service-ntp-or-chrony) listener (ntpd) configured and running on each node in all clusters"},{"id":"create-an-active-active-database","title":"Create an Active-Active database","role":"content","text":"1. Create service accounts on each participating cluster:\n\n    1. In a browser, open the Cluster Manager UI for the participating cluster.\n\n        The default address is: `https://<RS_address>:8443`\n\n    1. Go to the **Access Control > Users** tab:\n\n        ![Add role with name](https://redis.io/docs/latest/images/rs/access-control-user-panel.png)\n    \n    1. Click **+ Add user**.\n\n    1. Enter the username, email, and password for the user.\n    \n    1. Select the **Admin** role.\n    \n    1. Click **Save**.\n\n1. To verify network connectivity between participating clusters,\n    run the following `telnet` command from each participating cluster to all other participating clusters:\n\n    [code example]\n\n1. In a browser, open the Cluster Manager UI of the cluster where you want to create the Active-Active database.\n\n    The default address is: `https://<RS_address>:8443`\n\n1. Open the **Create database** menu with one of the following methods:\n\n    - Click the **+** button next to **Databases** in the navigation menu:\n\n        ![Create database menu has two options: Single Region and Active-Active database.](https://redis.io/docs/latest/images/rs/screenshots/databases/create-db-plus-drop-down.png)\n        \n    - Go to the **Databases** screen and select **Create database**:\n\n        ![Create database menu has two options: Single Region and Active-Active database.](https://redis.io/docs/latest/images/rs/screenshots/databases/create-db-button-drop-down.png)\n\n1. Select **Active-Active database**.\n\n1. Enter the cluster's local admin credentials, then click **Save**:\n\n    ![Enter the cluster's admin username and password.](https://redis.io/docs/latest/images/rs/screenshots/databases/active-active-databases/enter-local-admin-credentials.png)\n    \n1. Add participating clusters that will host instances of the Active-Active database:\n\n    1. In the **Participating clusters** section, go to **Other participating clusters** and click **+ Add cluster**.\n\n    1. In the **Add cluster** configuration panel, enter the new cluster's URL, port number, and the admin username and password for the new participating cluster:\n\n        ![Add cluster panel.](https://redis.io/docs/latest/images/rs/screenshots/databases/active-active-databases/create-db-add-participating-clusters.png)\n\n        > [!NOTE]\n        > If an Active-Active database [runs on flash memory](https://redis.io/docs/latest/operate/rs/databases/flash), you cannot add participating clusters that run on RAM only.\n        >\n\n    1. Click **Join cluster** to add the cluster to the list of participating clusters. \n\n1. Enter a **Database name**.\n\n1. If your cluster supports [Redis Flex or Auto Tiering](https://redis.io/docs/latest/operate/rs/databases/flash), in **Runs on** you can select **Flash** so that your database uses Flash memory. We recommend that you use AOF every 1 sec for the best performance during the initial Active-Active database sync of a new replica.\n\n1. To configure additional database settings, expand each relevant section to make changes.\n\n    See [Configuration settings](#configuration-settings) for more information about each setting.\n\n1. Click **Create**."},{"id":"configuration-settings","title":"Configuration settings","role":"configuration","text":"- **Database version** - The Redis version used by your database.\n\n- **Database name** - The database name requirements are:\n\n    - Maximum of 63 characters\n\n    - Only letters, numbers, or hyphens (-) are valid characters\n\n    - Must start and end with a letter or digit\n\n    - Case-sensitive\n\n- **Port** - You can define the port number that clients use to connect to the database. Otherwise, a port is randomly selected.\n\n    > [!NOTE]\n    > You cannot change the [port number](https://redis.io/docs/latest/operate/rs/networking/port-configurations)\n    > after the database is created.\n    >\n\n- **Memory limit** - [Database memory limits](https://redis.io/docs/latest/operate/rs/databases/memory-performance/memory-limit) include all database replicas and shards, including replica shards in database replication and database shards in database clustering.\n\n    If the total size of the database in the cluster reaches the memory limit, the data eviction policy for the database is enforced.\n\n    > [!NOTE]\n    > If you create a database with Auto Tiering enabled, you also need to set the RAM-to-Flash ratio\n    > for this database. Minimum RAM is 10%. Maximum RAM is 50%.\n    >\n\n- **Memory eviction** - The default [eviction policy](https://redis.io/docs/latest/operate/rs/databases/memory-performance/eviction-policy) for Active-Active databases is `noeviction`. Redis Software versions 6.0.20 and later support all eviction policies for Active-Active databases, unless [Redis Flex or Auto Tiering](https://redis.io/docs/latest/operate/rs/databases/flash) is enabled.\n\n- [**Capabilities**](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise) (previously **Modules**) - When you create a new in-memory database, you can enable additional capabilities in the database. You cannot enable them after database creation.\n\n    Active-Active databases created with or upgraded to Redis version 8 or later automatically enable [Redis Search](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/search-active-active) and [JSON](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json), which allows you to index, query, and perform full-text searches of nested JSON documents.\n\n    For Auto Tiering databases, you can enable capabilities that support Auto Tiering. See [Redis Software and Redis Stack feature compatibility \n](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/enterprise-capabilities) for compatibility details.\n\n    To add capabilities to the database:\n\n    1. In the **Capabilities** section, select one or more capabilities.\n    \n    1. To customize capabilities, select **Parameters** and enter the optional custom configuration.\n    \n    1. Select **Done**."},{"id":"tls","title":"TLS","role":"content","text":"If you enable TLS when you create the Active-Active database, the nodes use the TLS mode **Require TLS for CRDB communication only** to require TLS authentication and encryption for communications between participating clusters.\n        \nAfter you create the Active-Active database, you can set the TLS mode to **Require TLS for all communications** so client communication from applications are also authenticated and encryption."},{"id":"high-availability","title":"High availability","role":"content","text":"- [**Replication**](https://redis.io/docs/latest/operate/rs/databases/durability-ha/replication) - We recommend that all Active-Active database use replication for best intercluster synchronization performance.\n    \n    When replication is enabled, every Active-Active database master shard is replicated to a corresponding replica shard. The replica shards are then used to synchronize data between the instances, and the master shards are dedicated to handling client requests.\n    \n- [**Replica high availability**](https://redis.io/docs/latest/operate/rs/databases/configure/replica-ha) - We also recommend that you enable replica high availability to ensure replica shards are highly-available for this synchronization."},{"id":"clustering","title":"Clustering","role":"content","text":"- In the [**Clustering**](https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering) section, you can either:\n\n    - **Enable sharding** and select the number of shards you want to have in the database. When database clustering is enabled, databases have limitations for [multi-key operations](https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering#multikey-operations).\n    \n        You can increase the number of shards in the database at any time.\n        \n    - Clear the **Enable sharding** option to use only one shard, which allows you to use [multi-key operations](https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering#multikey-operations) without the limitations.\n\n    > [!NOTE]\n    > You cannot enable or turn off database clustering after the Active-Active database is created.\n    >\n\n- [**OSS Cluster API**](https://redis.io/docs/latest/operate/rs/databases/configure/oss-cluster-api) - The OSS Cluster API configuration allows access to multiple endpoints for increased throughput. The OSS Cluster API setting applies to all instances of the Active-Active database across participating clusters.\n\n    This configuration requires clients to connect to the primary node to retrieve the cluster topology before they can connect directly to proxies on each node.\n    \n    When you enable the OSS Cluster API, shard placement changes to _Sparse_, and the database proxy policy changes to _All primary shards_ automatically."},{"id":"durability","title":"Durability","role":"content","text":"To protect against loss of data stored in RAM, you can enable [**Persistence**](https://redis.io/docs/latest/operate/rs/databases/configure/database-persistence) to store a copy of the data on disk.\n        \nActive-Active databases support append-only file (AOF) persistence only. Snapshot persistence is not supported for Active-Active databases."},{"id":"access-control","title":"Access control","role":"content","text":"- **Unauthenticated access** - You can access the database as the default user without providing credentials.\n\n- **Password-only authentication** - When you configure a password for your database's default user, all connections to the database must authenticate with the [AUTH command](https://redis.io/docs/latest/commands/auth).\n\n    If you also configure an access control list, connections can specify other users for authentication, and requests are allowed according to the Redis ACLs specified for that user.\n\n    Creating a database without ACLs enables a *default* user with full access to the database. You can secure default user access by requiring a password.\n\n- **Access Control List** - You can specify the [user roles](https://redis.io/docs/latest/operate/rs/security/access-control/create-db-roles) that have access to the database and the [Redis ACLs](https://redis.io/docs/latest/operate/rs/security/access-control/redis-acl-overview) that apply to those connections.\n\n    You can only configure access control after the Active-Active database is created. In each participating cluster, add ACLs after database creation.\n\n    To define an access control list for a database:\n\n    1. In **Security > Access Control > Access Control List**, select **+ Add ACL**.\n\n    1. Select a [role](https://redis.io/docs/latest/operate/rs/security/access-control/create-db-roles) to grant database access.\n\n    1. Associate a [Redis ACL](https://redis.io/docs/latest/operate/rs/security/access-control/create-db-roles) with the role and database.\n\n    1. Select the check mark to add the ACL."},{"id":"causal-consistency","title":"Causal consistency","role":"content","text":"[**Causal consistency**](https://redis.io/docs/latest/operate/rs/databases/active-active/causal-consistency) in an Active-Active database guarantees that the order of operations on a specific key is maintained across all instances of an Active-Active database.\n    \nTo enable causal consistency for an existing Active-Active database, use the REST API."},{"id":"test-active-active-database-connections","title":"Test Active-Active database connections","role":"content","text":"With the Redis database created, you are ready to connect to your database. See [Connect to Active-Active databases](https://redis.io/docs/latest/operate/rs/databases/active-active/connect) for tutorials and examples of multiple connection methods."}],"examples":[{"id":"create-an-active-active-database-ex0","language":"sh","code":"telnet <target FQDN> 9443","section_id":"create-an-active-active-database"}]}
{"schema_version":2,"id":"operate/rs/databases/active-active/delete","title":"Delete Active-Active databases","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/delete/","summary":"Considerations while deleting Active-Active databases.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:24+01:00","page_type":"content","content_hash":"ca62fd3075d80e31a65c11b7e1ce96c485feed2671d0737a9785119254f42524","sections":[{"id":"content","title":"Content","role":"content","text":"When you delete an Active-Active database (formerly known as CRDB),\nall instances of the Active-Active database are deleted from all participating clusters.\n\n> [!WARNING]\n> This action is immediate, non-reversible, and has no rollback.\n\nBecause Active-Active databases are made up of instances on multiple participating clusters,\nto restore a deleted Active-Active database you must create the database again with all of its instances\nand then restore the data to the database from backup.\n\nWe recommended that you:\n\n- Back up your data and test the restore on another database before you delete an Active-Active database.\n- Consider [flushing the data](https://redis.io/docs/latest/operate/rs/databases/import-export/flush) from the database\n    so that you can keep the Active-Active database configuration and restore the data to it if necessary."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/databases/active-active/develop/app-failover-active-active","title":"Application failover with Active-Active databases","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/develop/app-failover-active-active/","summary":"How to failover your application to connect to a remote replica.","tags":["docs","operate","rs","rc"],"last_updated":"2026-09-24T09:16:24+01:00","page_type":"content","content_hash":"2610c4fa39dfcbb0ec1641ca456e3d9ae2c03315216b241c637df765b9128712","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Active-Active Redis deployments don't have a built-in failover or failback mechanism for application connections.\nAn application deployed with an Active-Active database connects to a replica of the database that is geographically nearby.\nIf that replica is not available, the application can failover to a remote replica, and failback again if necessary.\nIn this article we explain how this process works.\n\n> [!NOTE]\n> For other disaster recovery strategies including network-based, proxy-based, and client library approaches, see [Active-Active disaster recovery strategies](https://redis.io/docs/latest/operate/rs/databases/active-active/disaster-recovery).\n\nActive-Active connection failover can improve data availability, but can negatively impact data consistency.\nActive-Active replication, like Redis replication, is asynchronous.\nAn application that fails over to another replica can miss write operations.\nIf the failed replica saved the write operations in persistent storage,\nthen the write operations are processed when the failed replica recovers."},{"id":"detecting-failure","title":"Detecting Failure","role":"content","text":"Your application can detect two types of failure:\n\n1. **Local failures** - The local replica is down or otherwise unavailable\n1. **Replication failures** - The local replica is available but fails to replicate to or from remote replicas\n\nYou can also use [database availability API requests](https://redis.io/docs/latest/operate/rs/monitoring/db-availability) to determine if a database replica is available to handle read and write operations. The lag-aware database availability requests considers CRDT replication lag as a health check criterion to prevent reading stale data during failback scenarios."},{"id":"local-failures","title":"Local Failures","role":"content","text":"Local failure is detected when the application is unable to connect to the database endpoint for any reason. Reasons for a local failure can include: multiple node failures, configuration errors, connection refused, connection timed out, unexpected protocol level errors."},{"id":"replication-failures","title":"Replication Failures","role":"content","text":"Replication failures are more difficult to detect reliably without causing false positives. Replication failures can include: network split, replication configuration issues, remote replica failures.\n\nThe most reliable method for health-checking replication is by using the Redis publish/subscribe (pub/sub) mechanism.\n\n> [!NOTE]\n> Note that this document does not suggest that Redis pub/sub is reliable in the common sense. Messages can get lost in certain conditions, but that is acceptable in this case because typically the application determines that replication is down only after not being able to deliver a number of messages over a period of time.\n\nWhen you use the pub/sub data type to detect failures, the application:\n\n1. Connects to all replicas and subscribes to a dedicated channel for each replica.\n1. Connects to all replicas and periodically publishes a uniquely identifiable message.\n1. Monitors received messages and ensures that it is able to receive its own messages within a predetermined window of time.\n\nYou can also use known dataset changes to monitor the reliability of the replication stream,\nbut pub/sub is preferred method because:\n\n1. It does not involve dataset changes.\n1. It does not make any assumptions about the dataset.\n1. Pub/sub messages are delivered as replicated effects and are a more reliable indicator of a live replication link. In certain cases, dataset keys may appear to be modified even if the replication link fails. This happens because keys may receive updates through full-state replication (re-sync) or through online replication of effects."},{"id":"impact-of-sharding-on-failure-detection","title":"Impact of sharding on failure detection","role":"content","text":"If your sharding configuration is symmetric, make sure to use at least one key (PUB/SUB channels or real dataset key) per shard. Shards are replicated individually and are vulnerable to failure. Symmetric sharding configurations have the same number of shards and hash slots for all replicas.\nWe do not recommend an asymmetric sharding configuration, which requires at least one key per hash slot that intersects with a pair of shards.\n\nTo make sure that there is at least one key per shard, the application should:\n\n1. Use the Cluster API to retrieve the database sharding configuration.\n1. Compute a number of key names, such that there is one key per shard.\n1. Use those key names as channel names for the pub/sub mechanism."},{"id":"failing-over","title":"Failing over","role":"content","text":"When the application needs to failover to another replica, it should simply re-establish its connections with the endpoint on the remote replica. Because Active/Active and Redis replication are asynchronous, the remote endpoint may not have all of the locally performed and acknowledged writes.\n\nIt's best if your application doesn't read its own recent writes. Those writes can be either:\n\n1. Lost forever, if the local replica has an event such as a double failure or loss of persistent files.\n1. Temporarily unavailable, but will be available at a later time if the local replica's failure is temporary."},{"id":"failback-decision","title":"Failback decision","role":"content","text":"Your application can use the same checks described above to continue monitoring the state of the failed replica after failover.\n\nTo monitor the state of a replica during the failback process, you must make sure the replica is available, re-synced with the remote replicas, and not in stale mode. The PUB/SUB mechanism is an effective way to monitor this.\n\nDataset-based mechanisms are potentially less reliable for several reasons:\n1. In order to determine that a local replica is not stale, it is not enough to simply read keys from it. You must also attempt to write to it.\n1. As stated above, remote writes for some keys appear in the local replica before the replication link is back up and while the replica is still in stale mode.\n1. A replica that was never written to never becomes stale, so on startup it is immediately ready but serves stale data for a longer period of time."},{"id":"replica-configuration-changes","title":"Replica Configuration Changes","role":"content","text":"All failover and failback operations should be done strictly on the application side, and should not involve changes to the Active-Active configuration.\nThe only valid case for re-configuring the Active-Active deployment and removing a replica is when memory consumption becomes too high because garbage collection cannot be performed.\nOnce a replica is removed, it can only be re-joined as a new replica and it loses any writes that were not converged."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/databases/active-active/develop/data-types/hashes","title":"Hashes in an Active-Active databases","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/develop/data-types/hashes/","summary":"Information about using hashes with an Active-Active database.","tags":["docs","operate","rs","rc"],"last_updated":"2025-09-03T12:06:34-05:00","page_type":"content","content_hash":"3ccbfb437f1545ab6352d00fed8335433fa501302178cc63b26c23d37409f438","sections":[{"id":"content","title":"Content","role":"content","text":"Hashes are great for structured data that contain a map of fields and\nvalues. They are used for managing distributed user or app session\nstate, user preferences, form data and so on. Hash fields contain string\ntype and string types operate just like the standard Redis string types\nwhen it comes to CRDTs. Fields in hashes can be initialized as a string\nusing HSET or HMSET or can be used to initialize counter types that are\nnumeric integers using HINCRBY or floats using HINCRBYFLOAT.\n\nHashes in Active-Active databases behave the same and maintain additional metadata to\nachieve an \"OR-Set\" behavior to handle concurrent conflicting writes.\nWith the OR-Set behavior, writes to add new fields across multiple Active-Active database\ninstances are typically unioned except in cases of conflicts.\nConflicting instance writes can happen when an Active-Active database instance deletes a\nfield while the other adds the same field. In this case and observed\nremove rule is followed. That is, remove can only remove fields it has\nalready seen and in all other cases element add/update wins.\n\nField values behave just like CRDT strings. String values can be types\nstring, counter integer based on the command used for initialization of\nthe field value. See \"String Data Type in Active-Active databases\" and \"String Data Type\nwith Counter Value in Active-Active databases\" for more details.\n\nHere is an example of an \"add wins\" case:\n\n|  **Time** | **CRDB Instance1** | **CRDB Instance2** |\n|  ------: | :------: | :------: |\n|  t1 | HSET key1 field1 \"a\" |  |\n|  t2 |  | HSET key1 field2 \"b\" |\n|  t4 | - Sync - | - Sync - |\n|  t5 | HGETALL key1<br/>1) \"field2\"<br/>2) \"b\"<br/>3) \"field1\"<br/>4) \"a\" | HGETALL key1<br/>1) \"field2\"<br/>2) \"b\"<br/>3) \"field1\"<br/>4) \"a\" |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/databases/active-active/develop/data-types/hyperloglog","title":"HyperLogLog in Active-Active databases","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/develop/data-types/hyperloglog/","summary":"Information about using hyperloglog with an Active-Active database.","tags":["docs","operate","rs","rc"],"last_updated":"2026-09-24T09:16:24+01:00","page_type":"content","content_hash":"14c70ac1ccca2d825fa889da201502374cc526b70dec8fa1b7936263ac3d95e4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"**HyperLogLog** is an algorithm that addresses the [count-distinct problem](https://en.wikipedia.org/wiki/Count-distinct_problem).\nTo do this it approximates the numbers of items in a [set](https://en.wikipedia.org/wiki/Multiset).\nDetermining the _exact_ cardinality of a set requires memory according to the cardinality of the set.\nBecause it estimates the cardinality by probability, the HyperLogLog algorithm can run with more reasonable memory requirements."},{"id":"hyperloglog-in-redis","title":"HyperLogLog in Redis","role":"content","text":"Redis Open source implements [HyperLogLog](https://redis.io/docs/latest/develop/data-types/probabilistic/hyperloglogs) (HLL) as a native data structure.\nIt supports adding elements ([PFADD](https://redis.io/docs/latest/commands/pfadd) to an HLL, counting elements ([PFCOUNT](https://redis.io/docs/latest/commands/pfcount) of HLLs, and merging of ([PFMERGE](https://redis.io/docs/latest/commands/pfmerge) HLLs.\n\nHere is an example of a simple write case:\n\n| Time | Replica 1         | Replica 2         |\n| ---- | ----------------- | ----------------- |\n| t1   | PFADD hll x       |                   |\n| t2   | --- sync ---      |                   |\n| t3   |                   | PFADD hll y       |\n| t4   | --- sync ---      |                   |\n| t5   | PFCOUNT hll --> 2 | PFCOUNT hll --> 2 |\n\nHere is an example of a concurrent add case:\n\n| Time | Replica 1         | Replica 2         |\n| ---- | ----------------- | ----------------- |\n| t1   | PFADD hll x       | PFADD hll y       |\n| t2   | PFCOUNT hll --> 1 | PFCOUNT hll --> 1 |\n| t3   | --- sync ---      |                   |\n| t4   | PFCOUNT hll --> 2 | PFCOUNT hll --> 2 |"},{"id":"the-del-wins-approach","title":"The DEL-wins approach","role":"content","text":"Other collections in the Redis-CRDT implementation use the observed remove method to resolve conflicts.\nThe CRDT-HLL uses the DEL-wins method.\nIf a DEL request is received at the same time as any other request (ADD/MERGE/EXPIRE) on the HLL-key\nthe replicas consistently converge to delete key.\nIn the observed remove method used by other collections (sets, lists, sorted-sets and hashes),\nonly the replica that received the DEL request removes the elements, but elements added concurrently in other replicas exist in the consistently converged collection.\nWe chose to use the DEL-wins method for the CRDT-HLL to maintain the original time and space complexity of the HLL in Redis Open source.\n\nHere is an example of a DEL-wins case:\n\n| HLL  |                 |                 | \\|  | Set  |                     |                     |\n| ---- | --------------- | --------------- | --- | ---- | ------------------- | ------------------- |\n|      |                 |                 | \\|  |      |                     |                     |\n| Time | Replica 1       | Replica 2       | \\|  | Time | Replica 1           | Replica 2           |\n|      |                 |                 | \\|  |      |                     |                     |\n| t1   | PFADD h e1      |                 | \\|  | t1   | SADD s e1           |                     |\n| t2   | --- sync ---    |                 | \\|  | t2   | --- sync ---        |                     |\n| t3   | PFCOUNT h --> 1 | PFCOUNT h --> 1 | \\|  | t3   | SCARD s --> 1       | SCARD s --> 1       |\n| t4   | PFADD h e2      | Del h           | \\|  | t4   | SADD s e2           | Del S               |\n| t5   | PFCOUNT h --> 2 | PFCOUNT h --> 0 | \\|  | t5   | SCARD s --> 2       | SCARD s --> 0       |\n| t6   | --- sync ---    |                 | \\|  | t6   | --- sync ---        |                     |\n| t7   | PFCOUNT h --> 0 | PFCOUNT h --> 0 | \\|  | t7   | SCARD s --> 1       | SCARD s --> 1       |\n| t8   | Exists h --> 0  | Exists h --> 0  | \\|  | t8   | Exists s --> 1      | Exists s --> 1      |\n|      |                 |                 | \\|  | t9   | SMEMBERS s --> {e2} | SMEMBERS s --> {e2} |"},{"id":"hll-in-active-active-databases-versus-hll-in-redis-open-source","title":"HLL in Active-Active databases versus HLL in Redis Open source","role":"content","text":"In Active-Active databases, we implemented HLL within the CRDT on the basis of the Redis implementation with a few exceptions:\n\n- Redis keeps the HLL data structure as an encoded string object\n    such that you can potentially run any string request can on a key that contains an HLL. In CRDT, only get and set are supported for HLL.\n- In CRDT, if you do SET on a key that contains a value encoded as an HLL, then the value will remain an HLL. If the value is not encoded as HLL, then it will be a register."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/databases/active-active/develop/data-types","title":"Data types for Active-Active databases","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/develop/data-types/","summary":"Introduction to differences in data types between standalone and Active-Active Redis databases.","tags":["docs","operate","rs","rc"],"last_updated":"2025-06-30T08:23:21-05:00","children":[{"id":"operate/rs/databases/active-active/develop/data-types/hashes","summary":"Information about using hashes with an Active-Active database.","title":"Hashes in an Active-Active databases","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/develop/data-types/hashes/"},{"id":"operate/rs/databases/active-active/develop/data-types/hyperloglog","summary":"Information about using hyperloglog with an Active-Active database.","title":"HyperLogLog in Active-Active databases","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/develop/data-types/hyperloglog/"},{"id":"operate/rs/databases/active-active/develop/data-types/json","summary":"Information about using JSON with an Active-Active database.","title":"JSON in Active-Active databases","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/develop/data-types/json/"},{"id":"operate/rs/databases/active-active/develop/data-types/lists","summary":"Information about using list with an Active-Active database.","title":"Lists in Active-Active databases","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/develop/data-types/lists/"},{"id":"operate/rs/databases/active-active/develop/data-types/sets","summary":"Information about using sets with an Active-Active database.","title":"Sets in Active-Active databases","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/develop/data-types/sets/"},{"id":"operate/rs/databases/active-active/develop/data-types/sorted-sets","summary":"Information about using sorted sets with an Active-Active database.","title":"Sorted sets in Active-Active databases","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/develop/data-types/sorted-sets/"},{"id":"operate/rs/databases/active-active/develop/data-types/streams","summary":"Information about using streams with an Active-Active database.","title":"Streams in Active-Active databases","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/develop/data-types/streams/"},{"id":"operate/rs/databases/active-active/develop/data-types/strings","summary":"Information about using strings and bitfields with an Active-Active database.","title":"Strings and bitfields in Active-Active databases","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/develop/data-types/strings/"}],"page_type":"content","content_hash":"52929c0f556302030cd20000a7d657bc41fe355d9d8a171f96cd461ecedb2390","sections":[{"id":"content","title":"Content","role":"content","text":"Active-Active databases use conflict-free replicated data types (CRDTs). From a developer perspective, most supported data types work the same for Active-Active and standard Redis databases. However, a few methods also come with specific requirements in Active-Active databases.\n\nEven though they look identical to standard Redis data types, there are specific rules that govern the handling of\nconflicting concurrent writes for each data type.\n\nAs conflict handling rules differ between data types, some commands have slightly different requirements in Active-Active databases versus standard Redis databases.\n\nSee the following articles for more information."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/databases/active-active/develop/data-types/json","title":"JSON in Active-Active databases","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/develop/data-types/json/","summary":"Information about using JSON with an Active-Active database.","tags":["docs","operate","rs","rc"],"last_updated":"2026-09-24T09:16:24+01:00","page_type":"content","content_hash":"38a340da935a84abf52721470f99d2aba5338b3f1c2ba5bccb53c64503da0145","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Active-Active databases support JSON data structures.\n\nThe design is based on [A Conflict-Free Replicated JSON Datatype](https://arxiv.org/abs/1608.03960) by Kleppmann and Beresford, but the implementation includes some changes. Several [conflict resolution rule](#conflict-resolution-rules) examples were adapted from this paper as well."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"To use JSON in an Active-Active database, you must enable JSON during database creation.\n\nActive-Active Redis Cloud databases add JSON by default. See [Create an Active-Active subscription](https://redis.io/docs/latest/operate/rc/databases/active-active/create-active-active-database#select-capabilities) in the Redis Cloud documentation for details.\n\nIn Redis Software, Active-Active databases created with or upgraded to Redis version 8 or later automatically enable JSON. For earlier Redis versions, see [Create an Active-Active JSON database](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json/active-active#create-an-active-active-json-database) in the Redis Stack and Redis Software documentation for instructions."},{"id":"command-differences","title":"Command differences","role":"syntax","text":"Some JSON commands work differently for Active-Active databases."},{"id":"jsonclear","title":"`JSON.CLEAR`","role":"content","text":"[`JSON.CLEAR`](https://redis.io/docs/latest/commands/json.clear) resets JSON arrays and objects. It supports concurrent updates to JSON documents from different instances in an Active-Active database and allows the results to be merged."},{"id":"conflict-resolution-rules","title":"Conflict resolution rules","role":"content","text":"With Active-Active databases, it's possible for two different instances to try to run write operations on the same data at the same time. If this happens, conflicts can arise when the replicas attempt to sync these changes with each other. Conflict resolution rules determine how the database handles conflicting operations.\n\nThere are two types of conflict resolution:\n\n1. Merge:\n\n    - The operations are associative.\n\n    - Merges the results of both operations.\n\n1. Win over:\n\n    - The operations are not associative.\n\n    - One operation wins the conflict and sets the value.\n\n    - Ignores the losing operation.\n\nThe following conflict resolution rules show how Active-Active databases resolve conflicts for various JSON commands."},{"id":"assign-different-types-to-a-key","title":"Assign different types to a key","role":"content","text":"**Conflict**\n\nTwo instances concurrently assign values of different types to the same key within a JSON document.\n\nFor example:\n\nInstance 1 assigns an object to a key within a JSON document.\n\nInstance 2 assigns an array to the same key.\n\n**Resolution type**\n\nWin over\n\n**Resolution rule**\n\nThe instance with the smaller ID wins, so the key becomes an object in the given example.\n\n**Example**\n\n| Time  | Description | Instance 1 | Instance 2 |\n| :---: | :--- | :--- | :--- |\n| t1 | Set the same key to an object or an array | JSON.SET doc $.a '{}' | JSON.SET doc $.a '[]' |\n| t2 | Add data to the object and array | <nobr>JSON.SET doc  $.a.x '\"y\"'</nobr> <br /><br /> Result: <br /> {\"a\": {\"x\": \"y\"}} | <nobr>JSON.SET doc $.a '[\"z\"]'</nobr> <br /><br /> Result: <br /> {\"a\": [\"z\"]} |\n| t3 | Active-Active synchronization | – Sync – | – Sync – |\n| t4 | Instance 1 wins | JSON.GET doc $ <br /><br /> Result: <br /> {\"a\": {\"x\": \"y\"}} | JSON.GET doc $ <br /><br /> Result: <br /> {\"a\": {\"x\": \"y\"}} |"},{"id":"create-versus-create","title":"Create versus create","role":"content","text":"**Conflict**\n\nTwo instances concurrently use `JSON.SET` to assign a new JSON document to the same key.\n\n**Resolution type**\n\nWin over\n\n**Resolution rule**\n\nThe instance with the smaller ID wins.\n\n**Example**\n\n| Time  | Description | Instance 1 | Instance 2 |\n| :---: | :--- | :--- | :--- |\n| t1 | Create a new JSON document | <nobr>JSON.SET doc $ '{\"field\": \"a\"}'</nobr> | <nobr>JSON.SET doc $ '{\"field\": \"b\"}'</nobr> |\n| t2 | Active-Active synchronization | – Sync – | – Sync – |\n| t3 | Instance 1 wins | JSON.GET doc $ <br /><br /> Result: <br /> {\"field\": \"a\"} | JSON.GET doc $ <br /><br /> Result: <br /> {\"field\": \"a\"} |"},{"id":"create-versus-update","title":"Create versus update","role":"content","text":"**Conflict**\n\nInstance 1 creates a new document and assigns it to an existing key with `JSON.SET`.\n\nInstance 2 updates the existing content of the same key with `JSON.SET`.\n\n**Resolution type**\n\nWin over\n\n**Resolution rule**\n\nThe operation that creates a new document wins.\n\n**Example**\n\n| Time  | Description | Instance 1 | Instance 2 |\n| :---: | :--- | :--- | :--- |\n| t1 | The document exists on both instances | JSON.GET doc $ <br /><br /> Result: <br />  {\"field1\": \"value1\"} | JSON.GET doc $ <br /><br /> Result: <br />  {\"field1\": \"value1\"} |\n| t2 | Instance 1 creates a new document; instance 2 updates the existing document | <nobr>JSON.SET doc $ '{\"field2\": \"value2\"}'</nobr> | <nobr>JSON.SET doc $.field1 '[1, 2, 3]'</nobr> |\n| t3 | Active-Active synchronization | – Sync – | – Sync – |\n| t4 | Instance 1 wins | JSON.GET doc . <br /><br /> Result: <br /> {\"field2\": \"value2\"} | JSON.GET doc . <br /><br /> Result: <br /> {\"field2\": \"value2\"} |"},{"id":"delete-versus-create","title":"Delete versus create","role":"content","text":"**Conflict**\n\nInstance 1 deletes a JSON document with `JSON.DEL`.\n\nInstance 2 uses `JSON.SET` to create a new JSON document and assign it to the key deleted by instance 1.\n\n**Resolution type**\n\nWin over\n\n**Resolution rule**\n\nDocument creation wins over deletion.\n\n**Example**\n\n| Time  | Description | Instance 1 | Instance 2 |\n| :---: | :--- | :--- | :--- |\n| t1 | The document exists on both instances | JSON.GET doc $ <br /><br /> Result: <br /> {\"field1\": \"value1\"} | JSON.GET doc $ <br /><br /> Result: <br /> {\"field1\": \"value1\"} |\n| t2 | Instance 1 deletes the document; instance 2 creates a new document | JSON.DEL doc | <nobr>JSON.SET doc $ '{\"field1\": \"value2\"}'</nobr> |\n| t3 | Active-Active synchronization | – Sync – | – Sync – |\n| t4 | Instance 2 wins | JSON.GET doc $ <br /><br /> Result: <br /> <nobr>{\"field1\": \"value2\"}</nobr> | JSON.GET doc $ <br /><br /> Result: <br /> {\"field1\": \"value2\"} |"},{"id":"delete-versus-update","title":"Delete versus update","role":"content","text":"**Conflict**\n\nInstance 1 deletes a JSON document with `JSON.DEL`.\n\nInstance 2 updates the content of the same document with `JSON.SET`.\n\n**Resolution type**\n\nWin over\n\n**Resolution rule**\n\nDocument deletion wins over updates.\n\n**Example**\n\n| Time  | Description | Instance 1 | Instance 2 |\n| :---: | :--- | :--- | :--- |\n| t1 | The document exists on both instances | JSON.GET doc $ <br /><br /> Result: <br /> <nobr>{\"field1\": \"value1\"}</nobr> | JSON.GET doc $ <br /><br /> Result: <br /> {\"field1\": \"value1\"} |\n| t2 | Instance 1 deletes the document; instance 2 updates it | JSON.DEL doc | <nobr>JSON.SET doc $.field1 '[1, 2, 3]'</nobr> |\n| t3 | Active-Active synchronization | – Sync – | – Sync – |\n| t4 | Instance 1 wins | JSON.GET doc $ <br /><br /> Result: <br /> (nil) | JSON.GET doc $ <br /><br /> Result: <br /> (nil) |"},{"id":"update-versus-update","title":"Update versus update","role":"content","text":"**Conflict**\n\nInstance 1 updates a field inside a JSON document with `JSON.SET`.\n\nInstance 2 updates the same field with a different value.\n\n**Resolution type**\n\nWin over\n\n**Resolution rule**\n\nThe instance with the smallest ID wins.\n\n**Example**\n\n| Time  | Description | Instance 1 | Instance 2 |\n| :---: | :--- | :--- | :--- |\n| t1 | The document exists on both instances | JSON.GET doc $ <br /><br /> Result: <br /> {\"field\": \"a\"} | JSON.GET doc $ <br /><br /> Result: <br /> {\"field\": \"a\"} |\n| t2 | Update the same field with different data | <nobr>JSON.SET doc $.field \"b\"</nobr> | <nobr>JSON.SET doc $.field \"c\"</nobr> |\n| t3 | Active-Active synchronization | – Sync – | – Sync – |\n| t4 | Instance 1 wins | JSON.GET doc $ <br /><br /> Result: <br /> {\"field\": \"b\"} | JSON.GET doc $ <br /><br /> Result: <br /> {\"field\": \"b\"} |"},{"id":"update-versus-clear","title":"Update versus clear","role":"content","text":"The version of RedisJSON prior to v2.2 has two different ways to reset the content of a JSON object:\n\n- Assign a new empty JSON object:\n    \n    [code example]\n\n    If you use this method, it cannot be merged with a concurrent update.\n\n- For each key, remove it with `JSON.DEL`:\n\n    [code example]\n\n    With this method, it can merge the reset with concurrent updates.\n\nAs of RedisJSON v2.2, you can use the `JSON.CLEAR` command to reset the JSON document without removing each key manually. This method also lets concurrent updates be merged.\n\n#### Assign an empty object\n\n**Conflict**\n\nInstance 1 adds \"red\" to the existing \"colors\" object with `JSON.SET`.\n\nInstance 2 assigns a new empty object for \"colors\".\n\n**Resolution type**\n\nWin over\n\n**Resolution rule**\n\nDocument creation wins over the update, so the result will be an empty object.\n\n**Example**\n\n| Time  | Description | Instance 1 | Instance 2 |\n| :---: | :--- | :--- | :--- |\n| t1 | The document exists on both instances | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {\"blue\": \"#0000ff\"}} | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {\"blue\": \"#0000ff\"}} |\n| t2 | Instance 1 adds a new color; instance 2 resets colors to an empty object | <nobr>JSON.SET doc $.colors.red ‘#ff0000’</nobr> | JSON.SET doc $.colors ‘{}’ |\n| t3 | Instance 2 adds a new color |  | <nobr>JSON.SET doc $.colors.green ‘#00ff00’</nobr> |\n| t4 |  | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {\"blue\": \"#0000ff\", \"red\": \"#ff0000\"}} | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {\"green\": \"#00ff00\"}} |\n| t5 | Active-Active synchronization | – Sync – | – Sync – |\n| t6 | Instance 2 wins | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {\"green\": \"#00ff00\"}} | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {\"green\": \"#00ff00\"}} |\n\n#### Use `JSON.CLEAR`\n\n**Conflict**\n\nInstance 1 adds \"red\" to the existing \"colors\" object with `JSON.SET`.\n\nInstance 2 clears the \"colors\" object with `JSON.CLEAR` and adds \"green\" to \"colors\".\n\n**Resolution type**\n\nMerge\n\n**Resolution rule**\n\nMerges the results of all operations.\n\n**Example**\n\n| Time  | Description | Instance 1 | Instance 2 |\n| :---: | :--- | :--- | :--- |\n| t1 | The document exists on both instances | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {\"blue\": \"#0000ff\"}} | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {\"blue\": \"#0000ff\"}} |\n| t2 | Instance 1 adds a new color; instance 2 resets the colors | <nobr>JSON.SET doc $.colors.red ‘#ff0000’</nobr> | JSON.CLEAR doc $.colors |\n| t3 |  | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {\"blue\": \"#0000ff\", \"red\": \"#ff0000\"}} | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {}} |\n| t4 | Instance 2 adds a new color |  | <nobr>JSON.SET doc $.colors.green ‘#00ff00’</nobr> |\n| t5 |  |  | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {\"green\": \"#00ff00\"}} |\n| t6 | Active-Active synchronization | – Sync – | – Sync – |\n| t7 | Merges the results of both instances | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {\"red\": \"#ff0000\", \"green\": \"#00ff00\"}} | JSON.GET doc $ <br /><br /> Result: <br /> {\"colors\": {\"red\": \"#ff0000\", \"green\": \"#00ff00\"}} |"},{"id":"update-versus-update-array","title":"Update versus update array","role":"content","text":"**Conflict**\n\nTwo instances update the same existing array with different content.\n\n**Resolution type**\n\nMerge\n\n**Resolution rule**\n\nMerges the results of all operations on the array. Preserves the original element order from each instance.\n\n**Example**\n\n| Time  | Description | Instance 1 | Instance 2 |\n| :---: | :--- | :--- | :--- |\n| t1 | The document exists on both instances | JSON.GET doc $ <br /><br /> Result: <br /> '[\"a\", \"b\", \"c\"]' | JSON.GET doc $ <br /><br /> Result: <br /> '[\"a\", \"b\", \"c\"]' |\n| t2 | Instance 1 removes an array element; instance 2 adds one | JSON.ARRPOP doc $ 1 <br /><br /> Result: <br /> [\"a\", \"c\"] | <nobr>JSON.ARRINSERT doc $ 0 ‘\"y\"’</nobr> <br /><br /> Result: <br /> [\"y\", \"a\", \"b\", \"c\"] |\n| t3 | Both instances add another element to the array | <nobr>JSON.ARRINSERT doc $ 1 ‘\"x\"’</nobr> <br /><br /> Result: <br /> [\"a\", \"x\", \"c\"] | <nobr>JSON.ARRINSERT doc $ 2 ‘\"z\"’</nobr> <br /><br /> Result: <br /> [\"y\", \"a\", \"z\", \"b\", \"c\"] |\n| t4 | Active-Active synchronization | – Sync – | – Sync – |\n| t5 | Merge results from both instances | JSON.GET doc $ <br /><br /> Result: <br /> [\"y\", \"a\", \"x\", \"z\", \"c\"] | JSON.GET doc $ <br /><br /> Result: <br /> [\"y\", \"a\", \"x\", \"z\", \"c\"] |"},{"id":"update-versus-delete-array-element","title":"Update versus delete array element","role":"content","text":"**Conflict**\n\nInstance 1 removes an element from a JSON array with `JSON.ARRPOP`.\n\nInstance 2 updates the same element that instance 1 removes.\n\n**Resolution type**\n\nWin over\n\n**Resolution rule**\n\nDeletion wins over updates.\n\n**Example**\n\n| Time  | Description | Instance 1 | Instance 2 |\n| :---: | :--- | :--- | :--- |\n| t1 | The document exists on both instances | JSON.GET doc $ <br /><br /> Result: <br /> {\"todo\": [{\"title\": \"buy milk\", \"done\": false}]} | JSON.GET doc $ <br /><br /> Result: <br /> {\"todo\": [{\"title\": \"buy milk\", \"done\": false}]} |\n| t2 | Instance 1 removes an array element; instance 2 updates the same element | <nobr>JSON.ARRPOP doc $.todo 0</nobr> | <nobr>JSON.SET doc '$.todo[0][\"done\"]' 'true'’</nobr> |\n| t3 |  | JSON.GET doc $ <br /><br /> Result: <br /> {\"todo\": []} | JSON.GET doc $ <br /><br /> Result: <br /> [{\"title\": \"buy milk\", \"done\": true}]} |\n| t4 | Active-Active synchronization | – Sync – | – Sync – |\n| t5 | Instance 1 wins | JSON.GET doc $ <br /><br /> Result: <br /> doc = {\"todo\": []} | JSON.GET doc $ <br /><br /> Result: <br /> doc = {\"todo\": []} |"},{"id":"update-versus-update-object","title":"Update versus update object","role":"content","text":"**Conflict**\n\nBoth instances update the same existing object with different content.\n\n**Resolution type**\n\nMerge\n\n**Resolution rule**\n\nMerges the results of all operations on the object.\n\n**Example**\n\n| Time  | Description | Instance 1 | Instance 2 |\n| :---: | :--- | :--- | :--- |\n| t1 | The document exists on both instances | JSON.GET doc $ <br /><br /> Result: <br /> '{\"grocery\": []}' | JSON.GET doc $ <br /><br /> Result: <br /> '{\"grocery\": []}' |\n| t2 | Add new elements to the array | <nobr>JSON.ARRAPPEND doc $.grocery ‘\"eggs\"’</nobr> | JSON.ARRAPPEND doc $.grocery ‘\"milk\"’ |\n| t3 | Add new elements to the array | JSON.ARRAPPEND doc $.grocery ‘\"ham\"’ | <nobr>JSON.ARRAPPEND doc $.grocery ‘\"flour\"’</nobr> |\n| t4 |  | JSON.GET doc $ <br /><br /> Result: <br /> {\"grocery\":[\"eggs\", \"ham\"]} | JSON.GET doc $ <br /><br /> Result: <br /> {\"grocery\":[\"milk\", \"flour\"]} |\n| t5 | Active-Active synchronization | – Sync – | – Sync – |\n| t6 | Merges the results from both instances | JSON.GET doc . <br /><br /> Result: <br /> {\"grocery\":[\"eggs\",\"ham\",\"milk\", \"flour\"]} | JSON.GET doc . <br /><br /> Result: <br /> {\"grocery\":[\"eggs\",\"ham\",\"milk\", \"flour\" ]} |"}],"examples":[{"id":"update-versus-clear-ex0","language":"sh","code":"JSON.SET doc $.colors '{}'","section_id":"update-versus-clear"},{"id":"update-versus-clear-ex1","language":"sh","code":"JSON.DEL doc $.colors.blue","section_id":"update-versus-clear"}]}
{"schema_version":2,"id":"operate/rs/databases/active-active/develop/data-types/lists","title":"Lists in Active-Active databases","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/develop/data-types/lists/","summary":"Information about using list with an Active-Active database.","tags":["docs","operate","rs","rc"],"last_updated":"2025-09-03T12:06:34-05:00","page_type":"content","content_hash":"a5bc3695c5bd200e6e9bf1efa8896a4ce97de5f953fcc189fb46bd859df52bc7","sections":[{"id":"content","title":"Content","role":"content","text":"Redis lists are simply lists of strings, sorted by insertion order. It\nis possible to add elements to a Redis List that push new elements to\nthe head (on the left) or to the tail (on the right) of the list. Redis\nlists can be used to easily implement queues (using LPUSH and RPOP, for\nexample) and stacks (using LPUSH and LPOP, for\nexample).\n\nLists in Active-Active databases are just the same as regular Redis Lists. See the\nfollowing examples to get familiar with Lists' behavior in an\nActive-Active database.\n\nSimple Lists\nexample:\n\n|  **Time** | **CRDB Instance 1** | **CRDB Instance 2** |\n|  ------: | :------: | :------: |\n|  t1 | LPUSH mylist \"hello\" |  |\n|  t2 | — Sync — | — Sync — |\n|  t3 |  | LPUSH mylist \"world\" |\n|  t4 | — Sync — | — Sync — |\n|  t5 | LRANGE mylist 0 -1 =>\"world\" \"hello\" | LRANGE mylist 0 -1 => \"world\" \"hello\" |\n\n**Explanation**:\nThe final list contains both the \"world\" and \"hello\" elements, in that\norder (Instance 2 observed \"hello\" when it added\n\"world\").\n\nExample of Lists with Concurrent\nInsertions:\n\n|  **Time** | **CRDB Instance 1** | **CRDB Instance 2** |\n|  ------: | :------: | :------: |\n|  t1 | LPUSH L x |  |\n|  t2 | — Sync — | — Sync — |\n|  t3 | LINSERT L AFTER x y1 |  |\n|  t4 |  | LINSERT L AFTER x y2 |\n|  t5 | LRANGE L 0 -1 => x y1 | LRANGE L 0 -1 => x y2 |\n|  t6 | — Sync — | — Sync — |\n|  t7 | LRANGE L 0 -1 => x y1 y2 | LRANGE L 0 -1 => x y1 y2 |\n\n**Explanation**:\nInstance 1 added an element y1 after x, and then Instance 2 added element y2 after x.\nThe final List contains all three elements: x is the first element, after it y1 and then y2.\nThe Active-Active database resolves the conflict arbitrarily but applies the resolution consistently across all Active-Active database instances.\n\nExample of Deleting a List while Pushing a New\nElement:\n\n|  **Time** | **CRDB Instance 1** | **CRDB Instance 2** |\n|  ------: | :------: | :------: |\n|  t1 | LPUSH L x |  |\n|  t2 | — Sync — | — Sync — |\n|  t3 | LRANGE L 0 -1 => x | LRANGE L 0 -1 => x |\n|  t4 | LPUSH L y | DEL L |\n|  t5 | — Sync — | — Sync — |\n|  t6 | LRANGE L 0 -1 => y | LRANGE L 0 -1 => y |\n\n**Explanation**\nAt t4 - t6, DEL deletes only observed elements. This is why L still\ncontains y.\n\nExample of Popping Elements from a\nList:\n\n|  **Time** | **CRDB Instance 1** | **CRDB Instance 2** |\n|  ------: | :------: | :------: |\n|  t1 | LPUSH L x y z |  |\n|  t2 | — Sync — | — Sync — |\n|  t3 |  | RPOP L => x |\n|  t4 | — Sync — | — Sync — |\n|  t5 | RPOP L => y |  |\n|  t6 | — Sync — | — Sync — |\n|  t7 | RPOP L => z | RPOP L => z |\n\n**Explanation**:\nAt t1, the operation pushes elements x, y, z to List L. At t3, the\nsequential pops behave as expected from a queue. At t7, the concurrent\npop in both instances might show the same result. The instance was not\nable to sync regarding the z removal so, from the point of view of each\ninstance, z is located in the List and can be popped. After syncing,\nboth lists are empty.\n\nBe aware of the behavior of Lists in Active-Active databases when using List as a stack\nor queue. As seen in the above example, two parallel RPOP operations\nperformed by two different Active-Active database instances can get the same element in\nthe case of a concurrent operation. Lists in Active-Active databases guarantee that each\nelement is POP-ed at least once, but cannot guarantee that each\nelement is POP-ed only once. Such behavior should be taken into\naccount when, for example, using Lists in Active-Active databases as building blocks for\ninter-process communication systems.\n\nIn that case, if the same element cannot be handled twice by the\napplications, it's recommended that the POP operations be performed by\none Active-Active database instance, whereas the PUSH operations can be performed by\nmultiple instances."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/databases/active-active/develop/data-types/sets","title":"Sets in Active-Active databases","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/develop/data-types/sets/","summary":"Information about using sets with an Active-Active database.","tags":["docs","operate","rs","rc"],"last_updated":"2025-09-03T12:06:34-05:00","page_type":"content","content_hash":"ed52a8e6dd4890a60934975d9a50904b42be46367af3f1f960383a55f92b08ce","sections":[{"id":"content","title":"Content","role":"content","text":"A Redis set is an unordered collection of strings. It is possible to\nadd, remove, and test for the existence of members with Redis commands.\nA Redis set maintains a unique collection of elements. Sets can be great\nfor maintaining a list of events (click streams), users (in a group\nconversation), products (in recommendation lists), engagements (likes,\nshares) and so on.\n\nSets in Active-Active databases behave the same and maintain additional metadata to\nachieve an \"OR-Set\" behavior to handle concurrent conflicting\nwrites. With the OR-Set behavior, writes across multiple Active-Active database instances\nare typically unioned except in cases of conflicts. Conflicting instance\nwrites can happen when a Active-Active database instance deletes an element while the\nother adds the same element. In this case and observed remove rule is\nfollowed. That is, remove can only remove instances it has already seen\nand in all other cases element add wins.\n\nHere is an example of an \"add wins\" case:\n\n|  **Time** | **CRDB Instance1** | **CRDB Instance2** |\n|  ------: | :------: | :------: |\n|  t1 | SADD key1 \"a\" |  |\n|  t2 |  | SADD key1 \"b\" |\n|  t3 | SMEMBERS key1 \"a\" | SMEMBERS key1 \"b\" |\n|  t4 | — Sync — | — Sync — |\n|  t3 | SMEMBERS key1 \"a\" \"b\" | SMEMBERS key1 \"a\" \"b\" |\n\nHere is an example of an \"observed remove\" case.\n\n|  **Time** | **CRDB Instance1** | **CRDB Instance2** |\n|  ------: | :------: | :------: |\n|  t1 | SMEMBERS key1 \"a\" \"b\" | SMEMBERS key1 \"a\" \"b\" |\n|  t2 | SREM key1 \"a\" | SADD key1 \"c\" |\n|  t3 | SREM key1 \"c\" |  |\n|  t4 | — Sync — | — Sync — |\n|  t3 | SMEMBERS key1 \"c\" \"b\" | SMEMBERS key1 \"c\" \"b\" |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/databases/active-active/develop/data-types/sorted-sets","title":"Sorted sets in Active-Active databases","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/develop/data-types/sorted-sets/","summary":"Information about using sorted sets with an Active-Active database.","tags":["docs","operate","rs","rc"],"last_updated":"2026-09-24T09:16:24+01:00","page_type":"content","content_hash":"e2fb1c479e7984eb62a959b36948c1b819ac43b006eac92932aeaedcfd546940","sections":[{"id":"content","title":"Content","role":"content","text":"> [!NOTE]\n> [Redis Geospatial (Geo)](https://redis.io/docs/latest/commands/geoadd) is based on Sorted Sets, so the same Active-Active database development instructions apply to Geo.\n\nSimilar to Redis Sets, Redis Sorted Sets are non-repeating collections\nof Strings. The difference between the two is that every member of a\nSorted Set is associated with a score used to order the Sorted Set from\nlowest to highest. While members are unique, they may have the same\nscore.\n\nWith Sorted Sets, you can quickly add, remove or update elements as\nwell as get ranges by score or by rank (position). Sorted Sets in Active-Active databases\nbehave the same and maintain additional metadata to handle concurrent\nconflicting writes. Conflict resolution is done in two\nphases:\n\n1. First, the database resolves conflict at the set level using \"OR\n    Set\" (Observed-Remove Set). With OR-Set behavior, writes across\n    multiple Active-Active database instances are typically unioned except in cases of\n    conflicts. Conflicting writes can happen when an Active-Active database instance\n    deletes an element while the other adds or updates the same element.\n    In this case, an observed Remove rule is followed, and only\n    instances it has already seen are removed. In all other cases, the\n    Add / Update element wins.\n1. Second, the database resolves conflict at the score level. In this\n    case, the score is treated as a counter and applies the same\n    conflict resolution as regular counters.\n\nSee the following examples to get familiar with Sorted Sets'\nbehavior in Active-Active database:\n\nExample of Simple Sorted Set with No\nConflict:\n\n|  **Time** | **CRDB Instance 1** | **CRDB Instance 2** |\n|  ------: | :------: | :------: |\n|  t1 | ZADD Z 1.1 x |  |\n|  t2 | — Sync — | — Sync — |\n|  t3 |  | ZADD Z 1.2 y |\n|  t4 | — Sync — | — Sync — |\n|  t5 | ZRANGE Z 0 -1 => x y | ZRANGE Z 0 -1 => x y |\n\n**Explanation**:\nWhen adding two different elements to a Sorted Set from different\nreplicas (in this example, x with score 1.1 was added by Instance 1 to\nSorted Set Z, and y with score 1.2 was added by Instance 2 to Sorted Set\nZ) in a non-concurrent manner (i.e. each operation happened separately\nand after both instances were in sync), the end result is a Sorted\nSet including both elements in each Active-Active database instance.\nExample of Sorted Set and Concurrent\nAdd:\n\n|  **Time** | **CRDB Instance 1** | **CRDB Instance 2** |\n|  ------: | :------: | :------: |\n|  t1 | ZADD Z 1.1 x |  |\n|  t2 |  | ZADD Z 2.1 x |\n|  t3 | ZSCORE Z x => 1.1 | ZSCORE Z x => 2.1 |\n|  t4 | — Sync — | — Sync — |\n|  t5 | ZSCORE Z x => 2.1 | ZSCORE Z x => 2.1 |\n\n**Explanation**:\nWhen concurrently adding an element x to a Sorted Set Z by two different\nActive-Active database instances (Instance 1 added score 1.1 and Instance 2 added score\n2.1), the Active-Active database implements Last Write Win (LWW) to determine the score of\nx. In this scenario, Instance 2 performed the ZADD operation at time\nt2\\>t1 and therefore the Active-Active database sets the score 2.1 to\nx.\n\nExample of Sorted Set with Concurrent Add Happening at the Exact Same\nTime:\n\n|  **Time** | **CRDB Instance 1** | **CRDB Instance 2** |\n|  ------: | :------: | :------: |\n|  t1 | ZADD Z 1.1 x | ZADD Z 2.1 x |\n|  t2 | ZSCORE Z x => 1.1 | ZSCORE Z x => 2.1 |\n|  t3 | — Sync — | — Sync — |\n|  t4 | ZSCORE Z x => 1.1 | ZSCORE Z x => 1.1 |\n\n**Explanation**:\nThe example above shows a relatively rare situation, in which two Active-Active database\ninstances concurrently added the same element x to a Sorted Set at the\nsame exact time but with a different score, i.e. Instance 1 added x with\na 1.1 score and Instance 2 added x with a 2.1 score. After syncing, the\nActive-Active database realized that both operations happened at the same time and\nresolved the conflict by arbitrarily (but consistently across all Active-Active database\ninstances) giving precedence to Instance 1.\nExample of Sorted Set with Concurrent Counter\nIncrement:\n\n|  **Time** | **CRDB Instance 1** | **CRDB Instance 2** |\n|  ------: | :------: | :------: |\n|  t1 | ZADD Z 1.1 x |  |\n|  t2 | — Sync — | — Sync — |\n|  t3 | ZINCRBY Z 1.0 x | ZINCRBY Z 1.0 x |\n|  t4 | — Sync — | — Sync — |\n|  t5 | ZSCORE Z x => 3.1 | ZSCORE Z x => 3.1 |\n\n**Explanation**:\nThe result is the sum of all\nZINCRBY\noperations performed by all Active-Active database instances.\n\nExample of Removing an Element from a Sorted\nSet:\n\n|  **Time** | **CRDB Instance 1** | **CRDB Instance 2** |\n|  ------: | :------: | :------: |\n|  t1 | ZADD Z 4.1 x |  |\n|  t2 | — Sync — | — Sync — |\n|  t3 | ZSCORE Z x => 4.1 | ZSCORE Z x => 4.1 |\n|  t4 | ZREM Z x | ZINCRBY Z 2.0 x |\n|  t5 | ZSCORE Z x => nill | ZSCORE Z x => 6.1 |\n|  t6 | — Sync — | — Sync — |\n|  t7 | ZSCORE Z x => 2.0 | ZSCORE Z x => 2.0 |\n\n**Explanation**:\nAt t4 - t5, concurrent ZREM and ZINCRBY operations ran on Instance 1\nand Instance 2 respectively. Before the instances were in sync, the ZREM\noperation could only delete what had been seen by Instance 1, so\nInstance 2 was not affected. Therefore, the ZSCORE operation shows the\nlocal effect on x. At t7, after both instances were in-sync, the Active-Active database\nresolved the conflict by subtracting 4.1 (the value of element x in\nInstance 1) from 6.1 (the value of element x in Instance 2)."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/databases/active-active/develop/data-types/streams","title":"Streams in Active-Active databases","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/develop/data-types/streams/","summary":"Information about using streams with an Active-Active database.","tags":["docs","operate","rs","rc"],"last_updated":"2026-09-24T09:16:24+01:00","page_type":"content","content_hash":"a359e3e6e9266d2372251e4cc8c21fbf11d17ba205ee153a79e56c9392864855","sections":[{"id":"overview","title":"Overview","role":"overview","text":"A [Redis Stream](https://redis.io/docs/latest/develop/data-types/streams) is a data structure that acts like an append-only log.\nEach stream entry consists of:\n\n- A unique, monotonically increasing ID\n- A payload consisting of a series key-value pairs\n\nYou add entries to a stream with the XADD command. You access stream entries using the XRANGE, XREADGROUP, and XREAD commands (however, see the caveat about XREAD below)."},{"id":"streams-and-active-active","title":"Streams and Active-Active","role":"content","text":"Active-Active databases allow you to write to the same logical stream from more than one region.\nStreams are synchronized across the regions of an Active-Active database.\n\nIn the example below, we write to a stream concurrently from two regions. Notice that after syncing, both regions have identical streams:\n\n<table style='width: auto;'>\n<thead>\n<tr>\n<th>Time</th>\n<th>Region 1</th>\n<th>Region 2</th>\n</tr>\n</thead>\n\n<tbody>\n<tr>\n<td><em>t1</em></td>\n<td><code>XADD messages * text hello</code></td>\n<td><code>XADD messages * text goodbye</code></td>\n</tr>\n\n<tr>\n<td><em>t2</em></td>\n<td><code>XRANGE messages - +</code> <br/><strong>→ [1589929244828-1]</strong></td>\n<td><code>XRANGE messages - +</code> <br/><strong>→ [1589929246795-2]</strong></td>\n</tr>\n\n<tr>\n<td><em>t3</em></td>\n<td><em>— Sync —</em></td>\n<td><em>— Sync —</em></td>\n</tr>\n\n<tr>\n<td><em>t4</em></td>\n<td><code>XRANGE messages - +</code> <br/><strong>→ [1589929244828-1, 1589929246795-2]</strong></td>\n<td><code>XRANGE messages - +</code> <br/><strong>→ [1589929244828-1, 1589929246795-2]</strong></td>\n</tr>\n</tbody>\n</table>\n\nNotice also that the synchronized streams contain no duplicate IDs. As long as you allow the database to generate your stream IDs, you'll never have more than one stream entry with the same ID.\n\n> [!NOTE]\n> Redis Open Source uses one radix tree (referred to as `rax` in the code base) to implement each stream. However, Active-Active databases implement a single logical stream using one `rax` per region.\n> Each region adds entries only to its associated `rax` (but can remove entries from all `rax` trees).\n> This means that XREAD and XREADGROUP iterate simultaneously over all `rax` trees and return the appropriate entry by comparing the entry IDs from each `rax`."},{"id":"conflict-resolution","title":"Conflict resolution","role":"content","text":"Active-Active databases use an \"observed-remove\" approach to automatically resolve potential conflicts.\n\nWith this approach, a delete only affects the locally observable data.\n\nIn the example below, a stream, `x`, is created at _t1_. At _t3_, the stream exists in two regions.\n\n<table style='width: 100%;'>\n<thead>\n<tr>\n<th>Time</th>\n<th>Region 1</th>\n<th>Region 2</th>\n</tr>\n</thead>\n\n<tbody>\n<tr>\n<td><em>t1</em></td>\n<td><code>XADD messages * text hello</code></td>\n<td></td>\n</tr>\n\n<tr>\n<td><em>t2</em></td>\n<td><em>— Sync —</em></td>\n<td><em>— Sync —</em></td>\n</tr>\n\n<tr>\n<td><em>t3</em></td>\n<td><code>XRANGE messages - +</code> <br/><strong>→ [1589929244828-1]</strong></code></td>\n<td><code>XRANGE messages - +</code> <br/><strong>→ [1589929244828-1]</strong></code></td>\n</tr>\n\n\n<tr>\n<td><em>t4</em></td>\n<td><code>DEL messages</code></strong></td>\n<td><code>XADD messages * text goodbye</code></td>\n</tr>\n\n<tr>\n<td><em>t5</em></td>\n<td><em>— Sync —</em></td>\n<td><em>— Sync —</em></td>\n</tr>\n\n\n<tr>\n<td><em>t6</em></td>\n<td><code>XRANGE messages - +</code> <br/><strong>→ [1589929246795-2]</strong></code></td>\n<td><code>XRANGE messages - +</code> <br/><strong>→ [1589929246795-2]</strong></code></td>\n</tr>\n\n\n</tbody>\n</table>\n\nAt _t4_, the stream is deleted from Region 1. At the same time, an entry with ID ending in `3700` is added to the same stream at Region 2. After the sync, at _t6_, the entry with ID ending in `3700` exists in both regions. This is because that entry was not visible when the local stream was deleted at _t4_."},{"id":"id-generation-modes","title":"ID generation modes","role":"content","text":"Usually, you should allow Redis streams generate its own stream entry IDs. You do this by specifying `*` as the ID in calls to XADD. However, you _can_ provide your own custom ID when adding entries to a stream.\n\nBecause Active-Active databases replicate asynchronously, providing your own IDs can create streams with duplicate IDs. This can occur when you write to the same stream from multiple regions.\n\n| Time | Region 1                      | Region 2                      |\n| ---- | ------------------------------- | ------------------------------- |\n| _t1_   | `XADD x 100-1 f1 v1`              | `XADD x 100-1 f1 v1`              |\n| _t2_   | _— Sync —_                    | _— Sync —_                    |\n| _t3_   | `XRANGE x - +`<br/> **→ [100-1, 100-1]** | `XRANGE x - +`<br/> **→ [100-1, 100-1]** |\n\nIn this scenario, two entries with the ID `100-1` are added at _t1_. After syncing, the stream `x` contains two entries with the same ID.\n\n> [!NOTE]\n> Stream IDs in Redis Open Source consist of two integers separated by a dash ('-'). When the server generates the ID, the first integer is the current time in milliseconds, and the second integer is a sequence number. So, the format for stream IDs is MS-SEQ.\n\nTo prevent duplicate IDs and to comply with the original Redis streams design, Active-Active databases provide three ID modes for XADD:\n\n1. **Strict**: In _strict_ mode, XADD allows server-generated IDs (using the '`*`' ID specifier) or IDs consisting only of the millisecond (MS) portion. When the millisecond portion of the ID is provided, the ID's sequence number is calculated using the database's region ID. This prevents duplicate IDs in the stream. Strict mode rejects full IDs (that is, IDs containing both milliseconds and a sequence number).\n1. **Semi-strict**: _Semi-strict_ mode is just like _strict_ mode except that it allows full IDs (MS-SEQ). Because it allows full IDs, duplicate IDs are possible in this mode.\n1. **Liberal**: XADD allows any monotonically ascending ID. When given the millisecond portion of the ID, the sequence number will be set to `0`. This mode may also lead to duplicate IDs.\n\nThe default and recommended mode is _strict_, which prevents duplicate IDs.\n\n> [!WARNING]\n> Why do you want to prevent duplicate IDs? First, XDEL, XCLAIM, and other commands can affect more than one entry when duplicate IDs are present in a stream. Second, duplicate entries may be removed if a database is exported or renamed.\n\nTo change XADD's ID generation mode, use the `rladmin` command-line utility:\n\nSet _strict_ mode:\n[code example]\n\nSet _semi-strict_ mode:\n[code example]\n\nSet _liberal_ mode:\n[code example]"},{"id":"iterating-a-stream-with-xread","title":"Iterating a stream with XREAD","role":"content","text":"In Redis Open Source and in non-Active-Active databases, you can use XREAD to iterate over the entries in a Redis Stream. However, with an Active-Active database, XREAD may skip entries. This can happen when multiple regions write to the same stream.\n\nIn the example below, XREAD skips entry `115-2`.\n\n| Time | Region 1                                         | Region 2                                         |\n| ---- | -------------------------------------------------- | -------------------------------------------------- |\n| _t1_   | `XADD x 110 f1 v1`                                   | `XADD x 115 f1 v1`                                   |\n| _t2_   | `XADD x 120 f1 v1`                                   |                                                    |\n| _t3_   | `XADD x 130 f1 v1`                                   |                                                    |\n| _t4_   | `XREAD COUNT 2 STREAMS x 0` <br/>**→ [110-1, 120-1]**       |                                                    |\n| _t5_   | _— Sync —_                                       | _— Sync —_                                       |\n| _t6_   | `XREAD COUNT 2 STREAMS x 120-1` <br/>**→ [130-1]**            |                                                    |\n| _t7_   | `XREAD STREAMS x 0` <br/>**→[110-1, 115-2, 120-1, 130-1]** | `XREAD STREAMS x 0` <br/>**→[110-1, 115-2, 120-1, 130-1]** |\n\n\nYou can use XREAD to reliably consume a stream only if all writes to the stream originate from a single region. Otherwise, you should use XREADGROUP, which always guarantees reliable stream consumption."},{"id":"consumer-groups","title":"Consumer groups","role":"content","text":"Active-Active databases fully support consumer groups with Redis Streams. Here is an example of creating two consumer groups concurrently:\n\n| Time | Region 1                  | Region 2                  |\n| ---- | --------------------------- | --------------------------- |\n| _t1_   | `XGROUP CREATE x group1 0`        | `XGROUP CREATE x group2 0`        |\n| _t2_   | `XINFO GROUPS x` <br/>**→ [group1]**     | `XINFO GROUPS x` <br/>**→ [group2]**     |\n| _t3_   | _— Sync —_                | — Sync —                |\n| _t4_   | `XINFO GROUPS x` <br/>**→ [group1, group2]** | `XINFO GROUPS x` <br/>**→ [group1, group2]** |\n\n\n> [!NOTE]\n> Redis Open Source uses one radix tree (`rax`) to hold the global pending entries list and another `rax` for each consumer's PEL.\n> The global PEL is a unification of all consumer PELs, which are disjoint.\n>\n> An Active-Active database stream maintains a global PEL and a per-consumer PEL for each region.\n>\n> When given an ID different from the special \">\" ID, XREADGROUP iterates simultaneously over all of the PELs for all consumers.\n> It returns the next entry by comparing entry IDs from the different PELs."},{"id":"conflict-resolution-1","title":"Conflict resolution","role":"content","text":"The \"delete wins\" approach is a way to automatically resolve conflicts with consumer groups.\nIn case of concurrent consumer group operations, a delete will \"win\" over other concurrent operations on the same group.\n\nIn this example, the DEL at _t4_ deletes both the observed `group1` and the non-observed `group2`:\n\n| Time | Region 1              | Region 2              |\n| ---- | ----------------------- | ----------------------- |\n| _t1_   | `XGROUP CREATE x group1 0`    |                         |\n| _t2_   | _— Sync —_            | _— Sync —_            |\n| _t3_   | `XINFO GROUPS x` <br/>**→ [group1]** | `XINFO GROUPS x` <br/>**→ [group1]** |\n| _t4_   | `DEL x`                   | `XGROUP CREATE x group2 0`    |\n| _t5_   | _— Sync —_            | _— Sync —_            |\n| _t6_   | `EXISTS x` <br/>**→ 0** | `EXISTS x` <br/>**→  0**          |\n\nIn this example, the XGROUP DESTROY at _t4_ affects both the observed `group1` created in Region 1 and the non-observed `group1` created in Region 3:\n\n| time | Region 1              | Region 2              | Region 3            |\n| ---- | ----------------------- | ----------------------- | --------------------- |\n| _t1_   | `XGROUP CREATE x group1 0`    |                         |                       |\n| _t2_   | _— Sync —_            | _— Sync —_            |                       |\n| _t3_   | `XINFO GROUPS x` <br/>**→ [group1]** | `XINFO GROUPS x` <br/>**→  [group1]** | `XINFO GROUPS x` <br/>**→  []** |\n| _t4_   |                         | `XGROUP DESTROY x group1`     | `XGROUP CREATE x group1 0` |\n| _t5_   | _— Sync —_            | _— Sync —            | — Sync —          |\n| _t6_   | `EXISTS x` <br/>**→  0**          | `EXISTS x` <br/>**→ 0**          | `EXISTS x` <br/>**→  0**    |"},{"id":"group-replication","title":"Group replication","role":"content","text":"Calls to XREADGROUP and XACK change the state of a consumer group or consumer. However, it's not efficient to replicate every change to a consumer or consumer group.\n\nTo maintain consumer groups in Active-Active databases with optimal performance:\n\n1. Group existence (CREATE/DESTROY) is replicated.\n1. Most XACK operations are replicated.\n1. Other operations, such as XGROUP, SETID, DELCONSUMER, are not replicated.\n\nFor example:\n\n| Time | Region 1                                        | Region 2               |\n| ---- | ------------------------------------------------- | ------------------------ |\n| _t1_   | `XADD messages 110 text hello`                                |                          |\n| _t2_   | `XGROUP CREATE messages group1 0`                              |                          |\n| _t3_   | `XREADGROUP GROUP group1 Alice STREAMS messages >` <br/>**→ [110-1]** |                          |\n| _t4_   | _— Sync —_                                      | _— Sync —_             |\n| _t5_   | `XRANGE messages - +` <br/>**→ [110-1]**                          | XRANGE messages - + <br/>**→ [110-1]** |\n| _t6_   | `XINFO GROUPS messages` <br/>**→ [group1]**                            | XINFO GROUPS messages <br/>**→ [group1]**   |\n| _t7_   | `XINFO CONSUMERS messages group1` <br/>**→ [Alice]**                      | XINFO CONSUMERS messages group1 <br/>**→ []**  |\n| _t8_   | `XPENDING messages group1 - + 1` <br/>**→ [110-1]**                     | XPENDING messages group1 - + 1<br/>**→ []** |\n\nUsing XREADGROUP across regions can result in regions reading the same entries.\nThis is due to the fact that Active-Active Streams is designed for at-least-once reads or a single consumer.\nAs shown in the previous example, Region 2 is not aware of any consumer group activity, so redirecting the XREADGROUP traffic from Region 1 to Region 2 results in reading entries that have already been read."},{"id":"replication-performance-optimizations","title":"Replication performance optimizations","role":"content","text":"Consumers acknowledge messages using the XACK command. Each ack effectively records the last consumed message. This can result in a lot of cross-region traffic. To reduce this traffic, we replicate XACK messages only when all of the read entries are acknowledged.\n\n| Time | Region 1                                                      | Region 2   | Explanation                                                                                                     |\n| ---- | --------------------------------------------------------------- | ------------ | --------------------------------------------------------------------------------------------------------------- |\n| _t1_   | `XADD x 110-0 f1 v1`                                              |              |                                                                                                                 |\n| _t2_   | `XADD x 120-0 f1 v1`                                              |              |                                                                                                                 |\n| _t3_   | `XADD x 130-0 f1 v1`                                              |              |                                                                                                                 |\n| _t4_   | `XGROUP CREATE x group1 0`                                            |              |                                                                                                                 |\n| _t5_   | `XREADGROUP GROUP group1 Alice STREAMS x >` <br/>**→ [110-0, 120-0, 130-0]** |              |                                                                                                                 |\n| _t6_   | `XACK x group1 110-0`                                                   |              |                                                                                                                 |\n| _t7_   | _— Sync —_                                                    | _— Sync —_ | 110-0 and its preceding entries (none) were acknowledged. We replicate an XACK effect for 110-0.                |\n| _t8_   | `XACK x group1 130-0`                                                   |              |                                                                                                                 |\n| _t9_   | _— Sync —_                                                    | _— Sync —_ | 130-0 was acknowledged, but not its preceding entries (120-0). We DO NOT replicate an XACK effect for 130-0     |\n| _t10_  | `XACK x group1 120-0`                                                   |              |                                                                                                                 |\n| _t11_  | _— Sync —_                                                    | _— Sync —_ | 120-0 and its preceding entries (110-0 through 130-0) were acknowledged. We replicate an XACK effect for 130-0. |\n\nIn this scenario, if we redirect the XREADGROUP traffic from Region 1 to Region 2 we do not re-read entries 110-0, 120-0 and 130-0.\nThis means that the XREADGROUP does not return already-acknowledged entries."},{"id":"guarantees","title":"Guarantees","role":"content","text":"Unlike XREAD, XREADGOUP will never skip stream entries.\n\nIn traffic redirection, XREADGROUP may return entries that have been read but not acknowledged. It may also even return entries that have already been acknowledged.\n\nWhen entries are added from multiple regions, Redis guarantees ordered entries in the reply of a single read command (`XREAD`, `XREADGROUP`, or `XRANGE`). However, ordered IDs are not guaranteed for a sequence of read commands.\nFor example, two subsequent `XREADGROUP GROUP group consumer COUNT 1 STREAMS key >` commands may reply with decreasing IDs."},{"id":"summary","title":"Summary","role":"content","text":"With Active-Active streams, you can write to the same logical stream from multiple regions. As a result, the behavior of Active-Active streams differs somewhat from the behavior you get with Redis Open Source. This is summarized below:"},{"id":"stream-commands","title":"Stream commands","role":"content","text":"1. When using the _strict_ ID generation mode, XADD does not permit full stream entry IDs (that is, an ID containing both MS and SEQ).\n1. XREAD may skip entries when iterating a stream that is concurrently written to from more than one region. For reliable stream iteration, use XREADGROUP instead.\n1. XSETID fails when the new ID is less than current ID."},{"id":"consumer-group-notes","title":"Consumer group notes","role":"content","text":"The following consumer group operations are replicated:\n\n1. Consecutive XACK operations\n1. Consumer group creation and deletion (that is, XGROUP CREATE and XGROUP DESTROY)\n\nAll other consumer group metadata is not replicated.\n\nA few other notes:\n\n1. XGROUP SETID and DELCONSUMER are not replicated.\n1. Consumers exist locally (XREADGROUP creates a consumer implicitly).\n1. Renaming a stream (using RENAME) deletes all consumer group information."}],"examples":[{"id":"id-generation-modes-ex0","language":"sh","code":"rladmin tune db crdb crdt_xadd_id_uniqueness_mode strict","section_id":"id-generation-modes"},{"id":"id-generation-modes-ex1","language":"sh","code":"rladmin tune db crdb crdt_xadd_id_uniqueness_mode semi-strict","section_id":"id-generation-modes"},{"id":"id-generation-modes-ex2","language":"sh","code":"rladmin tune db crdb crdt_xadd_id_uniqueness_mode liberal","section_id":"id-generation-modes"}]}
{"schema_version":2,"id":"operate/rs/databases/active-active/develop/data-types/strings","title":"Strings and bitfields in Active-Active databases","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/develop/data-types/strings/","summary":"Information about using strings and bitfields with an Active-Active database.","tags":["docs","operate","rs","rc"],"last_updated":"2026-09-24T09:16:24+01:00","page_type":"content","content_hash":"71703cb17032839265ea8a5dbab9ce487b58a043d4e7a28ef124e6a6d42641a7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Active-Active databases support both strings and bitfields.\n\n> [!NOTE]\n> Active-Active **bitfield** support was added in RS version 6.0.20.\n\nChanges to both of these data structures will be replicated across Active-Active member databases."},{"id":"replication-semantics","title":"Replication semantics","role":"content","text":"Except in the case of [string counters](#string-counter-support) (see below), both strings and bitfields are replicated using a \"last write wins\" approach. The reason for this is that strings and bitfields are effectively binary objects. So, unlike with lists, sets, and hashes, the conflict resolution semantics of a given operation on a string or bitfield are undefined."},{"id":"how-last-write-wins-works","title":"How \"last write wins\" works","role":"content","text":"A wall-clock timestamp (OS time) is stored in the metadata of every string\nand bitfield operation. If the replication syncer cannot determine the order of operations,\nthe value with the latest timestamp wins. This is the only case with Active-Active databases where OS time is used to resolve a conflict.\n\nHere's an example where an update happening to the same key at a later\ntime (t2) wins over the update at t1.\n\n|  **Time** | **Region 1** | **Region 2** |\n|  :------: | :------: | :------: |\n|  t1 | SET text \"a\" |  |\n|  t2 |  | SET text \"b\" |\n|  t3 | — Sync — | — Sync — |\n|  t4 | SET text \"c\" |  |\n|  t5 | — Sync — | — Sync — |\n|  t6 |  | SET text \"d\" |"},{"id":"string-counter-support","title":"String counter support","role":"content","text":"When you're using a string as counter (for instance, with the [INCR](https://redis.io/docs/latest/commands/incr) or [INCRBY](https://redis.io/docs/latest/commands/incrby) commands),\nthen conflicts will be resolved semantically.\n\nOn conflicting writes, counters accumulate the total counter operations\nacross all member Active-Active databases in each sync.\n\nHere's an example of how counter\nvalues works when synced between two member Active-Active databases. With\neach sync, the counter value accumulates the private increment and\ndecrements of each site and maintain an accurate counter across\nconcurrent writes.\n\n|  **Time** | **Region 1** | **Region 2** |\n|  :------: | :------: | :------: |\n|  t1 | INCRBY counter 7 |  |\n|  t2 |  | INCRBY counter 3 |\n|  t3 | GET counter<br/>7 | GET counter<br/>3 |\n|  t4 | — Sync — | — Sync — |\n|  t5 | GET counter<br/>10 | GET counter<br/>10 |\n|  t6 | DECRBY counter 3 |  |\n|  t7 |  | INCRBY counter 6 |\n|  t8 | — Sync — | — Sync — |\n|  t9 | GET counter<br/>13 | GET counter<br/>13 |\n\n> [!NOTE]\n> Active-Active databases support 59-bit counters.\n> This limitation is to protect from overflowing a counter in a concurrent operation."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/databases/active-active/develop/develop-for-aa","title":"Develop applications with Active-Active databases","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/develop/develop-for-aa/","summary":"Overview of how developing applications differs for Active-Active databases from standalone Redis databases.","tags":["docs","operate","rs","rc"],"last_updated":"2026-09-24T09:16:24+01:00","page_type":"content","content_hash":"ee9a8032a3c0e0bd833e5b8d9166be24069f6b077c63d36bb4f9137894ef8a2e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Developing geo-distributed, multi-master applications can be difficult.\nApplication developers may have to understand a large number of race\nconditions between updates to various sites, network, and cluster\nfailures that could reorder the events and change the outcome of the\nupdates performed across geo-distributed writes.\n\nActive-Active databases (formerly known as CRDB) are geo-distributed databases that span multiple Redis Software clusters.\nActive-Active databases depend on [multi-master replication](https://en.wikipedia.org/wiki/Multi-master_replication) and [conflict-free\nreplicated data types (CRDTs)](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type) to power a simple development experience\nfor geo-distributed applications. Active-Active databases allow developers to use existing\nRedis data types and commands, but automatically handle conflicting concurrent writes to the same key\nacross multiple geographies. For example, developers can simply use the\n`INCR` or `INCRBY` method in Redis in all instances of the geo-distributed\napplication, and Active-Active databases handle the additive nature of `INCR` to reflect the\ncorrect final value.\n\nThe following example displays a sequence of events\nover time: t1 to t9. This Active-Active database has two member Active-Active databases: member CRDB1 and\nmember CRDB2. The local operations running in each member Active-Active database are\nlisted under the member Active-Active database name. The sync events represent the moment\nwhen synchronization catches up to distribute all local member Active-Active database\nupdates to other participating clusters and other member Active-Active databases.\n\n|  **Time** | **Member CRDB1** | **Member CRDB2** |\n|  :------: | :------: | :------: |\n|  t1 | INCRBY key1 7 |  |\n|  t2 |  | INCRBY key1 3 |\n|  t3 | GET key1<br/>7 | GET key1<br/>3 |\n|  t4 | — Sync — | — Sync — |\n|  t5 | GET key1<br/>10 | GET key1<br/>10 |\n|  t6 | DECRBY key1 3 |  |\n|  t7 |  | INCRBY key1 6 |\n|  t8 | — Sync — | — Sync — |\n|  t9 | GET key1<br/>13 | GET key1<br/>13 |\n\nDatabases provide various approaches to address some of these concerns:\n\n- **Active-Passive geo-distributed deployments**: With active-passive\n    distributions, all writes go to an active cluster. Redis Sofware\n    provides a [Replica Of](https://redis.io/docs/latest/operate/rs/databases/import-export/replica-of) capability that provides a similar approach.\n    This can be employed when the workload is heavily balanced toward\n    reads and few writes. However, WAN performance and availability\n    can be unreliable, and traveling large distances for writes takes away\n    from application performance and availability.\n- **Two-phase commit (2PC)**: This approach is designed around a protocol\n    that commits a transaction across multiple transaction managers.\n    Two-phase commit provides a consistent transactional write across\n    regions but fails transactions unless all participating transaction\n    managers are available at the time of the transaction. The number\n    of messages exchanged and its cross-regional availability\n    requirement make two-phase commit unsuitable for even moderate\n    throughputs and cross-geo writes that go over WANs.\n- **Sync update with quorum-based writes**: This approach synchronously\n    coordinates a write across the majority of replicas across\n    clusters spanning multiple regions. However, just like two-phase\n    commit, the number of messages exchanged and its cross-regional\n    availability requirement make geo-distributed quorum writes\n    unsuitable for moderate throughputs and cross-geo writes that go\n    over WANs.\n- **Last-Writer-Wins (LWW) conflict resolution**: Some systems provide\n    simplistic conflict resolution for all types of writes where\n    system clocks are used to determine the winner across conflicting\n    writes. LWW is lightweight and can be suitable for simpler data.\n    However, LWW can be destructive to updates that are not necessarily\n    conflicting. For example, adding a new element to a set across two\n    geographies concurrently would result in only one of these new\n    elements appearing in the final result with LWW.\n- **MVCC (multi-version concurrency control)**: MVCC systems maintain\n    multiple versions of data and may expose ways for applications to\n    resolve conflicts. Even though an MVCC system can provide a flexible\n    way to resolve conflicting writes, it comes at the cost of great\n    complexity in the development of a solution.\n\nEven though types and commands in Active-Active databases look identical to standard Redis\ntypes and commands, the underlying types in Redis Software are enhanced to maintain\nmore metadata to create the conflict-free data type experience. This\nsection explains what you need to know about developing with Active-Active databases on\nRedis Software."},{"id":"lua-scripts","title":"Lua scripts","role":"content","text":"Active-Active databases support Lua scripts, but unlike standard Redis, Lua scripts always\nexecute in effects replication mode. There is currently no way to\nexecute them in script-replication mode."},{"id":"eviction","title":"Eviction","role":"content","text":"The default policy for Active-Active databases is _noeviction_ mode. Redis Software version 6.0.20 and later support all eviction policies for Active-Active databases, unless [Redis Flex or Auto Tiering](https://redis.io/docs/latest/operate/rs/databases/flash) (previously known as Redis on Flash) is enabled.\n\nFor details, see [eviction for Active-Active databases (Redis Software)](https://redis.io/docs/latest/operate/rs/databases/memory-performance/eviction-policy#active-active-database-eviction) or [eviction for Active-Active databases (Redis Cloud)](https://redis.io/docs/latest/operate/rc/databases/configuration/data-eviction-policies#active-active-replication-considerations)."},{"id":"expiration","title":"Expiration","role":"content","text":"Expiration is supported with special multi-master semantics.\n\nIf a key's expiration time is changed at the same time on different\nmembers of the Active-Active database, the longer extended time set via TTL on a key is\npreserved.\n\nIf this command was performed on key1 on cluster #1:\n\n[code example]\n\nIf this command was performed on key1 on cluster #2:\n\n[code example]\n\nThe `EXPIRE` command setting the key to 50 would win.\n\nAnd if this command was performed on key1 on cluster #3:\n\n[code example]\n\nIt would win out of the three clusters hosting the Active-Active database as it sets the\nTTL on key1 to an infinite time.\n\nThe replica responsible for the winning expire value is also\nresponsible for expiring the key and propagating a DEL effect when this\nhappens. From this point on, a losing replica is not responsible\nfor expiring the key, unless another `EXPIRE` command resets the TTL.\nFurthermore, a replica that is not the owner of the expired value:\n\n- Silently ignores the key if a user attempts to access it in READ\n    mode, for example, treating it as if it was expired but not propagating a\n    DEL.\n- Expires it (sending a DEL) before making any modifications if a user\n    attempts to access it in WRITE mode.\n    \n    > [!NOTE]\n    > Expiration values are in the range of [0,&nbsp;2^49] for Active-Active databases and [0,&nbsp;2^64] for regular databases.\n    >"},{"id":"tombstones","title":"Tombstones","role":"content","text":"For conflict resolution purposes, Active-Active databases cannot immediately release a deleted key. Instead, the key is logically deleted but remains in memory as a tombstone until the garbage collector can safely remove it.\n\nWhen a deleted key becomes a tombstone, it frees some memory previously consumed by the key. The size of each tombstone varies depending on the data type and the key's history.\n\nThe garbage collector automatically removes a tombstone when all instances in the Active-Active database have observed the deletion operation.\n\nTo monitor tombstones, you can use shard-level metrics exposed by [`INFO crdt`](#info) and Grafana."},{"id":"outofmemory-oom","title":"Out-of-Memory (OOM)","role":"content","text":"If a member Active-Active database is out of memory, that member is marked as\n\"inconsistent\", the member stops responding to user traffic, and\nthe syncer initiates full reconciliation with other peers in the Active-Active database."},{"id":"active-active-database-key-counts","title":"Active-Active database key counts","role":"content","text":"Keys are counted differently for Active-Active databases:\n\n- DBSIZE (in `shard-cli dbsize`) reports key header instances\n    that represent multiple potential values of a key before a replication conflict is resolved.\n- expired_keys (in `bdb-cli info`) can be more than the keys count in DBSIZE (in `shard-cli dbsize`) \n    because expires are not always removed when a key becomes a tombstone.\n- The Expires average TTL (in `bdb-cli info`) is computed for local expires only."},{"id":"info","title":"INFO","role":"content","text":"The `INFO` command has an additional CRDT section that provides advanced troubleshooting information:\n\n|  **Section** | **Field** | **Description** |\n|  ------ | ------ | ------ |\n|  **CRDT Context** | crdt_config_version | Current Active-Active database configuration version. |\n|   | crdt_slots | Hash slots assigned and reported by this shard. |\n|   | crdt_replid | Unique replica/shard IDs. |\n|   | crdt_clock | Clock value of local vector clock. |\n|   | crdt_ovc | Locally observed Active-Active database vector clock. |\n|  **Peers** | | A list of currently connected peer replication peers. This is similar to the replicas list reported by Redis. |\n|  **Backlogs** | | A list of peer replication backlogs currently maintained. Typically in a full mesh topology, only a single backlog is used for all peers, as the requested IDs are identical. |\n|  **CRDT Stats** | crdt_sync_full | Number of inbound full synchronization processes performed. |\n|   | crdt_sync_partial_ok | Number of partial (backlog based) re-synchronization processes performed. |\n|   | crdt_sync_partial-err | Number of partial re-synchronization processes failed due to exhausted backlog. |\n|   | crdt_merge_reqs | Number of inbound merge requests processed. |\n|   | crdt_effect_reqs | Number of inbound effect requests processed. |\n|   | crdt_ovc_filtered_effect_reqs | Number of inbound effect requests filtered due to old vector clock. |\n|   | crdt_gc_pending | Number of elements pending garbage collection. |\n|   | crdt_gc_attempted | Number of attempts to garbage collect tombstones. |\n|   | crdt_gc_collected | Number of tombstones garbage collected successfully. |\n|   | crdt_gc_gvc_min | The minimal globally observed vector clock, as computed locally from all received observed clocks. |\n|   | crdt_stale_released_with_merge | Indicates the last stale flag transition was a result of a complete full sync. |\n|  **CRDT Replicas** | | A list of crdt_replica \\<uid> entries, each describes the known state of a remote instance with the following fields: |\n|   | config_version | Last configuration version reported. |\n|   | shards | Number of shards. |\n|   | slots | Total number of hash slots. |\n|   | slot_coverage | A flag indicating remote shards provide full coverage (all shards are alive). |\n|   | max_ops_lag | Number of local operations not yet observed by the least updated remote shard. |\n|   | min_ops_lag | Number of local operations not yet observed by the most updated remote shard. |"}],"examples":[{"id":"expiration-ex0","language":"sh","code":"127.0.0.1:6379> EXPIRE key1 10","section_id":"expiration"},{"id":"expiration-ex1","language":"sh","code":"127.0.0.1:6379> EXPIRE key1 50","section_id":"expiration"},{"id":"expiration-ex2","language":"sh","code":"127.0.0.1:6379> PERSIST key1","section_id":"expiration"}]}
{"schema_version":2,"id":"operate/rs/databases/active-active/develop","title":"Active-Active Redis applications","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/develop/","summary":"General information to keep in mind while developing applications for an Active-Active database.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","children":[{"id":"operate/rs/databases/active-active/develop/develop-for-aa","summary":"Overview of how developing applications differs for Active-Active databases from standalone Redis databases.","title":"Develop applications with Active-Active databases","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/develop/develop-for-aa/"},{"id":"operate/rs/databases/active-active/develop/data-types","summary":"Introduction to differences in data types between standalone and Active-Active Redis databases.","title":"Data types for Active-Active databases","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/develop/data-types/"},{"id":"operate/rs/databases/active-active/develop/app-failover-active-active","summary":"How to failover your application to connect to a remote replica.","title":"Application failover with Active-Active databases","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/develop/app-failover-active-active/"}],"page_type":"content","content_hash":"31e8cc1f9013debe79d7ea0f96da1d1ca2c02533c954675e7f06876b7e477a67","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Developing globally distributed applications can be challenging, as\ndevelopers have to think about race conditions and complex combinations\nof events under geo-failovers and cross-region write conflicts. In Redis Software (RS), Active-Active databases\nsimplify developing such applications by directly using built-in smarts\nfor handling conflicting writes based on the data type in use. Instead\nof depending on just simplistic \"last-writer-wins\" type conflict\nresolution, geo-distributed Active-Active databases (formerly known as CRDBs) combine techniques defined in CRDT\n(conflict-free replicated data types) research with Redis types to\nprovide smart and automatic conflict resolution based on the data type's\nintent.\n\nAn Active-Active database is a globally distributed database that spans multiple Redis\nEnterprise Software clusters. Each Active-Active database can have many Active-Active database instances\nthat come with added smarts for handling globally distributed writes\nusing the proven\n[CRDT](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type)\napproach.\n[CRDT](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type)\nresearch describes a set of techniques for creating systems that can\nhandle conflicting writes. CRDBs powered by Multi-Master Replication\n(MMR) provide a straightforward and effective way to replicate your\ndata between regions and simplify development of complex applications\nthat can maintain correctness under geo-failovers and concurrent\ncross-region writes to the same data.\n\n![Geo-replication world map](https://redis.io/docs/latest/images/rs/crdbs.png)\n\nActive-Active databases replicate data between multiple Redis Software\nclusters. Common uses for Active-Active databases include disaster recovery,\ngeographically redundant applications, and keeping data closer to your\nusers' locations. MMR is always multi-directional amongst the clusters\nconfigured in the Active-Active database. For unidirectional replication, please see the\nReplica Of capabilities in Redis Software."},{"id":"example-of-synchronization","title":"Example of synchronization","role":"example","text":"In the example below, database writes are concurrent at the points in\ntime t1 and t2 and happen before a sync can communicate the changes.\nHowever, writes at times t4 and t6 are not concurrent as a sync happened\nin between.\n\n|  **Time** | **CRDB Instance1** | **CRDB Instance2** |\n|  ------: | :------: | :------: |\n|  t1 | SET key1 \"a\" |  |\n|  t2 |  | SET key1 \"b\" |\n|  t3 | — Sync — | — Sync — |\n|  t4 | SET key1 \"c\" |  |\n|  t5 | — Sync — | — Sync — |\n|  t6 |  | SET key1 \"d\" |\n\n[Learn more about\nsynchronization for\neach supported data type](https://redis.io/docs/latest/operate/rs/databases/active-active/develop/data-types) and [how to develop applications](https://redis.io/docs/latest/operate/rs/databases/active-active/develop/develop-for-aa) with them on Redis Software."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/databases/active-active/disaster-recovery/application-based","title":"Application-based disaster recovery","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/disaster-recovery/application-based/","summary":"Application-based disaster recovery for Active-Active databases using custom application-level monitoring and connectivity management.","tags":["docs","operate","rs","rc"],"last_updated":"2026-09-24T09:16:24+01:00","page_type":"content","content_hash":"560c24b6657468482bb3d7df8d90d58114b2af88509f878fa1293e0da37f421c","sections":[{"id":"content","title":"Content","role":"content","text":"For complete control over failover and failback, you can implement disaster recovery mechanisms directly in the application server.\n\nFor more information, see [Application failover with Active-Active databases](https://redis.io/docs/latest/operate/rs/databases/active-active/develop/app-failover-active-active)."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/databases/active-active/disaster-recovery/client-library-based","title":"Client library-based disaster recovery","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/disaster-recovery/client-library-based/","summary":"Client library-based disaster recovery for Active-Active databases using Redis client libraries with built-in failover logic.","tags":["docs","operate","rs","rc"],"last_updated":"2026-10-01T15:27:03+01:00","page_type":"content","content_hash":"710824788c5d2d1600b786754222a220205c4edfc7181cf141f299b5c4b7cd30","sections":[{"id":"content","title":"Content","role":"content","text":"Some Redis client libraries support geographic failover and failback. These client libraries monitor all Active-Active database members and instantiate connections for all endpoints in advance to allow faster failover and failback.\n\nAdvantages:\n\n- No additional hardware or software components required.\n\n- No high availability considerations.\n\n- No scalability concerns.\n\n- Tighter control over connectivity, such as timeouts, connection retries, and dynamic reconfiguration.\n\n- OSS Cluster API support (currently only available in redis-py)\n\n- Low latency.\n\nConsiderations:\n\n- Requires code changes for failover and failback logic.\n\n- Concurrent access across replicas is possible, but can be mitigated using the distributed health status provided by the database availability API requests.\n\n- When a development framework uses Redis transparently, failover and failback might not be easy to configure.\n\nThe following diagram shows a client library-based disaster recovery approach:\n\n![Diagram of client libraries routing traffic to Active-Active database members](https://redis.io/docs/latest/images/active-active-disaster-recovery/client-library.svg)\n\nThe following diagram shows a client-based disaster recovery approach that also uses [connection pooling](https://redis.io/docs/latest/develop/clients/pools-and-muxing#connection-pooling):\n\n![Diagram of client libraries with connection pooling routing traffic to Active-Active database members](https://redis.io/docs/latest/images/active-active-disaster-recovery/client-library-connection-pool.svg)\n\nFor additional information, see the the introduction to\n[Client-side geographic failover](https://redis.io/docs/latest/develop/clients/failover)\nand also the following client library guides for failover and failback:\n\n- [Jedis (Java)](https://redis.io/docs/latest/develop/clients/jedis/failover)\n\n- [Lettuce (Java)](https://redis.io/docs/latest/develop/clients/lettuce/failover)\n\n- [redis-py (Python)](https://redis.io/docs/latest/develop/clients/redis-py/failover)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/databases/active-active/disaster-recovery","title":"Disaster recovery strategies for Active-Active databases","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/disaster-recovery/","summary":"Disaster recovery strategies for Active-Active databases using network, proxy, client library, and application-based approaches.","tags":["docs","operate","rs","rc"],"last_updated":"2026-09-24T09:16:24+01:00","children":[{"id":"operate/rs/databases/active-active/disaster-recovery/network-based","summary":"Network-based disaster recovery for Active-Active databases using global traffic managers and load balancing solutions.","title":"Network-based disaster recovery","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/disaster-recovery/network-based/"},{"id":"operate/rs/databases/active-active/disaster-recovery/proxy-based","summary":"Proxy-based disaster recovery for Active-Active databases.","title":"Proxy-based disaster recovery","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/disaster-recovery/proxy-based/"},{"id":"operate/rs/databases/active-active/disaster-recovery/client-library-based","summary":"Client library-based disaster recovery for Active-Active databases using Redis client libraries with built-in failover logic.","title":"Client library-based disaster recovery","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/disaster-recovery/client-library-based/"},{"id":"operate/rs/databases/active-active/disaster-recovery/application-based","summary":"Application-based disaster recovery for Active-Active databases using custom application-level monitoring and connectivity management.","title":"Application-based disaster recovery","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/disaster-recovery/application-based/"}],"page_type":"content","content_hash":"7b7fcdd34a87285fef0eebcbdf40696daa4c71827fbf4e1166fa259b73e8414c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"An application deployed with an Active-Active database connects to a database member that is geographically nearby. If that database member becomes unavailable, the application can fail over to a secondary Active-Active database member, and fail back to the original database member again if it recovers.\n\nHowever, because Active-Active Redis databases do not have a built-in [failover](https://en.wikipedia.org/wiki/Failover) or failback mechanism for application connections, you must implement one of the following [disaster recovery strategies](#disaster-recovery-strategies)."},{"id":"disaster-recovery-strategies","title":"Disaster recovery strategies","role":"content","text":"Depending on your requirements for Recovery Point Objective, Recovery Time Objective, consistency, scalability, resources, maintainability, and other factors, choose one of the following strategies to fail over to a secondary Active-Active member or fail back to the primary member:\n\n- [Network-based](https://redis.io/docs/latest/operate/rs/databases/active-active/disaster-recovery/network-based): Global traffic managers and load balancers handle routing at the network layer, requiring no application changes.\n\n- [Proxy-based](https://redis.io/docs/latest/operate/rs/databases/active-active/disaster-recovery/proxy-based): Software proxies handle detection and routing logic.\n\n- [Client library-based](https://redis.io/docs/latest/operate/rs/databases/active-active/disaster-recovery/client-library-based): Redis client libraries with built-in failover logic.\n\n- [Application-based](https://redis.io/docs/latest/operate/rs/databases/active-active/disaster-recovery/application-based): Custom application-level monitoring and connectivity management."},{"id":"considerations-for-disaster-recovery","title":"Considerations for disaster recovery","role":"content","text":"When implementing a disaster recovery strategy for an Active-Active database, consider the following:\n\n- Is the Active-Active database an on-premise, cloud, multi-cloud, or hybrid-cloud deployment?\n\n- Number of regions and availability zones.\n\n- Application server redundancy and deployment locations.\n\n- Acceptable values for the maximum amount of data that can be lost during a failure (Recovery Point Objective) and the maximum acceptable time to restore service after a failure (Recovery Time Objective).\n\n- Latency and throughput requirements.\n\n- Number of application errors that can be tolerated during a failure.\n\n- Tolerance for reading stale but eventually consistent data during a failover scenario.\n\n- Is concurrent access, in which different application servers can read from or write to different Active-Active database members, acceptable?\n\n- Are there any regulatory or policy requirements for disaster recovery?\n\n- Does the application connect to the Active-Active database using a Redis client library or through a development framework or ecosystem?\n\n- Does the Active-Active database use DNS, the [OSS Cluster API](https://redis.io/docs/latest/operate/rs/clusters/optimize/oss-cluster-api), or the [discovery service](https://redis.io/docs/latest/operate/rs/databases/durability-ha/discovery-service)?\n\n- Is rate-limiting control needed?\n\n- Can you modify the existing codebase or introduce new components, such as load balancers or proxies?"},{"id":"detect-failures-with-health-checks","title":"Detect failures with health checks","role":"content","text":"Depending on the failover strategy, you can use the following health checks to detect Active-Active database failures and determine whether to fail over to a secondary Active-Active member, or fail back to the primary member after the preferred endpoint is back online.\n\nTo determine which health checks to use, consider factors such as detection speed, integrability with the failover strategy, and writability or durability guarantees."},{"id":"lag-aware-database-availability-requests","title":"Lag-aware database availability requests","role":"content","text":"Lag-aware database availability requests are the recommended method to detect database failures in Redis Software deployments. This method guarantees that all the shards of a clustered database are connectable.\n\nSee [Lag-aware database availability requests](https://redis.io/docs/latest/operate/rs/monitoring/db-availability#lag-aware) for more information.\n\n> [!NOTE]\n> Lag-aware database availability requests are not supported for Redis Cloud databases."},{"id":"redis-connection-health-checks","title":"Redis connection health checks","role":"content","text":"You can use an existing connection to the database to check its availability.\n\n#### PING command\n\nThe [`PING`](https://redis.io/docs/latest/commands/ping) command checks that the database endpoint is available and the application can connect to the database.\n\n#### Connection timeouts or Redis errors\n\nBy capturing connection errors, you can determine when to fail over to a secondary Active-Active member or fail back to the primary member based on the [circuit breaker pattern](https://en.wikipedia.org/wiki/Circuit_breaker_design_pattern).\n\n#### Custom health check\n\nYou can also implement custom health checks.\n\nFor example, you can check the keyspace with write operations such as using the [`SET`](https://redis.io/docs/latest/commands/set) command to write arbitrary data. This check verifies that database shards are available and writable.\n\nFor example:\n\n[code example]\n\nUse multiple write operations with different randomized keys to access different shards and guarantee that all the shards are available."},{"id":"health-check-comparison","title":"Health check comparison","role":"content","text":"| Health check | Connectivity | Readability | Writability | Durability | Notes |\n|--------------|--------------|-------------|--------------|------------|-------|\n| Database availability requests |:white_check_mark: |  |  |  | No guarantees on readability. For example, the shard might be reloading from a snapshot. |\n| `PING` |:white_check_mark: |  |  |  | No guarantees on readability. For example, the shard might be reloading from a snapshot. |\n| Keyspace sampling |:white_check_mark: |:white_check_mark: |:white_check_mark: |:white_check_mark: | Write operations are persisted, increasing disk usage for AOF and RDB. |"}],"examples":[{"id":"redis-connection-health-checks-ex0","language":"plaintext","code":"SET <randomized_key_name> <arbitrary_value> EX 1","section_id":"redis-connection-health-checks"}]}
{"schema_version":2,"id":"operate/rs/databases/active-active/disaster-recovery/network-based","title":"Network-based disaster recovery","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/disaster-recovery/network-based/","summary":"Network-based disaster recovery for Active-Active databases using global traffic managers and load balancing solutions.","tags":["docs","operate","rs","rc"],"last_updated":"2026-10-01T15:27:03+01:00","page_type":"content","content_hash":"e4c4ffa5076d1551e4d40f7283ab57a37e3fbff15c72bc5c8286c31077fea9de","sections":[{"id":"overview","title":"Overview","role":"overview","text":"For cross-region availability, you can use a global traffic manager or a global load balancer. Both help route traffic across regions, but they work in different ways:\n\n- Global traffic managers are DNS-based and don't handle traffic directly. Instead, they answer DNS queries with the best IP address to connect to, based on factors such as distance, latency, or health. After DNS resolution, the client connects directly to the endpoint.\n\n- Global load balancers can route actual traffic and make decisions in real time, using request headers or connection state.\n\nIf your deployment does not require cross-region availability, you can use a regional load balancer to grant cross-zone redundancy."},{"id":"cross-region-availability","title":"Cross-region availability","role":"content","text":"For cross-region availability, you can use a global traffic manager or a global load balancer.\n\nAdvantages:\n\n- If DNS routing is available at the application level, no additional load balancer is required between the application and the data tier to resolve the Active-Active database member's FQDN, reducing latency.\n\n- Protects against data center failure since failure in one region should not affect services running in another region."},{"id":"global-traffic-manager","title":"Global traffic manager","role":"content","text":"A global traffic manager acts as an intelligent DNS server that directs clients to healthy endpoints based on distance, latency, or availability. You should configure the traffic manager to route to the local region first and fail over to other regions if an issue occurs.\n\nAdvantages:\n\n- High availability.\n\n- Latency optimization.\n\n- Seamless disaster recovery.\n\nConsiderations:\n\n- DNS propagation delays affect failover time.\n\n- DNS caches can impact proper functioning.\n\n- Limited custom health check support.\n\n- May route traffic during CRDT synchronization, causing stale data reads.\n\nThe following diagram shows how a global traffic manager with DNS resolution routes traffic:\n\n![Diagram of a global traffic manager routing applications to Active-Active database members across regions](https://redis.io/docs/latest/images/active-active-disaster-recovery/gtm-with-DNS.svg)\n\nIf the environment does not allow DNS resolution, you can use a load balancer to direct traffic to the cluster nodes: \n\n![Diagram of a global traffic manager with a load balancer directing traffic to Active-Active database members across regions](https://redis.io/docs/latest/images/active-active-disaster-recovery/gtm-with-load-balancer.svg)"},{"id":"global-load-balancer","title":"Global load balancer","role":"content","text":"For real-time traffic control and more advanced routing logic for cross-region failover and failback, you can use a global load balancer. However, this solution can have higher latency than a global traffic manager.\n\nThe following diagram shows how a global load balancer routes traffic between regions:\n\n![Diagram of a global load balancer routing traffic between Active-Active database members in different regions](https://redis.io/docs/latest/images/active-active-disaster-recovery/global-load-balancer.svg)"},{"id":"cross-zone-availability","title":"Cross-zone availability","role":"content","text":"If your deployment does not require cross-region availability, you can use a regional load balancer to route requests to a healthy Active-Active database member in a different availability zone within the same region.\n\nThe following diagram shows how a regional load balancer routes traffic across availability zones:\n\n![Diagram of a regional load balancer routing traffic across availability zones within a single region](https://redis.io/docs/latest/images/active-active-disaster-recovery/regional-load-balancer.svg)"},{"id":"advantages","title":"Advantages","role":"content","text":"Using a global traffic manager or global load balancer for disaster recovery has the following advantages:\n\n- Because the routing logic happens at the network level:\n\n    - No code changes are required. For applications, there is less logic to manage, and fewer working parts that can break.\n\n    - Development frameworks are agnostic and can connect to a single member Active-Active database endpoint.\n\n- If DNS routing is available at the application level, no additional load balancer is required between the application and the data tier to resolve the member Active-Active database’s FQDN, reducing latency.\n\n- Failure of any kind in one region should not affect services running in another. This approach is ideal for scenarios where an entire data center is compromised, and service continuity is granted at a data center level."},{"id":"considerations","title":"Considerations","role":"content","text":"If you plan to implement network-based disaster recovery, you should also consider the following challenges:\n\n- Standard health check mechanisms are available, but custom health checks may not be supported. Proper availability mechanisms, such as the database availability request, must be configured directly where possible or provided as an additional lightweight service alongside the application. \n\n- Global load balancers introduce latency.\n\n- Refreshing DNS records takes some time to propagate, which challenges the ability to control the failover time. In addition, DNS caches, often used by operating systems or the JVM, can impact the proper functioning of this approach.\n\n- When failback is required, routing traffic from a secondary Active-Active database member to the primary database member can happen during CRDT synchronization. If the failback's target has not completed synchronization, applications might read stale data. \n\n- The Active-Active health check policy should be designed to use the lag-aware database availability request, thus granting control over the observed CRDT replication lag. This method increases consistency in a failback scenario. However, specific global traffic managers and global load balancers might require extra effort to set the Active-Active health check policy. \n\n- Replicating the application stack for several regions impacts planning, maintenance, and costs."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/databases/active-active/disaster-recovery/proxy-based","title":"Proxy-based disaster recovery","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/disaster-recovery/proxy-based/","summary":"Proxy-based disaster recovery for Active-Active databases.","tags":["docs","operate","rs","rc"],"last_updated":"2026-10-01T15:27:03+01:00","page_type":"content","content_hash":"14b7225894f70fd5f231fe0304133463e3a8c7aed2b8f55d72147a7259865bad","sections":[{"id":"overview","title":"Overview","role":"overview","text":"If you add a lightweight proxy software component between the clients and the Active-Active database, applications can dynamically route requests to the optimal endpoint.\n\nAdvantages:\n\n- Proxies provide proactive and reactive health check methods, such as polling target health periodically using either a TCP connection or an HTTP request, or monitoring live operations for errors.\n\n- Proxies can be configured to run Active-Active health checks, such as the lag-aware database availability requests.\n\n- If an Active-Active database member fails, a proxy can automatically detect the issue and redirect traffic to a healthy Active-Active database member without requiring DNS propagation delays or client disconnections. This enables fast, controlled failover and minimizes downtime.\n\nConsiderations:\n\n- If you do not use DNS to resolve the Active-Active database members' FQDNs:\n\n    - The proxies must have static IPs.\n\n    - If you add a new node to the cluster, you must configure the proxy with the new endpoint.\n\n    - A configuration syncer component is required to discover topology changes and reconfigure the proxy.\n\n- Proxies introduce latency.\n\n- Proxy failures can disconnect clients and cause disruptions."},{"id":"avoid-concurrent-access-across-replicas","title":"Avoid concurrent access across replicas","role":"content","text":"If concurrent access across replicas must be avoided in every scenario, you can use a centralized proxy with a standby proxy instance for high availability.\n\nAdvantages:\n\n- Prevents concurrent access across replicas.\n\n- Failover and failback are simultaneous regardless of the Active-Active health check policy.\n\nConsiderations:\n\n- Although the proxy can be monitored with a watchdog and restarted in case of failure, this setup does not grant high availability for the proxy.\n\n- Limited scalability.\n\nThe following diagram shows a centralized proxy architecture with a standby proxy instance:\n\n![Diagram of a centralized proxy architecture with active and standby proxy instances routing to Active-Active database members](https://redis.io/docs/latest/images/active-active-disaster-recovery/centralized-proxy.svg)"},{"id":"co-locate-to-reduce-latency-and-improve-scalability","title":"Co-locate to reduce latency and improve scalability","role":"content","text":"To reduce latency and improve scalability, you can use a proxy co-located in the application server.\n\nAdvantages:\n\n- Reduced latency.\n\n- Better scalability.\n\nConsiderations:\n\n- Failover and failback might not be simultaneous depending on the Active-Active health check policy.\n\nThe following diagram shows a co-located proxy architecture where each application server has its own proxy:\n\n![Diagram of co-located proxy architecture where each application server has its own proxy instance](https://redis.io/docs/latest/images/active-active-disaster-recovery/co-located-proxy-and-app.svg)"},{"id":"pool-proxies-for-scalability","title":"Pool proxies for scalability","role":"content","text":"You can use a pool of active proxies to scale the routing layer. Application servers can balance new connections to the pool of proxies using a round-robin distribution algorithm, such as DNS-based round robin.\n\nAdvantages:\n\n- High availability without complex monitoring and failover solutions.\n\n- Flexible scalability of the routing layer.\n\nConsiderations:\n\n- Concurrent access across replicas is possible, but can be mitigated using database availability API requests.\n\nThe following diagram shows a pool of proxies:\n\n![Diagram of a pool of active proxy instances](https://redis.io/docs/latest/images/active-active-disaster-recovery/proxy-pool.svg)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/databases/active-active/get-started","title":"Get started with Redis Software Active-Active databases","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/get-started/","summary":"Quick start guide to create an Active-Active database for test and development.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"3215b1a21ec27b9e6a8b83564aef90ed70766ebe159da9ecedc6e37d2d1e0a7b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"To get started, this article will help you set up an Active-Active database, formerly known as CRDB (conflict-free replicated database), spanning across two Redis Software\nclusters for test and development environments. Here are the steps:\n\n1. Run two Redis Software Docker containers.\n\n1. Set up each container as a cluster.\n\n1. Create a new Redis Software Active-Active database.\n\n1. Test connectivity to the Active-Active database.\n\nTo run an Active-Active database on installations from the [Redis Software download package](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/redis-enterprise-software-quickstart),\nset up two Redis Software installations and continue from Step 2.\n\n> [!NOTE]\n> This getting started guide is for development or demonstration environments.\n> For production environments, see [Create an Active-Active geo-replicated database](https://redis.io/docs/latest/operate/rs/databases/active-active/create) for instructions."},{"id":"run-two-containers","title":"Run two containers","role":"content","text":"To spin up two Redis Software containers, run these commands:\n\n[code example]\n\n[code example]\n\n> [!NOTE]\n> The `-h` option sets the hostname of the container, which is important for cluster setup and identification.\n>\n> The `--name` option assigns a name to the container, making it easier to manage and reference with Docker commands.\n>\n> The `-p` options map the Cluster Manager UI port (8443), REST API port (9443), and database access port differently for each container to make sure that all containers can be accessed from the host OS that is running the containers."},{"id":"set-up-two-clusters","title":"Set up two clusters","role":"content","text":"1. For cluster 1, go to `https://localhost:8443` in a browser on the\nhost machine to access the Redis Software Cluster Manager UI.\n\n    > [!NOTE]\n    > Depending on your browser, you may see a certificate error. Continue to the website.\n    >\n\n1. Click **Create new cluster**:\n\n    ![When you first install Redis Software, you need to set up a cluster.](https://redis.io/docs/latest/images/rs/screenshots/cluster/setup/create-cluster.png)\n\n1. Enter an email and password for the administrator account, then click **Next** to proceed to cluster setup:\n\n    ![Set the credentials for your admin user.](https://redis.io/docs/latest/images/rs/screenshots/cluster/setup/admin-credentials.png)\n\n1. Enter your cluster license key if you have one. Otherwise, a trial version is installed.\n\n    ![Enter your cluster license key if you have one.](https://redis.io/docs/latest/images/rs/screenshots/cluster/setup/cluster-license-key.png)\n\n1. In the **Configuration** section of the **Cluster** settings page, enter a cluster FQDN, for example `cluster1.local`:\n\n    ![Configure the cluster FQDN.](https://redis.io/docs/latest/images/rs/screenshots/cluster/setup/config-cluster1.png)\n\n1. On the node setup screen, keep the default settings and click **Create cluster**:\n\n    ![Configure the node specific settings.](https://redis.io/docs/latest/images/rs/screenshots/cluster/setup/node-settings.png)\n\n1. Click **OK** to confirm that you are aware of the replacement of the HTTPS SSL/TLS\n    certificate on the node, and proceed through the browser warning.\n\n1. Repeat the previous steps for cluster 2 with these differences:\n\n    - In your web browser, go to `https://localhost:8445` to set up the cluster 2.\n\n    - For the **Cluster name (FQDN)**, enter a different name, such as `cluster2.local`.\n\nNow you have two Redis Software clusters with FQDNs\n`cluster1.local` and `cluster2.local`.\n\n> [!NOTE]\n> Each Active-Active instance must have a unique fully-qualified domain name (FQDN)."},{"id":"create-an-active-active-database","title":"Create an Active-Active database","role":"content","text":"1. Sign in to cluster1.local's Cluster Manager UI at `https://localhost:8443`\n\n1. Open the **Create database** menu with one of the following methods:\n\n    - Click the **+** button next to **Databases** in the navigation menu:\n\n        ![Create database menu has two options: Single Region and Active-Active database.](https://redis.io/docs/latest/images/rs/screenshots/databases/create-db-plus-drop-down.png)\n        \n    - Go to the **Databases** screen and select **Create database**:\n\n        ![Create database menu has two options: Single Region and Active-Active database.](https://redis.io/docs/latest/images/rs/screenshots/databases/create-db-button-drop-down.png)\n\n1. Select **Active-Active database**.\n\n1. Enter the cluster's local admin credentials, then click **Save**:\n\n    ![Enter the cluster's admin username and password.](https://redis.io/docs/latest/images/rs/screenshots/databases/active-active-databases/enter-local-admin-credentials-cluster1.png)\n\n1. Add participating clusters that will host instances of the Active-Active database:\n\n    1. In the **Participating clusters** section, go to **Other participating clusters** and click **+ Add cluster**.\n\n    1. In the **Add cluster** configuration panel, enter the new cluster's URL, port number, and the admin username and password for the new participating cluster:\n\n        In the **Other participating clusters** list, add the address and admin credentials for the other cluster: `https://cluster2.local:9443`\n\n        ![Add cluster panel.](https://redis.io/docs/latest/images/rs/screenshots/databases/active-active-databases/participating-clusters-add-cluster2.png)\n\n    1. Click **Join cluster** to add the cluster to the list of participating clusters. \n\n1. Enter `database1` for **Database name** and `12000` for **Port**:\n\n    ![Database name and port text boxes.](https://redis.io/docs/latest/images/rs/screenshots/databases/active-active-databases/quickstart-db-name-port.png)\n\n1. Configure additional settings:\n\n    1. In the **High availability** section, turn off **Replication** since each cluster has only one node in this setup:\n\n        ![Turn off replication in the High availability section.](https://redis.io/docs/latest/images/rs/screenshots/databases/active-active-databases/quickstart-ha-turn-off-replication-7-8-2.png)\n\n\n    1. In the **Clustering** section, either:\n\n        - Make sure that **Sharding** is enabled and select the number of shards you want to have in the database. When database clustering is enabled,\n        databases are subject to limitations on [Multi-key commands](https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering).\n        You can increase the number of shards in the database at any time.\n\n        - Turn off **Sharding** to use only one shard and avoid [Multi-key command](https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering) limitations.\n\n        > [!NOTE]\n        > You cannot enable or turn off database clustering after the Active-Active database is created.\n        >\n\n1. Click **Create**.\n\n    > [!NOTE]\n    > If you cannot activate the database because of a memory limitation,\nmake sure that Docker has at least 4 GB of memory allocated in the **Advanced** section of Docker **Settings**.\n\n    >\n\n1. After the Active-Active database is created, sign in to the Cluster Manager UIs for cluster 1 at `https://localhost:8443` and cluster 2 at `https://localhost:8445`.\n\n1. Make sure each cluster has an Active-Active database member database with the name `database1`.\n\n    In a real-world deployment, cluster 1 and cluster 2 would most likely be\n    in separate data centers in different regions. However, for\n    local testing we created the scale-minimized deployment using two\n    local clusters running on the same host."},{"id":"test-connection","title":"Test connection","role":"content","text":"With the Redis database created, you are ready to connect to your\ndatabase. See [Connect to Active-Active databases](https://redis.io/docs/latest/operate/rs/databases/active-active/connect) for tutorials and examples of multiple connection methods."}],"examples":[{"id":"run-two-containers-ex0","language":"sh","code":"docker run -d --cap-add sys_resource -h rs1_node1 --name rs1_node1 -p 8443:8443 -p 9443:9443 -p 12000:12000 redislabs/redis","section_id":"run-two-containers"},{"id":"run-two-containers-ex1","language":"sh","code":"docker run -d --cap-add sys_resource -h rs2_node1 --name rs2_node1 -p 8445:8443 -p 9445:9443 -p 12002:12000 redislabs/redis","section_id":"run-two-containers"}]}
{"schema_version":2,"id":"operate/rs/databases/active-active","title":"Active-Active geo-distributed Redis","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/","summary":"Overview of the Active-Active database in Redis Software","tags":["docs","operate","rs","kubernetes"],"last_updated":"2026-09-24T09:16:24+01:00","children":[{"id":"operate/rs/databases/active-active/get-started","summary":"Quick start guide to create an Active-Active database for test and development.","title":"Get started with Redis Software Active-Active databases","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/get-started/"},{"id":"operate/rs/databases/active-active/planning","summary":"Information about Active-Active database to take into consideration while planning a deployment, such as compatibility, limitations, and special configuration","title":"Considerations for planning Active-Active databases","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/planning/"},{"id":"operate/rs/databases/active-active/create","summary":"How to create an Active-Active database and things to consider when setting it up.","title":"Create an Active-Active geo-replicated database","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/create/"},{"id":"operate/rs/databases/active-active/connect","summary":"How to connect to an Active-Active database using redis-cli or a sample Python application.","title":"Connect to your Active-Active databases","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/connect/"},{"id":"operate/rs/databases/active-active/manage","summary":"Manage your Active-Active database settings.","title":"Manage Active-Active databases","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/manage/"},{"id":"operate/rs/databases/active-active/delete","summary":"Considerations while deleting Active-Active databases.","title":"Delete Active-Active databases","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/delete/"},{"id":"operate/rs/databases/active-active/disaster-recovery","summary":"Disaster recovery strategies for Active-Active databases using network, proxy, client library, and application-based approaches.","title":"Disaster recovery strategies for Active-Active databases","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/disaster-recovery/"},{"id":"operate/rs/databases/active-active/causal-consistency","summary":"Enable causal consistency in an Active-Active database.","title":"Enable causal consistency","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/causal-consistency/"},{"id":"operate/rs/databases/active-active/synchronization-mode","summary":"How to configure distributed synchronization so that any available proxy endpoint can manage synchronization traffic.","title":"Configure distributed synchronization","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/synchronization-mode/"},{"id":"operate/rs/databases/active-active/syncer","summary":"Detailed information about the syncer process and its role in distributed databases.","title":"Syncer process","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/syncer/"},{"id":"operate/rs/databases/active-active/develop","summary":"General information to keep in mind while developing applications for an Active-Active database.","title":"Active-Active Redis applications","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/develop/"}],"page_type":"content","content_hash":"f297f3983a1b6c25f4f2afb00b064860d3dd585f8e11c89cf085ce91b3ff2e3e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"In Redis Software, Active-Active geo-distribution is based on [CRDT technology](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type).\nThe Redis Software implementation of CRDT is called an Active-Active database (formerly known as CRDB).\nWith Active-Active databases, applications can read and write to the same data set from different geographical locations seamlessly and with latency less than one millisecond (ms),\nwithout changing the way the application connects to the database.\n\nActive-Active databases also provide disaster recovery and accelerated data read-access for geographically distributed users."},{"id":"high-availability","title":"High availability","role":"content","text":"The [high availability](https://redis.io/docs/latest/operate/rs/databases/durability-ha) that Active-Active replication provides is built upon a number of Redis Software features (such as [clustering](https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering), [replication](https://redis.io/docs/latest/operate/rs/databases/durability-ha/replication), and [replica HA](https://redis.io/docs/latest/operate/rs/databases/configure/replica-ha)) as well as some features unique to Active-Active ([multi-primary replication](#multi-primary-replication/), [automatic conflict resolution](#conflict-resolution/), and [strong eventual consistency](#strong-eventual-consistency/)).\n\nClustering and replication are used together in Active-Active databases to distribute multiple copies of the dataset across multiple nodes and multiple clusters. As a result, a node or cluster is less likely to become a single point of failure. If a primary node or primary shard fails, a replica is automatically promoted to primary. To avoid having one node hold all copies of certain data, the [replica HA](https://redis.io/docs/latest/operate/rs/databases/configure/replica-ha) feature (enabled by default) automatically migrates replica shards to available nodes."},{"id":"multi-primary-replication","title":"Multi-primary replication","role":"content","text":"In Redis Software, replication copies data from primary shards to replica shards. Active-Active geo-distributed replication also copies both primary and replica shards to other clusters. Each Active-Active database needs to span at least two clusters; these are called participating clusters.\n\nEach participating cluster hosts an instance of your database, and each instance has its own primary node. Having multiple primary nodes means you can connect to the proxy in any of your participating clusters. Connecting to the closest cluster geographically enables near-local latency. Multi-primary replication (previously referred to as multi-master replication) also means that your users still have access to the database if one of the participating clusters fails.\n\n> [!NOTE]\n> Active-Active databases do not replicate the entire database, only the data.\n> Database configurations, LUA scripts, and other support info are not replicated."},{"id":"syncer","title":"Syncer","role":"content","text":"Keeping multiple copies of the dataset consistent across multiple clusters is no small task. To achieve consistency between participating clusters, Redis Active-Active replication uses a process called the [syncer](https://redis.io/docs/latest/operate/rs/databases/active-active/syncer). \n\nThe syncer keeps a [replication backlog](https://redis.io/docs/latest/operate/rs/databases/active-active/manage#replication-backlog/), which stores changes to the dataset that the syncer sends to other participating clusters. The syncer uses partial syncs to keep replicas up to date with changes, or a full sync in the event a replica or primary is lost."},{"id":"conflict-resolution","title":"Conflict resolution","role":"content","text":"Because you can connect to any participating cluster to perform a write operation, concurrent and conflicting writes are always possible. Conflict resolution is an important part of the Active-Active technology. Active-Active databases only use [conflict-free replicated data types (CRDTs)](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type). These data types provide a predictable conflict resolution and don't require any additional work from the application or client side.\n\nWhen developing with CRDTs for Active-Active databases, you need to consider some important differences. See [Develop applications with Active-Active databases](https://redis.io/docs/latest/operate/rs/databases/active-active/develop) for related information."},{"id":"strong-eventual-consistency","title":"Strong eventual consistency","role":"content","text":"Maintaining strong consistency for replicated databases comes with tradeoffs in scalability and availability. Redis Active-Active databases use a strong eventual consistency model, which means that local values may differ across replicas for short periods of time, but they all eventually converge to one consistent state. Redis uses vector clocks and the CRDT conflict resolution to strengthen consistency between replicas. You can also enable the causal consistency feature to preserve the order of operations as they are synchronized among replicas.\n\nOther Redis Software features can also be used to enhance the performance, scalability, or durability of your Active-Active database. These include [data persistence](https://redis.io/docs/latest/operate/rs/databases/configure/database-persistence), [multiple active proxies](https://redis.io/docs/latest/operate/rs/databases/configure/proxy-policy), [distributed synchronization](https://redis.io/docs/latest/operate/rs/databases/active-active/synchronization-mode), [OSS Cluster API](https://redis.io/docs/latest/operate/rs/databases/configure/oss-cluster-api), and [rack-zone awareness](https://redis.io/docs/latest/operate/rs/clusters/configure/rack-zone-awareness)."},{"id":"next-steps","title":"Next steps","role":"content","text":"- [Plan your Active-Active deployment](https://redis.io/docs/latest/operate/rs/databases/active-active/planning)\n- [Get started with Active-Active](https://redis.io/docs/latest/operate/rs/databases/active-active/get-started)\n- [Create an Active-Active database](https://redis.io/docs/latest/operate/rs/databases/active-active/create)\n- [Develop applications with Active-Active databases](https://redis.io/docs/latest/operate/rs/databases/active-active/develop/develop-for-aa)\n- Review [disaster recovery strategies for Active-Active databases](https://redis.io/docs/latest/operate/rs/databases/active-active/disaster-recovery)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/databases/active-active/manage","title":"Manage Active-Active databases","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/manage/","summary":"Manage your Active-Active database settings.","tags":["docs","operate","rs"],"last_updated":"2026-10-02T16:06:07+01:00","page_type":"content","content_hash":"33218c46499ef8b7e5a02f3e24dc4929629038d9027c93ca90eeec96ff7b69fc","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can configure and manage your Active-Active database from either the Cluster Manager UI or the command line."},{"id":"database-settings","title":"Database settings","role":"content","text":"Most Active-Active database settings can be changed after database creation. One notable exception is database clustering, which can't be turned on or off after the database has been created.\n\nAs of Redis Software version 8.0.16, the Cluster Manager UI supports both [global](#change-global-configuration) and [local](#change-local-configuration) configuration changes for Active-Active databases. In earlier versions, configuration changes made in the Cluster Manager UI applied only to the local instance and required additional manual updates for each participating cluster."},{"id":"change-global-configuration","title":"Change global configuration","role":"content","text":"Global configuration changes are applied to all participating clusters in the Active-Active database. However, if your Active-Active mesh contains local configurations, they will continue to override global settings on the database instances where they are applied.\n\n> [!WARNING]\n> Applying global configuration changes will override any local settings created using the API or earlier Cluster Manager UI versions. To keep local changes, manage them in the [**Local** configuration tab](#change-local-configuration).\n\n<br />\n\n**Cluster Manager UI:**\n\nTo make global configuration changes in the Cluster Manager UI:\n\n1. Select the Active-Active database from the **Databases** list and go to its **Configuration** screen.\n\n1. While on the **Global** tab, click **Edit**.\n\n    ![The global configuration tab is selected.](https://redis.io/docs/latest/images/rs/screenshots/databases/active-active-databases/global-config-tab.png)\n\n1. Make your configuration changes.\n\n1. Click **Save** to apply the changes globally to all participating clusters.\n\n**crdb-cli:**\n\nTo change the global configuration from the command line, use [`crdb-cli crdb update`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/update):\n\n[code example]\n\nReplace the placeholders `<placeholder>` with your own values."},{"id":"change-local-configuration","title":"Change local configuration","role":"content","text":"Local configuration changes override global configuration settings and are applied only to the database instance on the current cluster.\n\nTo apply changes to a local Active-Active database instance, use one of the following methods:\n\n**Cluster Manager UI:**\n\nTo change the local configuration in the Cluster Manager UI:\n\n1. Select the Active-Active database from the **Databases** list and go to its **Configuration** screen.\n\n1. Go to the **Local** tab.\n\n    ![The local configuration tab is selected.](https://redis.io/docs/latest/images/rs/screenshots/databases/active-active-databases/local-config-tab.png)\n\n1. Click **Edit**, then make your configuration changes.\n\n1. Click **Save** to apply the changes to the current database instance only.\n\n**rladmin:**\n\nTo change the local configuration from the command line, use [`rladmin tune db`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-db):\n\n[code example]\n\nReplace the placeholders `<placeholder>` with your own values."},{"id":"view-differences-between-global-and-local-configuration","title":"View differences between global and local configuration","role":"content","text":"In the Cluster Manager UI, an Active-Active database's **Global** configuration tab indicates when a local configuration differs from the global configuration:\n\n![On the global configuration tab, a warning is visible that says: 'This Active Active mesh contains local configurations which will not be affected by the global settings'.](https://redis.io/docs/latest/images/rs/screenshots/databases/active-active-databases/local-config-warning.png)\n\nIf a warning symbol appears next to a setting on the **Global** configuration tab, point to the warning symbol to show details about the local configuration differences:\n\n![An example that shows memory eviction is set to noeviction globally but allkeys-lru locally.](https://redis.io/docs/latest/images/rs/screenshots/databases/active-active-databases/local-config-warning-details.png)\n\nOn the **Local** configuration tab, any locally configured settings that differ from the global settings are marked with a **Local configuration** label:\n\n![A local configuration label appears next to Memory eviction allkeys-lru.](https://redis.io/docs/latest/images/rs/screenshots/databases/active-active-databases/local-config-tag.png)"},{"id":"participating-clusters","title":"Participating clusters","role":"content","text":"You can add and remove participating clusters of an Active-Active database to change the topology.\nTo manage the changes to Active-Active topology, use [`crdb-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli) or the participating clusters list in the Cluster Manager UI."},{"id":"add-participating-clusters","title":"Add participating clusters","role":"content","text":"All existing participating clusters must be online and in a syncing state when you add new participating clusters.\n\nNew participating clusters create the Active-Active database instance based on the global Active-Active database configuration.\nAfter you add new participating clusters to an existing Active-Active database,\nthe new database instance can accept connections and read operations.\nThe new instance does not accept write operations until it is in the syncing state.\n\n> [!NOTE]\n> If an Active-Active database [runs on flash memory](https://redis.io/docs/latest/operate/rs/databases/flash), you cannot add participating clusters that run on RAM only.\n\nTo add a new participating cluster to an existing Active-Active configuration using the Cluster Manager UI:\n\n1. Select the Active-Active database from the **Databases** list and go to its **Configuration** screen.\n\n1. Click **Edit**.\n\n1. In the **Participating clusters** section, go to **Other participating clusters** and click **+ Add cluster**.\n\n1. In the **Add cluster** configuration panel, enter the new cluster's URL, port number, and the admin username and password for the new participating cluster:\n\n    ![Add cluster panel.](https://redis.io/docs/latest/images/rs/screenshots/databases/active-active-databases/participating-clusters-add-cluster.png)\n\n1. Click **Join cluster** to add the cluster to the list of participating clusters. \n\n1. Click **Save**."},{"id":"remove-participating-clusters","title":"Remove participating clusters","role":"content","text":"All existing participating clusters must be online and in a syncing state when you remove an online participating cluster.\nIf you must remove offline participating clusters, you can forcefully remove them.\nIf a forcefully removed participating cluster tries to rejoin the cluster,\nits Active-Active database membership will be out of date.\nThe joined participating clusters reject updates sent from the removed participating cluster.\nTo prevent rejoin attempts, purge the forcefully removed instance from the participating cluster.\n\nTo remove a participating cluster using the Cluster Manager UI:\n\n1. Select the Active-Active database from the **Databases** list and go to its **Configuration** screen.\n\n1. Click **Edit**.\n\n1. In the **Participating clusters** section, point to the cluster you want to delete in the **Other participating clusters** list:\n\n    ![Edit and delete buttons appear when you point to an entry in the Other participating clusters list.](https://redis.io/docs/latest/images/rs/screenshots/databases/active-active-databases/participating-clusters-edit-delete.png)\n\n1. Click ![The Delete button](https://redis.io/docs/latest/images/rs/buttons/delete-button.png#no-click) to remove the cluster.\n\n1. Click **Save**."},{"id":"replication-backlog","title":"Replication backlog","role":"content","text":"Redis databases that use [replication for high availability](https://redis.io/docs/latest/operate/rs/databases/durability-ha/replication) maintain a replication backlog (per shard) to synchronize the primary and replica shards of a database. In addition to the database replication backlog, Active-Active databases maintain a backlog (per shard) to synchronize the database instances between clusters.\n\nBy default, both the database and Active-Active replication backlogs are set to one percent (1%) of the database size divided by the number of shards. This can range between 1MB to 250MB per shard for each backlog."},{"id":"change-the-replication-backlog-size","title":"Change the replication backlog size","role":"content","text":"Use the [`crdb-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli) utility to control the size of the replication backlogs. You can set it to `auto` or set a specific size.  \n\nUpdate the database replication backlog configuration with the `crdb-cli` command shown below.\n\n[code example]\n\nUpdate the Active-Active (CRDT) replication backlog with the command shown below: \n\n[code example]"},{"id":"data-persistence","title":"Data persistence","role":"content","text":"Active-Active supports AOF (Append-Only File) data persistence only.  Snapshot persistence is _not_ supported for Active-Active databases and should not be used.\n\nIf an Active-Active database is currently using snapshot data persistence, use `crdb-cli` to switch to AOF persistence:\n[code example]"}],"examples":[{"id":"change-global-configuration-ex0","language":"sh","code":"crdb-cli crdb update --crdb-guid <guid> --<setting-name> <setting-value>","section_id":"change-global-configuration"},{"id":"change-local-configuration-ex0","language":"sh","code":"rladmin tune db { db:<id> | <name> } <setting-name> <setting-value>","section_id":"change-local-configuration"},{"id":"change-the-replication-backlog-size-ex0","language":"text","code":"crdb-cli crdb update --crdb-guid <crdb_guid> --default-db-config \"{\\\"repl_backlog_size\\\": <size in MB | 'auto'>}\"","section_id":"change-the-replication-backlog-size"},{"id":"change-the-replication-backlog-size-ex1","language":"text","code":"crdb-cli crdb update --crdb-guid <crdb_guid> --default-db-config \"{\\\"crdt_repl_backlog_size\\\": <size in MB | 'auto'>}\"","section_id":"change-the-replication-backlog-size"},{"id":"data-persistence-ex0","language":"text","code":"crdb-cli crdb update --crdb-guid <CRDB_GUID> --default-db-config '{\"data_persistence\": \"aof\", \"aof_policy\":\"appendfsync-every-sec\"}'","section_id":"data-persistence"}]}
{"schema_version":2,"id":"operate/rs/databases/active-active/planning","title":"Considerations for planning Active-Active databases","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/planning/","summary":"Information about Active-Active database to take into consideration while planning a deployment, such as compatibility, limitations, and special configuration","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:24+01:00","page_type":"content","content_hash":"c3c76cab62a3e81ffef734e5e8a3856ef578707fac25122e39548abffaeea754","sections":[{"id":"overview","title":"Overview","role":"overview","text":"In Redis Software, Active-Active geo-distribution is based on [conflict-free replicated data type (CRDT) technology](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type). Compared to databases without geo-distribution, Active-Active databases have more complex replication and networking, as well as a different data type.\n\nBecause of the complexities of Active-Active databases, there are special considerations to keep in mind while planning your Active-Active database.\n\nSee [Active-Active Redis](https://redis.io/docs/latest/operate/rs/databases/active-active) for more information about geo-distributed replication. For more info on other high availability features, see [Durability and high availability](https://redis.io/docs/latest/operate/rs/databases/durability-ha)."},{"id":"participating-clusters","title":"Participating clusters","role":"content","text":"You need at least [two participating clusters](https://redis.io/docs/latest/operate/rs/clusters/new-cluster-setup) for an Active-Active database. If your database requires more than ten participating clusters, contact Redis support. You can [add or remove participating clusters](https://redis.io/docs/latest/operate/rs/databases/active-active/manage#participating-clusters/) after database creation.\n\n> [!NOTE]\n> If an Active-Active database [runs on flash memory](https://redis.io/docs/latest/operate/rs/databases/flash), you cannot add participating clusters that run on RAM only.\n\nFor Redis Software versions earlier than 8.0.16, changes made from the Cluster Manager UI to an Active-Active database configuration only apply to the cluster you are editing. For global configuration changes across all clusters, use the `crdb-cli` command-line utility. As of Redis Software version 8.0.16, the Cluster Manager UI supports both global and local configuration changes for Active-Active databases."},{"id":"memory-limits","title":"Memory limits","role":"content","text":"Database memory limits define the maximum size of your database across all database replicas and [shards](https://redis.io/docs/latest/operate/rs/references/terminology#redis-instance-shard) on the cluster. Your memory limit also determines the number of shards.\n\nBesides your dataset, the memory limit must also account for replication, Active-Active metadata, and module overhead. These features can increase your database size, sometimes increasing it by two times or more.\n\nFactors to consider when sizing your database:\n\n- **dataset size**: you want your limit to be above your dataset size to leave room for overhead.\n- **database throughput**: high throughput needs more shards, leading to a higher memory limit.\n- [**modules**](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise): using modules with your database can consume more memory.\n- [**database clustering**](https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering): enables you to spread your data into shards across multiple nodes (scale out).\n- [**database replication**](https://redis.io/docs/latest/operate/rs/databases/durability-ha/replication): enabling replication doubles memory consumption\n- [**Active-Active replication**](https://redis.io/docs/latest/operate/rs/databases/active-active): enabling Active-Active replication requires double the memory of regular replication, which can be up to two times (2x) the original data size per instance.\n- [**database replication backlog**](https://redis.io/docs/latest/operate/rs/databases/active-active/manage#replication-backlog/) for synchronization between shards. By default, this is set to 1% of the database size.\n- [**Active-Active replication backlog**](https://redis.io/docs/latest/operate/rs/databases/active-active/manage) for synchronization between clusters. By default, this is set to 1% of the database size.\n\nIt's also important to know Active-Active databases have a lower threshold for activating the eviction policy, because it requires propagation to all participating clusters. The eviction policy starts to evict keys when one of the Active-Active instances reaches 80% of its memory limit. \n\nFor more information on memory limits, see [Memory and performance](https://redis.io/docs/latest/operate/rs/databases/memory-performance) or [Database memory limits](https://redis.io/docs/latest/operate/rs/databases/memory-performance/memory-limit)."},{"id":"replication-oom-protection","title":"Replication OOM protection","role":"content","text":"When a shard in an Active-Active database reaches an out-of-memory (OOM) condition:\n\n1. Replication between that shard and its peers stops immediately.\n\n1. The syncer process sends commands to the affected shard to trigger garbage collection and free memory.\n\nIf the database has no [eviction policy](https://redis.io/docs/latest/operate/rs/databases/memory-performance/eviction-policy) and no keys with [expiration times (TTL)](https://redis.io/docs/latest/develop/using-commands/keyspace#key-expiration), no memory can be freed, which can lead to persistent replication failure and data desynchronization.\n\nTo reduce this risk, Active-Active databases running Redis version 8.4 or later support a configurable memory buffer through the `replication_oom_threshold_percent` setting. This setting reserves a percentage of memory below `maxmemory` for internal replication operations.\n\nThe `replication_oom_threshold_percent` setting works as follows:\n\n- If memory usage is below the threshold, all client writes proceed normally.\n\n- If memory usage exceeds the threshold, Redis blocks external client write commands with an out-of-memory error, but internal replication and garbage collection continue in the reserved buffer.\n\n- If memory reaches `maxmemory` despite the client block, the standard out-of-memory behavior applies to all operations, including replication.\n\n`replication_oom_threshold_percent` defaults to `5`, which means 5% of `maxmemory` is reserved. To adjust the reserved percentage in all participating clusters, use an [update Active-Active database configuration](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/crdbs#patch-crdbs) REST API request:\n\n[code example]"},{"id":"networking","title":"Networking","role":"content","text":"Network requirements for Active-Active databases include:\n\n- A VPN between each network that hosts a cluster with an instance (if your database spans WAN).\n- A network connection to [several ports](#network-ports) on each cluster from all nodes in all participating clusters.\n- A [network time service](#network-time-service) running on each node in all clusters.\n\nNetworking between the clusters must be configured before creating an Active-Active database. The setup will fail if there is no connectivity between the clusters."},{"id":"network-ports","title":"Network ports","role":"content","text":"Every node must have access to the REST API ports of every other node as well as other ports for proxies, VPNs, and the Cluster Manager UI. See [Network port configurations](https://redis.io/docs/latest/operate/rs/networking/port-configurations) for more details. These ports should be allowed through firewalls that may be positioned between the clusters."},{"id":"network-time-service","title":"Network Time Service","role":"content","text":"Active-Active databases require a time service like NTP or Chrony to make sure the clocks on all cluster nodes are synchronized.\nThis is critical to avoid problems with internal cluster communications that can impact your data integrity.\n\nSee [Synchronizing cluster node clocks](https://redis.io/docs/latest/operate/rs/clusters/configure/sync-clocks) for more information."},{"id":"data-compression","title":"Data compression","role":"content","text":"Active-Active databases replicate data between participating clusters over the network. When clusters are geographically distributed, compressing the replicated data can:\n\n- Reduce network traffic.\n\n- Resolve throughput issues.\n\n- Reduce network traffic costs.\n\nThe compression level is an integer between 0 and 6 where:\n\n- `0` turns off compression. This compression level is recommended only when throughput is low, because compression can lead to high lag in such scenarios.\n\n- `6` provides the highest compression but uses the most resources.\n\n- The default compression level is `3`.\n\nTo change the compression level, use the `--compression` option when you [create](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/create) or [update](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/update) an Active-Active database with [`crdb-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli)."},{"id":"redis-modules","title":"Redis modules","role":"content","text":"Several Redis modules are compatible with Active-Active databases. Find the list of [compatible Redis modules](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/enterprise-capabilities).\n\nActive-Active databases created with or upgraded to Redis version 8 or later automatically enable [Redis Search](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/search-active-active) and [JSON](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json), which allows you to index, query, and perform full-text searches of nested JSON documents."},{"id":"limitations","title":"Limitations","role":"limits","text":"Active-Active databases have the following limitations:\n\n- An existing database can't be changed into an Active-Active database. To move data from an existing database to an Active-Active database, you must [create a new Active-Active database](https://redis.io/docs/latest/operate/rs/databases/active-active/create) and [migrate the data](https://redis.io/docs/latest/operate/rs/databases/import-export/migrate-to-active-active).\n- [Discovery service](https://redis.io/docs/latest/operate/rs/databases/durability-ha/discovery-service) is not supported with Active-Active databases. Active-Active databases require FQDNs or [mDNS](https://redis.io/docs/latest/operate/rs/networking/mdns).\n- The `FLUSH` command is not supported from the CLI. To flush your database, use the API or Cluster Manager UI.\n- The `UNLINK` command is a blocking command for all types of keys.\n- Cross slot multi commands (such as `MSET`) are not supported with Active-Active databases.\n- The hashing policy can't be changed after database creation.\n- Database clustering cannot be enabled or turned off after database creation.\n- Active-Active databases cannot be configured as Redis Flex deployments.\n- Active-Active databases handle replication internally and do not support the `redis.set_repl()` function in Lua scripts.\n- If you enabled the default database password during the creation of an Active-Active database, you should not turn off the default database password because it could prevent the removal of participating database instances.\n- When upgrading an Active-Active database from Redis 7.4 or earlier to version 8.0 or later, you cannot use module commands, such as [Redis Search](https://redis.io/docs/latest/commands/?group=search) and [JSON](https://redis.io/docs/latest/commands/?group=json) commands, until all Active-Active database instances in all participating clusters have been upgraded. These commands are not blocked automatically, and running these commands before finishing the upgrade process can cause syncer crashes."}],"examples":[{"id":"replication-oom-protection-ex0","language":"sh","code":"PATCH https://<host>:<port>/v1/crdbs/<crdb_guid>\n{ \n  \"default_db_config\": { \n    \"replication_oom_threshold_percent\": <integer from 0 to 20> \n  }\n}","section_id":"replication-oom-protection"}]}
{"schema_version":2,"id":"operate/rs/databases/active-active/syncer","title":"Syncer process","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/syncer/","summary":"Detailed information about the syncer process and its role in distributed databases.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:24+01:00","page_type":"content","content_hash":"6d6089fe15674e47d44e4f0e2373781151f034733b6e25a787550c51fbf7a1cc","sections":[{"id":"syncer-process","title":"Syncer process","role":"overview","text":"Each node in a cluster containing an instance of an Active-Active database hosts a process called the syncer.\nThe syncer process:\n\n1. Connects to the proxy on another participating cluster\n1. Reads data from that database instance\n1. Writes the data to the local cluster's primary(master) shard\n\nSome replication capabilities are also included in [Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack/management/replication).\n\nThe primary (also known as master) shard at the top of the primary-replica tree creates a replication ID.\nThis replication ID is identical for all replicas in that tree.\nWhen a new primary is appointed, the replication ID changes, but a partial sync from the previous ID is still possible.\n\n\nIn a partial sync, the backlog of operations since the offset are transferred as raw operations.\nIn a full sync, the data from the primary is transferred to the replica as an RDB file which is followed by a partial sync.\n\nPartial synchronization requires a backlog large enough to store the data operations until connection is restored. See [replication backlog](https://redis.io/docs/latest/operate/rs/databases/active-active/manage#replication-backlog) for more info on changing the replication backlog size."},{"id":"syncer-in-active-active-replication","title":"Syncer in Active-Active replication","role":"content","text":"In the case of an Active-Active database:\n\n- Multiple past replication IDs and offsets are stored to allow for multiple syncs\n- The [Active-Active replication backlog](https://redis.io/docs/latest/operate/rs/databases/active-active/manage#replication-backlog) is also sent to the replica during a full sync.\n\n> [!WARNING]\n> Full sync triggers heavy data transfers between geo-replicated instances of an Active-Active database.\n\nAn Active-Active database uses partial synchronization in the following situations:\n\n- Failover of primary shard to replica shard\n- Restart or crash of replica shard that requires sync from primary\n- Migrate replica shard to another node\n- Migrate primary shard to another node as a replica using failover and replica migration\n- Migrate primary shard and preserve roles using failover, replica migration, and second failover to return shard to primary\n\n> [!NOTE]\n> Synchronization of data from the primary shard to the replica shard is always a full synchronization."},{"id":"troubleshooting-syncer-errors","title":"Troubleshooting syncer errors","role":"errors","text":""},{"id":"unrecoverable-syncer-errors","title":"Unrecoverable syncer errors","role":"content","text":"Some syncer errors are unrecoverable and cause the syncer to exit with exit code 4. When this occurs, the Data Management Controller (DMC) automatically sets the `crdt_sync` or `replica_sync` value to `stopped`.\n\n#### Restart syncer for regular databases\n\nTo restart a regular database's syncer after an unrecoverable error, [update the database configuration](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#put-bdbs) with the REST API to enable `sync`:\n\n\n[code example]\n\n#### Restart syncer for Active-Active databases\n\nTo restart an Active-Active database's syncer after an unrecoverable error, use one of the following methods.\n\n-  For each participating cluster, [update the database configuration](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#put-bdbs) with the REST API to enable `sync`:\n\n   [code example]\n\n- Run [`crdb-cli crdb update`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/update):\n\n   [code example]\n\n> [!NOTE]\n> Replace `<username>`, `<password>`, `<host>`, `<port>`, `<database-id>`, and `<crdb-guid>` with your actual values."}],"examples":[{"id":"unrecoverable-syncer-errors-ex0","language":"sh","code":"curl -v -k -u <username>:<password> -X PUT \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"sync\": \"enabled\"}' \\\n  https://<host>:<port>/v1/bdbs/<database-id>","section_id":"unrecoverable-syncer-errors"},{"id":"unrecoverable-syncer-errors-ex1","language":"sh","code":"curl -v -k -u <username>:<password> -X PUT \\\n     -H \"Content-Type: application/json\" \\\n     -d '{\"sync\": \"enabled\"}' \\\n     https://<host>:<port>/v1/bdbs/<database-id>","section_id":"unrecoverable-syncer-errors"},{"id":"unrecoverable-syncer-errors-ex2","language":"sh","code":"crdb-cli crdb update --crdb-guid <crdb-guid> --force","section_id":"unrecoverable-syncer-errors"}]}
{"schema_version":2,"id":"operate/rs/databases/active-active/synchronization-mode","title":"Configure distributed synchronization","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/synchronization-mode/","summary":"How to configure distributed synchronization so that any available proxy endpoint can manage synchronization traffic.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:24+01:00","page_type":"content","content_hash":"764088c4e4a1714bd5d1b222c93c2474d01bf57813626946694d194a2ade0ff3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Replicated databases, such as [Replica Of](https://redis.io/docs/latest/operate/rs/databases/import-export/replica-of) and [Active-Active](https://redis.io/docs/latest/operate/rs/databases/active-active) databases,\nuse proxy endpoints to synchronize database changes with the databases on other participating clusters.\n\nTo improve the throughput and lower the latency for synchronization traffic,\nyou can configure a replicated database to use distributed synchronization where any available proxy endpoint can manage synchronization traffic.\n\nEvery database by default has one proxy endpoint that manages client and synchronization communication with the database shards,\nand that proxy endpoint is used for database synchronization.\nThis is called centralized synchronization.\n\nTo prepare a database to use distributed synchronization you must first make sure that the database [proxy policy](https://redis.io/docs/latest/operate/rs/databases/configure/proxy-policy)\nis defined so that either each node has a proxy endpoint or each primary (master) shard has a proxy endpoint.\nAfter you have multiple proxies for the database,\nyou can configure the database synchronization to use distributed synchronization."},{"id":"configure-distributed-synchronization","title":"Configure distributed synchronization","role":"configuration","text":"> [!NOTE]\n> You may use the database name in place of `db:<ID>` in the following `rladmin` commands.\n\nTo configure distributed synchronization:\n\n1. To check the proxy policy for the database, run: `rladmin status`\n\n    The output of the status command shows the list of endpoints on the cluster and the proxy policy for the endpoint.\n\n    [code example]\n\n    If the proxy policy (also known as a _role_) is `single`, configure the policy to `all-nodes` or `all-master-shards` according to your needs with the command:\n\n    [code example]\n\n1. To configure the database to use distributed synchronization, run:\n\n    [code example]\n\n    To change back to centralized synchronization, run:\n\n    [code example]"},{"id":"verify-database-synchronization","title":"Verify database synchronization","role":"content","text":"Use `rladmin` to verify a database synchronization role:\n\n[code example]\n\nThe current database role is reported as the `syncer_mode` value:\n\n[code example]"}],"examples":[{"id":"configure-distributed-synchronization-ex0","language":"sh","code":"ENDPOINTS:\n    DB:ID       NAME      ID                        NODE          ROLE                                SSL\n    db:1        db        endpoint:1:1              node:1        all-master-shards                   No","section_id":"configure-distributed-synchronization"},{"id":"configure-distributed-synchronization-ex1","language":"sh","code":"rladmin bind db db:<ID> endpoint <endpoint id> policy <all-master-shards|all-nodes>","section_id":"configure-distributed-synchronization"},{"id":"configure-distributed-synchronization-ex2","language":"sh","code":"rladmin tune db db:<ID> syncer_mode distributed","section_id":"configure-distributed-synchronization"},{"id":"configure-distributed-synchronization-ex3","language":"sh","code":"rladmin tune db db:<ID> syncer_mode centralized","section_id":"configure-distributed-synchronization"},{"id":"verify-database-synchronization-ex0","language":"sh","code":"rladmin info db db:<ID>","section_id":"verify-database-synchronization"},{"id":"verify-database-synchronization-ex1","language":"sh","code":"$ rladmin info db db:<ID>     \ndb:<ID> [<db_name>]:\n  // (Other settings removed) \n  syncer_mode: centralized","section_id":"verify-database-synchronization"}]}
{"schema_version":2,"id":"operate/rs/databases/configure/database-persistence","title":"Configure database persistence","url":"https://redis.io/docs/latest/operate/rs/databases/configure/database-persistence/","summary":"How to configure database persistence with either an append-only file (AOF) or snapshots.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:24+01:00","page_type":"content","content_hash":"6c70f191554b3622f015d7a2e2eca397315681ccf24237d99a536eba03572767","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Data is stored in RAM or a combination of RAM and flash memory ([Redis Flex and Auto Tiering](https://redis.io/docs/latest/operate/rs/databases/flash)), which risks data loss during process or server failures. Redis Software supports multiple methods to persist data to disk on a per-database basis to ensure data durability.\n\nYou can configure [persistence](https://redis.com/redis-enterprise/technology/durable-redis/) during database creation or by editing an existing database. Although the persistence model can be changed dynamically, the switch can take time depending on the database size and the models being switched."},{"id":"configure-database-persistence","title":"Configure database persistence","role":"configuration","text":"You can configure persistence when you [create a database](https://redis.io/docs/latest/operate/rs/databases/create), or you can edit an existing database's configuration:\n\n1. From the **Databases** list, select the database, then select **Configuration**.\n\n1. Select **Edit**.\n\n1. Expand the **Durability** section.\n\n1. For **Persistence**, select an [option](#data-persistence-options) from the list.\n\n1. Select **Save**."},{"id":"data-persistence-options","title":"Data persistence options","role":"content","text":"There are six options for persistence in Redis Software:\n\n|  **Options** | **Description** |\n|  ------ | ------ |\n|  None | Data is not persisted to disk at all. |\n|  Append-only file (AOF) - fsync every write | Data is fsynced to disk with every write. |\n|  Append-only file (AOF) - fsync every 1 sec | Data is fsynced to disk every second. |\n|  Snapshot, every 1 hour | A snapshot of the database is created every hour. |\n|  Snapshot, every 6 hours | A snapshot of the database is created every 6 hours. |\n|  Snapshot, every 12 hours | A snapshot of the database is created every 12 hours. |"},{"id":"select-a-persistence-strategy","title":"Select a persistence strategy","role":"content","text":"When selecting your persistence strategy, you should take into account your tolerance for data loss and performance needs. There will always be tradeoffs between the two.\nThe fsync() system call syncs data from file buffers to disk. You can configure how often Redis performs an fsync() to most effectively make tradeoffs between performance and durability for your use case.\nRedis supports three fsync policies: every write, every second, and disabled.\n\nRedis also allows snapshots through RDB files for persistence. Within Redis Software, you can configure both snapshots and fsync policies.\n\nFor any high availability needs, use replication to further reduce the risk of data loss.\n\n**For use cases where data loss has a high cost:**\n\nAppend-only file (AOF) - fsync every write - Redis Software sets the Redis directive `appendfsyncalways`.  With this policy, Redis will wait for the write and the fsync to complete prior to sending an acknowledgement to the client that the data has written. This introduces the performance overhead of the fsync in addition to the execution of the command. The fsync policy always favors durability over performance and should be used when there is a high cost for data loss.\n\n**For use cases where data loss is tolerable only limitedly:**\n\nAppend-only file (AOF) - fsync every 1 sec - Redis will fsync any newly written data every second. This policy balances performance and durability and should be used when minimal data loss is acceptable in the event of a failure. This is the default Redis policy. This policy could result in between 1 and 2 seconds worth of data loss but on average this will be closer to one second.\n\n> [!NOTE]\n> If you use AOF for persistence, enable replication to improve performance. When both features are enabled for a database, the replica handles persistence, which prevents any performance impact on the master.\n\n**For use cases where data loss is tolerable or recoverable for extended periods of time:**\n\n- Snapshot, every 1 hour - Performs a full backup every hour.\n- Snapshot, every 6 hour - Performs a full backup every 6 hours.\n- Snapshot, every 12 hour - Performs a full backup every 12 hours.\n- None - Does not back up or persist data at all."},{"id":"append-only-file-aof-vs-snapshot-rdb","title":"Append-only file (AOF) vs snapshot (RDB)","role":"content","text":"Now that you know the available options, to assist in making a decision\non which option is right for your use case, here is a table about the\ntwo:\n\n|  **Append-only File (AOF)** | **Snapshot (RDB)** |\n|------------|-----------------|\n|  More resource intensive | Less resource intensive |\n|  Provides better durability (recover the latest point in time) | Less durable |\n|  Slower time to recover (Larger files) | Faster recovery time |\n|  More disk space required (files tend to grow large and require compaction) | Requires less resources (I/O once every several hours and no compaction required) |"},{"id":"active-active-data-persistence","title":"Active-Active data persistence","role":"content","text":"Active-Active databases support AOF persistence only.  Snapshot persistence is not supported for Active-Active databases.\n\nIf an Active-Active database is using snapshot persistence, use `crdb-cli` to switch to AOF persistence:\n\n[code example]"},{"id":"auto-tiering-data-persistence","title":"Auto Tiering data persistence","role":"content","text":"Auto Tiering flash storage is not considered persistent storage.\n\nFlash-based databases are expected to hold larger datasets, and shard repair times can take longer after node failures. To better protect the database against node failures with longer repair times, consider enabling master and replica dual data persistence.\n\nHowever, dual data persistence with replication adds some processor\nand network overhead, especially for cloud configurations\nwith network-attached persistent storage, such as EBS-backed\nvolumes in AWS.\n\nThere may be times when performance is critical for your use case and\nyou don't want to risk data persistence adding latency.\n\nYou can enable or turn off data persistence on the master shards using the\nfollowing `rladmin` command:\n\n[code example]"}],"examples":[{"id":"active-active-data-persistence-ex0","language":"text","code":"crdb-cli crdb update --crdb-guid <CRDB_GUID> --default-db-config \\\n   '{\"data_persistence\": \"aof\", \"aof_policy\":\"appendfsync-every-sec\"}'","section_id":"active-active-data-persistence"},{"id":"auto-tiering-data-persistence-ex0","language":"sh","code":"rladmin tune db <database_ID_or_name> master_persistence <disabled | enabled>","section_id":"auto-tiering-data-persistence"}]}
{"schema_version":2,"id":"operate/rs/databases/configure/db-defaults","title":"Configure database defaults","url":"https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults/","summary":"Cluster-wide policies that determine default settings when creating new databases.","tags":["docs","operate","rs"],"last_updated":"2026-10-02T16:06:07+01:00","page_type":"content","content_hash":"21606ac0ac1d73f27ea23c937f6d7781d02c19df11e0b3ff5e53dbadd22e3b7c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Database defaults are cluster-wide policies that determine default settings when creating new databases."},{"id":"edit-database-defaults","title":"Edit database defaults","role":"content","text":"To edit default database configuration using the Cluster Manager UI:\n\n1. On the **Databases** screen, select ![Toggle actions button](https://redis.io/docs/latest/images/rs/buttons/button-toggle-actions-vertical.png#no-click) to open a list of additional actions.\n\n1. Select **Database defaults**.\n\n1. Configure [database defaults](#db-defaults).\n\n    ![Database defaults configuration panel.](https://redis.io/docs/latest/images/rs/screenshots/databases/db-defaults.png)\n\n1. Select **Save**."},{"id":"db-defaults","title":"Database defaults","role":"content","text":""},{"id":"endpoint-configuration","title":"Endpoint configuration","role":"content","text":"You can choose a predefined endpoint configuration to use the recommended database proxy and shards placement policies for your use case. If you want to set these policies manually instead, select **Custom** endpoint configuration.\n\n| Endpoint configuration | Database proxy | Shards placement | Description |\n|-----------|------------|----------------|------------------|------------|\n| Enterprise clustering | Single | Dense | Sets up a single endpoint that uses DNS to automatically reflect IP address updates after failover or topology changes. |\n| Using a load balancer | All nodes | Sparse | Configure Redis with a load balancer like HAProxy or Nginx for environments without DNS. |\n| Multiple endpoints | All primary shards | Sparse | To set up multiple endpoints, enable **OSS Cluster API** in the database settings and ensure client support. Clients initially connect to the primary node to retrieve the cluster topology, which allows direct connections to individual Redis proxies on each node. |\n| Custom | Single, all primary shards, or all nodes | Dense or sparse | Manually choose default database proxy and shards placement policies. |"},{"id":"database-proxy","title":"Database proxy","role":"content","text":"Redis Software uses [proxies](https://redis.io/docs/latest/operate/rs/references/terminology#proxy) to manage and optimize access to database shards. Each node in the cluster runs a single proxy process, which can be active (receives incoming traffic) or passive (waits for failovers).\n\nYou can configure default [proxy policies](https://redis.io/docs/latest/operate/rs/databases/configure/proxy-policy) to determine which nodes' proxies are active and bound to new databases by default.\n\nTo configure the default database proxy policy using the Cluster Manager UI:\n\n1. [**Edit database defaults**](#edit-database-defaults). \n\n1. Select a predefined [**Endpoint Configuration**](#endpoint-configuration) to use a recommended database proxy policy, or choose **Custom** to set the policy manually. Changing the database proxy default in the Cluster Manager UI affects both sharded and non-sharded proxy policies.\n\n    ![The Database defaults panel lets you select Database proxy and Shards placement if Endpoint Configuration is set to Custom.](https://redis.io/docs/latest/images/rs/screenshots/databases/db-defaults-endpoint-config-custom.png)\n\n#### Non-sharded proxy policy\n\nTo configure the default proxy policy for non-sharded databases, use one of the following methods:\n\n- [rladmin tune cluster](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-cluster): \n    \n    [code example]\n\n- [Update cluster policy](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/policy#put-cluster-policy) REST API request:\n\n    [code example]\n\n#### Sharded proxy policy\n\nTo configure the default proxy policy for sharded databases, use one of the following methods:\n\n- [rladmin tune cluster](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-cluster): \n    \n    [code example]\n\n- [Update cluster policy](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/policy#put-cluster-policy) REST API request:\n\n    [code example]"},{"id":"shards-placement","title":"Shards placement","role":"content","text":"The default [shard placement policy](https://redis.io/docs/latest/operate/rs/databases/memory-performance/shard-placement-policy) determines the distribution of database shards across nodes in the cluster.\n\nShard placement policies include:\n\n- `dense`: places shards on the smallest number of nodes.\n\n- `sparse`: spreads shards across many nodes.\n\nTo configure default shard placement, use one of the following methods:\n\n- Cluster Manager UI:\n\n    1. [**Edit database defaults**](#edit-database-defaults). \n\n    1. Select a predefined [**Endpoint Configuration**](#endpoint-configuration) to use a recommended shards placement policy, or choose **Custom** to set the policy manually.\n\n        ![The Database defaults panel lets you select Database proxy and Shards placement if Endpoint Configuration is set to Custom.](https://redis.io/docs/latest/images/rs/screenshots/databases/db-defaults-endpoint-config-custom.png)\n\n- [rladmin tune cluster](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-cluster): \n    \n    [code example]\n\n- [Update cluster policy](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/policy#put-cluster-policy) REST API request:\n\n    [code example]"},{"id":"database-version","title":"Database version","role":"content","text":"New databases use the default Redis database version unless you select a different **Database version** when you [create a database](https://redis.io/docs/latest/operate/rs/databases/create) in the Cluster Manager UI or specify the `redis_version` in a [create database REST API request](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs).\n\nTo configure the Redis database version, use one of the following methods:\n\n- Cluster Manager UI: Edit **Database version** in [**Database defaults**](#edit-database-defaults)\n\n\n- [rladmin tune cluster](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-cluster): \n    \n    [code example]\n\n- [Update cluster policy](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/policy#put-cluster-policy) REST API request:\n\n    [code example]"},{"id":"internode-encryption","title":"Internode encryption","role":"content","text":"Enable [internode encryption](https://redis.io/docs/latest/operate/rs/security/encryption/internode-encryption) to encrypt data in transit between nodes for new databases by default.\n\nTo enable or turn off internode encryption by default, use one of the following methods:\n\n- Cluster Manager UI: Edit **Internode Encryption** in [**Database defaults**](#edit-database-defaults)\n\n- [rladmin tune cluster](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-cluster): \n    \n    [code example]\n\n- [Update cluster policy](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/policy#put-cluster-policy) REST API request:\n\n    [code example]"}],"examples":[{"id":"database-proxy-ex0","language":"sh","code":"rladmin tune cluster default_non_sharded_proxy_policy { single | all-master-shards | all-nodes }","section_id":"database-proxy"},{"id":"database-proxy-ex1","language":"sh","code":"PUT /v1/cluster/policy \n    { \"default_non_sharded_proxy_policy\": \"single | all-master-shards | all-nodes\" }","section_id":"database-proxy"},{"id":"database-proxy-ex2","language":"sh","code":"rladmin tune cluster default_sharded_proxy_policy { single | all-master-shards | all-nodes }","section_id":"database-proxy"},{"id":"database-proxy-ex3","language":"sh","code":"PUT /v1/cluster/policy \n    { \"default_sharded_proxy_policy\": \"single | all-master-shards | all-nodes\" }","section_id":"database-proxy"},{"id":"shards-placement-ex0","language":"sh","code":"rladmin tune cluster default_shards_placement { dense | sparse }","section_id":"shards-placement"},{"id":"shards-placement-ex1","language":"sh","code":"PUT /v1/cluster/policy \n    { \"default_shards_placement\": \"dense | sparse\" }","section_id":"shards-placement"},{"id":"database-version-ex0","language":"sh","code":"rladmin tune cluster default_redis_version <x.y>","section_id":"database-version"},{"id":"database-version-ex1","language":"sh","code":"PUT /v1/cluster/policy \n    { \"default_provisioned_redis_version\": \"x.y\" }","section_id":"database-version"},{"id":"internode-encryption-ex0","language":"sh","code":"rladmin tune cluster data_internode_encryption { enabled | disabled }","section_id":"internode-encryption"},{"id":"internode-encryption-ex1","language":"sh","code":"PUT /v1/cluster/policy \n    { \"data_internode_encryption\": <boolean> }","section_id":"internode-encryption"}]}
{"schema_version":2,"id":"operate/rs/databases/configure/db-tags","title":"Manage database tags","url":"https://redis.io/docs/latest/operate/rs/databases/configure/db-tags/","summary":"Manage tags for databases in a Redis Software cluster.","tags":["docs","operate","rs"],"last_updated":"2026-10-02T16:06:07+01:00","page_type":"content","content_hash":"cb558a1439ac753a50fc9f9059200542ca38f72f2c4de1dee6aea3342791216b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can create custom tags to categorize databases in a Redis Software cluster. \n\nThe **Databases** screen shows tags for each database in the list.\n\n![The databases screen includes tags for each database.](https://redis.io/docs/latest/images/rs/screenshots/databases/view-db-list-tags.png)"},{"id":"add-database-tags","title":"Add database tags","role":"content","text":"You can add tags when you [create a database](https://redis.io/docs/latest/operate/rs/databases/create) or [edit an existing database's configuration](https://redis.io/docs/latest/operate/rs/databases/configure#edit-database-settings).\n\nTo add tags to a database using the Cluster Manager UI:\n\n1. While in edit mode on the database's configuration screen, click **Add tags**.\n\n    ![The Add tags button on the database configuration screen.](https://redis.io/docs/latest/images/rs/screenshots/databases/add-db-tags-button.png)\n\n1. Enter a key and value for the tag. Keys and values previously used by existing tags will appear as suggestions.\n\n    ![The Manage tags dialog lets you add, edit, or delete tags.](https://redis.io/docs/latest/images/rs/screenshots/databases/manage-db-tags-dialog-suggestions.png)\n\n1. To add additional tags, click **Add tag**.\n\n1. After you finish adding tags, click **Done** to close the tag manager.\n\n1. Click **Create** or **Save**."},{"id":"edit-database-tags","title":"Edit database tags","role":"content","text":"To edit a database's existing tags using the Cluster Manager UI:\n\n1. Go to the database's **Configuration** screen, then click **Edit**.\n\n1. Next to the existing **Tags**, click ![Edit tags button](https://redis.io/docs/latest/images/rs/buttons/edit-db-tags-button.png#no-click).\n\n    ![The Edit tags button on the database configuration screen.](https://redis.io/docs/latest/images/rs/screenshots/databases/edit-db-tags-button-location.png)\n\n1. Edit or delete existing tags, or click **Add tag** to add new tags.\n\n1. After you finish editing tags, click **Done** to close the tag manager.\n\n1. Click **Save**."},{"id":"tag-validation-rules","title":"Tag validation rules","role":"content","text":"The following rules apply to tags you create or edit.\n\nTag keys and values are preserved exactly as you enter them. Redis Software does not change their casing.\n\nKey rules:\n\n- A key must not be empty or contain only whitespace.\n- A key must be unique on the database. Uniqueness is exact and case-sensitive, so `env` and `Env` are different keys.\n- A key must be at most 64 characters.\n- A key must start with a letter or an underscore and can contain only letters, digits, and underscores.\n- A key must not be a system key. See [System tags](#system-tags).\n\nValid key examples: `env`, `team`, `TEAM`, `_owner`, `app_1`.\n\nInvalid key examples: `1team`, `env.prod`, `team-name`, `region:az`, `with space`, `bad/key`.\n\nValue rules:\n\n- A value must not be empty or contain only whitespace.\n- A value must be at most 128 characters.\n- A value can contain letters, digits, spaces, and the characters `. _ + - @ : /`.\n\nValid value examples: `prod`, `Team A`, `us-east-1`, `service/api`, `owner@example.com`, `v1.2.3`.\n\nCount limit:\n\n- A database can have at most 30 tags that meet these rules. [System tags](#system-tags) and unchanged [legacy tags](#backward-compatibility-for-existing-tags) do not count toward this limit."},{"id":"backward-compatibility-for-existing-tags","title":"Backward compatibility for existing tags","role":"content","text":"The current validation rules were introduced in Redis Software version 8.2.0, when database tags became eligible to be exposed as labels in [v2 metrics](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/prometheus-metrics-v2). In particular, tag keys must follow the [Prometheus label name rules](https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels). Tags created before version 8.2.0 might not meet these rules.\n\nExisting tags that do not meet the current validation rules can remain on a database for backward compatibility, as long as they are unchanged. A tag is considered unchanged only when both its key and its value stay exactly the same.\n\nIf you edit the key or value of such a tag, the edited tag must pass the current validation rules. Unchanged legacy tags that do not meet the rules are not eligible for metrics. To use them in metrics, replace or rename them with valid tags."},{"id":"system-tags","title":"System tags","role":"content","text":"Some tag keys, such as `redis`, `cluster`, and `db`, are reserved for internal use by Redis Software. These are called system tags:\n\n- You cannot add or manage system tags through normal database configuration. These reserved keys are rejected.\n- System tags cannot be exposed in metrics.\n- When you retrieve tags, the response can include an `isSystem` marker that identifies system tags. This marker is computed by Redis Software each time tags are returned. It is not a customer-editable or persistent field, and any `isSystem` value you send when writing tags is ignored. You cannot turn a tag into a system tag by setting `isSystem`.\n- Editing your own tags does not remove system tags that are already stored on the database."},{"id":"use-database-tags-in-metrics","title":"Use database tags in metrics","role":"content","text":"You can expose selected database tags as labels in [v2 metrics](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/prometheus-metrics-v2) through a dedicated `db_tags` metric. This lets you build dashboards, alerts, filters, and ownership views that are grouped by your tags, once the relevant tag keys are enabled in the cluster's metrics configuration.\n\nFor how to enable export, choose which tag keys are exposed, query metrics with `db_tags`, and use tags in observability platforms such as Prometheus, Grafana, Datadog, New Relic, and Dynatrace, see [Database tags in metrics](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/db-tags-in-metrics)."},{"id":"troubleshooting-database-tags","title":"Troubleshooting database tags","role":"errors","text":""},{"id":"why-cant-i-add-a-tag","title":"Why can't I add a tag?","role":"content","text":"Check if your tags adhere to the [validation rules](https://redis.io/docs/latest/operate/rs/databases/configure/db-tags#tag-validation-rules)."},{"id":"why-cant-i-update-the-value-of-some-of-the-existing-tags","title":"Why can't I update the value of some of the existing tags?","role":"content","text":"Those tags are likely [legacy tags](https://redis.io/docs/latest/operate/rs/databases/configure/db-tags#backward-compatibility-for-existing-tags) that no longer meet the current validation rules."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/databases/configure/db-upgrade","title":"Change database upgrade configuration","url":"https://redis.io/docs/latest/operate/rs/databases/configure/db-upgrade/","summary":"Configure cluster-wide policies that affect default database upgrades.","tags":["docs","operate","rs"],"last_updated":"2026-10-02T16:06:07+01:00","page_type":"content","content_hash":"f5d2aacc252ae802652c50992a1cbf80b34a75d3dee437b860b73985deb5721d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Database upgrade configuration includes cluster-wide policies that affect default database upgrades."},{"id":"edit-upgrade-configuration","title":"Edit upgrade configuration","role":"content","text":"To edit database upgrade configuration using the Cluster Manager UI:\n\n1. On the **Databases** screen, select ![Toggle actions button](https://redis.io/docs/latest/images/rs/buttons/button-toggle-actions-vertical.png#no-click) to open a list of additional actions.\n\n1. Select **Upgrade configuration**.\n\n1. Change database [upgrade configuration settings](#upgrade-config-settings).\n\n1. Select **Save**."},{"id":"upgrade-config-settings","title":"Upgrade configuration settings","role":"content","text":""},{"id":"database-shard-parallel-upgrade","title":"Database shard parallel upgrade","role":"content","text":"To change the number of shards upgraded in parallel during database upgrades, use one of the following methods:\n\n- Cluster Manager UI – Edit **Database shard parallel upgrade** in [**Upgrade configuration**](#edit-upgrade-configuration)\n\n- [rladmin tune cluster](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-cluster): \n    \n    [code example]\n\n- [Update cluster policy](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/policy#put-cluster-policy) REST API request:\n\n    [code example]"},{"id":"resp3-support","title":"RESP3 support","role":"content","text":"The cluster-wide option `resp3_default` determines the default value of the `resp3` option, which enables or deactivates RESP3 for a database, upon upgrading a database to version 7.2 or later. `resp3_default` is set to `enabled` by default.\n\nTo change `resp3_default` to `disabled`, use one of the following methods:\n\n- Cluster Manager UI – Edit **RESP3 support** in [**Upgrade configuration**](#edit-upgrade-configuration)\n\n- [rladmin tune cluster](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-cluster): \n    \n    [code example]\n\n- [Update cluster policy](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/policy#put-cluster-policy) REST API request:\n\n    [code example]"}],"examples":[{"id":"database-shard-parallel-upgrade-ex0","language":"sh","code":"rladmin tune cluster parallel_shards_upgrade { all | <integer> }","section_id":"database-shard-parallel-upgrade"},{"id":"database-shard-parallel-upgrade-ex1","language":"sh","code":"PUT /v1/cluster/policy \n    { \"parallel_shards_upgrade\": <integer> }","section_id":"database-shard-parallel-upgrade"},{"id":"resp3-support-ex0","language":"sh","code":"rladmin tune cluster resp3_default { enabled | disabled }","section_id":"resp3-support"},{"id":"resp3-support-ex1","language":"sh","code":"PUT /v1/cluster/policy \n    { \"resp3_default\": <boolean> }","section_id":"resp3-support"}]}
{"schema_version":2,"id":"operate/rs/databases/configure","title":"Configure database settings","url":"https://redis.io/docs/latest/operate/rs/databases/configure/","summary":"Configure settings specific to each database.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:24+01:00","children":[{"id":"operate/rs/databases/configure/db-defaults","summary":"Cluster-wide policies that determine default settings when creating new databases.","title":"Configure database defaults","url":"https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults/"},{"id":"operate/rs/databases/configure/db-upgrade","summary":"Configure cluster-wide policies that affect default database upgrades.","title":"Change database upgrade configuration","url":"https://redis.io/docs/latest/operate/rs/databases/configure/db-upgrade/"},{"id":"operate/rs/databases/configure/db-tags","summary":"Manage tags for databases in a Redis Software cluster.","title":"Manage database tags","url":"https://redis.io/docs/latest/operate/rs/databases/configure/db-tags/"},{"id":"operate/rs/databases/configure/oss-cluster-api","summary":"","title":"Enable OSS Cluster API","url":"https://redis.io/docs/latest/operate/rs/databases/configure/oss-cluster-api/"},{"id":"operate/rs/databases/configure/database-persistence","summary":"How to configure database persistence with either an append-only file (AOF) or snapshots.","title":"Configure database persistence","url":"https://redis.io/docs/latest/operate/rs/databases/configure/database-persistence/"},{"id":"operate/rs/databases/configure/proxy-policy","summary":"","title":"Configure proxy policy","url":"https://redis.io/docs/latest/operate/rs/databases/configure/proxy-policy/"},{"id":"operate/rs/databases/configure/replica-ha","summary":"Configure high availability for replica shards so that the cluster automatically migrates the replica shards to an available node.","title":"Configure high availability for replica shards","url":"https://redis.io/docs/latest/operate/rs/databases/configure/replica-ha/"},{"id":"operate/rs/databases/configure/shard-placement","summary":"Configure shard placement to improve performance.","title":"Configure shard placement","url":"https://redis.io/docs/latest/operate/rs/databases/configure/shard-placement/"}],"page_type":"content","content_hash":"7a9ee0250d6aab293a89bce1d0b36149ca007980b43969684d70b1e4988124c8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can manage your Redis Software databases with several tools:\n\n- [Cluster Manager UI](#edit-database-settings) (the web-based user interface)\n\n- Command-line tools:\n\n    - [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin) for standalone database configuration\n\n    - [`crdb-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli) for Active-Active database configuration\n\n    - [`redis-cli`](https://redis.io/docs/latest/develop/tools/cli) for Redis Open Source configuration\n\n- [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api)"},{"id":"edit-database-settings","title":"Edit database settings","role":"content","text":"You can change the configuration of a Redis Software database at any time.<!--more-->\n\nTo edit the configuration of a database using the Cluster Manager UI:\n\n1. On the **Databases** screen, select the database you want to edit.\n\n1. From the **Configuration** tab, select **Edit**.\n\n1. Change any [configurable database settings](#config-settings).\n\n    > [!NOTE]\n    > For [Active-Active database instances](https://redis.io/docs/latest/operate/rs/databases/active-active), most database settings only apply to the instance that you are editing.\n    >\n\n1. Select **Save**."},{"id":"config-settings","title":"Configuration settings","role":"configuration","text":""},{"id":"general","title":"General","role":"content","text":"- [**Tags**](https://redis.io/docs/latest/operate/rs/databases/configure/db-tags) - Add custom tags to categorize the database.\n\n- **Database version** - Select the Redis version when you create a database.\n\n- **Database name** - The database name requirements are:\n\n    - Maximum of 63 characters\n\n    - Only letters, numbers, or hyphens (-) are valid characters\n\n    - Must start and end with a letter or digit\n\n    - Case-sensitive\n\n- **Endpoint port number** - You can define the port number that clients use to connect to the database. Otherwise, a port is randomly selected.\n\n    > [!NOTE]\n    > You cannot change the [port number](https://redis.io/docs/latest/operate/rs/networking/port-configurations)\n    > after the database is created.\n    >"},{"id":"capacity","title":"Capacity","role":"content","text":"- **Memory limit** - [Database memory limits](https://redis.io/docs/latest/operate/rs/databases/memory-performance/memory-limit) include all database replicas and shards, including replica shards in database replication and database shards in database clustering.\n\n    If the total size of the database in the cluster reaches the memory limit, the memory eviction policy for the database is enforced.\n\n- **RAM limit** - If you create a database with Auto Tiering enabled, you also need to set the RAM-to-Flash ratio. Minimum RAM is 10%. Maximum RAM is 50%.\n\n- [**Memory eviction**](https://redis.io/docs/latest/operate/rs/databases/memory-performance/eviction-policy) - By default, when the total size of the database reaches its memory limit, the database evicts keys according to the least recently used keys out of all keys with an \"expire\" field set to make room for new keys. You can select a different eviction policy."},{"id":"capabilities","title":"Capabilities","role":"content","text":"When you create a new in-memory database, you can enable multiple Redis Stack [**Capabilities**](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise).\n\nDatabases created with or upgraded to Redis version 8 or later automatically enable the capabilities (modules) bundled with Redis Software as follows:\n\n| Database type | Automatically enabled capabilities |\n|---------------|------------------------------------|\n| RAM-only | [Redis Search](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search)<br />[JSON](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json)<br />[Time series](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/timeseries)<br />[Probabilistic](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/bloom)  |\n| Flash-enabled ([Redis Flex](https://redis.io/docs/latest/operate/rs/databases/flash)) | [JSON](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json)<br />[Probabilistic](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/bloom) |\n| [Active-Active](https://redis.io/docs/latest/operate/rs/databases/active-active)<sup>[1](#enabled-modules-table-note-1)</sup> | [Redis Search](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/search-active-active)<br />[JSON](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json) |\n\n1. <a name=\"enabled-modules-table-note-1\"></a>Upgrading existing Active-Active databases to Redis version 8 does not automatically enable these capabilities. Only new Active-Active databases created with Redis version 8 enable these capabilities by default.\n\nFor Auto Tiering databases, you can enable capabilities that support Auto Tiering. See [Redis Software and Redis Stack feature compatibility \n](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/enterprise-capabilities) for compatibility details.\n\n> [!NOTE]\n> To use Redis Stack capabilities, enable them when you create a new database.\n> You cannot enable them after database creation.\n\nTo add capabilities to the database:\n\n1. In the **Capabilities** section, select one or more capabilities.\n    \n1. To customize capabilities, click **Parameters** and enter the optional custom configuration.\n    \n1. Click **Done**.\n\nTo change capabilities' parameters for an existing database using the Cluster Manager UI:\n\n  1. In the **Capabilities** section, click **Edit Parameters**.\n\n  1. After you finish editing the module's configuration parameters, click **Done** to close the parameter editor."},{"id":"high-availability","title":"High Availability","role":"content","text":"- [**Replication**](https://redis.io/docs/latest/operate/rs/databases/durability-ha/replication) - We recommend you use intra-cluster replication to create replica shards for each database for high availability.\n\n    If the cluster is configured to support [rack-zone awareness](https://redis.io/docs/latest/operate/rs/clusters/configure/rack-zone-awareness), you can also enable rack-zone awareness for the database.\n\n- [**Replica high availability**](https://redis.io/docs/latest/operate/rs/databases/configure/replica-ha) - Automatically migrates replica shards to an available node if a replica node fails or is promoted to primary."},{"id":"clustering","title":"Clustering","role":"content","text":"- **Enable sharding** - You can either:\n\n    - Turn on sharding to enable [database clustering](https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering) and select the number of database shards.\n\n        When database clustering is enabled, databases are subject to limitations on [Multi-key commands](https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering).\n        \n        You can increase the number of shards in the database at any time.\n\n    - Turn off sharding to use only one shard so that you can use [Multi-key commands](https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering) without the limitations.\n\n- [**Shards placement**](https://redis.io/docs/latest/operate/rs/databases/memory-performance/shard-placement-policy) - Determines how to distribute database shards across nodes in the cluster.\n\n    - _Dense_ places shards on the smallest number of nodes.\n    \n    - _Sparse_ spreads shards across many nodes.\n\n- [**OSS Cluster API**](https://redis.io/docs/latest/operate/rs/databases/configure/oss-cluster-api) - The OSS Cluster API configuration allows access to multiple endpoints for increased throughput.\n\n    This configuration requires clients to connect to the primary node to retrieve the cluster topology before they can connect directly to proxies on each node.\n    \n    When you enable the OSS Cluster API, shard placement changes to _Sparse_, and the database proxy policy changes to _All primary shards_ automatically.\n\n    > [!NOTE]\n    > You must use a client that supports the cluster API to connect to a database that has the cluster API enabled.\n    >\n\n- **Hashing policy** - You can accept the [standard hashing policy](https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering#standard-hashing-policy), which is compatible with Redis Open Source, or define a [custom hashing policy](https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering#custom-hashing-policy) to define where keys are located in the clustered database.\n\n- [**Database proxy**](https://redis.io/docs/latest/operate/rs/databases/configure/proxy-policy) - Determines the number and location of active proxies, which manage incoming database operation requests."},{"id":"durability","title":"Durability","role":"content","text":"- [**Persistence**](https://redis.io/docs/latest/operate/rs/databases/configure/database-persistence) - To protect against loss of data stored in RAM, you can enable data persistence and store a copy of the data on disk with snapshots or an append-only file.\n\n- **Scheduled backup** - You can configure [periodic backups](https://redis.io/docs/latest/operate/rs/databases/import-export/schedule-backups) of the database, including the interval and backup location parameters."},{"id":"tls","title":"TLS","role":"content","text":"You can require [**TLS**](https://redis.io/docs/latest/operate/rs/security/encryption/tls) encryption and authentication for all communications, TLS encryption and authentication for Replica Of communication only, and TLS authentication for clients."},{"id":"access-control","title":"Access control","role":"content","text":"- **Unauthenticated access** - You can access the database as the default user without providing credentials.\n\n- **Password-only authentication** - When you configure a password for your database's default user, all connections to the database must authenticate with the [AUTH command](https://redis.io/docs/latest/commands/auth).\n\n    If you also configure an access control list, connections can specify other users for authentication, and requests are allowed according to the Redis ACLs specified for that user.\n\n    Creating a database without ACLs enables a *default* user with full access to the database. You can secure default user access by requiring a password.\n\n- **Access Control List** - You can specify the [user roles](https://redis.io/docs/latest/operate/rs/security/access-control/create-db-roles) that have access to the database and the [Redis ACLs](https://redis.io/docs/latest/operate/rs/security/access-control/redis-acl-overview) that apply to those connections.\n\n    To define an access control list for a database:\n\n    1. In **Security > Access Control > Access Control List**, select **+ Add ACL**.\n\n    1. Select a [role](https://redis.io/docs/latest/operate/rs/security/access-control/create-db-roles) to grant database access.\n\n    1. Associate a [Redis ACL](https://redis.io/docs/latest/operate/rs/security/access-control/create-db-roles) with the role and database.\n\n    1. Select the check mark to add the ACL."},{"id":"alerts","title":"Alerts","role":"content","text":"Select [alerts](https://redis.io/docs/latest/operate/rs/monitoring/v1_monitoring#database-alerts) to show in the database status and configure their thresholds.\n\nYou can also choose to [send alerts by email](https://redis.io/docs/latest/operate/rs/monitoring/v1_monitoring#send-alerts-by-email) to relevant users."},{"id":"replica-of","title":"Replica Of","role":"content","text":"With [**Replica Of**](https://redis.io/docs/latest/operate/rs/databases/import-export/replica-of/create), you can make the database a repository for keys from other databases."},{"id":"resp3-support","title":"RESP3 support","role":"content","text":"[RESP](https://redis.io/docs/latest/develop/reference/protocol-spec)  (Redis Serialization Protocol) is the protocol clients use to communicate with Redis databases. If you enable RESP3 support, the database will support the RESP3 protocol in addition to RESP2.\n\nFor more information about Redis Software's compatibility with RESP3, see [RESP compatibility with Redis Software](https://redis.io/docs/latest/operate/rs/references/compatibility/resp)."},{"id":"internode-encryption","title":"Internode encryption","role":"content","text":"Enable **Internode encryption** to encrypt data in transit between nodes for this database. See [Internode encryption](https://redis.io/docs/latest/operate/rs/security/encryption/internode-encryption) for more information."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/databases/configure/oss-cluster-api","title":"Enable OSS Cluster API","url":"https://redis.io/docs/latest/operate/rs/databases/configure/oss-cluster-api/","summary":"","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:27:03+01:00","page_type":"content","content_hash":"12f01466705fbdac3be61e92037162897e6aac3d28124fcf14154998926f462a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Review [OSS Cluster API](https://redis.io/docs/latest/operate/rs/clusters/optimize/oss-cluster-api) to determine if you should enable this feature for your database."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"The OSS Cluster API is supported only when a database meets specific criteria.\n\nThe database must:\n\n- Use the standard [hashing policy](https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering#supported-hashing-policies).\n- Have the [proxy policy](https://redis.io/docs/latest/operate/rs/databases/configure/proxy-policy) set to either _All primary shards_ or _All nodes_.\n\nIn addition, the database must _not_:\n\n- Use node `include` or `exclude` in the proxy policy.\n- Use [RedisTimeSeries](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/timeseries) or [RedisGears](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1) modules.\n\nThe OSS Cluster API setting applies to individual databases instead of the entire cluster.\n\n> [!NOTE]\n> For Kubernetes deployments, see [Enable cluster-aware clients (OSS Cluster API)](https://redis.io/docs/latest/operate/kubernetes/networking/cluster-aware-clients)."},{"id":"enable-oss-cluster-api-support","title":"Enable OSS Cluster API support","role":"content","text":"You can use the Cluster Manager UI, the `rladmin` utility, or the REST API to enable OSS Cluster API support for a database.\n\nWhen you enable OSS Cluster API support for an existing database, the change applies to new connections but does not affect existing connections. Clients must close existing connections and reconnect to apply the change.\n\n**Cluster Manager UI:**\n\nWhen you use the Cluster Manager UI to enable the OSS Cluster API, it automatically configures the [prerequisites](https://redis.io/docs/latest/operate/rs/databases/configure/oss-cluster-api#prerequisites).\n\nTo enable the OSS Cluster API for an existing database in the Cluster Manager UI:\n\n1. From the database's **Configuration** tab, click **Edit**.\n\n1. Expand the **Clustering** section.\n\n1. Enable **Sharding**.\n\n1. Select **OSS Cluster API**.\n\n    ![Use the *OSS Cluster API* setting to enable the API for the selected database.](https://redis.io/docs/latest/images/rs/screenshots/databases/config-clustering-oss-cluster-api-7-22.png)\n\n1. Click **Save**.\n\nYou can also use the Cluster Manager UI to enable the setting when creating a new database.\n\n**rladmin:**\n\nYou can use the [`rladmin` utility](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin) to enable the OSS Cluster API for Redis Software databases, including Replica Of databases.\n\nFor Active-Active (CRDB) databases, [use the crdb-cli utility](#active-active-databases).\n\nEnsure the [prerequisites](https://redis.io/docs/latest/operate/rs/databases/configure/oss-cluster-api#prerequisites) have been configured.  Then, enable the OSS Cluster API for a Redis database from the command line:\n\n[code example]\n\nTo determine the current setting for a database from the command line, use `rladmin info db` to return the value of the `oss_cluster` setting.\n\n[code example]\n\nThe OSS Cluster API setting applies to the specified database only; it does not apply to the cluster.\n\n**REST API:**\n\nYou can enable the OSS Cluster API when you [create a database](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#post-bdbs-v1) using the REST API:\n\n[code example]\n\nTo enable the OSS Cluster API for an existing database, you can use an [update database configuration](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#put-bdbs) REST API request:\n\n[code example]"},{"id":"active-active-databases","title":"Active-Active databases","role":"content","text":"The OSS Cluster API setting applies to all instances of the Active-Active database across participating clusters. To enable the OSS Cluster API for Active-Active databases, use the [Cluster Manager UI](#cluster-manager-ui) or the [`crdb-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli) utility.\n\n**Cluster Manager UI:**\n\nWhen you use the Cluster Manager UI to enable the OSS Cluster API, it automatically configures the [prerequisites](https://redis.io/docs/latest/operate/rs/databases/configure/oss-cluster-api#prerequisites).\n\nTo enable the OSS Cluster API for an existing Active-Active database in the Cluster Manager UI:\n\n1. From the database's **Configuration** tab, click **Edit**.\n\n1. Expand the **Clustering** section.\n\n1. Sharding must be enabled during Active-Active database creation.\n\n1. Select **OSS Cluster API**.\n\n    ![Use the *OSS Cluster API* setting to enable the API for the selected database.](https://redis.io/docs/latest/images/rs/screenshots/databases/config-clustering-oss-cluster-api-active-active-7-22.png)\n\n1. Click **Save**.\n\nYou can also use the Cluster Manager UI to enable the setting when creating a new database.\n\n**crdb-cli:**\n\nTo create an Active-Active database and enable the OSS Cluster API with `crdb-cli`:\n\n[code example]\n\nSee the [`crdb-cli crdb create`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/create) reference for more options.\n\nTo enable the OSS Cluster API for an existing Active-Active database with `crdb-cli`:\n\n1. Obtain the `CRDB-GUID` for the new database:\n\n    [code example]\n\n1. Use the `CRDB-GUID` to enable the OSS Cluster API:\n\n    [code example]"},{"id":"change-preferred-ip-type","title":"Change preferred IP type","role":"content","text":"By default, using [`CLUSTER SLOTS`](https://redis.io/docs/latest/commands/cluster-slots) and [`CLUSTER SHARDS`](https://redis.io/docs/latest/commands/cluster-shards) in a Redis Software cluster exposes the internal IP addresses for databases with the OSS Cluster API enabled.\n\nTo use external IP addresses instead of internal IP addresses, run the following [`rladmin tune db`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-db) command for each affected database:\n\n[code example]"},{"id":"turn-off-oss-cluster-api-support","title":"Turn off OSS Cluster API support","role":"content","text":"To deactivate OSS Cluster API support for a database, use one of the following methods:\n\n**Cluster Manager UI:**\n\nTo turn off the OSS Cluster API for standard databases or Active-Active databases in the Cluster Manager UI:\n\n1. From the database's **Configuration** tab, click **Edit**.\n\n1. Expand the **Clustering** section.\n\n1. Clear the **OSS Cluster API** checkbox.\n\n1. Click **Save**.\n\n**rladmin:**\n\nFor standard databases, including Replica Of, you can use `rladmin` to turn off the OSS Cluster API:\n\n[code example]\n\n**crdb-cli:**\n\nFor Active-Active databases, you can use `crdb-cli` to turn off the OSS Cluster API:\n\n[code example]\n\n\n\nWhen you turn off OSS Cluster API support for an existing database, the change applies to new connections but does not affect existing connections. Clients must close existing connections and reconnect to apply the change."},{"id":"multi-key-command-support","title":"Multi-key command support","role":"content","text":"When you enable the OSS Cluster API for a database, \n[multi-key commands](https://redis.io/docs/latest/operate/rc/databases/configuration/clustering#multikey-operations) are only allowed when all keys are mapped to the same slot.\n\nTo verify that your database meets this requirement, make sure that the `CLUSTER KEYSLOT` reply is the same for all keys affected by the multi-key command.  To learn more, see [multi-key operations](https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering#multikey-operations)."}],"examples":[{"id":"enable-oss-cluster-api-support-ex0","language":"sh","code":"$ rladmin tune db <database name or ID> oss_cluster enabled","section_id":"enable-oss-cluster-api-support"},{"id":"enable-oss-cluster-api-support-ex1","language":"sh","code":"$ rladmin info db test | grep oss_cluster:\n  oss_cluster: enabled","section_id":"enable-oss-cluster-api-support"},{"id":"enable-oss-cluster-api-support-ex2","language":"sh","code":"POST /v1/bdbs\n{ \n  \"oss_cluster\": true,\n  // Other database configuration parameters\n}","section_id":"enable-oss-cluster-api-support"},{"id":"enable-oss-cluster-api-support-ex3","language":"sh","code":"PUT /v1/bdbs/<database-id>\n{ \"oss_cluster\": true }","section_id":"enable-oss-cluster-api-support"},{"id":"active-active-databases-ex0","language":"sh","code":"$ crdb-cli crdb create --name <name> \\\n    --memory-size 10g --port <port> \\\n    --sharding true --shards-count 2  \\\n    --replication true --oss-cluster true --proxy-policy all-master-shards \\\n    --instance fqdn=<fqdn>,username=<user>,password=<pass> \\\n    --instance fqdn=<fqdn>,username=<user>,password=<pass> \\\n    --instance fqdn=<fqdn>,username=<user>,password=<pass>","section_id":"active-active-databases"},{"id":"active-active-databases-ex1","language":"sh","code":"$ crdb-cli crdb list\n    CRDB-GUID    NAME   REPL-ID  CLUSTER-FQDN\n    <CRDB-GUID>  Test   4        cluster1.local","section_id":"active-active-databases"},{"id":"active-active-databases-ex2","language":"sh","code":"$ crdb-cli crdb update --crdb-guid <CRDB-GUID> \\\n        --oss-cluster true","section_id":"active-active-databases"},{"id":"change-preferred-ip-type-ex0","language":"sh","code":"$ rladmin tune db db:<database-id> oss_cluster_api_preferred_ip_type external","section_id":"change-preferred-ip-type"},{"id":"turn-off-oss-cluster-api-support-ex0","language":"sh","code":"$ rladmin tune db <name or ID> oss_cluster disabled","section_id":"turn-off-oss-cluster-api-support"},{"id":"turn-off-oss-cluster-api-support-ex1","language":"sh","code":"$ crdb-cli crdb update --crdb-guid <CRDB-GUID> \\\n        --oss-cluster false","section_id":"turn-off-oss-cluster-api-support"}]}
{"schema_version":2,"id":"operate/rs/databases/configure/proxy-policy","title":"Configure proxy policy","url":"https://redis.io/docs/latest/operate/rs/databases/configure/proxy-policy/","summary":"","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:24+01:00","page_type":"content","content_hash":"65366272f90014939d1d6080208f55106926cd03810f95bb36e82411f843d95e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software provides high-performance data access\nthrough a proxy process that manages and optimizes access to shards\nwithin the Redis Software cluster. Each node contains a single proxy process.\nEach proxy can be active and take incoming traffic or it can be passive\nand wait for failovers."},{"id":"proxy-policies","title":"Proxy policies","role":"content","text":"A database can have one of the following proxy policies:\n\n| Proxy policy | Description | Recommended use cases | Advantages | Disadvantages |\n|--------------|-------------|-----------------------|-----------|-----------------|\n| Single | Only a single proxy is bound to the database. This is the default database configuration. | Most use cases without high traffic or load | Lower resource usage, fewer application-to-cluster connections | Higher latency, more network hops |\n| All primary shards | Multiple proxies are bound to the database, one on each node that hosts a database primary shard. | Most use cases that require multiple endpoints, such as when using the [OSS Cluster API](https://redis.io/docs/latest/operate/rs/clusters/optimize/oss-cluster-api) | Lower latency, fewer network hops, higher throughput | Higher resource usage, more application-to-proxy connections |\n| All nodes | Multiple proxies are bound to the database, one on each node in the cluster, regardless of whether or not there is a shard from this database on the node. | When using [load balancers](https://redis.io/docs/latest/operate/rs/networking/cluster-lba-setup) for environments without DNS | Higher throughput | Highest resource usage |"},{"id":"view-proxy-policy","title":"View proxy policy","role":"content","text":"You can use the Cluster Manager UI, [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin), or the [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api) to view proxy configuration settings.\n\nThe [`rladmin info cluster`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/info#info-cluster) command returns the current proxy policy for sharded and non-sharded (single shard) databases.\n\n[code example]"},{"id":"configure-database-proxy-policy","title":"Configure database proxy policy","role":"configuration","text":"You can use the [Cluster Manager UI](#cluster-manager-ui-method), the [REST API](#rest-api-method), or [`rladmin`](#command-line-method) to configure a database's proxy policy.\n\n> [!WARNING]\n> Any configuration update that unbinds existing proxies can disconnect existing client connections."},{"id":"cluster-manager-ui-method","title":"Cluster Manager UI method","role":"content","text":"You can change a database's proxy policy when you [create](https://redis.io/docs/latest/operate/rs/databases/create) or [edit](https://redis.io/docs/latest/operate/rs/databases/configure#edit-database-settings) a database using the Cluster Manager UI:\n\n1. While in edit mode on the database's configuration screen, expand the **Clustering** section.\n\n1. Select a policy from the **Database proxy** list.\n\n1. Click **Create** or **Save**."},{"id":"rest-api-method","title":"REST API method","role":"content","text":"You can specify a proxy policy when you [create a database](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#post-bdbs-v1) using the REST API:\n\n[code example]\n\nTo change the proxy policy of an existing database and endpoint, you can use an [update database configuration](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#put-bdbs) REST API request:\n\n[code example]"},{"id":"command-line-method","title":"Command-line method","role":"syntax","text":"You can configure a database's proxy policy using [`rladmin bind`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/bind).\n\nThe following example changes the bind policy for a database named \"db1\" with an endpoint ID \"1:1\" to \"All primary shards\" proxy policy:\n\n[code example]\n\nThe next command performs the same task using the database ID instead of the name. The ID of this database is \"1\".\n\n[code example]\n\n> [!NOTE]\n> You can find the endpoint ID for the endpoint argument by running `rladmin status`. Look for the endpoint ID information under\n> the `ENDPOINT` section of the output."},{"id":"reapply-policies-after-topology-changes","title":"Reapply policies after topology changes","role":"content","text":"If you want to reapply the policy after topology changes, such as node restarts,\nfailovers and migrations, run this command to reset the policy:\n\n[code example]\n\nThis is not required with single policies.\n\n#### Other implications\n\nDuring the regular operation of the cluster different actions might take\nplace, such as automatic migration or automatic failover, which change\nwhat proxy needs to be bound to what database. When such actions take\nplace the cluster attempts, as much as possible, to automatically change\nproxy bindings to adhere to the defined policies. That said, the cluster\nattempts to prevent any existing client connections from being\ndisconnected, and hence might not entirely enforce the policies. In such\ncases, you can enforce the policy using the appropriate rladmin\ncommands."},{"id":"multiple-active-proxies","title":"Multiple active proxies","role":"content","text":"Each database you create in a Redis Software cluster has an endpoint, which consists of a unique URL and port on the FQDN. This endpoint receives all the traffic for all operations for that database. By default, Redis Software binds this database endpoint to one of the proxies on a single node in the cluster. This proxy becomes an active proxy and receives all the operations for the given database. If the node with the active proxy fails, a new proxy on another node takes over as part of the failover process automatically.\n\nIn most cases, a single proxy can handle a large number of operations\nwithout consuming additional resources. However, under high load,\nnetwork bandwidth, or a high rate of packets per second (PPS) on the\nsingle active proxy can become a bottleneck to how fast database\noperations can be performed. In such cases, having multiple active proxies across multiple nodes, mapped to the same external database\nendpoint, can significantly improve throughput.\n\nYou can configure a database to have multiple internal proxies, which can improve performance in some cases.\nEven though multiple active proxies can help improve the throughput of database\noperations, configuring multiple active proxies may cause additional\nlatency in operations as the shards and proxies are spread across\nmultiple nodes in the cluster.\n\n> [!NOTE]\n> When the network on a single active proxy becomes the bottleneck, consider enabling multiple NIC support in Redis Software. With nodes that have multiple physical NICs (Network Interface Cards), you can configure Redis Software to separate internal and external traffic onto independent physical NICs. For more details, refer to [Multi-IP & IPv6](https://redis.io/docs/latest/operate/rs/networking/multi-ip-ipv6).\n\nHaving multiple proxies for a database can improve Redis Software's ability for fast failover in case of proxy or node failure. With multiple proxies for a database, a client doesn't need to wait for the cluster to spin up another proxy and a DNS change in most cases. Instead, the client uses the next IP address in the list to connect to another proxy."}],"examples":[{"id":"view-proxy-policy-ex0","language":"sh","code":"$ rladmin info cluster\ncluster configuration:\n   ...\n   default_non_sharded_proxy_policy: single\n   default_sharded_proxy_policy: single\n   ...","section_id":"view-proxy-policy"},{"id":"rest-api-method-ex0","language":"sh","code":"POST /v1/bdbs\n{ \n  \"proxy_policy\": \"single | all-master-shards | all-nodes\",\n  // Other database configuration parameters\n}","section_id":"rest-api-method"},{"id":"rest-api-method-ex1","language":"sh","code":"PUT /v1/bdbs/<database-id>\n{ \n  \"proxy_policy\": \"single | all-master-shards | all-nodes\",\n  \"endpoint\": <endpoint_uid>\n}","section_id":"rest-api-method"},{"id":"command-line-method-ex0","language":"sh","code":"rladmin bind db db1 endpoint 1:1 policy all-master-shards","section_id":"command-line-method"},{"id":"command-line-method-ex1","language":"sh","code":"rladmin bind db db:1 endpoint 1:1 policy all-master-shards","section_id":"command-line-method"},{"id":"reapply-policies-after-topology-changes-ex0","language":"sh","code":"rladmin bind db db:<ID> endpoint <endpoint id> policy <all-master-shards|all-nodes>","section_id":"reapply-policies-after-topology-changes"}]}
{"schema_version":2,"id":"operate/rs/databases/configure/replica-ha","title":"Configure high availability for replica shards","url":"https://redis.io/docs/latest/operate/rs/databases/configure/replica-ha/","summary":"Configure high availability for replica shards so that the cluster automatically migrates the replica shards to an available node.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"afc830600668496203016a529024e74fffe9ca5ffc316dbe90b3baff93f462df","sections":[{"id":"overview","title":"Overview","role":"overview","text":"When you enable [database replication](https://redis.io/docs/latest/operate/rs/databases/durability-ha/replication),\nRedis Software creates a replica of each primary shard.  The replica shard will always be \nlocated on a different node than the primary shard to make your data highly available.  If the primary shard \nfails or if the node hosting the primary shard fails, then the replica is promoted to primary.\n\nWithout replica high availability (_replica\\_ha_) enabled, the promoted primary shard becomes a single point of failure \nas the only copy of the data.\n\nEnabling _replica\\_ha_ configures the cluster to automatically replicate the promoted replica on an available node. \nThis automatically returns the database to a state where there are two copies of the data: \nthe former replica shard which has been promoted to primary and a new replica shard.\n\nAn available node:\n\n1. Meets replica migration requirements, such as [rack-awareness](https://redis.io/docs/latest/operate/rs/clusters/configure/rack-zone-awareness).\n1. Has enough available RAM to store the replica shard.\n1. Does not also contain the primary shard.\n\nIn practice, replica migration creates a new replica shard and copies the data from the primary shard to the new replica shard.\n\nFor example:\n\n1. Node:2 has a primary shard and node:3 has the corresponding replica shard.\n1. Either:\n\n    - Node:2 fails and the replica shard on node:3 is promoted to primary.\n    - Node:3 fails and the primary shard is no longer replicated to the replica shard on the failed node.\n\n1. If replica HA is enabled, a new replica shard is created on an available node.\n1. The data from the primary shard is replicated to the new replica shard.\n\n> [!NOTE]\n> - Replica HA follows all prerequisites of replica migration, such as [rack-awareness](https://redis.io/docs/latest/operate/rs/clusters/configure/rack-zone-awareness).\n> - Replica HA migrates as many shards as possible based on available DRAM in the target node. When no DRAM is available, replica HA stops migrating replica shards to that node."},{"id":"configure-high-availability-for-replica-shards","title":"Configure high availability for replica shards","role":"configuration","text":"If replica high availability is enabled for both the cluster and a database,\nthe database's replica shards automatically migrate to another node when a primary or replica shard fails.\nIf replica HA is not enabled at the cluster level,\nreplica HA will not migrate replica shards even if replica HA is enabled for a database.\n\nReplica high availability is enabled for the cluster by default.\n\nWhen you create a database using the Cluster Manager UI, replica high availability is enabled for the database by default if you enable replication.\n\n![When you select the Replication checkbox in the High availability section of the database configuration screen, the Replica high availability checkbox is also selected by default.](https://redis.io/docs/latest/images/rs/screenshots/databases/config-replica-ha-enabled-7-8-2.png)\n\nTo use replication without replication high availability, clear the **Replica high availability** checkbox.\n\nYou can also enable or turn off replica high availability for a database using `rladmin` or the REST API.\n\n> [!NOTE]\n> For Active-Active databases, replica HA is enabled for the database by default to make sure that replica shards are available for Active-Active replication."},{"id":"configure-cluster-policy-for-replica-ha","title":"Configure cluster policy for replica HA","role":"configuration","text":"To enable or turn off replica high availability by default for the entire cluster, use one of the following methods:\n\n- [rladmin tune cluster](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-cluster): \n    \n    [code example]\n\n- [Update cluster policy](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/policy#put-cluster-policy) REST API request:\n\n    [code example]"},{"id":"turn-off-replica-ha-for-a-database","title":"Turn off replica HA for a database","role":"content","text":"To turn off replica high availability for a specific database using `rladmin`, run:\n\n[code example]\n\nYou can use the database name in place of `db:<ID>` in the preceding command."},{"id":"configuration-options","title":"Configuration options","role":"configuration","text":"You can see the current configuration options for replica HA with:\n\n[code example]"},{"id":"grace-period","title":"Grace period","role":"content","text":"By default, replica HA has a 10-minute grace period after node failure and before new replica shards are created.\n\n> [!NOTE]\n> The default grace period is 30 minutes for containerized applications using [Redis Enterprise Software for Kubernetes](https://redis.io/docs/latest/operate/kubernetes).\n\n> [!NOTE]\n> For Kubernetes deployments, if ReplicaHA (previously SlaveHA) is enabled at the cluster level, you must also manually enable it at the database level for each RedisEnterpriseDatabase (REDB) using the UI or `rladmin`. Without manual database-level configuration, databases will show `slave_ha: disabled (database)` even when cluster-level ReplicaHA is enabled.\n\nTo configure this grace period from rladmin, run:\n\n[code example]"},{"id":"shard-priority","title":"Shard priority","role":"content","text":"Replica shard migration is based on priority.  When memory resources are limited, the most important replica shards are migrated first:\n\n1. `slave_ha_priority` - Replica shards with higher \n    integer values are migrated before shards with lower values.\n\n    To assign priority to a database, run:\n\n    [code example]\n    \n    You can use the database name in place of `db:<ID>` in the preceding command.\n\n1. Active-Active databases - Active-Active database synchronization uses replica shards to synchronize between the replicas.\n1. Database size - It is easier and more efficient to move replica shards of smaller databases.\n1. Database UID - The replica shards of databases with a higher UID are moved first."},{"id":"cooldown-periods","title":"Cooldown periods","role":"content","text":"Both the cluster and the database have cooldown periods.\n\nAfter node failure, the cluster cooldown period (`slave_ha_cooldown_period`) prevents another replica migration due to another node failure for any\ndatabase in the cluster until the cooldown period ends. The default is one hour.\n\nAfter a database is migrated with replica HA,\nit cannot go through another migration due to another node failure until the cooldown period for the database (`slave_ha_bdb_cooldown_period`) ends. The default is two hours.\n\nTo configure cooldown periods, use [`rladmin tune cluster`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-cluster):\n\n- For the cluster:\n\n    [code example]\n\n- For all databases in the cluster:\n\n    [code example]"},{"id":"alerts","title":"Alerts","role":"content","text":"The following alerts are sent during replica HA activation:\n\n- Shard migration begins after the grace period.\n- Shard migration fails because there is no available node (sent hourly).\n- Shard migration is delayed because of the cooldown period."},{"id":"limitations","title":"Limitations","role":"limits","text":"- If two nodes that host a primary shard and its corresponding replica shard go down at the same time, both shards will be lost and replica high availability will not be able to create a new replica shard for any primary shard still running.\n\n    > [!NOTE]\n    > This limitation applies only when cluster quorum is maintained after two nodes fail, such as clusters with at least five nodes. If two nodes fail simultaneously in a cluster with only three nodes, the entire cluster is lost and must be recovered.\n    >"}],"examples":[{"id":"configure-cluster-policy-for-replica-ha-ex0","language":"sh","code":"rladmin tune cluster slave_ha { enabled | disabled }","section_id":"configure-cluster-policy-for-replica-ha"},{"id":"configure-cluster-policy-for-replica-ha-ex1","language":"sh","code":"PUT /v1/cluster/policy \n    { \"slave_ha\": <boolean> }","section_id":"configure-cluster-policy-for-replica-ha"},{"id":"turn-off-replica-ha-for-a-database-ex0","language":" text","code":"rladmin tune db db:<ID> slave_ha disabled","section_id":"turn-off-replica-ha-for-a-database"},{"id":"configuration-options-ex0","language":" text","code":"rladmin info cluster","section_id":"configuration-options"},{"id":"grace-period-ex0","language":" text","code":"rladmin tune cluster slave_ha_grace_period <time_in_seconds>","section_id":"grace-period"},{"id":"shard-priority-ex0","language":" text","code":"rladmin tune db db:<ID> slave_ha_priority <positive integer>","section_id":"shard-priority"},{"id":"cooldown-periods-ex0","language":" text","code":"rladmin tune cluster slave_ha_cooldown_period <time_in_seconds>","section_id":"cooldown-periods"},{"id":"cooldown-periods-ex1","language":" text","code":"rladmin tune cluster slave_ha_bdb_cooldown_period <time_in_seconds>","section_id":"cooldown-periods"}]}
{"schema_version":2,"id":"operate/rs/databases/configure/shard-placement","title":"Configure shard placement","url":"https://redis.io/docs/latest/operate/rs/databases/configure/shard-placement/","summary":"Configure shard placement to improve performance.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"8806ef61e389174d6d8931a5bd4afba1a99ef42dce49c59b6370c31d03f65829","sections":[{"id":"overview","title":"Overview","role":"overview","text":"In Redis Software, the location of master and replica shards on the cluster nodes can impact the database and node performance.\nMaster shards and their corresponding replica shards are always placed on separate nodes for data resiliency.\nThe [shard placement policy](https://redis.io/docs/latest/operate/rs/databases/memory-performance/shard-placement-policy) helps to maintain optimal performance and resiliency.\n\nIn addition to the shard placement policy, considerations that determine shard placement are:\n\n- Separation of master and replica shards\n- Available persistence and Auto Tiering storage\n- [Rack-zone awareness](https://redis.io/docs/latest/operate/rs/clusters/configure/rack-zone-awareness)\n- Memory available to host the database when fully populated\n\nThe shard placement policies are:\n\n- `dense` - Place as many shards as possible on the smallest number of nodes to reduce the latency between the proxy and the database shards;\n    Recommended for Redis on RAM databases to optimize memory resources\n- `sparse` - Spread the shards across as many nodes in the cluster as possible to spread the traffic across cluster nodes;\n    Recommended for databases with Auto Tiering enabled to optimize disk resources\n\nWhen you create a Redis Software cluster, the default shard placement policy (`dense`) is assigned to all databases that you create on the cluster.\n\nYou can:\n\n- Change the default shard placement policy for the cluster to `sparse` so that the cluster applies that policy to all databases that you create\n- Change the shard placement policy for each database after the database is created"},{"id":"default-shard-placement-policy","title":"Default shard placement policy","role":"content","text":"When you create a new cluster, the cluster configuration has a `dense` default shard placement policy.\nWhen you create a database, this default policy is applied to the new database.\n\nTo see the current default shard placement policy, run `rladmin info cluster`:\n\n![](https://redis.io/docs/latest/images/rs/shard_placement_info_cluster.png)\n\nTo change the default shard placement policy so that new databases are created with the `sparse` shard placement policy, run:\n\n[code example]"},{"id":"shard-placement-policy-for-a-database","title":"Shard placement policy for a database","role":"content","text":"To see the shard placement policy for a database in `rladmin status`.\n\n![](https://redis.io/docs/latest/images/rs/shard_placement_rladmin_status.png)\n\nTo change the shard placement policy for a database, run:\n\n[code example]"}],"examples":[{"id":"default-shard-placement-policy-ex0","language":"sh","code":"rladmin tune cluster default_shards_placement [ dense | sparse ]","section_id":"default-shard-placement-policy"},{"id":"shard-placement-policy-for-a-database-ex0","language":"sh","code":"rladmin tune db { db:<ID> | <database-name> } shards_placement { dense | sparse }","section_id":"shard-placement-policy-for-a-database"}]}
{"schema_version":2,"id":"operate/rs/databases/connect","title":"Connect to a database","url":"https://redis.io/docs/latest/operate/rs/databases/connect/","summary":"Learn how to connect your application to a Redis database hosted by Redis Enterprise Software and test your connection.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","children":[{"id":"operate/rs/databases/connect/supported-clients-browsers","summary":"Info about Redis client libraries and supported clients when using the discovery service.","title":"Supported connection clients","url":"https://redis.io/docs/latest/operate/rs/databases/connect/supported-clients-browsers/"},{"id":"operate/rs/databases/connect/test-client-connectivity","summary":"","title":"Test client connection","url":"https://redis.io/docs/latest/operate/rs/databases/connect/test-client-connectivity/"},{"id":"operate/rs/databases/connect/troubleshooting-guide","summary":"Troubleshoot issues with Redis Software, including connectivity issues between the database and clients or applications.","title":"Troubleshooting pocket guide for Redis Software","url":"https://redis.io/docs/latest/operate/rs/databases/connect/troubleshooting-guide/"}],"page_type":"content","content_hash":"5ab129dafdbaaa468f154ce8f7fcae236e863b30ee1b89f9954863222697bc43","sections":[{"id":"overview","title":"Overview","role":"overview","text":"After you [set up a cluster](https://redis.io/docs/latest/operate/rs/clusters/new-cluster-setup) and [create a Redis database](https://redis.io/docs/latest/operate/rs/databases/create), you can connect to your database.\n\nTo connect to your database, you need the database endpoint, which includes the cluster name (FQDN) and the database port. To view and copy public and private endpoints for a database in the cluster, see the database’s **Configuration > General** section in the Cluster Manager UI.\n\n![View public and private endpoints from the General section of the database's Configuration screen.](https://redis.io/docs/latest/images/rs/screenshots/databases/config-general-endpoints.png)\n\nIf you try to connect with the FQDN, and the database does not respond, try connecting with the IP address. If this succeeds, DNS is not properly configured. To set up DNS, see [Configure cluster DNS](https://redis.io/docs/latest/operate/rs/networking/cluster-dns).\n\nIf you want to secure your connection, set up [TLS](https://redis.io/docs/latest/operate/rs/security/encryption/tls)."},{"id":"connect-to-a-database","title":"Connect to a database","role":"content","text":"Use one of the following connection methods to connect to your database:\n\n- [`redis-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/redis-cli) utility\n\n- [Redis Insight](https://redis.com/redis-enterprise/redis-insight/)\n\n- [Redis client](https://redis.io/docs/latest/develop/clients) for your preferred programming language\n\nFor examples, see [Test client connection](https://redis.io/docs/latest/operate/rs/databases/connect/test-client-connectivity)."},{"id":"continue-learning-with-redis-university","title":"Continue learning with Redis University","role":"content","text":"See the [Connect to a database on Redis Software](https://university.redis.io/course/zyxx6fdkcm5ahd) for the course."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/databases/connect/supported-clients-browsers","title":"Supported connection clients","url":"https://redis.io/docs/latest/operate/rs/databases/connect/supported-clients-browsers/","summary":"Info about Redis client libraries and supported clients when using the discovery service.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:24+01:00","page_type":"content","content_hash":"4e1adb4d37614c2ae67adf569250d5b5aca35c3fbe638e7945f8c88c5f96c096","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can connect to Redis Software databases programmatically using client libraries."},{"id":"redis-client-libraries","title":"Redis client libraries","role":"content","text":"To connect an application to a Redis database hosted by Redis Software, use a [client library](https://redis.io/docs/latest/develop/clients) appropriate for your programming language.\n\nYou can also use the `redis-cli` utility to connect to a database from the command line.\n\nFor examples of each approach, see the [Redis Software quickstart](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/redis-enterprise-software-quickstart).\n\nNote: You cannot use client libraries to configure Redis Software.  Instead, use:\n\n- The Redis Software [Cluster Manager UI](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/redis-enterprise-software-quickstart)\n- The [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api)\n- Command-line utilities, such as [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin)"},{"id":"discovery-service","title":"Discovery service","role":"content","text":"All [recommended Redis client libraries](https://redis.io/docs/latest/develop/clients) support the Redis Sentinel API, so you can use any of them with the [discovery service](https://redis.io/docs/latest/operate/rs/databases/durability-ha/discovery-service).\n\nIf you need to use a client that doesn't support Sentinel, you can use [Sentinel Tunnel](https://github.com/RedisLabs/sentinel_tunnel) to discover the current primary Redis endpoint with Sentinel and create a TCP tunnel between a local port on the client and the primary endpoint."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/databases/connect/test-client-connectivity","title":"Test client connection","url":"https://redis.io/docs/latest/operate/rs/databases/connect/test-client-connectivity/","summary":"","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"5eb39d38dd33b87c237dbc5bb7b776d62f6c9f80dcc6eace2313de1a53b00888","sections":[{"id":"overview","title":"Overview","role":"overview","text":"In various scenarios, such as after creating a new cluster or upgrading\nthe cluster, you should verify clients can connect to the\ndatabase.\n\nTo test client connectivity:\n\n1. After you [create a Redis database](https://redis.io/docs/latest/operate/rs/databases/create), copy the database endpoint, which contains the cluster name (FQDN).\n\n    To view and copy endpoints for a database in the cluster, see the database’s **Configuration > General** section in the Cluster Manager UI:\n\n    ![View public and private endpoints from the General section of the database's Configuration screen.](https://redis.io/docs/latest/images/rs/screenshots/databases/config-general-endpoints.png)\n\n1. Try to connect to the database endpoint from your client of choice,\n    and run database commands.\n\n1. If the database does not respond, try to connect to the database\n    endpoint using the IP address rather than the FQDN. If you\n    succeed, then DNS is not properly configured. For\n    additional details, see\n    [Configure cluster DNS](https://redis.io/docs/latest/operate/rs/networking/cluster-dns).\n\nIf any issues occur when testing database connections, [contact\nsupport](https://redis.com/company/support/)."},{"id":"test-database-connections","title":"Test database connections","role":"content","text":"After you create a Redis database, you can connect to your\ndatabase and store data using one of the following methods:\n\n- [`redis-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/redis-cli), the built-in command-line tool\n\n- [Redis Insight](https://redis.com/redis-enterprise/redis-insight/), a free Redis GUI that is available for macOS, Windows, and Linux \n\n- An application using a Redis client library, such as [`redis-py`](https://github.com/redis/redis-py) for Python. See the [client list](https://redis.io/docs/latest/develop/clients) to view all Redis clients by language."},{"id":"connect-with-redis-cli","title":"Connect with redis-cli","role":"content","text":"Connect to your database with `redis-cli` (located in the `/opt/redislabs/bin` directory), then store and retrieve a key:\n\n[code example]\n\nTo connect from your own machine rather than from a cluster node, you can [install the standalone `redis-cli` binary](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/install-redis-cli) on Linux or macOS.\n\nFor more `redis-cli` connection examples, see the [`redis-cli` reference](https://redis.io/docs/latest/operate/rs/references/cli-utilities/redis-cli)."},{"id":"connect-with-redis-insight","title":"Connect with Redis Insight","role":"content","text":"Redis Insight is a free Redis GUI that is available for macOS, Windows, and Linux.\n\n1. [Install Redis Insight](https://redis.io/docs/latest/develop/tools/insight).\n\n1. Open Redis Insight and select **Add Redis Database**.\n\n1. Enter the host and port in the **Host** and **Port** fields.\n\n1. Select **Use TLS** if [TLS](https://redis.io/docs/latest/operate/rs/security/encryption/tls) is set up.\n\n1. Select **Add Redis Database** to connect to the database.\n\nSee the [Redis Insight documentation](https://redis.io/docs/latest/develop/tools/insight) for more information."},{"id":"connect-with-python","title":"Connect with Python","role":"content","text":"Python applications can connect\nto the database using the `redis-py` client library. For installation instructions, see the\n[`redis-py` README](https://github.com/redis/redis-py#readme) on GitHub.\n\n1. From the command line, create a new file called\n`redis_test.py`:\n\n    [code example]\n\n1. Paste the following code in `redis_test.py`, and replace `<host>` and `<port>` with your database's endpoint details:\n\n    [code example]\n\n1. Run the application:\n\n    [code example]\n\n1. If the application successfully connects to your database, it outputs: \n\n    [code example]"},{"id":"connect-with-discovery-service","title":"Connect with discovery service","role":"content","text":"You can also connect a Python application to the database using the discovery service, which complies with the Redis Sentinel API.\n\nIn the IP-based connection method, you only need the database name, not the port number.\nThe following example uses the discovery service that listens on port 8001 on all nodes of the cluster\nto discover the endpoint for the database named \"db1\".\n\n[code example]\n\nFor more `redis-py` connection examples, see the [`redis-py` developer documentation](https://redis.readthedocs.io/en/stable/examples/connection_examples.html)."}],"examples":[{"id":"connect-with-redis-cli-ex0","language":"sh","code":"$ redis-cli -h <endpoint> -p <port>\n127.0.0.1:16653> set key1 123\nOK\n127.0.0.1:16653> get key1\n\"123\"","section_id":"connect-with-redis-cli"},{"id":"connect-with-python-ex0","language":"sh","code":"vi redis_test.py","section_id":"connect-with-python"},{"id":"connect-with-python-ex1","language":"python","code":"import redis\n\n    # Connect to the database\n    r = redis.Redis(host='<host>', port=<port>)\n\n    # Store a key\n    print(\"set key1 123\")\n    print(r.set('key1', '123'))\n\n    # Retrieve the key\n    print(\"get key1\")\n    print(r.get('key1'))","section_id":"connect-with-python"},{"id":"connect-with-python-ex2","language":"sh","code":"python redis_test.py","section_id":"connect-with-python"},{"id":"connect-with-python-ex3","language":"sh","code":"set key1 123\n    True\n    get key1\n    123","section_id":"connect-with-python"},{"id":"connect-with-discovery-service-ex0","language":"python","code":"from redis.sentinel import Sentinel\n\n# with IP based connections, a list of known node IP addresses is constructed\n# to allow connection even if any one of the nodes in the list is unavailable.\nsentinel_list = [\n('10.0.0.44', 8001),\n('10.0.0.45', 8001),\n('10.0.0.46', 8001)\n]\n\n# change this to the db name you want to connect\ndb_name = 'db1'\n\nsentinel = Sentinel(sentinel_list, socket_timeout=0.1)\nr = sentinel.master_for(db_name, socket_timeout=0.1)\n\n# set key \"foo\" to value \"bar\"\nprint(r.set('foo', 'bar'))\n# set value for key \"foo\"\nprint(r.get('foo'))","section_id":"connect-with-discovery-service"}]}
{"schema_version":2,"id":"operate/rs/databases/connect/troubleshooting-guide","title":"Troubleshooting pocket guide for Redis Software","url":"https://redis.io/docs/latest/operate/rs/databases/connect/troubleshooting-guide/","summary":"Troubleshoot issues with Redis Software, including connectivity issues between the database and clients or applications.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:24+01:00","page_type":"content","content_hash":"262f2d77bdbcaa68a51edaf317357e0f801a610ed974db4fe023ff7bf90015fb","sections":[{"id":"overview","title":"Overview","role":"overview","text":"If your client or application cannot connect to your database, verify the following."},{"id":"identify-redis-host-issues","title":"Identify Redis host issues","role":"content","text":"#### Check resource usage\n\n- Used disk space should be less than `90%`. To check the host machine's disk usage, run the [`df`](https://man7.org/linux/man-pages/man1/df.1.html) command:\n\n    [code example]\n\n- RAM and CPU utilization should be less than `80%`, and host resources must be available exclusively for Redis Software. You should also make sure that swap memory is not being used or is not configured.\n\n    1. Run the [`free`](https://man7.org/linux/man-pages/man1/free.1.html) command to check memory usage:\n\n        [code example]\n\n    1. Used CPU should be less than `80%`. To check CPU usage, use `top` or `vmstat`.\n\n        Run [`top`](https://man7.org/linux/man-pages/man1/top.1.html):\n\n        [code example]\n\n        Run [`vmstat`](https://man7.org/linux/man-pages/man8/vmstat.8.html):\n\n        [code example]\n\n    1. If CPU or RAM usage is greater than 80%, ask your system administrator which process is the culprit. If the process is not related to Redis, terminate it.\n\n#### Sync clock with time server\n\nIt is recommended to sync the host clock with a time server.\n\nVerify that time is synchronized with the time server using one of the following commands:\n\n- `ntpq -p`\n\n- `chronyc sources`\n\n- [`timedatectl`](https://man7.org/linux/man-pages/man1/timedatectl.1.html)\n\n#### Remove https_proxy and http_proxy variables\n\n1. Run [`printenv`](https://man7.org/linux/man-pages/man1/printenv.1.html) and check if `https_proxy` and `http_proxy` are configured as environment variables:\n\n    [code example]\n\n1. If `https_proxy` or `http_proxy` exist, remove them:\n\n    [code example]\n    [code example]\n\n#### Review system logs\n\nReview system logs including the syslog or journal for any error messages, warnings, or critical events. See [Logging](https://redis.io/docs/latest/operate/rs/clusters/logging) for more information."},{"id":"identify-issues-caused-by-security-hardening","title":"Identify issues caused by security hardening","role":"content","text":"- Temporarily deactivate any security hardening tools (such as selinux, cylance, McAfee, or dynatrace), and check if the problem is resolved. \n\n- The user `redislabs` must have read and write access to `/tmp` directory. Run the following commands to verify.\n\n    1. Create a test file in `/tmp` as the `redislabs` user:\n        [code example]\n\n    1. Verify the file was created successfully:\n        [code example]\n\n- Using a non-permissive file mode creation mask (`umask`) can cause issues.\n\n    1. Check the output of `umask`:\n\n        [code example]\n\n    1. If `umask`'s output differs from the default value `0022`, it might prevent normal operation. Consult your system administrator and revert to the default `umask` setting."},{"id":"identify-cluster-issues","title":"Identify cluster issues","role":"content","text":"- Use `supervisorctl status` to verify all processes are in a `RUNNING` state:\n\n    [code example]\n\n- Run `rlcheck` and verify no errors appear:\n\n    [code example]\n\n- Run [`rladmin status issues_only`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status) and verify that no issues appear:\n\n    [code example]\n\n- Run [`rladmin status shards`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status#status-shards). For each shard, `USED_MEMORY` should be less than 25 GB.\n\n    [code example]\n\n- Run [`rladmin cluster running_actions`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/running_actions) and confirm that no tasks are currently running (active):\n\n    [code example]"},{"id":"troubleshoot-connectivity","title":"Troubleshoot connectivity","role":"errors","text":"#### Database endpoint resolution\n\n1. On the client machine, check if the database endpoint can be resolved:\n\n    [code example]\n\n1. If endpoint resolution fails on the client machine, check on one of the cluster nodes:\n\n    [code example]\n\n1. If endpoint resolution succeeds on the cluster node but fails on the client machine, review the DNS configuration and fix any errors.\n\n1. If the endpoint can’t be resolved on the cluster node, [contact support](https://redis.com/company/support/).\n\n#### Client application issues\n\n1. To identify possible client application issues, test connectivity from the client machine to the database using [`redis-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/redis-cli):\n\n    [`INFO`](https://redis.io/docs/latest/commands/info):\n\n    [code example]\n\n    [`PING`](https://redis.io/docs/latest/commands/ping):\n\n    [code example]\n   \n    or if TLS is enabled:\n    \n    [code example]\n\n1. If the client machine cannot connect, try to connect to the database from one of the cluster nodes:\n\n    [code example]\n\n1. If the cluster node is also unable to connect to the database, [contact Redis support](https://redis.com/company/support/).\n\n1. If the client fails to connect, but the cluster node succeeds, perform health checks on the client and network.\n\n#### Firewall access\n\n1. Run one of the following commands to verify that database access is not blocked by a firewall on the client machine or cluster:\n\n    [code example]\n\n    [code example]\n\n    [code example]\n\n1. To resolve firewall issues:\n\n    1. If a firewall is configured for your database, add the client IP address to the firewall rules.\n\n    1. Configure third-party firewalls and external proxies to allow the cluster FQDN, database endpoint IP address, and database ports."},{"id":"troubleshoot-latency","title":"Troubleshoot latency","role":"errors","text":"#### Server-side latency\n\n- Make sure the database's used memory does not reach the configured database max memory limit. For more details, see [Database memory limits](https://redis.io/docs/latest/operate/rs/databases/memory-performance/memory-limit).\n\n- Try to correlate the time of the latency with any surge in the following metrics:\n\n    - Number of connections\n\n    - Used memory\n\n    - Evicted keys\n\n    - Expired keys \n\n- Run [`SLOWLOG GET`](https://redis.io/docs/latest/commands/slowlog-get) using [`redis-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/redis-cli) to identify slow commands such as [`KEYS`](https://redis.io/docs/latest/commands/keys) or [`HGETALL`](https://redis.io/docs/latest/commands/hgetall):\n\n    [code example]\n\n    Consider using alternative commands such as [`SCAN`](https://redis.io/docs/latest/commands/scan), [`SSCAN`](https://redis.io/docs/latest/commands/sscan), [`HSCAN`](https://redis.io/docs/latest/commands/hscan) and [`ZSCAN`](https://redis.io/docs/latest/commands/zscan)\n\n- Keys with large memory footprints can cause latency. To identify such keys, compare the keys returned by [`SLOWLOG GET`](https://redis.io/docs/latest/commands/slowlog-get) with the output of the following commands:\n\n    [code example]\n\n    [code example]\n\n- For additional diagnostics, see:\n\n    - [Diagnosing latency issues](https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/latency)\n\n    - [View Redis slow log](https://redis.io/docs/latest/operate/rs/clusters/logging/redis-slow-log)\n\n#### Client-side latency\n\nVerify the following:\n\n- There is no memory or CPU pressure on the client host.\n\n- The client uses a connection pool instead of frequently opening and closing connections.\n\n- The client does not erroneously open multiple connections that can pressure the client or server."}],"examples":[{"id":"identify-redis-host-issues-ex0","language":"sh","code":"$ df -h\n    Filesystem      Size  Used Avail Use% Mounted on\n    overlay          59G   23G   33G  41% /\n    /dev/vda1        59G   23G   33G  41% /etc/hosts","section_id":"identify-redis-host-issues"},{"id":"identify-redis-host-issues-ex1","language":"sh","code":"$ free\n                  total        used        free      shared  buff/cache   available\n        Mem:        6087028     1954664      993756      409196     3138608     3440856\n        Swap:       1048572           0     1048572","section_id":"identify-redis-host-issues"},{"id":"identify-redis-host-issues-ex2","language":"sh","code":"$ top\n        Tasks:  54 total,   1 running,  53 sleeping,   0 stopped,   0 zombie\n       %Cpu(s):  1.7 us,  1.4 sy,  0.0 ni, 96.8 id,  0.0 wa,  0.0 hi,  0.1 si,  0.0 st\n       KiB Mem :  6087028 total,   988672 free,  1958060 used,  3140296 buff/cache\n       KiB Swap:  1048572 total,  1048572 free,        0 used.  3437460 avail Mem","section_id":"identify-redis-host-issues"},{"id":"identify-redis-host-issues-ex3","language":"sh","code":"$ vmstat\n        procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----\n        r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st\n        2  0      0 988868 177588 2962876    0    0     0     6    7   12  1  1 99  0  0","section_id":"identify-redis-host-issues"},{"id":"identify-redis-host-issues-ex4","language":"sh","code":"printenv | grep -i proxy","section_id":"identify-redis-host-issues"},{"id":"identify-redis-host-issues-ex5","language":"sh","code":"unset https_proxy","section_id":"identify-redis-host-issues"},{"id":"identify-redis-host-issues-ex6","language":"sh","code":"unset http_proxy","section_id":"identify-redis-host-issues"},{"id":"identify-issues-caused-by-security-hardening-ex0","language":"sh","code":"$ su - redislabs -s /bin/bash -c 'touch /tmp/test'","section_id":"identify-issues-caused-by-security-hardening"},{"id":"identify-issues-caused-by-security-hardening-ex1","language":"sh","code":"$ ls -l /tmp/test\n        -rw-rw-r-- 1 redislabs redislabs 0 Aug 12 02:06 /tmp/test","section_id":"identify-issues-caused-by-security-hardening"},{"id":"identify-issues-caused-by-security-hardening-ex2","language":"sh","code":"$ umask\n        0022","section_id":"identify-issues-caused-by-security-hardening"},{"id":"identify-cluster-issues-ex0","language":"sh","code":"supervisorctl status","section_id":"identify-cluster-issues"},{"id":"identify-cluster-issues-ex1","language":"sh","code":"rlcheck","section_id":"identify-cluster-issues"},{"id":"identify-cluster-issues-ex2","language":"sh","code":"$ rladmin status issues_only\n    CLUSTER NODES:\n    NODE:ID ROLE ADDRESS EXTERNAL_ADDRESS HOSTNAME SHARDS CORES FREE_RAM PROVISIONAL_RAM VERSION STATUS\n\n    DATABASES:\n    DB:ID  NAME        TYPE  STATUS  SHARDS  PLACEMENT   REPLICATION    PERSISTENCE    ENDPOINT  \n\n    ENDPOINTS:\n    DB:ID               NAME             ID          NODE             ROLE             SSL       \n\n    SHARDS:\n    DB:ID      NAME     ID        NODE     ROLE     SLOTS      USED_MEMORY            STATUS","section_id":"identify-cluster-issues"},{"id":"identify-cluster-issues-ex3","language":"sh","code":"$ rladmin status shards\n    SHARDS:\n    DB:ID    NAME         ID          NODE      ROLE      SLOTS       USED_MEMORY       STATUS   \n    db:1     db1          redis:1     node:1    master    0-16383     2.13MB            OK","section_id":"identify-cluster-issues"},{"id":"identify-cluster-issues-ex4","language":"sh","code":"$ rladmin cluster running_actions\n    No active tasks","section_id":"identify-cluster-issues"},{"id":"troubleshoot-connectivity-ex0","language":"sh","code":"dig <endpoint>","section_id":"troubleshoot-connectivity"},{"id":"troubleshoot-connectivity-ex1","language":"sh","code":"dig @localhost <endpoint>","section_id":"troubleshoot-connectivity"},{"id":"troubleshoot-connectivity-ex2","language":"sh","code":"redis-cli -h <endpoint> -p <port> -a <password> INFO","section_id":"troubleshoot-connectivity"},{"id":"troubleshoot-connectivity-ex3","language":"sh","code":"redis-cli -h <endpoint> -p <port> -a <password> PING","section_id":"troubleshoot-connectivity"},{"id":"troubleshoot-connectivity-ex4","language":"sh","code":"redis-cli -h <endpoint> -p <port> -a <password> --tls --insecure --cert --key PING","section_id":"troubleshoot-connectivity"},{"id":"troubleshoot-connectivity-ex5","language":"sh","code":"redis-cli -h <node IP or hostname> -p <port> -a <password> PING","section_id":"troubleshoot-connectivity"},{"id":"troubleshoot-connectivity-ex6","language":"sh","code":"iptables -L","section_id":"troubleshoot-connectivity"},{"id":"troubleshoot-connectivity-ex7","language":"sh","code":"ufw status","section_id":"troubleshoot-connectivity"},{"id":"troubleshoot-connectivity-ex8","language":"sh","code":"firewall-cmd –list-all","section_id":"troubleshoot-connectivity"},{"id":"troubleshoot-latency-ex0","language":"sh","code":"redis-cli -h <endpoint> -p <port> -a <password> SLOWLOG GET <number of entries>","section_id":"troubleshoot-latency"},{"id":"troubleshoot-latency-ex1","language":"sh","code":"redis-cli -h <endpoint> -p <port> -a <password> --memkeys","section_id":"troubleshoot-latency"},{"id":"troubleshoot-latency-ex2","language":"sh","code":"redis-cli -h <endpoint> -p <port> -a <password> --bigkeys","section_id":"troubleshoot-latency"}]}
{"schema_version":2,"id":"operate/rs/databases/create","title":"Create a Redis Software database","url":"https://redis.io/docs/latest/operate/rs/databases/create/","summary":"Create a database with Redis Software.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"1a5f1b0640df8d6898cc02429a134f0c91b3f1910f2238ad8cc9fa4eabf203fa","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software lets you create databases and distribute them across a cluster of nodes using the [Cluster Manager UI](#cluster-manager-ui-method) or the [REST API](#rest-api-method)."},{"id":"cluster-manager-ui-method","title":"Cluster Manager UI method","role":"content","text":"To create a new database using the Cluster Manager UI:\n\n1. Sign in to the Cluster Manager UI at `https://<hostname>:8443`\n\n1. Use one of the following methods to create a new database:\n\n    - [Quick database](#quick-database)\n\n    - [Create database with additional configuration](#create-db-ui)\n\n1. If you did not specify a port number for the database, you can find the port number in the **Endpoint** field in the **Databases > Configuration > General** section.\n\n1. [Test client connectivity](https://redis.io/docs/latest/operate/rs/databases/connect/test-client-connectivity).\n\n\n> [!NOTE]\n> For databases with Active-Active replication for geo-distributed locations,\n> see [Create an Active-Active database](https://redis.io/docs/latest/operate/rs/databases/active-active/create). To create and manage Active-Active databases, use the legacy UI."},{"id":"quick-database","title":"Quick database","role":"content","text":"To quickly create a database and skip additional configuration options during initial creation:\n\n1. On the **Databases** screen, select **Quick database**.\n\n1. Select a Redis version from the **Database version** list.\n\n1. Configure settings that are required for database creation but can be changed later:\n\n    - Database name\n\n    - Memory limit (GB)\n\n2. Configure optional settings that can't be changed after database creation:\n\n    - Endpoint port (set by the cluster if not set manually)\n\n    - Capabilities (previously modules) to enable\n\n        Databases created with Redis version 8 or later automatically enable the capabilities (modules) bundled with Redis Software as follows:\n\n        | Database type | Automatically enabled capabilities |\n|---------------|------------------------------------|\n| RAM-only | [Redis Search](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search)<br />[JSON](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json)<br />[Time series](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/timeseries)<br />[Probabilistic](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/bloom)  |\n| Flash-enabled ([Redis Flex](https://redis.io/docs/latest/operate/rs/databases/flash)) | [JSON](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json)<br />[Probabilistic](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/bloom) |\n| [Active-Active](https://redis.io/docs/latest/operate/rs/databases/active-active)<sup>[1](#enabled-modules-table-note-1)</sup> | [Redis Search](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/search-active-active)<br />[JSON](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json) |\n\n1. <a name=\"enabled-modules-table-note-1\"></a>Upgrading existing Active-Active databases to Redis version 8 does not automatically enable these capabilities. Only new Active-Active databases created with Redis version 8 enable these capabilities by default.\n\n1. Optionally select **Full options** to configure [additional settings](https://redis.io/docs/latest/operate/rs/databases/configure#config-settings).\n\n1. Select **Create**."},{"id":"create-db-ui","title":"Create database with additional configuration","role":"content","text":"To create a new database and configure additional settings:\n\n1. Open the **Create database** menu with one of the following methods:\n\n    - Click the **+** button next to **Databases** in the navigation menu:\n\n        ![Create database menu has two options: Single Region and Active-Active database.](https://redis.io/docs/latest/images/rs/screenshots/databases/create-db-plus-drop-down.png)\n        \n    - Go to the **Databases** screen and select **Create database**:\n\n        ![Create database menu has two options: Single Region and Active-Active database.](https://redis.io/docs/latest/images/rs/screenshots/databases/create-db-button-drop-down.png)\n\n1. Select the database type:\n\n    - **Single Region**\n\n    - **Active-Active database** - Multiple participating Redis Software clusters can host instances of the same [Active-Active database](https://redis.io/docs/latest/operate/rs/databases/active-active) in different geographic locations. Every instance can receive write operations, which are synchronized across all instances without conflict.\n\n    > [!NOTE]\n    > For Active-Active databases, see [Create an Active-Active geo-replicated database](https://redis.io/docs/latest/operate/rs/databases/active-active/create).\n    >\n\n1. Select a Redis version from the **Database version** list.\n\n1. Enter a **Database name**.\n\n    - Maximum of 63 characters\n\n    - Only letters, numbers, or hyphens (-) are valid characters\n\n    - Must start and end with a letter or digit\n\n    - Case-sensitive\n\n1. To configure additional database settings, expand each relevant section to make changes.\n\n    See [Configuration settings](https://redis.io/docs/latest/operate/rs/databases/configure#config-settings) for more information about each setting.\n\n1. Select **Create**."},{"id":"rest-api-method","title":"REST API method","role":"content","text":"To [create a database](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#post-bdbs-v1) using a REST API request:\n\n[code example]\n\nFor additional database configuration fields, see the [BDB object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb) reference."},{"id":"continue-learning-with-redis-university","title":"Continue learning with Redis University","role":"content","text":""}],"examples":[{"id":"rest-api-method-ex0","language":"sh","code":"POST https://<host>:<port>/v1/bdbs\n{\n    \"name\": \"test-database\",\n    \"type\": \"redis\",\n    \"memory_size\": 1073741824,\n    // Additional fields\n}","section_id":"rest-api-method"}]}
{"schema_version":2,"id":"operate/rs/databases/delete","title":"Delete databases","url":"https://redis.io/docs/latest/operate/rs/databases/delete/","summary":"Delete a database from the Cluster Manager UI.","tags":["docs","operate","rs"],"last_updated":"2026-10-02T16:06:07+01:00","page_type":"content","content_hash":"9b49cc01b916e01fd1278db9ceaea735c4cd614004280ae87a62ed939a595778","sections":[{"id":"content","title":"Content","role":"content","text":"When you delete a database, both the database configuration and data are removed.\n\nTo delete a database from the Cluster Manager UI:\n\n1. From the **Databases** list, select the database, then select **Configuration**.\n\n1. Select ![Delete button](https://redis.io/docs/latest/images/rs/icons/delete-icon.png#no-click) **Delete**.\n\n1. In the **Delete database** dialog, confirm deletion."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/databases/durability-ha/clustering","title":"Database clustering","url":"https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering/","summary":"Clustering to allow customers to spread the load of a Redis process over multiple cores and the RAM of multiple servers.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:24+01:00","page_type":"content","content_hash":"a25a4abb466c9c383ce3a18c3e8ca6fc768a0d28bf71274310f9e7c4a9da5dc8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Source available [Redis](https://redis.io/docs/latest/get-started/) is a single-threaded process\nto provide speed and simplicity.\nA single Redis process is bound by the CPU core that it is running on and available memory on the server.\n\nRedis Software supports database clustering to allow customers\nto spread the load of a Redis process over multiple cores and the RAM of multiple servers.\nA database cluster is a set of Redis processes where each process manages a subset of the database keyspace.\n\nThe keyspace of a Redis Software cluster is partitioned into database shards.\nEach shard resides on a single node and is managed by that node.\nEach node in a Redis database cluster can manage multiple shards.\nThe key space in the shards is divided into hash slots.\nThe slot of a key is determined by a hash of the key name or part of the key name.\n\nDatabase clustering is transparent to the Redis client that connects to the database.\nThe Redis client accesses the database through a single endpoint that automatically routes all operations to the relevant shards.\nYou can connect an application to a single Redis process or a clustered database without any difference in the application logic."},{"id":"terminology","title":"Terminology","role":"content","text":"In clustering, these terms are commonly used:\n\n- Tag or Hash Tag - A part of the key that is used in the hash calculation.\n- Slot or Hash Slot - The result of the hash calculation.\n- Shard - Redis process that is part of the Redis clustered database."},{"id":"when-to-use-clustering-sharding","title":"When to use clustering (sharding)","role":"content","text":"Clustering is an efficient way of scaling Redis that should be used when:\n\n- The dataset is large enough to benefit from using the RAM resources of more than one node.\n    When a dataset is more than 25 GB (50 GB for RoF), we recommend that you enable clustering to create multiple shards of the database\n    and spread the data requests across nodes.\n- The operations performed against the database are CPU-intensive, resulting in performance degradation.\n    By having multiple CPU cores manage the database's shards, the load of operations is distributed among them."},{"id":"number-of-shards","title":"Number of shards","role":"content","text":"When enabling database clustering, you can set the number of database\nshards. The minimum number of shards per database is 2 and the maximum\ndepends on the subscription you purchased.\n\nAfter you enable database clustering and set the number of shards, you cannot deactivate database clustering or reduce the number of\nshards. You can only increase the number of shards by a multiple of the\ncurrent number of shards. For example, if the current number of shards\nis 3, you can increase the number of shards to 6, 9, or 12."},{"id":"supported-hashing-policies","title":"Supported hashing policies","role":"compatibility","text":""},{"id":"standard-hashing-policy","title":"Standard hashing policy","role":"content","text":"When using the standard hashing policy, a clustered Redis Software database behaves similarly to a standard [Redis Open Source cluster](https://redis.io/docs/latest/operate/oss_and_stack/reference/cluster-spec#hash-tags), except when using multiple hash tags in a key's name. We recommend using only a single hash tag in a key name for hashing in Redis Software.\n\n- **Keys with a hash tag**: a key's hash tag is any substring between\n    `{` and `}` in the key's name. When a key's name\n    includes the pattern `{...}`, the hash tag is used as input for the\n    hashing function.\n    \n    For example, the following key names have the same\n    hash tag and map to the same hash slot: `foo{bar}`,\n    `{bar}baz`, and `foo{bar}baz`.\n\n- **Keys without a hash tag**: when a key does not contain the `{...}`\n    pattern, the entire key's name is used for hashing.\n\nYou can use a hash tag to store related keys in the same hash\nslot so multi-key operations can run on these keys. If you do not use a hash tag in the key's name, the keys are distributed evenly across the keyspace's shards.\nIf your application does not perform multi-key operations, you do not\nneed to use hash tags."},{"id":"custom-hashing-policy","title":"Custom hashing policy","role":"content","text":"You can configure a custom hashing policy for a clustered database. A\ncustom hashing policy is required when different keys need to be kept\ntogether on the same shard to allow multi-key operations. The custom\nhashing policy is provided through a set of Perl Compatible Regular\nExpressions (PCRE) rules that describe the dataset's key name patterns.\n\nTo configure a custom hashing policy, enter the regular expression\n(RegEx) rules that identify the substring in the key's name - hash tag\n-- on which hashing is done. The hash tag is denoted in the\nRegEx by the use of the \\`tag\\` named subpattern. Different keys that\nhave the same hash tag are stored and managed in the same slot.\n\nAfter you enable the custom hashing policy, the following default RegEx\nrules are implemented. Update these rules to fit your specific logic:\n\n|  RegEx Rule | Description |\n|  ------ | ------ |\n|  .\\*{(?\\<tag\\>.\\*)}.\\* | Hashing is done on the substring between the curly braces. |\n|  (?\\<tag\\>.\\*) | The entire key's name is used for hashing. |\n\nYou can modify existing rules, add new ones, delete rules, or change\ntheir order to suit your application's requirements."},{"id":"custom-hashing-policy-notes-and-limitations","title":"Custom hashing policy notes and limitations","role":"content","text":"1. You can define up to 32 RegEx rules, each up to 256 characters.\n2. RegEx rules are evaluated in order, and the first rule matched\n    is used. Therefore, you should place common key name patterns at the\n    beginning of the rule list.\n3. Key names that do not match any of the RegEx rules trigger an\n    error.\n4. The '.\\*(?\\<tag\\>)' RegEx rule forces keys into a single slot\n    because the hash key is always empty. Therefore, when used,\n    this should be the last, catch-all rule.\n5. The following flag is enabled in the regular expression parser:\n    PCRE_ANCHORED: the pattern is constrained to match only at the\n    start of the string being searched."},{"id":"change-the-hashing-policy","title":"Change the hashing policy","role":"content","text":"The hashing policy of a clustered database can be changed. However,\nmost hashing policy changes trigger the deletion (FLUSHDB) of the\ndata before they can be applied.\n\nExamples of such changes include:\n\n- Changing the hashing policy from standard to custom or conversely,\n    custom to standard.\n- Changing the order of custom hashing policy rules.\n- Adding new rules in the custom hashing policy.\n- Deleting rules from the custom hashing policy.\n\n> [!NOTE]\n> The recommended workaround for updates that are not enabled,\n> or require flushing the database,\n> is to back up the database and import the data to a newly configured database."},{"id":"multikey-operations","title":"Multi-key operations","role":"content","text":"Operations on multiple keys in a clustered database are supported with\nthe following limitations:\n\n- **Multi-key commands**: Redis offers several commands that accept\n    multiple keys as arguments. In a clustered database, most multi-key\n    commands are not allowed across slots. The following multi-key\n    commands **are allowed** across slots: DEL, MSET, MGET, EXISTS, UNLINK, TOUCH\n\n    In Active-Active databases, multi-key write commands (DEL, MSET, UNLINK) can only be run on keys that are in the same slot. However, the following multi-key commands **are allowed** across slots in Active-Active databases: MGET, EXISTS, and TOUCH.\n\n    Commands that affect all keys or keys that match a specified pattern are allowed\n    in a clustered database, for example: FLUSHDB, FLUSHALL, KEYS\n\n    > [!NOTE]\n    > When using these commands in a sharded setup,\n    > the command is distributed across multiple shards\n    > and the responses from all shards are combined into a single response.\n    >\n\n- **Geo commands**: For the [GEORADIUS](https://redis.io/docs/latest/commands/georadius) and\n    [GEORADIUSBYMEMBER](https://redis.io/docs/latest/commands/georadiusbymember) commands, the\n    STORE and STOREDIST options can only be used when all affected keys\n    reside in the same slot.\n- **Transactions**: All operations within a WATCH / MULTI / EXEC block\n    should be performed on keys that are mapped to the same slot.\n- **Lua scripts**: All keys used by a Lua script must be mapped to the same\n    slot and must be provided as arguments to the EVAL / EVALSHA commands\n    (as per the Redis specification). Using keys in a Lua script that\n    were not provided as arguments might violate the sharding concept\n    but do not result in the proper violation error being returned.\n- **Renaming/Copy keys**: The use of the RENAME / RENAMENX / COPY commands is\n    allowed only when the key's original and new values are mapped to\n    the same slot."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/databases/durability-ha/consistency","title":"Consistency during replication","url":"https://redis.io/docs/latest/operate/rs/databases/durability-ha/consistency/","summary":"Explains the order write operations are communicated from app to proxy to shards for both non-blocking Redis write operations and blocking write operations on replication.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"2eb754e63020e4b5c00a8371873a4f4107ab7d341245a301a8f18b3bc7b26fd0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software comes with the ability to replicate data\nto another database instance for high availability and persist in-memory data on\ndisk permanently for durability. With the [`WAIT`](https://redis.io/docs/latest/commands/wait) command, you can\ncontrol the consistency and durability guarantees for the replicated and\npersisted database."},{"id":"non-blocking-redis-write-operation","title":"Non-blocking Redis write operation","role":"content","text":"Any updates that are issued to the database are typically performed with the following flow:\n\n1. The application issues a write.\n2. The proxy communicates with the correct primary (also known as master) shard in the system that contains the given key.\n3. The shard writes the data and sends an acknowledgment to the proxy.\n4. The proxy sends the acknowledgment back to the application.\n5. The write is communicated from the primary shard to the replica.\n6. The replica acknowledges the write back to the primary shard.\n7. The write to a replica is persisted to disk.\n8. The write is acknowledged within the replica.\n\n![](https://redis.io/docs/latest/images/rs/weak-consistency.png)"},{"id":"blocking-write-operation-on-replication","title":"Blocking write operation on replication","role":"content","text":"With the [`WAIT`](https://redis.io/docs/latest/commands/wait) or [`WAITAOF`](https://redis.io/docs/latest/commands/waitaof) commands, applications can ask to wait for\nacknowledgments only after replication or persistence is confirmed on\nthe replica. The flow of a write operation with `WAIT` or `WAITAOF` is:\n\n1. The application issues a write.\n2. The proxy communicates with the correct primary shard in the system that contains the given key.\n3. Replication communicates the update to the replica shard.\n4. If using `WAITAOF` and the AOF every write setting, the replica persists the update to disk before sending the acknowledgment.\n5. The acknowledgment is sent back from the replica all the way to the proxy with steps 5 to 8.\n\nThe application only gets the acknowledgment from the write after durability is achieved with replication to the replica for `WAIT` or `WAITAOF` and to the persistent storage for `WAITAOF` only.\n\n![](https://redis.io/docs/latest/images/rs/strong-consistency.png)\n\nThe `WAIT` command always returns the number of replicas that acknowledged the write commands sent by the current client before the `WAIT` command, both in the case where the specified number of replicas are reached, or when the timeout is reached. In Redis Software, the number of replicas for HA enabled databases is always 1.\n\nSee the [`WAITAOF`](https://redis.io/docs/latest/commands/waitaof) command for details for enhanced data safety and durability capabilities introduced with Redis 7.2."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/databases/durability-ha/discovery-service","title":"Discovery service","url":"https://redis.io/docs/latest/operate/rs/databases/durability-ha/discovery-service/","summary":"","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:24+01:00","page_type":"content","content_hash":"d86d57db0eb306b81dcc2e5156be4ff173b402970c2412a87c8c19ce18a667ce","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The Discovery Service provides an IP-based connection management service\nused when connecting to Redis Software databases. When used\nin conjunction with Redis Software's other high availability\nfeatures, the Discovery Service assists an application scope with\ntopology changes such as adding, removing of nodes, node failovers and\nso on. It does this by providing your application with the ability to\neasily discover which node hosts the database endpoint. The API used for\ndiscovery service is compliant with the Redis Sentinel API.\n\nDiscovery Service is an alternative for applications that do not want to\ndepend on DNS name resolution for their connectivity. Discovery Service\nand DNS based connectivity are not mutually exclusive. They can be used\nside by side in a given cluster where some clients can use Discovery\nService based connection while others can use DNS name resolution when\nconnecting to databases."},{"id":"how-discovery-service-works","title":"How discovery service works","role":"content","text":"The Discovery Service is available for querying on each node of the\ncluster, listening on port 8001. To employ it, your application utilizes\na [Redis Sentinel enabled client\nlibrary](https://redis.io/docs/latest/operate/rs/databases/connect/supported-clients-browsers)\nto connect to the Discovery Service and request the endpoint for the\ngiven database. The Discovery Service replies with the database's\nendpoint for that database. In case of a node failure, the Discovery\nService is updated by the cluster manager with the new endpoint and\nclients unable to connect to the database endpoint due to the failover,\ncan re-query the discovery service for the new endpoint for the\ndatabase.\n\nThe Discovery Service can return either the internal or external\nendpoint for a database. If you query the discovery service for the\nendpoint of a database named \"db1\", the Discovery Service returns\nthe external endpoint information by default. If only an internal\nendpoint exists with no external endpoint the default behavior is to\nreturn the internal endpoint. The \"\\@internal\" is added to the end of\nthe database name to explicitly ask for the internal endpoint. to query\nthe internal endpoint explicitly with database name \"db1\", you can pass\nin the database name as \"db1\\@internal\".\n\nIf you'd like to examine the metadata returned from Redis Software Discovery Service you can connect to port 8001 with redis-cli\nutility and execute \"SENTINEL masters\". Following is a sample output\nfrom one of the nodes of a Redis Software cluster:\n\n[code example]\n\nIt is important to note that, the Discovery Service is not a full\nimplementation of the [Redis Sentinel\nprotocol](https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel). There are aspects of the\nprotocol that are not applicable or would be duplication with existing\ntechnology in Redis Software. The Discovery Service\nimplements only the parts required to provide applications with easy\nHigh Availability, be compatible with the protocol, and not rely on DNS\nto derive which node in the cluster to communicate with.\n\n> [!NOTE]\n> To use Redis Sentinel, every database name must be unique across the cluster."},{"id":"supported-sentinel-commands","title":"Supported Sentinel commands","role":"compatibility","text":"The Discovery Service implements only the subset of the [Redis Sentinel commands](https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel#sentinel-commands) needed for endpoint discovery and high availability. Commands in the open source Sentinel API that are not listed below are not supported.\n\n| Command | Description |\n|---------|-------------|\n| `SENTINEL MASTER <name>` | Show the state and info of the specified primary. |\n| `SENTINEL MASTERS` | Show a list of monitored primaries and their state. |\n| `SENTINEL SLAVES <name>` | Show a list of replicas for this primary, and their state. |\n| `SENTINEL SENTINELS <name>` | Show a list of Sentinel instances for this primary, and their state. |\n| `SENTINEL GET-MASTER-ADDR-BY-NAME <name>` | Return the IP and port of the primary with that name. |\n| `PING` | Returns `PONG`. |\n| `HELLO` | Switch the connection's protocol (RESP2/RESP3). |\n| `SUBSCRIBE` | Subscribe to Sentinel Pub/Sub event channels. |\n| `UNSUBSCRIBE` | Unsubscribe from Sentinel Pub/Sub event channels. |"},{"id":"sentinel-slaves-vs-sentinel-replicas","title":"`SENTINEL SLAVES` vs. `SENTINEL REPLICAS`","role":"content","text":"The Discovery Service supports `SENTINEL SLAVES` to list a primary's replicas. It does **not** support `SENTINEL REPLICAS`, the command that open source Redis introduced in Redis 5.0 as the preferred name (`SLAVES` is retained in open source only as a backward-compatible alias).\n\nThis matters for client compatibility: a client that issues `SENTINEL REPLICAS` works against open source Redis Sentinel but returns an error against the Redis Software Discovery Service. If your client supports only `SENTINEL REPLICAS`, configure it to use `SENTINEL SLAVES`, or use a client that issues `SENTINEL SLAVES`, when connecting to the Discovery Service."},{"id":"redis-client-support","title":"Redis client support","role":"content","text":"All [recommended Redis client libraries](https://redis.io/docs/latest/develop/clients) support the Redis Sentinel API, so you can use any of them with the discovery service.\n\nIf you need to use a client that doesn't support Sentinel, you can use [Sentinel Tunnel](https://github.com/RedisLabs/sentinel_tunnel) to discover the current primary Redis endpoint with Sentinel and create a TCP tunnel between a local port on the client and the primary endpoint.\n\n> [!NOTE]\n> Redis Sentinel API can return endpoints for both primary and replica\n> endpoints.\n> Discovery Service only supports primary endpoints and does not\n> support returning replica endpoints for a database."}],"examples":[{"id":"how-discovery-service-works-ex0","language":"sh","code":"$ ./redis-cli -p 8001\n127.0.0.1:8001> SENTINEL masters\n1) 1) \"name\"\n2) \"db1@internal\"\n3) \"ip\"\n4) \"10.0.0.45\"\n5) \"port\"\n6) \"12000\"\n7) \"flags\"\n8) \"master,disconnected\"\n9) \"num-other-sentinels\"\n10) \"0\"\n2) 1) \"name\"\n2) \"db1\"\n3) \"ip\"\n4) \"10.0.0.45\"\n5) \"port\"\n6) \"12000\"\n7) \"flags\"\n8) \"master,disconnected\"\n9) \"num-other-sentinels\"\n10) \"0\"","section_id":"how-discovery-service-works"}]}
{"schema_version":2,"id":"operate/rs/databases/durability-ha","title":"Durability and high availability","url":"https://redis.io/docs/latest/operate/rs/databases/durability-ha/","summary":"Overview of Redis Software durability features such as replication, clustering, and rack-zone awareness.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:24+01:00","children":[{"id":"operate/rs/databases/durability-ha/clustering","summary":"Clustering to allow customers to spread the load of a Redis process over multiple cores and the RAM of multiple servers.","title":"Database clustering","url":"https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering/"},{"id":"operate/rs/databases/durability-ha/consistency","summary":"Explains the order write operations are communicated from app to proxy to shards for both non-blocking Redis write operations and blocking write operations on replication.","title":"Consistency during replication","url":"https://redis.io/docs/latest/operate/rs/databases/durability-ha/consistency/"},{"id":"operate/rs/databases/durability-ha/discovery-service","summary":"","title":"Discovery service","url":"https://redis.io/docs/latest/operate/rs/databases/durability-ha/discovery-service/"},{"id":"operate/rs/databases/durability-ha/replication","summary":"","title":"Database replication","url":"https://redis.io/docs/latest/operate/rs/databases/durability-ha/replication/"}],"page_type":"content","content_hash":"38b66e62176886389e54730b889d7fd2dbd512a17e4652537428b57bd9a7ca4c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software comes with several features that make your data more durable and accessible. The following features can help protect your data in cases of failures or outages and help keep your data available when you need it."},{"id":"replication","title":"Replication","role":"content","text":"When you [replicate your database](https://redis.io/docs/latest/operate/rs/databases/durability-ha/replication), each database instance (primary shard) is copied to a replica shard. When a primary shard fails, the cluster automatically promotes a replica shard to primary."},{"id":"clustering","title":"Clustering","role":"content","text":"[Clustering](https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering) (or sharding) breaks your database into individual instances (shards) and spreads them across several nodes. Clustering lets you add resources to your cluster to scale your database and prevents node failures from causing availability loss."},{"id":"database-persistence","title":"Database persistence","role":"content","text":"[Database persistence](https://redis.io/docs/latest/operate/rs/databases/configure/database-persistence) gives your database durability against process or server failures by saving data to disk at set intervals."},{"id":"active-active-geo-distributed-replication","title":"Active-Active geo-distributed replication","role":"content","text":"[Active-Active Redis Software databases](https://redis.io/docs/latest/operate/rs/databases/active-active) allow reading and writing to the same dataset from multiple clusters in different geographic locations. This increases the durability of your database by reducing the likelihood of data or availability loss. It also reduces data access latency by serving requests from the nearest cluster."},{"id":"rack-zone-awareness","title":"Rack-zone awareness","role":"content","text":"[Rack-zone awareness](https://redis.io/docs/latest/operate/rs/clusters/configure/rack-zone-awareness) maps each node in your Redis Software cluster to a physical rack or logical zone. The cluster uses this information to distribute primary shards and their replica shards in different racks or zones. This ensures data availability if a rack or zone fails."},{"id":"discovery-service","title":"Discovery service","role":"content","text":"The [discovery service](https://redis.io/docs/latest/operate/rs/databases/durability-ha/discovery-service) provides an IP-based connection management service used when connecting to Redis Software databases. It lets your application discover which node hosts the database endpoint. The discovery service API complies with the [Redis Sentinel API](https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel#sentinel-api)."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/databases/durability-ha/replication","title":"Database replication","url":"https://redis.io/docs/latest/operate/rs/databases/durability-ha/replication/","summary":"","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:24+01:00","page_type":"content","content_hash":"a6598cb9d37b0bee7ebf14dbfde2f64166f3acc2e20fae7caa54131b521e796a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Database replication helps ensure high availability.\nWhen replication is enabled, your dataset is replicated to a replica shard,\nwhich is constantly synchronized with the primary shard. If the primary \nshard fails, an automatic failover happens and the replica shard is promoted.  That is, it becomes the new primary shard. \n\nWhen the old primary shard recovers, it becomes\nthe replica shard of the new primary shard. This auto-failover mechanism\nguarantees that data is served with minimal interruption.\n\nYou can tune your high availability configuration with:\n\n- [Rack/Zone\nAwareness](https://redis.io/docs/latest/operate/rs/clusters/configure/rack-zone-awareness) - When rack-zone awareness is used additional logic ensures that master and replica shards never share the same rack, thus ensuring availability even under loss of an entire rack.\n- [High Availability for Replica Shards](https://redis.io/docs/latest/operate/rs/databases/configure/replica-ha) - When high availability\nfor replica shards is used, the replica shard is automatically migrated on node failover to maintain high availability.\n\n> [!WARNING]\n> Enabling replication has implications for the total database size,\n> as explained in [Database memory limits](https://redis.io/docs/latest/operate/rs/databases/memory-performance/memory-limit)."},{"id":"auto-tiering-replication-considerations","title":"Auto Tiering replication considerations","role":"content","text":"We recommend that you set the sequential replication feature using\n`rladmin`. This is due to the potential for relatively slow replication\ntimes that can occur with Auto Tiering enabled databases. In some\ncases, if sequential replication is not set up, you may run out of memory. \n\nWhile it does not cause data loss on the\nprimary shards, the replication to replica shards may not succeed as long\nas there is high write-rate traffic on the primary and multiple\nreplications at the same time.\n\nThe following `rladmin` command sets the number of primary shards eligible to\nbe replicated from the same cluster node, as well as the number of replica\nshards on the same cluster node that can run the replication process at\nany given time.\n\nThe recommended sequential replication configuration is two, i.e.:\n\n[code example]\n\n> [!NOTE]\n> This means that at any given time,\n> only one primary and one replica can be part of a full sync replication process."},{"id":"database-replication-backlog","title":"Database replication backlog","role":"content","text":"Redis databases that use [replication for high availability](https://redis.io/docs/latest/operate/rs/databases/durability-ha/replication) maintain a replication backlog (per shard) to synchronize the primary and replica shards of a database.\nBy default, the replication backlog is set to one percent (1%) of the database size divided by the database number of shards and ranges between 1MB to 250MB per shard.\nUse the [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin) and the [`crdb-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli) utilities to control the size of the replication backlog. You can set it to `auto` or set a specific size.  \n\nThe syntax varies between regular and Active-Active databases. \n\nFor a regular Redis database:\n[code example]\n\nFor an Active-Active database:\n[code example]"},{"id":"active-active-replication-backlog","title":"Active-Active replication backlog","role":"content","text":"In addition to the database replication backlog, Active-Active databases maintain a backlog (per shard) to synchronize the database instances between clusters.\nBy default, the Active-Active replication backlog is set to one percent (1%) of the database size divided by the database number of shards, and ranges between 1MB to 250MB per shard.\nUse the [`crdb-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli) utility to control the size of the CRDT replication backlog. You can set it to `auto` or set a specific size:  \n\n[code example]\n\n**For Redis Software versions earlier than 6.0.20:**\nThe replication backlog and the CRDT replication backlog defaults are set to 1MB and cannot be set dynamically with 'auto' mode.\nTo control the size of the replication log, use [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin) to tune the local database instance in each cluster.\n[code example]"}],"examples":[{"id":"auto-tiering-replication-considerations-ex0","language":"sh","code":"rladmin tune cluster max_redis_forks 1 max_slave_full_syncs 1","section_id":"auto-tiering-replication-considerations"},{"id":"database-replication-backlog-ex0","language":"text","code":"rladmin tune db <db:id | name> repl_backlog <Backlog size in MB | 'auto'>","section_id":"database-replication-backlog"},{"id":"database-replication-backlog-ex1","language":"text","code":"crdb-cli crdb update --crdb-guid <crdb_guid> --default-db-config \"{\\\"repl_backlog_size\\\": <size in MB | 'auto'>}\"","section_id":"database-replication-backlog"},{"id":"active-active-replication-backlog-ex0","language":"text","code":"crdb-cli crdb update --crdb-guid <crdb_guid> --default-db-config \"{\\\"crdt_repl_backlog_size\\\": <size in MB | 'auto'>}\"","section_id":"active-active-replication-backlog"},{"id":"active-active-replication-backlog-ex1","language":"text","code":"rladmin tune db <db:id | name> repl_backlog <Backlog size in MB (or if ending with bytes, KB or GB, in the respective unit)>","section_id":"active-active-replication-backlog"}]}
{"schema_version":2,"id":"operate/rs/databases/flash","title":"Flex and Auto Tiering","url":"https://redis.io/docs/latest/operate/rs/databases/flash/","summary":"Flex and Auto Tiering enable your data to span both RAM and dedicated flash memory.","aliases":["/operate/rs/databases/auto-tiering/"],"tags":["docs","operate","rs","rc"],"last_updated":"2026-09-24T09:16:24+01:00","children":[{"id":"operate/rs/databases/flash/quickstart","summary":"Get started with Flex and Auto Tiering quickly, creating a cluster and database using flash storage.","title":"Flex and Auto Tiering quick start","url":"https://redis.io/docs/latest/operate/rs/databases/flash/quickstart/"},{"id":"operate/rs/databases/flash/storage-engine","summary":"Manage the storage engine used for your database with auto tiering enabled.","title":"Manage Auto Tiering storage engine","url":"https://redis.io/docs/latest/operate/rs/databases/flash/storage-engine/"}],"page_type":"content","content_hash":"0627993b447cf5b976d502d1b9d13a23b01be9d76dc7d9a7dfb1a366a5b721c2","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This article includes a general overview of Flex and its predecessor Auto Tiering. For more detailed information about Flex, see the dedicated [Flex databases](https://redis.io/docs/latest/operate/rs/flex) section instead.\n\n\n[Flex](https://redis.io/docs/latest/operate/rs/flex) and Auto Tiering in Redis Software enable databases to use solid state drives (SSDs) to extend beyond DRAM capacity.\nDevelopers can build applications that require large datasets using the same Redis API.\nUsing SSDs can significantly reduce the infrastructure costs compared to only DRAM deployments. \n\nFrequently used data, called hot data, belongs in the fastest memory level to deliver a real-time user experience.\nData that is accessed less frequently, called warm data, can be kept in a slightly slower memory tier.\nFlex maintains hot data in DRAM, keeps warm data in SSDs, and transfers data between tiers automatically.\n\nFlex is based on a high-performance storage engine (Speedb) that manages the complexity of using SSDs and DRAM as the total available memory for databases in a Redis Software cluster. This implementation offers a performance boost of up to 10k operations per second per core of the database, doubling the performance of Redis on Flash.\n\nJust like all-RAM databases, Flex databases are compatible with existing Redis applications.\n\nFlex is also supported on [Redis Cloud](https://redis.io/docs/latest/operate/rc) and [Redis Enterprise Software for Kubernetes](https://redis.io/docs/latest/operate/kubernetes/flex)."},{"id":"flex-versus-auto-tiering","title":"Flex versus Auto Tiering","role":"content","text":"Flex is the enhanced successor to Auto Tiering, generally available as of Redis Software version 8.0.2.\n\nRedis database versions support Flex and Auto Tiering as follows:\n\n| Redis database version | Flex | Auto Tiering |\n|------------------------|------------|--------------|\n| 8.0 and later | <span title=\"Supported\">&#x2705;</span> | <span title=\"Not supported\">&#x274c;</span> |\n| 7.4 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span>|\n| 7.2 and earlier | <span title=\"Not supported\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> |\n\nFlex requires the Speedb driver, while Auto Tiering can use either RocksDB or Speedb. See [Manage Auto Tiering storage engine](https://redis.io/docs/latest/operate/rs/databases/flash/storage-engine) for more information."},{"id":"use-cases","title":"Use cases","role":"content","text":"The benefits associated with Flex and Auto Tiering are dependent on the use case.\n\nFlex and Auto Tiering are ideal when your:\n\n- working set is significantly smaller than your dataset (high RAM hit rate)\n- average key size is smaller than average value size\n- most recent data is the most frequently used (high RAM hit rate)\n\nFlex and Auto Tiering are not recommended for:\n\n- A large number of keys or long key names. With Auto Tiering, all key names are stored in RAM regardless of whether their values are on flash. High key count alone can exhaust the RAM limit even when individual key names are short.\n- Broad access patterns (any value could be pulled into RAM).\n- Large working sets (working set is stored in RAM).\n- Frequently moved data (moving to and from RAM too often can impact performance).\n- Large collection types (hashes, sets, or lists with millions of elements) whose total serialized size approaches or exceeds the RAM limit. Unlike scalar values, large collections cannot be partially offloaded to flash and must fit in RAM when accessed.\n\nFlex and Auto Tiering are not intended to be used for persistent storage. Redis Software database persistent and ephemeral storage should be on different disks, either local or attached."},{"id":"where-is-my-data","title":"Where is my data?","role":"content","text":"When using Flex, RAM storage holds:\n- Key indexes\n- Dictionaries\n- Hot data (working set), including frequently accessed keys and values\n\nAll data is accessed through RAM. If a key or value in flash memory is accessed, it becomes part of the working set and is moved to RAM. This data is referred to as \"hot data\".\n\nInactive or infrequently accessed data is referred to as \"warm data\" and stored in flash memory. When more space is needed in RAM, warm keys and values are moved from RAM to flash storage.\n\n> [!NOTE]\n>  When using Auto Tiering with Redis Search, indexes are also stored in RAM."},{"id":"ram-to-flash-ratio","title":"RAM to Flash ratio","role":"content","text":"Redis Software allows you to configure and tune the ratio of RAM-to-Flash for each database independently, optimizing performance for your specific use case.\nWhile this is an online operation requiring no downtime for your database, it is recommended to perform it during maintenance windows as data might move between tiers (RAM <-> Flash).\n\nThe RAM limit cannot be smaller than 10% of the total memory. We recommend you keep at least 20% of all values in RAM. Do not set the RAM limit to 100%."},{"id":"flash-memory","title":"Flash memory","role":"content","text":"Implementing Flex requires pre planning around memory and sizing. Considerations and requirements for Flex include:\n\n- Flash memory must be locally attached. Using network-attached storage (NAS), storage area networks (SAN), or solutions such as AWS Elastic Block Storage (EBS) is not supported. \n- Flash memory must be dedicated to Flex and not shared with other parts of the database, such as durability, binaries, or persistence.\n- For the best performance, the SSDs should be NVMe based, but SATA can also be used.\n- For Auto Tiering, the available flash space must be greater than or equal to the total database size (RAM+Flash). The extra space accounts for write buffers and [write amplification](https://en.wikipedia.org/wiki/Write_amplification).\n- For Flex, flash space should be approximately three times the total memory limit of all Flex databases on the node. Because you can increase a database's memory limit after creation, size flash space for the expected peak memory limit.\n\n> [!NOTE]\n>  The Redis Software database persistent and ephemeral storage should be on different disks, either local or attached. \n\nAfter these requirements are met, you can create and manage both Flex databases and\nall-RAM databases in the same cluster.\n\nWhen you begin planning the deployment of a Flex database in production,\nwe recommend working closely with the Redis technical team for sizing and performance tuning."},{"id":"cloud-environments","title":"Cloud environments","role":"content","text":"When running in a cloud environment:\n\n- Flash memory is on the ephemeral SSDs of the cloud instance (for example the local NVMe of AWS i4i instances and Azure Lsv2 and Lsv3 series).\n- Persistent database storage needs to be network attached (for example, AWS EBS for AWS).\n\n> [!NOTE]\n> We specifically recommend \"[Storage Optimized I4i - High I/O Instances](https://aws.amazon.com/ec2/instance-types/#storage-optimized)\" because of the performance of NVMe for flash memory."},{"id":"on-premises-environments","title":"On-premises environments","role":"content","text":"When you begin planning the deployment of Flex in production, we recommend working closely with the Redis technical team for sizing and performance tuning.\n\nOn-premises environments support more deployment options than other environments.\n\nThe following table shows which data types and features are supported for Flex and Auto Tiering:\n\n| Data type/feature | Flex support | Auto Tiering support |\n|-------------------|--------------|----------------------|\n| [Active-Active databases](https://redis.io/docs/latest/operate/rs/databases/active-active) | <span title=\"Not supported\">&#x274c;</span> |<span title=\"Supported\">&#x2705;</span> Requires Redis<br />technical team's approval |\n| [JSON](https://redis.io/docs/latest/develop/data-types/json) | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| [Probabilistic data structures](https://redis.io/docs/latest/develop/data-types#probabilistic-data-types) | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| [Redis Search](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search) | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Standard [Redis data types](https://redis.io/docs/latest/develop/data-types) | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| [Time series](https://redis.io/docs/latest/develop/data-types/timeseries) | <span title=\"Not supported\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n> [!WARNING]\n> Flex is not supported running on network attached storage (NAS), storage area network (SAN), or with local HDD drives."},{"id":"size-limits-for-keys-and-values","title":"Size limits for keys and values","role":"content","text":"Flex databases cannot store keys or values larger than 4GB in flash storage.\n\nKeys or values larger than 4GB will be stored in RAM only, and warnings will appear in the Redis logs similar to:\n\n[code example]\n\nIf oversized keys consume the shard's available RAM, the shard can return out-of-memory errors even when flash storage has free space remaining."},{"id":"next-steps","title":"Next steps","role":"content","text":"- [Get started with Flex databases for Redis Software](https://redis.io/docs/latest/operate/rs/flex/get-started)\n\n- [Auto Tiering quick start](https://redis.io/docs/latest/operate/rs/databases/flash/quickstart)\n\n- [Ephemeral and persistent storage](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/persistent-ephemeral-storage)\n\n- [Plan a Flex deployment for Redis Software](https://redis.io/docs/latest/operate/rs/flex/plan)\n\n- [Hardware requirements](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/hardware-requirements)\n\n- [Flex and Auto Tiering metrics](https://redis.io/docs/latest/operate/rs/references/metrics/auto-tiering)"}],"examples":[{"id":"size-limits-for-keys-and-values-ex0","language":"sh","code":"# WARNING: key too big for disk driver, size: 4703717276, key: subactinfo:htable","section_id":"size-limits-for-keys-and-values"}]}
{"schema_version":2,"id":"operate/rs/databases/flash/quickstart","title":"Flex and Auto Tiering quick start","url":"https://redis.io/docs/latest/operate/rs/databases/flash/quickstart/","summary":"Get started with Flex and Auto Tiering quickly, creating a cluster and database using flash storage.","aliases":["/operate/rs/databases/auto-tiering/quickstart/"],"tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"8537a1261cd79ee58a8d33e4642c577b8aaf9284fc720335b1b95fcbad662efb","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This article includes a quick start for Flex and its predecessor Auto Tiering. For more detailed information about Flex, see the dedicated [Flex databases](https://redis.io/docs/latest/operate/rs/flex) section instead.\n\n\nThis page guides you through a quick setup of [Flex and Auto Tiering](https://redis.io/docs/latest/operate/rs/databases/flash) with a single node for testing and demo purposes.\n\nFor production environments, you can find more detailed installation instructions in the [install and setup](https://redis.io/docs/latest/operate/rs/installing-upgrading) section.\n\nThe steps to set up a Redis Software cluster using Flex\nwith a single node are:\n\n1. Install Redis Software or run it in a Docker\n    container.\n1. Set up a Redis Software cluster with Flex.\n1. Create a new database with Flex enabled.\n1. Connect to your new database."},{"id":"before-deployment","title":"Before deployment","role":"content","text":"Before you set up a flash-enabled cluster and create a Flex or Auto Tiering database, consider the following:\n\n- Avoid a large number of keys or long key names. With Auto Tiering, all key names are stored in RAM regardless of whether their values are on flash. High key count alone can exhaust the RAM limit even when individual key names are short.\n\n- Avoid large collection types (hashes, sets, or lists with millions of elements) whose total serialized size approaches or exceeds the RAM limit. Unlike scalar values, large collections cannot be partially offloaded to flash and must fit in RAM when accessed.\n\n- Databases cannot store keys or values larger than 4GB in flash storage. Keys or values larger than 4GB are stored in RAM only. If oversized keys consume the shard's available RAM, the shard can return out-of-memory errors even when flash storage has free space remaining."},{"id":"install-redis-software","title":"Install Redis Software","role":"setup","text":""},{"id":"bare-metal-vm-cloud-instance","title":"Bare metal, VM, Cloud instance","role":"content","text":"To install on bare metal, a virtual machine, or an instance:\n\n1. Download the binaries from the [Redis Software download center](https://cloud.redis.io/#/sign-up/software?direct=true).\n\n1. Upload the binaries to a Linux-based operating system.\n\n1. Extract the image:\n\n    [code example]\n\n1. After the `tar` command completes, you can find a new `install.sh` script in the current directory:\n\n    [code example]"},{"id":"dockerbased-installation","title":"Docker-based installation","role":"content","text":"For testing purposes, you can run a Redis Software\nDocker container on Windows, MacOS, and Linux.\n\n[code example]"},{"id":"prepare-and-format-flash-memory","title":"Prepare and format flash memory","role":"content","text":"After you [install Redis Software](#install-redis-enterprise-software), use the `prepare_flash` script to prepare and format flash memory:\n\n[code example]\n\nThis script finds unformatted disks and mounts them as RAID partitions in `/var/opt/redislabs/flash`.\n\nTo verify the disk configuration, run:\n\n[code example]"},{"id":"set-up-a-clusterand-enable-flex","title":"Set up a cluster and enable Flex","role":"content","text":"1. Direct your browser to `https://localhost:8443` on the host machine to\nsee the Redis Software Cluster Manager UI.\n\n    > [!NOTE]\n    > Depending on your browser, you may see a certificate error.\n    > Choose \"continue to the website\" to go to the setup screen.\n    >\n\n1. Select **Create new cluster**.\n\n1. Set up account credentials for a cluster administrator, then select **Next** to proceed to cluster setup.\n\n1. Enter your cluster license key if you have one. Otherwise, the cluster uses the trial version.\n\n1. Provide a cluster FQDN such as `mycluster.local`, then select **Next**.\n\n1. In the **Storage configuration** section, turn on the **Enable flash storage** toggle.\n\n1. Select **Create cluster**.\n\n1. Select **OK** to confirm that you are aware of the replacement of the HTTPS TLS\ncertificate on the node, and proceed through the browser warning."},{"id":"create-a-database","title":"Create a database","role":"content","text":"On the **Databases** screen:\n\n1. Select **Quick database**.\n\n1. Verify **Flash** is selected for **Runs on**.\n\n    ![Create a quick database with Runs on Flash selected.](https://redis.io/docs/latest/images/rs/screenshots/databases/quick-db-flash-7-8-2.png)\n\n1. Enter `12000` for the endpoint **Port** number.\n\n1. _(Optional)_ Select **Full options** to see available alerts.\n\n1. Select **Create**.\n\nYou now have a database with Flex enabled!"},{"id":"connect-to-your-database","title":"Connect to your database","role":"content","text":"After you create the database, you can connect to it and store data. See [Test client connection](https://redis.io/docs/latest/operate/rs/databases/connect/test-client-connectivity) for connection options and examples."},{"id":"next-steps","title":"Next steps","role":"content","text":"To see the true performance and scale of Flex, you must tune your I/O path and set the flash path to the mounted path of SSD or NVMe flash memory as that is what it is designed to run on. For more information, see [Flex and Auto Tiering](https://redis.io/docs/latest/operate/rs/databases/flash)."}],"examples":[{"id":"bare-metal-vm-cloud-instance-ex0","language":"sh","code":"tar -vxf <downloaded tar file name>","section_id":"bare-metal-vm-cloud-instance"},{"id":"bare-metal-vm-cloud-instance-ex1","language":"sh","code":"sudo ./install.sh -y","section_id":"bare-metal-vm-cloud-instance"},{"id":"dockerbased-installation-ex0","language":"sh","code":"docker run -d --cap-add sys_resource --name rp -p 8443:8443 -p 12000:12000 redislabs/redis:latest","section_id":"dockerbased-installation"},{"id":"prepare-and-format-flash-memory-ex0","language":"sh","code":"sudo /opt/redislabs/sbin/prepare_flash.sh","section_id":"prepare-and-format-flash-memory"},{"id":"prepare-and-format-flash-memory-ex1","language":"sh","code":"sudo lsblk","section_id":"prepare-and-format-flash-memory"}]}
{"schema_version":2,"id":"operate/rs/databases/flash/storage-engine","title":"Manage Auto Tiering storage engine","url":"https://redis.io/docs/latest/operate/rs/databases/flash/storage-engine/","summary":"Manage the storage engine used for your database with auto tiering enabled.","aliases":["/operate/rs/databases/auto-tiering/storage-engine/"],"tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:24+01:00","page_type":"content","content_hash":"9367bf5077e4d6d9c11a87328dce05a6517c5ee25bb106fa365a069f65bafdb1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This article includes information about the storage engines used by Flex and its predecessor Auto Tiering. For more detailed information about Flex, see the dedicated [Flex databases](https://redis.io/docs/latest/operate/rs/flex) section instead."},{"id":"manage-the-storage-engine","title":"Manage the storage engine","role":"content","text":"Auto Tiering supports two storage engines:\n\n- Speedb: Redis proprietary storage engine. The default and recommended storage engine as of Redis Software version 7.2.4.\n\n- [RocksDB](https://rocksdb.org/): Used up to Redis version 6.2. Deprecated for later Redis versions.\n\nFlex supports Speedb only.\n\n> [!WARNING]\n> Switching between storage engines requires guidance by Redis Support or your Account Manager."},{"id":"change-the-storage-engine","title":"Change the storage engine","role":"content","text":"1. Change the cluster level configuration for default storage engine.\n\n  * API:\n\n      [code example]\n\n  * CLI:\n\n      [code example]\n\n2. Restart the each database on the cluster one by one.\n\n     [code example]\n\n> [!NOTE]\n>  We recommend restarting your database at times with low usage and avoiding peak hours. For databases without persistence enabled, we also recommend using export to backup your database first."},{"id":"monitor-the-storage-engine","title":"Monitor the storage engine","role":"content","text":"To get the current cluster level default storage engine run:\n\n* Use the `rladmin info cluster` command look for ‘bigstore_driver’.\n\n* Use the REST API:\n\n     [code example]\n\nVersions of Redis Software 7.2 and later provide a metric called `bdb_bigstore_shard_count` to help track the shard count per database, filtered by `bdb_id` and by storage engine as shown below:\n\n\n  [code example]\n\nFor more about metrics for Redis Software’s integration with Prometheus, see [Prometheus integration](https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise/prometheus-metrics-definitions)."}],"examples":[{"id":"change-the-storage-engine-ex0","language":" sh","code":"curl -k -u <username>:<password> -X PUT -H \"Content-Type: application/json\" -d '{\"bigstore_driver\":\"speedb\"}' https://localhost:9443/v1/cluster","section_id":"change-the-storage-engine"},{"id":"change-the-storage-engine-ex1","language":"sh","code":"rladmin cluster config bigstore_driver {speedb | rocksdb}","section_id":"change-the-storage-engine"},{"id":"change-the-storage-engine-ex2","language":"sh","code":"rladmin restart db { db:<id> | <name> }","section_id":"change-the-storage-engine"},{"id":"monitor-the-storage-engine-ex0","language":"sh","code":"curl -k -u <username>:<password> -X GET -H \"Content-Type: application/json\" https://localhost:9443/v1/cluster","section_id":"monitor-the-storage-engine"},{"id":"monitor-the-storage-engine-ex1","language":"sh","code":"bdb_bigstore_shard_count{bdb=\"1\",cluster=\"mycluster.local\",driver=\"rocksdb\"} 1.0\n  bdb_bigstore_shard_count{bdb=\"1\",cluster=\"mycluster.local\",driver=\"speedb\"} 2.0","section_id":"monitor-the-storage-engine"}]}
{"schema_version":2,"id":"operate/rs/databases/import-export/export-data","title":"Export data from a database","url":"https://redis.io/docs/latest/operate/rs/databases/import-export/export-data/","summary":"You can export data to import it into a new database or to make a backup. This article shows how to do so.","tags":["docs","operate","rs"],"last_updated":"2026-10-02T16:06:07+01:00","page_type":"content","content_hash":"c66e55956a1fbf76fbb4f66c79d40dd20fcbd4fb5b83bab26a3e128856d4b3fb","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can export the data from a specific database at any time.  The following destinations are supported:\n\n- FTP server\n- SFTP server\n- Amazon AWS S3\n- Local mount point\n- Azure Blob Storage\n- Google Cloud Storage\n\nIf you export a database configured for database clustering, export files are created for each shard."},{"id":"storage-space-requirements","title":"Storage space requirements","role":"content","text":"Before exporting data, verify that you have enough space available in the storage destination and on the local storage associated with the node hosting the database.\n\nExport is a two-step process: a temporary copy of the data is saved to the local storage of the node and then copied to the storage destination.  (The temporary file is removed after the copy operation.)\n\nExport fails when there isn't enough space for either step."},{"id":"export-database-data","title":"Export database data","role":"content","text":"To export data from a database using the Cluster Manager UI:\n\n1. On the **Databases** screen, select the database from the list, then select **Configuration**.\n\n1. Click ![Toggle actions button](https://redis.io/docs/latest/images/rs/buttons/button-toggle-actions-vertical.png#no-click) to open a list of additional actions.\n\n1. Select **Export**.\n\n1.  Select the tab that corresponds to your storage location type and enter the location details.\n\n    See [Supported storage locations](#supported-storage-locations) for more information about each storage location type.\n\n1.  Select **Export**."},{"id":"supported-storage-locations","title":"Supported storage locations","role":"compatibility","text":"Data can be exported to a local mount point, transferred to [a URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) using FTP/SFTP, or stored on cloud provider storage.\n\nWhen saved to a local mount point or a cloud provider, export locations need to be available to [the group and user](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/customize-user-and-group) running Redis Software, `redislabs:redislabs` by default.  \n\nRedis Software needs the ability to view permissions and update objects in the storage location. Implementation details vary according to the provider and your configuration. To learn more, consult the provider's documentation.\n\nThe following sections provide general guidelines.  Because provider features change frequently, use your provider's documentation for the latest info."},{"id":"ftp-server","title":"FTP server","role":"content","text":"Before exporting data to an FTP server, verify that:\n\n- Your Redis Software cluster can connect and authenticate to the FTP server.\n- The user specified in the FTP server location has permission to read and write files to the server.\n\nTo export data to an FTP server, set **Path** using the following syntax:\n\n[code example]\n\nWhere:\n\n- *protocol*: the server's protocol, can be either `ftp` or `ftps`.\n- *username*: your username, if needed.\n- *password*: your password, if needed.\n- *hostname*: the hostname or IP address of the server.\n- *port*: the port number of the server, if needed.\n- *path*: the export destination path, if needed.\n\nExample: `ftp://username:password@10.1.1.1/home/exports/`"},{"id":"local-mount-point","title":"Local mount point","role":"content","text":"Before exporting data to a local mount point, verify that:\n\n- The node can connect to the server hosting the mount point.\n- The `redislabs:redislabs` user has permission to read and write files to the local mount point and to the destination server.\n- The export location has enough disk space for your exported data.\n\nTo export to a local mount point:\n\n1. On each node in the cluster, create the mount point:\n    1. Connect to the node's terminal.\n    1. Mount the remote storage to a local mount point.\n\n        For example:\n\n        [code example]\n\n1. In the path for the export location, enter the mount point.\n\n    For example: `/mnt/Public`"},{"id":"sftp-server","title":"SFTP server","role":"content","text":"Before exporting data to an SFTP server, make sure that:\n\n- Your Redis Software cluster can connect and authenticate to the SFTP server.\n- The user specified in the SFTP server location has permission to read and write files to the server.\n- The SSH private keys are specified correctly.  You can use the key generated by the cluster or specify a custom key.\n\n    To use the cluster auto generated key:\n    \n    1. Go to **Cluster > Security > Certificates**.\n\n    1. Expand **Cluster SSH Public Key**.\n    \n    1. Download or copy the cluster SSH public key to the appropriate location on the SFTP server.\n\n        Use the server documentation to determine the appropriate location for the SSH public key.\n\nTo export data to an SFTP server, enter the SFTP server location in the format:\n\n[code example]\n\nWhere:\n\n- *username*: your username, if needed.\n- *password*: your password, if needed.\n- *hostname*: the hostname or IP address of the server.\n- *port*: the port number of the server, if needed.\n- *path*: the export destination path, if needed.\n\nFor example: `sftp://username:password@10.1.1.1/home/exports/`"},{"id":"aws-simple-storage-service","title":"AWS Simple Storage Service","role":"content","text":"To export data to an [Amazon Web Services](https://aws.amazon.com/) (AWS) Simple Storage Service (S3) bucket:\n\n1. Sign in to the [AWS console](https://console.aws.amazon.com/).\n\n1. [Create an S3 bucket](https://docs.aws.amazon.com/AmazonS3/latest/userguide/creating-buckets-s3.html) if you do not already have one.\n\n1. [Create an IAM User](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html#id_users_create_console) with permission to add objects to the bucket.\n\n1. [Create an access key](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html#Using_CreateAccessKey) for that user if you do not already have one.\n\n1. In the Redis Software Cluster Manager UI, when you enter the export location details:\n\n    - Select **AWS S3**.\n\n    - In the **Path** field, enter the path of your bucket.\n\n    - In the **Access key ID** field, enter the access key ID.\n\n    - In the **Secret access key** field, enter the secret access key.\n\nYou can also connect to a storage service that uses the S3 protocol but is not hosted by Amazon AWS. The storage service must have a valid SSL certificate.\n\nTo connect to an S3-compatible storage location:\n\n1. Configure the S3 URL with [`rladmin cluster config`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/config): \n\n    [code example]\n\n    Replace `<URL>` with the hostname or IP address of the S3-compatible storage location.\n\n1. Configure the S3 CA certificate:\n\n    [code example]\n\n    Replace `<filepath>` with the location of the S3 CA certificate `ca.pem`."},{"id":"google-cloud-storage","title":"Google Cloud Storage","role":"content","text":"To export to a [Google Cloud](https://developers.google.com/console/) storage bucket:\n\n1. Sign in to the Google Cloud console.\n\n1. [Create a JSON service account key](https://cloud.google.com/iam/docs/creating-managing-service-account-keys#creating) if you do not already have one.\n\n1. [Create a bucket](https://cloud.google.com/storage/docs/creating-buckets#create_a_new_bucket) if you do not already have one.\n\n1. [Add a principal](https://cloud.google.com/storage/docs/access-control/using-iam-permissions#bucket-add) to your bucket:\n\n    - In the **New principals** field, add the `client_email` from the service account key.\n\n    - Select \"Storage Legacy Bucket Writer\" from the **Role** list.\n\n1. In the Redis Software Cluster Manager UI, when you enter the export location details:\n\n    - Select **Google Cloud Storage**.\n\n    - In the **Path** field, enter the path of your bucket.\n\n    - In the **Client ID** field, enter the `client_id` from the service account key.\n\n    - In the **Client Email** field, enter the `client_email` from the service account key.\n\n    - In the **Private Key ID** field, enter the `private_key_id` from the service account key.\n\n    - In the **Private key** field, enter the `private_key` from the service account key.\n      Replace `\\n` with new lines."},{"id":"azure-blob-storage","title":"Azure Blob Storage","role":"content","text":"To export to Microsoft Azure Blob Storage, sign in to the Azure portal and then:\n\n1. [Create an Azure Storage account](https://docs.microsoft.com/en-us/azure/storage/common/storage-account-create) if you do not already have one.\n\n1. [Create a container](https://docs.microsoft.com/en-us/azure/storage/blobs/storage-quickstart-blobs-portal#create-a-container) if you do not already have one.\n\n1. [Manage storage account access keys](https://docs.microsoft.com/en-us/azure/storage/common/storage-account-keys-manage) to find the storage account name and account keys.\n\n1. In the Redis Software Cluster Manager UI, when you enter the export location details:\n\n    - Select **Azure Blob Storage**.\n\n    - In the **Path** field, enter the path of your bucket.\n\n    - In the **Account name** field, enter your storage account name.\n\n    - In the **Account key** field, enter the storage account key.\n\nTo learn more, see [Authorizing access to data in Azure Storage](https://docs.microsoft.com/en-us/azure/storage/common/storage-auth)."}],"examples":[{"id":"ftp-server-ex0","language":"sh","code":"[protocol]://[username]:[password]@[host]:[port]/[path]/","section_id":"ftp-server"},{"id":"local-mount-point-ex0","language":"sh","code":"sudo mount -t nfs 192.168.10.204:/DataVolume/Public /mnt/Public","section_id":"local-mount-point"},{"id":"sftp-server-ex0","language":"sh","code":"sftp://[username]:[password]@[host]:[port]/[path]/","section_id":"sftp-server"},{"id":"aws-simple-storage-service-ex0","language":"sh","code":"rladmin cluster config s3_url <URL>","section_id":"aws-simple-storage-service"},{"id":"aws-simple-storage-service-ex1","language":"sh","code":"rladmin cluster config s3_ca_cert <filepath>","section_id":"aws-simple-storage-service"}]}
{"schema_version":2,"id":"operate/rs/databases/import-export/flush","title":"Flush database data","url":"https://redis.io/docs/latest/operate/rs/databases/import-export/flush/","summary":"To delete the data in a database without deleting the database, you can use Redis CLI to flush it from the database. Learn how to flush data from standard databases, OSS Cluster API databases, and Active-Active databases using Redis CLI, the Cluster Manager UI, and the Redis Software REST API.","tags":["docs","operate","rs"],"last_updated":"2026-10-02T16:06:07+01:00","page_type":"content","content_hash":"f1329fa6cfdecd03a3fb7e2ce319acdb89d2a2a60481e0f2118f575a67019974","sections":[{"id":"overview","title":"Overview","role":"overview","text":"To delete the data in a database without deleting the database configuration,\nyou can flush the data from the database.\n\nYou can use the Cluster Manager UI to flush data from Active-Active databases.\n\n> [!WARNING] Data Loss Warning\n> The flush command deletes ALL in-memory and persistence data in the database.\n> We recommend that you [back up your database](https://redis.io/docs/latest/operate/rs/databases/import-export/schedule-backups) before you flush the data."},{"id":"flush-data-from-a-database","title":"Flush data from a database","role":"content","text":"From the command line, you can flush a database with the redis-cli command or with your favorite Redis client.\n\nTo flush data from a database with the redis-cli, run:\n\n[code example]\n\nExample:\n\n[code example]\n\n> [!NOTE]\n> Port 9443 is the default [port configuration](https://redis.io/docs/latest/operate/rs/networking/port-configurations#ports-and-port-ranges-used-by-redis-software)."},{"id":"flush-data-from-an-oss-cluster-api-database","title":"Flush data from an OSS Cluster API database","role":"content","text":"When using the OSS Cluster API, the `FLUSHDB` command only flushes the keys of the shard you're connected to, not all keys in the database. To flush all data from an OSS Cluster API database, you need to connect to each endpoint IP address and run the flush command.\n\nFollow these steps to flush all data from an OSS Cluster API database:\n\n1. Find all IP addresses associated with the database endpoint using the `dig` command:\n\n   [code example]\n\n2. Connect to each IP address using [`redis-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/redis-cli) and run the [`FLUSHDB`](https://redis.io/docs/latest/commands/flushdb) command:\n\n   [code example]"},{"id":"example","title":"Example","role":"example","text":"1. Find all IP addresses for the database endpoint:\n\n   [code example]\n\n2. Connect to each IP address and flush the data:\n\n   [code example]\n\nThis ensures that all shards in the OSS Cluster API database are flushed properly."},{"id":"flush-data-from-an-active-active-database","title":"Flush data from an Active-Active database","role":"content","text":"When you flush an Active-Active database (formerly known as CRDB), all of the replicas flush their data at the same time.\n\n> [!NOTE]\n> Run flush from only one participating cluster. The flush operation propagates to all other clusters automatically.\n\nTo flush data from an Active-Active database, use one of the following methods:\n\n- Cluster Manager UI\n\n    1. On the **Databases** screen, select the database from the list, then click **Configuration**.\n\n    1. Click ![Toggle actions button](https://redis.io/docs/latest/images/rs/buttons/button-toggle-actions-vertical.png#no-click) to open a list of additional actions.\n\n    1. Select **Flush database**.\n\n    1. Enter the name of the Active-Active database to confirm that you want to flush the data.\n\n    1. Click **Flush**.\n\n- Command line\n\n    1. To find the ID of the Active-Active database, run:\n\n        [code example]\n\n        For example:\n\n        [code example]\n\n    1. To flush the Active-Active database, run:\n\n        [code example]\n\n        The command output contains the task ID of the flush task, for example:\n\n        [code example]\n\n    1. To check the status of the flush task, run:\n\n        [code example]\n\n        For example:\n\n        [code example]\n\n- REST API\n\n    1. To find the ID of the Active-Active database, use [`GET /v1/crdbs`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/crdbs#get-all-crdbs):\n\n        [code example]\n\n    1. To flush the Active-Active database, use [`PUT /v1/crdbs/{guid}/flush`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/crdbs/flush#put-crdbs-flush):\n\n        [code example]\n\n        The command output contains the task ID of the flush task.\n\n    1. To check the status of the flush task, use [`GET /v1/crdb_tasks`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/crdb_tasks#get-crdb_task):\n\n        [code example]"}],"examples":[{"id":"flush-data-from-a-database-ex0","language":"sh","code":"redis-cli -h <hostname> -p <portnumber> -a <password> flushall","section_id":"flush-data-from-a-database"},{"id":"flush-data-from-a-database-ex1","language":"sh","code":"redis-cli -h redis-12345.cluster.local -p 9443 -a xyz flushall","section_id":"flush-data-from-a-database"},{"id":"flush-data-from-an-oss-cluster-api-database-ex0","language":"sh","code":"dig +noall +answer <database-endpoint>","section_id":"flush-data-from-an-oss-cluster-api-database"},{"id":"flush-data-from-an-oss-cluster-api-database-ex1","language":"sh","code":"redis-cli -h <ip-address> -p <port> flushdb","section_id":"flush-data-from-an-oss-cluster-api-database"},{"id":"example-ex0","language":"sh","code":"$ dig +noall +answer redis-19674.test2f4e15b0.cs.redislabs.com\n   redis-19674.test2f4e15b0.cs.redislabs.com. 5 IN A 192.0.2.0\n   redis-19674.test2f4e15b0.cs.redislabs.com. 5 IN A 198.51.100.0","section_id":"example"},{"id":"example-ex1","language":"sh","code":"$ redis-cli -h 192.0.2.0 -p 19674 flushdb\n   OK\n   $ redis-cli -h 198.51.100.0 -p 19674 flushdb\n   OK","section_id":"example"},{"id":"flush-data-from-an-active-active-database-ex0","language":"sh","code":"crdb-cli crdb list","section_id":"flush-data-from-an-active-active-database"},{"id":"flush-data-from-an-active-active-database-ex1","language":"sh","code":"$ crdb-cli crdb list\n        CRDB-GUID                                NAME                 REPL-ID CLUSTER-FQDN\n        a16fe643-4a7b-4380-a5b2-96109d2e8bca     crdb1                1       cluster1.local\n        a16fe643-4a7b-4380-a5b2-96109d2e8bca     crdb1                2       cluster2.local\n        a16fe643-4a7b-4380-a5b2-96109d2e8bca     crdb1                3       cluster3.local","section_id":"flush-data-from-an-active-active-database"},{"id":"flush-data-from-an-active-active-database-ex2","language":"sh","code":"crdb-cli crdb flush --crdb-guid <CRDB-GUID>","section_id":"flush-data-from-an-active-active-database"},{"id":"flush-data-from-an-active-active-database-ex3","language":"sh","code":"$ crdb-cli crdb flush --crdb-guid a16fe643-4a7b-4380-a5b2-96109d2e8bca\n        Task 63239280-d060-4639-9bba-fc6a242c19fc created\n        ---> Status changed: queued -> started","section_id":"flush-data-from-an-active-active-database"},{"id":"flush-data-from-an-active-active-database-ex4","language":"sh","code":"crdb-cli task status --task-id <Task-ID>","section_id":"flush-data-from-an-active-active-database"},{"id":"flush-data-from-an-active-active-database-ex5","language":"sh","code":"$ crdb-cli task status --task-id 63239280-d060-4639-9bba-fc6a242c19fc\n        Task-ID: 63239280-d060-4639-9bba-fc6a242c19fc\n        CRDB-GUID: -\n        Status: finished","section_id":"flush-data-from-an-active-active-database"},{"id":"flush-data-from-an-active-active-database-ex6","language":"sh","code":"GET https://[host][:port]/v1/crdbs","section_id":"flush-data-from-an-active-active-database"},{"id":"flush-data-from-an-active-active-database-ex7","language":"sh","code":"PUT https://[host][:port]/v1/crdbs/<guid>/flush","section_id":"flush-data-from-an-active-active-database"},{"id":"flush-data-from-an-active-active-database-ex8","language":"sh","code":"GET https://[host][:port]/v1/crdb_tasks/<task-id>","section_id":"flush-data-from-an-active-active-database"}]}
{"schema_version":2,"id":"operate/rs/databases/import-export/import-data","title":"Import data into a database","url":"https://redis.io/docs/latest/operate/rs/databases/import-export/import-data/","summary":"You can import export or backup files of a specific Redis Software database to restore data. You can either import from a single file or from multiple files, such as when you want to import from a backup of a clustered database.","tags":["docs","operate","rs"],"last_updated":"2026-10-02T16:06:07+01:00","page_type":"content","content_hash":"b2a5bb2463a5403d08a04ba366a72358f1f3ced91180c2b79366c4af58456cbe","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can import, [export](https://redis.io/docs/latest/operate/rs/databases/import-export/export-data),\nor [backup](https://redis.io/docs/latest/operate/rs/databases/import-export/schedule-backups)\nfiles of a specific Redis Software database to restore data.\nYou can either import from a single file or from multiple files,\nsuch as when you want to import from a backup of a clustered database.\n\n> [!WARNING]\n> Importing data erases all existing content in the database."},{"id":"import-data-into-a-database","title":"Import data into a database","role":"content","text":""},{"id":"cluster-manager-ui-method","title":"Cluster Manager UI method","role":"content","text":"To import data into a database using the Cluster Manager UI:\n\n1. On the **Databases** screen, select the database from the list, then select **Configuration**.\n1. Click ![Toggle actions button](https://redis.io/docs/latest/images/rs/buttons/button-toggle-actions-vertical.png#no-click) to open a list of additional actions.\n1. Select **Import**.\n1. Select the tab that corresponds to your storage location type and enter the location details.\n\n    See [Supported storage locations](#supported-storage-locations) for more information about each storage location type.\n1. Select **Import**."},{"id":"rest-api-method","title":"REST API method","role":"content","text":"To import data into a database using the REST API, send an [import database request](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/import):\n\n[code example]\n\n- Replace `<database-id>` with the destination database's ID.\n\n- Replace the data source's `<location-type>` with the relevant value from the following options:\n\n    | Location type | \"type\" value |\n    |---------------|--------------|\n    | FTPS | \"url\" |\n    | SFTP | \"sftp\" |\n    | Amazon S3 | \"s3\" |\n    | Google Cloud Storage | \"gs\" |\n    | Microsoft Azure Storage | \"abs\" |\n    | NAS/Local Storage | \"mount_point\" |\n\nSee the following storage location sections for REST API request examples for each location type."},{"id":"supported-storage-services","title":"Supported storage locations","role":"compatibility","text":"Data can be imported from a local mount point, transferred to [a URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) using FTP/SFTP, or stored on cloud provider storage.\n\nWhen importing from a local mount point or a cloud provider, import locations need to be available to [the group and user](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/customize-user-and-group) running Redis Software, `redislabs:redislabs` by default.  \n\nRedis Software needs the ability to view objects in the storage location. Implementation details vary according to the provider and your configuration. To learn more, consult the provider's documentation.\n\nThe following sections provide general guidelines.  Because provider features change frequently, use your provider's documentation for the latest info."},{"id":"ftp-server","title":"FTP server","role":"content","text":"Before importing data from an FTP server, make sure that:\n\n- Your Redis Software cluster can connect and authenticate to the FTP server.\n- The user that you specify in the FTP server location has permission to read files from the server.\n\nTo import data from an FTP server, set **RDB file path/s** using the following syntax:\n\n[code example]\n\nWhere:\n\n- *protocol*: the server's protocol, can be either `ftp` or `ftps`.\n- *username*: your username, if needed.\n- *password*: your password, if needed.\n- *hostname*: the hostname or IP address of the server.\n- *port*: the port number of the server, if needed.\n- *path*: the file's location path.\n- *filename*: the name of the file.\n\nExample: `ftp://username:password@10.1.1.1/home/backups/<filename>.rdb`\n\nSelect **Add path** to add another import file path.\n\nExample [import database REST API request](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/import):\n\n[code example]"},{"id":"local-mount-point","title":"Local mount point","role":"content","text":"Before importing data from a local mount point, make sure that:\n\n- The node can connect to the server hosting the mount point.\n\n- The `redislabs:redislabs` user has permission to read files on the local mount point and on the destination server.\n\n- You must mount the storage in the same path on all cluster nodes. You can also use local storage, but you must copy the imported files manually to all nodes because the import source folders on the nodes are not synchronized.\n\nTo import from a local mount point:\n\n1. On each node in the cluster, create the mount point:\n    1. Connect to the node's terminal.\n    1. Mount the remote storage to a local mount point.\n\n        For example:\n\n        [code example]\n\n1. In the path for the import location, enter the mount point.\n\n    For example: `/mnt/Public/<filename>.rdb`\n\nAs of version 6.2.12, Redis Software reads files directly from the mount point using a [symbolic link](https://en.wikipedia.org/wiki/Symbolic_link) (symlink) instead of copying them to a temporary directory on the node.\n\nSelect **Add path** to add another import file path.\n\nExample [import database REST API request](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/import):\n\n[code example]"},{"id":"sftp-server","title":"SFTP server","role":"content","text":"Before importing data from an SFTP server, make sure that:\n\n- Your Redis Software cluster can connect and authenticate to the SFTP server.\n- The user that you specify in the SFTP server location has permission to read files from the server.\n- The SSH private keys are specified correctly. You can use the key generated by the cluster or specify a custom key.\n    \n   To use the cluster auto generated key:\n    \n    1. Go to **Cluster > Security > Certificates**.\n\n    1. Expand **Cluster SSH Public Key**.\n    \n    1. Download or copy the cluster SSH public key to the appropriate location on the SFTP server.\n\n        Use the server documentation to determine the appropriate location for the SSH public key.\n\nTo import data from an SFTP server, enter the SFTP server location in the format:\n\n[code example]\n\nWhere:\n\n- *protocol*: the server's protocol, can be either `ftp` or `ftps`.\n- *username*: your username, if needed.\n- *password*: your password, if needed.\n- *hostname*: the hostname or IP address of the server.\n- *port*: the port number of the server, if needed.\n- *path*: the file's location path.\n- *filename*: the name of the file.\n\nExample: `sftp://username:password@10.1.1.1/home/backups/[filename].rdb`\n\nSelect **Add path** to add another import file path.\n\nExample [import database REST API request](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/import):\n\n[code example]"},{"id":"aws-s3","title":"AWS Simple Storage Service","role":"content","text":"Before you choose to import data from an [Amazon Web Services](https://aws.amazon.com/) (AWS) Simple Storage Service (S3) bucket, make sure you have:\n\n- The path to the file in your bucket in the format: `s3://[bucketname]/[path]/[filename].rdb`\n- [Access key ID and Secret access key](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html#Using_CreateAccessKey) for an [IAM user](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html#id_users_create_console) with permission to read files from the bucket. \n\nIn the Redis Software Cluster Manager UI, when you enter the export location details:\n\n- Select **AWS S3**.\n\n- In the **RDB file path/s** field, enter the path of your bucket. Select **Add path** to add another import file path.\n\n- In the **Access key ID** field, enter the access key ID.\n\n- In the **Secret access key** field, enter the secret access key.\n\nYou can also connect to a storage service that uses the S3 protocol but is not hosted by Amazon AWS. The storage service must have a valid SSL certificate.\n\nTo connect to an S3-compatible storage location:\n\n1. Configure the S3 URL with [`rladmin cluster config`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/config): \n\n    [code example]\n\n    Replace `<URL>` with the hostname or IP address of the S3-compatible storage location.\n\n1. Configure the S3 CA certificate:\n\n    [code example]\n\n    Replace `<filepath>` with the location of the S3 CA certificate `ca.pem`.\n\nExample [import database REST API request](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/import):\n\n[code example]"},{"id":"google-cloud-storage","title":"Google Cloud Storage","role":"content","text":"Before you import data from a [Google Cloud](https://developers.google.com/console/) storage bucket, make sure you have:\n\n- Storage location path in the format: `/bucket_name/[path]/[filename].rdb`\n- A [JSON service account key](https://cloud.google.com/iam/docs/creating-managing-service-account-keys#creating) for your account\n- A [principal](https://cloud.google.com/storage/docs/access-control/using-iam-permissions#bucket-add) for your bucket with the `client_email` from the service account key and a [role](https://cloud.google.com/storage/docs/access-control/iam-roles) with permissions to get files from the bucket (such as the **Storage Legacy Object Reader** role, which grants `storage.objects.get` permissions)\n\nIn the Redis Software Cluster Manager UI, when you enter the import location details:\n\n- Select **Google Cloud Storage**.\n\n- In the **RDB file path/s** field, enter the path of your file. Select **Add path** to add another import file path.\n\n- In the **Client ID** field, enter the `client_id` from the service account key.\n\n- In the **Client email** field, enter the `client_email` from the service account key.\n\n- In the **Private key id** field, enter the `private_key_id` from the service account key.\n\n- In the **Private key** field, enter the `private_key` from the service account key.\n    Replace `\\n` with new lines.\n\nExample [import database REST API request](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/import):\n\n[code example]"},{"id":"azure-blob-storage","title":"Azure Blob Storage","role":"content","text":"Before you choose to import from Azure Blob Storage, make sure that you have:\n\n- Storage location path in the format: `/container_name/[path/]/<filename>.rdb`\n- Account name\n- An authentication token, either an account key or an Azure [shared access signature](https://docs.microsoft.com/en-us/rest/api/storageservices/delegate-access-with-shared-access-signature) (SAS).\n\n    To find the account name and account key, see [Manage storage account access keys](https://docs.microsoft.com/en-us/azure/storage/common/storage-account-keys-manage).\n\n    Azure SAS support requires Redis Software version 6.0.20.  To learn more about Azure SAS, see [Grant limited access to Azure Storage resources using shared access signatures](https://docs.microsoft.com/en-us/azure/storage/common/storage-sas-overview).\n\nIn the Redis Software Cluster Manager UI, when you enter the import location details:\n\n- Select **Azure Blob Storage**.\n\n- In the **RDB file path/s** field, enter the path of your file. Select **Add path** to add another import file path.\n\n- In the **Azure Account Name** field, enter your storage account name.\n\n- In the **Azure Account Key** field, enter the storage account key.\n\nExample [import database REST API request](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/import):\n\n[code example]"},{"id":"importing-into-an-active-active-database","title":"Importing into an Active-Active database","role":"content","text":"When importing data into an Active-Active database, there are two options:\n\n- [Flush all data](https://redis.io/docs/latest/operate/rs/databases/import-export/flush#flush-data-from-an-active-active-database) from the Active-Active database, then import the data into the database.\n- Import data but merge it into the existing database.\n\nBecause Active-Active databases have a numeric counter data type,\nwhen you merge the imported data into the existing data RS increments counters by the value that is in the imported data.\nThe import through the Redis Software Cluster Manager UI handles these data types for you.\n\nYou can import data into an Active-Active database [from the Cluster Manager UI](#import-data-into-a-database).\nWhen you import data into an Active-Active database, there is a special prompt warning that the imported data will be merged into the existing database."},{"id":"continue-learning-with-redis-university","title":"Continue learning with Redis University","role":"content","text":""}],"examples":[{"id":"rest-api-method-ex0","language":"sh","code":"POST /v1/bdbs/<database-id>/actions/import\n{\n  \"dataset_import_sources\": [\n    {\n      \"type\": \"<location-type>\",\n      // additional fields, depending on location_type\n    },\n    {\n      \"type\": \"<location-type>\",\n      // additional fields, depending on location_type\n    }\n  ]\n}","section_id":"rest-api-method"},{"id":"ftp-server-ex0","language":"sh","code":"[protocol]://[username]:[password]@[host]:[port]/[path]/[filename].rdb","section_id":"ftp-server"},{"id":"ftp-server-ex1","language":"sh","code":"POST /v1/bdbs/<database-id>/actions/import\n{\n  \"dataset_import_sources\": [\n    {\n      \"type\": \"url\",\n      \"url\": \"ftp://<ftp_user>:<ftp_password>@example.com/<path>/<filename>.rdb.gz\"\n    }\n  ]\n}","section_id":"ftp-server"},{"id":"local-mount-point-ex0","language":"sh","code":"sudo mount -t nfs 192.168.10.204:/DataVolume/Public /mnt/Public","section_id":"local-mount-point"},{"id":"local-mount-point-ex1","language":"sh","code":"POST /v1/bdbs/<database-id>/actions/import\n{\n  \"dataset_import_sources\": [\n    {\n      \"type\": \"mount_point\",\n      \"path\": \"/<path>/<filename>.rdb.gz\"\n    }\n  ]\n}","section_id":"local-mount-point"},{"id":"sftp-server-ex0","language":"sh","code":"[protocol]://[username]:[password]@[host]:[port]/[path]/[filename].rdb","section_id":"sftp-server"},{"id":"sftp-server-ex1","language":"sh","code":"POST /v1/bdbs/<database-id>/actions/import\n{\n  \"dataset_import_sources\": [\n    {\n      \"type\": \"sftp\",\n      \"sftp_url\": \"sftp://<sftp_user>@example.com/<path>/<filename>.rdb\"\n    }\n  ]\n}","section_id":"sftp-server"},{"id":"aws-s3-ex0","language":"sh","code":"rladmin cluster config s3_url <URL>","section_id":"aws-s3"},{"id":"aws-s3-ex1","language":"sh","code":"rladmin cluster config s3_ca_cert <filepath>","section_id":"aws-s3"},{"id":"aws-s3-ex2","language":"sh","code":"POST /v1/bdbs/<database-id>/actions/import\n{\n  \"dataset_import_sources\": [\n    {\n      \"type\": \"s3\",\n      \"bucket_name\": \"backups\",\n      \"subdir\": \"test-db\",\n      \"filename\": \"<filename>.rdb\",\n      \"access_key_id\": \"XXXXXXXXXXXXX\",\n      \"secret_access_key\": \"XXXXXXXXXXXXXXXX\"\n    }\n  ]\n}","section_id":"aws-s3"},{"id":"google-cloud-storage-ex0","language":"sh","code":"POST /v1/bdbs/<database-id>/actions/import\n{\n  \"dataset_import_sources\": [\n    {\n      \"type\": \"gs\",\n      \"bucket_name\": \"backups\",\n      \"client_id\": \"XXXXXXXX\",\n      \"client_email\": \"cloud-storage-client@my-project-id.iam.gserviceaccount.com\",\n      \"subdir\": \"test-db\",\n      \"filename\": \"<filename>.rdb\",\n      \"private_key_id\": \"XXXXXXXXXXXXX\",\n      \"private_key\": \"XXXXXXXXXXXXXXXX\"\n    }\n  ]\n}","section_id":"google-cloud-storage"},{"id":"azure-blob-storage-ex0","language":"sh","code":"POST /v1/bdbs/<database-id>/actions/import\n{\n  \"dataset_import_sources\": [\n    {\n      \"type\": \"abs\",\n      \"container\": \"backups\",\n      \"subdir\": \"test-db\",\n      \"filename\": \"<filename>.rdb\",\n      \"account_name\": \"name\",\n      \"account_key\": \"XXXXXXXXXXXXXXXX\" // Or you can use \"sas_token\": \"XXXXXXXXXXXXXXXXXX\" instead\n    }\n  ]\n}","section_id":"azure-blob-storage"}]}
{"schema_version":2,"id":"operate/rs/databases/import-export","title":"Import and export data","url":"https://redis.io/docs/latest/operate/rs/databases/import-export/","summary":"How to import, export, flush, and migrate your data.","tags":["docs","operate","rs"],"last_updated":"2024-04-02T12:43:54-05:00","children":[{"id":"operate/rs/databases/import-export/import-data","summary":"You can import export or backup files of a specific Redis Software database to restore data. You can either import from a single file or from multiple files, such as when you want to import from a backup of a clustered database.","title":"Import data into a database","url":"https://redis.io/docs/latest/operate/rs/databases/import-export/import-data/"},{"id":"operate/rs/databases/import-export/export-data","summary":"You can export data to import it into a new database or to make a backup. This article shows how to do so.","title":"Export data from a database","url":"https://redis.io/docs/latest/operate/rs/databases/import-export/export-data/"},{"id":"operate/rs/databases/import-export/flush","summary":"To delete the data in a database without deleting the database, you can use Redis CLI to flush it from the database. Learn how to flush data from standard databases, OSS Cluster API databases, and Active-Active databases using Redis CLI, the Cluster Manager UI, and the Redis Software REST API.","title":"Flush database data","url":"https://redis.io/docs/latest/operate/rs/databases/import-export/flush/"},{"id":"operate/rs/databases/import-export/schedule-backups","summary":"Schedule backups of your databases to make sure you always have valid backups.","title":"Schedule periodic backups","url":"https://redis.io/docs/latest/operate/rs/databases/import-export/schedule-backups/"},{"id":"operate/rs/databases/import-export/migrate-to-active-active","summary":"Use Replica Of to migrate your database to an Active-Active database.","title":"Migrate a database to Active-Active","url":"https://redis.io/docs/latest/operate/rs/databases/import-export/migrate-to-active-active/"},{"id":"operate/rs/databases/import-export/replica-of","summary":"Replica Of provides read-only access to replicas of the dataset from different geographical locations.","title":"Replica Of geo-distributed Redis","url":"https://redis.io/docs/latest/operate/rs/databases/import-export/replica-of/"}],"page_type":"index","sections":[],"examples":[]}
{"schema_version":2,"id":"operate/rs/databases/import-export/migrate-to-active-active","title":"Migrate a database to Active-Active","url":"https://redis.io/docs/latest/operate/rs/databases/import-export/migrate-to-active-active/","summary":"Use Replica Of to migrate your database to an Active-Active database.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"39607c87aab51a73e7c996e31a2f1ded74c815e9e616482bca1d76a55f9d78ab","sections":[{"id":"overview","title":"Overview","role":"overview","text":"If you have data in a single-region Redis Software database that you want to migrate to an [Active-Active database](https://redis.io/docs/latest/operate/rs/databases/active-active),\nyou'll need to create a new Active-Active database and migrate the data into the new database as a [Replica Of](https://redis.io/docs/latest/operate/rs/databases/import-export/replica-of) the existing database.\nThis process will gradually populate the data in the Active-Active database.\n\nBefore data migration starts, all data is flushed from the Active-Active database.\nThe data is migrated to the Active-Active instance where you configured migration, and the data from that instance is copied to the other Active-Active instances.\n\nWhen data migration is finished, turn off migration and connect your applications to the Active-Active database.\n\n![Active-Active data migration process](https://redis.io/docs/latest/images/rs/A-A_migration.png)"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"- During the migration, any applications that connect to the Active-Active database must be **read-only** to ensure the dataset is identical to the source database during the migration process. However, you can continue to write to the source database during the migration process.\n\n- If you used the mDNS protocol for the cluster name (FQDN),\nthe [client mDNS prerequisites](https://redis.io/docs/latest/operate/rs/networking/mdns) must be met in order to communicate with other clusters."},{"id":"migrate-from-a-redis-software-cluster","title":"Migrate from a Redis Software cluster","role":"content","text":"You can migrate a Redis Software database from the [same cluster](#migrate-from-the-same-cluster) or a [different cluster](#migrate-from-a-different-cluster)."},{"id":"migrate-from-the-same-cluster","title":"Migrate from the same cluster","role":"content","text":"To migrate a database to Active-Active in the same Redis Software cluster:\n\n1. Create a new Active-Active database. For prerequisites and detailed instructions, see [Create an Active-Active geo-replicated database](https://redis.io/docs/latest/operate/rs/databases/active-active/create).\n\n1. After the Active-Active database is active, click **Edit** on the **Configuration** screen.\n\n1. Expand the **Migrate to Active-Active** section:\n\n    ![Migrate to Active-Active section.](https://redis.io/docs/latest/images/rs/screenshots/databases/migrate-to-active-active/migrate-to-active-active-section.png)\n\n1. Click **+ Add source database**.\n\n1. In the **Migrate to Active-Active** dialog, select **Current cluster**:\n\n    ![Migrate to Active-Active dialog with Current cluster tab selected.](https://redis.io/docs/latest/images/rs/screenshots/databases/migrate-to-active-active/migrate-to-active-active-dialog-current-cluster.png)\n\n1. Select the source database from the list.\n\n1. Click **Add source**.\n\n1. Click **Save**."},{"id":"migrate-from-a-different-cluster","title":"Migrate from a different cluster","role":"content","text":"> [!NOTE]\n> For a source database on a different Redis Software cluster,\n> you can [compress the replication data](https://redis.io/docs/latest/operate/rs/databases/import-export/replica-of#data-compression-for-replica-of) to save bandwidth.\n\nTo migrate a database to Active-Active in different Redis Software clusters:\n\n1. Sign in to the Cluster Manager UI of the cluster hosting the source database.\n\n    1. In **Databases**, select the source database and then select the **Configuration** tab.\n\n    1. In the **Replica Of** section, select **Use this database as a source for another database**.\n\n    1. Copy the Replica Of source URL.\n\n        ![Copy the Replica Of source URL from the Connection link to destination dialog.](https://redis.io/docs/latest/images/rs/screenshots/databases/config-replica-of-copy-source-url.png)\n\n        To change the internal password, select **Regenerate password**.\n\n        If you regenerate the password, replication to existing destinations fails until their credentials are updated with the new password.\n\n1. Sign in to the Cluster Manager UI of the destination database’s cluster.\n\n1. Create a new Active-Active database. For prerequisites and detailed instructions, see [Create an Active-Active geo-replicated database](https://redis.io/docs/latest/operate/rs/databases/active-active/create).\n\n1. After the Active-Active database is active, click **Edit** on the **Configuration** screen.\n\n1. Expand the **Migrate to Active-Active** section:\n\n    ![Migrate to Active-Active section.](https://redis.io/docs/latest/images/rs/screenshots/databases/migrate-to-active-active/migrate-to-active-active-section.png)\n\n1. Click **+ Add source database**.\n\n1. In the **Migrate to Active-Active** dialog, select **External**:\n\n    ![Migrate to Active-Active dialog with External tab selected.](https://redis.io/docs/latest/images/rs/screenshots/databases/migrate-to-active-active/migrate-to-active-active-dialog-external.png)\n\n1. For **Source database URL**, enter the Replica Of source URL you copied in step 1.\n\n1. Click **Add source**.\n\n1. Click **Save**."},{"id":"migrate-from-redis-open-source","title":"Migrate from Redis Open Source","role":"content","text":"To migrate a Redis Open Source database to Active-Active:\n\n1. Create a new Active-Active database. For prerequisites and detailed instructions, see [Create an Active-Active geo-replicated database](https://redis.io/docs/latest/operate/rs/databases/active-active/create).\n\n1. After the Active-Active database is active, click **Edit** on the **Configuration** screen.\n\n1. Expand the **Migrate to Active-Active** section:\n\n    ![Migrate to Active-Active section.](https://redis.io/docs/latest/images/rs/screenshots/databases/migrate-to-active-active/migrate-to-active-active-section.png)\n\n1. Click **+ Add source database**.\n\n1. In the **Migrate to Active-Active** dialog, select **External**:\n\n    ![Migrate to Active-Active dialog with External tab selected.](https://redis.io/docs/latest/images/rs/screenshots/databases/migrate-to-active-active/migrate-to-active-active-dialog-external.png)\n\n1. Enter the **Source database URL**:\n\n    - If the database has a password:\n\n        [code example]\n\n        Where the password is the Redis password represented with URL encoding escape characters.\n\n    - If the database does not have a password:\n\n        [code example]\n\n1. Click **Add source**.\n\n1. Click **Save**."},{"id":"stop-sync-after-migration","title":"Stop sync after migration","role":"content","text":"1. Wait until the migration is complete, indicated by the **Status** _Synced_. \n\n    > [!NOTE]\n    > Migration can take minutes to hours to complete depending on the dataset size and network quality.\n    >\n\n1. On the Active-Active database's **Configuration** screen, click **Edit**.\n\n1. In the **Migrate to Active-Active** section, click **Stop sync**:\n\n    ![The Migrate to Active-Active section shows the Active-Active database is synced with the source database.](https://redis.io/docs/latest/images/rs/screenshots/databases/migrate-to-active-active/migrate-to-active-active-synced.png)\n\n1. In the **Stop synchronization** dialog, click **Stop** to proceed.\n\n1. Redirect client connections to the Active-Active database after **Status** changes to _Sync stopped_:\n\n    ![The Migrate to Active-Active section shows the Active-Active database stopped syncing with the source database.](https://redis.io/docs/latest/images/rs/screenshots/databases/migrate-to-active-active/migrate-to-active-active-sync-stopped.png)"}],"examples":[{"id":"migrate-from-redis-open-source-ex0","language":"sh","code":"redis://:<password>@<host>:<port>","section_id":"migrate-from-redis-open-source"},{"id":"migrate-from-redis-open-source-ex1","language":"sh","code":"redis://<host>:<port>","section_id":"migrate-from-redis-open-source"}]}
{"schema_version":2,"id":"operate/rs/databases/import-export/replica-of/create","title":"Create a database with Replica Of","url":"https://redis.io/docs/latest/operate/rs/databases/import-export/replica-of/create/","summary":"Create Replica Of database","tags":["docs","operate","rs"],"last_updated":"2026-10-02T16:06:07+01:00","page_type":"content","content_hash":"06b9e6a7b6876cf9bb6e7bad675e1e9d8477c860a60792923cac60ebbf591c12","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Replica databases copy data from source databases (previously known as _master_), which enable read-only connections from apps and clients located in different geographic locations.\n\nTo create a replica connection, you define a database as a replica of a source database.  Replica Of databases (also known as _Active-Passive databases_) synchronize in the background.\n\nSources databases can be:\n\n- Located in the same Redis Software cluster\n- Located in a different Redis Software cluster\n- Hosted by a different deployment, e.g. Redis Cloud\n- Redis Open Source databases\n\nYour apps can connect to the source database to read and write data; they can also use any replica for read-only access.\n\nReplica Of can model a variety of data relationships, including:\n\n- One-to-many relationships, where multiple replicas copy a single source database.\n- Many-to-one relationships, where a single replica collects data from multiple source databases.\n\nWhen you change the replica status of a database by adding, removing, or changing sources, the replica database is synchronized to the new sources."},{"id":"configure-replica-of","title":"Configure Replica Of","role":"configuration","text":"You can configure a database as a Replica Of, where the source database is in one of the following clusters:\n\n- [Same Redis Software cluster](#same-cluster)\n\n- [Different Redis Software cluster](#different-cluster)\n\n- [Redis Open Source cluster](#source-available-cluster)\n\nThe order of the multiple Replica Of sources has no material impact on replication.\n\nFor best results when using the [Multicast DNS](https://en.wikipedia.org/wiki/Multicast_DNS) (mDNS) protocol to resolve the fully-qualified domain name (FQDN) of the cluster, verify that your client connections meet the [client mDNS prerequisites](https://redis.io/docs/latest/operate/rs/networking/mdns).\n\n> [!NOTE]\n> As long as Replica Of is enabled, data in the target database will not expire and will not be evicted regardless of the set [data eviction policy](https://redis.io/docs/latest/operate/rs/databases/memory-performance/eviction-policy)."},{"id":"same-cluster","title":"Same Redis Software cluster","role":"content","text":"To configure a Replica Of database in the same Redis Software cluster as the source database:\n\n1. [Create a new database](https://redis.io/docs/latest/operate/rs/databases/create) or select an existing database from the **Databases** screen.\n\n1. For an existing database, select **Edit** from the **Configuration** tab.\n\n1. Expand the **Replica Of** section.\n\n1. Select **+ Add source database**.\n\n1. In the **Connect a Replica Of source database** dialog, select **Current cluster**.\n\n1. Select the source database from the list.\n\n1. Select **Add source**.\n\n1. Select **Save**."},{"id":"different-cluster","title":"Different Redis Software cluster","role":"content","text":"To configure a Replica Of database in a different Redis Software cluster from the source database:\n\n1. Ensure the source database's port is allowed through firewalls between the clusters and can be accessed by the destination cluster's nodes.\n\n    > [!NOTE]\n    > Ports 10000-19999 are reserved for database traffic. See [Network port configurations](https://redis.io/docs/latest/operate/rs/networking/port-configurations) for more information about ports.\n    >\n\n1. Sign in to the Cluster Manager UI of the cluster hosting the source database.\n\n    1. In **Databases**, select the source database and then select the **Configuration** tab.\n\n    1. In the **Replica Of** section, select **Use this database as a source for another database**.\n\n    1. Copy the Replica Of source URL.\n\n        ![Copy the Replica Of source URL from the Connection link to destination dialog.](https://redis.io/docs/latest/images/rs/screenshots/databases/config-replica-of-copy-source-url.png)\n\n        To change the internal password, select **Regenerate password**.\n\n        If you regenerate the password, replication to existing destinations fails until their credentials are updated with the new password.\n\n1. Sign in to the Cluster Manager UI of the destination database's cluster.\n\n1. [Create a new database](https://redis.io/docs/latest/operate/rs/databases/create) or select an existing database from the **Databases** screen.\n\n1. For an existing database, select **Edit** from the **Configuration** tab.\n\n1. Expand the **Replica Of** section.\n\n1. Select **+ Add source database**.\n\n1. In the **Connect a Replica Of source database** dialog, select **External**.\n\n1. Enter the URL of the source database endpoint.\n\n1. Select **Add source**.\n\n1. Select **Save**.\n\nFor source databases on different clusters, you can [compress replication data](https://redis.io/docs/latest/operate/rs/databases/import-export/replica-of#data-compression-for-replica-of) to save bandwidth."},{"id":"source-available-cluster","title":"Redis Open Source cluster","role":"content","text":"To use a database from a Redis Open Source cluster as a Replica Of source:\n\n1. [Create a new database](https://redis.io/docs/latest/operate/rs/databases/create) or select an existing database from the **Databases** screen.\n\n1. For an existing database, select **Edit** from the **Configuration** tab.\n\n1. Expand the **Replica Of** section.\n\n1. Select **+ Add source database**.\n\n1. In the **Connect a Replica Of source database** dialog, select **External**.\n\n1. Enter the URL of the source endpoint in one of the following formats:\n\n    - For databases with passwords:\n\n        [code example]\n\n        Where the password is the Redis password represented with URL encoding escape characters.\n\n    - For databases without passwords:\n\n        [code example]\n\n1. Select **Add source**.\n\n1. Select **Save**."},{"id":"enforce-read-only-access","title":"Enforce read-only access","role":"content","text":"Writing directly to a Replica Of destination database is not supported and can result in replication errors, data inconsistencies, or data loss.\n\nAs of Redis Software version 7.22.0-95, you can set `replica_read_only` to `true` when you [create a Replica Of database](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#post-bdbs-v2) using the REST API. When enabled, the destination database rejects all write operations to prevent accidental writes. Replication remains one-way from source to destination. You cannot change `replica_read_only` after database creation.\n\nTo enforce read-only access when you create a Replica Of destination database:\n\n[code example]\n\nFor additional database configuration fields, see the [BDB object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb) reference.\n\nFor Redis Software versions earlier than 7.22.0-95, there is no product-level enforcement preventing writes to the destination database, so you should configure your application to direct all write operations exclusively to the source database."},{"id":"configure-tls-for-replica-of","title":"Configure TLS for Replica Of","role":"configuration","text":"When you enable TLS for Replica Of, the Replica Of synchronization traffic uses TLS certificates to authenticate the communication between the source and destination clusters.\n\nTo encrypt Replica Of synchronization traffic, configure encryption for the [source database](#encrypt-source-database-traffic) and the destination [replica database](#encrypt-replica-database-traffic)."},{"id":"encrypt-source-database-traffic","title":"Encrypt source database traffic","role":"content","text":"To enable TLS for Replica Of cluster connections:\n\n1. For each cluster hosting a replica:\n\n    1. Go to **Cluster > Security > Certificates**.\n\n    1. Expand the **Replica Of and Active-Active authentication (Syncer certificate)** section.\n\n        ![Syncer certificate for Replica Of and Active-Active authentication.](https://redis.io/docs/latest/images/rs/screenshots/cluster/security-syncer-cert.png)\n    \n    1. Download or copy the syncer certificate.\n\n1. From the **Security** tab of the Replica Of source database, select **Edit**.\n\n1. In the **TLS - Transport Layer Security for secure connections** section, make sure the checkbox is selected.\n\n1. In the **Apply TLS for** section, select **Between databases only**.\n\n1. Select **Mutual TLS (Client authentication)**.\n\n    ![Replica Of TLS authentication configuration.](https://redis.io/docs/latest/images/rs/screenshots/databases/security-tls-replica-of.png)\n\n1. Select **+ Add certificate**, paste or upload the syncer certificate, then select **Done**.\n\n    Repeat this process, adding the syncer certificate for each cluster hosting a replica of this database.\n\n1. _(Optional)_ To require TLS for client connections, change **Apply TLS for** to **Clients and databases + Between databases** and add client certificates.\n\n1. Select **Save**."},{"id":"encrypt-replica-database-traffic","title":"Encrypt replica database traffic","role":"content","text":"To enable TLS for Replica Of in the destination database:\n\n1. From the Cluster Manager UI of the cluster hosting the source database:\n\n    1. Go to **Cluster > Security > Certificates**.\n\n    1. Expand the **Server authentication (Proxy certificate)** section.\n\n        ![Proxy certificate for server authentication.](https://redis.io/docs/latest/images/rs/screenshots/cluster/security-proxy-cert.png)\n\n     1. Download or copy the proxy certificate.\n\n1. From the **Configuration** tab of the Replica Of destination database, select **Edit**.\n\n1. Expand the **Replica Of** section.\n\n1. Point to the source database entry and select ![The Edit button](https://redis.io/docs/latest/images/rs/buttons/edit-button.png#no-click) to edit it.\n\n1. Paste or upload the source proxy certificate, then select **Done**.\n\n1. Select **Save**."}],"examples":[{"id":"source-available-cluster-ex0","language":"sh","code":"redis://:<password>@<host>:<port>","section_id":"source-available-cluster"},{"id":"source-available-cluster-ex1","language":"sh","code":"redis://<host>:<port>","section_id":"source-available-cluster"},{"id":"enforce-read-only-access-ex0","language":"sh","code":"POST https://<host>:<port>/v2/bdbs\n{\n  \"name\": \"readonly-replica-db\",\n  \"memory_size\": 1073741824,\n  \"replica_sources\": [\n    {\n      \"uri\": \"redis://admin:<password>@<source-host>:<port>\"\n    }\n  ],\n  \"replica_read_only\": true,\n  // Additional fields\n}","section_id":"enforce-read-only-access"}]}
{"schema_version":2,"id":"operate/rs/databases/import-export/replica-of","title":"Replica Of geo-distributed Redis","url":"https://redis.io/docs/latest/operate/rs/databases/import-export/replica-of/","summary":"Replica Of provides read-only access to replicas of the dataset from different geographical locations.","aliases":["/operate/rs/administering/active-passive/"],"tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:24+01:00","children":[{"id":"operate/rs/databases/import-export/replica-of/create","summary":"Create Replica Of database","title":"Create a database with Replica Of","url":"https://redis.io/docs/latest/operate/rs/databases/import-export/replica-of/create/"},{"id":"operate/rs/databases/import-export/replica-of/replicaof-repeatedly-fails","summary":"Troubleshoot when the Replica Of process repeatedly fails and restarts.","title":"Replica Of Repeatedly Fails","url":"https://redis.io/docs/latest/operate/rs/databases/import-export/replica-of/replicaof-repeatedly-fails/"}],"page_type":"content","content_hash":"2cca2ee2cfcd7bd9a08f6c6f0fe973802984fa6ebcfeddf50a31bd79533d82ee","sections":[{"id":"overview","title":"Overview","role":"overview","text":"In Redis Software, the Replica Of feature provides active-passive geo-distribution to applications  for read-only access\nto replicas of the dataset from different geographical locations.\nThe Redis Software implementation of active-passive replication is called Replica Of.\n\nIn Replica Of, an administrator designates a database as a replica (destination) of one or more databases (sources).\nAfter the initial data load from source to destination is completed,\nall write commands are synchronized from the sources to the destination.\nReplica Of lets you distribute the read load of your application across multiple databases or\nsynchronize the database, either within Redis Software or external to Redis Software, to another database.\n\n> [!WARNING]\n> Do not write directly to a Replica Of destination database, because it can result in replication errors, data inconsistencies, or data loss. See [Enforce read-only access](https://redis.io/docs/latest/operate/rs/databases/import-export/replica-of/create#enforce-read-only-access) for details and instructions.\n\nYou can [create Active-Passive](https://redis.io/docs/latest/operate/rs/databases/import-export/replica-of/create) databases on Redis Software or Redis Cloud.\n\n[Active-Active Geo-Distribution (CRDB)](https://redis.io/docs/latest/operate/rs/databases/active-active)\nprovides these benefits and also provides write access to all of the database replicas.\n\n> [!WARNING]\n> Configuring a database as a replica of the database that it replicates\n> creates a cyclical replication and is not supported.\n\nThe Replica Of is defined in the context of the destination database\nby specifying the source databases.\n\nA destination database can have a maximum of thirty-two (32) source\ndatabases.\n\nIf only one source is defined, then the command execution order in the\nsource is kept in the destination. However, when multiple sources are\ndefined, commands that are replicated from the source databases are\nexecuted in the order in which they reach the destination database. As a\nresult, commands that were executed in a certain order when compared\nacross source databases might be executed in a different order on the\ndestination database.\n\n> [!NOTE]\n> The Replica Of feature should not be confused with the\n> in-memory [Database\n> replication](https://redis.io/docs/latest/operate/rs/databases/durability-ha/replication)\n> feature, which is used for creating a master / replica configuration that\n> enables ensuring database high-availability."},{"id":"replication-process","title":"Replication process","role":"content","text":"When a database is defined as a replica of another database, all its\nexisting data is deleted and replaced by data that is loaded from the\nsource database.\n\nOnce the initial data load is completed, an ongoing synchronization\nprocess takes place to keep the destination always synchronized with its\nsource. During the ongoing synchronization process, there is a certain\ndelay between the time when a command was executed on the source and\nwhen it is executed on the destination. This delay is referred to as the\n**Lag**.\n\nWhen there is a **synchronization error**, **the process might stop** or\nit might continue running on the assumption that the error automatically\nresolves. The result depends on the error type. See more details below.\n\nIn addition, **the user can manually stop the synchronization process**.\n\nWhen the process is in the stopped state - whether stopped by the user\nor by the system - the user can restart the process. **Restarting the\nprocess causes the synchronization process to flush the DB and restart\nthe process from the beginning**."},{"id":"replica-of-status","title":"Replica Of status","role":"content","text":"The replication process can have the following statuses:\n\n- **Syncing** - indicates that the synchronization process has\n    started from scratch. Progress is indicated in percentages (%).\n- **Synced** - indicates that the initial synchronization process was\n    completed and the destination is synchronizing changes on an ongoing\n    basis. The **Lag** delay in synchronization with the source is\n    indicated as a time duration.\n- **Sync stopped** - indicates that the synchronization process is\n    currently not running and the user needs to restart it in order for\n    it to continue running. This status happens if the user stops the\n    process, or if certain errors arose that prevent synchronization\n    from continuing without manual intervention. See more details below.\n\nThe statuses above are shown for the source database. In addition, a\ntimestamp is shown on the source indicating when the last command from\nthe source was executed on the destination.\n\nThe system also displays the destination database status as an aggregate\nof the statuses of all the sources.\n\n> [!NOTE]\n> If you encounter issues with the Replica Of process, refer\n> to the troubleshooting section [Replica Of repeatedly\n> fails](https://redis.io/docs/latest/operate/rs/databases/import-export/replica-of/replicaof-repeatedly-fails)."},{"id":"synchronization-errors","title":"Synchronization errors","role":"content","text":"Certain errors that occur during the synchronization process require\nuser intervention for their resolution. When such errors occur, the\nsynchronization process is automatically stopped.\n\nFor other errors, the synchronization process continues running on the\nassumption that the error automatically resolves.\n\nExamples of errors that require user intervention for their resolution\nand that stop the synchronization process include:\n\n- Error authenticating with the source database.\n- Cross slot violation error while executing a command on a sharded\n    destination database.\n- Out-of-memory error on a source or on the destination\n    database.\n\nExample of an error that does not cause the synchronization process to\nstop:\n\n- Connection error with the source database. A connection error might\n    occur occasionally, for example as result of temporary network\n    issues that get resolved. Depending on the connection error and its\n    duration the process might be able to start syncing from the last\n    point it reached (partial sync) or require a complete\n    resynchronization from scratch across all sources (full sync)."},{"id":"encryption","title":"Encryption","role":"content","text":"Replica Of supports the ability to encrypt uni-directional replication\ncommunications between source and destination clusters utilizing TLS 1.2\nbased encryption."},{"id":"data-compression-for-replica-of","title":"Data compression for Replica Of","role":"content","text":"When the Replica Of is defined across different Redis Software\nSoftware clusters, it may be beneficial to compress the data that flows\nthrough the network (depending on where the clusters physically reside\nand the available network).\n\nCompressing the data reduces the traffic and can help:\n\n- Resolve throughput issues\n- Reduce network traffic costs\n\nCompressing the data does have trade-offs, which is why it should not\nalways be turned on by default. For example:\n\n- It uses CPU and disk resources to compress the data before sending\n    it to the network and decompress it on the other side.\n- It takes time to compress and decompress the data which can increase\n    latency.\n- Replication is disk-based and done gradually, shard by shard in the\n    case of a multi-shard database. This may have an impact on\n    replication times depending on the speed of the disks and load on\n    the database.\n- If traffic is too fast and the compression takes too much time it\n    can cause the replication process to fail and be restarted.\n\nIt is advised that you test compression out in a lower environment\nbefore enabling it in production.\n\nIn the Redis Software Cluster Manager UI, when designating a\nReplica Of source from a different Redis Software cluster,\nthere is also an option to enable compression. When enabled, gzip\ncompression with level -6 is utilized."},{"id":"database-clustering-sharding-implications","title":"Database clustering (sharding) implications","role":"content","text":"If a **source** database is sharded, that entire database is treated as\na single source for the destination database.\n\nIf the **destination** database is sharded, when the commands replicated\nfrom the source are executed on the destination database, the\ndestination database's hashing function is executed to determine to\nwhich shard/s the command refers.\n\nThe source and destination can have different shard counts and functions\nfor placement of keys."},{"id":"synchronization-in-active-passive-replication","title":"Synchronization in Active-Passive Replication","role":"content","text":"In Active-Passive databases, one cluster hosts the source database that receives read-write operations\nand the other clusters host destination databases that receive synchronization updates from the source database.\n\nWhen there is a significant difference between the source and destination databases,\nthe destination database flushes all of the data from its memory and starts synchronizing the data again.\nThis process is called a **full sync**.\n\nFor example, if the database updates for the destination databases\nthat are stored by the destination database in a synchronization backlog exceed their allocated memory,\nthe source database starts a full sync.\n\n> [!WARNING]\n> When you failover to the destination database for write operations,\n> make sure that you disable **Replica Of** before you direct clients to the destination database.\n> This avoids a full sync that can overwrite your data."},{"id":"upgrade-order","title":"Upgrade order","role":"content","text":"When you [upgrade](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-database) a Replica Of database, upgrade the destination database before the source database to avoid possible replication failures."},{"id":"active-passive-replication-backlog","title":"Active-Passive replication backlog","role":"content","text":"In addition to the [database replication backlog](https://redis.io/docs/latest/operate/rs/databases/durability-ha/replication#database-replication-backlog), active-passive databases maintain a replication backlog (per shard) to synchronize the database instances between clusters.\nBy default, the replication backlog is set to one percent (1%) of the database size divided by the database number of shards and ranges between 1MB to 250MB per shard.\nUse the [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin) utility to control the size of the replication backlog. You can set it to `auto` or set a specific size.  \n\nFor an Active-Passive database:\n[code example]\n\n> [!NOTE]\n> On an Active-Passive database, the replication backlog configuration applies to both the replication backlog for shards synchronization and for synchronization of database instances between clusters."}],"examples":[{"id":"active-passive-replication-backlog-ex0","language":"text","code":"rladmin tune db <db:id | name> repl_backlog <Backlog size in MB or 'auto'>","section_id":"active-passive-replication-backlog"}]}
{"schema_version":2,"id":"operate/rs/databases/import-export/replica-of/replicaof-repeatedly-fails","title":"Replica Of Repeatedly Fails","url":"https://redis.io/docs/latest/operate/rs/databases/import-export/replica-of/replicaof-repeatedly-fails/","summary":"Troubleshoot when the Replica Of process repeatedly fails and restarts.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"a63b8ca9ffdf9b7b1ed1da879f9bcd97ce76e196acf9e1ec690b493d2a1c1887","sections":[{"id":"content","title":"Content","role":"content","text":"**Problem**: The Replica Of process repeatedly fails and restarts\n\n**Diagnostic**: A log entry in the Redis log of the source database shows repeated failures and restarts.\n\n**Cause**: The Redis \"client-output-buffer-limit\" setting on the source database\nis configured to a relatively small value, which causes the connection drop.\n\n**Resolution**: Reconfigure the buffer on the source database to a bigger value:\n\n- If the source is a Redis database on a Redis Software cluster,\n    increase the replica buffer size of the **source database** with:\n\n    `rladmin tune db < db:id | name > slave_buffer < value >`\n\n- If the source is a Redis database not on a Redis Software cluster,\n    use the [config set](http://redis.io/commands/config-set) command through\n    `redis-cli` to increase the client output buffer size of the **source database** with:\n\n    `config set client-output-buffer-limit \"slave <hard_limit> <soft_limit> <soft_seconds>\"`\n\n**Additional information**: [Top Redis Headaches for DevOps - Replication Buffer](https://redis.io/blog/top-redis-headaches-for-devops-replication-buffer/)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/databases/import-export/schedule-backups","title":"Schedule periodic backups","url":"https://redis.io/docs/latest/operate/rs/databases/import-export/schedule-backups/","summary":"Schedule backups of your databases to make sure you always have valid backups.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:24+01:00","page_type":"content","content_hash":"d9e480be1d838a91012576bc196db403bac6333f39caf670ca3e4790de6b0b2f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Periodic backups provide a way to restore data with minimal data loss.  With Redis Software, you can schedule periodic backups to occur once a day (every 24 hours), twice a day (every twelve hours), every four hours, or every hour.\n\nAs of v6.2.8, you can specify the start time in UTC for 24-hour or 12-hour backups.\n\nTo make an on-demand backup, [export your data](https://redis.io/docs/latest/operate/rs/databases/import-export/export-data).\n\nYou can schedule backups to a variety of locations, including:\n\n- FTP server\n- SFTP server\n- Local mount point\n- Amazon Simple Storage Service (S3)\n- Azure Blob Storage\n- Google Cloud Storage\n\nThe backup process creates compressed (.gz) RDB files that you can [import into a database](https://redis.io/docs/latest/operate/rs/databases/import-export/import-data). If the database name is longer than 30 characters, only the first 30 are used in backup file names.\n\nWhen you back up a database configured for database clustering,\nRedis Software creates a backup file for each shard in the configuration.  All backup files are copied to the storage location.\n\n> [!NOTE]\n> - Make sure that you have enough space available in your storage location.\n>     If there is not enough space in the backup location, the backup fails.\n> - The backup configuration only applies to the database it is configured on.\n> - To limit the parallel backup for shards, set both [`tune cluster max_simultaneous_backups`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-cluster) and [`tune node max_redis_forks`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-node). `max_simultaneous_backups` is set to 4 by default."},{"id":"schedule-periodic-backups","title":"Schedule periodic backups","role":"content","text":"Before scheduling periodic backups, verify that your storage location exists and is available to the user running Redis Software (`redislabs` by default).  You should verify that:\n\n- Permissions are set correctly.\n- The user running Redis Software is authorized to access the storage location.\n- The authorization credentials work.\n\nStorage location access is verified before periodic backups are scheduled.\n\nTo schedule periodic backups for a database:\n\n1. Sign in to the Redis Software Cluster Manager UI using admin credentials.\n\n1. From the **Databases** list, select the database, then select **Configuration**.\n\n1. Select the **Edit** button.\n\n1. Expand the **Durability** section.\n\n1. In the **Scheduled backup** section, click **Add backup path** to open the **Path configuration** dialog.\n\n1. Select the tab that corresponds to your storage location type, enter the location details, and select **Done**. \n\n    See [Supported storage locations](#supported-storage-locations) for more information about each storage location type.\n\n1. Set the backup **Interval** and **Starting time**.\n\n    | Setting | Description |\n    |--------------|-------------|\n    | **Interval** | Specifies the frequency of the backup; that is, the time between each backup snapshot.<br/><br/>Supported values include _Every 24 hours_, _Every 12 hours_, _Every 4 hours_, and _Every hour_. |\n    | **Starting time** | _v6.2.8 or later:&nbsp;_ Specifies the start time in UTC for the backup; available when **Interval** is set to _Every 24 hours_ or _Every 12 hours_.<br/><br/>If not specified, defaults to a time selected by Redis Software. |\n\n7. Select **Save**.\n\nAccess to the storage location is verified when you apply your updates.  This means the location, credentials, and other details must exist and function before you can enable periodic backups."},{"id":"default-backup-start-time","title":"Default backup start time","role":"content","text":"If you do _not_ specify a start time for twenty-four or twelve hour backups, Redis Software chooses a random starting time in UTC for you.\n\nThis choice assumes that your database is deployed to a multi-tenant cluster containing multiple databases.  This means that default start times are staggered (offset) to ensure availability.  This is done by calculating a random offset which specifies a number of seconds added to the start time.  \n\nHere's how it works:\n\n- Assume you're enabling the backup at 4:00 pm (1600 hours).\n- You choose to back up your database every 12 hours.\n- Because you didn't set a start time, the cluster randomly chooses an offset of 4,320 seconds (or 72 minutes).\n\nThis means your first periodic backup occurs 72 minutes after the time you enabled periodic backups (4:00&nbsp;pm&nbsp;+&nbsp;72&nbsp;minutes).  Backups repeat every twelve hours at roughly same time.\n\nThe backup time is imprecise because they're started by a trigger process that runs every five minutes.  When the process wakes, it compares the current time to the scheduled backup time.  If that time has passed, it triggers a backup.  \n\nIf the previous backup fails, the trigger process retries the backup until it succeeds.\n\nIn addition, throttling and resource limits also affect backup times.\n\nFor help with specific backup issues, [contact support](https://redis.com/company/support/)."},{"id":"supported-storage-locations","title":"Supported storage locations","role":"compatibility","text":"Database backups can be saved to a local mount point, transferred to [a URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) using FTP/SFTP, or stored on cloud provider storage.\n\nWhen saved to a local mount point or a cloud provider, backup locations need to be available to [the group and user](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/customize-user-and-group) running Redis Software, `redislabs:redislabs` by default.  \n\nRedis Software needs the ability to view permissions and update objects in the storage location. Implementation details vary according to the provider and your configuration. To learn more, consult the provider's documentation.\n\nThe following sections provide general guidelines.  Because provider features change frequently, use your provider's documentation for the latest info."},{"id":"ftp-server","title":"FTP server","role":"content","text":"Before enabling backups to an FTP server, verify that:\n\n- Your Redis Software cluster can connect and authenticate to the FTP server.\n- The user specified in the FTP server location has read and write privileges.\n\nTo store your backups on an FTP server, set its **Backup Path** using the following syntax:\n\n`ftp://[username]:[password]@[host]:[port]/[path]/`\n\nWhere:\n\n- *protocol*: the server's protocol, can be either `ftp` or `ftps`.\n- *username*: your username, if needed.\n- *password*: your password, if needed.\n- *hostname*: the hostname or IP address of the server.\n- *port*: the port number of the server, if needed.\n- *path*: the backup path, if needed.\n\nExample: `ftp://username:password@10.1.1.1/home/backups/`\n\nThe user account needs permission to write files to the server."},{"id":"sftp-server","title":"SFTP server","role":"content","text":"Before enabling backups to an SFTP server, make sure that:\n\n- Your Redis Software cluster can connect and authenticate to the SFTP server.\n- The user specified in the SFTP server location has read and write privileges.\n- The SSH private keys are specified correctly.  You can use the key generated by the cluster or specify a custom key.\n\n    To use the cluster auto generated key:\n    \n    1. Go to **Cluster > Security > Certificates**.\n\n    1. Expand **Cluster SSH Public Key**.\n    \n    1. Download or copy the cluster SSH public key to the appropriate location on the SFTP server.\n\n        Use the server documentation to determine the appropriate location for the SSH public key.\n\nTo backup to an SFTP server, enter the SFTP server location in the format:\n\n[code example]\n\nFor example: `sftp://username:password@10.1.1.1/home/backups/`"},{"id":"local-mount-point","title":"Local mount point","role":"content","text":"Before enabling periodic backups to a local mount point, verify that:\n\n- The node can connect to the destination server, the one hosting the mount point.\n- The `redislabs:redislabs` user has read and write privileges on the local mount point\nand on the destination server.\n- The backup location has enough disk space for your backup files. Backup files\nare saved with filenames that include the timestamp, which means that earlier backups are not overwritten.\n\nTo back up to a local mount point:\n\n1. On each node in the cluster, create the mount point:\n    1. Connect to a shell running on Redis Software server hosting the node.\n    1. Mount the remote storage to a local mount point.\n\n        For example:\n\n        [code example]\n\n1. In the path for the backup location, enter the mount point.\n\n    For example: `/mnt/Public`\n\n1. Verify that the user running Redis Software has permissions to access and update files in the mount location."},{"id":"aws-simple-storage-service","title":"AWS Simple Storage Service","role":"content","text":"To store backups in an Amazon Web Services (AWS) Simple Storage Service (S3) [bucket](https://docs.aws.amazon.com/AmazonS3/latest/userguide/creating-buckets-s3.html):\n\n1.  Sign in to the [AWS Management Console](https://console.aws.amazon.com/).\n\n1. [Create an S3 bucket](https://docs.aws.amazon.com/AmazonS3/latest/userguide/creating-buckets-s3.html) if you do not already have one.\n\n1. [Create an IAM User](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html#id_users_create_console) with permission to add objects to the bucket.\n\n1. [Create an access key](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html#Using_CreateAccessKey) for that user if you do not already have one.\n\n1. In the Redis Software Cluster Manager UI, when you enter the backup location details:\n\n    - Select the **AWS S3** tab on the **Path configuration** dialog.\n\n    - In the **Path** field, enter the path of your bucket.\n\n    - In the **Access Key ID** field, enter the access key ID.\n\n    - In the **Secret Access Key** field, enter the secret access key.\n\nYou can also connect to a storage service that uses the S3 protocol but is not hosted by Amazon AWS. The storage service must have a valid SSL certificate.\n\nTo connect to an S3-compatible storage location:\n\n1. Configure the S3 URL with [`rladmin cluster config`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/config): \n\n    [code example]\n\n    Replace `<URL>` with the hostname or IP address of the S3-compatible storage location.\n\n1. Configure the S3 CA certificate:\n\n    [code example]\n\n    Replace `<filepath>` with the location of the S3 CA certificate `ca.pem`."},{"id":"google-cloud-storage","title":"Google Cloud Storage","role":"content","text":"For [Google Cloud](https://developers.google.com/console/) subscriptions, store your backups in a Google Cloud Storage bucket:\n\n1. Sign in to the Google Cloud Platform console.\n\n1. [Create a JSON service account key](https://cloud.google.com/iam/docs/creating-managing-service-account-keys#creating) if you do not already have one.\n\n1. [Create a bucket](https://cloud.google.com/storage/docs/creating-buckets#create_a_new_bucket) if you do not already have one.\n\n1. [Add a principal](https://cloud.google.com/storage/docs/access-control/using-iam-permissions#bucket-add) to your bucket:\n\n    - In the **New principals** field, add the `client_email` from the service account key.\n\n    - Select \"Storage Legacy Bucket Writer\" from the **Role** list.\n\n1. In the Redis Software Cluster Manager UI, when you enter the backup location details:\n\n    - Select the **Google Cloud Storage** tab on the **Path configuration** dialog.\n\n    - In the **Path** field, enter the path of your bucket.\n\n    - In the **Client ID** field, enter the `client_id` from the service account key.\n\n    - In the **Client Email** field, enter the `client_email` from the service account key.\n\n    - In the **Private Key ID** field, enter the `private_key_id` from the service account key.\n\n    - In the **Private Key** field, enter the `private_key` from the service account key.\n      Replace `\\n` with new lines."},{"id":"azure-blob-storage","title":"Azure Blob Storage","role":"content","text":"To store your backup in Microsoft Azure Blob Storage, sign in to the Azure portal and then:\n\nTo export to Microsoft Azure Blob Storage, sign in to the Azure portal and then:\n\n1. [Create an Azure Storage account](https://docs.microsoft.com/en-us/azure/storage/common/storage-account-create) if you do not already have one.\n\n1. [Create a container](https://docs.microsoft.com/en-us/azure/storage/blobs/storage-quickstart-blobs-portal#create-a-container) if you do not already have one.\n\n1. [Manage storage account access keys](https://docs.microsoft.com/en-us/azure/storage/common/storage-account-keys-manage) to find the storage account name and account keys.\n\n1. In the Redis Software Cluster Manager UI, when you enter the backup location details:\n\n    - Select the **Azure Blob Storage** tab on the **Path configuration** dialog.\n\n    - In the **Path** field, enter the path of your bucket.\n\n    - In the **Azure Account Name** field, enter your storage account name.\n\n    - In the **Azure Account Key** field, enter the storage account key.\n\nTo learn more, see [Authorizing access to data in Azure Storage](https://docs.microsoft.com/en-us/azure/storage/common/storage-auth)."}],"examples":[{"id":"sftp-server-ex0","language":"sh","code":"sftp://user:password@host<:custom_port>/path/","section_id":"sftp-server"},{"id":"local-mount-point-ex0","language":"sh","code":"sudo mount -t nfs 192.168.10.204:/DataVolume/Public /mnt/Public","section_id":"local-mount-point"},{"id":"aws-simple-storage-service-ex0","language":"sh","code":"rladmin cluster config s3_url <URL>","section_id":"aws-simple-storage-service"},{"id":"aws-simple-storage-service-ex1","language":"sh","code":"rladmin cluster config s3_ca_cert <filepath>","section_id":"aws-simple-storage-service"}]}
{"schema_version":2,"id":"operate/rs/databases","title":"Manage databases","url":"https://redis.io/docs/latest/operate/rs/databases/","summary":"This page will help you find database management information in the Databases section.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:24+01:00","children":[{"id":"operate/rs/databases/create","summary":"Create a database with Redis Software.","title":"Create a Redis Software database","url":"https://redis.io/docs/latest/operate/rs/databases/create/"},{"id":"operate/rs/databases/configure","summary":"Configure settings specific to each database.","title":"Configure database settings","url":"https://redis.io/docs/latest/operate/rs/databases/configure/"},{"id":"operate/rs/databases/connect","summary":"Learn how to connect your application to a Redis database hosted by Redis Enterprise Software and test your connection.","title":"Connect to a database","url":"https://redis.io/docs/latest/operate/rs/databases/connect/"},{"id":"operate/rs/databases/import-export","summary":"How to import, export, flush, and migrate your data.","title":"Import and export data","url":"https://redis.io/docs/latest/operate/rs/databases/import-export/"},{"id":"operate/rs/databases/migrate-shards","summary":"How to migrate database shards to other nodes in a Redis Software cluster.","title":"Migrate database shards","url":"https://redis.io/docs/latest/operate/rs/databases/migrate-shards/"},{"id":"operate/rs/databases/recover","summary":"Recover a database after the cluster fails or the database is corrupted.","title":"Recover a failed database","url":"https://redis.io/docs/latest/operate/rs/databases/recover/"},{"id":"operate/rs/databases/delete","summary":"Delete a database from the Cluster Manager UI.","title":"Delete databases","url":"https://redis.io/docs/latest/operate/rs/databases/delete/"},{"id":"operate/rs/databases/active-active","summary":"Overview of the Active-Active database in Redis Software","title":"Active-Active geo-distributed Redis","url":"https://redis.io/docs/latest/operate/rs/databases/active-active/"},{"id":"operate/rs/databases/flash","summary":"Flex and Auto Tiering enable your data to span both RAM and dedicated flash memory.","title":"Flex and Auto Tiering","url":"https://redis.io/docs/latest/operate/rs/databases/flash/"},{"id":"operate/rs/databases/durability-ha","summary":"Overview of Redis Software durability features such as replication, clustering, and rack-zone awareness.","title":"Durability and high availability","url":"https://redis.io/docs/latest/operate/rs/databases/durability-ha/"},{"id":"operate/rs/databases/memory-performance","summary":"Learn more about managing your memory and optimizing performance for your database.","title":"Memory and performance","url":"https://redis.io/docs/latest/operate/rs/databases/memory-performance/"}],"page_type":"content","content_hash":"236d4d73480d5486be042b58dd1d301d1a9e0f86c47a1755898f6bcf858a8527","sections":[{"id":"content","title":"Content","role":"content","text":"You can manage your Redis Software databases with several different tools:\n\n- Cluster Manager UI (the web-based user interface)\n- Command-line tools ([`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin), [`redis-cli`](https://redis.io/docs/latest/develop/tools/cli), [`crdb-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli))\n- [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/databases/memory-performance/eviction-policy","title":"Eviction policy","url":"https://redis.io/docs/latest/operate/rs/databases/memory-performance/eviction-policy/","summary":"The eviction policy determines what happens when a database reaches its memory limit.","tags":["docs","operate","rs","kubernetes"],"last_updated":"2026-09-24T09:16:24+01:00","page_type":"content","content_hash":"829e96095cd8b81d16ae2404aab29766537dafb9fbcfd2c49c2045df6a18f6d4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The eviction policy determines what happens when a database reaches its memory limit.  \n\nTo make room for new data, older data is _evicted_ (removed) according to the selected policy. To prevent this from happening, make sure your database is large enough to hold all keys.\n\nIn clustered databases, each shard tracks its own memory usage and triggers eviction when it reaches its memory limit. If data is distributed unevenly across shards, some shards can fill up and start evicting keys while the overall database memory utilization is still below the configured limit.\n\n| **Eviction&nbsp;Policy** | **Description** |\n|------------|-----------------|\n|  noeviction | New values aren't saved when memory limit is reached<br/><br/>When a database uses replication, this applies to the primary database |\n|  allkeys-lru | Keeps most recently used keys; removes least recently used (LRU) keys |\n|  allkeys-lrm | Keeps most recently modifed keys; removes least recently modified (LRM) keys |\n|  allkeys-lfu | Keeps frequently used keys; removes least frequently used (LFU) keys |\n|  allkeys-random | Randomly removes keys |\n|  volatile-lru | Removes least recently used keys with `expire` field set to true |\n|  volatile-lrm | Removes least recently modified keys with `expire` field set to true |\n|  volatile-lfu | Removes least frequently used keys with `expire` field set to true |\n|  volatile-random | Randomly removes keys with `expire` field set to true |\n|  volatile-ttl | Removes keys with `expire` field set to true and the shortest remaining time-to-live (TTL) value |"},{"id":"eviction-policy-defaults","title":"Eviction policy defaults","role":"content","text":"`volatile-lru` is the default eviction policy for most databases.\n\nThe default policy for [Active-Active databases](https://redis.io/docs/latest/operate/rs/databases/active-active) is _noeviction_ policy."},{"id":"active-active-database-eviction","title":"Active-Active database eviction","role":"content","text":"The eviction policy mechanism for Active-Active databases starts earlier than for standalone databases because it requires propagation to all participating clusters. The eviction policy starts to evict keys when a shard in one of the Active-Active instances reaches 80% of its memory limit.\n\n- The 80% threshold applies to each shard's memory limit, not to the overall database limit.\n\n- If memory usage continues to rise while keys are being evicted, the eviction rate increases to avoid out-of-memory errors.\n\n- To avoid over eviction, internal heuristics might prevent keys from being evicted when the shard reaches the 80% memory limit. In such cases, keys will be evicted only when shard memory reaches 100%.\n\n- Redis Software sometimes allocates a higher `maxmemory` value to individual shards to accommodate uneven key distribution and avoid out-of-memory errors.\n\nIn case of network issues between Active-Active instances, memory can be freed only when all instances are in sync. If there is no communication between participating clusters, it can result in eviction of all keys and the instance reaching an out-of-memory state.\n\n> [!NOTE]\n> Data eviction policies are not supported for Active-Active databases with Auto Tiering."},{"id":"avoid-data-eviction","title":"Avoid data eviction","role":"content","text":"To avoid data eviction, make sure your database is large enough to hold required values.  \n\nFor larger databases, consider using [Redis Flex or Auto Tiering ](https://redis.io/docs/latest/operate/rs/databases/flash).\n\nAuto Tiering stores actively-used data (also known as _hot data_) in RAM and the remaining data in flash memory (SSD).\nThis lets you retain more data while ensuring the fastest access to the most critical data."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/databases/memory-performance","title":"Memory and performance","url":"https://redis.io/docs/latest/operate/rs/databases/memory-performance/","summary":"Learn more about managing your memory and optimizing performance for your database.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","children":[{"id":"operate/rs/databases/memory-performance/eviction-policy","summary":"The eviction policy determines what happens when a database reaches its memory limit.","title":"Eviction policy","url":"https://redis.io/docs/latest/operate/rs/databases/memory-performance/eviction-policy/"},{"id":"operate/rs/databases/memory-performance/memory-limit","summary":"When you set a database's memory limit, you define the maximum size the database can reach.","title":"Database memory limits","url":"https://redis.io/docs/latest/operate/rs/databases/memory-performance/memory-limit/"},{"id":"operate/rs/databases/memory-performance/shard-placement-policy","summary":"Detailed info about the shard placement policy.","title":"Shard placement policy","url":"https://redis.io/docs/latest/operate/rs/databases/memory-performance/shard-placement-policy/"}],"page_type":"content","content_hash":"cd78c4604c3cd478ec70fc0659ffa8bf3d508eea5f470cdd2ccaf7fad38c9077","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software has multiple mechanisms in its\narchitecture to help optimize storage and performance."},{"id":"memory-limits","title":"Memory limits","role":"content","text":"Database memory limits define the maximum size your database can reach across all database replicas and [shards](https://redis.io/docs/latest/glossary#letter-s) on the cluster. Your memory limit will also determine the number of shards you'll need.\n\nBesides your dataset, the memory limit must also account for replication, Active-Active overhead, and module overhead, and a number of other factors. These can significantly increase your database size, sometimes increasing it by four times or more.\n\nFor more information on memory limits, see [Database memory limits](https://redis.io/docs/latest/operate/rs/databases/memory-performance/memory-limit)."},{"id":"eviction-policies","title":"Eviction policies","role":"content","text":"When a database exceeds its memory limit, eviction policies determine which data is removed. The eviction policy removes keys based on frequency of use, how recently used, randomly, expiration date, or a combination of these factors. The policy can also be set to `noeviction` to return a memory limit error when trying to insert more data.\n\nThe default eviction policy for databases is `volatile-lru` which evicts the least recently used keys out of all keys with the `expire` field set. The default for Active-Active databases is `noeviction`.\n\nFor more information, see [eviction policies](https://redis.io/docs/latest/operate/rs/databases/memory-performance/eviction-policy)."},{"id":"database-persistence","title":"Database persistence","role":"content","text":"Both RAM memory and flash memory are at risk of data loss if a server or process fails. Persisting your data to disk helps protect it against loss in those situations. You can configure persistence at the time of database creation, or by editing the database’s configuration.\n\nThere are two main types of persistence strategies in Redis Software: append-only files (AoF) and snapshots.\n\nAppend-only files (AoF) keep a record of data changes and writes each change to the end of a file, allowing you to recover the dataset by replaying the writes in the append-only log.\n\nSnapshots capture all the data as it exists in one moment in time and writes it to disk, allowing you to recover the entire dataset as it existed at that moment in time.\n\nFor more info on data persistence see [Database persistence with Redis Software](https://redis.io/docs/latest/operate/rs/databases/configure/database-persistence) or [Durable Redis](https://redis.com/redis-enterprise/technology/durable-redis/)."},{"id":"auto-tiering","title":"Auto Tiering","role":"content","text":"By default, Redis Software stores your data entirely in [RAM](https://en.wikipedia.org/wiki/Random-access_memory) for improved performance. [Redis Flex and Auto Tiering](https://redis.io/docs/latest/operate/rs/databases/flash) enable your data to span both RAM and [SSD](https://en.wikipedia.org/wiki/Solid-state_drive) storage ([flash memory](https://en.wikipedia.org/wiki/Flash_memory)). Redis Flex tiers both keys and values to flash storage, ensuring that warm (infrequently accessed) data does not consume RAM. Auto Tiering stores keys in RAM but manages the location of their values. Frequently used (hot) data is stored in RAM, but infrequently used (warm) data is moved to flash memory. This saves on expensive RAM space, which gives you comparable performance at a lower cost for large datasets.\n\nFor more info, see [Redis Flex and Auto Tiering](https://redis.io/docs/latest/operate/rs/databases/flash)."},{"id":"shard-placement","title":"Shard placement","role":"content","text":"The location of the primary and replica shards on the cluster nodes can impact your database performance.\nPrimary shards and their corresponding replica shards are always placed on separate nodes for data resiliency and high availability.\nThe shard placement policy helps to maintain optimal performance and resiliency.\n\nRedis Software has two shard placement policies available:\n\n- **dense**: puts as many shards as possible on the smallest number of nodes\n- **sparse**: spread the shards across as many nodes as possible\n\nFor more info about the shard placement policy, see [Shard placement policy](https://redis.io/docs/latest/operate/rs/databases/memory-performance/shard-placement-policy)"},{"id":"metrics","title":"Metrics","role":"content","text":"From the Redis Software Cluster Manager UI, you can monitor the performance of your clusters, nodes, databases, and shards with real-time metrics. You can also enable alerts for node, cluster, or database events such as high memory usage or throughput.\n\nWith the Redis Software API, you can also integrate Redis Software metrics into other monitoring environments, such as Prometheus.\n\nFor more info about monitoring with Redis Software, see [Monitoring with metrics and alerts](https://redis.io/docs/latest/operate/rs/monitoring), and [Memory statistics](https://redis.io/docs/latest/operate/rs/databases/memory-performance/memory-limit#memory-metrics)."},{"id":"scaling-databases","title":"Scaling databases","role":"content","text":"Each Redis Software cluster can contain multiple databases. In Redis,\ndatabases represent data that belong to a single application, tenant, or\nmicroservice. Redis Software is built to scale to 100s of databases\nper cluster to provide flexible and efficient multi-tenancy models.\n\nEach database can contain few or many Redis shards. Sharding is\ntransparent to Redis applications. Master shards in the database process\ndata operations for a given subset of keys. The number of shards per\ndatabase is configurable and depend on the throughput needs of the\napplications. Databases in Redis Software can be resharded into more\nRedis shards to scale throughput while maintaining sub-millisecond\nlatencies. Resharding is performed without downtime.\n\n![](https://redis.io/docs/latest/images/rs/sharding.png)\n\nRedis Software places master shards and replicas in separate\nnodes, racks, and zones, and uses in-memory replication to protect data\nagainst failures.\n\nIn Redis Software, each database has a quota of RAM. The quota cannot\nexceed the limits of the RAM available on the node. However, with Redis\nEnterprise Flash, RAM is extended to the local flash drive (SATA, NVMe\nSSDs etc). The total quota of the database can take advantage of both\nRAM and Flash drive. The administrator can choose the RAM vs Flash ratio\nand adjust that anytime in the lifetime of the database without\ndowntime.\n\nWith Auto Tiering, instead of storing all keys and data for a\ngiven shard in RAM, less frequently accessed values are pushed to flash.\nIf applications need to access a value that is in flash, Redis\nEnterprise automatically brings the value into RAM. Depending on the\nflash hardware in use, applications experience slightly higher latency\nwhen bringing values back into RAM from flash. However subsequent\naccesses to the same value is fast, once the value is in RAM."},{"id":"client-side-caching","title":"Client-side caching","role":"content","text":"Client-side caching allows Redis clients to store a subset of data in a local cache and avoid sending repeated requests to the Redis database. When used to cache frequently accessed data, this technique can improve performance by decreasing network traffic, latency, and load on the database. For more information about client-side caching, see the [client-side caching introduction](https://redis.io/docs/latest/develop/clients/client-side-caching).\n\nRedis Software supports client-side caching for databases with Redis versions 7.4 and later. See [Client-side caching compatibility with Redis Software](https://redis.io/docs/latest/operate/rs/references/compatibility/client-side-caching) for more information about compatibility and configuration options."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/databases/memory-performance/memory-limit","title":"Database memory limits","url":"https://redis.io/docs/latest/operate/rs/databases/memory-performance/memory-limit/","summary":"When you set a database's memory limit, you define the maximum size the database can reach.","tags":["docs","operate","rs","rc"],"last_updated":"2026-09-24T09:16:24+01:00","page_type":"content","content_hash":"6574d2285222ccefa889647e0cb4ab986307a35b13a781ae6a08d077d04e88d2","sections":[{"id":"overview","title":"Overview","role":"overview","text":"When you set a database's memory limit, you define the maximum size the\ndatabase can reach in the cluster, across all database replicas and\nshards, including both primary and replica shards.\n\nIf the total size of the database in the cluster reaches the memory\nlimit, the data eviction policy is\napplied."},{"id":"factors-for-sizing","title":"Factors for sizing","role":"content","text":"Factors to consider when sizing your database:\n\n- **dataset size**: you want your limit to be above your dataset size to leave room for overhead.\n- **database throughput**: high throughput needs more shards, leading to a higher memory limit.\n- [**modules**](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise): using modules with your database consumes more memory.\n- [**database clustering**](https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering): enables you to spread your data into shards across multiple nodes.\n- [**database replication**](https://redis.io/docs/latest/operate/rs/databases/durability-ha/replication): enabling replication doubles memory consumption.\n\nAdditional factors for Active-Active databases:\n\n- [**Active-Active replication**](https://redis.io/docs/latest/operate/rs/databases/active-active): enabling Active-Active replication requires double the memory of regular replication, which can be up to two times (2x) the original data size per instance.\n- [**database replication backlog**](https://redis.io/docs/latest/operate/rs/databases/active-active/manage#replication-backlog/) for synchronization between shards. By default, this is set to 1% of the database size.\n- [**Active-Active replication backlog**](https://redis.io/docs/latest/operate/rs/databases/active-active/manage) for synchronization between clusters. By default, this is set to 1% of the database size.\n\n  It's also important to know Active-Active databases have a lower threshold for activating the eviction policy, because it requires propagation to all participating clusters. The eviction policy starts to evict keys when one of the Active-Active instances reaches 80% of its memory limit.\n\nAdditional factors for databases with flash enabled:\n\n- For [Auto Tiering](https://redis.io/docs/latest/operate/rs/databases/flash), the available flash space must be greater than or equal to the total database size (RAM+Flash). The extra space accounts for write buffers and [write amplification](https://en.wikipedia.org/wiki/Write_amplification).\n\n- For [Flex](https://redis.io/docs/latest/operate/rs/flex), flash space should be approximately three times the total memory limit of all Flex databases on the node. Because you can increase a database's memory limit after creation, size flash space for the expected peak memory limit.\n\n- [**database persistence**](https://redis.io/docs/latest/operate/rs/databases/configure/database-persistence): Auto Tiering uses dual database persistence where both the primary and replica shards persist to disk. This may add some processor and network overhead, especially in cloud configurations with network attached storage."},{"id":"what-happens-when-redis-software-is-low-on-ram","title":"What happens when Redis Software is low on RAM?","role":"content","text":"Redis Software manages node memory so that data is entirely in RAM (unless using Auto Tiering). If not enough RAM is available, Redis Software prevents adding more data into the databases.\n\nRedis Software protects the existing data and prevents the database from being able to store data into the shards.\n\nYou can configure the cluster to move the data to another node, or even discard it according to the [eviction policy](https://redis.io/docs/latest/operate/rs/databases/memory-performance/eviction-policy) set on each database by the administrator.\n\n[Redis Flex and Auto Tiering](https://redis.io/docs/latest/operate/rs/databases/flash)\nmanage memory so that you can also use flash memory (SSD) to store data."},{"id":"order-of-events-for-low-ram","title":"Order of events for low RAM","role":"content","text":"1. If there are other nodes available, your shards migrate to other nodes.\n2. If the eviction policy allows eviction, shards start to release memory,\nwhich can result in data loss.\n3. If the eviction policy does not allow eviction, you'll receive\nout of memory (OOM) messages.\n4. If shards can't free memory, Redis Software relies on the OS processes to stop replicas,\nbut tries to avoid stopping primary shards.\n\nWe recommend that you have a [monitoring platform](https://redis.io/docs/latest/operate/rs/monitoring) that alerts you before a system gets low on RAM.\nYou must maintain sufficient free memory to make sure that you have a healthy Redis Software installation."},{"id":"active-active-replication-oom-protection","title":"Active-Active replication OOM protection","role":"content","text":"When a shard in an Active-Active database reaches an out-of-memory (OOM) condition:\n\n1. Replication between that shard and its peers stops immediately.\n\n1. The syncer process sends commands to the affected shard to trigger garbage collection and free memory.\n\nIf the database has no [eviction policy](https://redis.io/docs/latest/operate/rs/databases/memory-performance/eviction-policy) and no keys with [expiration times (TTL)](https://redis.io/docs/latest/develop/using-commands/keyspace#key-expiration), no memory can be freed, which can lead to persistent replication failure and data desynchronization.\n\nTo reduce this risk, Active-Active databases running Redis version 8.4 or later support a configurable memory buffer through the `replication_oom_threshold_percent` setting. This setting reserves a percentage of memory below `maxmemory` for internal replication operations.\n\nThe `replication_oom_threshold_percent` setting works as follows:\n\n- If memory usage is below the threshold, all client writes proceed normally.\n\n- If memory usage exceeds the threshold, Redis blocks external client write commands with an out-of-memory error, but internal replication and garbage collection continue in the reserved buffer.\n\n- If memory reaches `maxmemory` despite the client block, the standard out-of-memory behavior applies to all operations, including replication.\n\n`replication_oom_threshold_percent` defaults to `5`, which means 5% of `maxmemory` is reserved. To adjust the reserved percentage in all participating clusters, use an [update Active-Active database configuration](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/crdbs#patch-crdbs) REST API request:\n\n[code example]"},{"id":"adaptive-memory-allocation","title":"Adaptive memory allocation","role":"content","text":"In rare cases during high-velocity data ingestion, databases can temporarily reach up to 200% of their configured memory limit. This adaptive memory allocation strategy allows large amounts of data to be written to the database quickly without rejecting valid transactions.\n\nFor example, when pushing data at approximately 100 MB/sec into a 2 GB database:\n- A database with 2 shards could reach 199% of its configured memory limit.\n- A database with 4 shards could reach around 220% of its configured memory limit.\n\nDatabases should return to their configured memory limits after data is removed according to the [eviction policy](https://redis.io/docs/latest/operate/rs/databases/memory-performance/eviction-policy) and [time-to-live (TTL)](https://redis.io/docs/latest/develop/using-commands/keyspace#key-expiration).\n\nIf you observe this behavior, consider [monitoring](https://redis.io/docs/latest/operate/rs/monitoring) memory usage and controlling the traffic load on the application side."},{"id":"resharding-duration-factors","title":"Resharding duration factors","role":"content","text":"Resharding enables you to increase database capacity by adding shards and utilizing more cluster memory. Understanding resharding duration helps you plan maintenance operations and minimize database risk."},{"id":"resharding-overview","title":"Resharding overview","role":"content","text":"Resharding follows a three-stage process for each new shard:\n\n1. **Shard creation**: Creates a new shard and performs partial synchronization based on assigned hash slots\n2. **Key trimming**: Removes keys from the original shard according to new hash slot assignments\n3. **Defragmentation**: Executes failovers to optimize new shard performance\n\nWhen resharding multiple shards (for example, expanding from 4 to 8 shards), this process runs serially for each new shard. The operation completes only after all stages finish for every shard.\n\n> [!NOTE]\n> Resharding is an atomic operation that cannot be interrupted. Database corruption can occur if the process fails or stops unexpectedly. Minimizing resharding time reduces this risk."},{"id":"resharding-duration-factors-1","title":"Resharding duration factors","role":"content","text":"Resharding duration increases linearly with the number of keys in your database. Key length and network traffic affect resharding duration:\n\nKey length directly affects resharding duration. Longer keys (2000 KB) require more time to process due to increased hash calculation overhead per key compared to short keys (10 bytes). \n\nNetwork traffic has a measurable but limited effect on resharding duration. Since resharding operations typically don't reach CPU limits, the impact on both resharding time and ongoing traffic remains minimal."},{"id":"memory-metrics","title":"Memory metrics","role":"content","text":"The Cluster Manager UI provides metrics that can help you evaluate your memory use.\n\n- Free RAM\n- RAM fragmentation\n- Used memory\n- Memory usage\n- Memory limit\n\nSee [console metrics](https://redis.io/docs/latest/operate/rs/references/metrics) for more detailed information."},{"id":"related-info","title":"Related info","role":"related","text":"- [Memory and performance](https://redis.io/docs/latest/operate/rs/databases/memory-performance)\n- [Disk sizing for heavy write scenarios](https://redis.io/docs/latest/operate/rs/clusters/optimize/disk-sizing-heavy-write-scenarios)\n- [Turn off services to free system memory](https://redis.io/docs/latest/operate/rs/clusters/optimize/turn-off-services)\n- [Eviction policy](https://redis.io/docs/latest/operate/rs/databases/memory-performance/eviction-policy)\n- [Shard placement policy](https://redis.io/docs/latest/operate/rs/databases/memory-performance/shard-placement-policy)\n- [Database persistence](https://redis.io/docs/latest/operate/rs/databases/configure/database-persistence)"}],"examples":[{"id":"active-active-replication-oom-protection-ex0","language":"sh","code":"PATCH https://<host>:<port>/v1/crdbs/<crdb_guid>\n{ \n  \"default_db_config\": { \n    \"replication_oom_threshold_percent\": <integer from 0 to 20> \n  }\n}","section_id":"active-active-replication-oom-protection"}]}
{"schema_version":2,"id":"operate/rs/databases/memory-performance/shard-placement-policy","title":"Shard placement policy","url":"https://redis.io/docs/latest/operate/rs/databases/memory-performance/shard-placement-policy/","summary":"Detailed info about the shard placement policy.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"778ca483a7b1a12352b98246d1c7ae8f58fde2a0fc9f853fcf131380f51bf235","sections":[{"id":"overview","title":"Overview","role":"overview","text":"In Redis Software, the location of master and replica shards on the cluster nodes can impact the database and node performance.\nMaster shards and their corresponding replica shards are always placed on separate nodes for data resiliency.\nThe shard placement policy helps to maintain optimal performance and resiliency.\n\nIn addition to the shard placement policy, considerations that determine shard placement are:\n\n- Separation of master and replica shards\n- Available persistence and Auto Tiering storage\n- [Rack-zone awareness](https://redis.io/docs/latest/operate/rs/clusters/configure/rack-zone-awareness)\n- Memory available to host the database when fully populated\n\nThe shard placement policies are:\n\n- `dense` - Place as many shards as possible on the smallest number of nodes to reduce the latency between the proxy and the database shards;\n    Recommended for Redis on RAM databases to optimize memory resources\n- `sparse` - Spread the shards across as many nodes in the cluster as possible to spread the traffic across cluster nodes;\n    Recommended for databases with Auto Tiering enabled to optimize disk resources\n\nWhen you create a Redis Software cluster, the default shard placement policy (`dense`) is assigned to all databases that you create on the cluster.\n\nYou can:\n\n- Change the default shard placement policy for the cluster to `sparse` so that the cluster applies that policy to all databases that you create\n- Change the shard placement policy for each database after the database is created"},{"id":"shard-placement-policies","title":"Shard placement policies","role":"content","text":""},{"id":"dense-shard-placement-policy","title":"Dense shard placement policy","role":"content","text":"In the dense policy, the cluster places the database shards on as few nodes as possible.\nWhen the node is not able to host all of the shards, some shards are moved to another node to maintain optimal node health.\n\nFor example, for a database with two master and two replica shards on a cluster with three nodes and a dense shard placement policy,\nthe two master shards are hosted on one node and the two replica shards are hosted on another node.\n\nFor Redis on RAM databases without the OSS cluster API enabled, use the dense policy to optimize performance.\n\n![](https://redis.io/docs/latest/images/rs/dense_placement.png)\n\n*Figure: Three nodes with two master shards (red) and two replica shards (white) with a dense placement policy*"},{"id":"sparse-shard-placement-policy","title":"Sparse shard placement policy","role":"content","text":"In the sparse policy, the cluster places shards on as many nodes as possible to distribute the shards of a database across all available nodes.\nWhen all nodes have database shards, the shards are distributed evenly across the nodes to maintain optimal node health.\n\nFor example, for a database with two master and two replica shards on a cluster with three nodes and a sparse shard placement policy:\n\n- Node 1 hosts one of the master shards\n- Node 2 hosts the replica for the first master shard\n- Node 3 hosts the second master shard\n- Node 1 hosts for the replica shard for master shard 2\n\nFor Redis on RAM databases with OSS cluster API enabled and for databases with Auto Tiering enabled, use the sparse policy to optimize performance.\n\n![](https://redis.io/docs/latest/images/rs/sparse_placement.png)\n\n*Figure: Three nodes with two master shards (red) and two replica shards (white) with a sparse placement policy*"},{"id":"related-articles","title":"Related articles","role":"related","text":"You can [configure the shard placement policy](https://redis.io/docs/latest/operate/rs/databases/configure/shard-placement) for each database."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/databases/migrate-shards","title":"Migrate database shards","url":"https://redis.io/docs/latest/operate/rs/databases/migrate-shards/","summary":"How to migrate database shards to other nodes in a Redis Software cluster.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:24+01:00","page_type":"content","content_hash":"61ac6cdfcb70c0f27a97d5c4a2cf0174787b5dc241b6e2d41059c6b30491f0df","sections":[{"id":"overview","title":"Overview","role":"overview","text":"To migrate database shards to other nodes in the cluster, you can use the [`rladmin migrate`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/migrate) command or [REST API requests](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/shards/actions/migrate)."},{"id":"use-cases-for-shard-migration","title":"Use cases for shard migration","role":"content","text":"Migrate database shards to a different node in the following scenarios:\n\n- Before node removal.\n\n- To balance the database manually in case of latency issues or uneven load distribution across nodes.\n\n- To manage node resources, such as memory usage."},{"id":"considerations-for-shard-migration","title":"Considerations for shard migration","role":"content","text":"For databases with replication:\n\n- Migrating a shard will not cause disruptions since a primary shard will still be available.\n\n- If you try to migrate a primary shard, it will be demoted to a replica shard and a replica shard will be promoted to primary before the migration. If you set `\"preserve_roles\": true` in the request, a second failover will occur after the migration finishes to change the migrated shard's role back to primary.\n\nFor databases without replication, the migrated shard will not be available until the migration is done.\n\nConnected clients shouldn't be disconnected in either case.\n\nIf too many primary shards are placed on the same node, it can impact database performance."},{"id":"migrate-specific-shard","title":"Migrate specific shard","role":"content","text":"To migrate a specific database shard, use one of the following methods:\n\n- [`rladmin migrate shard`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/migrate#migrate-shard):\n\n    [code example]\n\n- [Migrate shard](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/shards/actions/migrate#post-shard) REST API request:\n\n    Specify the ID of the shard to migrate in the request path and the destination node's ID as the `target_node_uid` in the request body. See the [request reference](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/shards/actions/migrate#post-request-body) for more options.\n\n    [code example]\n\n    Example JSON response body:\n\n    [code example]\n\n    You can track the action's progress with a [`GET /v1/actions/<action_uid>`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/actions#get-action) request."},{"id":"migrate-multiple-shards","title":"Migrate multiple shards","role":"content","text":"To migrate multiple database shards, use one of the following methods:\n\n- [`rladmin migrate shard`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/migrate#migrate-shard):\n\n    [code example]\n\n- [Migrate multiple shards](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/shards/actions/migrate#post-multi-shards) REST API request:\n\n    Specify the IDs of the shards to migrate in the `shard_uids` list and the destination node's ID as the `target_node_uid` in the request body. See the [request reference](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/shards/actions/migrate#post-multi-request-body) for more options.\n\n    [code example]\n\n    Example JSON response body:\n\n    [code example]\n\n    You can track the action's progress with a [`GET /v1/actions/<action_uid>`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/actions#get-action) request."},{"id":"migrate-all-shards-from-a-node","title":"Migrate all shards from a node","role":"content","text":"To migrate all shards from a specific node to another node, run [`rladmin migrate all_shards`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/migrate#migrate-all_shards):\n\n[code example]"},{"id":"migrate-primary-shards","title":"Migrate primary shards","role":"content","text":"You can use the [`rladmin migrate all_master_shards`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/migrate#migrate-all_master_shards) command to migrate all primary shards for a specific database or node to another node in the cluster.\n\nTo migrate a specific database's primary shards:\n\n[code example]\n\nTo migrate all primary shards from a specific node:\n\n[code example]"},{"id":"migrate-replica-shards","title":"Migrate replica shards","role":"content","text":"You can use the [`rladmin migrate all_slave_shards`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/migrate#migrate-all_slave_shards) command to migrate all replica shards for a specific database or node to another node in the cluster.\n\nTo migrate a specific database's replica shards:\n\n[code example]\n\nTo migrate all replica shards from a specific node:\n\n[code example]"}],"examples":[{"id":"migrate-specific-shard-ex0","language":"sh","code":"rladmin migrate shard <shard_id> target_node <node_id>","section_id":"migrate-specific-shard"},{"id":"migrate-specific-shard-ex1","language":"sh","code":"POST /v1/shards/<shard_id>/actions/migrate\n    {\n        \"target_node_uid\": <node_id>\n    }","section_id":"migrate-specific-shard"},{"id":"migrate-specific-shard-ex2","language":"json","code":"{\n        \"action_uid\": \"<action_id>\",\n        \"description\": \"Migrate was triggered\"\n    }","section_id":"migrate-specific-shard"},{"id":"migrate-multiple-shards-ex0","language":"sh","code":"rladmin migrate shard <shard_id1> <shard_id2> <shard_id3> target_node <node_id>","section_id":"migrate-multiple-shards"},{"id":"migrate-multiple-shards-ex1","language":"sh","code":"POST /v1/shards/actions/migrate\n    {\n        \"shard_uids\": [\"<shard_id1>\",\"<shard_id2>\",\"<shard_id3>\"],\n        \"target_node_uid\": <node_id>\n    }","section_id":"migrate-multiple-shards"},{"id":"migrate-multiple-shards-ex2","language":"json","code":"{\n        \"action_uid\": \"<action_id>\",\n        \"description\": \"Migrate was triggered\"\n    }","section_id":"migrate-multiple-shards"},{"id":"migrate-all-shards-from-a-node-ex0","language":"sh","code":"rladmin migrate node <origin_node_id> all_shards target_node <node_id>","section_id":"migrate-all-shards-from-a-node"},{"id":"migrate-primary-shards-ex0","language":"sh","code":"rladmin migrate db db:<id> all_master_shards target_node <node_id>","section_id":"migrate-primary-shards"},{"id":"migrate-primary-shards-ex1","language":"sh","code":"rladmin migrate node <origin_node_id> all_master_shards target_node <node_id>","section_id":"migrate-primary-shards"},{"id":"migrate-replica-shards-ex0","language":"sh","code":"rladmin migrate db db:<id> all_slave_shards target_node <node_id>","section_id":"migrate-replica-shards"},{"id":"migrate-replica-shards-ex1","language":"sh","code":"rladmin migrate node <origin_node_id> all_slave_shards target_node <node_id>","section_id":"migrate-replica-shards"}]}
{"schema_version":2,"id":"operate/rs/databases/recover","title":"Recover a failed database","url":"https://redis.io/docs/latest/operate/rs/databases/recover/","summary":"Recover a database after the cluster fails or the database is corrupted.","tags":["docs","operate","rs","kubernetes"],"last_updated":"2026-09-24T09:16:24+01:00","page_type":"content","content_hash":"8b19cabb9f6de48bad6d569e660546795d1f6534cfbbb3c317e495858db00072","sections":[{"id":"overview","title":"Overview","role":"overview","text":"When a cluster fails or a database is corrupted, you must:\n\n1. [Restore the cluster configuration](https://redis.io/docs/latest/operate/rs/clusters/cluster-recovery) from the CCS files\n1. Recover the databases with their previous configuration and data\n\nTo restore data to databases in the new cluster,\nyou must restore the database persistence files (backup, AOF, or snapshot files) to the databases.\nThese files are stored in the [persistence storage location](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/persistent-ephemeral-storage).\n\nThe database recovery process includes:\n\n1. If the cluster failed, [recover the cluster](https://redis.io/docs/latest/operate/rs/clusters/cluster-recovery).\n1. Identify recoverable databases.\n1. Restore the database data.\n1. Verify that the databases are active."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"- Before you start database recovery, make sure that the cluster that hosts the database is healthy.\n    In the case of a cluster failure,\n    you must [recover the cluster](https://redis.io/docs/latest/operate/rs/clusters/cluster-recovery) before you recover the databases.\n\n- We recommend that you allocate new persistent storage drives for the new cluster nodes.\n    If you use the original storage drives,\n    make sure to back up all files on the old persistent storage drives to another location."},{"id":"recover-databases","title":"Recover databases","role":"content","text":"After you prepare the cluster that hosts the database,\nyou can run the recovery process from the [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin)\ncommand-line interface (CLI).\n\nTo recover the database:\n\n1. Mount the persistent storage drives with the recovery files to the new nodes.\n    These drives must contain the cluster configuration backup files and database persistence files.\n\n    > [!NOTE]\n    > Make sure that the user `redislabs` has permissions to access the storage location\n    > of the configuration and persistence files on each of the nodes.\n    >\n\n    If you use local persistent storage, place all of the recovery files on each of the cluster nodes.\n\n1. To see which databases are recoverable, run [`rladmin recover list`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/recover#recover-list):\n\n    [code example]\n\n    The status for each database can be either ready for recovery or missing files.\n    An indication of missing files in any of the databases can result from:\n\n    - The storage location is not found - Make sure the recovery path is set correctly on all nodes in the cluster.\n    - Files are not found in the storage location - Move the files to the storage location.\n    - No permission to read the files - Change the file permissions so that redislabs:redislabs has 640 permissions.\n    - Files are corrupted - Locate copies of the files that are not corrupted.\n\n    If you cannot resolve the issues, contact [Redis support](https://redis.com/company/support/).\n\n1. Recover the database using one of the following [`rladmin recover`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/recover) commands:\n\n    - Recover all databases from the persistence files located in the persistent storage drives:\n    \n        [code example]\n\n    - Recover a single database from the persistence files located in the persistent storage drives:\n    \n        - By database ID:\n\n            [code example]\n\n        - By database name:\n        \n            [code example]\n\n    - Recover only the database configuration for a single database (without the data):\n    \n        [code example]\n\n    > [!NOTE]\n    > - If persistence was not configured for the database, the database is restored empty.\n    > - For Active-Active databases that still have live instances, we recommend that you recover the configuration for the failed instances and let the  data update from the other instances.\n    > - For Active-Active databases where all instances need to be recovered, we recommend you recover one instance with the data and only recover the configuration for the other instances.\n    >    The empty instances then update from the recovered data.\n    > - If the persistence files of the databases from the old cluster are not stored in the persistent storage location of the new node,\n    >    you must first map the recovery path of each node to the location of the old persistence files.\n    >    To do this, run the `node <id> recovery_path set` command in rladmin.\n    >    The persistence files for each database are located in the persistent storage path of the nodes from the old cluster, usually under `/var/opt/redislabs/persist/redis`.\n    >\n\n1. To verify that the recovered databases are now active, run: \n\n    [code example]\n\nAfter the databases are recovered, make sure your Redis clients can successfully connect to the databases."},{"id":"configure-automatic-recovery","title":"Configure automatic recovery","role":"configuration","text":"If you enable the automatic recovery cluster policy, Redis Software tries to quickly recover as much data as possible from before the disaster.\n\nTo enable automatic recovery, use one of the following methods:\n\n**rladmin:**\n\nTo enable automatic recovery using `rladmin`, run the [`rladmin tune cluster`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-cluster) command:\n\n[code example]\n\n**REST API:**\n\nTo enable automatic recovery using the REST API, use an [update cluster policy](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/policy#put-cluster-policy) request:\n\n[code example]\n\n\n\nRedis Software tries to recover databases from the best existing persistence files. If a persistence file isn't available, which can happen if its host node is down, the automatic recovery process waits for it to become available.\n\nFor each database, you can set the `recovery_wait_time` to define how many seconds the database waits for a persistence file to become available before recovery. After the wait time elapses, the recovery process continues, which can result in partial or full data loss. The default value is `-1`, which means to wait forever. Short wait times can increase the risk of potential data loss.\n\nTo change `recovery_wait_time` for an existing database using the REST API:\n\n[code example]\n\nYou can also set `recovery_wait_time` when you [create a database](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#post-bdbs-v1) using the REST API."}],"examples":[{"id":"recover-databases-ex0","language":"sh","code":"rladmin recover list","section_id":"recover-databases"},{"id":"recover-databases-ex1","language":"sh","code":"rladmin recover all","section_id":"recover-databases"},{"id":"recover-databases-ex2","language":"sh","code":"rladmin recover db db:<id>","section_id":"recover-databases"},{"id":"recover-databases-ex3","language":"sh","code":"rladmin recover db <name>","section_id":"recover-databases"},{"id":"recover-databases-ex4","language":"sh","code":"rladmin recover db <name> only_configuration","section_id":"recover-databases"},{"id":"recover-databases-ex5","language":"sh","code":"rladmin status","section_id":"recover-databases"},{"id":"configure-automatic-recovery-ex0","language":"sh","code":"rladmin tune cluster auto_recovery enabled","section_id":"configure-automatic-recovery"},{"id":"configure-automatic-recovery-ex1","language":"sh","code":"PUT /v1/cluster/policy\n{\n  \"auto_recovery\": true\n}","section_id":"configure-automatic-recovery"},{"id":"configure-automatic-recovery-ex2","language":"sh","code":"PUT /v1/bdbs/<bdb_uid>\n{\n  \"recovery_wait_time\": 3600\n}","section_id":"configure-automatic-recovery"}]}
{"schema_version":2,"id":"operate/rs/flex/get-started","title":"Get started with Flex databases for Redis Software","url":"https://redis.io/docs/latest/operate/rs/flex/get-started/","summary":"Configure Flex databases on your Redis Software cluster.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"db39ca89408c64476370acada8508b0b8b3daea90664fc939b0fa4a598d2b1bc","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This page guides you through a quick setup of [Flex](https://redis.io/docs/latest/operate/rs/flex) with a single node for testing and demo purposes.\n\nFor production environments, see [Plan a Flex deployment for Redis Software](https://redis.io/docs/latest/operate/rs/flex/plan) and more detailed installation instructions in the [install and setup](https://redis.io/docs/latest/operate/rs/installing-upgrading) section.\n\nTo set up a Redis Software cluster using Flex with a single node:\n\n1. Review the [version requirements](#version-requirements).\n\n1. [Install Redis Software](#install-redis-software) or run it in a [Docker container](#dockerbased-installation).\n\n1. [Prepare and format flash memory](#prepare-and-format-flash-memory).\n\n1. [Set up a cluster](#setup-cluster-flash) with flash storage.\n\n1. [Create a new database](#create-a-database) with flash enabled.\n\n1. [Connect to your new database](#connect-to-your-database)."},{"id":"version-requirements","title":"Version requirements","role":"compatibility","text":"To create Flex databases, you need:\n\n- Redis Software cluster version 8.0.2-17 or later\n\n- Redis database version 8.2 or later"},{"id":"install-redis-software","title":"Install Redis Software","role":"setup","text":""},{"id":"bare-metal-vm-cloud-instance","title":"Bare metal, VM, Cloud instance","role":"content","text":"To install on bare metal, a virtual machine, or a Cloud instance:\n\n1. Download the Redis Software binaries from the [download center](https://redis.io/downloads/#Redis_Software).\n\n1. Upload the binaries to a Linux-based operating system.\n\n1. Extract the image:\n\n    [code example]\n\n1. After the `tar` command completes, you can find a new `install.sh` script in the current directory:\n\n    [code example]"},{"id":"dockerbased-installation","title":"Docker-based installation","role":"content","text":"For testing purposes, you can run a Redis Software Docker container:\n\n[code example]"},{"id":"prepare-and-format-flash-memory","title":"Prepare and format flash memory","role":"content","text":"After you [install Redis Software](#install-redis-software), use the `prepare_flash` script to prepare and format flash memory:\n\n[code example]\n\nThis script finds unformatted disks and mounts them as RAID partitions in `/var/opt/redislabs/flash`.\n\nTo verify the disk configuration, run:\n\n[code example]"},{"id":"setup-cluster-flash","title":"Set up a cluster with flash storage","role":"content","text":"1. Direct your browser to `https://localhost:8443` on the host machine to\nsee the Redis Software Cluster Manager UI.\n\n    > [!NOTE]\n    > Depending on your browser, you may see a certificate error.\n    > Choose \"continue to the website\" to go to the setup screen.\n\n1. Click **Create new cluster**.\n\n1. Set up account credentials for a cluster administrator, then click **Next** to proceed to cluster setup.\n\n1. Enter your cluster license key if you have one. Otherwise, the cluster uses the trial version.\n\n1. Provide a cluster FQDN such as `mycluster.local`, then click **Next**.\n\n1. In the **Storage configuration** section, turn on the **Enable flash storage** toggle.\n\n1. Click **Create cluster**.\n\n1. Click **OK** to confirm that you are aware of the replacement of the HTTPS TLS certificate on the node, and proceed through the browser warning."},{"id":"create-a-database","title":"Create a database","role":"content","text":"On the **Databases** screen:\n\n1. Select **Quick database**.\n\n1. Verify **Flash** is selected for **Runs on**.\n\n    ![Create a quick database with Runs on Flash selected.](https://redis.io/docs/latest/images/rs/screenshots/databases/quick-db-flash-7-8-2.png)\n\n1. Enter `12000` for the endpoint **Port** number.\n\n1. Optionally, select **Full options** to see available alerts.\n\n1. Click **Create**.\n\nYou now have a Flex database."},{"id":"connect-to-your-database","title":"Connect to your database","role":"content","text":"After you create the database, you can connect to it and store data. See [Test client connection](https://redis.io/docs/latest/operate/rs/databases/connect/test-client-connectivity) for connection options and examples."},{"id":"next-steps","title":"Next steps","role":"content","text":"To see the true performance and scale of Flex, you must tune your I/O path and set the flash path to the mounted path of SSD or NVMe flash memory."}],"examples":[{"id":"bare-metal-vm-cloud-instance-ex0","language":"sh","code":"tar -vxf <downloaded tar file name>","section_id":"bare-metal-vm-cloud-instance"},{"id":"bare-metal-vm-cloud-instance-ex1","language":"sh","code":"sudo ./install.sh -y","section_id":"bare-metal-vm-cloud-instance"},{"id":"dockerbased-installation-ex0","language":"sh","code":"docker run -d --cap-add sys_resource --name rp -p 8443:8443 -p 12000:12000 redislabs/redis:latest","section_id":"dockerbased-installation"},{"id":"prepare-and-format-flash-memory-ex0","language":"sh","code":"sudo /opt/redislabs/sbin/prepare_flash.sh","section_id":"prepare-and-format-flash-memory"},{"id":"prepare-and-format-flash-memory-ex1","language":"sh","code":"sudo lsblk","section_id":"prepare-and-format-flash-memory"}]}
{"schema_version":2,"id":"operate/rs/flex","title":"Flex databases","url":"https://redis.io/docs/latest/operate/rs/flex/","summary":"Extend Redis databases with flash storage for large-scale, cost-efficient deployments.","tags":["docs","operate","rs","rc"],"last_updated":"2026-10-05T09:52:50-05:00","children":[{"id":"operate/rs/flex/plan","summary":"Hardware requirements, sizing guidelines, best practices, and limitations for Flex databases on Redis Software.","title":"Plan a Flex deployment for Redis Software","url":"https://redis.io/docs/latest/operate/rs/flex/plan/"},{"id":"operate/rs/flex/get-started","summary":"Configure Flex databases on your Redis Software cluster.","title":"Get started with Flex databases for Redis Software","url":"https://redis.io/docs/latest/operate/rs/flex/get-started/"},{"id":"operate/rs/flex/scale","summary":"Scaling strategies for Flex deployments for Redis Software.","title":"Scale Flex databases for Redis Software","url":"https://redis.io/docs/latest/operate/rs/flex/scale/"}],"page_type":"content","content_hash":"f7d1210772de3f3b299fe709a082bfd8b724e65d3e1a65893a4cf5788f92c384","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Flex extends your database capacity by combining RAM and flash (SSD) storage. This tiered architecture keeps frequently accessed (hot) data in RAM for sub-millisecond latency while storing less active (warm) data on flash to reduce costs and increase capacity. Flex frees RAM for hot data, leads to a higher RAM hit rate, and allows bigger datasets per node.\n\nFlex databases work with your existing Redis applications and the Redis API without modification."},{"id":"how-flex-works","title":"How Flex works","role":"content","text":""},{"id":"automatic-data-tiering","title":"Automatic data tiering","role":"content","text":"Flex moves data between RAM and flash based on access patterns:\n\n- Frequently accessed data stays in high-speed RAM.\n- Less active data moves to cost-efficient flash storage.\n- Data accessed from flash promotes back to RAM automatically.\n\nRedis uses an [LRU (least recently used)](https://redis.io/docs/latest/develop/reference/eviction#apx-lru) eviction policy to manage data placement. When memory pressure increases, Flex identifies cold objects, transfers them to flash, and frees RAM for new or frequently accessed keys.\n\nThis process requires no application changes. Your existing Redis commands work across both storage tiers."},{"id":"storage-engine","title":"Storage engine","role":"content","text":"Flex uses [Speedb](https://docs.speedb.io), a high-performance key-value storage engine optimized for flash drives:\n\n- Redis handles all data operations in memory.\n- Speedb manages the flash storage layer.\n\nThis design delivers predictable latency and throughput as datasets grow beyond RAM limits."},{"id":"compatibility","title":"Compatibility","role":"compatibility","text":"Flex is compatible with the Redis API and supports all [Redis data types](https://redis.io/docs/latest/develop/data-types), including JSON and probabilistic data structures (Bloom filters, Count-Min Sketch, Top-K). [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) on Flex databases is in beta.\n\nThe following features are not yet supported with Flex:\n\n- [Time series](https://redis.io/docs/latest/develop/data-types/timeseries)\n- [Active-Active](https://redis.io/docs/latest/operate/rs/databases/active-active)"},{"id":"when-to-use-flex","title":"When to use Flex","role":"content","text":"Use Flex when you need to:\n\n- Run Redis at terabyte scale while maintaining high throughput and sub-10 ms latency\n- Power real-time feature stores for machine learning applications\n- Operate large distributed caches with elastic scaling and consistent performance under heavy load\n- Reduce infrastructure costs by combining high-speed RAM with cost-efficient flash storage\n\n> [!NOTE]\n> Flex does not replace long-term data persistence. For workloads that require durability and recovery across restarts or failures, use Redis persistence features like [AOF (Append-Only File)](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence#append-only-file), [RDB snapshots](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence#snapshotting), or both. For more information, see [Database persistence](https://redis.io/docs/latest/operate/rs/databases/configure/database-persistence)."},{"id":"flex-and-auto-tiering","title":"Flex and Auto Tiering","role":"content","text":"Flex replaces [Auto Tiering](https://redis.io/docs/latest/operate/rs/7.22/databases/auto-tiering) (formerly known as Redis on Flash). Redis Software selects the implementation based on your Redis version:\n\n| Redis database version | Flex | Auto Tiering |\n|------------------------|------|--------------|\n| 8.0 and later | <span title=\"Supported\">&#x2705;</span> | <span title=\"Not supported\">&#x274c;</span> |\n| 7.4 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span>|\n| 7.2 and earlier | <span title=\"Not supported\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> |\n\nFor Redis Software version 7.22.2-22 or earlier, see [Auto Tiering](https://redis.io/docs/latest/operate/rs/7.22/databases/auto-tiering)."},{"id":"differences-between-flex-and-auto-tiering","title":"Differences between Flex and Auto Tiering","role":"content","text":"- Key and value offloading\n  - Auto Tiering offloads only values to flash while keys remain in RAM.\n  - Flex offloads both keys and values, which increases dataset density per node and reduces RAM consumption. This change frees RAM for hot data, leads to a higher RAM hit rate, and allows bigger datasets per node.\n- RAM population strategy\n  - Auto Tiering fills all available RAM before offloading data to flash. This maximizes hot-data performance but can cause non-linear performance changes at high utilization.\n  - Flex uses utilization-aware RAM population. When database utilization is below 50%, Flex uses up to 50% of configured RAM for hot data. Above 50% utilization, Flex uses both RAM and flash proportionally, following the configured RAM-to-flash ratio. This provides a stable performance curve, consistent RAM hit-rate, and predictable throughput and latency.\n- Storage engine\n  - Auto Tiering uses either RocksDB or Speedb as the storage engine.\n  - Flex uses Speedb only."},{"id":"next-steps","title":"Next steps","role":"content","text":"- [Plan your deployment](https://redis.io/docs/latest/operate/rs/flex/plan): Review hardware requirements, sizing guidelines, and limitations.\n- [Get started](https://redis.io/docs/latest/operate/rs/flex/get-started): Configure Flex on your cluster.\n- [Scale your deployment](https://redis.io/docs/latest/operate/rs/flex/scale): Learn scaling strategies and best practices."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/flex/plan","title":"Plan a Flex deployment for Redis Software","url":"https://redis.io/docs/latest/operate/rs/flex/plan/","summary":"Hardware requirements, sizing guidelines, best practices, and limitations for Flex databases on Redis Software.","tags":["docs","operate","rs"],"last_updated":"2026-10-05T09:52:50-05:00","page_type":"content","content_hash":"5ff6754bc2f381bea542389468abfc48fe22829884bd14355ab57dfbe8094588","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Review the following hardware requirements, sizing guidelines, best practices, and known limitations before you deploy a Redis Software cluster for Flex databases."},{"id":"checklist","title":"Checklist","role":"content","text":"Before you deploy Flex, verify:\n\n[code example]"},{"id":"version-requirements","title":"Version requirements","role":"compatibility","text":"To create Flex databases, you need:\n\n- Redis Software cluster version 8.0.2-17 or later\n\n- Redis database version 8.2 or later"},{"id":"hardware-requirements","title":"Hardware requirements","role":"content","text":"When planning a Flex deployment, consider the following flash drive requirements:\n\n- Flash storage must be locally attached. Network-attached storage (NAS), storage area networks (SAN), or solutions such as AWS Elastic Block Storage (EBS) are not supported.\n\n- Flash storage must be dedicated to Flex database data and keys. It should not be used for durability, binaries, or persistence.\n\n- For the best performance, the SSDs should be NVMe based. NVMe Gen 5 or Gen 4 is recommended. Gen 3 is also supported, but not recommended for best performance.\n\n- Flash storage must be greater than the total provisioned database size to account for write buffers, space amplification, and more. Flash space should be approximately three times the total memory limit of all Flex databases on the node. Because you can increase a database's memory limit after creation, size flash storage for the expected peak memory limit.\n\nSee the general Redis Software [hardware requirements](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/hardware-requirements) for additional requirements."},{"id":"sizing-guidelines","title":"Sizing guidelines","role":"content","text":"Size your Redis Flex databases based on data volume, shard count, RAM-to-flash ratio, and CPU allocation to balance capacity, throughput, and latency."},{"id":"shard-size","title":"Shard size","role":"content","text":"Flex databases consist of shards—independent data partitions that handle a portion of the dataset and request load. Each shard runs as a self-contained Redis process with dedicated memory, CPU, and flash resources.\n\nUse these standard building blocks to plan capacity:\n\n- 50 GB per shard\n\n- Allocate 1 vCPU per shard\n\nFor example, a 1 TB dataset requires 20 shards, with 50 GB per shard."},{"id":"cpu-allocation","title":"CPU allocation","role":"content","text":"Throughput capacity scales with both CPU cores and the RAM-to-flash ratio. At minimum, allocate 1 vCPU per 50 GB shard."},{"id":"ram-to-flash-ratio","title":"RAM-to-flash ratio","role":"content","text":"The RAM-to-flash ratio directly affects throughput and latency:\n\n- More RAM increases throughput and lowers latency.\n\n- Less RAM lowers throughput and increases latency, but reduces cost.\n\nActual performance can vary based on your data model, commands, and network latency."},{"id":"feature-and-data-type-compatibility","title":"Feature and data type compatibility","role":"content","text":"| Data type/feature | Flex support |\n|------------------|--------------|\n| [Active-Active databases](https://redis.io/docs/latest/operate/rs/databases/active-active) | <span title=\"Not supported\">&#x274c;</span>Not supported |\n| [JSON](https://redis.io/docs/latest/develop/data-types/json) | <span title=\"Supported\">&#x2705;</span> Supported |\n| [Probabilistic data structures](https://redis.io/docs/latest/develop/data-types#probabilistic-data-types) | <span title=\"Supported\">&#x2705;</span> Supported |\n| [Redis Search](https://redis.io/docs/latest/develop/ai/search-and-query) | <span title=\"Beta\">&#x2713;</span> Beta |\n| Standard [Redis data types](https://redis.io/docs/latest/develop/data-types) | <span title=\"Supported\">&#x2705;</span> Supported |\n| [Time series](https://redis.io/docs/latest/develop/data-types/timeseries) | <span title=\"Not supported\">&#x274c;</span>Not supported |"},{"id":"best-practices","title":"Best practices","role":"content","text":"- Store small keys and values when possible. Avoid objects larger than 10 KB, which can reduce performance because the entire value moves between RAM and flash.\n\n- Start with a balanced ratio, such as 20% RAM to 80% flash, and monitor latency and throughput metrics.\n\n- Increase RAM percentage if 99th-percentile latency exceeds your target or cache hit rates drop.\n\n- Decrease RAM percentage if memory utilization is high but latency remains stable to lower cost and improve efficiency.\n\n- Re-evaluate the ratio of RAM to flash periodically as dataset size and access patterns evolve."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"- Flex databases cannot store keys or values larger than 4GB in flash storage. Larger keys or values will be stored in RAM only, and warnings will appear in the Redis logs:\n\n    [code example]"},{"id":"next-steps","title":"Next steps","role":"content","text":"- [Get started](https://redis.io/docs/latest/operate/rs/flex/get-started): Configure Flex on your cluster.\n- [Scale your deployment](https://redis.io/docs/latest/operate/rs/flex/scale): Learn scaling strategies."}],"examples":[{"id":"checklist-ex0","language":"checklist {id=\"rs-flex-list\"}","code":"- [ ] You have locally attached NVMe or SSD dedicated to Flex data and keys.\n- [ ] The flash capacity is greater than the total provisioned database size.\n- [ ] Your working set is much smaller than the total dataset and fits a high-RAM hit-rate pattern.","section_id":"checklist"},{"id":"known-limitations-ex0","language":"sh","code":"# WARNING: key too big for disk driver, size: 4703717276, key: subactinfo:htable","section_id":"known-limitations"}]}
{"schema_version":2,"id":"operate/rs/flex/scale","title":"Scale Flex databases for Redis Software","url":"https://redis.io/docs/latest/operate/rs/flex/scale/","summary":"Scaling strategies for Flex deployments for Redis Software.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:24+01:00","page_type":"content","content_hash":"3f48b6e32eaadfb2324674dfc995a781c82893da0d1f540eeb9067219bb16e68","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This guide shows you how to scale Flex databases for Redis Software to meet changing workload demands."},{"id":"choose-a-scaling-strategy","title":"Choose a scaling strategy","role":"content","text":"Use the following table to determine the best scaling strategy for your Flex deployment:\n\n| Goal | Recommended action |\n|------|--------------------|\n| Increase data capacity only without adding CPU | Increase database limit and decrease RAM percentage |\n| Increase throughput only | Add shards and vCPU |\n| Increase data capacity and throughput | Add shards |\n| Improve latency under higher load | Increase RAM percentage |\n| Reduce cost while maintaining performance | Tune RAM-to-flash ratio |"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before you scale a self-managed Flex deployment, verify that your cluster has sufficient resources, such as memory, disk, and vCPU."},{"id":"scale-volume","title":"Scale volume","role":"content","text":"If your dataset requires more capacity while maintaining performance, you can prepare a Flex database to store more data using one of the following options:\n\n1. Increase the database limit and [add shards](#scale-volume-add-shards).\n\n1. Increase the database limit and [decrease the RAM-to-flash ratio](#decrease-ram-to-flash-ratio)."},{"id":"scale-volume-add-shards","title":"Add shards","role":"content","text":"You can add more shards to expand dataset capacity while maintaining the existing RAM-to-flash ratio. Throughput capacity also typically increases as a result of additional shards and infrastructure. This strategy is recommended when the dataset size and traffic are expected to grow together.\n\nBefore you increase the dataset capacity and add shards, you need to add more RAM and vCPUs to handle the increased number of shards.\n\nTo increase the dataset capacity and shards using the Cluster Manager UI:\n\n1. On the **Databases** screen, select the database you want to edit.\n\n1. From the **Configuration** tab, click **Edit**.\n\n1. In the **Capacity** section, increase the **Memory limit**.\n\n1. In the **Clustering** section, increase the **Number of shards**.\n\n1. Click **Save**."},{"id":"decrease-ram-to-flash-ratio","title":"Decrease RAM-to-flash ratio","role":"content","text":"You can allocate more data to the flash tier to increase the database capacity while keeping the same amount of RAM, shards, and vCPU. This strategy is recommended when scaling for volume only and SSD resources are underutilized.\n\nThis scaling strategy increases capacity without adding CPU or RAM, but might reduce RAM hit rate and increase p99 latency. You should monitor metrics before and after this change.\n\nTo increase the dataset capacity and decrease the RAM-to-flash ratio using the Cluster Manager UI:\n\n1. On the **Databases** screen, select the database you want to edit.\n\n1. From the **Configuration** tab, click **Edit**.\n\n1. In the **Capacity** section:\n\n    1. Increase the **Memory limit**.\n\n    1. Decrease the **RAM limit**.\n\n1. Click **Save**.\n\nFor example, to lower the RAM-to-flash ratio from 30% to 20% on a 1 TB database:\n\n- You can increase the memory limit from 1 TB to 1.5 TB for more total capacity.\n\n- If you keep the RAM limit at 300 GB, it becomes 20% of the new total capacity."},{"id":"scale-throughput","title":"Scale throughput","role":"content","text":"If your workload's read/write rate increases and latency starts to rise, you can prepare the database to handle more traffic using one of the following strategies:\n\n1. [Add shards or nodes](#add-shards-or-nodes).\n\n1. [Increase the RAM-to-flash ratio](#increase-ram-to-flash-ratio)."},{"id":"add-shards-or-nodes","title":"Add shards or nodes","role":"content","text":"You can add more shards or nodes to distribute traffic and increase throughput without changing the RAM-to-flash ratio. Dataset size capacity also typically increases as a result of additional shards and infrastructure. This strategy is recommended when the dataset size and traffic are expected to grow together.\n\nBefore you add shards or nodes, you need to add more RAM and vCPUs to handle the increased number of shards or nodes.\n\nTo add shards using the Cluster Manager UI:\n\n1. On the **Databases** screen, select the database you want to edit.\n\n1. From the **Configuration** tab, click **Edit**.\n\n1. In the **Clustering** section, increase the **Number of shards**.\n\n1. Click **Save**.\n\nTo add nodes to the cluster, see [Add a node](https://redis.io/docs/latest/operate/rs/clusters/add-node) for instructions."},{"id":"increase-ram-to-flash-ratio","title":"Increase RAM-to-flash ratio","role":"content","text":"To improve throughput and lower latency, you can expand the in-memory tier to serve a higher proportion of requests directly from RAM. This strategy is recommended when low latency is your primary goal and you don't need to increase the dataset size.\n\nBefore increasing the RAM-to-flash ratio, you might need to add more nodes to accommodate additional RAM. See [Add a node](https://redis.io/docs/latest/operate/rs/clusters/add-node) for instructions.\n\nTo increase the RAM-to-flash ratio using the Cluster Manager UI:\n\n1. On the **Databases** screen, select the database you want to edit.\n\n1. From the **Configuration** tab, click **Edit**.\n\n1. In the **Capacity** section, increase the **RAM limit**.\n\n1. Click **Save**."},{"id":"scale-infrastructure","title":"Scale infrastructure","role":"content","text":"You can increase or adjust the underlying resources supporting the database, such as CPU, memory, and disk.\n\nFor self-managed Redis Flex deployments, ensure the cluster has sufficient physical resources before scaling. The cluster requires:\n\n- Enough RAM to support the desired in-memory dataset size.\n\n- Enough SSD capacity for flash-tier data.\n\n- Adequate vCPU to support increased shard count or throughput.\n\n> [!WARNING]\n> Scaling operations will fail or underperform if the underlying cluster is resource-constrained.\n\nSee Flex [hardware requirements](https://redis.io/docs/latest/operate/rs/flex/plan#hardware-requirements) for more information."}],"examples":[]}
{"schema_version":2,"id":"operate/rs","title":"Redis Software","url":"https://redis.io/docs/latest/operate/rs/","summary":"The self-managed, enterprise-grade version of Redis.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:53:09+01:00","children":[{"id":"operate/rs/installing-upgrading","summary":"Learn how to install, set up, and upgrade Redis Software.","title":"Install, set up, and upgrade Redis Software","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/"},{"id":"operate/rs/clusters","summary":"Administrative tasks and information related to the Redis Software cluster.","title":"Manage clusters","url":"https://redis.io/docs/latest/operate/rs/clusters/"},{"id":"operate/rs/databases","summary":"This page will help you find database management information in the Databases section.","title":"Manage databases","url":"https://redis.io/docs/latest/operate/rs/databases/"},{"id":"operate/rs/flex","summary":"Extend Redis databases with flash storage for large-scale, cost-efficient deployments.","title":"Flex databases","url":"https://redis.io/docs/latest/operate/rs/flex/"},{"id":"operate/rs/networking","summary":"Networking features and considerations for designing your Redis Software deployment.","title":"Manage networks","url":"https://redis.io/docs/latest/operate/rs/networking/"},{"id":"operate/rs/security","summary":"","title":"Security","url":"https://redis.io/docs/latest/operate/rs/security/"},{"id":"operate/rs/monitoring","summary":"Use the metrics that measure the performance of your Redis Software clusters, nodes, databases, and shards to track the performance of your databases.","title":"Monitoring with metrics and alerts","url":"https://redis.io/docs/latest/operate/rs/monitoring/"},{"id":"operate/rs/troubleshooting","summary":"Resources for troubleshooting Redis Software","title":"Troubleshooting Redis Software","url":"https://redis.io/docs/latest/operate/rs/troubleshooting/"},{"id":"operate/rs/references","summary":"","title":"Reference","url":"https://redis.io/docs/latest/operate/rs/references/"},{"id":"operate/rs/release-notes","summary":"","title":"Release notes","url":"https://redis.io/docs/latest/operate/rs/release-notes/"},{"id":"operate/rs/rs-archive","summary":"Describes where to view the archive of Redis Enterprise Software documentation.","title":"Archive","url":"https://redis.io/docs/latest/operate/rs/rs-archive/"}],"page_type":"content","content_hash":"2e2763fa9013acca6a5740883cfa66acfff4dfe4203e10e2a9e9fff682451a16","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Redis Software](https://redis.io/software/) is a self-managed, enterprise-grade version of Redis.\n\nWith Redis Software, you get many enterprise-grade capabilities, including:\n- Linear scalability\n- High availability, backups, and recovery\n- Predictable performance\n- 24/7 support\n\nYou can run self-managed Redis Software in an on-premises data center or on your preferred cloud platform.\n\nIf you prefer a fully managed Redis database-as-a-service, available on major public cloud services, consider setting up a [Redis Cloud](https://redis.io/docs/latest/operate/rc) subscription. You can [try Redis Cloud](https://redis.io/try-free/) for free."},{"id":"get-started","title":"Get started","role":"content","text":"Build a small-scale cluster with the Redis Software container image.\n- [Linux quickstart](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/redis-enterprise-software-quickstart)\n- [Docker quickstart](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart)\n- [Get started with Active-Active](https://redis.io/docs/latest/operate/rs/databases/active-active/get-started)"},{"id":"install--setup","title":"Install & setup","role":"setup","text":"[Install & set up](https://redis.io/docs/latest/operate/rs/installing-upgrading) a Redis Software cluster.\n- [Networking](https://redis.io/docs/latest/operate/rs/networking)\n- [Set up](https://redis.io/docs/latest/operate/rs/clusters/new-cluster-setup) & [configure](https://redis.io/docs/latest/operate/rs/clusters/configure) a [cluster](https://redis.io/docs/latest/operate/rs/clusters)\n- [Release notes](https://redis.io/docs/latest/operate/rs/release-notes)"},{"id":"databases","title":"Databases","role":"content","text":"Create and manage a [Redis database](https://redis.io/docs/latest/operate/rs/databases) on a cluster.\n- [Create a Redis Software database](https://redis.io/docs/latest/operate/rs/databases/create)\n- [Configure database](https://redis.io/docs/latest/operate/rs/databases/configure)\n- [Create Active-Active database](https://redis.io/docs/latest/operate/rs/databases/active-active/create)\n- [Edit Active-Active database](https://redis.io/docs/latest/operate/rs/databases/active-active/manage)"},{"id":"security","title":"Security","role":"security","text":"[Manage secure connections](https://redis.io/docs/latest/operate/rs/security) to the cluster and databases.\n- [Access control](https://redis.io/docs/latest/operate/rs/security/access-control)\n- [Users](https://redis.io/docs/latest/operate/rs/security/access-control/manage-users) & [roles](https://redis.io/docs/latest/operate/rs/security/access-control)\n- [Certificates](https://redis.io/docs/latest/operate/rs/security/certificates)\n- [TLS](https://redis.io/docs/latest/operate/rs/security/encryption/tls) & [Encryption](https://redis.io/docs/latest/operate/rs/security/encryption)"},{"id":"reference","title":"Reference","role":"related","text":"Use command-line utilities and the REST API to manage the cluster and databases.\n- [rladmin](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin), [crdb-cli](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli), & [other utilities](https://redis.io/docs/latest/operate/rs/references/cli-utilities)\n- [`redisctl`](https://github.com/redis/redisctl) — a unified CLI for managing Redis Software and Redis Cloud from your terminal\n- [REST API reference](https://redis.io/docs/latest/operate/rs/references/rest-api) & [examples](https://redis.io/docs/latest/operate/rs/references/rest-api/quick-start)\n- [Redis commands](/content/commands)"},{"id":"archive","title":"Archive","role":"content","text":"You can use the version selector in the navigation menu to view documentation for Redis Software versions 7.4 and later.\n\nTo view documentation earlier than version 7.4, see the archived website:\n\n- [Redis Software v7.2 documentation archive](https://docs.redis.com/7.2/rs/) \n\n- [Redis Software v6.4 documentation archive](https://docs.redis.com/6.4/rs/) \n\n- [Redis Software v6.2 documentation archive](https://docs.redis.com/6.2/rs/) \n\n- [Redis Software v6.0 documentation archive](https://docs.redis.com/6.0/rs/)"},{"id":"related-info","title":"Related info","role":"related","text":"- [Redis Cloud](https://redis.io/docs/latest/operate/rc)\n- [Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack)\n- [Redis Stack](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise)\n- [Glossary](https://redis.io/docs/latest/glossary)"},{"id":"continue-learning-with-redis-university","title":"Continue learning with Redis University","role":"content","text":"* [Get started with Redis Software learning path](https://university.redis.io/learningpath/an0mgw5bjpjfbe?_gl=1*4gjdoe*_gcl_au*MTkyMTIyOTY3Mi4xNzM5MTk5Mjc4)\n* [Operate Redis Software learning path](https://university.redis.io/learningpath/uobc5j9rrhalce?tab=details)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/installing-upgrading/configuring/centos-rhel-firewall","title":"Configure CentOS/RHEL firewall","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/configuring/centos-rhel-firewall/","summary":"Configure firewall rules for Redis Software on CentOS or Red Hat Enterprise Linux (RHEL).","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:34+01:00","page_type":"content","content_hash":"d284f024ff43a44534d3601977f545d3947f10babfb09de42f6b2ef6c00b19f6","sections":[{"id":"content","title":"Content","role":"content","text":"CentOS and Red Hat Enterprise Linux (RHEL) distributions use [**firewalld**](https://firewalld.org/) by default to manage the firewall and configure [iptables](https://en.wikipedia.org/wiki/Iptables).\nThe default configuration assigns the network interfaces to the **public** zone and blocks all ports except port 22, which is used for [SSH](https://en.wikipedia.org/wiki/Secure_Shell).\n\nWhen you install Redis Software on CentOS or RHEL, it automatically creates two firewalld system services:\n\n- A service named **redislabs**, which includes all ports and protocols needed for communication between cluster nodes.\n- A service named **redislabs-clients**, which includes the ports and protocols needed for external communication (outside of the cluster).\n\nThese services are defined but not allowed through the firewall by default.\nDuring Redis Software installation, the [installer prompts](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/manage-installation-questions) you to confirm auto-configuration of a default (public) zone\nto allow the **redislabs** service.\n\nAlthough automatic firewall configuration simplifies installation, your deployment might not be secure if you did not use other methods to secure the host machine's network, such as external firewall rules or security groups.\nYou can use firewalld configuration tools such as **firewall-cmd** (command line) or **firewall-config** (UI)\nto create more specific firewall policies that allow these two services through the firewall, as necessary.\n\n> [!NOTE]\n> If databases are created with non-standard [Redis Software ports](https://redis.io/docs/latest/operate/rs/networking/port-configurations),\n> you need to explicitly configure firewalld to make sure those ports are not blocked."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/installing-upgrading/configuring/change-location-socket-files","title":"Change socket file locations","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/configuring/change-location-socket-files/","summary":"Change socket file locations.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:34+01:00","page_type":"content","content_hash":"5682641b9c0ae77cd341970a4fe1f3e51ad1da965d5862e5ed47a1319e87478c","sections":[{"id":"default-socket-file-locations","title":"Default socket file locations","role":"overview","text":"There are two default locations for the socket files in Redis Software:\n\n- `/tmp` - In clean installations of Redis Software version earlier than 5.2.2\n- `/var/opt/redislabs/run` - In clean installations of Redis Software version 5.2.2 and later\n\n    > [!NOTE]\n    > The default location was changed in case you run any maintenance procedures that delete the `/tmp` directory.\n\nWhen you upgrade Redis Software from an earlier version to 5.2.2 or later, the socket files are not moved to the new location by default. You need to specify the socket file location [during installation](#during-install)."},{"id":"during-install","title":"Specify socket file location during install","role":"content","text":"To specify the socket file location during a new installation, follow the [Install Redis Software on Linux](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/install-on-linux) instructions, but use the `-s` [installation script option](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/install-script):\n\n[code example]"},{"id":"after-cluster-setup","title":"Change socket file location for an existing cluster","role":"content","text":"Directly changing the socket file location directly is not supported for bootstrapped nodes. If you need to change the socket file location for an existing cluster, use one of the following methods instead:\n\n- [Replace nodes](#replace-nodes)\n\n- [Migrate the cluster using Replica Of](#migrate-replica-of)"},{"id":"replace-nodes","title":"Replace nodes","role":"content","text":"To change the socket file location for all nodes in a cluster, follow these steps:\n\n1. Install Redis Software on a new node with the new socket file location. Follow the [Install Redis Software on Linux](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/install-on-linux) instructions, but use the -s [installation script option](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/install-script):\n\n    [code example]\n\n1. [Add the new node](https://redis.io/docs/latest/operate/rs/clusters/add-node) to the existing cluster.\n\n1. [Remove a node](https://redis.io/docs/latest/operate/rs/clusters/remove-node#remove-a-node) with the old socket file location.\n\n1. Repeat the previous steps until all nodes with the old socket file location have been replaced."},{"id":"migrate-replica-of","title":"Migrate cluster using Replica Of","role":"content","text":"To migrate an existing cluster to a new cluster with a different socket file location:\n\n1. Create a new cluster with the new socket file location. To do so, follow the [Install Redis Software on Linux](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/install-on-linux) instructions, but use the `-s` [installation script option](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/install-script):\n\n    [code example]\n\n1. Use Replica Of to migrate your databases from the original cluster to the new cluster. For detailed steps, see the procedure to [configure Replica Of with different Redis Software clusters](https://redis.io/docs/latest/operate/rs/databases/import-export/replica-of/create#different-cluster)."}],"examples":[{"id":"during-install-ex0","language":"sh","code":"sudo ./install.sh -s </path/to/socket/files>","section_id":"during-install"},{"id":"replace-nodes-ex0","language":"sh","code":"sudo ./install.sh -s </path/to/socket/files>","section_id":"replace-nodes"},{"id":"migrate-replica-of-ex0","language":"plaintext","code":"sudo ./install.sh -s </path/to/socket/files>","section_id":"migrate-replica-of"}]}
{"schema_version":2,"id":"operate/rs/installing-upgrading/configuring","title":"Additional configuration","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/configuring/","summary":"","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","children":[{"id":"operate/rs/installing-upgrading/configuring/centos-rhel-firewall","summary":"Configure firewall rules for Redis Software on CentOS or Red Hat Enterprise Linux (RHEL).","title":"Configure CentOS/RHEL firewall","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/configuring/centos-rhel-firewall/"},{"id":"operate/rs/installing-upgrading/configuring/linux-swap","summary":"Turn off Linux swap space.","title":"Configure swap for Linux","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/configuring/linux-swap/"},{"id":"operate/rs/installing-upgrading/configuring/change-location-socket-files","summary":"Change socket file locations.","title":"Change socket file locations","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/configuring/change-location-socket-files/"}],"page_type":"content","content_hash":"bdd19802c5d2f6caf78689dee44e09505a44c177e891c9c305754692fd367d5c","sections":[{"id":"content","title":"Content","role":"content","text":"This section describes additional configuration options for Redis Software installation."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/installing-upgrading/configuring/linux-swap","title":"Configure swap for Linux","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/configuring/linux-swap/","summary":"Turn off Linux swap space.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:34+01:00","page_type":"content","content_hash":"b21f1036b07058d59427bd974436340198ec26ad3edacaf7a3493df18d2192f5","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Linux operating systems use swap space, which is enabled by default, to help manage memory (pages) by\ncopying pages from RAM to disk. Due to the way Redis Software\nutilizes and manages memory, it is best to prevent OS swapping. For more details, see [memory limits](https://redis.io/docs/latest/operate/rs/databases/memory-performance/memory-limit). The\nrecommendation is to turn off Linux swap completely in the OS.\n\nWhen you install or build the OS on the machine intended to host your Redis Software cluster, avoid configuring swap partitions if possible."},{"id":"turn-off-swap","title":"Turn off swap","role":"content","text":"To turn off swap in the OS of an existing server, VM, or instance, you\nmust have `sudo` access or be a root user to run the following commands.\n\nTurn off swap:\n\n[code example]\n\nTo ensure swap remains off even after a reboot:\n\n1. Open `/etc/fstab` in a text editor and locate the entry that defines the swap space.\n\n    Example swap space entries:\n\n    [code example]\n\n    [code example]\n\n1. Comment out the swap space entry, then save the file.\n\n    Example swap space entries commented out:\n\n    [code example]\n\n    [code example]"}],"examples":[{"id":"turn-off-swap-ex0","language":"sh","code":"sudo swapoff -a","section_id":"turn-off-swap"},{"id":"turn-off-swap-ex1","language":"sh","code":"/swapfile   none    swap    sw      0       0","section_id":"turn-off-swap"},{"id":"turn-off-swap-ex2","language":"sh","code":"UUID=1d5e1de7-226d-4f60-a99c-f7a31a045c59 none            swap    defaults        0 0","section_id":"turn-off-swap"},{"id":"turn-off-swap-ex3","language":"sh","code":"#/swapfile   none    swap    sw      0       0","section_id":"turn-off-swap"},{"id":"turn-off-swap-ex4","language":"sh","code":"#UUID=1d5e1de7-226d-4f60-a99c-f7a31a045c59 none            swap    defaults        0 0","section_id":"turn-off-swap"}]}
{"schema_version":2,"id":"operate/rs/installing-upgrading/creating-support-package","title":"Create a support package","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/creating-support-package/","summary":"Create a support package that gathers essential information to help debug issues.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:27:03+01:00","page_type":"content","content_hash":"14654f02cf28110f4cd5af3eae42ce87e31c75f7df3aa6f9538ce73f6db052a2","sections":[{"id":"overview","title":"Overview","role":"overview","text":"If you encounter any issues that you are not able to resolve yourself\nand need to [contact Redis support](https://redis.io/support/) for assistance, you can [create a support package](#create-support-package) that gathers all essential information to help debug\nyour issues.\n\n> [!NOTE]\n> The process of creating the support package can take several minutes and generates load on the system."},{"id":"create-support-package","title":"Create support package","role":"content","text":"**Cluster Manager UI:**\n\nTo create a support package from the Cluster Manager UI:\n\n1. In the navigation menu, select **Support**.\n\n    ![Select Support from the navigation menu and create a support package.](https://redis.io/docs/latest/images/rs/screenshots/create-support-package/support-package-dialog.png)\n\n1. Click **Proceed**.\n\n1. In the **Generate support package** dialog, select one of the following options:\n\n    - **Full package including all nodes and databases**: Creates a support package with information about all nodes and databases in the cluster.\n\n    - **For Databases**: Creates a support package that includes database information.\n\n        - Select **All Databases** from the **Database name (Database ID)** list to include information about all databases in the cluster.\n\n        - Select a specific database from the **Database name (Database ID)** list to include information about that database only.\n\n        ![The list of databases you can include in the support package.](https://redis.io/docs/latest/images/rs/screenshots/create-support-package/generate-support-package-select-db.png)\n\n    - **For Nodes**: Creates a support package that includes node information.\n\n        - Select **All Nodes** from the **Node ID (IP Addresses)** list to include information about all nodes in the cluster.\n\n        - Select a specific node from the **Node ID (IP Addresses)** list to include information about that node only.\n\n        ![The list of nodes you can include in the support package.](https://redis.io/docs/latest/images/rs/screenshots/create-support-package/generate-support-package-select-node.png)\n\n1. Click **Generate package**.\n\n1. The package is created and downloaded by your browser.\n\n    ![An alert appears that says, 'Support package created, attach it to your request in the Redis Support portal'. The Redis Support portal is a button you can click to contact Redis Support.](https://redis.io/docs/latest/images/rs/screenshots/create-support-package/support-package-created-alert.png)\n\n**rladmin:**\n\nIf package creation fails with `internal error` or if you cannot access the UI, create a support package for the cluster from the command line on any node in the cluster using the [`rladmin cluster debug_info`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/debug_info) command: \n\n[code example]\n\n- If `rladmin cluster debug_info` fails for lack of space in the `/tmp` directory, you can:\n\n    1. Change the storage location where the support package is saved: \n    \n        [code example]\n\n        The `redislabs` user must have write access to the storage location on all cluster nodes.\n\n    1. On any node in the cluster, run:\n        \n        [code example]\n\n- If `rladmin cluster debug_info` fails for another reason, you can create a support package for the cluster from the command line on each node in the cluster with the command: \n\n    [code example]\n\nUpload the tar file to [Redis support](https://redis.com/company/support/). The path to the archive is shown in the command output.\n\n**REST API:**\n\nYou can use `debuginfo` [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api) requests to create and download support packages.\n\nTo download debug info from all nodes and databases:\n\n[code example]\n\nTo download debug info from all nodes:\n\n[code example]\n\nTo download debug info from a specific node, replace `<uid>` in the following request with the node ID:\n\n[code example]\n\nTo download debug info from all databases:\n\n[code example]\n\nTo download debug info from a specific database, replace `<uid>` in the following request with the database ID:\n\n[code example]"},{"id":"support-package-files","title":"Support package files","role":"compatibility","text":"The support package is a zip file that contains all cluster configuration and logs.\n\nWhen downloaded from the Cluster Manager UI, the support package's name is `debuginfo.tar.gz`."},{"id":"database-support-package-files","title":"Database support package files","role":"content","text":"Cluster and database support packages collect database details in `database_<bdb_uid>` directories, where `<bdb_uid>` is the database ID, and Redis shard details in `<node_uid>` directories.\n\nThe following table describes the included files:\n\n| File | Description |\n|------|-------------|\n| ccs-redis.json | Primary node's local cluster configuration store (CCS). |\n| /database_<bdb_uid>/ | Directory that includes files for a specific database.<bdb_uid> is the database ID. |\n| database_<bdb_uid>_ccs_info.txt | Database information from the cluster configuration store (CCS). Includes settings for databases, endpoints, shards, replicas, and CRDB. |\n| database_<bdb_uid>.clientlist | List of clients connected to the database when the support package was created. |\n| database_<bdb_uid>.info | Redis information and statistics for the database. See [`INFO`](https://redis.io/docs/latest/commands/info) for details about the collected fields. |\n| database_<bdb_uid>.rladmin | Database information. See [`rladmin info db`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/info#info-db) for an example of collected fields. Also includes creation time, last changed time, Redis version, memory limit, persistence type, eviction policy, hashing policy, and whether SSL, backups, and email alerts are enabled. |\n| database_<bdb_uid>.slowlog | Contains slowlog output, which includes commands that took longer than 10 milliseconds. Only included if `slowlog_in_sanitized_support` is `true` in cluster settings. |\n| /node_<node_uid>/redis_<shard_uid>.txt | For each shard of the specified database only. Includes shard configuration and [information](https://redis.io/docs/latest/commands/info), slowlog information, and latency information. |"},{"id":"node-support-package-files","title":"Node support package files","role":"content","text":"Cluster and node support packages collect node details in `node_<node_uid>` directories, where `<node_uid>` is the node ID.\n\nThe following table describes the included files:\n\n| File | Description |\n|------|-------------|\n| ccs-redis.json | The node's local cluster configuration store (CCS). |\n| /conf/ | Directory that contains configuration files. |\n| /logs/ | Directory that includes logs. |\n| /metrics/standard/ | Directory that contains the node's standard-tier metrics history as Prometheus-compatible time series database (TSDB) blocks. For when this directory is included, see [Local metrics storage](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/local-metrics-storage#support-packages). |\n| /metrics/granular/ | Directory that contains the node's granular metrics as Prometheus-compatible TSDB blocks. For when this directory is included, see [Local metrics storage](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/local-metrics-storage#support-packages). |\n| node_<node_uid>.ccs | Includes cluster configuration, node configuration, and DMC proxy configuration. |\n| node_<node_uid>_envoy_config.json | Envoy configuration. |\n| node_<node_uid>.rladmin | Information about the cluster's nodes, databases, endpoints, and shards. See [`rladmin status`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status) for example output. |\n| node_<node_uid>_sys_info.txt | Node's system information including:<br />• Socket files list<br />• Log files list<br />• Processes running on the node<br />• Disk usage<br />• Persistent files list<br />• Memory usage<br />• Network interfaces<br />• Installed packages<br />• Active iptables<br />• OS and platform<br />• Network connection<br />• Status of Redis processes |\n| redis_<shard_uid>.txt | For each shard of the specified database only. Includes shard configuration and [information](https://redis.io/docs/latest/commands/info), slowlog information, and latency information. |\n\nEach node's `/conf/` directory contains the following files:\n\n- bootstrap_status.json\n- ccs-paths.conf\n- config.json\n- envoy.yaml\n- gossip_envoy.yaml\n- heartbeatd-config.json\n- last_bootstrap.json\n- local_addr.conf\n- node.id\n- node_local_config.json\n- redislabs_env_config.sh\n- socket.conf\n- supervisord_alert_mgr.conf\n- supervisord_cm_server.conf\n- supervisord_crdb_coordinator.conf\n- supervisord_crdb_worker.conf\n- supervisord_mdns_server.conf\n- supervisord_pdns_server.conf\n\nEach node's `/conf/` directory also contains the following key and cert modulus files:\n\n- api_cert.modulus\n- api_key.modulus\n- ccs_internode_encryption_cert.modulus\n- ccs_internode_encryption_key.modulus\n- cm_cert.modulus\n- cm_key.modulus\n- data_internode_encryption_cert.modulus\n- data_internode_encryption_key.modulus\n- gossip_ca_signed_cert.modulus\n- gossip_ca_signed_key.modulus\n- mesh_ca_signed_cert.modulus\n- mesh_ca_signed_key.modulus\n- metrics_exporter_cert.modulus\n- metrics_exporter_key.modulus\n- proxy_cert.modulus\n- proxy_key.modulus\n- syncer_cert.modulus\n- syncer_key.modulus"},{"id":"related-info","title":"Related info","role":"related","text":"- [Uploading Support Packages & Cluster Health Analysis](https://support.redislabs.com/hc/en-us/articles/26502436628370-Uploading-Support-Packages-Cluster-Health-Analysis)"}],"examples":[{"id":"create-support-package-ex0","language":"sh","code":"/opt/redislabs/bin/rladmin cluster debug_info","section_id":"create-support-package"},{"id":"create-support-package-ex1","language":"sh","code":"rladmin cluster config debuginfo_path <path>","section_id":"create-support-package"},{"id":"create-support-package-ex2","language":"sh","code":"rladmin cluster debug_info","section_id":"create-support-package"},{"id":"create-support-package-ex3","language":"sh","code":"/opt/redislabs/bin/debuginfo","section_id":"create-support-package"},{"id":"create-support-package-ex4","language":"sh","code":"GET /v1/cluster/debuginfo","section_id":"create-support-package"},{"id":"create-support-package-ex5","language":"sh","code":"GET /v1/nodes/debuginfo","section_id":"create-support-package"},{"id":"create-support-package-ex6","language":"sh","code":"GET /v1/nodes/<uid>/debuginfo","section_id":"create-support-package"},{"id":"create-support-package-ex7","language":"sh","code":"GET /v1/bdbs/debuginfo","section_id":"create-support-package"},{"id":"create-support-package-ex8","language":"sh","code":"GET /v1/bdbs/<uid>/debuginfo","section_id":"create-support-package"}]}
{"schema_version":2,"id":"operate/rs/installing-upgrading","title":"Install, set up, and upgrade Redis Software","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/","summary":"Learn how to install, set up, and upgrade Redis Software.","tags":["docs","operate","rs"],"last_updated":"2026-10-06T08:53:14-07:00","children":[{"id":"operate/rs/installing-upgrading/quickstarts","summary":"Follow these quickstarts to try out Redis Software.","title":"Redis Software quickstarts","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/"},{"id":"operate/rs/installing-upgrading/move-from-open-source","summary":"Plan a move from a self-managed Redis Open Source deployment to Redis Software, including how configuration works differently and how to migrate your data.","title":"Move from Redis Open Source to Redis Software","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/move-from-open-source/"},{"id":"operate/rs/installing-upgrading/install","summary":"Install Redis Software on Linux.","title":"Install Redis Software","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/install/"},{"id":"operate/rs/installing-upgrading/modules","summary":"","title":"Install and upgrade modules","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/"},{"id":"operate/rs/installing-upgrading/upgrading","summary":"How to upgrade a cluster's Redis Software version, database version, and operating system.","title":"Upgrade an existing Redis Software deployment","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/"},{"id":"operate/rs/installing-upgrading/uninstalling","summary":"","title":"Uninstall Redis Software","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/uninstalling/"},{"id":"operate/rs/installing-upgrading/configuring","summary":"","title":"Additional configuration","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/configuring/"},{"id":"operate/rs/installing-upgrading/product-lifecycle","summary":"The product lifecycle of Redis Software cluster versions.","title":"Redis Software product lifecycle","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle/"},{"id":"operate/rs/installing-upgrading/modules-lifecycle","summary":"","title":"Module lifecycle","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/modules-lifecycle/"},{"id":"operate/rs/installing-upgrading/creating-support-package","summary":"Create a support package that gathers essential information to help debug issues.","title":"Create a support package","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/creating-support-package/"}],"page_type":"content","content_hash":"9de24cb8cfffb96b28c2f518340cf17dfd74ff5f206da89ee123cdef79e56fe4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can run self-managed Redis Software in an on-premises data center or on your preferred cloud platform. \n\nIf you prefer a fully managed Redis database-as-a-service, available on major public cloud services, consider setting up a [Redis Cloud](https://redis.io/docs/latest/operate/rc) subscription. You can [try Redis Cloud](https://redis.io/try-free/) for free."},{"id":"quickstarts","title":"Quickstarts","role":"setup","text":"If you want to try out Redis Software, see the following quickstarts:\n\n- [Redis Software quickstart](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/redis-enterprise-software-quickstart)\n\n- [Docker quickstart for Redis Software](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart)"},{"id":"install-redis-software","title":"Install Redis Software","role":"setup","text":"To install Redis Software on a [supported platform](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/supported-platforms), you need to:\n\n1. [Plan your deployment](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment).\n\n1. [Prepare to install](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/prepare-install).\n\n1. [Perform the install](https://redis.io/docs/latest/operate/rs/installing-upgrading/install).\n\nDepending on your needs, you may also want to [customize the installation](#more-info-and-options).\n\nIf you're moving from a self-managed Redis Open Source deployment, see [Move from Redis Open Source to Redis Software](https://redis.io/docs/latest/operate/rs/installing-upgrading/move-from-open-source) for how configuration and deployment differ."},{"id":"upgrade-existing-deployment","title":"Upgrade existing deployment","role":"content","text":"If you already installed Redis Software, you can:\n\n- [Upgrade a cluster](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-cluster)\n\n- [Upgrade a database](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-database)\n\n- [Upgrade an Active-Active database](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-active-active)"},{"id":"install-and-upgrade-modules","title":"Install and upgrade modules","role":"setup","text":"To add capabilities with modules, you can:\n\n- [Install a module on a cluster](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/add-module-to-cluster)\n\n- [Enable a module for a database](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/add-module-to-database)\n\n- [Upgrade modules](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/upgrade-module)\n\nFor module versioning and end-of-life dates, see [Module lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules-lifecycle)."},{"id":"uninstall-redis-software","title":"Uninstall Redis Software","role":"content","text":"- [Uninstall existing deployment](https://redis.io/docs/latest/operate/rs/installing-upgrading/uninstalling)"},{"id":"more-info-and-options","title":"More info and options","role":"content","text":"More information is available to help with customization and related questions:\n\n- [CentOS/RHEL firewall configuration](https://redis.io/docs/latest/operate/rs/installing-upgrading/configuring/centos-rhel-firewall)\n- [Change socket file location](https://redis.io/docs/latest/operate/rs/installing-upgrading/configuring/change-location-socket-files)\n- [Cluster DNS configuration](https://redis.io/docs/latest/operate/rs/networking/cluster-dns)\n- [Cluster load balancer setup](https://redis.io/docs/latest/operate/rs/networking/cluster-lba-setup)\n- [File locations](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/file-locations)\n- [Linux swap space configuration](https://redis.io/docs/latest/operate/rs/installing-upgrading/configuring/linux-swap)\n- [mDNS client prerequisites](https://redis.io/docs/latest/operate/rs/networking/mdns)\n- [User and group ownership](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/customize-user-and-group)"},{"id":"next-steps","title":"Next steps","role":"content","text":"After you install Redis Software and set up your cluster, you can:\n\n- [Add users](https://redis.io/docs/latest/operate/rs/security/access-control/create-users) to the cluster with specific permissions.  To begin, start with [Access control](https://redis.io/docs/latest/operate/rs/security/access-control).\n\n- [Create databases](https://redis.io/docs/latest/operate/rs/databases/create) to use with your applications."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/installing-upgrading/install/customize-install-directories","title":"Customize installation directories","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/install/customize-install-directories/","summary":"Customize Redis Software installation directories.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:34+01:00","page_type":"content","content_hash":"45e9ded922fde75bf74b00c1a7135ff9489e87c46ec285bd4f5bd5e918ed277b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"When you install Redis Software on Red Hat Enterprise Linux, you can customize the installation directories.\n\nThe files are installed in the `redislabs` directory located in the path that you specify."},{"id":"considerations","title":"Considerations","role":"content","text":"- When you install with custom directories, the installation does not run as an RPM file.\n\n- If a `redislabs` directory already exists in the path that you specify, the installation fails.\n\n- All nodes in a cluster must be installed with the same file locations.\n\n- Custom installation directories are not supported for databases using Auto Tiering.\n\n- [Configure different mount points for data and log directories](#config-diff-data-log-dirs)."},{"id":"specify-file-locations","title":"Specify file locations","role":"content","text":"You can specify these file locations:\n\n| Files               | Installer flag | Example parameter | Example file location |\n| ------------------- | -------------- | ----------------- | --------------------- |\n| Binaries files      | --install-dir  | /opt              | /opt/redislabs        |\n| Configuration files | --config-dir   | /etc/opt          | /etc/opt/redislabs    |\n| Data and log files  | --var-dir      | /var/opt          | /var/opt/redislabs    |\n\nThese files are not in the custom directories:\n\n- OS files\n    - /etc/cron.d/redislabs\n    - /etc/firewalld/services\n    - /etc/firewalld/services/redislabs-clients.xml\n    - /etc/firewalld/services/redislabs.xml\n    - /etc/ld.so.conf.d/redislabs_ldconfig.conf.tmpl\n    - /etc/logrotate.d/redislabs\n    - /etc/profile.d/redislabs_env.sh\n    - /usr/lib/systemd/system/rlec_supervisor.service.tmpl\n    - /usr/share/selinux/mls/redislabs.pp\n    - /usr/share/selinux/targeted/redislabs.pp\n\n- Installation reference files\n    - /etc/opt/redislabs/redislabs_custom_install_version\n    - /etc/opt/redislabs/redislabs_env_config.sh\n\nTo specify directories during [installation](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/install-on-linux), include installer flags as [command-line options](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/install-script) when you run the `install.sh` script. For example:\n\n[code example]"},{"id":"config-diff-data-log-dirs","title":"Configure different mount points for data and log directories","role":"configuration","text":"To configure different mount points for data and log directories, use symbolic links:\n\n1. Create a symbolic link for the data directory:\n\n    [code example]\n\n1. Create a symbolic link for the log directory:\n\n    [code example]"},{"id":"customize-storage-paths","title":"Customize storage paths","role":"content","text":"When you install Redis Software to custom directories, you can specify custom storage paths for persistent and ephemeral storage during [cluster setup](https://redis.io/docs/latest/operate/rs/clusters/new-cluster-setup) or when [adding nodes](https://redis.io/docs/latest/operate/rs/clusters/add-node).\n\n\n1. Specify the custom file location with the `--var-dir` option when you install Redis Software:\n\n    [code example]\n\n2. Grant the `redislabs` user access to the custom directories:\n   [code example]\n\n3. Confirm the storage path is properly mounted:\n   [code example]\n\n4. Verify the directory exists and is accessible:\n   [code example]\n\nFor more information about storage requirements, see [Persistent and ephemeral node storage](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/persistent-ephemeral-storage)."},{"id":"limitations","title":"Limitations","role":"limits","text":"Several Redis Software installation reference files are installed to the directory `/etc/opt/redislabs/` even if you use custom installation directories.\n\nAs a workaround to install Redis Software without using any root directories, do the following before installing Redis Software:\n\n1. Create all custom, non-root directories you want to use with Redis Software.\n\n1. Mount `/etc/opt/redislabs` to one of the custom, non-root directories."}],"examples":[{"id":"specify-file-locations-ex0","language":"sh","code":"sudo ./install.sh --install-dir <path> --config-dir <path> --var-dir <path>","section_id":"specify-file-locations"},{"id":"config-diff-data-log-dirs-ex0","language":"sh","code":"ln -s /var/opt/redislabs/data </path/to/data/mount/point>","section_id":"config-diff-data-log-dirs"},{"id":"config-diff-data-log-dirs-ex1","language":"sh","code":"ln -s /var/opt/redislabs/log </path/to/log/mount/point>","section_id":"config-diff-data-log-dirs"},{"id":"customize-storage-paths-ex0","language":"sh","code":"sudo ./install.sh --var-dir <path>","section_id":"customize-storage-paths"},{"id":"customize-storage-paths-ex1","language":"sh","code":"$ sudo chown -R redislabs:redislabs /path/to/custom/storage\n   $ sudo chmod -R 755 /path/to/custom/storage","section_id":"customize-storage-paths"},{"id":"customize-storage-paths-ex2","language":"sh","code":"$ df -h /path/to/custom/storage\n   $ mount | grep /path/to/custom/storage","section_id":"customize-storage-paths"},{"id":"customize-storage-paths-ex3","language":"sh","code":"ls -l /path/to/custom/storage","section_id":"customize-storage-paths"}]}
{"schema_version":2,"id":"operate/rs/installing-upgrading/install/customize-user-and-group","title":"Customize system user and group","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/install/customize-user-and-group/","summary":"Specify the user and group who own all Redis Software processes.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:34+01:00","page_type":"content","content_hash":"853e95b283ff66ba4756323479dacebc107b67c22909f71d607a7746531b3f3a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"By default, Redis Software is installed with the user:group `redislabs:redislabs`.\n\nDuring installation, you can specify the user and group that own all Redis Software processes."},{"id":"considerations","title":"Considerations","role":"content","text":"- Custom installation user is supported on Red Hat Enterprise Linux and compatible distributions.\n\n- You must create the user and group before installing Redis Software.\n\n- You must install Redis Software on all nodes in the cluster with the same user and group.\n\n- You can specify an LDAP user as the installation user.\n\n- If you specify the user only, then installation is run with the primary group that the user belongs to.\n\n- The custom group must be the user's primary group. If the user's primary group differs from the custom group, database backups can fail because the installation user cannot change backup file ownership to a different group."},{"id":"install-with-custom-user-or-group","title":"Install with custom user or group","role":"setup","text":"To customize the user or group during [installation](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/install-on-linux), include the `--os-user` or `--os-group` [command-line options](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/install-script) when you run the `install.sh` script.\n\n[code example]"}],"examples":[{"id":"install-with-custom-user-or-group-ex0","language":"sh","code":"sudo ./install.sh --os-user <user> --os-group <group>","section_id":"install-with-custom-user-or-group"}]}
{"schema_version":2,"id":"operate/rs/installing-upgrading/install","title":"Install Redis Software","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/install/","summary":"Install Redis Software on Linux.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:34+01:00","children":[{"id":"operate/rs/installing-upgrading/install/plan-deployment","summary":"Plan a deployment of Redis Software.","title":"Plan Redis Software deployment","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/"},{"id":"operate/rs/installing-upgrading/install/prepare-install","summary":"Prepare to install Redis Software.","title":"Prepare to install Redis Software","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/install/prepare-install/"},{"id":"operate/rs/installing-upgrading/install/install-on-linux","summary":"Install Redis Software on Linux.","title":"Install Redis Software on Linux","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/install/install-on-linux/"},{"id":"operate/rs/installing-upgrading/install/install-script","summary":"Command-line options for the install.sh script.","title":"Installation script command-line options","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/install/install-script/"},{"id":"operate/rs/installing-upgrading/install/manage-installation-questions","summary":"Describes Redis Software installation questions and how to answer them automatically.","title":"Manage installation questions","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/install/manage-installation-questions/"},{"id":"operate/rs/installing-upgrading/install/customize-install-directories","summary":"Customize Redis Software installation directories.","title":"Customize installation directories","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/install/customize-install-directories/"},{"id":"operate/rs/installing-upgrading/install/customize-user-and-group","summary":"Specify the user and group who own all Redis Software processes.","title":"Customize system user and group","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/install/customize-user-and-group/"},{"id":"operate/rs/installing-upgrading/install/offline-installation","summary":"If you install Redis Software on a machine with no internet connection, you need to perform two tasks manually.","title":"Offline installation","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/install/offline-installation/"}],"page_type":"content","content_hash":"315da48c6aec96da24be03d3f04606fe3a8fcb6b940b032a2dfb396d349a73df","sections":[{"id":"overview","title":"Overview","role":"overview","text":"After you [plan your deployment](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment), [download a Redis Software installation package](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/prepare-install/download-install-package), and finish [installation preparation](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/prepare-install):\n\n1. [Install the Redis Software package](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/install-on-linux) on one of the nodes in the cluster.\n\n1. Repeat this process for each node in the cluster.\n\nFor installation on machines without an internet connection, see [Offline installation](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/offline-installation)."},{"id":"permissions-and-access","title":"Permissions and access","role":"security","text":"- Redis Software installation creates the `redislabs:redislabs` user and group. \n\n    Assigning other users to the `redislabs` group is optional. Users belonging to the `redislabs` group have permission to read and execute (e.g. use the `rladmin` status command) but are not allowed to write (or delete) files or directories.\n\n- Redis Software is certified to run with permissions set to `750`, an industry standard.\n\n    > [!WARNING]\n    > Do not reduce permissions to `700`. This configuration has not been tested and is not supported.\n\n- Redis Software requires `umask` to be `0022` during installation to correctly set permissions for the installed directories and files."},{"id":"more-info-and-options","title":"More info and options","role":"content","text":"If you've already installed Redis Software, you can also:\n\n- [Upgrade an existing deployment](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading).\n\n- [Uninstall an existing deployment](https://redis.io/docs/latest/operate/rs/installing-upgrading/uninstalling).\n\nTo learn more about customization and find answers to related questions, see:\n\n- [CentOS/RHEL Firewall configuration](https://redis.io/docs/latest/operate/rs/installing-upgrading/configuring/centos-rhel-firewall)\n- [Change socket file location](https://redis.io/docs/latest/operate/rs/installing-upgrading/configuring/change-location-socket-files)\n- [Cluster DNS configuration](https://redis.io/docs/latest/operate/rs/networking/cluster-dns)\n- [Cluster load balancer setup](https://redis.io/docs/latest/operate/rs/networking/cluster-lba-setup)\n- [File locations](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/file-locations)\n- [Supported platforms](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/supported-platforms)\n- [Manage installation questions](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/manage-installation-questions)\n- [mDNS client prerequisites](https://redis.io/docs/latest/operate/rs/networking/mdns)\n- [User and group ownership](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/customize-user-and-group)"},{"id":"next-steps","title":"Next steps","role":"content","text":"After your cluster is set up with nodes, you can:\n\n- [Add users](https://redis.io/docs/latest/operate/rs/security/access-control/create-users) to the cluster with specific permissions.  To begin, start with [Access control](https://redis.io/docs/latest/operate/rs/security/access-control).\n- [Create databases](https://redis.io/docs/latest/operate/rs/databases/create) to use with your applications."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/installing-upgrading/install/install-on-linux","title":"Install Redis Software on Linux","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/install/install-on-linux/","summary":"Install Redis Software on Linux.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:34+01:00","page_type":"content","content_hash":"6b78930309ac178409d2d3594eb70561ed1bf94be92e3da4561ec4620fd265c5","sections":[{"id":"overview","title":"Overview","role":"overview","text":"After you [download a Redis Software installation package](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/prepare-install/download-install-package), install it on one of the nodes in the cluster.\n\nFor installation on machines without an internet connection, see [Offline installation](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/offline-installation)."},{"id":"install-on-linux","title":"Install on Linux","role":"setup","text":"To install Redis Software, use the command line:\n\n1. Copy the installation package to the node.\n\n1. On the node, change to the directory where the installation package is located and extract the installation files:\n\n    [code example]\n\n1.  _(Optional)_ Use the  to confirm the authenticity of Ubuntu/Debian or RHEL RPM packages:\n\n    - For Ubuntu:\n        1. Import the key:\n        [code example]\n        2. Verify the package signature:\n        [code example]\n\n    - For RHEL:\n        1. Import the key:\n        [code example]\n        2. Verify the package signature:\n         [code example]\n\n1. To start the installation process, run the installation script. See [installation script options](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/install-script) for a complete list of command-line options.\n\n    **For default installation:**\n    [code example]\n\n    **For custom installation directories:**\n\n    If you need to specify custom storage paths for persistent or ephemeral storage during cluster setup, you must install Redis Software to custom directories.\n\n    [code example]\n\n    For detailed information about custom installations, see [Customize installation directories](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/customize-install-directories).\n\n    > [!NOTE]\n    > - The Redis Software files are installed in the default [file locations](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/file-locations) unless you specify custom directories.\n    > - By default, Redis Software runs on the OS as the `redislabs` user and `redislabs` group. If needed, you can [specify a different user and group](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/customize-user-and-group) during the installation.\n    > - You must either be the root user or use `sudo` to run the installation script.\n\n1. Answer the [installation questions](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/manage-installation-questions) when shown to complete the installation process.\n\n    > [!NOTE]\n    > To skip the installation questions, use one of the following methods:\n    >\n    > - Run `./install.sh -y` to answer yes to all of the questions.\n    > - Create an [answer file](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/manage-installation-questions#configure-file-to-answer) to answer installation questions automatically.\n\n1. When installation completes successfully, the output displays the Cluster Manager UI's IP address:\n\n    [code example]\n\n1. Repeat this process for each node in the cluster."},{"id":"redis-flex-and-auto-tiering-installation","title":"Redis Flex and Auto Tiering installation","role":"content","text":"If you want to use Redis Flex or Auto Tiering for your databases, review the prerequisites, storage requirements, and [other considerations](https://redis.io/docs/latest/operate/rs/databases/flash) and prepare and format the flash memory.\n\nAfter you install Redis Software, use the `prepare_flash` script to prepare and format flash memory:\n\n[code example]\n\nThis script finds unformatted disks and mounts them as RAID partitions in `/var/opt/redislabs/flash`.\n\nTo verify the disk configuration, run:\n\n[code example]"},{"id":"more-info-and-options","title":"More info and options","role":"content","text":"To learn more about customization and find answers to related questions, see:\n\n- [CentOS/RHEL firewall configuration](https://redis.io/docs/latest/operate/rs/installing-upgrading/configuring/centos-rhel-firewall)\n- [Change socket file location](https://redis.io/docs/latest/operate/rs/installing-upgrading/configuring/change-location-socket-files)\n- [Cluster DNS configuration](https://redis.io/docs/latest/operate/rs/networking/cluster-dns)\n- [Cluster load balancer setup](https://redis.io/docs/latest/operate/rs/networking/cluster-lba-setup)\n- [mDNS client prerequisites](https://redis.io/docs/latest/operate/rs/networking/mdns)\n- [File locations](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/file-locations)\n- [Supported platforms](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/supported-platforms)"},{"id":"limitations","title":"Limitations","role":"limits","text":"Several Redis Software installation reference files are installed to the directory `/etc/opt/redislabs/` even if you use [custom installation directories](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/customize-install-directories).\n\nAs a workaround to install Redis Software without using any root directories, do the following before installing Redis Software:\n\n1. Create all custom, non-root directories you want to use with Redis Software.\n\n1. Mount `/etc/opt/redislabs` to one of the custom, non-root directories."},{"id":"next-steps","title":"Next steps","role":"content","text":"1. [Create](https://redis.io/docs/latest/operate/rs/clusters/new-cluster-setup)\n    or [join](https://redis.io/docs/latest/operate/rs/clusters/add-node) an existing Redis Software cluster.\n\n1. [Create a database](https://redis.io/docs/latest/operate/rs/databases/create).\n\n    For geo-distributed Active-Active replication, create an [Active-Active](https://redis.io/docs/latest/operate/rs/databases/active-active/create) database.\n\n1. [Add users](https://redis.io/docs/latest/operate/rs/security/access-control/create-users) to the cluster with specific permissions.  To begin, start with [Access control](https://redis.io/docs/latest/operate/rs/security/access-control)."}],"examples":[{"id":"install-on-linux-ex0","language":"sh","code":"tar vxf <tarfile name>","section_id":"install-on-linux"},{"id":"install-on-linux-ex1","language":"sh","code":"gpg --import <path to GPG key>","section_id":"install-on-linux"},{"id":"install-on-linux-ex2","language":"sh","code":"dpkg-sig --verify </path-to/package.deb>","section_id":"install-on-linux"},{"id":"install-on-linux-ex3","language":"sh","code":"rpm --import <path to GPG key>","section_id":"install-on-linux"},{"id":"install-on-linux-ex4","language":"sh","code":"rpm --checksig </path-to/package.rpm>","section_id":"install-on-linux"},{"id":"install-on-linux-ex5","language":"sh","code":"sudo ./install.sh","section_id":"install-on-linux"},{"id":"install-on-linux-ex6","language":"sh","code":"sudo ./install.sh --install-dir <path> --config-dir <path> --var-dir <path>","section_id":"install-on-linux"},{"id":"install-on-linux-ex7","language":"sh","code":"Summary:\n    -------\n    ALL TESTS PASSED.\n    2017-04-24 10:54:15 [!] Please logout and login again to make\n    sure all environment changes are applied.\n    2017-04-24 10:54:15 [!] Point your browser at the following\n    URL to continue:\n    2017-04-24 10:54:15 [!] https://<your_ip_here>:8443","section_id":"install-on-linux"},{"id":"redis-flex-and-auto-tiering-installation-ex0","language":"sh","code":"sudo /opt/redislabs/sbin/prepare_flash.sh","section_id":"redis-flex-and-auto-tiering-installation"},{"id":"redis-flex-and-auto-tiering-installation-ex1","language":"sh","code":"sudo lsblk","section_id":"redis-flex-and-auto-tiering-installation"}]}
{"schema_version":2,"id":"operate/rs/installing-upgrading/install/install-script","title":"Installation script command-line options","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/install/install-script/","summary":"Command-line options for the install.sh script.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:34+01:00","page_type":"content","content_hash":"f27efd376d0c5e178e89e96a35bcce9f313769c5328b239c72876908bab97599","sections":[{"id":"content","title":"Content","role":"content","text":"Run `./install.sh --help` to view command-line options supported by the installation script.\n\nThe following options are supported:\n\n| Option | Description |\n|--------|-------------|\n| `-y` | Automatically answers `yes` to all install prompts, accepting all default values<br/>See [Manage install questions](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/manage-installation-questions)|\n| <nobr>`-c <answer file>`</nobr> | Specify answer file used to respond to install prompts<br/>See [Manage install questions](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/manage-installation-questions)|\n| <nobr>`-s <socket dir>`</s> | Specify directory for redislabs unix sockets  _(new installs only)_|\n| <nobr>`--install-dir <dir>`</nobr> | Specifies installation directory _(new installs only)_ <br/> See [Customize install locations](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/customize-install-directories)|  \n| <nobr>`--config-dir <dir>` | Configuration file directory *(new installs only)* <br/>See [Customize install locations](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/customize-install-directories)|\n| <nobr>`--var-dir <dir>`</nobr> | Var directory used for installation *(new installs only)* <br/>See [Customize install locations](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/customize-install-directories)|\n| <nobr>`--os-user <user>`| Operating system user account associated with install; default: `redislabs`<br/>See [Customize user and group](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/customize-user-and-group) *(new installs only)*|\n|<nobr>`--os-group <group>` | Operating system group associated with install; default: `redislabs`<br/>See [Customize user and group](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/customize-user-and-group) *(new installs only)* |\n| `--skip-dns-port-verification` | Skip DNS port verification during installation |\n| <nobr>`--update-env-path`</nobr> | Add paths related to Redis Software to root's PATH environment variable<br/>See [Manage install questions](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/manage-installation-questions) |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/installing-upgrading/install/manage-installation-questions","title":"Manage installation questions","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/install/manage-installation-questions/","summary":"Describes Redis Software installation questions and how to answer them automatically.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:34+01:00","page_type":"content","content_hash":"ee9de0a6aa66f558045216a85201f19b5b263038600cc78499ceca281df34c0a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Several questions are displayed during the Redis Software installation process.\n\nHere, you'll find a list of these questions and learn how to automatically answer these questions to perform a silent install."},{"id":"installation-questions","title":"Installation questions","role":"setup","text":"Several questions appear during installation:\n\n- **Linux swap file** - `Swap is enabled. Do you want to proceed? [Y/N]?`\n\n    We recommend that you [disable Linux swap](https://redis.io/docs/latest/operate/rs/installing-upgrading/configuring/linux-swap) in the operating system configuration\n    to give Redis Software control of the memory allocation.\n\n- **Automatic OS tuning** - `Do you want to automatically tune the system for best performance [Y/N]?`\n\n    To allow the installation process to optimize the OS for Redis Software, answer `Y`.\n    The installation process prompts you for additional information.\n\n    The `/opt/redislabs/sbin/systune.sh` file contains details about the tuning process.\n\n- **Network time** - `Do you want to set up NTP time synchronization now [Y/N]?`\n\n    Redis Software requires that all cluster nodes have synchronized time.\n    You can either let the installation process configure NTP\n    or you can [configure NTP manually](https://redis.io/docs/latest/operate/rs/clusters/configure/sync-clocks).\n\n- **Firewall ports** - `Would you like to open RedisLabs cluster ports on the default firewall zone [Y/N]?`\n\n    Redis Software requires that all nodes have [specific network ports](https://redis.io/docs/latest/operate/rs/networking/port-configurations) open.\n    To open the ports, you can:\n\n    - Answer `Y` to let the installation process open these ports.\n    - Answer `N` and configure the firewall manually for [RHEL/CentOS firewall](https://redis.io/docs/latest/operate/rs/installing-upgrading/configuring/centos-rhel-firewall).\n    - Answer `N` and configure the firewall on the node manually for your OS.\n\n- **Installation verification (rlcheck)** - `Would you like to run rlcheck to verify proper configuration? [Y/N]?`\n\n    Run the `rlcheck` installation verification to make sure that the installation completed successfully.\n    If you want to run this verification at a later time, you can run:\n    \n    [code example]\n\n- **User already exists** - `The user 'redislabs' already exists, which may lead to problems if it wasn't configured correctly. Would you like to proceed with the installation? (Y/N)?`\n\n- **Group already exists** - `The group 'redislabs' already exists, which may lead to problems if it wasn't configured correctly. Would you like to proceed with the installation? (Y/N)?`\n\n- **Update PATH** - `Add Redis-Enterprise paths to $PATH variable (recommended) [Y/N]?`"},{"id":"answer-install-questions-automatically","title":"Answer install questions automatically","role":"content","text":"To perform a silent (or automated) install, answer the questions when you start the [install](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/install-on-linux)."},{"id":"answer-yes-to-all-questions","title":"Answer yes to all questions","role":"content","text":"To automatically answer `yes` to all questions (which accepts the default values), run the [installation script](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/install-script) with the `-y` parameter:\n\n[code example]"},{"id":"configure-file-to-answer","title":"Configure file to answer","role":"configuration","text":"Use an answer file to manage your response:\n\n1. Create a text file to serve as an answer file.\n\n    The answer file can contain any of the following installation question parameters. If a parameter is not included in the file, the installation script will ask for your answer.\n\n    | Parameter | Values | Description |\n    |-----------|--------|-------------|\n    | `firewall` | `yes`<br />`no` | Configure firewall and open required Redis ports. |\n    | `ignore_existing_osuser_osgroup` | `yes`<br />`no` | Proceed if redislabs user/group already exists. |\n    | `ignore_master_version` | `yes`<br />`no` | Continue the upgrade even if the primary node isn't upgraded. If `no`, stops installation if the primary node hasn't been upgraded. |\n    | `ignore_swap` | `yes`<br />`no` | Continue even if swap is enabled. If `no`, stops installation if swap is enabled. |\n    | `ntp` | `yes`<br />`no` | Configure NTP for time synchronization. |\n    | `rlcheck` | `yes`<br />`no` | Run `rlcheck` after installation to validate the system. |\n    | `update_env_path` | `yes`<br />`no` | Add Redis Software paths to the PATH environment variable. |\n    | `systune` | `yes`<br />`no` | Automatically tune system performance (CPU, sysctl). If `yes`, answers `yes` to all system tuning questions. |\n\n    Example answer file:\n\n    [code example]\n\n1. Run the [installation script](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/install-script) with the `-c` command-line option and add the path to the answer file.\n\n    For example:\n\n    [code example]"}],"examples":[{"id":"installation-questions-ex0","language":"sh","code":"/opt/redislabs/bin/rlcheck","section_id":"installation-questions"},{"id":"answer-yes-to-all-questions-ex0","language":"bash","code":"./install.sh -y","section_id":"answer-yes-to-all-questions"},{"id":"configure-file-to-answer-ex0","language":"sh","code":"ignore_swap=no\n    systune=yes\n    ntp=no\n    firewall=no\n    rlcheck=yes\n    ignore_existing_osuser_osgroup=no\n    update_env_path=no\n    ignore_master_version=no","section_id":"configure-file-to-answer"},{"id":"configure-file-to-answer-ex1","language":"sh","code":"./install.sh -c /home/user/answers","section_id":"configure-file-to-answer"}]}
{"schema_version":2,"id":"operate/rs/installing-upgrading/install/offline-installation","title":"Offline installation","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/install/offline-installation/","summary":"If you install Redis Software on a machine with no internet connection, you need to perform two tasks manually.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:34+01:00","page_type":"content","content_hash":"fb9143d74c35c827b3a6d50f2d2b634e50d6d2d82b585932a41bb8eaf38fa59c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"By default, the installation process requires an internet connection to\nenable installing dependency packages and for [synchronizing the\noperating system clock](https://redis.io/docs/latest/operate/rs/clusters/configure/sync-clocks) against an NTP server.\n\nIf you install Redis Software on a machine without an\ninternet connection, you need to perform two tasks manually."},{"id":"install-required-dependency-packages","title":"Install required dependency packages","role":"setup","text":"When you install Redis Software on a machine that is not connected to the internet, the installation process fails and displays an error message informing you it failed to automatically install dependencies. Review the installation steps in the console to see which missing dependencies the process attempted to install. Install all these dependency packages and then run the installation again."},{"id":"set-up-ntp-time-synchronization","title":"Set up NTP time synchronization","role":"content","text":"At the end of the installation, the process asks if you want to set up NTP time synchronization. If you choose `Yes` while you are not connected to the internet, the action fails and displays the appropriate error message, but the installation completes successfully. Despite the successful completion of the installation, you still have to configure all nodes for [NTP time synchronization](https://redis.io/docs/latest/operate/rs/clusters/configure/sync-clocks)."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/installing-upgrading/install/plan-deployment/configuring-aws-instances","title":"Configure AWS EC2 instances for Redis Software","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/configuring-aws-instances/","summary":"Considerations for installing and running Redis Software on Amazon Elastic Cloud Compute (EC2) instances.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:34+01:00","page_type":"content","content_hash":"762a3fab1991277846d284a576b4dc1c3ca29b1ac9e87699bc06d7e752633dcc","sections":[{"id":"overview","title":"Overview","role":"overview","text":"There are some special considerations for installing\nand running Redis Software on Amazon Elastic Cloud Compute (EC2) instances. \n\nThese include:\n\n- [Storage considerations](#storage)\n- [Instance types](#instance-types)\n- [Security group configuration](#security)"},{"id":"storage","title":"Storage considerations","role":"content","text":"AWS EC2 instances are ephemeral, but your persistent database storage should\nnot be. If you require a persistent storage location for your database,\nthe storage must be located outside of the instance. When you\nset up an instance, make sure it has a properly sized EBS-backed volume\nconnected. When you set up Redis Software on the instance, make sure that [the\npersistence storage](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/persistent-ephemeral-storage) is configured to use this volume.\n\n> [!NOTE]\n> After [installing the Redis Software package](https://redis.io/docs/latest/operate/rs/installing-upgrading) on the instance\n> and **before** running through [the setup process](https://redis.io/docs/latest/operate/rs/clusters/new-cluster-setup),\n> you must give the group `redislabs` permission to the EBS volume by\n> running the following command from the OS command-line interface (CLI):\n> [code example]\n\nAnother feature that may be of importance to you is the use of\nProvisioned IOPS for EBS-backed volumes. Provisioned IOPS guarantee a\ncertain level of disk performance. There are two features in Redis Software where\nthis feature could be critical to use:\n\n1. When using [Redis Flex or Auto Tiering](https://redis.io/docs/latest/operate/rs/databases/flash)\n1. When using AOF on every write and there is a high write load. In\n    this case, the provisioned IOPS should be on the nodes used as\n    replicas in the cluster."},{"id":"instance-types","title":"Instance types","role":"content","text":"Choose an instance type that has (at minimum) enough free memory and\ndisk space to meet the Redis Software [hardware\nrequirements](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/hardware-requirements).\n\nIn addition, some instance types are optimized for EBS-backed volumes\nand some are not. If you are using persistent storage, you should use an\ninstance type that is, especially if disk drain rate matters to your database\nimplementation."},{"id":"security","title":"Security group configuration","role":"security","text":"When configuring the security group:\n\n- Define a custom TCP rule for port 8443 to allow web browser access\n    to the Redis Software Cluster Manager UI from the IP address range you use to\n    access the Cluster Manager UI.\n- If you are using the DNS resolving option with Redis Software, define a DNS UDP\n    rule for port 53 to allow access to the databases' endpoints by\n    using the [DNS resolving mechanism](https://redis.io/docs/latest/operate/rs/networking/cluster-dns).\n- To create a cluster that has multiple nodes all running as instances on AWS,\n    you need to define a security group that has an All TCP rule for all ports, 0 - 65535,\n    and add it to all instances that are part of the cluster.\n    This ensures that all nodes are able to communicate with each other.\n    To limit the number of open ports, you can open only the [ports used by Redis Software](https://redis.io/docs/latest/operate/rs/networking/port-configurations).\n\nAfter successfully launching the instances:\n\n1. Install Redis Software from the [Linux package or AWS AMI](https://redis.io/docs/latest/operate/rs/installing-upgrading).\n2. [Set up the cluster](https://redis.io/docs/latest/operate/rs/clusters/new-cluster-setup)."}],"examples":[{"id":"storage-ex0","language":"sh","code":"> chown redislabs:redislabs /< ebs folder name>\n>","section_id":"storage"}]}
{"schema_version":2,"id":"operate/rs/installing-upgrading/install/plan-deployment/file-locations","title":"File locations","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/file-locations/","summary":"Redis Software file installation locations.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:34+01:00","page_type":"content","content_hash":"6906a164a0889f0bcb2349d99bdb0dfe5429a2d8c531cde6c32175b29427dad8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!WARNING]\n> To ensure that Redis Software functions properly, be careful with the files in the application directories. If you modify or delete the application files, Redis Software might not work as expected."},{"id":"application-directories","title":"Application directories","role":"content","text":"The directories that Redis Software installs into are:\n\n| **Path** | **Description** |\n|------------|-----------------|\n| /opt/redislabs | Main installation directory for all Redis Software binaries |\n| /opt/redislabs/bin | Binaries for all the utilities for command-line access and management, such as [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin) or [`redis-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/redis-cli) |\n| /opt/redislabs/config | System configuration files |\n| /opt/redislabs/lib | System library files |\n| /opt/redislabs/sbin | System binaries for tweaking provisioning |"},{"id":"configuration-and-data-directories","title":"Configuration and data directories","role":"configuration","text":"The default directories that Redis Software uses for data and metadata are:\n\n| **Path** | **Description** |\n|------------|-----------------|\n| /var/opt/redislabs | Default storage location for the cluster data, system logs, backups, and ephemeral, persisted data |\n| /var/opt/redislabs/log | System logs for Redis Software |\n| /var/opt/redislabs/run | Socket files for Redis Software |\n| /etc/opt/redislabs | Default location for cluster manager configuration and certificates |\n| /tmp | The /tmp filesystem is for temporary files and should be sized according to this formula: `<number-of-cluster-nodes> * <size-of-/var/opt/redislabs/log-filesystem>`  |\n\nYou can change these file locations for:\n\n- [Ephemeral and persistence storage](https://redis.io/docs/latest/operate/rs/clusters/new-cluster-setup) during cluster setup\n- [Socket files](https://redis.io/docs/latest/operate/rs/installing-upgrading/configuring/change-location-socket-files) after cluster setup\n\n> [!NOTE]\n> To specify custom paths for ephemeral and persistent storage during cluster setup, you must install Redis Software using [custom installation directories](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/customize-install-directories). If you use the default installation, you cannot specify custom storage paths and will receive a \"path not mounted\" error."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/installing-upgrading/install/plan-deployment/hardware-requirements","title":"Hardware requirements","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/hardware-requirements/","summary":"Redis Software hardware requirements for development and production environments.","tags":["docs","operate","rs","kubernetes"],"last_updated":"2026-09-24T09:16:34+01:00","page_type":"content","content_hash":"a578027697c85d59e042e09febeed18f56046eea3e1469e247cb7a7c0b225bbe","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The hardware requirements for Redis Software are different for development and production environments.\n\n- In a development environment, you can test your application with a live database.\n\n    If you want to test your application under production conditions, use the production environment requirements.\n\n- In a production environment, you must have enough resources to handle the load on the database and recover from failures."},{"id":"architecture","title":"Architecture","role":"content","text":"Redis Software supports AMD64 (x86_64) and ARM64 architectures as shown in the following table:\n\n| Operating system | AMD64 (x86_64) support | ARM64 support |\n|------------------|------------------------|---------------|\n| RHEL 9           | <span title=\"Supported\">:white_check_mark:</span> Redis Software 7.4.2 and later | <span title=\"Supported\">:white_check_mark:</span> Redis Software 8.0.10 and later |\n| RHEL 8           | <span title=\"Supported\">:white_check_mark:</span> Redis Software 6.2.8 and later | <span title=\"Not supported\">:x:</span> Not supported |\n| Ubuntu 22        | <span title=\"Supported\">:white_check_mark:</span> Redis Software 7.8.4-66 and later | <span title=\"Supported\">:white_check_mark:</span> Redis Software 8.0.10 and later |\n| Ubuntu 20        | <span title=\"Supported\">:white_check_mark:</span> Redis Software 6.4.2-43 and later | <span title=\"Not supported\">:x:</span> Not supported |\n| Amazon Linux 2   | <span title=\"Supported\">:white_check_mark:</span> Redis Software 6.4.2-69 and later | <span title=\"Not supported\">:x:</span> Not supported |"},{"id":"developmentenvironment","title":"Development environment","role":"content","text":"You can build your development environment with non-production hardware, such as a laptop, desktop, or small VM or instance,\nand with these hardware requirements:\n\n| Item | Description | Minimum requirements | Recommended |\n|------------|-----------------|------------|-----------------|\n| Nodes per cluster | You can install on one node but many features require at least two nodes. | 1 node | >= 2 nodes |\n| RAM per node | The amount of RAM for each node. | 4GB | >= 10GB |\n| Storage per node | The amount of storage space for each node. | 10GB | >= 20GB |"},{"id":"productionenvironment","title":"Production environment","role":"content","text":"We recommend these hardware requirements for production systems or for development systems that are designed to demonstrate production use cases:\n\n| Item | Description | Minimum requirements | Recommended |\n|------------|-----------------|------------|-----------------|\n| Nodes<sup>[1](#table-note-1)</sup> per cluster | At least three nodes are required to support a reliable, highly available deployment that handles process failure, node failure, and network split events in a consistent manner. | 3 nodes | >= 3 nodes (Must be an odd number of nodes) |\n| Cores<sup>[2](#table-note-2)</sup> per node | Redis Software is based on a multi-tenant architecture and can run multiple Redis processes (or shards) on the same core without significant performance degradation. | 2 cores | >=8 cores |\n| RAM<sup>[3](#table-note-3)</sup> per node | Defining your RAM size must be part of the capacity planning for your Redis usage. | 8GB | >=32GB |\n| Ephemeral storage | Used for storing [replication files (RDB format) and cluster log files](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/persistent-ephemeral-storage). | RAM x 2 | >= RAM x 4 |\n| Persistent storage<sup>[4](#table-note-4)</sup> | Used for storing [snapshot (RDB format) and AOF files](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/persistent-ephemeral-storage) over a persistent storage media, such as AWS Elastic Block Storage (EBS) or Azure Data Disk. | RAM x 3 | In-memory >= RAM x 4 (except for [extreme 'write' scenarios](https://redis.io/docs/latest/operate/rs/clusters/optimize/disk-sizing-heavy-write-scenarios))<br /><br /> [Redis Flex and Auto Tiering](https://redis.io/docs/latest/operate/rs/databases/flash/) >= (RAM + Flash) x 4. |\n| Network<sup>[5](#table-note-5)</sup> | We recommend using multiple NICs per node where each NIC is >1Gbps, but Redis Software can also run over a single 1Gbps interface network used for processing application requests, inter-cluster communication, and storage access. | 1G | >=10G |\n| Local disk for [Auto Tiering](https://redis.io/docs/latest/operate/rs/databases/flash/) | Used to extend an Auto Tiering database's DRAM capacity with solid state drives (SSDs). Flash memory must be locally attached. [Read more](https://redis.io/docs/latest/operate/rs/databases/flash/) | (RAM+Flash) x 1.6 | (RAM+Flash) x 2.5 |\n| Local disk for [Flex](https://redis.io/docs/latest/operate/rs/flex/)<sup>[6](#table-note-6)</sup> | Used to extend a Flex database's DRAM capacity with solid state drives (SSDs). Flash memory must be locally attached. [Read more](https://redis.io/docs/latest/operate/rs/flex/) | (Total memory limit of all Flex databases on the node) x 3 | (Total memory limit of all Flex databases on the node) x 3 |\n\n\nAdditional considerations:\n\n1. <a name=\"table-note-1\"></a>Nodes per cluster:\n\n    - Clusters with more than 35 nodes are not supported. Please contact the Redis support team for assistance if your sizing calls for deploying a larger number of nodes.\n\n    - Quorum nodes also must comply with the above minimal hardware requirements.\n    \n    - To ensure synchronization and consistency, Active-Active deployments with three-node clusters should not use quorum nodes. Because quorum nodes do not store data shards, they cannot support replication. In case of a node failure, replica shards aren't available for Active-Active synchronization.\n\n2. <a name=\"table-note-2\"></a>Cores:\n\n    - When the CPU load reaches a certain level, Redis Software sends an alert to the operator.  \n\n    - If your application is designed to put a lot of load on your Redis database, make sure that you have at least one available core for each shard of your database.\n\n    - If some of the cluster nodes are utilizing more than 80% of the CPU, consider migrating busy resources to less busy nodes.\n\n    - If all the cluster nodes are utilizing over 80% of the CPU, highly consider scaling out the cluster by [adding a node](https://redis.io/docs/latest/operate/rs/clusters/add-node).\n\n3. <a name=\"table-note-3\"></a> RAM:\n\n    - Redis uses a relatively large number of buffers, which enable replica communication, client communication, pub/sub commands, and more.  As a result, you should ensure that 30% of the RAM is available on each node at any given time.\n\n    - If one or more cluster nodes utilizes more than 65% of the RAM, consider migrating resources to less active nodes.\n\n    - If all cluster nodes are utilizing more than 70% of available RAM, highly consider [adding a node](https://redis.io/docs/latest/operate/rs/clusters/add-node).\n\n    - Do not run any other memory-intensive processes on the Redis Software node.\n\n4. <a name=\"table-note-4\"></a>Persistent storage:\n\n    - If no databases on the cluster have [persistence](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/persistent-ephemeral-storage) enabled, minimum persistent storage is RAM x 1.1 and the recommended persistent storage is RAM x 2. Persistent storage is essential because Redis Software also uses it to maintain the cluster and database health, configurations, recovery procedures, and more.\n  \n5. <a name=\"table-note-5\"></a>Network:\n\n    - Only static IP addresses are supported to ensure nodes remain part of the cluster after a reboot.\n\n6. <a name=\"table-note-6\"></a>Local disk for Flex:\n\n    - Because you can increase a database's memory limit after creation, size the local disk for the expected peak memory limit."},{"id":"sizing-considerations","title":"Sizing considerations","role":"content","text":""},{"id":"general-sizing","title":"General database sizing","role":"content","text":"Factors to consider when sizing your database.\n\n- **Dataset size** – Your limit should be greater than your dataset size to leave room for overhead.\n- **Database throughput** – High throughput needs more shards, leading to a higher memory limit.\n- [**Modules**](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise) – Using modules with your database consumes more memory.\n- [**Database clustering**](https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering) – Allows you to spread your data into shards across multiple nodes.\n- [**Database replication**](https://redis.io/docs/latest/operate/rs/databases/durability-ha/replication) – Enabling replication doubles memory consumption."},{"id":"active-active-sizing","title":"Active-Active database sizing","role":"content","text":"Additional factors for sizing Active-Active databases:\n\n- [**Active-Active replication**](https://redis.io/docs/latest/operate/rs/databases/active-active) – Requires double the memory of regular replication, which can be up to two times (2x) the original data size per instance.\n- [**Database replication backlog**](https://redis.io/docs/latest/operate/rs/databases/durability-ha/replication#database-replication-backlog) – For synchronization between shards. By default, this is set to 1% of the database size.\n- [**Active-Active replication backlog**](https://redis.io/docs/latest/operate/rs/databases/active-active/manage#replication-backlog) – For synchronization between clusters. By default, this is set to 1% of the database size.\n\n> [!NOTE]\n> Active-Active databases have a lower threshold for activating the eviction policy, because it requires propagation to all participating clusters. The eviction policy starts to evict keys when one of the Active-Active instances reaches 80% of its memory limit."},{"id":"redis-on-flash-sizing","title":"Sizing databases with Auto Tiering enabled","role":"content","text":"Additional factors for sizing  databases with Auto Tiering enabled:\n\n- [**Database persistence**](https://redis.io/docs/latest/operate/rs/databases/configure/database-persistence#redis-on-flash-data-persistence) – Auto Tiering uses dual database persistence where both the primary and replica shards persist to disk. This may add some processor and network overhead, especially in cloud configurations with network-attached storage."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/installing-upgrading/install/plan-deployment","title":"Plan Redis Software deployment","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/","summary":"Plan a deployment of Redis Software.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:34+01:00","children":[{"id":"operate/rs/installing-upgrading/install/plan-deployment/hardware-requirements","summary":"Redis Software hardware requirements for development and production environments.","title":"Hardware requirements","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/hardware-requirements/"},{"id":"operate/rs/installing-upgrading/install/plan-deployment/supported-platforms","summary":"","title":"Supported platforms","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/supported-platforms/"},{"id":"operate/rs/installing-upgrading/install/plan-deployment/persistent-ephemeral-storage","summary":"Configure paths for persistent storage and ephemeral storage.","title":"Persistent and ephemeral node storage","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/persistent-ephemeral-storage/"},{"id":"operate/rs/installing-upgrading/install/plan-deployment/file-locations","summary":"Redis Software file installation locations.","title":"File locations","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/file-locations/"},{"id":"operate/rs/installing-upgrading/install/plan-deployment/configuring-aws-instances","summary":"Considerations for installing and running Redis Software on Amazon Elastic Cloud Compute (EC2) instances.","title":"Configure AWS EC2 instances for Redis Software","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/configuring-aws-instances/"}],"page_type":"content","content_hash":"ba22553a79defb1fd4929e01531a5681eed4553c40663ec4179e7b30ae5208da","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Before installing Redis Software, you need to:\n\n- Set up your hardware. See [Hardware requirements](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/hardware-requirements) and [Persistent and ephemeral node storage \n](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/persistent-ephemeral-storage) for more information.\n\n- Choose your [deployment platform](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/supported-platforms).\n\n    Redis Software supports a variety of platforms, including:\n\n    - Multiple Linux distributions (Ubuntu, Red Hat Enterprise Linux (RHEL), IBM CentOS, Oracle Linux)\n    - [Amazon AWS AMI](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/configuring-aws-instances)\n    - [Docker container](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) (for development and testing only)\n    - [Kubernetes](https://redis.io/docs/latest/operate/kubernetes)\n\n    For more details, see [Supported platforms](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/supported-platforms).\n\n- Open appropriate [network ports](https://redis.io/docs/latest/operate/rs/networking/port-configurations) in the firewall to allow connections to the nodes.\n\n- Configure [cluster DNS](https://redis.io/docs/latest/operate/rs/networking/cluster-dns) so that cluster nodes can reach each other by DNS names.\n- By default, the installation process requires an internet connection to install dependencies and synchronize the operating system clock. To learn more, see [Offline installation](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/offline-installation).\n\n- [Configure different mount points for data and log directories](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/customize-install-directories#config-diff-data-log-dirs)."},{"id":"next-steps","title":"Next steps","role":"content","text":"After you finish planning your deployment, you can:\n\n- [Download an installation package](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/prepare-install/download-install-package).\n\n- [Prepare to install](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/prepare-install) Redis Software.\n\n- [View installation questions](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/manage-installation-questions) and prepare answers before installation."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/installing-upgrading/install/plan-deployment/persistent-ephemeral-storage","title":"Persistent and ephemeral node storage","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/persistent-ephemeral-storage/","summary":"Configure paths for persistent storage and ephemeral storage.","tags":["docs","operate","rs","kubernetes"],"last_updated":"2026-09-24T09:16:34+01:00","page_type":"content","content_hash":"b7028af5a89a4ea19a228132172018a2e186510c17f6ed6d10e3070e80226bd3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"For each node in the cluster, you can configure paths for both persistent\nstorage and ephemeral storage. To do so, the volume must have full permissions for user and group `redislabs` or users:group `redislabs:redislabs`. See the [Customize system user and group](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/customize-user-and-group) page for instructions.\n\n> [!NOTE]\n> The persistent storage and ephemeral storage discussed in this document are not related\n> to Redis persistence or AWS ephemeral drives."},{"id":"persistent-storage","title":"Persistent storage","role":"content","text":"Persistent storage is mandatory. The cluster uses persistent storage to store\ninformation that needs to persist if a shard or a node fails,\nsuch as server logs, configurations, and files.\n\nTo set the frequency of syncs, you can configure [persistence](https://redis.io/docs/latest/operate/rs/databases/configure/database-persistence)\noptions for a database.\n    \nThe persistent volume must be a storage area network (SAN)\nusing an EXT4 or XFS file system and be connected as an external storage volume.\nNFS, NFS-like, and multi-read-write/shared storage options are not supported. These types of storage are often slow and can cause locking behaviors that are incompatible with the requirements of database storage.\n\nWhen using append-only file (AOF) persistence, use flash-based storage\nfor the persistent volume."},{"id":"ephemeral-storage","title":"Ephemeral storage","role":"content","text":"Ephemeral storage is optional. If configured, temporary information that does not need to be persisted is stored by the cluster in the ephemeral storage.\nThis improves performance and helps reduce the load on the persistent storage.\n\nEphemeral storage must be a locally attached volume on each node."},{"id":"disk-size-requirements","title":"Disk size requirements","role":"content","text":"For disk size requirements, see:\n\n- [Hardware\n    requirements](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/hardware-requirements)\n    for general guidelines regarding the ideal disk size for each type of\n    storage.\n- [Disk size requirements for extreme write\n    scenarios](https://redis.io/docs/latest/operate/rs/clusters/optimize/disk-sizing-heavy-write-scenarios)\n    for special considerations when dealing with a high rate of write\n    commands."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/installing-upgrading/install/plan-deployment/supported-platforms","title":"Supported platforms","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/supported-platforms/","summary":"","tags":["docs","operate","rs"],"last_updated":"2025-01-16T07:19:11-08:00","page_type":"content","content_hash":"ce8dd53d28e476a598bd22857f4d2902943f65edad1eb27123edb9f6cdbb1f78","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software is supported on several operating systems, cloud environments, and virtual environments."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n<span title=\"Dash icon\">–</span> Not supported – The platform is not supported for this version of Redis Software.\n\n| Redis Software<br />major versions | 8.2 | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Jul 2026 | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Jul 2028 | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2023<sup>[6](#table-note-6)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – | – | – |\n| Amazon Linux 2 | – | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance.\n\n6. <a name=\"table-note-6\"></a>Amazon Linux 2023 support was added in Redis Software version 8.0.20.\n\nThe following table shows which Redis Software version first tested and added support for each RHEL version:\n\n| RHEL version | Redis Software version |\n|--------------|--------------------------|\n| 8.4 | 6.2.8 |\n| 8.5 | 6.2.10 |\n| 8.6 | 6.2.10 |\n| 8.7 | 6.4.2 |\n| 8.8 | 6.4.2 |\n| 8.9 | 7.2.4 |\n| 9.3 | 7.4.2 |\n| 9.5 | 7.8.2 |\n| 9.6 | 7.22.0 |"},{"id":"operating-system-compatibility-policy","title":"Operating system compatibility policy","role":"content","text":"Redis maintains a list of [supported operating systems](#supported-platforms) for each major version of Redis Software and the specific OS versions tested with Redis Software releases. Because the list is updated as new OS versions are introduced and old ones become obsolete, we encourage you to check the list and plan upgrades accordingly. We also suggest you keep Redis Software and corresponding supported OS versions up to date.\n\nWe thoroughly test the most recent minor version of each supported major OS to ensure the best compatibility and performance with every Redis Software release. This process helps detect and address potential compatibility issues early on.\n\nDue to the vast array of minor updates and variations across operating systems, we cannot test compatibility with every minor OS version and each Redis Software version. However, because OS vendors each have an Application Binary Interface (ABI) they support and avoid breaking, except to address severe security issues, newer minor OS versions are generally expected to work correctly. We will add a note to this document if specific OS minor versions have significant ABI or dependency changes. An earlier OS minor version also might work, although it is not guaranteed.\n\nDespite our rigorous testing, we recommend users test their Redis applications with any new OS update before deploying it in a production environment. This additional testing layer can help identify any unique issues in your setup."},{"id":"red-hat-enterprise-linux-rhel","title":"Red Hat Enterprise Linux (RHEL)","role":"content","text":"Red Hat has a well-defined lifecycle for support. For details, see [Red Hat Enterprise Linux Life Cycle](https://access.redhat.com/support/policy/updates/errata#RHEL8_and_9_Life_Cycle).\n\nRedis supports and tests RHEL 8 and 9 minor releases and extended update support timeframes. However, none of the longer Red Hat support cycles, such as SAP and EEUS, are supported.\n\nWe only support what the vendor supports in accordance with their policies. When Red Hat no longer supports a particular release, it is no longer supported by Redis as well. If future Redis Software releases will not support a major RHEL version, the release notes and the [supported platforms](#supported-platforms) table will include deprecation warnings."},{"id":"rhel-clones-and-equivalent-enterprise-os","title":"RHEL clones and equivalent Enterprise OS","role":"content","text":"The [supported platforms](#supported-platforms) table lists the versions of Red Hat Enterprise Linux (RHEL) that Redis supports. This support extends to the ABI and package compatibility with RHEL of the same version.\n\nThe RHEL-compatible distributions CentOS, CentOS Stream, Alma Linux, Rocky Linux, and Oracle Linux running the Red Hat Compatible Kernel (RHCK) are supported if they provide full RHEL compatibility. If Redis identifies or questions some incompatibility from a clone, you might be asked to test on another clone or RHEL directly."},{"id":"operating-system-limitations","title":"Operating system limitations","role":"content","text":""},{"id":"tls-10-and-tls-11","title":"TLS 1.0 and TLS 1.1","role":"content","text":"Redis Software version 6.2.8 removed support for TLS 1.0 and TLS 1.1 on Red Hat Enterprise Linux 8 (RHEL 8) because that operating system [does not enable support](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/security_hardening/using-the-system-wide-cryptographic-policies_security-hardening) for these versions by default."},{"id":"rhel-8-crypto-policy-and-certificate-key-size","title":"RHEL 8 crypto policy and certificate key size","role":"content","text":"In RHEL 8, if the crypto policy is set to `FUTURE`, the system will not accept certificates with private key sizes smaller than 3072 bits. This affects use of custom certificates with smaller keys (such as 2048-bit keys).\n\nTo use certificates with smaller key sizes, you need to change the crypto policy from `FUTURE` to `DEFAULT`. For more information about crypto policies, see the [Red Hat documentation on system-wide cryptographic policies](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/security_hardening/using-the-system-wide-cryptographic-policies_security-hardening)."},{"id":"ubuntu-20-rejects-sha1-certificates","title":"Ubuntu 20 rejects SHA1 certificates","role":"content","text":"With Ubuntu 20.04, you cannot use the SHA1 hash algorithm because [OpenSSL's security level is set to 2 by default](https://manpages.ubuntu.com/manpages/focal/man3/SSL_CTX_set_security_level.3ssl.html#notes). As a result, the operating system rejects SHA1 certificates, even if you enable the `mtls_allow_weak_hashing` option.\n\nTo avoid issues with SHA1 certificates, replace them with new certificates that use SHA-256. Note that certificates provided by Redis Software use SHA-256."},{"id":"upgrade-rhel-when-using-modules","title":"Upgrade RHEL when using modules","role":"content","text":"RHEL 7 clusters cannot be directly upgraded to RHEL 8 when hosting databases using modules, due to binary differences in modules between the two operating systems. Instead, you need to create a new cluster on RHEL 8 and then migrate existing data from your RHEL 7 cluster. This does not apply to clusters that do not use modules.\n\nThis limitation is fixed for clusters using Redis Software version 7.2.4 and later."},{"id":"modules-cannot-load-in-oracle-linux-7--8","title":"Modules cannot load in Oracle Linux 7 & 8","role":"content","text":"Databases hosted on Oracle Linux 7 & 8 cannot load modules.\n\nAs a temporary workaround, you can change the node's `os_name` in the Cluster Configuration Store (CCS):\n\n[code example]\n\nThis limitation was fixed in [Redis Software version 7.2.4-64](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-64)."},{"id":"openssl-compatibility-issue-for-742-modules-on-amazon-linux-2","title":"OpenSSL compatibility issue for 7.4.2 modules on Amazon Linux 2","role":"content","text":"Due to an OpenSSL 1.1 compatibility issue between modules and clusters, Redis Software version 7.4.2-54 is not fully supported on Amazon Linux 2 clusters with databases that use the following modules: RedisGears, RediSearch, or RedisTimeSeries.\n\nThis issue will be fixed in a future maintenance release."},{"id":"redisgraph-prevents-upgrade-to-rhel-9","title":"RedisGraph prevents upgrade to RHEL 9","role":"content","text":"You cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9."},{"id":"virtualization-platforms","title":"Virtualization platforms","role":"content","text":"Redis Software is compatible with VMware and other similar virtualization platforms. Make sure to do the following:\n\n- Configure your memory, CPU, network, and storage settings to allow for optimal Redis Software performance.\n- Pin each Redis Software shard to a specific ESX or ESXi host by setting the appropriate affinity rules.\n- If you must manually migrate a virtual machine to another host, follow the best practices for [shard maintenance](https://redis.io/docs/latest/operate/rs/clusters/maintenance-mode) and contact support if you have questions.\n- Turn off VMware VMotion because Redis Software is not compatible with VMotion.\n- Don't use snapshots because Redis Software cluster manages states dynamically, so a snapshot might not have the correct node and cluster states."}],"examples":[{"id":"modules-cannot-load-in-oracle-linux-7--8-ex0","language":"sh","code":"ccs-cli hset node:<ID> os_name rhel","section_id":"modules-cannot-load-in-oracle-linux-7--8"}]}
{"schema_version":2,"id":"operate/rs/installing-upgrading/install/prepare-install/download-install-package","title":"Download a Redis Software installation package","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/install/prepare-install/download-install-package/","summary":"Download a Redis Software installation package.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:34+01:00","page_type":"content","content_hash":"65ea1ae98fd813bf9e5c7d92d59e773b6505021fae99ab851534ff62f92c8399","sections":[{"id":"content","title":"Content","role":"content","text":"To download the installation package for any of the supported platforms:\n\n1. Go to the [Redis download page](https://cloud.redis.io/#/rlec-downloads).\n1. Sign in with your Redis credentials or create a new account.\n1. In the **Downloads** section for Redis Software, select the installation package for your platform then select **Go**.\n\n> [!NOTE]\n> Before you install the Linux package or AWS AMI on an AWS EC2 instance,\n> review the [configuration requirements for AWS EC2 instances](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/configuring-aws-instances)."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/installing-upgrading/install/prepare-install","title":"Prepare to install Redis Software","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/install/prepare-install/","summary":"Prepare to install Redis Software.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:34+01:00","children":[{"id":"operate/rs/installing-upgrading/install/prepare-install/download-install-package","summary":"Download a Redis Software installation package.","title":"Download a Redis Software installation package","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/install/prepare-install/download-install-package/"},{"id":"operate/rs/installing-upgrading/install/prepare-install/port-availability","summary":"Make sure required ports are available.","title":"Ensure port availability","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/install/prepare-install/port-availability/"}],"page_type":"content","content_hash":"9f84dbea17b954ecda701c26c6360917129fb3cef43e894e3d5b2976d9b47c55","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Before you install Redis Software:\n\n- [Download an installation package](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/prepare-install/download-install-package).\n\n- [View installation questions](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/manage-installation-questions) and optionally prepare answers before installation.\n\n- Review the [security considerations](https://redis.io/docs/latest/operate/rs/security) for your deployment.\n\n- Check that you have root-level access to each node, either directly or with `sudo`.\n\n- Check that all [required ports are available](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/prepare-install/port-availability).\n\n- [Turn off Linux swap](https://redis.io/docs/latest/operate/rs/installing-upgrading/configuring/linux-swap) on all cluster nodes.\n\n- If you require the `redislabs` UID (user ID) and GID (group ID) numbers to be the same on all the nodes, create the `redislabs` user and group with the required numbers on each node.\n\n- If you want to use Auto Tiering for your databases, see [Auto Tiering installation](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/install-on-linux#auto-tiering-installation)."},{"id":"next-steps","title":"Next steps","role":"content","text":"- View [installation script options](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/install-script) before starting the installation.\n\n- [Install Redis Software](https://redis.io/docs/latest/operate/rs/installing-upgrading/install)."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/installing-upgrading/install/prepare-install/port-availability","title":"Ensure port availability","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/install/prepare-install/port-availability/","summary":"Make sure required ports are available.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:34+01:00","page_type":"content","content_hash":"4e0f92f9bd8d519bb73abb2a5c1c00b49fcedf4c0b4257c5032a605d355b0fec","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Before [installing Redis Software](https://redis.io/docs/latest/operate/rs/installing-upgrading/install), make sure all required ports are available.\n\nIf ports that Redis assigns to the database are being used by the operating system or other processes, the installation fails.\n\nFollow the relevant sections to configure required ports.\n\nFor recommended and optional port configuration, see [Network port configurations](https://redis.io/docs/latest/operate/rs/networking/port-configurations)."},{"id":"update-sysctlconf-to-avoid-port-collisions","title":"Update `sysctl.conf` to avoid port collisions","role":"content","text":"To avoid port collision, update `/etc/sysctl.conf` to include:\n\n[code example]"},{"id":"os-conflicts-with-port-53","title":"OS conflicts with port 53","role":"content","text":"If port 53 is in use, the installation fails. This issue can occur in default installations of certain operating systems in which `systemd-resolved` (DNS server) or `dnsmasq` is running.\n\nTo prevent this issue, change the system configuration to make this port available before installation.\n\nTo prevent `systemd-resolved` from using port 53:\n\n1. Edit `/etc/systemd/resolved.conf`: \n\n    [code example]\n\n1. Add `DNSStubListener=no` as the last line in the file and save the file.\n\n1. Rename the current `/etc/resolv.conf` file:\n\n    [code example]\n\n1. Create a symbolic link for `/etc/resolv.conf`:\n\n    [code example]\n\n    > [!NOTE]\n    > You might encounter a temporary name resolution error (`sudo: unable to resolve host {hostname}: Temporary failure in name resolution`), which should be fixed when you restart `systemd-resolved` in the next step.\n\n2. Restart the DNS service:\n\n    [code example]\n\nTo prevent `dnsmasq` from using port 53:\n\n1. Stop the `dnsmasq` service if it's running:\n\n    [code example]\n\n1. Prevent `dnsmasq` from starting automatically at system boot:\n\n    [code example]\n\n1. Mask `dnsmasq` to prevent it from being started manually or by other services:\n\n    [code example]\n\n1. Verify `dnsmasq` is no longer active and won't start at system boot:\n\n    [code example]"}],"examples":[{"id":"update-sysctlconf-to-avoid-port-collisions-ex0","language":" sh","code":"net.ipv4.ip_local_port_range = 30000 65535","section_id":"update-sysctlconf-to-avoid-port-collisions"},{"id":"os-conflicts-with-port-53-ex0","language":"sh","code":"sudo vi /etc/systemd/resolved.conf","section_id":"os-conflicts-with-port-53"},{"id":"os-conflicts-with-port-53-ex1","language":"sh","code":"sudo mv /etc/resolv.conf /etc/resolv.conf.orig","section_id":"os-conflicts-with-port-53"},{"id":"os-conflicts-with-port-53-ex2","language":"sh","code":"sudo ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf","section_id":"os-conflicts-with-port-53"},{"id":"os-conflicts-with-port-53-ex3","language":"sh","code":"sudo service systemd-resolved restart","section_id":"os-conflicts-with-port-53"},{"id":"os-conflicts-with-port-53-ex4","language":"sh","code":"sudo systemctl stop dnsmasq","section_id":"os-conflicts-with-port-53"},{"id":"os-conflicts-with-port-53-ex5","language":"sh","code":"sudo systemctl disable dnsmasq","section_id":"os-conflicts-with-port-53"},{"id":"os-conflicts-with-port-53-ex6","language":"sh","code":"sudo systemctl mask dnsmasq","section_id":"os-conflicts-with-port-53"},{"id":"os-conflicts-with-port-53-ex7","language":"sh","code":"sudo systemctl status dnsmasq","section_id":"os-conflicts-with-port-53"}]}
{"schema_version":2,"id":"operate/rs/installing-upgrading/modules/add-module-to-cluster","title":"Install a module on a cluster","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/add-module-to-cluster/","summary":"","aliases":["/operate/oss_and_stack/stack-with-enterprise/install/add-module-to-cluster/"],"tags":["docs","operate","rs"],"last_updated":"2026-10-06T08:53:14-07:00","page_type":"content","content_hash":"1a73fdf969f097953eb381abb11793dd75063dc4bd1f50948a94d454efb524b0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Redis Software](https://redis.io/docs/latest/operate/rs) comes packaged with several modules that provide additional Redis capabilities such as [Redis Search](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search), [JSON](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json), [time series](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/timeseries), and [probabilistic data structures](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/bloom). As of version 8.0, Redis Software includes multiple feature sets, compatible with different Redis database versions. You can view the installed modules, their versions, and their minimum compatible Redis database versions from **Cluster > Modules** in the Cluster Manager UI.\n\nTo use other modules or upgrade an existing module to a more recent version, you need to install the new module package on your cluster.\n\n> [!WARNING]\n> Some module versions are not supported or recommended for use with Redis Software."},{"id":"module-package-requirements","title":"Module package requirements","role":"content","text":"The module must be packaged as a `.zip` file containing:\n\n- **module.json**: A metadata file with module information including:\n  - `module_name`: The actual module name\n  - `version`: Numeric version\n  - `semantic_version`: Semantic version string (for example, \"1.0.0\")\n  - `min_redis_version`: Minimum compatible Redis version\n  - `commands`: List of commands the module provides\n  - `capabilities`: List of module capabilities\n\n- **Module binary**: The compiled `.so` file for the target platform"},{"id":"get-packaged-modules","title":"Get packaged modules","role":"content","text":"To install or upgrade a module on a [Redis Software](https://redis.io/docs/latest/operate/rs) cluster, you need a module package.\n\n- For versions of official Redis modules that are not available from the [Redis download center](https://redis.io/downloads/), [contact support](https://redis.io/support/).\n\n- For custom-packaged modules, download a [custom-packaged module](https://redislabs.com/community/redis-modules-hub/) from the developer.\n\n- User-defined modules are downloaded automatically if you [add them during bootstrapping](#bootstrap-user-defined-module)."},{"id":"bootstrap-user-defined-module","title":"Add user-defined modules during bootstrapping (Redis Software v8.0.6 and later)","role":"content","text":"As of Redis Software version 8.0.6, you can include `user_defined_modules` in REST API requests to [initiate boostrap operations](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bootstrap#post-bootstrap) such as `create_cluster`, `join_cluster`, or `recover_cluster`. Each node in the cluster independently downloads and installs the specified modules during its bootstrap process.\n\n`user_defined_modules` has the following JSON schema:\n\n[code example]"},{"id":"best-practices","title":"Best practices","role":"content","text":"- Use `https` instead of `http` for secure module downloads.\n\n- Include version numbers in module URLs.\n\n- Use the same `user_defined_modules` configuration for all nodes in a cluster.\n\n- If using authenticated downloads, ensure credentials are properly secured.\n\n- Ensure modules are compatible with the Redis database version running on your cluster.\n\n- Verify modules work correctly before deploying to production environments."},{"id":"example-requests","title":"Example requests","role":"example","text":"**Create cluster:**\n\nThe following example creates a cluster with multiple modules:\n\n\n[code example]\n\n**Join cluster:**\n\nThe following example joins a node to a cluster with multiple modules:\n\n[code example]\n\n**Recover cluster:**\n\nThe following example recovers a cluster with multiple modules:\n\n[code example]"},{"id":"troubleshooting","title":"Troubleshooting","role":"errors","text":"#### Error handling\n\nDownload failures do not fail the bootstrap process. If a module fails to download or install, a warning is logged and the bootstrap process continues with the remaining modules.\n\nWarnings are recorded in the bootstrap status with:\n- `warning_type`: `\"module_download_failed\"`\n- `message`: Error description\n- `details`: `{\"module_name\": \"<name>\"}`\n\n#### Module download failed\n\nCheck the bootstrap logs for detailed error messages:\n\n[code example]\n\nCommon causes:\n- Invalid URL\n- Network connectivity issues\n- Authentication failures\n- Module package format issues\n\n#### Module compatibility errors\n\nAfter processing user-defined modules, the system validates that all custom modules are compatible with existing databases in the cluster. This validation:\n\n1. Checks which custom modules are used by existing databases.\n\n1. Verifies that compatible module versions are available on the node.\n\n1. Fails the bootstrap process if incompatible modules are detected.\n\nIf the bootstrap process fails with an `incompatible_modules` error:\n\n1. Verify the module version is compatible with existing databases.\n\n1. Ensure the module binary exists and is accessible.\n\n#### Missing module.json\n\nIf you see `\"module.json missing\"` errors:\n\n1. Verify the zip file contains a valid `module.json` at the root level.\n\n1. Verify the JSON is properly formatted."},{"id":"add-user-defined-module-to-cluster","title":"Add a user-defined module to a cluster (Redis Software v8.0.x and later)","role":"content","text":"To add a custom module to a cluster running Redis Software version 8.0.x or later, use the following REST API requests:\n\n1. [Upload the custom module configuration](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/modules/user-defined#post-user-defined-module). Replace the values in the following example with your own.\n\n    [code example]\n\n1. For each node in the cluster, [upload the custom module artifact](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/modules/user-defined#post-local-user-defined-artifacts):\n\n    [code example]\n\n    The *module* parameter specifies the full path of the module artifact and must be submitted as form-data. In addition, the module artifact must be available and accessible to the server processing the request."},{"id":"add-a-module-to-a-cluster","title":"Add a module to a cluster (Redis Software v7.22.x and earlier)","role":"content","text":"Use one of the following methods to add a module to a cluster running Redis Software version 7.22.x or earlier:\n\n**Cluster Manager UI:**\n\nTo add a module to the cluster using the Cluster Manager UI:\n\n1. Go to **Cluster > Modules**.\n\n1. Select **Upload module**.\n\n1. Use the file browser to add the packaged module.\n\n**REST API:**\n\nTo add a module to the cluster using the REST API:\n\n1. Copy the module package to a node in the cluster.\n\n1. Add the module to the cluster with a [`POST` request to the `/v2/modules`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/modules#post-module-v2) endpoint:\n\n    [code example]\n\n    Here, the *module* parameter specifies the full path of the module package and must be submitted as form-data. In addition, the package must be available and accessible to the server processing the request.\n\n1. If the module installation succeeds, the `POST` request returns a [JSON object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/module) that represents the new module. If it fails, it may return a JSON object with an `error_code` and `description` with more details.\n\n\n\nFor RedisGears, follow these [installation instructions](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/gears-v1/installing-redisgears) instead.\n\n> [!WARNING]\n> We recommend consulting [Redis support](https://redis.io/support/) before you upgrade a module on the cluster, especially if the cluster is used in production."},{"id":"next-steps","title":"Next steps","role":"content","text":"- Create a database and [enable the new module](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/add-module-to-database).\n- [Upgrade a module](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/upgrade-module) to the new version."}],"examples":[{"id":"bootstrap-user-defined-module-ex0","language":"json","code":"{\n  \"user_defined_modules\": [\n    {\n      \"name\": \"string (required)\",\n      \"location\": {\n        \"location_type\": \"http | https (required)\",\n        \"url\": \"string (required)\",\n        \"credentials\": {\n          \"username\": \"string (optional)\",\n          \"password\": \"string (optional)\"\n        }\n      }\n    }\n  ]\n}","section_id":"bootstrap-user-defined-module"},{"id":"example-requests-ex0","language":"sh","code":"POST /v1/bootstrap/create_cluster\n{\n  \"action\": \"create_cluster\",\n  \"credentials\": {\n    \"username\": \"admin@example.com\",\n    \"password\": \"your-secure-password\"\n  },\n  \"cluster\": {\n    \"name\": \"my-cluster.example.com\"\n  },\n  \"user_defined_modules\": [\n    {\n      \"name\": \"ModuleA\",\n      \"location\": {\n        \"location_type\": \"https\",\n        \"url\": \"https://private-repo.example.com/enterprise-module-2.0.0.zip\",\n        \"credentials\": {\n          \"username\": \"download-user\",\n          \"password\": \"download-password\"\n        }\n      }\n    },\n    {\n      \"name\": \"ModuleB\",\n      \"location\": {\n        \"location_type\": \"https\",\n        \"url\": \"https://modules.example.com/module-b-2.5.0.zip\"\n      }\n    },\n    {\n      \"name\": \"ModuleC\",\n      \"location\": {\n        \"location_type\": \"http\",\n        \"url\": \"http://internal-server.local/module-c-1.2.0.zip\"\n      }\n    }\n  ]\n}","section_id":"example-requests"},{"id":"example-requests-ex1","language":"sh","code":"POST /v1/bootstrap/join_cluster\n{\n  \"action\": \"join_cluster\",\n  \"credentials\": {\n    \"username\": \"admin@example.com\",\n    \"password\": \"your-secure-password\"\n  },\n  \"cluster\": {\n    \"name\": \"my-cluster.example.com\",\n    \"nodes\": [\"192.168.1.10\", \"192.168.1.11\"]\n  },\n  \"user_defined_modules\": [\n    {\n      \"name\": \"ModuleA\",\n      \"location\": {\n        \"location_type\": \"https\",\n        \"url\": \"https://private-repo.example.com/enterprise-module-2.0.0.zip\",\n        \"credentials\": {\n          \"username\": \"download-user\",\n          \"password\": \"download-password\"\n        }\n      }\n    },\n    {\n      \"name\": \"ModuleB\",\n      \"location\": {\n        \"location_type\": \"https\",\n        \"url\": \"https://modules.example.com/module-b-2.5.0.zip\"\n      }\n    },\n    {\n      \"name\": \"ModuleC\",\n      \"location\": {\n        \"location_type\": \"http\",\n        \"url\": \"http://internal-server.local/module-c-1.2.0.zip\"\n      }\n    }\n  ]\n}","section_id":"example-requests"},{"id":"example-requests-ex2","language":"sh","code":"POST /v1/bootstrap/recover_cluster\n{\n  \"action\": \"recover_cluster\",\n  \"recovery_filename\": \"/path/to/backup.rdb\",\n  \"credentials\": {\n    \"username\": \"admin@example.com\",\n    \"password\": \"your-secure-password\"\n  },\n  \"user_defined_modules\": [\n    {\n      \"name\": \"ModuleA\",\n      \"location\": {\n        \"location_type\": \"https\",\n        \"url\": \"https://private-repo.example.com/enterprise-module-2.0.0.zip\",\n        \"credentials\": {\n          \"username\": \"download-user\",\n          \"password\": \"download-password\"\n        }\n      }\n    },\n    {\n      \"name\": \"ModuleB\",\n      \"location\": {\n        \"location_type\": \"https\",\n        \"url\": \"https://modules.example.com/module-b-2.5.0.zip\"\n      }\n    },\n    {\n      \"name\": \"ModuleC\",\n      \"location\": {\n        \"location_type\": \"http\",\n        \"url\": \"http://internal-server.local/module-c-1.2.0.zip\"\n      }\n    }\n  ]\n}","section_id":"example-requests"},{"id":"troubleshooting-ex0","language":"plaintext","code":"Failed to download and install custom module '<name>': <error details>","section_id":"troubleshooting"},{"id":"add-user-defined-module-to-cluster-ex0","language":"sh","code":"POST https://<host>:<port>/v2/modules/user-defined\n    {\n      \"module_name\": \"TestModule\",\n      \"version\": 1,\n      \"semantic_version\": \"0.0.1\",\n      \"display_name\": \"test module\",\n      \"commands\": [\n        {\n          \"command_arity\": -1,\n          \"command_name\": \"module.command\",\n          \"first_key\": 1,\n          \"flags\": [\"write\"],\n          \"last_key\": 1,\n          \"step\": 1\n        }\n      ],\n      \"command_line_args\": \"\",\n      \"capabilities\": [\"list\", \"of\", \"capabilities\"],\n      \"min_redis_version\": \"2.1\"\n    }","section_id":"add-user-defined-module-to-cluster"},{"id":"add-user-defined-module-to-cluster-ex1","language":"sh","code":"POST https://<host>:<port>/v2/local/modules/user-defined/artifacts\n    \"module=@/tmp/custom-module.zip\"","section_id":"add-user-defined-module-to-cluster"},{"id":"add-a-module-to-a-cluster-ex0","language":"sh","code":"POST https://<host>:<port>/v2/modules\n    \"module=@/tmp/redisearch.Linux-ubuntu16.04-x86_64.2.2.6.zip\"","section_id":"add-a-module-to-a-cluster"}]}
{"schema_version":2,"id":"operate/rs/installing-upgrading/modules/add-module-to-database","title":"Enable a module for a database","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/add-module-to-database/","summary":"","aliases":["/operate/oss_and_stack/stack-with-enterprise/install/add-module-to-database/"],"tags":["docs","operate","rs"],"last_updated":"2026-10-06T08:53:14-07:00","page_type":"content","content_hash":"e5590341093fa8ada2f6e20252856ed60d68e31fcff59fd8de602aa826b3dd4a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Modules add additional functionality to Redis databases for specific use cases. You can enable modules when you create a database."},{"id":"automatically-enabled-capabilities-in-redis-8","title":"Automatically enabled capabilities in Redis 8","role":"content","text":"Databases created with or upgraded to Redis version 8 or later automatically enable the capabilities (modules) bundled with Redis Software as follows:\n\n| Database type | Automatically enabled capabilities |\n|---------------|------------------------------------|\n| RAM-only | [Redis Search](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search)<br />[JSON](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json)<br />[Time series](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/timeseries)<br />[Probabilistic](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/bloom)  |\n| Flash-enabled ([Redis Flex](https://redis.io/docs/latest/operate/rs/databases/flash)) | [JSON](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json)<br />[Probabilistic](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/bloom) |\n| [Active-Active](https://redis.io/docs/latest/operate/rs/databases/active-active)<sup>[1](#enabled-modules-table-note-1)</sup> | [Redis Search](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/search-active-active)<br />[JSON](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json) |\n\n1. <a name=\"enabled-modules-table-note-1\"></a>Upgrading existing Active-Active databases to Redis version 8 does not automatically enable these capabilities. Only new Active-Active databases created with Redis version 8 enable these capabilities by default."},{"id":"create-a-database-with-a-module","title":"Create a database with a module","role":"content","text":"You can only add modules to a database when you first create it. You cannot add modules to an existing database."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"- [Installed the module on the cluster](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/add-module-to-cluster)\n- [Upgraded the module](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/upgrade-module) to the latest version"},{"id":"create-a-database-and-enable-capabilities","title":"Create a database and enable capabilities","role":"content","text":"In the Redis Software Cluster Manager UI, follow these steps to add modules to a database:\n\n1. From the **Databases** screen, select **Quick database** or **Create database**.\n\n1. In the **Capabilities** section, select one or more capabilities:\n\n    ![Select which capabilities to add to your database.](https://redis.io/docs/latest/images/rs/screenshots/databases/quick-db-capabilities-7-8-2.png)\n\n    > [!NOTE]\n    > You cannot use RediSearch 1.x and RediSearch 2.x in the same database.\n    >     \n\n1. To use custom configuration for capabilities:\n\n    1. Select **Parameters**.\n    \n    1. Enter the [configuration options](#module-configuration-options).\n    \n    1. Select **Done**.\n\n1. Configure additional database settings.\n\n    > [!NOTE]\n    > Depending on the [features supported by an enabled module](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/enterprise-capabilities#module-feature-support), certain database configuration fields may not be available.\n    >     \n\n1. Select **Create**."},{"id":"module-configuration-options","title":"Module configuration options","role":"content","text":"- [Redis Search configuration options](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/config)\n\n- [RedisTimeSeries configuration options](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/timeseries/config)\n\n- [RedisBloom configuration options](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/bloom/config)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/installing-upgrading/modules","title":"Install and upgrade modules","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/","summary":"","aliases":["/operate/oss_and_stack/stack-with-enterprise/install/"],"tags":["docs","operate","rs"],"last_updated":"2026-10-06T08:53:14-07:00","children":[{"id":"operate/rs/installing-upgrading/modules/add-module-to-cluster","summary":"","title":"Install a module on a cluster","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/add-module-to-cluster/"},{"id":"operate/rs/installing-upgrading/modules/add-module-to-database","summary":"","title":"Enable a module for a database","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/add-module-to-database/"},{"id":"operate/rs/installing-upgrading/modules/upgrade-module","summary":"","title":"Upgrade modules","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/upgrade-module/"}],"page_type":"content","content_hash":"c52249931da7475983914c57b772f1390c39a05a96ab4cba87a22f1466581214","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Several modules that provide additional Redis capabilities, such as Redis Search, JSON, time series, and probabilistic data structures, come packaged with [Redis Software](https://redis.io/docs/latest/operate/rs). As of version 8.0, Redis Software includes multiple feature sets, compatible with different Redis database versions.\n\nHowever, if you want to use additional modules or upgrade a module to a more recent version, you need to:\n\n1. [Install a module package](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/add-module-to-cluster) on the cluster.\n1. [Enable a module](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/add-module-to-database) for a new database or [upgrade a module](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/upgrade-module) in an existing database."},{"id":"automatically-enabled-capabilities-in-redis-8","title":"Automatically enabled capabilities in Redis 8","role":"content","text":"Databases created with or upgraded to Redis version 8 or later automatically enable the capabilities (modules) bundled with Redis Software as follows:\n\n| Database type | Automatically enabled capabilities |\n|---------------|------------------------------------|\n| RAM-only | [Redis Search](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search)<br />[JSON](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json)<br />[Time series](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/timeseries)<br />[Probabilistic](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/bloom)  |\n| Flash-enabled ([Redis Flex](https://redis.io/docs/latest/operate/rs/databases/flash)) | [JSON](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json)<br />[Probabilistic](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/bloom) |\n| [Active-Active](https://redis.io/docs/latest/operate/rs/databases/active-active)<sup>[1](#enabled-modules-table-note-1)</sup> | [Redis Search](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/search-active-active)<br />[JSON](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json) |\n\n1. <a name=\"enabled-modules-table-note-1\"></a>Upgrading existing Active-Active databases to Redis version 8 does not automatically enable these capabilities. Only new Active-Active databases created with Redis version 8 enable these capabilities by default."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/installing-upgrading/modules/upgrade-module","title":"Upgrade modules","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/upgrade-module/","summary":"","aliases":["/operate/oss_and_stack/stack-with-enterprise/install/upgrade-module/"],"tags":["docs","operate","rs"],"last_updated":"2026-10-06T08:53:14-07:00","page_type":"content","content_hash":"d92e5843487b4debe1adb619ae2beb003a21d98fb9ffbccc4b75f5af0ab5ce15","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Upgrade a module in Redis Software to get the latest features and fixes.\n\n> [!NOTE]\n> - If you upgrade a single-node cluster, it does not load the new modules that are bundled with the new cluster version.\n>\n> - Before you upgrade a database with the RediSearch module enabled to Redis 5.0, you must upgrade the RediSearch module to version 1.4.2 or later."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before you upgrade a module enabled in a database, [install the new version of the module on the cluster](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/add-module-to-cluster)."},{"id":"upgrade-a-module-for-a-database","title":"Upgrade a module for a database","role":"content","text":"After you install an updated module on the cluster, you can upgrade databases to use the new module version.\n\n> [!WARNING]\n> After you upgrade the module for a database, the database shards restart. This causes a short interruption in the availability of this database across the cluster.\n\nTo upgrade a module enabled for a database:\n\n1. Connect to the terminal of a node in the cluster.\n1. Run `rladmin status` to list the databases on the node.\n1. Copy the name of the database that uses the module that you want to upgrade.\n\n    ![](https://redis.io/docs/latest/images/rs/rladmin_status.png)\n\n1. Find the exact module name and version:\n\n    1. Extract the module archive (zip) file.\n    1. Open the JSON file.\n    1. Find the module name and version number in the file.\n\n    Here's an example of the JSON file for the RediSearch module:\n\n    ![](https://redis.io/docs/latest/images/rs/module_info.png)\n\n1. To see the versions of the modules on the cluster, run either:\n\n    - `rladmin status modules` - Shows the latest modules available on the cluster and the modules used by databases.\n    - `rladmin status modules all` - Shows all of the modules available on the cluster and the modules used by databases.\n\n1. To upgrade a database to the latest version of Redis and its modules to the latest version without changing the module arguments:\n\n    - For clusters with Redis Software versions 7.8.2 or later, run:\n\n        [code example]\n\n    - For clusters with versions earlier than 7.8.2, include the `latest_with_modules` option:\n\n        [code example]\n\n    - Use `keep_redis_version` to upgrade the modules without upgrading the database to the latest Redis version.\n    \n        `keep_redis_version` is deprecated as of Redis Software version 7.8.2. To upgrade modules without upgrading the Redis database version, set `redis_version` to the current Redis database version instead.\n\n    - To specify the modules to upgrade, add the following for each module:\n\n        [code example]\n\n        > [!WARNING]\n        > The upgrade process does not validate the module upgrade arguments, and incorrect arguments can cause unexpected downtime. Test module upgrade commands in a test environment before you upgrade modules in production.\n        >         \n\n        For the module arguments, use one of the following:\n\n        - `module_args \"<module_arguments>\"` to replace the existing module arguments.\n\n        - `module_args \"\"` without arguments to remove the existing module arguments.\n\n        - `module_args keep_args` to use the existing module arguments."},{"id":"examples-for-redis-software-v782-and-later","title":"Examples for Redis Software v7.8.2 and later","role":"example","text":"The following module upgrade examples are supported for Redis Software versions 7.8.2 and later:\n\n- Keep the current Redis database version, which is 7.2 in this example, and upgrade to the latest version of the enabled modules:\n\n    [code example]\n\n- Upgrade the database to use the latest version of Redis and the latest versions of the enabled modules:\n\n    [code example]"},{"id":"deprecated-examples","title":"Deprecated examples","role":"content","text":"As of Redis Software version 7.8.2, the following module upgrade examples are deprecated but still supported.\n\n- Keep the current version of Redis and upgrade to the latest version of the enabled modules:\n\n    [code example]\n\n- Upgrade the database to the latest Redis version and upgrade RediSearch to 1.6.7 with the specified arguments:\n\n    [code example]\n\n- Upgrade the database to the latest Redis version and upgrade RedisBloom to version 2.2.1 without arguments:\n\n    [code example]\n\n- Upgrade RedisJSON to 1.0.4 with the existing arguments and RedisBloom to version 2.2.1 without arguments:\n\n    [code example]\n\n- Upgrade the database to use the latest version of Redis and use the latest version of the enabled modules:\n\n    [code example]"}],"examples":[{"id":"upgrade-a-module-for-a-database-ex0","language":"sh","code":"rladmin upgrade db < database-name | database-ID >","section_id":"upgrade-a-module-for-a-database"},{"id":"upgrade-a-module-for-a-database-ex1","language":"sh","code":"rladmin upgrade db < database-name | database-ID > latest_with_modules","section_id":"upgrade-a-module-for-a-database"},{"id":"upgrade-a-module-for-a-database-ex2","language":"sh","code":"and module module_name <module_name> version <new_module_version_number> module_args \"<module arguments>\"","section_id":"upgrade-a-module-for-a-database"},{"id":"examples-for-redis-software-v782-and-later-ex0","language":"sh","code":"rladmin upgrade db shopping-cart redis_version 7.2","section_id":"examples-for-redis-software-v782-and-later"},{"id":"examples-for-redis-software-v782-and-later-ex1","language":"sh","code":"rladmin upgrade db shopping-cart","section_id":"examples-for-redis-software-v782-and-later"},{"id":"deprecated-examples-ex0","language":"sh","code":"rladmin upgrade db shopping-cart keep_redis_version latest_with_modules","section_id":"deprecated-examples"},{"id":"deprecated-examples-ex1","language":"sh","code":"rladmin upgrade db shopping-cart and module db_name shopping-cart module_name ft version 10607 module_args \"PARTITIONS AUTO\"","section_id":"deprecated-examples"},{"id":"deprecated-examples-ex2","language":"sh","code":"rladmin upgrade db db:3 and module db_name shopping-cart module_name bf version 20201 module_args \"\"","section_id":"deprecated-examples"},{"id":"deprecated-examples-ex3","language":"sh","code":"rladmin upgrade module db_name MyDB module_name ReJSON version 10004 module_args keep_args and module db_name MyDB module_name bf version 20201 module_args \"\"","section_id":"deprecated-examples"},{"id":"deprecated-examples-ex4","language":"sh","code":"rladmin upgrade db shopping-cart latest_with_modules","section_id":"deprecated-examples"}]}
{"schema_version":2,"id":"operate/rs/installing-upgrading/modules-lifecycle","title":"Module lifecycle","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/modules-lifecycle/","summary":"","aliases":["/operate/oss_and_stack/stack-with-enterprise/modules-lifecycle/"],"tags":["docs","operate","rs"],"last_updated":"2026-10-06T08:53:14-07:00","page_type":"content","content_hash":"270686f6baa70a85f3fbbdae76639c322ee74e5988f06712cc25e4f7af02eeb0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software follows the [Redis Software lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle).  (For complete details, see the Redis Software [subscription agreement](https://redis.com/software-subscription-agreement).)\n\nThe lifecycle model for modules changed with Redis 8.0. This page is organized into two sections accordingly:\n\n- [Redis 8.0 and later](#redis-80-and-later): modules are built into Redis Open Source and share the Redis lifecycle.\n- [Before Redis 8.0](#before-redis-80): legacy modules follow their own per-module release lifecycle and end-of-life schedule."},{"id":"redis-80-and-later","title":"Redis 8.0 and later","role":"content","text":"Starting with Redis 8.0, the capabilities that were previously distributed as separate modules—Redis Search (RediSearch), JSON (RedisJSON), Time series (RedisTimeSeries), and Probabilistic (RedisBloom)—are built into Redis Open Source. [Redis 8 in Redis Open Source replaces Redis Stack](https://redis.io/docs/latest/operate/oss_and_stack), so there is no separate module to install or version.\n\nAs a result:\n\n- **Modules are versioned in lockstep with Redis.** Redis 8.0 ships all of these capabilities as version 8.0, and each Redis version requires the exact matching module version. Redis X.Y requires modules of version X.Y.\n\n- **Modules share the Redis lifecycle.** Because the whole feature set (Redis X.Y plus its modules X.Y) ships and is supported as a single unit, the end-of-life for the modules follows the Redis lifecycle rather than a separate per-module schedule. For Redis Software, this is the [Redis Software product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle), where the end-of-life for each major release occurs 24 months after the formal release of the subsequent major version.\n\nThe module APIs continue to follow [semantic versioning](https://semver.org/)."},{"id":"before-redis-80","title":"Before Redis 8.0","role":"content","text":"The following sections describe the release lifecycle and end-of-life schedule for module versions released before Redis 8.0, when modules were distributed and versioned separately as part of Redis Stack."},{"id":"module-release-numbering","title":"Module release numbering","role":"content","text":"Redis modules use a three-place numbering scheme to identify released versions.\n\nThe format is \"Major1.Major2.Minor\".\n\n- Major sections of the version number represent fundamental changes to functionality and feature capabilities. The _Major1_ and _Major2_ part of the version number are incremented according to the size and scale of the changes in each release.\n\n- The _Minor_ section of the version number represents quality improvements and fixes to existing capabilities.  The minor release number is increased when release quality improves."},{"id":"modules-endoflife-schedule","title":"Module end-of-life schedule","role":"content","text":"End-of-Life for a given Major version is 18 months after the formal release of\nthat version or 12 months after the release of the next subsequent (following) version, whichever comes last.\n\n#### RediSearch\n\n\n\n#### RedisJSON\n\n\n\n#### RedisGraph\n\n\n\n#### RedisTimeSeries\n\n\n\n#### RedisBloom\n\n\n\n#### RedisGears"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/installing-upgrading/move-from-open-source","title":"Move from Redis Open Source to Redis Software","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/move-from-open-source/","summary":"Plan a move from a self-managed Redis Open Source deployment to Redis Software, including how configuration works differently and how to migrate your data.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:34+01:00","page_type":"content","content_hash":"ead17abc9bcf14bd30683501408bfdb71ecf4699091ac0d35d6db51e789704e6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software builds on Redis Open Source, so your data, commands, and client libraries work the same way. What changes is how you deploy and configure Redis: instead of running a single server from a `redis.conf` file, you run a cluster of nodes that hosts managed databases.\n\nThis guide explains those differences and walks through planning a move from a self-managed Redis Open Source deployment to Redis Software.\n\nFor the exhaustive list of supported commands, configuration settings, and RESP versions, see [Redis Open Source compatibility](https://redis.io/docs/latest/operate/rs/references/compatibility)."},{"id":"how-redis-software-differs-from-redis-open-source","title":"How Redis Software differs from Redis Open Source","role":"content","text":"In Redis Open Source, you run one Redis server process per instance and configure it directly, typically through a `redis.conf` file or [`CONFIG SET`](https://redis.io/docs/latest/commands/config-set) at runtime.\n\nRedis Software introduces two layers on top of that model:\n\n- A **cluster** of nodes that provides high availability, scaling, and a management layer.\n- **Databases** that run on the cluster. Each database is a managed object with its own settings, endpoints, and access controls. A single cluster can host many databases.\n\nBecause of this model, you don't edit a configuration file to tune a database. You set cluster-level options during cluster setup and database-level options when you create or update each database."},{"id":"configuration-model-from-redisconf-to-redis-software","title":"Configuration model: from redis.conf to Redis Software","role":"configuration","text":"Redis Software splits configuration across two levels:\n\n| Level | What it controls | How you set it |\n|:------|:-----------------|:---------------|\n| Cluster | Node roles, networking, security, and cluster-wide policies | During [cluster setup](https://redis.io/docs/latest/operate/rs/clusters/new-cluster-setup), then through the Cluster Manager UI, [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin), or the [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api) |\n| Database | Memory limit, persistence, replication, clustering, eviction, and supported Redis Open Source settings | When you [create a database](https://redis.io/docs/latest/operate/rs/databases/create), then through the Cluster Manager UI, `rladmin`, or the REST API |\n\nKey differences from a `redis.conf` workflow:\n\n- **No per-database configuration file.** You manage database settings through the Cluster Manager UI, `rladmin`, or the REST API, not a file you edit on disk.\n- **Only a subset of Redis Open Source settings applies.** Redis Software supports a subset of Redis Open Source configuration settings. Using [`CONFIG GET`](https://redis.io/docs/latest/commands/config-get) or [`CONFIG SET`](https://redis.io/docs/latest/commands/config-set) with an unsupported setting returns an error. See [Compatibility with Redis Open Source configuration settings](https://redis.io/docs/latest/operate/rs/references/compatibility/config-settings) for the full list.\n- **Some settings move to database-level controls.** A few settings that you'd set with `CONFIG SET` in Redis Open Source are instead set per database with `rladmin tune db` or the REST API."},{"id":"plan-your-migration","title":"Plan your migration","role":"content","text":"Use this checklist to plan a move from Redis Open Source to Redis Software:\n\n1. **Inventory your current configuration.** List the `redis.conf` settings and runtime `CONFIG` values your deployment relies on.\n2. **Check compatibility.** Confirm that the [commands](https://redis.io/docs/latest/operate/rs/references/compatibility/commands), [configuration settings](https://redis.io/docs/latest/operate/rs/references/compatibility/config-settings), and [RESP version](https://redis.io/docs/latest/operate/rs/references/compatibility/resp) your application uses are supported.\n3. **Choose a database topology.** Decide whether each database is a standard database or an [Active-Active database](https://redis.io/docs/latest/operate/rs/databases/active-active) for geo-distributed writes.\n4. **Size your deployment.** Review [hardware requirements](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/hardware-requirements) and [supported platforms](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/supported-platforms) for the cluster.\n5. **Plan your data migration.** Decide whether to sync from a running source with Replica Of or import from a file. See [Migrate your data](#migrate-your-data)."},{"id":"installation-and-configuration-flow","title":"Installation and configuration flow","role":"setup","text":"Moving to Redis Software follows this sequence. Reviewing it before you install helps you plan the whole flow, not just the software install.\n\n1. [Plan your deployment](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment) to choose platforms, size hardware, and plan networking.\n2. [Prepare to install](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/prepare-install) by meeting OS and system requirements on each node.\n3. [Install Redis Software](https://redis.io/docs/latest/operate/rs/installing-upgrading/install) on each node.\n4. [Set up the cluster](https://redis.io/docs/latest/operate/rs/clusters/new-cluster-setup) by creating a new cluster on the first node and joining the remaining nodes.\n5. [Create a database](https://redis.io/docs/latest/operate/rs/databases/create) and configure its settings.\n6. Connect your clients to the new database endpoint.\n\nIf you plan to deploy on Kubernetes, the flow maps to the same concepts through [Redis Software for Kubernetes](https://redis.io/docs/latest/operate/kubernetes): you declare the cluster and databases as custom resources, and the operator provisions them. Planning the configuration model up front matters most for Kubernetes deployments, where you define these objects declaratively rather than through the Cluster Manager UI."},{"id":"migrate-your-data","title":"Migrate your data","role":"content","text":"Redis Software offers two ways to move your existing data into a new database:\n\n- **Sync from a running source with Replica Of.** [Replica Of](https://redis.io/docs/latest/operate/rs/databases/import-export/replica-of) (also called Active-Passive) synchronizes a Redis Software database with one or more source databases, including a Redis Open Source database that's external to the cluster. Point the replica at your source's `redis://` endpoint, wait for the initial sync to finish, then cut your applications over to the new database. Use this path to migrate a running deployment with minimal downtime.\n- **Import from a file.** If you have an RDB or backup file, [import it](https://redis.io/docs/latest/operate/rs/databases/import-export/import-data) into the new database.\n\n> [!WARNING]\n> Importing data erases all existing content in the target database. Replica Of also overwrites the destination database during synchronization, so use an empty destination."},{"id":"next-steps","title":"Next steps","role":"content","text":"- See [Redis Open Source compatibility](https://redis.io/docs/latest/operate/rs/references/compatibility) for the full compatibility reference.\n- [Create a database](https://redis.io/docs/latest/operate/rs/databases/create) to configure your first database.\n- Try the [Redis Software quickstart](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/redis-enterprise-software-quickstart) before you plan a full deployment."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/installing-upgrading/product-lifecycle","title":"Redis Software product lifecycle","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle/","summary":"The product lifecycle of Redis Software cluster versions.","tags":["docs","operate","rs"],"last_updated":"2026-10-06T08:53:14-07:00","page_type":"content","content_hash":"e15a4e291b82a271761d1af8cd094bd8e4cb06638a7a2cb077092aa23fc4aa5c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The Redis Software product lifecycle fully reflects the [subscription agreement](https://redis.io/legal/software-agreement/).\nHowever, for any discrepancy between the two policies, the subscription agreement prevails.\n\nRedis Software modules follow the [modules lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules-lifecycle)."},{"id":"redis-software-cluster-version-lifecycle","title":"Redis Software cluster version lifecycle","role":"content","text":"This section describes the lifecycle policy for Redis Software cluster versions."},{"id":"cluster-version-release-numbers","title":"Cluster version release numbers","role":"content","text":"Redis Software uses a four-place numbering scheme to designate released cluster versions.\nThe format is \"Major1.Major2.Minor-Build\".\n\n- Major sections of the version number represents fundamental changes and additions in\n    capabilities to Redis Software. The Major1 and Major2 part of the\n    version number are incremented based on the size and scale of the changes in each\n    release.\n- The Minor section of the version number represents quality improvements, fixes to\n    existing capabilities, and new capabilities which are typically minor, feature-flagged, or optional. \n- Build number is incremented with any changes to the product. Build number is\n    incremented with each build when any change is made to the binaries.\n\nRedis Software typically gets two major releases every year but the product shipping cycles may vary.\nMaintenance releases, typically available on the last minor release of the current major1.major2 release are typically made available on a monthly cadence, although cycles may vary."},{"id":"endoflife-schedule","title":"Cluster version end-of-life schedule","role":"content","text":"For Redis Software cluster versions 6.2 and later, the end-of-life (EOL) for each major release occurs 24 months after the formal release of the subsequent major version. Monthly maintenance will be provided on the last minor release of the major1.major2 releases.\nThis update to the EOL policy allows a lead time of at least 24 months to upgrade to the new cluster version after it is available.\n\n\n| Version - Release date | End of Life (EOL)  |\n| ----------------------------------------- | ------------------ |\n| 8.2 – July 2026\t\t\t\t            | - |\n| 8.0 – October 2025\t\t\t\t        | July 31, 2028 |\n| 7.22 – May 2025\t\t\t\t            | October 30, 2027 |\n| 7.8 – November 2024\t\t\t\t        | May 30, 2027 |\n| 7.4 – February 2024\t\t\t\t        | November 30, 2026 |\n| 7.2 – August 2023\t\t\t\t            | February 28, 2026 |\n| 6.4 – February 2023\t\t\t\t\t\t| August 31, 2025 |\n| 6.2 – August 2021                         | February 28, 2025  |\n| 6.0 – May 2020                            | May 31, 2022  |\n| 5.6 – April 2020                          | October 31, 2021  |\n| 5.4 – December 2018                       | December 31, 2020  |\n| 5.2 – June 2018                           | December 31, 2019  |\n\nThe following timeline chart visualizes the Redis Software product lifecycle, showing release dates and end-of-life dates for each major version:\n\n[code example]"},{"id":"supported-cluster-version-upgrade-paths","title":"Supported cluster version upgrade paths","role":"compatibility","text":"<span title=\"Check mark icon\">&#x2705;</span> Supported – You can upgrade directly from the current Redis Software cluster version.\n\n<span title=\"X icon\">:x:</span> Not supported – You cannot upgrade directly from the current Redis Software cluster version. You must first upgrade to a supported intermediate version.\n\n| Current Redis Software cluster version | Upgrade to Redis Software 7.2.x | Upgrade to Redis Software 7.4.x |  Upgrade to Redis Software 7.8.x | Upgrade to Redis Software 7.22.x | Upgrade to Redis Software 8.0.2-8.0.10 | Upgrade to Redis Software 8.0.16-8.0.20 | Upgrade to Redis Software 8.2.x |\n|:-----------------------:|:----------------:|:----------------:|:----------------:|:----------------:|:----------------:|:----------------:|:----------------:|\n| 6.0.x | <span title=\"Supported\">&#x2705;</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> |\n| 6.2.4<br />6.2.8 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> |\n| 6.2.10<br />6.2.12<br />6.2.18 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> |\n| 6.4.x | <span title=\"Supported\">&#x2705;</span> |  <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> |\n| 7.2.x | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 7.4.x | – | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 7.8.x | – | – | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 7.22.x | – | – | – | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 8.0.2<br />8.0.6<br />8.0.10 | – | – | – | – | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 8.0.16<br />8.0.18<br />8.0.20 | – | – | – | – | - | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 8.2.x | – | – | – | – | – | – | <span title=\"Supported\">&#x2705;</span> |\n\n\nFor detailed upgrade instructions, see [Upgrade a Redis Software cluster](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-cluster).\n\n> [!NOTE]\n> Redis Enterprise for Kubernetes has its own support lifecycle, which accounts for the Kubernetes distribution lifecycle. For details, see [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions)."},{"id":"bundled-redis-database-versions","title":"Bundled Redis database versions","role":"content","text":"Each cluster version of Redis Software includes a set of Redis database versions as follows:\n\n| Redis Software<br />cluster version | Bundled Redis<br />DB versions | Default DB version<br />(upgraded/new databases) |\n|-------|----------|-----|\n| 8.2.0 | 6.2, 7.2, 7.4, 8.0, 8.2, 8.4, 8.6 | 8.6 |\n| 8.0.20 | 6.2, 7.2, 7.4, 8.0, 8.2, 8.4, 8.6 | 8.6 |\n| 8.0.18 | 6.2, 7.2, 7.4, 8.0, 8.2, 8.4, 8.6 | 8.6 |\n| 8.0.16 | 6.2, 7.2, 7.4, 8.0, 8.2, 8.4 | 8.4 |\n| 8.0.10 | 6.2, 7.2, 7.4, 8.0, 8.2, 8.4 | 8.4 |\n| 8.0.6 | 6.2, 7.2, 7.4, 8.0, 8.2 | 8.2 |\n| 8.0.2 | 6.2, 7.2, 7.4, 8.0, 8.2 | 8.2 |\n| 7.22.x | 6.2, 7.2, 7.4 | 7.4 |\n| 7.8.x | 6.2, 7.2, 7.4 | 7.4 |\n| 7.4.x | 6.0, 6.2, 7.2 | 7.2 |\n| 7.2.4 | 6.0, 6.2, 7.2 | 7.2 |\n| 6.4.2 | 6.0, 6.2 | 6.2 |\n| 6.2.x | 6.0, 6.2 | 6.0 |"}],"examples":[{"id":"endoflife-schedule-ex0","language":"timeline {title=\"Redis Software product lifecycle\"}","code":"8.2: Jul 2026 - TBD\n8.0: Oct 2025 - Jul 31, 2028\n7.22: May 2025 - Oct 30, 2027\n7.8: Nov 2024 - May 30, 2027\n7.4: Feb 2024 - Nov 30, 2026\n7.2: Aug 2023 - Feb 28, 2026\n6.4: Feb 2023 - Aug 31, 2025\n6.2: Aug 2021 - Feb 28, 2025\n6.0: May 2020 - May 31, 2022\n5.6: Apr 2020 - Oct 31, 2021\n5.4: Dec 2018 - Dec 31, 2020\n5.2: June 2018 - Dec 31, 2019","section_id":"endoflife-schedule"}]}
{"schema_version":2,"id":"operate/rs/installing-upgrading/quickstarts/docker-quickstart","title":"Docker quickstart for Redis Software","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart/","summary":"Set up a development or test deployment of Redis Software using Docker.","aliases":["/operate/rs/installing-upgrading/get-started-docker/"],"tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"71ee7c9cb30165bb60ee8b14d7ad11c9b0747263ff4cc24545126e9ff56180a2","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!WARNING]\n> Docker containers are currently only supported for development and test environments, not for production. Use [Redis Enterprise on Kubernetes](https://redis.io/docs/latest/operate/kubernetes) for a supported containerized deployment.\n\nFor testing purposes, you can run Redis Software on Docker containers on\nLinux, Windows, or MacOS.\nThe [Redis Software container](https://hub.docker.com/r/redislabs/redis/)\nacts as a node in a cluster.\n\nTo get started with a single Redis Software container:\n\n1. [Install Docker](#install-docker) for your operating system\n\n2. [Run the Redis Software Docker container](#run-the-container)\n\n3. [Set up a cluster](#set-up-a-cluster)\n\n4. [Create a new database](#create-a-database)\n\n5. [Connect to your database](#connect-to-your-database)"},{"id":"install-docker","title":"Install Docker","role":"setup","text":"Follow the Docker installation instructions for your operating system:\n\n- [Linux](https://docs.docker.com/install/#supported-platforms)\n- [MacOS](https://docs.docker.com/docker-for-mac/install/)\n- [Windows](https://store.docker.com/editions/community/docker-ce-desktop-windows)"},{"id":"run-the-container","title":"Run the container","role":"content","text":"To download and start the Redis Software Docker container, run the following\n[`docker run`](https://docs.docker.com/engine/reference/commandline/run/) command in the terminal or command line for your operating system.\n\n> [!NOTE]\n> On Windows, make sure Docker is configured to run Linux-based containers.\n\n[code example]\n\nThe example command runs the Docker container with Redis Software on `localhost` and opens the following ports: \n\n- Port 8443 for HTTPS connections\n\n- Port 9443 for [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api) connections\n\n- Port 12000 configured Redis database port allowing client connections\n\nYou can publish other [ports](https://redis.io/docs/latest/operate/rs/networking/port-configurations)\nwith `-p <host_port>:<container_port>` or use the `--network host` option to open all ports to the host network."},{"id":"set-up-a-cluster","title":"Set up a cluster","role":"content","text":"1. In a browser, go to `https://<name-or-IP-address-of-the-machine-with-Redis-Enterprise-Software-installed>:8443` to access the Cluster Manager UI. If you use a browser on the host machine, you can also access the Cluster Manager UI at `https://localhost:8443`.\n\n    The cluster generates self-signed TLS certificates to secure the connection. Because these self-signed certificates are unknown to the browser, you must accept them before you proceed.\n\n    If the server does not show the sign-in screen, try again after a few minutes.\n\n1. Select **Create new cluster**.\n\n    ![When you first install Redis Software, you need to set up a cluster.](https://redis.io/docs/latest/images/rs/screenshots/cluster/setup/create-cluster.png)\n\n2. Enter an email and password for the administrator account, then select **Next** to proceed to cluster setup.\n\n    ![Set the credentials for your admin user.](https://redis.io/docs/latest/images/rs/screenshots/cluster/setup/admin-credentials.png)\n\n    You can also use these credentials to connect to the [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api).\n\n3. Enter your cluster license key if you have one. Otherwise, a trial version is installed.\n\n    ![Enter your cluster license key if you have one.](https://redis.io/docs/latest/images/rs/screenshots/cluster/setup/cluster-license-key.png)\n\n4. In the **Configuration** section, enter a cluster FQDN such as `cluster.local`, then select **Next**.\n\n    ![Configure the cluster FQDN.](https://redis.io/docs/latest/images/rs/screenshots/cluster/setup/config-cluster.png)\n\n    > [!WARNING]\n    > If the FQDN is `cluster.local`, you cannot configure DNS. You cannot change the FQDN after cluster creation.\n\n1. On the node setup screen, select **Create cluster** to accept the defaults.\n\n    ![Configure the node specific settings.](https://redis.io/docs/latest/images/rs/screenshots/cluster/setup/node-settings.png)\n\n6. Select **OK** to acknowledge the replacement of the HTTPS TLS certificate on the node.  If you receive a browser warning, you can proceed safely.\n\n    ![Modal shown when a page refresh is needed because the certificates have been updated.](https://redis.io/docs/latest/images/rs/screenshots/cluster/setup/https-page-refresh-modal.png)"},{"id":"create-a-database","title":"Create a database","role":"content","text":"1. On the **Databases** screen, select **Quick database**.\n\n    ![Select Quick database on the Databases screen.](https://redis.io/docs/latest/images/rs/screenshots/databases/db-screen.png)\n\n1. Enter 12000 for the **Port**.\n\n    If port 12000 is not available, enter any available port number between 10000 to 19999 or leave it blank to let the cluster assign a port number for you. You will use this port number to connect to the database.\n\n    ![Create a quick database.](https://redis.io/docs/latest/images/rs/screenshots/databases/quick-db-7-8-2.png)\n\n1. Select **Create** to create your database.\n\nWhen you see **Database active** appear on the database configuration screen, the database is activated and ready for you to use.\n\n![Database active icon.](https://redis.io/docs/latest/images/rs/icons/db-active-icon.png)\n\n\nYou now have a Redis database!\n\n> [!NOTE]\n> If you cannot activate the database because of a memory limitation,\nmake sure that Docker has at least 4 GB of memory allocated in the **Advanced** section of Docker **Settings**."},{"id":"connect-to-your-database","title":"Connect to your database","role":"content","text":"After you create the Redis database, you can connect to it to begin storing data."},{"id":"connect-inside-docker","title":"Use redis-cli inside Docker","role":"content","text":"Every installation of Redis Software includes the command-line tool [`redis-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/redis-cli) to interact with your Redis database. You can use `redis-cli` to connect to your database from within the same Docker network.\n\nUse [`docker exec`](https://docs.docker.com/engine/reference/commandline/exec/) to start an interactive `redis-cli` session in the running Redis Software container:\n\n[code example]\n\nTo find the container name or ID, use [`docker ps`](https://docs.docker.com/reference/cli/docker/container/ls/) to list running containers."},{"id":"connect-outside-docker","title":"Connect from the host environment","role":"content","text":"The database you created uses port `12000`, which is also mapped from the Docker container back to the host environment. This lets you use any method you have available locally to [connect to a Redis database](https://redis.io/docs/latest/operate/rs/databases/connect). Use `localhost` as the `host` and `12000` as the port."},{"id":"test-different-topologies","title":"Test different topologies","role":"content","text":"> [!WARNING]\n> Docker containers are currently only supported for development and test environments, not for production. Use [Redis Enterprise on Kubernetes](https://redis.io/docs/latest/operate/kubernetes) for a supported containerized deployment.\n\nWhen deploying Redis Software using Docker for testing, several common topologies are available, according to your requirements:\n\n- [Single-node cluster](#single-node) – For local development or functional testing\n\n- [Multi-node cluster on a single host](#multi-node-one-host) – For a small-scale deployment that is similar to production\n\n- [Multi-node cluster with multiple hosts](#multi-node-multi-host) – For more predictable performance or high availability compared to single-host deployments"},{"id":"single-node","title":"Single node","role":"content","text":"The simplest topology is to run a single-node Redis Software cluster with a single container on a single host machine. You can use this topology for local development or functional testing.\n\nSingle-node clusters have limited functionality. For example, Redis Software can't use replication or protect against failures if the cluster has only one node.\n\n![](https://redis.io/docs/latest/images/rs/RS-Docker-container.png)"},{"id":"multi-node-one-host","title":"Multiple nodes on one host","role":"content","text":"You can create a multi-node Redis Software cluster by deploying multiple containers to a single host machine. The resulting cluster is scale minimized but similar to production deployments.\n\nHowever, this will also have several limitations.  For example, you cannot map the same port on multiple containers on the same host.\n\n![](https://redis.io/docs/latest/images/rs/RS-Docker-cluster-single-host.png)"},{"id":"multi-node-multi-host","title":"Multiple nodes and hosts","role":"content","text":"You can create a multi-node Redis Software cluster with multiple containers by deploying each container to a different host machine.\n\nThis topology minimizes interference between containers, allowing for the testing of more Redis Software features.\n\n![](https://redis.io/docs/latest/images/rs/RS-Docker-cluster-multi-host.png)"}],"examples":[{"id":"run-the-container-ex0","language":"sh","code":"docker run -d --cap-add sys_resource --name RE -p 8443:8443 -p 9443:9443 -p 12000:12000 redislabs/redis","section_id":"run-the-container"},{"id":"connect-inside-docker-ex0","language":"sh","code":"$ docker exec -it <container_name_or_ID> redis-cli -h <host_or_IP> -p <port>\n127.0.0.1:12000> SET key1 123\nOK\n127.0.0.1:12000> GET key1\n\"123\"","section_id":"connect-inside-docker"}]}
{"schema_version":2,"id":"operate/rs/installing-upgrading/quickstarts","title":"Redis Software quickstarts","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/","summary":"Follow these quickstarts to try out Redis Software.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:34+01:00","children":[{"id":"operate/rs/installing-upgrading/quickstarts/redis-enterprise-software-quickstart","summary":"Set up a test deployment of Redis Software for Linux.","title":"Redis Software quickstart","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/redis-enterprise-software-quickstart/"},{"id":"operate/rs/installing-upgrading/quickstarts/docker-quickstart","summary":"Set up a development or test deployment of Redis Software using Docker.","title":"Docker quickstart for Redis Software","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart/"}],"page_type":"content","content_hash":"8bf4f6065794d45b21819c00145cf94e38f939aa7d9a089c6775ff67d0ce1fde","sections":[{"id":"content","title":"Content","role":"content","text":"Try out Redis Software using one of the following quickstarts:\n\n- [Redis Software quickstart](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/redis-enterprise-software-quickstart)\n\n- [Docker quickstart for Redis Software](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart)\n\nAdditional quickstart guides are available to help you:\n\n- Set up a [Redis Flex or Auto Tiering cluster](https://redis.io/docs/latest/operate/rs/databases/flash/quickstart) to optimize  memory resources.\n\n- Set up an [Active-Active cluster](https://redis.io/docs/latest/operate/rs/databases/active-active/get-started) to enable high availability.\n\n- [Benchmark](https://redis.io/docs/latest/operate/rs/clusters/optimize/memtier-benchmark) Redis Software performance."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/installing-upgrading/quickstarts/redis-enterprise-software-quickstart","title":"Redis Software quickstart","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/redis-enterprise-software-quickstart/","summary":"Set up a test deployment of Redis Software for Linux.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:34+01:00","page_type":"content","content_hash":"3cb59e1bae5da236208766d08715885e54aaef0dc6f9b0fc6358776809d8fd5d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This guide helps you install Redis Software on a Linux host to test its capabilities.\n\nWhen finished, you'll have a simple cluster with a single node:\n\n1. [Ensure port availability](#ensure-port-availability)\n\n1. [Install Redis Software](#install-redis-enterprise-software)\n\n1. [Set up a Redis Software cluster](#set-up-a-cluster)\n\n1. [Create a new Redis database](#create-a-database)\n\n1. [Connect to your Redis database](#connect-to-your-database)\n\n> [!NOTE]\n> **This quickstart is designed for local testing only.**\n> For production environments, see the [install and setup](https://redis.io/docs/latest/operate/rs/installing-upgrading#install-redis-enterprise-software) guide for deployment options and instructions."},{"id":"ensure-port-availability","title":"Ensure port availability","role":"content","text":"If ports that Redis assigns to the database are being used by the operating system or other processes, the installation fails.\n\nFollow the relevant sections to configure required ports.\n\nFor recommended and optional port configuration, see [Network port configurations](https://redis.io/docs/latest/operate/rs/networking/port-configurations)."},{"id":"update-sysctlconf-to-avoid-port-collisions","title":"Update `sysctl.conf` to avoid port collisions","role":"content","text":"To avoid port collision, update `/etc/sysctl.conf` to include:\n\n[code example]"},{"id":"os-conflicts-with-port-53","title":"OS conflicts with port 53","role":"content","text":"If port 53 is in use, the installation fails. This issue can occur in default installations of certain operating systems in which `systemd-resolved` (DNS server) or `dnsmasq` is running.\n\nTo prevent this issue, change the system configuration to make this port available before installation.\n\nTo prevent `systemd-resolved` from using port 53:\n\n1. Edit `/etc/systemd/resolved.conf`: \n\n    [code example]\n\n1. Add `DNSStubListener=no` as the last line in the file and save the file.\n\n1. Rename the current `/etc/resolv.conf` file:\n\n    [code example]\n\n1. Create a symbolic link for `/etc/resolv.conf`:\n\n    [code example]\n\n    > [!NOTE]\n    > You might encounter a temporary name resolution error (`sudo: unable to resolve host {hostname}: Temporary failure in name resolution`), which should be fixed when you restart `systemd-resolved` in the next step.\n\n2. Restart the DNS service:\n\n    [code example]\n\nTo prevent `dnsmasq` from using port 53:\n\n1. Stop the `dnsmasq` service if it's running:\n\n    [code example]\n\n1. Prevent `dnsmasq` from starting automatically at system boot:\n\n    [code example]\n\n1. Mask `dnsmasq` to prevent it from being started manually or by other services:\n\n    [code example]\n\n1. Verify `dnsmasq` is no longer active and won't start at system boot:\n\n    [code example]"},{"id":"configuration-for-aws-and-gcp","title":"Configuration for AWS and GCP","role":"configuration","text":"For detailed configuration instructions, see your cloud provider's documentation.\n\n1. Create a VPC that you can use with regional subnets.\n\n1. Within this VPC, create firewall rules that allow external and internal access for Redis Software.\n\n\n| Ingress/Egress   | Source                                             | Protocol  | Ports                                    | Other protocols  |\n|------------------|----------------------------------------------------|-----------|------------------------------------------|------------------|\n| Ingress          | 0.0.0.0/0                                          | TCP       | 21, 22, 53, 8001, 8443, 9443, 8070, <nobr>10000-19999</nobr> | ICMP             |\n| Ingress          | 0.0.0.0/0                                          | UDP       | 53, 5353                                  |                  |\n| Ingress          | 10.0.0.0/8  (if subnets use 10. ranges) | all       | all                                      |                  |"},{"id":"install-redis-software","title":"Install Redis Software","role":"setup","text":"To install Redis Software:\n\n1. Download the installation files from the [Redis Software Download Center](https://redis.io/downloads/#Redis_Software)\nand copy the download package to a machine with a Linux-based OS. \n\n    > [!NOTE]\n    > You are required to create a free account to access the download center.\n\n1. Extract the installation files:\n\n    [code example]\n\n1. Run the `install.sh` script in the current directory:\n\n    [code example]"},{"id":"set-up-a-cluster","title":"Set up a cluster","role":"content","text":"To set up your machine as a Redis Software cluster:\n\n1. In a browser, go to `https://<name-or-IP-address-of-the-machine-with-Redis-Enterprise-Software-installed>:8443` to access the Cluster Manager UI. If you use a browser on the host machine, you can also access the Cluster Manager UI at `https://localhost:8443`.\n\n    The cluster generates self-signed TLS certificates to secure the connection. Because these self-signed certificates are unknown to the browser, you must accept them before you proceed.\n\n    If the server does not show the sign-in screen, try again after a few minutes.\n\n1. Select **Create new cluster**.\n\n    ![When you first install Redis Software, you need to set up a cluster.](https://redis.io/docs/latest/images/rs/screenshots/cluster/setup/create-cluster.png)\n\n2. Enter an email and password for the administrator account, then select **Next** to proceed to cluster setup.\n\n    ![Set the credentials for your admin user.](https://redis.io/docs/latest/images/rs/screenshots/cluster/setup/admin-credentials.png)\n\n    You can also use these credentials to connect to the [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api).\n\n3. Enter your cluster license key if you have one. Otherwise, a trial version is installed.\n\n    ![Enter your cluster license key if you have one.](https://redis.io/docs/latest/images/rs/screenshots/cluster/setup/cluster-license-key.png)\n\n4. In the **Configuration** section, enter a cluster FQDN such as `cluster.local`, then select **Next**.\n\n    ![Configure the cluster FQDN.](https://redis.io/docs/latest/images/rs/screenshots/cluster/setup/config-cluster.png)\n\n    > [!WARNING]\n    > If the FQDN is `cluster.local`, you cannot configure DNS. You cannot change the FQDN after cluster creation.\n\n1. On the node setup screen, select **Create cluster** to accept the defaults.\n\n    ![Configure the node specific settings.](https://redis.io/docs/latest/images/rs/screenshots/cluster/setup/node-settings.png)\n\n6. Select **OK** to acknowledge the replacement of the HTTPS TLS certificate on the node.  If you receive a browser warning, you can proceed safely.\n\n    ![Modal shown when a page refresh is needed because the certificates have been updated.](https://redis.io/docs/latest/images/rs/screenshots/cluster/setup/https-page-refresh-modal.png)"},{"id":"create-a-database","title":"Create a database","role":"content","text":"1. On the **Databases** screen, select **Quick database**.\n\n    ![Select Quick database on the Databases screen.](https://redis.io/docs/latest/images/rs/screenshots/databases/db-screen.png)\n\n1. Enter 12000 for the **Port**.\n\n    If port 12000 is not available, enter any available port number between 10000 to 19999 or leave it blank to let the cluster assign a port number for you. You will use this port number to connect to the database.\n\n    ![Create a quick database.](https://redis.io/docs/latest/images/rs/screenshots/databases/quick-db-7-8-2.png)\n\n1. Select **Create** to create your database.\n\nWhen you see **Database active** appear on the database configuration screen, the database is activated and ready for you to use.\n\n![Database active icon.](https://redis.io/docs/latest/images/rs/icons/db-active-icon.png)\n\n\nYou now have a Redis database!"},{"id":"connect-to-your-database","title":"Connect to your database","role":"content","text":"After you create the Redis database, you can connect to it and store data.\nSee [Test client connection](https://redis.io/docs/latest/operate/rs/databases/connect/test-client-connectivity) for connection options and examples."},{"id":"supported-web-browsers","title":"Supported web browsers","role":"compatibility","text":"To use the Redis Software Cluster Manager UI, you need a modern browser with JavaScript enabled.\n \nThe Cluster Manager UI is officially supported for the latest version of [Google Chrome](https://www.google.com/chrome/), as well as the three previous and three subsequent versions."},{"id":"continue-learning-with-redis-university","title":"Continue learning with Redis University","role":"content","text":""}],"examples":[{"id":"update-sysctlconf-to-avoid-port-collisions-ex0","language":" sh","code":"net.ipv4.ip_local_port_range = 30000 65535","section_id":"update-sysctlconf-to-avoid-port-collisions"},{"id":"os-conflicts-with-port-53-ex0","language":"sh","code":"sudo vi /etc/systemd/resolved.conf","section_id":"os-conflicts-with-port-53"},{"id":"os-conflicts-with-port-53-ex1","language":"sh","code":"sudo mv /etc/resolv.conf /etc/resolv.conf.orig","section_id":"os-conflicts-with-port-53"},{"id":"os-conflicts-with-port-53-ex2","language":"sh","code":"sudo ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf","section_id":"os-conflicts-with-port-53"},{"id":"os-conflicts-with-port-53-ex3","language":"sh","code":"sudo service systemd-resolved restart","section_id":"os-conflicts-with-port-53"},{"id":"os-conflicts-with-port-53-ex4","language":"sh","code":"sudo systemctl stop dnsmasq","section_id":"os-conflicts-with-port-53"},{"id":"os-conflicts-with-port-53-ex5","language":"sh","code":"sudo systemctl disable dnsmasq","section_id":"os-conflicts-with-port-53"},{"id":"os-conflicts-with-port-53-ex6","language":"sh","code":"sudo systemctl mask dnsmasq","section_id":"os-conflicts-with-port-53"},{"id":"os-conflicts-with-port-53-ex7","language":"sh","code":"sudo systemctl status dnsmasq","section_id":"os-conflicts-with-port-53"},{"id":"install-redis-software-ex0","language":"sh","code":"tar vxf <downloaded tar file name>","section_id":"install-redis-software"},{"id":"install-redis-software-ex1","language":"sh","code":"sudo ./install.sh -y","section_id":"install-redis-software"}]}
{"schema_version":2,"id":"operate/rs/installing-upgrading/uninstalling","title":"Uninstall Redis Software","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/uninstalling/","summary":"","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:34+01:00","page_type":"content","content_hash":"c10e13daf62938d67c5b916c0019516c33877c38f61b2c152d82c060dae538c1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Use the script `rl_uninstall.sh` to uninstall Redis Software and remove its files from a node. The script also deletes all Redis data and configuration from the node.\n\nThe uninstall script does not remove the node from the cluster, but the node's status changes to down. For node removal instructions, see [Remove a cluster node](https://redis.io/docs/latest/operate/rs/clusters/remove-node)."},{"id":"uninstall-redis-software","title":"Uninstall Redis Software","role":"content","text":"To uninstall Redis Software from a cluster node:\n\n1. Navigate to the script's location, which is in `/opt/redislabs/bin/` by default.\n\n1. Run the uninstall script as the root user:\n\n    [code example]\n\nWhen you run the uninstall script on a node, it only uninstalls Redis Software from that node. To uninstall Redis Software for the entire cluster, run the uninstall script on each cluster node."}],"examples":[{"id":"uninstall-redis-software-ex0","language":"sh","code":"sudo ./rl_uninstall.sh","section_id":"uninstall-redis-software"}]}
{"schema_version":2,"id":"operate/rs/installing-upgrading/upgrading","title":"Upgrade an existing Redis Software deployment","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/","summary":"How to upgrade a cluster's Redis Software version, database version, and operating system.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:34+01:00","children":[{"id":"operate/rs/installing-upgrading/upgrading/upgrade-cluster","summary":"Upgrade a cluster to a later version of Redis Software.","title":"Upgrade a Redis Software cluster","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-cluster/"},{"id":"operate/rs/installing-upgrading/upgrading/upgrade-database","summary":"Upgrade a Redis Software database.","title":"Upgrade a Redis Software database","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-database/"},{"id":"operate/rs/installing-upgrading/upgrading/upgrade-active-active","summary":"How to upgrade an Active-Active database.","title":"Upgrade an Active-Active database","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-active-active/"},{"id":"operate/rs/installing-upgrading/upgrading/upgrade-os","summary":"Upgrade a Redis Software cluster's operating system to a later major version.","title":"Upgrade a cluster's operating system","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os/"}],"page_type":"content","content_hash":"6972c8b06ab4db3573f51d5101a1d531a92dc63e8a149b968f643992b6ca1014","sections":[{"id":"upgrade-redis-software","title":"Upgrade Redis Software","role":"overview","text":"To upgrade Redis Software:\n\n1. Verify appropriate [network ports](https://redis.io/docs/latest/operate/rs/networking/port-configurations) are either open or used by Redis Software.\n\n1. Complete all [prerequisites](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-cluster#upgrade-prerequisites) before starting the upgrade.\n\n1. Upgrade the software on all nodes of the cluster using one of the following methods:\n    \n    - [In-place upgrade](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-cluster#in-place-upgrade) - Directly upgrade Redis Software on each node in the cluster. Although this method is simpler than the rolling upgrade method, it might cause brief service interruptions as each node is upgraded.\n    \n    - [Rolling upgrade](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-cluster#rolling-upgrade) - Minimize downtime by adding new nodes with an updated Redis Software version to the cluster, one at a time, while keeping the rest of the cluster operational. This method is recommended for production environments that require continuous availability."},{"id":"upgrade-redis-database","title":"Upgrade Redis database","role":"content","text":"[Upgrade each database](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-database) in the cluster or [upgrade an Active-Active database](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-active-active) to enable new features and important fixes."},{"id":"upgrade-operating-system","title":"Upgrade operating system","role":"content","text":"To upgrade the cluster's operating system:\n\n1. Complete all [prerequisites](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os#prerequisites)  before starting the upgrade.\n\n2. Use one of the following rolling upgrade methods:\n\n    - [Extra node method](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os#extra-node-upgrade) - Recommended if you have additional resources available.\n\n    - [Replace node method](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os#replace-node-upgrade) - Recommended if you cannot temporarily allocate additional resources."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/installing-upgrading/upgrading/upgrade-active-active","title":"Upgrade an Active-Active database","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-active-active/","summary":"How to upgrade an Active-Active database.","tags":["docs","operate","rs"],"last_updated":"2026-10-07T13:34:28-05:00","page_type":"content","content_hash":"4e81b2ca78de0bb0ba5f0c66ff916ce7954e6ac1cfd9dfd68b90bc5d9d4f3df6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"To upgrade an [Active-Active database](https://redis.io/docs/latest/operate/rs/databases/active-active) (formerly known as CRDB), send a single upgrade request with `crdb-cli` or the REST API. The request upgrades the database on each participating cluster in the Active-Active database, so you don't need to upgrade each one separately.\n\nThe Active-Active database upgrade:\n\n- Upgrades the Redis version and modules of the database on all participating clusters.\n\n- Updates the module information in the Active-Active database configuration to match the upgraded databases.\n\n- Upgrades the CRDB feature set version after the database is upgraded on all participating clusters."},{"id":"default-db-versions","title":"Default Redis database versions","role":"content","text":"When you upgrade an Active-Active database, it uses the latest Redis version bundled with Redis Software unless you specify a different version.\n\nFor the default Redis database version of each Redis Software release, see [Default Redis database versions](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-database#default-db-versions)."},{"id":"upgrade-prerequisites","title":"Upgrade prerequisites","role":"content","text":"Before upgrading an Active-Active database:\n\n- Review the relevant [release notes](https://redis.io/docs/latest/operate/rs/release-notes) for any preparation instructions.\n\n- [Upgrade Redis Software](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-cluster) on each node of every participating cluster in the Active-Active database. A coordinated upgrade requires Redis Software version 8.0.18 or later on all participating clusters.\n\n- Verify that the target database version is [supported](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-database#db-versions-table) by the Redis Software version of every participating cluster.\n\n- [Check the status](#check-database-status) of the Active-Active database on each participating cluster.\n\n- Check that your client libraries are compatible with the new Redis database version. See the [database upgrade prerequisites](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-database#upgrade-prerequisites) for details.\n\n- To avoid data loss during the upgrade, [back up your data](https://redis.io/docs/latest/operate/rs/databases/import-export/schedule-backups)."},{"id":"upgrade-an-active-active-database","title":"Upgrade an Active-Active database","role":"content","text":"To upgrade an Active-Active database:\n\n**crdb-cli:**\n\n1. Complete all [prerequisites](#upgrade-prerequisites) before starting the upgrade.\n\n1. Find the `<CRDB-GUID>` of your Active-Active database with the [`crdb-cli crdb list`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/list) command:\n\n    [code example]\n\n    Look for the fully qualified domain name (`CLUSTER-FQDN`) of your cluster and use the associated `GUID`:\n\n    [code example]\n\n1. Upgrade the Active-Active database with [`crdb-cli crdb upgrade`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/upgrade). Use the `--preserve-roles` option to keep the current primary shard placement and prevent the clusters from becoming unbalanced.\n\n    [code example]\n\n    To upgrade the database to a version other than the default version, use the `--redis-version` option:\n\n    [code example]\n\n    For additional options, see the [`crdb-cli crdb upgrade` parameters](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/upgrade#parameters).\n\n    By default, the command waits for the upgrade to finish and reports status changes:\n\n    [code example]\n\n    If you use `--no-wait`, check the upgrade's progress with [`crdb-cli task status`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/task/status):\n\n    [code example]\n\n1. Use [`rladmin status databases extra all`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status#status-databases) on each participating cluster to verify that the Redis version is set to the expected value.\n\n    [code example]\n\n**REST API:**\n\n1. Complete all [prerequisites](#upgrade-prerequisites) before starting the upgrade.\n\n1. Find the `<crdb-guid>` of your Active-Active database with a [`GET /v1/crdbs`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/crdbs) REST API request or the [`crdb-cli crdb list`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/list) command.\n\n1. Use an [upgrade Active-Active database](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/crdbs/upgrade) REST API request. Use the `preserve_roles` option to keep the current primary shard placement and prevent the clusters from becoming unbalanced.\n\n    [code example]\n\n    For additional options, see the [request body](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/crdbs/upgrade#request-body) section of the Active-Active database upgrade requests reference.\n\n1. Check the upgrade's progress with the ID of the [CRDB task](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/crdb_tasks#get-crdb_task) returned by the upgrade request:\n\n    [code example]\n\n    The task's `status` is `finished` when the upgrade is complete."},{"id":"check-database-status","title":"Check database status","role":"content","text":"To check the status of the Active-Active database on a participating cluster, run [`rladmin status`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status) on a node of that cluster:\n\n[code example]\n\n![](https://redis.io/docs/latest/images/rs/crdb-upgrade-node.png)\n\nThe statuses of the Active-Active database on the cluster can indicate:\n\n- `OLD REDIS VERSION`: The database is running a Redis version that is outdated or not fully compatible with the current Redis Software cluster version. [Upgrade the Active-Active database](#upgrade-an-active-active-database) to a later version of Redis bundled with the cluster's current Redis Software version.\n\n- `OLD CRDB PROTOCOL VERSION`: The database uses an older CRDB protocol. The upgrade updates the CRDB protocol version unless you use the `--keep-crdt-protocol-version` option (`keep_crdt_protocol_version` in the REST API). See [CRDB protocol version guidelines](#crdb-protocol-version-guidelines) for more information.\n\n- `OLD CRDB FEATURESET VERSION`: The database feature set version is outdated. The upgrade updates the feature set version after the database is upgraded on all participating clusters, unless you use the `--keep-crdt-featureset-version` option (`keep_crdt_featureset_version` in the REST API). See [Feature set version guidelines](#feature-set-version-guidelines) for more information."},{"id":"crdb-protocol-version-guidelines","title":"CRDB protocol version guidelines","role":"content","text":"The CRDB protocol version determines how the database on each participating cluster replicates write operations to the others. By default, the upgrade updates the CRDB protocol version on all participating clusters.\n\nIf you use the `keep_crdt_protocol_version` option, the database is upgraded without updating the CRDB protocol version. In this case:\n\n- You must upgrade the CRDB protocol version before the CRDB feature set version can be updated.\n\n- Upgrade the CRDB protocol version soon after with another [upgrade request](#upgrade-an-active-active-database) without the `keep_crdt_protocol_version` option."},{"id":"feature-set-version-guidelines","title":"Feature set version guidelines","role":"content","text":"Starting with Redis Software version 5.6.0, the feature set version helps support new Active-Active database features.\n\nThe feature set version is an internal version of the Active-Active database that enables new capabilities and improvements across participating clusters. When the feature set version is updated, it is updated for the database on all participating clusters.\n\nBy default, the upgrade updates the feature set version after the database is upgraded on all participating clusters. If you use the `keep_crdt_featureset_version` option, the upgrade keeps the current feature set version.\n\nFollow these upgrade guidelines:\n\n- As of Redis Software version 6.0.20, feature set version 0 is deprecated and support will be removed in a future version.\n\n- To avoid upgrade failures, make sure all your Active-Active databases are configured with the latest feature set version before upgrading to Redis Software 6.0.20 or later."},{"id":"upgrade-feature-set-version","title":"Upgrade feature set version","role":"content","text":"If the feature set version is old, as indicated by the `OLD CRDB FEATURESET VERSION` status:\n\n1. [Upgrade the Active-Active database](#upgrade-an-active-active-database) on all participating clusters and make sure the CRDB protocol version is not outdated.\n\n1. Find the `<CRDB-GUID>` of your Active-Active database.\n\n    You can use the [`crdb-cli crdb list`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/list) command:\n\n    [code example]\n\n    Look for the fully qualified domain name (`CLUSTER-FQDN`) of your cluster and use the associated `GUID`:\n\n    [code example]\n\n1. Update the feature set version of the Active-Active database. See [Feature set version guidelines](#feature-set-version-guidelines) for more information.\n\n    [code example]"},{"id":"upgrade-limitations","title":"Upgrade limitations","role":"content","text":"- When upgrading an Active-Active database from Redis 7.4 or earlier to version 8.0 or later, if you add a module to the database during the upgrade, you cannot use that module's commands, such as [Redis Search](https://redis.io/docs/latest/commands/?group=search) and [JSON](https://redis.io/docs/latest/commands/?group=json) commands, until the database has been upgraded on all participating clusters. These commands are not blocked automatically, and running these commands before finishing the upgrade process can cause syncer crashes.\n\n    This limitation applies only when you add modules to a database during the upgrade. If the database already had modules configured before the upgrade, this limitation does not apply."}],"examples":[{"id":"upgrade-an-active-active-database-ex0","language":"sh","code":"crdb-cli crdb list","section_id":"upgrade-an-active-active-database"},{"id":"upgrade-an-active-active-database-ex1","language":"sh","code":"CRDB-GUID                             NAME    REPL-ID  CLUSTER-FQDN\n    700140c5-478e-49d7-ad3c-64d517ddc486  aatest  1        aatest1.example.com\n    700140c5-478e-49d7-ad3c-64d517ddc486  aatest  2        aatest2.example.com","section_id":"upgrade-an-active-active-database"},{"id":"upgrade-an-active-active-database-ex2","language":"sh","code":"crdb-cli crdb upgrade --crdb-guid <CRDB-GUID> --preserve-roles","section_id":"upgrade-an-active-active-database"},{"id":"upgrade-an-active-active-database-ex3","language":"sh","code":"crdb-cli crdb upgrade --crdb-guid <CRDB-GUID> --redis-version <version> --preserve-roles","section_id":"upgrade-an-active-active-database"},{"id":"upgrade-an-active-active-database-ex4","language":"sh","code":"$ crdb-cli crdb upgrade --crdb-guid <CRDB-GUID> --preserve-roles\n    Task <task-id> created\n      ---> CRDB GUID Assigned: crdb:<CRDB-GUID>\n      ---> Status changed: queued -> started\n      ---> Status changed: started -> finished","section_id":"upgrade-an-active-active-database"},{"id":"upgrade-an-active-active-database-ex5","language":"sh","code":"crdb-cli task status --task-id <task-id>","section_id":"upgrade-an-active-active-database"},{"id":"upgrade-an-active-active-database-ex6","language":"sh","code":"rladmin status databases extra all","section_id":"upgrade-an-active-active-database"},{"id":"upgrade-an-active-active-database-ex7","language":"sh","code":"POST https://<host>:<port>/v1/crdbs/<crdb-guid>/upgrade\n    {\n        \"preserve_roles\": true,\n        // Additional fields\n    }","section_id":"upgrade-an-active-active-database"},{"id":"upgrade-an-active-active-database-ex8","language":"sh","code":"GET https://<host>:<port>/v1/crdb_tasks/<task-id>","section_id":"upgrade-an-active-active-database"},{"id":"check-database-status-ex0","language":"sh","code":"rladmin status","section_id":"check-database-status"},{"id":"upgrade-feature-set-version-ex0","language":"sh","code":"crdb-cli crdb list","section_id":"upgrade-feature-set-version"},{"id":"upgrade-feature-set-version-ex1","language":"sh","code":"CRDB-GUID                             NAME    REPL-ID  CLUSTER-FQDN\n    700140c5-478e-49d7-ad3c-64d517ddc486  aatest  1        aatest1.example.com\n    700140c5-478e-49d7-ad3c-64d517ddc486  aatest  2        aatest2.example.com","section_id":"upgrade-feature-set-version"},{"id":"upgrade-feature-set-version-ex2","language":"sh","code":"crdb-cli crdb update --crdb-guid <CRDB-GUID> --featureset-version yes","section_id":"upgrade-feature-set-version"}]}
{"schema_version":2,"id":"operate/rs/installing-upgrading/upgrading/upgrade-cluster","title":"Upgrade a Redis Software cluster","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-cluster/","summary":"Upgrade a cluster to a later version of Redis Software.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"2c7f415693f9efaba2415419df0b4b3d46eb3dd34e9ef38ab60507d32ed62626","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Before you upgrade a cluster to a later Redis Software version, review the [supported upgrade paths](#supported-upgrade-paths) and [prerequisites](#upgrade-prerequisites).\n\nTo upgrade a cluster's Redis Software version, use one of the following methods:\n\n- [In-place upgrade](#in-place-upgrade) - Directly upgrade Redis Software on each node in the cluster. Although this method is simpler than the rolling upgrade method, it might cause brief service interruptions as each node is upgraded.\n\n- [Rolling upgrade](#rolling-upgrade) - Minimize downtime by adding new nodes with an updated Redis Software version to the cluster, one at a time, while keeping the rest of the cluster operational. This method is recommended for production environments that require continuous availability."},{"id":"supported-upgrade-paths","title":"Supported upgrade paths","role":"compatibility","text":"<span title=\"Check mark icon\">&#x2705;</span> Supported – You can upgrade directly from the current Redis Software cluster version.\n\n<span title=\"X icon\">:x:</span> Not supported – You cannot upgrade directly from the current Redis Software cluster version. You must first upgrade to a supported intermediate version.\n\n| Current Redis Software cluster version | Upgrade to Redis Software 7.2.x | Upgrade to Redis Software 7.4.x |  Upgrade to Redis Software 7.8.x | Upgrade to Redis Software 7.22.x | Upgrade to Redis Software 8.0.2-8.0.10 | Upgrade to Redis Software 8.0.16-8.0.20 | Upgrade to Redis Software 8.2.x |\n|:-----------------------:|:----------------:|:----------------:|:----------------:|:----------------:|:----------------:|:----------------:|:----------------:|\n| 6.0.x | <span title=\"Supported\">&#x2705;</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> |\n| 6.2.4<br />6.2.8 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> |\n| 6.2.10<br />6.2.12<br />6.2.18 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> |\n| 6.4.x | <span title=\"Supported\">&#x2705;</span> |  <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> |\n| 7.2.x | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 7.4.x | – | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 7.8.x | – | – | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 7.22.x | – | – | – | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 8.0.2<br />8.0.6<br />8.0.10 | – | – | – | – | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 8.0.16<br />8.0.18<br />8.0.20 | – | – | – | – | - | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 8.2.x | – | – | – | – | – | – | <span title=\"Supported\">&#x2705;</span> |\n\n\nSee the [Redis Software product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle) for more information about release numbers and the end-of-life schedule.\n\n> [!NOTE]\n> Redis Enterprise for Kubernetes has its own support lifecycle, which accounts for the Kubernetes distribution lifecycle. For details, see [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions)."},{"id":"upgrade-prerequisites","title":"Upgrade prerequisites","role":"content","text":"Before upgrading a cluster:\n\n- Verify access to [rlcheck](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rlcheck) and [rladmin](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin#use-the-rladmin-shell) commands.\n\n- Run [rlcheck](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rlcheck) on each node and verify there are no issues:\n\n    [code example]\n\n- Verify [maintenance mode](https://redis.io/docs/latest/operate/rs/clusters/maintenance-mode) is not enabled:\n\n    1. On a node in the cluster, run [`rladmin status`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status):\n    \n        [code example] \n        \n    1. Review each node's `SHARDS` field. If the value is yellow, the node is in maintenance mode.\n\n        ![](https://redis.io/docs/latest/images/rs/maintenance_mode.png)\n\n    1. To deactivate maintenance mode on a node, run the following [`rladmin node maintenance_mode off`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/node/maintenance-mode#node-maintenance_mode-off) command. See [Deactivate maintenance mode](https://redis.io/docs/latest/operate/rs/clusters/maintenance-mode#deactivate-maintenance-mode) for additional details.\n\n        [code example]\n\n- Verify that you meet the upgrade path requirements for the target cluster version and review the relevant [release notes](https://redis.io/docs/latest/operate/rs/release-notes) for any preparation instructions.\n\n- Before you upgrade a cluster from Redis Software version 6.2.x to 7.8.x, you must follow these steps if the cluster has any databases with Redis version 6.0:\n\n    1. Set the Redis upgrade policy to `latest`:\n\n        [code example]\n\n    1. [Upgrade Redis 6.0 databases](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-database) to Redis 6.2.\n\n- [Upgrade your databases](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-database) to a version that is supported by the target Redis Software version before upgrading the cluster. We recommend you upgrade the databases to the latest supported version if possible. Make sure to test the upgrade in a non-production environment to determine any impact.\n\n- Avoid changing the database configuration or performing other cluster management operations during the cluster upgrade process, as this might cause unexpected results.\n\n- Upgrade the cluster's primary (master) node first. To identify the primary node, use one of the following methods:\n\n    - **Nodes** screen in the new Cluster Manager UI (only available for Redis Software versions 7.2 and later)\n\n    - [`rladmin status nodes`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status#status-nodes) command\n    \n    - [`GET /nodes/status`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/nodes/status#get-all-nodes-status) REST API request"},{"id":"in-place-upgrade","title":"In-place upgrade","role":"content","text":"Starting with the primary node, follow these steps for every node in the cluster. To ensure cluster availability, upgrade each node separately.\n\n1. Complete all [prerequisites](#upgrade-prerequisites) before starting the upgrade.\n\n1.  Verify node operation with the following commands:\n\n    [code example]\n\n    > [!WARNING]\n    > Do not proceed if any shard, node, or endpoint is not `OK`.\n\n1.  Download the Redis Software installation package to the machine running the node from the Download Center on [https://cloud.redis.io](https://cloud.redis.io).  \n\n1.  Extract the installation package:\n\n    [code example]\n\n    > [!NOTE]\n    > You cannot change the installation path or the user during the upgrade.\n\n1.  Run the install command. See [installation script options](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/install-script) for a list of command-line options you can add to the following command. You cannot use options marked as \"new installs only\" during an in-place upgrade.\n\n    [code example]\n\n    The installation script automatically recognizes the upgrade and responds accordingly.\n\n    The upgrade replaces all node processes, which might briefly interrupt any active connections.\n\n1.  Verify the node was upgraded to the new version and is still operational:\n\n    [code example]\n\n1.  Visit the Cluster Manager UI.\n\n    If the Cluster Manager UI was open in a web browser during the upgrade, refresh the browser to reload the console."},{"id":"rolling-upgrade","title":"Rolling upgrade","role":"content","text":"To perform a rolling upgrade of the cluster, use one of the following methods:\n\n- [Extra node method](#extra-node-upgrade) - recommended if you have additional resources available\n\n- [Replace node method](#replace-node-upgrade) - recommended if you cannot temporarily allocate additional resources"},{"id":"extra-node-upgrade","title":"Extra node upgrade method","role":"content","text":"1. Complete all [prerequisites](#upgrade-prerequisites) before starting the rolling upgrade.\n\n1. [Install a later version of Redis Software](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/install-on-linux) on a new node.\n\n1. [Add the new node](https://redis.io/docs/latest/operate/rs/clusters/add-node) to the cluster.\n\n1. If the [cluster uses DNS](https://redis.io/docs/latest/operate/rs/networking/cluster-dns), add the new node’s IP address to the DNS records.\n\n1. [Promote the first new node](https://redis.io/docs/latest/operate/rs/clusters/change-node-role#promote-secondary-node) to become the primary node.\n\n1. [Remove one node](https://redis.io/docs/latest/operate/rs/clusters/remove-node#remove-a-node) running the earlier Redis Software version from the cluster.\n\n1. Repeat the previous steps until all nodes with the earlier Redis Software version are removed. If the final node to remove from the cluster is the primary node, [demote it](https://redis.io/docs/latest/operate/rs/clusters/change-node-role#demote-primary-node) to a secondary node before you remove it."},{"id":"replace-node-upgrade","title":"Replace node upgrade method","role":"content","text":"1. Complete all [prerequisites](#upgrade-prerequisites) before starting the rolling upgrade.\n\n1. [Remove a node](https://redis.io/docs/latest/operate/rs/clusters/remove-node#remove-a-node) with the earlier Redis Software version from the cluster.\n\n1. Uninstall Redis Software from the removed node:\n\n    [code example]\n\n1. [Install a later version of Redis Software](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/install-on-linux) on the removed node or a new node.\n\n1. [Add the new node](https://redis.io/docs/latest/operate/rs/clusters/add-node) to the cluster.\n\n    If you want to reuse the removed node's ID when you add the node to the cluster, run [`rladmin cluster join`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/join) with the `replace_node` flag:\n\n    [code example]\n\n1. If the [cluster uses DNS](https://redis.io/docs/latest/operate/rs/networking/cluster-dns), add the new node’s IP address to the DNS records.\n\n1. [Promote the first new node](https://redis.io/docs/latest/operate/rs/clusters/change-node-role#promote-secondary-node) to become the primary node.\n\n1. Verify node health:\n\n    1. Run `rlcheck` on all nodes:\n\n        [code example]\n\n        The output lists the result of each verification test:\n\n        [code example]\n\n        For healthy nodes, the expected output is `ALL TESTS PASSED`.\n\n    1. Run [`rladmin status`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status) on the new node:\n\n        [code example]\n\n        The expected output is the `OK` status for the cluster, nodes, endpoints, and shards:\n\n        [code example]\n\n1. Repeat the previous steps until all nodes with the earlier Redis Software version are replaced. If the final node to remove from the cluster is the primary node, [demote it](https://redis.io/docs/latest/operate/rs/clusters/change-node-role#demote-primary-node) to a secondary node before you remove it."},{"id":"after-cluster-upgrade","title":"After cluster upgrade","role":"content","text":"After all nodes are upgraded, the cluster is fully upgraded. Certain features introduced in the new version of Redis Software only become available after upgrading the entire cluster.\n\n[code example]"}],"examples":[{"id":"upgrade-prerequisites-ex0","language":"sh","code":"rlcheck","section_id":"upgrade-prerequisites"},{"id":"upgrade-prerequisites-ex1","language":"sh","code":"rladmin status","section_id":"upgrade-prerequisites"},{"id":"upgrade-prerequisites-ex2","language":"sh","code":"rladmin node <node_id> maintenance_mode off","section_id":"upgrade-prerequisites"},{"id":"upgrade-prerequisites-ex3","language":"sh","code":"rladmin tune cluster redis_upgrade_policy latest","section_id":"upgrade-prerequisites"},{"id":"in-place-upgrade-ex0","language":" shell","code":"$ rlcheck\n    $ rladmin status extra all","section_id":"in-place-upgrade"},{"id":"in-place-upgrade-ex1","language":"sh","code":"tar vxf <tarfile name>","section_id":"in-place-upgrade"},{"id":"in-place-upgrade-ex2","language":" shell","code":"sudo ./install.sh","section_id":"in-place-upgrade"},{"id":"in-place-upgrade-ex3","language":" shell","code":"$ rlcheck\n    $ rladmin status extra all","section_id":"in-place-upgrade"},{"id":"replace-node-upgrade-ex0","language":"sh","code":"sudo ./rl_uninstall.sh","section_id":"replace-node-upgrade"},{"id":"replace-node-upgrade-ex1","language":"sh","code":"rladmin cluster join nodes <cluster_member_ip_address> username <username> password <password> replace_node <node_id>","section_id":"replace-node-upgrade"},{"id":"replace-node-upgrade-ex2","language":"sh","code":"rlcheck","section_id":"replace-node-upgrade"},{"id":"replace-node-upgrade-ex3","language":"sh","code":"##### Welcome to Redis Software Cluster settings verification utility ####\n        Running test: verify_bootstrap_status\n\t\t                PASS\n        ...\n        Running test: verify_encrypted_gossip\n\t\t                PASS\n        Summary:\n        -------\n        ALL TESTS PASSED.","section_id":"replace-node-upgrade"},{"id":"replace-node-upgrade-ex4","language":"sh","code":"rladmin status extra all","section_id":"replace-node-upgrade"},{"id":"replace-node-upgrade-ex5","language":"sh","code":"CLUSTER:\n        OK. Cluster master: 2 (<IP.address>)\n        Cluster health: OK, [0, 0.0, 0.0]\n        failures/minute - avg1 0.00, avg15 0.00, avg60 0.00.\n        ...","section_id":"replace-node-upgrade"},{"id":"after-cluster-upgrade-ex0","language":"sh","code":"supervisorctl restart cnm_exec","section_id":"after-cluster-upgrade"}]}
{"schema_version":2,"id":"operate/rs/installing-upgrading/upgrading/upgrade-database","title":"Upgrade a Redis Software database","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-database/","summary":"Upgrade a Redis Software database.","tags":["docs","operate","rs"],"last_updated":"2026-10-06T08:53:14-07:00","page_type":"content","content_hash":"6060d86df85ae2615cea3a93f379f47394ab01cf9376a13ad56b7d73fce6ab8c","sections":[{"id":"default-db-versions","title":"Default Redis database versions","role":"overview","text":"When you upgrade an existing database, it uses the latest bundled Redis version unless you specify a different version with the `redis_version` option in the [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs) or [`rladmin upgrade db`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/upgrade#upgrade-db).\n\nTo view available Redis database versions:\n\n- In the Cluster Manager UI, see **Redis database versions** on the **Cluster > Configuration** screen.\n\n- Send a [`GET /nodes` REST API request](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/nodes) and see `supported_database_versions` in the response.\n\nThe default Redis database version differs between Redis Software releases as follows:\n\n<a name=\"db-versions-table\"></a>\n| Redis Software<br />cluster version | Bundled Redis<br />DB versions | Default DB version<br />(upgraded/new databases) |\n|-------|----------|-----|\n| 8.2.0 | 6.2, 7.2, 7.4, 8.0, 8.2, 8.4, 8.6 | 8.6 |\n| 8.0.20 | 6.2, 7.2, 7.4, 8.0, 8.2, 8.4, 8.6 | 8.6 |\n| 8.0.18 | 6.2, 7.2, 7.4, 8.0, 8.2, 8.4, 8.6 | 8.6 |\n| 8.0.16 | 6.2, 7.2, 7.4, 8.0, 8.2, 8.4 | 8.4 |\n| 8.0.10 | 6.2, 7.2, 7.4, 8.0, 8.2, 8.4 | 8.4 |\n| 8.0.6 | 6.2, 7.2, 7.4, 8.0, 8.2 | 8.2 |\n| 8.0.2 | 6.2, 7.2, 7.4, 8.0, 8.2 | 8.2 |\n| 7.22.x | 6.2, 7.2, 7.4 | 7.4 |\n| 7.8.x | 6.2, 7.2, 7.4 | 7.4 |\n| 7.4.x | 6.0, 6.2, 7.2 | 7.2 |\n| 7.2.4 | 6.0, 6.2, 7.2 | 7.2 |\n| 6.4.2 | 6.0, 6.2 | 6.2 |\n| 6.2.x | 6.0, 6.2 | 6.0 |"},{"id":"upgrade-prerequisites","title":"Upgrade prerequisites","role":"content","text":"Before upgrading a database:\n\n- Review the relevant [release notes](https://redis.io/docs/latest/operate/rs/release-notes) for any preparation instructions.\n\n- Verify that both the [current database version and the target database version are supported](#db-versions-table) by the cluster's Redis Software version.\n\n    To determine the current database version:\n\n    - Use the Cluster Manager UI to open the **Configuration** tab for the database and select ![The About database button](https://redis.io/docs/latest/images/rs/icons/info-icon.png#no-click) **About**.\n\n    - _(Optional)_ Use the [`rladmin status extra all`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status) command to display configuration details:\n\n        [code example]\n    \n        When the database compatibility version is outdated, <nobr>`OLD REDIS VERSION`</nobr> appears in the command output.\n\n- Verify the cluster is fully upgraded and operational.\n\n    Use the Cluster Manager UI to display the **Configuration** tab for the cluster. The tab displays the cluster version information and the Redis database compatibility version.\n\n- For [Replica Of databases](https://redis.io/docs/latest/operate/rs/databases/import-export/replica-of), upgrade the destination database before the source database.\n\n- Check client compatibility with the database version.\n\n    If you run Redis Stack commands with Go-Redis versions 9 and later or Lettuce versions 6 and later, set the client’s protocol version to RESP2 before upgrading your database to Redis version 7.2 to prevent potential application issues due to RESP3 breaking changes. See [Client prerequisites for Redis 7.2 upgrade](https://redis.io/docs/latest/operate/rs/references/compatibility/resp#client-prerequisites-for-redis-72-upgrade) for more details and examples.\n\n- To avoid data loss during the upgrade, [back up your data](https://redis.io/docs/latest/operate/rs/databases/import-export/schedule-backups).  \n\n    You can [export the data](https://redis.io/docs/latest/operate/rs/databases/import-export/export-data) to an external location, [enable replication](https://redis.io/docs/latest/operate/rs/databases/durability-ha/replication), or [enable persistence](https://redis.io/docs/latest/operate/rs/databases/configure/database-persistence).\n\n    When choosing how to back up data, keep the following in mind:\n\n    - To reduce downtime when replication is enabled, a failover is performed before restarting the primary (master) database.\n\n    - When persistence is enabled without replication, the database is unavailable during restart because the data is restored from the persistence file. AOF persistence restoration is slower than snapshot restoration."},{"id":"upgrade-database","title":"Upgrade database","role":"content","text":"To upgrade a database:\n\n**Cluster Manager UI:**\n\n1. Complete all [prerequisites](#upgrade-prerequisites) before starting the upgrade.\n\n1. Optionally, back up the database to minimize the risk of data loss.\n\n1. Upgrade the database and its modules from the **Databases** screen or an individual database's **Configuration** screen in the Cluster Manager UI. During the upgrade process, the database will restart without losing any data.\n\n    To upgrade a database from the **Databases** screen:\n\n    1. Expand the database in the **Databases** list to show details.\n\n        ![The database details are expanded, including an upgrade option.](https://redis.io/docs/latest/images/rs/screenshots/databases/expand-db-details.png)\n\n    1. Click **Upgrade**.\n\n    1. For **Select version**, choose the Redis version for the database upgrade from the list.\n\n    1. Click **Upgrade**.\n\n    1. After the upgrade process finishes, check the expanded database details to verify that the Redis version is set to the expected value.\n\n    To upgrade a database from its **Configuration** screen:\n\n    1. Select the database from the **Databases** list and go to its **Configuration** screen.\n\n    1. Click the **More actions** button (**&vellip;**).\n\n    1. Select **Upgrade version** from the list.\n\n        ![The upgrade version option is selected in the more actions list.](https://redis.io/docs/latest/images/rs/screenshots/databases/db-more-actions-upgrade-version.png)\n\n    1. For **Select version**, choose the Redis version for the database upgrade from the list.\n\n    1. Click **Upgrade**.\n\n    1. After the upgrade process finishes, click the **About** button (&#9432;) and verify that the Redis version is set to the expected value.\n\n**rladmin:**\n\n1. Complete all [prerequisites](#upgrade-prerequisites) before starting the upgrade.\n\n1. Optionally, back up the database to minimize the risk of data loss.\n\n1. Upgrade the database using [`rladmin upgrade db`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/upgrade). During the upgrade process, the database will restart without losing any data. Use the `preserve_roles` option to keep the database's current state, including primary shard placement, and prevent the cluster from becoming unbalanced.\n\n    To upgrade a database and its modules:\n\n    [code example]\n\n    Example of a successful upgrade:\n\n    [code example]\n\n    To upgrade the database to a version other than the default version, use the `redis_version` parameter:\n\n    [code example]\n\n    For module upgrade options, see [Upgrade modules](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/upgrade-module).\n\n1. Use [`rladmin status databases extra all`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status#status-databases) to display a list of the databases in your cluster and their current Redis database compatibility version. Verify that the Redis version is set to the expected value.\n\n    [code example]\n\n**REST API:**\n\n1. Complete all [prerequisites](#upgrade-prerequisites) before starting the upgrade.\n\n1. Optionally, back up the database to minimize the risk of data loss.\n\n1. Use an [upgrade database](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/upgrade) REST API request. During the upgrade process, the database will restart without losing any data. Use the `preserve_roles` option to keep the database's current state, including primary shard placement, and prevent the cluster from becoming unbalanced.\n\n    [code example]\n\n    For additional database upgrade options, see the [request body](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/upgrade#request-body) section of the database upgrade requests reference."}],"examples":[{"id":"upgrade-prerequisites-ex0","language":"sh","code":"rladmin status extra all","section_id":"upgrade-prerequisites"},{"id":"upgrade-database-ex0","language":"sh","code":"rladmin upgrade db <database name | database ID> preserve_roles","section_id":"upgrade-database"},{"id":"upgrade-database-ex1","language":"sh","code":"rladmin> upgrade db demo\n    Monitoring d194c4a3-631c-4726-b799-331b399fc85c\n    active - SMUpgradeBDB init\n    active - SMUpgradeBDB wait_for_version\n    active - SMUpgradeBDB configure_shards\n    completed - SMUpgradeBDB\n    Done","section_id":"upgrade-database"},{"id":"upgrade-database-ex2","language":"sh","code":"rladmin upgrade db <database name | database ID> redis_version <version> preserve_roles","section_id":"upgrade-database"},{"id":"upgrade-database-ex3","language":"sh","code":"rladmin status databases extra all","section_id":"upgrade-database"},{"id":"upgrade-database-ex4","language":"sh","code":"POST https://<host>:<port>/v1/bdbs/<database_id>/upgrade\n    {\n        \"preserve_roles\": true,\n        // Additional fields\n    }","section_id":"upgrade-database"}]}
{"schema_version":2,"id":"operate/rs/installing-upgrading/upgrading/upgrade-os","title":"Upgrade a cluster's operating system","url":"https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os/","summary":"Upgrade a Redis Software cluster's operating system to a later major version.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:34+01:00","page_type":"content","content_hash":"2da5869de06705e42237187e5e2f77bdf54ef36035ff6e663a0e341647e2d795","sections":[{"id":"overview","title":"Overview","role":"overview","text":"To upgrade the operating system (OS) on a Redis Software cluster to a later major version, perform a rolling upgrade. Because you upgrade one node at a time, you can upgrade your cluster's OS without downtime."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before you upgrade a cluster's operating system:\n\n1. [Upgrade all nodes in the cluster](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-cluster) to a Redis Software version that supports the OS's current version and upgrade version.\n\n    To learn which versions of Redis Software support specific OS versions, see [Supported platforms](https://redis.io/docs/latest/operate/rs/references/supported-platforms#supported-platforms).\n\n\n1. If the cluster uses custom directories, make sure the OS upgrade version also supports custom directories, and specify the same custom directories during installation for all nodes. See [Customize installation directories](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/customize-install-directories) for details."},{"id":"perform-os-rolling-upgrade","title":"Perform OS rolling upgrade","role":"content","text":"To upgrade the cluster's operating system, use one of the following rolling upgrade methods:\n\n- [Extra node method](#extra-node-upgrade) - recommended if you have additional resources available\n\n- [Replace node method](#replace-node-upgrade) - recommended if you cannot temporarily allocate additional resources"},{"id":"extra-node-upgrade","title":"Extra node upgrade method","role":"content","text":"1. Complete all [prerequisites](#prerequisites) before starting the rolling upgrade.\n\n1. Create a node with the OS upgrade version.\n\n1. [Install the cluster's current Redis Software version](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/install-on-linux) on the new node using the installation package for the OS upgrade version.\n\n1. [Add the new node](https://redis.io/docs/latest/operate/rs/clusters/add-node)  to the cluster.\n\n1. If the [cluster uses DNS](https://redis.io/docs/latest/operate/rs/networking/cluster-dns), add the new node’s IP address to the DNS records.\n\n1. [Remove one node](https://redis.io/docs/latest/operate/rs/clusters/remove-node#remove-a-node) running the earlier OS version from the cluster.\n\n1. Repeat the previous steps until all nodes with the earlier OS version are removed. If the final node to remove from the cluster is the primary node, [demote it](https://redis.io/docs/latest/operate/rs/clusters/change-node-role#demote-primary-node) to a secondary node before you remove it."},{"id":"replace-node-upgrade","title":"Replace node upgrade method","role":"content","text":"1. Complete all [prerequisites](#prerequisites) before starting the rolling upgrade.\n\n1. [Remove a node](https://redis.io/docs/latest/operate/rs/clusters/remove-node#remove-a-node) with the earlier OS version from the cluster.\n\n1. Uninstall Redis Software from the removed node:\n\n    [code example]\n\n1. Either upgrade the existing node to the OS upgrade version, or create a new node with the OS upgrade version.\n\n1. [Install the cluster's current Redis Software version](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/install-on-linux) on the upgraded node using the installation package for the OS upgrade version.\n\n1. [Add the new node](https://redis.io/docs/latest/operate/rs/clusters/add-node) to the cluster.\n\n    If you want to reuse the removed node's ID when you add the node to the cluster, run [`rladmin cluster join`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/join) with the `replace_node` flag:\n\n    [code example]\n\n1. If the [cluster uses DNS](https://redis.io/docs/latest/operate/rs/networking/cluster-dns), add the new node’s IP address to the DNS records.\n\n1. Verify node health:\n\n    1. Run `rlcheck` on all nodes:\n\n        [code example]\n\n        The output lists the result of each verification test:\n\n        [code example]\n\n        For healthy nodes, the expected output is `ALL TESTS PASSED`.\n\n    1. Run [`rladmin status`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status) on the new node:\n\n        [code example]\n\n        The expected output is the `OK` status for the cluster, nodes, endpoints, and shards:\n\n        [code example]\n\n1. Repeat the previous steps until all nodes with the earlier OS version are replaced. If the final node to remove from the cluster is the primary node, [demote it](https://redis.io/docs/latest/operate/rs/clusters/change-node-role#demote-primary-node) to a secondary node before you remove it."}],"examples":[{"id":"replace-node-upgrade-ex0","language":"sh","code":"sudo ./rl_uninstall.sh","section_id":"replace-node-upgrade"},{"id":"replace-node-upgrade-ex1","language":"sh","code":"rladmin cluster join nodes <cluster_member_ip_address> username <username> password <password> replace_node <node_id>","section_id":"replace-node-upgrade"},{"id":"replace-node-upgrade-ex2","language":"sh","code":"rlcheck","section_id":"replace-node-upgrade"},{"id":"replace-node-upgrade-ex3","language":"sh","code":"##### Welcome to Redis Enterprise Cluster settings verification utility ####\n        Running test: verify_bootstrap_status\n\t\t                PASS\n        ...\n        Running test: verify_encrypted_gossip\n\t\t                PASS\n        Summary:\n        -------\n        ALL TESTS PASSED.","section_id":"replace-node-upgrade"},{"id":"replace-node-upgrade-ex4","language":"sh","code":"rladmin status extra all","section_id":"replace-node-upgrade"},{"id":"replace-node-upgrade-ex5","language":"sh","code":"CLUSTER:\n        OK. Cluster master: 2 (<IP.address>)\n        Cluster health: OK, [0, 0.0, 0.0]\n        failures/minute - avg1 0.00, avg15 0.00, avg60 0.00.\n        ...","section_id":"replace-node-upgrade"}]}
{"schema_version":2,"id":"operate/rs/monitoring/db-availability","title":"Check database availability for monitoring and load balancers","url":"https://redis.io/docs/latest/operate/rs/monitoring/db-availability/","summary":"Verify if a Redis Software database is available to perform read and write operations and can respond to queries from client applications.","aliases":["/operate/rs/databases/durability-ha/db-availability/"],"tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:24+01:00","page_type":"content","content_hash":"b2affabf0b043c5f1345640ae6b2615c848b354e5906291b11b8ee0b46f942f1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can use the [database availability API](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/availability) to verify whether a Redis Software database is available to perform read and write operations and can respond to queries from client applications. Load balancers and automated monitoring tools can use this API to monitor database availability.\n\n> [!NOTE]\n> Database availability does not guarantee data availability."},{"id":"check-database-availability-for-monitoring","title":"Check database availability for monitoring","role":"content","text":"To monitor database availability, use the following REST API request:\n\n[code example]\n\nIf the OSS Cluster API is enabled, this request verifies all endpoints for this database are available. Otherwise, it verifies the database has at least one available endpoint.\n\nAn endpoint is available only if the database's primary (master) shards are reachable and the endpoint's listener port is bound. If either check fails, the database is reported as unavailable, and the [`error_code` and `description`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/availability#get-db-error-codes) identify the specific reason.\n\nReturns the status code 200 OK if the database is available.\n\nIf the database is unavailable, returns an error status code and a JSON object that contains [`error_code` and `description` fields](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/availability#get-db-error-codes)."},{"id":"check-local-database-endpoint-availability-for-load-balancers","title":"Check local database endpoint availability for load balancers","role":"content","text":"To check database availability when using a load balancer and the recommended `all-nodes` proxy policy, use the local database endpoints for each node:\n\n[code example]\n\nReturns HTTP status code 200 OK if all primary (master) shards are reachable from the local database endpoint.\n\nIf the local database endpoint is unavailable, returns an error status code and a JSON object that contains [`error_code` and `description` fields](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/availability#get-endpoint-error-codes)."},{"id":"lag-aware","title":"Use lag-aware availability checks for disaster recovery","role":"content","text":"The database availability API supports lag-aware availability checks that consider replication lag tolerance. You can reduce the risk of data inconsistencies during disaster recovery by incorporating lag-aware availability checks into your disaster recovery solution and ensuring failover-failback flows only occur when databases are accessible and sufficiently synchronized."},{"id":"change-default-availability-lag-tolerance-threshold","title":"Change default availability lag tolerance threshold","role":"content","text":"The lag tolerance threshold is 100 milliseconds by default. Depending on factors such as workload, network conditions, and throughput, you might want to adjust the lag tolerance threshold.\n\nTo change the default threshold for the entire cluster, set `availability_lag_tolerance_ms` with an [update cluster](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster#put-cluster) request:\n\n[code example]"},{"id":"lag-aware-database-availability-checks","title":"Lag-aware database availability checks","role":"content","text":"To perform a lag-aware database availability check using the cluster's default lag tolerance threshold:\n\n[code example]\n\nTo perform a lag-aware database availability check and override the cluster's default lag tolerance threshold:\n\n[code example]"},{"id":"lag-aware-endpoint-availability-checks","title":"Lag-aware endpoint availability checks","role":"content","text":"To perform a lag-aware database endpoint availability check using the cluster's default lag tolerance threshold:\n\n[code example]\n\nTo perform a lag-aware database endpoint availability check and override the cluster's default lag tolerance threshold:\n\n[code example]"},{"id":"availability-by-database-status","title":"Availability by database status","role":"content","text":"The following table shows the relationship between a database's status and availability. For more details about the database status values, see [BDB status field](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/status).\n\n| Database status | Availability |\n|-----------------|--------------|\n| active          | <span title=\"Available\">&#x2705;</span> Available |\n| active-change-pending | <span title=\"Available\">&#x2705;</span> Available |\n| creation-failed | <span title=\"Not available\">:x:</span> Not available |\n| delete-pending  | <span title=\"Availability not guaranteed\" class=\"font-serif\">:warning:</span> Availability not guaranteed |\n| import-pending  | <span title=\"Available\">&#x2705;</span> Available |\n| pending         | <span title=\"Available\">&#x2705;</span> Available |\n| recovery        | <span title=\"Not available\">:x:</span> Not available |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS155734: Endpoint availability metrics do not work as expected due to a calculation error."}],"examples":[{"id":"check-database-availability-for-monitoring-ex0","language":"sh","code":"GET /v1/bdbs/<database_id>/availability","section_id":"check-database-availability-for-monitoring"},{"id":"check-local-database-endpoint-availability-for-load-balancers-ex0","language":"sh","code":"GET /v1/local/bdbs/<database_id>/endpoint/availability","section_id":"check-local-database-endpoint-availability-for-load-balancers"},{"id":"change-default-availability-lag-tolerance-threshold-ex0","language":"sh","code":"PUT /v1/cluster\n{ \"availability_lag_tolerance_ms\": 100 }","section_id":"change-default-availability-lag-tolerance-threshold"},{"id":"lag-aware-database-availability-checks-ex0","language":"sh","code":"GET /v1/bdbs/<database_id>/availability?extend_check=lag","section_id":"lag-aware-database-availability-checks"},{"id":"lag-aware-database-availability-checks-ex1","language":"sh","code":"GET /v1/bdbs/<database_id>/availability?extend_check=lag&availability_lag_tolerance_ms=100","section_id":"lag-aware-database-availability-checks"},{"id":"lag-aware-endpoint-availability-checks-ex0","language":"sh","code":"GET /v1/local/bdbs/<database_id>/endpoint/availability?extend_check=lag","section_id":"lag-aware-endpoint-availability-checks"},{"id":"lag-aware-endpoint-availability-checks-ex1","language":"sh","code":"GET /v1/local/bdbs/<database_id>/endpoint/availability?extend_check=lag&availability_lag_tolerance_ms=100","section_id":"lag-aware-endpoint-availability-checks"}]}
{"schema_version":2,"id":"operate/rs/monitoring/get-started","title":"Get started with monitoring Redis Software","url":"https://redis.io/docs/latest/operate/rs/monitoring/get-started/","summary":"Collect and visualize Redis Software metrics.","aliases":["/operate/rs/monitoring/prometheus_and_grafana/"],"tags":["docs","integrate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"732370eac4f185d2971f52e2d4480670d2a4f9005c856feb35d03a043b0d5016","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can use Prometheus and compatible integrations to collect and visualize your Redis Software metrics.\n\nMetrics are exposed at the cluster, node, database, shard, and proxy levels.\n\n- [Prometheus](https://prometheus.io/) is an open source systems monitoring and alerting toolkit that aggregates metrics from different sources.\n\nYou can use Prometheus integrations to:\n- Collect and display metrics not available in the Cluster Manager UI\n\n- Set up automatic alerts for all resources\n\n- Display Redis Software metrics alongside data from other systems"},{"id":"prometheus-integrations","title":"Prometheus integrations","role":"content","text":"You can integrate Redis Software with Prometheus and one of the following tools to collect and visualize your deployment's metrics:\n\n- [Grafana](https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise)\n\n- [Datadog](https://redis.io/docs/latest/integrate/datadog-with-redis-enterprise)\n\n- [Dynatrace](https://redis.io/docs/latest/integrate/dynatrace-with-redis-enterprise)\n\n- [New Relic](https://redis.io/docs/latest/integrate/new-relic-with-redis-enterprise)"},{"id":"best-practices-for-monitoring","title":"Best practices for monitoring","role":"content","text":"Follow these best practices when monitoring your Redis Software cluster using the metrics stream engine."},{"id":"monitor-host-level-metrics","title":"Monitor host-level metrics","role":"content","text":"For cluster health, resources, and node stability, monitor these metrics:\n\n| Group | Metric | Why monitor | Unit |\n|-------|--------|-------------|------|\n| CPU utilization | `node_cpu_user`,<br />`node_cpu_system` | Detect CPU saturation from Redis or the OS that results in higher latency and queueing. | Seconds (counter) |\n| Memory (freeable) | <span class=\"break-all\">`node_memory_MemTotal_bytes`</span>,<br /><span class=\"break-all\">`node_memory_MemFree_bytes`</span>,<br /><span class=\"break-all\">`node_memory_Buffers_bytes`</span>,<br /><span class=\"break-all\">`node_memory_Cached_bytes`</span> | Detect memory pressure early. Low free memory or cache can precede swapping or out-of-memory errors. | Bytes (gauge) |\n| Swap usage | <span class=\"break-all\">`node_ephemeral_storage_free`</span> | Monitor memory and disk pressure in your setup. Sustained pressure leads to latency spikes. | Bytes (gauge) |\n| Network traffic | <span class=\"break-all\">`node_ingress_bytes`</span>,<br /><span class=\"break-all\">`node_egress_bytes`</span> | Ensure the network interface is not saturated. Protects replication and client responsiveness. | Bytes (counter) |\n| Disk space | <span class=\"break-all\">`node_filesystem_avail_bytes`</span>,<br /><span class=\"break-all\">`node_filesystem_size_bytes`</span> | Prevent persistence and logging outages from low disk space. | Bytes (gauge) |\n| Cluster state | `has_quorum{…}` | Monitor whether quorum is maintained (1) or lost (0). | Boolean |\n| | `node_metrics_up` | Monitor whether the node is connected and reporting to the cluster. | Gauge |\n| Licensing | `license_shards_limit` | Track shard capacity limits by type (RAM or flash). | Count |\n| Certificates | <span class=\"break-all\">`node_cert_expires_in_seconds`</span> | Avoid downtime from expired node certificates. | Seconds (gauge) |\n| Services – CPU | <span class=\"break-all\">`namedprocess_namegroup_cpu_seconds_total`</span> | Identify abnormal CPU usage by platform services that can starve Redis, such as `alert_mgr`, `redis_mgr`, `dmc_proxy`. | Seconds (counter) |\n| Services – memory | <span class=\"break-all\">`namedprocess_namegroup_memory_bytes`</span> | Detect memory leaks or outliers in platform services, such as `alert_mgr`, `redis_mgr`, `dmc_proxy`. | Bytes (gauge) |"},{"id":"monitor-database-level-metrics","title":"Monitor database-level metrics","role":"content","text":"For database performance, availability, and efficiency, monitor the following metrics:\n\n| Group | Metric | Why monitor | Unit |\n|-------|--------|-------------|------|\n| Memory | <span class=\"break-all\">`redis_server_used_memory`</span> | Track actual data memory to prevent out-of-memory errors and evictions. | Bytes |\n| Memory | <span class=\"break-all\">`redis_server_allocator_allocated`</span> | Monitor bytes allocated by allocator (includes internal fragmentation). | Bytes |\n| Memory | <span class=\"break-all\">`redis_server_allocator_active`</span> | Monitor bytes in active pages (includes external fragmentation). Use delta/ratio versus allocated to infer defraggable memory. | Bytes |\n| Memory | <span class=\"break-all\">`redis_server_active_defrag_running`</span> | Monitor if defragmentation is active and the intended CPU %. High values can affect performance. | % (gauge) |\n| Latency | <span class=\"break-all\">`endpoint_read_requests_latency_histogram`</span>,<br /><span class=\"break-all\">`endpoint_write_requests_latency_histogram`</span>,<br /><span class=\"break-all\">`endpoint_other_requests_latency_histogram`</span> | Monitor server-side command latency. | Microseconds |\n| High availability | <span class=\"break-all\">`redis_server_master_repl_offset`</span> | Compute replica throughput and lag using deltas over time. | Bytes (counter) |\n| High availability | <span class=\"break-all\">`redis_server_master_link_status`</span> | Monitor replica link status (up or down) for early warning of high availability risk. | Status |\n| Active-Active | <span class=\"break-all\">`database_syncer_dst_lag`</span>,<br /><span class=\"break-all\">`database_syncer_lag_ms`</span> | Detect cross-region synchronization delays that impact consistency and SLAs. | Milliseconds (gauge) |\n| Active-Active | <span class=\"break-all\">`database_syncer_state`</span> | Monitor operational state for troubleshooting synchronization issues. | Gauge |\n| Traffic – requests | <span class=\"break-all\">`endpoint_read_requests`</span>,<br /><span class=\"break-all\">`endpoint_write_requests`</span>,<br /><span class=\"break-all\">`endpoint_other_requests`</span> | Monitor workload mix and spikes that drive capacity and latency. Total equals the sum of all three. | Counter |\n| Traffic – responses | <span class=\"break-all\">`endpoint_read_responses`</span>,<br /><span class=\"break-all\">`endpoint_write_responses`</span>,<br /><span class=\"break-all\">`endpoint_other_responses`</span> | Validate service responsiveness and symmetry with requests. | Counter |\n| Traffic – bytes | <span class=\"break-all\">`endpoint_ingress`</span>,<br /><span class=\"break-all\">`endpoint_egress`</span> | Monitor size trends and watch for sudden growth that impacts egress costs or bandwidth. | Bytes (counter) |\n| Egress queue | <span class=\"break-all\">`endpoint_egress_pending`</span>,<br /><span class=\"break-all\">`endpoint_egress_pending_discarded`</span> | Monitor back-pressure and drops that indicate network or client issues. | Bytes (counter) |\n| Connections | <span class=\"break-all\">`endpoint_client_connection`</span> | Monitor accepted connections over time and match against client rollouts or spikes. | Counter |\n| Connections | <span class=\"break-all\">`endpoint_client_connection_expired`</span> | Monitor connections closed due to TTL expiry, which can indicate idle policy or client issues. | Counter |\n| Connections | <span class=\"break-all\">`endpoint_longest_pipeline_histogram`</span> | Monitor long pipelines that can amplify latency bursts and detect misbehaving clients. | Histogram (count) |\n| Connections | <span class=\"break-all\">`endpoint_client_connections`</span>,<br /><span class=\"break-all\">`endpoint_client_disconnections`</span>,<br /><span class=\"break-all\">`endpoint_proxy_disconnections`</span> | Monitor connection churn and identify who closed the socket (client versus proxy). Current connections ≈ connections − disconnections. | Counter |\n| Cache efficiency | <span class=\"break-all\">`redis_server_db_keys`</span>,<br /><span class=\"break-all\">`redis_server_db_avg_ttl`</span> | Monitor key inventory and TTL coverage to inform eviction strategy. | Counter |\n| Cache efficiency | <span class=\"break-all\">`redis_server_evicted_keys\t`</span>,<br /><span class=\"break-all\">`redis_server_expired_keys`</span> | Monitor eviction and expiry rates. Frequent evictions indicate memory pressure or poor sizing. | Counter |\n| Cache efficiency | `cache_hits`,<br /><span class=\"break-all\">`cache_hit_rate`</span> | Monitor hit rate, which drives read latency and cost. Cache hit rate equals <span class=\"break-all\">cache_hits/(cache_hits+cache_misses)</span>. | Count / Ratio (%) |\n| Cache efficiency | <span class=\"break-all\">`endpoint_client_tracking_on_requests`</span>,<br /><span class=\"break-all\">`endpoint_client_tracking_off_requests`</span>,<br /><span class=\"break-all\">`endpoint_disposed_commands_after_client_caching`</span> | Track client-side caching usage and misuse. | Counter |\n| Big / complex keys | <span class=\"break-all\">`redis_server_<data_type>_<size_or_items>_<bucket>`</span> | Monitor oversized keys and cardinality that cause fragmentation, slow replication, and CPU spikes. Track to prevent incidents. Examples:<br /><span class=\"break-all\">`strings_sizes_over_512M`</span>,<br /><span class=\"break-all\">`zsets_items_over_8M`</span> | Gauge |\n| Security – clients | <span class=\"break-all\">`endpoint_client_expiration_refresh`</span>,<br /><span class=\"break-all\">`endpoint_client_establishment_failures`</span> | Monitor unstable clients or problems with authentication or setup. | Counter |\n| Security – LDAP | <span class=\"break-all\">`endpoint_successful_ldap_authentication`</span>,<br /><span class=\"break-all\">`endpoint_failed_ldap_authentication`</span>,<br /><span class=\"break-all\">`endpoint_disconnected_ldap_client`</span> | Monitor authentication health and detect brute-force attacks or misconfigurations. | Counter |\n| Security – cert-based | <span class=\"break-all\">`endpoint_successful_cba_authentication`</span>,<br /><span class=\"break-all\">`endpoint_failed_cba_authentication`</span>,<br /><span class=\"break-all\">`endpoint_disconnected_cba_client`</span> | Monitor certificate authentication status and failures. | Counter |\n| Security – password | <span class=\"break-all\">`endpoint_disconnected_user_password_client`</span> | Monitor password-authentication client disconnects and correlate with policy changes. | Counter |\n| Security – ACL | <span class=\"break-all\">`redis_server_acl_access_denied_auth`</span>,<br /><span class=\"break-all\">`redis_server_acl_access_denied_cmd`</span>,<br /><span class=\"break-all\">`redis_server_acl_access_denied_key`</span>,<br /><span class=\"break-all\">`redis_server_acl_access_denied_channel`</span> | Monitor unauthorized access attempts and incorrectly scoped ACLs. | Counter |\n| Configuration | <span class=\"break-all\">`db_config`</span>| This is an information metric that holds database configuration within labels such as: db_name, db_version, db_port, tls_mode. | counter |"},{"id":"prometheus-quick-start","title":"Prometheus quick start","role":"content","text":"To get started with Prometheus:\n\n1. Create a directory called `prometheus` on your local machine.\n\n1. Within that directory, create a configuration file called `prometheus.yml`.\n\n1. Add the following contents to the configuration file and replace `<cluster_name>` with your Redis Software cluster's FQDN:\n\n    **v2 (metrics stream engine):**\n\n[code example]\n\n**v1:**\n\n[code example]\n\n\n\n    > [!NOTE]\n    > **Use a single scrape target.** The v2 endpoint is cluster-wide. Every node aggregates metrics from all nodes and returns the same complete result, so one target is enough. If you list one target per node, Prometheus stores every series once per target and multiplies each `sum()`-based dashboard panel by the number of targets. This produces no error. Prometheus reports every target as up and Grafana renders normally. Use your cluster FQDN as the single target so metrics remain available if a node goes down.\n\n1. Set up your Prometheus server.\n\n    > [!NOTE]\n    > We recommend running Prometheus in Docker only for development and testing.\n\n    To set up Prometheus on Docker:\n\n    1. Create a _docker-compose.yml_ file:\n\n        [code example]\n\n    1. To start the containers, run:\n\n        [code example]\n\n    1. To check that all of the containers are up, run: \n    \n        [code example]\n\n    1. In your browser, sign in to Prometheus at `http://localhost:9090` to make sure the server is running.\n\n    1. Select **Status** and then **Targets** to check that Prometheus is collecting data from your Redis Software cluster.\n\n        ![The Redis Software target showing that Prometheus is connected to the Redis Software Cluster.](https://redis.io/docs/latest/images/rs/prometheus-target.png)\n\n        If Prometheus is connected to the cluster, you can type **node_up** in the Expression field on the Prometheus home page to see the cluster metrics.\n\n        To confirm you are not scraping duplicate targets, query `count(up{job=\"redis-enterprise\"})`. A correct configuration returns `1`. To confirm the cluster can reach every node, query `node_metrics_up`, which reports `0` for any node the aggregator failed to scrape.\n\n1. Integrate Redis Software and your Prometheus server with one of the [compatible tools](#prometheus-integrations). For help, see the integration guide and official documentation for your chosen tool.\n\n1. Add dashboards for cluster, database, node, and shard metrics."}],"examples":[{"id":"prometheus-quick-start-ex0","language":"yml","code":"global:\n  scrape_interval: 15s\n  evaluation_interval: 15s\n\n# Attach these labels to any time series or alerts when communicating with\n# external systems (federation, remote storage, Alertmanager).\n  external_labels:\n    monitor: \"prometheus-stack-monitor\"\n\n# Load and evaluate rules in this file every 'evaluation_interval' seconds.\n#rule_files:\n# - \"first.rules\"\n# - \"second.rules\"\n\nscrape_configs:\n# scrape Prometheus itself\n  - job_name: prometheus\n    scrape_interval: 10s\n    scrape_timeout: 5s\n    static_configs:\n      - targets: [\"localhost:9090\"]\n\n# scrape Redis Software\n  - job_name: redis-enterprise\n    scrape_interval: 30s\n    scrape_timeout: 30s\n    metrics_path: /v2\n    scheme: https\n    tls_config:\n      insecure_skip_verify: true\n    static_configs:\n      - targets: [\"<cluster_name>:8070\"]","section_id":"prometheus-quick-start"},{"id":"prometheus-quick-start-ex1","language":"yml","code":"global:\n  scrape_interval: 15s\n  evaluation_interval: 15s\n\n# Attach these labels to any time series or alerts when communicating with\n# external systems (federation, remote storage, Alertmanager).\n  external_labels:\n    monitor: \"prometheus-stack-monitor\"\n\n# Load and evaluate rules in this file every 'evaluation_interval' seconds.\n#rule_files:\n# - \"first.rules\"\n# - \"second.rules\"\n\nscrape_configs:\n# scrape Prometheus itself\n  - job_name: prometheus\n    scrape_interval: 10s\n    scrape_timeout: 5s\n    static_configs:\n      - targets: [\"localhost:9090\"]\n\n# scrape Redis Software\n  - job_name: redis-enterprise\n    scrape_interval: 30s\n    scrape_timeout: 30s\n    metrics_path: /\n    scheme: https\n    tls_config:\n      insecure_skip_verify: true\n    static_configs:\n      - targets: [\"<cluster_name>:8070\"]","section_id":"prometheus-quick-start"},{"id":"prometheus-quick-start-ex2","language":"yml","code":"version: '3'\n        services:\n            prometheus-server:\n                image: prom/prometheus\n                ports:\n                    - 9090:9090\n                volumes:\n                    - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml","section_id":"prometheus-quick-start"},{"id":"prometheus-quick-start-ex3","language":"sh","code":"$ docker compose up -d","section_id":"prometheus-quick-start"},{"id":"prometheus-quick-start-ex4","language":"sh","code":"docker ps","section_id":"prometheus-quick-start"}]}
{"schema_version":2,"id":"operate/rs/monitoring/health-report","title":"Consolidated health report","url":"https://redis.io/docs/latest/operate/rs/monitoring/health-report/","summary":"View the consolidated health report to get a read-only snapshot of a Redis Software cluster's current system health.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"677faef32865983a1ca61c326def06bd187b852d9d0aae019d8fcffba36d7a52","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The consolidated health report is a read-only, consolidated snapshot of a cluster's current system health, including:\n\n- License and certificate status\n\n- Nodes and services health\n\n- Node memory breakdown, including free and provisioned memory\n\n- Running actions status\n\n- Active alerts\n\n- Basic database status and version\n\n- High availability health\n\nThe consolidated health report consolidates cluster and database health information that was previously scattered across multiple APIs and CLI commands, such as [`rladmin status extra all`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status) and required SSH access to view.\n\nAs of Redis Software version 8.0.16, you can access the cluster and database health reports using the [Cluster manager UI](#view-health-overview) or [REST API requests](#health-rest-api-requests)."},{"id":"view-health-overview","title":"View health overview","role":"content","text":"You can access the cluster and database health reports using the **Overview** screen in the Cluster Manager UI.\n\nYou can view node health\n\n![The health report screen.](https://redis.io/docs/latest/images/rs/screenshots/overview/health-report-overview.png)"},{"id":"cluster-info","title":"Cluster info","role":"content","text":"The **Cluster Info** section provides basic cluster details, including:\n\n- Overall cluster status\n\n- Redis Software cluster version\n\n- License usage and expiration date"},{"id":"cluster-storage","title":"Cluster storage","role":"content","text":"The **Cluster Storage** section provides memory usage details, including used memory, free memory, and total memory for RAM, flash, persistent storage, and ephemeral storage on the cluster."},{"id":"nodes","title":"Nodes","role":"content","text":"The **Nodes** section shows the status of nodes in the cluster and indicates which node is the leader or primary node."},{"id":"databases","title":"Databases","role":"content","text":"In the **Databases** section, you can check each database's status and Redis version. You can click the number listed by each field to display the relevant database names and click on the database links to view database details.\n\n![Active database names and links are displayed.](https://redis.io/docs/latest/images/rs/screenshots/overview/db-status-hover.png)"},{"id":"certificates","title":"Certificates","role":"content","text":"The **Certificates** section shows whether the cluster's certificates are still valid. If a certificate appears as no longer valid, you can click the **Certificates** link to update the certificate.\n\n![The certificates section shows an alert that the SSO service certificate expires soon.](https://redis.io/docs/latest/images/rs/screenshots/overview/cert-expires-soon.png)"},{"id":"actions","title":"Actions","role":"content","text":"In the **Actions** section, you can view currently running actions.\n\n![The Actions section shows a database upgrade is in progress.](https://redis.io/docs/latest/images/rs/screenshots/overview/running-actions.png)"},{"id":"alerts-and-services","title":"Alerts and services","role":"content","text":"If a configured alert threshold is reached or a running service stops, an alert will appear in the **Alerts & Services** section.\n\n![Active alerts are displayed ephemeral and persistent storage have reached 90% capacity.](https://redis.io/docs/latest/images/rs/screenshots/overview/active-alerts.png)"},{"id":"view-node-health","title":"View node health","role":"content","text":"On the **Nodes** screen of the Cluster Manager UI, you can click on a node to go to its **Node configuration** tab and view node health details.\n\n![Node configuration screen.](https://redis.io/docs/latest/images/rs/screenshots/nodes/node-config-screen.png)"},{"id":"verify-nodes","title":"Verify nodes","role":"content","text":"You can check if nodes are functioning properly using one of the following methods:\n\n**Verify all nodes:**\n\nOn the **Nodes** screen, click **Verify all nodes**.\n\n**Verify specific node:**\n\n1. On the **Nodes** screen, find the node you want verify in the list and click the **More actions** button (**&vellip;**).\n    \n1. Select **Verify node** from the list.\n\n![Click the more actions button for a node to access node actions.](https://redis.io/docs/latest/images/rs/screenshots/nodes/primary-node-more-actions-8-0.png)"},{"id":"node-storage","title":"Node storage","role":"content","text":"The **Node storage** section of the **Node configuration** tab provides memory usage details, including used memory, free memory, and total memory for RAM, flash, persistent storage, and ephemeral storage on the node."},{"id":"services","title":"Services","role":"content","text":"In the **Services** section **Node configuration** tab, you can view the list of services running on the node and the most recent status of each.\n\n![The services section shows most services running and one stopped service.](https://redis.io/docs/latest/images/rs/screenshots/nodes/node-stopped-service.png)"},{"id":"shards","title":"Shards","role":"content","text":"When viewing a node's configuration, you can click the **Shards** tab to view a list of database shards running on the node and related details such as each shard's ID, current status, role, and slot range.\n\n![A list of database shards on the node.](https://redis.io/docs/latest/images/rs/screenshots/nodes/node-shards.png)"},{"id":"health-rest-api-requests","title":"Health report REST API requests","role":"content","text":""},{"id":"cluster-health-report-requests","title":"Cluster health report requests","role":"content","text":"[Cluster health report](https://redis.io/docs/latest/operate/rs/references/rest-api/api-reference#tag/Cluster/operation/cluster_cluster_health_report) REST API requests provide overall health information for a cluster:\n\n[code example]"},{"id":"database-health-report-requests","title":"Database health report requests","role":"content","text":"[Database health report](https://redis.io/docs/latest/operate/rs/references/rest-api/api-reference#tag/Database/operation/cluster_database_health_report) REST API requests provide detailed health information for a specific database:\n\n[code example]"}],"examples":[{"id":"cluster-health-report-requests-ex0","language":"sh","code":"GET https://<host>:<port>/v4/cluster/health","section_id":"cluster-health-report-requests"},{"id":"database-health-report-requests-ex0","language":"sh","code":"GET https://<host>:<port>/v4/bdb/<database_id>/health","section_id":"database-health-report-requests"}]}
{"schema_version":2,"id":"operate/rs/monitoring","title":"Monitoring with metrics and alerts","url":"https://redis.io/docs/latest/operate/rs/monitoring/","summary":"Use the metrics that measure the performance of your Redis Software clusters, nodes, databases, and shards to track the performance of your databases.","aliases":["/operate/rs/clusters/monitoring/","/operate/rs/7.4/clusters/monitoring/"],"tags":["docs","operate","rs","kubernetes"],"last_updated":"2026-09-24T10:14:24+01:00","children":[{"id":"operate/rs/monitoring/get-started","summary":"Collect and visualize Redis Software metrics.","title":"Get started with monitoring Redis Software","url":"https://redis.io/docs/latest/operate/rs/monitoring/get-started/"},{"id":"operate/rs/monitoring/v1_monitoring","summary":"Monitor Redis Software clusters and databases using internal monitoring systems and external monitoring tools.","title":"Metrics and alerts for monitoring v1","url":"https://redis.io/docs/latest/operate/rs/monitoring/v1_monitoring/"},{"id":"operate/rs/monitoring/metrics_stream_engine","summary":"The new metrics engine for monitoring Redis Software.","title":"Monitoring v2","url":"https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/"},{"id":"operate/rs/monitoring/observability","summary":"Using monitoring and observability with Redis Software","title":"Redis Software observability and monitoring guidance","url":"https://redis.io/docs/latest/operate/rs/monitoring/observability/"},{"id":"operate/rs/monitoring/health-report","summary":"View the consolidated health report to get a read-only snapshot of a Redis Software cluster's current system health.","title":"Consolidated health report","url":"https://redis.io/docs/latest/operate/rs/monitoring/health-report/"},{"id":"operate/rs/monitoring/db-availability","summary":"Verify if a Redis Software database is available to perform read and write operations and can respond to queries from client applications.","title":"Check database availability for monitoring and load balancers","url":"https://redis.io/docs/latest/operate/rs/monitoring/db-availability/"}],"page_type":"content","content_hash":"02ea0b6721807d25f671251d4b7a112c48de9bded17d9f303b639ef9a454d71c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can use the metrics that measure the performance of your Redis Software clusters, nodes, databases, and shards\nto monitor the performance of your databases."},{"id":"view-metrics-and-configure-alerts","title":"View metrics and configure alerts","role":"content","text":"In the Redis Software Cluster Manager UI, you can view metrics, configure alerts, and send notifications based on alert parameters. You can also access metrics and configure alerts through the REST API.\n\nSee [Metrics and alerts for monitoring v1](https://redis.io/docs/latest/operate/rs/monitoring/v1_monitoring) for more information."},{"id":"metrics-stream-engine","title":"Metrics stream engine","role":"content","text":"The new metrics stream engine is generally available as of [Redis Software version 8.0](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases) This new engine exposes the v2 Prometheus scraping endpoint at `https://<cluster_name>:8070/v2`, exports all time-series metrics to external monitoring tools, and enables real-time monitoring.\n\nSee [Metrics stream engine for monitoring v2](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine) for more information."},{"id":"integrate-with-external-monitoring-tools","title":"Integrate with external monitoring tools","role":"content","text":"To integrate Redis Software metrics into your monitoring environment, see the following integration guides:\n\n- [Grafana](https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise)\n\n- [Datadog](https://redis.io/docs/latest/integrate/datadog-with-redis-enterprise)\n\n- [Dynatrace](https://redis.io/docs/latest/integrate/dynatrace-with-redis-enterprise)\n\n- [New Relic](https://redis.io/docs/latest/integrate/new-relic-with-redis-enterprise)\n\nFor a detailed tutorial to deploy a complete monitoring stack with Prometheus and Grafana, see [Redis Software Observability with Prometheus and Grafana](https://redis.io/learn/operate/observability/redis-software-prometheus-and-grafana).\n\nFilter [Libraries and tools](https://redis.io/docs/latest/integrate) by \"observability\" for additional tools and guides."},{"id":"metrics-reference","title":"Metrics reference","role":"content","text":"Make sure you read the [definition of each metric](https://redis.io/docs/latest/operate/rs/references/metrics)\nso that you understand exactly what it represents."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/monitoring/metrics_stream_engine/alerts-v1-to-v2","title":"Transition cluster manager alerts to Prometheus alerts","url":"https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/alerts-v1-to-v2/","summary":"Transition from internal cluster manager alerts to external monitoring alerts using Prometheus.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:24+01:00","page_type":"content","content_hash":"56b2d6797817287ad77405ea3f42c3816673688a665508dfee7f1bc224630d20","sections":[{"id":"content","title":"Content","role":"content","text":"As Redis Software transitions from the [deprecated monitoring system](https://redis.io/docs/latest/operate/rs/monitoring/v1_monitoring) to the [new metrics stream engine](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine), some internal cluster manager alerts were deprecated in favor of external monitoring solutions.\n\nYou can use the following table to transition from the deprecated alerts and set up equivalent alerts in Prometheus with [PromQL (Prometheus Query Language)](https://prometheus.io/docs/prometheus/latest/querying/basics/):\n\n| Cluster manager alert | Equivalent PromQL | Description |\n|-----------------------|-------------------|-------------|\n| BdbSizeAlert | <span class=\"break-all\">`sum by(db, cluster) (redis_server_used_memory) / sum by(db, cluster) (redis_server_maxmemory) > 0.8`</span> | Redis server memory usage exceeds 80% |\n| NodeMemoryAlert | <span class=\"break-all\">`(node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes > 0.7`</span> | Node memory usage exceeds 70% |\n| NodeFreeFlashAlert | <span class=\"break-all\">`(node_available_flash_bytes - node_bigstore_free_bytes) / node_available_flash_bytes > 0.7`</span> | Node flash storage usage exceeds 70% |\n| NodeEphemeralStorageAlert | <span class=\"break-all\">`(node_ephemeral_storage_avail_bytes - node_ephemeral_storage_free_bytes) / node_ephemeral_storage_avail_bytes > 0.7`</span> | Node ephemeral storage usage exceeds 70% |\n| NodePersistentStorageAlert | <span class=\"break-all\">`(node_persistent_storage_avail_bytes - node_persistent_storage_free_bytes) / node_persistent_storage_avail_bytes > 0.7`</span> | Node persistent storage usage exceeds 70% |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/monitoring/metrics_stream_engine/db-tags-in-metrics","title":"Database tags in metrics","url":"https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/db-tags-in-metrics/","summary":"Expose Redis Software database tag keys as labels in v2 metrics, then join them onto database metrics in your observability tools.","tags":["docs","integrate","rs"],"last_updated":"2026-09-24T10:14:24+01:00","page_type":"content","content_hash":"1ddea2ab5fd8db106c404430c10080862e8cc51ab01cba8c04bdd1b05073300f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software can expose selected [database tags](https://redis.io/docs/latest/operate/rs/databases/configure/db-tags) as labels in the [v2 metrics](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/prometheus-metrics-v2) scraping endpoint. This lets you group, filter, and alert on database metrics by ownership, environment, service, tier, or any other metadata you store as tags.\n\nRedis Software exposes tags through a dedicated `db_tags` metric rather than adding them as labels to every metric. You then join `db_tags` onto your other database metrics in your observability tool.\n\nThe workflow has three parts:\n\n1. [Enable database tags in metrics](#enable-database-tags-in-metrics) and list the tag keys you want to expose.\n1. Redis Software emits a [`db_tags` metric](#exported-metric) for each database, with one label per exposed tag.\n1. In your observability platform, [join `db_tags` onto your database metrics](#use-database-tags-in-observability-platforms) using the shared `cluster` and `db` labels."},{"id":"enable-database-tags-in-metrics","title":"Enable database tags in metrics","role":"content","text":""},{"id":"before-you-begin","title":"Before you begin","role":"content","text":"- The databases whose tags you want to expose must already have [tags](https://redis.io/docs/latest/operate/rs/databases/configure/db-tags) set. Only tags that follow the [tag validation rules](https://redis.io/docs/latest/operate/rs/databases/configure/db-tags#tag-validation-rules) can be exposed.\n- You must be scraping the [v2 metrics endpoint](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/prometheus-metrics-v2), where the `db_tags` metric appears."},{"id":"configuration-fields","title":"Configuration fields","role":"configuration","text":"Two fields in the cluster's [metrics configuration](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/metrics-configuration) control database tag exposure:\n\n| Field | Type | Default | Description |\n|-------|------|---------|-------------|\n| `expose_db_tags` | boolean | `false` | When `true`, enables export of database tags through the `db_tags` metric. |\n| `metrics_tag_keys_exposed` | list of strings | `[]` | The database tag keys that are eligible to be exported as labels. Can contain at most 50 keys. |\n\nSet both fields: turn on `expose_db_tags`, and add the tag keys you want to expose to `metrics_tag_keys_exposed`. Redis Software exports only the tags whose keys appear in this list.\n\n> [!WARNING]\n> `metrics_tag_keys_exposed` is replaced in full whenever you update it. To add a key while keeping the current ones, send the complete list. For example, to add `tier` to `[\"env\", \"team\"]`, send `[\"env\", \"team\", \"tier\"]`; sending only `[\"tier\"]` removes `env` and `team`."},{"id":"set-the-configuration","title":"Set the configuration","role":"content","text":"Update the [metrics configuration](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/metrics-configuration) with either the REST API or `rladmin`. The following examples enable database tags and expose the `env`, `team`, and `tier` tag keys.\n\n**REST API** — send a [`PUT /v1/metrics_config`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/metrics_config#put-metrics-config) request. This is a partial update, so the fields you omit keep their current values:\n\n[code example]\n\n**rladmin** — use [`rladmin metrics config`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/metrics#metrics-config). On the command line, boolean fields take `enabled` or `disabled`, and list fields take a comma-separated set of values:\n\n[code example]"},{"id":"verify-the-configuration","title":"Verify the configuration","role":"content","text":"Confirm that the settings were applied:\n\n- **REST API**: send a [`GET /v1/metrics_config`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/metrics_config#get-metrics-config) request and check that `expose_db_tags` is `true` and `metrics_tag_keys_exposed` lists your keys.\n- **rladmin**: run [`rladmin info metrics`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/info#info-metrics).\n\nThen scrape the [v2 metrics endpoint](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/prometheus-metrics-v2) and confirm that `db_tags` series appear (see [Exported metric](#exported-metric)). If no `db_tags` series appear for a database, check the [emission rules](#emission-rules)."},{"id":"exported-metric","title":"Exported metric","role":"content","text":"When `expose_db_tags` is enabled and a database has at least one tag whose key is listed in `metrics_tag_keys_exposed`, Redis Software emits a `db_tags` sample for that database:\n\n[code example]\n\nKey points:\n\n- `db_tags` is an information metric. Its value is always `1`; the labels carry the information.\n- There is one `db_tags` series per database, identified by the fixed `cluster` and `db` labels.\n- Each exposed tag becomes an additional label on the series."},{"id":"emission-rules","title":"Emission rules","role":"content","text":"Redis Software emits `db_tags` per database according to these rules:\n\n- `db_tags` is emitted only after you [enable database tags in metrics](#enable-database-tags-in-metrics).\n- Only tags that follow the [tag validation rules](https://redis.io/docs/latest/operate/rs/databases/configure/db-tags#tag-validation-rules) can be exported. [System tags](https://redis.io/docs/latest/operate/rs/databases/configure/db-tags#system-tags) and [legacy tags](https://redis.io/docs/latest/operate/rs/databases/configure/db-tags#backward-compatibility-for-existing-tags) that do not meet the rules are not eligible.\n- Matching between a database's tag keys and the keys in `metrics_tag_keys_exposed` is exact and case-sensitive.\n- If a database has no tag whose key is listed in `metrics_tag_keys_exposed`, no `db_tags` sample is emitted for that database."},{"id":"use-database-tags-in-observability-platforms","title":"Use database tags in observability platforms","role":"content","text":"Because tags are exported through the separate `db_tags` metric, you combine them with other database metrics by matching on the shared `cluster` and `db` labels. Match on both labels in every platform so each database is uniquely identified: database IDs can repeat across clusters.\n\nThe following examples use the sample tags `env`, `team`, and `tier`, and the [`redis_server_used_memory`](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/prometheus-metrics-v2) metric."},{"id":"prometheus-and-grafana","title":"Prometheus and Grafana","role":"content","text":"#### Dashboards\n\nPromQL joins the labels from `db_tags` onto another database metric with a many-to-one vector match using the `on` and `group_left` operators. The same PromQL works in Prometheus and in Grafana panels that use a Prometheus data source.\n\n[code example]\n\nTo restrict the result to specific tag values, filter on the `db_tags` side of the match:\n\n[code example]\n\n#### Alerts\n\nYou can alert on tag-enriched metrics with the same join expression. The following is a Prometheus alerting rule that alerts on total memory by environment:\n\n[code example]\n\nReplace `THRESHOLD` with a value appropriate for your deployment."},{"id":"datadog","title":"Datadog","role":"content","text":"The Datadog OpenMetrics check can copy labels between collected metrics with the [`share_labels`](https://docs.datadoghq.com/integrations/openmetrics/) option. In your OpenMetrics instance configuration, share the `db_tags` labels with the metrics that match on `cluster` and `db`.\n\nAdd the following to your OpenMetrics instance configuration:\n\n[code example]\n\nQuery the enriched metric in Datadog:\n\n[code example]"},{"id":"new-relic","title":"New Relic","role":"content","text":"The New Relic [Prometheus OpenMetrics integration](https://docs.newrelic.com/docs/infrastructure/prometheus-integrations/install-configure-openmetrics/configure-prometheus-openmetrics-integrations/) can copy labels between metrics scraped from the same endpoint with the [`copy_attributes`](https://docs.newrelic.com/docs/infrastructure/prometheus-integrations/install-configure-openmetrics/add-rename-or-copy-prometheus-attributes/) transformation. Copy the `db_tags` labels onto the Redis database metrics, matching on `cluster` and `db`. The `to_metrics` prefix selects the target metrics, so adjust `redis_` to match the Redis metric names in your environment.\n\nAdd the following to your Prometheus OpenMetrics integration configuration:\n\n[code example]\n\nQuery the metric in New Relic:\n\n[code example]"},{"id":"dynatrace","title":"Dynatrace","role":"content","text":"Dynatrace has no built-in transformation to combine metrics. Use a custom transformation to attach the `db_tags` labels to your database metrics before ingestion.\n\nThe transformation attaches the `db_tags` labels to each database metric, matching on `cluster` and `db`:\n\n[code example]\n\nQuery the metric in Dynatrace with DQL, using the metric and dimension names as they appear in your environment:\n\n[code example]"},{"id":"manage-cardinality-and-performance","title":"Manage cardinality and performance","role":"content","text":"Redis Software exports tags through one dedicated `db_tags` metric per database instead of adding tag labels to every database metric, which would multiply the number of series by your tag labels. As a result, the performance overhead on the Redis cluster and the metric footprint are already small. Even so, the tags you expose can affect performance and cardinality, for example when you select many tag keys, use high-cardinality tag values, or change tag values frequently.\n\nWhen choosing which tags to expose:\n\n- Expose only the tags you need for dashboards, alerts, routing, or ownership. Exposing unnecessary tags increases monitoring costs and query load.\n- Start with a small set of stable, low-cardinality tags such as `env`, `team`, `service`, or `tier`.\n- Avoid user IDs, request IDs, timestamps, build numbers, and other frequently changing or high-cardinality values."},{"id":"avoid-exposing-sensitive-data","title":"Avoid exposing sensitive data","role":"content","text":"Redis Software sends the tags you expose in metrics to external monitoring tools. Do not use secrets, credentials, personal data, or confidential business data as tag keys or values."},{"id":"troubleshooting","title":"Troubleshooting","role":"errors","text":""},{"id":"tags-dont-appear-in-metrics","title":"Tags don't appear in metrics","role":"content","text":"Confirm that your tags meet the [emission rules](#emission-rules). A tag is exported only when database tags are enabled, the tag follows the [tag validation rules](https://redis.io/docs/latest/operate/rs/databases/configure/db-tags#tag-validation-rules), and its key exactly matches (case-sensitive) a key in `metrics_tag_keys_exposed`."},{"id":"you-see-more-time-series-than-expected","title":"You see more time series than expected","role":"content","text":"The tag labels you expose create additional time series in your monitoring backend. To keep the number of series under control, expose fewer or lower-cardinality tags. See [Manage cardinality and performance](#manage-cardinality-and-performance)."}],"examples":[{"id":"set-the-configuration-ex0","language":"sh","code":"curl -X PUT -H \"accept: application/json\" \\\n     -H \"content-type: application/json\" \\\n     -u \"[username]:[password]\" \\\n     https://[host]:[port]/v1/metrics_config \\\n     -d '{ \"expose_db_tags\": true, \"metrics_tag_keys_exposed\": [\"env\", \"team\", \"tier\"] }' -k -i","section_id":"set-the-configuration"},{"id":"set-the-configuration-ex1","language":"sh","code":"rladmin metrics config expose_db_tags enabled metrics_tag_keys_exposed env,team,tier","section_id":"set-the-configuration"},{"id":"exported-metric-ex0","language":"prometheus","code":"db_tags{cluster=\"my-cluster\", db=\"42\", env=\"prod\", team=\"core\"} 1","section_id":"exported-metric"},{"id":"prometheus-and-grafana-ex0","language":"promql","code":"sum by (env, team) (\n  redis_server_used_memory\n    * on (cluster, db) group_left(env, team)\n      db_tags\n)","section_id":"prometheus-and-grafana"},{"id":"prometheus-and-grafana-ex1","language":"promql","code":"sum by (team) (\n  redis_server_used_memory\n    * on (cluster, db) group_left(env, team)\n      db_tags{env=\"prod\"}\n)","section_id":"prometheus-and-grafana"},{"id":"prometheus-and-grafana-ex2","language":"yaml","code":"groups:\n  - name: redis-db-tags\n    rules:\n      - alert: HighRedisMemoryByEnvironment\n        expr: |\n          sum by (env) (\n            redis_server_used_memory\n              * on (cluster, db) group_left(env)\n                db_tags{env=\"prod\"}\n          ) > THRESHOLD\n        for: 5m","section_id":"prometheus-and-grafana"},{"id":"datadog-ex0","language":"yaml","code":"share_labels:\n  db_tags:\n    match:\n      - cluster\n      - db","section_id":"datadog"},{"id":"datadog-ex1","language":"text","code":"sum:rdse.redis_server_used_memory{*} by {env,team}","section_id":"datadog"},{"id":"new-relic-ex0","language":"yaml","code":"transformations:\n  - copy_attributes:\n      - from_metric: \"db_tags\"\n        to_metrics:\n          - \"redis_\"\n        match_by:\n          - cluster\n          - db","section_id":"new-relic"},{"id":"new-relic-ex1","language":"sql","code":"FROM Metric\nSELECT sum(redis_server_used_memory)\nFACET env, team","section_id":"new-relic"},{"id":"dynatrace-ex0","language":"text","code":"For each db_tags sample:\n  record the exposed tag labels, keyed by (cluster, db)\n\nFor each database metric with (cluster, db):\n  look up the recorded tag labels for (cluster, db)\n  add the env, team, and tier labels","section_id":"dynatrace"},{"id":"dynatrace-ex1","language":"text","code":"timeseries avg(redis_server_used_memory), by:{env, team}","section_id":"dynatrace"}]}
{"schema_version":2,"id":"operate/rs/monitoring/metrics_stream_engine","title":"Monitoring v2","url":"https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/","summary":"The new metrics engine for monitoring Redis Software.","tags":["docs","operate","rs","kubernetes"],"last_updated":"2026-09-29T16:51:25+03:00","children":[{"id":"operate/rs/monitoring/metrics_stream_engine/metrics-configuration","summary":"Configure the Redis Software v2 metrics stream engine.","title":"Metrics configuration","url":"https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/metrics-configuration/"},{"id":"operate/rs/monitoring/metrics_stream_engine/local-metrics-storage","summary":"Keep a history of v2 metrics on each Redis Software node and include it in support packages.","title":"Local metrics storage","url":"https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/local-metrics-storage/"},{"id":"operate/rs/monitoring/metrics_stream_engine/prometheus-metrics-v1-to-v2","summary":"Transition from v1 metrics to v2 PromQL equivalents.","title":"Transition from Prometheus v1 to Prometheus v2","url":"https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/prometheus-metrics-v1-to-v2/"},{"id":"operate/rs/monitoring/metrics_stream_engine/db-tags-in-metrics","summary":"Expose Redis Software database tag keys as labels in v2 metrics, then join them onto database metrics in your observability tools.","title":"Database tags in metrics","url":"https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/db-tags-in-metrics/"},{"id":"operate/rs/monitoring/metrics_stream_engine/prometheus-metrics-v2","summary":"V2 metrics available to Prometheus as of Redis Software version 7.8.2.","title":"Prometheus metrics v2","url":"https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/prometheus-metrics-v2/"},{"id":"operate/rs/monitoring/metrics_stream_engine/alerts-v1-to-v2","summary":"Transition from internal cluster manager alerts to external monitoring alerts using Prometheus.","title":"Transition cluster manager alerts to Prometheus alerts","url":"https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/alerts-v1-to-v2/"}],"page_type":"content","content_hash":"3631ff932b4d9d0d183f99be7779d5f5801693ed0f0ce350ca7952e34bdcda31","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The new metrics stream engine is generally available as of [Redis Software version 8.0](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases).\n\nThe new metrics stream engine:\n\n- Exposes the v2 Prometheus scraping endpoint at `https://<cluster_name>:8070/v2`.\n\n- Exports all time-series metrics to external monitoring tools such as Grafana, DataDog, NewRelic, and Dynatrace using Prometheus.\n\n- Enables real-time monitoring, including full monitoring during maintenance operations, which provides full visibility into performance during events such as shards' failovers and scaling operations."},{"id":"integrate-with-external-monitoring-tools","title":"Integrate with external monitoring tools","role":"content","text":"To integrate Redis Software metrics into your monitoring environment, see the following integration guides:\n\n- [Grafana](https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise)\n\n- [Datadog](https://redis.io/docs/latest/integrate/datadog-with-redis-enterprise)\n\n- [Dynatrace](https://redis.io/docs/latest/integrate/dynatrace-with-redis-enterprise)\n\n- [New Relic](https://redis.io/docs/latest/integrate/new-relic-with-redis-enterprise)\n\nFor a detailed tutorial to deploy a complete monitoring stack with Prometheus and Grafana, see [Redis Software Observability with Prometheus and Grafana](https://redis.io/learn/operate/observability/redis-software-prometheus-and-grafana).\n\nFilter [Libraries and tools](https://redis.io/docs/latest/integrate) by \"observability\" for additional tools and guides."},{"id":"prometheus-metrics-v2","title":"Prometheus metrics v2","role":"content","text":"For a list of all available v2 metrics, see [Prometheus metrics v2](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/prometheus-metrics-v2).\n\nThe v2 scraping endpoint also exposes metrics for `node_exporter`. For more information, see the [Prometheus node_exporter GitHub repository](https://github.com/prometheus/node_exporter)."},{"id":"local-metrics-storage","title":"Local metrics storage","role":"content","text":"[Local metrics storage](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/local-metrics-storage) keeps a history of metrics on each node. It's disabled by default."},{"id":"transition-from-prometheus-v1-to-prometheus-v2","title":"Transition from Prometheus v1 to Prometheus v2","role":"content","text":"If you are already using the existing scraping endpoint for integration, do the following to transition from v1 metrics to v2 metrics:\n\n1. Change the `metrics_path` in your Prometheus configuration file from `/` to `/v2` to use the new scraping endpoint.\n\n    Here's an example of the updated scraping configuration in `prometheus.yml`:\n\n    [code example]\n\n    > [!NOTE]\n    > **Use a single scrape target.** The v2 endpoint is cluster-wide. Every node aggregates metrics from all nodes and returns the same complete result, so one target is enough. If you list one target per node, Prometheus stores every series once per target and multiplies each `sum()`-based dashboard panel by the number of targets. This produces no error. Prometheus reports every target as up and Grafana renders normally. Use your cluster FQDN as the single target so metrics remain available if a node goes down.\n\n1. Use the metrics tables in [this guide](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/prometheus-metrics-v1-to-v2) to transition from v1 metrics to equivalent v2 PromQL.\n\nThe reason for a single target changed in v2. On v1, only the cluster master served the metrics endpoint and other nodes returned a redirect, so the protocol effectively forced one target. On v2, every node returns the full cluster view and no redirects are involved. If your v1 configuration listed multiple node targets, reduce it to one.\n\nIf you prefer a per-node scrape topology, scrape `/v2/node`, which returns only that node's own metrics. Aggregation adds the `cluster` and `node` labels, so `/v2/node` responses omit them. Add `relabel_configs` to supply those labels before using `/v2/node` with the Redis Software Grafana dashboards.\n\nIt is possible to scrape both existing and new endpoints simultaneously, allowing advanced dashboard preparation and a smooth transition."},{"id":"best-practices-for-monitoring","title":"Best practices for monitoring","role":"content","text":"Follow these best practices when monitoring your Redis Software cluster using the metrics stream engine."},{"id":"monitor-host-level-metrics","title":"Monitor host-level metrics","role":"content","text":"For cluster health, resources, and node stability, monitor these metrics:\n\n| Group | Metric | Why monitor | Unit |\n|-------|--------|-------------|------|\n| CPU utilization | `node_cpu_user`,<br />`node_cpu_system` | Detect CPU saturation from Redis or the OS that results in higher latency and queueing. | Seconds (counter) |\n| Memory (freeable) | <span class=\"break-all\">`node_memory_MemTotal_bytes`</span>,<br /><span class=\"break-all\">`node_memory_MemFree_bytes`</span>,<br /><span class=\"break-all\">`node_memory_Buffers_bytes`</span>,<br /><span class=\"break-all\">`node_memory_Cached_bytes`</span> | Detect memory pressure early. Low free memory or cache can precede swapping or out-of-memory errors. | Bytes (gauge) |\n| Swap usage | <span class=\"break-all\">`node_ephemeral_storage_free`</span> | Monitor memory and disk pressure in your setup. Sustained pressure leads to latency spikes. | Bytes (gauge) |\n| Network traffic | <span class=\"break-all\">`node_ingress_bytes`</span>,<br /><span class=\"break-all\">`node_egress_bytes`</span> | Ensure the network interface is not saturated. Protects replication and client responsiveness. | Bytes (counter) |\n| Disk space | <span class=\"break-all\">`node_filesystem_avail_bytes`</span>,<br /><span class=\"break-all\">`node_filesystem_size_bytes`</span> | Prevent persistence and logging outages from low disk space. | Bytes (gauge) |\n| Cluster state | `has_quorum{…}` | Monitor whether quorum is maintained (1) or lost (0). | Boolean |\n| | `node_metrics_up` | Monitor whether the node is connected and reporting to the cluster. | Gauge |\n| Licensing | `license_shards_limit` | Track shard capacity limits by type (RAM or flash). | Count |\n| Certificates | <span class=\"break-all\">`node_cert_expires_in_seconds`</span> | Avoid downtime from expired node certificates. | Seconds (gauge) |\n| Services – CPU | <span class=\"break-all\">`namedprocess_namegroup_cpu_seconds_total`</span> | Identify abnormal CPU usage by platform services that can starve Redis, such as `alert_mgr`, `redis_mgr`, `dmc_proxy`. | Seconds (counter) |\n| Services – memory | <span class=\"break-all\">`namedprocess_namegroup_memory_bytes`</span> | Detect memory leaks or outliers in platform services, such as `alert_mgr`, `redis_mgr`, `dmc_proxy`. | Bytes (gauge) |"},{"id":"monitor-database-level-metrics","title":"Monitor database-level metrics","role":"content","text":"For database performance, availability, and efficiency, monitor the following metrics:\n\n| Group | Metric | Why monitor | Unit |\n|-------|--------|-------------|------|\n| Memory | <span class=\"break-all\">`redis_server_used_memory`</span> | Track actual data memory to prevent out-of-memory errors and evictions. | Bytes |\n| Memory | <span class=\"break-all\">`redis_server_allocator_allocated`</span> | Monitor bytes allocated by allocator (includes internal fragmentation). | Bytes |\n| Memory | <span class=\"break-all\">`redis_server_allocator_active`</span> | Monitor bytes in active pages (includes external fragmentation). Use delta/ratio versus allocated to infer defraggable memory. | Bytes |\n| Memory | <span class=\"break-all\">`redis_server_active_defrag_running`</span> | Monitor if defragmentation is active and the intended CPU %. High values can affect performance. | % (gauge) |\n| Latency | <span class=\"break-all\">`endpoint_read_requests_latency_histogram`</span>,<br /><span class=\"break-all\">`endpoint_write_requests_latency_histogram`</span>,<br /><span class=\"break-all\">`endpoint_other_requests_latency_histogram`</span> | Monitor server-side command latency. | Microseconds |\n| High availability | <span class=\"break-all\">`redis_server_master_repl_offset`</span> | Compute replica throughput and lag using deltas over time. | Bytes (counter) |\n| High availability | <span class=\"break-all\">`redis_server_master_link_status`</span> | Monitor replica link status (up or down) for early warning of high availability risk. | Status |\n| Active-Active | <span class=\"break-all\">`database_syncer_dst_lag`</span>,<br /><span class=\"break-all\">`database_syncer_lag_ms`</span> | Detect cross-region synchronization delays that impact consistency and SLAs. | Milliseconds (gauge) |\n| Active-Active | <span class=\"break-all\">`database_syncer_state`</span> | Monitor operational state for troubleshooting synchronization issues. | Gauge |\n| Traffic – requests | <span class=\"break-all\">`endpoint_read_requests`</span>,<br /><span class=\"break-all\">`endpoint_write_requests`</span>,<br /><span class=\"break-all\">`endpoint_other_requests`</span> | Monitor workload mix and spikes that drive capacity and latency. Total equals the sum of all three. | Counter |\n| Traffic – responses | <span class=\"break-all\">`endpoint_read_responses`</span>,<br /><span class=\"break-all\">`endpoint_write_responses`</span>,<br /><span class=\"break-all\">`endpoint_other_responses`</span> | Validate service responsiveness and symmetry with requests. | Counter |\n| Traffic – bytes | <span class=\"break-all\">`endpoint_ingress`</span>,<br /><span class=\"break-all\">`endpoint_egress`</span> | Monitor size trends and watch for sudden growth that impacts egress costs or bandwidth. | Bytes (counter) |\n| Egress queue | <span class=\"break-all\">`endpoint_egress_pending`</span>,<br /><span class=\"break-all\">`endpoint_egress_pending_discarded`</span> | Monitor back-pressure and drops that indicate network or client issues. | Bytes (counter) |\n| Connections | <span class=\"break-all\">`endpoint_client_connection`</span> | Monitor accepted connections over time and match against client rollouts or spikes. | Counter |\n| Connections | <span class=\"break-all\">`endpoint_client_connection_expired`</span> | Monitor connections closed due to TTL expiry, which can indicate idle policy or client issues. | Counter |\n| Connections | <span class=\"break-all\">`endpoint_longest_pipeline_histogram`</span> | Monitor long pipelines that can amplify latency bursts and detect misbehaving clients. | Histogram (count) |\n| Connections | <span class=\"break-all\">`endpoint_client_connections`</span>,<br /><span class=\"break-all\">`endpoint_client_disconnections`</span>,<br /><span class=\"break-all\">`endpoint_proxy_disconnections`</span> | Monitor connection churn and identify who closed the socket (client versus proxy). Current connections ≈ connections − disconnections. | Counter |\n| Cache efficiency | <span class=\"break-all\">`redis_server_db_keys`</span>,<br /><span class=\"break-all\">`redis_server_db_avg_ttl`</span> | Monitor key inventory and TTL coverage to inform eviction strategy. | Counter |\n| Cache efficiency | <span class=\"break-all\">`redis_server_evicted_keys\t`</span>,<br /><span class=\"break-all\">`redis_server_expired_keys`</span> | Monitor eviction and expiry rates. Frequent evictions indicate memory pressure or poor sizing. | Counter |\n| Cache efficiency | `cache_hits`,<br /><span class=\"break-all\">`cache_hit_rate`</span> | Monitor hit rate, which drives read latency and cost. Cache hit rate equals <span class=\"break-all\">cache_hits/(cache_hits+cache_misses)</span>. | Count / Ratio (%) |\n| Cache efficiency | <span class=\"break-all\">`endpoint_client_tracking_on_requests`</span>,<br /><span class=\"break-all\">`endpoint_client_tracking_off_requests`</span>,<br /><span class=\"break-all\">`endpoint_disposed_commands_after_client_caching`</span> | Track client-side caching usage and misuse. | Counter |\n| Big / complex keys | <span class=\"break-all\">`redis_server_<data_type>_<size_or_items>_<bucket>`</span> | Monitor oversized keys and cardinality that cause fragmentation, slow replication, and CPU spikes. Track to prevent incidents. Examples:<br /><span class=\"break-all\">`strings_sizes_over_512M`</span>,<br /><span class=\"break-all\">`zsets_items_over_8M`</span> | Gauge |\n| Security – clients | <span class=\"break-all\">`endpoint_client_expiration_refresh`</span>,<br /><span class=\"break-all\">`endpoint_client_establishment_failures`</span> | Monitor unstable clients or problems with authentication or setup. | Counter |\n| Security – LDAP | <span class=\"break-all\">`endpoint_successful_ldap_authentication`</span>,<br /><span class=\"break-all\">`endpoint_failed_ldap_authentication`</span>,<br /><span class=\"break-all\">`endpoint_disconnected_ldap_client`</span> | Monitor authentication health and detect brute-force attacks or misconfigurations. | Counter |\n| Security – cert-based | <span class=\"break-all\">`endpoint_successful_cba_authentication`</span>,<br /><span class=\"break-all\">`endpoint_failed_cba_authentication`</span>,<br /><span class=\"break-all\">`endpoint_disconnected_cba_client`</span> | Monitor certificate authentication status and failures. | Counter |\n| Security – password | <span class=\"break-all\">`endpoint_disconnected_user_password_client`</span> | Monitor password-authentication client disconnects and correlate with policy changes. | Counter |\n| Security – ACL | <span class=\"break-all\">`redis_server_acl_access_denied_auth`</span>,<br /><span class=\"break-all\">`redis_server_acl_access_denied_cmd`</span>,<br /><span class=\"break-all\">`redis_server_acl_access_denied_key`</span>,<br /><span class=\"break-all\">`redis_server_acl_access_denied_channel`</span> | Monitor unauthorized access attempts and incorrectly scoped ACLs. | Counter |\n| Configuration | <span class=\"break-all\">`db_config`</span>| This is an information metric that holds database configuration within labels such as: db_name, db_version, db_port, tls_mode. | counter |"}],"examples":[{"id":"transition-from-prometheus-v1-to-prometheus-v2-ex0","language":"yaml","code":"scrape_configs:\n      # Scrape Redis Software\n      - job_name: redis-enterprise\n        scrape_interval: 30s\n        scrape_timeout: 30s\n        metrics_path: /v2\n        scheme: https\n        tls_config:\n          insecure_skip_verify: true\n        static_configs:\n          - targets: [\"<cluster_name>:8070\"]","section_id":"transition-from-prometheus-v1-to-prometheus-v2"}]}
{"schema_version":2,"id":"operate/rs/monitoring/metrics_stream_engine/local-metrics-storage","title":"Local metrics storage","url":"https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/local-metrics-storage/","summary":"Keep a history of v2 metrics on each Redis Software node and include it in support packages.","tags":["docs","integrate","rs"],"last_updated":"2026-09-29T16:51:25+03:00","page_type":"content","content_hash":"863f333c7e3dc0e947cd259e53b2c5ca20530250f0c55517e91b3a9ae37a453e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Local metrics storage is available as of [Redis Software version 8.2](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-2-releases).\n\nLocal metrics storage is an optional history layer on top of the [v2 metrics stream engine](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine) stored on each node. [Support packages](#support-packages) include this history automatically."},{"id":"tiers","title":"Tiers","role":"content","text":"Local storage collects metrics in two tiers:\n\n- **Standard** collects metrics about every 30 seconds whenever local storage is enabled.\n\n- **Granular** collects metrics every second, but only while you run it. You start and stop it on demand, on one node or on all nodes. Use it for short troubleshooting windows, not for continuous collection. Granular collection stops automatically after a maximum duration if you don't stop it first. Its data stays on disk after collection stops, so support packages still include it. The data is deleted automatically after a cleanup delay. You can also delete it yourself, but only after collection stops on every node you're deleting data from."},{"id":"resource-cost","title":"Resource cost","role":"content","text":"Use a tier only if the node has enough spare CPU and memory.\n\n**Standard** is expected to raise CPU use by about 3–5% and memory use by about 2.5–5.5%. For example:\n\n- If CPU is at about 50%, you can enable standard. CPU is expected to rise to about 52%.\n- If CPU is at about 95%, add headroom first. Otherwise, CPU could reach 98–100%.\n- If you have 2.6 GB of memory used and 1 GB available, you can enable standard. Memory use is expected to rise by about 65–143 MB.\n- If you have 2.6 GB of memory used and 100 MB available, add headroom first. Otherwise, the increase could use up the available memory.\n\n**Granular** is expected to raise CPU use by about 95–125%, which can nearly double it, and memory use by about 4–5.5%. For example:\n\n- If CPU is at about 30%, you can run granular collection. CPU is expected to rise to about 59–68%. The increase lasts only while granular collection runs. After collection stops, CPU is expected to return to its previous level.\n- If CPU is at about 50%, add headroom first. Otherwise, CPU demand could reach 98–113% and saturate the node.\n- If you have 2.6 GB of memory used and 1 GB available, you can run granular collection. Memory use is expected to rise by about 104–143 MB. The increase lasts only while granular collection runs. After collection stops, memory use is expected to return to its previous level.\n- If you have 2.6 GB of memory used and 100 MB available, add headroom first. Otherwise, the increase could use up the available memory."},{"id":"storage-pressure-retention","title":"Storage-pressure retention","role":"content","text":"Each node keeps its stored metrics within its size limit by shortening retention when disk usage runs high. Every minute, the node compares the size of its stored data with `local_storage_max_size_mb`:\n\n- When usage rises above 90% of the limit, the node's effective retention drops by one day, to a minimum of one day.\n- When usage falls below 50% of the limit, effective retention returns to the configured `local_storage_retention_days`.\n\nA busy or undersized node can therefore keep less history than you configured. To compare configured and effective retention for each node, see [metrics status](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/metrics#get-metrics-status)."},{"id":"support-packages","title":"Support packages","role":"compatibility","text":"When local storage is enabled, [support packages](https://redis.io/docs/latest/operate/rs/installing-upgrading/creating-support-package) include each node's stored metrics. You don't need to run a separate command or set a flag, and you can't leave the metrics out.\n\n- **Format**: The metrics are Prometheus-compatible time series database (TSDB) blocks. You can load them into your own Prometheus-compatible system and query them with Prometheus Query Language (PromQL), even if you had no external monitoring in place when the issue happened.\n- **History**: Each node contributes all the metrics it currently retains, up to its effective retention.\n- **Granular data**: Included after collection stops, until the data is deleted.\n\nFor where the metrics are in the package, see [support package files](https://redis.io/docs/latest/operate/rs/installing-upgrading/creating-support-package#node-support-package-files)."},{"id":"manage-local-storage","title":"Manage local storage","role":"content","text":""},{"id":"enable-local-storage","title":"Enable local storage","role":"content","text":"Local storage is disabled by default. To enable it, turn on `metrics_local_storage_service` with the [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/services_configuration) or the [CLI](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/config). The setting applies to every node in the cluster.\n\n- **REST API**: Send a `PUT /v1/cluster/services_configuration` request with the following body:\n\n    [code example]\n\n- **CLI**: Run the following `rladmin` command:\n\n    [code example]"},{"id":"set-the-standard-tiers-size-limit-and-retention","title":"Set the standard tier's size limit and retention","role":"content","text":"To change how much disk space the standard tier uses and how long it keeps metrics, update `local_storage_max_size_mb` and `local_storage_retention_days` with the [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/metrics_config) or the [CLI](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/metrics#metrics-config). The following examples set a 2048 MB limit and 14-day retention.\n\n- **REST API**: Send a `PUT /v1/metrics_config` request with the following body:\n\n    [code example]\n\n- **CLI**: Run the following `rladmin` command:\n\n    [code example]"},{"id":"check-local-storage-status","title":"Check local storage status","role":"content","text":"To check each node's store state, disk usage, and configured and effective retention, use the [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/metrics#get-metrics-status) or the [CLI](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/metrics#metrics-status).\n\n- **REST API**: Send the following request. To check a single node, add `?node_uid=<node-id>`.\n\n    [code example]\n\n- **CLI**: Run the following `rladmin` command. To check a single node, add `node <node-id>`.\n\n    [code example]"},{"id":"start-stop-or-check-granular-collection","title":"Start, stop, or check granular collection","role":"content","text":"To control granular collection, use the [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/metrics/granular) or the [CLI](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/metrics#metrics-granular-start). Each command applies to all nodes unless you specify a node.\n\n- **REST API**: Send one of the following requests. To target a single node, add `?node_uid=<node-id>`.\n\n    [code example]\n\n- **CLI**: Run one of the following `rladmin` commands. To target a single node, add `node <node-id>`.\n\n    [code example]"},{"id":"delete-granular-data","title":"Delete granular data","role":"content","text":"To delete granular data before the cleanup delay passes, use the [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/metrics/granular#delete-granular-data) or the [CLI](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/metrics#metrics-granular-cleanup). Stop granular collection on the affected nodes first.\n\n- **REST API**: Send the following request. To target a single node, add `?node_uid=<node-id>`.\n\n    [code example]\n\n- **CLI**: Run the following `rladmin` command. To target a single node, add `node <node-id>`.\n\n    [code example]"},{"id":"change-the-granular-maximum-duration-and-cleanup-delay","title":"Change the granular maximum duration and cleanup delay","role":"content","text":"To change how long granular collection runs and how long its data is kept after it stops, update [`granular_metrics_job_settings`](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/job_scheduler/granular_metrics_job_settings) with the [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/job_scheduler). Both values are in seconds. The following example sets a 2-hour maximum duration and a 48-hour cleanup delay.\n\n- **REST API**: Send a `PUT /v1/job_scheduler` request with the following body:\n\n    [code example]"},{"id":"get-the-stored-metrics","title":"Get the stored metrics","role":"content","text":"To get the stored metrics, [create a support package](https://redis.io/docs/latest/operate/rs/installing-upgrading/creating-support-package) with the [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/debuginfo) or the [CLI](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/debug_info).\n\n- **REST API**: Send the following request:\n\n    [code example]\n\n- **CLI**: Run the following `rladmin` command:\n\n    [code example]"}],"examples":[{"id":"enable-local-storage-ex0","language":"json","code":"{\n      \"metrics_local_storage_service\": {\n        \"operating_mode\": \"enabled\"\n      }\n    }","section_id":"enable-local-storage"},{"id":"enable-local-storage-ex1","language":"sh","code":"rladmin cluster config services metrics_local_storage_service enabled","section_id":"enable-local-storage"},{"id":"set-the-standard-tiers-size-limit-and-retention-ex0","language":"json","code":"{\n      \"local_storage_max_size_mb\": 2048,\n      \"local_storage_retention_days\": 14\n    }","section_id":"set-the-standard-tiers-size-limit-and-retention"},{"id":"set-the-standard-tiers-size-limit-and-retention-ex1","language":"sh","code":"rladmin metrics config local_storage_max_size_mb 2048 local_storage_retention_days 14","section_id":"set-the-standard-tiers-size-limit-and-retention"},{"id":"check-local-storage-status-ex0","language":"sh","code":"GET /v1/metrics/status","section_id":"check-local-storage-status"},{"id":"check-local-storage-status-ex1","language":"sh","code":"rladmin metrics status","section_id":"check-local-storage-status"},{"id":"start-stop-or-check-granular-collection-ex0","language":"sh","code":"POST /v1/metrics/granular/start\n    POST /v1/metrics/granular/stop\n    GET /v1/metrics/granular/status","section_id":"start-stop-or-check-granular-collection"},{"id":"start-stop-or-check-granular-collection-ex1","language":"sh","code":"rladmin metrics granular start\n    rladmin metrics granular stop\n    rladmin metrics granular status","section_id":"start-stop-or-check-granular-collection"},{"id":"delete-granular-data-ex0","language":"sh","code":"DELETE /v1/metrics/granular/data","section_id":"delete-granular-data"},{"id":"delete-granular-data-ex1","language":"sh","code":"rladmin metrics granular cleanup","section_id":"delete-granular-data"},{"id":"change-the-granular-maximum-duration-and-cleanup-delay-ex0","language":"json","code":"{\n      \"granular_metrics_job_settings\": {\n        \"granular_max_duration\": 7200,\n        \"granular_cleanup_delay\": 172800\n      }\n    }","section_id":"change-the-granular-maximum-duration-and-cleanup-delay"},{"id":"get-the-stored-metrics-ex0","language":"sh","code":"GET /v1/cluster/debuginfo","section_id":"get-the-stored-metrics"},{"id":"get-the-stored-metrics-ex1","language":"sh","code":"rladmin cluster debug_info","section_id":"get-the-stored-metrics"}]}
{"schema_version":2,"id":"operate/rs/monitoring/metrics_stream_engine/metrics-configuration","title":"Metrics configuration","url":"https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/metrics-configuration/","summary":"Configure the Redis Software v2 metrics stream engine.","tags":["docs","integrate","rs"],"last_updated":"2026-09-24T10:14:24+01:00","page_type":"content","content_hash":"81280914c51329beb8049744879d8d58a4264c903b7ee9b3c914f25db868867a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The metrics configuration is a cluster-wide resource that controls the behavior of the [v2 metrics stream engine](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine), such as which optional metrics are collected and how metrics are stored and exported.\n\nFor the full list of configuration fields, their types, and defaults, see the [metrics configuration object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/metrics_config)."},{"id":"configure-metrics-settings","title":"Configure metrics settings","role":"configuration","text":"You can read and update the metrics configuration through the REST API or with `rladmin`.\n\nChanges to the metrics configuration are recorded in the cluster's [event logs](https://redis.io/docs/latest/operate/rs/clusters/logging), so you can review who changed a setting and when."},{"id":"rest-api","title":"REST API","role":"content","text":"Use the `GET` and `PUT` `/v1/metrics_config` requests to read and update the configuration. For request and response details, required permissions, and status codes, see [Metrics configuration requests](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/metrics_config)."},{"id":"rladmin","title":"rladmin","role":"content","text":"Use [`rladmin info metrics`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/info#info-metrics) to view the current configuration and [`rladmin metrics config`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/metrics#metrics-config) to update it."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/monitoring/metrics_stream_engine/prometheus-metrics-v1-to-v2","title":"Transition from Prometheus v1 to Prometheus v2","url":"https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/prometheus-metrics-v1-to-v2/","summary":"Transition from v1 metrics to v2 PromQL equivalents.","tags":["docs","integrate","rs"],"last_updated":"2026-09-24T10:14:24+01:00","page_type":"content","content_hash":"f0357183bb2ba7b810a59f31afb3d7de3c472b629fbe6bc54759812fc9649baa","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can integrate Redis Software with Prometheus and tools such as [Grafana](https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise), [Datadog](https://redis.io/docs/latest/integrate/datadog-with-redis-enterprise), [Dynatrace](https://redis.io/docs/latest/integrate/dynatrace-with-redis-enterprise), or [New Relic](https://redis.io/docs/latest/integrate/new-relic-with-redis-enterprise) to create dashboards for important metrics.\n\nAs of Redis Software version 7.8.2, [PromQL (Prometheus Query Language)](https://prometheus.io/docs/prometheus/latest/querying/basics/) metrics are available. V1 metrics are deprecated but still available.\n\nTo transition from v1 metrics to v2 metrics, you need to change the `metrics_path` in your Prometheus configuration file from `/` to `/v2` to use the new scraping endpoint.\n\nHere's an example of the updated scraping configuration in `prometheus.yml`:\n\n[code example]\n\n> [!NOTE]\n> **Use a single scrape target.** The v2 endpoint is cluster-wide. Every node aggregates metrics from all nodes and returns the same complete result, so one target is enough. If you list one target per node, Prometheus stores every series once per target and multiplies each `sum()`-based dashboard panel by the number of targets. This produces no error. Prometheus reports every target as up and Grafana renders normally. Use your cluster FQDN as the single target so metrics remain available if a node goes down.\n\nThe reason for a single target changed in v2. On v1, only the cluster master served the metrics endpoint and other nodes returned a redirect, so the protocol effectively forced one target. On v2, every node returns the full cluster view and no redirects are involved. If your v1 configuration listed multiple node targets, reduce it to one.\n\nIf you prefer a per-node scrape topology, scrape `/v2/node`, which returns only that node's own metrics. Aggregation adds the `cluster` and `node` labels, so `/v2/node` responses omit them. Add `relabel_configs` to supply those labels before using `/v2/node` with the Redis Software Grafana dashboards.\n\nYou can scrape both v1 and v2 endpoints simultaneously during the transition period to prepare dashboards and ensure a smooth transition.\n\nYou can use the following tables to transition from v1 metrics to equivalent v2 PromQL. For a list of all available v2 metrics, see [Prometheus metrics v2](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/prometheus-metrics-v2)."},{"id":"database-metrics","title":"Database metrics","role":"content","text":"| V1&nbsp;metric | Equivalent V2 PromQL | Description |\n| --------- | :------------------- | :---------- |\n| <span class=\"break-all\">bdb_avg_latency</span> | <span class=\"break-all\">`(sum by (db)(irate(endpoint_read_requests_latency_histogram_sum{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_write_requests_latency_histogram_sum{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_other_requests_latency_histogram_sum{db=\"\"$db\"\"}[$__rate_interval])))/(sum by (db)(irate(endpoint_read_requests{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_write_requests{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_other_requests{db=\"\"$db\"\"}[$__rate_interval])))/1000000`</span> | Average latency of operations on the database (seconds); returned only when there is traffic |\n| <span class=\"break-all\">bdb_avg_latency_max</span> | <span class=\"break-all\">`avg(histogram_quantile(1, sum by (le, db) (irate(endpoint_read_requests_latency_histogram_bucket{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_write_requests_latency_histogram_bucket{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_other_requests_latency_histogram_bucket{db=\"\"$db\"\"}[$__rate_interval])))) / 1000000`</span> | Highest value of average latency of operations on the database (seconds); returned only when there is traffic |\n| <span class=\"break-all\">bdb_avg_read_latency</span> | <span class=\"break-all\">`(sum(irate(endpoint_read_requests_latency_histogram_sum{db=\"\"$db\"\"}[$__rate_interval]))/sum(irate(endpoint_read_requests{db=\"\"$db\"\"}[$__rate_interval])))/1000000`</span> | Average latency of read operations (seconds); returned only when there is traffic |\n| <span class=\"break-all\">bdb_avg_read_latency_max</span></span> | <span class=\"break-all\">`histogram_quantile(1, sum by (le) (irate(endpoint_read_requests_latency_histogram_bucket{db=\"\"$db\"\"}[$__rate_interval]))) / 1000000`</span> | Highest value of average latency of read operations (seconds); returned only when there is traffic |\n| <span class=\"break-all\">bdb_avg_write_latency</span> | <span class=\"break-all\">`(sum(irate(endpoint_write_requests_latency_histogram_sum{db=\"\"$db\"\"}[$__rate_interval]))/sum(irate(endpoint_write_requests{db=\"\"$db\"\"}[$__rate_interval])))/1000000`</span> | Average latency of write operations (seconds); returned only when there is traffic |\n| <span class=\"break-all\">bdb_avg_write_latency_max</span> | <span class=\"break-all\">`histogram_quantile(1, sum by (le) (irate(endpoint_write_requests_latency_histogram_bucket{db=\"\"$db\"\"}[$__rate_interval]))) / 1000000`</span> | Highest value of average latency of write operations (seconds); returned only when there is traffic |\n| <span class=\"break-all\">bdb_bigstore_shard_count</span> | <span class=\"break-all\">`sum((sum(label_replace(label_replace(namedprocess_namegroup_thread_count{groupname=~\"redis-\\d+\", threadname=~\"(speedb\\|rocksdb).*\"}, \"redis\", \"$1\", \"groupname\", \"redis-(\\d+)\"), \"driver\", \"$1\", \"threadname\", \"(speedb\\|rocksdb).*\")) by (redis, driver) > bool 0) * on (redis) group_left(db) redis_server_up) by (db, driver)`</span> | Shard count by database and by storage engine (driver - rocksdb / speedb); Only for databases with Auto Tiering enabled |\n| <span class=\"break-all\">bdb_conns</span> | <span class=\"break-all\">`sum(endpoint_client_connections{cluster=\"$cluster\", db=\"$db\"} - endpoint_client_disconnections{cluster=\"$cluster\", db=\"$db\"} - endpoint_proxy_disconnections{cluster=\"$cluster\", db=\"$db\"})`</span> | Number of client connections to database |\n| <span class=\"break-all\">bdb_egress_bytes</span> | <span class=\"break-all\">`sum by(db) (irate(endpoint_egress{db=\"$db\"}[$__rate_interval]))`</span> | Rate of outgoing network traffic from the database (bytes/sec) |\n| <span class=\"break-all\">bdb_egress_bytes_max</span> | <span class=\"break-all\">`max_over_time (sum by(db) (irate(endpoint_egress{db=\"$db\"}[$__rate_interval]))[$__range:])`</span> | Highest value of the rate of outgoing network traffic from the database (bytes/sec) |\n| <span class=\"break-all\">bdb_evicted_objects</span> | <span class=\"break-all\">`sum by (db) (irate(redis_server_evicted_keys{role=\"master\"}[$__rate_interval]))`</span> | Rate of key evictions from database (evictions/sec) |\n| <span class=\"break-all\">bdb_evicted_objects_max</span> | <span class=\"break-all\">`sum by (db) (irate(redis_server_evicted_keys{role=\"master\"}[$__rate_interval]))`</span> | Highest value of the rate of key evictions from database (evictions/sec) |\n| <span class=\"break-all\">bdb_expired_objects</span> | <span class=\"break-all\">`sum by (db) (irate(redis_server_expired_keys{role=\"master\"}[$__rate_interval]))`</span> | Rate of keys expired in database (expirations/sec) |\n| <span class=\"break-all\">bdb_expired_objects_max</span> | <span class=\"break-all\">`sum by (db) (irate(redis_server_expired_keys{role=\"master\"}[$__rate_interval]))`</span> | Highest value of the rate of keys expired in database (expirations/sec) |\n| <span class=\"break-all\">bdb_fork_cpu_system</span> | <span class=\"break-all\">`sum by (db) (irate(namedprocess_namegroup_thread_cpu_seconds_total{mode=\"system\"}[$__rate_interval]))`</span> | Cores utilization in system mode for all Redis shard fork child processes of this database |\n| <span class=\"break-all\">bdb_fork_cpu_system_max</span> | <span class=\"break-all\">`sum by (db) (irate(namedprocess_namegroup_thread_cpu_seconds_total{mode=\"system\"}[$__rate_interval]))`</span> | Highest value of cores utilization in system mode for all Redis shard fork child processes of this database |\n| <span class=\"break-all\">bdb_fork_cpu_user</span> | <span class=\"break-all\">`sum by (db) (irate(namedprocess_namegroup_thread_cpu_seconds_total{mode=\"user\"}[$__rate_interval]))`</span> | Cores utilization in user mode for all Redis shard fork child processes of this database |\n| <span class=\"break-all\">bdb_fork_cpu_user_max</span> | <span class=\"break-all\">`sum by (db) (irate(namedprocess_namegroup_thread_cpu_seconds_total{mode=\"user\"}[$__rate_interval]))`</span> | Highest value of cores utilization in user mode for all Redis shard fork child processes of this database |\n| <span class=\"break-all\">bdb_ingress_bytes</span> | <span class=\"break-all\">`sum by(db) (irate(endpoint_ingress{db=\"$db\"}[$__rate_interval]))`</span> | Rate of incoming network traffic to database (bytes/sec) |\n| <span class=\"break-all\">bdb_ingress_bytes_max</span> | <span class=\"break-all\">`max_over_time (sum by(db) (irate(endpoint_ingress{db=\"$db\"}[$__rate_interval]))[$__range:])`</span> | Highest value of the rate of incoming network traffic to database (bytes/sec) |\n| <span class=\"break-all\">bdb_instantaneous_ops_per_sec</span></span> | <span class=\"break-all\">`sum by(db) (redis_server_instantaneous_ops_per_sec)`</span> | Request rate handled by all shards of database (ops/sec) |\n| <span class=\"break-all\">bdb_main_thread_cpu_system</span> | <span class=\"break-all\">`sum by(db) (irate(namedprocess_namegroup_thread_cpu_seconds_total{mode=\"system\", threadname=~\"redis-server.*\"}[$__rate_interval]))`</span> | Cores utilization in system mode for all Redis shard main threads of this database |\n| <span class=\"break-all\">bdb_main_thread_cpu_system_max</span></span> | <span class=\"break-all\">`sum by(db) (irate(namedprocess_namegroup_thread_cpu_seconds_total{mode=\"system\", threadname=~\"redis-server.*\"}[$__rate_interval]))`</span> | Highest value of cores utilization in system mode for all Redis shard main threads of this database |\n| <span class=\"break-all\">bdb_main_thread_cpu_user</span> | <span class=\"break-all\">`sum by(irate(namedprocess_namegroup_thread_cpu_seconds_total{mode=\"user\", threadname=~\"redis-server.*\"}[$__rate_interval]))`</span> | Cores utilization in user mode for all Redis shard main threads of this database |\n| <span class=\"break-all\">bdb_main_thread_cpu_user_max</span></span> | <span class=\"break-all\">`sum by(irate(namedprocess_namegroup_thread_cpu_seconds_total{mode=\"user\", threadname=~\"redis-server.*\"}[$__rate_interval]))`</span> | Highest value of cores utilization in user mode for all Redis shard main threads of this database |\n| <span class=\"break-all\">bdb_mem_frag_ratio</span> | <span class=\"break-all\">`avg(redis_server_mem_fragmentation_ratio)`</span> | RAM fragmentation ratio (RSS / allocated RAM) |\n| <span class=\"break-all\">bdb_mem_size_lua</span> | <span class=\"break-all\">`sum by(db) (redis_server_used_memory_lua)`</span> | Redis lua scripting heap size (bytes) |\n| <span class=\"break-all\">bdb_memory_limit</span> | <span class=\"break-all\">`db_memory_limit_bytes`</span> | Configured RAM limit for the database |\n| <span class=\"break-all\">bdb_monitor_sessions_count</span> | <span class=\"break-all\">`sum by(db) (endpoint_monitor_sessions_count)`</span> | Number of clients connected in monitor mode to the database |\n| <span class=\"break-all\">bdb_no_of_keys</span> | <span class=\"break-all\">`sum by (db) (redis_server_db_keys{role=\"master\"})`</span> | Number of keys in database |\n| <span class=\"break-all\">bdb_other_req</span> | <span class=\"break-all\">`sum by(db) (irate(endpoint_other_requests{db=\"$db\"}[$__rate_interval]))`</span> | Rate of other (non read/write) requests on the database (ops/sec) |\n| <span class=\"break-all\">bdb_other_req_max</span> | <span class=\"break-all\">`max_over_time (sum by(db) (irate(endpoint_other_requests{db=\"$db\"}[$__rate_interval]))[$__range:])`</span> | Highest value of the rate of other (non read/write) requests on the database (ops/sec) |\n| <span class=\"break-all\">bdb_other_res</span> | <span class=\"break-all\">`sum by(db) (irate(endpoint_other_responses{db=\"$db\"}[$__rate_interval]))`</span> | Rate of other (non read/write) responses on the database (ops/sec) |\n| <span class=\"break-all\">bdb_other_res_max</span> | <span class=\"break-all\">`max_over_time (sum by(db) (irate(endpoint_other_responses{db=\"$db\"}[$__rate_interval]))[$__range:])`</span> | Highest value of the rate of other (non read/write) responses on the database (ops/sec) |\n| <span class=\"break-all\">bdb_pubsub_channels</span> | <span class=\"break-all\">`sum by(db) (redis_server_pubsub_channels)`</span> | Count the pub/sub channels with subscribed clients |\n| <span class=\"break-all\">bdb_pubsub_channels_max</span> | <span class=\"break-all\">`sum by(db) (redis_server_pubsub_channels)`</span> | Highest value of count the pub/sub channels with subscribed clients |\n| <span class=\"break-all\">bdb_pubsub_patterns</span> | <span class=\"break-all\">`sum by(db) (redis_server_pubsub_patterns)`</span> | Count the pub/sub patterns with subscribed clients |\n| <span class=\"break-all\">bdb_pubsub_patterns_max</span></span> | <span class=\"break-all\">`sum by(db) (redis_server_pubsub_patterns)`</span> | Highest value of count the pub/sub patterns with subscribed clients |\n| <span class=\"break-all\">bdb_read_hits</span> | <span class=\"break-all\">`sum by (db) (irate(redis_server_keyspace_read_hits{role=\"master\"}[$__rate_interval]))`</span> | Rate of read operations accessing an existing key (ops/sec) |\n| <span class=\"break-all\">bdb_read_hits_max</span> | <span class=\"break-all\">`sum by (db) (irate(redis_server_keyspace_read_hits{role=\"master\"}[$__rate_interval]))`</span> | Highest value of the rate of read operations accessing an existing key (ops/sec) |\n| <span class=\"break-all\">bdb_read_misses</span> | <span class=\"break-all\">`sum by (db) (irate(redis_server_keyspace_read_misses{role=\"master\"}[$__rate_interval]))`</span> | Rate of read operations accessing a non-existing key (ops/sec) |\n| <span class=\"break-all\">bdb_read_misses_max</span> | <span class=\"break-all\">`sum by (db) (irate(redis_server_keyspace_read_misses{role=\"master\"}[$__rate_interval]))`</span> | Highest value of the rate of read operations accessing a non-existing key (ops/sec) |\n| <span class=\"break-all\">bdb_read_req</span> | <span class=\"break-all\">`sum by (db) (irate(endpoint_read_requests{db=\"$db\"}[$__rate_interval]))`</span> | Rate of read requests on the database (ops/sec) |\n| <span class=\"break-all\">bdb_read_req_max</span> | <span class=\"break-all\">`max_over_time (sum by(db) (irate(endpoint_read_requests{db=\"$db\"}[$__rate_interval]))[$__range:])`</span> | Highest value of the rate of read requests on the database (ops/sec) |\n| <span class=\"break-all\">bdb_read_res</span> | <span class=\"break-all\">`sum by (db) (irate(endpoint_read_responses{\"$db\"}[$__rate_interval]))`</span> | Rate of read responses on the database (ops/sec) |\n| <span class=\"break-all\">bdb_read_res_max</span> | <span class=\"break-all\">`max_over_time (sum by(db) (irate(endpoint_read_responses{db=\"$db\"}[$__rate_interval]))[$__range:])`</span> | Highest value of the rate of read responses on the database (ops/sec) |\n| <span class=\"break-all\">bdb_shard_cpu_system</span> | <span class=\"break-all\">`sum by(db) (irate(namedprocess_namegroup_thread_cpu_seconds_total{mode=\"system\", role=\"master\"}[$__rate_interval]))`</span> | Cores utilization in system mode for all Redis shard processes of this database |\n| <span class=\"break-all\">bdb_shard_cpu_system_max<span></span> | <span class=\"break-all\">`sum by(db) (irate(namedprocess_namegroup_thread_cpu_seconds_total{mode=\"system\", role=\"master\"}[$__rate_interval]))`</span> | Highest value of cores utilization in system mode for all Redis shard processes of this database |\n| <span class=\"break-all\">bdb_shard_cpu_user</span> | <span class=\"break-all\">`sum by(db) (irate(namedprocess_namegroup_thread_cpu_seconds_total{mode=\"user\", role=\"master\"}[$__rate_interval]))`</span> | Cores utilization in user mode for the Redis shard process |\n| <span class=\"break-all\">bdb_shard_cpu_user_max</span> | <span class=\"break-all\">`sum by(db) (irate(namedprocess_namegroup_thread_cpu_seconds_total{mode=\"user\", role=\"master\"}[$__rate_interval]))`</span> | Highest value of cores utilization in user mode for the Redis shard process |\n| <span class=\"break-all\">bdb_shards_used</span> | <span class=\"break-all\">`sum((sum(label_replace(label_replace(label_replace(namedprocess_namegroup_thread_count{groupname=~\"redis-\\d+\"}, \"redis\", \"$1\", \"groupname\", \"redis-(\\d+)\"), \"shard_type\", \"flash\", \"threadname\", \"(bigstore).*\"), \"shard_type\", \"ram\", \"shard_type\", \"\")) by (redis, shard_type) > bool 0) * on (redis) group_left(db) redis_server_up) by (db, shard_type)`</span> | Used shard count by database and by shard type (ram / flash) |\n| <span class=\"break-all\">bdb_total_connections_received</span> | <span class=\"break-all\">`sum by(db) (irate(endpoint_client_connections{db=\"$db\"}[$__rate_interval]))`</span> | Rate of new client connections to database (connections/sec) |\n| <span class=\"break-all\">bdb_total_connections_received_max<span></span> | <span class=\"break-all\">`max_over_time (sum by(db) (irate(endpoint_client_connections{db=\"$db\"}[$__rate_interval]))[$__range:])`</span> | Highest value of the rate of new client connections to database (connections/sec) |\n| <span class=\"break-all\">bdb_total_req</span> | <span class=\"break-all\">`sum by (db) (irate(endpoint_read_requests{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_write_requests{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_other_requests{db=\"\"$db\"\"}[$__rate_interval]))`</span> | Rate of all requests on the database (ops/sec) |\n| <span class=\"break-all\">bdb_total_req_max</span> | <span class=\"break-all\">`max_over_time(sum by (db) (irate(endpoint_read_requests{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_write_requests{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_other_requests{db=\"\"$db\"\"}[$__rate_interval])) [$__range:])`</span> | Highest value of the rate of all requests on the database (ops/sec) |\n| <span class=\"break-all\">bdb_total_res</span> | <span class=\"break-all\">`sum by (db) (irate(endpoint_read_responses{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_write_responses{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_other_responses{db=\"\"$db\"\"}[$__rate_interval]))`</span> | Rate of all responses on the database (ops/sec) |\n| <span class=\"break-all\">bdb_total_res_max</span> | <span class=\"break-all\">`max_over_time(sum by (db) (irate(endpoint_read_responses{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_write_responses{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_other_responses{db=\"\"$db\"\"}[$__rate_interval])) [$__range:])`</span> | Highest value of the rate of all responses on the database (ops/sec) |\n| <span class=\"break-all\">bdb_up</span> | <span class=\"break-all\">`min by(db) (redis_server_up)`</span> | Database is up and running |\n| <span class=\"break-all\">bdb_used_memory</span> | <span class=\"break-all\">`sum by (db) (redis_server_used_memory)`</span> | Memory used by database (in BigRedis this includes flash) (bytes) |\n| <span class=\"break-all\">bdb_write_hits</span> | <span class=\"break-all\">`sum by (db) (irate(redis_server_keyspace_write_hits{role=\"master\"}[$__rate_interval]))`</span> | Rate of write operations accessing an existing key (ops/sec) |\n| <span class=\"break-all\">bdb_write_hits_max</span> | <span class=\"break-all\">`sum by (db) (irate(redis_server_keyspace_write_hits{role=\"master\"}[$__rate_interval]))`</span> | Highest value of the rate of write operations accessing an existing key (ops/sec) |\n| <span class=\"break-all\">bdb_write_misses</span> | <span class=\"break-all\">`sum by (db) (irate(redis_server_keyspace_write_misses{role=\"master\"}[$__rate_interval]))`</span> | Rate of write operations accessing a non-existing key (ops/sec) |\n| <span class=\"break-all\">bdb_write_misses_max</span> | <span class=\"break-all\">`sum by (db) (irate(redis_server_keyspace_write_misses{role=\"master\"}[$__rate_interval]))`</span> | Highest value of the rate of write operations accessing a non-existing key (ops/sec) |\n| <span class=\"break-all\">bdb_write_req</span> | <span class=\"break-all\">`sum by (db) (irate(endpoint_write_requests{db=\"$db\"}[$__rate_interval]))`</span> | Rate of write requests on the database (ops/sec) |\n| <span class=\"break-all\">bdb_write_req_max</span> | <span class=\"break-all\">`max_over_time(sum by (db) (irate(endpoint_write_requests{db=\"$db\"}[$__rate_interval]))[$__range:])`</span> | Highest value of the rate of write requests on the database (ops/sec) |\n| <span class=\"break-all\">bdb_write_res</span> | <span class=\"break-all\">`sum by(db) (irate(endpoint_write_responses{db=\"$db\"}[$__rate_interval]))`</span> | Rate of write responses on the database (ops/sec) |\n| <span class=\"break-all\">bdb_write_res_max</span> | <span class=\"break-all\">`max_over_time(sum by (db) (irate(endpoint_write_responses{db=\"$db\"}[$__rate_interval]))[$__range:])`</span> | Highest value of the rate of write responses on the database (ops/sec) |\n| <span class=\"break-all\">bdb_no_of_expires</span> | <span class=\"break-all\">`sum by(db) (redis_server_db_expires{role=\"master\"})`</span> | Current number of volatile keys in the database |"},{"id":"node-metrics","title":"Node metrics","role":"content","text":"| V1&nbsp;metric | Equivalent V2 PromQL | Description |\n| --------- | :------------------- | :---------- |\n| <span class=\"break-all\">node_available_flash</span> | <span class=\"break-all\">`node_available_flash_bytes`</span> | Available flash in the node (bytes) |\n| <span class=\"break-all\">node_available_flash_no_overbooking</span> | <span class=\"break-all\">`node_available_flash_no_overbooking_bytes`</span> | Available flash in the node (bytes), without taking into account overbooking |\n| <span class=\"break-all\">node_available_memory</span> | <span class=\"break-all\">`node_available_memory_bytes`</span> | Amount of free memory in the node (bytes) that is available for database provisioning |\n| <span class=\"break-all\">node_available_memory_no_overbooking</span> | <span class=\"break-all\">`node_available_memory_no_overbooking_bytes`</span> | Available RAM in the node (bytes) without taking into account overbooking |\n| <span class=\"break-all\">node_avg_latency</span> | <span class=\"break-all\">`sum by (proxy) (irate(endpoint_acc_latency[$__rate_interval])) / sum by (proxy) (irate(endpoint_total_started_res[$__rate_interval]))`</span> | Average latency of requests handled by endpoints on the node in microseconds; returned only when there is traffic |\n| <span class=\"break-all\">node_bigstore_free</span> | <span class=\"break-all\">`node_bigstore_free_bytes`</span> | Sum of free space of back-end flash (used by flash database's [BigRedis]) on all cluster nodes (bytes); returned only when BigRedis is enabled |\n| <span class=\"break-all\">node_bigstore_iops</span> | <span class=\"break-all\">`node_flash_reads_total + node_flash_writes_total`</span> | Rate of I/O operations against back-end flash for all shards which are part of a flash-based database (BigRedis) in the cluster (ops/sec); returned only when BigRedis is enabled |\n| <span class=\"break-all\">node_bigstore_kv_ops</span> | <span class=\"break-all\">`sum by (node) (irate(redis_server_big_io_dels[$__rate_interval]) + irate(redis_server_big_io_reads[$__rate_interval]) + irate(redis_server_big_io_writes[$__rate_interval]))`</span> | Rate of value read/write operations against back-end flash for all shards which are part of a flash-based database (BigRedis) in the cluster (ops/sec); returned only when BigRedis is enabled |\n| <span class=\"break-all\">node_bigstore_throughput</span> | <span class=\"break-all\">`sum by (node) (irate(redis_server_big_io_read_bytes[$__rate_interval]) + irate(redis_server_big_io_write_bytes[$__rate_interval]))`</span> | Throughput I/O operations against back-end flash for all shards which are part of a flash-based database (BigRedis) in the cluster (bytes/sec); returned only when BigRedis is enabled |\n| <span class=\"break-all\">node_cert_expiration_seconds</span> | <span class=\"break-all\">`node_cert_expires_in_seconds`</span> | Certificate expiration (in seconds) per given node; read more about [certificates in Redis Software](https://redis.io/docs/latest/operate/rs/security/certificates) and [monitoring certificates](https://redis.io/docs/latest/operate/rs/security/certificates/monitor-certificates) |\n| <span class=\"break-all\">node_conns</span> | <span class=\"break-all\">`sum by (node) (endpoint_client_connections)`</span> | Number of clients connected to endpoints on node |\n| <span class=\"break-all\">node_cpu_idle</span> | <span class=\"break-all\">`avg by (node) (irate(node_cpu_seconds_total{mode=\"idle\"}[$__rate_interval]))`</span> | CPU idle time portion (0-1, multiply by 100 to get percent) |\n| <span class=\"break-all\">node_cpu_idle_max</span> | <span class=\"break-all\">N/A</span> | Highest value of CPU idle time portion (0-1, multiply by 100 to get percent) |\n| <span class=\"break-all\">node_cpu_idle_median</span> | <span class=\"break-all\">N/A</span> | Median value of CPU idle time portion (0-1, multiply by 100 to get percent) |\n| <span class=\"break-all\">node_cpu_idle_min</span> | <span class=\"break-all\">N/A</span> | Lowest value of CPU idle time portion (0-1, multiply by 100 to get percent) |\n| <span class=\"break-all\">node_cpu_system</span> | <span class=\"break-all\">`avg by (node) (irate(node_cpu_seconds_total{mode=\"system\"}[$__rate_interval]))`</span> | CPU time portion spent in the kernel (0-1, multiply by 100 to get percent) |\n| <span class=\"break-all\">node_cpu_system_max</span> | <span class=\"break-all\">N/A</span> | Highest value of CPU time portion spent in the kernel (0-1, multiply by 100 to get percent) |\n| <span class=\"break-all\">node_cpu_system_median</span> | <span class=\"break-all\">N/A</span> | Median value of CPU time portion spent in the kernel (0-1, multiply by 100 to get percent) |\n| <span class=\"break-all\">node_cpu_system_min</span> | <span class=\"break-all\">N/A</span> | Lowest value of CPU time portion spent in the kernel (0-1, multiply by 100 to get percent) |\n| <span class=\"break-all\">node_cpu_user</span> | <span class=\"break-all\">`avg by (node) (irate(node_cpu_seconds_total{mode=\"user\"}[$__rate_interval]))`</span> | CPU time portion spent by user-space processes (0-1, multiply by 100 to get percent) |\n| <span class=\"break-all\">node_cpu_user_max</span> | <span class=\"break-all\">N/A</span> | Highest value of CPU time portion spent by user-space processes (0-1, multiply by 100 to get percent) |\n| <span class=\"break-all\">node_cpu_user_median</span> | <span class=\"break-all\">N/A</span> | Median value of CPU time portion spent by user-space processes (0-1, multiply by 100 to get percent) |\n| <span class=\"break-all\">node_cpu_user_min</span> | <span class=\"break-all\">N/A</span> | Lowest value of CPU time portion spent by user-space processes (0-1, multiply by 100 to get percent) |\n| <span class=\"break-all\">node_cur_aof_rewrites</span> | <span class=\"break-all\">`sum by (cluster, node) (redis_server_aof_rewrite_in_progress)`</span> | Number of AOF rewrites that are currently performed by shards on this node |\n| <span class=\"break-all\">node_egress_bytes</span> | <span class=\"break-all\">`irate(node_network_transmit_bytes_total{device=\"<interface>\"}[$__rate_interval])`</span> | Rate of outgoing network traffic to node (bytes/sec) |\n| <span class=\"break-all\">node_egress_bytes_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of outgoing network traffic to node (bytes/sec) |\n| <span class=\"break-all\">node_egress_bytes_median</span> | <span class=\"break-all\">N/A</span> | Median value of the rate of outgoing network traffic to node (bytes/sec) |\n| <span class=\"break-all\">node_egress_bytes_min</span> | <span class=\"break-all\">N/A</span> | Lowest value of the rate of outgoing network traffic to node (bytes/sec) |\n| <span class=\"break-all\">node_ephemeral_storage_avail</span> | <span class=\"break-all\">`node_ephemeral_storage_avail_bytes`</span> | Disk space available to RLEC processes on configured ephemeral disk (bytes) |\n| <span class=\"break-all\">node_ephemeral_storage_free</span> | <span class=\"break-all\">`node_ephemeral_storage_free_bytes`</span> | Free disk space on configured ephemeral disk (bytes) |\n| <span class=\"break-all\">node_free_memory</span> | <span class=\"break-all\">`node_memory_MemFree_bytes`</span> | Free memory in the node (bytes) |\n| <span class=\"break-all\">node_ingress_bytes</span> | <span class=\"break-all\">`irate(node_network_receive_bytes_total{device=\"<interface>\"}[$__rate_interval])`</span> | Rate of incoming network traffic to node (bytes/sec) |\n| <span class=\"break-all\">node_ingress_bytes_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of incoming network traffic to node (bytes/sec) |\n| <span class=\"break-all\">node_ingress_bytes_median</span> | <span class=\"break-all\">N/A</span> | Median value of the rate of incoming network traffic to node (bytes/sec) |\n| <span class=\"break-all\">node_ingress_bytes_min</span> | <span class=\"break-all\">N/A</span> | Lowest value of the rate of incoming network traffic to node (bytes/sec) |\n| <span class=\"break-all\">node_persistent_storage_avail</span> | <span class=\"break-all\">`node_persistent_storage_avail_bytes`</span> | Disk space available to RLEC processes on configured persistent disk (bytes) |\n| <span class=\"break-all\">node_persistent_storage_free</span> | <span class=\"break-all\">`node_persistent_storage_free_bytes`</span> | Free disk space on configured persistent disk (bytes) |\n| <span class=\"break-all\">node_provisional_flash</span> | <span class=\"break-all\">`node_provisional_flash_bytes`</span> | Amount of flash available for new shards on this node, taking into account overbooking, max Redis servers, reserved flash, and provision and migration thresholds (bytes) |\n| <span class=\"break-all\">node_provisional_flash_no_overbooking</span> | <span class=\"break-all\">`node_provisional_flash_no_overbooking_bytes`</span> | Amount of flash available for new shards on this node, without taking into account overbooking, max Redis servers, reserved flash, and provision and migration thresholds (bytes) |\n| <span class=\"break-all\">node_provisional_memory</span> | <span class=\"break-all\">`node_provisional_memory_bytes`</span> | Amount of RAM that is available for provisioning to databases out of the total RAM allocated for databases |\n| <span class=\"break-all\">node_provisional_memory_no_overbooking</span> | <span class=\"break-all\">`node_provisional_memory_no_overbooking_bytes`</span> | Amount of RAM that is available for provisioning to databases out of the total RAM allocated for databases, without taking into account overbooking |\n| <span class=\"break-all\">node_total_req</span> | <span class=\"break-all\">`sum by (cluster, node) (irate(endpoint_read_requests[$__rate_interval]) +  irate(endpoint_write_requests[$__rate_interval]) +  irate(endpoint_other_requests[$__rate_interval]) )`</span> | Request rate handled by endpoints on node (ops/sec) |\n| <span class=\"break-all\">node_up</span> | <span class=\"break-all\">`node_metrics_up`</span> | Node is part of the cluster and is connected |"},{"id":"cluster-metrics","title":"Cluster metrics","role":"content","text":"| V1&nbsp;metric | Equivalent V2 PromQL | Description |\n| --------- | :------------------- | :---------- |\n| cluster_shards_limit | `license_shards_limit` | Total shard limit by the license by shard type (ram / flash) |"},{"id":"proxy-metrics","title":"Proxy metrics","role":"content","text":"| V1&nbsp;metric | Equivalent V2 PromQL | Description |\n| --------- | :------------------- | :---------- |\n| <span class=\"break-all\">listener_acc_latency</span> | <span class=\"break-all\">N/A</span> | Accumulative latency (sum of the latencies) of all types of commands on the database. For the average latency, divide this value by listener_total_res |\n| <span class=\"break-all\">listener_acc_latency_max</span> | <span class=\"break-all\">N/A</span> | Highest value of accumulative latency of all types of commands on the database |\n| <span class=\"break-all\">listener_acc_other_latency</span> | <span class=\"break-all\">N/A</span> | Accumulative latency (sum of the latencies) of commands that are a type \"other\" on the database. For the average latency, divide this value by listener_other_res |\n| <span class=\"break-all\">listener_acc_other_latency_max</span> | <span class=\"break-all\">N/A</span> | Highest value of accumulative latency of commands that are a type \"other\" on the database |\n| <span class=\"break-all\">listener_acc_read_latency</span> | <span class=\"break-all\">N/A</span> | Accumulative latency (sum of the latencies) of commands that are a type \"read\" on the database. For the average latency, divide this value by listener_read_res |\n| <span class=\"break-all\">listener_acc_read_latency_max</span> | <span class=\"break-all\">N/A</span> | Highest value of accumulative latency of commands that are a type \"read\" on the database |\n| <span class=\"break-all\">listener_acc_write_latency</span> | <span class=\"break-all\">N/A</span> | Accumulative latency (sum of the latencies) of commands that are a type \"write\" on the database. For the average latency, divide this value by listener_write_res |\n| <span class=\"break-all\">listener_acc_write_latency_max</span> | <span class=\"break-all\">N/A</span> | Highest value of accumulative latency of commands that are a type \"write\" on the database |\n| <span class=\"break-all\">listener_auth_cmds</span> | <span class=\"break-all\">N/A</span> | Number of memcached AUTH commands sent to the database |\n| <span class=\"break-all\">listener_auth_cmds_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the number of memcached AUTH commands sent to the database |\n| <span class=\"break-all\">listener_auth_errors</span> | <span class=\"break-all\">N/A</span> | Number of error responses to memcached AUTH commands |\n| <span class=\"break-all\">listener_auth_errors_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the number of error responses to memcached AUTH commands |\n| <span class=\"break-all\">listener_cmd_flush</span> | <span class=\"break-all\">N/A</span> | Number of memcached FLUSH_ALL commands sent to the database |\n| <span class=\"break-all\">listener_cmd_flush_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the number of memcached FLUSH_ALL commands sent to the database |\n| <span class=\"break-all\">listener_cmd_get</span> | <span class=\"break-all\">N/A</span> | Number of memcached GET commands sent to the database |\n| <span class=\"break-all\">listener_cmd_get_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the number of memcached GET commands sent to the database |\n| <span class=\"break-all\">listener_cmd_set</span> | <span class=\"break-all\">N/A</span> | Number of memcached SET commands sent to the database |\n| <span class=\"break-all\">listener_cmd_set_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the number of memcached SET commands sent to the database |\n| <span class=\"break-all\">listener_cmd_touch</span> | <span class=\"break-all\">N/A</span> | Number of memcached TOUCH commands sent to the database |\n| <span class=\"break-all\">listener_cmd_touch_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the number of memcached TOUCH commands sent to the database |\n| <span class=\"break-all\">listener_conns</span> | <span class=\"break-all\">N/A</span> | Number of clients connected to the endpoint |\n| <span class=\"break-all\">listener_egress_bytes</span> | <span class=\"break-all\">N/A</span> | Rate of outgoing network traffic to the endpoint (bytes/sec) |\n| <span class=\"break-all\">listener_egress_bytes_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of outgoing network traffic to the endpoint (bytes/sec) |\n| <span class=\"break-all\">listener_ingress_bytes</span> | <span class=\"break-all\">N/A</span> | Rate of incoming network traffic to the endpoint (bytes/sec) |\n| <span class=\"break-all\">listener_ingress_bytes_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of incoming network traffic to the endpoint (bytes/sec) |\n| <span class=\"break-all\">listener_last_req_time</span> | <span class=\"break-all\">N/A</span> | Time of last command sent to the database |\n| <span class=\"break-all\">listener_last_res_time</span> | <span class=\"break-all\">N/A</span> | Time of last response sent from the database |\n| <span class=\"break-all\">listener_max_connections_exceeded</span> | <span class=\"break-all\">`irate(endpoint_maximal_connections_exceeded[$__rate_interval])`</span> | Number of times the number of clients connected to the database at the same time has exceeded the max limit |\n| <span class=\"break-all\">listener_max_connections_exceeded_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the number of times the number of clients connected to the database at the same time has exceeded the max limit |\n| <span class=\"break-all\">listener_monitor_sessions_count</span> | <span class=\"break-all\">N/A</span> | Number of clients connected in monitor mode to the endpoint |\n| <span class=\"break-all\">listener_other_req</span> | <span class=\"break-all\">N/A</span> | Rate of other (non-read/write) requests on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_other_req_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of other (non-read/write) requests on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_other_res</span> | <span class=\"break-all\">N/A</span> | Rate of other (non-read/write) responses on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_other_res_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of other (non-read/write) responses on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_other_started_res</span> | <span class=\"break-all\">N/A</span> | Number of responses sent from the database of type \"other\" |\n| <span class=\"break-all\">listener_other_started_res_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the number of responses sent from the database of type \"other\" |\n| <span class=\"break-all\">listener_read_req</span> | <span class=\"break-all\">`irate(endpoint_read_requests[$__rate_interval])`</span> | Rate of read requests on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_read_req_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of read requests on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_read_res</span> | <span class=\"break-all\">`irate(endpoint_read_responses[$__rate_interval])`</span> | Rate of read responses on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_read_res_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of read responses on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_read_started_res</span> | <span class=\"break-all\">N/A</span> | Number of responses sent from the database of type \"read\" |\n| <span class=\"break-all\">listener_read_started_res_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the number of responses sent from the database of type \"read\" |\n| <span class=\"break-all\">listener_total_connections_received</span> | <span class=\"break-all\">`irate(endpoint_total_connections_received[$__rate_interval])`</span> | Rate of new client connections to the endpoint (connections/sec) |\n| <span class=\"break-all\">listener_total_connections_received_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of new client connections to the endpoint (connections/sec) |\n| <span class=\"break-all\">listener_total_req</span> | <span class=\"break-all\">N/A</span> | Request rate handled by the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_total_req_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of all requests on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_total_res</span> | <span class=\"break-all\">N/A</span> | Rate of all responses on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_total_res_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of all responses on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_total_started_res</span> | <span class=\"break-all\">N/A</span> | Number of responses sent from the database of all types |\n| <span class=\"break-all\">listener_total_started_res_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the number of responses sent from the database of all types |\n| <span class=\"break-all\">listener_write_req</span> | <span class=\"break-all\">`irate(endpoint_write_requests[$__rate_interval])`</span> | Rate of write requests on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_write_req_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of write requests on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_write_res</span> | <span class=\"break-all\">`irate(endpoint_write_responses[$__rate_interval])`</span> | Rate of write responses on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_write_res_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of write responses on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_write_started_res</span> | <span class=\"break-all\">N/A</span> | Number of responses sent from the database of type \"write\" |\n| <span class=\"break-all\">listener_write_started_res_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the number of responses sent from the database of type \"write\" |"},{"id":"replication-metrics","title":"Replication metrics","role":"content","text":"| V1&nbsp;metric | Equivalent V2 PromQL | Description |\n| --------- | :------------------- | :---------- |\n| <span class=\"break-all\">bdb_replicaof_syncer_ingress_bytes</span> | <span class=\"break-all\">`rate(replica_src_ingress_bytes[$__rate_interval])`</span> | Rate of compressed incoming network traffic to a Replica Of database (bytes/sec) |\n| <span class=\"break-all\">bdb_replicaof_syncer_ingress_bytes_decompressed</span> | <span class=\"break-all\">`rate(replica_src_ingress_bytes_decompressed[$__rate_interval])`</span> | Rate of decompressed incoming network traffic to a Replica Of database (bytes/sec) |\n| <span class=\"break-all\">bdb_replicaof_syncer_local_ingress_lag_time</span> | <span class=\"break-all\">`database_syncer_lag_ms{syncer_type=\"replicaof\"}`</span> | Lag time between the source and the destination for Replica Of traffic (ms) |\n| <span class=\"break-all\">bdb_replicaof_syncer_status</span> | <span class=\"break-all\">`database_syncer_current_status{syncer_type=\"replicaof\"}`</span> | Syncer status for Replica Of traffic; 0 = in-sync, 1 = syncing, 2 = out of sync |\n| <span class=\"break-all\">bdb_crdt_syncer_ingress_bytes</span> | <span class=\"break-all\">`rate(crdt_src_ingress_bytes[$__rate_interval])`</span> | Rate of compressed incoming network traffic to CRDB (bytes/sec) |\n| <span class=\"break-all\">bdb_crdt_syncer_ingress_bytes_decompressed</span> | <span class=\"break-all\">`rate(crdt_src_ingress_bytes_decompressed[$__rate_interval])`</span> | Rate of decompressed incoming network traffic to CRDB (bytes/sec) |\n| <span class=\"break-all\">bdb_crdt_syncer_local_ingress_lag_time</span> | <span class=\"break-all\">`database_syncer_lag_ms{syncer_type=\"crdt\"}`</span> | Lag time between the source and the destination (ms) for CRDB traffic |\n| <span class=\"break-all\">bdb_crdt_syncer_status</span> | <span class=\"break-all\">`database_syncer_current_status{syncer_type=\"crdt\"}`</span> | Syncer status for CRDB traffic; 0 = in-sync, 1 = syncing, 2 = out of sync |"},{"id":"shard-metrics","title":"Shard metrics","role":"content","text":"| V1&nbsp;metric | Equivalent V2 PromQL | Description |\n| --------- | :------------------- | :---------- |\n| <span class=\"break-all\">redis_active_defrag_running</span> | <span class=\"break-all\">`redis_server_active_defrag_running`</span> | Automatic memory defragmentation current aggressiveness (% cpu) |\n| <span class=\"break-all\">redis_allocator_active</span> | <span class=\"break-all\">`redis_server_allocator_active`</span> | Total used memory, including external fragmentation |\n| <span class=\"break-all\">redis_allocator_allocated</span> | <span class=\"break-all\">`redis_server_allocator_allocated`</span> | Total allocated memory |\n| <span class=\"break-all\">redis_allocator_resident</span> | <span class=\"break-all\">`redis_server_allocator_resident`</span> | Total resident memory (RSS) |\n| <span class=\"break-all\">redis_aof_last_cow_size</span> | <span class=\"break-all\">`redis_server_aof_last_cow_size`</span> | Last AOFR, CopyOnWrite memory |\n| <span class=\"break-all\">redis_aof_rewrite_in_progress</span> | <span class=\"break-all\">`redis_server_aof_rewrite_in_progress`</span> | Indicates whether an AOF rewrite is in progress (1 = in progress, 0 = otherwise) |\n| <span class=\"break-all\">redis_aof_rewrites</span> | <span class=\"break-all\">`redis_server_aof_rewrites`</span> | Number of AOF rewrites this process executed |\n| <span class=\"break-all\">redis_aof_delayed_fsync</span> | <span class=\"break-all\">`redis_server_aof_delayed_fsync`</span> | Number of times an AOF fsync caused delays in the main Redis thread (inducing latency); this can indicate that the disk is slow or overloaded |\n| <span class=\"break-all\">redis_blocked_clients</span> | <span class=\"break-all\">`redis_server_blocked_clients`</span> | Count the clients waiting on a blocking call |\n| <span class=\"break-all\">redis_connected_clients</span> | <span class=\"break-all\">`redis_server_connected_clients`</span> | Number of client connections to the specific shard |\n| <span class=\"break-all\">redis_connected_slaves</span> | <span class=\"break-all\">`redis_server_connected_slaves`</span> | Number of connected replicas |\n| <span class=\"break-all\">redis_db0_avg_ttl</span> | <span class=\"break-all\">`redis_server_db0_avg_ttl`</span> | Average TTL of all volatile keys |\n| <span class=\"break-all\">redis_db0_expires</span> | <span class=\"break-all\">`redis_server_expired_keys`</span> | Total count of volatile keys |\n| <span class=\"break-all\">redis_db0_keys</span> | <span class=\"break-all\">`redis_server_db0_keys`</span> | Total key count |\n| <span class=\"break-all\">redis_evicted_keys</span> | <span class=\"break-all\">`redis_server_evicted_keys`</span> | Keys evicted so far (since restart) |\n| <span class=\"break-all\">redis_expire_cycle_cpu_milliseconds</span> | <span class=\"break-all\">`redis_server_expire_cycle_cpu_milliseconds`</span> | The cumulative amount of time spent on active expiry cycles |\n| <span class=\"break-all\">redis_expired_keys</span> | <span class=\"break-all\">`redis_server_expired_keys`</span> | Keys expired so far (since restart) |\n| <span class=\"break-all\">redis_forwarding_state</span> | <span class=\"break-all\">`redis_server_forwarding_state`</span> | Shard forwarding state (1 = on, 0 = off) |\n| <span class=\"break-all\">redis_keys_trimmed</span> | <span class=\"break-all\">`redis_server_keys_trimmed`</span> | The number of keys that were trimmed in the current or last resharding process |\n| <span class=\"break-all\">redis_keyspace_read_hits</span> | <span class=\"break-all\">`redis_server_keyspace_read_hits`</span> | Number of read operations accessing an existing keyspace |\n| <span class=\"break-all\">redis_keyspace_read_misses</span> | <span class=\"break-all\">`redis_server_keyspace_read_misses`</span> | Number of read operations accessing a non-existing keyspace |\n| <span class=\"break-all\">redis_keyspace_write_hits</span> | <span class=\"break-all\">`redis_server_keyspace_write_hits`</span> | Number of write operations accessing an existing keyspace |\n| <span class=\"break-all\">redis_keyspace_write_misses</span> | <span class=\"break-all\">`redis_server_keyspace_write_misses`</span> | Number of write operations accessing a non-existing keyspace |\n| <span class=\"break-all\">redis_master_link_status</span> | <span class=\"break-all\">`redis_server_master_link_status`</span> | Indicates if the replica is connected to its master (1 = up, 2 = down, 3 = none, 99 = unknown) |\n| <span class=\"break-all\">redis_master_repl_offset</span> | <span class=\"break-all\">`redis_server_master_repl_offset`</span> | Number of bytes sent to replicas by the shard; calculate the throughput for a time period by comparing the value at different times |\n| <span class=\"break-all\">redis_master_sync_in_progress</span> | <span class=\"break-all\">`redis_server_master_sync_in_progress`</span> | The master shard is synchronizing (1 true; 0 false) |\n| <span class=\"break-all\">redis_max_process_mem</span> | <span class=\"break-all\">`redis_server_max_process_mem`</span> | Current memory limit configured by redis_mgr according to node free memory |\n| <span class=\"break-all\">redis_maxmemory</span> | <span class=\"break-all\">`redis_server_maxmemory`</span> | Current memory limit configured by redis_mgr according to database memory limits. <br /><br />To calculate the percent memory usage:<br /><span class=\"break-all\">`sum by (cluster,db)(redis_server_used_memory{role=\"master\"}) / (avg by(cluster,db)(db_memory_limit_bytes) / max by(cluster,db)(db_replication_factor))`</span> |\n| <span class=\"break-all\">redis_mem_aof_buffer</span> | <span class=\"break-all\">`redis_server_mem_aof_buffer`</span> | Current size of AOF buffer |\n| <span class=\"break-all\">redis_mem_clients_normal</span> | <span class=\"break-all\">`redis_server_mem_clients_normal`</span> | Current memory used for input and output buffers of non-replica clients |\n| <span class=\"break-all\">redis_mem_clients_slaves</span> | <span class=\"break-all\">`redis_server_mem_clients_slaves`</span> | Current memory used for input and output buffers of replica clients |\n| <span class=\"break-all\">redis_mem_fragmentation_ratio</span> | <span class=\"break-all\">`redis_server_mem_fragmentation_ratio`</span> | Memory fragmentation ratio (1.3 means 30% overhead) |\n| <span class=\"break-all\">redis_mem_not_counted_for_evict</span> | <span class=\"break-all\">`redis_server_mem_not_counted_for_evict`</span> | Portion of used_memory (in bytes) that's not counted for eviction and OOM error |\n| <span class=\"break-all\">redis_mem_replication_backlog</span> | <span class=\"break-all\">`redis_server_mem_replication_backlog`</span> | Size of replication backlog |\n| <span class=\"break-all\">redis_module_fork_in_progress</span> | <span class=\"break-all\">`redis_server_module_fork_in_progress`</span> | A binary value that indicates if there is an active fork spawned by a module (1) or not (0) |\n| <span class=\"break-all\">redis_process_cpu_system_seconds_total</span> | <span class=\"break-all\">`namedprocess_namegroup_cpu_seconds_total{mode=\"system\"}`</span> | Shard process system CPU time spent in seconds |\n| <span class=\"break-all\">redis_process_cpu_usage_percent</span> | <span class=\"break-all\">`namedprocess_namegroup_cpu_seconds_total{mode=~\"system\\|user\"}`</span> | Shard process CPU usage percentage |\n| <span class=\"break-all\">redis_process_cpu_user_seconds_total</span> | <span class=\"break-all\">`namedprocess_namegroup_cpu_seconds_total{mode=\"user\"}`</span> | Shard user CPU time spent in seconds |\n| <span class=\"break-all\">redis_process_main_thread_cpu_system_seconds_total</span> | <span class=\"break-all\">`namedprocess_namegroup_thread_cpu_seconds_total{mode=\"system\",threadname=\"redis-server\"}`</span> | Shard main thread system CPU time spent in seconds |\n| <span class=\"break-all\">redis_process_main_thread_cpu_user_seconds_total</span> | <span class=\"break-all\">`namedprocess_namegroup_thread_cpu_seconds_total{mode=\"user\",threadname=\"redis-server\"}`</span> | Shard main thread user CPU time spent in seconds |\n| <span class=\"break-all\">redis_process_max_fds</span> | <span class=\"break-all\">`max(namedprocess_namegroup_open_filedesc)`</span> | Shard maximum number of open file descriptors |\n| <span class=\"break-all\">redis_process_open_fds</span> | <span class=\"break-all\">`namedprocess_namegroup_open_filedesc`</span> | Shard number of open file descriptors |\n| <span class=\"break-all\">redis_process_resident_memory_bytes</span> | <span class=\"break-all\">`namedprocess_namegroup_memory_bytes{memtype=\"resident\"}`</span> | Shard resident memory size in bytes |\n| <span class=\"break-all\">redis_process_start_time_seconds</span> | <span class=\"break-all\">`namedprocess_namegroup_oldest_start_time_seconds`</span> | Shard start time of the process since unix epoch in seconds |\n| <span class=\"break-all\">redis_process_virtual_memory_bytes</span> | <span class=\"break-all\">`namedprocess_namegroup_memory_bytes{memtype=\"virtual\"}`</span> | Shard virtual memory in bytes |\n| <span class=\"break-all\">redis_rdb_bgsave_in_progress</span> | <span class=\"break-all\">`redis_server_rdb_bgsave_in_progress`</span> | Indicates whether bgsave is currently in progress (1 = in progress, 0 = otherwise) |\n| <span class=\"break-all\">redis_rdb_last_cow_size</span> | <span class=\"break-all\">`redis_server_rdb_last_cow_size`</span> | Last bgsave (or SYNC fork) used CopyOnWrite memory |\n| <span class=\"break-all\">redis_rdb_saves</span> | <span class=\"break-all\">`redis_server_rdb_saves`</span> | Total count of bgsaves since the process was restarted (including replica fullsync and persistence) |\n| <span class=\"break-all\">redis_repl_touch_bytes</span> | <span class=\"break-all\">`redis_server_repl_touch_bytes`</span> | Number of bytes sent to replicas as TOUCH commands by the shard as a result of a READ command that was processed; calculate the throughput for a time period by comparing the value at different times |\n| <span class=\"break-all\">redis_total_commands_processed</span> | <span class=\"break-all\">`redis_server_total_commands_processed`</span> | Number of commands processed by the shard; calculate the number of commands for a time period by comparing the value at different times |\n| <span class=\"break-all\">redis_total_connections_received</span> | <span class=\"break-all\">`redis_server_total_connections_received`</span> | Number of connections received by the shard; calculate the number of connections for a time period by comparing the value at different times |\n| <span class=\"break-all\">redis_total_net_input_bytes</span> | <span class=\"break-all\">`redis_server_total_net_input_bytes`</span> | Number of bytes received by the shard; calculate the throughput for a time period by comparing the value at different times |\n| <span class=\"break-all\">redis_total_net_output_bytes</span> | <span class=\"break-all\">`redis_server_total_net_output_bytes`</span> | Number of bytes sent by the shard; calculate the throughput for a time period by comparing the value at different times |\n| <span class=\"break-all\">redis_up</span> | <span class=\"break-all\">`redis_server_up`</span> | Shard is up and running |\n| <span class=\"break-all\">redis_used_memory</span> | <span class=\"break-all\">`redis_server_used_memory`</span> | Memory used by shard (in BigRedis this includes flash) (bytes) |"}],"examples":[{"id":"overview-ex0","language":"yaml","code":"scrape_configs:\n  # Scrape Redis Software\n  - job_name: redis-enterprise\n    scrape_interval: 30s\n    scrape_timeout: 30s\n    metrics_path: /v2\n    scheme: https\n    tls_config:\n      insecure_skip_verify: true\n    static_configs:\n      - targets: [\"<cluster_name>:8070\"]","section_id":"overview"}]}
{"schema_version":2,"id":"operate/rs/monitoring/metrics_stream_engine/prometheus-metrics-v2","title":"Prometheus metrics v2","url":"https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/prometheus-metrics-v2/","summary":"V2 metrics available to Prometheus as of Redis Software version 7.8.2.","tags":["docs","integrate","rs"],"last_updated":"2026-09-24T10:14:24+01:00","page_type":"content","content_hash":"eedd4d98016f9ba650df86e4915ecc0ca308b335e380757d2e30a3cceec56558","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can integrate Redis Software with Prometheus and tools such as [Grafana](https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise), [Datadog](https://redis.io/docs/latest/integrate/datadog-with-redis-enterprise), [Dynatrace](https://redis.io/docs/latest/integrate/dynatrace-with-redis-enterprise), or [New Relic](https://redis.io/docs/latest/integrate/new-relic-with-redis-enterprise) to create dashboards for important metrics.\n\nThe v2 metrics in the following tables are available as of Redis Software version 7.8.0. For help transitioning from v1 metrics to v2 PromQL, see [Prometheus v1 metrics and equivalent v2 PromQL](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/prometheus-metrics-v1-to-v2).\n\nThe v2 scraping endpoint also exposes metrics for `node_exporter`. For more information, see the [Prometheus node_exporter GitHub repository](https://github.com/prometheus/node_exporter)."},{"id":"database-metrics","title":"Database metrics","role":"content","text":"| Metric | Type | Unit | Description |\n| :-------- | :--- | :--- | :---------- |\n| <span class=\"break-all\">endpoint_accepted_connections</span> | counter | count | Number of incoming accepted client connections |\n| endpoint_client_connections | counter | count | Number of client connection establishment events |\n| endpoint_client_disconnections | counter | count | Number of client disconnections initiated by the client |\n| <span class=\"break-all\">endpoint_client_connection_expired</span> | counter | count | Total number of client connections with expired TTL (Time To Live) |\n| <span class=\"break-all\">endpoint_client_establishment_failures</span> | counter | count | Number of client connections that failed to establish properly |\n| <span class=\"break-all\">endpoint_client_expiration_refresh</span> | counter | count | Number of expiration time changes of clients |\n| <span class=\"break-all\">endpoint_client_tracking_off_requests</span> | counter | count | Total number of `CLIENT TRACKING OFF` requests |\n| <span class=\"break-all\">endpoint_client_tracking_on_requests</span> | counter | count | Total number of `CLIENT TRACKING ON` requests |\n| <span class=\"break-all\">endpoint_connections_rate</span> | gauge | connections/second | The rate of incoming connections. Computed as `n_accepted / N` for the last interval where `n_accepted` is the number of accepted connections in this interval, and `N` is the interval in seconds. |\n| <span class=\"break-all\">endpoint_disconnected_cba_client</span> | counter | count | Number of certificate-based clients disconnected |\n| <span class=\"break-all\">endpoint_disconnected_ldap_client</span> | counter | count | Number of LDAP clients disconnected |\n| <span class=\"break-all\">endpoint_disconnected_user_password_client</span> | counter | count | Number of user&password clients disconnected |\n| <span class=\"break-all\">endpoint_dispatch_failures</span> | counter | count | Number of clients closed due to failure to be dispatched to workers |\n| <span class=\"break-all\">endpoint_disposed_commands_after_client_caching</span> | counter | count | Total number of client caching commands that were disposed due to misuse |\n| endpoint_egress | counter | bytes | Number of egress bytes |\n| endpoint_egress_pending | counter | bytes | Number of send-pending bytes |\n| <span class=\"break-all\">endpoint_egress_pending_discarded</span> | counter | bytes | Number of send-pending bytes that were discarded due to disconnection |\n| <span class=\"break-all\">endpoint_failed_cba_authentication</span> | counter | count | Number of clients that failed certificate-based authentication |\n| <span class=\"break-all\">endpoint_failed_ldap_authentication</span> | counter | count | Number of clients that failed LDAP authentication |\n| <span class=\"break-all\">endpoint_failed_user_password_authentication</span> | counter | count | Number of clients that failed user password authentication |\n| endpoint_ingress | counter | bytes | Number of ingress bytes |\n| <span class=\"break-all\">endpoint_longest_pipeline_histogram</span> | histogram | count | Tracks the distribution of longest observed pipeline lengths, where a pipeline is a sequence of client commands sent without waiting for responses. |\n| endpoint_other_requests | counter | count | Number of other requests |\n| <span class=\"break-all\">endpoint_other_requests_latency_histogram</span> | histogram | microseconds | Latency (in µs) histogram of other commands |\n| <span class=\"break-all\">endpoint_other_requests_latency_histogram_bucket</span> | histogram | microseconds | Latency histograms for commands other than read or write commands. Can be used to represent different latency percentiles.<br />p99.9 example:<br /><span class=\"break-all\">`histogram_quantile(0.999, sum(rate(endpoint_other_requests_latency_histogram_bucket{cluster=\"$cluster\", db=\"$db\"}[$__rate_interval]) ) by (le, db))`</span> |\n| endpoint_other_responses | counter | count | Number of other responses |\n| <span class=\"break-all\">endpoint_ping_failures</span> | gauge | count | Number of consecutive endpoint ping failures. Labels: endpoint_uid |\n| <span class=\"break-all\">endpoint_ping_failure_duration_seconds</span> | gauge | seconds | Duration of ongoing endpoint failures (0 when healthy). Labels: endpoint_uid |\n| endpoint_proxy_disconnections | counter | count | Number of client disconnections initiated by the proxy |\n| <span class=\"break-all\">endpoint_rate_limit_ok</span> | gauge | — | Rate limit status based on the last 2 intervals.<br />0 = rate limit was recently exceeded<br />1 = rate limit was not recently exceeded |\n| <span class=\"break-all\">endpoint_rate_limit_overflows</span> | counter | count | Total number of rate limit overflows |\n| endpoint_read_requests | counter | count | Number of read requests |\n| <span class=\"break-all\">endpoint_read_requests_latency_histogram</span> | histogram | microseconds | Latency (in µs) histogram of read commands |\n| <span class=\"break-all\">endpoint_read_requests_latency_histogram_bucket</span> | histogram | microseconds | Latency histograms for read commands. Can be used to represent different latency percentiles.<br />p99.9 example:<br /><span class=\"break-all\">`histogram_quantile(0.999, sum(rate(endpoint_read_requests_latency_histogram_bucket{cluster=\"$cluster\", db=\"$db\"}[$__rate_interval]) ) by (le, db))`</span> |\n| endpoint_read_responses | counter | count | Number of read responses |\n| <span class=\"break-all\">endpoint_successful_cba_authentication</span> | counter | count | Number of clients that successfully authenticated with certificate-based authentication |\n| <span class=\"break-all\">endpoint_successful_ldap_authentication</span> | counter | count | Number of clients that successfully authenticated with LDAP |\n| <span class=\"break-all\">endpoint_successful_user_password_authentication</span> | counter | count | Number of clients that successfully authenticated with user&password |\n| endpoint_write_requests | counter | count | Number of write requests |\n| <span class=\"break-all\">endpoint_write_requests_latency_histogram</span> | histogram | microseconds | Latency (in µs) histogram of write commands |\n| <span class=\"break-all\">endpoint_write_requests_latency_histogram_bucket</span> | histogram | microseconds | Latency histograms for write commands. Can be used to represent different latency percentiles.<br />p99.9 example:<br /><span class=\"break-all\">`histogram_quantile(0.999, sum(rate(endpoint_write_requests_latency_histogram_bucket{cluster=\"$cluster\", db=\"$db\"}[$__rate_interval]) ) by (le, db))`</span> |\n| endpoint_write_responses | counter | count | Number of write responses |\n| db_config | gauge | — | This is an information metric that holds database configuration within labels such as: db_name, db_version, db_port, tls_mode |\n| db_status | gauge | — | This is a status metric that reports on various database statuses: 0 = active, 1 = active-change-pending, 2 = pending, 3 = import-pending, 4 = delete-pending, 5 = recovery, 99 = unknown |\n| db_tags | gauge | — | Information metric that exposes database tags as labels; the value is always `1`. See [Database tags in metrics](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/db-tags-in-metrics). |"},{"id":"node-metrics","title":"Node metrics","role":"content","text":"| Metric | Type | Unit | Description |\n| :-------- | :--- | :--- | :---------- |\n| node_available_flash_bytes | gauge | bytes | Available flash in the node (bytes) |\n| <span class=\"break-all\">node_available_flash_no_overbooking_bytes</span> | gauge | bytes | Available flash in the node (bytes), without taking into account overbooking |\n| <span class=\"break-all\">node_available_memory_bytes</span> | gauge | bytes | Amount of free memory in the node (bytes) |\n| <span class=\"break-all\">node_available_memory_no_overbooking_bytes</span> | gauge | bytes | Available RAM in the node (bytes) without taking into account overbooking |\n| node_bigstore_free_bytes | gauge | bytes | Sum of free space of back-end flash (used by flash database's [BigRedis]) on all cluster nodes (bytes); returned only when BigRedis is enabled |\n| <span class=\"break-all\">node_cert_expires_in_seconds</span> | gauge | seconds | Certificate expiration (in seconds) per given node; read more about [certificates in Redis Software](https://redis.io/docs/latest/operate/rs/security/certificates) and [monitoring certificates](https://redis.io/docs/latest/operate/rs/security/certificates/monitor-certificates) |\n| <span class=\"break-all\">customer_managed_ine_certificates</span> | gauge | — | Indicates whether customer-provided internode encryption certificates are in use<br />0=No<br />1=Yes |\n| <span class=\"break-all\">node_ephemeral_storage_avail_bytes</span> | gauge | bytes | Disk space available to RLEC processes on configured ephemeral disk (bytes) |\n| <span class=\"break-all\">node_ephemeral_storage_free_bytes</span> | gauge | bytes | Free disk space on configured ephemeral disk (bytes) |\n| node_memory_MemFree_bytes | gauge | bytes | Free memory in the node (bytes) |\n| <span class=\"break-all\">node_persistent_storage_avail_bytes</span> | gauge | bytes | Disk space available to RLEC processes on configured persistent disk (bytes) |\n| <span class=\"break-all\">node_persistent_storage_free_bytes</span> | gauge | bytes | Free disk space on configured persistent disk (bytes) |\n| <span class=\"break-all\">node_provisional_flash_bytes</span> | gauge | bytes | Amount of flash available for new shards on this node, taking into account overbooking, max Redis servers, reserved flash, and provision and migration thresholds (bytes) |\n| <span class=\"break-all\">node_provisional_flash_no_overbooking_bytes</span> | gauge | bytes | Amount of flash available for new shards on this node, without taking into account overbooking, max Redis servers, reserved flash, and provision and migration thresholds (bytes) |\n| <span class=\"break-all\">node_provisional_memory_bytes</span> | gauge | bytes | Amount of RAM that is available for provisioning to databases out of the total RAM allocated for databases |\n| <span class=\"break-all\">node_provisional_memory_no_overbooking_bytes</span> | gauge | bytes | Amount of RAM that is available for provisioning to databases out of the total RAM allocated for databases, without taking into account overbooking |\n| node_metrics_up | gauge | — | Node is part of the cluster and is connected (1 = connected, 0 = not connected) |\n| <span class=\"break-all\">dmc_ping_failures</span> | gauge | count | Number of consecutive DMC ping failures |\n| <span class=\"break-all\">dmc_ping_failure_duration_seconds</span> | gauge | seconds | Duration of ongoing DMC failures (0 when healthy) |"},{"id":"cluster-metrics","title":"Cluster metrics","role":"content","text":"| Metric | Type | Unit | Description |\n| :-------- | :--- | :--- | :---------- |\n| <span class=\"break-all\">generation{node=<node_uid>}</span> | gauge | — | Current generation (state-version) number of the cluster watchdog on the specific node |\n| <span class=\"break-all\">has_quorum{node=<node_uid>, has_witness_disk=BOOL}</span> | gauge | — | Has_quorum = 1<br />No quorum = 0 |\n| <span class=\"break-all\">is_primary{node=<node_uid>}</span> | gauge | — | primary = 1<br />secondary = 0 |\n| <span class=\"break-all\">license_expiration_days</span> | gauge | days | Number of days until the license expires |\n| <span class=\"break-all\">license_shards_limit</span> | gauge | count | Total shard limit by the license by shard type (ram / flash) |\n| <span class=\"break-all\">total_live_nodes_count{node=<node_uid>}</span> | gauge | count | Number of live nodes |\n| <span class=\"break-all\">total_nodes_count{node=<node_uid>}</span> | gauge | count | Number of nodes |\n| <span class=\"break-all\">primary_selections_total{node=<node_uid>}</span> | counter | count | Monotonic counter for each selection process that started |\n| users_count | gauge | count | Current number of users on the cluster |"},{"id":"replication-metrics","title":"Replication metrics","role":"content","text":"| Metric | Type | Unit | Description |\n| :-------- | :--- | :--- | :---------- |\n| <span class=\"break-all\">database_syncer_config</span> | gauge | — | Used as a placeholder for configuration labels |\n| <span class=\"break-all\">database_syncer_current_status</span> | gauge | — | Syncer status for traffic; 0 = in-sync, 1 = out of sync |\n| <span class=\"break-all\">database_syncer_dst_connectivity_state</span> | gauge | — | Destination connectivity state (1 = connected, 0 = disconnected) |\n| <span class=\"break-all\">database_syncer_dst_connectivity_state_ms</span> | gauge | milliseconds | Destination connectivity state duration |\n| <span class=\"break-all\">database_syncer_dst_lag</span> | gauge | milliseconds | Lag in milliseconds between the syncer and the destination |\n| <span class=\"break-all\">database_syncer_dst_repl_offset</span> | gauge | bytes | Offset of the last command acknowledged |\n| <span class=\"break-all\">database_syncer_flush_counter</span> | gauge | count | Number of destination flushes |\n| <span class=\"break-all\">database_syncer_ingress_bytes</span> | gauge | bytes | Number of bytes read from source shard |\n| <span class=\"break-all\">database_syncer_ingress_bytes_decompressed</span> | gauge | bytes | Number of bytes read from source shard |\n| <span class=\"break-all\">database_syncer_internal_state</span> | gauge | — | Internal state of the syncer |\n| <span class=\"break-all\">database_syncer_lag_ms</span> | gauge | milliseconds | Lag time between the source and the destination for traffic in milliseconds |\n| <span class=\"break-all\">database_syncer_rdb_size</span> | gauge | bytes | The source's RDB size in bytes to be transferred during the syncing phase |\n| <span class=\"break-all\">database_syncer_rdb_transferred</span> | gauge | bytes | Number of bytes transferred from the source's RDB during the syncing phase |\n| <span class=\"break-all\">database_syncer_src_connectivity_state</span> | gauge | — | Source connectivity state (1 = connected, 0 = disconnected) |\n| <span class=\"break-all\">database_syncer_src_connectivity_state_ms</span> | gauge | milliseconds | Source connectivity state duration |\n| <span class=\"break-all\">database_syncer_src_repl_offset</span> | gauge | bytes | Last known source offset |\n| <span class=\"break-all\">database_syncer_state</span> | gauge | — | Internal state of the shard syncer |\n| <span class=\"break-all\">database_syncer_syncer_repl_offset</span> | gauge | bytes | Offset of the last command handled by the syncer |\n| <span class=\"break-all\">database_syncer_total_requests</span> | gauge | count | Number of destination writes |\n| <span class=\"break-all\">database_syncer_total_responses</span> | gauge | count | Number of destination writes acknowledged |"},{"id":"shard-metrics","title":"Shard metrics","role":"content","text":"| Metric | Type | Unit | Description |\n| :-------- | :--- | :--- | :---------- |\n| redis_server_active_defrag_running | gauge | percent | Automatic memory defragmentation current aggressiveness (% cpu) |\n| redis_server_allocator_active | gauge | bytes | Total used memory, including external fragmentation |\n| redis_server_allocator_allocated | gauge | bytes | Total allocated memory |\n| redis_server_allocator_resident | gauge | bytes | Total resident memory (RSS) |\n| redis_server_aof_last_cow_size | gauge | bytes | Last AOFR, CopyOnWrite memory |\n| <span class=\"break-all\">redis_server_aof_rewrite_in_progress</span> | gauge | — | Indicates whether an AOF rewrite is in progress (1 = in progress, 0 = otherwise) |\n| redis_server_aof_rewrites | gauge | count | Number of AOF rewrites this process executed |\n| redis_server_aof_delayed_fsync | gauge | count | Number of times an AOF fsync caused delays in the main Redis thread (inducing latency); this can indicate that the disk is slow or overloaded |\n| redis_server_blocked_clients | gauge | count | Count the clients waiting on a blocking call |\n| redis_server_connected_clients | gauge | count | Number of client connections to the specific shard |\n| redis_server_connected_slaves | gauge | count | Number of connected replicas |\n| redis_server_db_avg_ttl | gauge | milliseconds | Average TTL of all volatile keys |\n| redis_server_db0_avg_ttl | gauge | milliseconds | Average TTL of all volatile keys. Deprecated. |\n| redis_server_db_keys | gauge | count | Total key count. |\n| redis_server_db0_keys | gauge | count | Total key count. Deprecated. |\n| redis_server_evicted_keys | gauge | count | Keys evicted so far (since restart) |\n| <span class=\"break-all\">redis_server_expire_cycle_cpu_milliseconds</span> | gauge | milliseconds | The cumulative amount of time spent on active expiry cycles |\n| redis_server_expired_keys | gauge | count | Keys expired so far since restart |\n| redis_server_forwarding_state | gauge | — | Shard forwarding state (1 = on, 0 = off) |\n| redis_server_hashes_items_under_1M | gauge | count | Number of hash keys with under 1 million elements |\n| redis_server_hashes_items_1M_to_8M | gauge | count | Number of hash keys with an element count between 1 million and 8 million |\n| redis_server_hashes_items_over_8M | gauge | count | Number of hash keys with over 8 million elements |\n| redis_server_keys_trimmed | gauge | count | The number of keys that were trimmed in the current or last resharding process |\n| redis_server_keyspace_read_hits | gauge | count | Number of read operations accessing an existing keyspace |\n| redis_server_keyspace_read_misses | gauge | count | Number of read operations accessing a non-existing keyspace |\n| redis_server_keyspace_write_hits | gauge | count | Number of write operations accessing an existing keyspace |\n| <span class=\"break-all\">redis_server_keyspace_write_misses</span> | gauge | count | Number of write operations accessing a non-existing keyspace |\n| redis_server_lists_items_under_1M | gauge | count | Number of list keys with under 1 million elements |\n| redis_server_lists_items_1M_to_8M | gauge | count | Number of list keys with an element count between 1 million and 8 million |\n| redis_server_lists_items_over_8M | gauge | count | Number of list keys with over 8 million elements |\n| redis_server_master_link_status | gauge | — | Indicates whether the replica is connected to its master (1 = up, 2 = down, 3 = none, 99 = unknown) |\n| redis_server_master_repl_offset | gauge | bytes | Number of bytes sent to replicas by the shard; calculate the throughput for a time period by comparing the value at different times |\n| <span class=\"break-all\">redis_server_master_sync_in_progress</span> | gauge | — | The primary shard is synchronizing (1 true; 0 false) |\n| redis_server_max_process_mem | gauge | bytes | Current memory limit configured by redis_mgr according to node free memory |\n| redis_server_maxmemory | gauge | bytes | Current memory limit configured by redis_mgr according to database memory limits. <br /><br />To calculate the percent memory usage:<br /><span class=\"break-all\">`sum by (cluster,db)(redis_server_used_memory{role=\"master\"}) / (avg by(cluster,db)(db_memory_limit_bytes) / max by(cluster,db)(db_replication_factor))`</span> |\n| redis_server_mem_aof_buffer | gauge | bytes | Current size of AOF buffer |\n| redis_server_mem_clients_normal | gauge | bytes | Current memory used for input and output buffers of non-replica clients |\n| redis_server_mem_clients_slaves | gauge | bytes | Current memory used for input and output buffers of replica clients |\n| <span class=\"break-all\">redis_server_mem_fragmentation_ratio</span> | gauge | ratio | Memory fragmentation ratio (1.3 means 30% overhead) |\n| <span class=\"break-all\">redis_server_mem_not_counted_for_evict</span> | gauge | bytes | Portion of used_memory (in bytes) that's not counted for eviction and OOM error |\n| <span class=\"break-all\">redis_server_mem_replication_backlog</span> | gauge | bytes | Size of replication backlog |\n| <span class=\"break-all\">redis_server_slave_buffer</span> | gauge | bytes | Reports the effective replica output buffer hard limit for a shard, derived from the database slave_buffer configuration. For slave_buffer=auto, the hard limit is calculated as used_memory * auto_slavebuf_ratio / 100, then bounded by auto_slavebuf_min and auto_slavebuf_max. |\n| <span class=\"break-all\">redis_server_module_fork_in_progress</span> | gauge | — | A binary value that indicates if there is an active fork spawned by a module (1) or not (0) |\n| <span class=\"break-all\">namedprocess_namegroup_cpu_seconds_total</span> | counter | seconds | Shard process CPU usage in seconds |\n| <span class=\"break-all\">namedprocess_namegroup_thread_cpu_seconds_total</span> | counter | seconds | Shard main thread CPU time spent in seconds |\n| <span class=\"break-all\">namedprocess_namegroup_open_filedesc</span> | gauge | count | Shard number of open file descriptors |\n| <span class=\"break-all\">namedprocess_namegroup_memory_bytes</span> | gauge | bytes | Shard memory size in bytes |\n| <span class=\"break-all\">namedprocess_namegroup_oldest_start_time_seconds</span> | gauge | timestamp seconds | Shard start time of the process since unix epoch in seconds |\n| <span class=\"break-all\">redis_server_rdb_bgsave_in_progress</span> | gauge | — | Indicates whether bgsave is currently in progress (1 = in progress, 0 = otherwise) |\n| redis_server_rdb_last_cow_size | gauge | bytes | Last bgsave (or SYNC fork) used CopyOnWrite memory |\n| redis_server_rdb_saves | gauge | count | Total count of bgsaves since the process was restarted (including replica fullsync and persistence) |\n| redis_server_sets_items_under_1M | gauge | count | Number of set keys with under 1 million elements |\n| redis_server_sets_items_1M_to_8M | gauge | count | Number of set keys with an element count between 1 million and 8 million |\n| redis_server_sets_items_over_8M | gauge | count | Number of set keys with over 8 million elements |\n| redis_server_repl_touch_bytes | gauge | bytes | Number of bytes sent to replicas as TOUCH commands by the shard as a result of a READ command that was processed; calculate the throughput for a time period by comparing the value at different times |\n| <span class=\"break-all\">redis_server_total_commands_processed</span> | gauge | count | Number of commands processed by the shard; calculate the number of commands for a time period by comparing the value at different times |\n| <span class=\"break-all\">redis_server_total_connections_received</span> | gauge | count | Number of connections received by the shard; calculate the number of connections for a time period by comparing the value at different times |\n| redis_server_total_net_input_bytes | gauge | bytes | Number of bytes received by the shard; calculate the throughput for a time period by comparing the value at different times |\n| <span class=\"break-all\">redis_server_total_net_output_bytes</span> | gauge | bytes | Number of bytes sent by the shard; calculate the throughput for a time period by comparing the value at different times |\n| redis_server_up | gauge | — | Shard is up and running (1 = up, 0 = down) |\n| <span class=\"break-all\">redis_server_strings_sizes_under_128M</span> | gauge | count | Number of string keys with a memory size under 128 megabytes |\n| <span class=\"break-all\">redis_server_strings_sizes_128M_to_512M</span> | gauge | count | Number of string keys with a memory size between 128 and 512 megabytes |\n| <span class=\"break-all\">redis_server_strings_sizes_over_512M</span> | gauge | count | Number of string keys with a memory size over 512 megabytes |\n| redis_server_used_memory | gauge | bytes | Memory used by shard (in BigRedis this includes flash) (bytes) |\n| redis_server_zsets_items_under_1M | gauge | count | Number of sorted set keys with under 1 million elements |\n| redis_server_zsets_items_1M_to_8M | gauge | count | Number of sorted set keys with an element count between 1 million and 8 million |\n| redis_server_zsets_items_over_8M | gauge | count | Number of sorted set keys with over 8 million elements |\n| <span class=\"break-all\">redis_server_search_gc_bytes_collected</span> | gauge | bytes | The total amount of memory freed by the garbage collectors from indexes in the shard's memory in bytes. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_bytes_collected</span> | gauge | bytes | The total amount of memory freed by the garbage collectors from indexes in the shard's memory in bytes. Deprecated in 8.0 (renamed <span class=\"break-all\">redis_server_search_gc_bytes_collected</span>), but still available in older versions. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_gc_marked_deleted_vectors</span> | gauge | count | The number of vectors marked as deleted in the vector indexes that have not yet been cleaned. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_marked_deleted_vectors</span> | gauge | count | The number of vectors marked as deleted in the vector indexes that have not yet been cleaned. Deprecated in 8.0 (renamed <span class=\"break-all\">redis_server_search_gc_marked_deleted_vectors</span>), but still available in older versions. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_gc_total_cycles</span> | gauge | count | The total number of garbage collection cycles executed. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_total_cycles</span> | gauge | count | The total number of garbage collection cycles executed. Deprecated in 8.0 (renamed <span class=\"break-all\">redis_server_search_gc_total_cycles</span>), but still available in older versions. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_gc_total_docs_not_collected_by_gc</span> | gauge | count | The number of documents marked as deleted, whose memory has not yet been freed by the garbage collector. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_total_docs_not_collected_by_gc</span> | gauge | count | The number of documents marked as deleted, whose memory has not yet been freed by the garbage collector. Deprecated in 8.0 (renamed <span class=\"break-all\">redis_server_search_gc_total_docs_not_collected_by_gc</span>), but still available in older versions. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_gc_total_ms_run</span> | gauge | milliseconds | The total duration of all garbage collection cycles in the shard, measured in milliseconds. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_total_ms_run</span> | gauge | milliseconds | The total duration of all garbage collection cycles in the shard, measured in milliseconds. Deprecated in 8.0 (renamed <span class=\"break-all\">redis_server_search_gc_total_ms_run</span>), but still available in older versions. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_cursors_internal_idle</span> | gauge | count | The total number of coordinator cursors that are currently holding pending results in the shard. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_cursors_user_idle</span> | gauge | count | The total number of cursors that were explicitly requested by users, that are currently holding pending results in the shard. <sup>[4](#tnote-4)</sup> |\n| redis_server_search_global_idle | gauge | count | The total number of user and internal cursors currently holding pending results in the shard. Deprecated in 8.0 (split into <span class=\"break-all\">redis_server_search_cursors_internal_idle</span> and <span class=\"break-all\">redis_server_search_cursors_user_idle</span>), but still available in older versions. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_cursors_internal_active</span> | gauge | count | The total number of coordinator cursors in the shard, either holding pending results or actively executing `FT.CURSOR READ`. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_cursors_user_active</span> | gauge | count | The total number of user cursors in the shard, either holding pending results or actively executing `FT.CURSOR READ`. <sup>[4](#tnote-4)</sup> |\n| redis_server_search_global_total | gauge | count | The total number of user and internal cursors in the shard, either holding pending results or actively executing `FT.CURSOR READ`. Deprecated in 8.0 (split into <span class=\"break-all\">redis_server_search_cursors_internal_active</span> and <span class=\"break-all\">redis_server_search_cursors_user_active</span>), but still available in older versions. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_number_of_indexes</span> | gauge | count | Total number of indexes in the shard <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_number_of_active_indexes</span> | gauge | count | The total number of indexes running a background indexing and/or background query processing operation. Background indexing refers to vector ingestion process, or in-progress background indexer. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_total_num_docs_in_indexes</span> | gauge | count | The total number of documents currently indexed across all indexes in the shard. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_number_of_active_indexes_running_queries</span> | gauge | count | Total count of indexes currently running a background query process. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_number_of_active_indexes_indexing</span> | gauge | count | Total count of indexes currently undergoing a background indexing process. Background indexing refers to vector ingestion process, or in-progress background indexer. This metric is limited by the number of WORKER threads allocated for writing operations + the number of indexes. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_total_active_write_threads</span> | gauge | count | Total count of background write (indexing) processes currently running in the shard. Background indexing refers to vector ingestion process, or in-progress background indexer. This metric is limited by the number of threads allocated for writing operations. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_text_Text</span> | gauge | count | The total number of `TEXT` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_text_Sortable</span> | gauge | count | The total number of `SORTABLE TEXT` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_text_NoIndex</span> | gauge | count | The total number of `NOINDEX TEXT` fields across all indexes in the shard; i.e., used for sorting only but not indexed. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_text_IndexErrors</span> | gauge | count | The total number of indexing failures caused by attempts to index a document containing a `TEXT` field. This field appears only if `TEXT` fields exist. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_numeric_Numeric</span> | gauge | count | The total number of `NUMERIC` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_numeric_Sortable</span> | gauge | count | The total number of `SORTABLE NUMERIC` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_numeric_NoIndex</span> | gauge | count | The total number of `NOINDEX NUMERIC` fields across all indexes in the shard, which are used for sorting only but not indexed. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_numeric_IndexErrors</span> | gauge | count | The total number of indexing failures caused by attempts to index a document containing a `NUMERIC` field. This field appears only if `NUMERIC` fields exist. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_tag_Tag</span> | gauge | count | The total number of `TAG` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_tag_Sortable</span> | gauge | count | The total number of `SORTABLE TAG` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_tag_NoIndex</span> | gauge | count | The total number of `NOINDEX TAG` fields across all indexes in the shard; i.e., used for sorting only but not indexed. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_tag_CaseSensitive</span> | gauge | count | The total number of `CASESENSITIVE TAG` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_tag_IndexErrors</span> | gauge | count | The total number of indexing failures caused by attempts to index a document containing a `TAG` field. This field appears only if `TAG` fields exist. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_geo_Geo</span> | gauge | count | The total number of `GEO` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_geo_Sortable</span> | gauge | count | The total number of `SORTABLE GEO` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_geo_NoIndex</span> | gauge | count | The total number of `NOINDEX GEO` fields across all indexes in the shard; i.e., used for sorting only but not indexed. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_geo_IndexErrors</span> | gauge | count | The total number of indexing failures caused by attempts to index a document containing a `GEO` field. This field appears only if `GEO` fields exist. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_vector_Vector</span> | gauge | count | The total number of `VECTOR` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_vector_Flat</span> | gauge | count | The total number of `FLAT VECTOR` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_vector_HNSW</span> | gauge | count | The total number of `HNSW VECTOR` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_vector_SVS_VAMANA</span> | gauge | count | The total number of `SVS-VAMANA VECTOR` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_vector_SVS_VAMANA_Compressed</span> | gauge | count | The total number of `SVS-VAMANA VECTOR` fields with `COMPRESSION` enabled (e.g., `LVQ8`) across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_vector_IndexErrors</span> | gauge | count | The total number of indexing failures caused by attempts to index a document containing a `VECTOR` field. This field appears only if `VECTOR` fields exist. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_geoshape_Geoshape</span> | gauge | count | The total number of `GEOSHAPE` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_geoshape_Sortable</span> | gauge | count | The total number of `SORTABLE GEOSHAPE` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_geoshape_NoIndex</span> | gauge | count | The total number of `NOINDEX GEOSHAPE` fields across all indexes in the shard; i.e., used for sorting only but not indexed. This field appears only if its value is larger than 0. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_geoshape_IndexErrors</span> | gauge | count | The total number of indexing failures caused by attempts to index a document containing a `GEOSHAPE` field. This field appears only if `GEOSHAPE` fields exist. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_total_indexing_ops_text_fields</span> | gauge | count | The total number of indexing operations performed on `TEXT` fields across all indexes in the shard. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_total_indexing_ops_tag_fields</span> | gauge | count | The total number of indexing operations performed on `TAG` fields across all indexes in the shard. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_total_indexing_ops_numeric_fields</span> | gauge | count | The total number of indexing operations performed on `NUMERIC` fields across all indexes in the shard. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_total_indexing_ops_geo_fields</span> | gauge | count | The total number of indexing operations performed on `GEO` fields across all indexes in the shard. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_total_indexing_ops_geoshape_fields</span> | gauge | count | The total number of indexing operations performed on `GEOSHAPE` fields across all indexes in the shard. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_total_indexing_ops_vector_fields</span> | gauge | count | The total number of indexing operations performed on `VECTOR` fields across all indexes in the shard. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_used_memory_indexes</span> | gauge | bytes | The total memory allocated by all indexes in the shard in bytes. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_smallest_memory_index</span> | gauge | bytes | The memory usage of the index with the smallest memory usage in the shard in bytes. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_largest_memory_index</span> | gauge | bytes | The memory usage of the index with the largest memory usage in the shard in bytes. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_total_indexing_time</span> | gauge | milliseconds | The total time spent on indexing operations, excluding the background indexing of vectors in the `HNSW` graph. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_used_memory_vector_index</span> | gauge | bytes | The total memory usage of all vector indexes in the shard. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_total_queries_processed</span> | gauge | count | The total number of successful query executions (When using cursors, not counting reading from existing cursors) in the shard. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_total_query_commands</span> | gauge | count | The total number of successful query command executions (including `FT.SEARCH`, `FT.AGGREGATE`, and `FT.CURSOR READ`). <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_total_query_execution_time_ms</span> | gauge | milliseconds | The cumulative execution time of all query commands, including `FT.SEARCH`, `FT.AGGREGATE`, and `FT.CURSOR READ`, measured in ms. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_total_active_queries</span> | gauge | count | The total number of background queries currently being executed in the shard, excluding `FT.CURSOR READ`. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_total_coord_dispatch_time_ms</span> | gauge | milliseconds | The cumulative time the coordinator spent building and dispatching query commands (`FT.SEARCH`, `FT.AGGREGATE`, and `FT.HYBRID`) to the shards, measured in ms. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_errors_indexing_failures</span> | gauge | count | The total number of indexing failures recorded across all indexes in the shard. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_errors_for_index_with_max_failures</span> | gauge | count | The number of indexing failures in the index with the highest count of failures. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_OOM_indexing_failures_indexes_count</span> | gauge | count | The count of indexes that experienced out-of-memory (OOM) failures during background indexing. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_shard_total_query_errors_syntax</span> | gauge | count | The total number of query syntax errors occurred in the shard. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_shard_total_query_errors_arguments</span> | gauge | count | The total number of queries in the shard that failed due to missing or invalid arguments. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_shard_total_query_errors_timeout</span> | gauge | count | The total number of query timeout errors occurred in the shard (when timeout policy is 'fail'). <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_shard_total_query_warnings_timeout</span> | gauge | count | The total number of query timeout warnings occurred in the shard (when timeout policy is 'return partial results'). <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_shard_total_query_errors_oom</span> | gauge | count | The total number of query out-of-memory errors occurred in the shard. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_shard_total_query_warnings_oom</span> | gauge | count | The total number of query out-of-memory warnings occurred in the shard. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_shard_total_query_warnings_max_prefix_expansions</span> | gauge | count | The total number of max prefix expansion warnings occurred in the shard. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_coord_total_query_errors_syntax</span> | gauge | count | The total number of query syntax errors occurred at the coordinator. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_coord_total_query_errors_arguments</span> | gauge | count | The total number of query argument errors encountered by the shard's coordinator. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_coord_total_query_errors_timeout</span> | gauge | count | The total number of query timeout errors encountered by the shard's coordinator (when timeout policy is 'fail'). <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_coord_total_query_warnings_timeout</span> | gauge | count | The total number of query timeout warnings encountered by the shard's coordinator (when timeout policy is 'return partial results'). <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_coord_total_query_errors_oom</span> | gauge | count | The total number of query out-of-memory errors encountered by the shard's coordinator. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_coord_total_query_warnings_oom</span> | gauge | count | The total number of query out-of-memory warnings encountered by the shard's coordinator. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_coord_total_query_warnings_max_prefix_expansions</span> | gauge | count | The total number of max prefix expansion warnings encountered by the shard's coordinator. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_uv_threads_running_queries</span> | gauge | count | The number of I/O threads currently handling query distribution to shards in cluster environments. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_uv_threads_running_topology_update</span> | gauge | count | The number of UV threads currently running topology updates. <sup>[3](#tnote-3)</sup> |\n| <span class=\"break-all\">redis_server_search_active_worker_threads</span> | gauge | count | The number of active worker threads. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_active_coord_threads</span> | gauge | count | The number of active coordinator threads. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_workers_low_priority_pending_jobs</span> | gauge | count | The number of pending low-priority jobs in worker threads, such as vector background indexing, graph updates, and vector garbage collection in graph-based indexes. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_workers_high_priority_pending_jobs</span> | gauge | count | The number of pending high-priority jobs in worker threads, such as query execution. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_workers_admin_priority_pending_jobs</span> | gauge | count | The number of pending admin-priority jobs in worker threads, such as threadpool configuration changes. <sup>[3](#tnote-3)</sup> |\n| <span class=\"break-all\">redis_server_search_coord_high_priority_pending_jobs</span> | gauge | count | The number of pending jobs in the coordinator thread queue. Coordinator threads only have a high-priority queue and are primarily used for query distribution. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">shard_ping_failures</span> | gauge | count | Number of consecutive ping failures for a shard. Labels: shard_uid, role |\n| <span class=\"break-all\">shard_ping_failure_duration_seconds</span> | gauge | seconds | Duration of ongoing failures (0 when healthy). Labels: shard_uid, role |\n\n1. <a name=\"tnote-1\"></a> Available since RediSearch 2.6.\n2. <a name=\"tnote-2\"></a> Available since RediSearch 2.8.\n3. <a name=\"tnote-3\"></a> Available since RediSearch 2.10.\n4. <a name=\"tnote-4\"></a> Available since RediSearch 8.0."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/monitoring/observability","title":"Redis Software observability and monitoring guidance","url":"https://redis.io/docs/latest/operate/rs/monitoring/observability/","summary":"Using monitoring and observability with Redis Software","tags":["docs","integrate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"ada72460836b4bd64dda53434dd94de0bee0aee145c6451edcfe2a3355e52a59","sections":[{"id":"introduction","title":"Introduction","role":"overview","text":"This document provides observability and monitoring guidance for developers running applications\nthat connect to Redis Software. In particular, this guide focuses on the systems\nand resources that are most likely to impact the performance of your application.\n\nThe screenshot below shows a dashboard with relevant statistics for a node:\n\n![Dashboard showing relevant statistics for a Node](https://redis.io/docs/latest/images/node_summary.png)\n\nTo effectively monitor a Redis Software cluster you need to observe\ncore cluster resources and key database performance indicators as described in the following sections for this guide.\n\nCore cluster resources include:\n\n* Memory utilization\n* CPU utilization\n* Database connections\n* Network traffic\n* Synchronization\n\nKey database performance indicators include:\n\n* Latency\n* Cache hit rate\n* Key eviction rate\n* Proxy Performance\n\nDashboard showing an overview of cluster metrics:\n\n![Dashboard showing an overview of cluster metrics](https://redis.io/docs/latest/images/cluster_overview.png)\n\nIn addition to manually monitoring these resources and indicators, it is best practice to set up alerts."},{"id":"core-cluster-resource-monitoring","title":"Core cluster resource monitoring","role":"content","text":"Redis Software version 7.8.2 introduces a preview of the new metrics stream engine that exposes the v2 Prometheus scraping endpoint at `https://<cluster_name>:8070/v2`. This new engine exports all time-series metrics to external monitoring tools such as Grafana, DataDog, NewRelic, and Dynatrace using Prometheus.\n\nThe new engine enables real-time monitoring, including full monitoring during maintenance operations, providing full visibility into performance during events such as shards' failovers and scaling operations. See [Monitoring with metrics and alerts](https://redis.io/docs/latest/operate/rs/monitoring/) for more details.\n\nIf you are already using the existing scraping endpoint for integration, follow [this guide](https://redis.io/docs/latest/operate/rs/references/metrics/prometheus-metrics-v1-to-v2) to transition and try the new engine. You can scrape both existing and new endpoints simultaneously, which lets you create advanced dashboards and transition smoothly."},{"id":"memory","title":"Memory","role":"content","text":"Every Redis Software database has a maximum configured memory limit to ensure isolation\nin a multi-database cluster.\n\n| Metric name | Definition | Unit |\n| ------ | ------ | :------ |\n| Memory usage percentage metric | Percentage of used memory relative to the configured memory limit for a given database | Percentage |\n\nDashboard displaying high-level cluster metrics - [Cluster Dashboard](https://github.com/redis-field-engineering/redis-enterprise-observability/blob/main/grafana/dashboards/grafana_v9-11/software/classic/cluster_dashboard_v9-11.json)\n\n![Dashboard displaying high-level cluster metrics](https://redis.io/docs/latest/images/playbook_used-memory.png)"},{"id":"thresholds","title":"Thresholds","role":"content","text":"The appropriate memory threshold depends on how the application is using Redis.\n\n* Caching workloads, which permit Redis to evict keys, can safely use 100% of available memory.\n* Non-caching workloads do not permit key eviction and should be closely monitored as soon as memory usage reaches 80%."},{"id":"caching-workloads","title":"Caching workloads","role":"content","text":"For applications using Redis solely as a cache, you can safely let the memory usage\nreach 100% as long as you have an [eviction policy](https://redis.io/blog/cache-eviction-strategies/) in place. This will ensure\nthat Redis can evict keys while continuing to accept new writes.\n\n**Note:** Eviction will increase write command latency as Redis has to cleanup the memory/objects before accepting a new write to prevent OOM when memory usage is at 100%.\n\nWhile your Redis database is using 100% of available memory in a caching context,\nit's still important to monitor performance. The key performance indicators include:\n\n* Latency\n* Cache hit ratio\n* Evicted keys"},{"id":"read-latency","title":"Read latency","role":"content","text":"**Latency** has two important definitions, depending on context:\n\n* In the context of Redis itself, latency is **the time it takes for Redis\nto respond to a request**. The [Latency](#latency) section below provides a broader discussion of this metric.\n\n\n\n* In the context of your application, Latency is **the time it takes for the application\nto process a request**. This will include the time it takes to execute both reads and writes\nto Redis, as well as calls to other databases and services. Note that its possible for\nRedis to report low latency while the application is experiencing high latency.\nThis may indicate a low cache hit ratio, ultimately caused by insufficient memory.\n\nYou need to monitor both application-level and Redis-level latency to diagnose\ncaching performance issues in production."},{"id":"cache-hit-ratio-and-eviction","title":"Cache hit ratio and eviction","role":"content","text":"**Cache hit ratio** is the percentage of read requests that Redis serves successfully.\n**Eviction rate** is the rate at which Redis evicts keys from the cache. These metrics\nare sometimes inversely correlated: a high eviction rate may cause a low cache hit ratio if too many frequently-used keys are being evicted.\n\nIf the Redis server is empty, the hit ratio will be 0%. As the application runs and the fills the cache,\nthe hit ratio will increase.\n\n**When the entire cached working set fits in memory**, the cache hit ratio will reach close to 100%\nwhile the percent of used memory will remain below 100%.\n\n**When the working set cannot fit in memory**, the eviction policy will start to evict keys.\nIt is important to choose a policy that generally evicts rarely-used keys to keep the cache hit ratio as high as possible.\n\nIn both cases, keys will may be manually invalidated by the application or evicted through\nthe uses of TTLs (time-to-live) and an eviction policy.\n\nThe ideal cache hit ratio depends on the application, but generally, the ratio should be greater than 50%.\nLow hit ratios coupled with high numbers of object evictions may indicate that your cache is too small.\nThis can cause thrashing on the application side, a scenario where the cache is constantly being invalidated.\n\nThis means that when your Redis database is using 100% of available memory, you need\nto measure the rate of\n[key evictions](https://redis.io/docs/latest/operate/rs/references/metrics/database-operations#evicted-objectssec).\n\nAn acceptable rate of key evictions depends on the total number of keys in the database\nand the measure of application-level latency. If application latency is high,\ncheck to see that key evictions have not increased."},{"id":"eviction-policies","title":"Eviction policies","role":"content","text":"| Name | Description |\n| ------ | :------ |\n|noeviction | New values aren’t saved when memory limit is reached. When a database uses replication, this applies to the primary database |\n|allkeys-lru | Keeps most recently used keys; removes least recently used (LRU) keys |\n|allkeys-lfu | Keeps frequently used keys; removes least frequently used (LFU) keys |\n|volatile-lru | Removes least recently used keys with the expire field set to true. |\n|volatile-lfu | Removes least frequently used keys with the expire field set to true. |\n|allkeys-random | Randomly removes keys to make space for the new data added. |\n|volatile-random | Randomly removes keys with expire field set to true. |\n|volatile-ttl | Removes keys with expire field set to true and the shortest remaining time-to-live (TTL) value. |"},{"id":"eviction-policy-guidelines","title":"Eviction policy guidelines","role":"content","text":"* Use the allkeys-lru policy when you expect a power-law distribution in the popularity of your requests. That is, you expect a subset of elements will be accessed far more often than the rest. This is a good policy to choose if you are unsure.\n\n* Use the allkeys-random if you have a cyclic access where all the keys are scanned continuously, or when you expect the distribution to be uniform.\n\n* Use the volatile-ttl if you want to be able to provide hints to Redis about what are good candidates for expiration by using different TTL values when you create your cache objects.\n\nThe volatile-lru and volatile-random policies are mainly useful when you want to use a single instance for both caching and to have a set of persistent keys. However it is usually a better idea to run two Redis instances to solve such a problem.\n\n**Note:** Setting an expire value to a key costs memory, so using a policy like allkeys-lru is more memory efficient because there is no need for an expire configuration for the key to be evicted under memory pressure."},{"id":"non-caching-workloads","title":"Non-caching workloads","role":"content","text":"If no eviction policy is enabled, then Redis will stop accepting writes when memory usage reaches 100%.\nTherefore, for non-caching workloads, it is best practice to configure an alert at 80% memory usage.\nAfter your database reaches this 80% threshold, you should closely review the rate of memory usage growth."},{"id":"troubleshooting","title":"Troubleshooting","role":"errors","text":"|Issue | Possible causes | Remediation |\n| ------ | ------ | :------ |\n|Redis memory usage has reached 100% |This may indicate an insufficient Redis memory limit for your application's workload | For non-caching workloads (where eviction is unacceptable), immediately increase the memory limit for the database. You can accomplish this through the Redis Software console or its API. Alternatively, you can contact Redis support to assist. For caching workloads, you need to monitor performance closely. Confirm that you have an [eviction policy](https://redis.io/docs/latest/operate/rs/databases/memory-performance/eviction-policy) in place. If your application's performance starts to degrade, you may need to increase the memory limit, as described above. |\n|Redis has stopped accepting writes | Memory is at 100% and no eviction policy is in place | Increase the database's total amount of memory. If this is for a caching workload, consider enabling an [eviction policy](https://redis.io/docs/latest/operate/rs/databases/memory-performance/eviction-policy). In addition, you may want to determine whether the application can set a reasonable TTL (time-to-live) on some or all of the data being written to Redis. |\n|Cache hit ratio is steadily decreasing | The application's working set size may be steadily increasing. Alternatively, the application may be misconfigured (for example, generating more than one unique cache key per cached item.) | If the working set size is increasing, consider increasing the memory limit for the database. If the application is misconfigured, review the application's cache key generation logic. |"},{"id":"cpu","title":"CPU","role":"content","text":"Redis Software provides several CPU metrics:\n\n| Metric name | Definition | Unit |\n| ------ | ------ | :------ |\n| Shard CPU | CPU time portion spent by database shards as a percentage | up to 100% per shard |\n| Proxy CPU | CPU time portion spent by the cluster's proxy(s) as a percentage | 100% per proxy thread |\n| Node CPU (User and System) | CPU time portion spent by all user-space and kernel-level processesas a Percentage | 100% per node CPU |\n\n\nTo understand CPU metrics, it's worth recalling how a Redis Software cluster is organized.\nA cluster consists of one or more nodes. Each node is a VM (or cloud compute instance) or\na bare-metal server.\n\nA database is a set of processes, known as shards, deployed across the nodes of a cluster.\n\nIn the dashboard, shard CPU is the CPU utilization of the processes that make up the database.\nWhen diagnosing performance issues, start by looking at shard CPU.\n\nDashboard displaying CPU usage - [Database Dashboard](https://github.com/redis-field-engineering/redis-enterprise-observability/blob/main/grafana/dashboards/grafana_v9-11/software/classic/database_dashboard_v9-11.json)\n\n![Dashboard displaying CPU usage](https://redis.io/docs/latest/images/playbook_database-cpu-shard.png)"},{"id":"thresholds-1","title":"Thresholds","role":"content","text":"In general, we define high CPU as any CPU utilization above 80% of total capacity.\n\nShard CPU should remain below 80%. Shards are single-threaded, so a shard CPU of 100% means that the shard is fully utilized.\n\nDisplay showing Proxy CPU usage - [Proxy Dashboard](https://github.com/redis-field-engineering/redis-enterprise-observability/blob/main/grafana/dashboards/grafana_v9-11/software/classic/proxy_dashboard_v9-11.json)\n\n![Display showing Proxy CPU usage](https://redis.io/docs/latest/images/playbook_proxy-cpu-usage.png)\n\nProxy CPU should remain below 80% of total capacity.\nThe proxy is a multi-threaded process that handles client connections and forwards requests to the appropriate shard.\nBecause the total number of proxy threads is configurable, the proxy CPU may exceed 100%.\nA proxy configured with 6 threads can reach 600% CPU utilization, so in this case,\nkeeping utilization below 80% means keeping the total proxy CPU usage below 480%.\n\nDashboard displaying an ensemble of Node CPU usage data - [Node Dashboard](https://github.com/redis-field-engineering/redis-enterprise-observability/blob/main/grafana/dashboards/grafana_v9-11/software/classic/node_dashboard_v9-11.json)\n\n![Dashboard displaying an ensemble of Node CPU usage data](https://redis.io/docs/latest/images/node_cpu.png)\n\nNode CPU should also remain below 80% of total capacity. As with the proxy, the node CPU is variable depending\non the CPU capacity of the node. You will need to calibrate your alerting based on the number of cores in your nodes."},{"id":"troubleshooting-1","title":"Troubleshooting","role":"errors","text":"High CPU utilization has multiple possible causes. Common causes include an under-provisioned cluster,\nexcess inefficient Redis operations, and hot master shards.\n\n\n| Issue | Possible causes | Remediation \n| ------ | ------ | :------ |\n|High CPU utilization across all shards of a database | This usually indicates that the database is under-provisioned in terms of number of shards. A secondary cause may be that the application is running too many inefficient Redis operations. | You can detect slow Redis operations by enabling the slow log in the Redis Software UI. First, rule out inefficient Redis operations as the cause of the high CPU utilization. The Latency section below includes a broader discussion of this metric in the context of your application. If inefficient Redis operations are not the cause, then increase the number of shards in the database. |\n|High CPU utilization on a single shard, with the remaining shards having low CPU utilization | This usually indicates a master shard with at least one hot key. Hot keys are keys that are accessed extremely frequently (for example, more than 1000 times per second). | Hot key issues generally cannot be resolved by increasing the number of shards. To resolve this issue, see the section on Hot keys below. |\n| High Proxy CPU | There are several possible causes of high proxy CPU. First, review the behavior of connections to the database. Frequent cycling of connections, especially with TLS is enabled, can cause high proxy CPU utilization. This is especially true when you see more than 100 connections per second per thread. Such behavior is almost always a sign of a misbehaving application. Review the total number of operations per second against the cluster. If you see more than 50k operations per second per thread, you may need to increase the number of proxy threads. | In the case of high connection cycling, review the application's connection behavior. In the case of high operations per second, [increase the number of proxy threads](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-proxy). |\n|High Node CPU | You will typically detect high shard or proxy CPU utilization before you detect high node CPU utilization. Use the remediation steps above to address high shard and proxy CPU utilization. In spite of this, if you see high node CPU utilization, you may need to increase the number of nodes in the cluster. | Consider increasing the number of nodes in the cluster and the rebalancing the shards across the new nodes. This is a complex operation and you should do it with the help of Redis support. |\n|High System CPU | Most of the issues above will reflect user-space CPU utilization. However, if you see high system CPU utilization, this may indicate a problem at the network or storage level. | Review network bytes in and network bytes out to rule out any unexpected spikes in network traffic. You may need perform some deeper network diagnostics to identify the cause of the high system CPU utilization. For example, with high rates of packet loss, you may need to review network configurations or even the network hardware. |"},{"id":"connections","title":"Connections","role":"content","text":"The Redis Software database dashboard indicates the total number of connections to the database.\n\nYou should monitor this connection count metric with both a minimum and maximum number of connections in mind.\nBased on the number of application instances connecting to Redis (and whether your application uses [connection pooling](https://redis.io/docs/latest/develop/clients/pools-and-muxing)),\nyou should have a rough idea of the minimum and maximum number of connections you expect to see for any given database.\nThis number should remain relatively constant over time."},{"id":"troubleshooting-2","title":"Troubleshooting","role":"errors","text":"| Issue | Possible causes | Remediation |\n| ------ | ------ | :------ |\n|Fewer connections to Redis than expected |The application may not be connecting to the correct Redis database. There may be a network partition between the application and the Redis database. | Confirm that the application can successfully connect to Redis. This may require consulting the application logs or the application's connection configuration. | \n|Connection count continues to grow over time | Your application may not be releasing connections. The most common of such a connection leak is a manually implemented connection pool or a connection pool that is not properly configured. | Review the application's connection configuration |\n|Erratic connection counts (for example, spikes and drops) | Application misbehavior ([thundering herds](https://en.wikipedia.org/wiki/Thundering_herd_problem), connection cycling, or networking issues) | Review the application logs and network traffic to determine the cause of the erratic connection counts. |\n\n\nDashboard displaying connections - [Database Dashboard](https://github.com/redis-field-engineering/redis-enterprise-observability/blob/main/grafana/dashboards/grafana_v9-11/software/classic/database_dashboard_v9-11.json)\n\n![Dashboard displaying connections](https://redis.io/docs/latest/images/playbook_database-used-connections.png)"},{"id":"network-ingressegress","title":"Network ingress/egress","role":"content","text":"The network ingress/egress panel shows the amount of data being sent to and received from the database.\nLarge spikes in network traffic can indicate that the cluster is under-provisioned or that\nthe application is reading and/or writing unusually [large keys](#large-keys). A correlation between high network traffic\nand high CPU utilization may indicate a large key scenario.\n\n#### Unbalanced database endpoint\n\nOne possible cause of network traffic spikes is that the database endpoint is not located on the same node as the master shards. In addition to added network latency, if data plane internode encryption is enabled, CPU consumption can increase as well.\n\nOne solution is to use the optimal shard placement and proxy policy to ensure endpoints are collocated on nodes hosting master shards. If you need to restore balance (for example, after node failure) you can manually failover shard(s) with the `rladmin` cli tool.\n\nExtreme network traffic utilization may approach the limits of the underlying network infrastructure.\nIn this case, the only remediation is to add more nodes to the cluster and scale the database's shards across them."},{"id":"synchronization","title":"Synchronization","role":"content","text":"In Redis Software, geographically-distributed synchronization is based on Conflict-free replicated data types (CRDT) technology.\nThe Redis Software implementation of CRDT is called an Active-Active database (formerly known as CRDB).\nWith Active-Active databases, applications can read and write to the same data set from different geographical locations seamlessly and with low latency, without changing the way the application connects to the database.\n\nAn Active-Active architecture is a data resiliency architecture that distributes the database information over multiple data centers using independent and geographically distributed clusters and nodes.\nIt is a network of separate processing nodes, each having access to a common replicated database such that all nodes can participate in a common application ensuring local low latency with each region being able to run in isolation.\n\nTo achieve consistency between participating clusters, Redis Active-Active synchronization uses a process called the syncer.\n\nThe syncer keeps a replication backlog, which stores changes to the dataset that the syncer sends to other participating clusters.\nThe syncer uses partial syncs to keep replicas up to date with changes, or a full sync in the event a replica or primary is lost.\n\nDashboard displaying connection metrics between zones - [Synchronization Dashboard](https://github.com/redis-field-engineering/redis-enterprise-observability/blob/main/grafana/dashboards/grafana_v9-11/software/classic/synchronization_dashboard_v9-11.json)\n\n![Dashboard displaying connection metrics between zones](https://redis.io/docs/latest/images/playbook_network-connectivity.png)\n\nCRDT provides three fundamental benefits over other geo-distributed solutions:\n\n* It offers local latency on read and write operations, regardless of the number of geo-replicated regions and their distance from each other.\n* It enables seamless conflict resolution (\"conflict-free\") for simple and complex data types like those of Redis core.\n* Even if most of the geo-replicated regions in a CRDT database (for example, 3 out of 5) are down, the remaining geo-replicated regions are uninterrupted and can continue to handle read and write operations, ensuring business continuity."},{"id":"database-performance-indicators","title":"Database performance indicators","role":"content","text":"There are several key performance indicators that report your database's performance against your application's workload:\n\n* Latency\n* Cache hit rate\n* Key eviction rate"},{"id":"latency","title":"Latency","role":"content","text":"Latency is **the time it takes for Redis to respond to a request**.\nRedis Software measures latency from the first byte received by the proxy to the last byte sent in the command's response.\n\nAn adequately provisioned Redis database running efficient Redis operations will report an average latency below 1 millisecond. In fact, it's common to measure\nlatency in terms of microseconds. Businesses regularly achieve, and sometimes require, average latencies of 400-600\nmicroseconds.\n\nDashboard display of latency metrics - [Database Dashboard](https://github.com/redis-field-engineering/redis-enterprise-observability/blob/main/grafana/dashboards/grafana_v9-11/software/classic/database_dashboard_v9-11.json)\n\n![Dashboard display of latency metrics](https://redis.io/docs/latest/images/playbook_database-cluster-latency.png)\n\nThe metrics distinguish between read and write latency. Understanding whether high latency is due\nto read or writes can help you to isolate the underlying issue.\n\nNote that these latency metrics do not include network round trip time or application-level serialization,\nwhich is why it's essential to measure request latency at the application, as well.\n\nDisplay showing a noticeable spike in latency\n\n![Display showing a noticeable spike in latency](https://redis.io/docs/latest/images/latency_spike.png)"},{"id":"troubleshooting-3","title":"Troubleshooting","role":"errors","text":"Here are some possible causes of high database latency. Note that high database latency is just one of the reasons\nwhy application latency might be high. Application latency can be caused by a variety of factors, including\na low cache hit rate.\n\n| Issue | Possible causes | Remediation |\n| ------ | ------ | :------ |\n|Slow database operations | Confirm that there are no excessive slow operations in the Redis slow log. | If possible, reduce the number of slow operations being sent to the database. <br/>If this not possible, consider increasing the number of shards in the database. |\n|Increased traffic to the database | Review the network traffic and the database operations per second chart to determine if increased traffic is causing the latency. | If the database is underprovisioned due to increased traffic, consider increasing the number of shards in the database. |\n|Insufficient CPU | Check to see if the CPU utilization is increasing. | Confirm that slow operations are not causing the high CPU utilization. If the high CPU utilization is due to increased load, consider adding shards to the database. |"},{"id":"cache-hit-rate","title":"Cache hit rate","role":"content","text":"**Cache hit rate** is the percentage of all read operations that return a response. **Note:** Cache hit rate is a composite statistic that is computed by dividing the number of read hits by the total number of read operations.\nWhen an application tries to read a key that exists, this is known as a **cache hit**.\nAlternatively, when an application tries to read a key that does not exist, this is knows as a **cache miss**.\n\nFor caching workloads, the cache hit rate should generally be above 50%, although\nthe exact ideal cache hit rate can vary greatly depending on the application and depending on whether the cache\nis already populated.\n\nDashboard showing the cache hit ratio along with read/write misses - [Database Dashboard](https://github.com/redis-field-engineering/redis-enterprise-observability/blob/main/grafana/dashboards/grafana_v9-11/software/classic/database_dashboard_v9-11.json)\n\n![Dashboard showing the cache hit ratio along with read/write misses](https://redis.io/docs/latest/images/playbook_cache-hit.png)\n\n**Note:** Redis Software actually reports four different cache hit / miss metrics.\nThese are defined as follows:\n\n| Metric name | Definition | \n| ------ | :------ |\n| bdb_read_hits | The number of successful read operations |\n| bdb_read_misses | The number of read operations returning null |\n| bdb_write_hits | The number of write operations against existing keys |\n| bdb_write_misses | The number of write operations that create new keys |"},{"id":"troubleshooting-4","title":"Troubleshooting","role":"errors","text":"Cache hit rate is usually only relevant for caching workloads. Eviction will begin after the database approaches its maximum memory capacity.\n\nA high or increasing rate of evictions will negatively affect database latency, especially\nif the rate of necessary key evictions exceeds the rate of new key insertions.\n\nSee the [Cache hit ratio and eviction](#cache-hit-ratio-and-eviction) section for tips on troubleshooting cache hit rate."},{"id":"key-eviction-rate","title":"Key eviction rate","role":"content","text":"They **key eviction rate** is rate at which objects are being evicted from the database.\nSee [eviction policy](https://redis.io/docs/latest/operate/rs/databases/memory-performance/eviction-policy) for a discussion of key eviction and its relationship with memory usage.\n\nDashboard displaying object evictions - [Database Dashboard](https://github.com/redis-field-engineering/redis-enterprise-observability/blob/main/grafana/dashboards/grafana_v9-11/software/classic/database_dashboard_v9-11.json)\n\n![Dashboard displaying object evictions](https://redis.io/docs/latest/images/playbook_eviction-expiration.png)"},{"id":"proxy-performance","title":"Proxy performance","role":"content","text":"Redis Software provides high-performance data access through a proxy process that manages and optimizes access to shards within the cluster. Each node contains a single proxy process. Each proxy can be active and take incoming traffic or it can be passive and wait for failovers."},{"id":"proxy-policies","title":"Proxy policies","role":"content","text":"| Policy | Description |\n| ------ | :------ |\n|Single | There is only a single proxy that is bound to the database. This is the default database configuration and preferable in most use cases. |\n|All Master Shards | There are multiple proxies that are bound to the database, one on each node that hosts a database master shard. This mode fits most use cases that require multiple proxies. |\n|All Nodes | There are multiple proxies that are bound to the database, one on each node in the cluster, regardless of whether or not there is a shard from this database on the node. This mode should be used only in special cases, such as using a load balancer. |\n\nDashboard displaying proxy thread activity - [Proxy Thread Dashboard](https://github.com/redis-field-engineering/redis-enterprise-observability/blob/main/grafana/dashboards/grafana_v9-11/cloud/basic/redis-cloud-proxy-dashboard_v9-11.json)\n\n![Dashboard displaying proxy thread activity](https://redis.io/docs/latest/images/proxy-thread-dashboard.png)\n\nIf you need to, you can tune the number of proxy threads using the [`rladmin tune proxy`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-proxy) command to make the proxy use more CPU cores.\nCores used by the proxy won't be available for Redis, therefore we need to take into account the number of Redis nodes on the host and the total number of available cores.\n\nThe command has a few parameters you can use to set a new number of proxy cores:\n\n* `id|all` - you can either tune a specific proxy by its id, or all proxies.\n\n* `mode` - determines whether or not the proxy can automatically adjust the number of threads depending on load.\n\n* `threads` and `max_threads` - determine the initial number of threads created on startup, and the maximum number of threads allowed.\n\n* `scale_threshold` - determines the CPU utilization threshold that triggers spawning new threads. This CPU utilization level needs to be maintained for at least scale_duration seconds before automatic scaling is performed.\n\nThe following table indicates ideal proxy thread counts for the specified environments.\n\n\n| Total Cores | Redis (ROR) | Redis on Flash (ROF) |\n| ------ | ------ | :------ |\n|1|1|1 |\n|4|3|3 |\n|8|5|3 |\n|12|8|4 |\n|16|10|5 |\n|32|24|10 |\n|64/96|32|20 |\n|128|32|32 |"},{"id":"data-access-anti-patterns","title":"Data access anti-patterns","role":"content","text":"There are three data access patterns that can limit the performance of your Redis database:\n\n* Slow operations\n* Hot keys\n* Large keys\n\nThis section defines each of these patterns and describes how to diagnose and mitigate them."},{"id":"slow-operations","title":"Slow operations","role":"content","text":"**Slow operations** are operations that take longer than a few milliseconds to complete.\n\nNot all Redis operations are equally efficient.\nThe most efficient Redis operations are O(1) operations; that is, they have a constant time complexity.\nExample of such operations include [GET](https://redis.io/docs/latest/commands/get),\n[SET](https://redis.io/docs/latest/commands/set), [SADD](https://redis.io/docs/latest/commands/sadd),\nand [HSET](https://redis.io/docs/latest/commands/hset).\n\nThese constant time operations are unlikely to cause high CPU utilization. **Note:** Even so,\nit's still possible for a high rate of constant time operations to overwhelm an underprovisioned database.\n\nOther Redis operations exhibit greater levels of time complexity.\nO(n) (linear time) operations are more likely to cause high CPU utilization.\nExamples include [HGETALL](https://redis.io/docs/latest/commands/hgetall), [SMEMBERS](https://redis.io/docs/latest/commands/smembers),\nand [LREM](https://redis.io/docs/latest/commands/lrem).\nThese operations are not necessarily problematic, but they can be if executed against data structures holding\na large number of elements (for example, a list with 1 million elements).\n\nHowever, the [KEYS](https://redis.io/docs/latest/commands/keys) command should almost never be run against a\nproduction system, since returning a list of all keys in a large Redis database can cause significant slowdowns\nand block other operations. If you need to scan the keyspace, especially in a production cluster, always use the\n[SCAN](https://redis.io/docs/latest/commands/scan) command instead."},{"id":"troubleshooting-5","title":"Troubleshooting","role":"errors","text":"The best way to discover slow operations is to view the slow log.\nThe slow log is available in the Redis Software and Redis Cloud consoles:\n* [Redis Software slow log docs](https://redis.io/docs/latest/operate/rs/clusters/logging/redis-slow-log)\n* [Redis Cloud slow log docs](https://redis.io/docs/latest/operate/rc/databases/view-edit-database#other-actions-and-info)\n\nRedis Cloud dashboard showing slow database operations\n\n![Redis Cloud dashboard showing slow database operations](https://redis.io/docs/latest/images/slow_log.png)\n\n| Issue | Remediation |\n| ------ | :------ |\n|The KEYS command shows up in the slow log |Find the application that issues the KEYS command and replace it with a SCAN command. In an emergency situation, you can [alter the ACLs for the database user](https://redis.io/docs/latest/operate/rs/security/access-control/redis-acl-overview) so that Redis will reject the KEYS command altogether. |\n|The slow log shows a significant number of slow, O(n) operations | If these operations are being issued against large data structures, then the application may need to be refactored to use more efficient Redis commands. |\n|The slow logs contains only O(1) commands, and these commands are taking several milliseconds or more to complete |This likely indicates that the database is underprovisioned. Consider increasing the number of shards and/or nodes. |"},{"id":"hot-keys","title":"Hot keys","role":"content","text":"A **hot key** is a key that is accessed extremely frequently (for example, thousands of times a second or more).\n\nEach key in Redis belongs to one, and only one, shard.\nFor this reason, a hot key can cause high CPU utilization on that one shard,\nwhich can increase latency for all other operations."},{"id":"troubleshooting-6","title":"Troubleshooting","role":"errors","text":"You may suspect that you have a hot key if you see high CPU utilization on a single shard.\nThere are two main way to identify hot keys: using the Redis CLI and sampling the operations against Redis.\n\nTo use the Redis CLI to identify hot keys:\n\n1. First confirm that you have enough available memory to enable an eviction policy.\n2. Next, enable the LFU (least-frequently used) eviction policy on the database.\n3. Finally, run `redis-cli --hotkeys`\n\nYou may also identify hot keys by sampling the operations against Redis.\nYou can use do this by running the [MONITOR](https://redis.io/docs/latest/commands/monitor) command\nagainst the high CPU shard. Because this is a potentially high-impact operation, you should only\nuse this technique as a secondary option. For mission-critical databases, consider\ncontacting Redis support for assistance."},{"id":"remediation","title":"Remediation","role":"content","text":"After you discover a hot key, you need to find a way to reduce the number of operations against it.\nThis means getting an understanding of the application's access pattern and the reasons for such frequent access.\n\nIf the hot key operations are read-only, consider implementing an application-local cache so\nthat fewer read requests are sent to Redis. For example, even a local cache that expires every 5 seconds\ncan entirely eliminate a hot key issue."},{"id":"large-keys","title":"Large keys","role":"content","text":"**Large keys** are keys that are hundreds of kilobytes or larger.\nHigh network traffic and high CPU utilization can be caused by large keys."},{"id":"troubleshooting-7","title":"Troubleshooting","role":"errors","text":"To identify large keys, you can sample the keyspace using the Redis CLI.\n\nRun `redis-cli --memkeys` against your database to sample the keyspace in real time\nand potentially identify the largest keys in your database."},{"id":"remediation-1","title":"Remediation","role":"content","text":"Addressing a large key issue requires understanding why the application is creating large keys in the first place.\nAs such, it's difficult to provide general advice to solving this issue. Resolution often requires a change\nto the application's data model or the way it interacts with Redis."},{"id":"alerting","title":"Alerting","role":"content","text":"The Redis Software observability package includes a suite of alerts and their associated tests for use with Prometheus.\n\n- [Alerts for the v1 metrics endpoint](https://github.com/redis-field-engineering/redis-enterprise-observability/tree/main/prometheus)\n\n- [Alerts for the v2 metrics endpoint](https://github.com/redis-field-engineering/redis-enterprise-observability/tree/main/prometheus_v2)\n\n> [!NOTE]\n> Not all the alerts are appropriate for all environments; for example, installations that do not use persistence have no need for storage alerts.\n\nThe alerts are packaged with a series of tests\nthat validate the individual triggers. You can use these tests to validate your modifications to these alerts for specific environments and use cases.\n\n- [Tests for the v1 metrics endpoint](https://github.com/redis-field-engineering/redis-enterprise-observability/tree/main/prometheus/tests)\n\n- [Tests for the v2 metrics endpoint](https://github.com/redis-field-engineering/redis-enterprise-observability/tree/main/prometheus_v2/tests)\n\nTo use these alerts, install [Prometheus Alertmanager](https://prometheus.io/docs/alerting/latest/configuration/).\nFor a comprehensive guide to alerting with Prometheus and Grafana,\nsee the [Grafana blog post on the subject](https://grafana.com/blog/2020/02/25/step-by-step-guide-to-setting-up-prometheus-alertmanager-with-slack-pagerduty-and-gmail/)."},{"id":"configure-prometheus","title":"Configure Prometheus","role":"configuration","text":"To configure Prometheus for alerting, open the `prometheus.yml` configuration file.\n\nUncomment the `Alertmanager` section of the file.\nThe following configuration starts Alertmanager and instructs it to listen on its default port of 9093.\n\n[code example]\n\nThe Rule file section of the config file instructs Alertmanager to read specific rules files.\nIf you pasted the `alerts.yml` file into `/etc/prometheus` then the following configuration would be required.\n\n[code example]\n\nAfter you've done this, restart Prometheus.\n\nThe built-in configuration, `error_rules.yml`, has a single alert: Critical Connection Exception.\nIf you open the Prometheus console, by default located at port 9090, and select the Alert tab,\nyou will see this alert, as well as the alerts in any other file you have included as a rules file.\n\n![prometheus alerts image](https://redis.io/docs/latest/images/playbook_prometheus-alerts.png)\n\nThe following is a list of alerts contained in the `alerts.yml` file. There are several points to consider:\n\n- Not all Redis Software deployments export all metrics\n- Most metrics only alert if the specified trigger persists for a given duration"},{"id":"list-of-alerts","title":"List of alerts","role":"content","text":"| Description | Trigger |\n| ------ | :------ |\n|Average latency has reached a warning level | round(bdb_avg_latency * 1000) > 1 |\n|Average latency has reached a critical level indicating system degradation | round(bdb_avg_latency * 1000) > 4 |\n|Absence of any connection indicates improper configuration or firewall issue | bdb_conns < 1 |\n|A flood of connections has occurred that will impact normal operations | bdb_conns > 64000 |\n|Absence of any requests indicates improperly configured clients | bdb_total_req < 1 |\n|Excessive number of client requests indicates configuration and/or programmatic issues | bdb_total_req > 1000000 |\n|The database in question will soon be unable to accept new data | round((bdb_used_memory/bdb_memory_limit) * 100) > 98 |\n|The database in question will be unable to accept new data in two hours | round((bdb_used_memory/bdb_memory_limit) ** 100) < 98 and (predict_linear(bdb_used_memory[15m], 2 ** 3600) / bdb_memory_limit) > 0.3 and round(predict_linear(bdb_used_memory[15m], 2 * 3600)/bdb_memory_limit) > 0.98 |\n|Database read operations are failing to find entries more than 50% of the time | (100 * bdb_read_hits)/(bdb_read_hits + bdb_read_misses) < 50 |\n|In situations where TTL values are not set this indicates a problem | bdb_evicted_objects > 1 |\n|Replication between nodes is not in a satisfactory state | bdb_replicaof_syncer_status > 0 |\n|Record synchronization between nodes is not in a satisfactory state | bdb_crdt_syncer_status > 0 |\n|The amount by which replication lags behind events is worrisome | bdb_replicaof_syncer_local_ingress_lag_time > 500 |\n|The amount by which object replication lags behind events is worrisome | bdb_crdt_syncer_local_ingress_lag_time > 500 |\n|The number of active nodes is less than expected | count(node_up) != 3 |\n|Persistent storage will soon be exhausted | round((node_persistent_storage_free/node_persistent_storage_avail) * 100) <= 5 |\n|Ephemeral storage will soon be exhausted | round((node_ephemeral_storage_free/node_ephemeral_storage_avail) * 100) <= 5 |\n|The node in question is close to running out of memory | round((node_available_memory/node_free_memory) * 100) <= 15 |\n|The node in question has exceeded expected levels of CPU usage | round((1 - node_cpu_idle) * 100) >= 80 |\n|The shard in question is not reachable | redis_up == 0 |\n|The master shard is not reachable | floor(redis_master_link_status{role=\"slave\"}) < 1 |\n|The shard in question has exceeded expected levels of CPU usage | redis_process_cpu_usage_percent >= 80 |\n|The master shard has exceeded expected levels of CPU usage | redis_process_cpu_usage_percent{role=\"master\"} > 0.75 and redis_process_cpu_usage_percent{role=\"master\"} > on (bdb) group_left() (avg by (bdb)(redis_process_cpu_usage_percent{role=\"master\"}) + on(bdb) 1.2 * stddev by (bdb) (redis_process_cpu_usage_percent{role=\"master\"})) |\n|The shard in question has an unhealthily high level of connections | redis_connected_clients > 500 |"},{"id":"appendix-a-grafana-dashboards","title":"Appendix A: Grafana Dashboards","role":"content","text":"Grafana dashboards are available for Redis Software and Redis Cloud deployments.\n\nThese dashboards come in three styles, which may be used together to provide\na full picture of your deployment.\n\n1. Classic dashboards provide detailed information about the cluster, nodes, and individual databases.\n2. Basic dashboards provide a high-level overviews of the various cluster components.\n3. Extended dashboards. These require a third-party library to perform ReST calls.\n\nThere are also two workflow dashboards for Redis Software that provide drill-down functionality."},{"id":"software","title":"Software","role":"content","text":"- [Basic](https://github.com/redis-field-engineering/redis-enterprise-observability/tree/main/grafana/dashboards/grafana_v9-11/software/basic)\n- [Extended](https://github.com/redis-field-engineering/redis-enterprise-observability/tree/main/grafana/dashboards/grafana_v9-11/software/extended)\n- [Classic](https://github.com/redis-field-engineering/redis-enterprise-observability/tree/main/grafana/dashboards/grafana_v9-11/software/classic)"},{"id":"workflow","title":"Workflow","role":"content","text":"- [Database](https://github.com/redis-field-engineering/redis-enterprise-observability/tree/main/grafana/dashboards/grafana_v9-11/workflow/databases)\n- [Node](https://github.com/redis-field-engineering/redis-enterprise-observability/tree/main/grafana/dashboards/grafana_v9-11/workflow/nodes)"},{"id":"cloud","title":"Cloud","role":"content","text":"- [Basic](https://github.com/redis-field-engineering/redis-enterprise-observability/tree/main/grafana/dashboards/grafana_v9-11/cloud/basic)\n- [Extended](https://github.com/redis-field-engineering/redis-enterprise-observability/tree/main/grafana/dashboards/grafana_v9-11/cloud/extended)\n\n**Note:** - The 'workflow' dashboards are intended to be used as a package. Therefore they should all be installed, as they contain links to the other dashboards in the group permitting rapid navigation between the overview and the drill-down views."}],"examples":[{"id":"configure-prometheus-ex0","language":"plaintext","code":"# Alertmanager configuration\nalerting:\n  alertmanagers:\n    - static_configs:\n        - targets:\n          - alertmanager:9093","section_id":"configure-prometheus"},{"id":"configure-prometheus-ex1","language":"plaintext","code":"# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.\nrule_files:\n  - \"error_rules.yml\"\n  - \"alerts.yml\"","section_id":"configure-prometheus"}]}
{"schema_version":2,"id":"operate/rs/monitoring/v1_monitoring","title":"Metrics and alerts for monitoring v1","url":"https://redis.io/docs/latest/operate/rs/monitoring/v1_monitoring/","summary":"Monitor Redis Software clusters and databases using internal monitoring systems and external monitoring tools.","tags":["docs","operate","rs","kubernetes"],"last_updated":"2026-09-24T10:14:24+01:00","page_type":"content","content_hash":"4b521a7b0eab1a62fa41cb25fcc127027b0cd0ef5b6749319c14f6d76b60578a","sections":[{"id":"current-monitoring-system-deprecated","title":"Current monitoring system (deprecated)","role":"overview","text":"The current monitoring system, which is deprecated as of Redis Software version 7.22, consists of the following components:\n\n- Internal metrics storage:\n\n    - Metrics are internally aggregated, calculated, and stored for up to one year.\n\n    - This historical data is used to generate trends and performance insights over time.\n\n- [Statistics APIs](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/statistics):\n\n    - This set of RESTful APIs exposes metrics collected at regular intervals from clusters, nodes, databases, shards, and endpoints.\n\n    - These APIs allow customers to retrieve performance and usage statistics directly from the internal storage layer.\n\n- Cluster manager metrics and alerts:\n\n    - The Cluster Manager UI includes [dedicated metrics pages](#cluster-manager-metrics) that display pre-aggregated metrics.\n\n    - [Cluster alerts](#cluster-alerts) are triggered based on thresholds applied to these stored metrics.\n      \n- v1 Prometheus scraping endpoint:\n\n    - Redis Software exposes a legacy `/prometheus_metrics` endpoint to integrate with external observability platforms like [Prometheus and Grafana](https://redis.io/docs/latest/operate/rs/monitoring/get-started).\n\n    - This endpoint fetches data from the internal storage, providing basic monitoring integration."},{"id":"limitations","title":"Limitations","role":"limits","text":"The internal monitoring system, while functional, has several limitations that affect scalability and accuracy:\n      \n- **Limited granularity:** Metrics are aggregated before storage, resulting in a loss of fine-grained insights.\n\n- **Stale data:** Stored metrics can lag behind real-time system states, reducing the effectiveness of alerting.\n\n- **Visibility gaps during management operations:** Metrics and statistics may not be available or updated during management tasks, resulting in incomplete monitoring during critical activities.\n\n- **Scalability constraints:** Internal storage and processing introduce performance overhead and are not optimized for large-scale observability pipelines.\n\n- **Limited extensibility:** The system is tightly coupled with internal components, making it difficult to integrate with modern monitoring ecosystems."},{"id":"transition-to-the-metrics-stream-engine","title":"Transition to the metrics stream engine","role":"content","text":"To improve monitoring and address current limitations, Redis Software is transitioning to a new observability foundation: the [metrics stream engine](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine).\n\nThis modern monitoring stack introduces:\n\n- Real-time metrics, exposed directly from the engine without intermediate storage for high-fidelity, low-latency insights.\n\n- Scalable architecture designed for cloud-native observability with lightweight Prometheus collectors.\n\n- Deeper visibility by exposing new types of metrics such as key size distribution, server overall latency histograms, and system internals with per-endpoint resolution.\n\nWe recommend migrating to the metrics stream engine for enhanced accuracy, scalability, and future-proof observability.\n\nIf you are already using the existing scraping endpoint for integration, follow [this guide](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/prometheus-metrics-v1-to-v2) to transition and try the new engine. It is possible to scrape both existing and new endpoints simultaneously, allowing advanced dashboard preparation and a smooth transition."},{"id":"transition-cluster-manager-alerts","title":"Transition cluster manager alerts","role":"content","text":"As part of Redis Software's transition to the [new metrics stream engine](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine), some internal cluster manager alerts were deprecated in favor of external monitoring solutions. See the [alerts transition plan](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/alerts-v1-to-v2) for guidance."},{"id":"cluster-manager-metrics","title":"Cluster manager metrics","role":"content","text":"You can see the metrics of the cluster in:\n\n- **Cluster > Metrics**\n- **Node > Metrics** for each node\n- **Database > Metrics** for each database, including the shards for that database\n\nThe scale selector at the top of the page allows you to set the X-axis (time) scale of the graph.\n\nTo choose which metrics to display in the two large graphs at the top of the page:\n\n1. Hover over the graph you want to show in a large graph.\n1. Click on the right or left arrow to choose which side to show the graph.\n\nWe recommend that you show two similar metrics in the top graphs so you can compare them side-by-side.\n\nSee the following topics for metrics definitions:\n- [Database operations](https://redis.io/docs/latest/operate/rs/references/metrics/database-operations) for database metrics\n- [Resource usage](https://redis.io/docs/latest/operate/rs/references/metrics/resource-usage) for resource and database usage metrics\n- [Auto Tiering](https://redis.io/docs/latest/operate/rs/references/metrics/auto-tiering) for additional metrics for [Auto Tiering ](https://redis.io/docs/latest/operate/rs/databases/flash) databases"},{"id":"cluster-alerts","title":"Cluster alerts","role":"content","text":"In **Cluster > Alert Settings**, you can enable alerts for node or cluster events, such as high memory usage or throughput.\n\nConfigured alerts are shown:\n\n- As a notification on the status icon (  ) for the node and cluster\n- In the **log**\n- In email notifications, if you configure [email alerts](#send-alerts-by-email)\n\n> [!NOTE]\n> If you enable alerts for \"Node joined\" or \"Node removed\" actions,\n> you must also enable \"Receive email alerts\" so that the notifications are sent.\n\nTo enable alerts for a cluster:\n\n1. In **Cluster > Alert Settings**, click **Edit**. \n1. Select the alerts that you want to show for the cluster and click **Save**."},{"id":"database-alerts","title":"Database alerts","role":"content","text":"For each database, you can enable alerts for database events, such as high memory usage or throughput.\n\nConfigured alerts are shown:\n\n- As a notification on the status icon (  ) for the database\n- In the **log**\n- In emails, if you configure [email alerts](#send-alerts-by-email)\n\nTo enable alerts for a database:\n\n1. In **Configuration** for the database, click **Edit**.\n1. Select the **Alerts** section to open it.\n1. Select the alerts that you want to show for the database and click **Save**."},{"id":"send-alerts-by-email","title":"Send alerts by email","role":"content","text":"To send cluster and database alerts by email:\n\n1. In **Cluster > Alert Settings**, click **Edit**.\n1. Select **Set an email** to configure the [email server settings](https://redis.io/docs/latest/operate/rs/clusters/configure/cluster-settings#configuring-email-server-settings).\n1. In **Configuration** for the database, click **Edit**.\n1. Select the **Alerts** section to open it.\n1. Select **Receive email alerts** and click **Save**.\n1. In **Access Control**, select the [database and cluster alerts](https://redis.io/docs/latest/operate/rs/security/access-control/create-users#add-users) that you want each user to receive."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/networking/cluster-dns","title":"Configure cluster DNS","url":"https://redis.io/docs/latest/operate/rs/networking/cluster-dns/","summary":"Configure DNS to communicate between nodes in your cluster.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:24+01:00","page_type":"content","content_hash":"3bc14eb0fbc2047ca197db676635ae9c41987443757123811e19d36ab12e668f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"By default, Redis Software deployments use DNS to communicate between nodes.  You can also use the [Discovery Service](https://redis.io/docs/latest/operate/rs/databases/durability-ha/discovery-service), which uses IP addresses to connect and complies with the [Redis Sentinel API](https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel) supported by Redis Open Source.\n\nEach node in a Redis Software cluster includes a small DNS server to manage internal functions, such as high availability, automatic failover, automatic migration, and so on.\nNodes should only run the DNS server included with the software.  Running additional DNS servers can lead to unexpected behavior."},{"id":"cluster-name-and-connection-management","title":"Cluster name and connection management","role":"content","text":"Whether you're administering Redis Software or accessing databases, there are two ways to connect:\n\n- URL-based connections - URL-based connections use DNS to resolve the fully qualified cluster domain name (FQDN).  This means that DNS records might need to be updated when topology changes, such as adding (or removing) nodes from the cluster.  \n\n    Because apps and other client connections rely on the URL (rather than the address), they do not need to be modified when topology changes.  \n\n- IP-based connections - IP-based connections do not require DNS setup, as they rely on the underlying TCP/IP addresses.  As long as topology changes do not change the address of the cluster nodes, no configuration changes are needed, DNS or otherwise.  \n\n    However, changes to IP addresses (or changes to IP address access) impact all connections to the node, including apps and clients.  IP address changes can therefore be unpredictable or time-consuming."},{"id":"url-based-connections","title":"URL-based connections","role":"content","text":"The fully qualified domain name (FQDN) is the unique cluster identifier that enables clients to connect to the different components of Redis Software.\nThe FQDN is a crucial component of the high-availability mechanism because it's used internally to enable and implement automatic and transparent failover of nodes, databases, shards, and endpoints.\n\n> [!NOTE]\n> Setting the cluster's FQDN is a one-time operation, one that cannot be changed after being set.\n\nThe FQDN must always comply with the IETF's [RFC 952](https://datatracker.ietf.org/doc/html/rfc952) standard\nand section 2.1 of the [RFC 1123](https://datatracker.ietf.org/doc/html/rfc1123) standard."},{"id":"identify-the-cluster","title":"Identify the cluster","role":"content","text":"To identify the cluster, either use DNS to define a fully qualified domain name or use the IP addresses of each node."},{"id":"define-domain-using-dns","title":"Define domain using DNS","role":"content","text":"Use DNS if you:\n\n- have your own domain\n- want to integrate the cluster into that domain\n- can access and update the DNS records for that domain\n\n1. Make sure that the cluster and at least one node (preferably all nodes) in the cluster\n    are correctly configured in the DNS with the appropriate NS entries.\n\n    For example:\n\n    - Your domain is: `mydomain.com`\n    - You would like to name the Redis Software cluster `mycluster`\n    - You have three nodes in the cluster:\n        - node1 (IP address 1.1.1.1)\n        - node2 (2.2.2.2)\n        - node3 (3.3.3.3)\n\n1. In the FQDN field, enter the value `mycluster.mydomain.com`\n    and add the following records in the DNS table for `mydomain.com`:\n\n    [code example]"},{"id":"zeroconfiguration-using-mdns-development-option-only","title":"Zero-configuration using mDNS","role":"content","text":"Development and test environments can use [Multicast DNS](https://en.wikipedia.org/wiki/Multicast_DNS) (mDNS), a zero-configuration service designed for small networks.  Production environments should _not_ use mDNS.\n\nmDNS is a standard protocol that provides DNS-like name resolution and service discovery capabilities\nto machines on local networks with minimal to no configuration.\n\nBefore adopting mDNS, verify that it's supported by each client you wish to use to connect to your Redis databases.  Also make sure that your network infrastructure permits mDNS/multi-casting between clients and cluster nodes.\n\nConfiguring the cluster to support mDNS requires you to assign the cluster a `.local` name.\n\nFor example, if you want to name the Redis Software cluster `rediscluster`, specify the FQDN name as `rediscluster.local`.\n\nWhen using the DNS or mDNS option, failover can be done transparently and the DNS is updated automatically to point to the IP address of the new primary node."},{"id":"ip-based-connections","title":"IP-based connections","role":"content","text":"When you use the IP-based connection option, the FQDN does not need to have any special format\nbecause clients use IP addresses instead of hostnames to access the databases so you are free to choose whatever name you want.\nUsing the IP-based connection option does not require any DNS configuration either.\n\nTo administer the cluster you do need to know the IP address of at least one of the nodes in the cluster.\nOnce you have the IP address, you can simply connect to port number 8443 (for example: <https://10.0.0.12:8443>).\nHowever, as the topology of the cluster changes and node with the given IP address is removed,\nyou need to remember the IP address of another node participating in this cluster to connect to the Cluster Manager UI and manage the cluster.\n\nApplications connecting to Redis Software databases have the same constraints.\nWhen using the IP-based connection method, you can use the [Discovery Service](https://redis.io/docs/latest/operate/rs/databases/durability-ha/discovery-service)\nto discover the database endpoint for a given database name as long as you have an IP address for at least one of the nodes in the cluster.\nThe API used for discovery service is compliant with the Redis Sentinel API.\n\nTo test your connection, try pinging the service.  For help, see [Connect to your database](https://redis.io/docs/latest/operate/rs/databases/connect/test-client-connectivity)."}],"examples":[{"id":"define-domain-using-dns-ex0","language":" sh","code":"mycluster.mydomain.com        NS  node1.mycluster.mydomain.com\n                                      node2.mycluster.mydomain.com\n                                      node3.mycluster.mydomain.com \n    \n    node1.mycluster.mydomain.com  A   1.1.1.1\n    \n    node2.mycluster.mydomain.com  A   2.2.2.2\n    \n    node3.mycluster.mydomain.com  A   3.3.3.3","section_id":"define-domain-using-dns"}]}
{"schema_version":2,"id":"operate/rs/networking/cluster-lba-setup","title":"Set up a Redis Software cluster behind a load balancer","url":"https://redis.io/docs/latest/operate/rs/networking/cluster-lba-setup/","summary":"Set up a Redis Software cluster using a load balancer instead of DNS to direct traffic to cluster nodes.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"49c4c85f96748a3c9115fe5eff8ae373370b9d3871fae3da45576166a99dd00f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"To set up a Redis Software cluster in an environment that doesn't allow DNS, you can use a load balancer (LB) to direct traffic to the cluster nodes."},{"id":"dns-role-for-databases","title":"DNS role for databases","role":"content","text":"Normally, Redis Software uses DNS to provide dynamic database endpoints.\nA DNS name such as `redis-12345.clustername.domain` gives clients access to the database resource:\n\n- If multiple proxies are in use, the DNS name resolves to multiple IP addresses so clients can load balance.\n- On failover or topology changes, the DNS name is automatically updated to reflect the live IP addresses.\n\nWhen DNS cannot be used, clients can still connect to the endpoints with the IP addresses,\nbut the benefits of load balancing and automatic updates to IP addresses won't be available."},{"id":"network-architecture-with-load-balancer","title":"Network architecture with load balancer","role":"content","text":"You can compensate for the lack of DNS resolution with load balancers that can expose services and provide service discovery.\nA load balancer is configured in front of the Redis Software cluster, exposing several logical services:\n\n- Control plane services, such as the Cluster Manager UI\n- Data plane services, such as database endpoints for client application connections\n\nDepending on which Redis Software services you want to access outside the cluster, you may need to configure the load balancers separately.\nOne or more virtual IPs (VIPs) are defined on the load balancer to expose Redis Software services.\nThe architecture is shown in the following diagram with a 3-node Redis Software cluster with one database (DB1) configured on port 12000:\n\n![cluster-behind-load-balancer-top-down](https://redis.io/docs/latest/images/rs/cluster-behind-load-balancer-top-down.png)"},{"id":"set-up-a-cluster-with-load-balancers","title":"Set up a cluster with load balancers","role":"content","text":""},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"- [Install](https://redis.io/docs/latest/operate/rs/installing-upgrading) the latest version of Redis Software on your clusters\n- Configure the cluster with the cluster name (FQDN) even though DNS is not in use.\n    Remember that the same cluster name is used to issue the license keys.\n    We recommend that you use a \".local\" suffix in the FQDN."},{"id":"configure-load-balancers","title":"Configure load balancers","role":"configuration","text":"- Make sure that the load balancer is performing TCP health checks on the cluster nodes.\n- Expose the services that you require through a virtual IP, for example:\n    - Cluster Manager UI on port 8443\n    - Rest API on port 9443 for secure HTTPS connections and port 8080 for HTTP\n    - Database ports 10000-19999\n\nOther ports are shown in the list of [Redis Software network ports](https://redis.io/docs/latest/operate/rs/networking/port-configurations).\n\n> [!NOTE]\n> Sticky, secured connections are needed only for the Redis Software Cluster Manager UI on port 8443.\n>\n> - Certain load balancers provide specific logic to close idle connections. Either turn off this feature or make sure the applications connecting to Redis use reconnection logic.\n> - Make sure the load balancer is fast enough to resolve connections between two clusters or applications that are connected to Redis databases through a load balancer.\n> - Choose the standard load balancer that is commonly used in your environment so that you have easy access to in-house expertise for troubleshooting issues."},{"id":"configure-cluster","title":"Configure cluster","role":"configuration","text":"For clusters behind load balancers, we recommend using the `all-nodes` [proxy policy](https://redis.io/docs/latest/operate/rs/databases/configure/proxy-policy) and enabling `handle_redirects`.\n\nTo allow inbound connections to be terminated on the relevant node inside the cluster, run the following `rladmin` commands on the cluster:\n\n[code example]\n\nOptionally configure sparse shard placement to allow closer termination of client connections to where the Redis shard is located:\n\n[code example]"},{"id":"configure-database","title":"Configure database","role":"configuration","text":"After you update the cluster settings and configure the load balancers, you can go to the Redis Software Cluster Manager UI at `https://load-balancer-virtual-ip:8443/` and [create a new database](https://redis.io/docs/latest/operate/rs/databases/create).\n\nTo create an Active-Active database, use the `crdb-cli` utility. See the [`crdb-cli` reference](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli) for more information about creating Active-Active databases from the command line."},{"id":"update-load-balancer-configuration-when-cluster-configuration-changes","title":"Update load balancer configuration when cluster configuration changes","role":"content","text":"When your Redis Software cluster is behind a load balancer, you must update the load balancer when the cluster topology and IP addresses change.\nSome common cases that require you to update the load balancer are:\n\n- Adding new nodes to the Redis Software cluster\n- Removing nodes from the Redis Software cluster\n- Maintenance for Redis Software cluster nodes\n- IP address changes for Redis Software cluster nodes\n\nAfter these changes, make sure that the Redis connections in your applications can connect to the Redis database,\nespecially if they are directly connected on IP addresses that have changed."},{"id":"intercluster-communication-considerations","title":"Intercluster communication considerations","role":"content","text":"Redis Software supports several topologies that allow intercluster replication, such as [Replica Of](https://redis.io/docs/latest/operate/rs/databases/import-export/replica-of) and [Active-Active](https://redis.io/docs/latest/operate/rs/databases/active-active) deployment options.\nWhen your Redis Software clusters are behind load balancers, you must allow some network services to be open and defined in the load balancers to allow the replication to work."},{"id":"replica-of","title":"Replica Of","role":"content","text":"For Replica Of communication to work, you must expose database ports locally in each cluster and allow these ports through any firewalls between the clusters."},{"id":"active-active","title":"Active-Active","role":"content","text":"For Active-Active communication to work, you must expose several ports, including every database port and several control plane ports as defined in [Network port configurations](https://redis.io/docs/latest/operate/rs/networking/port-configurations). Pay attention to services that include \"Active-Active\" in the connection source column, and allow these ports through any firewalls between the clusters."}],"examples":[{"id":"configure-cluster-ex0","language":"sh","code":"# Enable all-nodes proxy policy by default\nrladmin tune cluster default_sharded_proxy_policy all-nodes default_non_sharded_proxy_policy all-nodes\n\n# Redirect where necessary when behind a load balancer\nrladmin cluster config handle_redirects enabled","section_id":"configure-cluster"},{"id":"configure-cluster-ex1","language":"sh","code":"# Enable sparse placement by default\nrladmin tune cluster default_shards_placement sparse","section_id":"configure-cluster"}]}
{"schema_version":2,"id":"operate/rs/networking/configuring-aws-route53-dns-redis-enterprise","title":"AWS Route53 DNS management","url":"https://redis.io/docs/latest/operate/rs/networking/configuring-aws-route53-dns-redis-enterprise/","summary":"How to configure AWS Route 53 DNS","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"7dea12532687ffbce9ad64e0ea231e3be67e5aca2cdd80bbd918afcb8012179e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software uses DNS to achieve high availability and fail-over regardless of where it is installed."},{"id":"what-is-aws-route-53","title":"What is AWS Route 53?","role":"content","text":"Route 53 is a scalable DNS service by Amazon Web Service (AWS). It routes user traffic to AWS resources and external sites, offering DNS health checks, traffic management, and failover capabilities. It's integral for high-availability architectures and also provides domain registration services."},{"id":"create-a-hosted-zone","title":"Create a hosted zone","role":"content","text":"Creating a hosted zone in Amazon Route 53 is a foundational step in managing your domain's DNS settings.\n\nA hosted zone functions as a container for the DNS records of a specific domain. To create one, you first need to:\n\n1. Log into the AWS Management Console\n2. Navigate to the Route 53 dashboard\n3. Select \"Create Hosted Zone\"\n4. Enter your domain name, and choose public hosted zone\n\nThe hosted zone provides you with a set of Name Server (NS) records, which you will need to update at your domain registrar. This process effectively delegates the DNS management of your domain to Route 53, allowing you to create, update, and manage DNS records for your domain within the AWS ecosystem.\n\n![](https://redis.io/docs/latest/images/rs/00-CreateHostedZone-en.png)\n\nOnce created, it will appear in the list of **Hosted zones**\n\n![](https://redis.io/docs/latest/images/rs/03-HostedZoneSelection-en.png)"},{"id":"create-glue-records","title":"Create glue records","role":"content","text":"A **glue record** is a type of DNS record that helps prevent circular dependencies by providing the IP addresses of your nameservers. To create glue records in Route 53, you first need to set up a hosted zone for your domain. You will create a separate A record for each node in your Redis Software cluster. The **Record name** will be a subdomain definition of the NS record you will define and the **value** should be set to the IP address of the node in your cluster.\n\n![](https://redis.io/docs/latest/images/rs/05-NS1Configuration-en.png)\n\nOnce complete, it should look something like this\n\n![](https://redis.io/docs/latest/images/rs/06-NSList-en.png)"},{"id":"create-nameserver-record","title":"Create nameserver record","role":"content","text":"When you create a new hosted zone in Route 53 for your domain, a set of NS records is automatically generated. These records list the nameservers assigned by Route 53 to your domain.\n\nYou will need to create a new NS record which will point to the glue records created in the previous step.\n\n> [!NOTE]\n> It is important to make sure that the **Record Name** of the NS record equals the FQDN (Fully Qualified Domain Name) of your Redis Software cluster. If not, DNS resolution will not function correctly.\n\n![](https://redis.io/docs/latest/images/rs/07-NSRecord-en.png)"},{"id":"validate","title":"Validate","role":"content","text":"Once all steps are completed, the configuration should look similar to this\n\n![](https://redis.io/docs/latest/images/rs/08-FinalConfig-en.png)\n\nYou can test and validate your settings by using the sh\ndig ns test.demo-rlec.redislabs.com\n\n; <<>> DiG 9.9.5-9+deb8u9-Debian <<>> ns test.demo-rlec.redislabs.com\n;; global options: +cmd\n;; Got answer:\n;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 25061\n;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version: 0, flags:; udp: 4096\n;; QUESTION SECTION:\n;test.demo-rlec.redislabs.com. IN NS\n\n;; ANSWER SECTION:\ntest.demo-rlec.redislabs.com. 3409 IN NS node2.test.demo-rlec.redislabs.com.\ntest.demo-rlec.redislabs.com. 3409 IN NS node1.test.demo-rlec.redislabs.com.\ntest.demo-rlec.redislabs.com. 3409 IN NS node3.test.demo-rlec.redislabs.com.\n\n;; Query time: 31 msec\n;; SERVER: 192.168.1.254#53(192.168.1.254)\n;; WHEN: Tue Feb 14 16:49:13 CET 2017\n;; MSG SIZE  rcvd: 120\n```\n\nYou can see that the name are given a prefix of `ns-`. This answer does not come\nfrom *Route53* but from the cluster nameservers themselves."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/networking","title":"Manage networks","url":"https://redis.io/docs/latest/operate/rs/networking/","summary":"Networking features and considerations for designing your Redis Software deployment.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","children":[{"id":"operate/rs/networking/configuring-aws-route53-dns-redis-enterprise","summary":"How to configure AWS Route 53 DNS","title":"AWS Route53 DNS management","url":"https://redis.io/docs/latest/operate/rs/networking/configuring-aws-route53-dns-redis-enterprise/"},{"id":"operate/rs/networking/cluster-lba-setup","summary":"Set up a Redis Software cluster using a load balancer instead of DNS to direct traffic to cluster nodes.","title":"Set up a Redis Software cluster behind a load balancer","url":"https://redis.io/docs/latest/operate/rs/networking/cluster-lba-setup/"},{"id":"operate/rs/networking/cluster-dns","summary":"Configure DNS to communicate between nodes in your cluster.","title":"Configure cluster DNS","url":"https://redis.io/docs/latest/operate/rs/networking/cluster-dns/"},{"id":"operate/rs/networking/multi-ip-ipv6","summary":"Information and requirements for using multiple IP addresses or IPv6 addresses with Redis Software.","title":"Manage IP addresses","url":"https://redis.io/docs/latest/operate/rs/networking/multi-ip-ipv6/"},{"id":"operate/rs/networking/mdns","summary":"Requirements for using the mDNS protocol in development and testing environments.","title":"Client prerequisites for mDNS","url":"https://redis.io/docs/latest/operate/rs/networking/mdns/"},{"id":"operate/rs/networking/port-configurations","summary":"This document describes the various network port ranges and their uses.","title":"Network port configurations","url":"https://redis.io/docs/latest/operate/rs/networking/port-configurations/"},{"id":"operate/rs/networking/private-public-endpoints","summary":"Describes how to enable public and private endpoints for databases on a cluster.","title":"Enable private and public database endpoints","url":"https://redis.io/docs/latest/operate/rs/networking/private-public-endpoints/"}],"page_type":"index","sections":[],"examples":[]}
{"schema_version":2,"id":"operate/rs/networking/mdns","title":"Client prerequisites for mDNS","url":"https://redis.io/docs/latest/operate/rs/networking/mdns/","summary":"Requirements for using the mDNS protocol in development and testing environments.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:24+01:00","page_type":"content","content_hash":"64ef0a63b49ac82fcd62caf070f1fd196605493d7b28995be7b91573c9c0d47f","sections":[{"id":"content","title":"Content","role":"content","text":"> [!NOTE]\n> mDNS is only supported for development and testing environments.\n\nIf you choose to use the mDNS protocol when [you set the cluster name](https://redis.io/docs/latest/operate/rs/networking/cluster-dns),\nmake sure that the configurations and prerequisites for resolving database endpoints are met on the client machines.\nIf you have [Replica Of](https://redis.io/docs/latest/operate/rs/databases/import-export/replica-of) databases on the cluster,\nthe configurations and prerequisites are also required for the Redis Software nodes.\n\nTo prepare a client or node for mDNS:\n\n1. Make sure that the clients and cluster nodes are on the same physical network\n    or have the network infrastructure configured to allow multicasting between them.\n1. Install these prerequisite packages:\n\n    - For Ubuntu:\n\n        [code example]\n\n    - For RHEL/CentOS 6.x:\n\n        [code example]\n\n    - For RHEL/CentOS 7:\n\n        [code example]\n\n1. If you are using [mDNS with IPv6 addresses](https://redis.io/docs/latest/operate/rs/networking/multi-ip-ipv6),\n    update the hosts line in `/etc/nsswitch.conf` to:\n\n    [code example]"}],"examples":[{"id":"content-ex0","language":"sh","code":"apt-get install libnss-mdns","section_id":"content"},{"id":"content-ex1","language":"sh","code":"$ rpm -ivh http://download.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm\n        $ yum install nss-mdns\n        $ service avahi-daemon start","section_id":"content"},{"id":"content-ex2","language":"sh","code":"$ rpm -ivh https://dl.fedoraproject.org/pub/epel/7/x86_64/Packages/e/epel-release-7-12.noarch.rpm\n        $ yum install nss-mdns\n        $ service avahi-daemon start","section_id":"content"},{"id":"content-ex3","language":"yaml","code":"hosts: files mdns4_minimal\n    \\[NOTFOUND=return\\] mdns","section_id":"content"}]}
{"schema_version":2,"id":"operate/rs/networking/multi-ip-ipv6","title":"Manage IP addresses","url":"https://redis.io/docs/latest/operate/rs/networking/multi-ip-ipv6/","summary":"Information and requirements for using multiple IP addresses or IPv6 addresses with Redis Software.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:24+01:00","page_type":"content","content_hash":"ad1019bcc69b9c2d3a557530b12522a25e4270138a5baa48d0a9c868dafe5e18","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software supports servers, instances, and VMs with\nmultiple IPv4 or IPv6 addresses."},{"id":"traffic-overview","title":"Traffic overview","role":"content","text":"Redis Software traffic is divided into internal traffic and external traffic:\n\n- \"Internal traffic\" refers to internal cluster communications, such as communications between the nodes for cluster management.\n\n- \"External traffic\" refers to communications between clients and databases and connections to the Cluster Manager UI.\n\nWhen only one IP address exists on a machine that serves as a Redis Software node, it is used for both internal and external traffic."},{"id":"multiple-ip-addresses","title":"Multiple IP addresses","role":"content","text":"During node configuration on a machine with multiple IP addresses, you must assign one address for internal traffic and one or more other addresses for external traffic.\n\nIf the cluster uses IPv4 for internal traffic, all communication between cluster nodes uses IPv4 addresses. If the cluster uses IPv6 for internal traffic, all communication between cluster nodes uses IPv6 addresses.\n\nTo update IP address configuration after cluster setup, see [Change internal IP address](#change-internal-ip-address) or [Configure external IP addresses](#configure-external-ip-addresses)."},{"id":"enable-ipv6-for-internal-traffic","title":"Enable IPv6 for internal traffic","role":"content","text":"IPv6 for internal communication is supported only for new clusters with Redis Software version 7.4.2 or later.\n\nIf the server has only IPv6 interfaces, IPv6 is automatically used for internal and external traffic. Otherwise, internal traffic uses IPv4 by default. \n\nTo use IPv6 for internal traffic on a machine with both IPv4 and IPv6 interfaces, set `use_internal_ipv6` to `true` when you create a cluster using the [bootstrap REST API request](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bootstrap#post-bootstrap):\n\n[code example]\n\nWhen other IPv6 nodes join a cluster that has `use_internal_ipv6` enabled, they automatically use IPv6 for internal traffic. Do not manually set `use_internal_ipv6` when joining a node to an existing IPv6 cluster, or a `NodeBootstrapError` can occur if the values do not match.\n\nIf you try to add a node without an IPv6 interface to a cluster that has `use_internal_ipv6` enabled, a `NodeBootstrapError` occurs.\n\nThe host file `/etc/hosts` on each node in the cluster must include the following entry:\n\n[code example]"},{"id":"change-internal-ip-address","title":"Change internal IP address","role":"content","text":"Before you change an internal IP address, consider the following:\n\n- Verify the address is valid and bound to an active interface on the node. Failure to do so prevents the node from coming back online and rejoining the cluster.\n\n- Joining a node that only has IPv4 network interfaces to a master node with IPv6 enabled causes a `NodeBootstrapError`.\n\n- Joining a node that only has IPv6 network interfaces to a master node that does not have IPv6 enabled causes a `NodeBootstrapError`.\n\n- You cannot change the internal address from IPv4 to IPv6 or IPv6 to IPv4 in a running cluster. You can only change the internal address within the same protocol as the cluster.\n\nIf you need to update the internal IP address in the OS, one option is to remove that node from the cluster, change the IP address, and then add the node back into the cluster.\n\nAlternatively, you can use the following steps to update a node's internal IP address without removing it from the cluster:\n\n1. Turn the node into a replica using [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/node/enslave):\n\n    [code example]\n\n1. Deactivate the `rlec_supervisor` service on the node:\n\n    [code example]\n\n1. Restart the node.\n\n1. Follow the operating system vendor's instructions to change the node's IP address.\n\n1. From a different cluster node, use [`rladmin node addr set`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/node/addr) to update the first node's IP address:\n\n    [code example]\n\n1. Enable the `rlec_supervisor` service on the node:\n\n    [code example]\n\n1. Restart `rlec_supervisor` or restart the node.\n\n\n    [code example]\n\n1. Verify the node rejoined the cluster:\n\n    [code example]\n\nRepeat this procedure for other cluster nodes to change their internal IP addresses."},{"id":"configure-external-ip-addresses","title":"Configure external IP addresses","role":"configuration","text":"You can configure external addresses that are not bound to an active interface, but are otherwise mapped or configured to route traffic to the node (such as AWS Elastic IPs or a load balancer VIP).\n\nYou can use [rladmin node external_addr](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/node/external-addr) to change a node's external IP addresses.\n\nAdd an external IP address:\n\n[code example]\n\nSet one or more external IP addresses:\n\n[code example]\n\n\nRemove an external IP address:\n\n[code example]\n\n> [!NOTE]\n> While [joining a new node to a\n> cluster](https://redis.io/docs/latest/operate/rs/clusters/add-node)\n> during the node bootstrap process,\n> when prompted to provide an IP of an existing node in the cluster,\n> if you use the node's IP, provide the node's internal IP address."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"- Using IPv6 for internal traffic is supported only for new clusters running Redis Software version 7.4.2 or later.\n\n- Changing an existing cluster's internal traffic from IPv4 to IPv6 is not supported.\n\n- All nodes must use the same protocol for internal traffic.\n\n- If a Redis Software node's host machine has both IPv4 and IPv6 addresses, internal communication within the node initially uses IPv4 until the bootstrap process finishes."}],"examples":[{"id":"enable-ipv6-for-internal-traffic-ex0","language":"sh","code":"POST /v1/bootstrap/create_cluster\n{\n    \"action\": \"create_cluster\",\n    \"cluster\": { \n        \"name\": \"cluster.fqdn\" \n    },\n    \"credentials\": {\n       \"username\": \"admin_username\",\n       \"password\": \"admin_password\"\n    },\n    \"node\": {\n       \"identity\": {\n          \"addr\": \"2001:DB8::/32\",\n          \"external_addr\": [\"2001:0db8:85a3:0000:0000:8a2e:0370:7334\"],\n          \"use_internal_ipv6\": true\n       },\n    },\n    ...\n}","section_id":"enable-ipv6-for-internal-traffic"},{"id":"enable-ipv6-for-internal-traffic-ex1","language":"sh","code":"::1 localhost","section_id":"enable-ipv6-for-internal-traffic"},{"id":"change-internal-ip-address-ex0","language":"sh","code":"rladmin node <ID> enslave demote_node","section_id":"change-internal-ip-address"},{"id":"change-internal-ip-address-ex1","language":"sh","code":"systemctl disable rlec_supervisor","section_id":"change-internal-ip-address"},{"id":"change-internal-ip-address-ex2","language":"sh","code":"rladmin node <ID> addr set <IP address>","section_id":"change-internal-ip-address"},{"id":"change-internal-ip-address-ex3","language":"sh","code":"systemctl enable rlec_supervisor","section_id":"change-internal-ip-address"},{"id":"change-internal-ip-address-ex4","language":"sh","code":"systemctl start rlec_supervisor","section_id":"change-internal-ip-address"},{"id":"change-internal-ip-address-ex5","language":"sh","code":"rladmin status nodes","section_id":"change-internal-ip-address"},{"id":"configure-external-ip-addresses-ex0","language":"sh","code":"rladmin node <ID> external_addr add <IP address>","section_id":"configure-external-ip-addresses"},{"id":"configure-external-ip-addresses-ex1","language":"sh","code":"rladmin node <ID> external_addr set <IP address 1> <IP address N>","section_id":"configure-external-ip-addresses"},{"id":"configure-external-ip-addresses-ex2","language":"sh","code":"rladmin node <ID> external_addr remove <IP address>","section_id":"configure-external-ip-addresses"}]}
{"schema_version":2,"id":"operate/rs/networking/port-configurations","title":"Network port configurations","url":"https://redis.io/docs/latest/operate/rs/networking/port-configurations/","summary":"This document describes the various network port ranges and their uses.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:24+01:00","page_type":"content","content_hash":"2650e935f1863e0f41fb539fe9cd20bc5f76bb970457a27b384544a262d77bdc","sections":[{"id":"overview","title":"Overview","role":"overview","text":"All Redis Software deployments span multiple physical/virtual nodes. You'll need to keep several ports open between these nodes. This document describes the various port ranges and their uses.\n\n> [!NOTE]\n> Whenever you create a new database, you must verify that the ports assigned to the new database's endpoints are open. The cluster will not perform this verification for you."},{"id":"ports-and-port-ranges-used-by-redis-software","title":"Ports and port ranges used by Redis Software","role":"content","text":"Redis Software's port usage falls into three general categories:\n\n- Internal: For traffic between or within cluster nodes\n- External: For traffic from client applications or external monitoring resources\n- Active-Active: For traffic to and from clusters hosting Active-Active databases\n\n| Protocol | Port | Configurable | Connection source | Description |\n|----------|------|--------------|-------------------|-------------|\n| TCP | 8001 | <span title=\"Not configurable\">&#x274c; No</span> | Internal, External | Traffic from application to Redis Software [Discovery Service](https://redis.io/docs/latest/operate/rs/databases/durability-ha/discovery-service) |\n| TCP | 8070 | <span title=\"Not configurable\">&#x274c; No</span> | External | Metrics exported and managed by the web proxy |\n| TCP | 3347-3349, 8000, 8071, 9091, 9125 | <span title=\"Not configurable\">&#x274c; No</span> | Internal | Internal metrics ports |\n| TCP | 8443 | <span title=\"Configurable\">&#x2705; Yes</span> | Internal, External | Secure (HTTPS) access to the management web UI |\n| TCP | 9081 | <span title=\"Configurable\">&#x2705; Yes</span> | Internal | CRDB coordinator for Active-Active management (internal) |\n| TCP | 9082 | <span title=\"Not configurable\">&#x274c; No</span> | Internal | Cluster API internal port |\n| TCP | 9443, 8080, 3346 | <span title=\"Configurable\">&#x2705; Yes</span> | Internal, External, Active-Active | REST API traffic, including cluster management and node bootstrap |\n| TCP | 10050 | <span title=\"Not configurable\">&#x274c; No</span> | Internal | Zabbix monitoring |\n| TCP | 10000-10049, 10051-19999 | <span title=\"Configurable\">&#x2705; Yes</span> | Internal, External, Active-Active | Database traffic |\n| UDP | 53, 5353 | <span title=\"Not configurable\">&#x274c; No</span> | Internal, External | DNS/mDNS traffic |\n| TCP | 1968 | <span title=\"Not configurable\">&#x274c; No</span> | Internal | Proxy traffic |\n| TCP | 3333-3345, 3350-3354, 36379 | <span title=\"Not configurable\">&#x274c; No</span> | Internal | Internode communication |\n| TCP | 3355 | <span title=\"Configurable\">&#x2705; Yes</span> | Internal | Authentication service internal port |\n| TCP | 3357 | <span title=\"Not configurable\">&#x274c; No</span> | Internal | Internal communication |\n| TCP | 20000-29999 | <span title=\"Not configurable\">&#x274c; No</span> | Internal | Database shard traffic |\n| TCP | 8002, 8004, 8006 | <span title=\"Configurable\">&#x2705; Yes</span> | Internal | Default system health monitoring (envoy admin, envoy management server, gossip envoy admin)|\n| TCP | 8444, 9080 | <span title=\"Not configurable\">&#x274c; No</span> | Internal | Traffic between web proxy and cnm_http/cm |\n\n> [!NOTE]\n> The cluster uses ports 20000-29999 for internal shard traffic. You can't change the range, but `reserved_ports` excludes specific ports or port ranges from shard assignment."},{"id":"change-port-configuration","title":"Change port configuration","role":"content","text":""},{"id":"reserve-ports","title":"Reserve ports","role":"content","text":"Redis Software reserves some ports by default (`system_reserved_ports`). To reserve other ports or port ranges and prevent the cluster from assigning them to database endpoints or internal shard traffic, configure `reserved_ports` using one of the following methods:\n\n- [rladmin cluster config](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/config)\n\n    [code example]\n\n    For example:\n\n    [code example]\n\n- [Update cluster settings](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster#put-cluster) REST API request\n\n    [code example]\n\n    For example:\n\n    [code example]"},{"id":"change-the-cluster-manager-ui-port","title":"Change the Cluster Manager UI port","role":"content","text":"The Redis Software Cluster Manager UI uses port 8443, by default. You can change this to a custom port as long as the new port is not in use by another process.\n\nTo change this port, run:\n\n[code example]\n\nAfter you change the Cluster Manager UI port, use the new port in the URL when accessing the UI on any node in the cluster. Example: `https://newnode.mycluster.example.com:<custom-port>`"},{"id":"change-the-envoy--ports","title":"Change the envoy  ports","role":"content","text":"For system health monitoring, Redis uses the following ports by default:\n\n- Port 8002 for envoy admin\n\n- Port 8004 for envoy management server\n\n- Port 8006 for gossip envoy admin\n\nYou can change each envoy port to a custom port using the [`rladmin cluster config`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/config) command as long as the new port is not in use by another process. When you change `envoy_admin_port`, expect a restart of envoy.\n\nTo change the envoy admin port, run:\n\n[code example]\n\nTo change the envoy management server port, run:\n\n[code example]\n\nTo change the gossip envoy admin port, run:\n\n[code example]"},{"id":"change-the-rest-api-port","title":"Change the REST API port","role":"content","text":"For the REST API, Redis Software uses port 9443 (secure) and port 8080 (not secure), by default. You can change this to a custom port as long as the new port is not in use by another process.\n\nTo change these ports, run:\n\n[code example]\n\n[code example]"},{"id":"os-conflicts-with-port-53","title":"OS conflicts with port 53","role":"content","text":"If port 53 is in use, the installation fails. This issue can occur in default installations of certain operating systems in which `systemd-resolved` (DNS server) or `dnsmasq` is running.\n\nTo prevent this issue, change the system configuration to make this port available before installation.\n\nTo prevent `systemd-resolved` from using port 53:\n\n1. Edit `/etc/systemd/resolved.conf`: \n\n    [code example]\n\n1. Add `DNSStubListener=no` as the last line in the file and save the file.\n\n1. Rename the current `/etc/resolv.conf` file:\n\n    [code example]\n\n1. Create a symbolic link for `/etc/resolv.conf`:\n\n    [code example]\n\n    > [!NOTE]\n    > You might encounter a temporary name resolution error (`sudo: unable to resolve host {hostname}: Temporary failure in name resolution`), which should be fixed when you restart `systemd-resolved` in the next step.\n\n2. Restart the DNS service:\n\n    [code example]\n\nTo prevent `dnsmasq` from using port 53:\n\n1. Stop the `dnsmasq` service if it's running:\n\n    [code example]\n\n1. Prevent `dnsmasq` from starting automatically at system boot:\n\n    [code example]\n\n1. Mask `dnsmasq` to prevent it from being started manually or by other services:\n\n    [code example]\n\n1. Verify `dnsmasq` is no longer active and won't start at system boot:\n\n    [code example]"},{"id":"update-sysctlconf-to-avoid-port-collisions","title":"Update `sysctl.conf` to avoid port collisions","role":"content","text":"To avoid port collision, update `/etc/sysctl.conf` to include:\n\n[code example]"},{"id":"configure-https","title":"Configure HTTPS","role":"configuration","text":""},{"id":"require-https-for-api-endpoints","title":"Require HTTPS for API endpoints","role":"content","text":"By default, the Redis Software API supports communication over HTTP and HTTPS. However, you can turn off HTTP support to ensure that API requests are encrypted.\n\nBefore you turn off HTTP support, make sure you migrate any scripts or proxy configurations that use HTTP to the encrypted API endpoint to prevent broken connections.\n\nTo turn off HTTP support for API endpoints, run:\n\n[code example]\n\nAfter you turn off HTTP support, traffic sent to the unencrypted API endpoint is blocked."},{"id":"http-to-https-redirection","title":"HTTP to HTTPS redirection","role":"content","text":"Starting with version 6.0.12, you cannot use automatic HTTP to HTTPS redirection.\nTo poll metrics from the `metrics_exporter` or to access the Cluster Manager UI, use HTTPS in your request. HTTP requests won't be automatically redirected to HTTPS for those services."},{"id":"nodes-on-different-vlans","title":"Nodes on different VLANs","role":"content","text":"Nodes in the same cluster must reside on the same VLAN. If you can't\nhost the nodes on the same VLAN, then you must open [all ports](https://redis.io/docs/latest/operate/rs/networking/port-configurations) between them."}],"examples":[{"id":"reserve-ports-ex0","language":"sh","code":"rladmin cluster config reserved_ports <list of ports/port ranges>","section_id":"reserve-ports"},{"id":"reserve-ports-ex1","language":"sh","code":"rladmin cluster config reserved_ports 11000 13000-13010 20048","section_id":"reserve-ports"},{"id":"reserve-ports-ex2","language":"sh","code":"PUT /v1/cluster\n    { \"reserved_ports\": [\"list of ports/port ranges\"] }","section_id":"reserve-ports"},{"id":"reserve-ports-ex3","language":"sh","code":"PUT /v1/cluster\n    { \"reserved_ports\": [\"11000\", \"13000-13010\", \"20048\"] }","section_id":"reserve-ports"},{"id":"change-the-cluster-manager-ui-port-ex0","language":"sh","code":"rladmin cluster config cm_port <custom-port>","section_id":"change-the-cluster-manager-ui-port"},{"id":"change-the-envoy--ports-ex0","language":"sh","code":"$ rladmin cluster config envoy_admin_port <new-port>\nUpdating envoy_admin_port... restarting now","section_id":"change-the-envoy--ports"},{"id":"change-the-envoy--ports-ex1","language":"sh","code":"$ rladmin cluster config envoy_mgmt_server_port <new-port>\nCluster configured successfully","section_id":"change-the-envoy--ports"},{"id":"change-the-envoy--ports-ex2","language":"sh","code":"$ rladmin cluster config gossip_envoy_admin_port <new-port>\nCluster configured successfully","section_id":"change-the-envoy--ports"},{"id":"change-the-rest-api-port-ex0","language":"sh","code":"rladmin cluster config cnm_http_port <new-port>","section_id":"change-the-rest-api-port"},{"id":"change-the-rest-api-port-ex1","language":"sh","code":"rladmin cluster config cnm_https_port <new-port>","section_id":"change-the-rest-api-port"},{"id":"os-conflicts-with-port-53-ex0","language":"sh","code":"sudo vi /etc/systemd/resolved.conf","section_id":"os-conflicts-with-port-53"},{"id":"os-conflicts-with-port-53-ex1","language":"sh","code":"sudo mv /etc/resolv.conf /etc/resolv.conf.orig","section_id":"os-conflicts-with-port-53"},{"id":"os-conflicts-with-port-53-ex2","language":"sh","code":"sudo ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf","section_id":"os-conflicts-with-port-53"},{"id":"os-conflicts-with-port-53-ex3","language":"sh","code":"sudo service systemd-resolved restart","section_id":"os-conflicts-with-port-53"},{"id":"os-conflicts-with-port-53-ex4","language":"sh","code":"sudo systemctl stop dnsmasq","section_id":"os-conflicts-with-port-53"},{"id":"os-conflicts-with-port-53-ex5","language":"sh","code":"sudo systemctl disable dnsmasq","section_id":"os-conflicts-with-port-53"},{"id":"os-conflicts-with-port-53-ex6","language":"sh","code":"sudo systemctl mask dnsmasq","section_id":"os-conflicts-with-port-53"},{"id":"os-conflicts-with-port-53-ex7","language":"sh","code":"sudo systemctl status dnsmasq","section_id":"os-conflicts-with-port-53"},{"id":"update-sysctlconf-to-avoid-port-collisions-ex0","language":" sh","code":"net.ipv4.ip_local_port_range = 30000 65535","section_id":"update-sysctlconf-to-avoid-port-collisions"},{"id":"require-https-for-api-endpoints-ex0","language":"sh","code":"rladmin cluster config http_support disabled","section_id":"require-https-for-api-endpoints"}]}
{"schema_version":2,"id":"operate/rs/networking/private-public-endpoints","title":"Enable private and public database endpoints","url":"https://redis.io/docs/latest/operate/rs/networking/private-public-endpoints/","summary":"Describes how to enable public and private endpoints for databases on a cluster.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"4437a44ea5bc3214b414ae3ba0ea16f3d9deb67bc560f718046fe1e305b36164","sections":[{"id":"content","title":"Content","role":"content","text":"Each node in Redis Software can be configured with [private and external IP addresses](https://redis.io/docs/latest/operate/rs/networking/multi-ip-ipv6). By default, Redis Software databases expose a single endpoint, e.g. cluster.com (FQDN), using the external IP addresses, making it available to the public network (e.g. the internet). Additionally, the cluster can be configured to expose a private FQDN, which utilizes the private IP addresses for access from the private network only (e.g. VPC or an internal network).\n\nWhen you create a cluster via the UI, you can configure it to expose private and public endpoints.\nThis is common for environments such as cloud platforms and enterprises.\n\nWhen doing so, the cluster creates an additional FQDN, e.g. internal.cluster.com for private network (e.g. VPC or an internal network), while the cluster.com FQDN can be used by a public network (e.g. the internet).\n\nYou can enable public and private endpoints during cluster creation only.\nHowever, you can still add an additional FQDN in a different domain (cluster.io, for example) after cluster creation.\n\nTo enable private and public endpoints:\n\n1. Verify the IP addresses are bound to the server or instance.\n\n1. During cluster setup, turn on **Enable public endpoints support** in the **Cluster** screen's **Configuration** section.\n\n    ![The endpoint support setting appears in the **Configuration section** of the **Cluster** setup screen.](https://redis.io/docs/latest/images/rs/screenshots/cluster/setup/enable-public-endpoints.png)\n\n    If this setting is not enabled when the cluster is created, databases on the cluster support only a single endpoint.\n\n1. Select **Next** to proceed to **Node** configuration.\n\n1. In the **Network configuration** section:\n\n    1. Configure the machine's public IP address for external traffic.\n\n    1. Configure the private IP address for both internal and external traffic so it can be used for private database endpoints.\n\nAfter cluster creation, both sets of endpoints are available for databases in the cluster.\n\nTo view and copy public and private endpoints for a database in the cluster, see the database's **Configuration > General** section.\n\n![View public and private endpoints from the General section of the database's Configuration screen.](https://redis.io/docs/latest/images/rs/screenshots/databases/config-general-endpoints.png)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/alerts/alerts-v1-to-v2","title":"Transition cluster manager alerts to Prometheus alerts","url":"https://redis.io/docs/latest/operate/rs/references/alerts/alerts-v1-to-v2/","summary":"Transition from internal cluster manager alerts to external monitoring alerts using Prometheus.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:21+01:00","page_type":"content","content_hash":"56b2d6797817287ad77405ea3f42c3816673688a665508dfee7f1bc224630d20","sections":[{"id":"content","title":"Content","role":"content","text":"As Redis Software transitions from the [deprecated monitoring system](https://redis.io/docs/latest/operate/rs/monitoring/v1_monitoring) to the [new metrics stream engine](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine), some internal cluster manager alerts were deprecated in favor of external monitoring solutions.\n\nYou can use the following table to transition from the deprecated alerts and set up equivalent alerts in Prometheus with [PromQL (Prometheus Query Language)](https://prometheus.io/docs/prometheus/latest/querying/basics/):\n\n| Cluster manager alert | Equivalent PromQL | Description |\n|-----------------------|-------------------|-------------|\n| BdbSizeAlert | <span class=\"break-all\">`sum by(db, cluster) (redis_server_used_memory) / sum by(db, cluster) (redis_server_maxmemory) > 0.8`</span> | Redis server memory usage exceeds 80% |\n| NodeMemoryAlert | <span class=\"break-all\">`(node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes > 0.7`</span> | Node memory usage exceeds 70% |\n| NodeFreeFlashAlert | <span class=\"break-all\">`(node_available_flash_bytes - node_bigstore_free_bytes) / node_available_flash_bytes > 0.7`</span> | Node flash storage usage exceeds 70% |\n| NodeEphemeralStorageAlert | <span class=\"break-all\">`(node_ephemeral_storage_avail_bytes - node_ephemeral_storage_free_bytes) / node_ephemeral_storage_avail_bytes > 0.7`</span> | Node ephemeral storage usage exceeds 70% |\n| NodePersistentStorageAlert | <span class=\"break-all\">`(node_persistent_storage_avail_bytes - node_persistent_storage_free_bytes) / node_persistent_storage_avail_bytes > 0.7`</span> | Node persistent storage usage exceeds 70% |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/alerts","title":"Alerts","url":"https://redis.io/docs/latest/operate/rs/references/alerts/","summary":"Documents the alerts that are tracked with Redis Software.","tags":["docs","operate","rs","rc"],"last_updated":"2026-09-24T10:14:21+01:00","children":[{"id":"operate/rs/references/alerts/alerts-v1-to-v2","summary":"Transition from internal cluster manager alerts to external monitoring alerts using Prometheus.","title":"Transition cluster manager alerts to Prometheus alerts","url":"https://redis.io/docs/latest/operate/rs/references/alerts/alerts-v1-to-v2/"}],"page_type":"content","content_hash":"fb3ae91383fdec6179ab90deab15381fe5caf47fd034ce097a61a3567ee346d4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Cluster alerts are triggered based on thresholds applied to these stored metrics."},{"id":"cluster-alerts","title":"Cluster alerts","role":"content","text":"In **Cluster > Alert Settings**, you can enable alerts for node or cluster events, such as high memory usage or throughput.\n\nConfigured alerts are shown:\n\n- As a notification on the status icon (  ) for the node and cluster\n- In the **log**\n- In email notifications, if you configure [email alerts](#send-alerts-by-email)\n\n> [!NOTE]\n> If you enable alerts for \"Node joined\" or \"Node removed\" actions,\n> you must also enable \"Receive email alerts\" so that the notifications are sent.\n\nTo enable alerts for a cluster:\n\n1. In **Cluster > Alert Settings**, click **Edit**. \n1. Select the alerts that you want to show for the cluster and click **Save**."},{"id":"database-alerts","title":"Database alerts","role":"content","text":"For each database, you can enable alerts for database events, such as high memory usage or throughput.\n\nConfigured alerts are shown:\n\n- As a notification on the status icon (  ) for the database\n- In the **log**\n- In emails, if you configure [email alerts](#send-alerts-by-email)\n\nTo enable alerts for a database:\n\n1. In **Configuration** for the database, click **Edit**.\n1. Select the **Alerts** section to open it.\n1. Select the alerts that you want to show for the database and click **Save**."},{"id":"send-alerts-by-email","title":"Send alerts by email","role":"content","text":"To send cluster and database alerts by email:\n\n1. In **Cluster > Alert Settings**, click **Edit**.\n1. Select **Set an email** to configure the [email server settings](https://redis.io/docs/latest/operate/rs/clusters/configure/cluster-settings#configure-email-server-settings).\n1. In **Configuration** for the database, click **Edit**.\n1. Select the **Alerts** section to open it.\n1. Select **Receive email alerts** and click **Save**.\n1. In **Access Control**, select the [database and cluster alerts](https://redis.io/docs/latest/operate/rs/security/access-control/create-users#add-users) that you want each user to receive."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/crdb-cli/crdb/add-instance","title":"crdb-cli crdb add-instance","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/add-instance/","summary":"Adds a peer replica to an Active-Active database.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:21+01:00","page_type":"content","content_hash":"9629e8f8e477a6c589787f4d89becbf39e716cac4ed5be20f81d09004147f96f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Adds a peer replica to an existing Active-Active database in order to host the database on another cluster. This creates an additional active instance of the database on the specified cluster.\n\n[code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameter | Value   | Description |\n|-----------|---------|-------------|\n| crdb-guid | string  | The GUID of the database (required) |\n| instance fqdn=\\<cluster_fqdn\\>,username=\\<username\\>,password=\\<password\\>,url=https://\\<hostname-or-IP\\>:9443,replication_endpoint=\\<hostname-or-IP\\>:\\<port\\>,replication_tls_sni=\\<hostname\\> | strings | The connection information for the new participating cluster (required)<br/><br/>**Required:**<br/>• `fqdn` - Cluster fully qualified domain name<br/>• Either a username and password, or certificate credentials:<br/>&nbsp;&nbsp;- `username` and `password` - Cluster username and password<br/>&nbsp;&nbsp;- `client_cert` and `client_key` - Client certificate and its private key, as PEM strings<br/><br/>Don't provide both for the same cluster. The command fails with `Use either username/password or client_cert/client_key/trusted_ca, not both`.<br/><br/>**Optional:**<br/>• `trusted_ca` - CA that validates the API certificate the peer cluster presents, as a PEM string. Only valid with certificate credentials. If you omit it, the cluster uses the certificates in its `mtls_trusted_ca.pem` file.<br/>• `url` - URL to access the cluster's REST API<br/>• `replication_endpoint` - Address to access the database instance for peer replication<br/>• `replication_tls_sni` - Cluster [Server Name Indication (SNI)](https://en.wikipedia.org/wiki/Server_Name_Indication) hostname for TLS connections<br/><br/>See [Certificate-based authentication for cluster management](https://redis.io/docs/latest/operate/rs/security/certificates/certificate-based-authentication#certificate-based-authentication-for-cluster-management). |\n| compression | 0-6 | The level of data compression: <br /><br > 0 = No compression <br /><br > 6 = High compression and resource load (Default: 3) |\n| wait | | Prevents `crdb-cli` from running another command before this command finishes |\n| no-wait | | `crdb-cli` can run another command before this command finishes |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns the task ID of the task that is adding the new instance.\n\nIf `--no-wait` is specified, the command exits. Otherwise, it will wait for the instance to be added and return `finished`."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"sh","code":"crdb-cli crdb add-instance --crdb-guid <guid>\n         --instance fqdn=<cluster_fqdn>,{ username=<username>,password=<password> | client_cert=<cert>,client_key=<key>[,trusted_ca=<ca>] }[,url=https://<hostname-or-IP>:9443,replication_endpoint=<hostname-or-IP>:<port>,replication_tls_sni=<hostname>]\n         [--compression <0-6>]\n         [--wait | --no-wait]","section_id":"overview"},{"id":"example-ex0","language":"sh","code":"$ crdb-cli crdb add-instance --crdb-guid db6365b5-8aca-4055-95d8-7eb0105c0b35 \\\n        --instance fqdn=cluster2.redis.local,username=admin@redis.local,password=admin-password\nTask f809fae7-8e26-4c8f-9955-b74dbbd47949 created\n  ---> Status changed: queued -> started\n  ---> Status changed: started -> finished","section_id":"example"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/crdb-cli/crdb/create","title":"crdb-cli crdb create","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/create/","summary":"Creates an Active-Active database.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:21+01:00","page_type":"content","content_hash":"92dcd4c0228037069aeb683ad85782151ea510236b9525b66dee8afd7d4c9d2c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Creates an Active-Active database.\n\n[code example]"},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before you create an Active-Active database, you must have:\n\n- At least two participating clusters\n- [Network connectivity](https://redis.io/docs/latest/operate/rs/networking/port-configurations) between the participating clusters"},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameter&nbsp;&&nbsp;options(s)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;                                                                             | Value                                           | Description                                                                                                                                                                                                                  |\n|---------------------------------------------------------------------------------------|-------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| name \\<CRDB_name\\>                                                                  | string                                          | Name of the Active-Active database (required)                                                                                                                                                                                |\n| memory-size \\<maximum_memory\\>                                                                | size in bytes, megabytes (MB), or gigabytes (GB) | Maximum database memory (required)                                                                                                                                                                                           |\n| instance fqdn=\\<cluster_fqdn\\>,username=\\<username\\>,password=\\<password\\>,url=https://\\<hostname-or-IP\\>:9443,replication_endpoint=\\<hostname-or-IP\\>:\\<port\\>,replication_tls_sni=\\<hostname\\>         | strings                                         | The connection information for the participating clusters (required for each participating cluster)<br/><br/>**Required:**<br/>• `fqdn` - Cluster fully qualified domain name<br/>• Either a username and password, or certificate credentials:<br/>&nbsp;&nbsp;- `username` and `password` - Cluster username and password<br/>&nbsp;&nbsp;- `client_cert` and `client_key` - Client certificate and its private key, as PEM strings<br/><br/>Don't provide both for the same cluster. The command fails with `Use either username/password or client_cert/client_key/trusted_ca, not both`.<br/><br/>**Optional:**<br/>• `trusted_ca` - CA that validates the API certificate the peer cluster presents, as a PEM string. Only valid with certificate credentials. If you omit it, the cluster uses the certificates in its `mtls_trusted_ca.pem` file.<br/>• `url` - URL to access the cluster's REST API<br/>• `replication_endpoint` - Address to access the database instance for peer replication<br/>• `replication_tls_sni` - Cluster [Server Name Indication (SNI)](https://en.wikipedia.org/wiki/Server_Name_Indication) hostname for TLS connections<br/><br/>See [Certificate-based authentication for cluster management](https://redis.io/docs/latest/operate/rs/security/certificates/certificate-based-authentication#certificate-based-authentication-for-cluster-management). |\n| port \\<port_number\\>                                                                 | integer                                         | TCP port for the Active-Active database on all participating clusters                                                                                                                                                        |\n| default-db-config \\<configuration\\>                                                  | string                                          | Default database configuration options. For a list of database settings, see the [CRDB database config object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb/database_config) reference.|\n| default-db-config-file \\<filename\\>                                                  | filepath                                        | Default database configuration options from a file. For a list of database settings, see the [CRDB database config object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb/database_config) reference.|\n| wait                                                                               |                                                 | Prevents `crdb-cli` from running another command before this command finishes                                                                                                                                                  |\n| no-wait                                                                            |                                                 | `crdb-cli` can run another command before this command finishes                                                                                                                                                 |\n| compression                                                                           | 0-6                                             | The level of data compression: <br /><br > 0 = No compression <br /><br > 6 = High compression and resource load (Default: 3)                                                                                                            |\n| causal-consistency                                                                    | true <br/> false (default: false)                        | [Causal consistency](https://redis.io/docs/latest/operate/rs/databases/active-active/causal-consistency) applies updates to all instances in the order they were received                                                     |\n| password \\<password\\>                                                                | string                                          | Password for access to the database                                                                                                                                                                                          |\n| replication                                                                           | true <br/> false (*default*)                        | Activates or deactivates [database replication](https://redis.io/docs/latest/operate/rs/databases/durability-ha/replication) where every master shard replicates to a replica shard                                                       |\n| encryption                                                                            | true <br/> false (*default*)                        | Activates or deactivates encryption                                                                                                                                                                                          |\n| sharding                                                                              | true (*default*)<br/> false                         | Activates or deactivates sharding (also known as [database clustering](https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering)). Cannot be updated after the database is created                                   |\n| shards-count \\<number_of_shards\\>                                                              | integer                                         | If sharding is enabled, this specifies the number of Redis shards for each database instance                                                                                                                                 |\n| oss-cluster                                                                           | true<br/>false (*default*)                               | Activates [OSS cluster API](https://redis.io/docs/latest/operate/rs/clusters/optimize/oss-cluster-api)                                                                                                                                |\n| oss-sharding                                                                          | true<br/>false                               | Use the OSS sharding policy instead of regex rules                                                                                                                                |\n| shard-key-regex \\<regex_rule\\>                                                       | string                                          | If clustering is enabled, this defines a regex rule (also known as a [hashing policy](https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering#custom-hashing-policy)) that determines which keys are located in each shard (defaults to `{u'regex': u'.*\\\\{(?<tag>.*)\\\\}.*'}, {u'regex': u'(?<tag>.*)'} `) |\n| bigstore                                                                              | true <br/> <br/> false (*default*)                        | If true, the database uses Auto Tiering to add flash memory to the database                                                                                                                                                |\n| bigstore-ram-size \\<size\\>                                                           | size in bytes, megabytes (MB), or gigabytes (GB) | Maximum RAM limit for  databases with Auto Tiering enabled                                                                                                                                                                           |\n| with-module name=\\<module_name\\>,version=\\<module_version\\>,args=\\<module_args\\> | strings                                         | Creates a database with a specific module                                                                                                                                                                                    |\n| eviction-policy                                                     | noeviction (*default*)<br/>allkeys-lru<br/>allkeys-lfu<br/>allkeys-random<br/>volatile-lru<br/>volatile-lfu<br/>volatile-random<br/>volatile-ttl | Sets [eviction policy](https://redis.io/docs/latest/operate/rs/databases/memory-performance/eviction-policy)                                                                                                          |\n| proxy-policy                                                                          | all-nodes<br>all-master-shards<br>single        | Sets proxy policy |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns the task ID of the task that is creating the database.\n\nIf `--no-wait` is specified, the command exits. Otherwise, it will wait for the database to be created and then return the CRDB GUID."},{"id":"examples","title":"Examples","role":"example","text":"[code example]\n\nTo create an Active-Active database with two shards in each instance and with encrypted traffic between the clusters:\n\n[code example]\n\nTo create an Active-Active database with two shards and with RediSearch 2.0.6 module:\n\n[code example]\n\nTo create an Active-Active database with two shards and with encrypted traffic between the clusters:\n\n[code example]\n\nTo create an Active-Active database with 1 shard in each instance and not wait for the response:\n\n[code example]\n\nTo create an Active-Active database where one participating cluster uses certificate credentials instead of a username and password:\n\n[code example]"}],"examples":[{"id":"overview-ex0","language":"sh","code":"crdb-cli crdb create --name <name>\n         --memory-size <maximum_memory>\n         --instance fqdn=<cluster1.example.com>,{ username=<username>,password=<password> | client_cert=<cert>,client_key=<key>[,trusted_ca=<ca>] }[,url=https://<hostname-or-IP>:9443,replication_endpoint=<hostname-or-IP>:<port>,replication_tls_sni=<hostname>]\n         --instance fqdn=<cluster2.example.com>,{ username=<username>,password=<password> | client_cert=<cert>,client_key=<key>[,trusted_ca=<ca>] }[,url=https://<hostname-or-IP>:9443,replication_endpoint=<hostname-or-IP>:<port>,replication_tls_sni=<hostname>]\n         [--port <port_number>]\n         [--wait | --no-wait]\n         [--default-db-config <configuration>]\n         [--default-db-config-file <filename>]\n         [--compression <0-6>]\n         [--causal-consistency { true | false } ]\n         [--password <password>]\n         [--replication { true | false } ]\n         [--encryption { true | false } ]\n         [--sharding { false | true } ]\n         [--shards-count <number_of_shards>]\n         [--shard-key-regex <regex_rule>]\n         [--oss-cluster { true | false } ]\n         [--oss-sharding { true | false } ]\n         [--bigstore { true | false }]\n         [--bigstore-ram-size <maximum_memory>]\n         [--eviction-policy { noeviction | allkeys-lru | allkeys-lfu | allkeys-random | volatile-lru | volatile-lfu | volatile-random | volatile-ttl }]\n         [--proxy-policy { all-nodes | all-master-shards | single }]\n         [--with-module name=<module_name>,version=<module_version>,args=<module_args>]","section_id":"overview"},{"id":"examples-ex0","language":"sh","code":"$ crdb-cli crdb create --name database1 --memory-size 1GB --port 12000 \\\n           --instance fqdn=cluster1.redis.local,username=admin@redis.local,password=admin \\\n           --instance fqdn=cluster2.redis.local,username=admin@redis.local,password=admin\nTask 633aaea3-97ee-4bcb-af39-a9cb25d7d4da created\n  ---> Status changed: queued -> started\n  ---> CRDB GUID Assigned: crdb:d84f6fe4-5bb7-49d2-a188-8900e09c6f66\n  ---> Status changed: started -> finished","section_id":"examples"},{"id":"examples-ex1","language":"sh","code":"crdb-cli crdb create --name mycrdb --memory-size 100mb --port 12000 --instance fqdn=cluster1.redis.local,username=admin@redis.local,password=admin --instance fqdn=cluster2.redis.local,username=admin@redis.local,password=admin --shards-count 2 --encryption true","section_id":"examples"},{"id":"examples-ex2","language":"sh","code":"crdb-cli crdb create --name mycrdb --memory-size 100mb --port 12000 --instance fqdn=cluster1.redis.local,username=admin@redis.local,password=admin --instance fqdn=cluster2.redis.local,username=admin@redis.local,password=admin --shards-count 2 --with-module name=search,version=\"2.0.6\",args=\"PARTITIONS AUTO\"","section_id":"examples"},{"id":"examples-ex3","language":"sh","code":"crdb-cli crdb create --name mycrdb --memory-size 100mb --port 12000 --instance fqdn=cluster1.redis.local,username=admin@redis.local,password=admin --instance fqdn=cluster2.redis.local,username=admin@redis.local,password=admin --encryption true --shards-count 2","section_id":"examples"},{"id":"examples-ex4","language":"sh","code":"crdb-cli crdb create --name mycrdb --memory-size 100mb --port 12000 --instance fqdn=cluster1.redis.local,username=admin@redis.local,password=admin --instance fqdn=cluster2.redis.local,username=admin@redis.local,password=admin --no-wait","section_id":"examples"},{"id":"examples-ex5","language":"sh","code":"crdb-cli crdb create --name mycrdb --memory-size 100mb --port 12000 \\\n           --instance fqdn=cluster1.redis.local,username=admin@redis.local,password=admin \\\n           --instance fqdn=cluster2.redis.local,client_cert=<client-cert>,client_key=<client-key>,trusted_ca=<trusted-ca>","section_id":"examples"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/crdb-cli/crdb/delete","title":"crdb-cli crdb delete","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/delete/","summary":"Deletes an Active-Active database.","tags":["docs","operate","rs"],"last_updated":"2023-12-14T15:27:49+01:00","page_type":"content","content_hash":"bc5ec45e968a8e74020a986475eb0f7d8a61f92f617e7adb290bed63a27b546c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Deletes an Active-Active database.\n\n[code example]\n\nThis command is irreversible. If the data in your database is important, back it up before you delete the database."},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameter           | Value  | Description                         |\n|---------------------|--------|-------------------------------------|\n| crdb-guid | string | The GUID of the database (required) |\n| no-wait             |        | Does not wait for the task to complete |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns the task ID of the task that is deleting the database.\n\nIf `--no-wait` is specified, the command exits. Otherwise, it will wait for the database to be deleted and return `finished`."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"sh","code":"crdb-cli crdb delete --crdb-guid <guid>\n         [ --no-wait ]","section_id":"overview"},{"id":"example-ex0","language":"sh","code":"$ crdb-cli crdb delete --crdb-guid db6365b5-8aca-4055-95d8-7eb0105c0b35\nTask dfe6cacc-88ff-4667-812e-938fd05fe359 created\n  ---> Status changed: queued -> started\n  ---> Status changed: started -> finished","section_id":"example"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/crdb-cli/crdb/flush","title":"crdb-cli crdb flush","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/flush/","summary":"Clears all keys from an Active-Active database.","tags":["docs","operate","rs"],"last_updated":"2026-04-24T09:17:44-05:00","page_type":"content","content_hash":"3c1cf2bfa9c5f5b8461e65864b46312af137877606af7a6dcde98aa965a7ec92","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Clears all keys from an Active-Active database.\n\n[code example]\n\nThis command is irreversible. If the data in your database is important, back it up before you flush the database.\n\nRun flush from only one participating cluster. The flush operation propagates to all other clusters automatically."},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameter           | Value  | Description                         |\n|---------------------|--------|-------------------------------------|\n| crdb-guid  | string | The GUID of the database (required) |\n| no-wait             |        | Does not wait for the task to complete |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns the task ID of the task clearing the database.\n\nIf `--no-wait` is specified, the command exits. Otherwise, it will wait for the database to be cleared and return `finished`."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"sh","code":"crdb-cli crdb flush --crdb-guid <guid>\n         [ --no-wait ]","section_id":"overview"},{"id":"example-ex0","language":"sh","code":"$ crdb-cli crdb flush --crdb-guid d84f6fe4-5bb7-49d2-a188-8900e09c6f66\nTask 53cdc59e-ecf5-4564-a8dd-448d71f9e568 created\n  ---> Status changed: queued -> started\n  ---> Status changed: started -> finished","section_id":"example"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/crdb-cli/crdb/get","title":"crdb-cli crdb get","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/get/","summary":"Shows the current configuration of an Active-Active database.","tags":["docs","operate","rs"],"last_updated":"2023-12-14T15:27:49+01:00","page_type":"content","content_hash":"9cd1e1d271702e2b46cfdccd5dced592eb8e8b0b660a6d1999f36ebb145a9d21","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Shows the current configuration of an Active-Active database.\n\n[code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameter           | Value  | Description                         |\n|---------------------|--------|-------------------------------------|\n| crdb-guid  | string | The GUID of the database (required) |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns the current configuration of the database."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"sh","code":"crdb-cli crdb get --crdb-guid <guid>","section_id":"overview"},{"id":"example-ex0","language":"sh","code":"$ crdb-cli crdb get --crdb-guid d84f6fe4-5bb7-49d2-a188-8900e09c6f66\nCRDB-GUID: d84f6fe4-5bb7-49d2-a188-8900e09c6f66\nName: database1\nEncryption: False\nCausal consistency: False\nProtocol version: 1\nFeatureSet version: 5\nModules: []\nDefault-DB-Config:\n  memory_size: 1073741824\n  port: 12000\n  replication: True\n  shard_key_regex: [{'regex': '.*\\\\{(?<tag>.*)\\\\}.*'}, {'regex': '(?<tag>.*)'}]\n  sharding: True\n  shards_count: 1\n  tls_mode: disabled\n  rack_aware: None\n  data_persistence: None\n  authentication_redis_pass: None\n  authentication_admin_pass: None\n  oss_sharding: None\n  oss_cluster: None\n  proxy_policy: None\n  shards_placement: None\n  oss_cluster_api_preferred_ip_type: None\n  bigstore: None\n  bigstore_ram_size: None\n  aof_policy: None\n  snapshot_policy: None\n  max_aof_load_time: None\n  max_aof_file_size: None\nInstance:\n  Id: 1\n  Cluster:\n    FQDN: cluster1.redis.local\n    URL: https://cluster1.redis.local:9443\n    Replication-Endpoint: <Default>\n    Replication TLS SNI: <Default>\n  Compression: 3\n  DB-Config:\n    authentication_admin_pass: \n    replication: None\n    rack_aware: None\n    memory_size: None\n    data_persistence: None\n    tls_mode: None\n    authentication_redis_pass: None\n    port: None\n    shards_count: None\n    shard_key_regex: None\n    oss_sharding: None\n    oss_cluster: None\n    proxy_policy: None\n    shards_placement: None\n    oss_cluster_api_preferred_ip_type: None\n    bigstore: None\n    bigstore_ram_size: None\n    aof_policy: None\n    snapshot_policy: None\n    max_aof_load_time: None\n    max_aof_file_size: None\nInstance:\n  Id: 2\n  Cluster:\n    FQDN: cluster2.redis.local\n    URL: https://cluster2.redis.local:9443\n    Replication-Endpoint: <Default>\n    Replication TLS SNI: <Default>\n  Compression: 3\n  DB-Config:\n    authentication_admin_pass: \n    replication: None\n    rack_aware: None\n    memory_size: None\n    data_persistence: None\n    tls_mode: None\n    authentication_redis_pass: None\n    port: None\n    shards_count: None\n    shard_key_regex: None\n    oss_sharding: None\n    oss_cluster: None\n    proxy_policy: None\n    shards_placement: None\n    oss_cluster_api_preferred_ip_type: None\n    bigstore: None\n    bigstore_ram_size: None\n    aof_policy: None\n    snapshot_policy: None\n    max_aof_load_time: None\n    max_aof_file_size: None","section_id":"example"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/crdb-cli/crdb/health-report","title":"crdb-cli crdb health-report","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/health-report/","summary":"Shows the health report of an Active-Active database.","tags":["docs","operate","rs"],"last_updated":"2023-12-14T15:27:49+01:00","page_type":"content","content_hash":"9df7f5bb746b2500970d35f8ce6e15118ba7222725a4bd646fb42ad4cfa6db52","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Shows the health report of the API management layer of an Active-Active database.\n\n[code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameter           | Value  | Description                         |\n|---------------------|--------|-------------------------------------|\n| crdb-guid  | string | The GUID of the database (required) |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns the health report of the API management layer of the database."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"sh","code":"crdb-cli crdb health-report --crdb-guid <guid>","section_id":"overview"},{"id":"example-ex0","language":"sh","code":"$ crdb-cli crdb health-report --crdb-guid d84f6fe4-5bb7-49d2-a188-8900e09c6f66\n[\n  {\n    \"active_config_version\":1,\n    \"cluster_name\":\"cluster2.redis.local\",\n    \"configurations\":[\n      {\n        \"causal_consistency\":false,\n        \"encryption\":false,\n        \"featureset_version\":5,\n        \"instances\":[\n          {\n            \"cluster\":{\n              \"name\":\"cluster1.redis.local\",\n              \"url\":\"https:\\/\\/cluster1.redis.local:9443\"\n            },\n            \"db_uid\":\"\",\n            \"id\":1\n          },\n          {\n            \"cluster\":{\n              \"name\":\"cluster2.redis.local\",\n              \"url\":\"https:\\/\\/cluster2.redis.local:9443\"\n            },\n            \"db_uid\":\"1\",\n            \"id\":2\n          }\n        ],\n        \"name\":\"database1\",\n        \"protocol_version\":1,\n        \"status\":\"commit-completed\",\n        \"version\":1\n      }\n    ],\n    \"connections\":[\n      {\n        \"name\":\"cluster1.redis.local\",\n        \"status\":\"ok\"\n      },\n      {\n        \"name\":\"cluster2.redis.local\",\n        \"status\":\"ok\"\n      }\n    ],\n    \"guid\":\"d84f6fe4-5bb7-49d2-a188-8900e09c6f66\",\n    \"name\":\"database1\",\n    \"connection_error\":null\n  },\n  {\n    \"active_config_version\":1,\n    \"cluster_name\":\"cluster1.redis.local\",\n    \"configurations\":[\n      {\n        \"causal_consistency\":false,\n        \"encryption\":false,\n        \"featureset_version\":5,\n        \"instances\":[\n          {\n            \"cluster\":{\n              \"name\":\"cluster1.redis.local\",\n              \"url\":\"https:\\/\\/cluster1.redis.local:9443\"\n            },\n            \"db_uid\":\"4\",\n            \"id\":1\n          },\n          {\n            \"cluster\":{\n              \"name\":\"cluster2.redis.local\",\n              \"url\":\"https:\\/\\/cluster2.redis.local:9443\"\n            },\n            \"db_uid\":\"\",\n            \"id\":2\n          }\n        ],\n        \"name\":\"database1\",\n        \"protocol_version\":1,\n        \"status\":\"commit-completed\",\n        \"version\":1\n      }\n    ],\n    \"connections\":[\n      {\n        \"name\":\"cluster1.redis.local\",\n        \"status\":\"ok\"\n      },\n      {\n        \"name\":\"cluster2.redis.local\",\n        \"status\":\"ok\"\n      }\n    ],\n    \"guid\":\"d84f6fe4-5bb7-49d2-a188-8900e09c6f66\",\n    \"name\":\"database1\",\n    \"connection_error\":null\n  }\n]","section_id":"example"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/crdb-cli/crdb","title":"crdb-cli crdb commands","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/","summary":"Manage Active-Active databases.","tags":["docs","operate","rs"],"last_updated":"2024-02-26T13:11:00+01:00","children":[{"id":"operate/rs/references/cli-utilities/crdb-cli/crdb/add-instance","summary":"Adds a peer replica to an Active-Active database.","title":"crdb-cli crdb add-instance","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/add-instance/"},{"id":"operate/rs/references/cli-utilities/crdb-cli/crdb/create","summary":"Creates an Active-Active database.","title":"crdb-cli crdb create","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/create/"},{"id":"operate/rs/references/cli-utilities/crdb-cli/crdb/delete","summary":"Deletes an Active-Active database.","title":"crdb-cli crdb delete","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/delete/"},{"id":"operate/rs/references/cli-utilities/crdb-cli/crdb/flush","summary":"Clears all keys from an Active-Active database.","title":"crdb-cli crdb flush","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/flush/"},{"id":"operate/rs/references/cli-utilities/crdb-cli/crdb/get","summary":"Shows the current configuration of an Active-Active database.","title":"crdb-cli crdb get","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/get/"},{"id":"operate/rs/references/cli-utilities/crdb-cli/crdb/health-report","summary":"Shows the health report of an Active-Active database.","title":"crdb-cli crdb health-report","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/health-report/"},{"id":"operate/rs/references/cli-utilities/crdb-cli/crdb/list","summary":"Shows a list of all Active-Active databases.","title":"crdb-cli crdb list","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/list/"},{"id":"operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance","summary":"Deletes data from a local instance and removes it from the Active-Active database.","title":"crdb-cli crdb purge-instance","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance/"},{"id":"operate/rs/references/cli-utilities/crdb-cli/crdb/remove-instance","summary":"Removes a peer replica from an Active-Active database.","title":"crdb-cli crdb remove-instance","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/remove-instance/"},{"id":"operate/rs/references/cli-utilities/crdb-cli/crdb/update","summary":"Updates the configuration of an Active-Active database.","title":"crdb-cli crdb update","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/update/"},{"id":"operate/rs/references/cli-utilities/crdb-cli/crdb/upgrade","summary":"Upgrades an Active-Active database.","title":"crdb-cli crdb upgrade","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/upgrade/"}],"page_type":"content","content_hash":"a0268d7973b4719172d48ee53545e0a1a07209f60b7fffae47481cc6b6c16839","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Use `crdb-cli crdb` commands to manage Active-Active databases."},{"id":"crdb-cli-crdb-commands","title":"`crdb-cli crdb` commands","role":"content","text":"| Command | Description |\n| --- | --- |\n| [add-instance](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/add-instance/) | Adds a peer replica to an Active-Active database. |\n| [create](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/create/) | Creates an Active-Active database. |\n| [delete](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/delete/) | Deletes an Active-Active database. |\n| [flush](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/flush/) | Clears all keys from an Active-Active database. |\n| [get](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/get/) | Shows the current configuration of an Active-Active database. |\n| [health-report](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/health-report/) | Shows the health report of an Active-Active database. |\n| [list](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/list/) | Shows a list of all Active-Active databases. |\n| [purge-instance](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance/) | Deletes data from a local instance and removes it from the Active-Active database. |\n| [remove-instance](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/remove-instance/) | Removes a peer replica from an Active-Active database. |\n| [update](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/update/) | Updates the configuration of an Active-Active database. |\n| [upgrade](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/upgrade/) | Upgrades an Active-Active database. |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/crdb-cli/crdb/list","title":"crdb-cli crdb list","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/list/","summary":"Shows a list of all Active-Active databases.","tags":["docs","operate","rs"],"last_updated":"2023-12-14T15:27:49+01:00","page_type":"content","content_hash":"bb15b727492770883c123f39eb45817379bfd30b1f080df45bd9a682c1f2300f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Shows a list of all Active-Active databases.\n\n[code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"None"},{"id":"returns","title":"Returns","role":"returns","text":"Returns a list of all Active-Active databases that the cluster participates in. Each database is represented with a unique GUID, the name of the database, an instance ID, and the FQDN of the cluster that hosts the instance."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"sh","code":"crdb-cli crdb list","section_id":"overview"},{"id":"example-ex0","language":"sh","code":"$ crdb-cli crdb list\nCRDB-GUID                             NAME       REPL-ID  CLUSTER-FQDN\nd84f6fe4-5bb7-49d2-a188-8900e09c6f66  database1  1        cluster1.redis.local\nd84f6fe4-5bb7-49d2-a188-8900e09c6f66  database1  2        cluster2.redis.local","section_id":"example"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance","title":"crdb-cli crdb purge-instance","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance/","summary":"Deletes data from a local instance and removes it from the Active-Active database.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:21+01:00","page_type":"content","content_hash":"c54b73dfa4537274dcb8302e3bf9d5bba377e0cf153909d6014aa02cb3d3be4a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Deletes data from a local instance and removes the instance from the Active-Active database.\n\n[code example]\n\nOnce this command finishes, the other replicas must remove this instance with [`crdb-cli crdb remove-instance --force`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/remove-instance)."},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameter                 | Value  | Description                                      |\n|---------------------------|--------|--------------------------------------------------|\n| crdb-guid        | string | The GUID of the database (required)              |\n| instance-id  | string | The ID of the local instance (required) |\n| no-wait                   |        | Does not wait for the task to complete           |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns the task ID of the task that is purging the local instance.\n\nIf `--no-wait` is specified, the command exits. Otherwise, it will wait for the instance to be purged and return `finished`."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"sh","code":"crdb-cli crdb purge-instance --crdb-guid <guid>\n         --instance-id <instance-id>\n         [ --no-wait ]","section_id":"overview"},{"id":"example-ex0","language":"sh","code":"$ crdb-cli crdb purge-instance --crdb-guid db6365b5-8aca-4055-95d8-7eb0105c0b35 --instance-id 2\nTask add0705c-87f1-4c28-ad6a-ab5d98e00c58 created\n  ---> Status changed: queued -> started\n  ---> Status changed: started -> finished","section_id":"example"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/crdb-cli/crdb/remove-instance","title":"crdb-cli crdb remove-instance","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/remove-instance/","summary":"Removes a peer replica from an Active-Active database.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:21+01:00","page_type":"content","content_hash":"bdd8ad366787b0025f44b9c92ee0fed45980a9eddf10ff45a00fde1a3e3a224b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Removes a peer replica instance from the Active-Active database and deletes the instance and its data from the participating cluster.\n\n[code example]\n\nIf the cluster cannot communicate with the instance that you want to remove, you can:\n\n1. Use the `--force` option to remove the instance from the Active-Active database without purging the data from the instance.\n\n1. Run [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) from the removed instance to delete the Active-Active database and its data."},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameter                    | Value  | Description|\n|------------------------------|--------|------------|\n| crdb-guid         | string | The GUID of the database (required) |\n| instance-id | string | The ID of the local instance to remove (required) |\n| force                        |        | Removes the instance without purging data from the instance. <br>If --force is specified, you must run [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) from the removed instance. |\n| no-wait                      |        | Does not wait for the task to complete |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns the task ID of the task that is deleting the instance.\n\nIf `--no-wait` is specified, the command exits. Otherwise, it will wait for the instance to be removed and return `finished`."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"sh","code":"crdb-cli crdb remove-instance --crdb-guid <guid>\n         --instance-id <instance-id>\n         [ --force ]\n         [ --no-wait ]","section_id":"overview"},{"id":"example-ex0","language":"sh","code":"$ crdb-cli crdb remove-instance --crdb-guid db6365b5-8aca-4055-95d8-7eb0105c0b35 --instance-id 2 --force\nTask b1eba5ba-90de-49e9-8678-d66daa1afb51 created\n  ---> Status changed: queued -> started\n  ---> Status changed: started -> finished","section_id":"example"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/crdb-cli/crdb/update","title":"crdb-cli crdb update","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/update/","summary":"Updates the configuration of an Active-Active database.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:21+01:00","page_type":"content","content_hash":"201ebf68d198e9476d78576ea336ccab1464bed3773379a20f1bdc3bddf45aa6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Updates the configuration of an Active-Active database.\n\n[code example]\n\nIf you want to change the configuration of the local instance only, use [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin) instead."},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameter                                                           | Value                                                                                                                         | Description                                                                                                                                                              |\n|---------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| crdb-guid \\<guid\\>                                                 | string                                                                                                                        | GUID of the Active-Active database (required)                                                                                                                            |\n| bigstore-ram-size \\<maximum_memory\\>                                                  | size in bytes, kilobytes (KB), or gigabytes (GB)                                                                               | Maximum RAM limit for the  databases with Auto Tiering enabled, if activated                                                                                                          |\n| memory-size \\<maximum_memory\\>                                                                | size in bytes, kilobytes (KB), or gigabytes (GB) | Maximum database memory (required)                                                                                                                                                                                           |\n| causal-consistency                                                  | true <br/>false                                                                                                           | [Causal consistency](https://redis.io/docs/latest/operate/rs/databases/active-active/causal-consistency) applies updates to all instances in the order they were received |\n| compression                                                         | 0-6                                                                                                                           | The level of data compression: <br /><br /> 0 = No compression <br /><br /> 6 = High compression and resource load (Default: 3)                                                        |\n| credentials id=\\<id\\>,username=\\<username\\>,password=\\<password\\> | strings                                                                                                                       | Updates the credentials for access to the instance identified by `id`.<br/><br/>Provide either a username and password, or certificate credentials—not both. The command fails with `Use either username/password or client_cert/client_key/trusted_ca, not both`.<br/><br/>• `username` and `password` - Cluster username and password<br/>• `client_cert` and `client_key` - Client certificate and its private key, as PEM strings. Both are required for certificate credentials.<br/>• `trusted_ca` - Optional CA that validates the API certificate the peer cluster presents, as a PEM string. If you omit it, the cluster uses the certificates in its `mtls_trusted_ca.pem` file.<br/><br/>Use this option to migrate a participating cluster from a username and password to certificate credentials. See [Certificate-based authentication for cluster management](https://redis.io/docs/latest/operate/rs/security/certificates/certificate-based-authentication#certificate-based-authentication-for-cluster-management). |\n| default-db-config \\<configuration\\>                                                  |                                                                                                                               | Default database configuration from stdin. For a list of database settings, see the [CRDB database config object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb/database_config) reference. |\n| default-db-config-file \\<filename\\>                                | filepath                                                                                                                      | Default database configuration from file. For a list of database settings, see the [CRDB database config object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb/database_config) reference.|\n| encryption                                                          | true <br/>false                                                                                                           | Activates or deactivates encryption                                                                                                                                      |\n| force                                                               |                                                                                                                               | Force an update even if there are no changes                                                                                                                             |\n| no-wait                                                             |                                                                                                                               | Do not wait for the command to finish                                                                                                                                    |\n| oss-cluster                                                         | true <br/>false                                                                                                           | Activates or deactivates OSS Cluster mode                                                                                                                                |\n| eviction-policy                                                     | noeviction<br/>allkeys-lru<br/>allkeys-lfu<br/>allkeys-random<br/>volatile-lru<br/>volatile-lfu<br/>volatile-random<br/>volatile-ttl | Updates [eviction policy](https://redis.io/docs/latest/operate/rs/databases/memory-performance/eviction-policy)                                                                                                          |\n| featureset-version                                                  | true<br/>false                                                                                                             | Updates to latest FeatureSet version                                                                                                                                     |\n| update-module name=\\<name>,featureset_version=\\<version\\>         | strings                                                                                                                       | Update a module to the specified version                                                                                                                                 |\n| update-db-config-modules                                        | true<br/>false                                                                                                             | Updates module information in the CRDB configuration (default-db-config) based on the latest module data for the database. Use this option only after all CRDB database instances have upgraded their modules.      |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns the task ID of the task that is updating the database.\n\nIf `--no-wait` is specified, the command exits. Otherwise, it will wait for the database to be updated and then return \"finished.\""},{"id":"examples","title":"Examples","role":"example","text":"The following example changes the maximum database memory:\n\n[code example]\n\nThe following example shows how to change a default database configuration setting:\n\n[code example]\n\nThe following example switches a participating cluster from a username and password to certificate credentials:\n\n[code example]"}],"examples":[{"id":"overview-ex0","language":"sh","code":"crdb-cli crdb update --crdb-guid <guid>\n         [--no-wait]\n         [--force]\n         [--default-db-config <configuration> ]\n         [--default-db-config-file <filename>]\n         [--compression <0-6>]\n         [--causal-consistency { true | false } ]\n         [--credentials id=<id>,{ username=<username>,password=<password> | client_cert=<cert>,client_key=<key>[,trusted_ca=<ca>] } ]\n         [--encryption { true | false } ]\n         [--oss-cluster { true | false } ]\n         [--featureset-version { true | false } ]\n         [--memory-size <maximum_memory>]\n         [--bigstore-ram-size <maximum_memory>]\n         [--eviction-policy { noeviction | allkeys-lru | allkeys-lfu | allkeys-random | volatile-lru | volatile-lfu | volatile-random | volatile-ttl }]\n         [--update-module name=<name>,featureset_version=<version>]\n         [--update-db-config-modules { true | false } ]","section_id":"overview"},{"id":"examples-ex0","language":"sh","code":"$ crdb-cli crdb update --crdb-guid <crdb-guid> --memory-size 2GB\nTask <task-id> created\n  ---> Status changed: queued -> started\n  ---> Status changed: started -> finished","section_id":"examples"},{"id":"examples-ex1","language":"sh","code":"$ crdb-cli crdb update --crdb-guid <crdb-guid> --default-db-config '{\"shards_count\": <primary_shards_count>}'\nTask <task-id> created\n  ---> Status changed: queued -> started\n  ---> Status changed: started -> finished","section_id":"examples"},{"id":"examples-ex2","language":"sh","code":"$ crdb-cli crdb update --crdb-guid <crdb-guid> \\\n        --credentials id=2,client_cert=<client-cert>,client_key=<client-key>,trusted_ca=<trusted-ca>\nTask <task-id> created\n  ---> Status changed: queued -> started\n  ---> Status changed: started -> finished","section_id":"examples"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/crdb-cli/crdb/upgrade","title":"crdb-cli crdb upgrade","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/upgrade/","summary":"Upgrades an Active-Active database.","tags":["docs","operate","rs"],"last_updated":"2026-10-05T17:53:21+03:00","page_type":"content","content_hash":"faac22f71fef0ee888bb0e4aa3f66ab7db8a65cbb3aac9c421dc5632e7cfccc5","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Upgrades the Redis version and modules of an Active-Active database.\n\n[code example]\n\nBefore you run this command, upgrade Redis Software to version 8.0.18 or later on each participating cluster in the Active-Active database and complete the other [upgrade prerequisites](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-active-active#upgrade-prerequisites). For the full procedure, see [Upgrade an Active-Active database](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-active-active)."},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameter | Value | Description |\n|-----------|-------|-------------|\n| crdb-guid \\<guid\\> | string | GUID of the Active-Active database (required) |\n| redis-version \\<version\\> | string | Upgrades the database to the specified Redis version instead of the latest version bundled with Redis Software |\n| preserve-roles | | Preserves the shards' primary and replica roles. Requires an extra failover. |\n| parallel-shards-upgrade \\<number\\> | integer or `all` | Maximum number of shards to upgrade in parallel (default: all) |\n| keep-crdt-protocol-version | | Keeps the current CRDB protocol version. See [CRDB protocol version guidelines](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-active-active#crdb-protocol-version-guidelines). |\n| keep-crdt-featureset-version | | Keeps the current CRDB feature set version. See [Feature set version guidelines](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-active-active#feature-set-version-guidelines). |\n| discard-data | | Discards data in a non-replicated, non-persistent database |\n| force-discard | | Discards data even if the database is replicated or persistent |\n| force-restart | | Restarts the shards even if the version doesn't change |\n| no-wait | | Does not wait for the task to complete |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns the task ID of the task that is upgrading the database.\n\nIf `--no-wait` is specified, the command exits. Otherwise, it waits for the upgrade to finish and returns `finished`. To check the status of a task started with `--no-wait`, use [`crdb-cli task status`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/task/status)."},{"id":"examples","title":"Examples","role":"example","text":"The following example upgrades an Active-Active database to the default Redis version and preserves the shards' roles:\n\n[code example]\n\nThe following example upgrades an Active-Active database to a specific Redis version:\n\n[code example]"}],"examples":[{"id":"overview-ex0","language":"sh","code":"crdb-cli crdb upgrade --crdb-guid <guid>\n         [--redis-version <version>]\n         [--preserve-roles]\n         [--parallel-shards-upgrade <number>]\n         [--keep-crdt-protocol-version]\n         [--keep-crdt-featureset-version]\n         [--discard-data]\n         [--force-discard]\n         [--force-restart]\n         [--no-wait]","section_id":"overview"},{"id":"examples-ex0","language":"sh","code":"$ crdb-cli crdb upgrade --crdb-guid <crdb-guid> --preserve-roles\nTask <task-id> created\n  ---> CRDB GUID Assigned: crdb:<crdb-guid>\n  ---> Status changed: queued -> started\n  ---> Status changed: started -> finished","section_id":"examples"},{"id":"examples-ex1","language":"sh","code":"$ crdb-cli crdb upgrade --crdb-guid <crdb-guid> --redis-version <version> --preserve-roles\nTask <task-id> created\n  ---> CRDB GUID Assigned: crdb:<crdb-guid>\n  ---> Status changed: queued -> started\n  ---> Status changed: started -> finished","section_id":"examples"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/crdb-cli","title":"crdb-cli","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/","summary":"Manage Active-Active databases.","tags":["docs","operate","rs"],"last_updated":"2026-10-05T17:53:21+03:00","children":[{"id":"operate/rs/references/cli-utilities/crdb-cli/crdb","summary":"Manage Active-Active databases.","title":"crdb-cli crdb commands","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/"},{"id":"operate/rs/references/cli-utilities/crdb-cli/task","summary":"Manage Active-Active tasks.","title":"crdb-cli task commands","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/task/"}],"page_type":"content","content_hash":"2fdc9fae71aa575572a72b131f6e6ea59e9f588a333dec8e596df3a6b9634066","sections":[{"id":"overview","title":"Overview","role":"overview","text":"An [Active-Active database](https://redis.io/docs/latest/operate/rs/databases/active-active) (also known as CRDB or conflict-free replicated database)\nreplicates your data across Redis Software clusters located in geographically distributed regions.\nActive-Active databases allow read-write access in all locations, making them ideal for distributed applications that require fast response times and disaster recovery.\n\nThe Active-Active database on an individual cluster is called an **instance**.\nEach cluster that hosts an instance is called a **participating cluster**.\n\nAn Active-Active database requires two or more participating clusters.\nEach instance is responsible for updating the instances that reside on other participating clusters with the transactions it receives.\nWrite conflicts are resolved using [conflict-free replicated data types](https://redis.io/docs/latest/operate/rs/databases/active-active) (CRDTs).\n\nTo programmatically maintain an Active-Active database and its instances, you can use the `crdb-cli` command-line tool."},{"id":"crdb-cli-commands","title":"`crdb-cli` commands","role":"content","text":"| Command | Description |\n| --- | --- |\n| [crdb](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/) | Manage Active-Active databases. |\n| [task](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/task/) | Manage Active-Active tasks. |"},{"id":"use-the-crdb-cli","title":"Use the crdb-cli","role":"content","text":"To use the `crdb-cli` tool, use SSH to sign in to a Redis Software host with a user that belongs to the group that Redis Software was installed with (Default: **redislabs**).\nIf you sign in with a non-root user, you must add `/opt/redislabs/bin/` to your `PATH` environment variables.\n\n`crdb-cli` commands use the syntax: `crdb-cli <command> <arguments>` to let you:\n\n- Create, list, update, upgrade, flush, or delete an Active-Active database.\n- Add or remove an instance of the Active-Active database on a specific cluster.\n\nEach command creates a task.\n\nBy default, the command runs immediately and displays the result in the output.\n\nIf you use the <nobr>`--no-wait`</nobr> flag, the command runs in the background so that your application is not delayed by the response.\n\nUse the [`crdb-cli task` commands](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/task) to manage Active-Active database tasks.\n\nFor each `crdb-cli` command, you can use <nobr>`--help`</nobr> for additional information about the command."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/crdb-cli/task/cancel","title":"crdb-cli task cancel","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/task/cancel/","summary":"Attempts to cancel a specified Active-Active database task.","tags":["docs","operate","rs"],"last_updated":"2024-11-22T10:22:30-08:00","page_type":"content","content_hash":"426eec41804967a26bb30ad617fce5316a53cf99d65b1ab94c47fd6f8b48fa06","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Cancels the Active-Active database task specified by the task ID.\n\n[code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameter           | Value  | Description                         |\n|---------------------|--------|-------------------------------------|\n| task-id \\<task_id\\>  | string | An Active-Active database task ID (required) |"},{"id":"returns","title":"Returns","role":"returns","text":"Attempts to cancel an Active-Active database task.\n\nBe aware that tasks may complete before they can be cancelled."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"sh","code":"crdb-cli task cancel --task-id <task_id>","section_id":"overview"},{"id":"example-ex0","language":"sh","code":"$ crdb-cli task cancel --task-id 2901c2a3-2828-4717-80c0-6f27f1dd2d7c","section_id":"example"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/crdb-cli/task","title":"crdb-cli task commands","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/task/","summary":"Manage Active-Active tasks.","tags":["docs","operate","rs"],"last_updated":"2024-02-26T13:11:00+01:00","children":[{"id":"operate/rs/references/cli-utilities/crdb-cli/task/cancel","summary":"Attempts to cancel a specified Active-Active database task.","title":"crdb-cli task cancel","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/task/cancel/"},{"id":"operate/rs/references/cli-utilities/crdb-cli/task/list","summary":"Lists active and recent Active-Active database tasks.","title":"crdb-cli task list","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/task/list/"},{"id":"operate/rs/references/cli-utilities/crdb-cli/task/status","summary":"Shows the status of a specified Active-Active database task.","title":"crdb-cli task status","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/task/status/"}],"page_type":"content","content_hash":"323918f84ec8768cfcb5e9f212846b67581f2bfc0a1c56d3a336eec511c309e9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `crdb-cli task` commands help investigate Active-Active database performance issues.  They should not be used except as directed by Support."},{"id":"crdb-cli-task-commands","title":"`crdb-cli task` commands","role":"content","text":"| Command | Description |\n| --- | --- |\n| [cancel](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/task/cancel/) | Attempts to cancel a specified Active-Active database task. |\n| [list](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/task/list/) | Lists active and recent Active-Active database tasks. |\n| [status](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/task/status/) | Shows the status of a specified Active-Active database task. |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/crdb-cli/task/list","title":"crdb-cli task list","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/task/list/","summary":"Lists active and recent Active-Active database tasks.","tags":["docs","operate","rs"],"last_updated":"2023-12-14T15:27:49+01:00","page_type":"content","content_hash":"589aa12369e885627d0a35252365d0b8c895231485f85c867340b359ab538c7f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Lists active and recent Active-Active database tasks.\n\n[code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"None"},{"id":"returns","title":"Returns","role":"returns","text":"A table listing current and recent Active-Active tasks.  Each entry includes the following:\n\n| Column | Description |\n|--------|-------------|\n| Task ID | String containing the unique ID associated with the task<br/>Example: `e1c49470-ae0b-4df8-885b-9c755dd614d0` |\n| CRDB-GUID | String containing the unique ID associated with the Active-Active database affected by the task<br/>Example: `1d7741cc-1110-4e2f-bc6c-935292783d24` |\n| Operation | String describing the task action<br/>Example: `create_crdb` |\n| Status | String indicating the task status<br/>Example: `finished` |\n| Worker name | String identifying the process handling the task<br/>Example: `crdb_worker:1:0` |\n| Started | TimeStamp value indicating when the task started ([UTC](https://en.wikipedia.org/wiki/Coordinated_Universal_Time))<br/>Example: `2022-10-12T09:33:41Z` |\n| Ended | TimeStamp value indicating when the task ended (UTC)<br/>Example: ` 2022-10-12T09:33:55Z` |"},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"sh","code":"crdb-cli task list","section_id":"overview"},{"id":"example-ex0","language":"sh","code":"$ crdb-cli task list\nTASK-ID    CRDB-GUID  OPERATION    STATUS    WORKER-NAME  STARTED    ENDED               \n<task-ID>  <crdb-ID>  <operation>  <result>  <worker-ID>  <started>  <ended>","section_id":"example"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/crdb-cli/task/status","title":"crdb-cli task status","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/task/status/","summary":"Shows the status of a specified Active-Active database task.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:21+01:00","page_type":"content","content_hash":"93371ad8f0bd5649f9c6ddae51ae907d36f9b4b713f59080860ac1e860f06bce","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Shows the status of a specified Active-Active database task.\n\n[code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameter           | Value  | Description                         |\n|---------------------|--------|-------------------------------------|\n| task-id \\<task_id\\>  | string | An Active-Active database task ID (required) |\n| verbose              | N/A    | Returns detailed information when specified |\n| no-verbose           | N/A    | Returns limited information when specified |\n\nThe `--verbose` and `--no-verbose` options are mutually incompatible; specify one or the other.\n\nThe `404 Not Found` error indicates an invalid task ID.  Use the [`task list`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/task/list) command to determine available task IDs."},{"id":"returns","title":"Returns","role":"returns","text":"Returns the status of an Active-Active database task."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"sh","code":"crdb-cli task status --task-id <task_id>","section_id":"overview"},{"id":"example-ex0","language":"sh","code":"$ crdb-cli task status --task-id e1c49470-ae0b-4df8-885b-9c755dd614d0\nTask-ID: e1c49470-ae0b-4df8-885b-9c755dd614d0\nCRDB-GUID: 1d7741cc-1110-4e2f-bc6c-935292783d24\nOperation: create_crdb\nStatus: finished\nWorker-Name: crdb_worker:1:0\nStarted: 2022-10-12T09:33:41Z\nEnded: 2022-10-12T09:33:55Z","section_id":"example"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities","title":"Command-line utilities","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/","summary":"Reference for Redis Software command-line utilities, including rladmin, redis-cli, crdb-cli, and rlcheck.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:21+01:00","children":[{"id":"operate/rs/references/cli-utilities/crdb-cli","summary":"Manage Active-Active databases.","title":"crdb-cli","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/"},{"id":"operate/rs/references/cli-utilities/redis-cli","summary":"Run Redis commands.","title":"redis-cli","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/redis-cli/"},{"id":"operate/rs/references/cli-utilities/rladmin","summary":"Manage Redis Software clusters and databases.","title":"rladmin","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/"},{"id":"operate/rs/references/cli-utilities/rlcheck","summary":"Verify nodes.","title":"rlcheck","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rlcheck/"}],"page_type":"content","content_hash":"b5e9b33af52aacfa1adcedc8c13cd465b450b49a572a35fed65caa9ba725fe0d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software includes a set of utilities to help you manage and test your cluster. To use a utility, run it from the command line."},{"id":"public-utilities","title":"Public utilities","role":"content","text":"Administrators can use these CLI tools to manage and test a Redis Software cluster. You can find the binaries in the `/opt/redislabs/bin/` directory.\n\n\n| Utility | Description |\n| --- | --- |\n| [crdb-cli (manage Active-Active)](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/) | Manage Active-Active databases. |\n| [redis-cli (run Redis commands)](https://redis.io/docs/latest/operate/rs/references/cli-utilities/redis-cli/) | Run Redis commands. |\n| [rladmin (manage cluster)](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/) | Manage Redis Software clusters and databases. |\n| [rlcheck (verify nodes)](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rlcheck/) | Verify nodes. |"},{"id":"internal-utilities","title":"Internal utilities","role":"content","text":"The `/opt/redislabs/bin/` directory also contains utilities used internally by Redis Software and for troubleshooting.\n\n> [!WARNING]\n> Do not use these tools for normal operations.\n\n| Utility | Description |\n|---------|-------------|\n| bdb-cli | `redis-cli` connected to a database. |\n| ccs-cli | Inspect Cluster Configuration Store. |\n| cnm_ctl | Manages services for provisioning, migration, monitoring, <br />resharding, rebalancing, deprovisioning, and autoscaling. |\n| consistency_checker | Checks the consistency of Redis instances. |\n| crdbtop | Monitor Active-Active databases. |\n| debug_mode | Enables debug mode. |\n| debuginfo | Collects cluster information. |\n| dmc-cli | Configure and monitor the DMC proxy. |\n| pdns_control | Sends commands to a running PowerDNS nameserver. |\n| [rdb-cli](https://github.com/redis/librdb) | Parse and convert RDB files. |\n| redis_ctl | Stops or starts Redis instances. |\n| rl_rdbloader | Load RDB backup files to a server. |\n| rlutil | Maintenance utility. |\n| shard-cli | `redis-cli` connected to a shard. |\n| supervisorctl | Manages the lifecycles of Redis Software services. |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/redis-cli","title":"redis-cli","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/redis-cli/","summary":"Run Redis commands.","tags":["docs","operate","rs","rc"],"last_updated":"2026-09-24T14:53:09+01:00","children":[],"page_type":"content","content_hash":"8e5e677d9fe80346ae8096c558dc501ee8aedbec74156b41d83de923847a8fc7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `redis-cli` command-line utility lets you interact with a Redis database. With `redis-cli`, you can run [Redis commands](/content/commands) directly from the command-line terminal or with [interactive mode](#interactive-mode).\n\nIf you want to run Redis commands without `redis-cli`, you can [connect to a database with Redis Insight](https://redis.io/docs/latest/develop/tools/insight) and use the built-in [CLI](https://redis.io/docs/latest/develop/tools/insight) prompt instead."},{"id":"install-redis-cli","title":"Install `redis-cli`","role":"setup","text":"When you install Redis Software or Redis Open Source, it also installs the `redis-cli` command-line utility.\n\nTo learn how to install Redis and `redis-cli`, see the following installation guides:\n\n- [Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack)\n\n- [Redis Software](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/redis-enterprise-software-quickstart)\n\n- [Redis Software with Docker](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart)\n\nIf you only need `redis-cli` on your local machine to connect to a remote database, you can [install the standalone `redis-cli` binary](https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/install-redis-cli) on Linux or macOS, without installing the full Redis Open Source distribution."},{"id":"connect-to-a-database","title":"Connect to a database","role":"content","text":"To run Redis commands with `redis-cli`, you need to connect to your Redis database.\n\nYou can find endpoint and port details in the **Databases** list or the database’s **Configuration** screen."},{"id":"connect-remotely","title":"Connect remotely","role":"content","text":"If you have `redis-cli` installed on your local machine, you can use it to connect to a remote Redis database. You will need to provide the database's connection details, such as the hostname or IP address, port, and password.\n\n[code example]\n\nYou can also provide the password with the `REDISCLI_AUTH` environment variable instead of the `-a` option:\n\n[code example]"},{"id":"connect-over-tls","title":"Connect over TLS","role":"content","text":"To connect to a Redis Software or Redis Cloud database over TLS:\n\n1. Download or copy the Redis Software server (or proxy) certificates.\n\n    - For Redis Cloud, see [Download certificates](https://redis.io/docs/latest/operate/rc/security/database-security/tls-ssl#download-certificates) for detailed instructions on how to download the server certificates (`redis_ca.pem`) from the [Redis Cloud console](https://cloud.redis.io/).\n\n    - For Redis Software, copy the proxy certificate from the Cluster Manager UI (**Cluster > Security > Certificates > Server authentication**) or from a cluster node (`/etc/opt/redislabs/proxy_cert.pem`).\n\n1. Copy the certificate to each client machine.\n\n1. If your database doesn't require client authentication, provide the Redis Software server certificate (`redis_ca.pem` for Cloud or `proxy_cert.pem` for Software) when you connect:\n\n    [code example]\n\n1. If your database requires client authentication, provide your client's private and public keys along with the Redis Software server certificate (`redis_ca.pem` for Cloud or `proxy_cert.pem` for Software) when you connect:\n\n    [code example]"},{"id":"connect-with-docker","title":"Connect with Docker","role":"content","text":"If your Redis database runs in a Docker container, you can use `docker exec` to run `redis-cli` commands:\n\n[code example]"},{"id":"basic-use","title":"Basic use","role":"content","text":"You can run `redis-cli` commands directly from the command-line terminal:\n\n[code example]\n\nFor example, you can use `redis-cli` to test your database connection and store a new Redis string in the database:\n\n[code example]\n\nFor more information, see [Command line usage](https://redis.io/docs/latest/develop/tools/cli#command-line-usage)."},{"id":"interactive-mode","title":"Interactive mode","role":"content","text":"In `redis-cli` [interactive mode](https://redis.io/docs/latest/develop/tools/cli#interactive-mode), you can:\n\n- Run any `redis-cli` command without prefacing it with `redis-cli`.\n- Enter `?` for more information about how to use the `HELP` command and [set `redis-cli` preferences](https://redis.io/docs/latest/develop/tools/cli#preferences).\n- Enter [`HELP`](https://redis.io/docs/latest/develop/tools/cli#showing-help-about-redis-commands) followed by the name of a command for more information about the command and its options.\n- Press the `Tab` key for command completion.\n- Enter `exit` or `quit` or press `Control+D` to exit interactive mode and return to the terminal prompt.\n\nThis example shows how to start interactive mode and run Redis commands:\n\n[code example]"},{"id":"examples","title":"Examples","role":"example","text":""},{"id":"check-slowlog","title":"Check slowlog","role":"content","text":"Run [`slowlog get`](https://redis.io/docs/latest/commands/slowlog-get) for a list of recent slow commands:\n\n[code example]"},{"id":"scan-for-big-keys","title":"Scan for big keys","role":"content","text":"Scan the database for big keys:\n\n[code example]\n\nSee [Scanning for big keys](https://redis.io/docs/latest/develop/tools/cli#scanning-for-big-keys) for more information."},{"id":"more-info","title":"More info","role":"content","text":"- [Redis CLI documentation](https://redis.io/docs/latest/develop/tools/cli)\n- [Redis commands reference](/content/commands)"}],"examples":[{"id":"connect-remotely-ex0","language":"sh","code":"$ redis-cli -h <endpoint> -p <port> -a <password>","section_id":"connect-remotely"},{"id":"connect-remotely-ex1","language":"sh","code":"$ export REDISCLI_AUTH=<password>\n$ redis-cli -h <endpoint> -p <port>","section_id":"connect-remotely"},{"id":"connect-over-tls-ex0","language":"sh","code":"redis-cli -h <endpoint> -p <port> --tls --cacert <redis_cert>.pem","section_id":"connect-over-tls"},{"id":"connect-over-tls-ex1","language":"sh","code":"redis-cli -h <endpoint> -p <port> --tls --cacert <redis_cert>.pem \\\n        --cert redis_user.crt --key redis_user_private.key","section_id":"connect-over-tls"},{"id":"connect-with-docker-ex0","language":"sh","code":"$ docker exec -it <Redis container name> redis-cli -p <port>","section_id":"connect-with-docker"},{"id":"basic-use-ex0","language":"sh","code":"$ redis-cli -h <endpoint> -p <port> <Redis command>","section_id":"basic-use"},{"id":"basic-use-ex1","language":"sh","code":"$ redis-cli -h <endpoint> -p 12000 PING\nPONG\n$ redis-cli -h <endpoint> -p 12000 SET mykey \"Hello world\"\nOK\n$ redis-cli -h <endpoint> -p 12000 GET mykey              \n\"Hello world\"","section_id":"basic-use"},{"id":"interactive-mode-ex0","language":"sh","code":"$ redis-cli -p 12000\n127.0.0.1:12000> PING\nPONG\n127.0.0.1:12000> SET mykey \"Hello world\"\nOK\n127.0.0.1:12000> GET mykey\n\"Hello world\"","section_id":"interactive-mode"},{"id":"check-slowlog-ex0","language":"sh","code":"redis-cli -h <endpoint> -p <port> slowlog get <number of entries>","section_id":"check-slowlog"},{"id":"scan-for-big-keys-ex0","language":"sh","code":"redis-cli -h <endpoint> -p <port> --bigkeys","section_id":"scan-for-big-keys"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/rladmin/bind","title":"rladmin bind","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/bind/","summary":"Manages the proxy policy for a specified database endpoint.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:21+01:00","page_type":"content","content_hash":"20dfd8ddd3e6548b4c22b13c8f4a770042440e22137bd9bd5a00247191efa183","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Manages the proxy policy for a specific database endpoint."},{"id":"bind-endpoint-exclude","title":"`bind endpoint exclude`","role":"content","text":"Defines a list of nodes to exclude from the proxy policy for a specific database endpoint. When you exclude a node, the endpoint cannot bind to the node's proxy.\n\nEach time you run an exclude command, it overwrites the previous list of excluded nodes.\n\n[code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameter | Type/Value                     | Description                                                                                   |\n|-----------|--------------------------------|-----------------------------------------------------------------------------------------------|\n| db        | db:\\<id\\><br /> name           | Only allows endpoints for the specified database                                               |\n| endpoint  | endpoint ID                    | Changes proxy settings for the specified endpoint                              |\n| proxy     | list of proxy IDs          | Proxies to exclude                                                           |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns `Finished successfully` if the list of excluded proxies was successfully changed. Otherwise, it returns an error.\n\nUse [`rladmin status endpoints`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status#status-endpoints) to verify that the policy changed."},{"id":"example","title":"Example","role":"example","text":"[code example]"},{"id":"bind-endpoint-include","title":"`bind endpoint include`","role":"content","text":"Defines a list of nodes to include in the proxy policy for the specific database endpoint.\n\nEach time you run an include command, it overwrites the previous list of included nodes.\n\n[code example]"},{"id":"parameters-1","title":"Parameters","role":"parameters","text":"| Parameter | Type/Value                     | Description                                                                                   |\n|-----------|--------------------------------|-----------------------------------------------------------------------------------------------|\n| db        | db:\\<id\\><br /> name           | Only allows endpoints for the specified database                                               |\n| endpoint  | endpoint ID                    | Changes proxy settings for the specified endpoint                              |\n| proxy     | list of proxy IDs          | Proxies to include                                                           |"},{"id":"returns-1","title":"Returns","role":"returns","text":"Returns `Finished successfully` if the list of included proxies was successfully changed. Otherwise, it returns an error.\n\nUse [`rladmin status endpoints`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status#status-endpoints) to verify that the policy changed."},{"id":"example-1","title":"Example","role":"example","text":"[code example]"},{"id":"bind-endpoint-policy","title":"`bind endpoint policy`","role":"content","text":"Changes the overall proxy policy for a specific database endpoint.\n\n[code example]"},{"id":"parameters-2","title":"Parameters","role":"parameters","text":"| Parameter | Type/Value                     | Description                                                                                   |\n|-----------|--------------------------------|-----------------------------------------------------------------------------------------------|\n| db        | db:\\<id\\><br /> name           | Only allows endpoints for the specified database                                               |\n| endpoint  | endpoint ID                    | Changes proxy settings for the specified endpoint                              |\n| policy    | <nobr>'all-master-shards'</nobr><br /> <nobr>'all-nodes'</nobr><br /> 'single' | Changes the [proxy policy](#proxy-policies) to the specified policy |\n\n| Proxy&nbsp;policy<a name=\"proxy-policies\"> | Description |\n| - | - |\n| all-master-shards | Multiple proxies, one on each master node (best for high traffic and multiple master shards)                     |\n| all-nodes | Multiple proxies, one on each node of the cluster (increases traffic in the cluster, only used in special cases) |\n| single | All traffic flows through a single proxy bound to the database endpoint (preferable in most cases)               |"},{"id":"returns-2","title":"Returns","role":"returns","text":"Returns `Finished successfully` if the proxy policy was successfully changed. Otherwise, it returns an error.\n\nUse [`rladmin status endpoints`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status#status-endpoints) to verify that the policy changed."},{"id":"example-2","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"bind-endpoint-exclude-ex0","language":"sh","code":"rladmin bind\n        [ db { db:<id> | <name> } ]\n        endpoint <id> exclude\n        <proxy_id1 .. proxy_idN>","section_id":"bind-endpoint-exclude"},{"id":"example-ex0","language":" sh","code":"$ rladmin status endpoints db db:6\nENDPOINTS:\nDB:ID    NAME   ID                  NODE      ROLE                      SSL\ndb:6     tr02   endpoint:6:1        node:2    all-nodes                 No\ndb:6     tr02   endpoint:6:1        node:1    all-nodes                 No\ndb:6     tr02   endpoint:6:1        node:3    all-nodes                 No\n$ rladmin bind endpoint 6:1 exclude 2\nExecuting bind endpoint: OOO.\nFinished successfully\n$ rladmin status endpoints db db:6\nENDPOINTS:\nDB:ID    NAME   ID                 NODE      ROLE                       SSL\ndb:6     tr02   endpoint:6:1       node:1    all-nodes -2               No\ndb:6     tr02   endpoint:6:1       node:3    all-nodes -2               No","section_id":"example"},{"id":"bind-endpoint-include-ex0","language":"sh","code":"rladmin bind\n        [ db { db:<id> | <name> } ]\n        endpoint <id> include\n        <proxy_id1 .. proxy_idN>","section_id":"bind-endpoint-include"},{"id":"example-1-ex0","language":" sh","code":"$ rladmin status endpoints db db:6\nENDPOINTS:\nDB:ID    NAME   ID                 NODE      ROLE                       SSL\ndb:6     tr02   endpoint:6:1       node:3    all-master-shards          No\n$ rladmin bind endpoint 6:1 include 3\nExecuting bind endpoint: OOO.\nFinished successfully\n$ rladmin status endpoints db db:6\nENDPOINTS:\nDB:ID   NAME   ID                NODE      ROLE                          SSL\ndb:6    tr02   endpoint:6:1      node:1    all-master-shards +3          No\ndb:6    tr02   endpoint:6:1      node:3    all-master-shards +3          No","section_id":"example-1"},{"id":"bind-endpoint-policy-ex0","language":"sh","code":"rladmin bind\n        [ db { db:<id> | <name> } ]\n        endpoint <id>\n        policy { single | all-master-shards | all-nodes }","section_id":"bind-endpoint-policy"},{"id":"example-2-ex0","language":" sh","code":"$ rladmin status endpoints db db:6\nENDPOINTS:\nDB:ID    NAME   ID                 NODE      ROLE                       SSL\ndb:6     tr02   endpoint:6:1       node:1    all-nodes -2               No\ndb:6     tr02   endpoint:6:1       node:3    all-nodes -2               No\n$ rladmin bind endpoint 6:1 policy all-master-shards\nExecuting bind endpoint: OOO.\nFinished successfully\n$ rladmin status endpoints db db:6\nENDPOINTS:\nDB:ID    NAME   ID                 NODE      ROLE                       SSL\ndb:6     tr02   endpoint:6:1       node:3    all-master-shards          No","section_id":"example-2"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/rladmin/cluster/certificate","title":"rladmin cluster certificate","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/certificate/","summary":"Sets cluster certificates.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:21+01:00","page_type":"content","content_hash":"dca63f120bc76c4593042fc46b83ed2dbcda86f67fc6aaeb11214e0404b72741","sections":[{"id":"cluster-certificate-set-certificate_name","title":"`cluster certificate set <certificate_name>`","role":"overview","text":"Sets a cluster certificate to a specified PEM file.\n\n[code example]\n\nTo set a certificate for a specific service, use the corresponding certificate name. See the [certificates table](https://redis.io/docs/latest/operate/rs/security/certificates) for the list of cluster certificates and their descriptions."},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameter | Type/Value | Description |\n|-----------|------------|-------------|\n| certificate_name | 'cm'<br /> 'api'<br /> 'proxy'<br /> 'syncer'<br /> 'metrics_exporter' | Name of the certificate to update. See the [certificates table](https://redis.io/docs/latest/operate/rs/security/certificates) for descriptions. |\n| certificate_file | filepath | Path to the certificate file |\n| key_file | filepath | Path to the key file (optional) |"},{"id":"returns","title":"Returns","role":"returns","text":"Reports that the certificate was set to the specified file. Returns an error message if the certificate fails to update."},{"id":"example","title":"Example","role":"example","text":"Update the proxy certificate:\n\n[code example]"},{"id":"cluster-certificate-set-internal","title":"`cluster certificate set internal`","role":"content","text":"Sets [customer-provided internode encryption certificates](https://redis.io/docs/latest/operate/rs/security/encryption/internode-encryption#customer-provided-certificates).\n\n[code example]"},{"id":"parameters-1","title":"Parameters","role":"parameters","text":"| Parameter | Type/Value | Description |\n|-----------|------------|-------------|\n| dpine_certificate_file | filepath | Path to the data plane internode encryption (DPINE) certificate file |\n| dpine_key_file | filepath | Path to the data plane internode encryption (DPINE) key file |\n| cpine_certificate_file | filepath | Path to the control plane internode encryption (CPINE) certificate file |\n| cpine_key_file | filepath | Path to the control plane internode encryption (CPINE) key file |"},{"id":"returns-1","title":"Returns","role":"returns","text":"Reports that the internal certificates were set to the specified files. Returns an error message if the certificates fail to update."},{"id":"example-1","title":"Example","role":"example","text":"Set up [customer-provided internode encryption certificates](https://redis.io/docs/latest/operate/rs/security/encryption/internode-encryption#customer-provided-certificates):\n\n[code example]"}],"examples":[{"id":"cluster-certificate-set-certificate_name-ex0","language":"sh","code":"rladmin cluster certificate set <certificate_name>\n        certificate_file <filepath>\n        [ key_file <filepath> ]","section_id":"cluster-certificate-set-certificate_name"},{"id":"example-ex0","language":"sh","code":"$ rladmin cluster certificate set proxy \\\n       certificate_file /tmp/proxy.pem\nSet proxy certificate to contents of file /tmp/proxy.pem","section_id":"example"},{"id":"cluster-certificate-set-internal-ex0","language":"sh","code":"rladmin cluster certificate set internal\n        dpine_certificate_file <filepath>\n        dpine_key_file <filepath>\n        cpine_certificate_file <filepath>\n        cpine_key_file <filepath>","section_id":"cluster-certificate-set-internal"},{"id":"example-1-ex0","language":"sh","code":"$ rladmin cluster certificate set internal \\\n       dpine_certificate_file /tmp/dpine_cert.pem \\\n       dpine_key_file /tmp/dpine_key.pem \\\n       cpine_certificate_file /tmp/cpine_cert.pem \\\n       cpine_key_file /tmp/cpine_key.pem","section_id":"example-1"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/rladmin/cluster/change_password_hashing_algorithm","title":"rladmin cluster change_password_hashing_algorithm","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/change_password_hashing_algorithm/","summary":"Changes the password hashing algorithm.","tags":["docs","operate","rs"],"last_updated":"2025-03-27T11:22:53-05:00","page_type":"content","content_hash":"99c6d5966cb5255c7fafc8b03e81b4f6eaf63c988c2a1214b21c00c1bcd2b76a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Changes the password hashing algorithm for the entire cluster. When you change the hashing algorithm, it rehashes the administrator password and passwords for all users, including default users.\n\n[code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameter | Type/Value | Description |\n|-----------|------------|-------------|\n| algorithm | SHA-256<br />PBKDF2 | Change to the specified hashing algorithm. The default hashing algorithm is `SHA-256`. |"},{"id":"returns","title":"Returns","role":"returns","text":"Reports whether the algorithm change succeeded or an error occurred."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"sh","code":"rladmin cluster change_password_hashing_algorithm <algorithm>","section_id":"overview"},{"id":"example-ex0","language":"sh","code":"$ rladmin cluster change_password_hashing_algorithm PBKDF2\nPlease confirm changing the password hashing algorithm\nPlease confirm [Y/N]: y\nAlgorithm changed","section_id":"example"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/rladmin/cluster/config","title":"rladmin cluster config","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/config/","summary":"Updates the cluster's configuration.","tags":["docs","operate","rs"],"last_updated":"2026-09-29T16:51:25+03:00","page_type":"content","content_hash":"7a2f003c43aa12759ab4aff46c7c95172b53e88f2b3efa82dcfeb3b5ccbd6a1d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Updates the cluster configuration.\n\n[code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameter | Type/Value | Description |\n|-----------|------------|-------------|\n| audit_address | string | TCP/IP address where a listener can capture [audit event notifications](https://redis.io/docs/latest/operate/rs/security/audit-events) |\n| audit_port | string | Port where a listener can capture [audit event notifications](https://redis.io/docs/latest/operate/rs/security/audit-events) |\n| audit_protocol | `tcp`<br/>`local` | Protocol used for [audit event notifications](https://redis.io/docs/latest/operate/rs/security/audit-events)<br/>For production systems, only `tcp` is supported. |\n| availability_lag_tolerance_ms | integer | Availability lag tolerance in milliseconds |\n| control_cipher_suites | list of ciphers | Cipher suites used for TLS connections to the Cluster Manager UI (specified in the format understood by the BoringSSL library)<br />(previously named `cipher_suites`) |\n| cm_port | integer | UI server listening port |\n| cm_session_timeout_minutes | integer | Timeout in minutes for the CM session\n| cnm_http_port | integer | HTTP REST API server listening port |\n| cnm_https_port | integer | HTTPS REST API server listening port |\n| crdb_coordinator_port | integer, (range:&nbsp;1024-65535) (default:&nbsp;9081) | CRDB coordinator port |\n| data_cipher_list | list of ciphers | Cipher suites used by the the data plane (specified in the format understood by the OpenSSL library) |\n| data_cipher_suites_tls_1_3 |  list of ciphers | Specifies the enabled TLS 1.3 ciphers for the data plane |\n| debuginfo_path | filepath | Local directory to place generated support package files |\n| encrypt_pkeys | `enabled`<br />`disabled` | Enable or turn off encryption of private keys |\n| envoy_admin_port | integer, (range:&nbsp;1024-65535) | Envoy admin port. Changing this port during runtime might result in an empty response because envoy serves as the cluster gateway.|\n| envoy_mgmt_server_port | integer, (range:&nbsp;1024-65535) | Envoy management server port|\n| external_ipv6 | `enabled`<br />`disabled` | Enable or disable use of IPv6 for external cluster communications |\n| handle_redirects | `enabled`<br />`disabled` | Enable or turn off handling DNS redirects when DNS is not configured and running behind a load balancer |\n| handle_metrics_redirects | `enabled`<br />`disabled` | Enable or turn off handling cluster redirects internally for Metrics API |\n| http_support | `enabled`<br />`disabled` | Enable or turn off using HTTP for REST API connections |\n| ipv6 | `enabled`<br />`disabled` | Enable or turn off IPv6 connections to the Cluster Manager UI (deprecated; use `external_ipv6` instead) |\n| min_control_TLS_version | `1.2`<br />`1.3` | The minimum TLS protocol version that is supported for the control path |\n| min_data_TLS_version | `1.2`<br />`1.3` | The minimum TLS protocol version that is supported for the data path |\n| min_sentinel_TLS_version | `1.2`<br />`1.3` | The minimum TLS protocol version that is supported for the discovery service |\n| options_method_forbidden | `enabled`<br />`disabled` | Enable or turn off forbidding `OPTIONS` method for CNM HTTPS port |\n| reserved_ports | list of ports/port ranges | Ports and port ranges that the cluster does not use for database endpoints or internal shard traffic (for example `reserved_ports 11000 13000-13010 20048`) |\n| s3_url | string | The URL of S3 export and import |\n| s3_ca_cert | string | The CA certificate filepath for S3 export and import |\n| sentinel_cipher_suites | list of ciphers | Cipher suites used by the discovery service (supported ciphers are implemented by the [cipher_suites.go](<https://golang.org/src/crypto/tls/cipher_suites.go>) package) |\n| sentinel_tls_mode | `allowed`<br />`required`<br />`disabled` | Define the SSL policy for the discovery service<br />(previously named `sentinel_ssl_policy`) |\n| services | `alert_mgr`<br />`call_home_agent`<br />`cm_server`<br />`crdb_controller`<br />`crdb_coordinator`<br />`crdb_worker`<br />`entraid_agent_mgr`<br />`ldap_agent_mgr`<br />`mdns_server`<br />`metrics_local_storage_service`<br />`pdns_server`<br />`sentinel_service`<br />`stats_archiver`<br /><br />`enabled`<br />`disabled` | Enable or turn off selected cluster services |\n| upgrade_mode | `enabled`<br />`disabled` | Enable or turn off upgrade mode on the cluster |"},{"id":"returns","title":"Returns","role":"returns","text":"Reports whether the cluster was configured successfully. Displays an error message if the configuration attempt fails."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"sh","code":"rladmin cluster config \n        [ auditing db_conns audit_protocol { TCP | local } \n           audit_address <audit_address> audit_port <audit_port> ]\n        [ availability_lag_tolerance_ms <milliseconds> ]\n        [bigstore_driver {speedb | rocksdb} ]\n        [ control_cipher_suites <BoringSSL cipher list> ]\n        [ cm_port <number> ]\n        [ cm_session_timeout_minutes <minutes> ]\n        [ cnm_http_port <number> ]\n        [ cnm_https_port <number> ]\n        [ crdb_coordinator_port <number> ]\n        [ data_cipher_list <openSSL cipher list> ]\n        [ data_cipher_suites_tls_1_3 <openSSL cipher list> ]\n        [ debuginfo_path <filepath> ]\n        [ encrypt_pkeys { enabled | disabled } ]\n        [ envoy_admin_port <new-port> ]\n        [ envoy_mgmt_server_port <new-port> ]\n        [ external_ipv6 { enabled | disabled } ]\n        [ handle_redirects { enabled | disabled } ]\n        [ handle_metrics_redirects { enabled | disabled } ]\n        [ http_support { enabled | disabled } ]\n        [ ipv6 { enabled | disabled } ]\n        [ min_control_TLS_version { 1.2 | 1.3 } ]\n        [ min_data_TLS_version { 1.2 | 1.3 } ]\n        [ min_sentinel_TLS_version { 1.2 | 1.3 } ]\n        [ options_method_forbidden { enabled | disabled } ]\n        [ reserved_ports <list of ports/port ranges> ]\n        [ s3_url <URL> ]\n        [ s3_ca_cert <filepath> ]\n        [ sentinel_tls_mode { allowed | required | disabled } ]\n        [ sentinel_cipher_suites <golang cipher list> ]\n        [ services { alert_mgr | call_home_agent | cm_server | \n                     crdb_controller | crdb_coordinator | crdb_worker | \n                     entraid_agent_mgr | ldap_agent_mgr | mdns_server | \n                     metrics_local_storage_service | \n                     pdns_server | sentinel_service | \n                     stats_archiver } { enabled | disabled } ]\n        [ upgrade_mode { enabled | disabled } ]","section_id":"overview"},{"id":"example-ex0","language":"sh","code":"$ rladmin cluster config cm_session_timeout_minutes 20\nCluster configured successfully","section_id":"example"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/rladmin/cluster/create","title":"rladmin cluster create","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/create/","summary":"Creates a new cluster.","tags":["docs","operate","rs"],"last_updated":"2025-05-19T15:06:21-05:00","page_type":"content","content_hash":"ded31ab18d6536c9ada189a92034673ec64e24fd22ccc25c32719961e6b1aaf5","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Creates a new cluster. The node where you run `rladmin cluster create` becomes the first node of the new cluster.\n\n[code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameter | Type/Value | Description |\n|-----------|------------|-------------|\n| addr | IP address | The node's internal IP address (optional) |\n| ccs_persistent_path | filepath (default:&nbsp;/var/opt/redislabs/persist) | Path to the location of CCS snapshots (optional) |\n| ephemeral_path | filepath (default:&nbsp;/var/opt/redislabs) | Path to the ephemeral storage location (optional) |\n| external_addr | list of IP addresses | A space-delimited list of the node's external IP addresses (optional) |\n| flash_enabled | | Enables flash storage (optional) |\n| flash_path | filepath (default:&nbsp;/var/opt/redislabs/flash) | Path to the flash storage location (optional) |\n| license_file | filepath | Path to the RLEC license file (optional) |\n| name | string | Cluster name |\n| node_uid | integer | Unique node ID (optional) |\n| password | string | Admin user's password |\n| persistent_path | filepath (default:&nbsp;/var/opt/redislabs/persist) | Path to the persistent storage location (optional) |\n| rack_aware | | Activates or deactivates rack awareness (optional) |\n| rack_id | string | The rack's unique identifier (optional) |\n| register_dns_suffix | | Enables database mapping to both internal and external IP addresses (optional) |\n| second_rack_id | string | The unique identifier of the node's second rack ID for two-dimensional rack awareness (optional) |\n| username | email address | Admin user's email address |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns `ok` if the new cluster was created successfully. Otherwise, it returns an error message."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"sh","code":"cluster create\n        name <cluster name>\n        username <admin email>\n        password <admin password>\n        [ node_uid <node UID> ] \n        [ rack_aware ]\n        [ rack_id <node rack ID> ]\n        [ second_rack_id <node's second rack ID> ]\n        [ license_file <file> ]\n        [ ephemeral_path <path> ]\n        [ persistent_path <path> ]\n        [ ccs_persistent_path <path> ]\n        [ register_dns_suffix ]\n        [ flash_enabled ]\n        [ flash_path <path> ]\n        [ addr <IP.address> ]\n        [ external_addr <IP.address.1> [<IP.address.2> ... <IP.address.N>] ]","section_id":"overview"},{"id":"example-ex0","language":"sh","code":"$ rladmin cluster create name cluster.local \\\n        username admin@example.com \\\n        password admin-password\nCreating a new cluster... ok","section_id":"example"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/rladmin/cluster/debug_info","title":"rladmin cluster debug_info","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/debug_info/","summary":"Creates a support package.","tags":["docs","operate","rs"],"last_updated":"2024-03-30T15:43:20+01:00","page_type":"content","content_hash":"1f62f754d6a7aafa3519806fd93637a4097338d99062ec6e62e15b118c99e9db","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Downloads a support package to the specified path. If you do not specify a path, it downloads the package to the default path specified in the cluster configuration file.\n\n[code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameter | Type/Value | Description |\n|-----------|------------|-------------|\n| node | integer | Downloads a support package for the specified node |\n| path | filepath | Specifies the location where the support package should download |"},{"id":"returns","title":"Returns","role":"returns","text":"Reports the progress of the support package download."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"sh","code":"rladmin cluster debug_info\n        [ node <ID> ]\n        [ path <path> ]","section_id":"overview"},{"id":"example-ex0","language":"sh","code":"$ rladmin cluster debug_info node 1\nPreparing the debug info files package\nDownloading...\n[==================================================]\nDownloading complete. File /tmp/debuginfo.20220511-215637.node-1.tar.gz is saved.","section_id":"example"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/rladmin/cluster","title":"rladmin cluster","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/","summary":"Manage cluster.","tags":["docs","operate","rs"],"last_updated":"2024-02-26T13:11:00+01:00","children":[{"id":"operate/rs/references/cli-utilities/rladmin/cluster/certificate","summary":"Sets cluster certificates.","title":"rladmin cluster certificate","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/certificate/"},{"id":"operate/rs/references/cli-utilities/rladmin/cluster/change_password_hashing_algorithm","summary":"Changes the password hashing algorithm.","title":"rladmin cluster change_password_hashing_algorithm","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/change_password_hashing_algorithm/"},{"id":"operate/rs/references/cli-utilities/rladmin/cluster/config","summary":"Updates the cluster's configuration.","title":"rladmin cluster config","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/config/"},{"id":"operate/rs/references/cli-utilities/rladmin/cluster/create","summary":"Creates a new cluster.","title":"rladmin cluster create","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/create/"},{"id":"operate/rs/references/cli-utilities/rladmin/cluster/debug_info","summary":"Creates a support package.","title":"rladmin cluster debug_info","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/debug_info/"},{"id":"operate/rs/references/cli-utilities/rladmin/cluster/join","summary":"Adds a node to an existing cluster.","title":"rladmin cluster join","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/join/"},{"id":"operate/rs/references/cli-utilities/rladmin/cluster/master","summary":"Identifies or changes the cluster's master node.","title":"rladmin cluster master","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/master/"},{"id":"operate/rs/references/cli-utilities/rladmin/cluster/ocsp","summary":"Manages OCSP.","title":"rladmin cluster ocsp","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/ocsp/"},{"id":"operate/rs/references/cli-utilities/rladmin/cluster/recover","summary":"Recovers a cluster from a backup file.","title":"rladmin cluster recover","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/recover/"},{"id":"operate/rs/references/cli-utilities/rladmin/cluster/reset_password","summary":"Changes the password for a given email.","title":"rladmin cluster reset_password","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/reset_password/"},{"id":"operate/rs/references/cli-utilities/rladmin/cluster/running_actions","summary":"Lists all active tasks.","title":"rladmin cluster running_actions","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/running_actions/"},{"id":"operate/rs/references/cli-utilities/rladmin/cluster/stats_archiver","summary":"Enables/deactivates the stats archiver.","title":"rladmin cluster stats_archiver","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/stats_archiver/"}],"page_type":"content","content_hash":"7d8aeeb339597a858f16301ba14b9a437459eb03c90eeea19afdceac22c3d2b9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Manages cluster configuration and administration. Most `rladmin cluster` commands are only for clusters that are already configured, while a few others are only for new clusters that have not been configured."},{"id":"commands-for-configured-clusters","title":"Commands for configured clusters","role":"syntax","text":"| Command | Description |\n| --- | --- |\n| [certificate](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/certificate/) | Sets cluster certificates. |\n| [change_password_hashing_algorithm](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/change_password_hashing_algorithm/) | Changes the password hashing algorithm. |\n| [config](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/config/) | Updates the cluster's configuration. |\n| [debug_info](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/debug_info/) | Creates a support package. |\n| [master](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/master/) | Identifies or changes the cluster's master node. |\n| [ocsp](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/ocsp/) | Manages OCSP. |\n| [reset_password](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/reset_password/) | Changes the password for a given email. |\n| [running_actions](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/running_actions/) | Lists all active tasks. |\n| [stats_archiver](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/stats_archiver/) | Enables/deactivates the stats archiver. |"},{"id":"commands-for-non-configured-clusters","title":"Commands for non-configured clusters","role":"syntax","text":"| Command | Description |\n| --- | --- |\n| [create](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/create/) | Creates a new cluster. |\n| [join](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/join/) | Adds a node to an existing cluster. |\n| [recover](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/recover/) | Recovers a cluster from a backup file. |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/rladmin/cluster/join","title":"rladmin cluster join","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/join/","summary":"Adds a node to an existing cluster.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:21+01:00","page_type":"content","content_hash":"acf13f3c1516e447550d3e1339bbb1d4772036d04f8dcbe4ae257ead0bdddd36","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Adds a node to an existing cluster.\n\n[code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameter | Type/Value | Description |\n|-----------|------------|-------------|\n| accept_servers | 'enabled'<br />'disabled' | Allows allocation of resources on the new node when enabled (optional) |\n| addr | IP address | Sets a node's internal IP address. If not provided, the node sets the address automatically. (optional) |\n| ccs_persistent_path | filepath (default:&nbsp;/var/opt/redislabs/persist) | Path to the CCS snapshot location (the default is the same as persistent_path) (optional) |\n| client_cert | filepath | Path to a file containing the client certificate in PEM format. Use either a username and password or certificate credentials to join a node. Required with `client_key`. See [Certificate-based authentication for cluster management](https://redis.io/docs/latest/operate/rs/security/certificates/certificate-based-authentication#certificate-based-authentication-for-cluster-management). |\n| client_key | filepath | Path to a file containing the client certificate's private key in PEM format. Required with `client_cert`. |\n| cnm_http_port | integer | Joins a cluster that has a non-default cnm_http_port (optional) |\n| ephemeral_path | filepath | Path to the ephemeral storage location (optional) |\n| external_addr | list of IP addresses | Sets a node's external IP addresses (space-delimited list). If not provided, the node sets the address automatically. (optional) |\n| flash_enabled |  | Enables flash capabilities for a database (optional) |\n| flash_path | filepath (default:&nbsp;/var/opt/redislabs/flash) | Path to the flash storage location in case the node does not support CAPI (required if flash_enabled) |\n| nodes | IP address | Internal IP address of an existing node in the cluster |\n| override_rack_id |  | Changes to a new rack, specified by `rack_id` (optional) |\n| override_repair |  | Enables joining a cluster with a dead node (optional) |\n| password | string | Admin user's password. Required unless you use certificate credentials. |\n| persistent_path | filepath (default:&nbsp;/var/opt/redislabs/persist) | Path to the persistent storage location (optional) |\n| rack_id | string | Moves the node to the specified rack (optional) |\n| replace_node | integer | Replaces the specified node with the new node (optional) |\n| second_rack_id | string | The unique identifier of the node's second rack ID for two-dimensional rack awareness (optional) |\n| trusted_ca | filepath | Path to a file containing the CA that validates the API certificate the cluster presents, in PEM format. If you omit it, the node uses the certificates in the cluster's `mtls_trusted_ca.pem` file. (optional) |\n| username | email address | Admin user's email address. Required unless you use certificate credentials. |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns `ok` if the node joined the cluster successfully. Otherwise, it returns an error message."},{"id":"example","title":"Example","role":"example","text":"[code example]\n\nTo join a node using certificate credentials instead of a username and password, pass the file paths to the certificate, key, and CA:\n\n[code example]"}],"examples":[{"id":"overview-ex0","language":"sh","code":"rladmin cluster join\n        nodes <node IP address>\n        { username <admin user email> password <admin password>\n          | client_cert <path> client_key <path> [ trusted_ca <path> ] }\n        [ ephemeral_path <path> ]\n        [ persistent_path <path> ]\n        [ ccs_persistent_path <path> ]\n        [ rack_id <node rack ID> ]\n        [ second_rack_id <node's second rack ID> ]\n        [ override_rack_id ]\n        [ replace_node <node UID> ]\n        [ flash_enabled ]\n        [ flash_path <path> ]\n        [ addr <IP.address> ]\n        [ external_addr <IP.address.1> [<IP.address.2> ... <IP.address.N>] ]\n        [ override_repair ]\n        [ accept_servers { enabled | disabled } ]\n        [ cnm_http_port <port> ]","section_id":"overview"},{"id":"example-ex0","language":"sh","code":"$ rladmin cluster join nodes 192.0.2.2 \\\n        username admin@example.com \\\n        password admin-password\nJoining cluster... ok","section_id":"example"},{"id":"example-ex1","language":"sh","code":"$ rladmin cluster join nodes 192.0.2.2 \\\n        client_cert /etc/opt/redislabs/client.pem \\\n        client_key /etc/opt/redislabs/client.key \\\n        trusted_ca /etc/opt/redislabs/ca.pem\nJoining cluster... ok","section_id":"example"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/rladmin/cluster/master","title":"rladmin cluster master","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/master/","summary":"Identifies or changes the cluster's master node.","tags":["docs","operate","rs"],"last_updated":"2023-12-14T15:27:49+01:00","page_type":"content","content_hash":"489f6816ebb3c3fda7b02975504cc5dea3db7f767794089517721f6d3163104b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Identifies the cluster's master node. Use `set` to change the cluster's master to a different node.\n\n[code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameter | Type/Value | Description |\n|-----------|------------|-------------|\n| node_id | integer | Unique node ID |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns the ID of the cluster's master node. Otherwise, it returns an error message."},{"id":"example","title":"Example","role":"example","text":"Identify the cluster's master node:\n\n[code example]\n\nChange the cluster master to node 3:\n\n[code example]"}],"examples":[{"id":"overview-ex0","language":"sh","code":"cluster master [ set <node_id> ]","section_id":"overview"},{"id":"example-ex0","language":"sh","code":"$ rladmin cluster master\nNode 1 is the cluster master node","section_id":"example"},{"id":"example-ex1","language":"sh","code":"$ rladmin cluster master set 3\nNode 3 set to be the cluster master node","section_id":"example"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/rladmin/cluster/ocsp","title":"rladmin cluster ocsp","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/ocsp/","summary":"Manages OCSP.","tags":["docs","operate","rs"],"last_updated":"2023-12-14T15:27:49+01:00","page_type":"content","content_hash":"3c2529991cc5408f5328d43addaaa081d601027c6f05e25c6299fa36836bd357","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Manages OCSP configuration and verifies the status of a server certificate maintained by a third-party [certificate authority (CA)](https://en.wikipedia.org/wiki/Certificate_authority)."},{"id":"ocsp-certificate_compatible","title":"`ocsp certificate_compatible`","role":"content","text":"Checks if the proxy certificate contains an OCSP URI.\n\n[code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"None"},{"id":"returns","title":"Returns","role":"returns","text":"Returns the OCSP URI if it exists. Otherwise, it returns an error."},{"id":"example","title":"Example","role":"example","text":"[code example]"},{"id":"ocsp-config","title":"`ocsp config`","role":"content","text":"Displays or updates OCSP configuration. Run the command without the `set` option to display the current configuration of a parameter.\n\n[code example]"},{"id":"parameters-1","title":"Parameters","role":"parameters","text":"| Parameter | Type/Value | Description |\n|-----------|---------------|-------------|\n| ocsp_functionality | enabled<br></br>disabled | Enables or turns off OCSP for the cluster |\n| query_frequency | integer <nobr>(range: 60-86400)</nobr> <nobr>(default: 3600)</nobr> | The time interval in seconds between OCSP queries to check the certificate's status |\n| recovery_frequency | integer <nobr>(range: 60-86400)</nobr> <nobr>(default: 60)</nobr> | The time interval in seconds between retries after a failed query |\n| recovery_max_tries | integer <nobr>(range: 1-100)</nobr> <nobr>(default: 5)</nobr> | The number of retries before the validation query fails and invalidates the certificate |\n| responder_url | string | The OCSP server URL embedded in the proxy certificate (you cannot manually set this parameter) |\n| response_timeout | integer <nobr>(range: 1-60)</nobr> <nobr>(default: 1)</nobr> | The time interval in seconds to wait for a response before timing out |"},{"id":"returns-1","title":"Returns","role":"returns","text":"If you run the `ocsp config` command without the `set` option, it displays the specified parameter's current configuration."},{"id":"example-1","title":"Example","role":"example","text":"[code example]"},{"id":"ocsp-status","title":"`ocsp status`","role":"content","text":"Returns the latest cached status of the certificate's OCSP response.\n\n[code example]"},{"id":"parameters-2","title":"Parameters","role":"parameters","text":"None"},{"id":"returns-2","title":"Returns","role":"returns","text":"Returns the latest cached status of the certificate's OCSP response."},{"id":"example-2","title":"Example","role":"example","text":"[code example]"},{"id":"ocsp-test_certificate","title":"`ocsp test_certificate`","role":"content","text":"Queries the OCSP server for the certificate's latest status, then caches and displays the response.\n\n[code example]"},{"id":"parameters-3","title":"Parameters","role":"parameters","text":"None"},{"id":"returns-3","title":"Returns","role":"returns","text":"Returns the latest status of the certificate's OCSP response."},{"id":"example-3","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"ocsp-certificate_compatible-ex0","language":"sh","code":"rladmin cluster ocsp certificate_compatible","section_id":"ocsp-certificate_compatible"},{"id":"example-ex0","language":"sh","code":"$ rladmin cluster ocsp certificate_compatible\nSuccess. OCSP URI is http://responder.ocsp.url.com","section_id":"example"},{"id":"ocsp-config-ex0","language":"sh","code":"rladmin cluster ocsp config <OCSP parameter>\n        [set <value>]","section_id":"ocsp-config"},{"id":"example-1-ex0","language":"sh","code":"$ rladmin cluster ocsp config recovery_frequency\nRecovery frequency of the OCSP server is 60 seconds\n$ rladmin cluster ocsp config recovery_frequency set 30\n$ rladmin cluster ocsp config recovery_frequency\nRecovery frequency of the OCSP server is 30 seconds","section_id":"example-1"},{"id":"ocsp-status-ex0","language":"sh","code":"rladmin cluster ocsp status","section_id":"ocsp-status"},{"id":"example-2-ex0","language":"sh","code":"$ rladmin cluster ocsp status\nOCSP certificate status is: REVOKED\nproduced_at: Wed, 22 Dec 2021 12:50:11 GMT\nresponder_url: http://responder.ocsp.url.com\nrevocation_time: Wed, 22 Dec 2021 12:50:04 GMT\nthis_update: Wed, 22 Dec 2021 12:50:11 GMT","section_id":"example-2"},{"id":"ocsp-test_certificate-ex0","language":"sh","code":"rladmin cluster ocsp test_certificate","section_id":"ocsp-test_certificate"},{"id":"example-3-ex0","language":"sh","code":"$ rladmin cluster ocsp test_certificate\nInitiating a query to OCSP server\n...OCSP certificate status is: REVOKED\nproduced_at: Wed, 22 Dec 2021 12:50:11 GMT\nresponder_url: http://responder.ocsp.url.com\nrevocation_time: Wed, 22 Dec 2021 12:50:04 GMT\nthis_update: Wed, 22 Dec 2021 12:50:11 GMT","section_id":"example-3"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/rladmin/cluster/recover","title":"rladmin cluster recover","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/recover/","summary":"Recovers a cluster from a backup file.","tags":["docs","operate","rs"],"last_updated":"2025-05-19T15:06:21-05:00","page_type":"content","content_hash":"d30a2d72cceaad6ce9f7095c7ebe6f5fca36b44f99ca71a767610510bba4b0e4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Recovers a cluster from a backup file. The default location of the configuration backup file is `/var/opt/redislabs/persist/ccs/ccs-redis.rdb`.\n\n[code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameter | Type/Value | Description |\n|-----------|------------|-------------|\n| addr | IP address | Sets a node's internal IP address. If not provided, the node sets the address automatically. (optional) |\n| ccs_persistent_path | filepath | Path to the location of CCS snapshots (default is the same as persistent_path) (optional) |\n| external_addr | IP address | Sets a node's external IP address. If not provided, the node sets the address automatically. (optional) |\n| ephemeral_path | filepath (default:&nbsp;/var/opt/redislabs) | Path to an ephemeral storage location (optional) |\n| filename | filepath | Backup file to use for recovery |\n| flash_enabled | | Enables flash storage (optional) |\n| flash_path | filepath (default:&nbsp;/var/opt/redislabs/flash) | Path to the flash storage location in case the node does not support CAPI (required if flash_enabled) |\n| node_uid | integer (default:&nbsp;1) | Specifies which node will recover first and become master (optional) |\n| override_rack_id | | Changes to a new rack, specified by `rack_id` (optional) |\n| persistent_path | filepath | Path to the persistent storage location (optional) |\n| rack_id | string | Switches to the specified rack (optional) |\n| second_rack_id | string | The unique identifier of a second rack ID for two-dimensional rack awareness (optional) |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns `ok` if the cluster recovered successfully. Otherwise, it returns an error message."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"sh","code":"rladmin cluster recover\n        filename <recovery filename>\n        [ ephemeral_path <path> ]\n        [ persistent_path <path> ]\n        [ ccs_persistent_path <path> ]\n        [ rack_id <ID> ]\n        [ second_rack_id <ID> ]\n        [ override_rack_id ]\n        [ node_uid <number> ]\n        [ flash_enabled ]\n        [ flash_path <path> ]\n        [ addr <IP address> ]\n        [ external_addr <IP address> ]","section_id":"overview"},{"id":"example-ex0","language":"sh","code":"$ rladmin cluster recover filename /tmp/persist/ccs/ccs-redis.rdb node_uid 1 rack_id 5\nInitiating cluster recovery... ok","section_id":"example"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/rladmin/cluster/reset_password","title":"rladmin cluster reset_password","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/reset_password/","summary":"Changes the password for a given email.","tags":["docs","operate","rs"],"last_updated":"2023-12-14T15:27:49+01:00","page_type":"content","content_hash":"2419e595a67c30bad41d8d435807f897ca009d1ce4a193b189fa53d2218bf9ec","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Changes the password for the user associated with the specified email address.\n\nEnter a new password when prompted. Then enter the same password when prompted a second time to confirm the password change.\n\n[code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameter | Type/Value | Description |\n|-----------|------------|-------------|\n| user email | email address | The email address of the user that needs a password reset |"},{"id":"returns","title":"Returns","role":"returns","text":"Reports whether the password change succeeded or an error occurred."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"sh","code":"rladmin cluster reset_password <user email>","section_id":"overview"},{"id":"example-ex0","language":"sh","code":"$ rladmin cluster reset_password user@example.com\nNew password: \nNew password (again): \nPassword changed.","section_id":"example"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/rladmin/cluster/running_actions","title":"rladmin cluster running_actions","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/running_actions/","summary":"Lists all active tasks.","tags":["docs","operate","rs"],"last_updated":"2023-12-14T15:27:49+01:00","page_type":"content","content_hash":"f0102d4ddaa0e25454c900b826c646b4815eded7a8382ef37bdbb5c9331262f6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Lists all active tasks running on the cluster.\n\n[code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"None"},{"id":"returns","title":"Returns","role":"returns","text":"Returns details about any active tasks running on the cluster."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"sh","code":"rladmin cluster running_actions","section_id":"overview"},{"id":"example-ex0","language":"sh","code":"$ rladmin cluster running_actions\nGot 1 tasks:\n1) Task: maintenance_on (ce391d81-8d51-4ce2-8f63-729c7ac2589e) Node: 1 Status: running","section_id":"example"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/rladmin/cluster/stats_archiver","title":"rladmin cluster stats_archiver","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/stats_archiver/","summary":"Enables/deactivates the stats archiver.","tags":["docs","operate","rs"],"last_updated":"2023-12-14T15:27:49+01:00","page_type":"content","content_hash":"1072e23a6feebd80e82ae6e2812056b638f3300c0c511664a1d3348470f09ba5","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Enables or deactivates the stats archiver, which logs statistics in CSV (comma-separated values) format.\n\n[code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameter | Description |\n|-----------|-------------|\n| enabled | Turn on the stats archiver |\n| disabled | Turn off the stats archiver |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns the updated status of the stats archiver."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"sh","code":"rladmin cluster stats_archiver { enabled | disabled }","section_id":"overview"},{"id":"example-ex0","language":"sh","code":"$ rladmin cluster stats_archiver enabled \nStatus: enabled","section_id":"example"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/rladmin/failover","title":"rladmin failover","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/failover/","summary":"Fail over primary shards of a database to their replicas.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:21+01:00","page_type":"content","content_hash":"1a9bf31e0f589e08f9beffc439b5bd28e015a8e4c14dd41277fa698e8bc9ed7b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Fails over one or more primary (also known as master) shards of a database and promotes their respective replicas to primary shards.\n\n[code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameter | Type/Value                     | Description                                                                                   |\n|-----------|--------------------------------|-----------------------------------------------------------------------------------------------|\n| db        | db:\\<id\\><br /> name           | Fail over shards for the specified database                                                   |\n| shard     | one or more primary shard IDs  | Primary shard or shards to fail over                                                          |\n| immediate |                                | Perform failover without verifying the replica shards are in full sync with the master shards |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns `Finished successfully` if the failover completed. Otherwise, it returns an error.\n\nUse [`rladmin status shards`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status#status-shards) to verify that the failover completed."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":" sh","code":"rladmin failover\n        [db { db:<id> | <name> }]\n        shard <id1 ... idN>\n        [immediate]","section_id":"overview"},{"id":"example-ex0","language":" sh","code":"$ rladmin status shards\nSHARDS:\nDB:ID  NAME        ID         NODE     ROLE     SLOTS     USED_MEMORY    STATUS\ndb:5   tr01        redis:12   node:1   slave    0-16383   3.02MB         OK     \ndb:5   tr01        redis:13   node:2   master   0-16383   3.09MB         OK     \n$ rladmin failover shard 13\nExecuting shard fail-over: OOO.\nFinished successfully\n$ rladmin status shards\nSHARDS:\nDB:ID  NAME        ID         NODE     ROLE     SLOTS     USED_MEMORY    STATUS\ndb:5   tr01        redis:12   node:1   master   0-16383   3.12MB         OK     \ndb:5   tr01        redis:13   node:2   slave    0-16383   2.99MB         OK","section_id":"example"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/rladmin/help","title":"rladmin help","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/help/","summary":"Shows available commands or specific command usage.","tags":["docs","operate","rs"],"last_updated":"2023-12-14T15:27:49+01:00","page_type":"content","content_hash":"0a6af761d241cd10f7cca6afdb10682343e89e7c286dd944d40ee46fbb37436c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Lists all options and parameters for `rladmin` commands.\n\n[code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameter | Description |\n|-----------|-------------|\n|  command   |  Display help for this `rladmin` command (optional)  |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns a list of available `rladmin` commands.\n\nIf a `command` is specified, returns a list of all the options and parameters for that `rladmin` command."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":" sh","code":"rladmin help [command]","section_id":"overview"},{"id":"example-ex0","language":"sh","code":"$ rladmin help\nusage: rladmin [options] [command] [command args]\n\nOptions:\n  -y  Assume Yes for all required user confirmations.\n\nCommands:\n  bind      Bind an endpoint\n  cluster   Cluster management commands\n  exit      Exit admin shell\n  failover  Fail-over master to slave\n  help      Show available commands, or use help <command> for a specific command\n  info      Show information about tunable parameters\n  migrate   Migrate elements between nodes\n  node      Node management commands\n  placement Configure shards placement policy\n  recover   Recover databases\n  restart   Restart database shards\n  status    Show status information\n  suffix    Suffix management\n  tune      Tune system parameters\n  upgrade   Upgrade entity version\n  verify    Cluster verification reports\n\nUse \"rladmin help [command]\" to get more information on a specific command.","section_id":"example"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/rladmin","title":"rladmin","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/","summary":"Manage Redis Software clusters and databases.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:21+01:00","children":[{"id":"operate/rs/references/cli-utilities/rladmin/bind","summary":"Manages the proxy policy for a specified database endpoint.","title":"rladmin bind","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/bind/"},{"id":"operate/rs/references/cli-utilities/rladmin/cluster","summary":"Manage cluster.","title":"rladmin cluster","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/"},{"id":"operate/rs/references/cli-utilities/rladmin/failover","summary":"Fail over primary shards of a database to their replicas.","title":"rladmin failover","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/failover/"},{"id":"operate/rs/references/cli-utilities/rladmin/help","summary":"Shows available commands or specific command usage.","title":"rladmin help","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/help/"},{"id":"operate/rs/references/cli-utilities/rladmin/info","summary":"Shows the current configuration of a cluster, database, node, or proxy.","title":"rladmin info","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/info/"},{"id":"operate/rs/references/cli-utilities/rladmin/metrics","summary":"Configures the metrics stream engine and manages local metrics storage.","title":"rladmin metrics","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/metrics/"},{"id":"operate/rs/references/cli-utilities/rladmin/migrate","summary":"Moves Redis Software shards or endpoints to a new node in the same cluster.","title":"rladmin migrate","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/migrate/"},{"id":"operate/rs/references/cli-utilities/rladmin/node","summary":"Manage nodes.","title":"rladmin node","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/node/"},{"id":"operate/rs/references/cli-utilities/rladmin/placement","summary":"Configures the shard placement policy for a database.","title":"rladmin placement","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/placement/"},{"id":"operate/rs/references/cli-utilities/rladmin/recover","summary":"Recovers databases in recovery mode.","title":"rladmin recover","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/recover/"},{"id":"operate/rs/references/cli-utilities/rladmin/restart","summary":"Restarts Redis Software processes for a specific database.","title":"rladmin restart","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/restart/"},{"id":"operate/rs/references/cli-utilities/rladmin/status","summary":"Displays the current cluster status and topology information.","title":"rladmin status","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status/"},{"id":"operate/rs/references/cli-utilities/rladmin/suffix","summary":"Manages the DNS suffixes in the cluster.","title":"rladmin suffix","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/suffix/"},{"id":"operate/rs/references/cli-utilities/rladmin/tune","summary":"Configures parameters for databases, proxies, nodes, and clusters.","title":"rladmin tune","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune/"},{"id":"operate/rs/references/cli-utilities/rladmin/upgrade","summary":"Upgrades a database's Redis version and modules.","title":"rladmin upgrade","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/upgrade/"},{"id":"operate/rs/references/cli-utilities/rladmin/verify","summary":"Prints verification reports for the cluster.","title":"rladmin verify","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/verify/"}],"page_type":"content","content_hash":"e4d360f3a9b67ab8a8a90a6022b5a53b4ccc7ab9e06c6be2c978701b2149484b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"`rladmin` is a command-line utility that lets you perform administrative tasks such as failover, migration, and endpoint binding on a Redis Software cluster. You can also use `rladmin` to edit cluster and database configurations.\n\nAlthough you can use the Cluster Manager UI for some of these tasks, others are unique to the `rladmin` command-line tool."},{"id":"rladmin-commands","title":"`rladmin` commands","role":"content","text":"| Command | Description |\n| --- | --- |\n| [bind](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/bind/) | Manages the proxy policy for a specified database endpoint. |\n| [cluster](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/) | Manage cluster. |\n| [failover](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/failover/) | Fail over primary shards of a database to their replicas. |\n| [help](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/help/) | Shows available commands or specific command usage. |\n| [info](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/info/) | Shows the current configuration of a cluster, database, node, or proxy. |\n| [metrics](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/metrics/) | Configures the metrics stream engine and manages local metrics storage. |\n| [migrate](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/migrate/) | Moves Redis Software shards or endpoints to a new node in the same cluster. |\n| [node](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/node/) | Manage nodes. |\n| [placement](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/placement/) | Configures the shard placement policy for a database. |\n| [recover](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/recover/) | Recovers databases in recovery mode. |\n| [restart](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/restart/) | Restarts Redis Software processes for a specific database. |\n| [status](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status/) | Displays the current cluster status and topology information. |\n| [suffix](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/suffix/) | Manages the DNS suffixes in the cluster. |\n| [tune](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune/) | Configures parameters for databases, proxies, nodes, and clusters. |\n| [upgrade](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/upgrade/) | Upgrades a database's Redis version and modules. |\n| [verify](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/verify/) | Prints verification reports for the cluster. |"},{"id":"use-the-rladmin-shell","title":"Use the `rladmin` shell","role":"content","text":"To open the `rladmin` shell:\n\n1. Sign in to a Redis Software node with an account that is a member of the **redislabs** group.\n\n    The `rladmin` binary is located in `/opt/redislabs/bin`. If you don't have this directory in your `PATH`, you may want to add it. Otherwise, you can use `bash -l <username>` to sign in as a user with permissions for that directory.\n\n1. Run: `rladmin`\n\n    > [!NOTE]\n    > If the CLI does not recognize the `rladmin` command,\n    > run this command to load the necessary configuration first: <nobr>`bash -l`</nobr>\n\nIn the `rladmin` shell, you can:\n\n- Run any `rladmin` command without prefacing it with `rladmin`.\n- Enter `?` to view the full list of available commands.\n- Enter [`help`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/help) followed by the name of a command for a detailed explanation of the command and its usage.\n- Press the `Tab` key for command completion.\n- Enter `exit` or press `Control+D` to exit the `rladmin` shell and return to the terminal prompt."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/rladmin/info","title":"rladmin info","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/info/","summary":"Shows the current configuration of a cluster, database, node, or proxy.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:21+01:00","page_type":"content","content_hash":"dbd705387a1c2786a1c1b6493b8d980c15a85aa8106f3bcbc96cef7ed67a8c50","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Shows the current configuration of specified databases, proxies, clusters, or nodes."},{"id":"info-cluster","title":"`info cluster`","role":"content","text":"Lists the current configuration for the cluster.\n\n[code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"None"},{"id":"returns","title":"Returns","role":"returns","text":"Returns the current configuration for the cluster."},{"id":"example","title":"Example","role":"example","text":"[code example]"},{"id":"info-db","title":"`info db`","role":"content","text":"Shows the current configuration for databases.\n\n[code example]"},{"id":"parameters-1","title":"Parameters","role":"parameters","text":"| Parameter | Description |\n|-----------|-------------|\n| db:id     | ID of the specified database (optional) |\n| name      | Name of the specified database (optional) |"},{"id":"returns-1","title":"Returns","role":"returns","text":"Returns the current configuration for all databases.\n\nIf `db:<id>` or `<name>` is specified, returns the current configuration for the specified database."},{"id":"example-1","title":"Example","role":"example","text":"[code example]"},{"id":"info-node","title":"`info node`","role":"content","text":"Lists the current configuration for all nodes.\n\n[code example]"},{"id":"parameters-2","title":"Parameters","role":"parameters","text":"| Parameter | Description |\n|-----------|-------------|\n| id     | ID of the specified node |"},{"id":"returns-2","title":"Returns","role":"returns","text":"Returns the current configuration for all nodes.\n\nIf `<id>` is specified, returns the current configuration for the specified node."},{"id":"example-2","title":"Example","role":"example","text":"[code example]"},{"id":"info-proxy","title":"`info proxy`","role":"content","text":"Lists the current configuration for a proxy.\n\n[code example]"},{"id":"parameters-3","title":"Parameters","role":"parameters","text":"| Parameter | Description |\n|-----------|-------------|\n| id      | ID of the specified proxy |\n| all       | Show the current configuration for all proxies (optional) |"},{"id":"returns-3","title":"Returns","role":"returns","text":"If no parameter is specified or the `all` option is specified, returns the current configuration for all proxies.\n\nIf `<id>`is specified, returns the current configuration for the specified proxy."},{"id":"example-3","title":"Example","role":"example","text":"[code example]"},{"id":"info-metrics","title":"`info metrics`","role":"content","text":"Shows the cluster-wide [metrics configuration](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/metrics-configuration) for the v2 metrics stream engine.\n\n[code example]"},{"id":"parameters-4","title":"Parameters","role":"parameters","text":"None"},{"id":"returns-4","title":"Returns","role":"returns","text":"Returns the current metrics configuration. To update it, use [`rladmin metrics config`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/metrics#metrics-config)."},{"id":"example-4","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"info-cluster-ex0","language":"sh","code":"rladmin info cluster","section_id":"info-cluster"},{"id":"example-ex0","language":" sh","code":"$ rladmin info cluster\nCluster configuration:\n    repl_diskless: enabled\n    shards_overbooking: disabled\n    default_non_sharded_proxy_policy: single\n    default_sharded_proxy_policy: single\n    default_shards_placement: dense\n    default_fork_evict_ram: enabled\n    default_provisioned_redis_version: 6.0\n    redis_migrate_node_threshold: 0KB (0 bytes)\n    redis_migrate_node_threshold_percent: 4 (%)\n    redis_provision_node_threshold: 0KB (0 bytes)\n    redis_provision_node_threshold_percent: 12 (%)\n    max_simultaneous_backups: 4\n    slave_ha: enabled\n    slave_ha_grace_period: 600\n    slave_ha_cooldown_period: 3600\n    slave_ha_bdb_cooldown_period: 7200\n    parallel_shards_upgrade: 0\n    show_internals: disabled\n    expose_hostnames_for_all_suffixes: disabled\n    login_lockout_threshold: 5\n    login_lockout_duration: 1800\n    login_lockout_counter_reset_after: 900\n    default_concurrent_restore_actions: 10\n    endpoint_rebind_propagation_grace_time: 15\n    data_internode_encryption: disabled\n    redis_upgrade_policy: major\n    db_conns_auditing: disabled\n    watchdog profile: local-network\n    http support: enabled\n    upgrade mode: disabled\n    cm_session_timeout_minutes: 15\n    cm_port: 8443\n    cnm_http_port: 8080\n    cnm_https_port: 9443\n    bigstore_driver: speedb","section_id":"example"},{"id":"info-db-ex0","language":"sh","code":"rladmin info db [ {db:<id> | <name>} ]","section_id":"info-db"},{"id":"example-1-ex0","language":" sh","code":"$ rladmin info db db:1\ndb:1 [database1]:\n    client_buffer_limits: 1GB (hard limit)/512MB (soft limit) in 30 seconds\n    slave_buffer: auto\n    pubsub_buffer_limits: 32MB (hard limit)/8MB (soft limit) in 60 seconds\n    proxy_client_buffer_limits: 0KB (hard limit)/0KB (soft limit) in 0 seconds\n    proxy_slave_buffer_limits: 1GB (hard limit)/512MB (soft limit) in 60 seconds\n    proxy_pubsub_buffer_limits: 32MB (hard limit)/8MB (soft limit) in 60 seconds\n    repl_backlog: 1.02MB (1073741 bytes)\n    repl_timeout: 360 seconds\n    repl_diskless: default\n    master_persistence: disabled\n    maxclients: 10000\n    conns: 5\n    conns_type: per-thread\n    sched_policy: cmp\n    max_aof_file_size: 300GB\n    max_aof_load_time: 3600 seconds\n    dedicated_replicaof_threads: 5\n    max_client_pipeline: 200\n    max_shard_pipeline: 2000\n    max_connections: 0\n    oss_cluster: disabled\n    oss_cluster_api_preferred_ip_type: internal\n    gradual_src_mode: disabled\n    gradual_src_max_sources: 1\n    gradual_sync_mode: auto\n    gradual_sync_max_shards_per_source: 1\n    slave_ha: disabled (database)\n    mkms: enabled\n    oss_sharding: disabled\n    mtls_allow_weak_hashing: disabled\n    mtls_allow_outdated_certs: disabled\n    data_internode_encryption: disabled\n    proxy_policy: single\n    db_conns_auditing: disabled\n    syncer_mode: centralized","section_id":"example-1"},{"id":"info-node-ex0","language":"sh","code":"rladmin info node [ <id> ]","section_id":"info-node"},{"id":"example-2-ex0","language":" sh","code":"$ rladmin info node 3\nCommand Output: node:3\n    address: 198.51.100.17\n    external addresses: N/A\n    recovery path: N/A\n    quorum only: disabled\n    max redis servers: 100\n    max listeners: 100","section_id":"example-2"},{"id":"info-proxy-ex0","language":" sh","code":"rladmin info proxy { <id> | all }","section_id":"info-proxy"},{"id":"example-3-ex0","language":" sh","code":"$ rladmin info proxy\nproxy:1\n    mode: dynamic\n    scale_threshold: 80 (%)\n    scale_duration: 30 (seconds)\n    max_threads: 8\n    threads: 3","section_id":"example-3"},{"id":"info-metrics-ex0","language":"sh","code":"rladmin info metrics","section_id":"info-metrics"},{"id":"example-4-ex0","language":" sh","code":"$ rladmin info metrics\nMetrics configuration:\n    key_distribution_enabled: True\n    key_size_buckets: 128M,512M\n    key_items_buckets: 1M,8M\n    local_storage_max_size_mb: 1024\n    local_storage_retention_days: 8\n    expose_db_tags: True\n    metrics_tag_keys_exposed: env,team\n    max_requests_in_flight: 2","section_id":"example-4"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/rladmin/metrics","title":"rladmin metrics","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/metrics/","summary":"Configures the metrics stream engine and manages local metrics storage.","tags":["docs","operate","rs"],"last_updated":"2026-09-29T16:51:25+03:00","page_type":"content","content_hash":"d3b3921e0d96f22f60360f1d9a87ada6b9a7b41950c42a373da7ae3aff313a56","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Manages the cluster-wide [metrics configuration](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/metrics-configuration) for the v2 metrics stream engine, and reports on and controls [local metrics storage](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/local-metrics-storage) on each node."},{"id":"metrics-config","title":"`metrics config`","role":"content","text":"Updates the cluster's metrics configuration. Specify one or more field/value pairs; at least one pair is required.\n\n[code example]\n\nTo view the current configuration, use [`rladmin info metrics`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/info#info-metrics)."},{"id":"parameters","title":"Parameters","role":"parameters","text":"For the available fields and their types, defaults, and validation, see the [metrics configuration object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/metrics_config).\n\nOn the command line, boolean fields take `enabled` or `disabled`, list fields take a comma-separated set of values, and an empty value (`\"\"`) clears a list field."},{"id":"returns","title":"Returns","role":"returns","text":"Returns a confirmation message when the update succeeds."},{"id":"example","title":"Example","role":"example","text":"[code example]"},{"id":"metrics-status","title":"`metrics status`","role":"content","text":"Shows the status of local metrics storage on all nodes or on a specific node.\n\n[code example]"},{"id":"parameters-1","title":"Parameters","role":"parameters","text":"| Parameter | Type/Value | Description |\n|-----------|------------|-------------|\n| node | integer | Shows the status of the specified node only. If omitted, shows all nodes. |"},{"id":"returns-1","title":"Returns","role":"returns","text":"Returns each node's store state, disk usage against the configured maximum, configured and effective retention, the date range of stored data, and health."},{"id":"example-1","title":"Example","role":"example","text":"[code example]\n\n[code example]"},{"id":"metrics-granular-start","title":"`metrics granular start`","role":"content","text":"Starts granular metrics collection on all nodes or on a specific node. Collection stops automatically after the maximum duration set in [`granular_metrics_job_settings`](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/job_scheduler/granular_metrics_job_settings).\n\n[code example]"},{"id":"parameters-2","title":"Parameters","role":"parameters","text":"| Parameter | Type/Value | Description |\n|-----------|------------|-------------|\n| node | integer | Starts collection on the specified node only. If omitted, starts collection on all nodes. |"},{"id":"returns-2","title":"Returns","role":"returns","text":"Returns the result for each node: `started`, `already running`, or `unknown`. If the result is `unknown`, run `rladmin metrics granular status node <uid>` to confirm it."},{"id":"example-2","title":"Example","role":"example","text":"[code example]"},{"id":"metrics-granular-stop","title":"`metrics granular stop`","role":"content","text":"Stops granular metrics collection on all nodes or on a specific node. The collected data stays on disk until you [delete it](#metrics-granular-cleanup) or the cleanup delay set in [`granular_metrics_job_settings`](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/job_scheduler/granular_metrics_job_settings) passes.\n\n[code example]"},{"id":"parameters-3","title":"Parameters","role":"parameters","text":"| Parameter | Type/Value | Description |\n|-----------|------------|-------------|\n| node | integer | Stops collection on the specified node only. If omitted, stops collection on all nodes. |"},{"id":"returns-3","title":"Returns","role":"returns","text":"Returns the result for each node: `stopped` or `already stopped`."},{"id":"example-3","title":"Example","role":"example","text":"[code example]"},{"id":"metrics-granular-status","title":"`metrics granular status`","role":"content","text":"Shows the granular metrics collection status of all nodes or of a specific node.\n\n[code example]"},{"id":"parameters-4","title":"Parameters","role":"parameters","text":"| Parameter | Type/Value | Description |\n|-----------|------------|-------------|\n| node | integer | Shows the status of the specified node only. If omitted, shows all nodes. |"},{"id":"returns-4","title":"Returns","role":"returns","text":"Returns each node's collection state and disk usage. For a running node, it shows when collection started and when it expires. For a stopped node, it shows when collection stopped and when the data is cleaned up."},{"id":"example-4","title":"Example","role":"example","text":"[code example]"},{"id":"metrics-granular-cleanup","title":"`metrics granular cleanup`","role":"content","text":"Deletes granular metrics data from all nodes or from a specific node.\n\n[code example]\n\nIf granular collection is still running on any node in scope, the command fails and deletes nothing. Stop collection with [`rladmin metrics granular stop`](#metrics-granular-stop) first."},{"id":"parameters-5","title":"Parameters","role":"parameters","text":"| Parameter | Type/Value | Description |\n|-----------|------------|-------------|\n| node | integer | Deletes granular data from the specified node only. If omitted, deletes granular data from all nodes. |"},{"id":"returns-5","title":"Returns","role":"returns","text":"Returns the result for each node. If collection is still running on any node in scope, returns an error that lists those nodes."},{"id":"example-5","title":"Example","role":"example","text":"The following command fails because collection is still running on node 1:\n\n[code example]"}],"examples":[{"id":"metrics-config-ex0","language":"sh","code":"rladmin metrics config <field> <value> [ <field> <value> ... ]","section_id":"metrics-config"},{"id":"example-ex0","language":"sh","code":"$ rladmin metrics config expose_db_tags enabled metrics_tag_keys_exposed env,team\nMetrics configuration updated successfully","section_id":"example"},{"id":"metrics-status-ex0","language":"sh","code":"rladmin metrics status [ node <uid> ]","section_id":"metrics-status"},{"id":"example-1-ex0","language":"sh","code":"$ rladmin metrics status\nMETRICS STORE:\nNODE  STATE     DISK USAGE              RETENTION       DATA RANGE                  HEALTH\n1     running   450MB / 1.0GB (44%)     8d (eff 6d)     2026-02-15 to 2026-02-23    ok\n2     running   512MB / 1.0GB (50%)     8d (eff 6d)     2026-02-15 to 2026-02-23    ok\n3     stopped   -                       8d              -                           -","section_id":"example-1"},{"id":"example-1-ex1","language":"sh","code":"$ rladmin metrics status node 1\nMetrics Store Status:\n  Node:             1\n  State:            running\n  Disk usage:       450MB / 1.0GB (44%)\n  Retention:        8 days (effective: 6 days)\n  Data range:       2026-02-15 to 2026-02-23\n  Health:           ok","section_id":"example-1"},{"id":"metrics-granular-start-ex0","language":"sh","code":"rladmin metrics granular start [ node <uid> ]","section_id":"metrics-granular-start"},{"id":"example-2-ex0","language":"sh","code":"$ rladmin metrics granular start\nStarting granular metrics:\n  - node:1: started (expires at 2026-02-22 15:30:00)\n  - node:2: already running (started at 2026-02-22 14:32:05, expires at 2026-02-22 15:32:05)\n  - node:4: unknown (could not reach node — retry 'rladmin metrics granular status node 4' to confirm)","section_id":"example-2"},{"id":"metrics-granular-stop-ex0","language":"sh","code":"rladmin metrics granular stop [ node <uid> ]","section_id":"metrics-granular-stop"},{"id":"example-3-ex0","language":"sh","code":"$ rladmin metrics granular stop\nStopping granular metrics:\n  - node:1: stopped (ran for 45m, stopped at 2026-02-22 14:45:00)\n  - node:2: already stopped","section_id":"example-3"},{"id":"metrics-granular-status-ex0","language":"sh","code":"rladmin metrics granular status [ node <uid> ]","section_id":"metrics-granular-status"},{"id":"example-4-ex0","language":"sh","code":"$ rladmin metrics granular status\nGRANULAR METRICS:\nNODE  STATE    STARTED              STOPPED              EXPIRES/CLEANUP    DISK\n1     running  2026-02-22 14:30:00  -                    expires in 47m     125MB\n2     stopped  -                    2026-02-22 14:30:00  cleanup in 23h     125MB","section_id":"example-4"},{"id":"metrics-granular-cleanup-ex0","language":"sh","code":"rladmin metrics granular cleanup [ node <uid> ]","section_id":"metrics-granular-cleanup"},{"id":"example-5-ex0","language":"sh","code":"$ rladmin metrics granular cleanup\nerror: cannot cleanup granular metrics — still running on 1 node:\n  - node:1 (started 2026-02-22 14:30:00, 47m ago)\nhint: stop these nodes first ('rladmin metrics granular stop' or 'rladmin metrics granular stop node <uid>').","section_id":"example-5"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/rladmin/migrate","title":"rladmin migrate","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/migrate/","summary":"Moves Redis Software shards or endpoints to a new node in the same cluster.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:21+01:00","page_type":"content","content_hash":"156e9612d3a2b3a74847d6f3dd1572df7c8d92d1825d557dd5d80815d6ce5fd9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Moves Redis Software shards or endpoints to a new node in the same cluster.\n\nFor more information about shard migration use cases and considerations, see [Migrate database shards](https://redis.io/docs/latest/operate/rs/databases/migrate-shards)."},{"id":"migrate-all_master_shards","title":"`migrate all_master_shards`","role":"content","text":"Moves all primary shards of a specified database or node to a new node in the same cluster.\n\n[code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameter                     | Type/Value             | Description                                                                     |\n|-------------------------------|------------------------|---------------------------------------------------------------------------------|\n| db                            | db:\\<id\\><br /> name   | Limits migration to a specific database                                           |\n| node                          | integer                | Limits migration to a specific origin node                                        |\n| target_node                   | integer                | Migration target node                                                           |\n| override_policy               |                        | Overrides the rack aware policy and allows primary and replica shards on the same node |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns `Done` if the migration completed successfully. Otherwise, returns an error.\n\nUse [`rladmin status shards`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status#status-shards) to verify the migration completed."},{"id":"example","title":"Example","role":"example","text":"[code example]"},{"id":"migrate-all_shards","title":"`migrate all_shards`","role":"content","text":"Moves all shards on a specified node to a new node in the same cluster.\n\n[code example]"},{"id":"parameters-1","title":"Parameters","role":"parameters","text":"| Parameter                     | Type/Value             | Description                                                                     |\n|-------------------------------|------------------------|---------------------------------------------------------------------------------|\n| node                          | integer                | Limits migration to a specific origin node                                        |\n| max_concurrent_bdb_migrations | integer                | Sets the maximum number of concurrent endpoint migrations                           |\n| override_policy               |                        | Overrides the rack aware policy and allows primary and replica shards on the same node |"},{"id":"returns-1","title":"Returns","role":"returns","text":"Returns `Done` if the migration completed successfully. Otherwise, returns an error.\n\nUse [`rladmin status shards`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status#status-shards) to verify the migration completed."},{"id":"example-1","title":"Example","role":"example","text":"[code example]"},{"id":"migrate-all_slave_shards","title":"`migrate all_slave_shards`","role":"content","text":"Moves all replica shards of a specified database or node to a new node in the same cluster.\n\n[code example]"},{"id":"parameters-2","title":"Parameters","role":"parameters","text":"| Parameter                     | Type/Value             | Description                                                                     |\n|-------------------------------|------------------------|---------------------------------------------------------------------------------|\n| db                            | db:\\<id\\><br /> name   | Limits migration to a specific database                                           |\n| node                          | integer                | Limits migration to a specific origin node                                        |\n| target_node                   | integer                | Migration target node                                                           |\n| override_policy               |                        | Overrides the rack aware policy and allows primary and replica shards on the same node |"},{"id":"returns-2","title":"Returns","role":"returns","text":"Returns `Done` if the migration completed successfully. Otherwise, returns an error.\n\nUse [`rladmin status shards`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status#status-shards) to verify the migration completed."},{"id":"example-2","title":"Example","role":"example","text":"[code example]"},{"id":"migrate-endpoint_to_shards","title":"`migrate endpoint_to_shards`","role":"content","text":"Moves database endpoints to the node where the majority of primary shards are located.\n\n[code example]"},{"id":"parameters-3","title":"Parameters","role":"parameters","text":"| Parameter                     | Type/Value             | Description                                                                     |\n|-------------------------------|------------------------|---------------------------------------------------------------------------------|\n| db                            | db:\\<id\\><br /> name   | Limits migration to a specific database                                           |\n| restrict_target_node          | integer | Moves the endpoint only if the target node matches the specified node |\n| commit                        |                        | Performs endpoint movement                                                      |\n| max_concurrent_bdb_migrations | integer                | Sets the maximum number of concurrent endpoint migrations                           |"},{"id":"returns-3","title":"Returns","role":"returns","text":"Returns a list of steps to perform the migration. If the `commit` flag is set, the steps will run and return `Finished successfully` if they were completed. Otherwise, returns an error.\n\nUse [`rladmin status endpoints`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status#status-endpoints) to verify that the endpoints were moved."},{"id":"example-3","title":"Example","role":"example","text":"[code example]"},{"id":"migrate-shard","title":"`migrate shard`","role":"content","text":"Moves one or more shards to a new node in the same cluster.\n\n[code example]"},{"id":"parameters-4","title":"Parameters","role":"parameters","text":"| Parameter                     | Type/Value             | Description                                                                     |\n|-------------------------------|------------------------|---------------------------------------------------------------------------------|\n| shard                         | list of shard IDs      | Shards to migrate                                                               |\n| preserve_roles                |                        | Performs an additional failover to guarantee the primary shards' roles are preserved |\n| target_node                   | integer                | Migration target node                                                           |\n| override_policy               |                        | Overrides the rack aware policy and allows primary and replica shards on the same node |"},{"id":"returns-4","title":"Returns","role":"returns","text":"Returns `Done` if the migration completed successfully. Otherwise, returns an error.\n\nUse [`rladmin status shards`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status#status-shards) to verify the migration completed."},{"id":"example-4","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"migrate-all_master_shards-ex0","language":"sh","code":"rladmin migrate { db { db:<id> | <name> } | node <origin node ID> }\n        all_master_shards\n        target_node <id>\n        [ override_policy ]","section_id":"migrate-all_master_shards"},{"id":"example-ex0","language":"sh","code":"$ rladmin status shards db db:6 sort ROLE\nSHARDS:\nDB:ID  NAME       ID         NODE    ROLE    SLOTS         USED_MEMORY   STATUS\ndb:6   tr02       redis:14   node:3  master  0-4095        3.01MB        OK     \ndb:6   tr02       redis:16   node:3  master  4096-8191     3.2MB         OK     \ndb:6   tr02       redis:18   node:3  master  8192-12287    3.2MB         OK     \ndb:6   tr02       redis:20   node:3  master  12288-16383   3.01MB        OK\n$ rladmin migrate db db:6 all_master_shards target_node 1\nMonitoring 8b0f28e2-4342-427a-a8e3-a68cba653ffe\nqueued - migrate_shards\nrunning - migrate_shards\nExecuting migrate_redis with shards_uids ['18', '14', '20', '16']\nOcompleted - migrate_shards\nDone\n$ rladmin status shards node 1\nSHARDS:\nDB:ID  NAME       ID         NODE    ROLE    SLOTS         USED_MEMORY   STATUS\ndb:6   tr02       redis:14   node:1  master  0-4095        3.22MB        OK     \ndb:6   tr02       redis:16   node:1  master  4096-8191     3.22MB        OK     \ndb:6   tr02       redis:18   node:1  master  8192-12287    3.22MB        OK     \ndb:6   tr02       redis:20   node:1  master  12288-16383   2.99MB        OK","section_id":"example"},{"id":"migrate-all_shards-ex0","language":" sh","code":"rladmin migrate node <origin node ID>\n            [ max_concurrent_bdb_migrations <value> ]\n            all_shards\n            target_node <id>\n            [ override_policy ]","section_id":"migrate-all_shards"},{"id":"example-1-ex0","language":"sh","code":"$ rladmin status shards node 1\nSHARDS:\nDB:ID  NAME       ID         NODE    ROLE    SLOTS         USED_MEMORY   STATUS\ndb:5   tr01       redis:12   node:1  master  0-16383       3.04MB        OK\ndb:6   tr02       redis:15   node:1  slave   0-4095        2.93MB        OK\ndb:6   tr02       redis:17   node:1  slave   4096-8191     2.93MB        OK\ndb:6   tr02       redis:19   node:1  slave   8192-12287    3.08MB        OK\ndb:6   tr02       redis:21   node:1  slave   12288-16383   3.08MB        OK\n$ rladmin migrate node 1 all_shards target_node 2\nMonitoring 71a4f371-9264-4398-a454-ce3ff4858c09\nqueued - migrate_shards\n.running - migrate_shards\nExecuting migrate_redis with shards_uids ['21', '15', '17', '19']\nOExecuting migrate_redis with shards_uids ['12']\nOcompleted - migrate_shards\nDone\n$ rladmin status shards node 2\nSHARDS:\nDB:ID  NAME       ID         NODE    ROLE    SLOTS         USED_MEMORY   STATUS\ndb:5   tr01       redis:12   node:2  master  0-16383       3.14MB        OK\ndb:6   tr02       redis:15   node:2  slave   0-4095        2.96MB        OK\ndb:6   tr02       redis:17   node:2  slave   4096-8191     2.96MB        OK\ndb:6   tr02       redis:19   node:2  slave   8192-12287    2.96MB        OK\ndb:6   tr02       redis:21   node:2  slave   12288-16383   2.96MB        OK","section_id":"example-1"},{"id":"migrate-all_slave_shards-ex0","language":"sh","code":"rladmin migrate { db { db:<id> | <name> } | node <origin node ID> }\n            all_slave_shards\n            target_node <id>\n            [ override_policy ]","section_id":"migrate-all_slave_shards"},{"id":"example-2-ex0","language":"sh","code":"$ rladmin status shards db db:6 node 2\nSHARDS:\nDB:ID  NAME      ID         NODE    ROLE   SLOTS          USED_MEMORY    STATUS\ndb:6   tr02      redis:15   node:2  slave  0-4095         3.06MB         OK\ndb:6   tr02      redis:17   node:2  slave  4096-8191      3.06MB         OK\ndb:6   tr02      redis:19   node:2  slave  8192-12287     3.06MB         OK\ndb:6   tr02      redis:21   node:2  slave  12288-16383    3.06MB         OK\n$ rladmin migrate db db:6 all_slave_shards target_node 3\nMonitoring 5d36a98c-3dc8-435f-8ed9-35809ba017a4\nqueued - migrate_shards\n.running - migrate_shards\nExecuting migrate_redis with shards_uids ['15', '17', '21', '19']\nOcompleted - migrate_shards\nDone\n$ rladmin status shards db db:6 node 3\nSHARDS:\nDB:ID  NAME      ID         NODE    ROLE   SLOTS          USED_MEMORY    STATUS\ndb:6   tr02      redis:15   node:3  slave  0-4095         3.04MB         OK\ndb:6   tr02      redis:17   node:3  slave  4096-8191      3.04MB         OK\ndb:6   tr02      redis:19   node:3  slave  8192-12287     3.04MB         OK\ndb:6   tr02      redis:21   node:3  slave  12288-16383    3.04MB         OK","section_id":"example-2"},{"id":"migrate-endpoint_to_shards-ex0","language":"sh","code":"rladmin migrate [ db { db:<id> | <name> } ]\n            endpoint_to_shards\n            [ restrict_target_node <id> ]\n            [ commit ]\n            [ max_concurrent_bdb_migrations <value> ]","section_id":"migrate-endpoint_to_shards"},{"id":"example-3-ex0","language":"sh","code":"$ rladmin status endpoints db db:6\nENDPOINTS:\nDB:ID    NAME   ID                 NODE      ROLE                       SSL     \ndb:6     tr02   endpoint:6:1       node:3    all-master-shards          No      \n$ rladmin migrate db db:6 endpoint_to_shards\n* Going to bind endpoint:6:1 to node 1\nDry-run completed, add 'commit' argument to execute\n$ rladmin migrate db db:6 endpoint_to_shards commit\n* Going to bind endpoint:6:1 to node 1\nExecuting bind endpoint:6:1: OOO.\nFinished successfully\n$ rladmin status endpoints db db:6\nENDPOINTS:\nDB:ID    NAME   ID                 NODE      ROLE                       SSL     \ndb:6     tr02   endpoint:6:1       node:1    all-master-shards          No","section_id":"example-3"},{"id":"migrate-shard-ex0","language":"sh","code":"rladmin migrate shard <id1.. idN>\n            [ preserve_roles ]\n            target_node <id>\n            [ override_policy ]","section_id":"migrate-shard"},{"id":"example-4-ex0","language":"sh","code":"$ rladmin status shards db db:5\nSHARDS:\nDB:ID  NAME        ID         NODE     ROLE     SLOTS     USED_MEMORY    STATUS\ndb:5   tr01        redis:12   node:2   master   0-16383   3.01MB         OK\ndb:5   tr01        redis:13   node:3   slave    0-16383   3.1MB          OK\n$ rladmin migrate shard 13 target_node 1\nMonitoring d2637eea-9504-4e94-a70c-76df087efcb2\nqueued - migrate_shards\n.running - migrate_shards\nExecuting migrate_redis with shards_uids ['13']\nOcompleted - migrate_shards\nDone\n$ rladmin status shards db db:5\nSHARDS:\nDB:ID  NAME        ID         NODE     ROLE     SLOTS     USED_MEMORY    STATUS\ndb:5   tr01        redis:12   node:2   master   0-16383   3.01MB         OK\ndb:5   tr01        redis:13   node:1   slave    0-16383   3.04MB         OK","section_id":"example-4"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/rladmin/node/addr","title":"rladmin node addr set","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/node/addr/","summary":"Sets a node's internal IP address.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:21+01:00","page_type":"content","content_hash":"181509abba44be4bdf59809fa0b44eb094a60b7ea529c8f97b898dfbbe4d1ed1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Sets the internal IP address of a node. You can only set the internal IP address when the node is down. See [Change internal IP address](https://redis.io/docs/latest/operate/rs/networking/multi-ip-ipv6#change-internal-ip-address) for detailed instructions.\n\n[code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameter | Type/Value                     | Description                                                                                   |\n|-----------|--------------------------------|-----------------------------------------------------------------------------------------------|\n| node      | integer                        | Sets the internal IP address of the specified node                                                |\n| addr   | IP address                     | Sets the node's internal IP address to the specified IP address                                      |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns `Updated successfully` if the IP address was set. Otherwise, it returns an error.\n\nUse [`rladmin status nodes`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status#status-nodes) to verify the internal IP address was changed."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"sh","code":"rladmin node <ID> addr set <IP address>","section_id":"overview"},{"id":"example-ex0","language":"sh","code":"$ rladmin status nodes\nCLUSTER NODES:\nNODE:ID ROLE   ADDRESS    EXTERNAL_ADDRESS HOSTNAME     SHARDS CORES FREE_RAM        PROVISIONAL_RAM VERSION   STATUS\n*node:1 master 192.0.2.2                   3d99db1fdf4b 5/100  6     16.06GB/19.54GB 12.46GB/16.02GB 6.2.12-37 OK\nnode:2  slave  192.0.2.3                   fc7a3d332458 0/100  6     -/19.54GB       -/16.02GB       6.2.12-37 DOWN, last seen 33s ago\nnode:3  slave  192.0.2.4                   b87cc06c830f 5/120  6     16.06GB/19.54GB 12.46GB/16.02GB 6.2.12-37 OK\n$ rladmin node 2 addr set 192.0.2.5\nUpdated successfully.\n$ rladmin status nodes\nCLUSTER NODES:\nNODE:ID ROLE   ADDRESS    EXTERNAL_ADDRESS HOSTNAME     SHARDS CORES FREE_RAM        PROVISIONAL_RAM VERSION   STATUS\n*node:1 master 192.0.2.2                   3d99db1fdf4b 5/100  6     14.78GB/19.54GB 11.18GB/16.02GB 6.2.12-37 OK\nnode:2  slave  192.0.2.5                   fc7a3d332458 0/100  6     14.78GB/19.54GB 11.26GB/16.02GB 6.2.12-37 OK\nnode:3  slave  192.0.2.4                   b87cc06c830f 5/120  6     14.78GB/19.54GB 11.18GB/16.02GB 6.2.12-37 OK","section_id":"example"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/rladmin/node/enslave","title":"rladmin node enslave","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/node/enslave/","summary":"Changes a node's resources to replicas.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:21+01:00","page_type":"content","content_hash":"a8072e1cb7ca0fe65ee39d7812538c5ac0f5575cb7d53a42ceeb2c888137b272","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Changes the resources of a node to replicas."},{"id":"node-enslave","title":"`node enslave`","role":"content","text":"Changes all of the node's endpoints and shards to replicas.\n\n[code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameter             | Type/Value                     | Description                                                                               |\n|-----------------------|--------------------------------|-------------------------------------------------------------------------------------------|\n| node                  | integer                        | Changes all of the node's endpoints and shards to replicas             |\n| demote_node           |                                | If the node is a primary node, changes the node to replica                     |\n| retry_timeout_seconds | integer                        | Retries on failure until the specified number of seconds has passed.                    |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns `OK` if the roles were successfully changed. Otherwise, it returns an error.\n\nUse [`rladmin status shards`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status#status-shards) to verify that the roles were changed."},{"id":"example","title":"Example","role":"example","text":"[code example]"},{"id":"node-enslave-endpoints_only","title":"`node enslave endpoints_only`","role":"content","text":"Changes the role for all endpoints on a node to replica.\n\n[code example]"},{"id":"parameters-1","title":"Parameters","role":"parameters","text":"| Parameter             | Type/Value                     | Description                                                                               |\n|-----------------------|--------------------------------|-------------------------------------------------------------------------------------------|\n| node                  | integer                        | Changes all of the node's endpoints to replicas                        |\n| retry_timeout_seconds | integer                        | Retries on failure until the specified number of seconds has passed.                    |"},{"id":"returns-1","title":"Returns","role":"returns","text":"Returns `OK` if the roles were successfully changed. Otherwise, it returns an error.\n\nUse [`rladmin status endpoints`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status#status-endpoints) to verify that the roles were changed."},{"id":"example-1","title":"Example","role":"example","text":"[code example]"},{"id":"node-enslave-shards_only","title":"`node enslave shards_only`","role":"content","text":"Changes the role for all shards of a node to replica.\n\n[code example]"},{"id":"parameters-2","title":"Parameters","role":"parameters","text":"| Parameter             | Type/Value                     | Description                                                                               |\n|-----------------------|--------------------------------|-------------------------------------------------------------------------------------------|\n| node                  | integer                        | Changes all of the node's shards to replicas                          |\n| retry_timeout_seconds | integer                        | Retries on failure until the specified number of seconds has passed.                    |"},{"id":"returns-2","title":"Returns","role":"returns","text":"Returns `OK` if the roles were successfully changed. Otherwise, it returns an error.\n\nUse [`rladmin status shards`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status#status-shards) to verify that the roles were changed."},{"id":"example-2","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"node-enslave-ex0","language":" sh","code":"rladmin node <ID> enslave\n        [demote_node]\n        [retry_timeout_seconds <seconds>]","section_id":"node-enslave"},{"id":"example-ex0","language":"sh","code":"$ rladmin status shards node 2\nSHARDS:\nDB:ID      NAME           ID               NODE         ROLE         SLOTS                  USED_MEMORY            STATUS\ndb:6       tr02           redis:14         node:2       master       0-4095                 3.2MB                  OK\ndb:6       tr02           redis:16         node:2       master       4096-8191              3.12MB                 OK\ndb:6       tr02           redis:18         node:2       master       8192-12287             3.16MB                 OK\ndb:6       tr02           redis:20         node:2       master       12288-16383            3.12MB                 OK\n$ rladmin status nodes\nCLUSTER NODES:\nNODE:ID ROLE   ADDRESS    EXTERNAL_ADDRESS  HOSTNAME     SHARDS CORES       FREE_RAM         PROVISIONAL_RAM  VERSION   STATUS\n*node:1 slave  192.0.2.12 198.51.100.1      3d99db1fdf4b 1/100  6           14.43GB/19.54GB  10.87GB/16.02GB  6.2.12-37 OK\nnode:2  master 192.0.2.13 198.51.100.2      fc7a3d332458 4/100  6           14.43GB/19.54GB  10.88GB/16.02GB  6.2.12-37 OK\nnode:3  slave  192.0.2.14                   b87cc06c830f 5/120  6           14.43GB/19.54GB  10.83GB/16.02GB  6.2.12-37 OK\n$ rladmin node 2 enslave demote_node\nPerforming enslave_node action on node:2: 100%\nOK\n$ rladmin status nodes\nCLUSTER NODES:\nNODE:ID ROLE   ADDRESS    EXTERNAL_ADDRESS  HOSTNAME     SHARDS CORES       FREE_RAM         PROVISIONAL_RAM  VERSION   STATUS\n*node:1 master 192.0.2.12 198.51.100.1      3d99db1fdf4b 1/100  6           14.72GB/19.54GB  10.91GB/16.02GB  6.2.12-37 OK\nnode:2  slave  192.0.2.13 198.51.100.2      fc7a3d332458 4/100  6           14.72GB/19.54GB  11.17GB/16.02GB  6.2.12-37 OK\nnode:3  slave  192.0.2.14                   b87cc06c830f 5/120  6           14.72GB/19.54GB  10.92GB/16.02GB  6.2.12-37 OK\n$ rladmin status shards node 2\nSHARDS:\nDB:ID      NAME             ID               NODE         ROLE       SLOTS                  USED_MEMORY            STATUS\ndb:6       tr02             redis:14         node:2       slave      0-4095                 2.99MB                 OK\ndb:6       tr02             redis:16         node:2       slave      4096-8191              3.01MB                 OK\ndb:6       tr02             redis:18         node:2       slave      8192-12287             2.93MB                 OK\ndb:6       tr02             redis:20         node:2       slave      12288-16383            3.06MB                 OK","section_id":"example"},{"id":"node-enslave-endpoints_only-ex0","language":" sh","code":"rladmin node <ID> enslave endpoints_only\n        [retry_timeout_seconds <seconds>]","section_id":"node-enslave-endpoints_only"},{"id":"example-1-ex0","language":"sh","code":"$ rladmin status endpoints\nENDPOINTS:\nDB:ID         NAME        ID                             NODE            ROLE                                       SSL       \ndb:5          tr01        endpoint:5:1                   node:1          single                                     No        \ndb:6          tr02        endpoint:6:1                   node:3          all-master-shards                          No        \n$ rladmin node 1 enslave endpoints_only\nPerforming enslave_node action on node:1: 100%\nOK\n$ rladmin status endpoints\nENDPOINTS:\nDB:ID         NAME        ID                             NODE            ROLE                                       SSL       \ndb:5          tr01        endpoint:5:1                   node:3          single                                     No        \ndb:6          tr02        endpoint:6:1                   node:3          all-master-shards                          No","section_id":"example-1"},{"id":"node-enslave-shards_only-ex0","language":" sh","code":"rladmin node <ID> enslave shards_only\n        [retry_timeout_seconds <seconds>]","section_id":"node-enslave-shards_only"},{"id":"example-2-ex0","language":"sh","code":"$ rladmin status shards node 3\nSHARDS:\nDB:ID      NAME           ID               NODE         ROLE         SLOTS                  USED_MEMORY            STATUS     \ndb:5       tr01           redis:12         node:3       master       0-16383                3.04MB                 OK         \ndb:6       tr02           redis:15         node:3       master       0-4095                 4.13MB                 OK         \ndb:6       tr02           redis:17         node:3       master       4096-8191              4.13MB                 OK         \ndb:6       tr02           redis:19         node:3       master       8192-12287             4.13MB                 OK         \ndb:6       tr02           redis:21         node:3       master       12288-16383            4.13MB                 OK         \n$ rladmin node 3 enslave shards_only\nPerforming enslave_node action on node:3: 100%\nOK\n$ rladmin status shards node 3\nSHARDS:\nDB:ID      NAME             ID               NODE         ROLE       SLOTS                  USED_MEMORY            STATUS     \ndb:5       tr01             redis:12         node:3       slave      0-16383                2.98MB                 OK         \ndb:6       tr02             redis:15         node:3       slave      0-4095                 4.23MB                 OK         \ndb:6       tr02             redis:17         node:3       slave      4096-8191              4.11MB                 OK         \ndb:6       tr02             redis:19         node:3       slave      8192-12287             4.19MB                 OK         \ndb:6       tr02             redis:21         node:3       slave      12288-16383            4.27MB                 OK","section_id":"example-2"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/rladmin/node/external-addr","title":"rladmin node external_addr","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/node/external-addr/","summary":"Configures a node's external IP addresses.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:21+01:00","page_type":"content","content_hash":"9829b2716861a8fe9479e6e129c5d448a16eee639dc024b1efce5e9a5ae2ab51","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Configures a node's external IP addresses."},{"id":"node-external_addr-add","title":"`node external_addr add`","role":"content","text":"Adds an external IP address that accepts inbound user connections for the node.\n\n[code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameter | Type/Value                     | Description                                                                                   |\n|-----------|--------------------------------|-----------------------------------------------------------------------------------------------|\n| node      | integer                        | Adds an external IP address for the specified node                                               |\n| IP address | IP address                     | External IP address of the node                                                               |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns `Updated successfully` if the IP address was added. Otherwise, it returns an error.\n\nUse [`rladmin status nodes`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status#status-nodes) to verify the external IP address was added."},{"id":"example","title":"Example","role":"example","text":"[code example]"},{"id":"node-external_addr-set","title":"`node external_addr set`","role":"content","text":"Sets one or more external IP addresses that accepts inbound user connections for the node.\n\n[code example]"},{"id":"parameters-1","title":"Parameters","role":"parameters","text":"| Parameter | Type/Value                     | Description                                                                                   |\n|-----------|--------------------------------|-----------------------------------------------------------------------------------------------|\n| node      | integer                        | Sets external IP addresses for the specified node                                             |\n| IP address | list of IP addresses           | Sets specified IP addresses as external addresses                                                 |"},{"id":"returns-1","title":"Returns","role":"returns","text":"Returns `Updated successfully` if the IP addresses were set. Otherwise, it returns an error.\n\nUse [`rladmin status nodes`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status#status-nodes) to verify the external IP address was set."},{"id":"example-1","title":"Example","role":"example","text":"[code example]"},{"id":"node-external_addr-remove","title":"`node external_addr remove`","role":"content","text":"Removes the specified external IP address from the node.\n\n[code example]"},{"id":"parameters-2","title":"Parameters","role":"parameters","text":"| Parameter | Type/Value                     | Description                                                                                   |\n|-----------|--------------------------------|-----------------------------------------------------------------------------------------------|\n| node      | integer                        | Removes an external IP address for the specified node                                            |\n| IP address | IP address                     | Removes the specified IP address of the node                                                      |"},{"id":"returns-2","title":"Returns","role":"returns","text":"Returns `Updated successfully` if the IP address was removed. Otherwise, it returns an error.\n\nUse [`rladmin status nodes`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status#status-nodes) to verify the external IP address was removed."},{"id":"example-2","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"node-external_addr-add-ex0","language":"sh","code":"rladmin node <ID> external_addr\n        add <IP address>","section_id":"node-external_addr-add"},{"id":"example-ex0","language":" sh","code":"$ rladmin node 1 external_addr add 198.51.100.1\nUpdated successfully.\n$ rladmin status nodes\nCLUSTER NODES:\nNODE:ID ROLE   ADDRESS   EXTERNAL_ADDRESS  HOSTNAME     SHARDS CORES       FREE_RAM         PROVISIONAL_RAM  VERSION   STATUS\n*node:1 master 192.0.2.2 198.51.100.1      3d99db1fdf4b 5/100  6           14.75GB/19.54GB  11.15GB/16.02GB  6.2.12-37 OK\nnode:2  slave  192.0.2.3                   fc7a3d332458 0/100  6           14.75GB/19.54GB  11.24GB/16.02GB  6.2.12-37 OK\nnode:3  slave  192.0.2.4                   b87cc06c830f 5/120  6           14.75GB/19.54GB  11.15GB/16.02GB  6.2.12-37 OK","section_id":"example"},{"id":"node-external_addr-set-ex0","language":"sh","code":"rladmin node <ID> external_addr\n        set <IP address 1> ... <IP address N>","section_id":"node-external_addr-set"},{"id":"example-1-ex0","language":" sh","code":"$ rladmin node 2 external_addr set 198.51.100.2 198.51.100.3\nUpdated successfully.\n$ rladmin status nodes\nCLUSTER NODES:\nNODE:ID ROLE   ADDRESS   EXTERNAL_ADDRESS          HOSTNAME     SHARDS CORES FREE_RAM        PROVISIONAL_RAM VERSION   STATUS\n*node:1 master 192.0.2.2 198.51.100.1              3d99db1fdf4b 5/100  6     14.75GB/19.54GB 11.15GB/16.02GB 6.2.12-37 OK\nnode:2  slave  192.0.2.3 198.51.100.2,198.51.100.3 fc7a3d332458 0/100  6     14.75GB/19.54GB 11.23GB/16.02GB 6.2.12-37 OK\nnode:3  slave  192.0.2.4                           b87cc06c830f 5/120  6     14.75GB/19.54GB 11.15GB/16.02GB 6.2.12-37 OK","section_id":"example-1"},{"id":"node-external_addr-remove-ex0","language":"sh","code":"rladmin node <ID> external_addr\n        remove <IP address>","section_id":"node-external_addr-remove"},{"id":"example-2-ex0","language":" sh","code":"$ rladmin status nodes\nCLUSTER NODES:\nNODE:ID ROLE   ADDRESS   EXTERNAL_ADDRESS          HOSTNAME     SHARDS CORES FREE_RAM        PROVISIONAL_RAM VERSION   STATUS\n*node:1 master 192.0.2.2 198.51.100.1              3d99db1fdf4b 5/100  6     14.75GB/19.54GB 11.15GB/16.02GB 6.2.12-37 OK\nnode:2  slave  192.0.2.3 198.51.100.2,198.51.100.3 fc7a3d332458 0/100  6     14.75GB/19.54GB 11.23GB/16.02GB 6.2.12-37 OK\nnode:3  slave  192.0.2.4                           b87cc06c830f 5/120  6     14.75GB/19.54GB 11.15GB/16.02GB 6.2.12-37 OK\n$ rladmin node 2 external_addr remove 198.51.100.3\nUpdated successfully.\n$ rladmin status nodes\nCLUSTER NODES:\nNODE:ID ROLE   ADDRESS   EXTERNAL_ADDRESS  HOSTNAME     SHARDS CORES       FREE_RAM         PROVISIONAL_RAM  VERSION   STATUS\n*node:1 master 192.0.2.2 198.51.100.1      3d99db1fdf4b 5/100  6           14.74GB/19.54GB  11.14GB/16.02GB  6.2.12-37 OK    \nnode:2  slave  192.0.2.3 198.51.100.2      fc7a3d332458 0/100  6           14.74GB/19.54GB  11.22GB/16.02GB  6.2.12-37 OK    \nnode:3  slave  192.0.2.4                   b87cc06c830f 5/120  6           14.74GB/19.54GB  11.14GB/16.02GB  6.2.12-37 OK","section_id":"example-2"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/rladmin/node","title":"rladmin node","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/node/","summary":"Manage nodes.","tags":["docs","operate","rs"],"last_updated":"2024-02-26T13:11:00+01:00","children":[{"id":"operate/rs/references/cli-utilities/rladmin/node/addr","summary":"Sets a node's internal IP address.","title":"rladmin node addr set","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/node/addr/"},{"id":"operate/rs/references/cli-utilities/rladmin/node/enslave","summary":"Changes a node's resources to replicas.","title":"rladmin node enslave","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/node/enslave/"},{"id":"operate/rs/references/cli-utilities/rladmin/node/external-addr","summary":"Configures a node's external IP addresses.","title":"rladmin node external_addr","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/node/external-addr/"},{"id":"operate/rs/references/cli-utilities/rladmin/node/maintenance-mode","summary":"Turns quorum-only mode on or off for a node.","title":"rladmin node maintenance_mode","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/node/maintenance-mode/"},{"id":"operate/rs/references/cli-utilities/rladmin/node/recovery-path","summary":"Sets a node's local recovery path.","title":"rladmin node recovery_path set","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/node/recovery-path/"},{"id":"operate/rs/references/cli-utilities/rladmin/node/remove","summary":"Removes a node from the cluster.","title":"rladmin node remove","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/node/remove/"},{"id":"operate/rs/references/cli-utilities/rladmin/node/snapshot","summary":"Manages snapshots of the configuration of a node's shards and endpoints.","title":"rladmin node snapshot","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/node/snapshot/"}],"page_type":"content","content_hash":"1a756a30dde91e080c4bb6334115d237985f4f0656914ad318b7dd38519cef24","sections":[{"id":"content","title":"Content","role":"content","text":"`rladmin node` commands manage nodes in the cluster.\n\n\n| Command | Description |\n| --- | --- |\n| [addr](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/node/addr/) | Sets a node's internal IP address. |\n| [enslave](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/node/enslave/) | Changes a node's resources to replicas. |\n| [external_addr](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/node/external-addr/) | Configures a node's external IP addresses. |\n| [maintenance_mode](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/node/maintenance-mode/) | Turns quorum-only mode on or off for a node. |\n| [recovery_path](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/node/recovery-path/) | Sets a node's local recovery path. |\n| [remove](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/node/remove/) | Removes a node from the cluster. |\n| [snapshot](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/node/snapshot/) | Manages snapshots of the configuration of a node's shards and endpoints. |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/rladmin/node/maintenance-mode","title":"rladmin node maintenance_mode","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/node/maintenance-mode/","summary":"Turns quorum-only mode on or off for a node.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:21+01:00","page_type":"content","content_hash":"2715cd330732f0d21d6b2f3bd0cdc4196cbbe4bc7717e7664bf22210094cb3df","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Configures [quorum-only mode](https://redis.io/docs/latest/operate/rs/clusters/maintenance-mode#activate-maintenance-mode) on a node."},{"id":"node-maintenance_mode-on","title":"`node maintenance_mode on`","role":"content","text":"Migrates shards out of the node and turns the node into a quorum node to prevent shards from returning to it.\n\n[code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameter             | Type/Value                     | Description                                                                               |\n|-----------------------|--------------------------------|-------------------------------------------------------------------------------------------|\n| node                  | integer                        | Turns the specified node into a quorum node                                              |\n| demote_node           |                                | If the node is a primary node, changes the node to replica                                |\n| evict_ha_replica | `enabled`<br />`disabled` | Migrates the HA replica shards in the node |\n| evict_active_active_replica | `enabled`<br />`disabled` | Migrates the Active-Active replica shards in the node |\n| evict_dbs | list of database names or IDs | Specify databases whose shards should be evicted from the node when entering maintenance mode.<br /><br />Examples:<br />`$ rladmin node 1 maintenance_mode on evict_dbs db:1 db:2`<br />`$ rladmin node 1 maintenance_mode on evict_dbs db_name1 db_name2` |\n| keep_slave_shards     |                                | Keeps replica shards in the node and demotes primary shards to replicas.<br /><br />Deprecated as of Redis Software 7.4.2. Use `evict_ha_replica disabled evict_active_active_replica disabled` instead. |\n| max_concurrent_actions | integer | Maximum number of concurrent actions during node maintenance |\n| overwrite_snapshot | | Overwrites the latest existing node snapshot taken when enabling maintenance mode |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns `OK` if the node was converted successfully. If the cluster does not have enough resources to migrate the shards, the process returns a warning.\n\nUse [`rladmin status nodes`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status#status-nodes) to verify the node became a quorum node."},{"id":"example","title":"Example","role":"example","text":"[code example]"},{"id":"node-maintenance_mode-off","title":"`node maintenance_mode off`","role":"content","text":"Turns maintenance mode off and returns the node to its previous state.\n\n[code example]"},{"id":"parameters-1","title":"Parameters","role":"parameters","text":"| Parameter             | Type/Value                     | Description                                                                               |\n|-----------------------|--------------------------------|-------------------------------------------------------------------------------------------|\n| node                  | integer                        | Restores the node back to the previous state                                              |\n| max_concurrent_actions | integer | Maximum number of concurrent actions during node maintenance |\n| skip_shards_restore   |                                | Does not restore shards back to the node                                                  |\n| snapshot_name         | string                         | Restores the node back to a state stored in the specified snapshot                        |"},{"id":"returns-1","title":"Returns","role":"returns","text":"Returns `OK` if the node was restored successfully.\n\nUse [`rladmin status nodes`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status#status-nodes) to verify the node was restored."},{"id":"example-1","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"node-maintenance_mode-on-ex0","language":"sh","code":"rladmin node <ID> maintenance_mode on\n        [ keep_slave_shards ]\n        [ evict_ha_replica { enabled | disabled } ]\n        [ evict_active_active_replica { enabled | disabled } ]\n        [ evict_dbs <list of database names or IDs> ]\n        [ demote_node ]\n        [ overwrite_snapshot ]\n        [ max_concurrent_actions <integer> ]","section_id":"node-maintenance_mode-on"},{"id":"example-ex0","language":"sh","code":"$ rladmin node 2 maintenance_mode on overwrite_snapshot\nFound snapshot from 2024-01-06T11:36:47Z, overwriting the snapshot\nPerforming maintenance_on action on node:2: 0%\ncreated snapshot NodeSnapshot<name=maintenance_mode_2024-01-11_20-25-37,time=None,node_uid=2>\n\nnode:2 will not accept any more shards\nPerforming maintenance_on action on node:2: 100%\nOK\n$ rladmin status nodes\nCLUSTER NODES:\nNODE:ID ROLE   ADDRESS    EXTERNAL_ADDRESS  HOSTNAME     SHARDS CORES       FREE_RAM         PROVISIONAL_RAM  VERSION   STATUS\n*node:1 master 192.0.2.12 198.51.100.1      3d99db1fdf4b 5/100  6           14.21GB/19.54GB  10.62GB/16.02GB  6.2.12-37 OK\nnode:2  slave  192.0.2.13 198.51.100.2      fc7a3d332458 0/0    6           14.21GB/19.54GB  0KB/0KB          6.2.12-37 OK\nnode:4  slave  192.0.2.14                   6d754fe12cb9 5/100  6           14.21GB/19.54GB  10.62GB/16.02GB  6.2.12-37 OK","section_id":"example"},{"id":"node-maintenance_mode-off-ex0","language":"sh","code":"rladmin node <ID> maintenance_mode off\n        [ { snapshot_name <name> | skip_shards_restore } ]\n        [ max_concurrent_actions <integer> ]","section_id":"node-maintenance_mode-off"},{"id":"example-1-ex0","language":"sh","code":"$ rladmin node 2 maintenance_mode off\nPerforming maintenance_off action on node:2: 0%\nFound snapshot: NodeSnapshot<name=maintenance_mode_2022-05-12_20-25-37,time=2022-05-12T20:25:37Z,node_uid=2>\nPerforming maintenance_off action on node:2: 0%\nmigrate redis:12 to node:2: executing\nPerforming maintenance_off action on node:2: 0%\nmigrate redis:12 to node:2: finished\nPerforming maintenance_off action on node:2: 0%\nmigrate redis:17 to node:2: executing\n\nmigrate redis:15 to node:2: executing\nPerforming maintenance_off action on node:2: 0%\nmigrate redis:17 to node:2: finished\n\nmigrate redis:15 to node:2: finished\nPerforming maintenance_off action on node:2: 0%\nfailover redis:16: executing\n\nfailover redis:14: executing\nPerforming maintenance_off action on node:2: 0%\nfailover redis:16: finished\n\nfailover redis:14: finished\nPerforming maintenance_off action on node:2: 0%\nfailover redis:18: executing\nPerforming maintenance_off action on node:2: 0%\nfailover redis:18: finished\n\nmigrate redis:21 to node:2: executing\n\nmigrate redis:19 to node:2: executing\nPerforming maintenance_off action on node:2: 0%\nmigrate redis:21 to node:2: finished\n\nmigrate redis:19 to node:2: finished\n\nfailover redis:20: executing\nPerforming maintenance_off action on node:2: 0%\nfailover redis:20: finished\nPerforming maintenance_off action on node:2: 0%\nrebind endpoint:6:1: executing\nPerforming maintenance_off action on node:2: 0%\nrebind endpoint:6:1: finished\nPerforming maintenance_off action on node:2: 100%\nOK\n$ rladmin status nodes\nCLUSTER NODES:\nNODE:ID ROLE   ADDRESS    EXTERNAL_ADDRESS  HOSTNAME      SHARDS CORES       FREE_RAM        PROVISIONAL_RAM  VERSION   STATUS\n*node:1 master 192.0.2.12 198.51.100.1      3d99db1fdf4b  5/100  6           14.2GB/19.54GB  10.61GB/16.02GB  6.2.12-37 OK    \nnode:2  slave  192.0.2.13 198.51.100.2      fc7a3d332458  5/100  6           14.2GB/19.54GB  10.61GB/16.02GB  6.2.12-37 OK    \nnode:4  slave  192.0.2.14                   6d754fe12cb9  0/100  6           14.2GB/19.54GB  10.69GB/16.02GB  6.2.12-37 OK","section_id":"example-1"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/rladmin/node/recovery-path","title":"rladmin node recovery_path set","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/node/recovery-path/","summary":"Sets a node's local recovery path.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:21+01:00","page_type":"content","content_hash":"3f6116e533e3db805d61e8715e5574f7d9c76c4dde281603b6d25701fb39db77","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Sets the node's local recovery path, which specifies the directory where [persistence files](https://redis.io/docs/latest/operate/rs/databases/configure/database-persistence) are stored. You can use these persistence files to [recover a failed database](https://redis.io/docs/latest/operate/rs/databases/recover).\n\n[code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameter | Type/Value                     | Description                                                                                   |\n|-----------|--------------------------------|-----------------------------------------------------------------------------------------------|\n| node      | integer                        | Sets the recovery path for the specified node                                            |\n| path      | filepath                       | Path to the folder where persistence files are stored                                         |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns `Updated successfully` if the recovery path was set. Otherwise, it returns an error."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"sh","code":"rladmin node <ID> recovery_path set <path>","section_id":"overview"},{"id":"example-ex0","language":"sh","code":"$ rladmin node 2 recovery_path set /var/opt/redislabs/persist/redis\nUpdated successfully.","section_id":"example"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/rladmin/node/remove","title":"rladmin node remove","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/node/remove/","summary":"Removes a node from the cluster.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:21+01:00","page_type":"content","content_hash":"c83563f6ad51ee368d8b77b9ab750a9a58b06b86c4d14155245464ec3d21e42c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Removes the specified node from the cluster.\n\n[code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameter             | Type/Value                     | Description                                                 |\n|-----------------------|--------------------------------|-------------------------------------------------------------|\n| node                  | integer                        | The node to remove from the cluster                    |\n| wait_for_persistence  | `enabled`<br />`disabled`      | Ensures persistence files are available for recovery. The cluster policy `persistent_node_removal` determines the default value. |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns `OK` if the node was removed successfully. Otherwise, it returns an error.\n\nUse [`rladmin status nodes`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status#status-nodes) to verify that the node was removed."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":"sh","code":"rladmin node <ID> remove [ wait_for_persistence { enabled | disabled } ]","section_id":"overview"},{"id":"example-ex0","language":"sh","code":"$ rladmin status nodes\nCLUSTER NODES:\nNODE:ID ROLE   ADDRESS    EXTERNAL_ADDRESS  HOSTNAME     SHARDS CORES       FREE_RAM         PROVISIONAL_RAM  VERSION   STATUS\n*node:1 master 192.0.2.12 198.51.100.1      3d99db1fdf4b 5/100  6           14.26GB/19.54GB  10.67GB/16.02GB  6.2.12-37 OK    \nnode:2  slave  192.0.2.13 198.51.100.2      fc7a3d332458 4/100  6           14.26GB/19.54GB  10.71GB/16.02GB  6.2.12-37 OK    \nnode:3  slave  192.0.2.14                   b87cc06c830f 1/120  6           14.26GB/19.54GB  10.7GB/16.02GB   6.2.12-37 OK    \n$ rladmin node 3 remove\nPerforming remove action on node:3: 100%\nOK\nCLUSTER NODES:\nNODE:ID ROLE   ADDRESS    EXTERNAL_ADDRESS  HOSTNAME     SHARDS CORES       FREE_RAM         PROVISIONAL_RAM  VERSION   STATUS\n*node:1 master 192.0.2.12 198.51.100.1      3d99db1fdf4b 5/100  6           14.34GB/19.54GB  10.74GB/16.02GB  6.2.12-37 OK    \nnode:2  slave  192.0.2.13 198.51.100.2      fc7a3d332458 5/100  6           14.34GB/19.54GB  10.74GB/16.02GB  6.2.12-37 OK","section_id":"example"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/rladmin/node/snapshot","title":"rladmin node snapshot","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/node/snapshot/","summary":"Manages snapshots of the configuration of a node's shards and endpoints.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:21+01:00","page_type":"content","content_hash":"99e82f26fd4ae063d9a54a045093641375ae30e881615211beb92141145b9f79","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Manages snapshots of the configuration of a node's shards and endpoints.\n\nYou can create node snapshots and use them to restore the node's shards and endpoints to a configuration from a previous point in time. If you restore a node from a snapshot (for example, after an event such as failover or maintenance), the node's shards have the same placement and roles as when the snapshot was created."},{"id":"node-snapshot-create","title":"`node snapshot create`","role":"content","text":"Creates a snapshot of a node's current configuration, including the placement of shards and endpoints on the node and the shards' roles.\n\n[code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameter             | Type/Value                     | Description                                                                               |\n|-----------------------|--------------------------------|-------------------------------------------------------------------------------------------|\n| node                  | integer                        | Creates a snapshot of the specified node                    |\n| name                  | string                         | Name of the created snapshot                                |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns `Done` if the snapshot was created successfully. Otherwise, returns an error."},{"id":"example","title":"Example","role":"example","text":"[code example]"},{"id":"node-snapshot-delete","title":"`node snapshot delete`","role":"content","text":"Deletes an existing snapshot of a node.\n\n[code example]\n\n> [!NOTE]\n> You cannot use this command to delete a snapshot created by maintenance mode. As of Redis Software version 7.4.2, only the latest maintenance mode snapshot is kept."},{"id":"parameters-1","title":"Parameters","role":"parameters","text":"| Parameter             | Type/Value                     | Description                                                                               |\n|-----------------------|--------------------------------|-------------------------------------------------------------------------------------------|\n| node                  | integer                        | Deletes a snapshot of the specified node                    |\n| name                  | string                         | Deletes the specified snapshot                                |"},{"id":"returns-1","title":"Returns","role":"returns","text":"Returns `Done` if the snapshot was deleted successfully. Otherwise, returns an error."},{"id":"example-1","title":"Example","role":"example","text":"[code example]"},{"id":"node-snapshot-list","title":"`node snapshot list`","role":"content","text":"Displays a list of created snapshots for the specified node.\n\n[code example]"},{"id":"parameters-2","title":"Parameters","role":"parameters","text":"| Parameter             | Type/Value                     | Description                                                                               |\n|-----------------------|--------------------------------|-------------------------------------------------------------------------------------------|\n| node                  | integer                        | Displays snapshots of the specified node                    |"},{"id":"returns-2","title":"Returns","role":"returns","text":"Returns a list of snapshots of the specified node."},{"id":"example-2","title":"Example","role":"example","text":"[code example]"},{"id":"node-snapshot-restore","title":"`node snapshot restore`","role":"content","text":"Restores a node's shards and endpoints as close to the stored snapshot as possible.\n\n[code example]"},{"id":"parameters-3","title":"Parameters","role":"parameters","text":"| Parameter             | Type/Value                     | Description                                                                               |\n|-----------------------|--------------------------------|-------------------------------------------------------------------------------------------|\n| node                  | integer                        | Restore the specified node from a snapshot.                    |\n| restore              | string                           | Name of the snapshot used to restore the node.              |"},{"id":"returns-3","title":"Returns","role":"returns","text":"Returns `Snapshot restore completed successfully` if the actions needed to restore the snapshot completed successfully. Otherwise, it returns an error."},{"id":"example-3","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"node-snapshot-create-ex0","language":"sh","code":"rladmin node <ID> snapshot create <name>","section_id":"node-snapshot-create"},{"id":"example-ex0","language":"sh","code":"$ rladmin node 1 snapshot create snap1\nCreating node snapshot 'snap1' for node:1\nDone.","section_id":"example"},{"id":"node-snapshot-delete-ex0","language":"sh","code":"rladmin node <ID> snapshot delete <name>","section_id":"node-snapshot-delete"},{"id":"example-1-ex0","language":"sh","code":"$ rladmin node 1 snapshot delete snap1\nDeleting node snapshot 'snap1' for node:1\nDone.","section_id":"example-1"},{"id":"node-snapshot-list-ex0","language":" sh","code":"rladmin node <ID> snapshot list","section_id":"node-snapshot-list"},{"id":"example-2-ex0","language":"sh","code":"$ rladmin node 2 snapshot list\nName                                               Node    Time\nsnap2                                              2       2022-05-12T19:27:51Z","section_id":"example-2"},{"id":"node-snapshot-restore-ex0","language":"sh","code":"rladmin node <ID> snapshot restore <name>","section_id":"node-snapshot-restore"},{"id":"example-3-ex0","language":"sh","code":"$ rladmin node 2 snapshot restore snap2\nReading node snapshot 'snap2' for node:2\nPlanning restore\nPlanned actions:\n* migrate redis:15 to node:2\n* failover redis:14\n* migrate redis:17 to node:2\n* failover redis:16\n* migrate redis:19 to node:2\n* failover redis:18\n* migrate redis:21 to node:2\n* failover redis:20\nProceed?[Y]es/[N]o? Y\n2022-05-12T19:43:31.486613 Scheduling 8 actions\n[2022-05-12T19:43:31.521422 Actions Status: 8 waiting ]\n* [migrate redis:21 to node:2] waiting => executing\n* [migrate redis:19 to node:2] waiting => executing\n* [migrate redis:17 to node:2] waiting => executing\n* [migrate redis:15 to node:2] waiting => executing\n[2022-05-12T19:43:32.586084 Actions Status: 4 executing | 4 waiting ]\n* [migrate redis:21 to node:2] executing => finished\n* [migrate redis:19 to node:2] executing => finished\n* [migrate redis:17 to node:2] executing => finished\n* [migrate redis:15 to node:2] executing => finished\n* [failover redis:20] waiting => executing\n* [failover redis:18] waiting => executing\n* [failover redis:16] waiting => executing\n* [failover redis:14] waiting => executing\n[2022-05-12T19:43:33.719496 Actions Status: 4 finished | 4 executing ]\n* [failover redis:20] executing => finished\n* [failover redis:18] executing => finished\n* [failover redis:16] executing => finished\n* [failover redis:14] executing => finished\nSnapshot restore completed successfully.","section_id":"example-3"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/rladmin/placement","title":"rladmin placement","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/placement/","summary":"Configures the shard placement policy for a database.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:21+01:00","page_type":"content","content_hash":"7aa8d35a450b65d0a9f546dab6d3dc584ecd0249632dee60c0d3f8de135d006f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Configures the shard placement policy for a specified database.\n\n[code example]\n\n> [!NOTE]\n> `rladmin placement db` is deprecated and will be removed in a future version. Use [`rladmin tune db`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-db) with the `shards_placement` parameter instead."},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameter | Type/Value                     | Description                                                                                   |\n|-----------|--------------------------------|-----------------------------------------------------------------------------------------------|\n| db        | db:\\<id\\><br /> name           | Configures shard placement for the specified database                                         |\n| dense     |                                | Places new shards on the same node as long as it has resources                                |\n| sparse    |                                | Places new shards on the maximum number of available nodes within the cluster                 |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns the new shard placement policy if the policy was changed successfully. Otherwise, it returns an error.\n\nUse [`rladmin status databases`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status#status-databases) to verify that the failover completed."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":" sh","code":"rladmin placement\n        db { db:<id> | <name> }\n        { dense | sparse }","section_id":"overview"},{"id":"example-ex0","language":" sh","code":"$ rladmin status databases\nDATABASES:\nDB:ID  NAME       TYPE   STATUS   SHARDS   PLACEMENT    REPLICATION    PERSISTENCE    ENDPOINT                                \ndb:5   tr01       redis  active   1        dense        enabled        aof            redis-12000.cluster.local:12000         \n$ rladmin placement db db:5 sparse\nShards placement policy is now sparse\n$ rladmin status databases\nDATABASES:\nDB:ID  NAME       TYPE   STATUS   SHARDS   PLACEMENT    REPLICATION    PERSISTENCE    ENDPOINT                                \ndb:5   tr01       redis  active   1        sparse       enabled        aof            redis-12000.cluster.local:12000","section_id":"example"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/rladmin/recover","title":"rladmin recover","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/recover/","summary":"Recovers databases in recovery mode.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:21+01:00","page_type":"content","content_hash":"df65091290dc3bff7960f9634446e0c95ddaf973bf08b24ffd8c6d5a998dbc61","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Recovers databases in recovery mode after events such as cluster failure, and restores the databases' configurations and data from stored persistence files. See [Recover a failed database](https://redis.io/docs/latest/operate/rs/databases/recover) for detailed instructions.\n\nDatabase persistence files are stored in `/var/opt/redislabs/persist/redis/` by default, but you can specify a different directory to use for database recovery with [`rladmin node <id> recovery_path set <path>`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/node/recovery-path)."},{"id":"recover-all","title":"`recover all`","role":"content","text":"Recovers all databases in recovery mode.\n\n[code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameters         | Type/Value | Description                                 |\n|--------------------|------------|---------------------------------------------|\n| only_configuration |            | Recover database configuration without data |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns `Completed successfully` if the database was recovered. Otherwise, returns an error."},{"id":"example","title":"Example","role":"example","text":"[code example]"},{"id":"recover-db","title":"`recover db`","role":"content","text":"Recovers a specific database in recovery mode.\n\n[code example]"},{"id":"parameters-1","title":"Parameters","role":"parameters","text":"| Parameters         | Type/Value           | Description                                 |\n|--------------------|----------------------|---------------------------------------------|\n| db                 | db:\\<id\\> <br />name | Database to recover                         |\n| only_configuration |                      | Recover database configuration without data |"},{"id":"returns-1","title":"Returns","role":"returns","text":"Returns `Completed successfully` if the database was recovered. Otherwise, returns an error."},{"id":"example-1","title":"Example","role":"example","text":"[code example]"},{"id":"recover-list","title":"`recover list`","role":"content","text":"Shows a list of all databases that are currently in recovery mode.\n\n[code example]"},{"id":"parameters-2","title":"Parameters","role":"parameters","text":"None"},{"id":"returns-2","title":"Returns","role":"returns","text":"Displays a list of all recoverable databases. If no databases are in recovery mode, returns `No recoverable databases found`."},{"id":"example-2","title":"Example","role":"example","text":"[code example]"},{"id":"recover-s3_import","title":"`recover s3_import`","role":"content","text":"Imports current database snapshot files from an AWS S3 bucket to a directory on the node.\n\n[code example]"},{"id":"parameters-3","title":"Parameters","role":"parameters","text":"| Parameters           | Type/Value | Description                                                      |\n|----------------------|------------|------------------------------------------------------------------|\n| s3_bucket            | string     | S3 bucket name                                                   |\n| s3_prefix            | string     | S3 object prefix                                                 |\n| s3_access_key_id     | string     | S3 access key ID                                                 |\n| s3_secret_access_key | string     | S3 secret access key                                             |\n| import_path           | filepath   | Local import path where all database snapshots will be imported  |"},{"id":"returns-3","title":"Returns","role":"returns","text":"Returns `Completed successfully` if the database files were imported. Otherwise, returns an error."},{"id":"example-3","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"recover-all-ex0","language":"sh","code":"rladmin recover all\n        [ only_configuration ]","section_id":"recover-all"},{"id":"example-ex0","language":"plaintext","code":"$ rladmin recover all\n  0% [ 0 recovered | 0 failed ] |         | Elapsed Time: 0:00:00[first-db (db:1) recovery] Initiated.[second-db (db:2) recovery] Initiated.\n 50% [ 0 recovered | 0 failed ] |###      | Elapsed Time: 0:00:04[first-db (db:1) recovery] Completed successfully\n 75% [ 1 recovered | 0 failed ] |######   | Elapsed Time: 0:00:06[second-db (db:2) recovery] Completed successfully\n100% [ 2 recovered | 0 failed ] |#########| Elapsed Time: 0:00:08","section_id":"example"},{"id":"recover-db-ex0","language":"sh","code":"rladmin recover db { db:<id> | <name> }\n        [ only_configuration ]","section_id":"recover-db"},{"id":"example-1-ex0","language":"plaintext","code":"$ rladmin recover db db:1\n  0% [ 0 recovered | 0 failed ] |      | Elapsed Time: 0:00:00[demo-db (db:1) recovery] Initiated.\n 50% [ 0 recovered | 0 failed ] |###   | Elapsed Time: 0:00:00[demo-db (db:1) recovery] Completed successfully\n100% [ 1 recovered | 0 failed ] |######| Elapsed Time: 0:00:02","section_id":"example-1"},{"id":"recover-list-ex0","language":"sh","code":"rladmin recover list","section_id":"recover-list"},{"id":"example-2-ex0","language":"sh","code":"$ rladmin recover list\nDATABASES IN RECOVERY STATE:\nDB:ID  NAME  TYPE   SHARDS  REPLICATION  PERSISTENCE  STATUS\ndb:5   tr01  redis  1       enabled      aof          missing-files\ndb:6   tr02  redis  4       enabled      snapshot     ready","section_id":"example-2"},{"id":"recover-s3_import-ex0","language":"sh","code":"rladmin recover s3_import\n                s3_bucket <bucket name>\n                [ s3_prefix <prefix> ]\n                s3_access_key_id <access key>\n                s3_secret_access_key <secret access key>\n                import_path <path>","section_id":"recover-s3_import"},{"id":"example-3-ex0","language":"sh","code":"rladmin recover s3_import s3_bucket <bucket-name> s3_prefix <s3-prefix>/ s3_access_key_id <access-key> s3_secret_access_key <secret-access-key> import_path /tmp","section_id":"example-3"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/rladmin/restart","title":"rladmin restart","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/restart/","summary":"Restarts Redis Software processes for a specific database.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"cdfe476ec976be1bf65e436373c94eb263e083933c483abb752ba2bfc2cf73b5","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Schedules a restart of the Redis Software processes on primary and replica instances of a specific database.\n\n[code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameter      | Type/Value                     | Description                                                           |\n|----------------|--------------------------------|-----------------------------------------------------------------------|\n| db             | db:\\<id\\><br /> name           | Restarts Redis Software processes for the specified database                |\n| discard_data   |                                | Allows discarding data if there is no persistence or replication      |\n| force_discard  |                                | Forcibly discards data even if there is persistence or replication    |\n| preserve_roles |                                | Performs an additional failover to maintain shard roles               |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns `Done` if the restart completed successfully. Otherwise, it returns an error."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"overview-ex0","language":" sh","code":"rladmin restart db { db:<id> | <name> }\n        [preserve_roles]\n        [discard_data]\n        [force_discard]","section_id":"overview"},{"id":"example-ex0","language":" sh","code":"$ rladmin restart db db:5 preserve_roles\nMonitoring 1db07491-35da-4bb6-9bc1-56949f4c312a\nactive - SMUpgradeBDB init\nactive - SMUpgradeBDB stop_forwarding\nactive - SMUpgradeBDB stop_active_expire\nactive - SMUpgradeBDB check_slave\noactive - SMUpgradeBDB stop_active_expire\nactive - SMUpgradeBDB second_failover\ncompleted - SMUpgradeBDB\nDone","section_id":"example"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/rladmin/status","title":"rladmin status","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status/","summary":"Displays the current cluster status and topology information.","tags":["docs","operate","rs"],"last_updated":"2026-05-14T08:58:05-05:00","page_type":"content","content_hash":"9715443a3fc527c6d86f4b5b1c69ba049453a07746d2aaeda957d3966cce2384","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Displays the current cluster status and topology information."},{"id":"status","title":"`status`","role":"content","text":"Displays the current status of all nodes, databases, database endpoints, and shards on the cluster.\n\n[code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameter | Description |\n|-----------|-------------|\n| extra \\<parameter\\> | Extra options that show more information |\n| issues_only | Filters out all items that have an `OK` status  |\n\n| Extra parameter | Description |\n|-------------------|-------------|\n| extra all | Shows all `extra` information |\n| extra backups | Shows periodic backup status |\n| extra frag | Shows fragmented memory available after the restart |\n| extra nodestats | Shows shards per node |\n| extra rack_id | Shows `rack_id` and `second_rack_id` even if the cluster is not `rack_aware` |\n| extra redis_version | Shows Redis version of all databases in the cluster |\n| extra state_machine | Shows execution of state machine information  |\n| extra traffic_manually_disabled | Conditionally shows if any database's traffic was manually stopped with the `stop_traffic` API |\n| extra watchdog | Shows watchdog status  |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns tables of the status of all nodes, databases, and database endpoints on the cluster.\n\nIf `issues_only` is specified, it only shows instances that do not have an `OK` status.\n\nIn the `CLUSTER NODES` section, `*node` indicates which node you are connected to.\n\nFor descriptions of the fields returned by `rladmin status extra all`, see the output tables for [nodes](#returns-nodes), [databases](#returns-dbs), [endpoints](#returns-endpoints), and [shards](#returns-shards)."},{"id":"example","title":"Example","role":"example","text":"[code example]"},{"id":"status-databases","title":"`status databases`","role":"content","text":"Displays the current status of all databases on the cluster.\n\n[code example]"},{"id":"parameters-1","title":"Parameters","role":"parameters","text":"| Parameter | Description |\n|-----------|-------------|\n| extra \\<parameter\\> | Extra options that show more information |\n| sort \\<column_titles\\> | Sort results by specified column titles |\n| issues_only | Filters out all items that have an `OK` status  |\n\n\n| Extra parameter | Description |\n|-------------------|-------------|\n| extra all | Shows all `extra` information |\n| extra backups | Shows periodic backup status |\n| extra frag | Shows fragmented memory available after the restart |\n| extra nodestats | Shows shards per node |\n| extra rack_id | Shows `rack_id` and `second_rack_id` even if the cluster is not `rack_aware` |\n| extra redis_version | Shows Redis version of all databases in the cluster |\n| extra state_machine | Shows execution of state machine information  |\n| extra traffic_manually_disabled | Conditionally shows if any database's traffic was manually stopped with the `stop_traffic` API |\n| extra watchdog | Shows watchdog status  |"},{"id":"returns-dbs","title":"Returns","role":"returns","text":"Returns a table of the status of all databases on the cluster.\n\nIf `sort <column_titles>` is specified, the result is sorted by the specified table columns.\n\nIf `issues_only` is specified, it only shows databases that do not have an `OK` status.\n\nThe following table describes the fields returned by `rladmin status databases extra all`:\n\n| Field | Description |\n|-------|-------------|\n| DB:ID | Database ID |\n| NAME | Database name |\n| TYPE | Database type: Redis or Memcached |\n| STATUS | Database status |\n| SHARDS | The number of primary shards in the database |\n| PLACEMENT | How the shards are spread across nodes in the cluster, densely or sparsely |\n| REPLICATION | Is replication enabled for the database |\n| PERSISTENCE | Is persistence enabled for the database |\n| ENDPOINT | Database endpoint |\n| EXEC_STATE |  The current state of the state machine |\n| EXEC_STATE_MACHINE | The name of the running state machine |\n| BACKUP_PROGRESS | The database’s backup progress |\n| MISSING_BACKUP_TIME | How long ago a backup was done |\n| REDIS_VERSION | The database’s Redis version |"},{"id":"example-1","title":"Example","role":"example","text":"[code example]"},{"id":"status-endpoints","title":"`status endpoints`","role":"content","text":"Displays the current status of all endpoints on the cluster.\n\n[code example]"},{"id":"parameters-2","title":"Parameters","role":"parameters","text":"| Parameter | Description |\n|-----------|-------------|\n| node \\<id\\> | Only show endpoints for the specified node ID |\n| db db:\\<id\\> | Only show endpoints for the specified database ID |\n| db \\<name\\> | Only show endpoints for the specified database name |\n| extra \\<parameter\\> | Extra options that show more information |\n| sort \\<column_titles\\> | Sort results by specified column titles |\n| issues_only | Filters out all items that have an `OK` status  |\n\n\n| Extra parameter | Description |\n|-------------------|-------------|\n| extra all | Shows all `extra` information |\n| extra backups | Shows periodic backup status |\n| extra frag | Shows fragmented memory available after the restart |\n| extra nodestats | Shows shards per node |\n| extra rack_id | Shows `rack_id` and `second_rack_id` even if the cluster is not `rack_aware` |\n| extra redis_version | Shows Redis version of all endpoints in the cluster |\n| extra state_machine | Shows execution of state machine information  |\n| extra watchdog | Shows watchdog status  |"},{"id":"returns-endpoints","title":"Returns","role":"returns","text":"Returns a table of the status of all endpoints on the cluster.\n\nIf `sort <column_titles>` is specified, the result is sorted by the specified table columns.\n\nIf `issues_only` is specified, it only shows endpoints that do not have an `OK` status.\n\nThe following table describes the fields returned by `rladmin status endpoints extra all`:\n\n| Field | Description |\n|-------|-------------|\n| DB:ID | Database ID |\n| NAME | Database name |\n| ID | Endpoint ID |\n| NODE | The node that hosts the endpoint |\n| ROLE | The proxy policy of the database: single, all-master-shards, or all-nodes |\n| SSL | Is SSL enabled |\n| WATCHDOG_STATUS | The shards related to the endpoint are monitored and healthy |"},{"id":"example-2","title":"Example","role":"example","text":"[code example]"},{"id":"status-modules","title":"`status modules`","role":"content","text":"Displays the current status of modules installed on the cluster and modules used by databases. This information is not included in the combined status report returned by [`rladmin status`](#status).\n\n[code example]"},{"id":"parameters-3","title":"Parameters","role":"parameters","text":"| Parameter | Description |\n|-----------|-------------|\n| db db:\\<id\\> | Provide a list of database IDs to show only modules used by the specified databases<br />(for example: `rladmin status modules db db:1 db:2`) |\n| db \\<name\\> | Provide a list of database names to show only modules used by the specified databases<br />(for example: `rladmin status modules db name1 name2`) |\n| extra all | Shows all extra information |\n| extra compatible_redis_version | Shows the compatible Redis database version for the module |\n| extra module_id | Shows module IDs |\n| extra&nbsp;min_redis_version | Shows the minimum compatible Redis database version for each module |"},{"id":"returns-1","title":"Returns","role":"returns","text":"Returns the status of modules installed on the cluster and modules used by databases."},{"id":"example-3","title":"Example","role":"example","text":"[code example]"},{"id":"status-nodes","title":"`status nodes`","role":"content","text":"Displays the current status of all nodes on the cluster.\n\n[code example]"},{"id":"parameters-4","title":"Parameters","role":"parameters","text":"| Parameter | Description |\n|-----------|-------------|\n| extra \\<parameter\\> | Extra options that show more information |\n| sort \\<column_titles\\> | Sort results by specified column titles |\n| issues_only | Filters out all items that have an `OK` status  |\n\n\n| Extra parameter | Description |\n|-------------------|-------------|\n| extra all | Shows all `extra` information |\n| extra backups | Shows periodic backup status |\n| extra frag | Shows fragmented memory available after the restart |\n| extra nodestats | Shows shards per node |\n| extra rack_id | Shows `rack_id` and `second_rack_id` even if the cluster is not `rack_aware` |\n| extra redis_version | Shows Redis version of all nodes in the cluster |\n| extra state_machine | Shows execution of state machine information  |\n| extra watchdog | Shows watchdog status  |"},{"id":"returns-nodes","title":"Returns","role":"returns","text":"Returns a table of the status of all nodes on the cluster.\n\nIf `sort <column_titles>` is specified, the result is sorted by the specified table columns.\n\nIf `issues_only` is specified, it only shows nodes that do not have an `OK` status.\n\n`*node` indicates which node you are connected to.\n\nThe following table describes the fields returned by `rladmin status nodes extra all`:\n\n| Field | Description |\n|-------|-------------|\n| NODE:ID | Node ID |\n| ROLE | Is the node a primary (`master`) or secondary (`slave`) node |\n| ADDRESS | The node’s internal IP address |\n| EXTERNAL ADDRESS | The node’s external IP address |\n| HOSTNAME | Node name |\n| MASTERS | The number of primary shards on the node |\n| SLAVES | The number of replica shards on the node |\n| OVERBOOKING_DEPTH | Memory available to create new shards, accounting for the memory reserved for existing shards to grow, even if `shards_overbooking` is enabled. A negative value indicates how much memory is overbooked rather than just showing that no memory is available for new shards. |\n| SHARDS | The number of shards on the node |\n| CORES | The number of cores on the node |\n| FREE_RAM | free_memory/total_memory<br />**free_memory**: the amount of free memory reported by the OS.<br />**total_memory**: the total physical memory available on the node. |\n| PROVISIONAL_RAM | Memory available to create new shards, displayed as available_provisional_memory/total_provisional_memory.<br />**available_provisional_memory**: memory currently available for the creation of new shards.<br />**total_provisional_memory**: memory that would be available to create new shards if the used memory on the node was 0.<br />If the available provisional memory is 0, the node cannot create new shards because the node has reached its shard limit, is in maintenance mode, or is a quorum-only node. |\n| FLASH | The amount of flash memory available on the node, similar to `FREE_RAM` |\n| AVAILABLE_FLASH | Flash memory available to create new shards, similar to `PROVISIONAL_RAM` |\n| VERSION | The cluster version installed on the node |\n| SHA | The node’s SHA hash |\n| RACK-ID | The node’s rack ID |\n| STATUS | The node’s status |"},{"id":"example-4","title":"Example","role":"example","text":"[code example]"},{"id":"status-shards","title":"`status shards`","role":"content","text":"Displays the current status of all shards on the cluster.\n\n[code example]"},{"id":"parameters-5","title":"Parameters","role":"parameters","text":"| Parameter | Description |\n|-----------|-------------|\n| node \\<id\\> | Only show shards for the specified node ID |\n| db db:\\<id\\> | Only show shards for the specified database ID |\n| db \\<name\\> | Only show shards for the specified database name |\n| extra \\<parameter\\> | Extra options that show more information |\n| sort \\<column_titles\\> | Sort results by specified column titles |\n| issues_only | Filters out all items that have an `OK` status  |\n\n\n| Extra parameter | Description |\n|-------------------|-------------|\n| extra all | Shows all `extra` information |\n| extra backups | Shows periodic backup status |\n| extra frag | Shows fragmented memory available after the restart |\n| extra shardstats | Shows shards per node |\n| extra rack_id | Shows `rack_id` and `second_rack_id` even if the cluster is not `rack_aware` |\n| extra redis_version | Shows Redis version of all shards in the cluster |\n| extra state_machine | Shows execution of state machine information  |\n| extra watchdog | Shows watchdog status  |"},{"id":"returns-shards","title":"Returns","role":"returns","text":"Returns a table of the status of all shards on the cluster.\n\nIf `sort <column_titles>` is specified, the result is sorted by the specified table columns.\n\nIf `issues_only` is specified, it only shows shards that do not have an `OK` status.\n\nThe following table describes the fields returned by `rladmin status shards extra all`:\n\n| Field | Description |\n|-------|-------------|\n| DB:ID | Database ID |\n| NAME | Database name |\n| ID | Shard ID |\n| NODE | The node on which the shard resides |\n| ROLE | The shard’s role: primary (`master`) or replica (`slave`) |\n| SLOTS | Redis keys slot range of the shard |\n| USED_MEMORY | Memory used by the shard |\n| BACKUP_PROGRESS | The shard’s backup progress |\n| RAM_FRAG | The shard’s RAM fragmentation caused by deleted data or expired keys. A large value can indicate inefficient memory allocation. |\n| FLASH_FRAG | For Auto Tiering databases, the shard’s flash fragmentation |\n| WATCHDOG_STATUS | The shard is being monitored by the node watchdog and the shard is healthy |\n| STATUS | The shard’s status |"},{"id":"example-5","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"status-ex0","language":" sh","code":"rladmin status\n        [ extra <parameter> ]\n        [ issues_only]","section_id":"status"},{"id":"example-ex0","language":" sh","code":"$ rladmin status extra all\nCLUSTER:\nOK. Cluster master: 1 (198.51.100.2)\nCluster health: OK, [1, 0.13333333333333333, 0.03333333333333333]\nfailures/minute - avg1 1.00, avg15 0.13, avg60 0.03.\n\nCLUSTER NODES:\nNODE:ID ROLE   ADDRESS      EXTERNAL_ADDRESS HOSTNAME     MASTERS SLAVES OVERBOOKING_DEPTH SHARDS CORES FREE_RAM        PROVISIONAL_RAM VERSION   SHA    RACK-ID STATUS\nnode:1  master 198.51.100.2                  3d99db1fdf4b 4       0      10.91GB           4/100  6     14.91GB/19.54GB 10.91GB/16.02GB 6.2.12-37 5c2106 -       OK    \nnode:2  slave  198.51.100.3                  fc7a3d332458 0       0      11.4GB            0/100  6     14.91GB/19.54GB 11.4GB/16.02GB  6.2.12-37 5c2106 -       OK    \n*node:3 slave  198.51.100.4                  b87cc06c830f 0       0      11.4GB            0/100  6     14.91GB/19.54GB 11.4GB/16.02GB  6.2.12-37 5c2106 -       OK    \n\nDATABASES:\nDB:ID NAME      TYPE  STATUS SHARDS PLACEMENT REPLICATION PERSISTENCE ENDPOINT                        EXEC_STATE EXEC_STATE_MACHINE BACKUP_PROGRESS MISSING_BACKUP_TIME REDIS_VERSION\ndb:3  database3 redis active 4      dense     disabled    disabled    redis-11103.cluster.local:11103 N/A        N/A                N/A             N/A                 6.0.16       \n\nENDPOINTS:\nDB:ID     NAME             ID                    NODE       ROLE       SSL        WATCHDOG_STATUS          \ndb:3      database3        endpoint:3:1          node:1     single     No         OK                       \n\nSHARDS:\nDB:ID NAME      ID      NODE   ROLE   SLOTS       USED_MEMORY BACKUP_PROGRESS RAM_FRAG WATCHDOG_STATUS STATUS\ndb:3  database3 redis:4 node:1 master 0-4095      2.08MB      N/A             4.73MB   OK              OK    \ndb:3  database3 redis:5 node:1 master 4096-8191   2.08MB      N/A             4.62MB   OK              OK    \ndb:3  database3 redis:6 node:1 master 8192-12287  2.08MB      N/A             4.59MB   OK              OK    \ndb:3  database3 redis:7 node:1 master 12288-16383 2.08MB      N/A             4.66MB   OK              OK","section_id":"example"},{"id":"status-databases-ex0","language":" sh","code":"rladmin status databases\n        [ extra <parameters> ]\n        [ sort <column_titles> ]\n        [ issues_only ]","section_id":"status-databases"},{"id":"example-1-ex0","language":" sh","code":"$ rladmin status databases sort REPLICATION PERSISTENCE\nDB:ID NAME      TYPE  STATUS SHARDS PLACEMENT REPLICATION PERSISTENCE ENDPOINT                                       \ndb:1  database1 redis active 1      dense     disabled    disabled    redis-10269.testdbd11169.localhost:10269\ndb:2  database2 redis active 1      dense     disabled    snapshot    redis-13897.testdbd11169.localhost:13897\ndb:3  database3 redis active 1      dense     enabled     snapshot    redis-19416.testdbd13186.localhost:19416","section_id":"example-1"},{"id":"status-endpoints-ex0","language":" sh","code":"rladmin status endpoints\n        [ node <id> ]\n        [ db { db:<id> | <name> } ]\n        [ extra <parameters> ]\n        [ sort <column_titles> ]\n        [ issues_only ]","section_id":"status-endpoints"},{"id":"example-2-ex0","language":" sh","code":"$ rladmin status endpoints\nDB:ID     NAME             ID                    NODE        ROLE        SSL    \ndb:1      database1        endpoint:1:1          node:1      single      No     \ndb:2      database2        endpoint:2:1          node:2      single      No     \ndb:3      database3        endpoint:3:1          node:3      single      No","section_id":"example-2"},{"id":"status-modules-ex0","language":" sh","code":"rladmin status modules\n        [ db { db:<id1> | <name1> } ... { db:<idN> | <nameN> } ]\n        [ extra { all | compatible_redis_version | min_redis_version | module_id } ]","section_id":"status-modules"},{"id":"example-3-ex0","language":"sh","code":"$ rladmin status modules extra all                \nCLUSTER MODULES:\nMODULE           VERSION   MIN_REDIS_VERSION  ID                                \nRedisBloom       2.4.5     6.0                1b895a180592cbcae5bd3bff6af24be2  \nRedisBloom       2.6.8     7.1                95264e7c9ac9540268c115c86a94659b  \nRediSearch 2     2.6.12    6.0                2c000539f65272f7a2712ed3662c2b6b  \nRediSearch 2     2.8.9     7.1                dd9a75710db528afa691767e9310ac6f  \nRedisGears       2.0.15    7.1                18c83d024b8ee22e7caf030862026ca6  \nRedisGraph       2.10.12   6.0                5a1f2fdedb8f6ca18f81371ea8d28f68  \nRedisJSON        2.4.7     6.0                28308b101a0203c21fa460e7eeb9344a  \nRedisJSON        2.6.8     7.1                b631b6a863edde1b53b2f7a27a49c004  \nRedisTimeSeries  1.8.11    6.0                8fe09b00f56afe5dba160d234a6606af  \nRedisTimeSeries  1.10.9    7.1                98a492a017ea6669a162fd3503bf31f3  \n\nDATABASE MODULES:\nDB:ID      NAME             MODULE            VERSION  ARGS              STATUS \ndb:1       search-json-db   RediSearch 2      2.8.9    PARTITIONS AUTO   OK     \ndb:1       search-json-db   RedisJSON         2.6.8                      OK     \ndb:2       timeseries-db    RedisTimeSeries   1.10.9                     OK","section_id":"example-3"},{"id":"status-nodes-ex0","language":" sh","code":"rladmin status nodes\n        [ extra <parameters> ]\n        [ sort <column_titles> ]\n        [ issues_only ]","section_id":"status-nodes"},{"id":"example-4-ex0","language":" sh","code":"$ rladmin status nodes sort PROVISIONAL_RAM HOSTNAME\nCLUSTER NODES:\nNODE:ID     ROLE       ADDRESS          EXTERNAL_ADDRESS          HOSTNAME            SHARDS     CORES          FREE_RAM                 PROVISIONAL_RAM          VERSION        STATUS   \nnode:1      master     198.51.100.2                                 3d99db1fdf4b        4/100      6              14.74GB/19.54GB          10.73GB/16.02GB          6.2.12-37      OK       \n*node:3     slave      198.51.100.4                                 b87cc06c830f        0/100      6              14.74GB/19.54GB          11.22GB/16.02GB          6.2.12-37      OK       \nnode:2      slave      198.51.100.3                                 fc7a3d332458        0/100      6              14.74GB/19.54GB          11.22GB/16.02GB          6.2.12-37      OK","section_id":"example-4"},{"id":"status-shards-ex0","language":" sh","code":"rladmin status shards\n        [ node <id> ]\n        [ db {db:<id> | <name>} ]\n        [ extra <parameters> ]\n        [ sort <column_titles> ]\n        [ issues_only ]","section_id":"status-shards"},{"id":"example-5-ex0","language":" sh","code":"$ rladmin status shards sort USED_MEMORY ID\nSHARDS:\nDB:ID               NAME                       ID                   NODE               ROLE               SLOTS                           USED_MEMORY                     STATUS          \ndb:3                database3                  redis:6              node:1             master             8192-12287                      2.04MB                          OK              \ndb:3                database3                  redis:4              node:1             master             0-4095                          2.08MB                          OK              \ndb:3                database3                  redis:5              node:1             master             4096-8191                       2.08MB                          OK              \ndb:3                database3                  redis:7              node:1             master             12288-16383                     2.08MB                          OK","section_id":"example-5"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/rladmin/suffix","title":"rladmin suffix","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/suffix/","summary":"Manages the DNS suffixes in the cluster.","tags":["docs","operate","rs"],"last_updated":"2023-12-14T15:27:49+01:00","page_type":"content","content_hash":"b01b6c30bc1c7c89678a32971809ab386902091b7261eeda18ad2c9be5e8e084","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Manages the DNS suffixes in the cluster."},{"id":"suffix-add","title":"`suffix add`","role":"content","text":"Adds a DNS suffix to the cluster.\n\n[code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameter | Type/Value       | Description                                                                                   |\n|-----------|------------------|-----------------------------------------------------------------------------------------------|\n| name      | string           | DNS suffix to add to the cluster                                                             |\n| default   |                  | Sets the given suffix as the default. If a default already exists, this overwrites it.  |\n| internal  |                  | Forces the given suffix to use private IPs                                                    |\n| mdns      |                  | Activates multicast DNS support for the given suffix                                           |\n| slaves    | list of IPv4 addresses | The given suffix will notify the frontend DNS servers when a change in the frontend DNS has occurred |\n| use_aaaa_ns |                | Activates IPv6 address support |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns `Added suffixes successfully` if the suffix was added. Otherwise, it returns an error."},{"id":"example","title":"Example","role":"example","text":"[code example]"},{"id":"suffix-delete","title":"`suffix delete`","role":"content","text":"Deletes an existing DNS suffix from the cluster.\n\n[code example]"},{"id":"parameters-1","title":"Parameters","role":"parameters","text":"| Parameter | Type/Value       | Description                                                                                   |\n|-----------|------------------|-----------------------------------------------------------------------------------------------|\n| name      | string           | DNS suffix to delete from the cluster                                                         |"},{"id":"returns-1","title":"Returns","role":"returns","text":"Returns `Suffix deleted successfully` if the suffix was deleted. Otherwise, it returns an error."},{"id":"example-1","title":"Example","role":"example","text":"[code example]"},{"id":"suffix-list","title":"`suffix list`","role":"content","text":"Lists the DNS suffixes in the cluster.\n\n[code example]"},{"id":"parameters-2","title":"Parameters","role":"parameters","text":"None"},{"id":"returns-2","title":"Returns","role":"returns","text":"Returns a list of the DNS suffixes."},{"id":"example-2","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"suffix-add-ex0","language":" sh","code":"rladmin suffix add name <name>\n        [default]\n        [internal]\n        [mdns]\n        [use_aaaa_ns]\n        [slaves <ip>..]","section_id":"suffix-add"},{"id":"example-ex0","language":" sh","code":"$ rladmin suffix add name new.rediscluster.local\nAdded suffixes successfully","section_id":"example"},{"id":"suffix-delete-ex0","language":" sh","code":"rladmin suffix delete name <name>","section_id":"suffix-delete"},{"id":"example-1-ex0","language":" sh","code":"$ rladmin suffix delete name new.rediscluster.local\nSuffix deleted successfully","section_id":"example-1"},{"id":"suffix-list-ex0","language":"sh","code":"rladmin suffix list","section_id":"suffix-list"},{"id":"example-2-ex0","language":" sh","code":"$ rladmin suffix list\nList of all suffixes:\ncluster.local\nnew.rediscluster.local","section_id":"example-2"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/rladmin/tune","title":"rladmin tune","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune/","summary":"Configures parameters for databases, proxies, nodes, and clusters.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:21+01:00","page_type":"content","content_hash":"38f4a51388968aa1e2e92ac4db46df501e798a94b224e2dc377590dc12ca7746","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Configures parameters for databases, proxies, nodes, and clusters."},{"id":"tune-cluster","title":"`tune cluster`","role":"content","text":"Configures cluster parameters.\n\n[code example]"},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameters                             | Type/Value                        | Description                                                                                                                  |\n|----------------------------------------|-----------------------------------|------------------------------------------------------------------------------------------------------------------------------|\n| acl_pubsub_default | `resetchannels`<br /> `allchannels` | Default pub/sub ACL rule for all databases in the cluster:<br />•`resetchannels` blocks access to all channels (restrictive)<br />•`allchannels` allows access to all channels (permissive) |\n| automatic_node_offload | `enabled`<br />`disabled` | Define whether automatic node offload migration will take place |\n| auto_recovery | `enabled`<br />`disabled` | Defines whether to use automatic recovery after shard failure |\n| control_plane_basic_authentication     | `enabled`<br />`disabled`       | Activates or deactivates [basic and digest authentication](https://redis.io/docs/latest/operate/rs/security/access-control/disable-basic-authentication) for the cluster management REST API. Enabled by default. |\n| data_internode_encryption              | `enabled`<br />`disabled`       | Activates or deactivates [internode encryption](https://redis.io/docs/latest/operate/rs/security/encryption/internode-encryption) for new databases    |\n| db_conns_auditing                      | `enabled`<br /> `disabled`      | Activates or deactivates [connection auditing](https://redis.io/docs/latest/operate/rs/security/audit-events) by default for new databases of a cluster                                                                  |\n| default_concurrent_restore_actions     | integer<br />`all`              | Default number of concurrent actions when restoring a node from a snapshot (positive integer or \"all\")                         |\n| default_non_sharded_proxy_policy | `single`<br /><br />`all-master-shards`<br /><br />`all-nodes` | Default [proxy policy](https://redis.io/docs/latest/operate/rs/databases/configure/proxy-policy) for newly created non-sharded databases' endpoints |\n| default_oss_sharding | `enabled`<br />`disabled` | Default hashing policy to use for new databases. Set to `disabled` by default. This field is for future use only and should not be changed. |\n| default_recovery_wait_time | integer | The default time for new databases to wait for the persistence file to be available during automatic recovery. After the wait time expires, auto recovery completes with potential data loss. The default `-1` means to wait forever. |\n| default_redis_version                  | version number                    | The default Redis database compatibility version used to create new databases.<br/><br/>  The value parameter should be a version number in the form of \"x.y\" where _x_ represents the major version number and _y_ represents the minor version number.  The final value corresponds to the desired version of Redis.<br/><br/>You cannot set _default_redis_version_ to a value higher than that supported by the current _redis_upgrade_policy_ value. |\n| default_sharded_proxy_policy | `single`<br /><br />`all-master-shards`<br /><br />`all-nodes` | Default [proxy policy](https://redis.io/docs/latest/operate/rs/databases/configure/proxy-policy) for newly created sharded databases' endpoints |\n| default_shards_placement | `dense`<br />`sparse` | New databases place shards according to the default [shard placement policy](https://redis.io/docs/latest/operate/rs/databases/memory-performance/shard-placement-policy) |\n| default_tracking_table_max_keys_policy | integer (default: 1000000) | Defines the default value of the client-side caching invalidation table size for new databases. 0 makes the cache unlimited. |\n| expose_hostnames_for_all_suffixes      | `enabled`<br />`disabled`       | Exposes hostnames for all DNS suffixes                                                                                       |\n| failure_detection_sensitivity | `high`<br />`low` | Predefined thresholds and timeouts for failure detection (previously known as `watchdog_profile`)<br />• `high` (previously `local-network`) – high failure detection sensitivity, lower thresholds, faster failure detection and failover<br />• `low` (previously `cloud`) – low failure detection sensitivity, higher tolerance for latency variance (also called network jitter) |\n| filter_node_wd_health_metrics | `enabled`<br />`disabled` | When enabled, `node_wd` health metrics (<span class=\"break-all\">`shard_ping_failures`</span>, <span class=\"break-all\">`endpoint_ping_failures`</span>, <span class=\"break-all\">`dmc_ping_failures`</span> and their duration counterparts) are not emitted to `statsd` |\n| login_lockout_counter_reset_after      | time in seconds                   | Time after failed login attempt before the counter resets to 0                                                                   |\n| login_lockout_duration                 | time in seconds                   | Time a locked account remains locked ( \"0\" means only an admin can unlock the account)                                   |\n| login_lockout_threshold                | integer                           | Number of failed sign-in attempts to trigger locking a user account (\"0\" means never lock the account)                   |\n| max_saved_events_per_type              | integer                           | Maximum number of events each type saved in CCS per object type                                                              |\n| max_simultaneous_backups               | integer (default: 4)                      | Number of database backups allowed to run at the same time. Combines with `max_redis_forks` (set by [`tune node`](#tune-node)) to determine the number of shard backups allowed to run simultaneously.                                                                                  |\n| metrics_exporter_expose_bdb_name       | `enabled`<br />`disabled`       | If enabled, adds a label with the database name to relevant metrics                                                          |\n| parallel_shards_upgrade                | integer<br />`all`              | Number of shards upgraded in parallel during DB upgrade (positive integer or \"all\")                                          |\n| redis_migrate_node_threshold           | size in MB                        | Memory (in MBs by default or can be specified) needed to migrate a database between nodes                                   |\n| redis_migrate_node_threshold_percent   | percentage                | Memory (in percentage) needed to migrate a database between nodes                                                            |\n| redis_provision_node_threshold         | size in MB                        | Memory (in MBs by default or can be specified) needed to provision a new database                                           |\n| redis_provision_node_threshold_percent | percentage                | Memory (in percentage) needed to provision a new database                                                                    |\n| redis_upgrade_policy                   | `latest`<br />`major`           | When you upgrade or create a new Redis database, this policy determines which version of Redis database compatibility is used.<br /><br />Supported values are:<ul><li><p>`latest`, which applies the most recent Redis compatibility update \\(_effective default prior to v6.2.4_)<p></li><li>`major`, which applies the most recent major release compatibility update (_default as of v6.2.4_).</li></ul>                                                                                                                 |\n| repl_diskless                          | `enabled`<br />`disabled`       | Activates or deactivates diskless replication (can be overridden per database)                                              |\n| resp3_default | `enabled` <br /> `disabled` | Determines the default value of the `resp3` option upon upgrading a database to version 7.2 (defaults to `enabled`) |\n| show_internals                         | `enabled`<br />`disabled`       | Controls the visibility of internal databases that are only used for the cluster's management                                |\n| slave_ha                               | `enabled` <br /> `disabled`   | Activates or deactivates [replica high availability](https://redis.io/docs/latest/operate/rs/databases/configure/replica-ha) in the cluster<br />(enabled by default; use [`rladmin tune db`](#tune-db) to change `slave_ha` for a specific database)<br /><br />Deprecated as of Redis Software v7.2.4. |\n| slave_ha_bdb_cooldown_period           | time in seconds (default: 7200) | Time (in seconds) a database must wait after its shards are relocated by [replica high availability](https://redis.io/docs/latest/operate/rs/databases/configure/replica-ha) before it can go through another shard migration if another node fails (default is 2 hours) |\n| slave_ha_cooldown_period               | time in seconds (default: 3600) | Time (in seconds) [replica high availability](https://redis.io/docs/latest/operate/rs/databases/configure/replica-ha) must wait after relocating shards due to node failure before performing another shard migration for any database in the cluster (default is 1 hour) |\n| slave_ha_grace_period                  | time in seconds (default: 600) | Time (in seconds) between when a node fails and when [replica high availability](https://redis.io/docs/latest/operate/rs/databases/configure/replica-ha) starts relocating shards to another node      |\n| watchdog_profile                       | `cloud` <br /> `local-network` | Watchdog profiles with preconfigured thresholds and timeouts (deprecated as of Redis Software v6.4.2-69; use <span class=\"break-all\">`failure_detection_sensitivity`</span> instead)<br />• `cloud` is suitable for common cloud environments and has a higher tolerance for latency variance (also called network jitter).<br />• `local-network` is suitable for dedicated LANs and has better failure detection and failover times. |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns `Finished successfully` if the cluster configuration was changed. Otherwise, it returns an error.\n\nUse [`rladmin info cluster`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/info#info-cluster) to verify the cluster configuration was changed."},{"id":"example","title":"Example","role":"example","text":"[code example]"},{"id":"tune-db","title":"`tune db`","role":"content","text":"Configures database parameters.\n\n[code example]"},{"id":"parameters-1","title":"Parameters","role":"parameters","text":"| Parameter                            | Type/Value                       | Description                                                                                                                           |\n|--------------------------------------|----------------------------------|---------------------------------------------------------------------------------------------------------------------------------------|\n| db:id                                | integer                          | ID of the specified database                                                                                                          |\n| name                                 | string                           | Name of the specified database                                                                                                        |\n| client_buffer                        | value in MB hard:soft:time       | Redis client output buffer limits                                                                                                     |\n| conns                                | integer                          | Size of internal connection pool, specified per-thread or per-shard depending on conns_type                                           |\n| conns_type                           | `per-thread`<br /> `per-shard`   | Specifies connection pool size as either per-thread or per-shard                                                                      |\n| continue_on_error                    |                                  | Flag that skips tuning shards that can't be reached                                                                                   |\n| crdt_repl_backlog                    | value in MB<br /> `auto`         | Size of the Active-Active replication buffer                                                                                          |\n| crdt_xadd_id_uniqueness_mode         | `liberal`<br /> `semi-strict`<br /> `strict` | XADD's behavior in an Active-Active database, defined as liberal, semi-strict, or strict (see descriptions below)         |\n| data_internode_encryption            | `enabled`<br /> `disabled`       | Activates or deactivates [internode encryption](https://redis.io/docs/latest/operate/rs/security/encryption/internode-encryption) for the database               |\n| db_conns_auditing                    | `enabled`<br /> `disabled`       | Activates or deactivates database [connection auditing](https://redis.io/docs/latest/operate/rs/security/audit-events) for a database                                                                 |\n| gradual_src_mode                     | `enabled`<br /> `disabled`       | Activates or deactivates gradual sync of sources                                                                                      |\n| gradual_sync_max_shards_per_source   | integer                          | Number of shards per sync source that can be replicated in parallel (positive integer)                                                |\n| gradual_sync_mode                    | `enabled`<br /> `disabled`<br /> `auto` | Activates, deactivates, or automatically determines gradual sync of source shards                                              |\n| master_persistence                   | `enabled`<br /> `disabled`       | If enabled, persists the primary shard in addition to replica shards in a replicated and persistent database. |\n| max_aof_file_size                    | size in MB                       | Maximum size (in MB, if not specified) of [AoF](https://redis.io/docs/latest/glossary#letter-a) file (minimum value is 10 GB)              |\n| max_aof_load_time | time in seconds | Time limit in seconds to load a shard from an append-only file (AOF). If exceeded, an AOF rewrite is initiated to decrease future load time.<br />Minimum: 2700 seconds (45 minutes) <br />Default: 3600 seconds (1 hour) |\n| max_client_pipeline                  | integer                          | Maximum commands in the proxy's pipeline per client connection (max value is 2047, default value is 200)                              |\n| max_connections                      | integer                          | Maximum client connections to the database's endpoint (default value is 0, which is unlimited)                                            |\n| max_shard_pipeline                   | integer                          | Maximum commands in the proxy's pipeline per shard connection (default value is 200)                                                  |\n| maxclients                           | integer                          | Controls the maximum client connections between the proxy and shards (default value is 10000)                                         |\n| metrics_export_all                   | `enabled`<br /> `disabled`       | Activates the exporter to expose all shard metrics                                                                                    |\n| mkms                                 | `enabled`<br /> `disabled`       | Activates multi-key multi-slot commands                                                                                               |\n| module_config_params | string | Configures module arguments at runtime. Enclose `module_config_params` within quotation marks. |\n| module_name | `search`<br />`ReJSON`<br />`graph`<br />`timeseries`<br />`bf`<br />`rg` | The module to configure with `module_config_params` |\n| mtls_allow_outdated_certs             | `enabled`<br /> `disabled`       | If enabled, allows outdated certificates in mTLS connections                                   |\n| mtls_allow_weak_hashing              | `enabled`<br /> `disabled`       | Activates weak hashing (less than 2048 bits) in mTLS connections                       |\n| oss_cluster                          | `enabled`<br /> `disabled`       | Activates OSS cluster API                                                                                                             |\n| oss_cluster_api_preferred_ip_type    | `internal`<br /> `external`      | IP type for the endpoint and database in the OSS cluster API (default is internal)                                     |\n| repl_backlog                         | size in MB<br /> `auto`          | Size of the replication buffer                                                                                        |\n| repl_diskless                        | `enabled`<br /> `disabled`<br /> `default`   | Activates or deactivates diskless replication (defaults to the cluster setting)                                          |\n| repl_timeout                         | time in seconds                  | Replication timeout (in seconds)                                                                                                      |\n| resp3 | `enabled`<br /> `disabled` | Enables or deactivates RESP3 support (defaults to `enabled`) |\n| schedpolicy                          | `cmp`<br /> `mru`<br /> `spread`<br /> `mnp` | Controls how server-side connections are used when forwarding traffic to shards                                           |\n| shards_placement | `dense`<br /> `sparse` | Configures the [shard placement policy](https://redis.io/docs/latest/operate/rs/databases/memory-performance/shard-placement-policy) for the database |\n| skip_import_analyze                  | `enabled`<br /> `disabled`       | Skips the analyzing step when importing a database                                                                                    |\n| slave_buffer                         | `auto`<br />value in MB<br /> hard:soft:time | Redis replica output buffer limits<br />• `auto`: dynamically adjusts the buffer limit based on the shard’s current used memory<br />• value in MB: sets the buffer limit in MB<br />• hard:soft:time: sets the hard limit (maximum buffer size in MB), soft limit in MB, and the time in seconds that the soft limit can be exceeded |\n| slave_ha                             | `enabled`<br /> `disabled`       | Activates or deactivates replica high availability (defaults to the cluster setting)                                                      |\n| slave_ha_priority                    | integer                          | Priority of the database in the replica high-availability mechanism                                                                           |\n| syncer_mode                          | `distributed`<br /> `centralized`| Configures syncer to run in distributed or centralized mode. For distributed syncer, the DMC policy must be all-nodes or all-master-nodes |\n| syncer_monitoring                    | `enabled`<br /> `disabled`       | Activates syncer monitoring                                                                                                           |\n| tracking_table_max_keys | integer | The client-side caching invalidation table size. 0 makes the cache unlimited. |\n\n| XADD behavior mode | Description |\n| - | - |\n| liberal | XADD succeeds with any valid ID (not recommended, allows duplicate IDs) |\n| semi-strict | Allows a full ID. Partial IDs are completed with the unique database instance ID (not recommended, allows duplicate IDs). |\n| strict | XADD fails if a full ID is given. Partial IDs are completed using the unique database instance ID. |"},{"id":"returns-1","title":"Returns","role":"returns","text":"Returns `Finished successfully` if the database configuration was changed. Otherwise, it returns an error.\n\nUse [`rladmin info db`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/info#info-db) to verify the database configuration was changed."},{"id":"example-1","title":"Example","role":"example","text":"[code example]"},{"id":"tune-node","title":"`tune node`","role":"content","text":"Configures node parameters.\n\n[code example]"},{"id":"parameters-2","title":"Parameters","role":"parameters","text":"| Parameter            | Type/Value | Description                                                                                                                      |\n|----------------------|------------|----------------------------------------------------------------------------------------------------------------------------------|\n| id                   | integer    | ID of the specified node                                                                                                         |\n| all                  |            | Configures settings for all nodes                                                                                                |\n| max_listeners        | integer    | Maximum number of endpoints that may be bound to the node                                                                        |\n| max_redis_forks      | integer    | Maximum number of background processes forked from shards that may exist on the node at any given time                           |\n| max_redis_servers    | integer    | Maximum number of shards allowed to reside on the node                                                                           |\n| max_slave_full_syncs | integer    | Maximum number of simultaneous replica full-syncs that may be running at any given time (0: Unlimited, -1: Use cluster settings) |\n| quorum_only          | `enabled`<br /> `disabled` | If activated, configures the node as a [quorum-only node](https://redis.io/docs/latest/glossary#letter-p)                         |"},{"id":"returns-2","title":"Returns","role":"returns","text":"Returns `Finished successfully` if the node configuration was changed. Otherwise, it returns an error.\n\nUse [`rladmin info node`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/info#info-node) to verify the node configuration was changed."},{"id":"example-2","title":"Example","role":"example","text":"[code example]"},{"id":"tune-proxy","title":"`tune proxy`","role":"content","text":"Configures proxy parameters.\n\n[code example]"},{"id":"parameters-3","title":"Parameters","role":"parameters","text":"| Parameter       | Type/Value                 | Description                                                                         |\n|-----------------|----------------------------|-------------------------------------------------------------------------------------|\n| id              | integer                    | ID of the specified proxy                                                           |\n| all             |                            | Configures settings for all proxies                                                 |\n| client_eviction | boolean (default: true) | If `true`, enables client eviction based on `maxmemory_clients`. |\n| incoming_connections_capacity | integer (default: 0) | The maximum number of concurrent incoming connections, also known as the maximum burst size. The default is 0, which means no limit. |\n| incoming_connections_min_capacity | integer (default: 10) | The minimum number of free slots required before accepting new connections after an overflow. |\n| incoming_connections_rate_limit | integer (default: 0) | The maximum number of incoming connections per second. The default is 0, which means no limit. |\n| max_threads     | integer, (range:&nbsp;1-255) | Maximum number of threads allowed                                                   |\n| mode            | `static`<br /> `dynamic` | Determines if the proxy automatically adjusts the number of threads based on load size  |\n| scale_duration  | time in seconds, (range:&nbsp;10-300) | Time of scale_threshold CPU utilization before the automatic proxy automatically scales |\n| scale_threshold | percentage, (range:&nbsp;50-99) | CPU utilization threshold that triggers spawning new threads                        |\n| threads         | integer, (range:&nbsp;1-255) | Initial number of threads created at startup                                        |"},{"id":"returns-3","title":"Returns","role":"returns","text":"Returns `OK` if the proxy configuration was changed. Otherwise, it returns an error.\n\nUse [`rladmin info proxy`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/info#info-proxy) to verify the proxy configuration was changed."},{"id":"example-3","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"tune-cluster-ex0","language":" sh","code":"rladmin tune cluster\n        [ repl_diskless { enabled | disabled } ]\n        [ redis_provision_node_threshold <size> ]\n        [ redis_migrate_node_threshold <size> ]\n        [ redis_provision_node_threshold_percent <percent> ]\n        [ redis_migrate_node_threshold_percent <percent> ]\n        [ max_simultaneous_backups <number> ]\n        [ failure_detection_sensitivity { high | low } ]\n        [ watchdog_profile { cloud | local-network } ]\n        [ slave_ha { enabled | disabled } ]\n        [ slave_ha_grace_period <seconds> ]\n        [ slave_ha_cooldown_period <seconds> ]\n        [ slave_ha_bdb_cooldown_period <seconds> ]\n        [ max_saved_events_per_type <value> ]\n        [ metrics_exporter_expose_bdb_name { enabled | disabled } ]\n        [ parallel_shards_upgrade <value> ]\n        [ default_concurrent_restore_actions <value> ]\n        [ show_internals { enabled | disabled } ]\n        [ expose_hostnames_for_all_suffixes { enabled | disabled } ]\n        [ redis_upgrade_policy { latest | major } ]\n        [ default_redis_version <value> ]\n        [ default_non_sharded_proxy_policy { single | all-master-shards | all-nodes } ]\n        [ default_sharded_proxy_policy { single | all-master-shards | all-nodes } ]\n        [ default_shards_placement { dense | sparse } ]\n        [ data_internode_encryption { enabled | disabled } ]\n        [ db_conns_auditing { enabled | disabled } ]\n        [ acl_pubsub_default { resetchannels | allchannels } ]\n        [ resp3_default { enabled | disabled } ]\n        [ automatic_node_offload { enabled | disabled } ]\n        [ auto_recovery { enabled | disabled } ]\n        [ default_recovery_wait_time <value> ]\n        [ default_tracking_table_max_keys_policy <value> ]\n        [ default_oss_sharding { enabled | disabled } ]\n        [ filter_node_wd_health_metrics { enabled | disabled } ]\n        [ control_plane_basic_authentication { enabled | disabled } ]\n ]","section_id":"tune-cluster"},{"id":"example-ex0","language":" sh","code":"$ rladmin tune cluster slave_ha enabled\nFinished successfully\n$ rladmin info cluster | grep slave_ha\n    slave_ha: enabled","section_id":"example"},{"id":"tune-db-ex0","language":" sh","code":"rladmin tune db { db:<id> | <name> }\n        [ slave_buffer <auto | valueMB | hard:soft:time> ]\n        [ client_buffer <value> ]\n        [ repl_backlog <valueMB | auto> ]\n        [ crdt_repl_backlog <valueMB | auto> ]\n        [ repl_timeout <seconds> ]\n        [ repl_diskless { enabled | disabled | default } ]\n        [ master_persistence { enabled | disabled } ]\n        [ maxclients <value> ]\n        [ schedpolicy { cmp | mru | spread | mnp } ]\n        [ max_shard_pipeline <value> ]\n        [ conns <value> ]\n        [ conns_type <value> ]\n        [ max_client_pipeline <value> ]\n        [ max_connections <value> ]\n        [ max_aof_file_size <size> ]\n        [ max_aof_load_time <seconds> ]\n        [ oss_cluster { enabled | disabled } ]\n        [ oss_cluster_api_preferred_ip_type <value> ]\n        [ slave_ha { enabled | disabled } ]\n        [ slave_ha_priority <value> ]\n        [ skip_import_analyze { enabled | disabled } ]\n        [ mkms { enabled | disabled } ]\n        [ continue_on_error ]\n        [ gradual_src_mode { enabled | disabled } ]\n        [ gradual_sync_mode { enabled | disabled | auto } ]\n        [ gradual_sync_max_shards_per_source <value> ]\n        [ module_name <value> ] [ module_config_params <value> ]\n        [ crdt_xadd_id_uniqueness_mode { liberal | semi-strict | strict } ]\n        [ metrics_export_all { enabled | disabled } ]\n        [ syncer_mode { distributed | centralized }]\n        [ syncer_monitoring { enabled | disabled } ]\n        [ mtls_allow_weak_hashing { enabled | disabled } ]\n        [ mtls_allow_outdated_certs { enabled | disabled } ]\n        [ data_internode_encryption { enabled | disabled } ]\n        [ db_conns_auditing { enabled | disabled } ]\n        [ resp3 { enabled | disabled } ]\n        [ shards_placement { dense | sparse } ]\n        [ tracking_table_max_keys <size> ]","section_id":"tune-db"},{"id":"example-1-ex0","language":" sh","code":"$ rladmin tune db db:4 repl_timeout 300\nTuning database: o\nFinished successfully\n$ rladmin info db db:4 | grep repl_timeout\n  repl_timeout: 300 seconds","section_id":"example-1"},{"id":"tune-node-ex0","language":" sh","code":"tune node  { <id> | all }\n     [ max_listeners <value> ]\n     [ max_redis_forks <value> ]\n     [ max_redis_servers <value> ]\n     [ max_slave_full_syncs <value> ]\n     [ quorum_only { enabled | disabled } ]","section_id":"tune-node"},{"id":"example-2-ex0","language":" sh","code":"$ rladmin tune node 3 max_redis_servers 120\nFinished successfully\n$ rladmin info node 3 | grep \"max redis servers\"\n    max redis servers: 120","section_id":"example-2"},{"id":"tune-proxy-ex0","language":" sh","code":"rladmin tune proxy { <id> | all }\n        [ mode { static | dynamic } ]\n        [ threads <value> ]\n        [ max_threads <value> ]\n        [ scale_threshold <value> ]\n        [ scale_duration <seconds> ]","section_id":"tune-proxy"},{"id":"example-3-ex0","language":" sh","code":"$ rladmin tune proxy 2 scale_threshold 75\nConfiguring proxies:\n  - proxy:2: ok\n$ rladmin info proxy 2 | grep scale_threshold\n    scale_threshold: 75 (%)","section_id":"example-3"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/rladmin/upgrade","title":"rladmin upgrade","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/upgrade/","summary":"Upgrades a database's Redis version and modules.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:21+01:00","page_type":"content","content_hash":"0a0b2f02848f2206c9460bd447c885605c517881e4c9e8f484103dad6877fb74","sections":[{"id":"upgrade-db","title":"`upgrade db`","role":"overview","text":"Schedules a restart of the primary and replica processes of a database and then upgrades the database to the latest version of Redis.\n\nFor more information, see [Upgrade an existing Redis Software Deployment](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading).\n\n[code example]\n\nAs of v6.2.4, the default behavior for `upgrade db` has changed.  It is now controlled by a new parameter that sets the default upgrade policy used to create new databases and to upgrade ones already in the cluster.  To learn more, see [`tune cluster default_redis_version`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-cluster).\n\nAs of Redis Software version 7.8.2, `upgrade db` will always upgrade modules."},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameters                 | Type/Value               | Description                                                                                                            |\n|----------------------------|--------------------------|------------------------------------------------------------------------------------------------------------------------|\n| db                         | db:\\<id\\> <br />name     | Database to upgrade                                                                                                    |\n| and module | [upgrade module](#upgrade-module) command | Clause that allows the upgrade of a database and a specified Redis module in a single step with only one restart (can be specified multiple times). Deprecated as of Redis Software v7.8.2.  |\n| discard_data               |                          | Indicates that data will not be saved after the upgrade                                                                |\n| force                      |                          | Forces upgrade and skips warnings and confirmations                                                                    |\n| force_discard              |                          | Forces `discard_data` if replication or persistence is enabled                                                   |\n| keep_crdt_protocol_version |                          | Keeps the current CRDT protocol version                                                                                |\n| keep_redis_version       |                          | Upgrades to a new patch release, not to the latest major.minor version. Deprecated as of Redis Software v7.8.2. To upgrade modules without upgrading the Redis database version, set `redis_version` to the current Redis database version instead. |\n| parallel_shards_upgrade    | integer <br />'all'        | Maximum number of shards to upgrade all at once                                                                        |\n| preserve_roles             |                          | Performs an additional failover to guarantee the shards' roles are preserved                                             |\n| redis_version              | Redis version            | Upgrades the database to the specified version instead of the latest version                                               |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns `Done` if the upgrade completed. Otherwise, it returns an error."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"upgrade-db-ex0","language":"sh","code":"rladmin upgrade db { db:<id> | <name> }\n                [ preserve_roles ]\n                [ keep_redis_version ]\n                [ discard_data ]\n                [ force_discard ]\n                [ parallel_shards_upgrade ]\n                [ keep_crdt_protocol_version ]\n                [ redis_version <version> ]\n                [ force ]\n                [ and module module_name <module name> version <version> module_args <arguments string> ]","section_id":"upgrade-db"},{"id":"example-ex0","language":"sh","code":"$ rladmin upgrade db db:5\nMonitoring e39c8e87-75f9-4891-8c86-78cf151b720b\nactive - SMUpgradeBDB init\nactive - SMUpgradeBDB check_slaves\n.active - SMUpgradeBDB prepare\nactive - SMUpgradeBDB stop_forwarding\noactive - SMUpgradeBDB start_wd\nactive - SMUpgradeBDB wait_for_version\n.completed - SMUpgradeBDB\nDone","section_id":"example"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/rladmin/verify","title":"rladmin verify","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/verify/","summary":"Prints verification reports for the cluster.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:21+01:00","page_type":"content","content_hash":"c8bca145cb0507b5f70437329f81d6b5563dab76a76de98f0d4bdfd5098a3484","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Prints verification reports for the cluster."},{"id":"verify-balance","title":"`verify balance`","role":"content","text":"Prints a balance report that displays all of the unbalanced endpoints or nodes in the cluster.\n\n[code example]\n\nThe [proxy policy](https://redis.io/docs/latest/operate/rs/databases/configure/proxy-policy#proxy-policies) determines which nodes or endpoints to report as unbalanced.\n\nA node is unbalanced if:\n- `all-nodes` proxy policy and the node has no endpoint\n\nAn endpoint is unbalanced in the following cases:\n- `single` proxy policy and one of the following is true:  \n    - Shard placement is [`sparse`](https://redis.io/docs/latest/operate/rs/databases/memory-performance/shard-placement-policy#sparse-shard-placement-policy) and none of the master shards are on the node\n    - Shard placement is [`dense`](https://redis.io/docs/latest/operate/rs/databases/memory-performance/shard-placement-policy#dense-shard-placement-policy) and some master shards are on a different node from the endpoint\n- `all-master-shards` proxy policy and one of the following is true:  \n    - None of the master shards are on the node\n    - Some master shards are on a different node from the endpoint"},{"id":"parameters","title":"Parameters","role":"parameters","text":"| Parameter | Type/Value | Description |\n|-----------|------------|-------------|\n| node | integer | Specify a node ID to return a balance table for that node only (optional) |"},{"id":"returns","title":"Returns","role":"returns","text":"Returns a table of unbalanced endpoints and nodes in the cluster."},{"id":"examples","title":"Examples","role":"example","text":"Verify all nodes:\n\n[code example]\n\nVerify a specific node:\n\n[code example]"},{"id":"verify-rack_aware","title":"`verify rack_aware`","role":"content","text":"Verifies that the cluster complies with the rack awareness policy and reports any discovered rack collisions, if [rack-zone awareness](https://redis.io/docs/latest/operate/rs/clusters/configure/rack-zone-awareness) is enabled.\n\n[code example]"},{"id":"parameters-1","title":"Parameters","role":"parameters","text":"None"},{"id":"returns-1","title":"Returns","role":"returns","text":"Returns whether the cluster is rack aware. If rack awareness is enabled, it returns any rack collisions."},{"id":"example","title":"Example","role":"example","text":"[code example]"}],"examples":[{"id":"verify-balance-ex0","language":"sh","code":"rladmin verify balance [ node <ID> ]","section_id":"verify-balance"},{"id":"examples-ex0","language":"sh","code":"$ rladmin verify balance       \nThe table presents all of the unbalanced endpoints/nodes in the cluster\nBALANCE:\nNODE:ID  DB:ID  NAME  ENDPOINT:ID  PROXY_POLICY  LOCAL SHARDS   TOTAL SHARDS","section_id":"examples"},{"id":"examples-ex1","language":"sh","code":"$ rladmin verify balance node 1\nThe table presents all of the unbalanced endpoints/nodes in the cluster\nBALANCE:\nNODE:ID  DB:ID  NAME  ENDPOINT:ID  PROXY_POLICY  LOCAL SHARDS   TOTAL SHARDS","section_id":"examples"},{"id":"verify-rack_aware-ex0","language":"sh","code":"rladmin verify rack_aware","section_id":"verify-rack_aware"},{"id":"example-ex0","language":"sh","code":"$ rladmin verify rack_aware\n\nCluster policy is not configured for rack awareness.","section_id":"example"}]}
{"schema_version":2,"id":"operate/rs/references/cli-utilities/rlcheck","title":"rlcheck","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/rlcheck/","summary":"Verify nodes.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","children":[],"page_type":"content","content_hash":"e021c2c302376ccbe4d70ffb5b88f7e10a2f4be37879d66433c36bee5454cee6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The `rlcheck` utility runs various [tests](#tests) to check the health of a Redis Software node and reports any discovered issues.\nYou can use this utility to confirm a successful installation or to verify that the node is functioning properly.\n\nTo resolve issues reported by `rlcheck`, [contact Redis support](https://redis.com/company/support/)."},{"id":"run-rlcheck","title":"Run rlcheck","role":"content","text":"You can run `rlcheck` from the node host's command line.\nThe output of `rlcheck` shows information specific to the host you run it on.\n\nTo run `rlcheck` tests:\n\n1. Sign in to the Redis Software host with an account that is a member of the **redislabs** operating system group.\n\n1. Run: \n\n    [code example]"},{"id":"options","title":"Options","role":"parameters","text":"You can run `rlcheck` with the following options:\n\n| Option | Description |\n|--------|-------------|\n| <nobr>`--suppress-tests TEXT`</nobr> | Skip the specified, comma-delimited list of tests. See [Tests](#tests) for the list of tests and descriptions. |\n| <nobr>`--retry-delay INTEGER`</nobr> | Delay between retries, in seconds. |\n| <nobr>`--retry INTEGER`</nobr> | Number of retries after a failure. |\n| <nobr>`--file-path TEXT`</nobr> | Custom path to `rlcheck.log`. |\n| <nobr>`--continue-on-error`</nobr> | Continue to run all tests even if a test fails, then show all errors when complete. |\n| `--help` | Return the list of `rlcheck` options. |"},{"id":"tests","title":"Tests","role":"content","text":"`rlcheck` runs the following tests by default:\n\n| Test name | Description |\n|-----------|-------------|\n| verify_owner_and_group | Verifies the owner and group for Redis Software files are correct. |\n| verify_bootstrap_status | Verifies the local node's bootstrap process completed without errors. |\n| verify_services | Verifies all Redis Software services are running. |\n| verify_port_range | Verifies the [`ip_local_port_range`](https://www.kernel.org/doc/html/latest/networking/ip-sysctl.html) doesn't conflict with the ports Redis Software might assign to shards. |\n| verify_pidfiles | Verifies all active local shards have PID files. |\n| verify_capabilities | Verifies all binaries have the proper capability bits. |\n| verify_existing_sockets | Verifies sockets exist for all processes that require them. |\n| verify_host_settings | Verifies the following:<br />• Linux `overcommit_memory` setting is 1.<br />•`transparent_hugepage` is disabled.<br />• Socket maximum connections setting `somaxconn` is 1024. |\n| verify_tcp_connectivity | Verifies this node can connect to all other alive nodes. |\n| verify_encrypted_gossip | Verifies gossip communication is encrypted. |"}],"examples":[{"id":"run-rlcheck-ex0","language":"sh","code":"rlcheck","section_id":"run-rlcheck"}]}
{"schema_version":2,"id":"operate/rs/references/client_references","title":"Develop with Redis clients","url":"https://redis.io/docs/latest/operate/rs/references/client_references/","summary":"Redis client libraries allow you to connect to Redis instances from within your application. This section provides an overview of several recommended Redis clients for popular programming and scripting languages.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:21+01:00","children":[],"page_type":"content","content_hash":"19a75cf8a13601ac310afdc1e10a83759bf3afefec48440f95da2a85e07a28f4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"To connect to Redis instances from within your application, use a Redis client library that matches your application's language."},{"id":"official-clients","title":"Official clients","role":"content","text":"| Language | Client name |\n| :---------- | :------------- |\n| .Net | [StackExchange.Redis](https://redis.io/docs/latest/develop/clients/dotnet) |\n| Go | [go-redis](https://redis.io/docs/latest/develop/clients/go) |\n| Java | [Jedis](https://redis.io/docs/latest/develop/clients/jedis) (Synchronous) and [Lettuce](https://redis.io/docs/latest/develop/clients/lettuce) (Asynchronous) |\n| Node.js | [node-redis](https://redis.io/docs/latest/develop/clients/nodejs) |\n| Python | [redis-py](https://redis.io/docs/latest/develop/clients/redis-py) |\n\nSelect a client name to see its quick start."},{"id":"other-clients","title":"Other clients","role":"content","text":"For a list of community-driven Redis clients, which are available for more programming languages, see\n[Community-supported clients](https://redis.io/docs/latest/develop/clients#community-supported-clients)."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/compatibility/client-side-caching","title":"Client-side caching compatibility with Redis Software and Redis Cloud","url":"https://redis.io/docs/latest/operate/rs/references/compatibility/client-side-caching/","summary":"Redis Software and Redis Cloud compatibility with client-side caching.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:21+01:00","page_type":"content","content_hash":"4e65e2d3172899a08762c3c3f458eb677a0eebe987bf777aaa5edceba787cca6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software and Redis Cloud support [client-side caching](https://redis.io/docs/latest/develop/clients/client-side-caching) for databases with Redis versions 7.4 or later."},{"id":"required-database-versions","title":"Required database versions","role":"content","text":"Client-side caching in Redis Software and Redis Cloud requires Redis database versions 7.4 or later.\n\nThe following table shows the differences in client-side caching support by product:\n\n| Redis product           | Client-side caching support |\n|-------------------------|-----------------------------|\n| Redis Open Source       | Redis v6.0 and later |\n| Redis Cloud             | Redis database v7.4 and later |\n| Redis Software          | Redis database v7.4 and later |"},{"id":"supported-resp-versions","title":"Supported RESP versions","role":"compatibility","text":"Client-side caching in Redis Software and Redis Cloud requires [RESP3](https://redis.io/docs/latest/develop/reference/protocol-spec#resp-versions).\n\nThe following table shows the differences in client-side caching support for RESP by product:\n\n| Redis product with client-side caching  | RESP2 | RESP3 |\n|-------------------------|-------|-------|\n| Redis Open Source       | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Redis Cloud             | <span title=\"Not supported\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Redis Software          | <span title=\"Not supported\">&#x274c;</span> | <span title=\"Supported\">&#x2705;</span> |"},{"id":"two-connections-mode-with-redirect-not-supported","title":"Two connections mode with REDIRECT not supported","role":"content","text":"Unlike Redis Open Source, Redis Software and Redis Cloud do not support [two connections mode](https://redis.io/docs/latest/develop/reference/client-side-caching#two-connections-mode) or the `REDIRECT` option for [`CLIENT TRACKING`](https://redis.io/docs/latest/commands/client-tracking)."},{"id":"change-tracking_table_max_keys-for-a-database","title":"Change tracking_table_max_keys for a database","role":"content","text":"When client-side caching is enabled, Redis uses an invalidation table to track which keys are cached by each connected client.\n\nThe configuration setting `tracking-table-max-keys` determines the maximum number of keys stored in the invalidation table and is set to `1000000` keys by default. Redis Software does not support using `CONFIG SET` to change this value, but you can use the REST API or rladmin instead.\n\nTo change `tracking_table_max_keys` for a database in a Redis Software cluster:\n\n- [`rladmin tune db`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-db):\n\n    [code example]\n\n    You can use the database name in place of `db:<ID>` in the preceding command.\n\n- [Update database configuration](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#put-bdbs) REST API request:\n\n    [code example]"},{"id":"change-default-tracking_table_max_keys","title":"Change default tracking_table_max_keys","role":"content","text":"The cluster-wide option `default_tracking_table_max_keys_policy` determines the default value of `tracking_table_max_keys` for new databases in a Redis Software cluster. `default_tracking_table_max_keys_policy` is set to `1000000` keys by default.\n\nTo change `default_tracking_table_max_keys_policy`, use one of the following methods:\n\n- [`rladmin tune cluster`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-cluster)\n\n    [code example]\n\n- [Update cluster policy](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/policy#put-cluster-policy) REST API request:\n\n    [code example]"}],"examples":[{"id":"change-tracking_table_max_keys-for-a-database-ex0","language":"sh","code":"rladmin tune db db:<ID> tracking_table_max_keys 2000000","section_id":"change-tracking_table_max_keys-for-a-database"},{"id":"change-tracking_table_max_keys-for-a-database-ex1","language":"sh","code":"PUT /v1/bdbs/<uid> \n    { \"tracking_table_max_keys\": 2000000 }","section_id":"change-tracking_table_max_keys-for-a-database"},{"id":"change-default-tracking_table_max_keys-ex0","language":"sh","code":"rladmin tune cluster default_tracking_table_max_keys_policy 2000000","section_id":"change-default-tracking_table_max_keys"},{"id":"change-default-tracking_table_max_keys-ex1","language":"sh","code":"PUT /v1/cluster/policy \n    { \"default_tracking_table_max_keys_policy\": 2000000 }","section_id":"change-default-tracking_table_max_keys"}]}
{"schema_version":2,"id":"operate/rs/references/compatibility/commands/cluster","title":"Cluster management commands compatibility","url":"https://redis.io/docs/latest/operate/rs/references/compatibility/commands/cluster/","summary":"Cluster management commands compatible with Redis Software.","tags":["docs","operate","rs","rc"],"last_updated":"2026-09-24T14:53:09+01:00","page_type":"content","content_hash":"c52501d142dfd066e95bd3efe7ce51ca7e01310b1301b1d1a57e7a1ca20dddbd","sections":[{"id":"content","title":"Content","role":"content","text":"[Clustering in Redis Software](https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering) and [Redis Cloud](https://redis.io/docs/latest/operate/rc/databases/configuration/clustering) differs from the [Redis Open Source cluster](https://redis.io/docs/latest/operate/oss_and_stack/management/scaling) and works with all standard Redis clients.\n\nRedis Software blocks most [cluster commands](/content/commands?group=cluster). If you try to use a blocked cluster command, it returns an error.\n\n| Command | Redis<br />Software | Redis<br />Cloud | Notes |\n|:--------|:----------------------|:-----------------|:------|\n| [ASKING](https://redis.io/docs/latest/commands/asking) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [CLUSTER ADDSLOTS](https://redis.io/docs/latest/commands/cluster-addslots) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [CLUSTER ADDSLOTSRANGE](https://redis.io/docs/latest/commands/cluster-addslotsrange) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [CLUSTER BUMPEPOCH](https://redis.io/docs/latest/commands/cluster-bumpepoch) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [CLUSTER COUNT-FAILURE-REPORTS](https://redis.io/docs/latest/commands/cluster-count-failure-reports) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [CLUSTER COUNTKEYSINSLOT](https://redis.io/docs/latest/commands/cluster-countkeysinslot) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [CLUSTER DELSLOTS](https://redis.io/docs/latest/commands/cluster-delslots) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [CLUSTER DELSLOTSRANGE](https://redis.io/docs/latest/commands/cluster-delslotsrange) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [CLUSTER FAILOVER](https://redis.io/docs/latest/commands/cluster-failover) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [CLUSTER FLUSHSLOTS](https://redis.io/docs/latest/commands/cluster-flushslots) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [CLUSTER FORGET](https://redis.io/docs/latest/commands/cluster-forget) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [CLUSTER GETKEYSINSLOT](https://redis.io/docs/latest/commands/cluster-getkeysinslot) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [CLUSTER HELP](https://redis.io/docs/latest/commands/cluster-help) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Only supported with the [OSS cluster API](https://redis.io/docs/latest/operate/rs/databases/configure/oss-cluster-api). |\n| [CLUSTER INFO](https://redis.io/docs/latest/commands/cluster-info) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Only supported with the [OSS cluster API](https://redis.io/docs/latest/operate/rs/databases/configure/oss-cluster-api). |\n| [CLUSTER KEYSLOT](https://redis.io/docs/latest/commands/cluster-keyslot) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Only supported with the [OSS cluster API](https://redis.io/docs/latest/operate/rs/databases/configure/oss-cluster-api). |\n| [CLUSTER LINKS](https://redis.io/docs/latest/commands/cluster-links) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [CLUSTER MIGRATION](https://redis.io/docs/latest/commands/cluster-migration) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [CLUSTER MEET](https://redis.io/docs/latest/commands/cluster-meet) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [CLUSTER MYID](https://redis.io/docs/latest/commands/cluster-myid) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [CLUSTER MYSHARDID](https://redis.io/docs/latest/commands/cluster-myshardid) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [CLUSTER NODES](https://redis.io/docs/latest/commands/cluster-nodes) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Only supported with the [OSS cluster API](https://redis.io/docs/latest/operate/rs/databases/configure/oss-cluster-api). |\n| [CLUSTER REPLICAS](https://redis.io/docs/latest/commands/cluster-replicas) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [CLUSTER REPLICATE](https://redis.io/docs/latest/commands/cluster-replicate) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [CLUSTER RESET](https://redis.io/docs/latest/commands/cluster-reset) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [CLUSTER SAVECONFIG](https://redis.io/docs/latest/commands/cluster-saveconfig) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [CLUSTER SET-CONFIG-EPOCH](https://redis.io/docs/latest/commands/cluster-set-config-epoch) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [CLUSTER SETSLOT](https://redis.io/docs/latest/commands/cluster-setslot) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [CLUSTER SHARDS](https://redis.io/docs/latest/commands/cluster-shards) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Only supported with the [OSS cluster API](https://redis.io/docs/latest/operate/rs/databases/configure/oss-cluster-api). |\n| [CLUSTER SLAVES](https://redis.io/docs/latest/commands/cluster-slaves) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | Deprecated as of Redis v5.0.0. |\n| [CLUSTER SLOT-STATS](https://redis.io/docs/latest/commands/cluster-slot-stats) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [CLUSTER SLOTS](https://redis.io/docs/latest/commands/cluster-slots) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Only supported with the [OSS cluster API](https://redis.io/docs/latest/operate/rs/databases/configure/oss-cluster-api). Deprecated as of Redis v7.0.0. |\n| [READONLY](https://redis.io/docs/latest/commands/readonly) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [READWRITE](https://redis.io/docs/latest/commands/readwrite) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/compatibility/commands/connection","title":"Connection management commands compatibility","url":"https://redis.io/docs/latest/operate/rs/references/compatibility/commands/connection/","summary":"Connection management commands compatibility.","tags":["docs","operate","rs","rc"],"last_updated":"2026-09-24T14:53:09+01:00","page_type":"content","content_hash":"a780955002f9961abe702c1e7b542e7f519c294011e05e5fb7609a1b23fdf540","sections":[{"id":"content","title":"Content","role":"content","text":"The following tables show which Redis Open Source [connection management commands](/content/commands?group=connection) are compatible with standard and Active-Active databases in Redis Software and Redis Cloud.\n\n\n| Command | Redis<br />Software | Redis<br />Cloud | Notes |\n|:--------|:----------------------|:-----------------|:------|\n| [AUTH](https://redis.io/docs/latest/commands/auth) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [CLIENT CACHING](https://redis.io/docs/latest/commands/client-caching) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [CLIENT GETNAME](https://redis.io/docs/latest/commands/client-getname) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [CLIENT GETREDIR](https://redis.io/docs/latest/commands/client-getredir) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [CLIENT ID](https://redis.io/docs/latest/commands/client-id) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Because Redis Software clustering allows [multiple active proxies](https://redis.io/docs/latest/operate/rs/databases/configure/proxy-policy), `CLIENT ID` cannot guarantee incremental IDs between clients that connect to different nodes under multi proxy policies. |\n| [CLIENT INFO](https://redis.io/docs/latest/commands/client-info) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [CLIENT KILL](https://redis.io/docs/latest/commands/client-kill) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [CLIENT LIST](https://redis.io/docs/latest/commands/client-list) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [CLIENT NO-EVICT](https://redis.io/docs/latest/commands/client-no-evict) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [CLIENT <nobr>NO-TOUCH</nobr>](https://redis.io/docs/latest/commands/client-no-touch) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [CLIENT PAUSE](https://redis.io/docs/latest/commands/client-pause) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [CLIENT REPLY](https://redis.io/docs/latest/commands/client-reply) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [CLIENT SETINFO](https://redis.io/docs/latest/commands/client-setinfo) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [CLIENT SETNAME](https://redis.io/docs/latest/commands/client-setname) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [CLIENT TRACKING](https://redis.io/docs/latest/commands/client-tracking) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [CLIENT TRACKINGINFO](https://redis.io/docs/latest/commands/client-trackinginfo) |<span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [CLIENT UNBLOCK](https://redis.io/docs/latest/commands/client-unblock) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [CLIENT UNPAUSE](https://redis.io/docs/latest/commands/client-unpause) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [ECHO](https://redis.io/docs/latest/commands/echo) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [HELLO](https://redis.io/docs/latest/commands/hello) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [PING](https://redis.io/docs/latest/commands/ping) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [QUIT](https://redis.io/docs/latest/commands/quit) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Deprecated as of Redis v7.2.0. |\n| [RESET](https://redis.io/docs/latest/commands/reset) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [SELECT](https://redis.io/docs/latest/commands/select) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | Redis Software does not support shared databases due to potential negative performance impacts and blocks any related commands. The `SELECT` command is supported solely for compatibility with Redis Open Source but does not perform any operations in Redis Software. |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/compatibility/commands/data-types","title":"Data type commands compatibility","url":"https://redis.io/docs/latest/operate/rs/references/compatibility/commands/data-types/","summary":"Data type commands compatibility (bitmaps, geospatial indices, hashes, HyperLogLogs, lists, sets, sorted sets, streams, strings).","tags":["docs","operate","rs","rc"],"last_updated":"2026-09-24T10:14:21+01:00","page_type":"content","content_hash":"e420f6bc58c83c4816ad80c3895f20a9e8dd14c9e3a3a9055d4a3c87dcdd08ac","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The following tables show which Redis Open Source data type commands are compatible with standard and Active-Active databases in Redis Software and Redis Cloud."},{"id":"array-commands","title":"Array commands","role":"content","text":"| <span style=\"min-width: 10em; display: table-cell\">Command</span> | Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:--------|:----------------------|:-----------------|:------|\n| [ARCOUNT](https://redis.io/docs/latest/commands/arcount) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [ARDEL](https://redis.io/docs/latest/commands/ardel) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [ARDELRANGE](https://redis.io/docs/latest/commands/ardelrange) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [ARGET](https://redis.io/docs/latest/commands/arget) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [ARGETRANGE](https://redis.io/docs/latest/commands/argetrange) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [ARGREP](https://redis.io/docs/latest/commands/argrep) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [ARINFO](https://redis.io/docs/latest/commands/arinfo) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [ARINSERT](https://redis.io/docs/latest/commands/arinsert) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [ARLASTITEMS](https://redis.io/docs/latest/commands/arlastitems) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [ARLEN](https://redis.io/docs/latest/commands/arlen) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [ARMGET](https://redis.io/docs/latest/commands/armget) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [ARMSET](https://redis.io/docs/latest/commands/armset) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [ARNEXT](https://redis.io/docs/latest/commands/arnext) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [AROP](https://redis.io/docs/latest/commands/arop) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [ARRING](https://redis.io/docs/latest/commands/arring) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [ARSCAN](https://redis.io/docs/latest/commands/arscan) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [ARSEEK](https://redis.io/docs/latest/commands/arseek) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [ARSET](https://redis.io/docs/latest/commands/arset) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"bitmap-commands","title":"Bitmap commands","role":"content","text":"| <span style=\"min-width: 10em; display: table-cell\">Command</span> | Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:--------|:----------------------|:-----------------|:------|\n| [BITCOUNT](https://redis.io/docs/latest/commands/bitcount) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [BITFIELD](https://redis.io/docs/latest/commands/bitfield) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [BITFIELD_RO](https://redis.io/docs/latest/commands/bitfield_ro) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [BITOP](https://redis.io/docs/latest/commands/bitop) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span>  |  |\n| [BITPOS](https://redis.io/docs/latest/commands/bitpos) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span>  |  |\n| [GETBIT](https://redis.io/docs/latest/commands/getbit) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [SETBIT](https://redis.io/docs/latest/commands/setbit) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"geospatial-indices-commands","title":"Geospatial indices commands","role":"content","text":"| <span style=\"min-width: 10em; display: table-cell\">Command</span> | Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:--------|:----------------------|:-----------------|:------|\n| [GEOADD](https://redis.io/docs/latest/commands/geoadd) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [GEODIST](https://redis.io/docs/latest/commands/geodist) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [GEOHASH](https://redis.io/docs/latest/commands/geohash) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [GEOPOS](https://redis.io/docs/latest/commands/geopos) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [GEORADIUS](https://redis.io/docs/latest/commands/georadius) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Deprecated as of Redis v6.2.0. |\n| [GEORADIUS_RO](https://redis.io/docs/latest/commands/georadius_ro) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Deprecated as of Redis v6.2.0. |\n| [GEORADIUSBYMEMBER](https://redis.io/docs/latest/commands/georadiusbymember) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Deprecated as of Redis v6.2.0. |\n| [GEORADIUSBYMEMBER_RO](https://redis.io/docs/latest/commands/georadiusbymember_ro) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Deprecated as of Redis v6.2.0. |\n| [GEOSEARCH](https://redis.io/docs/latest/commands/geosearch) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [GEOSEARCHSTORE](https://redis.io/docs/latest/commands/geosearchstore) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"hash-commands","title":"Hash commands","role":"content","text":"| <span style=\"min-width: 10em; display: table-cell\">Command</span> | Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:--------|:----------------------|:-----------------|:------|\n| [HDEL](https://redis.io/docs/latest/commands/hdel) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [HEXISTS](https://redis.io/docs/latest/commands/hexists) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [HEXPIRE](https://redis.io/docs/latest/commands/hexpire) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [HEXPIREAT](https://redis.io/docs/latest/commands/hexpireat) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [HEXPIRETIME](https://redis.io/docs/latest/commands/hexpiretime) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [HGET](https://redis.io/docs/latest/commands/hget) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [HGETALL](https://redis.io/docs/latest/commands/hgetall) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [HGETDEL](https://redis.io/docs/latest/commands/hgetdel) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [HGETEX](https://redis.io/docs/latest/commands/hgetex) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [HINCRBY](https://redis.io/docs/latest/commands/hincrby) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [HINCRBYFLOAT](https://redis.io/docs/latest/commands/hincrbyfloat) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [HKEYS](https://redis.io/docs/latest/commands/hkeys) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [HLEN](https://redis.io/docs/latest/commands/hlen) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [HMGET](https://redis.io/docs/latest/commands/hmget) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [HMSET](https://redis.io/docs/latest/commands/hmset) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Deprecated as of Redis v4.0.0. |\n| [HPERSIST](https://redis.io/docs/latest/commands/hpersist) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [HPEXPIRE](https://redis.io/docs/latest/commands/hpexpire) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [HPEXPIREAT](https://redis.io/docs/latest/commands/hpexpireat) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [HPEXPIRETIME](https://redis.io/docs/latest/commands/hpexpiretime) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [HPTTL](https://redis.io/docs/latest/commands/hpttl) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [HRANDFIELD](https://redis.io/docs/latest/commands/hrandfield) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [HSCAN](https://redis.io/docs/latest/commands/hscan) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [HSET](https://redis.io/docs/latest/commands/hset) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [HSETEX](https://redis.io/docs/latest/commands/hsetex) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [HSETNX](https://redis.io/docs/latest/commands/hsetnx) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [HSTRLEN](https://redis.io/docs/latest/commands/hstrlen) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [HTTL](https://redis.io/docs/latest/commands/httl) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [HVALS](https://redis.io/docs/latest/commands/hvals) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"hyperloglog-commands","title":"HyperLogLog commands","role":"content","text":"| <span style=\"min-width: 10em; display: table-cell\">Command</span> | Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:--------|:----------------------|:-----------------|:------|\n| [PFADD](https://redis.io/docs/latest/commands/pfadd) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [PFCOUNT](https://redis.io/docs/latest/commands/pfcount) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [PFDEBUG](https://redis.io/docs/latest/commands/pfdebug) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [PFMERGE](https://redis.io/docs/latest/commands/pfmerge) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [PFSELFTEST](https://redis.io/docs/latest/commands/pfselftest) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"list-commands","title":"List commands","role":"content","text":"| <span style=\"min-width: 10em; display: table-cell\">Command</span> | Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:--------|:----------------------|:-----------------|:------|\n| [BLMOVE](https://redis.io/docs/latest/commands/blmove) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [BLMOVEM](https://redis.io/docs/latest/commands/blmovem) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [BLMPOP](https://redis.io/docs/latest/commands/blmpop) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [BLPOP](https://redis.io/docs/latest/commands/blpop) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [BRPOP](https://redis.io/docs/latest/commands/brpop) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [BRPOPLPUSH](https://redis.io/docs/latest/commands/brpoplpush) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Deprecated as of Redis v6.2.0. |\n| [LINDEX](https://redis.io/docs/latest/commands/lindex) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [LINSERT](https://redis.io/docs/latest/commands/linsert) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [LLEN](https://redis.io/docs/latest/commands/llen) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [LMOVE](https://redis.io/docs/latest/commands/lmove) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [LMOVEM](https://redis.io/docs/latest/commands/lmovem) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [LMPOP](https://redis.io/docs/latest/commands/lmpop) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [LPOP](https://redis.io/docs/latest/commands/lpop) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [LPOS](https://redis.io/docs/latest/commands/lpos) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [LPUSH](https://redis.io/docs/latest/commands/lpush) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [LPUSHX](https://redis.io/docs/latest/commands/lpushx) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [LRANGE](https://redis.io/docs/latest/commands/lrange) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [LREM](https://redis.io/docs/latest/commands/lrem) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [LSET](https://redis.io/docs/latest/commands/lset) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [LTRIM](https://redis.io/docs/latest/commands/ltrim) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [RPOP](https://redis.io/docs/latest/commands/rpop) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [RPOPLPUSH](https://redis.io/docs/latest/commands/rpoplpush) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Deprecated as of Redis v6.2.0. |\n| [RPUSH](https://redis.io/docs/latest/commands/rpush) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [RPUSHX](https://redis.io/docs/latest/commands/rpushx) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"set-commands","title":"Set commands","role":"content","text":"| <span style=\"min-width: 10em; display: table-cell\">Command</span> | Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:--------|:----------------------|:-----------------|:------|\n| [SADD](https://redis.io/docs/latest/commands/sadd) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [SCARD](https://redis.io/docs/latest/commands/scard) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [SDIFF](https://redis.io/docs/latest/commands/sdiff) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [SDIFFCARD](https://redis.io/docs/latest/commands/sdiffcard) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [SDIFFSTORE](https://redis.io/docs/latest/commands/sdiffstore) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [SINTER](https://redis.io/docs/latest/commands/sinter) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [SINTERCARD](https://redis.io/docs/latest/commands/sintercard) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [SINTERSTORE](https://redis.io/docs/latest/commands/sinterstore) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [SISMEMBER](https://redis.io/docs/latest/commands/sismember) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [SMEMBERS](https://redis.io/docs/latest/commands/smembers) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [SMISMEMBER](https://redis.io/docs/latest/commands/smismember) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [SMOVE](https://redis.io/docs/latest/commands/smove) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [SPOP](https://redis.io/docs/latest/commands/spop) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [SRANDMEMBER](https://redis.io/docs/latest/commands/srandmember) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [SREM](https://redis.io/docs/latest/commands/srem) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [SSCAN](https://redis.io/docs/latest/commands/sscan) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [SUNION](https://redis.io/docs/latest/commands/sunion) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [SUNIONCARD](https://redis.io/docs/latest/commands/sunioncard) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [SUNIONSTORE](https://redis.io/docs/latest/commands/sunionstore) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"sorted-set-commands","title":"Sorted set commands","role":"content","text":"| <span style=\"min-width: 10em; display: table-cell\">Command</span> | Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:--------|:----------------------|:-----------------|:------|\n| [BZMPOP](https://redis.io/docs/latest/commands/bzmpop) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [BZPOPMAX](https://redis.io/docs/latest/commands/bzpopmax) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [BZPOPMIN](https://redis.io/docs/latest/commands/bzpopmin) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [ZADD](https://redis.io/docs/latest/commands/zadd) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [ZCARD](https://redis.io/docs/latest/commands/zcard) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [ZCOUNT](https://redis.io/docs/latest/commands/zcount) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [ZDIFF](https://redis.io/docs/latest/commands/zdiff) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [ZDIFFSTORE](https://redis.io/docs/latest/commands/zdiffstore) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [ZINCRBY](https://redis.io/docs/latest/commands/zincrby) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [ZINTER](https://redis.io/docs/latest/commands/zinter) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [ZINTERCARD](https://redis.io/docs/latest/commands/zintercard) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [ZINTERSTORE](https://redis.io/docs/latest/commands/zinterstore) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [ZLEXCOUNT](https://redis.io/docs/latest/commands/zlexcount) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [ZMPOP](https://redis.io/docs/latest/commands/zmpop) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [ZMSCORE](https://redis.io/docs/latest/commands/zmscore) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [ZPOPMAX](https://redis.io/docs/latest/commands/zpopmax) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [ZPOPMIN](https://redis.io/docs/latest/commands/zpopmin) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [ZRANDMEMBER](https://redis.io/docs/latest/commands/zrandmember) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [ZRANGE](https://redis.io/docs/latest/commands/zrange) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [ZRANGEBYLEX](https://redis.io/docs/latest/commands/zrangebylex) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Deprecated as of Redis v6.2.0. |\n| [ZRANGEBYSCORE](https://redis.io/docs/latest/commands/zrangebyscore) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Deprecated as of Redis v6.2.0. |\n| [ZRANGESTORE](https://redis.io/docs/latest/commands/zrangestore) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [ZRANK](https://redis.io/docs/latest/commands/zrank) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [ZREM](https://redis.io/docs/latest/commands/zrem) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [ZREMRANGEBYLEX](https://redis.io/docs/latest/commands/zremrangebylex) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [ZREMRANGEBYRANK](https://redis.io/docs/latest/commands/zremrangebyrank) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [ZREMRANGEBYSCORE](https://redis.io/docs/latest/commands/zremrangebyscore) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [ZREVRANGE](https://redis.io/docs/latest/commands/zrevrange) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Deprecated as of Redis v6.2.0. |\n| [ZREVRANGEBYLEX](https://redis.io/docs/latest/commands/zrevrangebylex) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Deprecated as of Redis v6.2.0. |\n| [ZREVRANGEBYSCORE](https://redis.io/docs/latest/commands/zrevrangebyscore) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Deprecated as of Redis v6.2.0. |\n| [ZREVRANK](https://redis.io/docs/latest/commands/zrevrank) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [ZSCAN](https://redis.io/docs/latest/commands/zscan) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [ZSCORE](https://redis.io/docs/latest/commands/zscore) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [ZUNION](https://redis.io/docs/latest/commands/zunion) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [ZUNIONSTORE](https://redis.io/docs/latest/commands/zunionstore) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"stream-commands","title":"Stream commands","role":"content","text":"| <span style=\"min-width: 10em; display: table-cell\">Command</span> | Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:--------|:----------------------|:-----------------|:------|\n| [XACK](https://redis.io/docs/latest/commands/xack) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [XACKDEL](https://redis.io/docs/latest/commands/xackdel) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [XADD](https://redis.io/docs/latest/commands/xadd) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [XAUTOCLAIM](https://redis.io/docs/latest/commands/xautoclaim) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [XCLAIM](https://redis.io/docs/latest/commands/xclaim) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [XCFGSET](https://redis.io/docs/latest/commands/xcfgset) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [XDEL](https://redis.io/docs/latest/commands/xdel) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [XDELEX](https://redis.io/docs/latest/commands/xdelex) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [XGROUP](https://redis.io/docs/latest/commands/xgroup) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [XIDMPRECORD](https://redis.io/docs/latest/commands/xidmprecord) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [XINFO](https://redis.io/docs/latest/commands/xinfo) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [XLEN](https://redis.io/docs/latest/commands/xlen) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [XNACK](https://redis.io/docs/latest/commands/xnack) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [XPENDING](https://redis.io/docs/latest/commands/xpending) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [XRANGE](https://redis.io/docs/latest/commands/xrange) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [XREAD](https://redis.io/docs/latest/commands/xread) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [XREADGROUP](https://redis.io/docs/latest/commands/xreadgroup) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [XREVRANGE](https://redis.io/docs/latest/commands/xrevrange) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [XSETID](https://redis.io/docs/latest/commands/xsetid) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [XTRIM](https://redis.io/docs/latest/commands/xtrim) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"string-commands","title":"String commands","role":"content","text":"| <span style=\"min-width: 10em; display: table-cell\">Command</span> | Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:--------|:----------------------|:-----------------|:------|\n| [APPEND](https://redis.io/docs/latest/commands/append) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [DECR](https://redis.io/docs/latest/commands/decr) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [DECRBY](https://redis.io/docs/latest/commands/decrby) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [DELEX](https://redis.io/docs/latest/commands/delex) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [DIGEST](https://redis.io/docs/latest/commands/digest) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [GET](https://redis.io/docs/latest/commands/get) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [GETDEL](https://redis.io/docs/latest/commands/getdel) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [GETEX](https://redis.io/docs/latest/commands/getex) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active\\*</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active\\*</nobr></span> | \\*Not supported for HyperLogLog. |\n| [GETRANGE](https://redis.io/docs/latest/commands/getrange) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [GETSET](https://redis.io/docs/latest/commands/getset) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Deprecated as of Redis v6.2.0. |\n| [INCR](https://redis.io/docs/latest/commands/incr) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [INCRBY](https://redis.io/docs/latest/commands/incrby) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [INCRBYFLOAT](https://redis.io/docs/latest/commands/incrbyfloat) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [INCREX](https://redis.io/docs/latest/commands/increx) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [LCS](https://redis.io/docs/latest/commands/lcs) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [MGET](https://redis.io/docs/latest/commands/mget) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [MSET](https://redis.io/docs/latest/commands/mset) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [MSETEX](https://redis.io/docs/latest/commands/msetex) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [MSETNX](https://redis.io/docs/latest/commands/msetnx) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [PSETEX](https://redis.io/docs/latest/commands/psetex) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [SET](https://redis.io/docs/latest/commands/set) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [SETEX](https://redis.io/docs/latest/commands/setex) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [SETNX](https://redis.io/docs/latest/commands/setnx) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [SETRANGE](https://redis.io/docs/latest/commands/setrange) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| STRALGO | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | Deprecated as of Redis v7.0.0. |\n| [STRLEN](https://redis.io/docs/latest/commands/strlen) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [SUBSTR](https://redis.io/docs/latest/commands/substr) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | Deprecated as of Redis v2.0.0. |"},{"id":"vector-set-commands","title":"Vector set commands","role":"content","text":"| <span style=\"min-width: 10em; display: table-cell\">Command</span> | Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:--------|:----------------------|:-----------------|:------|\n| [VADD](https://redis.io/docs/latest/commands/vadd) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [VCARD](https://redis.io/docs/latest/commands/vcard) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [VDIM](https://redis.io/docs/latest/commands/vdim) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [VEMB](https://redis.io/docs/latest/commands/vemb) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [VGETATTR](https://redis.io/docs/latest/commands/vgetattr) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [VINFO](https://redis.io/docs/latest/commands/vinfo) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [VISMEMBER](https://redis.io/docs/latest/commands/vismember) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [VLINKS](https://redis.io/docs/latest/commands/vlinks) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [VRANDMEMBER](https://redis.io/docs/latest/commands/vrandmember) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [VRANGE](https://redis.io/docs/latest/commands/vrange) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [VREM](https://redis.io/docs/latest/commands/vrem) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [VSETATTR](https://redis.io/docs/latest/commands/vsetattr) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [VSIM](https://redis.io/docs/latest/commands/vsim) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/compatibility/commands/generic","title":"Key commands compatibility","url":"https://redis.io/docs/latest/operate/rs/references/compatibility/commands/generic/","summary":"Generic key commands compatible with Redis Software.","tags":["docs","operate","rs","rc"],"last_updated":"2026-09-24T14:53:09+01:00","page_type":"content","content_hash":"739148a5b14ff363e7170b794dac1cefdd1ddc6f5a83bd2e63a4f8ec45c80fb7","sections":[{"id":"content","title":"Content","role":"content","text":"The following table shows which Redis Open Source [key (generic) commands](/content/commands?group=generic) are compatible with standard and Active-Active databases in Redis Software and Redis Cloud.\n\n| Command | Redis<br />Software | Redis<br />Cloud | Notes |\n|:--------|:----------------------|:-----------------|:------|\n| [COPY](https://redis.io/docs/latest/commands/copy) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active\\*</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active\\*</nobr></span> | For Active-Active or clustered databases, the source and destination keys must be in the same hash slot.<br /><br />\\*Not supported for stream consumer group info. |\n| [DEL](https://redis.io/docs/latest/commands/del) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [DUMP](https://redis.io/docs/latest/commands/dump) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [EXISTS](https://redis.io/docs/latest/commands/exists) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [EXPIRE](https://redis.io/docs/latest/commands/expire) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [EXPIREAT](https://redis.io/docs/latest/commands/expireat) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [EXPIRETIME](https://redis.io/docs/latest/commands/expiretime) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [KEYS](https://redis.io/docs/latest/commands/keys) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [MIGRATE](https://redis.io/docs/latest/commands/migrate) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [MOVE](https://redis.io/docs/latest/commands/move) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | Redis Software does not support shared databases due to potential negative performance impacts and blocks any related commands. |\n| [OBJECT ENCODING](https://redis.io/docs/latest/commands/object-encoding) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | For Active-Active databases, use `CRDT.DEBUG ENCODING` instead. |\n| [OBJECT FREQ](https://redis.io/docs/latest/commands/object-freq) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [OBJECT IDLETIME](https://redis.io/docs/latest/commands/object-idletime) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [OBJECT REFCOUNT](https://redis.io/docs/latest/commands/object-refcount) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [PERSIST](https://redis.io/docs/latest/commands/persist) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [PEXPIRE](https://redis.io/docs/latest/commands/pexpire) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [PEXPIREAT](https://redis.io/docs/latest/commands/pexpireat) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [PEXPIRETIME](https://redis.io/docs/latest/commands/pexpiretime) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [PTTL](https://redis.io/docs/latest/commands/pttl) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [RANDOMKEY](https://redis.io/docs/latest/commands/randomkey) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [RENAME](https://redis.io/docs/latest/commands/rename) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active\\*</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active\\*</nobr></span> | For Active-Active or clustered databases, the original key and new key must be in the same hash slot.<br /><br />\\*Not supported for stream consumer group info. |\n| [RENAMENX](https://redis.io/docs/latest/commands/renamenx) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | For Active-Active or clustered databases, the original key and new key must be in the same hash slot. |\n| [RESTORE](https://redis.io/docs/latest/commands/restore) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\">&#x274c; Active-Active\\*</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\">&#x274c; Active-Active\\*</nobr></span> | \\*Only supported for module keys. |\n| [SCAN](https://redis.io/docs/latest/commands/scan) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [SORT](https://redis.io/docs/latest/commands/sort) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [SORT_RO](https://redis.io/docs/latest/commands/sort_ro) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [TOUCH](https://redis.io/docs/latest/commands/touch) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [TTL](https://redis.io/docs/latest/commands/ttl) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [TYPE](https://redis.io/docs/latest/commands/type) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [UNLINK](https://redis.io/docs/latest/commands/unlink) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [WAIT](https://redis.io/docs/latest/commands/wait) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active\\*</nobr></span> | <span title=\"Not supported\">&#x274c; Standard**</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | \\*For Active-Active databases, `WAIT` commands are supported for primary and replica shard replication. You can contact support to enable `WAIT` for local replicas only. `WAIT` is not supported for cross-instance replication.<br></br>\\*\\*`WAIT` commands are supported on Redis Cloud Flexible subscriptions. |\n| [WAITAOF](https://redis.io/docs/latest/commands/waitaof) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/compatibility/commands","title":"Compatibility with Redis Open Source commands","url":"https://redis.io/docs/latest/operate/rs/references/compatibility/commands/","summary":"Redis Open Source commands compatible with Redis Software.","tags":["docs","operate","rs","rc"],"last_updated":"2026-09-24T10:14:21+01:00","children":[{"id":"operate/rs/references/compatibility/commands/cluster","summary":"Cluster management commands compatible with Redis Software.","title":"Cluster management commands compatibility","url":"https://redis.io/docs/latest/operate/rs/references/compatibility/commands/cluster/"},{"id":"operate/rs/references/compatibility/commands/connection","summary":"Connection management commands compatibility.","title":"Connection management commands compatibility","url":"https://redis.io/docs/latest/operate/rs/references/compatibility/commands/connection/"},{"id":"operate/rs/references/compatibility/commands/data-types","summary":"Data type commands compatibility (bitmaps, geospatial indices, hashes, HyperLogLogs, lists, sets, sorted sets, streams, strings).","title":"Data type commands compatibility","url":"https://redis.io/docs/latest/operate/rs/references/compatibility/commands/data-types/"},{"id":"operate/rs/references/compatibility/commands/generic","summary":"Generic key commands compatible with Redis Software.","title":"Key commands compatibility","url":"https://redis.io/docs/latest/operate/rs/references/compatibility/commands/generic/"},{"id":"operate/rs/references/compatibility/commands/pub-sub","summary":"Pub/sub commands compatibility.","title":"Pub/sub commands compatibility","url":"https://redis.io/docs/latest/operate/rs/references/compatibility/commands/pub-sub/"},{"id":"operate/rs/references/compatibility/commands/scripting","summary":"Scripting and function commands compatibility.","title":"Scripting commands compatibility","url":"https://redis.io/docs/latest/operate/rs/references/compatibility/commands/scripting/"},{"id":"operate/rs/references/compatibility/commands/server","summary":"Server management commands compatibility.","title":"Server management commands compatibility","url":"https://redis.io/docs/latest/operate/rs/references/compatibility/commands/server/"},{"id":"operate/rs/references/compatibility/commands/transactions","summary":"Transaction commands compatibility.","title":"Transaction commands compatibility","url":"https://redis.io/docs/latest/operate/rs/references/compatibility/commands/transactions/"}],"page_type":"content","content_hash":"1225e2d57db69fb339c36228cc28359411f960e899f1b229dcea33105ecfe85d","sections":[{"id":"content","title":"Content","role":"content","text":"Learn which Redis Open Source commands are compatible with Redis Software and [Redis Cloud](https://redis.io/docs/latest/operate/rc).\n\nSelect a command group for more details about compatibility with standard and Active-Active Redis Software.\n\n\n| Command group | Description |\n| --- | --- |\n| [Cluster management](https://redis.io/docs/latest/operate/rs/references/compatibility/commands/cluster/) | Cluster management commands compatible with Redis Software. |\n| [Connection management](https://redis.io/docs/latest/operate/rs/references/compatibility/commands/connection/) | Connection management commands compatibility. |\n| [Data types](https://redis.io/docs/latest/operate/rs/references/compatibility/commands/data-types/) | Data type commands compatibility (bitmaps, geospatial indices, hashes, HyperLogLogs, lists, sets, sorted sets, streams, strings). |\n| [Keys (generic)](https://redis.io/docs/latest/operate/rs/references/compatibility/commands/generic/) | Generic key commands compatible with Redis Software. |\n| [Pub/sub](https://redis.io/docs/latest/operate/rs/references/compatibility/commands/pub-sub/) | Pub/sub commands compatibility. |\n| [Scripting](https://redis.io/docs/latest/operate/rs/references/compatibility/commands/scripting/) | Scripting and function commands compatibility. |\n| [Server management](https://redis.io/docs/latest/operate/rs/references/compatibility/commands/server/) | Server management commands compatibility. |\n| [Transactions](https://redis.io/docs/latest/operate/rs/references/compatibility/commands/transactions/) | Transaction commands compatibility. |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/compatibility/commands/pub-sub","title":"Pub/sub commands compatibility","url":"https://redis.io/docs/latest/operate/rs/references/compatibility/commands/pub-sub/","summary":"Pub/sub commands compatibility.","tags":["docs","operate","rs","rc"],"last_updated":"2026-09-24T14:53:09+01:00","page_type":"content","content_hash":"591971d435f96b7b4d5b04a797ceefa3b5118022497f0fe7d5625aa10d23e392","sections":[{"id":"content","title":"Content","role":"content","text":"The following table shows which Redis Open Source [pub/sub commands](/content/commands?group=pubsub) are compatible with standard and Active-Active databases in Redis Software and Redis Cloud.\n\n| Command | Redis<br />Software | Redis<br />Cloud | Notes |\n|:--------|:----------------------|:-----------------|:------|\n| [PSUBSCRIBE](https://redis.io/docs/latest/commands/psubscribe) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [PUBLISH](https://redis.io/docs/latest/commands/publish) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [PUBSUB CHANNELS](https://redis.io/docs/latest/commands/pubsub-channels) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [PUBSUB NUMPAT](https://redis.io/docs/latest/commands/pubsub-numpat) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [PUBSUB NUMSUB](https://redis.io/docs/latest/commands/pubsub-numsub) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [PUBSUB SHARDCHANNELS](https://redis.io/docs/latest/commands/pubsub-shardchannels) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [PUBSUB SHARDNUMSUB](https://redis.io/docs/latest/commands/pubsub-shardnumsub) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [PUNSUBSCRIBE](https://redis.io/docs/latest/commands/punsubscribe) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [SPUBLISH](https://redis.io/docs/latest/commands/spublish) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [SSUBSCRIBE](https://redis.io/docs/latest/commands/ssubscribe) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [SUBSCRIBE](https://redis.io/docs/latest/commands/subscribe) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [SUNSUBSCRIBE](https://redis.io/docs/latest/commands/sunsubscribe) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [UNSUBSCRIBE](https://redis.io/docs/latest/commands/unsubscribe) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/compatibility/commands/scripting","title":"Scripting commands compatibility","url":"https://redis.io/docs/latest/operate/rs/references/compatibility/commands/scripting/","summary":"Scripting and function commands compatibility.","tags":["docs","operate","rs","rc"],"last_updated":"2026-09-24T14:53:09+01:00","page_type":"content","content_hash":"6978315c73e7ae1fd5fadc609bcce8fc5c32bef22b6489827fdda9fcf7c14872","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The following table shows which Redis Open Source [scripting and function commands](/content/commands?group=scripting) are compatible with standard and Active-Active databases in Redis Software and Redis Cloud."},{"id":"function-commands","title":"Function commands","role":"content","text":"| Command | Redis<br />Software | Redis<br />Cloud | Notes |\n|:--------|:----------------------|:-----------------|:------|\n| [FCALL](https://redis.io/docs/latest/commands/fcall) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [FCALL_RO](https://redis.io/docs/latest/commands/fcall_ro) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [FUNCTION DELETE](https://redis.io/docs/latest/commands/function-delete) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [FUNCTION DUMP](https://redis.io/docs/latest/commands/function-dump) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [FUNCTION FLUSH](https://redis.io/docs/latest/commands/function-flush) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [FUNCTION HELP](https://redis.io/docs/latest/commands/function-help) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [FUNCTION KILL](https://redis.io/docs/latest/commands/function-kill) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [FUNCTION LIST](https://redis.io/docs/latest/commands/function-list) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [FUNCTION LOAD](https://redis.io/docs/latest/commands/function-load) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [FUNCTION RESTORE](https://redis.io/docs/latest/commands/function-restore) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [FUNCTION STATS](https://redis.io/docs/latest/commands/function-stats) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"scripting-commands","title":"Scripting commands","role":"content","text":"| Command | Redis<br />Software | Redis<br />Cloud | Notes |\n|:--------|:----------------------|:-----------------|:------|\n| [EVAL](https://redis.io/docs/latest/commands/eval) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [EVAL_RO](https://redis.io/docs/latest/commands/eval_ro) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [EVALSHA](https://redis.io/docs/latest/commands/evalsha) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [EVALSHA_RO](https://redis.io/docs/latest/commands/evalsha_ro) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [SCRIPT DEBUG](https://redis.io/docs/latest/commands/script-debug) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [SCRIPT EXISTS](https://redis.io/docs/latest/commands/script-exists) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [SCRIPT FLUSH](https://redis.io/docs/latest/commands/script-flush) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [SCRIPT KILL](https://redis.io/docs/latest/commands/script-kill) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [SCRIPT LOAD](https://redis.io/docs/latest/commands/script-load) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/compatibility/commands/server","title":"Server management commands compatibility","url":"https://redis.io/docs/latest/operate/rs/references/compatibility/commands/server/","summary":"Server management commands compatibility.","tags":["docs","operate","rs","rc"],"last_updated":"2026-10-06T08:53:14-07:00","page_type":"content","content_hash":"4ecaffd0dc6f5db640338813af3e889d84f9fca9af69a46578536b78c29f0ae7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The following tables show which Redis Open Source [server management commands](/content/commands?group=server) are compatible with standard and Active-Active databases in Redis Software and Redis Cloud."},{"id":"access-control-commands","title":"Access control commands","role":"content","text":"Several access control list (ACL) commands are not available in Redis Software. Instead, you can manage access controls from the [Redis Software Cluster Manager UI](https://redis.io/docs/latest/operate/rs/security/access-control) and the [Redis Cloud console](https://redis.io/docs/latest/operate/rc/security/access-control/data-access-control/role-based-access-control).\n\n| <span style=\"min-width: 9em; display: table-cell\">Command</span> | Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:--------|:----------------------|:-----------------|:------|\n| [ACL CAT](https://redis.io/docs/latest/commands/acl-cat) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Not supported for [scripts](https://redis.io/docs/latest/develop/programmability). |\n| [ACL DELUSER](https://redis.io/docs/latest/commands/acl-deluser) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [ACL DRYRUN](https://redis.io/docs/latest/commands/acl-dryrun) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Might reply with \"unknown user\" for LDAP users even if `AUTH` succeeds. |\n| [ACL GENPASS](https://redis.io/docs/latest/commands/acl-genpass) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [ACL GETUSER](https://redis.io/docs/latest/commands/acl-getuser) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Not supported for [scripts](https://redis.io/docs/latest/develop/programmability). |\n| [ACL HELP](https://redis.io/docs/latest/commands/acl-help) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Not supported for [scripts](https://redis.io/docs/latest/develop/programmability). |\n| [ACL LIST](https://redis.io/docs/latest/commands/acl-list) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Not supported for [scripts](https://redis.io/docs/latest/develop/programmability). |\n| [ACL LOAD](https://redis.io/docs/latest/commands/acl-load) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [ACL LOG](https://redis.io/docs/latest/commands/acl-log) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [ACL SAVE](https://redis.io/docs/latest/commands/acl-save) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [ACL SETUSER](https://redis.io/docs/latest/commands/acl-setuser) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [ACL USERS](https://redis.io/docs/latest/commands/acl-users) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Not supported for [scripts](https://redis.io/docs/latest/develop/programmability). |\n| [ACL WHOAMI](https://redis.io/docs/latest/commands/acl-whoami) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Not supported for [scripts](https://redis.io/docs/latest/develop/programmability). |"},{"id":"configuration-commands","title":"Configuration commands","role":"configuration","text":"| <span style=\"min-width: 9em; display: table-cell\">Command</span> | Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:--------|:----------------------|:-----------------|:------|\n| [CONFIG GET](https://redis.io/docs/latest/commands/config-get) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | [Only supports a subset of configuration settings.](https://redis.io/docs/latest/operate/rs/references/compatibility/config-settings) |\n| [CONFIG RESETSTAT](https://redis.io/docs/latest/commands/config-resetstat) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [CONFIG REWRITE](https://redis.io/docs/latest/commands/config-rewrite) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [CONFIG SET](https://redis.io/docs/latest/commands/config-set) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | [Only supports a subset of configuration settings.](https://redis.io/docs/latest/operate/rs/references/compatibility/config-settings) |"},{"id":"general-server-commands","title":"General server commands","role":"content","text":"| <span style=\"min-width: 9em; display: table-cell\">Command</span> | Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:--------|:----------------------|:-----------------|:------|\n| [COMMAND](https://redis.io/docs/latest/commands/command) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [COMMAND COUNT](https://redis.io/docs/latest/commands/command-count) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [COMMAND DOCS](https://redis.io/docs/latest/commands/command-docs) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [COMMAND GETKEYS](https://redis.io/docs/latest/commands/command-getkeys) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [COMMAND GETKEYSANDFLAGS](https://redis.io/docs/latest/commands/command-getkeysandflags) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [COMMAND HELP](https://redis.io/docs/latest/commands/command-help) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [COMMAND INFO](https://redis.io/docs/latest/commands/command-info) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [COMMAND LIST](https://redis.io/docs/latest/commands/command-list) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [DEBUG](https://redis.io/docs/latest/commands/debug) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [FLUSHALL](https://redis.io/docs/latest/commands/flushall) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active\\*</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | \\*Can use the [Active-Active flush API request](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/crdbs/flush). |\n| [FLUSHDB](https://redis.io/docs/latest/commands/flushdb) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active\\*</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | \\*Can use the [Active-Active flush API request](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/crdbs/flush). |\n| [HOTKEYS GET](https://redis.io/docs/latest/commands/hotkeys-get) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [HOTKEYS HELP](https://redis.io/docs/latest/commands/hotkeys-help) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [HOTKEYS RESET](https://redis.io/docs/latest/commands/hotkeys-reset) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [HOTKEYS START](https://redis.io/docs/latest/commands/hotkeys-start) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [HOTKEYS STOP](https://redis.io/docs/latest/commands/hotkeys-stop) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [LOLWUT](https://redis.io/docs/latest/commands/lolwut) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [SHUTDOWN](https://redis.io/docs/latest/commands/shutdown) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [SWAPDB](https://redis.io/docs/latest/commands/swapdb) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [TIME](https://redis.io/docs/latest/commands/time) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"},{"id":"module-commands","title":"Module commands","role":"content","text":"For Redis Software, you can [manage Redis modules](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules) from the Cluster Manager UI or with [REST API requests](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/modules).\n\nRedis Cloud manages modules for you and lets you [enable modules](https://redis.io/docs/latest/operate/rc/databases/create-database#modules) when you create a database.\n\n| <span style=\"min-width: 9em; display: table-cell\">Command</span> | Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:--------|:----------------------|:-----------------|:------|\n| [MODULE HELP](https://redis.io/docs/latest/commands/module-help) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [MODULE LIST](https://redis.io/docs/latest/commands/module-list) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [MODULE LOAD](https://redis.io/docs/latest/commands/module-load) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [MODULE LOADEX](https://redis.io/docs/latest/commands/module-loadex) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [MODULE UNLOAD](https://redis.io/docs/latest/commands/module-unload) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"monitoring-commands","title":"Monitoring commands","role":"content","text":"Although Redis Software does not support certain monitoring commands, you can use the Cluster Manager UI to view Redis Software [metrics](https://redis.io/docs/latest/operate/rs/monitoring) and [logs](https://redis.io/docs/latest/operate/rs/clusters/logging) or the Redis Cloud console to view Redis Cloud [metrics](https://redis.io/docs/latest/operate/rc/databases/monitor-performance) and [logs](https://redis.io/docs/latest/operate/rc/logs-reports/system-logs).\n\n| <span style=\"min-width: 9em; display: table-cell\">Command</span> | Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:--------|:----------------------|:-----------------|:------|\n| [DBSIZE](https://redis.io/docs/latest/commands/dbsize) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [INFO](https://redis.io/docs/latest/commands/info) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | In Redis Software, `INFO` returns a different set of fields than Redis Open Source.<br />Not supported for [scripts](https://redis.io/docs/latest/develop/programmability). |\n| [LATENCY DOCTOR](https://redis.io/docs/latest/commands/latency-doctor) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [LATENCY GRAPH](https://redis.io/docs/latest/commands/latency-graph) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [LATENCY HELP](https://redis.io/docs/latest/commands/latency-help) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [LATENCY HISTOGRAM](https://redis.io/docs/latest/commands/latency-histogram) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [LATENCY HISTORY](https://redis.io/docs/latest/commands/latency-history) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [LATENCY LATEST](https://redis.io/docs/latest/commands/latency-latest) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [LATENCY RESET](https://redis.io/docs/latest/commands/latency-reset) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [MEMORY DOCTOR](https://redis.io/docs/latest/commands/memory-doctor) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [MEMORY HELP](https://redis.io/docs/latest/commands/memory-help) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Not supported for [scripts](https://redis.io/docs/latest/develop/programmability) in Redis versions earlier than 7. |\n| [MEMORY MALLOC-STATS](https://redis.io/docs/latest/commands/memory-malloc-stats) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [MEMORY PURGE](https://redis.io/docs/latest/commands/memory-purge) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [MEMORY STATS](https://redis.io/docs/latest/commands/memory-stats) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [MEMORY USAGE](https://redis.io/docs/latest/commands/memory-usage) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Not supported for [scripts](https://redis.io/docs/latest/develop/programmability) in Redis versions earlier than 7. |\n| [MONITOR](https://redis.io/docs/latest/commands/monitor) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [SLOWLOG GET](https://redis.io/docs/latest/commands/slowlog-get) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Not supported for [scripts](https://redis.io/docs/latest/develop/programmability). |\n| [SLOWLOG LEN](https://redis.io/docs/latest/commands/slowlog-len) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Not supported for [scripts](https://redis.io/docs/latest/develop/programmability). |\n| [SLOWLOG RESET](https://redis.io/docs/latest/commands/slowlog-reset) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Not supported for [scripts](https://redis.io/docs/latest/develop/programmability). |"},{"id":"persistence-commands","title":"Persistence commands","role":"content","text":"Data persistence and backup commands are not available in Redis Software. Instead, you can [manage data persistence](https://redis.io/docs/latest/operate/rs/databases/configure/database-persistence) and [backups](https://redis.io/docs/latest/operate/rs/databases/import-export/schedule-backups) from the Redis Software Cluster Manager UI and the [Redis Cloud console](https://redis.io/docs/latest/operate/rc/databases/view-edit-database#durability-section).\n\n| <span style=\"min-width: 9em; display: table-cell\">Command</span> | Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:--------|:----------------------|:-----------------|:------|\n| [BGREWRITEAOF](https://redis.io/docs/latest/commands/bgrewriteaof) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [BGSAVE](https://redis.io/docs/latest/commands/bgsave) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [LASTSAVE](https://redis.io/docs/latest/commands/lastsave) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [SAVE](https://redis.io/docs/latest/commands/save) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"},{"id":"replication-commands","title":"Replication commands","role":"content","text":"Redis Software automatically manages [replication](https://redis.io/docs/latest/operate/rs/databases/durability-ha/replication).\n\n| <span style=\"min-width: 9em; display: table-cell\">Command</span> | Redis<br />Software | Redis<br />Cloud | <span style=\"min-width: 9em; display: table-cell\">Notes</span> |\n|:--------|:----------------------|:-----------------|:------|\n| [FAILOVER](https://redis.io/docs/latest/commands/failover) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [MIGRATE](https://redis.io/docs/latest/commands/migrate) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [PSYNC](https://redis.io/docs/latest/commands/psync) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [REPLCONF](https://redis.io/docs/latest/commands/replconf) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [REPLICAOF](https://redis.io/docs/latest/commands/replicaof) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [RESTORE-ASKING](https://redis.io/docs/latest/commands/restore-asking) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [ROLE](https://redis.io/docs/latest/commands/role) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |\n| [SLAVEOF](https://redis.io/docs/latest/commands/slaveof) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | Deprecated as of Redis v5.0.0. |\n| [SYNC](https://redis.io/docs/latest/commands/sync) | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> |  |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/compatibility/commands/transactions","title":"Transaction commands compatibility","url":"https://redis.io/docs/latest/operate/rs/references/compatibility/commands/transactions/","summary":"Transaction commands compatibility.","tags":["docs","operate","rs","rc"],"last_updated":"2026-09-24T14:53:09+01:00","page_type":"content","content_hash":"3ec1b299d3d7d0b59e0d3e5519cc9919cd56297350039dcefaaa38c51a9ecc13","sections":[{"id":"content","title":"Content","role":"content","text":"The following table shows which Redis Open Source [transaction commands](/content/commands?group=transactions) are compatible with standard and Active-Active databases in Redis Software and Redis Cloud.\n\n| Command | Redis<br />Software | Redis<br />Cloud | Notes |\n|:--------|:----------------------|:-----------------|:------|\n| [DISCARD](https://redis.io/docs/latest/commands/discard) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [EXEC](https://redis.io/docs/latest/commands/exec) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [MULTI](https://redis.io/docs/latest/commands/multi) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [UNWATCH](https://redis.io/docs/latest/commands/unwatch) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| [WATCH](https://redis.io/docs/latest/commands/watch) | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/compatibility/config-settings","title":"Compatibility with Redis Open Source configuration settings","url":"https://redis.io/docs/latest/operate/rs/references/compatibility/config-settings/","summary":"Redis Open Source configuration settings supported by Redis Software.","tags":["docs","operate","rs","rc"],"last_updated":"2026-09-24T10:14:21+01:00","page_type":"content","content_hash":"a1883dbc409e450fc4618f987198daf783d6c95f7b3190dce1c6a6ec17e08007","sections":[{"id":"content","title":"Content","role":"content","text":"Redis Software and [Redis Cloud](https://redis.io/docs/latest/operate/rc) only support a subset of [Redis Open Source configuration settings](https://redis.io/docs/latest/operate/oss_and_stack/management/config). Using [`CONFIG GET`](https://redis.io/docs/latest/commands/config-get) or [`CONFIG SET`](https://redis.io/docs/latest/commands/config-set) with unsupported configuration settings returns an error.\n\n| Setting | Redis<br />Software | Redis<br />Cloud | Notes |\n|:--------|:----------------------|:-----------------|:------|\n| activerehashing | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| busy-reply-threshold | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | Value must be between 0 and 60000 milliseconds. |\n| hash-max-listpack-entries | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| hash-max-listpack-value | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| hash-max-ziplist-entries | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| hash-max-ziplist-value | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| hll-sparse-max-bytes | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| list-compress-depth | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| list-max-listpack-size | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| list-max-ziplist-size | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| lua-time-limit | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | Value must be between 0 and 60000 milliseconds. |\n| maxmemory-samples | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | |\n| notify-keyspace-events | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| set-max-intset-entries | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| slowlog-log-slower-than | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Value must be larger than 1000 microseconds. |\n| slowlog-max-len | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | Value must be between 128 and 1024. |\n| stream-node-max-bytes | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| stream-node-max-entries | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| tracking-table-max-keys | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | <span title=\"Not supported\">&#x274c; Standard</span><br /><span title=\"Not supported\"><nobr>&#x274c; Active-Active</nobr></span> | For Redis Software, use an [update database configuration](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#put-bdbs) REST API request or [`rladmin tune db`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-db) to set `tracking_table_max_keys` instead. |\n| zset-max-listpack-entries | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| zset-max-listpack-value | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| zset-max-ziplist-entries | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |\n| zset-max-ziplist-value | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> | <span title=\"Supported\">&#x2705; Standard</span><br /><span title=\"Supported\"><nobr>&#x2705; Active-Active</nobr></span> |  |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/compatibility","title":"Redis Software compatibility with Redis Open Source","url":"https://redis.io/docs/latest/operate/rs/references/compatibility/","summary":"Redis Software compatibility with Redis Open Source.","tags":["docs","operate","rs","rc"],"last_updated":"2026-09-24T10:14:21+01:00","children":[{"id":"operate/rs/references/compatibility/commands","summary":"Redis Open Source commands compatible with Redis Software.","title":"Compatibility with Redis Open Source commands","url":"https://redis.io/docs/latest/operate/rs/references/compatibility/commands/"},{"id":"operate/rs/references/compatibility/config-settings","summary":"Redis Open Source configuration settings supported by Redis Software.","title":"Compatibility with Redis Open Source configuration settings","url":"https://redis.io/docs/latest/operate/rs/references/compatibility/config-settings/"},{"id":"operate/rs/references/compatibility/client-side-caching","summary":"Redis Software and Redis Cloud compatibility with client-side caching.","title":"Client-side caching compatibility with Redis Software and Redis Cloud","url":"https://redis.io/docs/latest/operate/rs/references/compatibility/client-side-caching/"},{"id":"operate/rs/references/compatibility/resp","summary":"Redis Software supports RESP2 and RESP3.","title":"RESP compatibility with Redis Software","url":"https://redis.io/docs/latest/operate/rs/references/compatibility/resp/"}],"page_type":"content","content_hash":"474a72da2250461a66259a09de56cb4f3db3a5b455eb018e326b6162a3381e58","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Both Redis Software and [Redis Cloud](https://redis.io/docs/latest/operate/rc) are compatible with Redis Open Source. \n\nIf you're planning a move from a self-managed Redis Open Source deployment to Redis Software, see [Move from Redis Open Source to Redis Software](https://redis.io/docs/latest/operate/rs/installing-upgrading/move-from-open-source) for how configuration and deployment differ.\n\nRedis contributes extensively to the Redis project and uses it inside of Redis Software and Redis Cloud. As a rule, we adhere to the project's specifications and update both products with the latest version of Redis."},{"id":"redis-commands","title":"Redis commands","role":"content","text":"See [Compatibility with Redis commands](https://redis.io/docs/latest/operate/rs/references/compatibility/commands) to learn which Redis commands are compatible with Redis Software and Redis Cloud."},{"id":"configuration-settings","title":"Configuration settings","role":"configuration","text":"[Compatibility with Redis configuration settings](https://redis.io/docs/latest/operate/rs/references/compatibility/config-settings) lists the Redis configuration settings supported by Redis Software and Redis Cloud."},{"id":"redis-clients","title":"Redis clients","role":"content","text":"You can use any standard [Redis client](https://redis.io/docs/latest/develop/clients) with Redis Software and Redis Cloud."},{"id":"resp-compatibility","title":"RESP compatibility","role":"content","text":"Redis Software and Redis Cloud support RESP2 and RESP3. See [RESP compatibility with Redis Software](https://redis.io/docs/latest/operate/rs/references/compatibility/resp) for more information."},{"id":"client-side-caching-compatibility","title":"Client-side caching compatibility","role":"content","text":"Redis Software and Redis Cloud support [client-side caching](https://redis.io/docs/latest/develop/clients/client-side-caching) for databases with Redis versions 7.4 or later. See [Client-side caching compatibility with Redis Software and Redis Cloud](https://redis.io/docs/latest/operate/rs/references/compatibility/client-side-caching) for more information about compatibility and configuration options."},{"id":"compatibility-with-open-source-redis-cluster-api","title":"Compatibility with open source Redis Cluster API","role":"compatibility","text":"Redis Software supports [Redis OSS Cluster API](https://redis.io/docs/latest/operate/rs/clusters/optimize/oss-cluster-api) if it is enabled for a database. For more information, see [Enable OSS Cluster API](https://redis.io/docs/latest/operate/rs/databases/configure/oss-cluster-api)."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/compatibility/resp","title":"RESP compatibility with Redis Software","url":"https://redis.io/docs/latest/operate/rs/references/compatibility/resp/","summary":"Redis Software supports RESP2 and RESP3.","tags":["docs","operate","rs"],"last_updated":"2026-10-02T16:06:07+01:00","page_type":"content","content_hash":"9a9c1afcebb732114bee24cad1aef8992711866d89d8c3f0fe9e0be34cc17139","sections":[{"id":"overview","title":"Overview","role":"overview","text":"RESP (Redis Serialization Protocol) is the protocol that clients use to communicate with Redis databases. See the [RESP protocol specification](https://redis.io/docs/latest/develop/reference/protocol-spec) for more information."},{"id":"supported-resp-versions","title":"Supported RESP versions","role":"compatibility","text":"- RESP2 is supported by all Redis Software versions.\n\n- RESP3 is supported by Redis Software 7.2 and later.\n\n> [!NOTE]\n> Redis Software versions that support RESP3 continue to support RESP2."},{"id":"enable-resp3","title":"Enable RESP3 for a database","role":"content","text":"To use RESP3 with a Redis Software database:\n\n1. Upgrade Redis servers to version 7.2 or later.\n\n    For Active-Active and Replica Of databases:\n \n    1. Upgrade all participating clusters to Redis Software version 7.2.x or later.\n \n    1. Upgrade all databases to version 7.x or later.\n\n1. Enable RESP3 support for your database (`enabled` by default):\n\n    - [`rladmin tune db`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-db):\n\n        [code example]\n\n        You can use the database name in place of `db:<ID>` in the preceding command.\n\n    - [Update database configuration](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#put-bdbs) REST API request:\n\n        [code example]\n\n ## Deactivate RESP3 for a database {#deactivate-resp3}\n\n To deactivate RESP3 support for a database:\n\n- [`rladmin tune db`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-db):\n\n     [code example]\n\n    You can use the database name in place of `db:<ID>` in the preceding command.\n\n- [Update database configuration](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#put-bdbs) REST API request:\n\n    [code example]\n\n When RESP3 is deactivated, connected clients that use RESP3 are disconnected from the database.\n\n> [!NOTE]\n> You cannot use sharded pub/sub if you deactivate RESP3 support. When RESP3 is enabled, you can use sharded pub/sub with either RESP2 or RESP3."},{"id":"change-default-resp3-option","title":"Change default RESP3 option","role":"content","text":"The cluster-wide option `resp3_default` determines the default value of the `resp3` option, which enables or deactivates RESP3 for a database, upon upgrading a database to version 7.2. `resp3_default` is set to `enabled` by default.\n\nTo change `resp3_default` to `disabled`, use one of the following methods:\n\n- Cluster Manager UI:\n\n    1. On the **Databases** screen, select ![Toggle actions button](https://redis.io/docs/latest/images/rs/buttons/button-toggle-actions-vertical.png#no-click) to open a list of additional actions.\n\n    1. Select **Upgrade configuration**.\n\n    1. For **RESP3 support**, select **Disable**.\n\n    1. Click **Save**.\n\n- [`rladmin tune cluster`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-cluster)\n\n    [code example]\n\n- [Update cluster policy](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/policy#put-cluster-policy) REST API request:\n\n    [code example]"},{"id":"client-prerequisites-for-redis-72-upgrade","title":"Client prerequisites for Redis 7.2 upgrade","role":"content","text":"The Redis clients [Go-Redis](https://redis.uptrace.dev/) version 9 and [Lettuce](https://redis.github.io/lettuce/) versions 6 and later use RESP3 by default. If you use either client to run Redis Stack commands, you should set the client's protocol version to RESP2 before upgrading your database to Redis version 7.2 to prevent potential application issues due to RESP3 breaking changes."},{"id":"go-redis","title":"Go-Redis","role":"content","text":"For applications using Go-Redis v9.0.5 or later, set the protocol version to RESP2:\n\n[code example]"},{"id":"lettuce","title":"Lettuce","role":"content","text":"To set the protocol version to RESP2 with Lettuce v6 or later:\n\n[code example]\n\nIf you are using [LettuceMod](https://github.com/redis-developer/lettucemod/), you need to upgrade to [v3.6.0](https://github.com/redis-developer/lettucemod/releases/tag/v3.6.0)."}],"examples":[{"id":"enable-resp3-ex0","language":"sh","code":"rladmin tune db db:<ID> resp3 enabled","section_id":"enable-resp3"},{"id":"enable-resp3-ex1","language":"sh","code":"PUT /v1/bdbs/<uid> \n        { \"resp3\": true }","section_id":"enable-resp3"},{"id":"enable-resp3-ex2","language":"sh","code":"rladmin tune db db:<ID> resp3 disabled","section_id":"enable-resp3"},{"id":"enable-resp3-ex3","language":"sh","code":"PUT /v1/bdbs/<uid> \n    { \"resp3\": false }","section_id":"enable-resp3"},{"id":"change-default-resp3-option-ex0","language":"sh","code":"rladmin tune cluster resp3_default disabled","section_id":"change-default-resp3-option"},{"id":"change-default-resp3-option-ex1","language":"sh","code":"PUT /v1/cluster/policy \n    { \"resp3_default\": false }","section_id":"change-default-resp3-option"},{"id":"go-redis-ex0","language":"go","code":"client := redis.NewClient(&redis.Options{\n    Addr:     \"<database_endpoint>\",\n    Protocol: 2, // Pin the protocol version\n})","section_id":"go-redis"},{"id":"lettuce-ex0","language":"java","code":"import io.lettuce.core.*;\nimport io.lettuce.core.api.*;\nimport io.lettuce.core.protocol.ProtocolVersion;\n\n// ...\nRedisClient client = RedisClient.create(\"<database_endpoint>\");\nclient.setOptions(ClientOptions.builder()\n        .protocolVersion(ProtocolVersion.RESP2) // Pin the protocol version \t\n        .build());\n// ...","section_id":"lettuce"}]}
{"schema_version":2,"id":"operate/rs/references","title":"Reference","url":"https://redis.io/docs/latest/operate/rs/references/","summary":"","tags":["docs","operate","rs"],"last_updated":"2024-04-02T12:43:54-05:00","children":[{"id":"operate/rs/references/supported-platforms","summary":"Redis Software is supported on several operating systems, cloud environments, and virtual environments.","title":"Supported platforms","url":"https://redis.io/docs/latest/operate/rs/references/supported-platforms/"},{"id":"operate/rs/references/upgrade-paths","summary":"Supported paths to upgrade a Redis Software cluster.","title":"Supported upgrade paths for Redis Software","url":"https://redis.io/docs/latest/operate/rs/references/upgrade-paths/"},{"id":"operate/rs/references/client_references","summary":"Redis client libraries allow you to connect to Redis instances from within your application. This section provides an overview of several recommended Redis clients for popular programming and scripting languages.","title":"Develop with Redis clients","url":"https://redis.io/docs/latest/operate/rs/references/client_references/"},{"id":"operate/rs/references/alerts","summary":"Documents the alerts that are tracked with Redis Software.","title":"Alerts","url":"https://redis.io/docs/latest/operate/rs/references/alerts/"},{"id":"operate/rs/references/memtier-benchmark","summary":"","title":"Benchmark an Auto Tiering enabled database","url":"https://redis.io/docs/latest/operate/rs/references/memtier-benchmark/"},{"id":"operate/rs/references/cli-utilities","summary":"Reference for Redis Software command-line utilities, including rladmin, redis-cli, crdb-cli, and rlcheck.","title":"Command-line utilities","url":"https://redis.io/docs/latest/operate/rs/references/cli-utilities/"},{"id":"operate/rs/references/metrics","summary":"Documents the metrics that are tracked with Redis Software.","title":"Real-time metrics","url":"https://redis.io/docs/latest/operate/rs/references/metrics/"},{"id":"operate/rs/references/compatibility","summary":"Redis Software compatibility with Redis Open Source.","title":"Redis Software compatibility with Redis Open Source","url":"https://redis.io/docs/latest/operate/rs/references/compatibility/"},{"id":"operate/rs/references/rest-api","summary":"Documents the REST API available to Redis Software deployments.","title":"REST API","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/"},{"id":"operate/rs/references/terminology","summary":"Explains terms used in Redis Software and its docs.","title":"Terminology in Redis Software","url":"https://redis.io/docs/latest/operate/rs/references/terminology/"}],"page_type":"index","sections":[],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/memtier-benchmark","title":"Benchmark an Auto Tiering enabled database","url":"https://redis.io/docs/latest/operate/rs/references/memtier-benchmark/","summary":"","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:21+01:00","page_type":"content","content_hash":"2c450be983887c254381122adec7567237f59fc3c28794feff30b1b03f38b3fb","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Auto Tiering on Redis Software lets you use cost-effective Flash memory as a RAM extension for your database.\n\nBut what does the performance look like as compared to a memory-only database, one stored solely in RAM?\n\nThese scenarios use the `memtier_benchmark` utility to evaluate the performance of a Redis Software deployment, including the trial version.  \n\nThe `memtier_benchmark` utility is located in `/opt/redislabs/bin/` of Redis Software deployments.  To test performance for cloud provider deployments, see the [memtier-benchmark GitHub project](https://github.com/RedisLabs/memtier_benchmark).\n\nFor additional, such as assistance with larger clusters, [contact support](https://redislabs.com/company/support/)."},{"id":"benchmark-and-performance-test-considerations","title":"Benchmark and performance test considerations","role":"performance","text":"These tests assume you're using a trial version of Redis Software and want to test the performance of a Auto Tiering enabled database in the following scenarios:\n\n- Without replication: Four (4) master shards\n- With replication: Two (2) primary and two replica shards\n\nWith the trial version of Redis Software you can create a cluster of up to four shards using a combination of database configurations, including:\n\n- Four databases, each with a single master shard\n- Two highly available databases with replication enabled (each database has one master shard and one replica shard)\n- One non-replicated clustered database with four master shards\n- One highly available and clustered database with two master shards and two replica shards"},{"id":"test-environment-and-cluster-setup","title":"Test environment and cluster setup","role":"content","text":"For the test environment, you need to:\n\n1. Create a cluster with three nodes.\n1. Prepare the flash memory.\n1. Configure the load generation tool."},{"id":"creating-a-threenode-rs-cluster","title":"Creating a three-node cluster","role":"content","text":"This performance test requires a three-node cluster.\n\nYou can run all of these tests on Amazon AWS with these hosts:\n\n- 2 x i3.2xlarge (8 vCPU, 61 GiB RAM, up to 10GBit, 1.9TB NMVe SSD)\n\n    These nodes serve RoF data\n\n- 1 x m4.large, which acts as a quorum node\n\nTo learn how to install Redis Software and set up a cluster, see:\n\n- [Redis Software quickstart](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/redis-enterprise-software-quickstart) for a test installation\n- [Install and upgrade](https://redis.io/docs/latest/operate/rs/installing-upgrading) for a production installation\n\nThese tests use a quorum node to reduce AWS EC2 instance use while maintaining the three nodes required to support a quorum node in case of node failure.  Quorum nodes can be on less powerful instances because they do not have shards or support traffic.\n\nAs of this writing, i3.2xlarge instances are required because they support NVMe SSDs, which are required to support RoF.  Auto Tiering requires Flash-enabled storage, such as NVMe SSDs.\n\nFor best results, compare performance of a Flash-enabled deployment to the performance in a RAM-only environment, such as a strictly on-premises deployment."},{"id":"prepare-the-flash-memory","title":"Prepare the flash memory","role":"content","text":"After you install RS on the nodes,\nthe flash memory attached to the i3.2xlarge instances must be prepared and formatted with the `/opt/redislabs/sbin/prepare_flash.sh` script."},{"id":"set-up-the-load-generation-tool","title":"Set up the load generation tool","role":"content","text":"The memtier_benchmark load generator tool generates the load on the RoF databases.\nTo use this tool, install RS on a dedicated instance that is not part of the RS cluster\nbut is in the same region/zone/subnet of your cluster.\nWe recommend that you use a relatively powerful instance to avoid bottlenecks at the load generation tool itself.\n\nFor these tests, the load generation host uses a c4.8xlarge instance type."},{"id":"database-configuration-parameters","title":"Database configuration parameters","role":"content","text":""},{"id":"create-a-auto-tiering-test-database","title":"Create a Auto Tiering test database","role":"content","text":"You can use the Redis Software Cluster Manager UI to create a test database.\nWe recommend that you use a separate database for each test case with these requirements:\n\n|  **Parameter** | **With replication** | **Without replication** | **Description** |\n|  ------ | ------ | ------ | ------ |\n|  Name | test-1 | test-2 | The name of the test database |\n|  Memory limit | 100 GB | 100 GB | The memory limit refers to RAM+Flash, aggregated across all the shards of the database, including master and replica shards. |\n|  RAM limit | 0.3 | 0.3 | RoF always keeps the Redis keys and Redis dictionary in RAM and additional RAM is required for storing hot values. For the purpose of these tests 30% RAM was calculated as an optimal value. |\n|  Replication | Enabled | Disabled | A database with no replication has only master shards. A database with replication has master and replica shards. |\n|  Data persistence | None | None | No data persistence is needed for these tests. |\n|  Database clustering | Enabled | Enabled | A clustered database consists of multiple shards. |\n|  Number of (master) shards | 2 | 4 | Shards are distributed as follows:<br/>- With replication: One master shard and one replica shard on each node<br/>- Without replication: Two master shards on each node |\n|  Other parameters | Default | Default | Keep the default values for the other configuration parameters. |"},{"id":"data-population","title":"Data population","role":"content","text":""},{"id":"populate-the-benchmark-dataset","title":"Populate the benchmark dataset","role":"content","text":"The memtier_benchmark load generation tool populates the database.\nTo populate the database with N items of 500 Bytes each in size, on the load generation instance run:\n\n[code example]\n\nSet up a test database:\n\n|  **Parameter** | **Description** |\n|  ------ | ------ |\n|  Database host<br/>(-s) | The fully qualified name of the endpoint or the IP shown in the RS database configuration |\n|  Database port<br/>(-p) | The endpoint port shown in your database configuration |\n|  Number of items<br/>(–key-maximum) | With replication: 75 Million<br/>Without replication: 150 Million |\n|  Item size<br/>(-d) | 500 Bytes |"},{"id":"centralize-the-keyspace","title":"Centralize the keyspace","role":"content","text":""},{"id":"centralize-with-repl","title":"With replication","role":"content","text":"To create roughly 20.5 million items in RAM for your highly available clustered database with 75 million items, run:\n\n[code example]\n\nTo verify the database values, use **Values in RAM** metric, which is available from the **Metrics** tab of your database in the Cluster Manager UI."},{"id":"centralize-wo-repl","title":"Without replication","role":"content","text":"To create 41 million items in RAM without replication enabled and 150 million items, run:\n\n[code example]"},{"id":"test-runs","title":"Test runs","role":"content","text":""},{"id":"generate-load","title":"Generate load","role":"content","text":"#### With replication {#generate-with-repl}\n\nWe recommend that you do a dry run and double check the RAM Hit Ratio on the **Metrics** screen in the Cluster Manager UI before you write down the test results.\n\nTo test RoF with an 85% RAM Hit Ratio, run:\n\n[code example]\n\n#### Without replication {#generate-wo-repl}\n\nHere is the command for 150 million items:\n\n[code example]\n\nWhere:\n\n|  **Parameter** | **Description** |\n|------------|-----------------|\n|  Access pattern (--key-pattern) and standard deviation (--key-stddev) | Controls the RAM Hit ratio after the centralization process is complete |\n|  Number of threads (-t and -c)\\ | Controls how many connections are opened to the database, whereby the number of connections is the number of threads multiplied by the number of connections per thread (-t) and number of clients per thread (-c) |\n|  Pipelining (--pipeline)\\ | Pipelining allows you to send multiple requests without waiting for each individual response (-t) and number of clients per thread (-c) |\n|  Read\\write ratio (--ratio)\\ | A value of 1:1 means that you have the same number of write operations as read operations (-t) and number of clients per thread (-c) |"},{"id":"test-results","title":"Test results","role":"content","text":""},{"id":"monitor-the-test-results","title":"Monitor the test results","role":"content","text":"You can either monitor the results in the **Metrics** tab of the Cluster Manager UI or with the `memtier_benchmark` output.  However, be aware that:\n\n- The memtier_benchmark results include the network latency between the load generator instance and the cluster instances.\n\n- The metrics shown in the Cluster Manager UI do _not_ include network latency."},{"id":"expected-results","title":"Expected results","role":"content","text":"You should expect to see an average throughput of:\n\n- Around 160,000 ops/sec when testing without replication (i.e. Four master shards)\n- Around 115,000 ops/sec when testing with enabled replication (i.e. 2 master and 2 replica shards)\n\nIn both cases, the average latency should be below one millisecond."}],"examples":[{"id":"populate-the-benchmark-dataset-ex0","language":"sh","code":"$ memtier_benchmark -s $DB_HOST -p $DB_PORT --hide-histogram\n--key-maximum=$N -n allkeys -d 500 --key-pattern=P:P --ratio=1:0","section_id":"populate-the-benchmark-dataset"},{"id":"centralize-with-repl-ex0","language":"sh","code":"$ memtier_benchmark  -s $DB_HOST -p $DB_PORT --hide-histogram\n--key-minimum=27250000 --key-maximum=47750000 -n allkeys\n--key-pattern=P:P --ratio=0:1","section_id":"centralize-with-repl"},{"id":"centralize-wo-repl-ex0","language":"sh","code":"$ memtier_benchmark  -s $DB_HOST -p $DB_PORT --hide-histogram\n--key-minimum=54500000 --key-maximum=95500000 -n allkeys\n--key-pattern=P:P --ratio=0:1","section_id":"centralize-wo-repl"},{"id":"generate-load-ex0","language":"sh","code":"$ memtier_benchmark -s $DB_HOST -p $DB_PORT --pipeline=11 -c 20 -t 1\n-d 500 --key-maximum=75000000 --key-pattern=G:G --key-stddev=5125000\n--ratio=1:1 --distinct-client-seed --randomize --test-time=600\n--run-count=1 --out-file=test.out","section_id":"generate-load"},{"id":"generate-load-ex1","language":"sh","code":"$ memtier_benchmark -s $DB_HOST -p $DB_PORT --pipeline=24 -c 20 -t 1\n-d 500 --key-maximum=150000000 --key-pattern=G:G --key-stddev=10250000\n--ratio=1:1 --distinct-client-seed --randomize --test-time=600\n--run-count=1 --out-file=test.out","section_id":"generate-load"}]}
{"schema_version":2,"id":"operate/rs/references/metrics/auto-tiering","title":"Redis Flex and Auto Tiering Metrics","url":"https://redis.io/docs/latest/operate/rs/references/metrics/auto-tiering/","summary":"","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"9d7ce05000600fdc7f14001dc8f8dad69568c5ae56147d0f042b4bac38b5610c","sections":[{"id":"content","title":"Content","role":"content","text":"The following additional metrics for [Redis Flex and Auto Tiering ](https://redis.io/docs/latest/operate/rs/databases/flash) databases are available in the Redis Software Cluster Manager UI.\n\n![The database metrics page.](https://redis.io/docs/latest/images/rs/screenshots/metrics/flash-db-metrics.png)\n\n#### % Values in RAM\n\nPercent of keys whose values are stored in RAM.\n\nA low percentage alert means most of the RAM is used for holding keys and not much RAM is available for values. This can be due to a high number of small keys or a few large keys. Inserting more keys might cause the database to run out of memory.\n\nIf the percent of values in RAM is low for a subset of the database's shards, it might also indicate an unbalanced database.\n\n**Components measured**: Database and Shard\n\n#### Values in flash\n\nNumber of keys with values stored in flash, not including [replication](https://redis.io/docs/latest/operate/rs/databases/durability-ha/replication).\n\n**Components measured**: Database and Shard\n\n#### Values in RAM\n\nNumber of keys with values stored in RAM, not including [replication](https://redis.io/docs/latest/operate/rs/databases/durability-ha/replication).\n\n**Components measured**: Database and Shard \n\n#### Flash key-value operations\n\nNumber of operations on flash key values (read + write + del) per second.\n\n**Components measured**: Node\n\n#### Flash bytes/sec\n\nNumber of total bytes read and written per second on flash memory.\n\n**Components measured**: Cluster, Node, Database, and Shard\n\n#### Flash I/O operations/sec\n\nNumber of input/output operations per second on the flash storage device.\n\n**Components measured**: Cluster and Node\n\n#### RAM:Flash access ratio\n\nRatio between logical Redis key value operations and actual flash key value operations.\n\n**Components measured**: Database and Shard\n\n#### RAM hit ratio\n\nRatio of requests processed directly from RAM to total number of requests processed.\n\n**Components measured**: Database and Shard\n\n#### Used flash\n\nTotal amount of memory used to store values in flash.\n\n**Components measured**: Database and Shard\n\n#### Free flash\n\nAmount of free space on flash storage. \n\n**Components measured**: Cluster and Node\n\n#### Flash fragmentation\n\nRatio between the used logical flash memory and the physical flash memory that is used.\n\n**Components measured**: Database and Shard\n\n#### Used RAM\n\nTotal size of data stored in RAM, including keys, values, overheads, and [replication](https://redis.io/docs/latest/operate/rs/databases/durability-ha/replication) (if enabled).\n\n**Components measured**: Database and Shard\n\n#### RAM dataset overhead\n\nPercentage of the [RAM limit](#ram-limit) that is used for anything other than values, such as key names, dictionaries, and other overheads.\n\n**Components measured**: Database and Shard\n\n#### RAM limit\n\nMaximum amount of RAM that can be used in bytes.\n\n**Components measured**: Database\n\n#### RAM usage\n\nPercentage of the [RAM limit](#ram-limit) used.\n\n**Components measured**: Database\n\n#### Storage engine usage\n\nTotal count of shards used, filtered by the sorage engine (Speedb / RockSB) per given database.\n\n**Components measured**: Database, Shards\n\n\n\n#### Calculated metrics\n\nThese RoF statistics can be calculated from other metrics.\n\n- RoF average key size with overhead\n\n    ([ram_dataset_overhead](#ram-dataset-overhead) * [used_ram](#used-ram))\n                    / ([total_keys](https://redis.io/docs/latest/operate/rs/references/metrics/database-operations#total-keys) * 2)\n\n- RoF average value size in RAM\n\n    ((1 - [ram_dataset_overhead](#ram-dataset-overhead)) * [used_ram](#used-ram)) / ([values_in_ram](#values-in-ram) * 2)\n\n- RoF average value size in flash\n\n    [used_flash](#used-flash) / [values_in_flash](#values-in-flash)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/metrics/database-operations","title":"Database operations metrics","url":"https://redis.io/docs/latest/operate/rs/references/metrics/database-operations/","summary":"","tags":["docs","operate","rs","rc"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"1a8dbe68376488b7009b7594dfa65b9e8365b5624e0950a9f4c7929b73f38298","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The following metrics are available in the Redis Software Cluster Manager UI.\n\n![The database metrics page.](https://redis.io/docs/latest/images/rs/screenshots/metrics/db-metrics.png)"},{"id":"evicted-objectssec","title":"Evicted objects/sec","role":"content","text":"Number of objects evicted from the database per second.\n\nObjects are evicted from the database according to the [eviction policy](https://redis.io/docs/latest/operate/rs/databases/memory-performance/eviction-policy).\n\nObject information is not measured during [shard migration](https://redis.io/docs/latest/operate/rs/databases/configure/replica-ha).\n\n**Components measured**: Database and Shard"},{"id":"expired-objectssec","title":"Expired objects/sec","role":"content","text":"Number of expired objects per second.\n\nObject information is not measured during [shard migration](https://redis.io/docs/latest/operate/rs/databases/configure/replica-ha).\n\n**Components measured**: Database and Shard"},{"id":"hit-ratio","title":"Hit ratio","role":"content","text":"Ratio of the number of operations on existing keys out of the total number of operations. \n\n**Components measured**: Database and Shard"},{"id":"read-missessec","title":"Read misses/sec","role":"content","text":"The number of [read operations](#readssec) per second on keys that do not exist.\n\nRead misses are not measured during [shard migration](https://redis.io/docs/latest/operate/rs/databases/configure/replica-ha).\n\n**Components measured**: Database"},{"id":"write-missessec","title":"Write misses/sec","role":"content","text":"Number of [write operations](#writessec) per second on keys that do not exist.\n\nWrite misses are not measured during [shard migration](https://redis.io/docs/latest/operate/rs/databases/configure/replica-ha).\n\n**Components measured**: Database and Shard"},{"id":"latency","title":"Latency","role":"content","text":"The total amount of time between sending a Redis operation and receiving a response from the database.\n\nThe graph shows average, minimum, maximum, and last latency values for all latency metrics.\n\n**Components measured**: Database"},{"id":"reads-latency","title":"Reads latency","role":"content","text":"[Latency](#latency) of [read operations](#readssec).\n\n**Components measured**: Database"},{"id":"writes-latency","title":"Writes latency","role":"content","text":"[Latency](#latency) per [write operation](#writessec).\n\n**Components measured**: Database"},{"id":"other-commands-latency","title":"Other commands latency","role":"content","text":"[Latency](#latency) of [other operations](#other-commandssec).\n\n**Components measured**: Database"},{"id":"opssec","title":"Ops/sec","role":"content","text":"Number of total operations per second, which includes [read operations](#readssec), [write operations](#writessec), and [other operations](#other-commandssec).\n\n**Components measured**: Cluster, Node, Database, and Shard"},{"id":"readssec","title":"Reads/sec","role":"content","text":"Number of total read operations per second.\n\nTo find out which commands are read operations, run the following command with [`redis-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/redis-cli):\n\n[code example]\n\n**Components measured**: Database"},{"id":"writessec","title":"Writes/sec","role":"content","text":"Number of total write operations per second.\n\nTo find out which commands are write operations, run the following command with [`redis-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/redis-cli):\n\n[code example]\n\n**Components measured**: Database\n\n#### Pending writes min\n\nMinimum number of write operations queued per [Active-Active](https://redis.io/docs/latest/operate/rs/databases/active-active) replica database. \n\n#### Pending writes max\n\nMaximum number of write operations queued per [Active-Active](https://redis.io/docs/latest/operate/rs/databases/active-active) replica database."},{"id":"other-commandssec","title":"Other commands/sec","role":"content","text":"Number of operations per second that are not [read operations](#readssec) or [write operations](#writessec).\n\nExamples of other operations include [PING](https://redis.io/docs/latest/commands/ping), [AUTH](https://redis.io/docs/latest/commands/auth), and [INFO](https://redis.io/docs/latest/commands/info).\n\n**Components measured**: Database"},{"id":"total-keys","title":"Total keys","role":"content","text":"Total number of keys in the dataset.\n \nDoes not include replicated keys, even if [replication](https://redis.io/docs/latest/operate/rs/databases/durability-ha/replication) is enabled.\n\nTotal keys is not measured during [shard migration](https://redis.io/docs/latest/operate/rs/databases/configure/replica-ha). \n\n**Components measured**: Database"}],"examples":[{"id":"readssec-ex0","language":"sh","code":"ACL CAT read","section_id":"readssec"},{"id":"writessec-ex0","language":"sh","code":"ACL CAT write","section_id":"writessec"}]}
{"schema_version":2,"id":"operate/rs/references/metrics","title":"Real-time metrics","url":"https://redis.io/docs/latest/operate/rs/references/metrics/","summary":"Documents the metrics that are tracked with Redis Software.","tags":["docs","operate","rs","rc"],"last_updated":"2026-10-01T15:48:36+01:00","children":[{"id":"operate/rs/references/metrics/prometheus-metrics-v1","summary":"V1 metrics available to Prometheus.","title":"Prometheus metrics v1","url":"https://redis.io/docs/latest/operate/rs/references/metrics/prometheus-metrics-v1/"},{"id":"operate/rs/references/metrics/prometheus-metrics-v1-to-v2","summary":"Transition from v1 metrics to v2 PromQL equivalents.","title":"Transition from Prometheus v1 to Prometheus v2","url":"https://redis.io/docs/latest/operate/rs/references/metrics/prometheus-metrics-v1-to-v2/"},{"id":"operate/rs/references/metrics/prometheus-metrics-v2","summary":"V2 metrics available to Prometheus as of Redis Software version 7.8.2.","title":"Prometheus metrics v2","url":"https://redis.io/docs/latest/operate/rs/references/metrics/prometheus-metrics-v2/"},{"id":"operate/rs/references/metrics/database-operations","summary":"","title":"Database operations metrics","url":"https://redis.io/docs/latest/operate/rs/references/metrics/database-operations/"},{"id":"operate/rs/references/metrics/auto-tiering","summary":"","title":"Redis Flex and Auto Tiering Metrics","url":"https://redis.io/docs/latest/operate/rs/references/metrics/auto-tiering/"},{"id":"operate/rs/references/metrics/resource-usage","summary":"","title":"Resource usage metrics","url":"https://redis.io/docs/latest/operate/rs/references/metrics/resource-usage/"}],"page_type":"content","content_hash":"e423de9c53619b5a6d3b902127c356f6a028d755e85da3965869dd3e85a46644","sections":[{"id":"cluster-manager-metrics","title":"Cluster manager metrics","role":"overview","text":"In the Redis Software Cluster Manager UI, you can see real-time performance metrics for clusters, nodes, databases, and shards, and configure alerts that send notifications based on alert parameters. Select the **Metrics** tab to view the metrics for each component. For more information, see [Monitoring with metrics and alerts](https://redis.io/docs/latest/operate/rs/monitoring).\n\n![The database metrics page.](https://redis.io/docs/latest/images/rs/screenshots/metrics/db-metrics.png)\n\nSee the following topics for metrics definitions:\n- [Database operations](https://redis.io/docs/latest/operate/rs/references/metrics/database-operations) for database metrics\n- [Resource usage](https://redis.io/docs/latest/operate/rs/references/metrics/resource-usage) for resource and database usage metrics\n- [Auto Tiering](https://redis.io/docs/latest/operate/rs/references/metrics/auto-tiering) for additional metrics for [Auto Tiering ](https://redis.io/docs/latest/operate/rs/databases/flash) databases"},{"id":"prometheus-metrics","title":"Prometheus metrics","role":"content","text":"To collect and display metrics data from your databases and other cluster components,\nyou can connect your [Prometheus](https://prometheus.io/) and [Grafana](https://grafana.com/) server to your Redis Software cluster. We recommend you use Prometheus and Grafana to view metrics history and trends.\n\nSee [Prometheus integration](https://redis.io/docs/latest/operate/rs/monitoring/get-started) to learn how to connect Prometheus and Grafana to your Redis Software database.\n\nThe new metrics stream engine that exposes the v2 Prometheus scraping endpoint at `https://<cluster_name>:8070/v2` is generally available as of Redis Software version 8.0.\nThis new engine exports all time-series metrics to external monitoring tools such as Grafana, DataDog, NewRelic, and Dynatrace using Prometheus.\n\nThe new engine enables real-time monitoring, including full monitoring during maintenance operations, providing full visibility into performance during events such as shards' failovers and scaling operations.\n\nFor a list of available metrics, see the following references:\n\n- [Prometheus metrics v1](https://redis.io/docs/latest/operate/rs/references/metrics/prometheus-metrics-v1)\n\n- [Prometheus metrics v2](https://redis.io/docs/latest/operate/rs/references/metrics/prometheus-metrics-v2)\n\nIf you are already using the existing scraping endpoint for integration, follow [this guide](https://redis.io/docs/latest/operate/rs/references/metrics/prometheus-metrics-v1-to-v2) to transition and try the new engine. It is possible to scrape both existing and new endpoints simultaneously, allowing advanced dashboard preparation and a smooth transition."},{"id":"limitations","title":"Limitations","role":"limits","text":""},{"id":"shard-limit","title":"Shard limit","role":"content","text":"Metrics information is not shown for clusters with more than 128 shards. For large clusters, we recommend you use [Prometheus and Grafana](https://redis.io/docs/latest/operate/rs/monitoring/get-started) to view metrics."},{"id":"metrics-not-shown-during-shard-migration","title":"Metrics not shown during shard migration","role":"content","text":"The following metrics are not measured during [shard migration](https://redis.io/docs/latest/operate/rs/databases/configure/replica-ha) when using the [internal monitoring systems](https://redis.io/docs/latest/operate/rs/monitoring/v1_monitoring). If you view these metrics while resharding, the graph will be blank.\n\n- [Evicted objects/sec](https://redis.io/docs/latest/operate/rs/references/metrics/database-operations#evicted-objectssec)\n- [Expired objects/sec](https://redis.io/docs/latest/operate/rs/references/metrics/database-operations#expired-objectssec)\n- [Read misses/sec](https://redis.io/docs/latest/operate/rs/references/metrics/database-operations#read-missessec)\n- [Write misses/sec](https://redis.io/docs/latest/operate/rs/references/metrics/database-operations#write-missessec)\n- [Total keys](https://redis.io/docs/latest/operate/rs/references/metrics/database-operations#total-keys)\n- [Incoming traffic](https://redis.io/docs/latest/operate/rs/references/metrics/resource-usage#incoming-traffic)\n- [Outgoing traffic](https://redis.io/docs/latest/operate/rs/references/metrics/resource-usage#outgoing-traffic)\n- [Used memory](https://redis.io/docs/latest/operate/rs/references/metrics/resource-usage#used-memory)\n\nThis limitation does not apply to the new [metrics stream engine](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine)."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/metrics/prometheus-metrics-v1","title":"Prometheus metrics v1","url":"https://redis.io/docs/latest/operate/rs/references/metrics/prometheus-metrics-v1/","summary":"V1 metrics available to Prometheus.","tags":["docs","integrate","rs"],"last_updated":"2026-09-24T10:14:21+01:00","page_type":"content","content_hash":"465ff4b4a8292ad365e72b41bad57c0e82eb469d7cb10402524f67e49da79fb1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can integrate Redis Software with Prometheus and tools such as [Grafana](https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise), [Datadog](https://redis.io/docs/latest/integrate/datadog-with-redis-enterprise), [Dynatrace](https://redis.io/docs/latest/integrate/dynatrace-with-redis-enterprise), or [New Relic](https://redis.io/docs/latest/integrate/new-relic-with-redis-enterprise) to create dashboards for important metrics.\n\nAs of Redis Software version 7.8.2, v1 metrics are deprecated but still available. For help transitioning from v1 metrics to v2 PromQL, see [Prometheus v1 metrics and equivalent v2 PromQL](https://redis.io/docs/latest/operate/rs/references/metrics/prometheus-metrics-v1-to-v2).\n\n\nThe following tables include the v1 metrics available to Prometheus."},{"id":"database-metrics","title":"Database metrics","role":"content","text":"| Metric | Type | Unit | Description |\n| :-------- | :--- | :--- | :---------- |\n| bdb_avg_latency | gauge | seconds | Average latency of operations on the database (seconds); returned only when there is traffic |\n| bdb_avg_latency_max | gauge | seconds | Highest value of average latency of operations on the database (seconds); returned only when there is traffic |\n| bdb_avg_read_latency | gauge | seconds | Average latency of read operations (seconds); returned only when there is traffic |\n| bdb_avg_read_latency_max | gauge | seconds | Highest value of average latency of read operations (seconds); returned only when there is traffic |\n| bdb_avg_write_latency | gauge | seconds | Average latency of write operations (seconds); returned only when there is traffic |\n| bdb_avg_write_latency_max | gauge | seconds | Highest value of average latency of write operations (seconds); returned only when there is traffic |\n| bdb_bigstore_shard_count | gauge | count | Shard count by database and by storage engine (driver - rocksdb / speedb); Only for databases with Auto Tiering enabled |\n| bdb_conns | gauge | count | Number of client connections to the database |\n| bdb_egress_bytes | gauge | bytes/second | Rate of outgoing network traffic from the database (bytes/sec) |\n| bdb_egress_bytes_max | gauge | bytes/second | Highest value of the rate of outgoing network traffic from the database (bytes/sec) |\n| bdb_evicted_objects | gauge | evictions/second | Rate of key evictions from database (evictions/sec) |\n| bdb_evicted_objects_max | gauge | evictions/second | Highest value of the rate of key evictions from database (evictions/sec) |\n| bdb_expired_objects | gauge | expirations/second | Rate of keys expired in database (expirations/sec) |\n| bdb_expired_objects_max | gauge | expirations/second | Highest value of the rate of keys expired in database (expirations/sec) |\n| bdb_fork_cpu_system | gauge | ratio | Cores utilization in system mode for all Redis shard fork child processes of this database |\n| bdb_fork_cpu_system_max | gauge | ratio | Highest value of cores utilization in system mode for all Redis shard fork child processes of this database |\n| bdb_fork_cpu_user | gauge | ratio | Cores utilization in user mode for all Redis shard fork child processes of this database |\n| bdb_fork_cpu_user_max | gauge | ratio | Highest value of cores utilization in user mode for all Redis shard fork child processes of this database |\n| bdb_ingress_bytes | gauge | bytes/second | Rate of incoming network traffic to the database (bytes/sec) |\n| bdb_ingress_bytes_max | gauge | bytes/second | Highest value of the rate of incoming network traffic to the database (bytes/sec) |\n| bdb_instantaneous_ops_per_sec | gauge | operations/second | Request rate handled by all shards of database (ops/sec) |\n| bdb_main_thread_cpu_system | gauge | ratio | Cores utilization in system mode for all Redis shard main threads of this database |\n| bdb_main_thread_cpu_system_max | gauge | ratio | Highest value of cores utilization in system mode for all Redis shard main threads of this database |\n| bdb_main_thread_cpu_user | gauge | ratio | Cores utilization in user mode for all Redis shard main threads of this database |\n| bdb_main_thread_cpu_user_max | gauge | ratio | Highest value of cores utilization in user mode for all Redis shard main threads of this database |\n| bdb_mem_frag_ratio | gauge | ratio | RAM fragmentation ratio (RSS / allocated RAM) |\n| bdb_mem_size_lua | gauge | bytes | Redis lua scripting heap size (bytes) |\n| bdb_memory_limit | gauge | bytes | Configured RAM limit for the database |\n| bdb_monitor_sessions_count | gauge | count | Number of clients connected in monitor mode to the database |\n| bdb_no_of_keys | gauge | count | Number of keys in database |\n| bdb_other_req | gauge | operations/second | Rate of other (non read/write) requests on the database (ops/sec) |\n| bdb_other_req_max | gauge | operations/second | Highest value of the rate of other (non read/write) requests on the database (ops/sec) |\n| bdb_other_res | gauge | operations/second | Rate of other (non read/write) responses on the database (ops/sec) |\n| bdb_other_res_max | gauge | operations/second | Highest value of the rate of other (non read/write) responses on the database (ops/sec) |\n| bdb_pubsub_channels | gauge | count | Count the pub/sub channels with subscribed clients |\n| bdb_pubsub_channels_max | gauge | count | Highest value of count the pub/sub channels with subscribed clients |\n| bdb_pubsub_patterns | gauge | count | Count the pub/sub patterns with subscribed clients |\n| bdb_pubsub_patterns_max | gauge | count | Highest value of count the pub/sub patterns with subscribed clients |\n| bdb_read_hits | gauge | operations/second | Rate of read operations accessing an existing key (ops/sec) |\n| bdb_read_hits_max | gauge | operations/second | Highest value of the rate of read operations accessing an existing key (ops/sec) |\n| bdb_read_misses | gauge | operations/second | Rate of read operations accessing a non-existing key (ops/sec) |\n| bdb_read_misses_max | gauge | operations/second | Highest value of the rate of read operations accessing a non-existing key (ops/sec) |\n| bdb_read_req | gauge | operations/second | Rate of read requests on the database (ops/sec) |\n| bdb_read_req_max | gauge | operations/second | Highest value of the rate of read requests on the database (ops/sec) |\n| bdb_read_res | gauge | operations/second | Rate of read responses on the database (ops/sec) |\n| bdb_read_res_max | gauge | operations/second | Highest value of the rate of read responses on the database (ops/sec) |\n| bdb_shard_cpu_system | gauge | ratio | Cores utilization in system mode for all Redis shard processes of this database |\n| bdb_shard_cpu_system_max | gauge | ratio | Highest value of cores utilization in system mode for all Redis shard processes of this database |\n| bdb_shard_cpu_user | gauge | ratio | Cores utilization in user mode for the Redis shard process |\n| bdb_shard_cpu_user_max | gauge | ratio | Highest value of cores utilization in user mode for the Redis shard process |\n| bdb_shards_used | gauge | count | Used shard count by database and by shard type (ram / flash) |\n| bdb_total_connections_received | gauge | connections/second | Rate of new client connections to the database (connections/sec) |\n| bdb_total_connections_received_max | gauge | connections/second | Highest value of the rate of new client connections to the database (connections/sec) |\n| bdb_total_req | gauge | operations/second | Rate of all requests on the database (ops/sec) |\n| bdb_total_req_max | gauge | operations/second | Highest value of the rate of all requests on the database (ops/sec) |\n| bdb_total_res | gauge | operations/second | Rate of all responses on the database (ops/sec) |\n| bdb_total_res_max | gauge | operations/second | Highest value of the rate of all responses on the database (ops/sec) |\n| bdb_up | gauge | — | Database is up and running |\n| bdb_used_memory | gauge | bytes | Memory used by the database (in BigRedis this includes flash) (bytes) |\n| bdb_write_hits | gauge | operations/second | Rate of write operations accessing an existing key (ops/sec) |\n| bdb_write_hits_max | gauge | operations/second | Highest value of the rate of write operations accessing an existing key (ops/sec) |\n| bdb_write_misses | gauge | operations/second | Rate of write operations accessing a non-existing key (ops/sec) |\n| bdb_write_misses_max | gauge | operations/second | Highest value of the rate of write operations accessing a non-existing key (ops/sec) |\n| bdb_write_req | gauge | operations/second | Rate of write requests on the database (ops/sec) |\n| bdb_write_req_max | gauge | operations/second | Highest value of the rate of write requests on the database (ops/sec) |\n| bdb_write_res | gauge | operations/second | Rate of write responses on the database (ops/sec) |\n| bdb_write_res_max | gauge | operations/second | Highest value of the rate of write responses on the database (ops/sec) |\n| bdb_no_of_expires | gauge | count | Current number of volatile keys in the database |"},{"id":"node-metrics","title":"Node metrics","role":"content","text":"| Metric | Type | Unit | Description |\n| :-------- | :--- | :--- | :---------- |\n| node_available_flash | gauge | bytes | Available flash in the node (bytes) |\n| node_available_flash_no_overbooking | gauge | bytes | Available flash in the node (bytes), without taking into account overbooking |\n| node_available_memory | gauge | bytes | Amount of free memory in the node (bytes) that is available for database provisioning |\n| node_available_memory_no_overbooking | gauge | bytes | Available ram in the node (bytes) without taking into account overbooking |\n| node_avg_latency | gauge | microseconds | Average latency of requests handled by endpoints on the node in microseconds; returned only when there is traffic |\n| node_bigstore_free | gauge | bytes | Sum of free space of back-end flash (used by flash database's BigRedis) on all cluster nodes (bytes); returned only when BigRedis is enabled |\n| node_bigstore_iops | gauge | operations/second | Rate of i/o operations against back-end flash for all shards which are part of a flash-based database (BigRedis) in the cluster (ops/sec); returned only when BigRedis is enabled |\n| node_bigstore_kv_ops | gauge | operations/second | Rate of value read/write operations against back-end flash for all shards which are part of a flash-based database (BigRedis) in the cluster (ops/sec); returned only when BigRedis is enabled |\n| node_bigstore_throughput | gauge | bytes/second | Throughput i/o operations against back-end flash for all shards which are part of a flash-based database (BigRedis) in the cluster (bytes/sec); returned only when BigRedis is enabled |\n| node_cert_expiration_seconds | gauge | seconds | Certificate expiration (in seconds) per given node; read more about [certificates in Redis Software](https://redis.io/docs/latest/operate/rs/security/certificates) and [monitoring certificates](https://redis.io/docs/latest/operate/rs/security/certificates/monitor-certificates) |\n| node_conns | gauge | count | Number of clients connected to endpoints on node |\n| node_cpu_idle | gauge | ratio | CPU idle time portion (0-1, multiply by 100 to get percent) |\n| node_cpu_idle_max | gauge | ratio | Highest value of CPU idle time portion (0-1, multiply by 100 to get percent) |\n| node_cpu_idle_median | gauge | ratio | Median value of CPU idle time portion (0-1, multiply by 100 to get percent) |\n| node_cpu_idle_min | gauge | ratio | Lowest value of CPU idle time portion (0-1, multiply by 100 to get percent) |\n| node_cpu_system | gauge | ratio | CPU time portion spent in the kernel (0-1, multiply by 100 to get percent) |\n| node_cpu_system_max | gauge | ratio | Highest value of CPU time portion spent in the kernel (0-1, multiply by 100 to get percent) |\n| node_cpu_system_median | gauge | ratio | Median value of CPU time portion spent in the kernel (0-1, multiply by 100 to get percent) |\n| node_cpu_system_min | gauge | ratio | Lowest value of CPU time portion spent in the kernel (0-1, multiply by 100 to get percent) |\n| node_cpu_user | gauge | ratio | CPU time portion spent by users-pace processes (0-1, multiply by 100 to get percent) |\n| node_cpu_user_max | gauge | ratio | Highest value of CPU time portion spent by users-pace processes (0-1, multiply by 100 to get percent) |\n| node_cpu_user_median | gauge | ratio | Median value of CPU time portion spent by users-pace processes (0-1, multiply by 100 to get percent) |\n| node_cpu_user_min | gauge | ratio | Lowest value of CPU time portion spent by users-pace processes (0-1, multiply by 100 to get percent) |\n| node_cur_aof_rewrites | gauge | count | Number of aof rewrites that are currently performed by shards on this node |\n| node_egress_bytes | gauge | bytes/second | Rate of outgoing network traffic to node (bytes/sec) |\n| node_egress_bytes_max | gauge | bytes/second | Highest value of the rate of outgoing network traffic to node (bytes/sec) |\n| node_egress_bytes_median | gauge | bytes/second | Median value of the rate of outgoing network traffic to node (bytes/sec) |\n| node_egress_bytes_min | gauge | bytes/second | Lowest value of the rate of outgoing network traffic to node (bytes/sec) |\n| node_ephemeral_storage_avail | gauge | bytes | Disk space available to RLEC processes on configured ephemeral disk (bytes) |\n| node_ephemeral_storage_free | gauge | bytes | Free disk space on configured ephemeral disk (bytes) |\n| node_free_memory | gauge | bytes | Free memory in the node (bytes) |\n| node_ingress_bytes | gauge | bytes/second | Rate of incoming network traffic to node (bytes/sec) |\n| node_ingress_bytes_max | gauge | bytes/second | Highest value of the rate of incoming network traffic to node (bytes/sec) |\n| node_ingress_bytes_median | gauge | bytes/second | Median value of the rate of incoming network traffic to node (bytes/sec) |\n| node_ingress_bytes_min | gauge | bytes/second | Lowest value of the rate of incoming network traffic to node (bytes/sec) |\n| node_persistent_storage_avail | gauge | bytes | Disk space available to RLEC processes on configured persistent disk (bytes) |\n| node_persistent_storage_free | gauge | bytes | Free disk space on configured persistent disk (bytes) |\n| node_provisional_flash | gauge | bytes | Amount of flash available for new shards on this node, taking into account overbooking, max Redis servers, reserved flash and provision and migration thresholds (bytes) |\n| node_provisional_flash_no_overbooking | gauge | bytes | Amount of flash available for new shards on this node, without taking into account overbooking, max Redis servers, reserved flash and provision and migration thresholds (bytes) |\n| node_provisional_memory | gauge | bytes | Amount of RAM that is available for provisioning to databases out of the total RAM allocated for databases |\n| node_provisional_memory_no_overbooking | gauge | bytes | Amount of RAM that is available for provisioning to databases out of the total RAM allocated for databases, without taking into account overbooking |\n| node_total_req | gauge | operations/second | Request rate handled by endpoints on node (ops/sec) |\n| node_up | gauge | — | Node is part of the cluster and is connected |"},{"id":"cluster-metrics","title":"Cluster metrics","role":"content","text":"| Metric | Type | Unit | Description |\n| :-------- | :--- | :--- | :---------- |\n| cluster_shards_limit | gauge | count | Total shard limit by the license by shard type (ram / flash) |"},{"id":"proxy-metrics","title":"Proxy metrics","role":"content","text":"| Metric | Type | Unit | Description |\n| :-------- | :--- | :--- | :---------- |\n| listener_acc_latency | counter | seconds | Accumulative latency (sum of the latencies) of all types of commands on the database. For the average latency, divide this value by listener_total_res |\n| listener_acc_latency_max | counter | seconds | Highest value of accumulative latency of all types of commands on the database |\n| listener_acc_other_latency | counter | seconds | Accumulative latency (sum of the latencies) of commands that are the type \"other\" on the database. For the average latency, divide this value by listener_other_res |\n| listener_acc_other_latency_max | counter | seconds | Highest value of accumulative latency of commands that are the type \"other\" on the database |\n| listener_acc_read_latency | counter | seconds | Accumulative latency (sum of the latencies) of commands that are the type \"read\" on the database. For the average latency, divide this value by listener_read_res |\n| listener_acc_read_latency_max | counter | seconds | Highest value of accumulative latency of commands that are the type \"read\" on the database |\n| listener_acc_write_latency | counter | seconds | Accumulative latency (sum of the latencies) of commands that are the type \"write\" on the database. For the average latency, divide this value by listener_write_res |\n| listener_acc_write_latency_max | counter | seconds | Highest value of accumulative latency of commands that are the type \"write\" on the database |\n| listener_auth_cmds | counter | count | Number of memcached AUTH commands sent to the database |\n| listener_auth_cmds_max | counter | count | Highest value of the number of memcached AUTH commands sent to the database |\n| listener_auth_errors | counter | count | Number of error responses to memcached AUTH commands |\n| listener_auth_errors_max | counter | count | Highest value of the number of error responses to memcached AUTH commands |\n| listener_cmd_flush | counter | count | Number of memcached FLUSH_ALL commands sent to the database |\n| listener_cmd_flush_max | counter | count | Highest value of the number of memcached FLUSH_ALL commands sent to the database |\n| listener_cmd_get | counter | count | Number of memcached GET commands sent to the database |\n| listener_cmd_get_max | counter | count | Highest value of the number of memcached GET commands sent to the database |\n| listener_cmd_set | counter | count | Number of memcached SET commands sent to the database |\n| listener_cmd_set_max | counter | count | Highest value of the number of memcached SET commands sent to the database |\n| listener_cmd_touch | counter | count | Number of memcached TOUCH commands sent to the database |\n| listener_cmd_touch_max | counter | count | Highest value of the number of memcached TOUCH commands sent to the database |\n| listener_conns | counter | count | Number of clients connected to the endpoint |\n| listener_egress_bytes | counter | bytes/second | Rate of outgoing network traffic to the endpoint (bytes/sec) |\n| listener_egress_bytes_max | counter | bytes/second | Highest value of the rate of outgoing network traffic to the endpoint (bytes/sec) |\n| listener_ingress_bytes | counter | bytes/second | Rate of incoming network traffic to the endpoint (bytes/sec) |\n| listener_ingress_bytes_max | counter | bytes/second | Highest value of the rate of incoming network traffic to the endpoint (bytes/sec) |\n| listener_last_req_time | counter | timestamp seconds | Time of last command sent to the database |\n| listener_last_res_time | counter | timestamp seconds | Time of last response sent from the database |\n| listener_max_connections_exceeded | counter | count | Number of times the Number of clients connected to the database at the same time has exceeded the max limit |\n| listener_max_connections_exceeded_max | counter | count | Highest value of the number of times the Number of clients connected to the database at the same time has exceeded the max limit |\n| listener_monitor_sessions_count | counter | count | Number of client connected in monitor mode to the endpoint |\n| listener_other_req | counter | operations/second | Rate of other (non read/write) requests on the endpoint (ops/sec) |\n| listener_other_req_max | counter | operations/second | Highest value of the rate of other (non read/write) requests on the endpoint (ops/sec) |\n| listener_other_res | counter | operations/second | Rate of other (non read/write) responses on the endpoint (ops/sec) |\n| listener_other_res_max | counter | operations/second | Highest value of the rate of other (non read/write) responses on the endpoint (ops/sec) |\n| listener_other_started_res | counter | count | Number of responses sent from the database of type \"other\" |\n| listener_other_started_res_max | counter | count | Highest value of the number of responses sent from the database of type \"other\" |\n| listener_read_req | counter | operations/second | Rate of read requests on the endpoint (ops/sec) |\n| listener_read_req_max | counter | operations/second | Highest value of the rate of read requests on the endpoint (ops/sec) |\n| listener_read_res | counter | operations/second | Rate of read responses on the endpoint (ops/sec) |\n| listener_read_res_max | counter | operations/second | Highest value of the rate of read responses on the endpoint (ops/sec) |\n| listener_read_started_res | counter | count | Number of responses sent from the database of type \"read\" |\n| listener_read_started_res_max | counter | count | Highest value of the number of responses sent from the database of type \"read\" |\n| listener_total_connections_received | counter | connections/second | Rate of new client connections to the endpoint (connections/sec) |\n| listener_total_connections_received_max | counter | connections/second | Highest value of the rate of new client connections to the endpoint (connections/sec) |\n| listener_total_req | counter | operations/second | Request rate handled by the endpoint (ops/sec) |\n| listener_total_req_max | counter | operations/second | Highest value of the rate of all requests on the endpoint (ops/sec) |\n| listener_total_res | counter | operations/second | Rate of all responses on the endpoint (ops/sec) |\n| listener_total_res_max | counter | operations/second | Highest value of the rate of all responses on the endpoint (ops/sec) |\n| listener_total_started_res | counter | count | Number of responses sent from the database of all types |\n| listener_total_started_res_max | counter | count | Highest value of the number of responses sent from the database of all types |\n| listener_write_req | counter | operations/second | Rate of write requests on the endpoint (ops/sec) |\n| listener_write_req_max | counter | operations/second | Highest value of the rate of write requests on the endpoint (ops/sec) |\n| listener_write_res | counter | operations/second | Rate of write responses on the endpoint (ops/sec) |\n| listener_write_res_max | counter | operations/second | Highest value of the rate of write responses on the endpoint (ops/sec) |\n| listener_write_started_res | counter | count | Number of responses sent from the database of type \"write\" |\n| listener_write_started_res_max | counter | count | Highest value of the number of responses sent from the database of type \"write\" |"},{"id":"replication-metrics","title":"Replication metrics","role":"content","text":"| Metric | Type | Unit | Description |\n| :-------- | :--- | :--- | :---------- |\n| bdb_replicaof_syncer_ingress_bytes | gauge | bytes/second | Rate of compressed incoming network traffic to a Replica Of database (bytes/sec) |\n| bdb_replicaof_syncer_ingress_bytes_decompressed | gauge | bytes/second | Rate of decompressed incoming network traffic to a Replica Of database (bytes/sec) |\n| bdb_replicaof_syncer_local_ingress_lag_time | gauge | milliseconds | Lag time between the source and the destination for Replica Of traffic (ms) |\n| bdb_replicaof_syncer_status | gauge | — | Syncer status for Replica Of traffic; 0 = in-sync, 1 = syncing, 2 = out of sync |\n| bdb_crdt_syncer_ingress_bytes | gauge | bytes/second | Rate of compressed incoming network traffic to CRDB (bytes/sec) |\n| bdb_crdt_syncer_ingress_bytes_decompressed | gauge | bytes/second | Rate of decompressed incoming network traffic to CRDB (bytes/sec) |\n| bdb_crdt_syncer_local_ingress_lag_time | gauge | milliseconds | Lag time between the source and the destination (ms) for CRDB traffic |\n| bdb_crdt_syncer_status | gauge | — | Syncer status for CRDB traffic; 0 = in-sync, 1 = syncing, 2 = out of sync |"},{"id":"shard-metrics","title":"Shard metrics","role":"content","text":"| Metric | Type | Unit | Description |\n| :-------- | :--- | :--- | :---------- |\n| redis_active_defrag_running | gauge | percent | Automatic memory defragmentation current aggressiveness (% cpu) |\n| redis_allocator_active | gauge | bytes | Total used memory, including external fragmentation |\n| redis_allocator_allocated | gauge | bytes | Total allocated memory |\n| redis_allocator_resident | gauge | bytes | Total resident memory (RSS) |\n| redis_aof_last_cow_size | gauge | bytes | Last AOFR, CopyOnWrite memory |\n| redis_aof_rewrite_in_progress | gauge | — | Indicates whether an AOF rewrite is in progress (1 = in progress, 0 = otherwise) |\n| redis_aof_rewrites | gauge | count | Number of AOF rewrites this process executed |\n| redis_aof_delayed_fsync | gauge | count | Number of times an AOF fsync caused delays in the Redis main thread (inducing latency); this can indicate that the disk is slow or overloaded |\n| redis_blocked_clients | gauge | count | Count the clients waiting on a blocking call |\n| redis_connected_clients | gauge | count | Number of client connections to the specific shard |\n| redis_connected_slaves | gauge | count | Number of connected replicas |\n| redis_db0_avg_ttl | gauge | milliseconds | Average TTL of all volatile keys. Deprecated. |\n| redis_db0_expires | gauge | count | Total count of volatile keys. Deprecated. |\n| redis_db0_keys | gauge | count | Total key count. Deprecated. |\n| redis_evicted_keys | gauge | count | Keys evicted so far (since restart) |\n| redis_expire_cycle_cpu_milliseconds | gauge | milliseconds | The cumulative amount of time spent on active expiry cycles |\n| redis_expired_keys | gauge | count | Keys expired so far (since restart) |\n| redis_forwarding_state | gauge | — | Shard forwarding state (1 = on, 0 = off) |\n| redis_keys_trimmed | gauge | count | The number of keys that were trimmed in the current or last resharding process |\n| redis_keyspace_read_hits | gauge | count | Number of read operations accessing an existing keyspace |\n| redis_keyspace_read_misses | gauge | count | Number of read operations accessing a non-existing keyspace |\n| redis_keyspace_write_hits | gauge | count | Number of write operations accessing an existing keyspace |\n| redis_keyspace_write_misses | gauge | count | Number of write operations accessing a non-existing keyspace |\n| redis_master_link_status | gauge | — | Indicates if the replica is connected to its master (1 = up, 2 = down, 3 = none, 99 = unknown) |\n| redis_master_repl_offset | gauge | bytes | Number of bytes sent to replicas by the shard; calculate the throughput for a time period by comparing the value at different times |\n| redis_master_sync_in_progress | gauge | — | The primary shard is synchronizing (1 true; 0 false) |\n| redis_max_process_mem | gauge | bytes | Current memory limit configured by redis_mgr according to node free memory |\n| redis_maxmemory | gauge | bytes | Current memory limit configured by redis_mgr according to database memory limits |\n| redis_mem_aof_buffer | gauge | bytes | Current size of AOF buffer |\n| redis_mem_clients_normal | gauge | bytes | Current memory used for input and output buffers of non-replica clients |\n| redis_mem_clients_slaves | gauge | bytes | Current memory used for input and output buffers of replica clients |\n| redis_mem_fragmentation_ratio | gauge | ratio | Memory fragmentation ratio (1.3 means 30% overhead) |\n| redis_mem_not_counted_for_evict | gauge | bytes | Portion of used_memory (in bytes) that's not counted for eviction and OOM error |\n| redis_mem_replication_backlog | gauge | bytes | Size of replication backlog |\n| redis_module_fork_in_progress | gauge | — | A binary value that indicates if there is an active fork spawned by a module (1) or not (0) |\n| redis_process_cpu_system_seconds_total | counter | seconds | Shard process system CPU time spent in seconds |\n| redis_process_cpu_usage_percent | counter | percent | Shard process cpu usage precentage |\n| redis_process_cpu_user_seconds_total | counter | seconds | Shard user CPU time spent in seconds |\n| redis_process_main_thread_cpu_system_seconds_total | counter | seconds | Shard main thread system CPU time spent in seconds |\n| redis_process_main_thread_cpu_user_seconds_total | counter | seconds | Shard main thread user CPU time spent in seconds |\n| redis_process_max_fds | gauge | count | Shard maximum number of open file descriptors |\n| redis_process_open_fds | gauge | count | Shard number of open file descriptors |\n| redis_process_resident_memory_bytes | gauge | bytes | Shard resident memory size in bytes |\n| redis_process_start_time_seconds | gauge | timestamp seconds | Shard start time of the process since unix epoch in seconds |\n| redis_process_virtual_memory_bytes | gauge | bytes | Shard virtual memory in bytes |\n| redis_rdb_bgsave_in_progress | gauge | — | Indicates whether bgsave is currently in progress (1 = in progress, 0 = otherwise) |\n| redis_rdb_last_cow_size | gauge | bytes | Last bgsave (or SYNC fork) used CopyOnWrite memory |\n| redis_rdb_saves | gauge | count | Total count of bgsaves since process was restarted (including replica fullsync and persistence) |\n| redis_repl_touch_bytes | gauge | bytes | Number of bytes sent to replicas as TOUCH commands by the shard as a result of a READ command that was processed; calculate the throughput for a time period by comparing the value at different times |\n| redis_total_commands_processed | gauge | count | Number of commands processed by the shard; calculate the number of commands for a time period by comparing the value at different times |\n| redis_total_connections_received | gauge | count | Number of connections received by the shard; calculate the number of connections for a time period by comparing the value at different times |\n| redis_total_net_input_bytes | gauge | bytes | Number of bytes received by the shard; calculate the throughput for a time period by comparing the value at different times |\n| redis_total_net_output_bytes | gauge | bytes | Number of bytes sent by the shard; calculate the throughput for a time period by comparing the value at different times |\n| redis_up | gauge | — | Shard is up and running. The value is always 1; the metric is absent while the shard is down. |\n| redis_used_memory | gauge | bytes | Memory used by shard (in BigRedis this includes flash) (bytes) |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/metrics/prometheus-metrics-v1-to-v2","title":"Transition from Prometheus v1 to Prometheus v2","url":"https://redis.io/docs/latest/operate/rs/references/metrics/prometheus-metrics-v1-to-v2/","summary":"Transition from v1 metrics to v2 PromQL equivalents.","tags":["docs","integrate","rs"],"last_updated":"2026-09-24T10:14:21+01:00","page_type":"content","content_hash":"d2e69ffac4929129ea16ab2aeabfebc6ed1ae1a84a9afeb91da8269dca2273d5","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can integrate Redis Software with Prometheus and tools such as [Grafana](https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise), [Datadog](https://redis.io/docs/latest/integrate/datadog-with-redis-enterprise), [Dynatrace](https://redis.io/docs/latest/integrate/dynatrace-with-redis-enterprise), or [New Relic](https://redis.io/docs/latest/integrate/new-relic-with-redis-enterprise) to create dashboards for important metrics.\n\nAs of Redis Software version 7.8.2, [PromQL (Prometheus Query Language)](https://prometheus.io/docs/prometheus/latest/querying/basics/) metrics are available. V1 metrics are deprecated but still available.\n\nTo transition from v1 metrics to v2 metrics, you need to change the `metrics_path` in your Prometheus configuration file from `/` to `/v2` to use the new scraping endpoint.\n\nHere's an example of the updated scraping configuration in `prometheus.yml`:\n\n[code example]\n\n> [!NOTE]\n> **Use a single scrape target.** The v2 endpoint is cluster-wide. Every node aggregates metrics from all nodes and returns the same complete result, so one target is enough. If you list one target per node, Prometheus stores every series once per target and multiplies each `sum()`-based dashboard panel by the number of targets. This produces no error. Prometheus reports every target as up and Grafana renders normally. Use your cluster FQDN as the single target so metrics remain available if a node goes down.\n\nThe reason for a single target changed in v2. On v1, only the cluster master served the metrics endpoint and other nodes returned a redirect, so the protocol effectively forced one target. On v2, every node returns the full cluster view and no redirects are involved. If your v1 configuration listed multiple node targets, reduce it to one.\n\nIf you prefer a per-node scrape topology, scrape `/v2/node`, which returns only that node's own metrics. Aggregation adds the `cluster` and `node` labels, so `/v2/node` responses omit them. Add `relabel_configs` to supply those labels before using `/v2/node` with the Redis Software Grafana dashboards.\n\nIt is possible to scrape both v1 and v2 endpoints simultaneously during the transition period to prepare dashboards and ensure a smooth transition.\n\nYou can use the following tables to transition from v1 metrics to equivalent v2 PromQL. For a list of all available v2 metrics, see [Prometheus metrics v2](https://redis.io/docs/latest/operate/rs/references/metrics/prometheus-metrics-v2)."},{"id":"database-metrics","title":"Database metrics","role":"content","text":"| V1&nbsp;metric | Equivalent V2 PromQL | Description |\n| --------- | :------------------- | :---------- |\n| <span class=\"break-all\">bdb_avg_latency</span> | <span class=\"break-all\">`(sum by (db)(irate(endpoint_read_requests_latency_histogram_sum{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_write_requests_latency_histogram_sum{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_other_requests_latency_histogram_sum{db=\"\"$db\"\"}[$__rate_interval])))/(sum by (db)(irate(endpoint_read_requests{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_write_requests{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_other_requests{db=\"\"$db\"\"}[$__rate_interval])))/1000000`</span> | Average latency of operations on the database (seconds); returned only when there is traffic |\n| <span class=\"break-all\">bdb_avg_latency_max</span> | <span class=\"break-all\">`avg(histogram_quantile(1, sum by (le, db) (irate(endpoint_read_requests_latency_histogram_bucket{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_write_requests_latency_histogram_bucket{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_other_requests_latency_histogram_bucket{db=\"\"$db\"\"}[$__rate_interval])))) / 1000000`</span> | Highest value of average latency of operations on the database (seconds); returned only when there is traffic |\n| <span class=\"break-all\">bdb_avg_read_latency</span> | <span class=\"break-all\">`(sum(irate(endpoint_read_requests_latency_histogram_sum{db=\"\"$db\"\"}[$__rate_interval]))/sum(irate(endpoint_read_requests{db=\"\"$db\"\"}[$__rate_interval])))/1000000`</span> | Average latency of read operations (seconds); returned only when there is traffic |\n| <span class=\"break-all\">bdb_avg_read_latency_max</span></span> | <span class=\"break-all\">`histogram_quantile(1, sum by (le) (irate(endpoint_read_requests_latency_histogram_bucket{db=\"\"$db\"\"}[$__rate_interval]))) / 1000000`</span> | Highest value of average latency of read operations (seconds); returned only when there is traffic |\n| <span class=\"break-all\">bdb_avg_write_latency</span> | <span class=\"break-all\">`(sum(irate(endpoint_write_requests_latency_histogram_sum{db=\"\"$db\"\"}[$__rate_interval]))/sum(irate(endpoint_write_requests{db=\"\"$db\"\"}[$__rate_interval])))/1000000`</span> | Average latency of write operations (seconds); returned only when there is traffic |\n| <span class=\"break-all\">bdb_avg_write_latency_max</span> | <span class=\"break-all\">`histogram_quantile(1, sum by (le) (irate(endpoint_write_requests_latency_histogram_bucket{db=\"\"$db\"\"}[$__rate_interval]))) / 1000000`</span> | Highest value of average latency of write operations (seconds); returned only when there is traffic |\n| <span class=\"break-all\">bdb_bigstore_shard_count</span> | <span class=\"break-all\">`sum((sum(label_replace(label_replace(namedprocess_namegroup_thread_count{groupname=~\"redis-\\d+\", threadname=~\"(speedb\\|rocksdb).*\"}, \"redis\", \"$1\", \"groupname\", \"redis-(\\d+)\"), \"driver\", \"$1\", \"threadname\", \"(speedb\\|rocksdb).*\")) by (redis, driver) > bool 0) * on (redis) group_left(db) redis_server_up) by (db, driver)`</span> | Shard count by database and by storage engine (driver - rocksdb / speedb); Only for databases with Auto Tiering enabled |\n| <span class=\"break-all\">bdb_conns</span> | <span class=\"break-all\">`sum(endpoint_client_connections{cluster=\"$cluster\", db=\"$db\"} - endpoint_client_disconnections{cluster=\"$cluster\", db=\"$db\"} - endpoint_proxy_disconnections{cluster=\"$cluster\", db=\"$db\"})`</span> | Number of client connections to database |\n| <span class=\"break-all\">bdb_egress_bytes</span> | <span class=\"break-all\">`sum by(db) (irate(endpoint_egress{db=\"$db\"}[$__rate_interval]))`</span> | Rate of outgoing network traffic from the database (bytes/sec) |\n| <span class=\"break-all\">bdb_egress_bytes_max</span> | <span class=\"break-all\">`max_over_time (sum by(db) (irate(endpoint_egress{db=\"$db\"}[$__rate_interval]))[$__range:])`</span> | Highest value of the rate of outgoing network traffic from the database (bytes/sec) |\n| <span class=\"break-all\">bdb_evicted_objects</span> | <span class=\"break-all\">`sum by (db) (irate(redis_server_evicted_keys{role=\"master\"}[$__rate_interval]))`</span> | Rate of key evictions from database (evictions/sec) |\n| <span class=\"break-all\">bdb_evicted_objects_max</span> | <span class=\"break-all\">`sum by (db) (irate(redis_server_evicted_keys{role=\"master\"}[$__rate_interval]))`</span> | Highest value of the rate of key evictions from database (evictions/sec) |\n| <span class=\"break-all\">bdb_expired_objects</span> | <span class=\"break-all\">`sum by (db) (irate(redis_server_expired_keys{role=\"master\"}[$__rate_interval]))`</span> | Rate of keys expired in database (expirations/sec) |\n| <span class=\"break-all\">bdb_expired_objects_max</span> | <span class=\"break-all\">`sum by (db) (irate(redis_server_expired_keys{role=\"master\"}[$__rate_interval]))`</span> | Highest value of the rate of keys expired in database (expirations/sec) |\n| <span class=\"break-all\">bdb_fork_cpu_system</span> | <span class=\"break-all\">`sum by (db) (irate(namedprocess_namegroup_thread_cpu_seconds_total{mode=\"system\"}[$__rate_interval]))`</span> | Cores utilization in system mode for all Redis shard fork child processes of this database |\n| <span class=\"break-all\">bdb_fork_cpu_system_max</span> | <span class=\"break-all\">`sum by (db) (irate(namedprocess_namegroup_thread_cpu_seconds_total{mode=\"system\"}[$__rate_interval]))`</span> | Highest value of cores utilization in system mode for all Redis shard fork child processes of this database |\n| <span class=\"break-all\">bdb_fork_cpu_user</span> | <span class=\"break-all\">`sum by (db) (irate(namedprocess_namegroup_thread_cpu_seconds_total{mode=\"user\"}[$__rate_interval]))`</span> | Cores utilization in user mode for all Redis shard fork child processes of this database |\n| <span class=\"break-all\">bdb_fork_cpu_user_max</span> | <span class=\"break-all\">`sum by (db) (irate(namedprocess_namegroup_thread_cpu_seconds_total{mode=\"user\"}[$__rate_interval]))`</span> | Highest value of cores utilization in user mode for all Redis shard fork child processes of this database |\n| <span class=\"break-all\">bdb_ingress_bytes</span> | <span class=\"break-all\">`sum by(db) (irate(endpoint_ingress{db=\"$db\"}[$__rate_interval]))`</span> | Rate of incoming network traffic to database (bytes/sec) |\n| <span class=\"break-all\">bdb_ingress_bytes_max</span> | <span class=\"break-all\">`max_over_time (sum by(db) (irate(endpoint_ingress{db=\"$db\"}[$__rate_interval]))[$__range:])`</span> | Highest value of the rate of incoming network traffic to database (bytes/sec) |\n| <span class=\"break-all\">bdb_instantaneous_ops_per_sec</span></span> | <span class=\"break-all\">`sum by(db) (redis_server_instantaneous_ops_per_sec)`</span> | Request rate handled by all shards of database (ops/sec) |\n| <span class=\"break-all\">bdb_main_thread_cpu_system</span> | <span class=\"break-all\">`sum by(db) (irate(namedprocess_namegroup_thread_cpu_seconds_total{mode=\"system\", threadname=~\"redis-server.*\"}[$__rate_interval]))`</span> | Cores utilization in system mode for all Redis shard main threads of this database |\n| <span class=\"break-all\">bdb_main_thread_cpu_system_max</span></span> | <span class=\"break-all\">`sum by(db) (irate(namedprocess_namegroup_thread_cpu_seconds_total{mode=\"system\", threadname=~\"redis-server.*\"}[$__rate_interval]))`</span> | Highest value of cores utilization in system mode for all Redis shard main threads of this database |\n| <span class=\"break-all\">bdb_main_thread_cpu_user</span> | <span class=\"break-all\">`sum by(irate(namedprocess_namegroup_thread_cpu_seconds_total{mode=\"user\", threadname=~\"redis-server.*\"}[$__rate_interval]))`</span> | Cores utilization in user mode for all Redis shard main threads of this database |\n| <span class=\"break-all\">bdb_main_thread_cpu_user_max</span></span> | <span class=\"break-all\">`sum by(irate(namedprocess_namegroup_thread_cpu_seconds_total{mode=\"user\", threadname=~\"redis-server.*\"}[$__rate_interval]))`</span> | Highest value of cores utilization in user mode for all Redis shard main threads of this database |\n| <span class=\"break-all\">bdb_mem_frag_ratio</span> | <span class=\"break-all\">`avg(redis_server_mem_fragmentation_ratio)`</span> | RAM fragmentation ratio (RSS / allocated RAM) |\n| <span class=\"break-all\">bdb_mem_size_lua</span> | <span class=\"break-all\">`sum by(db) (redis_server_used_memory_lua)`</span> | Redis lua scripting heap size (bytes) |\n| <span class=\"break-all\">bdb_memory_limit</span> | <span class=\"break-all\">`db_memory_limit_bytes`</span> | Configured RAM limit for the database |\n| <span class=\"break-all\">bdb_monitor_sessions_count</span> | <span class=\"break-all\">`sum by(db) (endpoint_monitor_sessions_count)`</span> | Number of clients connected in monitor mode to the database |\n| <span class=\"break-all\">bdb_no_of_keys</span> | <span class=\"break-all\">`sum by (db) (redis_server_db_keys{role=\"master\"})`</span> | Number of keys in database |\n| <span class=\"break-all\">bdb_other_req</span> | <span class=\"break-all\">`sum by(db) (irate(endpoint_other_requests{db=\"$db\"}[$__rate_interval]))`</span> | Rate of other (non read/write) requests on the database (ops/sec) |\n| <span class=\"break-all\">bdb_other_req_max</span> | <span class=\"break-all\">`max_over_time (sum by(db) (irate(endpoint_other_requests{db=\"$db\"}[$__rate_interval]))[$__range:])`</span> | Highest value of the rate of other (non read/write) requests on the database (ops/sec) |\n| <span class=\"break-all\">bdb_other_res</span> | <span class=\"break-all\">`sum by(db) (irate(endpoint_other_responses{db=\"$db\"}[$__rate_interval]))`</span> | Rate of other (non read/write) responses on the database (ops/sec) |\n| <span class=\"break-all\">bdb_other_res_max</span> | <span class=\"break-all\">`max_over_time (sum by(db) (irate(endpoint_other_responses{db=\"$db\"}[$__rate_interval]))[$__range:])`</span> | Highest value of the rate of other (non read/write) responses on the database (ops/sec) |\n| <span class=\"break-all\">bdb_pubsub_channels</span> | <span class=\"break-all\">`sum by(db) (redis_server_pubsub_channels)`</span> | Count the pub/sub channels with subscribed clients |\n| <span class=\"break-all\">bdb_pubsub_channels_max</span> | <span class=\"break-all\">`sum by(db) (redis_server_pubsub_channels)`</span> | Highest value of count the pub/sub channels with subscribed clients |\n| <span class=\"break-all\">bdb_pubsub_patterns</span> | <span class=\"break-all\">`sum by(db) (redis_server_pubsub_patterns)`</span> | Count the pub/sub patterns with subscribed clients |\n| <span class=\"break-all\">bdb_pubsub_patterns_max</span></span> | <span class=\"break-all\">`sum by(db) (redis_server_pubsub_patterns)`</span> | Highest value of count the pub/sub patterns with subscribed clients |\n| <span class=\"break-all\">bdb_read_hits</span> | <span class=\"break-all\">`sum by (db) (irate(redis_server_keyspace_read_hits{role=\"master\"}[$__rate_interval]))`</span> | Rate of read operations accessing an existing key (ops/sec) |\n| <span class=\"break-all\">bdb_read_hits_max</span> | <span class=\"break-all\">`sum by (db) (irate(redis_server_keyspace_read_hits{role=\"master\"}[$__rate_interval]))`</span> | Highest value of the rate of read operations accessing an existing key (ops/sec) |\n| <span class=\"break-all\">bdb_read_misses</span> | <span class=\"break-all\">`sum by (db) (irate(redis_server_keyspace_read_misses{role=\"master\"}[$__rate_interval]))`</span> | Rate of read operations accessing a non-existing key (ops/sec) |\n| <span class=\"break-all\">bdb_read_misses_max</span> | <span class=\"break-all\">`sum by (db) (irate(redis_server_keyspace_read_misses{role=\"master\"}[$__rate_interval]))`</span> | Highest value of the rate of read operations accessing a non-existing key (ops/sec) |\n| <span class=\"break-all\">bdb_read_req</span> | <span class=\"break-all\">`sum by (db) (irate(endpoint_read_requests{db=\"$db\"}[$__rate_interval]))`</span> | Rate of read requests on the database (ops/sec) |\n| <span class=\"break-all\">bdb_read_req_max</span> | <span class=\"break-all\">`max_over_time (sum by(db) (irate(endpoint_read_requests{db=\"$db\"}[$__rate_interval]))[$__range:])`</span> | Highest value of the rate of read requests on the database (ops/sec) |\n| <span class=\"break-all\">bdb_read_res</span> | <span class=\"break-all\">`sum by (db) (irate(endpoint_read_responses{\"$db\"}[$__rate_interval]))`</span> | Rate of read responses on the database (ops/sec) |\n| <span class=\"break-all\">bdb_read_res_max</span> | <span class=\"break-all\">`max_over_time (sum by(db) (irate(endpoint_read_responses{db=\"$db\"}[$__rate_interval]))[$__range:])`</span> | Highest value of the rate of read responses on the database (ops/sec) |\n| <span class=\"break-all\">bdb_shard_cpu_system</span> | <span class=\"break-all\">`sum by(db) (irate(namedprocess_namegroup_thread_cpu_seconds_total{mode=\"system\", role=\"master\"}[$__rate_interval]))`</span> | Cores utilization in system mode for all Redis shard processes of this database |\n| <span class=\"break-all\">bdb_shard_cpu_system_max<span></span> | <span class=\"break-all\">`sum by(db) (irate(namedprocess_namegroup_thread_cpu_seconds_total{mode=\"system\", role=\"master\"}[$__rate_interval]))`</span> | Highest value of cores utilization in system mode for all Redis shard processes of this database |\n| <span class=\"break-all\">bdb_shard_cpu_user</span> | <span class=\"break-all\">`sum by(db) (irate(namedprocess_namegroup_thread_cpu_seconds_total{mode=\"user\", role=\"master\"}[$__rate_interval]))`</span> | Cores utilization in user mode for the Redis shard process |\n| <span class=\"break-all\">bdb_shard_cpu_user_max</span> | <span class=\"break-all\">`sum by(db) (irate(namedprocess_namegroup_thread_cpu_seconds_total{mode=\"user\", role=\"master\"}[$__rate_interval]))`</span> | Highest value of cores utilization in user mode for the Redis shard process |\n| <span class=\"break-all\">bdb_shards_used</span> | <span class=\"break-all\">`sum((sum(label_replace(label_replace(label_replace(namedprocess_namegroup_thread_count{groupname=~\"redis-\\d+\"}, \"redis\", \"$1\", \"groupname\", \"redis-(\\d+)\"), \"shard_type\", \"flash\", \"threadname\", \"(bigstore).*\"), \"shard_type\", \"ram\", \"shard_type\", \"\")) by (redis, shard_type) > bool 0) * on (redis) group_left(db) redis_server_up) by (db, shard_type)`</span> | Used shard count by database and by shard type (ram / flash) |\n| <span class=\"break-all\">bdb_total_connections_received</span> | <span class=\"break-all\">`sum by(db) (irate(endpoint_client_connections{db=\"$db\"}[$__rate_interval]))`</span> | Rate of new client connections to database (connections/sec) |\n| <span class=\"break-all\">bdb_total_connections_received_max<span></span> | <span class=\"break-all\">`max_over_time (sum by(db) (irate(endpoint_client_connections{db=\"$db\"}[$__rate_interval]))[$__range:])`</span> | Highest value of the rate of new client connections to database (connections/sec) |\n| <span class=\"break-all\">bdb_total_req</span> | <span class=\"break-all\">`sum by (db) (irate(endpoint_read_requests{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_write_requests{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_other_requests{db=\"\"$db\"\"}[$__rate_interval]))`</span> | Rate of all requests on the database (ops/sec) |\n| <span class=\"break-all\">bdb_total_req_max</span> | <span class=\"break-all\">`max_over_time(sum by (db) (irate(endpoint_read_requests{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_write_requests{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_other_requests{db=\"\"$db\"\"}[$__rate_interval])) [$__range:])`</span> | Highest value of the rate of all requests on the database (ops/sec) |\n| <span class=\"break-all\">bdb_total_res</span> | <span class=\"break-all\">`sum by (db) (irate(endpoint_read_responses{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_write_responses{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_other_responses{db=\"\"$db\"\"}[$__rate_interval]))`</span> | Rate of all responses on the database (ops/sec) |\n| <span class=\"break-all\">bdb_total_res_max</span> | <span class=\"break-all\">`max_over_time(sum by (db) (irate(endpoint_read_responses{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_write_responses{db=\"\"$db\"\"}[$__rate_interval]) + irate(endpoint_other_responses{db=\"\"$db\"\"}[$__rate_interval])) [$__range:])`</span> | Highest value of the rate of all responses on the database (ops/sec) |\n| <span class=\"break-all\">bdb_up</span> | <span class=\"break-all\">`min by(db) (redis_server_up)`</span> | Database is up and running |\n| <span class=\"break-all\">bdb_used_memory</span> | <span class=\"break-all\">`sum by (db) (redis_server_used_memory)`</span> | Memory used by database (in BigRedis this includes flash) (bytes) |\n| <span class=\"break-all\">bdb_write_hits</span> | <span class=\"break-all\">`sum by (db) (irate(redis_server_keyspace_write_hits{role=\"master\"}[$__rate_interval]))`</span> | Rate of write operations accessing an existing key (ops/sec) |\n| <span class=\"break-all\">bdb_write_hits_max</span> | <span class=\"break-all\">`sum by (db) (irate(redis_server_keyspace_write_hits{role=\"master\"}[$__rate_interval]))`</span> | Highest value of the rate of write operations accessing an existing key (ops/sec) |\n| <span class=\"break-all\">bdb_write_misses</span> | <span class=\"break-all\">`sum by (db) (irate(redis_server_keyspace_write_misses{role=\"master\"}[$__rate_interval]))`</span> | Rate of write operations accessing a non-existing key (ops/sec) |\n| <span class=\"break-all\">bdb_write_misses_max</span> | <span class=\"break-all\">`sum by (db) (irate(redis_server_keyspace_write_misses{role=\"master\"}[$__rate_interval]))`</span> | Highest value of the rate of write operations accessing a non-existing key (ops/sec) |\n| <span class=\"break-all\">bdb_write_req</span> | <span class=\"break-all\">`sum by (db) (irate(endpoint_write_requests{db=\"$db\"}[$__rate_interval]))`</span> | Rate of write requests on the database (ops/sec) |\n| <span class=\"break-all\">bdb_write_req_max</span> | <span class=\"break-all\">`max_over_time(sum by (db) (irate(endpoint_write_requests{db=\"$db\"}[$__rate_interval]))[$__range:])`</span> | Highest value of the rate of write requests on the database (ops/sec) |\n| <span class=\"break-all\">bdb_write_res</span> | <span class=\"break-all\">`sum by(db) (irate(endpoint_write_responses{db=\"$db\"}[$__rate_interval]))`</span> | Rate of write responses on the database (ops/sec) |\n| <span class=\"break-all\">bdb_write_res_max</span> | <span class=\"break-all\">`max_over_time(sum by (db) (irate(endpoint_write_responses{db=\"$db\"}[$__rate_interval]))[$__range:])`</span> | Highest value of the rate of write responses on the database (ops/sec) |\n| <span class=\"break-all\">bdb_no_of_expires</span> | <span class=\"break-all\">`sum by(db) (redis_server_db_expires{role=\"master\"})`</span> | Current number of volatile keys in the database |"},{"id":"node-metrics","title":"Node metrics","role":"content","text":"| V1&nbsp;metric | Equivalent V2 PromQL | Description |\n| --------- | :------------------- | :---------- |\n| <span class=\"break-all\">node_available_flash</span> | <span class=\"break-all\">`node_available_flash_bytes`</span> | Available flash in the node (bytes) |\n| <span class=\"break-all\">node_available_flash_no_overbooking</span> | <span class=\"break-all\">`node_available_flash_no_overbooking_bytes`</span> | Available flash in the node (bytes), without taking into account overbooking |\n| <span class=\"break-all\">node_available_memory</span> | <span class=\"break-all\">`node_available_memory_bytes`</span> | Amount of free memory in the node (bytes) that is available for database provisioning |\n| <span class=\"break-all\">node_available_memory_no_overbooking</span> | <span class=\"break-all\">`node_available_memory_no_overbooking_bytes`</span> | Available RAM in the node (bytes) without taking into account overbooking |\n| <span class=\"break-all\">node_avg_latency</span> | <span class=\"break-all\">`sum by (proxy) (irate(endpoint_acc_latency[$__rate_interval])) / sum by (proxy) (irate(endpoint_total_started_res[$__rate_interval]))`</span> | Average latency of requests handled by endpoints on the node in microseconds; returned only when there is traffic |\n| <span class=\"break-all\">node_bigstore_free</span> | <span class=\"break-all\">`node_bigstore_free_bytes`</span> | Sum of free space of back-end flash (used by flash database's [BigRedis]) on all cluster nodes (bytes); returned only when BigRedis is enabled |\n| <span class=\"break-all\">node_bigstore_iops</span> | <span class=\"break-all\">`node_flash_reads_total + node_flash_writes_total`</span> | Rate of I/O operations against back-end flash for all shards which are part of a flash-based database (BigRedis) in the cluster (ops/sec); returned only when BigRedis is enabled |\n| <span class=\"break-all\">node_bigstore_kv_ops</span> | <span class=\"break-all\">`sum by (node) (irate(redis_server_big_io_dels[$__rate_interval]) + irate(redis_server_big_io_reads[$__rate_interval]) + irate(redis_server_big_io_writes[$__rate_interval]))`</span> | Rate of value read/write operations against back-end flash for all shards which are part of a flash-based database (BigRedis) in the cluster (ops/sec); returned only when BigRedis is enabled |\n| <span class=\"break-all\">node_bigstore_throughput</span> | <span class=\"break-all\">`sum by (node) (irate(redis_server_big_io_read_bytes[$__rate_interval]) + irate(redis_server_big_io_write_bytes[$__rate_interval]))`</span> | Throughput I/O operations against back-end flash for all shards which are part of a flash-based database (BigRedis) in the cluster (bytes/sec); returned only when BigRedis is enabled |\n| <span class=\"break-all\">node_cert_expiration_seconds</span> | <span class=\"break-all\">`node_cert_expires_in_seconds`</span> | Certificate expiration (in seconds) per given node; read more about [certificates in Redis Software](https://redis.io/docs/latest/operate/rs/security/certificates) and [monitoring certificates](https://redis.io/docs/latest/operate/rs/security/certificates/monitor-certificates) |\n| <span class=\"break-all\">node_conns</span> | <span class=\"break-all\">`sum by (node) (endpoint_client_connections)`</span> | Number of clients connected to endpoints on node |\n| <span class=\"break-all\">node_cpu_idle</span> | <span class=\"break-all\">`avg by (node) (irate(node_cpu_seconds_total{mode=\"idle\"}[$__rate_interval]))`</span> | CPU idle time portion (0-1, multiply by 100 to get percent) |\n| <span class=\"break-all\">node_cpu_idle_max</span> | <span class=\"break-all\">N/A</span> | Highest value of CPU idle time portion (0-1, multiply by 100 to get percent) |\n| <span class=\"break-all\">node_cpu_idle_median</span> | <span class=\"break-all\">N/A</span> | Median value of CPU idle time portion (0-1, multiply by 100 to get percent) |\n| <span class=\"break-all\">node_cpu_idle_min</span> | <span class=\"break-all\">N/A</span> | Lowest value of CPU idle time portion (0-1, multiply by 100 to get percent) |\n| <span class=\"break-all\">node_cpu_system</span> | <span class=\"break-all\">`avg by (node) (irate(node_cpu_seconds_total{mode=\"system\"}[$__rate_interval]))`</span> | CPU time portion spent in the kernel (0-1, multiply by 100 to get percent) |\n| <span class=\"break-all\">node_cpu_system_max</span> | <span class=\"break-all\">N/A</span> | Highest value of CPU time portion spent in the kernel (0-1, multiply by 100 to get percent) |\n| <span class=\"break-all\">node_cpu_system_median</span> | <span class=\"break-all\">N/A</span> | Median value of CPU time portion spent in the kernel (0-1, multiply by 100 to get percent) |\n| <span class=\"break-all\">node_cpu_system_min</span> | <span class=\"break-all\">N/A</span> | Lowest value of CPU time portion spent in the kernel (0-1, multiply by 100 to get percent) |\n| <span class=\"break-all\">node_cpu_user</span> | <span class=\"break-all\">`avg by (node) (irate(node_cpu_seconds_total{mode=\"user\"}[$__rate_interval]))`</span> | CPU time portion spent by user-space processes (0-1, multiply by 100 to get percent) |\n| <span class=\"break-all\">node_cpu_user_max</span> | <span class=\"break-all\">N/A</span> | Highest value of CPU time portion spent by user-space processes (0-1, multiply by 100 to get percent) |\n| <span class=\"break-all\">node_cpu_user_median</span> | <span class=\"break-all\">N/A</span> | Median value of CPU time portion spent by user-space processes (0-1, multiply by 100 to get percent) |\n| <span class=\"break-all\">node_cpu_user_min</span> | <span class=\"break-all\">N/A</span> | Lowest value of CPU time portion spent by user-space processes (0-1, multiply by 100 to get percent) |\n| <span class=\"break-all\">node_cur_aof_rewrites</span> | <span class=\"break-all\">`sum by (cluster, node) (redis_server_aof_rewrite_in_progress)`</span> | Number of AOF rewrites that are currently performed by shards on this node |\n| <span class=\"break-all\">node_egress_bytes</span> | <span class=\"break-all\">`irate(node_network_transmit_bytes_total{device=\"<interface>\"}[$__rate_interval])`</span> | Rate of outgoing network traffic to node (bytes/sec) |\n| <span class=\"break-all\">node_egress_bytes_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of outgoing network traffic to node (bytes/sec) |\n| <span class=\"break-all\">node_egress_bytes_median</span> | <span class=\"break-all\">N/A</span> | Median value of the rate of outgoing network traffic to node (bytes/sec) |\n| <span class=\"break-all\">node_egress_bytes_min</span> | <span class=\"break-all\">N/A</span> | Lowest value of the rate of outgoing network traffic to node (bytes/sec) |\n| <span class=\"break-all\">node_ephemeral_storage_avail</span> | <span class=\"break-all\">`node_ephemeral_storage_avail_bytes`</span> | Disk space available to RLEC processes on configured ephemeral disk (bytes) |\n| <span class=\"break-all\">node_ephemeral_storage_free</span> | <span class=\"break-all\">`node_ephemeral_storage_free_bytes`</span> | Free disk space on configured ephemeral disk (bytes) |\n| <span class=\"break-all\">node_free_memory</span> | <span class=\"break-all\">`node_memory_MemFree_bytes`</span> | Free memory in the node (bytes) |\n| <span class=\"break-all\">node_ingress_bytes</span> | <span class=\"break-all\">`irate(node_network_receive_bytes_total{device=\"<interface>\"}[$__rate_interval])`</span> | Rate of incoming network traffic to node (bytes/sec) |\n| <span class=\"break-all\">node_ingress_bytes_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of incoming network traffic to node (bytes/sec) |\n| <span class=\"break-all\">node_ingress_bytes_median</span> | <span class=\"break-all\">N/A</span> | Median value of the rate of incoming network traffic to node (bytes/sec) |\n| <span class=\"break-all\">node_ingress_bytes_min</span> | <span class=\"break-all\">N/A</span> | Lowest value of the rate of incoming network traffic to node (bytes/sec) |\n| <span class=\"break-all\">node_persistent_storage_avail</span> | <span class=\"break-all\">`node_persistent_storage_avail_bytes`</span> | Disk space available to RLEC processes on configured persistent disk (bytes) |\n| <span class=\"break-all\">node_persistent_storage_free</span> | <span class=\"break-all\">`node_persistent_storage_free_bytes`</span> | Free disk space on configured persistent disk (bytes) |\n| <span class=\"break-all\">node_provisional_flash</span> | <span class=\"break-all\">`node_provisional_flash_bytes`</span> | Amount of flash available for new shards on this node, taking into account overbooking, max Redis servers, reserved flash, and provision and migration thresholds (bytes) |\n| <span class=\"break-all\">node_provisional_flash_no_overbooking</span> | <span class=\"break-all\">`node_provisional_flash_no_overbooking_bytes`</span> | Amount of flash available for new shards on this node, without taking into account overbooking, max Redis servers, reserved flash, and provision and migration thresholds (bytes) |\n| <span class=\"break-all\">node_provisional_memory</span> | <span class=\"break-all\">`node_provisional_memory_bytes`</span> | Amount of RAM that is available for provisioning to databases out of the total RAM allocated for databases |\n| <span class=\"break-all\">node_provisional_memory_no_overbooking</span> | <span class=\"break-all\">`node_provisional_memory_no_overbooking_bytes`</span> | Amount of RAM that is available for provisioning to databases out of the total RAM allocated for databases, without taking into account overbooking |\n| <span class=\"break-all\">node_total_req</span> | <span class=\"break-all\">`sum by (cluster, node) (irate(endpoint_read_requests[$__rate_interval]) +  irate(endpoint_write_requests[$__rate_interval]) +  irate(endpoint_other_requests[$__rate_interval]) )`</span> | Request rate handled by endpoints on node (ops/sec) |\n| <span class=\"break-all\">node_up</span> | <span class=\"break-all\">`node_metrics_up`</span> | Node is part of the cluster and is connected |"},{"id":"cluster-metrics","title":"Cluster metrics","role":"content","text":"| V1&nbsp;metric | Equivalent V2 PromQL | Description |\n| --------- | :------------------- | :---------- |\n| cluster_shards_limit | `license_shards_limit` | Total shard limit by the license by shard type (ram / flash) |"},{"id":"proxy-metrics","title":"Proxy metrics","role":"content","text":"| V1&nbsp;metric | Equivalent V2 PromQL | Description |\n| --------- | :------------------- | :---------- |\n| <span class=\"break-all\">listener_acc_latency</span> | <span class=\"break-all\">N/A</span> | Accumulative latency (sum of the latencies) of all types of commands on the database. For the average latency, divide this value by listener_total_res |\n| <span class=\"break-all\">listener_acc_latency_max</span> | <span class=\"break-all\">N/A</span> | Highest value of accumulative latency of all types of commands on the database |\n| <span class=\"break-all\">listener_acc_other_latency</span> | <span class=\"break-all\">N/A</span> | Accumulative latency (sum of the latencies) of commands that are a type \"other\" on the database. For the average latency, divide this value by listener_other_res |\n| <span class=\"break-all\">listener_acc_other_latency_max</span> | <span class=\"break-all\">N/A</span> | Highest value of accumulative latency of commands that are a type \"other\" on the database |\n| <span class=\"break-all\">listener_acc_read_latency</span> | <span class=\"break-all\">N/A</span> | Accumulative latency (sum of the latencies) of commands that are a type \"read\" on the database. For the average latency, divide this value by listener_read_res |\n| <span class=\"break-all\">listener_acc_read_latency_max</span> | <span class=\"break-all\">N/A</span> | Highest value of accumulative latency of commands that are a type \"read\" on the database |\n| <span class=\"break-all\">listener_acc_write_latency</span> | <span class=\"break-all\">N/A</span> | Accumulative latency (sum of the latencies) of commands that are a type \"write\" on the database. For the average latency, divide this value by listener_write_res |\n| <span class=\"break-all\">listener_acc_write_latency_max</span> | <span class=\"break-all\">N/A</span> | Highest value of accumulative latency of commands that are a type \"write\" on the database |\n| <span class=\"break-all\">listener_auth_cmds</span> | <span class=\"break-all\">N/A</span> | Number of memcached AUTH commands sent to the database |\n| <span class=\"break-all\">listener_auth_cmds_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the number of memcached AUTH commands sent to the database |\n| <span class=\"break-all\">listener_auth_errors</span> | <span class=\"break-all\">N/A</span> | Number of error responses to memcached AUTH commands |\n| <span class=\"break-all\">listener_auth_errors_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the number of error responses to memcached AUTH commands |\n| <span class=\"break-all\">listener_cmd_flush</span> | <span class=\"break-all\">N/A</span> | Number of memcached FLUSH_ALL commands sent to the database |\n| <span class=\"break-all\">listener_cmd_flush_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the number of memcached FLUSH_ALL commands sent to the database |\n| <span class=\"break-all\">listener_cmd_get</span> | <span class=\"break-all\">N/A</span> | Number of memcached GET commands sent to the database |\n| <span class=\"break-all\">listener_cmd_get_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the number of memcached GET commands sent to the database |\n| <span class=\"break-all\">listener_cmd_set</span> | <span class=\"break-all\">N/A</span> | Number of memcached SET commands sent to the database |\n| <span class=\"break-all\">listener_cmd_set_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the number of memcached SET commands sent to the database |\n| <span class=\"break-all\">listener_cmd_touch</span> | <span class=\"break-all\">N/A</span> | Number of memcached TOUCH commands sent to the database |\n| <span class=\"break-all\">listener_cmd_touch_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the number of memcached TOUCH commands sent to the database |\n| <span class=\"break-all\">listener_conns</span> | <span class=\"break-all\">N/A</span> | Number of clients connected to the endpoint |\n| <span class=\"break-all\">listener_egress_bytes</span> | <span class=\"break-all\">N/A</span> | Rate of outgoing network traffic to the endpoint (bytes/sec) |\n| <span class=\"break-all\">listener_egress_bytes_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of outgoing network traffic to the endpoint (bytes/sec) |\n| <span class=\"break-all\">listener_ingress_bytes</span> | <span class=\"break-all\">N/A</span> | Rate of incoming network traffic to the endpoint (bytes/sec) |\n| <span class=\"break-all\">listener_ingress_bytes_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of incoming network traffic to the endpoint (bytes/sec) |\n| <span class=\"break-all\">listener_last_req_time</span> | <span class=\"break-all\">N/A</span> | Time of last command sent to the database |\n| <span class=\"break-all\">listener_last_res_time</span> | <span class=\"break-all\">N/A</span> | Time of last response sent from the database |\n| <span class=\"break-all\">listener_max_connections_exceeded</span> | <span class=\"break-all\">`irate(endpoint_maximal_connections_exceeded[$__rate_interval])`</span> | Number of times the number of clients connected to the database at the same time has exceeded the max limit |\n| <span class=\"break-all\">listener_max_connections_exceeded_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the number of times the number of clients connected to the database at the same time has exceeded the max limit |\n| <span class=\"break-all\">listener_monitor_sessions_count</span> | <span class=\"break-all\">N/A</span> | Number of clients connected in monitor mode to the endpoint |\n| <span class=\"break-all\">listener_other_req</span> | <span class=\"break-all\">N/A</span> | Rate of other (non-read/write) requests on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_other_req_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of other (non-read/write) requests on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_other_res</span> | <span class=\"break-all\">N/A</span> | Rate of other (non-read/write) responses on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_other_res_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of other (non-read/write) responses on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_other_started_res</span> | <span class=\"break-all\">N/A</span> | Number of responses sent from the database of type \"other\" |\n| <span class=\"break-all\">listener_other_started_res_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the number of responses sent from the database of type \"other\" |\n| <span class=\"break-all\">listener_read_req</span> | <span class=\"break-all\">`irate(endpoint_read_requests[$__rate_interval])`</span> | Rate of read requests on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_read_req_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of read requests on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_read_res</span> | <span class=\"break-all\">`irate(endpoint_read_responses[$__rate_interval])`</span> | Rate of read responses on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_read_res_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of read responses on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_read_started_res</span> | <span class=\"break-all\">N/A</span> | Number of responses sent from the database of type \"read\" |\n| <span class=\"break-all\">listener_read_started_res_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the number of responses sent from the database of type \"read\" |\n| <span class=\"break-all\">listener_total_connections_received</span> | <span class=\"break-all\">`irate(endpoint_total_connections_received[$__rate_interval])`</span> | Rate of new client connections to the endpoint (connections/sec) |\n| <span class=\"break-all\">listener_total_connections_received_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of new client connections to the endpoint (connections/sec) |\n| <span class=\"break-all\">listener_total_req</span> | <span class=\"break-all\">N/A</span> | Request rate handled by the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_total_req_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of all requests on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_total_res</span> | <span class=\"break-all\">N/A</span> | Rate of all responses on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_total_res_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of all responses on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_total_started_res</span> | <span class=\"break-all\">N/A</span> | Number of responses sent from the database of all types |\n| <span class=\"break-all\">listener_total_started_res_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the number of responses sent from the database of all types |\n| <span class=\"break-all\">listener_write_req</span> | <span class=\"break-all\">`irate(endpoint_write_requests[$__rate_interval])`</span> | Rate of write requests on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_write_req_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of write requests on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_write_res</span> | <span class=\"break-all\">`irate(endpoint_write_responses[$__rate_interval])`</span> | Rate of write responses on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_write_res_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the rate of write responses on the endpoint (ops/sec) |\n| <span class=\"break-all\">listener_write_started_res</span> | <span class=\"break-all\">N/A</span> | Number of responses sent from the database of type \"write\" |\n| <span class=\"break-all\">listener_write_started_res_max</span> | <span class=\"break-all\">N/A</span> | Highest value of the number of responses sent from the database of type \"write\" |"},{"id":"replication-metrics","title":"Replication metrics","role":"content","text":"| V1&nbsp;metric | Equivalent V2 PromQL | Description |\n| --------- | :------------------- | :---------- |\n| <span class=\"break-all\">bdb_replicaof_syncer_ingress_bytes</span> | <span class=\"break-all\">`rate(replica_src_ingress_bytes[$__rate_interval])`</span> | Rate of compressed incoming network traffic to a Replica Of database (bytes/sec) |\n| <span class=\"break-all\">bdb_replicaof_syncer_ingress_bytes_decompressed</span> | <span class=\"break-all\">`rate(replica_src_ingress_bytes_decompressed[$__rate_interval])`</span> | Rate of decompressed incoming network traffic to a Replica Of database (bytes/sec) |\n| <span class=\"break-all\">bdb_replicaof_syncer_local_ingress_lag_time</span> | <span class=\"break-all\">`database_syncer_lag_ms{syncer_type=\"replicaof\"}`</span> | Lag time between the source and the destination for Replica Of traffic (ms) |\n| <span class=\"break-all\">bdb_replicaof_syncer_status</span> | <span class=\"break-all\">`database_syncer_current_status{syncer_type=\"replicaof\"}`</span> | Syncer status for Replica Of traffic; 0 = in-sync, 1 = syncing, 2 = out of sync |\n| <span class=\"break-all\">bdb_crdt_syncer_ingress_bytes</span> | <span class=\"break-all\">`rate(crdt_src_ingress_bytes[$__rate_interval])`</span> | Rate of compressed incoming network traffic to CRDB (bytes/sec) |\n| <span class=\"break-all\">bdb_crdt_syncer_ingress_bytes_decompressed</span> | <span class=\"break-all\">`rate(crdt_src_ingress_bytes_decompressed[$__rate_interval])`</span> | Rate of decompressed incoming network traffic to CRDB (bytes/sec) |\n| <span class=\"break-all\">bdb_crdt_syncer_local_ingress_lag_time</span> | <span class=\"break-all\">`database_syncer_lag_ms{syncer_type=\"crdt\"}`</span> | Lag time between the source and the destination (ms) for CRDB traffic |\n| <span class=\"break-all\">bdb_crdt_syncer_status</span> | <span class=\"break-all\">`database_syncer_current_status{syncer_type=\"crdt\"}`</span> | Syncer status for CRDB traffic; 0 = in-sync, 1 = syncing, 2 = out of sync |"},{"id":"shard-metrics","title":"Shard metrics","role":"content","text":"| V1&nbsp;metric | Equivalent V2 PromQL | Description |\n| --------- | :------------------- | :---------- |\n| <span class=\"break-all\">redis_active_defrag_running</span> | <span class=\"break-all\">`redis_server_active_defrag_running`</span> | Automatic memory defragmentation current aggressiveness (% cpu) |\n| <span class=\"break-all\">redis_allocator_active</span> | <span class=\"break-all\">`redis_server_allocator_active`</span> | Total used memory, including external fragmentation |\n| <span class=\"break-all\">redis_allocator_allocated</span> | <span class=\"break-all\">`redis_server_allocator_allocated`</span> | Total allocated memory |\n| <span class=\"break-all\">redis_allocator_resident</span> | <span class=\"break-all\">`redis_server_allocator_resident`</span> | Total resident memory (RSS) |\n| <span class=\"break-all\">redis_aof_last_cow_size</span> | <span class=\"break-all\">`redis_server_aof_last_cow_size`</span> | Last AOFR, CopyOnWrite memory |\n| <span class=\"break-all\">redis_aof_rewrite_in_progress</span> | <span class=\"break-all\">`redis_server_aof_rewrite_in_progress`</span> | Indicates whether an AOF rewrite is in progress (1 = in progress, 0 = otherwise) |\n| <span class=\"break-all\">redis_aof_rewrites</span> | <span class=\"break-all\">`redis_server_aof_rewrites`</span> | Number of AOF rewrites this process executed |\n| <span class=\"break-all\">redis_aof_delayed_fsync</span> | <span class=\"break-all\">`redis_server_aof_delayed_fsync`</span> | Number of times an AOF fsync caused delays in the main Redis thread (inducing latency); this can indicate that the disk is slow or overloaded |\n| <span class=\"break-all\">redis_blocked_clients</span> | <span class=\"break-all\">`redis_server_blocked_clients`</span> | Count the clients waiting on a blocking call |\n| <span class=\"break-all\">redis_connected_clients</span> | <span class=\"break-all\">`redis_server_connected_clients`</span> | Number of client connections to the specific shard |\n| <span class=\"break-all\">redis_connected_slaves</span> | <span class=\"break-all\">`redis_server_connected_slaves`</span> | Number of connected replicas |\n| <span class=\"break-all\">redis_db0_avg_ttl</span> | <span class=\"break-all\">`redis_server_db0_avg_ttl`</span> | Average TTL of all volatile keys |\n| <span class=\"break-all\">redis_db0_expires</span> | <span class=\"break-all\">`redis_server_expired_keys`</span> | Total count of volatile keys |\n| <span class=\"break-all\">redis_db0_keys</span> | <span class=\"break-all\">`redis_server_db0_keys`</span> | Total key count |\n| <span class=\"break-all\">redis_evicted_keys</span> | <span class=\"break-all\">`redis_server_evicted_keys`</span> | Keys evicted so far (since restart) |\n| <span class=\"break-all\">redis_expire_cycle_cpu_milliseconds</span> | <span class=\"break-all\">`redis_server_expire_cycle_cpu_milliseconds`</span> | The cumulative amount of time spent on active expiry cycles |\n| <span class=\"break-all\">redis_expired_keys</span> | <span class=\"break-all\">`redis_server_expired_keys`</span> | Keys expired so far (since restart) |\n| <span class=\"break-all\">redis_forwarding_state</span> | <span class=\"break-all\">`redis_server_forwarding_state`</span> | Shard forwarding state (1 = on, 0 = off) |\n| <span class=\"break-all\">redis_keys_trimmed</span> | <span class=\"break-all\">`redis_server_keys_trimmed`</span> | The number of keys that were trimmed in the current or last resharding process |\n| <span class=\"break-all\">redis_keyspace_read_hits</span> | <span class=\"break-all\">`redis_server_keyspace_read_hits`</span> | Number of read operations accessing an existing keyspace |\n| <span class=\"break-all\">redis_keyspace_read_misses</span> | <span class=\"break-all\">`redis_server_keyspace_read_misses`</span> | Number of read operations accessing a non-existing keyspace |\n| <span class=\"break-all\">redis_keyspace_write_hits</span> | <span class=\"break-all\">`redis_server_keyspace_write_hits`</span> | Number of write operations accessing an existing keyspace |\n| <span class=\"break-all\">redis_keyspace_write_misses</span> | <span class=\"break-all\">`redis_server_keyspace_write_misses`</span> | Number of write operations accessing a non-existing keyspace |\n| <span class=\"break-all\">redis_master_link_status</span> | <span class=\"break-all\">`redis_server_master_link_status`</span> | Indicates if the replica is connected to its master (1 = up, 2 = down, 3 = none, 99 = unknown) |\n| <span class=\"break-all\">redis_master_repl_offset</span> | <span class=\"break-all\">`redis_server_master_repl_offset`</span> | Number of bytes sent to replicas by the shard; calculate the throughput for a time period by comparing the value at different times |\n| <span class=\"break-all\">redis_master_sync_in_progress</span> | <span class=\"break-all\">`redis_server_master_sync_in_progress`</span> | The master shard is synchronizing (1 true; 0 false) |\n| <span class=\"break-all\">redis_max_process_mem</span> | <span class=\"break-all\">`redis_server_max_process_mem`</span> | Current memory limit configured by redis_mgr according to node free memory |\n| <span class=\"break-all\">redis_maxmemory</span> | <span class=\"break-all\">`redis_server_maxmemory`</span> | Current memory limit configured by redis_mgr according to database memory limits. <br /><br />To calculate the percent memory usage:<br /><span class=\"break-all\">`sum by (cluster,db)(redis_server_used_memory{role=\"master\"}) / (avg by(cluster,db)(db_memory_limit_bytes) / max by(cluster,db)(db_replication_factor))`</span> |\n| <span class=\"break-all\">redis_mem_aof_buffer</span> | <span class=\"break-all\">`redis_server_mem_aof_buffer`</span> | Current size of AOF buffer |\n| <span class=\"break-all\">redis_mem_clients_normal</span> | <span class=\"break-all\">`redis_server_mem_clients_normal`</span> | Current memory used for input and output buffers of non-replica clients |\n| <span class=\"break-all\">redis_mem_clients_slaves</span> | <span class=\"break-all\">`redis_server_mem_clients_slaves`</span> | Current memory used for input and output buffers of replica clients |\n| <span class=\"break-all\">redis_mem_fragmentation_ratio</span> | <span class=\"break-all\">`redis_server_mem_fragmentation_ratio`</span> | Memory fragmentation ratio (1.3 means 30% overhead) |\n| <span class=\"break-all\">redis_mem_not_counted_for_evict</span> | <span class=\"break-all\">`redis_server_mem_not_counted_for_evict`</span> | Portion of used_memory (in bytes) that's not counted for eviction and OOM error |\n| <span class=\"break-all\">redis_mem_replication_backlog</span> | <span class=\"break-all\">`redis_server_mem_replication_backlog`</span> | Size of replication backlog |\n| <span class=\"break-all\">redis_module_fork_in_progress</span> | <span class=\"break-all\">`redis_server_module_fork_in_progress`</span> | A binary value that indicates if there is an active fork spawned by a module (1) or not (0) |\n| <span class=\"break-all\">redis_process_cpu_system_seconds_total</span> | <span class=\"break-all\">`namedprocess_namegroup_cpu_seconds_total{mode=\"system\"}`</span> | Shard process system CPU time spent in seconds |\n| <span class=\"break-all\">redis_process_cpu_usage_percent</span> | <span class=\"break-all\">`namedprocess_namegroup_cpu_seconds_total{mode=~\"system\\|user\"}`</span> | Shard process CPU usage percentage |\n| <span class=\"break-all\">redis_process_cpu_user_seconds_total</span> | <span class=\"break-all\">`namedprocess_namegroup_cpu_seconds_total{mode=\"user\"}`</span> | Shard user CPU time spent in seconds |\n| <span class=\"break-all\">redis_process_main_thread_cpu_system_seconds_total</span> | <span class=\"break-all\">`namedprocess_namegroup_thread_cpu_seconds_total{mode=\"system\",threadname=\"redis-server\"}`</span> | Shard main thread system CPU time spent in seconds |\n| <span class=\"break-all\">redis_process_main_thread_cpu_user_seconds_total</span> | <span class=\"break-all\">`namedprocess_namegroup_thread_cpu_seconds_total{mode=\"user\",threadname=\"redis-server\"}`</span> | Shard main thread user CPU time spent in seconds |\n| <span class=\"break-all\">redis_process_max_fds</span> | <span class=\"break-all\">`max(namedprocess_namegroup_open_filedesc)`</span> | Shard maximum number of open file descriptors |\n| <span class=\"break-all\">redis_process_open_fds</span> | <span class=\"break-all\">`namedprocess_namegroup_open_filedesc`</span> | Shard number of open file descriptors |\n| <span class=\"break-all\">redis_process_resident_memory_bytes</span> | <span class=\"break-all\">`namedprocess_namegroup_memory_bytes{memtype=\"resident\"}`</span> | Shard resident memory size in bytes |\n| <span class=\"break-all\">redis_process_start_time_seconds</span> | <span class=\"break-all\">`namedprocess_namegroup_oldest_start_time_seconds`</span> | Shard start time of the process since unix epoch in seconds |\n| <span class=\"break-all\">redis_process_virtual_memory_bytes</span> | <span class=\"break-all\">`namedprocess_namegroup_memory_bytes{memtype=\"virtual\"}`</span> | Shard virtual memory in bytes |\n| <span class=\"break-all\">redis_rdb_bgsave_in_progress</span> | <span class=\"break-all\">`redis_server_rdb_bgsave_in_progress`</span> | Indicates whether bgsave is currently in progress (1 = in progress, 0 = otherwise) |\n| <span class=\"break-all\">redis_rdb_last_cow_size</span> | <span class=\"break-all\">`redis_server_rdb_last_cow_size`</span> | Last bgsave (or SYNC fork) used CopyOnWrite memory |\n| <span class=\"break-all\">redis_rdb_saves</span> | <span class=\"break-all\">`redis_server_rdb_saves`</span> | Total count of bgsaves since the process was restarted (including replica fullsync and persistence) |\n| <span class=\"break-all\">redis_repl_touch_bytes</span> | <span class=\"break-all\">`redis_server_repl_touch_bytes`</span> | Number of bytes sent to replicas as TOUCH commands by the shard as a result of a READ command that was processed; calculate the throughput for a time period by comparing the value at different times |\n| <span class=\"break-all\">redis_total_commands_processed</span> | <span class=\"break-all\">`redis_server_total_commands_processed`</span> | Number of commands processed by the shard; calculate the number of commands for a time period by comparing the value at different times |\n| <span class=\"break-all\">redis_total_connections_received</span> | <span class=\"break-all\">`redis_server_total_connections_received`</span> | Number of connections received by the shard; calculate the number of connections for a time period by comparing the value at different times |\n| <span class=\"break-all\">redis_total_net_input_bytes</span> | <span class=\"break-all\">`redis_server_total_net_input_bytes`</span> | Number of bytes received by the shard; calculate the throughput for a time period by comparing the value at different times |\n| <span class=\"break-all\">redis_total_net_output_bytes</span> | <span class=\"break-all\">`redis_server_total_net_output_bytes`</span> | Number of bytes sent by the shard; calculate the throughput for a time period by comparing the value at different times |\n| <span class=\"break-all\">redis_up</span> | <span class=\"break-all\">`redis_server_up`</span> | Shard is up and running |\n| <span class=\"break-all\">redis_used_memory</span> | <span class=\"break-all\">`redis_server_used_memory`</span> | Memory used by shard (in BigRedis this includes flash) (bytes) |"}],"examples":[{"id":"overview-ex0","language":"yaml","code":"scrape_configs:\n  # Scrape Redis Software\n  - job_name: redis-enterprise\n    scrape_interval: 30s\n    scrape_timeout: 30s\n    metrics_path: /v2\n    scheme: https\n    tls_config:\n      insecure_skip_verify: true\n    static_configs:\n      - targets: [\"<cluster_name>:8070\"]","section_id":"overview"}]}
{"schema_version":2,"id":"operate/rs/references/metrics/prometheus-metrics-v2","title":"Prometheus metrics v2","url":"https://redis.io/docs/latest/operate/rs/references/metrics/prometheus-metrics-v2/","summary":"V2 metrics available to Prometheus as of Redis Software version 7.8.2.","tags":["docs","integrate","rs"],"last_updated":"2026-09-24T10:14:21+01:00","page_type":"content","content_hash":"708fbbc05748c63cf1c8ab5e2b44f85cedfddb7595bea628afaa75fe6afa5662","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can integrate Redis Software with Prometheus and tools such as [Grafana](https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise), [Datadog](https://redis.io/docs/latest/integrate/datadog-with-redis-enterprise), [Dynatrace](https://redis.io/docs/latest/integrate/dynatrace-with-redis-enterprise), or [New Relic](https://redis.io/docs/latest/integrate/new-relic-with-redis-enterprise) to create dashboards for important metrics.\n\nThe v2 metrics in the following tables are available as of Redis Software version 7.8.0. For help transitioning from v1 metrics to v2 PromQL, see [Prometheus v1 metrics and equivalent v2 PromQL](https://redis.io/docs/latest/operate/rs/references/metrics/prometheus-metrics-v1-to-v2).\n\nThe v2 scraping endpoint also exposes metrics for `node_exporter`. For more information, see the [Prometheus node_exporter GitHub repository](https://github.com/prometheus/node_exporter)."},{"id":"database-metrics","title":"Database metrics","role":"content","text":"| Metric | Type | Unit | Description |\n| :-------- | :--- | :--- | :---------- |\n| <span class=\"break-all\">endpoint_accepted_connections</span> | counter | count | Number of incoming accepted client connections |\n| endpoint_client_connections | counter | count | Number of client connection establishment events |\n| endpoint_client_disconnections | counter | count | Number of client disconnections initiated by the client |\n| <span class=\"break-all\">endpoint_client_connection_expired</span> | counter | count | Total number of client connections with expired TTL (Time To Live) |\n| <span class=\"break-all\">endpoint_client_establishment_failures</span> | counter | count | Number of client connections that failed to establish properly |\n| <span class=\"break-all\">endpoint_client_expiration_refresh</span> | counter | count | Number of expiration time changes of clients |\n| <span class=\"break-all\">endpoint_client_tracking_off_requests</span> | counter | count | Total number of `CLIENT TRACKING OFF` requests |\n| <span class=\"break-all\">endpoint_client_tracking_on_requests</span> | counter | count | Total number of `CLIENT TRACKING ON` requests |\n| <span class=\"break-all\">endpoint_connections_rate</span> | gauge | connections/second | The rate of incoming connections. Computed as `n_accepted / N` for the last interval where `n_accepted` is the number of accepted connections in this interval, and `N` is the interval in seconds. |\n| <span class=\"break-all\">endpoint_disconnected_cba_client</span> | counter | count | Number of certificate-based clients disconnected |\n| <span class=\"break-all\">endpoint_disconnected_ldap_client</span> | counter | count | Number of LDAP clients disconnected |\n| <span class=\"break-all\">endpoint_disconnected_user_password_client</span> | counter | count | Number of user&password clients disconnected |\n| <span class=\"break-all\">endpoint_dispatch_failures</span> | counter | count | Number of clients closed due to failure to be dispatched to workers |\n| <span class=\"break-all\">endpoint_disposed_commands_after_client_caching</span> | counter | count | Total number of client caching commands that were disposed due to misuse |\n| endpoint_egress | counter | bytes | Number of egress bytes |\n| endpoint_egress_pending | counter | bytes | Number of send-pending bytes |\n| <span class=\"break-all\">endpoint_egress_pending_discarded</span> | counter | bytes | Number of send-pending bytes that were discarded due to disconnection |\n| <span class=\"break-all\">endpoint_failed_cba_authentication</span> | counter | count | Number of clients that failed certificate-based authentication |\n| <span class=\"break-all\">endpoint_failed_ldap_authentication</span> | counter | count | Number of clients that failed LDAP authentication |\n| <span class=\"break-all\">endpoint_failed_user_password_authentication</span> | counter | count | Number of clients that failed user password authentication |\n| endpoint_ingress | counter | bytes | Number of ingress bytes |\n| <span class=\"break-all\">endpoint_longest_pipeline_histogram</span> | histogram | count | Tracks the distribution of longest observed pipeline lengths, where a pipeline is a sequence of client commands sent without waiting for responses. |\n| endpoint_other_requests | counter | count | Number of other requests |\n| <span class=\"break-all\">endpoint_other_requests_latency_histogram</span> | histogram | microseconds | Latency (in µs) histogram of other commands |\n| <span class=\"break-all\">endpoint_other_requests_latency_histogram_bucket</span> | histogram | microseconds | Latency histograms for commands other than read or write commands. Can be used to represent different latency percentiles.<br />p99.9 example:<br /><span class=\"break-all\">`histogram_quantile(0.999, sum(rate(endpoint_other_requests_latency_histogram_bucket{cluster=\"$cluster\", db=\"$db\"}[$__rate_interval]) ) by (le, db))`</span> |\n| endpoint_other_responses | counter | count | Number of other responses |\n| <span class=\"break-all\">endpoint_ping_failures</span> | gauge | count | Number of consecutive endpoint ping failures. Labels: endpoint_uid |\n| <span class=\"break-all\">endpoint_ping_failure_duration_seconds</span> | gauge | seconds | Duration of ongoing endpoint failures (0 when healthy). Labels: endpoint_uid |\n| endpoint_proxy_disconnections | counter | count | Number of client disconnections initiated by the proxy |\n| <span class=\"break-all\">endpoint_rate_limit_ok</span> | gauge | — | Rate limit status based on the last 2 intervals.<br />0 = rate limit was recently exceeded<br />1 = rate limit was not recently exceeded |\n| <span class=\"break-all\">endpoint_rate_limit_overflows</span> | counter | count | Total number of rate limit overflows |\n| endpoint_read_requests | counter | count | Number of read requests |\n| <span class=\"break-all\">endpoint_read_requests_latency_histogram</span> | histogram | microseconds | Latency (in µs) histogram of read commands |\n| <span class=\"break-all\">endpoint_read_requests_latency_histogram_bucket</span> | histogram | microseconds | Latency histograms for read commands. Can be used to represent different latency percentiles.<br />p99.9 example:<br /><span class=\"break-all\">`histogram_quantile(0.999, sum(rate(endpoint_read_requests_latency_histogram_bucket{cluster=\"$cluster\", db=\"$db\"}[$__rate_interval]) ) by (le, db))`</span> |\n| endpoint_read_responses | counter | count | Number of read responses |\n| <span class=\"break-all\">endpoint_successful_cba_authentication</span> | counter | count | Number of clients that successfully authenticated with certificate-based authentication |\n| <span class=\"break-all\">endpoint_successful_ldap_authentication</span> | counter | count | Number of clients that successfully authenticated with LDAP |\n| <span class=\"break-all\">endpoint_successful_user_password_authentication</span> | counter | count | Number of clients that successfully authenticated with user&password |\n| endpoint_write_requests | counter | count | Number of write requests |\n| <span class=\"break-all\">endpoint_write_requests_latency_histogram</span> | histogram | microseconds | Latency (in µs) histogram of write commands |\n| <span class=\"break-all\">endpoint_write_requests_latency_histogram_bucket</span> | histogram | microseconds | Latency histograms for write commands. Can be used to represent different latency percentiles.<br />p99.9 example:<br /><span class=\"break-all\">`histogram_quantile(0.999, sum(rate(endpoint_write_requests_latency_histogram_bucket{cluster=\"$cluster\", db=\"$db\"}[$__rate_interval]) ) by (le, db))`</span> |\n| endpoint_write_responses | counter | count | Number of write responses |\n| db_config | gauge | — | This is an information metric that holds database configuration within labels such as: db_name, db_version, db_port, tls_mode |\n| db_status | gauge | — | This is a status metric that reports on various database statuses: 0 = active, 1 = active-change-pending, 2 = pending, 3 = import-pending, 4 = delete-pending, 5 = recovery, 99 = unknown |\n| db_tags | gauge | — | Information metric that exposes database tags as labels; the value is always `1`. See [Database tags in metrics](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/db-tags-in-metrics). |"},{"id":"node-metrics","title":"Node metrics","role":"content","text":"| Metric | Type | Unit | Description |\n| :-------- | :--- | :--- | :---------- |\n| node_available_flash_bytes | gauge | bytes | Available flash in the node (bytes) |\n| <span class=\"break-all\">node_available_flash_no_overbooking_bytes</span> | gauge | bytes | Available flash in the node (bytes), without taking into account overbooking |\n| <span class=\"break-all\">node_available_memory_bytes</span> | gauge | bytes | Amount of free memory in the node (bytes) |\n| <span class=\"break-all\">node_available_memory_no_overbooking_bytes</span> | gauge | bytes | Available RAM in the node (bytes) without taking into account overbooking |\n| node_bigstore_free_bytes | gauge | bytes | Sum of free space of back-end flash (used by flash database's [BigRedis]) on all cluster nodes (bytes); returned only when BigRedis is enabled |\n| <span class=\"break-all\">node_cert_expires_in_seconds</span> | gauge | seconds | Certificate expiration (in seconds) per given node; read more about [certificates in Redis Software](https://redis.io/docs/latest/operate/rs/security/certificates) and [monitoring certificates](https://redis.io/docs/latest/operate/rs/security/certificates/monitor-certificates) |\n| <span class=\"break-all\">customer_managed_ine_certificates</span> | gauge | — | Indicates whether customer-provided internode encryption certificates are in use<br />0=No<br />1=Yes |\n| <span class=\"break-all\">node_ephemeral_storage_avail_bytes</span> | gauge | bytes | Disk space available to RLEC processes on configured ephemeral disk (bytes) |\n| <span class=\"break-all\">node_ephemeral_storage_free_bytes</span> | gauge | bytes | Free disk space on configured ephemeral disk (bytes) |\n| node_memory_MemFree_bytes | gauge | bytes | Free memory in the node (bytes) |\n| <span class=\"break-all\">node_persistent_storage_avail_bytes</span> | gauge | bytes | Disk space available to RLEC processes on configured persistent disk (bytes) |\n| <span class=\"break-all\">node_persistent_storage_free_bytes</span> | gauge | bytes | Free disk space on configured persistent disk (bytes) |\n| <span class=\"break-all\">node_provisional_flash_bytes</span> | gauge | bytes | Amount of flash available for new shards on this node, taking into account overbooking, max Redis servers, reserved flash, and provision and migration thresholds (bytes) |\n| <span class=\"break-all\">node_provisional_flash_no_overbooking_bytes</span> | gauge | bytes | Amount of flash available for new shards on this node, without taking into account overbooking, max Redis servers, reserved flash, and provision and migration thresholds (bytes) |\n| <span class=\"break-all\">node_provisional_memory_bytes</span> | gauge | bytes | Amount of RAM that is available for provisioning to databases out of the total RAM allocated for databases |\n| <span class=\"break-all\">node_provisional_memory_no_overbooking_bytes</span> | gauge | bytes | Amount of RAM that is available for provisioning to databases out of the total RAM allocated for databases, without taking into account overbooking |\n| node_metrics_up | gauge | — | Node is part of the cluster and is connected (1 = connected, 0 = not connected) |\n| <span class=\"break-all\">dmc_ping_failures</span> | gauge | count | Number of consecutive DMC ping failures |\n| <span class=\"break-all\">dmc_ping_failure_duration_seconds</span> | gauge | seconds | Duration of ongoing DMC failures (0 when healthy) |"},{"id":"cluster-metrics","title":"Cluster metrics","role":"content","text":"| Metric | Type | Unit | Description |\n| :-------- | :--- | :--- | :---------- |\n| <span class=\"break-all\">generation{node=<node_uid>}</span> | gauge | — | Current generation (state-version) number of the cluster watchdog on the specific node |\n| <span class=\"break-all\">has_quorum{node=<node_uid>, has_witness_disk=BOOL}</span> | gauge | — | Has_quorum = 1<br />No quorum = 0 |\n| <span class=\"break-all\">is_primary{node=<node_uid>}</span> | gauge | — | primary = 1<br />secondary = 0 |\n| <span class=\"break-all\">license_expiration_days</span> | gauge | days | Number of days until the license expires |\n| <span class=\"break-all\">license_shards_limit</span> | gauge | count | Total shard limit by the license by shard type (ram / flash) |\n| <span class=\"break-all\">total_live_nodes_count{node=<node_uid>}</span> | gauge | count | Number of live nodes |\n| <span class=\"break-all\">total_nodes_count{node=<node_uid>}</span> | gauge | count | Number of nodes |\n| <span class=\"break-all\">primary_selections_total{node=<node_uid>}</span> | counter | count | Monotonic counter for each selection process that started |\n| users_count | gauge | count | Current number of users on the cluster |"},{"id":"replication-metrics","title":"Replication metrics","role":"content","text":"| Metric | Type | Unit | Description |\n| :-------- | :--- | :--- | :---------- |\n| <span class=\"break-all\">database_syncer_config</span> | gauge | — | Used as a placeholder for configuration labels |\n| <span class=\"break-all\">database_syncer_current_status</span> | gauge | — | Syncer status for traffic; 0 = in-sync, 1 = out of sync |\n| <span class=\"break-all\">database_syncer_dst_connectivity_state</span> | gauge | — | Destination connectivity state (1 = connected, 0 = disconnected) |\n| <span class=\"break-all\">database_syncer_dst_connectivity_state_ms</span> | gauge | milliseconds | Destination connectivity state duration |\n| <span class=\"break-all\">database_syncer_dst_lag</span> | gauge | milliseconds | Lag in milliseconds between the syncer and the destination |\n| <span class=\"break-all\">database_syncer_dst_repl_offset</span> | gauge | bytes | Offset of the last command acknowledged |\n| <span class=\"break-all\">database_syncer_flush_counter</span> | gauge | count | Number of destination flushes |\n| <span class=\"break-all\">database_syncer_ingress_bytes</span> | gauge | bytes | Number of bytes read from source shard |\n| <span class=\"break-all\">database_syncer_ingress_bytes_decompressed</span> | gauge | bytes | Number of bytes read from source shard |\n| <span class=\"break-all\">database_syncer_internal_state</span> | gauge | — | Internal state of the syncer |\n| <span class=\"break-all\">database_syncer_lag_ms</span> | gauge | milliseconds | Lag time between the source and the destination for traffic in milliseconds |\n| <span class=\"break-all\">database_syncer_rdb_size</span> | gauge | bytes | The source's RDB size in bytes to be transferred during the syncing phase |\n| <span class=\"break-all\">database_syncer_rdb_transferred</span> | gauge | bytes | Number of bytes transferred from the source's RDB during the syncing phase |\n| <span class=\"break-all\">database_syncer_src_connectivity_state</span> | gauge | — | Source connectivity state (1 = connected, 0 = disconnected) |\n| <span class=\"break-all\">database_syncer_src_connectivity_state_ms</span> | gauge | milliseconds | Source connectivity state duration |\n| <span class=\"break-all\">database_syncer_src_repl_offset</span> | gauge | bytes | Last known source offset |\n| <span class=\"break-all\">database_syncer_state</span> | gauge | — | Internal state of the shard syncer |\n| <span class=\"break-all\">database_syncer_syncer_repl_offset</span> | gauge | bytes | Offset of the last command handled by the syncer |\n| <span class=\"break-all\">database_syncer_total_requests</span> | gauge | count | Number of destination writes |\n| <span class=\"break-all\">database_syncer_total_responses</span> | gauge | count | Number of destination writes acknowledged |"},{"id":"shard-metrics","title":"Shard metrics","role":"content","text":"| Metric | Type | Unit | Description |\n| :-------- | :--- | :--- | :---------- |\n| redis_server_active_defrag_running | gauge | percent | Automatic memory defragmentation current aggressiveness (% cpu) |\n| redis_server_allocator_active | gauge | bytes | Total used memory, including external fragmentation |\n| redis_server_allocator_allocated | gauge | bytes | Total allocated memory |\n| redis_server_allocator_resident | gauge | bytes | Total resident memory (RSS) |\n| redis_server_aof_last_cow_size | gauge | bytes | Last AOFR, CopyOnWrite memory |\n| <span class=\"break-all\">redis_server_aof_rewrite_in_progress</span> | gauge | — | Indicates whether an AOF rewrite is in progress (1 = in progress, 0 = otherwise) |\n| redis_server_aof_rewrites | gauge | count | Number of AOF rewrites this process executed |\n| redis_server_aof_delayed_fsync | gauge | count | Number of times an AOF fsync caused delays in the main Redis thread (inducing latency); this can indicate that the disk is slow or overloaded |\n| redis_server_blocked_clients | gauge | count | Count the clients waiting on a blocking call |\n| redis_server_connected_clients | gauge | count | Number of client connections to the specific shard |\n| redis_server_connected_slaves | gauge | count | Number of connected replicas |\n| redis_server_db_avg_ttl | gauge | milliseconds | Average TTL of all volatile keys |\n| redis_server_db0_avg_ttl | gauge | milliseconds | Average TTL of all volatile keys. Deprecated. |\n| redis_server_db_keys | gauge | count | Total key count. |\n| redis_server_db0_keys | gauge | count | Total key count. Deprecated. |\n| redis_server_evicted_keys | gauge | count | Keys evicted so far (since restart) |\n| <span class=\"break-all\">redis_server_expire_cycle_cpu_milliseconds</span> | gauge | milliseconds | The cumulative amount of time spent on active expiry cycles |\n| redis_server_expired_keys | gauge | count | Keys expired so far since restart |\n| redis_server_forwarding_state | gauge | — | Shard forwarding state (1 = on, 0 = off) |\n| redis_server_hashes_items_under_1M | gauge | count | Number of hash keys with under 1 million elements |\n| redis_server_hashes_items_1M_to_8M | gauge | count | Number of hash keys with an element count between 1 million and 8 million |\n| redis_server_hashes_items_over_8M | gauge | count | Number of hash keys with over 8 million elements |\n| redis_server_keys_trimmed | gauge | count | The number of keys that were trimmed in the current or last resharding process |\n| redis_server_keyspace_read_hits | gauge | count | Number of read operations accessing an existing keyspace |\n| redis_server_keyspace_read_misses | gauge | count | Number of read operations accessing a non-existing keyspace |\n| redis_server_keyspace_write_hits | gauge | count | Number of write operations accessing an existing keyspace |\n| <span class=\"break-all\">redis_server_keyspace_write_misses</span> | gauge | count | Number of write operations accessing a non-existing keyspace |\n| redis_server_lists_items_under_1M | gauge | count | Number of list keys with under 1 million elements |\n| redis_server_lists_items_1M_to_8M | gauge | count | Number of list keys with an element count between 1 million and 8 million |\n| redis_server_lists_items_over_8M | gauge | count | Number of list keys with over 8 million elements |\n| redis_server_master_link_status | gauge | — | Indicates whether the replica is connected to its master (1 = up, 2 = down, 3 = none, 99 = unknown) |\n| redis_server_master_repl_offset | gauge | bytes | Number of bytes sent to replicas by the shard; calculate the throughput for a time period by comparing the value at different times |\n| <span class=\"break-all\">redis_server_master_sync_in_progress</span> | gauge | — | The primary shard is synchronizing (1 true; 0 false) |\n| redis_server_max_process_mem | gauge | bytes | Current memory limit configured by redis_mgr according to node free memory |\n| redis_server_maxmemory | gauge | bytes | Current memory limit configured by redis_mgr according to database memory limits. <br /><br />To calculate the percent memory usage:<br /><span class=\"break-all\">`sum by (cluster,db)(redis_server_used_memory{role=\"master\"}) / (avg by(cluster,db)(db_memory_limit_bytes) / max by(cluster,db)(db_replication_factor))`</span> |\n| redis_server_mem_aof_buffer | gauge | bytes | Current size of AOF buffer |\n| redis_server_mem_clients_normal | gauge | bytes | Current memory used for input and output buffers of non-replica clients |\n| redis_server_mem_clients_slaves | gauge | bytes | Current memory used for input and output buffers of replica clients |\n| <span class=\"break-all\">redis_server_mem_fragmentation_ratio</span> | gauge | ratio | Memory fragmentation ratio (1.3 means 30% overhead) |\n| <span class=\"break-all\">redis_server_mem_not_counted_for_evict</span> | gauge | bytes | Portion of used_memory (in bytes) that's not counted for eviction and OOM error |\n| <span class=\"break-all\">redis_server_mem_replication_backlog</span> | gauge | bytes | Size of replication backlog |\n| <span class=\"break-all\">redis_server_slave_buffer</span> | gauge | bytes | Reports the effective replica output buffer hard limit for a shard, derived from the database slave_buffer configuration. For slave_buffer=auto, the hard limit is calculated as used_memory * auto_slavebuf_ratio / 100, then bounded by auto_slavebuf_min and auto_slavebuf_max. |\n| <span class=\"break-all\">redis_server_module_fork_in_progress</span> | gauge | — | A binary value that indicates if there is an active fork spawned by a module (1) or not (0) |\n| <span class=\"break-all\">namedprocess_namegroup_cpu_seconds_total</span> | counter | seconds | Shard process CPU usage in seconds |\n| <span class=\"break-all\">namedprocess_namegroup_thread_cpu_seconds_total</span> | counter | seconds | Shard main thread CPU time spent in seconds |\n| <span class=\"break-all\">namedprocess_namegroup_open_filedesc</span> | gauge | count | Shard number of open file descriptors |\n| <span class=\"break-all\">namedprocess_namegroup_memory_bytes</span> | gauge | bytes | Shard memory size in bytes |\n| <span class=\"break-all\">namedprocess_namegroup_oldest_start_time_seconds</span> | gauge | timestamp seconds | Shard start time of the process since unix epoch in seconds |\n| <span class=\"break-all\">redis_server_rdb_bgsave_in_progress</span> | gauge | — | Indicates whether bgsave is currently in progress (1 = in progress, 0 = otherwise) |\n| redis_server_rdb_last_cow_size | gauge | bytes | Last bgsave (or SYNC fork) used CopyOnWrite memory |\n| redis_server_rdb_saves | gauge | count | Total count of bgsaves since the process was restarted (including replica fullsync and persistence) |\n| redis_server_sets_items_under_1M | gauge | count | Number of set keys with under 1 million elements |\n| redis_server_sets_items_1M_to_8M | gauge | count | Number of set keys with an element count between 1 million and 8 million |\n| redis_server_sets_items_over_8M | gauge | count | Number of set keys with over 8 million elements |\n| redis_server_repl_touch_bytes | gauge | bytes | Number of bytes sent to replicas as TOUCH commands by the shard as a result of a READ command that was processed; calculate the throughput for a time period by comparing the value at different times |\n| <span class=\"break-all\">redis_server_total_commands_processed</span> | gauge | count | Number of commands processed by the shard; calculate the number of commands for a time period by comparing the value at different times |\n| <span class=\"break-all\">redis_server_total_connections_received</span> | gauge | count | Number of connections received by the shard; calculate the number of connections for a time period by comparing the value at different times |\n| redis_server_total_net_input_bytes | gauge | bytes | Number of bytes received by the shard; calculate the throughput for a time period by comparing the value at different times |\n| <span class=\"break-all\">redis_server_total_net_output_bytes</span> | gauge | bytes | Number of bytes sent by the shard; calculate the throughput for a time period by comparing the value at different times |\n| redis_server_up | gauge | — | Shard is up and running (1 = up, 0 = down) |\n| <span class=\"break-all\">redis_server_strings_sizes_under_128M</span> | gauge | count | Number of string keys with a memory size under 128 megabytes |\n| <span class=\"break-all\">redis_server_strings_sizes_128M_to_512M</span> | gauge | count | Number of string keys with a memory size between 128 and 512 megabytes |\n| <span class=\"break-all\">redis_server_strings_sizes_over_512M</span> | gauge | count | Number of string keys with a memory size over 512 megabytes |\n| redis_server_used_memory | gauge | bytes | Memory used by shard (in BigRedis this includes flash) (bytes) |\n| redis_server_zsets_items_under_1M | gauge | count | Number of sorted set keys with under 1 million elements |\n| redis_server_zsets_items_1M_to_8M | gauge | count | Number of sorted set keys with an element count between 1 million and 8 million |\n| redis_server_zsets_items_over_8M | gauge | count | Number of sorted set keys with over 8 million elements |\n| <span class=\"break-all\">redis_server_search_gc_bytes_collected</span> | gauge | bytes | The total amount of memory freed by the garbage collectors from indexes in the shard's memory in bytes. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_bytes_collected</span> | gauge | bytes | The total amount of memory freed by the garbage collectors from indexes in the shard's memory in bytes. Deprecated in 8.0 (renamed <span class=\"break-all\">redis_server_search_gc_bytes_collected</span>), but still available in older versions. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_gc_marked_deleted_vectors</span> | gauge | count | The number of vectors marked as deleted in the vector indexes that have not yet been cleaned. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_marked_deleted_vectors</span> | gauge | count | The number of vectors marked as deleted in the vector indexes that have not yet been cleaned. Deprecated in 8.0 (renamed <span class=\"break-all\">redis_server_search_gc_marked_deleted_vectors</span>), but still available in older versions. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_gc_total_cycles</span> | gauge | count | The total number of garbage collection cycles executed. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_total_cycles</span> | gauge | count | The total number of garbage collection cycles executed. Deprecated in 8.0 (renamed <span class=\"break-all\">redis_server_search_gc_total_cycles</span>), but still available in older versions. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_gc_total_docs_not_collected_by_gc</span> | gauge | count | The number of documents marked as deleted, whose memory has not yet been freed by the garbage collector. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_total_docs_not_collected_by_gc</span> | gauge | count | The number of documents marked as deleted, whose memory has not yet been freed by the garbage collector. Deprecated in 8.0 (renamed <span class=\"break-all\">redis_server_search_gc_total_docs_not_collected_by_gc</span>), but still available in older versions. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_gc_total_ms_run</span> | gauge | milliseconds | The total duration of all garbage collection cycles in the shard, measured in milliseconds. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_total_ms_run</span> | gauge | milliseconds | The total duration of all garbage collection cycles in the shard, measured in milliseconds. Deprecated in 8.0 (renamed <span class=\"break-all\">redis_server_search_gc_total_ms_run</span>), but still available in older versions. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_cursors_internal_idle</span> | gauge | count | The total number of coordinator cursors that are currently holding pending results in the shard. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_cursors_user_idle</span> | gauge | count | The total number of cursors that were explicitly requested by users, that are currently holding pending results in the shard. <sup>[4](#tnote-4)</sup> |\n| redis_server_search_global_idle | gauge | count | The total number of user and internal cursors currently holding pending results in the shard. Deprecated in 8.0 (split into <span class=\"break-all\">redis_server_search_cursors_internal_idle</span> and <span class=\"break-all\">redis_server_search_cursors_user_idle</span>), but still available in older versions. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_cursors_internal_active</span> | gauge | count | The total number of coordinator cursors in the shard, either holding pending results or actively executing `FT.CURSOR READ`. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_cursors_user_active</span> | gauge | count | The total number of user cursors in the shard, either holding pending results or actively executing `FT.CURSOR READ`. <sup>[4](#tnote-4)</sup> |\n| redis_server_search_global_total | gauge | count | The total number of user and internal cursors in the shard, either holding pending results or actively executing `FT.CURSOR READ`. Deprecated in 8.0 (split into <span class=\"break-all\">redis_server_search_cursors_internal_active</span> and <span class=\"break-all\">redis_server_search_cursors_user_active</span>), but still available in older versions. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_number_of_indexes</span> | gauge | count | Total number of indexes in the shard <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_number_of_active_indexes</span> | gauge | count | The total number of indexes running a background indexing and/or background query processing operation. Background indexing refers to vector ingestion process, or in-progress background indexer. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_total_num_docs_in_indexes</span> | gauge | count | The total number of documents currently indexed across all indexes in the shard. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_number_of_active_indexes_running_queries</span> | gauge | count | Total count of indexes currently running a background query process. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_number_of_active_indexes_indexing</span> | gauge | count | Total count of indexes currently undergoing a background indexing process. Background indexing refers to vector ingestion process, or in-progress background indexer. This metric is limited by the number of WORKER threads allocated for writing operations + the number of indexes. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_total_active_write_threads</span> | gauge | count | Total count of background write (indexing) processes currently running in the shard. Background indexing refers to vector ingestion process, or in-progress background indexer. This metric is limited by the number of threads allocated for writing operations. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_text_Text</span> | gauge | count | The total number of `TEXT` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_text_Sortable</span> | gauge | count | The total number of `SORTABLE TEXT` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_text_NoIndex</span> | gauge | count | The total number of `NOINDEX TEXT` fields across all indexes in the shard; i.e., used for sorting only but not indexed. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_text_IndexErrors</span> | gauge | count | The total number of indexing failures caused by attempts to index a document containing a `TEXT` field. This field appears only if `TEXT` fields exist. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_numeric_Numeric</span> | gauge | count | The total number of `NUMERIC` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_numeric_Sortable</span> | gauge | count | The total number of `SORTABLE NUMERIC` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_numeric_NoIndex</span> | gauge | count | The total number of `NOINDEX NUMERIC` fields across all indexes in the shard, which are used for sorting only but not indexed. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_numeric_IndexErrors</span> | gauge | count | The total number of indexing failures caused by attempts to index a document containing a `NUMERIC` field. This field appears only if `NUMERIC` fields exist. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_tag_Tag</span> | gauge | count | The total number of `TAG` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_tag_Sortable</span> | gauge | count | The total number of `SORTABLE TAG` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_tag_NoIndex</span> | gauge | count | The total number of `NOINDEX TAG` fields across all indexes in the shard; i.e., used for sorting only but not indexed. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_tag_CaseSensitive</span> | gauge | count | The total number of `CASESENSITIVE TAG` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_tag_IndexErrors</span> | gauge | count | The total number of indexing failures caused by attempts to index a document containing a `TAG` field. This field appears only if `TAG` fields exist. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_geo_Geo</span> | gauge | count | The total number of `GEO` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_geo_Sortable</span> | gauge | count | The total number of `SORTABLE GEO` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_geo_NoIndex</span> | gauge | count | The total number of `NOINDEX GEO` fields across all indexes in the shard; i.e., used for sorting only but not indexed. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_geo_IndexErrors</span> | gauge | count | The total number of indexing failures caused by attempts to index a document containing a `GEO` field. This field appears only if `GEO` fields exist. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_vector_Vector</span> | gauge | count | The total number of `VECTOR` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_vector_Flat</span> | gauge | count | The total number of `FLAT VECTOR` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_vector_HNSW</span> | gauge | count | The total number of `HNSW VECTOR` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_vector_SVS_VAMANA</span> | gauge | count | The total number of `SVS-VAMANA VECTOR` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_vector_SVS_VAMANA_Compressed</span> | gauge | count | The total number of `SVS-VAMANA VECTOR` fields with `COMPRESSION` enabled (e.g., `LVQ8`) across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_vector_IndexErrors</span> | gauge | count | The total number of indexing failures caused by attempts to index a document containing a `VECTOR` field. This field appears only if `VECTOR` fields exist. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_geoshape_Geoshape</span> | gauge | count | The total number of `GEOSHAPE` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_geoshape_Sortable</span> | gauge | count | The total number of `SORTABLE GEOSHAPE` fields across all indexes in the shard. This field appears only if its value is larger than 0. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_geoshape_NoIndex</span> | gauge | count | The total number of `NOINDEX GEOSHAPE` fields across all indexes in the shard; i.e., used for sorting only but not indexed. This field appears only if its value is larger than 0. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_fields_geoshape_IndexErrors</span> | gauge | count | The total number of indexing failures caused by attempts to index a document containing a `GEOSHAPE` field. This field appears only if `GEOSHAPE` fields exist. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_total_indexing_ops_text_fields</span> | gauge | count | The total number of indexing operations performed on `TEXT` fields across all indexes in the shard. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_total_indexing_ops_tag_fields</span> | gauge | count | The total number of indexing operations performed on `TAG` fields across all indexes in the shard. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_total_indexing_ops_numeric_fields</span> | gauge | count | The total number of indexing operations performed on `NUMERIC` fields across all indexes in the shard. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_total_indexing_ops_geo_fields</span> | gauge | count | The total number of indexing operations performed on `GEO` fields across all indexes in the shard. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_total_indexing_ops_geoshape_fields</span> | gauge | count | The total number of indexing operations performed on `GEOSHAPE` fields across all indexes in the shard. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_total_indexing_ops_vector_fields</span> | gauge | count | The total number of indexing operations performed on `VECTOR` fields across all indexes in the shard. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_used_memory_indexes</span> | gauge | bytes | The total memory allocated by all indexes in the shard in bytes. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_smallest_memory_index</span> | gauge | bytes | The memory usage of the index with the smallest memory usage in the shard in bytes. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_largest_memory_index</span> | gauge | bytes | The memory usage of the index with the largest memory usage in the shard in bytes. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_total_indexing_time</span> | gauge | milliseconds | The total time spent on indexing operations, excluding the background indexing of vectors in the `HNSW` graph. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_used_memory_vector_index</span> | gauge | bytes | The total memory usage of all vector indexes in the shard. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_total_queries_processed</span> | gauge | count | The total number of successful query executions (When using cursors, not counting reading from existing cursors) in the shard. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_total_query_commands</span> | gauge | count | The total number of successful query command executions (including `FT.SEARCH`, `FT.AGGREGATE`, and `FT.CURSOR READ`). <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_total_query_execution_time_ms</span> | gauge | milliseconds | The cumulative execution time of all query commands, including `FT.SEARCH`, `FT.AGGREGATE`, and `FT.CURSOR READ`, measured in ms. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_total_active_queries</span> | gauge | count | The total number of background queries currently being executed in the shard, excluding `FT.CURSOR READ`. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_total_coord_dispatch_time_ms</span> | gauge | milliseconds | The cumulative time the coordinator spent building and dispatching query commands (`FT.SEARCH`, `FT.AGGREGATE`, and `FT.HYBRID`) to the shards, measured in ms. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_errors_indexing_failures</span> | gauge | count | The total number of indexing failures recorded across all indexes in the shard. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_errors_for_index_with_max_failures</span> | gauge | count | The number of indexing failures in the index with the highest count of failures. <sup>[1](#tnote-1)</sup> |\n| <span class=\"break-all\">redis_server_search_OOM_indexing_failures_indexes_count</span> | gauge | count | The count of indexes that experienced out-of-memory (OOM) failures during background indexing. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_shard_total_query_errors_syntax</span> | gauge | count | The total number of query syntax errors occurred in the shard. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_shard_total_query_errors_arguments</span> | gauge | count | The total number of queries in the shard that failed due to missing or invalid arguments. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_shard_total_query_errors_timeout</span> | gauge | count | The total number of query timeout errors occurred in the shard (when timeout policy is 'fail'). <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_shard_total_query_warnings_timeout</span> | gauge | count | The total number of query timeout warnings occurred in the shard (when timeout policy is 'return partial results'). <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_shard_total_query_errors_oom</span> | gauge | count | The total number of query out-of-memory errors occurred in the shard. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_shard_total_query_warnings_oom</span> | gauge | count | The total number of query out-of-memory warnings occurred in the shard. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_shard_total_query_warnings_max_prefix_expansions</span> | gauge | count | The total number of max prefix expansion warnings occurred in the shard. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_coord_total_query_errors_syntax</span> | gauge | count | The total number of query syntax errors occurred at the coordinator. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_coord_total_query_errors_arguments</span> | gauge | count | The total number of query argument errors encountered by the shard's coordinator. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_coord_total_query_errors_timeout</span> | gauge | count | The total number of query timeout errors encountered by the shard's coordinator (when timeout policy is 'fail'). <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_coord_total_query_warnings_timeout</span> | gauge | count | The total number of query timeout warnings encountered by the shard's coordinator (when timeout policy is 'return partial results'). <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_coord_total_query_errors_oom</span> | gauge | count | The total number of query out-of-memory errors encountered by the shard's coordinator. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_coord_total_query_warnings_oom</span> | gauge | count | The total number of query out-of-memory warnings encountered by the shard's coordinator. <sup>[4](#tnote-4)</sup> |\n| <span class=\"break-all\">redis_server_search_coord_total_query_warnings_max_prefix_expansions</span> | gauge | count | The total number of max prefix expansion warnings encountered by the shard's coordinator. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_uv_threads_running_queries</span> | gauge | count | The number of I/O threads currently handling query distribution to shards in cluster environments. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_uv_threads_running_topology_update</span> | gauge | count | The number of UV threads currently running topology updates. <sup>[3](#tnote-3)</sup> |\n| <span class=\"break-all\">redis_server_search_active_worker_threads</span> | gauge | count | The number of active worker threads. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_active_coord_threads</span> | gauge | count | The number of active coordinator threads. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_workers_low_priority_pending_jobs</span> | gauge | count | The number of pending low-priority jobs in worker threads, such as vector background indexing, graph updates, and vector garbage collection in graph-based indexes. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_workers_high_priority_pending_jobs</span> | gauge | count | The number of pending high-priority jobs in worker threads, such as query execution. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">redis_server_search_workers_admin_priority_pending_jobs</span> | gauge | count | The number of pending admin-priority jobs in worker threads, such as threadpool configuration changes. <sup>[3](#tnote-3)</sup> |\n| <span class=\"break-all\">redis_server_search_coord_high_priority_pending_jobs</span> | gauge | count | The number of pending jobs in the coordinator thread queue. Coordinator threads only have a high-priority queue and are primarily used for query distribution. <sup>[2](#tnote-2)</sup> |\n| <span class=\"break-all\">shard_ping_failures</span> | gauge | count | Number of consecutive ping failures for a shard. Labels: shard_uid, role |\n| <span class=\"break-all\">shard_ping_failure_duration_seconds</span> | gauge | seconds | Duration of ongoing failures (0 when healthy). Labels: shard_uid, role |\n\n1. <a name=\"tnote-1\"></a> Available since RediSearch 2.6.\n2. <a name=\"tnote-2\"></a> Available since RediSearch 2.8.\n3. <a name=\"tnote-3\"></a> Available since RediSearch 2.10.\n4. <a name=\"tnote-4\"></a> Available since RediSearch 8.0."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/metrics/resource-usage","title":"Resource usage metrics","url":"https://redis.io/docs/latest/operate/rs/references/metrics/resource-usage/","summary":"","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:21+01:00","page_type":"content","content_hash":"8ce542f5296ebebee64668e45a113cc4b5ecaef6ac2b95c910f59d2f661706eb","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The following metrics are available in the Redis Software Cluster Manager UI."},{"id":"connections","title":"Connections","role":"content","text":"Number of connections to the database.\n\n**Components measured**: Cluster, Node, and Database"},{"id":"cpu-usage","title":"CPU usage","role":"content","text":"Percent of the node CPU used. \n\n**Components measured**:  Cluster and Node"},{"id":"main-thread-cpu-usage","title":"Main thread CPU usage","role":"content","text":"Percent of the CPU used by the main thread.\n\n**Components measured**: Database and Shard"},{"id":"fork-cpu-usage","title":"Fork CPU usage","role":"content","text":"CPU usage of Redis child forks.\n\n**Components measured**: Database and Shard"},{"id":"total-cpu-usage","title":"Total CPU usage","role":"content","text":"Percent usage of the CPU for all nodes.\n\n**Components measured**: Database"},{"id":"free-disk-space","title":"Free disk space","role":"content","text":"Remaining unused disk space.\n\n**Components measured**:  Cluster and Node"},{"id":"memory","title":"Memory","role":"content","text":""},{"id":"used-memory","title":"Used memory","role":"content","text":"Total memory used by the database, including RAM, [Flash](https://redis.io/docs/latest/operate/rs/databases/flash) (if enabled), and [replication](https://redis.io/docs/latest/operate/rs/databases/durability-ha/replication) (if enabled).\n\nUsed memory does not include:\n\n1. Fragmentation overhead - The ratio of memory seen by the operating system to memory allocated by Redis\n2. Replication buffers at the primary nodes - Set to 10% of used memory and is between 64 MB and 2048 MB\n3. Memory used by Lua scripts - Does not exceed 1 MB\n4. Copy on Write (COW) operation that can be triggered by:\n    - A full replication process\n    - A database snapshot process\n    - AOF rewrite process\n\nUsed memory is not measured during [shard migration](https://redis.io/docs/latest/operate/rs/databases/configure/replica-ha).\n\nAdditional factors for [Active-Active databases](https://redis.io/docs/latest/operate/rs/databases/active-active):\n\n- By default, the [Active-Active replication backlog](https://redis.io/docs/latest/operate/rs/databases/active-active/manage#replication-backlog), used for synchronization between clusters or regions, is set to 1% of the database size.\n\n- Active-Active databases have a lower threshold for activating the eviction policy, because it requires propagation to all participating clusters. The eviction policy starts to evict keys when one of the Active-Active instances reaches 80% of its memory limit.\n\n**Components measured**: Database and Shard"},{"id":"free-ram","title":"Free RAM","role":"content","text":"Available RAM for System use.\n\n**Components measured**:  Cluster and Node"},{"id":"memory-limit","title":"Memory limit","role":"content","text":"Memory size limit of the database, enforced on the [used memory](#used-memory).\n\n**Components measured**: Database"},{"id":"memory-usage","title":"Memory usage","role":"content","text":"Percent of memory used by Redis out of the [memory limit](#memory-limit).\n\n**Components measured**: Database"},{"id":"traffic","title":"Traffic","role":"content","text":""},{"id":"incoming-traffic","title":"Incoming traffic","role":"content","text":"Total incoming traffic to the database in bytes/sec.\n\nAll incoming traffic is not measured during [shard migration](https://redis.io/docs/latest/operate/rs/databases/configure/replica-ha).\n\n**Components measured**: Cluster, Node and Database\n\n#### Incoming traffic compressed\n\nTotal incoming compressed traffic (in bytes/sec) per [Active-Active](https://redis.io/docs/latest/operate/rs/databases/active-active) replica database. \n\n#### Incoming traffic uncompressed\n\nTotal incoming uncompressed traffic (in bytes/sec) per [Active-Active](https://redis.io/docs/latest/operate/rs/databases/active-active) replica database."},{"id":"outgoing-traffic","title":"Outgoing traffic","role":"content","text":"Total outgoing traffic from the database in bytes per second.\n\nOutgoing traffic is not measured during [shard migration](https://redis.io/docs/latest/operate/rs/databases/configure/replica-ha).\n\n**Components measured**: Cluster, Node and Database"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/api-reference","title":"Redis Software API","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/api-reference/","summary":"","tags":[],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"index","sections":[],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/encryption","title":"Encrypt REST API requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/encryption/","summary":"","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"9619f05e302a60f85ba411a9d0b3b854ccc337656a21397e17782c3e7f44eb5b","sections":[{"id":"require-https-for-api-endpoints","title":"Require HTTPS for API endpoints","role":"overview","text":"By default, the Redis Software API supports communication over HTTP and HTTPS. However, you can turn off support for HTTP to ensure that API requests are encrypted.\n\nBefore you turn off HTTP support, be sure to migrate any scripts or proxy configurations that use HTTP to the encrypted API endpoint to prevent broken connections.\n\nTo turn off HTTP support for API endpoints, run:\n\n[code example]"}],"examples":[{"id":"require-https-for-api-endpoints-ex0","language":"sh","code":"rladmin cluster config http_support disabled","section_id":"require-https-for-api-endpoints"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api","title":"REST API","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/","summary":"Documents the REST API available to Redis Software deployments.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","children":[{"id":"operate/rs/references/rest-api/quick-start","summary":"Redis Software REST API quick start","title":"Redis Software REST API quick start","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/quick-start/"},{"id":"operate/rs/references/rest-api/encryption","summary":"","title":"Encrypt REST API requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/encryption/"},{"id":"operate/rs/references/rest-api/requests","summary":"Documents the requests supported by the Redis Software REST API calls.","title":"Redis Software REST API requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/"},{"id":"operate/rs/references/rest-api/objects","summary":"Documents the objects used with Redis Software REST API calls.","title":"Redis Software REST API objects","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/"},{"id":"operate/rs/references/rest-api/permissions","summary":"Documents the permissions used with Redis Software REST API calls.","title":"Permissions","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/permissions/"},{"id":"operate/rs/references/rest-api/api-reference","summary":"","title":"Redis Software API","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/api-reference/"}],"page_type":"content","content_hash":"d1784a40bde36df041804aa5539b4cc01b1d67356ec540666b423041df959f38","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software provides a REST API to help you automate common tasks.\n\nHere, you'll find the details of the API and how to use it.  \n\nFor more info, see:\n\n- Supported [request endpoints](https://redis.io/docs/latest/operate/rs/references/rest-api/requests), organized by path\n- Supported [objects](https://redis.io/docs/latest/operate/rs/references/rest-api/objects), both request and response\n- Built-in roles and associated [permissions](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions)\n- [Redis Software REST API quick start](https://redis.io/docs/latest/operate/rs/references/rest-api/quick-start) with examples"},{"id":"authentication","title":"Authentication","role":"security","text":"Authentication to the Redis Software API occurs via [Basic Auth](https://en.wikipedia.org/wiki/Basic_access_authentication). Provide your username and password as the basic auth credentials.\n\nIf the username and password is incorrect or missing, the request will fail with a [`401 Unauthorized`](https://www.rfc-editor.org/rfc/rfc9110.html#name-401-unauthorized) status code.\n\nExample request using [cURL](https://curl.se/):\n\n[code example]\n\nFor more examples, see the [Redis Software REST API quick start](https://redis.io/docs/latest/operate/rs/references/rest-api/quick-start)."},{"id":"permissions","title":"Permissions","role":"security","text":"By default, the admin user is authorized for access to all endpoints. Use [role-based access controls](https://redis.io/docs/latest/operate/rs/security/access-control) and [role permissions](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions) to manage access.\n\nIf a user attempts to access an endpoint that is not allowed in their role, the request will fail with a [`403 Forbidden`](https://www.rfc-editor.org/rfc/rfc9110.html#name-403-forbidden) status code. For more details on which user roles can access certain endpoints, see [Permissions](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions)."},{"id":"certificates","title":"Certificates","role":"content","text":"The Redis Software REST API uses [Self-signed certificates](https://redis.io/docs/latest/operate/rs/security/certificates) to ensure the product is secure. When you use the default self-signed certificates, the HTTPS requests will fail with `SSL certificate problem: self signed certificate` unless you turn off SSL certificate verification."},{"id":"ports","title":"Ports","role":"content","text":"All calls must be made over SSL to port 9443. For the API to work, port 9443 must be exposed to incoming traffic or mapped to a different port.\n\nIf you are using a [Redis Software Docker image](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart), run the following command to start the Docker image with port 9443 exposed:\n\n[code example]"},{"id":"versions","title":"Versions","role":"compatibility","text":"All API requests are versioned in order to minimize the impact of backwards-incompatible API changes and to coordinate between different versions operating in parallel.\n\nSpecify the version in the request [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier), as shown in the following table:\n\n| Request path | Description |\n|--------------|-------------|\n| POST `/v1/bdbs` | A version 1 request for the `/bdbs` endpoint. |\n| POST `/v2/bdbs` | A version 2 request for the `/bdbs` endpoint. |\n\nWhen an endpoint supports multiple versions, each version is documented on the corresponding endpoint.  For example, the [bdbs request](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/) page documents POST requests for [version 1](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/#post-bdbs-v1) and [version 2](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/#post-bdbs-v2)."},{"id":"headers","title":"Headers","role":"content","text":""},{"id":"requests","title":"Requests","role":"content","text":"Redis Software REST API requests support the following HTTP headers:\n\n| Header | Supported/Required Values |\n|--------|---------------------------|\n| Accept | `application/json` |\n| Content-Length | Length (in bytes) of request message |\n| Content-Type | `application/json` (required for PUT or POST requests) |\n\nIf the client specifies an invalid header, the request will fail with a [`400 Bad Request`](https://www.rfc-editor.org/rfc/rfc9110.html#name-400-bad-request) status code."},{"id":"responses","title":"Responses","role":"returns","text":"Redis Software REST API responses support the following HTTP headers:\n\n| Header | Supported/Required Values |\n|--------|---------------------------|\n| Content-Type | `application/json` |\n| Content-Length | Length (in bytes) of response message |"},{"id":"json-requests-and-responses","title":"JSON requests and responses","role":"content","text":"The Redis Software REST API uses [JavaScript Object Notation (JSON)](http://www.json.org) for requests and responses. See the [RFC 4627 technical specifications](http://www.ietf.org/rfc/rfc4627.txt) for additional information about JSON.\n\nSome responses may have an empty body but indicate the response with standard [HTTP codes](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html).\n\nBoth requests and responses may include zero or more objects.\n\nIf the request is for a single entity, the response returns a single JSON object or none. If the request is for a list of entities, the response returns a JSON array with zero or more elements.\n\nIf you omit certain JSON object fields from a request, they may be assigned default values, which often indicate that these fields are not in use."},{"id":"response-types-and-error-codes","title":"Response types and error codes","role":"returns","text":"[HTTP status codes](https://www.rfc-editor.org/rfc/rfc9110.html#name-status-codes) indicate the result of an API request. This can be `200 OK` if the server accepted the request, or it can be one of many error codes.\n\nThe most common responses for a Redis Software API request are:\n\n| Response | Condition/Required handling |\n|----------|-----------------------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | Success |\n| [400 Bad Request](https://www.rfc-editor.org/rfc/rfc9110.html#name-400-bad-request) | The request failed, generally due to a typo or other mistake. |\n| [401 Unauthorized](https://www.rfc-editor.org/rfc/rfc9110.html#name-401-unauthorized) | The request failed because the authentication information was missing or incorrect. |\n| [403 Forbidden](https://www.rfc-editor.org/rfc/rfc9110.html#name-403-forbidden) | The user cannot access the specified [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier). |\n| [404 Not Found](https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found) | The [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) does not exist. |\n| [503 Service Unavailable](https://www.rfc-editor.org/rfc/rfc9110.html#name-503-service-unavailable) | The node is not responding or is not a member of the cluster. |\n| [505&nbsp;HTTP&nbsp;Version&nbsp;Not&nbsp;Supported](https://www.rfc-editor.org/rfc/rfc9110.html#name-505-http-version-not-suppor) | An unsupported `x-api-version` was used. See [versions](#versions). |\n\nSome endpoints return different response codes. The request references for these endpoints document these special cases."}],"examples":[{"id":"authentication-ex0","language":" bash","code":"curl -u \"demo@redislabs.com:password\" \\\n    https://localhost:9443/v1/bdbs","section_id":"authentication"},{"id":"ports-ex0","language":"sh","code":"docker run -p 9443:9443 redislabs/redis","section_id":"ports"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/action","title":"Action object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/action/","summary":"An object that represents cluster actions","tags":["docs","operate","rs"],"last_updated":"2026-02-09T09:23:00-06:00","page_type":"content","content_hash":"13169b47042290ec42b1494e54e8a444e3d68e57f7be60cf66eaae73f4cef914","sections":[{"id":"content","title":"Content","role":"content","text":"The cluster allows you to invoke general maintenance actions such as rebalancing or taking a node offline by moving all of its entities to other nodes.\n\nActions are implemented as tasks in the cluster. Every task has a unique `task_id` assigned by the cluster, a task name which describes the task, a status, and additional task-specific parameters.\n\nThe REST API provides a simplified interface that allows callers to invoke actions and query their status without a specific `task_id`.\n\nThe action lifecycle is based on the following status and status transitions:\n\n[code example]\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| progress        | float <nobr>(range: 0-100)</nobr> | Represents percent completed (As of v7.4.2, the return value type changed to 'float' to provide improved progress indication) |\n| status          | queued | Requested operation and added it to the queue to await processing |\n|                 | starting | Picked up operation from the queue and started processing |\n|                 | running | Currently executing operation |\n|                 | cancelling | Operation cancellation is in progress |\n|                 | cancelled | Operation cancelled |\n|                 | aborted | Operation aborted |\n|                 | completed | Operation completed |\n|                 | failed | Operation failed |\n\nWhen a task fails, the `error_code` and `error_message` fields describe the error.\n\nPossible `error_code` values:\n\n Code                    | Description                                    |\n|-------------------------|------------------------------------------------|\n| internal_error          | An internal error that cannot be mapped to a more precise error code\n| insufficient_resources  | The cluster does not have sufficient resources to complete the required operation"}],"examples":[{"id":"content-ex0","language":"mermaid","code":"stateDiagram-v2\n    queued --> starting\n    queued --> cancelled\n    starting --> running\n    starting --> aborted\n    running --> completed\n    running --> cancelling\n    running --> failed\n    running --> aborted\n    cancelling --> cancelled","section_id":"content"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/alert","title":"Alert object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/alert/","summary":"An object that contains alert info","tags":["docs","operate","rs"],"last_updated":"2023-12-14T15:27:49+01:00","page_type":"content","content_hash":"df2df1e9e05c09800a35196c6bbf7b3bc97fbfec9cf42c42f7aa9afecc0e7599","sections":[{"id":"content","title":"Content","role":"content","text":"You can view, configure, and enable various alerts for the cluster.\n\nAlerts are bound to a cluster object (such as a [BDB](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb) or [node](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/node)), and the cluster's state determines whether the alerts turn on or off.\n\n  Name  | Type/Value | Description | Writable\n|-------|------------|-------------|----------|\n| change_time | string | Timestamp when alert state last changed | |\n| change_value | object | Contains data relevant to the evaluation time when the alert went on/off (thresholds, sampled values, etc.) | |\n| enabled | boolean | If true, alert is enabled | x |\n| severity | 'DEBUG'<br />'INFO'<br />'WARNING'<br />'ERROR'<br />'CRITICAL' | The alert's severity | |\n| state | boolean | If true, alert is currently triggered | |\n| threshold | string | Represents an alert threshold when applicable | x |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/bdb/audit_settings","title":"","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/audit_settings/","summary":"","tags":[],"last_updated":"2026-07-21T11:19:25-05:00","page_type":"content","content_hash":"4cb9646f8c134ef235b9cde6a7c5ce04e5571c1352e289a94f85e07f80684d73","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Title: audit_settings\nalwaysopen: false\ncategories:\n- docs\n- operate\n- rs\ndescription: An object that configures per-database connection and command auditing\nlinkTitle: audit_settings\nweight: $10\n---\n\nConfigures per-database connection and command (CRUD) auditing"},{"id":"audit_settings-object","title":"audit_settings object","role":"content","text":"| Name | Type/Value | Description |\n|---|---|---|\n| audit_mode | string; Values: `'disabled'`, `'connection'`, `'connection_and_crud'`; default: `'disabled'` | Per-database auditing mode. `'disabled'`: no audit records are generated. `'connection'`: audits connection and AUTH events only. `'connection_and_crud'`: audits connection, AUTH, and CRUD command events. Cannot be set to `'connection_and_crud'` without connection auditing also active. |\n| username_filter | [complex object](#username_filter); default: disabled | Filters which client usernames are included in or excluded from CRUD audit records. |\n| source_ip_filter | [complex object](#source_ip_filter); default: disabled | Filters which client source IPs are included in or excluded from CRUD audit records. |\n| max_total_key_bytes | integer (range: 0+) ; default: `131072` | Maximum total key bytes captured in a single command audit record. |\n\nFilter changes take effect for new client connections only. Existing connections continue to be audited according to the filters that were active when the connection was established."},{"id":"username_filter-object","title":"username_filter object","role":"content","text":"Controls which client usernames are included in or excluded from CRUD audit records.\n\n| Name | Type/Value | Description |\n|---|---|---|\n| enabled | boolean; default: `false` | Enables the username filter. |\n| usernames | array of strings; default: `[]`; max 512 entries | Usernames to include or exclude, depending on `filter_type`. |\n| filter_type | string; Values: `'inclusive'`, `'exclusive'`; default: `'inclusive'` | `'inclusive'` audits only the listed usernames. `'exclusive'` audits all usernames except the listed ones. |\n| allow_while_username_unknown | boolean; default: `false` | Allows command auditing to proceed before the connection's username is known. |\n\n**Validation:**\n- Each username must be 1–255 characters, using printable ASCII characters only.\n- Usernames must not contain `&`, `<`, `>`, or double-quote characters.\n- If `enabled` is `true`, `usernames` must not be empty."},{"id":"source_ip_filter-object","title":"source_ip_filter object","role":"content","text":"Controls which client source IP addresses are included in or excluded from CRUD audit records.\n\n| Name | Type/Value | Description |\n|---|---|---|\n| enabled | boolean; default: `false` | Enables the source IP filter. |\n| ip_addresses | array of strings; default: `[]` | Bare IPv4 or IPv6 addresses. |\n| cidr_ranges | array of strings; default: `[]` | IPv4 or IPv6 CIDR ranges. |\n| filter_type | string; Values: `'inclusive'`, `'exclusive'`; default: `'inclusive'` | `'inclusive'` audits only matching IPs/ranges. `'exclusive'` audits all except matching IPs/ranges. |\n\n**Validation:**\n- `ip_addresses` entries must be bare IPv4 or IPv6 addresses (no ranges or CIDR notation).\n- `cidr_ranges` entries must be valid IPv4 or IPv6 CIDR ranges.\n- If `enabled` is `true`, at least one `ip_addresses` or `cidr_ranges` entry is required.\n- The combined number of `ip_addresses` and `cidr_ranges` entries cannot exceed 512."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/bdb/backup_location","title":"BDB backup/export location object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/backup_location/","summary":"Documents the bdb backup_location/export_location object used with Redis Enterprise Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-10-05T16:11:44+01:00","page_type":"content","content_hash":"431b34f9a9a8ad6a53e2e18ffd3eb0a86796f779a4f2c241d01492ebe6a289b9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can back up or export a database's dataset to the following types of locations:\n\n-   FTP/S\n-   SFTP\n-   Amazon S3\n-   Amazon S3 or S3-compatible storage\n-   Google Cloud Storage\n-   Microsoft Azure Storage\n-   NAS/Local Storage"},{"id":"basic-parameters","title":"Basic parameters","role":"content","text":"For all backup/export location objects, you need to specify the location type via the `type` field.\n\n| Location type | \"type\" value |\n|---------------|--------------|\n| FTP/S | \"url\" |\n| SFTP | \"sftp\" |\n| Amazon S3 | \"s3\" |\n| Google Cloud Storage | \"gs\" |\n| Microsoft Azure Storage | \"abs\" |\n| NAS/Local Storage | \"mount_point\" |"},{"id":"location-specific-parameters","title":"Location-specific parameters","role":"content","text":"Any additional required parameters may differ based on the backup/export location type."},{"id":"ftp","title":"FTP","role":"content","text":"| Key name | Type | Description |\n|----------|------|-------------|\n| url | string | A URI that represents a FTP/S location with the following format: `ftp://user:password@host:port/path/`. The user and password can be omitted if not needed. |"},{"id":"sftp","title":"SFTP","role":"content","text":"| Key name | Type | Description |\n|----------|------|-------------|\n| key | string | SSH private key to secure the SFTP server connection. If you do not specify an SSH private key, the autogenerated private key of the cluster is used, and you must add the SSH public key of the cluster to the SFTP server configuration. (optional) |\n| sftp_url | string | SFTP URL in the format: `sftp://user:password@host[:port][/path/]`. The default port number is 22 and the default path is '/'. |"},{"id":"aws-s3","title":"AWS S3","role":"content","text":"| Key name | Type | Description |\n|----------|------|-------------|\n| access_key_id | string | The AWS Access Key ID with access to the bucket |\n| bucket_name | string | S3 bucket name |\n| region_name | string | Amazon S3 region name (optional) |\n| secret_access_key | string | The AWS Secret Access Key that matches the Access Key ID |\n| subdir | string | Path to the backup directory in the S3 bucket (optional) |\n\nYou can also connect to a storage service that uses the S3 protocol but is not hosted by Amazon AWS. The storage service must have a valid SSL certificate.\n\nTo connect to an S3-compatible storage location:\n\n1. Configure the S3 URL with [`rladmin cluster config`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/config): \n\n    [code example]\n\n    Replace `<URL>` with the hostname or IP address of the S3-compatible storage location.\n\n1. Configure the S3 CA certificate:\n\n    [code example]\n\n    Replace `<filepath>` with the location of the S3 CA certificate `ca.pem`."},{"id":"google-cloud-storage","title":"Google Cloud Storage","role":"content","text":"| Key name | Type | Description |\n|----------|------|-------------|\n| bucket_name | string | Cloud Storage bucket name |\n| client_email | string | Email address for the Cloud Storage client ID |\n| client_id | string | Cloud Storage client ID with access to the Cloud Storage bucket |\n| private_key | string | Cloud Storage private key that matches the private key ID |\n| private_key_id | string | Cloud Storage private key ID with access to the Cloud Storage bucket |\n| subdir | string | Path to the backup directory in the Cloud Storage bucket (optional) |"},{"id":"azure-blob-storage","title":"Azure Blob Storage","role":"content","text":"| Key name | Type | Description |\n|----------|------|-------------|\n| account_key | string | Access key for the storage account |\n| account_name | string | Storage account name with access to the container |\n| container | string | Blob Storage container name |\n| sas_token | string | Token to authenticate with shared access signature |\n| subdir | string | Path to the backup directory in the Blob Storage container (optional) |\n\n> [!NOTE]\n> `account_key` and `sas_token` are mutually exclusive"},{"id":"naslocal-storage","title":"NAS/Local Storage","role":"content","text":"| Key name | Type | Description |\n|----------|------|-------------|\n| path | string | Path to the local mount point. You must create the mount point on all nodes, and the `redislabs:redislabs` user must have read and write permissions on the local mount point. |"}],"examples":[{"id":"aws-s3-ex0","language":"sh","code":"rladmin cluster config s3_url <URL>","section_id":"aws-s3"},{"id":"aws-s3-ex1","language":"sh","code":"rladmin cluster config s3_ca_cert <filepath>","section_id":"aws-s3"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/bdb/dataset_import_sources","title":"BDB dataset import sources object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/dataset_import_sources/","summary":"Documents the bdb dataset_import_sources object used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-10-05T16:11:44+01:00","page_type":"content","content_hash":"3cb10240b5eaaef163e3229e4bac61ce915f41718b90216c80508abc9866b67a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can import data to a database from the following location types:\n\n-   HTTP/S\n-   FTP\n-   SFTP\n-   Amazon S3\n-   Amazon S3 or S3-compatible storage\n-   Google Cloud Storage\n-   Microsoft Azure Storage\n-   NAS/Local Storage\n\nThe source file to import should be in the [RDB](https://redis.io/docs/latest/operate/rs/databases/configure/database-persistence.md) format. It can also be in a compressed (gz) RDB file.\n\nSupply an array of dataset import source objects to import data from multiple files."},{"id":"basic-parameters","title":"Basic parameters","role":"content","text":"For all import location objects, you need to specify the location type via the `type` field.\n\n| Location type | \"type\" value |\n|---------------|--------------|\n| FTP/S | \"url\" |\n| SFTP | \"sftp\" |\n| Amazon S3 | \"s3\" |\n| Google Cloud Storage | \"gs\" |\n| Microsoft Azure Storage | \"abs\" |\n| NAS/Local Storage | \"mount_point\" |"},{"id":"location-specific-parameters","title":"Location-specific parameters","role":"content","text":"Any additional required parameters may differ based on the import location type."},{"id":"ftp","title":"FTP","role":"content","text":"| Key name | Type | Description |\n|----------|------|-------------|\n| url | string | A URI that represents the FTP/S location with the following format: `ftp://user:password@host:port/path/`. The user and password can be omitted if not needed. |"},{"id":"sftp","title":"SFTP","role":"content","text":"| Key name | Type | Description |\n|----------|------|-------------|\n| key | string | SSH private key to secure the SFTP server connection. If you do not specify an SSH private key, the autogenerated private key of the cluster is used and you must add the SSH public key of the cluster to the SFTP server configuration. (optional) |\n| sftp_url | string | SFTP URL in the format: `sftp://user:password@host[:port]/path/filename.rdb`. The default port number is 22 and the default path is '/'. |"},{"id":"aws-s3","title":"AWS S3","role":"content","text":"| Key name | Type | Description |\n|----------|------|-------------|\n| access_key_id | string | The AWS Access Key ID with access to the bucket |\n| bucket_name | string | S3 bucket name |\n| filename | string | RDB filename, including the file extension. |\n| region_name | string | Amazon S3 region name (optional) |\n| secret_access_key | string | The AWS Secret Access that matches the Access Key ID |\n| subdir | string | Path to the backup directory in the S3 bucket (optional) |\n\nYou can also connect to a storage service that uses the S3 protocol but is not hosted by Amazon AWS. The storage service must have a valid SSL certificate.\n\nTo connect to an S3-compatible storage location:\n\n1. Configure the S3 URL with [`rladmin cluster config`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/config):\n\n    [code example]\n\n    Replace `<URL>` with the hostname or IP address of the S3-compatible storage location.\n\n1. Configure the S3 CA certificate:\n\n    [code example]\n\n    Replace `<filepath>` with the location of the S3 CA certificate `ca.pem`."},{"id":"google-cloud-storage","title":"Google Cloud Storage","role":"content","text":"| Key name | Type | Description |\n|----------|------|-------------|\n| bucket_name | string | Cloud Storage bucket name |\n| client_email | string | Email address for the Cloud Storage client ID |\n| client_id | string | Cloud Storage client ID with access to the Cloud Storage bucket |\n| filename | string | RDB filename, including the file extension. |\n| private_key | string | Private key for the Cloud Storage matching the private key ID |\n| private_key_id | string | Cloud Storage private key ID with access to the Cloud Storage bucket |\n| subdir | string | Path to the backup directory in the Cloud Storage bucket (optional) |"},{"id":"azure-blob-storage","title":"Azure Blob Storage","role":"content","text":"| Key name | Type | Description |\n|----------|------|-------------|\n| account_key | string | Access key for the storage account |\n| account_name | string | Storage account name with access to the container |\n| container | string | Blob Storage container name |\n| filename | string | RDB filename, including the file extension. |\n| sas_token | string | Token to authenticate with shared access signature |\n| subdir | string | Path to the backup directory in the Blob Storage container (optional) |\n\n> [!NOTE]\n> `account_key` and `sas_token` are mutually exclusive"},{"id":"naslocal-storage","title":"NAS/Local Storage","role":"content","text":"| Key name | Type | Description |\n|----------|------|-------------|\n| path | string | Path to the locally mounted filename to import. You must create the mount point on all nodes, and the `redislabs:redislabs` user must have read permissions on the local mount point."}],"examples":[{"id":"aws-s3-ex0","language":"sh","code":"rladmin cluster config s3_url <URL>","section_id":"aws-s3"},{"id":"aws-s3-ex1","language":"sh","code":"rladmin cluster config s3_ca_cert <filepath>","section_id":"aws-s3"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/bdb","title":"BDB object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/","summary":"An object that represents a database","tags":["docs","operate","rs"],"last_updated":"2026-07-21T11:19:25-05:00","children":[{"id":"operate/rs/references/rest-api/objects/bdb/audit_settings","summary":"","title":"","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/audit_settings/"},{"id":"operate/rs/references/rest-api/objects/bdb/backup_location","summary":"Documents the bdb backup_location/export_location object used with Redis Enterprise Software REST API calls.","title":"BDB backup/export location object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/backup_location/"},{"id":"operate/rs/references/rest-api/objects/bdb/dataset_import_sources","summary":"Documents the bdb dataset_import_sources object used with Redis Software REST API calls.","title":"BDB dataset import sources object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/dataset_import_sources/"},{"id":"operate/rs/references/rest-api/objects/bdb/probabilistic","summary":"Configuration object for probabilistic data structures","title":"Probabilistic data structures configuration object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/probabilistic/"},{"id":"operate/rs/references/rest-api/objects/bdb/query_performance_factor","summary":"Configuration object for query performance factor","title":"Query performance factor object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/query_performance_factor/"},{"id":"operate/rs/references/rest-api/objects/bdb/replica_sources_status","summary":"Documents the bdb replica_sources status field used with Redis Software REST API calls.","title":"BDB replica sources status field","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/replica_sources_status/"},{"id":"operate/rs/references/rest-api/objects/bdb/replica_sync","summary":"Documents the bdb replica_sync field used with Redis Software REST API calls.","title":"BDB replica sync field","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/replica_sync/"},{"id":"operate/rs/references/rest-api/objects/bdb/search","summary":"Configuration object for Redis Search.","title":"Redis Search configuration object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/search/"},{"id":"operate/rs/references/rest-api/objects/bdb/snapshot_policy","summary":"Documents the snapshot_policy object used with Redis Software REST API calls.","title":"Snapshot policy object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/snapshot_policy/"},{"id":"operate/rs/references/rest-api/objects/bdb/status","summary":"Documents the bdb status field used with Redis Software REST API calls.","title":"BDB status field","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/status/"},{"id":"operate/rs/references/rest-api/objects/bdb/syncer_sources","summary":"Documents the syncer_sources object used with Redis Software REST API calls.","title":"Syncer sources object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/syncer_sources/"},{"id":"operate/rs/references/rest-api/objects/bdb/timeseries","summary":"Configuration object timeseries.","title":"Timeseries configuration object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/timeseries/"}],"page_type":"content","content_hash":"2f9f52c7d09510cd7341adc293dfa07ac17fc0bf1584512dbd58739f30d20d9f","sections":[{"id":"content","title":"Content","role":"content","text":"An API object that represents a managed database in the cluster.\n\n| Name | Type/Value & Description |\n|------|-------------------------|\n| uid | integer; Cluster unique ID of database. Can be set during creation but cannot be updated. |\n| account_id | integer; SM account ID |\n| action_uid | string; Currently running action's UID (read-only) |\n| active_defrag_cycle_max | integer, (range: 1-99); Maximum CPU percentage used for defragmentation when the upper threshold is reached |\n| active_defrag_cycle_min | integer, (range: 1-99) (default: 1); Minimal CPU percentage used for defragmentation when the lower threshold is reached |\n| active_defrag_ignore_bytes | string (default: 100mb); Minimum amount of fragmentation waste to start active defragmentation |\n| active_defrag_max_scan_fields | integer (default: 1000); Maximum number of set/hash/zset/list fields that will be processed from the main dictionary scan |\n| active_defrag_threshold_lower | integer, (range: 0-1000) (default: 10); Minimum percentage of fragmentation to start active defragmentation |\n| active_defrag_threshold_upper | integer, (range: 0-1000) (default: 100); Maximum percentage of fragmentation at which maximum effort is used |\n| activedefrag | Enable or turn off active defragmentation functionality.<br />Values:<br />'yes'<br />**'no'** |\n| aof_policy | Policy for Append-Only File data persistence<br />Values:<br />**'appendfsync-every-sec'** <br />'appendfsync-always' |\n| audit_settings | Complex object. Configures per-database connection and CRUD auditing mode, username/source-IP filtering, and key-byte capture limits. See [Audit database events](https://redis.io/docs/latest/operate/rs/security/audit-events). |\n| authentication_admin_pass | string; Password for administrative access to the BDB (used for SYNC from the BDB) |\n| authentication_redis_pass | string; Redis AUTH password authentication.  <br/>Use for Redis databases only.  Ignored for memcached databases. (deprecated as of Redis Software v7.2, replaced with multiple passwords feature in version 6.0.X) |\n| authentication_sasl_pass | string; Binary memcache SASL password |\n| authentication_sasl_uname | string; Binary memcache SASL username (pattern does not allow special characters &,\\<,>,\") |\n| authentication_ssl_client_certs | [{<br />  \"client_cert\": string<br />}, ...] List of authorized client certificates<br />**client_cert**: X.509 PEM (base64) encoded certificate |\n| authentication_ssl_crdt_certs | [{<br />  \"client_cert\": string<br />}, ...] List of authorized CRDT certificates<br />**client_cert**: X.509 PEM (base64) encoded certificate |\n| authorized_names | array of strings; Additional certified names (deprecated as of Redis Software v6.4.2; use authorized_subjects instead) |\n| authorized_subjects | [{<br />  \"CN\": string,<br />  \"O\": string,<br />  \"OU\": [array of strings],<br />  \"L\": string,<br />  \"ST\": string,<br />  \"C\": string<br />}, ...] A list of valid subjects used for additional certificate validations during TLS client authentication. All subject attributes are case-sensitive.<br />**Required subject fields**:<br />\"CN\" for Common Name<br />**Optional subject fields:**<br />\"O\" for Organization<br />\"OU\" for Organizational Unit (array of strings)<br />\"L\" for Locality (city)<br />\"ST\" for State/Province<br />\"C\" for 2-letter country code |\n| auto_shards_balancing | boolean (default: false); Automatically balance database shards. This field is for future use and should not be changed. |\n| <span class=\"break-all\">auto_shards_balancing_grace_period</span> | integer (default: 120); Time to wait before auto sharding is initiated. This field is for future use and should not be changed. |\n| auto_upgrade | boolean (default:&nbsp;false); Upgrade the database automatically after a cluster upgrade |\n| avoid_nodes | array of strings; Cluster node UIDs to avoid when placing the database's shards and binding its endpoints |\n| background_op | Deprecated as of Redis Software v7.8.2. Use [`GET /v1/actions/bdb/<bdb_uid>`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/actions/bdb) instead.<br />[{<br />  \"status\": string,<br />  \"name\": string,<br />  \"error\": object,<br />  \"progress\": number<br />}, ...] (read-only); **progress**: Percent of completed steps in current operation |\n| backup | boolean (default:&nbsp;false); Policy for periodic database backup |\n| backup_failure_reason | Reason of last failed backup process (read-only)<br />Values:<br />'no-permission'<br />'wrong-file-path'<br />'general-error' |\n| backup_history | integer (default:&nbsp;0); Backup history retention policy (number of days, 0 is forever) |\n| backup_interval | integer; Interval in seconds in which automatic backup will be initiated |\n| backup_interval_offset | integer; Offset (in seconds) from round backup interval when automatic backup will be initiated (should be less than backup_interval) |\n| backup_location | [complex object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/backup_location);  Target for automatic database backups. <br />Call `GET`&nbsp;`/jsonschema` to retrieve the object's structure. |\n| backup_progress | number, <nobr>(range: 0-100)</nobr>;  Database scheduled periodic backup progress (percentage) (read-only) |\n| backup_status | Status of scheduled periodic backup process (read-only)<br />Values:<br />'exporting'<br />'succeeded'<br />'failed' |\n| bigstore | boolean (default:&nbsp;false);  Database bigstore option |\n| bigstore_ram_size | integer (default:&nbsp;0);  Memory size of bigstore RAM part. |\n| bigstore_ram_weights | [{<br />  \"shard_uid\": integer,<br />  \"weight\": number<br />}, ...] List of shard UIDs and their bigstore RAM weights;<br /> **shard_uid**: Shard UID;<br /> **weight**: Relative weight of RAM distribution |\n| bigstore_version | The database's `bigstore_version`:<br />• `1` for Auto Tiering (Redis on Flash version 1). Default version.<br/>• `2` for Redis Flex (Redis on Flash version 2) on databases that support it. Can only be used with the `speedb` driver.<br /><br />You can only choose the `bigstore_version` for databases with Redis version 7.4. Database versions earlier than 7.4 use `bigstore_version` `1`. Database versions 8.0 and later use `bigstore_version` `2`. |\n| client_cert_subject_validation_type | Enables additional certificate validations that further limit connections to clients with valid certificates during TLS client authentication.<br />Values:<br />**disabled**: Authenticates clients with valid certificates. No additional validations are enforced.<br />**san_cn**: A client certificate is valid only if its Common Name (CN) matches an entry in the list of valid subjects. Ignores other Subject attributes.<br />**full_subject**: A client certificate is valid only if its Subject attributes match an entry in the list of valid subjects. |\n| conns | integer (default&nbsp;5);  Number of internal proxy connections |\n| <span class=\"break-all\">conns_global_maximum_dedicated</span> | integer (default: 0); Defines the maximum number of dedicated server connections for a given database. The total number across all workers. The default is 0 for unlimited. |\n| conns_minimum_dedicated | integer (default:&nbsp;2); Number of dedicated server connections the DMC has per worker per shard |\n| conns_type | Connections limit type<br />Values:<br />**‘per-thread’**<br />‘per-shard’ |\n| crdt | boolean (default:&nbsp;false);  Use CRDT-based data types for multi-master replication |\n| crdt_causal_consistency | boolean (default:&nbsp;false);  Causal consistent CRDB. |\n| crdt_config_version | integer;  Replica-set configuration version, for internal use only. |\n| crdt_featureset_version | integer;  CRDB active FeatureSet version |\n| crdt_ghost_replica_ids | string;  Removed replicas IDs, for internal use only. |\n| crdt_guid | string;  GUID of CRDB this database belongs to, for internal use only. |\n| crdt_modules | string;  CRDB modules information. The string representation of a JSON list, containing hashmaps. |\n| crdt_protocol_version | integer;  CRDB active Protocol version |\n| crdt_repl_backlog_size | string;  Active-Active replication backlog size ('auto' or size in bytes) |\n| crdt_replica_id | integer;  Local replica ID, for internal use only. |\n| crdt_replicas | string;  Replica set configuration, for internal use only. |\n| crdt_sources | array of [syncer_sources](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/syncer_sources) objects; Remote endpoints/peers of CRDB database to sync from. See the 'bdb -\\> replica_sources' section |\n| crdt_sync | Enable, disable, or pause syncing from specified crdt_sources. Applicable only for Active-Active databases. See [replica_sync](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/replica_sync) for more details.<br />Values:<br />'enabled'<br /> **'disabled'** <br />'paused'<br />'stopped' |\n| <span class=\"break-all\">crdt_sync_connection_alarm_timeout_seconds</span> | integer (default: 0); If the syncer takes longer than the specified number of seconds to connect to an Active-Active database, raise a connection alarm |\n| crdt_sync_dist | boolean;  Enable/disable distributed syncer in master-master |\n| crdt_syncer_auto_oom_unlatch | boolean (default:&nbsp;true);  Syncer automatically attempts to recover synchronisation from peers after this database throws an Out-Of-Memory error. Otherwise, the syncer exits |\n| crdt_xadd_id_uniqueness_mode | XADD strict ID uniqueness mode. CRDT only.<br />Values:<br />‘liberal’<br />**‘strict’**<br />‘semi-strict’ |\n| created_time | string;  The date and time the database was created (read-only) |\n| data_internode_encryption | boolean;  Should the data plane internode communication for this database be encrypted |\n| data_persistence | Database on-disk persistence policy. For snapshot persistence, a [snapshot_policy](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/snapshot_policy) must be provided<br />Values:<br />**'disabled'** <br />'snapshot'<br />'aof' |\n| dataset_import_sources | [complex object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/dataset_import_sources);  Array of source file location description objects to import from when performing an import action. This is write-only and cannot be read after set. <br />Call `GET /v1/jsonschema` to retrieve the object's structure. |\n| db_conns_auditing | boolean; deprecated as of Redis Software 8.2.0; use `audit_settings.audit_mode` instead. Enables/deactivates [database connection auditing](https://redis.io/docs/latest/operate/rs/security/audit-events) |\n| default_user | boolean (default:&nbsp;true); Allow/disallow a default user to connect |\n| disabled_commands | string (default: ); Redis commands which are disabled in db |\n| <span class=\"break-all\">disconnect_clients_on_password_removal</span> | boolean (default: false); Controls whether client connections using removed, revoked, or rotated passwords are actively disconnected |\n| dns_address_master | string;  Database private address endpoint FQDN (read-only) (deprecated as of Redis Software v4.3.3) |\n| email_alerts | boolean (default:&nbsp;false);  Send email alerts for this DB |\n| endpoint | string;  Latest bound endpoint. Used when reconfiguring an endpoint via update |\n| endpoint_ip | complex object;  External IP addresses of node hosting the BDB's endpoint. `GET`&nbsp;`/jsonschema` to retrieve the object's structure. (read-only) (deprecated as of Redis Software v4.3.3) |\n| endpoint_node | integer;  Node UID hosting the BDB's endpoint (read-only) (deprecated as of Redis Software v4.3.3) |\n| endpoints | array; List of database access endpoints (read-only)<br />**uid**: Unique identification of this source<br />**dns_name**: Endpoint’s DNS name<br />**port**: Endpoint’s TCP port number<br />**addr**: Endpoint’s accessible addresses<br />**proxy_policy**: The policy used for proxy binding to the endpoint<br />**exclude_proxies**: List of proxies to exclude<br />**include_proxies**: List of proxies to include<br />**addr_type**: Indicates if the endpoint is based on internal or external IPs<br /><span class=\"break-all\">**oss_cluster_api_preferred_ip_type**</span>: Indicates preferred IP type in the OSS cluster API: internal/external<br /><span class=\"break-all\">**oss_cluster_api_preferred_endpoint_type**</span>: Indicates preferred endpoint type in the OSS cluster API: ip/hostname |\n| enforce_client_authentication | Require authentication of client certificates for SSL connections to the database. If set to 'enabled', a certificate should be provided in either authentication_ssl_client_certs or authentication_ssl_crdt_certs<br />Values:<br />**'enabled'** <br />'disabled' |\n| eviction_policy | Database eviction policy (Redis style).<br />Values:<br />'volatile-lru'<br />'volatile-ttl'<br />'volatile-random'<br />'allkeys-lru'<br />'allkeys-random'<br />'noeviction'<br />'volatile-lfu'<br />'allkeys-lfu'<br />'allkeys-lrm'<br />'volatile-lrm'<br />**Redis DB default**: 'volatile-lru'<br />**memcached DB default**: 'allkeys-lru' |\n| export_failure_reason | Reason of last failed export process (read-only)<br />Values:<br />'no-permission'<br />'wrong-file-path'<br /> 'general-error' |\n| export_progress | number, <nobr>(range: 0-100)</nobr>; Database manually triggered export progress (percentage) (read-only) |\n| export_status | Status of manually triggered export process (read-only)<br />Values:<br />'exporting'<br />'succeeded'<br />'failed' |\n| generate_text_monitor | boolean;  Enable/disable generation of syncer monitoring information |\n| gradual_src_max_sources | integer (default:&nbsp;1); Sync a maximum N sources in parallel (gradual_src_mode should be enabled for this to take effect) |\n| gradual_src_mode | Indicates if gradual sync (of sync sources) should be activated<br />Values:<br />'enabled'<br />'disabled' |\n| gradual_sync_max_shards_per_source | integer (default:&nbsp;1); Sync a maximum of N shards per source in parallel (gradual_sync_mode should be enabled for this to take effect) |\n| gradual_sync_mode | Indicates if gradual sync (of source shards) should be activated ('auto' for automatic decision)<br />Values:<br />'enabled'<br />'disabled'<br />'auto' |\n| gradual_sync_policy | string (default: default); Managed gradual (parallel) full sync policy for replica synchronization. Non-custom policies own and normalize the limit fields in `gradual_sync_policy_parameters`.<br />Values:<br />'default'<br />'non-gradual'<br />'migration'<br />'replica'<br />'active-active'<br />'custom' |\n| <span class=\"break-all\">gradual_sync_policy_parameters</span> | object; Gradual full sync limits, applied when `gradual_sync_policy` is `custom`. Each limit accepts `-1` (no limit) or a positive integer; `0` is not valid. Includes `src_endpoint_limit` (default: 1), `dst_endpoint_limit`, `dst_shard_limit`, and `src_node_limit` (default: -1). |\n| hash_slots_policy | The policy used for hash slots handling<br />Values:<br/> **'legacy'**: slots range is '1-4096'<br /> **'16k'**: slots range is '0-16383' |\n| implicit_shard_key | boolean (default:&nbsp;false); Controls the behavior of what happens in case a key does not match any of the regex rules. <br /> **true**: if a key does not match any of the rules, the entire key will be used for the hashing function <br /> **false**: if a key does not match any of the rules, an error will be returned. |\n| import_failure_reason | Import failure reason (read-only)<br />Values:<br />'download-error'<br />'file-corrupted'<br />'general-error'<br />'file-larger-than-mem-limit:\\<n bytes of expected dataset>:\\<n bytes configured bdb limit>'<br />'key-too-long'<br />'invalid-bulk-length'<br />'out-of-memory' |\n| import_progress | number, <nobr>(range: 0-100)</nobr>; Database import progress (percentage) (read-only) |\n| import_status | Database import process status (read-only)<br />Values:<br />'idle'<br />'initializing'<br />'importing'<br />'succeeded'<br />'failed' |\n| internal | boolean (default:&nbsp;false); Is this a database used by the cluster internally |\n| last_backup_time | string; Time of last successful backup (read-only) |\n| last_changed_time | string; Last administrative configuration change (read-only) |\n| last_export_time | string; Time of last successful export (read-only) |\n| <span class=\"break-all\">link_sconn_on_full_request</span> | Feature-flag for DMC behaviour on linking client request<br />Values:<br />'enabled'<br />**'disabled'**<br />'ssl_only' |\n| max_aof_file_size | integer; Maximum size for shard's AOF file (bytes). Default 300GB, (on bigstore DB 150GB) |\n| max_aof_load_time | integer (default:&nbsp;3600); Maximum time shard's AOF reload should take (seconds). |\n| max_client_pipeline | integer (default:&nbsp;200); Maximum number of pipelined commands per connection. Maximum value is 2047. |\n| max_connections | integer (default:&nbsp;0); Maximum number of client connections allowed (0 unlimited) |\n| max_pipelined | integer (default:&nbsp;2000); Determines the maximum number of commands in the proxy’s pipeline per shard connection. |\n| maxclients | integer (default: 10000); The maximum number of connected clients at the same time. By default the limit is 10000 clients. |\n| master_persistence | boolean (default:&nbsp;false); If true, persists the primary shard in addition to replica shards in a replicated and persistent database. |\n| memory_size | integer (default:&nbsp;0); Database memory limit (0 is unlimited), expressed in bytes. |\n| metrics_export_all | boolean; Enable/disable exposing all shard metrics through the metrics exporter |\n| mkms | boolean (default:&nbsp;true); Are MKMS (Multi Key Multi Slots) commands supported? |\n| module_list | [{<br />  \"module_id\": string,<br />  \"module_args\": [<br />    u'string',<br />    u'null'],<br />  \"module_name\": string,<br />  \"semantic_version\": string<br />}, ...] List of modules associated with the database<br /><br />**module_id**: Module UID (deprecated; use `module_name` instead)<br />**module_args**: Module command-line arguments (deprecated; use the module objects for [search](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/search), [timeseries](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/timeseries), and [probabilistic](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/probabilistic) instead)<br />**module_name**: Module's name<br />**semantic_version**: Module's semantic version (deprecated; use `module_args` instead)<br /><br />**module_id** and **semantic_version** are optional as of Redis Software v7.4.2 and deprecated as of v7.8.2.<br />**module_args** is deprecated as of Redis Software v8.0. |\n| mtls_allow_outdated_certs | boolean; An optional mTLS relaxation flag for certs verification |\n| mtls_allow_weak_hashing | boolean; An optional mTLS relaxation flag for certs verification |\n| multi_commands_opt | If set to `batch`, it reduces the overhead of transaction management by batching multiple commands into a single transaction.<br />Values:<br />**disabled**: Turns off the optimization.<br />**batch**: Enables the optimization.<br />**auto**: Uses the [cluster's `multi_commands_opt` setting](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cluster). |\n| name | string; Database name. Only letters, numbers, or hyphens are valid characters. The name must start and end with a letter or number. |\n| oss_cluster | boolean (default:&nbsp;false); OSS Cluster mode option. Cannot be enabled with `'hash_slots_policy': 'legacy'` |\n| <span class=\"break-all\">oss_cluster_api_preferred_endpoint_type</span> | Endpoint type in the OSS cluster API<br />Values:<br />**‘ip’**<br />‘hostname’ |\n| <span class=\"break-all\">oss_cluster_api_preferred_ip_type</span> | Internal/external IP type in OSS cluster API. Default value for new endpoints<br />Values:<br />**'internal'** <br />'external' |\n| oss_sharding | boolean (default: false); This flag is for future use and should not be changed |\n| <span class=\"break-all\">partial_request_timeout_seconds</span> | integer (default: 3); When a client connection sends a command, takes a server connection, and stops writing before the command is complete, it causes head-of-line blocking on this server connection. Such commands will time out after this many seconds and the client connection will be closed. |\n| port | integer; TCP port on which the database is available. Generated automatically if omitted and returned as 0 |\n| <span class=\"break-all\">preemptive_drain_timeout_seconds</span> | integer (default: 2); Timeout in seconds for preemptive drain of client connections before a shard is taken down |\n| probabilistic | [complex object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/probabilistic); Configuration fields for probabilistic data structures. |\n| proxy_policy | The default policy used for proxy binding to endpoints<br />Values:<br />'single'<br />'all-master-shards'<br />'all-nodes' |\n| query_performance_factor | [complex object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/query_performance_factor); Configures query performance factor and related fields |\n| rack_aware | boolean (default:&nbsp;false); Require the database to always replicate across multiple racks |\n| recovery_wait_time | integer (default:&nbsp;-1); Defines how many seconds to wait for the persistence file to become available during auto recovery. After the wait time expires, auto recovery completes with potential data loss. The default `-1` means to wait forever. |\n| redis_version | string; Version of the redis-server processes: e.g. 6.0, 5.0-big |\n| repl_backlog_size | string; Redis replication backlog size ('auto' or size in bytes) |\n| replica_read_only | boolean (default: false); If `true`, enables an Active-Passive setup where Replica Of databases only allow read operations. Only configurable during database creation and cannot be changed later. |\n| replica_sconns_on_demand | Reduces DMC internode connections by at least 50%, conserving sockets, file descriptors, and KEEPALIVE traffic.<br />Values:<br />**'enabled'** <br />'disabled' |\n| replica_sources | array of [syncer_sources](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/syncer_sources) objects; Remote endpoints of database to sync from. See the 'bdb -\\> replica_sources' section |\n| [replica_sync](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/replica_sync) | Enable, disable, or pause syncing from specified replica_sources<br />Values:<br />'enabled'<br /> **'disabled'** <br />'paused'<br />'stopped' |\n| <span class=\"break-all\">replica_sync_connection_alarm_timeout_seconds</span> | integer (default: 0); If the syncer takes longer than the specified number of seconds to connect to a replica, raise a connection alarm |\n| replica_sync_dist | boolean; Enable/disable distributed syncer in replica-of |\n| replication | boolean (default:&nbsp;false); In-memory database replication mode |\n| replication_oom_threshold_percent | integer, (range: 0-20) (default: 5); Reserved memory buffer percentage below `maxmemory` that blocks client writes while allowing Active-Active replication. CRDT only, requires Redis 8.4+. |\n| resp3 | boolean (default:&nbsp;true); Enables or deactivates RESP3 support |\n| roles_permissions | [{<br />  \"role_uid\": integer,<br />  \"redis_acl_uid\": integer<br />}, ...] |\n| sched_policy | Controls how server-side connections are used when forwarding traffic to shards.<br />Values:<br />**cmp**: Closest to max_pipelined policy. Pick the connection with the most pipelined commands that has not reached the max_pipelined limit.<br />**mru**: Try to use most recently used connections.<br />**spread**: Try to use all connections.<br />**mnp**: Minimal pipeline policy. Pick the connection with the least pipelined commands. |\n| search | [complex object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/search); Configuration fields for Redis Search. |\n| search_on_bigstore | boolean (default: false); Include search module in Redis on flash v2 (Flex databases) |\n| shard_block_crossslot_keys | boolean (default:&nbsp;false); In Lua scripts, prevent use of keys from different hash slots within the range owned by the current shard |\n| shard_block_foreign_keys | boolean (default:&nbsp;true); In Lua scripts, `foreign_keys` prevent use of keys which could reside in a different shard (foreign keys) |\n| shard_key_regex | Custom keyname-based sharding rules.<br />`[{\"regex\": string}, ...]`<br />To use the default rules you should set the value to: <br />`[{\"regex\": \".*\\\\{(?<tag>.*)\\\\}.*\"}, {\"regex\": \"(?<tag>.*)\"}]` |\n| <span class=\"break-all\">shard_imbalance_threshold</span> | number (default: 314572800); Automatically balances shards only if their imbalance is greater than this threshold. This field is for future use and should not be changed. |\n| <span class=\"break-all\">shard_imbalance_threshold_percentage</span> | integer (default: 20); Automatically balances shards only if their imbalance percentage is greater than this threshold. This field is for future use and should not be changed. |\n| shard_list | array of integers; Cluster unique IDs of all database shards. |\n| sharding | boolean (default: false); Cluster mode (server-side sharding). When true, shard hashing rules must be provided by `shard_key_regex` |\n| shards_count | integer, <nobr>(range: 1-512)</nobr> (default:&nbsp;1); Number of database server-side shards |\n| shards_placement | Control the density of shards <br />Values:<br />**'dense'**: Shards reside on as few nodes as possible <br /> **'sparse'**: Shards reside on as many nodes as possible |\n| skip_import_analyze | Enable/disable skipping the analysis stage when importing an RDB file<br />Values:<br />'enabled'<br />'disabled' |\n| slave_buffer | Redis replica output buffer limits<br />Values:<br />'auto'<br />value in MB<br />hard:soft:time |\n| slave_ha | boolean; Enable replica high availability mechanism for this database (default takes the cluster setting) |\n| slave_ha_priority | integer; Priority of the BDB in replica high availability mechanism |\n| snapshot_policy | array of [snapshot_policy](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/snapshot_policy) objects; Policy for snapshot-based data persistence. A dataset snapshot will be taken every N secs if there are at least M writes changes in the dataset |\n| ssl | boolean (default:&nbsp;false); Require SSL authenticated and encrypted connections to the database (deprecated as of Redis Software v5.0.1) |\n| [status](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/status) | Database lifecycle status (read-only)<br />Values:<br />'pending'<br />'active'<br />'active-change-pending'<br />'delete-pending'<br />'import-pending'<br />'creation-failed'<br />'recovery' |\n| support_syncer_reconf | boolean; Determines whether the syncer handles its own configuration changes. If false, the DMC restarts the syncer upon a configuration change. |\n| sync | (deprecated as of Redis Software v5.0.1, use [replica_sync](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/replica_sync) or crdt_sync instead) Enable, disable, or pause syncing from specified sync_sources<br />Values:<br />'enabled'<br /> **'disabled'** <br />'paused'<br />'stopped' |\n| sync_dedicated_threads | integer (range:&nbsp;0-10) (default:&nbsp;5); Number of dedicated Replica Of threads |\n| sync_sources | [{<br />  \"uid\": integer,<br />  \"uri\": string,<br />  \"compression\": integer,<br />  \"status\": string,<br />  \"rdb_transferred\": integer,<br />  \"rdb_size\": integer,<br />  \"last_update\": string,<br />  \"lag\": integer,<br />  \"last_error\": string<br />}, ...] (deprecated as of Redis Software v5.0.1, instead use replica_sources or crdt_sources) Remote endpoints of database to sync from. See the 'bdb -\\> replica_sources' section<br />**uid**: Numeric unique identification of this source<br />**uri**: Source Redis URI<br />**compression**: Compression level for the replication link<br />**status**: Sync status of this source<br />**rdb_transferred**: Number of bytes transferred from the source's RDB during the syncing phase<br />**rdb_size**: The source's RDB size to be transferred during the syncing phase<br />**last_update**: Time last update was received from the source<br />**lag**: Lag in millisec between source and destination (while synced)<br />**last_error**: Last error encountered when syncing from the source |\n| syncer_log_level | Minimum syncer log level to log. Only logs with this level or higher will be logged.<br />Values:<br />‘crit’<br />‘error’<br />‘warn’<br />**‘info’**<br />‘trace’<br />‘debug’ |\n| syncer_mode | The syncer for replication between database instances is either on a single node (centralized) or on each node that has a proxy according to the proxy policy (distributed). (read-only)<br />Values:<br />'distributed'<br />'centralized' |\n| tags | [{<br />  \"key\": string,<br />  \"value\": string<br />}, ...] Optional list of tag objects attached to the database. Each tag requires a key-value pair.<br />**key**: Represents the tag's meaning and must be unique among tags (pattern does not allow special characters &,\\<,>,\")<br />**value**: The tag's value.|\n| timeseries | [complex object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/timeseries); Configuration fields for timeseries. |\n| tls_mode | Require TLS-authenticated and encrypted connections to the database<br />Values:<br />'enabled'<br /> **'disabled'** <br />'replica_ssl' |\n| tracking_table_max_keys | integer; The client-side caching invalidation table size. 0 makes the cache unlimited. |\n| traffic_manually_disabled | boolean; Whether traffic was manually turned off using the stop_traffic API (read-only) |\n| type | Type of database<br />Values:<br />**'redis'** <br />'memcached' |\n| use_nodes | array of strings; Cluster node UIDs to use for database shards and bound endpoints |\n| use_selective_flush | boolean (default: true); If true, enable selective flush of destination shards |\n| version | string; Database compatibility version: full Redis/memcached version number, such as 6.0.6. This value can only change during database creation and database upgrades.|\n| wait_command | boolean (default:&nbsp;true); Supports Redis wait command (read-only) |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/bdb/probabilistic","title":"Probabilistic data structures configuration object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/probabilistic/","summary":"Configuration object for probabilistic data structures","tags":["docs","operate","rs"],"last_updated":"2025-10-30T12:00:21-05:00","page_type":"content","content_hash":"f7dfc135a4beb3a2b2861f630af9769049d0107aed714670d4e74f8c7b8a8491","sections":[{"id":"content","title":"Content","role":"content","text":"Configuration fields for probabilistic data structures.\n\n| Field | Type/Value | Description |\n|-------|------------|-------------|\n| bf-error-rate | number (range: 0-1) (default: 0.01) | Error ratio for new Bloom filters. These defaults are applied to each new Bloom filter upon its creation. |\n| bf-initial-size | integer (range: 1-1048576) (default: 100) | Initial capacity for new Bloom filters. These defaults are applied to each new Bloom filter upon its creation. |\n| bf-expansion-factor | integer (range: 0-32768) (default: 2) | Expansion factor for new Bloom filters. These defaults are applied to each new Bloom filter upon its creation. |\n| cf-initial-size | integer (range: 1-1048576) (default: 1024) | Initial capacity for new Cuckoo filters. These defaults are applied to each new Cuckoo filter upon its creation. |\n| cf-bucket-size | integer (range: 1-255) (default: 2) | Number of items in each bucket for new Cuckoo filters. These defaults are applied to each new Cuckoo filter upon its creation. |\n| cf-max-iterations | integer (range: 1-65535) (default: 20) | Maximum iterations for new Cuckoo filters. These defaults are applied to each new Cuckoo filter upon its creation. |\n| cf-expansion-factor | integer (range: 0-32768) (default: 1) | Expansion factor for new Cuckoo filters. These defaults are applied to each new Cuckoo filter upon its creation. |\n| cf-max-expansions | integer (range: 1-65536) (default: 32) | Maximum expansions for new Cuckoo filters. These defaults are applied to each new Cuckoo filter upon its creation. |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/bdb/query_performance_factor","title":"Query performance factor object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/query_performance_factor/","summary":"Configuration object for query performance factor","tags":["docs","operate","rs"],"last_updated":"2025-10-30T12:00:21-05:00","page_type":"content","content_hash":"719394df0c26004c20447f3b0cb951d53b8b0137ec5e0cfb00baf3f7597e6377","sections":[{"id":"content","title":"Content","role":"content","text":"Configures [query performance factor](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/query-performance-factor) and related fields.\n\n| Field | Type/Value | Description |\n|-------|------------|-------------|\n| active | boolean (default: false) | If true, enables query performance factor for the database |\n| scaling_factor | integer (range: 0-16) (default: 0) | Scales the magnitude of the query performance factor |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/bdb/replica_sources_status","title":"BDB replica sources status field","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/replica_sources_status/","summary":"Documents the bdb replica_sources status field used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"5db4782897fbdab353a818f23ec806d2e8769a0d0e38bd439810b7742fd4ce54","sections":[{"id":"content","title":"Content","role":"content","text":"The `replica_sources` status field relates to the [Replica Of](https://redis.io/docs/latest/operate/rs/databases/import-export/replica-of/create.md) feature, which enables the creation of a Redis database (single- or multi-shard) that synchronizes data from another Redis database (single- or multi-shard).\n\nThe status field represents the Replica Of sync status for a specific sync source.\n\nPossible status values:\n\n| Status | Description | Possible next status |\n|--------|-------------|----------------------|\n| 'out-of-sync' | Sync process is disconnected from source and trying to reconnect | 'syncing' |\n| 'syncing' | Sync process is in progress | 'in-sync' <br />'out-of-sync' |\n| 'in-sync' | Sync process finished successfully, and new commands are syncing on a regular basis | 'syncing' <br />'out-of-sync'\n\n![Replica sources status](https://redis.io/docs/latest/images/rs/rest-api-replica-sources-status.png#no-click)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/bdb/replica_sync","title":"BDB replica sync field","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/replica_sync/","summary":"Documents the bdb replica_sync field used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"c2540f433bc59c9710f3d9185154d33fb4c2f91e1b3abba83eb22e2ef8f672db","sections":[{"id":"content","title":"Content","role":"content","text":"The BDB `replica_sync` field relates to the [Replica Of](https://redis.io/docs/latest/operate/rs/databases/import-export/replica-of/create.md) feature, which enables the creation of a Redis database (single- or multi-shard) that synchronizes data from another Redis database (single- or multi-shard).\n\nYou can use the `replica_sync` field to enable, disable, or pause the [Replica Of](https://redis.io/docs/latest/operate/rs/databases/import-export/replica-of/create.md) sync process. The BDB `crdt_sync` field has a similar purpose for the Redis CRDB.\n\nPossible BDB sync values:\n\n| Status | Description | Possible next status |\n|--------|-------------|----------------------|\n| 'disabled' | (default value) Disables the sync process and represents that no sync is currently configured or running. | 'enabled' |\n| 'enabled' | Enables the sync process and represents that the process is currently active. | 'stopped' <br />'paused' |\n| 'paused' | Pauses the sync process. The process is configured but is not currently executing any sync commands. | 'enabled' <br />'stopped' |\n| 'stopped' | An unrecoverable error occurred during the sync process, which caused the system to stop the sync. | 'enabled' |\n\n![BDB sync](https://redis.io/docs/latest/images/rs/rest-api-bdb-sync.png#no-click)\n\nWhen the sync is in the 'stopped' or 'paused' state, then the `last_error` field in the relevant source entry in the `sync_sources` \"status\" field contains the detailed error message."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/bdb/search","title":"Redis Search configuration object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/search/","summary":"Configuration object for Redis Search.","tags":["docs","operate","rs"],"last_updated":"2026-07-02T11:30:01-05:00","page_type":"content","content_hash":"5b387e72d575d2fe77f7e16354293081dbc3d5779f5bbed925bb700a6d2cdcfa","sections":[{"id":"content","title":"Content","role":"content","text":"Configuration fields for Redis Search.\n\n| Field | Type/Value | Description |\n|-------|------------|-------------|\n| search-timeout | integer (range: 1-9223372036854775807) (default: 500) | The maximum amount of time in milliseconds that a search query is allowed to run. |\n| search-ext-load | string  | If present, Redis Search will try to load an extension dynamic library from its specified file path. Requires a database restart to take effect. |\n| search-max-doctablesize | integer (range: 1-18446744073709551615) (default: 1000000) | The maximum size of the internal hash table used for storing the documents. Requires a database restart to take effect. |\n| search-friso-ini | string  | If present, load the custom Chinese dictionary from the specified path. Requires a database restart to take effect. |\n| search-cursor-max-idle | integer (range: 1-9223372036854775807) (default: 300000) | The maximum idle time in milliseconds that can be set to the cursor api. |\n| search-partial-indexed-docs | integer (range: 0-9223372036854775807) (default: 0) | Enable or turn off the Redis command filter. Requires a database restart to take effect. |\n| search-gc-scan-size | integer (range: 1-9223372036854775807) (default: 100) | The bulk size of the internal GC used for cleaning up indexes. Requires a database restart to take effect. |\n| search-no-gc | boolean (default: false) | If set, Garbage Collection is deactivated for all indexes. Requires a database restart to take effect. |\n| search-fork-gc-run-interval | integer (range: 1-9223372036854775807) (default: 30) | Interval in seconds between two consecutive fork GC runs. |\n| search-fork-gc-retry-interval | integer (range: 1-9223372036854775807) (default: 5) | Interval in seconds in which Redis Search will retry to run fork GC in case of a failure. |\n| search-fork-gc-clean-threshold | integer (range: 1-9223372036854775807) (default: 100) | The fork GC will only start to clean when the number of not cleaned documents exceeds this threshold; otherwise, it will skip this run. |\n| search-vss-max-resize | integer (range: 0-4294967295) (default: 0) | The maximum memory resize for vector similarity indexes in bytes. |\n| search-union-iterator-heap | integer (range: 1-9223372036854775807) (default: 20) | The minimum number of iterators in a union from which the iterator will switch to heap-based implementation. |\n| search-min-phonetic-term-len | integer (range: 1-9223372036854775807) (default: 3) | Minimum length of term to be considered for phonetic matching. |\n| search-multi-text-slop | integer (range: 1-4294967295) (default: 100) | Set Redis Search delta used to increase positional offsets between array slots for multi-text values. Requires a database restart to take effect. |\n| search-raw-docid-encoding | boolean (default: false) | Turn off compression for DocID inverted index. Boost CPU performance. Requires a database restart to take effect. |\n| search-_print-profile-clock | boolean (default: true) | Turn off print of time for ft.profile. For testing only. |\n| search-_free-resource-on-thread | boolean (default: true) | Determine whether some index resources are free on a second thread |\n| search-_numeric-compress | boolean (default: false) | Enable legacy compression of double to float. |\n| search-bg-index-sleep-gap | integer (range: 1-4294967295) (default: 100) | The number of iterations to run while performing background indexing before we call usleep(1) (sleep for 1 micro-second) and make sure that we allow Redis to process other commands. Requires a database restart to take effect. |\n| search-_numeric-ranges-parents | integer (range: 0-2) (default: 0) | Keep numeric ranges in numeric tree parent nodes of leaves for `x` |\n| search-fork-gc-sleep-before-exit | integer (range: 0-9223372036854775807) (default: 0) | Set the number of seconds for the fork GC to sleep before exists, should always be set to 0 (other then on tests). |\n| search-no-mem-pools | boolean (default: false) | Set Redis Search to run without memory pools. Requires a database restart to take effect. |\n| search-_prioritize-intersect-union-children | boolean (default: false) | Intersection iterator orders the children iterators by their relative estimated number of results in ascending order. If the first iterators have a lower count of results, skips a larger number of results, which translates into faster iteration. If this flag is set, we use this optimization in a way where union iterators are being factorized by the number of their own children, so that we sort by the number of children times the overall estimated number of results instead. |\n| search-conn-per-shard | integer (range: 0-9223372036854775807) (default: 0) | Number of connections to each shard in the cluster. Default to 0. If 0, the number of connections is set to `WORKERS` + 1. |\n| search-cursor-reply-threshold | integer (range: 1-9223372036854775807) (default: 1) | Maximum number of replies to accumulate before triggering `_FT.CURSOR READ` on the shards |\n| search-threads | integer  | Maximum number of replies to accumulate before triggering `_FT.CURSOR READ` on the shards. Requires a database restart to take effect. |\n| search-default-dialect | integer (range: 1-4) (default: 1) | The default DIALECT to be used by FT.CREATE, FT.AGGREGATE, FT.EXPLAIN, FT.EXPLAINCLI, and FT.SPELLCHECK. |\n| search-topology-validation-timeout | integer (range: 0-9223372036854775807) (default: 30000) | Sets the timeout for topology validation (in milliseconds). After this timeout, any pending requests will be processed, even if the topology is not fully connected. |\n| search-workers | integer (range: 0-8192) (default: 0) | Number of worker threads to use for query processing and background tasks. |\n| search-min-operation-workers | integer (range: 0-8192) (default: 4) | Number of worker threads to use for background tasks when the server is in an operation event. |\n| search-tiered-hnsw-buffer-limit | integer (range: 0-9223372036854775807) (default: 1024) | Sets the buffer limit threshold for vector similarity tiered HNSW index. If using WORKERS for indexing and the number of vectors waiting in the buffer to be indexed exceeds this limit, inserts new vectors directly into HNSW. Requires a database restart to take effect. |\n| search-workers-priority-bias-threshold | integer (range: 0-9223372036854775807) (default: 1) | The number of high-priority tasks to run at any given time by the worker thread pool, before executing low-priority tasks. After this number of high-priority tasks are running, the worker thread pool will run high and low-priority tasks alternately. Requires a database restart to take effect. |\n| search-on-timeout | \"RETURN\"<br />\"FAIL\"<br />(default: \"RETURN\") | The response policy for queries that exceed the TIMEOUT setting can be one of the following: RETURN / FAIL |\n| search-min-prefix | integer (range: 1-9223372036854775807) (default: 2) | The minimum number of characters allowed for prefix queries (e.g., hel*) |\n| search-min-stem-len | integer (range: 2-4294967295) (default: 4) | The minimum word length to stem |\n| search-max-prefix-expansions | integer (range: 1-9223372036854775807) (default: 200) | The maximum number of expansions allowed for query prefixes |\n| search-max-search-results | integer (range: 0-9223372036854775807) (default: 1000000) | The maximum number of results to be returned by the FT.SEARCH command if LIMIT is used |\n| search-max-aggregate-results | integer (range: 0-9223372036854775807) (default: 2147483648) | The maximum number of results to be returned by the FT.AGGREGATE command if LIMIT is used |\n| search-io-threads | integer (range: 1-256) (default: 1) | The number of threads the coordinator is using for IO threads to handle network to/from other shards |\n| search-enable-unstable-features | boolean (default: false) | Enable unstable features. |\n| search-index-cursor-limit | integer (range: 0-9223372036854775807) (default: 128) | The maximum number of cursors that can be opened, per shard, at any given time. Cursors can be opened by the user via FT.AGGREGATE WITHCURSOR. Cursors are also opened internally by Redis Search for long-running queries. Once INDEX_CURSOR_LIMIT is reached, any further attempts to open a cursor will result in an error. |\n| search-bm25std-tanh-factor | integer (range: 1-10000) (default: 4) | Set the BM25STD.TANH stretch factor. This is an integer value that divides the argument of the tanh function that is used to normalize the score computed by the BM25STD scorer. |\n| search-indexer-yield-every-ops | integer (range: 1-4294967295) (default: 1000) | The number of operations to perform before yielding to Redis during indexing while loading. |\n| search-on-oom | \"return\"<br />\"fail\"<br />\"ignore\"<br />(default: \"return\") | Action to perform when search OOM is exceeded. |\n| search-default-scorer | \"TFIDF\"<br />\"BM25\"<br />\"TFIDF.DOCNORM\"<br />\"BM25STD\"<br />\"BM25STD.TANH\"<br />\"BM25STD.NORM\"<br />\"DISMAX\"<br />\"DOCSCORE\"<br />\"HAMMING\"<br />(default: \"BM25STD\") | Default scorer to use when no scorer is specified in queries. |\n| search-_bg-index-mem-pct-thr | integer (range: 0-100) (default: 100) | Set the percentage of memory usage threshold out of maxmemory at which background indexing will stop. |\n| search-_bg-index-oom-pause-time | integer (range: 0-4294967295) (default: 5) | Set the time in seconds given to the background indexing thread to sleep when it reaches the memory limit. |\n| search-bg-index-sleep-duration-us | integer (range: 0-999999) (default: 1) | Sleep duration in microseconds (µs) during background indexing. |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/bdb/snapshot_policy","title":"Snapshot policy object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/snapshot_policy/","summary":"Documents the snapshot_policy object used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"73b3958d460072182fa13bb9ef17397288c146d6cfa35d710afc0dcb098bfce5","sections":[{"id":"content","title":"Content","role":"content","text":"| Name | Type/Value | Description |\n|------|------------|-------------|\n| secs   | integer | Interval in seconds between snapshots |\n| writes | integer | Number of write changes required to trigger a snapshot |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/bdb/status","title":"BDB status field","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/status/","summary":"Documents the bdb status field used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"d22f9d25dcfd252dd2af906279109a58124316d761f89a71b934ac8f82764abb","sections":[{"id":"content","title":"Content","role":"content","text":"The BDB status field is a read-only field that represents the database status.\n\nPossible status values:\n\n| Status | Description | Possible next status |\n|--------|-------------|----------------------|\n| 'active' | Database is active and no special action is in progress | 'active-change-pending' <br />'import-pending' <br />'delete-pending' |\n| 'active-change-pending' | |'active' |\n| 'creation-failed' | Initial database creation failed | |\n| 'delete-pending' | Database deletion is in progress | |\n| 'import-pending' | Dataset import is in progress | 'active' |\n| 'pending' | Temporary status during database creation | 'active'<br />'creation-failed' |\n| 'recovery' | Not currently relevant (intended for future use) | |\n\n![BDB status](https://redis.io/docs/latest/images/rs/rest-api-bdb-status.png#no-click)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/bdb/syncer_sources","title":"Syncer sources object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/syncer_sources/","summary":"Documents the syncer_sources object used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"ee13c592357b110a9e05f36910371f2dca6b00521f8fea6bafaeab2094ecafeb","sections":[{"id":"content","title":"Content","role":"content","text":"| Name | Type/Value | Description |\n|------|------------|-------------|\n| uid | integer | Unique ID of this source |\n| client_cert | string | Client certificate to use if encryption is enabled |\n| client_key | string | Client key to use if encryption is enabled |\n| compression | integer, <nobr>(range: 0-6)</nobr> | Compression level for the replication link |\n| encryption | boolean | Encryption enabled/disabled |\n| lag | integer | Lag in milliseconds between source and destination (while synced) |\n| last_error | string | Last error encountered when syncing from the source |\n| last_update | string | Time when we last received an update from the source |\n| rdb_size | integer | The source's RDB size to be transferred during the syncing phase |\n| rdb_transferred | integer | Number of bytes transferred from the source's RDB during the syncing phase |\n| replication_tls_sni | string | Replication TLS server name indication |\n| server_cert | string | Server certificate to use if encryption is enabled |\n| status | string | Sync status of this source |\n| uri | string | Source Redis URI |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/bdb/timeseries","title":"Timeseries configuration object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/timeseries/","summary":"Configuration object timeseries.","tags":["docs","operate","rs"],"last_updated":"2025-10-30T12:00:21-05:00","page_type":"content","content_hash":"8d71b89f0620af69857346c22653f7f7fbf9c541974acef8c2a0ad5082fb2596","sections":[{"id":"content","title":"Content","role":"content","text":"Configuration fields for timeseries.\n\n| Field | Type/Value | Description |\n|-------|------------|-------------|\n| ts-num-threads | integer (range: 1-16) | Number of threads for time series operations. Requires a database restart to take effect. |\n| ts-compaction-policy | string  | Default compaction rules. This default value is applied to each new time series upon its creation |\n| ts-retention-policy | integer (range: 0-9223372036854775807) (default: 0) | Default retention period, in milliseconds. This default value is applied to each new time series upon its creation, but if COMPACTION_POLICY is specified, it is overridden for created compactions as specified in COMPACTION_POLICY. |\n| ts-duplicate-policy | \"BLOCK\"<br />\"FIRST\"<br />\"LAST\"<br />\"MIN\"<br />\"MAX\"<br />\"SUM\"<br />(default: \"BLOCK\") | Default policy for handling insertion of multiple samples with identical timestamps. This default value is applied to each new time series upon its creation. |\n| ts-encoding | \"COMPRESSED\"<br />\"UNCOMPRESSED\"<br />(default: \"COMPRESSED\") | Default chunk encoding for automatically-created compacted time series. This default value is applied to each new compacted time series automatically created due to the creation of a new time series when COMPACTION_POLICY is specified. |\n| ts-chunk-size-bytes | integer (range: 48-1048576) (default: 4096) | Default initial allocation size, in bytes, for the data part of each new chunk. This default value is applied to each new time series upon its creation. |\n| ts-ignore-max-time-diff | integer (range: 0-9223372036854775807) (default: 0) | Default maximum time difference that can be expired to consider a new insertion to be a duplicate. This default value is applied to each new time series upon its creation. |\n| ts-ignore-max-val-diff | number (default: 0) | Default maximum value difference for a new insertion to be considered a duplicate. This default value is applied to each new time series upon its creation. |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/bdb_group","title":"BDB group object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb_group/","summary":"An object that represents a group of databases with a shared memory pool","tags":["docs","operate","rs"],"last_updated":"2023-12-14T15:27:49+01:00","page_type":"content","content_hash":"7612f410479ae235540980cbbb10313214cf1be0239266ca4edc047d5bec7e5d","sections":[{"id":"content","title":"Content","role":"content","text":"An API object that represents a group of databases that share a memory pool.\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| uid          | integer          | Cluster unique ID of the database group |\n| members      | array of strings | A list of UIDs of member databases (read-only) |\n| memory_size  | integer          | The common memory pool size limit for all databases in the group, expressed in bytes |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/bootstrap/cluster_identity","title":"Cluster identity object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bootstrap/cluster_identity/","summary":"Documents the cluster_identity object used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"d02506a11aeee7d652ff1cddf61e8c6a7742995325b5344a9439b0bded1f4f64","sections":[{"id":"content","title":"Content","role":"content","text":"| Name | Type/Value | Description |\n|------|------------|-------------|\n| name          | string                | Fully qualified cluster name. Limited to 64 characters and must comply with the IETF's RFC 952 standard and section 2.1 of the RFC 1123 standard. |\n| nodes         | array of strings       | Array of IP addresses of existing cluster nodes |\n| wait_command  | boolean (default:&nbsp;true) | Supports Redis wait command |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/bootstrap/credentials","title":"Credentials object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bootstrap/credentials/","summary":"Documents the credentials object used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-08-12T11:52:45-05:00","page_type":"content","content_hash":"275870cf1c8772f4e8772dbdf0f6a071cd639767226dc31e7bf95896ecaa24a5","sections":[{"id":"content","title":"Content","role":"content","text":"| Name | Type/Value | Description |\n|------|------------|-------------|\n| client_cert | string      | Client certificate as a PEM string. Use certificate credentials instead of a username and password to join a node to a cluster. Required with `client_key`. See [Certificate-based authentication for cluster management](https://redis.io/docs/latest/operate/rs/security/certificates/certificate-based-authentication#certificate-based-authentication-for-cluster-management). |\n| client_key | string       | The client certificate's private key as a PEM string. Required with `client_cert`. |\n| password   | string       | Admin password. Required unless you use certificate credentials. |\n| trusted_ca | string       | CA that validates the API certificate the cluster presents, as a PEM string. If you omit it, the cluster uses the certificates in its `mtls_trusted_ca.pem` file. |\n| username   | string       | Admin username (pattern does not allow special characters &,\\<,>,\"). Required unless you use certificate credentials. |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/bootstrap/identity","title":"Identity object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bootstrap/identity/","summary":"Documents the identity object used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"ab033594978587eed62d78877877d6c6e850d0f87a1b9f2d1013d6925f4ee28d","sections":[{"id":"content","title":"Content","role":"content","text":"| Name | Type/Value | Description |\n|------|------------|-------------|\n| uid | integer | Assumed node's UID to join cluster. Used to replace a dead node with a new one. |\n| accept_servers | boolean (default:&nbsp;true) | If true, no shards will be created on the node |\n| addr | string | Internal IP address of node |\n| external_addr | complex object | External IP addresses of node. `GET`&nbsp;`/jsonschema` to retrieve the object's structure. |\n| name | string | Node's name |\n| override_rack_id | boolean | When replacing an existing node in a rack-aware cluster, allows the new node to be located in a different rack |\n| rack_id | string | Rack ID, overrides cloud config |\n| second_rack_id | string | Second rack ID where node is installed |\n| use_internal_ipv6 | boolean (default:&nbsp;false) | Node uses IPv6 for internal communication |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/bootstrap","title":"Bootstrap object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bootstrap/","summary":"An object for bootstrap configuration","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","children":[{"id":"operate/rs/references/rest-api/objects/bootstrap/cluster_identity","summary":"Documents the cluster_identity object used with Redis Software REST API calls.","title":"Cluster identity object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bootstrap/cluster_identity/"},{"id":"operate/rs/references/rest-api/objects/bootstrap/credentials","summary":"Documents the credentials object used with Redis Software REST API calls.","title":"Credentials object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bootstrap/credentials/"},{"id":"operate/rs/references/rest-api/objects/bootstrap/identity","summary":"Documents the identity object used with Redis Software REST API calls.","title":"Identity object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bootstrap/identity/"},{"id":"operate/rs/references/rest-api/objects/bootstrap/limits","summary":"Documents the limits object used with Redis Software REST API calls.","title":"Limits object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bootstrap/limits/"},{"id":"operate/rs/references/rest-api/objects/bootstrap/node_identity","summary":"Documents the node_identity object used with Redis Software REST API calls.","title":"Node identity object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bootstrap/node_identity/"},{"id":"operate/rs/references/rest-api/objects/bootstrap/paths","summary":"Documents the paths object used with Redis Software REST API calls.","title":"Paths object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bootstrap/paths/"},{"id":"operate/rs/references/rest-api/objects/bootstrap/policy","summary":"Documents the policy object used with Redis Software REST API calls.","title":"Policy object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bootstrap/policy/"},{"id":"operate/rs/references/rest-api/objects/bootstrap/user_defined_module","summary":"An object for user-defined module configuration during bootstrap","title":"user_defined_module object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bootstrap/user_defined_module/"}],"page_type":"content","content_hash":"29d784b6b5f019dd46f615b905262a01442833c8b5c8643eb3364605e23edecf","sections":[{"id":"content","title":"Content","role":"content","text":"A bootstrap configuration object.\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| action | 'create_cluster'<br />'join_cluster'<br />'recover_cluster' | Action to perform |\n| cluster | [cluster_identity](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bootstrap/cluster_identity) object | Cluster to join or create |\n| cnm_https_port | integer | Port to join a cluster with non-default cnm_https port |\n| crdb_coordinator_port | integer, (range: 1024-65535) (default: 9081) | CRDB coordinator port |\n| credentials | [credentials](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bootstrap/credentials) object | Cluster admin credentials |\n| dns_suffixes | object | Explicit configuration of DNS suffixes<br />\n[{\n  \"name\": string,\n  \"cluster_default\": boolean,\n  \"use_aaaa_ns\": boolean,\n  \"use_internal_addr\": boolean,\n  \"slaves\": array\n}, ...]\n<br />**name**: DNS suffix name<br />**cluster_default**: Should this suffix be the default cluster suffix<br />**use_aaaa_ns**: Should AAAA records be published for NS records<br />**use_internal_addr**: Should internal cluster IPs be published for databases<br />**slaves**: List of replica servers that should be published as NS and notified |\n| envoy_admin_port | integer, (range: 1024-65535) | Envoy admin port. Changing this port during runtime might result in an empty response because envoy serves as the cluster gateway.|\n| envoy_mgmt_server_port | integer, (range: 1024-65535) | Envoy management server port|\n| <span class=\"break-all\">gossip_envoy_admin_port</span> | integer, (range: 1024-65535) | Gossip envoy admin port (deprecated; this port is no longer used because gossip functionality is handled by the main envoy process on `envoy_admin_port`) |\n| license | string | License string. If not provided, a trial license is set by default. |\n| max_retries | integer | Max number of retries in case of recoverable errors |\n| node | [node_identity](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bootstrap/node_identity) object | Node description |\n| policy | [policy](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bootstrap/policy) object | Policy object (deprecated; use [`PUT /v1/cluster/policy`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/policy#put-cluster-policy) after cluster creation instead) |\n| recovery_filename | string | Name of backup file to recover from |\n| required_version | string | This node can only join the cluster if all nodes in the cluster have a version greater than the required_version (deprecated as of Redis Software v7.8.6) |\n| retry_time | integer | Max waiting time between retries (in seconds) |\n| user_defined_modules | array of [user_defined_module](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bootstrap/user_defined_module) objects | List of custom modules to download and install during bootstrap. Each node downloads and installs the modules independently. |\n| witness_disk | object | An API object that represents the Witness Disk bootstrap configuration |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/bootstrap/limits","title":"Limits object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bootstrap/limits/","summary":"Documents the limits object used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"1f89fc357db7cafe737f16e50cda02162cdb014fa8f60a937ef2636a98a2a684","sections":[{"id":"content","title":"Content","role":"content","text":"| Name | Type/Value | Description |\n|------|------------|-------------|\n| max_listeners      | integer (default:&nbsp;100)    | Max allowed listeners on node |\n| max_redis_servers  | integer (default:&nbsp;100)    | Max allowed Redis servers on node |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/bootstrap/node_identity","title":"Node identity object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bootstrap/node_identity/","summary":"Documents the node_identity object used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"251a467d2ac121b955dd09d0d599313960300d79b014d829d5e02c7de5364834","sections":[{"id":"content","title":"Content","role":"content","text":"| Name | Type/Value | Description |\n|------|------------|-------------|\n| bigstore_driver | 'rocksdb' | Bigstore driver name or none (deprecated, use the [cluster object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cluster)'s `bigstore_driver` instead) |\n| bigstore_enabled | boolean (default: false) | If `true`, then flash storage is enabled on this node for [Redis Flex and Auto Tiering](https://redis.io/docs/latest/operate/rs/databases/flash) databases. Configurable during [bootstrapping](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bootstrap#post-bootstrap). After bootstrapping, it is read-only. |\n| identity | [identity](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bootstrap/identity) object | Node identity |\n| limits | [limits](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bootstrap/limits) object | Node limits |\n| paths | [paths](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bootstrap/paths) object | Storage paths object |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/bootstrap/paths","title":"Paths object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bootstrap/paths/","summary":"Documents the paths object used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"493a08d042a5ee6e1b3500775f64271714b53e211bd488880e8bed006e0c624f","sections":[{"id":"content","title":"Content","role":"content","text":"| Name | Type/Value | Description |\n|------|------------|-------------|\n| bigstore_path | string | Bigredis storage path |\n| ccs_persistent_path | string | Persistent storage path of CCS |\n| ephemeral_path | string | Ephemeral storage path |\n| persistent_path | string | Persistent storage path |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/bootstrap/policy","title":"Policy object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bootstrap/policy/","summary":"Documents the policy object used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"34be460d53a35bb56b6aca1249b5947722e1807b4901917ace1010cdb45991a5","sections":[{"id":"content","title":"Content","role":"content","text":"| Name | Type/Value | Description |\n|------|------------|-------------|\n| default_fork_evict_ram | boolean (default:&nbsp;false) | If true, the databases should evict data from RAM to ensure successful replication or persistence |\n| default_non_sharded_proxy_policy | **'single'** <br />'all-master-shards'<br />'all-nodes' | Default proxy_policy for newly created non-sharded databases' endpoints |\n| default_sharded_proxy_policy | 'single'<br /> **'all-master-shards'** <br />'all-nodes' | Default proxy_policy for newly created sharded databases' endpoints |\n| default_shards_placement | 'dense'<br /> **'sparse'** | Default shards_placement for newly created databases |\n| rack_aware | boolean | Cluster rack awareness |\n| shards_overbooking | boolean (default:&nbsp;true) | If true, all databases' memory_size settings are ignored during shards placement |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/bootstrap/user_defined_module","title":"user_defined_module object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bootstrap/user_defined_module/","summary":"An object for user-defined module configuration during bootstrap","tags":["docs","operate","rs"],"last_updated":"2025-12-22T11:45:26-06:00","page_type":"content","content_hash":"f51505ab5a8d475d1cfd952e4a36c08aab52d3851c15d408bf190e4d4f54ab8e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"A user-defined module configuration object for bootstrap operations.\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| name | string | Module name for presentation and logging purposes (required) |\n| location | object | Information on where to download the module from (required)<br />{\n  \"location_type\": \"http | https\",\n  \"url\": \"string\",\n  \"credentials\": {\n    \"username\": \"string\",\n    \"password\": \"string\"\n  }\n}<br />**location_type**: The type of location, either `http` or `https` (required)<br />**url**: The URL to download the module zip file from (required)<br />**credentials**: Optional credentials for downloads that require basic authentication |"},{"id":"module-package-requirements","title":"Module package requirements","role":"content","text":"The module must be packaged as a `.zip` file containing:\n\n- **module.json**: A metadata file with module information including:\n  - `module_name`: The actual module name\n  - `version`: Numeric version\n  - `semantic_version`: Semantic version string (for example, \"1.0.0\")\n  - `min_redis_version`: Minimum compatible Redis version\n  - `commands`: List of commands the module provides\n  - `capabilities`: List of module capabilities\n\n- **Module binary**: The compiled `.so` file for the target platform"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/certificates","title":"Certificates object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/certificates/","summary":"An object that represents a certificate","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"8e3e327c9b203d077ce56a87ad8801e88fbbaf83f8ad2adddf5cd968872fe1a8","sections":[{"id":"content","title":"Content","role":"content","text":"An API object that represents a certificate used by a Redis Software cluster.\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| name | \"cm\"<br />\"api\"<br />\"mtls_trusted_ca\"<br />\"proxy\"<br />\"metrics_exporter\"<br />\"syncer\"<br />\"ldap_client\"<br />\"ccs_internode_encryption\"<br />\"data_internode_encryption\"<br />\"sso_service\"<br />\"sso_issuer\" | Certificate type.<br />See the [certificates table](https://redis.io/docs/latest/operate/rs/security/certificates) for the list of cluster certificates and their descriptions. |\n| certificate | string | The certificate in PEM format |\n| key | string | The private key in PEM format |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/check_result","title":"Check result object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/check_result/","summary":"An object that contains the results of a cluster check","tags":["docs","operate","rs"],"last_updated":"2023-12-14T15:27:49+01:00","page_type":"content","content_hash":"abad815d254fe4346761191e787d651ceda7a3ab7bf06f4cda8be2a19e20e304","sections":[{"id":"content","title":"Content","role":"content","text":"Cluster check result\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| cluster_test_result | boolean | Indication if any of the tests failed |\n| nodes | \n[{\n  \"node_uid\": integer,\n  \"result\": boolean,\n  \"error\": string\n}, ...]\n | Nodes results |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/cluster/alert_settings","title":"Alert settings object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cluster/alert_settings/","summary":"Documents the alert_settings object used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"c0ca15e2e5c851edee47028b11599249cd9234376ad4740e9e2daf442467dbae","sections":[{"id":"content","title":"Content","role":"content","text":"| Name | Type/Value | Description |\n|------|------------|-------------|\n| cluster_certs_about_to_expire | <span class=\"break-all\">[cluster_alert_settings_with_threshold](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cluster/cluster_alert_settings_with_threshold)</span> object | Cluster certificate will expire in x days |\n| cluster_even_node_count | boolean (default: false) | True high availability requires an odd number of nodes in the cluster |\n| cluster_flash_overcommit | boolean (default: false) | Flash memory committed to databases is larger than cluster total flash memory |\n| cluster_inconsistent_rl_sw | boolean (default: false) | Some nodes in the cluster are running different versions of Redis Software |\n| cluster_internal_bdb | boolean (default: false) | Issues with internal cluster databases |\n| cluster_license_about_to_expire | <span class=\"break-all\">[cluster_alert_settings_with_threshold](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cluster/cluster_alert_settings_with_threshold)</span> object | Cluster license will expire in x days. This alert is enabled by default. Its default threshold is 7 days before license expiration. |\n| cluster_multiple_nodes_down | boolean (default: false) | Multiple cluster nodes are down (this might cause data loss) |\n| cluster_node_joined | boolean (default: false) | New node joined the cluster |\n| cluster_node_remove_abort_completed | boolean (default: false) | Cancel node remove operation completed |\n| cluster_node_remove_abort_failed | boolean (default: false) | Cancel node remove operation failed |\n| cluster_node_remove_completed | boolean (default: false) | Node removed from the cluster |\n| cluster_node_remove_failed | boolean (default: false) | Failed to remove a node from the cluster |\n| cluster_ocsp_query_failed | boolean (default: false) | Failed to query the OCSP server |\n| cluster_ocsp_status_revoked | boolean (default: false) | OCSP certificate status is REVOKED |\n| cluster_ram_overcommit | boolean (default: false) | RAM committed to databases is larger than cluster total RAM |\n| cluster_too_few_nodes_for_replication | boolean (default: false) | Replication requires at least 2 nodes in the cluster |\n| cluster_users_count_approaches_limit | <span class=\"break-all\">[cluster_alert_settings_with_threshold](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cluster/cluster_alert_settings_with_threshold)</span> object | Number of users approaches the maximum limit of 32,000 users. This alert is enabled with a 90% threshold by default on new clusters. |\n| node_aof_slow_disk_io | boolean (default: false) | AOF reaching disk I/O limits\n| node_checks_error | boolean (default: false) | Some node checks have failed |\n| node_cpu_utilization | <span class=\"break-all\">[cluster_alert_settings_with_threshold](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cluster/cluster_alert_settings_with_threshold)</span> object | Node CPU utilization has reached the threshold value (% of the utilization limit) |\n| node_ephemeral_storage | <span class=\"break-all\">[cluster_alert_settings_with_threshold](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cluster/cluster_alert_settings_with_threshold)</span> object | Node ephemeral storage has reached the threshold value (% of the storage limit) |\n| node_failed | boolean (default: false) | Node failed |\n| node_free_flash | <span class=\"break-all\">[cluster_alert_settings_with_threshold](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cluster/cluster_alert_settings_with_threshold)</span> object | Node flash storage has reached the threshold value (% of the storage limit) |\n| node_insufficient_disk_aofrw | boolean (default: false) | Insufficient AOF disk space |\n| node_internal_certs_about_to_expire | <span class=\"break-all\">[cluster_alert_settings_with_threshold](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cluster/cluster_alert_settings_with_threshold)</span> object| Internal certificate on node will expire in x days |\n| node_memory | <span class=\"break-all\">[cluster_alert_settings_with_threshold](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cluster/cluster_alert_settings_with_threshold)</span> object | Node memory has reached the threshold value (% of the memory limit) |\n| node_net_throughput | <span class=\"break-all\">[cluster_alert_settings_with_threshold](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cluster/cluster_alert_settings_with_threshold)</span> object | Node network throughput has reached the threshold value (bytes/s) |\n| node_persistent_storage | <span class=\"break-all\">[cluster_alert_settings_with_threshold](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cluster/cluster_alert_settings_with_threshold)</span> object | Node persistent storage has reached the threshold value (% of the storage limit) |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/cluster/cluster_alert_settings_with_threshold","title":"Cluster alert settings with threshold object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cluster/cluster_alert_settings_with_threshold/","summary":"Documents the cluster_alert_settings_with_threshold object used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"65d496b89e8c84e7aa0715372a666fec81d581f10ecd6a3d3c6960fcaac71a2e","sections":[{"id":"content","title":"Content","role":"content","text":"| Name | Type/Value | Description |\n|------|------------|-------------|\n| enabled    | boolean (default:&nbsp;false)  | Alert enabled or disabled |\n| threshold  | string                    | Threshold for alert going on/off |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/cluster","title":"Cluster object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cluster/","summary":"An object that represents a cluster","tags":["docs","operate","rs"],"last_updated":"2026-08-17T16:46:23-04:00","children":[{"id":"operate/rs/references/rest-api/objects/cluster/alert_settings","summary":"Documents the alert_settings object used with Redis Software REST API calls.","title":"Alert settings object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cluster/alert_settings/"},{"id":"operate/rs/references/rest-api/objects/cluster/cluster_alert_settings_with_threshold","summary":"Documents the cluster_alert_settings_with_threshold object used with Redis Software REST API calls.","title":"Cluster alert settings with threshold object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cluster/cluster_alert_settings_with_threshold/"},{"id":"operate/rs/references/rest-api/objects/cluster/logrotate_settings","summary":"Documents the logrotate_settings object used with Redis Software REST API calls.","title":"Logrotate settings object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cluster/logrotate_settings/"}],"page_type":"content","content_hash":"30a3d10cc7280e5f955a41f1471a583c2401704b7b5940d9069d66c563a1de97","sections":[{"id":"content","title":"Content","role":"content","text":"An API object that represents the cluster.\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| alert_settings | [alert_settings](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cluster/alert_settings) object | Cluster and node alert settings |\n| <span class=\"break-all\">allow_modules_upload</span> | boolean (default: false) | Allow uploading custom (user-defined) modules to the cluster. When disabled, module upload endpoints are rejected regardless of role. |\n| <span class=\"break-all\">availability_lag_tolerance_ms</span> | integer (default: 100) | The maximum replication lag in milliseconds tolerated between source and replicas during [lag-aware database availability checks](https://redis.io/docs/latest/operate/rs/monitoring/db-availability#lag-aware). |\n| bigstore_driver | \"speedb\"<br />\"rocksdb\" | Storage engine for [Redis Flex or Auto Tiering](https://redis.io/docs/latest/operate/rs/databases/flash) |\n| <span class=\"break-all\">cluster_ssh_public_key</span> | string | Cluster's autogenerated SSH public key |\n|  client_maint_notifications | boolean (default: false) | Toggles sending client maintenance notifications about shard movements |\n| cm_port | integer, (range: 1024-65535) | UI HTTPS listening port |\n| <span class=\"break-all\">cm_session_timeout_minutes</span> | integer (default: 15) | The timeout (in minutes) for the session to the CM |\n| <span class=\"break-all\">cnm_http_max_threads_per_worker</span> | integer (default: 10) | Maximum number of threads per worker in the `cnm_http` service (deprecated) |\n| cnm_http_port | integer, (range: 1024-65535) | API HTTP listening port |\n| cnm_http_workers | integer (default: 1) | Number of workers in the `cnm_http` service |\n| cnm_https_port | integer, (range: 1024-65535) | API HTTPS listening port |\n| control_cipher_suites | string | Specifies the enabled ciphers for the control plane. The ciphers are specified in the format understood by the BoringSSL library. |\n| <span class=\"break-all\">control_cipher_suites_tls_1_3</span> | string | Specifies the enabled TLS 1.3 ciphers for the control plane. The ciphers are specified in the format understood by the BoringSSL library. (read-only) |\n| <span class=\"break-all\">control_plane_basic_authentication</span> | boolean (default: true) | Enable Basic and Digest authentication on inbound cluster management API requests. When `false`, the cluster rejects Basic and Digest authentication; clients must use another configured method, such as certificate (mTLS), JWT, or LDAP authentication. See [Disable basic authentication for the REST API](https://redis.io/docs/latest/operate/rs/security/access-control/disable-basic-authentication). |\n| crdb_coordinator_port | integer, (range: 1024-65535) (default: 9081) | CRDB coordinator port |\n| <span class=\"break-all\">crdt_rest_client_retries</span> | integer | Maximum number of retries for the REST client used by the Active-Active management API |\n| <span class=\"break-all\">crdt_rest_client_timeout</span> | integer | Timeout for REST client used by the Active-Active management API |\n| <span class=\"break-all\">crdt_supported_featureset_version</span> | integer | CRDB supported featureset version (read-only) |\n| <span class=\"break-all\">crdt_supported_featureset_version_map</span> | object | CRDB supported featureset version mapping by Redis version (read-only) |\n| <span class=\"break-all\">crdt_supported_module_featureset_version_map</span> | object | Mapping of module names to per-module-semantic-version supported CRDB featureset versions (read-only) |\n| <span class=\"break-all\">crdt_supported_protocol_versions</span> | array of strings | CRDB supported protocol versions (read-only) |\n| <span class=\"break-all\">crdt_supported_protocol_versions_map</span> | object | CRDB supported protocol versions mapping by Redis version (read-only) |\n| created_time | string | Cluster creation date (read-only) |\n| data_cipher_list | string | Specifies the enabled ciphers for the data plane. The ciphers are specified in the format understood by the OpenSSL library. |\n| <span class=\"break-all\">data_cipher_suites_tls_1_3</span> | string | Specifies the enabled TLS 1.3 ciphers for the data plane. |\n| debuginfo_path | string | Path to a local directory used when generating support packages |\n| <span class=\"break-all\">default_non_sharded_proxy_policy</span> | string (default: single) | Default proxy_policy for newly created non-sharded databases' endpoints (read-only) |\n| <span class=\"break-all\">default_sharded_proxy_policy</span> | string (default: all-master-shards) | Default proxy_policy for newly created sharded databases' endpoints (read-only) |\n| <span class=\"break-all\">disconnect_clients_on_password_removal</span> | \"enabled\"<br />\"disabled\"<br />**\"auto\"** | This flag controls whether client connections using removed, revoked, or rotated passwords are actively disconnected |\n| <span class=\"break-all\">dmc_external_authentication</span> | boolean (default: false) | Enable DMC to call the authentication_service for basic authentication |\n| <span class=\"break-all\">dmc_external_cba_authentication</span> | boolean (default: false) | Enable DMC to call the authentication_service for certificate-based authentication |\n| <span class=\"break-all\">dmc_external_entraid_authentication</span> | boolean (default: false) | Enable DMC to call the authentication_service for entraid authentication |\n| <span class=\"break-all\">dmc_external_ldap_authentication</span> | boolean (default: false) | Enable DMC to call the authentication_service for LDAP-based authentication |\n| email_alerts | boolean (default: false) | Send node/cluster email alerts (requires valid SMTP and email_from settings) |\n| email_from | string | Sender email for automated emails |\n| encrypt_pkeys | boolean (default: false) | Enable or turn off encryption of private keys |\n| envoy_admin_port | integer, (range: 1024-65535) | Envoy admin port. Changing this port during runtime might result in an empty response because envoy serves as the cluster gateway.|\n| <span class=\"break-all\">envoy_max_downstream_connections</span> | integer, (range: 100-2048) | The max downstream connections envoy is allowed to open |\n| <span class=\"break-all\">envoy_mgmt_server_port</span> | integer, (range: 1024-65535) | Envoy management server port|\n| <span class=\"break-all\">gossip_envoy_admin_port</span> | integer, (range: 1024-65535) | Gossip envoy admin port (deprecated; this port is no longer used because gossip functionality is handled by the main envoy process on `envoy_admin_port`) |\n| handle_redirects | boolean (default: false) | Handle API HTTPS requests and redirect to the master node internally |\n| http_support | boolean (default: false) | Enable or turn off HTTP support |\n| logrotate_settings | [logrotate_settings](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cluster/logrotate_settings) object | Settings for logrotate configuration |\n| mask_bdb_credentials | boolean (default: false) | If set to true, the BDB credentials will be masked in the BDB API responses |\n| metrics_auth | boolean (default: false) | If true, requires authentication for requests to the metrics exporter |\n| <span class=\"break-all\">min_control_TLS_version</span> | \"1.2\"<br />\"1.3\" | The minimum version of TLS protocol which is supported at the control path |\n| min_data_TLS_version | \"1.2\"<br />\"1.3\" | The minimum version of TLS protocol which is supported at the data path |\n| <span class=\"break-all\">min_sentinel_TLS_version</span> | \"1.2\"<br />\"1.3\" | The minimum version of TLS protocol which is supported at the data path |\n| <span class=\"break-all\">mtls_authorized_subjects</span> | array | [{<br />  \"CN\": string,<br />  \"O\": string,<br />  \"OU\": [array of strings],<br />  \"L\": string,<br />  \"ST\": string,<br />  \"C\": string<br />}, ...] A list of valid subjects used for additional certificate validations during TLS client authentication. All subject attributes are case-sensitive.<br />**Required subject fields**:<br />\"CN\" for Common Name<br />**Optional subject fields:**<br />\"O\" for Organization<br />\"OU\" for Organizational Unit (array of strings)<br />\"L\" for Locality (city)<br />\"ST\" for State/Province<br />\"C\" for 2-letter country code |\n| <span class=\"break-all\">mtls_certificate_authentication</span> | boolean | Require authentication of client certificates for mTLS connections to the cluster. The API_CA certificate should be configured as a prerequisite. |\n| <span class=\"break-all\">mtls_client_cert_subject_validation_type</span> | \"disabled\"<br />\"san_cn\"<br />\"full_subject\" | Enables additional certificate validations that further limit connections to clients with valid certificates during TLS client authentication.<br />Values:<br />**disabled**: Authenticates clients with valid certificates. No additional validations are enforced.<br />**san_cn**: A client certificate is valid only if its Common Name (CN) matches an entry in the list of valid subjects. Ignores other Subject attributes.<br />**full_subject**: A client certificate is valid only if its Subject attributes match an entry in the list of valid subjects. |\n| multi_commands_opt | **\"disabled\"**<br />\"batch\"<br />\"force_disabled\" | Determines the default `multi_commands_opt` setting for databases in the cluster. If set to `batch`, it reduces the overhead of transaction management by batching multiple commands into a single transaction.<br />Values:<br />**disabled**: Turns off the optimization for all databases except those that override it on the [bdb level](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb). Default value.<br />**batch**: Enables the optimization on all databases except those that override it on the [bdb level](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb).<br />**force_disabled**: Disables the optimization for all databases, even those that override it on the [bdb level](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb). |\n| name | string | Cluster's fully qualified domain name (read-only) |\n| options_method_forbidden | boolean (default: false) | Make OPTIONS http method forbidden over CNM HTTPS port. |\n| password_complexity | boolean (default: false) | Enforce password complexity policy |\n| <span class=\"break-all\">password_expiration_duration</span> | integer (default: 0) | The number of days a password is valid until the user is required to replace it |\n| password_min_length | integer, (range: 8-256) (default: 8) | The minimum length required for a password. |\n| proxy_certificate | string | Cluster's proxy certificate |\n| <span class=\"break-all\">proxy_max_ccs_disconnection_time</span> | integer | Cluster-wide proxy timeout policy between proxy and CCS |\n| rack_aware | boolean | Cluster operates in a rack-aware mode (read-only) |\n| reserved_ports | array of strings | Ports and port ranges that the cluster does not use for database endpoints or internal shard traffic (for example `\"reserved_ports\": [\"11000\", \"13000-13010\", \"20048\"]`) |\n| <span class=\"break-all\">replica_sconns_on_demand</span> | \"enabled\"<br />\"disabled\"<br />**\"auto\"** | Reduces DMC internode connections by at least 50%, conserving sockets, file descriptors, and `KEEPALIVE` traffic |\n| robust_crdt_syncer | boolean (default: false) | If `true`, enables the robust syncer for Active-Active databases |\n| s3_ca_cert | string | Filepath to the PEM-encoded CA certificate to use for validating TLS connections to the S3 server |\n| s3_url | string | Specifies the URL for S3 export and import |\n| <span class=\"break-all\">sentinel_cipher_suites</span> | array | Specifies the list of enabled ciphers for the sentinel service. The supported ciphers are those implemented by the [cipher_suites.go](<https://golang.org/src/crypto/tls/cipher_suites.go>) package. |\n| <span class=\"break-all\">sentinel_cipher_suites_tls_1_3<span> | string | Specifies the list of enabled TLS 1.3 ciphers for the discovery (sentinel) service. The supported ciphers are those implemented by the [cipher_suites.go](<https://golang.org/src/crypto/tls/cipher_suites.go>) package.(read-only) |\n| sentinel_tls_mode | \"allowed\"<br />\"disabled\" <br />\"required\" | Determines whether the discovery service allows, blocks, or requires TLS connections (previously named `sentinel_ssl_policy`)<br />**allowed**: Allows both TLS and non-TLS connections<br />**disabled**: Allows only non-TLS connections<br />**required**: Allows only TLS connections |\n| slave_ha | boolean (default: false) | Enable the replica high-availability mechanism (read-only) |\n| <span class=\"break-all\">slave_ha_bdb_cooldown_period</span> | integer (default: 86400) | Time in seconds between runs of the replica high-availability mechanism on different nodes on the same database (read-only) |\n| <span class=\"break-all\">slave_ha_cooldown_period</span> | integer (default: 3600) | Time in seconds between runs of the replica high-availability mechanism on different nodes (read-only) |\n| <span class=\"break-all\">slave_ha_grace_period</span> | integer (default: 900) | Time in seconds between a node failure and when the replica high-availability mechanism starts relocating shards (read-only) |\n| <span class=\"break-all\">slowlog_in_sanitized_support</span> | boolean | Whether to include slowlogs in the sanitized support package |\n| smtp_host | string | SMTP server for automated emails |\n| smtp_password | string | SMTP server password |\n| smtp_port | integer | SMTP server port for automated emails |\n| smtp_tls_mode | \"none\"<br />\"starttls\"<br />\"tls\" | Specifies which TLS mode to use for SMTP access |\n| smtp_use_tls | boolean (default: false) | Use TLS for SMTP access (deprecated as of Redis Software v4.3.3, use smtp_tls_mode field instead) |\n| smtp_username | string | SMTP server username (pattern does not allow special characters &,\\<,>,\") |\n| <span class=\"break-all\">state_machine_smart_client_handoffs_delay_ms</span> | integer (range: 0-10000) (default: 2000) | For smart client handoffs feature, the time in milliseconds to pause the state machine between sending the notification to clients and performing the operation |\n| syncer_certificate | string | Cluster's syncer certificate |\n| upgrade_mode | boolean (default: false) | Is cluster currently in upgrade mode |\n| use_external_ipv6 | boolean (default: true) | Should redislabs services listen on ipv6 |\n| use_ipv6 | boolean (default: true) | Should redislabs services listen on ipv6 (deprecated as of Redis Software v6.4.2, replaced with use_external_ipv6) |\n| wait_command | boolean (default: true) | Supports Redis wait command (read-only) |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/cluster/logrotate_settings","title":"Logrotate settings object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cluster/logrotate_settings/","summary":"Documents the logrotate_settings object used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"fff5464632d8a00eac41c0cf7336ae16c594b14c3a1e29d1a6c7b282a5ecded3","sections":[{"id":"content","title":"Content","role":"content","text":"| Name | Type/Value | Description |\n|------|------------|-------------|\n| maxage | integer (default: 7) | Remove rotated logs older than the specified number of days |\n| maxsize | string (default: 200M) | The log will rotate after it reaches the specified size |\n| rotate | integer (default: 10) | Determines how many times the log will be rotated. If set to 0, old versions are removed rather than rotated. |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/cluster_settings","title":"Cluster settings object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cluster_settings/","summary":"An object for cluster resource management settings","tags":["docs","operate","rs"],"last_updated":"2026-02-19T10:49:21-06:00","page_type":"content","content_hash":"ecde749c771887b4f488d1fefadf5bd8154df9c2264bb5ff2a017d22601747b8","sections":[{"id":"content","title":"Content","role":"content","text":"Cluster resources management policy\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| acl_pubsub_default | `resetchannels`<br /> `allchannels` | Default pub/sub ACL rule for all databases in the cluster:<br />•`resetchannels` blocks access to all channels (restrictive)<br />•`allchannels` allows access to all channels (permissive) |\n| auto_recovery | boolean (default: false) | Defines whether to use automatic recovery after shard failure |\n| automatic_node_offload | boolean (default: true) | Defines whether the cluster will automatically migrate shards from a node, in case the node is overbooked |\n| <span class=\"break-all\">bigstore_migrate_node_threshold</span> | integer | Minimum free memory (excluding reserved memory) allowed on a node before automatic migration of shards from it to free more memory |\n| <span class=\"break-all\">bigstore_migrate_node_threshold_p</span> | integer | Minimum free memory (excluding reserved memory) allowed on a node before automatic migration of shards from it to free more memory |\n| <span class=\"break-all\">bigstore_provision_node_threshold</span> | integer | Minimum free memory (excluding reserved memory) allowed on a node before new shards can no longer be added to it |\n| <span class=\"break-all\">bigstore_provision_node_threshold_p</span> | integer | Minimum free memory (excluding reserved memory) allowed on a node before new shards can no longer be added to it |\n| data_internode_encryption | boolean | Enable/deactivate encryption of the data plane internode communication |\n| db_conns_auditing | boolean | [Audit connections](https://redis.io/docs/latest/operate/rs/security/audit-events) for new databases by default if set to true. |\n| default_bigstore_version | **`1`**<br />`2` | Determines the default value of `bigstore_version` for new databases with Redis version 7.4:<br />• `1` for Auto Tiering (Redis on Flash version 1). Default version.<br/>• `2` for Redis Flex (Redis on Flash version 2) on databases that support it. Can only be used with the `speedb` driver. |\n| <span class=\"break-all\">default_concurrent_restore_actions</span> | integer | Default number of restore actions allowed at the same time. Set to 0 to allow any number of simultaneous restore actions. |\n| default_fork_evict_ram | boolean | If true, the bdbs should evict data from RAM to ensure successful replication or persistence |\n| <span class=\"break-all\">default_non_sharded_proxy_policy</span> | `single`<br /><br />`all-master-shards`<br /><br />`all-nodes` | Default proxy_policy for newly created non-sharded databases' endpoints |\n| default_oss_sharding | boolean (default: false) | Default hashing policy to use for new databases. This field is for future use only and should not be changed. |\n| default_oss_cluster | boolean (default: false) | Default `oss_cluster` setting for new databases. Enables the OSS Cluster API if `true`, or turns it off if `false`. |\n| <span class=\"break-all\">default_provisioned_redis_version</span> | string | Default Redis version |\n| <span class=\"break-all\">default_recovery_wait_time</span> | integer (default: -1) | The default time for new databases to wait for the persistence file to be available during automatic recovery. -1 means wait forever. |\n| <span class=\"break-all\">default_sharded_proxy_policy</span> | `single`<br /><br />`all-master-shards`<br /><br />`all-nodes` | Default proxy_policy for newly created sharded databases' endpoints |\n| default_shards_placement | `dense`<br />`sparse` | Default shards_placement for a newly created databases |\n| <span class=\"break-all\">default_tracking_table_max_keys_policy</span> | integer (default: 1000000) | Defines the default value of the client-side caching invalidation table size for new databases. 0 makes the cache unlimited. |\n| diskless_repl | `yes`<br />`no` (default: yes) | If `yes`, enable default Redis diskless replication mechanism (deprecated; use `repl_diskless` instead) |\n| <span class=\"break-all\">endpoint_rebind_propagation_grace_time</span> | integer | Time to wait between the addition and removal of a proxy |\n| <span class=\"break-all\">evict_node_use_free_memory</span> | boolean | When evicting a node, use the free memory instead of the provisional memory to check if the shards from the old node fit on the new one |\n| <span class=\"break-all\">expose_hostnames_for_all_suffixes</span> | boolean (default: false) | If true, enables exposing hostnames for non-default DNS suffixes |\n| <span class=\"break-all\">failure_detection_sensitivity</span> | `high`<br />`low` | Predefined thresholds and timeouts for failure detection (previously known as <span class=\"break-all\">`watchdog_profile`</span>)<br />• `high` (previously `local-network`) – high failure detection sensitivity, lower thresholds, faster failure detection and failover<br />• `low` (previously `cloud`) – low failure detection sensitivity, higher tolerance for latency variance (also called network jitter) |\n| <span class=\"break-all\">filter_node_wd_health_metrics</span> | boolean (default: true) | When enabled, `node_wd` health metrics (<span class=\"break-all\">`shard_ping_failures`</span>, <span class=\"break-all\">`endpoint_ping_failures`</span>, <span class=\"break-all\">`dmc_ping_failures`</span> and their duration counterparts) are not emitted to `statsd` |\n| hide_user_data_from_log | boolean (default: false) | Set to `true` to enable the <span class=\"break-all\">`hide-user-data-from-log`</span> Redis configuration setting, which avoids logging user data |\n| <span class=\"break-all\">login_lockout_counter_reset_after</span> | integer | Number of seconds that must elapse between failed sign in attempts before the lockout counter is reset to 0. |\n| login_lockout_duration | integer | Duration (in secs) of account lockout. If set to 0, the account lockout will persist until released by an admin. |\n| login_lockout_threshold | integer | Number of failed sign in attempts allowed before locking a user account |\n| <span class=\"break-all\">master_healthcheck_api_auth</span> | boolean (default: true) | Defines if authentication is required by the local `master_healthcheck` API |\n| max_redis_forks | integer (default: 0) | Maximum number of background processes forked from shards that can exist on the node at any given time. 0 means unlimited. |\n| max_saved_events_per_type | integer | Maximum saved events per event type |\n| max_simultaneous_backups | integer (default: 4) | Maximum number of backup processes allowed at the same time |\n| <span class=\"break-all\">metrics_exporter_expose_bdb_name</span> | boolean (default: false) | If true, adds a label with the database name to relevant metrics |\n| parallel_shards_upgrade | integer | Maximum number of shards to upgrade in parallel |\n| max_slave_full_syncs | integer (default: 0) | Maximum number of simultaneous replica full syncs that can run at any given time. 0 means unlimited. |\n| <span class=\"break-all\">oss_cluster_api_preferred_endpoint_type</span> | `ip`<br />`hostname` (default: ip) | Determines the default endpoint type in the OSS Cluster API for new endpoints |\n| <span class=\"break-all\">oss_cluster_api_preferred_ip_type</span> | `internal`<br />`external` (default: internal) | Determines the default IP type in the OSS Cluster API for new endpoints |\n| <span class=\"break-all\">persistence_cleanup_grace_time</span> | integer | Time in seconds before an unmodified file is considered to be stale and to be removed by persistence cleanup |\n| <span class=\"break-all\">persistence_cleanup_scan_interval</span> | string | [CRON expression](https://en.wikipedia.org/wiki/Cron#CRON_expression) that defines the Redis cleanup schedule |\n| persistent_node_removal | boolean | When removing a node, wait for persistence files to be created for all migrated shards |\n| rack_aware | boolean | Cluster operates in a rack-aware mode |\n| <span class=\"break-all\">redis_migrate_node_threshold</span> | integer | Minimum free memory (excluding reserved memory) allowed on a node before automatic migration of shards from it to free more memory |\n| <span class=\"break-all\">redis_migrate_node_threshold_p</span> | integer | Minimum free memory (excluding reserved memory) allowed on a node before automatic migration of shards from it to free more memory |\n| <span class=\"break-all\">redis_provision_node_threshold</span> | integer | Minimum free memory (excluding reserved memory) allowed on a node before new shards can no longer be added to it |\n| <span class=\"break-all\">redis_provision_node_threshold_p</span> | integer | Minimum free memory (excluding reserved memory) allowed on a node before new shards can no longer be added to it |\n| redis_upgrade_policy | **`major`** <br />`latest` | Create/upgrade Redis Software on databases in the cluster by compatibility with major versions or latest versions of Redis Open Source |\n| repl_diskless | boolean (default: true) | If true, enables the default Redis diskless replication mechanism |\n| resp3_default | boolean (default: true) | Determines the default value of the `resp3` option upon upgrading a database to version 7.2 |\n| shards_overbooking | boolean | If true, all databases' memory_size is ignored during shards placement |\n| show_internals | boolean | Show internal databases (and their shards and endpoints) REST APIs |\n| <span class=\"break-all\">show_metrics_during_state_machine</span> | boolean | Show metrics during state machine operations |\n| slave_ha | boolean | Enable the replica high-availability mechanism. Deprecated as of Redis Software v7.2.4. |\n| <span class=\"break-all\">slave_ha_bdb_cooldown_period</span> | integer | Time in seconds between runs of the replica high-availability mechanism on different nodes on the same database |\n| <span class=\"break-all\">slave_ha_cooldown_period</span> | integer | Time in seconds between runs of the replica high-availability mechanism on different nodes on the same database |\n| slave_ha_grace_period | integer | Time in seconds between a node failure and when the replica high-availability mechanism starts relocating shards |\n| slow_log_max_len | integer (default: 1024) | Set max slow log entries in debug info |\n| use_librdb | boolean | If true, new databases use the new RDB parser instead of the old one |\n| <span class=\"break-all\">witness_disk_update_frequency_divisor</span> | integer (default: 3) | Redis Software updates the witness disk every <span class=\"break-all\">`node_max_update_time / witness_disk_update_frequency_divisor`</span> seconds |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/cm_settings","title":"Cluster Manager settings object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cm_settings/","summary":"A REST API object that represents Cluster Manager UI settings","tags":["docs","operate","rs"],"last_updated":"2024-11-12T09:34:06-06:00","page_type":"content","content_hash":"5ec112df744ee58f43253123e2cda2d3df929ad5c3ce5faff29db24f03806332","sections":[{"id":"content","title":"Content","role":"content","text":"| Name | Type/Value | Description |\n|------|------------|-------------|\n| timezone | string | Configurable [time zone](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) for the Cluster Manager UI. The default time zone is UTC. |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/crdb/cluster_info","title":"CRDB cluster info object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb/cluster_info/","summary":"An object that represents Active-Active cluster info","tags":["docs","operate","rs"],"last_updated":"2026-08-12T11:52:45-05:00","page_type":"content","content_hash":"91a739764456e020cc50eb1b7fc4abd7ce42a725561ea91c0e29ee507966efde","sections":[{"id":"content","title":"Content","role":"content","text":"Configuration details for a cluster that is part of an Active-Active database.\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| certificate_auth | \n{\n  \"client_cert\": string,\n  \"client_key\": string,\n  \"trusted_ca\": string\n}  | Certificate credentials for cluster access, as PEM strings. Required unless you use `credentials`; a request that includes both for the same cluster fails.<br />**client_cert**: Client certificate (required)<br />**client_key**: Client certificate's private key (required)<br />**trusted_ca**: CA that validates the API certificate the peer cluster presents. If omitted, the cluster uses the certificates in its `mtls_trusted_ca.pem` file.<br /><br />See [Certificate-based authentication for cluster management](https://redis.io/docs/latest/operate/rs/security/certificates/certificate-based-authentication#certificate-based-authentication-for-cluster-management). |\n| credentials | \n{\n  \"username\": string,\n  \"password\": string\n}  | Cluster access credentials. Required unless you use `certificate_auth`; a request that includes both for the same cluster fails. |\n| name | string | Cluster fully qualified name, used to uniquely identify the cluster. Typically this is the same as the hostname used in the URL, although in some configurations the URL may point to a different name/address. (required) |\n| replication_endpoint | string | Address to use for peer replication. If not specified, it is assumed that standard cluster naming conventions apply. |\n| replication_tls_sni | string | Cluster SNI for TLS connections |\n| url | string | Cluster access URL (required) |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/crdb/database_config","title":"CRDB database config object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb/database_config/","summary":"An object that represents the database configuration","tags":["docs","operate","rs"],"last_updated":"2026-08-07T07:19:40-05:00","page_type":"content","content_hash":"569e430d18c0df311eef99d2cfe636df381d622cd911d2f28e6b09c3eb30c112","sections":[{"id":"overview","title":"Overview","role":"overview","text":"An object that represents the database configuration. This configuration object is used in two contexts within CRDB objects:\n\n- As `default_db_config` in the main [CRDB object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb) for settings that apply to all instances. In most cases, instances should use the same configuration.\n\n- As `db_config` in individual [instance objects](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb/instance_info) to override `default_db_config` or add configuration values for specific instances. Use `db_config` only when an instance needs different settings than the default configuration."},{"id":"default_db_config-settings","title":"`default_db_config` settings","role":"content","text":""},{"id":"requires-default_db_config","title":"Requires `default_db_config`","role":"content","text":"Use `default_db_config` to set the following fields so they apply to every instance in the Active-Active database. Do not set these fields in `db_config` at the instance level because if they differ between instances, replication can fail to start, stop syncing, or return errors.\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| module_list | array of module objects | List of modules to be loaded to all participating clusters of the Active-Active database<br />[{<br />  \"module_id\": string,<br />  \"module_args\": string,<br />  \"module_name\": string,<br />  \"semantic_version\": string,<br />}, ...]<br />**module_id**: Module UID (deprecated; use `module_name` instead)<br />**module_args**: Module command-line arguments (pattern does not allow special characters &,\\<,>,\")<br />**module_name**: Module's name<br />**semantic_version**: Module's semantic version (deprecated; use `module_args` instead)<br /><br />**module_id** and **semantic_version** are optional as of Redis Software v7.4.2 and deprecated as of v7.8.2. |\n| oss_sharding | boolean (default: false) | An alternative to `shard_key_regex` for using the common case of the OSS shard hashing policy |\n| redis_version | string | Version of the redis-server processes used to create the Active-Active database, for example `7.4` |\n| sharding | boolean (default:&nbsp;false) | Cluster mode (server-side sharding). When true, shard hashing rules must be provided by either `oss_sharding` or `shard_key_regex` |\n| shard_key_regex | `[{ \"regex\": string }, ...]` | Custom keyname-based sharding rules (required if sharding is enabled)<br /><br />To use the default rules you should set the value to:<br />`[{\"regex\": \".*\\\\{(?<tag>.*)\\\\}.*\"}, {\"regex\": \"(?<tag>.*)\"}]` |\n| tls_mode | 'enabled'<br /> **'disabled'** <br />'replica_ssl' | Encrypt communication |"},{"id":"important-to-use-default_db_config","title":"Important to use `default_db_config`","role":"content","text":"The following fields don't break replication if they differ between instances, but mismatches can cause problems over time, such as uneven load, higher latency, or inconsistent behavior across clusters. Set them in `default_db_config` so they apply to every instance in the Active-Active database.\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| eviction_policy | **'noeviction'**<br />'allkeys-lru'<br />'allkeys-lfu'<br />'allkeys-random'<br />'volatile-lru'<br />'volatile-lfu'<br />'volatile-random'<br />'volatile-ttl' | Database memory eviction policy. See [eviction policy](https://redis.io/docs/latest/operate/rs/databases/memory-performance/eviction-policy) for more information. |\n| memory_size | integer (default: 0) | Database memory size limit in bytes. 0 is unlimited. |\n| oss_cluster | boolean (default: false) | Enables OSS Cluster mode |\n| <span class=\"break-all\">oss_cluster_api_preferred_ip_type</span> | 'internal'<br />'external' | Indicates preferred IP type in OSS cluster API |\n| proxy_policy | 'single'<br />'all-master-shards'<br />'all-nodes' | The policy used for proxy binding to the endpoint |\n| rack_aware | boolean (default: false) | Require the database to be always replicated across multiple racks |\n| shards_count | integer (range: 1-512) (default: 1) | Number of database shards |\n| shards_placement | 'dense'<br />'sparse' | Control the density of shards<br />Values:<br />**'dense'**: Shards reside on as few nodes as possible <br /> **'sparse'**: Shards reside on as many nodes as possible |"},{"id":"recommended-default_db_config","title":"Recommended `default_db_config`","role":"content","text":"The following fields don't break replication or cause future issues if they differ between instances. However, if there is no reason to use different values, you should use `default_db_config` to set them across all Active-Active database instances.\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| aof_policy | **'appendfsync-every-sec'** <br />'appendfsync-always' | Policy for Append-Only File data persistence |\n| <span class=\"break-all\">authentication_redis_pass</span> | string | Redis AUTH password (deprecated as of Redis Software v7.2, replaced with multiple passwords feature in version 6.0.X) |\n| data_persistence | 'disabled'<br />'snapshot'<br />**'aof'** | Database on-disk persistence policy. For snapshot persistence, a [snapshot_policy](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/snapshot_policy) must be provided |\n| max_aof_file_size | integer | Maximum AOF file size in bytes |\n| max_aof_load_time | integer (default: 3600) | Maximum AOF reload time in seconds |\n| replication | boolean (default: true) | Database replication |\n| <span class=\"break-all\">replication_oom_threshold_percent</span> | integer (range: 0-20) (default: 5) | Reserved memory buffer percentage below `maxmemory` that blocks client writes while allowing Active-Active replication. Requires Redis database version 8.4 or later. See [Replication OOM protection](https://redis.io/docs/latest/operate/rs/databases/active-active/planning#replication-oom-protection) for more information. |\n| snapshot_policy | array of [snapshot_policy](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/snapshot_policy) objects | Policy for snapshot-based data persistence. A dataset snapshot will be taken every N secs if there are at least M writes changes in the dataset. |"},{"id":"per-instance-db_config-settings","title":"Per-instance `db_config` settings","role":"content","text":"Set the following fields in an individual [instance](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb/instance_info)'s `db_config` because they are specific to that cluster's environment.\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| <span class=\"break-all\">authentication_admin_pass</span> | string | Administrative databases access token |\n| <span class=\"break-all\">authentication_ssl_client_certs</span> | array | List of authorized client certificates. For Active-Active databases, it is strongly advised to configure the client certificates individually for each instance instead of using the default database configuration, even if the same certificate is used across all instances.<br />[{<br />  \"client_cert\": string<br />}, ...]<br />**client_cert**: X.509 PEM (Base64) encoded certificate |\n| cert | string | Optional PEM-encoded server certificate for the underlying database instance |\n| <span class=\"break-all\">enforce_client_authentication</span> | **'enabled'** <br />'disabled' | Require authentication of client certificates for SSL connections to the database. If enabled, a certificate should be provided in either <span class=\"break-all\">`authentication_ssl_client_certs`</span> or <span class=\"break-all\">`authentication_ssl_crdt_certs`</span> |\n| <span class=\"break-all\">mtls_allow_outdated_certs</span> | boolean (default: false) | An optional mTLS relaxation flag for certs verification |\n| <span class=\"break-all\">mtls_allow_weak_hashing</span> | boolean (default: false) | An optional mTLS relaxation flag for certs verification |\n| port | integer | TCP port for database access |\n| private_key | string | Optional PEM-encoded private key matching the certificate for the underlying database instance |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/crdb/health_report/health_report_configuration","title":"CRDB health report configuration object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb/health_report/health_report_configuration/","summary":"An object that represents the database configuration to include in an Active-Active database health report.","tags":["docs","operate","rs"],"last_updated":"2024-05-14T13:33:36-05:00","page_type":"content","content_hash":"59dc0b444149435e174a21e1073f3ecf931919687937558fd8a6f0a00ec24280","sections":[{"id":"content","title":"Content","role":"content","text":"An object that represents the database configuration to include in an Active-Active database health report.\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| causal_consistency | boolean | Enables causal consistency across Active-Active replicas |\n| encryption | boolean | Intercluster encryption |\n| featureset_version | integer | CRDB active FeatureSet version |\n| instances | [{\n  // Unique instance ID\n  \"id\": integer,\n  // Local database instance ID\n  \"db_uid\": string,\n  \"cluster\": {\n    // Cluster FQDN\n    \"name\": string\n    // Cluster access URL\n    \"url\": string\n  }\n}, ...]  | Local database instances  |\n| name | string | Name of database |\n| protocol_version | integer | CRDB active protocol version |\n| status | string | Current status of the configuration.<br/>Possible values:<br/>**posted:**  Configuration was posted to all replicas<br/>**ready:** All replicas have finished processing posted configuration (create a database)<br/>**committed**: Posted configuration is now active on all replicas<br/>**commit-completed:** All replicas have finished processing committed configuration (database is active)<br/>**failed:** Configuration failed to post |\n| version | integer | Database configuration version |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/crdb/health_report","title":"CRDB health report object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb/health_report/","summary":"An object that represents an Active-Active database health report.","tags":["docs","operate","rs"],"last_updated":"2024-02-26T13:11:00+01:00","children":[{"id":"operate/rs/references/rest-api/objects/crdb/health_report/health_report_configuration","summary":"An object that represents the database configuration to include in an Active-Active database health report.","title":"CRDB health report configuration object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb/health_report/health_report_configuration/"}],"page_type":"content","content_hash":"72d3c587dd1c18cd93226c68c50396a6b870a10e0ff0920181d0e36ef44981e9","sections":[{"id":"content","title":"Content","role":"content","text":"An object that represents an Active-Active database health report.\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| active_config_version | integer | Active configuration version |\n| cluster_name | string | Name of local Active-Active cluster |\n| configurations | array of [health_report_configuration](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb/health_report/health_report_configuration) objects | Stored database configurations |\n| connection_error | string | Error string if remote cluster is not available |\n| connections | \n[{\n  \"name\": string,\n  \"replication_links\": [ \n    {\n      \"link_uid\": \"bdb_uid:replica_uid\", \n      \"status\": \"up | down\" \n    } ],\n  \"status\": string\n}, ...]  | Connections to other clusters and their statuses. A replication link's `bdb_uid` is the unique ID of a local database instance ([bdb](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb)) in the current cluster. The `replica_uid` is the unique ID of the database's remote replica, located in the connected cluster. |\n| name | string | Name of the Active-Active database |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/crdb","title":"CRDB object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb/","summary":"An object that represents an Active-Active database","tags":["docs","operate","rs"],"last_updated":"2026-07-06T14:48:53-05:00","children":[{"id":"operate/rs/references/rest-api/objects/crdb/cluster_info","summary":"An object that represents Active-Active cluster info","title":"CRDB cluster info object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb/cluster_info/"},{"id":"operate/rs/references/rest-api/objects/crdb/database_config","summary":"An object that represents the database configuration","title":"CRDB database config object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb/database_config/"},{"id":"operate/rs/references/rest-api/objects/crdb/health_report","summary":"An object that represents an Active-Active database health report.","title":"CRDB health report object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb/health_report/"},{"id":"operate/rs/references/rest-api/objects/crdb/instance_info","summary":"An object that represents Active-Active instance info","title":"CRDB instance info object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb/instance_info/"},{"id":"operate/rs/references/rest-api/objects/crdb/modify_request","summary":"An object to update an Active-Active database","title":"CRDB modify request object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb/modify_request/"}],"page_type":"content","content_hash":"ad6fd890fbe92b89ba32c1237aa7a537ed1d0a58f9a286b6c5aabc569b090f8b","sections":[{"id":"content","title":"Content","role":"content","text":"An object that represents an Active-Active database.\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| guid | string | The global unique ID of the Active-Active database |\n| causal_consistency | boolean | Enables causal consistency across CRDT instances |\n| default_db_config| [CRDB database_config](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb/database_config) object | Default database configuration applied to all instances in the CRDB object. In most cases, instances should use the same configuration. If you need to override `default_db_config` or add configuration values for specific instances, you can use `db_config` in individual [instance objects](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb/instance_info). For a list of which settings must be identical across all instances and which to set per instance, see the [CRDB database config object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb/database_config) reference. |\n| encryption | boolean | Encrypt communication |\n| featureset_version | integer | Active-Active database active FeatureSet version\n| instances | array of [CRDB instance_info](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb/instance_info) objects | |\n| local_databases | object | Mapping of instance IDs for local databases to local BDB IDs<br />[{\n  \"bdb_uid\": string,\n  \"id\": integer\n}, ...]  |\n| managed_by | string | The component that manages the Active-Active database |\n| modules | object | Modules used by the Active-Active database<br />[{\n  \"featureset_version\": integer,\n  \"module_name\": string\n}, ...]  |\n| name | string | Name of Active-Active database |\n| protocol_version | integer | Active-Active database active protocol version |\n| <span class=\"break-all\">volatile_config_fields</span> | array of strings | A list of database configuration fields that will be set even if unchanged |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/crdb/instance_info","title":"CRDB instance info object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb/instance_info/","summary":"An object that represents Active-Active instance info","tags":["docs","operate","rs"],"last_updated":"2026-07-06T14:48:53-05:00","page_type":"content","content_hash":"df0f7248b4988854f6bb20991b9b77532431505646c1e3ffd25e7f584b956987","sections":[{"id":"content","title":"Content","role":"content","text":"An object that represents Active-Active instance info.\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| id | integer | Unique instance ID |\n| cluster | [CRDB cluster_info](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb/cluster_info) object | |\n| compression | integer | Compression level when syncing from this source |\n| db_config | [CRDB database_config](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb/database_config) object | Database configuration for this specific instance. Use `db_config` only when you need to override or add configuration values that differ from the `default_db_config` in the main [CRDB object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb). For a list of which settings must be identical across all instances and which to set per instance, see the [CRDB database config object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb/database_config) reference. |\n| db_uid | string | ID of local database instance. This field is likely to be empty for instances other than the local one. |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/crdb/modify_request","title":"CRDB modify request object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb/modify_request/","summary":"An object to update an Active-Active database","tags":["docs","operate","rs"],"last_updated":"2023-12-14T15:27:49+01:00","page_type":"content","content_hash":"6ebbeea5ebe923596ecb8729442a2dec3e1d95d90fbe44e48f9be724eb0ae876","sections":[{"id":"content","title":"Content","role":"content","text":"An object to update an Active-Active database.\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| add_instances | array of [CRDB instance_info](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb/instance_info) objects | List of new CRDB instances |\n| crdb | [CRDB](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb) object | An object that represents an Active-Active database |\n| force_update | boolean | (Warning: This flag can cause unintended and dangerous changes) Force the configuration update and increment the configuration version even if there is no change to the configuration parameters. If you use force, you can mistakenly cause the other instances to update to the configuration version even though it was not changed. |\n| remove_instances | array of integers | List of unique instance IDs |\n| remove_instances.force_remove | boolean | Force removal of instance from the Active-Active database. Before we remove an instance from an Active-Active database, all of the operations that the instance received from clients must be propagated to the other instances. This is the safe method to remove an instance from the Active-Active database. If the instance does not have connectivity to other instances, the propagation fails and removal fails. To remove an instance that does not have connectivity to other instances, you must use the force flag. The removed instance keeps its data and configuration for the instance. After you remove an instance by force, you must use the purge_instances API on the removed instance. |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/crdb_task","title":"CRDB task object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb_task/","summary":"An object that represents a CRDB task","tags":["docs","operate","rs"],"last_updated":"2025-06-30T08:24:08-05:00","page_type":"content","content_hash":"0c674e4de69bece92bac51cfcbf4ba40b7bf06d5004b8bce06faf4d404b142e8","sections":[{"id":"content","title":"Content","role":"content","text":"An object that represents an Active-Active (CRDB) task.\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| id | string | CRDB task ID (read-only) |\n| crdb_guid | string | Globally unique Active-Active database ID (GUID) (read-only) |\n| ended | string | Timestamp when the task ended (read-only) |\n| errors | \n[{\n  \"cluster_name\": string,\n  \"description\": string,\n  \"error_code\": string\n}, ...]  | Details for errors that occurred on a cluster |\n| operation | string | The operation that is running (read-only) |\n| progress | \n{\n  \"clusters\": [{\n    \"name\": string,\n    \"progress\": string\n  }, ...],\n  \"worker\": string\n}  | • `name`: The instance cluster name<br />• `progress`: The step the instance coordinator is running<br />• `worker`: The step the worker is running |\n| started | string | Timestamp when the task started (read-only) |\n| status | 'queued' <br />'started' <br />'finished' <br />'failed' | CRDB task status (read-only) |\n| worker_name | string | The worker that runs the task (read-only) |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/db-conns-auditing-config","title":"Database connection auditing configuration object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/db-conns-auditing-config/","summary":"An object for database connection auditing settings","tags":["docs","operate","rs"],"last_updated":"2023-12-14T15:27:49+01:00","page_type":"content","content_hash":"8d2748dca22e3e0096c1903e294e6204859a4d34836801e330fb27c89e5cf834","sections":[{"id":"content","title":"Content","role":"content","text":"Database connection auditing configuration\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| audit_address | string | TCP/IP address where one can listen for notifications. |\n| audit_port | integer | Port where one can listen for notifications. |\n| audit_protocol | `TCP`<br />`local` | Protocol used to process notifications. For production systems, `TCP` is the only valid value. |\n| audit_reconnect_interval | integer | Interval (in seconds) between attempts to reconnect to the listener. Default is 1 second. |\n| audit_reconnect_max_attempts | integer | Maximum number of attempts to reconnect. Default is 0 (infinite). |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/db_alerts_settings/bdb_alert_settings_with_threshold","title":"BDB alert settings with threshold object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/db_alerts_settings/bdb_alert_settings_with_threshold/","summary":"Documents the bdb_alert_settings_with_threshold object used with Redis Enterprise Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2023-12-14T15:27:49+01:00","page_type":"content","content_hash":"cafd092f7bd2c3af3a471a423776804e7ca2c995e15ded3eb1edf8f8592b96b7","sections":[{"id":"content","title":"Content","role":"content","text":"| Name | Type/Value | Description |\n|------|------------|-------------|\n| enabled    | boolean (default:&nbsp;false)  | Alert enabled or disabled |\n| threshold  | string                         | Threshold for alert going on/off |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/db_alerts_settings","title":"Database alerts settings object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/db_alerts_settings/","summary":"An object for database alerts configuration","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","children":[{"id":"operate/rs/references/rest-api/objects/db_alerts_settings/bdb_alert_settings_with_threshold","summary":"Documents the bdb_alert_settings_with_threshold object used with Redis Enterprise Software REST API calls.","title":"BDB alert settings with threshold object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/db_alerts_settings/bdb_alert_settings_with_threshold/"}],"page_type":"content","content_hash":"3b2c4c6546fba8aa5721fbe5e9c9e8b2e1132595820302c7619eecfbf5b1a17a","sections":[{"id":"content","title":"Content","role":"content","text":"An API object that represents the database alerts configuration.\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| bdb_backup_delayed | [bdb_alert_settings_with_threshold](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/db_alerts_settings/bdb_alert_settings_with_threshold) object | Periodic backup has been delayed for longer than specified threshold value (minutes) |\n| bdb_crdt_src_high_syncer_lag | [bdb_alert_settings_with_threshold](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/db_alerts_settings/bdb_alert_settings_with_threshold) object | CRDB source sync lag is higher than specified threshold value (seconds) |\n| bdb_crdt_src_syncer_connection_error | [bdb_alert_settings_with_threshold](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/db_alerts_settings/bdb_alert_settings_with_threshold) object | CRDB source sync had a connection error while trying to connect to replica source |\n| bdb_crdt_src_syncer_general_error | [bdb_alert_settings_with_threshold](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/db_alerts_settings/bdb_alert_settings_with_threshold) object | CRDB sync encountered in general error |\n| bdb_high_latency | [bdb_alert_settings_with_threshold](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/db_alerts_settings/bdb_alert_settings_with_threshold) object | Latency is higher than specified threshold value (microsec) |\n| bdb_high_syncer_lag | [bdb_alert_settings_with_threshold](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/db_alerts_settings/bdb_alert_settings_with_threshold) object | Replica of sync lag is higher than specified threshold value (seconds) (deprecated as of Redis Software v5.0.1) |\n| bdb_high_throughput | [bdb_alert_settings_with_threshold](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/db_alerts_settings/bdb_alert_settings_with_threshold) object | Throughput is higher than specified threshold value (requests / sec) |\n| bdb_long_running_action | [bdb_alert_settings_with_threshold](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/db_alerts_settings/bdb_alert_settings_with_threshold) object | An alert for state machines that are running for too long |\n| bdb_low_throughput | [bdb_alert_settings_with_threshold](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/db_alerts_settings/bdb_alert_settings_with_threshold) object | Throughput is lower than specified threshold value (requests / sec) |\n| bdb_ram_dataset_overhead | [bdb_alert_settings_with_threshold](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/db_alerts_settings/bdb_alert_settings_with_threshold) object | Dataset RAM overhead of a shard has reached the threshold value (% of its RAM limit) |\n| bdb_ram_values | [bdb_alert_settings_with_threshold](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/db_alerts_settings/bdb_alert_settings_with_threshold) object | Percent of values kept in a shard's RAM is lower than (% of its key count) |\n| bdb_replica_src_high_syncer_lag | [bdb_alert_settings_with_threshold](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/db_alerts_settings/bdb_alert_settings_with_threshold) object | Replica of source sync lag is higher than specified threshold value (seconds) |\n| bdb_replica_src_syncer_connection_error | [bdb_alert_settings_with_threshold](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/db_alerts_settings/bdb_alert_settings_with_threshold) object | Replica of source sync has connection error while trying to connect replica source |\n| bdb_replica_src_syncer_general_error | [bdb_alert_settings_with_threshold](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/db_alerts_settings/bdb_alert_settings_with_threshold) object | Replica of sync encountered in general error |\n| bdb_shard_num_ram_values | [bdb_alert_settings_with_threshold](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/db_alerts_settings/bdb_alert_settings_with_threshold) object | Number of values kept in a shard's RAM is lower than (values) |\n| bdb_size | [bdb_alert_settings_with_threshold](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/db_alerts_settings/bdb_alert_settings_with_threshold) object | Dataset size has reached the threshold value \\(% of the memory limit) |\n| bdb_syncer_connection_error | [bdb_alert_settings_with_threshold](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/db_alerts_settings/bdb_alert_settings_with_threshold) object | Replica of sync has connection error while trying to connect replica source (deprecated as of Redis Software v5.0.1) |\n| bdb_syncer_general_error | [bdb_alert_settings_with_threshold](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/db_alerts_settings/bdb_alert_settings_with_threshold) object | Replica of sync encountered in general error (deprecated as of Redis Software v5.0.1) |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects","title":"Redis Software REST API objects","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/","summary":"Documents the objects used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","children":[{"id":"operate/rs/references/rest-api/objects/action","summary":"An object that represents cluster actions","title":"Action object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/action/"},{"id":"operate/rs/references/rest-api/objects/alert","summary":"An object that contains alert info","title":"Alert object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/alert/"},{"id":"operate/rs/references/rest-api/objects/bdb","summary":"An object that represents a database","title":"BDB object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/"},{"id":"operate/rs/references/rest-api/objects/bdb_group","summary":"An object that represents a group of databases with a shared memory pool","title":"BDB group object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb_group/"},{"id":"operate/rs/references/rest-api/objects/bootstrap","summary":"An object for bootstrap configuration","title":"Bootstrap object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bootstrap/"},{"id":"operate/rs/references/rest-api/objects/certificates","summary":"An object that represents a certificate","title":"Certificates object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/certificates/"},{"id":"operate/rs/references/rest-api/objects/check_result","summary":"An object that contains the results of a cluster check","title":"Check result object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/check_result/"},{"id":"operate/rs/references/rest-api/objects/cluster","summary":"An object that represents a cluster","title":"Cluster object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cluster/"},{"id":"operate/rs/references/rest-api/objects/cluster_settings","summary":"An object for cluster resource management settings","title":"Cluster settings object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cluster_settings/"},{"id":"operate/rs/references/rest-api/objects/cm_settings","summary":"A REST API object that represents Cluster Manager UI settings","title":"Cluster Manager settings object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cm_settings/"},{"id":"operate/rs/references/rest-api/objects/crdb","summary":"An object that represents an Active-Active database","title":"CRDB object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb/"},{"id":"operate/rs/references/rest-api/objects/crdb_task","summary":"An object that represents a CRDB task","title":"CRDB task object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb_task/"},{"id":"operate/rs/references/rest-api/objects/db_alerts_settings","summary":"An object for database alerts configuration","title":"Database alerts settings object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/db_alerts_settings/"},{"id":"operate/rs/references/rest-api/objects/db-conns-auditing-config","summary":"An object for database connection auditing settings","title":"Database connection auditing configuration object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/db-conns-auditing-config/"},{"id":"operate/rs/references/rest-api/objects/job_scheduler","summary":"An object for job scheduler settings","title":"Job scheduler object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/job_scheduler/"},{"id":"operate/rs/references/rest-api/objects/jwt_authorize","summary":"An object for user authentication or a JW token refresh request","title":"JWT authorize object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/jwt_authorize/"},{"id":"operate/rs/references/rest-api/objects/ldap","summary":"An object that contains the cluster's LDAP configuration","title":"LDAP object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/ldap/"},{"id":"operate/rs/references/rest-api/objects/ldap_mapping","summary":"An object that represents a mapping between an LDAP group and roles","title":"LDAP mapping object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/ldap_mapping/"},{"id":"operate/rs/references/rest-api/objects/metrics_config","summary":"An object that represents the cluster's v2 metrics stream engine configuration","title":"Metrics configuration object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/metrics_config/"},{"id":"operate/rs/references/rest-api/objects/module","summary":"An object that represents a Redis module","title":"Module object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/module/"},{"id":"operate/rs/references/rest-api/objects/module_metadata","summary":"An object that represents Redis module metadata","title":"Module metadata object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/module_metadata/"},{"id":"operate/rs/references/rest-api/objects/node","summary":"An object that represents a node in the cluster","title":"Node object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/node/"},{"id":"operate/rs/references/rest-api/objects/ocsp","summary":"An object that represents the cluster's OCSP configuration","title":"OCSP object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/ocsp/"},{"id":"operate/rs/references/rest-api/objects/ocsp_status","summary":"An object that represents the cluster's OCSP status","title":"OCSP status object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/ocsp_status/"},{"id":"operate/rs/references/rest-api/objects/proxy","summary":"An object that represents a proxy in the cluster","title":"Proxy object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/proxy/"},{"id":"operate/rs/references/rest-api/objects/redis_acl","summary":"An object that represents a Redis access control list (ACL)","title":"Redis ACL object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/redis_acl/"},{"id":"operate/rs/references/rest-api/objects/role","summary":"An object that represents a role","title":"Role object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/role/"},{"id":"operate/rs/references/rest-api/objects/services_configuration","summary":"An object for optional cluster services settings","title":"Services configuration object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/"},{"id":"operate/rs/references/rest-api/objects/shard","summary":"An object that represents a database shard","title":"Shard object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/shard/"},{"id":"operate/rs/references/rest-api/objects/sso","summary":"An object for single sign-on (SSO) configuration","title":"SSO object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/sso/"},{"id":"operate/rs/references/rest-api/objects/state-machine","summary":"An object that represents a state machine.","title":"State machine object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/state-machine/"},{"id":"operate/rs/references/rest-api/objects/statistics","summary":"An object that contains metrics for clusters, databases, nodes, or shards","title":"Statistics","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/statistics/"},{"id":"operate/rs/references/rest-api/objects/suffix","summary":"An object that represents a DNS suffix","title":"Suffix object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/suffix/"},{"id":"operate/rs/references/rest-api/objects/user","summary":"An API object that represents a Redis Software user","title":"User object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/user/"}],"page_type":"content","content_hash":"923dabd4a5c83754e60e242fc99bcf063f161f7ec3f1a010d1bcbbdb44073113","sections":[{"id":"content","title":"Content","role":"content","text":"Certain [REST API requests](https://redis.io/docs/latest/operate/rs/references/rest-api/requests) require you to include specific objects in the request body. Many requests also return objects in the response body.\n\nBoth REST API requests and responses represent these objects as [JSON](https://www.json.org).\n\n\n| Object | Description |\n| --- | --- |\n| [action](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/action/) | An object that represents cluster actions |\n| [alert](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/alert/) | An object that contains alert info |\n| [bdb](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/) | An object that represents a database |\n| [bdb_group](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb_group/) | An object that represents a group of databases with a shared memory pool |\n| [bootstrap](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bootstrap/) | An object for bootstrap configuration |\n| [certificates](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/certificates/) | An object that represents a certificate |\n| [check_result](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/check_result/) | An object that contains the results of a cluster check |\n| [cluster](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cluster/) | An object that represents a cluster |\n| [cluster_settings](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cluster_settings/) | An object for cluster resource management settings |\n| [cm_settings](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cm_settings/) | A REST API object that represents Cluster Manager UI settings |\n| [crdb](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb/) | An object that represents an Active-Active database |\n| [crdb_task](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb_task/) | An object that represents a CRDB task |\n| [db_alerts_settings](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/db_alerts_settings/) | An object for database alerts configuration |\n| [db_conns_auditing_config](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/db-conns-auditing-config/) | An object for database connection auditing settings |\n| [job_scheduler](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/job_scheduler/) | An object for job scheduler settings |\n| [jwt_authorize](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/jwt_authorize/) | An object for user authentication or a JW token refresh request |\n| [ldap](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/ldap/) | An object that contains the cluster's LDAP configuration |\n| [ldap_mapping](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/ldap_mapping/) | An object that represents a mapping between an LDAP group and roles |\n| [metrics_config](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/metrics_config/) | An object that represents the cluster's v2 metrics stream engine configuration |\n| [module](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/module/) | An object that represents a Redis module |\n| [module_metadata](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/module_metadata/) | An object that represents Redis module metadata |\n| [node](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/node/) | An object that represents a node in the cluster |\n| [ocsp](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/ocsp/) | An object that represents the cluster's OCSP configuration |\n| [ocsp_status](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/ocsp_status/) | An object that represents the cluster's OCSP status |\n| [proxy](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/proxy/) | An object that represents a proxy in the cluster |\n| [redis_acl](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/redis_acl/) | An object that represents a Redis access control list (ACL) |\n| [role](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/role/) | An object that represents a role |\n| [services_configuration](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/) | An object for optional cluster services settings |\n| [shard](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/shard/) | An object that represents a database shard |\n| [sso](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/sso/) | An object for single sign-on (SSO) configuration |\n| [state-machine](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/state-machine/) | An object that represents a state machine. |\n| [statistics](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/statistics/) | An object that contains metrics for clusters, databases, nodes, or shards |\n| [suffix](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/suffix/) | An object that represents a DNS suffix |\n| [user](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/user/) | An API object that represents a Redis Software user |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/job_scheduler/backup_job_settings","title":"Backup job settings object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/job_scheduler/backup_job_settings/","summary":"Documents the backup_job_settings object used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"b0a122bd763844679e382ce99848a58180bb68005eeb115d9a53b60fa1dfb58f","sections":[{"id":"content","title":"Content","role":"content","text":"| Name | Type/Value | Description |\n|------|------------|-------------|\n| cron_expression | string | [CRON expression](https://en.wikipedia.org/wiki/Cron#CRON_expression) that defines the backup schedule |\n| enabled | boolean (default: true) | Indicates whether this job is enabled |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/job_scheduler/bdb_usage_report_job_settings","title":"BDB usage report job settings object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/job_scheduler/bdb_usage_report_job_settings/","summary":"Documents the bdb_usage_report_job_settings object used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"946d2525b69a878c268a1b0fa6ea0f4ba1b727c693a0265dd7c8e809fd6eae9a","sections":[{"id":"content","title":"Content","role":"content","text":"| Name | Type/Value | Description |\n|------|------------|-------------|\n| cron_expression | string | [CRON expression](https://en.wikipedia.org/wiki/Cron#CRON_expression) that defines the database usage report schedule |\n| enabled | boolean (default: true) | Indicates whether this job is enabled |\n| file_retention_days | integer, 1-1000 (default: 365) | Number of days after a file is closed before it is deleted |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/job_scheduler/cert_rotation_job_settings","title":"Certificate rotation job settings object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/job_scheduler/cert_rotation_job_settings/","summary":"Documents the cert_rotation_job_settings object used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"d3bee327bca3540e451eb05380574964933640861e98707913833a6a9dc5cbab","sections":[{"id":"content","title":"Content","role":"content","text":"| Name | Type/Value | Description |\n|------|------------|-------------|\n| cron_expression              | string | [CRON expression](https://en.wikipedia.org/wiki/Cron#CRON_expression) that defines the certificate rotation schedule |\n| enabled | boolean (default: true) | Indicates whether this job is enabled |\n| expiry_days_before_rotation  | integer, (range:&nbsp;1-90) (default:&nbsp;60) | Number of days before a certificate expires before rotation |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/job_scheduler/granular_metrics_job_settings","title":"Granular metrics job settings object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/job_scheduler/granular_metrics_job_settings/","summary":"Documents the granular_metrics_job_settings object used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-09-29T16:51:25+03:00","page_type":"content","content_hash":"00b53e09c5ff3aeee061fd61095e45fd04aeae13f3148d416bfea5f1c2c7e3c9","sections":[{"id":"content","title":"Content","role":"content","text":"| Name | Type/Value | Description |\n|------|------------|-------------|\n| cron_expression | string (default: `* * * * *`) | [CRON expression](https://en.wikipedia.org/wiki/Cron#CRON_expression) that defines how often the [granular metrics](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/local-metrics-storage#tiers) lifecycle check runs. The default runs it every minute. |\n| enabled | boolean (default: true) | Indicates whether this job is enabled |\n| granular_cleanup_delay | integer (default: 86400) | Number of seconds after granular collection stops before its data is deleted automatically |\n| granular_max_duration | integer (default: 3600) | Number of seconds granular collection runs before it stops automatically |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/job_scheduler","title":"Job scheduler object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/job_scheduler/","summary":"An object for job scheduler settings","tags":["docs","operate","rs"],"last_updated":"2026-09-29T16:51:25+03:00","children":[{"id":"operate/rs/references/rest-api/objects/job_scheduler/backup_job_settings","summary":"Documents the backup_job_settings object used with Redis Software REST API calls.","title":"Backup job settings object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/job_scheduler/backup_job_settings/"},{"id":"operate/rs/references/rest-api/objects/job_scheduler/bdb_usage_report_job_settings","summary":"Documents the bdb_usage_report_job_settings object used with Redis Software REST API calls.","title":"BDB usage report job settings object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/job_scheduler/bdb_usage_report_job_settings/"},{"id":"operate/rs/references/rest-api/objects/job_scheduler/cert_rotation_job_settings","summary":"Documents the cert_rotation_job_settings object used with Redis Software REST API calls.","title":"Certificate rotation job settings object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/job_scheduler/cert_rotation_job_settings/"},{"id":"operate/rs/references/rest-api/objects/job_scheduler/granular_metrics_job_settings","summary":"Documents the granular_metrics_job_settings object used with Redis Software REST API calls.","title":"Granular metrics job settings object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/job_scheduler/granular_metrics_job_settings/"},{"id":"operate/rs/references/rest-api/objects/job_scheduler/log_rotation_job_settings","summary":"Documents the log_rotation_job_settings object used with Redis Software REST API calls.","title":"Log rotation job settings object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/job_scheduler/log_rotation_job_settings/"},{"id":"operate/rs/references/rest-api/objects/job_scheduler/node_checks_job_settings","summary":"Documents the node_checks_job_settings object used with Redis Software REST API calls.","title":"Node checks job settings object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/job_scheduler/node_checks_job_settings/"},{"id":"operate/rs/references/rest-api/objects/job_scheduler/redis_cleanup_job_settings","summary":"Documents the redis_cleanup_job_settings object used with Redis Software REST API calls.","title":"Redis cleanup job settings object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/job_scheduler/redis_cleanup_job_settings/"},{"id":"operate/rs/references/rest-api/objects/job_scheduler/rotate_ccs_job_settings","summary":"Documents the rotate_ccs_job_settings object used with Redis Software REST API calls.","title":"Rotate CCS job settings object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/job_scheduler/rotate_ccs_job_settings/"}],"page_type":"content","content_hash":"bc39b4a4886bf772da38e33e9cee665dfaf1aff012c24ecefd68d67679dc055f","sections":[{"id":"content","title":"Content","role":"content","text":"An API object that represents the job scheduler settings in the cluster.\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| backup_job_settings | [backup_job_settings](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/job_scheduler/backup_job_settings) object | Backup job settings |\n| <span class=\"break-all\">bdb_usage_report_job_settings</span> | <span class=\"break-all\">[bdb_usage_report_job_settings](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/job_scheduler/bdb_usage_report_job_settings)</span> object | Job settings for database usage reports |\n| <span class=\"break-all\">cert_rotation_job_settings</span> | <span class=\"break-all\">[cert_rotation_job_settings](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/job_scheduler/cert_rotation_job_settings)</span> object | Job settings for internal certificate rotation |\n| <span class=\"break-all\">granular_metrics_job_settings</span> | <span class=\"break-all\">[granular_metrics_job_settings](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/job_scheduler/granular_metrics_job_settings)</span> object | Job settings for granular metrics collection |\n| <span class=\"break-all\">log_rotation_job_settings</span> | <span class=\"break-all\">[log_rotation_job_settings](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/job_scheduler/log_rotation_job_settings)</span> object | Log rotation job settings |\n| <span class=\"break-all\">node_checks_job_settings</span> | <span class=\"break-all\">[node_checks_job_settings](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/job_scheduler/node_checks_job_settings)</span> object | Node checks job settings |\n| <span class=\"break-all\">redis_cleanup_job_settings</span> | <span class=\"break-all\">[redis_cleanup_job_settings](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/job_scheduler/redis_cleanup_job_settings)</span> object | Redis cleanup job settings (deprecated as of Redis Software v6.4.2, replaced with persistence_cleanup_scan_interval) |\n| rotate_ccs_job_settings | [rotate_ccs_job_settings](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/job_scheduler/rotate_ccs_job_settings) object | Rotate CCS job settings |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/job_scheduler/log_rotation_job_settings","title":"Log rotation job settings object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/job_scheduler/log_rotation_job_settings/","summary":"Documents the log_rotation_job_settings object used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"50c7fd7a064cb6e15ca1b92c0fe8639d05579529fe1653682fc9aaffd7b1a281","sections":[{"id":"content","title":"Content","role":"content","text":"| Name | Type/Value | Description |\n|------|------------|-------------|\n| cron_expression | string | [CRON expression](https://en.wikipedia.org/wiki/Cron#CRON_expression) that defines the log rotation schedule |\n| enabled | boolean (default: true) | Indicates whether this job is enabled |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/job_scheduler/node_checks_job_settings","title":"Node checks job settings object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/job_scheduler/node_checks_job_settings/","summary":"Documents the node_checks_job_settings object used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"329438e9d7a65191c256e1874d69475ba75d125e0581b0e960e84fe426ac8973","sections":[{"id":"content","title":"Content","role":"content","text":"| Name | Type/Value | Description |\n|------|------------|-------------|\n| cron_expression | string | [CRON expression](https://en.wikipedia.org/wiki/Cron#CRON_expression) that defines the node checks schedule |\n| enabled | boolean (default: true) | Indicates whether this job is enabled |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/job_scheduler/redis_cleanup_job_settings","title":"Redis cleanup job settings object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/job_scheduler/redis_cleanup_job_settings/","summary":"Documents the redis_cleanup_job_settings object used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"bd382f4bd3b4a97010050acb974e06119f23501d06e9066007f87aab7f19b38e","sections":[{"id":"content","title":"Content","role":"content","text":"Deprecated and replaced with `persistence_cleanup_scan_interval`.\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| cron_expression | string | [CRON expression](https://en.wikipedia.org/wiki/Cron#CRON_expression) that defines the Redis cleanup schedule |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/job_scheduler/rotate_ccs_job_settings","title":"Rotate CCS job settings object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/job_scheduler/rotate_ccs_job_settings/","summary":"Documents the rotate_ccs_job_settings object used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"24748cf5ac55149ab174bb35df547bce7b68913d92e22adf15fd46f3822d235b","sections":[{"id":"content","title":"Content","role":"content","text":"| Name | Type/Value | Description |\n|------|------------|-------------|\n| cron_expression | string | [CRON expression](https://en.wikipedia.org/wiki/Cron#CRON_expression) that defines the CCS rotation schedule |\n| enabled | boolean (default: true) | Indicates whether this job is enabled |\n| file_suffix | string (default:&nbsp;5min) | String added to the end of the rotated RDB files |\n| rotate_max_num | integer, (range:&nbsp;1-100) (default:&nbsp;24) | The maximum number of saved RDB files |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/jwt_authorize","title":"JWT authorize object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/jwt_authorize/","summary":"An object for user authentication or a JW token refresh request","tags":["docs","operate","rs"],"last_updated":"2023-12-14T15:27:49+01:00","page_type":"content","content_hash":"6dc3caaf5db91551b41c06b9ef4527bf1b8f2b93f6126b0c7a64bc3554f3b78a","sections":[{"id":"content","title":"Content","role":"content","text":"An API object for user authentication or a JW token refresh request.\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| password | string | The user’s password (required) |\n| ttl | integer (range: 1-86400) (default: 300) | Time to live - The amount of time in seconds the token will be valid |\n| username | string | The user’s username (required) |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/ldap","title":"LDAP object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/ldap/","summary":"An object that contains the cluster's LDAP configuration","tags":["docs","operate","rs"],"last_updated":"2026-05-14T08:58:05-05:00","page_type":"content","content_hash":"e104fa13afd27415a46e2a54c890e5d4beeaec92c4a50736a264d2514e8966fc","sections":[{"id":"content","title":"Content","role":"content","text":"An API object that represents the cluster's [LDAP](https://redis.io/docs/latest/operate/rs/security/access-control/ldap) configuration.\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| bind_dn | string | DN used when binding with the LDAP server to run queries |\n| bind_pass | string | Password used when binding with the LDAP server to run queries |\n| ca_cert | string | PEM-encoded CA certificate(s) used to validate TLS connections to the LDAP server |\n| cache_ttl | integer (default: 300) | Maximum TTL (in seconds) of cached entries |\n| cba | boolean (default: false) | Whether to allow LDAP as an identity source for certificate-based authentication |\n| cba_identity_oid | string | The certificate subject OID to use when `cba_identity_source` is set to `subject_oid` |\n| cba_identity_source | \"subject_cn\"<br />\"subject_oid\" | The certificate subject identity source to use for LDAP lookup |\n| control_plane | boolean (default: false) | Use LDAP for user authentication/authorization in the control plane |\n| data_plane | boolean (default: false) | Use LDAP for user authentication/authorization in the data plane |\n| directory_timeout_s | integer (range: 5-60) (default: 5) | The connection timeout to the LDAP server when authenticating a user, in seconds |\n| dn_group_attr | string | The name of an attribute of the LDAP user entity that contains a list of the groups that user belongs to. `dn_group_attr` is mutually exclusive with `dn_group_query`. |\n| dn_group_query | complex object | An LDAP search query used to find a user’s groups, which determine the user’s level of access to the cluster and database as defined by Redis ACLs when [mapped to roles](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/ldap_mappings). `dn_group_query` is mutually exclusive with `dn_group_attr`.<br><br>Contains the following fields:<br>**base**: Defines the starting point DN (unique identifier Distinguished Name) in the directory information tree for the search. Example value: `\"DC=example,DC=com\"`<br>**filter**: An [RFC-4515](https://www.rfc-editor.org/info/rfc4515) string representation of the search filter to apply. Defines the conditions required for an entry to appear in the search results. Example value: `\"member=%D\"` where `%D` is replaced with the user's DN.<br>**scope**: Defines the scope of the LDAP search according to the following values:<br>**\"base\"**: Search the base entry.<br>**\"one\"**: Search the base entry's immediate children.<br>**\"subtree\"**: Search the base entry and all its descendants. |\n| starttls | boolean (default: false) | Use StartTLS negotiation for the LDAP connection |\n| uris | array of strings | URIs of LDAP servers that only contain the schema, host, and port |\n| user_dn_query | complex object | An LDAP search query used to find and authenticate LDAP users. `user_dn_query` is mutually exclusive with `user_dn_template`.<br><br>Contains the following fields:<br>**base**: Defines the starting point DN (unique identifier Distinguished Name) in the directory information tree for the search. Example value: `\"DC=example,DC=com\"`<br>**filter**: An [RFC-4515](https://www.rfc-editor.org/info/rfc4515) string representation of the search filter to apply. Defines the conditions required for an entry to appear in the search results. Example value: `\"(&(objectClass=Person)(cn=%u))\"` where `%u` is replaced with the username.<br>**scope**: Defines the scope of the LDAP search according to the following values:<br>**\"base\"**: Search the base entry.<br>**\"one\"**: Search the base entry's immediate children.<br>**\"subtree\"**: Search the base entry and all its descendants. |\n| user_dn_template | string | A string template that maps between the username, provided to the cluster for authentication, and the LDAP DN. The substring \"%u\" will be replaced with the username. `user_dn_template` is mutually exclusive with `user_dn_query`. |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/ldap_mapping","title":"LDAP mapping object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/ldap_mapping/","summary":"An object that represents a mapping between an LDAP group and roles","tags":["docs","operate","rs"],"last_updated":"2023-12-14T15:27:49+01:00","page_type":"content","content_hash":"d8a7ce193db0889035567e78230a3b13f6a33592504ebfd73842376d0b5bdd7c","sections":[{"id":"content","title":"Content","role":"content","text":"An API object that represents an [LDAP mapping](https://redis.io/docs/latest/operate/rs/security/access-control/ldap/map-ldap-groups-to-roles) between an LDAP group and [roles](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/role).\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| uid | integer | LDAP mapping's unique ID |\n| account_id | integer | SM account ID |\n| action_uid | string | Action UID. If it exists, progress can be tracked by the `GET`&nbsp;`/actions/{uid}` API (read-only) |\n| bdbs_email_alerts | complex object | UIDs of databases that associated email addresses will receive alerts for |\n| cluster_email_alerts | boolean | Activate cluster email alerts for an associated email |\n| dn | string | An LDAP group's distinguished name |\n| email | string | Email address used for alerts (if set) |\n| email_alerts | boolean (default:&nbsp;true) | Activate email alerts for an associated email |\n| name | string | Role's name |\n| role_uids | array of integers | List of role UIDs associated with the LDAP group |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/metrics_config","title":"Metrics configuration object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/metrics_config/","summary":"An object that represents the cluster's v2 metrics stream engine configuration","tags":["docs","operate","rs"],"last_updated":"2026-09-29T16:51:25+03:00","page_type":"content","content_hash":"74f34e1428a7f6cc68823743cd2efed17b279bd412ca4a53223c64ab3541559e","sections":[{"id":"content","title":"Content","role":"content","text":"An API object that represents the cluster-wide configuration of the [v2 metrics stream engine](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine).\n\n| Name | Type | Default | Description |\n|------|------|---------|-------------|\n| key_distribution_enabled | boolean | `false` | Enables histogram metrics that report the distribution of key sizes and key item counts |\n| key_size_buckets | string | `\"\"` | Bucket boundaries for the key size distribution histogram. An empty value derives the boundaries from Redis. Otherwise, a comma-separated list of strictly ascending boundaries. Each boundary is a number (bytes) with an optional uppercase unit suffix `B`, `K`, `M`, `G`, `T`, `P`, or `E`; the numeric part must be between 1 and 1023. |\n| key_items_buckets | string | `\"\"` | Bucket boundaries for the key item-count distribution histogram. Uses the same format as `key_size_buckets`. |\n| local_storage_max_size_mb | integer | `1024` | Maximum size, in MB, of on-node [metrics storage](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/local-metrics-storage) (minimum 1) |\n| local_storage_retention_days | integer | `8` | Number of days metrics are retained in on-node [metrics storage](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/local-metrics-storage) (minimum 1). Effective retention can be lower under storage pressure. See [metrics status](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/metrics#get-metrics-status). |\n| expose_db_tags | boolean | `false` | When `true`, enables export of database tags through the `db_tags` metric |\n| metrics_tag_keys_exposed | array of strings | `[]` | The database tag keys that are eligible to be exported as labels (maximum 50 keys). Each key must follow the [tag key validation rules](https://redis.io/docs/latest/operate/rs/databases/configure/db-tags#tag-validation-rules). Keys are matched against database tag keys exactly and case-sensitively. |\n| max_requests_in_flight | integer | `2` | Maximum number of metrics requests the cluster processes concurrently. Set to 0 to remove the limit. |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/module","title":"Module object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/module/","summary":"An object that represents a Redis module","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"9f95dfdbfae2286c55f560834a44850d600b3356f0b0f7c3686878a58c9fa52b","sections":[{"id":"content","title":"Content","role":"content","text":"Represents a [Redis module](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise).\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| uid | string | Cluster unique ID of module |\n| author | string | Module creator |\n| bigstore_version_2_support | boolean (default: false) | Whether the module supports bigstore_version_2 capability, bypassing capability validation (optional) |\n| capabilities | array of strings | List of capabilities supported by this module |\n| capability_name | string | Short description of module functionality |\n| command_line_args | string | Command line arguments passed to the module |\n| compatible_redis_version | string | Redis version required by this module |\n| config_command | string | Name of command to configure module arguments at runtime |\n| crdb | CRDB object | CRDB-related information about the module<br />CRDB object fields:<br />**supported_featureset_versions**: array of integers |\n| dependencies | dependencies object | Module dependencies |\n| description | string | Short description of the module |\n| display_name | string | Name of module for display purposes |\n| email | string | Author's email address |\n| homepage | string | Module's homepage |\n| is_bundled | boolean | Whether module came bundled with a version of Redis Software |\n| license | string | Module is distributed under this license |\n| min_redis_version | string | Minimum Redis database version required by this module. Only relevant for Redis databases earlier than v7.4. |\n| module_file | string | Module filename |\n| module_name | string | Module's name<br />Values:<br />`search`<br />`ReJSON`<br />`timeseries`<br />`bf` |\n| semantic_version | string | Module's semantic version |\n| sha256 | string | SHA256 of module binary (deprecated) |\n| version | integer | Module's version |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/module_metadata","title":"Module metadata object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/module_metadata/","summary":"An object that represents Redis module metadata","tags":["docs","operate","rs"],"last_updated":"2025-10-30T12:00:21-05:00","page_type":"content","content_hash":"90008e647af6dc13a6956a062c3d2be5dcc16bfa987d55018b28da44c62939d2","sections":[{"id":"content","title":"Content","role":"content","text":"Represents a [Redis module](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise).\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| uid | string | Cluster unique ID of module |\n| author | string | Module creator |\n| bigstore_version_2_support | boolean (default: false) | Whether the module supports bigstore_version_2 capability, bypassing capability validation (optional) |\n| capabilities | array of strings | List of capabilities supported by this module |\n| capability_name | string | Short description of module functionality |\n| command_line_args | string | Command-line arguments passed to the module |\n| compatible_redis_version | string | Redis version required by this module |\n| config_command | string | Name of command to configure module arguments at runtime |\n| crdb | CRDB object | CRDB-related information about the module<br />CRDB object fields:<br />**name**: string<br />**supported_featureset_versions**: array of integers |\n| dependencies | dependencies object | Module dependencies |\n| description | string | Short description of the module |\n| display_name | string | Name of module for display purposes |\n| email | string | Author's email address |\n| homepage | string | Module's homepage |\n| license | string | Module is distributed under this license |\n| min_redis_version | string | Minimum Redis database version required by this module. Only relevant for Redis databases earlier than v7.4. |\n| module_file | string | Module filename |\n| module_name | string | Module's name |\n| semantic_version | string | Module's semantic version |\n| sha256 | string | SHA256 of module binary |\n| version | integer | Module's version |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/node","title":"Node object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/node/","summary":"An object that represents a node in the cluster","tags":["docs","operate","rs"],"last_updated":"2026-04-28T13:01:52-05:00","page_type":"content","content_hash":"fe25bd4fd67396f65a458988a29bb289b771a533972003fcbc9c000a46489134","sections":[{"id":"content","title":"Content","role":"content","text":"An API object that represents a node in the cluster.\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| uid | integer | Cluster unique ID of node (read-only) |\n| accept_servers | boolean (default:&nbsp;true) | The node only accepts new shards if `accept_servers` is `true` |\n| addr | string | Internal IP address of node |\n| architecture | string | Hardware architecture (read-only) |\n| bigredis_storage_path | string | Flash storage path (read-only) |\n| bigstore_driver | 'ibm-capi-ga1'<br />'ibm-capi-ga2'<br />'ibm-capi-ga4'<br />'speedb'<br />'rocksdb' | Bigstore driver name or none (deprecated as of Redis Software v7.2, use the [cluster object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cluster)'s `bigstore_driver` instead) |\n| bigstore_enabled | boolean | If `true`, then flash storage is enabled on this node for [Redis Flex and Auto Tiering](https://redis.io/docs/latest/operate/rs/databases/flash) databases. Configurable during [bootstrapping](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bootstrap#post-bootstrap). After bootstrapping, it is read-only. |\n| bigstore_size | integer | Storage size of bigstore storage (read-only) |\n| cores | integer | Total number of CPU cores (read-only) |\n| ephemeral_storage_path | string | Ephemeral storage path (read-only) |\n| ephemeral_storage_size | number | Ephemeral storage size (bytes) (read-only) |\n| external_addr | complex object | External IP addresses of node. `GET`&nbsp;`/jsonschema` to retrieve the object's structure. |\n| max_listeners | integer | Maximum number of listeners on the node |\n| max_redis_forks | integer (default: -1) | Maximum number of background processes forked from shards that can exist on the node at any given time. Set to 0 for unlimited. Set to -1 to use cluster settings. |\n| max_redis_servers | integer | Maximum number of shards on the node |\n| max_slave_full_syncs | integer (default: -1) | Maximum number of simultaneous replica full syncs that can run at any given time. Set to 0 for unlimited. Set to -1 to use cluster settings. |\n| os_family | 'rhel'<br />'ubuntu'<br />'amzn' | Operating system family (read-only) |\n| os_name | string | Operating system name (read-only) |\n| os_semantic_version | string | Full version number (read-only) |\n| os_version | string | Installed OS version (human-readable) (read-only) |\n| oss_cluster_ip_mapping | [{<br />  \"key\": string,<br />  \"value\": object<br />}, ...] | Per-node OSS cluster IP mapping by client subnet/hostname.<br />**key**: Client subnet/hostname key. Can be ipv4/ipv6 CIDR, hostname, or 'default'. |\n| persistent_storage_path | string | Persistent storage path (read-only) |\n| persistent_storage_size | number | Persistent storage size (bytes) (read- only) |\n| public_addr | string | Public IP address of node (deprecated as of Redis Software v4.3.3, use external_addr instead) |\n| rack_id | string | Rack ID where node is installed |\n| recovery_path | string | Recovery files path |\n| second_rack_id | string | Second rack ID where node is installed |\n| shard_count | integer | Number of shards on the node (read-only) |\n| shard_list | array of integers | Cluster unique IDs of all node shards |\n| software_version | string | Installed Redis Software cluster software version (read-only) |\n| status | 'active'<br />'decommissioning'<br />'down'<br />'provisioning' | Node status (read-only) |\n| supported_database_versions | \n[{\n  \"db_type\": string,\n  \"version\": string\n}, ...]\n | Versions of Redis Open Source databases supported by Redis Software on the node (read-only)<br />**db_type**: Type of database<br />**version**: Version of database |\n| system_time | string | System time (UTC) (read-only) |\n| total_memory | integer | Total memory of node (bytes) (read-only) |\n| uptime | integer | System uptime (seconds) (read-only) |\n| use_internal_ipv6 | boolean (default:&nbsp;false) | Node uses IPv6 for internal communication. Value is taken from bootstrap identity (read-only) |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/ocsp","title":"OCSP object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/ocsp/","summary":"An object that represents the cluster's OCSP configuration","tags":["docs","operate","rs"],"last_updated":"2023-12-14T15:27:49+01:00","page_type":"content","content_hash":"b77803d0d366e6bee6590ff54786a16eeea901d705ac5bdd5bf77a492ffbda83","sections":[{"id":"content","title":"Content","role":"content","text":"An API object that represents the cluster's OCSP configuration.\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| ocsp_functionality | boolean (default:&nbsp;false) | Enables or turns off OCSP for the cluster |\n| query_frequency | integer <nobr>(range: 60-86400)</nobr> (default:&nbsp;3600) | The time interval in seconds between OCSP queries to check the certificate’s status |\n| recovery_frequency | integer <nobr>(range: 60-86400)</nobr> (default:&nbsp;60) | The time interval in seconds between retries after the OCSP responder returns an invalid status for the certificate |\n| recovery_max_tries | integer <nobr>(range: 1-100)</nobr> (default:&nbsp;5) | The number of retries before the validation query fails and invalidates the certificate |\n| responder_url | string | The OCSP server URL embedded in the proxy certificate (if available) (read-only) |\n| response_timeout | integer <nobr>(range: 1-60)</nobr> (default:&nbsp;1) | The time interval in seconds to wait for a response before timing out |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/ocsp_status","title":"OCSP status object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/ocsp_status/","summary":"An object that represents the cluster's OCSP status","tags":["docs","operate","rs"],"last_updated":"2023-12-14T15:27:49+01:00","page_type":"content","content_hash":"b7d0f37f592fa98f860f95750f14805faa59f786b633c9309cbe2ec3f38cf179","sections":[{"id":"content","title":"Content","role":"content","text":"An API object that represents the cluster's OCSP status.\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| cert_status | string | Indicates the proxy certificate's status: GOOD/REVOKED/UNKNOWN (read-only) |\n| responder_url | string | The OCSP responder URL this status came from (read-only) |\n| next_update | string | The expected date and time of the next certificate status update (read-only) |\n| produced_at | string | The date and time when the OCSP responder signed this response (read-only) |\n| revocation_time | string | The date and time when the certificate was revoked or placed on hold (read-only) |\n| this_update | string | The most recent time that the responder confirmed the current status (read-only) |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/proxy","title":"Proxy object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/proxy/","summary":"An object that represents a proxy in the cluster","tags":["docs","operate","rs"],"last_updated":"2026-06-18T08:41:25-05:00","page_type":"content","content_hash":"6143e32c99c1e764b5e502d74d23107d683791c061d880b05c79ff9606df4a01","sections":[{"id":"content","title":"Content","role":"content","text":"An API object that represents a [proxy](https://en.wikipedia.org/wiki/Proxy_server) in the cluster.\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| uid | integer | Unique ID of the proxy (read-only) |\n| backlog | integer | TCP listen queue backlog |\n| client_eviction | boolean (default: true) | If `true`, enables client eviction based on `maxmemory_clients`. |\n| client_keepcnt | integer | Client TCP keepalive count |\n| client_keepidle | integer | Client TCP keepalive idle |\n| client_keepintvl | integer | Client TCP keepalive interval |\n| conns | integer | Number of connections |\n| duration_usage_threshold | integer, <nobr>(range: 10-300)</nobr> | Max number of threads |\n| dynamic_threads_scaling | boolean | Automatically adjust the number of threads|\n| ignore_bdb_cconn_limit | boolean | Ignore client connection limits |\n| ignore_bdb_cconn_output_buff_limits | boolean | Ignore buffer limit |\n| incoming_connections_capacity | integer (default: 0) | The maximum number of concurrent incoming connections, also known as the maximum burst size. The default is 0, which means no limit. |\n| incoming_connections_min_capacity | integer (default: 10) | The minimum number of free slots required before accepting new connections after an overflow. |\n| incoming_connections_rate_limit | integer (default: 0) | The maximum number of incoming connections per second. The default is 0, which means no limit. |\n| log_level | `crit`<br />`error`<br />`warn`<br />`info`<br />`trace`<br />`debug` | Minimum log level to log. Only logs with this level or greater will be logged. |\n| max_listeners | integer | Max number of listeners |\n| max_servers | integer | Max number of Redis servers |\n| max_threads | integer, <nobr>(range: 1-256)</nobr> | Max number of threads |\n| max_worker_client_conns | integer | Max client connections per thread |\n| max_worker_server_conns | integer | Max server connections per thread |\n| max_worker_txns | integer | Max in-flight transactions per thread |\n| maxmemory_clients | integer (default: 4294967296) | Maximum total memory, in bytes, used by clients |\n| threads | integer, <nobr>(range: 1-256)</nobr> | Number of threads |\n| threads_usage_threshold | integer, <nobr>(range: 50-99)</nobr> | Max number of threads |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/redis_acl","title":"Redis ACL object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/redis_acl/","summary":"An object that represents a Redis access control list (ACL)","tags":["docs","operate","rs"],"last_updated":"2024-07-30T08:15:00-05:00","page_type":"content","content_hash":"2ae780e0b6fdaee14a68ac63f4acdef1f43ee6764e236a72db8037909567e3e7","sections":[{"id":"content","title":"Content","role":"content","text":"An API object that represents a Redis [access control list (ACL)](https://redis.io/docs/latest/operate/rs/security/access-control/create-db-roles)\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| uid | integer | Object's unique ID |\n| account_id | integer | SM account ID |\n| acl | string | Redis ACL's string |\n| action_uid | string | Action UID. If it exists, progress can be tracked by the `GET`&nbsp;`/actions/{uid}` API (read-only) |\n| name | string | Redis ACL's name |\n| min_version | string | Minimum database version that supports this ACL. Read only |\n| max_version | string | Maximum database version that supports this ACL. Read only |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/role","title":"Role object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/role/","summary":"An object that represents a role","tags":["docs","operate","rs"],"last_updated":"2026-07-21T11:19:25-05:00","page_type":"content","content_hash":"2281cc4acbea2e4a415c2a6463cd780d3b60c009b593185855259743df679e81","sections":[{"id":"content","title":"Content","role":"content","text":"An API object that represents a role.\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| uid | integer | Role's unique ID |\n| account_id | integer | SM account ID |\n| action_uid | string | Action UID. If it exists, progress can be tracked by the GET /actions/{uid} API (read-only) |\n| management | 'admin'<br />'db_member'<br />'db_viewer'<br />'cluster_member'<br />'cluster_viewer'<br />'user_manager'<br />'none' | [Management role](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#roles) |\n| name | string | Role's name |\n| resources | array of objects | Optional list of resource scopes that limit a `db_member` or `db_viewer` management role to specific databases. Each scope has a `type` (currently only `db`) and a `uids` array of database IDs the role applies to. If omitted or empty, the role applies to all databases. Example: `[{\"type\": \"db\", \"uids\": [\"1\", \"2\"]}]` |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/services_configuration/alert_mgr","title":"Alert manager object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/alert_mgr/","summary":"Documents the alert_mgr object used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"2dbb0e909387320966827b0978a75182dbce999d74d3c4d2b132b60a24e7a412","sections":[{"id":"content","title":"Content","role":"content","text":"| Name | Type/Value | Description |\n|------|------------|-------------|\n| operating_mode | 'disabled'<br />'enabled' | Enable/disable the alert manager processes |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/services_configuration/call_home_agent","title":"Call home agent object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/call_home_agent/","summary":"Documents the call_home_agent object used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"cb387b649167087e458a6316ed77e0d04c8ca324d684152eb8d98be73d2517e1","sections":[{"id":"content","title":"Content","role":"content","text":"| Name | Type/Value | Description |\n|------|------------|-------------|\n| operating_mode | 'disabled'<br />'enabled' | Enable/disable the call_home_agent process, which sends daily usage statistics to Redis |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/services_configuration/cm_server","title":"CM server object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/cm_server/","summary":"Documents the cm_server object used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"bc80e8f565cad290891dd57a23119fff77783484de810ec002004568622d480d","sections":[{"id":"content","title":"Content","role":"content","text":"| Name | Type/Value | Description |\n|------|------------|-------------|\n| operating_mode | 'disabled'<br />'enabled' | Enable/disable the CM server |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/services_configuration/crdb_coordinator","title":"CRDB coordinator object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/crdb_coordinator/","summary":"Documents the crdb_coordinator object used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"92ebbf5bf257d428abc56a9ce07db35f1c929397e35ba11987ac58894fc7142f","sections":[{"id":"content","title":"Content","role":"content","text":"| Name | Type/Value | Description |\n|------|------------|-------------|\n| operating_mode | 'disabled'<br />'enabled' | Enable/disable the CRDB coordinator process |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/services_configuration/crdb_worker","title":"CRDB worker object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/crdb_worker/","summary":"Documents the crdb_worker object used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"71ac65174c561f2ed12caabec4713d0b9596f2a1c4db3352b63aa7e335baecdb","sections":[{"id":"content","title":"Content","role":"content","text":"| Name | Type/Value | Description |\n|------|------------|-------------|\n| operating_mode | 'disabled'<br />'enabled' | Enable/disable the CRDB worker processes |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/services_configuration/entraid_agent_mgr","title":"Entra ID agent manager object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/entraid_agent_mgr/","summary":"Documents the entraid_agent_mgr object used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"60ae056d8e30ec9b8aca8930a636d9e661eb4a943e4ceaa80219a9959d7213aa","sections":[{"id":"content","title":"Content","role":"content","text":"| Name | Type/Value | Description |\n|------|------------|-------------|\n| operating_mode | 'disabled'<br />'enabled' | Enable/disable the Entra ID agent manager processes |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/services_configuration","title":"Services configuration object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/","summary":"An object for optional cluster services settings","tags":["docs","operate","rs"],"last_updated":"2026-09-29T16:51:25+03:00","children":[{"id":"operate/rs/references/rest-api/objects/services_configuration/alert_mgr","summary":"Documents the alert_mgr object used with Redis Software REST API calls.","title":"Alert manager object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/alert_mgr/"},{"id":"operate/rs/references/rest-api/objects/services_configuration/call_home_agent","summary":"Documents the call_home_agent object used with Redis Software REST API calls.","title":"Call home agent object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/call_home_agent/"},{"id":"operate/rs/references/rest-api/objects/services_configuration/cm_server","summary":"Documents the cm_server object used with Redis Software REST API calls.","title":"CM server object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/cm_server/"},{"id":"operate/rs/references/rest-api/objects/services_configuration/crdb_coordinator","summary":"Documents the crdb_coordinator object used with Redis Software REST API calls.","title":"CRDB coordinator object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/crdb_coordinator/"},{"id":"operate/rs/references/rest-api/objects/services_configuration/crdb_worker","summary":"Documents the crdb_worker object used with Redis Software REST API calls.","title":"CRDB worker object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/crdb_worker/"},{"id":"operate/rs/references/rest-api/objects/services_configuration/entraid_agent_mgr","summary":"Documents the entraid_agent_mgr object used with Redis Software REST API calls.","title":"Entra ID agent manager object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/entraid_agent_mgr/"},{"id":"operate/rs/references/rest-api/objects/services_configuration/ldap_agent_mgr","summary":"Documents the ldap_agent_mgr object used with Redis Software REST API calls.","title":"LDAP agent manager object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/ldap_agent_mgr/"},{"id":"operate/rs/references/rest-api/objects/services_configuration/mdns_server","summary":"Documents the mdns_server object used with Redis Software REST API calls.","title":"MDNS server object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/mdns_server/"},{"id":"operate/rs/references/rest-api/objects/services_configuration/metrics_local_storage_service","summary":"Documents the metrics_local_storage_service object used with Redis Software REST API calls.","title":"Metrics local storage service object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/metrics_local_storage_service/"},{"id":"operate/rs/references/rest-api/objects/services_configuration/pdns_server","summary":"Documents the pdns_server object used with Redis Software REST API calls.","title":"PDNS server object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/pdns_server/"},{"id":"operate/rs/references/rest-api/objects/services_configuration/sentinel_service","summary":"Documents the sentinel_service object used with Redis Software REST API calls.","title":"Sentinel service object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/sentinel_service/"},{"id":"operate/rs/references/rest-api/objects/services_configuration/stats_archiver","summary":"Documents the stats_archiver object used with Redis Software REST API calls.","title":"Stats archiver object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/stats_archiver/"}],"page_type":"content","content_hash":"b15f822f7de43ad21f786fed22a76bce74a13ae40107fe2eeea16e6d562880f4","sections":[{"id":"content","title":"Content","role":"content","text":"Optional cluster services settings\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| alert_mgr | [alert_mgr](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/alert_mgr) object | Whether to enable/disable the alert manager processes |\n| call_home_agent | [call_home_agent](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/call_home_agent) object | Whether to enable/disable the call_home_agent process, which sends daily usage statistics to Redis |\n| cm_server | [cm_server](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/cm_server) object | Whether to enable/disable the CM server |\n| crdb_coordinator | [crdb_coordinator](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/crdb_coordinator) object | Whether to enable/disable the CRDB coordinator process |\n| crdb_worker | [crdb_worker](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/crdb_worker) object | Whether to enable/disable the CRDB worker processes |\n| entraid_agent_mgr | [entraid_agent_mgr](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/entraid_agent_mgr) object | Whether to enable/disable the Entra ID agent manager process |\n| ldap_agent_mgr | [ldap_agent_mgr](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/ldap_agent_mgr) object | Whether to enable/disable the LDAP agent manager processes |\n| mdns_server | [mdns_server](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/mdns_server) object | Whether to enable/disable the multicast DNS server |\n| metrics_local_storage_service | [metrics_local_storage_service](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/metrics_local_storage_service) object | Whether to enable/disable local metrics storage |\n| pdns_server | [pdns_server](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/pdns_server) object | Whether to enable/disable the PDNS server |\n| sentinel_service | [sentinel_service](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/sentinel_service) object | Whether to enable/disable the Sentinel service process |\n| stats_archiver | [stats_archiver](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/stats_archiver) object | Whether to enable/disable the stats archiver service |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/services_configuration/ldap_agent_mgr","title":"LDAP agent manager object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/ldap_agent_mgr/","summary":"Documents the ldap_agent_mgr object used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"0c225fa6d9cb6416e3439c001f41de4f91cb2646785520fccafd3246863d3e7d","sections":[{"id":"content","title":"Content","role":"content","text":"| Name | Type/Value | Description |\n|------|------------|-------------|\n| operating_mode | 'disabled'<br />'enabled' | Enable/disable the LDAP agent manager processes |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/services_configuration/mdns_server","title":"MDNS server object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/mdns_server/","summary":"Documents the mdns_server object used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"28d9e7b93b3031d824341ab40462fff4918bb1e30aa8c8a894a81f5e5faa8367","sections":[{"id":"content","title":"Content","role":"content","text":"| Name | Type/Value | Description |\n|------|------------|-------------|\n| operating_mode | 'disabled'<br />'enabled' | Enable/disable the multicast DNS server |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/services_configuration/metrics_local_storage_service","title":"Metrics local storage service object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/metrics_local_storage_service/","summary":"Documents the metrics_local_storage_service object used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-09-29T16:51:25+03:00","page_type":"content","content_hash":"7ee8f1210080b63bc746619f62474036c4eb4044625f88f50f3927613f5ecf2f","sections":[{"id":"content","title":"Content","role":"content","text":"| Name | Type/Value | Description |\n|------|------------|-------------|\n| operating_mode | 'disabled' (default)<br />'enabled' | Enable/disable [local metrics storage](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/local-metrics-storage) on all nodes in the cluster |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/services_configuration/pdns_server","title":"PDNS server object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/pdns_server/","summary":"Documents the pdns_server object used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"dde5794a04c63c1a3c5dd9febc909a7eea38206054524af2a8afb5858fcb4847","sections":[{"id":"content","title":"Content","role":"content","text":"| Name | Type/Value | Description |\n|------|------------|-------------|\n| operating_mode | 'disabled'<br />'enabled' | Enable/disable the PDNS server |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/services_configuration/sentinel_service","title":"Sentinel service object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/sentinel_service/","summary":"Documents the sentinel_service object used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"fb45c4e6c55d0c160334fdb04b65cb479f80494bd0c46e903ebf88e7f7fc0923","sections":[{"id":"content","title":"Content","role":"content","text":"| Name | Type/Value | Description |\n|------|------------|-------------|\n| operating_mode | 'disabled'<br />'enabled' | Enable/disable the Sentinel service process |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/services_configuration/stats_archiver","title":"Stats archiver object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration/stats_archiver/","summary":"Documents the stats_archiver object used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"c541cc247d3cbd44336857c6cfb79d8e69d518abddcd819464f4ea32723c2a5a","sections":[{"id":"content","title":"Content","role":"content","text":"| Name | Type/Value | Description |\n|------|------------|-------------|\n| operating_mode | 'disabled'<br />'enabled' | Enable/disable the stats archiver service |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/shard/backup","title":"Backup object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/shard/backup/","summary":"Documents the backup object used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"ae36f54a5ebbe9b7952bd5d108be350a0d2d3cfeee44fbbe8f1e2aef35a79d9b","sections":[{"id":"content","title":"Content","role":"content","text":"| Name | Type/Value | Description |\n|------|------------|-------------|\n| progress  | number, (range: 0-100) | Shard backup progress (percentage) |\n| status    | 'exporting'<br />'succeeded'<br />'failed' | Status of scheduled periodic backup process |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/shard","title":"Shard object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/shard/","summary":"An object that represents a database shard","tags":["docs","operate","rs"],"last_updated":"2024-06-27T07:52:26-07:00","children":[{"id":"operate/rs/references/rest-api/objects/shard/backup","summary":"Documents the backup object used with Redis Software REST API calls.","title":"Backup object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/shard/backup/"},{"id":"operate/rs/references/rest-api/objects/shard/loading","summary":"Documents the loading object used with Redis Software REST API calls.","title":"Loading object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/shard/loading/"},{"id":"operate/rs/references/rest-api/objects/shard/sync","summary":"Documents the sync object used with Redis Software REST API calls.","title":"Sync object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/shard/sync/"}],"page_type":"content","content_hash":"472533ff938f2da463b5fe139539d158cc682760370f7477f2df505e1f43c5aa","sections":[{"id":"content","title":"Content","role":"content","text":"An API object that represents a Redis shard in a database.\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| uid | string | Cluster unique ID of shard |\n| assigned_slots | string | Shards hash slot range |\n| backup | [backup](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/shard/backup) object | Current status of scheduled periodic backup process |\n| bdb_uid | integer | The ID of the database this shard belongs to |\n| bigstore_ram_weight | number | Shards RAM distribution weight |\n| detailed_status | 'busy'<br />'down'<br />'importing'<br />'loading'<br />'ok'<br />'timeout'<br />'trimming'<br />'unknown' | A more detailed status of the shard |\n| loading | [loading](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/shard/loading) object | Current status of dump file loading |\n| node_uid | string | The ID of the node this shard is located on |\n| redis_info | redis_info object | A sub-dictionary of the [Redis INFO command](https://redis.io/docs/latest/commands/info) |\n| report_timestamp | string | The time in which the shard's info was collected (read-only) |\n| role | 'master'<br />'slave' | Role of this shard |\n| status | 'active'<br />'inactive'<br />'trimming' | The current status of the shard |\n| sync | [sync](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/shard/sync.md) object | Shard's current sync status and progress |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/shard/loading","title":"Loading object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/shard/loading/","summary":"Documents the loading object used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"824425bcf786c46514b1a6b4e50c726f62fa379fc00e691cc09529d936bb8b2c","sections":[{"id":"content","title":"Content","role":"content","text":"| Name | Type/Value | Description |\n|------|------------|-------------|\n| progress  | number, (range: 0-100) | Percentage of bytes already loaded |\n| status    | 'in_progress'<br />'idle' | Status of the load of a dump file (read-only) |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/shard/sync","title":"Sync object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/shard/sync/","summary":"Documents the sync object used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"7f8e4dd3953acfb60d654411d365f39b9a1052c12949e46d99ddb64d4310da28","sections":[{"id":"content","title":"Content","role":"content","text":"| Name | Type/Value | Description |\n|------|------------|-------------|\n| progress  | integer        | Number of bytes remaining in current sync |\n| status    | 'in_progress'<br />'idle'<br />'link_down' | Indication of the shard's current sync status |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/sso","title":"SSO object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/sso/","summary":"An object for single sign-on (SSO) configuration","tags":["docs","operate","rs"],"last_updated":"2025-12-22T11:45:26-06:00","page_type":"content","content_hash":"1f085787020ccfffe314287f321c6b60b742aa2c8634dbc15783859eb2a990bd","sections":[{"id":"content","title":"Content","role":"content","text":"An API object that represents single sign-on (SSO) configuration in the cluster.\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| control_plane | boolean (default: false) | If `true`, enables single sign-on (SSO) for the control plane. |\n| enforce_control_plane | boolean (default: false) | If `true`, enforce SSO login for the control plane for non-admin users. If `false`, all users can still login using their local username and password if SSO is down. |\n| protocol | \"saml2\" | SSO protocol to use. |\n| issuer | complex object\t | Issuer related configuration.<br>Contains the following fields:<br>**id**: Unique ID of the issuer side (example: \"urn:sso:example:idp\")<br>**login_url**: SSO login URL (example: \"https://idp.example.com/sso/saml\")<br>**logout_url**: SSO logout URL (example: \"https://idp.example.com/sso/slo\")<br />**metadata**: Base64 encoded IdP metadata (read-only) |\n| service | complex object\t | Service related configuration.<br />For SAML2 service configuration:<br />{\n  \"address\": \"string\",\n  \"saml2\": {\n    \"entity_id\": \"string\",\n    \"acs_url\": \"string\",\n    \"slo_url\": \"string\"\n  }\n}<br>**address**: External service address used for SSO. By default, the cluster name with the Cluster Manager port is used.<br />**acs_url**: Assertion Consumer Service URL (read-only)<br>**slo_url**: Single Logout URL (read-only)<br>**entity_id**: Service entity ID (read-only) |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/state-machine","title":"State machine object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/state-machine/","summary":"An object that represents a state machine.","tags":["docs","operate","rs"],"last_updated":"2023-12-14T15:27:49+01:00","page_type":"content","content_hash":"70adadcaa1e5904233462122951a40e01c77f17243371c47c9fdc3295dbcee3f","sections":[{"id":"content","title":"Content","role":"content","text":"A state machine object tracks the status of database actions.\n\nA state machine contains the following attributes:\n\n| Name        | Type/Value | Description |\n|-------------|------------|-------------|\n| action_uid  | string     | A globally unique identifier of the action |\n| object_name | string     | Name of the object being manipulated by the state machine |\n| status      | pending    | Requested state machine has not started |\n|             | active     | State machine is currently running |\n|             | completed  | Operation complete |\n|             | failed     | Operation or state machine failed |\n| name        | string     | Name of the running (or failed) state machine |\n| state       | string     | Current state within the state machine, when known |\n| error       | string     | A descriptive error string for failed state machine, when known |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/statistics/cluster-metrics","title":"Cluster metrics","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/statistics/cluster-metrics/","summary":"Documents the cluster metrics used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"79cdb8fbf0ce9c775e93c51fc941f3a2d0d2f2db291d5eaf8d06924037792bf6","sections":[{"id":"content","title":"Content","role":"content","text":"The v1 statistics REST API endpoints are deprecated as of Redis Software version 7.22. Use the [metrics stream engine for monitoring v2](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine) instead for real-time monitoring and metrics collection.\n\n\n| Metric name | Type | Description |\n|-------------|------|-------------|\n| available_flash | float | Sum of available flash in all nodes (bytes) |\n| available_memory | float | Sum of available memory in all nodes (bytes) |\n| avg_latency | float | Average latency of requests handled by all cluster endpoints (micro-sec); returned only when there is traffic |\n| bigstore_free | float | Sum of free space of backend flash (used by flash DB's BigRedis) on all cluster nodes (bytes); only returned when BigRedis is enabled |\n| bigstore_iops | float | Rate of I/O operations against backend flash for all shards which are part of a flash-based DB (BigRedis) in the cluster (ops/sec); returned only when BigRedis is enabled |\n| bigstore_kv_ops | float | Rate of value read/write operations against back-end flash for all shards which are part of a flash based DB (BigRedis) in cluster (ops/sec); only returned when BigRedis is enabled |\n| bigstore_throughput | float | Throughput I/O operations against backend flash for all shards which are part of a flash-based DB (BigRedis) in the cluster (bytes/sec); only returned when BigRedis is enabled |\n| conns | float | Total number of clients connected to all cluster endpoints |\n| cpu_idle | float | CPU idle time portion, the value is weighted between all nodes based on number of cores in each node (0-1, multiply by 100 to get percent) |\n| cpu_system | float | CPU time portion spent in kernel on the cluster, the value is weighted between all nodes based on number of cores in each node (0-1, multiply by 100 to get percent) |\n| cpu_user | float | CPU time portion spent by users-pace processes on the cluster. The value is weighted between all nodes based on number of cores in each node (0-1, multiply by 100 to get percent). |\n| egress_bytes | float | Sum of rate of outgoing network traffic on all cluster nodes (bytes/sec) |\n| ephemeral_storage_avail | float | Sum of disk space available to Redis Software processes on configured ephemeral disk on all cluster nodes (bytes) |\n| ephemeral_storage_free | float | Sum of free disk space on configured ephemeral disk on all cluster nodes (bytes) |\n| free_memory | float | Sum of free memory in all cluster nodes (bytes) |\n| ingress_bytes | float | Sum of rate of incoming network traffic on all cluster nodes (bytes/sec) |\n| persistent_storage_avail | float | Sum of disk space available to Redis Software processes on configured persistent disk on all cluster nodes (bytes) |\n| persistent_storage_free | float | Sum of free disk space on configured persistent disk on all cluster nodes (bytes) |\n| provisional_flash | float | Sum of provisional flash in all nodes (bytes) |\n| provisional_memory | float | Sum of provisional memory in all nodes (bytes) |\n| total_req | float | Request rate handled by all endpoints on the cluster (ops/sec) |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/statistics/db-metrics","title":"DB metrics","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/statistics/db-metrics/","summary":"Documents the DB metrics used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"8027bbfcc7a1a20f334515f8aa37058fda974e3227d950152a02e94f7200d3c4","sections":[{"id":"content","title":"Content","role":"content","text":"The v1 statistics REST API endpoints are deprecated as of Redis Software version 7.22. Use the [metrics stream engine for monitoring v2](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine) instead for real-time monitoring and metrics collection.\n\n\n| Metric name | Type | Description |\n|-------------|------|-------------|\n| avg_latency | float | Average latency of operations on the DB (microseconds). Only returned when there is traffic. |\n| avg_other_latency | float | Average latency of other (non read/write) operations (microseconds). Only returned when there is traffic. |\n| avg_read_latency | float | Average latency of read operations (microseconds). Only returned when there is traffic. |\n| avg_write_latency | float | Average latency of write operations (microseconds). Only returned when there is traffic. |\n| big_del_flash | float | Rate of key deletes for keys on flash (BigRedis) (key access/sec). Only returned when BigRedis is enabled. |\n| big_del_ram | float | Rate of key deletes for keys in RAM (BigRedis) (key access/sec); this includes write misses (new keys created). Only returned when BigRedis is enabled. |\n| big_fetch_flash | float | Rate of key reads/updates for keys on flash (BigRedis) (key access/sec). Only returned when BigRedis is enabled. |\n| big_fetch_ram | float | Rate of key reads/updates for keys in RAM (BigRedis) (key access/sec). Only returned when BigRedis is enabled. |\n| big_io_ratio_flash | float | Rate of key operations on flash. Can be used to compute the ratio of I/O operations (key access/sec). Only returned when BigRedis is enabled. |\n| big_io_ratio_redis | float | Rate of Redis operations on keys. Can be used to compute the ratio of I/O operations (key access/sec). Only returned when BigRedis is enabled. |\n| big_write_flash | float | Rate of key writes for keys on flash (BigRedis) (key access/sec). Only returned when BigRedis is enabled. |\n| big_write_ram | float | Rate of key writes for keys in RAM (BigRedis) (key access/sec); this includes write misses (new keys created). Only returned when BigRedis is enabled. |\n| bigstore_io_dels | float | Rate of key deletions from flash (key access/sec). Only returned when BigRedis is enabled. |\n| bigstore_io_read_bytes | float | Throughput of I/O read operations against backend flash |for all shards of the DB (BigRedis) (bytes/sec). Only returned when BigRedis is enabled. |\n| bigstore_io_reads | float | Rate of key reads from flash (key access/sec). Only returned when BigRedis is enabled. |\n| bigstore_io_write_bytes | float | Throughput of I/O write operations against backend flash |for all shards of the DB (BigRedis) (bytes/sec). Only returned when BigRedis is enabled. |\n| bigstore_io_writes | float | Rate of key writes from flash (key access/sec). Only returned when BigRedis is enabled. |\n| bigstore_iops | float | Rate of I/O operations against backend flash for all shards of the DB (BigRedis) (ops/sec). Only returned when BigRedis is enabled. |\n| bigstore_kv_ops | float | Rate of value read/write/del operations against backend flash for all shards of the DB (BigRedis) (key access/sec). Only returned when BigRedis is enabled |\n| bigstore_objs_flash | float | Value count on flash (BigRedis). Only returned when BigRedis is enabled. |\n| bigstore_objs_ram | float | Value count in RAM (BigRedis). Only returned when BigRedis is enabled. |\n| bigstore_throughput | float | Throughput of I/O operations against backend flash for all shards of the DB (BigRedis) (bytes/sec). Only returned when BigRedis is enabled. |\n| conns | float | Number of client connections to the DB’s endpoints |\n| disk_frag_ratio | float | Flash fragmentation ratio (used/required). Only returned when BigRedis is enabled. |\n| egress_bytes | float | Rate of outgoing network traffic to the DB’s endpoint (bytes/sec) |\n| evicted_objects | float | Rate of key evictions from DB (evictions/sec) |\n| expired_objects | float | Rate keys expired in DB (expirations/sec) |\n| fork_cpu_system | float | % cores utilization in system mode for all Redis shard fork child processes of this database |\n| fork_cpu_user | float | % cores utilization in user mode for all Redis shard fork child processes of this database |\n| ingress_bytes | float | Rate of incoming network traffic to the DB’s endpoint (bytes/sec) |\n| instantaneous_ops_per_sec | float | Request rate handled by all shards of the DB (ops/sec) |\n| last_req_time | date, ISO_8601 format | Last request time received to the DB (ISO format 2015-07-05T22:16:18Z). Returns 1/1/1970 when unavailable. |\n| last_res_time | date, ISO_8601 format | Last response time received from DB (ISO format 2015-07-05T22:16:18Z). Returns 1/1/1970 when unavailable. |\n| main_thread_cpu_system | float | % cores utilization in system mode for all Redis shard main threads of this database |\n| main_thread_cpu_user | float | % cores utilization in user mode for all Redis shard main threads of this database |\n| mem_frag_ratio | float | RAM fragmentation ratio (RSS/allocated RAM) |\n| mem_not_counted_for_evict | float | Portion of used_memory (in bytes) not counted for eviction and OOM errors |\n| mem_size_lua | float | Redis Lua scripting heap size (bytes) |\n| monitor_sessions_count | float | Number of client connected in monitor mode to the DB |\n| no_of_expires | float | Number of volatile keys in the DB |\n| no_of_keys | float | Number of keys in the DB |\n| other_req | float | Rate of other (non read/write) requests on DB (ops/sec) |\n| other_res | float | Rate of other (non read/write) responses on DB (ops/sec) |\n| pubsub_channels | float | Count the pub/sub channels with subscribed clients |\n| pubsub_patterns | float | Count the pub/sub patterns with subscribed clients |\n| ram_overhead | float | Non values RAM overhead (BigRedis) (bytes). Only returned when BigRedis is enabled. |\n| read_hits | float | Rate of read operations accessing an existing key (ops/sec) |\n| read_misses | float | Rate of read operations accessing a nonexistent key (ops/sec) |\n| read_req | float | Rate of read requests on DB (ops/sec) |\n| read_res | float | Rate of read responses on DB (ops/sec) |\n| shard_cpu_system | float | % cores utilization in system mode for all Redis shard processes of this database |\n| shard_cpu_user | float | % cores utilization in user mode for the Redis shard process |\n| total_connections_received | float | Rate of new client connections to the DB (connections/sec) |\n| total_req | float | Rate of all requests on DB (ops/sec) |\n| total_res | float | Rate of all responses on DB (ops/sec) |\n| used_bigstore | float | Flash used by DB (BigRedis) (bytes). Only returned when BigRedis is enabled. |\n| used_memory | float | Memory used by DB (in BigRedis this includes flash) (bytes) |\n| used_ram | float | RAM used by DB (BigRedis) (bytes). Only returned when BigRedis is enabled. |\n| write_hits | float | Rate of write operations accessing an existing key (ops/sec) |\n| write_misses | float | Rate of write operations accessing a nonexistent key (ops/sec) |\n| write_req | float | Rate of write requests on DB (ops/sec) |\n| write_res | float | Rate of write responses on DB (ops/sec) |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/statistics","title":"Statistics","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/statistics/","summary":"An object that contains metrics for clusters, databases, nodes, or shards","tags":["docs","operate","rs"],"last_updated":"2026-10-05T16:11:44+01:00","children":[{"id":"operate/rs/references/rest-api/objects/statistics/cluster-metrics","summary":"Documents the cluster metrics used with Redis Software REST API calls.","title":"Cluster metrics","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/statistics/cluster-metrics/"},{"id":"operate/rs/references/rest-api/objects/statistics/db-metrics","summary":"Documents the DB metrics used with Redis Software REST API calls.","title":"DB metrics","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/statistics/db-metrics/"},{"id":"operate/rs/references/rest-api/objects/statistics/node-metrics","summary":"Documents the node metrics used with Redis Software REST API calls.","title":"Node metrics","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/statistics/node-metrics/"},{"id":"operate/rs/references/rest-api/objects/statistics/shard-metrics","summary":"Documents the shard metrics used with Redis Software REST API calls.","title":"Shard metrics","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/statistics/shard-metrics/"}],"page_type":"content","content_hash":"18e93d5f08e9af03617cabbb4cffc3b9dfdabd7cce775688afbbf99b9380e4fc","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The v1 statistics REST API endpoints are deprecated as of Redis Software version 7.22. Use the [metrics stream engine for monitoring v2](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine) instead for real-time monitoring and metrics collection."},{"id":"statistics-overview","title":"Statistics overview","role":"content","text":"Clusters, databases, nodes, and shards collect various statistics at regular time intervals. View the statistics for these objects using `GET stats` requests to their respective endpoints:\n- [Cluster stats](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/stats)\n- [Database stats](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/stats)\n- [Node stats](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/nodes/stats)\n- [Shard stats](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/shards/stats)\n\nView endpoint stats using `GET` requests, see:\n- [Endpoint stats](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/endpoints-stats)"},{"id":"response-object","title":"Response object","role":"returns","text":"Statistics returned from API requests always contain the following fields:\n- `interval`: a string that represents the statistics time interval. Valid values include: \n    - 1sec\n    - 10sec\n    - 5min\n    - 15min\n    - 1hour\n    - 12hour\n    - 1week\n- `stime`: a timestamp that represents the beginning of the interval, in the format \"2015-05-27T12:00:00Z\"\n- `etime`: a timestamp that represents the end of the interval, in the format \"2015-05-27T12:00:00Z\"\n\nThe statistics returned by the API also contain fields that represent the values of different metrics for an object during the specified time interval.\n\nMore details about the metrics relevant to each object:\n- [Cluster metrics](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/statistics/cluster-metrics)\n- [DB metrics](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/statistics/db-metrics)\n- [Node metrics](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/statistics/node-metrics)\n- [Shard metrics](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/statistics/shard-metrics)\n\n> [!NOTE]\n> Certain statistics are not documented because they are for internal use only and should be ignored. Some statistics will only appear in API responses when they are relevant."},{"id":"optional-url-parameters","title":"Optional URL parameters","role":"parameters","text":"There are several optional URL parameters you can pass to the various `GET stats` requests to filter the returned statistics.\n\n- `stime`: limit the start of the time range of the returned statistics\n- `etime`: limit the end of the time range of the returned statistics\n- `metrics`: only return the statistics for the specified metrics (comma-separated list)"},{"id":"maximum-number-of-samples-per-interval","title":"Maximum number of samples per interval","role":"content","text":"The system retains a maximum number of most recent samples for each interval.\n\n| Interval | Max samples |\n|----------|-------------|\n| 1sec     | 10 |\n| 10sec    | 30 |\n| 5min     | 12 |\n| 15min    | 96 |\n| 1hour    | 168 |\n| 12hour   | 62 |\n| 1week    | 53 |\n\nThe actual number of samples returned by a `GET stats` request depends on how many samples are available and any filters applied by the optional URL parameters. For example, newly created objects (clusters, nodes, databases, or shards) or a narrow time filter range will return fewer samples.\n\n> [!NOTE]\n> To reduce load generated by stats collection, relatively inactive databases or shards (less than 5 ops/sec) do not collect 1sec stats at one second intervals. Instead, they collect 1sec stats every 2-5 seconds but still retain the same maximum number of samples."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/statistics/node-metrics","title":"Node metrics","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/statistics/node-metrics/","summary":"Documents the node metrics used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"6fa1cb8474427db55001ad899bf0585a13cc380b1df42957deb2db5c49daef17","sections":[{"id":"content","title":"Content","role":"content","text":"The v1 statistics REST API endpoints are deprecated as of Redis Software version 7.22. Use the [metrics stream engine for monitoring v2](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine) instead for real-time monitoring and metrics collection.\n\n\n| Metric name | Type | Description |\n|-------------|------|-------------|\n| available_flash | float | Available flash on the node (bytes) |\n| available_memory | float | Available RAM on the node (bytes) |\n| avg_latency | float | Average latency of requests handled by endpoints on the node (micro-sec); returned only when there is traffic |\n| bigstore_free | float | Free space of backend flash (used by flash DB's BigRedis) (bytes); returned only when BigRedis is enabled |\n| bigstore_iops | float | Rate of I/O operations against backend flash for all shards which are part of a flash-based DB (BigRedis) on the node (ops/sec); returned only when BigRedis is enabled |\n| bigstore_kv_ops | float | Rate of value read/write operations against backend flash for all shards which are part of a flash-based DB (BigRedis) on the node (ops/sec); returned only when BigRedis is enabled |\n| bigstore_throughput | float | Throughput of I/O operations against backend flash for all shards which are part of a flash-based DB (BigRedis) on the node (bytes/sec); returned only when BigRedis is enabled |\n| conns | float | Number of clients connected to endpoints on the node |\n| cpu_idle | float | CPU idle time portion (0-1, multiply by 100 to get percent) |\n| cpu_system | float | CPU time portion spent in kernel (0-1, multiply by 100 to get percent) |\n| cpu_user | float | CPU time portion spent by users-pace processes (0-1, multiply by 100 to get percent) |\n| cur_aof_rewrites | float | Number of current AOF rewrites by shards on this node |\n| egress_bytes | float | Rate of outgoing network traffic to the node (bytes/sec) |\n| ephemeral_storage_avail | float | Disk space available to Redis Software processes on configured ephemeral disk (bytes) |\n| ephemeral_storage_free | float | Free disk space on configured ephemeral disk (bytes) |\n| free_memory | float | Free memory on the node (bytes) |\n| ingress_bytes | float | Rate of incoming network traffic to the node (bytes/sec) |\n| persistent_storage_avail | float | Disk space available to Redis Software processes on configured persistent disk (bytes) |\n| persistent_storage_free | float | Free disk space on configured persistent disk (bytes) |\n| provisional_flash | float | Amount of flash available for new shards on this node, taking into account overbooking, max Redis servers, reserved flash, and provision and migration thresholds (bytes) |\n| provisional_memory | float | Amount of RAM available for new shards on this node, taking into account overbooking, max Redis servers, reserved memory, and provision and migration thresholds (bytes) |\n| total_req | float | Request rate handled by endpoints on the node (ops/sec) |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/statistics/shard-metrics","title":"Shard metrics","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/statistics/shard-metrics/","summary":"Documents the shard metrics used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"402adf381f79e14e77f22d212d32791a2d6d42c57782374af899f8bca5ea02dd","sections":[{"id":"content","title":"Content","role":"content","text":"The v1 statistics REST API endpoints are deprecated as of Redis Software version 7.22. Use the [metrics stream engine for monitoring v2](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine) instead for real-time monitoring and metrics collection.\n\n\n| Metric name | Type | Description |\n|-------------|------|-------------|\n| aof_rewrite_inprog | float | The number of simultaneous AOF rewrites that are in progress |\n| avg_ttl | float | Estimated average time to live of a random key (msec) |\n| big_del_flash | float | Rate of key deletes for keys on flash (BigRedis) (key access/sec). Only returned when BigRedis is enabled. |\n| big_del_ram | float | Rate of key deletes for keys in RAM (BigRedis) (key access/sec); this includes write misses (new keys created). Only returned when BigRedis is enabled. |\n| big_fetch_flash | float | Rate of key reads/updates for keys on flash (BigRedis) (key access/sec). Only returned when BigRedis is enabled. |\n| big_fetch_ram | float | Rate of key reads/updates for keys in RAM (BigRedis) (key access/sec). Only returned when BigRedis is enabled. |\n| big_io_ratio_flash | float | Rate of key operations on flash. Can be used to compute the ratio of I/O operations (key access/sec). Only returned when BigRedis is enabled. |\n| big_io_ratio_redis | float | Rate of Redis operations on keys. Can be used to compute the ratio of I/O operations) (key access/sec). Only returned when BigRedis is enabled. |\n| big_write_flash | float | Rate of key writes for keys on flash (BigRedis) (key access/sec). Only returned when BigRedis is enabled. |\n| big_write_ram | float | Rate of key writes for keys in RAM (BigRedis) (key access/sec); this includes write misses (new keys created). Only returned when BigRedis is enabled. |\n| bigstore_io_dels | float | Rate of key deletions from flash (key access/sec). Only returned when BigRedis is enabled. |\n| bigstore_io_read_bytes | float | Throughput of I/O read operations against backend flash for all shards of the DB (BigRedis) (bytes/sec). Only returned when BigRedis is enabled. |\n| bigstore_io_reads | float | Rate of key reads from flash (key access/sec). Only returned when BigRedis is enabled. |\n| bigstore_io_write_bytes | float | Throughput of I/O write operations against backend flash for all shards of the DB (BigRedis) (bytes/sec). Only returned when BigRedis is enabled. |\n| bigstore_io_writes | float | Rate of key writes from flash (key access/sec). Only returned when BigRedis is enabled. |\n| bigstore_iops | float | Rate of I/O operations against backend flash for all shards of the DB (BigRedis) (ops/sec). Only returned when BigRedis is enabled. |\n| bigstore_kv_ops | float | Rate of value read/write/del operations against backend flash for all shards of the DB (BigRedis) (key access/sec). Only returned when BigRedis is enabled. |\n| bigstore_objs_flash | float | Key count on flash (BigRedis). Only returned when BigRedis is enabled. |\n| bigstore_objs_ram | float | Key count in RAM (BigRedis). Only returned when BigRedis is enabled. |\n| bigstore_throughput | float | Throughput of I/O operations against backend flash for all shards of the DB (BigRedis) (bytes/sec). Only returned when BigRedis is enabled. |\n| blocked_clients | float | Count the clients waiting on a blocking call |\n| connected_clients | float | Number of client connections to the specific shard |\n| disk_frag_ratio | float | Flash fragmentation ratio (used/required). Only returned when BigRedis is enabled. |\n| evicted_objects | float | Rate of key evictions from DB (evictions/sec) |\n| expired_objects | float | Rate keys expired in DB (expirations/sec) |\n| fork_cpu_system | float | % cores utilization in system mode for the Redis shard fork child process |\n| fork_cpu_user | float | % cores utilization in user mode for the Redis shard fork child process |\n| last_save_time | float | Time of the last RDB save |\n| main_thread_cpu_system | float | % cores utilization in system mode for the Redis shard main thread |\n| main_thread_cpu_user | float | % cores utilization in user mode for the Redis shard main thread |\n| mem_frag_ratio | float | RAM fragmentation ratio (RSS/allocated RAM) |\n| mem_not_counted_for_evict | float | Portion of used_memory (in bytes) not counted for eviction and OOM errors |\n| mem_size_lua | float | Redis Lua scripting heap size (bytes) |\n| no_of_expires | float | Number of volatile keys on the shard |\n| no_of_keys | float | Number of keys in DB |\n| pubsub_channels | float | Count the pub/sub channels with subscribed clients |\n| pubsub_patterns | float | Count the pub/sub patterns with subscribed clients |\n| rdb_changes_since_last_save | float | Count changes since last RDB save |\n| read_hits | float | Rate of read operations accessing an existing key (ops/sec) |\n| read_misses | float | Rate of read operations accessing a nonexistent key (ops/sec) |\n| shard_cpu_system | float | % cores utilization in system mode for the Redis shard process |\n| shard_cpu_user | float | % cores utilization in user mode for the Redis shard process |\n| total_req | float | Rate of operations on DB (ops/sec) |\n| used_memory | float | Memory used by shard (in BigRedis this includes flash) (bytes) |\n| used_memory_peak | float | The largest amount of memory used by this shard (bytes) |\n| used_memory_rss | float | Resident set size of this shard (bytes) |\n| write_hits | float | Rate of write operations accessing an existing key (ops/sec) |\n| write_misses | float | Rate of write operations accessing a nonexistent key (ops/sec) |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/suffix","title":"Suffix object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/suffix/","summary":"An object that represents a DNS suffix","tags":["docs","operate","rs"],"last_updated":"2023-12-14T15:27:49+01:00","page_type":"content","content_hash":"57fe1eaffaad64033a0b3720245c14f717390930406d49076ee20ec9622a0681","sections":[{"id":"content","title":"Content","role":"content","text":"An API object that represents a DNS suffix in the cluster.\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| default | boolean | Suffix is the default suffix for the cluster (read-only) |\n| internal | boolean | Does the suffix point to internal IP addresses (read-only) |\n| mdns | boolean | Support for multicast DNS (read-only) |\n| name | string | Unique suffix name that represents its zone (read-only) |\n| slaves | array of strings | Frontend DNS servers to be updated by this suffix |\n| use_aaaa_ns | boolean | Suffix uses AAAA NS entries (read-only) |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/objects/user","title":"User object","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/objects/user/","summary":"An API object that represents a Redis Software user","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"2115baf2aa3ac3321de7aeca1c97d7010530e1e43ca3e9bc0467783d7adf8c01","sections":[{"id":"content","title":"Content","role":"content","text":"| Name | Type/Value | Description |\n|------|------------|-------------|\n| uid | integer | User's unique ID |\n| account_id | integer | SM account ID |\n| action_uid | string | Action UID. If it exists, progress can be tracked by the <span class=\"break-all\">`GET /actions/{uid}`</span> API request (read-only) |\n| auth_method | **'regular'**<br />'certificate'<br />'entraid'<br />'sso' | User's authentication method |\n| bdbs_email_alerts | complex object | UIDs of databases that user will receive alerts for |\n| <span class=\"break-all\">certificate_subject_line</span> | string | The certificate’s subject line as defined by RFC2253. Used for certificate-based authentication users only. |\n| cluster_email_alerts | boolean | Activate cluster email alerts for a user |\n| email | string | User's email (pattern matching only ASCII characters) |\n| email_alerts | boolean (default: true) | Activate email alerts for a user |\n| last_login | integer | UNIX timestamp of the user's last login time. This denotes the last time an authentication with the user's credentials was successful. (read-only) |\n| name | string | User's name (pattern does not allow non-ASCII and special characters &,\\<,>,\") |\n| password | string | User's password. If `password_hash_method` is set to `1`, the password should be hashed using SHA-256. The format before hashing is <span class=\"break-all\">`username:clustername:password`</span>. | \n| <span class=\"break-all\">password_hash_method</span> | '1' | Used when password is passed pre-hashed to specify the hashing method |\n| <span class=\"break-all\">password_issue_date</span> | string | The date in which the password was set (read-only) |\n| role | 'admin'<br />'cluster_member'<br />'cluster_viewer'<br />'db_member'<br /> **'db_viewer'** <br />'user_manager'<br />'none' | User's [role](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#roles) |\n| role_uids | array of integers | UIDs of user's roles for role-based access control |\n| status | 'active'<br />'locked'<br />'password_expired' | User sign-in status (read-only)<br />**active**: able to sign in<br />**locked**: unable to sign in<br />**password_expired**: unable to sign in because the password expired |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/permissions","title":"Permissions","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/permissions/","summary":"Documents the permissions used with Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-08-27T15:53:06+01:00","page_type":"content","content_hash":"e9039be3e86d366acd6aa8a9ff6b5c6f75053d1a0755f4d3e6c6af2bfb7bb0b3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Some Redis Software [REST API requests](https://redis.io/docs/latest/operate/rs/references/rest-api/requests) may require the user to have specific permissions.\n\nAdministrators can assign a predefined role to a user with the [Cluster Manager UI](https://redis.io/docs/latest/operate/rs/security/access-control/create-users) or a [<nobr>`PUT /v1/users/{uid}`</nobr> API request](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/users#put-user) to grant necessary permissions to them."},{"id":"roles","title":"Roles","role":"content","text":"Each user in the cluster has an assigned cluster management role, which defines the permissions granted to the user.\n\nAvailable management roles include:\n\n- **none**: No REST API permissions.\n- **[db_viewer](#db-viewer-role)**: Can view database info.\n- **[db_member](#db-member-role)**: Can create or modify databases and view their info.\n- **[cluster_viewer](#cluster-viewer-role)**: Can view cluster and database info.\n- **[cluster_member](#cluster-member-role)**: Can modify the cluster and databases and view their info.\n- **[user_manager](#user-manager-role)**: Can modify users and view their info.\n- **[admin](#admin-role)**: Can view and modify all elements of the cluster."},{"id":"permissions-list-for-each-role","title":"Permissions list for each role","role":"security","text":"| Role | Permissions |\n|------|-------------|\n| none | No permissions |\n| <a name=\"admin-role\"></a>admin | [add_cluster_module](#add_cluster_module), [cancel_cluster_action](#cancel_cluster_action), [cancel_node_action](#cancel_node_action), [config_ldap](#config_ldap), [config_ocsp](#config_ocsp), [config_sso](#config_sso), [create_bdb](#create_bdb), [create_crdb](#create_crdb), [create_ldap_mapping](#create_ldap_mapping), [create_new_user](#create_new_user), [create_redis_acl](#create_redis_acl), [create_role](#create_role), [delete_bdb](#delete_bdb), [delete_cluster_module](#delete_cluster_module), [delete_crdb](#delete_crdb), [delete_ldap_mapping](#delete_ldap_mapping), [delete_redis_acl](#delete_redis_acl), [delete_role](#delete_role), [delete_user](#delete_user), [edit_bdb_module](#edit_bdb_module), [failover_shard](#failover_shard), [flush_crdb](#flush_crdb), [install_new_license](#install_new_license), [manage_cluster_modules](#manage_cluster_modules), [migrate_shard](#migrate_shard), [purge_instance](#purge_instance), [reset_bdb_current_backup_status](#reset_bdb_current_backup_status), [reset_bdb_current_export_status](#reset_bdb_current_export_status), [reset_bdb_current_import_status](#reset_bdb_current_import_status), [start_bdb_export](#start_bdb_export), [start_bdb_import](#start_bdb_import), [start_bdb_recovery](#start_bdb_recovery), [start_cluster_action](#start_cluster_action), [start_node_action](#start_node_action), [test_ocsp_status](#test_ocsp_status), [update_bdb](#update_bdb), [update_bdb_alerts](#update_bdb_alerts), [update_bdb_with_action](#update_bdb_with_action), [update_cluster](#update_cluster), [update_crdb](#update_crdb), [upgrade_crdb](#upgrade_crdb), [update_ldap_mapping](#update_ldap_mapping), [update_node](#update_node), [update_proxy](#update_proxy), [update_redis_acl](#update_redis_acl), [update_role](#update_role), [update_user](#update_user), [view_all_bdb_stats](#view_all_bdb_stats), [view_all_bdbs_alerts](#view_all_bdbs_alerts), [view_all_bdbs_info](#view_all_bdbs_info), [view_all_ldap_mappings_info](#view_all_ldap_mappings_info), [view_all_metrics](#view_all_metrics), [view_all_nodes_alerts](#view_all_nodes_alerts), [view_all_nodes_checks](#view_all_nodes_checks), [view_all_nodes_info](#view_all_nodes_info), [view_all_nodes_stats](#view_all_nodes_stats), [view_all_proxies_info](#view_all_proxies_info), [view_all_redis_acls_info](#view_all_redis_acls_info), [view_all_roles_info](#view_all_roles_info), [view_all_shard_stats](#view_all_shard_stats), [view_all_users_info](#view_all_users_info), [view_bdb_alerts](#view_bdb_alerts), [view_bdb_info](#view_bdb_info), [view_bdb_recovery_plan](#view_bdb_recovery_plan), [view_bdb_stats](#view_bdb_stats), [view_cluster_alerts](#view_cluster_alerts), [view_cluster_info](#view_cluster_info), [view_cluster_keys](#view_cluster_keys), [view_cluster_modules](#view_cluster_modules), [view_cluster_stats](#view_cluster_stats), [view_crdb](#view_crdb), [view_crdb_list](#view_crdb_list), [view_crdb_task](#view_crdb_task), [view_crdb_task_list](#view_crdb_task_list), [view_debugging_info](#view_debugging_info), [view_endpoint_stats](#view_endpoint_stats), [view_ldap_config](#view_ldap_config), [view_ldap_mapping_info](#view_ldap_mapping_info), [view_license](#view_license), [view_logged_events](#view_logged_events), [view_node_alerts](#view_node_alerts), [view_node_check](#view_node_check), [view_node_info](#view_node_info), [view_node_stats](#view_node_stats), [view_ocsp_config](#view_ocsp_config), [view_ocsp_status](#view_ocsp_status), [view_proxy_info](#view_proxy_info), [view_redis_acl_info](#view_redis_acl_info), [view_redis_pass](#view_redis_pass), [view_role_info](#view_role_info), [view_shard_stats](#view_shard_stats), [view_sso](#view_sso), [view_status_of_all_node_actions](#view_status_of_all_node_actions), [view_status_of_cluster_action](#view_status_of_cluster_action), [view_status_of_node_action](#view_status_of_node_action), [view_user_info](#view_user_info) |\n| <a name=\"cluster-member-role\"></a>cluster_member | [create_bdb](#create_bdb), [create_crdb](#create_crdb), [delete_bdb](#delete_bdb), [delete_crdb](#delete_crdb), [edit_bdb_module](#edit_bdb_module), [failover_shard](#failover_shard), [flush_crdb](#flush_crdb), [migrate_shard](#migrate_shard), [purge_instance](#purge_instance), [reset_bdb_current_backup_status](#reset_bdb_current_backup_status), [reset_bdb_current_export_status](#reset_bdb_current_export_status), [reset_bdb_current_import_status](#reset_bdb_current_import_status), [start_bdb_export](#start_bdb_export), [start_bdb_import](#start_bdb_import), [start_bdb_recovery](#start_bdb_recovery), [update_bdb](#update_bdb), [update_bdb_alerts](#update_bdb_alerts), [update_bdb_with_action](#update_bdb_with_action), [update_crdb](#update_crdb), [upgrade_crdb](#upgrade_crdb), [view_all_bdb_stats](#view_all_bdb_stats), [view_all_bdbs_alerts](#view_all_bdbs_alerts), [view_all_bdbs_info](#view_all_bdbs_info), [view_all_metrics](#view_all_metrics), [view_all_nodes_alerts](#view_all_nodes_alerts), [view_all_nodes_checks](#view_all_nodes_checks), [view_all_nodes_info](#view_all_nodes_info), [view_all_nodes_stats](#view_all_nodes_stats), [view_all_proxies_info](#view_all_proxies_info), [view_all_redis_acls_info](#view_all_redis_acls_info), [view_all_roles_info](#view_all_roles_info), [view_all_shard_stats](#view_all_shard_stats), [view_bdb_alerts](#view_bdb_alerts), [view_bdb_info](#view_bdb_info), [view_bdb_recovery_plan](#view_bdb_recovery_plan), [view_bdb_stats](#view_bdb_stats), [view_cluster_alerts](#view_cluster_alerts), [view_cluster_info](#view_cluster_info), [view_cluster_keys](#view_cluster_keys), [view_cluster_modules](#view_cluster_modules), [view_cluster_stats](#view_cluster_stats), [view_crdb](#view_crdb), [view_crdb_list](#view_crdb_list), [view_crdb_task](#view_crdb_task), [view_crdb_task_list](#view_crdb_task_list), [view_debugging_info](#view_debugging_info), [view_endpoint_stats](#view_endpoint_stats), [view_license](#view_license), [view_logged_events](#view_logged_events), [view_node_alerts](#view_node_alerts), [view_node_check](#view_node_check), [view_node_info](#view_node_info), [view_node_stats](#view_node_stats), [view_proxy_info](#view_proxy_info), [view_redis_acl_info](#view_redis_acl_info), [view_redis_pass](#view_redis_pass), [view_role_info](#view_role_info), [view_shard_stats](#view_shard_stats), [view_sso](#view_sso), [view_status_of_all_node_actions](#view_status_of_all_node_actions), [view_status_of_cluster_action](#view_status_of_cluster_action), [view_status_of_node_action](#view_status_of_node_action) |\n| <a name=\"cluster-viewer-role\"></a>cluster_viewer | [view_all_bdb_stats](#view_all_bdb_stats), [view_all_bdbs_alerts](#view_all_bdbs_alerts), [view_all_bdbs_info](#view_all_bdbs_info), [view_all_metrics](#view_all_metrics), [view_all_nodes_alerts](#view_all_nodes_alerts), [view_all_nodes_checks](#view_all_nodes_checks), [view_all_nodes_info](#view_all_nodes_info), [view_all_nodes_stats](#view_all_nodes_stats), [view_all_proxies_info](#view_all_proxies_info), [view_all_redis_acls_info](#view_all_redis_acls_info), [view_all_roles_info](#view_all_roles_info), [view_all_shard_stats](#view_all_shard_stats), [view_bdb_alerts](#view_bdb_alerts), [view_bdb_info](#view_bdb_info), [view_bdb_recovery_plan](#view_bdb_recovery_plan), [view_bdb_stats](#view_bdb_stats), [view_cluster_alerts](#view_cluster_alerts), [view_cluster_info](#view_cluster_info), [view_cluster_modules](#view_cluster_modules), [view_cluster_stats](#view_cluster_stats), [view_crdb](#view_crdb), [view_crdb_list](#view_crdb_list), [view_crdb_task](#view_crdb_task), [view_crdb_task_list](#view_crdb_task_list), [view_endpoint_stats](#view_endpoint_stats), [view_license](#view_license), [view_logged_events](#view_logged_events), [view_node_alerts](#view_node_alerts), [view_node_check](#view_node_check), [view_node_info](#view_node_info), [view_node_stats](#view_node_stats), [view_proxy_info](#view_proxy_info), [view_redis_acl_info](#view_redis_acl_info), [view_role_info](#view_role_info), [view_shard_stats](#view_shard_stats), [view_sso](#view_sso), [view_status_of_all_node_actions](#view_status_of_all_node_actions), [view_status_of_cluster_action](#view_status_of_cluster_action), [view_status_of_node_action](#view_status_of_node_action) |\n| <a name=\"db-member-role\"></a>db_member | [create_bdb](#create_bdb), [create_crdb](#create_crdb), [delete_bdb](#delete_bdb), [delete_crdb](#delete_crdb), [edit_bdb_module](#edit_bdb_module), [failover_shard](#failover_shard), [flush_crdb](#flush_crdb), [migrate_shard](#migrate_shard), [purge_instance](#purge_instance), [reset_bdb_current_backup_status](#reset_bdb_current_backup_status), [reset_bdb_current_export_status](#reset_bdb_current_export_status), [reset_bdb_current_import_status](#reset_bdb_current_import_status), [start_bdb_export](#start_bdb_export), [start_bdb_import](#start_bdb_import), [start_bdb_recovery](#start_bdb_recovery), [update_bdb](#update_bdb), [update_bdb_alerts](#update_bdb_alerts), [update_bdb_with_action](#update_bdb_with_action), [update_crdb](#update_crdb), [upgrade_crdb](#upgrade_crdb), [view_all_bdb_stats](#view_all_bdb_stats), [view_all_bdbs_alerts](#view_all_bdbs_alerts), [view_all_bdbs_info](#view_all_bdbs_info), [view_all_nodes_alerts](#view_all_nodes_alerts), [view_all_nodes_checks](#view_all_nodes_checks), [view_all_nodes_info](#view_all_nodes_info), [view_all_nodes_stats](#view_all_nodes_stats), [view_all_proxies_info](#view_all_proxies_info), [view_all_redis_acls_info](#view_all_redis_acls_info), [view_all_roles_info](#view_all_roles_info), [view_all_shard_stats](#view_all_shard_stats), [view_bdb_alerts](#view_bdb_alerts), [view_bdb_info](#view_bdb_info), [view_bdb_recovery_plan](#view_bdb_recovery_plan), [view_bdb_stats](#view_bdb_stats), [view_cluster_alerts](#view_cluster_alerts), [view_cluster_info](#view_cluster_info), [view_cluster_modules](#view_cluster_modules), [view_cluster_stats](#view_cluster_stats), [view_crdb](#view_crdb), [view_crdb_list](#view_crdb_list), [view_crdb_task](#view_crdb_task), [view_crdb_task_list](#view_crdb_task_list), [view_debugging_info](#view_debugging_info), [view_endpoint_stats](#view_endpoint_stats), [view_license](#view_license), [view_logged_events](#view_logged_events), [view_node_alerts](#view_node_alerts), [view_node_check](#view_node_check), [view_node_info](#view_node_info), [view_node_stats](#view_node_stats), [view_proxy_info](#view_proxy_info), [view_redis_acl_info](#view_redis_acl_info), [view_redis_pass](#view_redis_pass), [view_role_info](#view_role_info), [view_shard_stats](#view_shard_stats), [view_sso](#view_sso), [view_status_of_all_node_actions](#view_status_of_all_node_actions), [view_status_of_cluster_action](#view_status_of_cluster_action), [view_status_of_node_action](#view_status_of_node_action) |\n| <a name=\"db-viewer-role\"></a>db_viewer | [view_all_bdb_stats](#view_all_bdb_stats), [view_all_bdbs_alerts](#view_all_bdbs_alerts), [view_all_bdbs_info](#view_all_bdbs_info), [view_all_nodes_alerts](#view_all_nodes_alerts), [view_all_nodes_checks](#view_all_nodes_checks), [view_all_nodes_info](#view_all_nodes_info), [view_all_nodes_stats](#view_all_nodes_stats), [view_all_proxies_info](#view_all_proxies_info), [view_all_redis_acls_info](#view_all_redis_acls_info), [view_all_roles_info](#view_all_roles_info), [view_all_shard_stats](#view_all_shard_stats), [view_bdb_alerts](#view_bdb_alerts), [view_bdb_info](#view_bdb_info), [view_bdb_recovery_plan](#view_bdb_recovery_plan), [view_bdb_stats](#view_bdb_stats), [view_cluster_alerts](#view_cluster_alerts), [view_cluster_info](#view_cluster_info), [view_cluster_modules](#view_cluster_modules), [view_cluster_stats](#view_cluster_stats), [view_crdb](#view_crdb), [view_crdb_list](#view_crdb_list), [view_crdb_task](#view_crdb_task), [view_crdb_task_list](#view_crdb_task_list), [view_endpoint_stats](#view_endpoint_stats), [view_license](#view_license), [view_node_alerts](#view_node_alerts), [view_node_check](#view_node_check), [view_node_info](#view_node_info), [view_node_stats](#view_node_stats), [view_proxy_info](#view_proxy_info), [view_redis_acl_info](#view_redis_acl_info), [view_role_info](#view_role_info), [view_shard_stats](#view_shard_stats), [view_sso](#view_sso), [view_status_of_all_node_actions](#view_status_of_all_node_actions), [view_status_of_cluster_action](#view_status_of_cluster_action), [view_status_of_node_action](#view_status_of_node_action) |\n| <a name=\"user-manager-role\"></a>user_manager | [config_ldap](#config_ldap), [create_ldap_mapping](#create_ldap_mapping), [create_new_user](#create_new_user), [create_role](#create_role), [create_redis_acl](#create_redis_acl), [delete_ldap_mapping](#delete_ldap_mapping), [delete_redis_acl](#delete_redis_acl), [delete_role](#delete_role), [delete_user](#delete_user), [install_new_license](#install_new_license), [update_ldap_mapping](#update_ldap_mapping), [update_proxy](#update_proxy), [update_role](#update_role), [update_redis_acl](#update_redis_acl), [update_user](#update_user), [view_all_bdb_stats](#view_all_bdb_stats), [view_all_bdbs_alerts](#view_all_bdbs_alerts), [view_all_bdbs_info](#view_all_bdbs_info), [view_all_ldap_mappings_info](#view_all_ldap_mappings_info), [view_all_nodes_alerts](#view_all_nodes_alerts), [view_all_nodes_checks](#view_all_nodes_checks), [view_all_nodes_info](#view_all_nodes_info), [view_all_nodes_stats](#view_all_nodes_stats), [view_all_proxies_info](#view_all_proxies_info), [view_all_redis_acls_info](#view_all_redis_acls_info), [view_all_roles_info](#view_all_roles_info), [view_all_shard_stats](#view_all_shard_stats), [view_all_users_info](#view_all_users_info), [view_bdb_alerts](#view_bdb_alerts), [view_bdb_info](#view_bdb_info), [view_bdb_stats](#view_bdb_stats), [view_cluster_alerts](#view_cluster_alerts), [view_cluster_info](#view_cluster_info), [view_cluster_keys](#view_cluster_keys), [view_cluster_modules](#view_cluster_modules), [view_cluster_stats](#view_cluster_stats), [view_crdb](#view_crdb), [view_crdb_list](#view_crdb_list), [view_crdb_task](#view_crdb_task), [view_crdb_task_list](#view_crdb_task_list), [view_endpoint_stats](#view_endpoint_stats), [view_ldap_config](#view_ldap_config), [view_ldap_mapping_info](#view_ldap_mapping_info), [view_license](#view_license), [view_logged_events](#view_logged_events), [view_node_alerts](#view_node_alerts), [view_node_check](#view_node_check), [view_node_info](#view_node_info), [view_node_stats](#view_node_stats), [view_proxy_info](#view_proxy_info), [view_redis_acl_info](#view_redis_acl_info), [view_redis_pass](#view_redis_pass), [view_role_info](#view_role_info), [view_shard_stats](#view_shard_stats), [view_sso](#view_sso), [view_status_of_all_node_actions](#view_status_of_all_node_actions), [view_status_of_cluster_action](#view_status_of_cluster_action), [view_status_of_node_action](#view_status_of_node_action), [view_user_info](#view_user_info)\n |"},{"id":"roles-list-per-permission","title":"Roles list per permission","role":"content","text":"| Permission | Roles |\n|------------|-------|\n| <a name=\"add_cluster_module\"></a>add_cluster_module| admin |\n| <a name=\"cancel_cluster_action\"></a>cancel_cluster_action | admin |\n| <a name=\"cancel_node_action\"></a>cancel_node_action | admin |\n| <a name=\"config_ldap\"></a>config_ldap | admin<br />user_manager |\n| <a name=\"config_ocsp\"></a>config_ocsp | admin |\n| <a name=\"config_sso\"></a>config_sso | admin |\n| <a name=\"create_bdb\"></a>create_bdb | admin<br />cluster_member<br />db_member |\n| <a name=\"create_crdb\"></a>create_crdb | admin<br />cluster_member<br />db_member |\n| <a name=\"create_ldap_mapping\"></a>create_ldap_mapping | admin<br />user_manager |\n| <a name=\"create_new_user\"></a>create_new_user | admin<br />user_manager |\n| <a name=\"create_redis_acl\"></a>create_redis_acl | admin<br />user_manager |\n| <a name=\"create_role\"></a>create_role | admin<br />user_manager |\n| <a name=\"delete_bdb\"></a>delete_bdb | admin<br />cluster_member<br />db_member |\n| <a name=\"delete_cluster_module\"></a>delete_cluster_module | admin |\n| <a name=\"delete_crdb\"></a>delete_crdb | admin<br />cluster_member<br />db_member |\n| <a name=\"delete_ldap_mapping\"></a>delete_ldap_mapping | admin<br />user_manager |\n| <a name=\"delete_redis_acl\"></a>delete_redis_acl | admin<br />user_manager |\n| <a name=\"delete_role\"></a>delete_role | admin<br />user_manager |\n| <a name=\"delete_user\"></a>delete_user | admin<br />user_manager |\n| <a name=\"edit_bdb_module\"></a>edit_bdb_module | admin<br />cluster_member<br />db_member |\n| <a name=\"failover_shard\"></a>failover_shard | admin<br />cluster_member<br />db_member |\n| <a name=\"flush_crdb\"></a>flush_crdb | admin<br />cluster_member<br />db_member |\n| <a name=\"install_new_license\"></a>install_new_license | admin<br />user_manager |\n| <a name=\"manage_cluster_modules\"></a>manage_cluster_modules | admin |\n| <a name=\"migrate_shard\"></a>migrate_shard | admin<br />cluster_member<br />db_member |\n| <a name=\"purge_instance\"></a>purge_instance | admin<br />cluster_member<br />db_member |\n| <a name=\"reset_bdb_current_backup_status\"></a>reset_bdb_current_backup_status | admin<br />cluster_member<br />db_member |\n| <a name=\"reset_bdb_current_export_status\"></a>reset_bdb_current_export_status | admin<br />cluster_member<br />db_member |\n| <a name=\"reset_bdb_current_import_status\"></a>reset_bdb_current_import_status | admin<br />cluster_member<br />db_member |\n| <a name=\"start_bdb_export\"></a>start_bdb_export | admin<br />cluster_member<br />db_member |\n| <a name=\"start_bdb_import\"></a>start_bdb_import | admin<br />cluster_member<br />db_member |\n| <a name=\"start_bdb_recovery\"></a>start_bdb_recovery | admin<br />cluster_member<br />db_member |\n| <a name=\"start_cluster_action\"></a>start_cluster_action | admin |\n| <a name=\"start_node_action\"></a>start_node_action | admin |\n| <a name=\"test_ocsp_status\"></a>test_ocsp_status | admin |\n| <a name=\"update_bdb\"></a>update_bdb | admin<br />cluster_member<br />db_member |\n| <a name=\"update_bdb_alerts\"></a>update_bdb_alerts | admin<br />cluster_member<br />db_member |\n| <a name=\"update_bdb_with_action\"></a>update_bdb_with_action | admin<br />cluster_member<br />db_member |\n| <a name=\"update_cluster\"></a>update_cluster | admin |\n| <a name=\"update_crdb\"></a>update_crdb | admin<br />cluster_member<br />db_member |\n| <a name=\"update_ldap_mapping\"></a>update_ldap_mapping | admin<br />user_manager |\n| <a name=\"update_node\"></a>update_node | admin |\n| <a name=\"update_proxy\"></a>update_proxy | admin<br />user_manager |\n| <a name=\"update_redis_acl\"></a>update_redis_acl | admin<br />user_manager |\n| <a name=\"update_role\"></a>update_role | admin<br />user_manager |\n| <a name=\"update_user\"></a>update_user | admin<br />user_manager |\n| <a name=\"upgrade_crdb\"></a>upgrade_crdb | admin<br />cluster_member<br />db_member |\n| <a name=\"view_all_bdb_stats\"></a>view_all_bdb_stats | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |\n| <a name=\"view_all_bdbs_alerts\"></a>view_all_bdbs_alerts | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |\n| <a name=\"view_all_bdbs_info\"></a>view_all_bdbs_info | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |\n| <a name=\"view_all_ldap_mappings_info\"></a>view_all_ldap_mappings_info | admin<br />user_manager |\n| <a name=\"view_all_metrics\"></a>view_all_metrics | admin<br />cluster_member<br />cluster_viewer |\n| <a name=\"view_all_nodes_alerts\"></a>view_all_nodes_alerts | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |\n| <a name=\"view_all_nodes_checks\"></a>view_all_nodes_checks | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |\n| <a name=\"view_all_nodes_info\"></a>view_all_nodes_info | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |\n| <a name=\"view_all_nodes_stats\"></a>view_all_nodes_stats | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |\n| <a name=\"view_all_proxies_info\"></a>view_all_proxies_info | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |\n| <a name=\"view_all_redis_acls_info\"></a>view_all_redis_acls_info | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |\n| <a name=\"view_all_roles_info\"></a>view_all_roles_info | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |\n| <a name=\"view_all_shard_stats\"></a>view_all_shard_stats | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |\n| <a name=\"view_all_users_info\"></a>view_all_users_info | admin<br />user_manager |\n| <a name=\"view_bdb_alerts\"></a>view_bdb_alerts | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |<a\n| <a name=\"view_bdb_info\"></a>view_bdb_info | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |\n| <a name=\"view_bdb_recovery_plan\"></a>view_bdb_recovery_plan | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |\n| <a name=\"view_bdb_stats\"></a>view_bdb_stats | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |\n| <a name=\"view_cluster_alerts\"></a>view_cluster_alerts | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |\n| <a name=\"view_cluster_info\"></a>view_cluster_info | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |\n| <a name=\"view_cluster_keys\"></a>view_cluster_keys | admin<br />cluster_member<br />user_manager |\n| <a name=\"view_cluster_modules\"></a>view_cluster_modules | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |\n| <a name=\"view_cluster_stats\"></a>view_cluster_stats | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |\n| <a name=\"view_crdb\"></a>view_crdb | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |\n| <a name=\"view_crdb_list\"></a>view_crdb_list | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |\n| <a name=\"view_crdb_task\"></a>view_crdb_task | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |\n| <a name=\"view_crdb_task_list\"></a>view_crdb_task_list | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |\n| <a name=\"view_debugging_info\"></a>view_debugging_info | admin<br />cluster_member<br />db_member<br />user_manager |\n| <a name=\"view_endpoint_stats\"></a>view_endpoint_stats | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |\n| <a name=\"view_ldap_config\"></a>view_ldap_config | admin<br />user_manager |\n| <a name=\"view_ldap_mapping_info\"></a>view_ldap_mapping_info | admin<br />user_manager |\n| <a name=\"view_license\"></a>view_license | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |\n| <a name=\"view_logged_events\"></a>view_logged_events | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />user_manager |\n| <a name=\"view_node_alerts\"></a>view_node_alerts | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |\n| <a name=\"view_node_check\"></a>view_node_check | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |\n| <a name=\"view_node_info\"></a>view_node_info | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |\n| <a name=\"view_node_stats\"></a>view_node_stats | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |\n| <a name=\"view_ocsp_config\"></a>view_ocsp_config | admin |\n| <a name=\"view_ocsp_status\"></a>view_ocsp_status | admin |\n| <a name=\"view_proxy_info\"></a>view_proxy_info | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |\n| <a name=\"view_redis_acl_info\"></a>view_redis_acl_info | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |\n| <a name=\"view_redis_pass\"></a>view_redis_pass | admin<br />cluster_member<br />db_member<br />user_manager |\n| <a name=\"view_role_info\"></a>view_role_info | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |\n| <a name=\"view_shard_stats\"></a>view_shard_stats | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |\n| <a name=\"view_sso\"></a>view_sso | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |\n| <a name=\"view_status_of_all_node_actions\"></a>view_status_of_all_node_actions | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |\n| <a name=\"view_status_of_cluster_action\"></a>view_status_of_cluster_action | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |\n| <a name=\"view_status_of_node_action\"></a>view_status_of_node_action | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |\n| <a name=\"view_user_info\"></a>view_user_info | admin<br />user_manager |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/quick-start","title":"Redis Software REST API quick start","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/quick-start/","summary":"Redis Software REST API quick start","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"92608576e9e17b4b134664844915b57560541e112556c791efd4ca1edd586b76","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software includes a REST API that allows you to automate certain tasks. This article shows you how to send a request to the Redis Software REST API."},{"id":"fundamentals","title":"Fundamentals","role":"content","text":"No matter which method you use to send API requests, there are a few common concepts to remember.\n\n| Type | Description |\n|------|-------------|\n| [Authentication](https://redis.io/docs/latest/operate/rs/references/rest-api#authentication) | Use [Basic Auth](https://en.wikipedia.org/wiki/Basic_access_authentication) with your cluster username (email) and password |\n| [Ports](https://redis.io/docs/latest/operate/rs/references/rest-api#ports) | All calls are made to port 9443 by default |\n| [Versions](https://redis.io/docs/latest/operate/rs/references/rest-api#versions) | Specify the version in the request [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) |\n| [Headers](https://redis.io/docs/latest/operate/rs/references/rest-api#headers) | `Accept` and `Content-Type` should be `application/json` |\n| [Response types and error codes](https://redis.io/docs/latest/operate/rs/references/rest-api#response-types-and-error-codes) | A response of `200 OK` means success; otherwise, the request failed due to an error |  \n\nFor more information, see [Redis Software REST API](https://redis.io/docs/latest/operate/rs/references/rest-api/)."},{"id":"curl-example-requests","title":"cURL example requests","role":"content","text":"[cURL](https://curl.se/) is a command-line tool that allows you to send HTTP requests from a terminal.\n\nYou can use the following options to build a cURL request:\n\n| Option | Description |\n|--------|-------------|\n| -X     | Method (GET, PUT, PATCH, POST, or DELETE) |\n| -H     | Request header, can be specified multiple times |\n| -u     | Username and password information |\n| -d     | JSON data for PUT or POST requests |\n| -F     | Form data for PUT or POST requests, such as for the [`POST /v1/modules`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/modules/#post-module) or [`POST /v2/modules`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/modules/#post-module-v2) endpoint |\n| -k     | Turn off SSL  verification |\n| -i     | Show headers and status code as well as the response body |\n\nSee the [cURL documentation](https://curl.se/docs/) for more information."},{"id":"get-request","title":"GET request","role":"content","text":"Use the following cURL command to get a list of databases with the [GET `/v1/bdbs/`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/#get-all-bdbs) endpoint.\n\n[code example]\n\nIn the response body, the `uid` is the database ID. You can use the database ID to view or update the database using the API.\n\nFor more information about the fields returned by [GET `/v1/bdbs/`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/#get-all-bdbs), see the [`bdbs` object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/)."},{"id":"put-request","title":"PUT request","role":"content","text":"Once you have the database ID, you can use [PUT `/v1/bdbs/`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/#put-bdbs) to update the configuration of the database.\n\nFor example, you can pass the database `uid` 1 as a URL parameter and use the `-d` option to specify the new `name` when you send the request. This changes the database's `name` from `tr01` to `database1`:\n\n[code example]\n\nFor more information about the fields you can update with [PUT `/v1/bdbs/`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/#put-bdbs), see the [`bdbs` object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/)."},{"id":"client-examples","title":"Client examples","role":"content","text":"You can also use client libraries to make API requests in your preferred language.\n\nTo follow these examples, you need:\n\n- A [Redis Software](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/redis-enterprise-software-quickstart) node\n- Python 3 and the [requests](https://pypi.org/project/requests/) Python library\n- [node.js](https://nodejs.dev/) and [node-fetch](https://www.npmjs.com/package/node-fetch)"},{"id":"python","title":"Python","role":"content","text":"[code example]\n\nSee the [Python requests library documentation](https://requests.readthedocs.io/en/latest/) for more information.\n\n#### Output\n\n[code example]"},{"id":"nodejs","title":"node.js","role":"content","text":"[code example]\n\nSee the [node-fetch documentation](https://www.npmjs.com/package/node-fetch) for more info.\n\n#### Output\n\n[code example]"},{"id":"more-info","title":"More info","role":"content","text":"- [Redis Software REST API](https://redis.io/docs/latest/operate/rs/references/rest-api/)\n- [Redis Software REST API requests](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/)"}],"examples":[{"id":"get-request-ex0","language":"sh","code":"$ curl -X GET -H \"accept: application/json\" \\\n              -u \"[username]:[password]\" \\\n              https://[host][:port]/v1/bdbs -k -i\n\nHTTP/1.1 200 OK\nserver: envoy\ndate: Tue, 14 Jun 2022 19:24:30 GMT\ncontent-type: application/json\ncontent-length: 2833\ncluster-state-id: 42\nx-envoy-upstream-service-time: 25\n\n[\n    {\n        ...\n        \"name\": \"tr01\",\n        ...\n        \"uid\": 1,\n        \"version\": \"6.0.16\",\n        \"wait_command\": true\n    }\n]","section_id":"get-request"},{"id":"put-request-ex0","language":"sh","code":"$ curl -X PUT -H \"accept: application/json\" \\\n            -H \"content-type: application/json\" \\\n            -u \"cameron.bates@redis.com:test123\" \\\n            https://[host]:[port]/v1/bdbs/1 \\\n            -d '{ \"name\": \"database1\" }' -k -i\nHTTP/1.1 200 OK\nserver: envoy\ndate: Tue, 14 Jun 2022 20:00:25 GMT\ncontent-type: application/json\ncontent-length: 2933\ncluster-state-id: 43\nx-envoy-upstream-service-time: 159\n\n{\n    ...\n    \"name\" : \"database1\",\n    ...\n    \"uid\" : 1,\n    \"version\" : \"6.0.16\",\n    \"wait_command\" : true\n}","section_id":"put-request"},{"id":"python-ex0","language":"python","code":"import json\nimport requests\n\n# Required connection information - replace with your host, port, username, and password\nhost = \"[host]\"\nport = \"[port]\"\nusername = \"[username]\"\npassword = \"[password]\"\n\n# Get the list of databases using GET /v1/bdbs\nbdbs_uri = \"https://{}:{}/v1/bdbs\".format(host, port)\n\nprint(\"GET {}\".format(bdbs_uri))\nget_resp = requests.get(bdbs_uri,\n        auth = (username, password),\n        headers = { \"accept\" : \"application/json\" },\n        verify = False)\n\nprint(\"{} {}\".format(get_resp.status_code, get_resp.reason))\nfor header in get_resp.headers.keys():\n    print(\"{}: {}\".format(header, get_resp.headers[header]))\n\nprint(\"\\n\" + json.dumps(get_resp.json(), indent=4))\n\n# Rename all databases using PUT /v1/bdbs\nfor bdb in get_resp.json():\n    uid = bdb[\"uid\"] # Get the database ID from the JSON response\n\n    put_uri = \"{}/{}\".format(bdbs_uri, uid)\n    new_name = \"database{}\".format(uid)\n    put_data = { \"name\" : new_name }\n\n    print(\"PUT {} {}\".format(put_uri, json.dumps(put_data)))\n\n    put_resp = requests.put(put_uri,\n        data = json.dumps(put_data),\n        auth = (username, password),\n        headers = { \"content-type\" : \"application/json\" },\n        verify = False)\n\n    print(\"{} {}\".format(put_resp.status_code, put_resp.reason))\n    for header in put_resp.headers.keys():\n        print(\"{}: {}\".format(header, put_resp.headers[header]))\n\n    print(\"\\n\" + json.dumps(put_resp.json(), indent=4))","section_id":"python"},{"id":"python-ex1","language":"sh","code":"$ python rs_api.py\npython rs_api.py\nGET https://[host]:[port]/v1/bdbs\nInsecureRequestWarning: Unverified HTTPS request is being made to host '[host]'.\nAdding certificate verification is strongly advised.\nSee: https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html#ssl-warnings\n  warnings.warn(\n200 OK\nserver: envoy\ndate: Wed, 15 Jun 2022 15:49:43 GMT\ncontent-type: application/json\ncontent-length: 2832\ncluster-state-id: 89\nx-envoy-upstream-service-time: 27\n\n[\n    {\n        ...\n        \"name\": \"tr01\",\n        ...\n        \"uid\": 1,\n        \"version\": \"6.0.16\",\n        \"wait_command\": true\n    }\n]\n\nPUT https://[host]:[port]/v1/bdbs/1 {\"name\": \"database1\"}\nInsecureRequestWarning: Unverified HTTPS request is being made to host '[host]'.\nAdding certificate verification is strongly advised.\nSee: https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html#ssl-warnings\n  warnings.warn(\n200 OK\nserver: envoy\ndate: Wed, 15 Jun 2022 15:49:43 GMT\ncontent-type: application/json\ncontent-length: 2933\ncluster-state-id: 90\nx-envoy-upstream-service-time: 128\n\n{\n    ...\n    \"name\" : \"database1\",\n    ...\n    \"uid\" : 1,\n    \"version\" : \"6.0.16\",\n    \"wait_command\" : true\n}","section_id":"python"},{"id":"nodejs-ex0","language":"js","code":"import fetch, { Headers } from 'node-fetch';\nimport * as https from 'https';\n\nconst HOST = '[host]';\nconst PORT = '[port]';\nconst USERNAME = '[username]';\nconst PASSWORD = '[password]';\n\n// Get the list of databases using GET /v1/bdbs\nconst BDBS_URI = `https://${HOST}:${PORT}/v1/bdbs`;\nconst USER_CREDENTIALS = Buffer.from(`${USERNAME}:${PASSWORD}`).toString('base64');\nconst AUTH_HEADER = `Basic ${USER_CREDENTIALS}`;\n\nconsole.log(`GET ${BDBS_URI}`);\n\nconst HTTPS_AGENT = new https.Agent({\n    rejectUnauthorized: false\n});\n\nconst response = await fetch(BDBS_URI, {\n    method: 'GET',\n    headers: {\n        'Accept': 'application/json',\n        'Authorization': AUTH_HEADER\n    },\n    agent: HTTPS_AGENT\n});\n\nconst responseObject = await response.json();\nconsole.log(`${response.status}: ${response.statusText}`);\nconsole.log(responseObject);\n\n// Rename all databases using PUT /v1/bdbs\nfor (const database of responseObject) {\n    const DATABASE_URI = `${BDBS_URI}/${database.uid}`;\n    const new_name = `database${database.uid}`;\n\n    console.log(`PUT ${DATABASE_URI}`);\n\n    const response = await fetch(DATABASE_URI, {\n        method: 'PUT',\n        headers: {\n            'Authorization': AUTH_HEADER,\n            'Content-Type': 'application/json'\n        },\n        body: JSON.stringify({\n            'name': new_name\n        }),\n        agent: HTTPS_AGENT\n    });\n\n    console.log(`${response.status}: ${response.statusText}`);\n    console.log(await(response.json()));\n}","section_id":"nodejs"},{"id":"nodejs-ex1","language":"sh","code":"$ node rs_api.js\nGET https://[host]:[port]/v1/bdbs\n200: OK\n[\n    {\n        ...\n        \"name\": \"tr01\",\n        ...\n        \"slave_ha\" : false,\n        ...\n        \"uid\": 1,\n        \"version\": \"6.0.16\",\n        \"wait_command\": true\n    }\n]\nPUT https://[host]:[port]/v1/bdbs/1\n200: OK\n{\n    ...\n    \"name\" : \"tr01\",\n    ...\n    \"slave_ha\" : true,\n    ...\n    \"uid\" : 1,\n    \"version\" : \"6.0.16\",\n    \"wait_command\" : true\n}","section_id":"nodejs"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/actions/bdb","title":"Database actions requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/actions/bdb/","summary":"Database actions requests","tags":["docs","operate","rs"],"last_updated":"2026-10-18T15:40:08+03:00","page_type":"content","content_hash":"1e909e31ea93f2b8483925b6e19f6959b41e954292554a9abc01f25af2a7e430","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-db-actions) | `/v1/actions/bdb/{bdb_uid}` | Get the status of a specific database's actions |"},{"id":"get-db-actions","title":"Get database actions","role":"content","text":"[code example]\n\nGet the status of all currently executing or pending state-machine-related actions for a specific database. Completed actions are not returned, so an empty array means no actions are running for the database. This API tracks short-lived API requests that return `action_uid`.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_status_of_cluster_action](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_status_of_cluster_action) |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| bdb_uid | string | Unique database ID |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns an array of JSON objects with attributes from [actions](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/action) and [state machines](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/state-machine). Completed actions and state machines are not included.\n\nEach action contains the following attributes: `name`, `action_uid`, `status`, and `progress`.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error, response provides info about state-machine actions |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | bdb not found |"}],"examples":[{"id":"get-db-actions-ex0","language":"plaintext","code":"GET /v1/actions/bdb/{bdb_uid}","section_id":"get-db-actions"},{"id":"get-request-ex0","language":"plaintext","code":"GET /v1/actions/bdb/1","section_id":"get-request"},{"id":"get-response-ex0","language":"json","code":"[\n    {\n        \"action_uid\": \"8afc7f70-f3ae-4244-a5e9-5133e78b2e97\",\n        \"heartbeat\": 1703067908,\n        \"name\": \"SMUpdateBDB\",\n        \"object_name\": \"bdb:1\",\n        \"pending_ops\": {},\n        \"progress\": 50.0,\n        \"state\": \"proxy_policy\",\n        \"status\": \"active\"\n    }\n]","section_id":"get-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/actions","title":"Actions requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/actions/","summary":"Actions requests","tags":["docs","operate","rs"],"last_updated":"2025-05-19T15:06:21-05:00","children":[{"id":"operate/rs/references/rest-api/requests/actions/bdb","summary":"Database actions requests","title":"Database actions requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/actions/bdb/"}],"page_type":"content","content_hash":"5516d27206b1ac101691748ebc7609c2afd6cf38fd7cffae7154c84b11ccdbc1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-all-actions) | `/v1/actions` | Get all actions |\n| [GET](#get-all-actions-v2) | `/v2/actions` | Get all actions |\n| [GET](#get-action) | `/v1/actions/{uid}` | Get a single action |\n| [GET](#get-action-v2) | `/v2/actions/{uid}` | Get a single action |"},{"id":"get-all-actions","title":"Get all actions v1","role":"content","text":"[code example]\n\nGet the status of all running, pending, or completed actions on all clusters, nodes, and databases. This API tracks long-lived API requests that return either a `task_id` or an `action_uid`.\n\nThis API does not return any information about other actions, such as import, export, and backup. To get info about these actions, use [`GET /v2/actions`](#get-all-actions-v2).\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_status_of_cluster_action](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_status_of_cluster_action) |"},{"id":"get-all-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]"},{"id":"get-all-response","title":"Response","role":"returns","text":"Returns a JSON array of [action objects](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/action), which represent tasks, and an array of [state-machine objects](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/state-machine).\n\n| Field | Type/Value | Description |\n|-------|------------|-------------|\n| action_uid | string | The action's globally unique identifier |\n| name | string | Name of the running or failed state machine |\n| progress | float (range: 0-100) | Percent of completed steps for the action |\n| status | \"pending\"<br />\"active\"<br />\"completed\"<br />\"failed\" | The action's status |\n| node_uid | string | UID of the node where the operation runs (optional) |\n| object_name | string | The object that the action runs on (optional) |\n| state | string | The current state of the state machine (optional)  |\n| pending_ops | JSON object | List of operations that are waiting to run (optional)<br />\"pending_ops\": {<br />  \"3\": {<br />    \"heartbeat\": integer,<br />    \"snapshot\": { ... },<br />    \"last_sample_time\": integer,<br />    \"op_name\": string,<br />    \"status_code\": string,<br />    \"status_description\": string,<br />    \"progress\": float<br />  }<br />}<br />`pending_ops` is a map where the key is the `shard_id`, and the value is a map that can include the following optional fields:<br />**heartbeat**: The time, in seconds since the Unix epoch, since the last change in the progress of the operation.<br />**snapshot**: A map of properties stored by the operation that are needed to run.<br />**last_sample_time**: The time, in seconds since the Unix epoch, when the last snapshot of the operation was taken.<br />**op_name**: The name of the operation from the state machine that is running.<br />**status_code**: The code for the operation's current status.<br />**status_description**: The operation's current status.<br />**progress**: The operation's progress in percentage (1 to 100). |\n\nRegardless of an action’s source, each action in the response contains the following attributes: `name`, `action_uid`, `status`, and `progress`.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-all-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | No error, response provides info about an ongoing action |\n| [404 Not Found](https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found) | Action does not exist (for example, not currently running and no available status of last run).|"},{"id":"get-all-actions-v2","title":"Get all actions v2","role":"content","text":"[code example]\n\nGet the status of all currently running, pending, or completed actions from tasks, state-machines, and other actions, including import, export, and backup. This API tracks long-lived API requests that return either a `task_id` or an `action_uid`.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_status_of_cluster_action](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_status_of_cluster_action) |"},{"id":"get-all-request-v2","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]"},{"id":"get-all-response-v2","title":"Response","role":"returns","text":"Returns a JSON array of v2 [action objects](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/action).\n\nThe v2 action object includes the following fields:\n\n| Field | Type/Value | Description |\n|-------|------------|-------------|\n| action_uid | string | The action's globally unique identifier |\n| action_type | \"task\"<br />\"state-machine\"<br />\"other\" | The action's type |\n| creation_time | integer | The action's creation time. Unix timestamp in seconds. |\n| name | string | Name of the running or failed state machine |\n| progress | float (range: 0-100) | Percent of completed steps for the action |\n| status | \"queued\"<br />\"running\"<br />\"completed\"<br />\"failed\" | The action's status |\n| additional_info | JSON object | A dictionary that can include additional information about the action; only included in the response if it contains at least one key-value pair |\n\nThe `additional_info` object can contain any of the following fields:\n\n| Field | Type/Value | Description |\n|-------|------------|-------------|\n| description | string | Short description of the action |\n| error | string | A message that describes what error occurred if the action failed |\n| object_type | string | The type of object that was processed in the action, such as BDB or node |\n| object_uid | string | The unique ID of the object processed in the action |\n| pending_ops | JSON object | List of operations that are waiting to run (optional)<br />\"pending_ops\": {<br />  \"3\": {<br />    \"heartbeat\": integer,<br />    \"snapshot\": { ... },<br />    \"last_sample_time\": integer,<br />    \"op_name\": string,<br />    \"status_code\": string,<br />    \"status_description\": string,<br />    \"progress\": float<br />  }<br />}<br />`pending_ops` is a map where the key is the `shard_id`, and the value is a map that can include the following optional fields:<br />**heartbeat**: The time, in seconds since the Unix epoch, since the last change in the progress of the operation.<br />**snapshot**: A map of properties stored by the operation that are needed to run.<br />**last_sample_time**: **last_sample_time**: The time, in seconds since the Unix epoch, when the last snapshot of the operation was taken.<br />**op_name**: The name of the operation from the state machine that is running.<br />**status_code**: The code for the operation's current status.<br />**status_description**: The operation's current status.<br />**progress**: The operation's progress in percentage (1 to 100). |\n\nRegardless of an action’s source, each action in the response contains the following attributes: `name`, `action_uid`, `creation_time`, `status`, and `progress`.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-all-status-codes-v2","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | No error, response provides info about an ongoing action |"},{"id":"get-action","title":"Get a specific action v1","role":"content","text":"[code example]\n\nGet the status of a specific action.\n\nThis API does not return any information about other actions, such as import, export, and backup. To get info about these actions, use [`GET /v2/actions/<uid>`](#get-action-v2).\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_status_of_cluster_action](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_status_of_cluster_action) |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | string | The action_uid to check |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns an [action object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/action).\n\n| Field | Type/Value | Description |\n|-------|------------|-------------|\n| action_uid | string | The action's globally unique identifier |\n| name | string | Name of the running or failed state machine |\n| progress | float (range: 0-100) | Percent of completed steps for the action |\n| status | \"pending\"<br />\"active\"<br />\"completed\"<br />\"failed\" | The action's status |\n| node_uid | string | UID of the node where the operation runs (optional) |\n| object_name | string | The object that the action runs on (optional) |\n| state | string | The current state of the state machine (optional)  |\n| pending_ops | JSON object | List of operations that are waiting to run (optional)<br />\"pending_ops\": {<br />  \"3\": {<br />    \"heartbeat\": integer,<br />    \"snapshot\": { ... },<br />    \"last_sample_time\": integer,<br />    \"op_name\": string,<br />    \"status_code\": string,<br />    \"status_description\": string,<br />    \"progress\": float<br />  }<br />}<br />`pending_ops` is a map where the key is the `shard_id`, and the value is a map that can include the following optional fields:<br />**heartbeat**: The time, in seconds since the Unix epoch, since the last change in the progress of the operation.<br />**snapshot**: A map of properties stored by the operation that are needed to run.<br />**last_sample_time**: The time, in seconds since the Unix epoch, when the last snapshot of the operation was taken.<br />**op_name**: The name of the operation from the state machine that is running.<br />**status_code**: The code for the operation's current status.<br />**status_description**: The operation's current status.<br />**progress**: The operation's progress in percentage (1 to 100). |\n\nRegardless of an action’s source, each action contains the following attributes: `name`, `action_uid`, `status`, and `progress`.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | No error, response provides info about an ongoing action |\n| [404 Not Found](https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found) | Action does not exist (that is, not currently running and no available status of last run) |"},{"id":"get-action-v2","title":"Get a specific action v2","role":"content","text":"[code example]\n\nGet the status of a specific action. This API can also return information about actions like import, export, and backup.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_status_of_cluster_action](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_status_of_cluster_action) |"},{"id":"get-request-v2","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | string | The action_uid to check |"},{"id":"get-response-v2","title":"Response","role":"returns","text":"Returns a v2 [action object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/action).\n\nThe v2 action object includes the following fields:\n\n| Field | Type/Value | Description |\n|-------|------------|-------------|\n| action_uid | string | The action's globally unique identifier |\n| action_type | \"task\"<br />\"state-machine\"<br />\"other\" | The action's type |\n| creation_time | integer | The action's creation time. Unix timestamp in seconds. |\n| name | string | Name of the running or failed state machine |\n| progress | float (range: 0-100) | Percent of completed steps for the action |\n| status | \"queued\"<br />\"running\"<br />\"completed\"<br />\"failed\" | The action's status |\n| additional_info | JSON object | A dictionary that can include additional information about the action; only included in the response if it contains at least one key-value pair |\n\nThe `additional_info` object can contain any of the following fields:\n\n| Field | Type/Value | Description |\n|-------|------------|-------------|\n| description | string | Short description of the action |\n| error | string | A message that describes what error occurred if the action failed |\n| object_type | string | The type of object that was processed in the action, such as BDB or node |\n| object_uid | string | The unique ID of the object processed in the action |\n| pending_ops | JSON object | List of operations that are waiting to run (optional)<br />\"pending_ops\": {<br />  \"3\": {<br />    \"heartbeat\": integer,<br />    \"snapshot\": { ... },<br />    \"last_sample_time\": integer,<br />    \"op_name\": string,<br />    \"status_code\": string,<br />    \"status_description\": string,<br />    \"progress\": float<br />  }<br />}<br />`pending_ops` is a map where the key is the `shard_id`, and the value is a map that can include the following optional fields:<br />**heartbeat**: The time, in seconds since the Unix epoch, since the last change in the progress of the operation.<br />**snapshot**: A map of properties stored by the operation that are needed to run.<br />**last_sample_time**: The time, in seconds since the Unix epoch, when the last snapshot of the operation was taken.<br />**op_name**: The name of the operation from the state machine that is running.<br />**status_code**: The code for the operation's current status.<br />**status_description**: The operation's current status.<br />**progress**: The operation's progress in percentage (1 to 100). |\n\nRegardless of an action’s source, each action contains the following attributes: `name`, `action_uid`, `creation_time`, `status`, and `progress`.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes-v2","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | No error, response provides info about an ongoing action |\n| [404 Not Found](https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found) | Action does not exist (that is, not currently running and no available status of last run) |"}],"examples":[{"id":"get-all-actions-ex0","language":"plaintext","code":"GET /v1/actions","section_id":"get-all-actions"},{"id":"get-all-request-ex0","language":"plaintext","code":"GET /v1/actions","section_id":"get-all-request"},{"id":"get-all-response-ex0","language":"json","code":"{\n  \"actions\": [\n    {\n      \"action_uid\": \"159ca2f8-7bf3-4cda-97e8-4eb560665c28\",\n      \"name\": \"retry_bdb\",\n      \"node_uid\": \"2\",\n      \"progress\": \"100\",\n      \"status\": \"completed\",\n      \"task_id\": \"159ca2f8-7bf3-4cda-97e8-4eb560665c28\"\n    },\n    {\n      \"action_uid\": \"661697c5-c747-41bd-ab81-ffc8fd13c494\",\n      \"name\": \"retry_bdb\",\n      \"node_uid\": \"1\",\n      \"progress\": \"100\",\n      \"status\": \"completed\",\n      \"task_id\": \"661697c5-c747-41bd-ab81-ffc8fd13c494\"\n    }\n  ],\n  \"state-machines\": [\n    {\n      \"action_uid\": \"a10586b1-60bc-428e-9bc6-392eb5f0d8ae\",\n      \"heartbeat\": 1650378874,\n      \"name\": \"SMCreateBDB\",\n      \"object_name\": \"bdb:1\",\n      \"progress\": 100,\n      \"status\": \"completed\"\n    }\n  ]\n}","section_id":"get-all-response"},{"id":"get-all-actions-v2-ex0","language":"plaintext","code":"GET /v2/actions","section_id":"get-all-actions-v2"},{"id":"get-all-request-v2-ex0","language":"plaintext","code":"GET /v2/actions","section_id":"get-all-request-v2"},{"id":"get-all-response-v2-ex0","language":"json","code":"[\n    {\n        \"action_type\": \"task\",\n        \"action_uid\": \"6155403f-c26f-40ab-8afc-23ed663973f6\",\n        \"additional_info\": {\n            \"object_type\": \"node\",\n            \"object_uid\": \"1\"\n        },\n        \"creation_time\": 1742595918,\n        \"name\": \"retry_bdb\",\n        \"progress\": 100.0,\n        \"status\": \"completed\"\n    },\n    // Additional task objects\n]","section_id":"get-all-response-v2"},{"id":"get-action-ex0","language":"plaintext","code":"GET /v1/actions/{uid}","section_id":"get-action"},{"id":"get-request-ex0","language":"plaintext","code":"GET /v1/actions/{uid}","section_id":"get-request"},{"id":"get-response-ex0","language":"json","code":"{\n  \"action_uid\": \"159ca2f8-7bf3-4cda-97e8-4eb560665c28\",\n  \"name\": \"retry_bdb\",\n  \"node_uid\": \"2\",\n  \"progress\": \"100\",\n  \"status\": \"completed\",\n  \"task_id\": \"159ca2f8-7bf3-4cda-97e8-4eb560665c28\"\n}","section_id":"get-response"},{"id":"get-action-v2-ex0","language":"plaintext","code":"GET /v2/actions/{uid}","section_id":"get-action-v2"},{"id":"get-request-v2-ex0","language":"plaintext","code":"GET /v2/actions/{uid}","section_id":"get-request-v2"},{"id":"get-response-v2-ex0","language":"json","code":"{\n    \"action_type\": \"task\",\n    \"action_uid\": \"6155403f-c26f-40ab-8afc-23ed663973f6\",\n    \"additional_info\": {\n        \"object_type\": \"node\",\n        \"object_uid\": \"1\"\n    },\n    \"creation_time\": 1742595918,\n    \"name\": \"retry_bdb\",\n    \"progress\": 100.0,\n    \"status\": \"completed\"\n}","section_id":"get-response-v2"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/bdbs/actions/backup_reset_status","title":"Backup reset status database action requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/backup_reset_status/","summary":"Reset database backup status requests","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","page_type":"content","content_hash":"6cfa7c3d452a732adff9f372f1aa73252d76cd33dd48d73e4372bff7f8962c53","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [PUT](#put-bdbs-actions-backup-reset-status) | `/v1/bdbs/{uid}/actions/backup_reset_status` | Reset database backup status |"},{"id":"put-bdbs-actions-backup-reset-status","title":"Reset database backup status","role":"content","text":"[code example]\n\nResets the database's `backup_status` to idle if a backup is not in progress and clears the value of the `backup_failure_reason` field."},{"id":"permissions","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [reset_bdb_current_backup_status](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#reset_bdb_current_backup_status) | admin<br />cluster_member<br />db_member |"},{"id":"put-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the database |"},{"id":"put-response","title":"Response","role":"returns","text":"Returns a status code.\n\n#### Status codes {#put-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | The request is accepted and is being processed. |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Attempting to perform an action on a nonexistent database. |\n| [406&nbsp;Not&nbsp;Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | Not all the modules loaded to the database support 'backup_restore' capability |\n| [409 Conflict](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.10) | Database is currently busy with another action. In this context, this is a temporary condition and the request should be reattempted later. |"}],"examples":[{"id":"put-bdbs-actions-backup-reset-status-ex0","language":"sh","code":"PUT /v1/bdbs/{int: uid}/actions/backup_reset_status","section_id":"put-bdbs-actions-backup-reset-status"},{"id":"put-request-ex0","language":"sh","code":"PUT /v1/bdbs/1/actions/backup_reset_status","section_id":"put-request"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/bdbs/actions/export","title":"Export database action requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/export/","summary":"Export database requests","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","page_type":"content","content_hash":"472ac7d8abd97b9698677a76a6d57a6c189c5896eb35c52ea852e7b4d1b0c142","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [POST](#post-bdbs-actions-export) | `/v1/bdbs/{uid}/actions/export` | Initiate database export |"},{"id":"post-bdbs-actions-export","title":"Initiate database export","role":"content","text":"[code example]\n\nInitiate a database export."},{"id":"permissions","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [start_bdb_export](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#start_bdb_export) | admin<br />cluster_member<br />db_member |"},{"id":"post-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the database |\n\n\n#### Body\n\nThe request body should contain a JSON object with the following export parameters:\n\n| Field | Type | Description |\n|-------|------|-------------|\n| export_location | [backup_location/export_location](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/backup_location) object | Details for the export destination. Call [`GET /v1/jsonschema`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/jsonschema#get-jsonschema) on the bdb object and review the `backup_location` field to retrieve the object's structure.  |\n| email_notification | boolean | Enable/disable an email notification on export failure/ completion. (optional) |\n\n##### Example JSON body\n\n[code example]\n\nThe above request initiates an export operation to the specified location."},{"id":"post-response","title":"Response","role":"returns","text":"Returns a status code.\n\n#### Status codes {#post-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | The request is accepted and is being processed. In order to monitor progress, the BDB's `export_status`, `export_progress`, and `export_failure_reason` attributes can be consulted. |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Attempting to perform an action on a nonexistent database. |\n| [406&nbsp;Not&nbsp;Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | Not all the modules loaded to the database support 'backup_restore' capability |\n| [409 Conflict](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.10) | Database is currently busy with another action. In this context, this is a temporary condition and the request should be reattempted later. |"}],"examples":[{"id":"post-bdbs-actions-export-ex0","language":"sh","code":"POST /v1/bdbs/{int: uid}/actions/export","section_id":"post-bdbs-actions-export"},{"id":"post-request-ex0","language":"sh","code":"POST /v1/bdbs/1/actions/export","section_id":"post-request"},{"id":"post-request-ex1","language":"json","code":"{\n    \"export_location\": {\n        \"type\": \"url\",\n        \"url\": \"ftp://...\"\n    },\n    \"email_notification\": true\n}","section_id":"post-request"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/bdbs/actions/export_reset_status","title":"Export resets status database action requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/export_reset_status/","summary":"Reset database export status requests","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","page_type":"content","content_hash":"c99edafb234bf31b3548beb62181082e64834ac420ec7252b8413b05b275c24a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [PUT](#put-bdbs-actions-export-reset-status) | `/v1/bdbs/{uid}/actions/export_reset_status` | Reset database export status |"},{"id":"put-bdbs-actions-export-reset-status","title":"Reset database export status","role":"content","text":"PUT /v1/bdbs/{int: uid}/actions/export_reset_status\n\nResets the database's `export_status` to idle if an export is not in progress and clears the value of the `export_failure_reason` field."},{"id":"permissions","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [reset_bdb_current_export_status](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#reset_bdb_current_export_status) | admin<br />cluster_member<br />db_member |"},{"id":"put-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the database |"},{"id":"put-response","title":"Response","role":"returns","text":"Returns a status code.\n\n#### Status codes {#put-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | The request is accepted and is being processed. |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Attempting to perform an action on a nonexistent database. |\n| [406&nbsp;Not&nbsp;Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | Not all the modules loaded to the database support 'backup_restore' capability |\n| [409 Conflict](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.10) | Database is currently busy with another action. In this context, this is a temporary condition and the request should be reattempted later. |"}],"examples":[{"id":"put-request-ex0","language":"sh","code":"PUT /v1/bdbs/1/actions/export_reset_status","section_id":"put-request"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/bdbs/actions/import","title":"Import database action requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/import/","summary":"Import database requests","tags":["docs","operate","rs"],"last_updated":"2026-10-05T16:11:44+01:00","page_type":"content","content_hash":"15ac69b3f700a7fe26faf8e9ac3e122694cbdc23d20b04445f48e6c82f6f7821","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [POST](#post-bdbs-actions-import) | `/v1/bdbs/{uid}/actions/import` | Initiate manual dataset import |"},{"id":"post-bdbs-actions-import","title":"Initiate manual dataset import","role":"content","text":"[code example]\n\nInitiate a manual import process."},{"id":"permissions","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [start_bdb_import](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#start_bdb_import) | admin<br />cluster_member<br />db_member |"},{"id":"post-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n| Content-Length | 0 | Length of the request body in octets |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the database |\n\n#### Body\n\nThe request _may_ contain a subset of the [BDB JSON object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb), which includes the following import-related attributes:\n\n| Field | Type | Description |\n|-------|------|-------------|\n| dataset_import_sources | array of [dataset_import_sources](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb/dataset_import_sources) objects | Details for the import sources. Call [`GET /v1/jsonschema`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/jsonschema#get-jsonschema) on the bdb object and review the `dataset_import_sources` field to retrieve the object's structure.  |\n| email_notification | boolean | Enable/disable an email notification on import failure/ completion. (optional) |\n\n> [!NOTE]\n> Other attributes are not allowed and will cause the request to fail.\n\n##### Example JSON body\n\nGeneral example:\n\n[code example]\n\nThis request initiates an import process using `dataset_import_sources` values that were previously configured for the database.\n\nFTP example:\n\n[code example]\n\nSFTP example:\n\n[code example]\n\nAWS S3 example:\n\n[code example]\n\nGoogle Cloud Storage example:\n\n[code example]\n\nAzure Blob Storage example:\n\n[code example]"},{"id":"post-response","title":"Response","role":"returns","text":"Returns a status code."},{"id":"post-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | The request is accepted and is being processed. In order to monitor progress, the `import_status`, `import_progress`, and `import_failure_reason` attributes can be consulted. |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Attempting to perform an action on a nonexistent database. |\n| [406&nbsp;Not&nbsp;Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | Not all the modules loaded to the database support 'backup_restore' capability. |\n| [409 Conflict](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.10) | Database is currently busy with another action. In this context, this is a temporary condition and the request should be reattempted later. |"}],"examples":[{"id":"post-bdbs-actions-import-ex0","language":"sh","code":"POST /v1/bdbs/{int: uid}/actions/import","section_id":"post-bdbs-actions-import"},{"id":"post-request-ex0","language":"sh","code":"POST /v1/bdbs/1/actions/import","section_id":"post-request"},{"id":"post-request-ex1","language":"json","code":"{\n    \"dataset_import_sources\": [\n        {\n            \"type\": \"url\",\n            \"url\": \"http://...\"\n        },\n        {\n            \"type\": \"url\",\n            \"url\": \"redis://...\"\n        }\n    ],\n    \"email_notification\": true\n}","section_id":"post-request"},{"id":"post-request-ex2","language":"json","code":"{\n  \"dataset_import_sources\": [\n    {\n      \"type\": \"url\",\n      \"url\": \"ftp://<ftp_user>:<ftp_password>@example.com/<path>/<filename>.rdb.gz\"\n    }\n  ]\n}","section_id":"post-request"},{"id":"post-request-ex3","language":"json","code":"{\n  \"dataset_import_sources\": [\n    {\n      \"type\": \"sftp\",\n      \"sftp_url\": \"sftp://<sftp_user>@example.com/<path>/<filename>.rdb\"\n    }\n  ]\n}","section_id":"post-request"},{"id":"post-request-ex4","language":"json","code":"{\n  \"dataset_import_sources\": [\n    {\n      \"type\": \"s3\",\n      \"bucket_name\": \"backups\",\n      \"subdir\": \"test-db\",\n      \"filename\": \"<filename>.rdb\",\n      \"access_key_id\": \"XXXXXXXXXXXXX\",\n      \"secret_access_key\": \"XXXXXXXXXXXXXXXX\"\n    }\n  ]\n}","section_id":"post-request"},{"id":"post-request-ex5","language":"json","code":"{\n  \"dataset_import_sources\": [\n    {\n      \"type\": \"gs\",\n      \"bucket_name\": \"backups\",\n      \"client_id\": \"XXXXXXXX\",\n      \"client_email\": \"cloud-storage-client@my-project-id.iam.gserviceaccount.com\",\n      \"subdir\": \"test-db\",\n      \"filename\": \"<filename>.rdb\",\n      \"private_key_id\": \"XXXXXXXXXXXXX\",\n      \"private_key\": \"XXXXXXXXXXXXXXXX\"\n    }\n  ]\n}","section_id":"post-request"},{"id":"post-request-ex6","language":"json","code":"{\n  \"dataset_import_sources\": [\n    {\n      \"type\": \"abs\",\n      \"container\": \"backups\",\n      \"subdir\": \"test-db\",\n      \"filename\": \"<filename>.rdb\",\n      \"account_name\": \"name\",\n      \"account_key\": \"XXXXXXXXXXXXXXXX\" // Or you can use \"sas_token\": \"XXXXXXXXXXXXXXXXXX\" instead\n    }\n  ]\n}","section_id":"post-request"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/bdbs/actions/import_reset_status","title":"Import reset status database action requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/import_reset_status/","summary":"Reset database import status requests","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","page_type":"content","content_hash":"dfbcdb8bf3fa47540bef098107a3d3e732a28ba7d6915cb55d1974b191dc702b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [PUT](#put-bdbs-actions-import-reset-status) | `/v1/bdbs/{uid}/actions/import_reset_status` | Reset database import status |"},{"id":"put-bdbs-actions-import-reset-status","title":"Reset database import status","role":"content","text":"PUT /v1/bdbs/{int: uid}/actions/import_reset_status\n\nReset the database’s `import_status` to idle if a backup is not in progress and clears the value of the `import_failure_reason` field."},{"id":"permissions","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [reset_bdb_current_import_status](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#reset_bdb_current_import_status) | admin<br />cluster_member<br />db_member |"},{"id":"put-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the database |"},{"id":"put-response","title":"Response","role":"returns","text":"Returns a status code."},{"id":"put-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | The request is accepted and is being processed. |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Attempting to perform an action on a nonexistent database. |\n| [406 Not Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | Not all the modules loaded to the database support 'backup_restore' capability |\n| [409 Conflict](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.10) | Database is currently busy with another action. In this context, this is a temporary condition and the request should be reattempted later. |"}],"examples":[{"id":"put-request-ex0","language":"sh","code":"PUT /v1/bdbs/1/actions/import_reset_status","section_id":"put-request"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/bdbs/actions","title":"Database actions requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/","summary":"Database action requests","tags":["docs","operate","rs"],"last_updated":"2024-11-12T09:34:06-06:00","children":[{"id":"operate/rs/references/rest-api/requests/bdbs/actions/backup_reset_status","summary":"Reset database backup status requests","title":"Backup reset status database action requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/backup_reset_status/"},{"id":"operate/rs/references/rest-api/requests/bdbs/actions/export","summary":"Export database requests","title":"Export database action requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/export/"},{"id":"operate/rs/references/rest-api/requests/bdbs/actions/export_reset_status","summary":"Reset database export status requests","title":"Export resets status database action requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/export_reset_status/"},{"id":"operate/rs/references/rest-api/requests/bdbs/actions/import","summary":"Import database requests","title":"Import database action requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/import/"},{"id":"operate/rs/references/rest-api/requests/bdbs/actions/import_reset_status","summary":"Reset database import status requests","title":"Import reset status database action requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/import_reset_status/"},{"id":"operate/rs/references/rest-api/requests/bdbs/actions/optimize_shards_placement","summary":"Optimize shard placement requests","title":"Optimize shards placement database action requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/optimize_shards_placement/"},{"id":"operate/rs/references/rest-api/requests/bdbs/actions/rebalance","summary":"REST API requests to rebalance database shards","title":"Rebalance database requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/rebalance/"},{"id":"operate/rs/references/rest-api/requests/bdbs/actions/recover","summary":"REST API requests for database recovery","title":"Recover database requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/recover/"},{"id":"operate/rs/references/rest-api/requests/bdbs/actions/resume_traffic","summary":"REST API requests to resume traffic for a database","title":"Resume database traffic requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/resume_traffic/"},{"id":"operate/rs/references/rest-api/requests/bdbs/actions/revamp","summary":"REST API requests to update database configuration and optimize shard placement","title":"Revamp database requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/revamp/"},{"id":"operate/rs/references/rest-api/requests/bdbs/actions/stop_traffic","summary":"REST API requests to stop traffic for a database","title":"Stop database traffic requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/stop_traffic/"}],"page_type":"content","content_hash":"f9cc002f5454b84b94074899f504cccc9339e66c7e021b8947e8280df0969d1e","sections":[{"id":"backup","title":"Backup","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [PUT](https://redis.io/docs/latest/./backup_reset_status#put-bdbs-actions-backup-reset-status) | `/v1/bdbs/{uid}/actions/backup_reset_status` | Reset database backup status |"},{"id":"export","title":"Export","role":"content","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [PUT](https://redis.io/docs/latest/./export_reset_status#put-bdbs-actions-export-reset-status) | `/v1/bdbs/{uid}/actions/export_reset_status` | Reset database export status |\n| [POST](https://redis.io/docs/latest/./export#post-bdbs-actions-export) | `/v1/bdbs/{uid}/actions/export` | Initiate database export |"},{"id":"import","title":"Import","role":"content","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [PUT](https://redis.io/docs/latest/./import_reset_status#put-bdbs-actions-import-reset-status) | `/v1/bdbs/{uid}/actions/import_reset_status` | Reset database import status |\n| [POST](https://redis.io/docs/latest/./import#post-bdbs-actions-import) | `/v1/bdbs/{uid}/actions/import` | Initiate manual dataset import |"},{"id":"optimize-shards-placement","title":"Optimize shards placement","role":"content","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](https://redis.io/docs/latest/./optimize_shards_placement#get-bdbs-actions-optimize-shards-placement) | `/v1/bdbs/{uid}/actions/optimize_shards_placement` | Get optimized shards placement for a database  |"},{"id":"rebalance","title":"Rebalance","role":"content","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [PUT](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/rebalance#put-bdbs-actions-rebalance) | `/v1/bdbs/{uid}/actions/rebalance` | Rebalance database shards |"},{"id":"recover","title":"Recover","role":"content","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/recover#get-bdbs-actions-recover) | `/v1/bdbs/{uid}/actions/recover` | Get database recovery plan  |\n| [POST](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/recover#post-bdbs-actions-recover) | `/v1/bdbs/{uid}/actions/recover` | Recover database  |"},{"id":"resume-traffic","title":"Resume traffic","role":"content","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [POST](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/resume_traffic#post-bdbs-actions-resume-traffic) | `/v1/bdbs/{uid}/actions/resume_traffic` | Resume database traffic |"},{"id":"stop-traffic","title":"Stop traffic","role":"content","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [POST](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/stop_traffic#post-bdbs-actions-stop-traffic) | `/v1/bdbs/{uid}/actions/stop_traffic` | Stop database traffic |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/bdbs/actions/optimize_shards_placement","title":"Optimize shards placement database action requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/optimize_shards_placement/","summary":"Optimize shard placement requests","tags":["docs","operate","rs"],"last_updated":"2026-10-05T16:11:44+01:00","page_type":"content","content_hash":"767218513e68a9e2233d5fbf0a53aafd245fb05c2e22d5352f96f3735d2ff075","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This REST API path is deprecated as of Redis Software version 7.22. Use [`PUT /v1/bdbs/<uid>/actions/revamp?dry_run=true`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/revamp/#put-bdbs-actions-revamp) instead.\n\n\n| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-bdbs-actions-optimize-shards-placement) | `/v1/bdbs/{uid}/actions/optimize_shards_placement` | Get optimized shards placement for a database  |"},{"id":"get-bdbs-actions-optimize-shards-placement","title":"Get optimized shards placement","role":"content","text":"[code example]\n\nGet optimized shards placement for the given database.\n\n#### Required permissions\n\n| Permission name | Roles |\n|-----------------|-------|\n| [view_bdb_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_bdb_info) | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Query parameters\n\nInclude query parameters in a `GET` request to generate an optimized shard placement blueprint for a database, using settings that are different from the database's current configuration.\n\n| Field | Type | Description |\n|-------|------|-------------|\n| avoid_nodes | list of integers | Comma-separated list of cluster node IDs to avoid when placing the database’s shards and binding its endpoints (for example, `avoid_nodes=1,2`) |\n| memory_size | integer (default:&nbsp;0) | Database memory limit (0 is unlimited), expressed in bytes |\n| shards_count | integer, <nobr>(range: 1-512)</nobr> (default:&nbsp;1) | Number of database server-side shards |\n| shards_placement | `dense` <br />`sparse` | Control the density of shards <br /> `dense`: Shards reside on as few nodes as possible <br /> `sparse`: Shards reside on as many nodes as possible |\n| bigstore_ram_size | integer (default:&nbsp;0) | Memory size of bigstore RAM part, expressed in bytes |\n| replication | `enabled`<br />`disabled` | In-memory database replication mode |\n\nThe following example request includes `shards_count` and `memory_size` as query parameters:\n\n[code example]"},{"id":"get-response","title":"Response","role":"returns","text":"To rearrange the database shards, you can submit the blueprint returned in this response body as the `shards_blueprint` field in the [`PUT`&nbsp;`/v1/bdbs/{uid}`](#put-bdbs-rearrange-shards) request.\n\n#### Example JSON body\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Content-Length | 352 | Length of the request body in octets |\n| cluster-state-id | 30 | Cluster state ID |\n\n#### Status codes {#get-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Database UID does not exist |\n| [406 Not Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | Not enough resources in the cluster to host the database |"},{"id":"put-bdbs-rearrange-shards","title":"Rearrange database shards","role":"content","text":"Use the blueprint returned by the [`GET`&nbsp;`/v1/bdbs/{uid}/actions/optimize_shards_placement`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/optimize_shards_placement#get-bdbs-actions-optimize-shards-placement) request as the value of the `shards_blueprint` field to rearrange the database shards.\n\nTo ensure that the optimized shard placement is relevant for the current cluster state, pass the `cluster-state-id`, taken from the response header of the `GET` request, in the [`PUT`&nbsp;`/v1/bdbs/{uid}`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#put-bdbs) request headers.\n\nThe cluster will reject the update if its state was changed since the optimal shards placement was obtained."},{"id":"request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n| cluster-state-id | 30 | Cluster state ID |\n\n#### Example JSON body\n\n[code example]\n\n> [!WARNING]\n> If you submit such an optimized blueprint, it may cause strain on the cluster and its resources. Use with caution."}],"examples":[{"id":"get-bdbs-actions-optimize-shards-placement-ex0","language":"sh","code":"GET /v1/bdbs/{int: uid}/actions/optimize_shards_placement","section_id":"get-bdbs-actions-optimize-shards-placement"},{"id":"get-request-ex0","language":"sh","code":"GET /v1/bdbs/1/actions/optimize_shards_placement","section_id":"get-request"},{"id":"get-request-ex1","language":"sh","code":"GET /v1/bdbs/1/actions/optimize_shards_placement?shards_count=10&memory_size=10000","section_id":"get-request"},{"id":"get-response-ex0","language":"json","code":"[\n    {\n        \"nodes\": [\n            {\n                \"node_uid\": \"3\",\n                \"role\": \"master\"\n            },\n            {\n                \"node_uid\": \"1\",\n                \"role\": \"slave\"\n            }\n        ],\n        \"slot_range\": \"5461-10922\"\n    },\n    {\n        \"nodes\": [\n            {\n                \"node_uid\": \"3\",\n                \"role\": \"master\"\n            },\n            {\n                \"node_uid\": \"1\",\n                \"role\": \"slave\"\n            }\n        ],\n        \"slot_range\": \"10923-16383\"\n    },\n    {\n        \"nodes\": [\n            {\n                \"node_uid\": \"3\",\n                \"role\": \"master\"\n            },\n            {\n                \"node_uid\": \"1\",\n                \"role\": \"slave\"\n            }\n        ],\n        \"slot_range\": \"0-5460\"\n    }\n]","section_id":"get-response"},{"id":"request-ex0","language":"sh","code":"PUT /v1/bdbs/1","section_id":"request"},{"id":"request-ex1","language":"json","code":"{\n  \"shards_blueprint\": [\n    {\n      \"nodes\": [\n        {\n          \"node_uid\": \"2\",\n          \"role\": \"master\"\n        }\n      ],\n      \"slot_range\": \"0-8191\"\n    },\n    \"...\"\n  ]\n}","section_id":"request"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/bdbs/actions/rebalance","title":"Rebalance database requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/rebalance/","summary":"REST API requests to rebalance database shards","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","page_type":"content","content_hash":"ac3a9c4ba78e6629cc6ea4858a239e46124c2d2a5336262464704a6bb8b17d55","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [PUT](#put-bdbs-actions-rebalance) | `/v1/bdbs/{uid}/actions/rebalance` | Rebalance database shards |"},{"id":"put-bdbs-actions-rebalance","title":"Rebalance database shards","role":"content","text":"[code example]\n\nDistributes the database's shards across nodes based on the database's shard placement policy. See [Shard placement policy](https://redis.io/docs/latest/operate/rs/databases/memory-performance/shard-placement-policy) for more information about shard placement and available policies.\n\n#### Required permissions\n\n| Permission name | Roles |\n|-----------------|-------|\n| [view_bdb_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_bdb_info) | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer |"},{"id":"put-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\nDry-run example:\n\n[code example]\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the database to rebalance. |\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| dry_run | boolean | If true, returns the blueprint of the rebalanced database without actually changing the database. Default is false. |\n| only_failovers | boolean | If true, only runs failovers and no migrations. Default is false. |\n| max_operations | integer | Optional. The number of operations to do. Only works if `only_failovers` is true. If not provided, uses the number of the database's primary-replica pairs. |"},{"id":"put-response","title":"Response","role":"returns","text":"- If `dry_run` is `false`, returns an `action_uid`. You can track the action's progress with a [`GET /v1/actions/<action_uid>`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/actions#get-action) request.\n\n- If `dry_run` is `true`, returns the balanced shards' blueprint.\n\n  You can rearrange shards according to this blueprint if you use it in the `shards_blueprint` field of a [rearrange database shards](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/optimize_shards_placement#put-bdbs-rearrange-shards) request.\n  \n  You should also pass the rebalance shards' `cluster-state-id` response header as a request header of the rearrange database shards request to make sure the optimized shard placement is relevant for the current cluster state. The cluster will reject the update if its state changed since the optimal shards placement blueprint was generated.\n\n#### Example response\n\nIf `dry_run` is `false`:\n\n[code example]\n\nIf `dry_run` is `true`:\n\n[code example]\n\n#### Status codes {#put-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | When `dry_run` is false: The request is accepted and is being processed. When the database is recovered, its status will become active.<br />When `dry_run` is true: No error. |\n| [400 Bad Request](https://www.rfc-editor.org/rfc/rfc9110.html#name-400-bad-request) | Bad request. Invalid input parameters. |\n| [404 Not Found](https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found) | Attempting to perform an action on a nonexistent database. |\n| [406 Not Acceptable](https://www.rfc-editor.org/rfc/rfc9110.html#name-406-not-acceptable) | Not enough resources in the cluster to host the database. |\n| [409 Conflict](https://www.rfc-editor.org/rfc/rfc9110.html#name-409-conflict) | Database is currently busy with another action, recovery is already in progress, or is not in a recoverable state. |\n| [500 Internal Server Error](https://www.rfc-editor.org/rfc/rfc9110.html#name-500-internal-server-error) | Internal server error. |"}],"examples":[{"id":"put-bdbs-actions-rebalance-ex0","language":"sh","code":"PUT /v1/bdbs/{int: uid}/actions/rebalance","section_id":"put-bdbs-actions-rebalance"},{"id":"put-request-ex0","language":"sh","code":"PUT /v1/bdbs/1/actions/rebalance","section_id":"put-request"},{"id":"put-request-ex1","language":"sh","code":"PUT /v1/bdbs/1/actions/rebalance?only_failovers=true&dry_run=true","section_id":"put-request"},{"id":"put-response-ex0","language":"sh","code":"{\n    \"action_uid\": \"21ad01d5-55aa-4ec6-b5c0-44dc95176486\"\n}","section_id":"put-response"},{"id":"put-response-ex1","language":"sh","code":"[\n  {\n    \"nodes\": [\n      {\n        \"node_uid\": \"3\",\n        \"role\": \"master\"\n      },\n      {\n        \"node_uid\": \"1\",\n        \"role\": \"slave\"\n      }\n    ],\n    \"slot_range\": \"5461-10922\"\n  },\n  {\n    \"nodes\": [\n      {\n        \"node_uid\": \"3\",\n        \"role\": \"master\"\n      },\n      {\n        \"node_uid\": \"1\",\n        \"role\": \"slave\"\n      }\n    ],\n    \"slot_range\": \"10923-16383\"\n  },\n  {\n    \"nodes\": [\n      {\n        \"node_uid\": \"3\",\n        \"role\": \"master\"\n      },\n      {\n        \"node_uid\": \"1\",\n        \"role\": \"slave\"\n      }\n    ],\n    \"slot_range\": \"0-5460\"\n  }\n]","section_id":"put-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/bdbs/actions/recover","title":"Recover database requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/recover/","summary":"REST API requests for database recovery","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","page_type":"content","content_hash":"b009a492d8c5f0bab0f385359bb837b36672b722a271e36a44abc0edbbb708fc","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-bdbs-actions-recover) | `/v1/bdbs/{uid}/actions/recover` | Get database recovery plan  |\n| [POST](#post-bdbs-actions-recover) | `/v1/bdbs/{uid}/actions/recover` | Recover database  |"},{"id":"get-bdbs-actions-recover","title":"Get recovery plan","role":"content","text":"[code example]\n\nFetches the recovery plan for a database. The recovery plan provides information about the recovery status, such as whether recovery is possible, and details on available files to use for recovery.\n\n#### Required permissions\n\n| Permission name | Roles |\n|-----------------|-------|\n| [view_bdb_recovery_plan](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_bdb_recovery_plan) | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the database. |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns a JSON object that represents the database's recovery plan, including recovery files and status.\n\n#### Example response body\n\n[code example]\n\n#### Status codes {#get-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | No error. |\n| [404 Not Found](https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found) | Database UID does not exist. |"},{"id":"post-bdbs-actions-recover","title":"Recover database","role":"content","text":"[code example]\n\nInitiates [recovery for a database](https://redis.io/docs/latest/operate/rs/databases/recover) in a recoverable state where all the database's files are available after [cluster recovery](https://redis.io/docs/latest/operate/rs/clusters/cluster-recovery).\n\n#### Required permissions\n\n| Permission name | Roles |\n|-----------------|-------|\n| [start_bdb_recovery](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#start_bdb_recovery) | admin<br />cluster_member<br />db_member |"},{"id":"post-request","title":"Request","role":"content","text":"The request body can either be empty or include a recovery plan.\n\nIf the request body is empty, the database will be recovered automatically:\n\n- Databases with no persistence are recovered with no data.\n\n- Persistent files such as AOF or RDB will be loaded from their expected storage locations where replica or primary shards were last active.\n\n- If persistent files are not found where expected but can be located on other cluster nodes, they will be used.\n\n#### Example HTTP request\n\n[code example]\n\n#### Example request body\n\n[code example]\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the database to recover. |"},{"id":"post-response","title":"Response","role":"returns","text":"Returns a status code. Also returns a JSON object with an `action_uid` in the request body if successful.\n\n#### Status codes {#post-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | The request is accepted and is being processed. When the database is recovered, its status will become `active`. |\n| [404 Not Found](https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found) | Attempting to perform an action on a nonexistent database. |\n| [409 Conflict](https://www.rfc-editor.org/rfc/rfc9110.html#name-409-conflict) | Database is currently busy with another action, recovery is already in progress, or is not in a recoverable state. |"}],"examples":[{"id":"get-bdbs-actions-recover-ex0","language":"sh","code":"GET /v1/bdbs/{int: uid}/actions/recover","section_id":"get-bdbs-actions-recover"},{"id":"get-request-ex0","language":"sh","code":"GET /v1/bdbs/1/actions/recover","section_id":"get-request"},{"id":"get-response-ex0","language":"json","code":"{\n  \"data_files\": [\n    {\n      \"filename\": \"appendonly-1.aof\",\n      \"last_modified\": 1721164863.8883622,\n      \"node_uid\": \"1\",\n      \"shard_role\": \"master\",\n      \"shard_slots\": \"1-2048\",\n      \"shard_uid\": \"1\",\n      \"size\": 88\n    },\n    {\n      \"filename\": \"appendonly-2.aof\",\n      \"last_modified\": 1721164863.8883622,\n      \"node_uid\": \"2\",\n      \"shard_role\": \"slave\",\n      \"shard_slots\": \"2049-4096\",\n      \"shard_uid\": \"2\",\n      \"size\": 88\n    }\n  ],\n  \"status\": \"ready\"\n}","section_id":"get-response"},{"id":"post-bdbs-actions-recover-ex0","language":"sh","code":"POST /v1/bdbs/{int: uid}/actions/recover","section_id":"post-bdbs-actions-recover"},{"id":"post-request-ex0","language":"sh","code":"POST /v1/bdbs/1/actions/recover","section_id":"post-request"},{"id":"post-request-ex1","language":"json","code":"{\n  \"data_files\": [\n    {\n      \"filename\": \"appendonly-1.aof\",\n      \"node_uid\": \"1\",\n      \"shard_slots\": \"1-2048\"\n    },\n    {\n      \"filename\": \"appendonly-2.aof\",\n      \"node_uid\": \"2\",\n      \"shard_slots\": \"2049-4096\"\n    }\n  ],\n  \"ignore_errors\": false,\n  \"recover_without_data\": false\n}","section_id":"post-request"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/bdbs/actions/resume_traffic","title":"Resume database traffic requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/resume_traffic/","summary":"REST API requests to resume traffic for a database","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","page_type":"content","content_hash":"4e9cb02706acaaf2d0eda1a1d597e81c3106f68eca8ca5f5546ba1f31b13b6cf","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [POST](#post-bdbs-actions-resume-traffic) | `/v1/bdbs/{uid}/actions/resume_traffic` | Resume database traffic |"},{"id":"post-bdbs-actions-resume-traffic","title":"Resume database traffic","role":"content","text":"[code example]\n\nResume traffic handling for the database.\n\nUse this action to resume read and write traffic on a database, where traffic was previously paused using the [`stop_traffic`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/stop_traffic) action.\n\n#### Required permissions\n\n| Permission name | Roles |\n|-----------------|-------|\n| [update_bdb_with_action](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#update_bdb_with_action) | admin<br />cluster_member<br />db_member |"},{"id":"post-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the database. |"},{"id":"post-response","title":"Response","role":"returns","text":"Returns a JSON object with an `action_uid`. You can track the action's progress with a [`GET /v1/actions/<action_uid>`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/actions#get-action) request.\n\n#### Status codes {#post-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | The request is accepted and is being processed. The database state will be `active-change-pending` until the request has been fully processed. |\n| [404 Not Found](https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found) | Attempting to perform an action on a nonexistent database. |\n| [409 Conflict](https://www.rfc-editor.org/rfc/rfc9110.html#name-409-conflict) | Attempting to change a database while it is busy with another configuration change. This is a temporary condition, and the request should be reattempted later. |"}],"examples":[{"id":"post-bdbs-actions-resume-traffic-ex0","language":"sh","code":"POST /v1/bdbs/{int: uid}/actions/resume_traffic","section_id":"post-bdbs-actions-resume-traffic"},{"id":"post-request-ex0","language":"sh","code":"POST /v1/bdbs/1/actions/resume_traffic","section_id":"post-request"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/bdbs/actions/revamp","title":"Revamp database requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/revamp/","summary":"REST API requests to update database configuration and optimize shard placement","tags":["docs","operate","rs"],"last_updated":"2025-05-19T15:06:21-05:00","page_type":"content","content_hash":"94635642024a8d23f921cf78de7911b06c661b0cb5ccc9b7bd5ce7718903058d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [PUT](#put-bdbs-actions-revamp) | `/v1/bdbs/{uid}/actions/revamp` | Update database configuration and optimize shard placement |"},{"id":"put-bdbs-actions-revamp","title":"Revamp database","role":"content","text":"[code example]\n\nUpdates the topology-related configurations of an active database and optimizes the shard placement for the new configuration.\n\n#### Required permissions\n\n| Permission name | Roles |\n|-----------------|-------|\n| [update_bdb_with_action](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#update_bdb_with_action) | admin<br />cluster_member<br />db_member |"},{"id":"put-request","title":"Request","role":"content","text":"You can include the following parameters in the request JSON body to update their values:\n\n| Field | Type/Value | Description |\n|-------|------------|-------------|\n| avoid_nodes | array of strings | Cluster node UIDs to avoid when placing the database's shards and binding its endpoints. |\n| bigstore_ram_size | integer | Memory size of bigstore RAM part. |\n| memory_size | integer (default: 0) | Database memory limit in bytes. 0 is unlimited. |\n| replication | boolean | If `true`, enable in-memory database replication mode. |\n| shards_count | integer, (range: 1-512) (default: 1) | Number of database server-side shards. |\n| shards_placement | \"dense\"<br />\"sparse\" | Control the density of shards. Values:<br />**dense**: Shards reside on as few nodes as possible.<br />**sparse**: Shards reside on as many nodes as possible. |\n\n#### Example HTTP request\n\n[code example]\n\nDry-run example:\n\n[code example]\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the database to update. |\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| dry_run | boolean | If true, returns a blueprint of the database update without actually changing the database. Default is false. |\n| pause_persistence | boolean | If true, pause the persistence during the update. Default is false. |"},{"id":"put-response","title":"Response","role":"returns","text":"- If `dry_run` is `false`, returns an `action_uid`. You can track the action's progress with a [`GET /v1/actions/<action_uid>`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/actions#get-action) request.\n\n- If `dry_run` is `true`, returns a blueprint of the database update.\n\n#### Example response\n\nIf `dry_run` is `false`:\n\n[code example]\n\nIf `dry_run` is `true`:\n\n[code example]\n\n#### Status codes {#put-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | When `dry_run` is false: The request is accepted and is being processed. The database state will be `active-change-pending` until the request has been fully processed. <br />When `dry_run` is true: No error. |\n| [404 Not Found](https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found) | Attempting to perform an action on a nonexistent database. |\n| [406 Not Acceptable](https://www.rfc-editor.org/rfc/rfc9110.html#name-406-not-acceptable) | The requested configuration is invalid. |\n| [409 Conflict](https://www.rfc-editor.org/rfc/rfc9110.html#name-409-conflict) | Attempting to change a database while it is busy with another configuration change. In this context, this is a temporary condition and the request should be re-attempted later. |"}],"examples":[{"id":"put-bdbs-actions-revamp-ex0","language":"sh","code":"PUT /v1/bdbs/{int: uid}/actions/revamp","section_id":"put-bdbs-actions-revamp"},{"id":"put-request-ex0","language":"sh","code":"PUT /v1/bdbs/1/actions/revamp\n{\n    \"replication\": true,\n    \"shards_count\": 12\n}","section_id":"put-request"},{"id":"put-request-ex1","language":"sh","code":"PUT /v1/bdbs/1/actions/revamp?dry_run=true\n{\n  \"replication\": true,\n  \"shards_count\": 12\n}","section_id":"put-request"},{"id":"put-response-ex0","language":"sh","code":"{\n  \"action_uid\": \"21ad01d5-55aa-4ec6-b5c0-44dc95176486\"\n}","section_id":"put-response"},{"id":"put-response-ex1","language":"sh","code":"[\n  {\n    \"nodes\": [\n      {\n        \"node_uid\": \"3\",\n        \"role\": \"master\"\n      },\n      {\n        \"node_uid\": \"1\",\n        \"role\": \"slave\"\n      }\n    ],\n    \"slot_range\": \"5461-10922\"\n  },\n  {\n    \"nodes\": [\n      {\n        \"node_uid\": \"3\",\n        \"role\": \"master\"\n      },\n      {\n        \"node_uid\": \"1\",\n        \"role\": \"slave\"\n      }\n    ],\n    \"slot_range\": \"10923-16383\"\n  },\n  {\n    \"nodes\": [\n      {\n        \"node_uid\": \"3\",\n        \"role\": \"master\"\n      },\n      {\n        \"node_uid\": \"1\",\n        \"role\": \"slave\"\n      }\n    ],\n    \"slot_range\": \"0-5460\"\n  }\n]","section_id":"put-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/bdbs/actions/stop_traffic","title":"Stop database traffic requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/stop_traffic/","summary":"REST API requests to stop traffic for a database","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","page_type":"content","content_hash":"4d8907b224a0b5f6c4f74e690c738f806a071fbfbf82ac23f6ce50be48dd2da3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [POST](#post-bdbs-actions-stop-traffic) | `/v1/bdbs/{uid}/actions/stop_traffic` | Stop database traffic |"},{"id":"post-bdbs-actions-stop-traffic","title":"Stop database traffic","role":"content","text":"[code example]\n\nStop handling traffic for the database.\n\nUse this action to stop read and write traffic on a database. To resume traffic afterward, use the [`resume_traffic`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/resume_traffic) action.\n\n#### Required permissions\n\n| Permission name | Roles |\n|-----------------|-------|\n| [update_bdb_with_action](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#update_bdb_with_action) | admin<br />cluster_member<br />db_member |"},{"id":"post-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the database. |"},{"id":"post-response","title":"Response","role":"returns","text":"Returns a JSON object with an `action_uid`. You can track the action's progress with a [`GET /v1/actions/<action_uid>`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/actions#get-action) request.\n\n#### Status codes {#post-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | The request is accepted and is being processed. The database state will be `active-change-pending` until the request has been fully processed. |\n| [404 Not Found](https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found) | Attempting to perform an action on a nonexistent database. |\n| [409 Conflict](https://www.rfc-editor.org/rfc/rfc9110.html#name-409-conflict) | Attempting to change a database while it is busy with another configuration change. This is a temporary condition, and the request should be reattempted later. |"}],"examples":[{"id":"post-bdbs-actions-stop-traffic-ex0","language":"sh","code":"POST /v1/bdbs/{int: uid}/actions/stop_traffic","section_id":"post-bdbs-actions-stop-traffic"},{"id":"post-request-ex0","language":"sh","code":"POST /v1/bdbs/1/actions/stop_traffic","section_id":"post-request"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/bdbs/alerts","title":"Database alerts requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/alerts/","summary":"Database alert requests","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","page_type":"content","content_hash":"ff2ec03dea9c9c7a460579e3107f9585131df53cf42d184641266525a7ba6875","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-all-bdbs-alerts) | `/v1/bdbs/alerts` | Get all alert states for all databases |\n| [GET](#get-bdbs-alerts) | `/v1/bdbs/alerts/{uid}` | Get all alert states for a specific database |\n| [GET](#get-bdbs-alert) | `/v1/bdbs/alerts/{uid}/{alert}` | Get a specific database alert state |\n| [POST](#post-bdbs-alerts) | `/v1/bdbs/alerts/{uid}` | Update a database’s alerts configuration |"},{"id":"get-all-bdbs-alerts","title":"Get all database alerts","role":"content","text":"GET /v1/bdbs/alerts\n\nGet all alert states for all databases.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_all_bdbs_alerts](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_all_bdbs_alerts) |"},{"id":"get-all-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/bdbs/alerts \n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |"},{"id":"get-all-response","title":"Response","role":"returns","text":"Returns a hash of alert UIDs and the [alerts](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/alert) states for each database.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-all-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |"},{"id":"get-bdbs-alerts","title":"Get database alerts","role":"content","text":"GET /v1/bdbs/alerts/{int: uid}\n\nGet all alert states for a database.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_bdb_alerts](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_bdb_alerts) |"},{"id":"get-request-alerts","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/bdbs/alerts/1 \n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |"},{"id":"get-response-alerts","title":"Response","role":"returns","text":"Returns a hash of [alert objects](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/alert) and their states.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes-alerts","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Specified bdb does not exist |"},{"id":"get-bdbs-alert","title":"Get database alert","role":"content","text":"GET /v1/bdbs/alerts/{int: uid}/{alert}\n\nGet a database alert state.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_bdb_alerts](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_bdb_alerts) |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/bdbs/alerts/1/bdb_size \n\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the database |\n| alert | string | The alert name |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns an [alert object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/alert).\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |\n| [400 Bad Request](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1) | Bad request |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Specified alert or bdb does not exist |"},{"id":"post-bdbs-alerts","title":"Update database alert","role":"content","text":"POST /v1/bdbs/alerts/{int: uid}\n\nUpdates a database's alerts configuration.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [update_bdb_alerts](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#update_bdb_alerts) |"},{"id":"post-request","title":"Request","role":"content","text":"If passed with the dry_run URL query string, the function will validate the alert thresholds, but not commit them.\n\n#### Example HTTP request\n\n\tPOST /v1/bdbs/alerts/1 \n\n#### Example JSON body\n\n[code example]\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | Database ID |\n| dry_run | string | Validate the alert thresholds but do not apply them |\n\n#### Request body\n\nThe request must contain a single JSON object with one or many database [alert objects](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/alert)."},{"id":"post-response","title":"Response","role":"returns","text":"The response includes the updated database [alerts](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/alert)."},{"id":"post-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Specified database was not found. |\n| [406 Not Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | Invalid configuration parameters provided. |\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success, database alerts updated. |"}],"examples":[{"id":"get-all-response-ex0","language":"json","code":"{\n    \"1\": {\n        \"bdb_size\": {\n            \"enabled\": true,\n            \"state\": true,\n            \"threshold\": \"80\",\n            \"change_time\": \"2014-08-29T11:19:49Z\",\n            \"severity\": \"WARNING\",\n            \"change_value\": {\n                \"state\": true,\n                \"threshold\": \"80\",\n                \"memory_util\": 81.2\n            }\n        },\n        \"...\"\n    },\n    \"...\"\n}","section_id":"get-all-response"},{"id":"get-response-alerts-ex0","language":"json","code":"{\n    \"bdb_size\": {\n        \"enabled\": true,\n        \"state\": true,\n        \"threshold\": \"80\",\n        \"severity\": \"WARNING\",\n        \"change_time\": \"2014-08-29T11:19:49Z\",\n        \"change_value\": {\n            \"state\": true,\n            \"threshold\": \"80\",\n            \"memory_util\": 81.2\n        }\n    },\n    \"...\"\n}","section_id":"get-response-alerts"},{"id":"get-response-ex0","language":"json","code":"{\n    \"enabled\": true,\n    \"state\": true,\n    \"threshold\": \"80\",\n    \"severity\": \"WARNING\",\n    \"change_time\": \"2014-08-29T11:19:49Z\",\n    \"change_value\": {\n        \"state\": true,\n        \"threshold\": \"80\",\n        \"memory_util\": 81.2\n    }\n}","section_id":"get-response"},{"id":"post-request-ex0","language":"json","code":"{\n     \"bdb_size\":{\n         \"threshold\":\"80\",\n         \"enabled\":true\n     },\n     \"bdb_high_syncer_lag\":{\n         \"threshold\":\"\",\n         \"enabled\":false\n     },\n     \"bdb_low_throughput\":{\n         \"threshold\":\"1\",\n         \"enabled\":true\n     },\n     \"bdb_high_latency\":{\n         \"threshold\":\"3000\",\n         \"enabled\":true\n     },\n     \"bdb_high_throughput\":{\n         \"threshold\":\"1\",\n         \"enabled\":true\n     },\n     \"bdb_backup_delayed\":{\n         \"threshold\":\"1800\",\n         \"enabled\":true\n     }\n}","section_id":"post-request"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/bdbs/availability","title":"Database availability requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/availability/","summary":"REST API requests to check database availability","tags":["docs","operate","rs"],"last_updated":"2026-07-21T11:19:25-05:00","page_type":"content","content_hash":"7ee223820c77c3595079b2fb5acfdfa673511f59c4d337d8f4198c3db478dc05","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-db-endpoint-availability) | `/v1/local/bdbs/{uid}/endpoint/availability` | Verifies local database endpoint availability |\n| [GET](#get-db-availability) | `/v1/bdbs/{uid}/availability` | Verifies database availability |"},{"id":"get-db-endpoint-availability","title":"Get database endpoint availability","role":"content","text":"[code example]\n\nVerifies the local database endpoint is available. This request does not redirect to the primary node."},{"id":"permissions","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [view_bdb_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_bdb_info) | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |"},{"id":"get-endpoint-request","title":"Request","role":"content","text":"#### Example HTTP requests\n\nTo check database endpoint availability without any additional checks:\n\n[code example]\n\nTo perform a lag-aware database endpoint availability check using the cluster's default lag tolerance threshold:\n\n[code example]\n\nTo perform a lag-aware database endpoint availability check and override the cluster's default lag tolerance threshold:\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the database. |\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| extend_check | list of comma-separated strings | List of additional availability checks to perform (optional)<br />Values:<br />**lag**: Enables lag-aware checks to assess replication health. Determines if a replica is sufficiently synced with the primary for failover/failback scenarios. |\n| availability_lag_tolerance_ms | integer | Overrides the cluster's default lag tolerance threshold when using `extend_check=lag`. Recommended value: 100 milliseconds. |"},{"id":"get-endpoint-response","title":"Response","role":"returns","text":"Returns the status code `200 OK` if the local database endpoint is available.\n\nIf the local database endpoint is unavailable, returns an error status code and a JSON object that contains `error_code` and `description` fields."},{"id":"get-endpoint-error-codes","title":"Error codes","role":"errors","text":"When errors are reported, the server may return a JSON object with\n`error_code` and `description` fields that provide additional information.\nThe following are possible `error_code` values:\n\n| Code | Description |\n|------|-------------|\n| no_quorum | Master healthcheck failed (no quorum in the cluster) |\n| db_not_found | Database does not exist in the cluster |\n| bdb_endpoint_unavailable | Local database endpoint is not available |"},{"id":"get-endpoint-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | Database endpoint is available. |\n| [400 Bad Request](https://www.rfc-editor.org/rfc/rfc9110.html#name-400-bad-request) | Invalid schema. |\n| [404 Not Found](https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found) | Database not found. |\n| [503 Service Unavailable](https://www.rfc-editor.org/rfc/rfc9110.html#name-503-service-unavailable) | Database endpoint is unavailable. |"},{"id":"get-db-availability","title":"Get database availability","role":"content","text":"[code example]\n\nGets the availability status of a database.\n\n- If the OSS Cluster API is enabled, verifies all endpoints for this database are available.\n\n- Otherwise, verifies the database has at least one available endpoint."},{"id":"permissions-1","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [view_bdb_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_bdb_info) | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |"},{"id":"get-db-request","title":"Request","role":"content","text":"#### Example HTTP requests\n\n\nTo check database availability without any additional checks:\n\n[code example]\n\nTo perform a lag-aware database availability check using the cluster's default lag tolerance threshold:\n\n[code example]\n\nTo perform a lag-aware database availability check and override the cluster's default lag tolerance threshold:\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the database. |\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| extend_check | list of comma-separated strings | List of additional availability checks to perform (optional)<br />Values:<br />**lag**: Enables lag-aware checks to assess replication health. Determines if a replica is sufficiently synced with the primary for failover/failback scenarios. |\n| availability_lag_tolerance_ms | integer | Overrides the cluster's default lag tolerance threshold when using `extend_check=lag`. Recommended value: 100 milliseconds. |"},{"id":"get-db-response","title":"Response","role":"returns","text":"Returns the status code `200 OK` if the database is available.\n\nIf the database is unavailable, returns an error status code and a JSON object that contains `error_code` and `description` fields."},{"id":"get-db-error-codes","title":"Error codes","role":"errors","text":"When errors are reported, the server may return a JSON object with\n`error_code` and `description` fields that provide additional information.\nThe following are possible `error_code` values:\n\n| Code | Description |\n|------|-------------|\n| no_quorum | Master healthcheck failed (no quorum in the cluster) |\n| db_not_found | Database does not exist in the cluster |\n| bdb_unavailable | Database is not available. The `error_code` identifies why: it appends `_shard_unreachable` when a primary (master) shard is unreachable and `_port_unbound` when the endpoint's listener port is not bound. For example, `bdb_unavailable_shard_unreachable`, `bdb_unavailable_port_unbound`, or `bdb_unavailable_shard_unreachable_port_unbound`. The `description` names the same reasons, for example `Endpoint error status: shard unreachable, port unbound`. |"},{"id":"get-db-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | Database is available. |\n| [400 Bad Request](https://www.rfc-editor.org/rfc/rfc9110.html#name-400-bad-request) | Invalid schema. |\n| [404 Not Found](https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found) | Database not found. |\n| [503 Service Unavailable](https://www.rfc-editor.org/rfc/rfc9110.html#name-503-service-unavailable) | Database is unavailable or doesn't have quorum. |"}],"examples":[{"id":"get-db-endpoint-availability-ex0","language":"sh","code":"GET /v1/local/bdbs/{uid}/endpoint/availability","section_id":"get-db-endpoint-availability"},{"id":"get-endpoint-request-ex0","language":"sh","code":"GET /v1/local/bdbs/1/endpoint/availability","section_id":"get-endpoint-request"},{"id":"get-endpoint-request-ex1","language":"sh","code":"GET /v1/local/bdbs/1/endpoint/availability?extend_check=lag","section_id":"get-endpoint-request"},{"id":"get-endpoint-request-ex2","language":"sh","code":"GET /v1/local/bdbs/1/endpoint/availability?extend_check=lag&availability_lag_tolerance_ms=100","section_id":"get-endpoint-request"},{"id":"get-db-availability-ex0","language":"sh","code":"GET /v1/bdbs/{uid}/availability","section_id":"get-db-availability"},{"id":"get-db-request-ex0","language":"sh","code":"GET /v1/bdbs/1/availability","section_id":"get-db-request"},{"id":"get-db-request-ex1","language":"sh","code":"GET /v1/bdbs/1/availability?extend_check=lag","section_id":"get-db-request"},{"id":"get-db-request-ex2","language":"sh","code":"GET /v1/bdbs/1/availability?extend_check=lag&availability_lag_tolerance_ms=100","section_id":"get-db-request"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/bdbs/crdt_sources-alerts","title":"Database CRDT sources alerts requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/crdt_sources-alerts/","summary":"Conflict-free replicated data type (CRDT) source alert requests","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","page_type":"content","content_hash":"42107ddf06c1fd093aa745a7c6777be9bd4d179299d83dd1b5ddc8d3de51bee0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-all-bdbs-crdt-sources-alerts) | `/v1/bdbs/crdt_sources/alerts` | Get all CRDT sources alert states for all CRDB databases |\n| [GET](#get-bdbs-crdt-sources-alerts) | `/v1/bdbs/crdt_sources/alerts/{uid}` | Get all CRDT sources alert states for a database |\n| [GET](#get-bdbs-crdt-source-all-alerts) | `/v1/bdbs/crdt_sources/alerts/{uid}/{crdt_src_id}` | Get all alert states for a CRDT source |\n| [GET](#get-bdbs-crdt-source-alert) | `/v1/bdbs/crdt_sources/alerts/{uid}/{crdt_src_id}/{alert}` | Get a database alert state |"},{"id":"get-all-bdbs-crdt-sources-alerts","title":"Get all CRDB CRDT source alert states","role":"content","text":"GET /v1/bdbs/crdt_sources/alerts\n\nGet all alert states for all CRDT sources of all CRDBs.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_all_bdbs_alerts](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_all_bdbs_alerts) |"},{"id":"get-all-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/bdbs/crdt_sources/alerts \n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |"},{"id":"get-all-response","title":"Response","role":"returns","text":"Returns a hash of alert UIDs and the [alerts states](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/alert) for each local BDB of CRDB.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-all-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |"},{"id":"get-bdbs-crdt-sources-alerts","title":"Get all BDB CRDT sources alert states","role":"content","text":"GET /v1/bdbs/crdt_sources/alerts/{int: uid}\n\nGet all alert states for all crdt sources for a specific local bdb of a CRDB.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_bdb_alerts](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_bdb_alerts) |"},{"id":"get-request-all-crdt-alerts","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/bdbs/crdt_sources/alerts/1 \n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the database |"},{"id":"get-response-all-crdt-alerts","title":"Response","role":"returns","text":"Returns a hash of [alert objects](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/alert) and their states.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes-all-crdt-alerts","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Specified bdb does not exist |"},{"id":"get-bdbs-crdt-source-all-alerts","title":"Get all CRDT source alert states","role":"content","text":"GET /v1/bdbs/crdt_sources/alerts/{int: uid}/{int: crdt_src_id}\n\nGet all alert states for specific crdt source for a specific local BDB\nof a CRDB.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_bdb_alerts](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_bdb_alerts) |"},{"id":"get-request-crdt-alerts","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/bdbs/crdt_sources/alerts/1/2 \n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the database |\n| crdt_src_id | integer | The ID of the crdt source in this BDB |"},{"id":"get-response-crdt-alerts","title":"Response","role":"returns","text":"Returns a hash of [alert objects](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/alert) and their states. \n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes-crdt-alerts","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Specified bdb does not exist |"},{"id":"get-bdbs-crdt-source-alert","title":"Get database alert state","role":"content","text":"GET /v1/bdbs/crdt_sources/alerts/{int: uid}/{int: crdt_src_id}/{alert}\n\nGet a BDB alert state.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_bdb_alerts](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_bdb_alerts) |"},{"id":"get-request-alert","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/bdbs/crdt_sources/alerts/1/2/crdt_src_syncer_connection_error \n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the database |\n| crdt_src_id | integer | The ID of the crdt source in this BDB |\n| alert | string | The alert name |"},{"id":"get-response-alert","title":"Response","role":"returns","text":"Returns an [alert object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/alert).\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes-alert","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |\n| [400 Bad Request](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1) | Bad request |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Specified alert or bdb does not exist |"}],"examples":[{"id":"get-all-response-ex0","language":"json","code":"{\n    \"1\": {\n        \"crdt_src_syncer_connection_error\": {\n            \"enabled\": true,\n            \"state\": true,\n            \"threshold\": \"80\",\n            \"change_time\": \"2014-08-29T11:19:49Z\",\n            \"severity\": \"WARNING\",\n            \"change_value\": {\n                \"state\": true,\n                \"threshold\": \"80\",\n                \"memory_util\": 81.2\n            }\n        },\n        \"...\"\n    },\n    \"...\"\n}","section_id":"get-all-response"},{"id":"get-response-all-crdt-alerts-ex0","language":"json","code":"{\n    \"crdt_src_syncer_connection_error\": {\n        \"enabled\": true,\n        \"state\": true,\n        \"threshold\": \"80\",\n        \"severity\": \"WARNING\",\n        \"change_time\": \"2014-08-29T11:19:49Z\",\n        \"change_value\": {\n            \"state\": true,\n            \"threshold\": \"80\",\n            \"memory_util\": 81.2\n        }\n    },\n    \"...\"\n}","section_id":"get-response-all-crdt-alerts"},{"id":"get-response-crdt-alerts-ex0","language":"json","code":"{\n    \"crdt_src_syncer_connection_error\": {\n        \"enabled\": true,\n        \"state\": true,\n        \"threshold\": \"80\",\n        \"severity\": \"WARNING\",\n        \"change_time\": \"2014-08-29T11:19:49Z\",\n        \"change_value\": {\n            \"state\": true,\n            \"threshold\": \"80\",\n            \"memory_util\": 81.2\n        }\n    },\n    \"...\"\n}","section_id":"get-response-crdt-alerts"},{"id":"get-response-alert-ex0","language":"json","code":"{\n    \"enabled\": true,\n    \"state\": true,\n    \"threshold\": \"80\",\n    \"severity\": \"WARNING\",\n    \"change_time\": \"2014-08-29T11:19:49Z\",\n    \"change_value\": {\n        \"state\": true,\n        \"threshold\": \"80\",\n        \"memory_util\": 81.2\n    }\n}","section_id":"get-response-alert"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/bdbs/debuginfo","title":"Database debug info requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/debuginfo/","summary":"Documents the Redis Software REST API /bdbs/debuginfo requests.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"be256b8dc4a26c1f9f3ce892d70e05fafdf3a17599dd86eacce2183628c8b49d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-all-debuginfo-bdb) | `/v1/bdbs/debuginfo` | Get debug info from all databases |\n| [GET](#get-debuginfo-bdb) | `/v1/bdbs/{bdb_uid}/debuginfo` | Get debug info from a specific database |"},{"id":"get-all-debuginfo-bdb","title":"Get debug info from all databases","role":"content","text":"GET /v1/bdbs/debuginfo\n\nDownloads a tar file that contains debug info from all databases.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_debugging_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_debugging_info) |"},{"id":"get-all-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/bdbs/debuginfo"},{"id":"get-all-response","title":"Response","role":"returns","text":"Downloads the debug info in a tar file called `filename.tar.gz`. Extract the files from the tar file to access the debug info.\n\n#### Response headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Content-Type | application/x-gzip | Media type of request/response body |\n| Content-Length | 653350 | Length of the response body in octets |\n| Content-Disposition | attachment; filename=debuginfo.tar.gz | Display response in browser"},{"id":"get-all-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success. |\n| [500 Internal Server Error](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1) | Failed to get debug info. |"},{"id":"get-debuginfo-bdb","title":"Get database debug info","role":"content","text":"GET /v1/bdbs/{int: bdb_uid}/debuginfo\n\nDownloads a tar file that contains debug info from the database specified by `bdb_uid`.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_debugging_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_debugging_info) |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/bdbs/1/debuginfo"},{"id":"get-response","title":"Response","role":"returns","text":"Downloads the debug info in a tar file called `filename.tar.gz`. Extract the files from the tar file to access the debug info.\n\n#### Response headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Content-Type | application/x-gzip | Media type of request/response body |\n| Content-Length | 653350 | Length of the response body in octets |\n| Content-Disposition | attachment; filename=debuginfo.tar.gz | Display response in browser"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success. |\n| [500 Internal Server Error](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1) | Failed to get debug info. |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/bdbs","title":"Database requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/","summary":"Database requests","tags":["docs","operate","rs"],"last_updated":"2026-10-05T16:11:44+01:00","children":[{"id":"operate/rs/references/rest-api/requests/bdbs/actions","summary":"Database action requests","title":"Database actions requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/"},{"id":"operate/rs/references/rest-api/requests/bdbs/alerts","summary":"Database alert requests","title":"Database alerts requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/alerts/"},{"id":"operate/rs/references/rest-api/requests/bdbs/availability","summary":"REST API requests to check database availability","title":"Database availability requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/availability/"},{"id":"operate/rs/references/rest-api/requests/bdbs/crdt_sources-alerts","summary":"Conflict-free replicated data type (CRDT) source alert requests","title":"Database CRDT sources alerts requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/crdt_sources-alerts/"},{"id":"operate/rs/references/rest-api/requests/bdbs/debuginfo","summary":"Documents the Redis Software REST API /bdbs/debuginfo requests.","title":"Database debug info requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/debuginfo/"},{"id":"operate/rs/references/rest-api/requests/bdbs/modules","summary":"Redis module requests","title":"Database modules requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/modules/"},{"id":"operate/rs/references/rest-api/requests/bdbs/passwords","summary":"Database password requests","title":"Database passwords requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/passwords/"},{"id":"operate/rs/references/rest-api/requests/bdbs/peer_stats","summary":"Active-Active peer instance statistics requests","title":"CRDB peer stats requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/peer_stats/"},{"id":"operate/rs/references/rest-api/requests/bdbs/replica_sources-alerts","summary":"Replica source alert requests","title":"Database replica sources alerts requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/replica_sources-alerts/"},{"id":"operate/rs/references/rest-api/requests/bdbs/shards","summary":"REST API requests for database shards","title":"Database shards requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/shards/"},{"id":"operate/rs/references/rest-api/requests/bdbs/stats","summary":"Database statistics requests","title":"Database stats requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/stats/"},{"id":"operate/rs/references/rest-api/requests/bdbs/sync_source_stats","summary":"Syncer source statistics requests","title":"Database syncer source stats requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/sync_source_stats/"},{"id":"operate/rs/references/rest-api/requests/bdbs/syncer_state","summary":"Syncer state requests","title":"Syncer state requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/syncer_state/"},{"id":"operate/rs/references/rest-api/requests/bdbs/upgrade","summary":"Database upgrade requests","title":"Database upgrade requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/upgrade/"}],"page_type":"content","content_hash":"4701883ed7a676feb96870195d937793af41bd335e6a2e9b49398cff43c1ecaa","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-all-bdbs) | `/v1/bdbs` | Get all databases |\n| [GET](#get-bdbs) | `/v1/bdbs/{uid}` | Get a single database |\n| [PUT](#put-bdbs) | `/v1/bdbs/{uid}` | Update database configuration |\n| [PUT](#put-bdbs-action) | `/v1/bdbs/{uid}/{action}` | Update database configuration and perform additional action |\n| [POST](#post-bdbs-v1) | `/v1/bdbs` | Create a new database |\n| [POST](#post-bdbs-v2) | `/v2/bdbs` | Create a new database |\n| [DELETE](#delete-bdbs) | `/v1/bdbs/{uid}` | Delete a database |"},{"id":"get-all-bdbs","title":"Get all databases","role":"content","text":"[code example]\n\nGet all databases in the cluster."},{"id":"permissions","title":"Permissions","role":"security","text":"| Permission name | Roles   |\n|-----------------|---------|\n| [view_all_bdbs_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_all_bdbs_info) |  admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |"},{"id":"get-all-request","title":"Request","role":"content","text":"#### Example HTTP requests\n\nThe following request returns the unique IDs and names of all databases:\n\n[code example]\n\nThe following request returns all fields for the databases, including configuration fields for modules like search, probabilistic, and timeseries:\n\n[code example]\n\n#### Headers\n\n| Key | Value |\n|-----|-------|\n| Host | The domain name or IP of the cluster |\n| Accept | application/json |\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| fields | string | Comma-separated list of field names to return (by default all fields are returned). (optional) |\n| extended | boolean | If true, includes configuration fields for modules, such as search, timeseries, and probabilistic, in the response. If the `extended` query parameter is not included in the request, module configuration fields do not appear in the response. (optional) |"},{"id":"get-all-response","title":"Response","role":"returns","text":"The response body contains a JSON array with all databases, represented as [BDB objects](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb).\n\n#### Body\n\n[code example]\n\n#### Status codes {#get-all-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |"},{"id":"example-requests","title":"Example requests","role":"example","text":"#### cURL\n\n[code example]\n\n#### Python\n\n[code example]"},{"id":"get-bdbs","title":"Get a database","role":"content","text":"[code example]\n\nGet a single database.\n\n#### Permissions\n\n| Permission name | Roles |\n|-----------------|-------|\n| [view_bdb_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_bdb_info) | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP requests\n\nThe following request returns all fields for the specified database, except for module configuration fields:\n\n[code example]\n\nThe following request returns all fields for the specified database, including configuration fields for modules like search, probabilistic, and timeseries:\n\n[code example]\n\n#### Headers\n\n| Key | Value |\n|-----|-------|\n| Host | The domain name or IP of the cluster |\n| Accept | application/json |\n\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the database requested. |\n\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| fields | string | Comma-separated list of field names to return (by default all fields are returned). (optional) |\n| extended | boolean | If true, includes configuration fields for modules, such as search, timeseries, and probabilistic, in the response. If the `extended` query parameter is not included in the request, module configuration fields do not appear in the response. (optional) |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns a [BDB object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb).\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Database UID does not exist |"},{"id":"put-bdbs","title":"Update database configuration","role":"content","text":"[code example]\nUpdate the configuration of an active database.\n\nIf called with the `dry_run` URL query string, the function will validate the [BDB object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb) against the existing database, but will not invoke the state machine that will update it.\n\nThis is the basic version of the update request. See [Update database and perform action](#put-bdbs-action) to send an update request with an additional action.\n\nTo track this request's progress, poll the [`/actions/<action_uid>` endpoint](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions) with the action_uid returned in the response body."},{"id":"permissions-1","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [update_bdb](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#update_bdb) | admin<br />cluster_member<br />db_member |"},{"id":"put-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value |\n|-----|-------|\n| Host | The domain name or IP of the cluster |\n| Accept | application/json |\n| Content-type | application/json |\n\n#### Query parameters\n\n| Field   | Type | Description |\n|---------|------|---------------|\n| dry_run |      | Validate the new [BDB object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb) but don't apply the update. |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the database for which update is requested. |\n\n#### Body\n\nInclude a [BDB object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb) with updated fields in the request body.\n\n##### Example JSON body\n\n[code example]\n\nThe above request attempts to modify a database configuration to enable in-memory data replication and append-only file data persistence."},{"id":"put-response","title":"Response","role":"returns","text":"Returns the updated [BDB object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb).\n\n#### Example JSON body\n\n[code example]"},{"id":"put-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | The request is accepted and is being processed. The database state will be 'active-change-pending' until the request has been fully processed. |\n| [404&nbsp;Not&nbsp;Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Attempting to change a nonexistent database. |\n| [406&nbsp;Not&nbsp;Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | The requested configuration is invalid. |\n| [409 Conflict](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.10) | Attempting to change a database while it is busy with another configuration change. In this context, this is a temporary condition, and the request should be reattempted later. |\n\n#### Error codes {#put-error-codes}\n\nWhen errors are reported, the server may return a JSON object with    `error_code` and `message` field that provide additional information.    The following are possible `error_code` values:\n\n| Code | Description |\n|------|-------------|\n| rack_awareness_violation | • Non rack-aware cluster.<br />• Not enough nodes in unique racks. |\n| invalid_certificate | SSL client certificate is missing or malformed.|\n| certificate_expired | SSL client certificate has expired. |\n| duplicated_certs | An SSL client certificate appears more than once. |\n| insufficient_resources | Shards count exceeds shards limit per bdb. |\n| not_supported_action_on_crdt | `reset_admin_pass` action is not allowed on CRDT enabled BDB. |\n| name_violation | CRDT database name cannot be changed. |\n| bad_shards_blueprint | The sharding blueprint is broken or doesn’t fit the BDB. |\n| replication_violation | CRDT database must use replication. |\n| eviction_policy_violation | LFU eviction policy is not supported on bdb version<4 |\n| replication_node_violation | Not enough nodes for replication. |\n| replication_size_violation | Database limit too small for replication. |\n| invalid_oss_cluster_configuration | BDB configuration does not meet the requirements for OSS cluster mode |\n| missing_backup_interval | BDB backup is enabled but backup interval is missing. |\n| crdt_sharding_violation | CRDB created without sharding cannot be changed to use sharding\n| invalid_proxy_policy | Invalid proxy_policy value. |\n| invalid_bdb_tags | Tag objects with the same key parameter were passed. |\n| unsupported_module_capabilities | Not all modules configured for the database support the capabilities needed for the database configuration. |\n| redis_acl_unsupported | Redis ACL is not supported for this database. |"},{"id":"put-bdbs-action","title":"Update database and perform action","role":"content","text":"[code example]\nUpdate the configuration of an active database and perform an additional action.\n\nIf called with the `dry_run` URL query string, the function will validate the [BDB object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb) against the existing database, but will not invoke the state machine that will update it.\n\n#### Permissions\n\n| Permission name | Roles |\n|-----------------|-------|\n| [update_bdb_with_action](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#update_bdb_with_action) | admin<br />cluster_member<br />db_member |"},{"id":"put-request-action","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\nThe above request resets the admin password after updating the database.\n\n#### Headers\n\n| Key | Value |\n|-----|-------|\n| Host | The domain name or IP of the cluster |\n| Accept | application/json |\n| Content-type | application/json |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the database to update. |\n| action | string | Additional action to perform. Currently supported actions are: `flush`, `reset_admin_pass`. |\n\n#### Query parameters\n\n| Field   | Type | Description |\n|---------|------|---------------|\n| dry_run |       | Validate the new [BDB object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb) but don't apply the update. |\n\n#### Body\n\nInclude a [BDB object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb) with updated fields in the request body.\n\n##### Example JSON body\n\n[code example]\n\nThe above request attempts to modify a database configuration to enable in-memory data replication and append-only file data persistence.\n\n> [!NOTE]\n> To change the shard hashing policy, you must flush all keys from the database."},{"id":"put-response-action","title":"Response","role":"returns","text":"If the request succeeds, the response body returns the updated [BDB object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb). If an error occurs, the response body may include an error code and message with more details.\n\n#### Status codes {#put-status-codes-action}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | The request is accepted and is being processed. The database state will be 'active-change-pending' until the request has been fully processed. |\n| [403&nbsp;Forbidden](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.4) | redislabs license expired. |\n| [404&nbsp;Not&nbsp;Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Attempting to change a nonexistent database. |\n| [406&nbsp;Not&nbsp;Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | The requested configuration is invalid. |\n| [409 Conflict](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.10) | Attempting to change a database while it is busy with another configuration change. In this context, this is a temporary condition, and the request should be reattempted later. |\n\n#### Error codes {#put-error-codes-action}\n\nWhen errors are reported, the server may return a JSON object with    `error_code` and `message` field that provide additional information.    The following are possible `error_code` values:\n\n| Code | Description |\n|------|-------------|\n| rack_awareness_violation | • Non rack-aware cluster.<br />• Not enough nodes in unique racks. |\n| invalid_certificate | SSL client certificate is missing or malformed.|\n| certificate_expired | SSL client certificate has expired. |\n| duplicated_certs | An SSL client certificate appears more than once. |\n| insufficient_resources | Shards count exceeds shards limit per bdb. |\n| not_supported_action_on_crdt | `reset_admin_pass` action is not allowed on CRDT enabled BDB. |\n| name_violation | CRDT database name cannot be changed. |\n| bad_shards_blueprint | The sharding blueprint is broken or doesn’t fit the BDB. |\n| replication_violation | CRDT database must use replication. |\n| eviction_policy_violation | LFU eviction policy is not supported on bdb version<4 |\n| replication_node_violation | Not enough nodes for replication. |\n| replication_size_violation | Database limit too small for replication. |\n| invalid_oss_cluster_configuration | BDB configuration does not meet the requirements for OSS cluster mode |\n| missing_backup_interval | BDB backup is enabled but backup interval is missing. |\n| crdt_sharding_violation | CRDB created without sharding cannot be changed to use sharding\n| invalid_proxy_policy | Invalid proxy_policy value. |\n| invalid_bdb_tags | Tag objects with the same key parameter were passed. |\n| unsupported_module_capabilities | Not all modules configured for the database support the capabilities needed for the database configuration. |\n| redis_acl_unsupported | Redis ACL is not supported for this database. |"},{"id":"post-bdbs-v1","title":"Create database v1","role":"content","text":"[code example]\nCreate a new database in the cluster.\n\nThe request must contain a single JSON [BDB object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb) with the configuration parameters for the new database.\n\nThe following parameters are required to create the database:\n\n| Parameter | Type/Value | Description |\n|----------|------------|-------------|\n| name     | string     | Name of the new database |\n| memory_size | integer | Size of the database, in bytes |\n\nIf passed with the `dry_run` URL query string, the function will validate the [BDB object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb), but will not invoke the state machine that will create it.\n\nTo track this request's progress, poll the [`/actions/<action_uid>` endpoint](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions) with the `action_uid` returned in the response body.\n\nThe cluster will use default configuration for any missing database field. The cluster creates a database UID if it is missing."},{"id":"permissions-2","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [create_bdb](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#create_bdb) | admin<br />cluster_member<br />db_member |"},{"id":"post-request-v1","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value |\n|-----|-------|\n| Host | The domain name or IP of the cluster |\n| Accept | application/json |\n| Content-type | application/json |\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| dry_run |    | Validate the new [BDB object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb) but don't create the database. |\n\n#### Body\n\nInclude a [BDB object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb) in the request body.\n\nThe following parameters are required to create the database:\n\n| Paramter | Type/Value | Description |\n|----------|------------|-------------|\n| name     | string     | Name of the new database |\n| memory_size | integer | Size of the database, in bytes |\n\nThe `uid` of the database is auto-assigned by the cluster because it was not explicitly listed in this request. If you specify the database ID (`uid`), then you must specify the database ID for every subsequent database and make sure that the database ID does not conflict with an existing database. If you do not specify the database ID, then the it is automatically assigned in sequential order.\n\nDefaults are used for all other configuration parameters.\n\n#### Example JSON body\n\n[code example]\n\nThe above request is an attempt to create a Redis database with a user-specified name and a memory limit of 1GB."},{"id":"post-response-v1","title":"Response","role":"returns","text":"The response includes the newly created [BDB object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb).\n\n#### Example JSON body\n\n[code example]\n\n#### Error codes {#post-error-codes-v1}\n\nWhen errors are reported, the server may return a JSON object with `error_code` and `message` field that provide additional information. The following are possible `error_code` values:\n\n| Code | Description |\n|------|-------------|\n| uid_exists | The specified database UID is already in use. |\n| missing_db_name | DB name is a required property. |\n| missing_memory_size | Memory Size is a required property. |\n| missing_module | Modules missing from the cluster. |\n| port_unavailable | The specified database port is reserved or already in use. |\n| invalid_sharding | Invalid sharding configuration was specified. |\n| bad_shards_blueprint | The sharding blueprint is broken. |\n| not_rack_aware | Cluster is not rack-aware and a rack-aware database was requested. |\n| invalid_version | An invalid database version was requested. |\n| busy | The request failed because another request is being processed at the same time on the same database. |\n| invalid_data_persistence | Invalid data persistence configuration. |\n| invalid_proxy_policy | Invalid proxy_policy value. |\n| invalid_sasl_credentials | SASL credentials are missing or invalid. |\n| invalid_replication | Not enough nodes to perform replication. |\n| insufficient_resources | Not enough resources in cluster to host the database. |\n| rack_awareness_violation | • Rack awareness violation.<br/>• Not enough nodes in unique racks. |\n| invalid_certificate | SSL client certificate is missing or malformed. |\n| certificate_expired | SSL client certificate has expired. |\n| duplicated_certs | An SSL client certificate appears more than once. |\n| replication_violation | CRDT database must use replication. |\n| eviction_policy_violation | LFU eviction policy is not supported on bdb version<4 |\n| invalid_oss_cluster_configuration | BDB configuration does not meet the requirements for OSS cluster mode |\n| memcached_cannot_use_modules | Cannot create a memcached database with modules. |\n| missing_backup_interval | BDB backup is enabled but backup interval is missing. |\n| wrong_cluster_state_id | The given CLUSTER-STATE-ID does not match the current one\n| invalid_bdb_tags | Tag objects with the same key parameter were passed. |\n| unsupported_module_capabilities | Not all modules configured for the database support the capabilities needed for the database configuration. |\n| redis_acl_unsupported | Redis ACL is not supported for this database. |\n\n#### Status codes {#post-status-codes-v1}\n\n| Code | Description |\n|------|-------------|\n| [403 Forbidden](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.4) | redislabs license expired. |\n| [409 Conflict](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.10) | Database with the same UID already exists. |\n| [406 Not Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | Invalid configuration parameters provided. |\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success, database is being created. |"},{"id":"post-bdbs-v2","title":"Create database v2","role":"content","text":"[code example]\nCreate a new database in the cluster. See [`POST /v1/bdbs`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#post-bdbs-v1) for more information.\n\nThe database's configuration should be under the \"bdb\" field.\n\nThis endpoint allows you to specify a recovery_plan to recover a database. If you include a recovery_plan within the request body, the database will be loaded from the persistence files according to the recovery plan. The recovery plan must match the number of shards requested for the database.\n\nThe persistence files must exist in the locations specified by the recovery plan. The persistence files must belong to a database with the same shard settings as the one being created (slot range distribution and shard_key_regex); otherwise, the operation will fail or yield unpredictable results.\n\nIf you create a database with a shards_blueprint and a recovery plan, the shard placement may not fully follow the shards_blueprint."},{"id":"post-request-v2","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value |\n|-----|-------|\n| Host | The domain name or IP of the cluster |\n| Accept | application/json |\n| Content-type | application/json |\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| dry_run |    | Validate the new [BDB object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb) but don't create the database. |\n\n#### Body\n\nInclude a JSON object that contains a [BDB object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb) and an optional `recovery_plan` object in the request body.\n\n##### Example JSON body\n\n[code example]"},{"id":"post-response-v2","title":"Response","role":"returns","text":"The response includes the newly created [BDB object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb).\n\n#### Example JSON body\n\n[code example]"},{"id":"delete-bdbs","title":"Delete database","role":"content","text":"[code example]\nDelete an active database."},{"id":"permissions-3","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [delete_bdb](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#delete_bdb) | admin<br />cluster_member<br />db_member |"},{"id":"delete-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n#### Headers\n\n| Key | Value |\n|-----|-------|\n| Host | The domain name or IP of the cluster |\n| Accept | application/json |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the database to delete. |"},{"id":"delete-response","title":"Response","role":"returns","text":"Returns a status code that indicates the database deletion success or failure."},{"id":"delete-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | The request is accepted and is being processed. The database state will be 'delete-pending' until the request has been fully processed. |\n| [403 Forbidden](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.4) | Attempting to delete an internal database. |\n| [404&nbsp;Not&nbsp;Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Attempting to delete a nonexistent database. |\n| [409 Conflict](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.10) | Either the database is not in 'active' state and cannot be deleted, or it is busy with another configuration change. In the second case, this is a temporary condition, and the request should be re-attempted later. |"}],"examples":[{"id":"get-all-bdbs-ex0","language":"sh","code":"GET /v1/bdbs","section_id":"get-all-bdbs"},{"id":"get-all-request-ex0","language":"sh","code":"GET /v1/bdbs?fields=uid,name","section_id":"get-all-request"},{"id":"get-all-request-ex1","language":"sh","code":"GET /v1/bdbs?extended=true","section_id":"get-all-request"},{"id":"get-all-response-ex0","language":"json","code":"[\n    {\n        \"uid\": 1,\n        \"name\": \"name of database #1\",\n        \"// additional fields...\"\n    },\n    {\n        \"uid\": 2,\n        \"name\": \"name of database #2\",\n        \"// additional fields...\"\n    }\n]","section_id":"get-all-response"},{"id":"example-requests-ex0","language":"sh","code":"$ curl -k -X GET -u \"[username]:[password]\" \\\n       -H \"accept: application/json\" \\\n       https://[host][:port]/v1/bdbs?fields=uid,name","section_id":"example-requests"},{"id":"example-requests-ex1","language":"python","code":"import requests\nimport json\n\nurl = \"https://[host][:port]/v1/bdbs?fields=uid,name\"\nauth = (\"[username]\", \"[password]\")\n\nheaders = {\n  'Content-Type': 'application/json'\n}\n\nresponse = requests.request(\"GET\", url, auth=auth, headers=headers)\n\nprint(response.text)","section_id":"example-requests"},{"id":"get-bdbs-ex0","language":"sh","code":"GET /v1/bdbs/{int: uid}","section_id":"get-bdbs"},{"id":"get-request-ex0","language":"sh","code":"GET /v1/bdbs/1","section_id":"get-request"},{"id":"get-request-ex1","language":"sh","code":"GET /v1/bdbs/1?extended=true","section_id":"get-request"},{"id":"get-response-ex0","language":"json","code":"{\n    \"uid\": 1,\n    \"name\": \"name of database #1\",\n    \"// additional fields...\"\n}","section_id":"get-response"},{"id":"put-bdbs-ex0","language":"sh","code":"PUT /v1/bdbs/{int: uid}","section_id":"put-bdbs"},{"id":"put-request-ex0","language":"sh","code":"PUT /v1/bdbs/1","section_id":"put-request"},{"id":"put-request-ex1","language":"json","code":"{\n    \"replication\": true,\n    \"data_persistence\": \"aof\"\n}","section_id":"put-request"},{"id":"put-response-ex0","language":"json","code":"{\n    \"uid\": 1,\n    \"replication\": true,\n    \"data_persistence\": \"aof\",\n    \"// additional fields...\"\n}","section_id":"put-response"},{"id":"put-bdbs-action-ex0","language":"sh","code":"PUT /v1/bdbs/{int: uid}/{action}","section_id":"put-bdbs-action"},{"id":"put-request-action-ex0","language":"sh","code":"PUT /v1/bdbs/1/reset_admin_pass","section_id":"put-request-action"},{"id":"put-request-action-ex1","language":"json","code":"{\n    \"replication\": true,\n    \"data_persistence\": \"aof\"\n}","section_id":"put-request-action"},{"id":"post-bdbs-v1-ex0","language":"sh","code":"POST /v1/bdbs","section_id":"post-bdbs-v1"},{"id":"post-request-v1-ex0","language":"sh","code":"POST /v1/bdbs","section_id":"post-request-v1"},{"id":"post-request-v1-ex1","language":"json","code":"{\n    \"name\": \"test-database\",\n    \"type\": \"redis\",\n    \"memory_size\": 1073741824\n}","section_id":"post-request-v1"},{"id":"post-response-v1-ex0","language":"json","code":"{\n    \"uid\": 1,\n    \"name\": \"test-database\",\n    \"type\": \"redis\",\n    \"memory_size\": 1073741824,\n    \"// additional fields...\"\n}","section_id":"post-response-v1"},{"id":"post-bdbs-v2-ex0","language":"sh","code":"POST /v2/bdbs","section_id":"post-bdbs-v2"},{"id":"post-request-v2-ex0","language":"sh","code":"POST /v2/bdbs","section_id":"post-request-v2"},{"id":"post-request-v2-ex1","language":"json","code":"{\n    \"bdb\": {\n        \"name\": \"test-database\",\n        \"type\": \"redis\",\n        \"memory_size\": 1073741824,\n        \"shards_count\": 1\n    },\n    \"recovery_plan\": {\n        \"original_bdb_shards\": [\n            {\n                \"assigned_slots\": \"0-16383\",\n                \"node_uid\": \"1\", \n                \"role\": \"master\", \n                \"uid\": \"1\"\n            }\n        ]\n    }\n}","section_id":"post-request-v2"},{"id":"post-response-v2-ex0","language":"json","code":"{\n    \"uid\": 1,\n    \"name\": \"test-database\",\n    \"type\": \"redis\",\n    \"memory_size\": 1073741824,\n    \"shards_count\": 1,\n    \"// additional fields...\"\n}","section_id":"post-response-v2"},{"id":"delete-bdbs-ex0","language":"sh","code":"DELETE /v1/bdbs/{int: uid}","section_id":"delete-bdbs"},{"id":"delete-request-ex0","language":"sh","code":"DELETE /v1/bdbs/1","section_id":"delete-request"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/bdbs/modules/config","title":"Database modules config requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/modules/config/","summary":"Configure Redis module requests","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"507ba8a5e77b2b8a3ad75bdebe3218684299b5e3750d4e853f11c8f519a9bd2f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This REST API path is deprecated as of Redis Software version 7.4. Use [`PUT /v2/bdbs/<uid>`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#put-bdbs-action) instead.\n\n\n| Method | Path | Description |\n|--------|------|-------------|\n| [POST](#post-bdb-modules-config) | `/v1/bdbs/{uid}/modules/config` | Configure module |"},{"id":"post-bdb-modules-config","title":"Configure module","role":"configuration","text":"POST /v1/bdbs/{string: uid}/modules/config\n\nUse the module runtime configuration command (if defined) to configure new arguments for the module.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [edit_bdb_module](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#edit_bdb_module) |"},{"id":"post-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tPOST /v1/bdbs/1/modules/config\n\n#### Example JSON body\n\n[code example]\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n\n#### Request body\n\n| Field | Type | Description |\n|-------|------|-------------|\n| modules | list of JSON objects | List of modules (module_name) and their new configuration settings (module_args) |\n| module_name | `search`<br />`ReJSON`<br />`graph`<br />`timeseries`<br />`bf` | Module's name |\n| module_args | string | Module command line arguments (pattern does not allow special characters &,<,>,\") |"},{"id":"post-response","title":"Response","role":"returns","text":"Returns a status code. If an error occurs, the response body may include an error code and message with more details."},{"id":"post-error-codes","title":"Error codes","role":"errors","text":"When errors are reported, the server may return a JSON object with `error_code` and `message` field that provide additional information. The following are possible `error_code` values:\n\n| Code | Description |\n|------|-------------|\n| db_not_exist | Database with given UID doesn't exist in cluster | \n| missing_field | \"module_name\" or \"module_args\" are not defined in request | \n| invalid_schema | JSON object received is not a dict object | \n| param_error | \"module_args\" parameter was not parsed properly | \n| module_not_exist | Module with given \"module_name\" does not exist for the database |"},{"id":"post-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success, module updated on bdb. |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | bdb not found. |\n| [400 Bad Request](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1) | Bad or missing configuration parameters. |\n| [406 Not Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | Module does not support runtime configuration of arguments. |"}],"examples":[{"id":"post-request-ex0","language":"json","code":"{\n  \"modules\": [\n    {\n      \"module_name\": \"search\",\n      \"module_args\": \"MINPREFIX 3 MAXEXPANSIONS 1000\"\n    }\n  ]\n}","section_id":"post-request"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/bdbs/modules","title":"Database modules requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/modules/","summary":"Redis module requests","tags":["docs","operate","rs"],"last_updated":"2024-02-26T13:11:00+01:00","children":[{"id":"operate/rs/references/rest-api/requests/bdbs/modules/config","summary":"Configure Redis module requests","title":"Database modules config requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/modules/config/"},{"id":"operate/rs/references/rest-api/requests/bdbs/modules/upgrade","summary":"Upgrade Redis module requests","title":"Database upgrade modules requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/modules/upgrade/"}],"page_type":"content","content_hash":"277b70406a386b9d74d31544e807c0b59790f697db87b52a9ebfe9a207a77adc","sections":[{"id":"configure-module","title":"Configure module","role":"configuration","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [POST](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/modules/config#post-bdb-modules-config) | `/v1/bdbs/{uid}/modules/config` | Configure module |"},{"id":"upgrade-module","title":"Upgrade module","role":"content","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [POST](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/modules/upgrade#post-bdb-modules-upgrade) | `/v1/bdbs/{uid}/modules/upgrade` | Upgrade module |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/bdbs/modules/upgrade","title":"Database upgrade modules requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/modules/upgrade/","summary":"Upgrade Redis module requests","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"71a3c7e2635a04664b11e3ae6ab6f75a04cbe972974d57cc96be80d0333e9f4e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [POST](#post-bdb-modules-upgrade) | `/v1/bdbs/{uid}/modules/upgrade` | Upgrade module |"},{"id":"post-bdb-modules-upgrade","title":"Upgrade module","role":"content","text":"POST /v1/bdbs/{string: uid}/modules/upgrade\n\nUpgrades module version on a specific BDB.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [edit_bdb_module](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#edit_bdb_module) |"},{"id":"post-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tPOST /v1/bdbs/1/modules/upgrade\n\n#### Example JSON body\n\n[code example]\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n\n#### Request body\n\n| Field | Type | Description |\n|-------|------|-------------|\n| modules | list | List of dicts representing the modules that will be upgraded. Each dict must include: <br></br> • **current_module**: uid of a module to upgrade <br></br> • **new_module**: UID of the module we want to upgrade to <br></br> • **new_module_args**: args list for the new module |\n| preserve_roles | boolean | Preserve shards’ master/replica roles (optional) |\n| may_discard_data | boolean | Discard data in a non-replicated non-persistent bdb (optional) |"},{"id":"post-response","title":"Response","role":"returns","text":"Returns the upgraded [module object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/module).\n\n#### Example JSON body\n\n[code example]"},{"id":"post-error-codes","title":"Error codes","role":"errors","text":"When errors are reported, the server may return a JSON object with    `error_code` and `message` field that provide additional information.    The following are possible `error_code` values:\n\n| Code | Description |\n|------|-------------|\n| missing_module | Module is not present in cluster.| \n| module_downgrade_unsupported | Module downgrade is not allowed.| \n| redis_incompatible_version | Module min_redis_version is bigger than the current Redis version.| \n| redis_pack_incompatible_version | Module min_redis_pack_version is bigger than the current Redis Software version.| \n| unsupported_module_capabilities | New version of module does support all the capabilities needed for the database configuration|"},{"id":"post-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success, module updated on bdb. |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | bdb or node not found. |\n| [400 Bad Request](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1) | Bad or missing configuration parameters. |\n| [406 Not Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | The requested configuration is invalid. |"}],"examples":[{"id":"post-request-ex0","language":"json","code":"{\n     \"modules\": [\n         {\"module_name\": \"ReJson\",\n         \"current_semantic_version\": \"2.2.1\",\n         \"new_module\": \"aa3648d79bd4082d414587c42ea0b234\"}\n     ],\n     \"// Optional fields to fine-tune restart and failover behavior:\",\n     \"preserve_roles\": true,\n     \"may_discard_data\": false\n}","section_id":"post-request"},{"id":"post-response-ex0","language":"json","code":"{\n    \"uid\": 1,\n    \"name\": \"name of database #1\",\n    \"module_id\": \"aa3648d79bd4082d414587c42ea0b234\",\n    \"module_name\": \"ReJson\",\n    \"semantic_version\": \"2.2.2\"\n    \"// additional fields...\"\n}","section_id":"post-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/bdbs/passwords","title":"Database passwords requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/passwords/","summary":"Database password requests","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","page_type":"content","content_hash":"d60c0d78cc61c0e03d10f72d52e40bf9b9ea4db80ea1621463c58a82d87da5e1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [PUT](#put-bdbs-password) | `/v1/bdbs/{uid}/passwords` | Update database password |\n| [POST](#post-bdbs-password) | `/v1/bdbs/{uid}/passwords` | Add database password |\n| [DELETE](#delete-bdbs-password) | `/v1/bdbs/{uid}/passwords` | Delete database password |"},{"id":"put-bdbs-password","title":"Update database password","role":"content","text":"PUT /v1/bdbs/{int: uid}/passwords\n\nSet a single password for the bdb's default user (i.e., for `AUTH`&nbsp;`<password>` authentications).\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [update_bdb](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#update_bdb) |"},{"id":"put-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tPUT /v1/bdbs/1/passwords \n\n#### Example JSON body\n\n[code example]\n\nThe above request resets the password of the bdb to ‘new password’.\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the database to update the password. |\n\n\n#### Request body\n\n| Field | Type | Description |\n|-------|------|-------------|\n| password | string | New password |"},{"id":"put-response","title":"Response","role":"returns","text":"Returns a status code that indicates password update success or failure."},{"id":"put-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | The password was changed. |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | A nonexistent database. |\n| [406 Not Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | Invalid configuration parameters provided. |"},{"id":"post-bdbs-password","title":"Add database password","role":"content","text":"POST /v1/bdbs/{int: uid}/passwords\n\nAdd a password to the bdb's default user (i.e., for `AUTH`&nbsp;`<password>` authentications).\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [update_bdb](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#update_bdb) |"},{"id":"post-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tPOST /v1/bdbs/1/passwords \n\n#### Example JSON body\n\n[code example]\n\nThe above request adds a password to the bdb.\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the database to add password. |\n\n#### Request body\n\n| Field | Type | Description |\n|-------|------|-------------|\n| password | string | Password to add |"},{"id":"post-response","title":"Response","role":"returns","text":"Returns a status code that indicates password creation success or failure."},{"id":"post-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | The password was added. |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | A nonexistent database. |\n| [406 Not Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | Invalid configuration parameters provided. |"},{"id":"delete-bdbs-password","title":"Delete database password","role":"content","text":"DELETE /v1/bdbs/{int: uid}/passwords\n\nDelete a password from the bdb's default user (i.e., for `AUTH`&nbsp;`<password>` authentications).\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [update_bdb](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#update_bdb) |"},{"id":"delete-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tDELETE /v1/bdbs/1/passwords \n\n#### Example JSON body\n\n[code example]\n\nThe above request deletes a password from the bdb.\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the database to delete password. |\n\n#### Request body\n\n| Field | Type | Description |\n|-------|------|-------------|\n| password | string | Password to delete |"},{"id":"delete-response","title":"Response","role":"returns","text":"Returns a status code that indicates password deletion success or failure."},{"id":"delete-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | The password was deleted. |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | A nonexistent database. |\n| [406 Not Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | Invalid configuration parameters provided. |"}],"examples":[{"id":"put-request-ex0","language":"json","code":"{\n    \"password\": \"new password\"\n}","section_id":"put-request"},{"id":"post-request-ex0","language":"json","code":"{\n    \"password\": \"password to add\"\n}","section_id":"post-request"},{"id":"delete-request-ex0","language":"json","code":"{\n    \"password\": \"password to delete\"\n}","section_id":"delete-request"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/bdbs/peer_stats","title":"CRDB peer stats requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/peer_stats/","summary":"Active-Active peer instance statistics requests","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","page_type":"content","content_hash":"c7536371b982fe68b0d700f659f792fe2a0807c75942084f0884e6200e17a78a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-all-bdbs-peer_stats) | `/v1/bdbs/{bdb_uid}/peer_stats` | Get stats for all CRDB peer instances |\n| [GET](#get-bdbs-peer_stats) | `/v1/bdbs/{bdb_uid}/peer_stats/{uid}` | Get stats for a specific CRDB peer instance |"},{"id":"get-all-bdbs-peer_stats","title":"Get all CRDB peer stats","role":"content","text":"[code example]\n\nGet statistics for all peer instances of a local CRDB instance."},{"id":"permissions","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [view_bdb_stats](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_bdb_stats) | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |"},{"id":"get-all-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| bdb_uid | integer | The unique ID of the local CRDB instance. |\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| interval | string | Time interval for which we want stats: 1sec/10sec/5min/15min/1hour/12hour/1week (optional) |\n| stime | ISO_8601 | Start time from which we want the stats. Should comply with the [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601) format (optional) |\n| etime | ISO_8601 | End time after which we don't want the stats. Should comply with the [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601) format (optional) |"},{"id":"get-all-response","title":"Response","role":"returns","text":"Returns [statistics](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/statistics) for all CRDB peer instances.\n\n#### Example JSON body\n\n[code example]\n\n#### Status codes {#get-all-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Database does not exist. |\n| [406 Not Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | Database is not a CRDB. |"},{"id":"get-bdbs-peer_stats","title":"Get CRDB peer stats","role":"content","text":"[code example]\n\nGet statistics for a specific CRDB peer instance."},{"id":"permissions-1","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [view_bdb_stats](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_bdb_stats) | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| bdb_uid | integer | The unique ID of the local CRDB instance. |\n| uid | integer | The peer instance uid, as specified in the CRDB instance list. |\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| interval | string | Time interval for which we want stats: 1sec/10sec/5min/15min/1hour/12hour/1week (optional) |\n| stime | ISO_8601 | Start time from which we want the stats. Should comply with the [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601) format (optional) |\n| etime | ISO_8601 | End time after which we don't want the stats. Should comply with the [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601) format (optional) |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns [statistics](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/statistics) for a specific CRDB peer instance.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Database or peer does not exist. |\n| [406 Not Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | Database is not a CRDB. |"}],"examples":[{"id":"get-all-bdbs-peer_stats-ex0","language":"sh","code":"GET /v1/bdbs/{bdb_uid}/peer_stats","section_id":"get-all-bdbs-peer_stats"},{"id":"get-all-request-ex0","language":"sh","code":"GET /v1/bdbs/1/peer_stats?interval=5min","section_id":"get-all-request"},{"id":"get-all-response-ex0","language":"json","code":"{ \"peer_stats\": [\n     {\n         \"intervals\": [\n             {\n                 \"egress_bytes\": 0.0,\n                 \"egress_bytes_decompressed\": 0.0,\n                 \"etime\": \"2017-10-22T19:30:00Z\",\n                 \"ingress_bytes\": 18528,\n                 \"ingress_bytes_decompressed\": 185992,\n                 \"interval\": \"5min\",\n                 \"local_ingress_lag_time\": 0.244,\n                 \"pending_local_writes_max\": 0.0,\n                 \"pending_local_writes_min\": 0.0,\n                 \"stime\": \"2017-10-22T19:25:00Z\"\n             },\n             {\n                 \"egress_bytes\": 0.0,\n                 \"egress_bytes_decompressed\": 0.0,\n                 \"etime\": \"2017-10-22T19:35:00Z\",\n                 \"ingress_bytes\": 18,\n                 \"ingress_bytes_decompressed\": 192,\n                 \"interval\": \"5min\",\n                 \"local_ingress_lag_time\": 0.0,\n                 \"pending_local_writes_max\": 0.0,\n                 \"pending_local_writes_min\": 0.0,\n                 \"stime\": \"2017-10-22T19:30:00Z\"\n             }\n         ],\n         \"uid\": \"3\"\n     }\n   ]\n }","section_id":"get-all-response"},{"id":"get-bdbs-peer_stats-ex0","language":"sh","code":"GET /v1/bdbs/{bdb_uid}/peer_stats/{int: uid}","section_id":"get-bdbs-peer_stats"},{"id":"get-request-ex0","language":"sh","code":"GET /v1/bdbs/1/peer_stats/3?interval=5min","section_id":"get-request"},{"id":"get-response-ex0","language":"json","code":"{\n    \"intervals\": [\n        {\n            \"egress_bytes\": 0.0,\n            \"egress_bytes_decompressed\": 0.0,\n            \"etime\": \"2017-10-22T19:30:00Z\",\n            \"ingress_bytes\": 18528,\n            \"ingress_bytes_decompressed\": 185992,\n            \"interval\": \"5min\",\n            \"local_ingress_lag_time\": 0.244,\n            \"pending_local_writes_max\": 0.0,\n            \"pending_local_writes_min\": 0.0,\n            \"stime\": \"2017-10-22T19:25:00Z\"\n        },\n        {\n            \"egress_bytes\": 0.0,\n            \"egress_bytes_decompressed\": 0.0,\n            \"etime\": \"2017-10-22T19:35:00Z\",\n            \"ingress_bytes\": 18,\n            \"ingress_bytes_decompressed\": 192,\n            \"interval\": \"5min\",\n            \"local_ingress_lag_time\": 0.0,\n            \"pending_local_writes_max\": 0.0,\n            \"pending_local_writes_min\": 0.0,\n            \"stime\": \"2017-10-22T19:30:00Z\"\n        }\n    ],\n    \"uid\": \"3\"\n}","section_id":"get-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/bdbs/replica_sources-alerts","title":"Database replica sources alerts requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/replica_sources-alerts/","summary":"Replica source alert requests","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","page_type":"content","content_hash":"c461d85fa049a64bd09cf4799ee24af387dfc1ed698a262905c39f27caed8536","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-all-bdbs-replica-sources-alerts) | `/v1/bdbs/replica_sources/alerts` | Get all replica sources alert states for all BDBs |\n| [GET](#get-bdbs-replica-sources-alerts) | `/v1/bdbs/replica_sources/alerts/{uid}` | Get all replica sources alert states for a BDB |\n| [GET](#get-bdbs-replica_source-all-alerts) | `/v1/bdbs/replica_sources/alerts/{uid}/{replica_src_id}` | Get all alert states for a replica source |\n| [GET](#get-bdbs-replica-source-alert) | `/v1/bdbs/replica_sources/alerts/{uid}/{replica_src_id}/{alert}` | Get a replica source alert state |"},{"id":"get-all-bdbs-replica-sources-alerts","title":"Get all DBs replica sources alert states","role":"content","text":"GET /v1/bdbs/replica_sources/alerts\n\nGet all alert states for all replica sources of all BDBs.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_all_bdbs_alerts](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_all_bdbs_alerts) |"},{"id":"get-all-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/bdbs/replica_sources/alerts \n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |"},{"id":"get-all-response","title":"Response","role":"returns","text":"Returns a hash of alert UIDs and the alerts states for each BDB.\n\nSee [REST API alerts overview](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/alert) for a description of the alert state object.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-all-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |"},{"id":"get-bdbs-replica-sources-alerts","title":"Get DB replica source alert states","role":"content","text":"GET /v1/bdbs/replica_sources/alerts/{int: uid}\n\nGet all alert states for all replica sources of a specific bdb.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_bdb_alerts](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_bdb_alerts) |"},{"id":"get-request-all-replica-alerts","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/bdbs/replica_sources/alerts/1 \n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the database |"},{"id":"get-response-all-replica-alerts","title":"Response","role":"returns","text":"Returns a hash of [alert objects](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/alert) and their states.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes-all-replica-alerts","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Specified bdb does not exist |"},{"id":"get-bdbs-replica_source-all-alerts","title":"Get replica source alert states","role":"content","text":"GET /v1/bdbs/replica_sources/alerts/{int: uid}/{int: replica_src_id}\n\nGet all alert states for a specific replica source of a bdb.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_bdb_alerts](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_bdb_alerts) |"},{"id":"get-request-replica-alerts","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/bdbs/replica_sources/alerts/1/2 \n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the database |\n| replica_src_id | integer | The ID of the replica source in this BDB |"},{"id":"get-response-replica-alerts","title":"Response","role":"returns","text":"Returns a hash of [alert objects](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/alert) and their states.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes-replica-alerts","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Specified bdb does not exist |"},{"id":"get-bdbs-replica-source-alert","title":"Get replica source alert state","role":"content","text":"GET /v1/bdbs/replica_sources/alerts/{int: uid}/{int: replica_src_id}/{alert}\n\nGet a replica source alert state of a specific bdb.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_bdb_alerts](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_bdb_alerts) |"},{"id":"get-request-alert","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/bdbs/replica_sources/alerts/1/2/replica_src_syncer_connection_error \n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the database |\n| replica_src_id | integer | The ID of the replica source in this BDB |\n| alert | string | The alert name |"},{"id":"get-response-alert","title":"Response","role":"returns","text":"Returns an [alert state object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/alert).\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes-alert","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |\n| [400 Bad Request](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1) | Bad request |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Specified alert or bdb does not exist |"}],"examples":[{"id":"get-all-response-ex0","language":"json","code":"{\n    \"1\": {\n        \"replica_src_syncer_connection_error\": {\n            \"enabled\": true,\n            \"state\": true,\n            \"threshold\": \"80\",\n            \"change_time\": \"2014-08-29T11:19:49Z\",\n            \"severity\": \"WARNING\",\n            \"change_value\": {\n                \"state\": true,\n                \"threshold\": \"80\",\n                \"memory_util\": 81.2\n            }\n        },\n        \"...\"\n    },\n    \"...\"\n}","section_id":"get-all-response"},{"id":"get-response-all-replica-alerts-ex0","language":"json","code":"{\n    \"replica_src_syncer_connection_error\": {\n        \"enabled\": true,\n        \"state\": true,\n        \"threshold\": \"80\",\n        \"severity\": \"WARNING\",\n        \"change_time\": \"2014-08-29T11:19:49Z\",\n        \"change_value\": {\n            \"state\": true,\n            \"threshold\": \"80\",\n            \"memory_util\": 81.2\n        }\n    },\n    \"...\"\n}","section_id":"get-response-all-replica-alerts"},{"id":"get-response-replica-alerts-ex0","language":"json","code":"{\n    \"replica_src_syncer_connection_error\": {\n        \"enabled\": true,\n        \"state\": true,\n        \"threshold\": \"80\",\n        \"severity\": \"WARNING\",\n        \"change_time\": \"2014-08-29T11:19:49Z\",\n        \"change_value\": {\n            \"state\": true,\n            \"threshold\": \"80\",\n            \"memory_util\": 81.2\n        }\n    },\n    \"...\"\n}","section_id":"get-response-replica-alerts"},{"id":"get-response-alert-ex0","language":"json","code":"{\n    \"enabled\": true,\n    \"state\": true,\n    \"threshold\": \"80\",\n    \"severity\": \"WARNING\",\n    \"change_time\": \"2014-08-29T11:19:49Z\",\n    \"change_value\": {\n        \"state\": true,\n        \"threshold\": \"80\",\n        \"memory_util\": 81.2\n    }\n}","section_id":"get-response-alert"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/bdbs/shards","title":"Database shards requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/shards/","summary":"REST API requests for database shards","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","page_type":"content","content_hash":"b50b78d7a3dfceaa8546ae1c55300ba7387f36ba7816162e50c05c24cbd20498","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-bdb-shards) | `/v1/bdbs/{bdb_uid}/shards` | Get the status of a database's shards |"},{"id":"get-bdb-shards","title":"Get database shards","role":"content","text":"GET /v1/bdbs/{int: bdb_uid}/shards\n\nGets the status for all shards that belong to the specified database."},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/bdbs/1/shards?extra_info_keys=used_memory_rss&extra_info_keys=connected_clients\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| bdb_uid | integer | The unique ID of the database. |"},{"id":"get-response","title":"Response","role":"returns","text":"The response body contains a JSON array with all shards, represented as [shard objects](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/shard).\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | No error. |\n| [404 Not Found](https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found) | bdb uid does not exist. |"}],"examples":[{"id":"get-response-ex0","language":"json","code":"[\n    {\n        \"uid\": \"1\",\n\t\t\"role\": \"master\",\n\t\t\"assigned_slots\": \"0-8191\",\n        \"bdb_uid\": 1,\n        \"detailed_status\": \"ok\",\n        \"loading\": {\n            \"status\": \"idle\"\n        },\n        \"node_uid\": \"1\",\n        \"redis_info\": {\n\t\t\t\"connected_clients\": 14,\n            \"used_memory_rss\": 12460032\n        },\n        \"report_timestamp\": \"2024-09-13T15:28:10Z\",\n        \"status\": \"active\"\n    },\n    {\n        \"uid\": 2,\n        \"role\": \"slave\",\n        // additional fields...\n    }\n]","section_id":"get-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/bdbs/stats","title":"Database stats requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/stats/","summary":"Database statistics requests","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","children":[{"id":"operate/rs/references/rest-api/requests/bdbs/stats/last","summary":"Most recent database statistics requests","title":"Latest database stats requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/stats/last/"}],"page_type":"content","content_hash":"9eab4f6774e68dbb5cb125dd3bb9c128287a53e41f7174367d2e9e5433e38271","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The v1 statistics REST API endpoints are deprecated as of Redis Software version 7.22. Use the [metrics stream engine for monitoring v2](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine) instead for real-time monitoring and metrics collection.\n\n\n| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-all-bdbs-stats) | `/v1/bdbs/stats` | Get stats for all databases |\n| [GET](#get-bdbs-stats) | `/v1/bdbs/stats/{uid}` | Get stats for a specific database |"},{"id":"get-all-bdbs-stats","title":"Get all database stats","role":"content","text":"[code example]\n\nGet statistics for all databases."},{"id":"permissions","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [view_all_bdb_stats](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_all_bdb_stats) | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |"},{"id":"get-all-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| interval | string | Time interval for for which we want stats: 1sec/10sec/5min/15min/1hour/12hour/1week (optional) |\n| stime | ISO_8601 | Start time from which we want the stats. Should comply with the [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601) format (optional) |\n| etime | ISO_8601 | End time after which we don't want the stats. Should comply with the [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601) format (optional) |"},{"id":"get-all-response","title":"Response","role":"returns","text":"Returns [statistics](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/statistics) for all databases.\n\n#### Example JSON body\n\n[code example]\n\n#### Status codes {#get-all-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | No bdbs exist |"},{"id":"example-requests","title":"Example requests","role":"example","text":"#### cURL\n\n[code example]\n\n#### Python\n\n[code example]"},{"id":"get-bdbs-stats","title":"Get database stats","role":"content","text":"[code example]\n\nGet statistics for a specific database."},{"id":"permissions-1","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [view_bdb_stats](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_bdb_stats) | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the BDB requested. |\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| interval | string | Time interval for for which we want stats: 1sec/10sec/5min/15min/1hour/12hour/1week (optional) |\n| stime | ISO_8601 | Start time from which we want the stats. Should comply with the [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601) format (optional) |\n| etime | ISO_8601 | End time after which we don't want the stats. Should comply with the [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601) format (optional) |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns [statistics](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/statistics) for a specific database.\n\n#### Example JSON body\n\n[code example]\n\n#### Status codes {#get-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | bdb does not exist |\n| [406 Not Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | bdb isn't currently active |\n| [503 Service Unavailable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.4) | bdb is in recovery state |"}],"examples":[{"id":"get-all-bdbs-stats-ex0","language":"sh","code":"GET /v1/bdbs/stats","section_id":"get-all-bdbs-stats"},{"id":"get-all-request-ex0","language":"sh","code":"GET /v1/bdbs/stats?interval=1hour&stime=2014-08-28T10:00:00Z","section_id":"get-all-request"},{"id":"get-all-response-ex0","language":"json","code":"[\n  {\n    \"uid\": \"1\",\n    \"intervals\": [\n      {\n         \"interval\": \"1hour\",\n         \"stime\": \"2015-05-27T12:00:00Z\",\n         \"etime\": \"2015-05-28T12:59:59Z\",\n         \"avg_latency\": 0.0,\n         \"conns\": 0.0,\n         \"egress_bytes\": 0.0,\n         \"etime\": \"2015-05-28T00:00:00Z\",\n         \"evicted_objects\": 0.0,\n         \"expired_objects\": 0.0,\n         \"ingress_bytes\": 0.0,\n         \"instantaneous_ops_per_sec\": 0.00011973180076628352,\n         \"last_req_time\": \"1970-01-01T00:00:00Z\",\n         \"last_res_time\": \"1970-01-01T00:00:00Z\",\n         \"used_memory\": 5656299.362068966,\n         \"mem_size_lua\": 35840.0,\n         \"monitor_sessions_count\": 0.0,\n         \"no_of_keys\": 0.0,\n         \"other_req\": 0.0,\n         \"other_res\": 0.0,\n         \"read_hits\": 0.0,\n         \"read_misses\": 0.0,\n         \"read_req\": 0.0,\n         \"read_res\": 0.0,\n         \"total_connections_received\": 0.0,\n         \"total_req\": 0.0,\n         \"total_res\": 0.0,\n         \"write_hits\": 0.0,\n         \"write_misses\": 0.0,\n         \"write_req\": 0.0,\n         \"write_res\": 0.0\n      },\n      {\n         \"interval\": \"1hour\",\n         \"interval\": \"1hour\",\n         \"stime\": \"2015-05-27T13:00:00Z\",\n         \"etime\": \"2015-05-28T13:59:59Z\",\n         \"avg_latency\": 599.08,\n         \"// additional fields...\"\n      }\n    ]\n  },\n  {\n    \"uid\": \"2\",\n    \"intervals\": [\n      {\n        \"interval\": \"1hour\",\n        \"stime\": \"2015-05-27T12:00:00Z\",\n        \"etime\": \"2015-05-28T12:59:59Z\",\n        \"avg_latency\": 0.0,\n        \"// additional fields...\"\n      },\n      {\n        \"interval\": \"1hour\",\n        \"stime\": \"2015-05-27T13:00:00Z\",\n        \"etime\": \"2015-05-28T13:59:59Z\",\n\n        \"// additional fields...\"\n      }\n    ]\n  }\n]","section_id":"get-all-response"},{"id":"example-requests-ex0","language":"sh","code":"$ curl -k -u \"[username]:[password]\" -X GET\n        https://[host][:port]/v1/bdbs/stats?interval=1hour","section_id":"example-requests"},{"id":"example-requests-ex1","language":"python","code":"import requests\n\nurl = \"https://[host][:port]/v1/bdbs/stats?interval=1hour\"\nauth = (\"[username]\", \"[password]\")\n\nresponse = requests.request(\"GET\", url, auth=auth)\n\nprint(response.text)","section_id":"example-requests"},{"id":"get-bdbs-stats-ex0","language":"sh","code":"GET /v1/bdbs/stats/{int: uid}","section_id":"get-bdbs-stats"},{"id":"get-request-ex0","language":"sh","code":"GET /v1/bdbs/stats/1?interval=1hour&stime=2014-08-28T10:00:00Z","section_id":"get-request"},{"id":"get-response-ex0","language":"json","code":"{\n  \"uid\": \"1\",\n  \"intervals\": [\n    {\n      \"interval\": \"1hour\",\n      \"stime\": \"2015-05-27T12:00:00Z\",\n      \"etime\": \"2015-05-28T12:59:59Z\",\n      \"avg_latency\": 0.0,\n      \"conns\": 0.0,\n      \"egress_bytes\": 0.0,\n      \"evicted_objects\": 0.0,\n      \"pubsub_channels\": 0,\n      \"pubsub_patterns\": 0,\n      \"expired_objects\": 0.0,\n      \"ingress_bytes\": 0.0,\n      \"instantaneous_ops_per_sec\": 0.00011973180076628352,\n      \"last_req_time\": \"1970-01-01T00:00:00Z\",\n      \"last_res_time\": \"1970-01-01T00:00:00Z\",\n      \"used_memory\": 5656299.362068966,\n      \"mem_size_lua\": 35840.0,\n      \"monitor_sessions_count\": 0.0,\n      \"no_of_keys\": 0.0,\n      \"other_req\": 0.0,\n      \"other_res\": 0.0,\n      \"read_hits\": 0.0,\n      \"read_misses\": 0.0,\n      \"read_req\": 0.0,\n      \"read_res\": 0.0,\n      \"total_connections_received\": 0.0,\n      \"total_req\": 0.0,\n      \"total_res\": 0.0,\n      \"write_hits\": 0.0,\n      \"write_misses\": 0.0,\n      \"write_req\": 0.0,\n      \"write_res\": 0.0\n    },\n    {\n      \"interval\": \"1hour\",\n      \"stime\": \"2015-05-27T13:00:00Z\",\n      \"etime\": \"2015-05-28T13:59:59Z\",\n      \"// additional fields...\"\n    }\n  ]\n}","section_id":"get-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/bdbs/stats/last","title":"Latest database stats requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/stats/last/","summary":"Most recent database statistics requests","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","page_type":"content","content_hash":"dafadde5d9edba3a91cd7a0409a7a7274e3cd70b389f2b468d23a0a1294eb7d6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-all-bdbs-stats-last) | `/v1/bdbs/stats/last` | Get most recent stats for all databases |\n| [GET](#get-bdbs-stats-last) | `/v1/bdbs/stats/last/{uid}` | Get most recent stats for a specific database |"},{"id":"get-all-bdbs-stats-last","title":"Get latest stats for all databases","role":"content","text":"[code example]\n\nGet the most recent statistics for all databases.\n\n#### Required permissions\n\n| Permission name | Roles |\n|-----------------|-------|\n| [view_all_bdb_stats](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_all_bdb_stats) | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |"},{"id":"get-all-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n1. Without metrics filter (returns all metrics by default)\n    [code example]\n\n2. With metrics filter\n    [code example]\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| metrics | string | Comma-separated list of metric names for which we want statistics (default is all). (optional) |"},{"id":"get-all-response","title":"Response","role":"returns","text":"Returns [statistics](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/statistics) for all databases.\n\n#### Example JSON body\n\n1. Without metrics filter (returns all metrics by default)\n    [code example]\n\n2. With metrics filter\n    [code example]"},{"id":"get-all-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | No bdbs exist |"},{"id":"get-bdbs-stats-last","title":"Get latest database stats","role":"content","text":"[code example]\n\nGet the most recent statistics for a specific database.\n\n#### Permissions\n\n| Permission name | Roles |\n|-----------------|-------|\n| [view_bdb_stats](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_bdb_stats) | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the requested BDB. |\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| metrics | string | Comma-separated list of metric names for which we want statistics (default is all). (optional) |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns the most recent [statistics](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/statistics) for a specific database.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | bdb does not exist |\n| [406 Not Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | bdb isn't currently active |\n| [503 Service Unavailable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.4) | bdb is in recovery state |"}],"examples":[{"id":"get-all-bdbs-stats-last-ex0","language":"sh","code":"GET /v1/bdbs/stats/last","section_id":"get-all-bdbs-stats-last"},{"id":"get-all-request-ex0","language":"plaintext","code":"GET /v1/bdbs/stats/last","section_id":"get-all-request"},{"id":"get-all-request-ex1","language":"plaintext","code":"GET /v1/bdbs/stats/last?metrics=no_of_keys,used_memory","section_id":"get-all-request"},{"id":"get-all-response-ex0","language":"json","code":"{\n    \"1\": {\n        \"stime\": \"2015-05-28T08:06:37Z\",\n        \"etime\": \"2015-05-28T08:06:44Z\",\n        \"conns\": 0.0,\n        \"egress_bytes\": 0.0,\n        \"etime\": \"2015-05-28T08:06:44Z\",\n        \"evicted_objects\": 0.0,\n        \"expired_objects\": 0.0,\n        \"ingress_bytes\": 0.0,\n        \"instantaneous_ops_per_sec\": 0.0,\n        \"last_req_time\": \"1970-01-01T00:00:00Z\",\n        \"last_res_time\": \"1970-01-01T00:00:00Z\",\n        \"used_memory\": 5651336.0,\n        \"mem_size_lua\": 35840.0,\n        \"monitor_sessions_count\": 0.0,\n        \"no_of_keys\": 0.0,\n        \"other_req\": 0.0,\n        \"other_res\": 0.0,\n        \"read_hits\": 0.0,\n        \"read_misses\": 0.0,\n        \"read_req\": 0.0,\n        \"read_res\": 0.0,\n        \"total_connections_received\": 0.0,\n        \"total_req\": 0.0,\n        \"total_res\": 0.0,\n        \"write_hits\": 0.0,\n        \"write_misses\": 0.0,\n        \"write_req\": 0.0,\n        \"write_res\": 0.0\n    },\n    \"2\": {\n        \"stime\": \"2015-05-28T08:06:37Z\",\n        \"etime\": \"2015-05-28T08:06:44Z\",\n\n        \"// additional fields...\"\n    },\n\n    \"// Additional BDBs...\"\n    }","section_id":"get-all-response"},{"id":"get-all-response-ex1","language":"json","code":"{\n    \"1\": {\n      \"etime\": \"2015-05-28T08:06:44Z\",\n      \"used_memory\": 5651576.0,\n      \"no_of_keys\": 0.0,\n      \"stime\": \"2015-05-28T08:06:37Z\"\n    },\n    \"2\": {\n      \"etime\": \"2015-05-28T08:06:44ZZ\",\n      \"used_memory\": 5651440.0,\n      \"no_of_keys\": 0.0,\n      \"stime\": \"2015-05-28T08:06:37Z\"\n    },\n\n    \"// Additional BDBs..\"\n    }","section_id":"get-all-response"},{"id":"get-bdbs-stats-last-ex0","language":"sh","code":"GET /v1/bdbs/stats/last/{int: uid}","section_id":"get-bdbs-stats-last"},{"id":"get-request-ex0","language":"sh","code":"GET /v1/bdbs/stats/last/1?metrics=no_of_keys,used_memory","section_id":"get-request"},{"id":"get-response-ex0","language":"json","code":"{\n   \"1\": {\n     \"etime\": \"2015-06-23T12:05:08Z\",\n     \"used_memory\": 5651576.0,\n     \"no_of_keys\": 0.0,\n     \"stime\": \"2015-06-23T12:05:03Z\"\n   }\n}","section_id":"get-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/bdbs/sync_source_stats","title":"Database syncer source stats requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/sync_source_stats/","summary":"Syncer source statistics requests","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","page_type":"content","content_hash":"91dce3d66793bb646def44d0187402bb5d94dd66c68c92a5edca25320f2ae85b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-all-bdbs-sync_source_stats) | `/v1/bdbs/{bdb_uid}/sync_source_stats` | Get stats for all syncer sources |\n| [GET](#get-bdbs-sync_source_stats) | `/v1/bdbs/{bdb_uid}/sync_source_stats/{uid}` | Get stats for a specific syncer instance |"},{"id":"get-all-bdbs-sync_source_stats","title":"Get all syncer source stats","role":"content","text":"[code example]\n\nGet stats for all syncer sources of a local database."},{"id":"permissions","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [view_bdb_stats](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_bdb_stats) | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |"},{"id":"get-all-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| bdb_uid | integer | The unique ID of the local database. |\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| interval | string | Time interval for which we want stats: 1sec/10sec/5min/15min/1hour/12hour/1week (optional) |\n| stime | ISO_8601 | Start time from which we want the stats. Should comply with the [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601) format (optional) |\n| etime | ISO_8601 | Optional end time after which we don't want the stats. Should comply with the [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601) format (optional) |"},{"id":"get-all-response","title":"Response","role":"returns","text":"Returns [statistics](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/statistics) for all syncer sources.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-all-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Database does not exist. |"},{"id":"get-bdbs-sync_source_stats","title":"Get syncer instance stats","role":"content","text":"[code example]\n\nGet stats for a specific syncer (Replica Of) instance.\n\n#### Permissions\n\n| Permission name | Roles |\n|-----------------|-------|\n| [view_bdb_stats](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_bdb_stats) | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| bdb_uid | integer | The unique ID of the local database. |\n| uid | integer | The sync_source uid. |\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| interval | string | Time interval for which we want stats: 1sec/10sec/5min/15min/1hour/12hour/1week (optional) |\n| stime | ISO_8601 | Optional start time from which we want the stats. Should comply with the [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601) format (optional) |\n| etime | ISO_8601 | Optional end time after which we don't want the stats. Should comply with the [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601) format (optional) |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns [statistics](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/statistics) for a specific syncer instance.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Database or sync_source do not exist. |"}],"examples":[{"id":"get-all-bdbs-sync_source_stats-ex0","language":"sh","code":"GET /v1/bdbs/{bdb_uid}/sync_source_stats","section_id":"get-all-bdbs-sync_source_stats"},{"id":"get-all-request-ex0","language":"sh","code":"GET /v1/bdbs/1/sync_source_stats?interval=5min","section_id":"get-all-request"},{"id":"get-all-response-ex0","language":"json","code":"{ \"sync_source_stats\": [\n     {\n         \"intervals\": [\n             {\n                 \"etime\": \"2017-10-22T19:30:00Z\",\n                 \"ingress_bytes\": 18528,\n                 \"ingress_bytes_decompressed\": 185992,\n                 \"interval\": \"5min\",\n                 \"local_ingress_lag_time\": 0.244,\n                 \"stime\": \"2017-10-22T19:25:00Z\"\n             },\n             {\n                 \"etime\": \"2017-10-22T19:35:00Z\",\n                 \"ingress_bytes\": 18,\n                 \"ingress_bytes_decompressed\": 192,\n                 \"interval\": \"5min\",\n                 \"local_ingress_lag_time\": 0.0,\n                 \"stime\": \"2017-10-22T19:30:00Z\"\n             }\n         ],\n         \"uid\": \"1\"\n     }\n   ]\n }","section_id":"get-all-response"},{"id":"get-bdbs-sync_source_stats-ex0","language":"sh","code":"GET /v1/bdbs/{bdb_uid}/sync_source_stats/{int: uid}","section_id":"get-bdbs-sync_source_stats"},{"id":"get-request-ex0","language":"sh","code":"GET /v1/bdbs/1/sync_source_stats/1?interval=5min","section_id":"get-request"},{"id":"get-response-ex0","language":"json","code":"{\n    \"intervals\": [\n        {\n            \"etime\": \"2017-10-22T19:30:00Z\",\n            \"ingress_bytes\": 18528,\n            \"ingress_bytes_decompressed\": 185992,\n            \"interval\": \"5min\",\n            \"local_ingress_lag_time\": 0.244,\n            \"stime\": \"2017-10-22T19:25:00Z\"\n        },\n        {\n            \"etime\": \"2017-10-22T19:35:00Z\",\n            \"ingress_bytes\": 18,\n            \"ingress_bytes_decompressed\": 192,\n            \"interval\": \"5min\",\n            \"local_ingress_lag_time\": 0.0,\n            \"stime\": \"2017-10-22T19:30:00Z\"\n        }\n    ],\n    \"uid\": \"1\"\n}","section_id":"get-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/bdbs/syncer_state/crdt","title":"CRDT syncer state requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/syncer_state/crdt/","summary":"CRDT syncer state requests","tags":["docs","operate","rs"],"last_updated":"2024-11-12T09:34:06-06:00","page_type":"content","content_hash":"1f01c04dcda87f5f453972d8cbd9133e1508cd96629b5e5659eb32ebd8b83aed","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-syncer-state) | `/v1/bdbs/{uid}/syncer_state/crdt` |  Get a CRDB's syncer state |"},{"id":"get-syncer-state","title":"Get CRDB syncer state","role":"content","text":"[code example]\n\nGet a CRDB's syncer state as JSON."},{"id":"permissions","title":"Permissions","role":"security","text":"| Permission name | Roles   |\n|-----------------|---------|\n| [view_bdb_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_bdb_info) |  admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager|"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value |\n|-----|-------|\n| Host | The domain name or IP of the cluster. |\n| Accept | application/json |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the database requested. |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns a JSON object that represents the syncer state.\n\n#### Example JSON body\n\n[code example]\n\n#### Status codes {#get-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | OK |\n| [404 Not Found](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Syncer state key does not exist |\n| [500 Internal Server Error](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1) | Internal error |\n| [503 Service Unavailable](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.4) | Redis connection error, service unavailable |"}],"examples":[{"id":"get-syncer-state-ex0","language":"sh","code":"GET /v1/bdbs/{int: uid}/syncer_state/crdt","section_id":"get-syncer-state"},{"id":"get-request-ex0","language":"sh","code":"GET /v1/bdbs/1/syncer_state/crdt","section_id":"get-request"},{"id":"get-response-ex0","language":"json","code":"{\n    \"DB\": 22,\n    \"RunID\": 1584086516,\n    // additional fields...\n}","section_id":"get-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/bdbs/syncer_state","title":"Syncer state requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/syncer_state/","summary":"Syncer state requests","tags":["docs","operate","rs"],"last_updated":"2026-10-05T16:11:44+01:00","children":[{"id":"operate/rs/references/rest-api/requests/bdbs/syncer_state/crdt","summary":"CRDT syncer state requests","title":"CRDT syncer state requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/syncer_state/crdt/"},{"id":"operate/rs/references/rest-api/requests/bdbs/syncer_state/replica","summary":"Replica syncer state requests","title":"Replica syncer state requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/syncer_state/replica/"}],"page_type":"content","content_hash":"d648b1002c264c85c95a16b053b87565f8657ff33139141cd6ea13ccc1ba98a6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-syncer-state) | `/v1/bdbs/{uid}/syncer_state` | Get a CRDB's syncer state |"},{"id":"get-syncer-state","title":"Get syncer state","role":"content","text":"[code example]\n\nGet a CRDB's syncer state as JSON.\n\n> [!WARNING]\n> This endpoint is deprecated as of Redis Software version 7.2.4 and will be removed in a future release. Use [`/v1/bdbs/<uid>/syncer_state/crdt`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/syncer_state/crdt) instead."},{"id":"permissions","title":"Permissions","role":"security","text":"| Permission name | Roles   |\n|-----------------|---------|\n| [view_bdb_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_bdb_info) |  admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value |\n|-----|-------|\n| Host | The domain name or IP of the cluster. |\n| Accept | application/json |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the database requested. |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns a JSON object that represents the syncer state.\n\n#### Example JSON body\n\n[code example]\n\n#### Status codes {#get-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | OK |\n| [404 Not Found](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Syncer state key does not exist |\n| [500 Internal Server Error](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1) | Internal error |\n| [503 Service Unavailable](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.4) | Redis connection error, service unavailable |"}],"examples":[{"id":"get-syncer-state-ex0","language":"sh","code":"GET /v1/bdbs/{int: uid}/syncer_state","section_id":"get-syncer-state"},{"id":"get-request-ex0","language":"sh","code":"GET /v1/bdbs/1/syncer_state","section_id":"get-request"},{"id":"get-response-ex0","language":"json","code":"{\n    \"DB\": 22,\n    \"RunID\": 1584086516,\n    // additional fields...\n}","section_id":"get-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/bdbs/syncer_state/replica","title":"Replica syncer state requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/syncer_state/replica/","summary":"Replica syncer state requests","tags":["docs","operate","rs"],"last_updated":"2024-11-12T09:34:06-06:00","page_type":"content","content_hash":"b1311ea32099a0759bf1aeebdef02e5cafccca996145d2ae826d798b7fbb9e87","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-syncer-state) | `/v1/bdbs/{uid}/syncer_state/replica` | Get a CRDB replica's syncer state |"},{"id":"get-syncer-state","title":"Get replica syncer state","role":"content","text":"[code example]\n\nGet a CRDB replica's syncer state as JSON."},{"id":"permissions","title":"Permissions","role":"security","text":"| Permission name | Roles   |\n|-----------------|---------|\n| [view_bdb_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_bdb_info) |  admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value |\n|-----|-------|\n| Host | The domain name or IP of the cluster. |\n| Accept | application/json |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the database requested. |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns a JSON object that represents the syncer state.\n\n#### Example JSON body\n\n[code example]\n\n#### Status codes {#get-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | OK |\n| [404 Not Found](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Syncer state key does not exist |\n| [500 Internal Server Error](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1) | Internal error |\n| [503 Service Unavailable](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.4) | Redis connection error, service unavailable |"}],"examples":[{"id":"get-syncer-state-ex0","language":"sh","code":"GET /v1/bdbs/{int: uid}/syncer_state/replica","section_id":"get-syncer-state"},{"id":"get-request-ex0","language":"sh","code":"GET /v1/bdbs/1/syncer_state/replica","section_id":"get-request"},{"id":"get-response-ex0","language":"json","code":"{\n    \"DB\": 22,\n    \"RunID\": 1584086516,\n    // additional fields...\n}","section_id":"get-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/bdbs/upgrade","title":"Database upgrade requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/upgrade/","summary":"Database upgrade requests","aliases":["/operate/rs/references/rest-api/requests/modules/upgrade/"],"tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"8f437212e88231555533ace6aad733d3f3bf36e1e2c2af5c25a38f7ece039f94","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [POST](#post-bdbs-upgrade) | `/v1/bdbs/{uid}/upgrade` | Upgrade database |"},{"id":"post-bdbs-upgrade","title":"Upgrade database","role":"content","text":"POST /v1/bdbs/{int: uid}/upgrade\n\nUpgrade a database.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [update_bdb_with_action](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#update_bdb_with_action) |"},{"id":"post-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tPOST /v1/bdbs/1/upgrade \n\n#### Example JSON body\n\n[code example]\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### Request body\n\n| Field | Type | Description |\n|-------|------|-------------|\n| force_restart | boolean | Restart shards even if no version change (default: false) |\n| keep_redis_version | boolean | Keep current Redis version (default: false). Deprecated as of Redis Software v7.8.2. To upgrade modules without upgrading the Redis database version, set `redis_version` to the current Redis database version instead. |\n| keep_crdt_protocol_version | boolean | Keep current crdt protocol version (default: false)  |\n| may_discard_data | boolean | Discard data in a non-replicated, non-persistent bdb (default: false) |\n| force_discard | boolean | Discard data even if the bdb is replicated and/or persistent (default: false) |\n| preserve_roles | boolean | Preserve shards' master/replica roles (requires an extra failover) (default: false) |\n| parallel_shards_upgrade | integer | Max number of shards to upgrade in parallel (default: all) |\n| modules | list of modules | List of dicts representing the modules that will be upgraded. As of Redis Software v7.8.2, `current_module` and `new_module` are deprecated.<br></br>Each dict includes:<br></br>• `current_module`: uid of a module to upgrade (deprecated)<br></br>• `new_module`: uid of the module we want to upgrade to (deprecated)<br></br>• `new_module_args`: args list for the new module (no defaults for the three module-related parameters).\n| redis_version | version number | Upgrades the database to the specified Redis version instead of the latest version. To upgrade modules without upgrading the Redis database version, set `redis_version` to the current Redis database version instead. |\n| latest_with_modules | boolean | Upgrades the database to the latest Redis version and latest supported versions of modules available in the cluster. (default: true as of v7.8.2) Deprecated as of Redis Software v7.8.2. |"},{"id":"post-response","title":"Response","role":"returns","text":"Returns the upgraded [BDB object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb).\n\n#### Example JSON body\n\n[code example]"},{"id":"post-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success, bdb upgrade initiated (`action_uid` can be used to track progress) |\n| [400 Bad Request](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1) | Malformed or bad command |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | bdb not found |\n| [406 Not Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | New module version capabilities don't comply with the database configuration |\n| [500 Internal Server Error](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1) | Internal error |"}],"examples":[{"id":"post-request-ex0","language":"json","code":"{\n    \"swap_roles\": true,\n    \"may_discard_data\": false\n}","section_id":"post-request"},{"id":"post-response-ex0","language":"json","code":"{\n    \"uid\": 1,\n    \"replication\": true,\n    \"data_persistence\": \"aof\",\n    \"// additional fields...\"\n}","section_id":"post-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/bootstrap","title":"Bootstrap requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bootstrap/","summary":"Bootstrap requests","tags":["docs","operate","rs"],"last_updated":"2025-11-10T23:51:20+02:00","children":[{"id":"operate/rs/references/rest-api/requests/bootstrap/validate","summary":"Boostrap validation requests","title":"Bootstrap validation requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bootstrap/validate/"}],"page_type":"content","content_hash":"cd14023f0de3e6ffd43612ca0914543bd299e2557a3ac45a9ac628aaf0760c98","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-bootstrap) | `/v1/boostrap` | Get the local node's bootstrap status |\n| [POST](#post-bootstrap) | `/v1/bootstrap/{action}` | Initiate bootstrapping |"},{"id":"get-bootstrap","title":"Get bootstrap status","role":"content","text":"[code example]\n\nGet the local node's bootstrap status.\n\nThis request is accepted as soon the cluster software is installed and before the node is part of an active cluster.\n\nOnce the node is part of an active cluster, authentication is required."},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Accept | application/json | Accepted media type |"},{"id":"get-response","title":"Response","role":"returns","text":"The JSON response object contains a `bootstrap_status` object and a `local_node_info` object.\n\nThe `bootstrap_status` object contains the following information:\n\n| Field | Description |\n|-------|-------------|\n| state | Current bootstrap state.<br></br>`idle`: No bootstrapping started.<br></br>`initiated`: Bootstrap request received.<br></br>`creating_cluster`: In the process of creating a new cluster.<br></br>`recovering_cluster`: In the process of recovering a cluster.<br /><br />`joining_cluster`: In the process of joining an existing cluster.<br></br>`error`: The last bootstrap action failed.<br></br>`completed`: The last bootstrap action completed successfully.|\n| start_time | Bootstrap process start time |\n| end_time | Bootstrap process end time |\n| error_code | If state is `error`, this error code describes the type of error encountered. |\n| error_details | An error-specific object that may contain additional information about the error. A common field in use is `message` which provides a more verbose error message.\n\nThe `local_node_info` object is a subset of a [node object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/node) that provides information about the node configuration.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-error-codes","title":"Error codes","role":"errors","text":"| Code | Description |\n|------|-------------|\n| config_error | An error related to the bootstrap configuration provided (e.g. bad JSON). |\n| connect_error | Failed to connect to cluster (e.g. FQDN DNS could not resolve, no/wrong node IP provided, etc. |\n| access_denied | Invalid credentials supplied. |\n| invalid_license | The license string provided is invalid. Additional info can be fetched from the `error_details` object, which includes the violation code in case the license is valid but its terms are violated. |\n| repair_required | Cluster is in degraded mode and can only accept replacement nodes. When this happens, `error_details` contains two fields: `failed_nodes` and `replace_candidate`. The `failed_nodes` field is an array of objects, each describing a failed node with at least a `uid` field and an optional `rack_id`. `replace_candidate` is the UID of the node most suitable for replacement. |\n| insufficient_node_memory | An attempt to replace a dead node fails because the replaced node does not have enough memory. When this happens, error_details contains a required_memory field which indicates the node memory requirement. |\n| insufficient_node_flash | An attempt to replace a dead node fails because the replaced node does not have enough flash. When this happens, `error_details` contains a `required_flash` field which indicates the node flash requirement. |\n| time_not_sync | An attempt to join a node with system time not synchronized with the rest of the cluster. |\n| rack_id_required | An attempt to join a node with no `rack_id` in a rack-aware cluster. In addition, a `current_rack_ids` field will include an array of currently used rack ids. |\n| socket_directory_mismatch | An attempt to join a node with a socket directory setting that differs from the cluster |\n| node_config_mismatch | An attempt to join a node with a configuration setting (e.g. confdir, osuser, installdir) that differs from the cluster |\n| path_error | A needed path does not exist or is not accessable. |        \n| internal_error | A different, unspecified internal error was encountered. |"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |"},{"id":"post-bootstrap","title":"Start bootstrapping","role":"content","text":"[code example]\n\nInitiate bootstrapping.\n\nThe request must contain a bootstrap configuration JSON object, as\ndescribed in [Object attributes](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bootstrap) or a minimal subset.\n\nBootstrapping is permitted only when the current bootstrap state is\n`idle` or `error` (in which case the process will restart with the new\nconfiguration).\n\nThis request is asynchronous - once the request has been accepted,\nthe caller is expected to poll bootstrap status while waiting for it to\ncomplete."},{"id":"post-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Example JSON body\n\n##### Join cluster\n[code example]\n\n##### Create cluster\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### Request body\n\nInclude a [bootstrap object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bootstrap) in the request body."},{"id":"post-response","title":"Response","role":"returns","text":"#### Status codes {#post-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Request received and processing begins. |\n| [409 Conflict](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.10) | Bootstrap already in progress (check state) |"}],"examples":[{"id":"get-bootstrap-ex0","language":"sh","code":"GET /v1/bootstrap","section_id":"get-bootstrap"},{"id":"get-request-ex0","language":"sh","code":"GET /v1/bootstrap","section_id":"get-request"},{"id":"get-response-ex0","language":"json","code":"{\n    \"bootstrap_status\": {\n        \"start_time\": \"2014-08-29T11:19:49Z\",\n        \"end_time\": \"2014-08-29T11:19:49Z\",\n        \"state\": \"completed\"\n    },\n    \"local_node_info\": {\n        \"uid\": 3,\n        \"software_version\": \"0.90.0-1\",\n        \"cores\": 2,\n        \"ephemeral_storage_path\": \"/var/opt/redislabs/tmp\",\n        \"ephemeral_storage_size\": 1018889.8304,\n        \"os_version\": \"Ubuntu 14.04 LTS\",\n        \"persistent_storage_path\": \"/var/opt/redislabs/persist/redis\",\n        \"persistent_storage_size\": 1018889.8304,\n        \"total_memory\": 24137,\n        \"uptime\": 50278,\n        \"available_addrs\": [{\n                                 \"address\": \"172.16.50.122\",\n                                 \"format\": \"ipv4\",\n                                 \"if_name\": \"eth0\",\n                                 \"private\": true\n                             },\n                             {\n                                 \"address\": \"10.0.3.1\",\n                                 \"format\": \"ipv4\",\n                                 \"if_name\": \"lxcbr0\",\n                                 \"private\": true\n                             },\n                             {\n                                 \"address\": \"172.17.0.1\",\n                                 \"format\": \"ipv4\",\n                                 \"if_name\": \"docker0\",\n                                 \"private\": true\n                             },\n                            {\n                                 \"address\": \"2001:db8:0:f101::1\",\n                                 \"format\": \"ipv6\",\n                                 \"if_name\": \"eth0\",\n                                 \"private\": false\n                            }]\n    }\n}","section_id":"get-response"},{"id":"post-bootstrap-ex0","language":"sh","code":"POST /v1/bootstrap/{action}","section_id":"post-bootstrap"},{"id":"post-request-ex0","language":"sh","code":"POST /v1/bootstrap/create_cluster","section_id":"post-request"},{"id":"post-request-ex1","language":"json","code":"{\n    \"action\": \"join_cluster\",\n    \"cluster\": {\n       \"nodes\":[ \"1.1.1.1\", \"2.2.2.2\" ]\n    },\n    \"node\": {\n       \"paths\": {\n          \"persistent_path\": \"/path/to/persistent/storage\",\n          \"ephemeral_path\": \"/path/to/ephemeral/storage\",\n          \"bigstore_path\": \"/path/to/bigstore/storage\"\n       },\n       \"bigstore_enabled\": true,\n       \"identity\": {\n          \"addr\":\"1.2.3.4\",\n          \"external_addr\":[\"2001:0db8:85a3:0000:0000:8a2e:0370:7334\", \"3.4.5.6\"]\n       }\n    },\n    \"credentials\": {\n       \"username\": \"my_username\",\n       \"password\": \"my_password\"\n    }\n}","section_id":"post-request"},{"id":"post-request-ex2","language":"json","code":"{\n    \"action\": \"create_cluster\",\n    \"cluster\": {\n       \"nodes\": [],\n       \"name\": \"my.cluster\"\n    },\n    \"node\": {\n       \"paths\": {\n          \"persistent_path\": \"/path/to/persistent/storage\",\n          \"ephemeral_path\": \"/path/to/ephemeral/storage\",\n          \"bigstore_path\": \"/path/to/bigredis/storage\"\n       },\n       \"identity\": {\n          \"addr\":\"1.2.3.4\",\n          \"external_addr\":[\"2001:0db8:85a3:0000:0000:8a2e:0370:7334\", \"3.4.5.6\"]\n       },\n       \"bigstore_enabled\": true\n    },\n    \"license\": \"----- LICENSE START -----\\ndi+iK...KniI9\\n----- LICENSE END -----\\n\",\n    \"credentials\": {\n       \"username\": \"my_username\",\n       \"password\": \"my_password\"\n    }\n}","section_id":"post-request"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/bootstrap/validate","title":"Bootstrap validation requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bootstrap/validate/","summary":"Boostrap validation requests","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","page_type":"content","content_hash":"caaadd251fd093f9ebecb7ec93d5f81347564cd39c0ef428860bc055d6e71855","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [POST](#post-bootstrap-validate) | `/v1/bootstrap/validate/{action}` | Perform bootstrap validation |"},{"id":"post-bootstrap-validate","title":"Bootstrap validation","role":"content","text":"POST /v1/bootstrap/validate/{action}\n\nPerform bootstrap validation.\n\nUnlike actual bootstrapping, this request blocks and immediately\nreturns with a response."},{"id":"post-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n    POST /v1/bootstrap/validate/join_cluster\n\n#### Request body\n\nThe request must contain a [bootstrap configuration object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bootstrap), similar to the one used for actual bootstrapping."},{"id":"post-response","title":"Response","role":"returns","text":"If an error occurs, the call returns a `bootstrap_status` JSON object that contains the following fields:\n\n| Field | Description |\n|-------|-------------|\n| state | Current bootstrap state.<br></br>`idle`: No bootstrapping started.<br></br>`initiated`: Bootstrap request received.<br></br>`creating_cluster`: In the process of creating a new cluster.<br></br>`joining_cluster`: In the process of joining an existing cluster.<br></br>`error`: The last bootstrap action failed.<br></br>`completed`: The last bootstrap action completed successfully.|\n| start_time | Bootstrap process start time |\n| end_time | Bootstrap process end time |\n| error_code | If state is `error`, this error code describes the type of error encountered. |\n| error_details | An error-specific object that may contain additional information about the error. A common field in use is `message` which provides a more verbose error message."},{"id":"post-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error, validation was successful. |\n| [406 Not Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | Validation failed, bootstrap status is returned as body. |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/cluster/actions","title":"Cluster actions requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/actions/","summary":"Cluster action requests","tags":["docs","operate","rs"],"last_updated":"2025-10-30T12:00:21-05:00","page_type":"content","content_hash":"fd4f0493b80241241a80dd3f23de39673be1b52e9aef5e0fc41c4b49df018279","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-all-cluster-actions) | `/v1/cluster/actions` | Get the status of all actions  |\n| [GET](#get-cluster-action) | `/v1/cluster/actions/{action}` | Get the status of a specific action |\n| [POST](#post-cluster-action) | `/v1/cluster/actions/{action}` | Initiate a cluster-wide action |\n| [DELETE](#delete-cluster-action) | `/v1/cluster/actions/{action}` | Cancel action or remove action status |"},{"id":"get-all-cluster-actions","title":"Get all cluster actions","role":"content","text":"GET /v1/cluster/actions\n\nGet the status of all currently executing, queued, or completed cluster actions.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_status_of_cluster_action](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_status_of_cluster_action) |"},{"id":"get-all-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n    GET /v1/cluster/actions"},{"id":"get-all-response","title":"Response","role":"returns","text":"Returns a JSON array of [action objects](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/action). \n\n#### Example JSON body\n\n[code example]"},{"id":"get-all-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | No error, response provides info about an ongoing action. |\n| [404 Not Found](https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found) | Action does not exist (i.e. not currently running and no available status of last run). |"},{"id":"get-cluster-action","title":"Get cluster action","role":"content","text":"GET /v1/cluster/actions/{action}\n\nGet the status of a currently executing, queued, or completed cluster action.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_status_of_cluster_action](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_status_of_cluster_action) |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n    GET /v1/cluster/actions/action_name\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| action | string | The action to check. |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns an [action object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/action). \n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | No error, response provides info about an ongoing action. |\n| [404 Not Found](https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found) | Action does not exist (i.e. not currently running and no available status of last run). |"},{"id":"post-cluster-action","title":"Initiate cluster-wide action","role":"content","text":"POST /v1/cluster/actions/{action}\n\nInitiate a cluster-wide action.\n\nThe API allows only a single instance of any action type to be\ninvoked at the same time, and violations of this requirement will\nresult in a `409 CONFLICT` response.\n\nThe caller is expected to query and process the results of the\npreviously executed instance of the same action, which will be\nremoved as soon as the new one is submitted.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [start_cluster_action](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#start_cluster_action) |"},{"id":"post-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n    POST /v1/cluster/actions/action_name\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| action | string | The name of the action required. |\n\nSupported cluster actions:\n\n- `change_master`: Promotes a specified node to become the primary node of the cluster, which coordinates cluster-wide operations. Include the `node_uid` of the node you want to promote in the request body.\n\n    [code example]"},{"id":"post-response","title":"Response","role":"returns","text":"The body content may provide additional action details. Currently, it is not used."},{"id":"post-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | No error, action was initiated. |\n| [400 Bad Request](https://www.rfc-editor.org/rfc/rfc9110.html#name-400-bad-request) | Bad action or content provided. |\n| [404 Not Found](https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found) | Node does not exist. |\n| [406 Not Acceptable](https://www.rfc-editor.org/rfc/rfc9110.html#name-406-not-acceptable) | Node not bootstrapped. |\n| [409 Conflict](https://www.rfc-editor.org/rfc/rfc9110.html#name-409-conflict) | A conflicting action is already in progress. |"},{"id":"delete-cluster-action","title":"Cancel action","role":"content","text":"DELETE /v1/cluster/actions/{action}\n\nCancel a queued or executing cluster action, or remove the status of\na previously executed and completed action.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [cancel_cluster_action](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#cancel_cluster_action) |"},{"id":"delete-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n    DELETE /v1/cluster/actions/action_name\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| action | string | The name of the action to cancel, currently no actions are supported. |"},{"id":"delete-response","title":"Response","role":"returns","text":"Returns a status code."},{"id":"delete-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | Action will be cancelled when possible. |\n| [404 Not Found](https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found) | Action unknown or not currently running. |"}],"examples":[{"id":"get-all-response-ex0","language":"json","code":"{\n    \"actions\": [\n        {\n            \"name\": \"action_name\",\n            \"status\": \"queued\",\n            \"progress\": 0.0\n        }\n    ]\n}","section_id":"get-all-response"},{"id":"get-response-ex0","language":"json","code":"{\n    \"name\": \"action_name\",\n     \"status\": \"queued\",\n    \"progress\": 0.0\n}","section_id":"get-response"},{"id":"post-request-ex0","language":"sh","code":"POST /v1/cluster/actions/change_master\n    {\n        \"node_uid\": \"2\"\n    }","section_id":"post-request"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/cluster/alerts","title":"Cluster alerts requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/alerts/","summary":"Cluster alert requests","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","page_type":"content","content_hash":"40e3d2590fc63749b5146469b547e1def40640c51a55bce895d46a6ebb7a3818","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-all-alerts) | `/v1/cluster/alerts` | Get all cluster alerts |\n| [GET](#get-alert) | `/v1/cluster/alerts/{alert}` | Get a specific cluster alert |"},{"id":"get-all-alerts","title":"Get all cluster alerts","role":"content","text":"GET /v1/cluster/alerts\n\nGet all alert states for the cluster object.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_cluster_alerts](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_cluster_alerts) |"},{"id":"get-all-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/cluster/alerts \n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| ignore_settings | boolean | Retrieve updated alert state regardless of the cluster’s alert_settings. When not present, a disabled alert will always be retrieved as disabled with a false state. (optional) |"},{"id":"get-all-response","title":"Response","role":"returns","text":"Returns a hash of [alert objects](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/alert) and their states.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-all-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |"},{"id":"get-alert","title":"Get cluster alert","role":"content","text":"GET /v1/cluster/alerts/{alert}\n\nGet a cluster alert state.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_cluster_alerts](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_cluster_alerts) |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/cluster/alerts/cluster_too_few_nodes_for_replication \n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| ignore_settings | boolean | Retrieve updated alert state regardless of the cluster’s alert_settings. When not present, a disabled alert will always be retrieved as disabled with a false state. (optional) |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns an [alert object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/alert).\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Specified alert does not exist |"}],"examples":[{"id":"get-all-response-ex0","language":"json","code":"{\n    \"cluster_too_few_nodes_for_replication\": {\n        \"change_time\": \"2014-12-22T11:48:00Z\",\n        \"change_value\": {\n            \"state\": false\n        },\n       \"enabled\": true,\n       \"state\": \"off\",\n       \"severity\": \"WARNING\",\n    },\n    \"...\"\n}","section_id":"get-all-response"},{"id":"get-response-ex0","language":"json","code":"{\n    \"change_time\": \"2014-12-22T11:48:00Z\",\n    \"change_value\": {\n        \"state\": false\n    },\n   \"enabled\": true,\n   \"state\": \"off\",\n   \"severity\": \"WARNING\",\n}","section_id":"get-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/cluster/auditing-db-conns","title":"Auditing database connections requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/auditing-db-conns/","summary":"Auditing database connections requests","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","page_type":"content","content_hash":"b0b8855167cb1f2be4b5baf9398aeb9c5e1f3f3093f0d76e800fb29ff1a25df8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-cluster-audit-db-conns) | `/v1/cluster/auditing/db_conns` | Get database connection auditing settings |\n| [PUT](#put-cluster-audit-db-conns) | `/v1/cluster/auditing/db_conns` | Update database connection auditing settings |\n| [DELETE](#delete-cluster-audit-db-conns) | `/v1/cluster/auditing/db_conns` | Delete database connection auditing settings |"},{"id":"get-cluster-audit-db-conns","title":"Get database auditing settings","role":"content","text":"GET /v1/cluster/auditing/db_conns\n\nGets the configuration settings for [auditing database connections](https://redis.io/docs/latest/operate/rs/security/audit-events).\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_cluster_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_cluster_info) |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/cluster/auditing/db_conns \n\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns a [database connection auditing configuration object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/db-conns-auditing-config).\n\n#### Example JSON body\n\n[code example]"},{"id":"get-error-codes","title":"Error codes","role":"errors","text":"When errors are reported, the server may return a JSON object with `error_code` and `message` fields that provide additional information. The following are possible `error_code` values:\n\n| Code | Description |\n|------|-------------|\n| db_conns_auditing_unsupported_by_capability | Not all nodes support DB Connections Auditing capability  |"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | Success |\n| [406 Not Acceptable](https://www.rfc-editor.org/rfc/rfc9110.html#name-406-not-acceptable) | Feature not supported for all nodes |"},{"id":"put-cluster-audit-db-conns","title":"Update database auditing","role":"content","text":"PUT /v1/cluster/auditing/db_conns\n\nUpdates the configuration settings for [auditing database connections](https://redis.io/docs/latest/operate/rs/security/audit-events).\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [update_cluster](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#update_cluster) |"},{"id":"put-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tPUT /v1/cluster/auditing/db_conns\n\n#### Example JSON body\n\n[code example]\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### Request body\n\nInclude a [database connection auditing configuration object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/db-conns-auditing-config) with updated fields in the request body."},{"id":"put-response","title":"Response","role":"returns","text":"Returns the updated [database connection auditing configuration object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/db-conns-auditing-config).\n\n#### Example JSON body\n\n[code example]"},{"id":"put-error-codes","title":"Error codes","role":"errors","text":"When errors are reported, the server may return a JSON object with `error_code` and `message` fields that provide additional information. The following are possible `error_code` values:\n\n| Code | Description |\n|------|-------------|\n| db_conns_auditing_unsupported_by_capability | Not all nodes support DB Connections Auditing capability  |"},{"id":"put-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | Success |\n| [406 Not Acceptable](https://www.rfc-editor.org/rfc/rfc9110.html#name-406-not-acceptable) | Feature not supported for all nodes |"},{"id":"delete-cluster-audit-db-conns","title":"Delete database auditing settings","role":"content","text":"DELETE /v1/cluster/auditing/db_conns\n\nResets the configuration settings for [auditing database connections](https://redis.io/docs/latest/operate/rs/security/audit-events).\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [update_cluster](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#update_cluster) |"},{"id":"delete-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tDELETE /v1/cluster/auditing/db_conns \n\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |"},{"id":"delete-response","title":"Response","role":"returns","text":"Returns a status code that indicates whether the database connection auditing settings reset successfully or failed to reset."},{"id":"delete-error-codes","title":"Error codes","role":"errors","text":"When errors are reported, the server may return a JSON object with `error_code` and `message` fields that provide additional information. The following are possible `error_code` values:\n\n| Code | Description |\n|------|-------------|\n| db_conns_audit_config_not_found | Unable to find the auditing configuration |\n| cannot_delete_audit_config_when_policy_enabled | Auditing cluster policy is 'enabled' when trying to delete the auditing configuration |\n| cannot_delete_audit_config_when_bdb_enabled | One of the databases has auditing configuration 'enabled' when trying to delete the auditing configuration |"},{"id":"delete-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | Success |\n| [404 Not Found](https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found) | Configuration not found |\n| [406 Not Acceptable](https://www.rfc-editor.org/rfc/rfc9110.html#name-406-not-acceptable) | Feature not supported for all nodes |"}],"examples":[{"id":"get-response-ex0","language":"json","code":"{\n    \"audit_address\": \"127.0.0.1\",\n    \"audit_port\": 12345,\n    \"audit_protocol\": \"TCP\",\n    \"audit_reconnect_interval\": 1,\n    \"audit_reconnect_max_attempts\": 0\n}","section_id":"get-response"},{"id":"put-request-ex0","language":"json","code":"{\n    \"audit_protocol\": \"TCP\",\n    \"audit_address\": \"127.0.0.1\",\n    \"audit_port\": 12345,\n    \"audit_reconnect_interval\": 1,\n    \"audit_reconnect_max_attempts\": 0\n}","section_id":"put-request"},{"id":"put-response-ex0","language":"json","code":"{\n    \"audit_address\": \"127.0.0.1\",\n    \"audit_port\": 12345,\n    \"audit_protocol\": \"TCP\",\n    \"audit_reconnect_interval\": 1,\n    \"audit_reconnect_max_attempts\": 0\n}","section_id":"put-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/cluster/certificates","title":"Cluster certificates requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/certificates/","summary":"Cluster certificates requests","tags":["docs","operate","rs"],"last_updated":"2026-10-05T16:11:44+01:00","children":[{"id":"operate/rs/references/rest-api/requests/cluster/certificates/rotate","summary":"Rotate cluster certificates requests","title":"Rotate cluster certificates requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/certificates/rotate/"}],"page_type":"content","content_hash":"3d766cb90098f1afeb757cc98aff0ee3ff3dcac84b41c309b0c2062749cdd261","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-cluster-certificates) | `/v1/cluster/certificates` | Get cluster certificates |\n| [PUT](#put-cluster-certificates) | `/v1/cluster/certificates` | Update cluster certificates |\n| [PUT](#put-cluster-update_cert) | `/v1/cluster/update_cert` | Update a cluster certificate (deprecated as of Redis Software version 7.22.2) |\n| [DELETE](#delete-cluster-certificate) | `/v1/cluster/certificates/{certificate_name}` | Delete cluster certificate |"},{"id":"get-cluster-certificates","title":"Get cluster certificates","role":"content","text":"GET /v1/cluster/certificates\n\nGet the cluster's certificates.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_cluster_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_cluster_info) |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/cluster/certificates \n\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns a JSON object that contains the cluster's certificates and keys.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | No error |"},{"id":"put-cluster-certificates","title":"Update cluster certificates","role":"content","text":"[code example]\n\nReplaces multiple cluster certificates with the provided certificates on all nodes within the cluster. This endpoint validates all provided certificates before actually updating the cluster.\n\nSee the [certificates table](https://redis.io/docs/latest/operate/rs/security/certificates) for the list of cluster certificates and their descriptions."},{"id":"put-certificates-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Example JSON body\n\n[code example]\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### Request body\n\nInclude an array of [certificate objects](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/certificates) in the request body."},{"id":"put-certificates-response","title":"Response","role":"returns","text":"Returns a `200 OK` status code if all certificates are successfully replaced across the entire cluster.\n\nIf the response returns a failed status code, you should retry updating the certificates in case the cluster is no longer in an optimal state."},{"id":"put-certificates-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | No error |\n| [400 Bad Request](https://www.rfc-editor.org/rfc/rfc9110.html#name-400-bad-request) | Failed, invalid certificate(s) |\n| [403 Forbidden](https://www.rfc-editor.org/rfc/rfc9110.html#name-403-forbidden) | Failed, unknown certificate(s) |\n| [406 Not Acceptable](https://www.rfc-editor.org/rfc/rfc9110.html#name-406-not-acceptable) | Failed, expired certificate(s) |\n| [409 Conflict](https://www.rfc-editor.org/rfc/rfc9110.html#name-409-conflict) | Failed, not all nodes have been updated |"},{"id":"put-cluster-update_cert","title":"Update cluster certificate","role":"content","text":"[code example]\n\n> [!NOTE]\n> This REST API path is deprecated as of Redis Software 7.22.2 and will be removed in a future version. Use [`PUT /v1/cluster/certificates`](#put-cluster-certificates) instead.\n\nReplaces an existing certificate on all nodes within the cluster with a new certificate. The new certificate must pass validation before it can replace the old certificate.\n\nSee the [certificates table](https://redis.io/docs/latest/operate/rs/security/certificates) for the list of cluster certificates and their descriptions."},{"id":"put-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Example JSON body\n\n[code example]\n\nReplace `[key_content]` with the content of the private key and `[cert_content]` with the content of the certificate."},{"id":"put-response","title":"Response","role":"returns","text":"Responds with the `200 OK` status code if the certificate replacement succeeds across the entire cluster.\n\nOtherwise, retry the certificate update in case the failure was due to a temporary issue in the cluster."},{"id":"put-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | No error |\n| [400 Bad Request](https://www.rfc-editor.org/rfc/rfc9110.html#name-400-bad-request) | Failed, invalid certificate. |\n| [403 Forbidden](https://www.rfc-editor.org/rfc/rfc9110.html#name-403-forbidden) | Failed, unknown certificate. |\n| [404 Not Found](https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found) | Failed, invalid certificate. |\n| [406 Not Acceptable](https://www.rfc-editor.org/rfc/rfc9110.html#name-406-not-acceptable) | Failed, expired certificate. |\n| [409 Conflict](https://www.rfc-editor.org/rfc/rfc9110.html#name-409-conflict) | Failed, not all nodes have been updated. |"},{"id":"delete-cluster-certificate","title":"Delete cluster certificate","role":"content","text":"DELETE /v1/cluster/certificates/{string: certificate_name}\n\nRemoves the specified cluster certificate from both CCS and disk\nacross all nodes. Only optional certificates can be deleted through\nthis endpoint. See the [certificates table](https://redis.io/docs/latest/operate/rs/security/certificates) for the list of cluster certificates and their descriptions."},{"id":"delete-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tDELETE /v1/cluster/certificates/<certificate_name>\n\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |"},{"id":"delete-response","title":"Response","role":"returns","text":"Returns a status code that indicates the certificate deletion success or failure."},{"id":"delete-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | Operation successful |\n| [404 Not Found](https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found) | Failed, requested deletion of an unknown certificate |\n| [403 Forbidden](https://www.rfc-editor.org/rfc/rfc9110.html#name-403-forbidden) | Failed, requested deletion of a required certificate |\n| [500 Internal Server Error](https://www.rfc-editor.org/rfc/rfc9110.html#name-500-internal-server-error) | Failed, error while deleting certificate from disk |"}],"examples":[{"id":"get-response-ex0","language":"json","code":"{\n    \"api_cert\": \"-----BEGIN CERTIFICATE-----...-----END CERTIFICATE-----\",\n    \"api_key\": \"-----BEGIN RSA PRIVATE KEY-----...-----END RSA PRIVATE KEY-----\"\n    \"// additional certificates...\"\n}","section_id":"get-response"},{"id":"put-cluster-certificates-ex0","language":"sh","code":"PUT /v1/cluster/certificates","section_id":"put-cluster-certificates"},{"id":"put-certificates-request-ex0","language":"sh","code":"PUT /v1/cluster/certificates","section_id":"put-certificates-request"},{"id":"put-certificates-request-ex1","language":"json","code":"{\n  \"certificates\": [\n    {\n      \"name\": \"proxy\",\n      \"certificate\": \"-----BEGIN CERTIFICATE-----\\n...\\n-----END CERTIFICATE-----\",\n      \"key\": \"-----BEGIN PRIVATE KEY-----\\n...\\n-----END PRIVATE KEY-----\"\n    },\n    {\n      \"name\": \"api\",\n      \"certificate\": \"-----BEGIN CERTIFICATE-----\\n...\\n-----END CERTIFICATE-----\",\n      \"key\": \"-----BEGIN PRIVATE KEY-----\\n...\\n-----END PRIVATE KEY-----\"\n    }\n  ]\n}","section_id":"put-certificates-request"},{"id":"put-cluster-update_cert-ex0","language":"sh","code":"PUT /v1/cluster/update_cert","section_id":"put-cluster-update_cert"},{"id":"put-request-ex0","language":"sh","code":"PUT /v1/cluster/update_cert","section_id":"put-request"},{"id":"put-request-ex1","language":"json","code":"{\n    \"name\": \"certificate1\",\n    \"key\": \"-----BEGIN RSA PRIVATE KEY-----\\n[key_content]\\n-----END RSA PRIVATE KEY-----\",\n    \"certificate\": \"-----BEGIN CERTIFICATE-----\\n[cert_content]\\n-----END CERTIFICATE-----\",\n}","section_id":"put-request"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/cluster/certificates/rotate","title":"Rotate cluster certificates requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/certificates/rotate/","summary":"Rotate cluster certificates requests","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","page_type":"content","content_hash":"65c601f1ec70a842c390047b77727ec5e8869f44711fe08b44b1e1fee25f88de","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [POST](#post-cluster-certificates-rotate) | `/v1/cluster/certificates/rotate` | Regenerate all internal cluster certificates |"},{"id":"post-cluster-certificates-rotate","title":"Rotate cluster certificates","role":"content","text":"POST /v1/cluster/certificates/rotate\n\nRegenerates all _internal_ cluster certificates.\n\nThe certificate rotation will be performed on all nodes within the cluster. If\n\"name\" is provided, only rotate the specified certificate on all nodes within the cluster."},{"id":"post-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tPOST /v1/cluster/certificates/rotate\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |"},{"id":"post-response","title":"Response","role":"returns","text":"Responds with a `200 OK` status code if the internal certificates successfully rotate across the entire cluster."},{"id":"post-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |\n| [400 Bad Request](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1) | Failed, not all nodes have been updated. |\n| [403 Forbidden](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.4) | Unsupported internal certificate rotation. |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/cluster/change_password_hashing_algorithm","title":"Change password hashing algorithm requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/change_password_hashing_algorithm/","summary":"REST API requests to change the hashing algorithm for user passwords.","tags":["docs","operate","rs"],"last_updated":"2025-03-27T11:22:53-05:00","page_type":"content","content_hash":"92ad5389e088dc683db33ec9d94b5708592723381745dcf8005c470450f1b695","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [PATCH](#patch-change-password-hashing-algorithm) | `/v1/cluster/change_password_hashing_algorithm` | Change the hashing policy for user passwords |"},{"id":"patch-change-password-hashing-algorithm","title":"Change password hashing algorithm","role":"content","text":"PATCH /v1/cluster/change_password_hashing_algorithm\n\nChanges the password hashing algorithm for the entire cluster. When you change the hashing algorithm, it rehashes the administrator password and passwords for all users, including default users.\n\nThe hashing algorithm options are `SHA-256` or `PBKDF2`. The default hashing algorithm is `SHA-256`.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [update_cluster](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#update_cluster) |"},{"id":"patch-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tPATCH /v1/cluster/change_password_hashing_algorithm\n\n#### Example JSON body\n\n[code example]\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### Request body\n\nInclude a JSON object `{ \"algorithm\": \"<option>\" }` in the request body. The hashing algorithm options are `SHA-256` or `PBKDF2`."},{"id":"patch-response","title":"Response","role":"returns","text":"Returns a status code that indicates the success or failure of the update."},{"id":"patch-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | Success |\n| [400 Bad Request](https://www.rfc-editor.org/rfc/rfc9110.html#name-400-bad-request) | Supported algorithm must be provided, or this algorithm is already set |"}],"examples":[{"id":"patch-request-ex0","language":"json","code":"{ \"algorithm\": \"PBKDF2\" }","section_id":"patch-request"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/cluster/check","title":"Check all cluster nodes requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/check/","summary":"Requests that run checks on all cluster nodes.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"5527be5cc9bfee3c0dea387cbc59bb9e84b34ec9ef670788177f172fe8a8dfe1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-cluster-check) | `/v1/cluster/check` | Runs checks on all cluster nodes |"},{"id":"get-cluster-check","title":"Check all nodes","role":"content","text":"GET /v1/cluster/check\n\nRuns the following checks on all cluster nodes:\n\n| Check name | Description |\n|-----------|-------------|\n| bootstrap_status | Verifies the local node's bootstrap process completed without errors. |\n| services | Verifies all Redis Software services are running. |\n| port_range | Verifies the [`ip_local_port_range`](https://www.kernel.org/doc/html/latest/networking/ip-sysctl.html) doesn't conflict with the ports Redis Software might assign to shards. |\n| pidfiles | Verifies all active local shards have PID files. |\n| capabilities | Verifies all binaries have the proper capability bits. |\n| existing_sockets | Verifies sockets exist for all processes that require them. |\n| host_settings | Verifies the following:<br />• Linux `overcommit_memory` setting is 1.<br />• `transparent_hugepage` is disabled.<br />• Socket maximum connections setting `somaxconn` is 1024. |\n| tcp_connectivity | Verifies this node can connect to all other alive nodes. |\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_all_nodes_checks](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_all_nodes_checks) |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/cluster/check\n\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns a JSON array with results from all nodes.\n\nWhen errors occur, the server returns a JSON object with `result: false` and an `error` field that provides additional information for each node that had an error. If an error occurs during a check, the `error` field only includes a message for the first check that fails on each node.\n\nPossible `error` messages:\n\n- \"bootstrap request to cnm_http failed,resp_code: ...,resp_content: ...\"\n- \"process ... is not running or not responding (...)\"\n- \"could not communicate with 'supervisorctl': ...\"\n- \"connectivity check failed retrieving ports for testing\"\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | No error |"}],"examples":[{"id":"get-response-ex0","language":"json","code":"{\n  \"cluster_test_result\": false,\n  \"nodes\": [\n    {\n      \"node_uid\": \"1\",\n      \"result\": true\n    },\n    {\n      \"node_uid\": \"2\",\n      \"result\": true\n    },\n    {\n      \"node_uid\": \"3\",\n      \"result\": false,\n      \"error\": \"process alert_mgr is not running or not responding ([Errno 111] Connection refused)\"\n    }\n  ]\n}","section_id":"get-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/cluster/debuginfo","title":"Cluster debug info requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/debuginfo/","summary":"Documents the Redis Software REST API /cluster/debuginfo requests.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"6d5706c982285ea06e8325bcfd8499ec3c94e68189a01a3f3a24150bafd15f41","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-cluster-debuginfo) | `/v1/cluster/debuginfo` | Get debug info from all nodes and databases |"},{"id":"get-cluster-debuginfo","title":"Get cluster debug info","role":"content","text":"GET /v1/cluster/debuginfo\n\nDownloads a tar file that contains debug info from all nodes and databases.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_debugging_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_debugging_info) |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/cluster/debuginfo"},{"id":"get-response","title":"Response","role":"returns","text":"Downloads the debug info in a tar file called `filename.tar.gz`. Extract the files from the tar file to access the debug info for all nodes.\n\n#### Response headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Content-Type | application/x-gzip | Media type of request/response body |\n| Content-Length | 653350 | Length of the response body in octets |\n| Content-Disposition | attachment; filename=debuginfo.tar.gz | Display response in browser or download as attachment |"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success. |\n| [500 Internal Server Error](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1) | Failed to get debug info. |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/cluster","title":"Cluster requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/","summary":"Cluster settings requests","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","children":[{"id":"operate/rs/references/rest-api/requests/cluster/actions","summary":"Cluster action requests","title":"Cluster actions requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/actions/"},{"id":"operate/rs/references/rest-api/requests/cluster/alerts","summary":"Cluster alert requests","title":"Cluster alerts requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/alerts/"},{"id":"operate/rs/references/rest-api/requests/cluster/auditing-db-conns","summary":"Auditing database connections requests","title":"Auditing database connections requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/auditing-db-conns/"},{"id":"operate/rs/references/rest-api/requests/cluster/certificates","summary":"Cluster certificates requests","title":"Cluster certificates requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/certificates/"},{"id":"operate/rs/references/rest-api/requests/cluster/change_password_hashing_algorithm","summary":"REST API requests to change the hashing algorithm for user passwords.","title":"Change password hashing algorithm requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/change_password_hashing_algorithm/"},{"id":"operate/rs/references/rest-api/requests/cluster/check","summary":"Requests that run checks on all cluster nodes.","title":"Check all cluster nodes requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/check/"},{"id":"operate/rs/references/rest-api/requests/cluster/debuginfo","summary":"Documents the Redis Software REST API /cluster/debuginfo requests.","title":"Cluster debug info requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/debuginfo/"},{"id":"operate/rs/references/rest-api/requests/cluster/ldap","summary":"LDAP configuration requests","title":"Cluster LDAP requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/ldap/"},{"id":"operate/rs/references/rest-api/requests/cluster/module-capabilities","summary":"Redis module capabilities requests","title":"Cluster module capabilities requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/module-capabilities/"},{"id":"operate/rs/references/rest-api/requests/cluster/policy","summary":"Cluster policy requests","title":"Cluster policy requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/policy/"},{"id":"operate/rs/references/rest-api/requests/cluster/services_configuration","summary":"Cluster services configuration requests","title":"Cluster services configuration requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/services_configuration/"},{"id":"operate/rs/references/rest-api/requests/cluster/sso","summary":"Single sign-on (SSO) configuration requests","title":"Single sign-on requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/sso/"},{"id":"operate/rs/references/rest-api/requests/cluster/stats","summary":"Cluster statistics requests","title":"Cluster stats requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/stats/"}],"page_type":"content","content_hash":"711556a144907fee63e9788520f942991b4a35e8689d94104f55054d59abfc38","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-cluster) | `/v1/cluster` | Get cluster info |\n| [PUT](#put-cluster) | `/v1/cluster` | Update cluster settings |"},{"id":"get-cluster","title":"Get cluster info","role":"content","text":"GET /v1/cluster\n\nGet cluster info.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_cluster_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_cluster_info) |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/cluster \n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns a [cluster object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cluster).\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |"},{"id":"put-cluster","title":"Update cluster settings","role":"content","text":"PUT /v1/cluster\n\nUpdate cluster settings.\n\nIf called with the `dry_run` URL query string, the function will\nvalidate the [cluster object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cluster), but will not apply the requested\nchanges.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [update_cluster](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#update_cluster) |"},{"id":"put-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tPUT /v1/cluster \n\n#### Example JSON body\n\n[code example]\n\nThe above request will enable email alerts and alert reporting for node failures and node removals.\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| dry_run | string | Validate but don't apply the new cluster settings |\n\n#### Request body\n\nInclude a [cluster object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cluster) with updated fields in the request body."},{"id":"put-response","title":"Response","role":"returns","text":"#### Example JSON body\n\n[code example]"},{"id":"put-error-codes","title":"Error codes","role":"errors","text":"When errors are reported, the server may return a JSON object with `error_code` and `message` field that provide additional information. The following are possible `error_code` values:\n\n| Code | Description |\n|------|-------------|\n| bad_nginx_conf | • Designated port is already bound.<br></br>• nginx configuration is illegal. | \n| bad_debuginfo_path | • Debuginfo path doesn't exist.<br></br>• Debuginfo path is inaccessible. | \n| config_edit_conflict | Cluster config was edited by another source simultaneously. |"},{"id":"put-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error. |\n| [400 Bad Request](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1) | Bad content provided. |"}],"examples":[{"id":"get-response-ex0","language":"json","code":"{\n   \"name\": \"my-rlec-cluster\",\n   \"alert_settings\": { \"...\" },\n   \"created_time\": \"2015-04-29T09:09:25Z\",\n   \"email_alerts\": false,\n   \"email_from\": \"\",\n   \"rack_aware\": false,\n   \"smtp_host\": \"\",\n   \"smtp_password\": \"\",\n   \"smtp_port\": 25,\n   \"smtp_tls_mode\": \"none\",\n   \"smtp_username\": \"\"\n}","section_id":"get-response"},{"id":"put-request-ex0","language":"json","code":"{\n    \"email_alerts\": true,\n    \"alert_settings\": {\n        \"node_failed\": true,\n        \"node_memory\": {\n            \"enabled\": true,\n            \"threshold\": \"80\"\n        }\n    }\n}","section_id":"put-request"},{"id":"put-response-ex0","language":"json","code":"{\n    \"name\": \"mycluster.mydomain.com\",\n    \"email_alerts\": true,\n    \"alert_settings\": {\n        \"node_failed\": true,\n        \"node_memory\": {\n            \"enabled\": true,\n            \"threshold\": \"80\"\n        }\n    },\n    \"// additional fields...\"\n}","section_id":"put-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/cluster/ldap","title":"Cluster LDAP requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/ldap/","summary":"LDAP configuration requests","tags":["docs","operate","rs"],"last_updated":"2025-06-03T12:45:59-05:00","page_type":"content","content_hash":"f357f77086ab9a6c898f3195ab90a7dcb9b12bfeb6c0994951e1e3b1dba40f1e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-cluster-ldap) | `/v1/cluster/ldap` | Get LDAP configuration |\n| [PUT](#put-cluster-ldap) | `/v1/cluster/ldap` | Set/update LDAP configuration |\n| [DELETE](#delete-cluster-ldap) | `/v1/cluster/ldap` | Delete LDAP configuration |"},{"id":"get-cluster-ldap","title":"Get LDAP configuration","role":"content","text":"GET /v1/cluster/ldap\n\nGet the LDAP configuration.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_ldap_config](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_ldap_config) |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/cluster/ldap \n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns an [LDAP object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/ldap).\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success |"},{"id":"put-cluster-ldap","title":"Update LDAP configuration","role":"content","text":"PUT /v1/cluster/ldap\n\nSet or update the cluster LDAP configuration.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [config_ldap](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#config_ldap) |"},{"id":"put-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tPOST /v1/cluster/ldap \n\n#### Example JSON body\n\n[code example]\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n\n#### Request body\n\nInclude an [LDAP object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/ldap) with updated fields in the request body."},{"id":"put-response","title":"Response","role":"returns","text":"Returns a status code. If an error occurs, the response body may include an error code and message with more details."},{"id":"put-error-codes","title":"Error codes","role":"errors","text":"Possible `error_code` values:\n\n| Code | Description |\n|------|-------------|\n| illegal_fields_combination | An unacceptable combination of fields was specified for the configuration object (e.g.: two mutually-exclusive fields), or a required field is missing.|"},{"id":"put-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success, LDAP config has been set. |\n| [400 Bad Request](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1) | Bad or missing configuration parameters. |"},{"id":"delete-cluster-ldap","title":"Delete LDAP configuration","role":"content","text":"DELETE /v1/cluster/ldap\n\nClear the LDAP configuration.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [config_ldap](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#config_ldap) |"},{"id":"delete-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tDELETE /v1/cluster/ldap \n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |"},{"id":"delete-response","title":"Response","role":"returns","text":"Returns a status code."},{"id":"delete-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success |"}],"examples":[{"id":"get-response-ex0","language":"json","code":"{\n   \"bind_dn\": \"rl_admin\",\n   \"bind_pass\": \"***\",\n   \"ca_cert\": \"\",\n   \"control_plane\": false,\n   \"data_plane\": false,\n   \"dn_group_attr\": \"MemberOf\",\n   \"dn_group_query\": {\n      \"base\": \"DC=windowsAD,DC=qa,DC=redislabs,DC=com\",\n      \"filter\": \"member=%D\",\n      \"scope\": \"subtree\"\n   },\n   \"starttls\": false,\n   \"uris\": [\"ldap://ldap.example.org:636\"],\n   \"user_dn_query\": {\n      \"base\": \"DC=windowsAD,DC=qa,DC=redislabs,DC=com\",\n      \"filter\": \"(&(objectClass=Person)(cn=%u))\",\n      \"scope\": \"subtree\"\n   },\n   \"user_dn_template\": \"cn=%u, ou=users,dc=example,dc=org\"\n}","section_id":"get-response"},{"id":"put-request-ex0","language":"json","code":"{\n    \"uris\": [\n       \"ldap://ldap.redislabs.com:389\"\n    ],\n    \"bind_dn\": \"rl_admin\",\n    \"bind_pass\": \"secret\",\n    \"user_dn_template\": \"cn=%u,dc=example,dc=org\",\n    \"dn_group_attr\": \"MemberOf\",\n    \"directory_timeout_s\": 5\n}","section_id":"put-request"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/cluster/module-capabilities","title":"Cluster module capabilities requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/module-capabilities/","summary":"Redis module capabilities requests","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","page_type":"content","content_hash":"ebd888852a34b0e58dea9c858c10ee5609ebc2deaf7b29eb6aaefa438bcc7df7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-cluster-module-capabilities) | `/v1/cluster/module-capabilities` | List possible Redis module capabilities |"},{"id":"get-cluster-module-capabilities","title":"List Redis module capabilities","role":"content","text":"GET /v1/cluster/module-capabilities\n\nList possible Redis module capabilities.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_cluster_modules](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_cluster_modules) |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/cluster/module-capabilities \n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | \\*/\\* | Accepted media type |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns a JSON object that contains a list of capability names and descriptions.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |"}],"examples":[{"id":"get-response-ex0","language":"json","code":"{\n  \"all_capabilities\": [\n    {\"name\": \"types\", \"desc\": \"module has its own types and not only operate on existing redis types\"},\n    {\"name\": \"no_multi_key\", \"desc\": \"module has no methods that operate on multiple keys\"}\n    \"// additional capabilities...\"\n  ]\n}","section_id":"get-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/cluster/policy","title":"Cluster policy requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/policy/","summary":"Cluster policy requests","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","page_type":"content","content_hash":"4f07a5784315c5f241265e02c72ea29942f430a68f08317f973301e977076f67","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-cluster-policy) | `/v1/cluster/policy` | Get cluster policy settings |\n| [PUT](#put-cluster-policy) | `/v1/cluster/policy` | Update cluster policy settings |"},{"id":"get-cluster-policy","title":"Get cluster policy","role":"content","text":"GET /v1/cluster/policy\n\nGets the cluster's current policy settings.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_cluster_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_cluster_info) |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/cluster/policy \n\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns a [cluster settings object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cluster_settings).\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | Success |"},{"id":"put-cluster-policy","title":"Update cluster policy","role":"content","text":"PUT /v1/cluster/policy\n\nUpdate cluster policy settings.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [update_cluster](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#update_cluster) |"},{"id":"put-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tPUT /v1/cluster/policy\n\n#### Example JSON body\n\n[code example]\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### Request body\n\nInclude a [cluster settings object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cluster_settings) with updated fields in the request body."},{"id":"put-response","title":"Response","role":"returns","text":"Returns a status code that indicates the success or failure of the cluster settings update."},{"id":"put-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | Success |\n| [400 Bad Request](https://www.rfc-editor.org/rfc/rfc9110.html#name-400-bad-request) | Failed to set parameters |"}],"examples":[{"id":"get-response-ex0","language":"json","code":"{\n    \"db_conns_auditing\": false,\n    \"default_non_sharded_proxy_policy\": \"single\",\n    \"default_provisioned_redis_version\": \"6.0\",\n    \"default_sharded_proxy_policy\": \"single\",\n    \"default_shards_placement\": \"dense\",\n    \"redis_upgrade_policy\": \"major\",\n    \"// additional fields...\"\n}","section_id":"get-response"},{"id":"put-request-ex0","language":"json","code":"{\n    \"default_shards_placement\": \"sparse\",\n    \"default_sharded_proxy_policy\": \"all-nodes\"\n}","section_id":"put-request"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/cluster/services_configuration","title":"Cluster services configuration requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/services_configuration/","summary":"Cluster services configuration requests","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","page_type":"content","content_hash":"2f045a0960a06a01072920ad71f93a632821a4232c41a27cf2908a500af2e3a8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-cluster-services_config) | `/v1/cluster/services_configuration` | Get cluster services settings |\n| [PUT](#put-cluster-services_config) | `/v1/cluster/services_configuration` | Update cluster services settings |"},{"id":"get-cluster-services_config","title":"Get cluster services configuration","role":"content","text":"GET /v1/cluster/services_configuration\n\nGet cluster services settings.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_cluster_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_cluster_info) |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/cluster/services_configuration \n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns a [services configuration object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration).\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |"},{"id":"put-cluster-services_config","title":"Update cluster services configuration","role":"content","text":"PUT /v1/cluster/services_configuration\n\nUpdate the cluster services settings.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [update_cluster](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#update_cluster) |"},{"id":"put-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tPUT /v1/cluster/services_configuration \n\n#### Example JSON body\n\n[code example]\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### Request body\n\nInclude a [services configuration object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration) with updated fields in the request body."},{"id":"put-response","title":"Response","role":"returns","text":"Returns the updated [services configuration object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration).\n\n#### Example JSON body\n\n[code example]"},{"id":"put-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |"}],"examples":[{"id":"get-response-ex0","language":"json","code":"{\n     \"cm_server\": {\n         \"operating_mode\": \"disabled\"\n     },\n     \"mdns_server\": {\n         \"operating_mode\": \"enabled\"\n     },\n     \"// additional services...\"\n}","section_id":"get-response"},{"id":"put-request-ex0","language":"json","code":"{\n     \"cm_server\": {\n         \"operating_mode\": \"disabled\"\n     },\n     \"// additional services...\"\n}","section_id":"put-request"},{"id":"put-response-ex0","language":"json","code":"{\n     \"cm_server\": {\n         \"operating_mode\": \"disabled\"\n     },\n     \"mdns_server\": {\n         \"operating_mode\": \"enabled\"\n     },\n     \"// additional services...\"\n}","section_id":"put-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/cluster/sso","title":"Single sign-on requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/sso/","summary":"Single sign-on (SSO) configuration requests","tags":["docs","operate","rs"],"last_updated":"2025-12-22T11:45:26-06:00","page_type":"content","content_hash":"1c5f322794f404dfcd2d56e920e4e935d2557e948c4b25f1bd44b4328662dd97","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-cluster-sso) | `/v1/cluster/sso` | Get SSO configuration |\n| [PUT](#put-cluster-sso) | `/v1/cluster/sso` | Set or update SSO configuration |\n| [DELETE](#delete-cluster-sso) | `/v1/cluster/sso` | Clear SSO configuration |\n| [GET](#get-cluster-sso-saml-metadata) | `/v1/cluster/sso/saml/metadata/sp` | Get SAML service provider metadata |\n| [POST](#post-cluster-sso-saml-metadata) | `/v1/cluster/sso/saml/metadata/idp` | Upload SAML identity provider metadata |"},{"id":"get-cluster-sso","title":"Get SSO configuration","role":"content","text":"GET /v1/cluster/sso\n\nGet the single sign-on configuration as JSON.\n\n#### Required permissions\n\n| Permission name | Roles |\n|-----------------|-------|\n| [view_sso](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_sso) | admin<br />user_manager |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/cluster/sso\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns an [SSO object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/sso).\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | Success |"},{"id":"put-cluster-sso","title":"Update SSO configuration","role":"content","text":"PUT /v1/cluster/sso\n\nSet or update the cluster single sign-on configuration.\n\n#### Required permissions\n\n| Permission name | Roles |\n|-----------------|-------|\n| [config_sso](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#config_sso) | admin<br />user_manager |"},{"id":"put-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tPUT /v1/cluster/sso\n\n#### Example JSON body\n\n[code example]\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### Request body\n\nInclude an [SSO object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/sso) with updated fields in the request body."},{"id":"put-response","title":"Response","role":"returns","text":"Returns a status code. If an error occurs, the response body can include an error code and message with more details."},{"id":"put-error-codes","title":"Error codes","role":"errors","text":"Possible `error_code` values:\n\n| Code | Description |\n|------|-------------|\n| missing_param | A required parameter is missing while SSO is being enabled |\n| missing_certificate | SSO certificate is not found while SSO is being enabled |"},{"id":"put-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | Success, SSO config has been set |\n| [400 Bad Request](https://www.rfc-editor.org/rfc/rfc9110.html#name-400-bad-request) | Bad or missing configuration parameters |\n| [406 Not Acceptable](https://www.rfc-editor.org/rfc/rfc9110.html#name-406-not-acceptable) | Missing required certificate |"},{"id":"delete-cluster-sso","title":"Delete SSO configuration","role":"content","text":"DELETE /v1/cluster/sso\n\nClear the single sign-on configuration.\n\n#### Required permissions\n\n| Permission name | Roles |\n|-----------------|-------|\n| [config_sso](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#config_sso) | admin<br />user_manager |"},{"id":"delete-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tDELETE /v1/cluster/sso\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |"},{"id":"delete-response","title":"Response","role":"returns","text":"Returns a status code."},{"id":"delete-error-codes","title":"Error codes","role":"errors","text":"Possible `error_code` values:\n\n| Code | Description |\n|------|-------------|\n| delete_certificate_error | An error occurred during SSO certificate deletion |"},{"id":"delete-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | Success |\n| [500 Internal Server Error](https://www.rfc-editor.org/rfc/rfc9110.html#name-500-internal-server-error) | Error during deletion |"},{"id":"get-cluster-sso-saml-metadata","title":"Get SAML service provider metadata","role":"content","text":"GET /v1/cluster/sso/saml/metadata/sp\n\nGenerates and returns the SAML2 service provider metadata XML.\n\n#### Required permissions\n\n| Permission name | Roles |\n|-----------------|-------|\n| [view_sso](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_sso) | admin<br />user_manager |"},{"id":"get-metadata-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/cluster/sso/saml/metadata/sp\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/samlmetadata+xml | Accepted media type |"},{"id":"get-metadata-response","title":"Response","role":"returns","text":"Returns SAML2 service provider metadata as XML.\n\n#### Example response body\n\n[code example]"},{"id":"get-metadata-error-codes","title":"Error codes","role":"errors","text":"Possible `error_code` values:\n\n| Code | Description |\n|------|-------------|\n| missing_certificate | Service certificate is missing |\n| saml_metadata_generation_error | An error occurred while generating the XML metadata |"},{"id":"get-metadata-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | Success |\n| [406 Not Acceptable](https://www.rfc-editor.org/rfc/rfc9110.html#name-406-not-acceptable) | Missing required service certificate |\n| [500 Internal Server Error](https://www.rfc-editor.org/rfc/rfc9110.html#name-500-internal-server-error) | Unexpected error when generating metadata |"},{"id":"post-cluster-sso-saml-metadata","title":"Upload SAML identity provider metadata","role":"content","text":"POST /v1/cluster/sso/saml/metadata/idp\n\nUploads and validates the SAML2 identity provider metadata XML.\n\n#### Required permissions\n\n| Permission name | Roles |\n|-----------------|-------|\n| [config_sso](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#config_sso) | admin<br />user_manager |"},{"id":"post-metadata-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tPOST /v1/cluster/sso/saml/metadata/idp\n\n#### Example JSON body\n\n[code example]\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### Request body\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| idp_metadata | string | Base64-encoded SAML2 identity provider metadata XML |"},{"id":"post-metadata-response","title":"Response","role":"returns","text":"Returns an [SSO object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/sso) with the updated configuration.\n\n#### Example JSON body\n\n[code example]"},{"id":"post-metadata-error-codes","title":"Error codes","role":"errors","text":"Possible `error_code` values:\n\n| Code | Description |\n|------|-------------|\n| saml_metadata_validation_error | IdP metadata failed configuration validation checks |\n| saml_metadata_parsing_error | IdP metadata is not a valid base64-encoded XML |\n| missing_certificate | SSO certificate is not found while SSO is being enabled |"},{"id":"post-metadata-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | Success |\n| [400 Bad Request](https://www.rfc-editor.org/rfc/rfc9110.html#name-400-bad-request) | Bad or missing parameters |\n| [406 Not Acceptable](https://www.rfc-editor.org/rfc/rfc9110.html#name-406-not-acceptable) | Missing required service certificate |"}],"examples":[{"id":"get-response-ex0","language":"json","code":"{\n   \"control_plane\": true,\n   \"protocol\": \"saml2\",\n   \"enforce_control_plane\": false,\n   \"issuer\": {\n         \"id\": \"urn:sso:example:idp\",\n         \"login_url\": \"https://idp.example.com/sso/saml\",\n         \"logout_url\": \"https://idp.example.com/sso/slo\",\n         \"metadata\": \"<base64 encoded metadata>\"\n   },\n   \"service\": {\n         \"address\": \"https://hostname:port\",\n         \"saml2\": {\n             \"entity_id\": \"https://cnm.cluster.fqdn/sp\",\n             \"acs_url\": \"https://cnm.cluster.fqdn/v1/cluster/sso/saml/acs\",\n             \"slo_url\": \"https://cnm.cluster.fqdn/v1/cluster/sso/saml/slo\"\n         }\n   }\n}","section_id":"get-response"},{"id":"put-request-ex0","language":"json","code":"{\n   \"control_plane\": false,\n   \"protocol\": \"saml2\",\n   \"enforce_control_plane\": false,\n   \"issuer\": {\n         \"id\": \"urn:sso:example:idp\",\n         \"login_url\": \"https://idp.example.com/sso/saml\",\n         \"logout_url\": \"https://idp.example.com/sso/slo\"\n   },\n   \"service\": {\n         \"address\": \"https://hostname:port\"\n   }\n}","section_id":"put-request"},{"id":"get-metadata-response-ex0","language":"xml","code":"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<md:EntityDescriptor\nxmlns:md=\"urn:oasis:names:tc:SAML:2.0:metadata\">\n    ...\n</md:EntityDescriptor>","section_id":"get-metadata-response"},{"id":"post-metadata-request-ex0","language":"json","code":"{\n   \"idp_metadata\": \"YWp3cjkwcHR1eWF3MHJ0eTkwYXc0eXQwOW4...\"\n}","section_id":"post-metadata-request"},{"id":"post-metadata-response-ex0","language":"json","code":"{\n   \"control_plane\": true,\n   \"protocol\": \"saml2\",\n   \"enforce_control_plane\": false,\n   \"issuer\": {\n         \"id\": \"urn:sso:example:idp\",\n         \"login_url\": \"https://idp.example.com/sso/saml\",\n         \"logout_url\": \"https://idp.example.com/sso/slo\"\n   },\n   \"service\": {\n         \"saml2\": {\n             \"entity_id\": \"https://cnm.cluster.fqdn/sp\",\n             \"acs_url\": \"https://cnm.cluster.fqdn/v1/cluster/sso/saml/acs\",\n             \"slo_url\": \"https://cnm.cluster.fqdn/v1/cluster/sso/saml/slo\"\n         }\n   }\n}","section_id":"post-metadata-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/cluster/stats","title":"Cluster stats requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/stats/","summary":"Cluster statistics requests","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","children":[{"id":"operate/rs/references/rest-api/requests/cluster/stats/last","summary":"Most recent cluster statistics requests","title":"Cluster last stats requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/stats/last/"}],"page_type":"content","content_hash":"f4d85750fb449c08ee5aeb399b4832f2d73a3e5c8791a8cef66093aacb84611b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The v1 statistics REST API endpoints are deprecated as of Redis Software version 7.22. Use the [metrics stream engine for monitoring v2](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine) instead for real-time monitoring and metrics collection.\n\n\n| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-cluster-stats) | `/v1/cluster/stats` | Get cluster stats |"},{"id":"get-cluster-stats","title":"Get cluster stats","role":"content","text":"[code example]\n\nGet cluster statistics."},{"id":"permissions","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [view_cluster_stats](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_cluster_stats) | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| interval | string | Time interval for which we want stats: 1sec/10sec/5min/15min/1hour/12hour/1week (optional) |\n| stime | ISO_8601 | Start time from which we want the stats. Should comply with the [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601) format (optional) |\n| etime | ISO_8601 | End time after which we don't want the stats. Should comply with the [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601) format (optional) |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns [statistics](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/statistics) for the cluster.\n\n#### Example JSON body\n\n[code example]"},{"id":"example-requests","title":"Example requests","role":"example","text":"#### cURL\n\n[code example]\n\n#### Python\n\n[code example]"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |\n| [500 Internal Server Error](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1) | Internal server error |"}],"examples":[{"id":"get-cluster-stats-ex0","language":"sh","code":"GET /v1/cluster/stats","section_id":"get-cluster-stats"},{"id":"get-request-ex0","language":"sh","code":"GET /v1/cluster/stats/1?interval=1hour&stime=2014-08-28T10:00:00Z","section_id":"get-request"},{"id":"get-response-ex0","language":"json","code":"{\n  \"intervals\": [\n    {\n      \"interval\": \"1hour\",\n      \"stime\": \"2015-05-27T12:00:00Z\",\n      \"etime\": \"2015-05-28T12:59:59Z\",\n      \"conns\": 0.0,\n      \"cpu_idle\": 0.8533959401503577,\n      \"cpu_system\": 0.01602159448549579,\n      \"cpu_user\": 0.08721123782294203,\n      \"egress_bytes\": 1111.2184745131947,\n      \"ephemeral_storage_avail\": 3406676307.1449075,\n      \"ephemeral_storage_free\": 4455091440.360014,\n      \"free_memory\": 2745470765.673594,\n      \"ingress_bytes\": 220.84083194769272,\n      \"interval\": \"1week\",\n      \"persistent_storage_avail\": 3406676307.1533995,\n      \"persistent_storage_free\": 4455091440.088265,\n      \"total_req\": 0.0\n    },\n    {\n      \"interval\": \"1hour\",\n      \"stime\": \"2015-05-27T13:00:00Z\",\n      \"etime\": \"2015-05-28T13:59:59Z\",\n      \"// additional fields...\"\n    }\n  ]\n}","section_id":"get-response"},{"id":"example-requests-ex0","language":"sh","code":"$ curl -k -u \"[username]:[password]\" -X GET\n        https://[host][:port]/v1/cluster/stats?interval=1hour","section_id":"example-requests"},{"id":"example-requests-ex1","language":"python","code":"import requests\n\nurl = \"https://[host][:port]/v1/cluster/stats?interval=1hour\"\nauth = (\"[username]\", \"[password]\")\n\nresponse = requests.request(\"GET\", url, auth=auth)\n\nprint(response.text)","section_id":"example-requests"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/cluster/stats/last","title":"Cluster last stats requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/stats/last/","summary":"Most recent cluster statistics requests","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","page_type":"content","content_hash":"b7757c03e9175ed9110e15703e40f040decb817123393d32cbcc850f2892ef50","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-cluster-stats-last) | `/v1/cluster/stats/last` | Get most recent cluster stats |"},{"id":"get-cluster-stats-last","title":"Get latest cluster stats","role":"content","text":"GET /v1/cluster/stats/last\n\nGet the most recent cluster statistics.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_cluster_stats](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_cluster_stats) |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/cluster/stats/last?interval=1sec&stime=2015-10-14T06:44:00Z \n\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| interval | string | Time interval for which we want stats: 1sec/10sec/5min/15min/1hour/12hour/1week. Default: 1sec. (optional) |\n| stime | ISO_8601 | Start time from which we want the stats. Should comply with the [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601) format (optional) |\n| etime | ISO_8601 | End time after which we don't want the stats. Should comply with the [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601) format (optional) |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns the most recent [statistics](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/statistics) for the cluster.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |\n| [500 Internal Server Error](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1) | Internal server error |"}],"examples":[{"id":"get-response-ex0","language":"json","code":"{\n    \"conns\": 0.0,\n    \"cpu_idle\": 0.8424999999988358,\n    \"cpu_system\": 0.01749999999992724,\n    \"cpu_user\": 0.08374999999978172,\n    \"egress_bytes\": 7403.0,\n    \"ephemeral_storage_avail\": 151638712320.0,\n    \"ephemeral_storage_free\": 162375925760.0,\n    \"etime\": \"2015-10-14T06:44:01Z\",\n    \"free_memory\": 5862400000.0,\n    \"ingress_bytes\": 7469.0,\n    \"interval\": \"1sec\",\n    \"persistent_storage_avail\": 151638712320.0,\n    \"persistent_storage_free\": 162375925760.0,\n    \"stime\": \"2015-10-14T06:44:00Z\",\n    \"total_req\": 0.0\n}","section_id":"get-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/cm_settings","title":"Cluster Manager settings requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cm_settings/","summary":"REST API requests for Cluster Manager UI settings","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","children":[],"page_type":"content","content_hash":"e34f00afca39116f664af3d470b7955b852b6f5c6181900d027c2dca340659c8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-cm-settings) | `/v1/cm_settings` | Get Cluster Manager UI settings |\n| [PUT](#put-cm-settings) | `/v1/cm_settings` | Update Cluster Manager UI settings |"},{"id":"get-cm-settings","title":"Get CM settings","role":"content","text":"[code example]\n\nGet Cluster Manager UI settings."},{"id":"permissions","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [view_cluster_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_cluster_info) | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns a [cm_settings object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cm_settings).\n\n#### Example JSON body\n\n[code example]\n\n#### Status codes {#get-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | No error |"},{"id":"put-cm-settings","title":"Update CM settings","role":"content","text":"[code example]\n\nUpdate Cluster Manager UI settings."},{"id":"permissions-1","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [update_cluster](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#update_cluster) | admin |"},{"id":"put-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Example JSON body\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n\n#### Body\n\nInclude a [cm_settings object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cm_settings) with updated fields in the request body."},{"id":"put-response","title":"Response","role":"returns","text":"Returns a [cm_settings object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cm_settings) with the updated fields.\n\n#### Example JSON body\n\n[code example]\n\n#### Status codes {#put-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | Success, time zone config has been set. |\n| [400 Bad Request](https://www.rfc-editor.org/rfc/rfc9110.html#name-400-bad-request) | Bad or missing configuration parameters. |"}],"examples":[{"id":"get-cm-settings-ex0","language":"sh","code":"GET /v1/cm_settings","section_id":"get-cm-settings"},{"id":"get-request-ex0","language":"sh","code":"GET /v1/cm_settings","section_id":"get-request"},{"id":"get-response-ex0","language":"json","code":"{\n    \"timezone\": \"UTC\"\n}","section_id":"get-response"},{"id":"put-cm-settings-ex0","language":"sh","code":"PUT /v1/cm_settings","section_id":"put-cm-settings"},{"id":"put-request-ex0","language":"sh","code":"PUT /v1/cm_settings","section_id":"put-request"},{"id":"put-request-ex1","language":"json","code":"{\n    \"timezone\": \"US/Pacific\"\n}","section_id":"put-request"},{"id":"put-response-ex0","language":"json","code":"{\n    \"timezone\": \"US/Pacific\"\n}","section_id":"put-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/crdb_tasks","title":"CRDB tasks requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/crdb_tasks/","summary":"Active-Active database task status requests","tags":["docs","operate","rs"],"last_updated":"2025-06-30T08:24:08-05:00","children":[],"page_type":"content","content_hash":"338837eaaadc5c2107c0516eae28ffb714810556e5cc765871d5a53097018cab","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-all-crdb_tasks) | `/v1/crdb_tasks` | Get all running tasks |\n| [GET](#get-crdb_task) | `/v1/crdb_tasks/{task_id}` | Get the status of an executed task |\n| [POST](#post-crdb_task-cancel) | `/v1/crdb_tasks/{task_id}/actions/cancel` | Cancel a running or queued task |"},{"id":"get-all-crdb_tasks","title":"Get all running tasks","role":"content","text":"[code example]\n\nGet all running tasks."},{"id":"get-all-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]"},{"id":"get-all-response","title":"Response","role":"returns","text":"Returns a JSON array of [CRDB task objects](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb_task).\n\n#### Status codes {#get-all-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | A list of running tasks. |\n| [401 Unauthorized](https://www.rfc-editor.org/rfc/rfc9110.html#name-401-unauthorized) | Unauthorized request. Invalid credentials. |"},{"id":"get-crdb_task","title":"Get task status","role":"content","text":"GET /v1/crdb_tasks/{task_id}\n\nGet the status of an executed task.\n\nThe status of a completed task is kept for 500 seconds by default."},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n    GET /v1/crdb_tasks/1\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| X-Result-TTL | integer | Task time to live |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| task_id | string | Task ID |\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| verbose | boolean | Return detailed task information (optional) |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns a [CRDB task object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb_task)."},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | Task status. |\n| [401 Unauthorized](https://www.rfc-editor.org/rfc/rfc9110.html#name-401-unauthorized) | Unauthorized request. Invalid credentials |\n| [404 Not Found](https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found) | Task not found. |"},{"id":"post-crdb_task-cancel","title":"Cancel task","role":"content","text":"[code example]\n\nGracefully cancels a running or queued task.\n\nA task that already reached the commit phase cannot be canceled."},{"id":"post-cancel-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| task_id | string | Task ID |\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| force | boolean | Cancel even if the task is in the commit phase. **WARNING**: This can break the CRDB in the cluster configuration store (CCS), so use with caution. (optional) |"},{"id":"post-cancel-response","title":"Response","role":"returns","text":"Returns a status code.\n\n#### Status codes {#post-cancel-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | The request has been accepted. |\n| [401 Unauthorized](https://www.rfc-editor.org/rfc/rfc9110.html#name-401-unauthorized) | Unauthorized request. Invalid credentials. |\n| [404 Not Found](https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found) | Task not found. |\n| [406 Not Acceptable](https://www.rfc-editor.org/rfc/rfc9110.html#name-406-not-acceptable) | Task cannot be canceled. |\n| [409 Conflict](https://www.rfc-editor.org/rfc/rfc9110.html#name-409-conflict) | Failed to cancel task. |"}],"examples":[{"id":"get-all-crdb_tasks-ex0","language":"sh","code":"GET /v1/crdb_tasks","section_id":"get-all-crdb_tasks"},{"id":"get-all-request-ex0","language":"sh","code":"GET /v1/crdb_tasks","section_id":"get-all-request"},{"id":"post-crdb_task-cancel-ex0","language":"sh","code":"POST /v1/crdb_tasks/{task_id}/actions/cancel","section_id":"post-crdb_task-cancel"},{"id":"post-cancel-request-ex0","language":"sh","code":"POST /v1/crdb_tasks/1/actions/cancel","section_id":"post-cancel-request"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/crdbs/flush","title":"CRDB flush requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/crdbs/flush/","summary":"Flush Active-Active database requests","tags":["docs","operate","rs"],"last_updated":"2026-04-24T09:17:44-05:00","page_type":"content","content_hash":"93be4a6b2b362dc03a2bfedc24c4f0afdf51b035739c44768427d81e5cff902b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [PUT](#put-crdbs-flush) | `/v1/crdbs/{crdb_guid}/flush` | Flush an Active-Active database |"},{"id":"put-crdbs-flush","title":"Flush an Active-Active database","role":"content","text":"[code example]\n\nFlush an Active-Active database.\n\nRun flush from only one participating cluster. The flush operation propagates to all other clusters automatically."},{"id":"put-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| X-Task-ID | string | Specified task ID |\n| X-Result-TTL | integer | Time (in seconds) to keep task result |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| crdb_guid | string | Globally unique Active-Active database ID (GUID) |\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| async_flush | boolean | Perform asynchronous flush operation (optional) |"},{"id":"put-response","title":"Response","role":"returns","text":"Returns a [CRDB task object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb_task).\n\n#### Status codes {#put-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Action was successful. |\n| [400 Bad Request](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1) | The request is invalid or malformed. |\n| [401 Unauthorized](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2) | Unauthorized request. Invalid credentials |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Configuration or Active-Active database not found. |\n| [406 Not Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | Configuration cannot be accepted, typically because it was already committed. |"}],"examples":[{"id":"put-crdbs-flush-ex0","language":"sh","code":"PUT /v1/crdbs/{crdb_guid}/flush","section_id":"put-crdbs-flush"},{"id":"put-request-ex0","language":"sh","code":"PUT /v1/crdbs/552bbccb-99f3-4142-bd17-93d245f0bc79/flush","section_id":"put-request"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/crdbs/health_report","title":"CRDB health report requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/crdbs/health_report/","summary":"Active-Active database health report requests","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","page_type":"content","content_hash":"b0243427e08cb527ad9038f403b32fc5df2a1b3561f5920f9459b0021d50b0ba","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-crdbs-health) | `/v1/crdbs/{crdb_guid}/health_report` | Get a health report for an Active-Active database |"},{"id":"get-crdbs-health","title":"Get health report","role":"content","text":"GET /v1/crdbs/{crdb_guid}/health_report\n\nGet a health report for an Active-Active database."},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n    GET /v1/crdbs/{crdb_guid}/health_report\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| crdb_guid | string | Globally unique Active-Active database ID (GUID) |\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| instance_id | integer | The request retrieves information from the specified Active-Active database instance. If this instance doesn’t exist, the request retrieves information from all instances. (optional) |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns a JSON array of [CRDB health report objects](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb/health_report)."},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Action was successful. |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Configuration or Active-Active database not found. |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/crdbs","title":"CRDBs requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/crdbs/","summary":"Active-Active database requests","tags":["docs","operate","rs"],"last_updated":"2026-07-06T14:48:53-05:00","children":[{"id":"operate/rs/references/rest-api/requests/crdbs/flush","summary":"Flush Active-Active database requests","title":"CRDB flush requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/crdbs/flush/"},{"id":"operate/rs/references/rest-api/requests/crdbs/health_report","summary":"Active-Active database health report requests","title":"CRDB health report requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/crdbs/health_report/"},{"id":"operate/rs/references/rest-api/requests/crdbs/purge","summary":"Purge removed Active-Active database requests","title":"CRDB purge requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/crdbs/purge/"},{"id":"operate/rs/references/rest-api/requests/crdbs/updates","summary":"Update Active-Active configuration requests","title":"CRDB updates requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/crdbs/updates/"},{"id":"operate/rs/references/rest-api/requests/crdbs/upgrade","summary":"Upgrade Active-Active database requests","title":"CRDB upgrade requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/crdbs/upgrade/"}],"page_type":"content","content_hash":"26e44ab7ac5104b968adc4a5912b706798cad6d4b010e6a67c309f6c8e043889","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-all-crdbs) | `/v1/crdbs` | Get all Active-Active databases |\n| [GET](#get-crdb) | `/v1/crdbs/{crdb_guid}` | Get a specific Active-Active database |\n| [PATCH](#patch-crdbs) | `/v1/crdbs/{crdb_guid}` | Update an Active-Active database |\n| [POST](#post-crdb) | `/v1/crdbs` | Create a new Active-Active database |\n| [DELETE](#delete-crdb) | `/v1/crdbs/{crdb_guid}` | Delete an Active-Active database |"},{"id":"get-all-crdbs","title":"Get all Active-Active databases","role":"content","text":"[code example]\n\nGet a list of all Active-Active databases on the cluster."},{"id":"get-all-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| X-Task-ID | string | Specified task ID |\n| X-Result-TTL | integer | Time (in seconds) to keep task result |"},{"id":"get-all-response","title":"Response","role":"returns","text":"Returns a JSON array of [CRDB objects](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb).\n\n##### Status codes {#get-all-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | A list of Active-Active database. |\n| [401 Unauthorized](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2) | Unauthorized request. Invalid credentials |"},{"id":"get-crdb","title":"Get an Active-Active database","role":"content","text":"[code example]\n\nGet a specific Active-Active database."},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| X-Task-ID | string | Specified task ID |\n| X-Result-TTL | integer | Time (in seconds) to keep task result |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| crdb_guid | string | Globally unique Active-Active database ID (GUID) |\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| instance_id | integer | Instance from which to get the Active-Active database information |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns a [CRDB object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb).\n\n#### Status codes {#get-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Active-Active database information is returned. |\n| [401 Unauthorized](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2) | Unauthorized request. Invalid credentials |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Database or configuration does not exist. |"},{"id":"patch-crdbs","title":"Update an Active-Active database","role":"content","text":"[code example]\n\nUpdate an Active-Active database's configuration.\n\nUpdating `default_db_config` affects both existing and new instances.\n\nWhen you update `db_config`, it changes the configuration of the specified database instance. This field overrides any corresponding fields in `default_db_config`.\n\nFor a list of which settings must be identical across all instances and which to set per instance, see the [CRDB database config object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb/database_config) reference.\n\nTo add or remove instances, use [<nobr>`POST crdbs/{crdb_guid}/updates`</nobr>](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/crdbs/updates#post-crdbs-updates) instead."},{"id":"patch-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| X-Task-ID | string | Specified task ID |\n| X-Result-TTL | integer | Time (in seconds) to keep task result |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| crdb_guid | string | Globally unique Active-Active database ID (GUID) |\n\n#### Request body\n\nInclude a [CRDB object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb) with updated fields in the request body."},{"id":"patch-response","title":"Response","role":"returns","text":"Returns a [CRDB task object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb_task).\n\n#### Status codes {#patch-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | The request has been accepted. |\n| [400 Bad Request](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1) | The posted Active-Active database contains invalid parameters. |\n| [401 Unauthorized](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2) | Unauthorized request. Invalid credentials |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Configuration or Active-Active database not found. |\n| [406 Not Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | The posted Active-Active database cannot be accepted. |"},{"id":"post-crdb","title":"Create an Active-Active database","role":"content","text":"[code example]\n\nCreate a new Active-Active database.\n\nFor a list of which settings must be identical across all instances and which to set per instance, see the [CRDB database config object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb/database_config) reference."},{"id":"post-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| X-Task-ID | string | Specified task ID |\n| X-Result-TTL | integer | Time (in seconds) to keep task result |\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| dry_run | boolean | Validate the request without creating the database (optional) |\n\n#### Request body\n\nInclude a [CRDB object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb), which defines the Active-Active database, in the request body.\n\n##### Example body\n\n[code example]\n\nThis JSON body creates an Active-Active database without TLS and with two participating clusters."},{"id":"post-response","title":"Response","role":"returns","text":"Returns a [CRDB task object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb_task).\n\n#### Status codes {#post-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | The request has been accepted. |\n| [400 Bad Request](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1) | The request is invalid or malformed. |\n| [401 Unauthorized](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2) | Unauthorized request. Invalid credentials |\n| [406 Not Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | The posted Active-Active database cannot be accepted. |"},{"id":"delete-crdb","title":"Delete an Active-Active database","role":"content","text":"[code example]\n\nDelete an Active-Active database."},{"id":"delete-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| X-Task-ID | string | Specified task ID |\n| X-Result-TTL | integer | Time (in seconds) to keep task result |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| crdb_guid | string | Globally unique Active-Active database ID (GUID) |"},{"id":"delete-response","title":"Response","role":"returns","text":"Returns a [CRDB task object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb_task).\n\n#### Status codes {#delete-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Action was successful. |\n| [401 Unauthorized](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2) | Unauthorized request. Invalid credentials |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Configuration or Active-Active database not found. |\n| [406&nbsp;Not&nbsp;Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | The Active-Active GUID is invalid or the Active-Active database was already deleted. |"}],"examples":[{"id":"get-all-crdbs-ex0","language":"sh","code":"GET /v1/crdbs","section_id":"get-all-crdbs"},{"id":"get-all-request-ex0","language":"sh","code":"GET /v1/crdbs","section_id":"get-all-request"},{"id":"get-crdb-ex0","language":"sh","code":"GET /v1/crdbs/{crdb_guid}","section_id":"get-crdb"},{"id":"get-request-ex0","language":"sh","code":"GET /v1/crdbs/552bbccb-99f3-4142-bd17-93d245f0bc79","section_id":"get-request"},{"id":"patch-crdbs-ex0","language":"sh","code":"PATCH /v1/crdbs/{crdb_guid}","section_id":"patch-crdbs"},{"id":"patch-request-ex0","language":"sh","code":"PATCH /v1/crdbs/552bbccb-99f3-4142-bd17-93d245f0bc79","section_id":"patch-request"},{"id":"post-crdb-ex0","language":"sh","code":"POST /v1/crdbs","section_id":"post-crdb"},{"id":"post-request-ex0","language":"sh","code":"POST /v1/crdbs","section_id":"post-request"},{"id":"post-request-ex1","language":"json","code":"{\n    \"default_db_config\":\n    {\n        \"name\": \"sample-crdb\",\n        \"memory_size\": 214748365\n    },\n    \"instances\":\n    [\n        {\n            \"cluster\":\n            {\n                \"url\": \"http://<cluster1_FQDN>:9443\",\n                \"credentials\":\n                {\n                    \"username\": \"<username>\",\n                    \"password\": \"<password>\"\n                },\n                \"name\": \"cluster-1\"\n            },\n            \"compression\": 6\n        },\n        {\n            \"cluster\":\n            {\n                \"url\": \"http://<cluster2_FQDN>:9443\",\n                \"credentials\":\n                {\n                    \"username\": \"<username>\",\n                    \"password\": \"<password>\"\n                },\n                \"name\": \"cluster-2\"\n            },\n            \"compression\": 6\n        }\n    ],\n    \"name\": \"sample-crdb\"\n}","section_id":"post-request"},{"id":"delete-crdb-ex0","language":"sh","code":"DELETE /v1/crdbs/{crdb_guid}","section_id":"delete-crdb"},{"id":"delete-request-ex0","language":"sh","code":"DELETE /v1/crdbs/552bbccb-99f3-4142-bd17-93d245f0bc79","section_id":"delete-request"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/crdbs/purge","title":"CRDB purge requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/crdbs/purge/","summary":"Purge removed Active-Active database requests","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","page_type":"content","content_hash":"52a604b3dd7952f3346dc2e302117e62ba77e2646339fce0f28d0857741389f5","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [PUT](#put-crdbs-purge) | `/v1/crdbs/{crdb_guid}/purge` | Purge data from an instance that was forcibly removed from the Active-Active database |"},{"id":"put-crdbs-purge","title":"Purge data from removed instance","role":"content","text":"PUT /v1/crdbs/{crdb_guid}/purge\n\nPurge the data from an instance that was removed from the\nActive-Active database by force.\n\nWhen you force the removal of an instance from an Active-Active\ndatabase, the removed instance keeps the data and configuration\naccording to the last successful synchronization.\n\nTo delete the data and configuration from the forcefully removed\ninstance you must use this API (Must be executed locally on the\nremoved instance)."},{"id":"put-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n    PUT /v1/crdbs/1/purge\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| crdb_guid | string | Globally unique Active-Active database ID (GUID) |\n\n#### Request body\n\n| Field | Type | Description |\n|-------|------|-------------|\n| instances | array of integers | Array of unique instance IDs |"},{"id":"put-response","title":"Response","role":"returns","text":"Returns a [CRDB task object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb_task)."},{"id":"put-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Action was successful. |\n| [400 Bad Request](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1) | The request is invalid or malformed. |\n| [401 Unauthorized](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2) | Unauthorized request. Invalid credentials |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Configuration, instance, or Active-Active database not found. |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/crdbs/updates","title":"CRDB updates requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/crdbs/updates/","summary":"Update Active-Active configuration requests","tags":["docs","operate","rs"],"last_updated":"2026-10-05T16:11:44+01:00","page_type":"content","content_hash":"b896fd5ead1fd948135a1d308a2d12537ecb9c099b46dc85be03fe9ee861cf55","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [POST](#post-crdbs-updates) | `/v1/crdbs/{crdb_guid}/updates` | Modify Active-Active configuration |"},{"id":"post-crdbs-updates","title":"Modify Active-Active configuration","role":"content","text":"POST /v1/crdbs/{crdb_guid}/updates\n\nModify Active-Active configuration.\n\n> [!WARNING]\n> This is a very powerful API request and can cause damage if used incorrectly.\n\nTo add or remove instances, you must use this API. For simple configuration updates, use [`PATCH /crdbs/{crdb_guid}`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/crdbs#patch-crdbs) instead.\n\nUpdating `default_db_config` affects both existing and new instances.\n\nWhen you update `db_config`, it changes the configuration of the specified database instance. This field overrides any corresponding fields in `default_db_config`.\n\nFor a list of which settings must be identical across all instances and which to set per instance, see the [CRDB database config object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb/database_config) reference."},{"id":"post-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n    POST /v1/crdbs/1/updates\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| X-Task-ID | string | Specified task ID |\n| X-Result-TTL | integer | Time (in seconds) to keep task result |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| crdb_guid | string | Globally unique Active-Active database ID (GUID) |\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| dry_run | boolean | Validate the request without applying changes (optional) |\n\n#### Request body\n\nInclude a [CRDB modify_request object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb/modify_request) with updated fields in the request body."},{"id":"post-response","title":"Response","role":"returns","text":"Returns a [CRDB task object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb_task)."},{"id":"post-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | The request has been accepted. |\n| [400 Bad Request](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1) | The posted Active-Active database contains invalid parameters. |\n| [401 Unauthorized](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2) | Unauthorized request. Invalid credentials |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Configuration, instance or Active-Active database not found. |\n| [406&nbsp;Not&nbsp;Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | The posted Active-Active database cannot be accepted. |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/crdbs/upgrade","title":"CRDB upgrade requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/crdbs/upgrade/","summary":"Upgrade Active-Active database requests","tags":["docs","operate","rs"],"last_updated":"2026-10-05T17:53:21+03:00","page_type":"content","content_hash":"d0b7dceb932a3a37eb97d3de94354b319734971c73643ea138a0a0a5b2980240","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [POST](#post-crdbs-upgrade) | `/v1/crdbs/{crdb_guid}/upgrade` | Upgrade an Active-Active database |"},{"id":"post-crdbs-upgrade","title":"Upgrade an Active-Active database","role":"content","text":"[code example]\n\nUpgrades an Active-Active database. Upgrades the Redis version and modules of all instances in the Active-Active database."},{"id":"post-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Example JSON body\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| X-Task-ID | string | Specified task ID |\n| X-Result-TTL | integer | Time (in seconds) to keep task result |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| crdb_guid | string | Globally unique Active-Active database ID (GUID) |\n\n#### Request body\n\n| Field | Type | Description |\n|-------|------|-------------|\n| force_discard | boolean | Discard data even if the database is replicated and/or persistent (default: false) |\n| force_restart | boolean | Restart shards even if no version change (default: false) |\n| keep_crdt_featureset_version | boolean | Keep current CRDT feature set version (default: false) |\n| keep_crdt_protocol_version | boolean | Keep current CRDT protocol version (default: false) |\n| may_discard_data | boolean | Discard data in a non-replicated, non-persistent database (default: false) |\n| modules | array | List of modules to upgrade with optional new arguments. Each object includes:<br>• `current_module`: UID of module to upgrade (deprecated as of Redis Software v7.8.2)<br>• `new_module`: UID of the module to upgrade to (deprecated as of Redis Software v7.8.2)<br>• `new_module_args`: Arguments for the new module |\n| parallel_shards_upgrade | integer | Max number of shards to upgrade in parallel (default: all shards) |\n| preserve_roles | boolean | Preserve shards' primary/replica roles; requires an extra failover (default: false) |\n| redis_version | string | Upgrades the database to the specified Redis version instead of the latest version |"},{"id":"post-response","title":"Response","role":"returns","text":"Returns a [CRDB task object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb_task).\n\n#### Status codes {#post-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | Upgrade initiated successfully. |\n| [400 Bad Request](https://www.rfc-editor.org/rfc/rfc9110.html#name-400-bad-request) | Request validation error. |\n| [401 Unauthorized](https://www.rfc-editor.org/rfc/rfc9110.html#name-401-unauthorized) | Unauthorized request. Invalid credentials. |\n| [404 Not Found](https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found) | Active-Active database not found. |\n| [406 Not Acceptable](https://www.rfc-editor.org/rfc/rfc9110.html#name-406-not-acceptable) | New module version capabilities don't comply with the database configuration. |"}],"examples":[{"id":"post-crdbs-upgrade-ex0","language":"sh","code":"POST /v1/crdbs/{crdb_guid}/upgrade","section_id":"post-crdbs-upgrade"},{"id":"post-request-ex0","language":"sh","code":"POST /v1/crdbs/1/upgrade","section_id":"post-request"},{"id":"post-request-ex1","language":"json","code":"{\n    \"preserve_roles\": true,\n    \"may_discard_data\": false,\n    \"redis_version\": \"8.2\" //Uses the latest version if not specified\n}","section_id":"post-request"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/debuginfo/all/bdb","title":"All nodes database debug info requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/debuginfo/all/bdb/","summary":"Documents the Redis Software REST API debuginfo/all/bdb requests.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"3a4c70437d8e998de993424d1645c1e3748759f6ff6883108b856627679f00a6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This REST API path is deprecated as of Redis Software version 7.4.2. Use the new path [`/v1/bdbs/debuginfo`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/debuginfo) instead.\n\n\n| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-all-debuginfo-bdb) | `/v1/debuginfo/all/bdb/{bdb_uid}` | Get debug info for a database from all nodes |"},{"id":"get-all-debuginfo-bdb","title":"Get database debug info for all nodes","role":"content","text":"GET /v1/debuginfo/all/bdb/{int: bdb_uid}\n\nDownloads a tar file that contains debug info for the specified database (`bdb_uid`) from all nodes.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_debugging_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_debugging_info) |"},{"id":"get-all-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/debuginfo/all/bdb/1"},{"id":"get-all-response","title":"Response","role":"returns","text":"Downloads the debug info in a tar file called `filename.tar.gz`. Extract the files from the tar file to access the debug info.\n\n#### Response headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Content-Type | application/x-gzip | Media type of request/response body |\n| Content-Length | 653350 | Length of the response body in octets |\n| Content-Disposition | attachment; filename=debuginfo.tar.gz | Display response in browser"},{"id":"get-all-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success. |\n| [500 Internal Server Error](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1) | Failed to get debug info. |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/debuginfo/all","title":"All nodes debug info requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/debuginfo/all/","summary":"Documents the Redis Software REST API debuginfo/all requests.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","children":[{"id":"operate/rs/references/rest-api/requests/debuginfo/all/bdb","summary":"Documents the Redis Software REST API debuginfo/all/bdb requests.","title":"All nodes database debug info requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/debuginfo/all/bdb/"}],"page_type":"content","content_hash":"38676519e89e0ee538113ad1440ab66657a65d8a1f82cba28cbc4087545ac698","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This REST API path is deprecated as of Redis Software version 7.4.2. Use the new path [`/v1/cluster/debuginfo`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/debuginfo) instead.\n\n\n| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-all-debuginfo) | `/v1/debuginfo/all` | Get debug info for all nodes |"},{"id":"get-all-debuginfo","title":"Get debug info for all nodes","role":"content","text":"GET /v1/debuginfo/all\n\nDownloads a tar file that contains debug info from all nodes.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_debugging_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_debugging_info) |"},{"id":"get-all-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/debuginfo/all"},{"id":"get-all-response","title":"Response","role":"returns","text":"Downloads the debug info in a tar file called `filename.tar.gz`. Extract the files from the tar file to access the debug info for all nodes.\n\n#### Response headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Content-Type | application/x-gzip | Media type of request/response body |\n| Content-Length | 653350 | Length of the response body in octets |\n| Content-Disposition | attachment; filename=debuginfo.tar.gz | Display response in browser or download as attachment |"},{"id":"get-all-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success. |\n| [500 Internal Server Error](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1) | Failed to get debug info. |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/debuginfo","title":"Debug info requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/debuginfo/","summary":"Debug info requests","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","children":[{"id":"operate/rs/references/rest-api/requests/debuginfo/all","summary":"Documents the Redis Software REST API debuginfo/all requests.","title":"All nodes debug info requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/debuginfo/all/"},{"id":"operate/rs/references/rest-api/requests/debuginfo/node","summary":"Documents the Redis Software REST API debuginfo/node requests.","title":"Current node debug info requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/debuginfo/node/"}],"page_type":"content","content_hash":"27a1582b159ff1c6f94c0fbdcd3ee645d0045b70a2f723be218735fbfca9e6fa","sections":[{"id":"overview","title":"Overview","role":"overview","text":"These REST API paths are deprecated as of Redis Software version 7.4.2. Use the new paths [`/v1/cluster/debuginfo`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/debuginfo), [`/v1/nodes/debuginfo`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/nodes/debuginfo), and [`/v1/bdbs/debuginfo`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/debuginfo) instead.\n\n\nDownloads a support package, which includes logs and information about the cluster, nodes, databases, and shards, as a tar file called `filename.tar.gz`. Extract the files from the tar file to access the debug info."},{"id":"get-debug-info-for-all-nodes-in-the-cluster","title":"Get debug info for all nodes in the cluster","role":"content","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](https://redis.io/docs/latest/./all#get-all-debuginfo) | `/v1/debuginfo/all` | Gets debug info for all nodes |\n| [GET](https://redis.io/docs/latest/./all/bdb#get-all-debuginfo-bdb) | `/v1/debuginfo/all/bdb/{bdb_uid}` | Gets debug info for a database from all nodes |"},{"id":"get-debug-info-for-the-current-node","title":"Get debug info for the current node","role":"content","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](https://redis.io/docs/latest/./node#get-debuginfo-node) | `/v1/debuginfo/node` | Gets debug info for the current node |\n| [GET](https://redis.io/docs/latest/./node/bdb#get-debuginfo-node-bdb) | `/v1/debuginfo/node/bdb/{bdb_uid}` | Gets debug info for a database from the current node |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/debuginfo/node/bdb","title":"Current node database debug info requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/debuginfo/node/bdb/","summary":"Documents the Redis Software REST API debuginfo/node/bdb requests.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"77f3a45e98f530c42dbad296d344defb2911a4fc30cfe9a7996cd1a497ce6ebe","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This REST API path is deprecated as of Redis Software version 7.4.2. Use the new path [`/v1/bdbs/debuginfo`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/debuginfo) instead.\n\n\n| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-debuginfo-node-bdb) | `/v1/debuginfo/node/bdb/{bdb_uid}` | Get debug info for the current node regarding a specific database |"},{"id":"get-debuginfo-node-bdb","title":"Get database debug info for current node","role":"content","text":"GET /v1/debuginfo/node/bdb/{int: bdb_uid}\n\nDownloads a tar file that contains debug info for the specified database (`bdb_uid`) from the current node.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_debugging_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_debugging_info) |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/debuginfo/node/bdb/1"},{"id":"get-response","title":"Response","role":"returns","text":"Downloads the debug info in a tar file called `filename.tar.gz`. Extract the files from the tar file to access the debug info.\n\n#### Response headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Content-Type | application/x-gzip | Media type of request/response body |\n| Content-Length | 653350 | Length of the response body in octets |\n| Content-Disposition | attachment; filename=debuginfo.tar.gz | Display response in browser"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success. |\n| [500 Internal Server Error](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1) | Failed to get debug info. |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/debuginfo/node","title":"Current node debug info requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/debuginfo/node/","summary":"Documents the Redis Software REST API debuginfo/node requests.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","children":[{"id":"operate/rs/references/rest-api/requests/debuginfo/node/bdb","summary":"Documents the Redis Software REST API debuginfo/node/bdb requests.","title":"Current node database debug info requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/debuginfo/node/bdb/"}],"page_type":"content","content_hash":"7136fa352838a32cc182e0ac769423e6e600e49745dede36bece3ef43cb9b636","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This REST API path is deprecated as of Redis Software version 7.4.2. Use the new path [`/v1/nodes/debuginfo`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/nodes/debuginfo) instead.\n\n\n| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-debuginfo-node) | `/v1/debuginfo/node` | Get debug info for the current node |"},{"id":"get-debuginfo-node","title":"Get debug info for current node","role":"content","text":"GET /v1/debuginfo/node\n\nDownloads a tar file that contains debug info for the current node.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_debugging_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_debugging_info) |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/debuginfo/node"},{"id":"get-response","title":"Response","role":"returns","text":"Downloads the debug info in a tar file called `filename.tar.gz`. Extract the files from the tar file to access the debug info.\n\n#### Response headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Content-Type | application/x-gzip | Media type of request/response body |\n| Content-Length | 653350 | Length of the response body in octets |\n| Content-Disposition | attachment; filename=debuginfo.tar.gz | Display response in browser or download as attachment |"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success. |\n| [500 Internal Server Error](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1) | Failed to get debug info. |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/diagnostics","title":"Diagnostics requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/diagnostics/","summary":"REST API requests for the diagnostic logging service.","tags":["docs","operate","rs"],"last_updated":"2025-05-19T15:06:21-05:00","children":[],"page_type":"content","content_hash":"1aed26044d54d34ea6ceb3b9a872a9f5d743f01d5715dedbfe19f57c5fd0242c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-diagnostics) | `/v1/diagnostics` | Get diagnostic logging service configuration |\n| [PUT](#put-diagnostics) | `/v1/diagnostics` | Update diagnostic logging service configuration |"},{"id":"get-diagnostics","title":"Get diagnostic logging service configuration","role":"content","text":"[code example]\n\nGets the diagnostic logging service configuration as JSON.\n\n#### Required permissions\n\n| Permission name | Roles |\n|-----------------|-------|\n| [view_cluster_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_cluster_info) | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns a JSON object that represents the diagnostic logging service configuration. Each target includes a `cron_expression` that defines the log collection time interval. `slowlog_target` also includes `max_entries`, which specifies the maximum number of entries recorded in the slow log.\n\n#### Example response body\n\n[code example]\n\n#### Status codes {#get-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | No error |"},{"id":"put-diagnostics","title":"Update diagnostic logging service configuration","role":"content","text":"[code example]\n\nUpdates the diagnostic logging service configuration.\n\n#### Required permissions\n\n| Permission name | Roles |\n|-----------------|-------|\n| [update_cluster](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#update_cluster) | admin |"},{"id":"put-request","title":"Request","role":"content","text":"Provide a JSON object in the request body to update a diagnostic logging service target's configuration. Each target can include a `cron_expression` that defines the log collection time interval for that target. For `slowlog_target`, you can also change `max_entries`, which specifies the maximum number of entries recorded in the slow log.\n\n#### Example HTTP request\n\n[code example]\n\n#### Example JSON body\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |"},{"id":"put-response","title":"Response","role":"returns","text":"Returns a JSON object that represents the updated diagnostic logging service configuration. Each target includes a `cron_expression` that defines the log collection time interval. `slowlog_target` also includes `max_entries`, which specifies the maximum number of entries recorded in the slow log.\n\n\n#### Example response body\n\n[code example]\n\n#### Status codes {#put-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | No error. |\n| [400 Bad Request](https://www.rfc-editor.org/rfc/rfc9110.html#name-400-bad-request) | Bad content provided. |\n| [409 Conflict](https://www.rfc-editor.org/rfc/rfc9110.html#name-409-conflict) | Attempting to configure the diagnostic logging service target while it is busy with another configuration change. In this context, this is a temporary condition, and the request should be re-attempted later. |"}],"examples":[{"id":"get-diagnostics-ex0","language":"sh","code":"GET /v1/diagnostics","section_id":"get-diagnostics"},{"id":"get-request-ex0","language":"sh","code":"GET /v1/diagnostics","section_id":"get-request"},{"id":"get-response-ex0","language":"json","code":"{\n    \"bdb_client_list_target\": {\n        \"cron_expression\": \"*/10 * * * *\"\n    },\n    \"bdb_info_target\": {\n        \"cron_expression\": \"*/10 * * * *\"\n    },\n    \"bdb_target\": {\n        \"cron_expression\": \"*/10 * * * *\"\n    },\n    \"command_stats_target\": {\n        \"cron_expression\": \"*/30 * * * *\"\n    },\n    \"network_stats_target\": {\n        \"cron_expression\": \"*/30 * * * *\"\n    },\n    \"persistent_files_target\": {\n        \"cron_expression\": \"*/10 * * * *\"\n    },\n    \"rladmin_status_target\": {\n        \"cron_expression\": \"*/10 * * * *\"\n    },\n    \"shard_info_target\": {\n        \"cron_expression\": \"*/10 * * * *\"\n    },\n    \"shard_latency_histogram_target\": {\n        \"cron_expression\": \"*/10 * * * *\"\n    },\n    \"shard_latency_target\": {\n        \"cron_expression\": \"*/10 * * * *\"\n    },\n    \"shard_target\": {\n        \"cron_expression\": \"*/10 * * * *\"\n    },\n    \"slowlog_target\": {\n        \"cron_expression\": \"*/10 * * * *\",\n        \"max_entries\": 100\n    },\n    \"socket_files_target\": {\n        \"cron_expression\": \"*/10 * * * *\"\n    }\n}","section_id":"get-response"},{"id":"put-diagnostics-ex0","language":"sh","code":"PUT /v1/diagnostics","section_id":"put-diagnostics"},{"id":"put-request-ex0","language":"sh","code":"PUT /v1/diagnostics","section_id":"put-request"},{"id":"put-request-ex1","language":"json","code":"{\n   \"rladmin_status_target\": {\n     \"cron_expression\": \"5 * * * *\"\n   }\n}","section_id":"put-request"},{"id":"put-response-ex0","language":"json","code":"{\n    \"bdb_client_list_target\": {\n        \"cron_expression\": \"*/10 * * * *\"\n    },\n    \"bdb_info_target\": {\n        \"cron_expression\": \"*/10 * * * *\"\n    },\n    \"bdb_target\": {\n        \"cron_expression\": \"*/10 * * * *\"\n    },\n    \"command_stats_target\": {\n        \"cron_expression\": \"*/30 * * * *\"\n    },\n    \"network_stats_target\": {\n        \"cron_expression\": \"*/30 * * * *\"\n    },\n    \"persistent_files_target\": {\n        \"cron_expression\": \"*/10 * * * *\"\n    },\n    \"rladmin_status_target\": {\n        \"cron_expression\": \"5 * * * *\"\n    },\n    \"shard_info_target\": {\n        \"cron_expression\": \"*/10 * * * *\"\n    },\n    \"shard_latency_histogram_target\": {\n        \"cron_expression\": \"*/10 * * * *\"\n    },\n    \"shard_latency_target\": {\n        \"cron_expression\": \"*/10 * * * *\"\n    },\n    \"shard_target\": {\n        \"cron_expression\": \"*/10 * * * *\"\n    },\n    \"slowlog_target\": {\n        \"cron_expression\": \"*/10 * * * *\",\n        \"max_entries\": 100\n    },\n    \"socket_files_target\": {\n        \"cron_expression\": \"*/10 * * * *\"\n    }\n}","section_id":"put-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/endpoints-stats","title":"Endpoints stats requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/endpoints-stats/","summary":"Endpoint statistics requests","tags":["docs","operate","rs"],"last_updated":"2026-10-05T16:11:44+01:00","children":[],"page_type":"content","content_hash":"721f80a16c072e59fe4ab7a911df2e607c442d03b41c7d7780376617b3ed8b4b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The v1 statistics REST API endpoints are deprecated as of Redis Software version 7.22. Use the [metrics stream engine for monitoring v2](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine) instead for real-time monitoring and metrics collection.\n\n\n| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-endpoints-stats) | `/v1/endpoints/stats` | Get stats for all endpoints |"},{"id":"get-endpoints-stats","title":"Get all endpoints stats","role":"content","text":"GET /v1/endpoints/stats\n\nGet statistics for all endpoint-proxy links.\n\n> [!NOTE]\n> This method will return both endpoints and listeners stats for backwards\n> compatability.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_endpoint_stats](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_endpoint_stats) |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/endpoints/stats?interval=1hour&stime=2014-08-28T10:00:00Z \n\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| interval | string | Time interval for which we want stats: 1sec/10sec/5min/15min/1hour/12hour/1week (optional) |\n| stime | ISO_8601 | Start time from which we want the stats. Should comply with the [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601) format (optional) |\n| etime | ISO_8601 | End time after which we don't want the stats. Should comply with the [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601) format (optional) |"},{"id":"get-response","title":"Response","role":"returns","text":"The `uid` format in the response is: `\"BDB_UID:ENDPOINT_UID:PROXY_UID\"`\n\nFor example: `{\"uid\": \"1:2:3\"}` means `BDB_UID=1`, `ENDPOINT_UID=2`, and `PROXY_UID=3`\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |"}],"examples":[{"id":"get-response-ex0","language":"json","code":"[\n   {\n      \"uid\" : \"365:1:1\",\n      \"intervals\" : [\n         {\n            \"interval\" : \"10sec\",\n            \"total_req\" : 0,\n            \"egress_bytes\" : 0,\n            \"cmd_get\" : 0,\n            \"monitor_sessions_count\" : 0,\n            \"auth_errors\" : 0,\n            \"acc_latency\" : 0,\n            \"stime\" : \"2017-01-12T14:59:50Z\",\n            \"write_res\" : 0,\n            \"total_connections_received\" : 0,\n            \"cmd_set\" : 0,\n            \"read_req\" : 0,\n            \"max_connections_exceeded\" : 0,\n            \"acc_write_latency\" : 0,\n            \"write_req\" : 0,\n            \"other_res\" : 0,\n            \"cmd_flush\" : 0,\n            \"acc_read_latency\" : 0,\n            \"other_req\" : 0,\n            \"conns\" : 0,\n            \"write_started_res\" : 0,\n            \"cmd_touch\" : 0,\n            \"read_res\" : 0,\n            \"auth_cmds\" : 0,\n            \"etime\" : \"2017-01-12T15:00:00Z\",\n            \"total_started_res\" : 0,\n            \"ingress_bytes\" : 0,\n            \"last_res_time\" : 0,\n            \"read_started_res\" : 0,\n            \"acc_other_latency\" : 0,\n            \"total_res\" : 0,\n            \"last_req_time\" : 0,\n            \"other_started_res\" : 0\n         }\n      ]\n   },\n   {\n      \"intervals\" : [\n         {\n            \"acc_read_latency\" : 0,\n            \"other_req\" : 0,\n            \"etime\" : \"2017-01-12T15:00:00Z\",\n            \"auth_cmds\" : 0,\n            \"total_started_res\" : 0,\n            \"write_started_res\" : 0,\n            \"cmd_touch\" : 0,\n            \"conns\" : 0,\n            \"read_res\" : 0,\n            \"total_res\" : 0,\n            \"other_started_res\" : 0,\n            \"last_req_time\" : 0,\n            \"read_started_res\" : 0,\n            \"last_res_time\" : 0,\n            \"ingress_bytes\" : 0,\n            \"acc_other_latency\" : 0,\n            \"egress_bytes\" : 0,\n            \"interval\" : \"10sec\",\n            \"total_req\" : 0,\n            \"acc_latency\" : 0,\n            \"auth_errors\" : 0,\n            \"cmd_get\" : 0,\n            \"monitor_sessions_count\" : 0,\n            \"read_req\" : 0,\n            \"max_connections_exceeded\" : 0,\n            \"total_connections_received\" : 0,\n            \"cmd_set\" : 0,\n            \"acc_write_latency\" : 0,\n            \"write_req\" : 0,\n            \"stime\" : \"2017-01-12T14:59:50Z\",\n            \"write_res\" : 0,\n            \"cmd_flush\" : 0,\n            \"other_res\" : 0\n         }\n      ],\n      \"uid\" : \"333:1:2\"\n   }\n]","section_id":"get-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests","title":"Redis Software REST API requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/","summary":"Documents the requests supported by the Redis Software REST API calls.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","children":[{"id":"operate/rs/references/rest-api/requests/actions","summary":"Actions requests","title":"Actions requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/actions/"},{"id":"operate/rs/references/rest-api/requests/bdbs","summary":"Database requests","title":"Database requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/"},{"id":"operate/rs/references/rest-api/requests/bootstrap","summary":"Bootstrap requests","title":"Bootstrap requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bootstrap/"},{"id":"operate/rs/references/rest-api/requests/cluster","summary":"Cluster settings requests","title":"Cluster requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/"},{"id":"operate/rs/references/rest-api/requests/cm_settings","summary":"REST API requests for Cluster Manager UI settings","title":"Cluster Manager settings requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cm_settings/"},{"id":"operate/rs/references/rest-api/requests/crdb_tasks","summary":"Active-Active database task status requests","title":"CRDB tasks requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/crdb_tasks/"},{"id":"operate/rs/references/rest-api/requests/crdbs","summary":"Active-Active database requests","title":"CRDBs requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/crdbs/"},{"id":"operate/rs/references/rest-api/requests/debuginfo","summary":"Debug info requests","title":"Debug info requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/debuginfo/"},{"id":"operate/rs/references/rest-api/requests/diagnostics","summary":"REST API requests for the diagnostic logging service.","title":"Diagnostics requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/diagnostics/"},{"id":"operate/rs/references/rest-api/requests/endpoints-stats","summary":"Endpoint statistics requests","title":"Endpoints stats requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/endpoints-stats/"},{"id":"operate/rs/references/rest-api/requests/job_scheduler","summary":"REST API requests for the job scheduler","title":"Job scheduler requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/job_scheduler/"},{"id":"operate/rs/references/rest-api/requests/jsonschema","summary":"API object JSON schema requests","title":"JSON schema requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/jsonschema/"},{"id":"operate/rs/references/rest-api/requests/ldap_mappings","summary":"LDAP mappings requests","title":"LDAP mappings requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/ldap_mappings/"},{"id":"operate/rs/references/rest-api/requests/license","summary":"License requests","title":"License requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/license/"},{"id":"operate/rs/references/rest-api/requests/logs","summary":"Cluster event logs requests","title":"Logs requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/logs/"},{"id":"operate/rs/references/rest-api/requests/metrics","summary":"Local metrics storage requests","title":"Metrics requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/metrics/"},{"id":"operate/rs/references/rest-api/requests/metrics_config","summary":"Metrics configuration requests","title":"Metrics configuration requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/metrics_config/"},{"id":"operate/rs/references/rest-api/requests/migrations","summary":"REST API request to get the migration status of a database in the cluster when using Replica Of.","title":"Migrations requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/migrations/"},{"id":"operate/rs/references/rest-api/requests/modules","summary":"Redis modules requests","title":"Modules requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/modules/"},{"id":"operate/rs/references/rest-api/requests/node_master_healthcheck","summary":"REST API requests to check a node's connection to the cluster's primary node.","title":"Node master healthcheck requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/node_master_healthcheck/"},{"id":"operate/rs/references/rest-api/requests/nodes","summary":"Node requests","title":"Nodes requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/nodes/"},{"id":"operate/rs/references/rest-api/requests/ocsp","summary":"OCSP requests","title":"OCSP requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/ocsp/"},{"id":"operate/rs/references/rest-api/requests/proxies","summary":"Proxy requests","title":"Proxy requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/proxies/"},{"id":"operate/rs/references/rest-api/requests/redis_acls","summary":"Redis access control list (ACL) requests","title":"Redis access control list (ACL) requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/redis_acls/"},{"id":"operate/rs/references/rest-api/requests/roles","summary":"Roles requests","title":"Roles requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/roles/"},{"id":"operate/rs/references/rest-api/requests/services","summary":"REST API requests to list or modify Redis Software services.","title":"Services requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/services/"},{"id":"operate/rs/references/rest-api/requests/shards","summary":"REST API requests for database shards","title":"Shard requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/shards/"},{"id":"operate/rs/references/rest-api/requests/suffix","summary":"DNS suffix requests","title":"Suffix requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/suffix/"},{"id":"operate/rs/references/rest-api/requests/suffixes","summary":"DNS suffixes requests","title":"Suffixes requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/suffixes/"},{"id":"operate/rs/references/rest-api/requests/usage_report","summary":"REST API request to get the database usage report from the cluster.","title":"Usage report requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/usage_report/"},{"id":"operate/rs/references/rest-api/requests/users","summary":"User requests","title":"Users requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/users/"}],"page_type":"content","content_hash":"4fcb49a54af5930e3fc75bc8a7c0f6e343e1b106caf3d84fbeb0641ef474a2d9","sections":[{"id":"content","title":"Content","role":"content","text":"A REST API request requires the following components:\n- [HTTP method](https://restfulapi.net/http-methods/) (`GET`, `PUT`, `PATCH`, `POST`, `DELETE`)\n- Base URL\n- Endpoint\n\nSome requests may also require:\n- URL parameters\n- [Query parameters](https://en.wikipedia.org/wiki/Query_string)\n- [JSON](http://www.json.org) request body\n- [Permissions](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions)\n\n\n| Request | Description |\n| --- | --- |\n| [actions](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/actions/) | Actions requests |\n| [bdbs](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/) | Database requests |\n| [bootstrap](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bootstrap/) | Bootstrap requests |\n| [cluster](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/) | Cluster settings requests |\n| [cm_settings](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cm_settings/) | REST API requests for Cluster Manager UI settings |\n| [crdb_tasks](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/crdb_tasks/) | Active-Active database task status requests |\n| [crdbs](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/crdbs/) | Active-Active database requests |\n| [debuginfo](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/debuginfo/) | Debug info requests |\n| [diagnostics](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/diagnostics/) | REST API requests for the diagnostic logging service. |\n| [endpoints/stats](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/endpoints-stats/) | Endpoint statistics requests |\n| [job_scheduler](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/job_scheduler/) | REST API requests for the job scheduler |\n| [jsonschema](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/jsonschema/) | API object JSON schema requests |\n| [ldap_mappings](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/ldap_mappings/) | LDAP mappings requests |\n| [license](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/license/) | License requests |\n| [logs](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/logs/) | Cluster event logs requests |\n| [metrics](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/metrics/) | Local metrics storage requests |\n| [metrics_config](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/metrics_config/) | Metrics configuration requests |\n| [migrations](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/migrations/) | REST API request to get the migration status of a database in the cluster when using Replica Of. |\n| [modules](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/modules/) | Redis modules requests |\n| [node/master_healthcheck](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/node_master_healthcheck/) | REST API requests to check a node's connection to the cluster's primary node. |\n| [nodes](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/nodes/) | Node requests |\n| [ocsp](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/ocsp/) | OCSP requests |\n| [proxies](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/proxies/) | Proxy requests |\n| [redis_acls](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/redis_acls/) | Redis access control list (ACL) requests |\n| [roles](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/roles/) | Roles requests |\n| [services](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/services/) | REST API requests to list or modify Redis Software services. |\n| [shards](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/shards/) | REST API requests for database shards |\n| [suffix](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/suffix/) | DNS suffix requests |\n| [suffixes](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/suffixes/) | DNS suffixes requests |\n| [usage_report](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/usage_report/) | REST API request to get the database usage report from the cluster. |\n| [users](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/users/) | User requests |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/job_scheduler","title":"Job scheduler requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/job_scheduler/","summary":"REST API requests for the job scheduler","tags":["docs","operate","rs"],"last_updated":"2026-09-29T16:51:25+03:00","children":[],"page_type":"content","content_hash":"5fb00374357d35d262f581a2dde31dbbe7405a903705914b33a1f27df32652b7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-job-scheduler) | `/v1/job_scheduler` | Get job scheduler settings |\n| [PUT](#put-job-scheduler) | `/v1/job_scheduler` | Update job scheduler settings |"},{"id":"get-job-scheduler","title":"Get job scheduler settings","role":"content","text":"[code example]\n\nGet job scheduler information."},{"id":"permissions","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [view_cluster_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_cluster_info) | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns a [job_scheduler object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/job_scheduler).\n\n#### Example JSON body\n\n[code example]\n\n#### Status codes {#get-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | No error. |"},{"id":"put-job-scheduler","title":"Update job scheduler settings","role":"content","text":"[code example]\n\nUpdate job scheduler settings."},{"id":"permissions-1","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [update_cluster](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#update_cluster) | admin |"},{"id":"put-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Example JSON body\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n\n#### Body\n\nInclude a [job_scheduler object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/job_scheduler) with updated fields in the request body."},{"id":"put-response","title":"Response","role":"returns","text":"Returns a [job_scheduler object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/job_scheduler) with the updated fields.\n\n#### Example JSON body\n\n[code example]\n\n#### Status codes {#put-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | No error. |\n| [400 Bad Request](https://www.rfc-editor.org/rfc/rfc9110.html#name-400-bad-request) | Bad content provided. |\n| [409 Conflict](https://www.rfc-editor.org/rfc/rfc9110.html#name-409-conflict) | Attempting to configure the job_scheduler while it is busy with another configuration change. In this context, this is a temporary condition and the request should be re-attempted later. |"}],"examples":[{"id":"get-job-scheduler-ex0","language":"sh","code":"GET /v1/job_scheduler","section_id":"get-job-scheduler"},{"id":"get-request-ex0","language":"sh","code":"GET /v1/job_scheduler","section_id":"get-request"},{"id":"get-response-ex0","language":"json","code":"{\n  \"backup_job_settings\": {\n    \"cron_expression\": \"*/5 * * * *\",\n    \"enabled\": true\n  },\n  \"bdb_usage_report_job_settings\": {\n    \"cron_expression\": \"0 */1 * * *\",\n    \"enabled\": false,\n    \"file_retention_days\": 365\n  },\n  \"cert_rotation_job_settings\": {\n    \"cron_expression\": \"0 * * * *\",\n    \"enabled\": true,\n    \"expiry_days_before_rotation\": 60\n  },\n  \"granular_metrics_job_settings\": {\n    \"cron_expression\": \"* * * * *\",\n    \"enabled\": true,\n    \"granular_cleanup_delay\": 86400,\n    \"granular_max_duration\": 3600\n  },\n  \"log_rotation_job_settings\": {\n    \"cron_expression\": \"*/5 * * * *\",\n    \"enabled\": true\n  },\n  \"node_checks_job_settings\": {\n    \"cron_expression\": \"0 * * * *\",\n    \"enabled\": true\n  },\n  \"redis_cleanup_job_settings\": {\n    \"cron_expression\": \"0 * * * *\"\n  },\n  \"rotate_ccs_job_settings\": {\n    \"cron_expression\": \"*/5 * * * *\",\n    \"enabled\": true,\n    \"file_suffix\": \"5min\",\n    \"rotate_max_num\": 24\n  }\n}","section_id":"get-response"},{"id":"put-job-scheduler-ex0","language":"sh","code":"PUT /v1/job_scheduler","section_id":"put-job-scheduler"},{"id":"put-request-ex0","language":"sh","code":"PUT /v1/job_scheduler","section_id":"put-request"},{"id":"put-request-ex1","language":"json","code":"{\n   \"backup_job_settings\": {\n     \"cron_expression\": \"*/10 * * * *\"\n   }\n}","section_id":"put-request"},{"id":"put-response-ex0","language":"json","code":"{\n    \"backup_job_settings\": {\n        \"cron_expression\": \"*/10 * * * *\",\n        \"enabled\": true\n    },\n    \"bdb_usage_report_job_settings\": {\n        \"cron_expression\": \"0 */1 * * *\",\n        \"enabled\": false,\n        \"file_retention_days\": 365\n    },\n    \"cert_rotation_job_settings\": {\n        \"cron_expression\": \"0 * * * *\",\n        \"enabled\": true,\n        \"expiry_days_before_rotation\": 60\n    },\n    \"log_rotation_job_settings\": {\n        \"cron_expression\": \"*/5 * * * *\",\n        \"enabled\": true\n    },\n    \"node_checks_job_settings\": {\n        \"cron_expression\": \"0 * * * *\",\n        \"enabled\": true\n    },\n    \"rotate_ccs_job_settings\": {\n        \"cron_expression\": \"*/5 * * * *\",\n        \"enabled\": true,\n        \"file_suffix\": \"5min\",\n        \"rotate_max_num\": 24\n    }\n}","section_id":"put-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/jsonschema","title":"JSON schema requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/jsonschema/","summary":"API object JSON schema requests","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","children":[],"page_type":"content","content_hash":"75dc1866a4a46783a7fa36fd823868f4de6fe938997ee4cf8ab86c643b972eb3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-jsonschema) | `/v1/jsonschema` | Get JSON schema of API objects |"},{"id":"get-jsonschema","title":"Get object JSON schema","role":"content","text":"GET /v1/jsonschema\n\nGet the JSON schema of various [Redis Software REST API objects](https://redis.io/docs/latest/operate/rs/references/rest-api/objects)."},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/jsonschema?object=bdb \n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| object | string | Optional. The API object name: 'cluster', 'node', 'bdb' etc. |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns the JSON schema of the specified API object.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success. |\n| [406 Not Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | Invalid object. |"}],"examples":[{"id":"get-response-ex0","language":"json","code":"{\n     \"type\": \"object\",\n     \"description\": \"An API object that represents a managed database in the cluster.\",\n     \"properties\": {\n          \"....\"\n     },\n     \"....\"\n}","section_id":"get-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/ldap_mappings","title":"LDAP mappings requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/ldap_mappings/","summary":"LDAP mappings requests","tags":["docs","operate","rs"],"last_updated":"2025-07-09T16:04:22-05:00","children":[],"page_type":"content","content_hash":"eeb44b2d03d85fa4626b0c5a5fcc418b110292554d5cfa93439a19ba0298b2dc","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-all-ldap_mappings) | `/v1/ldap_mappings` | Get all LDAP mappings |\n| [GET](#get-ldap_mapping) | `/v1/ldap_mappings/{uid}` | Get a single LDAP mapping |\n| [PUT](#put-ldap_mapping) | `/v1/ldap_mappings/{uid}` | Update an LDAP mapping |\n| [POST](#post-ldap_mappings) | `/v1/ldap_mappings` | Create a new LDAP mapping |\n| [DELETE](#delete-ldap_mappings) | `/v1/ldap_mappings/{uid}` | Delete an LDAP mapping |"},{"id":"get-all-ldap_mappings","title":"Get all LDAP mappings","role":"content","text":"GET /v1/ldap_mappings\n\nGet all LDAP mappings.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_all_ldap_mappings_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_all_ldap_mappings_info) |"},{"id":"get-all-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/ldap_mappings \n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |"},{"id":"get-all-response","title":"Response","role":"returns","text":"Returns a JSON array of [LDAP mapping objects](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/ldap_mapping).\n\n#### Example JSON body\n\n[code example]"},{"id":"get-all-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |"},{"id":"get-ldap_mapping","title":"Get LDAP mapping","role":"content","text":"GET /v1/ldap_mappings/{int: uid}\n\nGet a specific LDAP mapping.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_ldap_mapping_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_ldap_mapping_info) |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/ldap_mappings/1 \n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The object's unique ID. |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns an [LDAP mapping object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/ldap_mapping).\n\n#### Example JSON body\n\n[code example]"},{"id":"get-error-codes","title":"Error codes","role":"errors","text":"Possible `error_code` values:\n\n| Code | Description |\n|------|-------------|\n| unsupported_resource | The cluster is not yet able to handle this resource type. This could happen in a partially upgraded cluster, where some of the nodes are still on a previous version.| \n| ldap_mapping_not_exist | An object does not exist|"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success. |\n| [403 Forbidden](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.4) | Operation is forbidden. |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | ldap_mapping does not exist. |\n| [501 Not Implemented](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.2) | Cluster doesn't support LDAP mappings yet. |"},{"id":"put-ldap_mapping","title":"Update LDAP mapping","role":"content","text":"PUT /v1/ldap_mappings/{int: uid}\n\nUpdate an existing ldap_mapping object.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [update_ldap_mapping](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#update_ldap_mapping) |"},{"id":"put-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tPUT /v1/ldap_mappings/17 \n\n#### Example JSON body\n\n[code example]\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n\n#### Request body\n\nInclude an [LDAP mapping object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/ldap_mapping) with updated fields in the request body."},{"id":"put-response","title":"Response","role":"returns","text":"#### Example JSON body\n\n[code example]"},{"id":"put-error-codes","title":"Error codes","role":"errors","text":"Possible `error_code` values:\n\n| Code | Description |\n|------|-------------|\n| unsupported_resource | The cluster is not yet able to handle this resource type. This could happen in a partially upgraded cluster, where some of the nodes are still on a previous version.| \n| name_already_exists | An object of the same type and name exists| \n| ldap_mapping_not_exist | An object does not exist| \n| invalid_dn_param | A dn parameter has an illegal value| \n| invalid_name_param | A name parameter has an illegal value| \n| invalid_role_uids_param | A role_uids parameter has an illegal value| \n| invalid_account_id_param | An account_id parameter has an illegal value|"},{"id":"put-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success, LDAP mapping is created. |\n| [400 Bad Request](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1) | Bad or missing configuration parameters. |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Attempting to change a non-existing LDAP mapping. |\n| [501 Not Implemented](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.2) | Cluster doesn't support LDAP mapping yet. |"},{"id":"post-ldap_mappings","title":"Create LDAP mapping","role":"content","text":"POST /v1/ldap_mappings\n\nCreate a new LDAP mapping.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [create_ldap_mapping](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#create_ldap_mapping) |"},{"id":"post-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tPOST /v1/ldap_mappings \n\n#### Example JSON body\n\n[code example]\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n\n#### Request body\n\nInclude an [LDAP mapping object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/ldap_mapping) in the request body."},{"id":"post-response","title":"Response","role":"returns","text":"#### Example JSON body\n\n[code example]"},{"id":"post-error-codes","title":"Error codes","role":"errors","text":"Possible `error_code` values:\n\n| Code | Description |\n|------|-------------|\n| unsupported_resource | The cluster is not yet able to handle this resource type. This could happen in a partially upgraded cluster, where some of the nodes are still on a previous version.| \n| name_already_exists | An object of the same type and name exists| \n| missing_field | A needed field is missing| \n| invalid_dn_param | A dn parameter has an illegal value| \n| invalid_name_param | A name parameter has an illegal value| \n| invalid_role_uids_param | A role_uids parameter has an illegal value|"},{"id":"post-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success, an LDAP-mapping object is created. |\n| [400 Bad Request](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1) | Bad or missing configuration parameters. |\n| [501 Not Implemented](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.2) | Cluster doesn't support LDAP mappings yet. |"},{"id":"delete-ldap_mappings","title":"Delete LDAP mapping","role":"content","text":"DELETE /v1/ldap_mappings/{int: uid}\n\nDelete an LDAP mapping object.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [delete_ldap_mapping](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#delete_ldap_mapping) |"},{"id":"delete-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tDELETE /v1/ldap_mappings/1 \n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The ldap_mapping unique ID. |"},{"id":"delete-response","title":"Response","role":"returns","text":"Returns a status code. If an error occurs, the response body may include a more specific error code and message."},{"id":"delete-error-codes","title":"Error codes","role":"errors","text":"Possible `error_code` values:\n\n| Code | Description |\n|------|-------------|\n| unsupported_resource | The cluster is not yet able to handle this resource type. This could happen in a partially upgraded cluster, where some of the nodes are still on a previous version.| \n| ldap_mapping_not_exist | An object does not exist|"},{"id":"delete-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success, the ldap_mapping is deleted. |\n| [406 Not Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | The request is not acceptable. |\n| [501 Not Implemented](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.2) | Cluster doesn't support LDAP mappings yet. |"}],"examples":[{"id":"get-all-response-ex0","language":"json","code":"[\n  {\n     \"uid\": 17,\n     \"name\": \"Admins\",\n     \"dn\": \"OU=ops.group,DC=redislabs,DC=com\",\n     \"email\": \"ops.group@redislabs.com\",\n     \"role_uids\": [1],\n     \"email_alerts\": true,\n     \"bdbs_email_alerts\": [\"1\",\"2\"],\n     \"cluster_email_alerts\": true\n  }\n]","section_id":"get-all-response"},{"id":"get-response-ex0","language":"json","code":"{\n     \"uid\": 17,\n     \"name\": \"Admins\",\n     \"dn\": \"OU=ops.group,DC=redislabs,DC=com\",\n     \"email\": \"ops.group@redislabs.com\",\n     \"role_uids\": [1],\n     \"email_alerts\": true,\n     \"bdbs_email_alerts\": [\"1\",\"2\"],\n     \"cluster_email_alerts\": true\n}","section_id":"get-response"},{"id":"put-request-ex0","language":"json","code":"{\n    \"dn\": \"OU=ops,DC=redislabs,DC=com\",\n    \"email\": \"ops@redislabs.com\",\n    \"email_alerts\": true,\n    \"bdbs_email_alerts\": [\"1\",\"2\"],\n    \"cluster_email_alerts\": true\n}","section_id":"put-request"},{"id":"put-response-ex0","language":"json","code":"{\n     \"uid\": 17,\n     \"name\": \"Admins\",\n     \"dn\": \"OU=ops,DC=redislabs,DC=com\",\n     \"email\": \"ops@redislabs.com\",\n     \"role_uids\": [1],\n     \"email_alerts\": true,\n     \"bdbs_email_alerts\": [\"1\",\"2\"],\n     \"cluster_email_alerts\": true\n}","section_id":"put-response"},{"id":"post-request-ex0","language":"json","code":"{\n     \"name\": \"Admins\",\n     \"dn\": \"OU=ops.group,DC=redislabs,DC=com\",\n     \"email\": \"ops.group@redislabs.com\",\n     \"role_uids\": [1]\n}","section_id":"post-request"},{"id":"post-response-ex0","language":"json","code":"{\n     \"uid\": 17,\n     \"name\": \"Admins\",\n     \"dn\": \"OU=ops.group,DC=redislabs,DC=com\",\n     \"email\": \"ops.group@redislabs.com\",\n     \"role_uids\": [1]\n}","section_id":"post-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/license","title":"License requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/license/","summary":"License requests","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","children":[],"page_type":"content","content_hash":"d6e50dbe399625b91cdeab81e76a3d57d5ecbade45ab53d846697dbd796ee019","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-license) | `/v1/license` | Get license details |\n| [PUT](#put-license) | `/v1/license` | Update the license |"},{"id":"get-license","title":"Get license","role":"content","text":"GET /v1/license\n\nReturns the license details, including license string, expiration,\nand supported features.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_license](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_license) |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/license \n\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns a JSON object that contains the license details:\n\n| Name | Type/Value | Description |\n|------|------------|-------------|\n| license | string | License data |\n| cluster_name | string | The cluster name (FQDN) |\n| expired | boolean | If the cluster key is expired (`true` or `false`) |\n| activation_date | string | The date of the cluster key activation |\n| expiration_date | string | The date of the cluster key expiration |\n| key | string | License key |\n| features | array of strings | Features supported by the cluster |\n| owner | string | License owner |\n| shards_limit | integer | The total number of shards allowed by the cluster key |\n| ram_shards_limit | integer | The number of RAM shards allowed by the cluster key (as of v7.2) |\n| ram_shards_in_use | integer | The number of RAM shards in use |\n| flash_shards_limit | integer | The number of flash shards (Auto Tiering) allowed by the cluster key (as of v7.2) |\n| flash_shards_in_use | integer | The number of flash shards in use |\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | License is returned in the response body. |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | No license is installed. |"},{"id":"put-license","title":"Update license","role":"content","text":"PUT /v1/license\n\nValidate and install a new license string.\n\nIf you do not provide a valid license, the cluster behaves as if the license was deleted. See [Expired cluster license](https://redis.io/docs/latest/operate/rs/clusters/configure/license-keys#expired-cluster-license) for a list of available actions and restrictions.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [install_new_license](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#install_new_license) |"},{"id":"put-request","title":"Request","role":"content","text":"The request must be a JSON object with a single key named \"license\".\n\n#### Example HTTP request\n\n\tPUT /v1/license \n\n#### Example JSON body\n\n[code example]\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Accept | application/json | Accepted media type |\n\n\n#### Request body\n\nInclude a JSON object that contains the new `license` string in the request body."},{"id":"put-response","title":"Response","role":"returns","text":"Returns an error if the new license is not valid."},{"id":"put-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | License installed successfully. |\n| [400 Bad Request](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1) | Invalid request, either bad JSON object or corrupted license was supplied. |\n| [406 Not Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | License violation. A response body provides more details on the specific cause. |"}],"examples":[{"id":"get-response-ex0","language":"json","code":"{\n    \"license\": \"----- LICENSE START -----\\\\ndi+iK...KniI9\\\\n----- LICENSE END -----\\\\n\",\n    \"expired\": true,\n    \"activation_date\":\"2018-12-31T00:00:00Z\",\n    \"expiration_date\":\"2019-12-31T00:00:00Z\",\n    \"ram_shards_in_use\": 0,\n    \"ram_shards_limit\": 300,\n    \"flash_shards_in_use\": 0,\n    \"flash_shards_limit\": 100,\n    \"shards_limit\": 400,\n    \"features\": [\"bigstore\"],\n    \"owner\": \"Redis\",\n    \"cluster_name\": \"mycluster.local\",\n    \"key\": \"----- LICENSE START -----\\\\ndi+iK...KniI9\\\\n----- LICENSE END -----\\\\n\"\n}","section_id":"get-response"},{"id":"put-request-ex0","language":"json","code":"{\n    \"license\": \"----- LICENSE START -----\\ndi+iK...KniI9\\n----- LICENSE END -----\\n\"\n}","section_id":"put-request"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/logs","title":"Logs requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/logs/","summary":"Cluster event logs requests","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","children":[],"page_type":"content","content_hash":"0df6db8d41e4128470831720c0bbd6d54fab61a0340851e42b9ab24560179d62","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-logs) | `/v1/logs` | Get cluster events log |"},{"id":"get-logs","title":"Get cluster events log","role":"content","text":"GET /v1/logs\n\nGet cluster events log.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_logged_events](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_logged_events) |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/logs?order=desc \n\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| stime | ISO_8601 | Start time before which we don't want events. (optional) |\n| etime | ISO_8601 | End time after which we don't want events. (optional) |\n| order | string | desc/asc - get events in descending or ascending order. Defaults to asc. |\n| limit | integer | Maximum number of events to return. (optional) |\n| offset | integer | Skip offset events before returning first one (useful for pagination). (optional) |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns a JSON array of events.\n\n#### Example JSON body\n\n[code example]\n\n#### Event object\n\n| Field | Description |\n|-------|-------------|\n| time | Timestamp when event happened. |\n| type | Event type. Additional fields may be available for certain event types. |\n| additional fields | Additional fields may be present based on event type.|"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |"}],"examples":[{"id":"get-response-ex0","language":"json","code":"[\n  {\n    \"time\": \"2014-08-29T11:19:49Z\",\n    \"type\": \"bdb_name_updated\",\n    \"severity\": \"INFO\",\n    \"bdb_uid\": \"1\",\n    \"old_val\": \"test\",\n    \"new_val\": \"test123\"\n  },\n  {\n    \"time\": \"2014-08-29T11:18:48Z\",\n    \"type\": \"cluster_bdb_created\",\n    \"severity\": \"INFO\",\n    \"bdb_uid\": \"1\",\n    \"bdb_name\": \"test\"\n  },\n  {\n    \"time\": \"2014-08-29T11:17:49Z\",\n    \"type\": \"cluster_node_joined\",\n    \"severity\": \"INFO\",\n    \"node_uid\": 2\n  }\n]","section_id":"get-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/metrics/granular","title":"Granular metrics requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/metrics/granular/","summary":"Granular metrics collection requests","tags":["docs","operate","rs"],"last_updated":"2026-09-29T16:51:25+03:00","page_type":"content","content_hash":"2c85c3bac1e1dbdaebc971c698c08dfdad861b92f996562e8db51a347be4215d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-granular-status) | `/v1/metrics/granular/status` | Get the granular metrics collection status of cluster nodes |\n| [POST](#post-granular-start) | `/v1/metrics/granular/start` | Start granular metrics collection |\n| [POST](#post-granular-stop) | `/v1/metrics/granular/stop` | Stop granular metrics collection |\n| [DELETE](#delete-granular-data) | `/v1/metrics/granular/data` | Delete granular metrics data |\n\nThese requests manage the granular tier of [local metrics storage](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/local-metrics-storage#tiers). None of them take a request body."},{"id":"get-granular-status","title":"Get granular metrics status","role":"content","text":"GET /v1/metrics/granular/status\n\nGet the granular metrics collection status of all nodes or of a specific node.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_cluster_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_cluster_info) |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/metrics/granular/status\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| node_uid | integer | Optional. The ID of the node to get the status of. If omitted, returns the status of all nodes. |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns a `nodes` array with one object for each node in scope.\n\n#### Example JSON body\n\n[code example]\n\n#### Node status fields\n\nTimestamps are Unix epoch seconds.\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | string | The node ID |\n| state | string | Granular collection state on the node:<br />`running`<br />`stopped` |\n| started_at | integer | When collection started. Included only while `state` is `running`. |\n| expires_in_sec | integer | Seconds until collection stops automatically. Included only while `state` is `running`. |\n| stopped_at | integer | When collection stopped. Included only after collection stops, while granular data is still on disk. |\n| auto_cleanup_in_sec | integer | Seconds until the granular data is deleted automatically. Included only after collection stops, while granular data is still on disk. |\n| disk_usage_bytes | integer | Disk space used by granular data, in bytes |"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success. |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | The `node_uid` doesn't match a node in the cluster (`error_code`: `node_not_found`). |\n| [500 Internal Server Error](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1) | Internal server error. |"},{"id":"post-granular-start","title":"Start granular metrics collection","role":"content","text":"POST /v1/metrics/granular/start\n\nStart granular metrics collection on all nodes or on a specific node. Collection stops automatically after the maximum duration set in [`granular_metrics_job_settings`](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/job_scheduler/granular_metrics_job_settings).\n\nThe request is idempotent. Starting collection on a node where it's already running returns `already_running`.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [update_cluster](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#update_cluster) |"},{"id":"post-start-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tPOST /v1/metrics/granular/start\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| node_uid | integer | Optional. The ID of the node to start collection on. If omitted, starts collection on all nodes. |"},{"id":"post-start-response","title":"Response","role":"returns","text":"Returns a `results` array with one object for each node in scope.\n\n#### Example JSON body\n\n[code example]\n\n#### Result fields\n\nTimestamps are Unix epoch seconds.\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | string | The node ID |\n| outcome | string | Result on the node:<br />`started`: collection started.<br />`already_running`: collection was already running.<br />`unknown`: the result couldn't be confirmed. [Get the granular status](#get-granular-status) of the node to check. |\n| started_at | integer | When collection started |\n| expires_at | integer | When collection stops automatically |"},{"id":"post-start-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success. |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | The `node_uid` doesn't match a node in the cluster (`error_code`: `node_not_found`). |\n| [500 Internal Server Error](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1) | Internal server error. |"},{"id":"post-granular-stop","title":"Stop granular metrics collection","role":"content","text":"POST /v1/metrics/granular/stop\n\nStop granular metrics collection on all nodes or on a specific node. The collected data stays on disk until it's [deleted](#delete-granular-data) or the cleanup delay set in [`granular_metrics_job_settings`](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/job_scheduler/granular_metrics_job_settings) passes.\n\nThe request is idempotent. Stopping collection on a node where it isn't running returns `already_stopped`.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [update_cluster](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#update_cluster) |"},{"id":"post-stop-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tPOST /v1/metrics/granular/stop\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| node_uid | integer | Optional. The ID of the node to stop collection on. If omitted, stops collection on all nodes. |"},{"id":"post-stop-response","title":"Response","role":"returns","text":"Returns a `results` array with one object for each node in scope.\n\n#### Example JSON body\n\n[code example]\n\n#### Result fields\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | string | The node ID |\n| outcome | string | Result on the node:<br />`stopped`: collection stopped.<br />`already_stopped`: collection wasn't running. |\n| stopped_at | integer | When collection stopped, in Unix epoch seconds |\n| ran_for_sec | integer | How long collection ran, in seconds |"},{"id":"post-stop-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success. |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | The `node_uid` doesn't match a node in the cluster (`error_code`: `node_not_found`). |\n| [500 Internal Server Error](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1) | Internal server error. |"},{"id":"delete-granular-data","title":"Delete granular metrics data","role":"content","text":"DELETE /v1/metrics/granular/data\n\nDelete granular metrics data from all nodes or from a specific node.\n\nIf granular collection is still running on any node in scope, the request fails with `409 Conflict`. The cluster is checked before any node's data is deleted, so a failed request deletes nothing. [Stop collection](#post-granular-stop) on those nodes first.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [update_cluster](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#update_cluster) |"},{"id":"delete-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tDELETE /v1/metrics/granular/data\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| node_uid | integer | Optional. The ID of the node to delete granular data from. If omitted, deletes granular data from all nodes. |"},{"id":"delete-response","title":"Response","role":"returns","text":"Returns a `results` array with one object for each node in scope.\n\n#### Example JSON body\n\n[code example]\n\n#### Result fields\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | string | The node ID |\n| outcome | string | Result on the node:<br />`deleted`: granular data was deleted.<br />`no_data`: the node had no granular data to delete. |\n| freed_bytes | integer | Disk space freed, in bytes. Included with the `deleted` outcome. |\n\n#### Example error response\n\nIf granular collection is still running on any node in scope, the response lists those nodes:\n\n[code example]"},{"id":"delete-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success. |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | The `node_uid` doesn't match a node in the cluster (`error_code`: `node_not_found`). |\n| [409 Conflict](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.10) | Granular collection is still running on at least one node in scope (`error_code`: `granular_metrics_running`). No data was deleted. |\n| [500 Internal Server Error](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1) | Internal server error. |"}],"examples":[{"id":"get-response-ex0","language":"json","code":"{\n  \"nodes\": [\n    { \"uid\": \"1\", \"state\": \"running\", \"started_at\": 1771770600, \"expires_in_sec\": 2820 },\n    { \"uid\": \"2\", \"state\": \"stopped\", \"stopped_at\": 1771770600, \"auto_cleanup_in_sec\": 82800, \"disk_usage_bytes\": 131072000 }\n  ]\n}","section_id":"get-response"},{"id":"post-start-response-ex0","language":"json","code":"{\n  \"results\": [\n    { \"uid\": \"1\", \"outcome\": \"started\", \"started_at\": 1771770600, \"expires_at\": 1771774200 },\n    { \"uid\": \"2\", \"outcome\": \"already_running\", \"started_at\": 1771769000, \"expires_at\": 1771772600 },\n    { \"uid\": \"3\", \"outcome\": \"unknown\" }\n  ]\n}","section_id":"post-start-response"},{"id":"post-stop-response-ex0","language":"json","code":"{\n  \"results\": [\n    { \"uid\": \"1\", \"outcome\": \"stopped\", \"stopped_at\": 1771774200, \"ran_for_sec\": 3600 },\n    { \"uid\": \"2\", \"outcome\": \"already_stopped\" }\n  ]\n}","section_id":"post-stop-response"},{"id":"delete-response-ex0","language":"json","code":"{\n  \"results\": [\n    { \"uid\": \"1\", \"outcome\": \"deleted\", \"freed_bytes\": 131072000 },\n    { \"uid\": \"2\", \"outcome\": \"no_data\" }\n  ]\n}","section_id":"delete-response"},{"id":"delete-response-ex1","language":"json","code":"{\n  \"error_code\": \"granular_metrics_running\",\n  \"description\": \"cannot cleanup granular metrics — still running on 1 node(s)\",\n  \"running_nodes\": [ { \"uid\": \"1\", \"started_at\": 1771770600 } ]\n}","section_id":"delete-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/metrics","title":"Metrics requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/metrics/","summary":"Local metrics storage requests","tags":["docs","operate","rs"],"last_updated":"2026-09-29T16:51:25+03:00","children":[{"id":"operate/rs/references/rest-api/requests/metrics/granular","summary":"Granular metrics collection requests","title":"Granular metrics requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/metrics/granular/"}],"page_type":"content","content_hash":"74586ead7a8dc97033a5fb300047803a2797a3645e89beaa2960ca6022486e7f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-metrics-status) | `/v1/metrics/status` | Get the local metrics storage status of cluster nodes |"},{"id":"get-metrics-status","title":"Get metrics status","role":"content","text":"GET /v1/metrics/status\n\nGet the status of all nodes or a specific node. The response describes the state of each node's store. It doesn't return the stored metrics.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_cluster_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_cluster_info) |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/metrics/status?node_uid=1\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| node_uid | integer | Optional. The ID of the node to get the status of. If omitted, returns the status of all nodes. |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns a `nodes` array with one object for each node in scope.\n\n#### Example JSON body\n\n[code example]\n\n#### Node status fields\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | string | The node ID |\n| state | string | State of the node's metrics store:<br />`running`<br />`stopped`<br />`unknown` |\n| disk_usage_bytes | integer | Disk space used by stored metrics, in bytes |\n| disk_max_bytes | integer | Configured maximum disk space for stored metrics, in bytes, set by [`local_storage_max_size_mb`](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/metrics_config) |\n| retention_days_configured | integer | Configured retention, in days, set by [`local_storage_retention_days`](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/metrics_config) |\n| retention_days_effective | integer | Retention currently in effect on the node, in days. It matches `retention_days_configured` unless the node has been under [storage pressure](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/local-metrics-storage#storage-pressure-retention), which lowers effective retention to keep stored data within `disk_max_bytes`. When the two values differ, the node keeps less history than configured. |\n| data_range | object | Time range covered by the node's stored samples, with `from` and `to` timestamps |\n| health | string | Health of the node's metrics store:<br />`ok`<br />`degraded`<br />`unknown` |"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success. |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | The `node_uid` doesn't match a node in the cluster (`error_code`: `node_not_found`). |\n| [500 Internal Server Error](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1) | Internal server error. |"}],"examples":[{"id":"get-response-ex0","language":"json","code":"{\n  \"nodes\": [\n    {\n      \"uid\": \"1\",\n      \"state\": \"running\",\n      \"disk_usage_bytes\": 471859200,\n      \"disk_max_bytes\": 1073741824,\n      \"retention_days_configured\": 8,\n      \"retention_days_effective\": 6,\n      \"data_range\": { \"from\": \"2026-02-15T00:00Z\", \"to\": \"2026-02-23T10:00Z\" },\n      \"health\": \"ok\"\n    }\n  ]\n}","section_id":"get-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/metrics_config","title":"Metrics configuration requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/metrics_config/","summary":"Metrics configuration requests","tags":["docs","operate","rs"],"last_updated":"2026-07-21T11:19:25-05:00","children":[],"page_type":"content","content_hash":"53fe0dac361c22baadd484a20f937731552447c99fa0f8be1b6297259574e9f8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-metrics-config) | `/v1/metrics_config` | Get the cluster's metrics configuration |\n| [PUT](#put-metrics-config) | `/v1/metrics_config` | Update the cluster's metrics configuration |"},{"id":"get-metrics-config","title":"Get metrics configuration","role":"content","text":"GET /v1/metrics_config\n\nGet the cluster-wide configuration of the [v2 metrics stream engine](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine).\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_cluster_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_cluster_info) |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/metrics_config\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns a [metrics configuration object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/metrics_config). If the configuration has not been set, the default values are returned.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |"},{"id":"put-metrics-config","title":"Update metrics configuration","role":"content","text":"PUT /v1/metrics_config\n\nUpdate the cluster's metrics configuration.\n\nThis is a partial update: only the fields included in the request are changed, and any omitted fields keep their stored values. When a list field is included, its entire value is replaced; it is not merged with the stored list. The request must include at least one recognized field. If the configuration has never been set, the first update creates it, and any omitted fields take their default values.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [update_cluster](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#update_cluster) |"},{"id":"put-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tPUT /v1/metrics_config\n\n#### Example JSON body\n\n[code example]\n\nThe above request enables the key distribution histograms, sets local metrics retention to 14 days, enables database tags in metrics, and exposes the `env` and `team` tag keys.\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### Request body\n\nInclude a [metrics configuration object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/metrics_config) with the fields to update in the request body."},{"id":"put-response","title":"Response","role":"returns","text":"Returns the complete [metrics configuration object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/metrics_config) after the update.\n\n#### Example JSON body\n\n[code example]"},{"id":"put-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error. |\n| [400 Bad Request](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1) | Bad content provided, or the request body is empty. |"}],"examples":[{"id":"get-response-ex0","language":"json","code":"{\n    \"key_distribution_enabled\": false,\n    \"key_size_buckets\": \"\",\n    \"key_items_buckets\": \"\",\n    \"local_storage_max_size_mb\": 1024,\n    \"local_storage_retention_days\": 8,\n    \"expose_db_tags\": false,\n    \"metrics_tag_keys_exposed\": [],\n    \"max_requests_in_flight\": 2\n}","section_id":"get-response"},{"id":"put-request-ex0","language":"json","code":"{\n    \"key_distribution_enabled\": true,\n    \"local_storage_retention_days\": 14,\n    \"expose_db_tags\": true,\n    \"metrics_tag_keys_exposed\": [\"env\", \"team\"]\n}","section_id":"put-request"},{"id":"put-response-ex0","language":"json","code":"{\n    \"key_distribution_enabled\": true,\n    \"key_size_buckets\": \"\",\n    \"key_items_buckets\": \"\",\n    \"local_storage_max_size_mb\": 1024,\n    \"local_storage_retention_days\": 14,\n    \"expose_db_tags\": true,\n    \"metrics_tag_keys_exposed\": [\"env\", \"team\"],\n    \"max_requests_in_flight\": 2\n}","section_id":"put-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/migrations","title":"Migrations requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/migrations/","summary":"REST API request to get the migration status of a database in the cluster when using Replica Of.","tags":["docs","operate","rs"],"last_updated":"2025-12-22T11:45:26-06:00","children":[],"page_type":"content","content_hash":"9c05a6aedd0c1fcc4a162d8315b9dfc4ddc92af8d0610ff563fee738c16dd8e5","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-migrations) | `/v1/migrations/<uid>` | Get database migration status |"},{"id":"get-migrations","title":"Get migration status","role":"content","text":"[code example]\n\nGets the migration status of a database in the cluster when using Replica Of.\n\n#### Required permissions\n\n| Permission name | Roles |\n|-----------------|-------|\n| [view_bdb_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_bdb_info) | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The database's unique ID |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns a JSON array with all data required by the migration orchestrator.\n\n#### Example response body\n\n[code example]\n\n#### Status codes {#get-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | No error |\n| [404 Not Found](https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found) | Database does not exist |"}],"examples":[{"id":"get-migrations-ex0","language":"sh","code":"GET /v1/migrations/<uid>","section_id":"get-migrations"},{"id":"get-request-ex0","language":"sh","code":"GET /v1/migrations/1","section_id":"get-request"},{"id":"get-response-ex0","language":"json","code":"{\n  \"migration\": {\n    \"status\": \"string\",\n    \"lag\": 0,\n    \"rdb_size\": 0,\n    \"rdb_transferred\": 0,\n    \"run_id\": \"string\",\n    \"flush_counter\": 0,\n    \"source_shards\": [\n      {\n        \"replication_id\": \"string\",\n        \"replication_offset\": 0\n      }\n    ],\n    \"error\": {\n      \"error_code\": \"string\",\n      \"message\": \"string\",\n      \"timestamp\": \"2019-08-24T14:15:22Z\"\n    }\n  }\n}","section_id":"get-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/modules/config","title":"Configure module requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/modules/config/","summary":"Configure module requests","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"2b8f4f606675687019259e2a85318795c622f998addb89c9a439030c00fabfa5","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This REST API path is deprecated as of Redis Software version 7.4. Use [`PUT /v2/bdbs/<uid>`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#put-bdbs-action) instead.\n\n\n| Method | Path | Description |\n|--------|------|-------------|\n| [POST](#post-modules-config-bdb) | `/v1/modules/config/bdb/{uid}` | Configure module |"},{"id":"post-modules-config-bdb","title":"Configure module","role":"configuration","text":"POST /v1/modules/config/bdb/{string: uid}\n\nUse the module runtime configuration command (if defined) to configure new arguments for the module.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [edit_bdb_module](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#edit_bdb_module) |"},{"id":"post-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tPOST /v1/modules/config/bdb/1\n\n#### Example JSON body\n\n[code example]\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n\n#### Request body\n\n| Field | Type | Description |\n|-------|------|-------------|\n| modules | list of JSON objects | List of modules (module_name) and their new configuration settings (module_args) |\n| module_name | `search`<br />`ReJSON`<br />`graph`<br />`timeseries`<br />`bf` | Module's name |\n| module_args | string | Module command line arguments (pattern does not allow special characters &,<,>,\") |"},{"id":"post-response","title":"Response","role":"returns","text":"Returns a status code. If an error occurs, the response body may include an error code and message with more details."},{"id":"post-error-codes","title":"Error codes","role":"errors","text":"When errors are reported, the server may return a JSON object with `error_code` and `message` field that provide additional information. The following are possible `error_code` values:\n\n| Code | Description |\n|------|-------------|\n| db_not_exist | Database with given UID doesn't exist in cluster | \n| missing_field | \"module_name\" or \"module_args\" are not defined in request | \n| invalid_schema | JSON object received is not a dict object | \n| param_error | \"module_args\" parameter was not parsed properly | \n| module_not_exist | Module with given \"module_name\" does not exist for the database |"},{"id":"post-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success, module updated on bdb. |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | bdb not found. |\n| [400 Bad Request](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1) | Bad or missing configuration parameters. |\n| [406 Not Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | Module does not support runtime configuration of arguments. |"}],"examples":[{"id":"post-request-ex0","language":"json","code":"{\n  \"modules\": [\n    {\n      \"module_name\": \"search\",\n      \"module_args\": \"MINPREFIX 3 MAXEXPANSIONS 1000\"\n    }\n  ]\n}","section_id":"post-request"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/modules","title":"Modules requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/modules/","summary":"Redis modules requests","tags":["docs","operate","rs"],"last_updated":"2025-10-30T12:00:21-05:00","children":[{"id":"operate/rs/references/rest-api/requests/modules/config","summary":"Configure module requests","title":"Configure module requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/modules/config/"},{"id":"operate/rs/references/rest-api/requests/modules/user-defined","summary":"Custom, user-defined Redis module requests","title":"User-defined module requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/modules/user-defined/"}],"page_type":"content","content_hash":"e54a9e4fc0534c315f2e7cc936a1f691feb597305ec3c94889f15457a034cc8c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#list-modules) | `/v1/modules` | List available modules |\n| [GET](#get-module) | `/v1/modules/{uid}` | Get a specific module |"},{"id":"list-modules","title":"List modules","role":"content","text":"[code example]\n\nList available modules, i.e. modules stored within the CCS.\n\n#### Permissions\n\n| Permission name | Roles |\n|-----------------|-------|\n| [view_cluster_modules](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_cluster_modules) | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |"},{"id":"list-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | 127.0.0.1:9443 | Domain name |\n| Accept | \\*/\\* | Accepted media type |"},{"id":"list-response","title":"Response","role":"returns","text":"Returns a JSON array of [module objects](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/module).\n\n#### Status codes {#list-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |"},{"id":"get-module","title":"Get module","role":"content","text":"[code example]\n\nGet specific available modules, i.e. modules stored within the CCS.\n\n#### Permissions\n\n| Permission name | Roles |\n|-----------------|-------|\n| [view_cluster_modules](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_cluster_modules) | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | 127.0.0.1:9443 | Domain name |\n| Accept | \\*/\\* | Accepted media type |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The module's unique ID. |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns a [module object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/module)."},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Module does not exist. |"}],"examples":[{"id":"list-modules-ex0","language":"sh","code":"GET /v1/modules","section_id":"list-modules"},{"id":"list-request-ex0","language":"sh","code":"GET /v1/modules","section_id":"list-request"},{"id":"get-module-ex0","language":"sh","code":"GET /v1/modules/{string: uid}","section_id":"get-module"},{"id":"get-request-ex0","language":"sh","code":"GET /v1/modules/1","section_id":"get-request"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/modules/user-defined","title":"User-defined module requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/modules/user-defined/","summary":"Custom, user-defined Redis module requests","tags":["docs","operate","rs"],"last_updated":"2025-12-12T16:08:56-06:00","page_type":"content","content_hash":"d9eb66b624d301dd6f7659d0a6b6b920bf9bc43495d42abfbefd79b05a457d7c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-local-user-defined-artifacts) | /v2/local/modules/user-defined/artifacts | List custom module artifacts on a node |\n| [POST](#post-user-defined-module) | /v2/modules/user-defined | Upload custom module configuration |\n| [POST](#post-local-user-defined-artifacts) | /v2/local/modules/user-defined/artifacts | Upload a custom module artifact to a node |\n| [DELETE](#delete-user-defined-module) | /v2/modules/user-defined/<uid> | Delete a custom module configuration |\n| [DELETE](#delete-local-user-defined-artifacts) | /v2/local/modules/user-defined/artifacts/<module_name>/<version> | Delete a custom module artifact from a node |"},{"id":"get-local-user-defined-artifacts","title":"List custom module artifacts","role":"content","text":"[code example]\n\nReturns a list of all custom module artifacts on the local node.\n\n#### Permissions\n\n| Permission name | Roles |\n|-----------------|-------|\n| [view_cluster_modules](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_cluster_modules) | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |"},{"id":"get-local-artifacts-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | \\*/\\* | Accepted media type |"},{"id":"get-local-artifacts-response","title":"Response","role":"returns","text":"Returns a JSON array of custom module artifacts.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-local-artifacts-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | Success, returns list of artifacts |"},{"id":"post-user-defined-module","title":"Upload custom module configuration","role":"content","text":"[code example]\n\nCreates a module object in the cluster configuration store (CCS). After calling this endpoint, you still need to upload the module's `.zip` file, which contains the `.so` and `module.json` files, to each node by calling [`POST /v2/local/modules/user-defined/artifacts`](#post-local-user-defined-artifacts) on each node.\n\n#### Permissions\n\n| Permission name | Roles |\n|-----------------|-------|\n| [manage_cluster_modules](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#manage_cluster_modules) | admin |"},{"id":"post-user-defined-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Example JSON body\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### Request body\n\nThe request body is a JSON object that must contain the following fields:\n\n| Field | Type | Description |\n|-------|------|-------------|\n| module_name | string | Name of the module |\n| version | integer | Module version number |\n| semantic_version | string | Module's semantic version |\n| display_name | string | Display name for the module |\n| commands | array of objects | List of commands provided by the module |\n| command_line_args | string | Command line arguments for the module |\n| capabilities | array of strings | List of capabilities supported by the module |\n| min_redis_version | string | Minimum Redis version required |"},{"id":"post-user-defined-response","title":"Response","role":"returns","text":"Returns a [module object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/module).\n\n#### Example JSON body\n\n[code example]"},{"id":"post-user-defined-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | Success, the module was created in the CCS. |\n| [406 Not Acceptable](https://www.rfc-editor.org/rfc/rfc9110.html#name-406-not-acceptable) | There was an issue with the module object, such as missing required fields or invalid values. |"},{"id":"post-local-user-defined-artifacts","title":"Upload custom module artifact to a node","role":"content","text":"[code example]\n\nA local API to upload a custom module's artifact to the current node. You must call this API on each cluster node.\n\nThe module must be packaged as a `.zip` file containing:\n\n- **module.json**: A metadata file with module information including:\n  - `module_name`: The actual module name\n  - `version`: Numeric version\n  - `semantic_version`: Semantic version string (for example, \"1.0.0\")\n  - `min_redis_version`: Minimum compatible Redis version\n  - `commands`: List of commands the module provides\n  - `capabilities`: List of module capabilities\n\n- **Module binary**: The compiled `.so` file for the target platform\n\n#### Permissions\n\n| Permission name | Roles |\n|-----------------|-------|\n| [manage_cluster_modules](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#manage_cluster_modules) | admin |"},{"id":"post-local-artifacts-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | 127.0.0.1:9443 | Domain name |\n| Accept | \\*/\\* | Accepted media type |\n| Content-Length | 865 | Length of the request body in octets |\n| Expect | 100-continue | Requires particular server behaviors |\n| Content-Type | multipart/form-data; boundary=------------------------4751ac3b332ace13 | Media type of request/response body |"},{"id":"post-local-artifacts-response","title":"Response","role":"returns","text":"Returns a status code to indicate upload success or failure."},{"id":"post-local-artifacts-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | Success, module artifact uploaded to local node |\n| [400 Bad Request](https://www.rfc-editor.org/rfc/rfc9110.html#name-400-bad-request) | Missing or bad artifact |\n| [406 Not Acceptable](https://www.rfc-editor.org/rfc/rfc9110.html#name-406-not-acceptable) | There was an issue with the module object artifact, such as bad metadata |"},{"id":"delete-user-defined-module","title":"Delete custom module","role":"content","text":"[code example]\n\nDelete a module object from the cluster configuration store (CCS). This REST API request does not delete the module artifact from the nodes, so you also need to call [`DELETE /v2/local/modules/user-defined/artifacts/<module_name>/<version>`](#delete-local-user-defined-artifacts) on each node.\n\n#### Permissions\n\n| Permission name | Roles |\n|-----------------|-------|\n| [manage_cluster_modules](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#manage_cluster_modules) | admin |"},{"id":"delete-user-defined-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | string | The module's unique ID |"},{"id":"delete-user-defined-response","title":"Response","role":"returns","text":"Returns a status code to indicate module deletion success or failure."},{"id":"delete-user-defined-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | Success, the module is deleted. |\n| [404 Not Found](https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found) | Attempting to delete a non-existing module. |\n| [406 Not Acceptable](https://www.rfc-editor.org/rfc/rfc9110.html#name-406-not-acceptable) | The request is not acceptable. |"},{"id":"delete-local-user-defined-artifacts","title":"Delete custom module artifact from a node","role":"content","text":"[code example]\n\nA local API to delete a custom module's artifact from the current node. You must call this API on each cluster node.\n\n#### Permissions\n\n| Permission name | Roles |\n|-----------------|-------|\n| [manage_cluster_modules](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#manage_cluster_modules) | admin |"},{"id":"delete-local-artifacts-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| module_name | string | The name of the module artifact to delete |\n| version | integer | The version of the module artifact to delete |"},{"id":"delete-local-artifacts-response","title":"Response","role":"returns","text":"Returns a status code to indicate deletion success or failure."},{"id":"delete-local-artifacts-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | Success, the module artifact is deleted from the local node. |\n| [404 Not Found](https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found) | Attempting to delete a non-existing module. |\n| [406 Not Acceptable](https://www.rfc-editor.org/rfc/rfc9110.html#name-406-not-acceptable) | The request is not acceptable. |"}],"examples":[{"id":"get-local-user-defined-artifacts-ex0","language":"sh","code":"GET /v2/local/modules/user-defined/artifacts","section_id":"get-local-user-defined-artifacts"},{"id":"get-local-artifacts-request-ex0","language":"sh","code":"GET /v2/local/modules/user-defined/artifacts","section_id":"get-local-artifacts-request"},{"id":"get-local-artifacts-response-ex0","language":"json","code":"[\n     {\n         \"module_name\": \"TestModule\",\n         \"version\": 123,\n         \"dependencies\": [\n             \"dep_1\",\n             \"dep_2\"\n         ]\n     }\n]","section_id":"get-local-artifacts-response"},{"id":"post-user-defined-module-ex0","language":"sh","code":"POST /v2/modules/user-defined","section_id":"post-user-defined-module"},{"id":"post-user-defined-request-ex0","language":"sh","code":"POST /v2/modules/user-defined","section_id":"post-user-defined-request"},{"id":"post-user-defined-request-ex1","language":"json","code":"{\n     \"module_name\": \"TestModule\",\n     \"version\": 1,\n     \"semantic_version\": \"0.0.1\",\n     \"display_name\": \"test module\",\n     \"commands\": [\n         {\n             \"command_arity\": -1,\n             \"command_name\": \"json.arrtrim\",\n             \"first_key\": 1,\n             \"flags\": [\"write\"],\n             \"last_key\": 1,\n             \"step\": 1\n         }\n     ],\n     \"command_line_args\": \"\",\n     \"capabilities\": [\"list\", \"of\", \"capabilities\"],\n     \"min_redis_version\": \"2.1\"\n}","section_id":"post-user-defined-request"},{"id":"post-user-defined-response-ex0","language":"json","code":"{\n    \"author\": \"author name\",\n    \"uid\": \"1952fcf9a5109fb59e61b1ad4d7e2d88\"\n    // additional fields...\n}","section_id":"post-user-defined-response"},{"id":"post-local-user-defined-artifacts-ex0","language":"sh","code":"POST /v2/local/modules/user-defined/artifacts","section_id":"post-local-user-defined-artifacts"},{"id":"post-local-artifacts-request-ex0","language":"sh","code":"POST /v2/local/modules/user-defined/artifacts","section_id":"post-local-artifacts-request"},{"id":"delete-user-defined-module-ex0","language":"sh","code":"DELETE /v2/modules/user-defined/{string: uid}","section_id":"delete-user-defined-module"},{"id":"delete-user-defined-request-ex0","language":"sh","code":"DELETE /v2/modules/user-defined/1","section_id":"delete-user-defined-request"},{"id":"delete-local-user-defined-artifacts-ex0","language":"sh","code":"DELETE /v2/local/modules/user-defined/artifacts/{string: module_name}/{int: version}","section_id":"delete-local-user-defined-artifacts"},{"id":"delete-local-artifacts-request-ex0","language":"sh","code":"DELETE /v2/local/modules/user-defined/artifacts/some-custom-module/123","section_id":"delete-local-artifacts-request"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/node_master_healthcheck","title":"Node master healthcheck requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/node_master_healthcheck/","summary":"REST API requests to check a node's connection to the cluster's primary node.","tags":["docs","operate","rs"],"last_updated":"2025-06-24T08:51:54-05:00","children":[],"page_type":"content","content_hash":"e74bd58db15a8e78cc930ab6f6d5591463baad06a60c1ac1a0793f2f46297e3c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-node-master-healthcheck) | `/v1/local/node/master_healthcheck` | Checks a node's connection to the primary node |"},{"id":"get-node-master-healthcheck","title":"Get master healthcheck","role":"content","text":"[code example]\n\nChecks whether the current node has a valid connection to the cluster's primary node and the Cluster Configuration Store (CCS)."},{"id":"required-permissions","title":"Required permissions","role":"content","text":"| Permission name | Roles |\n|-----------------|-------|\n| [view_cluster_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_cluster_info) | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns a JSON object that includes the status of the current node's connection to the cluster's primary node and the CCS.\n\n- `active`: the current node has a valid connection.\n\n- `inactive`: the current node doesn't have a valid connection.\n\n#### Example JSON response body\n\n[code example]\n\n#### Status codes {#get-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | No error |"}],"examples":[{"id":"get-node-master-healthcheck-ex0","language":"sh","code":"GET /v1/local/node/master_healthcheck","section_id":"get-node-master-healthcheck"},{"id":"get-request-ex0","language":"sh","code":"GET /v1/local/node/master_healthcheck","section_id":"get-request"},{"id":"get-response-ex0","language":"json","code":"{\n  \"status\": \"active\"\n}","section_id":"get-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/nodes/actions","title":"Node actions requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/nodes/actions/","summary":"Node action requests","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"789ef8f69b35d201aa1a29a396991d56cee30b88416d88e14558aa0aa72b78b1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-all-nodes-actions) | `/v1/nodes/actions` | Get status of all actions on all nodes|\n| [GET](#get-node-actions) | `/v1/nodes/{node_uid}/actions` | Get status of all actions on a specific node |\n| [GET](#get-node-action) | `/v1/nodes/{node_uid}/actions/{action}` | Get status of an action on a specific node |\n| [POST](#post-node-action) | `/v1/nodes/{node_uid}/actions/{action}` | Initiate node action |\n| [DELETE](#delete-node-action) | `/v1/nodes/{node_uid}/actions/{action}` | Cancel action or remove action status |"},{"id":"get-all-nodes-actions","title":"Get all actions","role":"content","text":"[code example]\n\nGet the status of all currently executing, pending, or completed\nactions on all nodes."},{"id":"permissions","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [view_status_of_all_node_actions](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_status_of_all_node_actions) | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |"},{"id":"get-all-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |"},{"id":"get-all-response","title":"Response","role":"returns","text":"Returns a list of [action objects](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/action)."},{"id":"get-all-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error, response provides details about an ongoing action. |"},{"id":"get-node-actions","title":"Get node actions statuses","role":"content","text":"[code example]\n\nGet the status of all actions on a specific node.\n\n#### Required permissions\n\n| Permission name | Roles |\n|-----------------|-------|\n| [view_status_of_node_action](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_status_of_node_action) | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |"},{"id":"get-request-all-actions","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| action | string | The action to check. |"},{"id":"get-response-all-actions","title":"Response","role":"returns","text":"Returns a JSON object that includes a list of [action objects](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/action) for the specified node.\n\nIf no actions are available, the response will include an empty array.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-error-codes-all-actions","title":"Error codes","role":"errors","text":"| Code | Description |\n|------|-------------|\n| internal_error | An internal error that cannot be mapped to a more precise error code has been encountered. |\n| insufficient_resources | The cluster does not have sufficient resources to complete the required operation. |"},{"id":"get-status-codes-all-actions","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error, response provides details about an ongoing action. |\n| [404&nbsp;Not&nbsp;Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Action does not exist (i.e. not currently running and no available status of last run). |"},{"id":"get-node-action","title":"Get node action status","role":"content","text":"[code example]\n\nGet the status of a currently executing, queued, or completed action on a specific node."},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns an [action object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/action) for the specified node."},{"id":"get-error-codes","title":"Error codes","role":"errors","text":"| Code | Description |\n|------|-------------|\n| internal_error | An internal error that cannot be mapped to a more precise error code has been encountered. |\n| insufficient_resources | The cluster does not have sufficient resources to complete the required operation. |"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error, response provides details about an ongoing action. |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Action does not exist (i.e. not currently running and no available status of last run). |"},{"id":"post-node-action","title":"Initiate node action","role":"content","text":"[code example]\n\nInitiate a node action.\n\nThe API allows only a single instance of any action type to be\ninvoked at the same time, and violations of this requirement will\nresult in a `409 CONFLICT` response.\n\nThe caller is expected to query and process the results of the\npreviously executed instance of the same action, which will be\nremoved as soon as the new one is submitted.\n\n#### Permissions\n\n| Permission name | Roles |\n|-----------------|-------|\n| [start_node_action](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#start_node_action) | admin |"},{"id":"post-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| action | string | The name of the action required. |\n\nCurrently supported actions are:\n\n- `remove`: Removes the node from the cluster after migrating all bound resources to other nodes. As soon as a successful remove request is received, the cluster will no longer automatically migrate resources, such as shards and endpoints, to the node even if the remove task fails at some point.\n\n    - By default, the remove node action completes after all resources migrate off the removed node. Node removal does not wait for migrated shards' persistence files to be created on the new nodes.\n    \n        To change node removal to wait for the creation of new persistence files for all migrated shards, set `wait_for_persistence` to `true` in the request body or [update the cluster policy](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/policy#put-cluster-policy) `persistent_node_removal` to `true` to change the cluster's default behavior.\n\n        [code example]\n\n- `maintenance_on`: Creates a snapshot of the node, migrates shards to other nodes, and prepares the node for maintenance. See [maintenance mode](https://redis.io/docs/latest/operate/rs/clusters/maintenance-mode) for more information.\n\n    - As of Redis Software version 7.4.2, a new node snapshot is created only if no maintenance mode snapshots already exist or if you set `\"overwrite_snapshot\": true` in the request body.\n\n        [code example]\n\n    - If there aren't enough resources to migrate shards out of the maintained node, set `\"evict_ha_replica\": false` and `\"evict_active_active_replica\": false` in the request body to keep the replica shards in place but demote any master shards. Use these two parameters instead of `keep_slave_shards`, which is deprecated as of Redis Software version 7.4.2.\n\n        [code example]\n\n    - To specify databases whose shards should be evicted from the node when entering maintenance mode, set `\"evict_dbs\": [\"List of database ID strings\"]` in the request body.\n\n        [code example]\n\n- `maintenance_off`: Restores node to its previous state before maintenance started. See [maintenance mode](https://redis.io/docs/latest/operate/rs/clusters/maintenance-mode) for more information.\n\n    - By default, it uses the latest node snapshot.\n\n    - Use `\"snapshot_name\":`&nbsp;`\"...\"` in the request body to restore the state from a specific snapshot.\n\n    - To avoid restoring shards at the node, use `\"skip_shards_restore\":`&nbsp;`true`.\n\n- `enslave_node`: Turn node into a replica."},{"id":"post-response","title":"Response","role":"returns","text":"The body content may provide additional action details."},{"id":"delete-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Action initiated successfully. |\n| [409 Conflict](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.10) | Only a single instance of any action type can be invoked at the same time. |"},{"id":"example-requests","title":"Example requests","role":"example","text":"#### cURL\n\n[code example]\n\n#### Python\n\n[code example]"},{"id":"delete-node-action","title":"Cancel action","role":"content","text":"[code example]\n\nCancel a queued or executing node action, or remove the status of a\npreviously executed and completed action."},{"id":"permissions-1","title":"Permissions","role":"security","text":"| Permission name |\n|-----------------|\n| [cancel_node_action](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#cancel_node_action) |"},{"id":"delete-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| action | string | The name of the action to cancel. |"},{"id":"delete-response","title":"Response","role":"returns","text":"Returns a status code.\n\n#### Status codes {#delete-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Action will be cancelled when possible. |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Action unknown or not currently running. |"}],"examples":[{"id":"get-all-nodes-actions-ex0","language":"sh","code":"GET /v1/nodes/actions","section_id":"get-all-nodes-actions"},{"id":"get-all-request-ex0","language":"sh","code":"GET /v1/nodes/actions","section_id":"get-all-request"},{"id":"get-node-actions-ex0","language":"sh","code":"GET /v1/nodes/{node_uid}/actions","section_id":"get-node-actions"},{"id":"get-request-all-actions-ex0","language":"sh","code":"GET /v1/nodes/1/actions","section_id":"get-request-all-actions"},{"id":"get-response-all-actions-ex0","language":"json","code":"{\n    \"actions\": [\n        {\n            \"name\": \"remove_node\",\n            \"node_uid\": \"1\",\n            \"status\": \"running\",\n            \"progress\": 10\n        }\n    ]\n}","section_id":"get-response-all-actions"},{"id":"get-node-action-ex0","language":"sh","code":"GET /v1/nodes/{node_uid}/actions/{action}","section_id":"get-node-action"},{"id":"get-request-ex0","language":"sh","code":"GET /v1/nodes/1/actions/remove","section_id":"get-request"},{"id":"post-node-action-ex0","language":"sh","code":"POST /v1/nodes/{node_uid}/actions/{action}","section_id":"post-node-action"},{"id":"post-request-ex0","language":"sh","code":"POST /v1/nodes/1/actions/remove","section_id":"post-request"},{"id":"post-request-ex1","language":"sh","code":"POST /v1/nodes/<node_id>/actions/remove\n        {\n            \"wait_for_persistence\": true\n        }","section_id":"post-request"},{"id":"post-request-ex2","language":"sh","code":"POST /v1/nodes/1/actions/maintenance_on\n        {\n            \"overwrite_snapshot\": true\n        }","section_id":"post-request"},{"id":"post-request-ex3","language":"sh","code":"POST /v1/nodes/1/actions/maintenance_on\n        {\n            \"overwrite_snapshot\": true,\n            \"evict_ha_replica\": false,\n            \"evict_active_active_replica\": false\n        }","section_id":"post-request"},{"id":"post-request-ex4","language":"sh","code":"POST /v1/nodes/1/actions/maintenance_on\n        {\n            \"overwrite_snapshot\": true,\n            \"evict_dbs\": [\"1\", \"3\"] \n        }","section_id":"post-request"},{"id":"example-requests-ex0","language":"sh","code":"$ curl -k -X POST -u \"[username]:[password]\" -d \"{}\"\n            https://[host][:port]/v1/nodes/1/actions/remove","section_id":"example-requests"},{"id":"example-requests-ex1","language":"python","code":"import requests\nimport json\n\nurl = \"https://[host][port]/v1/nodes/1/actions/remove\"\n\npayload = json.dumps({})\nheaders = {\n  'Content-Type': 'application/json',\n}\nauth = (\"[username]\", \"[password]\")\n\nresponse = requests.request(\"POST\", url, auth=auth, headers=headers, data=payload)\n\nprint(response.text)","section_id":"example-requests"},{"id":"delete-node-action-ex0","language":"sh","code":"DELETE /v1/nodes/{node_uid}/actions/{action}","section_id":"delete-node-action"},{"id":"delete-request-ex0","language":"sh","code":"DELETE /v1/nodes/1/actions/remove","section_id":"delete-request"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/nodes/alerts","title":"Node alerts requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/nodes/alerts/","summary":"Node alert requests","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","page_type":"content","content_hash":"d95f16ba76e80433f106030fe8351eac5749b665a148c29c53b236a2a5846cdb","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-all-nodes-alerts) | `/v1/nodes/alerts` | Get all alert states for all nodes |\n| [GET](#get-node-alerts) | `/v1/nodes/alerts/{uid}` | Get all alert states for a node |\n| [GET](#get-node-alert) | `/v1/nodes/alerts/{uid}/{alert}` | Get node alert state |"},{"id":"get-all-nodes-alerts","title":"Get all alert states","role":"content","text":"GET /v1/nodes/alerts\n\nGet all alert states for all nodes.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_all_nodes_alerts](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_all_nodes_alerts) |"},{"id":"get-all-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/nodes/alerts \n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| ignore_settings | boolean | Retrieve updated alert state regardless of the cluster's alert_settings. When not present, a disabled alert will always be retrieved as disabled with a false state. (optional) |"},{"id":"get-all-response","title":"Response","role":"returns","text":"Returns a hash of node UIDs and the [alert states](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/alert) for each node.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-all-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |"},{"id":"get-node-alerts","title":"Get node alert states","role":"content","text":"GET /v1/nodes/alerts/{int: uid}\n\nGet all alert states for a node.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_node_alerts](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_node_alerts) |"},{"id":"get-request-all-alerts","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/nodes/alerts/1 \n\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| ignore_settings | boolean | Retrieve updated alert state regardless of the cluster's alert_settings. When not present, a disabled alert will always be retrieved as disabled with a false state. (optional) |"},{"id":"get-response-all-alerts","title":"Response","role":"returns","text":"Returns a hash of [alert objects](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/alert) and their states for a specific node.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes-all-alerts","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Specified node does not exist |"},{"id":"get-node-alert","title":"Get node alert state","role":"content","text":"GET /v1/nodes/alerts/{int: uid}/{alert}\n\nGet a node alert state.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_node_alerts](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_node_alerts) |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/nodes/alerts/1/node_cpu_utilization \n\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| ignore_settings | boolean | Retrieve updated alert state regardless of the cluster's alert_settings. When not present, a disabled alert will always be retrieved as disabled with a false state. (optional) |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns an [alert object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/alert).\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Specified alert or node does not exist |"}],"examples":[{"id":"get-all-response-ex0","language":"json","code":"{\n    \"1\": {\n        \"node_cpu_utilization\": {\n            \"change_time\": \"2014-12-22T10:42:00Z\",\n            \"change_value\": {\n                \"cpu_util\": 2.500000000145519,\n                \"global_threshold\": \"1\",\n                \"state\": true\n            },\n            \"enabled\": true,\n            \"state\": true,\n            \"severity\": \"WARNING\"\n        },\n        \"...\"\n    },\n    \"...\"\n}","section_id":"get-all-response"},{"id":"get-response-all-alerts-ex0","language":"json","code":"{\n   \"node_cpu_utilization\": {\n        \"change_time\": \"2014-12-22T10:42:00Z\",\n        \"change_value\": {\n            \"cpu_util\": 2.500000000145519,\n            \"global_threshold\": \"1\",\n            \"state\": true\n        },\n        \"enabled\": true,\n        \"state\": true,\n        \"severity\": \"WARNING\",\n    },\n    \"...\"\n}","section_id":"get-response-all-alerts"},{"id":"get-response-ex0","language":"json","code":"{\n    \"change_time\": \"2014-12-22T10:42:00Z\",\n    \"change_value\": {\n        \"cpu_util\": 2.500000000145519,\n        \"global_threshold\": \"1\",\n        \"state\": true\n    },\n    \"enabled\": true,\n    \"state\": true,\n    \"severity\": \"WARNING\",\n}","section_id":"get-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/nodes/check","title":"Check node requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/nodes/check/","summary":"Requests that run checks on a cluster node.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"71fe55c09522bb23edbb7651355dff1668de73d60537ddb313f1033eeee79101","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-node-check) | `/v1/nodes/check/{uid}` | Runs checks on a cluster node |"},{"id":"get-node-check","title":"Check node","role":"content","text":"GET /v1/nodes/check/{int: uid}\n\nRuns the following checks on a cluster node:\n\n| Check name | Description |\n|-----------|-------------|\n| bootstrap_status | Verifies the local node's bootstrap process completed without errors. |\n| services | Verifies all Redis Software services are running. |\n| port_range | Verifies the [`ip_local_port_range`](https://www.kernel.org/doc/html/latest/networking/ip-sysctl.html) doesn't conflict with the ports Redis Software might assign to shards. |\n| pidfiles | Verifies all active local shards have PID files. |\n| capabilities | Verifies all binaries have the proper capability bits. |\n| existing_sockets | Verifies sockets exist for all processes that require them. |\n| host_settings | Verifies the following:<br />• Linux `overcommit_memory` setting is 1.<br />• `transparent_hugepage` is disabled.<br />• Socket maximum connections setting `somaxconn` is 1024. |\n| tcp_connectivity | Verifies this node can connect to all other alive nodes. |\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_node_check](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_node_check) |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/nodes/check/1\n\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The node's unique ID. |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns a JSON object with the node's check results.\n\nWhen errors occur, the server returns a JSON object with `result: false` and an `error` field that provides additional information. If an error occurs during a check, the `error` field only includes a message for the first check that fails.\n\nPossible `error` messages:\n\n- \"bootstrap request to cnm_http failed,resp_code: ...,resp_content: ...\"\n- \"process ... is not running or not responding (...)\"\n- \"could not communicate with 'supervisorctl': ...\"\n- \"connectivity check failed retrieving ports for testing\"\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | No error |"}],"examples":[{"id":"get-response-ex0","language":"json","code":"{\n    \"node_uid\": 1,\n    \"result\": true\n}","section_id":"get-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/nodes/debuginfo","title":"Node debug info requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/nodes/debuginfo/","summary":"Documents the Redis Software REST API /nodes/debuginfo requests.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"92f00bf17a0a67dac849729c1e45ca7fc31d02c7cc2fbcd0bf82d21cffe80c57","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-debuginfo-all-nodes) | `/v1/nodes/debuginfo` | Get debug info from all nodes |\n| [GET](#get-debuginfo-node) | `/v1/nodes/{node_uid}/debuginfo` | Get debug info from a specific node |"},{"id":"get-debuginfo-all-nodes","title":"Get debug info from all nodes","role":"content","text":"GET /v1/nodes/debuginfo\n\nDownloads a tar file that contains debug info from all nodes.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_debugging_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_debugging_info) |"},{"id":"get-all-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/nodes/debuginfo"},{"id":"get-all-response","title":"Response","role":"returns","text":"Downloads the debug info in a tar file called `filename.tar.gz`. Extract the files from the tar file to access the debug info.\n\n#### Response headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Content-Type | application/x-gzip | Media type of request/response body |\n| Content-Length | 653350 | Length of the response body in octets |\n| Content-Disposition | attachment; filename=debuginfo.tar.gz | Display response in browser or download as attachment |"},{"id":"get-all-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success. |\n| [500 Internal Server Error](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1) | Failed to get debug info. |"},{"id":"get-debuginfo-node","title":"Get node debug info","role":"content","text":"GET /v1/nodes/{int: node_uid}/debuginfo\n\nDownloads a tar file that contains debug info from a specific node.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_debugging_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_debugging_info) |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/nodes/1/debuginfo"},{"id":"get-response","title":"Response","role":"returns","text":"Downloads the debug info in a tar file called `filename.tar.gz`. Extract the files from the tar file to access the debug info.\n\n#### Response headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Content-Type | application/x-gzip | Media type of request/response body |\n| Content-Length | 653350 | Length of the response body in octets |\n| Content-Disposition | attachment; filename=debuginfo.tar.gz | Display response in browser or download as attachment |"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success. |\n| [500 Internal Server Error](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1) | Failed to get debug info. |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/nodes","title":"Nodes requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/nodes/","summary":"Node requests","tags":["docs","operate","rs"],"last_updated":"2026-10-05T16:11:44+01:00","children":[{"id":"operate/rs/references/rest-api/requests/nodes/actions","summary":"Node action requests","title":"Node actions requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/nodes/actions/"},{"id":"operate/rs/references/rest-api/requests/nodes/alerts","summary":"Node alert requests","title":"Node alerts requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/nodes/alerts/"},{"id":"operate/rs/references/rest-api/requests/nodes/check","summary":"Requests that run checks on a cluster node.","title":"Check node requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/nodes/check/"},{"id":"operate/rs/references/rest-api/requests/nodes/debuginfo","summary":"Documents the Redis Software REST API /nodes/debuginfo requests.","title":"Node debug info requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/nodes/debuginfo/"},{"id":"operate/rs/references/rest-api/requests/nodes/snapshots","summary":"Node snapshot requests","title":"Node snapshot requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/nodes/snapshots/"},{"id":"operate/rs/references/rest-api/requests/nodes/stats","summary":"Node statistics requests","title":"Node stats requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/nodes/stats/"},{"id":"operate/rs/references/rest-api/requests/nodes/status","summary":"Requests that return a node's hostname and role.","title":"Node status requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/nodes/status/"}],"page_type":"content","content_hash":"0077cce77b59c15ddc369771655426cb1eaee4898e97c5342b7948c0fae5666f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-all-nodes) | `/v1/nodes` | Get all cluster nodes |\n| [GET](#get-node) | `/v1/nodes/{uid}` | Get a single cluster node |\n| [PUT](#put-node) | `/v1/nodes/{uid}` | Update a node |"},{"id":"get-all-nodes","title":"Get all nodes","role":"content","text":"[code example]\n\nGet all cluster nodes."},{"id":"permissions","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [view_all_nodes_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_all_nodes_info) | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |"},{"id":"get-all-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |"},{"id":"get-all-response","title":"Response","role":"returns","text":"Returns a JSON array of [node objects](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/node).\n\n#### Example JSON body\n\n[code example]\n\n#### Status codes {#get-all-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |"},{"id":"get-node","title":"Get node","role":"content","text":"[code example]\n\nGet a single cluster node."},{"id":"permissions-1","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [view_node_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_node_info) | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the node requested. |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns a [node object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/node).\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Node UID does not exist |"},{"id":"put-node","title":"Update node","role":"content","text":"[code example]\n\nUpdate a [node object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/node).\n\nCurrently, you can edit the following attributes:\n\n| Field | Type/Value | Description |\n|-------|------------|-------------|\n| accept_servers | boolean (default:&nbsp;true) | The node only accepts new shards if `accept_servers` is `true` |\n| addr | string | Internal IP address of node |\n| external_addr | complex object | External IP addresses of node. `GET /v1/jsonschema` to retrieve the object's structure. |\n| max_listeners | integer | Maximum number of listeners on the node |\n| max_redis_forks | integer (default: -1) | Maximum number of background processes forked from shards that can exist on the node at any given time. Set to 0 for unlimited. Set to -1 to use cluster settings. |\n| max_redis_servers | integer | Maximum number of shards on the node |\n| max_slave_full_syncs | integer (default: -1) | Maximum number of simultaneous replica full syncs that can run at any given time. Set to 0 for unlimited. Set to -1 to use cluster settings. |\n| rack_id | string | Rack ID where node is installed |\n| recovery_path | string | Recovery files path |\n| second_rack_id | string | Second rack ID where node is installed |\n\n> [!NOTE]\n> You can only update the `addr` attribute for offline nodes. Otherwise, the request returns an error."},{"id":"permissions-2","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [update_node](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#update_node) | admin |"},{"id":"put-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Example JSON body\n\n[code example]\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n| Content-Type | application/json | Media type of request/response body |\n\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the updated node. |\n\n\n#### Body\n\n| Field | Type | Description |\n|-------|------|-------------|\n| addr | string | Internal IP address of node |\n| external_addr | JSON array | External IP addresses of the node |\n| recovery_path | string | Path for recovery files |\n| accept_servers | boolean | If true, no shards will be created on the node |"},{"id":"put-response","title":"Response","role":"returns","text":"If the request is successful, the body will be empty. Otherwise, it may contain a JSON object with an error code and error message.\n\n#### Status codes {#put-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error, the request has been processed. |\n| [406 Not Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | Update request cannot be processed. |\n| [400 Bad Request](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1) | Bad content provided. |\n\n#### Error codes {#put-error-codes}\n\n| Code | Description |\n|------|-------------|\n| node_not_found | Node does not exist |\n| node_not_offline | Attempted to change node address while it is online |\n| node_already_populated | The node contains shards or endpoints, cannot disable accept_servers |\n| invalid_oss_cluster_port_mapping | Cannot enable \"accept_servers\" since there are databases with \"oss_cluster_port_mapping\" that do not have a port configuration for the current node |\n| node_already_has_rack_id | Attempted to change node's rack_id when it already has one |"}],"examples":[{"id":"get-all-nodes-ex0","language":"sh","code":"GET /v1/nodes","section_id":"get-all-nodes"},{"id":"get-all-request-ex0","language":"sh","code":"GET /v1/nodes","section_id":"get-all-request"},{"id":"get-all-response-ex0","language":"json","code":"[\n    {\n      \"uid\": 1,\n      \"status\": \"active\",\n      \"uptime\": 262735,\n      \"total_memory\": 6260334592,\n      \"software_version\": \"0.90.0-1\",\n      \"ephemeral_storage_size\": 20639797248,\n      \"persistent_storage_path\": \"/var/opt/redislabs/persist\",\n      \"persistent_storage_size\": 20639797248,\n      \"os_version\": \"Ubuntu 14.04.2 LTS\",\n      \"ephemeral_storage_path\": \"/var/opt/redislabs/tmp\",\n      \"architecture\": \"x86_64\",\n      \"shard_count\": 23,\n      \"public_addr\": \"\",\n      \"cores\": 4,\n      \"rack_id\": \"\",\n      \"supported_database_versions\": [\n        {\n          \"db_type\": \"memcached\",\n          \"version\": \"1.4.17\"\n        },\n        {\n          \"db_type\": \"redis\",\n          \"version\": \"2.6.16\"\n        },\n        {\n          \"db_type\": \"redis\",\n          \"version\": \"2.8.19\"\n        }\n      ],\n      \"shard_list\": [1, 3, 4],\n      \"addr\": \"10.0.3.61\"\n    },\n    {\n      \"uid\": 1,\n      \"status\": \"active\",\n      \"// additional fields...\"\n    }\n]","section_id":"get-all-response"},{"id":"get-node-ex0","language":"sh","code":"GET /v1/nodes/{int: uid}","section_id":"get-node"},{"id":"get-request-ex0","language":"sh","code":"GET /v1/nodes/1","section_id":"get-request"},{"id":"get-response-ex0","language":"json","code":"{\n    \"uid\": 1,\n    \"name\": \"node:1\",\n    \"// additional fields...\"\n}","section_id":"get-response"},{"id":"put-node-ex0","language":"sh","code":"PUT /v1/nodes/{int: uid}","section_id":"put-node"},{"id":"put-request-ex0","language":"sh","code":"PUT /v1/nodes/1","section_id":"put-request"},{"id":"put-request-ex1","language":"json","code":"{\n    \"addr\": \"10.0.0.1\",\n    \"external_addr\" : [\n        \"192.0.2.24\"\n    ]\n}","section_id":"put-request"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/nodes/snapshots","title":"Node snapshot requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/nodes/snapshots/","summary":"Node snapshot requests","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","page_type":"content","content_hash":"4206b727aceaaf9993c6aaad98c898c09573cb37e877c1d6880c11ad02ec08b1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-snapshots) | `/v1/nodes/{uid}/snapshots` | Get node snapshots |\n| [DELETE](#delete-snapshot) | `/v1/nodes/{uid}/snapshots/{snapshot_name}` | Delete a node snapshot |"},{"id":"get-snapshots","title":"Get node snapshots","role":"content","text":"[code example]\n\nGet all cluster node snapshots of the specified node."},{"id":"permissions","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [view_node_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_node_info) | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the node requested. |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns an array of node snapshot JSON objects.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Node UID does not exist |"},{"id":"delete-snapshot","title":"Delete node snapshot","role":"content","text":"[code example]\n\nDelete a cluster node snapshot. Snapshots created by maintenance mode are not deleted."},{"id":"permissions-1","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [update_node](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#update_node) | admin |"},{"id":"delete-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the updated node. |\n| snapshot_name | string | The unique name of the snapshot to delete. |"},{"id":"delete-response","title":"Response","role":"returns","text":"Returns a JSON object that represents the deleted node snapshot.\n\n#### Example JSON body\n\n[code example]\n\n#### Status codes {#delete-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |\n| [403 Forbidden](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.4) | Node snapshot is a maintenance snapshot and cannot be deleted |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Node uid does not exist |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Snapshot name does not exist for this node uid |"}],"examples":[{"id":"get-snapshots-ex0","language":"sh","code":"GET /v1/nodes/{int: uid}/snapshots","section_id":"get-snapshots"},{"id":"get-request-ex0","language":"sh","code":"GET /v1/nodes/1/snapshots","section_id":"get-request"},{"id":"get-response-ex0","language":"json","code":"[\n    {\n        \"created_time\": \"2024-01-10 20:55:54\",\n        \"name\": \"nightly_snapshot_1\",\n        \"node_uid\": \"1\"\n    },\n    {\n        \"created_time\": \"2024-01-11 20:55:54\",\n        \"name\": \"nightly_snapshot_2\",\n        \"node_uid\": \"1\"\n    }\n]","section_id":"get-response"},{"id":"delete-snapshot-ex0","language":"sh","code":"DELETE /v1/nodes/{int: uid}/snapshots/{snapshot_name}","section_id":"delete-snapshot"},{"id":"delete-request-ex0","language":"sh","code":"DELETE /v1/nodes/1/snapshots/nightly_snapshot_19","section_id":"delete-request"},{"id":"delete-response-ex0","language":"json","code":"{\n    \"created_time\": \"2024-01-11 20:55:54\",\n    \"name\": \"nightly_snapshot_19\",\n    \"node_uid\": \"1\"\n}","section_id":"delete-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/nodes/stats","title":"Node stats requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/nodes/stats/","summary":"Node statistics requests","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","children":[{"id":"operate/rs/references/rest-api/requests/nodes/stats/last","summary":"Most recent node statistics requests","title":"Latest node stats requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/nodes/stats/last/"}],"page_type":"content","content_hash":"04914809bbf00082b97a6f6276f71c398abdee137c85a475283f03946d675722","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The v1 statistics REST API endpoints are deprecated as of Redis Software version 7.22. Use the [metrics stream engine for monitoring v2](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine) instead for real-time monitoring and metrics collection.\n\n\n| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-all-nodes-stats) | `/v1/nodes/stats` | Get stats for all nodes |\n| [GET](#get-node-stats) | `/v1/nodes/stats/{uid}` | Get stats for a single node |"},{"id":"get-all-nodes-stats","title":"Get all nodes stats","role":"content","text":"[code example]\n\nGet statistics for all nodes.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_all_nodes_stats](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_all_nodes_stats) |"},{"id":"get-all-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| interval | string | Time interval for which we want stats: 1sec/10sec/5min/15min/1hour/12hour/1week (optional)  |\n| stime | ISO_8601 | Start time from which we want the stats. Should comply with the [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601) format (optional) |\n| etime | ISO_8601 | End time after which we don't want the stats. Should comply with the [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601) format (optional) |"},{"id":"get-all-response","title":"Response","role":"returns","text":"Returns a JSON array of [statistics](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/statistics) for all nodes.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-all-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | No nodes exist |"},{"id":"get-node-stats","title":"Get node stats","role":"content","text":"[code example]\n\nGet statistics for a node.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_node_stats](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_node_stats) |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the node requested. |\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| interval | string | Time interval for which we want stats: 1sec/10sec/5min/15min/1hour/12hour/1week (optional) |\n| stime | ISO_8601 | Start time from which we want the stats. Should comply with the [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601) format (optional) |\n| etime | ISO_8601 | End time after which we don't want the stats. Should comply with the [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601) format (optional) |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns [statistics](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/statistics) for the specified node.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Node does not exist |\n| [406 Not Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | Node isn't currently active |\n| [503 Service Unavailable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.4) | Node is in recovery state |"}],"examples":[{"id":"get-all-nodes-stats-ex0","language":"sh","code":"GET /v1/nodes/stats","section_id":"get-all-nodes-stats"},{"id":"get-all-request-ex0","language":"sh","code":"GET /v1/nodes/stats?interval=1hour&stime=2014-08-28T10:00:00Z","section_id":"get-all-request"},{"id":"get-all-response-ex0","language":"json","code":"[\n  {\n    \"uid\": \"1\",\n    \"intervals\": [\n    {\n      \"interval\": \"1sec\",\n      \"stime\": \"2015-05-28T08:40:11Z\",\n      \"etime\": \"2015-05-28T08:40:12Z\",\n      \"conns\": 0.0,\n      \"cpu_idle\": 0.5499999999883585,\n      \"cpu_system\": 0.03499999999985448,\n      \"cpu_user\": 0.38000000000101863,\n      \"egress_bytes\": 0.0,\n      \"ephemeral_storage_avail\": 2929315840.0,\n      \"ephemeral_storage_free\": 3977830400.0,\n      \"free_memory\": 893485056.0,\n      \"ingress_bytes\": 0.0,\n      \"persistent_storage_avail\": 2929315840.0,\n      \"persistent_storage_free\": 3977830400.0,\n      \"total_req\": 0.0\n    },\n    {\n      \"interval\": \"1sec\",\n      \"stime\": \"2015-05-28T08:40:12Z\",\n      \"etime\": \"2015-05-28T08:40:13Z\",\n      \"cpu_idle\": 1.2,\n      \"// additional fields...\"\n    }\n   ]\n  },\n  {\n    \"uid\": \"2\",\n    \"intervals\": [\n    {\n      \"interval\": \"1sec\",\n      \"stime\": \"2015-05-28T08:40:11Z\",\n      \"etime\": \"2015-05-28T08:40:12Z\",\n      \"conns\": 0.0,\n      \"cpu_idle\": 0.5499999999883585,\n      \"cpu_system\": 0.03499999999985448,\n      \"cpu_user\": 0.38000000000101863,\n      \"egress_bytes\": 0.0,\n      \"ephemeral_storage_avail\": 2929315840.0,\n      \"ephemeral_storage_free\": 3977830400.0,\n      \"free_memory\": 893485056.0,\n      \"ingress_bytes\": 0.0,\n      \"persistent_storage_avail\": 2929315840.0,\n      \"persistent_storage_free\": 3977830400.0,\n      \"total_req\": 0.0\n    },\n    {\n      \"interval\": \"1sec\",\n      \"stime\": \"2015-05-28T08:40:12Z\",\n      \"etime\": \"2015-05-28T08:40:13Z\",\n      \"cpu_idle\": 1.2,\n      \"// additional fields...\"\n    }\n   ]\n  }\n]","section_id":"get-all-response"},{"id":"get-node-stats-ex0","language":"sh","code":"GET /v1/nodes/stats/{int: uid}","section_id":"get-node-stats"},{"id":"get-request-ex0","language":"sh","code":"GET /v1/nodes/stats/1?interval=1hour&stime=2014-08-28T10:00:00Z","section_id":"get-request"},{"id":"get-response-ex0","language":"json","code":"{\n  \"uid\": \"1\",\n  \"intervals\": [\n    {\n      \"interval\": \"1sec\",\n      \"stime\": \"2015-05-28T08:40:11Z\",\n      \"etime\": \"2015-05-28T08:40:12Z\",\n      \"conns\": 0.0,\n      \"cpu_idle\": 0.5499999999883585,\n      \"cpu_system\": 0.03499999999985448,\n      \"cpu_user\": 0.38000000000101863,\n      \"egress_bytes\": 0.0,\n      \"ephemeral_storage_avail\": 2929315840.0,\n      \"ephemeral_storage_free\": 3977830400.0,\n      \"free_memory\": 893485056.0,\n      \"ingress_bytes\": 0.0,\n      \"persistent_storage_avail\": 2929315840.0,\n      \"persistent_storage_free\": 3977830400.0,\n      \"total_req\": 0.0\n    },\n    {\n      \"interval\": \"1sec\",\n      \"stime\": \"2015-05-28T08:40:12Z\",\n      \"etime\": \"2015-05-28T08:40:13Z\",\n      \"cpu_idle\": 1.2,\n      \"// additional fields...\"\n    }\n  ]\n}","section_id":"get-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/nodes/stats/last","title":"Latest node stats requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/nodes/stats/last/","summary":"Most recent node statistics requests","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","page_type":"content","content_hash":"700fb83619ea1492f14f8b09beccba2333e04a1b0396025695ae505bac9b9f9d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-all-nodes-stats-last) | `/v1/nodes/stats/last` | Get latest stats for all nodes |\n| [GET](#get-node-stats-last) | `/v1/nodes/stats/last/{uid}` | Get latest stats for a single node |"},{"id":"get-all-nodes-stats-last","title":"Get latest stats for all nodes","role":"content","text":"[code example]\n\nGet latest statistics for all nodes.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_all_nodes_stats](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_all_nodes_stats) |"},{"id":"get-all-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| interval | string | Time interval for which we want stats: 1sec/10sec/5min/15min/1hour/12hour/1week. Default: 1sec. (optional) |\n| stime | ISO_8601 | Start time from which we want the stats. Should comply with the [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601) format (optional) |\n| etime | ISO_8601 | End time after which we don't want the stats. Should comply with the [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601) format (optional) |"},{"id":"get-all-response","title":"Response","role":"returns","text":"Returns most recent [statistics](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/statistics) for all nodes.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-all-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | No nodes exist |"},{"id":"get-node-stats-last","title":"Get latest node stats","role":"content","text":"[code example]\n\nGet the latest statistics of a node.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_node_stats](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_node_stats) |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the node requested. |\n\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| interval | string | Time interval for which we want stats: 1sec/10sec/5min/15min/1hour/12hour/1week. Default: 1sec. (optional) |\n| stime | ISO_8601 | Start time from which we want the stats. Should comply with the [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601)format (optional) |\n| etime | ISO_8601 | End time after which we don't want the stats. Should comply with the [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601)format (optional) |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns the most recent [statistics](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/statistics) for the specified node.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-error-codes","title":"Error codes","role":"errors","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Node does not exist |\n| [406 Not Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | Node isn't currently active |\n| [503 Service Unavailable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.4) | Mode is in recovery state |"}],"examples":[{"id":"get-all-nodes-stats-last-ex0","language":"sh","code":"GET /v1/nodes/stats/last","section_id":"get-all-nodes-stats-last"},{"id":"get-all-request-ex0","language":"sh","code":"GET /v1/nodes/stats/last?interval=1sec&stime=2015-10-14T06:29:43Z","section_id":"get-all-request"},{"id":"get-all-response-ex0","language":"json","code":"{\n    \"1\": {\n         \"conns\": 0.0,\n         \"cpu_idle\": 0.922500000015134,\n         \"cpu_system\": 0.007499999999708962,\n         \"cpu_user\": 0.01749999999810825,\n         \"cur_aof_rewrites\": 0.0,\n         \"egress_bytes\": 7887.0,\n         \"ephemeral_storage_avail\": 75821363200.0,\n         \"ephemeral_storage_free\": 81189969920.0,\n         \"etime\": \"2015-10-14T06:29:44Z\",\n         \"free_memory\": 2956963840.0,\n         \"ingress_bytes\": 4950.0,\n         \"interval\": \"1sec\",\n         \"persistent_storage_avail\": 75821363200.0,\n         \"persistent_storage_free\": 81189969920.0,\n         \"stime\": \"2015-10-14T06:29:43Z\",\n         \"total_req\": 0.0\n     },\n     \"2\": {\n         \"conns\": 0.0,\n         \"cpu_idle\": 0.922500000015134,\n         \"// additional fields...\"\n     }\n}","section_id":"get-all-response"},{"id":"get-node-stats-last-ex0","language":"sh","code":"GET /v1/nodes/stats/last/{int: uid}","section_id":"get-node-stats-last"},{"id":"get-request-ex0","language":"sh","code":"GET /v1/nodes/stats/last/1?interval=1sec&stime=2015-10-13T09:01:54Z","section_id":"get-request"},{"id":"get-response-ex0","language":"json","code":"{\n    \"1\": {\n         \"conns\": 0.0,\n         \"cpu_idle\": 0.8049999999930151,\n         \"cpu_system\": 0.02750000000014552,\n         \"cpu_user\": 0.12000000000080036,\n         \"cur_aof_rewrites\": 0.0,\n         \"egress_bytes\": 2169.0,\n         \"ephemeral_storage_avail\": 75920293888.0,\n         \"ephemeral_storage_free\": 81288900608.0,\n         \"etime\": \"2015-10-13T09:01:55Z\",\n         \"free_memory\": 3285381120.0,\n         \"ingress_bytes\": 3020.0,\n         \"interval\": \"1sec\",\n         \"persistent_storage_avail\": 75920293888.0,\n         \"persistent_storage_free\": 81288900608.0,\n         \"stime\": \"2015-10-13T09:01:54Z\",\n         \"total_req\": 0.0\n    }\n}","section_id":"get-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/nodes/status","title":"Node status requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/nodes/status/","summary":"Requests that return a node's hostname and role.","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","page_type":"content","content_hash":"0cd3e08869d9f5df5ac53c927262f8ef2187e4d0ee50981e5ba8e7266b748862","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-all-nodes-status) | `/v1/nodes/status` | Get the status of all nodes |\n| [GET](#get-node-status) | `/v1/nodes/{uid}/status` | Get a node's status |"},{"id":"get-all-nodes-status","title":"Get all node statuses","role":"content","text":"GET /v1/nodes/status\n\nGets the status of all nodes. Includes each node's hostname and role in the cluster:\n\n- Primary nodes return `\"role\": \"master\"`\n\n- Replica nodes return `\"role\": \"slave\"`\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_node_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_node_info) |"},{"id":"get-all-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/nodes/status\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |"},{"id":"get-all-response","title":"Response","role":"returns","text":"For each node in the cluster, returns a JSON object that contains each node's hostname, role, and other status details.\n\nIf a maintenance snapshot exists due to an in-progress or improperly stopped [node maintenance](https://redis.io/docs/latest/operate/rs/clusters/maintenance-mode) process, the response includes a `maintenance_snapshot` field.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-all-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |"},{"id":"get-node-status","title":"Get node status","role":"content","text":"GET /v1/nodes/{int: uid}/status\n\nGets the status of a node. Includes the node's hostname and role in the cluster:\n\n- Primary nodes return `\"role\": \"master\"`\n\n- Replica nodes return `\"role\": \"slave\"`\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_node_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_node_info) |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/nodes/1/status\n\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The node's unique ID. |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns a JSON object that contains the node's hostname, role, and other status details.\n\nIf a maintenance snapshot exists due to an in-progress or improperly stopped [node maintenance](https://redis.io/docs/latest/operate/rs/clusters/maintenance-mode) process, the response includes a `maintenance_snapshot` field.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Node UID does not exist |"}],"examples":[{"id":"get-all-response-ex0","language":"json","code":"{\n    \"1\": {\n        \"cores\": 8,\n        \"free_provisional_ram\": 0,\n        \"free_ram\": 3499368448,\n        \"hostname\": \"3d99db1fdf4b\",\n        \"maintenance_snapshot\": {\n            \"created_time\": \"2024-09-06 20:47:23\",\n            \"name\": \"maintenance_mode_2024-09-06_20-47-23\",\n            \"node_uid\": \"1\"\n        },\n        \"master_shards\": [],\n        \"node_overbooking_depth\": 0,\n        \"node_status\": \"active\",\n        \"role\": \"master\",\n        \"slave_shards\": [],\n        \"software_version\": \"7.4.6-22\",\n        \"software_version_sha\": \"6c37b1483b5fb6110c8055c1526aa58eec1d29d3519e92310859101419248831\",\n        \"total_memory\": 6219673600,\n        \"total_provisional_ram\": 0\n    },\n    \"2\": {\n        \"hostname\": \"fc7a3d332458\",\n        \"role\": \"slave\",\n        // additional fields\n    },\n    \"3\": {\n        \"hostname\": \"b87cc06c830f\",\n        \"role\": \"slave\",\n        // additional fields\n    }\n}","section_id":"get-all-response"},{"id":"get-response-ex0","language":"json","code":"{\n    \"cores\": 8,\n    \"free_provisional_ram\": 0,\n    \"free_ram\": 3504422912,\n    \"hostname\": \"3d99db1fdf4b\",\n    \"maintenance_snapshot\": {\n        \"created_time\": \"2024-09-06 20:47:23\",\n        \"name\": \"maintenance_mode_2024-09-06_20-47-23\",\n        \"node_uid\": \"1\"\n    },\n    \"master_shards\": [],\n    \"node_overbooking_depth\": 0,\n    \"node_status\": \"active\",\n    \"role\": \"master\",\n    \"slave_shards\": [],\n    \"software_version\": \"7.4.6-22\",\n    \"software_version_sha\": \"6c37b1483b5fb6110c8055c1526aa58eec1d29d3519e92310859101419248831\",\n    \"total_memory\": 6219673600,\n    \"total_provisional_ram\": 0\n}","section_id":"get-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/ocsp","title":"OCSP requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/ocsp/","summary":"OCSP requests","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","children":[{"id":"operate/rs/references/rest-api/requests/ocsp/status","summary":"OCSP status requests","title":"OCSP status requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/ocsp/status/"},{"id":"operate/rs/references/rest-api/requests/ocsp/test","summary":"OCSP test requests","title":"OCSP test requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/ocsp/test/"}],"page_type":"content","content_hash":"2e2d82a54337c3ef4a0903ee3f80730c50552f701b14c3e34623fa5b6e15f6d0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-ocsp) | `/v1/ocsp` | Get OCSP configuration |\n| [PUT](#put-ocsp) | `/v1/ocsp` | Update OCSP configuration |"},{"id":"get-ocsp","title":"Get OCSP configuration","role":"content","text":"GET /v1/ocsp\n\nGets the cluster's OCSP configuration.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_ocsp_config](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_ocsp_config) |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/ocsp \n\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns an [OCSP configuration object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/ocsp).\n\n#### Example JSON body\n\n[code example]"},{"id":"get-error-codes","title":"Error codes","role":"errors","text":"When errors occur, the server returns a JSON object with `error_code` and `message` fields that provide additional information. The following are possible `error_code` values:\n\n| Code | Description |\n|------|-------------|\n| ocsp_unsupported_by_capability | Not all nodes support OCSP capability |"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success |\n| [406 Not Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | Feature not supported in all nodes |"},{"id":"put-ocsp","title":"Update OCSP configuration","role":"content","text":"PUT /v1/ocsp\n\nUpdates the cluster's OCSP configuration.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [config_ocsp](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#config_ocsp) |"},{"id":"put-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tPUT /v1/ocsp \n\n#### Example JSON body\n\n[code example]\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### Request body\n\nInclude an [OCSP configuration object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/ocsp) with updated fields in the request body."},{"id":"put-response","title":"Response","role":"returns","text":"Returns the updated [OCSP configuration object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/ocsp)."},{"id":"put-error-codes","title":"Error codes","role":"errors","text":"When errors occur, the server returns a JSON object with `error_code` and `message` fields that provide additional information. The following are possible `error_code` values:\n\n| Code | Description |\n|------|-------------|\n| invalid_schema | An illegal parameter or a parameter with an illegal value |\n| no_responder_url | Tried to enable OCSP with no responder URL configured |\n| ocsp_unsupported_by_capability | Not all nodes support OCSP capability |"},{"id":"put-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success, OCSP config has been set |\n| [400 Bad Request](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1) | Bad or missing configuration parameters |\n| [406 Not Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | Feature not supported in all nodes |"}],"examples":[{"id":"get-response-ex0","language":"json","code":"{\n    \"ocsp_functionality\": true,\n    \"responder_url\": \"http://responder.ocsp.url.com\",\n    \"query_frequency\": 3800,\n    \"response_timeout\": 2,\n    \"recovery_frequency\": 80,\n    \"recovery_max_tries\": 20\n}","section_id":"get-response"},{"id":"put-request-ex0","language":"json","code":"{\n    \"ocsp_functionality\": true,\n    \"query_frequency\": 3800,\n    \"response_timeout\": 2,\n    \"recovery_frequency\": 80,\n    \"recovery_max_tries\": 20\n}","section_id":"put-request"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/ocsp/status","title":"OCSP status requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/ocsp/status/","summary":"OCSP status requests","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","page_type":"content","content_hash":"62fe1570d5505143f9b07c37c1a64085a6ea02b965d2f28a417b87d259c8f15d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-status) | `/v1/ocsp/status` | Get OCSP status |"},{"id":"get-status","title":"Get OCSP status","role":"content","text":"GET /v1/ocsp/status\n\nGets the latest cached status of the proxy certificate’s OCSP response.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_ocsp_status](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_ocsp_status) |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/ocsp/status \n\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns an [OCSP status object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/ocsp_status).\n\n#### Example JSON body\n\n[code example]"},{"id":"get-error-codes","title":"Error codes","role":"errors","text":"When errors occur, the server returns a JSON object with `error_code` and `message` fields that provide additional information. The following are possible `error_code` values:\n\n| Code | Description |\n|------|-------------|\n| ocsp_unsupported_by_capability | Not all nodes support OCSP capability | \n| invalid_ocsp_response | The server returned a response that is not OCSP-compatible |"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success |\n| [406 Not Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | Feature not supported in all nodes |"}],"examples":[{"id":"get-response-ex0","language":"json","code":"{\n    \"responder_url\": \"http://responder.ocsp.url.com\",\n    \"cert_status\": \"REVOKED\",\n    \"produced_at\": \"Wed, 22 Dec 2021 12:50:11 GMT\",\n    \"this_update\": \"Wed, 22 Dec 2021 12:50:11 GMT\",\n    \"next_update\": \"Wed, 22 Dec 2021 14:50:00 GMT\",\n    \"revocation_time\": \"Wed, 22 Dec 2021 12:50:04 GMT\"\n}","section_id":"get-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/ocsp/test","title":"OCSP test requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/ocsp/test/","summary":"OCSP test requests","tags":["docs","operate","rs"],"last_updated":"2025-09-03T12:06:34-05:00","page_type":"content","content_hash":"019a632467e986cf009d835cadefae07701914c4406f20770d7f95d8a224d7b0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [POST](#post-test) | `/v1/ocsp/test` | Test OCSP |"},{"id":"post-test","title":"Test OCSP","role":"content","text":"POST /v1/ocsp/test\n\nQueries the OCSP server for the proxy certificate’s latest status and returns the response as JSON. It caches the response if the OCSP feature is enabled.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [test_ocsp_status](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#test_ocsp_status) |"},{"id":"post-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tPOST /v1/ocsp/test \n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |"},{"id":"post-response","title":"Response","role":"returns","text":"Returns an [OCSP status object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/ocsp_status).\n\n#### Example JSON body\n\n[code example]"},{"id":"post-error-codes","title":"Error codes","role":"errors","text":"When errors occur, the server returns a JSON object with `error_code` and `message` fields that provide additional information. The following are possible `error_code` values:\n\n| Code | Description |\n|------|-------------|\n| no_responder_url | Tried to test OCSP status with no responder URL configured |\n| ocsp_unsupported_by_capability | Not all nodes support OCSP capability |\n| task_queued_for_too_long | OCSP polling task was in status \"queued\" for over 5 seconds |\n| invalid_ocsp_response | The server returned a response that is not compatible with OCSP |"},{"id":"post-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success querying the OCSP server |\n| [406 Not Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | Feature is not supported in all nodes |\n| [500 Internal Server Error](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1) | `responder_url` is not configured or polling task failed |"}],"examples":[{"id":"post-response-ex0","language":"json","code":"{\n    \"responder_url\": \"http://responder.ocsp.url.com\",\n    \"cert_status\": \"REVOKED\",\n    \"produced_at\": \"Wed, 22 Dec 2021 12:50:11 GMT\",\n    \"this_update\": \"Wed, 22 Dec 2021 12:50:11 GMT\",\n    \"next_update\": \"Wed, 22 Dec 2021 14:50:00 GMT\",\n    \"revocation_time\": \"Wed, 22 Dec 2021 12:50:04 GMT\"\n}","section_id":"post-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/proxies","title":"Proxy requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/proxies/","summary":"Proxy requests","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","children":[],"page_type":"content","content_hash":"5e5375249c6e35f2a73c1340a0050be2dbef4bdd932d33a7f67fcce9f1cb8e06","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-all-proxies) | `/v1/proxies` | Get all proxies |\n| [GET](#get-proxy) | `/v1/proxies/{uid}` | Get a proxy |\n| [PUT](#put-proxy) | `/v1/proxies/{uid}` | Update a proxy |\n| [PUT](#put-all-proxies) | `/v1/proxies` | Update all proxies |"},{"id":"get-all-proxies","title":"Get all proxies","role":"content","text":"[code example]\n\nGet all the proxies in the cluster."},{"id":"permissions","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [view_all_proxies_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_all_proxies_info) | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |"},{"id":"get-all-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |"},{"id":"get-all-response","title":"Response","role":"returns","text":"Returns a JSON array of [proxy objects](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/proxy).\n\n#### Example JSON body\n\n[code example]"},{"id":"get-all-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |"},{"id":"get-proxy","title":"Get proxy","role":"content","text":"[code example]\n\nGet a single proxy's info."},{"id":"permissions-1","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [view_proxy_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_proxy_info) | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The proxy's unique node ID |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns a [proxy object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/proxy).\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Proxy UID does not exist |"},{"id":"put-proxy","title":"Update proxy","role":"content","text":"[code example]\n\nUpdates a proxy object, notifies the proxy, and waits for acknowledgment (ACK) unless the node is dead.\n\nAutomatically restarts the proxy service if `allow_restart` is `true` and any updated parameters require a restart for the changes to take effect. For example, a restart is required if you change `threads` to a lower number.\n\nHowever, if `allow_restart` is `false`, such changes only take effect after the next proxy restart."},{"id":"permissions-2","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [update_proxy](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#update_proxy) | admin<br />user_manager |"},{"id":"put-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Example JSON body\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n| Content-Type | application/json | Request body media type |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the updated proxy. Corresponds to the node ID. |\n\n#### Request body\n\nInclude a JSON object in the request body. The JSON object can contain the boolean field `allow_restart` and a [proxy object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/proxy) with updated fields."},{"id":"put-response","title":"Response","role":"returns","text":"Returns a status code to indicate the success or failure of the proxy update."},{"id":"put-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error, the request has been processed |\n| [400 Bad Request](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1) | Bad content provided |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Proxy does not exist |\n| [500 Internal Server Error](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1) | Error while waiting for confirmation from proxy |\n| [504 Gateway Timeout](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.5) | Timeout while waiting for confirmation from proxy |"},{"id":"put-all-proxies","title":"Update all proxies","role":"content","text":"[code example]\n\nUpdates all the proxy objects, notifies the proxies, and waits for acknowledgment (ACK) unless the node is dead.\n\nAutomatically restarts the relevant proxy services if `allow_restart` is `true` and any updated parameters require a restart for the changes to take effect.\n\nHowever, if `allow_restart` is `false`, such changes only take effect after the next proxy restart."},{"id":"permissions-3","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [update_proxy](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#update_proxy) | admin<br />user_manager |"},{"id":"put-all-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Example JSON body\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n| Content-Type | application/json | Request body media type |\n\n#### Request body\n\nInclude a JSON object in the request body. The JSON object can contain the boolean field `allow_restart` and a [proxy object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/proxy) with updated fields."},{"id":"put-all-response","title":"Response","role":"returns","text":"Returns a status code to indicate the success or failure of the proxy updates."},{"id":"put-all-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error, the request has been processed |\n| [400 Bad Request](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1) | Bad content provided |\n| [500 Internal Server Error](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1) | Error while waiting for confirmation from proxy |\n| [504 Gateway Timeout](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.5) | Timeout while waiting for confirmation from proxy |"}],"examples":[{"id":"get-all-proxies-ex0","language":"sh","code":"GET /v1/proxies","section_id":"get-all-proxies"},{"id":"get-all-request-ex0","language":"sh","code":"GET /v1/proxies","section_id":"get-all-request"},{"id":"get-all-response-ex0","language":"json","code":"[\n    {\n         \"uid\": 1,\n         \"client_keepintvl\": 30,\n         \"max_worker_server_conns\": 16384,\n         \"client_keepcnt\": 6,\n         \"max_threads\": 64,\n         \"ignore_bdb_cconn_output_buff_limits\": false,\n         \"dynamic_threads_scaling\": false,\n         \"max_worker_client_conns\": 16384,\n         \"max_servers\": 4096,\n         \"client_keepidle\": 180,\n         \"duration_usage_threshold\": 30,\n         \"max_worker_txns\": 65536,\n         \"threads\": 3,\n         \"max_listeners\": 1024,\n         \"conns\": 500000,\n         \"ignore_bdb_cconn_limit\": false,\n         \"threads_usage_threshold\": 80,\n         \"backlog\": 1024\n    },\n    {\n        \"uid\": 2,\n        \"threads\": 3,\n        // additional fields...\n    }\n]","section_id":"get-all-response"},{"id":"get-proxy-ex0","language":"sh","code":"GET /v1/proxies/{int: uid}","section_id":"get-proxy"},{"id":"get-request-ex0","language":"sh","code":"GET /v1/proxies/1","section_id":"get-request"},{"id":"get-response-ex0","language":"json","code":"{\n     \"uid\": 1,\n     \"client_keepintvl\": 30,\n     \"max_worker_server_conns\": 16384,\n     \"client_keepcnt\": 6,\n     \"max_threads\": 64,\n     \"ignore_bdb_cconn_output_buff_limits\": false,\n     \"dynamic_threads_scaling\": false,\n     \"max_worker_client_conns\": 16384,\n     \"max_servers\": 4096,\n     \"client_keepidle\": 180,\n     \"duration_usage_threshold\": 30,\n     \"max_worker_txns\": 65536,\n     \"threads\": 3,\n     \"max_listeners\": 1024,\n     \"conns\": 500000,\n     \"ignore_bdb_cconn_limit\": false,\n     \"threads_usage_threshold\": 80,\n     \"backlog\": 1024\n}","section_id":"get-response"},{"id":"put-proxy-ex0","language":"sh","code":"PUT /v1/proxies/{int: uid}","section_id":"put-proxy"},{"id":"put-request-ex0","language":"sh","code":"PUT /v1/proxies/1","section_id":"put-request"},{"id":"put-request-ex1","language":"json","code":"{\n  \"allow_restart\": true,\n  \"proxy\": {\n    \"threads\": 8\n  }\n}","section_id":"put-request"},{"id":"put-all-proxies-ex0","language":"sh","code":"PUT /v1/proxies","section_id":"put-all-proxies"},{"id":"put-all-request-ex0","language":"sh","code":"PUT /v1/proxies","section_id":"put-all-request"},{"id":"put-all-request-ex1","language":"json","code":"{\n  \"allow_restart\": true,\n  \"proxy\": {\n    \"threads\": 8,\n    \"max_threads\": 12\n  }\n}","section_id":"put-all-request"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/redis_acls","title":"Redis access control list (ACL) requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/redis_acls/","summary":"Redis access control list (ACL) requests","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","children":[],"page_type":"content","content_hash":"ad277164a9fbf9243664b24fd45d6db3d612c3d42af18b4c300c586a85f7e4c9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-all-redis_acls) | `/v1/redis_acls` | Get all Redis ACLs |\n| [GET](#get-redis_acl) | `/v1/redis_acls/{uid}` | Get a single Redis ACL |\n| [PUT](#put-redis_acl) | `/v1/redis_acls/{uid}` | Update a Redis ACL |\n| [POST](#post-redis_acl) | `/v1/redis_acls` | Create a new Redis ACL |\n| [DELETE](#delete-redis_acl) | `/v1/redis_acls/{uid}` | Delete a Redis ACL |"},{"id":"get-all-redis_acls","title":"Get all Redis ACLs","role":"content","text":"[code example]\n\nGet all Redis ACL objects."},{"id":"permissions","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [view_all_redis_acls_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_all_redis_acls_info) | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |"},{"id":"get-all-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |"},{"id":"get-all-response","title":"Response","role":"returns","text":"Returns a JSON array of [Redis ACL objects](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/redis_acl).\n\n#### Example JSON body\n\n[code example]"},{"id":"get-all-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |\n| [501 Not Implemented](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.2) | Cluster doesn't support redis_acl yet. |"},{"id":"get-redis_acl","title":"Get Redis ACL","role":"content","text":"[code example]\n\nGet a single Redis ACL object."},{"id":"permissions-1","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [view_redis_acl_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_redis_acl_info) | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The object's unique ID. |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns a [Redis ACL object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/redis_acl).\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success. |\n| [403 Forbidden](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.4) | Operation is forbidden. |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | redis_acl does not exist. |\n| [501 Not Implemented](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.2) | Cluster doesn't support redis_acl yet. |"},{"id":"put-redis_acl","title":"Update Redis ACL","role":"content","text":"[code example]\n\nUpdate an existing Redis ACL object."},{"id":"permissions-2","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [update_redis_acl](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#update_redis_acl) | admin<br />user_manager |"},{"id":"put-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Example JSON body\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### Query parameters\n\n| Field   | Type | Description |\n|---------|------|---------------|\n| dry_run |  | Validate the updated [Redis ACL object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/redis_acl) but don't apply the update. |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The Redis ACL's unique ID. |\n\n#### Request body\n\nInclude a [Redis ACL object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/redis_acl) with updated fields in the request body."},{"id":"put-response","title":"Response","role":"returns","text":"Returns the updated [Redis ACL object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/redis_acl).\n\n#### Example JSON body\n\n[code example]"},{"id":"put-error-codes","title":"Error codes","role":"errors","text":"| Code | Description |\n|------|-------------|\n| unsupported_resource | The cluster is not yet able to handle this resource type. This could happen in a partially upgraded cluster, where some of the nodes are still on a previous version.|\n| name_already_exists | An object of the same type and name exists|\n| invalid_param | A parameter has an illegal value|"},{"id":"put-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success, redis_acl was updated. |\n| [400 Bad Request](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1) | Bad or missing configuration parameters. |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Attempting to change a non-existent redis_acl. |\n| [409 Conflict](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.10) | Cannot change a read-only object |\n| [501 Not Implemented](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.2) | Cluster doesn't support redis_acl yet. |"},{"id":"post-redis_acl","title":"Create Redis ACL","role":"content","text":"[code example]\n\nCreate a new Redis ACL object."},{"id":"permissions-3","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [create_redis_acl](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#create_redis_acl) | admin<br />user_manager |"},{"id":"post-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Example JSON body\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### Query parameters\n\n| Field   | Type | Description |\n|---------|------|---------------|\n| dry_run |  | Validate the new [Redis ACL object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/redis_acl) but don't apply the update. |\n\n#### Request body\n\nInclude a [Redis ACL object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/redis_acl) in the request body."},{"id":"post-response","title":"Response","role":"returns","text":"Returns the newly created [Redis ACL object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/redis_acl).\n\n#### Example JSON body\n\n[code example]"},{"id":"post-error-codes","title":"Error codes","role":"errors","text":"Possible `error_code` values:\n\n| Code | Description |\n|------|-------------|\n| unsupported_resource | The cluster is not yet able to handle this resource type. This could happen in a partially upgraded cluster, where some of the nodes are still on a previous version. |\n| name_already_exists | An object of the same type and name exists |\n| missing_field | A needed field is missing |\n| invalid_param | A parameter has an illegal value |"},{"id":"post-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success, redis_acl is created. |\n| [400 Bad Request](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1) | Bad or missing configuration parameters. |\n| [501 Not Implemented](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.2) | Cluster doesn't support redis_acl yet. |"},{"id":"examples","title":"Examples","role":"example","text":"#### cURL\n\n[code example]\n\n#### Python\n\n[code example]"},{"id":"delete-redis_acl","title":"Delete Redis ACL","role":"content","text":"[code example]\n\nDelete a Redis ACL object."},{"id":"permissions-4","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [delete_redis_acl](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#delete_redis_acl) | admin<br />user_manager |"},{"id":"delete-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The redis_acl unique ID. |"},{"id":"delete-response","title":"Response","role":"returns","text":"Returns a status code that indicates the Redis ACL deletion success or failure."},{"id":"delete-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success, the redis_acl is deleted. |\n| [406 Not Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | The request is not acceptable. |\n| [409 Conflict](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.10) | Cannot delete a read-only object |\n| [501 Not Implemented](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.2) | Cluster doesn't support redis_acl yet. |"}],"examples":[{"id":"get-all-redis_acls-ex0","language":"sh","code":"GET /v1/redis_acls","section_id":"get-all-redis_acls"},{"id":"get-all-request-ex0","language":"sh","code":"GET /v1/redis_acls","section_id":"get-all-request"},{"id":"get-all-response-ex0","language":"json","code":"[\n    {\n     \"uid\": 1,\n     \"name\": \"Full Access\",\n     \"acl\": \"+@all ~*\"\n    },\n    {\n     \"uid\": 2,\n     \"name\": \"Read Only\",\n     \"acl\": \"+@read ~*\"\n    },\n    {\n     \"uid\": 3,\n     \"name\": \"Not Dangerous\",\n     \"acl\": \"+@all -@dangerous ~*\"\n    },\n    {\n     \"uid\": 17,\n     \"name\": \"Geo\",\n     \"acl\": \"~* +@geo\"\n    }\n]","section_id":"get-all-response"},{"id":"get-redis_acl-ex0","language":"sh","code":"GET /v1/redis_acls/{int: uid}","section_id":"get-redis_acl"},{"id":"get-request-ex0","language":"sh","code":"GET /v1/redis_acls/1","section_id":"get-request"},{"id":"get-response-ex0","language":"json","code":"{\n     \"uid\": 17,\n     \"name\": \"Geo\",\n     \"acl\": \"~* +@geo\"\n}","section_id":"get-response"},{"id":"put-redis_acl-ex0","language":"sh","code":"PUT /v1/redis_acls/{int: uid}","section_id":"put-redis_acl"},{"id":"put-request-ex0","language":"sh","code":"PUT /v1/redis_acls/17","section_id":"put-request"},{"id":"put-request-ex1","language":"json","code":"{\n     \"acl\": \"~* +@geo -@dangerous\"\n}","section_id":"put-request"},{"id":"put-response-ex0","language":"json","code":"{\n     \"uid\": 17,\n     \"name\": \"Geo\",\n     \"acl\": \"~* +@geo -@dangerous\"\n}","section_id":"put-response"},{"id":"post-redis_acl-ex0","language":"sh","code":"POST /v1/redis_acls","section_id":"post-redis_acl"},{"id":"post-request-ex0","language":"sh","code":"POST /v1/redis_acls","section_id":"post-request"},{"id":"post-request-ex1","language":"json","code":"{\n     \"name\": \"Geo\",\n     \"acl\": \"~* +@geo\"\n}","section_id":"post-request"},{"id":"post-response-ex0","language":"json","code":"{\n     \"uid\": 17,\n     \"name\": \"Geo\",\n     \"acl\": \"~* +@geo\"\n}","section_id":"post-response"},{"id":"examples-ex0","language":"sh","code":"curl -k -u \"[username]:[password]\" -X POST \\\n     -H 'Content-Type: application/json' \\\n     -d '{ \"name\": \"Geo\", \"acl\": \"~* +@geo\" }' \\\n     https://[host][:port]/v1/redis_acls","section_id":"examples"},{"id":"examples-ex1","language":"python","code":"import requests\nimport json\n\nurl = \"https://[host][:port]/v1/redis_acls\"\n\nheaders = {\n  'Content-Type': 'application/json'\n}\n\npayload = json.dumps({\n    \"name\": \"Geo\",\n    \"acl\": \"~* +@geo\"\n})\nauth=(\"[username]\", \"[password]\")\n\nresponse = requests.request(\"POST\", url,\n           auth=auth, headers=headers, payload=payload, verify=False)\n\nprint(response.text)","section_id":"examples"},{"id":"delete-redis_acl-ex0","language":"sh","code":"DELETE /v1/redis_acls/{int: uid}","section_id":"delete-redis_acl"},{"id":"delete-request-ex0","language":"sh","code":"DELETE /v1/redis_acls/1","section_id":"delete-request"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/roles","title":"Roles requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/roles/","summary":"Roles requests","tags":["docs","operate","rs"],"last_updated":"2025-03-27T11:22:53-05:00","children":[],"page_type":"content","content_hash":"4414e63163695d23d8a3cb6893d013b93b303d1c979c53fc342c30dddb3b63d9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-all-roles) | `/v1/roles` | Get all roles |\n| [GET](#get-role) | `/v1/roles/{uid}` | Get a single role |\n| [PUT](#put-role) | `/v1/roles/{uid}` | Update an existing role |\n| [POST](#post-role) | `/v1/roles` | Create a new role |\n| [DELETE](#delete-role) | `/v1/roles/{uid}` | Delete a role |"},{"id":"get-all-roles","title":"Get all roles","role":"content","text":"[code example]\n\nGet all roles' details."},{"id":"permissions","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [view_all_roles_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_all_roles_info) | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |"},{"id":"get-all-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |"},{"id":"get-all-response","title":"Response","role":"returns","text":"Returns a JSON array of [role objects](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/role).\n\n#### Example JSON body\n\n[code example]\n\n#### Status codes {#get-all-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |\n| [501 Not Implemented](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.2) | Cluster doesn't support roles yet. |"},{"id":"get-role","title":"Get role","role":"content","text":"[code example]\n\nGet the details of a single role."},{"id":"permissions-1","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [view_role_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_role_info) | admin<br />cluster_member<br />cluster_viewer<br />db_member<br />db_viewer<br />user_manager |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The role's unique ID. |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns a [role object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/role).\n\n#### Example JSON body\n\n[code example]\n\n#### Status codes {#get-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success. |\n| [403 Forbidden](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.4) | Operation is forbidden. |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Role does not exist. |\n| [501 Not Implemented](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.2) | Cluster doesn't support roles yet. |"},{"id":"put-role","title":"Update role","role":"content","text":"[code example]\n\nUpdate an existing role's details."},{"id":"permissions-2","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [update_role](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#update_role) | admin<br />user_manager |"},{"id":"put-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Example JSON body\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### Query parameters\n\n| Field   | Type | Description |\n|---------|------|---------------|\n| dry_run |  | Validate the updated [role object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/role) but don't apply the update. |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The role's unique ID. |\n\n#### Body\n\nInclude a [role object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/role) with updated fields in the request body."},{"id":"put-response","title":"Response","role":"returns","text":"Returns a [role object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/role) with the updated fields.\n\n#### Example JSON body\n\n[code example]"},{"id":"put-error-codes","title":"Error codes","role":"errors","text":"Possible `error_code` values:\n\n| Code | Description |\n|------|-------------|\n| unsupported_resource | The cluster is not yet able to handle this resource type. This could happen in a partially upgraded cluster, where some of the nodes are still on a previous version.|\n| name_already_exists | An object of the same type and name exists.|\n| change_last_admin_role_not_allowed | At least one user with admin role should exist.|\n\n#### Status codes {#put-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success, role is created. |\n| [400 Bad Request](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1) | Bad or missing configuration parameters. |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Attempting to change a non-existant role. |\n| [501 Not Implemented](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.2) | Cluster doesn't support roles yet. |"},{"id":"post-role","title":"Create role","role":"content","text":"[code example]\n\nCreate a new role."},{"id":"permissions-3","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [create_role](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#create_role) | admin<br />user_manager |"},{"id":"post-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Example JSON body\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### Query parameters\n\n| Field   | Type | Description |\n|---------|------|---------------|\n| dry_run |  | Validate the new [role object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/role) but don't apply the update. |\n\n#### Body\n\nInclude a [role object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/role) in the request body."},{"id":"post-response","title":"Response","role":"returns","text":"Returns the newly created [role object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/role).\n\n#### Example JSON body\n\n[code example]"},{"id":"post-error-codes","title":"Error codes","role":"errors","text":"Possible `error_code`values:\n\n| Code | Description |\n|------|-------------|\n| unsupported_resource | The cluster is not yet able to handle this resource type. This could happen in a partially upgraded cluster, where some of the nodes are still on a previous version. |\n| name_already_exists | An object of the same type and name exists |\n| missing_field | A needed field is missing |\n\n#### Status codes {#post-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success, role is created. |\n| [400 Bad Request](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1) | Bad or missing configuration parameters. |\n| [501 Not Implemented](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.2) | Cluster doesn't support roles yet. |"},{"id":"examples","title":"Examples","role":"example","text":"#### cURL\n\n[code example]\n\n#### Python\n\n[code example]"},{"id":"delete-role","title":"Delete role","role":"content","text":"[code example]\n\nDelete a role object."},{"id":"permissions-4","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [delete_role](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#delete_role) | admin<br />user_manager |"},{"id":"delete-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The role unique ID. |"},{"id":"delete-response","title":"Response","role":"returns","text":"Returns a status code to indicate role deletion success or failure.\n\n#### Status codes {#delete-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success, the role is deleted. |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Role does not exist. |\n| [406 Not Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | The request is not acceptable. |\n| [501 Not Implemented](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.2) | Cluster doesn't support roles yet. |"}],"examples":[{"id":"get-all-roles-ex0","language":"sh","code":"GET /v1/roles","section_id":"get-all-roles"},{"id":"get-all-request-ex0","language":"sh","code":"GET /v1/roles","section_id":"get-all-request"},{"id":"get-all-response-ex0","language":"json","code":"[\n    {\n        \"uid\": 1,\n        \"name\": \"Admin\",\n        \"management\": \"admin\"\n    },\n    {\n        \"uid\": 2,\n        \"name\": \"Cluster Member\",\n        \"management\": \"cluster_member\"\n    },\n    {\n        \"uid\": 3,\n        \"name\": \"Cluster Viewer\",\n        \"management\": \"cluster_viewer\"\n    },\n    {\n        \"uid\": 4,\n        \"name\": \"DB Member\",\n        \"management\": \"db_member\"\n    },\n    {\n        \"uid\": 5,\n        \"name\": \"DB Viewer\",\n        \"management\": \"db_viewer\"\n    },\n    {\n        \"uid\": 6,\n        \"name\": \"None\",\n        \"management\": \"none\"\n    },\n    {\n        \"uid\": 17,\n        \"name\": \"DBA\",\n        \"management\": \"admin\"\n    }\n]","section_id":"get-all-response"},{"id":"get-role-ex0","language":"sh","code":"GET /v1/roles/{int: uid}","section_id":"get-role"},{"id":"get-request-ex0","language":"sh","code":"GET /v1/roles/1","section_id":"get-request"},{"id":"get-response-ex0","language":"json","code":"{\n     \"uid\": 17,\n     \"name\": \"DBA\",\n     \"management\": \"admin\"\n}","section_id":"get-response"},{"id":"put-role-ex0","language":"sh","code":"PUT /v1/roles/{int: uid}","section_id":"put-role"},{"id":"put-request-ex0","language":"sh","code":"PUT /v1/roles/17","section_id":"put-request"},{"id":"put-request-ex1","language":"json","code":"{\n     \"management\": \"cluster_member\"\n}","section_id":"put-request"},{"id":"put-response-ex0","language":"json","code":"{\n     \"uid\": 17,\n     \"name\": \"DBA\",\n     \"management\": \"cluster_member\"\n}","section_id":"put-response"},{"id":"post-role-ex0","language":"sh","code":"POST /v1/roles","section_id":"post-role"},{"id":"post-request-ex0","language":"sh","code":"POST /v1/roles","section_id":"post-request"},{"id":"post-request-ex1","language":"json","code":"{\n     \"name\": \"DBA\",\n     \"management\": \"admin\"\n}","section_id":"post-request"},{"id":"post-response-ex0","language":"json","code":"{\n     \"uid\": 17,\n     \"name\": \"DBA\",\n     \"management\": \"admin\"\n}","section_id":"post-response"},{"id":"examples-ex0","language":"sh","code":"curl -k -u \"[username]:[password]\" -X POST \\\n     -H 'Content-Type: application/json' \\\n     -d '{ \"name\": \"DBA\", \"management\": \"admin\" }' \\\n     https://[host][:port]/v1/roles","section_id":"examples"},{"id":"examples-ex1","language":"python","code":"import requests\nimport json\n\nurl = \"https://[host][:port]/v1/roles\"\n\nheaders = {\n  'Content-Type': 'application/json'\n}\n\npayload = json.dumps({\n    \"name\": \"DBA\",\n    \"management\": \"admin\"\n})\nauth=(\"[username]\", \"[password]\")\n\nresponse = requests.request(\"POST\", url,\n           auth=auth, headers=headers, payload=payload, verify=False)\n\nprint(response.text)","section_id":"examples"},{"id":"delete-role-ex0","language":"sh","code":"DELETE /v1/roles/{int: uid}","section_id":"delete-role"},{"id":"delete-request-ex0","language":"sh","code":"DELETE /v1/roles/1","section_id":"delete-request"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/services","title":"Services requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/services/","summary":"REST API requests to list or modify Redis Software services.","tags":["docs","operate","rs"],"last_updated":"2026-10-05T16:11:44+01:00","children":[],"page_type":"content","content_hash":"56fa0914fdd4ed416f326273b7699303758fb78c46bb0d0a6b587fedbee63696","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-local-services) | `/v1/local/services` | List Redis Software services on the local node |\n| [POST](#post-local-services) | `/v1/local/services` | Modify or perform operations on local processes |\n| [POST](#post-services) | `/v1/services` | Modify or perform operations on primary node processes |\n\n> [!WARNING]\n> This API is dangerous and should only be run with guidance from [Redis support](https://redis.io/support/).\n>\n> Stop, start, or restart optional services only. Changing the status of required services can negatively affect cluster behavior and cause a complete loss of the cluster and its data.\n>\n> For a list of optional services, see the [services configuration object reference](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration) or use a [`GET /v1/cluster/services_configuration`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/services_configuration#get-cluster-services_config) request."},{"id":"get-local-services","title":"Get local services","role":"content","text":"[code example]\n\nLists all Redis Software services currently running on the local node and relevant metadata."},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns a JSON object that describes all Redis Software services currently running on the local node and relevant metadata.\n\nPossible `status` values: \n- RESTARTING\n- RUNNING\n- STARTING\n- STOPPED\n\n#### Example JSON response body\n\n[code example]\n\n#### Status codes {#get-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | No error |"},{"id":"post-local-services","title":"Modify local services","role":"content","text":"[code example]\n\nModify Redis Software services or perform operations that directly interact with processes on the local node. To perform operations on the primary node, use [`POST /v1/services`](#post-services) instead.\n\nSupported `operation_type` values:\n\n- stop\n- start\n- restart\n\n> [!WARNING]\n> This API is dangerous and should only be run with guidance from [Redis support](https://redis.io/support/).\n>\n> Stop, start, or restart optional services only. Changing the status of required services can negatively affect cluster behavior and cause a complete loss of the cluster and its data.\n>\n> For a list of optional services, see the [services configuration object reference](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration) or use a [`GET /v1/cluster/services_configuration`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/services_configuration#get-cluster-services_config) request."},{"id":"post-local-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n\n#### Example JSON request body\n\n[code example]"},{"id":"post-local-response","title":"Response","role":"returns","text":"Returns a JSON object that shows whether the operation ran successfully or failed for each requested service.\n\n#### Example JSON response body\n\n[code example]\n\n\n#### Status codes {#post-local-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | No error. |"},{"id":"post-services","title":"Modify primary node services","role":"content","text":"[code example]\n\nModify Redis Software services or perform operations that directly interact with processes on the primary node. For operations on secondary nodes, use [`POST /v1/local/services`](#post-local-services) instead.\n\nSupported `operation_type` values:\n\n- stop\n- start\n- restart\n\n> [!WARNING]\n> This API is dangerous and should only be run with guidance from [Redis support](https://redis.io/support/).\n>\n> Stop, start, or restart optional services only. Changing the status of required services can negatively affect cluster behavior and cause a complete loss of the cluster and its data.\n>\n> For a list of optional services, see the [services configuration object reference](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/services_configuration) or use a [`GET /v1/cluster/services_configuration`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/services_configuration#get-cluster-services_config) request."},{"id":"post-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n\n#### Example JSON request body\n\n[code example]"},{"id":"post-response","title":"Response","role":"returns","text":"Returns a JSON object that shows whether the operation ran successfully or failed for each requested service.\n\n#### Example JSON response body\n\n[code example]\n\n\n#### Status codes {#post-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | No error. |"}],"examples":[{"id":"get-local-services-ex0","language":"sh","code":"GET /v1/local/services","section_id":"get-local-services"},{"id":"get-request-ex0","language":"sh","code":"GET /v1/local/services","section_id":"get-request"},{"id":"get-response-ex0","language":"json","code":"{\n    \"alert_mgr\": {\n        \"start_time\": \"2024-05-13T18:38:00Z\",\n        \"status\": \"RUNNING\",\n        \"uptime\": \"3 days, 0:58:59\"\n    },\n    \"ccs\": {\n        \"start_time\": \"2024-05-13T18:38:59Z\",\n        \"status\": \"RUNNING\",\n        \"uptime\": \"3 days, 0:58:00\"\n    },\n    ...\n}","section_id":"get-response"},{"id":"post-local-services-ex0","language":"sh","code":"POST /v1/local/services","section_id":"post-local-services"},{"id":"post-local-request-ex0","language":"sh","code":"POST /v1/local/services","section_id":"post-local-request"},{"id":"post-local-request-ex1","language":"json","code":"{\n  \"operation_type\": \"restart\",\n  \"services\": [\n    \"alert_mgr\"\n  ]\n}","section_id":"post-local-request"},{"id":"post-local-response-ex0","language":"json","code":"{\n    \"alert_mgr\": true,\n    \"metrics_exporter\": true\n}","section_id":"post-local-response"},{"id":"post-services-ex0","language":"sh","code":"POST /v1/services","section_id":"post-services"},{"id":"post-request-ex0","language":"sh","code":"POST /v1/services","section_id":"post-request"},{"id":"post-request-ex1","language":"json","code":"{\n  \"operation_type\": \"restart\",\n  \"services\": [\n    \"alert_mgr\"\n  ]\n}","section_id":"post-request"},{"id":"post-response-ex0","language":"json","code":"{\n    \"alert_mgr\": true,\n    \"metrics_exporter\": true\n}","section_id":"post-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/shards/actions/failover","title":"Shard failover requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/shards/actions/failover/","summary":"REST API requests to fail over database shards","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","page_type":"content","content_hash":"4ef99150ed4ff29ef1b8c17997dfc833e152fef8eced25d8c1547bb697b6cff9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [POST](#post-multi-shards) | `/v1/shards/actions/failover` | Fail over multiple shards |\n| [POST](#post-shard) | `/v1/shards/{uid}/actions/failover` | Fail over a specific shard |"},{"id":"post-multi-shards","title":"Fail over multiple shards","role":"content","text":"POST /v1/shards/actions/failover\n\nPerforms failover on the primary shards specified by `shard_uids` in the request body, and promotes their replicas to primary shards. This request is asynchronous.\n\nThe cluster automatically manages failover to ensure high availability. Use this failover REST API request only for testing and planned maintenance.\n\n#### Required permissions\n\n| Permission name | Roles |\n|-----------------|-------|\n| [failover_shard](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#failover_shard) | admin<br />cluster_member<br />db_member |"},{"id":"post-multi-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tPOST /v1/shards/actions/failover\n\n#### Example JSON body\n\n[code example]\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### Request body {#post-multi-request-body}\n\nThe request body is a JSON object that can contain the following fields:\n\n| Field | Type | Description |\n|-------|------|-------------|\n| shard_uids | array of strings | List of primary shard UIDs to fail over. The shards must belong to the same database. |\n| dead_uids | array of strings | Primary shards to avoid stopping. Optional. |\n| dead_nodes | array of strings | Nodes that should not be drained or used for promoted replica shards. Optional. |\n| dry_run | boolean | Determines whether the failover is actually done. If true, will just do a dry run. If the dry run succeeds, the request returns a `200 OK` status code.  Otherwise, it returns a JSON object with an error code and description. Optional. |\n| force_rebind | boolean | Rebind after promotion. Optional. |\n| redis_version_upgrade | string | New version of the promoted primary shards. Optional. |"},{"id":"post-multi-response","title":"Response","role":"returns","text":"Returns a JSON object with an `action_uid`. You can track the action's progress with a [`GET /v1/actions/<action_uid>`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/actions#get-action) request.\n\n#### Example JSON body\n\n[code example]"},{"id":"post-multi-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | No error. |\n| [400 Bad Request](https://www.rfc-editor.org/rfc/rfc9110.html#name-400-bad-request) | Shard is a replica or the specified failover shards are not in the same database. |\n| [404 Not Found](https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found) | A list of shard UIDs is required and not given, or a specified shard does not exist. |\n| [409 Conflict](https://www.rfc-editor.org/rfc/rfc9110.html#name-409-conflict) | Database is currently busy. |"},{"id":"put-multi-error-codes","title":"Error codes","role":"errors","text":"When errors are reported, the server may return a JSON object with `error_code` and `message` field that provide additional information. The following are possible `error_code` values:\n\n| Code | Description |\n|------|-------------|\n| db_busy | Database is currently busy. |\n| failover_shards_different_bdb | All failover shards should be in the same database. |\n| shard_is_slave | Shard is a replica. |\n| shard_not_exist | Shard does not exist. |\n| shard_uids_required | List of shard UIDs is required and not given. |"},{"id":"post-shard","title":"Fail over shard","role":"content","text":"POST /v1/shards/{int: uid}/actions/failover\n\nPerforms failover on the primary shard with the specified `shard_uid`, and promotes its replica shard to a primary shard. This request is asynchronous.\n\nThe cluster automatically manages failover to ensure high availability. Use this failover REST API request only for testing and planned maintenance.\n\n#### Required permissions\n\n| Permission name | Roles |\n|-----------------|-------|\n| [failover_shard](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#failover_shard) | admin<br />cluster_member<br />db_member |"},{"id":"post-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tPOST /v1/shards/1/actions/failover\n\n#### Example JSON body\n\n[code example]\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the shard to fail over. |\n\n\n#### Request body {#post-request-body}\n\nThe request body is a JSON object that can contain the following fields:\n\n| Field | Type | Description |\n|-------|------|-------------|\n| dead_uid | string | Primary shard to avoid stopping. Optional. |\n| dead_nodes | array of strings | Nodes that should not be drained or used for promoted replica shards. Optional. |\n| dry_run | boolean | Determines whether the failover is actually done. If true, will just do a dry run. If the dry run succeeds, the request returns a `200 OK` status code.  Otherwise, it returns a JSON object with an error code and description. Optional. |\n| force_rebind | boolean | Rebind after promotion. Optional. |\n| redis_version_upgrade | string | New version of the promoted primary shards. Optional. |"},{"id":"post-response","title":"Response","role":"returns","text":"Returns a JSON object with an `action_uid`. You can track the action's progress with a [`GET /v1/actions/<action_uid>`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/actions#get-action) request.\n\n#### Example JSON body\n\n[code example]"},{"id":"post-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | No error. |\n| [400 Bad Request](https://www.rfc-editor.org/rfc/rfc9110.html#name-400-bad-request) | Shard is a replica. |\n| [404 Not Found](https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found) | Specified shard does not exist. |\n| [409 Conflict](https://www.rfc-editor.org/rfc/rfc9110.html#name-409-conflict) | Database is currently busy. |"},{"id":"put-error-codes","title":"Error codes","role":"errors","text":"When errors are reported, the server may return a JSON object with `error_code` and `message` field that provide additional information. The following are possible `error_code` values:\n\n| Code | Description |\n|------|-------------|\n| db_busy | Database is currently busy. |\n| shard_is_slave | Shard is a replica. |\n| shard_not_exist | Shard does not exist. |"}],"examples":[{"id":"post-multi-request-ex0","language":"json","code":"{\n    \"shard_uids\": [\"2\",\"4\",\"6\"]\n}","section_id":"post-multi-request"},{"id":"post-multi-response-ex0","language":"json","code":"{\n    \"action_uid\": \"e5e24ddf-a456-4a7e-ad53-4463cd44880e\",\n    \"description\": \"Failover was triggered\"\n}","section_id":"post-multi-response"},{"id":"post-request-ex0","language":"json","code":"{\n    \"force_rebind\": true\n}","section_id":"post-request"},{"id":"post-response-ex0","language":"json","code":"{\n    \"action_uid\": \"e5e24ddf-a456-4a7e-ad53-4463cd44880e\",\n    \"description\": \"Failover was triggered\"\n}","section_id":"post-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/shards/actions","title":"Shard actions requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/shards/actions/","summary":"REST API requests to perform shard actions","tags":["docs","operate","rs"],"last_updated":"2024-09-23T11:04:55-05:00","children":[{"id":"operate/rs/references/rest-api/requests/shards/actions/failover","summary":"REST API requests to fail over database shards","title":"Shard failover requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/shards/actions/failover/"},{"id":"operate/rs/references/rest-api/requests/shards/actions/migrate","summary":"REST API requests to migrate database shards","title":"Migrate shards requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/shards/actions/migrate/"}],"page_type":"content","content_hash":"612075da707ffccdd52775f7b905e78918b6816fddf971fd2725c041202b4039","sections":[{"id":"migrate","title":"Migrate","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [POST](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/shards/actions/migrate#post-multi-shards) | `/v1/shards/actions/migrate` | Migrate multiple shards |\n| [POST](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/shards/actions/migrate#post-shard) | `/v1/shards/{uid}/actions/migrate` | Migrate a specific shard |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/shards/actions/migrate","title":"Migrate shards requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/shards/actions/migrate/","summary":"REST API requests to migrate database shards","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","page_type":"content","content_hash":"402c6aab0c3221198c9e98a0400d62247baa9ca3c388e6ff5cab0c696230429a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [POST](#post-multi-shards) | `/v1/shards/actions/migrate` | Migrate multiple shards |\n| [POST](#post-shard) | `/v1/shards/{uid}/actions/migrate` | Migrate a specific shard |"},{"id":"post-multi-shards","title":"Migrate multiple shards","role":"content","text":"POST /v1/shards/actions/migrate\n\nMigrates the list of given shard UIDs to the node specified by `target_node_uid`. The shards can be from multiple databases. This request is asynchronous.\n\nFor more information about shard migration use cases and considerations, see [Migrate database shards](https://redis.io/docs/latest/operate/rs/databases/migrate-shards).\n\n#### Required permissions\n\n| Permission name | Roles |\n|-----------------|-------|\n| [migrate_shard](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#migrate_shard) | admin<br />cluster_member<br />db_member |"},{"id":"post-multi-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tPOST /v1/shards/actions/migrate\n\n#### Example JSON body\n\n[code example]\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### Request body {#post-multi-request-body}\n\nThe request body is a JSON object that can contain the following fields:\n\n| Field | Type | Description |\n|-------|------|-------------|\n| shard_uids | array of strings | List of shard UIDs to migrate. |\n| target_node_uid | integer | UID of the node to where the shards should migrate. |\n| override_rack_policy | boolean | If true, overrides and ignores rack-aware policy violations. |\n| dry_run | boolean | Determines whether the migration is actually done. If true, will just do a dry run. If the dry run succeeds, the request returns a `200 OK` status code.  Otherwise, it returns a JSON object with an error code and description. |\n| preserve_roles | boolean | If true, preserves the migrated shards' roles after migration. |\n| max_concurrent_bdb_migrations | integer | The number of concurrent databases that can migrate shards. |"},{"id":"post-multi-response","title":"Response","role":"returns","text":"Returns a JSON object with an `action_uid`. You can track the action's progress with a [`GET /v1/actions/<action_uid>`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/actions#get-action) request.\n\n#### Example JSON body\n\n[code example]"},{"id":"post-multi-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | No error. |\n| [400 Bad Request](https://www.rfc-editor.org/rfc/rfc9110.html#name-400-bad-request) | Conflicting parameters. |\n| [404 Not Found](https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found) | A list of shard UIDs is required and not given, a specified shard does not exist, or a node UID is required and not given. |\n| [500 Internal Server Error](https://www.rfc-editor.org/rfc/rfc9110.html#name-500-internal-server-error) | Migration failed. |"},{"id":"post-shard","title":"Migrate shard","role":"content","text":"POST /v1/shards/{int: uid}/actions/migrate\n\nMigrates the shard with the given `shard_uid` to the node specified by `target_node_uid`. If the shard is already on the target node, nothing happens. This request is asynchronous.\n\nFor more information about shard migration use cases and considerations, see [Migrate database shards](https://redis.io/docs/latest/operate/rs/databases/migrate-shards).\n\n#### Required permissions\n\n| Permission name | Roles |\n|-----------------|-------|\n| [migrate_shard](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#migrate_shard) | admin<br />cluster_member<br />db_member |"},{"id":"post-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tPOST /v1/shards/1/actions/migrate\n\n#### Example JSON body\n\n[code example]\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the shard to migrate. |\n\n\n#### Request body {#post-request-body}\n\nThe request body is a JSON object that can contain the following fields:\n\n| Field | Type | Description |\n|-------|------|-------------|\n| target_node_uid | integer | UID of the node to where the shard should migrate. |\n| override_rack_policy | boolean | If true, overrides and ignores rack-aware policy violations. |\n| dry_run | boolean | Determines whether the migration is actually done. If true, will just do a dry run. If the dry run succeeds, the request returns a `200 OK` status code.  Otherwise, it returns a JSON object with an error code and description. |\n| preserve_roles | boolean | If true, preserves the migrated shards' roles after migration. |"},{"id":"post-response","title":"Response","role":"returns","text":"Returns a JSON object with an `action_uid`. You can track the action's progress with a [`GET /v1/actions/<action_uid>`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/actions#get-action) request.\n\n#### Example JSON body\n\n[code example]"},{"id":"post-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | No error. |\n| [404 Not Found](https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found) | Shard does not exist, or node UID is required and not given. |\n| [409 Conflict](https://www.rfc-editor.org/rfc/rfc9110.html#name-409-conflict) | Database is currently busy. |"}],"examples":[{"id":"post-multi-request-ex0","language":"json","code":"{\n  \"shard_uids\": [\"2\",\"4\",\"6\"],\n  \"target_node_uid\": 9,\n  \"override_rack_policy\": false,\n  \"preserve_roles\": false,\n  \"max_concurrent_bdb_migrations\": 3\n}","section_id":"post-multi-request"},{"id":"post-multi-response-ex0","language":"json","code":"{\n    \"action_uid\": \"e5e24ddf-a456-4a7e-ad53-4463cd44880e\",\n    \"description\": \"Migrate was triggered\"\n}","section_id":"post-multi-response"},{"id":"post-request-ex0","language":"json","code":"{\n    \"target_node_uid\": 9,\n    \"override_rack_policy\": false,\n    \"preserve_roles\": false\n}","section_id":"post-request"},{"id":"post-response-ex0","language":"json","code":"{\n    \"action_uid\": \"e5e24ddf-a456-4a7e-ad53-4463cd44880e\",\n    \"description\": \"Migrate was triggered\"\n}","section_id":"post-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/shards","title":"Shard requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/shards/","summary":"REST API requests for database shards","tags":["docs","operate","rs"],"last_updated":"2025-11-07T12:59:03-06:00","children":[{"id":"operate/rs/references/rest-api/requests/shards/actions","summary":"REST API requests to perform shard actions","title":"Shard actions requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/shards/actions/"},{"id":"operate/rs/references/rest-api/requests/shards/stats","summary":"Shard statistics requests","title":"Shards stats requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/shards/stats/"}],"page_type":"content","content_hash":"98e6a628d683ad13fd4c93a3cbe509ecb1af627df3edfd9f6a9e1cc6d0003fc2","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-all-shards) | `/v1/shards` | Get all shards |\n| [GET](#get-shard) | `/v1/shards/{uid}` | Get a specific shard |"},{"id":"get-all-shards","title":"Get all shards","role":"content","text":"GET /v1/shards\n\nGet information about all shards in the cluster."},{"id":"get-all-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/shards?extra_info_keys=used_memory_rss&extra_info_keys=connected_clients\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| extra_info_keys | list of strings | An optional list of extra keys to be fetched from the Redis [`INFO`](https://redis.io/docs/latest/commands/info) command. See [extra_info_keys](#extra_info_keys) for common keys to include. |"},{"id":"get-all-response","title":"Response","role":"returns","text":"Returns a JSON array of [shard objects](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/shard).\n\n#### Example JSON body\n\n[code example]"},{"id":"get-all-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | No error. |"},{"id":"get-shard","title":"Get shard","role":"content","text":"GET /v1/shards/{int: uid}\n\nGets information about a single shard."},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/shards/1?extra_info_keys=used_memory_rss&extra_info_keys=connected_clients\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the requested shard. |\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| extra_info_keys | list of strings | An optional list of extra keys to be fetched from the Redis [`INFO`](https://redis.io/docs/latest/commands/info) command. See [extra_info_keys](#extra_info_keys) for common keys to include. |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns a [shard object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/shard).\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | No error. |\n| [404 Not Found](https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found) | Shard UID does not exist. |"},{"id":"extra_info_keys","title":"extra_info_keys","role":"content","text":"You can include any Redis [`INFO`](https://redis.io/docs/latest/commands/info) command key for shard requests using the `extra_info_keys` query parameter. The requested keys are returned in the `redis_info` object of the response.\n\nSee the following tables for common keys to include."},{"id":"client-keys","title":"Client keys","role":"content","text":"| Key | Type | Description |\n|-----|------|-------------|\n| blocked_clients | integer | Number of clients pending on a blocking call |\n| client_recent_max_input_buffer | integer | Biggest input buffer among current client connections |\n| client_recent_max_output_buffer | integer | Biggest output buffer among current client connections |\n| connected_clients | integer | Number of client connections, excluding connections from replicas |"},{"id":"memory-keys","title":"Memory keys","role":"content","text":"| Key | Type | Description |\n|-----|------|-------------|\n| maxmemory | integer | Value of the maxmemory configuration directive |\n| used_memory | integer | Total number of bytes allocated by Redis |\n| used_memory_dataset | integer | Size in bytes of the dataset |\n| used_memory_lua | integer | Number of bytes used by the Lua engine |\n| used_memory_overhead | integer | Sum of all overheads that the server allocated for managing its internal data structures |\n| used_memory_peak | integer | Peak memory in bytes consumed by Redis |\n| used_memory_rss | integer | Number of bytes that Redis allocated as seen by the operating system |"},{"id":"persistence-keys","title":"Persistence keys","role":"content","text":"| Key | Type | Description |\n|-----|------|-------------|\n| aof_enabled | integer | Flag indicating append-only file (AOF) logging is activated |\n| aof_rewrite_in_progress | integer | Flag indicating an AOF rewrite operation is on-going |\n| rdb_bgsave_in_progress | integer | Flag indicating an RDB save is on-going |\n| rdb_changes_since_last_save | integer | Number of changes since the last dump |\n| rdb_last_bgsave_status | string | Status of the last RDB save operation |\n| rdb_last_save_time | integer | Epoch-based timestamp of last successful RDB save |"},{"id":"replication-keys","title":"Replication keys","role":"content","text":"| Key | Type | Description |\n|-----|------|-------------|\n| master_last_io_seconds_ago | integer | Number of seconds since the last interaction with the primary shard |\n| master_repl_offset | integer | The server's current replication offset |\n| slave_repl_offset | integer | Replication offset of the replica instance |"},{"id":"server-keys","title":"Server keys","role":"content","text":"| Key | Type | Description |\n|-----|------|-------------|\n| configured_hz | integer | Server's configured frequency setting |\n| hz | integer | Server's current frequency setting |\n| redis_version | string | Version of the Redis server |\n| uptime_in_days | integer | Number of days since Redis server start |\n| uptime_in_seconds | integer | Number of seconds since Redis server start |"},{"id":"statistics-keys","title":"Statistics keys","role":"content","text":"| Key | Type | Description |\n|-----|------|-------------|\n| instantaneous_ops_per_sec | integer | Number of commands processed per second |\n| keyspace_hits | integer | Number of successful lookup of keys in the main dictionary |\n| keyspace_misses | integer | Number of failed lookup of keys in the main dictionary |\n| total_commands_processed | integer | Total number of commands processed by the server |\n| total_connections_received | integer | Total number of connections accepted by the server |\n| total_net_input_bytes | integer | Total number of bytes read from the network |\n| total_net_output_bytes | integer | Total number of bytes written to the network |"}],"examples":[{"id":"get-all-response-ex0","language":"json","code":"[\n  {\n    \"uid\": \"1\",\n    \"role\": \"master\",\n    \"assigned_slots\": \"0-16383\",\n    \"bdb_uid\": 1,\n    \"detailed_status\": \"ok\",\n    \"loading\": {\n      \"status\": \"idle\"\n    },\n    \"node_uid\": \"1\",\n    \"redis_info\": {\n\t\t\t\"connected_clients\": 14,\n\t\t\t\"used_memory_rss\": 12263424\n\t\t},\n    \"report_timestamp\": \"2024-06-28T18:44:01Z\",\n    \"status\": \"active\"\n  },\n  {\n    \"uid\": \"2\",\n    \"role\": \"slave\",\n    // additional fields...\n  }\n]","section_id":"get-all-response"},{"id":"get-response-ex0","language":"json","code":"{\n  \"assigned_slots\": \"0-16383\",\n  \"bdb_uid\": 1,\n  \"detailed_status\": \"ok\",\n  \"loading\": {\n    \"status\": \"idle\"\n  },\n  \"node_uid\": \"1\",\n  \"redis_info\": {\n\t\t\"connected_clients\": 14,\n\t\t\"used_memory_rss\": 12263424\n\t},\n  \"role\": \"master\",\n  \"report_timestamp\": \"2024-06-28T18:44:01Z\",\n  \"status\": \"active\",\n  \"uid\": \"1\"\n}","section_id":"get-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/shards/stats","title":"Shards stats requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/shards/stats/","summary":"Shard statistics requests","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","children":[{"id":"operate/rs/references/rest-api/requests/shards/stats/last","summary":"Most recent shard statistics requests","title":"Latest shards stats requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/shards/stats/last/"}],"page_type":"content","content_hash":"e8ba4b21d00d9d2eda1ac8d2825e9c6f5ceeccf601d45d3712734eaf1abe5a3d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"The v1 statistics REST API endpoints are deprecated as of Redis Software version 7.22. Use the [metrics stream engine for monitoring v2](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine) instead for real-time monitoring and metrics collection.\n\n\n| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-all-shards-stats) | `/v1/shards/stats` | Get stats for all shards |\n| [GET](#get-shard-stats) | `/v1/shards/stats/{uid}` | Get stats for a specific shard |"},{"id":"get-all-shards-stats","title":"Get all shards stats","role":"content","text":"GET /v1/shards/stats\n\nGet statistics for all shards.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_all_shard_stats](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_all_shard_stats) |"},{"id":"get-all-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/shards/stats?interval=1hour&stime=2014-08-28T10:00:00Z \n\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| parent_uid | integer | Only return shard from the given BDB ID (optional) |\n| interval | string | Time interval for which we want stats: 1sec/10sec/5min/15min/1hour/12hour/1week (optional) |\n| stime | ISO_8601 | Start time from which we want the stats. Should comply with the [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601) format (optional) |\n| etime | ISO_8601 | End time after which we don't want the stats. Should comply with the [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601) format (optional) |\n| metrics | list | Comma-separated list of [metric names](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/statistics/shard-metrics) for which we want statistics (default is all) (optional) |"},{"id":"get-all-response","title":"Response","role":"returns","text":"Returns a JSON array of [statistics](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/statistics) for all shards.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-all-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | No error |\n| [404 Not Found](https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found) | No shards exist |"},{"id":"get-shard-stats","title":"Get shard stats","role":"content","text":"GET /v1/shards/stats/{int: uid}\n\nGet statistics for a specific shard.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_shard_stats](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_shard_stats) |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/shards/stats/1?interval=1hour&stime=2014-08-28T10:00:00Z \n\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the shard requested. |\n\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| interval | string | Time interval for which we want stats: 1sec/10sec/5min/15min/1hour/12hour/1week (optional) |\n| stime | ISO_8601 | Start time from which we want the stats. Should comply with the [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601) format (optional) |\n| etime | ISO_8601 | End time after which we don't want the stats. Should comply with the [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601) format (optional) |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns [statistics](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/statistics) for the specified shard.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | No error |\n| [404 Not Found](https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found) | Shard does not exist |\n| [406 Not Acceptable](https://www.rfc-editor.org/rfc/rfc9110.html#name-406-not-acceptable) | Shard isn't currently active |"}],"examples":[{"id":"get-all-response-ex0","language":"json","code":"[\n  {\n    \"status\": \"active\",\n    \"uid\": \"1\",\n    \"node_uid\": \"1\",\n    \"assigned_slots\": \"0-8191\",\n    \"intervals\": [\n    {\n      \"interval\": \"1sec\",\n      \"stime\": \"2015-05-28T08:27:35Z\",\n      \"etime\": \"2015-05-28T08:27:40Z\",\n      \"used_memory_peak\": 5888264.0,\n      \"used_memory_rss\": 5888264.0,\n      \"read_hits\": 0.0,\n      \"pubsub_patterns\": 0.0,\n      \"no_of_keys\": 0.0,\n      \"mem_size_lua\": 35840.0,\n      \"last_save_time\": 1432541051.0,\n      \"sync_partial_ok\": 0.0,\n      \"connected_clients\": 9.0,\n      \"avg_ttl\": 0.0,\n      \"write_misses\": 0.0,\n      \"used_memory\": 5651440.0,\n      \"sync_full\": 0.0,\n      \"expired_objects\": 0.0,\n      \"total_req\": 0.0,\n      \"blocked_clients\": 0.0,\n      \"pubsub_channels\": 0.0,\n      \"evicted_objects\": 0.0,\n      \"no_of_expires\": 0.0,\n      \"interval\": \"1sec\",\n      \"write_hits\": 0.0,\n      \"read_misses\": 0.0,\n      \"sync_partial_err\": 0.0,\n      \"rdb_changes_since_last_save\": 0.0\n    },\n    {\n      \"interval\": \"1sec\",\n      \"stime\": \"2015-05-28T08:27:40Z\",\n      \"etime\": \"2015-05-28T08:27:45Z\",\n      \"// additional fields...\"\n      }\n    ]\n  },\n  {\n    \"uid\": \"2\",\n    \"status\": \"active\",\n    \"node_uid\": \"1\",\n    \"assigned_slots\": \"8192-16383\",\n    \"intervals\": [\n      {\n        \"interval\": \"1sec\",\n        \"stime\": \"2015-05-28T08:27:35Z\",\n        \"etime\": \"2015-05-28T08:27:40Z\",\n        \"// additional fields...\"\n      },\n      {\n        \"interval\": \"1sec\",\n        \"stime\": \"2015-05-28T08:27:40Z\",\n        \"etime\": \"2015-05-28T08:27:45Z\",\n        \"// additional fields...\"\n      }\n    ]\n  }\n]","section_id":"get-all-response"},{"id":"get-response-ex0","language":"json","code":"{\n  \"uid\": \"1\",\n  \"status\": \"active\",\n  \"node_uid\": \"1\",\n  \"role\": \"master\",\n  \"intervals\": [\n    {\n      \"interval\": \"1sec\",\n      \"stime\": \"2015-05-28T08:24:13Z\",\n      \"etime\": \"2015-05-28T08:24:18Z\",\n      \"avg_ttl\": 0.0,\n      \"blocked_clients\": 0.0,\n      \"connected_clients\": 9.0,\n      \"etime\": \"2015-05-28T08:24:18Z\",\n      \"evicted_objects\": 0.0,\n      \"expired_objects\": 0.0,\n      \"last_save_time\": 1432541051.0,\n      \"used_memory\": 5651440.0,\n      \"mem_size_lua\": 35840.0,\n      \"used_memory_peak\": 5888264.0,\n      \"used_memory_rss\": 5888264.0,\n      \"no_of_expires\": 0.0,\n      \"no_of_keys\": 0.0,\n      \"pubsub_channels\": 0.0,\n      \"pubsub_patterns\": 0.0,\n      \"rdb_changes_since_last_save\": 0.0,\n      \"read_hits\": 0.0,\n      \"read_misses\": 0.0,\n      \"stime\": \"2015-05-28T08:24:13Z\",\n      \"sync_full\": 0.0,\n      \"sync_partial_err\": 0.0,\n      \"sync_partial_ok\": 0.0,\n      \"total_req\": 0.0,\n      \"write_hits\": 0.0,\n      \"write_misses\": 0.0\n    },\n    {\n      \"interval\": \"1sec\",\n      \"stime\": \"2015-05-28T08:24:18Z\",\n      \"etime\": \"2015-05-28T08:24:23Z\",\n\n      \"// additional fields...\"\n    }\n  ]\n}","section_id":"get-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/shards/stats/last","title":"Latest shards stats requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/shards/stats/last/","summary":"Most recent shard statistics requests","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","page_type":"content","content_hash":"4f2f29056e144221d161a2704f94fec8a8b4000376a2851e108f263890c803a4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-all-shards-stats-last) | `/v1/shards/stats/last` | Get most recent stats for all shards |\n| [GET](#get-shard-stats-last) | `/v1/shards/stats/last/{uid}` | Get most recent stats for a specific shard |"},{"id":"get-all-shards-stats-last","title":"Get latest stats for all shards","role":"content","text":"GET /v1/shards/stats/last\n\nGet most recent statistics for all shards.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_all_shard_stats](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_all_shard_stats) |"},{"id":"get-all-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/shards/stats/last?interval=1sec&stime=015-05-27T08:27:35Z \n\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| interval | string | Time interval for which we want stats: 1sec/10sec/5min/15min/1hour/12hour/1week. Default: 1sec (optional) |\n| stime | ISO_8601 | Start time from which we want the stats. Should comply with the [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601) format (optional) |\n| etime | ISO_8601 | End time after which we don't want the stats. Should comply with the [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601) format (optional) |"},{"id":"get-all-response","title":"Response","role":"returns","text":"Returns most recent [statistics](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/statistics) for all shards.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-all-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | No error |\n| [404 Not Found](https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found) | No shards exist |"},{"id":"get-shard-stats-last","title":"Get latest shard stats","role":"content","text":"GET /v1/shards/stats/last/{int: uid}\n\nGet most recent statistics for a specific shard.\n\n#### Required permissions\n\n| Permission name |\n|-----------------|\n| [view_shard_stats](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_shard_stats) |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/shards/stats/last/1?interval=1sec&stime=2015-05-28T08:27:35Z \n\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The unique ID of the shard requested. |\n\n\n#### Query parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| interval | string | Time interval for which we want stats: 1sec/10sec/5min/15min/1hour/12hour/1week. Default: 1sec. (optional) |\n| stime | ISO_8601 | Start time from which we want the stats. Should comply with the [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601) format (optional) |\n| etime | ISO_8601 | End time after which we don't want the stats. Should comply with the [ISO_8601](https://en.wikipedia.org/wiki/ISO_8601) format (optional) |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns the most recent [statistics](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/statistics) for the specified shard.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | No error |\n| [404 Not Found](https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found) | Shard does not exist |\n| [406 Not Acceptable](https://www.rfc-editor.org/rfc/rfc9110.html#name-406-not-acceptable) | Shard isn't currently active |"}],"examples":[{"id":"get-all-response-ex0","language":"json","code":"{\n   \"1\": {\n      \"interval\": \"1sec\",\n      \"stime\": \"2015-05-28T08:27:35Z\",\n      \"etime\": \"2015-05-28T08:28:36Z\",\n      \"used_memory_peak\": 5888264.0,\n      \"used_memory_rss\": 5888264.0,\n      \"read_hits\": 0.0,\n      \"pubsub_patterns\": 0.0,\n      \"no_of_keys\": 0.0,\n      \"mem_size_lua\": 35840.0,\n      \"last_save_time\": 1432541051.0,\n      \"sync_partial_ok\": 0.0,\n      \"connected_clients\": 9.0,\n      \"avg_ttl\": 0.0,\n      \"write_misses\": 0.0,\n      \"used_memory\": 5651440.0,\n      \"sync_full\": 0.0,\n      \"expired_objects\": 0.0,\n      \"total_req\": 0.0,\n      \"blocked_clients\": 0.0,\n      \"pubsub_channels\": 0.0,\n      \"evicted_objects\": 0.0,\n      \"no_of_expires\": 0.0,\n      \"interval\": \"1sec\",\n      \"write_hits\": 0.0,\n      \"read_misses\": 0.0,\n      \"sync_partial_err\": 0.0,\n      \"rdb_changes_since_last_save\": 0.0\n   },\n   \"2\": {\n      \"interval\": \"1sec\",\n      \"stime\": \"2015-05-28T08:27:40Z\",\n      \"etime\": \"2015-05-28T08:28:45Z\",\n      \"// additional fields...\"\n   }\n}","section_id":"get-all-response"},{"id":"get-response-ex0","language":"json","code":"{\n   \"1\": {\n      \"interval\": \"1sec\",\n      \"stime\": \"2015-05-28T08:27:35Z\",\n      \"etime\": \"2015-05-28T08:27:36Z\",\n      \"used_memory_peak\": 5888264.0,\n      \"used_memory_rss\": 5888264.0,\n      \"read_hits\": 0.0,\n      \"pubsub_patterns\": 0.0,\n      \"no_of_keys\": 0.0,\n      \"mem_size_lua\": 35840.0,\n      \"last_save_time\": 1432541051.0,\n      \"sync_partial_ok\": 0.0,\n      \"connected_clients\": 9.0,\n      \"avg_ttl\": 0.0,\n      \"write_misses\": 0.0,\n      \"used_memory\": 5651440.0,\n      \"sync_full\": 0.0,\n      \"expired_objects\": 0.0,\n      \"total_req\": 0.0,\n      \"blocked_clients\": 0.0,\n      \"pubsub_channels\": 0.0,\n      \"evicted_objects\": 0.0,\n      \"no_of_expires\": 0.0,\n      \"interval\": \"1sec\",\n      \"write_hits\": 0.0,\n      \"read_misses\": 0.0,\n      \"sync_partial_err\": 0.0,\n      \"rdb_changes_since_last_save\": 0.0\n   }\n}","section_id":"get-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/suffix","title":"Suffix requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/suffix/","summary":"DNS suffix requests","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","children":[],"page_type":"content","content_hash":"e2a9caedc9175cdb8998ac4aa41c633257f7b6e076c4042b06bf08750ed11542","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-suffix) | `/v1/suffix/{name}` | Get a single DNS suffix |"},{"id":"get-suffix","title":"Get suffix","role":"content","text":"GET /v1/suffix/{string: name}\n\nGet a single DNS suffix."},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/suffix/cluster.fqdn \n\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| name | string | The unique name of the suffix requested. |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns a [suffix object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/suffix).\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Suffix name does not exist |"}],"examples":[{"id":"get-response-ex0","language":"json","code":"{\n    \"name\": \"cluster.fqdn\",\n    \"// additional fields...\"\n}","section_id":"get-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/suffixes","title":"Suffixes requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/suffixes/","summary":"DNS suffixes requests","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","children":[],"page_type":"content","content_hash":"0b29e4b22cc0147e77ee9eb8138d1bfec95aa1030c3f7c34af04e56d3307cb07","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-all-suffixes) | `/v1/suffixes` | Get all DNS suffixes |"},{"id":"get-all-suffixes","title":"Get all suffixes","role":"content","text":"GET /v1/suffixes\n\nGet all DNS suffixes in the cluster."},{"id":"get-all-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tGET /v1/suffixes \n\n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |"},{"id":"get-all-response","title":"Response","role":"returns","text":"The response body contains a JSON array with all suffixes, represented as [suffix objects](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/suffix).\n\n#### Example JSON body\n\n[code example]"},{"id":"get-all-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |"}],"examples":[{"id":"get-all-response-ex0","language":"json","code":"[\n    {\n        \"name\": \"cluster.fqdn\",\n        \"// additional fields...\"\n    },\n    {\n        \"name\": \"internal.cluster.fqdn\",\n        \"// additional fields...\"\n    }\n]","section_id":"get-all-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/usage_report","title":"Usage report requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/usage_report/","summary":"REST API request to get the database usage report from the cluster.","tags":["docs","operate","rs"],"last_updated":"2026-07-02T11:30:01-05:00","children":[],"page_type":"content","content_hash":"ba18e1110a2f4d5a1654d427beb553b27027e13730a53dd9a5d5d2e4f7b3ca00","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-usage-report) | `/v1/usage_report` | Get the cluster's database usage report |"},{"id":"get-usage-report","title":"Get usage report","role":"content","text":"[code example]\n\nGets the database usage report from the cluster as Newline Delimited JSON (NDJSON). The response is returned as a gzip file if the request includes the `Accept-Encoding: gzip` header; otherwise, it returns an NDJSON file directly. The final line in the file is the response's MD5 hash."},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n| Accept-Encoding | gzip | (Optional) Request gzip compression for the response |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns Newline Delimited JSON (NDJSON), which represents the usage report for every database in the cluster. The response is returned as a gzip file if the request includes the `Accept-Encoding: gzip` header; otherwise, it returns an NDJSON file directly. The final line in the file is the response's MD5 hash.\n\n| Field | Type/Value | Description |\n|-------|------------|-------------|\n| active_active | boolean | Indicates if Active-Active is enabled |\n| api_version | string | API version |\n| backup | boolean | Indicates if backup is enabled |\n| bdb_uid | string | The database's unique ID |\n| cluster_name | string | Cluster name |\n| cluster_uuid | string | Cluster's unique ID |\n| date | string | Date of the report, including time and time zone |\n| <span class=\"break-all\">dominant_shard_criteria</span> | \"mem\"<br />\"ops\"<br />\"rof\" | Dominant criteria for shard selection |\n| type | \"core\"<br />\"premium\"<br />\"auto_tiering\" | Database type |\n| shard_type | \"micro\"<br />\"normal\"<br />\"large\"<br />\"auto_tiering\" | Shard type |\n| no_eviction | boolean | Indicates if no eviction policy is applied |\n| ops/sec | number | Consolidated ops/sec for the whole database |\n| persistence | boolean | Indicates if persistence is enabled |\n| <span class=\"break-all\">provisioned_memory</span> | number | Provisioned memory in bytes |\n| replication | boolean | Indicates if replication is enabled |\n| software_version | string | The Redis Software version |\n| used_memory | number | Used memory in bytes |\n| <span class=\"break-all\">using_redis_search</span> | boolean | Indicates if Redis Search is in use |\n| <span class=\"break-all\">master_shards_count</span> | number | Amount of primary shards |\n| license | object | License information for the cluster<br /> \"license\": {<br />    \"activation_date\": string,<br />    \"expiration_date\": string,<br />    \"ram_shards_in_use\": integer,<br />    \"ram_shards_limit\": integer,<br />    \"flash_shards_in_use\": integer,<br />    \"flash_shards_limit\": integer,<br />    \"shards_limit\": integer<br />}<br />**activation_date**: License activation date and time<br />**expiration_date**: License expiration date and time<br />**ram_shards_in_use**: Amount of RAM shards in use<br />**ram_shards_limit**: Amount of RAM shards allowed<br />**flash_shards_in_use**: Amount of flash shards in use<br />**flash_shards_limit**: Amount of flash shards allowed<br />**shards_limit**: Total shards limit |\n\n#### Example response\n\n[code example]\n\n#### Status codes {#get-status-codes}\n\n| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | No error |\n| [400 Bad Request](https://www.rfc-editor.org/rfc/rfc9110.html#name-400-bad-request) | Invalid date format |\n| [503 Service Unavailable](https://www.rfc-editor.org/rfc/rfc9110.html#name-503-service-unavailable) | Unreachable node |"}],"examples":[{"id":"get-usage-report-ex0","language":"sh","code":"GET /v1/usage_report","section_id":"get-usage-report"},{"id":"get-request-ex0","language":"sh","code":"GET /v1/usage_report","section_id":"get-request"},{"id":"get-response-ex0","language":"json","code":"{\n \"cluster_name\": \"mycluster.local\",\n \"cluster_uuid\": \"7e9f93c6-825e-4bbb-a067-7f6306b98609\",\n \"date\": \"2024-08-08T13:16:00.000000Z\",\n \"software_version\": \"7.0.0\",\n \"api_version\" \"1\",\n \"bdb_uid\": \"1\",\n \"type\": \"auto_tiering\",\n \"shard_type\": \"auto_tiering\",\n \"dominant_shard_criteria\": \"rof\",\n \"provisioned_memory\": 1073741824,\n \"used_memory\": 5828776,\n \"master_shards_count\": 3,\n \"no_eviction\": false,\n \"persistence\": false,\n \"backup\": false,\n \"using_redis_search\": false,\n \"ops_sec\": 0,\n \"replication\": false,\n \"active_active\": false,\n \"license\": {\n   \"activation_date\": \"2018-12-31T00:00:00Z\",\n   \"expiration_date\": \"2019-12-31T00:00:00Z\",\n   \"ram_shards_in_use\": 0,\n   \"ram_shards_limit\": 300,\n   \"flash_shards_in_use\": 0,\n   \"flash_shards_limit\": 100,\n   \"shards_limit\": 400\n }\n}\n{\n \"cluster_name\": \"mycluster.local\",\n \"cluster_uuid\": \"7e9f93c6-825e-4bbb-a067-7f6306b98609\",\n \"date\": \"2024-08-08T13:17:00.000000Z\",\n \"software_version\": \"7.0.0\",\n \"api_version\" \"1\",\n \"bdb_uid\": \"1\",\n \"type\": \"auto_tiering\",\n \"shard_type\": \"auto_tiering\",\n \"dominant_shard_criteria\": \"rof\",\n \"provisioned_memory\": 1073741824,\n \"used_memory\": 5828776,\n \"master_shards_count\": 3,\n \"no_eviction\": false,\n \"persistence\": false,\n \"backup\": false,\n \"using_redis_search\": false,\n \"ops_sec\": 0,\n \"replication\": false,\n \"active_active\": false,\n \"license\": {\n   \"activation_date\": \"2018-12-31T00:00:00Z\",\n   \"expiration_date\": \"2019-12-31T00:00:00Z\",\n   \"ram_shards_in_use\": 0,\n   \"ram_shards_limit\": 300,\n   \"flash_shards_in_use\": 0,\n   \"flash_shards_limit\": 100,\n   \"shards_limit\": 400\n }\n}\n...\n<MD5 hash of the entire response>","section_id":"get-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/users/authorize","title":"Authorize user requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/users/authorize/","summary":"Users authorization requests","tags":["docs","operate","rs"],"last_updated":"2025-05-16T16:04:15-05:00","page_type":"content","content_hash":"9f18acb73f59dc57d288ce26d351c4e53672d311432aff402f1896f2ab2ed6f4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [POST](#post-authorize) | `/v1/users/authorize` | Generate a token to authorize an authenticated user |"},{"id":"post-authorize","title":"Authorize user","role":"security","text":"POST /v1/users/authorize\n\nGenerates a JSON Web Token (JWT) for a user to use as authorization to access the REST API. The request authentication header must include the relevant username and password."},{"id":"post-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n    POST /v1/users/authorize\n\n#### Example JSON body\n\nThe request body is optional unless you want to specify the token's time to live:\n\n[code example]\n\n#### Request headers\n| Key    | Value            | Description         |\n|--------|------------------|---------------------|\n| Host   | cnm.cluster.fqdn | Domain name         |\n| Accept | application/json | Accepted media type |\n\n#### Request body\n\nOptionally include a JSON object in the request body to specify the time to live (`ttl`), which determines the amount of time in seconds the token will be valid. The default `ttl` is `300` seconds. The minimum `ttl` is `1` second and the maximum `ttl` is `86400` seconds."},{"id":"post-response","title":"Response","role":"returns","text":"Returns a JSON object that contains the generated access token.\n\n#### Example JSON body\n\n[code example]"},{"id":"post-error-codes","title":"Error codes","role":"errors","text":"When errors are reported, the server may return a JSON object with\n`error_code` and `message` fields that provide additional information.\nThe following are possible `error_code` values:\n\n| Code | Description |\n|------|-------------|\n| password_expired | The password has expired and must be changed. |"},{"id":"post-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | The user is authorized. |\n| [400 Bad Request](https://www.rfc-editor.org/rfc/rfc9110.html#name-400-bad-request) | The request could not be understood by the server due to malformed syntax. |\n| [401 Unauthorized](https://www.rfc-editor.org/rfc/rfc9110.html#name-401-unauthorized) | The user is unauthorized. |"}],"examples":[{"id":"post-request-ex0","language":"json","code":"{\n  \"ttl\": <time_in_seconds>\n}","section_id":"post-request"},{"id":"post-response-ex0","language":"json","code":"{\n  \"access_token\": \"eyJ5bGciOiKIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXViOjE0NjU0...\"\n}","section_id":"post-response"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/users","title":"Users requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/users/","summary":"User requests","tags":["docs","operate","rs"],"last_updated":"2026-10-05T16:11:44+01:00","children":[{"id":"operate/rs/references/rest-api/requests/users/authorize","summary":"Users authorization requests","title":"Authorize user requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/users/authorize/"},{"id":"operate/rs/references/rest-api/requests/users/password","summary":"User password requests","title":"User password requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/users/password/"},{"id":"operate/rs/references/rest-api/requests/users/refresh_jwt","summary":"Refresh JW token requests","title":"Refresh JWT requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/users/refresh_jwt/"}],"page_type":"content","content_hash":"548d1a2c3bb1b9d6b64bd66b985d563550254833642a7aae2bb66be75d25be50","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [GET](#get-all-users) | `/v1/users` | Get all users |\n| [GET](#get-user) | `/v1/users/{uid}` | Get a single user |\n| [PUT](#put-user) | `/v1/users/{uid}` | Update a user's configuration |\n| [POST](#post-user) | `/v1/users` | Create a new user |\n| [DELETE](#delete-user) | `/v1/users/{uid}` | Delete a user |"},{"id":"get-all-users","title":"Get all users","role":"content","text":"[code example]\n\nGet a list of all users."},{"id":"permissions","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [view_all_users_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_all_users_info) | admin<br />user_manager |"},{"id":"get-all-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |"},{"id":"get-all-response","title":"Response","role":"returns","text":"Returns a JSON array of [user objects](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/user).\n\n#### Example JSON body\n\n[code example]"},{"id":"get-all-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | No error |"},{"id":"get-user","title":"Get user","role":"content","text":"[code example]\n\nGet a single user's details."},{"id":"permissions-1","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [view_user_info](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#view_user_info) | admin<br />user_manager |"},{"id":"get-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The user's unique ID |"},{"id":"get-response","title":"Response","role":"returns","text":"Returns a [user object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/user) that contains the details for the specified user ID.\n\n#### Example JSON body\n\n[code example]"},{"id":"get-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success. |\n| [403 Forbidden](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.4) | Operation is forbidden. |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | User does not exist. |"},{"id":"put-user","title":"Update user","role":"content","text":"[code example]\n\nUpdate an existing user's configuration."},{"id":"permissions-2","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [update_user](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#update_user) | admin<br />user_manager |\n\nAny user can change their own name, password, or alert preferences."},{"id":"put-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Example JSON body\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### Query parameters\n\n| Field   | Type | Description |\n|---------|------|---------------|\n| dry_run |  | Validate the updated [user object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/user) but don't apply the update. |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The user's unique ID |\n\n\n#### Request body\n\nInclude a [user object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/user) with updated fields in the request body."},{"id":"put-response","title":"Response","role":"returns","text":"Returns the updated [user object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/user).\n\n#### Example JSON body\n\n[code example]\n\n> [!NOTE]\n> For [RBAC-enabled clusters](https://redis.io/docs/latest/operate/rs/security/access-control), the returned user details include `role_uids` instead of `role`."},{"id":"put-error-codes","title":"Error codes","role":"errors","text":"When errors are reported, the server may return a JSON object with    `error_code` and `message` field that provide additional information.    The following are possible `error_code` values:\n\n| Code | Description |\n|------|-------------|\n| password_not_complex | The given password is not complex enough (Only works when the password_complexity feature is enabled).|\n| new_password_same_as_current | The given new password is identical to the old password.|\n| email_already_exists | The given email is already taken.|\n| change_last_admin_role_not_allowed | At least one user with admin role should exist.|"},{"id":"put-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success, the user is updated. |\n| [400 Bad Request](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1) | Bad or missing configuration parameters. |\n| [404 Not Found](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5) | Attempting to change a non-existing user. |\n| [406 Not Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | The requested configuration is invalid. |"},{"id":"post-user","title":"Create user","role":"content","text":"[code example]\n\nCreate a new user."},{"id":"permissions-3","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [create_new_user](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#create_new_user) | admin<br />user_manager |"},{"id":"post-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### Query parameters\n\n| Field   | Type | Description |\n|---------|------|---------------|\n| dry_run |  | Validate the new [user object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/user) but don't apply the update. |\n\n#### Body\n\nInclude a single [user object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/user) in the request body. The user object must have an email, password, and role.\n\n> [!NOTE]\n> For [RBAC-enabled clusters](https://redis.io/docs/latest/operate/rs/security/access-control), use `role_uids` instead of `role` in the request body.\n\n`email_alerts` can be configured either as:\n\n- `true` - user will receive alerts for all databases configured in `bdbs_email_alerts`. The user will receive alerts for all databases by default if `bdbs_email_alerts` is not configured. `bdbs_email_alerts` can be a list of database UIDs or `[‘all’]` meaning all databases.\n\n- `false` - user will not receive alerts for any databases\n\n##### Example JSON body\n\n[code example]"},{"id":"post-response","title":"Response","role":"returns","text":"Returns the newly created [user object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/user).\n\n#### Example JSON body\n\n[code example]"},{"id":"post-error-codes","title":"Error codes","role":"errors","text":"When errors are reported, the server may return a JSON object with `error_code` and `message` field that provide additional information.\n\nThe following are possible `error_code` values:\n\n| Code | Description |\n|------|-------------|\n| password_not_complex | The given password is not complex enough (Only works when the password_complexity feature is enabled).|\n| email_already_exists | The given email is already taken.|\n| name_already_exists | The given name is already taken.|"},{"id":"post-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success, user is created. |\n| [400 Bad Request](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1) | Bad or missing configuration parameters. |\n| [409 Conflict](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.10) | User with the same email already exists. |"},{"id":"examples","title":"Examples","role":"example","text":"#### cURL\n\n[code example]\n\n#### Python\n\n[code example]"},{"id":"delete-user","title":"Delete user","role":"content","text":"[code example]\n\nDelete a user."},{"id":"permissions-4","title":"Permissions","role":"security","text":"| Permission name | Roles |\n|-----------------|-------|\n| [delete_user](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#delete_user) | admin<br />user_manager |"},{"id":"delete-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n[code example]\n\n#### Headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Accept | application/json | Accepted media type |\n\n#### URL parameters\n\n| Field | Type | Description |\n|-------|------|-------------|\n| uid | integer | The user's unique ID |"},{"id":"delete-response","title":"Response","role":"returns","text":"Returns a status code to indicate the success or failure of the user deletion."},{"id":"delete-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | Success, the user is deleted. |\n| [406 Not Acceptable](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7) | The request is not acceptable. |"}],"examples":[{"id":"get-all-users-ex0","language":"sh","code":"GET /v1/users","section_id":"get-all-users"},{"id":"get-all-request-ex0","language":"sh","code":"GET /v1/users","section_id":"get-all-request"},{"id":"get-all-response-ex0","language":"json","code":"[\n    {\n        \"uid\": 1,\n        \"password_issue_date\": \"2017-03-02T09:43:34Z\",\n        \"email\": \"user@example.com\",\n        \"name\": \"John Doe\",\n        \"email_alerts\": true,\n        \"bdbs_email_alerts\": [\"1\",\"2\"],\n        \"role\": \"admin\",\n        \"auth_method\": \"regular\",\n        \"status\": \"active\"\n    },\n    {\n        \"uid\": 2,\n        \"password_issue_date\": \"2017-03-02T09:43:34Z\",\n        \"email\": \"user2@example.com\",\n        \"name\": \"Jane Poe\",\n        \"email_alerts\": true,\n        \"role\": \"db_viewer\",\n        \"auth_method\": \"regular\",\n        \"status\": \"active\"\n    }\n]","section_id":"get-all-response"},{"id":"get-user-ex0","language":"sh","code":"GET /v1/users/{int: uid}","section_id":"get-user"},{"id":"get-request-ex0","language":"sh","code":"GET /v1/users/1","section_id":"get-request"},{"id":"get-response-ex0","language":"json","code":"{\n    \"uid\": 1,\n    \"password_issue_date\": \"2017-03-07T15:11:08Z\",\n    \"role\": \"db_viewer\",\n    \"email_alerts\": true,\n    \"bdbs_email_alerts\": [\"1\",\"2\"],\n    \"email\": \"user@example.com\",\n    \"name\": \"John Doe\",\n    \"auth_method\": \"regular\",\n    \"status\": \"active\"\n}","section_id":"get-response"},{"id":"put-user-ex0","language":"sh","code":"PUT /v1/users/{int: uid}","section_id":"put-user"},{"id":"put-request-ex0","language":"sh","code":"PUT /v1/users/1","section_id":"put-request"},{"id":"put-request-ex1","language":"json","code":"{\n     \"email_alerts\": false,\n     \"role_uids\": [ 2, 4 ]\n}","section_id":"put-request"},{"id":"put-response-ex0","language":"json","code":"{\n     \"uid\": 1,\n     \"password_issue_date\": \"2017-03-07T15:11:08Z\",\n     \"email\": \"user@example.com\",\n     \"name\": \"Jane Poe\",\n     \"email_alerts\": false,\n     \"role\": \"db_viewer\",\n     \"role_uids\": [ 2, 4 ],\n     \"auth_method\": \"regular\"\n}","section_id":"put-response"},{"id":"post-user-ex0","language":"sh","code":"POST /v1/users","section_id":"post-user"},{"id":"post-request-ex0","language":"sh","code":"POST /v1/users","section_id":"post-request"},{"id":"post-request-ex1","language":"json","code":"{\n     \"email\": \"newuser@example.com\",\n     \"password\": \"my-password\",\n     \"name\": \"Pat Doe\",\n     \"email_alerts\": true,\n     \"bdbs_email_alerts\": [\"1\",\"2\"],\n     \"role_uids\": [ 3, 4 ],\n     \"auth_method\": \"regular\"\n}","section_id":"post-request"},{"id":"post-response-ex0","language":"json","code":"{\n     \"uid\": 1,\n     \"password_issue_date\": \"2017-03-07T15:11:08Z\",\n     \"email\": \"newuser@example.com\",\n     \"name\": \"Pat Doe\",\n     \"email_alerts\": true,\n     \"bdbs_email_alerts\": [\"1\",\"2\"],\n     \"role\": \"db_viewer\",\n     \"role_uids\": [ 3, 4 ],\n     \"auth_method\": \"regular\"\n}","section_id":"post-response"},{"id":"examples-ex0","language":"sh","code":"$ curl -k -X POST -u '[username]:[password]' \\\n       -H 'Content-Type: application/json' \\\n       -d '{ \"email\": \"newuser@example.com\", \\\n           \"password\": \"my-password\", \\\n           \"name\": \"Pat Doe\", \\\n           \"email_alerts\": true, \\\n           \"bdbs_email_alerts\": [\"1\",\"2\"], \\\n           \"role_uids\": [ 3, 4 ], \\\n           \"auth_method\": \"regular\" }' \\\n       'https://[host][:port]/v1/users'","section_id":"examples"},{"id":"examples-ex1","language":"python","code":"import requests\nimport json\n\nurl = \"https://[host][:port]/v1/users\"\nauth = (\"[username]\", \"[password]\")\n\npayload = json.dumps({\n  \"email\": \"newuser@example.com\",\n  \"password\": \"my-password\",\n  \"name\": \"Pat Doe\",\n  \"email_alerts\": True,\n  \"bdbs_email_alerts\": [\n    \"1\",\n    \"2\"\n  ],\n  \"role_uids\": [\n    3,\n    4\n  ],\n  \"auth_method\": \"regular\"\n})\n\nheaders = {\n  'Content-Type': 'application/json'\n}\n\nresponse = requests.request(\"POST\", url, auth=auth, headers=headers, data=payload, verify=False)\n\nprint(response.text)","section_id":"examples"},{"id":"delete-user-ex0","language":"sh","code":"DELETE /v1/users/{int: uid}","section_id":"delete-user"},{"id":"delete-request-ex0","language":"sh","code":"DELETE /v1/users/1","section_id":"delete-request"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/users/password","title":"User password requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/users/password/","summary":"User password requests","tags":["docs","operate","rs"],"last_updated":"2025-06-30T12:38:51-05:00","page_type":"content","content_hash":"484e8b62e6cc68c49d5efcc7b71ca2d4b7a021d393a3e340142968b63d567de1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method                     | Path                 | Description                 |\n|----------------------------|----------------------|-----------------------------|\n| [PUT](#update-password)    | `/v1/users/password` | Replace passwords |\n| [POST](#add-password)      | `/v1/users/password` | Add a new password          |\n| [DELETE](#delete-password) | `/v1/users/password` | Delete a password           |"},{"id":"update-password","title":"Update password","role":"content","text":"PUT /v1/users/password\n    \nReplaces the password list of the specified user with a single new password. If a `username` is not provided in the JSON request body, it replaces the password list of the authenticated user making this request instead."},{"id":"put-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n    PUT /v1/users/password\n\n#### Example JSON body\n\n  [code example]\n\n#### Request headers\n| Key    | Value            | Description         |\n|--------|------------------|---------------------|\n| Host   | cnm.cluster.fqdn | Domain name         |\n| Accept | application/json | Accepted media type |\n\n#### Request body\n\nThe request must contain a JSON object with the following fields:\n\n| Field | Type | Description |\n|-------|------|-------------|\n| username     | string | (Optional) The username of the affected user. If missing, defaults to the authenticated user. |\n| new_password | string | The new password (required) |"},{"id":"put-response","title":"Response","role":"returns","text":"Returns a status code to indicate password update success or failure."},{"id":"put-error-codes","title":"Error codes","role":"errors","text":"When errors are reported, the server may return a JSON object with\n`error_code` and `message` fields that provide additional information.\nThe following are possible `error_code` values:\n\n| Code | Description |\n|------|-------------|\n| password_not_complex | The given password is not complex enough (Only work when the password_complexity feature is enabled). |\n| new_password_same_as_current | The given new password is identical to one of the already existing passwords. |\n| user_not_exist | User does not exist. |\n| unauthorized_action | Updating another user's password is acceptable by an admin user only. |"},{"id":"put-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | Success, password changed. |\n| [400 Bad Request](https://www.rfc-editor.org/rfc/rfc9110.html#name-400-bad-request) | Bad or missing parameters. |\n| [401 Unauthorized](https://www.rfc-editor.org/rfc/rfc9110.html#name-401-unauthorized) | The user is unauthorized. |\n| [403 Forbidden](https://www.rfc-editor.org/rfc/rfc9110.html#name-403-forbidden) | Insufficient privileges. |\n| [404 Not Found](https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found) | User not found. |"},{"id":"add-password","title":"Add password","role":"content","text":"POST /v1/users/password\n\nAdds a new password to the specified user's password list. If a `username` is not provided in the JSON request body, it adds the password to the password list of the authenticated user making this request instead."},{"id":"post-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n    POST /v1/users/password\n\n#### Example JSON body\n\n  [code example]\n\n#### Request headers\n| Key    | Value            | Description         |\n|--------|------------------|---------------------|\n| Host   | cnm.cluster.fqdn | Domain name         |\n| Accept | application/json | Accepted media type |\n\n#### Request body\n\nThe request must contain a JSON object with the following fields:\n\n| Field | Type | Description |\n|-------|------|-------------|\n| username     | string | (Optional) The username of the affected user. If missing, defaults to the authenticated user. |\n| new_password | string | New password to add (required) |"},{"id":"post-response","title":"Response","role":"returns","text":"Returns a status code to indicate password creation success or failure. If an error occurs, the response body may include a more specific error code and message."},{"id":"post-error-codes","title":"Error codes","role":"errors","text":"When errors are reported, the server may return a JSON object with\n`error_code` and `message` fields that provide additional information.\nThe following are possible `error_code` values:\n\n| Code | Description |\n|------|-------------|\n| password_not_complex | The given password is not complex enough (Only work when the password_complexity feature is enabled). |\n| new_password_same_as_current | The given new password is identical to one of the already existing passwords. |\n| user_not_exist | User does not exist. |\n| unauthorized_action | Updating another user's password is acceptable by an admin user only. |"},{"id":"post-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | Success, new password was added to the list of valid passwords. |\n| [400 Bad Request](https://www.rfc-editor.org/rfc/rfc9110.html#name-400-bad-request) | Bad or missing parameters. |\n| [401 Unauthorized](https://www.rfc-editor.org/rfc/rfc9110.html#name-401-unauthorized) | The user is unauthorized. |\n| [403 Forbidden](https://www.rfc-editor.org/rfc/rfc9110.html#name-403-forbidden) | Insufficient privileges. |\n| [404 Not Found](https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found) | User not found. |"},{"id":"delete-password","title":"Delete password","role":"content","text":"DELETE /v1/users/password\n\nDeletes a password from the specified user's password list. If a `username` is not provided in the JSON request body, it deletes the password from the password list of the authenticated user making this request instead."},{"id":"delete-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n    DELETE /v1/users/password\n\n#### Example JSON body\n\n  [code example]\n\n#### Request headers\n| Key    | Value            | Description         |\n|--------|------------------|---------------------|\n| Host   | cnm.cluster.fqdn | Domain name         |\n| Accept | application/json | Accepted media type |\n\n#### Request body\n\nThe request must contain a JSON object with the following fields:\n\n| Field | Type | Description |\n|-------|------|-------------|\n| username | string | (Optional) The username of the affected user. If missing, defaults to the authenticated user. |\n| old_password | string | Existing password to be deleted (required) |"},{"id":"delete-response","title":"Response","role":"returns","text":""},{"id":"delete-error-codes","title":"Error codes","role":"errors","text":"When errors are reported, the server may return a JSON object with\n`error_code` and `message` fields that provide additional information.\nThe following are possible `error_code` values:\n\n| Code | Description |\n|------|-------------|\n| cannot_delete_last_password | Cannot delete the last password of a user. |\n| user_not_exist | User does not exist. |\n| unauthorized_action | Updating another user's password is acceptable by an admin user only. |"},{"id":"delete-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](https://www.rfc-editor.org/rfc/rfc9110.html#name-200-ok) | Success, new password was deleted from the list of valid passwords. |\n| [400 Bad Request](https://www.rfc-editor.org/rfc/rfc9110.html#name-400-bad-request) | Bad or missing parameters. |\n| [401 Unauthorized](https://www.rfc-editor.org/rfc/rfc9110.html#name-401-unauthorized) | The user is unauthorized. |\n| [403 Forbidden](https://www.rfc-editor.org/rfc/rfc9110.html#name-403-forbidden) | Insufficient privileges. |\n| [404 Not Found](https://www.rfc-editor.org/rfc/rfc9110.html#name-404-not-found) | User not found. |"}],"examples":[{"id":"put-request-ex0","language":"json","code":"{\n      \"username\": \"The username of the affected user. If missing, default to the authenticated user.\",\n      \"new_password\": \"the new (single) password\"\n  }","section_id":"put-request"},{"id":"post-request-ex0","language":"json","code":"{\n      \"username\": \"The username of the affected user. If missing, default to the authenticated user.\",\n      \"new_password\": \"a password to add\"\n  }","section_id":"post-request"},{"id":"delete-request-ex0","language":"json","code":"{\n      \"username\": \"The username of the affected user. If missing, default to the authenticated user.\",\n      \"old_password\": \"an existing password to delete\"\n  }","section_id":"delete-request"}]}
{"schema_version":2,"id":"operate/rs/references/rest-api/requests/users/refresh_jwt","title":"Refresh JWT requests","url":"https://redis.io/docs/latest/operate/rs/references/rest-api/requests/users/refresh_jwt/","summary":"Refresh JW token requests","tags":["docs","operate","rs"],"last_updated":"2025-03-10T13:08:54-04:00","page_type":"content","content_hash":"a2fdfe7d4bebfe99c183ca9adced0262e7afa1388e3c5a45b2615c3f4d2bf81a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"| Method | Path | Description |\n|--------|------|-------------|\n| [POST](#post-refresh_jwt) | `/v1/users/refresh_jwt` | Get a new authentication token |"},{"id":"post-refresh_jwt","title":"Get a new authentication token","role":"content","text":"POST /v1/users/refresh_jwt\n\nGenerate a new JSON Web Token (JWT) for authentication.\n\nTakes a valid token and returns the new token generated by the request."},{"id":"post-request","title":"Request","role":"content","text":"#### Example HTTP request\n\n\tPOST /v1/users/refresh_jwt \n\n#### Request headers\n\n| Key | Value | Description |\n|-----|-------|-------------|\n| Host | cnm.cluster.fqdn | Domain name |\n| Authorization | JWT eyJ5bGciOiKIUzI1NiIsInR5cCI6IkpXVCJ9.<br></br>eyJpYXViOjE0NjU0NzU0ODYsInVpZFI1IjEiLCJ<br></br>leHAiOjE0NjU0Nz30OTZ9.2xYXumd1rDoE0e<br></br>dFzcLElMOHsshaqQk2HUNgdsUKxMU | Valid JSON Web Token (JWT) |\n\n#### Request body\n\n| Field | Type | Description |\n|-------|------|-------------|\n| ttl | integer | Time to live - The amount of time in seconds the token will be valid (optional) |"},{"id":"post-response","title":"Response","role":"returns","text":"Returns a JSON object that contains the generated access token.\n\n#### Example JSON body\n\n[code example]"},{"id":"post-status-codes","title":"Status codes","role":"content","text":"| Code | Description |\n|------|-------------|\n| [200 OK](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1) | A new token is given. |\n| [401 Unauthorized](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2) | The token is invalid or password has expired. |"}],"examples":[{"id":"post-response-ex0","language":"json","code":"{\n     \"access_token\": \"eyJ5bGciOiKIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXViOjE0NjU0NzU0ODYsInVpZFI1IjEiLCJleHAiOjE0NjU0Nz30OTZ9.2xYXumd1rDoE0edFzcLElMOHsshaqQk2HUNgdsUKxMU\"\n }","section_id":"post-response"}]}
{"schema_version":2,"id":"operate/rs/references/supported-platforms","title":"Supported platforms","url":"https://redis.io/docs/latest/operate/rs/references/supported-platforms/","summary":"Redis Software is supported on several operating systems, cloud environments, and virtual environments.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","page_type":"content","content_hash":"533240caf11aa8c88c32109fb129d2f09244c4b1e536108627f75fb7b48449bb","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software is supported on several operating systems, cloud environments, and virtual environments."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n<span title=\"Dash icon\">–</span> Not supported – The platform is not supported for this version of Redis Software.\n\n| Redis Software<br />major versions | 8.2 | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Jul 2026 | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Jul 2028 | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2023<sup>[6](#table-note-6)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – | – | – |\n| Amazon Linux 2 | – | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance.\n\n6. <a name=\"table-note-6\"></a>Amazon Linux 2023 support was added in Redis Software version 8.0.20.\n\nThe following table shows which Redis Software version first tested and added support for each RHEL version:\n\n| RHEL version | Redis Software version |\n|--------------|--------------------------|\n| 8.4 | 6.2.8 |\n| 8.5 | 6.2.10 |\n| 8.6 | 6.2.10 |\n| 8.7 | 6.4.2 |\n| 8.8 | 6.4.2 |\n| 8.9 | 7.2.4 |\n| 9.3 | 7.4.2 |\n| 9.5 | 7.8.2 |\n| 9.6 | 7.22.0 |"},{"id":"operating-system-compatibility-policy","title":"Operating system compatibility policy","role":"content","text":"Redis maintains a list of [supported operating systems](#supported-platforms) for each major version of Redis Software and the specific OS versions tested with Redis Software releases. Because the list is updated as new OS versions are introduced and old ones become obsolete, we encourage you to check the list and plan upgrades accordingly. We also suggest you keep Redis Software and corresponding supported OS versions up to date.\n\nWe thoroughly test the most recent minor version of each supported major OS to ensure the best compatibility and performance with every Redis Software release. This process helps detect and address potential compatibility issues early on.\n\nDue to the vast array of minor updates and variations across operating systems, we cannot test compatibility with every minor OS version and each Redis Software version. However, because OS vendors each have an Application Binary Interface (ABI) they support and avoid breaking, except to address severe security issues, newer minor OS versions are generally expected to work correctly. We will add a note to this document if specific OS minor versions have significant ABI or dependency changes. An earlier OS minor version also might work, although it is not guaranteed.\n\nDespite our rigorous testing, we recommend users test their Redis applications with any new OS update before deploying it in a production environment. This additional testing layer can help identify any unique issues in your setup."},{"id":"red-hat-enterprise-linux-rhel","title":"Red Hat Enterprise Linux (RHEL)","role":"content","text":"Red Hat has a well-defined lifecycle for support. For details, see [Red Hat Enterprise Linux Life Cycle](https://access.redhat.com/support/policy/updates/errata#RHEL8_and_9_Life_Cycle).\n\nRedis supports and tests RHEL 8 and 9 minor releases and extended update support timeframes. However, none of the longer Red Hat support cycles, such as SAP and EEUS, are supported.\n\nWe only support what the vendor supports in accordance with their policies. When Red Hat no longer supports a particular release, it is no longer supported by Redis as well. If future Redis Software releases will not support a major RHEL version, the release notes and the [supported platforms](#supported-platforms) table will include deprecation warnings."},{"id":"rhel-clones-and-equivalent-enterprise-os","title":"RHEL clones and equivalent Enterprise OS","role":"content","text":"The [supported platforms](#supported-platforms) table lists the versions of Red Hat Enterprise Linux (RHEL) that Redis supports. This support extends to the ABI and package compatibility with RHEL of the same version.\n\nThe RHEL-compatible distributions CentOS, CentOS Stream, Alma Linux, Rocky Linux, and Oracle Linux running the Red Hat Compatible Kernel (RHCK) are supported if they provide full RHEL compatibility. If Redis identifies or questions some incompatibility from a clone, you might be asked to test on another clone or RHEL directly."},{"id":"operating-system-limitations","title":"Operating system limitations","role":"content","text":""},{"id":"tls-10-and-tls-11","title":"TLS 1.0 and TLS 1.1","role":"content","text":"Redis Software version 6.2.8 removed support for TLS 1.0 and TLS 1.1 on Red Hat Enterprise Linux 8 (RHEL 8) because that operating system [does not enable support](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/security_hardening/using-the-system-wide-cryptographic-policies_security-hardening) for these versions by default."},{"id":"rhel-8-crypto-policy-and-certificate-key-size","title":"RHEL 8 crypto policy and certificate key size","role":"content","text":"In RHEL 8, if the crypto policy is set to `FUTURE`, the system will not accept certificates with private key sizes smaller than 3072 bits. This affects use of custom certificates with smaller keys (such as 2048-bit keys).\n\nTo use certificates with smaller key sizes, you need to change the crypto policy from `FUTURE` to `DEFAULT`. For more information about crypto policies, see the [Red Hat documentation on system-wide cryptographic policies](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/security_hardening/using-the-system-wide-cryptographic-policies_security-hardening)."},{"id":"ubuntu-20-rejects-sha1-certificates","title":"Ubuntu 20 rejects SHA1 certificates","role":"content","text":"With Ubuntu 20.04, you cannot use the SHA1 hash algorithm because [OpenSSL's security level is set to 2 by default](https://manpages.ubuntu.com/manpages/focal/man3/SSL_CTX_set_security_level.3ssl.html#notes). As a result, the operating system rejects SHA1 certificates, even if you enable the `mtls_allow_weak_hashing` option.\n\nTo avoid issues with SHA1 certificates, replace them with new certificates that use SHA-256. Note that certificates provided by Redis Software use SHA-256."},{"id":"upgrade-rhel-when-using-modules","title":"Upgrade RHEL when using modules","role":"content","text":"RHEL 7 clusters cannot be directly upgraded to RHEL 8 when hosting databases using modules, due to binary differences in modules between the two operating systems. Instead, you need to create a new cluster on RHEL 8 and then migrate existing data from your RHEL 7 cluster. This does not apply to clusters that do not use modules.\n\nThis limitation is fixed for clusters using Redis Software version 7.2.4 and later."},{"id":"modules-cannot-load-in-oracle-linux-7--8","title":"Modules cannot load in Oracle Linux 7 & 8","role":"content","text":"Databases hosted on Oracle Linux 7 & 8 cannot load modules.\n\nAs a temporary workaround, you can change the node's `os_name` in the Cluster Configuration Store (CCS):\n\n[code example]\n\nThis limitation was fixed in [Redis Software version 7.2.4-64](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-64)."},{"id":"openssl-compatibility-issue-for-742-modules-on-amazon-linux-2","title":"OpenSSL compatibility issue for 7.4.2 modules on Amazon Linux 2","role":"content","text":"Due to an OpenSSL 1.1 compatibility issue between modules and clusters, Redis Software version 7.4.2-54 is not fully supported on Amazon Linux 2 clusters with databases that use the following modules: RedisGears, RediSearch, or RedisTimeSeries.\n\nThis issue will be fixed in a future maintenance release."},{"id":"redisgraph-prevents-upgrade-to-rhel-9","title":"RedisGraph prevents upgrade to RHEL 9","role":"content","text":"You cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9."},{"id":"virtualization-platforms","title":"Virtualization platforms","role":"content","text":"Redis Software is compatible with VMware and other similar virtualization platforms. Make sure to do the following:\n\n- Configure your memory, CPU, network, and storage settings to allow for optimal Redis Software performance.\n- Pin each Redis Software shard to a specific ESX or ESXi host by setting the appropriate affinity rules.\n- If you must manually migrate a virtual machine to another host, follow the best practices for [shard maintenance](https://redis.io/docs/latest/operate/rs/clusters/maintenance-mode) and contact support if you have questions.\n- Turn off VMware VMotion because Redis Software is not compatible with VMotion.\n- Don't use snapshots because Redis Software cluster manages states dynamically, so a snapshot might not have the correct node and cluster states."}],"examples":[{"id":"modules-cannot-load-in-oracle-linux-7--8-ex0","language":"sh","code":"ccs-cli hset node:<ID> os_name rhel","section_id":"modules-cannot-load-in-oracle-linux-7--8"}]}
{"schema_version":2,"id":"operate/rs/references/terminology","title":"Terminology in Redis Software","url":"https://redis.io/docs/latest/operate/rs/references/terminology/","summary":"Explains terms used in Redis Software and its docs.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:21+01:00","page_type":"content","content_hash":"22ac8500f9076554e2ef9fd337ec2928811c78d93d07ac256f4c04404c18aa6e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Here are explanations of some of the terms used in Redis Software."},{"id":"node","title":"Node","role":"content","text":"A _node_ is a physical machine, virtual machine, container or cloud\ninstance on which the RS installation package was installed and the\n[setup process](https://redis.io/docs/latest/operate/rs/clusters/new-cluster-setup) was run in order to make the machine part of the cluster.\n\nEach node is a container for running multiple Redis\ninstances, referred to as \"shards\".\n\nThe recommended configuration for a production cluster is an uneven\nnumber of nodes, with a minimum of three. Note that in some\nconfigurations, certain functionalities might be blocked. For example,\nif a cluster has only one node you cannot enable database replication,\nwhich helps to achieve high availability.\n\nA node is made up of several components, as detailed below, and works\ntogether with the other cluster nodes. You can [add nodes](https://redis.io/docs/latest/operate/rs/clusters/add-node), [remove nodes](https://redis.io/docs/latest/operate/rs/clusters/remove-node), or [replace nodes](https://redis.io/docs/latest/operate/rs/clusters/replace-node) as needed."},{"id":"redis-instance-shard","title":"Redis instance (shard)","role":"content","text":"As indicated above, each node serves as a container for hosting multiple\ndatabase instances, referred to as \"shards\".\n\nRedis Software supports various database configurations:\n\n- **Standard Redis database** - A single Redis shard with no\n    replication or clustering.\n- [**Highly available Redis database**](https://redis.io/docs/latest/operate/rs/databases/durability-ha/replication) - Every database master shard\n    has a replica shard, so that if the master shard fails the\n    cluster can automatically fail over to the replica with minimal impact. Master and replica shards are always placed on separate\n    nodes to ensure high availability.\n- [**Clustered Redis database**](https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering) - The data stored in the database is\n    split across several shards. The number of shards can be defined by\n    the user. Various performance optimization algorithms define where\n    shards are placed within the cluster. During the lifetime of the\n    cluster, these algorithms might [migrate a shard](https://redis.io/docs/latest/operate/rs/databases/migrate-shards) between nodes.\n- **Clustered and highly available Redis database** - Each master shard\n    in the clustered database has a replica shard, enabling failover if\n    the master shard fails.\n\nYou can configure [shard placement](https://redis.io/docs/latest/operate/rs/databases/configure/shard-placement) to optimize performance and availability."},{"id":"proxy","title":"Proxy","role":"content","text":"Each node includes one zero-latency, multi-threaded proxy\n(written in low-level C) that masks the underlying system complexity. The\nproxy oversees forwarding Redis operations to the database shards on\nbehalf of a Redis client.\n\nThe proxy simplifies the cluster operation, from the application or\nRedis client point of view, by enabling the use of a standard Redis\nclient. The zero-latency proxy is built over a cut-through architecture\nand employs various optimization methods. For example, to help ensure\nhigh-throughput and low-latency performance, the proxy might use\ninstruction pipelining even if not instructed to do so by the client.\n\nYou can configure [proxy policies](https://redis.io/docs/latest/operate/rs/databases/configure/proxy-policy) to determine which nodes' proxies are active and bound to databases."},{"id":"database-endpoint","title":"Database endpoint","role":"content","text":"Each database is served by a [database endpoint](https://redis.io/docs/latest/operate/rs/databases/connect/test-client-connectivity) that is part of and\nmanaged by the proxies. The endpoint oversees forwarding Redis\noperations to specific database shards.\n\nIf the master shard fails and the replica shard is promoted to master, the\nmaster endpoint is updated to point to the new master shard.\n\nIf the master endpoint fails, the replica endpoint is promoted to be the\nnew master endpoint and is updated to point to the master shard.\n\nSimilarly, if both the master shard and the master endpoint fail, then\nboth the replica shard and the replica endpoint are promoted to be the new\nmaster shard and master endpoint.\n\nShards and their endpoints do not\nhave to reside within the same node in the cluster.\n\nIn the case of a clustered database with multiple database shards, only\none master endpoint acts as the master endpoint for all master shards,\nforwarding Redis operations to all shards as needed.\n\nFor high availability, you can configure [private and public endpoints](https://redis.io/docs/latest/operate/rs/networking/private-public-endpoints) for your databases."},{"id":"cluster-manager","title":"Cluster manager","role":"content","text":"The cluster manager oversees all node management-related tasks, and the\ncluster manager in the master node looks after all the cluster related\ntasks.\n\nThe cluster manager is designed in a way that is totally decoupled from\nthe Redis operation. This enables RS to react in a much faster and\naccurate manner to failure events, so that, for example, a node failure\nevent triggers mass failover operations of all the master endpoints\nand master shards that are hosted on the failed node.\n\nIn addition, this architecture guarantees that each Redis shard is only\ndealing with processing Redis commands in a shared-nothing architecture,\nthus maintaining the inherent high-throughput and low-latency of each\nRedis process. Lastly, this architecture guarantees that any change in\nthe cluster manager itself does not affect the Redis operation.\n\nSome of the primary functionalities of the cluster manager include:\n\n- Deciding where shards are created\n- Deciding when shards are migrated and to where\n- Monitoring database size\n- Monitoring databases and endpoints across all nodes\n- Running the database resharding process\n- Running the database provisioning and de-provisioning processes\n- Gathering [operational statistics](https://redis.io/docs/latest/operate/rs/monitoring)\n- Enforcing [license and subscription limitations](https://redis.io/docs/latest/operate/rs/clusters/configure/license-keys)\n\nYou can manage your Redis Software clusters using the [Cluster Manager UI](https://redis.io/docs/latest/operate/rs/clusters/configure), [command-line tools](https://redis.io/docs/latest/operate/rs/references/cli-utilities), or the [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api)."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/references/upgrade-paths","title":"Supported upgrade paths for Redis Software","url":"https://redis.io/docs/latest/operate/rs/references/upgrade-paths/","summary":"Supported paths to upgrade a Redis Software cluster.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:21+01:00","page_type":"content","content_hash":"a4a0b91170082952ee8ba7697c09516dc854951d968ca6d2c09036095cfeb06b","sections":[{"id":"content","title":"Content","role":"content","text":"<span title=\"Check mark icon\">&#x2705;</span> Supported – You can upgrade directly from the current Redis Software cluster version.\n\n<span title=\"X icon\">:x:</span> Not supported – You cannot upgrade directly from the current Redis Software cluster version. You must first upgrade to a supported intermediate version.\n\n| Current Redis Software cluster version | Upgrade to Redis Software 7.2.x | Upgrade to Redis Software 7.4.x |  Upgrade to Redis Software 7.8.x | Upgrade to Redis Software 7.22.x | Upgrade to Redis Software 8.0.2-8.0.10 | Upgrade to Redis Software 8.0.16-8.0.20 | Upgrade to Redis Software 8.2.x |\n|:-----------------------:|:----------------:|:----------------:|:----------------:|:----------------:|:----------------:|:----------------:|:----------------:|\n| 6.0.x | <span title=\"Supported\">&#x2705;</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> |\n| 6.2.4<br />6.2.8 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> |\n| 6.2.10<br />6.2.12<br />6.2.18 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> |\n| 6.4.x | <span title=\"Supported\">&#x2705;</span> |  <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Not supported\">:x:</span> | <span title=\"Not supported\">:x:</span> |\n| 7.2.x | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 7.4.x | – | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 7.8.x | – | – | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 7.22.x | – | – | – | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 8.0.2<br />8.0.6<br />8.0.10 | – | – | – | – | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 8.0.16<br />8.0.18<br />8.0.20 | – | – | – | – | - | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 8.2.x | – | – | – | – | – | – | <span title=\"Supported\">&#x2705;</span> |\n\n\nFor detailed upgrade instructions, see [Upgrade a Redis Software cluster](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-cluster).\n\nSee the [Redis Software product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle) for more information about release numbers and the end-of-life schedule.\n\n> [!NOTE]\n> Redis Enterprise for Kubernetes has its own support lifecycle, which accounts for the Kubernetes distribution lifecycle. For details, see [Supported Kubernetes distributions](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions)."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes","title":"Release notes","url":"https://redis.io/docs/latest/operate/rs/release-notes/","summary":"","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","children":[{"id":"operate/rs/release-notes/rs-8-2-releases","summary":"Database-scoped management roles. Data path command auditing.","title":"Redis Software release notes 8.2.x","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-2-releases/"},{"id":"operate/rs/release-notes/rs-8-0-releases","summary":"Redis Software 8! The most performant, most secure, and richest version so far. Built for performance, scale, and reliability to power modern ML and AI applications.","title":"Redis Software release notes 8.0.x","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/"},{"id":"operate/rs/release-notes/rs-7-22-releases","summary":"Diagnostic logging service. Call home client to send daily usage statistics to Redis. Usage reports in support packages. Revamp database API. Migration status API. Two-dimensional rack awareness. New version for actions API. Additional REST API enhancements.","title":"Redis Software release notes 7.22.x","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/"},{"id":"operate/rs/release-notes/rs-7-8-releases","summary":"Redis Community Edition 7.4 features. Hash field expiration. Client-side caching support. Metrics stream engine preview. New APIs to check database availability, rebalance shards, fail over shards, and control database traffic. Cluster Manager UI enhancements for node actions, database tags, and database configuration. User manager role. Log rotation based on both size and time. Module management enhancements. Configurable minimum password length. Configurable license expiration alert threshold.","title":"Redis Software release notes 7.8.x","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/"},{"id":"operate/rs/release-notes/rs-7-4-2-releases","summary":"New Cluster Manager UI enhancements, including Active-Active database management. Full TLS 1.3 support. Automatic recovery configuration. Full IPv6 support, including for internal traffic. Maintenance mode enhancements. Module management enhancements. RHEL 9 support.","title":"Redis Enterprise Software release notes 7.4.x","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/"},{"id":"operate/rs/release-notes/rs-7-2-4-releases","summary":"Redis 7.0 and 7.2 features. Auto Tiering (enhanced successor to Redis on Flash). RESP3 support. Sharded pub/sub. Preview of the new Cluster Manager UI. Redis Stack 7.2 features. Three Redis database versions. License file structure updates. Redis ACL selectors and enhanced key-based permissions. New INFO fields. Log rotation enhancements. Multi-OS upgrade support for clusters with modules.","title":"Redis Enterprise Software release notes 7.2.4","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/"},{"id":"operate/rs/release-notes/rs-6-4-2-releases","summary":"Pub/sub ACLs & default permissions. Validate client certificates by subject attributes. Ubuntu 20.04 support.","title":"Redis Enterprise Software release notes 6.4.2","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/"},{"id":"operate/rs/release-notes/rs-6-2-18-releases","summary":"Database auditing. Private key encryption. Active-Active database support for MEMORY USAGE command.","title":"Redis Enterprise Software release notes 6.2.18","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-18-releases/"},{"id":"operate/rs/release-notes/rs-6-2-12","summary":"OCSP Support. Password & session configuration changes. RHEL 8.6 support.","title":"Redis Enterprise Software release notes 6.2.12 (August 2022)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-12/"},{"id":"operate/rs/release-notes/rs-6-2-10-february-2022","summary":"Python 3 support. RHEL 8.5 support.","title":"Redis Enterprise Software Release Notes 6.2.10 (February 2022)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-10-february-2022/"},{"id":"operate/rs/release-notes/rs-6-2-8-october-2021","summary":"RHEL 8 support. Set backup start time.","title":"Redis Enterprise Software Release Notes 6.2.8 (October 2021)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-8-october-2021/"},{"id":"operate/rs/release-notes/rs-6-2-4-august-2021","summary":"Internode encryption. Nginx replaced by envoy. New upgrade policies/behavior.","title":"Redis Enterprise Software Release Notes 6.2.4 (August 2021)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-4-august-2021/"},{"id":"operate/rs/release-notes/rs-6-0-20-april-2021","summary":"Role-based LDAP integration. Enhanced client mutual authentication. Active-Active improvements for eviction policies, migration, and the BITFIELD data type.","title":"Redis Enterprise Software Release Notes 6.0.20 (April 2021)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-0-20-april-2021/"},{"id":"operate/rs/release-notes/rs-6-0-12-january-2021","summary":"Distribute synchronization across nodes for Active-Active and Active-Passive databases. Disable internal services to free memory. User accounts support password rotation. Module depdencies automatically installed. Syncer process recovery.","title":"Redis Enterprise Software Release Notes 6.0.12 (January 2021)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-0-12-january-2021/"},{"id":"operate/rs/release-notes/rs-6-0-8-september-2020","summary":"RediSearch 2.0 support. Improved rladmin support for module upgrades.","title":"Redis Enterprise Software Release Notes 6.0.8 (September 2020)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-0-8-september-2020/"},{"id":"operate/rs/release-notes/rs-6-0-may-2020","summary":"ACL and RBAC improvements for database access. Active-Active databases support Redis Streams.","title":"Redis Enterprise Software Release Notes 6.0 (May 2020)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-0-may-2020/"},{"id":"operate/rs/release-notes/legacy-release-notes","summary":"Release notes for Redis Enterprise Software 5.6.0 (April 2020) and earlier versions.","title":"Previous releases","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/"}],"page_type":"content","content_hash":"da3bef9d8367a7a1979180602e3cca3eb9674999d33d01528cbf6b88920a393d","sections":[{"id":"content","title":"Content","role":"content","text":"Here's what changed recently in Redis Software:\n\n\n| Version (Release date) | Major changes | Redis Open Source compatibility |\n| --- | --- | --- |\n| [8.2.x releases](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-2-releases/) | Database-scoped management roles. Data path command auditing. | Redis 8.6, 8.4, 8.2, 8.0, 7.4, 7.2, 6.2 |\n| [8.0.x releases](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/) | Redis Software 8! The most performant, most secure, and richest version so far. Built for performance, scale, and reliability to power modern ML and AI applications. | Redis 8.6, 8.4, 8.2, 8.0, 7.4, 7.2, 6.2 |\n| [7.22.x releases](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/) | Diagnostic logging service. Call home client to send daily usage statistics to Redis. Usage reports in support packages. Revamp database API. Migration status API. Two-dimensional rack awareness. New version for actions API. Additional REST API enhancements. | Redis 7.4, 7.2, 6.2 |\n| [7.8.x releases](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/) | Redis Community Edition 7.4 features. Hash field expiration. Client-side caching support. Metrics stream engine preview. New APIs to check database availability, rebalance shards, fail over shards, and control database traffic. Cluster Manager UI enhancements for node actions, database tags, and database configuration. User manager role. Log rotation based on both size and time. Module management enhancements. Configurable minimum password length. Configurable license expiration alert threshold. | Redis 7.4, 7.2, 6.2 |\n| [7.4.x releases](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/) | New Cluster Manager UI enhancements, including Active-Active database management. Full TLS 1.3 support. Automatic recovery configuration. Full IPv6 support, including for internal traffic. Maintenance mode enhancements. Module management enhancements. RHEL 9 support. | Redis 7.2, 6.2, 6.0 |\n| [7.2.4 releases](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/) | Redis 7.0 and 7.2 features. Auto Tiering (enhanced successor to Redis on Flash). RESP3 support. Sharded pub/sub. Preview of the new Cluster Manager UI. Redis Stack 7.2 features. Three Redis database versions. License file structure updates. Redis ACL selectors and enhanced key-based permissions. New INFO fields. Log rotation enhancements. Multi-OS upgrade support for clusters with modules. | Redis 7.2, 6.2, 6.0 |\n| [6.4.2 releases](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/) | Pub/sub ACLs & default permissions. Validate client certificates by subject attributes. Ubuntu 20.04 support. | Redis 6.2, 6.0 |\n| [6.2.18 releases](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-18-releases/) | Database auditing. Private key encryption. Active-Active database support for MEMORY USAGE command. | Redis 6.2, 6.0 |\n| [6.2.12 (August 2022)](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-12/) | OCSP Support. Password & session configuration changes. RHEL 8.6 support. | Redis 6.2.6, 6.0 |\n| [6.2.10 (February 2022)](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-10-february-2022/) | Python 3 support. RHEL 8.5 support. | Redis 6.2.5, 6.0 |\n| [6.2.8 (October 2021)](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-8-october-2021/) | RHEL 8 support. Set backup start time. | Redis 6.2.3, 6.0 |\n| [6.2.4 (August 2021)](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-4-august-2021/) | Internode encryption. Nginx replaced by envoy. New upgrade policies/behavior. | Redis 6.2, 6.0 |\n| [6.0.20 (April 2021)](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-0-20-april-2021/) | Role-based LDAP integration. Enhanced client mutual authentication. Active-Active improvements for eviction policies, migration, and the BITFIELD data type. | Redis 6.0.9 |\n| [6.0.12 (January 2021)](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-0-12-january-2021/) | Distribute synchronization across nodes for Active-Active and Active-Passive databases. Disable internal services to free memory. User accounts support password rotation. Module depdencies automatically installed. Syncer process recovery. | Redis 6.0.6 |\n| [6.0.8 (September 2020)](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-0-8-september-2020/) | RediSearch 2.0 support. Improved rladmin support for module upgrades. | Redis 6.0.5 |\n| [6.0 (May 2020)](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-0-may-2020/) | ACL and RBAC improvements for database access. Active-Active databases support Redis Streams. | Redis 6 |\n| [Previous releases](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/) | Release notes for Redis Enterprise Software 5.6.0 (April 2020) and earlier versions. |  |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/legacy-release-notes","title":"Previous releases","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/","summary":"Release notes for Redis Enterprise Software 5.6.0 (April 2020) and earlier versions.","tags":["docs","operate","rs"],"last_updated":"2024-02-26T13:11:00+01:00","children":[{"id":"operate/rs/release-notes/legacy-release-notes/rs-5-6-0-april-2020","summary":"Install improvements for RHEL 6 and 7. Active-Active support for HyperLogLog. Auto Tiering now supports RedisJSON. Active-Active default changes for high availability and OSS Cluster API support. Backup support for Google Cloud Storage and Azure Blob storage.","title":"Redis Enterprise Software Release Notes 5.6.0 (April 2020)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-6-0-april-2020/"},{"id":"operate/rs/release-notes/legacy-release-notes/rs-5-5-preview-april-2019","summary":"Preview release. Databases support multiple modules.","title":"Redis Enterprise Software Release Notes 5.5 Preview (April 2019)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-5-preview-april-2019/"},{"id":"operate/rs/release-notes/legacy-release-notes/rs-5-4-14-february-2020","summary":"","title":"Redis Enterprise Software Release Notes 5.4.14 (February 2020)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-14-february-2020/"},{"id":"operate/rs/release-notes/legacy-release-notes/rs-5-4-10-december-2019","summary":"","title":"Redis Enterprise Software Release Notes 5.4.10 (December 2019)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-10-december-2019/"},{"id":"operate/rs/release-notes/legacy-release-notes/rs-5-4-6-july-2019","summary":"","title":"Redis Enterprise Software Release Notes 5.4.6 (July 2019)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-6-july-2019/"},{"id":"operate/rs/release-notes/legacy-release-notes/rs-5-4-4-june-2019","summary":"","title":"Redis Enterprise Software Release Notes 5.4.4 (June 2019)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-4-june-2019/"},{"id":"operate/rs/release-notes/legacy-release-notes/rs-5-4-2-april-2019","summary":"","title":"Redis Enterprise Software Release Notes 5.4.2 (April 2019)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-2-april-2019/"},{"id":"operate/rs/release-notes/legacy-release-notes/rs-5-4-december-2018","summary":"","title":"Redis Enterprise Software Release Notes 5.4 (December 2018)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-december-2018/"},{"id":"operate/rs/release-notes/legacy-release-notes/rs-5-2-2-august-2018","summary":"","title":"Redis Enterprise Software 5.2.2 (August 2018)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-2-2-august-2018/"},{"id":"operate/rs/release-notes/legacy-release-notes/rs-5-3-beta-july-2018","summary":"","title":"Redis Enterprise Software Release Notes 5.3 BETA (July 2018)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-3-beta-july-2018/"},{"id":"operate/rs/release-notes/legacy-release-notes/rs-5-2-june-2018","summary":"","title":"Redis Enterprise Software Release Notes 5.2 (June 2018)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-2-june-2018/"},{"id":"operate/rs/release-notes/legacy-release-notes/release-notes-redis-enterprise-software-v5-0-2","summary":"","title":"Redis Enterprise Software 5.0.2 (2018 March)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/release-notes-redis-enterprise-software-v5-0-2/"},{"id":"operate/rs/release-notes/legacy-release-notes/redis-enterprise-5","summary":"","title":"Redis Enterprise Pack 5.0 Release Notes (November 2017)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/redis-enterprise-5/"},{"id":"operate/rs/release-notes/legacy-release-notes/redis-pack-4-5-0-may-2017","summary":"","title":"Redis Enterprise Pack 4.5 Release Notes (May 2017)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/redis-pack-4-5-0-may-2017/"},{"id":"operate/rs/release-notes/legacy-release-notes/rlec-4-4-dec-2016","summary":"","title":"RLEC 4.4 Release Notes (December 2016)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rlec-4-4-dec-2016/"},{"id":"operate/rs/release-notes/legacy-release-notes/rlec-4-3-aug-2016","summary":"","title":"RLEC 4.3.0-230 Release Notes (August 2, 2016)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rlec-4-3-aug-2016/"},{"id":"operate/rs/release-notes/legacy-release-notes/rlec-4-2-october-2015","summary":"","title":"RLEC 4.2.1-30 Release Notes (October 18, 2015)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rlec-4-2-october-2015/"},{"id":"operate/rs/release-notes/legacy-release-notes/rlec-4-0-june-2015","summary":"","title":"RLEC 4.0.0-49 Release Notes (June 18, 2015)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rlec-4-0-june-2015/"},{"id":"operate/rs/release-notes/legacy-release-notes/rlec-0-99-february-2015","summary":"","title":"RLEC 0.99.5-24 Release Notes (February 15, 2015)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rlec-0-99-february-2015/"},{"id":"operate/rs/release-notes/legacy-release-notes/rlec-0-99-5-11-january-2015","summary":"","title":"RLEC 0.99.5-11 Release Notes (January 5, 2015)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rlec-0-99-5-11-january-2015/"}],"page_type":"content","content_hash":"b181a77b7173eab1f3726d6ad4a062d3a5c047dab5bcec0a381dc506fdf036a2","sections":[{"id":"content","title":"Content","role":"content","text":"| Version (Release date) | Major changes | OSS Redis compatibility |\n| --- | --- | --- |\n| [5.6.0 (April 2020)](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-6-0-april-2020/) | Install improvements for RHEL 6 and 7. Active-Active support for HyperLogLog. Auto Tiering now supports RedisJSON. Active-Active default changes for high availability and OSS Cluster API support. Backup support for Google Cloud Storage and Azure Blob storage. | Redis 5.0.8 |\n| [5.5 preview (April 2019)](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-5-preview-april-2019/) | Preview release. Databases support multiple modules. |  |\n| [5.4.14 (February 2020)](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-14-february-2020/) |  |  |\n| [5.4.10 (December 2019)](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-10-december-2019/) |  |  |\n| [5.4.6 (July 2019)](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-6-july-2019/) |  |  |\n| [5.4.4 (June 2019)](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-4-june-2019/) |  |  |\n| [5.4.2 (April 2019)](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-2-april-2019/) |  |  |\n| [5.4 (December 2018)](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-december-2018/) |  |  |\n| [5.2.2 (August 2018)](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-2-2-august-2018/) |  |  |\n| [5.3 beta(July 2018)](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-3-beta-july-2018/) |  |  |\n| [5.2 (June 2018)](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-2-june-2018/) |  |  |\n| [5.0.2 (2018 March)](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/release-notes-redis-enterprise-software-v5-0-2/) |  |  |\n| [5.0 (November 2017)](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/redis-enterprise-5/) |  |  |\n| [4.5 (May 2017)](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/redis-pack-4-5-0-may-2017/) |  |  |\n| [4.4 (December 2016)](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rlec-4-4-dec-2016/) |  |  |\n| [4.3.0-230 (August 2, 2016)](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rlec-4-3-aug-2016/) |  |  |\n| [4.2.1-30 (October 18, 2015)](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rlec-4-2-october-2015/) |  |  |\n| [4.0.0-49 (June 18, 2015)](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rlec-4-0-june-2015/) |  |  |\n| [0.99.5-24 (February 15, 2015)](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rlec-0-99-february-2015/) |  |  |\n| [0.99.5-11 (January 5, 2015)](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rlec-0-99-5-11-january-2015/) |  |  |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/legacy-release-notes/redis-enterprise-5","title":"Redis Enterprise Pack 5.0 Release Notes (November 2017)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/redis-enterprise-5/","summary":"","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"970d1ae5dc842cb28908943f85d0dc88e4c55e73df73b5ef2e5481765ecad325","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"Redis Enterprise Software 5.0 is now available. Key features include\nGeo-Distributed Active-Active Conflict-free Replicated Databases (CRDB),\nLDAP integration, Redis Module integration, and support for the Redis\nCluster API."},{"id":"overview","title":"Overview","role":"overview","text":"If you are upgrading from a previous version, make sure to review the\nupgrade instructions before beginning the upgrade process.\n\nYou can upgrade to RS 5.0 from RS 4.4.2 and above. If you have a version\nolder than this, you must first upgrade to at least 4.4.2."},{"id":"new-features","title":"New features","role":"content","text":""},{"id":"support-for-redis-cluster-api","title":"Support for Redis Cluster API","role":"compatibility","text":"The Redis Cluster API support in Redis Enterprise Software (RS) provides\na simple mechanism for Cluster enabled Redis clients to learn and know\nthe cluster topology. This enables clients to connect directly to an RS\nproxy on the node hosting the master shard for the data being operated\non. The result is that for all but the initial call to get the cluster\ntopology or reacquire the location of the master shard, the client will\nconnect to the RS endpoint proxy where the master shard is located.\n[Learn more about the Cluster API\nimplementation](https://redis.io/docs/latest/operate/rs/clusters/optimize/oss-cluster-api)."},{"id":"geodistributed-activeactive-conflictfree-replicated-databases-crdb","title":"Geo-distributed Active-Active conflict-free replicated databases (CRDB)","role":"content","text":"Developing globally distributed applications can be challenging, as\ndevelopers have to think about race conditions and complex combinations\nof events under geo-failovers and cross-region write conflicts. CRDBs\nsimplify developing such applications by directly using built-in smarts\nfor handling conflicting writes based on the data type in use. Instead\nof depending on just simplistic \"last-writer-wins\" type conflict\nresolution, geo-distributed CRDBs combines techniques defined in CRDT\n(conflict-free replicated data types) research with Redis types to\nprovide smart and automatic conflict resolution based on the data type's\nintent.\n\nFor more information, go here. For information, go to [Developing with\nCRDBs](https://redis.io/docs/latest/operate/rs/databases/active-active/develop/develop-for-aa)."},{"id":"redis-modules","title":"Redis modules","role":"content","text":"Redis Modules enable you to extend the functionality of Redis Enterprise\nPack. One can add new data types, capabilities, etc. to tailor the\ncluster to a specific use case or need. Once installed, modules benefit\nfrom the high performance, scalability, and high availability that Redis\nEnterprise is known for.\n\n#### Redis modules\n\nRedis has developed and certified the following modules for use with \nRedis Enterprise Pack:\n\n- [RediSearch](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search)\n    - This module turns RS into a supercharged distributed in-memory\n      full-text indexing and search beast.\n- [ReJSON](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json)\n    - Now you have the convenience JSON as a built-in data type and\n      easily able to address nested data via a path.\n- [RedisBloom](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/bloom)\n    - Enables RS to have a scalable bloom filter as a data type. Bloom\n      filters are probabilistic data structures that\n      quickly determine if values are in a set.\n\n#### Custom modules\n\nIn addition, Redis Enterprise Pack provides the ability to load and use\ncustom [Redis modules](https://redislabs.com/community/redis-modules-hub/) or of\nyour own creation."},{"id":"support-for-docker","title":"Support for Docker","role":"compatibility","text":"Deploying and running your Redis Enterprise Software cluster on Docker\ncontainers is supported in development systems and\navailable to pull from Docker hub. With the official image, you can\neasily and quickly test several containers to build the scalable\nand highly available cluster Redis Enterprise Software is famous for.\n\nFor more information, go to [quick start with Redis Enterprise on\nDocker](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart)."},{"id":"ldap-integration","title":"LDAP Integration","role":"content","text":"As part of our continued emphasis on security, administrative user\naccounts in Redis Enterprise Pack can now use either built-in\nauthentication or authenticate externally via LDAP with saslauthd. The\naccounts can be used for administering resources on the cluster via\ncommand line, Rest API, or admin console.\n\nFor more information see [LDAP\nIntegration](https://redis.io/docs/latest/operate/rs/security/access-control/ldap)."},{"id":"additionalcapabilities","title":"Additional capabilities","role":"content","text":"Support for additional Redis commands and features:\n\n- Support for Redis version 4.0.2\n- Support added for RHEL/CentOS 6.9 and 7.4"},{"id":"information","title":"Information","role":"content","text":"- In the node bootstrap API, the structure of the JSON has changed for\n    adding an external IP during the bootstrap process.\n- End-of-Life for RHEL/CentOS 6.5 and 6.6 have been reached, so those\n    versions are no longer supported.\n- Modules are not supported in Redis Enterprise Pack 5.0 for\n    RHEL/CentOS 6.x"},{"id":"important-fixes","title":"Important fixes","role":"content","text":""},{"id":"5-0-031","title":"5.0.0-31","role":"content","text":"- RP9299 - Issue with reliability of metric ingress\n- RP9680 - Redis Enterprise Pack starting before /etc/rc.local script\n    executed\n- RP12363 - In some cases, flash drives do not mount following a\n    stop/start of the node\n- RP12493 - Allow change to debug package creation location\n- RP13079 - DNS doesn't change after having removed the external IP\n    address in some cases\n- RP13933 - rladmin balance sometimes shows incorrect information\n- RP14060 - pubsub stats aren't reflected correctly by the stats\n    archiver\n- RP14939 - Add license checks to all needed entry points in the\n    cluster\n- RP15090 - Problem in log rotate script causes other logs to not\n    rotate\n- RP15104 - rlutil check fix doesn't work sometimes\n- RP15130 - Fixed permission on two logs with incorrect ownership\n- RP15160 - Allow option to change ports for API and CM in NGINX\n- RP15164 - Allow unix socket folder to be configurable at build time\n- RP15499 - rladmin command not responding on a cluster with large\n    number of shards\n- RP15853 - When trying to add a new db the 'activate' button was\n    changed to 'update' and was grayed out.\n- RP15861 - Allow unix socket folder to be configurable at install\n    time\n- RP16115 - TTL bug with Replica Of and import\n- RP16447 - DMC client connection reports incorrect number of\n    connections to monitoring applications.\n- RP16481 - In some cases, resource_mgr uses incorrect directories\n    to compute persistent and ephemeral storage"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/legacy-release-notes/redis-pack-4-5-0-may-2017","title":"Redis Enterprise Pack 4.5 Release Notes (May 2017)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/redis-pack-4-5-0-may-2017/","summary":"","tags":["docs","operate","rs"],"last_updated":"2023-12-14T15:27:49+01:00","page_type":"content","content_hash":"1a03776beeda112b47995e607dd0fa422f18f269195af6ba2556c15866987c87","sections":[{"id":"overview","title":"Overview","role":"overview","text":"If you are upgrading from a previous version, make sure to review the\nupgrade instructions before beginning the upgrade process.\n\nYou can upgrade to this version from any 4.4 version. If you have a\nversion older than 4.4 you must first upgrade to 4.4 or higher, and only\nthen upgrade to this version."},{"id":"new-features","title":"New features","role":"content","text":""},{"id":"the-new-discovery-service-with-supportfor-redis-sentinel-api","title":"The new discovery service with supportfor Redis Sentinel API","role":"content","text":"The Discovery Service provides an IP-based connection management service\nused when connecting to Redis Enterprise Pack databases. When used in\nconjunction with Redis Enterprise Pack's other high availability\nfeatures, the Discovery Service assists an application cope with\ntopology changes such as adding, removing of nodes, node failovers and\nso on. It does this by providing your application with the ability to\neasily discover which node hosts the database endpoint. The API used for\ndiscovery service is compliant with the Redis Sentinel API.\n\nDiscovery Service is an alternative for applications that do not want to\ndepend on DNS name resolution for their connectivity. Discovery Service\nand DNS based connectivity are not mutually exclusive. They can be used\nside by side in a given cluster where some clients can use Discovery\nService based connection while others can use DNS name resolution when\nconnecting to databases."},{"id":"building-large-databases-with-ram-and-flash-memory-in-auto-tiering-v20","title":"Building large databases with RAM and Flash memory in Auto Tiering v2.0","role":"content","text":"With Redis Enterprise Pack 4.5, Auto Tiering v2 is production\nready. RFv2 brings performance, reliability, and stability enhancements\nas well as many features customers have been waiting for.\n\nAuto Tiering offers users of Redis Enterprise Pack and\nRedis Cloud Private the unique ability to operate a Redis\ndatabase that spans both RAM and flash memory (SSD), but remains\nseparate from Redis Enterprise Pack's persistence mechanisms. Whilst\nkeys are always stored in RAM, RoF intelligently manages the location of\ntheir values (RAM vs Flash) in the database via a LRU-based\n(least-recently-used) mechanism. Hot values will be in RAM and\ninfrequently used, while warm values will be ejected to flash memory.\nThis enables you to have much larger datasets with RAM-like latency and\nperformance, but at dramatically lower cost than an all-RAM database."},{"id":"additionalcapabilities","title":"Additional capabilities","role":"content","text":"Support for additional Redis commands and features:\n\n- The Redis TOUCH command is now supported\n- Redis version upgraded to 3.2.8\n- Support for OBJECT in Lua scripts\n\nSupport has been added RHEL 7.3 with this version."},{"id":"important-fixes","title":"Important fixes","role":"content","text":"- RP10106 - SSL Certificate should not need to be signed with a\n    stronger hashing algorithm to be accepted\n- RP10465 - failover times can be higher under certain scenarios in\n    local watchdog profile\n- RP10633 - Improve install.sh and answers file\n- RP11880 - Improved replica sync and add node robustness\n- RP8689 - Minimized impact when changing RAM-Flash limit on Redis\n    Enterprise Flash\n- RP12063 - Improved Redis Flash data import/population performance\n- RP11608 - Improvements to databases.txt creation in support package\n- RP11941 - Eliminated warning and errors during upgrade on RHEL6\n    with leash and python2.6 is installed\n- RP11994 - Databases under certain cases may not display in UI even\n    though they are in the cluster metadata and safely operating.\n- RP12438 - Email alerts with Amazon SES may fail under certain\n    conditions.\n- RP12538 - Redis failover was initiated by node_wd during sync to\n    new replica\n- RP10264 - Improved debuginfo package for better supportability\n\nImportant fixes in RP 4.5.0-22\n\n- RP12667 - NGINX security improvements, improved TLS and SSL related\n    warnings on security scans.\n- RP12690 - Added simpler ability to recover cluster from AWS S3\n- RP13359 - Advanced memory allocation and booking enhancements\n\nImportant fixes in RP 4.5.0-31\n\n- RP13060 - Client may experience reconnect issues after failover of\n    the endpoint.\n- RP13711 - Disabling IPv6 may cause startup of node services to fail\n    (NGINX) on RHEL 7\n- RP12747 - Calculation can be incorrect for memory quota for\n    databases\n\nImportant fixes in RP 4.5.0-35\n\n- RP12844 - cnm_exec crashes on \"not enough arguments for format\n    string\"\n\nImportant fixes in RP 4.5.0-43\n\n- RP9846 - In UI, Used Memory may show incorrect values\n- RP12211 - UI fails import from a Redis OSS DB with password\n- RP13356 - Enable failover when license expires\n- RP14692 - rladmin status command may crash during backup\n- RP14541 - In rare cases, DMC log grew quickly and caused stability\n    issues\n- RP15107 - When using Auto Tiering, it may cause DMC proxy\n    crashes\n\nImportant fixes in RP 4.5.0-47\n\n- Multiple important Auto Tiering updates.\n\nImportant fixes in RP 4.5.0-51\n\n- 15161 - Make Unix socket folder configurable at install time\n- 15164 - Make Unix socket folder configurable at build time\n- 16082 - Make Unix socket folder configurable at runtime\n- Fixed an issue where on import of data, TTL information was set\n    incorrectly"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/legacy-release-notes/release-notes-redis-enterprise-software-v5-0-2","title":"Redis Enterprise Software 5.0.2 (2018 March)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/release-notes-redis-enterprise-software-v5-0-2/","summary":"","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"243116e0e57157fff447ea69794dc62e6508244bfbaf10c023ed56101c83196b","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"Redis Enterprise Software 5.0.2 is now available. Key features include\nfunctional and performance updates for CRDB, changes to module\ndeployment, and general fixes."},{"id":"overview","title":"Overview","role":"overview","text":"If you are upgrading from a previous version, make sure to review the\nupgrade instructions before beginning the upgrade process.\n\nYou can upgrade to RS 5.0.2 from RS 4.4.2 and above. If you have a\nversion older than 4.4.2, you must first upgrade to at least 5.0.\n\n> [!NOTE]\n> Starting from RS 5.0.2, ports 3338 and 3339 should also be opened\n> on each node for the purpose of internal cluster communication.\n> For more information, check the ['network port\n> configurations'](https://redis.io/docs/latest/operate/rs/networking/port-configurations)\n> page"},{"id":"new-features","title":"New features","role":"content","text":""},{"id":"crdbs","title":"CRDBs","role":"content","text":"- The ability to add and remove participating clusters from a CRDB\n- Communications between participating clusters can be encrypted using\n    SSL/TLS\n- Imports can be done to an existing database without flushing the\n    existing data beforehand."},{"id":"modules","title":"Modules","role":"content","text":"- Redis Enterprise Modules are installed with Redis Enterprise\n    Software by default\n- RedisBloom and RediSearch Enterprise have been updated to newer\n    versions"},{"id":"other","title":"Other","role":"content","text":"- Discovery Service supports encryption using SSL/TLS\n- Starting from version 5.0.2 build #30, Redis Enterprise Software is\n    supported on RHEL 7.5"},{"id":"important-fixes","title":"Important fixes","role":"content","text":"- RS16153 -- Supervisord version update\n- RS16667 - Fixed issue with 'rladmin status' timeout\n- RS17746 - Fixed upgrade issue when -s option used\n- RS17997, RS18088 - Upgrade issues when using non-default socket\n    file path\n- RS8584 -- Endpoint migration provided misleading plan message\n- RS17696 - Fixed issues with Multi-proxy and intermittent network\n    issues\n- RS17362 - Upgrade fails under some circumstances\n- RS18351 - Listener active after node has been declared dead\n- RS18874 - Fixed OOM issue due to redis_mgr high memory consumption\n- RS19002 - Fixed wrong message when an upgrade of a quorum node with\n    all-nodes policy takes place\n\n**Important Fixes in Build #30:**\n\n- RS19701 - Fixed high CPU usage on large scale clusters\n- RS19869- Added support for Redis\n    version 4.0.9\n- RS20153- Fixed Redis important security issues related to the Lua\n    scripting engine\n- RS19852- Fixed proxy crash which might happen for SSL-enabled\n    DBs"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"- Since Redis Enterprise CRDBs have counters, unlike traditional Redis\n    databases, they must be handled differently when importing. There is\n    a special type of import because of importing counter data types.\n    When performing the import through the admin console, you will be prompted\n    to confirm you want to add the data to the CRDB or stop and go flush\n    the database.\n- This version of RS comes with a pre-bundled python which might\n    over-ride your default installed python version, this can be solved\n    by changing your PATH environment variable.\n- Uploading a Redis Module through the admin console, can be performed only\n    when the admin console is connected to the master node.\n- Write operations are not allowed for database which was created with\n    password of exactly 50-characters."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/legacy-release-notes/rlec-0-99-5-11-january-2015","title":"RLEC 0.99.5-11 Release Notes (January 5, 2015)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rlec-0-99-5-11-january-2015/","summary":"","tags":["docs","operate","rs"],"last_updated":"2023-12-14T15:27:49+01:00","page_type":"content","content_hash":"3cd7d9f73048488f0608b2914272edee7d07b871c37ce50c0c89e0ec100f5709","sections":[{"id":"new-features","title":"New features","role":"overview","text":"Initial release, everything is new!"},{"id":"changes","title":"Changes","role":"content","text":"Initial release, no changes!"},{"id":"fixed-issues","title":"Fixed issues","role":"content","text":"None."},{"id":"known-issues","title":"Known issues","role":"content","text":"- **Issue:** When taking a node offline or removing a node, if the\n    node being taken offline or removed is currently serving as the web\n    server for the web browser being used to view the management UI, the\n    management UI appears down while the node is down.\n    **Workaround:** If you are using the cluster name in order to\n    connect to the management UI in the browser, and the cluster name is\n    registered in your external DNS or you are using the mDNS option,\n    then the DNS entries will be updated to point to another node in the\n    cluster after a few seconds and the UI will open properly. If you\n    are not using the cluster name but rather the node IP in order to\n    connect to the management UI in the web browser, you have to use the\n    IP of another node in the cluster to access the management UI."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/legacy-release-notes/rlec-0-99-february-2015","title":"RLEC 0.99.5-24 Release Notes (February 15, 2015)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rlec-0-99-february-2015/","summary":"","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"93718f66d0cd62a26eaede8d40e25bfbc79f016877c1e73b1c291963867fa952","sections":[{"id":"overview","title":"Overview","role":"overview","text":"If you are upgrading from a previous version, make sure to review the\n[upgrade\ninstructions](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading)\nbefore running through the upgrade process.\n\nIf you are upgrading from version 0.99.5-11:\n\n1. You must restart the services after the upgrade by running the\n    following command with user root (sudo su). From the operating\n    system's CLI, run the following command:\n    cnm_ctl restart\n1. After the upgrade, rladmin status command will report that the\n    databases are from an old version. It is recommended that you\n    upgrade the databases as soon as possible, as described in the\n    [upgrade\n    instructions](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading)."},{"id":"new-features","title":"New features","role":"content","text":"None."},{"id":"changes","title":"Changes","role":"content","text":"- Enhancements to memtier_benchmark tool that is included in the\n    installation package. You can find more details in the [GitHub\n    project.](https://github.com/RedisLabs/memtier_benchmark)"},{"id":"fixed-issues","title":"Fixed issues","role":"content","text":"- Improvements and fixes related to node failover, remove node and\n    take node offline functionality."},{"id":"known-issues","title":"Known issues","role":"content","text":"- **Issue:** When taking a node offline or removing a node, if the\n    node being taken offline or removed is currently serving as the web\n    server for the web browser being used to view the management UI, the\n    management UI appears down while the node is down.\n    **Workaround:** If you are using the cluster name in order to\n    connect to the management UI in the browser, and the cluster name is\n    registered in your external DNS or you are using the mDNS option,\n    then the DNS entries will be updated to point to another node in the\n    cluster after a few seconds and the UI will open properly. If you\n    are not using the cluster name but rather the node IP in order to\n    connect to the management UI in the web browser, you have to use the\n    IP of another node in the cluster to access the management UI."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/legacy-release-notes/rlec-4-0-june-2015","title":"RLEC 4.0.0-49 Release Notes (June 18, 2015)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rlec-4-0-june-2015/","summary":"","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"c521b83c33ed9a245a516284a339e1a1a0322f641bfb61b3d081d551d6933272","sections":[{"id":"overview","title":"Overview","role":"overview","text":"If you are upgrading from a previous version, make sure to review the\n[upgrade\ninstructions](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading)\nbefore running through the upgrade process.\n\nIn addition, when running the install.sh script to upgrade the software,\nyou might be prompted to approve changes to a configuration file named\nccs-redis.conf. It is crucial that you choose Yes when asked whether to\nupdate this file."},{"id":"new-features","title":"New features","role":"content","text":"- Support for Red Hat Enterprise Linux (RHEL) and CentOS 6.5 and 7\n    operating systems.\n- Support for additional AWS AMIs for Ubuntu and Amazon Linux, on\n    multiple AWS regions.\n- Support for additional browsers and operating systems for the\n    management UI.\n- Replica Of feature which enables creating a Redis database that\n    keeps synchronizing data from another Redis database.\n- Rack-zone awareness feature which enables mapping nodes to\n    racks/zones to ensure a more sophisticated high-availability\n    mechanism.\n- Database-related alerts and email alerts.\n- Auto-configuration of synchronization of cluster server clocks with\n    NTP as part of installation script.\n- Database Export functionality.\n- Email alerts on database Export and Import."},{"id":"changes","title":"Changes","role":"content","text":"- Database Backup Now functionality replaced with Export\n    functionality.\n- Database performance improvements to increase throughput and reduce\n    latency.\n- Improvement to AOF rewrite mechanism to deal with extreme-write\n    scenarios and limited disk space configurations.\n- Enhancements to rladmin CLI to support additional commands."},{"id":"fixed-issues","title":"Fixed issues","role":"content","text":"- Cluster stability improvements related to removing nodes and taking\n    nodes offline.\n- rladmin CLI bug fixes."},{"id":"known-issues","title":"Known issues","role":"content","text":"- **Issue**: RLEC-6819 - Uninstall on Red Hat Enterprise Linux does\n    not stop all services and if you try to install the software again\n    on the same machine the new installation might use prior\n    installation data.\n    \n    **Workaround**: Before installing the software\n    again restart the machine or verify that all services are down.\n\n- **Issue**: In the replica of process, if the source database is\n    resharded while the replica of process is active, the\n    synchronization process will fail.\n    \n    **Workaround**: You need to\n    manually stop and restart the synchronization process after\n    resharding of the source database is done.\n\n- **Issue**: In the replica of process, high database traffic might cause the replica of process to restart frequently\n    as result of the \"replica buffer\" being exceeded. In this case you see\n    the status of the replica of process as \"Syncing\"\n    frequently.\n    \n    **Workaround**: You need to manually reconfigure the\n    \"replica buffer\" through rladmin and set the buffer size to a new\n    size. In order to find the appropriate buffer size please contact\n    support at: <support@redislabs.com>.\n\n- **Issue**: In a cluster that is configured to support rack-zone\n    awareness, if the user forces migration of a master or replica shard,\n    through rladmin, to a node on the same rack-zone as its\n    corresponding master or replica shard, and later runs the rebalance\n    process, the rebalance process will not migrate the shards to ensure\n    rack-zone awareness compliance.\n    \n    **Workaround**: In the scenario\n    described above, you need to manually migrate the shard, through\n    rladmin, to a node on a valid rack-zone in order to ensure rack-zone\n    awareness compliance.\n\n- **Issue**: In case you deploy a cluster and use the DNS option for\n    the cluster name (see details in [How to set the Cluster Name\n    (FQDN)](https://redis.io/docs/latest/operate/rs/networking/cluster-dns),\n    do not configure the DNS entries for the cluster nodes, and try to\n    configure a database that is a replica of another database within\n    the cluster, then the UI allows you to configure the source database\n    but the replica of process fails in runtime.\n    \n    **Workaround**: The\n    configuration indicated in this issue is not a valid cluster\n    configuration. If you choose to use the DNS option for the cluster\n    name then you must configure DNS entries for the nodes, otherwise\n    the cluster does not operate correctly. You have to either update\n    the DNS accordingly, or recreate the cluster and use the mDNS option\n    for the cluster name as described in [How to set the Cluster Name\n    (FQDN)](https://redis.io/docs/latest/operate/rs/networking/cluster-dns).\n    \n- **Issue**: When taking a node offline or removing a node, if the\n    node being taken offline or removed is currently serving as the web\n    server for the web browser being used to view the management UI,\n    then the management UI appears down while the node is\n    down.\n    \n    **Workaround**: If you are using the cluster name in order to\n    connect to the management UI in the browser, and the cluster name is\n    registered in your external DNS or you are using the mDNS option,\n    then the DNS entries will be updated to point to another node in the\n    cluster after a few seconds and the UI will open properly. If you\n    are not using the cluster name but rather the node IP in order to\n    connect to the management UI in the web browser, you have to use the\n    IP of another node in the cluster to access the management UI."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/legacy-release-notes/rlec-4-2-october-2015","title":"RLEC 4.2.1-30 Release Notes (October 18, 2015)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rlec-4-2-october-2015/","summary":"","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"dc46fb19be59d3ca2663be7a6b5c23cd1b771693b9e6dc5a46532d6aa2baa281","sections":[{"id":"overview","title":"Overview","role":"overview","text":"If you are upgrading from a previous version, make sure to review the\n[upgrade\ninstructions](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading)\nbefore running through the upgrade process."},{"id":"new-features","title":"New features","role":"content","text":"- rsyslog logging support - RLEC now writes by default to syslog,\n    which enables monitoring through rsyslog.\n- Profile support for tuning cloud and non-cloud environments --\n    enables the administrator to configure RLEC to run with different\n    performance profiles that are optimized for either cloud or\n    non-cloud environments. For additional details, refer to the\n    [Performance\n    optimization](https://redis.io/docs/latest/operate/rs/clusters/optimize/optimization)\n    section.\n- SLA for AOF rewrite - enables the administrator to configure\n    database parameters, by running the rladmin tune command, related to\n    when AOF rewrite is triggered based on the time it would take to\n    load the database from the AOF file, and on the maximum AOF rewrite\n    file size. In addition, updates to the AOF rewrite mechanism\n    minimize chances of the disk getting full.\n- New warning and alerts related to AOF rewrite mechanism - a warning\n    is shown during the setup process in case the disk size is lower\n    than twice the size of the RAM. New cluster level alerts added to\n    alert when node available disk space is lower than the needed disk\n    space for AOF rewrite purposes, and when node performance is\n    degraded due to reaching disk I/O limits.\n- Replica Of support for multiple sources - the Replica Of feature is\n    enhanced to support creating a database that is a replica of\n    multiple source databases. For additional details, refer to the Replica Of section.\n- Cross cluster Replica Of - the Replica Of feature now supports\n    defining a database that is a replica of databases that belong to a\n    different RLEC cluster. For additional details, refer to the\n    Replica Of section.\n- Multi-IP support - on a node that has multiple IPs, enables the\n    administrator to specify which IP address is used for internal\n    traffic and which IP addresses are used for external traffic. For\n    additional details, refer to [Multi-IP &\n    IPv6](https://redis.io/docs/latest/operate/rs/networking/multi-ip-ipv6)\n    support.\n- IPv6 support for external traffic - on a node that has multiple\n    IPs, external IP addresses can be of IPv6 type. For additional\n    details, refer to [Multi-IP &\n    IPv6](https://redis.io/docs/latest/operate/rs/networking/multi-ip-ipv6)\n    support section.\n- Support for OpenStack Object Store (\"Swift\") location for import /\n    export / backup. For additional details, refer to [Database\n    backup](https://redis.io/docs/latest/operate/rs/databases/import-export/schedule-backups)\n    and [Importing data to a\n    database](https://redis.io/docs/latest/operate/rs/databases/import-export/import-data)\n    sections.\n- Import of a sharded database - support for importing data of a\n    sharded database by indicating multiple files paths. For additional\n    details, refer to the [Importing data to a\n    database](https://redis.io/docs/latest/operate/rs/databases/import-export/import-data)\n    section.\n- Enable running the install script in silent mode using \"-y\"\n    parameter for default answers (\"Y\") or \"-c\" for file path parameters\n    for custom answers. For additional details, refer to [Accessing and\n    installing the setup\n    package](https://redis.io/docs/latest/operate/rs/installing-upgrading)\n    section.\n- New rladmin command-line-interface \"info\" command allows for\n    fetching current value of tunable parameters."},{"id":"changes","title":"Changes","role":"content","text":"- rladmin command-line-interface can only be run under user root or\n    redislabs. For additional details, refer to the [rladmin\n    command-line\n    interface (CLI)](https://docs.redis.com/latest/rs/references/rladmin/)\n    section.\n- Import / export / backup to/from Amazon S3 requires supplying the\n    credentials per usage instance; it does not use central cloud\n    credentials that used to be supplied in the Settings -\\> General\n    page.\n- Fields related to storing Amazon S3 Cloud credentials have been\n    removed from Settings -\\> General page.\n- Performance optimization in the database resharding mechanism.\n- Persistent and ephemeral storage cluster level alerts are enabled by\n    default and set to 70%.\n- Various enhancements to rladmin command-line-interface (CLI) to\n    support new commands.\n- Redis version updated to 2.8.21 that addresses\n    [CVE-2015-4335/DSA-3279 - Redis Lua Sandbox\n    Escape](https://groups.google.com/forum/#!msg/redis-db/4Y6OqK8gEyk/Dg-5cejl-eUJ).\n- Port 3336 added to the list of ports being used by RLEC.\n- Node \"Network utilization\" alert measured in percentages (%) has\n    been updated to \"Network throughput\" alert measured in MBps. If the\n    alert was defined then it will be disabled when upgrading to this\n    version and the user needs to reconfigure it with a new value.\n- Performance improvements to the database Import to make the process\n    much faster.\n- Enhancements to the support package to include additional details.\n- Removed support for SSL 2.0/3.0 protocols due to security\n    vulnerabilities.\n- Disabled \"dofile\" functionality in Redis to solve a possible\n    security vulnerability.\n- rladmin CLI \"tune watchdog profile\" command syntax updated to \"tune\n    cluster watchdog_profile\"."},{"id":"fixed-issues","title":"Fixed issues","role":"content","text":"- Support for relative path for backup and import functionalities.\n- Fix issue with TLS configuration of email server settings.\n- Email alerts not sent for database export and import processes.\n- Fix erroneous entries in the Log page.\n- Sometimes a wrong value is reported in the UI for node used\n    ephemeral storage space.\n- Sometime the wrong Replica Of Lag value is reported in the UI.\n- RLEC-6875 - email server settings not working when using port 587.\n- RLEC-5498 - Improve rladmin response time when a node is down.\n- Validation of Replica Of source definition did not fail in the UI\n    and would only fail in runtime if it was using the Cluster Name\n    (FQDN) and the FQDN was not properly configured in the DNS.\n- Various improvements to error messages reported by rladmin."},{"id":"known-issues","title":"Known issues","role":"content","text":"- **Issue**: Connecting from a client to a database endpoint with\n    mixed upper case and lower case letters can result in a slow\n    response from the database.\n    \n    **Workaround**: The cluster name (FQDN)\n    should consist of only lower-case letters. When connecting from a\n    client to a database endpoint, only lower case letters should be\n    used in the endpoint.\n\n- **Issue**: When upgrading a node to a new RLEC version (refer to\n    [Upgrading\n    nodes](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading)\n    while the node is in the offline state (refer to [Taking a node\n    offline](https://redis.io/docs/latest/operate/rs/clusters/remove-node),\n    the upgrade process succeeds but might result in an unstable\n    cluster.\n    \n    **Workaround**: Do not try to upgrade a node while it is in\n    the offline state.\n\n- **Issue**: In Red Hat Enterprise Linux, and CentOS operating\n    systems, the process used for cleaning up internal log files does\n    not work, thereby allowing the log files to grow and possibly result\n    in disk space issues.\n    \n    **Workaround**: On each machine that functions\n    as a node in the cluster, create a file named \"redislabs\", and save\n    it in the following location: \"/etc/logrotate.d/\" (e.g.\n    \"/etc/logrotate.d/redislabs\").\n    The file should contain the following text:\n    [code example]\n    The file's permissions should be root:root, 644.\n    Afterwards, from the operating system command line interface (CLI)\n    run the following commands:\n    [code example]\n\n- **Issue**: In the Replica Of process, if the target database does\n    not have replication enabled and it is restarted or fails for any\n    reason, the data on target database might not be in sync with the\n    source database, although the status of the Replica Of process\n    indicates it is.\n    \n    **Workaround**: You need to manually stop and\n    restart the synchronization process in order to ensure the databases\n    are in sync.\n\n- **Issue**: In the Replica Of process, if the source database is\n    resharded while the replica of process is active, the\n    synchronization process will fail.\n    \n    **Workaround**: You need to\n    manually stop and restart the synchronization process after\n    resharding of the source database is done.\n- **Issue**: In the replica of process, high database traffic might restart the Replica of process as result of the \"replica buffer\" being exceeded. In this case you see\n    the status of the replica of process as \"Syncing\"\n    frequently.\n    \n    **Workaround**: You need to manually reconfigure the\n    \"replica buffer\" through rladmin and set the buffer size to a new\n    size. In order to find the appropriate buffer size please contact\n    support at: <support@redislabs.com>.\n\n- **Issue**: In a cluster that is configured to support rack-zone\n    awareness, if the user forces migration of a master or replica shard,\n    through rladmin, to a node on the same rack-zone as its\n    corresponding master or replica shard, and later runs the rebalance\n    process, the rebalance process will not migrate the shards to ensure\n    rack-zone awareness compliance.\n    \n    **Workaround**: In the scenario\n    described above, you need to manually migrate the shard, through\n    rladmin, to a node on a valid rack-zone in order to ensure rack-zone\n    awareness\n    compliance."}],"examples":[{"id":"known-issues-ex0","language":"plaintext","code":"/var/opt/redislabs/log/\\*.log {\n    daily\n    missingok\n    copytruncate\n    rotate 7\n    compress\n    notifempty\n    }","section_id":"known-issues"},{"id":"known-issues-ex1","language":"plaintext","code":"yum install policycoreutils-python\n    semanage fcontext -a -t var_log_t '/var/opt/redislabs/log(/.\\*)?'\n    restsorecon -R /var/opt/redislabs/log","section_id":"known-issues"}]}
{"schema_version":2,"id":"operate/rs/release-notes/legacy-release-notes/rlec-4-3-aug-2016","title":"RLEC 4.3.0-230 Release Notes (August 2, 2016)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rlec-4-3-aug-2016/","summary":"","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"ccd66eaa9adc10ea569140aadc7379156a19f4bb9cf2ff011cbc4e5af77f5c4b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"If you are upgrading from a previous version, make sure to review the\n[upgrade\ninstructions](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading)\nbefore running through the upgrade process.\n\nYou can upgrade to this version from any 4.2 version. If you have a\nversion older than 4.2 you should first upgrade to 4.2 and only then\nupgrade to this version."},{"id":"new-features","title":"New features","role":"content","text":"- Various improvements to internal performance and stability were\n    implemented.\n- RLEC Flash functionality added. For additional details, refer to\n    [Auto Tiering](https://redis.io/docs/latest/operate/rs/databases/flash)\n    and contact <support@redislabs.com> if you are interested in this\n    functionality.\n- Support for Redis version 3.0 added. When you install or upgrade the\n    cluster the new default version for Redis databases will be 3.0 and\n    when you upgrade the databases they will be updated to this version.\n    If you would like to change the default version to Redis 2.8 refer\n    to the instruction in the [Upgrading\n    databases](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading)\n    section. If you would like to upgrade existing databases to the\n    latest 2.8 minor version, refer to the Known Issues section below.\n- Complete cluster failure recovery instructions added. For additional\n    details, refer to [Cluster\n    Recovery](https://redis.io/docs/latest/operate/rs/clusters/cluster-recovery).\n- Major improvements made to database replication performance process\n    by using diskless replication between master and replica shards. The\n    data between the master and replica shards is streamed directly,\n    instead of using the default file-on-disk mechanism. This behavior\n    can be changed for the entire cluster or per database through\n    rladmin.\n- Major enhancements made to rladmin command line interface to add new\n    administration functionalities.\n- rlcheck installation verification utility added to facilitate\n    checking node health. For additional details, refer to [rlcheck\n    installation verification\n    utility](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rlcheck).\n- Added the ability to allow the user to configure how machine IP\n    addresses are used in Node Configuration setup in the management UI.\n    For additional details, refer to [Initial setup - creating a new\n    cluster.](https://redis.io/docs/latest/operate/rs/clusters/new-cluster-setup)\n- Connection to database endpoint can now be encrypted with SSL. For\n    additional details, refer to [Securing client connection with\n    SSL](https://redis.io/docs/latest/operate/rs/security/encryption/tls).\n- Added support for running the cluster on the following operating\n    systems and versions: RHEL/CentOS 6.6, 7.1, 7.2, RHEL 6.7, Oracle\n    Linux 6.5."},{"id":"changes","title":"Changes","role":"content","text":"- Environment configuration profile with name \"default\" has been\n    changed to \"cloud\" and the default value has been changed to\n    \"local-network\". For additional details, refer to [Performance\n    optimization](https://redis.io/docs/latest/operate/rs/clusters/optimize/optimization)\n    section.\n- In the REST API, when creating a database and not setting the\n    database replication parameter to \"true\", the default value assigned\n    by the cluster has changed from \"true\" to \"false\".\n- rladmin syntax updates can affect commands written for prior\n    versions of RLEC. In this version commands that are run directly\n    from the operating system CLI prompt (not through the rladmin\n    prompt) no longer require quotation marks for text with special\n    characters.\n- Option added to the *Replica-of* process that allows gradual\n    \"shard-by- shard\" replication of a sharded database, reducing the\n    load on internal buffers. This optimization setting can be\n    configured on the target database using the gradual_sync_mode\n    parameter in rladmin.\n- The functionality for taking a node offline was removed from the UI."},{"id":"fixed-issues","title":"Fixed issues","role":"content","text":"- RLEC-7110 - node does not recover properly after restart in case\n    ephemeral storage is not available yet\n- RLEC-7502 - log rotate job not working properly on RHEL operating\n    system\n- RLEC-7599 - issues running on a server with no IPv6 kernel support\n- RLEC-7561, RLEC-7597 - issues connecting to database endpoint as\n    result of cluster name containing capital letters\n- RLEC-7245 - on machines with multiple IPs sometimes the wrong IP\n    address is chosen for internal traffic\n- RLEC-6815 - wrong log entry is added when enabling cluster alert\n    regarding database version compatibility\n- RLEC-7652 - database is down in certain failover scenarios only\n    when the database is completely empty\n- RLEC-7737 - issue where in a specific scenario after node restarts,\n    a database with replication both master and replica shards are\n    reported as down\n- RLEC-7712 - in some cases, the Replica Of process may fail when\n    Redis password is set\n- RLEC-7726 - node object \"avg_latency\" statistic is not returned in\n    the REST API\n- RLEC-7358 - install script issue when running on LVM disks\n- RLEC-8086 - port 9443 missing from redislabs-clients.xml\n- RLEC-7281 - rotation of internal log files not working properly\n- RLEC-8279 - updates to a user definition might cause password reset\n    to be required\n- RLEC-8512 - when upgrading an existing cluster that has uppercase\n    letters in the cluster name (FQDN) the cluster might not function\n    properly after the upgrade and attempts to connect to a database\n    might fail\n- RLEC-8371 - email alerts do not work when using Amazon SES service\n- In certain scenarios the node upgrade process may fail if the node\n    is in the offline state"},{"id":"known-issues","title":"Known issues","role":"content","text":"- **Issue**: When upgrading to this version from a previous RLEC\n    version, rladmin status output will show the database status as\n    having an old version. When you upgrade the Redis database (using\n    rladmin upgrade db command) the Redis version will be updated to 3.0\n    even if you updated the cluster's Redis default version to\n    2.8.\n    \n    **Workaround**: If you would like to cancel the old version\n    indication in rladmin status without upgrading the Redis version to\n    3.0 you should first change the cluster default version to 2.8\n    (using rladmin tune cluster command), and then trigger the Redis\n    process to be restarted by migrating the database shards (using\n    rladmin migrate db command).\n\n- **Issue**: RLEC-8486 - On Ubuntu, when uninstalling RLEC using the\n    apt-get purge command, some of the Redis processes on the machine\n    might continue running.\n    \n    **Workaround**: If you encounter this issue\n    you must manually kill the Redis processes.\n\n- **Issue**: RLEC-8283 - The cluster recovery process does not work\n    properly when the cluster that needs to be recovered does not have a\n    node with ID 1.\n    \n    **Workaround**: If you encounter this issue please\n    [contact Redis support](https://redislabs.com/company/support/)\n\n- **Issue**: In the Replica Of process, if the target database does\n    not have replication enabled and it is restarted or fails for any\n    reason, the data on the target database might not be in sync with\n    the source database, although the status of the Replica Of process\n    indicates that it is.\n    \n    **Workaround**: You must manually stop and\n    restart the synchronization process in order to ensure the databases\n    are in sync.\n\n- **Issue**: In the Replica Of process, if the source database is\n    resharded while the Replica Of process is active, the\n    synchronization process will fail.\n\n    **Workaround**: You must manually\n    stop and restart the synchronization process after the resharding of\n    the source database is done.\n\n- **Issue**: In the Replica Of process, high database traffic might restart the Replica Of process due to the \"replica buffer\" being exceeded. In this case\n    you will often see the status of the Replica Of process display as\n    \"Syncing\".\n    \n    **Workaround**: You must manually increase the \"replica\n    buffer\" size through rladmin. In order to find the appropriate\n    buffer size please [contact Redis support](https://redislabs.com/company/support/)\n\n- **Issue**: In a cluster that is configured to support rack-zone\n    awareness, if the user forces migration of a master or replica shard\n    through rladmin to a node on the same rack-zone as its corresponding\n    master or replica shard, and later runs the rebalance process, the\n    rebalance process will not migrate the shards to ensure rack-zone\n    awareness compliance.\n    \n    **Workaround**: In the scenario described\n    above, you must use rladmin to manually migrate the shard, to a node\n    on a valid rack-zone in order to ensure rack-zone awareness\n    compliance."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/legacy-release-notes/rlec-4-4-dec-2016","title":"RLEC 4.4 Release Notes (December 2016)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rlec-4-4-dec-2016/","summary":"","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"c173fbae7f263213b9f41928b8addd0005ee90e1325207996cdacbd84373e28d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"If you are upgrading from a previous version, make sure to review the\n[upgrade\ninstructions](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading)\nbefore beginning the upgrade process.\n\nYou can upgrade to this version from any 4.3 version. If you have a\nversion older than 4.3 you must first upgrade to 4.3 and only then\nupgrade to this version."},{"id":"new-features","title":"New features","role":"content","text":"- Databases can now be configured to have multiple proxies for\n    improved performance. Note that when you upgrade the cluster to this\n    version and then upgrade existing databases, the databases will be\n    updated to use the Single proxy policy and Dense shard placement\n    policy. For additional details, refer to [Multiple active\n    proxies](https://redis.io/docs/latest/operate/rs/databases/configure/proxy-policy).\n- Support for Redis version 3.2 added. When you install or upgrade the\n    cluster the new default version for Redis databases will be 3.2 and\n    when you upgrade the databases they will be updated to this version.\n    If you would like to change the default version to Redis 3.0, refer\n    to the instruction in the [Upgrading\n    databases](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading)\n    If you would like to upgrade existing databases to the latest 3.0\n    minor version, refer to the Known Issues section below.\n- The cluster can now be configured to support both private and public\n    IPs to connect to database endpoints through both public and private\n    networks. For additional details, refer to [Private and Public\n    Endpoints](https://redis.io/docs/latest/operate/rs/networking/private-public-endpoints).\n- **rladmin status** command output has been enhanced to include an\n    indication on which node rladmin is running by adding the '\\*' sign\n    next to the node entry, and to show the host name of the machine the\n    node is running on.\n- Users can now be assigned security roles to control what level of\n    the databases or cluster the users can view and/or edit."},{"id":"changes","title":"Changes","role":"content","text":"- As result of adding the support for multiple proxies for a database,\n    the following changes have been made:\n    - When you upgrade the cluster to this version and then upgrade\n          existing databases, the databases will be updated to use the\n          Single proxy policy and Dense shard placement policy.\n    - **rladmin status** command output has been updated.\n    - **failover \\[db \\<db:id \\| name\\>\\] endpoint \\<id1 .. idN\\>**\n          and **migrate \\[db \\<db:id \\| name\\> \\| node \\<origin\n          node:id\\>\\] endpoint \\<id\\> target_node \\<id\\>** commands are\n          no longer relevant for databases using the **single \\|\n          all-master-shards \\| all-nodes** proxy policy. Instead proxies\n          can be bound or unbounded to databases as needed.\n    - New **rladmin** commands were added, such as **bind** and\n          **placement**.\n- RLEC has been updated to remove the need to use **sudo** in runtime.\n    You still need to be root or use **sudo** when initially installing\n    RLEC.\n- You no longer need to be root or use **sudo** to run the rladmin\n    command, now it is preferred to be a non-privileged user that is\n    member of the *redislabs* group to run the command.\n- All cluster services are now run using the supervisor mechanism. As\n    a result starting, stopping and restarting RLEC services should be\n    done using **supervisorctl** command from the OS CLI.\n- Linux OS vm.swappiness is now advised to be set to zero, for more\n    information see [Disabling Swap in\n    Linux](https://redis.io/docs/latest/operate/rs/installing-upgrading/configuring/linux-swap)."},{"id":"important-fixed-issues-since-430","title":"Important fixed issues since 4.3.0","role":"content","text":"- RLEC-7542 - Add ability to create and manage role based user\n    security\n- RLEC-8283 - The cluster recovery process does not work properly\n    when the cluster that needs to be recovered does not have a node\n    with ID 1.\n- RLEC-8284 - Add functionality to rladmin to mark a node as a quorum\n    only node\n- RLEC-8498 - Backup fails under rare conditions\n- RLEC-8579 - rladmin supports uppercase for external_addr value\n- RLEC-8656 - Fixed conflict with SELinux\n- RLEC-8687 - Fixed issue where strong password requirements were not\n    honored correctly.\n- RLEC-8694 - DMC failed while creating DB with 75 (150 replicated)\n    shards\n- RLEC-8700 - Fixed issue with network split scenario\n- RLEC-8833 - Fixed issue where in some cases endpoint were not\n    getting new IPs after node replacement.\n- RLEC-9069 - Fixed issue related to RHEL 7 and IPv6.\n- RLEC-9156 - Fixed issue causing a full resync of data when a source\n    or destination failure occurred.\n- RLEC-9173 - Issue with writing data after master and replica failed\n- RLEC-9235 - Issue with SSL connection error and self signed\n    certificates\n- RLEC-9491 - Fixed alerting issue due to incorrect measurement\n- RLEC-9534 - Fixed issue with node remove command after RLEC\n    uninstalled\n- RLEC-9658 - Failed to import backup file from FTP server.\n- RLEC-9737 - Fixed issue with backup process to use ephemeral\n    storage when needed\n- RLEC-9761 - UI had incorrect value increments\n- RLEC-9827 - Server with a high number of cores and running RHEL can\n    have issues running systune.sh\n- RLEC-9853 - Fixed issues with logrotate on RHEL 7.1 so it runs as\n    non-privileged user\n- RLEC-9858 - If proxy crashed, in some cases this would prevent\n    completion of redis failover process\n- RLEC-9893 - DB recovery process doesn't recognize original rack\n    name when in uppercase\n- RLEC-9905 - x.509 certificate signed by custom CA cannot be loaded\n    in UI\n- RLEC-9925 - master endpoint and shards goes down if co-hosted with\n    master of the cluster and the node goes down (single proxy policy)\n- RLEC-9926 - Master shard could remain down if on the same node as\n    the master of the cluster and the entire node goes down\n- RLEC-10340 - Fixed a typo that crashed rladmin status output in\n    some cases\n\nChanges in 4.4.2-42:\n\n- RLEC-11941 - Upgrade to 4.4.2-35 on RHEL6 - leash failed when\n    python2.6 is installed\n- RLEC-11994 - RLEC 4.4.2-35: the UI doesn't display the DBs with\n    replication\n\nChanges in 4.4.2 - 49\n\n- RLEC-11209 - Unable to run upgrade due to running_actions check\n- RLEC-12647 - Backup to S3 with periods in bucket name are failing\n    in some cases"},{"id":"known-issues","title":"Known issues","role":"content","text":"- **Issue:** When upgrading to this version from a previous RLEC\n    version, **rladmin status** output will show the database status as\n    having an old version. When you upgrade the Redis database (using\n    **rladmin upgrade db** command) the Redis version will be updated to\n    3.2 even if you updated the cluster's Redis default version to 3.0.\n    **Workaround:** If you would like to cancel the old version\n    indication in **rladmin status** without upgrading the Redis version\n    to 3.2 you should run the **rladmin upgrade db** command with the\n    **keep_current_version** flag which will ensure the database is\n    upgraded to the latest 3.0 version supported by RLEC.\n- **Issue:** RLEC-9200 - in a database configured with multiple\n    proxies, if a client sends the MONITOR, CLIENT LIST or CLIENT KILL\n    commands, only commands from clients connected from the same proxy\n    are returned instead of all commands from all connections.\n    **Workaround:** If you would like to get a result across all\n    clients, you need to send the monitor command to all proxies and\n    aggregate them.\n- **Issue:** RLEC-9296 - Different actions in the cluster, like node\n    failure or taking a node offline, might cause the Proxy policy to\n    change Manual.\n    **Workaround:** You can use the **rladmin bind \\[db \\<db:id \\|\n    name\\>\\] endpoint \\<id\\> policy \\<single \\| all-master-shards \\|\n    all-nodes\\>** command to set the policy back to the required policy,\n    which will ensure all needed proxies are bounded. Note that existing\n    client connections might disconnected as result of this process.\n- **Issue:** RLEC-8787 - In some cases when using the replica-of\n    feature, if the source database(s) are larget than the target\n    database, the memory limit on the target database is not enforced\n    and that used memory of the target database can go over the memory\n    limit set.\n    **Workaround:** You should make sure that the total memory limit of all\n    source databases is not bigger than the memory limit of the target\n    database.\n- **Issue:** RLEC-8487 - Some Redis processes stay running after\n    purging RLEC from the machine and causes an attempt to reinstall\n    RLEC to fail.\n    **Workaround:** Run the purge process for a second time and ensure\n    that the Redis processes were removed.\n- **Issue:** RLEC-8747 - When upgrading to this version, if the UI is\n    open in the browser the UI might not work properly after the\n    upgrade.\n    **Workaround:** Refresh the browser and the UI will return to work\n    properly.\n- **Issue:** In the Replica Of process, if the target database does\n    not have replication enabled and it is restarted or fails for any\n    reason, the data on the target database might not be in sync with\n    the source database, although the status of the Replica Of process\n    indicates that it is.\n    **Workaround:** You must manually stop and restart the\n    synchronization process in order to ensure the databases are in\n    sync.\n- **Issue:** In the Replica Of process, if the source database is\n    resharded while the Replica Of process is active, the\n    synchronization process will fail.\n    **Workaround:** You must manually stop and restart the\n    synchronization process after the resharding of the source database\n    is done.\n- **Issue:** In the Replica Of process, if there is very high\n    traffic on the database the Replica Of process might be restarted\n    frequently due to the \"replica buffer\" being exceeded. In this case,\n    you will often see the status of the Replica Of process display as\n    \"Syncing\".\n    **Workaround:** You must manually increase the \"replica buffer\" size\n    through rladmin. To find the appropriate buffer size please contact\n    support at: <support@redislabs.com>.\n- **Issue:** In a cluster that is configured to support rack-zone\n    awareness, if the user forces migration of a master or replica shard\n    through rladmin to a node on the same rack-zone as its corresponding\n    master or replica shard, and later runs the rebalance process, the\n    rebalance process will not migrate the shards to ensure rack-zone\n    awareness compliance.\n    **Workaround:** In the scenario described above, you must use\n    rladmin to manually migrate the shard to a node on a valid rack-zone\n    in order to ensure rack-zone awareness compliance.\n- **Issue:** DNS doesn't change after having removed the external IP\n    address.\n    **Workaround:** Unbind IP from affected node and then bind it back.\n- **Issue**: CCS gets an error and won't start\n    if /var/opt/redislabs/persist/ does not exist.\n    **Workaround**: Make sure this directory is not deleted and\n    continues to exist."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/legacy-release-notes/rs-5-2-2-august-2018","title":"Redis Enterprise Software 5.2.2 (August 2018)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-2-2-august-2018/","summary":"","tags":["docs","operate","rs"],"last_updated":"2025-09-03T12:06:34-05:00","page_type":"content","content_hash":"3ea2c55dd9743648d6f25c266c7a5b050462dc10ea46773296ddcce01a4e9520","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"Redis Enterprise Software (RS) 5.2.2 is now available.\n\nRS 5.2.2 is a minor version that includes important fixes and minor enhancements to Redis Enterprise 5.2."},{"id":"overview","title":"Overview","role":"overview","text":"If you are upgrading from a previous version, make sure to review the upgrade instructions before beginning the upgrade process. You can upgrade to RS 5.2.2 from RS 4.5 and above. If you have a version older than 4.5, you should first upgrade to version 5.0 (or at least 4.5)."},{"id":"new-capabilities","title":"New capabilities","role":"content","text":"Support for Redis version 4.0.10\nRediSearch Enterprise, which is installed with Redis Enterprise Software by default, has been updated to a newer version (1.4.0)"},{"id":"important-fixes","title":"Important fixes","role":"content","text":"- RED-21080 – Fixed the memory limit calculation for RoF databases\n- RED-20825 – Updated the ‘RAM limit’ of RoF databases to a range between 10% and 100%\n- RED-20506 – Fixed high CPU and File Descriptors utilization by the node watchdog process (node_wd)\n- RED-20275 – Fixed wrong metrics values while importing a dataset into an RoF database\n- RED-20214 – Fixed obstacles to login to the UI when using an LDAP integration\n- RED-20162 – Fixed known limitation of being able to activate \"Require SSL for All Communication\" to Redis Enterprise CRDBs via Rest API without providing a certificate\n- RED-19758 – Upgraded NGINX from 1.10.3 to 1.13.12\n- RED-19415, RED-18945 – Improved support for Lettuce client with OSS Cluster enabled\n- RED-19287 – Fixed a scenario of a stuck shard migration process\n- RED-18459 – Updated the data persistence (AOF / Snapshot) of RoF databases to be handled, by default, by the replica shard/s\n- RED-20541– Improved handling of aof file when its tail is corrupted (using aof-load-corrupt-tail flag)\n- RED-21936 – Improved handling of CRDB configuration update when URL parameter was supplied at creation time\n- RED-19760 – Added the capability to control the minimum TLS version that can be used for encrypting the Discovery Service data"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"- When updating the general settings of a cluster, the ‘username’ and ‘password’ fields in the email server settings cannot be left empty. In case one wants to update the general settings and prefer to leave the ‘username’ and ‘password’ fields empty, the REST API should be used.\n- An issue prevents the user from defining ‘min_data_TLS_version’ on the source cluster when working with Replica Of or CRDB."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/legacy-release-notes/rs-5-2-june-2018","title":"Redis Enterprise Software Release Notes 5.2 (June 2018)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-2-june-2018/","summary":"","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"435d06b65dc5673615e4a926854ad8bc8a1d9ea673d8b865a0c3732bbdf02d97","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"Redis Enterprise Software (RS) 5.2 is now available. Key features\ninclude new data types and casual consistency for active-active (also\nknown as the Redis CRDT or CRDB Conflict-free Replicated Database), as\nwell as enhanced security features."},{"id":"overview","title":"Overview","role":"overview","text":"If you are upgrading from a previous version, make sure to review the\nupgrade instructions before beginning the upgrade process. You can\nupgrade to RS 5.2 from RS 4.5 and above. If you have a version older\nthan 4.5, you should first upgrade to version 5.0 (or at least\n4.5)."},{"id":"new-features","title":"New features","role":"content","text":""},{"id":"activeactive-redis-crdb-supports-sorted-sets-and-lists","title":"Active-Active Redis (CRDB) supports sorted sets and lists","role":"content","text":"RS 5.2 adds active-active Redis (CRDB) support for Sorted Sets and\nLists. Now all major Redis data types are supported with CRDT, so you\ncan use Redis Enterprise in an active-active manner for all your Redis\nuse cases, with seamless conflict resolution. [Click here](https://redis.io/docs/latest/operate/rs/databases/active-active/develop/develop-for-aa)\nfor more information about how to develop applications with\ngeo-replicated CRDBs."},{"id":"causal-consistency-in-activeactive-redis-crdb","title":"Causal consistency in Active-Active Redis (CRDB)","role":"content","text":"casual consistency in active-active Redis (CRDB) guarantees that the\norder of operations on a specific key will be maintained across all CRDB\ninstances. For instance, if operations A and B were applied on the same\nkey, and B was performed after the effect of A was observed by the CRDB\ninstance that initiated B, then all CRDB instances would observe the\neffect of A before observing the effect of B.\n\nThis way, any causal relationship between operations on the same key is\nalso observed and maintained by every replica. Such capability is\nimportant for various applications, e.g. social network status updates\nor chat applications (assuring that the chronology of messages doesn't\nget mixed up). For more information, [click\nhere](https://redis.io/docs/latest/operate/rs/databases/active-active/causal-consistency)."},{"id":"enhanced-security-features","title":"Enhanced security features","role":"content","text":"- **Admin action audit trail**\n\nManagement actions performed with Redis Software are audited in order\nto fulfill two major objectives: (1) make sure that system management tasks\nare appropriately performed and/or monitored by the Administrator(s),\nand (2) facilitate compliance with regulatory standards such as HIPAA,\nSOC2, PCI, etc.\n\nIn order to fulfill both objectives, audit records contain the\nfollowing information: (1) who performed the action, (2) what exactly\nwas the performed action, (3) when the action was performed, and (4)\nwhether  the action succeeded.\n\nTo get the list of audit records/events, one can use the REST API or\nthe Log page in the UI; the Log page displays the system and user events\nregarding alerts, notifications and\nconfigurations.\n\n- **Ability to disable TLS versions**\n\nVersion 5.2 introduces the option to set the minimum TLS version that\ncan be used for encrypting the various flows. You can do so using the\nREST API or the following rladmin commands:\n\n- For the management UI and the REST API:\n\n[code example]\n\n- For the data path encryption:\n\n[code example]\n\nNote that communications that use older TLS versions will not be\nallowed.\n\n- **HTTPS enforcement**\n\nWith this feature, you can restrict REST API  access to only those\nusing HTTPS. In order to do so, use the REST API or the following\nrladmin command:\n\n[code example]"},{"id":"additional-capabilities","title":"Additional capabilities","role":"content","text":"- Support for Redis version 4.0.9\n- Fixes for Redis important security issues related to the Lua\n    scripting engine"},{"id":"information","title":"Information","role":"content","text":"- The end-of-service-life (EOSL) for Redis Enterprise Software 4.4.X\n    is June 30th, 2018, in accordance with our [published\n    policy](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle).\n    We recommend that customers running version 4.4 or below\n    upgrade to the latest version."},{"id":"important-fixes","title":"Important fixes","role":"content","text":"- RS19755 - Added the ability to enforce HTTPS communication when\n    accessing REST API\n- RS19571 - Fixed RHEL installation when FIPS mode is\n    enabled\n- RS19490 - Fixed a failure while upgrading from 4.5 to\n    5.0.2\n- RS19475 - Added the ability to disable TLS versions for the\n    control and data paths\n- RS18712 - Fixed a failure in endpoint failover\n\n- RS17208 - Added an option to disable Redis commands via Rest\n    API\n- RS14973 - Updated NGINX.conf template to turn off exposing the\n    server version\n- RS11181 - Set file permissions of log files, redis-conf files, rdb\n    files and their rotations to \"640\""},{"id":"known-limitations","title":"Known limitations","role":"content","text":"- After creating Redis Enterprise CRDBs which \"Require SSL for CRDB\n    communication only,\" one can set definitions via Rest API to\n    activate \"Require SSL for All Communication\" without providing a\n    certificate. In such case, connections to the cluster will be\n    blocked."}],"examples":[{"id":"enhanced-security-features-ex0","language":"sh","code":"rladmin> cluster config min_control_TLS_version [version, e.g. 1.2]","section_id":"enhanced-security-features"},{"id":"enhanced-security-features-ex1","language":"sh","code":"rladmin> cluster config min_data_TLS_version [version, e.g. 1.2]","section_id":"enhanced-security-features"},{"id":"enhanced-security-features-ex2","language":"sh","code":"rladmin> cluster config http_support [disabled | enabled]","section_id":"enhanced-security-features"}]}
{"schema_version":2,"id":"operate/rs/release-notes/legacy-release-notes/rs-5-3-beta-july-2018","title":"Redis Enterprise Software Release Notes 5.3 BETA (July 2018)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-3-beta-july-2018/","summary":"","tags":["docs","operate","rs"],"last_updated":"2023-12-14T15:27:49+01:00","page_type":"content","content_hash":"109d04ff4227cbc905e30ace71b2f3a1e29dd5e7f058c463a6bdf0ec640b1b77","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise Software (RS) 5.3 is now available.\n\nRS 5.3 is a preview version that includes all the capabilities of Redis Enterprise 5.2, plus support for Redis 5.0 with the new data type, which is called Streams."},{"id":"new-features","title":"New features","role":"content","text":"RS 5.3 adds support for Redis 5.0 and is based on its latest Release Candidate (RC3). Redis 5.0 exposes the new Redis Streams data type, which provides a super fast in-memory abstraction of an append-only log. For more information and usage examples, check out the Streams documentation here."},{"id":"preview-considerations","title":"Preview considerations","role":"content","text":"As a preview version, we do not support upgrading to RS 5.3 from any previous version or upgrading from RS 5.3 to any future version. In addition, we do not commit to fixing bugs in RS 5.3."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"The RS 5.3 preview version does not support Enterprise Modules (- RediSearch, ReJSON and ReBloom), Auto Tiering or active-active Redis (CRDB)."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/legacy-release-notes/rs-5-4-10-december-2019","title":"Redis Enterprise Software Release Notes 5.4.10 (December 2019)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-10-december-2019/","summary":"","tags":["docs","operate","rs"],"last_updated":"2026-10-06T08:53:14-07:00","page_type":"content","content_hash":"195510c9343cd16a812750e84adb3c86c54921540a35e7026176a01cafb37509","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"[Redis Enterprise Software (RS) 5.4.10](https://redislabs.com/redis-enterprise/software/downloads/#downloads) is now available.\nThis release includes an improved synchronization mechanism for Active-Active Redis and Replica-of, several enhancements, and bug fixes."},{"id":"overview","title":"Overview","role":"overview","text":"Follow these [instructions](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading) for upgrading to RS 5.4.10 from RS 5.0 and above."},{"id":"new-features","title":"New features","role":"content","text":""},{"id":"synchronization-mechanism-in-active-active-redis-and-replica-of","title":"Synchronization mechanism in Active-Active Redis and Replica-of","role":"content","text":"RS 5.4.10 incorporates the improved [Redis synchronization mechanism](https://redis.io/docs/latest/operate/rs/databases/active-active#syncer-process) ([PSYNC2](https://redis.io/docs/latest/operate/oss_and_stack/management/replication)) for Active-Active Redis (CRDB) and Replica-of.\n\nAs a result, failure scenarios in any A-A replica (and the source database of Replica-of), require only partial synchronization between the cross-region replicas instead of full synchronization that can be costly in time and bandwidth."},{"id":"rs-on-rhel-7-supports-openssl-102-and-up","title":"RS on RHEL 7 supports OpenSSL 1.0.2 and up","role":"content","text":"To keep RS secure and keep our internal libraries up-to-date, starting from RS 5.4.10 our RHEL 7 installations require a minimum of OpenSSL 1.0.2.\n\nWhen you install or upgrade RS 5.4.10 on RHEL 7 with older version of OpenSLL, the installation fails with the error:\n\n[code example]\n\nIf you see this error, upgrade to OpenSSL 1.0.2 or higher before you install RS."},{"id":"additional-capabilities","title":"Additional capabilities","role":"content","text":"- The following new GA releases of Redis Modules are bundled in RS 5.4.10:\n    - RedisBloom, version 2.0.3\n    - RedisSearch, version 1.4.18\n    - RedisJson, version 1.0.4\n    - RedisGraph, version 1.2.2\n    - RedisTimeSeries, version 1.0.3\n- Version 5.0.5 of Redis OSS, along with a fix to a corruption related to the HyperLogLog (that is part of [Redis 5.0.6](https://raw.githubusercontent.com/antirez/redis/5.0/00-RELEASENOTES)) are merged into RS 5.4.10.\n- Using REST API, you can retrieve various license details such as activation date, expiration date,and the number of licensed shards. To get these details, run:\n\n    `curl -v -u <user>:<password> https://localhost:9443/v1/license`\n\n- Updated PDNS version from 4.1.5 to 4.1.13."},{"id":"information","title":"Information","role":"content","text":"- End of Life (EOL) for Redis Enterprise Software 5.4, as well as for Redis Modules and previous RS versions, can be found [here](https://docs.redis.com/latest/rs/installing-upgrading/product-lifecycle).\n- Google Chrome browser on macOS Catalina requires self-signed certificate generated after June 2019 to include the extendedKeyUsage field in order to connect to the RS admin console.\n    If you use a self-signed certificate that does not include this field, [update the self-signed certificate](https://redis.io/docs/latest/operate/rs/security/certificates/updating-certificates).\n- When you upgrade an Active-Active Redis with active AOF from version RS 5.4.2 or lower to version RS 5.4.4 or higher:\n    - If replication is enabled, you must run the BGREWRITEAOF command on all replica shards after the upgrade.\n    - If replication is not enabled, you must run the BGREWRITEAOF command on all shards after the upgrade."},{"id":"important-fixes","title":"Important fixes","role":"content","text":"- The titles of the ‘rladmin status nodes’ command output were updated from ‘RAM’ to ‘FREE_RAM’ (the amount of RAM in the node that is currently not used) and from ‘AVAILABLE_RAM’ to ‘PROVISIONAL_RAM’ (the amount of RAM in the node that can be provisioned).\n- RS31492 - Upgraded dependent libraries: [python-cryptography to version 2.7](https://github.com/redislabsdev/Redis-Enterprise/pull/4209/commits/3b5a408696b91a0b545f670ce35bb920d5a4beb4); [NGINX to version 1.16.0](https://github.com/redislabsdev/Redis-Enterprise/pull/4209/commits/4ab171d4467bd91c6b38cec81da3c52a6113a787); [PyYaml to version 5.1.2](https://github.com/redislabsdev/Redis-Enterprise/pull/4209/commits/11e814ae0d14c85b248bc7451edbbbcb71f3858f); [python-requests to version 2.22.0](https://github.com/redislabsdev/Redis-Enterprise/pull/4209/commits/0e2ab74b4e2b2dc9872a86fbdb5593f5354eb103); [urllib3 to version 1.25.3](https://github.com/redislabsdev/Redis-Enterprise/pull/4209/commits/038e44163d7dc1fed4e3b67cb252a84583c2f44a)\n- RS31187 - Upgraded the internal Python interpreter to version 2.7.16\n- RS33042 - Fixed Support Package to contain complete SLOWLOG information\n- RS32699 - Avoided unnecessary restart and failover of Redis processes when Active-Active database is upgraded\n- RS32061 - Improved support of the Redis WAIT command\n- RS31759 - Fixed failure during database import\n- RS31747 - Fixed failure in upgrade from version 5.0.0-31 to 5.4.6-11\n- RS30063 - Fixed the upgrade process when WatchdogAPI fails to bind to its port\n- RS31477 - Fixed wrong calculation of node’s ‘AVAILABLE_RAM’ (‘PROVISIONAL_RAM’) as displayed the output of ‘rladmin status nodes’ command\n- RS30165 - Fixed failover scenario that did not take place during node restart\n- RS29250 - REST API documentation was updated to include the SFTP and Mount Point backup/export options\n- RS27327 - Improved the backup timing when using the database parameter of ‘backup_interval_offset’ through the REST API\n- RS33883 - HCSAN command in Active-Active Redis updated to return Integer instead of a String.\n- Fixed a limitation so Redis 5 and Redis 4 can be selected as the Redis version to use CRDB and RoF"},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"upgrade","title":"Upgrade","role":"content","text":"- [RS 5.4.2](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-2-april-2019) introduced new Active-Active Redis (CRDB) capabilities\n    that improve its compatibility with open source Redis.\n    Now the string data-type in Active-Active Redis (CRDB) is implicitly and dynamically typed, just like open source Redis.\n    To use the new capabilities on nodes that are upgraded from version RS 5.4.2 or lower,\n    you must [upgrade the CRDB protocol](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading#upgrading-crdbs).\n- Before you upgrade a database with RediSearch Module to Redis 5.0,\n    you must [upgrade the RediSearch Module](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/upgrade-module) to version 1.4.2 or above.\n- Node upgrade fails if the SSL certificates were configured in version 5.0.2 or above\n    by manually updating the certificates on the disk instead of [updating them through the API](https://redis.io/docs/latest/operate/rs/security/certificates/updating-certificates).\n    For assistance with this issue, [contact Redis support](https://redislabs.com/company/support/).\n- We recommend that you test module upgrade commands in a test environment before you upgrade modules in a production environment.\n    The module upgrade arguments are not validated during the upgrade process and incorrect arguments can cause unexpected downtime.\n- Starting from RS 5.4.2, to preserve the current Redis major.minor version during database upgrade you must use the `keep_redis_version` option instead of `keep_current_version`."},{"id":"cluster-api","title":"Cluster API","role":"content","text":"- The API for removing a node is updated in RS 5.4.2 or higher. The API call must include json data and the \"Content-Type: application/json\" header. For example:\n\n    `curl -X POST -H \"Content-Type: application/json\" -i -k -u user@redislabs.com:password https://localhost:9443/v1/nodes/3/actions/remove --data \"{}\"`"},{"id":"discovery-service","title":"Discovery service","role":"content","text":"- For [Redis Sentinel (Discovery Service)](https://redis.io/docs/latest/operate/rs/databases/durability-ha/discovery-service), every database name must be unique across the cluster."},{"id":"redis-commands","title":"Redis commands","role":"content","text":"- The capability of disabling specific Redis commands does not work on commands specific to Redis Modules.\n- The CLIENT ID command cannot guarantee incremental IDs between clients that connect to different nodes under multi proxy policies.\n- CLIENT UNBLOCK command is not supported in RS 5.4 and above\n- Starting from RS 5.4.2 and after upgrading the CRDB, TYPE command for string data-type in CRDBs return \"string\" (OSS Redis standard)."}],"examples":[{"id":"rs-on-rhel-7-supports-openssl-102-and-up-ex0","language":"sh","code":"Error: Package: redislabs-5.4.10-1.rhel7.x86_64 (/redislabs-5.4.10-1.rhel7.x86_64)\n        Requires: libcrypto.so.10(OPENSSL_1.0.2)(64bit)","section_id":"rs-on-rhel-7-supports-openssl-102-and-up"}]}
{"schema_version":2,"id":"operate/rs/release-notes/legacy-release-notes/rs-5-4-14-february-2020","title":"Redis Enterprise Software Release Notes 5.4.14 (February 2020)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-14-february-2020/","summary":"","tags":["docs","operate","rs"],"last_updated":"2026-10-06T08:53:14-07:00","page_type":"content","content_hash":"0224eab6b5eb12fcbaff0a8c450fa9c4aaa13a87ec7550ea8dd4160aa67ea9da","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"[Redis Enterprise Software (RS) 5.4.14](https://redislabs.com/redis-enterprise/software/downloads/#downloads) is now available.\nThis release bundles OSS Redis 5.0.7 and includes new Redis Modules versions, several enhancements, and bug fixes."},{"id":"overview","title":"Overview","role":"overview","text":"Follow these [instructions](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading) for upgrading to RS 5.4.14 from RS 5.0.2 and above."},{"id":"new-features","title":"New features","role":"content","text":"- Version [5.0.7](https://raw.githubusercontent.com/antirez/redis/5.0/00-RELEASENOTES) of OSS Redis is merged into RS 5.4.14.\n- The following GA releases of Redis Modules are bundled in RS 5.4.14:\n    - [RedisBloom](https://redislabs.com/redis-enterprise/redis-bloom/), version 2.2.1 (updated, [release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.2-release-notes#redisbloom-221-january-2020))\n    - [RedisGraph](https://redislabs.com/redis-enterprise/redis-graph/), version 2.0.1 (updated, [release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.0-release-notes#redisgraph-20-ga-201---january-2020))\n    - [RedisJSON](https://redislabs.com/redis-enterprise/redis-json/), version 1.0.4 (update, [release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-1.0-release-notes))\n    - [RediSearch](https://redislabs.com/redis-enterprise/redis-search/), version 1.4.25 (updated, [release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-1.4-release-notes#redisearch-1425-march-2020))\n    - [RedisTimeSeries](https://redislabs.com/redis-enterprise/redis-time-series/), version 1.2.3 (updated, [release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.2-release-notes))"},{"id":"additional-capabilities","title":"Additional capabilities","role":"content","text":"- Added the ability to retrieve license details with a REST API command.\n    Now you can get your license details from the admin console (settings > general) or from the REST API command:\n\n    `GET https://localhost:9443/v1/license`\n\n    The REST API response includes:\n\n    - license - The cluster’s name (FQDN) in the key string\n    - expired - If the cluster key is expired (True or False)\n    - activation_date - The date of the cluster key activation\n    - expiration_date - The date of the cluster key expiration\n    - shards_limit - The number of shards allowed by the cluster key\n- Added Flush command in the UI console for Active-Active databases. The command flushes the data from all participating clusters.\n- Updated `rladmin upgrade module` command so module arguments are optional. When you upgrade the module for a database, you can either:\n    - Specify the module arguments to replace the existing arguments\n    - Specify the `keep_module_args` flag to use the existing arguments\n\n    Examples:\n\n    1. To upgrade the version of RediSearch to 10017 and replace the module arguments:\n\n        `rladmin upgrade module db_name MyAwesomeDB module_name ft version 10017 module_args \"PARTITIONS AUTO\"`\n\n    1. To upgrade RedisBloom to version 10100 and remove the current module arguments:\n\n        `rladmin upgrade module db_name MyDB module_name bf version 10100 module_args \" \"`\n\n    1. To upgrade RedisJSON to 10002 and use the current module arguments:\n\n        `rladmin upgrade module db_name MyDB module_name ReJSON version 10002 keep_module_args`\n\n- Upgraded `js-yaml` version to 3.13.1, and `lodash` version to 4.17.15 (RS31819)\n- Added an alert for actions that run for an extended period of time. For example, a notification is sent when an action like updating database property runs for more than a configurable threshold. Default value is set to 24 hours."},{"id":"information","title":"Information","role":"content","text":"- End of Life (EOL) for Redis Enterprise Software 5.4, as well as for Redis Modules and previous RS versions, can be found [here](https://docs.redis.com/latest/rs/installing-upgrading/product-lifecycle).\n- Google Chrome browser on macOS Catalina requires self-signed certificate generated after June 2019 to include the extendedKeyUsage field in order to connect to the RS admin console.\n    If you use a self-signed certificate that does not include this field, [update the self-signed certificate](https://redis.io/docs/latest/operate/rs/security/certificates/updating-certificates).\n- When you upgrade an Active-Active Redis with active AOF from version RS 5.4.2 or lower to version RS 5.4.4 or higher:\n    - If replication is enabled, you must run the BGREWRITEAOF command on all replica shards after the upgrade.\n    - If replication is not enabled, you must run the BGREWRITEAOF command on all shards after the upgrade."},{"id":"important-fixes","title":"Important fixes","role":"content","text":"- RS23396 - Improved the disconnection mechanism in case of inactive UI session\n- RS27924 - Added descriptive error messages in the UI console when validating the license\n- RS29968 - Improved internal mechanism to better support high scale of clients connections\n- RS35675 - Updated the upgrade process of Active-Active Redis databases to save causal consistency and encryption flags\n- RS36922 - Fixed an issue in a specific cluster upgrade scenario from versions earlier than RS 5.4.0 to RS 5.4.0 or higher"},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"upgrade","title":"Upgrade","role":"content","text":"- When you upgrade a cluster from version 5.0.2-20 to version 5.4.14, you must first upgrade to version 5.2.2 and then to version 5.4.14.\n- [RS 5.4.2](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-2-april-2019) introduced new Active-Active Redis (CRDB) capabilities\n    that improve its compatibility with open source Redis.\n    Now the string data-type in Active-Active Redis (CRDB) is implicitly and dynamically typed, just like open source Redis.\n    To use the new capabilities on nodes that are upgraded from version RS 5.4.2 or lower,\n    you must [upgrade the CRDB protocol](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading#upgrading-crdbs).\n- Before you upgrade a database with RediSearch Module to Redis 5.0,\n    you must [upgrade the RediSearch Module](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/upgrade-module) to version 1.4.2 or above.\n- Node upgrade fails if the SSL certificates were configured in version 5.0.2 or above\n    by manually updating the certificates on the disk instead of [updating them through the API](https://redis.io/docs/latest/operate/rs/security/certificates/updating-certificates).\n    For assistance with this issue, [contact Redis support](https://redislabs.com/company/support/).\n- We recommend that you test module upgrade commands in a test environment before you upgrade modules in a production environment.\n    The module upgrade arguments are not validated during the upgrade process and incorrect arguments can cause unexpected downtime.\n- Starting from RS 5.4.2, to preserve the current Redis major.minor version during database upgrade you must use the `keep_redis_version` option instead of `keep_current_version`."},{"id":"cluster-api","title":"Cluster API","role":"content","text":"- The API for removing a node is updated in RS 5.4.2 or higher. The API call must include json data and the \"Content-Type: application/json\" header. For example:\n\n    `curl -X POST -H \"Content-Type: application/json\" -i -k -u user@redislabs.com:password https://localhost:9443/v1/nodes/3/actions/remove --data \"{}\"`"},{"id":"discovery-service","title":"Discovery service","role":"content","text":"- For [Redis Sentinel (Discovery Service)](https://redis.io/docs/latest/operate/rs/databases/durability-ha/discovery-service), every database name must be unique across the cluster."},{"id":"redis-commands","title":"Redis commands","role":"content","text":"- The capability of disabling specific Redis commands does not work on commands specific to Redis Modules.\n- The CLIENT ID command cannot guarantee incremental IDs between clients that connect to different nodes under multi proxy policies.\n- CLIENT UNBLOCK command is not supported in RS 5.4 and above\n- Starting from RS 5.4.2 and after upgrading the CRDB, TYPE command for string data-type in CRDBs return \"string\" (OSS Redis standard)."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/legacy-release-notes/rs-5-4-2-april-2019","title":"Redis Enterprise Software Release Notes 5.4.2 (April 2019)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-2-april-2019/","summary":"","tags":["docs","operate","rs"],"last_updated":"2026-10-06T08:53:14-07:00","page_type":"content","content_hash":"57473ea04e21158b435659fc67d0a413c77482e910844d529ddb61ac4304a2b3","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"[Redis Enterprise Software (RS) 5.4.2](https://redislabs.com/redis-enterprise/software/downloads/#downloads) is now available.\nThis release improves the compatibility of Active-Active Redis (CRDB) with open source Redis, adds SFTP and Mount Points as backup destinations, email alerting and a number of other enhancements and bug fixes."},{"id":"overview","title":"Overview","role":"overview","text":"Follow these [instructions](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading) for upgrading to RS 5.4.2 from RS 5.0 and above.\nIf you have a version older than 5.0, you must first upgrade to version 5.2 (or at least 5.0)."},{"id":"new-features","title":"New features","role":"content","text":""},{"id":"activeactive-redis-crdb","title":"Active-Active Redis (CRDB)","role":"content","text":"RS 5.4.2 introduces new Active-Active Redis (CRDB) capabilities which improve its compatibility with open source Redis.\nThis simplifies the progress of migration from open source Redis to Active-Active Redis:\n\n1. The string data-type in CRDB is now implicitly and dynamically typed, just like open source Redis.\n1. Geospatial data is now supported in CRDB.\n\nThese changes required upgrading the internal structures of CRDB and the communication protocol between the CRDB replicas.\nTo take advantage of these updates with existing CRDBs you must [upgrade the CRDB protocol](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading#upgrading-crdbs/)."},{"id":"new-backup-destinations---sftp-and-mount-point","title":"New backup destinations - SFTP and mount point","role":"content","text":"The storage options for [periodic database backup](https://redis.io/docs/latest/operate/rs/databases/import-export/schedule-backups), [data export](https://docs.redis.com/latest/rs/databases/import-export/export-data.md), and [data import](https://docs.redis.com/latest/rs/databases/import-export/import-data/) are extended to include:\n\n- SFTP - a secured and well accepted protocol\n- A local mount path that can point to network storage\n\nThese new options are in addition to the existing backup locations of FTP, AWS S3 and OpenStack Swift."},{"id":"email-alerts-per-database-per-team-member","title":"Email alerts per database (per team member)","role":"content","text":"Improved distribution of [email alerts](https://redis.io/docs/latest/operate/rs/security/access-control/create-users) for cluster or database alerts.\nThe cluster administrator can define, for each team member, the specific databases to receive alerts for, and whether to get the cluster alerts."},{"id":"optional-client-authentication-tls","title":"Optional client authentication (TLS)","role":"parameters","text":"You can now fine tune the [TLS configuration](https://redis.io/docs/latest/operate/rs/security/encryption/tls), and ease certificates management by excluding client authentication enforcement,\nso that the database clients, such as applications or other clusters, can connect to your database without authentication."},{"id":"node-maintenance-mode","title":"Node maintenance mode","role":"content","text":"When you do hardware or operating system maintenance on a server that hosts an Redis Enterprise node,\nit is important that you move all of the shards on that node to another node to protect the data.\nStarting with RS 5.4.2 you can use [maintenance mode](https://redis.io/docs/latest/operate/rs/clusters/maintenance-mode) to handle this process simply and efficiently."},{"id":"additional-capabilities","title":"Additional capabilities","role":"content","text":"- Support for open source Redis version 5.0.4\n- Viewer roles were further limited to improve security compliance. The DB Viewer and Cluster Viewer roles cannot view the Redis password of a database. The DB Viewer role also cannot view the ‘log’ screen.\n\n    > [!NOTE]\n    > These improvements are also included in the REST API and are breaking-changes.\n    > If you use the relevant API requests with a DB Viewer or Cluster Viewer role, make sure that you update it accordingly.\n\n- External user credentials for periodic backup destinations are now hidden.\n    In REST API responses the password is hashed and in the admin console the password is displayed as asterisks."},{"id":"information","title":"Information","role":"content","text":"- For Redis Enterprise Software 5.4 End of Life (EOL), as well as for previous Redis Enterprise Software versions, see [product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle)."},{"id":"important-fixes","title":"Important fixes","role":"content","text":"- RS26985 - Fixed a failure of a DB with all-nodes policy, when a node is rebooted\n- RS26433 - Fixed a scenario in which the CRDB sync stopped on one of the participating clusters\n- RS25968 - Fixed a redis crash on RoF cluster\n- RS25835 - Fixed a failure in periodic backup when AOF rewrite is in progress on the same shard\n- RS25558 - Enabled updating absolute path in periodic backup FTP setup\n- RS25381 - Fixed a race condition while updating a database parameter\n- RS24188 - Fixed a failure during database upgrade\n- RS23508 - Fixed a failure in ‘rladmin status’ command\n- RS22485 - Fixed irregularities in service after increasing the size of a database which uses OSS cluster API"},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"upgrade","title":"Upgrade","role":"content","text":"- Before you upgrade a database with RediSearch Module to Redis 5.0,\n    the RediSearch [Module must be upgraded](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/upgrade-module) to version 1.4.2 or above.\n- Node upgrade fails if the SSL certificates were configured in version 5.0.2 and above by updating the certificates on the disk instead of using the new API.\n    For assistance with this issue, see [SSL/TLS certificate updates](https://redis.io/docs/latest/operate/rs/security/certificates/updating-certificates) in our documentation or [contact Redis support](https://redislabs.com/company/support/).\n- We recommend that you test module upgrade commands in a test environment before you upgrade modules in a production environment.\n    The module upgrade arguments are not validated during the upgrade process and incorrect arguments can cause unexpected downtime.\n- The rladmin option to use to preserve the current Redis major.minor version during database upgrade is now `keep_redis_version`, instead of `keep_current_version`."},{"id":"cluster-api","title":"Cluster API","role":"content","text":"- The API for node removal was updated. It must include json data, and \"Content-Type: application/json\" header. For example:\n\n    [code example]"},{"id":"redis-commands","title":"Redis commands","role":"content","text":"- The capability of disabling specific Redis commands does not work on Redis Module specific commands.\n- The CLIENT ID command cannot guarantee incremental IDs between clients that connect to different nodes under multi proxy policies.\n- CLIENT UNBLOCK command is not supported in RS 5.4 and RS 5.4.2\n- Starting from RS 5.4.2 and after upgrading the CRDB, TYPE command for string data-type in CRDBs return \"string\" (OSS Redis standard)."}],"examples":[{"id":"cluster-api-ex0","language":"sh","code":"curl -X POST -H \"Content-Type: application/json\" -i -k -u user@redislabs.com:passsword https://localhost:9443/v1/nodes/3/actions/remove --data \"{}\"","section_id":"cluster-api"}]}
{"schema_version":2,"id":"operate/rs/release-notes/legacy-release-notes/rs-5-4-4-june-2019","title":"Redis Enterprise Software Release Notes 5.4.4 (June 2019)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-4-june-2019/","summary":"","tags":["docs","operate","rs"],"last_updated":"2026-10-06T08:53:14-07:00","page_type":"content","content_hash":"ce29ee4122e5dc8de3774b664e60e9effe60cc786eebc156f5cab7e24cd41b01","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"Redis Enterprise Software (RS) 5.4.4 is now available.\nThis release enables the functionality of Active-Active Redis (CRDB) combined with RoF (Auto Tiering),\nsupports the creation of Redis databases with multiple modules,\nand adds other enhancements and bug fixes."},{"id":"overview","title":"Overview","role":"overview","text":"Follow these [instructions for upgrading](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading) to RS 5.4.4 from RS 5.0 and above.\nIf you have a version older than 5.0, you must first upgrade to version 5.2 (or at least 5.0)."},{"id":"new-features","title":"New features","role":"content","text":""},{"id":"activeactive-redis-with-rof","title":"Active-Active Redis with RoF","role":"content","text":"RS 5.4.4 lets you create Active-Active Redis databases (CRDBs) with Auto Tiering and get the benefits of geo-distributed Redis databases along with the significant cost savings of using Auto Tiering.\n\n> [!NOTE]\n> You must select **Redis 5** as the Redis version to use CRDB and RoF."},{"id":"support-for-multiple-modules","title":"Support for multiple modules","role":"compatibility","text":"RS 5.4.4 supports the creation and management of Redis databases with a combination of any of the following GA modules:\n\n- RediSearch (GA)\n- RedisGraph (GA)\n- RedisBloom (GA)\n- RedisJSON (GA)\n\n\n\nTo upgrade databases with multiple modules, you can use the `rladmin upgrade` command.\n\n> [!NOTE]\n> The syntax for the `rladmin upgrade` command now requires that the module_args parameter be written inside quotation marks.\n\n- To upgrade only the modules within an existing database, for example with RedisBloom (bf) and RedisJSON:\n\n    [code example]\n\n- To upgrade a database with its modules, for example db1 with RediSearch and RedisGraph:\n\n    [code example]\n\nYou can also upgrade the modules with the REST API.\n\n- To upgrade multiple modules, enter the details of each module in the `modules` parameter:\n\n    [code example]\n\n- To upgrade a single module, you can either:\n    - Enter the details of the module in the `modules` parameter\n    - Enter the details of the module without the `modules` parameter, as in previous releases"},{"id":"additional-capabilities","title":"Additional capabilities","role":"content","text":"- Support for persistence of AOF every 1 second in Active-Active Redis (CRDB), in addition to AOF every write.\n    We recommend that you use AOF every 1 second for the best performance during the initial CRDB sync of a new replica.\n- NGINX version updated to 1.14.2"},{"id":"information","title":"Information","role":"content","text":"- Stay up to date with the [End of Life (EOL) Policy](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle) for RS 5.4 and all previous RS versions."},{"id":"important-fixes","title":"Important fixes","role":"content","text":"- RS26508 - Fixed redis-cli failure for a specific CRDB data type.\n- RS29191 - Fixed a failure when removing a replica from a CRDB.\n- RS29097 - Fixed a misconfiguration when using SFTP backup and encounter a node failure.\n- RS28286 - Updated `SETEX` and `PSETEX` commands output of CRDB to match Redis outputs.\n- RS26984 - Fixed metrics_exporter reports for node and shard level metrics.\n- RS19854 - Fixed uploading a Redis Module so you can upload a Redis Module when the admin console is connected to any node.\n- RS29238 - Improved the compression performance in CRDB."},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"upgrade","title":"Upgrade","role":"content","text":"- [RS 5.4.2](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-2-april-2019) introduced new Active-Active Redis (CRDB) capabilities that improve its compatibility with open source Redis. Now the string data-type in CRDB is implicitly and dynamically typed, just like open source Redis. To use the new capabilities on nodes that are upgraded from version RS 5.4.2 or lower, you must [upgrade the CRDB protocol](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading#upgrading-crdbs).\n- Before you upgrade a database with RediSearch Module to Redis 5.0, you must [upgrade the RediSearch Module](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/upgrade-module) to version 1.4.2 or above.\n- Node upgrade fails if the SSL certificates were configured in version 5.0.2 or above by manually updating the certificates on the disk instead of [updating them through the API](https://redis.io/docs/latest/operate/rs/security/certificates/updating-certificates). For assistance with this issue, [contact Redis support](https://redislabs.com/company/support/).\n- We recommend that you test module upgrade commands in a test environment before you upgrade modules in a production environment. The module upgrade arguments are not validated during the upgrade process and incorrect arguments can cause unexpected downtime.\n- Starting from RS 5.4.2, to preserve the current Redis major.minor version during database upgrade you must use the `keep_redis_version` option instead of `keep_current_version`."},{"id":"subscriptions","title":"Subscriptions","role":"content","text":"- If a user subscribes to a channel during recovery of a CRDB from AOF, the user receives old messages that are stored in the AOF file."},{"id":"cluster-api","title":"Cluster API","role":"content","text":"- The API for removing a node is updated in RS 5.4.2 or higher. The API call must include json data and the \"Content-Type: application/json\" header. For example:\n\n    [code example]"},{"id":"redis-commands","title":"Redis commands","role":"content","text":"- The capability of disabling specific Redis commands does not work on commands specific to Redis Modules.\n- The CLIENT ID command cannot guarantee incremental IDs between clients that connect to different nodes under multi proxy policies.\n- CLIENT UNBLOCK command is not supported in RS 5.4.x\n- Starting from RS 5.4.2 and after upgrading the CRDB, TYPE command for string data-type in CRDBs return \"string\" (OSS Redis standard)."}],"examples":[{"id":"support-for-multiple-modules-ex0","language":"sh","code":"rladmin upgrade module db_name db1 module_name bf version 10003 module_args \"\" module_name ReJSON version 10001 module_args \"\"","section_id":"support-for-multiple-modules"},{"id":"support-for-multiple-modules-ex1","language":"sh","code":"rladmin upgrade db db1 and module module_name redisearch version 103 module_args \"ON_TIMEOUT FAIL NOGC\" and module module_name graph version 10016 module_args \"\"","section_id":"support-for-multiple-modules"},{"id":"support-for-multiple-modules-ex2","language":"sh","code":"curl -X POST -u \"demo@redislabs.com:password\" -H \"Content-Type: application/json\" -d '{\"modules\":[{\"module_name\": \"ReJSON\", \"current_module\": \"<module_uid>\", \"new_module\": \"<module_uid>\", \"new_module_args\": \"\", \"current_semantic_version\":\"1.0.4\"}, {\"module_name\":\"ft\",\"current_module\":\"<module_uid>\",\"new_module\": \"<module_uid>\",\"current_semantic_version\":\"1.4.3\", \"new_module_args\":\"PARTITIONS AUTO\"}], \"force_restart\":true}' https://127.0.0.1:9443/v1/bdbs/2/modules/upgrade","section_id":"support-for-multiple-modules"},{"id":"cluster-api-ex0","language":"sh","code":"curl -X POST -H \"Content-Type: application/json\" -i -k -u user@redislabs.com:passsword https://localhost:9443/v1/nodes/3/actions/remove --data \"{}\"","section_id":"cluster-api"}]}
{"schema_version":2,"id":"operate/rs/release-notes/legacy-release-notes/rs-5-4-6-july-2019","title":"Redis Enterprise Software Release Notes 5.4.6 (July 2019)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-6-july-2019/","summary":"","tags":["docs","operate","rs"],"last_updated":"2026-10-06T08:53:14-07:00","page_type":"content","content_hash":"e0b40f44f77f5a48a7a48b1ca4b98f496dd96989c40e782cf230c5d2caa42bed","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"[Redis Enterprise Software (RS) 5.4.6](https://redislabs.com/redis-enterprise/software/downloads/#downloads) is now available.\nThis release includes the latest version of Redis 5 (5.0.5), bundles the GA release of the new RedisTimeSeries module,\nand adds other enhancements and bug fixes."},{"id":"overview","title":"Overview","role":"overview","text":"Follow these [instructions](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading) for upgrading to RS 5.4.6 from RS 5.0 and above.\nIf you have a version older than 5.0, you must first upgrade to version 5.2 (or at least 5.0)."},{"id":"new-features","title":"New features","role":"content","text":""},{"id":"time-series","title":"Time series","role":"content","text":"New GA release of the [RedisTimeSeries](https://redislabs.com/blog/redistimeseries-ga-making-4th-dimension-truly-immersive/) (version v1.0.0) module is bundled in RS 5.4.6."},{"id":"modules-versions","title":"Modules versions","role":"content","text":"Updated versions of GA modules:\n\n- RedisTimeSeries  - v1.0.0 (new module)\n- RedisBloom   -  v2.0.0 (version update)\n- RediSearch  -  v1.4.11 (version update)\n- RedisGraph  -  v1.2.2 (no change)\n- RedisJSON  -  v1.0.3 (no change)"},{"id":"additional-capabilities","title":"Additional capabilities","role":"content","text":"- Latest version of Redis 5 (5.0.5) was merged into RS 5.4.6.\n- If a user subscribes to a channel during recovery of a Active-Active Redis (CRDB) from an Append Only File (AOF), the user receives only the new messages.\n    (Fixes known limitation from RS 5.4.4)\n- CROSSSLOT behavior now matches Redis OSS behavior for accessing multiple keys in a single command.\n    Now, when you use CRC16 hash function (default), you can pass a command for different keys/tags that are mapped to the same slot. (RS23189)\n- Cluster upgrade process was improved so that the duration of the cluster upgrade process reduced."},{"id":"information","title":"Information","role":"content","text":"- End of Life (EOL) for Redis Enterprise Software 5.4, as well as for Redis Modules and previous RS versions,\n    can be found [here](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle)."},{"id":"important-fixes","title":"Important fixes","role":"content","text":"- RS28679 - Syncer and peers related statistics for Active-Active Redis (CRDB),\n    which are already available with the REST API, are exposed through Prometheus.\n- RS28946 - Metrics exporter related minor memory leak fix.\n- RS26312 - Ports handling during installation fix.\n- RS31703 - Fixed performance issue with MSET command."},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"upgrade","title":"Upgrade","role":"content","text":"- [RS 5.4.2](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-2-april-2019) introduced new Active-Active Redis (CRDB) capabilities\n    that improve its compatibility with open source Redis.\n    Now the string data-type in Active-Active Redis (CRDB) is implicitly and dynamically typed, just like open source Redis.\n    To use the new capabilities on nodes that are upgraded from version RS 5.4.2 or lower,\n    you must [upgrade the CRDB protocol](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading#upgrading-crdbs).\n- Before you upgrade a database with RediSearch Module to Redis 5.0,\n    you must [upgrade the RediSearch Module](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/upgrade-module) to version 1.4.2 or above.\n- Node upgrade fails if the SSL certificates were configured in version 5.0.2 or above\n    by manually updating the certificates on the disk instead of [updating them through the API](https://redis.io/docs/latest/operate/rs/security/certificates/updating-certificates).\n    For assistance with this issue, [contact Redis support](https://redislabs.com/company/support/).\n- We recommend that you test module upgrade commands in a test environment before you upgrade modules in a production environment.\n    The module upgrade arguments are not validated during the upgrade process and incorrect arguments can cause unexpected downtime.\n- Starting from RS 5.4.2, to preserve the current Redis major.minor version during database upgrade you must use the `keep_redis_version` option instead of `keep_current_version`."},{"id":"cluster-api","title":"Cluster API","role":"content","text":"- The API for removing a node is updated in RS 5.4.2 or higher. The API call must include json data and the \"Content-Type: application/json\" header. For example:\n\n    [code example]"},{"id":"redis-commands","title":"Redis commands","role":"content","text":"- The capability of disabling specific Redis commands does not work on commands specific to Redis Modules.\n- The CLIENT ID command cannot guarantee incremental IDs between clients that connect to different nodes under multi proxy policies.\n- CLIENT UNBLOCK command is not supported in RS 5.4 and above\n- Starting from RS 5.4.2 and after upgrading the CRDB, TYPE command for string data-type in CRDBs return \"string\" (OSS Redis standard)."}],"examples":[{"id":"cluster-api-ex0","language":"sh","code":"curl -X POST -H \"Content-Type: application/json\" -i -k -u user@redislabs.com:passsword https://localhost:9443/v1/nodes/3/actions/remove --data \"{}\"","section_id":"cluster-api"}]}
{"schema_version":2,"id":"operate/rs/release-notes/legacy-release-notes/rs-5-4-december-2018","title":"Redis Enterprise Software Release Notes 5.4 (December 2018)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-december-2018/","summary":"","tags":["docs","operate","rs"],"last_updated":"2026-10-06T08:53:14-07:00","page_type":"content","content_hash":"507737fb025dae864045807100c1c381e5f2199ff5d6e0fb41d7a30db37cd317","sections":[{"id":"overview-1","title":"Overview","role":"overview","text":"Redis Enterprise Software (RS) 5.4 is now available. RS 5.4 adds support for Redis 5.0 (GA) with the new Redis Streams data type."},{"id":"overview","title":"Overview","role":"overview","text":"You can upgrade to RS 5.4 from RS 5.0 and above according to the [upgrade instructions](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading). If you have a version older than 5.0, you should first upgrade to version 5.2 (or at least 5.0)."},{"id":"new-features","title":"New features","role":"content","text":""},{"id":"redis-50-ga---redis-streams","title":"Redis 5.0 GA - Redis Streams","role":"content","text":"RS 5.4 adds support for Redis 5.0 (GA version- 5.0.2), which introduces the new [Redis Streams](https://redis.io/docs/latest/develop/data-types/streams) data type. Redis Streams models a log data structure in-memory and implements additional powerful operations, such as Consumer Groups."},{"id":"redis-graph-module","title":"Redis graph module","role":"content","text":"Starting from RS 5.4, Redis Graph, a new Redis Enterprise Module that introduces the world's fastest graph database, is an integral part of Redis Enterprise package.\n\nRedisGraph is the first queryable [Property Graph](https://github.com/opencypher/openCypher/blob/master/docs/property-graph-model.adoc) database to use [sparse matrices](https://en.wikipedia.org/wiki/Sparse_matrix) to represent the [adjacency matrix](https://en.wikipedia.org/wiki/Adjacency_matrix) in graphs and [linear algebra](http://faculty.cse.tamu.edu/davis/GraphBLAS.html) to query the graph."},{"id":"activeactive-redis-crdb-creation-in-nonclustering-mode","title":"Active-Active Redis (CRDB) - Creation in non-clustering mode","role":"content","text":"In RS 5.4 you can [create Active-Active databases (CRDBs)](https://redis.io/docs/latest/operate/rs/databases/active-active/create) in a non-clustering mode. As a result, the following creation options are allowed:\n\n1. Clustering mode - Creates a CRDB that consists of any number of shards in a clustering mode and is subject to [multi-key commands limitations](https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering).\n1. Non-clustering mode - Creates a CRDB that consists of one shard only in a non-clustering mode so that [multi-key command limitations](https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering) do not apply."},{"id":"high-availability-for-replica-shards","title":"High availability for replica shards","role":"content","text":"When [replica high availability](https://redis.io/docs/latest/operate/rs/databases/configure/replica-ha) is enabled and a master shard fails, a replica (formerly _slave_) shard is automatically promoted to a master shard to maintain data availability. This creates a single point of failure until a new replica shard is manually created.\n\nRS 5.4 expands the high availability capabilities by adding the ability to automatically avoid this single point of failure by configuring the cluster to automatically migrate the replica shard to another available node. In practice, replica migration creates a new replica shard and replicates the data from the master shard to the new replica shard.\n\n*_Note that just as is the case with the Redis open-source project, Redis is in the process of changing the \"master-replica\" terminology to \"master-replica\" everywhere, including within our documentation._"},{"id":"additional-capabilities","title":"Additional capabilities","role":"content","text":"- Support for new operating systems- Ubuntu 18.04 and RHEL 7.6."},{"id":"product-version-lifecycle","title":"Product version lifecycle","role":"content","text":"- The End of Life (EOL) for Redis Enterprise Software 4.5.X was November 30th, 2018, in accordance with our [published policy](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle). We recommend that customers with version 4.5 or below upgrade to the latest version."},{"id":"important-fixes","title":"Important fixes","role":"content","text":"- RS23616 - Fixed a failure when updating the memory limit of RoF database.\n- RS22871 - Fixed a certificate verify failure after nodes upgrade.\n- RS2862 - Improved admin console performance in case multiple browsers or windows are directed to the admin console.\n- RS22751 - Fixed an issue in the backup process which caused temporary service outage.\n- RS22636 - Fixed Redis process failure when a ReJSON Module's command is executed.\n- RS22601 - Fixed a failure during shard migration procedure.\n- RS22478 - Fixed a failure in replica-of process between two databases with ReBloom Module.\n- RS21974 - SMTP username and password are not mandatory in the email server settings when there is no need for authentication.\n- RS21801 - Fixed admin console issues when cluster is configured with FIPS compliance.\n- RS21772 - Fixed a failure when trying to update a database's endpoint policy to all-master-shards.\n- RS19842 - Updated permissions of some internal files.\n- RS19433 - Improved RAM eviction process for RoF databases.\n- RS18875 - Added the ability to upgrade database gradually, few shards at a time.\n- RS15207 - Fixed a failure during re-shard operation."},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"installation","title":"Installation","role":"setup","text":"- In default Ubuntu 18.04 installations, port 53 is in use by systemd-resolved (DNS server). In such a case, the system configuration must be changed to make this port available before running RS installation."},{"id":"upgrade","title":"Upgrade","role":"content","text":"- Before you upgrade a database with the RediSearch module to Redis 5.0, you must [upgrade the RediSearch module on that DB](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/upgrade-module) to 1.4.2 or higher. We recommend that you upgrade the RediSearch module before you upgrade the cluster to RS 5.4.\n- Node upgrade fails if SSL certificates were configured in version 5.0.2 and above by updating the certificates on the disk instead of using the new API. For assistance with this issue, please [contact Redis support](https://redislabs.com/company/support/)."},{"id":"cluster-api","title":"Cluster API","role":"content","text":"- Removed the deprecated argument `backup_path` from cluster API. To create or update BDBs, please use `backup_location`."},{"id":"redis-commands","title":"Redis commands","role":"content","text":"- The capability of disabling specific Redis commands does not work on Redis Module specific commands.\n- The CLIENT ID command cannot guarantee incremental IDs between clients that connect to different nodes under multi proxy policies.\n- The length of the socket_path variable, which is defined in a node, cannot exceed 88 characters.\n- CLIENT UNBLOCK command is not supported in RS 5.4."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/legacy-release-notes/rs-5-5-preview-april-2019","title":"Redis Enterprise Software Release Notes 5.5 Preview (April 2019)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-5-preview-april-2019/","summary":"Preview release. Databases support multiple modules.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"0045a0c7a5a097359a8d6af1e27edec9f3a945af56f2c22422e3832e4ac4b7d9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Enterprise Software (RS) 5.5 Preview Edition is now available.\n\nRS 5.5 is a preview version that includes all the capabilities of Redis Enterprise 5.4,\nplus support for creation of Redis databases with multiple modules and support for these modules:\n\n- RediSearch (GA)\n- RedisGraph (GA)\n- RedisBloom (GA)\n- RedisJSON (GA)\n- RedisAI (Preview Version)\n- RedisTimeSeries (Preview Version)\n- RedisGears (Preview Version)"},{"id":"new-features","title":"New features","role":"content","text":"RS 5.5 lets you create Redis databases with multiple Redis modules."},{"id":"preview-considerations","title":"Preview considerations","role":"content","text":"This preview version is a standalone version and is not supported for production environments.\nYou cannot upgrade to it from a lower version or upgrade from it to a higher version.\nUnexpected behaviors/issues found in this release will be addressed in future releases.\n\nThis preview version is not supported for networks that are isolated from the internet."},{"id":"installation-instructions","title":"Installation instructions","role":"setup","text":"To set up a cluster with nodes that can host databases with multiple modules, you must follow this procedure on each node in the cluster:\n\n1. [Install RS 5.5](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/redis-enterprise-software-quickstart).\n1. To install the modules, run: `sudo ./install-modules.sh`\n1. Either:\n    - Set up the node as the [first node in the cluster](https://redis.io/docs/latest/operate/rs/clusters/new-cluster-setup).\n    - [Join the node to an existing cluster](https://redis.io/docs/latest/operate/rs/clusters/add-node)."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/legacy-release-notes/rs-5-6-0-april-2020","title":"Redis Enterprise Software Release Notes 5.6.0 (April 2020)","url":"https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-6-0-april-2020/","summary":"Install improvements for RHEL 6 and 7. Active-Active support for HyperLogLog. Auto Tiering now supports RedisJSON. Active-Active default changes for high availability and OSS Cluster API support. Backup support for Google Cloud Storage and Azure Blob storage.","tags":["docs","operate","rs"],"last_updated":"2026-10-06T08:53:14-07:00","page_type":"content","content_hash":"a669ba4dbcfebcd60596cdf5ba5d4ac5206cb5d578f7d0b3bc70160ceb68c789","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Redis Enterprise Software (RS) 5.6.0](https://redislabs.com/download-center/) is now available. This major version release includes:\n\n- Improved installation process to be customizable\n- Support for the HyperLogLog data type in Active-Active databases\n- RedisJSON support in databases with Auto Tiering enabled\n- Support for Redis OSS Cluster API for Active-Active and Replica Of databases\n- Replica HA enabled by default for Active-Active databases\n- Cloud backup locations support\n- Support for Red Hat Enterprise Linux version 7.7, 7.8\n- Additional enhancements, and minor bug fixes"},{"id":"information","title":"Information","role":"content","text":""},{"id":"upgrade","title":"Upgrade","role":"content","text":"- Follow [these instructions](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading) for upgrading to RS 5.6.0 from RS 5.0.2 and above.\n- For Active-Active databases, you must upgrade the [feature set version](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading#upgrade-activeactive-databases)."},{"id":"end-of-life","title":"End of life","role":"content","text":"- End of Life (EOL) for Redis Enterprise Software 5.6 and previous RS versions can be found [here](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle).\n    EOL for Redis Modules can be found [here](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules-lifecycle#modules-endoflife-schedule).\n- Support of Red Hat Enterprise Linux 6 and Oracle Linux 6 [operating systems platforms](https://redis.io/docs/latest/operate/rs/references/supported-platforms) will end on November 30, 2020.\n- Support of OpenStack Object Storage (\"Swift\") for [backup, import and export location](https://docs.redis.com/latest/rs/databases/import-export/import-data/) will end on November 30, 2020."},{"id":"new-features","title":"New features","role":"content","text":""},{"id":"redis-software-installer","title":"Redis Software installer","role":"content","text":"Redis Enterprise Software installer adds the ability to specify custom installation paths and a custom installation user, group, or both on RHEL versions 6 and 7.\n\nWhen you run the installer you can specify the installation, the configuration and the var as well as the user and the group:\n\n[code example]\n\nFor more information, check out the [Redis Enterprise Software installer documentation](https://docs.redis.com/latest/rs/installing-upgrading/)."},{"id":"hyperloglog-on-active-active","title":"Hyperloglog on Active-Active","role":"content","text":"RS 5.6.0 adds the support of [HyperLogLog](http://www.antirez.com/news/75) data structure for [Active-Active Redis](https://redis.io/docs/latest/operate/rs/databases/import-export/replica-of) databases.\n\nBecause HyperLogLog is a counting data structure by nature, conflicts can occur when deleting entries. For efficiency, performance and memory considerations, conflicts between instances are resolved with DEL (delete) operations winning over ADD operations that took place in concurrent or before the DEL operation.\n\nFor more information, check out the [HyperLogLog on Active-Active documentation](/operate/rs/databases/active-active/develop/hyperloglog-active-active.md)."},{"id":"redisjson-on-auto-tiering","title":"RedisJSON on Auto Tiering","role":"content","text":"RS 5.6.0 adds support for [Auto Tiering](https://redis.io/docs/latest/operate/rs/databases/flash) databases with [Redis Modules](https://docs.redis.com/latest/modules). [RedisJSON](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json) is the first module to be available to run on a database with Auto Tiering enabled.\n\nUsing RedisJSON on Auto Tiering allows you to benefit from high performance redis with high data volume at lower costs."},{"id":"oss-cluster-api-support","title":"OSS Cluster API support","role":"content","text":"For more information, check out the [OSS Cluster API documentation](https://redis.io/docs/latest/operate/rs/databases/configure/oss-cluster-api).\n\n**Support for Active-Active and Replica Of databases**\n\nYou can configure [Active-Active](https://redis.io/docs/latest/operate/rs/databases/import-export/replica-of) databases to use the [OSS Cluster API](https://docs.redis.com/latest/rs/clusters/optimize/oss-cluster-api/) using the admin console.\nThe OSS Cluster API improves the performance of user operations against your database.\n\nYou can also create or modify an Active-Active Redis database in OSS Cluster mode using the `crdb-cli` tool with the `--oss-cluster` option to apply the changes to all of the instances.\n\n**Create and edit database using the admin console**\n\nYou can configure OSS Cluster API for databases using the admin console.\n\nFor Active-Active databases, you can create the database with OSS Cluster API enabled for all of its instances.\nWhen you enable OSS Cluster after the Active-Active database is created, the change applies only to the local instance."},{"id":"replica-ha-defaults-for-active-active","title":"Replica HA defaults for Active-Active","role":"content","text":"To enhance the availability and the consistency of [Active-Active geo-distributed Redis](https://redis.io/docs/latest/operate/rs/databases/active-active),\n[Replica HA](https://redis.io/docs/latest/operate/rs/databases/configure/replica-ha) is enabled by default for all existing and new Active-Active Redis Databases.\n\nTo disable Replica HA for for a local instance of an Active-Active database, run this command on the instance: `rladmin tune db <bdb_uid> slave_ha disabled`"},{"id":"cloud-backup-locations-support","title":"Cloud backup locations support","role":"content","text":"Redis Enterprise 5.6.0 adds the ability to configure Azure Blob Storage and Google Cloud Storage as backup, import, and export locations."},{"id":"red-hat-enterprise-linux-77-78","title":"Red Hat Enterprise Linux 7.7, 7.8","role":"content","text":"Redis Enterprise 5.6.0 adds RHEL 7.7, 7.8 to its [supported platforms](https://redis.io/docs/latest/operate/rs/references/supported-platforms)."},{"id":"redis-modules","title":"Redis modules","role":"content","text":"The following GA releases of Redis Modules are bundled in RS 5.6.0:\n\n- [RedisBloom](https://redislabs.com/redis-enterprise/redis-bloom/), version [2.2.1](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.2-release-notes)\n- [RedisGraph](https://redislabs.com/redis-enterprise/redis-graph/), version [2.0.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.0-release-notes)\n- [RedisJson](https://redislabs.com/redis-enterprise/redis-json/), version [1.0.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-1.0-release-notes)\n- [RediSearch](https://redislabs.com/redis-enterprise/redis-search/), version [1.6.11](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-1.6-release-notes) (updated)\n- [RedisTimeSeries](https://redislabs.com/redis-enterprise/redis-time-series/), version [1.2.3](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.2-release-notes)"},{"id":"additional-capabilities","title":"Additional capabilities","role":"content","text":"- OSS Redis version [5.0.8](https://raw.githubusercontent.com/antirez/redis/5.0/00-RELEASENOTES)  is merged into RS 5.6.0.\n- Starting RS 5.6.0, to upgrade modules during database upgrade you must use the module_args option instead of keep_module_args or <argument> or \" \" (for no arguments). The module_args option must follow with one of the following (Always in quotation marks): \"keep_args\" or \" \" or \"<argument>\".\nFor more info and examples, check out [upgrading Redis Modules documentation](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/upgrade-module) .\n- rladmin adds the ability to demote the cluster master node when setting it to [maintenance mode](https://redis.io/docs/latest/operate/rs/clusters/maintenance-mode) by using the `demote_node` option:\n\n[code example]\n\n- The `SENTINEL MASTER` command output format was updated to be aligned with OSS equivalent output."},{"id":"important-fixes","title":"Important fixes","role":"content","text":"- RS38315, RS33747 - Added the LUA script name (SHA) and its arguments to the warning message indicating the LUA script has been running for more than 5 seconds.\n- RS38706 - Fixed a bug which caused a stuck state machine in some scenarios of restoring DB from RDB.\n- RS34309 - Improved internal passwords handling.\n- RS38498 - Fixed a bug in the upgrade process of a database to avoid failure when saving large RDB (Redis Database Backup) file.\n- RS38706 - Fixed a bug that caused a stuck state machine after restoring a snapshot of an Active-Active database.\n- RS43996 - Fixed a bug in the upgrade process when using ./install.sh -y and firewalld is not running.\n- RS45777 - Fixed a bug that caused clients on a shared connection to the proxy to get disonnected. Disconnections occured in case a response for a request of an already disconnected client was received.\nWith build 5.6.0-39:\n- RS45627, RS47382 - Fixed bugs causing clients to disconnect when using XREAD and XREADGROUP commands in blocking mode on other clients’ connections.\n- RS44656 - Fixed a bug causing TLS mode for clients connections to toggle between ‘all communication’ to ‘for crdb communication only’ when performing a global configuration change."},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"active-active-databases","title":"Active-Active databases","role":"content","text":"- RS51359 - Active-Active databases, using replication and Append Only File (AOF) for [database persistence](https://redis.io/docs/latest/operate/rs/databases/configure/database-persistence), are suffering from memory leaks on replica shards, causing them to grow bigger than the master shards. Customers are advised to upgrade to RS 6.0.12. Meanwhile, you can use a snapshot for database persistence or restart the replica shards."},{"id":"upgrade-notes","title":"Upgrade notes","role":"content","text":"- [RS 5.4.2](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-2-april-2019) introduced new Active-Active Redis Database capabilities that improve its compatibility with open source Redis.\n    Now the string data-type in Active-Active Redis Database is implicitly and dynamically typed, just like open source Redis.\n    To use the new capabilities on nodes that are upgraded from version RS 5.4.2 or lower,\n    you must [upgrade the Active-Active Redis Database protocol](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading#upgrading-crdbs).\n- When you upgrade an Active-Active Redis with active AOF from version [RS 5.4.2](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-2-april-2019) or lower to version [RS 5.4.4](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-4-june-2019) or higher:\n    - If replication is enabled, you must run the BGREWRITEAOF command on all replica shards after the upgrade.\n    - If replication is not enabled, you must run the BGREWRITEAOF command on all shards after the upgrade.\n- Node upgrade fails if the SSL certificates were configured in version 5.0.2 or above by manually updating the certificates on the disk instead of [updating them through the API](https://redis.io/docs/latest/operate/rs/security/certificates/updating-certificates).\n    For assistance with this issue, contact Support.\n- Starting from [RS 5.4.2](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-2-april-2019), to preserve the current Redis major.minor version during database upgrade you must use the keep_redis_version option instead of keep_current_version.\n- Google Chrome browser on macOS Catalina requires a self-signed certificate generated after June 2019 to include the extendedKeyUsage field in order to connect to the RS admin console. If you use a self-signed certificate that does not include this field, [update the self-signed certificate](https://redis.io/docs/latest/operate/rs/security/certificates/updating-certificates)."},{"id":"modules-upgrade","title":"Modules upgrade","role":"content","text":"- We recommend that you test module upgrade commands in a test environment before you upgrade modules in a production environment. The module upgrade arguments are not validated during the upgrade process and incorrect arguments can cause unexpected downtime.\n- Before you upgrade a database with RediSearch Module to Redis 5.0, you must [upgrade the RediSearch Module](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/upgrade-module) to version 1.4.2 or above."},{"id":"cluster-api","title":"Cluster API","role":"content","text":"- The API for removing a node is updated in [RS 5.4.2](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-2-april-2019) or higher. The API call must include json data and the \"Content-Type: application/json\" header. For example:\n\n    [code example]"},{"id":"discover-service","title":"Discover service","role":"content","text":"- For [Redis Sentinel (Discovery Service)](https://redis.io/docs/latest/operate/rs/databases/durability-ha/discovery-service), every database name must be unique across the cluster."},{"id":"redis-commands","title":"Redis commands","role":"content","text":"- The capability of disabling specific Redis commands does not work on commands specific to Redis Modules.\n- The CLIENT ID command cannot guarantee incremental IDs between clients that connect to different nodes under multi proxy policies.\n- CLIENT UNBLOCK command is not supported in RS 5.4 and above\n- Starting from RS 5.4.2 and after upgrading the CRDB, TYPE commands for string data-type in CRDBs return \"string\" (OSS Redis standard)."}],"examples":[{"id":"redis-software-installer-ex0","language":"sh","code":"sudo ./install.sh --install-dir <path> --config-dir <path> --var-dir <path> --os-user <user> --os-group <group>","section_id":"redis-software-installer"},{"id":"additional-capabilities-ex0","language":"sh","code":"rladmin node <node_uid> maintenance_mode on demote_node","section_id":"additional-capabilities"},{"id":"cluster-api-ex0","language":"sh","code":"curl -X POST -H \"Content-Type: application/json\" -i -k -u user@redislabs.com:password https://localhost:9443/v1/nodes/3/actions/remove --data \"{}\"","section_id":"cluster-api"}]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-6-0-12-january-2021","title":"Redis Enterprise Software Release Notes 6.0.12 (January 2021)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-0-12-january-2021/","summary":"Distribute synchronization across nodes for Active-Active and Active-Passive databases. Disable internal services to free memory. User accounts support password rotation. Module depdencies automatically installed. Syncer process recovery.","tags":["docs","operate","rs"],"last_updated":"2026-10-06T08:53:14-07:00","page_type":"content","content_hash":"b38dc66e98b7aedbe46fb626900c0eb948c1df10b1611a7f89dd036b608c7ff7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Redis Enterprise Software (RS) 6.0.12](https://redislabs.com/download-center/#downloads) is now available!\nThis version includes the following new features and improvements:\n\n- [Synchronization](https://redis.io/docs/latest/operate/rs/databases/active-active#syncer-process) can now be [distributed across the nodes](https://redis.io/docs/latest/operate/rs/databases/active-active/synchronization-mode) of Active-Active or Active-Passive databases\n- You can [disable several internal RS services](https://redis.io/docs/latest/operate/rs/clusters/optimize/turn-off-services) to free up more memory\n- User accounts can have multiple passwords to allow for [password rotation](https://redis.io/docs/latest/operate/rs/security/access-control/manage-passwords/rotate-passwords)\n- [Dependencies are automatically installed](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/add-module-to-cluster#rest-api-method) when you add modules to a cluster\n- [Envoy replaces NGINX](https://redis.io/docs/latest/operate/rs/networking/port-configurations) for internal cluster administration\n- Automatic recovery of the [syncer process](https://redis.io/docs/latest/operate/rs/databases/active-active#syncer-process)s from out-of-memory (preview mode)\n\nAnd other functional and stability improvements."},{"id":"version-information","title":"Version information","role":"compatibility","text":""},{"id":"upgrade-instructions","title":"Upgrade instructions","role":"content","text":"- Follow [these instructions](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading) for upgrading to RS 6.0.12 from RS 5.4.0 and above.\n- For Active-Active deployments, this release requires that you [upgrade the CRDB featureset version](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-active-active)."},{"id":"product-lifecycle-information","title":"Product lifecycle information","role":"content","text":"- End of Life (EOL) for Redis Enterprise Software 6.0 and previous RS versions, can be found [here](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle).\n- EOL for Redis modules can be found [here](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules-lifecycle#modules-endoflife-schedule)."},{"id":"deprecation-notice","title":"Deprecation Notice","role":"content","text":"- Support for RS 5.4.X ended on December 31, 2020.\n- Support for Red Hat Enterprise Linux 6 and Oracle Linux 6 and Ubuntu 14.04 (Trusty) operating systems platforms ended on November 30, 2020.\n- This is the last RS version that supports direct upgrades from versions below 5.6.0.\n- This is the last RS version that supports Active-Active protocol version below 1 and featureset version below 1."},{"id":"new-features","title":"New Features","role":"content","text":"#### Distributed Syncer\n\nThe syncer process now supports running in a [distributed mode](https://redis.io/docs/latest/operate/rs/databases/active-active/synchronization-mode). This option can improve the latency for Active-Active databases with a very high throughput profile. You can configure a replicated database to use distributed synchronization so that any available proxy endpoint can manage synchronization traffic.\n\n#### Disabling RS services to free memory\n\nRedis Software users can now use the REST API to [disable the following services](https://redis.io/docs/latest/operate/rs/clusters/optimize/turn-off-services):\n\n- cm_server\n- mdns_server\n- pdns_server\n- stats_archiver\n- saslauthd\n- crdb_coordinator\n- crdb_worker\n\nOnce disabled, services are not monitored and controlled by the supervisord.\n\n> [!WARNING]\n> This feature can cause unintended results if the cluster relies on the disabled services.\n> To make sure you understand the impact of disabled services, test the system in a lab environment before you deploy in production.\n\n#### Support for multiple passwords\n\nFor users of Redis 6 and RS 6.0 and above, you can now add more security to your password management by maintaining multiple passwords for a user to allow seamless password rotation.\n\nAs of RS 6.0, you can assign specific data access permissions (Redis ACLs) and cluster administration permissions to users.\nPassword rotation is especially helpful so that you can do a rolling update of the passwords in the application clients that connect to the Redis databases.\n\nIn this version, you can only configure multiple passwords [using the REST API](https://redis.io/docs/latest/operate/rs/security/access-control/manage-passwords/rotate-passwords).\n\n#### Redis Modules dependencies management\n\nRedisGears GA and RedisAI GA require Redis Software to fetch and manage external dependencies.\nModules declare dependencies at release time in their ramp file.\n\nIn this version of RS, these dependencies are installed by Redis Software when the module is added to the cluster.\nThe master node downloads the required dependencies and prompts the other nodes to copy the dependencies from the master node.\nWhen all dependency requirements are satisfied, the module installation is complete.\nNew nodes to the cluster also automatically install the dependencies.\n\n#### Syncer automatic recovery from out-of-memory (Preview mode)\n\nFor Active-Active databases, the syncer process gracefully recovers from an out of memory (OOM) state.\n\nAlthough Active-Active synchronization is bi-directional, in each direction we can define a source instance and a destination instance.\nWhen a destination instance (that is, the instance running the syncer process) gets OOM, the syncer process attempts to automatically recover the data synchronization when memory becomes available.\n\nThis is a configurable option and currently under preview mode. This behavior will be GA and set as default in the next RS version.\n\nTo enable the syncer automatic recovery, do these steps on each participating cluster:\n\n1. [Upgrade the featureset version](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-active-active) to `3`.\n1. Enable the syncer automatic recovery using the REST API:\n\n    [code example]\n\nThe syncer process restarts to with automatic recovery on."},{"id":"redis-modules","title":"Redis modules","role":"content","text":"The following GA releases of Redis modules are bundled with RS 6.0.12:\n\n- [RediSearch](https://redislabs.com/redis-enterprise/redis-search/), version [2.0.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.0-release-notes)\n- [RedisJSON](https://redislabs.com/redis-enterprise/redis-json/), version [1.0.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-1.0-release-notes)\n- [RedisGraph](https://redislabs.com/redis-enterprise/redis-graph/), version [2.2.11](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph)\n- [RedisTimeSeries](https://redislabs.com/redis-enterprise/redis-time-series/), version [1.4.7](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries)\n- [RedisBloom](https://redislabs.com/redis-enterprise/redis-bloom/), version [2.2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.2-release-notes)\n\nTo use the updated modules with a database, you must [upgrade the module on the database](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/upgrade-module)."},{"id":"additional-capabilities","title":"Additional capabilities","role":"content","text":"- RS 6.0.12 includes open source Redis 6.0.6. For more information about Redis 6.0.6, check out the [release notes](https://raw.githubusercontent.com/redis/redis/6.0.6/00-RELEASENOTES).\n- The bundled Nginx version was updated from version 1.16.0 to 1.18.0.\n- The crdb-cli syntax to remove an instance is changed from\n`remove-instance [--ordered|--unordered]` to `remove-instance [--force|--no-force]`."},{"id":"important-fixes","title":"Important fixes","role":"content","text":"- RS45627, RS47382 - Fixed bugs causing clients to disconnect when using XREAD and XREADGROUP commands in blocking mode on other clients’ connections.\n- RS44656 - Fixed a bug causing TLS mode for clients connections to toggle between ‘all communication’ to ‘for crdb communication only’ when performing a global configuration change.\n- RS42587 - Fixed a bug in the web UI console if the FQDN of the cluster is a substring of the FQDN of a participating cluster\n- RS49404 - Fixed a bug in for upgrades with custom directories that prevent users from creating databases via the web UI console.\n- RS43961 - bigkeys command fixed to handle non-printable key names\n- RS45707 - Fixed a bug that caused RCP (Redis Cloud Pro) databases to reject connections while resharding the database.\n- RS51144 - Fixed a bug in the syncer process that was stopping synchronization between all instances in some scenarios of network disconnection of one or more participating clusters.\n\nwith 6.0.12-58:\n- RS50865 - Fixed a bug causing rladmin change master node to fail when performed after a prior successful master change.\n- RS51359 - Fixed a memory leak on replica shards in Active-Active databases with replication and AOF for persistence.\n- RS52363 - Updated PUB/SUB max message value size from 64KB to 512MB"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"-RS81463 - A shard may crash when resharding an Active-Active database with Auto Tiering . Specifically, the shard will crash when volatile keys or Active-Active tombstone keys reside in Flash memory."},{"id":"installation-limitations","title":"Installation limitations","role":"setup","text":"Several Redis Enterprise Software installation reference files are installed to the directory `/etc/opt/redislabs/` even if you use [custom installation directories](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/customize-install-directories).\n\nAs a workaround to install Redis Enterprise Software without using any root directories, do the following before installing Redis Enterprise Software:\n\n1. Create all custom, non-root directories you want to use with Redis Enterprise Software.\n\n1. Mount `/etc/opt/redislabs` to one of the custom, non-root directories.\n\n#### Upgrade\n\n- [RS 5.4.2](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-2-april-2019) introduced new Active-Active Redis Database capabilities that improve its compatibility with open source Redis. Now the string data-type in Active-Active Redis Database is implicitly and dynamically typed, just like open source Redis. To use the new capabilities on nodes that are upgraded from version RS 5.4.2 or lower, you must [upgrade the Active-Active Redis Database protocol](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading#upgrading-crdbs).\n- When you upgrade an Active-Active Redis with active AOF from version [RS 5.4.2](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-2-april-2019) or earlier to version [RS 5.4.4](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-4-june-2019) or later:\n    - If replication is enabled, you must run the BGREWRITEAOF command on all replica shards after the upgrade.\n    - If replication is not enabled, you must run the BGREWRITEAOF command on all shards after the upgrade.\n- Node upgrade fails if the SSL certificates were configured in version 5.0.2 or above by manually updating the certificates on the disk instead of [updating them through the API](https://redis.io/docs/latest/operate/rs/security/certificates/updating-certificates). For assistance with this issue, contact Support.\n- Starting from [RS 5.4.2](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-2-april-2019), to preserve the current Redis major.minor version during database upgrade you must use the keep_redis_version option instead of keep_current_version.\n\n#### Redis commands\n\n- The capability of disabling specific Redis commands does not work on commands specific to Redis modules.\n- CLIENT UNBLOCK command is not supported in RS 5.4 and above\n- Starting from RS 5.4.2 and after upgrading the CRDB, TYPE commands for string data-type in CRDBs return \"string\" (OSS Redis standard)."}],"examples":[{"id":"new-features-ex0","language":"sh","code":"curl -v -k -u <username>:<password> -X PUT -H \"Content-Type: application/json\" -d ‘{crdt_syncer_auto_oom_unlatch\":true}’ http://<cluster_address>:8080/v1/bdbs/<database_ID>","section_id":"new-features"}]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-6-0-20-april-2021","title":"Redis Enterprise Software Release Notes 6.0.20 (April 2021)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-0-20-april-2021/","summary":"Role-based LDAP integration. Enhanced client mutual authentication. Active-Active improvements for eviction policies, migration, and the BITFIELD data type.","tags":["docs","operate","rs"],"last_updated":"2026-10-06T08:53:14-07:00","page_type":"content","content_hash":"ba4551f05bdea9bf281355dae57877c1fe7e1059c681282ca64f8b2c13abefc3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Redis Enterprise Software version 6.0.20](https://redislabs.com/redis-enterprise-software/download-center/software/)\nis now available! This version includes the following new features and\nimprovements:\n\n-   A [new integration for LDAP](https://redis.io/docs/latest/operate/rs/security/access-control/ldap) authentication and\n    authorization into RS role-based access controls (RBAC). You can now use\n    LDAP to authorize access to the admin console and to authorize database\n    access.\n\n-   An enhanced [clients mutual authentication](https://redis.io/docs/latest/operate/rs/security/encryption/tls)\n    mechanism, adding the ability to authenticate client connections using a\n    Certificate Authority (CA).\n\n-   Support of [Redis eviction\n    policies](https://redis.io/docs/latest/operate/rs/databases/memory-performance/eviction-policy)\n    on\n    [Active-Active](https://redis.io/docs/latest/operate/rs/databases/active-active/create)\n    Redis databases.\n\n-   A new [migration process for Active-Active](https://redis.io/docs/latest/operate/rs/databases/import-export/migrate-to-active-active)\n    Redis database using the Active-Passive (Replica Of) mechanism.\n\n-   Support for the [BITFIELD data type on Active-Active](https://redis.io/docs/latest/operate/rs/databases/active-active/develop/data-types/strings)\n    Redis databases.\n\nAnd other functional and stability improvements."},{"id":"version-information","title":"Version information","role":"compatibility","text":""},{"id":"upgrade-instructions","title":"Upgrade instructions","role":"content","text":"-   Follow [these instructions](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading)\n    for upgrading to Redis Software 6.0.20 from Redis Software 5.6.0 and above.\n\n    -   Note that upgrades from earlier Redis Software versions are not supported.\n\n-   For Active-Active deployments, this release requires that you [upgrade the CRDB featureset version](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-active-active).\n\n-   Upgrades of Active-Active databases to Redis Software 6.0.20, will require all their\n    instances to run with protocol version 1 and featureset version 1 or above.\n    Active-Active databases running on protocol version 0 and/or featureset\n    version 0 will block the upgrade."},{"id":"product-lifecycle-information","title":"Product lifecycle information","role":"content","text":"-   End of Life (EOL) for Redis Enterprise Software 6.0 and earlier \n    versions, can be found\n    [here](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle).\n\n-   EOL for Redis modules can be found\n    [here](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules-lifecycle#modules-endoflife-schedule)."},{"id":"deprecation-notice","title":"Deprecation Notice","role":"content","text":"-   Upgrades to the next Redis Software will be enabled from version 6.0 and above.\n\n-   Support for the SASL-based LDAP mechanism was deprecated in v6.0.20.  As of v6.2.12, support has been removed and the feature is obsolete.\n\n-   Starting with Redis Software version 6.0.12, Envoy replaces Nginx for internal cluster administration.\n    Support for Nginx is considered deprecated, it will be removed in a future\n    version."},{"id":"new-features","title":"New Features","role":"content","text":"#### New LDAP integration\n\nRedis Enterprise Software integrates Lightweight Directory Access Protocol\n(LDAP) authentication and authorization into its [role-based access controls](https://redis.io/docs/latest/operate/rs/security/access-control)\n(RBAC). You can now use LDAP to authorize access to the admin console and to\nmanage database access.\n\n#### Clients Mutual TLS authentication using a Certificate Authority (CA)\n\nRedis Enterprise Software adds the ability to use a Certificate Authority (CA)\nfor client authentications, allowing clients to rotate their certificates\nwithout the need to load new certificates to your database.\n\n#### Redis eviction policies on Active-Active Redis databases\n\nAll Redis [eviction policies](https://redis.io/docs/latest/operate/rs/databases/memory-performance/eviction-policy)\nare now supported on Active-Active Redis databases. You can create new\nActive-Active databases or edit existing ones using the UI console to enable it.\n\n-   Note that eviction is not supported yet for Active-Active Redis databases\n    running with Auto Tiering .\n\n#### Migration to an Active-Active Redis database\n\nRedis Enterprise Software adds the ability to easily [migrate your Redis\ndatabase to an Active-Active](https://redis.io/docs/latest/operate/rs/databases/import-export/migrate-to-active-active)\nRedis database using the Active-Passive (Replica Of) mechanism.\n\n#### BITFIELD on Active-Active Redis databases\n\nRedis Enterprise Software adds the ability to use the BITFIELD data type on\nActive-Active Redis databases. Please read more about [developing for Active-Active with BITFIELD](https://redis.io/docs/latest/operate/rs/databases/active-active/develop/data-types/strings)\nto understand the conflict resolution and limitations."},{"id":"redis-modules","title":"Redis modules","role":"content","text":"The following GA releases of Redis modules are bundled with Redis Software 6.0.20:\n(Please read the below updates for 6.0.20-97)\n\n-   [RediSearch](https://redislabs.com/redis-enterprise/redis-search/), version\n    [2.0.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.0-release-notes)\n\n-   [RedisJSON](https://redislabs.com/redis-enterprise/redis-json/), version\n    [1.0.7](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-1.0-release-notes)\n\n-   [RedisGraph](https://redislabs.com/redis-enterprise/redis-graph/), version\n    [2.2.14](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.2-release-notes)\n\n-   [RedisTimeSeries](https://redislabs.com/redis-enterprise/redis-time-series/),\n    version\n    [1.4.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.4-release-notes)\n\n-   [RedisBloom](https://redislabs.com/redis-enterprise/redis-bloom/), version\n    [2.2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.2-release-notes)\n\nTo use the updated modules with a database, you must [upgrade the module on the\ndatabase](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/upgrade-module)."},{"id":"additional-capabilities","title":"Additional capabilities","role":"content","text":"-   Redis Software 6.0.20 includes open source Redis 6.0.9. For more information about Redis\n    6.0.9, check out the [release notes](https://raw.githubusercontent.com/redis/redis/6.0.9/00-RELEASENOTES).\n\n-   Redis Software 6.0.20 adds new `rladmin` commands for setting Ciphers suites and minimal\n    TLS version for:\n\n    -   Control plane: setting Envoy\n\n    -   Data plane: setting the Proxy for clients connections\n\n    -   Sentinel discovery service\n\n-   Starting with Redis Software 6.0.20, new clusters will be set with minimal TLS version v1.2\n\n\nAll known bugs around setting ciphers were fixed.  To learn more, see [Configure cipher suites](https://redis.io/docs/latest/operate/rs/security/encryption/tls/ciphers).\n\n-   Starting with Redis Software 6.0.20, the [syncer process](https://redis.io/docs/latest/operate/rs/databases/active-active#syncer-process)\n    was improved to automatically recover and resume synchronisation after\n    reaching out-of-memory.\n\n-   Envoy updated and verified with multiple security headers.\n\n-   Starting with Redis Software 6.0.20:\n\n    -   The replication backlog size of new databases is allocated dynamically according to shard size.\n\n    -   The Active-Active replication backlog size of new Active-Active databases is allocated dynamically according to shard size."},{"id":"important-fixes","title":"Important fixes","role":"content","text":"-   RS50905, RS54809, 54940 - Fix in Redis preventing missing process PID\n\n-   RS53639 - Fix to avoid stuck state machine when assigning incorrect Redis\n    ACL with the allkeys alias or \\~\\* and also with \\~\\<somekey\\>\n\n-   RS47983 - Fixed dependencies with installation using custom directories\n\n-   RS54382 - Fixed missing API documentation\n\n-   RS52433, RS53417, RS42195, RS42194, RS30526, RS46821, RS48928 - Fixed\n    security headers for Envoy"},{"id":"starting-6020-69","title":"Starting 6.0.20-69","role":"content","text":"-   RS55504 - Fixed issue that caused RediSearch cursor to break."},{"id":"starting-6020-97","title":"Starting 6.0.20-97","role":"content","text":"-   RS57659 - Fixed force removing an Active-Active instance which is hosted on an inaccessible cluster\n-   RS57315 - Fixed creation and editing of an Active-Active database with LDAP users of the new RBAC LDAP integration. This applies to UI access and REST API and does not apply to LDAP users for database access via Redis Clients\n-   RS57073 - Fixed a bug caused in the shard migration process which could leave unattended shards on the node\n-   RS56508 - Fixed backwards compatibility of client certificate when upgrading from earlier versions and using a certificate chain with Extended Key Usage extension being set to \"TLS Web Server Authentication\" instead of \"TLS Web Client Authentication\"\n-   RS49289 - Fixed updating the log rotation config file according to the custom config path that was set during the installation\n-   The bundled RedisGraph module was upgraded to v2.4.6\n-   The bundled RedisTimeSeries module was upgraded to v1.4.9\n-   The bundled RediSearch module was upgraded to v2.0.8"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"-RS81463 - A shard may crash when resharding an Active-Active database with Auto Tiering . Specifically, the shard will crash when volatile keys or Active-Active tombstone keys reside in Flash memory.\n-   RS59983 - Clients may get disconnected by the proxy when one client sends an UNSUBSCRIBE command without being subscribed to any channel and disconnect before the response returns back from the server (from the proxy).\n-   RS60068 - The pdns might not resolve the master node after master node change. Restarting the pdns service is required in this case. \n-   RS61114 - Active-Active synchronization will fail in the following scenario: a new syncer connection is established AND a partial sync (psync) was initiated AND a cron job runs before the first ACK of the psync was received.\n-   RS55504 - Bug RS6.0.20-66 (Build #66) causes RediSearch cursor to break. Please upgrade to a higher build when running with RediSearch."},{"id":"installation-limitations","title":"Installation limitations","role":"setup","text":"Several Redis Enterprise Software installation reference files are installed to the directory `/etc/opt/redislabs/` even if you use [custom installation directories](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/customize-install-directories).\n\nAs a workaround to install Redis Enterprise Software without using any root directories, do the following before installing Redis Enterprise Software:\n\n1. Create all custom, non-root directories you want to use with Redis Enterprise Software.\n\n1. Mount `/etc/opt/redislabs` to one of the custom, non-root directories.\n\n#### Upgrade\n\n-   [Redis Software 5.4.2](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-2-april-2019)\n    introduced new Active-Active Redis Database capabilities that improve its\n    compatibility with open source Redis. Now the string data-type in\n    Active-Active Redis Database is implicitly and dynamically typed, just like\n    open source Redis. To use the new capabilities on nodes that are upgraded\n    from version RS 5.4.2 or lower, you must \n    [upgrade the Active-Active Redis Database protocol](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-active-active).\n\n-   When you upgrade an Active-Active Redis with active AOF from [Redis Software 5.4.2](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-2-april-2019)\n    or earlier to version [Redis Software 5.4.4](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-4-june-2019)\n    or later:\n\n    -   If replication is enabled, you must run the BGREWRITEAOF command on all\n        replica shards after the upgrade.\n\n    -   If replication is not enabled, you must run the BGREWRITEAOF command on\n        all shards after the upgrade.\n\n-   Node upgrade fails if the SSL certificates were configured in version 5.0.2\n    or above by manually updating the certificates on the disk instead of\n    [updating them through the API](https://redis.io/docs/latest/operate/rs/security/certificates/updating-certificates).\n    For help with this issue, contact Support.\n\n-   Starting from [Redis Software 5.4.2](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-2-april-2019),\n    to preserve the current Redis major.minor version during database upgrade\n    you must use the keep_redis_version option instead of keep_current_version.\n\n#### Redis commands \n\n-   The capability of disabling specific Redis commands does not work on\n    commands specific to Redis modules.\n\n-   CLIENT UNBLOCK command is not supported in RS 5.4 and above\n\n-   Starting from RS 5.4.2 and after upgrading the CRDB, TYPE commands for\n    string data-type in CRDBs return \"string\" (OSS Redis standard).\n    \n#### Security\n\n-   As part of Redis commitment to security, the following [Open Source Redis](https://github.com/redis/redis) [CVE's](https://github.com/redis/redis/security/advisories) have been addressed in Redis Enterprise 6.0.20: \n \n    -   [CVE-2021-32626](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-32626) - Lua scripts can overflow the heap-based Lua stack. This has been addressed in Redis Enterprise 6.0.20-62\n\n    -   [CVE-2021-32627](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-32627) - Integer overflow issue with Streams. This has been addressed in Redis Enterprise 6.0.20-1\n\n    -   [CVE-2021-32628](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-32628) - Vulnerability in handling large ziplists. This has been addressed in Redis Enterprise 6.0.20-1\n\n    -   [CVE-2021-32687](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-32687) - Integer overflow issue with intsets. This has been addressed in Redis Enterprise 6.0.20-89\n\n-   The following [Open Source Redis](https://github.com/redis/redis) [CVE's](https://github.com/redis/redis/security/advisories) do not affect Redis Enterprise:\n\n    -   [CVE-2021-32625](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-32625) - Redis Enterprise is not impacted by the CVE that was found and fixed in open source Redis since Redis Enterprise does not implement `LCS`. Additional information about the open source Redis fix is on [the Redis GitHub page](https://github.com/redis/redis/releases) (Redis 6.2.4, Redis 6.0.14)\n\n    -   [CVE-2021-32672](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-32672) - Redis Enterprise is not impacted by the CVE that was found and fixed in open source Redis because the LUA debugger is unsupported in Redis Enterprise. Additional information about the open source Redis fix is on [the Redis GitHub page](https://github.com/redis/redis/releases) (Redis 6.2.6, Redis 6.0.16)\n\n    -   [CVE-2021-32675](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-32675) - Redis Enterprise is not impacted by the CVE that was found and fixed in open source Redis because the proxy in Redis Enterprise does not forward unauthenticated requests. Additional information about the open source Redis fix is on [the Redis GitHub page](https://github.com/redis/redis/releases) (Redis 6.2.6, Redis 6.0.16)\n\n    -   [CVE-2021-32762](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-32762) - Redis Enterprise is not impacted by the CVE that was found and fixed in open source Redis because the memory allocator used in Redis Enterprise is not vulnerable. Additional information about the open source Redis fix is on [the Redis GitHub page](https://github.com/redis/redis/releases) (Redis 6.2.6, Redis 6.0.16)\n\n    -   [CVE-2021-41099](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41099) - Redis Enterprise is not impacted by the CVE that was found and fixed in open source Redis because the proto-max-bulk-len CONFIG is blocked in Redis Enterprise. Additional information about the open source Redis fix is on [the Redis GitHub page](https://github.com/redis/redis/releases) (Redis 6.2.6, Redis 6.0.16)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-6-0-8-september-2020","title":"Redis Enterprise Software Release Notes 6.0.8 (September 2020)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-0-8-september-2020/","summary":"RediSearch 2.0 support. Improved rladmin support for module upgrades.","tags":["docs","operate","rs"],"last_updated":"2026-10-06T08:53:14-07:00","page_type":"content","content_hash":"c7b3699376a87141ac4a7429359a11675a587d2e33b66fa4741e44166cb70166","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Redis Enterprise Software (RS) 6.0.8](https://redislabs.com/download-center/#downloads) is now available!\nThis version includes the new RediSearch 2.0 module, open source Redis 6.0.5, changes the rladmin tool for upgrading modules, and includes bug fixes."},{"id":"version-information","title":"Version information","role":"compatibility","text":""},{"id":"upgrade-instructions","title":"Upgrade instructions","role":"content","text":"Follow [these instructions](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading) for upgrading to RS 6.0.8 from RS 5.4.0 and above.\nFor Active-Active deployments, this release requires that you [upgrade the CRDB featureset version](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-active-active)."},{"id":"end-of-life","title":"End of life","role":"content","text":"End of Life (EOL) for Redis Enterprise Software 6.0 and previous RS versions, can be found [here](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle).\nEOL for Redis Modules can be found [here](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle).\n\n- Support for Red Hat Enterprise Linux 6 and Oracle Linux 6 [operating systems platforms](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/supported-platforms) will end on November 30, 2020.\n- Support for Ubuntu 14.04 (Trusty Tahr) [operating systems platforms](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/supported-platforms) will end on November 30, 2020."},{"id":"new-features","title":"New features","role":"content","text":""},{"id":"open-source-redis-6","title":"Open source Redis 6","role":"content","text":"RS 6.0 includes open source Redis 6.0.5.\nFor more information about Redis 6.0.5, check out the [release notes](https://raw.githubusercontent.com/redis/redis/6.0.5/00-RELEASENOTES)."},{"id":"upgrading-redis-modules-via-rladmin","title":"Upgrading Redis modules via rladmin","role":"content","text":"The [`rladmin` CLI](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin) introduces several updates to the commands for upgrading modules.\nIt is now easier to upgrade your modules to the latest module version.\nFind out more [here](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/upgrade-module)."},{"id":"redis-modules","title":"Redis modules","role":"content","text":"The following GA releases of Redis Modules are bundled in RS 6.0:\n\n- [RediSearch](https://redislabs.com/redis-enterprise/redis-search/), version [2.0](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch) (updated)\n- [RedisJSON](https://redislabs.com/redis-enterprise/redis-json/), version [1.0.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-1.0-release-notes)\n- [RedisGraph](https://redislabs.com/redis-enterprise/redis-graph/), version [2.0.19](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.0-release-notes) (updated)\n- [RedisTimeSeries](https://redislabs.com/redis-enterprise/redis-time-series/), version [1.2.7](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.2-release-notes) (updated)\n- [RedisBloom](https://redislabs.com/redis-enterprise/redis-bloom/), version [2.2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.2-release-notes) (updated)\n\nTo use the updated modules with a database, you must [upgrade the module on the database](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/upgrade-module)."},{"id":"additional-capabilities","title":"Additional capabilities","role":"content","text":"- [Shard level metrics](https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise/prometheus-metrics-definitions#shard-metrics) have been added to the metrics_exporter and are now available from Prometheus. You can find all of the metrics [here](https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise/prometheus-metrics-definitions).\n\n- RS DEB packages (for Ubuntu) and RPM packages (for RHEL) are now signed with a GPG key so customers can verify that the package is authentic and has not been tampered with. You can access the GPG on the [installaion page](https://redis.io/docs/latest/operate/rs/installing-upgrading#installing-rs-on-linux).\n\n- The [`crdb-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli) history log is now being added to support packages."},{"id":"important-fixes","title":"Important fixes","role":"content","text":"- RS33193 - Improved log files handling in the proxy for large files.\n- RS43572 - Fixed a bug causing the UI to fail when enabling SMTP STARTLS.\n- RS46062 - Fixed missing metrics of Active-Active databases in Grafana.\n- RS44758 - Fixed non responding button for saving a new user via the UI.\nWith build 6.0.8-32:\n- RS45627, RS47382 - Fixed bugs causing clients to disconnect when using XREAD and XREADGROUP commands in blocking mode on other clients’ connections."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"-RS81463 - A shard may crash when resharding an Active-Active database with Auto Tiering . Specifically, the shard will crash when volatile keys or Active-Active tombstone keys reside in Flash memory."},{"id":"active-active-databases","title":"Active-Active databases","role":"content","text":"- RS44656 - A bug causing TLS mode for clients connections to toggle between ‘all communication’ to ‘for crdb communication only’ when performing a global configuration change.\n- RS51359 - Active-Active databases, using replication and Append Only File (AOF) for [Database persistence](https://redis.io/docs/latest/operate/rs/databases/configure/database-persistence), are suffering from memory leaks on replica shards, causing them to grow bigger than the master shards. Customers are advised to upgrade to RS 6.0.12. Meanwhile you can use snapshots for database persistence or restart the replica shards."},{"id":"installation-limitations","title":"Installation limitations","role":"setup","text":"Several Redis Enterprise Software installation reference files are installed to the directory `/etc/opt/redislabs/` even if you use [custom installation directories](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/customize-install-directories).\n\nAs a workaround to install Redis Enterprise Software without using any root directories, do the following before installing Redis Enterprise Software:\n\n1. Create all custom, non-root directories you want to use with Redis Enterprise Software.\n\n1. Mount `/etc/opt/redislabs` to one of the custom, non-root directories."},{"id":"upgrade","title":"Upgrade","role":"content","text":"- [RS 5.4.2](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-2-april-2019) introduced new Active-Active Redis Database capabilities that improve its compatibility with open source Redis. Now the string data-type in Active-Active Redis Database is implicitly and dynamically typed, just like open source Redis. To use the new capabilities on nodes that are upgraded from version RS 5.4.2 or lower, you must [upgrade the Active-Active Redis Database protocol](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading#upgrading-crdbs).\n- When you upgrade an Active-Active Redis with active AOF from version [RS 5.4.2](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-2-april-2019) or lower to version [RS 5.4.2](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-4-june-2019) or higher:\n    - If replication is enabled, you must run the BGREWRITEAOF command on all replica shards after the upgrade.\n    - If replication is not enabled, you must run the BGREWRITEAOF command on all shards after the upgrade.\n- Node upgrade fails if the SSL certificates were configured in version 5.0.2 or above by manually updating the certificates on the disk instead of [updating them through the API](https://redis.io/docs/latest/operate/rs/security/certificates/updating-certificates).\n    For assistance with this issue, contact [Support](https://redislabs.com/support).\n- Starting from [RS 5.4.2](https://redis.io/docs/latest/operate/rs/release-notes/legacy-release-notes/rs-5-4-2-april-2019), to preserve the current Redis major.minor version during database upgrade you must use the keep_redis_version option instead of keep_current_version."},{"id":"redis-commands","title":"Redis commands","role":"content","text":"- The capability of disabling specific Redis commands does not work on commands specific to Redis Modules.\n- Starting from RS 5.4.2 and after you upgrade an Active-Active database, TYPE commands for string data-type in Active-Active databases return \"string\" (OSS Redis standard)."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-6-0-may-2020","title":"Redis Enterprise Software Release Notes 6.0 (May 2020)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-0-may-2020/","summary":"ACL and RBAC improvements for database access. Active-Active databases support Redis Streams.","tags":["docs","operate","rs"],"last_updated":"2026-10-06T08:53:14-07:00","page_type":"content","content_hash":"9abd031e2a51f6ea73515f30b47432c716559d0902a9be77a992d205709446b5","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Redis Enterprise Software (RS) 6.0](https://redislabs.com/download-center/#downloads) is now available!\nThis new version bundles open-source Redis 6, implements enhanced Access Control List (ACL) capabilities using Role-Based Access Control (RBAC) for database access, and adds the support of Redis Streams on Active-Active databases."},{"id":"version-information","title":"Version information","role":"compatibility","text":""},{"id":"upgrade-instructions","title":"Upgrade instructions","role":"content","text":"Follow [these instructions](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading) for upgrading to RS 6.0 from RS 5.4.0 and above.\nFor Active-Active deployments, this release requires that you [upgrade the CRDB featureset version](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-active-active)."},{"id":"end-of-life","title":"End of life","role":"content","text":"End of Life (EOL) for Redis Enterprise Software 6.0 and previous RS versions, can be found [here](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle).\nEOL for Redis Modules can be found [here](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle).\n\n- Support for Red Hat Enterprise Linux 6 and Oracle Linux 6 [operating systems platforms](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/supported-platforms) will end on November 30, 2020.\n- Support for Ubuntu 14.04 (Trusty Tahr) [operating systems platforms](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/supported-platforms) will end on November 30, 2020."},{"id":"new-features","title":"New features","role":"content","text":""},{"id":"open-source-redis-6","title":"Open source Redis 6","role":"content","text":"RS 6.0 bundles latest open source Redis 6.\nFor more information, check out the [Diving into Redis 6](https://redislabs.com/blog/diving-into-redis-6/) article."},{"id":"access-control-list-acl","title":"Access control list (ACL)","role":"content","text":"Based on OSS Redis 6, RS 6.0 offers the ability to manage and control connections to your databases using users and their data access permissions in terms of commands they can execute and keys they can access.\n\nIn OSS Redis, the ACLs are managed separately per user for each database. In Redis Enterprise Software, Redis ACLs are managed for the databases at the cluster.\nFor more information, check out the [Redis Enterprise Software user management documentation](https://redis.io/docs/latest/operate/rs/security/access-control/create-db-roles)."},{"id":"role-based-access-control-rbac","title":"Role-based access control (RBAC)","role":"content","text":"RS 6.0 leverages Redis ACLs to implement role-based access control that easily scale and manage data access permissions. Using roles minimizes the overhead involved in managing a cluster with many databases, multiple users, and various access control lists.\nFor more information, check out the [Redis Enterprise Software user management documentation](https://redis.io/docs/latest/operate/rs/security/access-control)."},{"id":"active-active-support-for-redis-streams","title":"Active-Active support for Redis Streams","role":"content","text":"RS 6.0 adds support for Redis streams on Active-Active geo-distributed databases using conflict-free replicated data type (CRDT). You can now use all Redis streams commands including consumer groups on Active-Active databases. To enable it, [upgrade your Active-Active database](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-active-active) featureset version to the latest (featureset version = 2) as part of the upgrade process.\nFor more information, check out [Redis Streams on Active Active databases](https://redis.io/docs/latest/operate/rs/databases/active-active/develop/data-types/strings)."},{"id":"redis-modules","title":"Redis modules","role":"content","text":"The following GA releases of Redis Modules are bundled in RS 6.0:\n\n- [RedisBloom](https://redislabs.com/redis-enterprise/redis-bloom/), version [2.2.2](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.2-release-notes) (updated)\n- [RedisGraph](https://redislabs.com/redis-enterprise/redis-graph/), version [2.0.11](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.0-release-notes) (updated)\n- [RedisJson](https://redislabs.com/redis-enterprise/redis-json/), version [1.0.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-1.0-release-notes)\n- [RediSearch](https://redislabs.com/redis-enterprise/redis-search/), version [1.6.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-1.6-release-notes) (updated)\n- [RedisTimeSeries](https://redislabs.com/redis-enterprise/redis-time-series/), version [1.2.5](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.2-release-notes) (updated)\n\nTo use the updated modules with a database, you must [upgrade the module on the database](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/upgrade-module)."},{"id":"additional-capabilities","title":"Additional capabilities","role":"content","text":"- Added the ability to configure a storage service that uses the S3 protocol. Configuration for [backup location](https://redis.io/docs/latest/operate/rs/databases/import-export/schedule-backups) and for [import](https://redis.io/docs/latest/operate/rs/databases/import-export/import-data) and [export](https://redis.io/docs/latest/operate/rs/databases/import-export/export-data) locations of [RDB files](https://redis.io/docs/latest/operate/rs/databases/configure/database-persistence) is possible. The storage service must have a valid SSL certificate. To connect to an S3-compatible storage location, run: `rladmin cluster config s3_url <url>`\n\n- The crdb-cli tool was updated so it is now displaying the Active-Active database’s featureset version and the protocol version per instance. For example:\n\n    [code example]\n\n- Added REST API and rladmin commands to modify the timeout for [automatically disconnecting an inactive admin console session](https://redis.io/docs/latest/operate/rs/security/access-control/manage-users/login-lockout#session-timeout).\n    - Using the rladmin run: `rladmin cluster config cm_session_timeout_minutes <int_value>`\n    - Using the REST API:\n\n        [code example]\n\n- Added `no_of_expires` metrics for database metrics and for shard metrics. You can access this metric from the REST API:\n    - `no_of_expires` shows the current number of volatile keys in the database.\n    - `expired_objects` shows the rate of keys expired in DB (expirations/sec).\n\n- Added the ability to customize the welcome message on the login page in the admin console console."},{"id":"important-fixes","title":"Important fixes","role":"content","text":"- RS39121, RS35335 - Optimized the XREAD and XREADGROUP commands so when using them in a non blocking fashion (without BLOCK keyword) they use the shared connection instead of a dedicated connection.\n- RS26448 - Fixed ‘ram overhead’ metric calculation for ROF databases using AOF.\n- RS31190 - Fixed a bug that causes databases with OSS Cluster API enabled to override the ‘preferred IP type’ attribute from ‘external’ to ‘internal’ (the default value).\n- RS34009 - Updated the modules loading procedure for clusters with FIPS compliance enabled.\n- RS38233 - Improved the Redis cleanup job handling the persistent directory\n- RS39228 - Fixed a bug in the WAIT command that in some cases was  released after a longer period than requested.\n- RS39749 - Fixed a bug that blocked eviction while LUA scripts were in progress.\n- RS43996 - Fixed a bug when aborting an upgrade to RS 6.0."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"-RS81463 - A shard may crash when resharding an Active-Active database with Auto Tiering . Specifically, the shard will crash when volatile keys or Active-Active tombstone keys reside in Flash memory."},{"id":"active-active-databases","title":"Active-Active databases","role":"content","text":"- RS51359 - Active-Active databases, using replication and Append Only File (AOF) for [database persistence](https://redis.io/docs/latest/operate/rs/databases/configure/database-persistence), are suffering from memory leaks on replica shards, causing them to grow bigger than the master shards. Customers are advised to upgrade to RS 6.0.12. Meanwhile you can use snapshots for database persistence or restart the replica shards."},{"id":"installation-limitations","title":"Installation limitations","role":"setup","text":"Several Redis Enterprise Software installation reference files are installed to the directory `/etc/opt/redislabs/` even if you use [custom installation directories](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/customize-install-directories).\n\nAs a workaround to install Redis Enterprise Software without using any root directories, do the following before installing Redis Enterprise Software:\n\n1. Create all custom, non-root directories you want to use with Redis Enterprise Software.\n\n1. Mount `/etc/opt/redislabs` to one of the custom, non-root directories."},{"id":"upgrade","title":"Upgrade","role":"content","text":"- RS 5.4.2 introduced new Active-Active Redis Database capabilities that improve its compatibility with open source Redis. Now the string data-type in Active-Active Redis Database is implicitly and dynamically typed, just like open source Redis. To use the new capabilities on nodes that are upgraded from version RS 5.4.2 or lower, you must upgrade the Active-Active Redis Database protocol.\n- When you upgrade an Active-Active Redis with active AOF from version RS 5.4.2 or lower to version RS 5.4.4 or higher:\n    - If replication is enabled, you must run the BGREWRITEAOF command on all replica shards after the upgrade.\n    - If replication is not enabled, you must run the BGREWRITEAOF command on all shards after the upgrade.\n- Starting from RS 5.4.2, to preserve the current Redis major.minor version during database upgrade you must use the keep_redis_version option instead of keep_current_version.\n- Dynatrace agent installed on the cluster nodes can hamper the working on Envoy process leading to failure of UI and REST API. Prior upgrading we recommend removing Dynatrace completely or try upgrading to newer versions."},{"id":"redis-commands","title":"Redis commands","role":"content","text":"- The capability of disabling specific Redis commands does not work on commands specific to Redis Modules.\n- Starting from RS 5.4.2 and after you upgrade an Active-Active database, TYPE commands for string data-type in Active-Active databases return \"string\" (OSS Redis standard)."}],"examples":[{"id":"additional-capabilities-ex0","language":"sh","code":"$ crdb-cli crdb list --verbose\n    CRDB-GUID               NAME                REPL-ID\tPROTOCOL    FEATURESET    DB-ID     CLUSTER-FQDN\n    969122be-...\tloremipsum1           1\t      0 \t   \t 0\t    bdb:1       cluster1.local\n    969122be-...\tloremipsum1           2\t      0 \t   \t 0\t    bdb:1       cluster2.local","section_id":"additional-capabilities"},{"id":"additional-capabilities-ex1","language":"sh","code":"curl --request PUT \\\n        --url https://localhost:9443/v1/cluster \\\n        --header 'content-type: application/json' \\\n        --data '{\n            \"cm_session_timeout_minutes\": 10\n        }'","section_id":"additional-capabilities"}]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-6-2-10-february-2022","title":"Redis Enterprise Software Release Notes 6.2.10 (February 2022)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-10-february-2022/","summary":"Python 3 support. RHEL 8.5 support.","tags":["docs","operate","rs"],"last_updated":"2026-10-06T08:53:14-07:00","page_type":"content","content_hash":"a9c2991311729ac11833f8e07ab8761b98ceddc6b1923d16864d4d7157c0b048","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Redis Enterprise Software version 6.2.10](https://redislabs.com/redis-enterprise-software/download-center/software/) is now available! \n\nThe following table shows the MD5 checksums for the available packages.\n\n|Package| MD5 Checksum |\n|:------|:-------------|\n| Ubuntu 16 | `531cea69a58fbc1125bc5f76ba01da7f` |\n| Ubuntu 18 | `ec9ac6e0111dc85605d3b98e83f50150` |\n| RedHat Enterprise Linux (RHEL) 7<br/>Oracle Enterprise Linux (OL) 7 | `2f7572caab9600417ef8b4ee474d6768` |\n| RedHat Enterprise Linux (RHEL) 8<br/>Oracle Enterprise Linux (OL) 8 | `377a539ee050515e1e0640dec1e04129` |\n| K8s Ubuntu | `099192416a70a12790535bdcd78a6e87` |\n| K8s RHEL   | `f267abe81770ddf36f022232f4c2cb2e` |"},{"id":"features-and-enhancements","title":"Features and enhancements","role":"content","text":"- Upgrade the Redis Enterprise infrastructure to [Python v3.9](https://www.python.org/).\n\n- [Red Hat Enterprise Linux (RHEL) v8.5](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/8.5_release_notes/index) and [Red Hat Enterprise Linux (RHEL) v8.6](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/8.6_release_notes/index) is now a [supported platform](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/supported-platforms).\n\n- [Oracle Linux v8](https://docs.oracle.com/en/operating-systems/oracle-linux/8/) is now a [supported platform](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/plan-deployment/supported-platforms).\n\n- Compatibility with [open source Redis 6.2.5](https://raw.githubusercontent.com/redis/redis/6.2/00-RELEASENOTES).\n\n- Compatibility with the [security fixes](https://github.com/redis/redis/releases/tag/6.2.6) of the latest [open source Redis 6.2.6](https://github.com/redis/redis/releases/tag/6.2.6).\n\n- Enhancements and bug fixes."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"prerequisites-and-notes","title":"Prerequisites and notes","role":"content","text":"-  You can [upgrade to v6.2.10](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading) from Redis Enterprise Software v6.0 and later. \n\n- Refer to [v6.2.4 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-4-august-2021) for important notes regarding changes made to the upgrade.\n\n- Upgrades from versions earlier than v6.0 are not supported.\n\n- If you plan to upgrade your cluster to RHEL 8, refer to [v6.2.8 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-8-october-2021) for known limitations.\n\n- If you are using Active-Active or Active-Passive (ReplicaOf) databases and experience synchronization issues as a result of the upgrade, see RS67434 details in [Resolved issues](#resolved-issues) for help resolving the problem."},{"id":"product-lifecycle-updates","title":"Product lifecycle updates","role":"content","text":"Redis Enterprise Software v6.0.x will reach end of life (EOF) on May 31, 2022.\n\nTo learn more, see the Redis Enterprise Software [product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle), which details the release number and the end-of-life schedule for Redis Enterprise Software.\n\nFor Redis modules information and lifecycle, see [Module lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules-lifecycle)."},{"id":"redis-modules","title":"Redis modules","role":"content","text":"Redis Enterprise Software v6.2.10 includes the following Redis modules:\n\n- [RediSearch v2.2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch)\n- [RedisJSON v2.0.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson)\n- [RedisBloom v2.2.9](/operate/oss_and_stack/stack-with-enterprise/bloom/release-notes/)\n- [RedisGraph v2.4.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph)\n- [RedisTimeSeries v1.4.13](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries)\n\nStarting with Redis Enterprise Software v6.2.10 build 121, the included modules versions are:\n\n- [RediSearch v2.4.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch)\n- [RedisJSON v2.0.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson)\n- [RedisBloom v2.2.14](/operate/oss_and_stack/stack-with-enterprise/bloom/release-notes/)\n- [RedisGraph v2.8.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph)\n- [RedisTimeSeries v1.6.9](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries)\n\nFor help upgrading a module, see [Add a module to a cluster](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/add-module-to-cluster#upgrading-the-module-for-the-database)."},{"id":"interface-enhancements","title":"Interface enhancements","role":"content","text":"- When choosing RedisJSON, the user interface (UI) now suggests RedisSearch as well. To learn more, see the [RedisJSON preview announcement](https://redis.com/blog/redisjson-public-preview-performance-benchmarking/), which details the benefits of combining [RedisJSON](http://redisjson.io/) and [RediSearch](http://redisearch.io/).\n- Adds the ability to sort the columns of the node list (RS48256).\n- When creating a new geo-distributed (Active-Active) database, an endpoint port is no longer required.  The system assigns one if none if provided (RS27632)."},{"id":"additional-enhancements","title":"Additional enhancements","role":"content","text":"- Added an option to run a connectivity health check for the management layer of Active-Active databases. Run the following REST API command:\n\n    [code example]\n\n- Added TLS handshake error messages to the DMC proxy log (RS59346)."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS58219 - Fixes a UI error message that showed a path instead of a relevant error message.\n- RS44958 - Fixes incorrect description for the graph \"incoming traffic\" in Active-Active (geo-distributed) database UI Metrics.\n- RS66280 - Fixes the lexicographic [SORT](https://redis.io/docs/latest/commands/sort) command on Active-Active databases (e.g. `SORT mylist ALPHA`). The SORT command should only run on keys mapped to the same slot.\n- RS64575 - Fixes a bug in the replication between primary and replica shards of a destination Active-active database in the scenario of using Replica-Of from a single to an Active-Active database, where the syncer process went down during the full sync.\n- RS65370 - Adds logic to remove old syncer entries in the cluster configuration during upgrades.\n- RS67434 - Version 6.2.10 fixes the mTLS handshake between the [syncer process](https://redis.io/docs/latest/operate/rs/databases/active-active#syncer-process) and the [proxy (DMC)](https://docs.redis.com/latest//rs/references/terminology/#proxy), where the proxy presented a leaf certificate without its full chain to the syncer. After upgrading to 6.2.10, syncer connections using invalid certificates will break the synchronization between Active-Active instances or deployments using Replica Of when TLS is enabled. To ensure certificates are valid before upgrading do the following: \n\n    - For Active-Active databases, run the following command from one of the clusters:\n        \n        `crdb-cli crdb update --crdb-guid <CRDB-GUID> --force`\n         \n    - For Active-Passive (Replica Of) databases: use the admin console to verify that the destination syncer has the correct certificate for the source proxy (DMC).  For details, see [Configure TLS for Replica Of](https://redis.io/docs/latest/operate/rs/databases/import-export/replica-of/create#configure-tls-on-replica-database)."},{"id":"issues-resolved-in-build-96","title":"Issues resolved in build 96","role":"content","text":"- RS67133 - An issue in Redis Enterprise Software affected replication in replica databases using RedisGraph, RediSearch, and RedisGears in specific scenarios.  The problem appeared when importing an RDB file or while synchronizing target Active-Passive (ReplicaOf) databases. \n\n    This issue is fixed in Redis Enterprise Software v6.2.10-96 and RedisGraph v2.8.11.  We recommend upgrading to these versions at your earliest opportunity.  (Failure to upgrade can lead to data loss.)  \n    \n    Once the upgrades are complete, secondary shards might need to be restarted.  You can use `rlutil` to restart secondary shards:\n\n    [code example]"},{"id":"issues-resolved-in-build-100","title":"Issues resolved in build 100","role":"content","text":"- RS74171 - A new command was added as part of Redis 6.2: [XAUTOCLAIM](https://redis.io/docs/latest/commands/xautoclaim). When used in an Active-Active configuration, this command may cause Redis shards to crash, potentially resulting in data loss. The issue is fixed in Redis Enterprise Software version 6.2.12. Additionally, we recommend enabling AOF persistence for all Active-Active configurations."},{"id":"issues-resolved-in-build-121","title":"Issues resolved in build 121","role":"content","text":"- RS68668, RS72082 - Improvements for internode encryption certification rotation\n- RS72304 - Avoid starting a master shard when both master and replica shards crash and the replica did not finish recovery\n- RS74469 - Fix for some Redis Active-Active + Redis Streams scenarios that could lead to shard crash during backup; failure to backup"},{"id":"issues-resolved-in-build-129","title":"Issues resolved in build 129","role":"content","text":"- RS77003 - Add grace time to job scheduler to allow certificate rotation in case of failure due to scheduling conflicts.\n- RS71112 - Update validation during db configuration to not fail due to ports associated with nodes that are no longer in the cluster. This was done to allow db configuration during adding and removing nodes as part of load balancing.\n- RS78486 - Fix known issue from 6.2.10 build 100 - When using rladmin tune db to change the replica buffer size, the command appears to succeed, but the change does not take effect."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"- RS81463 - A shard may crash when resharding an Active-Active database with Auto Tiering . Specifically, the shard will crash when volatile keys or Active-Active tombstone keys reside in Flash memory.\n\n- RS78364 - When using `rladmin tune db` to change the replica buffer size, the command appears to succeed, but the change does not take effect. This issue was introduced in build 100; it will be fixed in a future build of Redis Enterprise Software v6.2.10 and in the next release (v6.2.12).\n\n- RS63258 - Redis Enterprise Software is not currently supported on RHEL 8 with FIPS enabled.\n\n    FIPS changes system-generated keys, which can limit secure access to the cluster or the admin console via port 8443.\n\n- RS63375 - RHEL 7 clusters cannot be directly upgraded to RHEL 8 when hosting databases using modules.\n\n    Due to binary differences in modules between the two operating systems, you cannot directly update RHEL 7 clusters to RHEL 8 when those clusters host databases using modules.  Instead, you need to create a new cluster on RHEL 8 and then migrate existing data from your RHEL 7 cluster. This does not apply to clusters that do not use modules.\n\nAll [known limitations](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-4-august-2021#known-limitations) listed in the v6.2.4 release notes have been addressed."},{"id":"installation-limitations","title":"Installation limitations","role":"setup","text":"Several Redis Enterprise Software installation reference files are installed to the directory `/etc/opt/redislabs/` even if you use [custom installation directories](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/customize-install-directories).\n\nAs a workaround to install Redis Enterprise Software without using any root directories, do the following before installing Redis Enterprise Software:\n\n1. Create all custom, non-root directories you want to use with Redis Enterprise Software.\n\n1. Mount `/etc/opt/redislabs` to one of the custom, non-root directories."},{"id":"known-issues","title":"Known issues","role":"content","text":"- The `ZRANGESTORE` command, with a special `zset-max-ziplist-entries` configuration can crash Redis 6.2. See [Redis repository 10767](https://github.com/redis/redis/pull/10767) for more details.\n\n- RS40641 - API requests are redirected to an internal IP in case the request arrives from a node which is not the master. To avoid this issue, use [`rladmin cluster config`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/config) to configure `handle_redirects` or `handle_metrics_redirects`."},{"id":"security","title":"Security","role":"security","text":""},{"id":"open-source-redis-security-fixes-compatibility","title":"Open Source Redis Security fixes compatibility","role":"content","text":"As part of Redis commitment to security, Redis Enterprise Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with open source Redis. The following [Open Source Redis](https://github.com/redis/redis) [CVE’s](https://github.com/redis/redis/security/advisories) do not affect Redis Enterprise:\n\n- [CVE-2021-32625](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-32625) - Redis Enterprise is not impacted by the CVE that was found and fixed in open source Redis since Redis Enterprise does not implement LCS. Additional information about the open source Redis fix is on [the Redis GitHub page](https://github.com/redis/redis/releases) (Redis 6.2.4, Redis 6.0.14)\n\n- [CVE-2021-32672](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-32672) - Redis Enterprise is not impacted by the CVE that was found and fixed in open source Redis because the LUA debugger is unsupported in Redis Enterprise. Additional information about the open source Redis fix is on [the Redis GitHub page](https://github.com/redis/redis/releases) (Redis 6.2.6, Redis 6.0.16)\n\n- [CVE-2021-32675](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-32675) - Redis Enterprise is not impacted by the CVE that was found and fixed in open source Redis because the proxy in Redis Enterprise does not forward unauthenticated requests. Additional information about the open source Redis fix is on [the Redis GitHub page](https://github.com/redis/redis/releases) (Redis 6.2.6, Redis 6.0.16)\n\n- [CVE-2021-32762](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-32762) - Redis Enterprise is not impacted by the CVE that was found and fixed in open source Redis because the memory allocator used in Redis Enterprise is not vulnerable. Additional information about the open source Redis fix is on [the Redis GitHub page](https://github.com/redis/redis/releases) (Redis 6.2.6, Redis 6.0.16)\n\n- [CVE-2021-41099](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41099) - Redis Enterprise is not impacted by the CVE that was found and fixed in open source Redis because the proto-max-bulk-len CONFIG is blocked in Redis Enterprise. Additional information about the open source Redis fix is on [the Redis GitHub page](https://github.com/redis/redis/releases) (Redis 6.2.6, Redis 6.0.16)security fixes for [recent CVE’s](https://github.com/redis/redis/security/advisories). Redis Enterprise has already included the fixes for the relevant CVE’s. Some CVE’s announced for Open Source Redis do not affect Redis Enterprise due to different and additional functionality available in Redis Enterprise that is not available in Open Source Redis."}],"examples":[{"id":"additional-enhancements-ex0","language":"sh","code":"GET https:/[host][:port]/v1/crdbs/<crdb_guid>/health_report","section_id":"additional-enhancements"},{"id":"issues-resolved-in-build-96-ex0","language":" sh","code":"rlutil redis_restart redis=<shard-id1>,<shard-id2>,...","section_id":"issues-resolved-in-build-96"}]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-6-2-12","title":"Redis Enterprise Software release notes 6.2.12 (August 2022)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-12/","summary":"OCSP Support. Password & session configuration changes. RHEL 8.6 support.","tags":["docs","operate","rs"],"last_updated":"2026-10-06T08:53:14-07:00","page_type":"content","content_hash":"7a2666f4fb4c56a2211ce14b4acb7d8d7fbf59190f79aab2421324be05940835","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Redis Enterprise Software version 6.2.12](https://redislabs.com/redis-enterprise-software/download-center/software/) is now available! \n\nThis version of Redis Enterprise Software offers:\n\n- OCSP stapling of the server proxy certificate\n- Password and session configuration settings via the admin console\n- Compatibility with open source Redis v6.2.6\n- Support for Red Hat Enterprise Linux (RHEL) v8.6\n- Additional enhancements and fixes\n\nThe following table shows the MD5 checksums for the available packages.\n\n|Package| MD5 Checksum |\n|:------|:-------------|\n| Ubuntu 16 | `e702c906f200940e06ef031e6b8006d9` |\n| Ubuntu 18 | `7ea70067e8828b59336380df087fe03d` |\n| RedHat Enterprise Linux (RHEL) 7<br/>Oracle Enterprise Linux (OL) 7 | `8ffda6186f70354b9d10c1ce43938c3c` |\n| RedHat Enterprise Linux (RHEL) 8<br/>Oracle Enterprise Linux (OL) 8 | `334fe7979a7376b28fcf48913403bfb7` |"},{"id":"features-and-enhancements","title":"Features and enhancements","role":"content","text":"- Server side OCSP Stapling\n\n    [Online Certificate Status Protocol](https://en.wikipedia.org/wiki/Online_Certificate_Status_Protocol) (OCSP) helps verify the status of certificates managed by a third-party [certificate authority](https://en.wikipedia.org/wiki/Certificate_authority) (CA).  It tells you whether certificates are valid, revoked, or unknown.\n\n    Redis Enterprise Software v6.2.12 implements OCSP stapling, which allows clients to validate the status of a server proxy certificate.  When OCSP is enabled, the Redis Enterprise server regularly polls the CA OCSP responder to determine a certificate's status.  The response is cached until the next polling attempt; the cached value is served to clients during the [TLS handshake](https://en.wikipedia.org/wiki/Transport_Layer_Security#TLS_handshake).\n\n    To learn more, see [Enable OCSP stapling](https://redis.io/docs/latest/operate/rs/security/certificates/ocsp-stapling). \n\n- Session and security attributes in the admin console\n\n    You can now use the admin console to configure [password complexity rules](https://redis.io/docs/latest/operate/rs/security/access-control/manage-passwords#password-complexity-rules), [user login lockout](https://redis.io/docs/latest/operate/rs/security/access-control/manage-users/login-lockout#user-login-lockout), and [session timeout](https://redis.io/docs/latest/operate/rs/security/access-control/manage-users/login-lockout#session-timeout).  \n    \n- Mount point import enhancement\n\n    When importing data, Redis Enterprise copies files to a temporary directory on the node. For mount point import sources only, Redis Enterprise now reads files directly from the mount point. Because this import method does not copy files to a temporary directory, nodes do not require extra disk space. This new behavior is enabled by default and does not require configuration."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"prerequisites-and-notes","title":"Prerequisites and notes","role":"content","text":"-  You can [upgrade to v6.2.12](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading) from Redis Enterprise Software v6.0 and later. \n\n- Refer to [v6.2.4 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-4-august-2021) for important notes regarding changes made to the upgrade policy and how those changes might impct your experience. \n\n- Upgrades from versions earlier than v6.0 are not supported.\n\n- If you are using the earlier cluster-based LDAP mechanism, you _must_ migrate to the role-based mechanism _before_ upgrading to v6.2.12.  For details, see [Migrate to role-based LDAP](https://redis.io/docs/latest/operate/rs/security/access-control/ldap/migrate-to-role-based-ldap).\n\n- If you plan to upgrade your cluster to RHEL 8, see the [v6.2.8 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-8-october-2021) for known limitations."},{"id":"future-deprecation-notice","title":"Future deprecation notice","role":"content","text":"#### TLS 1.0 and TLS 1.1 \n\nTLS 1.0 and TLS 1.1 connections are considered deprecated in favor of TLS 1.2 or later.  \n\nPlease verify that all clients, apps, and connections support TLS 1.2.  Support for the earlier protocols will be removed in a future release.\n\nCertain operating systems, such as RHEL 8, have already removed support for the earlier protocols.  Redis Enterprise Software cannot support connection protocols that are not supported by the underlying operating system."},{"id":"product-lifecycle-updates","title":"Product lifecycle updates","role":"content","text":"Redis Enterprise Software v6.0.x will reach end of life (EOL) on May 31, 2022.\n\nTo learn more, see the Redis Enterprise Software [product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle), which details the release number and the end-of-life schedule for Redis Enterprise Software.\n\nFor Redis modules information and lifecycle, see [Module lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules-lifecycle)."},{"id":"redis-modules","title":"Redis modules","role":"content","text":"Redis Enterprise Software v6.2.12 includes the following Redis modules:\n\n- [RediSearch v2.4.9](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch)\n- [RedisJSON v2.0.11](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson)\n- [RedisBloom v2.2.17](/operate/oss_and_stack/stack-with-enterprise/bloom/release-notes/)\n- [RedisGraph v2.8.15](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph)\n- [RedisTimeSeries v1.6.16](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries)\n\nFor help upgrading a module, see [Add a module to a cluster](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/add-module-to-cluster#upgrading-the-module-for-the-database)."},{"id":"interface-enhancements","title":"Interface enhancements","role":"content","text":"- Allow creation and editing of sharded databases with a single shard.  This lets you prepare for future scaling by ensuring your apps work in clustering mode, regardless of the actual number of configured shards\n- Enhanced the slowlog in the UI to display unprintable characters in RESTORE commands\n- Added support for custom paths when bootstrapping the cluster via the UI\n- Improve readability of geo-distributed log entries in the UI"},{"id":"additional-enhancements","title":"Additional enhancements","role":"content","text":"- Added support for the MODULE LIST command on Active-Active databases\n- Enhanced validity checks of the input parameters of the CRDB-CLI tool"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS38320 A failed task leaves the nodes list outdated in the UI\n- RS73768, RS72082 Increased certificate rotation timeout to allow it to finish \n- RS72466, RS68668 Fix false positive alerts for certification expiration\n- RS69256 Change pre-bootstrap default TLS version to 1.2\n- RS67133 Fixed replication for command RESTOREMODAUX \n- RS66468 Fixed \"Test regex keys\" in the UI\n- RS58156 Fixed the installation to abort and alert when encountering issues in NTP\n- RS67935 Fixed releasing 30MB of memory when deleting an Active-Active database\n- RS64276 Fixed high memory consumption of the DMC output buffers when running CLIENT LIST"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"- RS81463 A shard may crash when resharding an Active-Active database with Auto Tiering . Specifically, the shard will crash when volatile keys or Active-Active tombstone keys reside in Flash memory.\n\n- RS54131 Returning +OK reply from the QUIT command on a TLS enabled database\n\n- RS40641 - API requests are redirected to an internal IP in case the request arrives from a node which is not the master. To avoid this issue, use [`rladmin cluster config`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/config) to configure `handle_redirects` or `handle_metrics_redirects`."},{"id":"installation-limitations","title":"Installation limitations","role":"setup","text":"Several Redis Enterprise Software installation reference files are installed to the directory `/etc/opt/redislabs/` even if you use [custom installation directories](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/customize-install-directories).\n\nAs a workaround to install Redis Enterprise Software without using any root directories, do the following before installing Redis Enterprise Software:\n\n1. Create all custom, non-root directories you want to use with Redis Enterprise Software.\n\n1. Mount `/etc/opt/redislabs` to one of the custom, non-root directories."},{"id":"security","title":"Security","role":"security","text":""},{"id":"open-source-redis-security-fixes-compatibility","title":"Open Source Redis Security fixes compatibility","role":"content","text":"As part of Redis commitment to security, Redis Enterprise Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with open source Redis. The following [Open Source Redis](https://github.com/redis/redis) [CVEs](https://github.com/redis/redis/security/advisories) do not affect Redis Enterprise:\n\n- [CVE-2022-24834](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-24834) - Redis Enterprise versions 6.2.12 and later are not impacted by the CVE that was found and fixed in open source Redis. A fix to prevent Lua scripts from causing heap overflow was implemented in Redis Enterprise version 6.2.12. Additional information about the open source Redis fix is on [the Redis GitHub page](https://github.com/redis/redis/releases) (Redis 7.0.12, Redis 6.2.13, Redis 6.0.20)\n\n- [CVE-2021-32625](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-32625) - Redis Enterprise is not impacted by the CVE that was found and fixed in open source Redis since Redis Enterprise does not implement LCS. Additional information about the open source Redis fix is on [the Redis GitHub page](https://github.com/redis/redis/releases) (Redis 6.2.4, Redis 6.0.14)\n\n- [CVE-2021-32672](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-32672) - Redis Enterprise is not impacted by the CVE that was found and fixed in open source Redis because the LUA debugger is unsupported in Redis Enterprise. Additional information about the open source Redis fix is on [the Redis GitHub page](https://github.com/redis/redis/releases) (Redis 6.2.6, Redis 6.0.16)\n\n- [CVE-2021-32675](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-32675) - Redis Enterprise is not impacted by the CVE that was found and fixed in open source Redis because the proxy in Redis Enterprise does not forward unauthenticated requests. Additional information about the open source Redis fix is on [the Redis GitHub page](https://github.com/redis/redis/releases) (Redis 6.2.6, Redis 6.0.16)\n\n- [CVE-2021-32762](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-32762) - Redis Enterprise is not impacted by the CVE that was found and fixed in open source Redis because the memory allocator used in Redis Enterprise is not vulnerable. Additional information about the open source Redis fix is on [the Redis GitHub page](https://github.com/redis/redis/releases) (Redis 6.2.6, Redis 6.0.16)\n\n- [CVE-2021-41099](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41099) - Redis Enterprise is not impacted by the CVE that was found and fixed in open source Redis because the proto-max-bulk-len CONFIG is blocked in Redis Enterprise. Additional information about the open source Redis fix is on [the Redis GitHub page](https://github.com/redis/redis/releases) (Redis 6.2.6, Redis 6.0.16) security fixes for [recent CVEs](https://github.com/redis/redis/security/advisories).\n\nRedis Enterprise has already included the fixes for the relevant CVEs. Some CVEs announced for open source Redis do not affect Redis Enterprise due to different and additional functionality available in Redis Enterprise that is not available in open source Redis."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-6-2-18-releases","title":"Redis Enterprise Software release notes 6.2.18","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-18-releases/","summary":"Database auditing. Private key encryption. Active-Active database support for MEMORY USAGE command.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","children":[{"id":"operate/rs/release-notes/rs-6-2-18-releases/rs-6-2-18-70","summary":"New script to generate new self-signed certificates for renewal. Improved logs. Deactivate alert_mgr using rladmin.","title":"Redis Enterprise Software release notes 6.2.18-70 (January 2023)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-18-releases/rs-6-2-18-70/"},{"id":"operate/rs/release-notes/rs-6-2-18-releases/rs-6-2-18-65","summary":"Added To field to test email server alerts. New flag to skip cluster resource validation during imports. Improved error messages.","title":"Redis Enterprise Software release notes 6.2.18-65 (December 2022)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-18-releases/rs-6-2-18-65/"},{"id":"operate/rs/release-notes/rs-6-2-18-releases/rs-6-2-18-58","summary":"UI support for custom REST API port. Added info level for troubleshooting redis_mgr.","title":"Redis Enterprise Software release notes 6.2.18-58 (November 2022)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-18-releases/rs-6-2-18-58/"},{"id":"operate/rs/release-notes/rs-6-2-18-releases/rs-6-2-18-49","summary":"New REST API endpoint to check whether a node is a primary or a replica. Added metrics to track certificate expiration time.","title":"Redis Enterprise Software release notes 6.2.18-49 (October 2022)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-18-releases/rs-6-2-18-49/"},{"id":"operate/rs/release-notes/rs-6-2-18-releases/rs-6-2-18-43","summary":"Database auditing. Private key encryption. Active-Active database support for MEMORY USAGE command. Improvements to crdb-cli.","title":"Redis Enterprise Software release notes 6.2.18-43 (September 2022)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-18-releases/rs-6-2-18-43/"}],"page_type":"content","content_hash":"1e26886b7919c70b8fd48c59bf1d31defb133b87045eedc5b200a821b786d22c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!WARNING]\n> Redis Software 6.2 reached end of life on February 28, 2025. It no longer\n> receives security patches, bug fixes, or maintenance releases.\n> Support for 6.2-specific issues is limited per your subscription agreement,\n> and you may be asked to upgrade before an issue can be investigated.\n>\n> Upgrade to a supported version. Reaching the latest version may require an\n> intermediate upgrade first — see the\n> [supported upgrade paths](https://redis.io/docs/latest/operate/rs/references/upgrade-paths)\n> and the [Redis Software product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule).\n\n[Redis Enterprise Software version 6.2.18](https://redislabs.com/redis-enterprise-software/download-center/software/) is now available! \n\nThis version of Redis Enterprise Software offers:\n\n- RedisJSON on Active-Active General Availability \n- Database connection auditing\n- Private key encryption\n- Active-Active support for `memory usage` command\n- `crdb-cli` improvements\n- Compatibility with open source Redis v6.2.6\n- Additional enhancements and fixes"},{"id":"detailed-release-notes","title":"Detailed release notes","role":"content","text":"For more detailed release notes, select a build version from the following table:\n\n\n| Version (Release date) | Major changes | OSS Redis compatibility |\n| --- | --- | --- |\n| [6.2.18-70 (January 2023)](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-18-releases/rs-6-2-18-70/) | New script to generate new self-signed certificates for renewal. Improved logs. Deactivate alert_mgr using rladmin. | Redis 6.2.6, 6.0 |\n| [6.2.18-65 (December 2022)](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-18-releases/rs-6-2-18-65/) | Added To field to test email server alerts. New flag to skip cluster resource validation during imports. Improved error messages. | Redis 6.2.6, 6.0 |\n| [6.2.18-58 (November 2022)](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-18-releases/rs-6-2-18-58/) | UI support for custom REST API port. Added info level for troubleshooting redis_mgr. | Redis 6.2.6, 6.0 |\n| [6.2.18-49 (October 2022)](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-18-releases/rs-6-2-18-49/) | New REST API endpoint to check whether a node is a primary or a replica. Added metrics to track certificate expiration time. | Redis 6.2.6, 6.0 |\n| [6.2.18-43 (September 2022)](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-18-releases/rs-6-2-18-43/) | Database auditing. Private key encryption. Active-Active database support for MEMORY USAGE command. Improvements to crdb-cli. | Redis 6.2.6, 6.0 |"},{"id":"deprecations","title":"Deprecations","role":"content","text":""},{"id":"active-active-database-persistence","title":"Active-Active database persistence","role":"content","text":"The snapshot option for [data persistence on Active-Active databases](https://redis.io/docs/latest/operate/rs/databases/active-active/manage#data-persistence) will be deprecated in a future version of Redis Enterprise Software. If you have an Active-Active database using snapshot persistence, switch to AOF persistence. Use `crdb-cli` to do so:\n[code example]"},{"id":"tls-10-and-tls-11","title":"TLS 1.0 and TLS 1.1","role":"content","text":"TLS 1.0 and TLS 1.1 connections are considered deprecated in favor of TLS 1.2 or later.  \n\nPlease verify that all clients, apps, and connections support TLS 1.2.  Support for the earlier protocols will be removed in a future release.\n\nCertain operating systems, such as RHEL 8, have removed support for the earlier protocols. Redis Enterprise Software does not support connection protocols that are not supported by the underlying operating system."},{"id":"3des-encryption-cipher","title":"3DES encryption cipher","role":"content","text":"The 3DES encryption cipher is considered deprecated in favor of stronger ciphers like AES.\n\nPlease verify that all clients, apps, and connections support the AES cipher. Support for 3DES will be removed in a future release.\n\nCertain operating systems, such as RHEL 8, have already removed support for 3DES. Redis Enterprise Software cannot support cipher suites that are not supported by the underlying operating system."},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"feature-limitations","title":"Feature limitations","role":"content","text":"- RS54131 Running the `QUIT` command on a TLS connected database closes connection and does not return a `+OK` reply\n\n- An intermittent issue can occur where a CRDB process becomes stuck in a restart loop. If this issue occurs while upgrading to Redis Enterprise Software version 6.2.18, please upgrade to the latest version [6.4.2-69](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-69) or [contact support](https://redis.com/company/support/).\n\n- RS40641 - API requests are redirected to an internal IP in case the request arrives from a node which is not the master. To avoid this issue, use [`rladmin cluster config`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/config) to configure `handle_redirects` or `handle_metrics_redirects`.\n\n- RS51144, RS102128 - Active-Active: To start successfully, the syncer (`crdt-syncer`) must connect to all sources. In multi-cluster configurations (more than 2 A-A clusters participating), in some cases, if one or more of the clusters is not available, A-A replication will be down."},{"id":"installation-limitations","title":"Installation limitations","role":"setup","text":"Several Redis Enterprise Software installation reference files are installed to the directory `/etc/opt/redislabs/` even if you use [custom installation directories](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/customize-install-directories).\n\nAs a workaround to install Redis Enterprise Software without using any root directories, do the following before installing Redis Enterprise Software:\n\n1. Create all custom, non-root directories you want to use with Redis Enterprise Software.\n\n1. Mount `/etc/opt/redislabs` to one of the custom, non-root directories."},{"id":"upgrade-limitations","title":"Upgrade limitations","role":"content","text":"Before you upgrade a cluster that hosts Active-Active databases with modules to v6.2.18, perform the following steps:\n\n1. Use `crdb-cli` to verify that the modules (`modules`) and their versions (in `module_list`) are as they appear in the database configuration and in the default database configuration:\n\n    [code example]\n\n1. From the admin console's **redis modules** tab, validate that these modules with their specific versions are loaded to the cluster.\n\n1. If one or more of the modules/versions are missing or if you need help, [contact Redis support](https://redis.com/company/support/) before taking additional steps."}],"examples":[{"id":"active-active-database-persistence-ex0","language":"text","code":"crdb-cli crdb update --crdb-guid <CRDB_GUID> --default-db-config '{\"data_persistence\": \"aof\", \"aof_policy\":\"appendfsync-every-sec\"}'","section_id":"active-active-database-persistence"},{"id":"upgrade-limitations-ex0","language":"sh","code":"crdb-cli crdb get --crdb-guid <crdb-guid>","section_id":"upgrade-limitations"}]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-6-2-18-releases/rs-6-2-18-43","title":"Redis Enterprise Software release notes 6.2.18-43 (September 2022)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-18-releases/rs-6-2-18-43/","summary":"Database auditing. Private key encryption. Active-Active database support for MEMORY USAGE command. Improvements to crdb-cli.","tags":["docs","operate","rs"],"last_updated":"2026-10-06T08:53:14-07:00","page_type":"content","content_hash":"0cb3a1ca1506bffbb6e96504b72b3d8906b38647870e153b650a4df07f070b65","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Redis Enterprise Software version 6.2.18](https://redislabs.com/redis-enterprise-software/download-center/software/) is now available! \n\nThis version of Redis Enterprise Software offers:\n\n- RedisJSON on Active-Active General Availability \n- Database connection auditing\n- Private key encryption\n- Active-Active support for `memory usage` command\n- `crdb-cli` improvements\n- Compatibility with open source Redis v6.2.6\n- Additional enhancements and fixes\n\nThe following table shows the MD5 checksums for the available packages:\n\n| Package | MD5 checksum (6.2.18-43 September release) |\n|:--------|:-------------|\n| Ubuntu 16 | `055973eb7009073b0c199ec1dfd81018` |\n| Ubuntu 18 | `8c37c6ae10b0ae4956e3c11db80d18ce` |\n| RedHat Enterprise Linux (RHEL) 7<br/>Oracle Enterprise Linux (OL) 7 | `c770a66d9bfdd8734f1208d64aa67784` |\n| RedHat Enterprise Linux (RHEL) 8<br/>Oracle Enterprise Linux (OL) 8 | `85eb6339f837205d83f215e32c1d028f` |"},{"id":"new-features-and-enhancements","title":"New features and enhancements","role":"content","text":"#### General Availability of Active-Active databases with RedisJSON\n\nActive-Active databases now support Index, query, and full-text search of nested JSON documents when combining RedisJSON with RediSearch.\n\n#### Database connection auditing\n\nYou can now [audit database connection](https://redis.io/docs/latest/operate/rs/security/audit-events) and authentication events to track and troubleshoot activity.  Administrators can now use third-party systems to track and analyze connection events in realtime.  \n\n#### Private key encryption\n\nWhen enabled, private key encryption encrypts private keys stored in the cluster configuration store (CCS).  Private keys are encrypted using a secure, self-contained internal process.  Databases must be at least version 6.2.2 or later to use this feature.\n\n#### Active-Active support for MEMORY USAGE command\n\nRedis Enterprise Active-Active databases now support the [MEMORY USAGE](https://redis.io/docs/latest/commands/memory-usage) command, which simplifies troubleshooting and lets applications detect anomalous behavior and dangerous trends.\nTo learn more, see  [`Active-Active with RedisJSON`](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json/active-active).\n\nMEMORY USAGE reports the RAM memory use, in bytes, of a key and its value.  The result includes the memory allocated for the data value and the administrative overhead associated with the key.\n\n#### `crdb-cli` improvements\n\nThe `crdb-cli` utility used to manage Active-Active databases now allows greater visibility into management operations to make it easier to investigate and troubleshoot problems. You can now use:\n\n- `crdb-cli task list` to retrieve details about current and previous tasks for all Active-Active databases on a cluster\n\n- `crdb-cli task --task-id <task-id>` to get detailed information about a specific task.\n\nTo learn more, see [`crdb-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli)\n\n#### Redis modules \n\nRedis Enterprise Software v6.2.18-43 (September release) includes the following Redis modules:\n\n- [RediSearch v2.4.11](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.4-release-notes#v2411-july-2022)\n- [RedisJSON v2.2.0](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.2-release-notes#v220-july-2022)\n- [RedisBloom v2.2.18](/operate/oss_and_stack/stack-with-enterprise/bloom/release-notes/redisbloom-2.2-release-notes/#v2218-july-2022)\n- [RedisGraph v2.8.17](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.8-release-notes#v2817-july-2022)\n- [RedisTimeSeries v1.6.17](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.6-release-notes#v1617-july-2022)\n\nSee [Upgrade modules](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/upgrade-module) to learn how to upgrade a module for a database. \n\n#### Additional enhancements\n\n- Added support for the MODULE LIST command on Active-Active databases\n- Enhanced validity checks of the input parameters of the CRDB-CLI tool\n- The Syncer lag calculation has been improved and fixes multiple miscalculations\n- Support package includes additional information for Active-Active databases\n- Added the ability to retrieve the syncer mode (centralized / distributed) by running   \n\n    `rladmin info db [{db:<id> | <name>}]`  \n\n    To learn more, see [Distributed sychronization](https://redis.io/docs/latest/operate/rs/databases/active-active/synchronization-mode)"},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"RS84006 - When using the REST API to create a database, this specific set of conditions can lead to an error:\n\n- sharding is enabled\n- oss_sharding and implicit_shard_key are both deactivated\n- A shard_key_regex is _not_ defined\n\nIf this occurs, the database endpoint returns `(error) ERR key \"test\" does not match any rule`.\n\nTo address this issue, do one of the following:\n\n- Explicitly define shard_key_regex\n- Enable oss_sharding or implicit_shard_key (enabling both also works)"},{"id":"prerequisites-and-notes","title":"Prerequisites and notes","role":"content","text":"-  You can [upgrade to v6.2.18](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading) from Redis Enterprise Software v6.0 and later. \n\n- Refer to [v6.2.4 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-4-august-2021) for important notes regarding changes made to the upgrade policy and how those changes might impact your experience. \n\n- Upgrades from versions earlier than v6.0 are not supported.\n\n- If you plan to upgrade your cluster to RHEL 8, see the [v6.2.8 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-8-october-2021) for known limitations."},{"id":"deprecations","title":"Deprecations","role":"content","text":"#### Active-Active database persistence\n\nThe snapshot option for [data persistence on Active-Active databases](https://redis.io/docs/latest/operate/rs/databases/active-active/manage#data-persistence) will be deprecated in a future version of Redis Enterprise Software.  If you have an Active-Active database using snapshot persistence, we strongly encourage you to switch to AOF persistence.  Use `crdb-cli` to do so:\n[code example]\n\n#### TLS 1.0 and TLS 1.1 \n\nTLS 1.0 and TLS 1.1 connections are considered deprecated in favor of TLS 1.2 or later.  \n\nPlease verify that all clients, apps, and connections support TLS 1.2.  Support for the earlier protocols will be removed in a future release.\n\nCertain operating systems, such as RHEL 8, have already removed support for the earlier protocols.  Redis Enterprise Software cannot support connection protocols that are not supported by the underlying operating system.\n\n#### 3DES encryption cipher\n\nThe 3DES encryption cipher is considered deprecated in favor of stronger ciphers like AES.\n\nPlease verify that all clients, apps, and connections support the AES cipher. Support for 3DES will be removed in a future release.\n\nCertain operating systems, such as RHEL 8, have already removed support for 3DES. Redis Enterprise Software cannot support cipher suites that are not supported by the underlying operating system."},{"id":"product-lifecycle-updates","title":"Product lifecycle updates","role":"content","text":"Redis Enterprise Software v6.0.x reached end-of-life (EOL) on May 31, 2022.\n\nEOL of Redis Enterprise Software 6.2.x was reset to occur 18 months after the release of version 6.4, in accordance with the updated EOL policy.  The new EOL date is August 31, 2024.\n\nTo learn more, see the Redis Enterprise Software [product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle), which details the release number and the end-of-life schedule for Redis Enterprise Software.\n\nFor Redis modules information and lifecycle, see [Module lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules-lifecycle)."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS64002 - Fixes email alerts for LDAP mappings. \n- RS79519 - fixes a bug that prevented Administrators from editing an Active-Active database using the API with username instead of email as their identifier.\n- RS78039 - fixes a bug that caused the sync between two shards to pause when resetting a shard's data which can be when performing full sync or importing an RDB.\n- RS73454 - Updates internal timeouts to enable faster resharding.\n- RS75783 - Fixes failover due to false identification of dead nodes when master node goes down.\n- RS75206, RS52686 - Fixes backup_interval_offset in case where the user chose an offset that is higher than backup_interval; Fixes the UI from resetting backup_interval_offset after manual DB configuration.\n- RS75176 - Fixes rare case of stuck state machine during \"maintenance off\".\n- RS57200 - Add an IP address to \"failed_authentication_attempt\" errors.\n- RS56615 - Changed rladmin tune db db_name max_aof_load_time to receive the value in seconds; Added max_aof_load_time option to rladmin help tune.\n- RS54745 - Fixes the Rest API to reject BDB creation using negative integers as a uid.\n- RS46092 - Fixes rlcheck failure when somaxconn policy is a value other than 1024.\n- RS68965, RS80615 - Adds internode encryption ports 3340-3344 to rlcheck connectivity.\n- RS63302 - Adds umask validation for root user when installing.\n- RS46947 - Fixes removal of old installations in install.sh."},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"feature-limitations","title":"Feature limitations","role":"content","text":"- RS81463 A shard might crash when resharding an Active-Active database with Auto Tiering . Specifically, the shard will crash when volatile keys or Active-Active tombstone keys reside in Flash memory.\n\n- RS54131 Running the `QUIT` command on a TLS connected database closes connection and does not return a `+OK` reply"},{"id":"upgrade-limitations","title":"Upgrade limitations","role":"content","text":"Before you upgrade a cluster that hosts Active-Active databases with modules to v6.2.18, perform the following steps:\n\n1. Use `crdb-cli` to verify that the modules (`modules`) and their versions (in `module_list`) are as they appear in the database configuration and in the default database configuration:\n\n    [code example]\n\n1. From the admin console's **redis modules** tab, validate that these modules with their specific versions are loaded to the cluster.\n\n1. If one or more of the modules/versions are missing or if you need help, [contact Redis support](https://redis.com/company/support/) before taking additional steps."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Enterprise Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with open source Redis. The following [Open Source Redis](https://github.com/redis/redis) [CVEs](https://github.com/redis/redis/security/advisories) do not affect Redis Enterprise:\n\n- [CVE-2021-32625](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-32625) - Redis Enterprise is not impacted by the CVE that was found and fixed in open source Redis since Redis Enterprise does not implement LCS. Additional information about the open source Redis fix is on [the Redis GitHub page](https://github.com/redis/redis/releases) (Redis 6.2.4, Redis 6.0.14)\n\n- [CVE-2021-32672](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-32672) - Redis Enterprise is not impacted by the CVE that was found and fixed in open source Redis because the LUA debugger is unsupported in Redis Enterprise. Additional information about the open source Redis fix is on [the Redis GitHub page](https://github.com/redis/redis/releases) (Redis 6.2.6, Redis 6.0.16)\n\n- [CVE-2021-32675](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-32675) - Redis Enterprise is not impacted by the CVE that was found and fixed in open source Redis because the proxy in Redis Enterprise does not forward unauthenticated requests. Additional information about the open source Redis fix is on [the Redis GitHub page](https://github.com/redis/redis/releases) (Redis 6.2.6, Redis 6.0.16)\n\n- [CVE-2021-32762](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-32762) - Redis Enterprise is not impacted by the CVE that was found and fixed in open source Redis because the memory allocator used in Redis Enterprise is not vulnerable. Additional information about the open source Redis fix is on [the Redis GitHub page](https://github.com/redis/redis/releases) (Redis 6.2.6, Redis 6.0.16)\n\n- [CVE-2021-41099](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41099) - Redis Enterprise is not impacted by the CVE that was found and fixed in open source Redis because the proto-max-bulk-len CONFIG is blocked in Redis Enterprise. Additional information about the open source Redis fix is on [the Redis GitHub page](https://github.com/redis/redis/releases) (Redis 6.2.6, Redis 6.0.16) security fixes for [recent CVEs](https://github.com/redis/redis/security/advisories). Redis Enterprise has already included the fixes for the relevant CVEs. Some CVEs announced for Open Source Redis do not affect Redis Enterprise due to different and additional functionality available in Redis Enterprise that is not available in Open Source Redis."}],"examples":[{"id":"deprecations-ex0","language":"text","code":"crdb-cli crdb update --crdb-guid <CRDB_GUID> --default-db-config '{\"data_persistence\": \"aof\", \"aof_policy\":\"appendfsync-every-sec\"}'","section_id":"deprecations"},{"id":"upgrade-limitations-ex0","language":"sh","code":"crdb-cli crdb get --crdb-guid <crdb-guid>","section_id":"upgrade-limitations"}]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-6-2-18-releases/rs-6-2-18-49","title":"Redis Enterprise Software release notes 6.2.18-49 (October 2022)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-18-releases/rs-6-2-18-49/","summary":"New REST API endpoint to check whether a node is a primary or a replica. Added metrics to track certificate expiration time.","tags":["docs","operate","rs"],"last_updated":"2026-10-06T08:53:14-07:00","page_type":"content","content_hash":"a95b7b90751e8483a1a5b1f3a40f701fefc36ee9cf9e6a2f6529f01d6dbcd211","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Enterprise Software version 6.2.18](https://redis.io/downloads/#software).\n\nThe following table shows the MD5 checksums for the available packages:\n\n| Package | MD5 checksum (6.2.18-49 October release) |\n|:--------|:-------------|\n| Ubuntu 16 | `89029a28a2b0e3ad379559b94d00ae32` |\n| Ubuntu 18 | `830e8704c0f6902a04df9ff53cc5e41f` |\n| RedHat Enterprise Linux (RHEL) 7<br/>Oracle Enterprise Linux (OL) 7 | `766d2f8448fc28d0ea67cac20387a9e3` |\n| RedHat Enterprise Linux (RHEL) 8<br/>Oracle Enterprise Linux (OL) 8 | `7e31fb3e3416b404513dad508669f836` |"},{"id":"new-features-and-enhancements","title":"New features and enhancements","role":"content","text":"- The [`/nodes/status`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/nodes/status) REST API endpoint indicates whether a node is the primary.  (RS82566)\n- Added metrics to track certificates expiration time (RS56040)\n\n#### Redis modules \n\nRedis Enterprise Software v6.2.18-49 (October release) includes the following Redis modules:\n\n- [RediSearch v2.4.11](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.4-release-notes#v2411-july-2022)\n- [RedisJSON v2.2.0](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.2-release-notes#v220-july-2022)\n- [RedisBloom v2.2.18](/operate/oss_and_stack/stack-with-enterprise/bloom/release-notes/redisbloom-2.2-release-notes/#v2218-july-2022)\n- [RedisGraph v2.8.17](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.8-release-notes#v2817-july-2022)\n- [RedisTimeSeries v1.6.17](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.6-release-notes#v1617-july-2022)\n\nSee [Upgrade modules](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/upgrade-module) to learn how to upgrade a module for a database."},{"id":"known-upgrade-limitations","title":"Known upgrade limitations","role":"content","text":"Before you upgrade a cluster that hosts Active-Active databases with modules to v6.2.18, perform the following steps:\n\n1. Use `crdb-cli` to verify that the modules (`modules`) and their versions (in `module_list`) are as they appear in the database configuration and in the default database configuration:\n\n    [code example]\n\n1. From the admin console's **redis modules** tab, validate that these modules with their specific versions are loaded to the cluster.\n\n1. If one or more of the modules/versions are missing or if you need help, [contact Redis support](https://redis.com/company/support/) before taking additional steps."}],"examples":[{"id":"known-upgrade-limitations-ex0","language":"sh","code":"crdb-cli crdb get --crdb-guid <crdb-guid>","section_id":"known-upgrade-limitations"}]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-6-2-18-releases/rs-6-2-18-58","title":"Redis Enterprise Software release notes 6.2.18-58 (November 2022)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-18-releases/rs-6-2-18-58/","summary":"UI support for custom REST API port. Added info level for troubleshooting redis_mgr.","tags":["docs","operate","rs"],"last_updated":"2026-10-06T08:53:14-07:00","page_type":"content","content_hash":"6cfaa6d2884e0024c614f3e88c05942657e47782acfe911d961f7b0da0ab15c3","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Enterprise Software version 6.2.18](https://redis.io/downloads/#software).\n\nThe following table shows the MD5 checksums for the available packages:\n\n| Package | MD5 checksum (6.2.18-58 November release) |\n|:--------|:-------------|\n| Ubuntu 16 | `38d974c47004df0808b71c2bd5a96996` |\n| Ubuntu 18 | `6a5ca7a287f1808a50d13312162aac4b` |\n| RedHat Enterprise Linux (RHEL) 7<br/>Oracle Enterprise Linux (OL) 7 | `d8283a3475a70823641249e3cfe3ab6e` |\n| RedHat Enterprise Linux (RHEL) 8<br/>Oracle Enterprise Linux (OL) 8 | `040e45c866fde391b35ae85d648a4afa` |"},{"id":"new-features-and-enhancements","title":"New features and enhancements","role":"content","text":"- Added support to the UI to work with custom REST API port (RS84428)\n- Added info level print to redis_mgr for troubleshooting (RS85385)\n\n#### Redis modules \n\nRedis Enterprise Software v6.2.18-58 (November release) includes newer versions of the following Redis modules:\n\n- [RediSearch v2.4.14](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.4-release-notes#v2414-august-2022)\n- [RedisGraph v2.8.19](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.8-release-notes#v2819-august-2022)\n\nSee [Upgrade modules](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/upgrade-module) to learn how to upgrade a module for a database."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS85369 - Fixes umask validation during installation to allow for temporary umask change\n- RS77339 - Fixes the \"FROM\" email addresses of email alerts\n- RS86005 - Fixes  `bdbs/<int:uid>/actions/recover` API to return the bdb status and not the `recovery_plan`"},{"id":"known-upgrade-limitations","title":"Known upgrade limitations","role":"content","text":"Before you upgrade a cluster that hosts Active-Active databases with modules to v6.2.18, perform the following steps:\n\n1. Use `crdb-cli` to verify that the modules (`modules`) and their versions (in `module_list`) are as they appear in the database configuration and in the default database configuration:\n\n    [code example]\n\n1. From the admin console's **redis modules** tab, validate that these modules with their specific versions are loaded to the cluster.\n\n1. If one or more of the modules/versions are missing or if you need help, [contact Redis support](https://redis.com/company/support/) before taking additional steps."}],"examples":[{"id":"known-upgrade-limitations-ex0","language":"sh","code":"crdb-cli crdb get --crdb-guid <crdb-guid>","section_id":"known-upgrade-limitations"}]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-6-2-18-releases/rs-6-2-18-65","title":"Redis Enterprise Software release notes 6.2.18-65 (December 2022)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-18-releases/rs-6-2-18-65/","summary":"Added To field to test email server alerts. New flag to skip cluster resource validation during imports. Improved error messages.","tags":["docs","operate","rs"],"last_updated":"2026-10-06T08:53:14-07:00","page_type":"content","content_hash":"78f61cdfaaeb58a556bf383a5b2b56a6c9dd8cbf1e689f412ca4fd34536f1b31","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Enterprise Software version 6.2.18](https://redis.io/downloads/#software).\n\nThe following table shows the MD5 checksums for the available packages:\n\n| Package | MD5 checksum (6.2.18-65 December release) |\n|:--------|:-------------|\n| Ubuntu 16 | `515c7bbc97eaf1939757c8894eda9523` |\n| Ubuntu 18 | `830e8704c0f6902a04df9ff53cc5e41f` |\n| RedHat Enterprise Linux (RHEL) 7<br/>Oracle Enterprise Linux (OL) 7 | `b51d13e30f4a7e1a134982342b8865b0` |\n| RedHat Enterprise Linux (RHEL) 8<br/>Oracle Enterprise Linux (OL) 8 | `762f907c1f0e6e0e88e4ae5bf967e143` |"},{"id":"new-features-and-enhancements","title":"New features and enhancements","role":"content","text":"- When setting up an alert email server in the UI: added a `To` field to the `test email server` function (RS86119)\n- Added a new flag to skip cluster resource validation when performing import (RS88086)\n- Enhanced error message information for connection handling \n- Added logging details to `insufficient_resources` planner error \n\n#### Redis modules \n\nRedis Enterprise Software v6.2.18-65 (December release) includes newer versions of the following Redis modules:\n\n- [RediSearch v2.4.16](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.4-release-notes#v2416-november-2022)\n- [RedisGraph v2.8.20](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.8-release-notes#v2820-september-2022)\n\nSee [Upgrade modules](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/upgrade-module) to learn how to upgrade a module for a database."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS87191 - Fixes syncer stop due to cross slot violation error in Auto Tiering"},{"id":"known-upgrade-limitations","title":"Known upgrade limitations","role":"content","text":"Before you upgrade a cluster that hosts Active-Active databases with modules to v6.2.18, perform the following steps:\n\n1. Use `crdb-cli` to verify that the modules (`modules`) and their versions (in `module_list`) are as they appear in the database configuration and in the default database configuration:\n\n    [code example]\n\n1. From the admin console's **redis modules** tab, validate that these modules with their specific versions are loaded to the cluster.\n\n1. If one or more of the modules/versions are missing or if you need help, [contact Redis support](https://redis.com/company/support/) before taking additional steps."}],"examples":[{"id":"known-upgrade-limitations-ex0","language":"sh","code":"crdb-cli crdb get --crdb-guid <crdb-guid>","section_id":"known-upgrade-limitations"}]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-6-2-18-releases/rs-6-2-18-70","title":"Redis Enterprise Software release notes 6.2.18-70 (January 2023)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-18-releases/rs-6-2-18-70/","summary":"New script to generate new self-signed certificates for renewal. Improved logs. Deactivate alert_mgr using rladmin.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"0ea9a08f476251af4e84dfb81fef29dac08fe0210d2d4dec1f520550d73768d5","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Enterprise Software version 6.2.18](https://redis.io/downloads/#software).\n\nThe following table shows the MD5 checksums for the available packages:\n\n| Package | MD5 checksum (6.2.18-70 January release) |\n|:--------|:-------------|\n| Ubuntu 16 | `69d2d2c71232adb15cebf29308ac54da` |\n| Ubuntu 18 | `22e0637107a32ccb96a704abe9650adf` |\n| RedHat Enterprise Linux (RHEL) 7<br/>Oracle Enterprise Linux (OL) 7 | `e14fcf6973418602f2b64a55a0bc8374` |\n| RedHat Enterprise Linux (RHEL) 8<br/>Oracle Enterprise Linux (OL) 8 <br/>Rocky Enterprise Linux | `f61d0d8f0bb5ad90a470482d6575eb27` |"},{"id":"new-features-and-enhancements","title":"New features and enhancements","role":"content","text":"- Added a new script to generate new self-signed certificates for renewal. For details, see [Certificates](https://redis.io/docs/latest/operate/rs/security/certificates)\n- Added `rpm -q` command output to the logs (RS82673)\n- Added logs for server operations: `start`, `stop`, `restart` (RS87775)\n- Added `alert_mgr` to the [services you can deactivate with `rladmin`](https://redis.io/docs/latest/operate/rs/clusters/optimize/turn-off-services) (RS89572)"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS61062 - Remove leading colon added to `PATH` variable"},{"id":"known-upgrade-limitations","title":"Known upgrade limitations","role":"content","text":"Before you upgrade a cluster that hosts Active-Active databases with modules to v6.2.18, perform the following steps:\n\n1. Use `crdb-cli` to verify that the modules (`modules`) and their versions (in `module_list`) are as they appear in the database configuration and in the default database configuration:\n\n    [code example]\n\n1. From the admin console's **redis modules** tab, validate that these modules with their specific versions are loaded to the cluster.\n\n1. If one or more of the modules/versions are missing or if you need help, [contact Redis support](https://redis.com/company/support/) before taking additional steps."}],"examples":[{"id":"known-upgrade-limitations-ex0","language":"sh","code":"crdb-cli crdb get --crdb-guid <crdb-guid>","section_id":"known-upgrade-limitations"}]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-6-2-4-august-2021","title":"Redis Enterprise Software Release Notes 6.2.4 (August 2021)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-4-august-2021/","summary":"Internode encryption. Nginx replaced by envoy. New upgrade policies/behavior.","tags":["docs","operate","rs"],"last_updated":"2026-10-06T08:53:14-07:00","page_type":"content","content_hash":"d483495bf4c5d78e8ccf27cc41f73947a2dd3be3a88e1c0e4a9008402ba3493c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Redis Enterprise Software version 6.2.4](https://redislabs.com/redis-enterprise-software/download-center/software/) is now available! \n\nThis version offers:\n\n- Encryption of all communications within cluster nodes \n- Security enhancements\n- Bug fixes\n- Compatibility with the latest version of open source Redis 6.2.3"},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"prerequisites-and-notes","title":"Prerequisites and notes","role":"content","text":"You can [upgrade to v6.2.4](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading) from Redis Enterprise Software v6.0 and later. \n\nKeep the following in mind:\n\n- Upgrades from versions earlier than v6.0 are not supported\n\n- The new internode encryption feature requires port 3342 to be open on all machines in the cluster.\n\n- [In v6.0.20](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-0-20-april-2021), Redis Enterprise Software replaced Nginx with envoy to improve internal security and communication.  As of v6.2.4, Nginx is no longer provided with Redis Enterprise Software."},{"id":"database-upgrade-default-changes","title":"Database upgrade default changes","role":"content","text":"The default behavior of the `upgrade db` command has changed.  It is now controlled by a new cluster policy (`redis_upgrade_policy`), which defines the policy for creating new databases and upgrading existing databases.  The policy supports the following values:\n\n- When set to `major`, the policy allows databases to be created or updated to versions of Redis compatible with open source Redis major releases.  This allows for longer upgrade cycles by supporting Redis versions across multiple Redis Enterprise Software releases.  \n\n    This is the default value for Redis Enterprise Software.\n\n- When set to `latest`, the policy creates new databases and upgrades existing ones to be compatible with the latest (most recent) version of open source Redis, which was the default behavior of earlier versions of Redis Enterprise Software.  This is no longer the default behavior.\n\n    Setting the upgrade policy to `latest` ensures that the most recent Redis features are available to new databases and ones that are upgraded.  It also requires more frequent upgrades, as open source Redis is updated more frequently than Redis Enterprise Software.\n\nThe Redis Enterprise Software 6.2.4 package includes compatibility with the most recent major Redis release (v6.0) and the latest (most recent) update to Redis (v6.2.3).  \n\nBy default, compatibility with v6.0 will be installed.  To change this, use `rladmin` to set the upgrade policy and the default Redis version:\n\n[code example]\n\nTo learn more, see the [upgrade instructions](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading)."},{"id":"product-lifecycle-updates","title":"Product lifecycle updates","role":"content","text":"Redis Enterprise Software v5.6.0 will reach end of life (EOF) on October 31, 2021.\n\nTo learn more, see the Redis Enterprise Software [product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle), which details the release number and the end-of-life schedule for Redis Enterprise Software.\n\nRedis Enterprise modules have individual release numbers [and lifecycles](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules-lifecycle)."},{"id":"deprecation-notices","title":"Deprecation notices","role":"content","text":"- [In v6.0.20](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-0-20-april-2021), the SASL-based LDAP mechanism was deprecated in favor of a new [RBAC-based approach](https://redis.io/docs/latest/operate/rs/security/access-control/ldap).  As of v6.2.12, support for the older mechanism has been removed.\n\n    For help migrating to the LDAP-based mechanism, see [Migrate to role-based LDAP](https://redis.io/docs/latest/operate/rs/security/access-control/ldap/migrate-to-role-based-ldap).\n    \n- [OpenStack Object Storage](https://wiki.openstack.org/wiki/Swift) (\"Swift\") has reached end-of-life.  Consequently, you can no longer use ObjectStack Swift as a target for database backup or export operations."},{"id":"features-and-enhancements","title":"Features and enhancements","role":"content","text":""},{"id":"internode-encryption","title":"Internode encryption","role":"content","text":"Internode encryption (INE) encrypts all communication between nodes in a cluster; it is available for the control plane and the data plane. \n\n#### Control plane internode encryption\n\nControl plane internode encryption encrypts all management communication within a cluster.  It is enabled by default for all new clusters and upgraded clusters.\n\n#### Data plane internode encryption\n\nData plane internode encryption encrypts communication between nodes within a cluster, such as database replication between nodes.  \n\nData plane internode encryption is available for new or fully upgraded clusters.  It is not enabled by default.  \n\nYou can enable data plane internode encryption by:\n\n- Setting the cluster policy to enable data plane internode encryption by default for new databases\n    \n    [code example]\n         \n- Enabling it for individual existing databases\n\n    [code example]"},{"id":"internal-certificate-management","title":"Internal certificate management","role":"content","text":"Internode encryption relies on internal certificates signed by a unique, private CA certificate created for your deployment.  The private CA generates and signs leaf certificates for internode encryption only.  It's generated when you install or upgrade to Redis Enterprise 6.2.4.  It's used only within the cluster and is not exposed outside of the cluster.  \n\nThe leaf certificates expire regularly; they're automatically rotated before expiration and alerts are issued as needed."},{"id":"open-source-redis-compatibility","title":"Open source Redis compatibility","role":"content","text":"[Redis 6.2](https://raw.githubusercontent.com/redis/redis/6.2/00-RELEASENOTES) introduced new commands, feature improvements, and security fixes; it addresses many customer requests.\n\nRedis Enterprise Software supports all new commands, except [RESET](https://redis.io/docs/latest/commands/reset) and [FAILOVER](https://redis.io/docs/latest/commands/failover).  (Redis Enterprise takes a different approach to connectivity; it also separates control plane operations from data plane operations.)\n\nTo learn more, see Redis Enterprise Software [compatibility with open source](https://redis.io/docs/latest/operate/rs/references/compatibility)."},{"id":"redis-modules","title":"Redis modules","role":"content","text":"Redis Enterprise Software v6.2.4 includes the following Redis modules:\n\n- [RediSearch v2.0.11](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.0-release-notes)\n- [RedisJSON v1.0.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-1.0-release-notes)\n- [RedisBloom v2.2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.2-release-notes) \n- [RedisGraph v2.4.7](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.4-release-notes)\n- [RedisTimeSeries v1.4.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.4-release-notes)"},{"id":"internode-encryption-for-modules","title":"Internode encryption for modules","role":"content","text":"To utilize data plane encryption for existing databases with modules, update the module to the latest version prior to enabling data plane encryption. \n\nFor help, see [Upgrade the module for a database](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/upgrade-module)."},{"id":"module-related-enhancements","title":"Module-related enhancements","role":"content","text":"Added the capability to update current module arguments for an existing database. \nIn earlier versions, you could do this only when upgrading a module.\nTo learn more, see [`rladmin upgrade`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/upgrade)."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS39954 - Changed the UI status indication for the \n[default user](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/upgrade) from `Active/Inactive` to `Enabled/Disabled` \n\n- RS42626 - Increased the max length for modules commands from 23 characters to 64 characters\n\n- RS54732 - Fixed incorrect reporting of number database connections, which caused the number of connections to be reported as a 20 digit number\n\n- RS52265 - Fixed excessive log lines reporting when an Active-Active database is on featureset `0`. [Upgrade the featureset](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-active-active) version to the latest.\n\n- RS56122 - Fixed a bug that was causing AOF files to grow when the replicas of two Active-Active databases became disconnected during full synchronization\n\n- RS58184 - Fixed a bug when trying to create an Active-Active database with expired syncer certificates; participating clusters were creating replicas even though the create operation failed.\n\n- RS48988 - Add the username description in the log upon an unauthorized REST API request"},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"installation-limitations","title":"Installation limitations","role":"setup","text":"Several Redis Enterprise Software installation reference files are installed to the directory `/etc/opt/redislabs/` even if you use [custom installation directories](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/customize-install-directories).\n\nAs a workaround to install Redis Enterprise Software without using any root directories, do the following before installing Redis Enterprise Software:\n\n1. Create all custom, non-root directories you want to use with Redis Enterprise Software.\n\n1. Mount `/etc/opt/redislabs` to one of the custom, non-root directories."},{"id":"known-issues","title":"Known issues","role":"content","text":"- A new command was added as part of Redis 6.2: [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim). When used in an Active-Active configuration, this command may cause Redis shards to crash, potentially resulting in data loss. The issue is fixed in Redis Enterprise Software version 6.2.12. Additionally, we recommend enabling AOF persistence for all Active-Active configurations.\n\n- The `ZRANGESTORE` command, with a special `zset-max-ziplist-entries` configuration can crash Redis 6.2. See [Redis repository 10767](https://github.com/redis/redis/pull/10767) for more details.\n\n- RS81463 - A shard may crash when resharding an Active-Active database with Auto Tiering . Specifically, the shard will crash when volatile keys or Active-Active tombstone keys reside in Flash memory.\n\n- RS40641 - API requests are redirected to an internal IP in case the request arrives from a node which is not the master. To avoid this issue, use [`rladmin cluster config`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/config) to configure `handle_redirects` or `handle_metrics_redirects`."},{"id":"security","title":"Security","role":"security","text":"-   The following [Open Source Redis](https://github.com/redis/redis) [CVE's](https://github.com/redis/redis/security/advisories) do not affect Redis Enterprise:\n\n    -   [CVE-2021-32625](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-32625) - Redis Enterprise is not impacted by the CVE that was found and fixed in open source Redis since Redis Enterprise does not implement `LCS`. Additional information about the open source Redis fix is on [the Redis GitHub page](https://github.com/redis/redis/releases) (Redis 6.2.4, Redis 6.0.14)\n\n    -   [CVE-2021-32672](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-32672) - Redis Enterprise is not impacted by the CVE that was found and fixed in open source Redis because the LUA debugger is unsupported in Redis Enterprise. Additional information about the open source Redis fix is on [the Redis GitHub page](https://github.com/redis/redis/releases) (Redis 6.2.6, Redis 6.0.16)\n\n    -   [CVE-2021-32675](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-32675) - Redis Enterprise is not impacted by the CVE that was found and fixed in open source Redis because the proxy in Redis Enterprise does not forward unauthenticated requests. Additional information about the open source Redis fix is on [the Redis GitHub page](https://github.com/redis/redis/releases) (Redis 6.2.6, Redis 6.0.16)\n\n    -   [CVE-2021-32762](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-32762) - Redis Enterprise is not impacted by the CVE that was found and fixed in open source Redis because the memory allocator used in Redis Enterprise is not vulnerable. Additional information about the open source Redis fix is on [the Redis GitHub page](https://github.com/redis/redis/releases) (Redis 6.2.6, Redis 6.0.16)\n\n    -   [CVE-2021-41099](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41099) - Redis Enterprise is not impacted by the CVE that was found and fixed in open source Redis because the proto-max-bulk-len CONFIG is blocked in Redis Enterprise. Additional information about the open source Redis fix is on [the Redis GitHub page](https://github.com/redis/redis/releases) (Redis 6.2.6, Redis 6.0.16)"}],"examples":[{"id":"database-upgrade-default-changes-ex0","language":" shell","code":"$ rladmin tune cluster redis_upgrade_policy latest\n$ rladmin tune cluster default_redis_version 6.2","section_id":"database-upgrade-default-changes"},{"id":"internode-encryption-ex0","language":" shell","code":"rladmin tune cluster data_internode_encryption enabled","section_id":"internode-encryption"},{"id":"internode-encryption-ex1","language":" shell","code":"rladmin tune db <db:id | name> data_internode_encryption enabled","section_id":"internode-encryption"}]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-6-2-8-october-2021","title":"Redis Enterprise Software Release Notes 6.2.8 (October 2021)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-8-october-2021/","summary":"RHEL 8 support. Set backup start time.","tags":["docs","operate","rs"],"last_updated":"2026-10-06T08:53:14-07:00","page_type":"content","content_hash":"14f0fa3f1c1a53a7c02abe3d135f5b908ebcbee4f11b8507b6b426e82c2582df","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Redis Enterprise Software version 6.2.8](https://redislabs.com/redis-enterprise-software/download-center/software/) is now available!"},{"id":"features-and-enhancements","title":"Features and enhancements","role":"content","text":"This version features:\n\n- Support for Red Hat Linux Edition (RHEL) 8\n- You can now set the start time for [12- and 24-hour backups](https://redis.io/docs/latest/operate/rs/databases/import-export/schedule-backups) \n- Compatibility with version of [open source Redis 6.2.3](https://github.com/redis/redis/releases/tag/6.2.3) (starting with [Redis Enterprise Software v6.2.4](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-4-august-2021))\n- Compatibility with the security fixes of the latest [open source Redis 6.2.6](https://github.com/redis/redis/releases/tag/6.2.6)\n- Enhancements and bug fixes"},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"prerequisites-and-notes","title":"Prerequisites and notes","role":"content","text":"- You can [upgrade to v6.2.8](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading) from Redis Enterprise Software v6.0 and later. \n\n- Refer to the [v6.2.4 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-4-august-2021) for important notes regarding the upgrade process.\n\n- When upgrading a cluster from Redis Enterprise 6.0.8 and earlier to 6.2.8 only, the DMC proxy might crash when proxy certificates contain additional text as comments.  Redis removes these comments during upgrade, but a change to the v6.2.8 internal upgrade action sequence might cause this problem. \n\n\n    If you plan to upgrade from a pre-6.0.8 release to 6.2.8, check whether your proxy certificate includes additional comments and manually remove them.  The change was reverted in 6.2.10.\n\n- Upgrades from versions earlier than v6.0 are not supported."},{"id":"product-lifecycle-updates","title":"Product lifecycle updates","role":"content","text":"As of 31 October 2021, Redis Enterprise Software v5.6.0 is end of life (EOF).\n\nTo learn more, see the Redis Enterprise Software [product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle), which details the release number and the end-of-life schedule for Redis Enterprise Software.\n\nRedis Enterprise modules have individual release numbers [and lifecycles](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules-lifecycle)."},{"id":"redis-modules","title":"Redis modules","role":"content","text":"Redis Enterprise Software v6.2.8 includes the following Redis modules:\n\n- [RediSearch v2.0.11](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.0-release-notes)\n- [RedisJSON v1.0.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-1.0-release-notes)\n- [RedisBloom v2.2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.2-release-notes) \n- [RedisGraph v2.4.7](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.4-release-notes)\n- [RedisTimeSeries v1.4.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.4-release-notes)\n\nTo learn more, see [Upgrade the module for a database](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/upgrade-module)."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":""},{"id":"user-interface-fixes","title":"User interface fixes","role":"content","text":"- RS58804 - Display an error message in case of a login attempt with an LDAP user\n- RS56680 - Notify that SASLAUTHD should be disabled prior to enabling LDAP \n- RS55844 - Use the correct password and mask it on LDAP password update\n- RS60877 - Fixed reset of Active-Active database compression level, in cases where the compression level wasn’t set to default, when changing any other configuration via the DB configuration page\n- RS43999 - Fixed UI database configuration to allow changes when SFTP SSH key is customized\n- RS59861 - Fixed the UI to display an explanation error message when password complexity does not meet requirements\n- RS57734 - Fixed inaccessible UI after cluster upgrade due to missing certificate\n- RS43041 - Mask secret keys for backup destination for view and edit in the UI"},{"id":"additional-fixes","title":"Additional fixes","role":"content","text":"- RS60068 / RS59146 - Fixed unresolved endpoint due to PDNS issues\n- RS52812 - Expand API wrapper to return API 405 errors as JSON/XML\n- RS57666 - Fixed false shard migration message when the shard fails to bind the port\n- RS57444, RS55294, RS4903 - Fixed false \"backup finished successfully\" message when the backup failed due to restricted access to the backup destination"},{"id":"fixes-on-build-53","title":"Fixes on build #53","role":"content","text":"- RS67829 - Fixed a bug that caused Modules' auxiliary field not to get replicated between the primary and the replica shards. Applicable for RediSearch, RedisGraph and RedisGears and happening only at following scenarios:\n         - (A) On the destination databases of a Replica Of upon a full sync operation\n         - (B) Upon import operation"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"-RS81463 - A shard may crash when resharding an Active-Active database with Auto Tiering . Specifically, the shard will crash when volatile keys or Active-Active tombstone keys reside in Flash memory.\n\n- RS63258 - Redis Enterprise Software 6.2.8 is not supported on RHEL 8 with FIPS enabled.\n\n    FIPS changes system-generated keys, which can limit secure access to the cluster or the admin console via port 8443.\n\n- RS63375 - RHEL 7 clusters cannot be directly upgraded to RHEL 8 when hosting databases using modules.\n\n    Due to binary differences in modules between the two operating systems, you cannot directly update RHEL 7 clusters to RHEL 8 when those clusters host databases using modules.  Instead, you need to create a new cluster on RHEL 8 and then migrate existing data from your RHEL 7 cluster. This does not apply to clusters that do not use modules.\n\nAll [known limitations](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-4-august-2021#known-limitations) from v6.2.4 have been fixed."},{"id":"installation-limitations","title":"Installation limitations","role":"setup","text":"Several Redis Enterprise Software installation reference files are installed to the directory `/etc/opt/redislabs/` even if you use [custom installation directories](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/customize-install-directories).\n\nAs a workaround to install Redis Enterprise Software without using any root directories, do the following before installing Redis Enterprise Software:\n\n1. Create all custom, non-root directories you want to use with Redis Enterprise Software.\n\n1. Mount `/etc/opt/redislabs` to one of the custom, non-root directories."},{"id":"known-issues","title":"Known issues","role":"content","text":"- A new command was added as part of Redis 6.2: [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim). When used in an Active-Active configuration, this command may cause Redis shards to crash, potentially resulting in data loss. The issue is fixed in Redis Enterprise Software version 6.2.12. Additionally, we recommend enabling AOF persistence for all Active-Active configurations.\n\n- The `ZRANGESTORE` command, with a special `zset-max-ziplist-entries` configuration can crash Redis 6.2. See [Redis repository 10767](https://github.com/redis/redis/pull/10767) for more details.\n\n- RS40641 - API requests are redirected to an internal IP in case the request arrives from a node which is not the master. To avoid this issue, use [`rladmin cluster config`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/config) to configure `handle_redirects` or `handle_metrics_redirects`."},{"id":"security","title":"Security","role":"security","text":""},{"id":"open-source-redis-security-fix-compatibility","title":"Open source Redis security fix compatibility","role":"content","text":"As part of its commitment to security, Redis Enterprise Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with open source Redis.\n\nThe following [open source Redis](https://github.com/redis/redis) [CVEs](https://github.com/redis/redis/security/advisories) do not affect Redis Enterprise:\n\n- [CVE-2021-32625](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-32625) - Redis Enterprise is not impacted by the CVE that was found and fixed in open source Redis since Redis Enterprise does not implement `LCS`. Additional information about the open source Redis fix is on [the Redis GitHub page](https://github.com/redis/redis/releases) (Redis 6.2.4, Redis 6.0.14)\n\n- [CVE-2021-32672](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-32672) - Redis Enterprise is not impacted by the CVE that was found and fixed in open source Redis because the LUA debugger is unsupported in Redis Enterprise. Additional information about the open source Redis fix is on [the Redis GitHub page](https://github.com/redis/redis/releases) (Redis 6.2.6, Redis 6.0.16)\n\n- [CVE-2021-32675](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-32675) - Redis Enterprise is not impacted by the CVE that was found and fixed in open source Redis because the proxy in Redis Enterprise does not forward unauthenticated requests. Additional information about the open source Redis fix is on [the Redis GitHub page](https://github.com/redis/redis/releases) (Redis 6.2.6, Redis 6.0.16)\n\n- [CVE-2021-32762](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-32762) - Redis Enterprise is not impacted by the CVE that was found and fixed in open source Redis because the memory allocator used in Redis Enterprise is not vulnerable. Additional information about the open source Redis fix is on [the Redis GitHub page](https://github.com/redis/redis/releases) (Redis 6.2.6, Redis 6.0.16)\n\n- [CVE-2021-41099](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41099) - Redis Enterprise is not impacted by the CVE that was found and fixed in open source Redis because the proto-max-bulk-len CONFIG is blocked in Redis Enterprise. Additional information about the open source Redis fix is on [the Redis GitHub page](https://github.com/redis/redis/releases) (Redis 6.2.6, Redis 6.0.16)\n\nSome CVEs announced for Open Source Redis do not affect Redis Enterprise due to functionality that is either different from (or not available in) open source Redis."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-6-4-2-releases","title":"Redis Enterprise Software release notes 6.4.2","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/","summary":"Pub/sub ACLs & default permissions. Validate client certificates by subject attributes. Ubuntu 20.04 support.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","children":[{"id":"operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-131","summary":"Internal fixes and improvements.","title":"Redis Enterprise Software release notes 6.4.2-131 (September 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-131/"},{"id":"operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-127","summary":"Internal fixes and improvements.","title":"Redis Enterprise Software release notes 6.4.2-127 (June 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-127/"},{"id":"operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-121","summary":"Internal fixes and improvements.","title":"Redis Enterprise Software release notes 6.4.2-121 (March 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-121/"},{"id":"operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-115","summary":"RediSearch v2.6.23. RedisBloom v2.4.12. RedisTimeSeries v1.8.15.","title":"Redis Enterprise Software release notes 6.4.2-115 (Oct 2024)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-115/"},{"id":"operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-110","summary":"RedisGraph v2.10.15. RedisBloom v2.4.8. Bug fixes.","title":"Redis Enterprise Software release notes 6.4.2-110 (May 2024)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-110/"},{"id":"operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-103","summary":"RHEL 8.8 support. RediSearch v2.6.12. RedisGraph v2.10.12. RedisTimeSeries v1.8.11 Log when CCS schema changes. Bug fixes.","title":"Redis Enterprise Software release notes 6.4.2-103 (October 2023)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-103/"},{"id":"operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-94","summary":"Look-ahead mechanism for planner attempts. Package OS compatibility validation.","title":"Redis Enterprise Software release notes 6.4.2-94 (July 2023)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-94/"},{"id":"operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-81","summary":"Email alerts for database backup failures and replica high availability shard relocation failures.","title":"Redis Enterprise Software release notes 6.4.2-81 (June 2023)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-81/"},{"id":"operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-69","summary":"Amazon Linux 2 support. Configure envoy ports using rladmin. Added option to avoid specific nodes when using the optimized shards placement API. Added failure_detection_sensitivity to replace watchdog_profile.","title":"Redis Enterprise Software release notes 6.4.2-69 (May 2023)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-69/"},{"id":"operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-61","summary":"Amazon Linux 2 support. Fixed known limitations for custom installation on RHEL 7 and RHEL 8, running rl_rdbconvert manually, and resharding rack-aware databases with no replication.","title":"Redis Enterprise Software release notes 6.4.2-61 (April 2023)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-61/"},{"id":"operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-43","summary":"Ubuntu 20.04 support. Safe node removal. Allow gossip_envoy port configuration.","title":"Redis Enterprise Software release notes 6.4.2-43 (March 2023)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-43/"},{"id":"operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-30","summary":"Pub/sub ACLs & default permissions. Validate client certificates by subject attributes.","title":"Redis Enterprise Software release notes 6.4.2-30 (February 2023)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-30/"}],"page_type":"content","content_hash":"ebace792e3e64486615e41ea97802e9bd9c688b1e112aeda2826e1f68b7a547a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!WARNING]\n> Redis Software 6.4 reached end of life on August 31, 2025. It no longer\n> receives security patches, bug fixes, or maintenance releases.\n> Support for 6.4-specific issues is limited per your subscription agreement,\n> and you may be asked to upgrade before an issue can be investigated.\n>\n> Upgrade to a supported version. Reaching the latest version may require an\n> intermediate upgrade first — see the\n> [supported upgrade paths](https://redis.io/docs/latest/operate/rs/references/upgrade-paths)\n> and the [Redis Software product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule).\n\n​[​Redis Enterprise Software version 6.4.2](https://redis.io/downloads/#software) is now available!\n\nThis version offers:\n\n- Extended validation of client certificates via mTLS (mutual TLS) full subject support\n\n- Support for default restrictive permissions when using publish/subscribe commands and access control lists (ACLs)\n\n- Enhanced TLS performance when Redis returns large arrays in responses\n\n- Compatibility with [open source Redis](https://github.com/redis/redis) 6.2.7 and 6.2.10.\n\n- Additional enhancements and bug fixes"},{"id":"detailed-release-notes","title":"Detailed release notes","role":"content","text":"For more detailed release notes, select a build version from the following table:\n\n\n| Version (Release date) | Major changes | OSS Redis compatibility |\n| --- | --- | --- |\n| [6.4.2-131 (September 2025)](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-131/) | Internal fixes and improvements. | Redis 6.2.10, 6.0.17 |\n| [6.4.2-127 (June 2025)](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-127/) | Internal fixes and improvements. | Redis 6.2.10, 6.0.17 |\n| [6.4.2-121 (March 2025)](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-121/) | Internal fixes and improvements. | Redis 6.2.10, 6.0.17 |\n| [6.4.2-115 (Oct 2024)](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-115/) | RediSearch v2.6.23. RedisBloom v2.4.12. RedisTimeSeries v1.8.15. | Redis 6.2.10, 6.0.17 |\n| [6.4.2-110 (May 2024)](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-110/) | RedisGraph v2.10.15. RedisBloom v2.4.8. Bug fixes. | Redis 6.2.10, 6.0.17 |\n| [6.4.2-103 (October 2023)](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-103/) | RHEL 8.8 support. RediSearch v2.6.12. RedisGraph v2.10.12. RedisTimeSeries v1.8.11 Log when CCS schema changes. Bug fixes. | Redis 6.2.10, 6.0.17 |\n| [6.4.2-94 (July 2023)](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-94/) | Look-ahead mechanism for planner attempts. Package OS compatibility validation. | Redis 6.2.10, 6.0.17 |\n| [6.4.2-81 (June 2023)](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-81/) | Email alerts for database backup failures and replica high availability shard relocation failures. | Redis 6.2.10, 6.0.17 |\n| [6.4.2-69 (May 2023)](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-69/) | Amazon Linux 2 support. Configure envoy ports using rladmin. Added option to avoid specific nodes when using the optimized shards placement API. Added failure_detection_sensitivity to replace watchdog_profile. | Redis 6.2.10, 6.0.17 |\n| [6.4.2-61 (April 2023)](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-61/) | Amazon Linux 2 support. Fixed known limitations for custom installation on RHEL 7 and RHEL 8, running rl_rdbconvert manually, and resharding rack-aware databases with no replication. | Redis 6.2.10, 6.0.17 |\n| [6.4.2-43 (March 2023)](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-43/) | Ubuntu 20.04 support. Safe node removal. Allow gossip_envoy port configuration. | Redis 6.2.10, 6.0.17 |\n| [6.4.2-30 (February 2023)](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-30/) | Pub/sub ACLs & default permissions. Validate client certificates by subject attributes. | Redis 6.2.7, 6.0.16 |"},{"id":"deprecations","title":"Deprecations","role":"content","text":""},{"id":"ubuntu-1604","title":"Ubuntu 16.04","role":"content","text":"Ubuntu 16 support is considered deprecated and will be removed in a future release. Ubuntu 16.04 LTS (Xenial) has reached the end of its free initial five-year security maintenance period as of April 30, 2021."},{"id":"active-active-database-persistence","title":"Active-Active database persistence","role":"content","text":"The RDB snapshot option for [Active-Active database persistence](https://redis.io/docs/latest/operate/rs/databases/active-active/manage#data-persistence) is deprecated and will be removed in a future release.\n\nPlease plan to reconfigure any Active-Active databases to use append-only file (AOF) persistence with the following command:\n\n[code example]"},{"id":"tls-10-and-tls-11","title":"TLS 1.0 and TLS 1.1","role":"content","text":"TLS 1.0 and TLS 1.1 connections are considered deprecated in favor of TLS 1.2 or later.\nPlease verify that all clients, apps, and connections support TLS 1.2. Support for the earlier protocols will be removed in a future release.\nCertain operating systems, such as RHEL 8, have already removed support for the earlier protocols. Redis Enterprise Software cannot support connection protocols that are not supported by the underlying operating system."},{"id":"3des-encryption-cipher","title":"3DES encryption cipher","role":"content","text":"The 3DES encryption cipher is considered deprecated in favor of stronger ciphers like AES.\nPlease verify that all clients, apps, and connections support the AES cipher. Support for 3DES will be removed in a future release.\nCertain operating systems, such as RHEL 8, have already removed support for 3DES. Redis Enterprise Software cannot support cipher suites that are not supported by the underlying operating system."},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"feature-limitations","title":"Feature limitations","role":"content","text":"- RS97971 - [Resharding fails for rack-aware databases with no replication](#resharding-fails-for-rack-aware-databases-with-no-replication) (fixed and resolved as part of [v6.4.2-61](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-61)).\n\n- RS101204 - High memory consumption caused by the `persistence_mgr` service when AOF persistence is configured for every second (fixed and resolved as part of [v6.4.2-81](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-81)).\n\n- RS40641 - API requests are redirected to an internal IP in case the request arrives from a node which is not the master. To avoid this issue, use [`rladmin cluster config`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/config) to configure `handle_redirects` or `handle_metrics_redirects`.\n\n- RS51144, RS102128 - Active-Active: To start successfully, the syncer (`crdt-syncer`) must connect to all sources. In multi-cluster configurations (more than 2 A-A clusters participating), in some cases, if one or more of the clusters is not available, A-A replication will be down.\n\n- RS123142 - In an Active-Active setup with at least three participating clusters, removing and re-adding a cluster after removing older clusters without re-adding them can cause missing keys and potentially lead to data loss or data inconsistency.\n\n    This issue will be fixed in a future maintenance release. To prevent this issue, avoid adding clusters until you upgrade to the upcoming maintenance release when available.\n\n#### Resharding fails for rack-aware databases with no replication\n\nWhen a database is configured as [rack-aware](https://redis.io/docs/latest/operate/rs/clusters/configure/rack-zone-awareness) and replication is turned off, the resharding operation fails.\n\nRS97971 - This limitation was fixed and resolved as part of [v6.4.2-61](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-61).   \n\nWorkaround:\n\nBefore resharding your database, turn off rack awareness:\n\n[code example]\n\nAfter the resharding process is complete, you can re-enable rack awareness:\n\n[code example]"},{"id":"installation-limitations","title":"Installation limitations","role":"setup","text":"Several Redis Enterprise Software installation reference files are installed to the directory `/etc/opt/redislabs/` even if you use [custom installation directories](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/customize-install-directories).\n\nAs a workaround to install Redis Enterprise Software without using any root directories, do the following before installing Redis Enterprise Software:\n\n1. Create all custom, non-root directories you want to use with Redis Enterprise Software.\n\n1. Mount `/etc/opt/redislabs` to one of the custom, non-root directories."},{"id":"upgrade-limitations","title":"Upgrade limitations","role":"content","text":"Before you upgrade a cluster that hosts Active-Active databases with modules to v6.4.2-30, perform the following steps:\n\n1. Use `crdb-cli` to verify that the modules (`modules`) and their versions (in `module_list`) are as they appear in the database configuration and in the default database configuration:\n\n    [code example]\n\n1. From the admin console's **redis modules** tab, validate that these modules with their specific versions are loaded to the cluster.\n\n1. If one or more of the modules/versions are missing or if you need help, [contact Redis support](https://redis.com/company/support/) before taking additional steps.\n\nThis limitation has been fixed and resolved as of [v6.4.2-43](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-43)."},{"id":"operating-system-limitations","title":"Operating system limitations","role":"content","text":"#### RHEL 7 and RHEL 8\n\nRS95344 - CRDB database will not start on Redis Enterprise v6.4.2 with a custom installation path.\n\nFor a workaround, use the following commands to add the relevant CRDB files to the Redis library:\n\n[code example]\n\nThis limitation has been fixed and resolved as of [v6.4.2-61](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-61).\n\n#### RHEL 8\n\nDue to module binary differences between RHEL 7 and RHEL 8, you cannot upgrade RHEL 7 clusters to RHEL 8 when they host databases using modules. Instead, you need to create a new cluster on RHEL 8 and then migrate existing data from your RHEL 7 cluster. This does not apply to clusters that do not use modules.\n\n#### Ubuntu 20.04\n\nBy default, you cannot use the SHA1 hash algorithm ([OpenSSL’s default security level is set to 2](https://manpages.ubuntu.com/manpages/focal/man3/SSL_CTX_set_security_level.3ssl.html#notes)). The operating system will reject SHA1 certificates even if the `mtls_allow_weak_hashing` option is enabled. You need to replace SHA1 certificates with newer certificates that use SHA-256. Note that the certificates provided with Redis Enterprise Software use SHA-256.  \n\n#### Modules not supported for Amazon Linux 2 release candidate\n\nA database with modules cannot reside on an Amazon Linux 2 (release candidate) node. Support was added as part of [v6.4.2-69](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-69)."}],"examples":[{"id":"active-active-database-persistence-ex0","language":"sh","code":"crdb-cli crdb update --crdb-guid <CRDB_GUID> \\\n    --default-db-config '{\"data_persistence\": \"aof\", \"aof_policy\":\"appendfsync-every-sec\"}'","section_id":"active-active-database-persistence"},{"id":"feature-limitations-ex0","language":"sh","code":"curl -k -u \"<user>:<password>\" -H \"Content-type: application/json\" -d '{\"rack_aware\": false}' -X PUT \"https://localhost:9443/v1/bdbs/<bdb_uid>\"","section_id":"feature-limitations"},{"id":"feature-limitations-ex1","language":"sh","code":"curl -k -u \"<user>:<password>\" -H \"Content-type: application/json\" -d '{\"rack_aware\": true}' -X PUT \"https://localhost:9443/v1/bdbs/<bdb_uid>\"","section_id":"feature-limitations"},{"id":"upgrade-limitations-ex0","language":"sh","code":"crdb-cli crdb get --crdb-guid <crdb-guid>","section_id":"upgrade-limitations"},{"id":"operating-system-limitations-ex0","language":"sh","code":"$ yum install -y chrpath\n$ find $installdir -name \"crdt.so\" | xargs -n1 -I {} /bin/bash -c 'chrpath -r ${libdir} {}'","section_id":"operating-system-limitations"}]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-103","title":"Redis Enterprise Software release notes 6.4.2-103 (October 2023)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-103/","summary":"RHEL 8.8 support. RediSearch v2.6.12. RedisGraph v2.10.12. RedisTimeSeries v1.8.11 Log when CCS schema changes. Bug fixes.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"4cd905950a0d7993b7a48208168536257e2241321465f9069217b834b411b8c7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Enterprise Software version 6.4.2](https://redis.io/downloads/#software)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- RHEL 8.8 support\n\n- RediSearch v2.6.12, RedisGraph v2.10.12, and RedisTimeSeries v1.8.11\n\n- Log when CCS schema changes\n\n- Bug fixes"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"enhancements","title":"Enhancements","role":"content","text":"- RHEL 8.8 support\n\n- Log when CCS schema changes due to schema upgrade\n\n#### Redis modules\n\nRedis Enterprise Software version 6.4.2-103 includes the following Redis Stack modules:\n\n- [RediSearch v2.6.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)\n\n- [RedisJSON v2.4.7](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)\n\n- [RedisBloom v2.4.5](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)\n\n- [RedisGraph v2.10.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes)\n\n- [RedisTimeSeries v1.8.11](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS109744 - Fixed an issue where node removal sometimes got stuck in a `starting` state when `wait_for_persistence` was enabled.\n\n- RS76441 - For databases created before Redis Enterprise Software v6.0.20, `crdt_syncer_auto_oom_unlatch` was `EMPTY` and `disabled` by default. `crdt_syncer_auto_oom_unlatch` is now enabled by default for new databases."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Enterprise Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Enterprise Software.\n\n<span title=\"Warning icon\">&#x26A0;&#xFE0F;</span> Deprecated – The platform is still supported for this version of Redis Enterprise Software, but support will be removed in a future release.\n\n<span title=\"X icon\">&#x274c;</span> End of life – Platform support ended in this version of Redis Enterprise Software.\n\n| Redis Enterprise | 7.2.4 | 6.4.2 | 6.2.18 | 6.2.12 | 6.2.10 | 6.2.8 | 6.2.4 |\n|------------------|-------|-------|--------|--------|--------|--------|-------|\n| **Release date** | Aug<br />2023 | Feb<br />2023 | Sept<br />2022 | Aug<br />2022 | Feb<br />2022 | Oct<br />2021 | Aug<br />2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | – | Feb<br />2025 | Aug<br />2024 | Aug<br />2024 | Aug<br />2024 | Aug<br />2024 | Aug<br />2024 |\n| **Ubuntu**<sup>[1](#table-note-1)</sup> |\n| 20.04 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span><sup>[6](#table-note-6)</sup> | – | – | – | – | – |\n| 18.04 | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\"><span title=\"Supported\">&#x2705;</span></span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 16.04 | <span title=\"End of life\">&#x274c;</span> | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **RHEL & CentOS**<sup>[2](#table-note-2)</sup>\n| 8.8 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span><sup>[8](#table-note-8)</sup> | – | – | – | – | – |\n| 8.7 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – | – |\n| 8.5-8.6 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – |\n| 8.0-8.4 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| 7.0-7.9 | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Oracle Linux**<sup>[3](#table-note-3)</sup> |\n| 8 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – |\n| 7 | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Rocky Linux**<sup>[3](#table-note-3)</sup> |\n| 8 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| **Amazon Linux** |\n| 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span><sup>[7](#table-note-7)</sup> | – | – | – | – | – |\n| 1 | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Docker**<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Kubernetes**<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n2. <a name=\"table-note-2\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>RHEL and CentOS deployments require OpenSSL 1.0.2 and [firewall configuration](https://redis.io/docs/latest/operate/rs/installing-upgrading/configuring/centos-rhel-firewall).\n\n3. <a name=\"table-note-3\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>Based on the corresponding RHEL version.\n\n4. <a name=\"table-note-4\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>\n[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Enterprise Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes).\n\n6. <a name=\"table-note-6\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>Ubuntu 20.04 support was added in Redis Enterprise Software [6.4.2-43](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-43).\n\n7. <a name=\"table-note-7\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>A release candidate for Amazon Linux 2 support was added in Redis Enterprise Software [6.4.2-61](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-61). Official support for Amazon Linux 2 was added in Redis Enterprise Software [6.4.2-69](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-69).\n\n8. <a name=\"table-note-8\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>Redis Enterprise Software [6.4.2-103](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-103) and later supports RHEL 8.8."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the MD5 checksums for the available packages:\n\n| Package | MD5 checksum (6.4.2-103 October release) |\n|---------|---------------------------------------|\n| Ubuntu 18 | 6544fd5d1625ab9da7b0d99f195fe4f1 |\n| Ubuntu 20 | d623c8458a28f09a1e6ae0c0a8650023 |\n| RedHat Enterprise Linux (RHEL) 7<br/>Oracle Enterprise Linux (OL) 7 | 06cc49ddbf560c516c52ae721c2806cd |\n| RedHat Enterprise Linux (RHEL) 8<br/>Oracle Enterprise Linux (OL) 8 <br/>Rocky Enterprise Linux | 2d799af5ee03c0a94660a60cd99ea361 |\n| Amazon Linux 2 | f2c5c2b5e879b8fd6792336f757593e2 |"},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Enterprise Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Enterprise has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Enterprise due to different or additional functionality available in Redis Enterprise that is not available in open source Redis.\n\n\nRedis Enterprise 6.4.2-103 supports open source Redis 6.2 and 6.0. Below is the list of open source Redis CVEs fixed by version.\n\nRedis 6.2.x:\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)\n\nRedis 6.0.x:\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.0.20)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.0.19)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.0.18)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 6.0.18)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.0.17)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-110","title":"Redis Enterprise Software release notes 6.4.2-110 (May 2024)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-110/","summary":"RedisGraph v2.10.15. RedisBloom v2.4.8. Bug fixes.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"ec797db53cdaecad255275443d1ad2618277c088f894b0553a5135df5b6f9090","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Enterprise Software version 6.4.2](https://redis.io/downloads/#software)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- RedisGraph v2.10.15\n\n  > [!NOTE]\n  > Redis has announced the end of life for RedisGraph. See the [announcement](https://redis.io/blog/redisgraph-eol/) for details.\n\n- RedisBloom v2.4.8\n\n- Bug fixes"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"enhancements","title":"Enhancements","role":"content","text":"#### Redis modules\n\nRedis Enterprise Software version 6.4.2-110 includes the following Redis Stack modules:\n\n- [RediSearch v2.6.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)\n\n- [RedisJSON v2.4.7](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)\n\n- [RedisBloom v2.4.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)\n\n- [RedisGraph v2.10.15](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes)\n\n- [RedisTimeSeries v1.8.11](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS123237: CRDT syncer prevents potential data mismatches or data loss due to missing keys when removing and re-adding a cluster in an Active-Active setup with at least three participating clusters after removing older clusters without re-adding them."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Enterprise Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Enterprise Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Enterprise Software, but support will be removed in a future release.\n\n| Redis Enterprise<br />major versions | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | July 2025 | Feb 2025 | Aug 2024 |\n| **Platforms** | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>\n[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Enterprise Software are certified for development and testing only."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the MD5 checksums for the available packages:\n\n| Package | MD5 checksum (6.4.2-110 May release) |\n|---------|---------------------------------------|\n| Ubuntu 16 | e61aab4bbb6837230451b70f8e0aa056 |\n| Ubuntu 18 | 3180ea41e744acca860257dcef6c49c4 |\n| Red Hat Enterprise Linux (RHEL) 7 | aaf0c1110f5ecc9ce55bf2935f2bcb73 |\n| Red Hat Enterprise Linux (RHEL) 8 | d69894c4bc0860274973a4d78d53cb5d |\n| Amazon Linux 2 | 19ac38908e47bd59c5890d1fc3bdba17 |"},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Enterprise Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Enterprise has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Enterprise due to different or additional functionality available in Redis Enterprise that is not available in open source Redis.\n\nRedis Enterprise 6.4.2-110 supports open source Redis 6.2 and 6.0. Below is the list of open source Redis CVEs fixed by version.\n\nRedis 6.2.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-45145) The wrong order of listen(2) and chmod(2) calls creates a\nrace condition that can be used by another process to bypass desired Unix\nsocket permissions on startup. (Redis 6.2.14)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)\n\nRedis 6.0.x:\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.0.20)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.0.19)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.0.18)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 6.0.18)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.0.17)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-115","title":"Redis Enterprise Software release notes 6.4.2-115 (Oct 2024)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-115/","summary":"RediSearch v2.6.23. RedisBloom v2.4.12. RedisTimeSeries v1.8.15.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"15f82a67762cc0b77064e875125e686b27615eb29d629aaf6b92a6c82a00dbac","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Enterprise Software version 6.4.2](https://redis.io/downloads/#software)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- RediSearch v2.6.23\n\n- RedisBloom v2.4.12\n\n- RedisTimeSeries v1.8.15"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"enhancements","title":"Enhancements","role":"content","text":"#### Redis modules\n\nRedis Enterprise Software version 6.4.2-115 includes the following Redis Stack modules:\n\n- [RediSearch v2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)\n\n- [RedisJSON v2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)\n\n- [RedisBloom v2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)\n\n- [RedisGraph v2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes)\n\n- [RedisTimeSeries v1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)"},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Enterprise Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Enterprise Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Enterprise Software, but support will be removed in a future release.\n\n| Redis Enterprise<br />major versions | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>\n[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Enterprise Software are certified for development and testing only."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the MD5 checksums for the available packages:\n\n| Package | MD5 checksum (6.4.2-115 Oct release) |\n|---------|---------------------------------------|\n| Ubuntu 16 | 5fb2186abb6c87efebd987ebf827b949 |\n| Ubuntu 18 | e8a938c7125fbc7d7f85f0e19d4afafb |\n| Red Hat Enterprise Linux (RHEL) 7 | dc646920043bd888299726027d9e9216 |\n| Red Hat Enterprise Linux (RHEL) 8 | 54574ca69f2966f7361f281f1ea3b312 |\n| Amazon Linux 2 | 8315ba719f1549e8bf423a9db004d49c |"},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Enterprise Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Enterprise has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Enterprise due to different or additional functionality available in Redis Enterprise that is not available in open source Redis.\n\nRedis Enterprise 6.4.2-115 supports open source Redis 6.2 and 6.0. Below is the list of open source Redis CVEs fixed by version.\n\nRedis 6.2.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-45145) The wrong order of listen(2) and chmod(2) calls creates a\nrace condition that can be used by another process to bypass desired Unix\nsocket permissions on startup. (Redis 6.2.14)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow can occur when handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` can occur when parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)\n\nRedis 6.0.x:\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.0.20)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.0.19)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.0.18)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 6.0.18)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.0.17)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-121","title":"Redis Enterprise Software release notes 6.4.2-121 (March 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-121/","summary":"Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"eb4111788bae5fb1a27e55f64ad37fd577d34a1a578915e992280bb174d342b1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Enterprise Software version 6.4.2](https://redis.io/downloads/#software)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":"#### Redis modules\n\nRedis Enterprise Software version 6.4.2-121 includes the following Redis Stack modules:\n\n- [RediSearch v2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)\n\n- [RedisJSON v2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)\n\n- [RedisBloom v2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)\n\n- [RedisGraph v2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes)\n\n- [RedisTimeSeries v1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)"},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the MD5 checksums for the available packages:\n\n| Package | MD5 checksum (6.4.2-121 March release) |\n|---------|---------------------------------------|\n| Ubuntu 16 | 251324d7ee04b524e0e08d3ddb6e2723 |\n| Ubuntu 18 | fb7e0931e75174cc8433cb7f4c9b3b15 |\n| Ubuntu 20 | 1e6abb275aa9a44246571313a46f391a |\n| Red Hat Enterprise Linux (RHEL) 7 | 3da1cf1c73c2bb116b4d09456f8693bd |\n| Red Hat Enterprise Linux (RHEL) 8 | 8b06790eb63d769216e87c183a5a64e1 |\n| Amazon Linux 2 | 3ff40b912c0f5de632fa3a144f6a77e9 |"},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Enterprise Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Enterprise has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Enterprise due to different or additional functionality available in Redis Enterprise that is not available in open source Redis.\n\nRedis Enterprise 6.4.2-121 supports open source Redis 6.2 and 6.0. Below is the list of open source Redis CVEs fixed by version.\n\nRedis 6.2.x:\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-45145) The wrong order of listen(2) and chmod(2) calls creates a\nrace condition that can be used by another process to bypass desired Unix\nsocket permissions on startup. (Redis 6.2.14)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow can occur when handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` can occur when parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)\n\nRedis 6.0.x:\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.0.20)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.0.19)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.0.18)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 6.0.18)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.0.17)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-127","title":"Redis Enterprise Software release notes 6.4.2-127 (June 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-127/","summary":"Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"cc587c2ae36e5a599d91267087f0e7e1051dda3a855b68e99d4ae3ff97803024","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Enterprise Software version 6.4.2](https://redis.io/downloads/#software)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-modules","title":"Redis modules","role":"content","text":"Redis Enterprise Software version 6.4.2-127 includes the following Redis Stack modules:\n\n- [RediSearch v2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)\n\n- [RedisJSON v2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)\n\n- [RedisBloom v2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)\n\n- [RedisGraph v2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes)\n\n- [RedisTimeSeries v1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)"},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Enterprise Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Enterprise Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Enterprise Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the MD5 checksums for the available packages:\n\n| Package | MD5 checksum (6.4.2-127 June release) |\n|---------|---------------------------------------|\n| Ubuntu 16 | a7f5838f86a08bdc9bdac7cefdb45b5d |\n| Ubuntu 18 | ca5e7852ce6f85aa5d7e6d31df6eba62 |\n| Ubuntu 20 | a88ca5b0a38f7cac661a873c2a02fb3d |\n| Red Hat Enterprise Linux (RHEL) 7 | 58ab0ba78bd2752607cbc0f1e5ae0900 |\n| Red Hat Enterprise Linux (RHEL) 8 | 75c885e70bff697e33733783ff488a97 |\n| Amazon Linux 2 | cb8ba0e4105dd926b8709dfd20d31cee |"},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Enterprise Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Enterprise has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Enterprise due to different or additional functionality available in Redis Enterprise that is not available in open source Redis.\n\nRedis Enterprise 6.4.2-127 supports open source Redis 6.2 and 6.0. Below is the list of open source Redis CVEs fixed by version.\n\nRedis 6.2.x:\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-45145) The wrong order of listen(2) and chmod(2) calls creates a\nrace condition that can be used by another process to bypass desired Unix\nsocket permissions on startup. (Redis 6.2.14)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow can occur when handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` can occur when parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)\n\nRedis 6.0.x:\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.0.20)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.0.19)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.0.18)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 6.0.18)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.0.17)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-131","title":"Redis Enterprise Software release notes 6.4.2-131 (September 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-131/","summary":"Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"0fc9c7547f7558529245dff0d1db06d594175046a226f43d07dd9b9d0d810e01","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​Redis Enterprise Software version 6.4.2."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-modules","title":"Redis modules","role":"content","text":"Redis Enterprise Software version 6.4.2-131 includes the following Redis Stack modules:\n\n- [RediSearch v2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)\n\n- [RedisJSON v2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)\n\n- [RedisBloom v2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)\n\n- [RedisGraph v2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes)\n\n- [RedisTimeSeries v1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)"},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Enterprise Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Enterprise Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Enterprise Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the MD5 checksums for the available packages:\n\n| Package | MD5 checksum (6.4.2-131 September release) |\n|---------|---------------------------------------|\n| Ubuntu 18 | acdb7e4623d17f653985e52e87de98a4 |\n| Ubuntu 20 | 076aceab8f51f27e8061f8fe40d13869 |\n| Red Hat Enterprise Linux (RHEL) 8 | 689b8b362e93bd83df161b5c6da4bf0d |\n| Amazon Linux 2 | 75d9b521f4a1aabeb9a5a16f6a1e9fd8 |"},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Enterprise Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Enterprise has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Enterprise due to different or additional functionality available in Redis Enterprise that is not available in open source Redis.\n\nRedis Enterprise 6.4.2-131 supports open source Redis 6.2 and 6.0. Below is the list of open source Redis CVEs and other security vulnerabilities fixed by version.\n\nRedis 6.2.x:\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-45145) The wrong order of listen(2) and chmod(2) calls creates a\nrace condition that can be used by another process to bypass desired Unix\nsocket permissions on startup. (Redis 6.2.14)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow can occur when handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` can occur when parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)\n\nRedis 6.0.x:\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.0.20)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.0.19)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.0.18)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 6.0.18)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.0.17)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-30","title":"Redis Enterprise Software release notes 6.4.2-30 (February 2023)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-30/","summary":"Pub/sub ACLs & default permissions. Validate client certificates by subject attributes.","tags":["docs","operate","rs"],"last_updated":"2026-10-06T08:53:14-07:00","page_type":"content","content_hash":"292aa0f75b778091e2473217893e25a56f38200f92c9b0e170b8d2ba637862e4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"​[​Redis Enterprise Software version 6.4.2](https://redis.io/downloads/#software) is now available!\n\nThis version offers:\n\n- Extended validation of client certificates via mTLS (mutual TLS) full subject support\n\n- Support for default restrictive permissions when using publish/subscribe commands and ACLs (access control lists)\n\n- Enhanced TLS performance when Redis returns large arrays in responses\n\n- Compatibility with [open source Redis 6.2.7](https://github.com/redis/redis)\n\n- Additional enhancements and bug fixes\n\nThe following table shows the MD5 checksums for the available packages:\n\n| Package | MD5 checksum (6.4.2-30 February release) |\n|---------|---------------------------------------|\n| Ubuntu 16 | b0dbecaa974ca08245dda55d53b6fe9b |\n| Ubuntu 18 | a5192e8b0734db80d6b7c2b98a170c58 |\n| RedHat Enterprise Linux (RHEL) 7<br/>Oracle Enterprise Linux (OL) 7 | c1537855dcfe7a7cedf9031ce01e2b9b |\n| RedHat Enterprise Linux (RHEL) 8<br/>Oracle Enterprise Linux (OL) 8 <br/>Rocky Enterprise Linux | a24dc749d6dcb5df2162d7a41791c7aa |"},{"id":"new-features-and-enhancements","title":"New features and enhancements","role":"content","text":"#### Validate client certificates by subject attributes\n\nYou can now validate client certificates by their `Subject` attributes. When a client attempts to connect to a database, Redis Enterprise Software compares the values of the client certificate subject attributes to the subject values allowed by the database. Clients can connect to the database only if the subject values match. This gives more flexibility in controlling which clients can access which databases.\n\nSee [Enable TLS](https://redis.io/docs/latest/operate/rs/security/encryption/tls/enable-tls) for more information.\n\n\n#### Default pub/sub ACL permissions\n\nRedis is continuously enhancing its ACL (access control list) functionality and coverage. Redis version 6.2 enhances [ACLs](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl) to allow and disallow pub/sub channels. \n\nPart of protecting pub/sub channels requires changing the default access from permissive to restrictive, which blocks all pub/sub channels unless specifically permitted by an ACL rule. To allow this transition across all databases in the cluster, Redis Enterprise Software 6.4.2 provides a new configuration option `acl-pubsub-default` that sets the cluster-wide default for all channels to either permitted or restricted.\n\nThe 6.4.2 installation-provided value of `acl-pubsub-default` is permissive (`allchannels`) to comply with earlier Redis versions. After you upgrade all databases in the cluster to Redis DB version 6.2 (or later in future versions), you can use `rladmin` or the REST API to change the value to restrictive (`resetchannels`).\n\nTo allow certain users to access specific pub/sub channels, define the appropriate ACL. Redis Enterprise Software 6.4.2 enhances the admin console (UI), CLI, and REST API to support pub/sub channel ACL definitions.\n\nIf you use ACLs and pub/sub channels, we recommend you review your databases and ACL settings and plan to change your cluster to restricted mode. This will help you prepare for future Redis Enterprise Software releases that use restrictive `resetchannels` as the new default for `acl-pubsub-default`.\n\n#### Redis modules \n\nRedis Enterprise Software v6.4.2 includes the following Redis modules:\n\n- [RediSearch v2.4.16](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.4-release-notes#v2416-november-2022)\n\n- [RedisJSON v2.2.0](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.2-release-notes#v220-july-2022)\n\n- [RedisBloom v2.2.18](/operate/oss_and_stack/stack-with-enterprise/bloom/release-notes/redisbloom-2.2-release-notes/#v2218-july-2022)\n\n- [RedisGraph v2.8.20](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.8-release-notes#v2820-september-2022)\n\n- [RedisTimeSeries v1.6.17](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.6-release-notes#v1617-july-2022)\n\nSee [Upgrade modules](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/upgrade-module) to learn how to upgrade a module for a database. \n\n#### Installations, upgrades, and troubleshooting\n\n- Added the ability for `install.sh` to run even if \"upgrade mode\" is already enabled to allow reruns in case of a previous run failure (RS77319)\n\n- Added log messages to the `redis_mgr` process (RS77891), the job_scheduler process (RS82673), and the `install.sh` script (RS82673)\n\n- Improved `rladmin` error messages for certificate validation (RS79933)\n\n- Added internode encryption ports to command-line utility `rlcheck` validation (RS68965)\n\n- Added an alert to notify when a node operation (such as maintenance mode) failed, aborted, or was canceled. The alert is enabled by default (RS76089)"},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"- REST API: the `authorized_names` field of the [BDB object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb) is deprecated. Use the new `authorized_subjects` field instead."},{"id":"new-default-redis-db-version","title":"New default Redis DB version","role":"content","text":"Both Redis Enterprise Software versions 6.2.x and 6.4.x package two Redis DB versions: Redis DB 6.0 and Redis DB 6.2. Until now, the default Redis DB version for creating new databases and upgrading existing databases was 6.0 (enforced by the `redis_upgrade_policy` parameter).\n\nTo allow customers more flexibility in future upgrades, starting with Redis Enterprise Software 6.4.2, the default Redis DB version for new and upgraded databases is now 6.2 for all upgrade policies (`redis_upgrade_policy=major` and `redis_upgrade_policy=latest`). \n\n| Redis<br />Enterprise | Bundled Redis<br />DB versions | Default DB version<br />(upgraded/new databases) |\n|-------|----------|-----|\n| 6.2.x | 6.0, 6.2 | 6.0 |\n| 6.4.2 | 6.0, 6.2 | 6.2 |\n\nYou can override the default version with [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-cluster); however, we recommend that you don't change this setting."},{"id":"deprecations","title":"Deprecations","role":"content","text":"#### Ubuntu 16.04\n\nUbuntu 16 support is considered deprecated and will be removed in a future release. Ubuntu 16.04 LTS (Xenial) has reached the end of its free initial five-year security maintenance period as of April 30, 2021.\n\n#### Active-Active database persistence\n\nThe snapshot option for Active-Active database persistence is deprecated. We advise customers running Active-Active databases, configured with snapshot data persistence, to reconfigure their data persistence mode to use the AOF (Append Only File) option with the following command:\n\n[code example]\n\n#### TLS 1.0 and TLS 1.1\n\nTLS 1.0 and TLS 1.1 connections are considered deprecated in favor of TLS 1.2 or later.\nPlease verify that all clients, apps, and connections support TLS 1.2. Support for the earlier protocols will be removed in a future release.\nCertain operating systems, such as RHEL 8, have already removed support for the earlier protocols. Redis Enterprise Software cannot support connection protocols that are not supported by the underlying operating system.\n\n#### 3DES encryption cipher\n\nThe 3DES encryption cipher is considered deprecated in favor of stronger ciphers like AES.\nPlease verify that all clients, apps, and connections support the AES cipher. Support for 3DES will be removed in a future release.\nCertain operating systems, such as RHEL 8, have already removed support for 3DES. Redis Enterprise Software cannot support cipher suites that are not supported by the underlying operating system."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS72866 - Improved performance for client connections which use TLS\n\n- RS78241 - Fixed shard placement to always respect rack-zone restrictions and avoid a state where a primary (master) and replica are on the same rack, even if temporarily\n\n- RS78144 - Removed the dependency on system-wide `ldconfig` so non-interactive processes will use their own dynamic libraries without impacting external services\n\n- RS78028 - Fixed race condition during rolling upgrade that might result in shards repeatedly restarting\n\n- RS77964 - Fixed module deletion to remove the old directory with the module\n\n- RS75259 - Fixed node to prevent using plain text communication instead of TLS after losing connectivity\n\n- RS69616 - Fixed validation for internode communication ports\n\n- RS83535 - Fixed `sentinel_service` to start on RHEL 8 with DISA STIG profile \n\n- RS87191 - Fixed a cross slot error when using Auto Tiering with Replica Of, in case a key on the source database swapped from RAM to flash and expired while it was also part of Lua script execution"},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"feature-limitations","title":"Feature limitations","role":"content","text":"- RS101204 - High memory consumption caused by the `persistence_mgr` service when AOF persistence is configured for every second. Monitor RAM usage of the process. In case of high usage, the temporary workaround is to restart the service by running `supervisorctl restart persistence_mgr`. A permanent fix is to install or upgrade to the 6.4.2 June maintenance release."},{"id":"upgrade-limitations","title":"Upgrade limitations","role":"content","text":"Before you upgrade a cluster that hosts Active-Active databases with modules to v6.4.2-30, perform the following steps:\n\n1. Use `crdb-cli` to verify that the modules (`modules`) and their versions (in `module_list`) are as they appear in the database configuration and in the default database configuration:\n\n    [code example]\n\n1. From the admin console's **redis modules** tab, validate that these modules with their specific versions are loaded to the cluster.\n\n1. If one or more of the modules/versions are missing or if you need help, [contact Redis support](https://redis.com/company/support/) before taking additional steps.\n\nThis limitation has been fixed and resolved as of [v6.4.2-43](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-43)."},{"id":"operating-system-limitations","title":"Operating system limitations","role":"content","text":"#### RHEL 7 and RHEL 8\n\nIf you have a custom installation with a non-default `$installdir` and use Active-Active or Auto Tiering features, failures might occur when you upgrade. This issue will be fixed in a future maintenance release. \n\n#### RHEL 8\n\nDue to module binary differences between RHEL 7 and RHEL 8, you cannot upgrade RHEL 7 clusters to RHEL 8 when they host databases using modules. Instead, you need to create a new cluster on RHEL 8 and then migrate existing data from your RHEL 7 cluster. This does not apply to clusters that do not use modules."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Enterprise Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). The following open source Redis [CVEs](https://github.com/redis/redis/security/advisories) do not affect Redis Enterprise:\n\n- [CVE-2021-32625](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-32625) — Redis Enterprise is not impacted by the CVE that was found and fixed in open source Redis since Redis Enterprise does not implement LCS. Additional information about the open source Redis fix is on the [Redis GitHub page](https://github.com/redis/redis/releases) (Redis 6.2.4, Redis 6.0.14)\n\n- [CVE-2021-32672](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-32672) — Redis Enterprise is not impacted by the CVE that was found and fixed in open source Redis because the LUA debugger is unsupported in Redis Enterprise. Additional information about the open source Redis fix is on the [Redis GitHub page](https://github.com/redis/redis/releases) (Redis 6.2.6, Redis 6.0.16)\n\n- [CVE-2021-32675](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-32675) — Redis Enterprise is not impacted by the CVE that was found and fixed in open source Redis because the proxy in Redis Enterprise does not forward unauthenticated requests. Additional information about the open source Redis fix is on the [Redis GitHub page](https://github.com/redis/redis/releases) (Redis 6.2.6, Redis 6.0.16)\n\n- [CVE-2021-32762](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-32762) — Redis Enterprise is not impacted by the CVE that was found and fixed in open source Redis because the memory allocator used in Redis Enterprise is not vulnerable. Additional information about the open source Redis fix is on the [Redis GitHub page](https://github.com/redis/redis/releases) (Redis 6.2.6, Redis 6.0.16)\n\n- [CVE-2021-41099](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41099) — Redis Enterprise is not impacted by the CVE that was found and fixed in open source Redis because the `proto-max-bulk-len CONFIG` is blocked in Redis Enterprise. Additional information about the open source Redis fix is on the [Redis GitHub page](https://github.com/redis/redis/releases) (Redis 6.2.6, Redis 6.0.16)\n\nRedis Enterprise has already included the fixes for the relevant CVEs. Some CVEs announced for open source Redis do not affect Redis Enterprise due to different and additional functionality available in Redis Enterprise that is not available in open source Redis."}],"examples":[{"id":"deprecations-ex0","language":"sh","code":"crdb-cli crdb update --crdb-guid <CRDB_GUID> \\\n    --default-db-config '{\"data_persistence\": \"aof\", \"aof_policy\":\"appendfsync-every-sec\"}'","section_id":"deprecations"},{"id":"upgrade-limitations-ex0","language":"sh","code":"crdb-cli crdb get --crdb-guid <crdb-guid>","section_id":"upgrade-limitations"}]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-43","title":"Redis Enterprise Software release notes 6.4.2-43 (March 2023)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-43/","summary":"Ubuntu 20.04 support. Safe node removal. Allow gossip_envoy port configuration.","tags":["docs","operate","rs"],"last_updated":"2026-10-06T08:53:14-07:00","page_type":"content","content_hash":"bb30cfc657085cadbdded541d0cab539df550200803726cdc64d4408e44c8c06","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Enterprise Software version 6.4.2](https://redis.io/downloads/#software).\n\nThe following table shows the MD5 checksums for the available packages:\n\n| Package | MD5 checksum (6.4.2-43 March release) |\n|---------|---------------------------------------|\n| Ubuntu 16 | bb3d3ab7590834790d2d9d9e3dcdf788 |\n| Ubuntu 18 | 55e99e6db76277c15fa46c49d58995b5 |\n| Ubuntu 20 | cccfa62ca7ea955b8c19d9c40bdc1fea |\n| RedHat Enterprise Linux (RHEL) 7<br/>Oracle Enterprise Linux (OL) 7 | eed4083b0b4c066d31ea9b8ba60a34f5 |\n| RedHat Enterprise Linux (RHEL) 8<br/>Oracle Enterprise Linux (OL) 8 <br/>Rocky Enterprise Linux | 7fd4321f6a5502c76197fc039f44ffa3 |"},{"id":"new-features-and-enhancements","title":"New features and enhancements","role":"content","text":"#### Support for Ubuntu 20\n\nAdded operating system support for Ubuntu 20.04.\n\n#### Validate client certificates by subject attributes\n\n- Added the ability to enter multiple `Organizational Unit (OU)` values to a certificate validation's subject entry. Subject validation only succeeds if all `OU` values match.\n\n- Validation fails if the certificate’s subject contains an extra field which is missing from the certificate’s configured subject (empty field).\n\n#### Safe node removal (RS92095)\n\nAs of this release, the node removal task waits for persistence files to be created on a new node before finishing removal. For more information, see [Remove a cluster node](https://redis.io/docs/latest/operate/rs/clusters/remove-node) and [`rladmin node remove`](https://docs.redis.com/latest/rs/references/cli-utilities/rladmin/node/remove/).\n\n#### Allow port configuration for gossip_envoy service (RS72028) \n\nYou can use the REST API to configure the `gossip_envoy_admin_port` during bootstrapping or runtime. See the [cluster object reference](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cluster) for more details.\n\nSupport for `rladmin` will be added in a future 6.4.2 maintenance release.\n\n#### Redis Stack v6.2.6\n\nRedis Enterprise Software v6.4.2 includes the new features delivered in the latest [Redis Stack release (v6.2.6)](https://redis.com/blog/introducing-redis-stack-6-2-6-and-7-0-6/ ):\n\n- [RediSearch v2.6.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes#v264-december-2022)\n\n- [RedisJSON v2.4.5](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes#v245-february-2023)\n\n- [RedisBloom v2.4.3](/operate/oss_and_stack/stack-with-enterprise/bloom/release-notes/redisbloom-2.4-release-notes/#v24-ga-v243-november-2022)\n\n- [RedisGraph v2.10.5](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes#v2105-december-2022)\n\n- [RedisTimeSeries v1.8.5](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes#v185-january-2023)\n\nSee [Upgrade modules](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/upgrade-module) to learn how to upgrade a module for a database."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS95344 - Fix custom installation linkage bug for files related to Active-Active\n\n- RS91554 - Enhance visibility of `redis_mgr` \n\n- RS90496 - Improve cross-node communication to support failovers\n\n- RS93004 - Allow positive `audit_port` configuration\n\n- RS58918 - Reduce the number of clients blocked by flushing a database. Only the client that requested the flush will be blocked until the action finishes. Other clients won't be affected.\n\n- RS85447 - When creating a database, the memory size is total memory for the database, not the total dataset size. Therefore, if you enable replication and the database has two shards, each shard will have half of the allocated memory size.\n\n- RS90556 -  `Heartbeatd` - upgrade [`sysinfo`](https://crates.io/crates/sysinfo) dependency 0.19.1 -> 0.27.7\n\n- RS93566 - Added [`rlcheck`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rlcheck) validation step to verify that all needed modules (modules used during the initial creation of any CRDBs on the cluster) are actually available on the cluster. In some cases, if a specific module version is required, the user will need to upload it in order to upgrade the cluster. After the upgrade is complete, you can delete the required module."},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"feature-limitations","title":"Feature limitations","role":"content","text":"#### Resharding fails for rack-aware databases with no replication\n\nWhen a database is configured as [rack-aware](https://redis.io/docs/latest/operate/rs/clusters/configure/rack-zone-awareness) and replication is turned off, the resharding operation fails.\n\nRS97971 - This limitation will be fixed in a future 6.4.2 maintenance release.    \n\nWorkaround:\n\nBefore resharding your database, turn off rack awareness:\n\n[code example]\n\nAfter the resharding process is complete, you can re-enable rack awareness:\n\n[code example]"},{"id":"operating-system-limitations","title":"Operating system limitations","role":"content","text":"#### RHEL 7 and RHEL 8\n\nRS95344 - CRDB database will not start on Redis Enterprise v6.4.2 with a custom installation path.\n\nFor a workaround, use the following commands to add the relevant CRDB files to the Redis library:\n\n[code example]\n\nThis limitation will be fixed in a future 6.4.2 maintenance release.\n\n#### Ubuntu 20.04\n\nBy default, you cannot use the SHA1 hash algorithm ([OpenSSL’s default security level is set to 2](https://manpages.ubuntu.com/manpages/focal/man3/SSL_CTX_set_security_level.3ssl.html#notes)). The operating system will reject SHA1 certificates even if the `mtls_allow_weak_hashing` option is enabled. You need to replace SHA1 certificates with newer certificates that use SHA-256. Note that the certificates provided with Redis Enterprise Software use SHA-256.  \n\n\n#### Ubuntu 16, 18, 20\n\nRS95824 - An error occurs when running `rl_rdbconvert` manually.\n\nAs a workaround, run the following command:\n\n[code example]"}],"examples":[{"id":"feature-limitations-ex0","language":"sh","code":"curl -k -u \"<user>:<password>\" -H \"Content-type: application/json\" -d '{\"rack_aware\": false}' -X PUT \"https://localhost:9443/v1/bdbs/<bdb_uid>\"","section_id":"feature-limitations"},{"id":"feature-limitations-ex1","language":"sh","code":"curl -k -u \"<user>:<password>\" -H \"Content-type: application/json\" -d '{\"rack_aware\": true}' -X PUT \"https://localhost:9443/v1/bdbs/<bdb_uid>\"","section_id":"feature-limitations"},{"id":"operating-system-limitations-ex0","language":"sh","code":"$ yum install -y chrpath\n$ find $installdir -name \"crdt.so\" | xargs -n1 -I {} /bin/bash -c 'chrpath -r ${libdir} {}'","section_id":"operating-system-limitations"},{"id":"operating-system-limitations-ex1","language":"sh","code":"LD_LIBRARY_PATH=$libdir rl_rdbconvert","section_id":"operating-system-limitations"}]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-61","title":"Redis Enterprise Software release notes 6.4.2-61 (April 2023)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-61/","summary":"Amazon Linux 2 support. Fixed known limitations for custom installation on RHEL 7 and RHEL 8, running rl_rdbconvert manually, and resharding rack-aware databases with no replication.","tags":["docs","operate","rs"],"last_updated":"2026-10-06T08:53:14-07:00","page_type":"content","content_hash":"0c5c0122cf47489ba1aa39086e1042870718e9875c8ae2535f954ea787fadb18","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Enterprise Software version 6.4.2](https://redis.io/downloads/#software).\n\nThe following table shows the MD5 checksums for the available packages:\n\n| Package | MD5 checksum (6.4.2-61 April release) |\n|---------|---------------------------------------|\n| Ubuntu 16 | f8f616147c9daaaeb9cd8cb1ae44157f |\n| Ubuntu 18 | 1de5b0bafd4edec8d7817bddcecf3824 |\n| Ubuntu 20 | 829e91c7fac1a0ab7e36cb4a65d19906 |\n| RedHat Enterprise Linux (RHEL) 7<br/>Oracle Enterprise Linux (OL) 7 | 477f1d3161ea16e8d52c41eb61d92637 |\n| RedHat Enterprise Linux (RHEL) 8<br/>Oracle Enterprise Linux (OL) 8 <br/>Rocky Enterprise Linux | 1170af43f88bf5f9095890916c0fe74d |\n| Amazon Linux 2 (Release Candidate) | e246ae158db32e0a483a23392b2a7f47 |"},{"id":"new-features-and-enhancements","title":"New features and enhancements","role":"content","text":"- Amazon Linux 2 supported as a release candidate (RC)\n\n    > [!NOTE]\n    > A database with modules cannot reside on an Amazon Linux 2 (release candidate) node. Support will be added in a future maintenance release.\n\n- Add node ID indication to [`debug_info` package](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/debug_info) (RS95360)\n\n- Add support for underscore `_` as a valid character for [rack awareness](https://redis.io/docs/latest/operate/rs/clusters/configure/rack-zone-awareness) (RS87458)\n\n- When updating a [BDB object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb), the version property is immutable. Any validation will be performed according to the BDB object version that was set during upgrade or install (RS93294)\n\n#### Redis Stack v6.2.6\n\nRedis Enterprise Software v6.4.2 includes the new features delivered in the latest [Redis Stack release (6.2.6 v6)](https://redis.com/blog/introducing-redis-stack-6-2-6-and-7-0-6/):\n\n- [RediSearch v2.6.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search)\n\n- [RedisJSON v2.4.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json)\n\n- [RedisBloom v2.4.5](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/bloom)\n\n- [RedisGraph v2.10.9](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/graph)\n\n- [RedisTimeSeries v1.8.9](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/timeseries)\n\nSee [Upgrade modules](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/upgrade-module) to learn how to upgrade a module for a database."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"deprecations","title":"Deprecations","role":"content","text":"#### Ubuntu 16.04\n\nUbuntu 16 support is deprecated and will be removed in a future release.\n\n#### Active-Active database persistence\n\nThe RDB snapshot option for [Active-Active database persistence](https://redis.io/docs/latest/operate/rs/databases/active-active/manage#data-persistence) is deprecated and will be removed in a future release.\n\nPlease plan to reconfigure any Active-Active databases to use append-only file (AOF) persistence with the following command:\n\n[code example]"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS95344 - Fixed known limitation for RHEL 7 and RHEL 8 where CRDB databases fail to start when installed using custom installation paths\n\n- RS88010 - Roll back node configuration when the [remove node](https://redis.io/docs/latest/operate/rs/clusters/remove-node) operation fails\n\n- RS95824 - Fixed an issue with running `rl_rdbconvert` on Ubuntu\n\n- RS97971 - Fixed known limitation for the edge case where resharding fails for rack-aware databases with no replication"},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"operating-system-limitations","title":"Operating system limitations","role":"content","text":"#### Ubuntu 20.04\n\nBy default, you cannot use the SHA1 hash algorithm ([OpenSSL’s default security level is set to 2](https://manpages.ubuntu.com/manpages/focal/man3/SSL_CTX_set_security_level.3ssl.html#notes)). The operating system will reject SHA1 certificates even if the `mtls_allow_weak_hashing` option is enabled. You need to replace SHA1 certificates with newer certificates that use SHA-256. Note that the certificates provided with Redis Enterprise Software use SHA-256.\n\n#### Modules not supported for Amazon Linux 2 release candidate\n\nA database with modules cannot reside on an Amazon Linux 2 (release candidate) node. Support will be added in a future maintenance release."}],"examples":[{"id":"deprecations-ex0","language":"sh","code":"crdb-cli crdb update --crdb-guid <CRDB_GUID> \\\n    --default-db-config '{\"data_persistence\": \"aof\", \"aof_policy\":\"appendfsync-every-sec\"}'","section_id":"deprecations"}]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-69","title":"Redis Enterprise Software release notes 6.4.2-69 (May 2023)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-69/","summary":"Amazon Linux 2 support. Configure envoy ports using rladmin. Added option to avoid specific nodes when using the optimized shards placement API. Added failure_detection_sensitivity to replace watchdog_profile.","tags":["docs","operate","rs"],"last_updated":"2026-10-06T08:53:14-07:00","page_type":"content","content_hash":"68e453b15d3cf301a4d3c6247ec7b2d4901734f9cb9997262c795275a51a7d1d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Enterprise Software version 6.4.2](https://redis.io/downloads/#software).\n\nThe following table shows the MD5 checksums for the available packages:\n\n| Package | MD5 checksum (6.4.2-69 May release) |\n|---------|---------------------------------------|\n| Ubuntu 16 | 31631248672de0154ec20aee0bff9adc |\n| Ubuntu 18 | 7d9ebd4ea5a23137d2bb3928134ea6ef |\n| Ubuntu 20 | 357ceda7467fd66962f3cb5aa143def7 |\n| RedHat Enterprise Linux (RHEL) 7<br/>Oracle Enterprise Linux (OL) 7 | 4d53fcce8c80910a05ed37ab64b57a7a |\n| RedHat Enterprise Linux (RHEL) 8<br/>Oracle Enterprise Linux (OL) 8 <br/>Rocky Enterprise Linux | 88dc6a4d96b948b086f785dc42786d03 |\n| Amazon Linux 2 | 7ed06bc0b35494e4a5c084528d6e8b9f |"},{"id":"new-features-and-enhancements","title":"New features and enhancements","role":"content","text":"- Amazon Linux 2 support\n\n- Enhanced installation logs (RS69079, RS69571, RS77857, RS89760)\n\n- Added support for `rladmin` envoy port configuration (RS95483)\n\n- Support the option to avoid specific nodes when using optimized shards placement API  (RS98795)\n\n- Check port availability before installation and upgrade (RS80815)\n\n- Added `failure_detection_sensitivity` policy to [cluster policy REST API requests](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/policy). See the [`watchdog_profile` deprecation notice](#watchdog_profile) for more details.\n\n#### Redis Stack v6.2.6\n\nRedis Enterprise Software v6.4.2 includes the new features delivered in the latest [Redis Stack release (6.2.6 v6)](https://redis.com/blog/introducing-redis-stack-6-2-6-and-7-0-6/):\n\n- [RediSearch v2.6.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search)\n\n- [RedisJSON v2.4.7](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json)\n\n- [RedisBloom v2.4.5](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/bloom)\n\n- [RedisGraph v2.10.9](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/graph)\n\n- [RedisTimeSeries v1.8.9](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/timeseries)\n\nSee [Upgrade modules](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/upgrade-module) to learn how to upgrade a module for a database."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"deprecations","title":"Deprecations","role":"content","text":"#### watchdog_profile\n\nThe `watchdog_profile` setting is deprecated and will be removed in a future release. Instead, use the `failure_detection_sensitivity` policy for predefined thresholds and timeouts:\n\n[code example]\n\nThe `failure_detection_sensitivity` policy has the following options:\n\n- `high` (previously known as `local-network watchdog_profile`) – high failure detection sensitivity, lower thresholds, and faster failure detection and failover\n\n- `low` (previously known as `cloud watchdog_profile`) – low failure detection sensitivity and higher tolerance for latency variance (also called network jitter)\n\n#### Ubuntu 16.04\n\nUbuntu 16 support is deprecated, and support will be removed in a future release.\n\n#### Active-Active database persistence\n\nThe RDB snapshot option for [Active-Active database persistence](https://redis.io/docs/latest/operate/rs/databases/active-active/manage#data-persistence) is deprecated and will be removed in a future release.\n\nPlease plan to reconfigure any Active-Active databases to use append-only file (AOF) persistence with the following command:\n\n[code example]"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS97528 - Prevent [self-signed certificate](https://redis.io/docs/latest/operate/rs/security/certificates/create-certificates) script error on Ubuntu 20.04 by changing the user instructions.\n\n- RS99643 - Fix [`cipher_suites`](https://redis.io/docs/latest/operate/rs/security/encryption/tls/ciphers) configuration to allow default cipher suites according to the control plane, data plane, and discovery service.\n\n- RS99696 - Fix  [`HINCRBYFLOT`](https://github.com/redis/redis/security/advisories/GHSA-hjv8-vjf6-wcr6) CVE\n\n- RS54226 - During upgrades, a CRDB worker process would intermittently get stuck in a restart loop. This issue is now fixed."},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"feature-limitations","title":"Feature limitations","role":"content","text":"- RS78430 -  When [tuning module arguments](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-db), any typo or use of unsupported arguments can cause shards to get stuck after restart."},{"id":"operating-system-limitations","title":"Operating system limitations","role":"content","text":"#### Ubuntu 20.04\n\nBy default, you cannot use the SHA1 hash algorithm ([OpenSSL’s default security level is set to 2](https://manpages.ubuntu.com/manpages/focal/man3/SSL_CTX_set_security_level.3ssl.html#notes)). The operating system will reject SHA1 certificates even if the `mtls_allow_weak_hashing` option is enabled. You need to replace SHA1 certificates with newer certificates that use SHA-256. Note that the certificates provided with Redis Enterprise Software use SHA-256."}],"examples":[{"id":"deprecations-ex0","language":"sh","code":"rladmin tune cluster failure_detection_sensitivity [ high | low ]","section_id":"deprecations"},{"id":"deprecations-ex1","language":"sh","code":"crdb-cli crdb update --crdb-guid <CRDB_GUID> \\\n    --default-db-config '{\"data_persistence\": \"aof\", \"aof_policy\":\"appendfsync-every-sec\"}'","section_id":"deprecations"}]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-81","title":"Redis Enterprise Software release notes 6.4.2-81 (June 2023)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-81/","summary":"Email alerts for database backup failures and replica high availability shard relocation failures.","tags":["docs","operate","rs"],"last_updated":"2026-10-06T08:53:14-07:00","page_type":"content","content_hash":"d3ac0dc30a19f960ab4cca5f8891a0aed3150adf5c8925ce4c7931da0d8cfe0c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Enterprise Software version 6.4.2](https://redis.io/downloads/#software).\n\nThe following table shows the MD5 checksums for the available packages:\n\n| Package | MD5 checksum (6.4.2-81 June release) |\n|---------|---------------------------------------|\n| Ubuntu 16 | 31631248672de0154ec20aee0bff9adc |\n| Ubuntu 18 | eb4e8c1bc1443923ebcd457e51684f7e |\n| Ubuntu 20 | 1256292b8dca6456e69dc309be51a8d0 |\n| RedHat Enterprise Linux (RHEL) 7<br/>Oracle Enterprise Linux (OL) 7 | 1e51c5fdf22c0b6988565444ba08c6cd |\n| RedHat Enterprise Linux (RHEL) 8<br/>Oracle Enterprise Linux (OL) 8 <br/>Rocky Enterprise Linux | 96dc036339fe18fb218e8d4c8ff50c99 |\n| Amazon Linux 2 | 1a549bdb360c9e22ef764faeadbd4c90 |"},{"id":"new-features-and-enhancements","title":"New features and enhancements","role":"content","text":"- RS77279 - Database backup failures and replica high availability ([replica HA](https://redis.io/docs/latest/operate/rs/databases/configure/replica-ha)) failures (for example, when relocating a shard from a node) now generate email alerts\n\n#### Redis Stack v6.2.6\n\nRedis Enterprise Software v6.4.2 includes the new features delivered in the latest [Redis Stack release (6.2.6 v6)](https://redis.com/blog/introducing-redis-stack-6-2-6-and-7-0-6/):\n\n- [RediSearch v2.6.9](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search)\n\n- [RedisJSON v2.4.7](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json)\n\n- [RedisBloom v2.4.5](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/bloom)\n\n- [RedisGraph v2.10.9](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/graph)\n\n- [RedisTimeSeries v1.8.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/timeseries)\n\nSee [Upgrade modules](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/upgrade-module) to learn how to upgrade a module for a database."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS101204 - Fixed high memory consumption that was caused by the `persistence_mgr` service when AOF persistence is configured for every second.\n\n- RS39322 , RS35526  - [`rlcheck`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rlcheck) no longer requires root privileges and can run with the `redislabs` group and user.\n\n- RS97315, RS100853  - Fixed the robustness of the support package process when collecting the shard configuration.\n\n- RS65110 - Fixed firewalld SELinux configuration.\n\n- RS101691 - Clean false error messages at the end of installation."},{"id":"known-limitations","title":"Known limitations","role":"content","text":""},{"id":"feature-limitations","title":"Feature limitations","role":"content","text":"- RS78430 - When [tuning module arguments](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-db), typos or unsupported arguments can cause shards to get stuck after restarting.\n\n- RS40641 - API requests are redirected to an internal IP in case the request arrives from a node which is not the master. To avoid this issue, use [`rladmin cluster config`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/config) to configure `handle_redirects` or `handle_metrics_redirects`."},{"id":"operating-system-limitations","title":"Operating system limitations","role":"content","text":"#### Ubuntu 20.04\n\nBy default, you cannot use the SHA1 hash algorithm ([OpenSSL’s default security level is set to 2](https://manpages.ubuntu.com/manpages/focal/man3/SSL_CTX_set_security_level.3ssl.html#notes)). The operating system will reject SHA1 certificates even if the `mtls_allow_weak_hashing` option is enabled. You need to replace SHA1 certificates with newer certificates that use SHA-256. Note that the certificates provided with Redis Enterprise Software use SHA-256."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-94","title":"Redis Enterprise Software release notes 6.4.2-94 (July 2023)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-94/","summary":"Look-ahead mechanism for planner attempts. Package OS compatibility validation.","tags":["docs","operate","rs"],"last_updated":"2026-10-06T08:53:14-07:00","page_type":"content","content_hash":"d96bcc060f955a62ba0b0c18c495651ecdc5c62d30db0c2db51c037657073729","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Enterprise Software version 6.4.2](https://redis.io/downloads/#software).\n\nThe following table shows the MD5 checksums for the available packages:\n\n| Package | MD5 checksum (6.4.2-94 July release) |\n|---------|---------------------------------------|\n| Ubuntu 16 | e664a93108aeb72cf9dc849b84b79239 |\n| Ubuntu 18 | 9b547be7e093a0668caa9d5af36ddd21 |\n| Ubuntu 20 | d623c8458a28f09a1e6ae0c0a8650023 |\n| RedHat Enterprise Linux (RHEL) 7<br/>Oracle Enterprise Linux (OL) 7 | f9df1080427660bab601f1690ee7ffca |\n| RedHat Enterprise Linux (RHEL) 8<br/>Oracle Enterprise Linux (OL) 8 <br/>Rocky Enterprise Linux | f44dfaf4da5d54d35d739ba1e243eca0 |\n| Amazon Linux 2 | f2c5c2b5e879b8fd6792336f757593e2 |"},{"id":"new-features-and-enhancements","title":"New features and enhancements","role":"content","text":"- RS98361 - Enhanced the algorithm responsible for placing shards in the cluster. This improvement applies when creating new databases and when calling the REST API `GET optimize_shards_placement` for existing databases.\n\n  With this update, the algorithm efficiently finds a legal placement while reducing the calculation time significantly. These changes ensure a smoother and faster experience when managing databases with a large number of shards and dense shard placement policy.\n\n- RS62197 - Added validation of the operating system (OS) version before installation. The installer automatically validates the OS type and version and verifies the correct binary is used for the corresponding system. This prevents missing dependencies which may cause issues later on.\n\n  The validation applies to major versions of all operating systems. If the check fails, the installation process halts with an appropriate error message.\n\n\n#### Redis Stack v6.2.6\n\nRedis Enterprise Software v6.4.2 includes the new features delivered in the latest [Redis Stack release (6.2.6 v7)](https://redis.com/blog/introducing-redis-stack-6-2-6-and-7-0-6/):\n\n- [RediSearch v2.6.9](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)\n\n- [RedisJSON v2.4.7](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)\n\n- [RedisBloom v2.4.5](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)\n\n- [RedisGraph v2.10.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes)\n\n- [RedisTimeSeries v1.8.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)\n\nSee [Upgrade modules](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/upgrade-module) to learn how to upgrade a module for a database."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS100903 - Fixed a v6.4.2 issue: when a database client requests [`SLOWLOG GET [count]`](https://redis.io/docs/latest/commands/slowlog-get), the proxy ignores \"count\" and always replies with 128 records\n\n- RS95394 - Fixed Replica Of sync causing high DMC memory usage on source cluster when TLS is enabled \"for Replica Of Only\".\n\n- RS102905 - Fixed invalid character that could potentially cause failure in generating support package for Active-Active databases.\n\n- RS92430 - Optimized internal certification rotation process benefiting mainly clusters.\n\n- RS99495 - Updated UI title property to \"Redis Enterprise\""}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-2-4-releases","title":"Redis Enterprise Software release notes 7.2.4","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/","summary":"Redis 7.0 and 7.2 features. Auto Tiering (enhanced successor to Redis on Flash). RESP3 support. Sharded pub/sub. Preview of the new Cluster Manager UI. Redis Stack 7.2 features. Three Redis database versions. License file structure updates. Redis ACL selectors and enhanced key-based permissions. New INFO fields. Log rotation enhancements. Multi-OS upgrade support for clusters with modules.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","children":[{"id":"operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-154","summary":"Internal fixes and improvements.","title":"Redis Software release notes 7.2.4-154 (July 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-154/"},{"id":"operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-153","summary":"Internal fixes and improvements.","title":"Redis Enterprise Software release notes 7.2.4-153 (April 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-153/"},{"id":"operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-138","summary":"Internal fixes and improvements.","title":"Redis Enterprise Software release notes 7.2.4-138 (September 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-138/"},{"id":"operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-132","summary":"Bug fix for mismatched key counts in Active-Active databases after cluster recovery.","title":"Redis Enterprise Software release notes 7.2.4-132 (June 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-132/"},{"id":"operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-130","summary":"Retry mechanism for listener creation on database ports. Bug fix for Lua out-of-memory errors preventing scripts from running Redis commands.","title":"Redis Enterprise Software release notes 7.2.4-130 (May 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-130/"},{"id":"operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-122","summary":"Internal fixes and improvements.","title":"Redis Enterprise Software release notes 7.2.4-122 (March 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-122/"},{"id":"operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-109","summary":"Bug fixes.","title":"Redis Enterprise Software release notes 7.2.4-109 (May 2024)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-109/"},{"id":"operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-105","summary":"LDAP authentication timeout is configurable using the REST API. RHEL 8.9 support.","title":"Redis Enterprise Software release notes 7.2.4-105 (February 2024)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-105/"},{"id":"operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-92","summary":"New Cluster Manager UI enhancements - LDAP configuration improvements. New `capability_name` field added to module REST API object. Automatic removal of deprecated, predefined roles and ACLs upon cluster upgrade, unless they are associated with any users or databases.","title":"Redis Enterprise Software release notes 7.2.4-92 (November 2023)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-92/"},{"id":"operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-72","summary":"New Cluster Manager UI enhancements - database upgrade configuration, database defaults, user lockout parameters, and TLS configuration improvements.","title":"Redis Enterprise Software release notes 7.2.4-72 (October 2023)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-72/"},{"id":"operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-64","summary":"Improved cluster recovery with manually uploaded modules. Support package contains supervisorctl status. Configure port range using rladmin and REST API. License API returns the number of used shards (RAM & flash).","title":"Redis Enterprise Software release notes 7.2.4-64 (September 2023)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-64/"},{"id":"operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52","summary":"Redis 7.0 and 7.2 features. Auto Tiering (enhanced successor to Redis on Flash). RESP3 support. Sharded pub/sub. Preview of the new Cluster Manager UI. Redis Stack 7.2 features. Three Redis database versions. License file structure updates. Redis ACL selectors and enhanced key-based permissions. New INFO fields. Log rotation enhancements. Multi-OS upgrade support for clusters with modules.","title":"Redis Enterprise Software release notes 7.2.4-52 (August 2023)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52/"}],"page_type":"content","content_hash":"e2ec22574e07465b3bf96c8595df6979b85d71d352996944ba880a9b23a7db59","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!WARNING]\n> Redis Software 7.2 reached end of life on February 28, 2026. It no longer\n> receives security patches, bug fixes, or maintenance releases.\n> Support for 7.2-specific issues is limited per your subscription agreement,\n> and you may be asked to upgrade before an issue can be investigated.\n>\n> Upgrade to a supported version. See the\n> [supported upgrade paths](https://redis.io/docs/latest/operate/rs/references/upgrade-paths)\n> and the [Redis Software product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule).\n\n​[​Redis Enterprise Software version 7.2.4](https://redis.io/downloads/#software) is now available!"},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n \n- Redis 7.0 and 7.2 features\n\n- Auto Tiering (enhanced successor to Redis on Flash)\n\n- RESP3 support\n\n- Sharded pub/sub\n\n- A preview of the new Cluster Manager UI (admin console)\n\n- Redis Stack 7.2 features\n\n- Three Redis database versions: 7.2, 6.2, 6.0\n\n- License file structure updates\n\n- Redis ACL selectors and enhanced key-based permissions\n\n- New INFO fields\n\n- Log rotation enhancements\n\n- Multi-OS upgrade support for clusters with modules"},{"id":"detailed-release-notes","title":"Detailed release notes","role":"content","text":"For more detailed release notes, select a build version from the following table:\n\n\n| Version (Release date) | Major changes | OSS Redis compatibility |\n| --- | --- | --- |\n| [7.2.4-154 (July 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-154/) | Internal fixes and improvements. | Redis 7.2.0, 6.2.13, 6.0.20 |\n| [7.2.4-153 (April 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-153/) | Internal fixes and improvements. | Redis 7.2.0, 6.2.13, 6.0.20 |\n| [7.2.4-138 (September 2025)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-138/) | Internal fixes and improvements. | Redis 7.2.0, 6.2.13, 6.0.20 |\n| [7.2.4-132 (June 2025)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-132/) | Bug fix for mismatched key counts in Active-Active databases after cluster recovery. | Redis 7.2.0, 6.2.13, 6.0.20 |\n| [7.2.4-130 (May 2025)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-130/) | Retry mechanism for listener creation on database ports. Bug fix for Lua out-of-memory errors preventing scripts from running Redis commands. | Redis 7.2.0, 6.2.13, 6.0.20 |\n| [7.2.4-122 (March 2025)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-122/) | Internal fixes and improvements. | Redis 7.2.0, 6.2.13, 6.0.20 |\n| [7.2.4-109 (May 2024)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-109/) | Bug fixes. | Redis 7.2.0, 6.2.13, 6.0.20 |\n| [7.2.4-105 (February 2024)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-105/) | LDAP authentication timeout is configurable using the REST API. RHEL 8.9 support. | Redis 7.2.0, 6.2.13, 6.0.20 |\n| [7.2.4-92 (November 2023)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-92/) | New Cluster Manager UI enhancements - LDAP configuration improvements. New `capability_name` field added to module REST API object. Automatic removal of deprecated, predefined roles and ACLs upon cluster upgrade, unless they are associated with any users or databases. | Redis 7.2.0, 6.2.13, 6.0.20 |\n| [7.2.4-72 (October 2023)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-72/) | New Cluster Manager UI enhancements - database upgrade configuration, database defaults, user lockout parameters, and TLS configuration improvements. | Redis 7.2.0, 6.2.13, 6.0.20 |\n| [7.2.4-64 (September 2023)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-64/) | Improved cluster recovery with manually uploaded modules. Support package contains supervisorctl status. Configure port range using rladmin and REST API. License API returns the number of used shards (RAM & flash). | Redis 7.2.0, 6.2.13, 6.0.20 |\n| [7.2.4-52 (August 2023)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52/) | Redis 7.0 and 7.2 features. Auto Tiering (enhanced successor to Redis on Flash). RESP3 support. Sharded pub/sub. Preview of the new Cluster Manager UI. Redis Stack 7.2 features. Three Redis database versions. License file structure updates. Redis ACL selectors and enhanced key-based permissions. New INFO fields. Log rotation enhancements. Multi-OS upgrade support for clusters with modules. | Redis 7.2.0, 6.2.13, 6.0.20 |"},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"For a list of potentially breaking changes introduced in version 7.2, see:\n\n- [Breaking changes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52#breaking-changes)\n\n- [Redis 7.2 breaking changes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52#redis-72-breaking-changes)\n\nTo prevent potential application issues due to RESP3 breaking changes, see [Client prerequisites for Redis 7.2 upgrade](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52#client-prerequisites-for-redis-72-upgrade)."},{"id":"deprecations","title":"Deprecations","role":"content","text":"#### Command deprecations\n\n- [`CLUSTER SLOTS`](https://redis.io/docs/latest/commands/cluster-slots) is deprecated as of Redis 7.0\n\n- [`JSON.RESP`](https://redis.io/docs/latest/commands/json.resp) is deprecated as of Redis Stack 7.2.\n\n- [`QUIT`](https://redis.io/docs/latest/commands/quit) is deprecated as of Redis 7.2\n\n#### API deprecations\n\nFields deprecated as of Redis Enterprise v4.3.3:\n\n- `smtp_use_tls` (replaced with `smtp_tls_mode`)\n\n- `dns_address_master`\n\n- `endpoint_node`\n\n- `endpoint_ip`\n\n- `public_addr` (replaced with `external_addr`)\n\nFields deprecated as of Redis Enterprise v4.4.2:\n\n- `default_shards_overbooking` (replaced with `shards_overbooking`)\n\nFields deprecated as of Redis Enterprise v6.4.2:\n\n- `use_ipv6` (replaced with `use_external_ipv6`)\n\n- `redis_cleanup_job_settings` (replaced with `persistence_cleanup_scan_interval`)\n\nFields deprecated as of Redis Enterprise v5.0.1:\n\n- `bdb_high_syncer_lag` (replaced with `replica_src_high_syncer_lag` and `crdt_src_high_syncer_lag`)\n\n- `bdb_syncer_connection_error`\n\n- `bdb_syncer_general_error`\n\n- `sync_sources` (replaced with `replica_sources` and `crdt_sources`)\n\n- `sync` (replaced with `replica_sync` and `crdt_sync`)\n\n- `ssl` (replaced with `tls_mode`)\n\nFields deprecated as of Redis Enterprise v7.2.4:\n\n- `node.bigstore_driver` (replaced with `cluster.bigstore_driver`)\n\n- `auth_method`\n\n- `authentication_redis_pass` (replaced with multiple passwords feature in version 6.0.X)\n\n- `slave_ha` cluster policy\n\nOther deprecated fields:\n\n- `import/rdb_url` (deprecated as of Redis Enterprise v4.X)\n\n- `logrotate_dir` (to be replaced with `logrotate_config` or removed)\n\nDeprecated CLI commands:\n\n- `rlutil change_master` (deprecated as of Redis Enterprise v6.2.18, replaced with `rladmin change_master`)\n\n- `rlutil reserved_ports` (deprecated as of Redis Enterprise v7.2.4, replaced with `rladmin cluster config reserved_ports`)\n\nREST API requests deprecated as of Redis Enterprise v7.2.4:\n\n- `POST /v1/modules` (replaced with `POST /v2/modules`)\n\n- `DELETE /v1/modules` (replaced with `DELETE /v2/modules`)\n\n#### Access control deprecations\n\n- The following predefined roles and Redis ACLs are not available after upgrading to Redis Enterprise Software version 7.2.4 if they are not associated with any users or databases in the cluster:\n\n    - Custom roles (not management roles): Cluster Member, Cluster Viewer, DB Member, DB Viewer, None.\n\n    - Redis ACLs: Not Dangerous and Read Only.\n\n- A deprecation notice for SASL-based LDAP was included in [previous Redis Enterprise Software release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-4-august-2021#deprecation-notices). When you upgrade to Redis Enterprise Software version 7.2.4, all existing \"external\" users (previously used to support SASL-based LDAP) will be removed.\n\n#### Legacy UI\n\nWith the release of the new Cluster Manager UI, the legacy UI is considered deprecated and will eventually be phased out. New functionality will only be implemented in the new Cluster Manager UI, and the old UI will no longer be maintained except for critical bug fixes.\n\n#### RedisGraph\n\nRedis has announced the end of life for RedisGraph. Redis will continue to support all RedisGraph customers, including releasing patch versions until January 31, 2025.\n\nSee the [RedisGraph end-of-life announcement](https://redis.com/blog/redisgraph-eol/) for more details.\n\n#### RHEL and CentOS 7.0-7.9\n\nSupport for RHEL and CentOS 7.0-7.9 is considered deprecated and will be removed in a future release.\n\n#### Oracle Linux 7\n\nOracle Linux 7 support is considered deprecated and will be removed in a future release.\n\n#### Amazon Linux 1\n\nAmazon Linux 1 support is considered deprecated and will be removed in a future release.\n\n#### Ubuntu 16.04\n\nThe deprecation of Ubuntu 16.04 was announced in the [Redis Enterprise Software 6.4.2 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases#deprecations). As of Redis Enterprise Software 7.2.4, Ubuntu 16.04 is no longer supported.\n\n#### RC4 encryption cipher\n\nThe RC4 encryption cipher is considered deprecated in favor of stronger ciphers. Support for RC4 by the [discovery service](https://redis.io/docs/latest/operate/rs/databases/durability-ha/discovery-service) will be removed in a future release.\n\n#### 3DES encryption cipher\n\nThe 3DES encryption cipher is considered deprecated in favor of stronger ciphers like AES.\nPlease verify that all clients, apps, and connections support the AES cipher. Support for 3DES will be removed in a future release.\nCertain operating systems, such as RHEL 8, have already removed support for 3DES. Redis Enterprise Software cannot support cipher suites that are not supported by the underlying operating system.\n\n#### TLS 1.0 and TLS 1.1\n\nTLS 1.0 and TLS 1.1 connections are considered deprecated in favor of TLS 1.2 or later.\nPlease verify that all clients, apps, and connections support TLS 1.2. Support for the earlier protocols will be removed in a future release.\nCertain operating systems, such as RHEL 8, have already removed support for the earlier protocols. Redis Enterprise Software cannot support connection protocols that are not supported by the underlying operating system."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Enterprise Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Enterprise Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Enterprise Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Enterprise Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS114185 - During an upgrade to [Redis Enterprise Software version 7.2.4-86](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-92), the proxy might not start due to a `Failed to get default_suffix` error, which appears in `dmcproxy.log`.\n\n    As a workaround, start `dmcproxy` manually:\n\n    [code example]\n\n    This issue was fixed in [Redis Enterprise Software version 7.2.4-92](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-92).\n\n\n- RS123142 - In an Active-Active setup with at least three participating clusters, removing and re-adding a cluster after removing older clusters without re-adding them can cause missing keys and potentially lead to data loss or data inconsistency.\n\n    This issue will be fixed in a future maintenance release. To prevent this issue, avoid adding clusters until you upgrade to the upcoming maintenance release when available."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Command limitations\n\n- [`CLIENT NO-TOUCH`](https://redis.io/docs/latest/commands/client-no-touch) might not run correctly in the following cases:\n\n    - The Redis database version is earlier than 7.2.0.\n\n    - The `CLIENT NO-TOUCH` command is forbidden by ACL rules.\n\n    Before sending this command, clients should verify the database version is 7.2.0 or later and that using this command is allowed. \n\n- You cannot use [`SUNSUBSCRIBE`](https://redis.io/docs/latest/commands/sunsubscribe) to unsubscribe from a shard channel if the regex changed while subscribed.\n\n- Using [`XREADGROUP BLOCK`](https://redis.io/docs/latest/commands/xreadgroup) with `>` to return all new streams will cause the Redis database to freeze until the shard is restarted. ([#12031](https://github.com/redis/redis/pull/12301))\n\n- Because a rejected command does not record the duration for command stats, an error will appear after it is reprocessed that will cause the Redis database to freeze until the shard is restarted. ([#12247](https://github.com/redis/redis/pull/12247))\n\n#### Modules cannot load in Oracle Linux 7 & 8\n\nDatabases hosted on Oracle Linux 7 & 8 cannot load modules.\n\nAs a temporary workaround, you can change the node's `os_name` in the Cluster Configuration Store (CCS):\n\n[code example]\n\nThis limitation was fixed in [Redis Enterprise Software version 7.2.4-64](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-64).\n\n#### Cluster recovery with manually uploaded modules\n\nFor clusters containing databases with manually uploaded modules, [cluster recovery](https://redis.io/docs/latest/operate/rs/clusters/cluster-recovery) requires an extra step.\n\nAfter installing Redis Enterprise Software on the cluster nodes, upload compatible modules to `modulesdir` (`/opt/redislabs/lib/modules`) before continuing the recovery process.\n\nThis limitation was fixed in [Redis Enterprise Software version 7.2.4-64](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-64).\n\n#### Cannot create Redis v6.x Active-Active databases with modules\n\nYou cannot create Active-Active databases that use Redis version 6.0 or 6.2 with modules. Databases that use Redis version 7.2 do not have this limitation.\n\nThis limitation will be fixed in a maintenance release for Redis Enterprise version 7.4.2.\n\n#### Modules prevent nodes from joining upgraded clusters on RHEL clones\n\nIf you upgrade an existing cluster to Redis Enterprise Software version 7.2.4 and any existing databases use earlier module versions, new nodes will fail to join the cluster on operating systems that are RHEL clones. RHEL, Ubuntu, and Amazon Linux are not affected by this issue.\n\nFor a workaround for affected operating systems, [contact support](https://redis.io/support/).\n\nThis limitation will be fixed in a future release."}],"examples":[{"id":"known-issues-ex0","language":"sh","code":"supervisorctl restart dmcproxy","section_id":"known-issues"},{"id":"known-limitations-ex0","language":"sh","code":"ccs-cli hset node:<ID> os_name rhel","section_id":"known-limitations"}]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-105","title":"Redis Enterprise Software release notes 7.2.4-105 (February 2024)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-105/","summary":"LDAP authentication timeout is configurable using the REST API. RHEL 8.9 support.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"d238de62f6ac21609ced1ea7a622174dfe1ad7320a3bf4b0edc745b9f46e325e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Enterprise Software version 7.2.4](https://redis.io/downloads/#software)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Configurable LDAP authentication timeout\n\n- RHEL 8.9 support"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"enhancements","title":"Enhancements","role":"content","text":"- LDAP authentication timeout `directory_timeout_s` is now configurable using a [`PUT /v1/cluster/ldap`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/ldap#put-cluster-ldap) REST API request.\n\n- Added support for RHEL 8.9.\n\n#### Redis modules\n\nRedis Enterprise Software versions 7.2.4-105 includes the following Redis Stack modules:\n\n- [RediSearch 2.8.9](https://github.com/RediSearch/RediSearch/releases/tag/v2.8.9)\n\n- [RedisJSON 2.6.6](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.6.6)\n\n- [RedisTimeSeries 1.10.6](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.10.6)\n\n- [RedisBloom 2.6.11](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.6.11)\n\n- [RedisGears 2.0.17](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.17-m18)"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS106070: Cluster upgrade will fail and display an error to the user after a timeout is exceeded.\n\n- RS107974: Increased retry count for join requests in case the local CCS isn’t available.\n\n- RS118229: Fixed an issue in the new Cluster Manager UI where a database could not be scaled up to use the maximum shards available for a trial license."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Enterprise Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Enterprise Software.\n\n<span title=\"Warning icon\">&#x26A0;&#xFE0F;</span> Deprecated – The platform is still supported for this version of Redis Enterprise Software, but support will be removed in a future release.\n\n<span title=\"X icon\">&#x274c;</span> End of life – Platform support ended in this version of Redis Enterprise Software.\n\n| Redis Enterprise | 7.2.4 | 6.4.2 | 6.2.18 | 6.2.12 | 6.2.10 | 6.2.8 | 6.2.4 |\n|------------------|-------|-------|--------|--------|--------|--------|-------|\n| **Release date** | Aug<br />2023 | Feb<br />2023 | Sept<br />2022 | Aug<br />2022 | Feb<br />2022 | Oct<br />2021 | Aug<br />2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | – | Feb<br />2025 | Aug<br />2024 | Aug<br />2024 | Aug<br />2024 | Aug<br />2024 | Aug<br />2024 |\n| **Ubuntu**<sup>[1](#table-note-1)</sup> |\n| 20.04 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span><sup>[6](#table-note-6)</sup> | – | – | – | – | – |\n| 18.04 | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\"><span title=\"Supported\">&#x2705;</span></span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 16.04 | <span title=\"End of life\">&#x274c;</span> | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **RHEL & CentOS**<sup>[2](#table-note-2)</sup>\n| 8.9| <span title=\"Supported\">&#x2705;</span></span><sup>[9](#table-note-9)</sup> | – | – | – | – | – | – |\n| 8.8 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span><sup>[8](#table-note-8)</sup> | – | – | – | – | – |\n| 8.7 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – | – |\n| 8.5-8.6 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – |\n| 8.0-8.4 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| 7.0-7.9 | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Oracle Linux**<sup>[3](#table-note-3)</sup> |\n| 8 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – |\n| 7 | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Rocky Linux**<sup>[3](#table-note-3)</sup> |\n| 8 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| **Amazon Linux** |\n| 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span><sup>[7](#table-note-7)</sup> | – | – | – | – | – |\n| 1 | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Docker**<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Kubernetes**<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n2. <a name=\"table-note-2\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>RHEL and CentOS deployments require OpenSSL 1.0.2 and [firewall configuration](https://redis.io/docs/latest/operate/rs/installing-upgrading/configuring/centos-rhel-firewall).\n\n3. <a name=\"table-note-3\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>Based on the corresponding RHEL version.\n\n4. <a name=\"table-note-4\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>\n[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Enterprise Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes).\n\n6. <a name=\"table-note-6\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>Ubuntu 20.04 support was added in Redis Enterprise Software [6.4.2-43](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-43).\n\n7. <a name=\"table-note-7\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>A release candidate for Amazon Linux 2 support was added in Redis Enterprise Software [6.4.2-61](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-61). Official support for Amazon Linux 2 was added in Redis Enterprise Software [6.4.2-69](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-69).\n\n8. <a name=\"table-note-8\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>Redis Enterprise Software [6.4.2-103](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-103) and later supports RHEL 8.8.\n\n9. <a name=\"table-note-9\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>Redis Enterprise Software [7.2.4-105](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-105) and later supports RHEL 8.9."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the MD5 checksums for the available packages:\n\n| Package | MD5 checksum (7.2.4-105 February release) |\n|---------|---------------------------------------|\n| Ubuntu 18 | 8d78258f76d0968437c0b6bc4e1c5e12 |\n| Ubuntu 20 | 48bcb267f6d154989726b556e93f64aa |\n| RedHat Enterprise Linux (RHEL) 7<br/>Oracle Enterprise Linux (OL) 7 | 2d5ad0090de6f5ef513cbf4e07da9ed5 |\n| RedHat Enterprise Linux (RHEL) 8<br/>Oracle Enterprise Linux (OL) 8 <br/>Rocky Enterprise Linux | 974c6815bfd53bf74e349b2beae1780f |\n| Amazon Linux 2 | bf296f206da39830f3c4d8d46e2193c1 |"},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Enterprise Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Enterprise has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Enterprise due to different or additional functionality available in Redis Enterprise that is not available in open source Redis.\n\nRedis Enterprise 7.2.4-105 supports open source Redis 7.2, 6.2, and 6.0. Below is the list of open source Redis CVEs fixed by version.\n\nRedis 7.2.x:\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` command can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)\n\nRedis 6.0.x:\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.0.20)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.0.19)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.0.18)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 6.0.18)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.0.17)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-109","title":"Redis Enterprise Software release notes 7.2.4-109 (May 2024)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-109/","summary":"Bug fixes.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"7601c41aa2cf7123ed7767dd0a1e5fd7b8e0918ff7b07e4c144983a4d16e6b58","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Enterprise Software version 7.2.4](https://redis.io/downloads/#software)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Bug fixes"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"enhancements","title":"Enhancements","role":"content","text":"#### Redis modules\n\nRedis Enterprise Software versions 7.2.4-109 includes the following Redis Stack modules:\n\n- [RediSearch 2.8.9](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)\n\n- [RedisJSON 2.6.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)\n\n- [RedisTimeSeries 1.10.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)\n\n- [RedisBloom 2.6.11](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes)\n\n- [RedisGears 2.0.17](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.17-m18)"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS121673: Search commands no longer return an error after fixing incorrect configuration.\n\n- RS123237: CRDT syncer prevents potential data mismatches or data loss due to missing keys when removing and re-adding a cluster in an Active-Active setup with at least three participating clusters after removing older clusters without re-adding them.\n\n- RS122221: `redis-server` creates PID file only after binding to a socket."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Enterprise Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Enterprise Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Enterprise Software, but support will be removed in a future release.\n\n| Redis Enterprise<br />major versions | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | July 2025 | Feb 2025 | Aug 2024 |\n| **Platforms** | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>\n[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Enterprise Software are certified for development and testing only."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the MD5 checksums for the available packages:\n\n| Package | MD5 checksum (7.2.4-109 May release) |\n|---------|---------------------------------------|\n| Ubuntu 18 | ae041ffa0eee0652afe69767683a64e4 |\n| Ubuntu 20 | 9e33c630f357a657eed8f084eec70d8d |\n| Red Hat Enterprise Linux (RHEL) 7 | 8f3c56ad8ae2a790a31e2253eaaa1ccc |\n| Red Hat Enterprise Linux (RHEL) 8 | 974c6815bfd53bf74e349b2beae1780f |\n| Amazon Linux 2 | 70c7a8560ee96d72065132e77cb79fe0 |"},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Enterprise Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Enterprise has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Enterprise due to different or additional functionality available in Redis Enterprise that is not available in open source Redis.\n\nRedis Enterprise 7.2.4-109 supports open source Redis 7.2, 6.2, and 6.0. Below is the list of open source Redis CVEs fixed by version.\n\nRedis 7.2.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` command can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-45145) The wrong order of listen(2) and chmod(2) calls creates a\nrace condition that can be used by another process to bypass desired Unix\nsocket permissions on startup. (Redis 6.2.14)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)\n\nRedis 6.0.x:\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.0.20)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.0.19)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.0.18)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 6.0.18)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.0.17)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-122","title":"Redis Enterprise Software release notes 7.2.4-122 (March 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-122/","summary":"Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"75d8fac0035b197035b48093878bbdc358f8dcf15d847f17193c27ce9cf33511","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Enterprise Software version 7.2.4](https://redis.io/downloads/#software)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":"#### Redis modules\n\nRedis Enterprise Software versions 7.2.4-122 includes the following Redis Stack modules:\n\n- [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)\n\n- [RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)\n\n- [RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)\n\n- [RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes)\n\n- [RedisGears 2.0](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.17-m18)"},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the MD5 checksums for the available packages:\n\n| Package | MD5 checksum (7.2.4-122 March release) |\n|---------|---------------------------------------|\n| Ubuntu 18 | ce855ba32bce9c9b1880e3a604d7c722 |\n| Ubuntu 20 | 74fed091eda2241fae06456b3aef2cbf |\n| Red Hat Enterprise Linux (RHEL) 7 | f2c5538b109397eb8cea293924e6ba70 |\n| Red Hat Enterprise Linux (RHEL) 8 | 21660961b8810aa82bdee5ef00f8be82 |\n| Amazon Linux 2 | 0060c27b44dd5f5a1e5037a8b415378f |"},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Enterprise Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Enterprise has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Enterprise due to different or additional functionality available in Redis Enterprise that is not available in open source Redis.\n\nRedis Enterprise 7.2.4-122 supports open source Redis 7.2, 6.2, and 6.0. Below is the list of open source Redis CVEs fixed by version.\n\nRedis 7.2.x:\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` command can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-45145) The wrong order of listen(2) and chmod(2) calls creates a\nrace condition that can be used by another process to bypass desired Unix\nsocket permissions on startup. (Redis 6.2.14)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)\n\nRedis 6.0.x:\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.0.20)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.0.19)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.0.18)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 6.0.18)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.0.17)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-130","title":"Redis Enterprise Software release notes 7.2.4-130 (May 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-130/","summary":"Retry mechanism for listener creation on database ports. Bug fix for Lua out-of-memory errors preventing scripts from running Redis commands.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"78093fbd96c8c84e787c9683ebdafad87d68019ffb422e0f8c374403672ad1bc","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Enterprise Software version 7.2.4](https://redis.io/downloads/#software)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Retry mechanism for listener creation on database ports\n\n- Bug fix for Lua out-of-memory errors preventing scripts from running Redis commands"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-modules","title":"Redis modules","role":"content","text":"Redis Enterprise Software versions 7.2.4-130 includes the following Redis Stack modules:\n\n- [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)\n\n- [RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)\n\n- [RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)\n\n- [RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes)\n\n- [RedisGears 2.0](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.17-m18)"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS150746: Added a retry mechanism in case the DMC fails to create a listener on a database port.\n\n- RS132033: Fixed an issue where out-of-memory errors in the Lua interpreter prevented scripts from running Redis commands until the shard was restarted. This fix is included in Redis database version 7.2 and requires a database upgrade from earlier versions."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Enterprise Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Enterprise Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span>| <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Enterprise Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the MD5 checksums for the available packages:\n\n| Package | MD5 checksum (7.2.4-130 May release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | 121004b2795ed37d8cb97c4bc6bbbe2b |\n| Red Hat Enterprise Linux (RHEL) 7 | b9fcb3b7611cbfa33ae79f6cfff4ac1b |\n| Red Hat Enterprise Linux (RHEL) 8 | e61d9bcac8dbde4ca4ef69d5621c31e6 |\n| Amazon Linux 2 | a0221fac6aaa2a114cc4c096dc0ae154 |"},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Enterprise Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Enterprise has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Enterprise due to different or additional functionality available in Redis Enterprise that is not available in open source Redis.\n\nRedis Enterprise 7.2.4-130 supports open source Redis 7.2, 6.2, and 6.0. Below is the list of open source Redis CVEs fixed by version.\n\nRedis 7.2.x:\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` command can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-45145) The wrong order of listen(2) and chmod(2) calls creates a\nrace condition that can be used by another process to bypass desired Unix\nsocket permissions on startup. (Redis 6.2.14)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)\n\nRedis 6.0.x:\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.0.20)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.0.19)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.0.18)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 6.0.18)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.0.17)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-132","title":"Redis Enterprise Software release notes 7.2.4-132 (June 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-132/","summary":"Bug fix for mismatched key counts in Active-Active databases after cluster recovery.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"49664a1c4b1c02fb1991dbcb2c9aa9260574253b79fbd2315a11c5896d4a9c6b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​​Redis Enterprise Software version 7.2.4."},{"id":"highlights","title":"Highlights","role":"content","text":"This maintenance release offers:\n\n- Bug fix for mismatched key counts in Active-Active databases after cluster recovery"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-modules","title":"Redis modules","role":"content","text":"Redis Enterprise Software versions 7.2.4-132 includes the following Redis Stack modules:\n\n- [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)\n\n- [RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)\n\n- [RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)\n\n- [RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes)\n\n- [RedisGears 2.0](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.17-m18)"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS140077: Fixed a syncer issue where the number of keys did not match across the instances of Active-Active databases reconfigured with `only_configuration` after cluster recovery."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Enterprise Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Enterprise Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span>| <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Enterprise Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the MD5 checksums for the available packages:\n\n| Package | MD5 checksum (7.2.4-132 June release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | 8a054599723f6c5a90a30f3a50f5d750 |\n| Red Hat Enterprise Linux (RHEL) 7 | cc32052a167ec65f8f8e7906279707dc |\n| Red Hat Enterprise Linux (RHEL) 8 | 8e235cf780b34cb87db7d30353e3867a |\n| Amazon Linux 2 | c8bdf408a86631064d4b920c7d80cd02 |"},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Enterprise Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Enterprise has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Enterprise due to different or additional functionality available in Redis Enterprise that is not available in open source Redis.\n\nRedis Enterprise 7.2.4-132 supports open source Redis 7.2, 6.2, and 6.0. Below is the list of open source Redis CVEs fixed by version.\n\nRedis 7.2.x:\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` command can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-45145) The wrong order of listen(2) and chmod(2) calls creates a\nrace condition that can be used by another process to bypass desired Unix\nsocket permissions on startup. (Redis 6.2.14)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)\n\nRedis 6.0.x:\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.0.20)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.0.19)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.0.18)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 6.0.18)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.0.17)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-138","title":"Redis Enterprise Software release notes 7.2.4-138 (September 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-138/","summary":"Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"4dd4a50d0ce20844397e7b68ce1e086a9b61b1e951f644844d828d612861c967","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​​Redis Enterprise Software version 7.2.4."},{"id":"highlights","title":"Highlights","role":"content","text":"This maintenance release offers:\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-modules","title":"Redis modules","role":"content","text":"Redis Enterprise Software versions 7.2.4-138 includes the following Redis Stack modules:\n\n- [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)\n\n- [RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)\n\n- [RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)\n\n- [RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes)\n\n- [RedisGears 2.0](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.17-m18)"},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Enterprise Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Enterprise Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Enterprise Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the MD5 checksums for the available packages:\n\n| Package | MD5 checksum (7.2.4-138 September release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | 103ce2200ae023fe79ae0fd382c86ca4 |\n| Red Hat Enterprise Linux (RHEL) 8 | ce97caa8e84945a449a34a50197dcf1a |\n| Amazon Linux 2 | 1a63e5cea1f06594413932668b29e0ba |"},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Enterprise Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Enterprise has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Enterprise due to different or additional functionality available in Redis Enterprise that is not available in open source Redis.\n\nRedis Enterprise 7.2.4-138 supports open source Redis 7.2, 6.2, and 6.0. Below is the list of open source Redis CVEs and other security vulnerabilities fixed by version.\n\nRedis 7.2.x:\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` command can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-45145) The wrong order of listen(2) and chmod(2) calls creates a\nrace condition that can be used by another process to bypass desired Unix\nsocket permissions on startup. (Redis 6.2.14)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)\n\nRedis 6.0.x:\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.0.20)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.0.19)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.0.18)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 6.0.18)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.0.17)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-153","title":"Redis Enterprise Software release notes 7.2.4-153 (April 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-153/","summary":"Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"ae9411cd103d3d1804c02f0f6b11bcac52c99850ef840ce3a5a3756477d54a8b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​​Redis Enterprise Software version 7.2.4."},{"id":"highlights","title":"Highlights","role":"content","text":"This maintenance release offers:\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-modules","title":"Redis modules","role":"content","text":"Redis Enterprise Software versions 7.2.4-153 includes the following Redis Stack modules:\n\n- [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)\n\n- [RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)\n\n- [RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)\n\n- [RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes)\n\n- [RedisGears 2.0](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.17-m18)"},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the MD5 checksums for the available packages:\n\n| Package | MD5 checksum (7.2.4-153 April release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | 87dd925c91ff375f67431589fd61ea1e |\n| Red Hat Enterprise Linux (RHEL) 8 | 49b641207021e4cf3ae40f1923e7f791 |"},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Enterprise Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Enterprise has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Enterprise due to different or additional functionality available in Redis Enterprise that is not available in open source Redis.\n\nRedis Enterprise 7.2.4-153 supports open source Redis 7.2, 6.2, and 6.0. Below is the list of open source Redis CVEs and other security vulnerabilities fixed by version.\n\nRedis 7.2.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` command can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-45145) The wrong order of listen(2) and chmod(2) calls creates a\nrace condition that can be used by another process to bypass desired Unix\nsocket permissions on startup. (Redis 6.2.14)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)\n\nRedis 6.0.x:\n\n- RedisBloom: Restore invalid filter.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.0.20)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.0.19)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.0.18)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 6.0.18)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.0.17)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-154","title":"Redis Software release notes 7.2.4-154 (July 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-154/","summary":"Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"db6f7cb5781de6e447f51b703f137bfbecc626357e7bb417cd5e89c3d3a88215","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​​Redis Software version 7.2.4."},{"id":"highlights","title":"Highlights","role":"content","text":"This maintenance release offers:\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-modules","title":"Redis modules","role":"content","text":"Redis Software versions 7.2.4-154 includes the following Redis Stack modules:\n\n- [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)\n\n- [RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)\n\n- [RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)\n\n- [RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes)\n\n- [RedisGears 2.0](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.17-m18)"},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the MD5 checksums for the available packages:\n\n| Package | MD5 checksum (7.2.4-154 July release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | 49ca9b30deaee8df58a26b161d839927 |\n| Red Hat Enterprise Linux (RHEL) 8 | 025a8bbfbb063a2287c9859cc563970d |"},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Enterprise has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Enterprise due to different or additional functionality available in Redis Enterprise that is not available in open source Redis.\n\nRedis Enterprise 7.2.4-154 supports open source Redis 7.2, 6.2, and 6.0. Below is the list of open source Redis CVEs and other security vulnerabilities fixed by version.\n\n\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\n\n\n\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` command can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\n\n\n\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-45145) The wrong order of listen(2) and chmod(2) calls creates a\nrace condition that can be used by another process to bypass desired Unix\nsocket permissions on startup. (Redis 6.2.14)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)\n\n\n\n\n\n- RedisBloom: Restore invalid filter.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.0.20)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.0.19)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.0.18)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 6.0.18)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.0.17)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52","title":"Redis Enterprise Software release notes 7.2.4-52 (August 2023)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52/","summary":"Redis 7.0 and 7.2 features. Auto Tiering (enhanced successor to Redis on Flash). RESP3 support. Sharded pub/sub. Preview of the new Cluster Manager UI. Redis Stack 7.2 features. Three Redis database versions. License file structure updates. Redis ACL selectors and enhanced key-based permissions. New INFO fields. Log rotation enhancements. Multi-OS upgrade support for clusters with modules.","tags":["docs","operate","rs"],"last_updated":"2026-10-06T08:53:14-07:00","page_type":"content","content_hash":"92048e8bb02970be50ac2466ef7c0b733eab992f3bc3389388a052ee537e4a18","sections":[{"id":"overview","title":"Overview","role":"overview","text":"​[​Redis Enterprise Software version 7.2.4](https://redis.io/downloads/#software) is now available!"},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Redis 7.0 and 7.2 features\n\n- Auto Tiering (enhanced successor to Redis on Flash)\n\n- RESP3 support\n\n- Sharded pub/sub\n\n- A preview of the new Cluster Manager UI (admin console)\n\n- Redis Stack 7.2 features\n\n- Three Redis database versions: 7.2, 6.2, 6.0\n\n- License file structure updates\n\n- Redis ACL selectors and enhanced key-based permissions\n\n- New INFO fields\n\n- Log rotation enhancements\n\n- Multi-OS upgrade support for clusters with modules"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"new-features","title":"New features","role":"content","text":"#### Redis 7.0 features\n\nThe following Redis 7.0 features are now supported:\n\n- [Redis functions](https://redis.io/docs/latest/develop/programmability/functions-intro)\n\n    In Redis Enterprise Software, [`FUNCTION STATS`](https://redis.io/docs/latest/commands/function-stats) returns an extra parameter, an array called `all_running_scripts`, to reflect multiple functions running at the same time.\n\n- [Multipart AOF](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence#append-only-file) (append-only files)\n\n- New commands\n\n- Sharded `PUBSUB` (see [Sharded pub/sub](#sharded-pubsub) for details)\n\n#### Redis 7.2 features\n\nThe following Redis 7.2 features are now supported:\n\n- Various performance improvements\n\n- `CONFIG SET` for locale\n\n- Connection layer modularization\n\n- Encoding improvements: listpack for sets and lists\n\n- Observability: authentication metrics (exposed by `INFO` command)\n\n- Stream consumer group improvements\n\n- Commands: `ZRANK`, `ZREVRANK` new `WITHSCORE` option\n\n- Shard IDs in cluster shards topology\n\n- Introduce shard ID to Redis cluster\n\n- Support `CLIENT NO-TOUCH` command\n\n- `WAIT AOF`\n\n#### Auto Tiering - Redis on Flash evolution doubles throughput and reduces latency {#auto-tiering}\n\nRedis Enterprise version 7.2 introduces Auto Tiering as an enhanced successor to Redis on Flash, which allows you to provision larger databases at a lower cost by extending the RAM with flash drives.\n\nRedis Enterprise Auto Tiering replaces RocksDB with [Speedb](https://www.speedb.io/) as its storage engine, doubling the throughput and reducing latencies, achieved using the same infrastructure resources. For example, a 1 TB database with 50K ops/sec can now serve 100K ops/sec based on the same infrastructure.\n\nTo switch existing databases to use Speedb for Auto Tiering and improve performance:\n\n1. Upgrade the cluster to Redis Enterprise Software version 7.2.4.\n\n1. Upgrade each database with Auto Tiering enabled to Redis database version 7.2.\n\nFor more information about Auto Tiering, see:\n\n- [Auto Tiering overview](https://redis.io/docs/latest/operate/rs/databases/flash)\n\n- [Auto Tiering quick start](https://redis.io/docs/latest/operate/rs/databases/flash/quickstart)\n\n#### RESP3 support\n\nSupport for RESP3 and the [`HELLO`](https://redis.io/docs/latest/commands/hello) command was added in Redis Enterprise 7.2.\n\nTo use RESP3 with Redis Enterprise:\n\n1. Upgrade Redis servers to version 7.2 or later.\n\n    For Active-Active and Replica Of databases:\n \n    1. Upgrade all participating clusters to Redis Enterprise version 7.2.x or later.\n \n    1. Upgrade all databases to version 7.x or later.\n\n1. Enable RESP3 support for your database (`enabled` by default):\n\n    [code example]\n\nIf you run Redis Stack commands with Redis clients [Go-Redis](https://redis.uptrace.dev/) version 9 or [Lettuce](https://redis.github.io/lettuce/) versions 6 and later, see [client prerequisites](#client-prerequisites-for-redis-72-upgrade) before you upgrade to Redis 7.2 to learn how to prevent potential application issues due to RESP3 breaking changes.\n\n#### Sharded pub/sub\n\n[Sharded pub/sub](https://redis.io/docs/latest/develop/pubsub#sharded-pubsub) is now supported.\n\nYou cannot use sharded pub/sub if you [deactivate RESP3 support](https://redis.io/docs/latest/operate/rs/references/compatibility/resp#deactivate-resp3).\n\n#### New Cluster Manager UI preview\n\nA preview of the new Cluster Manager UI (admin console) is available in Redis Enterprise Software version 7.2.4.\n\nTo try out the new UI:\n\n- On the sign-in screen:\n\n    1. Enter your credentials.\n\n    1. Select **Sign in the new interface**.\n\n- Sign in directly from the new UI's sign-in screen at <nobr>`https://<hostname or IP address>:8443/new`</nobr>\n\n- If you are currently signed in to the legacy UI:\n\n    1. Select **Switch to the new Admin Console** to expand the banner at the top of the screen.\n    \n    1. Click the **Try it now** button to open the new UI in another tab.\n\n##### New UI benefits\n\n- User-driven design\n\n- Provides full functionality to complete tasks entirely in the UI\n\n- New attributes and improved feature visibility\n\n- Provides configuration flexibility while highlighting the recommended path\n\n- Addresses the needs of different personas and use cases\n\n- Quicker troubleshooting and easier maintenance\n\n##### New UI highlights\n\n- More configurable database attributes, including replica high availability, shards placement, and proxy policy.\n\n- Nodes indicate whether it’s a primary or secondary node.\n\n- Modules show the databases that are using them.\n\n- Certificates show expiration and validity, and you can upload and copy certificates.\n\n- Cluster license displays the number of shards that are used out of the number of shards that are licensed to the cluster. The new UI allows you to paste or upload a new license.\n\n- Role-based access control (RBAC) has explanations to improve clarity. \n\n- Access Control List (ACLs) now support defining ACLs for modules.\n\n- The databases screen has more information per database for faster troubleshooting. It also allows you to filter databases and compare database metrics.\n\n- The cluster name, user, and user role are shown in the upper right for quickly identifying the cluster from any screen. You can also **Change user password** from the dropdown menu.\n\n- Auto Tiering licensing and a toggle for the storage engine used in Auto Tiering enabled databases (available only in the new UI).\n\n- Input validations.\n\n##### New UI limitations\n\nThe following features are not supported in this preview but will be added in future releases. Until then, temporarily switch to the legacy UI to do the following:\n\n- Provision and configure Active-Active databases (viewing is available).\n\n- Search and export the event log.\n\n- Remove a node from the UI.\n\nAdditional limitations:\n\n- Although Redis supports memcached databases, the new UI only allows view and delete. Memcached users are advised to migrate to Redis to enjoy the full benefits of Redis and its UI.\n\nTo open the legacy admin console when signed in to the new UI, select your username, then select **Switch to legacy Admin Console** from the list:\n\n![Select switch to legacy admin console from the dropdown.](https://redis.io/docs/latest/images/rs/screenshots/switch-to-legacy-ui.png)\n\n##### Future UI enhancements\n\n- Configure default database settings and database upgrade settings\n\n- Security preferences related to password and login management\n\n- LDAP improvements\n\n- IPv6 support\n\n- ACL improvements, such as ACLv2 smart validations\n\n- And more\n\n> [!NOTE]\n> With the release of the new Cluster Manager UI, the legacy UI is considered deprecated and will eventually be phased out. New functionality will only be implemented in the new Cluster Manager UI, and the old UI will no longer be maintained except for critical bug fixes.\n\n#### Redis Stack 7.2 features\n\nRedis Enterprise Software version 7.2.4 supports features included in Redis Stack version 7.2.\n\nThe following sections include a few highlights. For more details, see the [Redis Stack 7.2 release notes](https://github.com/redis-stack/redis-stack/releases/tag/v7.2.0-v0).\n\n##### Search and query\n\n- Introduces [Geo Polygon](https://redis.io/docs/latest/commands/ft.search#examples) Search. Geo range queries now accept the [`GEOSHAPE`](https://redis.io/docs/latest/commands/ft.create#required-arguments) field type, which supports polygon shapes using [WKT notation](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry). `GEOSHAPE` supports `POLYGON` and `POINT` as shape formats and polygon operations.\n\n- Performance improvements for `SORT BY` operations using [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search#optional-arguments) and [`FT.AGGREGATE`](https://redis.io/docs/latest/commands/ft.aggregate#optional-arguments).\n\n- New `FORMAT` for improved readability and future support for better error handling responses on `FT.SEARCH` and `FT.AGGREGATE` in RESP3 only.\n\n##### JSON\n\nJSON introduces two new commands:\n\n - [JSON.MERGE](https://redis.io/docs/latest/commands/json.merge) merges a given JSON value into matching paths to update, delete, or expand the JSON values at the matching paths.\n\n - [JSON.MSET](https://redis.io/docs/latest/commands/json.mset) sets or updates one or more JSON values according to specified key-path-value triplets.\n\n##### Triggers and functions preview\n\nA preview of triggers and functions is available.\n\nTriggers and functions provide support for running JavaScript functions inside the Redis process. These functions can be executed on-demand, by an event-driven trigger, or by a stream processing trigger.\n\nTry it out with the [triggers and functions quick start](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions/Quick_Start_RI).\n\n> [!NOTE]\n> - The preview version of triggers and functions is not intended for production use since the API might change in the future and potentially cause application issues when upgrading to a later version.\n>\n> - During preview, triggers and functions are not supported for databases with Auto Tiering enabled (previously known as Redis on Flash).\n\n##### Module versions\n\nRedis Enterprise Software version 7.2.4 includes the following Redis Stack modules:\n\n- [RediSearch 2.8.4](https://github.com/RediSearch/RediSearch/releases/tag/v2.8.4)\n\n- [RedisJSON 2.6.6](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.6.6)\n\n- [RedisTimeSeries 1.10.4](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.10.4)\n\n- [RedisBloom 2.6.3](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.6.3)\n\n- [RedisGears 2.0.11](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.11-m12)\n\n\nSee [Upgrade modules](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/upgrade-module) to learn how to upgrade a module for a database."},{"id":"enhancements","title":"Enhancements","role":"content","text":"#### Three Redis database versions\n\nRedis Enterprise Software version 6.x includes two Redis database versions: 6.0 and 6.2. As of version 7.2, Redis Enterprise Software includes three Redis database versions: 6.0, 6.2, and 7.2.\n\nTo view available Redis database versions:\n\n- In the Cluster Manager UI, see **Redis database versions** on the **Cluster > Configuration** screen.\n\n- Send a [`GET /nodes` REST API request](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/nodes) and see `supported_database_versions` in the response.\n\nThe default Redis database version, which is used when you upgrade an existing database or create a new one, differs between Redis Enterprise releases as follows:\n\n| Redis<br />Enterprise | Bundled Redis<br />DB versions | Default DB version<br />(upgraded/new databases) |\n|-------|----------|-----|\n| 7.2 | 6.0, 6.2, 7.2 | 7.2 |\n| 6.4.2 | 6.0, 6.2 | 6.2 |\n| 6.2.x | 6.0, 6.2 | 6.0 |\n\nFor Redis Enterprise Software version 7.2.4, `default_redis_version` is 7.2 for both `major` and `latest` upgrade policies.\n\n#### Updated Redis Enterprise license format\n\nRedis Enterprise Software version 7.2.4 includes updates to its license format, which add separate shard limits for RAM and flash shards used for Auto Tiering.\n\nFor more information, see [Cluster license keys](https://redis.io/docs/latest/operate/rs/clusters/configure/license-keys).\n\n#### Redis ACL selectors and key-based permissions\n\nRedis ACLs in Redis Enterprise version 7.2 support key permissions and selectors.\n\nKey permissions:\n\n- `%R~<pattern>`: Grants read access to keys that match the given pattern.\n\n- `%W~<pattern>`: Grants write access to keys that match the given pattern.\n\n- `%RW~<pattern>`: Alias for `~<pattern>`. Grants read and write access to keys that match the given pattern.\n\n    See [key permissions](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl#key-permissions) for more information.\n\nSelectors let you define multiple sets of rules in a single Redis ACL (only supported for databases with Redis version 7.2 or later). A command is allowed if it matches the base rule or any selector in the Redis ACL. See [selectors](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl#selectors) for more information.\n\n- `(<rule list>)`: Creates a new selector.\n\n- `clearselectors`: Deletes all existing selectors for a user. This action does not delete the base ACL rule.\n\nRedis ACLs have the following differences in Redis Enterprise Software:\n\n- Nested selectors are not supported.\n\n    For example, the following selectors are not valid in Redis Enterprise: <nobr>`+GET ~key1 (+SET (+SET ~key2) ~key3)`</nobr>\n\n- Key and pub/sub patterns do not allow the following characters: `'(', ')'`\n\n- The following password syntax is not supported: `'>', '<', '#!', 'resetpass'`\n\n    To change passwords in Redis Enterprise Software, use one of the following methods:\n\n    - Cluster Manager UI (admin console)\n\n    - [`rladmin cluster reset_password`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/reset_password):\n    \n        [code example]\n\n    - REST API [`PUT /v1/users`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/users#put-user) request and provide `password`\n\n- The **ACL builder** does not support selectors and key permissions. Use **Free text command** to manually define them instead.\n\n#### New INFO fields\n\nThe [`INFO`](https://redis.io/docs/latest/commands/info) command includes new fields:\n\n- Under the `STATS` section:\n\n    - `current_eviction_exceeded_time` - Redis Enterprise reply is always \"0\"\n\n    - `total_eviction_exceeded_time` - Redis Enterprise reply is always \"0\"\n\n    - `current_active_defrag_time` - Redis Enterprise reply is always \"0\"\n\n    - `total_active_defrag_time` - Redis Enterprise reply is always \"0\"\n\n- Under the `MEMORY` section:\n\n    - `maxmemory_policy` - The value of the `maxmemory-policy` configuration directive\n\nThe `INFO` command can now accept multiple section arguments (requires Redis database version 7 or later).\n\n#### Log rotation enhancements\n\n- The `logrotate` tool rotates logs that exceed 200 MB.\n\n- `logrotate` runs every five minutes instead of once a day.\n\n- The job scheduler runs `logrotate` instead of the OS.\n\n- Every cluster upgrade overwrites the log rotation configuration.\n\n- You can edit the log rotation configuration at `$pkgconfdir/logrotate.conf` (`pkgconfdir` is `/opt/redislabs/config` by default, but can be changed in a custom installation). Note that the configuration file moved since last version.\n\n- You can change how often the `logrotate` tool runs using the job scheduler REST API request <nobr>`PUT /v1/job_scheduler`</nobr>.\n\n#### Multi-OS upgrade support for clusters with modules {#os-upgrades-with-modules}\n\nStarting from Redis Enterprise version 7.2, all future 7.2.x upgrades are supported for clusters containing databases with modules in combination with Operating System (OS) upgrades."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS54131 - `+OK` reply not received on TLS-enabled database\n\n- RS101525 - Cluster provides wrong number of database connections on Grafana\n\n- RS104028 - Fix the self-signed certificate script: error generating certificates with multiple FQDNs\n\n- RS87920 - Proxy log is full of the warning message \"Failed to check status of running child syncer process 0 : No child processes\"\n\n- RS99916 - Fixed the UI log to include the names of LDAP users at login\n\n- RS84273 - When an LDAP user with a Redis `admin` role viewed the log in the UI, they received `db_viewer` permissions instead of `admin`, which limited log visibility\n\n- RS62552 - Fixed database authentication failures for LDAP users when the password contains the `%` character"},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"- Differences when using the `UNWATCH` command within a `MULTI` command sequence:\n\n    - Redis Enterprise: `UNWATCH` is not allowed within a `MULTI` command sequence and returns an error.\n\n    - OSS: `UNWATCH` is allowed within a `MULTI` sequence but has no effect.\n\n- When sending a `PUBSUB SHARDNUMSUB` command in OSS Cluster mode in Redis Enterprise, Redis Enterprise checks the hash slots of the requested channels. Redis Enterprise responds with a `CROSSSLOT` error if the channels don’t hash to the same slot, or a `MOVED` error if the channels hash to a different node."},{"id":"redis-72-breaking-changes","title":"Redis 7.2 breaking changes","role":"content","text":"When new major versions of open source Redis change existing commands, upgrading your database to a new version can potentially break some functionality. Before you upgrade, make sure to read the provided list of breaking changes that affect Redis Enterprise and update any applications that connect to your database to handle these changes.\n\nConfirm your Redis database version (`redis_version`) using the admin console or run the following [`INFO`](https://redis.io/docs/latest/commands/info) command via [`redis-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/redis-cli):\n\n[code example]\n\n#### Breaking changes from version 6.2\n\nUpgrading to open source Redis version 7.2 from version 6.2 introduces the following potentially breaking changes to Redis Enterprise.\n\n##### Programmability\n\n-  Lua scripts no longer have access to the `print()` function ([#10651](https://github.com/redis/redis/pull/10651)) - The `print`  function was removed from Lua because it can potentially cause the Redis processes to get stuck (if no one reads from stdout). Users should use redis.log. An alternative is to override the  `print`  implementation and print the message to the log file.  \n\n- Block [`PFCOUNT`](https://redis.io/docs/latest/commands/pfcount) and [`PUBLISH`](https://redis.io/docs/latest/commands/publish) in read-only scripts (*_RO commands,  `no-writes`) ([#10744](https://github.com/redis/redis/pull/10744)) - Consider `PFCOUNT` and `PUBLISH` as write commands in scripts, in addition to `EVAL`; meaning:\n  - They can never be used in scripts with shebang (`#!`) and no `no-writes` flag\n  - They are blocked in `EVAL_RO` and `_RO` variants, (even in scripts without shebang (`#!`) flags)\n  - Allow `no-write` scripts in EVAL (not just in EVAL_RO), even during `CLIENT PAUSE WRITE` \n\n- Hide the `may_replicate` flag from the [`COMMAND`](https://redis.io/docs/latest/commands/command) command response  ([#10744](https://github.com/redis/redis/pull/10744)) - As part of the change to treat `may_replicate` commands `PFCOUNT` and `PUBLISH` as write commands in scripts, in addition to `EVAL`, the `may_replicate` flag has been removed from the `COMMAND` response.\n\n- Time sampling is now frozen during command execution and scripts ([#10300](https://github.com/redis/redis/pull/10300)). While a command or script is running, the keys used by the command or script will not expire. This breaks any script that uses a loop to wait for a key to expire. \n\n- Blocked commands in scripts now work the same way as when they are used in [transactions](https://redis.io/docs/latest/develop/using-commands/transactions) ([#11568](https://github.com/redis/redis/pull/11568)). \n\n##### Error handling\n\n- Rephrased some error responses about invalid commands or arguments ([#10612](https://github.com/redis/redis/pull/10612)) - \n  - Error response for unknown command introduced a case change (`Unknown` to `unknown`)\n  - Errors for module commands extended to cover subcommands, updated syntax to match Redis Server syntax\n  - Arity errors for module commands introduce a case change (`Wrong` to `wrong`); will consider full command name\n\n- Corrected error codes returned from [`EVAL`](https://redis.io/docs/latest/commands/eval) scripts ([#10218](https://github.com/redis/redis/pull/10218), [#10329](https://github.com/redis/redis/pull/10329)). \n\n  These examples show changes in behavior:\n\n  [code example]\n\n- [`ZPOPMIN`](https://redis.io/docs/latest/commands/zpopmin)/[`ZPOPMAX`](https://redis.io/docs/latest/commands/zpopmax) used to produce wrong replies when count is 0 with non-zset  [#9711](https://github.com/redis/redis/pull/9711)):\n  -  `ZPOPMIN`/`ZPOPMAX` used to produce an `(empty array)` when `key` was not a sorted set and the optional `count` argument was set to `0` and now produces a `WRONGTYPE` error response instead.\n  -  The optional `count` argument must be positive. A negative value produces a `value is out of range` error.\n\n  These examples show changes in behavior:\n\n  [code example]\n\n- [`LPOP`](https://redis.io/docs/latest/commands/lpop)/[`RPOP`](https://redis.io/docs/latest/commands/rpop) with count against a nonexistent list returns a null array instead of `(nil)`([#10095](https://github.com/redis/redis/pull/10095)). This change was backported to 6.2.\n\n- [`LPOP`](https://redis.io/docs/latest/commands/lpop)/[`RPOP`](https://redis.io/docs/latest/commands/rpop) used to produce `(nil)` when count is 0, now produces a null array ([#9692](https://github.com/redis/redis/pull/9692)). This change was backported to 6.2.\n\n- [`XCLAIM`](https://redis.io/docs/latest/commands/xclaim)/[`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) skips deleted entries instead of replying with `nil` and deletes them from the pending entry list ([#10227](https://github.com/redis/redis/pull/10227)) - `XCLAIM`/`XAUTOCLAIM` now behaves in the following way:\n  - If you try to claim a deleted entry, it is deleted from the pending entry list (PEL) where it is found (as well as the group PEL). Therefore, such an entry is not claimed, just cleared from PEL (because it doesn't exist in the stream anyway).\n  - Because deleted entries are not claimed, `X[AUTO]CLAIM` does not return \"nil\" instead of an entry.\n  - Added an array of all the deleted stream IDs to `XAUTOCLAIM` response.\n\n- A blocked stream command that is released when a key no longer exists returns a different error code ([#11012](https://github.com/redis/redis/pull/11012)).\n    - For newly unblocked streams, lists, and zsets, the old implementation returned `UNBLOCKED` when the stream key was deleted or overwritten with a different type. Now, errors will be the same as if the command was processed after the effect.\n\n- ACL errors have been unified across Redis. ([#11160](https://github.com/redis/redis/pull/11160))\n    - When using [RedisModule_Call](https://redis.io/docs/latest/develop/reference/modules/modules-api-ref#redismodule_call) module API function, ACL errors return `-NOPERM` instead of `-ERR`\n\n- [`XREADGROUP`](https://redis.io/docs/latest/commands/xreadgroup) and [`XAUTOCLAIM`](https://redis.io/docs/latest/commands/xautoclaim) create a consumer regardless of whether it was able to perform reading or claiming ([#11012](https://github.com/redis/redis/pull/11012)).\n\n- Any float that is Not a Number will return `nan` ([#11597](https://github.com/redis/redis/pull/11597)).\n\n#####  ACLs\n\n- [`ACL GETUSER`](https://redis.io/docs/latest/commands/acl-getuser) reply now uses ACL syntax for `keys` and `channels` ([#9974](https://github.com/redis/redis/pull/9974)). `ACL GETUSER` now uses the ACL DSL (Domain Specific Language) for keys and channels. \n\n  These examples show changes in behavior:\n  [code example]\n\n- [`SORT`](https://redis.io/docs/latest/commands/sort)/[`SORT_RO`](https://redis.io/docs/latest/commands/sort_ro) commands reject key access patterns in `GET` and `BY` if the ACL doesn't grant the command full keyspace access ([#10340](https://github.com/redis/redis/pull/10340)) - The `sort` and `sort_ro` commands can access external keys via `GET` and `BY`. In order to make sure the user cannot violate the authorization ACL rules, Redis 7 will reject external keys access patterns unless ACL allows `SORT` full access to all keys.\nFor backwards compatibility, `SORT` with `GET`/`BY` keeps working, but if ACL has restrictions to certain keys, the use of these features will result in a permission denied error.\n\n  These examples show changes in behavior:\n\n  [code example]\n\n  [code example]\n\n  [code example]\n\n  [code example]\n\n- Fix ACL category for [`SELECT`](https://redis.io/docs/latest/commands/select), [`WAIT`](https://redis.io/docs/latest/commands/wait, [`ROLE`](https://redis.io/docs/latest/commands/role) [`LASTSAVE`](https://redis.io/docs/latest/commands/lastsave, [`READONLY`](https://redis.io/docs/latest/commands/readonly, [`READWRITE`](https://redis.io/docs/latest/commands/readwrite, [`ASKING`](https://redis.io/docs/latest/commands/asking, [#9208](https://github.com/redis/redis/pull/9208)):\n  - `SELECT` and `WAIT` have been recategorized from `@keyspace` to `@connection`  \n  - `ROLE`, `LASTSAVE` have been categorized as  `@admin`  and  `@dangerous`\n  -  `ASKING`, `READONLY`, `READWRITE` have also been assigned the  `@connection`  category and  removed from `@keyspace`\n  - Command categories are explained in [ACL documentation](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl#command-categories)\n\n  - When a blocked client is being unblocked, checks for ACLs and OOM condition checks are now re-evaluated ([#11012](https://github.com/redis/redis/pull/11012)).\n    - If the ACL rules have changed since the command was executed, the command might fail after the client is unblocked.\n\n##### Command introspection, stats, and configuration\n\n- [`COMMAND`](https://redis.io/docs/latest/commands/command) reply drops `random` and `sort-for-scripts` flags, which are now part of [command tips](https://redis.io/docs/latest/develop/reference/command-tips) ([#10104](https://github.com/redis/redis/pull/10104)) - The `random` flag was replaced with the `nondeterministic_output` tip; the `sort-for-scripts` flag was replaced by the ` nondeterministic_output_order` tip\n\n- [`INFO`](https://redis.io/docs/latest/commands/info) `commandstats` now shows the stats per sub-command ([#9504](https://github.com/redis/redis/pull/9504))\nFor example, while previous versions would provide a single entry for all command usage, in Redis 7, each sub command is reported separately:\n\n  - Redis 6.2:\n  \n    [code example]\n\n  - Redis 7:\n  \n    [code example]\n\n- [`CONFIG REWRITE`](https://redis.io/docs/latest/commands/config-rewrite), [`CONFIG RESETSTAT`](https://redis.io/docs/latest/commands/config-resetstat), and most [`CONFIG SET`](https://redis.io/docs/latest/commands/config-set) commands are now allowed during loading ([#9878](https://github.com/redis/redis/pull/9878))\n\n- When running [`XINFO CONSUMERS`](https://redis.io/docs/latest/commands/xinfo-consumers), the `idle` time now shows the number of milliseconds that have passed since the last attempted interaction, and the `inactive` time shows the number of milliseconds since the last successful interaction ([#11099](https://github.com/redis/redis/pull/11099))\n    - Previously, `idle` time showed the number of milliseconds that passed since the last successful interaction and there was no `inactive` time.\n\n- Command stats are only updated when the command executes ([#11012](https://github.com/redis/redis/pull/11012)).\n    - Previously, the command stats were updated even if a command was blocked. The command stats are now updated only if and when the command is executed.\n\n\n#### Client prerequisites for Redis 7.2 upgrade\n\nThe Redis clients [Go-Redis](https://redis.uptrace.dev/) version 9 and [Lettuce](https://redis.github.io/lettuce/) versions 6 and later use RESP3 by default. If you use either client to run Redis Stack commands, you should set the client's protocol version to RESP2 before upgrading your database to Redis version 7.2 to prevent potential application issues due to RESP3 breaking changes.\n\nFor applications using Go-Redis v9.0.5 or later, set the protocol version to RESP2:\n\n[code example]\n\nTo set the protocol version to RESP2 with Lettuce v6 or later:\n\n[code example]\n\nIf you are using [LettuceMod](https://github.com/redis-developer/lettucemod/), you need to upgrade to [v3.6.0](https://github.com/redis-developer/lettucemod/releases/tag/v3.6.0)."},{"id":"deprecations","title":"Deprecations","role":"content","text":"#### Command deprecations\n\n- [`CLUSTER SLOTS`](https://redis.io/docs/latest/commands/cluster-slots) is deprecated as of Redis 7.0\n\n- [`JSON.RESP`](https://redis.io/docs/latest/commands/json.resp) is deprecated as of Redis Stack 7.2.\n\n- [`QUIT`](https://redis.io/docs/latest/commands/quit) is deprecated as of Redis 7.2\n\n#### API deprecations\n\nFields deprecated as of Redis Enterprise v4.3.3:\n\n- `smtp_use_tls` (replaced with `smtp_tls_mode`)\n\n- `dns_address_master`\n\n- `endpoint_node`\n\n- `endpoint_ip`\n\n- `public_addr` (replaced with `external_addr`)\n\nFields deprecated as of Redis Enterprise v4.4.2:\n\n- `default_shards_overbooking` (replaced with `shards_overbooking`)\n\nFields deprecated as of Redis Enterprise v6.4.2:\n\n- `use_ipv6` (replaced with `use_external_ipv6`)\n\n- `redis_cleanup_job_settings` (replaced with `persistence_cleanup_scan_interval`)\n\nFields deprecated as of Redis Enterprise v5.0.1:\n\n- `bdb_high_syncer_lag` (replaced with `replica_src_high_syncer_lag` and `crdt_src_high_syncer_lag`)\n\n- `bdb_syncer_connection_error`\n\n- `bdb_syncer_general_error`\n\n- `sync_sources` (replaced with `replica_sources` and `crdt_sources`)\n\n- `sync` (replaced with `replica_sync` and `crdt_sync`)\n\n- `ssl` (replaced with `tls_mode`)\n\nFields deprecated as of Redis Enterprise v7.2.4:\n\n- `node.bigstore_driver` (replaced with `cluster.bigstore_driver`)\n\n- `auth_method`\n\n- `authentication_redis_pass` (replaced with multiple passwords feature in version 6.0.X)\n\n- `slave_ha` cluster policy\n\nOther deprecated fields:\n\n- `import/rdb_url` (deprecated as of Redis Enterprise v4.X)\n\n- `logrotate_dir` (to be replaced with `logrotate_config` or removed)\n\nDeprecated CLI commands:\n\n- `rlutil change_master` (deprecated as of Redis Enterprise v6.2.18, replaced with `rladmin change_master`)\n\n- `rlutil reserved_ports` (deprecated as of Redis Enterprise v7.2, replaced with `rladmin cluster config reserved_ports`)\n\nREST API requests deprecated as of Redis Enterprise v7.2:\n\n- `POST /v1/modules` (replaced with `POST /v2/modules`)\n\n- `DELETE /v1/modules` (replaced with `DELETE /v2/modules`)\n\n#### Access control deprecations\n\n- The following predefined roles and Redis ACLs are no longer available for new Redis Enterprise Software version 7.2.4 clusters:\n\n    - Custom roles (not management roles): Cluster Member, Cluster Viewer, DB Member, DB Viewer, None.\n\n    - Redis ACLs: Not Dangerous and Read Only.\n      \n- In upcoming maintenance releases, the deprecated roles and ACLs will be removed automatically when you upgrade to Redis Enterprise Software version 7.2.4, unless they are associated with any users or databases in the cluster.\n\n- A deprecation notice for SASL-based LDAP was included in [previous Redis Enterprise Software release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-4-august-2021#deprecation-notices). When you upgrade to Redis Enterprise Software version 7.2.4, all existing \"external\" users (previously used to support SASL-based LDAP) will be removed.\n\n#### Legacy UI\n\nWith the release of the new Cluster Manager UI, the legacy UI is considered deprecated and will eventually be phased out. New functionality will only be implemented in the new Cluster Manager UI, and the old UI will no longer be maintained except for critical bug fixes.\n\n#### RedisGraph\n\nRedis has announced the end of life for RedisGraph. Redis will continue to support all RedisGraph customers, including releasing patch versions until January 31, 2025.\n\nSee the [RedisGraph end-of-life announcement](https://redis.com/blog/redisgraph-eol/) for more details.\n\n#### RHEL and CentOS 7.0-7.9\n\nSupport for RHEL and CentOS 7.0-7.9 is considered deprecated and will be removed in a future release.\n\n#### Oracle Linux 7\n\nOracle Linux 7 support is considered deprecated and will be removed in a future release.\n\n#### Amazon Linux 1\n\nAmazon Linux 1 support is considered deprecated and will be removed in a future release.\n\n#### Ubuntu 16.04\n\nThe deprecation of Ubuntu 16.04 was announced in the [Redis Enterprise Software 6.4.2 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases#deprecations). As of Redis Enterprise Software 7.2.4, Ubuntu 16.04 is no longer supported.\n\n#### RC4 encryption cipher\n\nThe RC4 encryption cipher is considered deprecated in favor of stronger ciphers. Support for RC4 by the [discovery service](https://redis.io/docs/latest/operate/rs/databases/durability-ha/discovery-service) will be removed in a future release.\n\n#### 3DES encryption cipher\n\nThe 3DES encryption cipher is considered deprecated in favor of stronger ciphers like AES.\nPlease verify that all clients, apps, and connections support the AES cipher. Support for 3DES will be removed in a future release.\nCertain operating systems, such as RHEL 8, have already removed support for 3DES. Redis Enterprise Software cannot support cipher suites that are not supported by the underlying operating system.\n\n#### TLS 1.0 and TLS 1.1\n\nTLS 1.0 and TLS 1.1 connections are considered deprecated in favor of TLS 1.2 or later.\nPlease verify that all clients, apps, and connections support TLS 1.2. Support for the earlier protocols will be removed in a future release.\nCertain operating systems, such as RHEL 8, have already removed support for the earlier protocols. Redis Enterprise Software cannot support connection protocols that are not supported by the underlying operating system."},{"id":"upcoming-changes","title":"Upcoming changes","role":"content","text":"#### Prepare for restrictive pub/sub permissions\n\nRedis database version 6.2 introduced pub/sub ACL rules that determine which [pub/sub channels](https://redis.io/docs/latest/develop/pubsub) a user can access.\n\nThe configuration option `acl-pubsub-default`, added in Redis Enterprise Software version 6.4.2, determines the cluster-wide default level of access for all pub/sub channels. Redis Enterprise Software uses the following pub/sub permissions by default:\n\n- For versions 6.4.2 and 7.2, `acl-pubsub-default` is permissive (`allchannels` or `&*`) by default to accommodate earlier Redis versions.\n\n- In future versions, `acl-pubsub-default` will change to restrictive (`resetchannels`). Restrictive permissions block all pub/sub channels by default, unless explicitly permitted by an ACL rule.\n\nIf you use ACLs and pub/sub channels, you should review your databases and ACL settings and plan to transition your cluster to restrictive pub/sub permissions in preparation for future Redis Enterprise Software releases.\n\nWhen you change the cluster's default pub/sub permissions to restrictive, `&*` is added to the **Full Access** ACL. Before you make this change, consider the following:\n\n- Because pub/sub ACL syntax was added in Redis 6.2, you can't associate the **Full Access** ACL with database versions 6.0 or lower after this change.\n\n- The **Full Access** ACL is not reverted if you change `acl-pubsub-default` to permissive again.\n\n- Every database with the default user enabled uses the **Full Access** ACL.\n\nTo secure pub/sub channels and prepare your cluster for future Redis Enterprise Software releases that default to restrictive pub/sub permissions:\n\n1. Upgrade Redis databases:\n\n    - For Redis Enterprise Software version 6.4.2, upgrade all databases in the cluster to Redis DB version 6.2.\n\n    - For Redis Enterprise Software version 7.2.4, upgrade all databases in the cluster to Redis DB version 7.2 or 6.2.\n\n1. Create or update ACLs with permissions for specific channels using the `resetchannels &channel` format.\n\n1. Associate the ACLs with relevant databases.\n\n1. Set default pub/sub permissions (`acl-pubsub-default`) to restrictive. See [Change default pub/sub permissions](#change-default-pubsub-permissions) for details.\n\n1. If any issues occur, you can temporarily change the default pub/sub setting back to permissive. Resolve any problematic ACLs before making pub/sub permissions restrictive again.\n\n#### Upcoming command request and reponse changes\n\nOpen source Redis version 7.2 changes the request and response policies for several commands. Because the GA release of Redis Enterprise version 7.2 does not include these policy changes, commands might behave differently from open source Redis. However, these changes will be included in a future Redis Enterprise maintenance release:\n\n- [`RANDOMKEY`](https://redis.io/docs/latest/commands/randomkey) and [`SCAN`](https://redis.io/docs/latest/commands/scan) will change from no response policy to a `SPECIAL` response policy.\n\n- [`MSETNX`](https://redis.io/docs/latest/commands/msetnx) currently has a `MULTI_SHARD` request policy and `AGG_MIN` response policy. Both will change to no policy.\n\nFor more information about request and response policies, see [Redis command tips](https://redis.io/docs/latest/develop/reference/command-tips)."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Enterprise Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Enterprise Software.\n\n<span title=\"Warning icon\">&#x26A0;&#xFE0F;</span> Deprecated – The platform is still supported for this version of Redis Enterprise Software, but support will be removed in a future release.\n\n<span title=\"X icon\">&#x274c;</span> End of life – Platform support ended in this version of Redis Enterprise Software.\n\n| Redis Enterprise | 7.2.4 | 6.4.2 | 6.2.18 | 6.2.12 | 6.2.10 | 6.2.8 | 6.2.4 |\n|------------------|-------|-------|--------|--------|--------|--------|-------|\n| **Ubuntu**<sup>[1](#table-note-1)</sup> |\n| 20.04 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span><sup>[6](#table-note-6)</sup> | – | – | – | – | – |\n| 18.04 | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\"><span title=\"Supported\">&#x2705;</span></span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 16.04 | <span title=\"End of life\">&#x274c;</span> | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **RHEL & CentOS**<sup>[2](#table-note-2)</sup>\n| 8.8 | <span title=\"Supported\">&#x2705;</span> | – | – | – | – | – | – |\n| 8.7 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – | – |\n| 8.5-8.6 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – |\n| 8.0-8.4 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| 7.0-7.9 | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Oracle Linux**<sup>[3](#table-note-3)</sup> |\n| 8 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – |\n| 7 | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Rocky Linux**<sup>[3](#table-note-3)</sup> |\n| 8 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| **Amazon Linux** |\n| 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span><sup>[7](#table-note-7)</sup> | – | – | – | – | – |\n| 1 | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Docker**<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Kubernetes**<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n2. <a name=\"table-note-2\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>RHEL and CentOS deployments require OpenSSL 1.0.2 and [firewall configuration](https://redis.io/docs/latest/operate/rs/installing-upgrading/configuring/centos-rhel-firewall).\n\n3. <a name=\"table-note-3\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>Based on the corresponding RHEL version.\n\n4. <a name=\"table-note-4\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>\n[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Enterprise Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes).\n\n6. <a name=\"table-note-6\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>Ubuntu 20.04 support was added in Redis Enterprise Software [6.4.2-43](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-43).\n\n7. <a name=\"table-note-7\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>A release candidate for Amazon Linux 2 support was added in Redis Enterprise Software [6.4.2-61](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-61). Official support for Amazon Linux 2 was added in Redis Enterprise Software [6.4.2-69](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-69)."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the MD5 checksums for the available packages:\n\n| Package | MD5 checksum (7.2.4-52 August release) |\n|---------|---------------------------------------|\n| Ubuntu 18 | 7c7e465c8e129a03ee9f585137b2a1d9 |\n| Ubuntu 20 | 631f27311b19806955fde012953ff9c9 |\n| RedHat Enterprise Linux (RHEL) 7<br/>Oracle Enterprise Linux (OL) 7 | ae76798b1b7243313b4f4cba6ede88d7 |\n| RedHat Enterprise Linux (RHEL) 8<br/>Oracle Enterprise Linux (OL) 8 <br/>Rocky Enterprise Linux | 48936b25aefa2921d38aea84ad06134d |\n| Amazon Linux 2 | 3e8180d7a9ebc3784ab6080234edefd5 |"},{"id":"known-issues","title":"Known issues","role":"content","text":"#### Legacy UI known issues\n\nWhen using the legacy UI, you cannot update and save your changes on the **settings > preferences** tab even though these settings are visible. This issue will be fixed in the next maintenance release.\n\nAs a workaround, use the new Cluster Manager UI to update these settings from the **Cluster > Security > Preferences** tab.\n\n#### Pub/sub channel ACL limitations\n\nIn Redis Enterprise Software version 6.4.2, you could use `&channel` syntax in Redis ACL rules to allow access to specific pub/sub channels even when default pub/sub permissions were permissive (`&allchannels` or `&*`), allowing all channels by default. However, `&allchannels &channel` is not valid syntax.\n\nAs of Redis Enterprise Software version 7.2.4, you cannot create Redis ACLs with this combination of rules. You can only use `&channel` to allow access to specific channels if the default pub/sub permissions are restrictive (`resetchannels`).\n\nAssociating an ACL that contains the invalid syntax <nobr>`&allchannels &channel`</nobr> (created before version 7.2) with a user and database might leave the database in a pending state, unable to function.\n\nTo prevent this issue:\n\n1. Review all existing ACL rules.\n\n1. For each rule containing `&channel`, either:\n\n    - Add the `resetchannels` prefix to restrict access to all channels by default.\n    \n    - Delete the rule if not needed."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Command limitations\n\n- [`CLIENT NO-TOUCH`](https://redis.io/docs/latest/commands/client-no-touch) might not run correctly in the following cases:\n\n    - The Redis database version is earlier than 7.2.0.\n\n    - The `CLIENT NO-TOUCH` command is forbidden by ACL rules.\n\n    Before sending this command, clients should verify the database version is 7.2.0 or later and that using this command is allowed. \n\n- You cannot use [`SUNSUBSCRIBE`](https://redis.io/docs/latest/commands/sunsubscribe) to unsubscribe from a shard channel if the regex changed while subscribed.\n\n- Using [`XREADGROUP BLOCK`](https://redis.io/docs/latest/commands/xreadgroup) with `>` to return all new streams will cause the Redis database to freeze until the shard is restarted. ([#12031](https://github.com/redis/redis/pull/12301))\n\n- Because a rejected command does not record the duration for command stats, an error will appear after it is reprocessed that will cause the Redis database to freeze until the shard is restarted. ([#12247](https://github.com/redis/redis/pull/12247))\n\n#### Modules cannot load in Oracle Linux 7 & 8\n\nDatabases hosted on Oracle Linux 7 & 8 cannot load modules.\n\nAs a temporary workaround, you can change the node's `os_name` in the Cluster Configuration Store (CCS):\n\n[code example]\n\n#### Cluster recovery with manually uploaded modules\n\nFor clusters containing databases with manually uploaded modules, [cluster recovery](https://redis.io/docs/latest/operate/rs/clusters/cluster-recovery) requires an extra step.\n\nAfter installing Redis Enterprise Software on the cluster nodes, upload compatible modules to `modulesdir` (`/opt/redislabs/lib/modules`) before continuing the recovery process.\n\nThis limitation will be removed in a future maintenance release."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Enterprise Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Enterprise has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Enterprise due to different or additional functionality available in Redis Enterprise that is not available in open source Redis.\n\nRedis Enterprise 7.2.4-52 supports open source Redis 7.2, 6.2, and 6.0. Below is the list of open source Redis CVEs fixed by version.\n\nRedis 7.2.0 includes all of the CVE fixes from previous versions.\n\nRedis 7.0.x:\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` command can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)\n\nRedis 6.0.x:\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.0.20)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.0.19)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.0.18)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 6.0.18)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.0.17)"}],"examples":[{"id":"new-features-ex0","language":"sh","code":"rladmin tune db db:<ID> resp3 enabled","section_id":"new-features"},{"id":"enhancements-ex0","language":"sh","code":"rladmin cluster reset_password <user email>","section_id":"enhancements"},{"id":"redis-72-breaking-changes-ex0","language":"sh","code":"$ redis-cli -p <port> INFO\n\"# Server\nredis_version:7.0.8\n...\"","section_id":"redis-72-breaking-changes"},{"id":"redis-72-breaking-changes-ex1","language":"diff","code":"1: config set maxmemory 1\n    2: +OK\n    3: eval \"return redis.call('set','x','y')\" 0\n  - 4: -ERR Error running script (call to 71e6319f97b0fe8bdfa1c5df3ce4489946dda479): @user_script:1: @user_script: 1: -OOM command not allowed when used memory > 'maxmemory'.\n  + 4: -ERR Error running script (call to 71e6319f97b0fe8bdfa1c5df3ce4489946dda479): @user_script:1: OOM command not allowed when used memory > 'maxmemory'.\n    5: eval \"return redis.pcall('set','x','y')\" 0\n  - 6: -@user_script: 1: -OOM command not allowed when used memory > 'maxmemory'.\n  + 6: -OOM command not allowed when used memory > 'maxmemory'.\n    7: eval \"return redis.call('select',99)\" 0\n    8: -ERR Error running script (call to 4ad5abfc50bbccb484223905f9a16f09cd043ba8): @user_script:1: ERR DB index is out of range\n    9: eval \"return redis.pcall('select',99)\" 0\n   10: -ERR DB index is out of range\n   11: eval_ro \"return redis.call('set','x','y')\" 0\n  -12: -ERR Error running script (call to 71e6319f97b0fe8bdfa1c5df3ce4489946dda479): @user_script:1: @user_script: 1: Write commands are not allowed from read-only scripts.\n  +12: -ERR Error running script (call to 71e6319f97b0fe8bdfa1c5df3ce4489946dda479): @user_script:1: ERR Write commands are not allowed from read-only scripts.\n   13: eval_ro \"return redis.pcall('set','x','y')\" 0\n  -14: -@user_script: 1: Write commands are not allowed from read-only scripts.\n  +14: -ERR Write commands are not allowed from read-only scripts.","section_id":"redis-72-breaking-changes"},{"id":"redis-72-breaking-changes-ex2","language":"diff","code":"1: zadd myzset 1 \"one\"\n    2: (integer) 1\n    3: zadd myzset 2 \"two\"\n    4: (integer) 1\n    5: zadd myzset 3 \"three\"\n    6: (integer) 1\n    7: zpopmin myzset -1\n  - 8: (empty array)\n  + 8: (error) ERR value is out of range, must be positive\n    9: 127.0.0.1:6379> set foo bar\n   10: OK\n   11: zpopmin foo 0\n  -12: (empty array)\n  +12: (error) WRONGTYPE Operation against a key holding the wrong kind of value","section_id":"redis-72-breaking-changes"},{"id":"redis-72-breaking-changes-ex3","language":"diff","code":"1: acl setuser foo off resetchannels &channel1 -@all +get\n    2: OK\n    3: acl getuser foo\n    4: 1) \"flags\"\n    5: 2) 1) \"off\"\n    6: 3) \"passwords\"\n    7: 4) (empty array)\n    8: 5) \"commands\"\n    9: 6) \"-@all +get\"\n   10: 7) \"keys\"\n  -11: 8) (empty array)\n  +11: 8) \"\"\n   12: 9)\"channels\"\n  -13 10) 1) \"channel1\"\n  +13 10) \"&channel1\"","section_id":"redis-72-breaking-changes"},{"id":"redis-72-breaking-changes-ex4","language":"plaintext","code":"USER FOO (+sort ~* ~mylist) \n  #FOO> sort mylist by w* get v*  - is O.K since ~* provides full key access","section_id":"redis-72-breaking-changes"},{"id":"redis-72-breaking-changes-ex5","language":"plaintext","code":"USER FOO (+sort %R~* ~mylist) \n  #FOO> sort mylist by w* get v*  - is O.K since %R~* provides full key READ access**","section_id":"redis-72-breaking-changes"},{"id":"redis-72-breaking-changes-ex6","language":"plaintext","code":"USER FOO (+sort %W~* ~mylist)\n  #FOO> sort mylist by w* get v*  - will now fail since $W~* only provides full key WRITE access","section_id":"redis-72-breaking-changes"},{"id":"redis-72-breaking-changes-ex7","language":"plaintext","code":"USER FOO (+sort ~v* ~mylist)\n  #FOO> sort mylist by w* get v*  - will now fail since ~v* only provides partial key access","section_id":"redis-72-breaking-changes"},{"id":"redis-72-breaking-changes-ex8","language":"plaintext","code":"cmdstat_acl:calls=4,usec=279,usec_per_call=69.75,rejected_calls=0,failed_calls=2","section_id":"redis-72-breaking-changes"},{"id":"redis-72-breaking-changes-ex9","language":"  ","code":"cmdstat_acl|list:calls=1,usec=4994,usec_per_call=4994.00,rejected_calls=0,failed_calls=0\n    cmdstat_acl|setuser:calls=2,usec=16409,usec_per_call=8204.50,rejected_calls=0,failed_calls=0\n    cmdstat_acl|deluser:calls=1,usec=774,usec_per_call=774.00,rejected_calls=0,failed_calls=0\n    cmdstat_acl|getuser:calls=1,usec=6044,usec_per_call=6044.00,rejected_calls=0,failed_calls=0","section_id":"redis-72-breaking-changes"},{"id":"redis-72-breaking-changes-ex10","language":"go","code":"client := redis.NewClient(&redis.Options{\n    Addr:     \"<database_endpoint>\",\n    Protocol: 2, // Pin the protocol version\n})","section_id":"redis-72-breaking-changes"},{"id":"redis-72-breaking-changes-ex11","language":"java","code":"import io.lettuce.core.*;\nimport io.lettuce.core.api.*;\nimport io.lettuce.core.protocol.ProtocolVersion;\n\n// ...\nRedisClient client = RedisClient.create(\"<database_endpoint>\");\nclient.setOptions(ClientOptions.builder()\n        .protocolVersion(ProtocolVersion.RESP2) // Pin the protocol version \t\n        .build());\n// ...","section_id":"redis-72-breaking-changes"},{"id":"known-limitations-ex0","language":"sh","code":"ccs-cli hset node:<ID> os_name rhel","section_id":"known-limitations"}]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-64","title":"Redis Enterprise Software release notes 7.2.4-64 (September 2023)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-64/","summary":"Improved cluster recovery with manually uploaded modules. Support package contains supervisorctl status. Configure port range using rladmin and REST API. License API returns the number of used shards (RAM & flash).","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"5a56516b79568bc9b3378556b6136806d3a91f37560edfd1e05d9b513c349503","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Enterprise Software version 7.2.4](https://redis.io/downloads/#software)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Improved cluster recovery with manually uploaded modules\n\n- Support package enhancements\n\n- Configurable port range\n\n- License API enhancements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"enhancements","title":"Enhancements","role":"content","text":"- Cluster recovery with manually uploaded modules \n\n    - For clusters containing databases with manually uploaded modules, [cluster recovery](https://redis.io/docs/latest/operate/rs/clusters/cluster-recovery) is now seamlessly integrated.\n\n- Support package now contains `supervisorctl` status when created by the [`rladmin` command](https://redis.io/docs/latest/operate/rs/installing-upgrading/creating-support-package#command-line-method) (RS107879).\n\n- Port range (`reserved_ports`) is now configurable using [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/config) or the [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster#put-cluster).\n\n    - Removed `rlutil reserved_ports`, which was deprecated in Redis Enterprise Software [version 7.2.4-52](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52#api-deprecations).\n\n- [License REST API requests](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/license) return the number of used shards (RAM & flash).\n\n#### Redis modules\n\nRedis Enterprise Software version 7.2.4-64 includes the following Redis Stack modules (no changes since [Redis Enterprise Software version 7.2.4-52](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52):\n\n- [RediSearch 2.8.4](https://github.com/RediSearch/RediSearch/releases/tag/v2.8.4)\n\n- [RedisJSON 2.6.6](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.6.6)\n\n- [RedisTimeSeries 1.10.4](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.10.4)\n\n- [RedisBloom 2.6.3](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.6.3)\n\n- [RedisGears 2.0.11](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.11-m12)"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS107986 - Allow ports lower than 1024 when configuring [auditing](https://redis.io/docs/latest/operate/rs/security/audit-events) using `rladmin`.\n\n- RS105335 - Added error handling to [`CLIENT NO-TOUCH`](https://redis.io/docs/latest/commands/client-no-touch) to return an error if the Redis database version is earlier than 7.2.0 or the command is restricted by ACL rules (handles `CLIENT NO-TOUCH` [command limitations](https://docs.redis.com/latest/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52/#command-limitations)).\n\n- RS105137 - Fixed audit reconnect issue: sometimes the proxy failed to reconnect to a restarted audit listener.\n\n- RS108394 - Added API validation to prevent configuring Replica Of with TLS without providing a server certificate.\n\n- RS108233 - Proceed with the upgrade even if an empty `saslauthd` configuration exists.\n\n- RS107730 - Fixed a bug where the `failed_authentication_attempt` event log always contained 127.0.0.1 instead of the real client IP address.\n\n- RS107727 - Fixed permission issue for `/etc/cron.d/redislabs` file in Red Hat.\n\n- RS108230 - Removed unused test certificate and key files from the `redis-enterprise` package.\n\n- RS107718 - When `cm_server` is disabled through `optional_services`, envoy (the cluster’s reverse proxy) now stops listening on the `cm_server` port.\n\n- RS107909 - Added `os_family`, which groups operating systems into categories, to node properties and upgrade logic. Fixed the [known limitation](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases#modules-cannot-load-in-oracle-linux-7--8) for upgrades and module uploads for the following operating systems: Rocky Linux, Oracle Linux 7, Oracle Linux 8, CentOS 7, and CentOS 8."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Enterprise Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Enterprise Software.\n\n<span title=\"Warning icon\">&#x26A0;&#xFE0F;</span> Deprecated – The platform is still supported for this version of Redis Enterprise Software, but support will be removed in a future release.\n\n<span title=\"X icon\">&#x274c;</span> End of life – Platform support ended in this version of Redis Enterprise Software.\n\n| Redis Enterprise | 7.2.4 | 6.4.2 | 6.2.18 | 6.2.12 | 6.2.10 | 6.2.8 | 6.2.4 |\n|------------------|-------|-------|--------|--------|--------|--------|-------|\n| **Ubuntu**<sup>[1](#table-note-1)</sup> |\n| 20.04 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span><sup>[6](#table-note-6)</sup> | – | – | – | – | – |\n| 18.04 | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\"><span title=\"Supported\">&#x2705;</span></span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 16.04 | <span title=\"End of life\">&#x274c;</span> | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **RHEL & CentOS**<sup>[2](#table-note-2)</sup>\n| 8.8 | <span title=\"Supported\">&#x2705;</span> | – | – | – | – | – | – |\n| 8.7 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – | – |\n| 8.5-8.6 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – |\n| 8.0-8.4 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| 7.0-7.9 | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Oracle Linux**<sup>[3](#table-note-3)</sup> |\n| 8 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – |\n| 7 | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Rocky Linux**<sup>[3](#table-note-3)</sup> |\n| 8 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| **Amazon Linux** |\n| 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span><sup>[7](#table-note-7)</sup> | – | – | – | – | – |\n| 1 | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Docker**<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Kubernetes**<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n2. <a name=\"table-note-2\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>RHEL and CentOS deployments require OpenSSL 1.0.2 and [firewall configuration](https://redis.io/docs/latest/operate/rs/installing-upgrading/configuring/centos-rhel-firewall).\n\n3. <a name=\"table-note-3\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>Based on the corresponding RHEL version.\n\n4. <a name=\"table-note-4\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>\n[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Enterprise Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes).\n\n6. <a name=\"table-note-6\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>Ubuntu 20.04 support was added in Redis Enterprise Software [6.4.2-43](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-43).\n\n7. <a name=\"table-note-7\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>A release candidate for Amazon Linux 2 support was added in Redis Enterprise Software [6.4.2-61](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-61). Official support for Amazon Linux 2 was added in Redis Enterprise Software [6.4.2-69](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-69)."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the MD5 checksums for the available packages:\n\n| Package | MD5 checksum (7.2.4-64 September release) |\n|---------|---------------------------------------|\n| Ubuntu 18 | eed1a8ba22a9eb2ae6e23d557961e9a7 |\n| Ubuntu 20 | 49b29512123fcd9dd402b53b380e8c78 |\n| RedHat Enterprise Linux (RHEL) 7<br/>Oracle Enterprise Linux (OL) 7 | d90a75b56fe1bc628d2ac27bc137af41 |\n| RedHat Enterprise Linux (RHEL) 8<br/>Oracle Enterprise Linux (OL) 8 <br/>Rocky Enterprise Linux | d9d98b0859b2d6fe8a7802cb6f44f132 |\n| Amazon Linux 2 | b9f1c99b39bb8084583b10d1546f80e1 |"},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Enterprise Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Enterprise has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Enterprise due to different or additional functionality available in Redis Enterprise that is not available in open source Redis.\n\nRedis Enterprise 7.2.4-64 supports open source Redis 7.2, 6.2, and 6.0. Below is the list of open source Redis CVEs fixed by version.\n\nRedis 7.2.x:\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` command can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)\n\nRedis 6.0.x:\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.0.20)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.0.19)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.0.18)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 6.0.18)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.0.17)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-72","title":"Redis Enterprise Software release notes 7.2.4-72 (October 2023)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-72/","summary":"New Cluster Manager UI enhancements - database upgrade configuration, database defaults, user lockout parameters, and TLS configuration improvements.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"3a177a61d67fc7bc590710c4b3cc44dcbb484230d78abed34f5d3df69ab0778e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Enterprise Software version 7.2.4](https://redis.io/downloads/#software)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- New Cluster Manager UI enhancements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"enhancements","title":"Enhancements","role":"content","text":"- New Cluster Manager UI enhancements:\n\n    - [Database upgrade configuration](https://redis.io/docs/latest/operate/rs/databases/configure/db-upgrade) - cluster-wide policies that affect default database upgrades\n\n    - [Database defaults](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults) - cluster-wide policies that determine default settings when creating new databases\n\n    - [User lockout parameters](https://redis.io/docs/latest/operate/rs/security/access-control/manage-users/login-lockout) - locks user accounts after excessive failed login attempts\n\n    - [TLS configuration](https://redis.io/docs/latest/operate/rs/security/encryption/tls/enable-tls) improvements\n\n#### Redis modules\n\nRedis Enterprise Software version 7.2.4-72 includes the following Redis Stack modules (no changes since [Redis Enterprise Software version 7.2.4-52](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52):\n\n- [RediSearch 2.8.4](https://github.com/RediSearch/RediSearch/releases/tag/v2.8.4)\n\n- [RedisJSON 2.6.6](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.6.6)\n\n- [RedisTimeSeries 1.10.4](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.10.4)\n\n- [RedisBloom 2.6.3](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.6.3)\n\n- [RedisGears 2.0.11](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.11-m12)"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS107463 - Fixed \"user with that email already exists\" error for API requests that create a user with `email` set to an empty string.\n\n- RS109810 - Added missing information for `sys_info` files in 7.2.4 cluster support packages.\n\n- RS108515 - Added missing license fields to support packages.\n\n- RS102497 - Removed ICMP communication from installation, upgrade, and `rladmin status`."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Enterprise Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Enterprise Software.\n\n<span title=\"Warning icon\">&#x26A0;&#xFE0F;</span> Deprecated – The platform is still supported for this version of Redis Enterprise Software, but support will be removed in a future release.\n\n<span title=\"X icon\">&#x274c;</span> End of life – Platform support ended in this version of Redis Enterprise Software.\n\n| Redis Enterprise | 7.2.4 | 6.4.2 | 6.2.18 | 6.2.12 | 6.2.10 | 6.2.8 | 6.2.4 |\n|------------------|-------|-------|--------|--------|--------|--------|-------|\n| **Release date** | Aug<br />2023 | Feb<br />2023 | Sept<br />2022 | Aug<br />2022 | Feb<br />2022 | Oct<br />2021 | Aug<br />2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | – | Feb<br />2025 | Aug<br />2024 | Aug<br />2024 | Aug<br />2024 | Aug<br />2024 | Aug<br />2024 |\n| **Ubuntu**<sup>[1](#table-note-1)</sup> |\n| 20.04 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span><sup>[6](#table-note-6)</sup> | – | – | – | – | – |\n| 18.04 | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\"><span title=\"Supported\">&#x2705;</span></span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 16.04 | <span title=\"End of life\">&#x274c;</span> | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **RHEL & CentOS**<sup>[2](#table-note-2)</sup>\n| 8.8 | <span title=\"Supported\">&#x2705;</span> | – | – | – | – | – | – |\n| 8.7 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – | – |\n| 8.5-8.6 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – |\n| 8.0-8.4 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| 7.0-7.9 | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Oracle Linux**<sup>[3](#table-note-3)</sup> |\n| 8 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – |\n| 7 | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Rocky Linux**<sup>[3](#table-note-3)</sup> |\n| 8 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| **Amazon Linux** |\n| 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span><sup>[7](#table-note-7)</sup> | – | – | – | – | – |\n| 1 | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Docker**<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Kubernetes**<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n2. <a name=\"table-note-2\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>RHEL and CentOS deployments require OpenSSL 1.0.2 and [firewall configuration](https://redis.io/docs/latest/operate/rs/installing-upgrading/configuring/centos-rhel-firewall).\n\n3. <a name=\"table-note-3\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>Based on the corresponding RHEL version.\n\n4. <a name=\"table-note-4\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>\n[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Enterprise Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes).\n\n6. <a name=\"table-note-6\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>Ubuntu 20.04 support was added in Redis Enterprise Software [6.4.2-43](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-43).\n\n7. <a name=\"table-note-7\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>A release candidate for Amazon Linux 2 support was added in Redis Enterprise Software [6.4.2-61](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-61). Official support for Amazon Linux 2 was added in Redis Enterprise Software [6.4.2-69](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-69)."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the MD5 checksums for the available packages:\n\n| Package | MD5 checksum (7.2.4-72 October release) |\n|---------|---------------------------------------|\n| Ubuntu 18 | 8a26e0983daa266d39e1f45248f5be3b |\n| Ubuntu 20 | dccab4ee04daffa2354e1faa3fbd14d2 |\n| RedHat Enterprise Linux (RHEL) 7<br/>Oracle Enterprise Linux (OL) 7 | 79ded85fd19cb675b363ed7be3543082 |\n| RedHat Enterprise Linux (RHEL) 8<br/>Oracle Enterprise Linux (OL) 8 <br/>Rocky Enterprise Linux | 0b5681f346761e4ff38efc266608857f |\n| Amazon Linux 2 | b5cc186da8b84329e9838339a8736397 |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS110481 - Importing a database mid-upgrade fails when the master node has the new version.\n    \n    As a workaround, either finish the upgrade before retrying the import, or move the master node to an old node, then perform the upgrade."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Enterprise Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Enterprise has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Enterprise due to different or additional functionality available in Redis Enterprise that is not available in open source Redis.\n\nRedis Enterprise 7.2.4-64 supports open source Redis 7.2, 6.2, and 6.0. Below is the list of open source Redis CVEs fixed by version.\n\nRedis 7.2.x:\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` command can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)\n\nRedis 6.0.x:\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.0.20)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.0.19)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.0.18)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 6.0.18)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.0.17)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-92","title":"Redis Enterprise Software release notes 7.2.4-92 (November 2023)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-92/","summary":"New Cluster Manager UI enhancements - LDAP configuration improvements. New `capability_name` field added to module REST API object. Automatic removal of deprecated, predefined roles and ACLs upon cluster upgrade, unless they are associated with any users or databases.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"24c3cefc2a262933d07bb506691d7a560a8218b93c4e433f29ee42805ca8105f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Enterprise Software version 7.2.4](https://redis.io/downloads/#software), originally released as version 7.2.4-86. Version 7.2.4-92 fixes several additional issues that affect version 7.2.4-86."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- New Cluster Manager UI enhancements\n\n- New `capability_name` field in module REST API object \n\n- Automatic removal of deprecated, predefined roles and ACLs upon cluster upgrade, unless they are associated with any users or databases"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"enhancements","title":"Enhancements","role":"content","text":"- New Cluster Manager UI enhancements:\n\n    - Improved [LDAP configuration](https://redis.io/docs/latest/operate/rs/security/access-control/ldap/enable-role-based-ldap) and moved it from **Cluster > Security > LDAP** to **Access Control > LDAP > Configuration**\n\n- New `capability_name` field added to [module REST API object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/module)\n\n- The following predefined roles and ACLs are removed automatically when you upgrade the cluster, unless they are associated with any users or databases, as planned in the Redis Enterprise Software version 7.2.4-52 [deprecation notice](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52#access-control-deprecations):\n\n    - Custom roles (not management roles): Cluster Member, Cluster Viewer, DB Member, DB Viewer, None.\n\n    - Redis ACLs: Not Dangerous and Read Only.\n\n#### Redis modules\n\nRedis Enterprise Software versions 7.2.4-92 and 7.2.4-86 include the following Redis Stack modules:\n\n- [RediSearch 2.8.4](https://github.com/RediSearch/RediSearch/releases/tag/v2.8.4)\n\n- [RedisJSON 2.6.6](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.6.6)\n\n- [RedisTimeSeries 1.10.6](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.10.6)\n\n- [RedisBloom 2.6.3](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.6.3)\n\n- [RedisGears 2.0.12](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.12-m13)"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"The following issues were resolved in ​Redis Enterprise Software version 7.2.4-92:\n\n- RS114368 - Cluster upgrades should no longer show a warning that the `redislabs` user or group already exists.\n\n- RS114185 - Resolved a [known issue](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-92#known-issues) regarding cluster upgrade failures due to a `Failed to get default_suffix` error, which appears in `dmcproxy.log`.\n\nThe following issues were resolved in ​Redis Enterprise Software version 7.2.4-86:\n\n- RS109744 - Node removal sometimes got stuck in a `starting` state when `wait_for_persistence` was enabled.\n\n- RS110481 - Importing a database mid-upgrade failed when the primary (master) node had the new version.\n\n- RS111363 - Fixed an issue in the legacy UI, where you could not update and save your changes on the **settings > preferences** tab even though these settings were visible.\n\n- RS39744 - The `/opt/redislabs` directory was not created with `redislabs` user and group.\n\n- RS111648 - In `cnm_exec.log`, replaced \"root\" with the module utils log name.\n\n- RS112517 - Fixed an issue in the `prepare_flash` script that reduced Auto Tiering performance on Ubuntu 20.\n\n- RS104189 - Added timeout to `/shards` API to prevent long response times if a shard is stuck.\n\n- RS108771 - Fixed migration issue when a node containing the source shard was down.\n\n- RS105989 - Fixed node removal failure during cluster upgrade.\n\n- RS112568 - Fixed migration and node removal scenarios where a node contained a module that was not spread to all cluster nodes.\n\n- RS110204 - Fixed failure to change the primary (master) node when requested during an upgrade if a shard was down."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Enterprise Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Enterprise Software.\n\n<span title=\"Warning icon\">&#x26A0;&#xFE0F;</span> Deprecated – The platform is still supported for this version of Redis Enterprise Software, but support will be removed in a future release.\n\n<span title=\"X icon\">&#x274c;</span> End of life – Platform support ended in this version of Redis Enterprise Software.\n\n| Redis Enterprise | 7.2.4 | 6.4.2 | 6.2.18 | 6.2.12 | 6.2.10 | 6.2.8 | 6.2.4 |\n|------------------|-------|-------|--------|--------|--------|--------|-------|\n| **Release date** | Aug<br />2023 | Feb<br />2023 | Sept<br />2022 | Aug<br />2022 | Feb<br />2022 | Oct<br />2021 | Aug<br />2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | – | Feb<br />2025 | Aug<br />2024 | Aug<br />2024 | Aug<br />2024 | Aug<br />2024 | Aug<br />2024 |\n| **Ubuntu**<sup>[1](#table-note-1)</sup> |\n| 20.04 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span><sup>[6](#table-note-6)</sup> | – | – | – | – | – |\n| 18.04 | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\"><span title=\"Supported\">&#x2705;</span></span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 16.04 | <span title=\"End of life\">&#x274c;</span> | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **RHEL & CentOS**<sup>[2](#table-note-2)</sup>\n| 8.8 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span><sup>[8](#table-note-8)</sup> | – | – | – | – | – |\n| 8.7 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – | – |\n| 8.5-8.6 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – |\n| 8.0-8.4 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| 7.0-7.9 | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Oracle Linux**<sup>[3](#table-note-3)</sup> |\n| 8 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – |\n| 7 | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Rocky Linux**<sup>[3](#table-note-3)</sup> |\n| 8 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| **Amazon Linux** |\n| 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span><sup>[7](#table-note-7)</sup> | – | – | – | – | – |\n| 1 | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Docker**<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Kubernetes**<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n2. <a name=\"table-note-2\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>RHEL and CentOS deployments require OpenSSL 1.0.2 and [firewall configuration](https://redis.io/docs/latest/operate/rs/installing-upgrading/configuring/centos-rhel-firewall).\n\n3. <a name=\"table-note-3\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>Based on the corresponding RHEL version.\n\n4. <a name=\"table-note-4\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>\n[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Enterprise Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes).\n\n6. <a name=\"table-note-6\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>Ubuntu 20.04 support was added in Redis Enterprise Software [6.4.2-43](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-43).\n\n7. <a name=\"table-note-7\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>A release candidate for Amazon Linux 2 support was added in Redis Enterprise Software [6.4.2-61](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-61). Official support for Amazon Linux 2 was added in Redis Enterprise Software [6.4.2-69](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-69).\n\n8. <a name=\"table-note-8\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>Redis Enterprise Software [6.4.2-103](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-103) and later supports RHEL 8.8."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the MD5 checksums for the available packages:\n\n| Package | MD5 checksum (7.2.4-92 November release) |\n|---------|---------------------------------------|\n| Ubuntu 18 | 17166732725284728162180d92e3fa72 |\n| Ubuntu 20 | d4bd75933d95bbd18d487b9834180517 |\n| RedHat Enterprise Linux (RHEL) 7<br/>Oracle Enterprise Linux (OL) 7 | 78447b4643fafb5dcec37c2981ca7b59 |\n| RedHat Enterprise Linux (RHEL) 8<br/>Oracle Enterprise Linux (OL) 8 <br/>Rocky Enterprise Linux | 6dc4f3ba3d789d8e02f7a963128b79a4 |\n| Amazon Linux 2 | f829ecba9874ed30229736617ad8f0f8 |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS114185 - During an upgrade to Redis Enterprise Software version 7.2.4-86, the proxy might not start due to a `Failed to get default_suffix` error, which appears in `dmcproxy.log`.\n\n    As a workaround, start `dmcproxy` manually:\n\n    [code example]\n    This issue was fixed in Redis Enterprise Software version 7.2.4-92."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Enterprise Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Enterprise has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Enterprise due to different or additional functionality available in Redis Enterprise that is not available in open source Redis.\n\nRedis Enterprise 7.2.4-92 and 7.2.4-86 support open source Redis 7.2, 6.2, and 6.0. Below is the list of open source Redis CVEs fixed by version.\n\nRedis 7.2.x:\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` command can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)\n\nRedis 6.0.x:\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.0.20)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.0.19)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.0.18)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 6.0.18)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.0.17)"}],"examples":[{"id":"known-issues-ex0","language":"sh","code":"supervisorctl restart dmcproxy","section_id":"known-issues"}]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-22-releases","title":"Redis Software release notes 7.22.x","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/","summary":"Diagnostic logging service. Call home client to send daily usage statistics to Redis. Usage reports in support packages. Revamp database API. Migration status API. Two-dimensional rack awareness. New version for actions API. Additional REST API enhancements.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","children":[{"id":"operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-204","summary":"Bug fixes for DMC proxy stability. Internal fixes and improvements.","title":"Redis Software release notes 7.22.2-204 (October 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-204/"},{"id":"operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-189","summary":"Added scoping for shards and database endpoints. Internal fixes and improvements.","title":"Redis Software release notes 7.22.2-189 (September 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-189/"},{"id":"operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-179","summary":"Internal fixes and improvements.","title":"Redis Software release notes 7.22.2-179 (August 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-179/"},{"id":"operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-170","summary":"Improved proxy efficiency. Bug fix for a node join failures due to misidentified IPv6 addresses. Bug fix for -MOVED response metrics visibility. Internal fixes and improvements.","title":"Redis Software release notes 7.22.2-170 (July 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-170/"},{"id":"operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-133","summary":"Bug fix for an issue where the Sentinel service could become unresponsive while processing certain commands due to a timing issue. Internal fixes and improvements.","title":"Redis Software release notes 7.22.2-133 (May 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-133/"},{"id":"operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-116","summary":"Bug fixes for node manager crashes after cluster upgrades and the creation of circular symlinks during cluster upgrades. Internal fixes and improvements.","title":"Redis Software release notes 7.22.2-116 (April 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-116/"},{"id":"operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-93","summary":"Bug fix for flash database percent capacity displayed in the Cluster Manager UI. Internal fixes and improvements.","title":"Redis Software release notes 7.22.2-93 (March 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-93/"},{"id":"operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-79","summary":"Bug fixes for configuration updates on promoted shards after failover, database recovery, certificate validation, and database availability API responses.","title":"Redis Software release notes 7.22.2-79 (February 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-79/"},{"id":"operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-55","summary":"Bug fixes for configuring backups and memory limits in the Cluster Manager UI, and database metrics collection.","title":"Redis Software release notes 7.22.2-55 (January 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-55/"},{"id":"operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-41","summary":"Bug fixes for `cluster_wd` configuration after restarts and internode encryption certificate storage and retrieval.","title":"Redis Software release notes 7.22.2-41 (November 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-41/"},{"id":"operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-20","summary":"Cluster Manager UI bug fixes for issues with S3 periodic backups configuration and connection issues. API bug fixes for omitted default values in responses and Active-Active database creation failures.","title":"Redis Software release notes 7.22.2-20 (October 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-20/"},{"id":"operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-14","summary":"Customer-managed certificates for internode encryption. Cluster Manager UI bug fixes for viewing shard memory usage metrics and editing Active-Active databases that have participating clusters with empty ports.","title":"Redis Software release notes 7.22.2-14 (September 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-14/"},{"id":"operate/rs/release-notes/rs-7-22-releases/rs-7-22-0-250","summary":"Bug fix for LDAPS port configuration in the Cluster Manager UI. Internal fixes and improvements.","title":"Redis Software release notes 7.22.0-250 (August 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-0-250/"},{"id":"operate/rs/release-notes/rs-7-22-releases/rs-7-22-0-241","summary":"Bug fixes for LDAP authentication, Active-Active databases, database recovery, support package generation, memory leaks, and the Cluster Manager UI.","title":"Redis Software release notes 7.22.0-241 (July 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-0-241/"},{"id":"operate/rs/release-notes/rs-7-22-releases/rs-7-22-0-216","summary":"license_expiration_days metric for monitoring. Bug fixes for user password management REST API requests, cluster setup in the Cluster Manager UI, cluster upgrades, maintenance mode snapshot cleanup, database availability API performance, Active-Active syncing, and more.","title":"Redis Software release notes 7.22.0-216 (June 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-0-216/"},{"id":"operate/rs/release-notes/rs-7-22-releases/rs-7-22-0-95","summary":"Diagnostic logging service. Call home client to send daily usage statistics to Redis. Usage reports in support packages. Revamp database API. Migration status API. Two-dimensional rack awareness. New version for actions API. Additional REST API enhancements.","title":"Redis Software release notes 7.22.0-95 (May 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-0-95/"}],"page_type":"content","content_hash":"ee46835cdb83f6acbf34a72acb8fa50ab0a88d33a0a25c6d3c7fcbcb88c938af","sections":[{"id":"overview","title":"Overview","role":"overview","text":"​​Redis Software version 7.22 is now available!"},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Performance improvements\n\n- Diagnostic logging service\n\n- Call home client to send daily usage statistics to Redis\n\n- Usage reports in support packages\n\n- Revamp database API\n\n- Migration status API\n\n- Two-dimensional rack awareness\n\n- New version for actions API\n\n- Additional REST API enhancements"},{"id":"detailed-release-notes","title":"Detailed release notes","role":"content","text":"For more detailed release notes, select a build version from the following table:\n\n\n| Version (Release date) | Major changes | Redis Open Source compatibility |\n| --- | --- | --- |\n| [7.22.2-204 (October 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-204/) | Bug fixes for DMC proxy stability. Internal fixes and improvements. | Redis 7.4.3, 7.2.7, 6.2.13 |\n| [7.22.2-189 (September 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-189/) | Added scoping for shards and database endpoints. Internal fixes and improvements. | Redis 7.4.3, 7.2.7, 6.2.13 |\n| [7.22.2-179 (August 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-179/) | Internal fixes and improvements. | Redis 7.4.3, 7.2.7, 6.2.13 |\n| [7.22.2-170 (July 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-170/) | Improved proxy efficiency. Bug fix for a node join failures due to misidentified IPv6 addresses. Bug fix for -MOVED response metrics visibility. Internal fixes and improvements. | Redis 7.4.3, 7.2.7, 6.2.13 |\n| [7.22.2-133 (May 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-133/) | Bug fix for an issue where the Sentinel service could become unresponsive while processing certain commands due to a timing issue. Internal fixes and improvements. | Redis 7.4.3, 7.2.7, 6.2.13 |\n| [7.22.2-116 (April 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-116/) | Bug fixes for node manager crashes after cluster upgrades and the creation of circular symlinks during cluster upgrades. Internal fixes and improvements. | Redis 7.4.3, 7.2.7, 6.2.13 |\n| [7.22.2-93 (March 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-93/) | Bug fix for flash database percent capacity displayed in the Cluster Manager UI. Internal fixes and improvements. | Redis 7.4.3, 7.2.7, 6.2.13 |\n| [7.22.2-79 (February 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-79/) | Bug fixes for configuration updates on promoted shards after failover, database recovery, certificate validation, and database availability API responses. | Redis 7.4.3, 7.2.7, 6.2.13 |\n| [7.22.2-55 (January 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-55/) | Bug fixes for configuring backups and memory limits in the Cluster Manager UI, and database metrics collection. | Redis 7.4.3, 7.2.7, 6.2.13 |\n| [7.22.2-41 (November 2025)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-41/) | Bug fixes for `cluster_wd` configuration after restarts and internode encryption certificate storage and retrieval. | Redis 7.4.3, 7.2.7, 6.2.13 |\n| [7.22.2-20 (October 2025)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-20/) | Cluster Manager UI bug fixes for issues with S3 periodic backups configuration and connection issues. API bug fixes for omitted default values in responses and Active-Active database creation failures. | Redis 7.4.3, 7.2.7, 6.2.13 |\n| [7.22.2-14 (September 2025)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-14/) | Customer-managed certificates for internode encryption. Cluster Manager UI bug fixes for viewing shard memory usage metrics and editing Active-Active databases that have participating clusters with empty ports. | Redis 7.4.2, 7.2.7, 6.2.13 |\n| [7.22.0-250 (August 2025)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-0-250/) | Bug fix for LDAPS port configuration in the Cluster Manager UI. Internal fixes and improvements. | Redis 7.4.2, 7.2.7, 6.2.13 |\n| [7.22.0-241 (July 2025)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-0-241/) | Bug fixes for LDAP authentication, Active-Active databases, database recovery, support package generation, memory leaks, and the Cluster Manager UI. | Redis 7.4.2, 7.2.7, 6.2.13 |\n| [7.22.0-216 (June 2025)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-0-216/) | license_expiration_days metric for monitoring. Bug fixes for user password management REST API requests, cluster setup in the Cluster Manager UI, cluster upgrades, maintenance mode snapshot cleanup, database availability API performance, Active-Active syncing, and more. | Redis 7.4.2, 7.2.7, 6.2.13 |\n| [7.22.0-95 (May 2025)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-0-95/) | Diagnostic logging service. Call home client to send daily usage statistics to Redis. Usage reports in support packages. Revamp database API. Migration status API. Two-dimensional rack awareness. New version for actions API. Additional REST API enhancements. | Redis 7.4.2, 7.2.7, 6.2.13 |"},{"id":"version-changes","title":"Version changes","role":"compatibility","text":"- The fully qualified domain name is now validated using the FQDN library instead of a regex during cluster creation."},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"- Redis Software installation script changes:\n\n    - Added `--skip-updating-env-path` option to [`install.sh`](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/install-script).\n\n    - Added `skip_updating_env_path` parameter to the [installation answers file](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/manage-installation-questions#configure-file-to-answer)."},{"id":"reserved-ports","title":"Reserved ports","role":"content","text":"Make sure the following ports are open before upgrading Redis Software.\n\nPorts reserved as of Redis Software version 7.22.0:\n\n| Port | Process name | Usage | \n|------|--------------|-------|\n| 3346 | cluster_api_internal | Cluster API internal port |\n| 3351 | cluster_watchdog_grpc_api | Cluster watchdog now supports gRPC |\n| 3352 | grpc_service_mesh | gRPC communication between nodes |\n| 3353 | local_grpc_service_mesh | Local gRPC services |\n| 3354 | grpc_gossip_envoy | gRPC gossip protocol communication between nodes |\n| 3355 | authentication_service | Authentication service internal port |\n\nPorts reserved as of Redis Software version 7.8.2:\n\n| Port | Process name | Usage | \n|------|--------------|-------|\n| 3347 | cert_exporter | Reports cluster certificate metrics |\n| 3348 | process_exporter | Reports process metrics for DMC and Redis processes |\n| 3349 | cluster_wd_exporter | Reports cluster watchdog metrics |\n| 3350 | db_controller | Internode communication |\n| 9091 | node_exporter | Reports host node metrics related to CPU, memory, disk, and more |\n| 9125 | statsd_exporter | Reports push metrics related to the DMC and syncer, and some cluster and node metrics |\n\nSee [Ports and port ranges used by Redis Software](https://redis.io/docs/latest/operate/rs/networking/port-configurations#ports-and-port-ranges-used-by-redis-enterprise-software) for a complete list."},{"id":"deprecations","title":"Deprecations","role":"content","text":"#### API deprecations\n\n- Deprecated [`GET /v1/bdbs/<uid>/actions/optimize_shards_placement`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/optimize_shards_placement#get-bdbs-actions-optimize-shards-placement) REST API request. Use [`PUT /v1/bdbs/<uid>/actions/revamp?dry_run=true`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/revamp#put-bdbs-actions-revamp) to get an optimized shard placement blueprint for a database instead.\n\n- Deprecated the `data_files` option for the `recovery_plan` specified in [`POST /v2/bdbs`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#post-bdbs-v2) requests. Use the new `original_bdb_shards` option to recover a database from the provided list of shards instead.\n\n- Deprecated [`PUT /v1/cluster/update_cert`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/certificates#put-cluster-update_cert) REST API requests as of Redis Software version 7.22.2. Use [`PUT /v1/cluster/certificates`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/certificates#put-cluster-certificates) to update cluster certificates instead.\n\n#### Internal monitoring and v1 Prometheus metrics deprecation\n\nThe existing [internal monitoring engine](https://redis.io/docs/latest/operate/rs/monitoring/v1_monitoring) is deprecated. We recommend transitioning to the new [metrics stream engine](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine) for improved performance, enhanced integration capabilities, and modernized metrics streaming.\n\nV1 Prometheus metrics are deprecated but still available. To transition to the new metrics stream engine, either migrate your existing dashboards using [this guide](https://redis.io/docs/latest/operate/rs/references/metrics/prometheus-metrics-v1-to-v2) now, or wait to use new preconfigured dashboards when they become available in a future release."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors.\n\n- RS155734: Endpoint availability metrics do not work as expected due to a calculation error.\n\n- RS156391: The `job_scheduler`'s memory usage can increase significantly when the diagnostic logging service is enabled."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Upload modules before OS upgrade\n\nIf the cluster contains any databases that use modules, you must upload module packages for the target OS version to a node in the existing cluster before you upgrade the cluster's operating system.\n\nSee [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os) for detailed upgrade instructions.\n\n#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### RedisGraph prevents upgrade to RHEL 9 \n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached end-of-life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Query results might include hash keys with lazily expired fields\n\nIf one or more fields of a hash key expire after an `FT.SEARCH` or `FT.AGGREGATE` query begins, Redis does not account for these lazily expired fields. As a result, keys with expired fields might still be included in the query results, leading to potentially incorrect or inconsistent results.\n\n#### Active defragmentation does not stop mid-key for JSON\n\nActive defragmentation does not stop mid-key for JSON data. Large keys are defragmented in full, which might cause latency spikes."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-22-releases/rs-7-22-0-216","title":"Redis Software release notes 7.22.0-216 (June 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-0-216/","summary":"license_expiration_days metric for monitoring. Bug fixes for user password management REST API requests, cluster setup in the Cluster Manager UI, cluster upgrades, maintenance mode snapshot cleanup, database availability API performance, Active-Active syncing, and more.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"4221381fb72449f441e77dad1d866c53b81ea780a400c2d98a4954e82153675f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"​[​Redis Software version 7.22.0](https://redis.io/downloads/#software) is now available!"},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- `license_expiration_days` metric for monitoring\n\n- Bug fixes for user password management REST API requests, cluster setup in the Cluster Manager UI, cluster upgrades, maintenance mode snapshot cleanup, database availability API performance, Active-Active syncing, and more"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"enhancements","title":"Enhancements","role":"content","text":"- Added the `license_expiration_days` metric to the [metrics stream engine](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine). This metric tracks the number of days until the [cluster license expires](https://redis.io/docs/latest/operate/rs/clusters/configure/license-keys#expired-cluster-license)."},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 7.22.0 includes three Redis database versions: 7.4.2, 7.2.7, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 7.4."},{"id":"redis-module-feature-sets","title":"Redis module feature sets","role":"content","text":"Redis Software comes packaged with several modules. As of version 7.22.0, Redis Software includes three feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)  |"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS161290: Fixed an issue where the `/v1/users/password` REST API updated the password of the authenticated admin user instead of the intended target user specified in the JSON request body.\n\n- RS161222: Fixed an issue in the Cluster Manager UI that caused joining new nodes to a cluster using private IP addresses to fail.\n\n- RS161716: Fixed an issue in the Cluster Manager UI where cluster creation could fail on nodes with multiple network interfaces.\n\n- RS160378: Fixed an issue where certain processes might not terminate as expected during cluster upgrades due to timeouts.\n\n- RS122458: Fixed an issue where old snapshots were not properly deleted when turning off maintenance mode with the `skip_shards_restore` option.\n\n- RS161301: Fixed an issue where the DMC proxy could crash when using `CLIENT REPLY OFF`.\n\n- RS157762: Fixed an issue where certain operations could fail with `No such state: error`.\n\n- RS160914: Fixed slow response times for database availability API requests.\n\n- RS140077: Fixed a syncer issue where the number of keys did not match across the instances of Active-Active databases reconfigured with `only_configuration` after cluster recovery.\n\n- RS161419: Fixed an issue in the Cluster Manager UI where database configuration updates failed with \"Unable to access backup server\" errors after configuring Google Cloud Storage backups.\n\n- RS152761: Fixed an issue where the database endpoint availability API returned the status `500 Internal Server Error` instead of `503 Service Unavailable` if the endpoint was not available on that node."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.22.0-216 June release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">3371eba5dc12758c19f37388783678b5154003328e0acc4cfdc136fa5656d223</span> |\n| Ubuntu 22 | <span class=\"break-all\">09272ad6dd0e990a5faa9bbc455102094396c949f660cc6eca34bde848dec3b4</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">1973273042ded9678a5aa886eb5d362f9108a285db9c86660b96f2ae0c37dd81</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">2e5b6d5253c6cb5fc08fc0d8bcb36d17721ec4ea70b160a0972e7db0b88cf0f6</span> |\n| Amazon Linux 2 | <span class=\"break-all\">9cfd34bb3acdbafe449489d6c346e6f44ac3dc0134c23ed0302e674cf39bf41e</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors.\n\n- RS155734: Endpoint availability metrics do not work as expected due to a calculation error.\n\n- RS153589: The metrics stream engine preview reports incorrect latency metrics."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Upload modules before OS upgrade\n\nIf the cluster contains any databases that use modules, you must upload module packages for the target OS version to a node in the existing cluster before you upgrade the cluster's operating system.\n\nSee [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os) for detailed upgrade instructions.\n\n#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### RedisGraph prevents upgrade to RHEL 9\n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Query results might include hash keys with lazily expired fields\n\nIf one or more fields of a hash key expire after an `FT.SEARCH` or `FT.AGGREGATE` query begins, Redis does not account for these lazily expired fields. As a result, keys with expired fields might still be included in the query results, leading to potentially incorrect or inconsistent results.\n\n#### Active defragmentation does not stop mid-key for JSON\n\nActive defragmentation does not stop mid-key for JSON data. Large keys are defragmented in full, which might cause latency spikes."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.22.0-216 supports open source Redis 7.4, 7.2, and 6.2. Below is the list of open source Redis CVEs fixed by version.\n\nRedis 7.4.x:\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\nRedis 7.2.x:\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-22-releases/rs-7-22-0-241","title":"Redis Software release notes 7.22.0-241 (July 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-0-241/","summary":"Bug fixes for LDAP authentication, Active-Active databases, database recovery, support package generation, memory leaks, and the Cluster Manager UI.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"2fbb8bf080695b80aa890ddf324a95ccbabde5388d9384b07c9b9aec836f7fe6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"​[​Redis Software version 7.22.0](https://redis.io/downloads/#software) is now available!"},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Bug fixes for LDAP authentication, Active-Active databases, database recovery, support package generation, memory leaks, and the Cluster Manager UI."},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 7.22.0 includes three Redis database versions: 7.4.2, 7.2.7, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 7.4."},{"id":"redis-module-feature-sets","title":"Redis module feature sets","role":"content","text":"Redis Software comes packaged with several modules. As of version 7.22.0, Redis Software includes three feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)  |"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS140077: Fixed a syncer issue where the number of keys did not match across the instances of Active-Active databases reconfigured with `only_configuration` after cluster recovery.\n\n- RS155910: Fixed an issue where event logs incorrectly attributed configuration changes to `system` instead of the user who made the changes if they authenticated using LDAP.\n\n- RS149040: Fixed a Gunicorn memory leak when using Active-Active databases in Kubernetes (REAADB).\n\n- RS160282: Fixed an issue where database recovery failed if any cluster nodes were still unavailable after cluster recovery.\n\n- RS156160: Fixed an issue where the DMC proxy restarted unnecessarily after LDAP configuration updates.\n\n- RS110383: Fixed LDAP socket file permissions to allow users in the `redislabs` group to run commands.\n\n- RS163846: Fixed an issue where Active-Active database resharding could assign duplicate shard IDs.\n\n- RS161588: Fixed an issue where support package generation could fail if Redis Software was installed with the `--skip-updating-env-path` option.\n\n- RS115284: Fixed pdns file permissions.\n\n- RS120628: Fixed an issue where database routing was not updated after shard role changes, causing write requests to be sent to replica shards instead of primary shards.\n\n- RS161522: Fixed an issue where hash keys with counters were not properly garbage collected, causing these keys to grow in size when frequently deleted and recreated.\n\n- RS161524: Fixed an issue where the Cluster Manager UI could not modify Active-Active database configurations when instance URLs were created without explicit port numbers.\n\n- RS163674: Fixed an issue where clusters could become non-operational when nodes had multiple network interfaces and the Redis interface was not first in the interface enumeration order."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.22.0-241 July release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">f7ee57058b7db2e1f8e861253d07290e38a0426a706268f4658336e2c443e3ff</span> |\n| Ubuntu 22 | <span class=\"break-all\">72d9c6dbf4d15ace5a7003ca2cecc3bd534ed76cb66d7751a03e53693f8f3e1e</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">be3a285252f90654485cb3272ad9c793a774ab361359eed09564caac2f995851</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">ec43ca4e139370c5bd8ad58180857ad13372aaae97a0246a7c9560ae0209d8ae</span> |\n| Amazon Linux 2 | <span class=\"break-all\">6d7d6bae26864fbc09f38820ad535f3aba94ee6572afaa3f8c5c1b5f67b33d63</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors.\n\n- RS155734: Endpoint availability metrics do not work as expected due to a calculation error.\n\n- RS153589: The metrics stream engine preview reports incorrect latency metrics."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Upload modules before OS upgrade\n\nIf the cluster contains any databases that use modules, you must upload module packages for the target OS version to a node in the existing cluster before you upgrade the cluster's operating system.\n\nSee [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os) for detailed upgrade instructions.\n\n#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### RedisGraph prevents upgrade to RHEL 9\n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Query results might include hash keys with lazily expired fields\n\nIf one or more fields of a hash key expire after an `FT.SEARCH` or `FT.AGGREGATE` query begins, Redis does not account for these lazily expired fields. As a result, keys with expired fields might still be included in the query results, leading to potentially incorrect or inconsistent results.\n\n#### Active defragmentation does not stop mid-key for JSON\n\nActive defragmentation does not stop mid-key for JSON data. Large keys are defragmented in full, which might cause latency spikes."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.22.0-241 supports open source Redis 7.4, 7.2, and 6.2. Below is the list of open source Redis CVEs fixed by version.\n\nRedis 7.4.x:\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\nRedis 7.2.x:\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-22-releases/rs-7-22-0-250","title":"Redis Software release notes 7.22.0-250 (August 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-0-250/","summary":"Bug fix for LDAPS port configuration in the Cluster Manager UI. Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"be0f3527680fa1d537c9101bc23aa8e024d7f880baf2517d0230abf3707222f2","sections":[{"id":"overview","title":"Overview","role":"overview","text":"​[​Redis Software version 7.22.0](https://redis.io/downloads/#software) is now available!"},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- A bug fix for LDAPS port configuration in the Cluster Manager UI\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 7.22.0 includes three Redis database versions: 7.4.2, 7.2.7, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 7.4."},{"id":"redis-module-feature-sets","title":"Redis module feature sets","role":"content","text":"Redis Software comes packaged with several modules. As of version 7.22.0, Redis Software includes three feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)  |"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS166727: Fixed an issue in the Cluster Manager UI that prevented configuring LDAPS servers on ports other than the default port 389."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.22.0-250 August release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">fc6ba9365a2eae1adbbdb8d319656fd65ffba0983db3aea1825da0825cec3262</span> |\n| Ubuntu 22 | <span class=\"break-all\">76b4f1b73f21f2c144867e4d7fef53d949726de92694bd34fb85a90d925b5b2e</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">1e596e23d0578401f8107b96cfb63eda1722d2dc4ff38a9158288551e6db8595</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">36706c4e124b5c8ee5cf685879499c0a972776b261c9b4774c11ce533f70a2ed</span> |\n| Amazon Linux 2 | <span class=\"break-all\">c0f73935f85f0f100d6d6365f338e2f0609478177ec2e55d1422e2dad9fee2f7</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors.\n\n- RS155734: Endpoint availability metrics do not work as expected due to a calculation error.\n\n- RS153589: The metrics stream engine preview reports incorrect latency metrics."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Upload modules before OS upgrade\n\nIf the cluster contains any databases that use modules, you must upload module packages for the target OS version to a node in the existing cluster before you upgrade the cluster's operating system.\n\nSee [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os) for detailed upgrade instructions.\n\n#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### RedisGraph prevents upgrade to RHEL 9\n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Query results might include hash keys with lazily expired fields\n\nIf one or more fields of a hash key expire after an `FT.SEARCH` or `FT.AGGREGATE` query begins, Redis does not account for these lazily expired fields. As a result, keys with expired fields might still be included in the query results, leading to potentially incorrect or inconsistent results.\n\n#### Active defragmentation does not stop mid-key for JSON\n\nActive defragmentation does not stop mid-key for JSON data. Large keys are defragmented in full, which might cause latency spikes."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.22.0-250 supports open source Redis 7.4, 7.2, and 6.2. Below is the list of open source Redis CVEs fixed by version.\n\nRedis 7.4.x:\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\nRedis 7.2.x:\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-22-releases/rs-7-22-0-95","title":"Redis Software release notes 7.22.0-95 (May 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-0-95/","summary":"Diagnostic logging service. Call home client to send daily usage statistics to Redis. Usage reports in support packages. Revamp database API. Migration status API. Two-dimensional rack awareness. New version for actions API. Additional REST API enhancements.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"7eb4b02a3d7b0bd599c50c75a6463cabad5ba1770a95a26f7ac15bd28c67e7fe","sections":[{"id":"overview","title":"Overview","role":"overview","text":"​[​Redis Software version 7.22.0](https://redis.io/downloads/#software) is now available!"},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Performance improvements\n\n- Diagnostic logging service\n\n- Call home client to send daily usage statistics to Redis\n\n- Usage reports in support packages\n\n- Revamp database API\n\n- Migration status API\n\n- Two-dimensional rack awareness\n\n- New version for actions API\n\n- Additional REST API enhancements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"new-features","title":"New features","role":"content","text":"- Performance improvements:\n\n    - This version includes major performance improvements, mostly for low-end environments (VMs with 2, 4, or 8 cores) and single-shard (non-clustered) databases.\n\n- [Diagnostic logging service](https://redis.io/docs/latest/operate/rs/clusters/logging/diagnostic-logging):\n\n    - New diagnostic logs for improved troubleshooting.\n\n    - Diagnostic logs are enabled by default.\n\n    - Logs are configurable using [REST API requests](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/diagnostics).\n\n    - Logs have a predefined rotation and retention policy, which can be updated as needed.\n\n    - JSON response format for easy integration with external logging tools.\n\n- The [call home client](https://redis.io/docs/latest/operate/rs/clusters/configure/call-home) sends daily usage statistics to Redis for operational insights. Reports include memory usage, shard information, enabled features, and other operational metrics.\n\n- [Support packages](https://redis.io/docs/latest/operate/rs/installing-upgrading/creating-support-package) now include [usage reports](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/usage_report).\n\n- [Revamp database REST API requests](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/revamp):\n\n    - Updates topology-related configurations of an active database and optimises the shards placement for the new configuration. Example configuration parameters include `memory_size`, `shards_count`, `avoid_nodes`, `shards_placement`, `bigstore_ram_size`, and `replication`.\n\n    - `PUT /v1/bdbs/<uid>/actions/revamp?dry_run=true` replaces the deprecated request to [optimize shards placement](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/optimize_shards_placement).\n\n- [Migration status REST API request](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/migrations), which reports the migration status of a database in the cluster when using Replica Of.\n\n- Added `secondary_rack_id` to bootstrap and node configuration to support [two-dimensional rack awareness](https://redis.io/docs/latest/operate/rs/clusters/configure/rack-zone-awareness#set-up-two-dimensional-rack-zone-awareness).\n\n- A new version of the [actions API](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/actions), which adds progress tracking to all background actions, is available at `GET /v2/actions`."},{"id":"enhancements","title":"Enhancements","role":"content","text":"- REST API enhancements:\n\n    - Adjusted the API to [get the database recovery plan](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/recover#get-bdbs-actions-recover) to only return the best persistence file per slot range for databases with `master_persistence` enabled.\n\n    - A database's `maxclients` can now be configured with an [update database configuration](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#put-bdbs) request.\n\n    - Added `bigstore_version` to database configuration and `default_bigstore_version` to cluster settings.\n\n    - Added `default_oss_cluster` to cluster settings.\n\n    - Added `sentinel_service` as an optional service that can be enabled or turned off with an [update cluster services configuration](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/services_configuration#put-cluster-services_config) request.\n\n    - Added `replica_read_only` to database configuration. If set to `true`, it enables an Active-Passive setup where Replica Of databases only allow read operations. `replica_read_only` is only configurable during [database creation](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#post-bdbs-v1) and cannot be changed later.\n\n    - Added `robust_crdt_syncer` to enable or turn off the robust syncer for Active-Active databases.\n\n    - Added `incoming_connections_rate_limit`, `incoming_connections_capacity`, and `incoming_connections_min_capacity` to proxy configuration.\n\n    - Added `client_eviction` to proxy configuration. If set to `true`, it enables client eviction based on `maxmemory_clients`.\n\n    - The cluster's `logrotate_settings` can now be configured with an [update cluster configuration](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster#put-cluster) request.\n\n    - Added `multi_commands_opt` to cluster configuration and database configuration. If set to `batch`, it reduces the overhead of transaction management by batching multiple commands into a single transaction.\n\n    - Added the following active defragmentation parameters to database configuration: `activedefrag`, `active_defrag_cycle_max`, `active_defrag_cycle_min`, `active_defrag_ignore_bytes`, `active_defrag_max_scan_fields`, `active_defrag_threshold_lower`, and `active_defrag_threshold_upper`.\n\n    - Added an `original_bdb_shards` option for the `recovery_plan` specified in [`POST /v2/bdbs`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#post-bdbs-v2) requests to recover a database from the provided list of shards.\n\n- Added module information to database creation log messages.\n\n- Performance improvements for full-sync replication flows.\n\n- Additional performance improvements:\n\n    - Introduced proxy optimizations for single-sharded databases when a user sends a pipeline of commands. \n\n    - Enabled deeper default optimization levels on the proxy, namely O3 and link time optimization (LTO).\n\n    - Introduced `redis-last` shard optimizations that improved overall baseline performance for `@fast` commands.\n\n- Reserved the following ports:\n\n    | Port | Process name | Usage | \n    |------|--------------|-------|\n    | 3346 | cluster_api_internal | Cluster API internal port |\n    | 3351 | cluster_watchdog_grpc_api | Cluster watchdog now supports gRPC |\n    | 3352 | grpc_service_mesh | gRPC communication between nodes |\n    | 3353 | local_grpc_service_mesh | Local gRPC services |\n    | 3354 | grpc_gossip_envoy | gRPC gossip protocol communication between nodes |\n    | 3355 | authentication_service | Authentication service internal port |"},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 7.22.0 includes three Redis database versions: 7.4.2, 7.2.7, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 7.4."},{"id":"redis-module-feature-sets","title":"Redis module feature sets","role":"content","text":"Redis Software comes packaged with several modules. As of version 7.22.0, Redis Software includes three feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)  |"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS134225: Fixed an issue where the server restart could fail during cluster upgrade due to a timeout.\n\n- RS133342: Fixed an issue where too many API requests in the queue could cause new API requests to fail.\n\n- RS125543: Changed slow log duration to milliseconds in the new Cluster Manager UI.\n\n- RS142296: Fixed a REST API issue where updating an existing database with an empty module list removed modules from the database. Such requests now return an error instead.\n\n- RS94080: Fixed `PUT /v1/bdbs/<id>/<action>` requests to allow `flush` and `reset_admin_pass` actions without requiring a request body.\n\n- RS140649: Fixed an issue where database backups were not deleted at the expected time based on the configured retention period.\n\n- RS136409: Improved checks to determine if the cluster is in an unstable state.\n\n- RS122370: Changed Envoy concurrency to 4 by default to prevent an issue where Envoy on the primary node in a cluster with many nodes sometimes failed to receive API requests.\n\n- RS107325: Fixed an issue where database recovery could get stuck due to shard UID conflicts.\n\n- RS139699: Fixed an issue where the Cluster Manager UI reported \"invalid username or password\" if the connection timed out during a sign-in attempt.\n\n- RS138625: Fixed an issue where `rlcheck` reported `verify_tcp_connectivity` as failed for optional services that were not enabled instead of skipping the check.\n\n- RS134742: Fixed an issue where the `virbr0` interface was automatically added to the external addresses when nodes joined the cluster, which could cause network conflicts and connectivity issues due to duplicated IP addresses.\n\n- RS138490: Reduced log entries for unbootstrapped nodes, which do not have log rotation scheduled yet, to prevent filling the disk with logs.\n\n- RS123576: Fixed an issue that prevented reconfiguring an existing ACL file with `rlutil`.\n\n- RS133679: Added a validation check to the optimize shards API to return quicker if the database cannot fit on the cluster.\n\n- RS134968: Improved the functionality around killing shards.\n\n- RS146096: Fixed an issue where the syncer could fail due to the RDB file parser incorrectly parsing least frequently used values larger than 127.\n\n- RS125845: Fixed an issue where outdated AOF persistence files were not rotated upon upgrading the Redis database version from 6 to 7.\n\n- RS147882: Fixed a `Failed to write PID file: Permission denied` error in `ccs-redis.log` by removing an unneeded CCS PID file.\n\n- RS146941: Fixed an issue where `crdb-cli crdb get` failed with a `KeyError` when `replication_endpoint` or `replication_tls_sni` were not set for the Active-Active database.\n\n- RS122668: Fixed an issue where new DMC workers created after a certificate update could still have the old certificate.\n\n- RS141853: Optimized connection pool handling to reduce memory and CPU usage by `node_wd`.\n\n- RS150853: Fixed an issue during RDB loading where replica shards sometimes terminated with the module error `no matching module type 'AAAAAAAAA'`.\n\n- RS149480: Fixed an issue where port 9091 was missing from the reserved ports list returned by `rladmin` and `ccs-cli`.\n\n- RS148075: Changed the default value of `gradual_sync_mode` to `enabled` for Replica Of databases to sync data from one shard at a time and reduce load on the destination.\n\n- RS135446: Added cleanup of temporary files after `debug_info` generation failures.\n\n- RS151534: Fixed a permissions issue for `crdb-cli.log` that caused `crdb-cli crdb list` to fail for users in the `redislabs` group.\n\n- RS150746: Added a retry mechanism in case the DMC fails to create a listener on a database port.\n\n- RS145047: Fixed an issue where repeated error messages were excessively logged when the auditing server was no longer reachable.\n\n- RS155261: Fixed an issue where the CRDB syncer could fail after an upgrade due to `SASL negotation failed` errors.\n\n- RS153478: Fixed an issue that could cause inconsistent memory settings across shards and out-of-memory errors.\n\n- RS156391: Fixed an issue where the `job_scheduler`'s memory usage could increase significantly when the diagnostic logging service was enabled."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":"- The fully qualified domain name is now validated using the FQDN library instead of a regex during cluster creation."},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"- Redis Software installation script changes:\n\n    - Added `--skip-updating-env-path` option to [`install.sh`](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/install-script).\n\n    - Added `skip_updating_env_path` parameter to the [installation answers file](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/manage-installation-questions#configure-file-to-answer)."},{"id":"deprecations","title":"Deprecations","role":"content","text":"#### API deprecations\n\n- Deprecated [`GET /v1/bdbs/<uid>/actions/optimize_shards_placement`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/optimize_shards_placement#get-bdbs-actions-optimize-shards-placement) REST API request. Use [`PUT /v1/bdbs/<uid>/actions/revamp?dry_run=true`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/revamp#put-bdbs-actions-revamp) to get an optimized shard placement blueprint for a database instead.\n\n- Deprecated the `data_files` option for the `recovery_plan` specified in [`POST /v2/bdbs`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#post-bdbs-v2) requests. Use the new `original_bdb_shards` option to recover a database from the provided list of shards instead.\n\n#### Internal monitoring and v1 Prometheus metrics deprecation\n\nThe existing [internal monitoring engine](https://redis.io/docs/latest/operate/rs/monitoring/v1_monitoring) is deprecated. We recommend transitioning to the new [metrics stream engine](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine) for improved performance, enhanced integration capabilities, and modernized metrics streaming.\n\nV1 Prometheus metrics are deprecated but still available. To transition to the new metrics stream engine, either migrate your existing dashboards using [this guide](https://redis.io/docs/latest/operate/rs/references/metrics/prometheus-metrics-v1-to-v2) now, or wait to use new preconfigured dashboards when they become available in a future release."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.22.0-95 May release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">f9ff8f4cbc65b7f9c80397f2af1cac72086531039f16f3067125203c760e8f6c</span> |\n| Ubuntu 22 | <span class=\"break-all\">4fa09dbdde6daa14387cef3258f3ffc5de8d133afb980f4564468cd0b7cf8104</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">9aaf28320e27bb7224146269e1d1146d5164404118044b05d77713a93fffe92c</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">23ef0c2d25f05e88aafa11f1506ac17311dd3b44379dd6dff16dd062dfb37abd</span> |\n| Amazon Linux 2 | <span class=\"break-all\">2a7156d0bb4aede87d20784ce0de89c5ecdd35ce73b1fb4f6a2a35f72687973f</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors.\n\n- RS155734: Endpoint availability metrics do not work as expected due to a calculation error.\n\n- RS153589: The metrics stream engine preview reports incorrect latency metrics."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Upload modules before OS upgrade\n\nIf the cluster contains any databases that use modules, you must upload module packages for the target OS version to a node in the existing cluster before you upgrade the cluster's operating system.\n\nSee [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os) for detailed upgrade instructions.\n\n#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### RedisGraph prevents upgrade to RHEL 9 \n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Query results might include hash keys with lazily expired fields\n\nIf one or more fields of a hash key expire after an `FT.SEARCH` or `FT.AGGREGATE` query begins, Redis does not account for these lazily expired fields. As a result, keys with expired fields might still be included in the query results, leading to potentially incorrect or inconsistent results.\n\n#### Active defragmentation does not stop mid-key for JSON\n\nActive defragmentation does not stop mid-key for JSON data. Large keys are defragmented in full, which might cause latency spikes."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.22.0-95 supports open source Redis 7.4, 7.2, and 6.2. Below is the list of open source Redis CVEs fixed by version.\n\nRedis 7.4.x:\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\nRedis 7.2.x:\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-116","title":"Redis Software release notes 7.22.2-116 (April 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-116/","summary":"Bug fixes for node manager crashes after cluster upgrades and the creation of circular symlinks during cluster upgrades. Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"11aba2357772a42ebc18a6ddbaf60997aebe3d077cb416ae228e4807a6815400","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​​Redis Software version 7.22.2."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Bug fixes for node manager crashes after cluster upgrades and the creation of circular symlinks during cluster upgrades\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 7.22.2 includes three Redis database versions: 7.4.3, 7.2.7, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 7.4."},{"id":"redis-module-feature-sets","title":"Redis module feature sets","role":"content","text":"Redis Software comes packaged with several modules. As of version 7.22.0, Redis Software includes three feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)  |"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS177450: Fixed an issue that could cause `node_mgr FATAL` crashes after cluster upgrades.\n\n- RS182007: Modified the custom installer script to prevent the creation of circular symlinks during cluster upgrades."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.22.2-116 April release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">488f291a74283aba373ff818f129b528f0d14166e5a5bfeaff6fefde5479ae4b</span> |\n| Ubuntu 22 | <span class=\"break-all\">946869e8d37effd42a02ac4c271868320b602893728045e2e7f52d0164225ccc</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">f4902612838a50772ba8d880dbd5d065c14a5a4988014d9ac4df7739a119432c</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">fa3ceddb0bc5afea3154c942deb1f2feecf739d22ccfa8251cea2d79a9bef8e9</span> |\n| Amazon Linux 2 | <span class=\"break-all\">798da3fcec7d6fcfd863092f48594c6a3f5b2a8df7bde6b816d343a5dd79c080</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors.\n\n- RS155734: Endpoint availability metrics do not work as expected due to a calculation error.\n\n- RS153589: The metrics stream engine preview reports incorrect latency metrics."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Upload modules before OS upgrade\n\nIf the cluster contains any databases that use modules, you must upload module packages for the target OS version to a node in the existing cluster before you upgrade the cluster's operating system.\n\nSee [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os) for detailed upgrade instructions.\n\n#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### RedisGraph prevents upgrade to RHEL 9\n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Query results might include hash keys with lazily expired fields\n\nIf one or more fields of a hash key expire after an `FT.SEARCH` or `FT.AGGREGATE` query begins, Redis does not account for these lazily expired fields. As a result, keys with expired fields might still be included in the query results, leading to potentially incorrect or inconsistent results.\n\n#### Active defragmentation does not stop mid-key for JSON\n\nActive defragmentation does not stop mid-key for JSON data. Large keys are defragmented in full, which might cause latency spikes."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.22.2-116 supports open source Redis 7.4, 7.2, and 6.2. Below is the list of open source Redis CVEs and other security vulnerabilities fixed by version.\n\nRedis 7.4.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Restore invalid filter.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\nRedis 7.2.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Restore invalid filter.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Restore invalid filter.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-133","title":"Redis Software release notes 7.22.2-133 (May 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-133/","summary":"Bug fix for an issue where the Sentinel service could become unresponsive while processing certain commands due to a timing issue. Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"1f15dda13d46aa7884591275b2c8c51dba0845c10929bb619d8654691b7cda4e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​​Redis Software version 7.22.2."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Bug fix for an issue where the Sentinel service could become unresponsive while processing certain commands due to a timing issue\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 7.22.2 includes three Redis database versions: 7.4.3, 7.2.7, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 7.4."},{"id":"redis-module-feature-sets","title":"Redis module feature sets","role":"content","text":"Redis Software comes packaged with several modules. As of version 7.22.0, Redis Software includes three feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)  |"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS166825: Fixed an issue where the Sentinel service could become unresponsive while processing certain commands due to a timing issue."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.22.2-133 May release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">34edda5de9dc008f3790296b9ea50265afc182a68a1b9da6c726a80af2f40851</span> |\n| Ubuntu 22 | <span class=\"break-all\">63c528bdab51a5984e91324b7ed5f4649732ca3862f4ca638e1cfe167bab40e0</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">4a32620a5c4f49619defa2ed7e7d28e6d108f20ba1c0f3de4b36d36d4790364a</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">bdb0de61448c75e47947df0069f29ba6ef9420386b13affdb603a18edad532c1</span> |\n| Amazon Linux 2 | <span class=\"break-all\">24d55667d1c31cbb48a9cd747b51108bb62f643d448a9692bb4e651a330af21d</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors.\n\n- RS155734: Endpoint availability metrics do not work as expected due to a calculation error.\n\n- RS153589: The metrics stream engine preview reports incorrect latency metrics."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Upload modules before OS upgrade\n\nIf the cluster contains any databases that use modules, you must upload module packages for the target OS version to a node in the existing cluster before you upgrade the cluster's operating system.\n\nSee [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os) for detailed upgrade instructions.\n\n#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### RedisGraph prevents upgrade to RHEL 9\n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Query results might include hash keys with lazily expired fields\n\nIf one or more fields of a hash key expire after an `FT.SEARCH` or `FT.AGGREGATE` query begins, Redis does not account for these lazily expired fields. As a result, keys with expired fields might still be included in the query results, leading to potentially incorrect or inconsistent results.\n\n#### Active defragmentation does not stop mid-key for JSON\n\nActive defragmentation does not stop mid-key for JSON data. Large keys are defragmented in full, which might cause latency spikes."},{"id":"security","title":"Security","role":"security","text":"Redis Software 7.22.2-133 updates pyOpenSSL to 26.0.0, which fixes a buffer overflow in the DTLS cookie-generation callback (CVE-2026-27459 / GHSA-5pwr-322w-8jr4).\n\n#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.22.2-133 supports open source Redis 7.4, 7.2, and 6.2. Below is the list of open source Redis CVEs and other security vulnerabilities fixed by version.\n\nRedis 7.4.x:\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Restore invalid filter.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\nRedis 7.2.x:\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Restore invalid filter.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Restore invalid filter.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-14","title":"Redis Software release notes 7.22.2-14 (September 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-14/","summary":"Customer-managed certificates for internode encryption. Cluster Manager UI bug fixes for viewing shard memory usage metrics and editing Active-Active databases that have participating clusters with empty ports.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"773d9221dbbe6bb2b5b8d0d247f242bd995998b60472c1244841aafd85cddb47","sections":[{"id":"overview","title":"Overview","role":"overview","text":"​[​Redis Software version 7.22.2](https://redis.io/downloads/#software) is now available! This release includes API enhancements that warranted a new minor version instead of a maintenance release for version 7.22.0. However, you can upgrade from 7.22.0 to 7.22.2 without issue."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Customer-managed certificates for internode encryption\n\n- Cluster Manager UI bug fixes for viewing shard memory usage metrics and editing Active-Active databases that have participating clusters with empty ports"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"new-features","title":"New features","role":"content","text":"#### Customer-managed certificates for internode encryption\n\nInstead of using Redis Software's self-signed certificates for internode encryption, you can provide certificates generated by your Certificate Authority (CA). See [Customer-provided certificates ](https://redis.io/docs/latest/operate/rs/security/encryption/internode-encryption#customer-provided-certificates) for details."},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 7.22.2 includes three Redis database versions: 7.4.2, 7.2.7, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 7.4."},{"id":"redis-module-feature-sets","title":"Redis module feature sets","role":"content","text":"Redis Software comes packaged with several modules. As of version 7.22.0, Redis Software includes three feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)  |"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS165532: Fixed an issue that could prevent viewing shard memory usage metrics in the Cluster Manager UI and cause the page to fail to load when switching between database and shard metrics.\n\n- RS166270: Fixed an issue in the Cluster Manager UI that prevented editing Active-Active databases that have participating clusters with empty ports.\n\n- RS162972: Fixed an issue where the REST API was only accessible from the primary node when certificate-based authentication was enabled."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"deprecations","title":"Deprecations","role":"content","text":"#### API deprecations\n\n- Deprecated [`PUT /v1/cluster/update_cert`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/certificates#put-cluster-update_cert) REST API requests. Use [`PUT /v1/cluster/certificates`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/certificates#put-cluster-certificates) to update cluster certificates instead."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.22.2-14 September release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">20643eb1b112aad74af75ffdcac96fa4fa74a2ccb8778cf5498e79b4a4f44a27</span> |\n| Ubuntu 22 | <span class=\"break-all\">2d22f96005758f076af16d4e47da7407ec1a14a793460bf57eaeb41eb69bef19</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">ece47e14ae1129ae1bd8924510139461819ca31084e540b1b159d74668265ba2</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">160238efe7246276dc7e62f9aa05544516e615f3c791241bd87a455588f156c3</span> |\n| Amazon Linux 2 | <span class=\"break-all\">41d177582ff6f237441d2a5f5c121e69de6b5578ada6877f8cc4084a538cf498</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors.\n\n- RS155734: Endpoint availability metrics do not work as expected due to a calculation error.\n\n- RS153589: The metrics stream engine preview reports incorrect latency metrics."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Upload modules before OS upgrade\n\nIf the cluster contains any databases that use modules, you must upload module packages for the target OS version to a node in the existing cluster before you upgrade the cluster's operating system.\n\nSee [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os) for detailed upgrade instructions.\n\n#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### RedisGraph prevents upgrade to RHEL 9\n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Query results might include hash keys with lazily expired fields\n\nIf one or more fields of a hash key expire after an `FT.SEARCH` or `FT.AGGREGATE` query begins, Redis does not account for these lazily expired fields. As a result, keys with expired fields might still be included in the query results, leading to potentially incorrect or inconsistent results.\n\n#### Active defragmentation does not stop mid-key for JSON\n\nActive defragmentation does not stop mid-key for JSON data. Large keys are defragmented in full, which might cause latency spikes."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.22.2-14 supports open source Redis 7.4, 7.2, and 6.2. Below is the list of open source Redis CVEs and other security vulnerabilities fixed by version.\n\nRedis 7.4.x:\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\nRedis 7.2.x:\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-170","title":"Redis Software release notes 7.22.2-170 (July 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-170/","summary":"Improved proxy efficiency. Bug fix for a node join failures due to misidentified IPv6 addresses. Bug fix for -MOVED response metrics visibility. Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"66bf9b73aaf932a7bdf333f7f85f0dcad8e69abe01a60b9134839633f365257a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​​Redis Software version 7.22.2."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Improved proxy efficiency\n\n- Bug fix for a node join failures due to misidentified IPv6 addresses\n\n- Bug fix for `-MOVED` response metrics visibility\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"enhancements","title":"Enhancements","role":"content","text":"- Improved proxy efficiency by no longer forwarding `UNWATCH` to shards when the client has no active `WATCH`."},{"id":"redis-database-versions-and-feature-sets","title":"Redis database versions and feature sets","role":"content","text":"Redis Software version 7.22.2 includes three Redis database versions: 7.4.3, 7.2.7, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 7.4.\n\nRedis Software comes packaged with several modules. As of version 7.22.0, Redis Software includes three feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)  |"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS188659: Fixed an issue where IPv6 addresses that contain the substring `::1` could be misidentified as localhost loopback addresses and cause nodes to fail to join the cluster.\n\n- RS159953: Fixed an issue where `-MOVED` responses were counted as read or write operations instead of errors, so requests sent to the wrong shard or cluster slot were not visible as errors in database metrics."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.22.2-170 July release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">7b9f3ffec8b3efaf0a6e428b4ff1df868f72ac324159b9b00ad6791cee990fe4</span> |\n| Ubuntu 22 | <span class=\"break-all\">4f3ead59b00a290b1260f23d09cecf3da5866447dbb33ff2a4901e39a44d4521</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">343a495260aa938160e200be3054965a3e20caa5637246fe2157391fbbb617bc</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">1ae4157778a17fbfd21a8b4cd4d1200a7291e87dafa89adc25e7064821accea5</span> |\n| Amazon Linux 2 | <span class=\"break-all\">538aadc0753d120fba0173ad711dd1afff500f5cc488d5b554c6f9e7e5aa9412</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors.\n\n- RS155734: Endpoint availability metrics do not work as expected due to a calculation error."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Upload modules before OS upgrade\n\nIf the cluster contains any databases that use modules, you must upload module packages for the target OS version to a node in the existing cluster before you upgrade the cluster's operating system.\n\nSee [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os) for detailed upgrade instructions.\n\n#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### RedisGraph prevents upgrade to RHEL 9\n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Query results might include hash keys with lazily expired fields\n\nIf one or more fields of a hash key expire after an `FT.SEARCH` or `FT.AGGREGATE` query begins, Redis does not account for these lazily expired fields. As a result, keys with expired fields might still be included in the query results, leading to potentially incorrect or inconsistent results.\n\n#### Active defragmentation does not stop mid-key for JSON\n\nActive defragmentation does not stop mid-key for JSON data. Large keys are defragmented in full, which might cause latency spikes."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.22.2-170 supports open source Redis 7.4, 7.2, and 6.2. Below is the list of open source Redis CVEs and other security vulnerabilities fixed by version.\n\n\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Restore invalid filter.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n\n\n\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Restore invalid filter.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\n\n\n\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\n\n\n\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Restore invalid filter.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-179","title":"Redis Software release notes 7.22.2-179 (August 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-179/","summary":"Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"a102b58105db92ca55518cae096060c77fe4051530816e5a973209b5ca6390ae","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​​Redis Software version 7.22.2.\n\nThis version offers internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-database-versions-and-feature-sets","title":"Redis database versions and feature sets","role":"content","text":"Redis Software version 7.22.2 includes three Redis database versions: 7.4.3, 7.2.7, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 7.4.\n\nRedis Software comes packaged with several modules. As of version 7.22.0, Redis Software includes three feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)  |"},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.22.2-179 August release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">34173d5bb0eaf1e903d06f622d046093dc821edb341623258ae646b117eebc4f</span> |\n| Ubuntu 22 | <span class=\"break-all\">87ae322ed02c913f9515e588604efa416f75a4be6726a0940cb5832afd7c7d3e</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">2eee1ae13d8961b98968629ac041a7e46f3871eb41375a87179e6c8384f6509b</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">d7a544a512e73417ff77cb1fd1f647a87a438f651ee2cf684a5f5f9f80f75d4e</span> |\n| Amazon Linux 2 | <span class=\"break-all\">eef548f0e1dd422a210ef9931406e3b7df91e0f19536873f129ee8435b281df5</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors.\n\n- RS155734: Endpoint availability metrics do not work as expected due to a calculation error."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Upload modules before OS upgrade\n\nIf the cluster contains any databases that use modules, you must upload module packages for the target OS version to a node in the existing cluster before you upgrade the cluster's operating system.\n\nSee [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os) for detailed upgrade instructions.\n\n#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### RedisGraph prevents upgrade to RHEL 9\n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Query results might include hash keys with lazily expired fields\n\nIf one or more fields of a hash key expire after an `FT.SEARCH` or `FT.AGGREGATE` query begins, Redis does not account for these lazily expired fields. As a result, keys with expired fields might still be included in the query results, leading to potentially incorrect or inconsistent results.\n\n#### Active defragmentation does not stop mid-key for JSON\n\nActive defragmentation does not stop mid-key for JSON data. Large keys are defragmented in full, which might cause latency spikes."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.22.2-179 supports open source Redis 7.4, 7.2, and 6.2. Below is the list of open source Redis CVEs and other security vulnerabilities fixed by version.\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Restore invalid filter.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Restore invalid filter.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\n\n\n\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Restore invalid filter.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-189","title":"Redis Software release notes 7.22.2-189 (September 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-189/","summary":"Added scoping for shards and database endpoints. Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"46ce18eeace98bec9edbe53763b813311db144b6115e2d7f248e9c0ba28aa640","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​​Redis Software version 7.22.2."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Scoping for shards and database endpoints\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"enhancements","title":"Enhancements","role":"content","text":"- Added scoping for shards and database endpoints."},{"id":"redis-database-versions-and-feature-sets","title":"Redis database versions and feature sets","role":"content","text":"Redis Software version 7.22.2 includes three Redis database versions: 7.4.3, 7.2.7, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 7.4.\n\nRedis Software comes packaged with several modules. As of version 7.22.0, Redis Software includes three feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)  |"},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.22.2-189 September release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">d1de686ffcea860f8a21e81f4da737afd3efb8ee5c3607528615a273c2c2c799</span> |\n| Ubuntu 22 | <span class=\"break-all\">e117a6aaf763528df46fb31aaa1d83d3da63b8652be3c46f679e942b03891022</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">99bf1331cd4053261ddff336f6a7dc8c0e9b98046b5e251d75e627436bc5c051</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">50564221ebaa966a79951d39f051b42716e5afa429521a85580df4b111b60301</span> |\n| Amazon Linux 2 | <span class=\"break-all\">dd608bb54c3749ad3aef086028222b725f48695e1c4bf2f463520f742f6e8689</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors.\n\n- RS155734: Endpoint availability metrics do not work as expected due to a calculation error."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Upload modules before OS upgrade\n\nIf the cluster contains any databases that use modules, you must upload module packages for the target OS version to a node in the existing cluster before you upgrade the cluster's operating system.\n\nSee [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os) for detailed upgrade instructions.\n\n#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### RedisGraph prevents upgrade to RHEL 9\n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Query results might include hash keys with lazily expired fields\n\nIf one or more fields of a hash key expire after an `FT.SEARCH` or `FT.AGGREGATE` query begins, Redis does not account for these lazily expired fields. As a result, keys with expired fields might still be included in the query results, leading to potentially incorrect or inconsistent results.\n\n#### Active defragmentation does not stop mid-key for JSON\n\nActive defragmentation does not stop mid-key for JSON data. Large keys are defragmented in full, which might cause latency spikes."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.22.2-189 supports open source Redis 7.4, 7.2, and 6.2. Below is the list of open source Redis CVEs and other security vulnerabilities fixed by version.\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Restore invalid filter.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Restore invalid filter.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\n\n\n\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Restore invalid filter.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-20","title":"Redis Software release notes 7.22.2-20 (October 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-20/","summary":"Cluster Manager UI bug fixes for issues with S3 periodic backups configuration and connection issues. API bug fixes for omitted default values in responses and Active-Active database creation failures.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"9265b04b34dc6d6d9b3fe8a9a0f86a660a5576cf01573e3a84a7e10597718033","sections":[{"id":"overview","title":"Overview","role":"overview","text":"​[​Redis Software version 7.22.2](https://redis.io/downloads/#Redis_Software) is now available!"},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Cluster Manager UI bug fixes for issues with S3 periodic backups configuration and connection issues\n\n- API bug fixes for omitted default values in responses and Active-Active database creation failures"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 7.22.2 includes three Redis database versions: 7.4.3, 7.2.7, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 7.4."},{"id":"redis-module-feature-sets","title":"Redis module feature sets","role":"content","text":"Redis Software comes packaged with several modules. As of version 7.22.0, Redis Software includes three feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)  |"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS107596: Fixed an issue where REST API responses did not include configuration fields that use default values.\n\n- RS171579: Fixed an issue where the new UI incorrectly added `default_user: False` when the `default_user` field was absent, causing connection issues.\n\n- RS171170: Fixed an issue where S3 periodic backups could not be configured or adjusted from the Cluster Manager UI.\n\n- RS171560: Fixed an issue where creating an Active-Active database with a CRDB REST API request could mistakenly fail when using valid `X-Result-TTL` header values."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.22.2-20 October release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">35c34a5244f2001764ca717fd82c51717676bdb40893db4f4029099dd90381ae</span> |\n| Ubuntu 22 | <span class=\"break-all\">88e453047c3137aba077931add184ad9577f1c3f1ae4098278b6159ce6a87583</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">491f255bdc764505e93f71a9765a67d3c8ad1024ef9b7e689248fff41065d2ac</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">8650a9d142d639f822c7281e5356391fb496e237945b374ba29796cd698790cf</span> |\n| Amazon Linux 2 | <span class=\"break-all\">33f3c83bc89504a5ef4d98b378829ec7f53fe5ac1e287f75c9af39de4cdf8031</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors.\n\n- RS155734: Endpoint availability metrics do not work as expected due to a calculation error.\n\n- RS153589: The metrics stream engine preview reports incorrect latency metrics."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Upload modules before OS upgrade\n\nIf the cluster contains any databases that use modules, you must upload module packages for the target OS version to a node in the existing cluster before you upgrade the cluster's operating system.\n\nSee [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os) for detailed upgrade instructions.\n\n#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### RedisGraph prevents upgrade to RHEL 9\n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Query results might include hash keys with lazily expired fields\n\nIf one or more fields of a hash key expire after an `FT.SEARCH` or `FT.AGGREGATE` query begins, Redis does not account for these lazily expired fields. As a result, keys with expired fields might still be included in the query results, leading to potentially incorrect or inconsistent results.\n\n#### Active defragmentation does not stop mid-key for JSON\n\nActive defragmentation does not stop mid-key for JSON data. Large keys are defragmented in full, which might cause latency spikes."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.22.2-20 supports open source Redis 7.4, 7.2, and 6.2. Below is the list of open source Redis CVEs and other security vulnerabilities fixed by version.\n\nRedis 7.4.x:\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\nRedis 7.2.x:\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-204","title":"Redis Software release notes 7.22.2-204 (October 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-204/","summary":"Bug fixes for DMC proxy stability. Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-10-19T14:33:15-05:00","page_type":"content","content_hash":"ef9c57ef9271202be1685a468501e7e37f1c50dab8ec5c2c1598519bf48697f1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​​Redis Software version 7.22.2."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Bug fixes for DMC proxy stability\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-database-versions-and-feature-sets","title":"Redis database versions and feature sets","role":"content","text":"Redis Software version 7.22.2 includes three Redis database versions: 7.4.3, 7.2.7, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 7.4.\n\nRedis Software comes packaged with several modules. As of version 7.22.0, Redis Software includes three feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)  |"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS216045: Fixed an issue where the DMC proxy could crash repeatedly when a client sent a command without key arguments, such as `PUBSUB SHARDNUMSUB` with no channels, in a pipeline with another command.\n\n- RS215608: Fixed an issue where running the `dmc-cli` `list bdb` or `list server` commands could cause the proxy to crash."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.22.2-204 October release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">5fa0b0cadf37596b93e9bbea24127eb7096073cccc6895e14c5cee3ea585f475</span> |\n| Ubuntu 22 | <span class=\"break-all\">8893a1aea58a7fbee4bacb940ce9e1c652f05e6f3b08fa20251a01fe50324fc3</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">81e35b0034daaeba41cbab05485e4db6e11b7b409522cba1597cc26553e63407</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">fb086b134e9b09bc08b840f051031310c7828a65ae1e20467ad1093cf2375e75</span> |\n| Amazon Linux 2 | <span class=\"break-all\">80b4cef04796c5415f82532b409811e7becb7b66e79580b33a9b27b4293060b7</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors.\n\n- RS155734: Endpoint availability metrics do not work as expected due to a calculation error."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Upload modules before OS upgrade\n\nIf the cluster contains any databases that use modules, you must upload module packages for the target OS version to a node in the existing cluster before you upgrade the cluster's operating system.\n\nSee [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os) for detailed upgrade instructions.\n\n#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### RedisGraph prevents upgrade to RHEL 9\n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Query results might include hash keys with lazily expired fields\n\nIf one or more fields of a hash key expire after an `FT.SEARCH` or `FT.AGGREGATE` query begins, Redis does not account for these lazily expired fields. As a result, keys with expired fields might still be included in the query results, leading to potentially incorrect or inconsistent results.\n\n#### Active defragmentation does not stop mid-key for JSON\n\nActive defragmentation does not stop mid-key for JSON data. Large keys are defragmented in full, which might cause latency spikes."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.22.2-204 supports open source Redis 7.4, 7.2, and 6.2. Below is the list of open source Redis CVEs and other security vulnerabilities fixed by version.\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Restore invalid filter.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Restore invalid filter.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\n\n\n\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Restore invalid filter.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-41","title":"Redis Software release notes 7.22.2-41 (November 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-41/","summary":"Bug fixes for `cluster_wd` configuration after restarts and internode encryption certificate storage and retrieval.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"46a0f120a446652eb4c49329b0619087bddfa22aad76f13d3f41441daa4a4c3f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​​Redis Software version 7.22.2."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Bug fixes for `cluster_wd` configuration after restarts and internode encryption certificate storage and retrieval."},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 7.22.2 includes three Redis database versions: 7.4.3, 7.2.7, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 7.4."},{"id":"redis-module-feature-sets","title":"Redis module feature sets","role":"content","text":"Redis Software comes packaged with several modules. As of version 7.22.0, Redis Software includes three feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)  |"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS176280: Fixed an issue where `cluster_wd` reverted to the default configuration after restarting.\n\n- RS176540: Fixed inconsistencies between storage and retrieval APIs for internode encryption certificates, which could cause issues in Kubernetes deployments."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.22.2-41 November release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">a05b60e6204768eae6f26dc3b6cb11651fdbfb406b3938afb3f63eed73b35828</span> |\n| Ubuntu 22 | <span class=\"break-all\">bef8a0f3108b0f9cbcd438efd8f62942117e15843c9b93823625346571427da9</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">b09258cc5fab234888de3bb4cff0732d166ab5b32e657591e592071c18d97dcb</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">560b12262293f5bf74694d8ea311208549b47b5912389b852d46cf730eb87c02</span> |\n| Amazon Linux 2 | <span class=\"break-all\">0c6a974fd7b1bab1befe0c6dbcecabfaac07097fd0b74717a25f4d53419ffd9c</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors.\n\n- RS155734: Endpoint availability metrics do not work as expected due to a calculation error.\n\n- RS153589: The metrics stream engine preview reports incorrect latency metrics."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Upload modules before OS upgrade\n\nIf the cluster contains any databases that use modules, you must upload module packages for the target OS version to a node in the existing cluster before you upgrade the cluster's operating system.\n\nSee [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os) for detailed upgrade instructions.\n\n#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### RedisGraph prevents upgrade to RHEL 9\n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Query results might include hash keys with lazily expired fields\n\nIf one or more fields of a hash key expire after an `FT.SEARCH` or `FT.AGGREGATE` query begins, Redis does not account for these lazily expired fields. As a result, keys with expired fields might still be included in the query results, leading to potentially incorrect or inconsistent results.\n\n#### Active defragmentation does not stop mid-key for JSON\n\nActive defragmentation does not stop mid-key for JSON data. Large keys are defragmented in full, which might cause latency spikes."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.22.2-41 supports open source Redis 7.4, 7.2, and 6.2. Below is the list of open source Redis CVEs and other security vulnerabilities fixed by version.\n\nRedis 7.4.x:\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\nRedis 7.2.x:\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-55","title":"Redis Software release notes 7.22.2-55 (January 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-55/","summary":"Bug fixes for configuring backups and memory limits in the Cluster Manager UI, and database metrics collection.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"1dac7f56eaecb2919ef247013616fbe239e16a44ca0bda6b95911e98d84015ee","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​​Redis Software version 7.22.2."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Bug fixes for configuring backups and memory limits in the Cluster Manager UI, and database metrics collection"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 7.22.2 includes three Redis database versions: 7.4.3, 7.2.7, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 7.4."},{"id":"redis-module-feature-sets","title":"Redis module feature sets","role":"content","text":"Redis Software comes packaged with several modules. As of version 7.22.0, Redis Software includes three feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)  |"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS176400: Fixed an issue where Google Cloud Storage backup locations could be set to the incorrect type when configured in the Cluster Manager UI.\n\n- RS179215: Fixed a validation issue in the Cluster Manager UI that could prevent increasing memory limits for flash-enabled databases despite sufficient cluster resources.\n\n- RS171771: Fixed an issue in the metrics exporter that could cause missing database metrics after cluster recovery."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.22.2-55 January release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">4f2e5ecd9129426062e1252e972269f98f158d2fe0f9efe91892562a781fa08c</span> |\n| Ubuntu 22 | <span class=\"break-all\">ad4553d6a1de82cc1f892d4a37b2bc54a6dff8e2c7ec92f0b403edb10d46ab6e</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">226583c18002ec6ef3cccdd275b32e05d65656d44b3af1875cbee8d04cb1bc3c</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">264bba1c0d84f8fbbd7a30bc791371b5593d1abeed4dddc9910495dab3a197e3</span> |\n| Amazon Linux 2 | <span class=\"break-all\">f9f2d0cc9b04c15bf9fdc66a19768fa8bbfdb0ea24aa20004b6efdc1195c1380</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors.\n\n- RS155734: Endpoint availability metrics do not work as expected due to a calculation error.\n\n- RS153589: The metrics stream engine preview reports incorrect latency metrics."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Upload modules before OS upgrade\n\nIf the cluster contains any databases that use modules, you must upload module packages for the target OS version to a node in the existing cluster before you upgrade the cluster's operating system.\n\nSee [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os) for detailed upgrade instructions.\n\n#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### RedisGraph prevents upgrade to RHEL 9\n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Query results might include hash keys with lazily expired fields\n\nIf one or more fields of a hash key expire after an `FT.SEARCH` or `FT.AGGREGATE` query begins, Redis does not account for these lazily expired fields. As a result, keys with expired fields might still be included in the query results, leading to potentially incorrect or inconsistent results.\n\n#### Active defragmentation does not stop mid-key for JSON\n\nActive defragmentation does not stop mid-key for JSON data. Large keys are defragmented in full, which might cause latency spikes."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.22.2-55 supports open source Redis 7.4, 7.2, and 6.2. Below is the list of open source Redis CVEs and other security vulnerabilities fixed by version.\n\nRedis 7.4.x:\n\n- RedisBloom: Restore invalid filter.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\nRedis 7.2.x:\n\n- RedisBloom: Restore invalid filter.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- RedisBloom: Restore invalid filter.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-79","title":"Redis Software release notes 7.22.2-79 (February 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-79/","summary":"Bug fixes for configuration updates on promoted shards after failover, database recovery, certificate validation, and database availability API responses.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"41d6129066adfeb9663babf4aef5f73f49ff621a168d0bf4273e224f76a5f792","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​​Redis Software version 7.22.2."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Bug fixes for configuration updates on promoted shards after failover, database recovery, certificate validation, and database availability API responses"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 7.22.2 includes three Redis database versions: 7.4.3, 7.2.7, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 7.4."},{"id":"redis-module-feature-sets","title":"Redis module feature sets","role":"content","text":"Redis Software comes packaged with several modules. As of version 7.22.0, Redis Software includes three feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)  |"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS165065: Fixed an issue with configuration updates on promoted shards after failover if the target node was temporarily unresponsive.\n\n- RS173258: Fixed an issue where database recovery could fail during cluster recovery due to a missing state handler.\n\n- RS176474: Fixed an issue where clusters could fail after uploading internal certificates that lacked the \"TLS Web Client Authentication\" option. The system now validates and rejects certificates without this required option during upload to prevent cluster failures.\n\n- RS182423: Fixed an issue where database availability REST API requests to secondary nodes returned 307 Temporary Redirect status codes instead of the expected availability status."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.22.2-79 February release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">5ea57fc7157220536bf384822271bfa7eb60e5b323a1442379b84d55d8fcfdc6</span> |\n| Ubuntu 22 | <span class=\"break-all\">92742b84a5497d67da3ba97be2718b6fabaa08a52b0a6d0d174db02657d5fa00</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">d2a5b09487450857cca1a313641fb1bd98e867db596c270351783274ca9c09ea</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">f2302b1d608141e7333bf222f56abf8970ddc803f80b433b2b07ba9d0e4b87bf</span> |\n| Amazon Linux 2 | <span class=\"break-all\">9457ac526814965648e7bb7d435cc33103cc8d6fd87535888ae1e4015df87b46</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors.\n\n- RS155734: Endpoint availability metrics do not work as expected due to a calculation error.\n\n- RS153589: The metrics stream engine preview reports incorrect latency metrics."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Upload modules before OS upgrade\n\nIf the cluster contains any databases that use modules, you must upload module packages for the target OS version to a node in the existing cluster before you upgrade the cluster's operating system.\n\nSee [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os) for detailed upgrade instructions.\n\n#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### RedisGraph prevents upgrade to RHEL 9\n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Query results might include hash keys with lazily expired fields\n\nIf one or more fields of a hash key expire after an `FT.SEARCH` or `FT.AGGREGATE` query begins, Redis does not account for these lazily expired fields. As a result, keys with expired fields might still be included in the query results, leading to potentially incorrect or inconsistent results.\n\n#### Active defragmentation does not stop mid-key for JSON\n\nActive defragmentation does not stop mid-key for JSON data. Large keys are defragmented in full, which might cause latency spikes."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.22.2-79 supports open source Redis 7.4, 7.2, and 6.2. Below is the list of open source Redis CVEs and other security vulnerabilities fixed by version.\n\nRedis 7.4.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Restore invalid filter.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\nRedis 7.2.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Restore invalid filter.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Restore invalid filter.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-93","title":"Redis Software release notes 7.22.2-93 (March 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-22-releases/rs-7-22-2-93/","summary":"Bug fix for flash database percent capacity displayed in the Cluster Manager UI. Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"ad94577e78ced8609143fa8cff043f941e8c38b5cd5ab766b6146ce5f64c7b03","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​​Redis Software version 7.22.2."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- A bug fix for flash database percent capacity displayed in the Cluster Manager UI\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 7.22.2 includes three Redis database versions: 7.4.3, 7.2.7, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 7.4."},{"id":"redis-module-feature-sets","title":"Redis module feature sets","role":"content","text":"Redis Software comes packaged with several modules. As of version 7.22.0, Redis Software includes three feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)  |"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS176173: Fixed an issue that could cause the Cluster Manager UI to display over 100% capacity for flash databases, even when memory usage was under the configured limit."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.22.2-93 March release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">08fe22b8ccb2e06c7e960fa15880e1f0ef421a3f43e63823155637b84220c866</span> |\n| Ubuntu 22 | <span class=\"break-all\">efdc33d7c5566e4ecb0f4f0715f55cdcfcb56f8c187fcfc52a4a7397dec999fa</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">92d60a4405638b049706f939499b13298f31504b7bd647c03e4ebbf112bcb2d4</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">0d99ce90b9d4891304aa5c98663ab0d1a040bf6b3aa2e7ec0e577551727174a2</span> |\n| Amazon Linux 2 | <span class=\"break-all\">90e732ed482d4243057464a89812afbabbfcbbf8a0df1a9c0765fa5dd27f7220</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors.\n\n- RS155734: Endpoint availability metrics do not work as expected due to a calculation error.\n\n- RS153589: The metrics stream engine preview reports incorrect latency metrics."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Upload modules before OS upgrade\n\nIf the cluster contains any databases that use modules, you must upload module packages for the target OS version to a node in the existing cluster before you upgrade the cluster's operating system.\n\nSee [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os) for detailed upgrade instructions.\n\n#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### RedisGraph prevents upgrade to RHEL 9\n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Query results might include hash keys with lazily expired fields\n\nIf one or more fields of a hash key expire after an `FT.SEARCH` or `FT.AGGREGATE` query begins, Redis does not account for these lazily expired fields. As a result, keys with expired fields might still be included in the query results, leading to potentially incorrect or inconsistent results.\n\n#### Active defragmentation does not stop mid-key for JSON\n\nActive defragmentation does not stop mid-key for JSON data. Large keys are defragmented in full, which might cause latency spikes."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.22.2-93 supports open source Redis 7.4, 7.2, and 6.2. Below is the list of open source Redis CVEs and other security vulnerabilities fixed by version.\n\nRedis 7.4.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Restore invalid filter.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\nRedis 7.2.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Restore invalid filter.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Restore invalid filter.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-4-2-releases","title":"Redis Enterprise Software release notes 7.4.x","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/","summary":"New Cluster Manager UI enhancements, including Active-Active database management. Full TLS 1.3 support. Automatic recovery configuration. Full IPv6 support, including for internal traffic. Maintenance mode enhancements. Module management enhancements. RHEL 9 support.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","children":[{"id":"operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-293","summary":"Ubuntu 18.04 is no longer supported. Internal fixes and improvements.","title":"Redis Software release notes 7.4.6-293 (October 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-293/"},{"id":"operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-283","summary":"Internal fixes and improvements.","title":"Redis Software release notes 7.4.6-283 (August 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-283/"},{"id":"operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-280","summary":"Internal fixes and improvements.","title":"Redis Software release notes 7.4.6-280 (June 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-280/"},{"id":"operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-279","summary":"Internal fixes and improvements.","title":"Redis Software release notes 7.4.6-279 (February 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-279/"},{"id":"operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-272","summary":"Internal fixes and improvements.","title":"Redis Enterprise Software release notes 7.4.6-272 (September 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-272/"},{"id":"operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-268","summary":"Internal fixes and improvements.","title":"Redis Enterprise Software release notes 7.4.6-268 (June 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-268/"},{"id":"operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-232","summary":"Internal fixes and improvements.","title":"Redis Enterprise Software release notes 7.4.6-232 (March 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-232/"},{"id":"operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-188","summary":"Bug fix for a memory leak in the DMC proxy process.","title":"Redis Enterprise Software release notes 7.4.6-188 (January 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-188/"},{"id":"operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-102","summary":"Updated module feature sets with later versions of RediSearch and RedisTimeSeries.","title":"Redis Enterprise Software release notes 7.4.6-102 (October 2024)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-102/"},{"id":"operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-77","summary":"Updated module feature sets. Bug fixes.","title":"Redis Enterprise Software release notes 7.4.6-77 (September 2024)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-77/"},{"id":"operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-22","summary":"Support for SHA-384 certificates in client authentication. New parameters for optimize_shard_placement and recover database REST API requests.","title":"Redis Enterprise Software release notes 7.4.6-22 (July 2024)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-22/"},{"id":"operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-216","summary":"region_name field for new AWS S3 regions. Bug fixes.","title":"Redis Enterprise Software release notes 7.4.2-216 (July 2024)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-216/"},{"id":"operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-169","summary":"Configuration options for cluster management API workers and threads. Reduced Cluster Configuration Store CPU usage.","title":"Redis Enterprise Software release notes 7.4.2-169 (May 2024)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-169/"},{"id":"operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-129","summary":"Fixes for bugs and known limitations that affect Redis Enterprise Software v7.4.2-126.","title":"Redis Enterprise Software release notes 7.4.2-129 (April 2024)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-129/"},{"id":"operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-126","summary":"Redis database version selection during database creation in the Cluster Manager UI. New Redis logo.","title":"Redis Enterprise Software release notes 7.4.2-126 (April 2024)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-126/"},{"id":"operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-104","summary":"New Cluster Manager UI enhancements to change passwords from the sign-in screen, view locked user accounts, and unlock user accounts with password reset.","title":"Redis Enterprise Software release notes 7.4.2-104 (March 2024)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-104/"},{"id":"operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-54","summary":"New Cluster Manager UI enhancements, including Active-Active database management. Full TLS 1.3 support. Automatic recovery configuration. Full IPv6 support, including for internal traffic. Maintenance mode enhancements. Module management enhancements. RHEL 9 support.","title":"Redis Enterprise Software release notes 7.4.2-54 (February 2024)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-54/"}],"page_type":"content","content_hash":"f5db02ed167f2af15e1e857e8ed9a55aca52d1ae9eccf039a187e7d4a825aef1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!NOTE]\n> Redis Software 7.4 reaches end of life on November 30, 2026. After that date,\n> 7.4.x clusters no longer receive security patches, bug fixes, or maintenance\n> releases, and support for 7.4-specific issues is limited per your subscription\n> agreement.\n>\n> Plan your upgrade to a supported version. See the\n> [supported upgrade paths](https://redis.io/docs/latest/operate/rs/references/upgrade-paths)\n> and the [Redis Software product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule).\n\n​[​Redis Enterprise Software version 7.4](https://redis.io/downloads/#software) is now available!"},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n \n- New Cluster Manager UI enhancements, including Active-Active database management\n\n- Full TLS 1.3 support\n\n- Automatic recovery configuration\n\n- Full IPv6 support, including for internal traffic\n\n- Maintenance mode enhancements\n\n- Module management enhancements\n\n- RHEL 7 and Oracle Linux 7, which were previously deprecated, are no longer supported\n\n- RHEL 9 support"},{"id":"detailed-release-notes","title":"Detailed release notes","role":"content","text":"For more detailed release notes, select a build version from the following table:\n\n\n| Version (Release date) | Major changes | OSS Redis compatibility |\n| --- | --- | --- |\n| [7.4.6-293 (October 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-293/) | Ubuntu 18.04 is no longer supported. Internal fixes and improvements. | Redis 7.2.4, 6.2.13, 6.0.20 |\n| [7.4.6-283 (August 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-283/) | Internal fixes and improvements. | Redis 7.2.4, 6.2.13, 6.0.20 |\n| [7.4.6-280 (June 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-280/) | Internal fixes and improvements. | Redis 7.2.4, 6.2.13, 6.0.20 |\n| [7.4.6-279 (February 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-279/) | Internal fixes and improvements. | Redis 7.2.4, 6.2.13, 6.0.20 |\n| [7.4.6-272 (September 2025)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-272/) | Internal fixes and improvements. | Redis 7.2.4, 6.2.13, 6.0.20 |\n| [7.4.6-268 (June 2025)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-268/) | Internal fixes and improvements. | Redis 7.2.4, 6.2.13, 6.0.20 |\n| [7.4.6-232 (March 2025)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-232/) | Internal fixes and improvements. | Redis 7.2.4, 6.2.13, 6.0.20 |\n| [7.4.6-188 (January 2025)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-188/) | Bug fix for a memory leak in the DMC proxy process. | Redis 7.2.4, 6.2.13, 6.0.20 |\n| [7.4.6-102 (October 2024)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-102/) | Updated module feature sets with later versions of RediSearch and RedisTimeSeries. | Redis 7.2.4, 6.2.13, 6.0.20 |\n| [7.4.6-77 (September 2024)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-77/) | Updated module feature sets. Bug fixes. | Redis 7.2.4, 6.2.13, 6.0.20 |\n| [7.4.6-22 (July 2024)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-22/) | Support for SHA-384 certificates in client authentication. New parameters for optimize_shard_placement and recover database REST API requests. | Redis 7.2.4, 6.2.13, 6.0.20 |\n| [7.4.2-216 (July 2024)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-216/) | region_name field for new AWS S3 regions. Bug fixes. | Redis 7.2.4, 6.2.13, 6.0.20 |\n| [7.4.2-169 (May 2024)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-169/) | Configuration options for cluster management API workers and threads. Reduced Cluster Configuration Store CPU usage. | Redis 7.2.4, 6.2.13, 6.0.20 |\n| [7.4.2-129 (April 2024)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-129/) | Fixes for bugs and known limitations that affect Redis Enterprise Software v7.4.2-126. | Redis 7.2.3, 6.2.13, 6.0.20 |\n| [7.4.2-126 (April 2024)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-126/) | Redis database version selection during database creation in the Cluster Manager UI. New Redis logo. | Redis 7.2.3, 6.2.13, 6.0.20 |\n| [7.4.2-104 (March 2024)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-104/) | New Cluster Manager UI enhancements to change passwords from the sign-in screen, view locked user accounts, and unlock user accounts with password reset. | Redis 7.2.3, 6.2.13, 6.0.20 |\n| [7.4.2-54 (February 2024)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-54/) | New Cluster Manager UI enhancements, including Active-Active database management. Full TLS 1.3 support. Automatic recovery configuration. Full IPv6 support, including for internal traffic. Maintenance mode enhancements. Module management enhancements. RHEL 9 support. | Redis 7.2.3, 6.2.13, 6.0.20 |"},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"product-lifecycle-updates","title":"Product lifecycle updates","role":"content","text":"#### End-of-life policy extension\n\nThe end-of-life policy for Redis Enterprise Software versions 6.2 and later has been extended to 24 months after the formal release of the subsequent major version. For the updated end-of-life schedule, see the [Redis Enterprise Software product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle).\n\n#### Supported upgrade paths\n\nRedis Enterprise Software versions 6.2.4 and 6.2.8 do not support direct upgrades beyond version 7.4.x. Versions 6.2.10, 6.2.12, and 6.2.18 are part of the [upgrade path](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-cluster#supported-upgrade-paths). To upgrade from 6.2.4 or 6.2.8 to versions later than 7.4.x, an intermediate upgrade is required.\n\nThe next major Redis Enterprise Software release will still bundle Redis database version 6.2 and allow database upgrades from Redis database version 6.2 to 7.x.\n\nSee the [Redis Enterprise Software product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle) for more information about release numbers."},{"id":"deprecations","title":"Deprecations","role":"content","text":"#### API deprecations\n\n- The replica HA cluster policy is deprecated.\n\n- The maintenance mode option `keep_slave_shards` is deprecated in `rladmin` and the REST API. Use `evict_ha_replica` and `evict_active_active_replica` instead.\n\n- `/v1/debuginfo` REST API paths are deprecated. Use the new paths [`/v1/cluster/debuginfo`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/debuginfo), [`/v1/nodes/debuginfo`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/nodes/debuginfo), and [`/v1/bdbs/debuginfo`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/debuginfo) instead.\n\n#### Legacy UI deprecation\n\nThe legacy UI is deprecated in favor of the new Cluster Manager UI and will be removed in a future release.\n\n#### Redis 6.0 database deprecation\n\nRedis database version 6.0 is deprecated as of Redis Enterprise Software version 7.4.2 and will be removed in a future release.\n\nTo prepare for the future removal of Redis 6.0:\n\n- For Redis Enterprise 6.2.* clusters, upgrade Redis 6.0 databases to Redis 6.2. See the [Redis 6.2 release notes](https://raw.githubusercontent.com/redis/redis/6.2/00-RELEASENOTES) for the list of changes.\n\n- For Redis Enterprise 7.2.4 and 7.4.x clusters, upgrade Redis 6.0 databases to Redis 7.2. Before you upgrade your databases, see the list of [Redis 7.2 breaking changes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52#redis-72-breaking-changes) and update any applications that connect to your database to handle these changes.\n\n#### End of triggers and functions preview\n\nThe [triggers and functions](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions) (RedisGears) preview has been discontinued.\n\n- Commands such as `TFCALL`, `TFCALLASYNC`, and `TFUNCTION` will be deprecated and will return error messages.\n\n- Any JavaScript functions stored in Redis will be removed. \n\n- JavaScript-based triggers will be blocked.\n\n- Lua functions and scripts will not be affected.\n\nIf your database currently uses triggers and functions, you need to: \n\n1. Adjust your applications to accommodate these changes.\n\n1. Delete all triggers and functions libraries from your existing database:\n\n    1. Run `TFUNCTION LIST`.\n\n    1. Copy all library names.\n\n    1. Run `TFUNCTION DELETE` for each library in the list.\n\n    If any triggers and functions libraries remain in the database, the RDB snapshot won't load on a cluster without RedisGears.\n\n1. Migrate your database to a new database without the RedisGears module.\n\n#### RedisGraph end of life\n\nRedis has announced the end of life for RedisGraph. Redis will continue to support all RedisGraph customers, including releasing patch versions until January 31, 2025.\n\nSee the [RedisGraph end-of-life announcement](https://redis.com/blog/redisgraph-eol/) for more details.\n\n#### Operating system retirements\n\n- RHEL 7 and Oracle Linux 7 were previously announced as deprecated in the [Redis Enterprise Software 7.2.4 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases#deprecations). As of Redis Enterprise Software 7.4.2, RHEL 7 and Oracle Linux 7 are no longer supported.\n\n#### Security retirements\n\n- The RC4 encryption cipher, which was previously deprecated in favor of stronger ciphers, is no longer supported.\n\n- The 3DES encryption cipher, which was previously deprecated in favor of stronger ciphers like AES, is no longer supported. Verify that all clients, applications, and connections support the AES cipher.\n\n- TLS 1.0 and TLS 1.1 connections, which were previously deprecated in favor of TLS 1.2 or later, are no longer supported. Verify that all clients, applications, and connections support TLS 1.2 or later."},{"id":"upcoming-changes","title":"Upcoming changes","role":"content","text":"#### Default image change for Redis Enterprise Software containers\n\nStarting with the next major version, Redis Enterprise Software containers with the image tag `x.y.z-build` will be based on RHEL instead of Ubuntu.\n\nThis change will only affect you if you use containers outside the official [Redis Enterprise for Kubernetes](https://redis.io/docs/latest/operate/kubernetes) product and use Ubuntu-specific commands.\n\nTo use Ubuntu-based images after this change, you can specify the operating system suffix in the image tag. For example, use the image tag `7.4.2-216.focal` instead of `7.4.2-216`."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Enterprise Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Enterprise Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Enterprise Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Enterprise Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors.\n\n- RS61676: Full chain certificate update fails if any certificate in the chain does not have a Common Name (CN).\n\n- RS119958: The `debuginfo` script fails with the error `/bin/tar: Argument list too long` if there are too many RocksDB log files. This issue only affects clusters with Auto Tiering.\n\n- RS122570: REST API `POST /crdbs` responds with a confusing error message if the cluster does not have the requested CRDB-compatible module that complies with the requested featureset.\n\n    This issue was fixed in [Redis Enterprise Software version 7.4.2-126](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-126).\n\n- RS123142: In an Active-Active setup with at least three participating clusters, removing and re-adding a cluster after removing older clusters without re-adding them can cause missing keys and potentially lead to data loss or data inconsistency.\n\n    To prevent this issue, avoid adding clusters until you upgrade to the upcoming maintenance release when available.\n\n    This issue was fixed in [Redis Enterprise Software version 7.4.2-169](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-169)."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Remove a node.\n\n    Use the REST API or legacy UI instead. See [Remove a cluster node](https://redis.io/docs/latest/operate/rs/clusters/remove-node) for instructions.\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### OpenSSL compatibility issue for 7.4.2 modules on Amazon Linux 2\n\nDue to an OpenSSL 1.1 compatibility issue between modules and clusters, Redis Enterprise Software version 7.4.2-54 is not fully supported on Amazon Linux 2 clusters with databases that use the following modules: RedisGears, RediSearch, or RedisTimeSeries.\n\nThis issue will be fixed in a future maintenance release.\n\n#### RedisGraph prevents upgrade to RHEL 9 \n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Enterprise cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Cannot create Redis v6.x Active-Active databases with modules\n\nYou cannot create Active-Active databases that use Redis version 6.0 or 6.2 with modules. Databases that use Redis version 7.2 do not have this limitation.\n\nThis limitation was fixed in [Redis Enterprise Software version 7.4.2-104](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-104).\n\n#### Firewalld configuration fails on RHEL 9 due to file permissions\n\nWhen you install Redis Enterprise Software version 7.4.2 on RHEL 9, `firewalld` configuration fails to add the `redislabs` service if `/etc/firewalld/services/redislabs-clients.xml` and `/etc/firewalld/services/redislabs.xml` are owned by `redislabs` instead of `root`.\n\nAs a workaround:\n\n1. Change the files' owner and group to `root`: \n\n    [code example]\n\n1. Add the `redislabs` service to `firewalld`:\n\n    [code example]\n\nThis limitation was fixed in [Redis Enterprise Software version 7.4.2-129](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-129)."}],"examples":[{"id":"known-limitations-ex0","language":"sh","code":"$ chown root:root /etc/firewalld/services/redislabs-clients.xml\n    $ chown root:root /etc/firewalld/services/redislabs.xml","section_id":"known-limitations"},{"id":"known-limitations-ex1","language":"sh","code":"$ systemctl daemon-reload\n    $ systemctl restart firewalls\n    $ /bin/firewall-cmd --add-service=redislabs","section_id":"known-limitations"}]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-104","title":"Redis Enterprise Software release notes 7.4.2-104 (March 2024)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-104/","summary":"New Cluster Manager UI enhancements to change passwords from the sign-in screen, view locked user accounts, and unlock user accounts with password reset.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"932c132f6f43581375bd06f6f4c843d098ab91b85feaa86d440b41276bdc8f4b","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Enterprise Software version 7.4.2](https://redis.io/downloads/#software)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- New Cluster Manager UI enhancements for password management and identifying locked user accounts"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"enhancements","title":"Enhancements","role":"content","text":"- New Cluster Manager UI enhancements:\n\n    - You can change your password from the sign-in screen.\n    \n    - Added a \"User is locked out\" label to the **Access Control > Users** screen to help administrators identify and manage locked users.\n\n#### Redis module feature sets\n\nRedis Enterprise comes packaged with several modules. As of version 7.4.2, Redis Enterprise includes two feature sets, compatible with different Redis database versions.\n\nBundled Redis modules compatible with Redis database version 7.2:\n\n- [RediSearch 2.8.11](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)\n\n- [RedisJSON 2.6.9](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)\n\n- [RedisTimeSeries 1.10.11](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)\n\n- [RedisBloom 2.6.11](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes)\n\n- [RedisGears 2.0.17](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.17-m18)\n\nBundled Redis modules compatible with Redis database versions 6.0 and 6.2:\n\n- [RediSearch 2.6.15](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)\n\n- [RedisJSON 2.4.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)\n\n- [RedisTimeSeries 1.8.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)\n\n- [RedisBloom 2.4.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)\n\n- [RedisGraph v2.10.15](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes)"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS112165: `metrics_exporter` now reports the `bdb_crdt_syncer_status` metric during database out-of-memory events.\n\n- RS115577: Changed `connect_timeout` and `read_timeout` values for S3 from 30 to 60 seconds.\n\n- RS98042: The master node now logs all `replace_node` errors.\n\n- RS118147: Dynamically allocate buffer size for OSS cluster port mapping configuration.\n\n- RS114971: Update `endpoint` when `oss_cluster_api_preferred_ip_type` changes.\n\n- RS121248: Fixed a known issue that prevented creating Active-Active databases that use Redis version 6.0 or 6.2 with modules."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":"- Removed the restriction that prevented adding nodes to a cluster with an expired cluster license, which sometimes caused issues for Kubernetes deployments.\n\n    To avoid issues due to an expired cluster license, we recommend replacing the license before the expiration date. See [Update cluster license](https://redis.io/docs/latest/operate/rs/clusters/configure/license-keys#update-cluster-license) for Cluster Manager UI instructions or use a [`PUT /v1/license`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/license#put-license) REST API request."},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"- [#1131](https://github.com/RedisJSON/RedisJSON/issues/1131), [#1143](https://github.com/RedisJSON/RedisJSON/pull/1143): RedisJSON v2.6.9 reverts JSONPath default path value from `$` to `.` under RESP3."},{"id":"deprecations","title":"Deprecations","role":"content","text":"#### API deprecations\n\n- The BDB REST API object's `background_op` field is deprecated. Use [`GET v1/actions/bdb/<bdb_uid>`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/actions/bdb) instead."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Enterprise Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Enterprise Software and Redis Stack modules.\n\n<span title=\"Warning icon\">&#x26A0;&#xFE0F;</span> Deprecation warning – The platform is still supported for this version of Redis Enterprise Software, but support will be removed in a future release.\n\n| Redis Enterprise<br />major versions | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | July 2025 | Feb 2025 | Aug 2024 |\n| **Platforms** | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>\n[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Enterprise Software are certified for development and testing only."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the MD5 checksums for the available packages:\n\n| Package | MD5 checksum (7.4.2-104 March release) |\n|---------|---------------------------------------|\n| Ubuntu 18 | a65c8505f5e0e1bdff63fb778b010856 |\n| Ubuntu 20 | 7c3e0aef16147c26fd5f1089e6f68dc2 |\n| Red Hat Enterprise Linux (RHEL) 8 | 31df503bafdf43330be74267075401e3 |\n| Red Hat Enterprise Linux (RHEL) 9 | 52199b917ebaab05352227c4c9c008ac |\n| Amazon Linux 2 | 3823405fe934ce590aa6a9c71001168e |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS61676: Full chain certificate update fails if any certificate in the chain does not have a Common Name (CN).\n\n- RS119958: The `debuginfo` script fails with the error `/bin/tar: Argument list too long` due to excessive RocksDB log files.\n\n- RS122570: REST API `POST /crdbs` responds with a confusing error message if the cluster does not have the requested CRDB-compatible module that complies with the requested featureset."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Firewalld configuration fails on RHEL 9 due to file permissions\n\nWhen you install Redis Enterprise Software version 7.4.2 on RHEL 9, `firewalld` configuration fails to add the `redislabs` service if `/etc/firewalld/services/redislabs-clients.xml` and `/etc/firewalld/services/redislabs.xml` are owned by `redislabs` instead of `root`.\n\nAs a workaround:\n\n1. Change the files' owner and group to `root`: \n\n    [code example]\n\n1. Add the `redislabs` service to `firewalld`:\n\n    [code example]\n\nThis limitation will be fixed in a future maintenance release."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Enterprise Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Enterprise has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Enterprise due to different or additional functionality available in Redis Enterprise that is not available in open source Redis.\n\nRedis Enterprise 7.4.2-104 supports open source Redis 7.2, 6.2, and 6.0. Below is the list of open source Redis CVEs fixed by version.\n\nRedis 7.2.x:\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` command can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)\n\nRedis 6.0.x:\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.0.20)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.0.19)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.0.18)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 6.0.18)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.0.17)"}],"examples":[{"id":"known-limitations-ex0","language":"sh","code":"$ chown root:root /etc/firewalld/services/redislabs-clients.xml\n    $ chown root:root /etc/firewalld/services/redislabs.xml","section_id":"known-limitations"},{"id":"known-limitations-ex1","language":"sh","code":"$ systemctl daemon-reload\n    $ systemctl restart firewalls\n    $ /bin/firewall-cmd --add-service=redislabs","section_id":"known-limitations"}]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-126","title":"Redis Enterprise Software release notes 7.4.2-126 (April 2024)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-126/","summary":"Redis database version selection during database creation in the Cluster Manager UI. New Redis logo.","aliases":["/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-119/","/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-tba/"],"tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"7d2fc3aa62a76ed5203a403a48467cef60d189ee9bfd75453e155e4e5731df22","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Enterprise Software version 7.4.2](https://redis.io/downloads/#software)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Redis database version selection during database creation in the Cluster Manager UI\n\n- New Redis logo"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"enhancements","title":"Enhancements","role":"content","text":"- New Cluster Manager UI enhancements:\n\n    - You can choose the Redis database version during [database creation](https://redis.io/docs/latest/operate/rs/databases/create).\n\n    - Updated the Redis logo.\n\n#### Redis module feature sets\n\nRedis Enterprise comes packaged with several modules. As of version 7.4.2, Redis Enterprise includes two feature sets, compatible with different Redis database versions.\n\nBundled Redis modules compatible with Redis database version 7.2:\n\n- [RediSearch 2.8.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)\n\n- [RedisJSON 2.6.9](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)\n\n- [RedisTimeSeries 1.10.11](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)\n\n- [RedisBloom 2.6.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes)\n\n- [RedisGears 2.0.19](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.19-m20)\n\nBundled Redis modules compatible with Redis database versions 6.0 and 6.2:\n\n- [RediSearch 2.6.16](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)\n\n- [RedisJSON 2.4.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)\n\n- [RedisTimeSeries 1.8.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)\n\n- [RedisBloom 2.4.9](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)\n\n- [RedisGraph v2.10.15](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes)"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS113727: Added `automatic_node_offload` policy, which defines whether the cluster can automatically migrate shards from a node if the node is overbooked and is enabled by default. During an upgrade, don't offload even if the policy is enabled.\n\n- RS113828: Removed legacy function that adjusted existing users to role-based access control.\n\n- RS115636: Delete tasks with \"aborted\" status from the resource manager result queue.\n\n- RS122225: Improved descriptions in Cluster Manager UI logs for database upgrade, check node error, and upgrade mode.\n\n- RS122031: Improved performance of database search in the Cluster Manager UI.\n\n- RS121866: Block scripts that contain write commands from running on stale CRDB servers.\n\n- RS122570: Improved error message when unable to locate a requested CRDB-compatible module during Active-Active database creation.\n\n- RS120820: When using OSS Cluster API, improved availability during failover, database migration, database upgrade, and node optimization.\n\n- RS117388: Improved `rladmin status` output for stale shards."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Enterprise Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Enterprise Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Enterprise Software, but support will be removed in a future release.\n\n| Redis Enterprise<br />major versions | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | July 2025 | Feb 2025 | Aug 2024 |\n| **Platforms** | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>\n[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Enterprise Software are certified for development and testing only."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the MD5 checksums for the available packages:\n\n| Package | MD5 checksum (7.4.2-126 April release) |\n|---------|---------------------------------------|\n| Ubuntu 18 | 0cca90f27f368d35b9ca08e4016047c7 |\n| Ubuntu 20 | 1933107157365c1c1b38b69b9644b47 |\n| Red Hat Enterprise Linux (RHEL) 8 | 4eb11678fa80cddca17fc7cfb94b9289 |\n| Red Hat Enterprise Linux (RHEL) 9 | 217b6669ff8ac5bbcc2fc8fbc2930c59 |\n| Amazon Linux 2 | d0f472f173f09c56a14c57f17d146deb |"},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Enterprise Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Enterprise has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Enterprise due to different or additional functionality available in Redis Enterprise that is not available in open source Redis.\n\nRedis Enterprise 7.4.2 supports open source Redis 7.2, 6.2, and 6.0. Below is the list of open source Redis CVEs fixed by version.\n\nRedis 7.2.x:\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` command can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)\n\nRedis 6.0.x:\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.0.20)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.0.19)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.0.18)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 6.0.18)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.0.17)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-129","title":"Redis Enterprise Software release notes 7.4.2-129 (April 2024)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-129/","summary":"Fixes for bugs and known limitations that affect Redis Enterprise Software v7.4.2-126.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"89ece56cf8eb975e036ea6e0ab6d58b68607afb8dc9f49c2cf3b5c02f088abcb","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Enterprise Software version 7.4.2](https://redis.io/downloads/#software). This version fixes several issues that affect [version 7.4.2-126](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-126)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Fixes for bugs and known limitations that affect Redis Enterprise Software [version 7.4.2-126](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-126)"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS121899: Upgraded the `boto3` library from version 1.26.65 to 1.34.76 to fix a [known memory leak](https://github.com/boto/boto3/issues/3614).\n\n- RS122241: Changed file permissions to allow `firewalld` configuration to add the `redislabs` service on RHEL 9.\n\n- RS121673: Search commands no longer return an error after fixing incorrect configuration."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Enterprise Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Enterprise Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Enterprise Software, but support will be removed in a future release.\n\n| Redis Enterprise<br />major versions | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | July 2025 | Feb 2025 | Aug 2024 |\n| **Platforms** | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>\n[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Enterprise Software are certified for development and testing only."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the MD5 checksums for the available packages:\n\n| Package | MD5 checksum (7.4.2-129 April release) |\n|---------|---------------------------------------|\n| Ubuntu 18 | 6f4b3ffa5769322bf1496dc35670b317 |\n| Ubuntu 20 | 794952d515d99a16dd584450517f47da |\n| Red Hat Enterprise Linux (RHEL) 8 | de59c2d2e5bf6cc2e4e3542761e0e476 |\n| Red Hat Enterprise Linux (RHEL) 9 | 5247575205a6b6ba5b4d35ccc9d2cfa3 |\n| Amazon Linux 2 | 02881466864c4eaa349487edce73050b |"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-169","title":"Redis Enterprise Software release notes 7.4.2-169 (May 2024)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-169/","summary":"Configuration options for cluster management API workers and threads. Reduced Cluster Configuration Store CPU usage.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"c9f0ad72081ddf8d08b0d8e0be73f0c20d071ec27c3b845d36728f6d3d80bb33","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Enterprise Software version 7.4.2](https://redis.io/downloads/#software)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Configuration options for cluster management API workers and threads\n\n- Reduced Cluster Configuration Store CPU usage\n\n- Bug fixes"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"enhancements","title":"Enhancements","role":"content","text":"- Allow configuring `cnm_http_workers` and `cnm_http_max_threads_per_worker` using a [`PUT /v1/cluster`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster#put-cluster) REST API request. You can update these settings if the API load is higher than the cluster can handle by default. Configuring more workers than the default will consume more memory.\n\n- Reduced Cluster Configuration Store CPU usage, following the addition of the internal caching mechanism in the resource management calculations.\n\n#### Redis module feature sets\n\nRedis Enterprise comes packaged with several modules. As of version 7.4.2, Redis Enterprise includes two feature sets, compatible with different Redis database versions.\n\nBundled Redis modules compatible with Redis database version 7.2:\n\n- [RediSearch 2.8.13](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)\n\n- [RedisJSON 2.6.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)\n\n- [RedisTimeSeries 1.10.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)\n\n- [RedisBloom 2.6.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes)\n\n- [RedisGears 2.0.20](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.20-m21)\n\nBundled Redis modules compatible with Redis database versions 6.0 and 6.2:\n\n- [RediSearch 2.6.18](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)\n\n- [RedisJSON 2.4.9](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)\n\n- [RedisTimeSeries 1.8.13](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)\n\n- [RedisBloom 2.4.9](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)\n\n- [RedisGraph v2.10.15](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes)"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS123237: CRDT syncer prevents potential data mismatches or data loss due to missing keys when removing and re-adding a cluster in an Active-Active setup with at least three participating clusters after removing older clusters without re-adding them.\n\n- RS93876: When resharding is performed with `shards_blueprint`, improved the order of resharding to avoid stuck state machines due to low memory.\n\n- RS119350: Avoid removing roles bound to LDAP mappings during cluster upgrade even if the roles aren't assigned to any users.\n\n- RS123561: Improved the installation validation process by identifying an occupied DNS port.\n\n- RS122365: Improved platform and module readiness checks by ignoring operating systems that are no longer part of the cluster.\n\n- RS96400: DMC management threads properly terminate instead of maintaining a blocking connection if they can't reach the CCS after a predefined time.\n\n- RS117188: Replaced the policy cache mechanism. Cache data from the versions file.\n\n- RS120425: Skip [`rlcheck`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rlcheck) `ccs_dns` check when `pdns_server` is not enabled.\n\n- RS118252: For databases with OSS Cluster API enabled, endpoints can bind to nodes that have master shards even if `max_listeners=0`.\n\n- RS115382: Updated DMC to disconnect the client with an error if the token size exceeds `MAX_KEY_BULK_SIZE`.\n\n- RS110798: Reduced `cluster_wd` request latency, fixing communication errors on large clusters.\n\n- RS119705: On in-place upgrades, minimize DMC downtime by performing CCS schema upgrade before services are restarted.\n\n- RS117224: Avoid RAM eviction during scripts.\n\n- RS122062: Use `os_family` instead of `os_name` when creating module platforms during cluster upgrade, fixing join requests for operating systems like CentOS and Oracle Linux."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Enterprise Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Enterprise Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Enterprise Software, but support will be removed in a future release.\n\n| Redis Enterprise<br />major versions | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | July 2025 | Feb 2025 | Aug 2024 |\n| **Platforms** | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>\n[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Enterprise Software are certified for development and testing only."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the MD5 checksums for the available packages:\n\n| Package | MD5 checksum (7.4.2-169 May release) |\n|---------|---------------------------------------|\n| Ubuntu 18 | 20ff0c73c6df04921355e2b256672f8e |\n| Ubuntu 20 | 1bdfad0d0a435bf6bcc648034bcc6bd6 |\n| Red Hat Enterprise Linux (RHEL) 8 | 5a7ebdce87aff285ea78d5a29ccbafc5 |\n| Red Hat Enterprise Linux (RHEL) 9 | 06dae1500cca622a6f61f51f107685df |\n| Amazon Linux 2 | 1eb64a1e1c10c17d126494b3bfc5f090 |"},{"id":"security","title":"Security","role":"security","text":"**Source available Redis security fixes compatibility**\n\nAs part of Redis's commitment to security, Redis Enterprise Software implements the latest [security fixes](https://github.com/redis/redis/releases) from [source available Redis](https://github.com/redis/redis). Redis Enterprise has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for Redis do not affect Redis Enterprise due to different or additional functionality in Redis Enterprise.\n\nRedis Enterprise 7.4.2 supports Redis 7.2, 6.2, and 6.0. Below is the list of Redis CVEs fixed by version.\n\nRedis 7.2.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` command can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-45145) The wrong order of listen(2) and chmod(2) calls creates a\nrace condition that can be used by another process to bypass desired Unix\nsocket permissions on startup. (Redis 6.2.14)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow can occur while handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result in remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow can occur while handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)\n\nRedis 6.0.x:\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.0.20)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.0.19)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.0.18)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.0.18)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.0.17)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-216","title":"Redis Enterprise Software release notes 7.4.2-216 (July 2024)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-216/","summary":"region_name field for new AWS S3 regions. Bug fixes.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"415dbecc444546ffbdffcbda5bdb10a59bc1750c489631990ae9ec125f249890","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Enterprise Software version 7.4.2](https://redis.io/downloads/#software)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- `region_name` field for new AWS S3 regions\n\n- Bug fixes"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"enhancements","title":"Enhancements","role":"content","text":"- Added the `region_name` field in the REST API to import, export, and back up data in new AWS S3 regions.\n\n#### Redis module feature sets\n\nRedis Enterprise comes packaged with several modules. As of version 7.4.2, Redis Enterprise includes two feature sets, compatible with different Redis database versions.\n\nBundled Redis modules compatible with Redis database version 7.2:\n\n- [RediSearch 2.8.13](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)\n\n- [RedisJSON 2.6.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)\n\n- [RedisTimeSeries 1.10.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)\n\n- [RedisBloom 2.6.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes)\n\n- [RedisGears 2.0.20 preview](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.20-m21)\n\nBundled Redis modules compatible with Redis database versions 6.0 and 6.2:\n\n- [RediSearch 2.6.18](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)\n\n- [RedisJSON 2.4.9](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)\n\n- [RedisTimeSeries 1.8.13](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)\n\n- [RedisBloom 2.4.9](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)\n\n- [RedisGraph v2.10.15](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes)"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS124753: Fixed a bug with databases created using a recovery plan with replication enabled where shards did not write persistence files.\n\n- RS126290: Allow editing an existing Replica Of source's proxy certificate in the Cluster Manager UI.\n\n- RS126476: Fixed unexpected database audit connection errors in the DMC proxy log.\n\n- RS126382: Log info when a node fails to join the cluster.\n\n- RS126655: Fixed database RAM limit display issue in the Cluster Manager UI.\n\n- RS125930: Fixed a bug where the cluster rejected certificate chains with unknown extensions due to a cryptography bug.\n\n- RS118231: Fixed output of `rladmin tune db` when updating buffer limits fails due to unreachable shards.\n\n- RS128091: Fixed a bug in `XREADGROUP` on CRDB that prevented it from resetting the idle time of a consumer in `XINFO`."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"product-lifecycle-updates","title":"Product lifecycle updates","role":"content","text":"After August 31, 2024, Redis Enterprise Software versions 6.2.4 and 6.2.8 will not be included in [supported upgrade paths](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-cluster#supported-upgrade-paths) for Redis Enterprise Software versions beyond 7.4.x. Redis Enterprise Software versions 6.2.10, 6.2.12 and 6.2.18 continue to be part of the upgrade path.\nThe next major Redis Enterprise Software release will still bundle Redis database version 6.2 and allow database upgrades from Redis database version 6.2 to 7.x.\n\nSee the [Redis Enterprise Software product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle) for more information about release numbers."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Enterprise Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Enterprise Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Enterprise Software, but support will be removed in a future release.\n\n| Redis Enterprise<br />major versions | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | July 2025 | Feb 2025 | Aug 2024 |\n| **Platforms** | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>\n[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Enterprise Software are certified for development and testing only."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.4.2-216 July release) |\n|---------|---------------------------------------|\n| Ubuntu 18 | <span class=\"break-all\">d20e3cc11bf97d8feb20e3c24e9530b2aae28bf7450dd498674d44014f3fa079</span> |\n| Ubuntu 20 | <span class=\"break-all\">724ef7cbd78612252a246ff66a43c3bdf8aa0102086df8c25e825edce9dcb01f</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">1d8e0d16ff802c6a87fc1f9290ae4c27e71f170e1573349d179d9ef042accec2</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">7717999ebebd7dd4d66db7cbd92b0439f95dadeddee1990da8c9bf7cdec84b71</span> |\n| Amazon Linux 2 | <span class=\"break-all\">99b57ab72bbbcacdf9363d5b8eddfeaf6a23930a97cb6724334c4742810fcf5f</span> |"},{"id":"security","title":"Security","role":"security","text":"**Source available Redis security fixes compatibility**\n\nAs part of Redis's commitment to security, Redis Enterprise Software implements the latest [security fixes](https://github.com/redis/redis/releases) from [source available Redis](https://github.com/redis/redis). Redis Enterprise has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for Redis do not affect Redis Enterprise due to different or additional functionality in Redis Enterprise.\n\nRedis Enterprise 7.4.2 supports Redis 7.2, 6.2, and 6.0. Below is the list of Redis CVEs fixed by version.\n\nRedis 7.2.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) A specially crafted `MSETNX` command can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-45145) The wrong order of listen(2) and chmod(2) calls creates a\nrace condition that can be used by another process to bypass desired Unix\nsocket permissions on startup. (Redis 6.2.14)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow can occur while handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result in remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow can occur while handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)\n\nRedis 6.0.x:\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.0.20)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.0.19)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.0.18)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.0.18)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.0.17)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-54","title":"Redis Enterprise Software release notes 7.4.2-54 (February 2024)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-2-54/","summary":"New Cluster Manager UI enhancements, including Active-Active database management. Full TLS 1.3 support. Automatic recovery configuration. Full IPv6 support, including for internal traffic. Maintenance mode enhancements. Module management enhancements. RHEL 9 support.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"b0c5ae71c453b807bc99037f41e541038c12994331a9963b00c81c897d3fdad4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"​[​Redis Enterprise Software version 7.4.2](https://redis.io/downloads/#software) is now available!"},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- New Cluster Manager UI enhancements, including Active-Active database management\n\n- Full TLS 1.3 support\n\n- Automatic recovery configuration\n\n- Full IPv6 support, including for internal traffic\n\n- Maintenance mode enhancements\n\n- Module management enhancements\n\n- RHEL 7 and Oracle Linux 7, which were previously deprecated, are no longer supported\n\n- RHEL 9 support"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"new-features","title":"New features","role":"content","text":"- Added full support for TLS 1.3:\n\n    - TLS 1.3 cipher suites are now configurable for database connections. See [Configure cipher suites](https://redis.io/docs/latest/operate/rs/security/encryption/tls/ciphers) for details.\n\n    - TLS 1.3 can be set as the minimum TLS version. See [Configure TLS protocol](https://redis.io/docs/latest/operate/rs/security/encryption/tls/tls-protocols) for details.\n\n- Automatic recovery of databases from persistence files is configurable using the REST API. See [Configure automatic recovery](https://redis.io/docs/latest/operate/rs/databases/recover#configure-automatic-recovery) for details.\n\n- New clusters fully support IPv6, including for internal traffic. See [Enable IPv6 for internal traffic](https://redis.io/docs/latest/operate/rs/networking/multi-ip-ipv6#enable-ipv6-for-internal-traffic) for details."},{"id":"enhancements","title":"Enhancements","role":"content","text":"- New Cluster Manager UI enhancements:\n\n    - [Create](https://redis.io/docs/latest/operate/rs/databases/active-active/create) and [manage](https://redis.io/docs/latest/operate/rs/databases/active-active/manage) Active-Active databases.\n\n    - [Configure database defaults](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults): endpoint configuration, database proxy, and shards placement.\n\n    - Configure [TLS cipher suites](https://redis.io/docs/latest/operate/rs/security/encryption/tls/ciphers#edit-ciphers-ui) and [minimum TLS protocol](https://redis.io/docs/latest/operate/rs/security/encryption/tls/tls-protocols#edit-tls-ui).\n\n    - Configure LDAP [authentication timeout](https://redis.io/docs/latest/operate/rs/security/access-control/ldap/enable-role-based-ldap#authentication-timeout).\n\n- [Maintenance mode](https://redis.io/docs/latest/operate/rs/clusters/maintenance-mode) enhancements:\n\n    - Only the latest maintenance mode snapshot is kept.\n    \n    - Use the `overwrite_snapshot` option to create a new snapshot when you turn on maintenance mode.\n\n    - `keep_slave_shards` is deprecated. Use `evict_ha_replica` and `evict_active_active_replica` instead.\n    \n    - View or delete node snapshots using [`/v1/nodes/{node_uid}/snapshots`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/nodes/snapshots) REST API requests.\n\n- Added support for RHEL 9.\n\n- Improved `actions` REST API:\n\n    - Improved `progress` field returned by [`GET /v1/actions`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/actions) requests.\n\n    - New request to get the status of all currently executing, pending, or completed actions for a specific database: [`GET /v1/actions/bdb/<bdb_uid>`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/actions/bdb)\n\n- [`GET /v1/users`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/users#get-all-users) REST API requests include a new `status` field in the response, which indicates whether the user can sign in:\n\n    - `active`: user can sign in\n\n    - `locked`: user cannot sign in\n\n- Replaced the use of processes for `job_scheduler`, `cnm_http`, and `crdb_coordinator` with threads, which results in significant memory optimization.\n\n#### Redis module feature sets\n\nRedis Enterprise comes packaged with several modules. As of version 7.4.2, Redis Enterprise includes two feature sets, compatible with different Redis database versions.\n\nBundled Redis modules compatible with Redis database version 7.2:\n\n- [RediSearch 2.8.9](https://github.com/RediSearch/RediSearch/releases/tag/v2.8.9)\n\n- [RedisJSON 2.6.8](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.6.8)\n\n- [RedisTimeSeries 1.10.9](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.10.9)\n\n- [RedisBloom 2.6.11](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.6.11)\n\n- [RedisGears 2.0.17](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.17-m18)\n\nBundled Redis modules compatible with Redis database versions 6.0 and 6.2:\n\n- [RediSearch 2.6.12](https://github.com/RediSearch/RediSearch/releases/tag/v2.6.12)\n\n- [RedisJSON 2.4.7](https://github.com/RedisJSON/RedisJSON/releases/tag/v2.4.7)\n\n- [RedisTimeSeries 1.8.11](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.8.11)\n\n- [RedisBloom 2.4.8](https://github.com/RedisBloom/RedisBloom/releases/tag/v2.4.8)\n\n- [RedisGraph v2.10.12](https://github.com/RedisGraph/RedisGraph/releases/tag/v2.10.12)"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS57417: Azure OSS cluster with Private Link should no longer return public IPs in response to `CLUSTER` commands.\n\n- RS106159: If RESP3 is not supported, `HELLO` requests now return an unknown command error instead of a protocol error.\n\n- RS107538: Fixed an issue where DMC proxy perpetually restarted due to a CCS inconsistency."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"- Renamed `sentinel_ssl_policy` field. The new name is `sentinel_tls_mode`.\n\n    - You can only change the discovery service policy using `sentinel_tls_mode`.\n\n    - When retrieving the value of the current policy using [`GET /v1/cluster`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster#get-cluster), both fields are returned for backward compatibility.\n\n- Port 3345 is now reserved for internal use.\n- Type of `progress` field returned by [`GET /v1/actions`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/actions) request modified from `integer` to `float`."},{"id":"deprecations","title":"Deprecations","role":"content","text":"#### API deprecations\n\n- The replica HA cluster policy is deprecated.\n\n- The maintenance mode option `keep_slave_shards` is deprecated in `rladmin` and the REST API. Use `evict_ha_replica` and `evict_active_active_replica` instead.\n\n- `/v1/debuginfo` REST API paths are deprecated. Use the new paths [`/v1/cluster/debuginfo`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/debuginfo), [`/v1/nodes/debuginfo`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/nodes/debuginfo), and [`/v1/bdbs/debuginfo`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/debuginfo) instead.\n\n#### Legacy UI deprecation\n\nThe legacy UI is deprecated in favor of the new Cluster Manager UI and will be removed in a future release.\n\n#### Redis 6.0 database deprecation\n\nRedis database version 6.0 is deprecated as of Redis Enterprise Software version 7.4.2 and will be removed in a future release.\n\nTo prepare for the future removal of Redis 6.0:\n\n- For Redis Enterprise 6.2.* clusters, upgrade Redis 6.0 databases to Redis 6.2. See the [Redis 6.2 release notes](https://raw.githubusercontent.com/redis/redis/6.2/00-RELEASENOTES) for the list of changes.\n\n- For Redis Enterprise 7.2.4 and 7.4.2 clusters, upgrade Redis 6.0 databases to Redis 7.2. Before you upgrade your databases, see the list of [Redis 7.2 breaking changes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52#redis-72-breaking-changes) and update any applications that connect to your database to handle these changes.\n\n#### Operating system retirements\n\n- RHEL 7 and Oracle Linux 7 were previously announced as deprecated in the [Redis Enterprise Software 7.2.4 release notes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases#deprecations). As of Redis Enterprise Software 7.4.2, RHEL 7 and Oracle Linux 7 are no longer supported.\n\n#### Security retirements\n\n- The RC4 encryption cipher, which was previously deprecated in favor of stronger ciphers, is no longer supported.\n\n- The 3DES encryption cipher, which was previously deprecated in favor of stronger ciphers like AES, is no longer supported. Verify that all clients, applications, and connections support the AES cipher.\n\n- TLS 1.0 and TLS 1.1 connections, which were previously deprecated in favor of TLS 1.2 or later, are no longer supported. Verify that all clients, applications, and connections support TLS 1.2 or later."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Enterprise Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Enterprise Software.\n\n<span title=\"Warning icon\">&#x26A0;&#xFE0F;</span> Deprecated – The platform is still supported for this version of Redis Enterprise Software, but support will be removed in a future release.\n\n<span title=\"X icon\">&#x274c;</span> End of life – Platform support ended in this version of Redis Enterprise Software.\n\n| Redis Enterprise | 7.4.2 | 7.2.4 | 6.4.2 | 6.2.18 | 6.2.12 | 6.2.10 | 6.2.8 | 6.2.4 |\n|------------------|-------|-------|-------|--------|--------|--------|--------|-------|\n| **Release date** | Feb<br />2024 | Aug<br />2023 | Feb<br />2023 | Sept<br />2022 | Aug<br />2022 | Feb<br />2022 | Oct<br />2021 | Aug<br />2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | – | July<br />2025 | Feb<br />2025 | Aug<br />2024 | Aug<br />2024 | Aug<br />2024 | Aug<br />2024 | Aug<br />2024 |\n| **Ubuntu**<sup>[1](#table-note-1)</sup> |\n| 20.04 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span><sup>[6](#table-note-6)</sup> | – | – | – | – | – |\n| 18.04 | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\"><span title=\"Supported\">&#x2705;</span></span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| 16.04 | – | <span title=\"End of life\">&#x274c;</span> | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **RHEL & CentOS**<sup>[2](#table-note-2)</sup>\n| 9.0-9.3 | <span title=\"Supported\">&#x2705;</span> | – | – | – | – | – | – | – |\n| 8.9| <span title=\"Supported\">&#x2705;</span> | – | – | – | – | – | – | – |\n| 8.8 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span><sup>[8](#table-note-8)</sup> | – | – | – | – | – |\n| 8.7 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – | – |\n| 8.5-8.6 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – |\n| 8.0-8.4 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| 7.0-7.9 | <span title=\"End of life\">&#x274c;</span> | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Oracle Linux**<sup>[3](#table-note-3)</sup> |\n| 8 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – |\n| 7 | <span title=\"End of life\">&#x274c;</span> | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Rocky Linux**<sup>[3](#table-note-3)</sup> |\n| 8 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| **Amazon Linux** |\n| 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span><sup>[7](#table-note-7)</sup> | – | – | – | – | – |\n| 1 | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Deprecated\">&#x26A0;&#xFE0F;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Docker**<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| **Kubernetes**<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n2. <a name=\"table-note-2\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>RHEL and CentOS deployments require [firewall configuration](https://redis.io/docs/latest/operate/rs/installing-upgrading/configuring/centos-rhel-firewall).\n\n3. <a name=\"table-note-3\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>Based on the corresponding RHEL version.\n\n4. <a name=\"table-note-4\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>\n[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Enterprise Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes).\n\n6. <a name=\"table-note-6\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>Ubuntu 20.04 support was added in Redis Enterprise Software [6.4.2-43](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-43).\n\n7. <a name=\"table-note-7\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>A release candidate for Amazon Linux 2 support was added in Redis Enterprise Software [6.4.2-61](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-61). Official support for Amazon Linux 2 was added in Redis Enterprise Software [6.4.2-69](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-69).\n\n8. <a name=\"table-note-8\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>Redis Enterprise Software [6.4.2-103](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-4-2-releases/rs-6-4-2-103) and later supports RHEL 8.8."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the MD5 checksums for the available packages:\n\n| Package | MD5 checksum (7.4.2-54 February release) |\n|---------|---------------------------------------|\n| Ubuntu 18 | d22100b9ce51f602b7b93751bc0ab439 |\n| Ubuntu 20 | 784f3d07eafc99104da124d69f49b617 |\n| RedHat Enterprise Linux (RHEL) 8<br/>Oracle Enterprise Linux (OL) 8 <br/>Rocky Enterprise Linux | 107c1ce8510fd16026bac4179bb2aaa8 |\n| RedHat Enterprise Linux (RHEL) 9 | d5fe1b085147b6ee1fc6f70aa43b263f |\n| Amazon Linux 2 | 62b3dfb977750a3879c7e83c66a7726c |"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Remove a node.\n\n    Use the REST API or legacy UI instead. See [Remove a cluster node](https://redis.io/docs/latest/operate/rs/clusters/remove-node) for instructions.\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### OpenSSL compatibility issue for 7.4.2 modules on Amazon Linux 2\n\nDue to an OpenSSL 1.1 compatibility issue between modules and clusters, Redis Enterprise Software version 7.4.2-54 is not fully supported on Amazon Linux 2 clusters with databases that use the following modules: RedisGears, RediSearch, or RedisTimeSeries.\n\nThis issue will be fixed in a future maintenance release.\n\n#### RedisGraph prevents upgrade to RHEL 9 \n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Enterprise cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Cannot create Redis v6.x Active-Active databases with modules\n\nYou cannot create Active-Active databases that use Redis version 6.0 or 6.2 with modules. Databases that use Redis version 7.2 do not have this limitation.\n\nThis limitation will be fixed in a future maintenance release."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Enterprise Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Enterprise has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Enterprise due to different or additional functionality available in Redis Enterprise that is not available in open source Redis.\n\nRedis Enterprise 7.4.2-54 supports open source Redis 7.2, 6.2, and 6.0. Below is the list of open source Redis CVEs fixed by version.\n\nRedis 7.2.x:\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` command can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)\n\nRedis 6.0.x:\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.0.20)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.0.19)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.0.18)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. (Redis 6.0.18)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.0.17)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-102","title":"Redis Enterprise Software release notes 7.4.6-102 (October 2024)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-102/","summary":"Updated module feature sets with later versions of RediSearch and RedisTimeSeries.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"405d2312beb0207e4eff4ee34830dd3c2d0869762402254ea161d8b6fef683c9","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Enterprise Software version 7.4.6](https://redis.io/downloads/#software)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Updated module feature sets with later versions of RediSearch and RedisTimeSeries"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":"#### Redis module feature sets\n\nRedis Enterprise comes packaged with several modules. As of version 7.4.2, Redis Enterprise includes two feature sets, compatible with different Redis database versions.\n\nBundled Redis modules compatible with Redis database version 7.2:\n\n- [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)\n\n- [RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)\n\n- [RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)\n\n- [RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes)\n\n- [RedisGears 2.0 preview](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.20-m21): The RedisGears preview will not be promoted to GA and will be removed in a future release.\n\nBundled Redis modules compatible with Redis database versions 6.0 and 6.2:\n\n- [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)\n\n- [RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)\n\n- [RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)\n\n- [RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)\n\n- [RedisGraph v2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes): RedisGraph end-of-life has been announced and will be removed in a future release. See the [RedisGraph end-of-life announcement](https://redis.io/blog/redisgraph-eol/) for more details."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"product-lifecycle-updates","title":"Product lifecycle updates","role":"content","text":"#### End-of-life policy extension\n\nThe end-of-life policy for Redis Enterprise Software versions 6.2 and later has been extended to 24 months after the formal release of the subsequent major version. For the updated end-of-life schedule, see the [Redis Enterprise Software product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle).\n\n#### Supported upgrade paths\n\nRedis Enterprise Software versions 6.2.4 and 6.2.8 do not support direct upgrades beyond version 7.4.x. Versions 6.2.10, 6.2.12, and 6.2.18 are part of the [upgrade path](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-cluster#supported-upgrade-paths). To upgrade from 6.2.4 or 6.2.8 to versions later than 7.4.x, an intermediate upgrade is required.\n\nThe next major Redis Enterprise Software release will still bundle Redis database version 6.2 and allow database upgrades from Redis database version 6.2 to 7.x.\n\nSee the [Redis Enterprise Software product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle) for more information about release numbers."},{"id":"deprecations","title":"Deprecations","role":"content","text":"#### Legacy UI deprecation\n\nThe legacy UI is deprecated in favor of the new Cluster Manager UI and will be removed in a future release.\n\n#### Redis 6.0 database deprecation\n\nRedis database version 6.0 is deprecated as of Redis Enterprise Software version 7.4.2 and will be removed in a future release.\n\nTo prepare for the future removal of Redis 6.0:\n\n- For Redis Enterprise 6.2.* clusters, upgrade Redis 6.0 databases to Redis 6.2. See the [Redis 6.2 release notes](https://raw.githubusercontent.com/redis/redis/6.2/00-RELEASENOTES) for the list of changes.\n\n- For Redis Enterprise 7.2.4 and 7.4.x clusters, upgrade Redis 6.0 databases to Redis 7.2. Before you upgrade your databases, see the list of [Redis 7.2 breaking changes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52#redis-72-breaking-changes) and update any applications that connect to your database to handle these changes.\n\n#### End of triggers and functions preview\n\nThe [triggers and functions](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions) (RedisGears) preview has been discontinued.\n\n- Commands such as `TFCALL`, `TFCALLASYNC`, and `TFUNCTION` will be deprecated and will return error messages.\n\n- Any JavaScript functions stored in Redis will be removed. \n\n- JavaScript-based triggers will be blocked.\n\n- Lua functions and scripts will not be affected.\n\nIf your database currently uses triggers and functions, you need to: \n\n1. Adjust your applications to accommodate these changes.\n\n1. Delete all triggers and functions libraries from your existing database:\n\n    1. Run `TFUNCTION LIST`.\n\n    1. Copy all library names.\n\n    1. Run `TFUNCTION DELETE` for each library in the list.\n\n    If any triggers and functions libraries remain in the database, the RDB snapshot won't load on a cluster without RedisGears.\n\n1. Migrate your database to a new database without the RedisGears module.\n\n#### RedisGraph end of life\n\nRedis has announced the end of life for RedisGraph. Redis will continue to support all RedisGraph customers, including releasing patch versions until January 31, 2025.\n\nSee the [RedisGraph end-of-life announcement](https://redis.com/blog/redisgraph-eol/) for more details."},{"id":"upcoming-changes","title":"Upcoming changes","role":"content","text":"#### Default image change for Redis Enterprise Software containers\n\nStarting with the next major version, Redis Enterprise Software containers with the image tag `x.y.z-build` will be based on RHEL instead of Ubuntu.\n\nThis change will only affect you if you use containers outside the official [Redis Enterprise for Kubernetes](https://redis.io/docs/latest/operate/kubernetes) product and use Ubuntu-specific commands.\n\nTo use Ubuntu-based images after this change, you can specify the operating system suffix in the image tag. For example, use the image tag `7.4.2-216.focal` instead of `7.4.2-216`."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Enterprise Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Enterprise Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Enterprise Software, but support will be removed in a future release.\n\n| Redis Enterprise<br />major versions | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>\n[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Enterprise Software are certified for development and testing only."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.4.6-102 October release) |\n|---------|---------------------------------------|\n| Ubuntu 18 | <span class=\"break-all\">f4e6af466b565e59d2e3fb1f5f0c70b7903564a0f1a45cc5a6816caf20059f93</span> |\n| Ubuntu 20 | <span class=\"break-all\">9f10ed232a5655fdc5b46d2dd98157449484fb03290c8c71994cf29b50c4b4a7</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">412a2244e5fd464382acccf811a221671c120fcb51d00bf6bc3089b6f9ad034e</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">b4fc25f6a812fe05ab990049f7f6f8a22932c5d8c507c475455802009ed5acca</span> |\n| Amazon Linux 2 | <span class=\"break-all\">ab3dafdc46823548a6a4a2870be1db21cd2d907533bd0fddf59a895b94c00863</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors.\n\n- RS61676: Full chain certificate update fails if any certificate in the chain does not have a Common Name (CN).\n\n- RS119958: The `debuginfo` script fails with the error `/bin/tar: Argument list too long` if there are too many RocksDB log files. This issue only affects clusters with Auto Tiering."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Remove a node.\n\n    Use the REST API or legacy UI instead. See [Remove a cluster node](https://redis.io/docs/latest/operate/rs/clusters/remove-node) for instructions.\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### OpenSSL compatibility issue for 7.4.2 modules on Amazon Linux 2\n\nDue to an OpenSSL 1.1 compatibility issue between modules and clusters, Redis Enterprise Software version 7.4.2-54 is not fully supported on Amazon Linux 2 clusters with databases that use the following modules: RedisGears, RediSearch, or RedisTimeSeries.\n\nThis issue will be fixed in a future maintenance release.\n\n#### RedisGraph prevents upgrade to RHEL 9 \n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Enterprise cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Enterprise Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Enterprise has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Enterprise due to different or additional functionality available in Redis Enterprise that is not available in open source Redis.\n\nRedis Enterprise 7.4.6-102 supports open source Redis 7.2, 6.2, and 6.0. Below is the list of open source Redis CVEs fixed by version.\n\nRedis 7.2.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)\n\nRedis 6.0.x:\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.0.20)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.0.19)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.0.18)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.0.18)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.0.17)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-188","title":"Redis Enterprise Software release notes 7.4.6-188 (January 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-188/","summary":"Bug fix for a memory leak in the DMC proxy process.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"77b83e1164c52b35854ba4859a253be282b591c042a326ed2b0fc4f12cc2dbb7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Enterprise Software version 7.4.6](https://redis.io/downloads/#software)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- A bug fix for a memory leak in the DMC proxy process"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":"#### Redis module feature sets\n\nRedis Enterprise comes packaged with several modules. As of version 7.4.2, Redis Enterprise includes two feature sets, compatible with different Redis database versions.\n\nBundled Redis modules compatible with Redis database version 7.2:\n\n- [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)\n\n- [RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)\n\n- [RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)\n\n- [RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes)\n\n- [RedisGears 2.0 preview](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.20-m21): The RedisGears preview will not be promoted to GA and will be removed in a future release.\n\nBundled Redis modules compatible with Redis database versions 6.0 and 6.2:\n\n- [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)\n\n- [RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)\n\n- [RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)\n\n- [RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)\n\n- [RedisGraph v2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes): RedisGraph end-of-life has been announced and will be removed in a future release. See the [RedisGraph end-of-life announcement](https://redis.io/blog/redisgraph-eol/) for more details."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS126364: Fixed a memory leak in the DMC proxy process."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"product-lifecycle-updates","title":"Product lifecycle updates","role":"content","text":"#### End-of-life policy extension\n\nThe end-of-life policy for Redis Enterprise Software versions 6.2 and later has been extended to 24 months after the formal release of the subsequent major version. For the updated end-of-life schedule, see the [Redis Enterprise Software product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle).\n\n#### Supported upgrade paths\n\nRedis Enterprise Software versions 6.2.4 and 6.2.8 do not support direct upgrades beyond version 7.4.x. Versions 6.2.10, 6.2.12, and 6.2.18 are part of the [upgrade path](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-cluster#supported-upgrade-paths). To upgrade from 6.2.4 or 6.2.8 to versions later than 7.4.x, an intermediate upgrade is required.\n\nThe next major Redis Enterprise Software release will still bundle Redis database version 6.2 and allow database upgrades from Redis database version 6.2 to 7.x.\n\nSee the [Redis Enterprise Software product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle) for more information about release numbers."},{"id":"deprecations","title":"Deprecations","role":"content","text":"#### Legacy UI deprecation\n\nThe legacy UI is deprecated in favor of the new Cluster Manager UI and will be removed in a future release.\n\n#### Redis 6.0 database deprecation\n\nRedis database version 6.0 is deprecated as of Redis Enterprise Software version 7.4.2 and will be removed in a future release.\n\nTo prepare for the future removal of Redis 6.0:\n\n- For Redis Enterprise 6.2.* clusters, upgrade Redis 6.0 databases to Redis 6.2. See the [Redis 6.2 release notes](https://raw.githubusercontent.com/redis/redis/6.2/00-RELEASENOTES) for the list of changes.\n\n- For Redis Enterprise 7.2.4 and 7.4.x clusters, upgrade Redis 6.0 databases to Redis 7.2. Before you upgrade your databases, see the list of [Redis 7.2 breaking changes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52#redis-72-breaking-changes) and update any applications that connect to your database to handle these changes.\n\n#### End of triggers and functions preview\n\nThe [triggers and functions](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions) (RedisGears) preview has been discontinued.\n\n- Commands such as `TFCALL`, `TFCALLASYNC`, and `TFUNCTION` will be deprecated and will return error messages.\n\n- Any JavaScript functions stored in Redis will be removed. \n\n- JavaScript-based triggers will be blocked.\n\n- Lua functions and scripts will not be affected.\n\nIf your database currently uses triggers and functions, you need to: \n\n1. Adjust your applications to accommodate these changes.\n\n1. Delete all triggers and functions libraries from your existing database:\n\n    1. Run `TFUNCTION LIST`.\n\n    1. Copy all library names.\n\n    1. Run `TFUNCTION DELETE` for each library in the list.\n\n    If any triggers and functions libraries remain in the database, the RDB snapshot won't load on a cluster without RedisGears.\n\n1. Migrate your database to a new database without the RedisGears module.\n\n#### RedisGraph end of life\n\nRedis has announced the end of life for RedisGraph. Redis will continue to support all RedisGraph customers, including releasing patch versions until January 31, 2025.\n\nSee the [RedisGraph end-of-life announcement](https://redis.com/blog/redisgraph-eol/) for more details."},{"id":"upcoming-changes","title":"Upcoming changes","role":"content","text":"#### Default image change for Redis Enterprise Software containers\n\nStarting with the next major version, Redis Enterprise Software containers with the image tag `x.y.z-build` will be based on RHEL instead of Ubuntu.\n\nThis change will only affect you if you use containers outside the official [Redis Enterprise for Kubernetes](https://redis.io/docs/latest/operate/kubernetes) product and use Ubuntu-specific commands.\n\nTo use Ubuntu-based images after this change, you can specify the operating system suffix in the image tag. For example, use the image tag `7.4.2-216.focal` instead of `7.4.2-216`."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.4.6-188 January release) |\n|---------|---------------------------------------|\n| Ubuntu 18 | <span class=\"break-all\">452446126776a93a6ac943f6b9ed0a274a2b48eb18d5ec057d15459e6148caa2</span> |\n| Ubuntu 20 | <span class=\"break-all\">91c245d39007fc1c5ad86dc991286ce30d837289f541518bfee5249ccb3864bb</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">995bfa4aad1f1ab4611fbe83d1d3cbb2ddc45b9fa6d53900267385e6e5c4d162</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">b4fc25f6a812fe05ab990049f7f6f8a22932c5d8c507c475455802009ed5acca</span> |\n| Amazon Linux 2 | <span class=\"break-all\">042b016fd9fac8fac95d51e9fbfece6f4ab5e705146e7423ea23b68ce872c76f</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors.\n\n- RS61676: Full chain certificate update fails if any certificate in the chain does not have a Common Name (CN).\n\n- RS119958: The `debuginfo` script fails with the error `/bin/tar: Argument list too long` if there are too many RocksDB log files. This issue only affects clusters with Auto Tiering."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Remove a node.\n\n    Use the REST API or legacy UI instead. See [Remove a cluster node](https://redis.io/docs/latest/operate/rs/clusters/remove-node) for instructions.\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### OpenSSL compatibility issue for 7.4.2 modules on Amazon Linux 2\n\nDue to an OpenSSL 1.1 compatibility issue between modules and clusters, Redis Enterprise Software version 7.4.2-54 is not fully supported on Amazon Linux 2 clusters with databases that use the following modules: RedisGears, RediSearch, or RedisTimeSeries.\n\nThis issue will be fixed in a future maintenance release.\n\n#### RedisGraph prevents upgrade to RHEL 9 \n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Enterprise cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Enterprise Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Enterprise has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Enterprise due to different or additional functionality available in Redis Enterprise that is not available in open source Redis.\n\nRedis Enterprise 7.4.6-188 supports open source Redis 7.2, 6.2, and 6.0. Below is the list of open source Redis CVEs fixed by version.\n\nRedis 7.2.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)\n\nRedis 6.0.x:\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.0.20)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.0.19)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.0.18)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.0.18)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.0.17)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-22","title":"Redis Enterprise Software release notes 7.4.6-22 (July 2024)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-22/","summary":"Support for SHA-384 certificates in client authentication. New parameters for optimize_shard_placement and recover database REST API requests.","aliases":["/operate/rs/release-notes/rs-7-4-6-releases/rs-7-4-6-22/"],"tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"204735d90b08feed457d9405e0f479a19b410ba9e2db805589322995092eadda","sections":[{"id":"overview","title":"Overview","role":"overview","text":"​[​Redis Enterprise Software version 7.4.6](https://redis.io/downloads/#software) is now available! This release includes API enhancements that warranted a new minor version instead of a maintenance release for version 7.4.2. However, you can upgrade from 7.4.2 to 7.4.6 without issue."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Support for SHA-384 certificates in client authentication\n\n- New parameters for `optimize_shard_placement` and `recover` database REST API requests"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"enhancements","title":"Enhancements","role":"content","text":"- Added support for SHA-384 certificates when using mutual TLS for client authentication.\n\n- Added an option to the [optimize_shards_placement](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/optimize_shards_placement) REST API request to consider replication when calculating the database’s optimized shard placement.\n\n- Added `shard_uid` and `shard_role` for each persistence file in the response body of the [recover](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/recover) REST API request. When the database has master_persistence enabled, these fields help identify the primary shard's persistence file, which is most up-to-date and recommended for recovery.\n\n#### Redis module feature sets\n\nRedis Enterprise comes packaged with several modules. As of version 7.4.2, Redis Enterprise includes two feature sets, compatible with different Redis database versions.\n\nBundled Redis modules compatible with Redis database version 7.2:\n\n- [RediSearch 2.8.13](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)\n\n- [RedisJSON 2.6.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)\n\n- [RedisTimeSeries 1.10.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)\n\n- [RedisBloom 2.6.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes)\n\n- [RedisGears 2.0.20 preview](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.20-m21): The RedisGears preview will not be promoted to GA and will be removed in a future release.\n\nBundled Redis modules compatible with Redis database versions 6.0 and 6.2:\n\n- [RediSearch 2.6.18](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)\n\n- [RedisJSON 2.4.9](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)\n\n- [RedisTimeSeries 1.8.13](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)\n\n- [RedisBloom 2.4.9](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)\n\n- [RedisGraph v2.10.15](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes): RedisGraph end-of-life has been announced and will be removed in a future release. See the [RedisGraph end-of-life announcement](https://redis.io/blog/redisgraph-eol/) for more details."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS118231: Fixed an issue where configuration changes to `slave_buffer` failed due to shard connection issues but incorrectly reported success.\n\n- RS117757: Increased the number of connections reserved for admin clients to 64 to prevent connection limit issues.\n\n- RS129900: Fixed an issue that prevented creating a database with modules that were uploaded during a cluster upgrade.\n\n- RS128254: Fixed a TLS protocol version compatibility issue with cluster upgrades by setting new nodes to TLS protocol version 1.2 if the cluster's current TLS protocol version is no longer supported.\n\n- RS125593: Added validation to verify the LDAP server URI contains a host and port when updating LDAP configuration."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":"- Added validation to verify the LDAP server URI contains a host and port when updating LDAP configuration."},{"id":"product-lifecycle-updates","title":"Product lifecycle updates","role":"content","text":"#### End-of-life policy extension\n\nThe end-of-life policy for Redis Enterprise Software versions 6.2 and later has been extended to 24 months after the formal release of the subsequent major version. For the updated end-of-life schedule, see the [Redis Enterprise Software product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle).\n\n#### Supported upgrade paths\n\nRedis Enterprise Software versions 6.2.4 and 6.2.8 do not support direct upgrades beyond version 7.4.x. Versions 6.2.10, 6.2.12, and 6.2.18 are part of the [upgrade path](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-cluster#supported-upgrade-paths). To upgrade from 6.2.4 or 6.2.8 to versions later than 7.4.x, an intermediate upgrade is required.\n\nThe next major Redis Enterprise Software release will still bundle Redis database version 6.2 and allow database upgrades from Redis database version 6.2 to 7.x.\n\nSee the [Redis Enterprise Software product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle) for more information about release numbers."},{"id":"deprecations","title":"Deprecations","role":"content","text":"#### Legacy UI deprecation\n\nThe legacy UI is deprecated in favor of the new Cluster Manager UI and will be removed in a future release.\n\n#### Redis 6.0 database deprecation\n\nRedis database version 6.0 is deprecated as of Redis Enterprise Software version 7.4.2 and will be removed in a future release.\n\nTo prepare for the future removal of Redis 6.0:\n\n- For Redis Enterprise 6.2.* clusters, upgrade Redis 6.0 databases to Redis 6.2. See the [Redis 6.2 release notes](https://raw.githubusercontent.com/redis/redis/6.2/00-RELEASENOTES) for the list of changes.\n\n- For Redis Enterprise 7.2.4 and 7.4.2 clusters, upgrade Redis 6.0 databases to Redis 7.2. Before you upgrade your databases, see the list of [Redis 7.2 breaking changes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52#redis-72-breaking-changes) and update any applications that connect to your database to handle these changes."},{"id":"upcoming-changes","title":"Upcoming changes","role":"content","text":"#### Default image change for Redis Enterprise Software containers\n\nStarting with version 7.6, Redis Enterprise Software containers with the image tag `x.y.z-build` will be based on RHEL instead of Ubuntu.\n\nThis change will only affect you if you use containers outside the official [Redis Enterprise for Kubernetes](https://redis.io/docs/latest/operate/kubernetes) product and use Ubuntu-specific commands.\n\nTo use Ubuntu-based images after this change, you can specify the operating system suffix in the image tag. For example, use the image tag `7.4.2-216.focal` instead of `7.4.2-216`."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Enterprise Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Enterprise Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Enterprise Software, but support will be removed in a future release.\n\n| Redis Enterprise<br />major versions | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>\n[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Enterprise Software are certified for development and testing only."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.4.6-22 July release) |\n|---------|---------------------------------------|\n| Ubuntu 18 | <span class=\"break-all\">3bf13415cf013869b6417165c6bc2966cade201a08ac7c2a4a7286990fca03df</span> |\n| Ubuntu 20 | <span class=\"break-all\">c0951fc688e8b02aeec06b0e98baf77988edef7fd2dc828b6af4126ecb9753ae</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">4667c579c52f899a4992cfe5d97e7b4f70ae9f34f5ae9bf75f10cfe2869a80d4</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">6c37b1483b5fb6110c8055c1526aa58eec1d29d3519e92310859101419248831</span> |\n| Amazon Linux 2 | <span class=\"break-all\">5782ac40b18b813f1fd6244f45c7107bfe00baccf3e0f3439149bdd0cbbfd806</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS61676: Full chain certificate update fails if any certificate in the chain does not have a Common Name (CN).\n\n- RS119958: The `debuginfo` script fails with the error `/bin/tar: Argument list too long` if there are too many RocksDB log files. This issue only affects clusters with Auto Tiering."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Remove a node.\n\n    Use the REST API or legacy UI instead. See [Remove a cluster node](https://redis.io/docs/latest/operate/rs/clusters/remove-node) for instructions.\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### OpenSSL compatibility issue for 7.4.2 modules on Amazon Linux 2\n\nDue to an OpenSSL 1.1 compatibility issue between modules and clusters, Redis Enterprise Software version 7.4.2-54 is not fully supported on Amazon Linux 2 clusters with databases that use the following modules: RedisGears, RediSearch, or RedisTimeSeries.\n\nThis issue will be fixed in a future maintenance release.\n\n#### RedisGraph prevents upgrade to RHEL 9 \n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Enterprise cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Enterprise Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Enterprise has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Enterprise due to different or additional functionality available in Redis Enterprise that is not available in open source Redis.\n\nRedis Enterprise 7.4.6-22 supports open source Redis 7.2, 6.2, and 6.0. Below is the list of open source Redis CVEs fixed by version.\n\nRedis 7.2.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)\n\nRedis 6.0.x:\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.0.20)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.0.19)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.0.18)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.0.18)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.0.17)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-232","title":"Redis Enterprise Software release notes 7.4.6-232 (March 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-232/","summary":"Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"15f061ded613d8a7806b89d8814ea20814be1cf4feb9e62e743ee26ac360d0ae","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Enterprise Software version 7.4.6](https://redis.io/downloads/#software)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":"#### Redis module feature sets\n\nRedis Enterprise comes packaged with several modules. As of version 7.4.2, Redis Enterprise includes two feature sets, compatible with different Redis database versions.\n\nBundled Redis modules compatible with Redis database version 7.2:\n\n- [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)\n\n- [RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)\n\n- [RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)\n\n- [RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes)\n\n- [RedisGears 2.0 preview](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.20-m21): The RedisGears preview will not be promoted to GA and will be removed in a future release.\n\nBundled Redis modules compatible with Redis database versions 6.0 and 6.2:\n\n- [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)\n\n- [RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)\n\n- [RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)\n\n- [RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)\n\n- [RedisGraph v2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes): RedisGraph end-of-life has been announced and will be removed in a future release. See the [RedisGraph end-of-life announcement](https://redis.io/blog/redisgraph-eol/) for more details."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"product-lifecycle-updates","title":"Product lifecycle updates","role":"content","text":"#### End-of-life policy extension\n\nThe end-of-life policy for Redis Enterprise Software versions 6.2 and later has been extended to 24 months after the formal release of the subsequent major version. For the updated end-of-life schedule, see the [Redis Enterprise Software product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle).\n\n#### Supported upgrade paths\n\nRedis Enterprise Software versions 6.2.4 and 6.2.8 do not support direct upgrades beyond version 7.4.x. Versions 6.2.10, 6.2.12, and 6.2.18 are part of the [upgrade path](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-cluster#supported-upgrade-paths). To upgrade from 6.2.4 or 6.2.8 to versions later than 7.4.x, an intermediate upgrade is required.\n\nThe next major Redis Enterprise Software release will still bundle Redis database version 6.2 and allow database upgrades from Redis database version 6.2 to 7.x.\n\nSee the [Redis Enterprise Software product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle) for more information about release numbers."},{"id":"deprecations","title":"Deprecations","role":"content","text":"#### Legacy UI deprecation\n\nThe legacy UI is deprecated in favor of the new Cluster Manager UI and will be removed in a future release.\n\n#### Redis 6.0 database deprecation\n\nRedis database version 6.0 is deprecated as of Redis Enterprise Software version 7.4.2 and will be removed in a future release.\n\nTo prepare for the future removal of Redis 6.0:\n\n- For Redis Enterprise 6.2.* clusters, upgrade Redis 6.0 databases to Redis 6.2. See the [Redis 6.2 release notes](https://raw.githubusercontent.com/redis/redis/6.2/00-RELEASENOTES) for the list of changes.\n\n- For Redis Enterprise 7.2.4 and 7.4.x clusters, upgrade Redis 6.0 databases to Redis 7.2. Before you upgrade your databases, see the list of [Redis 7.2 breaking changes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52#redis-72-breaking-changes) and update any applications that connect to your database to handle these changes.\n\n#### End of triggers and functions preview\n\nThe [triggers and functions](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions) (RedisGears) preview has been discontinued.\n\n- Commands such as `TFCALL`, `TFCALLASYNC`, and `TFUNCTION` will be deprecated and will return error messages.\n\n- Any JavaScript functions stored in Redis will be removed. \n\n- JavaScript-based triggers will be blocked.\n\n- Lua functions and scripts will not be affected.\n\nIf your database currently uses triggers and functions, you need to: \n\n1. Adjust your applications to accommodate these changes.\n\n1. Delete all triggers and functions libraries from your existing database:\n\n    1. Run `TFUNCTION LIST`.\n\n    1. Copy all library names.\n\n    1. Run `TFUNCTION DELETE` for each library in the list.\n\n    If any triggers and functions libraries remain in the database, the RDB snapshot won't load on a cluster without RedisGears.\n\n1. Migrate your database to a new database without the RedisGears module.\n\n#### RedisGraph end of life\n\nRedis has announced the end of life for RedisGraph. Redis will continue to support all RedisGraph customers, including releasing patch versions until January 31, 2025.\n\nSee the [RedisGraph end-of-life announcement](https://redis.com/blog/redisgraph-eol/) for more details."},{"id":"upcoming-changes","title":"Upcoming changes","role":"content","text":"#### Default image change for Redis Enterprise Software containers\n\nStarting with the next major version, Redis Enterprise Software containers with the image tag `x.y.z-build` will be based on RHEL instead of Ubuntu.\n\nThis change will only affect you if you use containers outside the official [Redis Enterprise for Kubernetes](https://redis.io/docs/latest/operate/kubernetes) product and use Ubuntu-specific commands.\n\nTo use Ubuntu-based images after this change, you can specify the operating system suffix in the image tag. For example, use the image tag `7.4.2-216.focal` instead of `7.4.2-216`."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.4.6-232 March release) |\n|---------|---------------------------------------|\n| Ubuntu 18 | <span class=\"break-all\">b1657f0a9298899e8f671686c981dcc2be2f7fc339e46fde8d8545f402de1f76</span> |\n| Ubuntu 20 | <span class=\"break-all\">bd30669650d3ae7b4a768365bf3166d9a466fb8aa7067d84a3e6162d2004ca77</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">4b50b6648a601e20425d64d0a32cb18f5e25010e2b2f43afe5e0accc32db9a48</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">12ff65d14d4dea143748ea9f2b91904dd94167a67c7955cc70cfe810fc8f56f8</span> |\n| Amazon Linux 2 | <span class=\"break-all\">8d7a6fb648b844aa176d5200be8c6388aa1efe2d520af54155733441c40c146d</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors.\n\n- RS61676: Full chain certificate update fails if any certificate in the chain does not have a Common Name (CN).\n\n- RS119958: The `debuginfo` script fails with the error `/bin/tar: Argument list too long` if there are too many RocksDB log files. This issue only affects clusters with Auto Tiering."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Remove a node.\n\n    Use the REST API or legacy UI instead. See [Remove a cluster node](https://redis.io/docs/latest/operate/rs/clusters/remove-node) for instructions.\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### OpenSSL compatibility issue for 7.4.2 modules on Amazon Linux 2\n\nDue to an OpenSSL 1.1 compatibility issue between modules and clusters, Redis Enterprise Software version 7.4.2-54 is not fully supported on Amazon Linux 2 clusters with databases that use the following modules: RedisGears, RediSearch, or RedisTimeSeries.\n\nThis issue will be fixed in a future maintenance release.\n\n#### RedisGraph prevents upgrade to RHEL 9 \n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Enterprise cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Enterprise Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Enterprise has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Enterprise due to different or additional functionality available in Redis Enterprise that is not available in open source Redis.\n\nRedis Enterprise 7.4.6-232 supports open source Redis 7.2, 6.2, and 6.0. Below is the list of open source Redis CVEs fixed by version.\n\nRedis 7.2.x:\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)\n\nRedis 6.0.x:\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.0.20)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.0.19)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.0.18)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.0.18)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.0.17)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-268","title":"Redis Enterprise Software release notes 7.4.6-268 (June 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-268/","summary":"Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"a9b19f7515a5b006b360428ab857b119b3bd6df24d7a7d38bd8b5a4538bc0cad","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​​Redis Enterprise Software version 7.4.6."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":"#### Redis module feature sets\n\nRedis Enterprise comes packaged with several modules. As of version 7.4.2, Redis Enterprise includes two feature sets, compatible with different Redis database versions.\n\nBundled Redis modules compatible with Redis database version 7.2:\n\n- [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)\n\n- [RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)\n\n- [RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)\n\n- [RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes)\n\n- [RedisGears 2.0 preview](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.20-m21): The RedisGears preview will not be promoted to GA and will be removed in a future release.\n\nBundled Redis modules compatible with Redis database versions 6.0 and 6.2:\n\n- [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)\n\n- [RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)\n\n- [RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)\n\n- [RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)\n\n- [RedisGraph v2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes): RedisGraph end-of-life has been announced and will be removed in a future release. See the [RedisGraph end-of-life announcement](https://redis.io/blog/redisgraph-eol/) for more details."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"product-lifecycle-updates","title":"Product lifecycle updates","role":"content","text":"#### End-of-life policy extension\n\nThe end-of-life policy for Redis Enterprise Software versions 6.2 and later has been extended to 24 months after the formal release of the subsequent major version. For the updated end-of-life schedule, see the [Redis Enterprise Software product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle).\n\n#### Supported upgrade paths\n\nRedis Enterprise Software versions 6.2.4 and 6.2.8 do not support direct upgrades beyond version 7.4.x. Versions 6.2.10, 6.2.12, and 6.2.18 are part of the [upgrade path](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-cluster#supported-upgrade-paths). To upgrade from 6.2.4 or 6.2.8 to versions later than 7.4.x, an intermediate upgrade is required.\n\nThe next major Redis Enterprise Software release will still bundle Redis database version 6.2 and allow database upgrades from Redis database version 6.2 to 7.x.\n\nSee the [Redis Enterprise Software product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle) for more information about release numbers."},{"id":"deprecations","title":"Deprecations","role":"content","text":"#### Legacy UI deprecation\n\nThe legacy UI is deprecated in favor of the new Cluster Manager UI and will be removed in a future release.\n\n#### Redis 6.0 database deprecation\n\nRedis database version 6.0 is deprecated as of Redis Enterprise Software version 7.4.2 and will be removed in a future release.\n\nTo prepare for the future removal of Redis 6.0:\n\n- For Redis Enterprise 6.2.* clusters, upgrade Redis 6.0 databases to Redis 6.2. See the [Redis 6.2 release notes](https://raw.githubusercontent.com/redis/redis/6.2/00-RELEASENOTES) for the list of changes.\n\n- For Redis Enterprise 7.2.4 and 7.4.x clusters, upgrade Redis 6.0 databases to Redis 7.2. Before you upgrade your databases, see the list of [Redis 7.2 breaking changes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52#redis-72-breaking-changes) and update any applications that connect to your database to handle these changes.\n\n#### End of triggers and functions preview\n\nThe [triggers and functions](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions) (RedisGears) preview has been discontinued.\n\n- Commands such as `TFCALL`, `TFCALLASYNC`, and `TFUNCTION` will be deprecated and will return error messages.\n\n- Any JavaScript functions stored in Redis will be removed. \n\n- JavaScript-based triggers will be blocked.\n\n- Lua functions and scripts will not be affected.\n\nIf your database currently uses triggers and functions, you need to: \n\n1. Adjust your applications to accommodate these changes.\n\n1. Delete all triggers and functions libraries from your existing database:\n\n    1. Run `TFUNCTION LIST`.\n\n    1. Copy all library names.\n\n    1. Run `TFUNCTION DELETE` for each library in the list.\n\n    If any triggers and functions libraries remain in the database, the RDB snapshot won't load on a cluster without RedisGears.\n\n1. Migrate your database to a new database without the RedisGears module.\n\n#### RedisGraph end of life\n\nRedisGraph reached end of life on January 31, 2025. See the [RedisGraph end-of-life announcement](https://redis.com/blog/redisgraph-eol/) for more details."},{"id":"upcoming-changes","title":"Upcoming changes","role":"content","text":"#### Default image change for Redis Enterprise Software containers\n\nStarting with the next major version, Redis Enterprise Software containers with the image tag `x.y.z-build` will be based on RHEL instead of Ubuntu.\n\nThis change will only affect you if you use containers outside the official [Redis Enterprise for Kubernetes](https://redis.io/docs/latest/operate/kubernetes) product and use Ubuntu-specific commands.\n\nTo use Ubuntu-based images after this change, you can specify the operating system suffix in the image tag. For example, use the image tag `7.4.2-216.focal` instead of `7.4.2-216`."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Enterprise Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Enterprise Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Enterprise Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.4.6-268 June release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">b6f24453563bf05fc5fcc503c826a481f207b392566cb64efd4b2d47b3d8e65a</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">16c612869cd5037f9fdf367c8e4010d1d526cf8e769930505ea8a40aa128c10f</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">dfb3391542214ea9d38edd8f3f15c7e0c09dd3ddd8f62817b931319173d2355e</span> |\n| Amazon Linux 2 | <span class=\"break-all\">cf25e3afadefbfc6a1da45c0efbf85785172f8e1e27d4824b1bcbfb9f6b63be4</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors.\n\n- RS61676: Full chain certificate update fails if any certificate in the chain does not have a Common Name (CN).\n\n- RS119958: The `debuginfo` script fails with the error `/bin/tar: Argument list too long` if there are too many RocksDB log files. This issue only affects clusters with Auto Tiering."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Remove a node.\n\n    Use the REST API or legacy UI instead. See [Remove a cluster node](https://redis.io/docs/latest/operate/rs/clusters/remove-node) for instructions.\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### OpenSSL compatibility issue for 7.4.2 modules on Amazon Linux 2\n\nDue to an OpenSSL 1.1 compatibility issue between modules and clusters, Redis Enterprise Software version 7.4.2-54 is not fully supported on Amazon Linux 2 clusters with databases that use the following modules: RedisGears, RediSearch, or RedisTimeSeries.\n\nThis issue will be fixed in a future maintenance release.\n\n#### RedisGraph prevents upgrade to RHEL 9 \n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Enterprise cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Enterprise Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Enterprise has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Enterprise due to different or additional functionality available in Redis Enterprise that is not available in open source Redis.\n\nRedis Enterprise 7.4.6-268 supports open source Redis 7.2, 6.2, and 6.0. Below is the list of open source Redis CVEs fixed by version.\n\nRedis 7.2.x:\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)\n\nRedis 6.0.x:\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.0.20)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.0.19)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.0.18)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.0.18)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.0.17)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-272","title":"Redis Enterprise Software release notes 7.4.6-272 (September 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-272/","summary":"Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"86f6ec4b9197a7b46638e0e1d3ee3ac8ae264e174ea4d659c0e64089c3912dad","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​​Redis Enterprise Software version 7.4.6."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":"#### Redis module feature sets\n\nRedis Enterprise comes packaged with several modules. As of version 7.4.2, Redis Enterprise includes two feature sets, compatible with different Redis database versions.\n\nBundled Redis modules compatible with Redis database version 7.2:\n\n- [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)\n\n- [RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)\n\n- [RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)\n\n- [RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes)\n\n- [RedisGears 2.0 preview](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.20-m21): The RedisGears preview will not be promoted to GA and will be removed in a future release.\n\nBundled Redis modules compatible with Redis database versions 6.0 and 6.2:\n\n- [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)\n\n- [RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)\n\n- [RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)\n\n- [RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)\n\n- [RedisGraph v2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes): RedisGraph end-of-life has been announced and will be removed in a future release. See the [RedisGraph end-of-life announcement](https://redis.io/blog/redisgraph-eol/) for more details."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"product-lifecycle-updates","title":"Product lifecycle updates","role":"content","text":"#### End-of-life policy extension\n\nThe end-of-life policy for Redis Enterprise Software versions 6.2 and later has been extended to 24 months after the formal release of the subsequent major version. For the updated end-of-life schedule, see the [Redis Enterprise Software product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle).\n\n#### Supported upgrade paths\n\nRedis Enterprise Software versions 6.2.4 and 6.2.8 do not support direct upgrades beyond version 7.4.x. Versions 6.2.10, 6.2.12, and 6.2.18 are part of the [upgrade path](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-cluster#supported-upgrade-paths). To upgrade from 6.2.4 or 6.2.8 to versions later than 7.4.x, an intermediate upgrade is required.\n\nThe next major Redis Enterprise Software release will still bundle Redis database version 6.2 and allow database upgrades from Redis database version 6.2 to 7.x.\n\nSee the [Redis Enterprise Software product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle) for more information about release numbers."},{"id":"deprecations","title":"Deprecations","role":"content","text":"#### Legacy UI deprecation\n\nThe legacy UI is deprecated in favor of the new Cluster Manager UI and will be removed in a future release.\n\n#### Redis 6.0 database deprecation\n\nRedis database version 6.0 is deprecated as of Redis Enterprise Software version 7.4.2 and will be removed in a future release.\n\nTo prepare for the future removal of Redis 6.0:\n\n- For Redis Enterprise 6.2.* clusters, upgrade Redis 6.0 databases to Redis 6.2. See the [Redis 6.2 release notes](https://raw.githubusercontent.com/redis/redis/6.2/00-RELEASENOTES) for the list of changes.\n\n- For Redis Enterprise 7.2.4 and 7.4.x clusters, upgrade Redis 6.0 databases to Redis 7.2. Before you upgrade your databases, see the list of [Redis 7.2 breaking changes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52#redis-72-breaking-changes) and update any applications that connect to your database to handle these changes.\n\n#### End of triggers and functions preview\n\nThe [triggers and functions](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions) (RedisGears) preview has been discontinued.\n\n- Commands such as `TFCALL`, `TFCALLASYNC`, and `TFUNCTION` will be deprecated and will return error messages.\n\n- Any JavaScript functions stored in Redis will be removed. \n\n- JavaScript-based triggers will be blocked.\n\n- Lua functions and scripts will not be affected.\n\nIf your database currently uses triggers and functions, you need to: \n\n1. Adjust your applications to accommodate these changes.\n\n1. Delete all triggers and functions libraries from your existing database:\n\n    1. Run `TFUNCTION LIST`.\n\n    1. Copy all library names.\n\n    1. Run `TFUNCTION DELETE` for each library in the list.\n\n    If any triggers and functions libraries remain in the database, the RDB snapshot won't load on a cluster without RedisGears.\n\n1. Migrate your database to a new database without the RedisGears module.\n\n#### RedisGraph end of life\n\nRedisGraph reached end of life on January 31, 2025. See the [RedisGraph end-of-life announcement](https://redis.com/blog/redisgraph-eol/) for more details."},{"id":"upcoming-changes","title":"Upcoming changes","role":"content","text":"#### Default image change for Redis Enterprise Software containers\n\nStarting with the next major version, Redis Enterprise Software containers with the image tag `x.y.z-build` will be based on RHEL instead of Ubuntu.\n\nThis change will only affect you if you use containers outside the official [Redis Enterprise for Kubernetes](https://redis.io/docs/latest/operate/kubernetes) product and use Ubuntu-specific commands.\n\nTo use Ubuntu-based images after this change, you can specify the operating system suffix in the image tag. For example, use the image tag `7.4.2-216.focal` instead of `7.4.2-216`."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Enterprise Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Enterprise Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Enterprise Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.4.6-272 September release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">64ce550109e0147aff92df867f9fc9887591e4ab973c8962a8b56a0b2bcb5a07</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">2df3e06d34a89311224cccf97a7ebf2569e692eac322f72ca99e3b0459f24d0f</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">d1b2ccd47416639ff5c0915cc6dfbaabfddde14275debd416f8e43ab31bbcc59</span> |\n| Amazon Linux 2 | <span class=\"break-all\">f133dea58cb5c090dde493f730750ba573153e1135e06515118782000442b7ab</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors.\n\n- RS61676: Full chain certificate update fails if any certificate in the chain does not have a Common Name (CN).\n\n- RS119958: The `debuginfo` script fails with the error `/bin/tar: Argument list too long` if there are too many RocksDB log files. This issue only affects clusters with Auto Tiering."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Remove a node.\n\n    Use the REST API or legacy UI instead. See [Remove a cluster node](https://redis.io/docs/latest/operate/rs/clusters/remove-node) for instructions.\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### OpenSSL compatibility issue for 7.4.2 modules on Amazon Linux 2\n\nDue to an OpenSSL 1.1 compatibility issue between modules and clusters, Redis Enterprise Software version 7.4.2-54 is not fully supported on Amazon Linux 2 clusters with databases that use the following modules: RedisGears, RediSearch, or RedisTimeSeries.\n\nThis issue will be fixed in a future maintenance release.\n\n#### RedisGraph prevents upgrade to RHEL 9 \n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Enterprise cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Enterprise Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Enterprise has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Enterprise due to different or additional functionality available in Redis Enterprise that is not available in open source Redis.\n\nRedis Enterprise 7.4.6-272 supports open source Redis 7.2, 6.2, and 6.0. Below is the list of open source Redis CVEs and other security vulnerabilities fixed by version.\n\nRedis 7.2.x:\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)\n\nRedis 6.0.x:\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.0.20)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.0.19)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.0.18)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.0.18)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.0.17)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-279","title":"Redis Software release notes 7.4.6-279 (February 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-279/","summary":"Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"7b6080857c24d34807a3cbac7af5742f77f1a740c50b0bafb5cf48a5d51cdcc7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​​Redis Software version 7.4.6."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":"#### Redis module feature sets\n\nRedis Software comes packaged with several modules. As of version 7.4.2, Redis Software includes two feature sets, compatible with different Redis database versions.\n\nBundled Redis modules compatible with Redis database version 7.2:\n\n- [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)\n\n- [RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)\n\n- [RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)\n\n- [RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes)\n\n- [RedisGears 2.0 preview](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.20-m21): The RedisGears preview will not be promoted to GA and will be removed in a future release.\n\nBundled Redis modules compatible with Redis database versions 6.0 and 6.2:\n\n- [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)\n\n- [RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)\n\n- [RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)\n\n- [RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)\n\n- [RedisGraph v2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes): RedisGraph end-of-life has been announced and will be removed in a future release. See the [RedisGraph end-of-life announcement](https://redis.io/blog/redisgraph-eol/) for more details."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"product-lifecycle-updates","title":"Product lifecycle updates","role":"content","text":"#### End-of-life policy extension\n\nThe end-of-life policy for Redis Software versions 6.2 and later has been extended to 24 months after the formal release of the subsequent major version. For the updated end-of-life schedule, see the [Redis Software product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle).\n\n#### Supported upgrade paths\n\nRedis Software versions 6.2.4 and 6.2.8 do not support direct upgrades beyond version 7.4.x. Versions 6.2.10, 6.2.12, and 6.2.18 are part of the [upgrade path](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-cluster#supported-upgrade-paths). To upgrade from 6.2.4 or 6.2.8 to versions later than 7.4.x, an intermediate upgrade is required.\n\nThe next major Redis Software release will still bundle Redis database version 6.2 and allow database upgrades from Redis database version 6.2 to 7.x.\n\nSee the [Redis Software product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle) for more information about release numbers."},{"id":"deprecations","title":"Deprecations","role":"content","text":"#### Legacy UI deprecation\n\nThe legacy UI is deprecated in favor of the new Cluster Manager UI and will be removed in a future release.\n\n#### Redis 6.0 database deprecation\n\nRedis database version 6.0 is deprecated as of Redis Software version 7.4.2 and will be removed in a future release.\n\nTo prepare for the future removal of Redis 6.0:\n\n- For Redis Software 6.2.* clusters, upgrade Redis 6.0 databases to Redis 6.2. See the [Redis 6.2 release notes](https://raw.githubusercontent.com/redis/redis/6.2/00-RELEASENOTES) for the list of changes.\n\n- For Redis Software 7.2.4 and 7.4.x clusters, upgrade Redis 6.0 databases to Redis 7.2. Before you upgrade your databases, see the list of [Redis 7.2 breaking changes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52#redis-72-breaking-changes) and update any applications that connect to your database to handle these changes.\n\n#### End of triggers and functions preview\n\nThe [triggers and functions](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions) (RedisGears) preview has been discontinued.\n\n- Commands such as `TFCALL`, `TFCALLASYNC`, and `TFUNCTION` will be deprecated and will return error messages.\n\n- Any JavaScript functions stored in Redis will be removed. \n\n- JavaScript-based triggers will be blocked.\n\n- Lua functions and scripts will not be affected.\n\nIf your database currently uses triggers and functions, you need to: \n\n1. Adjust your applications to accommodate these changes.\n\n1. Delete all triggers and functions libraries from your existing database:\n\n    1. Run `TFUNCTION LIST`.\n\n    1. Copy all library names.\n\n    1. Run `TFUNCTION DELETE` for each library in the list.\n\n    If any triggers and functions libraries remain in the database, the RDB snapshot won't load on a cluster without RedisGears.\n\n1. Migrate your database to a new database without the RedisGears module.\n\n#### RedisGraph end of life\n\nRedisGraph reached end of life on January 31, 2025. See the [RedisGraph end-of-life announcement](https://redis.com/blog/redisgraph-eol/) for more details."},{"id":"upcoming-changes","title":"Upcoming changes","role":"content","text":"#### Default image change for Redis Software containers\n\nStarting with the next major version, Redis Software containers with the image tag `x.y.z-build` will be based on RHEL instead of Ubuntu.\n\nThis change will only affect you if you use containers outside the official [Redis Enterprise for Kubernetes](https://redis.io/docs/latest/operate/kubernetes) product and use Ubuntu-specific commands.\n\nTo use Ubuntu-based images after this change, you can specify the operating system suffix in the image tag. For example, use the image tag `7.4.2-216.focal` instead of `7.4.2-216`."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.4.6-279 February release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">fbf7054b9908843248fb4f594a7555e6f9d788e8a54b9bb2e96ace632886acf7</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">5a2cd5735a0403208fe9a4fd90589cd4d248249f9218796a31e134ab54080004</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">ec1da1af74554545634af7001c2fef608c187f348842c028484f76f626809140</span> |\n| Amazon Linux 2 | <span class=\"break-all\">23d08c517e414b6451f1d8ed44d0e0b44eac1219055983832214464d0f18e846</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors.\n\n- RS61676: Full chain certificate update fails if any certificate in the chain does not have a Common Name (CN).\n\n- RS119958: The `debuginfo` script fails with the error `/bin/tar: Argument list too long` if there are too many RocksDB log files. This issue only affects clusters with Auto Tiering."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Remove a node.\n\n    Use the REST API or legacy UI instead. See [Remove a cluster node](https://redis.io/docs/latest/operate/rs/clusters/remove-node) for instructions.\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### OpenSSL compatibility issue for 7.4.2 modules on Amazon Linux 2\n\nDue to an OpenSSL 1.1 compatibility issue between modules and clusters, Redis Software version 7.4.2-54 is not fully supported on Amazon Linux 2 clusters with databases that use the following modules: RedisGears, RediSearch, or RedisTimeSeries.\n\nThis issue will be fixed in a future maintenance release.\n\n#### RedisGraph prevents upgrade to RHEL 9 \n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.4.6-279 supports open source Redis 7.2, 6.2, and 6.0. Below is the list of open source Redis CVEs and other security vulnerabilities fixed by version.\n\nRedis 7.2.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)\n\nRedis 6.0.x:\n\n- RedisBloom: Restore invalid filter.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.0.20)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.0.19)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.0.18)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.0.18)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.0.17)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-280","title":"Redis Software release notes 7.4.6-280 (June 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-280/","summary":"Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"1f81c8aed768a73e49ceb97f02a0c6d0bf42146d1d90fa4a376312728b166969","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​​Redis Software version 7.4.6."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":"#### Redis module feature sets\n\nRedis Software comes packaged with several modules. As of version 7.4.2, Redis Software includes two feature sets, compatible with different Redis database versions.\n\nBundled Redis modules compatible with Redis database version 7.2:\n\n- [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)\n\n- [RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)\n\n- [RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)\n\n- [RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes)\n\n- [RedisGears 2.0 preview](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.20-m21): The RedisGears preview will not be promoted to GA and will be removed in a future release.\n\nBundled Redis modules compatible with Redis database versions 6.0 and 6.2:\n\n- [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)\n\n- [RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)\n\n- [RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)\n\n- [RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)\n\n- [RedisGraph v2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes): RedisGraph end-of-life has been announced and will be removed in a future release. See the [RedisGraph end-of-life announcement](https://redis.io/blog/redisgraph-eol/) for more details."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"product-lifecycle-updates","title":"Product lifecycle updates","role":"content","text":"#### End-of-life policy extension\n\nThe end-of-life policy for Redis Software versions 6.2 and later has been extended to 24 months after the formal release of the subsequent major version. For the updated end-of-life schedule, see the [Redis Software product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle).\n\n#### Supported upgrade paths\n\nRedis Software versions 6.2.4 and 6.2.8 do not support direct upgrades beyond version 7.4.x. Versions 6.2.10, 6.2.12, and 6.2.18 are part of the [upgrade path](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-cluster#supported-upgrade-paths). To upgrade from 6.2.4 or 6.2.8 to versions later than 7.4.x, an intermediate upgrade is required.\n\nThe next major Redis Software release will still bundle Redis database version 6.2 and allow database upgrades from Redis database version 6.2 to 7.x.\n\nSee the [Redis Software product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle) for more information about release numbers."},{"id":"deprecations","title":"Deprecations","role":"content","text":"#### Legacy UI deprecation\n\nThe legacy UI is deprecated in favor of the new Cluster Manager UI and will be removed in a future release.\n\n#### Redis 6.0 database deprecation\n\nRedis database version 6.0 is deprecated as of Redis Software version 7.4.2 and will be removed in a future release.\n\nTo prepare for the future removal of Redis 6.0:\n\n- For Redis Software 6.2.* clusters, upgrade Redis 6.0 databases to Redis 6.2. See the [Redis 6.2 release notes](https://raw.githubusercontent.com/redis/redis/6.2/00-RELEASENOTES) for the list of changes.\n\n- For Redis Software 7.2.4 and 7.4.x clusters, upgrade Redis 6.0 databases to Redis 7.2. Before you upgrade your databases, see the list of [Redis 7.2 breaking changes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52#redis-72-breaking-changes) and update any applications that connect to your database to handle these changes.\n\n#### End of triggers and functions preview\n\nThe [triggers and functions](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions) (RedisGears) preview has been discontinued.\n\n- Commands such as `TFCALL`, `TFCALLASYNC`, and `TFUNCTION` will be deprecated and will return error messages.\n\n- Any JavaScript functions stored in Redis will be removed. \n\n- JavaScript-based triggers will be blocked.\n\n- Lua functions and scripts will not be affected.\n\nIf your database currently uses triggers and functions, you need to: \n\n1. Adjust your applications to accommodate these changes.\n\n1. Delete all triggers and functions libraries from your existing database:\n\n    1. Run `TFUNCTION LIST`.\n\n    1. Copy all library names.\n\n    1. Run `TFUNCTION DELETE` for each library in the list.\n\n    If any triggers and functions libraries remain in the database, the RDB snapshot won't load on a cluster without RedisGears.\n\n1. Migrate your database to a new database without the RedisGears module.\n\n#### RedisGraph end of life\n\nRedisGraph reached end of life on January 31, 2025. See the [RedisGraph end-of-life announcement](https://redis.com/blog/redisgraph-eol/) for more details."},{"id":"upcoming-changes","title":"Upcoming changes","role":"content","text":"#### Default image change for Redis Software containers\n\nStarting with the next major version, Redis Software containers with the image tag `x.y.z-build` will be based on RHEL instead of Ubuntu.\n\nThis change will only affect you if you use containers outside the official [Redis Enterprise for Kubernetes](https://redis.io/docs/latest/operate/kubernetes) product and use Ubuntu-specific commands.\n\nTo use Ubuntu-based images after this change, you can specify the operating system suffix in the image tag. For example, use the image tag `7.4.2-216.focal` instead of `7.4.2-216`."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.4.6-280 June release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">be83f6d827f2cc9ddeb4f1a61b7c9cbdfe3f511da13aa786e44def9aea71d1f9</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">c1b258b68c63394e07905500af37fdad65cf56f6d9b7b49fdb0f5113c6a3326a</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">25d1db81d2e95b73680baeebcb73d59943f727e77b5c93b6eaf1a081e8c5d917</span> |\n| Amazon Linux 2 | <span class=\"break-all\">900d0bde42c83c7c8446befe76824a7d30b5ecb1ca601bd05df50ad541bd1497</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors.\n\n- RS61676: Full chain certificate update fails if any certificate in the chain does not have a Common Name (CN).\n\n- RS119958: The `debuginfo` script fails with the error `/bin/tar: Argument list too long` if there are too many RocksDB log files. This issue only affects clusters with Auto Tiering."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Remove a node.\n\n    Use the REST API or legacy UI instead. See [Remove a cluster node](https://redis.io/docs/latest/operate/rs/clusters/remove-node) for instructions.\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### OpenSSL compatibility issue for 7.4.2 modules on Amazon Linux 2\n\nDue to an OpenSSL 1.1 compatibility issue between modules and clusters, Redis Software version 7.4.2-54 is not fully supported on Amazon Linux 2 clusters with databases that use the following modules: RedisGears, RediSearch, or RedisTimeSeries.\n\nThis issue will be fixed in a future maintenance release.\n\n#### RedisGraph prevents upgrade to RHEL 9 \n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.4.6-280 supports open source Redis 7.2, 6.2, and 6.0. Below is the list of open source Redis CVEs and other security vulnerabilities fixed by version.\n\n\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\n\n\n\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\n\n\n\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)\n\n\n\n\n\n- RedisBloom: Restore invalid filter.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.0.20)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.0.19)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.0.18)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.0.18)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.0.17)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-283","title":"Redis Software release notes 7.4.6-283 (August 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-283/","summary":"Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"b1e5aa10c1d0af6bedeee3739763bbba6a7482ed3cc1175f11c427ba493f9733","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​​Redis Software version 7.4.6."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":"#### Redis module feature sets\n\nRedis Software comes packaged with several modules. As of version 7.4.2, Redis Software includes two feature sets, compatible with different Redis database versions.\n\nBundled Redis modules compatible with Redis database version 7.2:\n\n- [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)\n\n- [RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)\n\n- [RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)\n\n- [RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes)\n\n- [RedisGears 2.0 preview](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.20-m21): The RedisGears preview will not be promoted to GA and will be removed in a future release.\n\nBundled Redis modules compatible with Redis database versions 6.0 and 6.2:\n\n- [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)\n\n- [RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)\n\n- [RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)\n\n- [RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)\n\n- [RedisGraph v2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes): RedisGraph end-of-life has been announced and will be removed in a future release. See the [RedisGraph end-of-life announcement](https://redis.io/blog/redisgraph-eol/) for more details."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"product-lifecycle-updates","title":"Product lifecycle updates","role":"content","text":"#### End-of-life policy extension\n\nThe end-of-life policy for Redis Software versions 6.2 and later has been extended to 24 months after the formal release of the subsequent major version. For the updated end-of-life schedule, see the [Redis Software product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle).\n\n#### Supported upgrade paths\n\nRedis Software versions 6.2.4 and 6.2.8 do not support direct upgrades beyond version 7.4.x. Versions 6.2.10, 6.2.12, and 6.2.18 are part of the [upgrade path](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-cluster#supported-upgrade-paths). To upgrade from 6.2.4 or 6.2.8 to versions later than 7.4.x, an intermediate upgrade is required.\n\nThe next major Redis Software release will still bundle Redis database version 6.2 and allow database upgrades from Redis database version 6.2 to 7.x.\n\nSee the [Redis Software product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle) for more information about release numbers."},{"id":"deprecations","title":"Deprecations","role":"content","text":"#### Legacy UI deprecation\n\nThe legacy UI is deprecated in favor of the new Cluster Manager UI and will be removed in a future release.\n\n#### Redis 6.0 database deprecation\n\nRedis database version 6.0 is deprecated as of Redis Software version 7.4.2 and will be removed in a future release.\n\nTo prepare for the future removal of Redis 6.0:\n\n- For Redis Software 6.2.* clusters, upgrade Redis 6.0 databases to Redis 6.2. See the [Redis 6.2 release notes](https://raw.githubusercontent.com/redis/redis/6.2/00-RELEASENOTES) for the list of changes.\n\n- For Redis Software 7.2.4 and 7.4.x clusters, upgrade Redis 6.0 databases to Redis 7.2. Before you upgrade your databases, see the list of [Redis 7.2 breaking changes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52#redis-72-breaking-changes) and update any applications that connect to your database to handle these changes.\n\n#### End of triggers and functions preview\n\nThe [triggers and functions](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions) (RedisGears) preview has been discontinued.\n\n- Commands such as `TFCALL`, `TFCALLASYNC`, and `TFUNCTION` will be deprecated and will return error messages.\n\n- Any JavaScript functions stored in Redis will be removed. \n\n- JavaScript-based triggers will be blocked.\n\n- Lua functions and scripts will not be affected.\n\nIf your database currently uses triggers and functions, you need to: \n\n1. Adjust your applications to accommodate these changes.\n\n1. Delete all triggers and functions libraries from your existing database:\n\n    1. Run `TFUNCTION LIST`.\n\n    1. Copy all library names.\n\n    1. Run `TFUNCTION DELETE` for each library in the list.\n\n    If any triggers and functions libraries remain in the database, the RDB snapshot won't load on a cluster without RedisGears.\n\n1. Migrate your database to a new database without the RedisGears module.\n\n#### RedisGraph end of life\n\nRedisGraph reached end of life on January 31, 2025. See the [RedisGraph end-of-life announcement](https://redis.com/blog/redisgraph-eol/) for more details."},{"id":"upcoming-changes","title":"Upcoming changes","role":"content","text":"#### Default image change for Redis Software containers\n\nStarting with the next major version, Redis Software containers with the image tag `x.y.z-build` will be based on RHEL instead of Ubuntu.\n\nThis change will only affect you if you use containers outside the official [Redis Enterprise for Kubernetes](https://redis.io/docs/latest/operate/kubernetes) product and use Ubuntu-specific commands.\n\nTo use Ubuntu-based images after this change, you can specify the operating system suffix in the image tag. For example, use the image tag `7.4.2-216.focal` instead of `7.4.2-216`."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.4.6-283 August release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">c7bcd52823997fd49fd963cccffce3bb9e754caa1eefdcfabf949fbf2a1a1f74</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">4d6c727c28d3c5b03678e554823b0b9d16967b6b8bbb8248d9306d7ed8170ea4</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">042dc8097ceb84d978b791616de65dcdf86a1d3bd7d981354ce7523a925d325d</span> |\n| Amazon Linux 2 | <span class=\"break-all\">d257aa66424af05d5bb10353859f9efc302166ac1d4a466c66fc7b2a2f7dbce0</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors.\n\n- RS61676: Full chain certificate update fails if any certificate in the chain does not have a Common Name (CN).\n\n- RS119958: The `debuginfo` script fails with the error `/bin/tar: Argument list too long` if there are too many RocksDB log files. This issue only affects clusters with Auto Tiering."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Remove a node.\n\n    Use the REST API or legacy UI instead. See [Remove a cluster node](https://redis.io/docs/latest/operate/rs/clusters/remove-node) for instructions.\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### OpenSSL compatibility issue for 7.4.2 modules on Amazon Linux 2\n\nDue to an OpenSSL 1.1 compatibility issue between modules and clusters, Redis Software version 7.4.2-54 is not fully supported on Amazon Linux 2 clusters with databases that use the following modules: RedisGears, RediSearch, or RedisTimeSeries.\n\nThis issue will be fixed in a future maintenance release.\n\n#### RedisGraph prevents upgrade to RHEL 9 \n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.4.6-283 supports open source Redis 7.2, 6.2, and 6.0. Below is the list of open source Redis CVEs and other security vulnerabilities fixed by version.\n\n\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\n\n\n\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\n\n\n\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)\n\n\n\n\n\n- RedisBloom: Restore invalid filter.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.0.20)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.0.19)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.0.18)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.0.18)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.0.17)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-293","title":"Redis Software release notes 7.4.6-293 (October 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-293/","summary":"Ubuntu 18.04 is no longer supported. Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-10-13T14:39:41-05:00","page_type":"content","content_hash":"a4829a7b156b025e0fbf63c40fc399bc44fccf4bc40199e2a213a2351134d8b0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for Redis Software version 7.4.6."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Ubuntu 18.04, which was previously deprecated, is no longer supported\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":"#### Redis module feature sets\n\nRedis Software comes packaged with several modules. As of version 7.4.2, Redis Software includes two feature sets, compatible with different Redis database versions.\n\nBundled Redis modules compatible with Redis database version 7.2:\n\n- [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)\n\n- [RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)\n\n- [RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)\n\n- [RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes)\n\n- [RedisGears 2.0 preview](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.20-m21): The RedisGears preview will not be promoted to GA and will be removed in a future release.\n\nBundled Redis modules compatible with Redis database versions 6.0 and 6.2:\n\n- [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)\n\n- [RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)\n\n- [RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)\n\n- [RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)\n\n- [RedisGraph v2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes): RedisGraph end-of-life has been announced and will be removed in a future release. See the [RedisGraph end-of-life announcement](https://redis.io/blog/redisgraph-eol/) for more details."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"product-lifecycle-updates","title":"Product lifecycle updates","role":"content","text":"#### End-of-life policy extension\n\nThe end-of-life policy for Redis Software versions 6.2 and later has been extended to 24 months after the formal release of the subsequent major version. For the updated end-of-life schedule, see the [Redis Software product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle).\n\n#### Supported upgrade paths\n\nRedis Software versions 6.2.4 and 6.2.8 do not support direct upgrades beyond version 7.4.x. Versions 6.2.10, 6.2.12, and 6.2.18 are part of the [upgrade path](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-cluster#supported-upgrade-paths). To upgrade from 6.2.4 or 6.2.8 to versions later than 7.4.x, an intermediate upgrade is required.\n\nThe next major Redis Software release will still bundle Redis database version 6.2 and allow database upgrades from Redis database version 6.2 to 7.x.\n\nSee the [Redis Software product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle) for more information about release numbers."},{"id":"deprecations","title":"Deprecations","role":"content","text":"#### Legacy UI deprecation\n\nThe legacy UI is deprecated in favor of the new Cluster Manager UI and will be removed in a future release.\n\n#### Redis 6.0 database deprecation\n\nRedis database version 6.0 is deprecated as of Redis Software version 7.4.2 and will be removed in a future release.\n\nTo prepare for the future removal of Redis 6.0:\n\n- For Redis Software 6.2.* clusters, upgrade Redis 6.0 databases to Redis 6.2. See the [Redis 6.2 release notes](https://raw.githubusercontent.com/redis/redis/6.2/00-RELEASENOTES) for the list of changes.\n\n- For Redis Software 7.2.4 and 7.4.x clusters, upgrade Redis 6.0 databases to Redis 7.2. Before you upgrade your databases, see the list of [Redis 7.2 breaking changes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52#redis-72-breaking-changes) and update any applications that connect to your database to handle these changes.\n\n#### End of triggers and functions preview\n\nThe [triggers and functions](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions) (RedisGears) preview has been discontinued.\n\n- Commands such as `TFCALL`, `TFCALLASYNC`, and `TFUNCTION` will be deprecated and will return error messages.\n\n- Any JavaScript functions stored in Redis will be removed. \n\n- JavaScript-based triggers will be blocked.\n\n- Lua functions and scripts will not be affected.\n\nIf your database currently uses triggers and functions, you need to: \n\n1. Adjust your applications to accommodate these changes.\n\n1. Delete all triggers and functions libraries from your existing database:\n\n    1. Run `TFUNCTION LIST`.\n\n    1. Copy all library names.\n\n    1. Run `TFUNCTION DELETE` for each library in the list.\n\n    If any triggers and functions libraries remain in the database, the RDB snapshot won't load on a cluster without RedisGears.\n\n1. Migrate your database to a new database without the RedisGears module.\n\n#### RedisGraph end of life\n\nRedisGraph reached end of life on January 31, 2025. See the [RedisGraph end-of-life announcement](https://redis.com/blog/redisgraph-eol/) for more details.\n\n#### Ubuntu 18.04 not supported\n\nUbuntu 18.04 was deprecated in Redis Software version 7.2.4 and is no longer supported as of Redis Software version 7.4.6-293."},{"id":"upcoming-changes","title":"Upcoming changes","role":"content","text":"#### Default image change for Redis Software containers\n\nStarting with the next major version, Redis Software containers with the image tag `x.y.z-build` will be based on RHEL instead of Ubuntu.\n\nThis change will only affect you if you use containers outside the official [Redis Enterprise for Kubernetes](https://redis.io/docs/latest/operate/kubernetes) product and use Ubuntu-specific commands.\n\nTo use Ubuntu-based images after this change, you can specify the operating system suffix in the image tag. For example, use the image tag `7.4.2-216.focal` instead of `7.4.2-216`."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.4.6-293 October release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">eef748e5c126ede9aa120c8809fbc5926067c9d817b3df097c7dffe5eb8c30e0</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">57439084b65d1da0be40a5615943fd6a53a91473ea268efcb7a39b164f037a74</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">61dff1e2da28f2db3fc173a4d41bc6515d32ee76aebc385a96dfebd1482db271</span> |\n| Amazon Linux 2 | <span class=\"break-all\">76a371f8cf3e139c10d9e2848352a2361b1f7cca5ae512cd4807f2c791a21468</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors.\n\n- RS61676: Full chain certificate update fails if any certificate in the chain does not have a Common Name (CN).\n\n- RS119958: The `debuginfo` script fails with the error `/bin/tar: Argument list too long` if there are too many RocksDB log files. This issue only affects clusters with Auto Tiering."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Remove a node.\n\n    Use the REST API or legacy UI instead. See [Remove a cluster node](https://redis.io/docs/latest/operate/rs/clusters/remove-node) for instructions.\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### OpenSSL compatibility issue for 7.4.2 modules on Amazon Linux 2\n\nDue to an OpenSSL 1.1 compatibility issue between modules and clusters, Redis Software version 7.4.2-54 is not fully supported on Amazon Linux 2 clusters with databases that use the following modules: RedisGears, RediSearch, or RedisTimeSeries.\n\nThis issue will be fixed in a future maintenance release.\n\n#### RedisGraph prevents upgrade to RHEL 9 \n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.4.6-293 supports open source Redis 7.2, 6.2, and 6.0. Below is the list of open source Redis CVEs and other security vulnerabilities fixed by version.\n\n\n\n- RedisTimeSeries: Heap buffer overflows and invalid memory access when restoring malformed time-series data.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\n\n\n\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\n\n\n\n\n- RedisTimeSeries: Heap buffer overflows and invalid memory access when restoring malformed time-series data.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)\n\n\n\n\n\n- RedisBloom: Restore invalid filter.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.0.20)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.0.19)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.0.18)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.0.18)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.0.17)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-77","title":"Redis Enterprise Software release notes 7.4.6-77 (September 2024)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-4-2-releases/rs-7-4-6-77/","summary":"Updated module feature sets. Bug fixes.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"ec96b117c241d418e0b1246e90db48ccb4ab6823d957b6e3e85aee4a564cfbb0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Enterprise Software version 7.4.6](https://redis.io/downloads/#software)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Updated module feature sets\n\n- Bug fixes"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":"#### Redis module feature sets\n\nRedis Enterprise comes packaged with several modules. As of version 7.4.2, Redis Enterprise includes two feature sets, compatible with different Redis database versions.\n\nBundled Redis modules compatible with Redis database version 7.2:\n\n- [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)\n\n- [RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)\n\n- [RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)\n\n- [RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes)\n\n- [RedisGears 2.0 preview](https://github.com/RedisGears/RedisGears/releases/tag/v2.0.20-m21): The RedisGears preview will not be promoted to GA and will be removed in a future release.\n\nBundled Redis modules compatible with Redis database versions 6.0 and 6.2:\n\n- [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)\n\n- [RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)\n\n- [RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)\n\n- [RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)\n\n- [RedisGraph v2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes): RedisGraph end-of-life has been announced and will be removed in a future release. See the [RedisGraph end-of-life announcement](https://redis.io/blog/redisgraph-eol/) for more details."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS133928: Updated LDAP URI validation to allow escape characters.\n\n- RS128184: Fixed an issue that sometimes prevented a new node from joining the cluster after removing a node.\n\n- RS132653: Fixed an issue that caused shard crashes when resharding Active-Active databases with Redis version 6.0.\n\n- RS131360: Changed key eviction for Active-Active databases to delete a key instead of expiring it."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"product-lifecycle-updates","title":"Product lifecycle updates","role":"content","text":"#### End-of-life policy extension\n\nThe end-of-life policy for Redis Enterprise Software versions 6.2 and later has been extended to 24 months after the formal release of the subsequent major version. For the updated end-of-life schedule, see the [Redis Enterprise Software product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle).\n\n#### Supported upgrade paths\n\nRedis Enterprise Software versions 6.2.4 and 6.2.8 do not support direct upgrades beyond version 7.4.x. Versions 6.2.10, 6.2.12, and 6.2.18 are part of the [upgrade path](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-cluster#supported-upgrade-paths). To upgrade from 6.2.4 or 6.2.8 to versions later than 7.4.x, an intermediate upgrade is required.\n\nThe next major Redis Enterprise Software release will still bundle Redis database version 6.2 and allow database upgrades from Redis database version 6.2 to 7.x.\n\nSee the [Redis Enterprise Software product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle) for more information about release numbers."},{"id":"deprecations","title":"Deprecations","role":"content","text":"#### Legacy UI deprecation\n\nThe legacy UI is deprecated in favor of the new Cluster Manager UI and will be removed in a future release.\n\n#### Redis 6.0 database deprecation\n\nRedis database version 6.0 is deprecated as of Redis Enterprise Software version 7.4.2 and will be removed in a future release.\n\nTo prepare for the future removal of Redis 6.0:\n\n- For Redis Enterprise 6.2.* clusters, upgrade Redis 6.0 databases to Redis 6.2. See the [Redis 6.2 release notes](https://raw.githubusercontent.com/redis/redis/6.2/00-RELEASENOTES) for the list of changes.\n\n- For Redis Enterprise 7.2.4 and 7.4.x clusters, upgrade Redis 6.0 databases to Redis 7.2. Before you upgrade your databases, see the list of [Redis 7.2 breaking changes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52#redis-72-breaking-changes) and update any applications that connect to your database to handle these changes.\n\n#### End of triggers and functions preview\n\nThe [triggers and functions](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions) (RedisGears) preview has been discontinued.\n\n- Commands such as `TFCALL`, `TFCALLASYNC`, and `TFUNCTION` will be deprecated and will return error messages.\n\n- Any JavaScript functions stored in Redis will be removed. \n\n- JavaScript-based triggers will be blocked.\n\n- Lua functions and scripts will not be affected.\n\nIf your database currently uses triggers and functions, you need to: \n\n1. Adjust your applications to accommodate these changes.\n\n1. Delete all triggers and functions libraries from your existing database:\n\n    1. Run `TFUNCTION LIST`.\n\n    1. Copy all library names.\n\n    1. Run `TFUNCTION DELETE` for each library in the list.\n\n    If any triggers and functions libraries remain in the database, the RDB snapshot won't load on a cluster without RedisGears.\n\n1. Migrate your database to a new database without the RedisGears module.\n\n#### RedisGraph end of life\n\nRedis has announced the end of life for RedisGraph. Redis will continue to support all RedisGraph customers, including releasing patch versions until January 31, 2025.\n\nSee the [RedisGraph end-of-life announcement](https://redis.com/blog/redisgraph-eol/) for more details."},{"id":"upcoming-changes","title":"Upcoming changes","role":"content","text":"#### Default image change for Redis Enterprise Software containers\n\nStarting with the next major version, Redis Enterprise Software containers with the image tag `x.y.z-build` will be based on RHEL instead of Ubuntu.\n\nThis change will only affect you if you use containers outside the official [Redis Enterprise for Kubernetes](https://redis.io/docs/latest/operate/kubernetes) product and use Ubuntu-specific commands.\n\nTo use Ubuntu-based images after this change, you can specify the operating system suffix in the image tag. For example, use the image tag `7.4.2-216.focal` instead of `7.4.2-216`."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Enterprise Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Enterprise Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Enterprise Software, but support will be removed in a future release.\n\n| Redis Enterprise<br />major versions | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\" style=\"display: block; height: 80px; margin-top: -80px;\"></a>\n[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Enterprise Software are certified for development and testing only."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.4.6-77 September release) |\n|---------|---------------------------------------|\n| Ubuntu 18 | <span class=\"break-all\">2a1c5b9a1efcf2be79e8485171b50b948b7ff10da36ca7bd95aeb572770e6862</span> |\n| Ubuntu 20 | <span class=\"break-all\">816f13736ef774d77745279817aa792d64d6375f3b0c8313717a46f339e733d2</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">c22979928937ae08f65e83cc459cde98340ffae1eb0f414b8ed111a047feb5f1</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">e794d0a973b150449e532cd21280f920d8eba1eb47fba39157f5b10bb3842cd1</span> |\n| Amazon Linux 2 | <span class=\"break-all\">d5f55646a0356e3844c42eabda855f220a9f38ccfd839397709a41864e1a4470</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors.\n\n- RS61676: Full chain certificate update fails if any certificate in the chain does not have a Common Name (CN).\n\n- RS119958: The `debuginfo` script fails with the error `/bin/tar: Argument list too long` if there are too many RocksDB log files. This issue only affects clusters with Auto Tiering."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Remove a node.\n\n    Use the REST API or legacy UI instead. See [Remove a cluster node](https://redis.io/docs/latest/operate/rs/clusters/remove-node) for instructions.\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### OpenSSL compatibility issue for 7.4.2 modules on Amazon Linux 2\n\nDue to an OpenSSL 1.1 compatibility issue between modules and clusters, Redis Enterprise Software version 7.4.2-54 is not fully supported on Amazon Linux 2 clusters with databases that use the following modules: RedisGears, RediSearch, or RedisTimeSeries.\n\nThis issue will be fixed in a future maintenance release.\n\n#### RedisGraph prevents upgrade to RHEL 9 \n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Enterprise cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Enterprise Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Enterprise has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Enterprise due to different or additional functionality available in Redis Enterprise that is not available in open source Redis.\n\nRedis Enterprise 7.4.6-77 supports open source Redis 7.2, 6.2, and 6.0. Below is the list of open source Redis CVEs fixed by version.\n\nRedis 7.2.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)\n\nRedis 6.0.x:\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.0.20)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.0.19)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.0.18)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.0.18)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.0.17)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-8-releases","title":"Redis Software release notes 7.8.x","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/","summary":"Redis Community Edition 7.4 features. Hash field expiration. Client-side caching support. Metrics stream engine preview. New APIs to check database availability, rebalance shards, fail over shards, and control database traffic. Cluster Manager UI enhancements for node actions, database tags, and database configuration. User manager role. Log rotation based on both size and time. Module management enhancements. Configurable minimum password length. Configurable license expiration alert threshold.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","children":[{"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-331","summary":"Bug fixes for DMC proxy stability. Internal fixes and improvements.","title":"Redis Software release notes 7.8.6-331 (October 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-331/"},{"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-315","summary":"Bug fix for a memory leak in the Active-Active database syncer. Added scoping for shards and database endpoints. Internal fixes and improvements.","title":"Redis Software release notes 7.8.6-315 (September 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-315/"},{"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-303","summary":"Internal fixes and improvements.","title":"Redis Software release notes 7.8.6-303 (August 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-303/"},{"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-286","summary":"Internal fixes and improvements.","title":"Redis Software release notes 7.8.6-286 (July 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-286/"},{"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-263","summary":"Internal fixes and improvements.","title":"Redis Software release notes 7.8.6-263 (May 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-263/"},{"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-260","summary":"Internal fixes and improvements.","title":"Redis Software release notes 7.8.6-260 (April 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-260/"},{"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-256","summary":"Internal fixes and improvements.","title":"Redis Software release notes 7.8.6-256 (March 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-256/"},{"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-253","summary":"Bug fixes for database recovery and modifying databases in the Cluster Manager UI. Internal fixes and improvements.","title":"Redis Software release notes 7.8.6-253 (February 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-253/"},{"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-207","summary":"Enhanced timeout diagnostics and logging when restarting shards. Bug fix for viewing shard memory usage metrics in the Cluster Manager UI. Internal fixes and improvements.","title":"Redis Software release notes 7.8.6-207 (September 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-207/"},{"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-152","summary":"Bug fixes for Active-Active syncing, event log entries for configuration changes made by LDAP users, LDAP mapping visibility in the Cluster Manager UI, database max_connections setting, and memory leaks when using Active-Active databases in Kubernetes.","title":"Redis Software release notes 7.8.6-152 (August 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-152/"},{"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-119","summary":"Bug fixes for user password management REST API requests, maintenance mode snapshot cleanup, and rlec_supervisor file permissions.","title":"Redis Software release notes 7.8.6-119 (June 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-119/"},{"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-95","summary":"Performance improvements for the Cluster Manager UI's nodes screen. Bug fix for slowness and intermittent failures in the Cluster Manager UI, `rladmin`, and `rlcheck` after upgrading large clusters to version 7.8.4. Bug fix for an issue that could cause inconsistent memory settings across shards and out-of-memory errors. Bug fix for an issue where certain processes might not terminate as expected during cluster upgrades due to timeouts.","title":"Redis Software release notes 7.8.6-95 (May 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-95/"},{"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-60","summary":"Bug fixes for an LDAP connection issue caused by setting LDAP bind credentials that include `<` in the Cluster Manager UI and an issue where migrated shards can appear stuck on multiple nodes. Deprecated required_version in the bootstrap cluster API.","title":"Redis Software release notes 7.8.6-60 (April 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-60/"},{"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-36","summary":"Bug fixes, including a fix for an issue where new workers might use an outdated certificate after a certificate update.","title":"Redis Software release notes 7.8.6-36 (April 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-36/"},{"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-13","summary":"Configurable hashing algorithm for user passwords. Dry run option to validate REST API requests for users and roles.","title":"Redis Software release notes 7.8.6-13 (March 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-13/"},{"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-4-95","summary":"Bug fixes for server restart failures during upgrades, connectivity checks for optional services, swapped shard status and watchdog status values in `rladmin`, process exporter crashes, and a syncer issue for Active-Active databases that have the OSS Cluster API enabled.","title":"Redis Software release notes 7.8.4-95 (March 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-4-95/"},{"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-4-66","summary":"Ubuntu 22 support. Bug fixes for crashes, unexpected restarts, and incorrect type for the automatically calculated replica buffer.","title":"Redis Software release notes 7.8.4-66 (February 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-4-66/"},{"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-4-18","summary":"Certificate-based authentication for the REST API. Dry run option to validate Redis ACL REST API requests.","title":"Redis Software release notes 7.8.4-18 (December 2024)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-4-18/"},{"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-2-60","summary":"Bug fixes, including a fix for a RHEL 8 OpenSSL dependency issue that caused frequent restarts of the `cm_server` and prevented access to the Redis Software Cluster Manager UI after upgrading to Redis Software version 7.8.2-34.","title":"Redis Software release notes 7.8.2-60 (December 2024)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-2-60/"},{"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-2-34","summary":"Redis Community Edition 7.4 features. Hash field expiration. Client-side caching support. Metrics stream engine preview. New APIs to check database availability, rebalance shards, fail over shards, and control database traffic. Cluster Manager UI enhancements for node actions, database tags, and database configuration. User manager role. Log rotation based on both size and time. Module management enhancements. Configurable minimum password length. Configurable license expiration alert threshold.","title":"Redis Software release notes 7.8.2-34 (November 2024)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-2-34/"}],"page_type":"content","content_hash":"e1feabb5927b6e26bd2ae6afb8bfa487d2521a9fa0a7b167f1fe3cd819f9550f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"​[​Redis Software version 7.8](https://redis.io/downloads/#software) is now available!"},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Redis Community Edition 7.4 features\n\n- Hash field expiration\n\n- Client-side caching support\n\n- Metrics stream engine preview\n\n- New APIs to check database availability, rebalance shards, fail over shards, and control database traffic\n\n- Cluster Manager UI enhancements for node actions, database tags, and database configuration\n\n- User manager role\n\n- Log rotation based on both size and time\n\n- Module management enhancements\n\n- Configurable minimum password length\n\n- Configurable license expiration alert threshold"},{"id":"detailed-release-notes","title":"Detailed release notes","role":"content","text":"For more detailed release notes, select a build version from the following table:\n\n\n| Version (Release date) | Major changes | Redis Open Source compatibility |\n| --- | --- | --- |\n| [7.8.6-331 (October 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-331/) | Bug fixes for DMC proxy stability. Internal fixes and improvements. | Redis 7.4.0, 7.2.4, 6.2.13 |\n| [7.8.6-315 (September 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-315/) | Bug fix for a memory leak in the Active-Active database syncer. Added scoping for shards and database endpoints. Internal fixes and improvements. | Redis 7.4.0, 7.2.4, 6.2.13 |\n| [7.8.6-303 (August 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-303/) | Internal fixes and improvements. | Redis 7.4.0, 7.2.4, 6.2.13 |\n| [7.8.6-286 (July 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-286/) | Internal fixes and improvements. | Redis 7.4.0, 7.2.4, 6.2.13 |\n| [7.8.6-263 (May 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-263/) | Internal fixes and improvements. | Redis 7.4.0, 7.2.4, 6.2.13 |\n| [7.8.6-260 (April 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-260/) | Internal fixes and improvements. | Redis 7.4.0, 7.2.4, 6.2.13 |\n| [7.8.6-256 (March 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-256/) | Internal fixes and improvements. | Redis 7.4.0, 7.2.4, 6.2.13 |\n| [7.8.6-253 (February 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-253/) | Bug fixes for database recovery and modifying databases in the Cluster Manager UI. Internal fixes and improvements. | Redis 7.4.0, 7.2.4, 6.2.13 |\n| [7.8.6-207 (September 2025)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-207/) | Enhanced timeout diagnostics and logging when restarting shards. Bug fix for viewing shard memory usage metrics in the Cluster Manager UI. Internal fixes and improvements. | Redis 7.4.0, 7.2.4, 6.2.13 |\n| [7.8.6-152 (August 2025)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-152/) | Bug fixes for Active-Active syncing, event log entries for configuration changes made by LDAP users, LDAP mapping visibility in the Cluster Manager UI, database max_connections setting, and memory leaks when using Active-Active databases in Kubernetes. | Redis 7.4.0, 7.2.4, 6.2.13 |\n| [7.8.6-119 (June 2025)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-119/) | Bug fixes for user password management REST API requests, maintenance mode snapshot cleanup, and rlec_supervisor file permissions. | Redis 7.4.0, 7.2.4, 6.2.13 |\n| [7.8.6-95 (May 2025)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-95/) | Performance improvements for the Cluster Manager UI's nodes screen. Bug fix for slowness and intermittent failures in the Cluster Manager UI, `rladmin`, and `rlcheck` after upgrading large clusters to version 7.8.4. Bug fix for an issue that could cause inconsistent memory settings across shards and out-of-memory errors. Bug fix for an issue where certain processes might not terminate as expected during cluster upgrades due to timeouts. | Redis 7.4.0, 7.2.4, 6.2.13 |\n| [7.8.6-60 (April 2025)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-60/) | Bug fixes for an LDAP connection issue caused by setting LDAP bind credentials that include `<` in the Cluster Manager UI and an issue where migrated shards can appear stuck on multiple nodes. Deprecated required_version in the bootstrap cluster API. | Redis 7.4.0, 7.2.4, 6.2.13 |\n| [7.8.6-36 (April 2025)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-36/) | Bug fixes, including a fix for an issue where new workers might use an outdated certificate after a certificate update. | Redis 7.4.0, 7.2.4, 6.2.13 |\n| [7.8.6-13 (March 2025)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-13/) | Configurable hashing algorithm for user passwords. Dry run option to validate REST API requests for users and roles. | Redis 7.4.0, 7.2.4, 6.2.13 |\n| [7.8.4-95 (March 2025)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-4-95/) | Bug fixes for server restart failures during upgrades, connectivity checks for optional services, swapped shard status and watchdog status values in `rladmin`, process exporter crashes, and a syncer issue for Active-Active databases that have the OSS Cluster API enabled. | Redis 7.4.0, 7.2.4, 6.2.13 |\n| [7.8.4-66 (February 2025)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-4-66/) | Ubuntu 22 support. Bug fixes for crashes, unexpected restarts, and incorrect type for the automatically calculated replica buffer. | Redis 7.4.0, 7.2.4, 6.2.13 |\n| [7.8.4-18 (December 2024)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-4-18/) | Certificate-based authentication for the REST API. Dry run option to validate Redis ACL REST API requests. | Redis 7.4.0, 7.2.4, 6.2.13 |\n| [7.8.2-60 (December 2024)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-2-60/) | Bug fixes, including a fix for a RHEL 8 OpenSSL dependency issue that caused frequent restarts of the `cm_server` and prevented access to the Redis Software Cluster Manager UI after upgrading to Redis Software version 7.8.2-34. | Redis 7.4.0, 7.2.4, 6.2.13 |\n| [7.8.2-34 (November 2024)](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-2-34/) | Redis Community Edition 7.4 features. Hash field expiration. Client-side caching support. Metrics stream engine preview. New APIs to check database availability, rebalance shards, fail over shards, and control database traffic. Cluster Manager UI enhancements for node actions, database tags, and database configuration. User manager role. Log rotation based on both size and time. Module management enhancements. Configurable minimum password length. Configurable license expiration alert threshold. | Redis 7.4.0, 7.2.4, 6.2.13 |"},{"id":"version-changes","title":"Version changes","role":"compatibility","text":"- Added validation to verify the LDAP server URI contains a host and port when updating LDAP configuration.\n\n- The value of the `oss_sharding` API field had no effect in previous versions of Redis Software. However, `oss_sharding` is now set to take effect as part of future plans. Until further notice, set this field to `false` to avoid unintended impacts."},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"Redis Software version 7.8.2 introduces the following breaking changes:\n\n- When you upgrade a database, the upgrade process also attempts to upgrade database modules by default.\n\n    - The default value of `latest_with_modules` has changed to `true`.\n\n    - [`rladmin upgrade db`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/upgrade#upgrade-db) will always upgrade the database's modules.\n\n    - When you [upgrade a database](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/upgrade#post-bdbs-upgrade) using the REST API, you can set `\"latest_with_modules\": false` in the request body to prevent module upgrades.\n\n- Authentication method changes for [`/v1/users/password`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/users/password) REST API requests.\n\n    - `PUT`, `POST`, and `DELETE` methods require users to include their usernames and a current password in the authentication header to change their password lists. If the authentication header is not provided, the response status will be `401 Unauthorized`.\n\n    - `/v1/users/password` requests change the password list of the user who provides their credentials in the authorization header when sending the requests.\n\n    - `PUT` and `POST` requests will ignore `username` and `old_password` parameters provided in the request body.\n\n    - `DELETE` requests will ignore the `username` parameter provided in the request body.\n\n- Authentication method changes for [`POST /v1/users/authorize`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/users/authorize) REST API requests.\n\n    - The `POST` method requires users to include their usernames and a current password in the authentication header to generate a JSON Web Token.\n\n    - `POST /v1/users/authorize` generates a token for the user who provides their credentials in the authorization header when sending the requests.\n\n    - `POST` requests will ignore `username` and `password` parameters provided in the request body.\n\n#### Redis database version 7.4 breaking changes {#redis-74-breaking-changes}\n\nWhen new major versions of Redis Community Edition change existing commands, upgrading your database to a new version can potentially break some functionality. Before you upgrade, read the provided list of breaking changes that affect Redis Software and update any applications that connect to your database to handle these changes.\n\nConfirm your Redis database version (`redis_version`) using the Cluster Manager UI or run the following [`INFO`](https://redis.io/docs/latest/commands/info) command with [`redis-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/redis-cli):\n\n[code example]\n\n##### Security behavior changes\n\n- [#13108](https://github.com/redis/redis/pull/13108) Lua: LRU eviction for scripts generated with `EVAL`. \n\n##### Other general behavior changes\n\n- [#13133](https://github.com/redis/redis/pull/13133) Lua: allocate VM code with jemalloc instead of libc and count it as used memory.\n\n- [#12171](https://github.com/redis/redis/pull/12171) `ACL LOAD`: do not disconnect all clients."},{"id":"product-lifecycle-updates","title":"Product lifecycle updates","role":"content","text":"#### End-of-life policy extension\n\nThe end-of-life policy for Redis Software versions 6.2 and later has been extended to 24 months after the formal release of the subsequent major version. For the updated end-of-life schedule, see the [Redis Software product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle).\n\n#### Supported upgrade paths\n\nRedis Software versions 6.2.4 and 6.2.8 do not support direct upgrades beyond version 7.4.x. Versions 6.2.10, 6.2.12, and 6.2.18 are part of the [upgrade path](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-cluster#supported-upgrade-paths). To upgrade from 6.2.4 or 6.2.8 to versions later than 7.4.x, an intermediate upgrade is required.\n\nThe next major Redis Software release will still bundle Redis database version 6.2 and allow database upgrades from Redis database version 6.2 to 7.x.\n\nSee the [Redis Software product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle) for more information about release numbers.\n\n#### End of triggers and functions preview\n\nThe [triggers and functions](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions) (RedisGears) preview has been discontinued.\n\n- Commands such as `TFCALL`, `TFCALLASYNC`, and `TFUNCTION` will be deprecated and will return error messages.\n\n- Any JavaScript functions stored in Redis will be removed. \n\n- JavaScript-based triggers will be blocked.\n\n- Lua functions and scripts will not be affected.\n\nIf your database currently uses triggers and functions, you need to: \n\n1. Adjust your applications to accommodate these changes.\n\n1. Delete all triggers and functions libraries from your existing database:\n\n    1. Run `TFUNCTION LIST`.\n\n    1. Copy all library names.\n\n    1. Run `TFUNCTION DELETE` for each library in the list.\n\n    If any triggers and functions libraries remain in the database, the RDB snapshot won't load on a cluster without RedisGears.\n\n1. Migrate your database to a new database without the RedisGears module."},{"id":"reserved-ports","title":"Reserved ports","role":"content","text":"Make sure the following ports are open before upgrading Redis Software.\n\nPorts reserved as of Redis Software version 7.8.2:\n\n| Port | Process name | Usage | \n|------|--------------|-------|\n| 3347 | cert_exporter | Reports cluster certificate metrics |\n| 3348 | process_exporter | Reports process metrics for DMC and Redis processes |\n| 3349 | cluster_wd_exporter | Reports cluster watchdog metrics |\n| 3350 | db_controller | Internode communication |\n| 9091 | node_exporter | Reports host node metrics related to CPU, memory, disk, and more |\n| 9125 | statsd_exporter | Reports push metrics related to the DMC and syncer, and some cluster and node metrics |\n\nSee [Ports and port ranges used by Redis Software](https://redis.io/docs/latest/operate/rs/networking/port-configurations#ports-and-port-ranges-used-by-redis-enterprise-software) for a complete list."},{"id":"deprecations","title":"Deprecations","role":"content","text":"#### API deprecations\n\n- Deprecated `background_op` field from BDB REST API object. Use [`GET /v1/actions/bdb/<bdb_uid>`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/actions/bdb) instead.\n\n- Deprecated the following fields for [upgrade database](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/upgrade) REST API requests:\n\n    - `keep_redis_version`; use `redis_version` instead\n\n    - `current_module`; use `new_module_args` instead\n\n    - `new_module`; use `new_module_args` instead\n\n- Deprecated the following `module_list` fields for [create database](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#post-bdbs-v1) REST API requests:\n\n    - `module_id`; use `module_name` instead\n\n    - `semantic_version`; use module_args instead\n\n- `min_redis_version` is only relevant to Redis database versions earlier than 7.4 and is replaced with `compatible_redis_version` in [module REST API](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/modules) responses.\n\n- Deprecated the [`rladmin upgrade modules`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/upgrade#upgrade-module) command. Use [`rladmin upgrade db`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/upgrade#upgrade-db) instead.\n\n- Deprecated `POST /v1/modules/upgrade/bdb/<uid>` REST API request. Use [`POST /v1/bdbs/<uid>/upgrade`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/upgrade#post-bdbs-upgrade) to upgrade modules instead.\n\n- Deprecated the `required_version` option for the bootstrap cluster API.\n\n#### V1 Prometheus metrics deprecation\n\n V1 Prometheus metrics are deprecated but still available. To transition to the new metrics stream engine, either migrate your existing dashboards using [Prometheus v1 metrics and equivalent v2 PromQL](https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise/prometheus-metrics-v1-to-v2) now, or wait to use new preconfigured dashboards when they become available in a future release.\n\n#### Download center modules deprecation\n\nNew Redis modules will not be available for download from the Redis download center.\n\n#### Legacy UI not supported\n\nThe legacy UI was deprecated in favor of the new Cluster Manager UI in Redis Software version 7.2.4 and is no longer supported as of Redis Software version 7.8.2.\n\n#### Redis 6.0 databases not supported\n\nRedis database version 6.0 was deprecated in Redis Software version 7.4.2 and is no longer supported as of Redis Software version 7.8.2.\n\nTo prepare for the removal of Redis database version 6.0 before you upgrade to Redis Software version 7.8.2:\n\n- For Redis Software 6.2.* clusters:\n\n    1. Set the Redis upgrade policy to `latest`:\n\n        [code example]\n\n    1. [Upgrade Redis 6.0 databases](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-database) to Redis 6.2. See the [Redis 6.2 release notes](https://raw.githubusercontent.com/redis/redis/6.2/00-RELEASENOTES) for the list of changes.\n\n- For Redis Software 7.2.4 and 7.4.2 clusters, upgrade Redis 6.0 databases to Redis 7.2. Before you upgrade your databases, see the list of [Redis 7.2 breaking changes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52#redis-72-breaking-changes) and update any applications that connect to your database to handle these changes.\n\n#### Ubuntu 18.04 not supported\n\nUbuntu 18.04 was deprecated in Redis Software version 7.2.4 and is no longer supported as of Redis Software version 7.8.2."},{"id":"upcoming-changes","title":"Upcoming changes","role":"content","text":"#### Default image change for Redis Software containers\n\nStarting with version 7.8, Redis Software containers with the image tag `x.y.z-build` will be based on RHEL instead of Ubuntu.\n\nThis change will only affect you if you use containers outside the official [Redis Enterprise for Kubernetes](https://redis.io/docs/latest/operate/kubernetes) product and use Ubuntu-specific commands.\n\nTo use Ubuntu-based images after this change, you can specify the operating system suffix in the image tag. For example, use the image tag `7.4.2-216.focal` instead of `7.4.2-216`."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors.\n\n- RS151990: For RHEL 8 and RHEL 9 clusters, importing an RDB file can fail with the exception: `Failed to import rdb file: general-error`.\n\n    As a workaround:\n\n    1. On each cluster node, run:\n\n        [code example]\n\n    1. Verify that `libboost_program_options.so.1.75.0` points to a path to confirm the installation succeeded:\n\n        [code example]\n\n    This issue was fixed in [Redis Software version 7.8.6-36](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-36)."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Upload modules before OS upgrade\n\nIf the cluster contains any databases that use modules, you must upload module packages for the target OS version to a node in the existing cluster before you upgrade the cluster's operating system.\n\nSee [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os) for detailed upgrade instructions.\n\n#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### RedisGraph prevents upgrade to RHEL 9 \n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached end-of-life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Query results might include hash keys with lazily expired fields\n\nIf one or more fields of a hash key expire after an `FT.SEARCH` or `FT.AGGREGATE` query begins, Redis does not account for these lazily expired fields. As a result, keys with expired fields might still be included in the query results, leading to potentially incorrect or inconsistent results."}],"examples":[{"id":"breaking-changes-ex0","language":"sh","code":"$ redis-cli -p <port> INFO\n\"# Server\nredis_version:7.0.8\n...\"","section_id":"breaking-changes"},{"id":"deprecations-ex0","language":"sh","code":"rladmin tune cluster redis_upgrade_policy latest","section_id":"deprecations"},{"id":"known-issues-ex0","language":"sh","code":"sudo dnf install boost-program-options","section_id":"known-issues"},{"id":"known-issues-ex1","language":"sh","code":"ldd /opt/redislabs/bin/rl_rdbloader","section_id":"known-issues"}]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-2-34","title":"Redis Software release notes 7.8.2-34 (November 2024)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-2-34/","summary":"Redis Community Edition 7.4 features. Hash field expiration. Client-side caching support. Metrics stream engine preview. New APIs to check database availability, rebalance shards, fail over shards, and control database traffic. Cluster Manager UI enhancements for node actions, database tags, and database configuration. User manager role. Log rotation based on both size and time. Module management enhancements. Configurable minimum password length. Configurable license expiration alert threshold.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"dccf9ebc5d03755e8c1c530d61bd0f613af0bb7be8750d961e1b593bb03087ea","sections":[{"id":"overview","title":"Overview","role":"overview","text":"​[​Redis Software version 7.8.2](https://redis.io/downloads/#software) is now available!"},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Redis Community Edition 7.4 features\n\n- Hash field expiration\n\n- Client-side caching support\n\n- Metrics stream engine preview\n\n- New APIs to check database availability, rebalance shards, fail over shards, and control database traffic\n\n- Cluster Manager UI enhancements for node actions, database tags, and database configuration\n\n- User manager role\n\n- Log rotation based on both size and time\n\n- Module management enhancements\n\n- Configurable minimum password length\n\n- Configurable license expiration alert threshold"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"new-features","title":"New features","role":"content","text":"- Redis Community Edition and Redis Stack 7.4 features are now available when you [create](https://redis.io/docs/latest/operate/rs/databases/create) or [upgrade](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-database) a database with database version 7.4, including:\n\n    - [Hash field expiration](https://redis.io/docs/latest/develop/data-types/hashes#field-expiration)\n    \n    - New vector data types to reduce ‌memory usage\n    \n    - Time series insertion filters\n\n    - See the [Redis 7.4 release blog post](https://redis.io/blog/announcing-redis-community-edition-and-redis-stack-74/) and [Redis Community Edition 7.4 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisce-7.4-release-notes) for details.\n\n- Client-side caching support:\n\n    - Client-side caching allows Redis clients to store a subset of data in a local cache and avoid sending repeated read requests to the Redis database.\n    \n    - When used to cache frequently accessed data, this technique can improve performance by decreasing network traffic, latency, and load on the database.\n\n    - Supported for Redis databases with Redis versions 7.4 and later.\n    \n    - For more information, see the [client-side caching introduction](https://redis.io/docs/latest/develop/clients/client-side-caching) and [client-side caching compatibility with Redis Software](https://redis.io/docs/latest/operate/rs/references/compatibility/client-side-caching).\n\n- Database availability API:\n\n    - Verifies whether a Redis Software database is available to perform read and write operations and can respond to queries from client applications. \n\n    - Load balancers and automated monitoring tools can use this API to monitor database availability.\n\n    - See [Check database availability](https://redis.io/docs/latest/operate/rs/monitoring/db-availability) and the [REST API reference](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/availability) for details.\n\n- Metrics stream engine preview:\n\n    - The new metrics stream engine's exporter-based infrastructure provides access to more accurate, real-time data. This enhanced, scalable monitoring system allows you to set up more effective alerts and respond to issues faster.\n\n    - Exposes a new `/v2` Prometheus scraping endpoint that you can use to export metrics to external monitoring tools such as Grafana, DataDog, NewRelic, and Dynatrace.\n\n    - Exports raw data instead of aggregated data to improve monitoring at scale and accuracy compared to v1 Prometheus metrics.\n\n    - For an initial list of metrics exported by the new metrics stream engine, see [Prometheus metrics v2](https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise/prometheus-metrics-definitions). While the metrics stream engine is in preview, this document provides only a partial list. More metrics will be added.\n\n    - V1 Prometheus metrics are deprecated but still available. To transition to the new metrics stream engine, either migrate your existing dashboards using [Prometheus v1 metrics and equivalent v2 PromQL](https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise/prometheus-metrics-v1-to-v2) now, or wait to use new preconfigured dashboards when they become available in a future release.\n\n    - The new metrics stream engine resolved some known issues and bugs:\n\n        - RS31923: Added missing types to Prometheus metrics.\n\n        - RS80626: Added DMC thread utilization metrics to the metrics exporter.\n\n        - RS113028: Upgraded the node exporter dependency to version 1.7.0.\n\n        - RS103537: Fixed an issue with missing indications when a node went down. You can see the node's metrics up until it went down and can monitor `node_metrics_up` to get an indication that it is down.\n\n        - RS103251: Metrics are now available during state machine operations.\n\n        - RS74732: Fixed an issue where a replica CRDB's stats were missing after removing and then re-adding the replica CRDB instance.\n\n        - RS65948: Fixed an issue where the metrics exporter generated duplicate stats headers when the cluster had both regular databases and Active-Active databases.\n\n- [Rebalance shard placement REST API request](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/rebalance), which distributes the database's shards across nodes based on the database's shard placement policy. See [Shard placement policy](https://redis.io/docs/latest/operate/rs/databases/memory-performance/shard-placement-policy) for more information about shard placement and available policies.\n\n- [Shard failover REST API requests](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/shards/actions/failover), which perform failover on specified primary shards and promotes their replicas to primary shards.\n\n- REST API requests to [stop traffic](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/stop_traffic) or [resume traffic](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/resume_traffic) to a database."},{"id":"enhancements","title":"Enhancements","role":"content","text":"- New Cluster Manager UI enhancements:\n\n    - Perform node actions from the **Nodes** screen to [verify nodes](https://redis.io/docs/latest/operate/rs/clusters/add-node#verify-node), [set a node as primary or secondary](https://redis.io/docs/latest/operate/rs/clusters/change-node-role), [remove nodes](https://redis.io/docs/latest/operate/rs/clusters/remove-node), and manage node alert settings.\n\n    - Categorize databases with [custom tags](https://redis.io/docs/latest/operate/rs/databases/configure/db-tags). When you add new tags to a database, the keys and values already used by existing tags will appear as suggestions.\n\n    - Moved several settings on the database configuration screen:\n    \n        - The eviction setting now appears in the [**Capacity**](https://redis.io/docs/latest/operate/rs/databases/configure#capacity) section.\n\n        - [**High availability**](https://redis.io/docs/latest/operate/rs/databases/configure#high-availability) and [**Durability**](https://redis.io/docs/latest/operate/rs/databases/configure#durability) have separate sections.\n\n    - Improved error messages on the sign-in screen for locked out users versus incorrect or expired passwords.\n\n    - Flush an Active-Active database.\n\n- A new **User Manager** role designed for user administration is available for [role-based access control](https://redis.io/docs/latest/operate/rs/security/access-control).\n\n    - This management role allows assigned users to create, edit, and delete users using the Cluster Manager UI and REST API.\n\n    - For more details about the privileges granted by the **User Manager** role, see [Cluster Manager UI permissions](https://redis.io/docs/latest/operate/rs/security/access-control/create-cluster-roles#cluster-manager-ui-permissions) and [REST API permissions](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions#user-manager-role).\n\n- When you upgrade a database, the upgrade process also attempts to upgrade database modules by default.\n\n    - [`rladmin upgrade db`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/upgrade#upgrade-db) will always upgrade the database's modules.\n\n    - If you [upgrade a database](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/upgrade#post-bdbs-upgrade) using the REST API, you can set `\"latest_with_modules\": false` in the request body to prevent module upgrades.\n\n- Added support for [log rotation](https://redis.io/docs/latest/operate/rs/clusters/logging/log-security#log-rotation) based on both size and time.\n\n- [Minimum password length](https://redis.io/docs/latest/operate/rs/security/access-control/manage-passwords/password-complexity-rules#change-minimum-password-length), previously hardcoded as 8 characters, is now configurable in the Cluster Manager UI and the REST API.\n\n- The [cluster license expiration alert threshold](https://redis.io/docs/latest/operate/rs/clusters/configure/license-keys#configure-license-expiration-alert), which determines how far in advance you want to be notified of the license expiration, is configurable in the Cluster Manager UI and the REST API.\n\n- The Cluster Manager UI's time zone can be configured with an [update CM settings REST API request](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cm_settings#put-cm-settings).\n\n- Timeouts for raising connection alarms can be configured with an [update database configuration REST API request](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#put-bdbs):\n\n    - `crdt_sync_connection_alarm_timeout_seconds`: if the syncer takes longer than the specified number of seconds to connect to an Active-Active database, raise a connection alarm.\n\n    - `replica_sync_connection_alarm_timeout_seconds`: if the syncer takes longer than the specified number of seconds to connect to a replica, raise a connection alarm.\n\n- Reserved the following ports:\n\n    | Port | Process name | Usage | \n    |------|--------------|-------|\n    | 3347 | cert_exporter | Reports cluster certificate metrics |\n    | 3348 | process_exporter | Reports process metrics for DMC and Redis processes |\n    | 3349 | cluster_wd_exporter | Reports cluster watchdog metrics |\n    | 3350 | db_controller | Internode communication |\n    | 9091 | node_exporter | Reports host node metrics related to CPU, memory, disk, and more |\n    | 9125 | statsd_exporter | Reports push metrics related to the DMC and syncer, and some cluster and node metrics |"},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 7.8.2 includes three Redis database versions: 7.4.0, 7.2.4, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 7.4."},{"id":"redis-module-feature-sets","title":"Redis module feature sets","role":"content","text":"Redis Software comes packaged with several modules. As of version 7.8.2, Redis Software includes three feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)<br />[RedisGraph v2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes)<sup>[1](#module-note-1)</sup>  |\n\n1. <a name=\"module-note-1\"></a>RedisGraph end-of-life has been announced and will be removed in a future release. See the [RedisGraph end-of-life announcement](https://redis.io/blog/redisgraph-eol/) for more details."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS123645: Fixed inconsistent behavior for shard creation when you enable sharding. Now, when creating a database with sharding enabled, you must always provide a `shard_key_regex`.\n\n- RS130444: Fixed an issue that prevented creating or editing users without an email address in the Cluster Manager UI.\n\n- RS121796: The \"multiple endpoint\" configuration database default setting should also enable sharding when selected.\n\n- RS128768: Fixed an issue that prevented metric graph timestamp details from displaying in the Cluster Manager UI.\n\n- RS127120: Fixed an issue where `crdt_replicas` were not properly updated when flushing an Active-Active database.\n\n- RS127054: Fixed an issue where the install script incorrectly reported \"Port 53 is occupied\" instead of the correct value of the occupied port.\n\n- RS125934: Fixed validation that prevented updating the Active-Active database configuration if existing TLS certificates expired.\n\n- RS125412: Fixed an issue where deleted external IP addresses were still listed as available during node configuration when joining a cluster.\n\n- RS122012: Fixed an issue that sent a cutoff email message for long-running alerts.\n\n- RS121726: Block the `remove_shards` option for `rlutil`. You can use Replica Of to decrease the shard count instead.\n\n- RS121076: Added a 5-minute connection timeout when promoting a replica shard to a primary role.\n\n- RS118103: Removed the inaccurate shard `BACKUP_PROGRESS` column from the `rladmin status` command's output.\n\n- RS116990: Fixed an issue with flash-enabled databases where `FLUSHDB` could cause shard crashes in versions 7.0 and 7.2, and cause clients to stop responding in earlier versions.\n\n- RS114923: Fixed an issue where the legacy RDB parser could generate an `XGROUP CREATE` command with an incorrect `ENTRIESREAD` value when syncing a source RDB to a database with Redis version 7.0 or later.\n\n- RS114258: Fixed an issue where an Active-Active database instance could lose keys during partial syncing if the destination shard was stale.\n\n- RS133653: Fixed a validation issue where expired client certificates prevented updates to database configuration unrelated to TLS.\n\n- RS126235: Fixed an issue where database updates could time out and enter a change pending state due to outdated shard data.\n\n- RS125128: Improved real-time logging for installation and upgrade for better visibility.\n\n- RS119958: Removed the log file limit that caused the debuginfo script to fail with the error \"/bin/tar: Argument list too long\" in Auto Tiering clusters and improved RocksDB log file rotation.\n\n- RS137396: Providing an email address is no longer mandatory when creating new ACL users in the Cluster Manager UI.\n\n- RS134238: Improved database sorting performance in the Cluster Manager UI.\n\n- RS129418: Improved log rotation mechanism for Auto Tiering clusters to reduce excessive logs.\n\n- RS137231: Fixed an issue where database shards could become stuck during migration due to outdated node data."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":"- Added validation to verify the LDAP server URI contains a host and port when updating LDAP configuration.\n\n- The value of the `oss_sharding` API field had no effect in previous versions of Redis Software. However, `oss_sharding` is now set to take effect as part of future plans. Until further notice, set this field to `false` to avoid unintended impacts."},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"Redis Software version 7.8.2 introduces the following breaking changes:\n\n- When you upgrade a database, the upgrade process also attempts to upgrade database modules by default.\n\n    - The default value of `latest_with_modules` has changed to `true`.\n\n    - [`rladmin upgrade db`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/upgrade#upgrade-db) will always upgrade the database's modules.\n\n    - When you [upgrade a database](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/upgrade#post-bdbs-upgrade) using the REST API, you can set `\"latest_with_modules\": false` in the request body to prevent module upgrades.\n\n- Authentication method changes for [`/v1/users/password`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/users/password) REST API requests.\n\n    - `PUT`, `POST`, and `DELETE` methods require users to include their usernames and a current password in the authentication header to change their password lists. If the authentication header is not provided, the response status will be `401 Unauthorized`.\n\n    - `/v1/users/password` requests change the password list of the user who provides their credentials in the authorization header when sending the requests.\n\n    - `PUT` and `POST` requests will ignore `username` and `old_password` parameters provided in the request body.\n\n    - `DELETE` requests will ignore the `username` parameter provided in the request body.\n\n- Authentication method changes for [`POST /v1/users/authorize`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/users/authorize) REST API requests.\n\n    - The `POST` method requires users to include their usernames and a current password in the authentication header to generate a JSON Web Token.\n\n    - `POST /v1/users/authorize` generates a token for the user who provides their credentials in the authorization header when sending the requests.\n\n    - `POST` requests will ignore `username` and `password` parameters provided in the request body."},{"id":"redis-74-breaking-changes","title":"Redis database version 7.4 breaking changes","role":"content","text":"When new major versions of Redis Community Edition change existing commands, upgrading your database to a new version can potentially break some functionality. Before you upgrade, read the provided list of breaking changes that affect Redis Software and update any applications that connect to your database to handle these changes.\n\nConfirm your Redis database version (`redis_version`) using the Cluster Manager UI or run the following [`INFO`](https://redis.io/docs/latest/commands/info) command with [`redis-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/redis-cli):\n\n[code example]\n\n#### Security behavior changes\n\n- [#13108](https://github.com/redis/redis/pull/13108) Lua: LRU eviction for scripts generated with `EVAL`. \n\n#### Other general behavior changes\n\n- [#13133](https://github.com/redis/redis/pull/13133) Lua: allocate VM code with jemalloc instead of libc and count it as used memory.\n\n- [#12171](https://github.com/redis/redis/pull/12171) `ACL LOAD`: do not disconnect all clients."},{"id":"product-lifecycle-updates","title":"Product lifecycle updates","role":"content","text":"#### End-of-life policy extension\n\nThe end-of-life policy for Redis Software versions 6.2 and later has been extended to 24 months after the formal release of the subsequent major version. For the updated end-of-life schedule, see the [Redis Software product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle).\n\n#### Supported upgrade paths\n\nRedis Software versions 6.2.4 and 6.2.8 do not support direct upgrades beyond version 7.4.x. Versions 6.2.10, 6.2.12, and 6.2.18 are part of the [upgrade path](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-cluster#supported-upgrade-paths). To upgrade from 6.2.4 or 6.2.8 to versions later than 7.4.x, an intermediate upgrade is required.\n\nThe next major Redis Software release will still bundle Redis database version 6.2 and allow database upgrades from Redis database version 6.2 to 7.x.\n\nSee the [Redis Software product lifecycle](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle) for more information about release numbers.\n\n#### End of triggers and functions preview\n\nThe [triggers and functions](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/deprecated-features/triggers-and-functions) (RedisGears) preview has been discontinued.\n\n- Commands such as `TFCALL`, `TFCALLASYNC`, and `TFUNCTION` will be deprecated and will return error messages.\n\n- Any JavaScript functions stored in Redis will be removed. \n\n- JavaScript-based triggers will be blocked.\n\n- Lua functions and scripts will not be affected.\n\nIf your database currently uses triggers and functions, you need to: \n\n1. Adjust your applications to accommodate these changes.\n\n1. Delete all triggers and functions libraries from your existing database:\n\n    1. Run `TFUNCTION LIST`.\n\n    1. Copy all library names.\n\n    1. Run `TFUNCTION DELETE` for each library in the list.\n\n    If any triggers and functions libraries remain in the database, the RDB snapshot won't load on a cluster without RedisGears.\n\n1. Migrate your database to a new database without the RedisGears module."},{"id":"deprecations","title":"Deprecations","role":"content","text":"#### API deprecations\n\n- Deprecated `background_op` field from BDB REST API object. Use [`GET /v1/actions/bdb/<bdb_uid>`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/actions/bdb) instead.\n\n- Deprecated the following fields for [upgrade database](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/upgrade) REST API requests:\n\n    - `keep_redis_version`; use `redis_version` instead\n\n    - `current_module`; use `new_module_args` instead\n\n    - `new_module`; use `new_module_args` instead\n\n- Deprecated the following `module_list` fields for [create database](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#post-bdbs-v1) REST API requests:\n\n    - `module_id`; use `module_name` instead\n\n    - `semantic_version`; use module_args instead\n\n- `min_redis_version` is only relevant to Redis database versions earlier than 7.4 and is replaced with `compatible_redis_version` in [module REST API](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/modules) responses.\n\n- Deprecated the [`rladmin upgrade modules`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/upgrade#upgrade-module) command. Use [`rladmin upgrade db`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/upgrade#upgrade-db) instead.\n\n- Deprecated `POST /v1/modules/upgrade/bdb/<uid>` REST API request. Use [`POST /v1/bdbs/<uid>/upgrade`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/upgrade#post-bdbs-upgrade) to upgrade modules instead.\n\n#### V1 Prometheus metrics deprecation\n\n V1 Prometheus metrics are deprecated but still available. To transition to the new metrics stream engine, either migrate your existing dashboards using [Prometheus v1 metrics and equivalent v2 PromQL](https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise/prometheus-metrics-v1-to-v2) now, or wait to use new preconfigured dashboards when they become available in a future release.\n\n#### Download center modules deprecation\n\nNew Redis modules will not be available for download from the Redis download center.\n\n#### Legacy UI not supported\n\nThe legacy UI was deprecated in favor of the new Cluster Manager UI in Redis Software version 7.2.4 and is no longer supported as of Redis Software version 7.8.2.\n\n#### Redis 6.0 databases not supported\n\nRedis database version 6.0 was deprecated in Redis Software version 7.4.2 and is no longer supported as of Redis Software version 7.8.2.\n\nTo prepare for the removal of Redis database version 6.0 before you upgrade to Redis Software version 7.8.2:\n\n- For Redis Software 6.2.* clusters:\n\n    1. Set the Redis upgrade policy to `latest`:\n\n        [code example]\n\n    1. [Upgrade Redis 6.0 databases](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-database) to Redis 6.2. See the [Redis 6.2 release notes](https://raw.githubusercontent.com/redis/redis/6.2/00-RELEASENOTES) for the list of changes.\n\n- For Redis Software 7.2.4 and 7.4.2 clusters, upgrade Redis 6.0 databases to Redis 7.2. Before you upgrade your databases, see the list of [Redis 7.2 breaking changes](https://redis.io/docs/latest/operate/rs/release-notes/rs-7-2-4-releases/rs-7-2-4-52#redis-72-breaking-changes) and update any applications that connect to your database to handle these changes.\n\n#### Ubuntu 18.04 not supported\n\nUbuntu 18.04 was deprecated in Redis Software version 7.2.4 and is no longer supported as of Redis Software version 7.8.2."},{"id":"upcoming-changes","title":"Upcoming changes","role":"content","text":"#### Default image change for Redis Software containers\n\nStarting with version 7.8, Redis Software containers with the image tag `x.y.z-build` will be based on RHEL instead of Ubuntu.\n\nThis change will only affect you if you use containers outside the official [Redis Enterprise for Kubernetes](https://redis.io/docs/latest/operate/kubernetes) product and use Ubuntu-specific commands.\n\nTo use Ubuntu-based images after this change, you can specify the operating system suffix in the image tag. For example, use the image tag `7.4.2-216.focal` instead of `7.4.2-216`."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.8.2-34 Nov release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">17500356d8338e4f8fd8a37e7b39a190d05ca66d35ae6c4aa3aa8cbc7bb99864</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">caa2ccd24749ae1fb904841df50a8b69b69c74441458649ca49b9b617e286191</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">31f9d07beb7dfd9239083ecad99ecbfe3cdfcf96673881ebed08171d8194bff3</span> |\n| Amazon Linux 2 | <span class=\"break-all\">a737ca86d800caf9ca266d5771fbcffd3f973a7fb8e63e7b819681322ff6ed67</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### RedisGraph prevents upgrade to RHEL 9 \n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Query results might include hash keys with lazily expired fields\n\nIf one or more fields of a hash key expire after an `FT.SEARCH` or `FT.AGGREGATE` query begins, Redis does not account for these lazily expired fields. As a result, keys with expired fields might still be included in the query results, leading to potentially incorrect or inconsistent results."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.8.2-34 supports open source Redis 7.4, 7.2, and 6.2. Below is the list of open source Redis CVEs fixed by version.\n\nRedis 7.2.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[{"id":"redis-74-breaking-changes-ex0","language":"sh","code":"$ redis-cli -p <port> INFO\n\"# Server\nredis_version:7.0.8\n...\"","section_id":"redis-74-breaking-changes"},{"id":"deprecations-ex0","language":"sh","code":"rladmin tune cluster redis_upgrade_policy latest","section_id":"deprecations"}]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-2-60","title":"Redis Software release notes 7.8.2-60 (December 2024)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-2-60/","summary":"Bug fixes, including a fix for a RHEL 8 OpenSSL dependency issue that caused frequent restarts of the `cm_server` and prevented access to the Redis Software Cluster Manager UI after upgrading to Redis Software version 7.8.2-34.","aliases":["/operate/rs/release-notes/rs-7-8-releases/rs-7-8-2-tba/"],"tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"f442f25c81e3f7c4bded9a9799eab630645223001c8fca17be4b9fc97d335c25","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Software version 7.8.2](https://redis.io/downloads/#software)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Bug fixes, including a fix for a RHEL 8 OpenSSL dependency issue that caused frequent restarts of the `cm_server` and prevented access to the Redis Software Cluster Manager UI after upgrading to Redis Software version 7.8.2-34."},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 7.8.2 includes three Redis database versions: 7.4.0, 7.2.4, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 7.4."},{"id":"redis-module-feature-sets","title":"Redis module feature sets","role":"content","text":"Redis Software comes packaged with several modules. As of version 7.8.2, Redis Software includes three feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)<br />[RedisGraph v2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes)<sup>[1](#module-note-1)</sup>  |\n\n1. <a name=\"module-note-1\"></a>RedisGraph end-of-life has been announced and will be removed in a future release. See the [RedisGraph end-of-life announcement](https://redis.io/blog/redisgraph-eol/) for more details."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS142899: Fixed a RHEL 8 OpenSSL dependency issue that caused frequent restarts of the `cm_server` and prevented access to the Cluster Manager UI after upgrading to Redis Software version 7.8.2-34.\n\n- RS126364: Fixed a memory leak in the DMC proxy process."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.8.2-60 Dec release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">c2913aaf2641273bd35ef3564b2e51b0911c3e3ef7b552735efd6372edc4425d</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">351b0bc249973d6fe93d3bb19f2dd1d2275128459e4542c052587341f883152f</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">361dad6fba36e5da3975ebf060d1ebe8bbb3839a2a22d11e98761667f497a6a2</span> |\n| Amazon Linux 2 | <span class=\"break-all\">bad3f9cff028a51b71c666e73fe5a2fd81ab863cce69f418d55e75a5faa24aa9</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### RedisGraph prevents upgrade to RHEL 9 \n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Query results might include hash keys with lazily expired fields\n\nIf one or more fields of a hash key expire after an `FT.SEARCH` or `FT.AGGREGATE` query begins, Redis does not account for these lazily expired fields. As a result, keys with expired fields might still be included in the query results, leading to potentially incorrect or inconsistent results."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.8.2-60 supports open source Redis 7.4, 7.2, and 6.2. Below is the list of open source Redis CVEs fixed by version.\n\nRedis 7.2.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-4-18","title":"Redis Software release notes 7.8.4-18 (December 2024)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-4-18/","summary":"Certificate-based authentication for the REST API. Dry run option to validate Redis ACL REST API requests.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"e87f3634d2dec570c4d2eacfe32dc3c625df67f0648a13c202c30566825f17c1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"​[​Redis Software version 7.8.4](https://redis.io/downloads/#software) is now available! This release includes API enhancements that warranted a new minor version instead of a maintenance release for version 7.8.2. However, you can upgrade from 7.8.2 to 7.8.4 without issue."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Certificate-based authentication for the REST API\n\n- Dry run option to validate Redis ACL REST API requests"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"new-features","title":"New features","role":"content","text":"- Certificate-based authentication for the REST API\n\n    - Added support for certificate-based authentication to enable secure, passwordless access to the Redis Software REST API.\n\n    - Certificate-based authentication can be used in addition to basic authentication with a username and password.\n\n    - To set up certificate-based authentication, use [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api) requests to do the following:\n\n        1. Configure a trusted CA certificate `mtls_trusted_ca` using an [update cluster certificate](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/certificates#put-cluster-update_cert) request:\n\n            [code example]\n\n        1. [Update cluster settings](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster#put-cluster) with mutual TLS configuration:\n\n            [code example]\n\n        1. Include `\"auth_method\": \"certificate\"` and `certificate_subject_line` in the request body when you [create new users](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/users#post-user) or [update existing users](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/users#put-user):\n\n            [code example]\n\n    - Limitations: \n\n        - Certificate-based authentication is not implemented for the Cluster Manager UI."},{"id":"enhancements","title":"Enhancements","role":"content","text":"- If you create or update a Redis ACL using the REST API, you can use the `dry_run` query parameter to validate the request without actually applying the update.\n\n    - [Create Redis ACL](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/redis_acls#post-redis_acl) example request:\n\n        [code example]\n\n    - [Update Redis ACL](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/redis_acls#put-redis_acl) example request: \n\n        [code example]"},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 7.8.4 includes three Redis database versions: 7.4.0, 7.2.4, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 7.4."},{"id":"redis-module-feature-sets","title":"Redis module feature sets","role":"content","text":"Redis Software comes packaged with several modules. As of version 7.8.2, Redis Software includes three feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)<br />[RedisGraph v2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes)<sup>[1](#module-note-1)</sup>  |\n\n1. <a name=\"module-note-1\"></a>RedisGraph end-of-life has been announced and will be removed in a future release. See the [RedisGraph end-of-life announcement](https://redis.io/blog/redisgraph-eol/) for more details."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS137337: Maintenance mode tasks are no longer incorrectly marked as `aborted` when the primary node is demoted.\n\n- RS125921: Fixed a PDNS server script to ensure the symbolic links are not expanded.\n\n- RS142457: Fixed an issue in the Cluster Manager UI where incorrect FQDN information was shown for Active-Active databases in Kubernetes.\n\n- RS134410: Removed dependency test files that contain TLS keys from the Python install for Kubernetes.\n\n- RS136270: Changed the owner and group of the `rlec_supervisor.service` file to `root:root` instead of `redislabs:redislabs` to improve security.\n\n- RS142166: Fixed the representation of available RAM."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.8.4-18 Dec release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">1aee0b6cee72b6ceae73951611ac2073d8905189174c731561f1228c16cbcc3a</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">cceb563d56221d391667dd302ac334b5d98e3a8dc59bc479a79fe9b83c1634d6</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">396b2eeafe9b4a75aa8579b4d618ca7f2df8619b1256aeec7863c80ecba7703a</span> |\n| Amazon Linux 2 | <span class=\"break-all\">56e18f73fb8507fec476d9b59a00ed1b5fcc52b80005a749b869650641e6f6f6</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors.\n\n- RS151990: For RHEL 8 and RHEL 9 clusters, importing an RDB file can fail with the exception: `Failed to import rdb file: general-error`.\n\n    As a workaround:\n\n    1. On each cluster node, run:\n\n        [code example]\n\n    1. Verify that `libboost_program_options.so.1.75.0` points to a path to confirm the installation succeeded:\n\n        [code example]"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### RedisGraph prevents upgrade to RHEL 9 \n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Query results might include hash keys with lazily expired fields\n\nIf one or more fields of a hash key expire after an `FT.SEARCH` or `FT.AGGREGATE` query begins, Redis does not account for these lazily expired fields. As a result, keys with expired fields might still be included in the query results, leading to potentially incorrect or inconsistent results."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.8.4-18 supports open source Redis 7.4, 7.2, and 6.2. Below is the list of open source Redis CVEs fixed by version.\n\nRedis 7.2.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[{"id":"new-features-ex0","language":"sh","code":"PUT /v1/cluster/update_cert\n            {\n              \"name\": \"mtls_trusted_ca\",\n              \"certificate\": \"<content of certificate PEM file>\"\n            }","section_id":"new-features"},{"id":"new-features-ex1","language":"sh","code":"PUT /v1/cluster\n            {\n              \"mtls_certificate_authentication\": true,\n              \"mtls_client_cert_subject_validation_type\": \"san_cn\",\n              \"mtls_authorized_subjects\": [{\n                \"CN\": \"test\",\n                \"OU\": [],\n                \"O\": \"\",\n                \"C\": \"\",\n                \"L\": \"\",\n                \"ST\": \"\"\n              }]\n            }","section_id":"new-features"},{"id":"new-features-ex2","language":"sh","code":"POST /v1/users | PUT /v1/users/<user-id>\n            {\n              \"auth_method\": \"certificate\",\n              \"certificate_subject_line\": \"<subject of the user's client certificate>\"\n            }","section_id":"new-features"},{"id":"enhancements-ex0","language":"sh","code":"POST /v1/redis_acls?dry_run","section_id":"enhancements"},{"id":"enhancements-ex1","language":"sh","code":"PUT /v1/redis_acls/<uid>?dry_run","section_id":"enhancements"},{"id":"known-issues-ex0","language":"sh","code":"sudo dnf install boost-program-options","section_id":"known-issues"},{"id":"known-issues-ex1","language":"sh","code":"ldd /opt/redislabs/bin/rl_rdbloader","section_id":"known-issues"}]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-4-66","title":"Redis Software release notes 7.8.4-66 (February 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-4-66/","summary":"Ubuntu 22 support. Bug fixes for crashes, unexpected restarts, and incorrect type for the automatically calculated replica buffer.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"e02cdc394dbb0336043680d4f05b8d6842c1d7263b9f377c293b2134ee0ed639","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Software version 7.8.4](https://redis.io/downloads/#software)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Ubuntu 22 support\n\n- Bug fixes for crashes, unexpected restarts, and incorrect type for the automatically calculated replica buffer"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"new-features","title":"New features","role":"content","text":"- Added support for Ubuntu 22. See [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os) for detailed upgrade instructions."},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 7.8.4 includes three Redis database versions: 7.4.0, 7.2.4, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 7.4."},{"id":"redis-module-feature-sets","title":"Redis module feature sets","role":"content","text":"Redis Software comes packaged with several modules. As of version 7.8.2, Redis Software includes three feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)<br />[RedisGraph v2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes)<sup>[1](#module-note-1)</sup>  |\n\n1. <a name=\"module-note-1\"></a>RedisGraph end-of-life has been announced and will be removed in a future release. See the [RedisGraph end-of-life announcement](https://redis.io/blog/redisgraph-eol/) for more details."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS146125: Fixed an issue with the CCS cached context that could cause crashes.\n\n- RS145856: Fixed the automatically calculated replica buffer's type from float to integer.\n\n- RS146282: Fixed a missing Prisma dependency for OpenSSL 1.0 that caused unexpected `cm_server` restarts on Ubuntu 20.\n\n- RS103760: Added a check that blocks upgrading Redis Software on a node while there are running state machines to prevent data loss or unavailability.\n\n- RS148302: Fixed a dependency issue that can cause the installation of Redis Software version 7.8.4 to fail."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span>| <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.8.4-66 Feb release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">5b14894713d118688a9b21d63edd5208cd6b0d5a2a035ecd67589d33d5ddfbbb</span> |\n| Ubuntu 22 | <span class=\"break-all\">fb4dda7c75c6ded114073edd0632257cc56c52e92ce1e8d286070d5adc593431</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">1d6b0579c6af5e57e045b051bd0026638ac0eb224266b19a8d4be1e1135d05ea</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">fa74e1864eb72aafe6428ee3aa7bc0fb491b637d4bdf11be785f67fbca7130e6</span> |\n| Amazon Linux 2 | <span class=\"break-all\">748268cb234d828c0dc1542ac0e0d4ff74363a2008ee473c08abe1d949bc850d</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors.\n\n- RS151990: For RHEL 8 and RHEL 9 clusters, importing an RDB file can fail with the exception: `Failed to import rdb file: general-error`.\n\n    As a workaround:\n\n    1. On each cluster node, run:\n\n        [code example]\n\n    1. Verify that `libboost_program_options.so.1.75.0` points to a path to confirm the installation succeeded:\n\n        [code example]"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Upload modules before OS upgrade\n\nIf the cluster contains any databases that use modules, you must upload module packages for the target OS version to a node in the existing cluster before you upgrade the cluster's operating system.\n\nSee [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os) for detailed upgrade instructions.\n\n#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### RedisGraph prevents upgrade to RHEL 9 \n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Query results might include hash keys with lazily expired fields\n\nIf one or more fields of a hash key expire after an `FT.SEARCH` or `FT.AGGREGATE` query begins, Redis does not account for these lazily expired fields. As a result, keys with expired fields might still be included in the query results, leading to potentially incorrect or inconsistent results."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.8.4-66 supports open source Redis 7.4, 7.2, and 6.2. Below is the list of open source Redis CVEs fixed by version.\n\nRedis 7.2.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[{"id":"known-issues-ex0","language":"sh","code":"sudo dnf install boost-program-options","section_id":"known-issues"},{"id":"known-issues-ex1","language":"sh","code":"ldd /opt/redislabs/bin/rl_rdbloader","section_id":"known-issues"}]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-4-95","title":"Redis Software release notes 7.8.4-95 (March 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-4-95/","summary":"Bug fixes for server restart failures during upgrades, connectivity checks for optional services, swapped shard status and watchdog status values in `rladmin`, process exporter crashes, and a syncer issue for Active-Active databases that have the OSS Cluster API enabled.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"0b72f1867f87b419bea23b0bed8e05aa674c9b32eb0319241754308ef7d8fd0d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Software version 7.8.4](https://redis.io/downloads/#software)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Bug fixes for server restart failures during upgrades, connectivity checks for optional services, swapped shard status and watchdog status values in `rladmin`, process exporter crashes, and a syncer issue for Active-Active databases that have the OSS Cluster API enabled"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 7.8.4 includes three Redis database versions: 7.4.0, 7.2.4, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 7.4."},{"id":"redis-module-feature-sets","title":"Redis module feature sets","role":"content","text":"Redis Software comes packaged with several modules. As of version 7.8.2, Redis Software includes three feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)<br />[RedisGraph v2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes)<sup>[1](#module-note-1)</sup>  |\n\n1. <a name=\"module-note-1\"></a>RedisGraph end-of-life has been announced and will be removed in a future release. See the [RedisGraph end-of-life announcement](https://redis.io/blog/redisgraph-eol/) for more details."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS134225: Fixed an issue where the server restart could fail during cluster upgrade due to a timeout.\n\n- RS138625: Fixed an issue where `rlcheck` reported `verify_tcp_connectivity` as failed for optional services that were not enabled instead of skipping the checks.\n\n- RS146525: Fixed an `rladmin status` issue that swapped the shard `STATUS` and `WATCHDOG_STATUS` values.\n\n- RS143339: Fixed an issue where an ambiguous path could cause the process exporter to crash with the error: \"`flag provided but not defined: -remove-empty-groups`\".\n\n- RS150588: Fixed a syncer issue that could cause data inconsistency or loss in Active-Active databases when the OSS Cluster API is enabled."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.8.4-95 March release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">f7b98686e0b0a6dd72cef0c59b0d5a6126e716251e8606df2e0296e714e89854</span> |\n| Ubuntu 22 | <span class=\"break-all\">b2bda6ed49edcae32cac5bf67dcdcc3fc1baca47945b3c0a9e7c5a11bee4839f</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">cecaa7aa9db1a2b93b91389bd7b5958af26d2cfb5c4fc5d393dbc83370156aad</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">8f62996176ba58a073b66c0623f3a280c7fe0f25358d727df375021354d4464f</span> |\n| Amazon Linux 2 | <span class=\"break-all\">baea0cdada0ac42d38035b8ca826eabc2077256e2633b8cea5ff5010ff0ac53d</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors.\n\n- RS151990: For RHEL 8 and RHEL 9 clusters, importing an RDB file can fail with the exception: `Failed to import rdb file: general-error`.\n\n    As a workaround:\n\n    1. On each cluster node, run:\n\n        [code example]\n\n    1. Verify that `libboost_program_options.so.1.75.0` points to a path to confirm the installation succeeded:\n\n        [code example]"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Upload modules before OS upgrade\n\nIf the cluster contains any databases that use modules, you must upload module packages for the target OS version to a node in the existing cluster before you upgrade the cluster's operating system.\n\nSee [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os) for detailed upgrade instructions.\n\n#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### RedisGraph prevents upgrade to RHEL 9 \n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Query results might include hash keys with lazily expired fields\n\nIf one or more fields of a hash key expire after an `FT.SEARCH` or `FT.AGGREGATE` query begins, Redis does not account for these lazily expired fields. As a result, keys with expired fields might still be included in the query results, leading to potentially incorrect or inconsistent results."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.8.4-95 supports open source Redis 7.4, 7.2, and 6.2. Below is the list of open source Redis CVEs fixed by version.\n\nRedis 7.4.x:\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\nRedis 7.2.x:\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[{"id":"known-issues-ex0","language":"sh","code":"sudo dnf install boost-program-options","section_id":"known-issues"},{"id":"known-issues-ex1","language":"sh","code":"ldd /opt/redislabs/bin/rl_rdbloader","section_id":"known-issues"}]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-119","title":"Redis Software release notes 7.8.6-119 (June 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-119/","summary":"Bug fixes for user password management REST API requests, maintenance mode snapshot cleanup, and rlec_supervisor file permissions.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"09d7bc6493d599e0afdbb40fa54d1491ff0796fdcfa3ef93dc02d80664a84b58","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for Redis Software version 7.8.6."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Bug fixes for user password management REST API requests, maintenance mode snapshot cleanup, and rlec_supervisor file permissions"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 7.8.6 includes three Redis database versions: 7.4.0, 7.2.4, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 7.4."},{"id":"redis-module-feature-sets","title":"Redis module feature sets","role":"content","text":"Redis Software comes packaged with several modules. As of version 7.8.2, Redis Software includes three feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)<br />[RedisGraph v2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes)<sup>[1](#module-note-1)</sup>  |\n\n1. <a name=\"module-note-1\"></a>RedisGraph end-of-life has been announced and will be removed in a future release. See the [RedisGraph end-of-life announcement](https://redis.io/blog/redisgraph-eol/) for more details."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS161290: Fixed an issue where the `/v1/users/password` REST API updated the password of the authenticated admin user instead of the intended target user specified in the JSON request body.\n\n- RS122458: Fixed an issue where old snapshots were not properly deleted when turning off maintenance mode with the `skip_shards_restore` option.\n\n- RS136270: Changed the owner and group of the `rlec_supervisor.service` file to `root:root` instead of `redislabs:redislabs` to improve security."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.8.6-119 June release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">b304aec42e9e4f8e205d7becc71b1e84e0f0fee328b0b14a61c28517edff7712</span> |\n| Ubuntu 22 | <span class=\"break-all\">69b5a35f62ed7467aea31ed07a0feb162fc5527254136d4aea6d8c9b3097ab5a</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">5f757f97945e70e81ab7456672acf670d5a76eb1f9e916b09da97223f6a8edec</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">197803953831bcf875ad7489cb718d3359d175a1cfe03e7cf1e76ecf30545d04</span> |\n| Amazon Linux 2 | <span class=\"break-all\">0700afc9e2ebcac27a6ca609c1376a110eb8177d6e82e0f6a0fbec30529415a8</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Upload modules before OS upgrade\n\nIf the cluster contains any databases that use modules, you must upload module packages for the target OS version to a node in the existing cluster before you upgrade the cluster's operating system.\n\nSee [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os) for detailed upgrade instructions.\n\n#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### RedisGraph prevents upgrade to RHEL 9 \n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Query results might include hash keys with lazily expired fields\n\nIf one or more fields of a hash key expire after an `FT.SEARCH` or `FT.AGGREGATE` query begins, Redis does not account for these lazily expired fields. As a result, keys with expired fields might still be included in the query results, leading to potentially incorrect or inconsistent results."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.8.6-119 supports open source Redis 7.4, 7.2, and 6.2. Below is the list of open source Redis CVEs fixed by version.\n\nRedis 7.4.x:\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\nRedis 7.2.x:\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-13","title":"Redis Software release notes 7.8.6-13 (March 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-13/","summary":"Configurable hashing algorithm for user passwords. Dry run option to validate REST API requests for users and roles.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"b224584bd64a2c6097d33f1254167001f30ac4b8bf907f6b196e035b68a41678","sections":[{"id":"overview","title":"Overview","role":"overview","text":"​[​Redis Software version 7.8.6](https://redis.io/downloads/#software) is now available! This release includes API enhancements that warranted a new minor version instead of a maintenance release for version 7.8.4. However, you can upgrade from 7.8.2 or 7.8.4 to 7.8.6 without issue."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Configurable hashing algorithm for user passwords\n\n- Dry run option to validate REST API requests for users and roles"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"new-features","title":"New features","role":"content","text":"- The hashing algorithm for user passwords is now configurable using [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin) or the [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api). When you [change the hashing algorithm](https://redis.io/docs/latest/operate/rs/security/access-control/manage-passwords/password-hashing-algorithm), it rehashes the administrator password and passwords for all users, including default users.\n\n    The hashing algorithm options are `SHA-256` or `PBKDF2`. The default hashing algorithm is `SHA-256`.\n\n    To change the password hashing algorithm, use one of the following methods:\n\n    - [`rladmin cluster change_password_hashing_algorithm`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/change_password_hashing_algorithm):\n\n        [code example]\n\n    - [Change password hashing algorithm REST API request](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/change_password_hashing_algorithm#patch-change-password-hashing-algorithm):\n\n        [code example]"},{"id":"enhancements","title":"Enhancements","role":"content","text":"- If you create or update users or roles using the [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api), you can use the `dry_run` query parameter to validate the request without actually applying the update.\n\n    - [Create role](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/roles#post-role) example request:\n\n        [code example]\n\n    - [Update role](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/roles#put-role) example request: \n\n        [code example]\n\n    - [Create user](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/users#post-user) example request:\n\n        [code example]\n\n    - [Update user](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/users#put-user) example request: \n\n        [code example]"},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 7.8.6 includes three Redis database versions: 7.4.0, 7.2.4, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 7.4."},{"id":"redis-module-feature-sets","title":"Redis module feature sets","role":"content","text":"Redis Software comes packaged with several modules. As of version 7.8.2, Redis Software includes three feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)<br />[RedisGraph v2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes)<sup>[1](#module-note-1)</sup>  |\n\n1. <a name=\"module-note-1\"></a>RedisGraph end-of-life has been announced and will be removed in a future release. See the [RedisGraph end-of-life announcement](https://redis.io/blog/redisgraph-eol/) for more details."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS147991: Fixed an issue where the creation of too many Replica Of workers could cause the DMC proxy to restart repeatedly."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.8.6-13 March release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">8fa5336a6b487f4082353f7abad924a86b4ce2719e700ea0c8766718a2066020</span> |\n| Ubuntu 22 | <span class=\"break-all\">0013ad5d97b3480b9d8237ec22f15d0aa7d08f3caa75312e5d2597583229b2fa</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">e66cc1661540028f941189e41a17a32b17797c82abf90237f7da9491ea20a1c0</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">d588491fa96cc652aa92d43902efa490a90fbfe8967c649ea54284c7986f37bd</span> |\n| Amazon Linux 2 | <span class=\"break-all\">baea0cdada0ac42d38035b8ca826eabc2077256e2633b8cea5ff5010ff0ac53d</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors.\n\n- RS151990: For RHEL 8 and RHEL 9 clusters, importing an RDB file can fail with the exception: `Failed to import rdb file: general-error`.\n\n    As a workaround:\n\n    1. On each cluster node, run:\n\n        [code example]\n\n    1. Verify that `libboost_program_options.so.1.75.0` points to a path to confirm the installation succeeded:\n\n        [code example]"},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Upload modules before OS upgrade\n\nIf the cluster contains any databases that use modules, you must upload module packages for the target OS version to a node in the existing cluster before you upgrade the cluster's operating system.\n\nSee [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os) for detailed upgrade instructions.\n\n#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### RedisGraph prevents upgrade to RHEL 9 \n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Query results might include hash keys with lazily expired fields\n\nIf one or more fields of a hash key expire after an `FT.SEARCH` or `FT.AGGREGATE` query begins, Redis does not account for these lazily expired fields. As a result, keys with expired fields might still be included in the query results, leading to potentially incorrect or inconsistent results."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.8.6-13 supports open source Redis 7.4, 7.2, and 6.2. Below is the list of open source Redis CVEs fixed by version.\n\nRedis 7.4.x:\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\nRedis 7.2.x:\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[{"id":"new-features-ex0","language":"sh","code":"rladmin cluster change_password_hashing_algorithm PBKDF2","section_id":"new-features"},{"id":"new-features-ex1","language":"sh","code":"PATCH /v1/cluster/change_password_hashing_algorithm\n        { \"algorithm\": \"PBKDF2\" }","section_id":"new-features"},{"id":"enhancements-ex0","language":"sh","code":"POST /v1/roles?dry_run","section_id":"enhancements"},{"id":"enhancements-ex1","language":"sh","code":"PUT /v1/roles/<uid>?dry_run","section_id":"enhancements"},{"id":"enhancements-ex2","language":"sh","code":"POST /v1/users?dry_run","section_id":"enhancements"},{"id":"enhancements-ex3","language":"sh","code":"PUT /v1/users/<uid>?dry_run","section_id":"enhancements"},{"id":"known-issues-ex0","language":"sh","code":"sudo dnf install boost-program-options","section_id":"known-issues"},{"id":"known-issues-ex1","language":"sh","code":"ldd /opt/redislabs/bin/rl_rdbloader","section_id":"known-issues"}]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-152","title":"Redis Software release notes 7.8.6-152 (August 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-152/","summary":"Bug fixes for Active-Active syncing, event log entries for configuration changes made by LDAP users, LDAP mapping visibility in the Cluster Manager UI, database max_connections setting, and memory leaks when using Active-Active databases in Kubernetes.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"9bdde71f7f8f43fc6a4dcd5002fde27dad4167da9e7314c96b30f1210308b191","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for Redis Software version 7.8.6."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Bug fixes for Active-Active syncing, event log entries for configuration changes made by LDAP users, LDAP mapping visibility in the Cluster Manager UI, database max_connections setting, and memory leaks when using Active-Active databases in Kubernetes."},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 7.8.6 includes three Redis database versions: 7.4.0, 7.2.4, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 7.4."},{"id":"redis-module-feature-sets","title":"Redis module feature sets","role":"content","text":"Redis Software comes packaged with several modules. As of version 7.8.2, Redis Software includes three feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)<br />[RedisGraph v2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes)<sup>[1](#module-note-1)</sup>  |\n\n1. <a name=\"module-note-1\"></a>RedisGraph end-of-life has been announced and will be removed in a future release. See the [RedisGraph end-of-life announcement](https://redis.io/blog/redisgraph-eol/) for more details."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS140077: Fixed a syncer issue where the number of keys did not match across the instances of Active-Active databases reconfigured with `only_configuration` after cluster recovery.\n\n- RS155910: Fixed an issue where event logs incorrectly attributed configuration changes to `system` instead of the user who made the changes if they authenticated using LDAP.\n\n- RS144065: Fixed an issue where LDAP mappings created using the REST API without the `bdbs_email_alerts` field were not visible in the Cluster Manager UI.\n\n- RS144380: Fixed an issue where updating a database configuration in the Cluster Manager UI reset `max_connections` to 0, which allowed unlimited client connections.\n\n- RS149040: Fixed a Gunicorn memory leak when using Active-Active databases in Kubernetes (REAADB).\n\n- RS110383: Fixed LDAP socket file permissions to allow users in the `redislabs` group to run commands."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"behavior-changes","title":"Behavior changes","role":"content","text":"- The system now preserves user-defined `max_connections` for databases instead of resetting the values to unlimited (0) when the database configuration is updated using the Cluster Manager UI. If you previously set a database's `max_connections` to a value that doesn't align with your expected workload, review and adjust your configuration to prevent unintended client disconnections in case the threshold is too low."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.8.6-152 August release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">4733282e2487ddf283461f68051c53d413c9fcdb23cd094beec011e01951282e</span> |\n| Ubuntu 22 | <span class=\"break-all\">e1064227ea1da4d3be52e0dc001c49d757050507a8be6c6fec32f37a8e34ed3d</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">e10de5341fff4e2ff6e502987a74eaf4b37b4ce91e6f0b184398cfa172b18286</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">34e678b3120f2e50f19135dd8e3ab7c50d64793809c95353444f10a37cd57b91</span> |\n| Amazon Linux 2 | <span class=\"break-all\">9c2613103031b59f82c6354b548fdd5c7292f444ee0d970e560cb591a545427e</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Upload modules before OS upgrade\n\nIf the cluster contains any databases that use modules, you must upload module packages for the target OS version to a node in the existing cluster before you upgrade the cluster's operating system.\n\nSee [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os) for detailed upgrade instructions.\n\n#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### RedisGraph prevents upgrade to RHEL 9\n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Query results might include hash keys with lazily expired fields\n\nIf one or more fields of a hash key expire after an `FT.SEARCH` or `FT.AGGREGATE` query begins, Redis does not account for these lazily expired fields. As a result, keys with expired fields might still be included in the query results, leading to potentially incorrect or inconsistent results."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.8.6-152 supports open source Redis 7.4, 7.2, and 6.2. Below is the list of open source Redis CVEs fixed by version.\n\nRedis 7.4.x:\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\nRedis 7.2.x:\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-207","title":"Redis Software release notes 7.8.6-207 (September 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-207/","summary":"Enhanced timeout diagnostics and logging when restarting shards. Bug fix for viewing shard memory usage metrics in the Cluster Manager UI. Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"4719fc621e487fa2afee4f9e8c86a393f560ad87ea7ed974312c36371bc67c48","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for Redis Software version 7.8.6."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Enhanced timeout diagnostics and logging when restarting shards\n\n- Bug fix for viewing shard memory usage metrics in the Cluster Manager UI\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"enhancements","title":"Enhancements","role":"content","text":"- Enhanced timeout diagnostics and logging when waiting for a Redis shard to restart."},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 7.8.6 includes three Redis database versions: 7.4.0, 7.2.4, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 7.4."},{"id":"redis-module-feature-sets","title":"Redis module feature sets","role":"content","text":"Redis Software comes packaged with several modules. As of version 7.8.2, Redis Software includes three feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)<br />[RedisGraph v2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes)<sup>[1](#module-note-1)</sup>  |\n\n1. <a name=\"module-note-1\"></a>RedisGraph end-of-life has been announced and will be removed in a future release. See the [RedisGraph end-of-life announcement](https://redis.io/blog/redisgraph-eol/) for more details."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS165532: Fixed an issue that could prevent viewing shard memory usage metrics in the Cluster Manager UI and cause the page to fail to load when switching between database and shard metrics."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.8.6-207 September release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">ba7fe4762109e43d4fc799cb7052aa8137558378f7f03b4afe8e0ac1ab45d0e6</span> |\n| Ubuntu 22 | <span class=\"break-all\">d302716e23ce913a068431756a572b55ebf8aac4dfbdedf7a82fff38bc8f76ec</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">84e9882242c940c322f512e4b9f71af71ab285e42e5d00ab41f9ac3d9d93368e</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">234933d15b4d701115cc33fa463bab00c888f10af553282593074d7a9c79aa34</span> |\n| Amazon Linux 2 | <span class=\"break-all\">398f986cead73ecbad65f4467f762d18c859ccd2e911d0e54c94cc45d60a8bd6</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Upload modules before OS upgrade\n\nIf the cluster contains any databases that use modules, you must upload module packages for the target OS version to a node in the existing cluster before you upgrade the cluster's operating system.\n\nSee [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os) for detailed upgrade instructions.\n\n#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### RedisGraph prevents upgrade to RHEL 9\n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Query results might include hash keys with lazily expired fields\n\nIf one or more fields of a hash key expire after an `FT.SEARCH` or `FT.AGGREGATE` query begins, Redis does not account for these lazily expired fields. As a result, keys with expired fields might still be included in the query results, leading to potentially incorrect or inconsistent results."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.8.6-207 supports open source Redis 7.4, 7.2, and 6.2. Below is the list of open source Redis CVEs and other security vulnerabilities fixed by version.\n\nRedis 7.4.x:\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\nRedis 7.2.x:\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-253","title":"Redis Software release notes 7.8.6-253 (February 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-253/","summary":"Bug fixes for database recovery and modifying databases in the Cluster Manager UI. Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"52ddb62c50818f38bf04a8ddf1cefddc11858dc781a329cc2f40eb9cc69d5528","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for Redis Software version 7.8.6."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Bug fixes for database recovery and modifying databases in the Cluster Manager UI\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 7.8.6 includes three Redis database versions: 7.4.0, 7.2.4, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 7.4."},{"id":"redis-module-feature-sets","title":"Redis module feature sets","role":"content","text":"Redis Software comes packaged with several modules. As of version 7.8.2, Redis Software includes three feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)<br />[RedisGraph v2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes)<sup>[1](#module-note-1)</sup>  |\n\n1. <a name=\"module-note-1\"></a>RedisGraph end-of-life has been announced and will be removed in a future release. See the [RedisGraph end-of-life announcement](https://redis.io/blog/redisgraph-eol/) for more details."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS173258: Fixed an issue where database recovery could fail during cluster recovery due to a missing state handler.\n\n- RS177376: Fixed an issue where modifying existing databases through the Cluster Manager UI could fail with \"The requested resource is unavailable\" errors."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.8.6-253 February release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">c11e1d3e1106f1161557d3b4981078430a56c4aec9f072de0bc6a21925b59877</span> |\n| Ubuntu 22 | <span class=\"break-all\">88cce7a456cade9785f8f8aaf9685cbc99a14622b10ebef06f16863f7f678010</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">7d9cd026f38eaea3f6505a79f66d1fb61e63cfd9ab17835ed7a47deed13b1b2d</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">942d014db6e61f294a17deafe5260bd3bc8b07fbf88fca4bf12c48747ee33d7e</span> |\n| Amazon Linux 2 | <span class=\"break-all\">f8cf7f6942ba4da1a4b6b6c571c3a2ec3af0c5f7c86b7f1d2398d80767bea42e</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Upload modules before OS upgrade\n\nIf the cluster contains any databases that use modules, you must upload module packages for the target OS version to a node in the existing cluster before you upgrade the cluster's operating system.\n\nSee [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os) for detailed upgrade instructions.\n\n#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### RedisGraph prevents upgrade to RHEL 9\n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Query results might include hash keys with lazily expired fields\n\nIf one or more fields of a hash key expire after an `FT.SEARCH` or `FT.AGGREGATE` query begins, Redis does not account for these lazily expired fields. As a result, keys with expired fields might still be included in the query results, leading to potentially incorrect or inconsistent results."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.8.6-253 supports open source Redis 7.4, 7.2, and 6.2. Below is the list of open source Redis CVEs and other security vulnerabilities fixed by version.\n\nRedis 7.4.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\nRedis 7.2.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-256","title":"Redis Software release notes 7.8.6-256 (March 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-256/","summary":"Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"585a5d09170b2b2208638cbd4852d890df609c12bbb03d2f18cff5e6b9281136","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for Redis Software version 7.8.6."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 7.8.6 includes three Redis database versions: 7.4.0, 7.2.4, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 7.4."},{"id":"redis-module-feature-sets","title":"Redis module feature sets","role":"content","text":"Redis Software comes packaged with several modules. As of version 7.8.2, Redis Software includes three feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)<br />[RedisGraph v2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes)<sup>[1](#module-note-1)</sup>  |\n\n1. <a name=\"module-note-1\"></a>RedisGraph end-of-life has been announced and will be removed in a future release. See the [RedisGraph end-of-life announcement](https://redis.io/blog/redisgraph-eol/) for more details."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.8.6-256 March release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">69fa9e70d9718e2646c46ee7a09285c4e47febcc556a9728ac6e747c7c75f499</span> |\n| Ubuntu 22 | <span class=\"break-all\">bf624bfe54b2e80a608ec97aad8d902e70445175a4cf4b2c0acfba47b4b95f2f</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">7565de90080cf80729aeed7134b248c1c46e911d37df83cd5fc1a97448df9a46</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">5cf42bf8cc35420b87bd3124142eebb95c97dd3721e767c41b5f9977ce2f3aa5</span> |\n| Amazon Linux 2 | <span class=\"break-all\">1873daa09d9e17f09a456a17ee46f7415e40ac360d8fc919bcebe2630d979710</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Upload modules before OS upgrade\n\nIf the cluster contains any databases that use modules, you must upload module packages for the target OS version to a node in the existing cluster before you upgrade the cluster's operating system.\n\nSee [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os) for detailed upgrade instructions.\n\n#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### RedisGraph prevents upgrade to RHEL 9\n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Query results might include hash keys with lazily expired fields\n\nIf one or more fields of a hash key expire after an `FT.SEARCH` or `FT.AGGREGATE` query begins, Redis does not account for these lazily expired fields. As a result, keys with expired fields might still be included in the query results, leading to potentially incorrect or inconsistent results."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.8.6-256 supports open source Redis 7.4, 7.2, and 6.2. Below is the list of open source Redis CVEs and other security vulnerabilities fixed by version.\n\nRedis 7.4.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\nRedis 7.2.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-260","title":"Redis Software release notes 7.8.6-260 (April 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-260/","summary":"Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"0f509688793a7af781142252f3e7e75935aae2360d199ea593ba3b5f1ac6d7df","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for Redis Software version 7.8.6."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 7.8.6 includes three Redis database versions: 7.4.0, 7.2.4, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 7.4."},{"id":"redis-module-feature-sets","title":"Redis module feature sets","role":"content","text":"Redis Software comes packaged with several modules. As of version 7.8.2, Redis Software includes three feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)<br />[RedisGraph v2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes)<sup>[1](#module-note-1)</sup>  |\n\n1. <a name=\"module-note-1\"></a>RedisGraph end-of-life has been announced and will be removed in a future release. See the [RedisGraph end-of-life announcement](https://redis.io/blog/redisgraph-eol/) for more details."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.8.6-260 April release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">8c95381d3903bad4bac4c647230b75d95c2db7e99053a2062ddfb624504392cc</span> |\n| Ubuntu 22 | <span class=\"break-all\">a4606532641e97295ded37b58351852bce219a6c3dce97026660a3cf2db9fd68</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">945d98e018f15dc2047d54dd35b466c3d5a4a9ea10fca0dc2d9946052d6d4ee0</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">d60c568b0dbcf7bb286c358d0210d79f2d1f27f227f6f4fc06b0129253bc727f</span> |\n| Amazon Linux 2 | <span class=\"break-all\">0b6d71f65a13ab6fd656ff4b0df292c00201eeac89e41c85f331d21d1a3a3806</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Upload modules before OS upgrade\n\nIf the cluster contains any databases that use modules, you must upload module packages for the target OS version to a node in the existing cluster before you upgrade the cluster's operating system.\n\nSee [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os) for detailed upgrade instructions.\n\n#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### RedisGraph prevents upgrade to RHEL 9\n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Query results might include hash keys with lazily expired fields\n\nIf one or more fields of a hash key expire after an `FT.SEARCH` or `FT.AGGREGATE` query begins, Redis does not account for these lazily expired fields. As a result, keys with expired fields might still be included in the query results, leading to potentially incorrect or inconsistent results."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.8.6-260 supports open source Redis 7.4, 7.2, and 6.2. Below is the list of open source Redis CVEs and other security vulnerabilities fixed by version.\n\nRedis 7.4.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\nRedis 7.2.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-263","title":"Redis Software release notes 7.8.6-263 (May 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-263/","summary":"Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"0221d9489f628b87f1abd3af19e7e2aac43f12464879d87429cc191f4ce9bff5","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for Redis Software version 7.8.6."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 7.8.6 includes three Redis database versions: 7.4.0, 7.2.4, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 7.4."},{"id":"redis-module-feature-sets","title":"Redis module feature sets","role":"content","text":"Redis Software comes packaged with several modules. As of version 7.8.2, Redis Software includes three feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)<br />[RedisGraph v2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes)<sup>[1](#module-note-1)</sup>  |\n\n1. <a name=\"module-note-1\"></a>RedisGraph end-of-life has been announced and will be removed in a future release. See the [RedisGraph end-of-life announcement](https://redis.io/blog/redisgraph-eol/) for more details."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.8.6-263 April release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">012e7289a1cac79412b6f448a0f318fa4ddcaafebaf522ca53e1ed096d30f329</span> |\n| Ubuntu 22 | <span class=\"break-all\">f936c51117c4027f21bbe07408a52f9ae10c6bfc646624bb8dd474cd99b91eae</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">c3f8749cab066f7ab80883dbbdf27547d6d9450efccb98e2bcab44c1b45b29ae</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">115a5a7cc217ce4703a12db482bf900ee68705019a4478337c48b2bee6a90deb</span> |\n| Amazon Linux 2 | <span class=\"break-all\">d51139976ba446573725d38a33300c9aa1354546b0fb2dd72c7525cb7881d867</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Upload modules before OS upgrade\n\nIf the cluster contains any databases that use modules, you must upload module packages for the target OS version to a node in the existing cluster before you upgrade the cluster's operating system.\n\nSee [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os) for detailed upgrade instructions.\n\n#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### RedisGraph prevents upgrade to RHEL 9\n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Query results might include hash keys with lazily expired fields\n\nIf one or more fields of a hash key expire after an `FT.SEARCH` or `FT.AGGREGATE` query begins, Redis does not account for these lazily expired fields. As a result, keys with expired fields might still be included in the query results, leading to potentially incorrect or inconsistent results."},{"id":"security","title":"Security","role":"security","text":"Redis Software 7.8.6-263 updates pyOpenSSL to 26.0.0, which fixes a buffer overflow in the DTLS cookie-generation callback (CVE-2026-27459 / GHSA-5pwr-322w-8jr4).\n\n#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.8.6-263 supports open source Redis 7.4, 7.2, and 6.2. Below is the list of open source Redis CVEs and other security vulnerabilities fixed by version.\n\nRedis 7.4.x:\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\nRedis 7.2.x:\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-286","title":"Redis Software release notes 7.8.6-286 (July 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-286/","summary":"Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"58254f075015641944b1bee94cd8b37a8d95ac1dc6d0882dd7c51fef62e0a211","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for Redis Software version 7.8.6."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-database-versions-and-feature-sets","title":"Redis database versions and feature sets","role":"content","text":"Redis Software version 7.8.6 includes three Redis database versions: 7.4.0, 7.2.4, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 7.4.\n\nRedis Software comes packaged with several modules. As of version 7.8.2, Redis Software includes three feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)<br />[RedisGraph v2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes)<sup>[1](#module-note-1)</sup>  |\n\n1. <a name=\"module-note-1\"></a>RedisGraph end-of-life has been announced and will be removed in a future release. See the [RedisGraph end-of-life announcement](https://redis.io/blog/redisgraph-eol/) for more details."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.8.6-286 July release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">e6337895ea06e234528146d6d3daa03614ced29be4ea90385a88a554f6c656e8</span> |\n| Ubuntu 22 | <span class=\"break-all\">b58352585620abf6ab32702c4c4b63d436eea8b946646c37e511911618b3006a</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">1a817c6e46f14aa8eb4699c7273155c9884422d09d478579e7261e4e9b0dc9d4</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">9bc87714d847f83dbb07ef99f26d5e6fa454f6bfa5e8a40af5810a5913ba1313</span> |\n| Amazon Linux 2 | <span class=\"break-all\">42693137e739ba3fc754520b0a67bdb3eb89dbb744ff5abe2fb8682a31050ae1</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Upload modules before OS upgrade\n\nIf the cluster contains any databases that use modules, you must upload module packages for the target OS version to a node in the existing cluster before you upgrade the cluster's operating system.\n\nSee [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os) for detailed upgrade instructions.\n\n#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### RedisGraph prevents upgrade to RHEL 9\n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Query results might include hash keys with lazily expired fields\n\nIf one or more fields of a hash key expire after an `FT.SEARCH` or `FT.AGGREGATE` query begins, Redis does not account for these lazily expired fields. As a result, keys with expired fields might still be included in the query results, leading to potentially incorrect or inconsistent results."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.8.6-286 supports open source Redis 7.4, 7.2, and 6.2. Below is the list of open source Redis CVEs and other security vulnerabilities fixed by version.\n\n\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n\n\n\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\n\n\n\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\n\n\n\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-303","title":"Redis Software release notes 7.8.6-303 (August 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-303/","summary":"Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"cdc7e71124b80b2540b7154c55e82bac10b755166a6da83908d4c4617ef40437","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for Redis Software version 7.8.6.\n\nThis version offers internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-database-versions-and-feature-sets","title":"Redis database versions and feature sets","role":"content","text":"Redis Software version 7.8.6 includes three Redis database versions: 7.4.0, 7.2.4, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 7.4.\n\nRedis Software comes packaged with several modules. As of version 7.8.2, Redis Software includes three feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)<br />[RedisGraph v2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes)<sup>[1](#module-note-1)</sup>  |\n\n1. <a name=\"module-note-1\"></a>RedisGraph end-of-life has been announced and will be removed in a future release. See the [RedisGraph end-of-life announcement](https://redis.io/blog/redisgraph-eol/) for more details."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.8.6-303 August release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">6805de5b5959edfa82ce01240bc2930bd6b2c19047002d34d2743bc3e400e233</span> |\n| Ubuntu 22 | <span class=\"break-all\">1beb25c9df60a3424ff58eec218fb39965a7a3a57395bc27481ee49a1ac22f3f</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">d8ba54c2ec6cd92f017e5c3f8240212ab796589231f11c2d18cc48d48535c612</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">888a239048d940bfbce885331ba601a0f213c01e4fed4d376b8391265b5274e1</span> |\n| Amazon Linux 2 | <span class=\"break-all\">7c737a38896802cb75f7c70bb1684497ce90dd6378016c9527ab64b807487ff2</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Upload modules before OS upgrade\n\nIf the cluster contains any databases that use modules, you must upload module packages for the target OS version to a node in the existing cluster before you upgrade the cluster's operating system.\n\nSee [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os) for detailed upgrade instructions.\n\n#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### RedisGraph prevents upgrade to RHEL 9\n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Query results might include hash keys with lazily expired fields\n\nIf one or more fields of a hash key expire after an `FT.SEARCH` or `FT.AGGREGATE` query begins, Redis does not account for these lazily expired fields. As a result, keys with expired fields might still be included in the query results, leading to potentially incorrect or inconsistent results."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.8.6-303 supports open source Redis 7.4, 7.2, and 6.2. Below is the list of open source Redis CVEs and other security vulnerabilities fixed by version.\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\n\n\n\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-315","title":"Redis Software release notes 7.8.6-315 (September 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-315/","summary":"Bug fix for a memory leak in the Active-Active database syncer. Added scoping for shards and database endpoints. Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"185a507880ff0c2ac495d6c267daba5e61181c5b3291be5f762f4aee6a736ab2","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for Redis Software version 7.8.6."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Bug fix for a memory leak in the Active-Active database syncer\n\n- Scoping for shards and database endpoints\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"enhancements","title":"Enhancements","role":"content","text":"- Added scoping for shards and database endpoints."},{"id":"redis-database-versions-and-feature-sets","title":"Redis database versions and feature sets","role":"content","text":"Redis Software version 7.8.6 includes three Redis database versions: 7.4.0, 7.2.4, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 7.4.\n\nRedis Software comes packaged with several modules. As of version 7.8.2, Redis Software includes three feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)<br />[RedisGraph v2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes)<sup>[1](#module-note-1)</sup>  |\n\n1. <a name=\"module-note-1\"></a>RedisGraph end-of-life has been announced and will be removed in a future release. See the [RedisGraph end-of-life announcement](https://redis.io/blog/redisgraph-eol/) for more details."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS194243: Fixed an issue where the Active-Active (CRDB) syncer leaked memory when a participating cluster became unavailable and was not removed from the Active-Active database."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.8.6-315 September release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">5988a564e1b10441b24b1c031a99e7a5ba28bff053475fa640cb236f56bea267</span> |\n| Ubuntu 22 | <span class=\"break-all\">6828d54c78dd51ed8f343bb9a64615e44f8fa713b7c61e39b8d20e7e25a57eaa</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">3e92e82912e367adc7fa10b5c8a5f6addbfb73bd9e65798d6f4e2d029e755167</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">f67ed60a3066aae6316dac222ab2750a23d6c54c81422918d076b0a8864db894</span> |\n| Amazon Linux 2 | <span class=\"break-all\">4c52efb7bec16a108a1124c33b3e0aa46bff8000b38673be5a100c692e3121a4</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Upload modules before OS upgrade\n\nIf the cluster contains any databases that use modules, you must upload module packages for the target OS version to a node in the existing cluster before you upgrade the cluster's operating system.\n\nSee [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os) for detailed upgrade instructions.\n\n#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### RedisGraph prevents upgrade to RHEL 9\n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Query results might include hash keys with lazily expired fields\n\nIf one or more fields of a hash key expire after an `FT.SEARCH` or `FT.AGGREGATE` query begins, Redis does not account for these lazily expired fields. As a result, keys with expired fields might still be included in the query results, leading to potentially incorrect or inconsistent results."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.8.6-315 supports open source Redis 7.4, 7.2, and 6.2. Below is the list of open source Redis CVEs and other security vulnerabilities fixed by version.\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\n\n\n\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-331","title":"Redis Software release notes 7.8.6-331 (October 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-331/","summary":"Bug fixes for DMC proxy stability. Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-10-13T15:08:11-05:00","page_type":"content","content_hash":"e466a7ac26f6153e2651f9f8d22bc76e722379573ffec326129efd3fe891d5e2","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for Redis Software version 7.8.6."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Bug fixes for DMC proxy stability\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-database-versions-and-feature-sets","title":"Redis database versions and feature sets","role":"content","text":"Redis Software version 7.8.6 includes three Redis database versions: 7.4.0, 7.2.4, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 7.4.\n\nRedis Software comes packaged with several modules. As of version 7.8.2, Redis Software includes three feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)<br />[RedisGraph v2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes)<sup>[1](#module-note-1)</sup>  |\n\n1. <a name=\"module-note-1\"></a>RedisGraph end-of-life has been announced and will be removed in a future release. See the [RedisGraph end-of-life announcement](https://redis.io/blog/redisgraph-eol/) for more details."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS216045: Fixed an issue where the DMC proxy could crash repeatedly when a client sent a command without key arguments, such as `PUBSUB SHARDNUMSUB` with no channels, in a pipeline with another command.\n\n- RS215608: Fixed an issue where running `dmc-cli list bdb` or `dmc-cli list server` could corrupt memory and cause the DMC proxy to crash."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.8.6-331 October release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">00c14e181dfb67df806b8db18e06c941bfe39b72f473c515aaf32191352a7fef</span> |\n| Ubuntu 22 | <span class=\"break-all\">89e9cfe3463c9d666a794531cfe81f45194f1514efa9ae0bd6daff71d73be2b3</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">ef870a527105e5ce438dcde3b6c693ae4a87760397f0e5219eb4175c9523cc5e</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">2cf37cf6c903ca58788835b3c3b86f0afa19c8ed594d843a0c38988a00c0718f</span> |\n| Amazon Linux 2 | <span class=\"break-all\">33988df9efae0e8d74147db1ab7285a1db06dbef938ff2599c5c66095d255b44</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Upload modules before OS upgrade\n\nIf the cluster contains any databases that use modules, you must upload module packages for the target OS version to a node in the existing cluster before you upgrade the cluster's operating system.\n\nSee [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os) for detailed upgrade instructions.\n\n#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### RedisGraph prevents upgrade to RHEL 9\n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Query results might include hash keys with lazily expired fields\n\nIf one or more fields of a hash key expire after an `FT.SEARCH` or `FT.AGGREGATE` query begins, Redis does not account for these lazily expired fields. As a result, keys with expired fields might still be included in the query results, leading to potentially incorrect or inconsistent results."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.8.6-331 supports open source Redis 7.4, 7.2, and 6.2. Below is the list of open source Redis CVEs and other security vulnerabilities fixed by version.\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\n\n\n\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-36","title":"Redis Software release notes 7.8.6-36 (April 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-36/","summary":"Bug fixes, including a fix for an issue where new workers might use an outdated certificate after a certificate update.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"f089c6121bddd8ae811c810144c2df79d2fc64e10f029b99576ded28c08bbe71","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Software version 7.8.6](https://redis.io/downloads/#software)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Bug fixes, including a fix for an issue where new workers might use an outdated certificate after a certificate update."},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 7.8.6 includes three Redis database versions: 7.4.0, 7.2.4, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 7.4."},{"id":"redis-module-feature-sets","title":"Redis module feature sets","role":"content","text":"Redis Software comes packaged with several modules. As of version 7.8.2, Redis Software includes three feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)<br />[RedisGraph v2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes)<sup>[1](#module-note-1)</sup>  |\n\n1. <a name=\"module-note-1\"></a>RedisGraph end-of-life has been announced and will be removed in a future release. See the [RedisGraph end-of-life announcement](https://redis.io/blog/redisgraph-eol/) for more details."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS154601: Fixed an issue where new workers created after the proxy certificate was updated might use the old certificate.\n\n- RS151990: Fixed an issue where importing an RDB file in RHEL 8 and RHEL 9 clusters can fail with the exception: `Failed to import rdb file: general-error`."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.8.6-36 April release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">876dff68a00333f868a758a5abd23be509bcb1065c1a32c05260ace20ef72199</span> |\n| Ubuntu 22 | <span class=\"break-all\">180ea7d0ef9e8e313ff7d3f4f38b9f3049baa15cc6a62f929f524fe7403aac11</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">66138bd6d19f264b29d04718383542b0141eb5b9f6ffebbf1c5890a3dd51e078</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">869bd603cf8d4b6ac991a5c34c460c2c8f11af55a53508afbf621a67917c11f3</span> |\n| Amazon Linux 2 | <span class=\"break-all\">2622c195c80ee41c625f45f703cf2fa48202d3325330aba1d90645ad6f232b27</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Upload modules before OS upgrade\n\nIf the cluster contains any databases that use modules, you must upload module packages for the target OS version to a node in the existing cluster before you upgrade the cluster's operating system.\n\nSee [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os) for detailed upgrade instructions.\n\n#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### RedisGraph prevents upgrade to RHEL 9 \n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Query results might include hash keys with lazily expired fields\n\nIf one or more fields of a hash key expire after an `FT.SEARCH` or `FT.AGGREGATE` query begins, Redis does not account for these lazily expired fields. As a result, keys with expired fields might still be included in the query results, leading to potentially incorrect or inconsistent results."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.8.6-36 supports open source Redis 7.4, 7.2, and 6.2. Below is the list of open source Redis CVEs fixed by version.\n\nRedis 7.4.x:\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\nRedis 7.2.x:\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-60","title":"Redis Software release notes 7.8.6-60 (April 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-60/","summary":"Bug fixes for an LDAP connection issue caused by setting LDAP bind credentials that include `<` in the Cluster Manager UI and an issue where migrated shards can appear stuck on multiple nodes. Deprecated required_version in the bootstrap cluster API.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"2198a36b90851de9c7abf0e2afb16c9889ac0ae20c121dbf5d46eef8260374cf","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Software version 7.8.6](https://redis.io/downloads/#software)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Bug fixes for an LDAP connection issue caused by setting LDAP bind credentials that include `<` in the Cluster Manager UI and an issue where migrated shards can appear stuck on multiple nodes."},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 7.8.6 includes three Redis database versions: 7.4.0, 7.2.4, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 7.4."},{"id":"redis-module-feature-sets","title":"Redis module feature sets","role":"content","text":"Redis Software comes packaged with several modules. As of version 7.8.2, Redis Software includes three feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)<br />[RedisGraph v2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes)<sup>[1](#module-note-1)</sup>  |\n\n1. <a name=\"module-note-1\"></a>RedisGraph end-of-life has been announced and will be removed in a future release. See the [RedisGraph end-of-life announcement](https://redis.io/blog/redisgraph-eol/) for more details."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS155783: Fixed an issue where using the special character `<` when setting LDAP bind credentials in the Cluster Manager UI caused LDAP connection failures.\n\n- RS144215: Fixed an issue where a shard's status might not update correctly after migration, causing it to appear stuck on multiple nodes even if the migration completed successfully."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"deprecations","title":"Deprecations","role":"content","text":"#### API deprecations\n\n- Deprecated the `required_version` option for the bootstrap cluster API."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.8.6-60 April release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">490a54371f3f958991079bec5a350297d62c12f5820fceb821fe0caf7a33945e</span> |\n| Ubuntu 22 | <span class=\"break-all\">e28c4c59771078ce5cc16043e676f24b37428174bc927f69e44eb688e861ebd1</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">2315099f7c41a0113dd2e4de8914c264366018140f42645b874560ccc04c60c3</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">a76e61d02bc5b1d86afe0cd97939a762ce2e9f91d10b70f6f6dbe34efe4cc81d</span> |\n| Amazon Linux 2 | <span class=\"break-all\">6c8d2b3266ff99b4acef7632555e9cd44d7642f6d2cd703143c5ae7106677107</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Upload modules before OS upgrade\n\nIf the cluster contains any databases that use modules, you must upload module packages for the target OS version to a node in the existing cluster before you upgrade the cluster's operating system.\n\nSee [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os) for detailed upgrade instructions.\n\n#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### RedisGraph prevents upgrade to RHEL 9 \n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Query results might include hash keys with lazily expired fields\n\nIf one or more fields of a hash key expire after an `FT.SEARCH` or `FT.AGGREGATE` query begins, Redis does not account for these lazily expired fields. As a result, keys with expired fields might still be included in the query results, leading to potentially incorrect or inconsistent results."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.8.6-60 supports open source Redis 7.4, 7.2, and 6.2. Below is the list of open source Redis CVEs fixed by version.\n\nRedis 7.4.x:\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\nRedis 7.2.x:\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-95","title":"Redis Software release notes 7.8.6-95 (May 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-7-8-releases/rs-7-8-6-95/","summary":"Performance improvements for the Cluster Manager UI's nodes screen. Bug fix for slowness and intermittent failures in the Cluster Manager UI, `rladmin`, and `rlcheck` after upgrading large clusters to version 7.8.4. Bug fix for an issue that could cause inconsistent memory settings across shards and out-of-memory errors. Bug fix for an issue where certain processes might not terminate as expected during cluster upgrades due to timeouts.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"600dc43fd8efeea60eb3ce88530d052922f3b899863f03110d47a290763bd0bf","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Software version 7.8.6](https://redis.io/downloads/#software)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Performance improvements for the Cluster Manager UI's nodes screen\n\n- Bug fix for slowness and intermittent failures in the Cluster Manager UI, `rladmin`, and `rlcheck` after upgrading large clusters to version 7.8.4\n\n- Bug fix for an issue that could cause inconsistent memory settings across shards and out-of-memory errors\n\n- Bug fix for an issue where certain processes might not terminate as expected during cluster upgrades due to timeouts"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"enhancements","title":"Enhancements","role":"content","text":"- Improved performance of the Cluster Manager UI's nodes screen."},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 7.8.6 includes three Redis database versions: 7.4.0, 7.2.4, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 7.4."},{"id":"redis-module-feature-sets","title":"Redis module feature sets","role":"content","text":"Redis Software comes packaged with several modules. As of version 7.8.2, Redis Software includes three feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes)<br />[RedisGraph v2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisgraph/redisgraph-2.10-release-notes)<sup>[1](#module-note-1)</sup>  |\n\n1. <a name=\"module-note-1\"></a>RedisGraph end-of-life has been announced and will be removed in a future release. See the [RedisGraph end-of-life announcement](https://redis.io/blog/redisgraph-eol/) for more details."},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS160539: Fixed an issue that could cause slowness and intermittent failures in the Cluster Manager UI, `rladmin`, and `rlcheck` after upgrading large clusters to Redis Software version 7.8.4.\n\n- RS153478: Fixed an issue that could cause inconsistent memory settings across shards and out-of-memory errors.\n\n- RS160378: Fixed an issue where certain processes might not terminate as expected during cluster upgrades due to timeouts."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (7.8.6-95 May release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">20b03e80443e31806a32ee4281c4f28a9060c251ffc8b8b0e7797d62e0bed6f2</span> |\n| Ubuntu 22 | <span class=\"break-all\">bfad491f35f2e641bf1866bcbdda088911a9475b1af8add4d40362ba3e1cfecf</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">32891431ee94f8e21275319e6d854aeb919e6236e8faa1919fc41c069ba00c16</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">4b38e054a95112f552cae280d3e01d99e2ed59be3514c6bdd284cc690ac4d190</span> |\n| Amazon Linux 2 | <span class=\"break-all\">24a3522f4c71c8c006c1983e07cdf1c144113055916c86e3285a4347b5728b16</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Upload modules before OS upgrade\n\nIf the cluster contains any databases that use modules, you must upload module packages for the target OS version to a node in the existing cluster before you upgrade the cluster's operating system.\n\nSee [Upgrade a cluster's operating system](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-os) for detailed upgrade instructions.\n\n#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### RedisGraph prevents upgrade to RHEL 9 \n\nYou cannot upgrade from a prior RHEL version to RHEL 9 if the Redis Software cluster contains a RedisGraph module, even if unused by any database. The [RedisGraph module has reached End-of-Life](https://redis.com/blog/redisgraph-eol/) and is completely unavailable in RHEL 9.\n\n#### Query results might include hash keys with lazily expired fields\n\nIf one or more fields of a hash key expire after an `FT.SEARCH` or `FT.AGGREGATE` query begins, Redis does not account for these lazily expired fields. As a result, keys with expired fields might still be included in the query results, leading to potentially incorrect or inconsistent results."},{"id":"security","title":"Security","role":"security","text":"#### Open source Redis security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [open source Redis](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for open source Redis do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in open source Redis.\n\nRedis Software 7.8.6-95 supports open source Redis 7.4, 7.2, and 6.2. Below is the list of open source Redis CVEs fixed by version.\n\nRedis 7.4.x:\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\nRedis 7.2.x:\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-8-0-releases","title":"Redis Software release notes 8.0.x","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/","summary":"Redis Software 8! The most performant, most secure, and richest version so far. Built for performance, scale, and reliability to power modern ML and AI applications.","tags":["docs","operate","rs"],"last_updated":"2026-10-09T10:01:18-05:00","children":[{"id":"operate/rs/release-notes/rs-8-0-releases/rs-8-0-20-127","summary":"Bug fixes for authentication after just-in-time user provisioning, Google Cloud Storage backup configuration, DMC proxy stability, periodic latency spikes, and cluster scaling. Internal fixes and improvements.","title":"Redis Software release notes 8.0.20-127 (October 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-20-127/"},{"id":"operate/rs/release-notes/rs-8-0-releases/rs-8-0-20-108","summary":"Restored Active-Active database synchronization status indicators on the Cluster Manager overview page. Bug fixes for basic authentication when changing a user password through the REST API, database connectivity, and partial upgrades. Internal fixes and improvements.","title":"Redis Software release notes 8.0.20-108 (September 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-20-108/"},{"id":"operate/rs/release-notes/rs-8-0-releases/rs-8-0-20-96","summary":"Bug fixes for Active-Active database upgrades and synchronization, internode encryption certificates, upgrades on RHEL 9 with an earlier OpenSSL version, and metrics exporter startup. Internal fixes and improvements.","title":"Redis Software release notes 8.0.20-96 (August 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-20-96/"},{"id":"operate/rs/release-notes/rs-8-0-releases/rs-8-0-20-68","summary":"Bug fixes for `-MOVED` response metrics visibility, incomplete cleanup of stale Redis processes, internode encryption certificates, high CPU usage due to excessive heartbeat logging, and certificate-based authentication for LDAP blocking local user authentication. Internal fixes and improvements.","title":"Redis Software release notes 8.0.20-68 (July 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-20-68/"},{"id":"operate/rs/release-notes/rs-8-0-releases/rs-8-0-20-44","summary":"Enabled `client_eviction` by default to fix an issue where a massive volume of traffic directed at a single hot key could cause high latency and out-of-memory errors. Bug fixes for Active-Active databases, LDAP, and ACLs. Internal fixes and improvements.","title":"Redis Software release notes 8.0.20-44 (June 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-20-44/"},{"id":"operate/rs/release-notes/rs-8-0-releases/rs-8-0-20-19","summary":"Certificate-based authentication with LDAP group-based authorization. Amazon Linux 2023 support. API enhancements for Active-Active databases and identifying manually stopped database traffic. New search metrics.","title":"Redis Software release notes 8.0.20-19 (May 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-20-19/"},{"id":"operate/rs/release-notes/rs-8-0-releases/rs-8-0-18-36","summary":"Bug fix for a cluster upgrade issue. Internal fixes and improvements.","title":"Redis Software release notes 8.0.18-36 (May 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-18-36/"},{"id":"operate/rs/release-notes/rs-8-0-releases/rs-8-0-18-23","summary":"Redis 8.6 feature set and enhancements. Disk-based search indexing preview. Active-Active database upgrade API. JSON string reuse mechanism for Active-Active databases. Additional REST API enhancements.","title":"Redis Software release notes 8.0.18-23 (April 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-18-23/"},{"id":"operate/rs/release-notes/rs-8-0-releases/rs-8-0-16-33","summary":"Bug fix for LDAP authentication failures after cluster upgrades. Bug fix for Active-Active database configuration update failures due to missing module featureset version errors after database upgrades.","title":"Redis Software release notes 8.0.16-33 (March 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-16-33/"},{"id":"operate/rs/release-notes/rs-8-0-releases/rs-8-0-16-29","summary":"Consolidated health report. Global Active-Active configuration in the Cluster Manager UI.","title":"Redis Software release notes 8.0.16-29 (March 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-16-29/"},{"id":"operate/rs/release-notes/rs-8-0-releases/rs-8-0-10-81","summary":"License information in usage reports. Bug fix for high disk usage due to excessive flash logs.","title":"Redis Software release notes 8.0.10-81 (February 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-10-81/"},{"id":"operate/rs/release-notes/rs-8-0-releases/rs-8-0-10-76","summary":"Bug fix for database failover error after node crashes and recovery. `filter_node_wd_health_metrics` policy, enabled by default to filter new node watchdog health metrics. Internal fixes and improvements.","title":"Redis Software release notes 8.0.10-76 (February 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-10-76/"},{"id":"operate/rs/release-notes/rs-8-0-releases/rs-8-0-10-64","summary":"Redis 8.4 feature set and enhancements. ARM support for RHEL 9 and Ubuntu 22.04. Redis Flex operational optimizations during maintenance, scaling, full sync, and shard migration.","title":"Redis Software release notes 8.0.10-64 (February 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-10-64/"},{"id":"operate/rs/release-notes/rs-8-0-releases/rs-8-0-6-54","summary":"Single sign-on for the Cluster Manager UI. Error reports for Replica Of migration status. Automatically download and install user-defined modules during bootstrapping.","title":"Redis Software release notes 8.0.6-54 (December 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-6-54/"},{"id":"operate/rs/release-notes/rs-8-0-releases/rs-8-0-2-41","summary":"Bug fixes for cluster_wd configuration after restarts, internode encryption certificate storage and retrieval, database creation, Lua script migration issues, and database endpoint assignment after node removal.","title":"Redis Software release notes 8.0.2-41 (November 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-2-41/"},{"id":"operate/rs/release-notes/rs-8-0-releases/rs-8-0-2-17","summary":"Redis Software 8! The most performant, most secure, and richest version so far. Built for performance, scale, and reliability to power modern ML and AI applications.","title":"Redis Software release notes 8.0.2-17 (October 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-2-17/"}],"page_type":"content","content_hash":"7e4410667a61a3595dbeb8ccccb45df3f8e7a69b080c4d7360e073ab5a7bd982","sections":[{"id":"overview","title":"Overview","role":"overview","text":"​[​Redis Software version 8.0](https://redis.io/downloads/#Redis_Software) is now available!"},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Redis 8.0, 8.2, 8.4, and 8.6 feature set versions\n\n- Performance improvements and memory reduction\n\n- New vector set data structure\n\n- Redis Flex revamped engine\n\n- Redis Search upgrades\n\n- Lag-aware availability API\n\n- Metrics stream engine (General Availability)\n\n- Simplified module management\n\n- New REST API fields for database and cluster configuration\n\n- Customer-managed certificates for internode encryption\n\n- Smart client handoffs"},{"id":"detailed-release-notes","title":"Detailed release notes","role":"content","text":"For more detailed release notes, select a build version from the following table:\n\n\n| Version (Release date) | Major changes | Redis Open Source compatibility |\n| --- | --- | --- |\n| [8.0.20-127 (October 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-20-127/) | Bug fixes for authentication after just-in-time user provisioning, Google Cloud Storage backup configuration, DMC proxy stability, periodic latency spikes, and cluster scaling. Internal fixes and improvements. | Redis 8.6.2, 8.4.0, 8.2.1, 8.0.2, 7.4.3, 7.2.7, 6.2.13 |\n| [8.0.20-108 (September 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-20-108/) | Restored Active-Active database synchronization status indicators on the Cluster Manager overview page. Bug fixes for basic authentication when changing a user password through the REST API, database connectivity, and partial upgrades. Internal fixes and improvements. | Redis 8.6.2, 8.4.0, 8.2.1, 8.0.2, 7.4.3, 7.2.7, 6.2.13 |\n| [8.0.20-96 (August 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-20-96/) | Bug fixes for Active-Active database upgrades and synchronization, internode encryption certificates, upgrades on RHEL 9 with an earlier OpenSSL version, and metrics exporter startup. Internal fixes and improvements. | Redis 8.6.2, 8.4.0, 8.2.1, 8.0.2, 7.4.3, 7.2.7, 6.2.13 |\n| [8.0.20-68 (July 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-20-68/) | Bug fixes for `-MOVED` response metrics visibility, incomplete cleanup of stale Redis processes, internode encryption certificates, high CPU usage due to excessive heartbeat logging, and certificate-based authentication for LDAP blocking local user authentication. Internal fixes and improvements. | Redis 8.6.2, 8.4.0, 8.2.1, 8.0.2, 7.4.3, 7.2.7, 6.2.13 |\n| [8.0.20-44 (June 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-20-44/) | Enabled `client_eviction` by default to fix an issue where a massive volume of traffic directed at a single hot key could cause high latency and out-of-memory errors. Bug fixes for Active-Active databases, LDAP, and ACLs. Internal fixes and improvements. | Redis 8.6.2, 8.4.0, 8.2.1, 8.0.2, 7.4.3, 7.2.7, 6.2.13 |\n| [8.0.20-19 (May 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-20-19/) | Certificate-based authentication with LDAP group-based authorization. Amazon Linux 2023 support. API enhancements for Active-Active databases and identifying manually stopped database traffic. New search metrics. | Redis 8.6.2, 8.4.0, 8.2.1, 8.0.2, 7.4.3, 7.2.7, 6.2.13 |\n| [8.0.18-36 (May 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-18-36/) | Bug fix for a cluster upgrade issue. Internal fixes and improvements. | Redis 8.6.2, 8.4.0, 8.2.1, 8.0.2, 7.4.3, 7.2.7, 6.2.13 |\n| [8.0.18-23 (April 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-18-23/) | Redis 8.6 feature set and enhancements. Disk-based search indexing preview. Active-Active database upgrade API. JSON string reuse mechanism for Active-Active databases. Additional REST API enhancements. | Redis 8.6.2, 8.4.0, 8.2.1, 8.0.2, 7.4.3, 7.2.7, 6.2.13 |\n| [8.0.16-33 (March 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-16-33/) | Bug fix for LDAP authentication failures after cluster upgrades. Bug fix for Active-Active database configuration update failures due to missing module featureset version errors after database upgrades. | Redis 8.4.0, 8.2.1, 8.0.2, 7.4.3, 7.2.7, 6.2.13 |\n| [8.0.16-29 (March 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-16-29/) | Consolidated health report. Global Active-Active configuration in the Cluster Manager UI. | Redis 8.4.0, 8.2.1, 8.0.2, 7.4.3, 7.2.7, 6.2.13 |\n| [8.0.10-81 (February 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-10-81/) | License information in usage reports. Bug fix for high disk usage due to excessive flash logs. | Redis 8.4.0, 8.2.1, 8.0.2, 7.4.3, 7.2.7, 6.2.13 |\n| [8.0.10-76 (February 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-10-76/) | Bug fix for database failover error after node crashes and recovery. `filter_node_wd_health_metrics` policy, enabled by default to filter new node watchdog health metrics. Internal fixes and improvements. | Redis 8.4.0, 8.2.1, 8.0.2, 7.4.3, 7.2.7, 6.2.13 |\n| [8.0.10-64 (February 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-10-64/) | Redis 8.4 feature set and enhancements. ARM support for RHEL 9 and Ubuntu 22.04. Redis Flex operational optimizations during maintenance, scaling, full sync, and shard migration. | Redis 8.4.0, 8.2.1, 8.0.2, 7.4.3, 7.2.7, 6.2.13 |\n| [8.0.6-54 (December 2025)](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-6-54/) | Single sign-on for the Cluster Manager UI. Error reports for Replica Of migration status. Automatically download and install user-defined modules during bootstrapping. | Redis 8.2.1, 8.0.2, 7.4.3, 7.2.7, 6.2.13 |\n| [8.0.2-41 (November 2025)](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-2-41/) | Bug fixes for cluster_wd configuration after restarts, internode encryption certificate storage and retrieval, database creation, Lua script migration issues, and database endpoint assignment after node removal. | Redis 8.2.1, 8.0.2, 7.4.3, 7.2.7, 6.2.13 |\n| [8.0.2-17 (October 2025)](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-2-17/) | Redis Software 8! The most performant, most secure, and richest version so far. Built for performance, scale, and reliability to power modern ML and AI applications. | Redis 8.2.1, 8.0.2, 7.4.3, 7.2.7, 6.2.13 |"},{"id":"version-changes","title":"Version changes","role":"compatibility","text":"- [`POST /v1/cluster/actions/change_master`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/actions#post-cluster-action) REST API requests will no longer allow a node that exists but is not finished bootstrapping to become the primary node. Such requests will now return the status code `406 Not Acceptable`.\n\n- Node status now returns the actual provisional RAM and flash values even when the maximum number of shards on the node (`max_redis_servers`) is reached. Previously, the API returned 0 for `provisional_ram_of_node` and `provisional_flash_of_node` when a node reached its shard limit. This change affects REST API node status requests and the `rladmin status nodes` command's output.\n\n- `crdb_controller` is enabled by default as of Redis Software version 8.0.18."},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"- Upgrading to Redis Software version 8.0.10 through 8.0.16-29 can cause LDAP authentication to fail with \"certificate signed by unknown authority\" errors if your cluster currently uses LDAP authentication. This issue was fixed in [Redis Software version 8.0.16-33](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-16-33).\n\n- For Redis Software versions 8.0.2 through 8.0.10, LDAP filters for `user_dn_query` and `dn_group_query` strictly require parentheses to function correctly. Filters that previously worked without parentheses will no longer work after upgrading to these versions. For example, you must include the parentheses in `(sAMAccountName=%u)`. As of version 8.0.16, this breaking change no longer applies, and both `(sAMAccountName=%u)` and `sAMAccountName=%u` are valid filters.\n\n- Redis Software installation script changes:\n\n    - Changed the `--skip-updating-env-path` option to `--update-env-path` when running [`install.sh`](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/install-script).\n\n    - Changed the `skip_updating_env_path` parameter to `update_env_path` in the [installation answers file](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/manage-installation-questions#configure-file-to-answer)."},{"id":"redis-8-breaking-changes","title":"Redis database version 8 breaking changes","role":"content","text":"When new major versions of Redis Open Source change existing commands, upgrading your database to a new version can potentially break some functionality. Before you upgrade, read the provided list of breaking changes that affect Redis Software and update any applications that connect to your database to handle these changes.\n\n#### ACL behavior changes\n\nBefore Redis 8, the existing [ACL](https://redis.io/docs/latest/operate/rs/security/access-control/redis-acl-overview) categories `@read`, `@write`, `@dangerous`, `@admin`, `@slow`, and `@fast` did not include commands for Redis Search and the JSON, time series, and probabilistic data structures.\n\nStarting with Redis 8, Redis includes all Query Engine, JSON, time series, Bloom filter, cuckoo filter, top-k, count-min sketch, and t-digest commands in these existing ACL categories.\n\nAs a result:\n\n- Existing ACL rules such as `+@read +@write` will allow access to more commands than in previous versions of Redis. Here are some examples:\n\n  - A user with `+@read` access will be able to execute `FT.SEARCH`.\n\n  - A user with `+@write` access will be able to execute `JSON.SET`. \n\n- ACL rules such as `+@all -@write`  will allow access to fewer commands than previous versions of Redis.\n\n  - For example, a user with `+@all -@write` will not be able to execute `JSON.SET`.\n\n  - Explicit inclusion of new [command categories](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl#command-categories) is required to maintain access. The new categories are: `@search`, `@json`, `@timeseries`, `@bloom`, `@cuckoo`, `@topk`, `@cms`, and `@tdigest`.\n\n- ACL rules such as `+@read +JSON.GET` can now be simplified as `+@read` because `JSON.GET` is included in the `@read` category.\n\nNote that the `@all` category did not change, as it always included all the commands.\n\n#### Redis Search changes\n\nThe following changes affect behavior and validation in Redis Search:\n\n- Enforces validation for `LIMIT` arguments (offset must be 0 if limit is 0).\n\n- Enforces parsing rules for `FT.CURSOR READ` and `FT.ALIASADD`.\n\n- Parentheses are now required for exponentiation precedence in `APPLY` expressions.\n\n- Invalid input now returns errors instead of empty results.\n\n- Default values revisited for reducers like `AVG`, `COUNT`, `SUM`, `STDDEV`, `QUANTILE`, and others.\n\n- Updates to scoring (`BM25` is now the default instead of `TF-IDF`).\n\n- Improved handling of expired records, memory constraints, and malformed fields."},{"id":"openssl-version-on-rhel-9","title":"OpenSSL version on RHEL 9","role":"content","text":"On RHEL 9, Redis Software versions 8.0.16 through 8.0.20-68 require OpenSSL 3.3 or later. Version 8.0.20-96 and later are built against an earlier OpenSSL version, so the requirement no longer applies. Other supported platforms are unaffected.\n\nThe requirement exists because those versions are built against the OpenSSL 3.3 runtime included in recent RHEL 9 minor releases. Nodes on an earlier RHEL 9 minor release don't meet it. For example, RHEL 9.6 ships OpenSSL 3.2.2.\n\nThe pre-upgrade checks don't detect an incompatible OpenSSL version, so an upgrade can pass validation and then fail partway through. This can leave the management services on the node in a failed state.\n\nBefore you upgrade to a version between 8.0.16 and 8.0.20-68, check the OpenSSL version on each node:\n\n[code example]\n\nIf the version is earlier than 3.3.0, either upgrade the `openssl` and `openssl-libs` packages first, or upgrade to Redis Software version 8.0.20-96 or later instead."},{"id":"reserved-ports","title":"Reserved ports","role":"content","text":"Make sure the following ports are open before upgrading Redis Software.\n\nThe following port was added as a reserved port in Redis Software version 8.0.18; however, it is optional instead of reserved in Redis Software version 8.0.20-44:\n\n| Port | Process name | Usage |\n|------|--------------|-------|\n| 3357 | reconciliation_tree_grpc | Internal communication |\n\n| Port | Process name | Usage | \n|------|--------------|-------|\n| 3357 | reconciliation_tree_grpc | Internal communication |\n\nPorts reserved as of Redis Software version 7.22.0:\n\n| Port | Process name | Usage | \n|------|--------------|-------|\n| 3346 | cluster_api_internal | Cluster API internal port |\n| 3351 | cluster_watchdog_grpc_api | Cluster watchdog now supports gRPC |\n| 3352 | grpc_service_mesh | gRPC communication between nodes |\n| 3353 | local_grpc_service_mesh | Local gRPC services |\n| 3354 | grpc_gossip_envoy | gRPC gossip protocol communication between nodes |\n| 3355 | authentication_service | Authentication service internal port |\n\nPorts reserved as of Redis Software version 7.8.2:\n\n| Port | Process name | Usage | \n|------|--------------|-------|\n| 3347 | cert_exporter | Reports cluster certificate metrics |\n| 3348 | process_exporter | Reports process metrics for DMC and Redis processes |\n| 3349 | cluster_wd_exporter | Reports cluster watchdog metrics |\n| 3350 | db_controller | Internode communication |\n| 9091 | node_exporter | Reports host node metrics related to CPU, memory, disk, and more |\n| 9125 | statsd_exporter | Reports push metrics related to the DMC and syncer, and some cluster and node metrics |\n\nSee [Ports and port ranges used by Redis Software](https://redis.io/docs/latest/operate/rs/networking/port-configurations#ports-and-port-ranges-used-by-redis-software) for a complete list."},{"id":"deprecations","title":"Deprecations","role":"content","text":"#### API deprecations\n\n- Deprecated the `policy` field for [bootstrap](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bootstrap) REST API requests. Use [`PUT /v1/cluster/policy`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/policy#put-cluster-policy) to change cluster policies after cluster creation instead.\n\n- Deprecated the `module_args` field for [database](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs) REST API requests. Use the new module configuration objects `search`, `timeseries`, and `probabilistic` instead.\n\n- Deprecated `event_archive_cleanup_task_settings` for `job_scheduler` REST API requests.\n\n#### Redis Search deprecations\n\n- Deprecated commands: `FT.ADD`, `FT.SAFEADD`, `FT.DEL`, `FT.GET`, `FT.MGET`, `FT.SYNADD`, `FT.DROP`, `FT._DROPIFX`, and `FT.CONFIG`.\n\n- Deprecated `FT.SEARCH` options: `GEOFILTER`, `FILTER`, and `NOSTOPWORDS`.\n\n- Deprecated vector search options: `INITIAL_CAP` and `BLOCK_SIZE`.\n\n- Deprecated configuration parameters: `WORKER_THREADS`, `MT_MODE`, `PRIVILEGED_THREADS_NUM`, and `GCSCANSIZE`.\n\n- Deprecated dialects: `DIALECT 1`, `DIALECT 3`, and `DIALECT 4`.\n\n#### Internal monitoring and v1 Prometheus metrics deprecation\n\nThe existing [internal monitoring engine](https://redis.io/docs/latest/operate/rs/monitoring/v1_monitoring) is deprecated. We recommend transitioning to the new [metrics stream engine](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine) for improved performance, enhanced integration capabilities, and modernized metrics streaming.\n\nV1 Prometheus metrics are deprecated but still available. To transition to the new metrics stream engine, either migrate your existing dashboards using [this guide](https://redis.io/docs/latest/operate/rs/references/metrics/prometheus-metrics-v1-to-v2) or use [new preconfigured dashboards](https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise#grafana-dashboards-for-redis-software).\n\nAs part of the transition to the metrics stream engine, some internal cluster manager alerts were deprecated in favor of external monitoring solutions. See the [alerts transition plan](https://redis.io/docs/latest/operate/rs/references/alerts/alerts-v1-to-v2) for guidance."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS196225: After upgrading to Redis Software version 8.0.x, previously working LDAP filters that use an `OR` clause to match multiple attributes can fail to find a unique DN for some users.\n\n    This issue is fixed in Redis Software version 8.0.20-68 and later.\n\n- RS193156: Active Directory LDAP authentication can fail in the Cluster Manager UI after upgrading to Redis Software version 8.0.16-33 due to an issue with LDAP TLS client certificate handling. Users previously authenticated through Active Directory can no longer sign in to the Cluster Manager UI after the upgrade.\n\n    As a workaround, configure an LDAP client certificate using an [update cluster certificates](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/certificates) REST API request:\n\n    [code example]\n    \n    See [Create certificates](https://redis.io/docs/latest/operate/rs/security/certificates/create-certificates) and [Update certificates](https://redis.io/docs/latest/operate/rs/security/certificates/updating-certificates) for more detailed instructions.\n\n    This issue was fixed in Redis Software version 8.0.18.\n\n- RS180550: You cannot set up SSO when the Cluster Manager UI is exposed through an IPv6-based load balancer or gateway.\n\n    As a workaround, use an IPv4-based address for the SSO service base address, or register a DNS name that resolves to the IPv6 address.\n\n    This issue was fixed in Redis Software version 8.0.10.\n\n- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors. This issue was fixed in Redis Software version 8.0.6.\n\n- RS155734: Endpoint availability metrics do not work as expected due to a calculation error."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Redis Search query failures during rolling upgrades across the 8.4 version boundary\n\nRedis Search queries can fail during a rolling upgrade when a cluster contains shards running Redis versions earlier than 8.4 and shards running Redis version 8.4 or later, due to an internal protocol change introduced in version 8.4. This issue affects only clusters where `parallel_shards_upgrade` has been changed from its default value of `0`. If both conditions apply, expect Redis Search downtime until all nodes are upgraded.\n\n#### Trim ACKED not supported for Active-Active 8.4 databases\n\nFor Active-Active databases running Redis database version 8.4, the `ACKED` option is not supported for trimming commands.\n\n#### Rolling upgrade limitation for clusters with custom or deprecated modules\n\nDue to module handling changes introduced in Redis Software version 8.0, upgrading a cluster that contains custom or deprecated modules, such as RedisGraph and RedisGears v2, can become stuck when adding a new node to the cluster during a rolling upgrade.\n\n#### Module commands limitation during Active-Active database upgrades to Redis 8.0\n\nWhen upgrading an Active-Active database to Redis version 8.0, you cannot use module commands until all Active-Active database instances have been upgraded. Currently, these commands are not blocked automatically.\n\n#### Redis 8.0 database cannot be created with flash\n\nYou cannot create a Redis 8.0 database with flash storage enabled. Create a Redis 8.0 database with RAM-only storage instead, or use Redis 8.2 for flash-enabled (Redis Flex) databases.\n\n#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### SCAN results can be inconsistent during a rolling upgrade in OSS Cluster API mode\n\nDuring a rolling upgrade with Smart client handoffs (SCH) enabled, when some slot's shards have migrated to a different node but are still reachable from their original node, results of the `SCAN` command might be inconsistent: it might show some of the keys, it might show keys that are not reachable from that node, or it might show no keys. When the slot migration is done, this issue will fix itself."}],"examples":[{"id":"openssl-version-on-rhel-9-ex0","language":"sh","code":"openssl version","section_id":"openssl-version-on-rhel-9"},{"id":"known-issues-ex0","language":"sh","code":"PUT https://<host>:<port>/v1/cluster/certificates\n    {\n      \"certificates\": [\n        {\n          \"name\": \"ldap_client\",\n          \"certificate\": \"<cert>\",\n          \"key\": \"<key>\"\n        }\n      ]\n    }","section_id":"known-issues"}]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-8-0-releases/rs-8-0-10-64","title":"Redis Software release notes 8.0.10-64 (February 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-10-64/","summary":"Redis 8.4 feature set and enhancements. ARM support for RHEL 9 and Ubuntu 22.04. Redis Flex operational optimizations during maintenance, scaling, full sync, and shard migration.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"004f34aa3677cd82a9b0873a044eae5d21c919d0f8c7fbaba38092ed6d688a0a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"​[​Redis Software version 8.0.10](https://redis.io/downloads/#Redis_Software) is now available! This release includes API enhancements that warranted a new minor version instead of a maintenance release for version 8.0.6. However, you can upgrade from 8.0.2 or 8.0.6 to 8.0.10 without issue."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Redis 8.4 feature set and enhancements\n\n- ARM support for RHEL 9 and Ubuntu 22.04\n\n- Redis Flex operational optimizations during maintenance, scaling, full sync, and shard migration"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"new-features","title":"New features","role":"content","text":"#### Redis 8.4 feature set and enhancements\n\nThe Redis 8.4 feature set is now available when you [create](https://redis.io/docs/latest/operate/rs/databases/create) or [upgrade](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-database) a database with database version 8.4.\n\nRedis 8.4 builds on the foundation of Redis 8.2 with significant enhancements to cluster operations, string manipulation, and stream processing capabilities.\n\nThis release delivers major improvements across multiple areas:\n\n- Enhanced string operations with atomic compare-and-set functionality\n\n- Advanced stream processing with idle entry claiming\n\n- Hybrid search capabilities combining multiple ranking algorithms\n\n- High-performance SIMD optimizations for bit operations and vector processing\n\n- Improved JSON handling with better memory efficiency\n\n- See [What's new in Redis 8.4](https://redis.io/docs/latest/develop/whats-new/8-4) and [Redis Open Source 8.4 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.4-release-notes) for more details.\n\n#### Redis Flex operational optimizations \n\nRedis Flex databases running on Redis 8.4 have major operational optimizations:\n\n- Scaling is 2x faster, cutting the duration of scaling in half.\n\n- A new internal replication mechanism:\n\n    - Enables 10x faster full sync operations, where an entire shard is replicated across nodes. Operations such as maintenance, shard migration, recovery from node failure, and upgrades are now up to 10x faster than before.\n\n    - Enables shorter copy-on-write (CoW), which lowers peak memory usage, reduces latency jitter, and improves write throughput during persistence.\n\n#### ARM support for RHEL 9 and Ubuntu 22.04\n\nRedis Software now supports ARM architecture on RHEL 9 and Ubuntu 22.04, enabling deployments on cost-efficient, high-performance ARM-based servers."},{"id":"enhancements","title":"Enhancements","role":"content","text":"- Added support for database name labels in v2 metrics when the `metrics_exporter_expose_bdb_name` cluster policy setting is enabled:\n\n    [code example]\n\n- Added new v2 metrics for monitoring the health of shards, the DMC, and endpoints:\n\n    - shard_ping_failures \n\n    - shard_ping_failure_duration_seconds \n\n    - dmc_ping_failures\n\n    - dmc_ping_failure_duration_seconds\n\n    - endpoint_ping_failures\n\n    - endpoint_ping_failure_duration_seconds"},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 8.0.10 includes the following Redis database versions: 8.4.0, 8.2.1, 8.0.2, 7.4.3, 7.2.7, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 8.4."},{"id":"redis-feature-sets","title":"Redis feature sets","role":"content","text":"Redis Software includes multiple feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 8.4 | RediSearch 8.4<br />RedisJSON 8.4<br />RedisTimeSeries 8.4<br />RedisBloom 8.4<br />See [What's new in Redis 8.4](https://redis.io/docs/latest/develop/whats-new/8-4) and [Redis Open Source 8.4 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.4-release-notes) |\n| 8.2 | RediSearch 8.2<br />RedisJSON 8.2<br />RedisTimeSeries 8.2<br />RedisBloom 8.2<br />See [What's new in Redis 8.2](https://redis.io/docs/latest/develop/whats-new/8-2) and [Redis Open Source 8.2 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.2-release-notes) |\n| 8.0 | RediSearch 8.0<br />RedisJSON 8.0<br />RedisTimeSeries 8.0<br />RedisBloom 8.0<br />See [What's new in Redis 8.0](https://redis.io/docs/latest/develop/whats-new/8-0) and [Redis Open Source 8.0 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.0-release-notes) |\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes) |"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS124443: Fixed an error that could occur during fresh installations when attempting to set file permissions before the `redislabs` user and group were created.\n\n- RS147692: Fixed an issue with persistence configuration and storage path handling where persistence could fail after temporary storage disruptions.\n\n- RS160493: Improved reliability of failover operations by enhancing retry mechanisms and connection handling.\n\n- RS161545: Fixed an issue in two-node clusters with witness disk where a freeze of the primary node could lead to a split-brain scenario where both nodes believe they are the primary node, leading to cluster instability.\n\n- RS162828: Fixed an issue where database authentication could fail after ACL changes due to outdated access control configurations.\n\n- RS165065: Fixed an issue with configuration updates on promoted shards after failover if the target node was temporarily unresponsive.\n\n- RS169857: Fixed an issue where `dmcproxy` could get stuck in restart loops after VM freeze events and repeatedly run unnecessary detailed connection reports.\n\n- RS170790: Fixed an issue where append-only files could become corrupted if the persistent disk ran out of space during write operations, causing partial or missing data at the end of the file.\n\n- RS172012: Fixed an issue with the handling of audit logging connections that could cause high CPU utilization by `dmcproxy`.\n\n- RS176174: Improved reliability of database upgrades by adjusting shutdown timeouts and restart handling for failed shards.\n\n- RS177036: Fixed an issue where importing RDB files from Azure Blob Storage could fail if the files were stored in subdirectories.\n\n- RS179774: Fixed an issue where installation could fail on systems with firewall enabled due to missing reserved ports in firewalld configuration.\n\n- RS179834: Fixed an issue where client connections could become stuck when using sharded pub/sub commands with the OSS cluster API.\n\n- RS177380: Fixed authorization errors that could occur due to fetching log paths before a user signed in to the Cluster Manager UI.\n\n- RS142855: Enhanced Active-Active database creation API to support mTLS configuration during database creation, allowing client certificate authentication to be configured without requiring changes after creation.\n\n- RS171991: Improved node bootstrapping reliability during cluster patching by increasing retry attempts for connection to the local CCS.\n\n- RS177389: Fixed an issue where database recovery could fail with a retryable error when given an empty recovery plan.\n\n- RS167864: Fixed an issue with the shards API that could cause temporary inconsistencies with shards' reported roles after database upgrades.\n\n- RS180550: Fixed an issue that prevented setting up SSO when the Cluster Manager UI was exposed through an IPv6-based load balancer or gateway.\n\n- RS176474: Fixed an issue where clusters could fail after uploading internal certificates that lacked the \"TLS Web Client Authentication\" option. The system now validates and rejects certificates without this required option during upload to prevent cluster failures.\n\n- RS182423: Fixed an issue where database availability REST API requests to secondary nodes returned 307 Temporary Redirect status codes instead of the expected availability status.\n\n- RS181962: Fixed an issue where database upgrade operations could become stuck after cluster upgrade."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (8.0.10-64 February release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">afbae370af72b09502d96d3a518e04f96abb41f2f59447f4f1463182d52b0acb</span> |\n| Ubuntu 22 (amd64) | <span class=\"break-all\">06b31440513f4ea876a9d57782d5b73657635ff5150920f74f0e39e229084ee8</span> |\n| Ubuntu 22 (arm64) | <span class=\"break-all\">7eeb22f83a4ea7a4c11cbd064b11e5fd0dd245c84905d37c8f4b9c573c697c81</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">025aa8994d14e7e87726f238095a063556d75ad1657591f585c6596a291a11af</span> |\n| Red Hat Enterprise Linux (RHEL) 9 (amd64) | <span class=\"break-all\">e7c32b8e0aec3c20eaeb11384954c0533c6d30079f70882d7afc2bba402eba07</span> |\n| Red Hat Enterprise Linux (RHEL) 9 (arm64) | <span class=\"break-all\">1870626f0e280acf3165c8992648ee1b35ed0a8bc4ad5b861c4b82bd26799684</span> |\n| Amazon Linux 2 | <span class=\"break-all\">db7362f554def31cd071bc14743910a8893ca7ae91b689e4c94331dc2f7c7732</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS155734: Endpoint availability metrics do not work as expected due to a calculation error."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Redis Search query failures during rolling upgrades across the 8.4 version boundary\n\nRedis Search queries can fail during a rolling upgrade when a cluster contains shards running Redis versions earlier than 8.4 and shards running Redis version 8.4 or later, due to an internal protocol change introduced in version 8.4. This issue affects only clusters where `parallel_shards_upgrade` has been changed from its default value of `0`. If both conditions apply, expect Redis Search downtime until all nodes are upgraded.\n\n#### Trim ACKED not supported for Active-Active 8.4 databases\n\nFor Active-Active databases running Redis database version 8.4, the `ACKED` option is not supported for trimming commands.\n\n#### Rolling upgrade limitation for clusters with custom or deprecated modules\n\nDue to module handling changes introduced in Redis Software version 8.0, upgrading a cluster that contains custom or deprecated modules, such as RedisGraph and RedisGears v2, can become stuck when adding a new node to the cluster during a rolling upgrade.\n\n#### Module commands limitation during Active-Active database upgrades to Redis 8.0\n\nWhen upgrading an Active-Active database to Redis version 8.0, you cannot use module commands until all Active-Active database instances have been upgraded. Currently, these commands are not blocked automatically.\n\n#### Redis 8.0 database cannot be created with flash\n\nYou cannot create a Redis 8.0 database with flash storage enabled. Create a Redis 8.0 database with RAM-only storage instead, or use Redis 8.2 for flash-enabled (Redis Flex) databases.\n\n#### Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log."},{"id":"security","title":"Security","role":"security","text":"#### Redis Open Source security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [Redis Open Source](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for Redis Open Source do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in Redis Open Source.\n\nRedis Software 8.0.10-64 supports Redis Open Source 8.4, 8.2, 8.0, 7.4, 7.2, and 6.2. Below is the list of Redis Open Source CVEs and other security vulnerabilities fixed by version.\n\nRedis 8.4.x:\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\nRedis 8.2.x:\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- (CVE-2025-62507) A user can run the `XACKDEL` command with multiple IDs and trigger a stack buffer overflow, which can potentially lead to remote code execution.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\nRedis 8.0.x:\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\nRedis 7.4.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\nRedis 7.2.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[{"id":"enhancements-ex0","language":"sh","code":"PUT /v1/cluster/policy\n    { \"metrics_exporter_expose_bdb_name\": true }","section_id":"enhancements"}]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-8-0-releases/rs-8-0-10-76","title":"Redis Software release notes 8.0.10-76 (February 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-10-76/","summary":"Bug fix for database failover error after node crashes and recovery. `filter_node_wd_health_metrics` policy, enabled by default to filter new node watchdog health metrics. Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"f15b26776c72bdee82c968476739be7dd78fa62809ebcd7b1e5d98ef6925e13f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Software version 8.0.10](https://redis.io/downloads/#Redis_Software)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Bug fix for database failover error after node crashes and recovery\n\n- `filter_node_wd_health_metrics` policy, enabled by default to filter new node watchdog health metrics\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 8.0.10 includes the following Redis database versions: 8.4.0, 8.2.1, 8.0.2, 7.4.3, 7.2.7, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 8.4."},{"id":"redis-feature-sets","title":"Redis feature sets","role":"content","text":"Redis Software includes multiple feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 8.4 | RediSearch 8.4<br />RedisJSON 8.4<br />RedisTimeSeries 8.4<br />RedisBloom 8.4<br />See [What's new in Redis 8.4](https://redis.io/docs/latest/develop/whats-new/8-4) and [Redis Open Source 8.4 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.4-release-notes) |\n| 8.2 | RediSearch 8.2<br />RedisJSON 8.2<br />RedisTimeSeries 8.2<br />RedisBloom 8.2<br />See [What's new in Redis 8.2](https://redis.io/docs/latest/develop/whats-new/8-2) and [Redis Open Source 8.2 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.2-release-notes) |\n| 8.0 | RediSearch 8.0<br />RedisJSON 8.0<br />RedisTimeSeries 8.0<br />RedisBloom 8.0<br />See [What's new in Redis 8.0](https://redis.io/docs/latest/develop/whats-new/8-0) and [Redis Open Source 8.0 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.0-release-notes) |\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes) |"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS180524: Fixed an issue where database failover operations could enter a final error state after node crashes and recovery.\n\n- RS186040: Added a `filter_node_wd_health_metrics` policy and enabled it by default to filter new node watchdog health metrics."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"- Upgrading to this Redis Software version can cause LDAP authentication to fail with \"certificate signed by unknown authority\" errors if your cluster currently uses LDAP authentication. This issue will be fixed in a future maintenance release."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (8.0.10-76 February release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">11e1f537688f701d1c81f1932bea14f1f1613ff7e95539fb413f918d35e17685</span> |\n| Ubuntu 22 (amd64) | <span class=\"break-all\">3f416174d406153a0b3f03ab5bc02a4353aa3f2fd0550d9923518b48c8065ab0</span> |\n| Ubuntu 22 (arm64) | <span class=\"break-all\">d58de57044463eadbf716945dcf56a71c4f0c6a63dd9a9cbc69f5729ad1bf03a</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">fecf9476f6a86130c3fdedaf2f8c4905e80f98f5ecb43c3a7ad2c7322730b5a9</span> |\n| Red Hat Enterprise Linux (RHEL) 9 (amd64) | <span class=\"break-all\">23c4aeb236237710de3aae5da15792022e260c53f381d9f5920c2a2f6bbbc18e</span> |\n| Red Hat Enterprise Linux (RHEL) 9 (arm64) | <span class=\"break-all\">0c4c629f02c36cb222a732f141a15f3b0e7b634abc8d4839b7df4bdb1d1953e8</span> |\n| Amazon Linux 2 | <span class=\"break-all\">90835d32ff0f982ddee9ff53b4794d984711afc4c01f60521adfe3c23cd0dbbf</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS155734: Endpoint availability metrics do not work as expected due to a calculation error."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Redis Search query failures during rolling upgrades across the 8.4 version boundary\n\nRedis Search queries can fail during a rolling upgrade when a cluster contains shards running Redis versions earlier than 8.4 and shards running Redis version 8.4 or later, due to an internal protocol change introduced in version 8.4. This issue affects only clusters where `parallel_shards_upgrade` has been changed from its default value of `0`. If both conditions apply, expect Redis Search downtime until all nodes are upgraded.\n\n#### Trim ACKED not supported for Active-Active 8.4 databases\n\nFor Active-Active databases running Redis database version 8.4, the `ACKED` option is not supported for trimming commands.\n\n#### Rolling upgrade limitation for clusters with custom or deprecated modules\n\nDue to module handling changes introduced in Redis Software version 8.0, upgrading a cluster that contains custom or deprecated modules, such as RedisGraph and RedisGears v2, can become stuck when adding a new node to the cluster during a rolling upgrade.\n\n#### Module commands limitation during Active-Active database upgrades to Redis 8.0\n\nWhen upgrading an Active-Active database to Redis version 8.0, you cannot use module commands until all Active-Active database instances have been upgraded. Currently, these commands are not blocked automatically.\n\n#### Redis 8.0 database cannot be created with flash\n\nYou cannot create a Redis 8.0 database with flash storage enabled. Create a Redis 8.0 database with RAM-only storage instead, or use Redis 8.2 for flash-enabled (Redis Flex) databases.\n\n#### Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log."},{"id":"security","title":"Security","role":"security","text":"#### Redis Open Source security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [Redis Open Source](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for Redis Open Source do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in Redis Open Source.\n\nRedis Software 8.0.10-76 supports Redis Open Source 8.4, 8.2, 8.0, 7.4, 7.2, and 6.2. Below is the list of Redis Open Source CVEs and other security vulnerabilities fixed by version.\n\nRedis 8.4.x:\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\nRedis 8.2.x:\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- (CVE-2025-62507) A user can run the `XACKDEL` command with multiple IDs and trigger a stack buffer overflow, which can potentially lead to remote code execution.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\nRedis 8.0.x:\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\nRedis 7.4.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\nRedis 7.2.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-8-0-releases/rs-8-0-10-81","title":"Redis Software release notes 8.0.10-81 (February 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-10-81/","summary":"License information in usage reports. Bug fix for high disk usage due to excessive flash logs.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"ed10b6fcf65c8e14a0e125b4908c2ba9420c0a5ab712c2965d6a32df95c29de7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Software version 8.0.10](https://redis.io/downloads/#Redis_Software)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- License information in usage reports\n\n- Bug fix for high disk usage due to excessive flash logs"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"enhancements","title":"Enhancements","role":"content","text":"- Added license information to [usage reports](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/usage_report), including activation date, expiration date, shard limits, and shard usage."},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 8.0.10 includes the following Redis database versions: 8.4.0, 8.2.1, 8.0.2, 7.4.3, 7.2.7, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 8.4."},{"id":"redis-feature-sets","title":"Redis feature sets","role":"content","text":"Redis Software includes multiple feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 8.4 | RediSearch 8.4<br />RedisJSON 8.4<br />RedisTimeSeries 8.4<br />RedisBloom 8.4<br />See [What's new in Redis 8.4](https://redis.io/docs/latest/develop/whats-new/8-4) and [Redis Open Source 8.4 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.4-release-notes) |\n| 8.2 | RediSearch 8.2<br />RedisJSON 8.2<br />RedisTimeSeries 8.2<br />RedisBloom 8.2<br />See [What's new in Redis 8.2](https://redis.io/docs/latest/develop/whats-new/8-2) and [Redis Open Source 8.2 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.2-release-notes) |\n| 8.0 | RediSearch 8.0<br />RedisJSON 8.0<br />RedisTimeSeries 8.0<br />RedisBloom 8.0<br />See [What's new in Redis 8.0](https://redis.io/docs/latest/develop/whats-new/8-0) and [Redis Open Source 8.0 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.0-release-notes) |\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes) |"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS182731: Fixed an issue with log truncation that could cause high disk usage due to excessive flash logs."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"- Upgrading to this Redis Software version can cause LDAP authentication to fail with \"certificate signed by unknown authority\" errors if your cluster currently uses LDAP authentication. This issue will be fixed in a future maintenance release."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (8.0.10-81 February release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">a84c0789e9f63c3569c6bb0f5626ec4f96aeff97311671431aa6365eed04b161</span> |\n| Ubuntu 22 (amd64) | <span class=\"break-all\">ea82563bcab66f47d0165adc2dbcb52be6ca995f8ac67cf96e218075e6f3ae31</span> |\n| Ubuntu 22 (arm64) | <span class=\"break-all\">309d45ca2962d23e864725f85039c35367a8636761637dba1a04cfe123663f13</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">4e7b5f9c3a456740f519c08df2a69aed72b4c1cb47247ca6580768072426d0f4</span> |\n| Red Hat Enterprise Linux (RHEL) 9 (amd64) | <span class=\"break-all\">ac45a8cb7cd3c4db22d3c15394b8702ffb1df431e6b91c0030f705eeff62a546</span> |\n| Red Hat Enterprise Linux (RHEL) 9 (arm64) | <span class=\"break-all\">0282e4a2f9f43ac53d9c6571a733030c847a49aba65c608c906d103040258644</span> |\n| Amazon Linux 2 | <span class=\"break-all\">b64078c315b48b6854b841c7574060792ecc7ba6c9bbb42606d0045be54233a2</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS196225: After upgrading to Redis Software version 8.0.x, previously working LDAP filters that use an `OR` clause to match multiple attributes can fail to find a unique DN for some users.\n\n- RS155734: Endpoint availability metrics do not work as expected due to a calculation error."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Redis Search query failures during rolling upgrades across the 8.4 version boundary\n\nRedis Search queries can fail during a rolling upgrade when a cluster contains shards running Redis versions earlier than 8.4 and shards running Redis version 8.4 or later, due to an internal protocol change introduced in version 8.4. This issue affects only clusters where `parallel_shards_upgrade` has been changed from its default value of `0`. If both conditions apply, expect Redis Search downtime until all nodes are upgraded.\n\n#### Trim ACKED not supported for Active-Active 8.4 databases\n\nFor Active-Active databases running Redis database version 8.4, the `ACKED` option is not supported for trimming commands.\n\n#### Rolling upgrade limitation for clusters with custom or deprecated modules\n\nDue to module handling changes introduced in Redis Software version 8.0, upgrading a cluster that contains custom or deprecated modules, such as RedisGraph and RedisGears v2, can become stuck when adding a new node to the cluster during a rolling upgrade.\n\n#### Module commands limitation during Active-Active database upgrades to Redis 8.0\n\nWhen upgrading an Active-Active database to Redis version 8.0, you cannot use module commands until all Active-Active database instances have been upgraded. Currently, these commands are not blocked automatically.\n\n#### Redis 8.0 database cannot be created with flash\n\nYou cannot create a Redis 8.0 database with flash storage enabled. Create a Redis 8.0 database with RAM-only storage instead, or use Redis 8.2 for flash-enabled (Redis Flex) databases.\n\n#### Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log."},{"id":"security","title":"Security","role":"security","text":"#### Redis Open Source security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [Redis Open Source](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for Redis Open Source do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in Redis Open Source.\n\nRedis Software 8.0.10-81 supports Redis Open Source 8.4, 8.2, 8.0, 7.4, 7.2, and 6.2. Below is the list of Redis Open Source CVEs and other security vulnerabilities fixed by version.\n\nRedis 8.4.x:\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\nRedis 8.2.x:\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- (CVE-2025-62507) A user can run the `XACKDEL` command with multiple IDs and trigger a stack buffer overflow, which can potentially lead to remote code execution.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\nRedis 8.0.x:\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\nRedis 7.4.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\nRedis 7.2.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-8-0-releases/rs-8-0-16-29","title":"Redis Software release notes 8.0.16-29 (March 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-16-29/","summary":"Consolidated health report. Global Active-Active configuration in the Cluster Manager UI.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"434d2809016d0aa342437372376de034bf4383f246b0258c9d606df7a6e99405","sections":[{"id":"overview","title":"Overview","role":"overview","text":"​[​Redis Software version 8.0.16](https://redis.io/downloads/#Redis_Software) is now available! This release includes API enhancements that warranted a new minor version instead of a maintenance release for version 8.0.10. However, you can upgrade from 8.0.2, 8.0.6, or 8.0.10 to 8.0.16 without issue."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Consolidated health report\n\n- Global Active-Active configuration in the Cluster Manager UI"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"new-features","title":"New features","role":"content","text":"#### Consolidated health report\n\nRedis Software's new health report is a read-only, consolidated snapshot of a cluster's current system health, including:\n\n  - License and certificate status\n\n  - Nodes and services health\n  \n  - Node memory breakdown, including free and provisioned memory\n  \n  - Running actions status\n  \n  - Active alerts\n  \n  - Basic database status and version\n  \n  - High availability health\n\nThe new health report consolidates cluster and database health information that was previously scattered across multiple APIs and CLI commands, such as [`rladmin status extra all`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status) and required SSH access to view.\n\nNow, you can access the cluster and database health reports using the **Overview** screen in the Cluster manager UI or the new health API paths:\n\n- [Cluster health report](https://redis.io/docs/latest/operate/rs/references/rest-api/api-reference#tag/Cluster/operation/cluster_cluster_health_report):\n\n    [code example]\n\n- [Database health report](https://redis.io/docs/latest/operate/rs/references/rest-api/api-reference#tag/Database/operation/cluster_database_health_report):\n\n    [code example]\n\nFor more information, see [Consolidated health report](https://redis.io/docs/latest/operate/rs/monitoring/health-report).\n\n#### Global Active-Active configuration in the Cluster Manager UI\n\nThe Cluster Manager UI now supports global configuration updates for Active-Active databases, bringing UI behavior in line with the CRDB API. \n\nPreviously, when modifying Active-Active databases using the CRDB API, configuration changes were applied globally by default. However, when performing the same actions in the Cluster Manager UI, modifications applied only to the local instance, requiring additional manual updates for each instance in participating clusters.\n\nGlobal configuration is now the default in the Cluster Manager UI. You can still make local configuration changes when needed, which will override global settings on the database instances where they are applied.\n\n> [!WARNING]\n> Applying global configuration changes will override any local settings created using the API or earlier Cluster Manager UI versions. To preserve existing local changes, manage them in the **Local** configuration tab before applying global changes.\n\nFor more information, see [Manage Active-Active databases](https://redis.io/docs/latest/operate/rs/databases/active-active/manage#database-settings)."},{"id":"enhancements","title":"Enhancements","role":"content","text":"- Added the ability to undo configuration overrides for an Active-Active database instance and reset it to the default database configuration using a REST API request to [update an Active-Active database](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/crdbs#patch-crdbs). For example:\n\n    [code example]\n\n- For [smart client handoffs](https://redis.io/docs/latest/operate/rs/clusters/configure/sch):\n\n    - Added smart client handoff support for databases with OSS cluster API enabled.\n\n    - The delay between notifying clients and performing an operation is now configurable using the new `state_machine_smart_client_handoffs_delay_ms` cluster setting in the REST API."},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 8.0.16 includes the following Redis database versions: 8.4.0, 8.2.1, 8.0.2, 7.4.3, 7.2.7, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 8.4."},{"id":"redis-feature-sets","title":"Redis feature sets","role":"content","text":"Redis Software includes multiple feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 8.4 | RediSearch 8.4<br />RedisJSON 8.4<br />RedisTimeSeries 8.4<br />RedisBloom 8.4<br />See [What's new in Redis 8.4](https://redis.io/docs/latest/develop/whats-new/8-4) and [Redis Open Source 8.4 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.4-release-notes) |\n| 8.2 | RediSearch 8.2<br />RedisJSON 8.2<br />RedisTimeSeries 8.2<br />RedisBloom 8.2<br />See [What's new in Redis 8.2](https://redis.io/docs/latest/develop/whats-new/8-2) and [Redis Open Source 8.2 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.2-release-notes) |\n| 8.0 | RediSearch 8.0<br />RedisJSON 8.0<br />RedisTimeSeries 8.0<br />RedisBloom 8.0<br />See [What's new in Redis 8.0](https://redis.io/docs/latest/develop/whats-new/8-0) and [Redis Open Source 8.0 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.0-release-notes) |\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes) |"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS168233: Fixed an issue where Active-Active database instance removal could fail with authentication errors if the default database password was enabled during database creation and deactivated after creation.\n\n- RS150707: Fixed an issue where endpoint binding operations could become stuck if a node restarted during the binding process.\n\n- RS176558: Fixed a misleading error message that mentioned LDAP service problems when authentication issues occurred with Entra ID.\n\n- RS160689: Fixed an issue where `redis-cli INFO` could temporarily show an outdated data eviction policy after changing the policy in the Cluster Manager UI.\n\n- RS171340: Fixed an issue where `install.sh` would continue running even when required ports were already occupied.\n\n- RS171806: Fixed an issue where the `rladmin cluster running_actions` command could fail when active tasks did not have progress information available.\n\n- RS184175: Fixed an issue where active cluster alerts were not properly displayed in health reports.\n\n- RS128780: Reduced excessive logging by removing unnecessary \"Dropping obsolete sample\" message from the CCS log file.\n\n- RS185428: Improved error messages for missing root or chain of trust issues during certificate upload.\n\n- RS180061: Fixed an issue where user password update REST API requests would fail if the new password matched the current password.\n\n- RS176555: Fixed an issue that could cause the DMC to crash if a client disconnected during the authentication process.\n\n- RS167847: Fixed an issue where turning off `alert_mgr` also turned off `event_log`.\n\n- RS182007: Modified the custom installer script to prevent the creation of circular symlinks during cluster upgrades.\n\n- RS188362: Fixed an issue with missing metrics in the Cluster Manager UI.\n\n- RS189517: Fixed a breaking change where LDAP filters for `user_dn_query` and `dn_group_query` strictly required parentheses to function correctly. This change caused filters that previously worked without parentheses to stop working after upgrading to Redis Software version 8.0.x. As of version 8.0.16, this breaking change no longer applies, and both `(sAMAccountName=%u)` and `sAMAccountName=%u` are valid filters."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"- Upgrading to this Redis Software version can cause LDAP authentication to fail with \"certificate signed by unknown authority\" errors if your cluster currently uses LDAP authentication. This issue will be fixed in an upcoming maintenance release."},{"id":"openssl-version-on-rhel-9","title":"OpenSSL version on RHEL 9","role":"content","text":"On RHEL 9, Redis Software version 8.0.16 and later requires OpenSSL 3.3 or later. Other supported platforms are unaffected.\n\nRedis Software is built against the OpenSSL 3.3 runtime included in recent RHEL 9 minor releases. Nodes on an earlier RHEL 9 minor release don't meet this requirement. For example, RHEL 9.6 ships OpenSSL 3.2.2.\n\nThe pre-upgrade checks don't detect an incompatible OpenSSL version, so an upgrade can pass validation and then fail partway through. This can leave the management services on the node in a failed state.\n\nBefore you upgrade, check the OpenSSL version on each node:\n\n[code example]\n\nIf the version is earlier than 3.3.0, upgrade the `openssl` and `openssl-libs` packages before you upgrade Redis Software."},{"id":"deprecations","title":"Deprecations","role":"content","text":"#### API deprecations\n\n- Deprecated `event_archive_cleanup_task_settings` for `job_scheduler` REST API requests."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (8.0.16-29 March release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">03584071d957d96e28a8f510ea966aa3c2be4f940d8cd190aae531287f73d8be</span> |\n| Ubuntu 22 (amd64) | <span class=\"break-all\">5850891683fba8bc950eb19576c6ec5f53b1b36f433552bda25bebc9cb90de69</span> |\n| Ubuntu 22 (arm64) | <span class=\"break-all\">8ab2cf746959a9de1b4f83b6392b9535021656d07766e64e6b9bd7f0869167bf</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">6187e26c9363c5502249f5eeb5c810132c51be5f436c5af68c1285422677169c</span> |\n| Red Hat Enterprise Linux (RHEL) 9 (amd64) | <span class=\"break-all\">320d19635deb3f2da3e10143f8f7ff63a07f9d03152f9c6553a7be94a4f44287</span> |\n| Red Hat Enterprise Linux (RHEL) 9 (arm64) | <span class=\"break-all\">59107de776a5a2a3acf3abfc759d57a7ac5acee0ad91a1f1675c821c88381dc2</span> |\n| Amazon Linux 2 | <span class=\"break-all\">138110772cc6bd99d3e97809c5704cf6568b5925c1769248755f1da4694d0ae1</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS155734: Endpoint availability metrics do not work as expected due to a calculation error."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Redis Search query failures during rolling upgrades across the 8.4 version boundary\n\nRedis Search queries can fail during a rolling upgrade when a cluster contains shards running Redis versions earlier than 8.4 and shards running Redis version 8.4 or later, due to an internal protocol change introduced in version 8.4. This issue affects only clusters where `parallel_shards_upgrade` has been changed from its default value of `0`. If both conditions apply, expect Redis Search downtime until all nodes are upgraded.\n\n#### Trim ACKED not supported for Active-Active 8.4 databases\n\nFor Active-Active databases running Redis database version 8.4, the `ACKED` option is not supported for trimming commands.\n\n#### Rolling upgrade limitation for clusters with custom or deprecated modules\n\nDue to module handling changes introduced in Redis Software version 8.0, upgrading a cluster that contains custom or deprecated modules, such as RedisGraph and RedisGears v2, can become stuck when adding a new node to the cluster during a rolling upgrade.\n\n#### Module commands limitation during Active-Active database upgrades to Redis 8.0\n\nWhen upgrading an Active-Active database to Redis version 8.0, you cannot use module commands until all Active-Active database instances have been upgraded. Currently, these commands are not blocked automatically.\n\n#### Redis 8.0 database cannot be created with flash\n\nYou cannot create a Redis 8.0 database with flash storage enabled. Create a Redis 8.0 database with RAM-only storage instead, or use Redis 8.2 for flash-enabled (Redis Flex) databases.\n\n#### Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log."},{"id":"security","title":"Security","role":"security","text":"#### Redis Open Source security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [Redis Open Source](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for Redis Open Source do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in Redis Open Source.\n\nRedis Software 8.0.16-29 supports Redis Open Source 8.4, 8.2, 8.0, 7.4, 7.2, and 6.2. Below is the list of Redis Open Source CVEs and other security vulnerabilities fixed by version.\n\nRedis 8.4.x:\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\nRedis 8.2.x:\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- (CVE-2025-62507) A user can run the `XACKDEL` command with multiple IDs and trigger a stack buffer overflow, which can potentially lead to remote code execution.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\nRedis 8.0.x:\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\nRedis 7.4.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\nRedis 7.2.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[{"id":"new-features-ex0","language":"sh","code":"GET /v4/cluster/health","section_id":"new-features"},{"id":"new-features-ex1","language":"sh","code":"GET /v4/bdb/<database_id>/health","section_id":"new-features"},{"id":"enhancements-ex0","language":"sh","code":"PATCH /v1/crdbs/<crdb_guid>\n    { \"instances\": [{\n        \"db_uid\": \"<local_database_instance_id>\",\n        \"id\": <instance_id>,\n        \"db_config\": { \"<overridden_field_name>\": null }\n      }\n    ]}","section_id":"enhancements"},{"id":"openssl-version-on-rhel-9-ex0","language":"sh","code":"openssl version","section_id":"openssl-version-on-rhel-9"}]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-8-0-releases/rs-8-0-16-33","title":"Redis Software release notes 8.0.16-33 (March 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-16-33/","summary":"Bug fix for LDAP authentication failures after cluster upgrades. Bug fix for Active-Active database configuration update failures due to missing module featureset version errors after database upgrades.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"3185e1e768b9eea828995eb90bd3d4c9ceef75299b2cdf6296c346b9dde3bfce","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Software version 8.0.16](https://redis.io/downloads/#Redis_Software)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- A bug fix for LDAP authentication failures after cluster upgrades\n\n- A bug fix for Active-Active database configuration update failures due to missing module featureset version errors after database upgrades"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 8.0.16 includes the following Redis database versions: 8.4.0, 8.2.1, 8.0.2, 7.4.3, 7.2.7, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 8.4."},{"id":"redis-feature-sets","title":"Redis feature sets","role":"content","text":"Redis Software includes multiple feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 8.4 | RediSearch 8.4<br />RedisJSON 8.4<br />RedisTimeSeries 8.4<br />RedisBloom 8.4<br />See [What's new in Redis 8.4](https://redis.io/docs/latest/develop/whats-new/8-4) and [Redis Open Source 8.4 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.4-release-notes) |\n| 8.2 | RediSearch 8.2<br />RedisJSON 8.2<br />RedisTimeSeries 8.2<br />RedisBloom 8.2<br />See [What's new in Redis 8.2](https://redis.io/docs/latest/develop/whats-new/8-2) and [Redis Open Source 8.2 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.2-release-notes) |\n| 8.0 | RediSearch 8.0<br />RedisJSON 8.0<br />RedisTimeSeries 8.0<br />RedisBloom 8.0<br />See [What's new in Redis 8.0](https://redis.io/docs/latest/develop/whats-new/8-0) and [Redis Open Source 8.0 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.0-release-notes) |\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes) |"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS191290: Fixed an issue that could cause LDAP authentication to fail with \"certificate signed by unknown authority\" errors after upgrading to Redis Software version 8.0.10 or 8.0.16-29.\n\n- RS191210: Fixed an issue where `PATCH /v1/crdbs/<crdb_guid>` requests to update Active-Active database configurations could fail with \"unable to find featureset version for module\" errors after upgrading the cluster version to Redis Software 8.0.10 or 8.0.16 and the database version to Redis 8.4."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"openssl-version-on-rhel-9","title":"OpenSSL version on RHEL 9","role":"content","text":"On RHEL 9, Redis Software version 8.0.16 and later requires OpenSSL 3.3 or later. Other supported platforms are unaffected.\n\nRedis Software is built against the OpenSSL 3.3 runtime included in recent RHEL 9 minor releases. Nodes on an earlier RHEL 9 minor release don't meet this requirement. For example, RHEL 9.6 ships OpenSSL 3.2.2.\n\nThe pre-upgrade checks don't detect an incompatible OpenSSL version, so an upgrade can pass validation and then fail partway through. This can leave the management services on the node in a failed state.\n\nBefore you upgrade, check the OpenSSL version on each node:\n\n[code example]\n\nIf the version is earlier than 3.3.0, upgrade the `openssl` and `openssl-libs` packages before you upgrade Redis Software."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (8.0.16-33 March release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">c6dca8c93569f4902e06ecb9989dddd5b5f2cecc985c22377d70227ba713952d</span> |\n| Ubuntu 22 (amd64) | <span class=\"break-all\">298f3f9f9873266a71737b1f43f2c8e7f5dfba0f066b895835989fc82996a6ff</span> |\n| Ubuntu 22 (arm64) | <span class=\"break-all\">44e328ef023833c5708c8693b73c7e52ca05f363f1d3a052b12e794878155804</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">071f8770a5f677adac02ef73715ccd90f3b64f3912f718f55dba87ecaedcaf4e</span> |\n| Red Hat Enterprise Linux (RHEL) 9 (amd64) | <span class=\"break-all\">6be76e5c5eb21a9a0355cf8cfb93d5c563d6b3d472b57fbea029fb0eb9f6768b</span> |\n| Red Hat Enterprise Linux (RHEL) 9 (arm64) | <span class=\"break-all\">c4065741fae80f4a30e3226f9b45b3b6d72ca392e23bc79ca0a8798fb8fcb660</span> |\n| Amazon Linux 2 | <span class=\"break-all\">7314d582c3724a45d4eacb5af0ccb1d54ba804f0929a5fec56da5021cf205479</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS193156: Active Directory LDAP authentication can fail in the Cluster Manager UI after upgrading to Redis Software version 8.0.16-33 due to an issue with LDAP TLS client certificate handling. Users previously authenticated through Active Directory can no longer sign in to the Cluster Manager UI after the upgrade.\n\n    As a workaround, configure an LDAP client certificate using an [update cluster certificates](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/certificates) REST API request:\n\n    [code example]\n    \n    See [Create certificates](https://redis.io/docs/latest/operate/rs/security/certificates/create-certificates) and [Update certificates](https://redis.io/docs/latest/operate/rs/security/certificates/updating-certificates) for more detailed instructions.\n\n- RS155734: Endpoint availability metrics do not work as expected due to a calculation error."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Redis Search query failures during rolling upgrades across the 8.4 version boundary\n\nRedis Search queries can fail during a rolling upgrade when a cluster contains shards running Redis versions earlier than 8.4 and shards running Redis version 8.4 or later, due to an internal protocol change introduced in version 8.4. This issue affects only clusters where `parallel_shards_upgrade` has been changed from its default value of `0`. If both conditions apply, expect Redis Search downtime until all nodes are upgraded.\n\n#### Trim ACKED not supported for Active-Active 8.4 databases\n\nFor Active-Active databases running Redis database version 8.4, the `ACKED` option is not supported for trimming commands.\n\n#### Rolling upgrade limitation for clusters with custom or deprecated modules\n\nDue to module handling changes introduced in Redis Software version 8.0, upgrading a cluster that contains custom or deprecated modules, such as RedisGraph and RedisGears v2, can become stuck when adding a new node to the cluster during a rolling upgrade.\n\n#### Module commands limitation during Active-Active database upgrades to Redis 8.0\n\nWhen upgrading an Active-Active database to Redis version 8.0, you cannot use module commands until all Active-Active database instances have been upgraded. Currently, these commands are not blocked automatically.\n\n#### Redis 8.0 database cannot be created with flash\n\nYou cannot create a Redis 8.0 database with flash storage enabled. Create a Redis 8.0 database with RAM-only storage instead, or use Redis 8.2 for flash-enabled (Redis Flex) databases.\n\n#### Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log."},{"id":"security","title":"Security","role":"security","text":"#### Redis Open Source security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [Redis Open Source](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for Redis Open Source do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in Redis Open Source.\n\nRedis Software 8.0.16-33 supports Redis Open Source 8.4, 8.2, 8.0, 7.4, 7.2, and 6.2. Below is the list of Redis Open Source CVEs and other security vulnerabilities fixed by version.\n\nRedis 8.4.x:\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\nRedis 8.2.x:\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- (CVE-2025-62507) A user can run the `XACKDEL` command with multiple IDs and trigger a stack buffer overflow, which can potentially lead to remote code execution.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\nRedis 8.0.x:\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\nRedis 7.4.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\nRedis 7.2.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[{"id":"openssl-version-on-rhel-9-ex0","language":"sh","code":"openssl version","section_id":"openssl-version-on-rhel-9"},{"id":"known-issues-ex0","language":"sh","code":"PUT https://<host>:<port>/v1/cluster/certificates\n    {\n      \"certificates\": [\n        {\n          \"name\": \"ldap_client\",\n          \"certificate\": \"<cert>\",\n          \"key\": \"<key>\"\n        }\n      ]\n    }","section_id":"known-issues"}]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-8-0-releases/rs-8-0-18-23","title":"Redis Software release notes 8.0.18-23 (April 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-18-23/","summary":"Redis 8.6 feature set and enhancements. Disk-based search indexing preview. Active-Active database upgrade API. JSON string reuse mechanism for Active-Active databases. Additional REST API enhancements.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"421c89563c399dbef2d7655538a2698c634920832ba3a49d808456fb1b7793af","sections":[{"id":"overview","title":"Overview","role":"overview","text":"​[​Redis Software version 8.0.18](https://redis.io/downloads/#Redis_Software) is now available! This release includes API enhancements that warranted a new minor version instead of a maintenance release for version 8.0.16. However, you can upgrade from 8.0.2 through 8.0.16 to 8.0.18 without issue."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Redis 8.6 feature set and enhancements\n\n- Disk-based search indexing preview\n\n- Active-Active database upgrade API\n\n- JSON string reuse mechanism for Active-Active databases\n\n- Additional REST API enhancements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"new-features","title":"New features","role":"content","text":"#### Redis 8.6 feature set and enhancements\n\nThe Redis 8.6 feature set is now available when you [create](https://redis.io/docs/latest/operate/rs/databases/create) or [upgrade](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-database) a database with database version 8.6.\n\nRedis 8.6 builds on the foundation of Redis 8.4 with significant enhancements to stream reliability, memory management, and security features.\n\nThis release delivers major improvements across multiple areas:\n\n- Stream idempotency with at-most-once delivery guarantees\n\n- New eviction policies for least recently modified data\n\n- Hot key detection and reporting capabilities\n\n- TLS certificate-based automatic client authentication\n\n- Enhanced time series support with NaN values and new aggregators\n\n- Substantial memory reduction for hashes and sorted sets\n\n- Keys memory size histograms for better monitoring\n\n- More than 20 performance and resource utilization improvements\n\n- See [What's new in Redis 8.6](https://redis.io/docs/latest/develop/whats-new/8-6) and [Redis Open Source 8.6 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.6-release-notes) for more details.\n\n#### Disk-based search indexing preview\n\nA preview of disk-based indexing for `TEXT` and `VECTOR` is now available in Redis Software. Search indices are stored on disk instead of in memory, which allows cost optimization for large datasets at the expense of query performance.\n\nTo try this preview, select **Query Engine** in the **Capabilities** section when [creating a new Flex database](https://redis.io/docs/latest/operate/rs/flex/get-started) in the Cluster Manager UI.\n\n#### Active-Active database upgrade API\n\nA new REST API endpoint allows automated upgrading of Active-Active databases through a single request instead of the previous manual process of upgrading each Active-Active database participant separately. The [`POST /v1/crdbs/{crdb_guid}/upgrade`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/crdbs/upgrade) request manages the entire upgrade process automatically, including:\n\n- Upgrading all Active-Active database participants across regions.\n\n- Managing Redis version and module upgrades consistently.\n\n- Handling CRDB featureset version upgrade after all participants are upgraded.\n\n> [!NOTE]\n> The previous method of upgrading Active-Active databases by manually upgrading each participant separately is also still supported."},{"id":"enhancements","title":"Enhancements","role":"content","text":"- Implemented the [JSON string reuse mechanism](https://redis.io/docs/latest/develop/data-types/json/ram#json-string-reuse-mechanism) for Active-Active databases to reduce memory usage.\n\n- REST API enhancements:\n\n    - Added the `oss_cluster_ip_mapping` field to the node object to define custom hostnames for each node by mapping client subnets or hostnames to specific IP addresses.\n\n    - Enhanced Active-Active replication OOM handling with `replication_oom_threshold_percent` configuration. This enhancement prevents sync failures in Active-Active databases running Redis version 8.4 or later during out-of-memory conditions by prioritizing replication sync over client writes when memory usage reaches the configured threshold percentage below `maxmemory`.\n\n    - Added the `search-bg-index-sleep-duration-us` configuration field to control sleep duration in microseconds during background indexing. Increasing the duration can help prevent server unresponsiveness during intensive indexing workloads such as vector indexes."},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 8.0.18 includes the following Redis database versions: 8.6.2, 8.4.0, 8.2.1, 8.0.2, 7.4.3, 7.2.7, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 8.6."},{"id":"redis-feature-sets","title":"Redis feature sets","role":"content","text":"Redis Software includes multiple feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 8.6 | RediSearch 8.6<br />RedisJSON 8.6<br />RedisTimeSeries 8.6<br />RedisBloom 8.6<br />See [What's new in Redis 8.6](https://redis.io/docs/latest/develop/whats-new/8-6) and [Redis Open Source 8.6 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.6-release-notes) |\n| 8.4 | RediSearch 8.4<br />RedisJSON 8.4<br />RedisTimeSeries 8.4<br />RedisBloom 8.4<br />See [What's new in Redis 8.4](https://redis.io/docs/latest/develop/whats-new/8-4) and [Redis Open Source 8.4 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.4-release-notes) |\n| 8.2 | RediSearch 8.2<br />RedisJSON 8.2<br />RedisTimeSeries 8.2<br />RedisBloom 8.2<br />See [What's new in Redis 8.2](https://redis.io/docs/latest/develop/whats-new/8-2) and [Redis Open Source 8.2 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.2-release-notes) |\n| 8.0 | RediSearch 8.0<br />RedisJSON 8.0<br />RedisTimeSeries 8.0<br />RedisBloom 8.0<br />See [What's new in Redis 8.0](https://redis.io/docs/latest/develop/whats-new/8-0) and [Redis Open Source 8.0 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.0-release-notes) |\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes) |"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS186863: Fixed an issue where scheduled database backups could fail due to a data type error when reading backup configuration settings.\n\n- RS185422: Improved error logging when database imports from Google Cloud Storage fail without error messages.\n\n- RS175025: Reduced failover delays by improving the timing of internal service startup processes.\n\n- RS190352: Fixed an issue where the cluster did not check for incompatible modules before upgrading databases from Auto Tiering to Redis Flex.\n\n- RS190322: Improved `bdb-cli` output gathering reliability and performance\n\n- RS187486: Fixed an issue where search lazy-expire effects might not propagate from the primary shard to the replica shard and cause keys to become out of sync between Active-Active database instances.\n\n- RS186477: Fixed an issue where Active-Active database synchronization could fail when key names larger than 64 KB were created through Lua scripts.\n\n- RS184865: Fixed an issue where performing certain maintenance operations during failover could cause dual primary shards.\n\n- RS184743: Fixed an issue where Active-Active databases could fail to replicate between nodes running different Redis Software versions during cluster upgrades from version 7.2.x to 7.8.x or later.\n\n- RS175208: Fixed an issue where excessive logging from Active-Active database synchronization could fill up disk space.\n\n- RS189994: Fixed a proxy connection crash that could occur when running one-to-many transactions on a node without any primary shards.\n\n- RS193156: Fixed an issue where Users previously authenticated through Active Directory LDAP authentication can no longer sign in to the Cluster Manager UI after upgrading to Redis Software version 8.0.16-33.\n\n- RS176498: Fixed an issue that could prevent the addition of new participants to Active-Active databases after upgrades when using modules, due to version incompatibility in the database configuration.\n\n- RS192329: Fixed an issue where Redis Flex database shards could crash during resharding operations when concurrent replica connections requested different types of synchronization.\n\n- RS192035: Fixed an issue where Redis Flex database shards could crash during reshard trimming when append-only file (AOF) persistence was enabled.\n\n- RS192072: Fixed an issue where Redis Flex database shards could crash during replication when key eviction or expiration was occurring."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":"- `crdb_controller` is now enabled by default."},{"id":"openssl-version-on-rhel-9","title":"OpenSSL version on RHEL 9","role":"content","text":"On RHEL 9, Redis Software version 8.0.16 and later requires OpenSSL 3.3 or later. Other supported platforms are unaffected.\n\nRedis Software is built against the OpenSSL 3.3 runtime included in recent RHEL 9 minor releases. Nodes on an earlier RHEL 9 minor release don't meet this requirement. For example, RHEL 9.6 ships OpenSSL 3.2.2.\n\nThe pre-upgrade checks don't detect an incompatible OpenSSL version, so an upgrade can pass validation and then fail partway through. This can leave the management services on the node in a failed state.\n\nBefore you upgrade, check the OpenSSL version on each node:\n\n[code example]\n\nIf the version is earlier than 3.3.0, upgrade the `openssl` and `openssl-libs` packages before you upgrade Redis Software."},{"id":"reserved-ports","title":"Reserved ports","role":"content","text":"Make sure the following ports are open before upgrading Redis Software.\n\nPorts reserved as of Redis Software version 8.0.18:\n\n| Port | Process name | Usage | \n|------|--------------|-------|\n| 3357 | reconciliation_tree_grpc | Internal communication |\n\nPorts reserved as of Redis Software version 7.22.0:\n\n| Port | Process name | Usage | \n|------|--------------|-------|\n| 3346 | cluster_api_internal | Cluster API internal port |\n| 3351 | cluster_watchdog_grpc_api | Cluster watchdog now supports gRPC |\n| 3352 | grpc_service_mesh | gRPC communication between nodes |\n| 3353 | local_grpc_service_mesh | Local gRPC services |\n| 3354 | grpc_gossip_envoy | gRPC gossip protocol communication between nodes |\n| 3355 | authentication_service | Authentication service internal port |\n\nPorts reserved as of Redis Software version 7.8.2:\n\n| Port | Process name | Usage | \n|------|--------------|-------|\n| 3347 | cert_exporter | Reports cluster certificate metrics |\n| 3348 | process_exporter | Reports process metrics for DMC and Redis processes |\n| 3349 | cluster_wd_exporter | Reports cluster watchdog metrics |\n| 3350 | db_controller | Internode communication |\n| 9091 | node_exporter | Reports host node metrics related to CPU, memory, disk, and more |\n| 9125 | statsd_exporter | Reports push metrics related to the DMC and syncer, and some cluster and node metrics |\n\nSee [Ports and port ranges used by Redis Software](https://redis.io/docs/latest/operate/rs/networking/port-configurations#ports-and-port-ranges-used-by-redis-enterprise-software) for a complete list."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (8.0.18-23 April release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">cae7f2ec4f04345efa3cadf5c8dedb9192886b26cb87a282868b82df7c8399c8</span> |\n| Ubuntu 22 (amd64) | <span class=\"break-all\">75241db7c52d68f7428780cb74a17b203ed0dc064716df0111fb4ff4632ed6fa</span> |\n| Ubuntu 22 (arm64) | <span class=\"break-all\">32e8397397ae730cb9b68255d4346eab13f799b46fb7f40fe2c3393979c9812b</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">000ab08510c1ec4f487a49b754ad911a2b036eaa79dccd91abd1e038ed9942dd</span> |\n| Red Hat Enterprise Linux (RHEL) 9 (amd64) | <span class=\"break-all\">09ed4c87994a0e64c9ed20cdc2e4c27cdcffad1e915466d70a7666a667dd3d83</span> |\n| Red Hat Enterprise Linux (RHEL) 9 (arm64) | <span class=\"break-all\">01eb15e2ad47a598356161f3b960f380b46c6080e2b7b6bcdc7e17a1a9197ad8</span> |\n| Amazon Linux 2 | <span class=\"break-all\">044b8782a244b7ad7ddbf6da461759f9d57862869c523461383090af2b42c0b6</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS155734: Endpoint availability metrics do not work as expected due to a calculation error."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Redis Search query failures during rolling upgrades across the 8.4 version boundary\n\nRedis Search queries can fail during a rolling upgrade when a cluster contains shards running Redis versions earlier than 8.4 and shards running Redis version 8.4 or later, due to an internal protocol change introduced in version 8.4. This issue affects only clusters where `parallel_shards_upgrade` has been changed from its default value of `0`. If both conditions apply, expect Redis Search downtime until all nodes are upgraded.\n\n#### Trim ACKED not supported for Active-Active 8.4 databases\n\nFor Active-Active databases running Redis database version 8.4, the `ACKED` option is not supported for trimming commands.\n\n#### Rolling upgrade limitation for clusters with custom or deprecated modules\n\nDue to module handling changes introduced in Redis Software version 8.0, upgrading a cluster that contains custom or deprecated modules, such as RedisGraph and RedisGears v2, can become stuck when adding a new node to the cluster during a rolling upgrade.\n\n#### Module commands limitation during Active-Active database upgrades to Redis 8.0\n\nWhen upgrading an Active-Active database to Redis version 8.0, you cannot use module commands until all Active-Active database instances have been upgraded. Currently, these commands are not blocked automatically.\n\n#### Redis 8.0 database cannot be created with flash\n\nYou cannot create a Redis 8.0 database with flash storage enabled. Create a Redis 8.0 database with RAM-only storage instead, or use Redis 8.2 for flash-enabled (Redis Flex) databases.\n\n#### Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log."},{"id":"security","title":"Security","role":"security","text":"#### Redis Open Source security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [Redis Open Source](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for Redis Open Source do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in Redis Open Source.\n\nRedis Software 8.0.18-23 supports Redis Open Source 8.6, 8.4, 8.2, 8.0, 7.4, 7.2, and 6.2. Below is the list of Redis Open Source CVEs and other security vulnerabilities fixed by version.\n\nRedis 8.6.x:\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\nRedis 8.4.x:\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\nRedis 8.2.x:\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- (CVE-2025-62507) A user can run the `XACKDEL` command with multiple IDs and trigger a stack buffer overflow, which can potentially lead to remote code execution.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\nRedis 8.0.x:\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\nRedis 7.4.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\nRedis 7.2.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[{"id":"openssl-version-on-rhel-9-ex0","language":"sh","code":"openssl version","section_id":"openssl-version-on-rhel-9"}]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-8-0-releases/rs-8-0-18-36","title":"Redis Software release notes 8.0.18-36 (May 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-18-36/","summary":"Bug fix for a cluster upgrade issue. Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-10-09T10:01:31-05:00","page_type":"content","content_hash":"d61b3c0891238c86e785e2a8d2f065ea0df4e699e67d43423e40d158df1cf705","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Software version 8.0.18](https://redis.io/downloads/#Redis_Software)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Bug fix for a cluster upgrade issue\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 8.0.18 includes the following Redis database versions: 8.6.2, 8.4.0, 8.2.1, 8.0.2, 7.4.3, 7.2.7, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 8.6."},{"id":"redis-feature-sets","title":"Redis feature sets","role":"content","text":"Redis Software includes multiple feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 8.6 | RediSearch 8.6<br />RedisJSON 8.6<br />RedisTimeSeries 8.6<br />RedisBloom 8.6<br />See [What's new in Redis 8.6](https://redis.io/docs/latest/develop/whats-new/8-6) and [Redis Open Source 8.6 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.6-release-notes) |\n| 8.4 | RediSearch 8.4<br />RedisJSON 8.4<br />RedisTimeSeries 8.4<br />RedisBloom 8.4<br />See [What's new in Redis 8.4](https://redis.io/docs/latest/develop/whats-new/8-4) and [Redis Open Source 8.4 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.4-release-notes) |\n| 8.2 | RediSearch 8.2<br />RedisJSON 8.2<br />RedisTimeSeries 8.2<br />RedisBloom 8.2<br />See [What's new in Redis 8.2](https://redis.io/docs/latest/develop/whats-new/8-2) and [Redis Open Source 8.2 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.2-release-notes) |\n| 8.0 | RediSearch 8.0<br />RedisJSON 8.0<br />RedisTimeSeries 8.0<br />RedisBloom 8.0<br />See [What's new in Redis 8.0](https://redis.io/docs/latest/develop/whats-new/8-0) and [Redis Open Source 8.0 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.0-release-notes) |\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes) |"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS195106: Fixed a cluster upgrade issue where adding multiple nodes with a new version simultaneously to a cluster could cause node configuration data to be incorrectly deleted and lead to node addition failures."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"openssl-version-on-rhel-9","title":"OpenSSL version on RHEL 9","role":"content","text":"On RHEL 9, Redis Software version 8.0.16 and later requires OpenSSL 3.3 or later. Other supported platforms are unaffected.\n\nRedis Software is built against the OpenSSL 3.3 runtime included in recent RHEL 9 minor releases. Nodes on an earlier RHEL 9 minor release don't meet this requirement. For example, RHEL 9.6 ships OpenSSL 3.2.2.\n\nThe pre-upgrade checks don't detect an incompatible OpenSSL version, so an upgrade can pass validation and then fail partway through. This can leave the management services on the node in a failed state.\n\nBefore you upgrade, check the OpenSSL version on each node:\n\n[code example]\n\nIf the version is earlier than 3.3.0, upgrade the `openssl` and `openssl-libs` packages before you upgrade Redis Software."},{"id":"reserved-ports","title":"Reserved ports","role":"content","text":"Make sure the following ports are open before upgrading Redis Software.\n\nPorts reserved as of Redis Software version 8.0.18:\n\n| Port | Process name | Usage | \n|------|--------------|-------|\n| 3357 | reconciliation_tree_grpc | Internal communication |\n\nPorts reserved as of Redis Software version 7.22.0:\n\n| Port | Process name | Usage | \n|------|--------------|-------|\n| 3346 | cluster_api_internal | Cluster API internal port |\n| 3351 | cluster_watchdog_grpc_api | Cluster watchdog now supports gRPC |\n| 3352 | grpc_service_mesh | gRPC communication between nodes |\n| 3353 | local_grpc_service_mesh | Local gRPC services |\n| 3354 | grpc_gossip_envoy | gRPC gossip protocol communication between nodes |\n| 3355 | authentication_service | Authentication service internal port |\n\nPorts reserved as of Redis Software version 7.8.2:\n\n| Port | Process name | Usage | \n|------|--------------|-------|\n| 3347 | cert_exporter | Reports cluster certificate metrics |\n| 3348 | process_exporter | Reports process metrics for DMC and Redis processes |\n| 3349 | cluster_wd_exporter | Reports cluster watchdog metrics |\n| 3350 | db_controller | Internode communication |\n| 9091 | node_exporter | Reports host node metrics related to CPU, memory, disk, and more |\n| 9125 | statsd_exporter | Reports push metrics related to the DMC and syncer, and some cluster and node metrics |\n\nSee [Ports and port ranges used by Redis Software](https://redis.io/docs/latest/operate/rs/networking/port-configurations#ports-and-port-ranges-used-by-redis-enterprise-software) for a complete list."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"openshift-virtualization","title":"OpenShift Virtualization","role":"content","text":"Redis Software version 8.0.18-36 supports Red Hat OpenShift Virtualization 4.x.\n\nRun Redis Software on virtual machines managed by OpenShift Virtualization using RHEL 9 or a compatible distribution as the guest operating system. Install and configure the cluster the same way you would on any other supported platform. For operating system compatibility details, see the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms)."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (8.0.18-36 May release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">d366c39e9e05aeab225cd01551a93d96ca0cb587606bcc5a1f14ece4796c6e5d</span> |\n| Ubuntu 22 (amd64) | <span class=\"break-all\">0862b25de01f09d99a46078b70bbd96e87bb64267f93a55600679c354e669a58</span> |\n| Ubuntu 22 (arm64) | <span class=\"break-all\">8d1736d340686a52767ee028d76445afe3a1d5159d549f320cafd183d3f20ec4</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">5c6b3804f9bf9eaa57bce3a40de653f35c7c2290dab7bca7c211bd1ddbb40fb4</span> |\n| Red Hat Enterprise Linux (RHEL) 9 (amd64) | <span class=\"break-all\">3e997df37a419bbce98a32de8deb14f31142acd7540c1c52837e4d8e3cbd007b</span> |\n| Red Hat Enterprise Linux (RHEL) 9 (arm64) | <span class=\"break-all\">40ab295e9a16b5f42960a664a94c85f62b47758add316a6edbcfbe0bc8ca226b</span> |\n| Amazon Linux 2 | <span class=\"break-all\">dfb7ad871b67fb9175124a4bc5b1df6880799e7695b61f95a4ac0d464c74fe8c</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS155734: Endpoint availability metrics do not work as expected due to a calculation error."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Redis Search query failures during rolling upgrades across the 8.4 version boundary\n\nRedis Search queries can fail during a rolling upgrade when a cluster contains shards running Redis versions earlier than 8.4 and shards running Redis version 8.4 or later, due to an internal protocol change introduced in version 8.4. This issue affects only clusters where `parallel_shards_upgrade` has been changed from its default value of `0`. If both conditions apply, expect Redis Search downtime until all nodes are upgraded.\n\n#### Trim ACKED not supported for Active-Active 8.4 databases\n\nFor Active-Active databases running Redis database version 8.4, the `ACKED` option is not supported for trimming commands.\n\n#### Rolling upgrade limitation for clusters with custom or deprecated modules\n\nDue to module handling changes introduced in Redis Software version 8.0, upgrading a cluster that contains custom or deprecated modules, such as RedisGraph and RedisGears v2, can become stuck when adding a new node to the cluster during a rolling upgrade.\n\n#### Module commands limitation during Active-Active database upgrades to Redis 8.0\n\nWhen upgrading an Active-Active database to Redis version 8.0, you cannot use module commands until all Active-Active database instances have been upgraded. Currently, these commands are not blocked automatically.\n\n#### Redis 8.0 database cannot be created with flash\n\nYou cannot create a Redis 8.0 database with flash storage enabled. Create a Redis 8.0 database with RAM-only storage instead, or use Redis 8.2 for flash-enabled (Redis Flex) databases.\n\n#### Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log."},{"id":"security","title":"Security","role":"security","text":"#### Redis Open Source security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [Redis Open Source](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for Redis Open Source do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in Redis Open Source.\n\nRedis Software 8.0.18-36 supports Redis Open Source 8.6, 8.4, 8.2, 8.0, 7.4, 7.2, and 6.2. Below is the list of Redis Open Source CVEs and other security vulnerabilities fixed by version.\n\nRedis 8.6.x:\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\nRedis 8.4.x:\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\nRedis 8.2.x:\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- (CVE-2025-62507) A user can run the `XACKDEL` command with multiple IDs and trigger a stack buffer overflow, which can potentially lead to remote code execution.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\nRedis 8.0.x:\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\nRedis 7.4.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\nRedis 7.2.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[{"id":"openssl-version-on-rhel-9-ex0","language":"sh","code":"openssl version","section_id":"openssl-version-on-rhel-9"}]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-8-0-releases/rs-8-0-2-17","title":"Redis Software release notes 8.0.2-17 (October 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-2-17/","summary":"Redis Software 8! The most performant, most secure, and richest version so far. Built for performance, scale, and reliability to power modern ML and AI applications.","tags":["docs","operate","rs"],"last_updated":"2026-10-19T14:11:36-05:00","page_type":"content","content_hash":"725187cfd687d91b5435a457e87f5bd0c20ca04b500883d404da5a849cdb4b6f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"​[​Redis Software version 8.0.2](https://redis.io/downloads/#Redis_Software) is now available!"},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Redis 8.0 and 8.2 feature set versions\n\n- Performance improvements and memory reduction\n\n- New vector set data structure\n\n- Redis Flex revamped engine\n\n- Redis Search upgrades\n\n- Lag-aware availability API\n\n- Metrics stream engine (General Availability)\n\n- Simplified module management\n\n- New REST API fields for database and cluster configuration\n\n- Customer-managed certificates for internode encryption\n\n- Smart client handoffs"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"new-features","title":"New features","role":"content","text":"#### Redis 8.0 and 8.2 feature sets\n\nRedis 8.0 and 8.2 feature sets are now available when you [create](https://redis.io/docs/latest/operate/rs/databases/create) or [upgrade](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-database) a database with database version 8.2.\n\n##### Redis 8.0 feature set and enhancements\n\n- Preview of a new [vector set](https://redis.io/docs/latest/develop/data-types/vector-sets) data structure that supports high-dimensional vector similarity search, ideal for AI use cases such as semantic search and recommendation systems.\n\n- New hash commands [`HGETEX`](https://redis.io/docs/latest/commands/hgetex), [`HSETEX`](https://redis.io/docs/latest/commands/hsetex), and [`HGETDEL`](https://redis.io/docs/latest/commands/hgetdel), which can simplify caching and session management patterns.\n\n- Enhanced access control lists (ACLs) to support new data structures introduced in Redis 8.\n\n    - Existing ACL categories such as `@read` and `@write` now include commands for JSON, time series, vector, and probabilistic data structures.\n\n    - New ACL categories: `@search`, `@json`, `@timeseries`, `@bloom`, `@cuckoo`, `@topk`, `@cms`, and `@tdigest`.\n\n- Redis Search improvements.\n\n- Significant performance improvements, including:\n\n    - Up to 87% lower command latency.\n\n    - 35% memory savings for replica nodes.\n\n    - 16x more query processing capacity with horizontal and vertical scaling.\n\n- See [What's new in Redis 8.0](https://redis.io/docs/latest/develop/whats-new/8-0) and [Redis Open Source 8.0 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.0-release-notes) for more details.\n\n##### Redis 8.2 feature set and enhancements\n\n- New Redis streams commands [`XDELEX`](https://redis.io/docs/latest/commands/xdelex) and [`XACKDEL`](https://redis.io/docs/latest/commands/xackdel) that simplify consumer group management and stream lifecycle operations.\n\n- New operators `DIFF`, `DIFF1`, `ANDOR`, and `ONE` for the [`BITOP`](https://redis.io/docs/latest/commands/bitop) command, which enable more complex bitmap workflows and can simplify operations that previously required multiple commands.\n\n- New keyspace notification event types `OVERWRITTEN` and `TYPE_CHANGED` that provide better visibility into data changes.\n\n- Performance optimizations and memory efficiency improvements.\n\n- Redis Search improvements:\n\n    - New [SVS-VAMANA vector index](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#svs-vamana-index) type, which supports vector compression.\n\n    - New `SHARD_K_RATIO` parameter for [K-nearest neighbor (KNN) vector queries](https://redis.io/docs/latest/develop/ai/search-and-query/vectors#knn-vector-search), which favors network latency over accuracy to provide faster responses when exact precision isn't critical.\n\n- See [What's new in Redis 8.2](https://redis.io/docs/latest/develop/whats-new/8-2) and [Redis Open Source 8.2 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.2-release-notes) for more details.\n\n#### Built-in capabilities with Redis 8\n\nRedis Software databases created with or upgraded to Redis version 8 include all the Redis capabilities, built in the database version as follows:\n\n| Database type | Automatically enabled capabilities |\n|---------------|------------------------------------|\n| RAM-only | [Search and query](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search)<br />[JSON](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json)<br />[Time series](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/timeseries)<br />[Probabilistic](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/bloom)  |\n| Flash-enabled ([Redis Flex](https://redis.io/docs/latest/operate/rs/databases/flash)) | [JSON](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json)<br />[Probabilistic](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/bloom) |\n| [Active-Active](https://redis.io/docs/latest/operate/rs/databases/active-active)<sup>[1](#enabled-modules-table-note-1)</sup> | [Search and query](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/search-active-active)<br />[JSON](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/json) |\n\n1. <a name=\"enabled-modules-table-note-1\"></a>Upgrading existing Active-Active databases to Redis version 8 does not automatically enable these capabilities. Only new Active-Active databases created with Redis version 8 enable these capabilities by default.\n\n#### Performance improvements and memory reduction\n\n- More than 30 performance, resource utilization, and memory footprint improvements.\n\n- An improved replication mechanism that is more performant (faster) and robust and saves up to 35% memory during replication.\n\n- Up to 87% lower command latency.\n\n- Redis Search: Up to 144% higher QPS and new vector compression.\n\n#### Redis Flex revamped engine\n\nRedis Flex is getting a revamped engine with performance improvements, increased stability, and the removal of previous limitations around RAM utilization.\n\nRedis Flex now offloads the least used keys with their values, allowing more hot data to be stored in RAM. This increases performance for most real-world use cases.\n\nPast limitations requiring minimum RAM allocation to store all the database's keys are no longer relevant, allowing more flexibility and higher resource utilization.\n\n- Databases with Redis version 8.2 and later (newly created or upgraded to those versions) automatically use the new Redis Flex engine.\n\n- Databases with Redis version 7.4 and earlier will continue to run on the previous engine.\n\nFor more information about Redis Flex, see:\n\n- [Redis Flex overview](https://redis.io/docs/latest/operate/rs/databases/flash)\n- [Redis Flex quick start](https://redis.io/docs/latest/operate/rs/databases/flash/quickstart)\n\n#### Lag-aware availability API\n\nThe [database availability API](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/availability) now supports lag-aware availability checks that consider replication lag tolerance.\n\nYou can reduce the risk of data inconsistencies during disaster recovery by incorporating lag-aware availability checks into your disaster recovery solution and ensuring failover-failback flows only occur when databases are accessible and sufficiently synchronized.\n\nThe lag tolerance threshold is 100 milliseconds by default. Depending on factors such as workload, network conditions, and  throughput, you might want to adjust the lag tolerance threshold using one of the following methods:\n\n- Change the default threshold for the entire cluster by setting `availability_lag_tolerance_ms` with an [update cluster](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster#put-cluster) request.\n\n- Override the default threshold by adding the `availability_lag_tolerance_ms` query parameter to specific lag-aware [availability checks](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/availability). For example:\n\n    [code example]\n\nFor more details, see [Check database availability for monitoring and load balancers](https://redis.io/docs/latest/operate/rs/monitoring/db-availability).\n\n#### Metrics stream engine GA\n\nThe [metrics stream engine](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine) is now generally available:\n\n- The metrics stream engine's exporter-based infrastructure provides access to more accurate, real-time data. This enhanced, scalable monitoring system allows you to set up more effective alerts and respond to issues faster.\n\n- Exposes a new `/v2` Prometheus scraping endpoint that you can use to export metrics to external monitoring tools such as Grafana, DataDog, NewRelic, and Dynatrace.\n\n- Exports raw data instead of aggregated data to improve monitoring at scale and accuracy compared to v1 Prometheus metrics.\n\n- For a list of metrics exported by the metrics stream engine, see [Prometheus metrics v2](https://redis.io/docs/latest/operate/rs/references/metrics/prometheus-metrics-v2).\n\n- To transition to the metrics stream engine, either migrate your existing dashboards using [Prometheus v1 metrics and equivalent v2 PromQL](https://redis.io/docs/latest/operate/rs/references/metrics/prometheus-metrics-v1-to-v2) or use [new preconfigured dashboards](https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise#grafana-dashboards-for-redis-software).\n\n- As part of the transition to the metrics stream engine, some internal cluster manager alerts were deprecated in favor of external monitoring solutions. See the [alerts transition plan](https://redis.io/docs/latest/operate/rs/references/alerts/alerts-v1-to-v2) for guidance.\n\n- See [Best practices for monitoring](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine#best-practices-for-monitoring) for a list of recommended metrics to monitor.\n\n#### Customer-managed certificates for internode encryption\n\nInstead of using Redis Software's self-signed certificates for internode encryption, you can provide certificates generated by your Certificate Authority (CA). See [Customer-provided certificates ](https://redis.io/docs/latest/operate/rs/security/encryption/internode-encryption#customer-provided-certificates) for details.\n\n#### Smart client handoffs\n\nSmart client handoffs enable zero-disruption maintenance for Redis databases by allowing clients to reconnect gracefully during planned operations like cluster upgrades or database version upgrades. Applications stay online with no hard disconnects or timeout errors. See [Smart client handoffs](https://redis.io/docs/latest/operate/rs/clusters/configure/sch) for more information."},{"id":"enhancements","title":"Enhancements","role":"content","text":"- Module management enhancements:\n\n    - Operating system (OS) upgrades no longer require manually uploading module packages compiled for the target OS version to a node in the existing cluster.\n\n    - Copying module packages to a node in the cluster before cluster recovery is no longer required.\n\n    - Added new REST API requests to manage custom, user-defined modules. See [Custom module management APIs](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/modules/user-defined) for details.\n\n    - Added module configuration fields to the database configuration. Use `search`, `query_performance_factor`, `timeseries`, and `probabilistic` objects to configure Redis modules instead of the deprecated `module_args` field. These fields are visible in [`GET /v1/bdbs`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs) requests only when using the `extended=true` query parameter.\n\n    - Added `--update-db-config-modules` option to the [`crdb-cli crdb update`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/update) command to streamline updating module information in the CRDB configuration after upgrading modules used by Active-Active databases. Use this option only after all CRDB database instances have upgraded their modules.\n\n        [code example]\n\n- Added a check to block new user creation after the maximum limit of 32,000 users has been reached:\n\n    - Added a [cluster alert](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/cluster/alert_settings) `cluster_users_count_approaches_limit`, which triggers when the number of users surpasses a threshold percentage of the maximum user limit. This alert is enabled with a 90% threshold by default on new clusters.\n\n    - Added a `users_count` cluster metric to [Prometheus metrics v2](https://redis.io/docs/latest/operate/rs/references/metrics/prometheus-metrics-v2) that shows the current number of users on the cluster.\n\n- Additional REST API enhancements:\n\n    - New `last_login` field for [users](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/users), which stores the UNIX timestamp of the user's last successful login to the Cluster Manager UI or REST API.\n\n    - Added [cluster configuration](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster) fields:\n\n        - `disconnect_clients_on_password_removal`: Controls whether client connections using removed, revoked, or rotated passwords are actively disconnected.\n\n        - `replica_sconns_on_demand`: When enabled, the DMC stops holding persistent connections to replica shards and reduces the number of internode connections by half.\n\n        - `metrics_auth`: If set to `true`, enables basic authentication for Prometheus exporters and restricts access to authenticated users with `admin`, `cluster_member`, or `cluster_viewer` [management roles](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions).\n\n    - Added [database configuration](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs) fields:\n\n        - `conns_global_maximum_dedicated`: Defines the maximum number of dedicated server connections for a database across all workers.\n\n        - `conns_minimum_dedicated`: Defines the minimum number of dedicated server connections the DMC maintains per worker per shard.\n\n        - `disconnect_clients_on_password_removal`: Controls whether client connections using removed, revoked, or rotated passwords are actively disconnected.\n\n        - `link_sconn_on_full_request`: Feature flag for DMC behavior on linking client requests.\n\n        - `partial_request_timeout_seconds`: Timeout for incomplete client commands that cause head-of-line blocking.\n\n        - `preemptive_drain_timeout_seconds`: Timeout for preemptive drain of client connections before a shard is taken down.\n\n        - `replica_sconns_on_demand`: When enabled, the DMC stops holding persistent connections to replica shards and reduces the number of internode connections by half.\n\n        - `use_selective_flush`: Enables selective flush of destination shards.\n\n- Added action IDs to operation and state machine log entries.\n\n- Internal connections no longer generate `new_int_conn` audit records.\n\n- Improved control plane authentication handling for new clusters with a dedicated authentication service.\n\n- Improved handling of long-running read-only scripts to reduce unnecessary failovers."},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 8.0.2 includes five Redis database versions: 8.2.1, 8.0.2, 7.4.3, 7.2.7, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 8.2."},{"id":"redis-feature-sets","title":"Redis feature sets","role":"content","text":"Redis Software includes multiple feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 8.2 | RediSearch 8.2<br />RedisJSON 8.2<br />RedisTimeSeries 8.2<br />RedisBloom 8.2<br />See [What's new in Redis 8.2](https://redis.io/docs/latest/develop/whats-new/8-2) and [Redis Open Source 8.2 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.2-release-notes) |\n| 8.0 | RediSearch 8.0<br />RedisJSON 8.0<br />RedisTimeSeries 8.0<br />RedisBloom 8.0<br />See [What's new in Redis 8.0](https://redis.io/docs/latest/develop/whats-new/8-0) and [Redis Open Source 8.0 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.0-release-notes) |\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes) |"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS156391: Fixed an issue where the `job_scheduler`'s memory usage could increase significantly when the diagnostic logging service was enabled.\n\n- RS132033: Fixed an issue where out-of-memory errors in the Lua interpreter prevented scripts from running Redis commands until the shard was restarted. This fix is included in Redis database version 7.2 and requires a database upgrade from earlier versions.\n\n- RS153192: Updated the installer's minimum RAM requirement to 8 GB.\n\n- RS159685: Fixed an issue with high DMC CPU usage after changing the primary node of a cluster that has Active-Active databases.\n\n- RS160546: Fixed an issue where `rladmin status extra all` did not show available RAM.\n\n- RS150592: Fixed an issue where connection errors were not automatically retried.\n\n- RS161945: Fixed an issue where state machine logs showed a generic state machine ID instead of the descriptive state machine name when creating a database from persistence.\n\n- RS160196: Fixed an issue where a node could be set as primary before completing the bootstrap process.\n\n- RS153736: Fixed an issue where the `PUBSUB SHARDNUMSUB` command would not respond when called without arguments if the OSS Cluster API was enabled.\n\n- RS163254: Fixed an issue where the policy update logs displayed inconsistent boolean value formats, mixing `enabled/disabled` and `True/False`.\n\n- RS158250: Fixed an issue with Active-Active databases with search enabled where replica shards could crash after migration to a new node.\n\n- RS164471: Fixed an issue where the script to generate self-signed certificates (generate_self_signed_certs.sh) failed on custom installations due to hard-coded file paths.\n\n- RS164218: Fixed an issue where Speedb log files were not properly rotated and archived, causing logs to accumulate and consume disk space.\n\n- RS162719: Fixed an issue where connection problems could prevent shards from restarting during failover and cause the failover process to become stuck.\n\n- RS161589: Changed the installer answers file parameter from `skip_updating_env_path` to `update_env_path` to improve clarity and accuracy.\n\n- RS161574: Fixed an issue where Active-Active database synchronization could fail when Lua scripts used certain read-only commands that accessed keys across multiple slots.\n\n- RS160347: Made optimizations to reduce the `heartbeatd` service's memory usage.\n\n- RS156394: Improved error messages when module commands are temporarily unavailable during cluster configuration changes.\n\n- RS154815: Improved diagnostic reporting for connection issues when the maximum number of transactions is reached.\n\n- RS147053: Fixed an issue where some `system_reserved_ports` were not displayed in the `rladmin info cluster` command output.\n\n- RS114668: Fixed an issue where setting `failure_detection_sensitivity` with the `bootstrap` API did not automatically update `watchdog_profile` accordingly.\n\n- RS163266: Fixed an issue where shard rebalancing could take excessive time when replicas were unresponsive due to high CPU load by reducing connection retry attempts from 300 to 5.\n\n- RS162524: Fixed an issue where the DNS backend could fail with \"too many open files\" errors due to socket leaks.\n\n- RS161547: Fixed an issue where nodes could fail to send messages related to state machines due to a timing issue between notification threads and management threads.\n\n- RS155990: Fixed an issue where the `forwarding_state` field was missing from the endpoint schema.\n\n- RS166307: Updated v2 Prometheus metric names to comply with naming conventions by changing the `proxy_` prefix to `endpoint_` for `connections_rate`, `rate_limit_ok`, `rate_limit_overflows`, `accepted_connections`, and `dispatch_failures`.\n\n- RS164703: Improved diagnostic reporting for shard restart operations by adding PID logging before shutdown.\n\n- RS152179: Reduced log noise by removing a harmless error message that appeared repeatedly in DMC proxy logs.\n\n- RS132087: Fixed inconsistent node status reports between `rladmin` and the REST API.\n\n- RS166878: Fixed legacy `module_args` mapping to handle boolean fields as `TRUE/FALSE` values instead of flags.\n\n- RS166825: Fixed an issue where the Sentinel service could become unresponsive while processing certain commands due to a timing issue.\n\n- RS162290: Fixed an issue where the node status API returned 0 instead of the actual provisional RAM and flash values if the node reached its shard limit.\n\n- RS158251: Added a check to block new user creation after the maximum limit of 32,000 users has been reached to prevent DMC proxy crashes.\n\n- RS166813: Fixed an issue where Lua incorrectly converted empty JSON arrays into empty JSON objects.\n\n- RS166683: Fixed an issue where `FT.DROPINDEX index DD` deleted indexed keys on the local Active-Active database instance but failed to sync the deletions to instances in other participating clusters.\n\n- RS162972: Fixed an issue where the REST API was only accessible from the primary node when certificate-based authentication was enabled.\n\n- RS158972: Fixed an issue where certificate verification failed during node join and replace operations when internode encryption was enabled, causing connection errors until certificates were fetched from the primary node.\n\n- RS123263: Fixed an issue where creating a new role with a specified UID failed with \"A uid is already assigned\" error.\n\n- RS120420: Fixed an issue where `rladmin cluster config` incorrectly included quotes as part of the cipher suite value when updating `control_cipher_suites` configuration.\n\n- RS170611: Fixed an issue where the `generate_self_signed_certs.sh` script incorrectly formatted wildcard certificate entries.\n\n- RS167849: Fixed an issue where `rlutil check` incorrectly reported that existing databases did not exist.\n\n- RS167199: Fixed an issue where the remove node action could become stuck during node decommissioning.\n\n- RS166990: Fixed an issue where install logs were not included in support packages when installation or upgrade operations failed.\n\n- RS166528: Improved error handling when verifying that a data file has been loaded.\n\n- RS162973: Fixed an issue with shard failover where the shard failed to restart because its port was not released quickly enough after it crashed.\n\n- RS166122: Fixed an issue where the actions API could incorrectly report state machine operations as running after they completed.\n\n- RS171579: Fixed an issue where the new UI incorrectly added `default_user: False` when the default_user field was absent, causing connection issues."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":"- [`POST /v1/cluster/actions/change_master`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/actions#post-cluster-action) REST API requests will no longer allow a node that exists but is not finished bootstrapping to become the primary node. Such requests will now return the status code `406 Not Acceptable`.\n\n- Node status now returns the actual provisional RAM and flash values even when the maximum number of shards on the node (`max_redis_servers`) is reached. Previously, the API returned 0 for `provisional_ram_of_node` and `provisional_flash_of_node` when a node reached its shard limit. This change affects REST API node status requests and the `rladmin status nodes` command's output."},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"- LDAP filters for `user_dn_query` and `dn_group_query` now strictly require parentheses to function correctly. Filters that previously worked without parentheses will no longer work after upgrading to Redis Software 8.0.x. For example, you must include the parentheses in `(sAMAccountName=%u)`.\n\n- Redis Software installation script changes:\n\n    - Changed the `--skip-updating-env-path` option to `--update-env-path` when running [`install.sh`](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/install-script).\n\n    - Changed the `skip_updating_env_path` parameter to `update_env_path` in the [installation answers file](https://redis.io/docs/latest/operate/rs/installing-upgrading/install/manage-installation-questions#configure-file-to-answer)."},{"id":"redis-8-breaking-changes","title":"Redis database version 8 breaking changes","role":"content","text":"When new major versions of Redis Open Source change existing commands, upgrading your database to a new version can potentially break some functionality. Before you upgrade, read the provided list of breaking changes that affect Redis Software and update any applications that connect to your database to handle these changes.\n\n#### ACL behavior changes\n\nBefore Redis 8, the existing [ACL](https://redis.io/docs/latest/operate/rs/security/access-control/redis-acl-overview) categories `@read`, `@write`, `@dangerous`, `@admin`, `@slow`, and `@fast` did not include commands for Redis Search and the JSON, time series, and probabilistic data structures.\n\nStarting with Redis 8, Redis includes all Query Engine, JSON, time series, Bloom filter, cuckoo filter, top-k, count-min sketch, and t-digest commands in these existing ACL categories.\n\nAs a result:\n\n- Existing ACL rules such as `+@read +@write` will allow access to more commands than in previous versions of Redis. Here are some examples:\n\n  - A user with `+@read` access will be able to execute `FT.SEARCH`.\n  \n  - A user with `+@write` access will be able to execute `JSON.SET`. \n\n- ACL rules such as `+@all -@write`  will allow access to fewer commands than previous versions of Redis.\n\n  - For example, a user with `+@all -@write` will not be able to execute `JSON.SET`.\n\n  - Explicit inclusion of new [command categories](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl#command-categories) is required to maintain access. The new categories are: `@search`, `@json`, `@timeseries`, `@bloom`, `@cuckoo`, `@topk`, `@cms`, and `@tdigest`.\n\n- ACL rules such as `+@read +JSON.GET` can now be simplified as `+@read` because `JSON.GET` is included in the `@read` category.\n\nNote that the `@all` category did not change, as it always included all the commands.\n\n#### Redis Search changes\n\nThe following changes affect behavior and validation in Redis Search:\n\n- Enforces validation for `LIMIT` arguments (offset must be 0 if limit is 0).\n\n- Enforces parsing rules for `FT.CURSOR READ` and `FT.ALIASADD`.\n\n- Parentheses are now required for exponentiation precedence in `APPLY` expressions.\n\n- Invalid input now returns errors instead of empty results.\n\n- Default values revisited for reducers like `AVG`, `COUNT`, `SUM`, `STDDEV`, `QUANTILE`, and others.\n\n- Updates to scoring (`BM25` is now the default instead of `TF-IDF`).\n\n- Improved handling of expired records, memory constraints, and malformed fields."},{"id":"reserved-ports","title":"Reserved ports","role":"content","text":"Make sure the following ports are open before upgrading Redis Software.\n\nPorts reserved as of Redis Software version 7.22.0:\n\n| Port | Process name | Usage | \n|------|--------------|-------|\n| 3346 | cluster_api_internal | Cluster API internal port |\n| 3351 | cluster_watchdog_grpc_api | Cluster watchdog now supports gRPC |\n| 3352 | grpc_service_mesh | gRPC communication between nodes |\n| 3353 | local_grpc_service_mesh | Local gRPC services |\n| 3354 | grpc_gossip_envoy | gRPC gossip protocol communication between nodes |\n| 3355 | authentication_service | Authentication service internal port |\n\nPorts reserved as of Redis Software version 7.8.2:\n\n| Port | Process name | Usage | \n|------|--------------|-------|\n| 3347 | cert_exporter | Reports cluster certificate metrics |\n| 3348 | process_exporter | Reports process metrics for DMC and Redis processes |\n| 3349 | cluster_wd_exporter | Reports cluster watchdog metrics |\n| 3350 | db_controller | Internode communication |\n| 9091 | node_exporter | Reports host node metrics related to CPU, memory, disk, and more |\n| 9125 | statsd_exporter | Reports push metrics related to the DMC and syncer, and some cluster and node metrics |\n\nSee [Ports and port ranges used by Redis Software](https://redis.io/docs/latest/operate/rs/networking/port-configurations#ports-and-port-ranges-used-by-redis-enterprise-software) for a complete list."},{"id":"deprecations","title":"Deprecations","role":"content","text":"#### API deprecations\n\n- Deprecated the `policy` field for [bootstrap](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bootstrap) REST API requests. Use [`PUT /v1/cluster/policy`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/policy#put-cluster-policy) to change cluster policies after cluster creation instead.\n\n- Deprecated the `module_args` field for [database](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs) REST API requests. Use the new module configuration objects `search`, `timeseries`, and `probabilistic` instead.\n\n#### Redis Search deprecations\n\n- Deprecated commands: `FT.ADD`, `FT.SAFEADD`, `FT.DEL`, `FT.GET`, `FT.MGET`, `FT.SYNADD`, `FT.DROP`, `FT._DROPIFX`, and `FT.CONFIG`.\n\n- Deprecated `FT.SEARCH` options: `GEOFILTER`, `FILTER`, and `NOSTOPWORDS`.\n\n- Deprecated vector search options: `INITIAL_CAP` and `BLOCK_SIZE`.\n\n- Deprecated configuration parameters: `WORKER_THREADS`, `MT_MODE`, `PRIVILEGED_THREADS_NUM`, and `GCSCANSIZE`.\n\n- Deprecated dialects: `DIALECT 1`, `DIALECT 3`, and `DIALECT 4`.\n\n#### Internal monitoring and v1 Prometheus metrics deprecation\n\nThe existing [internal monitoring engine](https://redis.io/docs/latest/operate/rs/monitoring/v1_monitoring) is deprecated. We recommend transitioning to the new [metrics stream engine](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine) for improved performance, enhanced integration capabilities, and modernized metrics streaming.\n\nV1 Prometheus metrics are deprecated but still available. To transition to the new metrics stream engine, either migrate your existing dashboards using [this guide](https://redis.io/docs/latest/operate/rs/references/metrics/prometheus-metrics-v1-to-v2) or use [new preconfigured dashboards](https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise#grafana-dashboards-for-redis-software).\n\nAs part of the transition to the metrics stream engine, some internal cluster manager alerts were deprecated in favor of external monitoring solutions. See the [alerts transition plan](https://redis.io/docs/latest/operate/rs/references/alerts/alerts-v1-to-v2) for guidance."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (8.0.2-17 October release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">48671f14727daeb79a2e30d4f176ce2ed611641530f7f91fab4639572f7e33fc</span> |\n| Ubuntu 22 (amd64) | <span class=\"break-all\">dd3f0fc0ac499f68b8bc155290e86ca1a6686f85312be1058ae3a05c46ec5558</span> |\n| Ubuntu 22 (arm64) | <span class=\"break-all\">0fca0f5a365954fa062fd5b7304666bb147f1f6f63eea93b4d932faab4082edd</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">f707a31101598a88c64fc2cd7eee7266d51ff0e1a90c768584f88e1459d158fc</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">00015c18a6734d243ae82c1046bd320cbb904b78912c9835d287b17d807c1161</span> |\n| Amazon Linux 2 | <span class=\"break-all\">aaeb4c71f6ee4883213909fcface49b442b2f1c536c244159dd5acc2cafed06e</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors.\n\n- RS155734: Endpoint availability metrics do not work as expected due to a calculation error."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Rolling upgrade limitation for clusters with custom or deprecated modules\n\nDue to module handling changes introduced in Redis Software version 8.0, upgrading a cluster that contains custom or deprecated modules, such as RedisGraph and RedisGears v2, can become stuck when adding a new node to the cluster during a rolling upgrade.\n\n#### Module commands limitation during Active-Active database upgrades to Redis 8.0\n\nWhen upgrading an Active-Active database to Redis version 8.0, you cannot use module commands until all Active-Active database instances have been upgraded. Currently, these commands are not blocked automatically.\n\n#### Redis 8.0 database cannot be created with flash\n\nYou cannot create a Redis 8.0 database with flash storage enabled. Create a Redis 8.0 database with RAM-only storage instead, or use Redis 8.2 for flash-enabled (Redis Flex) databases.\n\n#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log."},{"id":"security","title":"Security","role":"security","text":"#### Redis Open Source security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [Redis Open Source](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for Redis Open Source do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in Redis Open Source.\n\nRedis Software 8.0.2-17 supports Redis Open Source 8.2, 8.0, 7.4, 7.2, and 6.2. Below is the list of Redis Open Source CVEs and other security vulnerabilities fixed by version.\n\nRedis 8.2.x:\n\n- (CVE-2025-62507) A user can run the `XACKDEL` command with multiple IDs and trigger a stack buffer overflow, which can potentially lead to remote code execution.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\nRedis 8.0.x:\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\nRedis 7.4.x:\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\nRedis 7.2.x:\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[{"id":"new-features-ex0","language":"sh","code":"GET /v1/bdbs/<database_id>/availability?extend_check=lag&availability_lag_tolerance_ms=100","section_id":"new-features"},{"id":"enhancements-ex0","language":"sh","code":"crdb-cli crdb update --crdb-guid <guid> --update-db-config-modules true","section_id":"enhancements"}]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-8-0-releases/rs-8-0-2-41","title":"Redis Software release notes 8.0.2-41 (November 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-2-41/","summary":"Bug fixes for cluster_wd configuration after restarts, internode encryption certificate storage and retrieval, database creation, Lua script migration issues, and database endpoint assignment after node removal.","aliases":["/operate/rs/release-notes/rs-8-0-releases/rs-8-0-2-tba/"],"tags":["docs","operate","rs"],"last_updated":"2026-10-19T14:11:36-05:00","page_type":"content","content_hash":"a005ee3086c3b748b72320d567a029d60a1acf72f25d41493d02f597d6f14309","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Software version 8.0.2](https://redis.io/downloads/#Redis_Software)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Bug fixes for `cluster_wd` configuration after restarts, internode encryption certificate storage and retrieval, database creation, Lua script migration issues, and database endpoint assignment after node removal"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 8.0.2 includes five Redis database versions: 8.2.1, 8.0.2, 7.4.3, 7.2.7, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 8.2."},{"id":"redis-feature-sets","title":"Redis feature sets","role":"content","text":"Redis Software includes multiple feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 8.2 | RediSearch 8.2<br />RedisJSON 8.2<br />RedisTimeSeries 8.2<br />RedisBloom 8.2<br />See [What's new in Redis 8.2](https://redis.io/docs/latest/develop/whats-new/8-2) and [Redis Open Source 8.2 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.2-release-notes) |\n| 8.0 | RediSearch 8.0<br />RedisJSON 8.0<br />RedisTimeSeries 8.0<br />RedisBloom 8.0<br />See [What's new in Redis 8.0](https://redis.io/docs/latest/develop/whats-new/8-0) and [Redis Open Source 8.0 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.0-release-notes) |\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes) |"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS176280: Fixed an issue where `cluster_wd` reverted to the default configuration after restarting.\n\n- RS176540: Fixed inconsistencies between storage and retrieval APIs for internode encryption certificates, which could cause issues in Kubernetes deployments.\n\n- RS170732: Fixed an issue where database creation could become stuck due to connection issues.\n\n- RS173541: Fixed an issue where Lua scripts were not migrated during database import and sync operations, causing script execution errors on the destination cluster.\n\n- RS172076: Fixed an issue where database endpoints could mistakenly be assigned to a recently removed node."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (8.0.2-41 November release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">a70ac44203766d034f392fec0f8526a9589609544440ecc92a3092432ab28dec</span> |\n| Ubuntu 22 (amd64) | <span class=\"break-all\">4c2201b6eeb0b5c7027d919db9fef091a46e80dfffc8ba0b543275cf2471f1a6</span> |\n| Ubuntu 22 (arm64) | <span class=\"break-all\">bf41407af79a3ef20b3259c3ee32c0945b9f28e11757e93093555a7c52790e2d</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">97cd023348844dc8dc120cf2a6022191d82ca753fa4e85c3e8779927692be352</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">c631b5914d871b9811243c527e47e0f41ed8ea9110c70cb016af6e4731a58e72</span> |\n| Amazon Linux 2 | <span class=\"break-all\">9d3504741ac8dabc17133705f4565e21b6eaea36ea9e3eb1d9f6a5367e74e137</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS131972: Creating an ACL that contains a line break in the Cluster Manager UI can cause shard migration to fail due to ACL errors.\n\n- RS155734: Endpoint availability metrics do not work as expected due to a calculation error."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Rolling upgrade limitation for clusters with custom or deprecated modules\n\nDue to module handling changes introduced in Redis Software version 8.0, upgrading a cluster that contains custom or deprecated modules, such as RedisGraph and RedisGears v2, can become stuck when adding a new node to the cluster during a rolling upgrade.\n\n#### Module commands limitation during Active-Active database upgrades to Redis 8.0\n\nWhen upgrading an Active-Active database to Redis version 8.0, you cannot use module commands until all Active-Active database instances have been upgraded. Currently, these commands are not blocked automatically.\n\n#### Redis 8.0 database cannot be created with flash\n\nYou cannot create a Redis 8.0 database with flash storage enabled. Create a Redis 8.0 database with RAM-only storage instead, or use Redis 8.2 for flash-enabled (Redis Flex) databases.\n\n#### Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log."},{"id":"security","title":"Security","role":"security","text":"#### Redis Open Source security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [Redis Open Source](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for Redis Open Source do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in Redis Open Source.\n\nRedis Software 8.0.2-41 supports Redis Open Source 8.2, 8.0, 7.4, 7.2, and 6.2. Below is the list of Redis Open Source CVEs and other security vulnerabilities fixed by version.\n\nRedis 8.2.x:\n\n- RedisBloom: Restore invalid filter.\n\n- (CVE-2025-62507) A user can run the `XACKDEL` command with multiple IDs and trigger a stack buffer overflow, which can potentially lead to remote code execution.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\nRedis 8.0.x:\n\n- RedisBloom: Restore invalid filter.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\nRedis 7.4.x:\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\nRedis 7.2.x:\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-8-0-releases/rs-8-0-20-108","title":"Redis Software release notes 8.0.20-108 (September 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-20-108/","summary":"Restored Active-Active database synchronization status indicators on the Cluster Manager overview page. Bug fixes for basic authentication when changing a user password through the REST API, database connectivity, and partial upgrades. Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-10-02T11:12:43-05:00","page_type":"content","content_hash":"6ac93df48a8483a39e947ed990441e1b0724cb54d7422555e8ba2f01822ebae4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Software version 8.0.20](https://redis.io/downloads/#Redis_Software)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Restored Active-Active database synchronization status indicators on the Cluster Manager overview page\n\n- Bug fixes for basic authentication when changing a user password through the REST API, database connectivity after a quorum node failure, and partial upgrades\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"enhancements","title":"Enhancements","role":"content","text":"- Restored the color-coded Active-Active database synchronization status indicators on the Cluster Manager overview page, so you can check synchronization health across databases without opening each one."},{"id":"redis-database-versions-and-feature-sets","title":"Redis database versions and feature sets","role":"content","text":"Redis Software version 8.0.20 includes the following Redis database versions: 8.6.2, 8.4.0, 8.2.1, 8.0.2, 7.4.3, 7.2.7, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 8.6.\n\nRedis Software includes multiple feature sets, compatible with different Redis database versions.\n\n\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 8.6 | RediSearch 8.6<br />RedisJSON 8.6<br />RedisTimeSeries 8.6<br />RedisBloom 8.6<br />See [What's new in Redis 8.6](https://redis.io/docs/latest/develop/whats-new/8-6) and [Redis Open Source 8.6 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.6-release-notes) |\n| 8.4 | RediSearch 8.4<br />RedisJSON 8.4<br />RedisTimeSeries 8.4<br />RedisBloom 8.4<br />See [What's new in Redis 8.4](https://redis.io/docs/latest/develop/whats-new/8-4) and [Redis Open Source 8.4 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.4-release-notes) |\n| 8.2 | RediSearch 8.2<br />RedisJSON 8.2<br />RedisTimeSeries 8.2<br />RedisBloom 8.2<br />See [What's new in Redis 8.2](https://redis.io/docs/latest/develop/whats-new/8-2) and [Redis Open Source 8.2 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.2-release-notes) |\n| 8.0 | RediSearch 8.0<br />RedisJSON 8.0<br />RedisTimeSeries 8.0<br />RedisBloom 8.0<br />See [What's new in Redis 8.0](https://redis.io/docs/latest/develop/whats-new/8-0) and [Redis Open Source 8.0 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.0-release-notes) |\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes) |"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS193817: Fixed an issue where the `PUT /v1/users/password` REST API endpoint rejected basic authentication when the credentials used a user's display name.\n\n- RS197092: Fixed an issue where databases became unreachable after a quorum node went down.\n\n- RS213616: Fixed an issue where removing an instance running the new version failed during a partial upgrade."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"openssl-version-on-rhel-9","title":"OpenSSL version on RHEL 9","role":"content","text":"Redis Software versions 8.0.16 through 8.0.20-68 required OpenSSL 3.3 or later on RHEL 9. That requirement no longer applies, because Redis Software is now built against an earlier OpenSSL version.\n\nOn RHEL 9, you no longer need to upgrade the `openssl` and `openssl-libs` packages before you upgrade Redis Software."},{"id":"reserved-ports","title":"Reserved ports","role":"content","text":"Make sure the following ports are open before upgrading Redis Software.\n\nAs of Redis Software version 8.0.20, port 3357 is optional rather than reserved:\n\n| Port | Process name | Usage |\n|------|--------------|-------|\n| 3357 | reconciliation_tree_grpc | Internal communication |\n\nPorts reserved as of Redis Software version 7.22.0:\n\n| Port | Process name | Usage |\n|------|--------------|-------|\n| 3346 | cluster_api_internal | Cluster API internal port |\n| 3351 | cluster_watchdog_grpc_api | Cluster watchdog now supports gRPC |\n| 3352 | grpc_service_mesh | gRPC communication between nodes |\n| 3353 | local_grpc_service_mesh | Local gRPC services |\n| 3354 | grpc_gossip_envoy | gRPC gossip protocol communication between nodes |\n| 3355 | authentication_service | Authentication service internal port |\n\nPorts reserved as of Redis Software version 7.8.2:\n\n| Port | Process name | Usage |\n|------|--------------|-------|\n| 3347 | cert_exporter | Reports cluster certificate metrics |\n| 3348 | process_exporter | Reports process metrics for DMC and Redis processes |\n| 3349 | cluster_wd_exporter | Reports cluster watchdog metrics |\n| 3350 | db_controller | Internode communication |\n| 9091 | node_exporter | Reports host node metrics related to CPU, memory, disk, and more |\n| 9125 | statsd_exporter | Reports push metrics related to the DMC and syncer, and some cluster and node metrics |\n\nSee [Ports and port ranges used by Redis Software](https://redis.io/docs/latest/operate/rs/networking/port-configurations#ports-and-port-ranges-used-by-redis-enterprise-software) for a complete list."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2023<sup>[6](#table-note-6)</sup> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – | – | – |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance.\n\n6. <a name=\"table-note-6\"></a>Amazon Linux 2023 support was added in Redis Software version 8.0.20."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (8.0.20-108 September release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">5453d53b3c8caa51d803e616e98212255f39c565617716eea481a48c4a0e5d00</span> |\n| Ubuntu 22 (amd64) | <span class=\"break-all\">7e981b94c32a93daa32d7829658939bba79884c1c721d7aa5ba3d79d8cb4a707</span> |\n| Ubuntu 22 (arm64) | <span class=\"break-all\">17ddd8187cf133348a07d2026c7568e41c9b03ac31b7fb29543eb367bb36df18</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">2cdb0c401cd1d13af1891cc65e1130762d67d5b023d0bbb7191653379b8d34b9</span> |\n| Red Hat Enterprise Linux (RHEL) 9 (amd64) | <span class=\"break-all\">32cc25ee2e79a2b66d0275410a88911717c9325cbe56836fcdd97dea33299c23</span> |\n| Red Hat Enterprise Linux (RHEL) 9 (arm64) | <span class=\"break-all\">a29d1b7b55f14ba4e460b476f79243044b3e3ca6e4ea95621a82110ee74ce03d</span> |\n| Amazon Linux 2 | <span class=\"break-all\">3bbfb525192d27ecf8589754d96cc545ff9fb34c5265a2d2851c0b308732c654</span> |\n| Amazon Linux 2023 (amd64) | <span class=\"break-all\">bd4cb71e48161f0011d9be9df6224377346b9466899efb00df73f5fe5360a414</span> |\n| Amazon Linux 2023 (arm64) | <span class=\"break-all\">3515add8c27ed732ffea6f2b700657a9212a0a0615b61d732479827797e299b6</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS155734: Endpoint availability metrics do not work as expected due to a calculation error."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Redis Search query failures during rolling upgrades across the 8.4 version boundary\n\nRedis Search queries can fail during a rolling upgrade when a cluster contains shards running Redis versions earlier than 8.4 and shards running Redis version 8.4 or later, due to an internal protocol change introduced in version 8.4. This issue affects only clusters where `parallel_shards_upgrade` has been changed from its default value of `0`. If both conditions apply, expect Redis Search downtime until all nodes are upgraded.\n\n#### Trim ACKED not supported for Active-Active 8.4 databases\n\nFor Active-Active databases running Redis database version 8.4, the `ACKED` option is not supported for trimming commands.\n\n#### Rolling upgrade limitation for clusters with custom or deprecated modules\n\nDue to module handling changes introduced in Redis Software version 8.0, upgrading a cluster that contains custom or deprecated modules, such as RedisGraph and RedisGears v2, can become stuck when adding a new node to the cluster during a rolling upgrade.\n\n#### Module commands limitation during Active-Active database upgrades to Redis 8.0\n\nWhen upgrading an Active-Active database to Redis version 8.0, you cannot use module commands until all Active-Active database instances have been upgraded. Currently, these commands are not blocked automatically.\n\n#### Redis 8.0 database cannot be created with flash\n\nYou cannot create a Redis 8.0 database with flash storage enabled. Create a Redis 8.0 database with RAM-only storage instead, or use Redis 8.2 for flash-enabled (Redis Flex) databases.\n\n#### Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead."},{"id":"security","title":"Security","role":"security","text":"#### Redis Open Source security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [Redis Open Source](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for Redis Open Source do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in Redis Open Source.\n\nRedis Software 8.0.20-108 supports Redis Open Source 8.6, 8.4, 8.2, 8.0, 7.4, 7.2, and 6.2. Below is the list of Redis Open Source CVEs and other security vulnerabilities fixed by version.\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- VSET: Missing `level` cap when loading a serialized HNSW graph may lead to resource exhaustion.\n\n- VSET: Missing validation of the parameter count when loading a serialized vector set may lead to oversized allocation and resource exhaustion.\n\n- Certificate common-name handling can allow an authentication bypass when `tls-auth-clients-user` is set to `CN`.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- VSET: Missing `level` cap when loading a serialized HNSW graph may lead to resource exhaustion.\n\n- VSET: Missing validation of the parameter count when loading a serialized vector set may lead to oversized allocation and resource exhaustion.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- VSET: Missing `level` cap when loading a serialized HNSW graph may lead to resource exhaustion.\n\n- VSET: Missing validation of the parameter count when loading a serialized vector set may lead to oversized allocation and resource exhaustion.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- (CVE-2025-62507) A user can run the `XACKDEL` command with multiple IDs and trigger a stack buffer overflow, which can potentially lead to remote code execution.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\n\n\n\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-8-0-releases/rs-8-0-20-127","title":"Redis Software release notes 8.0.20-127 (October 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-20-127/","summary":"Bug fixes for authentication after just-in-time user provisioning, Google Cloud Storage backup configuration, DMC proxy stability, periodic latency spikes, and cluster scaling. Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-10-19T14:11:26-05:00","page_type":"content","content_hash":"6b82ca0b9dfa92ea9a9ee938fb454d335ba1966111ddf2b6398ac5f53472e1af","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Software version 8.0.20](https://redis.io/downloads/#Redis_Software)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Bug fix for authentication to ACL-only databases after just-in-time (JIT) user provisioning\n\n- Bug fix for Google Cloud Storage backup configuration in the Cluster Manager UI\n\n- Bug fixes for DMC proxy stability\n\n- Bug fixes for periodic latency spikes and cluster scaling\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-database-versions-and-feature-sets","title":"Redis database versions and feature sets","role":"content","text":"Redis Software version 8.0.20 includes the following Redis database versions: 8.6.2, 8.4.0, 8.2.1, 8.0.2, 7.4.3, 7.2.7, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 8.6.\n\nRedis Software includes multiple feature sets, compatible with different Redis database versions.\n\n\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 8.6 | RediSearch 8.6<br />RedisJSON 8.6<br />RedisTimeSeries 8.6<br />RedisBloom 8.6<br />See [What's new in Redis 8.6](https://redis.io/docs/latest/develop/whats-new/8-6) and [Redis Open Source 8.6 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.6-release-notes) |\n| 8.4 | RediSearch 8.4<br />RedisJSON 8.4<br />RedisTimeSeries 8.4<br />RedisBloom 8.4<br />See [What's new in Redis 8.4](https://redis.io/docs/latest/develop/whats-new/8-4) and [Redis Open Source 8.4 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.4-release-notes) |\n| 8.2 | RediSearch 8.2<br />RedisJSON 8.2<br />RedisTimeSeries 8.2<br />RedisBloom 8.2<br />See [What's new in Redis 8.2](https://redis.io/docs/latest/develop/whats-new/8-2) and [Redis Open Source 8.2 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.2-release-notes) |\n| 8.0 | RediSearch 8.0<br />RedisJSON 8.0<br />RedisTimeSeries 8.0<br />RedisBloom 8.0<br />See [What's new in Redis 8.0](https://redis.io/docs/latest/develop/whats-new/8-0) and [Redis Open Source 8.0 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.0-release-notes) |\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes) |"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS192993: Fixed an issue where creating a new user with just-in-time (JIT) provisioning could cause authentication to fail for all databases that use ACL-only authentication.\n\n- RS194421: Fixed periodic traffic drops and latency spikes caused by internal scheduled tasks that all started at the same time.\n\n- RS196137: Fixed an issue where the Cluster Manager did not send the required `private_key` field when configuring Google Cloud Storage (GCS) backups.\n\n- RS211091: Fixed an issue where the cluster could become unresponsive after a newly joined node became the primary node while scaling the cluster.\n\n- RS215608: Fixed an issue where running `dmc-cli list bdb` or `dmc-cli list server` could corrupt memory and cause the DMC proxy to crash.\n\n- RS216045: Fixed an issue where the DMC proxy could crash repeatedly when a client sent a command without key arguments, such as `PUBSUB SHARDNUMSUB` with no channels, in a pipeline with another command."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"openssl-version-on-rhel-9","title":"OpenSSL version on RHEL 9","role":"content","text":"Redis Software versions 8.0.16 through 8.0.20-68 required OpenSSL 3.3 or later on RHEL 9. That requirement no longer applies, because Redis Software is now built against an earlier OpenSSL version.\n\nOn RHEL 9, you no longer need to upgrade the `openssl` and `openssl-libs` packages before you upgrade Redis Software."},{"id":"reserved-ports","title":"Reserved ports","role":"content","text":"Make sure the following ports are open before upgrading Redis Software.\n\nAs of Redis Software version 8.0.20, port 3357 is optional rather than reserved:\n\n| Port | Process name | Usage |\n|------|--------------|-------|\n| 3357 | reconciliation_tree_grpc | Internal communication |\n\nPorts reserved as of Redis Software version 7.22.0:\n\n| Port | Process name | Usage |\n|------|--------------|-------|\n| 3346 | cluster_api_internal | Cluster API internal port |\n| 3351 | cluster_watchdog_grpc_api | Cluster watchdog now supports gRPC |\n| 3352 | grpc_service_mesh | gRPC communication between nodes |\n| 3353 | local_grpc_service_mesh | Local gRPC services |\n| 3354 | grpc_gossip_envoy | gRPC gossip protocol communication between nodes |\n| 3355 | authentication_service | Authentication service internal port |\n\nPorts reserved as of Redis Software version 7.8.2:\n\n| Port | Process name | Usage |\n|------|--------------|-------|\n| 3347 | cert_exporter | Reports cluster certificate metrics |\n| 3348 | process_exporter | Reports process metrics for DMC and Redis processes |\n| 3349 | cluster_wd_exporter | Reports cluster watchdog metrics |\n| 3350 | db_controller | Internode communication |\n| 9091 | node_exporter | Reports host node metrics related to CPU, memory, disk, and more |\n| 9125 | statsd_exporter | Reports push metrics related to the DMC and syncer, and some cluster and node metrics |\n\nSee [Ports and port ranges used by Redis Software](https://redis.io/docs/latest/operate/rs/networking/port-configurations#ports-and-port-ranges-used-by-redis-enterprise-software) for a complete list."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2023<sup>[6](#table-note-6)</sup> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – | – | – |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance.\n\n6. <a name=\"table-note-6\"></a>Amazon Linux 2023 support was added in Redis Software version 8.0.20."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (8.0.20-127 October release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">0719abd5ce45f7a31bc52b48a972d382699cc92b9ef6f120b3c615f0025f344d</span> |\n| Ubuntu 22 (amd64) | <span class=\"break-all\">bf5b62cebe16a2d7f7d25c252de5a3bacb70c927177a1869f5c1257821bee8d8</span> |\n| Ubuntu 22 (arm64) | <span class=\"break-all\">2d893ea4e48391458ed2615f451c114859abe3b9dc8c24beeee1b34c8f9d315b</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">cbf8bcb1f082784a02076e2ffec39b19d3bf18c1a379de219c19faca68797a58</span> |\n| Red Hat Enterprise Linux (RHEL) 9 (amd64) | <span class=\"break-all\">247a906a5f995f678f54feec198156c90fd39e74a54b970fea1b63b94bd78b87</span> |\n| Red Hat Enterprise Linux (RHEL) 9 (arm64) | <span class=\"break-all\">ca13be067048cc93660cffa7e1ed6769853f9503a6425abba350e4c97fa99c48</span> |\n| Amazon Linux 2 | <span class=\"break-all\">1aa364cef82f0fd6a09c0a21cdfc727367f154a666e83edec04ef803b0a00a8e</span> |\n| Amazon Linux 2023 (amd64) | <span class=\"break-all\">4977a09448f7ebd4f1e00046677487e68a48f41e783643dbb9b73768bff42b4e</span> |\n| Amazon Linux 2023 (arm64) | <span class=\"break-all\">c07dc53b1387bc6a2e220e8dacf30bac79af9a94e418ab25030e8ceff90a04a1</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS155734: Endpoint availability metrics do not work as expected due to a calculation error."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Redis Search query failures during rolling upgrades across the 8.4 version boundary\n\nRedis Search queries can fail during a rolling upgrade when a cluster contains shards running Redis versions earlier than 8.4 and shards running Redis version 8.4 or later, due to an internal protocol change introduced in version 8.4. This issue affects only clusters where `parallel_shards_upgrade` has been changed from its default value of `0`. If both conditions apply, expect Redis Search downtime until all nodes are upgraded.\n\n#### Trim ACKED not supported for Active-Active 8.4 databases\n\nFor Active-Active databases running Redis database version 8.4, the `ACKED` option is not supported for trimming commands.\n\n#### Rolling upgrade limitation for clusters with custom or deprecated modules\n\nDue to module handling changes introduced in Redis Software version 8.0, upgrading a cluster that contains custom or deprecated modules, such as RedisGraph and RedisGears v2, can become stuck when adding a new node to the cluster during a rolling upgrade.\n\n#### Module commands limitation during Active-Active database upgrades to Redis 8.0\n\nWhen upgrading an Active-Active database to Redis version 8.0, you cannot use module commands until all Active-Active database instances have been upgraded. Currently, these commands are not blocked automatically.\n\n#### Redis 8.0 database cannot be created with flash\n\nYou cannot create a Redis 8.0 database with flash storage enabled. Create a Redis 8.0 database with RAM-only storage instead, or use Redis 8.2 for flash-enabled (Redis Flex) databases.\n\n#### Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead."},{"id":"security","title":"Security","role":"security","text":"#### Redis Open Source security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [Redis Open Source](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for Redis Open Source do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in Redis Open Source.\n\nRedis Software 8.0.20-127 supports Redis Open Source 8.6, 8.4, 8.2, 8.0, 7.4, 7.2, and 6.2. Below is the list of Redis Open Source CVEs and other security vulnerabilities fixed by version.\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- VSET: Missing `level` cap when loading a serialized HNSW graph may lead to resource exhaustion.\n\n- VSET: Missing validation of the parameter count when loading a serialized vector set may lead to oversized allocation and resource exhaustion.\n\n- Certificate common-name handling can allow an authentication bypass when `tls-auth-clients-user` is set to `CN`.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- VSET: Missing `level` cap when loading a serialized HNSW graph may lead to resource exhaustion.\n\n- VSET: Missing validation of the parameter count when loading a serialized vector set may lead to oversized allocation and resource exhaustion.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- VSET: Missing `level` cap when loading a serialized HNSW graph may lead to resource exhaustion.\n\n- VSET: Missing validation of the parameter count when loading a serialized vector set may lead to oversized allocation and resource exhaustion.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- (CVE-2025-62507) A user can run the `XACKDEL` command with multiple IDs and trigger a stack buffer overflow, which can potentially lead to remote code execution.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\n\n\n\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-8-0-releases/rs-8-0-20-19","title":"Redis Software release notes 8.0.20-19 (May 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-20-19/","summary":"Certificate-based authentication with LDAP group-based authorization. Amazon Linux 2023 support. API enhancements for Active-Active databases and identifying manually stopped database traffic. New search metrics.","tags":["docs","operate","rs"],"last_updated":"2026-10-09T10:01:18-05:00","page_type":"content","content_hash":"5d8b26294aaf1d82952dcac5fe2d7cd4542206d83bb5c600cc000057384c40c0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"**This release has been recalled.** Don't install Redis Software version 8.0.20-19 or upgrade to it. Use [Redis Software version 8.0.20-68](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-20-68) or later instead, which fixes an internal certificate issue that can cause upgrades to fail (RS199373).\n\n\n​[​Redis Software version 8.0.20](https://redis.io/downloads/#Redis_Software) includes API enhancements that warranted a new minor version instead of a maintenance release for version 8.0.18."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Certificate-based authentication with LDAP group-based authorization\n\n- Amazon Linux 2023 support\n\n- API enhancements for Active-Active databases and identifying manually stopped database traffic\n\n- New search metrics"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"new-features","title":"New features","role":"content","text":"#### Certificate-based authentication with LDAP group-based authorization\n\nRedis Software now enables secure client certificate authentication for LDAP users, combining strong authentication with centralized role-based authorization managed through LDAP.\n\nSee [Certificate-based authentication for LDAP](https://redis.io/docs/latest/operate/rs/security/access-control/ldap/certificate-based-authentication) for details and instructions.\n\n#### Amazon Linux 2023 support\n\nRedis Software now supports the Amazon Linux 2023 operating system.\n\n#### Enhancements\n\n- Added a `traffic_manually_disabled` field to the [BDB object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb) to indicate whether database traffic was manually stopped using a [`stop_traffic`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs/actions/stop_traffic) REST API request. [`rladmin status extra all`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status) also conditionally displays a `TRAFFIC_MANUALLY_DISABLED` column if any database's traffic was manually stopped.\n\n- Added a `crdt_supported_module_featureset_version_map` field to the response for REST API requests to [get cluster info](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster#get-cluster). This new field provides aggregated module featureset version information, enabling clients to query which featureset versions are supported by each module across the cluster.\n\n- Exposed new search [metrics](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/prometheus-metrics-v2#shard-metrics)."},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 8.0.20 includes the following Redis database versions: 8.6.2, 8.4.0, 8.2.1, 8.0.2, 7.4.3, 7.2.7, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 8.6."},{"id":"redis-feature-sets","title":"Redis feature sets","role":"content","text":"Redis Software includes multiple feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 8.6 | RediSearch 8.6<br />RedisJSON 8.6<br />RedisTimeSeries 8.6<br />RedisBloom 8.6<br />See [What's new in Redis 8.6](https://redis.io/docs/latest/develop/whats-new/8-6) and [Redis Open Source 8.6 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.6-release-notes) |\n| 8.4 | RediSearch 8.4<br />RedisJSON 8.4<br />RedisTimeSeries 8.4<br />RedisBloom 8.4<br />See [What's new in Redis 8.4](https://redis.io/docs/latest/develop/whats-new/8-4) and [Redis Open Source 8.4 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.4-release-notes) |\n| 8.2 | RediSearch 8.2<br />RedisJSON 8.2<br />RedisTimeSeries 8.2<br />RedisBloom 8.2<br />See [What's new in Redis 8.2](https://redis.io/docs/latest/develop/whats-new/8-2) and [Redis Open Source 8.2 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.2-release-notes) |\n| 8.0 | RediSearch 8.0<br />RedisJSON 8.0<br />RedisTimeSeries 8.0<br />RedisBloom 8.0<br />See [What's new in Redis 8.0](https://redis.io/docs/latest/develop/whats-new/8-0) and [Redis Open Source 8.0 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.0-release-notes) |\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes) |"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS193164: Fixed an issue where the `rladmin cluster join` command would incorrectly print a bootstrap error when replacing a node in a cluster that uses a custom `cnm_https_port`, despite the join operation completing successfully.\n\n- RS192584: Fixed an issue where the installation script failed to skip the DNS port verification check when the `--skip-dns-port-verification` flag was provided.\n\n- RS178757: Simplified the `node_wd` log output to include only the relevant IP addresses and shard roles instead of all shard details when there is a mismatch with the primary shard IP address during replication to reduce excessive logging.\n\n- RS192988: Fixed an issue where `dmcproxy` could experience high CPU utilization when shards reached their maximum client connection limits, which could cause shard migration and failover issues.\n\n- RS192736: Fixed an issue where shards could be left in an invalid role state after brief node failures and recoveries under high CPU load.\n\n- RS192586: Fixed an issue where endpoint configuration operations could fail during cluster upgrades if a primary node failover occurred while an operation was in progress between nodes running different software versions.\n\n- RS192576: Fixed an issue where the `rof_partial_rdb_support` cluster setting for `repl-use-sst-transfer` for Redis Flex databases was only applied to existing shards and not correctly applied to newly created shards.\n\n- RS184302: Fixed an issue where creating an Active-Active database could fail when Append-Only File (AOF) persistence was enabled with the `appendfsync-always` policy.\n\n- RS177923: Fixed an issue that could allow partial diagnostic logs to be read before completion.\n\n- RS187553: Fixed an issue where certain PEM and configuration files were not automatically deleted after the deletion of Active-Active or Replica Of databases or after syncer deactivation, which could lead to false certificate expiration alerts.\n\n- RS170893: Limited `cluster_wd` metrics exporter exposure to localhost."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"openssl-version-on-rhel-9","title":"OpenSSL version on RHEL 9","role":"content","text":"On RHEL 9, Redis Software version 8.0.16 and later requires OpenSSL 3.3 or later. Other supported platforms are unaffected.\n\nRedis Software is built against the OpenSSL 3.3 runtime included in recent RHEL 9 minor releases. Nodes on an earlier RHEL 9 minor release don't meet this requirement. For example, RHEL 9.6 ships OpenSSL 3.2.2.\n\nThe pre-upgrade checks don't detect an incompatible OpenSSL version, so an upgrade can pass validation and then fail partway through. This can leave the management services on the node in a failed state.\n\nBefore you upgrade, check the OpenSSL version on each node:\n\n[code example]\n\nIf the version is earlier than 3.3.0, upgrade the `openssl` and `openssl-libs` packages before you upgrade Redis Software."},{"id":"reserved-ports","title":"Reserved ports","role":"content","text":"Make sure the following ports are open before upgrading Redis Software.\n\nPorts reserved as of Redis Software version 8.0.18:\n\n| Port | Process name | Usage |\n|------|--------------|-------|\n| 3357 | reconciliation_tree_grpc | Internal communication |\n\nPorts reserved as of Redis Software version 7.22.0:\n\n| Port | Process name | Usage |\n|------|--------------|-------|\n| 3346 | cluster_api_internal | Cluster API internal port |\n| 3351 | cluster_watchdog_grpc_api | Cluster watchdog now supports gRPC |\n| 3352 | grpc_service_mesh | gRPC communication between nodes |\n| 3353 | local_grpc_service_mesh | Local gRPC services |\n| 3354 | grpc_gossip_envoy | gRPC gossip protocol communication between nodes |\n| 3355 | authentication_service | Authentication service internal port |\n\nPorts reserved as of Redis Software version 7.8.2:\n\n| Port | Process name | Usage |\n|------|--------------|-------|\n| 3347 | cert_exporter | Reports cluster certificate metrics |\n| 3348 | process_exporter | Reports process metrics for DMC and Redis processes |\n| 3349 | cluster_wd_exporter | Reports cluster watchdog metrics |\n| 3350 | db_controller | Internode communication |\n| 9091 | node_exporter | Reports host node metrics related to CPU, memory, disk, and more |\n| 9125 | statsd_exporter | Reports push metrics related to the DMC and syncer, and some cluster and node metrics |\n\nSee [Ports and port ranges used by Redis Software](https://redis.io/docs/latest/operate/rs/networking/port-configurations#ports-and-port-ranges-used-by-redis-enterprise-software) for a complete list."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2023<sup>[6](#table-note-6)</sup> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – | – | – |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance.\n\n6. <a name=\"table-note-6\"></a>Amazon Linux 2023 support was added in Redis Software version 8.0.20."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (8.0.20-19 May release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">5850da6ed6100ed691794cc4f30dd7a5fb50f8938329a2f7dcf246dd39accfc0</span> |\n| Ubuntu 22 (amd64) | <span class=\"break-all\">7851a6f71ec3b7edf8d9c32a5c67e6eed9dd169e8cb292d34f339dd9ad8ae33a</span> |\n| Ubuntu 22 (arm64) | <span class=\"break-all\">7d05b15dcf50528b822f87932cd025e2099e3e49a4577f124dc7308f94e66671</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">7808eca31394e2c2e174bfd5f505ca2aea4a9c578638739cd1a06da9684ce13d</span> |\n| Red Hat Enterprise Linux (RHEL) 9 (amd64) | <span class=\"break-all\">c88fac563718dddd433cb945cf31b765316b2d43783b3a3c43b8cf4c8bf25896</span> |\n| Red Hat Enterprise Linux (RHEL) 9 (arm64) | <span class=\"break-all\">e38dff701b02a397f5bea045ba2b03e18c37b24e97ce21ebcd09a4d9a8802fe7</span> |\n| Amazon Linux 2 | <span class=\"break-all\">45ddf6cb71e3ff7271ac1c0c5f3b3a524263f45a90714aef619a32072155eae8</span> |\n| Amazon Linux 2023 (amd64) | <span class=\"break-all\">b0da0f22f9b1f953ef10ff3cb59b0cf64aff5390dd203c7782d416c3eb591eae</span> |\n| Amazon Linux 2023 (arm64) | <span class=\"break-all\">4c397005a239eee1a4729d8ed13e76c738e61efbf4f0594208cc1786565ddbfa</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS196225: After upgrading to Redis Software version 8.0.x, previously working LDAP filters that use an `OR` clause to match multiple attributes can fail to find a unique DN for some users.\n\n- RS155734: Endpoint availability metrics do not work as expected due to a calculation error."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Redis Search query failures during rolling upgrades across the 8.4 version boundary\n\nRedis Search queries can fail during a rolling upgrade when a cluster contains shards running Redis versions earlier than 8.4 and shards running Redis version 8.4 or later, due to an internal protocol change introduced in version 8.4. This issue affects only clusters where `parallel_shards_upgrade` has been changed from its default value of `0`. If both conditions apply, expect Redis Search downtime until all nodes are upgraded.\n\n#### Trim ACKED not supported for Active-Active 8.4 databases\n\nFor Active-Active databases running Redis database version 8.4, the `ACKED` option is not supported for trimming commands.\n\n#### Rolling upgrade limitation for clusters with custom or deprecated modules\n\nDue to module handling changes introduced in Redis Software version 8.0, upgrading a cluster that contains custom or deprecated modules, such as RedisGraph and RedisGears v2, can become stuck when adding a new node to the cluster during a rolling upgrade.\n\n#### Module commands limitation during Active-Active database upgrades to Redis 8.0\n\nWhen upgrading an Active-Active database to Redis version 8.0, you cannot use module commands until all Active-Active database instances have been upgraded. Currently, these commands are not blocked automatically.\n\n#### Redis 8.0 database cannot be created with flash\n\nYou cannot create a Redis 8.0 database with flash storage enabled. Create a Redis 8.0 database with RAM-only storage instead, or use Redis 8.2 for flash-enabled (Redis Flex) databases.\n\n#### Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead."},{"id":"security","title":"Security","role":"security","text":"Redis Software 8.0.20-19 includes pyOpenSSL 26.0.0, which fixes a buffer overflow in the DTLS cookie-generation callback (CVE-2026-27459 / GHSA-5pwr-322w-8jr4).\n\n#### Redis Open Source security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [Redis Open Source](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for Redis Open Source do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in Redis Open Source.\n\nRedis Software 8.0.20-19 supports Redis Open Source 8.6, 8.4, 8.2, 8.0, 7.4, 7.2, and 6.2. Below is the list of Redis Open Source CVEs and other security vulnerabilities fixed by version.\n\nRedis 8.6.x:\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\nRedis 8.4.x:\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\nRedis 8.2.x:\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- (CVE-2025-62507) A user can run the `XACKDEL` command with multiple IDs and trigger a stack buffer overflow, which can potentially lead to remote code execution.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\nRedis 8.0.x:\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\nRedis 7.4.x:\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\nRedis 7.2.x:\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[{"id":"openssl-version-on-rhel-9-ex0","language":"sh","code":"openssl version","section_id":"openssl-version-on-rhel-9"}]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-8-0-releases/rs-8-0-20-44","title":"Redis Software release notes 8.0.20-44 (June 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-20-44/","summary":"Enabled `client_eviction` by default to fix an issue where a massive volume of traffic directed at a single hot key could cause high latency and out-of-memory errors. Bug fixes for Active-Active databases, LDAP, and ACLs. Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-10-09T10:01:18-05:00","page_type":"content","content_hash":"e4857c566e7e6c0b7d983669ef93fd5725adbb85b3c78433653df4c738414e7d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"**This release has been recalled.** Don't install Redis Software version 8.0.20-44 or upgrade to it. Use [Redis Software version 8.0.20-68](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-20-68) or later instead, which fixes an internal certificate issue that can cause upgrades to fail (RS199373).\n\n\nThis is a maintenance release for ​[​Redis Software version 8.0.20](https://redis.io/downloads/#Redis_Software)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Enabled `client_eviction` by default to fix an issue where a massive volume of traffic directed at a single hot key could cause high latency and out-of-memory errors\n\n- Bug fixes for Active-Active databases\n\n- Bug fixes for LDAP and ACLs\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"enhancements","title":"Enhancements","role":"content","text":"- Enabled `client_eviction` by default to fix an issue where a massive volume of traffic directed at a single hot key could cause high latency and out-of-memory errors."},{"id":"redis-database-versions-and-feature-sets","title":"Redis database versions and feature sets","role":"content","text":"Redis Software version 8.0.20 includes the following Redis database versions: 8.6.2, 8.4.0, 8.2.1, 8.0.2, 7.4.3, 7.2.7, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 8.6.\n\nRedis Software includes multiple feature sets, compatible with different Redis database versions.\n\n\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 8.6 | RediSearch 8.6<br />RedisJSON 8.6<br />RedisTimeSeries 8.6<br />RedisBloom 8.6<br />See [What's new in Redis 8.6](https://redis.io/docs/latest/develop/whats-new/8-6) and [Redis Open Source 8.6 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.6-release-notes) |\n| 8.4 | RediSearch 8.4<br />RedisJSON 8.4<br />RedisTimeSeries 8.4<br />RedisBloom 8.4<br />See [What's new in Redis 8.4](https://redis.io/docs/latest/develop/whats-new/8-4) and [Redis Open Source 8.4 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.4-release-notes) |\n| 8.2 | RediSearch 8.2<br />RedisJSON 8.2<br />RedisTimeSeries 8.2<br />RedisBloom 8.2<br />See [What's new in Redis 8.2](https://redis.io/docs/latest/develop/whats-new/8-2) and [Redis Open Source 8.2 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.2-release-notes) |\n| 8.0 | RediSearch 8.0<br />RedisJSON 8.0<br />RedisTimeSeries 8.0<br />RedisBloom 8.0<br />See [What's new in Redis 8.0](https://redis.io/docs/latest/develop/whats-new/8-0) and [Redis Open Source 8.0 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.0-release-notes) |\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes) |"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS185386: Enabled `client_eviction` by default to fix an issue where a massive volume of traffic directed at a single hot key could cause high latency and out-of-memory errors.\n\n- RS190875: Fixed an issue where ciphers that use SHA-1 could not be removed when FIPS mode was enabled. FIPS mode now excludes SHA and CHACHA20 from `control_cipher_suites`.\n\n- RS192582: Fixed an issue where the creation of Redis 8.x Active-Active databases could fail when no modules were specified due to missing module semantic versions in requests.\n\n- RS194243: Fixed a memory leak in the Active-Active database syncer that could occur when a participating cluster became unavailable but was not removed from the CRDB configuration.\n\n- RS195089: Fixed an issue where a major reconfiguration of an Active-Active database could cause an unnecessary full synchronization across all participating clusters.\n\n- RS196225: Fixed an issue where LDAP filters that use an `OR` clause to match multiple attributes could fail to find a unique DN for some users after upgrading to Redis Software version 8.0.x.\n\n- RS196954: Fixed an issue where a user configuration could become corrupted if it was deleted and updated concurrently, which broke ACL authentication for all databases on the cluster and prevented users from connecting.\n\n- RS199012: Fixed a memory leak in `dmcproxy` that could occur when a multi-key command received a local `MOVED` or `CROSSSLOT` response.\n\n- RS199077: Fixed an issue where orphaned resource manager tasks could remain stuck in a cancelling state."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"openssl-version-on-rhel-9","title":"OpenSSL version on RHEL 9","role":"content","text":"On RHEL 9, Redis Software version 8.0.16 and later requires OpenSSL 3.3 or later. Other supported platforms are unaffected.\n\nRedis Software is built against the OpenSSL 3.3 runtime included in recent RHEL 9 minor releases. Nodes on an earlier RHEL 9 minor release don't meet this requirement. For example, RHEL 9.6 ships OpenSSL 3.2.2.\n\nThe pre-upgrade checks don't detect an incompatible OpenSSL version, so an upgrade can pass validation and then fail partway through. This can leave the management services on the node in a failed state.\n\nBefore you upgrade, check the OpenSSL version on each node:\n\n[code example]\n\nIf the version is earlier than 3.3.0, upgrade the `openssl` and `openssl-libs` packages before you upgrade Redis Software."},{"id":"reserved-ports","title":"Reserved ports","role":"content","text":"Make sure the following ports are open before upgrading Redis Software.\n\nThe following port was added as a reserved port in Redis Software version 8.0.18; however, it is now optional instead of reserved in this release:\n\n| Port | Process name | Usage |\n|------|--------------|-------|\n| 3357 | reconciliation_tree_grpc | Internal communication |\n\nPorts reserved as of Redis Software version 7.22.0:\n\n| Port | Process name | Usage |\n|------|--------------|-------|\n| 3346 | cluster_api_internal | Cluster API internal port |\n| 3351 | cluster_watchdog_grpc_api | Cluster watchdog now supports gRPC |\n| 3352 | grpc_service_mesh | gRPC communication between nodes |\n| 3353 | local_grpc_service_mesh | Local gRPC services |\n| 3354 | grpc_gossip_envoy | gRPC gossip protocol communication between nodes |\n| 3355 | authentication_service | Authentication service internal port |\n\nPorts reserved as of Redis Software version 7.8.2:\n\n| Port | Process name | Usage |\n|------|--------------|-------|\n| 3347 | cert_exporter | Reports cluster certificate metrics |\n| 3348 | process_exporter | Reports process metrics for DMC and Redis processes |\n| 3349 | cluster_wd_exporter | Reports cluster watchdog metrics |\n| 3350 | db_controller | Internode communication |\n| 9091 | node_exporter | Reports host node metrics related to CPU, memory, disk, and more |\n| 9125 | statsd_exporter | Reports push metrics related to the DMC and syncer, and some cluster and node metrics |\n\nSee [Ports and port ranges used by Redis Software](https://redis.io/docs/latest/operate/rs/networking/port-configurations#ports-and-port-ranges-used-by-redis-enterprise-software) for a complete list."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2023<sup>[6](#table-note-6)</sup> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – | – | – |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance.\n\n6. <a name=\"table-note-6\"></a>Amazon Linux 2023 support was added in Redis Software version 8.0.20."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (8.0.20-44 June release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">5ec8fe4fcbafe4a164e37fb63e3f46213f1d6b2fc8a0aeb3eacc9b81507f5aae</span> |\n| Ubuntu 22 (amd64) | <span class=\"break-all\">955fd01910b8029f181dc122c454881c4c053a61fe041cbec58c426558da3172</span> |\n| Ubuntu 22 (arm64) | <span class=\"break-all\">fcb8dae0cec60ca38f6ba51b42b387b51227ad38a6b07de14774ad1ca79b73f6</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">01d1a6497236ff2b69cdf414a84d9ad84434dd385c33c5dd6f88258bb410a95f</span> |\n| Red Hat Enterprise Linux (RHEL) 9 (amd64) | <span class=\"break-all\">b25df42b067176be8b45e26f90189c8976ca9a7f16441ae22f69be411e4bf867</span> |\n| Red Hat Enterprise Linux (RHEL) 9 (arm64) | <span class=\"break-all\">c2e09d8aec20d8cbb4df3ddc6e4437b9a7a0f744d00dcc260d95b22d1f73e4a2</span> |\n| Amazon Linux 2 | <span class=\"break-all\">8d5022802449461488cce1afdfc91d600749d6c0b637a602a6b021566b7298ef</span> |\n| Amazon Linux 2023 (amd64) | <span class=\"break-all\">fa1f61dfb46c03988456c6641ee47c5918f4e20e008d81430972f05df0f1c8eb</span> |\n| Amazon Linux 2023 (arm64) | <span class=\"break-all\">bbc58c8d6f7b8fd866c4db188a7d4ab29b9972e900cc06c0dcd698cfafc90ecf</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS155734: Endpoint availability metrics do not work as expected due to a calculation error."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Redis Search query failures during rolling upgrades across the 8.4 version boundary\n\nRedis Search queries can fail during a rolling upgrade when a cluster contains shards running Redis versions earlier than 8.4 and shards running Redis version 8.4 or later, due to an internal protocol change introduced in version 8.4. This issue affects only clusters where `parallel_shards_upgrade` has been changed from its default value of `0`. If both conditions apply, expect Redis Search downtime until all nodes are upgraded.\n\n#### Trim ACKED not supported for Active-Active 8.4 databases\n\nFor Active-Active databases running Redis database version 8.4, the `ACKED` option is not supported for trimming commands.\n\n#### Rolling upgrade limitation for clusters with custom or deprecated modules\n\nDue to module handling changes introduced in Redis Software version 8.0, upgrading a cluster that contains custom or deprecated modules, such as RedisGraph and RedisGears v2, can become stuck when adding a new node to the cluster during a rolling upgrade.\n\n#### Module commands limitation during Active-Active database upgrades to Redis 8.0\n\nWhen upgrading an Active-Active database to Redis version 8.0, if you add a module to the database during the upgrade, you cannot use that module's commands until all Active-Active database instances have been upgraded. Currently, these commands are not blocked automatically. This limitation applies only to modules added during the upgrade; it does not apply if the database already had modules configured before the upgrade.\n\n#### Redis 8.0 database cannot be created with flash\n\nYou cannot create a Redis 8.0 database with flash storage enabled. Create a Redis 8.0 database with RAM-only storage instead, or use Redis 8.2 for flash-enabled (Redis Flex) databases.\n\n#### Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead."},{"id":"security","title":"Security","role":"security","text":"#### Redis Open Source security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [Redis Open Source](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for Redis Open Source do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in Redis Open Source.\n\nRedis Software 8.0.20-44 supports Redis Open Source 8.6, 8.4, 8.2, 8.0, 7.4, 7.2, and 6.2. Below is the list of Redis Open Source CVEs and other security vulnerabilities fixed by version.\n\n\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n\n\n\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n\n\n\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- (CVE-2025-62507) A user can run the `XACKDEL` command with multiple IDs and trigger a stack buffer overflow, which can potentially lead to remote code execution.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n\n\n\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n\n\n\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n\n\n\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\n\n\n\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\n\n\n\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[{"id":"openssl-version-on-rhel-9-ex0","language":"sh","code":"openssl version","section_id":"openssl-version-on-rhel-9"}]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-8-0-releases/rs-8-0-20-68","title":"Redis Software release notes 8.0.20-68 (July 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-20-68/","summary":"Bug fixes for `-MOVED` response metrics visibility, incomplete cleanup of stale Redis processes, internode encryption certificates, high CPU usage due to excessive heartbeat logging, and certificate-based authentication for LDAP blocking local user authentication. Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-10-09T10:01:18-05:00","page_type":"content","content_hash":"799c9bb0e9349b2bd27e4c615f51b8cb1bd76e3956a3a2f0f3cc2950cc618d0a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Software version 8.0.20](https://redis.io/downloads/#Redis_Software)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Bug fixes for `-MOVED` response metrics visibility, incomplete cleanup of stale Redis processes, internode encryption certificates, high CPU usage due to excessive heartbeat logging, and certificate-based authentication for LDAP blocking local user authentication\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-database-versions-and-feature-sets","title":"Redis database versions and feature sets","role":"content","text":"Redis Software version 8.0.20 includes the following Redis database versions: 8.6.2, 8.4.0, 8.2.1, 8.0.2, 7.4.3, 7.2.7, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 8.6.\n\nRedis Software includes multiple feature sets, compatible with different Redis database versions.\n\n\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 8.6 | RediSearch 8.6<br />RedisJSON 8.6<br />RedisTimeSeries 8.6<br />RedisBloom 8.6<br />See [What's new in Redis 8.6](https://redis.io/docs/latest/develop/whats-new/8-6) and [Redis Open Source 8.6 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.6-release-notes) |\n| 8.4 | RediSearch 8.4<br />RedisJSON 8.4<br />RedisTimeSeries 8.4<br />RedisBloom 8.4<br />See [What's new in Redis 8.4](https://redis.io/docs/latest/develop/whats-new/8-4) and [Redis Open Source 8.4 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.4-release-notes) |\n| 8.2 | RediSearch 8.2<br />RedisJSON 8.2<br />RedisTimeSeries 8.2<br />RedisBloom 8.2<br />See [What's new in Redis 8.2](https://redis.io/docs/latest/develop/whats-new/8-2) and [Redis Open Source 8.2 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.2-release-notes) |\n| 8.0 | RediSearch 8.0<br />RedisJSON 8.0<br />RedisTimeSeries 8.0<br />RedisBloom 8.0<br />See [What's new in Redis 8.0](https://redis.io/docs/latest/develop/whats-new/8-0) and [Redis Open Source 8.0 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.0-release-notes) |\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes) |"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS159953: Fixed an issue where `-MOVED` responses were counted as read or write operations instead of errors, so requests sent to the wrong shard or cluster slot were not visible as errors in database metrics.\n\n- RS191347: Fixed an issue with Active-Active, Auto Tiering, and Flex databases where incomplete cleanup of stale Redis processes after shard migration and node restarts could cause shard port conflicts and prevent turning maintenance mode off.\n\n- RS194253: Fixed an issue where intermediate CA certificates were not added to the trust store when uploading a certificate chain with three or more levels for internode encryption, which caused TLS verification failures and cluster health degradation.\n\n- RS197867: Fixed an issue where excessive heartbeat logging could cause high CPU usage.\n\n- RS199373: Fixed an issue where internal certificate rotation could time out after writing a new private key but before installing the matching certificate. The resulting key and certificate mismatch caused errors such as `[X509: KEY_VALUES_MISMATCH] key values mismatch`, could prevent components from connecting to the cluster configuration store, and could cause upgrades to fail.\n\n- RS203277: Fixed an issue that prevented local users from authenticating with a username and password when certificate-based authentication for LDAP was enabled."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"openssl-version-on-rhel-9","title":"OpenSSL version on RHEL 9","role":"content","text":"On RHEL 9, Redis Software version 8.0.16 and later requires OpenSSL 3.3 or later. Other supported platforms are unaffected.\n\nRedis Software is built against the OpenSSL 3.3 runtime included in recent RHEL 9 minor releases. Nodes on an earlier RHEL 9 minor release don't meet this requirement. For example, RHEL 9.6 ships OpenSSL 3.2.2.\n\nThe pre-upgrade checks don't detect an incompatible OpenSSL version, so an upgrade can pass validation and then fail partway through. This can leave the management services on the node in a failed state.\n\nBefore you upgrade, check the OpenSSL version on each node:\n\n[code example]\n\nIf the version is earlier than 3.3.0, upgrade the `openssl` and `openssl-libs` packages before you upgrade Redis Software."},{"id":"reserved-ports","title":"Reserved ports","role":"content","text":"Make sure the following ports are open before upgrading Redis Software.\n\nAs of Redis Software version 8.0.20, port 3357 is optional rather than reserved:\n\n| Port | Process name | Usage |\n|------|--------------|-------|\n| 3357 | reconciliation_tree_grpc | Internal communication |\n\nPorts reserved as of Redis Software version 7.22.0:\n\n| Port | Process name | Usage |\n|------|--------------|-------|\n| 3346 | cluster_api_internal | Cluster API internal port |\n| 3351 | cluster_watchdog_grpc_api | Cluster watchdog now supports gRPC |\n| 3352 | grpc_service_mesh | gRPC communication between nodes |\n| 3353 | local_grpc_service_mesh | Local gRPC services |\n| 3354 | grpc_gossip_envoy | gRPC gossip protocol communication between nodes |\n| 3355 | authentication_service | Authentication service internal port |\n\nPorts reserved as of Redis Software version 7.8.2:\n\n| Port | Process name | Usage |\n|------|--------------|-------|\n| 3347 | cert_exporter | Reports cluster certificate metrics |\n| 3348 | process_exporter | Reports process metrics for DMC and Redis processes |\n| 3349 | cluster_wd_exporter | Reports cluster watchdog metrics |\n| 3350 | db_controller | Internode communication |\n| 9091 | node_exporter | Reports host node metrics related to CPU, memory, disk, and more |\n| 9125 | statsd_exporter | Reports push metrics related to the DMC and syncer, and some cluster and node metrics |\n\nSee [Ports and port ranges used by Redis Software](https://redis.io/docs/latest/operate/rs/networking/port-configurations#ports-and-port-ranges-used-by-redis-enterprise-software) for a complete list."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2023<sup>[6](#table-note-6)</sup> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – | – | – |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance.\n\n6. <a name=\"table-note-6\"></a>Amazon Linux 2023 support was added in Redis Software version 8.0.20."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (8.0.20-68 July release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">4b0b29d28003dbec150416272dbdc55e8b3d87730a9338819b5c01befa843a19</span> |\n| Ubuntu 22 (amd64) | <span class=\"break-all\">6b15d294e70a99febf7bc6e680d59a1a7a7d9fdd36d4645dcd7e53f20367dec7</span> |\n| Ubuntu 22 (arm64) | <span class=\"break-all\">8d1560c4cf8f6e75f4e0afc6b02b4aaaec254d42b48ddc1a84e077005e20dab1</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">8ac4882357445d0f90391cd07021c290e01e3b77b90e25edc73f787da4160fba</span> |\n| Red Hat Enterprise Linux (RHEL) 9 (amd64) | <span class=\"break-all\">72006b3cb6ea1c02c4c8a496a4cf0f6782dd9363b75bbe592adaaba5113a131a</span> |\n| Red Hat Enterprise Linux (RHEL) 9 (arm64) | <span class=\"break-all\">a477d6acc0e08479b263137a377d1e241af3fa2fa5f34d77f96e25d905ed007e</span> |\n| Amazon Linux 2 | <span class=\"break-all\">40a2a1d584508a6a93d7d6bfc4d12e11fa3feaba496ab587a8ac862cd8e44725</span> |\n| Amazon Linux 2023 (amd64) | <span class=\"break-all\">506d13bd0911e7908a426685d882e81d432c269d1b654df5edc00a258e4e19f1</span> |\n| Amazon Linux 2023 (arm64) | <span class=\"break-all\">22d829e3c928ae083db46cfc5f62217957d14d1dabc688a081c6705ee96b7b05</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS155734: Endpoint availability metrics do not work as expected due to a calculation error."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Redis Search query failures during rolling upgrades across the 8.4 version boundary\n\nRedis Search queries can fail during a rolling upgrade when a cluster contains shards running Redis versions earlier than 8.4 and shards running Redis version 8.4 or later, due to an internal protocol change introduced in version 8.4. This issue affects only clusters where `parallel_shards_upgrade` has been changed from its default value of `0`. If both conditions apply, expect Redis Search downtime until all nodes are upgraded.\n\n#### Trim ACKED not supported for Active-Active 8.4 databases\n\nFor Active-Active databases running Redis database version 8.4, the `ACKED` option is not supported for trimming commands.\n\n#### Rolling upgrade limitation for clusters with custom or deprecated modules\n\nDue to module handling changes introduced in Redis Software version 8.0, upgrading a cluster that contains custom or deprecated modules, such as RedisGraph and RedisGears v2, can become stuck when adding a new node to the cluster during a rolling upgrade.\n\n#### Module commands limitation during Active-Active database upgrades to Redis 8.0\n\nWhen upgrading an Active-Active database to Redis version 8.0, you cannot use module commands until all Active-Active database instances have been upgraded. Currently, these commands are not blocked automatically.\n\n#### Redis 8.0 database cannot be created with flash\n\nYou cannot create a Redis 8.0 database with flash storage enabled. Create a Redis 8.0 database with RAM-only storage instead, or use Redis 8.2 for flash-enabled (Redis Flex) databases.\n\n#### Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead."},{"id":"security","title":"Security","role":"security","text":"#### Redis Open Source security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [Redis Open Source](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for Redis Open Source do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in Redis Open Source.\n\nRedis Software 8.0.20-68 supports Redis Open Source 8.6, 8.4, 8.2, 8.0, 7.4, 7.2, and 6.2. Below is the list of Redis Open Source CVEs and other security vulnerabilities fixed by version.\n\n\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n\n\n\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n\n\n\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- (CVE-2025-62507) A user can run the `XACKDEL` command with multiple IDs and trigger a stack buffer overflow, which can potentially lead to remote code execution.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n\n\n\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n\n\n\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n\n\n\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\n\n\n\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\n\n\n\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[{"id":"openssl-version-on-rhel-9-ex0","language":"sh","code":"openssl version","section_id":"openssl-version-on-rhel-9"}]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-8-0-releases/rs-8-0-20-96","title":"Redis Software release notes 8.0.20-96 (August 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-20-96/","summary":"Bug fixes for Active-Active database upgrades and synchronization, internode encryption certificates, upgrades on RHEL 9 with an earlier OpenSSL version, and metrics exporter startup. Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"721185f0ce0d30794ab22dd076447906e91c59f71d0f5b18c40ba8fb6394592e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Software version 8.0.20](https://redis.io/downloads/#Redis_Software)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Bug fixes for Active-Active database upgrades and synchronization, internode encryption certificates, upgrades on RHEL 9 with an earlier OpenSSL version, and metrics exporter startup\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-database-versions-and-feature-sets","title":"Redis database versions and feature sets","role":"content","text":"Redis Software version 8.0.20 includes the following Redis database versions: 8.6.2, 8.4.0, 8.2.1, 8.0.2, 7.4.3, 7.2.7, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 8.6.\n\nRedis Software includes multiple feature sets, compatible with different Redis database versions.\n\n\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 8.6 | RediSearch 8.6<br />RedisJSON 8.6<br />RedisTimeSeries 8.6<br />RedisBloom 8.6<br />See [What's new in Redis 8.6](https://redis.io/docs/latest/develop/whats-new/8-6) and [Redis Open Source 8.6 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.6-release-notes) |\n| 8.4 | RediSearch 8.4<br />RedisJSON 8.4<br />RedisTimeSeries 8.4<br />RedisBloom 8.4<br />See [What's new in Redis 8.4](https://redis.io/docs/latest/develop/whats-new/8-4) and [Redis Open Source 8.4 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.4-release-notes) |\n| 8.2 | RediSearch 8.2<br />RedisJSON 8.2<br />RedisTimeSeries 8.2<br />RedisBloom 8.2<br />See [What's new in Redis 8.2](https://redis.io/docs/latest/develop/whats-new/8-2) and [Redis Open Source 8.2 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.2-release-notes) |\n| 8.0 | RediSearch 8.0<br />RedisJSON 8.0<br />RedisTimeSeries 8.0<br />RedisBloom 8.0<br />See [What's new in Redis 8.0](https://redis.io/docs/latest/develop/whats-new/8-0) and [Redis Open Source 8.0 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.0-release-notes) |\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes) |"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS196656: Fixed an issue where upgrading Redis Software failed partway through on RHEL 9 nodes running an OpenSSL version earlier than 3.3.\n\n- RS197912: Fixed an issue where the self-upgrade status for an Active-Active database could stay non-final until the API client timed out instead of reporting a final outcome.\n\n- RS199887: Fixed an issue where some wrapper scripts relied on the installation adding the `bin` directory to `PATH`, which prevented the metrics exporters from starting when the installation was configured not to modify the environment.\n\n- RS208976: Fixed an issue where an Active-Active database upgrade could stall during post-upgrade verification when a shard was still loading its dataset, which left the feature set version out of sync and the database stuck in a pending state.\n\n- RS208996: Fixed an issue where applying a custom internode encryption certificate restarted the heartbeat service and made the cluster management API on port 9443 unreachable on all nodes for several minutes.\n\n- RS209759: Fixed an issue where an unrecognized configuration change in the Active-Active (CRDB) syncer triggered a full synchronization instead of being ignored.\n\n- RS209889: Fixed an issue where an Active-Active database resent the full dataset instead of only changes during a full state sync when an internal version clock exceeded 32 bits."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"openssl-version-on-rhel-9","title":"OpenSSL version on RHEL 9","role":"content","text":"Redis Software versions 8.0.16 through 8.0.20-68 required OpenSSL 3.3 or later on RHEL 9. That requirement no longer applies, because Redis Software is now built against an earlier OpenSSL version.\n\nOn RHEL 9, you no longer need to upgrade the `openssl` and `openssl-libs` packages before you upgrade Redis Software."},{"id":"reserved-ports","title":"Reserved ports","role":"content","text":"Make sure the following ports are open before upgrading Redis Software.\n\nAs of Redis Software version 8.0.20, port 3357 is optional rather than reserved:\n\n| Port | Process name | Usage |\n|------|--------------|-------|\n| 3357 | reconciliation_tree_grpc | Internal communication |\n\nPorts reserved as of Redis Software version 7.22.0:\n\n| Port | Process name | Usage |\n|------|--------------|-------|\n| 3346 | cluster_api_internal | Cluster API internal port |\n| 3351 | cluster_watchdog_grpc_api | Cluster watchdog now supports gRPC |\n| 3352 | grpc_service_mesh | gRPC communication between nodes |\n| 3353 | local_grpc_service_mesh | Local gRPC services |\n| 3354 | grpc_gossip_envoy | gRPC gossip protocol communication between nodes |\n| 3355 | authentication_service | Authentication service internal port |\n\nPorts reserved as of Redis Software version 7.8.2:\n\n| Port | Process name | Usage |\n|------|--------------|-------|\n| 3347 | cert_exporter | Reports cluster certificate metrics |\n| 3348 | process_exporter | Reports process metrics for DMC and Redis processes |\n| 3349 | cluster_wd_exporter | Reports cluster watchdog metrics |\n| 3350 | db_controller | Internode communication |\n| 9091 | node_exporter | Reports host node metrics related to CPU, memory, disk, and more |\n| 9125 | statsd_exporter | Reports push metrics related to the DMC and syncer, and some cluster and node metrics |\n\nSee [Ports and port ranges used by Redis Software](https://redis.io/docs/latest/operate/rs/networking/port-configurations#ports-and-port-ranges-used-by-redis-enterprise-software) for a complete list."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2023<sup>[6](#table-note-6)</sup> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – | – | – |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance.\n\n6. <a name=\"table-note-6\"></a>Amazon Linux 2023 support was added in Redis Software version 8.0.20."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (8.0.20-96 August release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">f9d55d1fb35ddb3547afe2474b38fb1dcdeaa3a898976306cef66f21f2a4a441</span> |\n| Ubuntu 22 (amd64) | <span class=\"break-all\">3df873bfe633b94b5909efe32d0fe3d65c57fe8bf155b21b1a5425b40299bb8b</span> |\n| Ubuntu 22 (arm64) | <span class=\"break-all\">6a8bb29d06d5a37575071839bc9b14f761aba65ac44c955e1a34293abe224681</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">d7b45466cbec89e8f4a3519439358085362bf17b1004106618cae759790f249f</span> |\n| Red Hat Enterprise Linux (RHEL) 9 (amd64) | <span class=\"break-all\">376f37b5c1f04ecd67ca34c8ffd43521da8fc69501fdcca644b09f8f8f4938c1</span> |\n| Red Hat Enterprise Linux (RHEL) 9 (arm64) | <span class=\"break-all\">a294ebeddf8d69c11b1039782e0c4fecfdaeaaefd49c7dc422853f5769f1526d</span> |\n| Amazon Linux 2 | <span class=\"break-all\">5a17cbc0f0038cd649acb8f7ba0826941129fc345feb0b598cebfb71d1d1d958</span> |\n| Amazon Linux 2023 (amd64) | <span class=\"break-all\">1db0bb0e492f0bb40f7453d4909fe76259c8a4ac36cdb91c2c268b139132c616</span> |\n| Amazon Linux 2023 (arm64) | <span class=\"break-all\">6b15cf7e411d447984ab8486c310e422c54a97917475057bbd3c9aae3533ae4e</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS155734: Endpoint availability metrics do not work as expected due to a calculation error."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Redis Search query failures during rolling upgrades across the 8.4 version boundary\n\nRedis Search queries can fail during a rolling upgrade when a cluster contains shards running Redis versions earlier than 8.4 and shards running Redis version 8.4 or later, due to an internal protocol change introduced in version 8.4. This issue affects only clusters where `parallel_shards_upgrade` has been changed from its default value of `0`. If both conditions apply, expect Redis Search downtime until all nodes are upgraded.\n\n#### Trim ACKED not supported for Active-Active 8.4 databases\n\nFor Active-Active databases running Redis database version 8.4, the `ACKED` option is not supported for trimming commands.\n\n#### Rolling upgrade limitation for clusters with custom or deprecated modules\n\nDue to module handling changes introduced in Redis Software version 8.0, upgrading a cluster that contains custom or deprecated modules, such as RedisGraph and RedisGears v2, can become stuck when adding a new node to the cluster during a rolling upgrade.\n\n#### Module commands limitation during Active-Active database upgrades to Redis 8.0\n\nWhen upgrading an Active-Active database to Redis version 8.0, you cannot use module commands until all Active-Active database instances have been upgraded. Currently, these commands are not blocked automatically.\n\n#### Redis 8.0 database cannot be created with flash\n\nYou cannot create a Redis 8.0 database with flash storage enabled. Create a Redis 8.0 database with RAM-only storage instead, or use Redis 8.2 for flash-enabled (Redis Flex) databases.\n\n#### Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead."},{"id":"security","title":"Security","role":"security","text":"#### Redis Open Source security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [Redis Open Source](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for Redis Open Source do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in Redis Open Source.\n\nRedis Software 8.0.20-96 supports Redis Open Source 8.6, 8.4, 8.2, 8.0, 7.4, 7.2, and 6.2. Below is the list of Redis Open Source CVEs and other security vulnerabilities fixed by version.\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- VSET: Missing `level` cap when loading a serialized HNSW graph may lead to resource exhaustion.\n\n- VSET: Missing validation of the parameter count when loading a serialized vector set may lead to oversized allocation and resource exhaustion.\n\n- Certificate common-name handling can allow an authentication bypass when `tls-auth-clients-user` is set to `CN`.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- VSET: Missing `level` cap when loading a serialized HNSW graph may lead to resource exhaustion.\n\n- VSET: Missing validation of the parameter count when loading a serialized vector set may lead to oversized allocation and resource exhaustion.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- VSET: Missing `level` cap when loading a serialized HNSW graph may lead to resource exhaustion.\n\n- VSET: Missing validation of the parameter count when loading a serialized vector set may lead to oversized allocation and resource exhaustion.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- (CVE-2025-62507) A user can run the `XACKDEL` command with multiple IDs and trigger a stack buffer overflow, which can potentially lead to remote code execution.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\n\n\n\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-8-0-releases/rs-8-0-6-54","title":"Redis Software release notes 8.0.6-54 (December 2025)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-0-releases/rs-8-0-6-54/","summary":"Single sign-on for the Cluster Manager UI. Error reports for Replica Of migration status. Automatically download and install user-defined modules during bootstrapping.","tags":["docs","operate","rs"],"last_updated":"2026-10-19T14:11:36-05:00","page_type":"content","content_hash":"2f3d43a2a656d7099521a987012397f2c3fa8849210808fbd8b3ebf4434909b1","sections":[{"id":"overview","title":"Overview","role":"overview","text":"​[​Redis Software version 8.0.6](https://redis.io/downloads/#Redis_Software) is now available! This release includes API enhancements that warranted a new minor version instead of a maintenance release for version 8.0.2. However, you can upgrade from 8.0.2 to 8.0.6 without issue."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Single sign-on for the Cluster Manager UI\n\n- Error reports for Replica Of migration status\n\n- Automatically download and install user-defined modules during bootstrapping"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"new-features","title":"New features","role":"content","text":"#### Single sign-on for the Cluster Manager UI {#sso}\n\nRedis Software now supports IdP-initiated and SP-initiated single sign-on (SSO) with SAML (Security Assertion Markup Language) 2.0 for the Cluster Manager UI.\n\nWhen SSO is activated:\n\n- Users can sign in to the Redis Software Cluster Manager UI using their identity provider (IdP) instead of usernames and passwords.\n\n- Optionally, you can enforce SSO for the cluster, which means non-admin users can no longer sign in with their previous usernames and passwords and must use SSO instead.\n\n- With just-in-time (JIT) user provisioning, Redis Software automatically creates a user account the first time a new user signs in with SSO.\n\nFor more information and setup instructions, see [SAML single sign-on](https://redis.io/docs/latest/operate/rs/security/access-control/saml-sso).\n\nKnown limitation: You cannot change the default service provider address using the Cluster Manager UI. You can only change this address using a REST API request."},{"id":"enhancements","title":"Enhancements","role":"content","text":"- Added error report to Replica Of [migration status](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/migrations) REST API responses.\n\n- Added support for automatically downloading and installing user-defined modules during bootstrap operations. You can now specify `user_defined_modules` in [bootstrap requests](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bootstrap#post-bootstrap) for `create_cluster`, `join_cluster`, and `recover_cluster` actions. See [Add user-defined modules during bootstrapping](https://redis.io/docs/latest/operate/rs/installing-upgrading/modules/add-module-to-cluster#bootstrap-user-defined-module) for details.\n\n- Added `cert` and `private_key` fields to the [CRDB database config object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/crdb/database_config) for an optional PEM-encoded server certificate for the underlying database instance."},{"id":"redis-database-versions","title":"Redis database versions","role":"content","text":"Redis Software version 8.0.6 includes five Redis database versions: 8.2.1, 8.0.2, 7.4.3, 7.2.7, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 8.2."},{"id":"redis-feature-sets","title":"Redis feature sets","role":"content","text":"Redis Software includes multiple feature sets, compatible with different Redis database versions.\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 8.2 | RediSearch 8.2<br />RedisJSON 8.2<br />RedisTimeSeries 8.2<br />RedisBloom 8.2<br />See [What's new in Redis 8.2](https://redis.io/docs/latest/develop/whats-new/8-2) and [Redis Open Source 8.2 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.2-release-notes) |\n| 8.0 | RediSearch 8.0<br />RedisJSON 8.0<br />RedisTimeSeries 8.0<br />RedisBloom 8.0<br />See [What's new in Redis 8.0](https://redis.io/docs/latest/develop/whats-new/8-0) and [Redis Open Source 8.0 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.0-release-notes) |\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes) |"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS131972: Fixed an issue where creating an ACL that contains a line break in the Cluster Manager UI could cause shard migration to fail due to ACL errors.\n\n- RS140424: Fixed an issue where configuration changes initiated topology updates even if the topology did not change.\n\n- RS144636: Improved support package generation to collect available database information even when some data collection steps fail.\n\n- RS162503: Fixed an issue where force-removed Active-Active database instances could not be re-added as participating members without purging.\n\n- RS155782: Improved logs and added validation to ensure operations are properly queued and prevent stuck state machines.\n\n- RS167151: Improved reliability of node removal operations by increasing retry attempts for failover and reshard operations.\n\n- RS167280: Fixed an issue where a subset of shards on a restarted node could fail to start due to temporary connection issues.\n\n- RS172813: Improved logging for Active-Active database failover scenarios to provide better visibility into data recovery processes.\n\n- RS173195: Fixed an issue where cluster operations could fail when attempting to communicate with unreachable nodes.\n\n- RS174154: Fixed an issue where EntraID authentication service was not properly enabled despite being configured and running.\n\n- RS174819: Fixed an issue where duplicate syncers could spawn on the same node.\n\n- RS176400: Fixed an issue where Google Cloud Storage backup locations could be set to the incorrect type when configured in the Cluster Manager UI.\n\n- RS165983: Fixed an issue where an incorrect value was printed for `region_name` in the event log."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2 | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (8.0.6-54 December release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">70ef3f95bc8bc16e988e8f5572a3956d2ab3068a7d069ce03578ca2a30431301</span> |\n| Ubuntu 22 (amd64) | <span class=\"break-all\">576653f63613be103fcf8e1cd7760f6373bbc12a698ab59a12b452ea8f967fd4</span> |\n| Ubuntu 22 (arm64) | <span class=\"break-all\">422635cdf0231a59aa9bb2e27c9fca8a13e1ea145ccd002159e66085d6b271e2</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">fa744961e7075a506b6456a9bb70b19e02b31f172ea6c9ef2154f80dfb5c1dcb</span> |\n| Red Hat Enterprise Linux (RHEL) 9 | <span class=\"break-all\">9c4c24ee3086ad8d595dc6fea2865b6520f07f8f589c214efae5229040727ac8</span> |\n| Amazon Linux 2 | <span class=\"break-all\">8bd6008bc9036b675d57f3995f5a4725e264db86c981f940cada4529b001d3f7</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS196225: After upgrading to Redis Software version 8.0.x, previously working LDAP filters that use an `OR` clause to match multiple attributes can fail to find a unique DN for some users.\n\n- RS180550: You cannot set up SSO when the Cluster Manager UI is exposed through an IPv6-based load balancer or gateway.\n\n    As a workaround, use an IPv4-based address for the SSO service base address, or register a DNS name that resolves to the IPv6 address.\n\n- RS155734: Endpoint availability metrics do not work as expected due to a calculation error."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Cannot change SP address for SSO in the Cluster Manager UI\n\nYou cannot change the default service provider address using the Cluster Manager UI. You can only change this address using a REST API request.\n\n#### Rolling upgrade limitation for clusters with custom or deprecated modules\n\nDue to module handling changes introduced in Redis Software version 8.0, upgrading a cluster that contains custom or deprecated modules, such as RedisGraph and RedisGears v2, can become stuck when adding a new node to the cluster during a rolling upgrade.\n\n#### Module commands limitation during Active-Active database upgrades to Redis 8.0\n\nWhen upgrading an Active-Active database to Redis version 8.0, you cannot use module commands until all Active-Active database instances have been upgraded. Currently, these commands are not blocked automatically.\n\n#### Redis 8.0 database cannot be created with flash\n\nYou cannot create a Redis 8.0 database with flash storage enabled. Create a Redis 8.0 database with RAM-only storage instead, or use Redis 8.2 for flash-enabled (Redis Flex) databases.\n\n#### Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log."},{"id":"security","title":"Security","role":"security","text":"#### Redis Open Source security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [Redis Open Source](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for Redis Open Source do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in Redis Open Source.\n\nRedis Software 8.0.6-54 supports Redis Open Source 8.2, 8.0, 7.4, 7.2, and 6.2. Below is the list of Redis Open Source CVEs and other security vulnerabilities fixed by version.\n\nRedis 8.2.x:\n\n- RedisBloom: Restore invalid filter.\n\n- (CVE-2025-62507) A user can run the `XACKDEL` command with multiple IDs and trigger a stack buffer overflow, which can potentially lead to remote code execution.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\nRedis 8.0.x:\n\n- RedisBloom: Restore invalid filter.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\nRedis 7.4.x:\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\nRedis 7.2.x:\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\nRedis 7.0.x:\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\nRedis 6.2.x:\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-8-2-releases","title":"Redis Software release notes 8.2.x","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-2-releases/","summary":"Database-scoped management roles. Data path command auditing.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","children":[{"id":"operate/rs/release-notes/rs-8-2-releases/rs-8-2-0-125","summary":"Bug fixes for upgrades from Redis Software 7.2.x and 7.4.x, cluster management after a primary node change, node removal during upgrades, Active-Active proxy recovery, DMC proxy stability, scaling and resharding, ACL validation, cluster recovery, Google Cloud Storage backup configuration, and signing in with passwords that contain a colon. Internal fixes and improvements.","title":"Redis Software release notes 8.2.0-125 (October 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-2-releases/rs-8-2-0-125/"},{"id":"operate/rs/release-notes/rs-8-2-releases/rs-8-2-0-78","summary":"Bug fixes for minor version upgrades, shard placement, LDAP client authentication, database connectivity, DMC proxy connection cleanup, internode encryption certificates, Auto Tiering database resizing, crdb-cli certificate-based authentication, metrics export, module configuration, and support package collection. Internal fixes and improvements.","title":"Redis Software release notes 8.2.0-78 (September 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-2-releases/rs-8-2-0-78/"},{"id":"operate/rs/release-notes/rs-8-2-releases/rs-8-2-0-46","summary":"Bug fixes for internal certificate rotation, rolling upgrades, the Cluster Manager UI, shard rebalancing, and Active-Active synchronization. Internal fixes and improvements.","title":"Redis Software release notes 8.2.0-46 (August 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-2-releases/rs-8-2-0-46/"},{"id":"operate/rs/release-notes/rs-8-2-releases/rs-8-2-0-25","summary":"Database-scoped management roles. Data path command auditing.","title":"Redis Software release notes 8.2.0-25 (July 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-2-releases/rs-8-2-0-25/"}],"page_type":"content","content_hash":"731dc572e746a1b6fddf5c4f1916725b0737bf93609b7662c44925bf0fea8e11","sections":[{"id":"overview","title":"Overview","role":"overview","text":"​[​Redis Software version 8.2](https://redis.io/downloads/#Redis_Software) is now available!"},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Database-scoped management roles\n\n- Data path command auditing\n\n- FIPS 140-3 support"},{"id":"detailed-release-notes","title":"Detailed release notes","role":"content","text":"For more detailed release notes, select a build version from the following table:\n\n\n| Version (Release date) | Major changes | Redis Open Source compatibility |\n| --- | --- | --- |\n| [8.2.0-125 (October 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-2-releases/rs-8-2-0-125/) | Bug fixes for upgrades from Redis Software 7.2.x and 7.4.x, cluster management after a primary node change, node removal during upgrades, Active-Active proxy recovery, DMC proxy stability, scaling and resharding, ACL validation, cluster recovery, Google Cloud Storage backup configuration, and signing in with passwords that contain a colon. Internal fixes and improvements. | Redis 8.6.2, 8.4.0, 8.2.1, 8.0.2, 7.4.3, 7.2.7, 6.2.13 |\n| [8.2.0-78 (September 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-2-releases/rs-8-2-0-78/) | Bug fixes for minor version upgrades, shard placement, LDAP client authentication, database connectivity, DMC proxy connection cleanup, internode encryption certificates, Auto Tiering database resizing, crdb-cli certificate-based authentication, metrics export, module configuration, and support package collection. Internal fixes and improvements. | Redis 8.6.2, 8.4.0, 8.2.1, 8.0.2, 7.4.3, 7.2.7, 6.2.13 |\n| [8.2.0-46 (August 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-2-releases/rs-8-2-0-46/) | Bug fixes for internal certificate rotation, rolling upgrades, the Cluster Manager UI, shard rebalancing, and Active-Active synchronization. Internal fixes and improvements. | Redis 8.6.2, 8.4.0, 8.2.1, 8.0.2, 7.4.3, 7.2.7, 6.2.13 |\n| [8.2.0-25 (July 2026)](https://redis.io/docs/latest/operate/rs/release-notes/rs-8-2-releases/rs-8-2-0-25/) | Database-scoped management roles. Data path command auditing. | Redis 8.6.2, 8.4.0, 8.2.1, 8.0.2, 7.4.3, 7.2.7, 6.2.13 |"},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"Redis Software version 8.2.0 introduces the following breaking changes:\n\n- **Local import from Redis Software system directories is no longer supported.** To improve security, database imports that use the `mount_point` source can no longer read files from Redis Software system directories, including `/opt/redislabs`, `/var/opt/redislabs`, `/etc/opt/redislabs`, and any of their subdirectories.\n\n    - Import operations that reference files in these directories now fail.\n\n    - Store import files in a non-system location accessible to Redis Software before you start the import.\n\n- **`smtp_password` is now a write-only field in the Cluster API.** To improve security, the API no longer returns the SMTP password in responses to `GET /v1/cluster`, and the Cluster Manager UI no longer reads or displays the existing password.\n\n    - Applications or scripts that previously expected `smtp_password` to be returned by the Cluster API no longer receive this field.\n\n    - When you update SMTP settings, the existing password cannot be retrieved. To change the SMTP password, provide a new password as part of the update request.\n\n- **Custom module upload is disabled by default.** To improve security, custom (user-defined) module uploads are now disabled by default. A new cluster setting, `allow_modules_upload`, controls access to the custom module upload APIs. When this setting is disabled (the default), requests to `POST /v2/modules/user-defined` and `POST /v2/local/modules/user-defined/artifacts` return `403 modules_upload_disabled`, regardless of user role.\n\n    - To enable custom module uploads, set `allow_modules_upload` to `true` with a `PUT /v1/cluster` request.\n\n    - Applications or automation that call the custom module upload APIs directly receive a `403 modules_upload_disabled` response until the setting is enabled.\n\n    - Existing functionality for listing, deleting, and loading modules is unchanged."},{"id":"reserved-ports","title":"Reserved ports","role":"content","text":"Make sure the following ports are open before upgrading Redis Software.\n\nThe following port was added as a reserved port in Redis Software version 8.0.18; however, it is optional instead of reserved in Redis Software version 8.0.20-44:\n\n| Port | Process name | Usage |\n|------|--------------|-------|\n| 3357 | reconciliation_tree_grpc | Internal communication |\n\nPorts reserved as of Redis Software version 7.22.0:\n\n| Port | Process name | Usage | \n|------|--------------|-------|\n| 3346 | cluster_api_internal | Cluster API internal port |\n| 3351 | cluster_watchdog_grpc_api | Cluster watchdog now supports gRPC |\n| 3352 | grpc_service_mesh | gRPC communication between nodes |\n| 3353 | local_grpc_service_mesh | Local gRPC services |\n| 3354 | grpc_gossip_envoy | gRPC gossip protocol communication between nodes |\n| 3355 | authentication_service | Authentication service internal port |\n\nPorts reserved as of Redis Software version 7.8.2:\n\n| Port | Process name | Usage | \n|------|--------------|-------|\n| 3347 | cert_exporter | Reports cluster certificate metrics |\n| 3348 | process_exporter | Reports process metrics for DMC and Redis processes |\n| 3349 | cluster_wd_exporter | Reports cluster watchdog metrics |\n| 3350 | db_controller | Internode communication |\n| 9091 | node_exporter | Reports host node metrics related to CPU, memory, disk, and more |\n| 9125 | statsd_exporter | Reports push metrics related to the DMC and syncer, and some cluster and node metrics |\n\nSee [Ports and port ranges used by Redis Software](https://redis.io/docs/latest/operate/rs/networking/port-configurations#ports-and-port-ranges-used-by-redis-software) for a complete list."},{"id":"deprecations","title":"Deprecations","role":"content","text":"#### API deprecations\n\n- The `db_conns_auditing` cluster policy and database configuration field is deprecated. Use the `audit_settings.audit_mode` field instead. Setting `db_conns_auditing` to `true` is equivalent to setting `audit_mode` to `connection`. See [Audit events](https://redis.io/docs/latest/operate/rs/security/audit-events) for the current auditing configuration.\n\n- The `gradual_src_mode`, `gradual_sync_mode`, and `gradual_sync_max_shards_per_source` parameters are deprecated. Use `gradual_sync_policy` instead. You can set it with [`rladmin tune db`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune) or the REST API.\n\n- The `old_password` field in `POST /v1/users/password` and `PUT /v1/users/password` requests is deprecated and ignored. It was optional and had no effect when adding or resetting a password, so you can remove it from these requests. `old_password` is still required for `DELETE /v1/users/password`, which uses it to identify the password to delete.\n\n- The following REST API fields, deprecated since Redis Software version 6.4.2, are removed in this release:\n\n    - `use_ipv6` (cluster object) — use `use_external_ipv6` instead.\n\n    - `redis_cleanup_job_settings` (job scheduler object) — use `persistence_cleanup_scan_interval` instead.\n\n#### Internal monitoring and v1 Prometheus metrics deprecation\n\nThe existing [internal monitoring engine](https://redis.io/docs/latest/operate/rs/monitoring/v1_monitoring) is deprecated. We recommend transitioning to the new [metrics stream engine](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine) for improved performance, enhanced integration capabilities, and modernized metrics streaming.\n\nV1 Prometheus metrics are deprecated but still available. To transition to the new metrics stream engine, either migrate your existing dashboards using [this guide](https://redis.io/docs/latest/operate/rs/references/metrics/prometheus-metrics-v1-to-v2) or use [new preconfigured dashboards](https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise#grafana-dashboards-for-redis-software).\n\nAs part of the transition to the metrics stream engine, some internal cluster manager alerts were deprecated in favor of external monitoring solutions. See the [alerts transition plan](https://redis.io/docs/latest/operate/rs/references/alerts/alerts-v1-to-v2) for guidance."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.2 | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Jul 2026 | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Jul 2028 | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2023<sup>[6](#table-note-6)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – | – | – |\n| Amazon Linux 2 | – | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance.\n\n6. <a name=\"table-note-6\"></a>Amazon Linux 2023 support was added in Redis Software version 8.0.20."},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS155734: Endpoint availability metrics do not work as expected due to a calculation error."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Redis Search query failures during rolling upgrades across the 8.4 version boundary\n\nRedis Search queries can fail during a rolling upgrade when a cluster contains shards running Redis versions earlier than 8.4 and shards running Redis version 8.4 or later, due to an internal protocol change introduced in version 8.4. This issue affects only clusters where `parallel_shards_upgrade` has been changed from its default value of `0`. If both conditions apply, expect Redis Search downtime until all nodes are upgraded.\n\n#### Trim ACKED not supported for Active-Active 8.4 databases\n\nFor Active-Active databases running Redis database version 8.4, the `ACKED` option is not supported for trimming commands.\n\n#### Rolling upgrade limitation for clusters with custom or deprecated modules\n\nDue to module handling changes introduced in Redis Software version 8.0, upgrading a cluster that contains custom or deprecated modules, such as RedisGraph and RedisGears v2, can become stuck when adding a new node to the cluster during a rolling upgrade.\n\n#### Module commands limitation during Active-Active database upgrades to Redis 8.0\n\nWhen upgrading an Active-Active database to Redis version 8.0, you cannot use module commands until all Active-Active database instances have been upgraded. Currently, these commands are not blocked automatically.\n\n#### Redis 8.0 database cannot be created with flash\n\nYou cannot create a Redis 8.0 database with flash storage enabled. Create a Redis 8.0 database with RAM-only storage instead, or use Redis 8.2 for flash-enabled (Redis Flex) databases.\n\n#### New Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead.\n\n- Search and export the log.\n\n#### SCAN results can be inconsistent during a rolling upgrade in OSS Cluster API mode\n\nDuring a rolling upgrade with Smart client handoffs (SCH) enabled, when some slot's shards have migrated to a different node but are still reachable from their original node, results of the `SCAN` command might be inconsistent: it might show some of the keys, it might show keys that are not reachable from that node, or it might show no keys. When the slot migration is done, this issue will fix itself."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-8-2-releases/rs-8-2-0-125","title":"Redis Software release notes 8.2.0-125 (October 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-2-releases/rs-8-2-0-125/","summary":"Bug fixes for upgrades from Redis Software 7.2.x and 7.4.x, cluster management after a primary node change, node removal during upgrades, Active-Active proxy recovery, DMC proxy stability, scaling and resharding, ACL validation, cluster recovery, Google Cloud Storage backup configuration, and signing in with passwords that contain a colon. Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-10-20T11:01:25-05:00","page_type":"content","content_hash":"fcf680dacc8efa9e51c46e0df9343033e37453cc5279744a98ade6ea4bfddfff","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Software version 8.2.0](https://redis.io/downloads/#Redis_Software)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Bug fix for upgrades from Redis Software 7.2.x and 7.4.x\n\n- Bug fixes for cluster management after a primary node change\n\n- Bug fix for node removal during rolling upgrades\n\n- Bug fixes for DMC proxy stability and Active-Active proxy recovery\n\n- Bug fixes for scaling, resharding, and cluster recovery\n\n- Bug fix for ACL rule validation\n\n- Bug fix for Google Cloud Storage backup configuration\n\n- Bug fix for signing in with passwords that contain a colon\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-database-versions-and-feature-sets","title":"Redis database versions and feature sets","role":"content","text":"Redis Software version 8.2.0 includes the following Redis database versions: 8.6.2, 8.4.0, 8.2.1, 8.0.2, 7.4.3, 7.2.7, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 8.6.\n\nRedis Software includes multiple feature sets, compatible with different Redis database versions.\n\n\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 8.6 | RediSearch 8.6<br />RedisJSON 8.6<br />RedisTimeSeries 8.6<br />RedisBloom 8.6<br />See [What's new in Redis 8.6](https://redis.io/docs/latest/develop/whats-new/8-6) and [Redis Open Source 8.6 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.6-release-notes) |\n| 8.4 | RediSearch 8.4<br />RedisJSON 8.4<br />RedisTimeSeries 8.4<br />RedisBloom 8.4<br />See [What's new in Redis 8.4](https://redis.io/docs/latest/develop/whats-new/8-4) and [Redis Open Source 8.4 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.4-release-notes) |\n| 8.2 | RediSearch 8.2<br />RedisJSON 8.2<br />RedisTimeSeries 8.2<br />RedisBloom 8.2<br />See [What's new in Redis 8.2](https://redis.io/docs/latest/develop/whats-new/8-2) and [Redis Open Source 8.2 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.2-release-notes) |\n| 8.0 | RediSearch 8.0<br />RedisJSON 8.0<br />RedisTimeSeries 8.0<br />RedisBloom 8.0<br />See [What's new in Redis 8.0](https://redis.io/docs/latest/develop/whats-new/8-0) and [Redis Open Source 8.0 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.0-release-notes) |\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes) |"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS196137: Fixed an issue where the Cluster Manager did not send the required `private_key` field when configuring Google Cloud Storage (GCS) backups.\n\n- RS201541: Fixed an issue where removing a node could get stuck in a decommissioning state during a rolling upgrade of a cluster with AOF persistence on RHEL and Amazon Linux.\n\n- RS202231: Fixed an issue where the DMC proxy could crash while resharding a database during a scale-out.\n\n- RS202483: Fixed an issue where ACL rule validation did not apply the cluster's `acl_pubsub_default` setting, which caused validation to reject valid ACL rules or accept invalid rules that then left the database change pending.\n\n- RS209436: Fixed an issue where a proxy that started during a database failover did not resume forwarding traffic and closed client connections while the database appeared active.\n\n- RS210971: Fixed an issue where endpoint request metrics did not count requests to Memcached databases.\n\n- RS211091: Fixed an issue where the cluster could become unresponsive after a newly joined node became the primary node while scaling the cluster.\n\n- RS212081: Fixed an issue where changing the primary node could time out and leave the cluster without a working primary node.\n\n- RS212205: Fixed an issue where users with a password that contains a colon (`:`) could not sign in to the Cluster Manager UI or authenticate REST API requests.\n\n- RS212633: Fixed an issue where recovering a cluster after a full cluster restart could use outdated persistence files, which prevented the database from being recovered.\n\n- RS213016: Fixed an issue where a node that was frozen and then resumed could stop responding to cluster management requests, which caused cluster health checks to fail and left the database in an `active-change-pending` state.\n\n- RS213801: Fixed an issue where a shard could remain in the wrong role after resharding or scaling.\n\n- RS215390: Fixed an issue where a database with the `all-nodes` proxy policy could stay in an `active-change-pending` state after a node was added to the cluster.\n\n- RS215608: Fixed an issue where running `dmc-cli list bdb` or `dmc-cli list server` could corrupt memory and cause the DMC proxy to crash.\n\n- RS215676: Fixed an issue where adding a participating cluster to an Active-Active database could cause the new participating cluster's shards to crash repeatedly during the full sync, which also disrupted synchronization between the other participating clusters.\n\n- RS216045: Fixed an issue where the DMC proxy could crash repeatedly when a client sent a command without key arguments, such as `PUBSUB SHARDNUMSUB` with no channels, in a pipeline with another command.\n\n- RS219531: Fixed an issue where upgrading a cluster from Redis Software version 7.2.x or 7.4.x to version 8.2.0 could fail because a node that still ran the earlier version lost its replication link to the cluster configuration store."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"jwt-token-invalidation-on-upgrade","title":"JWT token invalidation on upgrade","role":"content","text":"To improve system security, the JWT signing secret has changed in version 8.0.22 and later. As a result, upgrading invalidates all previously issued JWT tokens, which must be regenerated. Tokens used programmatically within the cluster are regenerated automatically. Cluster Manager users must sign out and sign in again, or wait for their session or token to expire."},{"id":"reserved-ports","title":"Reserved ports","role":"content","text":"Make sure the following ports are open before upgrading Redis Software.\n\nThe following port was added as a reserved port in Redis Software version 8.0.18; however, it is optional instead of reserved in Redis Software version 8.0.20-44:\n\n| Port | Process name | Usage |\n|------|--------------|-------|\n| 3357 | reconciliation_tree_grpc | Internal communication |\n\nPorts reserved as of Redis Software version 7.22.0:\n\n| Port | Process name | Usage |\n|------|--------------|-------|\n| 3346 | cluster_api_internal | Cluster API internal port |\n| 3351 | cluster_watchdog_grpc_api | Cluster watchdog now supports gRPC |\n| 3352 | grpc_service_mesh | gRPC communication between nodes |\n| 3353 | local_grpc_service_mesh | Local gRPC services |\n| 3354 | grpc_gossip_envoy | gRPC gossip protocol communication between nodes |\n| 3355 | authentication_service | Authentication service internal port |\n\nPorts reserved as of Redis Software version 7.8.2:\n\n| Port | Process name | Usage |\n|------|--------------|-------|\n| 3347 | cert_exporter | Reports cluster certificate metrics |\n| 3348 | process_exporter | Reports process metrics for DMC and Redis processes |\n| 3349 | cluster_wd_exporter | Reports cluster watchdog metrics |\n| 3350 | db_controller | Internode communication |\n| 9091 | node_exporter | Reports host node metrics related to CPU, memory, disk, and more |\n| 9125 | statsd_exporter | Reports push metrics related to the DMC and syncer, and some cluster and node metrics |\n\nSee [Ports and port ranges used by Redis Software](https://redis.io/docs/latest/operate/rs/networking/port-configurations#ports-and-port-ranges-used-by-redis-software) for a complete list."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n**Amazon Linux 2 is no longer supported.** Starting with Redis Software version 8.0.22, Redis Software no longer builds or publishes Amazon Linux 2 (`amzn2`) packages. Use Amazon Linux 2023 instead.\n\n\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.2 | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Jul 2026 | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Jul 2028 | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2023<sup>[6](#table-note-6)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – | – | – |\n| Amazon Linux 2 | – | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance.\n\n6. <a name=\"table-note-6\"></a>Amazon Linux 2023 support was added in Redis Software version 8.0.20."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (8.2.0-125 October release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">edeb44f10ac5db8648c3a1ecd4266ec4a7e73035ea2e6415468ee6596c22da00</span> |\n| Ubuntu 22 (amd64) | <span class=\"break-all\">841feb35520a76b3ee51d7635f8fd2ed511b45c43a72d212c57af10394241527</span> |\n| Ubuntu 22 (arm64) | <span class=\"break-all\">9c527e55411fafca18353db49613381c669a1189e784e87010d92b8d2556bf17</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">c23106b4801c07e18adc660317b6b93aa7161f5ffb4b15f74893db91c761ffe6</span> |\n| Red Hat Enterprise Linux (RHEL) 9 (amd64) | <span class=\"break-all\">8bbd7fd39ac073a7b52e1545800d66c3995719b4766a58f762b5bc66c59ab5f0</span> |\n| Red Hat Enterprise Linux (RHEL) 9 (arm64) | <span class=\"break-all\">d908c2f20579e4e6c8e1d41c91cfb5bc48403306f4741d46f81b5afa784dcb6e</span> |\n| Red Hat Enterprise Linux (RHEL) 9 FIPS (amd64) | <span class=\"break-all\">896bb24fd57bfd3aaa269ebb9a31e42337c57f3afb2cae9ee45339e21b5ecfb2</span> |\n| Amazon Linux 2023 (amd64) | <span class=\"break-all\">06ac73b6788a38f1144288db2bccab9656deaad9523de1e0b508eb26c1129e21</span> |\n| Amazon Linux 2023 (arm64) | <span class=\"break-all\">b801de92c1c1633bacd2c2ac06ffbc8586de24fe689486b8c2a04a0084e6a362</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS155734: Endpoint availability metrics do not work as expected due to a calculation error.\n\n- For databases that use RedisTimeSeries with Redis database version 8.2, 8.4, or 8.6, after a shard restarts and recovers from its append-only file (AOF), requests routed to that shard can receive replies that belong to other commands or no reply at all. The issue does not resolve on its own."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Increased memory usage in FIPS mode\n\nFIPS-enabled builds use more memory than non-FIPS builds. Each Go-based control-plane service uses roughly 34 MB more when running in FIPS mode, which adds up to approximately 210 MB of additional memory per node. Account for this overhead when sizing FIPS-enabled deployments. This limitation affects FIPS-enabled builds only.\n\n#### SFTP storage not supported in FIPS mode\n\nFIPS-enabled builds do not support SFTP as a storage location for backups, imports, or exports, because the SFTP library is not FIPS-compliant. In FIPS mode, use a different storage location. This limitation affects FIPS-enabled builds only.\n\n#### Redis Search query failures during rolling upgrades across the 8.4 version boundary\n\nRedis Search queries can fail during a rolling upgrade when a cluster contains shards running Redis versions earlier than 8.4 and shards running Redis version 8.4 or later, due to an internal protocol change introduced in version 8.4. This issue affects only clusters where `parallel_shards_upgrade` has been changed from its default value of `0`. If both conditions apply, expect Redis Search downtime until all nodes are upgraded.\n\n#### Trim ACKED not supported for Active-Active 8.4 databases\n\nFor Active-Active databases running Redis database version 8.4, the `ACKED` option is not supported for trimming commands.\n\n#### Rolling upgrade limitation for clusters with custom or deprecated modules\n\nDue to module handling changes introduced in Redis Software version 8.0, upgrading a cluster that contains custom or deprecated modules, such as RedisGraph and RedisGears v2, can become stuck when adding a new node to the cluster during a rolling upgrade.\n\n#### Cluster Manager sign-outs and false \"down\" status during an Active-Active upgrade\n\nWhile an Active-Active database has participating clusters running different Redis Software versions, the Cluster Manager UI on an upgraded cluster can sign users out intermittently and show a participating cluster as down, even though replication is active and the syncers are connected.\n\nThis affects the Cluster Manager UI only. It's caused by a JWT signing-key mismatch between nodes running different versions during the upgrade window (see [JWT token invalidation on upgrade](#jwt-token-invalidation-on-upgrade)). The data path and replication are not affected.\n\nTo resolve it, upgrade the remaining participating clusters. The issue clears after every participating cluster runs the same version. If you're signed out, sign in again.\n\n#### Module commands limitation during Active-Active database upgrades to Redis 8.0\n\nWhen upgrading an Active-Active database to Redis version 8.0, you cannot use module commands until all Active-Active database instances have been upgraded. Currently, these commands are not blocked automatically.\n\n#### Redis 8.0 database cannot be created with flash\n\nYou cannot create a Redis 8.0 database with flash storage enabled. Create a Redis 8.0 database with RAM-only storage instead, or use Redis 8.2 for flash-enabled (Redis Flex) databases.\n\n#### Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead."},{"id":"security","title":"Security","role":"security","text":"#### Redis Open Source security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [Redis Open Source](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for Redis Open Source do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in Redis Open Source.\n\nRedis Software 8.2.0-125 supports Redis Open Source 8.6, 8.4, 8.2, 8.0, 7.4, 7.2, and 6.2. Below is the list of Redis Open Source CVEs and other security vulnerabilities fixed by version.\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- VSET: Missing `level` cap when loading a serialized HNSW graph may lead to resource exhaustion.\n\n- VSET: Missing validation of the parameter count when loading a serialized vector set may lead to oversized allocation and resource exhaustion.\n\n- Certificate common-name handling can allow an authentication bypass when `tls-auth-clients-user` is set to `CN`.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- VSET: Missing `level` cap when loading a serialized HNSW graph may lead to resource exhaustion.\n\n- VSET: Missing validation of the parameter count when loading a serialized vector set may lead to oversized allocation and resource exhaustion.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- VSET: Missing `level` cap when loading a serialized HNSW graph may lead to resource exhaustion.\n\n- VSET: Missing validation of the parameter count when loading a serialized vector set may lead to oversized allocation and resource exhaustion.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- (CVE-2025-62507) A user can run the `XACKDEL` command with multiple IDs and trigger a stack buffer overflow, which can potentially lead to remote code execution.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\n\n\n\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-8-2-releases/rs-8-2-0-25","title":"Redis Software release notes 8.2.0-25 (July 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-2-releases/rs-8-2-0-25/","summary":"Database-scoped management roles. Data path command auditing.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"fa12ebde114a36b222e95c20cc479f1865231e62874809777570f5d671256d89","sections":[{"id":"overview","title":"Overview","role":"overview","text":"​[Redis Software version 8.2.0](https://redis.io/downloads/#Redis_Software) is now available!"},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Database-scoped management roles\n\n- Data path command auditing\n\n- FIPS 140-3 support"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"database-scoped-management-roles","title":"Database-scoped management roles","role":"content","text":"You can now scope the `db_member` and `db_viewer` [management roles](https://redis.io/docs/latest/operate/rs/security/access-control/create-cluster-roles) to specific databases instead of granting access to every database in the cluster. This lets you give application or tenant teams management access to only their own databases in a shared cluster.\n\n- To scope a role, specify the databases it applies to using the new `resources` field when you create or update a [role](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/role). For example, `\"resources\": [{\"type\": \"db\", \"uids\": [\"1\", \"2\"]}]` scopes the role to databases `1` and `2`.\n\n- Roles without a `resources` scope continue to apply to all databases, so existing roles are unaffected.\n\n- Users assigned a database-scoped role see only their authorized databases in the Cluster Manager UI and REST API responses, including database lists, metrics, alerts, and event logs. Other databases are hidden."},{"id":"data-path-command-auditing","title":"Data path command auditing","role":"content","text":"Redis Software now supports auditing of data path commands (CRUD operations), in addition to the existing connection auditing. This lets you record which commands run against a database, along with the connection and authentication context, and stream the audit records to an external audit destination for security monitoring and compliance.\n\n- Enable auditing per database with the new `audit_settings` configuration, choosing to audit connection events only (`connection`) or both connection events and commands (`connection_and_crud`).\n\n- Filter audited commands by username, source IP address, or both to control the volume of audit data.\n\n- Monitor the auditing pipeline with new `audit_sink_*` metrics.\n\nFor more information, see [Audit events](https://redis.io/docs/latest/operate/rs/security/audit-events)."},{"id":"fips-140-3-support","title":"FIPS 140-3 support","role":"content","text":"Redis Software supports deployments on RHEL 9 VMs and RHEL 9 containerized Redis on GKE using cryptographic libraries based on FIPS 140-3 validated modules, enabling the use of FIPS-approved cryptographic algorithms in supported environments.\n\n- Install Redis Software on a RHEL 9 host that has FIPS mode enabled. See [Supported platforms](#supported-platforms) for the platforms that support FIPS mode.\n\n- In FIPS mode, Redis Software uses only FIPS-approved cryptographic algorithms and cipher suites; non-approved ciphers and algorithms are blocked.\n\n- Because FIPS mode fixes the allowed cipher suites, you cannot change the cluster's TLS cipher suites while FIPS mode is enabled. Requests to update them return a `tls_cipher_update_not_allowed` error.\n\nFor FIPS-mode limitations, see [Increased memory usage in FIPS mode](#increased-memory-usage-in-fips-mode) and [SFTP storage not supported in FIPS mode](#sftp-storage-not-supported-in-fips-mode)."},{"id":"enhancements","title":"Enhancements","role":"content","text":"- **Disable Basic authentication for the REST API.** You can now disable Basic and Digest authentication for the cluster management REST API using the new `control_plane_basic_authentication` cluster setting. Basic authentication remains enabled by default; when you disable it, the cluster rejects Basic and Digest authentication on inbound REST API requests. Configure it with [`rladmin tune cluster`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune) or an [update cluster settings](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster#put-cluster) REST API request. Cluster join and Active-Active (CRDB) management operations support certificate credentials when Basic and Digest authentication are disabled or unavailable. See [Disable basic authentication for the REST API](https://redis.io/docs/latest/operate/rs/security/access-control/disable-basic-authentication).\n\n- **Expose database tags in metrics.** You can now expose selected [database tags](https://redis.io/docs/latest/operate/rs/databases/configure/db-tags) as labels in [v2 metrics](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine/prometheus-metrics-v2) through a new `db_tags` metric, so you can group, filter, and alert on database metrics by your own tags. Enable it in the cluster's metrics configuration using the `expose_db_tags` and `metrics_tag_keys_exposed` settings.\n\n- **Faster replica synchronization.** Redis Software now manages gradual (parallel) synchronization through a `gradual_sync_policy` that applies tuned defaults for common scenarios, such as migration, adding a replica, and Active-Active. This speeds up replica bootstrap in multi-node and multi-shard deployments, such as during upgrades and scaling events, while leaving small deployments unaffected. You can select a policy or set custom parameters with [`rladmin tune db`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune) or the REST API.\n\n- **Certificate–cipher suite validation.** When you upload a cluster certificate, Redis Software now validates that the certificate is compatible with the configured cipher suites and rejects incompatible certificates at upload time, so mismatches are caught early instead of surfacing later. For example, uploading an ECDSA certificate while only RSA-authenticated cipher suites are configured now fails at upload. The check applies to the control-plane certificates (such as `api`, `cm`, and `metrics_exporter`) and to the proxy certificate against the data-path cipher suites. If an upload is rejected, update either the certificate or the configured cipher suites so they are compatible."},{"id":"redis-database-versions-and-feature-sets","title":"Redis database versions and feature sets","role":"content","text":"Redis Software version 8.2.0 includes the following Redis database versions: 8.6.2, 8.4.0, 8.2.1, 8.0.2, 7.4.3, 7.2.7, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 8.6.\n\nRedis Software includes multiple feature sets, compatible with different Redis database versions.\n\n\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 8.6 | RediSearch 8.6<br />RedisJSON 8.6<br />RedisTimeSeries 8.6<br />RedisBloom 8.6<br />See [What's new in Redis 8.6](https://redis.io/docs/latest/develop/whats-new/8-6) and [Redis Open Source 8.6 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.6-release-notes) |\n| 8.4 | RediSearch 8.4<br />RedisJSON 8.4<br />RedisTimeSeries 8.4<br />RedisBloom 8.4<br />See [What's new in Redis 8.4](https://redis.io/docs/latest/develop/whats-new/8-4) and [Redis Open Source 8.4 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.4-release-notes) |\n| 8.2 | RediSearch 8.2<br />RedisJSON 8.2<br />RedisTimeSeries 8.2<br />RedisBloom 8.2<br />See [What's new in Redis 8.2](https://redis.io/docs/latest/develop/whats-new/8-2) and [Redis Open Source 8.2 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.2-release-notes) |\n| 8.0 | RediSearch 8.0<br />RedisJSON 8.0<br />RedisTimeSeries 8.0<br />RedisBloom 8.0<br />See [What's new in Redis 8.0](https://redis.io/docs/latest/develop/whats-new/8-0) and [Redis Open Source 8.0 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.0-release-notes) |\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes) |"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS200909: Fixed an issue where alert emails were not sent when the SMTP server was configured with STARTTLS and username/password authentication.\n\n- RS199037: Fixed an issue where Active-Active (CRDB) shard creation could fail with an invalid CRDT key type error.\n\n- RS199021: Fixed an issue where LDAP authentication could fail when a user's mapped group appeared beyond roughly the 100th entry in the group query results.\n\n- RS199012: Fixed a memory leak in `dmcproxy` that could occur when a multi-key command received a local `MOVED` or `CROSSSLOT` response.\n\n- RS198858: Added a readiness endpoint to the `envoy_control_plane` service to improve health reporting.\n\n- RS197703: Fixed an issue where an exception during first-node bootstrap could leave a cluster partially created without its modules.\n\n- RS196975: Fixed an issue where deleting and updating a user at the same time could corrupt the user record stored on the cluster, which was the underlying cause of cluster-wide ACL authentication failures.\n\n- RS196954: Fixed an issue where a user configuration could become corrupted if it was deleted and updated concurrently, which broke ACL authentication for all databases on the cluster and prevented users from connecting.\n\n- RS196903: Fixed an issue where upgrading a module could leave the shard manager stuck unless replica shards were stopped first.\n\n- RS196225: Fixed an issue where LDAP filters that use an `OR` clause to match multiple attributes could fail to find a unique DN for some users after upgrading to Redis Software version 8.0.x.\n\n- RS195811: Fixed an issue where the `search-workers-priority-bias-threshold` configuration could not be set at runtime.\n\n- RS195106: Fixed a cluster upgrade issue where adding multiple nodes with a new version simultaneously to a cluster could cause node configuration data to be incorrectly deleted and lead to node addition failures.\n\n- RS195089: Fixed an issue where a major reconfiguration of an Active-Active database could cause an unnecessary full synchronization across all participating clusters.\n\n- RS194243: Fixed a memory leak in the Active-Active database syncer that could occur when a participating cluster became unavailable but was not removed from the CRDB configuration.\n\n- RS193861: Fixed an issue where a cluster could become unresponsive after a node was redeployed.\n\n- RS192993: Fixed an issue where creating a new user with just-in-time (JIT) provisioning could cause authentication to fail for all databases that use ACL-only authentication.\n\n- RS192582: Fixed an issue where the creation of Redis 8.x Active-Active databases could fail when no modules were specified due to missing module semantic versions in requests.\n\n- RS192405: Improved `dmcproxy` SSL handshake error messages to provide actionable context when a TLS version mismatch occurs.\n\n- RS192048: Fixed an issue where a feature set incompatibility could cause a database upgrade to fail.\n\n- RS191757: Fixed an issue where a replica shard could fail to replicate and block failover for an extended period.\n\n- RS191347: Fixed an issue where an orphaned shard on a node could block maintenance mode and leave a database in an active-change-pending state.\n\n- RS190875: Fixed an issue where ciphers that use SHA-1 could not be removed when FIPS mode was enabled. FIPS mode now excludes SHA and CHACHA20 from `control_cipher_suites`.\n\n- RS188659: Fixed an issue where a valid IPv6 address could be misidentified as the localhost loopback address.\n\n- RS181161: Fixed an issue where access control users in an error state could not be deleted.\n\n- RS177787: Reduced the volume of message logs included in support packages.\n\n- RS176225: Fixed an issue where a backup exported from a Memcached database could not be imported.\n\n- RS148518: Fixed an issue where an SMTP server could not be configured to send email alerts.\n\n- RS144017: Fixed an issue where synchronization could fail with a `-BUSYKEY Target key name already exists` error."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"breaking-changes","title":"Breaking changes","role":"content","text":"Redis Software version 8.2.0 introduces the following breaking changes:\n\n- **Local import from Redis Software system directories is no longer supported.** To improve security, database imports that use the `mount_point` source can no longer read files from Redis Software system directories, including `/opt/redislabs`, `/var/opt/redislabs`, `/etc/opt/redislabs`, and any of their subdirectories.\n\n    - Import operations that reference files in these directories now fail.\n\n    - Store import files in a non-system location accessible to Redis Software before you start the import.\n\n- **`smtp_password` is now a write-only field in the Cluster API.** To improve security, the API no longer returns the SMTP password in responses to `GET /v1/cluster`, and the Cluster Manager UI no longer reads or displays the existing password.\n\n    - Applications or scripts that previously expected `smtp_password` to be returned by the Cluster API no longer receive this field.\n\n    - When you update SMTP settings, the existing password cannot be retrieved. To change the SMTP password, provide a new password as part of the update request.\n\n- **Custom module upload is disabled by default.** To improve security, custom (user-defined) module uploads are now disabled by default. A new cluster setting, `allow_modules_upload`, controls access to the custom module upload APIs. When this setting is disabled (the default), requests to `POST /v2/modules/user-defined` and `POST /v2/local/modules/user-defined/artifacts` return `403 modules_upload_disabled`, regardless of user role.\n\n    - To enable custom module uploads, set `allow_modules_upload` to `true` with a `PUT /v1/cluster` request.\n\n    - Applications or automation that call the custom module upload APIs directly receive a `403 modules_upload_disabled` response until the setting is enabled.\n\n    - Existing functionality for listing, deleting, and loading modules is unchanged."},{"id":"jwt-token-invalidation-on-upgrade","title":"JWT token invalidation on upgrade","role":"content","text":"To improve system security, the JWT signing secret has changed in version 8.0.22 and later. As a result, upgrading invalidates all previously issued JWT tokens, which must be regenerated. Tokens used programmatically within the cluster are regenerated automatically. Cluster Manager users must sign out and sign in again, or wait for their session or token to expire."},{"id":"reserved-ports","title":"Reserved ports","role":"content","text":"Make sure the following ports are open before upgrading Redis Software.\n\nThe following port was added as a reserved port in Redis Software version 8.0.18; however, it is optional instead of reserved in Redis Software version 8.0.20-44:\n\n| Port | Process name | Usage |\n|------|--------------|-------|\n| 3357 | reconciliation_tree_grpc | Internal communication |\n\nPorts reserved as of Redis Software version 7.22.0:\n\n| Port | Process name | Usage |\n|------|--------------|-------|\n| 3346 | cluster_api_internal | Cluster API internal port |\n| 3351 | cluster_watchdog_grpc_api | Cluster watchdog now supports gRPC |\n| 3352 | grpc_service_mesh | gRPC communication between nodes |\n| 3353 | local_grpc_service_mesh | Local gRPC services |\n| 3354 | grpc_gossip_envoy | gRPC gossip protocol communication between nodes |\n| 3355 | authentication_service | Authentication service internal port |\n\nPorts reserved as of Redis Software version 7.8.2:\n\n| Port | Process name | Usage |\n|------|--------------|-------|\n| 3347 | cert_exporter | Reports cluster certificate metrics |\n| 3348 | process_exporter | Reports process metrics for DMC and Redis processes |\n| 3349 | cluster_wd_exporter | Reports cluster watchdog metrics |\n| 3350 | db_controller | Internode communication |\n| 9091 | node_exporter | Reports host node metrics related to CPU, memory, disk, and more |\n| 9125 | statsd_exporter | Reports push metrics related to the DMC and syncer, and some cluster and node metrics |\n\nSee [Ports and port ranges used by Redis Software](https://redis.io/docs/latest/operate/rs/networking/port-configurations#ports-and-port-ranges-used-by-redis-enterprise-software) for a complete list."},{"id":"deprecations","title":"Deprecations","role":"content","text":"- The `db_conns_auditing` cluster policy and database configuration field is deprecated. Use the `audit_settings.audit_mode` field instead. Setting `db_conns_auditing` to `true` is equivalent to setting `audit_mode` to `connection`. See [Audit events](https://redis.io/docs/latest/operate/rs/security/audit-events) for the current auditing configuration.\n\n- The `gradual_src_mode`, `gradual_sync_mode`, and `gradual_sync_max_shards_per_source` parameters are deprecated. Use `gradual_sync_policy` instead. You can set it with [`rladmin tune db`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune) or the REST API.\n\n- The `old_password` field in `POST /v1/users/password` and `PUT /v1/users/password` requests is deprecated and ignored. It was optional and had no effect when adding or resetting a password, so you can remove it from these requests. `old_password` is still required for `DELETE /v1/users/password`, which uses it to identify the password to delete.\n\n- The following REST API fields, deprecated since Redis Software version 6.4.2, are removed in this release:\n\n    - `use_ipv6` (cluster object) — use `use_external_ipv6` instead.\n\n    - `redis_cleanup_job_settings` (job scheduler object) — use `persistence_cleanup_scan_interval` instead."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n**Amazon Linux 2 is no longer supported.** Starting with Redis Software version 8.0.22, Redis Software no longer builds or publishes Amazon Linux 2 (`amzn2`) packages. Use Amazon Linux 2023 instead.\n\n\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.2 | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Jul 2026 | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Jul 2028 | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2023<sup>[6](#table-note-6)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – | – | – |\n| Amazon Linux 2 | – | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance.\n\n6. <a name=\"table-note-6\"></a>Amazon Linux 2023 support was added in Redis Software version 8.0.20."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (8.2.0-25 July release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">e68acba411779c2a1ea419305e42d31d007959ed7dfe31cc7624a0ed34cee9ed</span> |\n| Ubuntu 22 (amd64) | <span class=\"break-all\">9113dc664965aaff8844b212a6625eda350c935eb8b436cb233194f213bc0893</span> |\n| Ubuntu 22 (arm64) | <span class=\"break-all\">a0611bc21fb97742100e8529f2ff56c4986175dedfa674e6abfba4e30bc7fc9a</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">b0729139cf8cde0eb93e85b8e8fae82cf5b1e35d78eba972877148ee7f879186</span> |\n| Red Hat Enterprise Linux (RHEL) 9 (amd64) | <span class=\"break-all\">b4c540c9ed785a26af90e54e196d12c3ccb9e10ca8498fe9c17f2dcbbb2c6420</span> |\n| Red Hat Enterprise Linux (RHEL) 9 (arm64) | <span class=\"break-all\">62aff83e8ff44ebd0ae9b7b94bd6fa815d2274fe8143d980e290c5daca64c0c6</span> |\n| Red Hat Enterprise Linux (RHEL) 9 FIPS (amd64) | <span class=\"break-all\">1cb61db526cd1231b6cbbc79f1087c3c9afce7f0f578c5c5f55c9972b5b05462</span> |\n| Amazon Linux 2023 (amd64) | <span class=\"break-all\">17ba316aaa3a59ac217453b470e89b95ec639e53e09bd3be47326b6869080c44</span> |\n| Amazon Linux 2023 (arm64) | <span class=\"break-all\">f4a3e5ee809a17ed1bf3b4ef54b0799df65102ac1829eac48655767441ed6219</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS155734: Endpoint availability metrics do not work as expected due to a calculation error."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Increased memory usage in FIPS mode\n\nFIPS-enabled builds use more memory than non-FIPS builds. Each Go-based control-plane service uses roughly 34 MB more when running in FIPS mode, which adds up to approximately 210 MB of additional memory per node. Account for this overhead when sizing FIPS-enabled deployments. This limitation affects FIPS-enabled builds only.\n\n#### SFTP storage not supported in FIPS mode\n\nFIPS-enabled builds do not support SFTP as a storage location for backups, imports, or exports, because the SFTP library is not FIPS-compliant. In FIPS mode, use a different storage location. This limitation affects FIPS-enabled builds only.\n\n#### Redis Search query failures during rolling upgrades across the 8.4 version boundary\n\nRedis Search queries can fail during a rolling upgrade when a cluster contains shards running Redis versions earlier than 8.4 and shards running Redis version 8.4 or later, due to an internal protocol change introduced in version 8.4. This issue affects only clusters where `parallel_shards_upgrade` has been changed from its default value of `0`. If both conditions apply, expect Redis Search downtime until all nodes are upgraded.\n\n#### Trim ACKED not supported for Active-Active 8.4 databases\n\nFor Active-Active databases running Redis database version 8.4, the `ACKED` option is not supported for trimming commands.\n\n#### Rolling upgrade limitation for clusters with custom or deprecated modules\n\nDue to module handling changes introduced in Redis Software version 8.0, upgrading a cluster that contains custom or deprecated modules, such as RedisGraph and RedisGears v2, can become stuck when adding a new node to the cluster during a rolling upgrade.\n\n#### Cluster Manager sign-outs and false \"down\" status during an Active-Active upgrade\n\nWhile an Active-Active database has participating clusters running different Redis Software versions, the Cluster Manager UI on an upgraded cluster can sign users out intermittently and show a participating cluster as down, even though replication is active and the syncers are connected.\n\nThis affects the Cluster Manager UI only. It's caused by a JWT signing-key mismatch between nodes running different versions during the upgrade window (see [JWT token invalidation on upgrade](#jwt-token-invalidation-on-upgrade)). The data path and replication are not affected.\n\nTo resolve it, upgrade the remaining participating clusters. The issue clears after every participating cluster runs the same version. If you're signed out, sign in again.\n\n#### Module commands limitation during Active-Active database upgrades to Redis 8.0\n\nWhen upgrading an Active-Active database to Redis version 8.0, you cannot use module commands until all Active-Active database instances have been upgraded. Currently, these commands are not blocked automatically.\n\n#### Redis 8.0 database cannot be created with flash\n\nYou cannot create a Redis 8.0 database with flash storage enabled. Create a Redis 8.0 database with RAM-only storage instead, or use Redis 8.2 for flash-enabled (Redis Flex) databases.\n\n#### Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead."},{"id":"security","title":"Security","role":"security","text":"#### Redis Open Source security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [Redis Open Source](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for Redis Open Source do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in Redis Open Source.\n\nRedis Software 8.2.0-25 supports Redis Open Source 8.6, 8.4, 8.2, 8.0, 7.4, 7.2, and 6.2. Below is the list of Redis Open Source CVEs and other security vulnerabilities fixed by version.\n\n\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n\n\n\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n\n\n\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- (CVE-2025-62507) A user can run the `XACKDEL` command with multiple IDs and trigger a stack buffer overflow, which can potentially lead to remote code execution.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n\n\n\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n\n\n\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n\n\n\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\n\n\n\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\n\n\n\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-8-2-releases/rs-8-2-0-46","title":"Redis Software release notes 8.2.0-46 (August 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-2-releases/rs-8-2-0-46/","summary":"Bug fixes for internal certificate rotation, rolling upgrades, the Cluster Manager UI, shard rebalancing, and Active-Active synchronization. Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:54:25+01:00","page_type":"content","content_hash":"adadf53fd808d36f03180474ac7c0d1aadebb40d461541ae3bd75d6079e26294","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Software version 8.2.0](https://redis.io/downloads/#Redis_Software)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- Bug fixes for internal certificate rotation and rolling upgrades\n\n- Bug fix for the Cluster Manager UI Overview page not loading\n\n- Bug fixes for shard rebalancing and cluster configuration store growth\n\n- Bug fixes for Active-Active synchronization\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"redis-database-versions-and-feature-sets","title":"Redis database versions and feature sets","role":"content","text":"Redis Software version 8.2.0 includes the following Redis database versions: 8.6.2, 8.4.0, 8.2.1, 8.0.2, 7.4.3, 7.2.7, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 8.6.\n\nRedis Software includes multiple feature sets, compatible with different Redis database versions.\n\n\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 8.6 | RediSearch 8.6<br />RedisJSON 8.6<br />RedisTimeSeries 8.6<br />RedisBloom 8.6<br />See [What's new in Redis 8.6](https://redis.io/docs/latest/develop/whats-new/8-6) and [Redis Open Source 8.6 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.6-release-notes) |\n| 8.4 | RediSearch 8.4<br />RedisJSON 8.4<br />RedisTimeSeries 8.4<br />RedisBloom 8.4<br />See [What's new in Redis 8.4](https://redis.io/docs/latest/develop/whats-new/8-4) and [Redis Open Source 8.4 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.4-release-notes) |\n| 8.2 | RediSearch 8.2<br />RedisJSON 8.2<br />RedisTimeSeries 8.2<br />RedisBloom 8.2<br />See [What's new in Redis 8.2](https://redis.io/docs/latest/develop/whats-new/8-2) and [Redis Open Source 8.2 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.2-release-notes) |\n| 8.0 | RediSearch 8.0<br />RedisJSON 8.0<br />RedisTimeSeries 8.0<br />RedisBloom 8.0<br />See [What's new in Redis 8.0](https://redis.io/docs/latest/develop/whats-new/8-0) and [Redis Open Source 8.0 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.0-release-notes) |\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes) |"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS200137: Fixed an issue where internal certificate rotation skipped nodes that were down when rotation started and never retried them, which left those nodes with outdated certificates after they came back online.\n\n- RS200495: Fixed an issue where `rladmin master set` could time out during a rolling upgrade when a node still running the earlier build rejected a master shard candidate running the new build while database configuration updates were in progress.\n\n- RS200753: Fixed an issue where the Cluster Manager UI Overview page did not load and returned `invalid_schema` errors for `v2/actions` requests.\n\n- RS202176: Fixed an issue where rebalancing shards while adding a node to a cluster could overflow the connection tracking (conntrack) table on multiple nodes and cause master shards to go down.\n\n- RS202429: Fixed an issue where the `sm_fixer:all` key in the cluster configuration store grew without bound.\n\n- RS209759: Fixed an issue where an unrecognized configuration change in the Active-Active (CRDB) syncer triggered a full synchronization instead of being ignored.\n\n- RS209889: Fixed an issue where an Active-Active database resent the full dataset instead of only changes during a full state sync when an internal version clock exceeded 32 bits."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"jwt-token-invalidation-on-upgrade","title":"JWT token invalidation on upgrade","role":"content","text":"To improve system security, the JWT signing secret has changed in version 8.0.22 and later. As a result, upgrading invalidates all previously issued JWT tokens, which must be regenerated. Tokens used programmatically within the cluster are regenerated automatically. Cluster Manager users must sign out and sign in again, or wait for their session or token to expire."},{"id":"reserved-ports","title":"Reserved ports","role":"content","text":"Make sure the following ports are open before upgrading Redis Software.\n\nThe following port was added as a reserved port in Redis Software version 8.0.18; however, it is optional instead of reserved in Redis Software version 8.0.20-44:\n\n| Port | Process name | Usage |\n|------|--------------|-------|\n| 3357 | reconciliation_tree_grpc | Internal communication |\n\nPorts reserved as of Redis Software version 7.22.0:\n\n| Port | Process name | Usage |\n|------|--------------|-------|\n| 3346 | cluster_api_internal | Cluster API internal port |\n| 3351 | cluster_watchdog_grpc_api | Cluster watchdog now supports gRPC |\n| 3352 | grpc_service_mesh | gRPC communication between nodes |\n| 3353 | local_grpc_service_mesh | Local gRPC services |\n| 3354 | grpc_gossip_envoy | gRPC gossip protocol communication between nodes |\n| 3355 | authentication_service | Authentication service internal port |\n\nPorts reserved as of Redis Software version 7.8.2:\n\n| Port | Process name | Usage |\n|------|--------------|-------|\n| 3347 | cert_exporter | Reports cluster certificate metrics |\n| 3348 | process_exporter | Reports process metrics for DMC and Redis processes |\n| 3349 | cluster_wd_exporter | Reports cluster watchdog metrics |\n| 3350 | db_controller | Internode communication |\n| 9091 | node_exporter | Reports host node metrics related to CPU, memory, disk, and more |\n| 9125 | statsd_exporter | Reports push metrics related to the DMC and syncer, and some cluster and node metrics |\n\nSee [Ports and port ranges used by Redis Software](https://redis.io/docs/latest/operate/rs/networking/port-configurations#ports-and-port-ranges-used-by-redis-enterprise-software) for a complete list."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n**Amazon Linux 2 is no longer supported.** Starting with Redis Software version 8.0.22, Redis Software no longer builds or publishes Amazon Linux 2 (`amzn2`) packages. Use Amazon Linux 2023 instead.\n\n\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.2 | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Jul 2026 | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Jul 2028 | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2023<sup>[6](#table-note-6)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – | – | – |\n| Amazon Linux 2 | – | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance.\n\n6. <a name=\"table-note-6\"></a>Amazon Linux 2023 support was added in Redis Software version 8.0.20."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (8.2.0-46 August release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">f403c0a0029e477552d23892f4822e6709a84e9c664e16c6ba881f83002cb7a0</span> |\n| Ubuntu 22 (amd64) | <span class=\"break-all\">dc78326342dac6b20d80a4d7eaf4ca581ec59f60a1639385395f09f4dfb0aca0</span> |\n| Ubuntu 22 (arm64) | <span class=\"break-all\">1fef79f41f681a11df1ca3075491ac387348781f0e8d7d333fdf0ac26cf6674b</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">44d91105a41ab9aba51eb2d2b44bfb45360f1e28fa9bb5f824e0705f93d71864</span> |\n| Red Hat Enterprise Linux (RHEL) 9 (amd64) | <span class=\"break-all\">ea6c5983d902571d59f63263cc636cc811438367b40da6230cc7dce03145b07d</span> |\n| Red Hat Enterprise Linux (RHEL) 9 (arm64) | <span class=\"break-all\">a99719de0779cafa591ec614e5e8b0bfcde5617bed889e14feea71b77b01d6ef</span> |\n| Red Hat Enterprise Linux (RHEL) 9 FIPS (amd64) | <span class=\"break-all\">341c9198f349cc65e8d212fcff1055af3ae44c6125b8363024da57cc244426bf</span> |\n| Amazon Linux 2023 (amd64) | <span class=\"break-all\">d810b015198c118546b99ffca3528936029fc832f393c1cc727e93f0035e6218</span> |\n| Amazon Linux 2023 (arm64) | <span class=\"break-all\">9929eee00837ef1d7f8e333a29ec72133e090071954c65e66613ff51bd6b71f6</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS155734: Endpoint availability metrics do not work as expected due to a calculation error."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Increased memory usage in FIPS mode\n\nFIPS-enabled builds use more memory than non-FIPS builds. Each Go-based control-plane service uses roughly 34 MB more when running in FIPS mode, which adds up to approximately 210 MB of additional memory per node. Account for this overhead when sizing FIPS-enabled deployments. This limitation affects FIPS-enabled builds only.\n\n#### SFTP storage not supported in FIPS mode\n\nFIPS-enabled builds do not support SFTP as a storage location for backups, imports, or exports, because the SFTP library is not FIPS-compliant. In FIPS mode, use a different storage location. This limitation affects FIPS-enabled builds only.\n\n#### Redis Search query failures during rolling upgrades across the 8.4 version boundary\n\nRedis Search queries can fail during a rolling upgrade when a cluster contains shards running Redis versions earlier than 8.4 and shards running Redis version 8.4 or later, due to an internal protocol change introduced in version 8.4. This issue affects only clusters where `parallel_shards_upgrade` has been changed from its default value of `0`. If both conditions apply, expect Redis Search downtime until all nodes are upgraded.\n\n#### Trim ACKED not supported for Active-Active 8.4 databases\n\nFor Active-Active databases running Redis database version 8.4, the `ACKED` option is not supported for trimming commands.\n\n#### Rolling upgrade limitation for clusters with custom or deprecated modules\n\nDue to module handling changes introduced in Redis Software version 8.0, upgrading a cluster that contains custom or deprecated modules, such as RedisGraph and RedisGears v2, can become stuck when adding a new node to the cluster during a rolling upgrade.\n\n#### Cluster Manager sign-outs and false \"down\" status during an Active-Active upgrade\n\nWhile an Active-Active database has participating clusters running different Redis Software versions, the Cluster Manager UI on an upgraded cluster can sign users out intermittently and show a participating cluster as down, even though replication is active and the syncers are connected.\n\nThis affects the Cluster Manager UI only. It's caused by a JWT signing-key mismatch between nodes running different versions during the upgrade window (see [JWT token invalidation on upgrade](#jwt-token-invalidation-on-upgrade)). The data path and replication are not affected.\n\nTo resolve it, upgrade the remaining participating clusters. The issue clears after every participating cluster runs the same version. If you're signed out, sign in again.\n\n#### Module commands limitation during Active-Active database upgrades to Redis 8.0\n\nWhen upgrading an Active-Active database to Redis version 8.0, you cannot use module commands until all Active-Active database instances have been upgraded. Currently, these commands are not blocked automatically.\n\n#### Redis 8.0 database cannot be created with flash\n\nYou cannot create a Redis 8.0 database with flash storage enabled. Create a Redis 8.0 database with RAM-only storage instead, or use Redis 8.2 for flash-enabled (Redis Flex) databases.\n\n#### Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead."},{"id":"security","title":"Security","role":"security","text":"#### Redis Open Source security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [Redis Open Source](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for Redis Open Source do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in Redis Open Source.\n\nRedis Software 8.2.0-46 supports Redis Open Source 8.6, 8.4, 8.2, 8.0, 7.4, 7.2, and 6.2. Below is the list of Redis Open Source CVEs and other security vulnerabilities fixed by version.\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- VSET: Missing `level` cap when loading a serialized HNSW graph may lead to resource exhaustion.\n\n- VSET: Missing validation of the parameter count when loading a serialized vector set may lead to oversized allocation and resource exhaustion.\n\n- Certificate common-name handling can allow an authentication bypass when `tls-auth-clients-user` is set to `CN`.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- VSET: Missing `level` cap when loading a serialized HNSW graph may lead to resource exhaustion.\n\n- VSET: Missing validation of the parameter count when loading a serialized vector set may lead to oversized allocation and resource exhaustion.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- VSET: Missing `level` cap when loading a serialized HNSW graph may lead to resource exhaustion.\n\n- VSET: Missing validation of the parameter count when loading a serialized vector set may lead to oversized allocation and resource exhaustion.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- (CVE-2025-62507) A user can run the `XACKDEL` command with multiple IDs and trigger a stack buffer overflow, which can potentially lead to remote code execution.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\n\n\n\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/release-notes/rs-8-2-releases/rs-8-2-0-78","title":"Redis Software release notes 8.2.0-78 (September 2026)","url":"https://redis.io/docs/latest/operate/rs/release-notes/rs-8-2-releases/rs-8-2-0-78/","summary":"Bug fixes for minor version upgrades, shard placement, LDAP client authentication, database connectivity, DMC proxy connection cleanup, internode encryption certificates, Auto Tiering database resizing, crdb-cli certificate-based authentication, metrics export, module configuration, and support package collection. Internal fixes and improvements.","tags":["docs","operate","rs"],"last_updated":"2026-10-02T11:12:43-05:00","page_type":"content","content_hash":"d50a57c2cb36bc694b87fffd6ecb5c0b47d8dbafed40cab690f48e5f4c270eb5","sections":[{"id":"overview","title":"Overview","role":"overview","text":"This is a maintenance release for ​[​Redis Software version 8.2.0](https://redis.io/downloads/#Redis_Software)."},{"id":"highlights","title":"Highlights","role":"content","text":"This version offers:\n\n- FIPS 140-3 support for AWS EKS with Bottlerocket FIPS worker nodes\n\n- Bug fixes for minor version upgrades and shard placement\n\n- Bug fix for LDAP client authentication\n\n- Bug fixes for database connectivity and DMC proxy connection cleanup\n\n- Bug fix for applying custom internode encryption certificates\n\n- Bug fix for resizing a database with Auto Tiering enabled\n\n- Bug fix for `crdb-cli` certificate-based authentication\n\n- Bug fixes for metrics export and Prometheus ingestion\n\n- Bug fixes for module configuration and support package collection\n\n- Internal fixes and improvements"},{"id":"new-in-this-release","title":"New in this release","role":"content","text":""},{"id":"fips-140-3-support-for-aws-eks","title":"FIPS 140-3 support for AWS EKS","role":"content","text":"Redis Software now supports FIPS 140-3 for RHEL 9 containerized Redis on AWS EKS with Bottlerocket FIPS worker nodes, using cryptographic libraries based on FIPS 140-3 validated modules, enabling the use of FIPS-approved cryptographic algorithms in supported environments."},{"id":"redis-database-versions-and-feature-sets","title":"Redis database versions and feature sets","role":"content","text":"Redis Software version 8.2.0 includes the following Redis database versions: 8.6.2, 8.4.0, 8.2.1, 8.0.2, 7.4.3, 7.2.7, and 6.2.13.\n\nThe [default Redis database version](https://redis.io/docs/latest/operate/rs/databases/configure/db-defaults#database-version) is 8.6.\n\nRedis Software includes multiple feature sets, compatible with different Redis database versions.\n\n\n\nThe following table shows which Redis modules are compatible with each Redis database version included in this release.\n\n| Redis database version | Compatible Redis modules |\n|------------------------|--------------------------|\n| 8.6 | RediSearch 8.6<br />RedisJSON 8.6<br />RedisTimeSeries 8.6<br />RedisBloom 8.6<br />See [What's new in Redis 8.6](https://redis.io/docs/latest/develop/whats-new/8-6) and [Redis Open Source 8.6 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.6-release-notes) |\n| 8.4 | RediSearch 8.4<br />RedisJSON 8.4<br />RedisTimeSeries 8.4<br />RedisBloom 8.4<br />See [What's new in Redis 8.4](https://redis.io/docs/latest/develop/whats-new/8-4) and [Redis Open Source 8.4 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.4-release-notes) |\n| 8.2 | RediSearch 8.2<br />RedisJSON 8.2<br />RedisTimeSeries 8.2<br />RedisBloom 8.2<br />See [What's new in Redis 8.2](https://redis.io/docs/latest/develop/whats-new/8-2) and [Redis Open Source 8.2 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.2-release-notes) |\n| 8.0 | RediSearch 8.0<br />RedisJSON 8.0<br />RedisTimeSeries 8.0<br />RedisBloom 8.0<br />See [What's new in Redis 8.0](https://redis.io/docs/latest/develop/whats-new/8-0) and [Redis Open Source 8.0 release notes](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisce/redisos-8.0-release-notes) |\n| 7.4 | [RediSearch 2.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.10-release-notes)<br />[RedisJSON 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.8-release-notes)<br />[RedisTimeSeries 1.12](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.12-release-notes)<br />[RedisBloom 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.8-release-notes) |\n| 7.2 | [RediSearch 2.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.8-release-notes)<br />[RedisJSON 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.6-release-notes)<br />[RedisTimeSeries 1.10](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.10-release-notes)<br />[RedisBloom 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.6-release-notes) |\n| 6.2 | [RediSearch 2.6](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisearch/redisearch-2.6-release-notes)<br />[RedisJSON 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisjson/redisjson-2.4-release-notes)<br />[RedisTimeSeries 1.8](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redistimeseries/redistimeseries-1.8-release-notes)<br />[RedisBloom 2.4](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/release-notes/redisbloom/redisbloom-2.4-release-notes) |"},{"id":"resolved-issues","title":"Resolved issues","role":"content","text":"- RS149489: Fixed an issue where Redis Software exported metrics samples that shared a timestamp but carried different values, which caused Prometheus to reject them with an ingestion error.\n\n- RS192976: Fixed an issue where a master shard could not be reached while the shard placement blueprint was applied through migrations and failovers.\n\n- RS197092: Fixed an issue where databases became unreachable after a quorum node went down.\n\n- RS197402: Fixed an issue where LDAP client authentication failed after a period of time.\n\n- RS205561: Fixed an issue where v2 metrics were exported with a duplicate cluster label, which caused Grafana dashboards based on v2 metrics to show no data.\n\n- RS206926: Fixed an issue where the DMC proxy could fail an assertion while cleaning up an SSL client disconnection over IPv6.\n\n- RS208996: Fixed an issue where applying a custom internode encryption certificate restarted the heartbeat daemon and made the cluster configuration store and the management API on port 9443 unavailable cluster-wide.\n\n- RS210365: Fixed an issue where removing module arguments from a database configuration did not update the `redis.conf` file on the shards.\n\n- RS212043: Fixed an issue where `crdb-cli` did not use the configured client key when certificate-based authentication was enabled.\n\n- RS212448: Fixed an issue where resizing a database with Auto Tiering enabled did not complete after a cluster upgrade, which left the database in a pending state and caused subsequent requests to return a busy error.\n\n- RS213492: Fixed an issue where shards loaded slowly from persistence during a minor version upgrade of a non-replicated database, which extended the time the database spent in a loading state.\n\n- RS215377: Fixed an issue where `rladmin cluster debug_info` silently omitted nodes whose collection failed, which produced a support package that appeared complete."},{"id":"version-changes","title":"Version changes","role":"compatibility","text":""},{"id":"jwt-token-invalidation-on-upgrade","title":"JWT token invalidation on upgrade","role":"content","text":"To improve system security, the JWT signing secret has changed in version 8.0.22 and later. As a result, upgrading invalidates all previously issued JWT tokens, which must be regenerated. Tokens used programmatically within the cluster are regenerated automatically. Cluster Manager users must sign out and sign in again, or wait for their session or token to expire."},{"id":"reserved-ports","title":"Reserved ports","role":"content","text":"Make sure the following ports are open before upgrading Redis Software.\n\nThe following port was added as a reserved port in Redis Software version 8.0.18; however, it is optional instead of reserved in Redis Software version 8.0.20-44:\n\n| Port | Process name | Usage |\n|------|--------------|-------|\n| 3357 | reconciliation_tree_grpc | Internal communication |\n\nPorts reserved as of Redis Software version 7.22.0:\n\n| Port | Process name | Usage |\n|------|--------------|-------|\n| 3346 | cluster_api_internal | Cluster API internal port |\n| 3351 | cluster_watchdog_grpc_api | Cluster watchdog now supports gRPC |\n| 3352 | grpc_service_mesh | gRPC communication between nodes |\n| 3353 | local_grpc_service_mesh | Local gRPC services |\n| 3354 | grpc_gossip_envoy | gRPC gossip protocol communication between nodes |\n| 3355 | authentication_service | Authentication service internal port |\n\nPorts reserved as of Redis Software version 7.8.2:\n\n| Port | Process name | Usage |\n|------|--------------|-------|\n| 3347 | cert_exporter | Reports cluster certificate metrics |\n| 3348 | process_exporter | Reports process metrics for DMC and Redis processes |\n| 3349 | cluster_wd_exporter | Reports cluster watchdog metrics |\n| 3350 | db_controller | Internode communication |\n| 9091 | node_exporter | Reports host node metrics related to CPU, memory, disk, and more |\n| 9125 | statsd_exporter | Reports push metrics related to the DMC and syncer, and some cluster and node metrics |\n\nSee [Ports and port ranges used by Redis Software](https://redis.io/docs/latest/operate/rs/networking/port-configurations#ports-and-port-ranges-used-by-redis-enterprise-software) for a complete list."},{"id":"supported-platforms","title":"Supported platforms","role":"compatibility","text":"The following table provides a snapshot of supported platforms as of this Redis Software release. See the [supported platforms reference](https://redis.io/docs/latest/operate/rs/references/supported-platforms) for more details about operating system compatibility.\n\n**Amazon Linux 2 is no longer supported.** Starting with Redis Software version 8.0.22, Redis Software no longer builds or publishes Amazon Linux 2 (`amzn2`) packages. Use Amazon Linux 2023 instead.\n\n\n\n<span title=\"Check mark icon\">&#x2705;</span> Supported – The platform is supported for this version of Redis Software and Redis Stack modules.\n\n<span title=\"Warning icon\" class=\"font-serif\">:warning:</span> Deprecation warning – The platform is still supported for this version of Redis Software, but support will be removed in a future release.\n\n| Redis Software<br />major versions | 8.2 | 8.0 | 7.22 | 7.8 | 7.4 | 7.2 | 6.4 | 6.2 |\n|---------------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|\n| **Release date** | Jul 2026 | Oct 2025 | May 2025 | Nov 2024 | Feb 2024 | Aug 2023 | Feb 2023 | Aug 2021 |\n| [**End-of-life date**](https://redis.io/docs/latest/operate/rs/installing-upgrading/product-lifecycle#endoflife-schedule) | Determined after<br />next major release | Jul 2028 | Oct 2027 | May 2027 | Nov 2026 | Feb 2026 | Aug 2025 | Feb 2025 |\n| **Platforms** | | | | | | | | |\n| RHEL 9 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – |\n| RHEL 9<br />FIPS mode<sup>[5](#table-note-5)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| RHEL 8 &<br />compatible distros<sup>[1](#table-note-1)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| RHEL 7 &<br />compatible distros<sup>[1](#table-note-1)</sup> | – | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 22.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – |\n| Ubuntu 20.04<sup>[2](#table-note-2)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Ubuntu 18.04<sup>[2](#table-note-2)</sup> | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Ubuntu 16.04<sup>[2](#table-note-2)</sup> | – | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Amazon Linux 2023<sup>[6](#table-note-6)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – | – | – | – | – | – |\n| Amazon Linux 2 | – | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | – |\n| Amazon Linux 1 | – | – | – | – | – | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> | <span title=\"Deprecated\" class=\"font-serif\">:warning:</span> |\n| Kubernetes<sup>[3](#table-note-3)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n| Docker<sup>[4](#table-note-4)</sup> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> | <span title=\"Supported\">&#x2705;</span> |\n\n1. <a name=\"table-note-1\"></a>The RHEL-compatible distributions CentOS, CentOS Stream, Alma, and Rocky are supported if they have full RHEL compatibility. Oracle Linux running the Red Hat Compatible Kernel (RHCK) is supported, but the Unbreakable Enterprise Kernel (UEK) is not supported.\n\n2. <a name=\"table-note-2\"></a>The server version of Ubuntu is recommended for production installations. The desktop version is only recommended for development deployments.\n\n3. <a name=\"table-note-3\"></a>See the [Redis Enterprise for Kubernetes documentation](https://redis.io/docs/latest/operate/kubernetes/reference/supported_k8s_distributions) for details about support per version and Kubernetes distribution.\n\n4. <a name=\"table-note-4\"></a>[Docker images](https://redis.io/docs/latest/operate/rs/installing-upgrading/quickstarts/docker-quickstart) of Redis Software are certified for development and testing only.\n\n5. <a name=\"table-note-5\"></a>Supported only if [FIPS was enabled during RHEL installation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/switching-rhel-to-fips-mode_security-hardening#proc_installing-the-system-with-fips-mode-enabled_switching-rhel-to-fips-mode) to ensure FIPS compliance.\n\n6. <a name=\"table-note-6\"></a>Amazon Linux 2023 support was added in Redis Software version 8.0.20."},{"id":"downloads","title":"Downloads","role":"content","text":"The following table shows the SHA256 checksums for the available packages:\n\n| Package | SHA256 checksum (8.2.0-78 September release) |\n|---------|---------------------------------------|\n| Ubuntu 20 | <span class=\"break-all\">9b6e72f4a6e33edfc1fcfecb8c3e2d9fa3eaa7046671769b9ef4577232bcc752</span> |\n| Ubuntu 22 (amd64) | <span class=\"break-all\">c8db7bdd96d62477ba63b0cd6394cdb8f9b9324a42d75b7555184cd5af9c8ac7</span> |\n| Ubuntu 22 (arm64) | <span class=\"break-all\">49df72b4dc94d8d1e82c93b8e7d6ceb619a54c1de39635c0fe181be35a02d496</span> |\n| Red Hat Enterprise Linux (RHEL) 8 | <span class=\"break-all\">3f2e2982de2635c3dd95cf9c6e61cc3017cdecaf6882443f78a8ac0ef3b4b35e</span> |\n| Red Hat Enterprise Linux (RHEL) 9 (amd64) | <span class=\"break-all\">90f6d4b0384309a8a0cd56483c2792ba70431c97e901beec3619eadfbb453767</span> |\n| Red Hat Enterprise Linux (RHEL) 9 (arm64) | <span class=\"break-all\">47d8a20b7400f888ea6eb91fcdf4e5a4a8f0907466cd2e939f862c0a92853b0f</span> |\n| Red Hat Enterprise Linux (RHEL) 9 FIPS (amd64) | <span class=\"break-all\">f6dfd1a5ff272c83ca58c596636ddfd579fcc8d29ae725e9f1db8001831b7e6a</span> |\n| Amazon Linux 2023 (amd64) | <span class=\"break-all\">bd9e235ad9e5691bd28c2f044e5cb82487855c5936400b3e6e32dc3c4ed0c36a</span> |\n| Amazon Linux 2023 (arm64) | <span class=\"break-all\">19edcb85236007f1dcd6b62aeea0828f439a20b76247c75303a1f4592d1fb748</span> |"},{"id":"known-issues","title":"Known issues","role":"content","text":"- RS155734: Endpoint availability metrics do not work as expected due to a calculation error."},{"id":"known-limitations","title":"Known limitations","role":"content","text":"#### Increased memory usage in FIPS mode\n\nFIPS-enabled builds use more memory than non-FIPS builds. Each Go-based control-plane service uses roughly 34 MB more when running in FIPS mode, which adds up to approximately 210 MB of additional memory per node. Account for this overhead when sizing FIPS-enabled deployments. This limitation affects FIPS-enabled builds only.\n\n#### SFTP storage not supported in FIPS mode\n\nFIPS-enabled builds do not support SFTP as a storage location for backups, imports, or exports, because the SFTP library is not FIPS-compliant. In FIPS mode, use a different storage location. This limitation affects FIPS-enabled builds only.\n\n#### Redis Search query failures during rolling upgrades across the 8.4 version boundary\n\nRedis Search queries can fail during a rolling upgrade when a cluster contains shards running Redis versions earlier than 8.4 and shards running Redis version 8.4 or later, due to an internal protocol change introduced in version 8.4. This issue affects only clusters where `parallel_shards_upgrade` has been changed from its default value of `0`. If both conditions apply, expect Redis Search downtime until all nodes are upgraded.\n\n#### Trim ACKED not supported for Active-Active 8.4 databases\n\nFor Active-Active databases running Redis database version 8.4, the `ACKED` option is not supported for trimming commands.\n\n#### Rolling upgrade limitation for clusters with custom or deprecated modules\n\nDue to module handling changes introduced in Redis Software version 8.0, upgrading a cluster that contains custom or deprecated modules, such as RedisGraph and RedisGears v2, can become stuck when adding a new node to the cluster during a rolling upgrade.\n\n#### Cluster Manager sign-outs and false \"down\" status during an Active-Active upgrade\n\nWhile an Active-Active database has participating clusters running different Redis Software versions, the Cluster Manager UI on an upgraded cluster can sign users out intermittently and show a participating cluster as down, even though replication is active and the syncers are connected.\n\nThis affects the Cluster Manager UI only. It's caused by a JWT signing-key mismatch between nodes running different versions during the upgrade window (see [JWT token invalidation on upgrade](#jwt-token-invalidation-on-upgrade)). The data path and replication are not affected.\n\nTo resolve it, upgrade the remaining participating clusters. The issue clears after every participating cluster runs the same version. If you're signed out, sign in again.\n\n#### Module commands limitation during Active-Active database upgrades to Redis 8.0\n\nWhen upgrading an Active-Active database to Redis version 8.0, you cannot use module commands until all Active-Active database instances have been upgraded. Currently, these commands are not blocked automatically.\n\n#### Redis 8.0 database cannot be created with flash\n\nYou cannot create a Redis 8.0 database with flash storage enabled. Create a Redis 8.0 database with RAM-only storage instead, or use Redis 8.2 for flash-enabled (Redis Flex) databases.\n\n#### Cluster Manager UI limitations\n\nThe following legacy UI features are not yet available in the new Cluster Manager UI:\n\n- Purge an Active-Active instance.\n\n    Use [`crdb-cli crdb purge-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/purge-instance) instead."},{"id":"security","title":"Security","role":"security","text":"#### Redis Open Source security fixes compatibility\n\nAs part of Redis's commitment to security, Redis Software implements the latest [security fixes](https://github.com/redis/redis/releases) available with [Redis Open Source](https://github.com/redis/redis). Redis Software has already included the fixes for the relevant CVEs.\n\nSome CVEs announced for Redis Open Source do not affect Redis Software due to different or additional functionality available in Redis Software that is not available in Redis Open Source.\n\nRedis Software 8.2.0-78 supports Redis Open Source 8.6, 8.4, 8.2, 8.0, 7.4, 7.2, and 6.2. Below is the list of Redis Open Source CVEs and other security vulnerabilities fixed by version.\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- VSET: Missing `level` cap when loading a serialized HNSW graph may lead to resource exhaustion.\n\n- VSET: Missing validation of the parameter count when loading a serialized vector set may lead to oversized allocation and resource exhaustion.\n\n- Certificate common-name handling can allow an authentication bypass when `tls-auth-clients-user` is set to `CN`.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- VSET: Missing `level` cap when loading a serialized HNSW graph may lead to resource exhaustion.\n\n- VSET: Missing validation of the parameter count when loading a serialized vector set may lead to oversized allocation and resource exhaustion.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- VSET: Missing `level` cap when loading a serialized HNSW graph may lead to resource exhaustion.\n\n- VSET: Missing validation of the parameter count when loading a serialized vector set may lead to oversized allocation and resource exhaustion.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- (CVE-2025-62507) A user can run the `XACKDEL` command with multiple IDs and trigger a stack buffer overflow, which can potentially lead to remote code execution.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- VSET: Large `VADD REDUCE` values can lead to out-of-memory crashes or buffer overflow.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- The `HGETEX` command can lead to a buffer overflow.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.2.1)\n\n\n\n\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory buffers, which can result in incorrect accounting of buffer sizes and lead to heap overflow and potential remote code execution.\n\n- (CVE-2023-41053) Redis does not correctly identify keys accessed by `SORT_RO` and, as a result, may grant users executing this command access to keys that are not explicitly authorized by the ACL configuration. (Redis 7.0.13)\n\n- (CVE-2023-36824) Extracting key names from a command and a list of arguments may, in some cases, trigger a heap overflow and result in reading random heap memory, heap corruption, and potentially remote code execution. Specifically: using `COMMAND GETKEYS*` and validation of key names in ACL rules. (Redis 7.0.12)\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 7.0.11)\n\n- (CVE-2023-28425) Specially crafted `MSETNX` commands can lead to assertion and denial-of-service. (Redis 7.0.10)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 7.0.9)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 7.0.8)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 7.0.9)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 7.0.8)\n\n- (CVE-2022-35951) Executing an `XAUTOCLAIM` command on a stream key in a specific state, with a specially crafted `COUNT` argument, may cause an integer overflow, a subsequent heap overflow, and potentially lead to remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.5)\n\n- (CVE-2022-31144) A specially crafted `XAUTOCLAIM` command on a stream key in a specific state may result in heap overflow and potentially remote code execution. The problem affects Redis versions 7.0.0 or newer. (Redis 7.0.4)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 7.0.12)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 7.0.0)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 7.0.0)\n\n\n\n\n\n- Memory corruption in `SLOT_INFO` handling may allow an unauthenticated user to potentially run their own code.\n\n- A use-after-free in TLS pending-data processing may allow an authenticated user to cause a denial-of-service.\n\n- A vulnerability in the `RESTORE` command and in RDB loading allows an authenticated user to trigger a use-after-free in stream consumer groups via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- An ACL key-pattern bypass in `GEORADIUS` and `GEORADIUSBYMEMBER` may allow an authenticated user to write to keys outside their permitted key patterns.\n\n- An out-of-bounds read during command key extraction may cause the server to crash.\n\n- (CVE-2026-62356) An authenticated user may trigger a heap out-of-bounds write in RedisBloom by loading a specially crafted Count-Min Sketch (`CMSketch`) object, for example via the `RESTORE` command or a crafted RDB file.\n\n- RedisBloom: Out-of-bounds access in the `TopK` heap cleanup path.\n\n- (CVE-2026-23479) When a blocked client is evicted while re-executing a blocked command, an authenticated user may trigger a use-after-free and potentially lead to remote code execution.\n\n- (CVE‑2026‑25243) A vulnerability in the Redis `RESTORE` command allows an authenticated user to trigger an invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-25588) A vulnerability in the `RESTORE` command, when used with the RedisTimeSeries module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE‑2026‑25589) A vulnerability in the `RESTORE` command, when used with the RedisBloom module, allows an authenticated attacker to trigger invalid memory access via a specially crafted serialized payload, potentially resulting in remote code execution.\n\n- (CVE-2026-23631) An authenticated user may exploit the synchronization mechanism of the master-replica and trigger a use-after-free vulnerability, potentially leading to remote code execution. The bug affects only replicas that are configured, or may be configured with `replica-read-only` disabled, and exists in all versions of Redis with Lua scripting.\n\n- Insufficient sanitization of Lua script error messages can allow an authenticated user with `EVAL` permission to inject arbitrary RESP protocol responses, leading to denial of service, data manipulation, and connection pool poisoning.\n\n- RedisBloom: Integer overflow in `TopK` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `td_histogram_t` RDB loading may lead to heap buffer overflow.\n\n- RedisBloom: Missing validation in `CuckooFilter` RDB loading may lead to heap out-of-bounds access.\n\n- RedisBloom: Missing validation in `SBChain` and `CMSketch` RDB loading may lead to heap out-of-bounds write.\n\n- Added validation of limits during RDB load and data creation to avoid resource exhaustion and Denial-of-Service (DoS) vulnerabilities.\n\n- RedisBloom: Restore invalid filter.\n\n- Integer overflow in `hllPatLen`.\n\n- RedisBloom: Cuckoo filter counter overflow.\n\n- RedisBloom: Invalid Bloom filters can cause arbitrary memory reads and writes.\n\n- RedisBloom: Reachable assert in `TopK_Create`\n\n- RedisBloom: Out-of-bounds access with empty Bloom chains.\n\n- RedisBloom: Division by zero in Cuckoo filter insertion.\n\n- (CVE-2025-46818) An authenticated user may use a specially crafted Lua script to manipulate different LUA objects and potentially run their own code in the context of another user.\n\n- (CVE-2025-46819) An authenticated user may use a specially crafted LUA script to read out-of-bound data or crash the server and lead to subsequent denial of service.\n\n- (CVE-2025-46817) An authenticated user may use a specially crafted Lua script to cause an integer overflow and potentially lead to remote code execution.\n\n- (CVE-2025-49844) An authenticated user may use a specially crafted Lua script to manipulate the garbage collector, trigger a use-after-free, and potentially lead to remote code execution.\n\n- (CVE-2025-32023) An authenticated user can use a specially crafted string to trigger a stack/heap out-of-bounds write on HyperLogLog operations, which can lead to remote code execution.\n\n- (CVE-2025-21605) An unauthenticated client can cause unlimited growth of output buffers until the server runs out of memory or is terminated, which can lead to denial-of-service.\n\n- (CVE-2024-31449) An authenticated user may use a specially crafted Lua script to trigger a stack buffer overflow in the bit library, which may potentially lead to remote code execution.\n\n- (CVE-2024-31228) An authenticated user can trigger a denial-of-service by using specially crafted, long string match patterns on supported commands such as `KEYS`, `SCAN`, `PSUBSCRIBE`, `FUNCTION LIST`, `COMMAND LIST`, and ACL definitions. Matching of extremely long patterns may result in unbounded recursion, leading to stack overflow and process crashes.\n\n- (CVE-2023-28856) Authenticated users can use the `HINCRBYFLOAT` command to create an invalid hash field that will crash Redis on access. (Redis 6.2.12)\n\n- (CVE-2023-25155) Specially crafted `SRANDMEMBER`, `ZRANDMEMBER`, and `HRANDFIELD` commands can trigger an integer overflow, resulting in a runtime assertion and termination of the Redis server process. (Redis 6.2.11)\n\n- (CVE-2023-22458) Integer overflow in the Redis `HRANDFIELD` and `ZRANDMEMBER` commands can lead to denial-of-service. (Redis 6.2.9)\n\n- (CVE-2022-36021) String matching commands (like `SCAN` or `KEYS`) with a specially crafted pattern to trigger a denial-of-service attack on Redis can cause it to hang and consume 100% CPU time. (Redis 6.2.11)\n\n- (CVE-2022-35977) Integer overflow in the Redis `SETRANGE` and `SORT`/`SORT_RO` commands can drive Redis to OOM panic. (Redis 6.2.9)\n\n- (CVE-2022-24834) A specially crafted Lua script executing in Redis can trigger a heap overflow in the cjson and cmsgpack libraries, and result in heap corruption and potentially remote code execution. The problem exists in all versions of Redis with Lua scripting support, starting from 2.6, and affects only authenticated and authorized users. (Redis 6.2.13)\n\n- (CVE-2022-24736) An attacker attempting to load a specially crafted Lua script can cause NULL pointer dereference which will result in a crash of the `redis-server` process. This issue affects all versions of Redis. (Redis 6.2.7)\n\n- (CVE-2022-24735) By exploiting weaknesses in the Lua script execution environment, an attacker with access to Redis can inject Lua code that will execute with the (potentially higher) privileges of another Redis user. (Redis 6.2.7)\n\n- (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when `proto-max-bulk-len` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32762) Integer to heap buffer overflow issue in `redis-cli` and `redis-sentinel` parsing large multi-bulk replies on some older and less common platforms. (Redis 6.2.6)\n\n- (CVE-2021-32761) An integer overflow bug in Redis version 2.2 or newer can be exploited using the `BITFIELD` command to corrupt the heap and potentially result with remote code execution. (Redis 6.2.5)\n\n- (CVE-2021-32687) Integer to heap buffer overflow with intsets, when `set-max-intset-entries` is manually configured to a non-default, very large value. (Redis 6.2.6)\n\n- (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections. (Redis 6.2.6)\n\n- (CVE-2021-32672) Random heap reading issue with Lua Debugger. (Redis 6.2.6)\n\n- (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for `hash-max-ziplist-entries`, `hash-max-ziplist-value`, `zset-max-ziplist-entries` or `zset-max-ziplist-value`. (Redis 6.2.6)\n\n- (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for `proto-max-bulk-len` and `client-query-buffer-limit`. (Redis 6.2.6)\n\n- (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow. (Redis 6.2.6)\n\n- (CVE-2021-32625) An integer overflow bug in Redis version 6.0 or newer can be exploited using the STRALGO LCS command to corrupt the heap and potentially result with remote code execution. This is a result of an incomplete fix by CVE-2021-29477. (Redis 6.2.4)\n\n- (CVE-2021-29478) An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and potentially result with remote code execution. The vulnerability involves changing the default set-max-intset-entries configuration value, creating a large set key that consists of integer values and using the COPY command to duplicate it. The integer overflow bug exists in all versions of Redis starting with 2.6, where it could result with a corrupted RDB or DUMP payload, but not exploited through COPY (which did not exist before 6.2). (Redis 6.2.3)\n\n- (CVE-2021-29477) An integer overflow bug in Redis version 6.0 or newer could be exploited using the STRALGO LCS command to corrupt the heap and potentially result in remote code execution. The integer overflow bug exists in all versions of Redis starting with 6.0. (Redis 6.2.3)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/rs-archive","title":"Archive","url":"https://redis.io/docs/latest/operate/rs/rs-archive/","summary":"Describes where to view the archive of Redis Enterprise Software documentation.","tags":["docs","operate","rs"],"last_updated":"2024-04-03T13:28:25-07:00","page_type":"content","content_hash":"7f739fdbe2242936f88a70f7691ad51b06c272286332f051b958f0396afda31c","sections":[{"id":"content","title":"Content","role":"content","text":"Previous versions of Redis Enterprise Software documentation are available on the archived web site:\n\n- [Redis Enterprise Software v7.4 documentation archive](https://docs.redis.com/7.4/rs/)  \n\n- [Redis Enterprise Software v7.2 documentation archive](https://docs.redis.com/7.2/rs/) \n\n- [Redis Enterprise Software v6.4 documentation archive](https://docs.redis.com/6.4/rs/) \n\n- [Redis Enterprise Software v6.2 documentation archive](https://docs.redis.com/6.2/rs/) \n\n- [Redis Enterprise Software v6.0 documentation archive](https://docs.redis.com/6.0/rs/)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/security/access-control/create-cluster-roles","title":"Create roles with cluster access only","url":"https://redis.io/docs/latest/operate/rs/security/access-control/create-cluster-roles/","summary":"Create roles with cluster access only.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:27:03+01:00","page_type":"content","content_hash":"3af45dcb23ce769928421ab46ad82106af499258802959fe211b1d12c093a381","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Roles with cluster access allow access to the Cluster Management UI and REST API."},{"id":"default-management-roles","title":"Default management roles","role":"content","text":"Redis Software includes five predefined roles that determine a user's level of access to the Cluster Manager UI and [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api).\n\n1. **DB Viewer** - Read database settings\n1. **DB Member** - Administer databases\n1. **Cluster Viewer** - Read cluster settings\n1. **Cluster Member** - Administer the cluster\n1. **User Manager** - Administer users\n1. **Admin** - Full cluster access\n1. **None** - For data access only - cannot access the Cluster Manager UI or use the REST API\n\nFor more details about the privileges granted by each of these roles, see [Cluster Manager UI permissions](#cluster-manager-ui-permissions) or [REST API permissions](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions)."},{"id":"cluster-manager-ui-permissions","title":"Cluster Manager UI permissions","role":"content","text":"Here's a summary of the Cluster Manager UI actions permitted by each default management role:\n\n| Action | DB Viewer | DB Member | Cluster Viewer | Cluster Member | Admin | User Manager |\n|--------|:---------:|:---------:|:--------------:|:-----------:|:------:|:------:|\n| Create, edit, delete users and LDAP mappings | <span title=\"Not allowed\">&#x274c; No</span> | <span title=\"Not allowed\">&#x274c; No</span> | <span title=\"Not allowed\">&#x274c; No</span> | <span title=\"Not allowed\">&#x274c; No</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Allowed\">&#x2705; Yes</span> |\n| Create support package | <span title=\"Not allowed\">&#x274c; No</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Not allowed\">&#x274c; No</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Not allowed\">&#x274c; No</span> |\n| Edit database configuration | <span title=\"Not allowed\">&#x274c; No</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Not allowed\">&#x274c; No</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Not allowed\">&#x274c; No</span> |\n| Reset slow log | <span title=\"Not allowed\">&#x274c; No</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Not allowed\">&#x274c; No</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Not allowed\">&#x274c; No</span> |\n| View cluster configuration | <span title=\"Not allowed\">&#x274c; No</span> | <span title=\"Not allowed\">&#x274c; No</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Allowed\">&#x2705; Yes</span> |\n| View cluster logs | <span title=\"Not allowed\">&#x274c; No</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Allowed\">&#x2705; Yes</span><br /> | <span title=\"Allowed\">&#x2705; Yes</span><br /> |\n| View cluster metrics | <span title=\"Not allowed\">&#x274c; No</span> | <span title=\"Not allowed\">&#x274c; No</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Allowed\">&#x2705; Yes</span> |\n| View database configuration | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Allowed\">&#x2705; Yes</span> |\n| View database metrics | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Allowed\">&#x2705; Yes</span> |\n| View node configuration | <span title=\"Not allowed\">&#x274c; No</span> | <span title=\"Not allowed\">&#x274c; No</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Allowed\">&#x2705; Yes</span> |\n| View node metrics | <span title=\"Not allowed\">&#x274c; No</span> | <span title=\"Not allowed\">&#x274c; No</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Allowed\">&#x2705; Yes</span> |\n| View Redis database password | <span title=\"Not allowed\">&#x274c; No</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Not allowed\">&#x274c; No</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Allowed\">&#x2705; Yes</span> |\n| View slow log | <span title=\"Not allowed\">&#x274c; No</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Not allowed\">&#x274c; No</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Not allowed\">&#x274c; No</span> |\n| View and edit cluster settings | <span title=\"Not allowed\">&#x274c; No</span> | <span title=\"Not allowed\">&#x274c; No</span> | <span title=\"Not allowed\">&#x274c; No</span> | <span title=\"Not allowed\">&#x274c; No</span> | <span title=\"Allowed\">&#x2705; Yes</span> | <span title=\"Not allowed\">&#x274c; No</span> |"},{"id":"create-cluster-role","title":"Create roles for cluster access","role":"content","text":"You can use the [Cluster Manager UI](#create-roles-ui) or the [REST API](#define-roles-rest-api) to create a role that grants cluster access but does not grant access to any databases.\n\n**Cluster Manager UI:**\n\nTo create a role that grants cluster access using the Cluster Manager UI:\n\n1. From **Access Control** > **Roles**, you can:\n\n    - Select a role from the list of existing roles to edit it.\n\n    - Click **+ Add role** to create a new role.\n\n    ![Add role with name](https://redis.io/docs/latest/images/rs/screenshots/access-control/7-22-updates/roles-screen.png)\n\n1. Enter a descriptive name for the role.\n\n1. Choose a **Management role** to determine cluster management permissions.\n\n    ![Select a cluster management role to set the level of cluster management permissions for the new role.](https://redis.io/docs/latest/images/rs/screenshots/access-control/7-22-updates/rbac-create-role-cluster-only.png)\n    \n1. To prevent database access when using this role, do not add any ACLs.\n\n1. Click **Save**.\n\n**REST API:**\n\nTo [create a role](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/roles#post-role) that grants cluster access using the REST API:\n\n[code example]\n\n\n\nYou can [assign the new role to users](https://redis.io/docs/latest/operate/rs/security/access-control/create-users#assign-roles-to-users) to grant cluster access."},{"id":"scope-a-role-to-specific-databases","title":"Scope a role to specific databases","role":"content","text":"By default, the `db_member` and `db_viewer` [management roles](#default-management-roles) grant access to every database in the cluster. In a shared or multi-tenant cluster, you can scope one of these roles to specific databases so that users assigned the role can view or manage only those databases.\n\nTo scope a role, add the `resources` field when you [create](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/roles#post-role) or [update](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/roles#put-role) a role with the REST API. The `resources` field is a list of resource scopes; each scope has a `type` (currently only `db`) and a `uids` array of the database IDs the role applies to.\n\nFor example, the following request creates a `db_member` role scoped to databases `1` and `2`:\n\n[code example]\n\nWhen you [assign this role to a user](https://redis.io/docs/latest/operate/rs/security/access-control/create-users#assign-roles-to-users):\n\n- The user can view or manage only the databases listed in the role's `resources` scope.\n\n- In the Cluster Manager UI and REST API responses, the user sees only their authorized databases, including in database lists, metrics, alerts, and event logs. Other databases are hidden.\n\nTo return a role to cluster-wide scope, update it with an empty `resources` list or omit the field. Roles without a `resources` scope apply to all databases, so existing roles are unaffected.\n\n> [!NOTE]\n> Database scoping applies to the `db_member` and `db_viewer` management roles."}],"examples":[{"id":"create-cluster-role-ex0","language":"sh","code":"POST /v1/roles\n{ \n  \"name\": \"<role-name>\",\n  \"management\": \"db_viewer | db_member | cluster_viewer | cluster_member | user_manager | admin\" \n}","section_id":"create-cluster-role"},{"id":"scope-a-role-to-specific-databases-ex0","language":"sh","code":"POST /v1/roles\n{\n  \"name\": \"team-a-dbs\",\n  \"management\": \"db_member\",\n  \"resources\": [\n    { \"type\": \"db\", \"uids\": [\"1\", \"2\"] }\n  ]\n}","section_id":"scope-a-role-to-specific-databases"}]}
{"schema_version":2,"id":"operate/rs/security/access-control/create-combined-roles","title":"Create roles with combined access","url":"https://redis.io/docs/latest/operate/rs/security/access-control/create-combined-roles/","summary":"Create roles with both cluster and database access.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:27:03+01:00","page_type":"content","content_hash":"aefbf4f3a33b75da06662a655d62813ba80ced781b4f78feaddce629ece4c4f2","sections":[{"id":"overview","title":"Overview","role":"overview","text":"To create a role that grants database access privileges and allows access to the Cluster Management UI and REST API:\n\n1. [Define Redis ACLs](#define-redis-acls) that determine database access privileges.\n\n1. [Create a role with ACLs](#create-role) added and choose a **Management role** other than **None**."},{"id":"define-redis-acls","title":"Define Redis ACLs","role":"content","text":"You can use the [Cluster Manager UI](#define-acls-ui) or the [REST API](#define-acls-rest-api) to define Redis ACL rules that you can assign to roles.\n\n**Cluster Manager UI:**\n\nTo define a Redis ACL rule using the Cluster Manager UI:\n\n1. From **Access Control > Roles > Data ACLs**, you can either:\n\n    - Select an existing Redis ACL from the list to edit it.\n\n    - Click **+ Add Redis ACL** to create a new Redis ACL.\n\n1. Enter a descriptive name for the Redis ACL. This will be used to associate the ACL rule with the role.\n\n1. Define the ACL rule. For more information about Redis ACL rules and syntax, see the [Redis ACL overview](https://redis.io/docs/latest/operate/rs/security/access-control/redis-acl-overview).\n\n1. Click **Save**.\n\n**REST API:**\n\nTo define a Redis ACL rule using the REST API, use a [create Redis ACL](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/redis_acls#post-redis_acl) request. For more information about Redis ACL rules and syntax, see the [Redis ACL overview](https://redis.io/docs/latest/operate/rs/security/access-control/redis-acl-overview).\n\nExample request:\n\n[code example]\n\nExample response body:\n\n[code example]\n\nTo associate the Redis ACL with a role and database, use the `uid` from the response as the `redis_acl_uid` when you add `roles_permissions` to the database.\n\n\n\n> [!NOTE]\n> For multi-key commands on multi-slot keys, the return value is `failure`, but the command runs on the keys that are allowed."},{"id":"create-role","title":"Create roles with ACLs and cluster access","role":"content","text":"You can create a role that grants database access privileges and allows access to the Cluster Management UI and REST API.\n\n**Cluster Manager UI:**\n\nTo define a role for combined access using the Cluster Manager UI:\n\n1. From **Access Control** > **Roles**, you can:\n\n    - Select a role from the list of existing roles to edit it.\n\n    - Click **+ Add role** to create a new role.\n\n    ![Add role with name](https://redis.io/docs/latest/images/rs/screenshots/access-control/7-22-updates/roles-screen.png)\n\n1. Enter a descriptive name for the role. This will be used to reference the role when configuring users.\n\n1. Choose a **Management role** other than **None**. For details about permissions granted by each role, see [Cluster Manager UI permissions](https://redis.io/docs/latest/operate/rs/security/access-control/create-cluster-roles#cluster-manager-ui-permissions) and [REST API permissions](https://redis.io/docs/latest/operate/rs/references/rest-api/permissions).\n\n    ![Add role with name](https://redis.io/docs/latest/images/rs/screenshots/access-control/7-22-updates/create-role-combined-access-select-management-role.png)\n    \n1. Click **+ Add ACL**.\n\n1.  Choose a Redis ACL and databases to associate with the role.\n\n    ![Add databases to access](https://redis.io/docs/latest/images/rs/screenshots/access-control/7-22-updates/create-role-combined-access-select-db.png)\n\n1. Click the check mark to confirm.\n\n1. Click **Save**.\n\n**REST API:**\n\nTo define a role for combined access using the REST API:\n\n1. Use a [create role](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/roles#post-role) request:\n\n    [code example]\n\n    Example response body:\n\n    [code example]\n\n    To associate the role with a Redis ACL and database, use the `uid` from the response as the `role_uid` when you add `roles_permissions` to the database.\n\n2. [Update a database's configuration](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#put-bdbs) to add `roles_permissions` with the role and Redis ACL:\n\n    [code example]\n\n\n\nYou can [assign the new role to users](https://redis.io/docs/latest/operate/rs/security/access-control/create-users#assign-roles-to-users) to grant database access and access to the Cluster Manager UI and REST API."}],"examples":[{"id":"define-redis-acls-ex0","language":"sh","code":"POST /v1/redis_acls\n{ \n  \"name\": \"Test_ACL_1\",\n  \"acl\": \"+@read +FT.INFO +FT.SEARCH\"\n}","section_id":"define-redis-acls"},{"id":"define-redis-acls-ex1","language":"json","code":"{ \n  \"acl\": \"+@read +FT.INFO +FT.SEARCH\",\n  \"name\": \"Test_ACL_1\",\n  \"uid\": 11\n}","section_id":"define-redis-acls"},{"id":"create-role-ex0","language":"sh","code":"POST /v1/roles\n    { \n      \"name\": \"<role-name>\",\n      \"management\": \"db_viewer | db_member | cluster_viewer | cluster_member | admin\" \n    }","section_id":"create-role"},{"id":"create-role-ex1","language":"json","code":"{ \n      \"management\": \"admin\",\n      \"name\": \"<role-name>\",\n      \"uid\": 7\n    }","section_id":"create-role"},{"id":"create-role-ex2","language":"sh","code":"POST /v1/bdbs/<database-id>\n    {\n      \"roles_permissions\":\n      [\n        {\n          \"role_uid\": <integer>,\n          \"redis_acl_uid\": <integer>\n        }\n      ]\n    }","section_id":"create-role"}]}
{"schema_version":2,"id":"operate/rs/security/access-control/create-db-roles","title":"Create roles with database access only","url":"https://redis.io/docs/latest/operate/rs/security/access-control/create-db-roles/","summary":"Create roles with database access only.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:27:03+01:00","page_type":"content","content_hash":"f5426e5dac1893c58b777af442a4dce4e8c00848531cf4a1a548d5a41e5f23dc","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Roles with database access grant the ability to access and interact with a database's data. Database access privileges are determined by defining [Redis ACLs](https://redis.io/docs/latest/operate/rs/security/access-control/redis-acl-overview) and adding them to roles.\n\nTo create a role that grants database access without granting access to the Redis Software Cluster Manager UI and REST API:\n\n1. [Define Redis ACLs](#define-redis-acls) that determine database access privileges.\n\n1. [Create a role with ACLs](#create-roles-with-acls) added and leave the **Management role** as **None**."},{"id":"define-redis-acls","title":"Define Redis ACLs","role":"content","text":"You can use the [Cluster Manager UI](#define-acls-ui) or the [REST API](#define-acls-rest-api) to define Redis ACL rules that you can assign to roles.\n\n**Cluster Manager UI:**\n\nTo define a Redis ACL rule using the Cluster Manager UI:\n\n1. From **Access Control > Roles > Data ACLs**, you can either:\n\n    - Select an existing Redis ACL from the list to edit it.\n\n    - Click **+ Add Redis ACL** to create a new Redis ACL.\n\n1. Enter a descriptive name for the Redis ACL. This will be used to associate the ACL rule with the role.\n\n1. Define the ACL rule. For more information about Redis ACL rules and syntax, see the [Redis ACL overview](https://redis.io/docs/latest/operate/rs/security/access-control/redis-acl-overview).\n\n1. Click **Save**.\n\n**REST API:**\n\nTo define a Redis ACL rule using the REST API, use a [create Redis ACL](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/redis_acls#post-redis_acl) request. For more information about Redis ACL rules and syntax, see the [Redis ACL overview](https://redis.io/docs/latest/operate/rs/security/access-control/redis-acl-overview).\n\nExample request:\n\n[code example]\n\nExample response body:\n\n[code example]\n\nTo associate the Redis ACL with a role and database, use the `uid` from the response as the `redis_acl_uid` when you add `roles_permissions` to the database.\n\n\n\n> [!NOTE]\n> For multi-key commands on multi-slot keys, the return value is `failure`, but the command runs on the keys that are allowed."},{"id":"create-roles-with-acls","title":"Create roles with ACLs","role":"content","text":"To create a role that grants database access to users but blocks access to the Redis Software Cluster Manager UI and REST API, set the **Management role** to **None**.\n\n**Cluster Manager UI:**\n\nTo define a role for database access using the Cluster Manager UI:\n\n1. From **Access Control** > **Roles**, you can:\n\n    - Select a role from the list of existing roles to edit it.\n\n    - Click **+ Add role** to create a new role.\n\n    ![Add role with name](https://redis.io/docs/latest/images/rs/screenshots/access-control/7-22-updates/roles-screen.png)\n\n1. Enter a descriptive name for the role. This will be used to reference the role when configuring users.\n\n1. Leave **Management role** as the default **None**.\n    \n1. Click **+ Add ACL**.\n\n1.  Choose a Redis ACL and databases to associate with the role.\n\n    ![Add databases to access](https://redis.io/docs/latest/images/rs/screenshots/access-control/7-22-updates/create-role-db-access-only.png)\n\n1. Click the check mark to confirm.\n\n1. Click **Save**.\n\n**REST API:**\n\nTo define a role for database access using the REST API:\n\n1. Use a [create role](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/roles#post-role) request:\n\n    [code example]\n\n    Example response body:\n\n    [code example]\n\n    To associate the role with a Redis ACL and database, use the `uid` from the response as the `role_uid` when you add `roles_permissions` to the database.\n\n1. [Update a database's configuration](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#put-bdbs) to add `roles_permissions` with the role and Redis ACL:\n\n    [code example]\n\n\n\nYou can [assign the new role to users](https://redis.io/docs/latest/operate/rs/security/access-control/create-users#assign-roles-to-users) to grant database access."}],"examples":[{"id":"define-redis-acls-ex0","language":"sh","code":"POST /v1/redis_acls\n{ \n  \"name\": \"Test_ACL_1\",\n  \"acl\": \"+@read +FT.INFO +FT.SEARCH\"\n}","section_id":"define-redis-acls"},{"id":"define-redis-acls-ex1","language":"json","code":"{ \n  \"acl\": \"+@read +FT.INFO +FT.SEARCH\",\n  \"name\": \"Test_ACL_1\",\n  \"uid\": 11\n}","section_id":"define-redis-acls"},{"id":"create-roles-with-acls-ex0","language":"sh","code":"POST /v1/roles\n    { \n      \"name\": \"<role-name>\",\n      \"management\": \"none\" \n    }","section_id":"create-roles-with-acls"},{"id":"create-roles-with-acls-ex1","language":"json","code":"{ \n      \"management\": \"none\",\n      \"name\": \"<role-name>\",\n      \"uid\": 7\n    }","section_id":"create-roles-with-acls"},{"id":"create-roles-with-acls-ex2","language":"sh","code":"POST /v1/bdbs/<database-id>\n    {\n      \"roles_permissions\":\n      [\n        {\n          \"role_uid\": <integer>,\n          \"redis_acl_uid\": <integer>\n        }\n      ]\n    }","section_id":"create-roles-with-acls"}]}
{"schema_version":2,"id":"operate/rs/security/access-control/create-users","title":"Create users","url":"https://redis.io/docs/latest/operate/rs/security/access-control/create-users/","summary":"Create users and assign access control roles.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"d0957d9c3658668c314d2563d211d9c341aa171d0f5c349cd82609bc36f9b02c","sections":[{"id":"prerequisites","title":"Prerequisites","role":"overview","text":"Before you create other users:\n\n1. Review the [access control overview](https://redis.io/docs/latest/operate/rs/security/access-control) to learn how to use role-based access control (RBAC) to manage users' cluster access and database access.\n\n1. Create roles you can assign to users. See [Create roles with cluster access only](https://redis.io/docs/latest/operate/rs/security/access-control/create-cluster-roles), [Create roles with database access only](https://redis.io/docs/latest/operate/rs/security/access-control/create-db-roles), or [Create roles with combined access](https://redis.io/docs/latest/operate/rs/security/access-control/create-combined-roles) for instructions."},{"id":"add-users","title":"Add users","role":"content","text":"To add a user to the cluster:\n\n1. From the **Access Control > Users** tab in the Cluster Manager UI, select **+ Add user**.\n\n    ![The list of existing users on the Access Control > Users screen.](https://redis.io/docs/latest/images/rs/screenshots/access-control/7-22-updates/users-screen.png)\n\n1. Enter the name, email, and password of the new user.\n\n    > [!NOTE]\n    > - Usernames must contain ASCII characters only.\n    >\n    > - To use [single sign-on (SSO)](https://redis.io/docs/latest/operate/rs/security/access-control/saml-sso), users must have email addresses.\n    >     \n\n    ![Create user panel with fields for username, email, password, and alerts.](https://redis.io/docs/latest/images/rs/screenshots/access-control/7-22-updates/create-user-panel.png)\n\n1. Select the **Alerts** the user should receive by email:\n\n    - **Receive alerts for databases** - The alerts that are enabled for the selected databases will be sent to the user. Choose **All databases** or **Customize** to select the individual databases to send alerts for.\n    \n    - **Receive cluster alerts** - The alerts that are enabled for the cluster in **Cluster > Alerts Settings** are sent to the user.\n\n1. Click **Add permissions**.\n\n1. Assign a **Role** to the user to grant permissions for cluster management and data access.\n\n    ![Add role to new user.](https://redis.io/docs/latest/images/rs/screenshots/access-control/7-22-updates/create-user-role-select.png)\n\n1. Click **Save user**."},{"id":"assign-roles-to-users","title":"Assign roles to users","role":"content","text":"Assign a role, associated with specific databases and access control lists (ACLs), to a user to grant database access:\n\n1. From the **Access Control > Users** tab in the Cluster Manager UI, select an existing user from the list of users.\n\n1. Click the **Edit** button.\n\n1. In the **User roles** section, click **Edit**.\n\n    ![The User roles section of the Edit user panel.](https://redis.io/docs/latest/images/rs/screenshots/access-control/7-22-updates/edit-user-roles.png)\n\n1. Select a role to assign to the user.\n\n    ![Select role for user.](https://redis.io/docs/latest/images/rs/screenshots/access-control/7-22-updates/edit-user-select-role.png)\n\n1. Click **Done** to close the **Roles** dialog.\n\n1. Click **Save**."},{"id":"next-steps","title":"Next steps","role":"content","text":"Depending on the type of the user's assigned role (cluster management role or data access role), the user can now:\n\n- [Connect to a database](https://redis.io/docs/latest/operate/rs/databases/connect) associated with the role and run limited Redis commands, depending on the role's Redis ACLs.\n\n- Sign in to the Redis Software Cluster Manager UI.\n\n- Make a [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api) request."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/security/access-control/disable-basic-authentication","title":"Disable basic authentication for the REST API","url":"https://redis.io/docs/latest/operate/rs/security/access-control/disable-basic-authentication/","summary":"Disable Basic and Digest authentication for the cluster management REST API and require certificate or JWT authentication instead.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:28+01:00","page_type":"content","content_hash":"945f5f53b43dd17c646019e315dd4ba42091a8b35bfcceef37af6770f581364e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"By default, the Redis Software cluster management REST API accepts HTTP Basic authentication using a cluster username and password. Starting with Redis Software 8.2.0, you can disable Basic and Digest authentication cluster-wide with the `control_plane_basic_authentication` setting.\n\nWhen you disable basic authentication, the cluster rejects Basic and Digest authentication on all inbound REST API requests, and clients must authenticate with a client certificate (mTLS) or a JSON Web Token (JWT) instead. This reduces the cluster's attack surface by removing password-based access to the management API.\n\n> [!NOTE]\n> This setting applies only to the cluster management REST API. It does not change how clients authenticate to databases."},{"id":"before-you-begin","title":"Before you begin","role":"content","text":"> [!WARNING]\n> Before you disable basic authentication, make sure every client and tool that calls the REST API can still authenticate another way. If you disable basic authentication without another working method in place, you lose REST API access to the cluster.\n\nWhen basic authentication is disabled, other configured authentication methods—such as certificate-based (mTLS), JWT, and LDAP—continue to work. Set up at least one before you disable basic authentication:\n\n- **Certificate-based (mTLS) authentication** (recommended) — see [Certificate-based authentication](https://redis.io/docs/latest/operate/rs/security/certificates/certificate-based-authentication).\n\n- **JWT authentication** — obtain a token with an [authorize user](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/users/authorize) request, then send it as a bearer token on subsequent requests.\n\nSome cluster-management flows support **certificate credentials** when Basic and Digest authentication are disabled or unavailable. These flows don't use JWT or LDAP:\n\n- **Joining a node to the cluster** and **Active-Active database management.** Configure these flows to use certificate credentials (client certificate, client key, and trusted CA) instead of a username and password. The client certificate's signing CA must be present in the cluster's `mtls_trusted_ca`. See [Certificate-based authentication for cluster management](https://redis.io/docs/latest/operate/rs/security/certificates/certificate-based-authentication#certificate-based-authentication-for-cluster-management)."},{"id":"follow-this-order","title":"Follow this order","role":"content","text":"Set up and verify certificate-based authentication *before* you disable basic authentication. If you disable it first, you can lock yourself out of the cluster and out of Active-Active database management.\n\n1. Enable certificate-based authentication on the cluster (`mtls_certificate_authentication`) and add the client certificate's signing CA to the cluster's `mtls_trusted_ca`. See [Set up certificate-based authentication for the REST API](https://redis.io/docs/latest/operate/rs/security/certificates/certificate-based-authentication#set-up-certificate-based-authentication-for-the-rest-api).\n\n1. Verify that certificate authentication works *while basic authentication is still enabled*. Enabling certificate-based authentication is additive—it doesn't disable password-based access:\n\n    [code example]\n\n1. Verify that your client trusts the cluster's API certificate chain:\n\n    [code example]\n\n1. Configure certificate credentials for each participating cluster whose basic authentication you're disabling. Use either a username and password or certificate credentials per cluster—not both—so participating clusters can migrate one at a time. See [Manage an Active-Active database](https://redis.io/docs/latest/operate/rs/security/certificates/certificate-based-authentication#manage-an-active-active-database).\n\n1. Disable basic authentication.\n\nIf you lose access, see [Re-enable basic authentication](#re-enable-basic-authentication)—`rladmin` runs locally on a cluster node and doesn't require REST API access."},{"id":"disable-basic-authentication","title":"Disable basic authentication","role":"content","text":"Basic authentication is enabled by default (`control_plane_basic_authentication` is `true`). To disable it, use one of the following methods:\n\n**REST API:**\n\nTo disable basic authentication using the REST API, use an [update cluster settings](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster#put-cluster) request:\n\n[code example]\n\n**rladmin:**\n\nTo disable basic authentication using [`rladmin tune cluster`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune):\n\n[code example]"},{"id":"verify","title":"Verify","role":"content","text":"After you disable basic authentication, a request that uses Basic authentication returns `401 Unauthorized`:\n\n[code example]\n\nA request that uses a client certificate or a JWT succeeds. For example, using a client certificate:\n\n[code example]"},{"id":"re-enable-basic-authentication","title":"Re-enable basic authentication","role":"content","text":"> [!NOTE]\n> If disabling basic authentication left you without cluster access, use the `rladmin` method below. It runs locally on a cluster node and doesn't require REST API access.\n\nTo re-enable basic authentication, use one of the following methods:\n\n**REST API:**\n\n[code example]\n\n**rladmin:**\n\n[code example]"}],"examples":[{"id":"follow-this-order-ex0","language":"sh","code":"curl --cert client.pem --key client.key https://<host>:9443/v1/cluster\n    # HTTP 200","section_id":"follow-this-order"},{"id":"follow-this-order-ex1","language":"sh","code":"curl --cacert <ca-bundle> --cert client.pem --key client.key https://<host>:9443/v1/cluster","section_id":"follow-this-order"},{"id":"disable-basic-authentication-ex0","language":"sh","code":"PUT https://<host>:<port>/v1/cluster\n{\n    \"control_plane_basic_authentication\": false\n}","section_id":"disable-basic-authentication"},{"id":"disable-basic-authentication-ex1","language":"sh","code":"rladmin tune cluster control_plane_basic_authentication disabled","section_id":"disable-basic-authentication"},{"id":"verify-ex0","language":"sh","code":"curl -k -u \"<username>:<password>\" https://<host>:<port>/v1/cluster\n# HTTP 401 Unauthorized","section_id":"verify"},{"id":"verify-ex1","language":"sh","code":"curl -k --cert <client-cert> --key <client-key> https://<host>:<port>/v1/cluster","section_id":"verify"},{"id":"re-enable-basic-authentication-ex0","language":"sh","code":"PUT https://<host>:<port>/v1/cluster\n{\n    \"control_plane_basic_authentication\": true\n}","section_id":"re-enable-basic-authentication"},{"id":"re-enable-basic-authentication-ex1","language":"sh","code":"rladmin tune cluster control_plane_basic_authentication enabled","section_id":"re-enable-basic-authentication"}]}
{"schema_version":2,"id":"operate/rs/security/access-control","title":"Access control","url":"https://redis.io/docs/latest/operate/rs/security/access-control/","summary":"An overview of access control in Redis Software.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","children":[{"id":"operate/rs/security/access-control/create-users","summary":"Create users and assign access control roles.","title":"Create users","url":"https://redis.io/docs/latest/operate/rs/security/access-control/create-users/"},{"id":"operate/rs/security/access-control/create-cluster-roles","summary":"Create roles with cluster access only.","title":"Create roles with cluster access only","url":"https://redis.io/docs/latest/operate/rs/security/access-control/create-cluster-roles/"},{"id":"operate/rs/security/access-control/create-db-roles","summary":"Create roles with database access only.","title":"Create roles with database access only","url":"https://redis.io/docs/latest/operate/rs/security/access-control/create-db-roles/"},{"id":"operate/rs/security/access-control/create-combined-roles","summary":"Create roles with both cluster and database access.","title":"Create roles with combined access","url":"https://redis.io/docs/latest/operate/rs/security/access-control/create-combined-roles/"},{"id":"operate/rs/security/access-control/redis-acl-overview","summary":"An overview of Redis ACLs, syntax, and ACL command support in Redis Software.","title":"Overview of Redis ACLs in Redis Software","url":"https://redis.io/docs/latest/operate/rs/security/access-control/redis-acl-overview/"},{"id":"operate/rs/security/access-control/manage-users","summary":"Manage user account security settings.","title":"Manage user security","url":"https://redis.io/docs/latest/operate/rs/security/access-control/manage-users/"},{"id":"operate/rs/security/access-control/manage-passwords","summary":"Set password policies.","title":"Set password policies","url":"https://redis.io/docs/latest/operate/rs/security/access-control/manage-passwords/"},{"id":"operate/rs/security/access-control/disable-basic-authentication","summary":"Disable Basic and Digest authentication for the cluster management REST API and require certificate or JWT authentication instead.","title":"Disable basic authentication for the REST API","url":"https://redis.io/docs/latest/operate/rs/security/access-control/disable-basic-authentication/"},{"id":"operate/rs/security/access-control/ldap","summary":"Describes how Redis Software integrates LDAP authentication and authorization. Also describes how to enable LDAP for your deployment of Redis Enterprise Software.","title":"LDAP authentication","url":"https://redis.io/docs/latest/operate/rs/security/access-control/ldap/"},{"id":"operate/rs/security/access-control/saml-sso","summary":"Set up single sign-on with SAML for the Redis Software Cluster Manager UI.","title":"SAML single sign-on","url":"https://redis.io/docs/latest/operate/rs/security/access-control/saml-sso/"}],"page_type":"content","content_hash":"83a0dd346b63732c4c76b947d8b8224dcf94378aed2198bfee4a87326d8de765","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software lets you use role-based access control (RBAC) to manage users' access privileges. RBAC requires you to do the following:\n\n1. Create roles and define each role's access privileges.\n\n1. Create users and assign roles to them. The assigned role determines the user's access privileges."},{"id":"cluster-access-versus-database-access","title":"Cluster access versus database access","role":"content","text":"Redis Software allows two separate paths of access:\n\n- **Cluster access** allows performing management-related actions, such as creating databases and viewing statistics.\n\n- **Database access** allows performing data-related actions, like reading and writing data in a database.\n\nYou can grant cluster access, database access, or both to each role. These roles let you differentiate between users who can access databases and users who can access cluster management, according to your organization's security needs.\n\nThe following diagram shows three different options for roles and users:\n\n![Role-based access control diagram.](https://redis.io/docs/latest/images/rs/rbac-diagram.png)\n\n- Role A was created with permission to access the cluster and perform management-related actions. Because user A was assigned role A, they can access the cluster but cannot access databases.\n\n- Role B was created with permission to access one or more databases and perform data-related actions. Because user B was assigned role B, they cannot access the cluster but can access databases.\n\n- Role C was created with cluster access and database access permissions. Because user C was assigned role C, they can access the cluster and databases."},{"id":"default-database-access","title":"Default database access","role":"content","text":"When you create a database, [default user access](https://redis.io/docs/latest/operate/rs/security/access-control/manage-users/default-user) is enabled automatically.\n\nIf you set up role-based access controls for your database and don't require compatibility with versions earlier than Redis 6, you can [deactivate the default user](https://redis.io/docs/latest/operate/rs/security/access-control/manage-users/default-user).\n\n> [!WARNING]\n> Before you [deactivate default user access](https://redis.io/docs/latest/operate/rs/security/access-control/manage-users/default-user#deactivate-default-user), make sure the role associated with the database is [assigned to a user](https://redis.io/docs/latest/operate/rs/security/access-control/create-users#assign-roles-to-users). Otherwise, the database will be inaccessible."},{"id":"more-info","title":"More info","role":"content","text":""}],"examples":[]}
{"schema_version":2,"id":"operate/rs/security/access-control/ldap/certificate-based-authentication","title":"Certificate-based authentication for LDAP","url":"https://redis.io/docs/latest/operate/rs/security/access-control/ldap/certificate-based-authentication/","summary":"Learn how to set up certificate-based authentication for LDAP in Redis Software.","tags":[],"last_updated":"2026-09-24T09:16:28+01:00","page_type":"content","content_hash":"341b68ca50eae172408c87774da149417de835de869d0743515916ded6d9c537","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can configure Redis Software to use certificate-based authentication in combination with LDAP to authenticate and authorize users.\n\nHere's how it works:\n\n1. Users authenticate to the database using client certificates.\n\n1. Redis Software extracts the user identity from the certificate.\n\n1. Redis Software looks up the user in LDAP.\n\n1. LDAP group mappings and database ACLs determine authorization."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before enabling certificate-based authentication for LDAP, you must complete the following prerequisites:\n\n1. [Enable mutual TLS](https://redis.io/docs/latest/operate/rs/security/encryption/tls/enable-tls) for the relevant databases to set up certificate-based authentication.\n\n1. [Set up LDAP for the cluster](https://redis.io/docs/latest/operate/rs/security/access-control/ldap/enable-role-based-ldap#set-up-ldap-connection).\n\n1. [Map LDAP users to appropriate cluster roles](https://redis.io/docs/latest/operate/rs/security/access-control/ldap/map-ldap-groups-to-roles).\n\n1. [Configure database ACLs](https://redis.io/docs/latest/operate/rs/security/access-control/ldap/update-database-acls) to authorize LDAP users."},{"id":"set-up-certificate-based-authentication-for-ldap","title":"Set up certificate-based authentication for LDAP","role":"content","text":"To set up certificate-based authentication for LDAP:\n\n1. Enable certificate-based authentication in the cluster LDAP configuration using an [update LDAP configuration](https://redis.io/docs/latest/operate/rs/references/rest-api/api-reference#tag/Cluster/operation/cluster_update_ldap_config) REST API request.\n\n    To perform LDAP lookup for certificate-authenticated users:\n    \n    - Redis Software must extract an identity from the client certificate's subject line, using either the subject CN or a custom OID. Extracting user identity from SAN is not currently supported.\n    \n    - The certificate subject must follow [RFC 4514](https://datatracker.ietf.org/doc/html/rfc4514) formatting.\n\n    - Do not create local user accounts for LDAP users.\n\n    **Subject CN:**\n\nTo enable certificate-based authentication and use the certificate subject CN as the LDAP identifier:\n\n[code example]\n\n**Subject OID:**\n\nTo enable certificate-based authentication and use a custom subject OID as the LDAP identifier:\n\n[code example]\n\nThe certificate subject must include the OID.\n\n\n\n1. Enable external certificate-based authentication in cluster settings using an [update cluster settings](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster#put-cluster) REST API request:\n\n    [code example]"},{"id":"verify-your-setup","title":"Verify your setup","role":"content","text":"After configuration is complete, try to [connect to the database with certificate-based authentication](https://redis.io/docs/latest/operate/rs/security/certificates/certificate-based-authentication#authenticate-database-connections) and verify your setup."}],"examples":[{"id":"set-up-certificate-based-authentication-for-ldap-ex0","language":"sh","code":"PUT https://<host>:<port>/v1/cluster/ldap\n{\n  \"cba\": true,\n  \"cba_identity_source\": \"subject_cn\",\n  \"control_plane\": true,\n  \"data_plane\": true\n}","section_id":"set-up-certificate-based-authentication-for-ldap"},{"id":"set-up-certificate-based-authentication-for-ldap-ex1","language":"sh","code":"PUT https://<host>:<port>/v1/cluster/ldap\n{\n  \"cba\": true,\n  \"cba_identity_source\": \"subject_oid\",\n  \"cba_identity_oid\": \"1.2.3.4\",\n  \"control_plane\": true,\n  \"data_plane\": true\n}","section_id":"set-up-certificate-based-authentication-for-ldap"},{"id":"set-up-certificate-based-authentication-for-ldap-ex2","language":"sh","code":"PUT https://<host>:<port>/v1/cluster\n    {\n      \"dmc_external_cba_authentication\": true\n    }","section_id":"set-up-certificate-based-authentication-for-ldap"}]}
{"schema_version":2,"id":"operate/rs/security/access-control/ldap/enable-role-based-ldap","title":"Enable role-based LDAP","url":"https://redis.io/docs/latest/operate/rs/security/access-control/ldap/enable-role-based-ldap/","summary":"Describes how role-based LDAP authentication and authorization work, how to configure each setting, and how to validate your configuration before you save it.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"3852ab8cbe9fd548b316c34c044ca14e753e661693e4fd46fe34a24a7d7ef56c","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software uses a role-based mechanism to authorize users authenticated with LDAP authentication or with certificate-based authentication (CBA). You map LDAP groups to [access control roles](https://redis.io/docs/latest/operate/rs/security/access-control/ldap/map-ldap-groups-to-roles), and each user receives the access level of the role mapped to their group.\n\nRole-based LDAP authorizes both cluster management users (previously known as _external users_) and database users. To authenticate users with client certificates instead of passwords, see [certificate-based authentication for LDAP](https://redis.io/docs/latest/operate/rs/security/access-control/ldap/certificate-based-authentication)."},{"id":"how-role-based-ldap-works","title":"How role-based LDAP works","role":"content","text":"When a user signs in with LDAP credentials, Redis Software runs three steps. Each configuration section on this page maps to one of them:\n\n1. **Bind** — Redis Software connects to the LDAP server using a dedicated service account, the _bind user_. Redis Software uses this connection to run the search queries in the next two steps. Configure it under [LDAP server settings](#ldap-server-settings) and [Bind credentials](#bind-credentials).\n\n1. **Authenticate** — Redis Software finds the user's directory entry and verifies the supplied password by binding to the LDAP server as that user. Configure this under [Authentication query](#authentication-query).\n\n1. **Authorize** — Redis Software finds the group or groups the user belongs to, then matches them against the groups you mapped to roles under [LDAP mappings](https://redis.io/docs/latest/operate/rs/security/access-control/ldap/map-ldap-groups-to-roles). The user receives the access level of the matched group's role. Configure this under [Authorization query](#authorization-query).\n\nIf authentication succeeds but no group matches a mapping, Redis Software denies access. A common symptom is the user being redirected to a change-password screen."},{"id":"ldap-terminology","title":"LDAP terminology","role":"content","text":"| _Term_ | _Meaning_ |\n|:-------|:----------|\n| **Distinguished Name (DN)** | The full, unique path to an entry in the directory tree, for example `cn=jdoe,ou=dev,dc=example,dc=com`. |\n| **Bind DN** | The DN of the service account Redis Software uses to connect to the LDAP server and run search queries. |\n| **Base (Base DN)** | The DN where a search starts. The search looks for entries below this point in the tree. |\n| **Filter** | The LDAP search expression that selects matching entries, for example `(cn=%u)`. Enclose it in parentheses. |\n| **Scope** | How deep a search goes below the base: _baseObject_ (the base only), _singleLevel_ (one level below), or _wholeSubtree_ (the base and everything below it). |\n| **`%u`** | Placeholder replaced with the username of the person signing in. Used in the authentication query. |\n| **`%D`** | Placeholder replaced with the signing-in user's Distinguished Name. Used in the authorization query. |"},{"id":"set-up-ldap-connection","title":"Set up LDAP connection","role":"content","text":"To configure and enable LDAP from the Cluster Manager UI:\n\n1. Go to **Access Control > LDAP > Configuration**.\n\n1. Select **+ Create**.\n\n1. In **Set LDAP**, configure [LDAP server settings](#ldap-server-settings), [bind credentials](#bind-credentials), [authentication query](#authentication-query), and [authorization query](#authorization-query).\n\n   ![The LDAP configuration screen in the Cluster Manager UI](https://redis.io/docs/latest/images/rs/screenshots/access-control/ldap-config.png)\n\n1. Select **Save & Enable**.\n\nBefore you save, [validate your configuration](#validate-your-ldap-configuration) so you can confirm each value is correct."},{"id":"ldap-server-settings","title":"LDAP server settings","role":"content","text":"The **LDAP server** settings define how Redis Software connects to the LDAP server:\n\n| _Setting_ | _Description_ |\n|:----------|:--------------|\n| **Protocol type** | Underlying communication protocol; must be _LDAP_, _LDAPS_, or _STARTTLS_ |\n| **Host** | URL of the LDAP server |\n| **Port** | LDAP server port number |\n| **Trusted CA certificate** |  _(LDAPS or STARTTLS protocols only)_ PEM-encoded certificate of the certificate authority (CA) that issued the LDAP server's certificate |\n\nWhen you define multiple LDAP hosts, the directory tree structure must be identical for all hosts.\n\n#### Trusted CA certificate\n\nFor LDAPS or STARTTLS, Redis Software validates the LDAP server's certificate against the trusted CA certificate you provide, in addition to the operating system's system CA certificates. Provide the certificate of the CA that issued the server's certificate, PEM-encoded. For a certificate that chains up to a root CA, provide the root CA certificate. In a root CA certificate, the _Issuer_ and _Subject_ fields are identical.\n\nThe server's certificate must be valid for the **Host** value you configured, because Redis Software verifies the hostname against the certificate."},{"id":"bind-credentials","title":"Bind credentials","role":"content","text":"These settings define the bind user, the service account Redis Software uses to run the authentication and authorization search queries:\n\n| _Setting_ | _Description_ |\n|:----------|:--------------|\n| **Distinguished Name** | DN of the bind user. Example: `cn=admin,dc=example,dc=org` |\n| **Password** | Password of the bind user. Example: `admin1` |\n| **Client certificate authentication** |_(LDAPS or STARTTLS protocols only)_ Place checkmark to enable |\n| **Client public key** | _(LDAPS or STARTTLS protocols only)_ The client public key for authentication |\n| **Client private key** | _(LDAPS or STARTTLS protocols only)_ The client private key for authentication |"},{"id":"authentication-query","title":"Authentication query","role":"security","text":"The authentication query locates the user's entry so Redis Software can verify their password. Choose how to find the user:\n\n| _Setting_ | _Description_ |\n|:----------|:--------------|\n| **Search user by** | Either _Template_ or _Query_ |\n| **Template** | _(template search)_ A pattern that builds the user's DN directly from the username. Example: `cn=%u,ou=dev,dc=example,dc=com` |\n| **Base** | _(query search)_ Where the search for the user starts. Example: `ou=dev,dc=example,dc=com` |\n| **Filter** | _(query search)_ Selects the user entry. Enclose in parentheses. Example: `(cn=%u)` |\n| **Scope**  | _(query search)_ Must be _baseObject_, _singleLevel_, or _wholeSubtree_ |\n\nRedis Software replaces `%u` with the username of the person signing in.\n\n- Use **Template** when every user's DN follows the same pattern. Redis Software builds the DN from the template and binds directly as the user, with no search.\n- Use **Query** when you must search for the user's entry. Redis Software runs the search as the bind user, then binds as the matched user to verify the password."},{"id":"authorization-query","title":"Authorization query","role":"security","text":"The authorization query finds the groups a user belongs to. Choose how to find them:\n\n| _Setting_ | _Description_ |\n|:----------|:--------------|\n| **Search groups by** | Either _Attribute_ or _Query_ |\n| **Attribute** | _(attribute search)_ An attribute on the user's own entry that lists their groups. Example: `memberOf` (case-sensitive) |\n| **Base** | _(query search)_ Where the search for groups starts. Example: `ou=groups,dc=example,dc=com` |\n| **Filter** | _(query search)_ Selects the groups that list the user as a member. Enclose in parentheses. Example: `(member=%D)` |\n| **Scope**  | _(query search)_ Must be _baseObject_, _singleLevel_, or _wholeSubtree_ |\n\nRedis Software replaces `%D` with the Distinguished Name of the person signing in.\n\n- Use **Attribute** when the user's entry already lists its groups, for example in a `memberOf` attribute. Redis Software reads the attribute from the user entry it found during authentication.\n- Use **Query** when you must search for group entries that list the user as a member."},{"id":"authentication-timeout","title":"Authentication timeout","role":"security","text":"The **Authentication timeout** setting determines the connection timeout to the LDAP server during user authentication.\n\nBy default, the timeout is 5 seconds, which is recommended for most cases.\n\nHowever, if you enable multi-factor authentication (MFA) for your LDAP server, you might need to increase the timeout to provide enough time for MFA verification. You can set it to any integer in the range of 5-60 seconds."},{"id":"validate-your-ldap-configuration","title":"Validate your LDAP configuration","role":"content","text":"Before you save your configuration in the Cluster Manager UI, verify each setting from any machine that can reach the LDAP server using `ldapsearch`. The `ldapsearch` command-line tool is provided by `ldap-utils` on Debian and Ubuntu, or `openldap-clients` on RHEL.\n\nThese commands run the same queries Redis Software runs internally, so a successful result means the corresponding UI setting is correct. Validating each setting on its own lets you find the one wrong value instead of only seeing a failed sign-in after you save.\n\nReplace the placeholders with the values you intend to enter in the UI:\n\n| _Placeholder_ | _Setting it validates_ |\n|:--------------|:-----------------------|\n| `<HOST>` / `<PORT>`  | **Host** / **Port**  |\n| `<BIND_DN>` / `<BIND_PW>`  | **Bind credentials → Distinguished Name / Password**  |\n| `<USER>`  | A real test username (substituted for `%u`)  |\n| `<USER_DN>`  | That user's full DN (substituted for `%D`)  |\n| `<USER_PW>`  | That user's own password  |\n| `/path/to/ca.pem`  | **Trusted CA certificate** (LDAPS or STARTTLS only)  |"},{"id":"1-validate-protocol-host-port-and-trusted-ca-certificate","title":"1. Validate protocol, host, port, and trusted CA certificate","role":"content","text":"Confirm the server is reachable on the chosen protocol and port, and, for LDAPS or STARTTLS, that your CA certificate validates the server's certificate. A successful command returns the server's root entry (`result: 0 Success`); a TLS error means the CA certificate is wrong or missing.\n\n[code example]\n\nIf you configure multiple hosts, run this against each one. All hosts must share an identical directory tree structure."},{"id":"2-validate-bind-credentials","title":"2. Validate bind credentials","role":"content","text":"Confirm the bind user can authenticate. Success means the **Distinguished Name** and **Password** fields are correct. Failure returns `Invalid credentials (49)`.\n\n[code example]"},{"id":"3-validate-the-authentication-query","title":"3. Validate the authentication query","role":"content","text":"Confirm Redis Software can locate the user's entry. Substitute `<USER>` with a real username.\n\nIf you chose **Query**, this validates **Base**, **Filter**, and **Scope**. The command must return exactly one entry:\n\n[code example]\n\n`-s sub` corresponds to **Scope = wholeSubtree**; use `-s one` for **singleLevel** or `-s base` for **baseObject**. Replace the filter with your **Filter** value, substituting `%u` with `<USER>`.\n\nIf you chose **Template**, bind as the user your template produces to confirm the template resolves to a valid DN and the password authenticates:\n\n[code example]\n\nReplace the DN with your **Template** value, substituting `%u` with `<USER>`."},{"id":"4-validate-the-authorization-query","title":"4. Validate the authorization query","role":"content","text":"Confirm Redis Software can find the user's groups. The returned group DN must later match a group you enter under **LDAP mappings**.\n\nIf you chose **Attribute** (for example, `memberOf`), request that attribute from the user's entry. The `memberOf:` lines in the output are the user's groups:\n\n[code example]\n\nThe attribute name is case-sensitive.\n\nIf you chose **Query**, this validates the group **Base**, **Filter** (with `%D` = user DN), and **Scope**:\n\n[code example]"},{"id":"5-validate-the-ldap-mapping","title":"5. Validate the LDAP mapping","role":"content","text":"The most common configuration error is a mismatch between the group DN returned in step 4 and the DN entered under **LDAP mappings**. Copy the group DN exactly as `ldapsearch` returns it, including character case and spacing, into the mapping. If they do not match exactly, authentication succeeds but Redis Software denies the user access."},{"id":"end-to-end-check","title":"End-to-end check","role":"content","text":"After you save, sign in to the Cluster Manager UI, or connect to a database with `redis-cli`, using the test user's LDAP credentials. Success confirms all the steps are configured correctly."},{"id":"more-info","title":"More info","role":"content","text":"- Map LDAP groups to [access control roles](https://redis.io/docs/latest/operate/rs/security/access-control/ldap/map-ldap-groups-to-roles)\n- Update database ACLs to [authorize LDAP access](https://redis.io/docs/latest/operate/rs/security/access-control/ldap/update-database-acls)\n- Learn more about Redis Software [security and practices](https://redis.io/docs/latest/operate/rs/security)"}],"examples":[{"id":"1-validate-protocol-host-port-and-trusted-ca-certificate-ex0","language":"sh","code":"# LDAP (port 389, no encryption)\nldapsearch -x -H ldap://<HOST>:389 -s base -b \"\" \"(objectClass=*)\"\n\n# LDAPS (port 636, TLS)\nLDAPTLS_CACERT=/path/to/ca.pem \\\n  ldapsearch -x -H ldaps://<HOST>:636 -s base -b \"\" \"(objectClass=*)\"\n\n# STARTTLS (port 389, upgraded to TLS — note -ZZ)\nLDAPTLS_CACERT=/path/to/ca.pem \\\n  ldapsearch -x -ZZ -H ldap://<HOST>:389 -s base -b \"\" \"(objectClass=*)\"","section_id":"1-validate-protocol-host-port-and-trusted-ca-certificate"},{"id":"2-validate-bind-credentials-ex0","language":"sh","code":"LDAPTLS_CACERT=/path/to/ca.pem \\\n  ldapsearch -H ldaps://<HOST>:636 \\\n  -D \"<BIND_DN>\" -w '<BIND_PW>' \\\n  -s base -b \"<BIND_DN>\" \"(objectClass=*)\"","section_id":"2-validate-bind-credentials"},{"id":"3-validate-the-authentication-query-ex0","language":"sh","code":"LDAPTLS_CACERT=/path/to/ca.pem \\\n  ldapsearch -H ldaps://<HOST>:636 \\\n  -D \"<BIND_DN>\" -w '<BIND_PW>' \\\n  -b \"<BASE>\" -s sub \"(cn=<USER>)\"","section_id":"3-validate-the-authentication-query"},{"id":"3-validate-the-authentication-query-ex1","language":"sh","code":"LDAPTLS_CACERT=/path/to/ca.pem \\\n  ldapsearch -H ldaps://<HOST>:636 \\\n  -D \"cn=<USER>,ou=dev,dc=example,dc=com\" -w '<USER_PW>' \\\n  -s base -b \"cn=<USER>,ou=dev,dc=example,dc=com\" \"(objectClass=*)\"","section_id":"3-validate-the-authentication-query"},{"id":"4-validate-the-authorization-query-ex0","language":"sh","code":"LDAPTLS_CACERT=/path/to/ca.pem \\\n  ldapsearch -H ldaps://<HOST>:636 \\\n  -D \"<BIND_DN>\" -w '<BIND_PW>' \\\n  -b \"<USER_DN>\" -s base \"(objectClass=*)\" memberOf","section_id":"4-validate-the-authorization-query"},{"id":"4-validate-the-authorization-query-ex1","language":"sh","code":"LDAPTLS_CACERT=/path/to/ca.pem \\\n  ldapsearch -H ldaps://<HOST>:636 \\\n  -D \"<BIND_DN>\" -w '<BIND_PW>' \\\n  -b \"<GROUP_BASE>\" -s sub \"(member=<USER_DN>)\"","section_id":"4-validate-the-authorization-query"}]}
{"schema_version":2,"id":"operate/rs/security/access-control/ldap","title":"LDAP authentication","url":"https://redis.io/docs/latest/operate/rs/security/access-control/ldap/","summary":"Describes how Redis Software integrates LDAP authentication and authorization. Also describes how to enable LDAP for your deployment of Redis Enterprise Software.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","children":[{"id":"operate/rs/security/access-control/ldap/enable-role-based-ldap","summary":"Describes how role-based LDAP authentication and authorization work, how to configure each setting, and how to validate your configuration before you save it.","title":"Enable role-based LDAP","url":"https://redis.io/docs/latest/operate/rs/security/access-control/ldap/enable-role-based-ldap/"},{"id":"operate/rs/security/access-control/ldap/map-ldap-groups-to-roles","summary":"Describes how to map LDAP authorization groups to Redis Software roles using the Cluster Manager UI.","title":"Map LDAP groups to roles","url":"https://redis.io/docs/latest/operate/rs/security/access-control/ldap/map-ldap-groups-to-roles/"},{"id":"operate/rs/security/access-control/ldap/update-database-acls","summary":"Describes how to use the Cluster Manager UI to update database access control lists (ACLs) to authorize access to roles authorizing LDAP user access.","title":"Update database ACLs","url":"https://redis.io/docs/latest/operate/rs/security/access-control/ldap/update-database-acls/"},{"id":"operate/rs/security/access-control/ldap/certificate-based-authentication","summary":"Learn how to set up certificate-based authentication for LDAP in Redis Software.","title":"Certificate-based authentication for LDAP","url":"https://redis.io/docs/latest/operate/rs/security/access-control/ldap/certificate-based-authentication/"},{"id":"operate/rs/security/access-control/ldap/migrate-to-role-based-ldap","summary":"Describes how to migrate existing cluster-based LDAP deployments to role-based LDAP.","title":"Migrate to role-based LDAP","url":"https://redis.io/docs/latest/operate/rs/security/access-control/ldap/migrate-to-role-based-ldap/"}],"page_type":"content","content_hash":"c4eae4657f90d983d27764bbbce682d0e6bc1346cca347f0afdb859f39ae2ce6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software supports [Lightweight Directory Access Protocol](https://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol) (LDAP) authentication and authorization through its [role-based access controls](https://redis.io/docs/latest/operate/rs/security/access-control) (RBAC).  You can use LDAP to authorize access to the Cluster Manager UI and to control database access.\n\nYou can configure LDAP roles using the Redis Software Cluster Manager UI or [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/ldap_mappings)."},{"id":"how-it-works","title":"How it works","role":"content","text":"Here's how role-based LDAP integration works:\n\n![LDAP overview](https://redis.io/docs/latest/images/rs/access-control-ldap-diagram.png)\n\n1.  A user signs in with their LDAP credentials.  \n\n    Based on the LDAP configuration details, the username is mapped to an LDAP Distinguished Name.\n\n1.  A simple [LDAP bind request](https://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol#Bind_(authenticate)) is attempted using the Distinguished Name and the password.  The sign-in fails if the bind fails.\n\n1.  Obtain the user’s LDAP group memberships.\n\n    Using configured LDAP details, obtain a list of the user’s group memberships.\n\n1.  Compare the user’s LDAP group memberships to those mapped to local roles.\n\n1.  Determine if one of the user's groups is authorized to access the target resource.  If so, the user is granted the level of access authorized to the role.  \n\nTo access the Cluster Manager UI, the user needs to belong to an LDAP group mapped to an administrative role.  \n\nFor database access, the user needs to belong to an LDAP group mapped to a role listed in the database’s access control list (ACL).  The rights granted to the group determine the user's level of access."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Before you enable LDAP in Redis Software, you need:\n\n1. The following LDAP details:\n\n    - Server URI, including host, port, and protocol details.  \n    - Certificate details for secure protocols.  \n    - Bind credentials, including Distinguished Name, password, and (optionally) client public and private keys for certificate authentication.  \n    - Authentication query details, whether template or query.  \n    - Authorization query details, whether attribute or query.  \n    - The Distinguished Names of LDAP groups you’ll use to authorize access to Redis Software resources. \n\n1. The LDAP groups that correspond to the levels of access you wish to authorize.  Each LDAP group will be mapped to a Redis Software access control role.\n\n1. A Redis Software access control role for each LDAP group. Before you enable LDAP, you need to set up [role-based access controls](https://redis.io/docs/latest/operate/rs/security/access-control) (RBAC)."},{"id":"enable-ldap","title":"Enable LDAP","role":"content","text":"To enable LDAP:\n\n1.  From **Access Control > LDAP** in the Cluster Manager UI, select the **Configuration** tab and [enable LDAP access](https://redis.io/docs/latest/operate/rs/security/access-control/ldap/enable-role-based-ldap).\n\n    ![Configure LDAP screen](https://redis.io/docs/latest/images/rs/screenshots/access-control/7-22-updates/ldap-config.png)\n\n2.  Map LDAP groups to [access control roles](https://redis.io/docs/latest/operate/rs/security/access-control/ldap/map-ldap-groups-to-roles).\n\n3.  Update database access control lists (ACLs) to [authorize role access](https://redis.io/docs/latest/operate/rs/security/access-control/ldap/update-database-acls).  \n\nIf you already have appropriate roles, you can update them to include LDAP groups."},{"id":"more-info","title":"More info","role":"content","text":"- Enable and configure [role-based LDAP](https://redis.io/docs/latest/operate/rs/security/access-control/ldap/enable-role-based-ldap)\n- Map LDAP groups to [access control roles](https://redis.io/docs/latest/operate/rs/security/access-control/ldap/map-ldap-groups-to-roles)\n- Update database ACLs to [authorize LDAP access](https://redis.io/docs/latest/operate/rs/security/access-control/ldap/update-database-acls)\n- Learn more about Redis Software [security and practices](https://redis.io/docs/latest/operate/rs/security)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/security/access-control/ldap/map-ldap-groups-to-roles","title":"Map LDAP groups to roles","url":"https://redis.io/docs/latest/operate/rs/security/access-control/ldap/map-ldap-groups-to-roles/","summary":"Describes how to map LDAP authorization groups to Redis Software roles using the Cluster Manager UI.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"4143c367b522d932f50c3edd4eb4775b35099ddbfc525b6382b19d75e1be6432","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software uses a role-based mechanism to enable LDAP authentication and authorization.  \n\nOnce LDAP is enabled, you need to map LDAP groups to Redis Software access control roles."},{"id":"map-ldap-groups-to-roles","title":"Map LDAP groups to roles","role":"content","text":"To map LDAP groups to access control roles in the Cluster Manager UI:\n\n1. Select **Access Control > LDAP > Mapping**.\n\n    > [!NOTE]\n    > You can map LDAP roles when LDAP configuration is not enabled, but they won't have any effect until you [configure and enable LDAP](https://redis.io/docs/latest/operate/rs/security/access-control/ldap/enable-role-based-ldap).\n    >     \n\n    ![Enable LDAP mappings Panel](https://redis.io/docs/latest/images/rs/access-control-ldap-mappings-panel.png)\n\n1.  Select the **+ Add LDAP Mapping** button to create a new mapping and then enter the following details:\n\n    | _Setting_ | _Description_ | \n|:----------|:--------------|\n| **Name** | A descriptive, unique name for the mapping |\n| **Distinguished Name** | The distinguished name of the LDAP group to be mapped.  <br/>Example: `cn=admins,ou=groups,dc=example,dc=com` |\n| **Role** | The Redis Software access control role defined for this group |\n| **Email** | _(Optional)_ An address to receive alerts|\n| **Alerts**  | Selections identifying the desired alerts. |\n\n    ![Create LDAP mapping dialog](https://redis.io/docs/latest/images/rs/screenshots/access-control/7-22-updates/ldap-mappings-add.png)\n\n1.  When finished, select the **Save** button.\n\nCreate a mapping for each LDAP group used to authenticate and/or authorize access to Redis Software resources.\n\nThe scope of the authorization depends on the access control role:\n\n- If the role authorizes admin management, LDAP users are authorized as cluster management administrators.\n\n- If the role authorizes database access, LDAP users are authorized to use the database to the limits specified in the role.\n\n- To authorize LDAP users to specific databases, update the database access control lists (ACLs) to include the mapped LDAP role."},{"id":"more-info","title":"More info","role":"content","text":"- Enable and configure [role-based LDAP](https://redis.io/docs/latest/operate/rs/security/access-control/ldap/enable-role-based-ldap)\n- Update database ACLs to [authorize LDAP access](https://redis.io/docs/latest/operate/rs/security/access-control/ldap/update-database-acls)\n- Learn more about Redis Software [security and practices](https://redis.io/docs/latest/operate/rs/security)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/security/access-control/ldap/migrate-to-role-based-ldap","title":"Migrate to role-based LDAP","url":"https://redis.io/docs/latest/operate/rs/security/access-control/ldap/migrate-to-role-based-ldap/","summary":"Describes how to migrate existing cluster-based LDAP deployments to role-based LDAP.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:28+01:00","page_type":"content","content_hash":"286a588cf0f987f8650d1525a1fc768fc05eb78c3bf2b3d1b47f59700f10a432","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software supports LDAP through a [role-based mechanism](https://redis.io/docs/latest/operate/rs/security/access-control/ldap), first introduced [in v6.0.20](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-0-20-april-2021).\n\nEarlier versions of Redis Software supported a cluster-based mechanism; however, that mechanism was removed in v6.2.12.\n\nIf you're using the cluster-based mechanism to enable LDAP authentication, you need to migrate to the role-based mechanism before upgrading to Redis Software v6.2.12 or later."},{"id":"migration-checklist","title":"Migration checklist","role":"content","text":"This checklist covers the basic process:\n\n1.  Identify accounts per app on the customer end.\n\n1.  Create or identify an LDAP user account on the server that is responsible for LDAP authentication and authorization.\n\n1.  Create or identify an LDAP group that contains the app team members.\n\n1.  Verify or configure the Redis Software ACLs.\n\n1.  Configure each database ACL.\n\n1.  Remove the earlier \"external\" (LDAP) users from Redis Software.\n\n1.  _(Recommended)_ Update cluster configuration to replace the cluster-based configuration file.\n\n    You can use `rladmin` to update the cluster configuration:\n\n    [code example]\n\n    Here, a blank file replaces the earlier configuration.\n\n1.  Use **Access Control > LDAP > Configuration** to enable role-based LDAP.\n\n1.  Map your LDAP groups to access control roles.\n\n1.  Test application connectivity using the LDAP credentials of an app team member.\n\n1.  _(Recommended)_ Turn off default access for the database to avoid anonymous client connections.\n\n Because deployments and requirements vary, you’ll likely need to adjust these guidelines."},{"id":"test-ldap-access","title":"Test LDAP access","role":"content","text":"To test your LDAP integration, you can:\n\n- Connect with `redis-cli` and use the [`AUTH` command](https://redis.io/docs/latest/commands/auth) to test LDAP username/password credentials.\n\n- Sign in to the Cluster Manager UI using LDAP credentials authorized for admin access.\n\n- Use [Redis Insight](https://redis.io/docs/latest/develop/tools/insight) to access a database using authorized LDAP credentials.\n\n- Use the [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api) to connect using authorized LDAP credentials."},{"id":"more-info","title":"More info","role":"content","text":"- Enable and configure [role-based LDAP](https://redis.io/docs/latest/operate/rs/security/access-control/ldap/enable-role-based-ldap)\n- Map LDAP groups to [access control roles](https://redis.io/docs/latest/operate/rs/security/access-control/ldap/map-ldap-groups-to-roles)\n- Update database ACLs to [authorize LDAP access](https://redis.io/docs/latest/operate/rs/security/access-control/ldap/update-database-acls)\n- Learn more about Redis Software [security and practices](https://redis.io/docs/latest/operate/rs/security)"}],"examples":[{"id":"migration-checklist-ex0","language":" bash","code":"$ touch /tmp/saslauthd_empty.conf\n    $ rladmin cluster config saslauthd_ldap_conf \\\n         /tmp/saslauthd_empty.conf","section_id":"migration-checklist"}]}
{"schema_version":2,"id":"operate/rs/security/access-control/ldap/update-database-acls","title":"Update database ACLs","url":"https://redis.io/docs/latest/operate/rs/security/access-control/ldap/update-database-acls/","summary":"Describes how to use the Cluster Manager UI to update database access control lists (ACLs) to authorize access to roles authorizing LDAP user access.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"ce98629028cae39f8797e002d47db0864e70a2bd540cecc83f4cf0611cc1779d","sections":[{"id":"overview","title":"Overview","role":"overview","text":"To grant LDAP users access to a database, assign the mapped access role to the access control list (ACL) for the database.\n\n1.  In the Cluster Manager UI, go to **Databases**, then select the database from the list.\n\n1.  From the **Security** tab, select the **Edit** button.\n\n1.  In the **Access Control List** section, select **+ Add ACL**.\n\n    ![Updating a database access control list (ACL)](https://redis.io/docs/latest/images/rs/screenshots/databases/security-access-control-acl-only.png)\n\n1.  Select the appropriate roles and then save your changes.\n\nIf you assign multiple roles to an ACL and a user is authorized by more than one of these roles, their access is determined by the first \"matching\" rule in the list.  \n\nIf the first rule gives them read access and the third rule authorizes write access, the user will only be able to read data.  \n\nAs a result, we recommend ordering roles so that higher access roles appear before roles with more limited access."},{"id":"more-info","title":"More info","role":"content","text":"- Enable and configure [role-based LDAP](https://redis.io/docs/latest/operate/rs/security/access-control/ldap/enable-role-based-ldap)\n- Map LDAP groups to [access control roles](https://redis.io/docs/latest/operate/rs/security/access-control/ldap/map-ldap-groups-to-roles)\n- Learn more about Redis Software [security and practices](https://redis.io/docs/latest/operate/rs/security)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/security/access-control/manage-passwords/active-active-admin-credentials","title":"Update admin credentials for Active-Active databases","url":"https://redis.io/docs/latest/operate/rs/security/access-control/manage-passwords/active-active-admin-credentials/","summary":"Update admin credentials for Active-Active databases.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:28+01:00","page_type":"content","content_hash":"296a400c2615786b77953e695baa33fbb49eae8b238d16879a8cd4baca6e44f1","sections":[{"id":"content","title":"Content","role":"content","text":"Active-Active databases use administrator credentials to manage operations. When you change the administrator password on clusters with Active-Active databases, you must update the Active-Active database configuration to prevent authentication failures during Active-Active management operations.\n\n> [!WARNING]\n> Do not perform any management operations on the databases until these steps are complete.\n\nTo update the administrator password on a cluster with Active-Active databases:\n\n1. From the user management page, update the administrator user password on the clusters you want to update.\n\n1. Run [`crdb-cli crdb list`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/list) to find the `CRDB-GUID` that uniquely identifies each Active-Active database and the fully qualified domain names (`FQDN`) of each participating cluster:\n\n    [code example]\n\n    Example output:\n\n    [code example]\n\n1. Update the Active-Active database credentials using the [`crdb-cli crdb update`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/update) command:\n\n    [code example]\n\n    Replace the following values:\n\n    - `<CRDB-GUID>`: The `CRDB-GUID` from the `crdb-cli crdb list` output\n\n    - `<admin-username>`: The administrator username for the clusters\n\n    - `<REPL-ID-1>` and `<REPL-ID-2>`: The `REPL-ID` for the corresponding FQDNs\n\n    - `<FQDN-1-password>` and `<FQDN-2-password>`: The current administrator passwords for each cluster"}],"examples":[{"id":"content-ex0","language":"sh","code":"crdb-cli crdb list","section_id":"content"},{"id":"content-ex1","language":"sh","code":"CRDB-GUID                             NAME        REPL-ID  FQDN\n    4053a0dd-a4a5-4f38-b135-75b7a2dc7331  my-aa-db    1        fqdn1.example.com\n    4053a0dd-a4a5-4f38-b135-75b7a2dc7331  my-aa-db    2        fqdn2.example.com","section_id":"content"},{"id":"content-ex2","language":"sh","code":"crdb-cli crdb update \\\n      --crdb-guid <CRDB-GUID> \\\n      --credentials id=<REPL-ID-1>,username=<admin-username>,password=<FQDN-1-password> \\\n      --credentials id=<REPL-ID-2>,username=<admin-username>,password=<FQDN-2-password> \\\n      --force","section_id":"content"}]}
{"schema_version":2,"id":"operate/rs/security/access-control/manage-passwords","title":"Set password policies","url":"https://redis.io/docs/latest/operate/rs/security/access-control/manage-passwords/","summary":"Set password policies.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:28+01:00","children":[{"id":"operate/rs/security/access-control/manage-passwords/password-complexity-rules","summary":"Enable password complexity rules and configure minimum password length.","title":"Configure password complexity rules","url":"https://redis.io/docs/latest/operate/rs/security/access-control/manage-passwords/password-complexity-rules/"},{"id":"operate/rs/security/access-control/manage-passwords/password-expiration","summary":"Configure password expiration to enforce expiration of a user's password after a specified number of days.","title":"Configure password expiration","url":"https://redis.io/docs/latest/operate/rs/security/access-control/manage-passwords/password-expiration/"},{"id":"operate/rs/security/access-control/manage-passwords/rotate-passwords","summary":"Rotate user passwords.","title":"Rotate passwords","url":"https://redis.io/docs/latest/operate/rs/security/access-control/manage-passwords/rotate-passwords/"},{"id":"operate/rs/security/access-control/manage-passwords/active-active-admin-credentials","summary":"Update admin credentials for Active-Active databases.","title":"Update admin credentials for Active-Active databases","url":"https://redis.io/docs/latest/operate/rs/security/access-control/manage-passwords/active-active-admin-credentials/"},{"id":"operate/rs/security/access-control/manage-passwords/password-hashing-algorithm","summary":"Change the password hashing algorithm for user passwords in a Redis Software cluster.","title":"Change the password hashing algorithm","url":"https://redis.io/docs/latest/operate/rs/security/access-control/manage-passwords/password-hashing-algorithm/"}],"page_type":"content","content_hash":"bb37a6cec498a0e3c49f7b8a7ba248ce2efe78dfe8e730223c3e6d987b2861e8","sections":[{"id":"content","title":"Content","role":"content","text":"Redis Software provides several ways to manage the passwords of local accounts, including:\n\n- [Password complexity rules](https://redis.io/docs/latest/operate/rs/security/access-control/manage-passwords/password-complexity-rules)\n\n- [Password expiration](https://redis.io/docs/latest/operate/rs/security/access-control/manage-passwords/password-expiration)\n\n- [Password rotation](https://redis.io/docs/latest/operate/rs/security/access-control/manage-passwords/rotate-passwords)\n\nYou can also manage a user's ability to [sign in](https://redis.io/docs/latest/operate/rs/security/access-control/manage-users/login-lockout#user-login-lockout) and control [session timeout](https://redis.io/docs/latest/operate/rs/security/access-control/manage-users/login-lockout#session-timeout).\n\nTo enforce more advanced password policies, we recommend using [LDAP integration](https://redis.io/docs/latest/operate/rs/security/access-control/ldap) with an external identity provider, such as Active Directory.\n\n> [!NOTE]\n> Redis Software securely stores all user passwords using a cryptographic hash function. The default password hashing algorithm is `SHA-256`, but you can [change the password hashing algorithm](https://redis.io/docs/latest/operate/rs/security/access-control/manage-passwords/password-hashing-algorithm) to `PBKDF2` as of Redis Software version 7.8.6-13."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/security/access-control/manage-passwords/password-complexity-rules","title":"Configure password complexity rules","url":"https://redis.io/docs/latest/operate/rs/security/access-control/manage-passwords/password-complexity-rules/","summary":"Enable password complexity rules and configure minimum password length.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"1ae4026056f928735783146df9cd7f395ab79f434176c782c1515ba9cb51e825","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software provides optional password complexity rules that meet common requirements.  When enabled, these rules require the password to have:\n\n- At least 8 characters\n- At least one uppercase character\n- At least one lowercase character\n- At least one number\n- At least one special character \n\nThese requirements reflect v6.2.12 and later. Earlier versions did not support numbers or special characters as the first or the last character of a password. This restriction was removed in v6.2.12.\n\nIn addition, the password:\n\n- Cannot contain the user's email address or the reverse of the email address.\n- Cannot have more than three repeating characters.\n\nPassword complexity rules apply when a new user account is created and when the password is changed.  Password complexity rules are not applied to accounts authenticated by an external identity provider."},{"id":"enable-password-complexity-rules","title":"Enable password complexity rules","role":"content","text":"To enable password complexity rules, use one of the following methods:\n\n- Cluster Manager UI:\n\n    1. Go to **Cluster > Security > Preferences**, then select **Edit**.\n\n    1. In the **Password** section, enable **Complexity rules**.\n\n    1. Select **Save**.\n\n- [Update cluster](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster#put-cluster) REST API request:\n\n    [code example]"},{"id":"change-minimum-password-length","title":"Change minimum password length","role":"content","text":"When password complexity rules are enabled, passwords must have at least 8 characters by default.\n\nIf you change the minimum password length, the new minimum is enforced for new users and when existing users change their passwords.\n\nTo change the minimum password length, use one of the following methods:\n\n- Cluster Manager UI:\n\n    1. Go to **Cluster > Security > Preferences**.\n    \n    1. Click **Edit**.\n\n    1. In the **Password** section, enable **Complexity rules**.\n\n    1. Set the number of characters for **Minimum password length**.\n\n        ![The minimum password length setting appears in the password section of the cluster security preferences screen when complexity rules are enabled.](https://redis.io/docs/latest/images/rs/screenshots/cluster/security-preferences-min-password-length.png)\n\n    1. Click **Save**.\n\n- [Update cluster](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster#put-cluster) REST API request:\n\n    [code example]"},{"id":"deactivate-password-complexity-rules","title":"Deactivate password complexity rules","role":"content","text":"To deactivate password complexity rules, use one of the following methods:\n\n- Cluster Manager UI:\n\n    1. Go to **Cluster > Security > Preferences**, then select **Edit**.\n\n    1. In the **Password** section, turn off **Complexity rules**.\n\n    1. Select **Save**.\n\n- [Update cluster](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster#put-cluster) REST API request:\n\n    [code example]"}],"examples":[{"id":"enable-password-complexity-rules-ex0","language":"sh","code":"PUT https://[host][:port]/v1/cluster\n    { \"password_complexity\": true }","section_id":"enable-password-complexity-rules"},{"id":"change-minimum-password-length-ex0","language":"sh","code":"PUT https://[host][:port]/v1/cluster\n    { \"password_min_length\": <integer between 8-256> }","section_id":"change-minimum-password-length"},{"id":"deactivate-password-complexity-rules-ex0","language":"sh","code":"PUT https://[host][:port]/v1/cluster\n    { \"password_complexity\": false }","section_id":"deactivate-password-complexity-rules"}]}
{"schema_version":2,"id":"operate/rs/security/access-control/manage-passwords/password-expiration","title":"Configure password expiration","url":"https://redis.io/docs/latest/operate/rs/security/access-control/manage-passwords/password-expiration/","summary":"Configure password expiration to enforce expiration of a user's password after a specified number of days.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:28+01:00","page_type":"content","content_hash":"145bcc1367d044ba06f8cfab933d1ce9e9ad470f33649ff158299490f5c4dbaa","sections":[{"id":"enable-password-expiration","title":"Enable password expiration","role":"overview","text":"> [!WARNING]\n> Password expiration is calculated from the time the password was last updated, not from when the policy is enabled. Passwords that were set long enough ago to already be expired will immediately be locked out when you enable this policy. Before enabling password expiration, verify all user passwords have been updated recently enough to avoid immediate lockouts.\n\nTo enforce an expiration of a user's password after a specified number of days:\n\n- Use the Cluster Manager UI:\n\n    1. Go to **Cluster > Security > Preferences**, then select **Edit**.\n\n    1. In the **Password** section, turn on **Expiration**.\n\n    1. Enter the number of days before passwords expire.\n\n    1. Select **Save**.\n\n- Use the `cluster` endpoint of the REST API\n\n    [code example]"},{"id":"deactivate-password-expiration","title":"Deactivate password expiration","role":"content","text":"To deactivate password expiration:\n\n- Use the Cluster Manager UI:\n\n    1. Go to **Cluster > Security > Preferences**, then select **Edit**.\n\n    1. In the **Password** section, turn off **Expiration**.\n\n    1. Select **Save**.\n\n- Use the `cluster` REST API endpoint to set `password_expiration_duration` to `0` (zero)."}],"examples":[{"id":"enable-password-expiration-ex0","language":" REST","code":"PUT https://[host][:port]/v1/cluster\n    {\"password_expiration_duration\":<number_of_days>}","section_id":"enable-password-expiration"}]}
{"schema_version":2,"id":"operate/rs/security/access-control/manage-passwords/password-hashing-algorithm","title":"Change the password hashing algorithm","url":"https://redis.io/docs/latest/operate/rs/security/access-control/manage-passwords/password-hashing-algorithm/","summary":"Change the password hashing algorithm for user passwords in a Redis Software cluster.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:28+01:00","page_type":"content","content_hash":"552c1b401439af8fce26c25d0c8465988a8a1be07b45f390ee569ac20bd87ba7","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software securely stores all user passwords using a cryptographic hash function. The default password hashing algorithm is `SHA-256`, but `PBKDF2` is also supported as of Redis Software version 7.8.6-13.\n\nYou can change the password hashing algorithm using [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin) or the [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api). When you change the password hashing algorithm, the cluster rehashes the administrator password and passwords for all users, including default users."},{"id":"command-line-method","title":"Command-line method","role":"syntax","text":"To change the password hashing algorithm from the command line, run [`rladmin cluster change_password_hashing_algorithm`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/change_password_hashing_algorithm):\n\n[code example]"},{"id":"rest-api-method","title":"REST API method","role":"content","text":"You can [change the password hashing algorithm](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/change_password_hashing_algorithm#patch-change-password-hashing-algorithm) using a REST API request:\n\n[code example]"}],"examples":[{"id":"command-line-method-ex0","language":"sh","code":"rladmin cluster change_password_hashing_algorithm PBKDF2","section_id":"command-line-method"},{"id":"rest-api-method-ex0","language":"sh","code":"PATCH /v1/cluster/change_password_hashing_algorithm\n{ \"algorithm\": \"PBKDF2\" }","section_id":"rest-api-method"}]}
{"schema_version":2,"id":"operate/rs/security/access-control/manage-passwords/rotate-passwords","title":"Rotate passwords","url":"https://redis.io/docs/latest/operate/rs/security/access-control/manage-passwords/rotate-passwords/","summary":"Rotate user passwords.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:28+01:00","page_type":"content","content_hash":"c6163919ce32482fa5c6bef702792e61cbe5386999075f84069202c6dd8d2a8a","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software lets you implement password rotation policies using the [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api).\n\nYou can add a new password for a database user without immediately invalidating the old one to prevent possible authentication errors in production.\n\n> [!NOTE]\n> Password rotation does not work for the default user. [Add additional users](https://redis.io/docs/latest/operate/rs/security/access-control/create-users) to enable password rotation."},{"id":"password-rotation-policies","title":"Password rotation policies","role":"content","text":"For user access to the Redis Software Cluster Manager UI,\nyou can set a [password expiration policy](https://redis.io/docs/latest/operate/rs/security/access-control/manage-passwords/password-expiration) to prompt the user to change their password.\n\nHowever, for database connections that rely on password authentication,\nyou need to allow for authentication with the existing password while you roll out the new password to your systems.\n\nWith the Redis Software REST API, you can add additional passwords to your user account for authentication to the database or the Cluster Manager UI and API.\n\nAfter the old password is replaced in the database connections, you can delete the old password to finish the password rotation process.\n\n> [!WARNING]\n> Multiple passwords are only supported using the REST API.\n> If you reset the password for a user in the Cluster Manager UI,\n> the new password replaces all other passwords for that user.\n\nThe new password cannot already exist as a password for the user and must meet the [password complexity](https://redis.io/docs/latest/operate/rs/security/access-control/manage-passwords/password-complexity-rules) requirements, if enabled."},{"id":"rotate-password","title":"Rotate password","role":"content","text":"Admins can rotate passwords for any user. If you are not an admin, you can only rotate passwords for your account.\n\nTo rotate passwords:\n\n1. Add an additional password to a user's password list with [`POST /v1/users/password`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/users/password#add-password).\n\n    [code example]\n\n    After you send this request, the user can authenticate with both the old and the new password.\n\n1. Update the password in all database connections that connect with the user account.\n1. Delete the original password with [`DELETE /v1/users/password`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/users/password#delete-password):\n\n    [code example]\n\n    If there is only one valid password for a user account, you cannot delete that password."},{"id":"replace-all-passwords","title":"Replace all passwords","role":"content","text":"You can replace all existing passwords for your account with a single password that does not match any existing passwords. Admins can reset passwords for any user.\nThis can be helpful if you suspect that your passwords are compromised and you want to quickly resecure the account.\n\nTo replace a user's passwords, use [`PUT /v1/users/password`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/users/password#update-password).\n\n[code example]\n\nAfter this request, all of the user's existing passwords are deleted and only the new password is valid."}],"examples":[{"id":"rotate-password-ex0","language":"sh","code":"POST https://<host>:<port>/v1/users/password\n    {\n      \"username\": \"<target_username>\",\n      \"new_password\": \"<a_new_password>\"\n    }","section_id":"rotate-password"},{"id":"rotate-password-ex1","language":"sh","code":"DELETE https://<host>:<port>/v1/users/password\n    {\n      \"username\": \"<target_username>\",\n      \"old_password\": \"<existing_password_to_delete>\"\n    }","section_id":"rotate-password"},{"id":"replace-all-passwords-ex0","language":"sh","code":"PUT https://<host>:<port>/v1/users/password\n{\n  \"username\": \"<target_username>\",\n  \"new_password\": \"<a_new_password>\"\n}","section_id":"replace-all-passwords"}]}
{"schema_version":2,"id":"operate/rs/security/access-control/manage-users/default-user","title":"Manage default user","url":"https://redis.io/docs/latest/operate/rs/security/access-control/manage-users/default-user/","summary":"Manage a database's default user.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"eeecdc8e83561eaa320e2359583ef73a4aa29f59cf7ff3df3620c57f482fcaf4","sections":[{"id":"overview","title":"Overview","role":"overview","text":"When you [create a database](https://redis.io/docs/latest/operate/rs/databases/create), default user database access is enabled by default (**Unauthenticated access** is selected). This gives the default user full access to the database and enables compatibility with versions of Redis before Redis 6.\n\nSelect **Password-only authentication**, then enter and confirm a default database password to require authentication for connections to the database.\n\n![Select Password-only authentication to require a password to access the database.](https://redis.io/docs/latest/images/rs/screenshots/databases/security-access-control-password-only.png)"},{"id":"authenticate-as-default-user","title":"Authenticate as default user","role":"security","text":"When you configure a password for your database, all connections to the database must authenticate using the [AUTH](https://redis.io/docs/latest/commands/auth) command. See Redis security's [authentication](https://redis.io/docs/latest/operate/oss_and_stack/management/security#authentication) section for more information.\n\n[code example]"},{"id":"change-default-database-password","title":"Change default database password","role":"content","text":"To change the default user's password:\n\n1. From the database's **Security** tab, select **Edit**.\n\n1. In the **Access Control** section, select **Password-only authentication** as the **Access method**.\n\n1. Enter and re-enter the new password.\n\n1. Select **Save**."},{"id":"deactivate-default-user","title":"Deactivate default user","role":"content","text":"If you set up [role-based access control](https://redis.io/docs/latest/operate/rs/security/access-control) with [access control lists](https://redis.io/docs/latest/operate/rs/security/access-control/create-db-roles) (ACLs) for your database and don't require backwards compatibility with versions earlier than Redis 6, you can [deactivate the default user](https://redis.io/docs/latest/operate/rs/security/access-control/manage-users/default-user).\n\n> [!WARNING]\n> - Before you deactivate default user access, make sure the role associated with the database is [assigned to a user](https://redis.io/docs/latest/operate/rs/security/access-control/create-users). Otherwise, the database will be inaccessible.\n>\n> - If you enabled the default database password during the creation of an [Active-Active database](https://redis.io/docs/latest/operate/rs/databases/active-active), you should not turn off the default database password because it could prevent the removal of participating database instances.\n\nTo deactivate the default user:\n\n1. From the database's **Security** tab, select **Edit**.\n\n1. In the **Access Control** section, select **Using ACL only** as the **Access method**.\n\n    ![Select Using ACL only to deactivate default user access to the database.](https://redis.io/docs/latest/images/rs/screenshots/databases/security-access-control-acl-only.png)\n\n1. Choose at least one role and Redis ACL to access the database.\n\n1. Select **Save**."}],"examples":[{"id":"authenticate-as-default-user-ex0","language":"sh","code":"AUTH <default-database-password>","section_id":"authenticate-as-default-user"}]}
{"schema_version":2,"id":"operate/rs/security/access-control/manage-users","title":"Manage user security","url":"https://redis.io/docs/latest/operate/rs/security/access-control/manage-users/","summary":"Manage user account security settings.","tags":["docs","operate","rs"],"last_updated":"2026-01-30T12:29:26-06:00","children":[{"id":"operate/rs/security/access-control/manage-users/login-lockout","summary":"Manage user login lockout and session timeout.","title":"Manage user login","url":"https://redis.io/docs/latest/operate/rs/security/access-control/manage-users/login-lockout/"},{"id":"operate/rs/security/access-control/manage-users/default-user","summary":"Manage a database's default user.","title":"Manage default user","url":"https://redis.io/docs/latest/operate/rs/security/access-control/manage-users/default-user/"}],"page_type":"content","content_hash":"822e63106859e0774df77d925f2f8f121fc6a3bc3918a8f8c6136478e0d75ce6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software supports the following user account security settings:\n\n- Password complexity\n- Password expiration\n- User lockouts\n- Account inactivity timeout"},{"id":"manage-users-and-user-security","title":"Manage users and user security","role":"content","text":""}],"examples":[]}
{"schema_version":2,"id":"operate/rs/security/access-control/manage-users/login-lockout","title":"Manage user login","url":"https://redis.io/docs/latest/operate/rs/security/access-control/manage-users/login-lockout/","summary":"Manage user login lockout and session timeout.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"b9c1ed933798345ccbb7e60ed283096b356e3e945139fdd01f654aa3b355c2d8","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software secures user access in a few different ways, including automatically:\n\n- Locking user accounts after a series of authentication failures (invalid passwords)\n\n- Signing sessions out after a period of inactivity\n\nHere, you learn how to configure the relevant settings."},{"id":"user-login-lockout","title":"User login lockout","role":"content","text":"By default, after 5 failed login attempts within 15 minutes, the user account is locked for 30 minutes. You can change the user login lockout settings in the Cluster Manager UI or with [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin)."},{"id":"view-login-lockout-settings","title":"View login lockout settings","role":"content","text":"You can view the cluster's user login lockout settings from **Cluster > Security > Preferences > Lockout threshold** in the Cluster Manager UI or with [`rladmin info cluster`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/info#info-cluster):\n\n[code example]"},{"id":"configure-user-login-lockout","title":"Configure user login lockout","role":"configuration","text":"To change the user login lockout settings using the Cluster Manager UI:\n\n1. Go to **Cluster > Security > Preferences**, then select **Edit**.\n\n1. In the **Lockout threshold** section, make sure the checkbox is selected.\n\n    ![The Lockout threshold configuration section](https://redis.io/docs/latest/images/rs/screenshots/cluster/security-preferences-lockout-threshold.png)\n\n1. Configure the following **Lockout threshold** settings:\n\n    1. **Log-in attempts until user is revoked** - The number of failed login attempts allowed before the user account is locked.\n\n    1. **Time between failed login attempts** in seconds, minutes, or hours - The amount of time during which failed login attempts are counted.\n\n    1. For **Unlock method**, select one of the following:\n\n        - **Locked duration** to set how long the user account is locked after excessive failed login attempts.\n\n        - **Only Admin can unlock the user by resetting the password**.\n\n1. Select **Save**."},{"id":"change-allowed-login-attempts","title":"Change allowed login attempts","role":"content","text":"To change the number of failed login attempts allowed before the user account is locked, use one of the following methods:\n\n- [Cluster Manager UI](#configure-user-login-lockout)\n\n- [`rladmin tune cluster`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-cluster):\n\n    [code example]\n\n    For example, to set the lockout threshold to 10 failed login attempts, run:\n\n    [code example]\n\n    If you set the lockout threshold to 0, it turns off account lockout, and the cluster settings show `login_lockout_threshold: disabled`.\n\n    [code example]"},{"id":"change-time-before-login-attempts-reset","title":"Change time before login attempts reset","role":"content","text":"To change the amount of time during which failed login attempts are counted, use one of the following methods:\n\n- [Cluster Manager UI](#configure-user-login-lockout)\n\n- [`rladmin tune cluster`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-cluster):\n\n    [code example]\n\n    For example, to set the lockout reset to 1 hour, run:\n\n    [code example]"},{"id":"change-login-lockout-duration","title":"Change login lockout duration","role":"content","text":"To change the amount of time that the user account is locked after excessive failed login attempts, use one of the following methods:\n\n- [Cluster Manager UI](#configure-user-login-lockout)\n\n- [`rladmin tune cluster`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-cluster):\n\n    [code example]\n\n    For example, to set the lockout duration to 1 hour, run:\n\n    [code example]\n\n    If you set the lockout duration to 0, then the account can be unlocked only when an administrator changes the account's password.\n\n    [code example]\n\n    The cluster settings now show `login_lockout_duration: admin-release`."},{"id":"unlock-locked-user-accounts","title":"Unlock locked user accounts","role":"content","text":"To unlock a locked user account as an admin, use one of the following methods:\n\n**Cluster Manager UI:**\n\nTo unlock a user account in the Cluster Manager UI:\n\n1. Go to **Access Control > Users**. Locked users have a \"User is locked out\" label:\n\n   ![The Access Control > Users configuration screen in the Cluster Manager UI](https://redis.io/docs/latest/images/rs/screenshots/access-control/7-22-updates/users-locked-out.png)\n\n1. Point to the \"User is locked out\" label, then click **Reset to unlock** when it appears:\n\n   ![Reset to unlock button appears when you point to a locked user in the list](https://redis.io/docs/latest/images/rs/screenshots/access-control/7-22-updates/users-reset-to-unlock.png)\n\n1. In the **Reset user password** dialog, enter a new password for the user:\n\n   ![Reset user password dialog](https://redis.io/docs/latest/images/rs/screenshots/access-control/users-reset-user-password-dialog.png)\n\n1. Select **Save** to reset the user's password and unlock their account.\n\n**rladmin:**\n\nTo unlock a user account or reset a user password with `rladmin`, run the [`rladmin cluster reset_password`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/reset_password) command:\n\n[code example]\n\n**REST API:**\n\nTo unlock a user account or reset a user password with the REST API, use [`PUT /v1/users`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/users#put-user):\n\n[code example]"},{"id":"turn-off-login-lockout","title":"Turn off login lockout","role":"content","text":"To turn off user login lockout and allow unlimited login attempts, use one of the following methods:\n\n**Cluster Manager UI:**\n\nTo turn off user login lockout using the Cluster Manager UI:\n\n1. Go to **Cluster > Security > Preferences**, then select **Edit**.\n\n1. Clear the **Lockout threshold** checkbox.\n\n1. Select **Save**.\n\n**rladmin:**\n\nTo turn off user login lockout using `rladmin`, run the [`rladmin tune cluster`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-cluster) command:\n\n[code example]\n\n\n\nThe cluster settings show `login_lockout_threshold: disabled`."},{"id":"configure-session-timeout","title":"Configure session timeout","role":"configuration","text":"The Redis Software Cluster Manager UI supports session timeouts. By default, users are automatically logged out after 15 minutes of inactivity.\n\nTo customize the session timeout, use one of the following methods:\n\n**Cluster Manager UI:**\n\nTo configure the session timeout using the Cluster Manager UI:\n\n1. Go to **Cluster > Security > Preferences**, then select **Edit**.\n\n1. For **Session timeout**, select minutes or hours from the list and enter the timeout value.\n\n1. Select **Save**.\n\n**rladmin:**\n\nTo configure the session timeout using `rladmin`, run the [`rladmin cluster config`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/config) command:\n\n[code example]\n\nThe `<number_of_min>` is the number of minutes after which sessions will time out."}],"examples":[{"id":"view-login-lockout-settings-ex0","language":"sh","code":"$ rladmin info cluster | grep login_lockout\n    login_lockout_counter_reset_after: 900\n    login_lockout_duration: 1800\n    login_lockout_threshold: 5","section_id":"view-login-lockout-settings"},{"id":"change-allowed-login-attempts-ex0","language":"sh","code":"rladmin tune cluster login_lockout_threshold <integer>","section_id":"change-allowed-login-attempts"},{"id":"change-allowed-login-attempts-ex1","language":"sh","code":"rladmin tune cluster login_lockout_threshold 10","section_id":"change-allowed-login-attempts"},{"id":"change-allowed-login-attempts-ex2","language":"sh","code":"rladmin tune cluster login_lockout_threshold 0","section_id":"change-allowed-login-attempts"},{"id":"change-time-before-login-attempts-reset-ex0","language":"sh","code":"rladmin tune cluster login_lockout_counter_reset_after <seconds>","section_id":"change-time-before-login-attempts-reset"},{"id":"change-time-before-login-attempts-reset-ex1","language":"sh","code":"rladmin tune cluster login_lockout_counter_reset_after 3600","section_id":"change-time-before-login-attempts-reset"},{"id":"change-login-lockout-duration-ex0","language":"sh","code":"rladmin tune cluster login_lockout_duration <login_lockout_duration>","section_id":"change-login-lockout-duration"},{"id":"change-login-lockout-duration-ex1","language":"sh","code":"rladmin tune cluster login_lockout_duration 3600","section_id":"change-login-lockout-duration"},{"id":"change-login-lockout-duration-ex2","language":"sh","code":"rladmin tune cluster login_lockout_duration 0","section_id":"change-login-lockout-duration"},{"id":"unlock-locked-user-accounts-ex0","language":"sh","code":"rladmin cluster reset_password <user_email>","section_id":"unlock-locked-user-accounts"},{"id":"unlock-locked-user-accounts-ex1","language":"sh","code":"PUT /v1/users\n{\"password\": \"<new_password>\"}","section_id":"unlock-locked-user-accounts"},{"id":"turn-off-login-lockout-ex0","language":"sh","code":"rladmin tune cluster login_lockout_threshold 0","section_id":"turn-off-login-lockout"},{"id":"configure-session-timeout-ex0","language":"sh","code":"rladmin cluster config cm_session_timeout_minutes <number_of_min>","section_id":"configure-session-timeout"}]}
{"schema_version":2,"id":"operate/rs/security/access-control/redis-acl-overview","title":"Overview of Redis ACLs in Redis Software","url":"https://redis.io/docs/latest/operate/rs/security/access-control/redis-acl-overview/","summary":"An overview of Redis ACLs, syntax, and ACL command support in Redis Software.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T14:53:09+01:00","page_type":"content","content_hash":"e892ef278b1b713e2012d49bd4a949b7313def78b2fcad03b5ba2e156e62100f","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis access control lists (Redis ACLs) allow you to define named permissions for specific Redis commands, keys, and pub/sub channels. You can use defined Redis ACLs for multiple databases and roles."},{"id":"predefined-redis-acls","title":"Predefined Redis ACLs","role":"content","text":"Redis Software provides one predefined Redis ACL named **Full Access**. This ACL allows all commands on all keys and cannot be edited."},{"id":"redis-acl-syntax","title":"Redis ACL syntax","role":"content","text":"Redis ACLs are defined by a set of rules where you specify the commands or command categories that are allowed for specific keys."},{"id":"commands-and-categories","title":"Commands and categories","role":"syntax","text":"Redis ACL rules can allow or block specific [Redis commands](/content/commands) or [command categories](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl#command-categories).\n\n- `+` includes commands\n\n- `-` excludes commands\n\n- `+@` includes command categories\n\n- `-@` excludes command categories\n\nThe following example allows all `read` commands and the `SET` command:\n\n[code example]\n\nFor database versions earlier than Redis 8.2, module commands have several ACL limitations:\n\n- [Redis modules](https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise) do not have command categories.\n\n- Other [command category](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl#command-categories) ACLs, such as `+@read` and `+@write`, do not include Redis module commands. `+@all` is the only exception because it allows all Redis commands.\n\n- You have to include individual module commands in a Redis ACL rule to allow them.\n\n    For example, the following Redis ACL rule allows read-only commands and the Redis Search commands `FT.INFO` and `FT.SEARCH`:\n\n    [code example]"},{"id":"key-patterns","title":"Key patterns","role":"content","text":"To define access to specific keys or key patterns, use the following prefixes:\n\n- `~` or `%RW~` allows read and write access to keys.\n\n- `%R~` allows read access to keys.\n\n- `%W~` allows write access to keys.\n\n`%RW~`, `%R~`, and `%W~` are only supported for databases with Redis version 7.2 or later.\n\nThe following example allows read and write access to all keys that start with \"app1\" and read-only access to all keys that start with \"app2\":\n\n[code example]"},{"id":"pubsub-channels","title":"Pub/sub channels","role":"content","text":"The `&` prefix allows access to [pub/sub channels](https://redis.io/docs/latest/develop/pubsub) (only supported for databases with Redis version 6.2 or later).\n\nTo limit access to specific channels, include `resetchannels` before the allowed channels:\n\n[code example]"},{"id":"selectors","title":"Selectors","role":"content","text":"[Selectors](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl#selectors) let you define multiple sets of rules in a single Redis ACL (only supported for databases with Redis version 7.2 or later). A command is allowed if it matches the base rule or any selector in the Redis ACL.\n\n- `(<rule set>)` creates a new selector.\n\n- `clearselectors` deletes all existing selectors for a user. This action does not delete the base ACL rule.\n\nIn the following example, the base rule allows `GET key1` and the selector allows `SET key2`:\n\n[code example]"},{"id":"default-pubsub-permissions","title":"Default pub/sub permissions","role":"content","text":"Redis database version 6.2 introduced pub/sub ACL rules that determine which [pub/sub channels](https://redis.io/docs/latest/develop/pubsub) a user can access.\n\nThe configuration option `acl-pubsub-default`, added in Redis Software version 6.4.2, determines the cluster-wide default level of access for all pub/sub channels. Redis Software uses the following pub/sub permissions by default:\n\n- For versions 6.4.2 and 7.2, `acl-pubsub-default` is permissive (`allchannels` or `&*`) by default to accommodate earlier Redis versions.\n\n- In future versions, `acl-pubsub-default` will change to restrictive (`resetchannels`). Restrictive permissions block all pub/sub channels by default, unless explicitly permitted by an ACL rule.\n\nIf you use ACLs and pub/sub channels, you should review your databases and ACL settings and plan to transition your cluster to restrictive pub/sub permissions in preparation for future Redis Software releases."},{"id":"prepare-for-restrictive-pubsub-permissions","title":"Prepare for restrictive pub/sub permissions","role":"content","text":"To secure pub/sub channels and prepare your cluster for future Redis Software releases that default to restrictive pub/sub permissions:\n\n1. Upgrade Redis databases:\n\n    - For Redis Software version 6.4.2, upgrade all databases in the cluster to Redis DB version 6.2.\n    \n    - For Redis Software version 7.2, upgrade all databases in the cluster to Redis DB version 7.2 or 6.2.\n\n1. Create or update ACLs with permissions for specific channels using the `resetchannels &channel` format.\n\n1. Associate the ACLs with relevant databases.\n\n1. Set default pub/sub permissions (`acl-pubsub-default`) to restrictive. See [Change default pub/sub permissions](#change-default-pubsub-permissions) for details.\n\n1. If any issues occur, you can temporarily change the default pub/sub setting back to permissive. Resolve any problematic ACLs before making pub/sub permissions restrictive again.\n\n> [!NOTE]\n> When you change the cluster's default pub/sub permissions to restrictive, `&*` is added to the **Full Access** ACL. Before you make this change, consider the following:\n>\n> - Because pub/sub ACL syntax was added in Redis 6.2, you can't associate the **Full Access** ACL with database versions 6.0 or lower after this change.\n>\n> - The **Full Access** ACL is not reverted if you change `acl-pubsub-default` to permissive again.\n>\n> - Every database with the default user enabled uses the **Full Access** ACL."},{"id":"change-default-pubsub-permissions","title":"Change default pub/sub permissions","role":"content","text":"As of Redis Software version 6.4.2, you can configure `acl_pubsub_default`, which determines the default pub/sub permissions for all databases in the cluster. You can set `acl_pubsub_default` to the following values:\n\n- `resetchannels` is restrictive and blocks access to all channels by default.\n\n- `allchannels` is permissive and allows access to all channels by default. \n\nTo make default pub/sub permissions restrictive:\n\n1. [Upgrade all databases](https://redis.io/docs/latest/operate/rs/installing-upgrading/upgrading/upgrade-database) in the cluster to Redis version 6.2 or later.\n\n1. Set the default to restrictive (`resetchannels`) using one of the following methods:\n\n    **Cluster Manager UI:**\n\nTo set the default pub/sub permissions using the Cluster Manager UI:\n    \n1. Navigate to **Access Control > Roles > Pub/Sub ACLs** and click **Edit**.\n\n1. Read the warning, then click **Continue**.\n        \n1. For **Default permissions for Pub/Sub ACLs**, select **Restrictive**, then **Save**.\n\n**rladmin:**\n\nTo set the default pub/sub permissions using `rladmin`, run the [`rladmin tune cluster`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-cluster) command:\n\n[code example]\n\n**REST API:**\n\nTo set the default pub/sub permissions using the REST API, use an [update cluster policy](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/policy#put-cluster-policy) request:\n\n[code example]"},{"id":"acl-command-support","title":"ACL command support","role":"security","text":"Redis Software does not support certain Redis ACL commands. Instead, you can manage access controls from the Cluster Manager UI.\n\n| Command | Supported |\n|:--------|:----------|\n| [ACL CAT](https://redis.io/docs/latest/commands/acl-cat) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> \n| [ACL DELUSER](https://redis.io/docs/latest/commands/acl-deluser) | <span title=\"Not supported\">&#x274c; Not supported</span>| <span title=\"Not supported\">&#x274c; Not supported</span>|  |\n| [ACL DRYRUN](https://redis.io/docs/latest/commands/acl-dryrun) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> \n| [ACL GENPASS](https://redis.io/docs/latest/commands/acl-genpass) | <span title=\"Not supported\">&#x274c; Not supported</span>| <span title=\"Not supported\">&#x274c; Not supported</span>|  |\n| [ACL GETUSER](https://redis.io/docs/latest/commands/acl-getuser) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> \n| [ACL HELP](https://redis.io/docs/latest/commands/acl-help) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> \n| [ACL LIST](https://redis.io/docs/latest/commands/acl-list) | <span title=\"Supported\">&#x2705; Supported</span> | <span title=\"Supported\">&#x2705; Supported</span> \n| [ACL LOAD](https://redis.io/docs/latest/commands/acl-load) | <span title=\"Not supported\">&#x274c; Not supported</span>| <span title=\"Not supported\">&#x274c; Not supported</span>|  |\n| [ACL LOG](https://redis.io/docs/latest/commands/acl-log) | <span title=\"Not supported\">&#x274c; Not supported</span>| <span title=\"Not supported\">&#x274c; Not supported</span>|  |\n| [ACL SAVE](https://redis.io/docs/latest/commands/acl-save) | <span title=\"Not supported\">&#x274c; Not supported</span>| <span title=\"Not supported\">&#x274c; Not supported</span>|  |\n| [ACL SETUSER](https://redis.io/docs/latest/commands/acl-setuser) | <span title=\"Not supported\">&#x274c; Not supported</span>| <span title=\"Not supported\">&#x274c; Not supported</span>|  |\n| [ACL USERS](https://redis.io/docs/latest/commands/acl-users) | <span title=\"Supported\">&#x2705; Supported</span> |\n| [ACL WHOAMI](https://redis.io/docs/latest/commands/acl-whoami) | <span title=\"Supported\">&#x2705; Supported</span> |\n\n\nRedis ACLs also have the following differences in Redis Software:\n\n- The `MULTI`, `EXEC`, `DISCARD` commands are always allowed, but ACLs are enforced on `MULTI` subcommands.\n\n- Nested selectors are not supported.\n\n    For example, the following selectors are not valid in Redis Software: <nobr>`+GET ~key1 (+SET (+SET ~key2) ~key3)`</nobr>\n\n- Key and pub/sub patterns do not allow the following characters: `'(', ')'`\n\n- The following password configuration syntax is not supported: `'>', '<', '#!', 'resetpass'`\n\n    To configure passwords in Redis Software, use one of the following methods:\n\n    - [`rladmin cluster reset_password`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/reset_password):\n    \n        [code example]\n\n    - REST API [`PUT /v1/users`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/users#put-user) request and provide `password`"},{"id":"see-also","title":"See also","role":"related","text":"[Redis ACL rules](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl)"}],"examples":[{"id":"commands-and-categories-ex0","language":"sh","code":"+@read +SET","section_id":"commands-and-categories"},{"id":"commands-and-categories-ex1","language":"sh","code":"+@read +FT.INFO +FT.SEARCH","section_id":"commands-and-categories"},{"id":"key-patterns-ex0","language":"sh","code":"~app1* %R~app2*","section_id":"key-patterns"},{"id":"pubsub-channels-ex0","language":"sh","code":"resetchannels &channel1 &channel2","section_id":"pubsub-channels"},{"id":"selectors-ex0","language":"sh","code":"+GET ~key1 (+SET ~key2)","section_id":"selectors"},{"id":"change-default-pubsub-permissions-ex0","language":"sh","code":"rladmin tune cluster acl_pubsub_default resetchannels","section_id":"change-default-pubsub-permissions"},{"id":"change-default-pubsub-permissions-ex1","language":"sh","code":"PUT /v1/cluster/policy\n{ \"acl_pubsub_default\": \"resetchannels\" }","section_id":"change-default-pubsub-permissions"},{"id":"acl-command-support-ex0","language":"sh","code":"rladmin cluster reset_password <user email>","section_id":"acl-command-support"}]}
{"schema_version":2,"id":"operate/rs/security/access-control/saml-sso","title":"SAML single sign-on","url":"https://redis.io/docs/latest/operate/rs/security/access-control/saml-sso/","summary":"Set up single sign-on with SAML for the Redis Software Cluster Manager UI.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:27:03+01:00","page_type":"content","content_hash":"a0a1602117a3abe91c48380baf6062f463a51218787d7078f4a8affd60a466c6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software supports both [IdP-initiated](#idp-initiated-sso) and [SP-initiated](#sp-initiated-sso) [single sign-on (SSO)](https://en.wikipedia.org/wiki/Single_sign-on) with [SAML (Security Assertion Markup Language)](https://en.wikipedia.org/wiki/Security_Assertion_Markup_Language) for the Cluster Manager UI. Redis Software uses SAML 2.0, which is the latest SAML version and an industry standard.\n\nYou cannot use [SCIM (System for Cross-domain Identity Management)](https://en.wikipedia.org/wiki/System_for_Cross-domain_Identity_Management) to provision Redis Software users. However, Redis Software supports just-in-time (JIT) user provisioning, which means Redis Software automatically creates a user account the first time a new user signs in with SSO."},{"id":"sso-overview","title":"SSO overview","role":"content","text":"When single sign-on is activated, users can sign in to the Redis Software Cluster Manager UI using their [identity provider (IdP)](https://en.wikipedia.org/wiki/Identity_provider) instead of usernames and passwords. If [SSO is enforced](#enforce-sso), non-admin users can no longer sign in with their previous usernames and passwords and must use SSO instead.\n\nBefore users can sign in to the Cluster Manager UI with SSO, the identity provider admin needs to set up these users on the IdP side with matching email addresses.\n\nWith just-in-time (JIT) user provisioning, Redis Software automatically creates user accounts for new users assigned to the SAML application in your identity provider when they sign in to the Cluster Manager UI for the first time. For these users, you must configure the `redisRoleMapping` attribute in your identity provider to assign appropriate roles for [role-based access control](https://redis.io/docs/latest/operate/rs/security/access-control) during account creation."},{"id":"idp-initiated-sso","title":"IdP-initiated SSO","role":"content","text":"With IdP-initiated single sign-on, you can select the Redis Software application after you sign in to your [identity provider (IdP)](https://en.wikipedia.org/wiki/Identity_provider). This redirects you to the Redis Software Cluster Manager UI and signs you in."},{"id":"sp-initiated-sso","title":"SP-initiated SSO","role":"content","text":"You can also initiate single sign-on from the Redis Software Cluster Manager UI. This process is known as [service provider (SP)](https://en.wikipedia.org/wiki/Service_provider)-initiated single sign-on.\n\nOn the Redis Software Cluster Manager UI's sign-in screen, click **Sign in with SSO**.\n\n- If you already have an active SSO session with your identity provider, this signs you in.\n\n- Otherwise, the SSO flow redirects you to your identity provider's sign in screen. Enter your IdP user credentials to sign in. This redirects you back to the Redis Software Cluster Manager UI and automatically signs you in.\n\nAuthentication requests expire after 3 minutes."},{"id":"idp-requirements","title":"IdP requirements","role":"content","text":"You can use any identity provider to integrate with Redis Software as long as it supports the following:\n\n- [SAML](https://en.wikipedia.org/wiki/Security_Assertion_Markup_Language) 2.0 protocol.\n\n- Signed SAML responses since Redis Software will not accept any unsigned SAML responses.\n\n- HTTP-Redirect binding for SP-initiated SSO.\n\n- HTTP-POST binding for SAML assertions."},{"id":"set-up-saml-sso","title":"Set up SAML SSO","role":"content","text":"To set up SAML single sign-on for a Redis Software cluster:\n\n1. [Upload the service provider certificate and private key](#upload-sp-certificate).\n\n1. [Download the service provider metadata](#download-sp-metadata).\n\n1. [Set up a SAML app](#set-up-app) to integrate Redis Software with your identity provider.\n\n1. [Download identity provider metadata](#download-idp-metadata).\n\n1. [Configure SAML identity provider in Redis Software](#configure-idp-metadata).\n\n1. [Assign the SAML app to existing users](#assign-saml-app-to-existing-users).\n\n1. [Activate SSO](#activate-sso)."},{"id":"upload-sp-certificate","title":"Upload SP certificate","role":"content","text":"1. Create a service provider certificate for Redis Software. See [Create certificates ](https://redis.io/docs/latest/operate/rs/security/certificates/create-certificates#create-certificates) for instructions.\n\n1. Upload the service provider certificate and key to the Redis Software cluster:\n\n    **Cluster Manager UI:**\n\n1. Sign in to the Redis Software Cluster Manager UI using admin credentials.\n\n1. Go to **Access Control > Single Sign-On**.\n\n    ![The single sign-on configuration screen.](https://redis.io/docs/latest/images/rs/screenshots/access-control/sso/sso-before-config.png)\n\n1. In the **Service Provider (Redis) metadata** section, find **Service-provider's public certificate + private key** and click **Upload**.\n\n1. Enter or upload the private key and certificate for your service provider.\n\n1. Click **Upload** to save.\n\n**REST API:**\n\nTo upload a certificate using the REST API, use an [update cluster certificates](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/certificates#put-cluster-certificates) request.\n\n[code example]"},{"id":"download-sp-metadata","title":"Download SP metadata","role":"content","text":"You need to download the service provider metadata for Redis Software and use it to configure the SAML integration app for your identity provider.\n\n**Cluster Manager UI:**\n\nTo download the service provider's metadata using the Cluster Manager UI:\n\n1. Go to **Access Control > Single Sign-On**.\n\n1. In the **Service Provider (Redis) metadata** section, click the following buttons to download the service provider files needed to set up a SAML app:\n\n    1. **Public certificate**\n\n    1. **Metadata file**\n\n    ![The service provider Redis metadata section.](https://redis.io/docs/latest/images/rs/screenshots/access-control/sso/sp-metadata-after-cert-upload.png)\n\n1. Optionally copy the following values for future SAML app setup in the identity provider. You can also find these values in the service provider's metadata file.\n\n    1. **SP entity ID**: `https://<cluster-FQDN>/sp`\n\n    1. **Assertion Consumer Service (ACS)**: `https://<cluster-FQDN>:8443/cluster/sso/saml/acs`\n\n    1. **Single Logout Service**: `https://<cluster-FQDN>:8443/cluster/sso/saml/slo`\n\n**REST API:**\n\nTo download the service provider's metadata using the REST API, use a [get SAML service provider metadata](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/sso#get-cluster-sso-saml-metadata) request.\n\n[code example]\n\n\n\nHere's an abridged example of the service provider metadata XML:\n\n[code example]\n\nSee [Metadata for the OASIS Security\nAssertion Markup Language (SAML)\nV2.0](https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf) for more information about the metadata fields.\n\n> [!NOTE]\n> Redis Software metadata expiration time is equivalent to the SSO service certificate's expiration time. The service provider metadata will only change if the service address used for the Assertion Consumer Service (ACS) and the single logout (SLO) URL is modified."},{"id":"set-up-app","title":"Set up SAML app","role":"content","text":"Set up a SAML app to integrate Redis Software with your identity provider:\n\n1. Sign in to your identity provider's admin console.\n\n1. Create or add a SAML integration app for the service provider Redis Software. For detailed setup instructions, see your identity provider's documentation.\n\n1. Configure the SAML app with the service provider metadata.\n\n    - Some identity providers let you upload the XML file directly. \n    \n    - Others require you to manually configure the service provider app with specific metadata fields, such as:\n    \n        | Setting | Value | Description |\n        |---------|-------|-------------|\n        | Audience URI (SP Entity ID) | `https://<cluster-FQDN>:8443/sp` | Unique URL that identifies the Redis Software service provider.<br /><br />Copy the **SP entity ID** from the **Access Control > Single Sign-On** page in the Cluster Manager UI or `EntityDescriptor`'s `entityID` in the metadata XML. |\n        | Single sign-on URL | <span class=\"break-all\">`https://<cluster-FQDN>:8443/cluster/sso/saml/acs`</span> | The service provider endpoint where the identity provider sends a SAML assertion that authenticates a user.<br /><br />Copy the **Assertion Consumer Service (ACS)** from the **Access Control > Single Sign-On** page in the Cluster Manager UI or `AssertionConsumerService`'s `Location` in the metadata XML. |\n        | Name ID format | EmailAddress | |\n        | Application username | Email | |\n\n1. For the signature certificate, upload the Service Provider (Redis) public certificate.\n\n1. Enable signed requests.\n\n1. Optionally, you can enable single log-out (SLO) to allow users to automatically sign out of the the identity provider when they sign out of the Redis Software Cluster Manager UI. Copy the **Single Logout Service** from the **Access Control > Single Sign-On** page in the Cluster Manager UI (`https://<cluster-FQDN>:8443/cluster/sso/saml/slo`) and configure it in the SAML app.\n\n    > [!NOTE]\n    > Redis Software only supports SP-initiated logout, where the user logs out from the Redis Software Cluster Manager UI. IdP-initiated logout requests are not supported.\n    >     \n\n1. Set up your SAML service provider app so the SAML assertion contains the following attributes:\n\n    | Attribute name (case-sensitive) | Description |\n    |-------------------------------------------|-------------|\n    | firstName | User's first name |\n    | lastName | User's last name |\n    | email | User's email address (used as the username in the Redis Software Cluster Manager UI and must contain ASCII characters only.) |\n    | redisRoleMapping | String array that includes the role UID for role-based access control in Redis Software. Only used for just-in-time (JIT) user provisioning. If a user already exists in Redis Software, this attribute is ignored and their existing roles are preserved. |\n\n    > [!NOTE]\n    > To confirm the identity provider's SAML assertions contain the required attributes, you can use a SAML-tracer web developer tool to inspect them.\n    >     \n\n1. Set up any additional configuration required by your identity provider to ensure you can configure the `redisRoleMapping` attribute for SAML users.\n\n    If your identity provider lets you configure custom attributes with workflows or group rules, you can set up automation to configure the `redisRoleMapping` field automatically instead of manually."},{"id":"download-idp-metadata","title":"Download IdP metadata","role":"content","text":"After you create the SAML app in your identity provider, retrieve the following information:\n\n| Setting | Description |\n|---------|-------------|\n| Issuer (IdP entity ID) | The unique entity ID for the identity provider |\n| IdP server URL | The identity provider's HTTPS URL for SAML SSO |\n| Single logout URL | The URL used to sign out of the identity provider and connected apps (optional) |\n| Assertion signing certificate | Public SHA-256 certificate used to validate SAML assertions from the identity provider |\n\nYou will use this certificate and metadata to configure the identity provider metadata in Redis Software. To find these metadata values, see your identity provider's documentation."},{"id":"configure-idp-metadata","title":"Configure IdP metadata in Redis Software","role":"configuration","text":"After you set up the SAML integration app, you need to configure the identity provider metadata in your Redis Software cluster.\n\n**Cluster Manager UI:**\n\n1. Sign in to the Redis Software Cluster Manager UI using admin credentials.\n\n1. Go to **Access Control > Single Sign-On**.\n\n1. In the **Identity Provider metadata** section, click **Edit**.\n\n1. Enter the **Identity Provider metadata** settings. \n\n    ![The identity provider metadata dialog.](https://redis.io/docs/latest/images/rs/screenshots/access-control/sso/edit-idp-metadata.png)\n\n1. Click **Save**.\n\n**REST API:**\n\n1. Upload your SAML app's assertion signing certificate using an [update cluster certificates](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/certificates#put-cluster-certificates) REST API request.\n\n    [code example]\n\n1. Configure the identity provider metadata using an [update SSO configuration](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/sso#put-cluster-sso) REST API request.\n\n    [code example]"},{"id":"assign-saml-app-to-existing-users","title":"Assign SAML app to existing users","role":"content","text":"In the identity provider's admin console:\n\n1. Create user profiles in the identity provider for existing Redis Software users. Make sure each user's email address matches in the identity provider and Redis Software.\n\n    > [!NOTE]\n    > You do not need to configure the `redisRoleMapping` attribute for existing Redis Software users. Their current roles will be preserved, and the `redisRoleMapping` attribute is ignored if provided.\n    >     \n\n2. Assign the new SAML integration app to each user.\n\nSee your identity provider's documentation for detailed instructions."},{"id":"activate-sso","title":"Activate SSO","role":"content","text":"After you finish the required SAML SSO configuration between your identity provider and Redis Software cluster, you can activate SSO.\n\n**Cluster Manager UI:**\n\nTo activate single sign-on using the Cluster Manager UI:\n\n1. Go to **Access Control > Single Sign-On**.\n\n1. Click **Activate SSO**.\n\n**REST API:**\n\nTo activate single sign-on using the REST API, use an [update SSO configuration](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/sso#put-cluster-sso) request.\n\n[code example]"},{"id":"add-new-users-with-jit-provisioning","title":"Add new users with JIT provisioning","role":"content","text":"After single sign-on is activated for Redis Software, you can create new Redis Software users on the identity provider side using just-in-time (JIT) provisioning.\n\n1. In the identity provider's admin console, create a new user profile with a valid email address that contains ASCII characters only. See your identity provider's documentation for detailed instructions.\n\n1. Configure the `redisRoleMapping` and assign a Redis Software role UID to the user.\n\n    > [!NOTE]\n    > To see a list of available role UIDs in your cluster, use a REST API request to [get all roles](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/roles#get-all-roles):\n    >\n    > [code example]\n    >     \n\n1. Assign the new SAML integration app to the user.\n\n1. Redis Software will create a new user with the mapped role the first time the new user signs in to the Cluster Manager UI using SSO."},{"id":"enforce-sso","title":"Enforce SSO","role":"content","text":"If SSO is enforced for the cluster, non-admin users can no longer sign in with their previous usernames and passwords and must use SSO instead.\n\n**Cluster Manager UI:**\n\nTo enforce single sign-on using the Cluster Manager UI:\n\n1. Go to **Access Control > Single Sign-On**.\n\n1. Find **Fallback behavior** and click **Edit**.\n\n1. Select **Enforce SSO-only login**.\n\n    ![Enforce SSO-only login is selected.](https://redis.io/docs/latest/images/rs/screenshots/access-control/sso/enforce-sso.png)\n\n1. Click **Save**.\n\n**REST API:**\n\nTo enforce single sign-on using the REST API, use an [update SSO configuration](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/sso#put-cluster-sso) request.\n\n[code example]"},{"id":"update-config","title":"Update configuration","role":"content","text":"If you change certain metadata or configuration settings after you set up SSO, such as the assertion signing certificate, remember to do the following:\n\n1. [Update the SAML SSO configuration](#configure-idp-metadata) with the new values.\n\n1. [Download the updated service provider metadata](#download-sp) and use it to update the Redis Software service provider app."},{"id":"change-sp-address","title":"Change SP address","role":"content","text":"If your deployment's default service provider address is not accessible to external identity providers, you can change it to an external hostname.\n\n> [!WARNING]\n> If you change the service address, the existing SSO integration will break because the metadata file, SP login and logout URLs, and entity ID will change to match the new address. You must update the service provider configuration on the identity provider's side after this change.\n\nTo change the service provider address, use an [update SSO configuration](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/sso#put-cluster-sso) REST API request:\n\n[code example]"},{"id":"deactivate-sso","title":"Deactivate SSO","role":"content","text":"**Cluster Manager UI:**\n\nTo deactivate single sign-on using the Cluster Manager UI:\n\n1. Go to **Access Control > Single Sign-On**.\n\n1. Click **Deactivate SSO**.\n\n1. Click **Confirm**.\n\n**REST API:**\n\nTo deactivate single sign-on using the REST API, use an [update SSO configuration](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/sso#put-cluster-sso) request.\n\n[code example]"}],"examples":[{"id":"upload-sp-certificate-ex0","language":"sh","code":"PUT https://<host>:<port>/v1/cluster/certificates\n{\n  \"certificates\": [\n    {\n      \"name\": \"<cert_name>\",\n      \"certificate\": \"sso_service\",\n      \"key\": \"<key>\"\n    }\n  ]\n}","section_id":"upload-sp-certificate"},{"id":"download-sp-metadata-ex0","language":"sh","code":"GET https://<host>:<port>/v1/cluster/sso/saml/metadata/sp","section_id":"download-sp-metadata"},{"id":"download-sp-metadata-ex1","language":"xml","code":"<md:EntityDescriptor xmlns:md=\"urn:oasis:names:tc:SAML:2.0:metadata\" validUntil=\"2025-12-25T20:38:29\" cacheDuration=\"PT2589134S\" entityID=\"https://<cluster-FQDN>:8443/sp\" ID=\"<ID>\">\n    ...\n    <md:SPSSODescriptor AuthnRequestsSigned=\"true\" WantAssertionsSigned=\"true\" protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\">\n        ...\n        <md:SingleLogoutService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\" Location=\"https://<cluster-FQDN>:8443/cluster/sso/saml/slo\"/>\n        <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat>\n        <md:AssertionConsumerService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" Location=\"https://<cluster-FQDN>:8443/cluster/sso/saml/acs\" index=\"1\"/>\n        <md:AttributeConsumingService index=\"1\">\n            <md:ServiceName xml:lang=\"en\">Redis Cluster Enterprise - <cluster-FQDN></md:ServiceName>\n            <md:ServiceDescription xml:lang=\"en\">Redis Cluster Enterprise SSO</md:ServiceDescription>\n            <md:RequestedAttribute Name=\"firstName\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\" FriendlyName=\"firstName\" isRequired=\"true\"/>\n            <md:RequestedAttribute Name=\"lastName\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\" FriendlyName=\"lastName\" isRequired=\"true\"/>\n            <md:RequestedAttribute Name=\"email\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\" FriendlyName=\"email\" isRequired=\"true\"/>\n            <md:RequestedAttribute Name=\"redisRoleMapping\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\" FriendlyName=\"redisRoleMapping\"/>\n        </md:AttributeConsumingService>\n    </md:SPSSODescriptor>\n</md:EntityDescriptor>","section_id":"download-sp-metadata"},{"id":"configure-idp-metadata-ex0","language":"sh","code":"PUT https://<host>:<port>/v1/cluster/certificates\n    {\n      \"certificates\": [\n        {\n          \"name\": \"<cert_name>\",\n          \"certificate\": \"sso_issuer\",\n          \"key\": \"<key>\"\n        }\n      ]\n    }","section_id":"configure-idp-metadata"},{"id":"configure-idp-metadata-ex1","language":"sh","code":"PUT https://<host>:<port>/v1/cluster/sso\n    {\n      \"protocol\": \"saml2\",\n      \"issuer\": {\n        \"id\": \"urn:sso:example:idp\",\n        \"login_url\": \"https://idp.example.com/sso/saml\",\n        \"logout_url\": \"https://idp.example.com/sso/slo\"\n      }\n    }","section_id":"configure-idp-metadata"},{"id":"activate-sso-ex0","language":"sh","code":"PUT https://<host>:<port>/v1/cluster/sso\n{\n    \"control_plane\": true\n}","section_id":"activate-sso"},{"id":"add-new-users-with-jit-provisioning-ex0","language":"sh","code":"> GET https://<host>:<port>/v1/roles\n    >","section_id":"add-new-users-with-jit-provisioning"},{"id":"enforce-sso-ex0","language":"sh","code":"PUT https://<host>:<port>/v1/cluster/sso\n{\n    \"enforce_control_plane\": true\n}","section_id":"enforce-sso"},{"id":"change-sp-address-ex0","language":"sh","code":"PUT https://<host>:<port>/v1/cluster/sso\n{\n  \"service\": {\n    \"address\": \"https://<external-domain>\"\n  }\n}","section_id":"change-sp-address"},{"id":"deactivate-sso-ex0","language":"sh","code":"PUT https://<host>:<port>/v1/cluster/sso\n{\n    \"control_plane\": false\n}","section_id":"deactivate-sso"}]}
{"schema_version":2,"id":"operate/rs/security/audit-events","title":"Audit database commands and connections","url":"https://redis.io/docs/latest/operate/rs/security/audit-events/","summary":"Describes how to audit commands and connection events.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:28+01:00","page_type":"content","content_hash":"3968b995767cc22c1a4f8207389c52ed23d5288679ca9e4827e67ba362fde319","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software lets you audit database connection, authentication, and command (CRUD) events. This helps you track and troubleshoot connection and data-access activity.\n\nThe following events can be tracked, depending on the configured audit mode:\n\n- Database connection attempts\n- Authentication requests, including requests for new and existing connections\n- Database disconnects\n- Individual database commands (CRUD operations) if command auditing is enabled\n\nWhen a tracked event occurs, Redis Software sends a notification over TCP to the address and port you configure when you enable auditing. Notifications arrive in near real time for an external listener to consume, such as a TCP listener, third-party service, or related utility. Successfully sending a record to the configured endpoint doesn't guarantee that the downstream consumer received, stored, or processed it.\n\nRedis Software doesn't require or enforce any particular SIEM or DAM vendor. Any listener that can consume the TCP notification stream works.\n\nExample external listeners include:\n\n- [`ncat`](https://nmap.org/ncat/): useful for debugging but not suitable for production environments.\n\n- Imperva Sonar: a third-party service available for purchase separately from Redis Software. See [Redis Onboarding Steps](https://docs-cybersec.thalesgroup.com/bundle/onboarding-databases-to-sonar-reference-guide/page/Redis-Enterprise-Software-Onboarding-Steps_48368215.html) for more information.\n\n- IBM Guardium\n\n- Other SIEM or DAM platforms\n\nIn development and testing environments, you can save notifications to a local file. This is neither supported nor intended for production.\n\nAuditing is turned off by default for performance reasons. It runs asynchronously in the background and is non-blocking: the action that triggered a notification continues regardless of the notification's status or the listener's availability.\n\nRedis Software always favors database availability over audit completeness. If auditing fails—for example, because an internal buffer overflows or the audit destination is unreachable—database operations continue uninterrupted. Audit buffers are size-limited to prevent memory exhaustion, and dropped records are reported through the [auditing metrics](#monitor-auditing-metrics)."},{"id":"performance-considerations","title":"Performance considerations","role":"performance","text":"Enabling command (CRUD) auditing adds processing overhead compared to connection-only auditing. Before you enable it:\n\n- Performance impact is workload-dependent. It varies with hardware, operation mix (read-heavy vs. write-heavy), throughput, and the volume of audited events.\n- Evaluate command auditing in a non-production environment before enabling it in production.\n- Apply username or source IP filters to reduce the volume of audited commands and limit the performance impact.\n- Allocate enough hardware resources to keep CPU utilization at a reasonable level.\n- Auditing never includes replication traffic on internal connections, such as data synchronization from a primary to a replica or between Active-Active clusters, regardless of the audit configuration."},{"id":"configure-cluster-audit-destination","title":"Configure cluster audit destination","role":"configuration","text":"Before enabling auditing on any database, configure the audit destination using one of the following methods:\n\n**REST API:**\n\nTo configure the audit destination using the REST API, use an [update database auditing](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/auditing-db-conns#put-cluster-audit-db-conns) cluster request:\n\n[code example]\n\n**rladmin:**\n\nTo configure the audit destination using `rladmin`, run the following command:\n\n[code example]\n\n\n\n- _audit\\_protocol_ sets the protocol used to send notifications. For production systems, _TCP_ is the only supported value. Use _local_ only for development and testing.\n\n- _audit\\_address_ sets the address where the listener receives notifications.\n\n- _audit\\_port_ sets the port where the listener receives notifications.\n   \n- _audit\\_reconnect\\_interval_ sets the interval, in seconds, between reconnection attempts. Default is 1 second.\n    \n- _audit\\_reconnect\\_max\\_attempts_ sets the maximum number of reconnection attempts. Default is 0 (infinite).\n\n- _audit\\_queue\\_max\\_bytes_ sets the maximum amount of audit data, in bytes, that can wait in the outgoing audit queue. Default is `524288` (512 KB). When the queue has no remaining capacity, new audit records can be dropped."},{"id":"local-socket-for-testing","title":"Local socket for testing","role":"content","text":"For testing and training, development systems can set _audit\\_protocol_ to `local`. This setting is _not_ supported for production use.  \n    \nWhen `audit_protocol` is set to `local`, `<address>` should be set to a [stream socket](https://man7.org/linux/man-pages/man7/unix.7.html) defined on the machine running Redis Software and _`<port>`_ should not be specified.\n\n**REST API:**\n\nTo configure a local audit destination using the REST API:\n\n[code example]\n\n**rladmin:**\n\nTo configure a local audit destination using `rladmin`:\n\n[code example]\n\n\n\nThe socket file and path must be accessible by the user and group running Redis Software."},{"id":"audit-modes","title":"Audit modes","role":"content","text":"Auditing is configured independently for each database with `audit_settings.audit_mode`:\n\n| Value | Description |\n|---|---|\n| `disabled` | Disables connection, authentication, and command audit records for the database. This is the default mode. |\n| `connection` | Audits database connection, disconnection, and authentication events only. |\n| `connection_and_crud` | Audits connection and authentication events plus individual database commands. |\n\nCommand auditing can't be enabled without connection auditing. `connection_and_crud` always includes the connection and authentication events that `connection` mode generates."},{"id":"enable-command-and-connection-auditing","title":"Enable command and connection auditing","role":"content","text":"After you configure the audit destination for your cluster, you can enable command (CRUD) and connection auditing for individual databases.\n\nYou can filter by usernames, source IP addresses, or both to manage data volume and avoid capturing irrelevant traffic. If both filters are configured, only requests matching both criteria are audited.\n\n> [!NOTE]\n> Filter changes affect new client connections only. Existing connections continue to be audited based on the filters that were active when the connection was established.\n\n**REST API:**\n\nTo enable command and connection auditing and configure filters, use an [update database configuration](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#put-bdbs) REST API request:\n\n[code example]\n\n**rladmin:**\n\nTo enable command and connection auditing using `rladmin`:\n\n[code example]"},{"id":"inclusive-versus-exclusive-filters","title":"Inclusive versus exclusive filters","role":"content","text":"For full CRUD auditing with clear scoping, use `inclusive` filters to audit only known application users or networks.\n\nUse `exclusive` filters when the goal is to audit everything except specific users or networks."},{"id":"username-filtering","title":"Username filtering","role":"content","text":"Username filtering controls which client usernames are included in or excluded from command audit records.\n\n| Field | Type | Default | Description |\n|---|---|---|---|\n| `enabled` | boolean | `false` | Enables the username filter. |\n| `usernames` | string array | `[]` | Usernames to include or exclude. Maximum 512 entries. |\n| `filter_type` | string | `inclusive` | Either `inclusive` (audit only listed usernames) or `exclusive` (audit all except listed usernames). |\n| `allow_while_username_unknown` | boolean | `false` | Allows command auditing before the username is known. |\n\n**Username validation:**\n\n- Each username must be 1–255 characters.\n- Each username must use printable ASCII characters only.\n- Usernames must not contain `&`, `<`, `>`, or double-quote characters.\n- If `username_filter.enabled` is `true`, `usernames` must not be empty."},{"id":"source-ip-filtering","title":"Source IP filtering","role":"content","text":"Source IP filtering controls which client IP addresses are included in or excluded from command audit records.\n\n| Field | Type | Default | Description |\n|---|---|---|---|\n| `enabled` | boolean | `false` | Enables the source IP filter. |\n| `ip_addresses` | string array | `[]` | Bare IPv4 or IPv6 addresses. |\n| `cidr_ranges` | string array | `[]` | IPv4 or IPv6 CIDR ranges. |\n| `filter_type` | string | `inclusive` | Either `inclusive` (audit only listed IPs) or `exclusive` (audit all except listed IPs). |\n\n**Source IP validation:**\n\n- `ip_addresses` must contain only bare IPv4 or IPv6 addresses and no ranges.\n- `cidr_ranges` must contain only valid IPv4 or IPv6 CIDR ranges.\n- If `source_ip_filter.enabled` is `true`, at least one `ip_addresses` or `cidr_ranges` entry is required.\n- The combined number of `ip_addresses` and `cidr_ranges` entries cannot exceed 512."},{"id":"key-byte-limit","title":"Key byte limit","role":"content","text":"You can set the `max_total_key_bytes` to control how much key data is captured per command audit record. The default value is `131072` and the minimum is `0`."},{"id":"partial-updates","title":"Partial updates","role":"content","text":"**REST API:**\n\nUpdates to `audit_settings` merge with the database's existing audit configuration, so you can change one setting without resending the others. For example, the following request disables the username filter while preserving its username list and filter type:\n\n[code example]\n\n**rladmin:**\n\nEach `rladmin tune db audit_settings` command updates only the setting you specify, merging with the database's existing audit configuration. For example, the following commands set the filtered usernames and then enable the filter:\n\n[code example]"},{"id":"enable-connection-auditing-only","title":"Enable connection auditing only","role":"content","text":"After you configure the audit destination for your cluster, you can enable connection auditing only for individual databases using one of the following methods:\n\n**REST API:**\n\nTo enable connection auditing only using the REST API, use an [update database configuration](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#put-bdbs) request:\n\n[code example]\n\n**rladmin:**\n\nTo enable connection auditing only using `rladmin`:\n\n[code example]\n\n\n\n> [!NOTE]\n> The legacy `db_conns_auditing` field enables connection auditing only; it does not enable command (CRUD) auditing. To audit commands, set `audit_settings.audit_mode` to `connection_and_crud`. If both are set, `audit_settings.audit_mode` takes precedence."},{"id":"set-policy-defaults-for-new-databases","title":"Set policy defaults for new databases","role":"content","text":"To audit connections for new databases by default, use one of the following methods:\n\n**REST API:**\n\nTo enable auditing connections for new databases by default using the REST API, use an [update cluster policy](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/policy#put-cluster-policy) request:\n\n[code example]\n\nTo deactivate this policy, set `db_conns_auditing` to `false`.\n\n**rladmin:**\n\nTo enable auditing connections for new databases by default using `rladmin`:\n\n[code example]\n\nTo deactivate this policy, set `db_conns_auditing` to `disabled`."},{"id":"auditing-validation-errors","title":"Auditing validation errors","role":"content","text":"| Error code | Cause |\n|---|---|\n| `audit_settings_config_missing` | Attempted to enable auditing before configuring `/v1/cluster/auditing/db_conns`. |\n| `audit_settings_empty_filter_list` | Enabled username or source IP filter has no entries. |\n| `audit_settings_invalid_ip_address` | Invalid value in `source_ip_filter.ip_addresses`. |\n| `audit_settings_invalid_cidr_range` | Invalid value in `source_ip_filter.cidr_ranges`. |\n| `audit_settings_filter_list_too_large` | Combined source IP and CIDR entries exceed 512. |\n| `invalid_schema` | Request violates schema constraints, such as an invalid enum, negative size, extra fields, or an over-limit array. |\n| `db_conns_auditing_missing_config` | TCP audit sink is missing required configuration, such as `audit_port`. |\n| `db_conns_auditing_bad_config` | Invalid sink field combination, such as `audit_port` with `local`. |\n| `db_conns_auditing_bad_address` | Invalid sink address for the selected protocol. |"},{"id":"turn-off-auditing","title":"Turn off auditing","role":"content","text":"**REST API:**\n\nTo turn off auditing for a specific database:\n\n[code example]\n\n**rladmin:**\n\nTo turn off auditing for a specific database using `rladmin`:\n\n[code example]"},{"id":"notification-examples","title":"Notification examples","role":"content","text":"Audit event notifications are reported as JSON objects."},{"id":"new-connection","title":"New connection","role":"content","text":"This example reports a new connection for a database:\n\n[code example]"},{"id":"authentication-request","title":"Authentication request","role":"security","text":"Here is a sample authentication request for a database:\n\n[code example]"},{"id":"successful-command","title":"Successful command","role":"content","text":"The following example shows the audit notification for a successful command, such as `GET myKey`:\n\n[code example]"},{"id":"failed-command","title":"Failed command","role":"content","text":"The following example shows the audit notification for a failed command, such as when you run `SET` without a key:\n\n[code example]"},{"id":"close-connection","title":"Close connection","role":"content","text":"Here's what's reported when a database connection is closed:\n\n[code example]"},{"id":"notification-field-reference","title":"Notification field reference","role":"content","text":"All audit records follow a unified JSON structure that is backward compatible with the existing authentication request format used for connection auditing.\n\n> [!NOTE]\n> Command audit records never include the payload value associated with a key. Only the key name is recorded."},{"id":"record-types","title":"Record types","role":"content","text":"The field that appears immediately after the timestamp (`\"ts\"`) describes the action that triggered the notification:\n\n| Type | Description |\n|------|-------------|\n| new_conn | Emitted when a new client connection is established. |\n| auth | `\"action\": \"auth\"` indicates an authentication request and can refer to new authentication requests or authorization checks on existing connections. |\n| command | `\"action\": \"command\"` indicates CRUD requests. Emitted for audited database commands. |\n| close_conn | Emitted when a client connection is closed. |"},{"id":"notification-fields","title":"Notification fields","role":"content","text":"The following fields can appear in audit event notifications:\n\n| Field | Type | Description |\n|---|---|---|\n| ts | integer | Unix timestamp of the event, in UTC. Granularity is within one second. |\n| action | string | `auth` for authentication requests; `command` for CRUD requests. (Not present on `new_conn`/`close_conn` records, which are identified by their own key instead.) |\n| id | integer | Unique connection ID assigned by the proxy. |\n| srcip | string | Source IP address of the client. |\n| srcp | string | Source port of the client connection. |\n| trgip | string | Target IP address (the Redis endpoint). |\n| trgp | string | Target port. |\n| hname | string | Client hostname, if available. Currently empty; reserved for future use. |\n| bdb_name | string | Name of the database. |\n| bdb_uid | string | Unique cluster ID of the database. |\n| status | integer | Result code. See [Status result codes](#status-result-codes). |\n| username | string | Authentication username associated with the connection. |\n| identity | string | A unique ID the proxy assigned to the user for the current connection. |\n| acl-rules | string | ACL rules associated with the user. |\n| group | string | LDAP group name associated with the user. Present only when the user has an associated LDAP group. |\n| command | string | The Redis command name for CRUD requests. |\n| keys | array[string] | The key or keys associated with the command. Empty when no keys are captured. |\n| full_key_size_bytes | integer | Byte size of the full key payload before truncation. |\n| captured_key_size_bytes | integer | Byte size of the key payload actually captured in the audit record. |\n| total_keys_count | integer | Total number of keys referenced by the command. |\n| key_truncated | boolean | Whether the captured key list was truncated. |\n| affected_count | integer / null | Number of keys or elements affected by the command, when applicable. |\n| error | string | Error text for failed command execution (present on failure only). |\n| error_truncated | boolean | Whether the error text was truncated (present on failure only). |\n| audit_worker_id | integer | ID of the proxy worker thread that produced the audit record. For debugging purposes only. |\n| audit_seq | integer | Per-worker sequence number of the audit report. Useful for debugging and detecting dropped audit reports. |"},{"id":"fields-by-record-type","title":"Fields by record type","role":"content","text":"The following table shows which fields can appear in each record type:\n\n| Field | new_conn / close_conn | auth | command |\n|---|---|---|---|\n| ts | <span title=\"Supported\">:white_check_mark:</span> | <span title=\"Supported\">:white_check_mark:</span> | <span title=\"Supported\">:white_check_mark:</span> |\n| action | | <span title=\"Supported\">:white_check_mark:</span> | <span title=\"Supported\">:white_check_mark:</span> |\n| id | <span title=\"Supported\">:white_check_mark:</span> | <span title=\"Supported\">:white_check_mark:</span> | <span title=\"Supported\">:white_check_mark:</span> |\n| srcip | <span title=\"Supported\">:white_check_mark:</span> | <span title=\"Supported\">:white_check_mark:</span> | <span title=\"Supported\">:white_check_mark:</span> |\n| srcp | <span title=\"Supported\">:white_check_mark:</span> | <span title=\"Supported\">:white_check_mark:</span> | <span title=\"Supported\">:white_check_mark:</span> |\n| trgip | <span title=\"Supported\">:white_check_mark:</span> | <span title=\"Supported\">:white_check_mark:</span> | <span title=\"Supported\">:white_check_mark:</span> |\n| trgp | <span title=\"Supported\">:white_check_mark:</span> | <span title=\"Supported\">:white_check_mark:</span> | <span title=\"Supported\">:white_check_mark:</span> |\n| bdb_name | <span title=\"Supported\">:white_check_mark:</span> | <span title=\"Supported\">:white_check_mark:</span> | <span title=\"Supported\">:white_check_mark:</span> |\n| bdb_uid | <span title=\"Supported\">:white_check_mark:</span> | <span title=\"Supported\">:white_check_mark:</span> | <span title=\"Supported\">:white_check_mark:</span> |\n| status | | <span title=\"Supported\">:white_check_mark:</span> | <span title=\"Supported\">:white_check_mark:</span> |\n| username | | <span title=\"Supported\">:white_check_mark:</span> | |\n| identity | | <span title=\"Supported\">:white_check_mark:</span> | |\n| acl-rules | | <span title=\"Supported\">:white_check_mark:</span> | |\n| group | | <span title=\"Supported\">:white_check_mark:</span> | |\n| command | | | <span title=\"Supported\">:white_check_mark:</span> |\n| keys | | | <span title=\"Supported\">:white_check_mark:</span> |\n| full_key_size_bytes | | | <span title=\"Supported\">:white_check_mark:</span> |\n| captured_key_size_bytes | | | <span title=\"Supported\">:white_check_mark:</span> |\n| total_keys_count | | | <span title=\"Supported\">:white_check_mark:</span> |\n| key_truncated | | | <span title=\"Supported\">:white_check_mark:</span> |\n| affected_count | | | <span title=\"Supported\">:white_check_mark:</span> |\n| error | | | <span title=\"Supported\">:white_check_mark:</span> |\n| error_truncated | | | <span title=\"Supported\">:white_check_mark:</span> |\n| audit_worker_id | <span title=\"Supported\">:white_check_mark:</span> | <span title=\"Supported\">:white_check_mark:</span> | <span title=\"Supported\">:white_check_mark:</span> |\n| audit_seq | <span title=\"Supported\">:white_check_mark:</span> | <span title=\"Supported\">:white_check_mark:</span> | <span title=\"Supported\">:white_check_mark:</span> |"},{"id":"status-result-codes","title":"Status result codes","role":"content","text":"The `status` field reports a numeric result code. Its meaning depends on the record type: authentication requests (`\"action\": \"auth\"`) and commands (`\"action\": \"command\"`) use separate sets of codes.\n\n#### Authentication request status codes\n\nFor authentication request records, `status` reports the result of the authentication attempt:\n\n| Status code | Description |\n|-------------|-------------|\n| `0` | AUTHENTICATION_FAILED: Invalid username and/or password. |\n| `2` | AUTHENTICATION_NOT_REQUIRED: Client tried to authenticate, but authentication isn't necessary. |\n| `3` | AUTHENTICATION_DIRECTORY_PENDING: Attempting to receive authentication info from the directory in async mode. |\n| `4` | AUTHENTICATION_DIRECTORY_ERROR: Authentication attempt failed due to a directory connection error. |\n| `5` | AUTHENTICATION_SYNCER_IN_PROGRESS: Syncer SASL handshake; return SASL response and wait for the next request. |\n| `6` | AUTHENTICATION_SYNCER_FAILED: Syncer SASL handshake; returned SASL response and closed the connection. |\n| `7` | AUTHENTICATION_SYNCER_OK: Syncer authenticated; returned SASL response. |\n| `8` | AUTHENTICATION_OK: Client successfully authenticated. |\n| `9` | AUTHENTICATION_ENTRAID_ERROR: Authentication attempt failed due to an EntraID connection error. |\n| `10` | AUTHENTICATION_CBA_PENDING: Certificate-based authentication (CBA) pending; waiting for the external authentication service response. |\n\n#### Command status codes\n\nFor command records, `status` reports whether the command completed successfully:\n\n| Status code | Description |\n|-------------|-------------|\n| `9` | The command completed successfully. |\n| `10` | The command failed. The `error` field contains the returned error message. |"},{"id":"monitor-auditing-metrics","title":"Monitor auditing metrics","role":"content","text":""},{"id":"audit-delivery-during-sink-interruptions","title":"Audit delivery during sink interruptions","role":"content","text":"If the audit destination becomes unavailable, Redis Software keeps serving database traffic and reconnects according to `audit_reconnect_interval` and `audit_reconnect_max_attempts`. Records can wait in the outgoing queue (bounded by `audit_queue_max_bytes`) while delivery is unavailable.\n\nAudit delivery is best effort—database operations are never delayed or rejected because a record can't be delivered. Redis Software can discard records when the queue is full, a single record exceeds the size limit, no connection to the destination is available, a send attempt fails, or queuing is disabled. Use the `audit_sink_*` metrics to detect these conditions."},{"id":"available-metrics","title":"Available metrics","role":"content","text":"You can use the following metrics exported by Redis Software to monitor your database auditing pipeline. For more information about monitoring Redis Software, see the [monitoring v2 documentation](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine).\n\n| Metric | Description |\n|--------|---|\n| audit_sink_sent_messages | Count of audit records sent to the audit sink. Does not guarantee the remote consumer received them. |\n| audit_sink_sent_bytes | Total size, in bytes, of audit data sent to the audit sink. Does not guarantee the remote consumer received it. |\n| audit_sink_dropped_messages | Count of audit records discarded before they could be delivered to the sink. |\n| audit_sink_dropped_bytes | Total size, in bytes, of audit data discarded before delivery to the sink. |\n| audit_sink_pending_bytes | Current amount of audit data waiting in the outgoing queue to be sent to the sink. |\n| audit_sink_connect_events | Count of times a connection to the sink was successfully established. |\n| audit_sink_disconnect_events | Count of times the connection to the sink was disconnected. |\n| audit_sink_reconnect_attempts | Count of attempts to create or re-create a connection to the sink, successful or not. |\n| audit_sink_reconnect_attempts_exhausted | Count of times the system gave up reconnecting after reaching the configured maximum number of attempts. |\n| audit_sink_disconnect_loss_events | Total disconnect events that lost buffered audit data. |\n| audit_sink_disconnect_loss_bytes | Total buffered audit bytes lost due to disconnect events. |\n| audit_sink_dropped_queue_full_messages | Count of audit records discarded because an individual worker's queue had no more capacity. |\n| audit_sink_dropped_record_too_large_messages | Count of audit records discarded because a single record was larger than the allowed queue byte limit. |\n| audit_sink_dropped_disconnected_messages | Count of audit records discarded because there was no active connection to the sink when delivery was needed. |\n| audit_sink_dropped_queue_disabled_messages | Count of audit records discarded because queuing was disabled. |\n| audit_sink_dropped_send_failed_messages | Count of audit records discarded because an attempt to send them to the sink failed. |\n| audit_sink_connected_workers | Number of workers that currently have an active connection to the sink. |\n| audit_sink_queue_high_watermark_bytes | Sum of each worker's highest observed pending-buffer size since the process started. |"}],"examples":[{"id":"configure-cluster-audit-destination-ex0","language":"sh","code":"PUT https://<host>:<port>/v1/cluster/auditing/db_conns\n{\n    \"audit_protocol\": \"TCP\",\n    \"audit_address\": \"audit-sink.example.com\",\n    \"audit_port\": <port>,\n    \"audit_reconnect_interval\": <seconds>,\n    \"audit_reconnect_max_attempts\": <integer>,\n    \"audit_queue_max_bytes\": <bytes>\n}","section_id":"configure-cluster-audit-destination"},{"id":"configure-cluster-audit-destination-ex1","language":"plaintext","code":"rladmin cluster config auditing db_conns \\\n    audit_protocol <TCP|local> \\\n    audit_address <address> \\\n    audit_port <port> \\\n    audit_reconnect_interval <interval in seconds> \\\n    audit_reconnect_max_attempts <number of attempts>","section_id":"configure-cluster-audit-destination"},{"id":"local-socket-for-testing-ex0","language":"sh","code":"PUT https://<host>:<port>/v1/cluster/auditing/db_conns\n{\n    \"audit_protocol\": \"local\",\n    \"audit_address\": \"/var/opt/redislabs/run/db-audit.sock\"\n}","section_id":"local-socket-for-testing"},{"id":"local-socket-for-testing-ex1","language":"plaintext","code":"rladmin cluster config auditing db_conns \\\n    audit_protocol local audit_address <socket-file>","section_id":"local-socket-for-testing"},{"id":"enable-command-and-connection-auditing-ex0","language":"plaintext","code":"PUT https://<host>:<port>/v1/bdbs/<database-id>\n{\n    \"audit_settings\": {\n        \"audit_mode\": \"connection_and_crud\",\n        \"username_filter\": {\n            \"enabled\": true,\n            \"usernames\": [\"application-user\"],\n            \"filter_type\": \"inclusive\",\n            \"allow_while_username_unknown\": false\n        },\n        \"source_ip_filter\": {\n            \"enabled\": true,\n            \"ip_addresses\": [],\n            \"cidr_ranges\": [\"10.20.0.0/16\"],\n            \"filter_type\": \"inclusive\"\n        },\n        \"max_total_key_bytes\": 131072\n    }\n}","section_id":"enable-command-and-connection-auditing"},{"id":"enable-command-and-connection-auditing-ex1","language":"plaintext","code":"rladmin tune db db:<id|name> audit_settings audit_mode connection_and_crud","section_id":"enable-command-and-connection-auditing"},{"id":"partial-updates-ex0","language":"sh","code":"PUT https://<host>:<port>/v1/bdbs/<database-id>\n{\n    \"audit_settings\": {\n        \"username_filter\": {\n            \"enabled\": false\n        }\n    }\n}","section_id":"partial-updates"},{"id":"partial-updates-ex1","language":"plaintext","code":"rladmin tune db db:<id|name> audit_settings username_filter usernames alice,bob\nrladmin tune db db:<id|name> audit_settings username_filter enabled enabled","section_id":"partial-updates"},{"id":"enable-connection-auditing-only-ex0","language":"plaintext","code":"PUT https://<host>:<port>/v1/bdbs/<database-id>\n{\n    \"audit_settings\": {\n        \"audit_mode\": \"connection\"\n    }\n}","section_id":"enable-connection-auditing-only"},{"id":"enable-connection-auditing-only-ex1","language":"plaintext","code":"rladmin tune db db:<id|name> audit_settings audit_mode connection","section_id":"enable-connection-auditing-only"},{"id":"set-policy-defaults-for-new-databases-ex0","language":"plaintext","code":"PUT /v1/cluster/policy\n{ \"db_conns_auditing\": true }","section_id":"set-policy-defaults-for-new-databases"},{"id":"set-policy-defaults-for-new-databases-ex1","language":"sh","code":"rladmin tune cluster db_conns_auditing enabled","section_id":"set-policy-defaults-for-new-databases"},{"id":"turn-off-auditing-ex0","language":"sh","code":"PUT https://<host>:<port>/v1/bdbs/<database-id>\n{\n    \"audit_settings\": {\n        \"audit_mode\": \"disabled\"\n    }\n}","section_id":"turn-off-auditing"},{"id":"turn-off-auditing-ex1","language":"plaintext","code":"rladmin tune db db:<id|name> audit_settings audit_mode disabled","section_id":"turn-off-auditing"},{"id":"new-connection-ex0","language":"json","code":"{\n    \"ts\": 1779874366,\n    \"new_conn\": {\n        \"id\": 13001000,\n        \"srcip\": \"127.0.0.1\",\n        \"srcp\": \"46252\",\n        \"trgip\": \"127.0.0.1\",\n        \"trgp\": \"12000\",\n        \"bdb_name\": \"D1\",\n        \"bdb_uid\": \"1\"\n    },\n    \"audit_worker_id\": 0,\n    \"audit_seq\": 0\n}","section_id":"new-connection"},{"id":"authentication-request-ex0","language":"json","code":"{\n    \"ts\": 1779874366,\n    \"action\": \"auth\",\n    \"id\": 13001000,\n    \"srcip\": \"127.0.0.1\",\n    \"srcp\": \"46252\",\n    \"trgip\": \"127.0.0.1\",\n    \"trgp\": \"12000\",\n    \"bdb_name\": \"D1\",\n    \"bdb_uid\": \"1\",\n    \"status\": 2,\n    \"username\": \"default\",\n    \"identity\": \"user:default\",\n    \"acl-rules\": \"allkeys +@all allchannels\",\n    \"audit_worker_id\": 0,\n    \"audit_seq\": 1\n}","section_id":"authentication-request"},{"id":"successful-command-ex0","language":"json","code":"{\n    \"ts\": 1779874374,\n    \"action\": \"command\",\n    \"id\": 13001000,\n    \"srcip\": \"127.0.0.1\",\n    \"srcp\": \"46252\",\n    \"trgip\": \"127.0.0.1\",\n    \"trgp\": \"12000\",\n    \"bdb_name\": \"D1\",\n    \"bdb_uid\": \"1\",\n    \"status\": 9,\n    \"command\": \"get\",\n    \"keys\": [\"myKey\"],\n    \"full_key_size_bytes\": 3,\n    \"captured_key_size_bytes\": 3,\n    \"total_keys_count\": 1,\n    \"key_truncated\": false,\n    \"affected_count\": null,\n    \"audit_worker_id\": 0,\n    \"audit_seq\": 3\n}","section_id":"successful-command"},{"id":"failed-command-ex0","language":"json","code":"{\n    \"ts\": 1779874378,\n    \"action\": \"command\",\n    \"id\": 13001000,\n    \"srcip\": \"127.0.0.1\",\n    \"srcp\": \"46252\",\n    \"trgip\": \"127.0.0.1\",\n    \"trgp\": \"12000\",\n    \"bdb_name\": \"D1\",\n    \"bdb_uid\": \"1\",\n    \"status\": 10,\n    \"command\": \"set\",\n    \"keys\": [],\n    \"full_key_size_bytes\": 0,\n    \"captured_key_size_bytes\": 0,\n    \"total_keys_count\": 0,\n    \"key_truncated\": false,\n    \"affected_count\": null,\n    \"error\": \"ERR wrong number of arguments for 'set' command\",\n    \"error_truncated\": false,\n    \"audit_worker_id\": 0,\n    \"audit_seq\": 4\n}","section_id":"failed-command"},{"id":"close-connection-ex0","language":"json","code":"{\n    \"ts\": 1779874380,\n    \"close_conn\": {\n        \"id\": 13001000,\n        \"srcip\": \"127.0.0.1\",\n        \"srcp\": \"46252\",\n        \"trgip\": \"127.0.0.1\",\n        \"trgp\": \"12000\",\n        \"bdb_name\": \"D1\",\n        \"bdb_uid\": \"1\"\n    },\n    \"audit_worker_id\": 0,\n    \"audit_seq\": 5\n}","section_id":"close-connection"}]}
{"schema_version":2,"id":"operate/rs/security/certificates/certificate-based-authentication","title":"Certificate-based authentication","url":"https://redis.io/docs/latest/operate/rs/security/certificates/certificate-based-authentication/","summary":"Certificate-based authentication allows secure, passwordless access to the REST API and databases.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:28+01:00","page_type":"content","content_hash":"dadc6a567374ff1a1ff3f2503460338dd2a42d833c15ede2d68eb4d9172685f5","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can set up certificate-based authentication for specific users to enable secure, passwordless access to the Redis Software [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api) and databases."},{"id":"certificate-based-authentication-for-the-rest-api","title":"Certificate-based authentication for the REST API","role":"content","text":""},{"id":"set-up-certificate-based-authentication-for-the-rest-api","title":"Set up certificate-based authentication for the REST API","role":"content","text":"To set up certificate-based authentication:\n\n1. Add a trusted CA certificate `mtls_trusted_ca` to the cluster using an [update cluster certificates](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/certificates) request:\n\n    **Redis Software v7.22.2 and later:**\n\nFor Redis Software versions 7.22.2 and later, use:\n\n[code example]\n\n**Redis Software v7.22.0 and earlier:**\n\nFor Redis Software versions 7.22.0 and earlier, use:\n\n[code example]\n\n\n\n1. [Update cluster settings](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster#put-cluster) with mutual TLS (mTLS) configuration using one of the following options:\n\n    **Without subject validation:**\n\nAdditional certificate validation is optional. To enable mutual TLS without subject validation, use:\n\n[code example]\n\n**With SAN validation:**\n\nFor certificate validation by Subject Alternative Name (SAN), use:\n\n[code example]\n\nReplace the placeholder value `<>` with your client certificate's Subject Common Name or SAN DNS entry.\n\n**Example certificate and mTLS settings**\n\nIf a client certificate has:\n\n- Subject: `CN=client.example.com`\n\n- SAN: `DNS:app1.example.com, DNS:client.example.com, DNS:app1-prod.example.com`\n\nYou can use any of these values for the CN in `mtls_authorized_subjects`:\n\n[code example]\n\n**With Full Subject Name validation:**\n\nFor certificate validation by full Subject Name, use:\n\n[code example]\n\nReplace the placeholder values `<>` with your client certificate's subject values.\n\n**Example certificate and mTLS settings**\n\nIf a client certificate has:\n\n- Subject: `CN=client.example.com`\n\n- SAN: `DNS:app1.example.com, DNS:client.example.com, DNS:app1-prod.example.com`\n\nYou can use any of these values for the CN in `mtls_authorized_subjects`:\n\n[code example]\n\n\n\n1. When you [create new users](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/users#post-user), include `\"auth_method\": \"certificate\"` and `certificate_subject_line` in the request body:\n\n    [code example]\n\n    Replace the placeholder values `<>` with your client certificate's subject values.\n\n    > [!NOTE]\n    > The `certificate_subject_line` must:\n    >\n    > - Follow [RFC 2253](https://www.rfc-editor.org/rfc/rfc2253) format.\n    >\n    > - List the attributes in reverse order, starting with the Common Name (`CN`).\n    >\n    > - Not contain spaces after the commas that separate attributes.\n    >\n    > - Exactly match the certificate's RFC 2253 subject.\n    >\n    > - Contain only one Organizational Unit (`OU`) value.\n    >"},{"id":"authenticate-rest-api-requests","title":"Authenticate REST API requests","role":"security","text":"To use the REST API with certificate-based authentication, you must provide a client certificate, signed by the trusted CA `mtls_trusted_ca`, and a private key.\n\nThe following example uses [cURL](https://curl.se/) to send a [REST API request](https://redis.io/docs/latest/operate/rs/references/rest-api/requests):\n\n[code example]"},{"id":"certificate-based-authentication-for-cluster-management","title":"Certificate-based authentication for cluster management","role":"content","text":"Two cluster-management flows support certificate credentials when Basic and Digest authentication are disabled or unavailable: joining a node to a cluster, and managing an Active-Active database. These flows don't use JWT or LDAP.\n\nCertificate authentication isn't automatic—you must configure each flow to use certificate credentials instead of a username and password. First complete [Set up certificate-based authentication for the REST API](#set-up-certificate-based-authentication-for-the-rest-api) so the cluster has a trusted CA (`mtls_trusted_ca`) and mutual TLS enabled (`mtls_certificate_authentication`)."},{"id":"certificate-credentials","title":"Certificate credentials","role":"content","text":"Certificate credentials consist of three values:\n\n| Value | Required | Description |\n|---|---|---|\n| `client_cert` | Yes | The client certificate. |\n| `client_key` | Yes | The client certificate's private key. |\n| `trusted_ca` | No | The CA that validates the API certificate the peer cluster presents. If you omit it, the cluster uses the certificates in its `mtls_trusted_ca.pem` file. |\n\nMutual TLS applies in both directions. The client presents its certificate, which the cluster validates against its locally configured `mtls_trusted_ca`. The cluster presents its API certificate chain, which the client validates using the CA configured for that connection. Make sure the CA that signed your client certificates is present in `mtls_trusted_ca` on the cluster.\n\nFor any given cluster, use either a username and password or certificate credentials—never both. A request that includes both for the same cluster fails.\n\nThe same three values take different formats depending on the interface:\n\n| Interface | Format |\n|-----------|--------|\n| Bootstrap API `credentials` | PEM strings |\n| [`rladmin cluster join`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/join) | File paths |\n| Active-Active REST API `certificate_auth` | PEM strings |\n| [`crdb-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli) | PEM strings |"},{"id":"join-a-node-to-the-cluster","title":"Join a node to the cluster","role":"content","text":"To join a node with certificate credentials, include `client_cert`, `client_key`, and `trusted_ca` in the `credentials` object of a [bootstrap](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bootstrap) request. These fields take PEM strings:\n\n[code example]\n\n[`rladmin cluster join`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/join) accepts the same three values as file paths, not PEM strings:\n\n[code example]"},{"id":"manage-an-active-active-database","title":"Manage an Active-Active database","role":"content","text":"Each participating cluster in an Active-Active database authenticates separately. To use certificate credentials for a cluster, replace that cluster's `credentials` object with a `certificate_auth` object.\n\nParticipating clusters can use different authentication methods, so you can migrate them from passwords to certificates one at a time. In the following example, the first cluster still uses a username and password while the second uses certificate credentials:\n\n[code example]\n\nTo create an Active-Active database with certificate credentials from the command line, use [`crdb-cli crdb create`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/create). The `--instance` option takes PEM strings:\n\n[code example]\n\nTo add a participating cluster to an existing Active-Active database, use [`crdb-cli crdb add-instance`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/add-instance). Its `--instance` option accepts the same fields as `crdb-cli crdb create`:\n\n[code example]\n\nTo switch an existing participating cluster from a username and password to certificate credentials, use [`crdb-cli crdb update`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/update) with the instance's `id`:\n\n[code example]"},{"id":"certificate-based-authentication-for-databases","title":"Certificate-based authentication for databases","role":"content","text":""},{"id":"set-up-certificate-based-authentication-for-databases","title":"Set up certificate-based authentication for databases","role":"content","text":"To set up certificate-based authentication for databases:\n\n1. Enable mutual TLS for the relevant databases. See [Enable TLS](https://redis.io/docs/latest/operate/rs/security/encryption/tls/enable-tls) for detailed instructions.\n\n1. When you [create new users](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/users#post-user), include `\"auth_method\": \"certificate\"` and `certificate_subject_line` in the request body:\n\n    [code example]\n\n    Replace the placeholder values `<>` with your client certificate's subject values.\n\n    > [!NOTE]\n    > The `certificate_subject_line` must:\n    >\n    > - Follow [RFC 2253](https://www.rfc-editor.org/rfc/rfc2253) format.\n    >\n    > - List the attributes in reverse order, starting with the Common Name (`CN`).\n    >\n    > - Not contain spaces after the commas that separate attributes.\n    >\n    > - Exactly match the certificate's RFC 2253 subject.\n    >\n    > - Contain only one Organizational Unit (`OU`) value.\n    >"},{"id":"authenticate-database-connections","title":"Authenticate database connections","role":"security","text":"To connect to a database with certificate-based authentication, you must provide a client certificate, signed by a trusted CA, and a private key. The client certificate must either be one you previously added to the database to [enable mutual TLS](https://redis.io/docs/latest/operate/rs/security/encryption/tls/enable-tls#enable-mutual-tls) (`authentication_ssl_client_certs` in the REST API), or be signed by one of these certificates.\n\nThe following example shows how to connect to a Redis database with [`redis-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/redis-cli):\n\n[code example]"},{"id":"limitations","title":"Limitations","role":"limits","text":"- Certificate-based authentication is not implemented for the Cluster Manager UI."}],"examples":[{"id":"set-up-certificate-based-authentication-for-the-rest-api-ex0","language":"sh","code":"PUT /v1/cluster/certificates\n{\n  \"certificates\": [\n    {\n      \"name\": \"mtls_trusted_ca\",\n      \"certificate\": \"<content of certificate PEM file>\"\n    }\n  ]\n}","section_id":"set-up-certificate-based-authentication-for-the-rest-api"},{"id":"set-up-certificate-based-authentication-for-the-rest-api-ex1","language":"sh","code":"PUT /v1/cluster/update_cert\n{\n  \"name\": \"mtls_trusted_ca\",\n  \"certificate\": \"<content of certificate PEM file>\"\n}","section_id":"set-up-certificate-based-authentication-for-the-rest-api"},{"id":"set-up-certificate-based-authentication-for-the-rest-api-ex2","language":"sh","code":"PUT /v1/cluster\n{\n  \"mtls_certificate_authentication\": true,\n  \"mtls_client_cert_subject_validation_type\": \"disabled\"\n}","section_id":"set-up-certificate-based-authentication-for-the-rest-api"},{"id":"set-up-certificate-based-authentication-for-the-rest-api-ex3","language":"sh","code":"PUT /v1/cluster\n{\n  \"mtls_certificate_authentication\": true,\n  \"mtls_client_cert_subject_validation_type\": \"san_cn\",\n  \"mtls_authorized_subjects\": [{\n    \"CN\": \"<Subject Common Name or SAN DNS entry>\"\n  }]\n}","section_id":"set-up-certificate-based-authentication-for-the-rest-api"},{"id":"set-up-certificate-based-authentication-for-the-rest-api-ex4","language":"sh","code":"PUT /v1/cluster\n{\n  \"mtls_certificate_authentication\": true,\n  \"mtls_client_cert_subject_validation_type\": \"san_cn\",\n  \"mtls_authorized_subjects\": [\n    {\"CN\": \"client.example.com\"},   // Subject CN\n    {\"CN\": \"app1.example.com\"},     // SAN DNS entry\n    {\"CN\": \"app1-prod.example.com\"} // Another SAN DNS entry\n  ]\n}","section_id":"set-up-certificate-based-authentication-for-the-rest-api"},{"id":"set-up-certificate-based-authentication-for-the-rest-api-ex5","language":"sh","code":"PUT /v1/cluster\n{\n  \"mtls_certificate_authentication\": true,\n  \"mtls_client_cert_subject_validation_type\": \"full_subject\",\n  \"mtls_authorized_subjects\": [{\n    \"CN\": \"<Common Name>\",\n    \"OU\": [<array of Organizational Unit strings>],\n    \"O\": \"<Organization>\",\n    \"C\": \"<2-letter country code>\",\n    \"L\": \"<Locality (city)>\",\n    \"ST\": \"<State/Province>\"\n  }]\n}","section_id":"set-up-certificate-based-authentication-for-the-rest-api"},{"id":"set-up-certificate-based-authentication-for-the-rest-api-ex6","language":"sh","code":"PUT /v1/cluster\n{\n  \"mtls_certificate_authentication\": true,\n  \"mtls_client_cert_subject_validation_type\": \"san_cn\",\n  \"mtls_authorized_subjects\": [\n    {\"CN\": \"client.example.com\"},   // Subject CN\n    {\"CN\": \"app1.example.com\"},     // SAN DNS entry\n    {\"CN\": \"app1-prod.example.com\"} // Another SAN DNS entry\n  ]\n}","section_id":"set-up-certificate-based-authentication-for-the-rest-api"},{"id":"set-up-certificate-based-authentication-for-the-rest-api-ex7","language":"sh","code":"POST /v1/users\n    {\n      \"auth_method\": \"certificate\",\n      \"certificate_subject_line\": \"CN=<Common Name>,OU=<Organizational Unit>,O=<Organization>,L=<Locality>,ST=<State/Province>,C=<Country>\"\n    }","section_id":"set-up-certificate-based-authentication-for-the-rest-api"},{"id":"authenticate-rest-api-requests-ex0","language":"sh","code":"curl --request <METHOD> --url https://<hostname-or-IP-address>:9443/<API-version>/<API-path> --cert client.pem --key client.key","section_id":"authenticate-rest-api-requests"},{"id":"join-a-node-to-the-cluster-ex0","language":"sh","code":"POST /v1/bootstrap/join_cluster\n{\n  \"action\": \"join_cluster\",\n  \"cluster\": {\n    \"nodes\": \"<target-node-ip>\"\n  },\n  \"credentials\": {\n    \"client_cert\": \"-----BEGIN CERTIFICATE-----\\n...\\n-----END CERTIFICATE-----\\n\",\n    \"client_key\": \"-----BEGIN PRIVATE KEY-----\\n...\\n-----END PRIVATE KEY-----\\n\",\n    \"trusted_ca\": \"-----BEGIN CERTIFICATE-----\\n...\\n-----END CERTIFICATE-----\\n\"\n  }\n}","section_id":"join-a-node-to-the-cluster"},{"id":"join-a-node-to-the-cluster-ex1","language":"sh","code":"rladmin cluster join nodes <target-node-ip> \\\n  client_cert <path-to-cert> \\\n  client_key <path-to-key> \\\n  trusted_ca <path-to-trusted-ca>","section_id":"join-a-node-to-the-cluster"},{"id":"manage-an-active-active-database-ex0","language":"sh","code":"POST /v1/crdbs\n{\n  \"name\": \"cert-auth-aa\",\n  \"guid\": \"<guid>\",\n  \"default_db_config\": {\n    \"memory_size\": 104857600,\n    \"replication\": true\n  },\n  \"instances\": [\n    {\n      \"cluster\": {\n        \"name\": \"cluster1.local\",\n        \"url\": \"https://<cluster1-ip>:9443\",\n        \"credentials\": {\n          \"username\": \"<username>\",\n          \"password\": \"<password>\"\n        }\n      }\n    },\n    {\n      \"cluster\": {\n        \"name\": \"cluster2.local\",\n        \"url\": \"https://<cluster2-ip>:9443\",\n        \"certificate_auth\": {\n          \"client_cert\": \"-----BEGIN CERTIFICATE-----\\n...\\n-----END CERTIFICATE-----\\n\",\n          \"client_key\": \"-----BEGIN PRIVATE KEY-----\\n...\\n-----END PRIVATE KEY-----\\n\",\n          \"trusted_ca\": \"-----BEGIN CERTIFICATE-----\\n...\\n-----END CERTIFICATE-----\\n\"\n        }\n      }\n    }\n  ]\n}","section_id":"manage-an-active-active-database"},{"id":"manage-an-active-active-database-ex1","language":"sh","code":"crdb-cli crdb create \\\n  --name cert-crdb \\\n  --instance \"fqdn=cluster1.local,username=<username>,password=<password>\" \\\n  --instance \"fqdn=cluster2.local,client_cert=<client-cert>,client_key=<client-key>,trusted_ca=<trusted-ca>\"","section_id":"manage-an-active-active-database"},{"id":"manage-an-active-active-database-ex2","language":"sh","code":"crdb-cli crdb add-instance \\\n  --crdb-guid <crdb-guid> \\\n  --instance \"fqdn=cluster3.local,client_cert=<client-cert>,client_key=<client-key>,trusted_ca=<trusted-ca>\"","section_id":"manage-an-active-active-database"},{"id":"manage-an-active-active-database-ex3","language":"sh","code":"crdb-cli crdb update \\\n  --crdb-guid <crdb-guid> \\\n  --credentials \"id=2,client_cert=<client-cert>,client_key=<client-key>,trusted_ca=<trusted-ca>\"","section_id":"manage-an-active-active-database"},{"id":"set-up-certificate-based-authentication-for-databases-ex0","language":"sh","code":"POST /v1/users\n    {\n      \"auth_method\": \"certificate\",\n      \"certificate_subject_line\": \"CN=<Common Name>,OU=<Organizational Unit>,O=<Organization>,L=<Locality>,ST=<State/Province>,C=<Country>\"\n    }","section_id":"set-up-certificate-based-authentication-for-databases"},{"id":"authenticate-database-connections-ex0","language":"sh","code":"redis-cli -h <hostname-or-IP-address> -p <port> --tls --cacert <redis_cert>.pem --cert redis_user.crt --key redis_user_private.key","section_id":"authenticate-database-connections"}]}
{"schema_version":2,"id":"operate/rs/security/certificates/create-certificates","title":"Create certificates","url":"https://redis.io/docs/latest/operate/rs/security/certificates/create-certificates/","summary":"Create self-signed certificates to install on a Redis Software cluster.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:28+01:00","page_type":"content","content_hash":"ffd6340125ab4472e0eb11a00bb2a494a4e780fa555556a9d7020d3c32d3fbbb","sections":[{"id":"overview","title":"Overview","role":"overview","text":"When you first install Redis Software, self-signed certificates are created to enable encryption for Redis Software endpoints.  These certificates expire after a year (365 days) and must be renewed.\n\nYou can renew these certificates by replacing them with new self-signed certificates or by replacing them with certificates signed by a [certificate authority](https://en.wikipedia.org/wiki/Certificate_authority) (CA)."},{"id":"renew-self-signed-certificates","title":"Renew self-signed certificates","role":"content","text":"As of [v6.2.18-70](https://redis.io/docs/latest/operate/rs/release-notes/rs-6-2-18-releases/rs-6-2-18-70), Redis Software includes a script to generate self-signed certificates.  \n\nBy default, the `generate_self_signed_certs.sh` script is located in `/opt/redislabs/utils/`.  \n\nHere, you learn how to use this script to generate new certificates and how to install them."},{"id":"generate-self-signed-certs-script-options","title":"Generate self-signed certs script options","role":"content","text":"You can run the `generate_self_signed_certs.sh` script with the following options:\n\n| Option | Description |\n|----------|-------------|\n| `-h`<br />`--help` | Displays usage instructions. (Optional) |\n| `-d <days>`<br />`--days <days>` | Number of days the self-signed certificate is valid for. Setting this field longer than a year (365 days) is not recommended. (Optional, default: 365) |\n| `-f <names>`<br /><nobr>`--fqdnNames <names>`</nobr> | Space-separated list of [fully qualified domain names (FQDNs)](https://en.wikipedia.org/wiki/Fully_qualified_domain_name). Used for [Subject Alternative Names (SANs)](https://en.wikipedia.org/wiki/Public_key_certificate#Subject_Alternative_Name_certificate). (Required)<br />Example: `-f \"redis.example.com redis-1.example.com\"` |\n| `-t <type>`<br />`--type <type>` | Type of certificate to generate. (Optional, default: all) <br />Values:<br />**cm**: Cluster Manager UI certificate<br />**api**: REST API certificate<br /> **proxy**: database endpoint proxy certificate<br />**syncer**: syncer component certificate<br />**metrics**: metrics exporter certificate<br />**all**: generates all certificate types |"},{"id":"step-1-generate-new-certificates","title":"Step 1: Generate new certificates","role":"content","text":"Sign in to the machine hosting the cluster's master node and then run the following command:\n\n[code example]\n\nWhen you run the script, it either reports success (`\"Self signed cert generated successfully\"`) or an error message. Use the error message to troubleshoot any issues.\n\nThe following example generates all self signed certificates for `mycluster.example.com`; these certificates expire one year after the command is run:\n\n[code example]\n\nSuppose you want to create a Cluster Manager UI certificate to support two clusters for a period of two years.  The following example shows how:\n\n[code example]\n\nHere, a certificate file and certificate key are generated to support the following domains:\n\n[code example]"},{"id":"step-2-locate-the-new-certificate-files","title":"Step 2: Locate the new certificate files","role":"content","text":"When successful, the script generates two .PEM files for each generated certificate: a certificate file and a certificate key, each named after the type of certificate generated (see earlier table for individual certificate names.)\n\nThese files can be found in the `/tmp` directory.\n\n[code example]"},{"id":"step-3-set-permissions","title":"Step 3: Set permissions","role":"content","text":"We recommend setting the permissions of your new certificate files to limit read and write access to the file owner and to set group and other user permissions to read access.\n\n[code example]"},{"id":"replace-self-signed","title":"Step 4: Replace existing certificates","role":"content","text":"You can use `rladmin` to replace the existing certificates with new certificates:\n\n[code example]\n\nThe following values are supported for the _\\<CertName>_ parameter:\n\n| Value | Description |\n|-------|-------------|\n| `api` | The REST API |\n| `cm` | The Cluster Manager UI |\n| `metrics_exporter` | The metrics exporter |\n| `proxy` | The database endpoint |\n| `syncer` | The synchronization process |\n\nYou can also use the REST API.  To learn more, see [Update certificates](https://redis.io/docs/latest/operate/rs/security/certificates/updating-certificates#how-to-update-certificates)."},{"id":"create-ca-signed-certificates","title":"Create CA-signed certificates","role":"content","text":"You can use certificates signed by a [certificate authority](https://en.wikipedia.org/wiki/Certificate_authority) (CA) for Redis Software cluster security.\n\nFor best results, use the following guidelines to create the certificates."},{"id":"certificate-requirements","title":"Certificate requirements","role":"content","text":"- Certificates must include both of the following in the extended key usage extension:\n\n    - TLS Web Server Authentication (OID: 1.3.6.1.5.5.7.3.1)\n\n    - TLS Web Client Authentication (OID: 1.3.6.1.5.5.7.3.2)\n\n    > [!WARNING]\n    > Using certificate templates that only include Server Authentication will cause SSL and TLS errors.\n    >     \n\n- Include the full certificate chain when creating certificate .PEM files. List certificates in order from leaf to root. Some deployments may not require the root CA in the certificate file.\n\n    [code example]\n\n- The Subject Alternative Name (SAN) extension must include the following DNS entries based on your cluster's fully qualified domain name (FQDN):\n\n    [code example]\n\n    For example, if the cluster FQDN is `redis.example.com`, the DNS entries include:\n\n    [code example]\n\n- The key usage extension must include:\n\n    - Digital Signature\n    \n    - Key Encipherment\n\n- Use the cluster's fully qualified domain name (FQDN) for the Common Name (CN).\n\n- Use SHA-256 or SHA-512 for the signature algorithm.\n\n    > [!NOTE]\n    > SHA-1 is deprecated and may be blocked by some operating systems.\n    >     \n\n- The minimum RSA key size is 2048 bits. 4096 bits is recommended for enhanced security.\n\n- A validity period of 1 year or less is recommended."},{"id":"create-certificates-with-openssl","title":"Create certificates with OpenSSL","role":"content","text":"1. Create a configuration file named `redis-cert.cnf` with the following content:\n\n    [code example]\n\n    Replace `redis-cluster.example.com` with your cluster's FQDN and replace the `[dn]` section with your own details. Check with your security team or certificate authority for help creating a valid configuration file for your environment.\n\n1. Create a private key:\n\n    [code example]\n\n    For enhanced security, use a 4096-bit key:\n\n    [code example]\n\n1. Create a certificate signing request (CSR):\n\n    [code example]\n\n1. [Verify the certificate signing request and submit it to your CA](#submit-csr-to-the-certificate-authority).\n\n1. [Create a certificate chain file](#create-a-certificate-chain-file).\n\n#### Submit CSR to the certificate authority\n\n1. Before submitting to your CA, verify the certificate signing request contains all required attributes:\n\n    [code example]\n\n    Check the output for the following:\n\n    - Subject contains the correct Common Name.\n\n    - Subject Alternative Names include all required DNS entries.\n\n    - Key Usage includes `Digital Signature, Key Encipherment`.\n\n    - Extended Key Usage includes `TLS Web Server Authentication, TLS Web Client Authentication`.\n\n1. Submit `redis-cert.csr` to your certificate authority for signing. The process varies by CA:\n\n    - For an internal CA, follow your organization's certificate request process.\n\n    - For a commercial CA, use their web portal or API.\n\n    - For testing or development purposes only, you can create a self-signed certificate:\n\n        [code example]\n\n        > [!WARNING]\n        > Do not use self-signed certificates in production.\n        >         \n\n#### Create a certificate chain file\n\nAfter receiving the signed certificate from your CA, create a certificate chain file by combining the certificates in the correct order:\n\n[code example]\n\nYou must order your certificates as follows:\n\n1. Leaf certificate, which is your domain certificate.\n\n1. Intermediate CA certificates.\n\n1. Root CA certificate, which is optional depending on your deployment."},{"id":"create-certificates-with-windows-active-directory-certificate-services","title":"Create certificates with Windows Active Directory Certificate Services","role":"content","text":"To create certificates using Windows Active Directory Certificate Services:\n\n1. Complete the [prerequisites](#windows-ad-prereqs).\n\n1. [Create a modified Web Server template](#create-certificate-template).\n\n1. [Publish the template](#publish-the-template).\n\n1. [Request a certificate using certreq](#request-a-certificate-using-certreq).\n\n1. [Export the certificate and private key](#export-certificate-and-private-key).\n\n#### Prerequisites {#windows-ad-prereqs}\n\nBefore you can create certificates with Windows Active Directory Certificate Services, you need:\n\n- Access to Windows Active Directory Certificate Services.\n\n- Permissions to modify certificate templates.\n\n- Certificate Templates Console certtmpl.msc.\n\n#### Create certificate template\n\nThe default Web Server template in Windows AD CS only includes Server Authentication. You must create a modified template that includes both Server and Client Authentication.\n\n1. Open the Certificate Templates Console as an administrator:\n\n    [code example]\n\n1. Right-click the **Web Server** template, then select **Duplicate Template**.\n\n1. In the **General** tab:\n\n    1. Set **Template name** to `Redis Software Server`.\n\n    1. Set the **Validity period** to 1 year.\n\n1. In the **Extensions** tab:\n\n    1. Select **Application Policies**.\n\n    1. Click **Edit** and verify both of the following are present:\n\n        - Server Authentication (1.3.6.1.5.5.7.3.1)\n\n        - Client Authentication (1.3.6.1.5.5.7.3.2)\n\n    1. If Client Authentication is missing, click **Add**, select **Client Authentication**, and click **OK**.\n\n1. In the **Subject Name** tab, select **Supply in the request** to allow specifying Subject Alternative Names during the certificate request.\n\n1. In the **Extensions** tab:\n\n    1. Select **Key Usage**.\n\n    1. Click **Edit**.\n\n    1. Make sure **Digital signature** and **Key encipherment** are selected.\n\n1. In the **Security** tab:\n\n    1. Add appropriate users and groups.\n\n    1. Grant **Read** and **Enroll** permissions.\n\n1. Click **OK** to save the template.\n\n#### Publish the template\n\n1. Open the Certification Authority console:\n\n    [code example]\n\n1. Expand your CA.\n\n1. Right-click **Certificate Templates**, then select **New > Certificate Template to Issue**.\n\n1. Select `Redis Software Server` and click **OK**.\n\n#### Request a certificate using certreq\n\n1. Create a certificate request file `redis-cert.inf`:\n\n    [code example]\n\n    Replace `redis-cluster.example.com` with your cluster's FQDN.\n\n1. Generate the certificate request:\n\n    [code example]\n\n1. Submit the request to your CA:\n\n    [code example]\n\n1. Accept the issued certificate:\n\n    [code example]\n\n#### Export certificate and private key\n\n1. Open Certificate Manager:\n\n    [code example]\n\n1. Go to **Personal > Certificates**.\n\n1. Right-click your certificate, then select **All Tasks > Export**.\n\n1. Export the certificate with the private key:\n\n    1. Select **Yes, export the private key**.\n\n    1. Choose **Personal Information Exchange - PKCS #12 (.PFX)**.\n\n    1. Set a password.\n\n    1. Save as `redis-cert.pfx`.\n\n1. Convert the certificate and key to PEM format using OpenSSL:\n\n    1. Extract the certificate chain:\n\n        [code example]\n\n    1. Extract the private key:\n        [code example]"},{"id":"validate-certificates","title":"Validate certificates","role":"content","text":"Before you upload certificates to Redis Software, validate that they meet all requirements:\n\n1. Validate extended key usage:\n\n    [code example]\n\n    Expected output:\n\n    [code example]\n\n    If Client Authentication is missing, the certificate will fail. You must reissue the certificate with both Server and Client Authentication.\n\n1. Validate key usage:\n\n    [code example]\n\n    Expected output:\n\n    [code example]\n\n1. Validate Subject Alternative Names:\n\n    [code example]\n\n    Expected output:\n\n    [code example]\n\n1. Validate certificate chain:\n\n    1. Count the certificates in the chain:\n\n        [code example]\n\n        Expected output: 2 or 3 if the root CA is included.\n\n    1. View all certificates in the chain:\n\n        [code example]\n\n    1. Verify the chain order. Each certificate's `Issuer` should match the `Subject` of the next certificate.\n\n1. Validate the signature algorithm:\n\n    [code example]\n\n    Expected output: `sha256WithRSAEncryption` or `sha512WithRSAEncryption`.\n    \n    > [!NOTE]\n    > Avoid `sha1WithRSAEncryption` because it is deprecated and might be blocked.\n    >     \n\n1. Validate the public key size:\n\n    [code example]\n\n    Expected output: `Public-Key: (2048 bit)` or higher.\n\n1. Verify the private key matches the certificate:\n\n    1. Get the certificate modulus:\n\n        [code example]\n\n    1. Get the key modulus:\n\n        [code example]\n\n    1. If the MD5 hashes do not match, you have the wrong private key file.\n\n1. Verify the certificate is currently valid and not expired:\n\n    [code example]"},{"id":"install-certificates","title":"Install certificates","role":"setup","text":"After creating and validating your certificates, install them on the Redis Software cluster:\n\n1. Set restrictive file permissions for the private key:\n\n    [code example]\n\n1. Set readable file permissions for the certificate:\n\n    [code example]\n\n1. Replace the existing certificates with the new certificates using [`rladmin cluster certificate`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/certificate):\n\n    [code example]\n\n    Replace `<cert-name>` with the relevant certificate name:\n\n    | Value | Description |\n    |-------|-------------|\n    | `api` | REST API certificate |\n    | `cm` | Cluster Manager UI certificate |\n    | `metrics_exporter` | Metrics exporter certificate |\n    | `proxy` | Database endpoint proxy certificate |\n    | `syncer` | Synchronization process certificate |\n\n1. Verify the installed certificate:\n\n    [code example]"},{"id":"troubleshooting","title":"Troubleshooting","role":"errors","text":"#### SSLV3_ALERT_UNSUPPORTED_CERTIFICATE error\n\nIf the certificate's extended key usage does not include TLS Web Client Authentication, the following error occurs:\n\n[code example]\n\nTo fix this error:\n\n1. Validate extended key usage:\n\n    [code example]\n\n1. If Client Authentication is missing, reissue the certificate:\n\n    - For OpenSSL, add the following line to the configuration file.\n\n        [code example]\n\n    - For Windows AD CS, modify the certificate template and add Client Authentication to **Application Policies**.\n\n#### CERTIFICATE_VERIFY_FAILED error\n\nIf a `CERTIFICATE_VERIFY_FAILED` error occurs, the certificate chain is incomplete or in the wrong order.\n\nTo fix this error:\n\n1. Verify the certificate chain order. Each certificate's `Issuer` should match the `Subject` of the next certificate.\n\n    [code example]\n\n1. Rebuild the certificate chain in the correct order:\n\n    [code example]\n\n#### Key values mismatch error\n\nIf a `key_values_mismatch` error occurs, the private key does not match the certificate.\n\nVerify the private key matches the certificate:\n\n1. Get the certificate modulus:\n\n    [code example]\n\n1. Get the key modulus:\n\n    [code example]\n\n1. If the MD5 hashes don't match, you have the wrong private key file. To fix this error, find the correct private key file.\n\n#### Missing Subject Alternative Names\n\nIf a certificate only has a Common Name, but no Subject Alternative Names, modern TLS implementations might reject it.\n\nTo fix this issue, reissue the certificate with proper Subject Alternative Names, including all required DNS entries."}],"examples":[{"id":"step-1-generate-new-certificates-ex0","language":" bash","code":"% sudo -u redislabs /opt/redislabs/utils/generate_self_signed_certs.sh \\\n   -f \"<DomainName1 DomainName2>\" -d <Days> -t <Type>","section_id":"step-1-generate-new-certificates"},{"id":"step-1-generate-new-certificates-ex1","language":" bash","code":"$ sudo -u redislabs /opt/redislabs/utils/generate_self_signed_certs.sh \\\n    -f \"mycluster.example.com\"`","section_id":"step-1-generate-new-certificates"},{"id":"step-1-generate-new-certificates-ex2","language":" bash","code":"$ sudo -u redislabs /opt/redislabs/utils/generate_self_signed_certs.sh \\\n    -f \"mycluster.example.com anothercluster.example.com\" -d 730 -t cm","section_id":"step-1-generate-new-certificates"},{"id":"step-1-generate-new-certificates-ex3","language":" text","code":"mycluster.example.com\n*.mycluster.example.com\nanothercluster.example.com \n*.anothercluster.example.com","section_id":"step-1-generate-new-certificates"},{"id":"step-2-locate-the-new-certificate-files-ex0","language":" bash","code":"$ ls -la /tmp/*.pem","section_id":"step-2-locate-the-new-certificate-files"},{"id":"step-3-set-permissions-ex0","language":" bash","code":"$ sudo chmod 644 /tmp/*.pem","section_id":"step-3-set-permissions"},{"id":"replace-self-signed-ex0","language":" console","code":"$ rladmin cluster certificate set <CertName> certificate_file \\\n   <CertFilename>.pem key_file <KeyFilename>.pem","section_id":"replace-self-signed"},{"id":"certificate-requirements-ex0","language":"sh","code":"-----BEGIN CERTIFICATE-----\n    <Leaf/Domain Certificate>\n    -----END CERTIFICATE-----\n    -----BEGIN CERTIFICATE-----\n    <Intermediate CA Certificate>\n    -----END CERTIFICATE-----\n    -----BEGIN CERTIFICATE-----\n    <Root CA Certificate>\n    -----END CERTIFICATE-----","section_id":"certificate-requirements"},{"id":"certificate-requirements-ex1","language":"ini","code":"dns=<cluster-fqdn>\n    dns=*.<cluster-fqdn>\n    dns=internal.<cluster-fqdn>\n    dns=*.internal.<cluster-fqdn>","section_id":"certificate-requirements"},{"id":"certificate-requirements-ex2","language":"ini","code":"dns=redis.example.com\n    dns=*.redis.example.com\n    dns=internal.redis.example.com\n    dns=*.internal.redis.example.com","section_id":"certificate-requirements"},{"id":"create-certificates-with-openssl-ex0","language":"ini","code":"[req]\n    default_bits = 2048\n    prompt = no\n    default_md = sha256\n    distinguished_name = dn\n    req_extensions = v3_req\n\n    [dn]\n    C = US\n    ST = California\n    L = Los Angeles\n    O = Your Organization\n    CN = redis-cluster.example.com\n\n    [v3_req]\n    keyUsage = critical, digitalSignature, keyEncipherment\n    extendedKeyUsage = serverAuth, clientAuth\n    subjectAltName = @alt_names\n\n    [alt_names]\n    DNS.1 = redis-cluster.example.com\n    DNS.2 = *.redis-cluster.example.com\n    DNS.3 = internal.redis-cluster.example.com\n    DNS.4 = *.internal.redis-cluster.example.com","section_id":"create-certificates-with-openssl"},{"id":"create-certificates-with-openssl-ex1","language":"sh","code":"openssl genrsa -out redis-key.pem 2048","section_id":"create-certificates-with-openssl"},{"id":"create-certificates-with-openssl-ex2","language":"sh","code":"openssl genrsa -out redis-key.pem 4096","section_id":"create-certificates-with-openssl"},{"id":"create-certificates-with-openssl-ex3","language":"sh","code":"openssl req -new \\\n        -key redis-key.pem \\\n        -out redis-cert.csr \\\n        -config redis-cert.cnf","section_id":"create-certificates-with-openssl"},{"id":"create-certificates-with-openssl-ex4","language":"sh","code":"openssl req -text -noout -verify -in redis-cert.csr","section_id":"create-certificates-with-openssl"},{"id":"create-certificates-with-openssl-ex5","language":"sh","code":"openssl x509 -req \\\n          -in redis-cert.csr \\\n          -signkey redis-key.pem \\\n          -out redis-cert.pem \\\n          -days 365 \\\n          -extensions v3_req \\\n          -extfile redis-cert.cnf","section_id":"create-certificates-with-openssl"},{"id":"create-certificates-with-openssl-ex6","language":"sh","code":"cat redis-cert.pem intermediate-ca.pem root-ca.pem > redis-cert-chain.pem","section_id":"create-certificates-with-openssl"},{"id":"create-certificates-with-windows-active-directory-certificate-services-ex0","language":"sh","code":"certtmpl.msc","section_id":"create-certificates-with-windows-active-directory-certificate-services"},{"id":"create-certificates-with-windows-active-directory-certificate-services-ex1","language":"sh","code":"certsrv.msc","section_id":"create-certificates-with-windows-active-directory-certificate-services"},{"id":"create-certificates-with-windows-active-directory-certificate-services-ex2","language":"ini","code":"[NewRequest]\n    Subject = \"CN=redis-cluster.example.com,O=Your Organization,L=City,ST=State,C=US\"\n    KeyLength = 2048\n    KeySpec = 1\n    Exportable = TRUE\n    MachineKeySet = TRUE\n    ProviderName = \"Microsoft RSA SChannel Cryptographic Provider\"\n    RequestType = PKCS10\n\n    [Extensions]\n    2.5.29.17 = \"{text}\"\n    _continue_ = \"dns=redis-cluster.example.com&\"\n    _continue_ = \"dns=*.redis-cluster.example.com&\"\n    _continue_ = \"dns=internal.redis-cluster.example.com&\"\n    _continue_ = \"dns=*.internal.redis-cluster.example.com\"","section_id":"create-certificates-with-windows-active-directory-certificate-services"},{"id":"create-certificates-with-windows-active-directory-certificate-services-ex3","language":"sh","code":"certreq -new redis-cert.inf redis-cert.req","section_id":"create-certificates-with-windows-active-directory-certificate-services"},{"id":"create-certificates-with-windows-active-directory-certificate-services-ex4","language":"sh","code":"certreq -submit -config \"CA-SERVER\\CA-Name\" redis-cert.req redis-cert.cer","section_id":"create-certificates-with-windows-active-directory-certificate-services"},{"id":"create-certificates-with-windows-active-directory-certificate-services-ex5","language":"sh","code":"certreq -accept redis-cert.cer","section_id":"create-certificates-with-windows-active-directory-certificate-services"},{"id":"create-certificates-with-windows-active-directory-certificate-services-ex6","language":"sh","code":"certlm.msc","section_id":"create-certificates-with-windows-active-directory-certificate-services"},{"id":"create-certificates-with-windows-active-directory-certificate-services-ex7","language":"sh","code":"openssl pkcs12 -in redis-cert.pfx -nokeys -out redis-cert.pem","section_id":"create-certificates-with-windows-active-directory-certificate-services"},{"id":"create-certificates-with-windows-active-directory-certificate-services-ex8","language":"sh","code":"openssl pkcs12 -in redis-cert.pfx -nocerts -out redis-key.pem -nodes","section_id":"create-certificates-with-windows-active-directory-certificate-services"},{"id":"validate-certificates-ex0","language":"sh","code":"openssl x509 -in redis-cert-chain.pem -text -noout | grep -A 3 \"Extended Key Usage\"","section_id":"validate-certificates"},{"id":"validate-certificates-ex1","language":"sh","code":"X509v3 Extended Key Usage:\n        TLS Web Server Authentication, TLS Web Client Authentication","section_id":"validate-certificates"},{"id":"validate-certificates-ex2","language":"bash","code":"openssl x509 -in redis-cert-chain.pem -text -noout | grep -A 2 \"Key Usage\"","section_id":"validate-certificates"},{"id":"validate-certificates-ex3","language":"text","code":"X509v3 Key Usage: critical\n        Digital Signature, Key Encipherment","section_id":"validate-certificates"},{"id":"validate-certificates-ex4","language":"sh","code":"openssl x509 -in redis-cert-chain.pem -text -noout | grep -A 10 \"Subject Alternative Name\"","section_id":"validate-certificates"},{"id":"validate-certificates-ex5","language":"sh","code":"X509v3 Subject Alternative Name:\n        DNS:redis-cluster.example.com\n        DNS:*.redis-cluster.example.com\n        DNS:internal.redis-cluster.example.com\n        DNS:*.internal.redis-cluster.example.com","section_id":"validate-certificates"},{"id":"validate-certificates-ex6","language":"sh","code":"grep -c \"BEGIN CERTIFICATE\" redis-cert-chain.pem","section_id":"validate-certificates"},{"id":"validate-certificates-ex7","language":"bash","code":"openssl crl2pkcs7 -nocrl -certfile redis-cert-chain.pem | \\\n            openssl pkcs7 -print_certs -text -noout | \\\n            grep -E \"Subject:|Issuer:\"","section_id":"validate-certificates"},{"id":"validate-certificates-ex8","language":"bash","code":"openssl x509 -in redis-cert-chain.pem -text -noout | grep \"Signature Algorithm\"","section_id":"validate-certificates"},{"id":"validate-certificates-ex9","language":"bash","code":"openssl x509 -in redis-cert-chain.pem -text -noout | grep \"Public-Key\"","section_id":"validate-certificates"},{"id":"validate-certificates-ex10","language":"sh","code":"openssl x509 -noout -modulus -in redis-cert-chain.pem | openssl md5","section_id":"validate-certificates"},{"id":"validate-certificates-ex11","language":"sh","code":"openssl rsa -noout -modulus -in redis-key.pem | openssl md5","section_id":"validate-certificates"},{"id":"validate-certificates-ex12","language":"bash","code":"openssl x509 -in redis-cert-chain.pem -noout -dates","section_id":"validate-certificates"},{"id":"install-certificates-ex0","language":"bash","code":"$ chmod 600 redis-key.pem\n    $ chown redislabs:redislabs redis-key.pem","section_id":"install-certificates"},{"id":"install-certificates-ex1","language":"bash","code":"$ chmod 644 redis-cert-chain.pem\n    $ chown redislabs:redislabs redis-cert-chain.pem","section_id":"install-certificates"},{"id":"install-certificates-ex2","language":"bash","code":"rladmin cluster certificate set <cert-name> \\\n        certificate_file /path/to/redis-cert-chain.pem \\\n        key_file /path/to/redis-key.pem","section_id":"install-certificates"},{"id":"install-certificates-ex3","language":"sh","code":"openssl x509 -in /etc/opt/redislabs/<cert-name>_cert.pem -noout -subject -issuer -dates","section_id":"install-certificates"},{"id":"troubleshooting-ex0","language":"sh","code":"[SSL: SSLV3_ALERT_UNSUPPORTED_CERTIFICATE] ssl/tls alert unsupported certificate","section_id":"troubleshooting"},{"id":"troubleshooting-ex1","language":"bash","code":"openssl x509 -in redis-cert-chain.pem -text -noout | grep -A 3 \"Extended Key Usage\"","section_id":"troubleshooting"},{"id":"troubleshooting-ex2","language":"ini","code":"extendedKeyUsage = serverAuth, clientAuth","section_id":"troubleshooting"},{"id":"troubleshooting-ex3","language":"sh","code":"openssl crl2pkcs7 -nocrl -certfile redis-cert-chain.pem | \\\n        openssl pkcs7 -print_certs -text -noout | \\\n        grep -E \"Subject:|Issuer:\"","section_id":"troubleshooting"},{"id":"troubleshooting-ex4","language":"sh","code":"-----BEGIN CERTIFICATE-----\n    <Leaf/Domain Certificate>\n    -----END CERTIFICATE-----\n    -----BEGIN CERTIFICATE-----\n    <Intermediate CA Certificate>\n    -----END CERTIFICATE-----\n    -----BEGIN CERTIFICATE-----\n    <Root CA Certificate>\n    -----END CERTIFICATE-----","section_id":"troubleshooting"},{"id":"troubleshooting-ex5","language":"sh","code":"openssl x509 -noout -modulus -in redis-cert-chain.pem | openssl md5","section_id":"troubleshooting"},{"id":"troubleshooting-ex6","language":"sh","code":"openssl rsa -noout -modulus -in redis-key.pem | openssl md5","section_id":"troubleshooting"}]}
{"schema_version":2,"id":"operate/rs/security/certificates","title":"Certificates","url":"https://redis.io/docs/latest/operate/rs/security/certificates/","summary":"An overview of certificates in Redis Software.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:28+01:00","children":[{"id":"operate/rs/security/certificates/create-certificates","summary":"Create self-signed certificates to install on a Redis Software cluster.","title":"Create certificates","url":"https://redis.io/docs/latest/operate/rs/security/certificates/create-certificates/"},{"id":"operate/rs/security/certificates/monitor-certificates","summary":"Monitor certificates on a Redis Software cluster.","title":"Monitor certificates","url":"https://redis.io/docs/latest/operate/rs/security/certificates/monitor-certificates/"},{"id":"operate/rs/security/certificates/updating-certificates","summary":"Update certificates in a Redis Software cluster.","title":"Update certificates","url":"https://redis.io/docs/latest/operate/rs/security/certificates/updating-certificates/"},{"id":"operate/rs/security/certificates/ocsp-stapling","summary":"Use OCSP stapling to verify certificates maintained by a third-party CA and authenticate connection attempts between clients and servers.","title":"Enable OCSP stapling","url":"https://redis.io/docs/latest/operate/rs/security/certificates/ocsp-stapling/"},{"id":"operate/rs/security/certificates/certificate-based-authentication","summary":"Certificate-based authentication allows secure, passwordless access to the REST API and databases.","title":"Certificate-based authentication","url":"https://redis.io/docs/latest/operate/rs/security/certificates/certificate-based-authentication/"}],"page_type":"content","content_hash":"e42d77ab8317752f90cfd6b455d479d3b881b21b188c93f5f0ffcfb9e50a90f6","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software uses self-signed certificates by default to ensure that the product is secure. These certificates are autogenerated on the first node of each Redis Software installation and are copied to all other nodes added to the cluster.\n\nYou can replace a self-signed certificate with one signed by a certificate authority of your choice."},{"id":"supported-certificates","title":"Supported certificates","role":"compatibility","text":"Here's the list of supported certificates that create secure, encrypted connections to your Redis Software cluster:\n\n| Certificate name | Autogenerated | Description |\n|------------------|:---------------:|-------------|\n| `api` | <span title=\"Yes\">&#x2705;</span> | Encrypts [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api) requests and responses. |\n| `ccs_internode_encryption` | <span title=\"Yes\">&#x2705;</span> | Control plane [internode encryption](https://redis.io/docs/latest/operate/rs/security/encryption/internode-encryption). Encrypts internal traffic for cluster management. |\n| `cm` | <span title=\"Yes\">&#x2705;</span> | Secures connections to the Redis Software Cluster Manager UI. |\n| `data_internode_encryption` | <span title=\"Yes\">&#x2705;</span> | Data plane [internode encryption](https://redis.io/docs/latest/operate/rs/security/encryption/internode-encryption). Encrypts internal data traffic between shards and the data management controller (DMC). |\n| `ldap_client` | <span title=\"No\">:x:</span> | Secures connections between LDAP clients and LDAP servers. |\n| `metrics_exporter` | <span title=\"Yes\">&#x2705;</span> | Sends Redis Software metrics to external [monitoring tools](https://redis.io/docs/latest/operate/rs/monitoring) over a secure connection. |\n| `mtls_trusted_ca` | <span title=\"No\">:x:</span>  | Required to enable certificate-based authentication for secure, passwordless access to the REST API. |\n| `proxy` | <span title=\"Yes\">&#x2705;</span> | Creates secure, encrypted connections between clients and databases. |\n| `sso_issuer` | <span title=\"No\">:x:</span> | Identity provider certificate for [single sign-on (SSO)](https://redis.io/docs/latest/operate/rs/security/access-control/saml-sso). |\n| `sso_service` | <span title=\"No\">:x:</span> | Service provider certificate for [single sign-on (SSO)](https://redis.io/docs/latest/operate/rs/security/access-control/saml-sso). |\n| `syncer` | <span title=\"Yes\">&#x2705;</span> | For [Active-Active](https://redis.io/docs/latest/operate/rs/databases/active-active) or [Replica Of](https://redis.io/docs/latest/operate/rs/databases/import-export/replica-of) databases, encrypts data during the synchronization of participating clusters. |\n\nCertificates that are not autogenerated are optional unless you want to use certain features. For example, you must provide your own `ldap_client` certificate to enable [LDAP authentication](https://redis.io/docs/latest/operate/rs/security/access-control/ldap), an `mtls_trusted_ca` certificate to enable certificate-based authentication, or `sso_issuer` and `sso_service` certificates for [single sign-on (SSO)](https://redis.io/docs/latest/operate/rs/security/access-control/saml-sso)."},{"id":"accept-self-signed-certificates-to-access-the-cluster-manager-ui","title":"Accept self-signed certificates to access the Cluster Manager UI","role":"content","text":"When you use the default self-signed certificates and you connect to the Cluster Manager UI over a web browser, you'll see an untrusted connection notification. Depending on your browser, you can allow the connection for each session or add an exception to trust the certificate for all future sessions."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/security/certificates/monitor-certificates","title":"Monitor certificates","url":"https://redis.io/docs/latest/operate/rs/security/certificates/monitor-certificates/","summary":"Monitor certificates on a Redis Software cluster.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:28+01:00","page_type":"content","content_hash":"cf0d2d7452e488bc653192672cc67a7ef3360a157d1cceb2434e4f8ee8575221","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software exposes the expiration time in seconds of each certificate on each node. You can use Prometheus to monitor these certificates.\n\nTo learn how to monitor Redis Software metrics using Prometheus, see [Monitoring with metrics and alerts](https://redis.io/docs/latest/operate/rs/monitoring) and the [Prometheus integration quick start](https://redis.io/docs/latest/integrate/prometheus-with-redis-enterprise)."},{"id":"v1-metrics","title":"V1 metrics","role":"content","text":"The [v1 metrics](https://redis.io/docs/latest/operate/rs/monitoring/v1_monitoring) endpoint (`https://<cluster_name>:8070/`) uses `node_cert_expiration_seconds`."},{"id":"v1-cert-expire-seconds","title":"Certificate expiration in seconds","role":"content","text":"[code example]\n\nThe following certificates relate to [internode communication TLS encryption](https://redis.io/docs/latest/operate/rs/security/encryption/internode-encryption) and are automatically rotated by Redis Software:\n\n[code example]"},{"id":"v2-metrics","title":"V2 metrics","role":"content","text":"The [v2 metrics](https://redis.io/docs/latest/operate/rs/monitoring/metrics_stream_engine) endpoint (`https://<cluster_name>:8070/v2`) uses `node_cert_expires_in_seconds`."},{"id":"v2-cert-expire-seconds","title":"Certificate expiration in seconds","role":"content","text":"[code example]\n\nThe following certificates relate to [internode communication TLS encryption](https://redis.io/docs/latest/operate/rs/security/encryption/internode-encryption) and are automatically rotated by Redis Software:\n\n[code example]"},{"id":"v2-cert-expire-days","title":"Certificate expiration in days","role":"content","text":"[code example]\n\nThe following certificates relate to [internode communication TLS encryption](https://redis.io/docs/latest/operate/rs/security/encryption/internode-encryption) and are automatically rotated by Redis Software:\n\n[code example]"}],"examples":[{"id":"v1-cert-expire-seconds-ex0","language":"sh","code":"node_cert_expiration_seconds{cluster=\"mycluster.local\",logical_name=\"cm\",node=\"1\",path=\"/etc/opt/redislabs/cm_cert.pem\"} 31104000.0\nnode_cert_expiration_seconds{cluster=\"mycluster.local\",logical_name=\"api\",node=\"1\",path=\"/etc/opt/redislabs/api_cert.pem\"} 31104000.0\nnode_cert_expiration_seconds{cluster=\"mycluster.local\",logical_name=\"proxy\",node=\"1\",path=\"/etc/opt/redislabs/proxy_cert.pem\"} 31104000.0\nnode_cert_expiration_seconds{cluster=\"mycluster.local\",logical_name=\"metrics_exporter\",node=\"1\",path=\"/etc/opt/redislabs/metrics_exporter_cert.pem\"} 31104000.0\nnode_cert_expiration_seconds{cluster=\"mycluster.local\",logical_name=\"syncer\",node=\"1\",path=\"/etc/opt/redislabs/syncer_cert.pem\"} 31104000.0","section_id":"v1-cert-expire-seconds"},{"id":"v1-cert-expire-seconds-ex1","language":"sh","code":"node_cert_expiration_seconds{cluster=\"mycluster.local\",logical_name=\"ccs_internode_encryption\",node=\"1\",path=\"/etc/opt/redislabs/ccs_internode_encryption_cert.pem\"} 2592000.0\nnode_cert_expiration_seconds{cluster=\"mycluster.local\",logical_name=\"data_internode_encryption\",node=\"1\",path=\"/etc/opt/redislabs/data_internode_encryption_cert.pem\"} 2592000.0\nnode_cert_expiration_seconds{cluster=\"mycluster.local\",logical_name=\"mesh_ca_signed\",node=\"1\",path=\"/etc/opt/redislabs/mesh_ca_signed_cert.pem\"} 2592000.0\nnode_cert_expiration_seconds{cluster=\"mycluster.local\",logical_name=\"gossip_ca_signed\",node=\"1\",path=\"/etc/opt/redislabs/gossip_ca_signed_cert.pem\"} 2592000.0","section_id":"v1-cert-expire-seconds"},{"id":"v2-cert-expire-seconds-ex0","language":"sh","code":"node_cert_expires_in_seconds{cluster=\"mycluster.local\",logical_name=\"cm\",node=\"1\",path=\"/etc/opt/redislabs/cm_cert.pem\"} 31104000.0\nnode_cert_expires_in_seconds{cluster=\"mycluster.local\",logical_name=\"api\",node=\"1\",path=\"/etc/opt/redislabs/api_cert.pem\"} 31104000.0\nnode_cert_expires_in_seconds{cluster=\"mycluster.local\",logical_name=\"proxy\",node=\"1\",path=\"/etc/opt/redislabs/proxy_cert.pem\"} 31104000.0\nnode_cert_expires_in_seconds{cluster=\"mycluster.local\",logical_name=\"metrics_exporter\",node=\"1\",path=\"/etc/opt/redislabs/metrics_exporter_cert.pem\"} 31104000.0\nnode_cert_expires_in_seconds{cluster=\"mycluster.local\",logical_name=\"syncer\",node=\"1\",path=\"/etc/opt/redislabs/syncer_cert.pem\"} 31104000.0","section_id":"v2-cert-expire-seconds"},{"id":"v2-cert-expire-seconds-ex1","language":"sh","code":"node_cert_expires_in_seconds{cluster=\"mycluster.local\",logical_name=\"ccs_internode_encryption\",node=\"1\",path=\"/etc/opt/redislabs/ccs_internode_encryption_cert.pem\"} 2592000.0\nnode_cert_expires_in_seconds{cluster=\"mycluster.local\",logical_name=\"data_internode_encryption\",node=\"1\",path=\"/etc/opt/redislabs/data_internode_encryption_cert.pem\"} 2592000.0\nnode_cert_expires_in_seconds{cluster=\"mycluster.local\",logical_name=\"mesh_ca_signed\",node=\"1\",path=\"/etc/opt/redislabs/mesh_ca_signed_cert.pem\"} 2592000.0\nnode_cert_expires_in_seconds{cluster=\"mycluster.local\",logical_name=\"gossip_ca_signed\",node=\"1\",path=\"/etc/opt/redislabs/gossip_ca_signed_cert.pem\"} 2592000.0","section_id":"v2-cert-expire-seconds"},{"id":"v2-cert-expire-days-ex0","language":"sh","code":"max by(filename) (\n  max_over_time(node_cert_expires_in_seconds{cluster=\"$cluster\", filename=~\"api_cert.pem|cm_cert.pem|metrics_exporter_cert.pem|proxy_cert.pem|syncer_cert.pem|trusted_ca.pem\"}[5m])\n) / 86400","section_id":"v2-cert-expire-days"},{"id":"v2-cert-expire-days-ex1","language":"sh","code":"max by(filename) (\n  max_over_time(node_cert_expires_in_seconds{cluster=\"$cluster\", filename=~\"ccs_internode_encryption_cert.pem|data_internode_encryption_cert.pem|mesh_ca_signed_cert.pem|gossip_ca_signed_cert.pem\"}[5m])\n) / 86400","section_id":"v2-cert-expire-days"}]}
{"schema_version":2,"id":"operate/rs/security/certificates/ocsp-stapling","title":"Enable OCSP stapling","url":"https://redis.io/docs/latest/operate/rs/security/certificates/ocsp-stapling/","summary":"Use OCSP stapling to verify certificates maintained by a third-party CA and authenticate connection attempts between clients and servers.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:28+01:00","page_type":"content","content_hash":"d741f47ff5f34bf5793c57d7b267ec1900d766fdad84c655c3bfb26221b1eecf","sections":[{"id":"overview","title":"Overview","role":"overview","text":"OCSP ([Online Certificate Status Protocol](https://en.wikipedia.org/wiki/Online_Certificate_Status_Protocol)) lets a client or server verify the status (`GOOD`, `REVOKED`, or `UNKNOWN`) of a certificate maintained by a third-party [certificate authority (CA)](https://en.wikipedia.org/wiki/Certificate_authority).\n\nTo check whether a certificate is still valid or has been revoked, a client or server can send a request to the CA's OCSP server (also called an OCSP responder). The OCSP responder checks the certificate's status in the CA's [certificate revocation list](https://en.wikipedia.org/wiki/Certificate_revocation_list) and sends the status back as a signed and timestamped response."},{"id":"ocsp-stapling-overview","title":"OCSP stapling overview","role":"content","text":"With OCSP enabled, the Redis Software server regularly polls the CA's OCSP responder for the certificate's status. After it receives the response, the server caches this status until its next polling attempt.\n\n When a client tries to connect to the Redis Software server, they perform a [TLS handshake](https://en.wikipedia.org/wiki/Transport_Layer_Security#TLS_handshake) to authenticate the server and create a secure, encrypted connection. During the TLS handshake, [OCSP stapling](https://en.wikipedia.org/wiki/OCSP_stapling) lets the Redis Software server send (or \"staple\") the cached certificate status to the client.\n\nIf the stapled OCSP response confirms the certificate is still valid, the TLS handshake succeeds and the client connects to the server.\n\nThe TLS handshake fails and the client blocks the connection to the server if the stapled OCSP response indicates either:\n\n- The certificate has been revoked.\n\n- The certificate's status is unknown. This can happen if the OCSP responder fails to send a response."},{"id":"set-up-ocsp-stapling","title":"Set up OCSP stapling","role":"content","text":"You can configure and enable OCSP stapling for your Redis Software cluster with the [Cluster Manager UI](#cluster-manager-ui-method), the [REST API](#rest-api-method), or [`rladmin`](#rladmin-method).\n\nWhile OCSP is enabled, the server always staples the cached OCSP status when a client tries to connect. It is the client's responsibility to use the stapled OCSP status. Some Redis clients, such as [Jedis](https://github.com/redis/jedis) and [redis-py](https://github.com/redis/redis-py), already support OCSP stapling, but others might require additional configuration."},{"id":"cluster-manager-ui-method","title":"Cluster Manager UI method","role":"content","text":"To set up OCSP stapling with the Redis Software Cluster Manager UI:\n\n1. Go to **Cluster > Security > OCSP**.\n\n1. In the **Responder URI** section, select **Replace Certificate** to update the proxy certificate.\n\n1. Provide the key and certificate signed by your third-party CA, then select **Save**.\n\n1. Configure query settings if you don't want to use their default values:\n\n    | Name | Default value | Description |\n    |------|---------------|-------------|\n    | **Query frequency** | 1 hour | The time interval between OCSP queries to the responder URI. |\n    | **Response timeout** | 1 second | The time interval in seconds to wait for a response before timing out. |\n    | **Recovery frequency** | 1 minute | The time interval between retries after a failed query. |\n    | **Recovery maximum tries** | 5 | The number of retries before the validation query fails and invalidates the certificate. |\n\n1. Select **Enable** to turn on OCSP stapling."},{"id":"rest-api-method","title":"REST API method","role":"content","text":"To set up OCSP stapling with the [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api):\n\n1. Use the REST API to [replace the proxy certificate](https://redis.io/docs/latest/operate/rs/security/certificates/updating-certificates#use-the-rest-api) with a certificate signed by your third-party CA.\n\n1. To configure and enable OCSP, send a [`PUT` request to the `/v1/ocsp`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/ocsp#put-ocsp) endpoint and include an [OCSP JSON object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/ocsp) in the request body:\n\n    [code example]"},{"id":"rladmin-method","title":"`rladmin` method","role":"content","text":"To set up OCSP stapling with the [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin) command-line utility:\n\n1. Use [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/certificate) to [replace the proxy certificate](https://redis.io/docs/latest/operate/rs/security/certificates/updating-certificates#use-the-cli) with a certificate signed by your third-party CA.\n\n1. Update the cluster's OCSP settings with the [`rladmin cluster ocsp config`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/ocsp#ocsp-config) command if you don't want to use their default values.\n\n    For example: \n\n    [code example]\n\n1. Enable OCSP:\n\n    [code example]"}],"examples":[{"id":"rest-api-method-ex0","language":"json","code":"{\n        \"ocsp_functionality\": true,\n        \"query_frequency\": 3600,\n        \"response_timeout\": 1,\n        \"recovery_frequency\": 60,\n        \"recovery_max_tries\": 5\n    }","section_id":"rest-api-method"},{"id":"rladmin-method-ex0","language":"sh","code":"rladmin cluster ocsp config recovery_frequency set 30","section_id":"rladmin-method"},{"id":"rladmin-method-ex1","language":"sh","code":"rladmin cluster ocsp config ocsp_functionality set enabled","section_id":"rladmin-method"}]}
{"schema_version":2,"id":"operate/rs/security/certificates/updating-certificates","title":"Update certificates","url":"https://redis.io/docs/latest/operate/rs/security/certificates/updating-certificates/","summary":"Update certificates in a Redis Software cluster.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:27:03+01:00","page_type":"content","content_hash":"e009ad2f0b36ee88b37c829f03129782ef4922b896e8b5767806767db56fe913","sections":[{"id":"overview","title":"Overview","role":"overview","text":"> [!WARNING]\n> When you update the certificates, the new certificate replaces the same certificates on all nodes in the cluster."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"You need to create new certificates before you can update them in the cluster. To create replacement certificates, see [Renew self-signed certificates](https://redis.io/docs/latest/operate/rs/security/certificates/create-certificates#renew-self-signed-certificates) or [Create CA-signed certificates](https://redis.io/docs/latest/operate/rs/security/certificates/create-certificates#create-ca-signed-certificates) for detailed instructions."},{"id":"how-to-update-certificates","title":"How to update certificates","role":"content","text":"You can use the [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin) command-line interface (CLI) or the [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api) to update certificates. The Cluster Manager UI lets you update proxy, syncer, and internode encryption certificates on the **Cluster > Security > Certificates** screen.\n\n**Cluster Manager UI:**\n\nTo replace proxy, syncer, or internode encryption certificates using the Cluster Manager UI:\n\n1. Go to **Cluster > Security > Certificates**.\n\n1. Expand the section for the certificate you want to update:\n    - For internode encryption certificates, expand **Internode encryption certificates**.\n    - For the proxy certificate, expand **Server authentication**.\n    - For the syncer certificate, expand **Replica Of and Active-Active authentication**.\n\n    ![Expanded proxy certificate for server authentication.](https://redis.io/docs/latest/images/rs/screenshots/cluster/security-certs-with-ine-expand-proxy-cert.png)\n\n1. Click **Replace Certificate** to open the dialog.\n\n    ![Replace proxy certificate dialog.](https://redis.io/docs/latest/images/rs/screenshots/cluster/security-replace-proxy-cert.png)\n\n1. Upload the key file.\n\n1. Upload the new certificate.\n\n1. Click **Save**.\n\n**rladmin:**\n\nTo replace certificates with the `rladmin` CLI, run the [`cluster certificate set`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/certificate) command:\n\n[code example]\n\nReplace the following variables with your own values:\n\n- `<cert-name>` - The name of the certificate you want to replace. See the [certificates table](https://redis.io/docs/latest/operate/rs/security/certificates) for the list of valid certificate names.\n- `<cert-file-name>` - The name of your certificate file\n- `<key-file-name>` - The name of your key file\n\nFor example, to replace the Cluster Manager UI (`cm`) certificate with the private key `key.pem` and the certificate file `cluster.pem`:\n\n[code example]\n\n**REST API:**\n\nTo replace a certificate using the REST API, use an [update cluster certificates](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/certificates) request.\n\nFor Redis Software versions 7.22.2 and later, use:\n\n[code example]\n\nFor Redis Software versions 7.22.0 and earlier, use:\n\n[code example]\n\nReplace the following variables with your own values:\n\n- `<cert_name>` - The name of the certificate to replace. See the [certificates table](https://redis.io/docs/latest/operate/rs/security/certificates) for the list of valid certificate names.\n- `<key>` - The contents of the \\*\\_key.pem file\n\n    > [!TIP]\n    >   The key file contains `\\n` end of line characters (EOL) that you cannot paste into the API call.\n    >   You can use `sed -z 's/\\n/\\\\\\n/g'` to escape the EOL characters.\n    >   \n\n- `<cert>` - The contents of the \\*\\_cert.pem file\n\n\n\nNew proxy and syncer certificates are used the next time clients connect to the database. For internode encryption certificates, the new certificates are used after they are replaced on all existing nodes in the cluster.\n\nWhen you add a new node to the cluster, the certificates are automatically copied to the new node.\n\n> [!NOTE]\n> Don't manually overwrite the files located in `/etc/opt/redislabs`. Instead, upload new certificates to a temporary location on one of the cluster nodes, such as the `/tmp` directory."},{"id":"replica-of-database-certificates","title":"Replica Of database certificates","role":"content","text":"This section describes how to update certificates for Replica Of databases."},{"id":"update-ap-proxy-certs","title":"Update proxy certificates","role":"content","text":"To update the proxy certificate on clusters running Replica Of databases:\n\n1. Use the Cluster Manager UI, `rladmin`, or the REST API to update the proxy certificate on the source database cluster.\n\n1. From the Cluster Manager UI, update the destination database (_replica_) configuration with the [new certificate](https://redis.io/docs/latest/operate/rs/databases/import-export/replica-of/create#encrypt-replica-database-traffic).\n\n> [!NOTE]\n> - Perform step 2 as quickly as possible after performing step 1.  Connections using the previous certificate are rejected after applying the new certificate.  Until both steps are performed, recovery of the database sync cannot be established."},{"id":"active-active-database-certificates","title":"Active-Active database certificates","role":"content","text":""},{"id":"update-aa-proxy-certs","title":"Update proxy certificates","role":"content","text":"To update proxy certificate on clusters running Active-Active databases:\n\n1. Use the Cluster Manager UI, `rladmin`, or the REST API to update proxy certificates on a single cluster, multiple clusters, or all participating clusters.\n\n1. Use the [`crdb-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli) utility to update Active-Active database configuration from the command line. Run the following command once for each Active-Active database residing on the modified clusters:\n\n    [code example]\n\n> [!NOTE]\n> - Perform step 2 as quickly as possible after performing step 1.  Connections using the previous certificate are rejected after applying the new certificate.  Until both steps are performed, recovery of the database sync cannot be established.<br/>\n> - Do not run any other `crdb-cli crdb update` operations between the two steps."},{"id":"update-aa-syncer-certs","title":"Update syncer certificates","role":"content","text":"To update your syncer certificate on clusters running Active-Active databases, follow these steps:\n\n1. Update your syncer certificate on one or more of the participating clusters using the Cluster Manager UI, `rladmin`, or the REST API. You can update a single cluster, multiple clusters, or all participating clusters.\n\n1. Update the Active-Active database configuration from the command line with the [`crdb-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli) utility. Run this command once for each Active-Active database that resides on the modified clusters:\n\n    [code example]\n\n> [!NOTE]\n> - Run step 2 as quickly as possible after step 1. Between the two steps, new syncer connections that use the ‘old’ certificate will get rejected by the cluster that has been updated with the new certificate (in step 1).<br/>\n> - Do not run any other `crdb-cli crdb update` operations between the two steps.<br/>"},{"id":"troubleshoot-rhel-8-crypto-policy-and-certificate-key-size","title":"Troubleshoot RHEL 8 crypto policy and certificate key size","role":"errors","text":"In RHEL 8, if the crypto policy is set to `FUTURE`, the system will not accept certificates with private key sizes smaller than 3072 bits. This affects the use of custom certificates with smaller keys (such as 2048-bit keys).\n\nTo use certificates with smaller key sizes, you need to change the crypto policy from `FUTURE` to `DEFAULT`. For more information about crypto policies, see the [Red Hat documentation on system-wide cryptographic policies](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/security_hardening/using-the-system-wide-cryptographic-policies_security-hardening)."}],"examples":[{"id":"how-to-update-certificates-ex0","language":"sh","code":"rladmin cluster certificate set <cert-name> certificate_file <cert-file-name>.pem key_file <key-file-name>.pem","section_id":"how-to-update-certificates"},{"id":"how-to-update-certificates-ex1","language":"sh","code":"rladmin cluster certificate set cm certificate_file cluster.pem key_file key.pem","section_id":"how-to-update-certificates"},{"id":"how-to-update-certificates-ex2","language":"sh","code":"PUT https://<host>:<port>/v1/cluster/certificates\n{\n  \"certificates\": [\n    {\n      \"name\": \"<cert_name>\",\n      \"certificate\": \"<cert>\",\n      \"key\": \"<key>\"\n    }\n  ]\n}","section_id":"how-to-update-certificates"},{"id":"how-to-update-certificates-ex3","language":"sh","code":"PUT https://<host>:<port>/v1/cluster/update_cert\n    '{ \"name\": \"<cert_name>\", \"key\": \"<key>\", \"certificate\": \"<cert>\" }'","section_id":"how-to-update-certificates"},{"id":"update-aa-proxy-certs-ex0","language":"sh","code":"crdb-cli crdb update --crdb-guid <CRDB-GUID> --force","section_id":"update-aa-proxy-certs"},{"id":"update-aa-syncer-certs-ex0","language":"sh","code":"crdb-cli crdb update --crdb-guid <CRDB-GUID> --force","section_id":"update-aa-syncer-certs"}]}
{"schema_version":2,"id":"operate/rs/security/encryption","title":"Encryption in Redis Software","url":"https://redis.io/docs/latest/operate/rs/security/encryption/","summary":"Encryption in Redis Software.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:28+01:00","children":[{"id":"operate/rs/security/encryption/tls","summary":"An overview of Transport Layer Security (TLS).","title":"Transport Layer Security (TLS)","url":"https://redis.io/docs/latest/operate/rs/security/encryption/tls/"},{"id":"operate/rs/security/encryption/internode-encryption","summary":"Describes internode encryption which improves the security of data in transit.","title":"Internode encryption","url":"https://redis.io/docs/latest/operate/rs/security/encryption/internode-encryption/"},{"id":"operate/rs/security/encryption/pem-encryption","summary":"Enable PEM encryption to encrypt all private keys on disk.","title":"Encrypt private keys","url":"https://redis.io/docs/latest/operate/rs/security/encryption/pem-encryption/"}],"page_type":"content","content_hash":"e22486ddb7290e534ca8f465c30ae37612bf173d2c313572144de404b8386fc0","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software uses encryption to secure communications between clusters, nodes, databases, and clients and to protect [data in transit](https://en.wikipedia.org/wiki/Data_in_transit), [at rest](https://en.wikipedia.org/wiki/Data_at_rest), and [in use](https://en.wikipedia.org/wiki/Data_in_use)."},{"id":"encrypt-data-in-transit","title":"Encrypt data in transit","role":"content","text":""},{"id":"tls","title":"TLS","role":"content","text":"Redis Software uses [Transport Layer Security (TLS)](https://redis.io/docs/latest/operate/rs/security/encryption/tls) to encrypt communications for the following:\n\n- Cluster Manager UI\n\n- Command-line utilities\n\n- REST API\n\n- Internode communication\n\nYou can also [enable TLS authentication](https://redis.io/docs/latest/operate/rs/security/encryption/tls/enable-tls) for the following:\n\n- Communication from clients or applications to your database\n\n- Communication from your database to other clusters for replication using [Replica Of](https://redis.io/docs/latest/operate/rs/databases/import-export/replica-of)\n\n- Communication to and from your database to other clusters for [Active-Active](https://redis.io/docs/latest/operate/rs/databases/active-active) synchronization"},{"id":"internode-encryption","title":"Internode encryption","role":"content","text":"[Internode encryption](https://redis.io/docs/latest/operate/rs/security/encryption/internode-encryption) uses TLS to encrypt data in transit between cluster nodes.\n\nBy default, internode encryption is enabled for the control plane, which manages the cluster and databases. If you also want to encrypt replication and proxy communications between database shards on different nodes, [enable data internode encryption](https://redis.io/docs/latest/operate/rs/security/encryption/internode-encryption#enable-data-internode-encryption)."},{"id":"require-https-for-rest-api-endpoints","title":"Require HTTPS for REST API endpoints","role":"content","text":"By default, the Redis Software API supports communication over HTTP and HTTPS. However, you can [turn off HTTP support](https://redis.io/docs/latest/operate/rs/references/rest-api/encryption) to ensure that API requests are encrypted."},{"id":"encrypt-data-at-rest","title":"Encrypt data at rest","role":"content","text":""},{"id":"file-system-encryption","title":"File system encryption","role":"content","text":"To encrypt data stored on disk, use file system-based encryption capabilities available on Linux operating systems before you install Redis Software."},{"id":"private-key-encryption","title":"Private key encryption","role":"content","text":"Enable PEM encryption to [encrypt all private keys](https://redis.io/docs/latest/operate/rs/security/encryption/pem-encryption) on disk."},{"id":"encrypt-data-in-use","title":"Encrypt data in use","role":"content","text":""},{"id":"client-side-encryption","title":"Client-side encryption","role":"content","text":"Use client-side encryption to encrypt the data an application stores in a Redis database. The application decrypts the data when it retrieves it from the database.\n\nYou can add client-side encryption logic to your application or use built-in client functions.\n\nClient-side encryption has the following limitations:\n\n- Operations that must operate on the data, such as increments, comparisons, and searches will not function properly.\n\n- Increases management overhead.\n\n- Reduces performance."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/security/encryption/internode-encryption","title":"Internode encryption","url":"https://redis.io/docs/latest/operate/rs/security/encryption/internode-encryption/","summary":"Describes internode encryption which improves the security of data in transit.","tags":["docs","operate","rs"],"last_updated":"2026-10-02T16:06:07+01:00","page_type":"content","content_hash":"afb5413832dd91699a5ab79300ce05426f0603fcd3b00316dec5a8ab0dfc7ada","sections":[{"id":"overview","title":"Overview","role":"overview","text":"As of v6.2.4, Redis Software supports _internode encryption_, which encrypts internal communication between nodes. This improves the security of data as it travels within a cluster.\n\nInternode encryption is enabled for the _control plane_, which manages the cluster and its databases.\n\nInternode encryption is supported for the _data plane_, which encrypts communication used to replicate shards between nodes and proxy communication with shards located on different nodes.\n\nThe following diagram shows how this works.\n\n![A diagram showing the interaction between data internode encryption, control plane encryption, and various elements of a cluster.](https://redis.io/docs/latest/images/rs/internode-encryption.png)\n\nData internode encryption is disabled by default for individual databases in order to optimize for performance.  Encryption adds latency and overhead; the impact is measurable and varies according to the database, its field types, and the details of the underlying use case. \n\nYou can enable data internode encryption for a database by changing the database configuration settings.  This lets you choose when to favor performance and when to encrypt data."},{"id":"prerequisites","title":"Prerequisites","role":"content","text":"Internode encryption requires certain prerequisites.  \n\nYou need to:\n\n- Upgrade all nodes in the cluster to v6.2.4 or later.\n\n- Open port 3342 for the TLS channel used for encrypted communication."},{"id":"enable-data-internode-encryption","title":"Enable data internode encryption","role":"content","text":"To enable internode encryption for a database (also called _data internode encryption_), you need to enable the appropriate setting for each database you wish to encrypt.  To do so, you can:\n\n- Use the Cluster Manager UI to enable the **Internode Encryption** setting from the database **Security** screen.\n\n-  Use the `rladmin` command-line utility to set the [data_internode_encryption](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-db) setting for the database:\n\n    [code example] \n\n- Use the Redis Software REST API to set the `data_internode_encryption` setting for the database.\n\n    [code example]\n\nWhen you change the data internode encryption setting for a database, all active remote client connections are disconnected.  This restarts the internal (DMC) proxy and disconnects all client connections."},{"id":"change-cluster-policy","title":"Change cluster policy","role":"content","text":"To enable internode encryption for new databases by default, use one of the following methods:\n\n- Cluster Manager UI\n\n    1. On the **Databases** screen, select ![Toggle actions button](https://redis.io/docs/latest/images/rs/buttons/button-toggle-actions-vertical.png#no-click) to open a list of additional actions.\n\n    1. Select **Database defaults**.\n\n    1. Go to **Internode Encryption** and click **Change**.\n\n    1. Select **Enabled** to enable internode encryption for new databases by default.\n\n    1. Click **Change**.\n\n    1. Select **Save**.\n\n- [rladmin tune cluster](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-cluster): \n    \n    [code example]\n\n- [Update cluster policy](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/policy#put-cluster-policy) REST API request:\n\n    [code example]"},{"id":"self-signed-certificates","title":"Self-signed certificates","role":"content","text":"By default, Redis Software uses self-signed certificates for internode encryption. These certificates are automatically generated, managed, and rotated by the system."},{"id":"encryption-ciphers-and-settings","title":"Encryption ciphers and settings","role":"content","text":"To encrypt internode communications, Redis Software uses TLS 1.2 and the following cipher suites:\n\n- ECDHE-RSA-AES256-GCM-SHA384\n- ECDHE-RSA-AES128-GCM-SHA256\n\nAs of Redis Software v7.4, internode encryption also supports TLS 1.3 with the following cipher suites:\n\n- TLS_AES_128_GCM_SHA256\n- TLS_AES_256_GCM_SHA384\n\nThe TLS layer determines which TLS version to use.\n\nNo configurable settings are exposed; internode encryption is used internally within a cluster and not exposed to any outside service."},{"id":"certificate-authority-and-rotation","title":"Certificate authority and rotation","role":"content","text":"Starting with v6.2.4, internode communication is managed, in part, by two certificates: one for the control plane and one for the data plane.  These certificates are signed by a private certificate authority (CA).  The CA is not exposed outside of the cluster, so it cannot be accessed by external processes or services.  In addition, each cluster generates a unique CA that is not used anywhere else.\n\nThe private CA is generated when a cluster is created or upgraded to 6.2.4.  \n\nWhen nodes join the cluster, the cluster CA is used to generate certificates for the new node, one for each plane.  Certificates signed by the private CA are not shared between clusters and they're not exposed outside the cluster.\n\nAll certificates signed by the internal CA expire after ninety (90) days and automatically rotate every thirty (30) days.  Alerts also monitor certificate expiration and trigger when certificate expiration falls below 45 days.  If you receive such an alert, contact support.\n\nYou can use the Redis Software REST API to rotate certificates manually:\n\n[code example]"},{"id":"customer-provided-certificates","title":"Customer-provided certificates","role":"content","text":"Instead of using Redis Software's self-signed certificates for internode encryption, you can provide certificates generated by your Certificate Authority (CA)."},{"id":"certificate-requirements","title":"Certificate requirements","role":"content","text":"Customer-provided internode encryption certificates must meet the following requirements:\n\n- Certificates must be in PEM format.\n\n- Certificates must contain the certificate chain and the leaf certificate.\n\n- Certificates must use an RSA key. Using other key types, such as ECDSA or EdDSA, might prevent internode encryption from working.\n\n- Certificates must be created with the TLS Web Client Authentication attribute enabled."},{"id":"upload-customer-provided-certificates","title":"Upload customer-provided certificates","role":"content","text":"You can upload either:\n\n- One certificate for both data plane internode encryption (DPINE) and control plane internode encryption (CPINE).\n\n- Two separate certificates: one for DPINE and one for CPINE.\n\nTo upload internode encryption certificates, use one of the following methods:\n\n**Cluster Manager UI:**\n\n1. In the Cluster Manager UI, go to **Cluster > Security > Certificates**.\n\n1. Expand the **Internode encryption certificates** section.\n\n1. Click **Replace certificates**.\n\n1. In the **Internode encryption certificates** panel, select one of the following options:\n\n    - Use the same certificate for data and control plane internode encryption\n\n    - Use separate certificates for data and control plane internode encryption\n\n1. For each certificate and key, click **Upload** and use the file browser to select the relevant PEM file.\n\n1. Click **Save & Rotate**.\n\n**rladmin:**\n\nTo use the same certificate for data and control plane internode encryption, run the following [`rladmin cluster certificate`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/certificate#cluster-certificate-set-internal) command:\n\n[code example]\n\nTo use separate certificates for data and control plane internode encryption, run the following [`rladmin cluster certificate`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/certificate#cluster-certificate-set-internal) command:\n\n[code example]\n\n**REST API:**\n\nTo use the same certificate for data and control plane internode encryption, send a [`PUT /v1/cluster/certificates`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/certificates#put-cluster-certificates) request:\n\n[code example]\n\nTo use separate certificates for data and control plane internode encryption, send a [`PUT /v1/cluster/certificates`](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster/certificates#put-cluster-certificates) request:\n\n[code example]"},{"id":"certificate-expiration-and-rotation","title":"Certificate expiration and rotation","role":"content","text":"Customer-provided internode encryption certificates are not rotated automatically. To prevent possible issues with cluster and database unavailability caused by expired internode encryption certificates:\n\n- You should monitor the certificates' expiration dates and [renew them](https://redis.io/docs/latest/operate/rs/security/certificates/updating-certificates#how-to-update-certificates) before they expire.\n\n- In case you do not renew the customer-managed certificates before they expire, Redis Software automatically replaces them with self-signed certificates 5 days before expiration. You can upload new customer-provided certificates to replace these certificates anytime."},{"id":"performance-recommendations","title":"Performance recommendations","role":"performance","text":"For optimal performance with customer-provided certificates, follow the same recommendations as for self-signed certificates detailed in the [Encryption ciphers and settings](#encryption-ciphers-and-settings) section."}],"examples":[{"id":"enable-data-internode-encryption-ex0","language":" shell","code":"rladmin tune db <database_id> data_internode_encryption enabled","section_id":"enable-data-internode-encryption"},{"id":"enable-data-internode-encryption-ex1","language":" rest","code":"put /v1/bdbs/${database_id}\n    { \"data_internode_encryption\" : true }","section_id":"enable-data-internode-encryption"},{"id":"change-cluster-policy-ex0","language":"sh","code":"rladmin tune cluster data_internode_encryption enabled","section_id":"change-cluster-policy"},{"id":"change-cluster-policy-ex1","language":"sh","code":"PUT /v1/cluster/policy \n    { \"data_internode_encryption\": true }","section_id":"change-cluster-policy"},{"id":"certificate-authority-and-rotation-ex0","language":" rest","code":"POST /v1/cluster/certificates/rotate","section_id":"certificate-authority-and-rotation"},{"id":"upload-customer-provided-certificates-ex0","language":"sh","code":"rladmin cluster certificate set internal dpine_certificate_file <path-to-shared-cert> dpine_key_file <path-to-shared-key> cpine_certificate_file <path-to-shared-cert> cpine_key_file <path-to-shared-key>","section_id":"upload-customer-provided-certificates"},{"id":"upload-customer-provided-certificates-ex1","language":"sh","code":"rladmin cluster certificate set internal dpine_certificate_file <path-to-dpine-cert> dpine_key_file <path-to-dpine-key> cpine_certificate_file <path-to-cpine-cert> cpine_key_file <path-to-cpine-key>","section_id":"upload-customer-provided-certificates"},{"id":"upload-customer-provided-certificates-ex2","language":"sh","code":"PUT https://<host>:<port>/v1/cluster/certificates\n{\n  \"certificates\": [\n    {\n      \"name\": \"ccs_internode_encryption\",\n      \"key\": \"<shared-private-key>\",\n      \"certificate\": \"<shared-certificate-chain>\"\n    },\n    {\n      \"name\": \"data_internode_encryption\",\n      \"key\": \"<shared-private-key>\",\n      \"certificate\": \"<shared-certificate-chain>\"\n    }\n  ]\n}","section_id":"upload-customer-provided-certificates"},{"id":"upload-customer-provided-certificates-ex3","language":"sh","code":"PUT https://<host>:<port>/v1/cluster/certificates\n{\n  \"certificates\": [\n    {\n      \"name\": \"ccs_internode_encryption\",\n      \"key\": \"<cpine-private-key>\",\n      \"certificate\": \"<cpine-certificate-chain>\"\n    },\n    {\n      \"name\": \"data_internode_encryption\",\n      \"key\": \"<dpine-private-key>\",\n      \"certificate\": \"<dpine-certificate-chain>\"\n    }\n  ]\n}","section_id":"upload-customer-provided-certificates"}]}
{"schema_version":2,"id":"operate/rs/security/encryption/pem-encryption","title":"Encrypt private keys","url":"https://redis.io/docs/latest/operate/rs/security/encryption/pem-encryption/","summary":"Enable PEM encryption to encrypt all private keys on disk.","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:28+01:00","page_type":"content","content_hash":"84b155658d667f63d334a04d79ec6a34b350c40c772d513b499875eacffdc708","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Enable PEM encryption to automatically encrypt all private keys on disk. Public keys (`.cert` files) are not encrypted.\n\nWhen certificates are rotated, the encrypted private keys are also rotated."},{"id":"enable-pem-encryption","title":"Enable PEM encryption","role":"content","text":"To enable PEM encryption and encrypt private keys on the disk, use [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin) or the [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api).\n\n\n- [`rladmin cluster config`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/config):\n\n    [code example]\n\n- [Update cluster settings](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster#put-cluster) REST API request:\n\n    [code example]"},{"id":"deactivate-pem-encryption","title":"Deactivate PEM encryption","role":"content","text":"To deactivate PEM encryption and decrypt private keys on the disk, use [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin) or the [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api).\n\n- [`rladmin cluster config`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/config):\n\n    [code example]\n\n- [Update cluster settings](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster#put-cluster) REST API request:\n\n    [code example]"}],"examples":[{"id":"enable-pem-encryption-ex0","language":"sh","code":"rladmin cluster config encrypt_pkeys enabled","section_id":"enable-pem-encryption"},{"id":"enable-pem-encryption-ex1","language":"sh","code":"PUT /v1/cluster\n    { \"encrypt_pkeys\": true }","section_id":"enable-pem-encryption"},{"id":"deactivate-pem-encryption-ex0","language":"sh","code":"rladmin cluster config encrypt_pkeys disabled","section_id":"deactivate-pem-encryption"},{"id":"deactivate-pem-encryption-ex1","language":"sh","code":"PUT /v1/cluster\n    { \"encrypt_pkeys\": false }","section_id":"deactivate-pem-encryption"}]}
{"schema_version":2,"id":"operate/rs/security/encryption/tls/ciphers","title":"Configure cipher suites","url":"https://redis.io/docs/latest/operate/rs/security/encryption/tls/ciphers/","summary":"Shows how to configure cipher suites.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"ba9bc2511ec3d09d662034c93e0e93b59be6dc404bc7395ed3d075a036e2ab48","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Ciphers are algorithms that help secure connections between clients and servers. You can change the ciphers to improve the security of your Redis Software cluster and databases. The default settings are in line with industry best practices, but you can customize them to match the security policy of your organization."},{"id":"tls-12-cipher-suites","title":"TLS 1.2 cipher suites","role":"content","text":"| Name | Configurable | Description |\n|------------|--------------|-------------|\n| control_cipher_suites | <span title=\"Yes\">&#x2705; Yes</span> | Cipher list for TLS 1.2 communications for cluster administration (control plane) |\n| data_cipher_list | <span title=\"Yes\">&#x2705; Yes</span> | Cipher list for TLS 1.2 communications between applications and databases (data plane) |\n| sentinel_cipher_suites | <span title=\"Yes\">&#x2705; Yes</span> | Cipher list for [discovery service](https://redis.io/docs/latest/operate/rs/databases/durability-ha/discovery-service) (Sentinel) TLS 1.2 communications |"},{"id":"tls-13-cipher-suites","title":"TLS 1.3 cipher suites","role":"content","text":"| Name | Configurable | Description |\n|------------|--------------|-------------|\n| control_cipher_suites_tls_1_3 | <span title=\"No\">&#x274c; No</span> | Cipher list for TLS 1.3 communications for cluster administration (control plane) |\n| data_cipher_suites_tls_1_3 | <span title=\"Yes\">&#x2705; Yes</span> | Cipher list for TLS 1.3 communications between applications and databases (data plane) |\n| sentinel_cipher_suites_tls_1_3 | <span title=\"No\">&#x274c; No</span> | Cipher list for [discovery service](https://redis.io/docs/latest/operate/rs/databases/durability-ha/discovery-service) (Sentinel) TLS 1.3 communications |"},{"id":"configure-cipher-suites","title":"Configure cipher suites","role":"configuration","text":"You can configure ciphers with the [Cluster Manager UI](#edit-ciphers-ui), [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/config), or the [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster#put-cluster).\n\n> [!WARNING]\n> Configuring cipher suites overwrites existing ciphers rather than appending new ciphers to the list.\n\nWhen you modify your cipher suites, make sure:\n\n- The configured TLS version matches the required cipher suites.\n- The certificates in use are properly signed to support the required cipher suites.\n\n> [!NOTE]\n> - Redis Software doesn't support static [Diffie–Hellman (`DH`) key exchange](https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange) ciphers.\n>\n> - Support for Ephemeral Diffie–Hellman (ECDHE) key exchange ciphers depends on the operating system version and security policy."},{"id":"edit-ciphers-ui","title":"Edit cipher suites in the UI","role":"content","text":"To configure cipher suites using the Cluster Manager UI:\n\n1. Go to **Cluster > Security**, then select the **TLS** tab.\n\n1. In the **Cipher suites lists** section, click **Configure**:\n\n    ![Cipher suites lists as shown in the Cluster Manager UI.](https://redis.io/docs/latest/images/rs/screenshots/cluster/security-tls-cipher-suites-view.png)\n\n1. Edit the TLS cipher suites in the text boxes:\n\n    ![Edit cipher suites drawer in the Cluster Manager UI.](https://redis.io/docs/latest/images/rs/screenshots/cluster/security-tls-cipher-suites-edit.png)\n\n1. Click **Save**."},{"id":"control-plane-ciphers-tls-1-2","title":"Control plane cipher suites","role":"content","text":"As of Redis Software version 6.0.12, control plane cipher suites can use the BoringSSL library format for TLS connections to the Cluster Manager UI. See the BoringSSL documentation for a full list of available [BoringSSL configurations](https://github.com/google/boringssl/blob/887d517832ccd678abf4216531786ca675928629/ssl/test/runner/cipher_suites.go#L99-L130).\n\n#### Configure TLS 1.2 control plane cipher suites\n\nTo configure TLS 1.2 cipher suites for cluster communication, use the following [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin) command syntax:\n\n[code example]\n\nSee the example below to configure cipher suites for the control plane:\n\n[code example]\n> [!NOTE]\n> - The deprecated 3DES and RC4 cipher suites are no longer supported."},{"id":"data-plane-ciphers-tls-1-2","title":"Data plane cipher suites","role":"content","text":"Data plane cipher suites use the OpenSSL library format in Redis Software version 6.0.20 or later. For a list of available OpenSSL configurations, see [Ciphers](https://www.openssl.org/docs/man1.1.1/man1/ciphers.html) (OpenSSL).\n\n#### Configure TLS 1.2 data plane cipher suites\n\nTo configure TLS 1.2 cipher suites for communications between applications and databases, use the following [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin) command syntax:\n\n[code example]\n\nSee the example below to configure cipher suites for the data plane:\n\n[code example]\n> [!NOTE]\n> - The deprecated 3DES and RC4 cipher suites are no longer supported.\n\n#### Configure TLS 1.3 data plane cipher suites\n\nTo configure TLS 1.3 cipher suites for communications between applications and databases, use the following [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin) command syntax:\n\n[code example]\n\nThe following example configures TLS 1.3 cipher suites for the data plane:\n\n[code example]"},{"id":"discovery-service-ciphers-tls-1-2","title":"Discovery service cipher suites","role":"content","text":"Sentinel service cipher suites use the golang.org OpenSSL format for [discovery service](https://redis.io/docs/latest/operate/rs/databases/durability-ha/discovery-service) TLS connections in Redis Software version 6.0.20 or later. See their documentation for a list of [available configurations](https://golang.org/src/crypto/tls/cipher_suites.go).\n\n#### Configure TLS 1.2 discovery service cipher suites\n\nTo configure TLS 1.2 cipher suites for the discovery service cipher suites, use the following [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin) command syntax:\n\n[code example]\n\nSee the example below to configure cipher suites for the sentinel service:\n\n[code example]"}],"examples":[{"id":"control-plane-ciphers-tls-1-2-ex0","language":"sh","code":"rladmin cluster config control_cipher_suites <BoringSSL cipher list>","section_id":"control-plane-ciphers-tls-1-2"},{"id":"control-plane-ciphers-tls-1-2-ex1","language":"sh","code":"rladmin cluster config control_cipher_suites ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305","section_id":"control-plane-ciphers-tls-1-2"},{"id":"data-plane-ciphers-tls-1-2-ex0","language":"sh","code":"rladmin cluster config  data_cipher_list <OpenSSL cipher list>","section_id":"data-plane-ciphers-tls-1-2"},{"id":"data-plane-ciphers-tls-1-2-ex1","language":"sh","code":"rladmin cluster config data_cipher_list AES128-SHA:AES256-SHA","section_id":"data-plane-ciphers-tls-1-2"},{"id":"data-plane-ciphers-tls-1-2-ex2","language":"sh","code":"rladmin cluster config data_cipher_suites_tls_1_3 <OpenSSL cipher list>","section_id":"data-plane-ciphers-tls-1-2"},{"id":"data-plane-ciphers-tls-1-2-ex3","language":"sh","code":"rladmin cluster config data_cipher_suites_tls_1_3 TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256","section_id":"data-plane-ciphers-tls-1-2"},{"id":"discovery-service-ciphers-tls-1-2-ex0","language":"sh","code":"rladmin cluster config  sentinel_cipher_suites <golang cipher list>","section_id":"discovery-service-ciphers-tls-1-2"},{"id":"discovery-service-ciphers-tls-1-2-ex1","language":"sh","code":"rladmin cluster config sentinel_cipher_suites TLS_RSA_WITH_AES_128_CBC_SHA:TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384","section_id":"discovery-service-ciphers-tls-1-2"}]}
{"schema_version":2,"id":"operate/rs/security/encryption/tls/enable-tls","title":"Enable TLS","url":"https://redis.io/docs/latest/operate/rs/security/encryption/tls/enable-tls/","summary":"Shows how to enable TLS.","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"9ae072ecb4150e45169b1fe9444e6c846922be5d200cadaaabea7e38a6b04f39","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can use TLS authentication for one or more of the following types of communication:\n\n- Communication from clients (applications) to your database\n- Communication from your database to other clusters for replication using [Replica Of](https://redis.io/docs/latest/operate/rs/databases/import-export/replica-of)\n- Communication to and from your database to other clusters for synchronization using [Active-Active](https://redis.io/docs/latest/operate/rs/databases/active-active)\n\n> [!NOTE]\n> When you enable or turn off TLS, the change applies to new connections but does not affect existing connections. You must update TLS parameters in the client's connection configuration, then clients must close existing connections and reconnect to apply the change."},{"id":"client","title":"Enable TLS for client connections","role":"content","text":"**Cluster Manager UI:**\n\nTo enable TLS for client connections using the Cluster Manager UI:\n\n1. From your database's **Security** tab, select **Edit**.\n\n1. Expand the **TLS - Transport Layer Security for secure connections** section, then select **On**.\n\n1. In the **Apply TLS for** section, select **Clients and databases + Between databases**.\n\n1. Select **Save**.\n\n**REST API:**\n\nYou can also enable TLS for client connections using the REST API.\n\nTo enable TLS for client connections during database creation, include `\"tls_mode\": \"enabled\"` when you [create a database](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#post-bdbs-v1):\n\n[code example]\n\nFor additional database configuration fields, see the [BDB object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb) reference.\n\nTo enable TLS for client connections after database creation, you can use an [update database configuration](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#put-bdbs) REST API request:\n\n[code example]"},{"id":"enable-mutual-tls","title":"Enable mutual TLS","role":"content","text":"Optionally, you can enable mutual TLS for client connections.\n\n**Cluster Manager UI:**\n\nTo enable mutual TLS using the Cluster Manager UI:\n\n1. Select **Mutual TLS (Client authentication)**.\n\n    ![Mutual TLS authentication configuration.](https://redis.io/docs/latest/images/rs/screenshots/databases/security-mtls-clients-7-8-2.png)\n\n1. For all clients, upload a client certificate, an intermediary certificate, or a trusted client CA certificate.\n\n    1. Click **+ Add certificate**.\n\n    1. Paste or upload the client certificate, intermediary certificate, or trusted client CA certificate.\n\n    1. Click **Done**.\n\n    If your database uses Replica Of, you also need to add the syncer certificates for the participating clusters. See [Enable TLS for Replica Of cluster connections](#enable-tls-for-replica-of-cluster-connections) for instructions.\n\n1. You can configure **Additional certificate validations** to further limit connections to clients with valid certificates.\n\n    Additional certificate validations occur only when loading a [certificate chain](https://en.wikipedia.org/wiki/Chain_of_trust#Computer_security) that includes the [root certificate](https://en.wikipedia.org/wiki/Root_certificate) and intermediate [CA](https://en.wikipedia.org/wiki/Certificate_authority) certificate but does not include a leaf (end-entity) certificate. If you include a leaf certificate, mutual client authentication skips any additional certificate validations.\n\n    1. Select a certificate validation option.\n\n        | Validation option | Description |\n        |-------------------|-------------|\n        | _No validation_ | Authenticates clients with valid certificates. No additional validations are enforced. |\n        | _By Subject Alternative Name_ | A client certificate is valid only if its Subject Alternative Name (SAN) DNS entries or Common Name (CN) match an entry in the list of valid subjects. Ignores other [`Subject`](https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.6) attributes. |\n        | _By full Subject Name_ | A client certificate is valid only if its [`Subject`](https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.6) attributes match an entry in the list of valid subjects. |\n\n    1. If you selected **No validation**, you can skip this step. Otherwise, select **+ Add validation** to create a new entry and then enter valid [`Subject`](https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.6) attributes for your client certificates. All `Subject` attributes are case-sensitive.\n\n        | Subject attribute<br />(case-sensitive) | Description |\n        |-------------------|-------------|\n        | _Common Name (CN)_ | Name of the client authenticated by the certificate (_required_) |\n        | _Organization (O)_ | The client's organization or company name |\n        | <nobr>_Organizational Unit (OU)_</nobr> | Name of the unit or department within the organization |\n        | _Locality (L)_ | The organization's city |\n        | _State / Province (ST)_ | The organization's state or province |\n        | _Country (C)_ | 2-letter code that represents the organization's country |\n\n        You can only enter a single value for each field, except for the _Organizational Unit (OU)_ field. If your client certificate has a `Subject` with multiple  _Organizational Unit (OU)_ values, press the `Enter` or `Return` key after entering each value to add multiple Organizational Units.\n\n        ![An example that shows adding a certificate validation with multiple organizational units.](https://redis.io/docs/latest/images/rs/screenshots/databases/security-mtls-add-cert-validation-multi-ou.png)\n\n        **Breaking change:** If you use the [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api) instead of the Cluster Manager UI to configure additional certificate validations, note that `authorized_names` is deprecated as of Redis Software v6.4.2. Use `authorized_subjects` instead. See the [BDB object reference](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb) for more details.\n\n1. Select **Save**.\n\n**REST API:**\n\nYou can also enable mutual TLS using the REST API.\n\nTo enable mutual TLS during database creation, include the following fields when you [create a database](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#post-bdbs-v1):\n\n[code example]\n\nFor additional database configuration fields, see the [BDB object](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/bdb) reference.\n\nTo enable mutual TLS after database creation, you can use an [update database configuration](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#put-bdbs) REST API request:\n\n[code example]"},{"id":"validate-client-certificate-expiration","title":"Validate client certificate expiration","role":"content","text":"By default, Redis Software validates client certificate expiration dates.  You can use [`rladmin tune db`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/tune#tune-db) to turn off this behavior.\n\n[code example]"},{"id":"connect-over-tls","title":"Connect over TLS","role":"content","text":"To connect to a Redis Software database over TLS using [`redis-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/redis-cli):\n\n1. Download or copy the server (or proxy) certificate from the Cluster Manager UI (**Cluster > Security > Certificates > Server authentication**) or from a cluster node (`/etc/opt/redislabs/proxy_cert.pem`).\n\n1. Copy the certificate to each client machine.\n\n1. If your database doesn't require client authentication with mutual TLS, provide the server certificate when you connect:\n\n    [code example]\n\n1. If your database requires client authentication with mutual TLS, provide your client's private and public keys along with the Redis Software server certificate when you connect:\n\n    [code example]"},{"id":"enable-tls-for-active-active-cluster-connections","title":"Enable TLS for Active-Active cluster connections","role":"content","text":"You can enable TLS for Active-Active cluster connections when you create a database using the Cluster Manager UI, [`crdb-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli), or the [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api).\n\nIf you need to enable or turn off TLS after the Active-Active database is created, you must use [`crdb-cli`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli) or the [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api)."},{"id":"enable-tls-during-database-creation","title":"Enable TLS during database creation","role":"content","text":"To enable TLS for Active-Active cluster connections using the Cluster Manager UI:\n\n1. During [database creation](https://redis.io/docs/latest/operate/rs/databases/active-active/create), expand the **TLS** configuration section.\n\n1. Select **On** to enable TLS.\n\n    ![TLS is enabled on the Cluster Manager UI screen.](https://redis.io/docs/latest/images/rs/screenshots/databases/active-active-databases/enable-tls-for-active-active-db.png)\n\n1. Click **Create**.\n\nIf you also want to require TLS for client connections during creation, you can use the [create an Active-Active database](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/crdbs#post-crdb) REST API request to create an Active-Active database with TLS enabled for client connections.\nYou should configure the client certificates individually for each instance instead of using the default database configuration, even if the same certificate is used across all instances.\nThis allows you to rotate the certificates independently for each instance and to avoid outages in case of certificate expiration.\n\n[code example]"},{"id":"enable-tls-after-database-creation","title":"Enable TLS after database creation","role":"content","text":"You can enable TLS for an existing Active-Active database using either `crdb-cli` or the REST API.\n\n**CLI:**\n\nRun the following [`crdb-cli crdb update`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/crdb-cli/crdb/update) command:\n\n[code example]\n\nReplace `<guid>` with your Active-Active database's globally unique identifier.\n\n**REST API:**\n\nYou can use an [update database configuration](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/bdbs#put-bdbs) request to enable TLS.\n\nTo enable TLS for Active-Active database communications only:\n\n[code example]\n\nTo enable TLS for all communications:\n\n[code example]"},{"id":"enable-tls-for-replica-of-cluster-connections","title":"Enable TLS for Replica Of cluster connections","role":"content","text":"To enable TLS for Replica Of cluster connections:\n\n1. For each cluster hosting a replica:\n\n    1. Go to **Cluster > Security > Certificates**.\n\n    1. Expand the **Replica Of and Active-Active authentication (Syncer certificate)** section.\n\n        ![Syncer certificate for Replica Of and Active-Active authentication.](https://redis.io/docs/latest/images/rs/screenshots/cluster/security-syncer-cert.png)\n    \n    1. Download or copy the syncer certificate.\n\n1. From the **Security** tab of the Replica Of source database, select **Edit**.\n\n1. In the **TLS - Transport Layer Security for secure connections** section, make sure the checkbox is selected.\n\n1. In the **Apply TLS for** section, select **Between databases only**.\n\n1. Select **Mutual TLS (Client authentication)**.\n\n    ![Replica Of TLS authentication configuration.](https://redis.io/docs/latest/images/rs/screenshots/databases/security-tls-replica-of.png)\n\n1. Select **+ Add certificate**, paste or upload the syncer certificate, then select **Done**.\n\n    Repeat this process, adding the syncer certificate for each cluster hosting a replica of this database.\n\n1. _(Optional)_ To require TLS for client connections, change **Apply TLS for** to **Clients and databases + Between databases** and add client certificates.\n\n1. Select **Save**."}],"examples":[{"id":"client-ex0","language":"sh","code":"POST https://<host>:<port>/v1/bdbs\n{\n  \"tls_mode\": \"enabled\",\n  // Additional fields\n}","section_id":"client"},{"id":"client-ex1","language":"sh","code":"PUT https://<host>:<port>/v1/bdbs/<database_id>\n{\n  \"tls_mode\": \"enabled\"\n}","section_id":"client"},{"id":"enable-mutual-tls-ex0","language":"sh","code":"POST https://<host>:<port>/v1/bdbs\n{\n  \"authorized_subjects\": [{\n    \"CN\": <string>,\n    \"O\": <string>,\n    \"OU\": [<array of strings>],\n    \"L\": <string>,\n    \"ST\": <string>,\n    \"C\": <string>\n  }],\n  \"authentication_ssl_client_certs\": [{\n    \"client_cert\": \"-----BEGIN CERTIFICATE-----\\n...\\n-----END CERTIFICATE-----\\n\"\n  }],\n  \"client_cert_subject_validation_type\": <\"disabled\" | \"san_cn\" | \"full_subject\">,\n  \"enforce_client_authentication\": \"enabled\",\n  \"tls_mode\": \"enabled\",\n  // Additional fields\n}","section_id":"enable-mutual-tls"},{"id":"enable-mutual-tls-ex1","language":"sh","code":"PUT https://<host>:<port>/v1/bdbs/<database_id>\n{\n  \"authorized_subjects\": [{\n    \"CN\": <string>,\n    \"O\": <string>,\n    \"OU\": [<array of strings>],\n    \"L\": <string>,\n    \"ST\": <string>,\n    \"C\": <string>\n  }],\n  \"authentication_ssl_client_certs\": [{\n    \"client_cert\": \"-----BEGIN CERTIFICATE-----\\n...\\n-----END CERTIFICATE-----\\n\"\n  }],\n  \"client_cert_subject_validation_type\": <\"disabled\" | \"san_cn\" | \"full_subject\">,\n  \"enforce_client_authentication\": \"enabled\",\n  \"tls_mode\": \"enabled\"\n}","section_id":"enable-mutual-tls"},{"id":"validate-client-certificate-expiration-ex0","language":"sh","code":"rladmin tune db < db:id | name > mtls_allow_outdated_certs { enabled | disabled }","section_id":"validate-client-certificate-expiration"},{"id":"connect-over-tls-ex0","language":"sh","code":"redis-cli -h <endpoint> -p <port> --tls --cacert proxy_cert.pem","section_id":"connect-over-tls"},{"id":"connect-over-tls-ex1","language":"sh","code":"redis-cli -h <endpoint> -p <port> --tls --cacert proxy_cert.pem \\\n        --cert redis_user.crt --key redis_user_private.key","section_id":"connect-over-tls"},{"id":"enable-tls-during-database-creation-ex0","language":"json","code":"POST https://<host>:9443/v1/crdb\n{\n  \"name\": \"crdb-mtls\",\n  \"default_db_config\": {\n    \"name\": \"crdb-mtls\",\n    \"bigstore\": false,\n    \"memory_size\": 4294967296\n  },\n  \"encryption\": true,\n  \"instances\": [\n    {\n      \"cluster\": {\n        \"name\": \"<cluster1_host>\",\n        \"url\": \"https://<cluster1_host>:9443\",\n        \"credentials\": {\n          \"username\": \"<username>\",\n          \"password\": \"<password>\"\n        }\n      },\n      \"db_config\": {\n        \"tls_mode\": \"enabled\",\n        \"enforce_client_authentication\": \"enabled\",\n        \"authentication_ssl_client_certs\": [\n          {\n            \"client_cert\": \"-----BEGIN CERTIFICATE-----\\n...\\n-----END CERTIFICATE-----\\n\"\n          }\n        ]\n      }\n    },\n    {\n      \"cluster\": {\n        \"name\": \"<cluster2_host>\",\n        \"url\": \"https://<cluster2_host>:9443\",\n        \"credentials\": {\n          \"username\": \"<username>\",\n          \"password\": \"<password>\"\n        }\n      },\n      \"db_config\": {\n        \"tls_mode\": \"enabled\",\n        \"enforce_client_authentication\": \"enabled\",\n        \"authentication_ssl_client_certs\": [\n          {\n            \"client_cert\": \"-----BEGIN CERTIFICATE-----\\n...\\n-----END CERTIFICATE-----\\n\"\n          }\n        ]\n      }\n    }\n  ],\n  \"causal_consistency\": false\n}","section_id":"enable-tls-during-database-creation"},{"id":"enable-tls-after-database-creation-ex0","language":"sh","code":"crdb-cli crdb update --crdb-guid <guid> --encryption true","section_id":"enable-tls-after-database-creation"},{"id":"enable-tls-after-database-creation-ex1","language":"sh","code":"PUT https://<host>:9443/v1/bdbs/<database-id>\n{\n  \"enforce_client_authentication\": \"disabled\",\n  \"tls_mode\": \"replica_ssl\"\n}","section_id":"enable-tls-after-database-creation"},{"id":"enable-tls-after-database-creation-ex2","language":"sh","code":"PUT https://<host>:9443/v1/bdbs/<database-id>\n{\n  \"enforce_client_authentication\": \"disabled\",\n  \"tls_mode\": \"enabled\"\n}","section_id":"enable-tls-after-database-creation"}]}
{"schema_version":2,"id":"operate/rs/security/encryption/tls","title":"Transport Layer Security (TLS)","url":"https://redis.io/docs/latest/operate/rs/security/encryption/tls/","summary":"An overview of Transport Layer Security (TLS).","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:28+01:00","children":[{"id":"operate/rs/security/encryption/tls/enable-tls","summary":"Shows how to enable TLS.","title":"Enable TLS","url":"https://redis.io/docs/latest/operate/rs/security/encryption/tls/enable-tls/"},{"id":"operate/rs/security/encryption/tls/tls-protocols","summary":"","title":"Configure TLS protocol","url":"https://redis.io/docs/latest/operate/rs/security/encryption/tls/tls-protocols/"},{"id":"operate/rs/security/encryption/tls/ciphers","summary":"Shows how to configure cipher suites.","title":"Configure cipher suites","url":"https://redis.io/docs/latest/operate/rs/security/encryption/tls/ciphers/"}],"page_type":"content","content_hash":"5bfa6ae11e18d65487ba7adca178561bb07d693acc48fd82d31731ff8590db66","sections":[{"id":"overview","title":"Overview","role":"overview","text":"[Transport Layer Security (TLS)](https://en.wikipedia.org/wiki/Transport_Layer_Security), a successor to SSL, ensures the privacy of data sent between applications and Redis databases. TLS also secures connections between Redis Software nodes.\n\nYou can [use TLS authentication](https://redis.io/docs/latest/operate/rs/security/encryption/tls/enable-tls) for the following types of communication:\n\n- Communication from clients (applications) to your database\n- Communication from your database to other clusters for replication using [Replica Of](https://redis.io/docs/latest/operate/rs/databases/import-export/replica-of)\n- Communication to and from your database to other clusters for synchronization using [Active-Active](https://redis.io/docs/latest/operate/rs/databases/active-active)"},{"id":"protocols-and-ciphers","title":"Protocols and ciphers","role":"content","text":"TLS protocols and ciphers define the overall suite of algorithms that clients are able to connect to the servers with.\n\nYou can change the [TLS protocols](https://redis.io/docs/latest/operate/rs/security/encryption/tls/tls-protocols) and [ciphers](https://redis.io/docs/latest/operate/rs/security/encryption/tls/ciphers) to improve the security of your Redis Software cluster and databases. The default settings are in line with industry best practices, but you can customize them to match the security policy of your organization."},{"id":"troubleshooting","title":"Troubleshooting","role":"errors","text":"For help troubleshooting TLS failures, see the following knowledge base guides:\n\n- [Troubleshooting TLS Failures](https://support.redislabs.com/hc/en-us/articles/26867190871314-Troubleshooting-TLS-Failures)\n\n- [Troubleshooting TLS Connection Failures Caused by Certificate Expiration](https://support.redislabs.com/hc/en-us/articles/27021922067090-Troubleshooting-TLS-Connection-Failures-Caused-by-Certificate-Expiration)"}],"examples":[]}
{"schema_version":2,"id":"operate/rs/security/encryption/tls/tls-protocols","title":"Configure TLS protocol","url":"https://redis.io/docs/latest/operate/rs/security/encryption/tls/tls-protocols/","summary":"","tags":["docs","operate","rs"],"last_updated":"2026-10-01T15:48:36+01:00","page_type":"content","content_hash":"7faa4b2824d18e19283cda0707215f0c42953b3e548cf0fbfcc0c390ec340105","sections":[{"id":"overview","title":"Overview","role":"overview","text":"You can change TLS protocols to improve the security of your Redis Software cluster and databases. The default settings are in line with industry best practices, but you can customize them to match the security policy of your organization."},{"id":"configure-tls-protocol","title":"Configure TLS protocol","role":"configuration","text":"The communications for which you can modify TLS protocols are:\n\n- Control plane - The TLS configuration for cluster administration.\n- Data plane - The TLS configuration for the communication between applications and databases.\n- Discovery service (Sentinel) - The TLS configuration for the [discovery service](https://redis.io/docs/latest/operate/rs/databases/durability-ha/discovery-service).\n\nYou can configure TLS protocols with the [Cluster Manager UI](#edit-tls-ui), [`rladmin`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/cluster/config), or the [REST API](https://redis.io/docs/latest/operate/rs/references/rest-api/requests/cluster#put-cluster).\n\n> [!WARNING]\n> - After you set the minimum TLS version, Redis Software does not accept communications with TLS versions older than the specified version.\n>\n> - If you set TLS 1.3 as the minimum TLS version, clients must support TLS 1.3 to connect to Redis Software.\n\nTLS support depends on the operating system. You cannot enable support for protocols or versions that aren't supported by the operating system running Redis Software.  In addition, updates to the operating system or to Redis Software can impact protocol and version support.  \n\nIf you have trouble enabling specific versions of TLS, verify that they're supported by your operating system and that they're configured correctly.\n\n> [!NOTE]\n> TLSv1.2 is generally recommended as the minimum TLS version for encrypted communications. Check with your security team to confirm which TLS protocols meet your organization's policies."},{"id":"edit-tls-ui","title":"Edit TLS settings in the UI","role":"content","text":"To configure minimum TLS versions using the Cluster Manager UI:\n\n1. Go to **Cluster > Security**, then select the **TLS** tab.\n\n1. Click **Edit**.\n\n1. Select the minimum TLS version for cluster connections, database connections, and the discovery service:\n\n    ![Cluster > Security > TLS settings in edit mode in the Cluster Manager UI.](https://redis.io/docs/latest/images/rs/screenshots/cluster/security-tls-protocols-edit.png)\n  \n1. Select the TLS mode for the discovery service:\n\n    - **Allowed** - Allows both TLS and non-TLS connections\n    - **Required** - Allows only TLS connections\n    - **Disabled** - Allows only non-TLS connections\n\n1. Click **Save**."},{"id":"control-plane-tls","title":"Control plane TLS","role":"content","text":"To set the minimum TLS protocol for the control plane using `rladmin`:\n\n- Default minimum TLS protocol: TLSv1.2\n- Syntax: `rladmin cluster config min_control_TLS_version <TLS_Version>`\n- TLS versions available:\n  - For TLSv1.2 - 1.2\n  - For TLSv1.3 - 1.3\n\nFor example:\n\n[code example]"},{"id":"data-plane-tls","title":"Data plane TLS","role":"content","text":"To set the minimum TLS protocol for the data path using `rladmin`:\n\n- Default minimum TLS protocol: TLSv1.2\n- Syntax: `rladmin cluster config min_data_TLS_version <TLS_Version>`\n- TLS versions available:\n  - For TLSv1.2 - 1.2\n  - For TLSv1.3 - 1.3\n\nFor example:\n\n[code example]"},{"id":"discovery-service-tls","title":"Discovery service TLS","role":"content","text":"To enable TLS for the discovery service using `rladmin`:\n\n- Default: Allows both TLS and non-TLS connections\n- Syntax: `rladmin cluster config sentinel_tls_mode <ssl_policy>`\n- `ssl_policy` values available:\n  - `allowed` - Allows both TLS and non-TLS connections\n  - `required` - Allows only TLS connections\n  - `disabled` - Allows only non-TLS connections\n\nTo set the minimum TLS protocol for the discovery service using `rladmin`:\n\n- Default minimum TLS protocol: TLSv1.2\n- Syntax: `rladmin cluster config min_sentinel_TLS_version <TLS_Version>`\n- TLS versions available:\n  - For TLSv1.2 - 1.2\n  - For TLSv1.3 - 1.3\n\nTo enforce a minimum TLS version for the discovery service, run the following commands:\n\n1. Allow only TLS connections:\n\n    [code example]\n\n1. Set the minimal TLS version:\n\n    [code example]\n\n1. Restart the discovery service on all cluster nodes to apply your changes:\n\n    [code example]"}],"examples":[{"id":"control-plane-tls-ex0","language":"sh","code":"rladmin cluster config min_control_TLS_version 1.2","section_id":"control-plane-tls"},{"id":"data-plane-tls-ex0","language":"sh","code":"rladmin cluster config min_data_TLS_version 1.2","section_id":"data-plane-tls"},{"id":"discovery-service-tls-ex0","language":"sh","code":"rladmin cluster config sentinel_tls_mode required","section_id":"discovery-service-tls"},{"id":"discovery-service-tls-ex1","language":"sh","code":"rladmin cluster config min_sentinel_TLS_version 1.2","section_id":"discovery-service-tls"},{"id":"discovery-service-tls-ex2","language":"sh","code":"supervisorctl restart sentinel_service","section_id":"discovery-service-tls"}]}
{"schema_version":2,"id":"operate/rs/security","title":"Security","url":"https://redis.io/docs/latest/operate/rs/security/","summary":"","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:28+01:00","children":[{"id":"operate/rs/security/recommended-security-practices","summary":"","title":"Recommended security practices","url":"https://redis.io/docs/latest/operate/rs/security/recommended-security-practices/"},{"id":"operate/rs/security/access-control","summary":"An overview of access control in Redis Software.","title":"Access control","url":"https://redis.io/docs/latest/operate/rs/security/access-control/"},{"id":"operate/rs/security/audit-events","summary":"Describes how to audit commands and connection events.","title":"Audit database commands and connections","url":"https://redis.io/docs/latest/operate/rs/security/audit-events/"},{"id":"operate/rs/security/certificates","summary":"An overview of certificates in Redis Software.","title":"Certificates","url":"https://redis.io/docs/latest/operate/rs/security/certificates/"},{"id":"operate/rs/security/encryption","summary":"Encryption in Redis Software.","title":"Encryption in Redis Software","url":"https://redis.io/docs/latest/operate/rs/security/encryption/"}],"page_type":"content","content_hash":"556e9c9c0780e1ff4ffd7bb0a82a3063c385e264cc201a5b778f1e6ee16c6b63","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Redis Software provides various features to secure your deployment:\n\n| Login and passwords | Users and roles | Encryption and TLS | Certificates and audit |\n|---------------------|-----------------|--------------------|-----------------------|\n| [Password attempts and session timeout](https://redis.io/docs/latest/operate/rs/security/access-control/manage-users/login-lockout) | [Cluster and database access explained](https://redis.io/docs/latest/operate/rs/security/access-control) | [Enable TLS](https://redis.io/docs/latest/operate/rs/security/encryption/tls/enable-tls) | [Create certificates](https://redis.io/docs/latest/operate/rs/security/certificates/create-certificates) |\n| [Password complexity](https://redis.io/docs/latest/operate/rs/security/access-control/manage-passwords/password-complexity-rules) | [Create users](https://redis.io/docs/latest/operate/rs/security/access-control/create-users) | [Configure TLS protocols](https://redis.io/docs/latest/operate/rs/security/encryption/tls/tls-protocols) | [Monitor certificates](https://redis.io/docs/latest/operate/rs/security/certificates/monitor-certificates) |\n| [Password expiration](https://redis.io/docs/latest/operate/rs/security/access-control/manage-passwords/password-expiration) | [Create roles](https://redis.io/docs/latest/operate/rs/security/access-control/create-combined-roles) | [Configure cipher suites](https://redis.io/docs/latest/operate/rs/security/encryption/tls/ciphers) | [Update certificates](https://redis.io/docs/latest/operate/rs/security/certificates/updating-certificates) |\n| [Default database access](https://redis.io/docs/latest/operate/rs/security/access-control/manage-users/default-user) | [Redis ACLs](https://redis.io/docs/latest/operate/rs/security/access-control/redis-acl-overview) | [Encrypt private keys on disk](https://redis.io/docs/latest/operate/rs/security/encryption/pem-encryption) | [Enable OCSP stapling](https://redis.io/docs/latest/operate/rs/security/certificates/ocsp-stapling) |\n| [Rotate user passwords](https://redis.io/docs/latest/operate/rs/security/access-control/manage-passwords/rotate-passwords) | [Integrate with LDAP](https://redis.io/docs/latest/operate/rs/security/access-control/ldap) | [Internode encryption](https://redis.io/docs/latest/operate/rs/security/encryption/internode-encryption) | [Audit database connections](https://redis.io/docs/latest/operate/rs/security/audit-events) |\n| [Single sign-on (SSO)](https://redis.io/docs/latest/operate/rs/security/access-control/saml-sso) | | | |"},{"id":"recommended-security-practices","title":"Recommended security practices","role":"content","text":"See [Recommended security practices](https://redis.io/docs/latest/operate/rs/security/recommended-security-practices) to learn how to protect Redis Software."},{"id":"redis-trust-center","title":"Redis Trust Center","role":"content","text":"Visit our [Trust Center](https://trust.redis.io/) to learn more about Redis security policies. If you find a suspected security bug, you can [submit a report](https://hackerone.com/redis-vdp?type=team)."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/security/recommended-security-practices","title":"Recommended security practices","url":"https://redis.io/docs/latest/operate/rs/security/recommended-security-practices/","summary":"","tags":["docs","operate","rs"],"last_updated":"2026-09-24T09:16:28+01:00","page_type":"content","content_hash":"a5b45a5db37e37c42704063da9373b310bdce9851d89b4d9387ddddf7b5c4482","sections":[{"id":"deployment-security","title":"Deployment security","role":"overview","text":"When deploying Redis Software to production, we recommend the following practices:\n\n- **Deploy Redis Software inside a trusted network**:  Redis Software is database software and should be deployed on a trusted network not accessible to the public internet. Deploying Redis Software in a trusted network reduces the likelihood that someone can obtain unauthorized access to your data or the ability to manage your database configuration.\n\n- **Implement anti-virus exclusions**: To ensure that anti-virus solutions that scan files or intercept processes to protect memory do not interfere with Redis Software, you should ensure that anti-virus exclusions are implemented across all nodes in their Redis Software cluster in a consistent policy. This helps ensure that anti-virus software does not impact the availability of your Redis Software cluster.\n\n    If you are replacing your existing antivirus solution or installing/supporting Redis Software, make sure that the below paths are excluded:\n\n    > [!NOTE]\n    > For antivirus solutions that intercept processes, binary files may have to be excluded directly depending on the requirements of your anti-virus vendor.\n    >     \n\n    | **Path** | **Description** |\n    |------------|-----------------|\n    | /opt/redislabs | Main installation directory for all Redis Software binaries |\n    | /opt/redislabs/bin | Binaries for all the utilities for command line access and managements such as \"rladmin\" or \"redis-cli\" |\n    | /opt/redislabs/config | System configuration files |\n    | /opt/redislabs/lib | System library files |\n    | /opt/redislabs/sbin | System binaries for tweaking provisioning |\n\n- **Send logs to a remote logging server**: Redis Software is configured to send logs by default to syslog. To send these logs to a remote logging server, you must [configure syslog](https://redis.io/docs/latest/operate/rs/clusters/logging/log-security) based on the requirements of the remote logging server vendor. Remote logging helps ensure that the logs are not deleted so that you can rotate the logs to prevent your server disk from filling up.\n\n- **Deploy clusters with an odd number of 3 or more nodes**: Redis is an available and partition-tolerant database. We recommend that Redis Software be deployed in a cluster of an odd number of 3 or more nodes so that you are able to successfully failover in the event of a failure.\n\n- **Reboot nodes in a sequence rather than all at once**: It is best practice to frequently maintain reboot schedules. If you reboot too many servers at once, it is possible to cause a quorum failure that results in loss of availability of the database. We recommend that rebooting be done in a phased manner so that quorum is not lost. For example, to maintain quorum in a 3 node cluster, at least 2 nodes must be up at all times. Only one server should be rebooted at any given time to maintain quorum.\n\n- **Implement client-side encryption**: Client-side encryption, or the practice of encrypting data within an application before storing it in a database, such as Redis, is the most widely adopted method to achieve encryption in memory. Redis is an in-memory database and stores data in-memory. If you require encryption in memory, better known as encryption in use, then client side encryption may be the right solution for you. Please be aware that database functions that need to operate on data — such as simple searching functions, comparisons, and incremental operations — don’t work with client-side encryption."},{"id":"cluster-security","title":"Cluster security","role":"content","text":"- **Control the level of access to your system**: Redis Software lets you decide which users can access the cluster, which users can access databases, and which users can access both. We recommend preventing database users from accessing the cluster. See [Access control](https://redis.io/docs/latest/operate/rs/security/access-control) for more information.\n\n- **Enable LDAP authentication**: If your organization uses the Lightweight Directory Access Protocol (LDAP), we recommend enabling Redis Software support for role-based LDAP authentication.\n\n- **Require HTTPS for API endpoints**: Redis Software comes with a REST API to help automate tasks. This API is available in both an encrypted and unencrypted endpoint for backward compatibility. You can [disable the unencrypted endpoint](https://redis.io/docs/latest/operate/rs/references/rest-api/encryption#require-https-for-api-endpoints) with no loss in functionality."},{"id":"database-security","title":"Database security","role":"content","text":"Redis Software offers several database security controls to help protect your data against unauthorized access and to improve the operational security of your database. The following section details configurable security controls available for implementation.\n\n- **Use strong Redis passwords**: A frequent recommendation in the security industry is to use strong passwords to authenticate users. This helps to prevent brute force password guessing attacks against your database. Its important to check that your password aligns with your organizations security policy.\n\n- **Deactivate default user access**: Redis Software comes with a \"default\" user for backwards compatibility with applications designed with versions of Redis prior to Redis Software 6. The default user is turned on by default. This allows you to access the database without specifying a username and only using a shared secret. For applications designed to use access control lists, we recommend that you [deactivate default user access](https://redis.io/docs/latest/operate/rs/security/access-control/manage-users/default-user#deactivate-default-user).\n\n- **Configure Transport Layer Security (TLS)**: Similar to the control plane, you can also [configure TLS protocols](https://redis.io/docs/latest/operate/rs/security/encryption/tls/tls-protocols) to help support your security and compliance needs.\n\n- **Enable client certificate authentication**: To prevent unauthorized access to your data, Redis Software databases support the [TLS protocol](https://redis.io/docs/latest/operate/rs/security/encryption/tls#client-certificate-authentication), which includes authentication and encryption. Client certificate authentication can be used to ensure only authorized hosts can access the database.\n\n- **Install trusted certificates**: Redis implements self-signed certificates for the database proxy and replication service, but many organizations prefer to [use their own certificates](https://redis.io/docs/latest/operate/rs/security/certificates/create-certificates).\n\n- **Configure and verify database backups**: Implementing a disaster recovery strategy is an important part of data security. Redis Software supports [database backups to many destinations](https://redis.io/docs/latest/operate/rs/databases/import-export/schedule-backups)."}],"examples":[]}
{"schema_version":2,"id":"operate/rs/troubleshooting","title":"Troubleshooting Redis Software","url":"https://redis.io/docs/latest/operate/rs/troubleshooting/","summary":"Resources for troubleshooting Redis Software","tags":["docs","operate","rs"],"last_updated":"2026-09-24T10:14:16+01:00","page_type":"content","content_hash":"4e8055851eb680344239f331e58a13a0e7bfec224b1ccb614d15e0ee1b030c06","sections":[{"id":"general-troubleshooting","title":"General troubleshooting","role":"overview","text":"For general troubleshooting, see:\n\n- [Create a support package](https://redis.io/docs/latest/operate/rs/installing-upgrading/creating-support-package) to gather information about the cluster, nodes, and databases to help debug issues\n\n- [Logging](https://redis.io/docs/latest/operate/rs/clusters/logging)\n\n- [`rladmin status`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rladmin/status) command, which displays the current status of nodes, databases, database endpoints, and shards on the cluster\n\n- [`rlcheck`](https://redis.io/docs/latest/operate/rs/references/cli-utilities/rlcheck)  utility, which runs various tests to check node health and reports any discovered issues\n\n- [Troubleshooting pocket guide](https://redis.io/docs/latest/operate/rs/databases/connect/troubleshooting-guide)"},{"id":"troubleshooting-by-topic","title":"Troubleshooting by topic","role":"errors","text":"For troubleshooting specific issues, see:\n\n- [Database connectivity troubleshooting](https://redis.io/docs/latest/operate/rs/databases/connect/troubleshooting-guide#troubleshoot-connectivity)\n\n- [Latency troubleshooting](https://redis.io/docs/latest/operate/rs/databases/connect/troubleshooting-guide#troubleshoot-latency)\n\n- [Replica Of repeatedly fails](https://redis.io/docs/latest/operate/rs/databases/import-export/replica-of/replicaof-repeatedly-fails)\n\n- CRDT [INFO](https://redis.io/docs/latest/operate/rs/databases/active-active/develop/develop-for-aa#info) for troubleshooting Active-Active databases"},{"id":"knowledge-base","title":"Knowledge base","role":"content","text":"The [Redis Software knowledge base](https://support.redislabs.com/hc/en-us/categories/26174244088594-Redis-Software) provides additional guides and troubleshooting resources.\n\n- [Uploading Support Packages & Cluster Health Analysis](https://support.redislabs.com/hc/en-us/articles/26502436628370-Uploading-Support-Packages-Cluster-Health-Analysis)\n\n- [Tools for Identifying Failures in Clusters](https://support.redislabs.com/hc/en-us/articles/26759137301394-Tools-for-Identifying-Failures-in-Clusters)\n\n- [Troubleshooting TLS Failures](https://support.redislabs.com/hc/en-us/articles/26867190871314-Troubleshooting-TLS-Failures)\n\n- [Troubleshooting TLS Connection Failures Caused by Certificate Expiration](https://support.redislabs.com/hc/en-us/articles/27021922067090-Troubleshooting-TLS-Connection-Failures-Caused-by-Certificate-Expiration)\n\n- [Diagnosing and Resolving Endpoint Flapping in Redis Software](https://support.redislabs.com/hc/en-us/articles/27001052658706-Diagnosing-and-Resolving-Endpoint-Flapping-in-Redis-Software)\n\n- See [Troubleshooting Redis Software](https://support.redislabs.com/hc/en-us/sections/26758971861778-Troubleshooting-Redis-Software) for more troubleshooting guides."},{"id":"contact-support","title":"Contact support","role":"content","text":"If initial troubleshooting doesn't resolve the issue, you can [contact support](https://redis.io/support/) for additional help."}],"examples":[]}
